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
749 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
750 &li.bd_acid_eats_element, EL_BD_SAND
754 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
755 &li.bd_acid_spread_rate, 3
759 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
760 &li.bd_acid_turns_to_element, EL_EMPTY
765 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
766 &li.bd_biter_move_delay, 0
770 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
771 &li.bd_biter_eats_element, EL_BD_DIAMOND
776 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
777 &li.bd_bladder_converts_by_element, EL_BD_VOODOO_DOLL
781 EL_BD_EXPANDABLE_WALL_ANY, -1,
782 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
783 &li.bd_change_expanding_wall, FALSE
787 EL_BD_REPLICATOR, -1,
788 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
789 &li.bd_replicators_active, TRUE
792 EL_BD_REPLICATOR, -1,
793 TYPE_INTEGER, CONF_VALUE_8_BIT(2),
794 &li.bd_replicator_create_delay, 4
798 EL_BD_CONVEYOR_LEFT, -1,
799 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
800 &li.bd_conveyor_belts_active, TRUE
803 EL_BD_CONVEYOR_LEFT, -1,
804 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
805 &li.bd_conveyor_belts_changed, FALSE
810 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
811 &li.bd_water_cannot_flow_down, FALSE
816 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
817 &li.bd_nut_content, EL_BD_NUT_BREAKING_1
820 // (the following values are related to various game elements)
824 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
825 &li.score[SC_EMERALD], 10
830 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
831 &li.score[SC_DIAMOND], 10
836 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
837 &li.score[SC_BUG], 10
842 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
843 &li.score[SC_SPACESHIP], 10
848 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
849 &li.score[SC_PACMAN], 10
854 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
855 &li.score[SC_NUT], 10
860 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
861 &li.score[SC_DYNAMITE], 10
866 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
867 &li.score[SC_KEY], 10
872 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
873 &li.score[SC_PEARL], 10
878 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
879 &li.score[SC_CRYSTAL], 10
882 // (amoeba values used by R'n'D game engine only)
885 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
886 &li.amoeba_content, EL_DIAMOND
890 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
895 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
896 &li.grow_into_diggable, TRUE
898 // (amoeba values used by BD game engine only)
901 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
902 &li.bd_amoeba_wait_for_hatching, FALSE
906 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
907 &li.bd_amoeba_start_immediately, TRUE
911 TYPE_BOOLEAN, CONF_VALUE_8_BIT(4),
912 &li.bd_amoeba_2_explode_by_amoeba, TRUE
916 TYPE_INTEGER, CONF_VALUE_16_BIT(3),
917 &li.bd_amoeba_threshold_too_big, 200
921 TYPE_INTEGER, CONF_VALUE_16_BIT(4),
922 &li.bd_amoeba_slow_growth_time, 200
926 TYPE_INTEGER, CONF_VALUE_8_BIT(5),
927 &li.bd_amoeba_slow_growth_rate, 3
931 TYPE_INTEGER, CONF_VALUE_8_BIT(6),
932 &li.bd_amoeba_fast_growth_rate, 25
936 TYPE_ELEMENT, CONF_VALUE_16_BIT(5),
937 &li.bd_amoeba_content_too_big, EL_BD_ROCK
941 TYPE_ELEMENT, CONF_VALUE_16_BIT(6),
942 &li.bd_amoeba_content_enclosed, EL_BD_DIAMOND
947 TYPE_INTEGER, CONF_VALUE_16_BIT(3),
948 &li.bd_amoeba_2_threshold_too_big, 200
952 TYPE_INTEGER, CONF_VALUE_16_BIT(4),
953 &li.bd_amoeba_2_slow_growth_time, 200
957 TYPE_INTEGER, CONF_VALUE_8_BIT(5),
958 &li.bd_amoeba_2_slow_growth_rate, 3
962 TYPE_INTEGER, CONF_VALUE_8_BIT(6),
963 &li.bd_amoeba_2_fast_growth_rate, 25
967 TYPE_ELEMENT, CONF_VALUE_16_BIT(5),
968 &li.bd_amoeba_2_content_too_big, EL_BD_ROCK
972 TYPE_ELEMENT, CONF_VALUE_16_BIT(6),
973 &li.bd_amoeba_2_content_enclosed, EL_BD_DIAMOND
977 TYPE_ELEMENT, CONF_VALUE_16_BIT(7),
978 &li.bd_amoeba_2_content_exploding, EL_EMPTY
982 TYPE_ELEMENT, CONF_VALUE_16_BIT(8),
983 &li.bd_amoeba_2_content_looks_like, EL_BD_AMOEBA_2
988 TYPE_CONTENT_LIST, CONF_VALUE_BYTES(1),
989 &li.yamyam_content, EL_ROCK, NULL,
990 &li.num_yamyam_contents, 4, MAX_ELEMENT_CONTENTS
994 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
995 &li.score[SC_YAMYAM], 10
1000 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1001 &li.score[SC_ROBOT], 10
1005 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
1011 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1017 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1018 &li.time_magic_wall, 10
1022 EL_GAME_OF_LIFE, -1,
1023 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
1024 &li.game_of_life[0], 2
1027 EL_GAME_OF_LIFE, -1,
1028 TYPE_INTEGER, CONF_VALUE_8_BIT(2),
1029 &li.game_of_life[1], 3
1032 EL_GAME_OF_LIFE, -1,
1033 TYPE_INTEGER, CONF_VALUE_8_BIT(3),
1034 &li.game_of_life[2], 3
1037 EL_GAME_OF_LIFE, -1,
1038 TYPE_INTEGER, CONF_VALUE_8_BIT(4),
1039 &li.game_of_life[3], 3
1042 EL_GAME_OF_LIFE, -1,
1043 TYPE_BOOLEAN, CONF_VALUE_8_BIT(5),
1044 &li.use_life_bugs, FALSE
1049 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
1054 TYPE_INTEGER, CONF_VALUE_8_BIT(2),
1059 TYPE_INTEGER, CONF_VALUE_8_BIT(3),
1064 TYPE_INTEGER, CONF_VALUE_8_BIT(4),
1069 EL_TIMEGATE_SWITCH, -1,
1070 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1071 &li.time_timegate, 10
1075 EL_LIGHT_SWITCH_ACTIVE, -1,
1076 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1081 EL_SHIELD_NORMAL, -1,
1082 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1083 &li.shield_normal_time, 10
1086 EL_SHIELD_NORMAL, -1,
1087 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
1088 &li.score[SC_SHIELD], 10
1092 EL_SHIELD_DEADLY, -1,
1093 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1094 &li.shield_deadly_time, 10
1097 EL_SHIELD_DEADLY, -1,
1098 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
1099 &li.score[SC_SHIELD], 10
1104 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1109 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
1110 &li.extra_time_score, 10
1114 EL_TIME_ORB_FULL, -1,
1115 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1116 &li.time_orb_time, 10
1119 EL_TIME_ORB_FULL, -1,
1120 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1121 &li.use_time_orb_bug, FALSE
1126 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1127 &li.use_spring_bug, FALSE
1132 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1133 &li.android_move_time, 10
1137 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
1138 &li.android_clone_time, 10
1141 EL_EMC_ANDROID, SAVE_CONF_NEVER,
1142 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(1),
1143 &li.android_clone_element[0], EL_EMPTY, NULL,
1144 &li.num_android_clone_elements, 1, MAX_ANDROID_ELEMENTS_OLD
1148 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(2),
1149 &li.android_clone_element[0], EL_EMPTY, NULL,
1150 &li.num_android_clone_elements, 1, MAX_ANDROID_ELEMENTS
1155 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1156 &li.lenses_score, 10
1160 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
1165 EL_EMC_MAGNIFIER, -1,
1166 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1167 &li.magnify_score, 10
1170 EL_EMC_MAGNIFIER, -1,
1171 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
1172 &li.magnify_time, 10
1176 EL_EMC_MAGIC_BALL, -1,
1177 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1181 EL_EMC_MAGIC_BALL, -1,
1182 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1183 &li.ball_random, FALSE
1186 EL_EMC_MAGIC_BALL, -1,
1187 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
1188 &li.ball_active_initial, FALSE
1191 EL_EMC_MAGIC_BALL, -1,
1192 TYPE_CONTENT_LIST, CONF_VALUE_BYTES(1),
1193 &li.ball_content, EL_EMPTY, NULL,
1194 &li.num_ball_contents, 4, MAX_ELEMENT_CONTENTS
1198 EL_SOKOBAN_FIELD_EMPTY, -1,
1199 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1200 &li.sb_fields_needed, TRUE
1204 EL_SOKOBAN_OBJECT, -1,
1205 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1206 &li.sb_objects_needed, TRUE
1211 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1212 &li.mm_laser_red, FALSE
1216 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
1217 &li.mm_laser_green, FALSE
1221 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
1222 &li.mm_laser_blue, TRUE
1227 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1228 &li.df_laser_red, TRUE
1232 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
1233 &li.df_laser_green, TRUE
1237 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
1238 &li.df_laser_blue, FALSE
1242 EL_MM_FUSE_ACTIVE, -1,
1243 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1244 &li.mm_time_fuse, 25
1248 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1249 &li.mm_time_bomb, 75
1253 EL_MM_GRAY_BALL, -1,
1254 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1255 &li.mm_time_ball, 75
1258 EL_MM_GRAY_BALL, -1,
1259 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
1260 &li.mm_ball_choice_mode, ANIM_RANDOM
1263 EL_MM_GRAY_BALL, -1,
1264 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(1),
1265 &li.mm_ball_content, EL_EMPTY, NULL,
1266 &li.num_mm_ball_contents, 8, MAX_MM_BALL_CONTENTS
1269 EL_MM_GRAY_BALL, -1,
1270 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
1271 &li.rotate_mm_ball_content, TRUE
1274 EL_MM_GRAY_BALL, -1,
1275 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
1276 &li.explode_mm_ball, FALSE
1280 EL_MM_STEEL_BLOCK, -1,
1281 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1282 &li.mm_time_block, 75
1285 EL_MM_LIGHTBALL, -1,
1286 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1287 &li.score[SC_ELEM_BONUS], 10
1297 static struct LevelFileConfigInfo chunk_config_NOTE[] =
1301 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
1302 &xx_envelope.xsize, MAX_ENVELOPE_XSIZE,
1306 TYPE_INTEGER, CONF_VALUE_8_BIT(2),
1307 &xx_envelope.ysize, MAX_ENVELOPE_YSIZE,
1312 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
1313 &xx_envelope.autowrap, FALSE
1317 TYPE_BOOLEAN, CONF_VALUE_8_BIT(4),
1318 &xx_envelope.centered, FALSE
1323 TYPE_STRING, CONF_VALUE_BYTES(1),
1324 &xx_envelope.text, -1, NULL,
1325 &xx_string_length_unused, -1, MAX_ENVELOPE_TEXT_LEN,
1326 &xx_default_string_empty[0]
1336 static struct LevelFileConfigInfo chunk_config_CUSX_base[] =
1340 TYPE_STRING, CONF_VALUE_BYTES(1),
1341 &xx_ei.description[0], -1,
1342 &yy_ei.description[0],
1343 &xx_string_length_unused, -1, MAX_ELEMENT_NAME_LEN,
1344 &xx_default_description[0]
1349 TYPE_BITFIELD, CONF_VALUE_32_BIT(1),
1350 &xx_ei.properties[EP_BITFIELD_BASE_NR], EP_BITMASK_BASE_DEFAULT,
1351 &yy_ei.properties[EP_BITFIELD_BASE_NR]
1353 #if ENABLE_RESERVED_CODE
1354 // (reserved for later use)
1357 TYPE_BITFIELD, CONF_VALUE_32_BIT(2),
1358 &xx_ei.properties[EP_BITFIELD_BASE_NR + 1], EP_BITMASK_DEFAULT,
1359 &yy_ei.properties[EP_BITFIELD_BASE_NR + 1]
1365 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1366 &xx_ei.use_gfx_element, FALSE,
1367 &yy_ei.use_gfx_element
1371 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
1372 &xx_ei.gfx_element_initial, EL_EMPTY_SPACE,
1373 &yy_ei.gfx_element_initial
1378 TYPE_BITFIELD, CONF_VALUE_8_BIT(2),
1379 &xx_ei.access_direction, MV_ALL_DIRECTIONS,
1380 &yy_ei.access_direction
1385 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
1386 &xx_ei.collect_score_initial, 10,
1387 &yy_ei.collect_score_initial
1391 TYPE_INTEGER, CONF_VALUE_16_BIT(3),
1392 &xx_ei.collect_count_initial, 1,
1393 &yy_ei.collect_count_initial
1398 TYPE_INTEGER, CONF_VALUE_16_BIT(4),
1399 &xx_ei.ce_value_fixed_initial, 0,
1400 &yy_ei.ce_value_fixed_initial
1404 TYPE_INTEGER, CONF_VALUE_16_BIT(5),
1405 &xx_ei.ce_value_random_initial, 0,
1406 &yy_ei.ce_value_random_initial
1410 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
1411 &xx_ei.use_last_ce_value, FALSE,
1412 &yy_ei.use_last_ce_value
1417 TYPE_INTEGER, CONF_VALUE_16_BIT(6),
1418 &xx_ei.push_delay_fixed, 8,
1419 &yy_ei.push_delay_fixed
1423 TYPE_INTEGER, CONF_VALUE_16_BIT(7),
1424 &xx_ei.push_delay_random, 8,
1425 &yy_ei.push_delay_random
1429 TYPE_INTEGER, CONF_VALUE_16_BIT(8),
1430 &xx_ei.drop_delay_fixed, 0,
1431 &yy_ei.drop_delay_fixed
1435 TYPE_INTEGER, CONF_VALUE_16_BIT(9),
1436 &xx_ei.drop_delay_random, 0,
1437 &yy_ei.drop_delay_random
1441 TYPE_INTEGER, CONF_VALUE_16_BIT(10),
1442 &xx_ei.move_delay_fixed, 0,
1443 &yy_ei.move_delay_fixed
1447 TYPE_INTEGER, CONF_VALUE_16_BIT(11),
1448 &xx_ei.move_delay_random, 0,
1449 &yy_ei.move_delay_random
1453 TYPE_INTEGER, CONF_VALUE_16_BIT(16),
1454 &xx_ei.step_delay_fixed, 0,
1455 &yy_ei.step_delay_fixed
1459 TYPE_INTEGER, CONF_VALUE_16_BIT(17),
1460 &xx_ei.step_delay_random, 0,
1461 &yy_ei.step_delay_random
1466 TYPE_BITFIELD, CONF_VALUE_32_BIT(3),
1467 &xx_ei.move_pattern, MV_ALL_DIRECTIONS,
1472 TYPE_BITFIELD, CONF_VALUE_8_BIT(4),
1473 &xx_ei.move_direction_initial, MV_START_AUTOMATIC,
1474 &yy_ei.move_direction_initial
1478 TYPE_INTEGER, CONF_VALUE_8_BIT(5),
1479 &xx_ei.move_stepsize, TILEX / 8,
1480 &yy_ei.move_stepsize
1485 TYPE_ELEMENT, CONF_VALUE_16_BIT(12),
1486 &xx_ei.move_enter_element, EL_EMPTY_SPACE,
1487 &yy_ei.move_enter_element
1491 TYPE_ELEMENT, CONF_VALUE_16_BIT(13),
1492 &xx_ei.move_leave_element, EL_EMPTY_SPACE,
1493 &yy_ei.move_leave_element
1497 TYPE_INTEGER, CONF_VALUE_8_BIT(6),
1498 &xx_ei.move_leave_type, LEAVE_TYPE_UNLIMITED,
1499 &yy_ei.move_leave_type
1504 TYPE_INTEGER, CONF_VALUE_8_BIT(7),
1505 &xx_ei.slippery_type, SLIPPERY_ANY_RANDOM,
1506 &yy_ei.slippery_type
1511 TYPE_INTEGER, CONF_VALUE_8_BIT(8),
1512 &xx_ei.explosion_type, EXPLODES_3X3,
1513 &yy_ei.explosion_type
1517 TYPE_INTEGER, CONF_VALUE_16_BIT(14),
1518 &xx_ei.explosion_delay, 16,
1519 &yy_ei.explosion_delay
1523 TYPE_INTEGER, CONF_VALUE_16_BIT(15),
1524 &xx_ei.ignition_delay, 8,
1525 &yy_ei.ignition_delay
1530 TYPE_CONTENT_LIST, CONF_VALUE_BYTES(2),
1531 &xx_ei.content, EL_EMPTY_SPACE,
1533 &xx_num_contents, 1, 1
1536 // ---------- "num_change_pages" must be the last entry ---------------------
1539 -1, SAVE_CONF_ALWAYS,
1540 TYPE_INTEGER, CONF_VALUE_8_BIT(9),
1541 &xx_ei.num_change_pages, 1,
1542 &yy_ei.num_change_pages
1553 static struct LevelFileConfigInfo chunk_config_CUSX_change[] =
1555 // ---------- "current_change_page" must be the first entry -----------------
1558 -1, SAVE_CONF_ALWAYS,
1559 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
1560 &xx_current_change_page, -1
1563 // ---------- (the remaining entries can be in any order) -------------------
1567 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
1568 &xx_change.can_change, FALSE
1573 TYPE_BITFIELD, CONF_VALUE_32_BIT(1),
1574 &xx_event_bits[0], 0
1578 TYPE_BITFIELD, CONF_VALUE_32_BIT(2),
1579 &xx_event_bits[1], 0
1584 TYPE_BITFIELD, CONF_VALUE_8_BIT(3),
1585 &xx_change.trigger_player, CH_PLAYER_ANY
1589 TYPE_BITFIELD, CONF_VALUE_8_BIT(4),
1590 &xx_change.trigger_side, CH_SIDE_ANY
1594 TYPE_BITFIELD, CONF_VALUE_32_BIT(3),
1595 &xx_change.trigger_page, CH_PAGE_ANY
1600 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
1601 &xx_change.target_element, EL_EMPTY_SPACE
1606 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
1607 &xx_change.delay_fixed, 0
1611 TYPE_INTEGER, CONF_VALUE_16_BIT(3),
1612 &xx_change.delay_random, 0
1616 TYPE_INTEGER, CONF_VALUE_16_BIT(4),
1617 &xx_change.delay_frames, FRAMES_PER_SECOND
1622 TYPE_ELEMENT, CONF_VALUE_16_BIT(5),
1623 &xx_change.initial_trigger_element, EL_EMPTY_SPACE
1628 TYPE_BOOLEAN, CONF_VALUE_8_BIT(6),
1629 &xx_change.explode, FALSE
1633 TYPE_BOOLEAN, CONF_VALUE_8_BIT(7),
1634 &xx_change.use_target_content, FALSE
1638 TYPE_BOOLEAN, CONF_VALUE_8_BIT(8),
1639 &xx_change.only_if_complete, FALSE
1643 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
1644 &xx_change.use_random_replace, FALSE
1648 TYPE_INTEGER, CONF_VALUE_8_BIT(10),
1649 &xx_change.random_percentage, 100
1653 TYPE_INTEGER, CONF_VALUE_8_BIT(11),
1654 &xx_change.replace_when, CP_WHEN_EMPTY
1659 TYPE_BOOLEAN, CONF_VALUE_8_BIT(12),
1660 &xx_change.has_action, FALSE
1664 TYPE_INTEGER, CONF_VALUE_8_BIT(13),
1665 &xx_change.action_type, CA_NO_ACTION
1669 TYPE_INTEGER, CONF_VALUE_8_BIT(14),
1670 &xx_change.action_mode, CA_MODE_UNDEFINED
1674 TYPE_INTEGER, CONF_VALUE_16_BIT(6),
1675 &xx_change.action_arg, CA_ARG_UNDEFINED
1680 TYPE_ELEMENT, CONF_VALUE_16_BIT(7),
1681 &xx_change.action_element, EL_EMPTY_SPACE
1686 TYPE_CONTENT_LIST, CONF_VALUE_BYTES(1),
1687 &xx_change.target_content, EL_EMPTY_SPACE, NULL,
1688 &xx_num_contents, 1, 1
1698 static struct LevelFileConfigInfo chunk_config_GRPX[] =
1702 TYPE_STRING, CONF_VALUE_BYTES(1),
1703 &xx_ei.description[0], -1, NULL,
1704 &xx_string_length_unused, -1, MAX_ELEMENT_NAME_LEN,
1705 &xx_default_description[0]
1710 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1711 &xx_ei.use_gfx_element, FALSE
1715 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
1716 &xx_ei.gfx_element_initial, EL_EMPTY_SPACE
1721 TYPE_INTEGER, CONF_VALUE_8_BIT(2),
1722 &xx_group.choice_mode, ANIM_RANDOM
1727 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(2),
1728 &xx_group.element[0], EL_EMPTY_SPACE, NULL,
1729 &xx_group.num_elements, 1, MAX_ELEMENTS_IN_GROUP
1739 static struct LevelFileConfigInfo chunk_config_EMPX[] =
1743 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1744 &xx_ei.use_gfx_element, FALSE
1748 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
1749 &xx_ei.gfx_element_initial, EL_EMPTY_SPACE
1759 static struct LevelFileConfigInfo chunk_config_CONF[] = // (OBSOLETE)
1763 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
1764 &li.block_snap_field, TRUE
1768 TYPE_BOOLEAN, CONF_VALUE_8_BIT(13),
1769 &li.continuous_snapping, TRUE
1773 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
1774 &li.initial_player_stepsize[0], STEPSIZE_NORMAL
1778 TYPE_BOOLEAN, CONF_VALUE_8_BIT(10),
1779 &li.use_start_element[0], FALSE
1783 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
1784 &li.start_element[0], EL_PLAYER_1
1788 TYPE_BOOLEAN, CONF_VALUE_8_BIT(11),
1789 &li.use_artwork_element[0], FALSE
1793 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
1794 &li.artwork_element[0], EL_PLAYER_1
1798 TYPE_BOOLEAN, CONF_VALUE_8_BIT(12),
1799 &li.use_explosion_element[0], FALSE
1803 TYPE_ELEMENT, CONF_VALUE_16_BIT(3),
1804 &li.explosion_element[0], EL_PLAYER_1
1819 filetype_id_list[] =
1821 { LEVEL_FILE_TYPE_RND, "RND" },
1822 { LEVEL_FILE_TYPE_BD, "BD" },
1823 { LEVEL_FILE_TYPE_EM, "EM" },
1824 { LEVEL_FILE_TYPE_SP, "SP" },
1825 { LEVEL_FILE_TYPE_DX, "DX" },
1826 { LEVEL_FILE_TYPE_SB, "SB" },
1827 { LEVEL_FILE_TYPE_DC, "DC" },
1828 { LEVEL_FILE_TYPE_MM, "MM" },
1829 { LEVEL_FILE_TYPE_MM, "DF" },
1834 // ============================================================================
1835 // level file functions
1836 // ============================================================================
1838 static boolean check_special_flags(char *flag)
1840 if (strEqual(options.special_flags, flag) ||
1841 strEqual(leveldir_current->special_flags, flag))
1847 static struct DateInfo getCurrentDate(void)
1849 time_t epoch_seconds = time(NULL);
1850 struct tm *now = localtime(&epoch_seconds);
1851 struct DateInfo date;
1853 date.year = now->tm_year + 1900;
1854 date.month = now->tm_mon + 1;
1855 date.day = now->tm_mday;
1857 date.src = DATE_SRC_CLOCK;
1862 static void resetEventFlags(struct ElementChangeInfo *change)
1866 for (i = 0; i < NUM_CHANGE_EVENTS; i++)
1867 change->has_event[i] = FALSE;
1870 static void resetEventBits(void)
1874 for (i = 0; i < NUM_CE_BITFIELDS; i++)
1875 xx_event_bits[i] = 0;
1878 static void setEventFlagsFromEventBits(struct ElementChangeInfo *change)
1882 /* important: only change event flag if corresponding event bit is set
1883 (this is because all xx_event_bits[] values are loaded separately,
1884 and all xx_event_bits[] values are set back to zero before loading
1885 another value xx_event_bits[x] (each value representing 32 flags)) */
1887 for (i = 0; i < NUM_CHANGE_EVENTS; i++)
1888 if (xx_event_bits[CH_EVENT_BITFIELD_NR(i)] & CH_EVENT_BIT(i))
1889 change->has_event[i] = TRUE;
1892 static void setEventBitsFromEventFlags(struct ElementChangeInfo *change)
1896 /* in contrast to the above function setEventFlagsFromEventBits(), it
1897 would also be possible to set all bits in xx_event_bits[] to 0 or 1
1898 depending on the corresponding change->has_event[i] values here, as
1899 all xx_event_bits[] values are reset in resetEventBits() before */
1901 for (i = 0; i < NUM_CHANGE_EVENTS; i++)
1902 if (change->has_event[i])
1903 xx_event_bits[CH_EVENT_BITFIELD_NR(i)] |= CH_EVENT_BIT(i);
1906 static char *getDefaultElementDescription(struct ElementInfo *ei)
1908 static char description[MAX_ELEMENT_NAME_LEN + 1];
1909 char *default_description = (ei->custom_description != NULL ?
1910 ei->custom_description :
1911 ei->editor_description);
1914 // always start with reliable default values
1915 for (i = 0; i < MAX_ELEMENT_NAME_LEN + 1; i++)
1916 description[i] = '\0';
1918 // truncate element description to MAX_ELEMENT_NAME_LEN bytes
1919 strncpy(description, default_description, MAX_ELEMENT_NAME_LEN);
1921 return &description[0];
1924 static void setElementDescriptionToDefault(struct ElementInfo *ei)
1926 char *default_description = getDefaultElementDescription(ei);
1929 for (i = 0; i < MAX_ELEMENT_NAME_LEN + 1; i++)
1930 ei->description[i] = default_description[i];
1933 static void setConfigToDefaultsFromConfigList(struct LevelFileConfigInfo *conf)
1937 for (i = 0; conf[i].data_type != -1; i++)
1939 int default_value = conf[i].default_value;
1940 int data_type = conf[i].data_type;
1941 int conf_type = conf[i].conf_type;
1942 int byte_mask = conf_type & CONF_MASK_BYTES;
1944 if (byte_mask == CONF_MASK_MULTI_BYTES)
1946 int default_num_entities = conf[i].default_num_entities;
1947 int max_num_entities = conf[i].max_num_entities;
1949 *(int *)(conf[i].num_entities) = default_num_entities;
1951 if (data_type == TYPE_STRING)
1953 char *default_string = conf[i].default_string;
1954 char *string = (char *)(conf[i].value);
1956 strncpy(string, default_string, max_num_entities);
1958 else if (data_type == TYPE_ELEMENT_LIST)
1960 int *element_array = (int *)(conf[i].value);
1963 for (j = 0; j < max_num_entities; j++)
1964 element_array[j] = default_value;
1966 else if (data_type == TYPE_CONTENT_LIST)
1968 struct Content *content = (struct Content *)(conf[i].value);
1971 for (c = 0; c < max_num_entities; c++)
1972 for (y = 0; y < 3; y++)
1973 for (x = 0; x < 3; x++)
1974 content[c].e[x][y] = default_value;
1977 else // constant size configuration data (1, 2 or 4 bytes)
1979 if (data_type == TYPE_BOOLEAN)
1980 *(boolean *)(conf[i].value) = default_value;
1982 *(int *) (conf[i].value) = default_value;
1987 static void copyConfigFromConfigList(struct LevelFileConfigInfo *conf)
1991 for (i = 0; conf[i].data_type != -1; i++)
1993 int data_type = conf[i].data_type;
1994 int conf_type = conf[i].conf_type;
1995 int byte_mask = conf_type & CONF_MASK_BYTES;
1997 if (byte_mask == CONF_MASK_MULTI_BYTES)
1999 int max_num_entities = conf[i].max_num_entities;
2001 if (data_type == TYPE_STRING)
2003 char *string = (char *)(conf[i].value);
2004 char *string_copy = (char *)(conf[i].value_copy);
2006 strncpy(string_copy, string, max_num_entities);
2008 else if (data_type == TYPE_ELEMENT_LIST)
2010 int *element_array = (int *)(conf[i].value);
2011 int *element_array_copy = (int *)(conf[i].value_copy);
2014 for (j = 0; j < max_num_entities; j++)
2015 element_array_copy[j] = element_array[j];
2017 else if (data_type == TYPE_CONTENT_LIST)
2019 struct Content *content = (struct Content *)(conf[i].value);
2020 struct Content *content_copy = (struct Content *)(conf[i].value_copy);
2023 for (c = 0; c < max_num_entities; c++)
2024 for (y = 0; y < 3; y++)
2025 for (x = 0; x < 3; x++)
2026 content_copy[c].e[x][y] = content[c].e[x][y];
2029 else // constant size configuration data (1, 2 or 4 bytes)
2031 if (data_type == TYPE_BOOLEAN)
2032 *(boolean *)(conf[i].value_copy) = *(boolean *)(conf[i].value);
2034 *(int *) (conf[i].value_copy) = *(int *) (conf[i].value);
2039 void copyElementInfo(struct ElementInfo *ei_from, struct ElementInfo *ei_to)
2043 xx_ei = *ei_from; // copy element data into temporary buffer
2044 yy_ei = *ei_to; // copy element data into temporary buffer
2046 copyConfigFromConfigList(chunk_config_CUSX_base);
2051 // ---------- reinitialize and copy change pages ----------
2053 ei_to->num_change_pages = ei_from->num_change_pages;
2054 ei_to->current_change_page = ei_from->current_change_page;
2056 setElementChangePages(ei_to, ei_to->num_change_pages);
2058 for (i = 0; i < ei_to->num_change_pages; i++)
2059 ei_to->change_page[i] = ei_from->change_page[i];
2061 // ---------- copy group element info ----------
2062 if (ei_from->group != NULL && ei_to->group != NULL) // group or internal
2063 *ei_to->group = *ei_from->group;
2065 // mark this custom element as modified
2066 ei_to->modified_settings = TRUE;
2069 void setElementChangePages(struct ElementInfo *ei, int change_pages)
2071 int change_page_size = sizeof(struct ElementChangeInfo);
2073 ei->num_change_pages = MAX(1, change_pages);
2076 checked_realloc(ei->change_page, ei->num_change_pages * change_page_size);
2078 if (ei->current_change_page >= ei->num_change_pages)
2079 ei->current_change_page = ei->num_change_pages - 1;
2081 ei->change = &ei->change_page[ei->current_change_page];
2084 void setElementChangeInfoToDefaults(struct ElementChangeInfo *change)
2086 xx_change = *change; // copy change data into temporary buffer
2088 setConfigToDefaultsFromConfigList(chunk_config_CUSX_change);
2090 *change = xx_change;
2092 resetEventFlags(change);
2094 change->direct_action = 0;
2095 change->other_action = 0;
2097 change->pre_change_function = NULL;
2098 change->change_function = NULL;
2099 change->post_change_function = NULL;
2102 static void setLevelInfoToDefaults_Level(struct LevelInfo *level)
2106 li = *level; // copy level data into temporary buffer
2107 setConfigToDefaultsFromConfigList(chunk_config_INFO);
2108 *level = li; // copy temporary buffer back to level data
2110 setLevelInfoToDefaults_BD();
2111 setLevelInfoToDefaults_EM();
2112 setLevelInfoToDefaults_SP();
2113 setLevelInfoToDefaults_MM();
2115 level->native_bd_level = &native_bd_level;
2116 level->native_em_level = &native_em_level;
2117 level->native_sp_level = &native_sp_level;
2118 level->native_mm_level = &native_mm_level;
2120 level->file_version = FILE_VERSION_ACTUAL;
2121 level->game_version = GAME_VERSION_ACTUAL;
2123 level->creation_date = getCurrentDate();
2125 level->encoding_16bit_field = TRUE;
2126 level->encoding_16bit_yamyam = TRUE;
2127 level->encoding_16bit_amoeba = TRUE;
2129 // clear level name and level author string buffers
2130 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
2131 level->name[i] = '\0';
2132 for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
2133 level->author[i] = '\0';
2135 // set level name and level author to default values
2136 strcpy(level->name, NAMELESS_LEVEL_NAME);
2137 strcpy(level->author, ANONYMOUS_NAME);
2139 // set level playfield to playable default level with player and exit
2140 for (x = 0; x < MAX_LEV_FIELDX; x++)
2141 for (y = 0; y < MAX_LEV_FIELDY; y++)
2142 level->field[x][y] = EL_SAND;
2144 level->field[0][0] = EL_PLAYER_1;
2145 level->field[STD_LEV_FIELDX - 1][STD_LEV_FIELDY - 1] = EL_EXIT_CLOSED;
2147 BorderElement = EL_STEELWALL;
2149 // detect custom elements when loading them
2150 level->file_has_custom_elements = FALSE;
2152 // set all bug compatibility flags to "false" => do not emulate this bug
2153 level->use_action_after_change_bug = FALSE;
2155 if (leveldir_current)
2157 // try to determine better author name than 'anonymous'
2158 if (!strEqual(leveldir_current->author, ANONYMOUS_NAME))
2160 strncpy(level->author, leveldir_current->author, MAX_LEVEL_AUTHOR_LEN);
2161 level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
2165 switch (LEVELCLASS(leveldir_current))
2167 case LEVELCLASS_TUTORIAL:
2168 strcpy(level->author, PROGRAM_AUTHOR_STRING);
2171 case LEVELCLASS_CONTRIB:
2172 strncpy(level->author, leveldir_current->name, MAX_LEVEL_AUTHOR_LEN);
2173 level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
2176 case LEVELCLASS_PRIVATE:
2177 strncpy(level->author, getRealName(), MAX_LEVEL_AUTHOR_LEN);
2178 level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
2182 // keep default value
2189 static void setLevelInfoToDefaults_Elements(struct LevelInfo *level)
2191 static boolean clipboard_elements_initialized = FALSE;
2194 InitElementPropertiesStatic();
2196 li = *level; // copy level data into temporary buffer
2197 setConfigToDefaultsFromConfigList(chunk_config_ELEM);
2198 *level = li; // copy temporary buffer back to level data
2200 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2203 struct ElementInfo *ei = &element_info[element];
2205 if (element == EL_MM_GRAY_BALL)
2207 struct LevelInfo_MM *level_mm = level->native_mm_level;
2210 for (j = 0; j < level->num_mm_ball_contents; j++)
2211 level->mm_ball_content[j] =
2212 map_element_MM_to_RND(level_mm->ball_content[j]);
2215 // never initialize clipboard elements after the very first time
2216 // (to be able to use clipboard elements between several levels)
2217 if (IS_CLIPBOARD_ELEMENT(element) && clipboard_elements_initialized)
2220 if (IS_ENVELOPE(element))
2222 int envelope_nr = element - EL_ENVELOPE_1;
2224 setConfigToDefaultsFromConfigList(chunk_config_NOTE);
2226 level->envelope[envelope_nr] = xx_envelope;
2229 if (IS_CUSTOM_ELEMENT(element) ||
2230 IS_GROUP_ELEMENT(element) ||
2231 IS_INTERNAL_ELEMENT(element))
2233 xx_ei = *ei; // copy element data into temporary buffer
2235 setConfigToDefaultsFromConfigList(chunk_config_CUSX_base);
2240 setElementChangePages(ei, 1);
2241 setElementChangeInfoToDefaults(ei->change);
2243 if (IS_CUSTOM_ELEMENT(element) ||
2244 IS_GROUP_ELEMENT(element))
2246 setElementDescriptionToDefault(ei);
2248 ei->modified_settings = FALSE;
2251 if (IS_CUSTOM_ELEMENT(element) ||
2252 IS_INTERNAL_ELEMENT(element))
2254 // internal values used in level editor
2256 ei->access_type = 0;
2257 ei->access_layer = 0;
2258 ei->access_protected = 0;
2259 ei->walk_to_action = 0;
2260 ei->smash_targets = 0;
2263 ei->can_explode_by_fire = FALSE;
2264 ei->can_explode_smashed = FALSE;
2265 ei->can_explode_impact = FALSE;
2267 ei->current_change_page = 0;
2270 if (IS_GROUP_ELEMENT(element) ||
2271 IS_INTERNAL_ELEMENT(element))
2273 struct ElementGroupInfo *group;
2275 // initialize memory for list of elements in group
2276 if (ei->group == NULL)
2277 ei->group = checked_malloc(sizeof(struct ElementGroupInfo));
2281 xx_group = *group; // copy group data into temporary buffer
2283 setConfigToDefaultsFromConfigList(chunk_config_GRPX);
2288 if (IS_EMPTY_ELEMENT(element) ||
2289 IS_INTERNAL_ELEMENT(element))
2291 xx_ei = *ei; // copy element data into temporary buffer
2293 setConfigToDefaultsFromConfigList(chunk_config_EMPX);
2299 clipboard_elements_initialized = TRUE;
2302 static void setLevelInfoToDefaults(struct LevelInfo *level,
2303 boolean level_info_only,
2304 boolean reset_file_status)
2306 setLevelInfoToDefaults_Level(level);
2308 if (!level_info_only)
2309 setLevelInfoToDefaults_Elements(level);
2311 if (reset_file_status)
2313 level->no_valid_file = FALSE;
2314 level->no_level_file = FALSE;
2317 level->changed = FALSE;
2320 static void setFileInfoToDefaults(struct LevelFileInfo *level_file_info)
2322 level_file_info->nr = 0;
2323 level_file_info->type = LEVEL_FILE_TYPE_UNKNOWN;
2324 level_file_info->packed = FALSE;
2326 setString(&level_file_info->basename, NULL);
2327 setString(&level_file_info->filename, NULL);
2330 int getMappedElement_SB(int, boolean);
2332 static void ActivateLevelTemplate(void)
2336 if (check_special_flags("load_xsb_to_ces"))
2338 // fill smaller playfields with padding "beyond border wall" elements
2339 if (level.fieldx < level_template.fieldx ||
2340 level.fieldy < level_template.fieldy)
2342 short field[level.fieldx][level.fieldy];
2343 int new_fieldx = MAX(level.fieldx, level_template.fieldx);
2344 int new_fieldy = MAX(level.fieldy, level_template.fieldy);
2345 int pos_fieldx = (new_fieldx - level.fieldx) / 2;
2346 int pos_fieldy = (new_fieldy - level.fieldy) / 2;
2348 // copy old playfield (which is smaller than the visible area)
2349 for (y = 0; y < level.fieldy; y++) for (x = 0; x < level.fieldx; x++)
2350 field[x][y] = level.field[x][y];
2352 // fill new, larger playfield with "beyond border wall" elements
2353 for (y = 0; y < new_fieldy; y++) for (x = 0; x < new_fieldx; x++)
2354 level.field[x][y] = getMappedElement_SB('_', TRUE);
2356 // copy the old playfield to the middle of the new playfield
2357 for (y = 0; y < level.fieldy; y++) for (x = 0; x < level.fieldx; x++)
2358 level.field[pos_fieldx + x][pos_fieldy + y] = field[x][y];
2360 level.fieldx = new_fieldx;
2361 level.fieldy = new_fieldy;
2365 // Currently there is no special action needed to activate the template
2366 // data, because 'element_info' property settings overwrite the original
2367 // level data, while all other variables do not change.
2369 // Exception: 'from_level_template' elements in the original level playfield
2370 // are overwritten with the corresponding elements at the same position in
2371 // playfield from the level template.
2373 for (x = 0; x < level.fieldx; x++)
2374 for (y = 0; y < level.fieldy; y++)
2375 if (level.field[x][y] == EL_FROM_LEVEL_TEMPLATE)
2376 level.field[x][y] = level_template.field[x][y];
2378 if (check_special_flags("load_xsb_to_ces"))
2380 struct LevelInfo level_backup = level;
2382 // overwrite all individual level settings from template level settings
2383 level = level_template;
2385 // restore level file info
2386 level.file_info = level_backup.file_info;
2388 // restore playfield size
2389 level.fieldx = level_backup.fieldx;
2390 level.fieldy = level_backup.fieldy;
2392 // restore playfield content
2393 for (x = 0; x < level.fieldx; x++)
2394 for (y = 0; y < level.fieldy; y++)
2395 level.field[x][y] = level_backup.field[x][y];
2397 // restore name and author from individual level
2398 strcpy(level.name, level_backup.name);
2399 strcpy(level.author, level_backup.author);
2401 // restore flag "use_custom_template"
2402 level.use_custom_template = level_backup.use_custom_template;
2406 static boolean checkForPackageFromBasename_BD(char *basename)
2408 // check for native BD level file extensions
2409 if (!strSuffixLower(basename, ".bd") &&
2410 !strSuffixLower(basename, ".bdr") &&
2411 !strSuffixLower(basename, ".brc") &&
2412 !strSuffixLower(basename, ".gds"))
2415 // check for standard single-level BD files (like "001.bd")
2416 if (strSuffixLower(basename, ".bd") &&
2417 strlen(basename) == 6 &&
2418 basename[0] >= '0' && basename[0] <= '9' &&
2419 basename[1] >= '0' && basename[1] <= '9' &&
2420 basename[2] >= '0' && basename[2] <= '9')
2423 // this is a level package in native BD file format
2427 static char *getLevelFilenameFromBasename(char *basename)
2429 static char *filename = NULL;
2431 checked_free(filename);
2433 filename = getPath2(getCurrentLevelDir(), basename);
2438 static int getFileTypeFromBasename(char *basename)
2440 // !!! ALSO SEE COMMENT IN checkForPackageFromBasename() !!!
2442 static char *filename = NULL;
2443 struct stat file_status;
2445 // ---------- try to determine file type from filename ----------
2447 // check for typical filename of a Supaplex level package file
2448 if (strlen(basename) == 10 && strPrefixLower(basename, "levels.d"))
2449 return LEVEL_FILE_TYPE_SP;
2451 // check for typical filename of a Diamond Caves II level package file
2452 if (strSuffixLower(basename, ".dc") ||
2453 strSuffixLower(basename, ".dc2"))
2454 return LEVEL_FILE_TYPE_DC;
2456 // check for typical filename of a Sokoban level package file
2457 if (strSuffixLower(basename, ".xsb") &&
2458 strchr(basename, '%') == NULL)
2459 return LEVEL_FILE_TYPE_SB;
2461 // check for typical filename of a Boulder Dash (GDash) level package file
2462 if (checkForPackageFromBasename_BD(basename))
2463 return LEVEL_FILE_TYPE_BD;
2465 // ---------- try to determine file type from filesize ----------
2467 checked_free(filename);
2468 filename = getPath2(getCurrentLevelDir(), basename);
2470 if (stat(filename, &file_status) == 0)
2472 // check for typical filesize of a Supaplex level package file
2473 if (file_status.st_size == 170496)
2474 return LEVEL_FILE_TYPE_SP;
2477 return LEVEL_FILE_TYPE_UNKNOWN;
2480 static int getFileTypeFromMagicBytes(char *filename, int type)
2484 if ((file = openFile(filename, MODE_READ)))
2486 char chunk_name[CHUNK_ID_LEN + 1];
2488 getFileChunkBE(file, chunk_name, NULL);
2490 if (strEqual(chunk_name, "MMII") ||
2491 strEqual(chunk_name, "MIRR"))
2492 type = LEVEL_FILE_TYPE_MM;
2500 static boolean checkForPackageFromBasename(char *basename)
2502 // !!! WON'T WORK ANYMORE IF getFileTypeFromBasename() ALSO DETECTS !!!
2503 // !!! SINGLE LEVELS (CURRENTLY ONLY DETECTS LEVEL PACKAGES !!!
2505 return (getFileTypeFromBasename(basename) != LEVEL_FILE_TYPE_UNKNOWN);
2508 static char *getSingleLevelBasenameExt(int nr, char *extension)
2510 static char basename[MAX_FILENAME_LEN];
2513 sprintf(basename, "%s", LEVELTEMPLATE_FILENAME);
2515 sprintf(basename, "%03d.%s", nr, extension);
2520 static char *getSingleLevelBasename(int nr)
2522 return getSingleLevelBasenameExt(nr, LEVELFILE_EXTENSION);
2525 static char *getPackedLevelBasename(int type)
2527 static char basename[MAX_FILENAME_LEN];
2528 char *directory = getCurrentLevelDir();
2530 DirectoryEntry *dir_entry;
2532 strcpy(basename, UNDEFINED_FILENAME); // default: undefined file
2534 if ((dir = openDirectory(directory)) == NULL)
2536 Warn("cannot read current level directory '%s'", directory);
2541 while ((dir_entry = readDirectory(dir)) != NULL) // loop all entries
2543 char *entry_basename = dir_entry->basename;
2544 int entry_type = getFileTypeFromBasename(entry_basename);
2546 if (entry_type != LEVEL_FILE_TYPE_UNKNOWN) // found valid level package
2548 if (type == LEVEL_FILE_TYPE_UNKNOWN ||
2551 strcpy(basename, entry_basename);
2558 closeDirectory(dir);
2563 static char *getSingleLevelFilename(int nr)
2565 return getLevelFilenameFromBasename(getSingleLevelBasename(nr));
2568 #if ENABLE_UNUSED_CODE
2569 static char *getPackedLevelFilename(int type)
2571 return getLevelFilenameFromBasename(getPackedLevelBasename(type));
2575 char *getDefaultLevelFilename(int nr)
2577 return getSingleLevelFilename(nr);
2580 #if ENABLE_UNUSED_CODE
2581 static void setLevelFileInfo_SingleLevelFilename(struct LevelFileInfo *lfi,
2585 lfi->packed = FALSE;
2587 setString(&lfi->basename, getSingleLevelBasename(lfi->nr, lfi->type));
2588 setString(&lfi->filename, getLevelFilenameFromBasename(lfi->basename));
2592 static void setLevelFileInfo_FormatLevelFilename(struct LevelFileInfo *lfi,
2593 int type, char *format, ...)
2595 static char basename[MAX_FILENAME_LEN];
2598 va_start(ap, format);
2599 vsprintf(basename, format, ap);
2603 lfi->packed = FALSE;
2605 setString(&lfi->basename, basename);
2606 setString(&lfi->filename, getLevelFilenameFromBasename(lfi->basename));
2609 static void setLevelFileInfo_PackedLevelFilename(struct LevelFileInfo *lfi,
2615 setString(&lfi->basename, getPackedLevelBasename(lfi->type));
2616 setString(&lfi->filename, getLevelFilenameFromBasename(lfi->basename));
2619 static int getFiletypeFromID(char *filetype_id)
2621 char *filetype_id_lower;
2622 int filetype = LEVEL_FILE_TYPE_UNKNOWN;
2625 if (filetype_id == NULL)
2626 return LEVEL_FILE_TYPE_UNKNOWN;
2628 filetype_id_lower = getStringToLower(filetype_id);
2630 for (i = 0; filetype_id_list[i].id != NULL; i++)
2632 char *id_lower = getStringToLower(filetype_id_list[i].id);
2634 if (strEqual(filetype_id_lower, id_lower))
2635 filetype = filetype_id_list[i].filetype;
2639 if (filetype != LEVEL_FILE_TYPE_UNKNOWN)
2643 free(filetype_id_lower);
2648 char *getLocalLevelTemplateFilename(void)
2650 return getDefaultLevelFilename(-1);
2653 char *getGlobalLevelTemplateFilename(void)
2655 // global variable "leveldir_current" must be modified in the loop below
2656 LevelDirTree *leveldir_current_last = leveldir_current;
2657 char *filename = NULL;
2659 // check for template level in path from current to topmost tree node
2661 while (leveldir_current != NULL)
2663 filename = getDefaultLevelFilename(-1);
2665 if (fileExists(filename))
2668 leveldir_current = leveldir_current->node_parent;
2671 // restore global variable "leveldir_current" modified in above loop
2672 leveldir_current = leveldir_current_last;
2677 static void determineLevelFileInfo_Filename(struct LevelFileInfo *lfi)
2681 // special case: level number is negative => check for level template file
2684 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
2685 getSingleLevelBasename(-1));
2687 // replace local level template filename with global template filename
2688 setString(&lfi->filename, getGlobalLevelTemplateFilename());
2690 // no fallback if template file not existing
2694 // special case: check for file name/pattern specified in "levelinfo.conf"
2695 if (leveldir_current->level_filename != NULL)
2697 int filetype = getFiletypeFromID(leveldir_current->level_filetype);
2699 setLevelFileInfo_FormatLevelFilename(lfi, filetype,
2700 leveldir_current->level_filename, nr);
2702 lfi->packed = checkForPackageFromBasename(leveldir_current->level_filename);
2704 if (fileExists(lfi->filename))
2707 else if (leveldir_current->level_filetype != NULL)
2709 int filetype = getFiletypeFromID(leveldir_current->level_filetype);
2711 // check for specified native level file with standard file name
2712 setLevelFileInfo_FormatLevelFilename(lfi, filetype,
2713 "%03d.%s", nr, LEVELFILE_EXTENSION);
2714 if (fileExists(lfi->filename))
2718 // check for native Rocks'n'Diamonds level file
2719 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
2720 "%03d.%s", nr, LEVELFILE_EXTENSION);
2721 if (fileExists(lfi->filename))
2724 // check for native Boulder Dash level file
2725 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_BD, "%03d.bd", nr);
2726 if (fileExists(lfi->filename))
2729 // check for Emerald Mine level file (V1)
2730 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "a%c%c",
2731 'a' + (nr / 10) % 26, '0' + nr % 10);
2732 if (fileExists(lfi->filename))
2734 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "A%c%c",
2735 'A' + (nr / 10) % 26, '0' + nr % 10);
2736 if (fileExists(lfi->filename))
2739 // check for Emerald Mine level file (V2 to V5)
2740 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%d", nr);
2741 if (fileExists(lfi->filename))
2744 // check for Emerald Mine level file (V6 / single mode)
2745 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02ds", nr);
2746 if (fileExists(lfi->filename))
2748 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02dS", nr);
2749 if (fileExists(lfi->filename))
2752 // check for Emerald Mine level file (V6 / teamwork mode)
2753 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02dt", nr);
2754 if (fileExists(lfi->filename))
2756 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02dT", nr);
2757 if (fileExists(lfi->filename))
2760 // check for various packed level file formats
2761 setLevelFileInfo_PackedLevelFilename(lfi, LEVEL_FILE_TYPE_UNKNOWN);
2762 if (fileExists(lfi->filename))
2765 // no known level file found -- use default values (and fail later)
2766 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
2767 "%03d.%s", nr, LEVELFILE_EXTENSION);
2770 static void determineLevelFileInfo_Filetype(struct LevelFileInfo *lfi)
2772 if (lfi->type == LEVEL_FILE_TYPE_UNKNOWN)
2773 lfi->type = getFileTypeFromBasename(lfi->basename);
2775 if (lfi->type == LEVEL_FILE_TYPE_RND)
2776 lfi->type = getFileTypeFromMagicBytes(lfi->filename, lfi->type);
2779 static void setLevelFileInfo(struct LevelFileInfo *level_file_info, int nr)
2781 // always start with reliable default values
2782 setFileInfoToDefaults(level_file_info);
2784 level_file_info->nr = nr; // set requested level number
2786 determineLevelFileInfo_Filename(level_file_info);
2787 determineLevelFileInfo_Filetype(level_file_info);
2790 static void copyLevelFileInfo(struct LevelFileInfo *lfi_from,
2791 struct LevelFileInfo *lfi_to)
2793 lfi_to->nr = lfi_from->nr;
2794 lfi_to->type = lfi_from->type;
2795 lfi_to->packed = lfi_from->packed;
2797 setString(&lfi_to->basename, lfi_from->basename);
2798 setString(&lfi_to->filename, lfi_from->filename);
2801 // ----------------------------------------------------------------------------
2802 // functions for loading R'n'D level
2803 // ----------------------------------------------------------------------------
2805 int getMappedElement(int element)
2807 // remap some (historic, now obsolete) elements
2811 case EL_PLAYER_OBSOLETE:
2812 element = EL_PLAYER_1;
2815 case EL_KEY_OBSOLETE:
2819 case EL_EM_KEY_1_FILE_OBSOLETE:
2820 element = EL_EM_KEY_1;
2823 case EL_EM_KEY_2_FILE_OBSOLETE:
2824 element = EL_EM_KEY_2;
2827 case EL_EM_KEY_3_FILE_OBSOLETE:
2828 element = EL_EM_KEY_3;
2831 case EL_EM_KEY_4_FILE_OBSOLETE:
2832 element = EL_EM_KEY_4;
2835 case EL_ENVELOPE_OBSOLETE:
2836 element = EL_ENVELOPE_1;
2844 if (element >= NUM_FILE_ELEMENTS)
2846 Warn("invalid level element %d", element);
2848 element = EL_UNKNOWN;
2856 static int getMappedElementByVersion(int element, int game_version)
2858 // remap some elements due to certain game version
2860 if (game_version <= VERSION_IDENT(2,2,0,0))
2862 // map game font elements
2863 element = (element == EL_CHAR('[') ? EL_CHAR_AUMLAUT :
2864 element == EL_CHAR('\\') ? EL_CHAR_OUMLAUT :
2865 element == EL_CHAR(']') ? EL_CHAR_UUMLAUT :
2866 element == EL_CHAR('^') ? EL_CHAR_COPYRIGHT : element);
2869 if (game_version < VERSION_IDENT(3,0,0,0))
2871 // map Supaplex gravity tube elements
2872 element = (element == EL_SP_GRAVITY_PORT_LEFT ? EL_SP_PORT_LEFT :
2873 element == EL_SP_GRAVITY_PORT_RIGHT ? EL_SP_PORT_RIGHT :
2874 element == EL_SP_GRAVITY_PORT_UP ? EL_SP_PORT_UP :
2875 element == EL_SP_GRAVITY_PORT_DOWN ? EL_SP_PORT_DOWN :
2882 static int LoadLevel_VERS(File *file, int chunk_size, struct LevelInfo *level)
2884 level->file_version = getFileVersion(file);
2885 level->game_version = getFileVersion(file);
2890 static int LoadLevel_DATE(File *file, int chunk_size, struct LevelInfo *level)
2892 level->creation_date.year = getFile16BitBE(file);
2893 level->creation_date.month = getFile8Bit(file);
2894 level->creation_date.day = getFile8Bit(file);
2896 level->creation_date.src = DATE_SRC_LEVELFILE;
2901 static int LoadLevel_HEAD(File *file, int chunk_size, struct LevelInfo *level)
2903 int initial_player_stepsize;
2904 int initial_player_gravity;
2907 level->fieldx = getFile8Bit(file);
2908 level->fieldy = getFile8Bit(file);
2910 level->time = getFile16BitBE(file);
2911 level->gems_needed = getFile16BitBE(file);
2913 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
2914 level->name[i] = getFile8Bit(file);
2915 level->name[MAX_LEVEL_NAME_LEN] = 0;
2917 for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
2918 level->score[i] = getFile8Bit(file);
2920 level->num_yamyam_contents = STD_ELEMENT_CONTENTS;
2921 for (i = 0; i < STD_ELEMENT_CONTENTS; i++)
2922 for (y = 0; y < 3; y++)
2923 for (x = 0; x < 3; x++)
2924 level->yamyam_content[i].e[x][y] = getMappedElement(getFile8Bit(file));
2926 level->amoeba_speed = getFile8Bit(file);
2927 level->time_magic_wall = getFile8Bit(file);
2928 level->time_wheel = getFile8Bit(file);
2929 level->amoeba_content = getMappedElement(getFile8Bit(file));
2931 initial_player_stepsize = (getFile8Bit(file) == 1 ? STEPSIZE_FAST :
2934 for (i = 0; i < MAX_PLAYERS; i++)
2935 level->initial_player_stepsize[i] = initial_player_stepsize;
2937 initial_player_gravity = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2939 for (i = 0; i < MAX_PLAYERS; i++)
2940 level->initial_player_gravity[i] = initial_player_gravity;
2942 level->encoding_16bit_field = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2943 level->em_slippery_gems = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2945 level->use_custom_template = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2947 level->block_last_field = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2948 level->sp_block_last_field = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2949 level->can_move_into_acid_bits = getFile32BitBE(file);
2950 level->dont_collide_with_bits = getFile8Bit(file);
2952 level->use_spring_bug = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2953 level->use_step_counter = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2955 level->instant_relocation = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2956 level->can_pass_to_walkable = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2957 level->grow_into_diggable = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2959 level->game_engine_type = getFile8Bit(file);
2961 ReadUnusedBytesFromFile(file, LEVEL_CHUNK_HEAD_UNUSED);
2966 static int LoadLevel_NAME(File *file, int chunk_size, struct LevelInfo *level)
2970 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
2971 level->name[i] = getFile8Bit(file);
2972 level->name[MAX_LEVEL_NAME_LEN] = 0;
2977 static int LoadLevel_AUTH(File *file, int chunk_size, struct LevelInfo *level)
2981 for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
2982 level->author[i] = getFile8Bit(file);
2983 level->author[MAX_LEVEL_AUTHOR_LEN] = 0;
2988 static int LoadLevel_BODY(File *file, int chunk_size, struct LevelInfo *level)
2991 int chunk_size_expected = level->fieldx * level->fieldy;
2993 /* Note: "chunk_size" was wrong before version 2.0 when elements are
2994 stored with 16-bit encoding (and should be twice as big then).
2995 Even worse, playfield data was stored 16-bit when only yamyam content
2996 contained 16-bit elements and vice versa. */
2998 if (level->encoding_16bit_field && level->file_version >= FILE_VERSION_2_0)
2999 chunk_size_expected *= 2;
3001 if (chunk_size_expected != chunk_size)
3003 ReadUnusedBytesFromFile(file, chunk_size);
3004 return chunk_size_expected;
3007 for (y = 0; y < level->fieldy; y++)
3008 for (x = 0; x < level->fieldx; x++)
3009 level->field[x][y] =
3010 getMappedElement(level->encoding_16bit_field ? getFile16BitBE(file) :
3015 static int LoadLevel_CONT(File *file, int chunk_size, struct LevelInfo *level)
3018 int header_size = 4;
3019 int content_size = MAX_ELEMENT_CONTENTS * 3 * 3;
3020 int chunk_size_expected = header_size + content_size;
3022 /* Note: "chunk_size" was wrong before version 2.0 when elements are
3023 stored with 16-bit encoding (and should be twice as big then).
3024 Even worse, playfield data was stored 16-bit when only yamyam content
3025 contained 16-bit elements and vice versa. */
3027 if (level->encoding_16bit_field && level->file_version >= FILE_VERSION_2_0)
3028 chunk_size_expected += content_size;
3030 if (chunk_size_expected != chunk_size)
3032 ReadUnusedBytesFromFile(file, chunk_size);
3033 return chunk_size_expected;
3037 level->num_yamyam_contents = getFile8Bit(file);
3041 // correct invalid number of content fields -- should never happen
3042 if (level->num_yamyam_contents < 1 ||
3043 level->num_yamyam_contents > MAX_ELEMENT_CONTENTS)
3044 level->num_yamyam_contents = STD_ELEMENT_CONTENTS;
3046 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
3047 for (y = 0; y < 3; y++)
3048 for (x = 0; x < 3; x++)
3049 level->yamyam_content[i].e[x][y] =
3050 getMappedElement(level->encoding_16bit_field ?
3051 getFile16BitBE(file) : getFile8Bit(file));
3055 static int LoadLevel_CNT2(File *file, int chunk_size, struct LevelInfo *level)
3060 int content_array[MAX_ELEMENT_CONTENTS][3][3];
3062 element = getMappedElement(getFile16BitBE(file));
3063 num_contents = getFile8Bit(file);
3065 getFile8Bit(file); // content x size (unused)
3066 getFile8Bit(file); // content y size (unused)
3068 ReadUnusedBytesFromFile(file, LEVEL_CHUNK_CNT2_UNUSED);
3070 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
3071 for (y = 0; y < 3; y++)
3072 for (x = 0; x < 3; x++)
3073 content_array[i][x][y] = getMappedElement(getFile16BitBE(file));
3075 // correct invalid number of content fields -- should never happen
3076 if (num_contents < 1 || num_contents > MAX_ELEMENT_CONTENTS)
3077 num_contents = STD_ELEMENT_CONTENTS;
3079 if (element == EL_YAMYAM)
3081 level->num_yamyam_contents = num_contents;
3083 for (i = 0; i < num_contents; i++)
3084 for (y = 0; y < 3; y++)
3085 for (x = 0; x < 3; x++)
3086 level->yamyam_content[i].e[x][y] = content_array[i][x][y];
3088 else if (element == EL_BD_AMOEBA)
3090 level->amoeba_content = content_array[0][0][0];
3094 Warn("cannot load content for element '%d'", element);
3100 static int LoadLevel_CNT3(File *file, int chunk_size, struct LevelInfo *level)
3106 int chunk_size_expected;
3108 element = getMappedElement(getFile16BitBE(file));
3109 if (!IS_ENVELOPE(element))
3110 element = EL_ENVELOPE_1;
3112 envelope_nr = element - EL_ENVELOPE_1;
3114 envelope_len = getFile16BitBE(file);
3116 level->envelope[envelope_nr].xsize = getFile8Bit(file);
3117 level->envelope[envelope_nr].ysize = getFile8Bit(file);
3119 ReadUnusedBytesFromFile(file, LEVEL_CHUNK_CNT3_UNUSED);
3121 chunk_size_expected = LEVEL_CHUNK_CNT3_SIZE(envelope_len);
3122 if (chunk_size_expected != chunk_size)
3124 ReadUnusedBytesFromFile(file, chunk_size - LEVEL_CHUNK_CNT3_HEADER);
3125 return chunk_size_expected;
3128 for (i = 0; i < envelope_len; i++)
3129 level->envelope[envelope_nr].text[i] = getFile8Bit(file);
3134 static int LoadLevel_CUS1(File *file, int chunk_size, struct LevelInfo *level)
3136 int num_changed_custom_elements = getFile16BitBE(file);
3137 int chunk_size_expected = 2 + num_changed_custom_elements * 6;
3140 if (chunk_size_expected != chunk_size)
3142 ReadUnusedBytesFromFile(file, chunk_size - 2);
3143 return chunk_size_expected;
3146 for (i = 0; i < num_changed_custom_elements; i++)
3148 int element = getMappedElement(getFile16BitBE(file));
3149 int properties = getFile32BitBE(file);
3151 if (IS_CUSTOM_ELEMENT(element))
3152 element_info[element].properties[EP_BITFIELD_BASE_NR] = properties;
3154 Warn("invalid custom element number %d", element);
3156 // older game versions that wrote level files with CUS1 chunks used
3157 // different default push delay values (not yet stored in level file)
3158 element_info[element].push_delay_fixed = 2;
3159 element_info[element].push_delay_random = 8;
3162 level->file_has_custom_elements = TRUE;
3167 static int LoadLevel_CUS2(File *file, int chunk_size, struct LevelInfo *level)
3169 int num_changed_custom_elements = getFile16BitBE(file);
3170 int chunk_size_expected = 2 + num_changed_custom_elements * 4;
3173 if (chunk_size_expected != chunk_size)
3175 ReadUnusedBytesFromFile(file, chunk_size - 2);
3176 return chunk_size_expected;
3179 for (i = 0; i < num_changed_custom_elements; i++)
3181 int element = getMappedElement(getFile16BitBE(file));
3182 int custom_target_element = getMappedElement(getFile16BitBE(file));
3184 if (IS_CUSTOM_ELEMENT(element))
3185 element_info[element].change->target_element = custom_target_element;
3187 Warn("invalid custom element number %d", element);
3190 level->file_has_custom_elements = TRUE;
3195 static int LoadLevel_CUS3(File *file, int chunk_size, struct LevelInfo *level)
3197 int num_changed_custom_elements = getFile16BitBE(file);
3198 int chunk_size_expected = LEVEL_CHUNK_CUS3_SIZE(num_changed_custom_elements);
3201 if (chunk_size_expected != chunk_size)
3203 ReadUnusedBytesFromFile(file, chunk_size - 2);
3204 return chunk_size_expected;
3207 for (i = 0; i < num_changed_custom_elements; i++)
3209 int element = getMappedElement(getFile16BitBE(file));
3210 struct ElementInfo *ei = &element_info[element];
3211 unsigned int event_bits;
3213 if (!IS_CUSTOM_ELEMENT(element))
3215 Warn("invalid custom element number %d", element);
3217 element = EL_INTERNAL_DUMMY;
3220 for (j = 0; j < MAX_ELEMENT_NAME_LEN; j++)
3221 ei->description[j] = getFile8Bit(file);
3222 ei->description[MAX_ELEMENT_NAME_LEN] = 0;
3224 ei->properties[EP_BITFIELD_BASE_NR] = getFile32BitBE(file);
3226 // some free bytes for future properties and padding
3227 ReadUnusedBytesFromFile(file, 7);
3229 ei->use_gfx_element = getFile8Bit(file);
3230 ei->gfx_element_initial = getMappedElement(getFile16BitBE(file));
3232 ei->collect_score_initial = getFile8Bit(file);
3233 ei->collect_count_initial = getFile8Bit(file);
3235 ei->push_delay_fixed = getFile16BitBE(file);
3236 ei->push_delay_random = getFile16BitBE(file);
3237 ei->move_delay_fixed = getFile16BitBE(file);
3238 ei->move_delay_random = getFile16BitBE(file);
3240 ei->move_pattern = getFile16BitBE(file);
3241 ei->move_direction_initial = getFile8Bit(file);
3242 ei->move_stepsize = getFile8Bit(file);
3244 for (y = 0; y < 3; y++)
3245 for (x = 0; x < 3; x++)
3246 ei->content.e[x][y] = getMappedElement(getFile16BitBE(file));
3248 // bits 0 - 31 of "has_event[]"
3249 event_bits = getFile32BitBE(file);
3250 for (j = 0; j < MIN(NUM_CHANGE_EVENTS, 32); j++)
3251 if (event_bits & (1u << j))
3252 ei->change->has_event[j] = TRUE;
3254 ei->change->target_element = getMappedElement(getFile16BitBE(file));
3256 ei->change->delay_fixed = getFile16BitBE(file);
3257 ei->change->delay_random = getFile16BitBE(file);
3258 ei->change->delay_frames = getFile16BitBE(file);
3260 ei->change->initial_trigger_element = getMappedElement(getFile16BitBE(file));
3262 ei->change->explode = getFile8Bit(file);
3263 ei->change->use_target_content = getFile8Bit(file);
3264 ei->change->only_if_complete = getFile8Bit(file);
3265 ei->change->use_random_replace = getFile8Bit(file);
3267 ei->change->random_percentage = getFile8Bit(file);
3268 ei->change->replace_when = getFile8Bit(file);
3270 for (y = 0; y < 3; y++)
3271 for (x = 0; x < 3; x++)
3272 ei->change->target_content.e[x][y] =
3273 getMappedElement(getFile16BitBE(file));
3275 ei->slippery_type = getFile8Bit(file);
3277 // some free bytes for future properties and padding
3278 ReadUnusedBytesFromFile(file, LEVEL_CPART_CUS3_UNUSED);
3280 // mark that this custom element has been modified
3281 ei->modified_settings = TRUE;
3284 level->file_has_custom_elements = TRUE;
3289 static int LoadLevel_CUS4(File *file, int chunk_size, struct LevelInfo *level)
3291 struct ElementInfo *ei;
3292 int chunk_size_expected;
3296 // ---------- custom element base property values (96 bytes) ----------------
3298 element = getMappedElement(getFile16BitBE(file));
3300 if (!IS_CUSTOM_ELEMENT(element))
3302 Warn("invalid custom element number %d", element);
3304 ReadUnusedBytesFromFile(file, chunk_size - 2);
3309 ei = &element_info[element];
3311 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
3312 ei->description[i] = getFile8Bit(file);
3313 ei->description[MAX_ELEMENT_NAME_LEN] = 0;
3315 ei->properties[EP_BITFIELD_BASE_NR] = getFile32BitBE(file);
3317 ReadUnusedBytesFromFile(file, 4); // reserved for more base properties
3319 ei->num_change_pages = getFile8Bit(file);
3321 chunk_size_expected = LEVEL_CHUNK_CUS4_SIZE(ei->num_change_pages);
3322 if (chunk_size_expected != chunk_size)
3324 ReadUnusedBytesFromFile(file, chunk_size - 43);
3325 return chunk_size_expected;
3328 ei->ce_value_fixed_initial = getFile16BitBE(file);
3329 ei->ce_value_random_initial = getFile16BitBE(file);
3330 ei->use_last_ce_value = getFile8Bit(file);
3332 ei->use_gfx_element = getFile8Bit(file);
3333 ei->gfx_element_initial = getMappedElement(getFile16BitBE(file));
3335 ei->collect_score_initial = getFile8Bit(file);
3336 ei->collect_count_initial = getFile8Bit(file);
3338 ei->drop_delay_fixed = getFile8Bit(file);
3339 ei->push_delay_fixed = getFile8Bit(file);
3340 ei->drop_delay_random = getFile8Bit(file);
3341 ei->push_delay_random = getFile8Bit(file);
3342 ei->move_delay_fixed = getFile16BitBE(file);
3343 ei->move_delay_random = getFile16BitBE(file);
3345 // bits 0 - 15 of "move_pattern" ...
3346 ei->move_pattern = getFile16BitBE(file);
3347 ei->move_direction_initial = getFile8Bit(file);
3348 ei->move_stepsize = getFile8Bit(file);
3350 ei->slippery_type = getFile8Bit(file);
3352 for (y = 0; y < 3; y++)
3353 for (x = 0; x < 3; x++)
3354 ei->content.e[x][y] = getMappedElement(getFile16BitBE(file));
3356 ei->move_enter_element = getMappedElement(getFile16BitBE(file));
3357 ei->move_leave_element = getMappedElement(getFile16BitBE(file));
3358 ei->move_leave_type = getFile8Bit(file);
3360 // ... bits 16 - 31 of "move_pattern" (not nice, but downward compatible)
3361 ei->move_pattern |= (getFile16BitBE(file) << 16);
3363 ei->access_direction = getFile8Bit(file);
3365 ei->explosion_delay = getFile8Bit(file);
3366 ei->ignition_delay = getFile8Bit(file);
3367 ei->explosion_type = getFile8Bit(file);
3369 // some free bytes for future custom property values and padding
3370 ReadUnusedBytesFromFile(file, 1);
3372 // ---------- change page property values (48 bytes) ------------------------
3374 setElementChangePages(ei, ei->num_change_pages);
3376 for (i = 0; i < ei->num_change_pages; i++)
3378 struct ElementChangeInfo *change = &ei->change_page[i];
3379 unsigned int event_bits;
3381 // always start with reliable default values
3382 setElementChangeInfoToDefaults(change);
3384 // bits 0 - 31 of "has_event[]" ...
3385 event_bits = getFile32BitBE(file);
3386 for (j = 0; j < MIN(NUM_CHANGE_EVENTS, 32); j++)
3387 if (event_bits & (1u << j))
3388 change->has_event[j] = TRUE;
3390 change->target_element = getMappedElement(getFile16BitBE(file));
3392 change->delay_fixed = getFile16BitBE(file);
3393 change->delay_random = getFile16BitBE(file);
3394 change->delay_frames = getFile16BitBE(file);
3396 change->initial_trigger_element = getMappedElement(getFile16BitBE(file));
3398 change->explode = getFile8Bit(file);
3399 change->use_target_content = getFile8Bit(file);
3400 change->only_if_complete = getFile8Bit(file);
3401 change->use_random_replace = getFile8Bit(file);
3403 change->random_percentage = getFile8Bit(file);
3404 change->replace_when = getFile8Bit(file);
3406 for (y = 0; y < 3; y++)
3407 for (x = 0; x < 3; x++)
3408 change->target_content.e[x][y]= getMappedElement(getFile16BitBE(file));
3410 change->can_change = getFile8Bit(file);
3412 change->trigger_side = getFile8Bit(file);
3414 change->trigger_player = getFile8Bit(file);
3415 change->trigger_page = getFile8Bit(file);
3417 change->trigger_page = (change->trigger_page == CH_PAGE_ANY_FILE ?
3418 CH_PAGE_ANY : (1 << change->trigger_page));
3420 change->has_action = getFile8Bit(file);
3421 change->action_type = getFile8Bit(file);
3422 change->action_mode = getFile8Bit(file);
3423 change->action_arg = getFile16BitBE(file);
3425 // ... bits 32 - 39 of "has_event[]" (not nice, but downward compatible)
3426 event_bits = getFile8Bit(file);
3427 for (j = 32; j < NUM_CHANGE_EVENTS; j++)
3428 if (event_bits & (1u << (j - 32)))
3429 change->has_event[j] = TRUE;
3432 // mark this custom element as modified
3433 ei->modified_settings = TRUE;
3435 level->file_has_custom_elements = TRUE;
3440 static int LoadLevel_GRP1(File *file, int chunk_size, struct LevelInfo *level)
3442 struct ElementInfo *ei;
3443 struct ElementGroupInfo *group;
3447 element = getMappedElement(getFile16BitBE(file));
3449 if (!IS_GROUP_ELEMENT(element))
3451 Warn("invalid group element number %d", element);
3453 ReadUnusedBytesFromFile(file, chunk_size - 2);
3458 ei = &element_info[element];
3460 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
3461 ei->description[i] = getFile8Bit(file);
3462 ei->description[MAX_ELEMENT_NAME_LEN] = 0;
3464 group = element_info[element].group;
3466 group->num_elements = getFile8Bit(file);
3468 ei->use_gfx_element = getFile8Bit(file);
3469 ei->gfx_element_initial = getMappedElement(getFile16BitBE(file));
3471 group->choice_mode = getFile8Bit(file);
3473 // some free bytes for future values and padding
3474 ReadUnusedBytesFromFile(file, 3);
3476 for (i = 0; i < MAX_ELEMENTS_IN_GROUP; i++)
3477 group->element[i] = getMappedElement(getFile16BitBE(file));
3479 // mark this group element as modified
3480 element_info[element].modified_settings = TRUE;
3482 level->file_has_custom_elements = TRUE;
3487 static int LoadLevel_MicroChunk(File *file, struct LevelFileConfigInfo *conf,
3488 int element, int real_element)
3490 int micro_chunk_size = 0;
3491 int conf_type = getFile8Bit(file);
3492 int byte_mask = conf_type & CONF_MASK_BYTES;
3493 boolean element_found = FALSE;
3496 micro_chunk_size += 1;
3498 if (byte_mask == CONF_MASK_MULTI_BYTES)
3500 int num_bytes = getFile16BitBE(file);
3501 byte *buffer = checked_malloc(num_bytes);
3503 ReadBytesFromFile(file, buffer, num_bytes);
3505 for (i = 0; conf[i].data_type != -1; i++)
3507 if (conf[i].element == element &&
3508 conf[i].conf_type == conf_type)
3510 int data_type = conf[i].data_type;
3511 int num_entities = num_bytes / CONF_ENTITY_NUM_BYTES(data_type);
3512 int max_num_entities = conf[i].max_num_entities;
3514 if (num_entities > max_num_entities)
3516 Warn("truncating number of entities for element %d from %d to %d",
3517 element, num_entities, max_num_entities);
3519 num_entities = max_num_entities;
3522 if (num_entities == 0 && (data_type == TYPE_ELEMENT_LIST ||
3523 data_type == TYPE_CONTENT_LIST))
3525 // for element and content lists, zero entities are not allowed
3526 Warn("found empty list of entities for element %d", element);
3528 // do not set "num_entities" here to prevent reading behind buffer
3530 *(int *)(conf[i].num_entities) = 1; // at least one is required
3534 *(int *)(conf[i].num_entities) = num_entities;
3537 element_found = TRUE;
3539 if (data_type == TYPE_STRING)
3541 char *string = (char *)(conf[i].value);
3544 for (j = 0; j < max_num_entities; j++)
3545 string[j] = (j < num_entities ? buffer[j] : '\0');
3547 else if (data_type == TYPE_ELEMENT_LIST)
3549 int *element_array = (int *)(conf[i].value);
3552 for (j = 0; j < num_entities; j++)
3554 getMappedElement(CONF_ELEMENTS_ELEMENT(buffer, j));
3556 else if (data_type == TYPE_CONTENT_LIST)
3558 struct Content *content= (struct Content *)(conf[i].value);
3561 for (c = 0; c < num_entities; c++)
3562 for (y = 0; y < 3; y++)
3563 for (x = 0; x < 3; x++)
3564 content[c].e[x][y] =
3565 getMappedElement(CONF_CONTENTS_ELEMENT(buffer, c, x, y));
3568 element_found = FALSE;
3574 checked_free(buffer);
3576 micro_chunk_size += 2 + num_bytes;
3578 else // constant size configuration data (1, 2 or 4 bytes)
3580 int value = (byte_mask == CONF_MASK_1_BYTE ? getFile8Bit (file) :
3581 byte_mask == CONF_MASK_2_BYTE ? getFile16BitBE(file) :
3582 byte_mask == CONF_MASK_4_BYTE ? getFile32BitBE(file) : 0);
3584 for (i = 0; conf[i].data_type != -1; i++)
3586 if (conf[i].element == element &&
3587 conf[i].conf_type == conf_type)
3589 int data_type = conf[i].data_type;
3591 if (data_type == TYPE_ELEMENT)
3592 value = getMappedElement(value);
3594 if (data_type == TYPE_BOOLEAN)
3595 *(boolean *)(conf[i].value) = (value ? TRUE : FALSE);
3597 *(int *) (conf[i].value) = value;
3599 element_found = TRUE;
3605 micro_chunk_size += CONF_VALUE_NUM_BYTES(byte_mask);
3610 char *error_conf_chunk_bytes =
3611 (byte_mask == CONF_MASK_1_BYTE ? "CONF_VALUE_8_BIT" :
3612 byte_mask == CONF_MASK_2_BYTE ? "CONF_VALUE_16_BIT" :
3613 byte_mask == CONF_MASK_4_BYTE ? "CONF_VALUE_32_BIT" :"CONF_VALUE_BYTES");
3614 int error_conf_chunk_token = conf_type & CONF_MASK_TOKEN;
3615 int error_element = real_element;
3617 Warn("cannot load micro chunk '%s(%d)' value for element %d ['%s']",
3618 error_conf_chunk_bytes, error_conf_chunk_token,
3619 error_element, EL_NAME(error_element));
3622 return micro_chunk_size;
3625 static int LoadLevel_INFO(File *file, int chunk_size, struct LevelInfo *level)
3627 int real_chunk_size = 0;
3629 li = *level; // copy level data into temporary buffer
3631 while (!checkEndOfFile(file))
3633 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_INFO, -1, -1);
3635 if (real_chunk_size >= chunk_size)
3639 *level = li; // copy temporary buffer back to level data
3641 return real_chunk_size;
3644 static int LoadLevel_CONF(File *file, int chunk_size, struct LevelInfo *level)
3646 int real_chunk_size = 0;
3648 li = *level; // copy level data into temporary buffer
3650 while (!checkEndOfFile(file))
3652 int element = getMappedElement(getFile16BitBE(file));
3654 real_chunk_size += 2;
3655 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_CONF,
3657 if (real_chunk_size >= chunk_size)
3661 *level = li; // copy temporary buffer back to level data
3663 return real_chunk_size;
3666 static int LoadLevel_ELEM(File *file, int chunk_size, struct LevelInfo *level)
3668 int real_chunk_size = 0;
3670 li = *level; // copy level data into temporary buffer
3672 while (!checkEndOfFile(file))
3674 int element = getMappedElement(getFile16BitBE(file));
3676 real_chunk_size += 2;
3677 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_ELEM,
3679 if (real_chunk_size >= chunk_size)
3683 *level = li; // copy temporary buffer back to level data
3685 return real_chunk_size;
3688 static int LoadLevel_NOTE(File *file, int chunk_size, struct LevelInfo *level)
3690 int element = getMappedElement(getFile16BitBE(file));
3691 int envelope_nr = element - EL_ENVELOPE_1;
3692 int real_chunk_size = 2;
3694 xx_envelope = level->envelope[envelope_nr]; // copy into temporary buffer
3696 while (!checkEndOfFile(file))
3698 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_NOTE,
3701 if (real_chunk_size >= chunk_size)
3705 level->envelope[envelope_nr] = xx_envelope; // copy from temporary buffer
3707 return real_chunk_size;
3710 static int LoadLevel_CUSX(File *file, int chunk_size, struct LevelInfo *level)
3712 int element = getMappedElement(getFile16BitBE(file));
3713 int real_chunk_size = 2;
3714 struct ElementInfo *ei = &element_info[element];
3717 xx_ei = *ei; // copy element data into temporary buffer
3719 xx_ei.num_change_pages = -1;
3721 while (!checkEndOfFile(file))
3723 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_CUSX_base,
3725 if (xx_ei.num_change_pages != -1)
3728 if (real_chunk_size >= chunk_size)
3734 if (ei->num_change_pages == -1)
3736 Warn("LoadLevel_CUSX(): missing 'num_change_pages' for '%s'",
3739 ei->num_change_pages = 1;
3741 setElementChangePages(ei, 1);
3742 setElementChangeInfoToDefaults(ei->change);
3744 return real_chunk_size;
3747 // initialize number of change pages stored for this custom element
3748 setElementChangePages(ei, ei->num_change_pages);
3749 for (i = 0; i < ei->num_change_pages; i++)
3750 setElementChangeInfoToDefaults(&ei->change_page[i]);
3752 // start with reading properties for the first change page
3753 xx_current_change_page = 0;
3755 while (!checkEndOfFile(file))
3757 // level file might contain invalid change page number
3758 if (xx_current_change_page >= ei->num_change_pages)
3761 struct ElementChangeInfo *change = &ei->change_page[xx_current_change_page];
3763 xx_change = *change; // copy change data into temporary buffer
3765 resetEventBits(); // reset bits; change page might have changed
3767 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_CUSX_change,
3770 *change = xx_change;
3772 setEventFlagsFromEventBits(change);
3774 if (real_chunk_size >= chunk_size)
3778 level->file_has_custom_elements = TRUE;
3780 return real_chunk_size;
3783 static int LoadLevel_GRPX(File *file, int chunk_size, struct LevelInfo *level)
3785 int element = getMappedElement(getFile16BitBE(file));
3786 int real_chunk_size = 2;
3787 struct ElementInfo *ei = &element_info[element];
3788 struct ElementGroupInfo *group = ei->group;
3793 xx_ei = *ei; // copy element data into temporary buffer
3794 xx_group = *group; // copy group data into temporary buffer
3796 while (!checkEndOfFile(file))
3798 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_GRPX,
3801 if (real_chunk_size >= chunk_size)
3808 level->file_has_custom_elements = TRUE;
3810 return real_chunk_size;
3813 static int LoadLevel_EMPX(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];
3819 xx_ei = *ei; // copy element data into temporary buffer
3821 while (!checkEndOfFile(file))
3823 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_EMPX,
3826 if (real_chunk_size >= chunk_size)
3832 level->file_has_custom_elements = TRUE;
3834 return real_chunk_size;
3837 static void LoadLevelFromFileInfo_RND(struct LevelInfo *level,
3838 struct LevelFileInfo *level_file_info,
3839 boolean level_info_only)
3841 char *filename = level_file_info->filename;
3842 char cookie[MAX_LINE_LEN];
3843 char chunk_name[CHUNK_ID_LEN + 1];
3847 if (!(file = openFile(filename, MODE_READ)))
3849 level->no_valid_file = TRUE;
3850 level->no_level_file = TRUE;
3852 if (level_info_only)
3855 Warn("cannot read level '%s' -- using empty level", filename);
3857 if (!setup.editor.use_template_for_new_levels)
3860 // if level file not found, try to initialize level data from template
3861 filename = getGlobalLevelTemplateFilename();
3863 if (!(file = openFile(filename, MODE_READ)))
3866 // default: for empty levels, use level template for custom elements
3867 level->use_custom_template = TRUE;
3869 level->no_valid_file = FALSE;
3872 getFileChunkBE(file, chunk_name, NULL);
3873 if (strEqual(chunk_name, "RND1"))
3875 getFile32BitBE(file); // not used
3877 getFileChunkBE(file, chunk_name, NULL);
3878 if (!strEqual(chunk_name, "CAVE"))
3880 level->no_valid_file = TRUE;
3882 Warn("unknown format of level file '%s'", filename);
3889 else // check for pre-2.0 file format with cookie string
3891 strcpy(cookie, chunk_name);
3892 if (getStringFromFile(file, &cookie[4], MAX_LINE_LEN - 4) == NULL)
3894 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
3895 cookie[strlen(cookie) - 1] = '\0';
3897 if (!checkCookieString(cookie, LEVEL_COOKIE_TMPL))
3899 level->no_valid_file = TRUE;
3901 Warn("unknown format of level file '%s'", filename);
3908 if ((level->file_version = getFileVersionFromCookieString(cookie)) == -1)
3910 level->no_valid_file = TRUE;
3912 Warn("unsupported version of level file '%s'", filename);
3919 // pre-2.0 level files have no game version, so use file version here
3920 level->game_version = level->file_version;
3923 if (level->file_version < FILE_VERSION_1_2)
3925 // level files from versions before 1.2.0 without chunk structure
3926 LoadLevel_HEAD(file, LEVEL_CHUNK_HEAD_SIZE, level);
3927 LoadLevel_BODY(file, level->fieldx * level->fieldy, level);
3935 int (*loader)(File *, int, struct LevelInfo *);
3939 { "VERS", LEVEL_CHUNK_VERS_SIZE, LoadLevel_VERS },
3940 { "DATE", LEVEL_CHUNK_DATE_SIZE, LoadLevel_DATE },
3941 { "HEAD", LEVEL_CHUNK_HEAD_SIZE, LoadLevel_HEAD },
3942 { "NAME", LEVEL_CHUNK_NAME_SIZE, LoadLevel_NAME },
3943 { "AUTH", LEVEL_CHUNK_AUTH_SIZE, LoadLevel_AUTH },
3944 { "INFO", -1, LoadLevel_INFO },
3945 { "BODY", -1, LoadLevel_BODY },
3946 { "CONT", -1, LoadLevel_CONT },
3947 { "CNT2", LEVEL_CHUNK_CNT2_SIZE, LoadLevel_CNT2 },
3948 { "CNT3", -1, LoadLevel_CNT3 },
3949 { "CUS1", -1, LoadLevel_CUS1 },
3950 { "CUS2", -1, LoadLevel_CUS2 },
3951 { "CUS3", -1, LoadLevel_CUS3 },
3952 { "CUS4", -1, LoadLevel_CUS4 },
3953 { "GRP1", -1, LoadLevel_GRP1 },
3954 { "CONF", -1, LoadLevel_CONF },
3955 { "ELEM", -1, LoadLevel_ELEM },
3956 { "NOTE", -1, LoadLevel_NOTE },
3957 { "CUSX", -1, LoadLevel_CUSX },
3958 { "GRPX", -1, LoadLevel_GRPX },
3959 { "EMPX", -1, LoadLevel_EMPX },
3964 while (getFileChunkBE(file, chunk_name, &chunk_size))
3968 while (chunk_info[i].name != NULL &&
3969 !strEqual(chunk_name, chunk_info[i].name))
3972 if (chunk_info[i].name == NULL)
3974 Warn("unknown chunk '%s' in level file '%s'",
3975 chunk_name, filename);
3977 ReadUnusedBytesFromFile(file, chunk_size);
3979 else if (chunk_info[i].size != -1 &&
3980 chunk_info[i].size != chunk_size)
3982 Warn("wrong size (%d) of chunk '%s' in level file '%s'",
3983 chunk_size, chunk_name, filename);
3985 ReadUnusedBytesFromFile(file, chunk_size);
3989 // call function to load this level chunk
3990 int chunk_size_expected =
3991 (chunk_info[i].loader)(file, chunk_size, level);
3993 if (chunk_size_expected < 0)
3995 Warn("error reading chunk '%s' in level file '%s'",
3996 chunk_name, filename);
4001 // the size of some chunks cannot be checked before reading other
4002 // chunks first (like "HEAD" and "BODY") that contain some header
4003 // information, so check them here
4004 if (chunk_size_expected != chunk_size)
4006 Warn("wrong size (%d) of chunk '%s' in level file '%s'",
4007 chunk_size, chunk_name, filename);
4019 // ----------------------------------------------------------------------------
4020 // functions for loading BD level
4021 // ----------------------------------------------------------------------------
4023 static void CopyNativeLevel_RND_to_BD(struct LevelInfo *level)
4025 struct LevelInfo_BD *level_bd = level->native_bd_level;
4026 GdCave *cave = NULL; // will be changed below
4027 int cave_w = MIN(level->fieldx, MAX_PLAYFIELD_WIDTH);
4028 int cave_h = MIN(level->fieldy, MAX_PLAYFIELD_HEIGHT);
4031 setLevelInfoToDefaults_BD_Ext(cave_w, cave_h);
4033 // cave and map newly allocated when set to defaults above
4034 cave = level_bd->cave;
4037 cave->intermission = level->bd_intermission;
4040 cave->level_time[0] = level->time;
4041 cave->level_diamonds[0] = level->gems_needed;
4044 cave->scheduling = level->bd_scheduling_type;
4045 cave->pal_timing = level->bd_pal_timing;
4046 cave->level_speed[0] = level->bd_cycle_delay_ms;
4047 cave->level_ckdelay[0] = level->bd_cycle_delay_c64;
4048 cave->level_hatching_delay_frame[0] = level->bd_hatching_delay_cycles;
4049 cave->level_hatching_delay_time[0] = level->bd_hatching_delay_seconds;
4052 cave->level_timevalue[0] = level->score[SC_TIME_BONUS];
4053 cave->diamond_value = level->score[SC_EMERALD];
4054 cave->extra_diamond_value = level->score[SC_DIAMOND_EXTRA];
4056 // compatibility settings
4057 cave->lineshift = level->bd_line_shifting_borders;
4058 cave->border_scan_first_and_last = level->bd_scan_first_and_last_row;
4059 cave->short_explosions = level->bd_short_explosions;
4060 cave->gravity_affects_all = level->bd_gravity_affects_all;
4062 // player properties
4063 cave->diagonal_movements = level->bd_diagonal_movements;
4064 cave->active_is_first_found = level->bd_topmost_player_active;
4065 cave->pushing_stone_prob = level->bd_pushing_prob * 10000;
4066 cave->pushing_stone_prob_sweet = level->bd_pushing_prob_with_sweet * 10000;
4067 cave->mega_stones_pushable_with_sweet = level->bd_push_mega_rock_with_sweet;
4068 cave->snap_element = map_element_RND_to_BD_cave(level->bd_snap_element);
4070 // element properties
4071 cave->level_bonus_time[0] = level->bd_clock_extra_time;
4072 cave->voodoo_collects_diamonds = level->bd_voodoo_collects_diamonds;
4073 cave->voodoo_any_hurt_kills_player = level->bd_voodoo_hurt_kills_player;
4074 cave->voodoo_dies_by_stone = level->bd_voodoo_dies_by_rock;
4075 cave->voodoo_disappear_in_explosion = level->bd_voodoo_vanish_by_explosion;
4076 cave->level_penalty_time[0] = level->bd_voodoo_penalty_time;
4077 cave->level_magic_wall_time[0] = level->time_magic_wall;
4078 cave->magic_timer_wait_for_hatching = level->bd_magic_wall_wait_hatching;
4079 cave->magic_wall_stops_amoeba = level->bd_magic_wall_stops_amoeba;
4081 cave->magic_diamond_to = map_element_RND_to_BD_cave(level->bd_magic_wall_diamond_to);
4082 cave->magic_stone_to = map_element_RND_to_BD_cave(level->bd_magic_wall_rock_to);
4083 cave->magic_mega_stone_to = map_element_RND_to_BD_cave(level->bd_magic_wall_mega_rock_to);
4084 cave->magic_nut_to = map_element_RND_to_BD_cave(level->bd_magic_wall_nut_to);
4085 cave->magic_nitro_pack_to = map_element_RND_to_BD_cave(level->bd_magic_wall_nitro_pack_to);
4086 cave->magic_flying_diamond_to = map_element_RND_to_BD_cave(level->bd_magic_wall_flying_diamond_to);
4087 cave->magic_flying_stone_to = map_element_RND_to_BD_cave(level->bd_magic_wall_flying_rock_to);
4089 cave->amoeba_timer_wait_for_hatching = level->bd_amoeba_wait_for_hatching;
4090 cave->amoeba_timer_started_immediately= level->bd_amoeba_start_immediately;
4091 cave->amoeba_2_explodes_by_amoeba = level->bd_amoeba_2_explode_by_amoeba;
4092 cave->level_amoeba_threshold[0] = level->bd_amoeba_threshold_too_big;
4093 cave->level_amoeba_time[0] = level->bd_amoeba_slow_growth_time;
4094 cave->amoeba_growth_prob = level->bd_amoeba_slow_growth_rate * 10000;
4095 cave->amoeba_fast_growth_prob = level->bd_amoeba_fast_growth_rate * 10000;
4096 cave->level_amoeba_2_threshold[0] = level->bd_amoeba_2_threshold_too_big;
4097 cave->level_amoeba_2_time[0] = level->bd_amoeba_2_slow_growth_time;
4098 cave->amoeba_2_growth_prob = level->bd_amoeba_2_slow_growth_rate * 10000;
4099 cave->amoeba_2_fast_growth_prob = level->bd_amoeba_2_fast_growth_rate * 10000;
4101 cave->amoeba_too_big_effect = map_element_RND_to_BD_cave(level->bd_amoeba_content_too_big);
4102 cave->amoeba_enclosed_effect = map_element_RND_to_BD_cave(level->bd_amoeba_content_enclosed);
4103 cave->amoeba_2_too_big_effect = map_element_RND_to_BD_cave(level->bd_amoeba_2_content_too_big);
4104 cave->amoeba_2_enclosed_effect = map_element_RND_to_BD_cave(level->bd_amoeba_2_content_enclosed);
4105 cave->amoeba_2_explosion_effect = map_element_RND_to_BD_cave(level->bd_amoeba_2_content_exploding);
4106 cave->amoeba_2_looks_like = map_element_RND_to_BD_cave(level->bd_amoeba_2_content_looks_like);
4108 cave->slime_predictable = level->bd_slime_is_predictable;
4109 cave->slime_correct_random = level->bd_slime_correct_random;
4110 cave->level_slime_permeability[0] = level->bd_slime_permeability_rate * 10000;
4111 cave->level_slime_permeability_c64[0] = level->bd_slime_permeability_bits_c64;
4112 cave->level_slime_seed_c64[0] = level->bd_slime_random_seed_c64;
4113 cave->level_rand[0] = level->bd_cave_random_seed_c64;
4115 cave->acid_eats_this = map_element_RND_to_BD_cave(level->bd_acid_eats_element);
4116 cave->acid_spread_ratio = level->bd_acid_spread_rate * 10000;
4117 cave->acid_turns_to = map_element_RND_to_BD_cave(level->bd_acid_turns_to_element);
4119 cave->biter_delay_frame = level->bd_biter_move_delay;
4120 cave->biter_eat = map_element_RND_to_BD_cave(level->bd_biter_eats_element);
4122 cave->bladder_converts_by = map_element_RND_to_BD_cave(level->bd_bladder_converts_by_element);
4124 cave->expanding_wall_changed = level->bd_change_expanding_wall;
4126 cave->replicators_active = level->bd_replicators_active;
4127 cave->replicator_delay_frame = level->bd_replicator_create_delay;
4129 cave->conveyor_belts_active = level->bd_conveyor_belts_active;
4130 cave->conveyor_belts_direction_changed= level->bd_conveyor_belts_changed;
4132 cave->water_does_not_flow_down = level->bd_water_cannot_flow_down;
4134 cave->nut_turns_to_when_crushed = map_element_RND_to_BD_cave(level->bd_nut_content);
4137 strncpy(cave->name, level->name, sizeof(GdString));
4138 cave->name[sizeof(GdString) - 1] = '\0';
4140 // playfield elements
4141 for (x = 0; x < cave->w; x++)
4142 for (y = 0; y < cave->h; y++)
4143 cave->map[y][x] = map_element_RND_to_BD_cave(level->field[x][y]);
4146 static void CopyNativeLevel_BD_to_RND(struct LevelInfo *level)
4148 struct LevelInfo_BD *level_bd = level->native_bd_level;
4149 GdCave *cave = level_bd->cave;
4150 int bd_level_nr = level_bd->level_nr;
4153 level->fieldx = MIN(cave->w, MAX_LEV_FIELDX);
4154 level->fieldy = MIN(cave->h, MAX_LEV_FIELDY);
4157 level->bd_intermission = cave->intermission;
4160 level->time = cave->level_time[bd_level_nr];
4161 level->gems_needed = cave->level_diamonds[bd_level_nr];
4164 level->bd_scheduling_type = cave->scheduling;
4165 level->bd_pal_timing = cave->pal_timing;
4166 level->bd_cycle_delay_ms = cave->level_speed[bd_level_nr];
4167 level->bd_cycle_delay_c64 = cave->level_ckdelay[bd_level_nr];
4168 level->bd_hatching_delay_cycles = cave->level_hatching_delay_frame[bd_level_nr];
4169 level->bd_hatching_delay_seconds = cave->level_hatching_delay_time[bd_level_nr];
4172 level->score[SC_TIME_BONUS] = cave->level_timevalue[bd_level_nr];
4173 level->score[SC_EMERALD] = cave->diamond_value;
4174 level->score[SC_DIAMOND_EXTRA] = cave->extra_diamond_value;
4176 // compatibility settings
4177 level->bd_line_shifting_borders = cave->lineshift;
4178 level->bd_scan_first_and_last_row = cave->border_scan_first_and_last;
4179 level->bd_short_explosions = cave->short_explosions;
4180 level->bd_gravity_affects_all = cave->gravity_affects_all;
4182 // player properties
4183 level->bd_diagonal_movements = cave->diagonal_movements;
4184 level->bd_topmost_player_active = cave->active_is_first_found;
4185 level->bd_pushing_prob = cave->pushing_stone_prob / 10000;
4186 level->bd_pushing_prob_with_sweet = cave->pushing_stone_prob_sweet / 10000;
4187 level->bd_push_mega_rock_with_sweet = cave->mega_stones_pushable_with_sweet;
4188 level->bd_snap_element = map_element_BD_to_RND_cave(cave->snap_element);
4190 // element properties
4191 level->bd_clock_extra_time = cave->level_bonus_time[bd_level_nr];
4192 level->bd_voodoo_collects_diamonds = cave->voodoo_collects_diamonds;
4193 level->bd_voodoo_hurt_kills_player = cave->voodoo_any_hurt_kills_player;
4194 level->bd_voodoo_dies_by_rock = cave->voodoo_dies_by_stone;
4195 level->bd_voodoo_vanish_by_explosion = cave->voodoo_disappear_in_explosion;
4196 level->bd_voodoo_penalty_time = cave->level_penalty_time[bd_level_nr];
4197 level->time_magic_wall = cave->level_magic_wall_time[bd_level_nr];
4198 level->bd_magic_wall_wait_hatching = cave->magic_timer_wait_for_hatching;
4199 level->bd_magic_wall_stops_amoeba = cave->magic_wall_stops_amoeba;
4201 level->bd_magic_wall_diamond_to = map_element_BD_to_RND_cave(cave->magic_diamond_to);
4202 level->bd_magic_wall_rock_to = map_element_BD_to_RND_cave(cave->magic_stone_to);
4203 level->bd_magic_wall_mega_rock_to = map_element_BD_to_RND_cave(cave->magic_mega_stone_to);
4204 level->bd_magic_wall_nut_to = map_element_BD_to_RND_cave(cave->magic_nut_to);
4205 level->bd_magic_wall_nitro_pack_to = map_element_BD_to_RND_cave(cave->magic_nitro_pack_to);
4206 level->bd_magic_wall_flying_diamond_to= map_element_BD_to_RND_cave(cave->magic_flying_diamond_to);
4207 level->bd_magic_wall_flying_rock_to = map_element_BD_to_RND_cave(cave->magic_flying_stone_to);
4209 level->bd_amoeba_wait_for_hatching = cave->amoeba_timer_wait_for_hatching;
4210 level->bd_amoeba_start_immediately = cave->amoeba_timer_started_immediately;
4211 level->bd_amoeba_2_explode_by_amoeba = cave->amoeba_2_explodes_by_amoeba;
4212 level->bd_amoeba_threshold_too_big = cave->level_amoeba_threshold[bd_level_nr];
4213 level->bd_amoeba_slow_growth_time = cave->level_amoeba_time[bd_level_nr];
4214 level->bd_amoeba_slow_growth_rate = cave->amoeba_growth_prob / 10000;
4215 level->bd_amoeba_fast_growth_rate = cave->amoeba_fast_growth_prob / 10000;
4216 level->bd_amoeba_2_threshold_too_big = cave->level_amoeba_2_threshold[bd_level_nr];
4217 level->bd_amoeba_2_slow_growth_time = cave->level_amoeba_2_time[bd_level_nr];
4218 level->bd_amoeba_2_slow_growth_rate = cave->amoeba_2_growth_prob / 10000;
4219 level->bd_amoeba_2_fast_growth_rate = cave->amoeba_2_fast_growth_prob / 10000;
4221 level->bd_amoeba_content_too_big = map_element_BD_to_RND_cave(cave->amoeba_too_big_effect);
4222 level->bd_amoeba_content_enclosed = map_element_BD_to_RND_cave(cave->amoeba_enclosed_effect);
4223 level->bd_amoeba_2_content_too_big = map_element_BD_to_RND_cave(cave->amoeba_2_too_big_effect);
4224 level->bd_amoeba_2_content_enclosed = map_element_BD_to_RND_cave(cave->amoeba_2_enclosed_effect);
4225 level->bd_amoeba_2_content_exploding = map_element_BD_to_RND_cave(cave->amoeba_2_explosion_effect);
4226 level->bd_amoeba_2_content_looks_like = map_element_BD_to_RND_cave(cave->amoeba_2_looks_like);
4228 level->bd_slime_is_predictable = cave->slime_predictable;
4229 level->bd_slime_correct_random = cave->slime_correct_random;
4230 level->bd_slime_permeability_rate = cave->level_slime_permeability[bd_level_nr] / 10000;
4231 level->bd_slime_permeability_bits_c64 = cave->level_slime_permeability_c64[bd_level_nr];
4232 level->bd_slime_random_seed_c64 = cave->level_slime_seed_c64[bd_level_nr];
4233 level->bd_cave_random_seed_c64 = cave->level_rand[bd_level_nr];
4235 level->bd_acid_eats_element = map_element_BD_to_RND_cave(cave->acid_eats_this);
4236 level->bd_acid_spread_rate = cave->acid_spread_ratio / 10000;
4237 level->bd_acid_turns_to_element = map_element_BD_to_RND_cave(cave->acid_turns_to);
4239 level->bd_biter_move_delay = cave->biter_delay_frame;
4240 level->bd_biter_eats_element = map_element_BD_to_RND_cave(cave->biter_eat);
4242 level->bd_bladder_converts_by_element = map_element_BD_to_RND_cave(cave->bladder_converts_by);
4244 level->bd_change_expanding_wall = cave->expanding_wall_changed;
4246 level->bd_replicators_active = cave->replicators_active;
4247 level->bd_replicator_create_delay = cave->replicator_delay_frame;
4249 level->bd_conveyor_belts_active = cave->conveyor_belts_active;
4250 level->bd_conveyor_belts_changed = cave->conveyor_belts_direction_changed;
4252 level->bd_water_cannot_flow_down = cave->water_does_not_flow_down;
4254 level->bd_nut_content = map_element_BD_to_RND_cave(cave->nut_turns_to_when_crushed);
4257 char *cave_name = getStringPrint("%s / %d", cave->name, bd_level_nr + 1);
4259 strncpy(level->name, cave_name, MAX_LEVEL_NAME_LEN);
4260 level->name[MAX_LEVEL_NAME_LEN] = '\0';
4262 // playfield elements
4263 for (x = 0; x < level->fieldx; x++)
4264 for (y = 0; y < level->fieldy; y++)
4265 level->field[x][y] = map_element_BD_to_RND_cave(cave->map[y][x]);
4267 checked_free(cave_name);
4270 static void setTapeInfoToDefaults(void);
4272 static void CopyNativeTape_BD_to_RND(struct LevelInfo *level)
4274 struct LevelInfo_BD *level_bd = level->native_bd_level;
4275 GdCave *cave = level_bd->cave;
4276 GdReplay *replay = level_bd->replay;
4282 // always start with reliable default values
4283 setTapeInfoToDefaults();
4285 tape.level_nr = level_nr; // (currently not used)
4286 tape.random_seed = replay->seed;
4288 TapeSetDateFromIsoDateString(replay->date);
4291 tape.pos[tape.counter].delay = 0;
4293 tape.bd_replay = TRUE;
4295 // all time calculations only used to display approximate tape time
4296 int cave_speed = cave->speed;
4297 int milliseconds_game = 0;
4298 int milliseconds_elapsed = 20;
4300 for (i = 0; i < replay->movements->len; i++)
4302 int replay_action = replay->movements->data[i];
4303 int tape_action = map_action_BD_to_RND(replay_action);
4304 byte action[MAX_TAPE_ACTIONS] = { tape_action };
4305 boolean success = 0;
4309 success = TapeAddAction(action);
4311 milliseconds_game += milliseconds_elapsed;
4313 if (milliseconds_game >= cave_speed)
4315 milliseconds_game -= cave_speed;
4322 tape.pos[tape.counter].delay = 0;
4323 tape.pos[tape.counter].action[0] = 0;
4327 Warn("BD replay truncated: size exceeds maximum tape size %d", MAX_TAPE_LEN);
4333 TapeHaltRecording();
4337 // ----------------------------------------------------------------------------
4338 // functions for loading EM level
4339 // ----------------------------------------------------------------------------
4341 static void CopyNativeLevel_RND_to_EM(struct LevelInfo *level)
4343 static int ball_xy[8][2] =
4354 struct LevelInfo_EM *level_em = level->native_em_level;
4355 struct CAVE *cav = level_em->cav;
4358 cav->width = MIN(level->fieldx, MAX_PLAYFIELD_WIDTH);
4359 cav->height = MIN(level->fieldy, MAX_PLAYFIELD_HEIGHT);
4361 cav->time_seconds = level->time;
4362 cav->gems_needed = level->gems_needed;
4364 cav->emerald_score = level->score[SC_EMERALD];
4365 cav->diamond_score = level->score[SC_DIAMOND];
4366 cav->alien_score = level->score[SC_ROBOT];
4367 cav->tank_score = level->score[SC_SPACESHIP];
4368 cav->bug_score = level->score[SC_BUG];
4369 cav->eater_score = level->score[SC_YAMYAM];
4370 cav->nut_score = level->score[SC_NUT];
4371 cav->dynamite_score = level->score[SC_DYNAMITE];
4372 cav->key_score = level->score[SC_KEY];
4373 cav->exit_score = level->score[SC_TIME_BONUS];
4375 cav->num_eater_arrays = level->num_yamyam_contents;
4377 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
4378 for (y = 0; y < 3; y++)
4379 for (x = 0; x < 3; x++)
4380 cav->eater_array[i][y * 3 + x] =
4381 map_element_RND_to_EM_cave(level->yamyam_content[i].e[x][y]);
4383 cav->amoeba_time = level->amoeba_speed;
4384 cav->wonderwall_time = level->time_magic_wall;
4385 cav->wheel_time = level->time_wheel;
4387 cav->android_move_time = level->android_move_time;
4388 cav->android_clone_time = level->android_clone_time;
4389 cav->ball_random = level->ball_random;
4390 cav->ball_active = level->ball_active_initial;
4391 cav->ball_time = level->ball_time;
4392 cav->num_ball_arrays = level->num_ball_contents;
4394 cav->lenses_score = level->lenses_score;
4395 cav->magnify_score = level->magnify_score;
4396 cav->slurp_score = level->slurp_score;
4398 cav->lenses_time = level->lenses_time;
4399 cav->magnify_time = level->magnify_time;
4401 cav->wind_time = 9999;
4402 cav->wind_direction =
4403 map_direction_RND_to_EM(level->wind_direction_initial);
4405 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
4406 for (j = 0; j < 8; j++)
4407 cav->ball_array[i][j] =
4408 map_element_RND_to_EM_cave(level->ball_content[i].
4409 e[ball_xy[j][0]][ball_xy[j][1]]);
4411 map_android_clone_elements_RND_to_EM(level);
4413 // first fill the complete playfield with the empty space element
4414 for (y = 0; y < EM_MAX_CAVE_HEIGHT; y++)
4415 for (x = 0; x < EM_MAX_CAVE_WIDTH; x++)
4416 cav->cave[x][y] = Cblank;
4418 // then copy the real level contents from level file into the playfield
4419 for (y = 0; y < cav->height; y++) for (x = 0; x < cav->width; x++)
4421 int new_element = map_element_RND_to_EM_cave(level->field[x][y]);
4423 if (level->field[x][y] == EL_AMOEBA_DEAD)
4424 new_element = map_element_RND_to_EM_cave(EL_AMOEBA_WET);
4426 cav->cave[x][y] = new_element;
4429 for (i = 0; i < MAX_PLAYERS; i++)
4431 cav->player_x[i] = -1;
4432 cav->player_y[i] = -1;
4435 // initialize player positions and delete players from the playfield
4436 for (y = 0; y < cav->height; y++) for (x = 0; x < cav->width; x++)
4438 if (IS_PLAYER_ELEMENT(level->field[x][y]))
4440 int player_nr = GET_PLAYER_NR(level->field[x][y]);
4442 cav->player_x[player_nr] = x;
4443 cav->player_y[player_nr] = y;
4445 cav->cave[x][y] = map_element_RND_to_EM_cave(EL_EMPTY);
4450 static void CopyNativeLevel_EM_to_RND(struct LevelInfo *level)
4452 static int ball_xy[8][2] =
4463 struct LevelInfo_EM *level_em = level->native_em_level;
4464 struct CAVE *cav = level_em->cav;
4467 level->fieldx = MIN(cav->width, MAX_LEV_FIELDX);
4468 level->fieldy = MIN(cav->height, MAX_LEV_FIELDY);
4470 level->time = cav->time_seconds;
4471 level->gems_needed = cav->gems_needed;
4473 sprintf(level->name, "Level %d", level->file_info.nr);
4475 level->score[SC_EMERALD] = cav->emerald_score;
4476 level->score[SC_DIAMOND] = cav->diamond_score;
4477 level->score[SC_ROBOT] = cav->alien_score;
4478 level->score[SC_SPACESHIP] = cav->tank_score;
4479 level->score[SC_BUG] = cav->bug_score;
4480 level->score[SC_YAMYAM] = cav->eater_score;
4481 level->score[SC_NUT] = cav->nut_score;
4482 level->score[SC_DYNAMITE] = cav->dynamite_score;
4483 level->score[SC_KEY] = cav->key_score;
4484 level->score[SC_TIME_BONUS] = cav->exit_score;
4486 level->num_yamyam_contents = cav->num_eater_arrays;
4488 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
4489 for (y = 0; y < 3; y++)
4490 for (x = 0; x < 3; x++)
4491 level->yamyam_content[i].e[x][y] =
4492 map_element_EM_to_RND_cave(cav->eater_array[i][y * 3 + x]);
4494 level->amoeba_speed = cav->amoeba_time;
4495 level->time_magic_wall = cav->wonderwall_time;
4496 level->time_wheel = cav->wheel_time;
4498 level->android_move_time = cav->android_move_time;
4499 level->android_clone_time = cav->android_clone_time;
4500 level->ball_random = cav->ball_random;
4501 level->ball_active_initial = cav->ball_active;
4502 level->ball_time = cav->ball_time;
4503 level->num_ball_contents = cav->num_ball_arrays;
4505 level->lenses_score = cav->lenses_score;
4506 level->magnify_score = cav->magnify_score;
4507 level->slurp_score = cav->slurp_score;
4509 level->lenses_time = cav->lenses_time;
4510 level->magnify_time = cav->magnify_time;
4512 level->wind_direction_initial =
4513 map_direction_EM_to_RND(cav->wind_direction);
4515 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
4516 for (j = 0; j < 8; j++)
4517 level->ball_content[i].e[ball_xy[j][0]][ball_xy[j][1]] =
4518 map_element_EM_to_RND_cave(cav->ball_array[i][j]);
4520 map_android_clone_elements_EM_to_RND(level);
4522 // convert the playfield (some elements need special treatment)
4523 for (y = 0; y < level->fieldy; y++) for (x = 0; x < level->fieldx; x++)
4525 int new_element = map_element_EM_to_RND_cave(cav->cave[x][y]);
4527 if (new_element == EL_AMOEBA_WET && level->amoeba_speed == 0)
4528 new_element = EL_AMOEBA_DEAD;
4530 level->field[x][y] = new_element;
4533 for (i = 0; i < MAX_PLAYERS; i++)
4535 // in case of all players set to the same field, use the first player
4536 int nr = MAX_PLAYERS - i - 1;
4537 int jx = cav->player_x[nr];
4538 int jy = cav->player_y[nr];
4540 if (jx != -1 && jy != -1)
4541 level->field[jx][jy] = EL_PLAYER_1 + nr;
4544 // time score is counted for each 10 seconds left in Emerald Mine levels
4545 level->time_score_base = 10;
4549 // ----------------------------------------------------------------------------
4550 // functions for loading SP level
4551 // ----------------------------------------------------------------------------
4553 static void CopyNativeLevel_RND_to_SP(struct LevelInfo *level)
4555 struct LevelInfo_SP *level_sp = level->native_sp_level;
4556 LevelInfoType *header = &level_sp->header;
4559 level_sp->width = level->fieldx;
4560 level_sp->height = level->fieldy;
4562 for (x = 0; x < level->fieldx; x++)
4563 for (y = 0; y < level->fieldy; y++)
4564 level_sp->playfield[x][y] = map_element_RND_to_SP(level->field[x][y]);
4566 header->InitialGravity = (level->initial_player_gravity[0] ? 1 : 0);
4568 for (i = 0; i < SP_LEVEL_NAME_LEN; i++)
4569 header->LevelTitle[i] = level->name[i];
4570 // !!! NO STRING TERMINATION IN SUPAPLEX VB CODE YET -- FIX THIS !!!
4572 header->InfotronsNeeded = level->gems_needed;
4574 header->SpecialPortCount = 0;
4576 for (x = 0; x < level->fieldx; x++) for (y = 0; y < level->fieldy; y++)
4578 boolean gravity_port_found = FALSE;
4579 boolean gravity_port_valid = FALSE;
4580 int gravity_port_flag;
4581 int gravity_port_base_element;
4582 int element = level->field[x][y];
4584 if (element >= EL_SP_GRAVITY_ON_PORT_RIGHT &&
4585 element <= EL_SP_GRAVITY_ON_PORT_UP)
4587 gravity_port_found = TRUE;
4588 gravity_port_valid = TRUE;
4589 gravity_port_flag = 1;
4590 gravity_port_base_element = EL_SP_GRAVITY_ON_PORT_RIGHT;
4592 else if (element >= EL_SP_GRAVITY_OFF_PORT_RIGHT &&
4593 element <= EL_SP_GRAVITY_OFF_PORT_UP)
4595 gravity_port_found = TRUE;
4596 gravity_port_valid = TRUE;
4597 gravity_port_flag = 0;
4598 gravity_port_base_element = EL_SP_GRAVITY_OFF_PORT_RIGHT;
4600 else if (element >= EL_SP_GRAVITY_PORT_RIGHT &&
4601 element <= EL_SP_GRAVITY_PORT_UP)
4603 // change R'n'D style gravity inverting special port to normal port
4604 // (there are no gravity inverting ports in native Supaplex engine)
4606 gravity_port_found = TRUE;
4607 gravity_port_valid = FALSE;
4608 gravity_port_base_element = EL_SP_GRAVITY_PORT_RIGHT;
4611 if (gravity_port_found)
4613 if (gravity_port_valid &&
4614 header->SpecialPortCount < SP_MAX_SPECIAL_PORTS)
4616 SpecialPortType *port = &header->SpecialPort[header->SpecialPortCount];
4618 port->PortLocation = (y * level->fieldx + x) * 2;
4619 port->Gravity = gravity_port_flag;
4621 element += EL_SP_GRAVITY_PORT_RIGHT - gravity_port_base_element;
4623 header->SpecialPortCount++;
4627 // change special gravity port to normal port
4629 element += EL_SP_PORT_RIGHT - gravity_port_base_element;
4632 level_sp->playfield[x][y] = element - EL_SP_START;
4637 static void CopyNativeLevel_SP_to_RND(struct LevelInfo *level)
4639 struct LevelInfo_SP *level_sp = level->native_sp_level;
4640 LevelInfoType *header = &level_sp->header;
4641 boolean num_invalid_elements = 0;
4644 level->fieldx = level_sp->width;
4645 level->fieldy = level_sp->height;
4647 for (x = 0; x < level->fieldx; x++)
4649 for (y = 0; y < level->fieldy; y++)
4651 int element_old = level_sp->playfield[x][y];
4652 int element_new = getMappedElement(map_element_SP_to_RND(element_old));
4654 if (element_new == EL_UNKNOWN)
4656 num_invalid_elements++;
4658 Debug("level:native:SP", "invalid element %d at position %d, %d",
4662 level->field[x][y] = element_new;
4666 if (num_invalid_elements > 0)
4667 Warn("found %d invalid elements%s", num_invalid_elements,
4668 (!options.debug ? " (use '--debug' for more details)" : ""));
4670 for (i = 0; i < MAX_PLAYERS; i++)
4671 level->initial_player_gravity[i] =
4672 (header->InitialGravity == 1 ? TRUE : FALSE);
4674 // skip leading spaces
4675 for (i = 0; i < SP_LEVEL_NAME_LEN; i++)
4676 if (header->LevelTitle[i] != ' ')
4680 for (j = 0; i < SP_LEVEL_NAME_LEN; i++, j++)
4681 level->name[j] = header->LevelTitle[i];
4682 level->name[j] = '\0';
4684 // cut trailing spaces
4686 if (level->name[j - 1] == ' ' && level->name[j] == '\0')
4687 level->name[j - 1] = '\0';
4689 level->gems_needed = header->InfotronsNeeded;
4691 for (i = 0; i < header->SpecialPortCount; i++)
4693 SpecialPortType *port = &header->SpecialPort[i];
4694 int port_location = port->PortLocation;
4695 int gravity = port->Gravity;
4696 int port_x, port_y, port_element;
4698 port_x = (port_location / 2) % level->fieldx;
4699 port_y = (port_location / 2) / level->fieldx;
4701 if (port_x < 0 || port_x >= level->fieldx ||
4702 port_y < 0 || port_y >= level->fieldy)
4704 Warn("special port position (%d, %d) out of bounds", port_x, port_y);
4709 port_element = level->field[port_x][port_y];
4711 if (port_element < EL_SP_GRAVITY_PORT_RIGHT ||
4712 port_element > EL_SP_GRAVITY_PORT_UP)
4714 Warn("no special port at position (%d, %d)", port_x, port_y);
4719 // change previous (wrong) gravity inverting special port to either
4720 // gravity enabling special port or gravity disabling special port
4721 level->field[port_x][port_y] +=
4722 (gravity == 1 ? EL_SP_GRAVITY_ON_PORT_RIGHT :
4723 EL_SP_GRAVITY_OFF_PORT_RIGHT) - EL_SP_GRAVITY_PORT_RIGHT;
4726 // change special gravity ports without database entries to normal ports
4727 for (x = 0; x < level->fieldx; x++)
4728 for (y = 0; y < level->fieldy; y++)
4729 if (level->field[x][y] >= EL_SP_GRAVITY_PORT_RIGHT &&
4730 level->field[x][y] <= EL_SP_GRAVITY_PORT_UP)
4731 level->field[x][y] += EL_SP_PORT_RIGHT - EL_SP_GRAVITY_PORT_RIGHT;
4733 level->time = 0; // no time limit
4734 level->amoeba_speed = 0;
4735 level->time_magic_wall = 0;
4736 level->time_wheel = 0;
4737 level->amoeba_content = EL_EMPTY;
4739 // original Supaplex does not use score values -- rate by playing time
4740 for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
4741 level->score[i] = 0;
4743 level->rate_time_over_score = TRUE;
4745 // there are no yamyams in supaplex levels
4746 for (i = 0; i < level->num_yamyam_contents; i++)
4747 for (x = 0; x < 3; x++)
4748 for (y = 0; y < 3; y++)
4749 level->yamyam_content[i].e[x][y] = EL_EMPTY;
4752 static void CopyNativeTape_RND_to_SP(struct LevelInfo *level)
4754 struct LevelInfo_SP *level_sp = level->native_sp_level;
4755 struct DemoInfo_SP *demo = &level_sp->demo;
4758 // always start with reliable default values
4759 demo->is_available = FALSE;
4762 if (TAPE_IS_EMPTY(tape))
4765 demo->level_nr = tape.level_nr; // (currently not used)
4767 level_sp->header.DemoRandomSeed = tape.random_seed;
4771 for (i = 0; i < tape.length; i++)
4773 int demo_action = map_key_RND_to_SP(tape.pos[i].action[0]);
4774 int demo_repeat = tape.pos[i].delay;
4775 int demo_entries = (demo_repeat + 15) / 16;
4777 if (demo->length + demo_entries >= SP_MAX_TAPE_LEN)
4779 Warn("tape truncated: size exceeds maximum SP demo size %d",
4785 for (j = 0; j < demo_repeat / 16; j++)
4786 demo->data[demo->length++] = 0xf0 | demo_action;
4788 if (demo_repeat % 16)
4789 demo->data[demo->length++] = ((demo_repeat % 16 - 1) << 4) | demo_action;
4792 demo->is_available = TRUE;
4795 static void CopyNativeTape_SP_to_RND(struct LevelInfo *level)
4797 struct LevelInfo_SP *level_sp = level->native_sp_level;
4798 struct DemoInfo_SP *demo = &level_sp->demo;
4799 char *filename = level->file_info.filename;
4802 // always start with reliable default values
4803 setTapeInfoToDefaults();
4805 if (!demo->is_available)
4808 tape.level_nr = demo->level_nr; // (currently not used)
4809 tape.random_seed = level_sp->header.DemoRandomSeed;
4811 TapeSetDateFromEpochSeconds(getFileTimestampEpochSeconds(filename));
4814 tape.pos[tape.counter].delay = 0;
4816 for (i = 0; i < demo->length; i++)
4818 int demo_action = demo->data[i] & 0x0f;
4819 int demo_repeat = (demo->data[i] & 0xf0) >> 4;
4820 int tape_action = map_key_SP_to_RND(demo_action);
4821 int tape_repeat = demo_repeat + 1;
4822 byte action[MAX_TAPE_ACTIONS] = { tape_action };
4823 boolean success = 0;
4826 for (j = 0; j < tape_repeat; j++)
4827 success = TapeAddAction(action);
4831 Warn("SP demo truncated: size exceeds maximum tape size %d",
4838 TapeHaltRecording();
4842 // ----------------------------------------------------------------------------
4843 // functions for loading MM level
4844 // ----------------------------------------------------------------------------
4846 static void CopyNativeLevel_RND_to_MM(struct LevelInfo *level)
4848 struct LevelInfo_MM *level_mm = level->native_mm_level;
4851 level_mm->fieldx = MIN(level->fieldx, MM_MAX_PLAYFIELD_WIDTH);
4852 level_mm->fieldy = MIN(level->fieldy, MM_MAX_PLAYFIELD_HEIGHT);
4854 level_mm->time = level->time;
4855 level_mm->kettles_needed = level->gems_needed;
4856 level_mm->auto_count_kettles = level->auto_count_gems;
4858 level_mm->mm_laser_red = level->mm_laser_red;
4859 level_mm->mm_laser_green = level->mm_laser_green;
4860 level_mm->mm_laser_blue = level->mm_laser_blue;
4862 level_mm->df_laser_red = level->df_laser_red;
4863 level_mm->df_laser_green = level->df_laser_green;
4864 level_mm->df_laser_blue = level->df_laser_blue;
4866 strcpy(level_mm->name, level->name);
4867 strcpy(level_mm->author, level->author);
4869 level_mm->score[SC_EMERALD] = level->score[SC_EMERALD];
4870 level_mm->score[SC_PACMAN] = level->score[SC_PACMAN];
4871 level_mm->score[SC_KEY] = level->score[SC_KEY];
4872 level_mm->score[SC_TIME_BONUS] = level->score[SC_TIME_BONUS];
4873 level_mm->score[SC_ELEM_BONUS] = level->score[SC_ELEM_BONUS];
4875 level_mm->amoeba_speed = level->amoeba_speed;
4876 level_mm->time_fuse = level->mm_time_fuse;
4877 level_mm->time_bomb = level->mm_time_bomb;
4878 level_mm->time_ball = level->mm_time_ball;
4879 level_mm->time_block = level->mm_time_block;
4881 level_mm->num_ball_contents = level->num_mm_ball_contents;
4882 level_mm->ball_choice_mode = level->mm_ball_choice_mode;
4883 level_mm->rotate_ball_content = level->rotate_mm_ball_content;
4884 level_mm->explode_ball = level->explode_mm_ball;
4886 for (i = 0; i < level->num_mm_ball_contents; i++)
4887 level_mm->ball_content[i] =
4888 map_element_RND_to_MM(level->mm_ball_content[i]);
4890 for (x = 0; x < level->fieldx; x++)
4891 for (y = 0; y < level->fieldy; y++)
4893 level_mm->field[x][y] = map_element_RND_to_MM(level->field[x][y]);
4896 static void CopyNativeLevel_MM_to_RND(struct LevelInfo *level)
4898 struct LevelInfo_MM *level_mm = level->native_mm_level;
4901 level->fieldx = MIN(level_mm->fieldx, MAX_LEV_FIELDX);
4902 level->fieldy = MIN(level_mm->fieldy, MAX_LEV_FIELDY);
4904 level->time = level_mm->time;
4905 level->gems_needed = level_mm->kettles_needed;
4906 level->auto_count_gems = level_mm->auto_count_kettles;
4908 level->mm_laser_red = level_mm->mm_laser_red;
4909 level->mm_laser_green = level_mm->mm_laser_green;
4910 level->mm_laser_blue = level_mm->mm_laser_blue;
4912 level->df_laser_red = level_mm->df_laser_red;
4913 level->df_laser_green = level_mm->df_laser_green;
4914 level->df_laser_blue = level_mm->df_laser_blue;
4916 strcpy(level->name, level_mm->name);
4918 // only overwrite author from 'levelinfo.conf' if author defined in level
4919 if (!strEqual(level_mm->author, ANONYMOUS_NAME))
4920 strcpy(level->author, level_mm->author);
4922 level->score[SC_EMERALD] = level_mm->score[SC_EMERALD];
4923 level->score[SC_PACMAN] = level_mm->score[SC_PACMAN];
4924 level->score[SC_KEY] = level_mm->score[SC_KEY];
4925 level->score[SC_TIME_BONUS] = level_mm->score[SC_TIME_BONUS];
4926 level->score[SC_ELEM_BONUS] = level_mm->score[SC_ELEM_BONUS];
4928 level->amoeba_speed = level_mm->amoeba_speed;
4929 level->mm_time_fuse = level_mm->time_fuse;
4930 level->mm_time_bomb = level_mm->time_bomb;
4931 level->mm_time_ball = level_mm->time_ball;
4932 level->mm_time_block = level_mm->time_block;
4934 level->num_mm_ball_contents = level_mm->num_ball_contents;
4935 level->mm_ball_choice_mode = level_mm->ball_choice_mode;
4936 level->rotate_mm_ball_content = level_mm->rotate_ball_content;
4937 level->explode_mm_ball = level_mm->explode_ball;
4939 for (i = 0; i < level->num_mm_ball_contents; i++)
4940 level->mm_ball_content[i] =
4941 map_element_MM_to_RND(level_mm->ball_content[i]);
4943 for (x = 0; x < level->fieldx; x++)
4944 for (y = 0; y < level->fieldy; y++)
4945 level->field[x][y] = map_element_MM_to_RND(level_mm->field[x][y]);
4949 // ----------------------------------------------------------------------------
4950 // functions for loading DC level
4951 // ----------------------------------------------------------------------------
4953 #define DC_LEVEL_HEADER_SIZE 344
4955 static unsigned short getDecodedWord_DC(unsigned short data_encoded,
4958 static int last_data_encoded;
4962 int diff_hi, diff_lo;
4963 int data_hi, data_lo;
4964 unsigned short data_decoded;
4968 last_data_encoded = 0;
4975 diff = data_encoded - last_data_encoded;
4976 diff_hi = diff & ~0xff;
4977 diff_lo = diff & 0xff;
4981 data_hi = diff_hi - (offset1 << 8) + (offset2 & 0xff00);
4982 data_lo = (diff_lo + (data_hi >> 16)) & 0x00ff;
4983 data_hi = data_hi & 0xff00;
4985 data_decoded = data_hi | data_lo;
4987 last_data_encoded = data_encoded;
4989 offset1 = (offset1 + 1) % 31;
4990 offset2 = offset2 & 0xff;
4992 return data_decoded;
4995 static int getMappedElement_DC(int element)
5003 // 0x0117 - 0x036e: (?)
5006 // 0x042d - 0x0684: (?)
5022 element = EL_CRYSTAL;
5025 case 0x0e77: // quicksand (boulder)
5026 element = EL_QUICKSAND_FAST_FULL;
5029 case 0x0e99: // slow quicksand (boulder)
5030 element = EL_QUICKSAND_FULL;
5034 element = EL_EM_EXIT_OPEN;
5038 element = EL_EM_EXIT_CLOSED;
5042 element = EL_EM_STEEL_EXIT_OPEN;
5046 element = EL_EM_STEEL_EXIT_CLOSED;
5049 case 0x0f4f: // dynamite (lit 1)
5050 element = EL_EM_DYNAMITE_ACTIVE;
5053 case 0x0f57: // dynamite (lit 2)
5054 element = EL_EM_DYNAMITE_ACTIVE;
5057 case 0x0f5f: // dynamite (lit 3)
5058 element = EL_EM_DYNAMITE_ACTIVE;
5061 case 0x0f67: // dynamite (lit 4)
5062 element = EL_EM_DYNAMITE_ACTIVE;
5069 element = EL_AMOEBA_WET;
5073 element = EL_AMOEBA_DROP;
5077 element = EL_DC_MAGIC_WALL;
5081 element = EL_SPACESHIP_UP;
5085 element = EL_SPACESHIP_DOWN;
5089 element = EL_SPACESHIP_LEFT;
5093 element = EL_SPACESHIP_RIGHT;
5097 element = EL_BUG_UP;
5101 element = EL_BUG_DOWN;
5105 element = EL_BUG_LEFT;
5109 element = EL_BUG_RIGHT;
5113 element = EL_MOLE_UP;
5117 element = EL_MOLE_DOWN;
5121 element = EL_MOLE_LEFT;
5125 element = EL_MOLE_RIGHT;
5133 element = EL_YAMYAM_UP;
5137 element = EL_SWITCHGATE_OPEN;
5141 element = EL_SWITCHGATE_CLOSED;
5145 element = EL_DC_SWITCHGATE_SWITCH_UP;
5149 element = EL_TIMEGATE_CLOSED;
5152 case 0x144c: // conveyor belt switch (green)
5153 element = EL_CONVEYOR_BELT_3_SWITCH_MIDDLE;
5156 case 0x144f: // conveyor belt switch (red)
5157 element = EL_CONVEYOR_BELT_1_SWITCH_MIDDLE;
5160 case 0x1452: // conveyor belt switch (blue)
5161 element = EL_CONVEYOR_BELT_4_SWITCH_MIDDLE;
5165 element = EL_CONVEYOR_BELT_3_MIDDLE;
5169 element = EL_CONVEYOR_BELT_3_LEFT;
5173 element = EL_CONVEYOR_BELT_3_RIGHT;
5177 element = EL_CONVEYOR_BELT_1_MIDDLE;
5181 element = EL_CONVEYOR_BELT_1_LEFT;
5185 element = EL_CONVEYOR_BELT_1_RIGHT;
5189 element = EL_CONVEYOR_BELT_4_MIDDLE;
5193 element = EL_CONVEYOR_BELT_4_LEFT;
5197 element = EL_CONVEYOR_BELT_4_RIGHT;
5201 element = EL_EXPANDABLE_WALL_HORIZONTAL;
5205 element = EL_EXPANDABLE_WALL_VERTICAL;
5209 element = EL_EXPANDABLE_WALL_ANY;
5212 case 0x14ce: // growing steel wall (left/right)
5213 element = EL_EXPANDABLE_STEELWALL_HORIZONTAL;
5216 case 0x14df: // growing steel wall (up/down)
5217 element = EL_EXPANDABLE_STEELWALL_VERTICAL;
5220 case 0x14e8: // growing steel wall (up/down/left/right)
5221 element = EL_EXPANDABLE_STEELWALL_ANY;
5225 element = EL_SHIELD_DEADLY;
5229 element = EL_EXTRA_TIME;
5237 element = EL_EMPTY_SPACE;
5240 case 0x1578: // quicksand (empty)
5241 element = EL_QUICKSAND_FAST_EMPTY;
5244 case 0x1579: // slow quicksand (empty)
5245 element = EL_QUICKSAND_EMPTY;
5255 element = EL_EM_DYNAMITE;
5258 case 0x15a1: // key (red)
5259 element = EL_EM_KEY_1;
5262 case 0x15a2: // key (yellow)
5263 element = EL_EM_KEY_2;
5266 case 0x15a3: // key (blue)
5267 element = EL_EM_KEY_4;
5270 case 0x15a4: // key (green)
5271 element = EL_EM_KEY_3;
5274 case 0x15a5: // key (white)
5275 element = EL_DC_KEY_WHITE;
5279 element = EL_WALL_SLIPPERY;
5286 case 0x15a8: // wall (not round)
5290 case 0x15a9: // (blue)
5291 element = EL_CHAR_A;
5294 case 0x15aa: // (blue)
5295 element = EL_CHAR_B;
5298 case 0x15ab: // (blue)
5299 element = EL_CHAR_C;
5302 case 0x15ac: // (blue)
5303 element = EL_CHAR_D;
5306 case 0x15ad: // (blue)
5307 element = EL_CHAR_E;
5310 case 0x15ae: // (blue)
5311 element = EL_CHAR_F;
5314 case 0x15af: // (blue)
5315 element = EL_CHAR_G;
5318 case 0x15b0: // (blue)
5319 element = EL_CHAR_H;
5322 case 0x15b1: // (blue)
5323 element = EL_CHAR_I;
5326 case 0x15b2: // (blue)
5327 element = EL_CHAR_J;
5330 case 0x15b3: // (blue)
5331 element = EL_CHAR_K;
5334 case 0x15b4: // (blue)
5335 element = EL_CHAR_L;
5338 case 0x15b5: // (blue)
5339 element = EL_CHAR_M;
5342 case 0x15b6: // (blue)
5343 element = EL_CHAR_N;
5346 case 0x15b7: // (blue)
5347 element = EL_CHAR_O;
5350 case 0x15b8: // (blue)
5351 element = EL_CHAR_P;
5354 case 0x15b9: // (blue)
5355 element = EL_CHAR_Q;
5358 case 0x15ba: // (blue)
5359 element = EL_CHAR_R;
5362 case 0x15bb: // (blue)
5363 element = EL_CHAR_S;
5366 case 0x15bc: // (blue)
5367 element = EL_CHAR_T;
5370 case 0x15bd: // (blue)
5371 element = EL_CHAR_U;
5374 case 0x15be: // (blue)
5375 element = EL_CHAR_V;
5378 case 0x15bf: // (blue)
5379 element = EL_CHAR_W;
5382 case 0x15c0: // (blue)
5383 element = EL_CHAR_X;
5386 case 0x15c1: // (blue)
5387 element = EL_CHAR_Y;
5390 case 0x15c2: // (blue)
5391 element = EL_CHAR_Z;
5394 case 0x15c3: // (blue)
5395 element = EL_CHAR_AUMLAUT;
5398 case 0x15c4: // (blue)
5399 element = EL_CHAR_OUMLAUT;
5402 case 0x15c5: // (blue)
5403 element = EL_CHAR_UUMLAUT;
5406 case 0x15c6: // (blue)
5407 element = EL_CHAR_0;
5410 case 0x15c7: // (blue)
5411 element = EL_CHAR_1;
5414 case 0x15c8: // (blue)
5415 element = EL_CHAR_2;
5418 case 0x15c9: // (blue)
5419 element = EL_CHAR_3;
5422 case 0x15ca: // (blue)
5423 element = EL_CHAR_4;
5426 case 0x15cb: // (blue)
5427 element = EL_CHAR_5;
5430 case 0x15cc: // (blue)
5431 element = EL_CHAR_6;
5434 case 0x15cd: // (blue)
5435 element = EL_CHAR_7;
5438 case 0x15ce: // (blue)
5439 element = EL_CHAR_8;
5442 case 0x15cf: // (blue)
5443 element = EL_CHAR_9;
5446 case 0x15d0: // (blue)
5447 element = EL_CHAR_PERIOD;
5450 case 0x15d1: // (blue)
5451 element = EL_CHAR_EXCLAM;
5454 case 0x15d2: // (blue)
5455 element = EL_CHAR_COLON;
5458 case 0x15d3: // (blue)
5459 element = EL_CHAR_LESS;
5462 case 0x15d4: // (blue)
5463 element = EL_CHAR_GREATER;
5466 case 0x15d5: // (blue)
5467 element = EL_CHAR_QUESTION;
5470 case 0x15d6: // (blue)
5471 element = EL_CHAR_COPYRIGHT;
5474 case 0x15d7: // (blue)
5475 element = EL_CHAR_UP;
5478 case 0x15d8: // (blue)
5479 element = EL_CHAR_DOWN;
5482 case 0x15d9: // (blue)
5483 element = EL_CHAR_BUTTON;
5486 case 0x15da: // (blue)
5487 element = EL_CHAR_PLUS;
5490 case 0x15db: // (blue)
5491 element = EL_CHAR_MINUS;
5494 case 0x15dc: // (blue)
5495 element = EL_CHAR_APOSTROPHE;
5498 case 0x15dd: // (blue)
5499 element = EL_CHAR_PARENLEFT;
5502 case 0x15de: // (blue)
5503 element = EL_CHAR_PARENRIGHT;
5506 case 0x15df: // (green)
5507 element = EL_CHAR_A;
5510 case 0x15e0: // (green)
5511 element = EL_CHAR_B;
5514 case 0x15e1: // (green)
5515 element = EL_CHAR_C;
5518 case 0x15e2: // (green)
5519 element = EL_CHAR_D;
5522 case 0x15e3: // (green)
5523 element = EL_CHAR_E;
5526 case 0x15e4: // (green)
5527 element = EL_CHAR_F;
5530 case 0x15e5: // (green)
5531 element = EL_CHAR_G;
5534 case 0x15e6: // (green)
5535 element = EL_CHAR_H;
5538 case 0x15e7: // (green)
5539 element = EL_CHAR_I;
5542 case 0x15e8: // (green)
5543 element = EL_CHAR_J;
5546 case 0x15e9: // (green)
5547 element = EL_CHAR_K;
5550 case 0x15ea: // (green)
5551 element = EL_CHAR_L;
5554 case 0x15eb: // (green)
5555 element = EL_CHAR_M;
5558 case 0x15ec: // (green)
5559 element = EL_CHAR_N;
5562 case 0x15ed: // (green)
5563 element = EL_CHAR_O;
5566 case 0x15ee: // (green)
5567 element = EL_CHAR_P;
5570 case 0x15ef: // (green)
5571 element = EL_CHAR_Q;
5574 case 0x15f0: // (green)
5575 element = EL_CHAR_R;
5578 case 0x15f1: // (green)
5579 element = EL_CHAR_S;
5582 case 0x15f2: // (green)
5583 element = EL_CHAR_T;
5586 case 0x15f3: // (green)
5587 element = EL_CHAR_U;
5590 case 0x15f4: // (green)
5591 element = EL_CHAR_V;
5594 case 0x15f5: // (green)
5595 element = EL_CHAR_W;
5598 case 0x15f6: // (green)
5599 element = EL_CHAR_X;
5602 case 0x15f7: // (green)
5603 element = EL_CHAR_Y;
5606 case 0x15f8: // (green)
5607 element = EL_CHAR_Z;
5610 case 0x15f9: // (green)
5611 element = EL_CHAR_AUMLAUT;
5614 case 0x15fa: // (green)
5615 element = EL_CHAR_OUMLAUT;
5618 case 0x15fb: // (green)
5619 element = EL_CHAR_UUMLAUT;
5622 case 0x15fc: // (green)
5623 element = EL_CHAR_0;
5626 case 0x15fd: // (green)
5627 element = EL_CHAR_1;
5630 case 0x15fe: // (green)
5631 element = EL_CHAR_2;
5634 case 0x15ff: // (green)
5635 element = EL_CHAR_3;
5638 case 0x1600: // (green)
5639 element = EL_CHAR_4;
5642 case 0x1601: // (green)
5643 element = EL_CHAR_5;
5646 case 0x1602: // (green)
5647 element = EL_CHAR_6;
5650 case 0x1603: // (green)
5651 element = EL_CHAR_7;
5654 case 0x1604: // (green)
5655 element = EL_CHAR_8;
5658 case 0x1605: // (green)
5659 element = EL_CHAR_9;
5662 case 0x1606: // (green)
5663 element = EL_CHAR_PERIOD;
5666 case 0x1607: // (green)
5667 element = EL_CHAR_EXCLAM;
5670 case 0x1608: // (green)
5671 element = EL_CHAR_COLON;
5674 case 0x1609: // (green)
5675 element = EL_CHAR_LESS;
5678 case 0x160a: // (green)
5679 element = EL_CHAR_GREATER;
5682 case 0x160b: // (green)
5683 element = EL_CHAR_QUESTION;
5686 case 0x160c: // (green)
5687 element = EL_CHAR_COPYRIGHT;
5690 case 0x160d: // (green)
5691 element = EL_CHAR_UP;
5694 case 0x160e: // (green)
5695 element = EL_CHAR_DOWN;
5698 case 0x160f: // (green)
5699 element = EL_CHAR_BUTTON;
5702 case 0x1610: // (green)
5703 element = EL_CHAR_PLUS;
5706 case 0x1611: // (green)
5707 element = EL_CHAR_MINUS;
5710 case 0x1612: // (green)
5711 element = EL_CHAR_APOSTROPHE;
5714 case 0x1613: // (green)
5715 element = EL_CHAR_PARENLEFT;
5718 case 0x1614: // (green)
5719 element = EL_CHAR_PARENRIGHT;
5722 case 0x1615: // (blue steel)
5723 element = EL_STEEL_CHAR_A;
5726 case 0x1616: // (blue steel)
5727 element = EL_STEEL_CHAR_B;
5730 case 0x1617: // (blue steel)
5731 element = EL_STEEL_CHAR_C;
5734 case 0x1618: // (blue steel)
5735 element = EL_STEEL_CHAR_D;
5738 case 0x1619: // (blue steel)
5739 element = EL_STEEL_CHAR_E;
5742 case 0x161a: // (blue steel)
5743 element = EL_STEEL_CHAR_F;
5746 case 0x161b: // (blue steel)
5747 element = EL_STEEL_CHAR_G;
5750 case 0x161c: // (blue steel)
5751 element = EL_STEEL_CHAR_H;
5754 case 0x161d: // (blue steel)
5755 element = EL_STEEL_CHAR_I;
5758 case 0x161e: // (blue steel)
5759 element = EL_STEEL_CHAR_J;
5762 case 0x161f: // (blue steel)
5763 element = EL_STEEL_CHAR_K;
5766 case 0x1620: // (blue steel)
5767 element = EL_STEEL_CHAR_L;
5770 case 0x1621: // (blue steel)
5771 element = EL_STEEL_CHAR_M;
5774 case 0x1622: // (blue steel)
5775 element = EL_STEEL_CHAR_N;
5778 case 0x1623: // (blue steel)
5779 element = EL_STEEL_CHAR_O;
5782 case 0x1624: // (blue steel)
5783 element = EL_STEEL_CHAR_P;
5786 case 0x1625: // (blue steel)
5787 element = EL_STEEL_CHAR_Q;
5790 case 0x1626: // (blue steel)
5791 element = EL_STEEL_CHAR_R;
5794 case 0x1627: // (blue steel)
5795 element = EL_STEEL_CHAR_S;
5798 case 0x1628: // (blue steel)
5799 element = EL_STEEL_CHAR_T;
5802 case 0x1629: // (blue steel)
5803 element = EL_STEEL_CHAR_U;
5806 case 0x162a: // (blue steel)
5807 element = EL_STEEL_CHAR_V;
5810 case 0x162b: // (blue steel)
5811 element = EL_STEEL_CHAR_W;
5814 case 0x162c: // (blue steel)
5815 element = EL_STEEL_CHAR_X;
5818 case 0x162d: // (blue steel)
5819 element = EL_STEEL_CHAR_Y;
5822 case 0x162e: // (blue steel)
5823 element = EL_STEEL_CHAR_Z;
5826 case 0x162f: // (blue steel)
5827 element = EL_STEEL_CHAR_AUMLAUT;
5830 case 0x1630: // (blue steel)
5831 element = EL_STEEL_CHAR_OUMLAUT;
5834 case 0x1631: // (blue steel)
5835 element = EL_STEEL_CHAR_UUMLAUT;
5838 case 0x1632: // (blue steel)
5839 element = EL_STEEL_CHAR_0;
5842 case 0x1633: // (blue steel)
5843 element = EL_STEEL_CHAR_1;
5846 case 0x1634: // (blue steel)
5847 element = EL_STEEL_CHAR_2;
5850 case 0x1635: // (blue steel)
5851 element = EL_STEEL_CHAR_3;
5854 case 0x1636: // (blue steel)
5855 element = EL_STEEL_CHAR_4;
5858 case 0x1637: // (blue steel)
5859 element = EL_STEEL_CHAR_5;
5862 case 0x1638: // (blue steel)
5863 element = EL_STEEL_CHAR_6;
5866 case 0x1639: // (blue steel)
5867 element = EL_STEEL_CHAR_7;
5870 case 0x163a: // (blue steel)
5871 element = EL_STEEL_CHAR_8;
5874 case 0x163b: // (blue steel)
5875 element = EL_STEEL_CHAR_9;
5878 case 0x163c: // (blue steel)
5879 element = EL_STEEL_CHAR_PERIOD;
5882 case 0x163d: // (blue steel)
5883 element = EL_STEEL_CHAR_EXCLAM;
5886 case 0x163e: // (blue steel)
5887 element = EL_STEEL_CHAR_COLON;
5890 case 0x163f: // (blue steel)
5891 element = EL_STEEL_CHAR_LESS;
5894 case 0x1640: // (blue steel)
5895 element = EL_STEEL_CHAR_GREATER;
5898 case 0x1641: // (blue steel)
5899 element = EL_STEEL_CHAR_QUESTION;
5902 case 0x1642: // (blue steel)
5903 element = EL_STEEL_CHAR_COPYRIGHT;
5906 case 0x1643: // (blue steel)
5907 element = EL_STEEL_CHAR_UP;
5910 case 0x1644: // (blue steel)
5911 element = EL_STEEL_CHAR_DOWN;
5914 case 0x1645: // (blue steel)
5915 element = EL_STEEL_CHAR_BUTTON;
5918 case 0x1646: // (blue steel)
5919 element = EL_STEEL_CHAR_PLUS;
5922 case 0x1647: // (blue steel)
5923 element = EL_STEEL_CHAR_MINUS;
5926 case 0x1648: // (blue steel)
5927 element = EL_STEEL_CHAR_APOSTROPHE;
5930 case 0x1649: // (blue steel)
5931 element = EL_STEEL_CHAR_PARENLEFT;
5934 case 0x164a: // (blue steel)
5935 element = EL_STEEL_CHAR_PARENRIGHT;
5938 case 0x164b: // (green steel)
5939 element = EL_STEEL_CHAR_A;
5942 case 0x164c: // (green steel)
5943 element = EL_STEEL_CHAR_B;
5946 case 0x164d: // (green steel)
5947 element = EL_STEEL_CHAR_C;
5950 case 0x164e: // (green steel)
5951 element = EL_STEEL_CHAR_D;
5954 case 0x164f: // (green steel)
5955 element = EL_STEEL_CHAR_E;
5958 case 0x1650: // (green steel)
5959 element = EL_STEEL_CHAR_F;
5962 case 0x1651: // (green steel)
5963 element = EL_STEEL_CHAR_G;
5966 case 0x1652: // (green steel)
5967 element = EL_STEEL_CHAR_H;
5970 case 0x1653: // (green steel)
5971 element = EL_STEEL_CHAR_I;
5974 case 0x1654: // (green steel)
5975 element = EL_STEEL_CHAR_J;
5978 case 0x1655: // (green steel)
5979 element = EL_STEEL_CHAR_K;
5982 case 0x1656: // (green steel)
5983 element = EL_STEEL_CHAR_L;
5986 case 0x1657: // (green steel)
5987 element = EL_STEEL_CHAR_M;
5990 case 0x1658: // (green steel)
5991 element = EL_STEEL_CHAR_N;
5994 case 0x1659: // (green steel)
5995 element = EL_STEEL_CHAR_O;
5998 case 0x165a: // (green steel)
5999 element = EL_STEEL_CHAR_P;
6002 case 0x165b: // (green steel)
6003 element = EL_STEEL_CHAR_Q;
6006 case 0x165c: // (green steel)
6007 element = EL_STEEL_CHAR_R;
6010 case 0x165d: // (green steel)
6011 element = EL_STEEL_CHAR_S;
6014 case 0x165e: // (green steel)
6015 element = EL_STEEL_CHAR_T;
6018 case 0x165f: // (green steel)
6019 element = EL_STEEL_CHAR_U;
6022 case 0x1660: // (green steel)
6023 element = EL_STEEL_CHAR_V;
6026 case 0x1661: // (green steel)
6027 element = EL_STEEL_CHAR_W;
6030 case 0x1662: // (green steel)
6031 element = EL_STEEL_CHAR_X;
6034 case 0x1663: // (green steel)
6035 element = EL_STEEL_CHAR_Y;
6038 case 0x1664: // (green steel)
6039 element = EL_STEEL_CHAR_Z;
6042 case 0x1665: // (green steel)
6043 element = EL_STEEL_CHAR_AUMLAUT;
6046 case 0x1666: // (green steel)
6047 element = EL_STEEL_CHAR_OUMLAUT;
6050 case 0x1667: // (green steel)
6051 element = EL_STEEL_CHAR_UUMLAUT;
6054 case 0x1668: // (green steel)
6055 element = EL_STEEL_CHAR_0;
6058 case 0x1669: // (green steel)
6059 element = EL_STEEL_CHAR_1;
6062 case 0x166a: // (green steel)
6063 element = EL_STEEL_CHAR_2;
6066 case 0x166b: // (green steel)
6067 element = EL_STEEL_CHAR_3;
6070 case 0x166c: // (green steel)
6071 element = EL_STEEL_CHAR_4;
6074 case 0x166d: // (green steel)
6075 element = EL_STEEL_CHAR_5;
6078 case 0x166e: // (green steel)
6079 element = EL_STEEL_CHAR_6;
6082 case 0x166f: // (green steel)
6083 element = EL_STEEL_CHAR_7;
6086 case 0x1670: // (green steel)
6087 element = EL_STEEL_CHAR_8;
6090 case 0x1671: // (green steel)
6091 element = EL_STEEL_CHAR_9;
6094 case 0x1672: // (green steel)
6095 element = EL_STEEL_CHAR_PERIOD;
6098 case 0x1673: // (green steel)
6099 element = EL_STEEL_CHAR_EXCLAM;
6102 case 0x1674: // (green steel)
6103 element = EL_STEEL_CHAR_COLON;
6106 case 0x1675: // (green steel)
6107 element = EL_STEEL_CHAR_LESS;
6110 case 0x1676: // (green steel)
6111 element = EL_STEEL_CHAR_GREATER;
6114 case 0x1677: // (green steel)
6115 element = EL_STEEL_CHAR_QUESTION;
6118 case 0x1678: // (green steel)
6119 element = EL_STEEL_CHAR_COPYRIGHT;
6122 case 0x1679: // (green steel)
6123 element = EL_STEEL_CHAR_UP;
6126 case 0x167a: // (green steel)
6127 element = EL_STEEL_CHAR_DOWN;
6130 case 0x167b: // (green steel)
6131 element = EL_STEEL_CHAR_BUTTON;
6134 case 0x167c: // (green steel)
6135 element = EL_STEEL_CHAR_PLUS;
6138 case 0x167d: // (green steel)
6139 element = EL_STEEL_CHAR_MINUS;
6142 case 0x167e: // (green steel)
6143 element = EL_STEEL_CHAR_APOSTROPHE;
6146 case 0x167f: // (green steel)
6147 element = EL_STEEL_CHAR_PARENLEFT;
6150 case 0x1680: // (green steel)
6151 element = EL_STEEL_CHAR_PARENRIGHT;
6154 case 0x1681: // gate (red)
6155 element = EL_EM_GATE_1;
6158 case 0x1682: // secret gate (red)
6159 element = EL_EM_GATE_1_GRAY;
6162 case 0x1683: // gate (yellow)
6163 element = EL_EM_GATE_2;
6166 case 0x1684: // secret gate (yellow)
6167 element = EL_EM_GATE_2_GRAY;
6170 case 0x1685: // gate (blue)
6171 element = EL_EM_GATE_4;
6174 case 0x1686: // secret gate (blue)
6175 element = EL_EM_GATE_4_GRAY;
6178 case 0x1687: // gate (green)
6179 element = EL_EM_GATE_3;
6182 case 0x1688: // secret gate (green)
6183 element = EL_EM_GATE_3_GRAY;
6186 case 0x1689: // gate (white)
6187 element = EL_DC_GATE_WHITE;
6190 case 0x168a: // secret gate (white)
6191 element = EL_DC_GATE_WHITE_GRAY;
6194 case 0x168b: // secret gate (no key)
6195 element = EL_DC_GATE_FAKE_GRAY;
6199 element = EL_ROBOT_WHEEL;
6203 element = EL_DC_TIMEGATE_SWITCH;
6207 element = EL_ACID_POOL_BOTTOM;
6211 element = EL_ACID_POOL_TOPLEFT;
6215 element = EL_ACID_POOL_TOPRIGHT;
6219 element = EL_ACID_POOL_BOTTOMLEFT;
6223 element = EL_ACID_POOL_BOTTOMRIGHT;
6227 element = EL_STEELWALL;
6231 element = EL_STEELWALL_SLIPPERY;
6234 case 0x1695: // steel wall (not round)
6235 element = EL_STEELWALL;
6238 case 0x1696: // steel wall (left)
6239 element = EL_DC_STEELWALL_1_LEFT;
6242 case 0x1697: // steel wall (bottom)
6243 element = EL_DC_STEELWALL_1_BOTTOM;
6246 case 0x1698: // steel wall (right)
6247 element = EL_DC_STEELWALL_1_RIGHT;
6250 case 0x1699: // steel wall (top)
6251 element = EL_DC_STEELWALL_1_TOP;
6254 case 0x169a: // steel wall (left/bottom)
6255 element = EL_DC_STEELWALL_1_BOTTOMLEFT;
6258 case 0x169b: // steel wall (right/bottom)
6259 element = EL_DC_STEELWALL_1_BOTTOMRIGHT;
6262 case 0x169c: // steel wall (right/top)
6263 element = EL_DC_STEELWALL_1_TOPRIGHT;
6266 case 0x169d: // steel wall (left/top)
6267 element = EL_DC_STEELWALL_1_TOPLEFT;
6270 case 0x169e: // steel wall (right/bottom small)
6271 element = EL_DC_STEELWALL_1_BOTTOMRIGHT_2;
6274 case 0x169f: // steel wall (left/bottom small)
6275 element = EL_DC_STEELWALL_1_BOTTOMLEFT_2;
6278 case 0x16a0: // steel wall (right/top small)
6279 element = EL_DC_STEELWALL_1_TOPRIGHT_2;
6282 case 0x16a1: // steel wall (left/top small)
6283 element = EL_DC_STEELWALL_1_TOPLEFT_2;
6286 case 0x16a2: // steel wall (left/right)
6287 element = EL_DC_STEELWALL_1_VERTICAL;
6290 case 0x16a3: // steel wall (top/bottom)
6291 element = EL_DC_STEELWALL_1_HORIZONTAL;
6294 case 0x16a4: // steel wall 2 (left end)
6295 element = EL_DC_STEELWALL_2_LEFT;
6298 case 0x16a5: // steel wall 2 (right end)
6299 element = EL_DC_STEELWALL_2_RIGHT;
6302 case 0x16a6: // steel wall 2 (top end)
6303 element = EL_DC_STEELWALL_2_TOP;
6306 case 0x16a7: // steel wall 2 (bottom end)
6307 element = EL_DC_STEELWALL_2_BOTTOM;
6310 case 0x16a8: // steel wall 2 (left/right)
6311 element = EL_DC_STEELWALL_2_HORIZONTAL;
6314 case 0x16a9: // steel wall 2 (up/down)
6315 element = EL_DC_STEELWALL_2_VERTICAL;
6318 case 0x16aa: // steel wall 2 (mid)
6319 element = EL_DC_STEELWALL_2_MIDDLE;
6323 element = EL_SIGN_EXCLAMATION;
6327 element = EL_SIGN_RADIOACTIVITY;
6331 element = EL_SIGN_STOP;
6335 element = EL_SIGN_WHEELCHAIR;
6339 element = EL_SIGN_PARKING;
6343 element = EL_SIGN_NO_ENTRY;
6347 element = EL_SIGN_HEART;
6351 element = EL_SIGN_GIVE_WAY;
6355 element = EL_SIGN_ENTRY_FORBIDDEN;
6359 element = EL_SIGN_EMERGENCY_EXIT;
6363 element = EL_SIGN_YIN_YANG;
6367 element = EL_WALL_EMERALD;
6371 element = EL_WALL_DIAMOND;
6375 element = EL_WALL_PEARL;
6379 element = EL_WALL_CRYSTAL;
6383 element = EL_INVISIBLE_WALL;
6387 element = EL_INVISIBLE_STEELWALL;
6391 // EL_INVISIBLE_SAND
6394 element = EL_LIGHT_SWITCH;
6398 element = EL_ENVELOPE_1;
6402 if (element >= 0x0117 && element <= 0x036e) // (?)
6403 element = EL_DIAMOND;
6404 else if (element >= 0x042d && element <= 0x0684) // (?)
6405 element = EL_EMERALD;
6406 else if (element >= 0x157c && element <= 0x158b)
6408 else if (element >= 0x1590 && element <= 0x159f)
6409 element = EL_DC_LANDMINE;
6410 else if (element >= 0x16bc && element <= 0x16cb)
6411 element = EL_INVISIBLE_SAND;
6414 Warn("unknown Diamond Caves element 0x%04x", element);
6416 element = EL_UNKNOWN;
6421 return getMappedElement(element);
6424 static void LoadLevelFromFileStream_DC(File *file, struct LevelInfo *level)
6426 byte header[DC_LEVEL_HEADER_SIZE];
6428 int envelope_header_pos = 62;
6429 int envelope_content_pos = 94;
6430 int level_name_pos = 251;
6431 int level_author_pos = 292;
6432 int envelope_header_len;
6433 int envelope_content_len;
6435 int level_author_len;
6437 int num_yamyam_contents;
6440 getDecodedWord_DC(0, TRUE); // initialize DC2 decoding engine
6442 for (i = 0; i < DC_LEVEL_HEADER_SIZE / 2; i++)
6444 unsigned short header_word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
6446 header[i * 2 + 0] = header_word >> 8;
6447 header[i * 2 + 1] = header_word & 0xff;
6450 // read some values from level header to check level decoding integrity
6451 fieldx = header[6] | (header[7] << 8);
6452 fieldy = header[8] | (header[9] << 8);
6453 num_yamyam_contents = header[60] | (header[61] << 8);
6455 // do some simple sanity checks to ensure that level was correctly decoded
6456 if (fieldx < 1 || fieldx > 256 ||
6457 fieldy < 1 || fieldy > 256 ||
6458 num_yamyam_contents < 1 || num_yamyam_contents > 8)
6460 level->no_valid_file = TRUE;
6462 Warn("cannot decode level from stream -- using empty level");
6467 // maximum envelope header size is 31 bytes
6468 envelope_header_len = header[envelope_header_pos];
6469 // maximum envelope content size is 110 (156?) bytes
6470 envelope_content_len = header[envelope_content_pos];
6472 // maximum level title size is 40 bytes
6473 level_name_len = MIN(header[level_name_pos], MAX_LEVEL_NAME_LEN);
6474 // maximum level author size is 30 (51?) bytes
6475 level_author_len = MIN(header[level_author_pos], MAX_LEVEL_AUTHOR_LEN);
6479 for (i = 0; i < envelope_header_len; i++)
6480 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
6481 level->envelope[0].text[envelope_size++] =
6482 header[envelope_header_pos + 1 + i];
6484 if (envelope_header_len > 0 && envelope_content_len > 0)
6486 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
6487 level->envelope[0].text[envelope_size++] = '\n';
6488 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
6489 level->envelope[0].text[envelope_size++] = '\n';
6492 for (i = 0; i < envelope_content_len; i++)
6493 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
6494 level->envelope[0].text[envelope_size++] =
6495 header[envelope_content_pos + 1 + i];
6497 level->envelope[0].text[envelope_size] = '\0';
6499 level->envelope[0].xsize = MAX_ENVELOPE_XSIZE;
6500 level->envelope[0].ysize = 10;
6501 level->envelope[0].autowrap = TRUE;
6502 level->envelope[0].centered = TRUE;
6504 for (i = 0; i < level_name_len; i++)
6505 level->name[i] = header[level_name_pos + 1 + i];
6506 level->name[level_name_len] = '\0';
6508 for (i = 0; i < level_author_len; i++)
6509 level->author[i] = header[level_author_pos + 1 + i];
6510 level->author[level_author_len] = '\0';
6512 num_yamyam_contents = header[60] | (header[61] << 8);
6513 level->num_yamyam_contents =
6514 MIN(MAX(MIN_ELEMENT_CONTENTS, num_yamyam_contents), MAX_ELEMENT_CONTENTS);
6516 for (i = 0; i < num_yamyam_contents; i++)
6518 for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
6520 unsigned short word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
6521 int element_dc = ((word & 0xff) << 8) | ((word >> 8) & 0xff);
6523 if (i < MAX_ELEMENT_CONTENTS)
6524 level->yamyam_content[i].e[x][y] = getMappedElement_DC(element_dc);
6528 fieldx = header[6] | (header[7] << 8);
6529 fieldy = header[8] | (header[9] << 8);
6530 level->fieldx = MIN(MAX(MIN_LEV_FIELDX, fieldx), MAX_LEV_FIELDX);
6531 level->fieldy = MIN(MAX(MIN_LEV_FIELDY, fieldy), MAX_LEV_FIELDY);
6533 for (y = 0; y < fieldy; y++) for (x = 0; x < fieldx; x++)
6535 unsigned short word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
6536 int element_dc = ((word & 0xff) << 8) | ((word >> 8) & 0xff);
6538 if (x < MAX_LEV_FIELDX && y < MAX_LEV_FIELDY)
6539 level->field[x][y] = getMappedElement_DC(element_dc);
6542 x = MIN(MAX(0, (header[10] | (header[11] << 8)) - 1), MAX_LEV_FIELDX - 1);
6543 y = MIN(MAX(0, (header[12] | (header[13] << 8)) - 1), MAX_LEV_FIELDY - 1);
6544 level->field[x][y] = EL_PLAYER_1;
6546 x = MIN(MAX(0, (header[14] | (header[15] << 8)) - 1), MAX_LEV_FIELDX - 1);
6547 y = MIN(MAX(0, (header[16] | (header[17] << 8)) - 1), MAX_LEV_FIELDY - 1);
6548 level->field[x][y] = EL_PLAYER_2;
6550 level->gems_needed = header[18] | (header[19] << 8);
6552 level->score[SC_EMERALD] = header[20] | (header[21] << 8);
6553 level->score[SC_DIAMOND] = header[22] | (header[23] << 8);
6554 level->score[SC_PEARL] = header[24] | (header[25] << 8);
6555 level->score[SC_CRYSTAL] = header[26] | (header[27] << 8);
6556 level->score[SC_NUT] = header[28] | (header[29] << 8);
6557 level->score[SC_ROBOT] = header[30] | (header[31] << 8);
6558 level->score[SC_SPACESHIP] = header[32] | (header[33] << 8);
6559 level->score[SC_BUG] = header[34] | (header[35] << 8);
6560 level->score[SC_YAMYAM] = header[36] | (header[37] << 8);
6561 level->score[SC_DYNAMITE] = header[38] | (header[39] << 8);
6562 level->score[SC_KEY] = header[40] | (header[41] << 8);
6563 level->score[SC_TIME_BONUS] = header[42] | (header[43] << 8);
6565 level->time = header[44] | (header[45] << 8);
6567 level->amoeba_speed = header[46] | (header[47] << 8);
6568 level->time_light = header[48] | (header[49] << 8);
6569 level->time_timegate = header[50] | (header[51] << 8);
6570 level->time_wheel = header[52] | (header[53] << 8);
6571 level->time_magic_wall = header[54] | (header[55] << 8);
6572 level->extra_time = header[56] | (header[57] << 8);
6573 level->shield_normal_time = header[58] | (header[59] << 8);
6575 // shield and extra time elements do not have a score
6576 level->score[SC_SHIELD] = 0;
6577 level->extra_time_score = 0;
6579 // set time for normal and deadly shields to the same value
6580 level->shield_deadly_time = level->shield_normal_time;
6582 // Diamond Caves has the same (strange) behaviour as Emerald Mine that gems
6583 // can slip down from flat walls, like normal walls and steel walls
6584 level->em_slippery_gems = TRUE;
6586 // time score is counted for each 10 seconds left in Diamond Caves levels
6587 level->time_score_base = 10;
6590 static void LoadLevelFromFileInfo_DC(struct LevelInfo *level,
6591 struct LevelFileInfo *level_file_info,
6592 boolean level_info_only)
6594 char *filename = level_file_info->filename;
6596 int num_magic_bytes = 8;
6597 char magic_bytes[num_magic_bytes + 1];
6598 int num_levels_to_skip = level_file_info->nr - leveldir_current->first_level;
6600 if (!(file = openFile(filename, MODE_READ)))
6602 level->no_valid_file = TRUE;
6604 if (!level_info_only)
6605 Warn("cannot read level '%s' -- using empty level", filename);
6610 // fseek(file, 0x0000, SEEK_SET);
6612 if (level_file_info->packed)
6614 // read "magic bytes" from start of file
6615 if (getStringFromFile(file, magic_bytes, num_magic_bytes + 1) == NULL)
6616 magic_bytes[0] = '\0';
6618 // check "magic bytes" for correct file format
6619 if (!strPrefix(magic_bytes, "DC2"))
6621 level->no_valid_file = TRUE;
6623 Warn("unknown DC level file '%s' -- using empty level", filename);
6628 if (strPrefix(magic_bytes, "DC2Win95") ||
6629 strPrefix(magic_bytes, "DC2Win98"))
6631 int position_first_level = 0x00fa;
6632 int extra_bytes = 4;
6635 // advance file stream to first level inside the level package
6636 skip_bytes = position_first_level - num_magic_bytes - extra_bytes;
6638 // each block of level data is followed by block of non-level data
6639 num_levels_to_skip *= 2;
6641 // at least skip header bytes, therefore use ">= 0" instead of "> 0"
6642 while (num_levels_to_skip >= 0)
6644 // advance file stream to next level inside the level package
6645 if (seekFile(file, skip_bytes, SEEK_CUR) != 0)
6647 level->no_valid_file = TRUE;
6649 Warn("cannot fseek in file '%s' -- using empty level", filename);
6654 // skip apparently unused extra bytes following each level
6655 ReadUnusedBytesFromFile(file, extra_bytes);
6657 // read size of next level in level package
6658 skip_bytes = getFile32BitLE(file);
6660 num_levels_to_skip--;
6665 level->no_valid_file = TRUE;
6667 Warn("unknown DC2 level file '%s' -- using empty level", filename);
6673 LoadLevelFromFileStream_DC(file, level);
6679 // ----------------------------------------------------------------------------
6680 // functions for loading SB level
6681 // ----------------------------------------------------------------------------
6683 int getMappedElement_SB(int element_ascii, boolean use_ces)
6691 sb_element_mapping[] =
6693 { ' ', EL_EMPTY, EL_CUSTOM_1 }, // floor (space)
6694 { '#', EL_STEELWALL, EL_CUSTOM_2 }, // wall
6695 { '@', EL_PLAYER_1, EL_CUSTOM_3 }, // player
6696 { '$', EL_SOKOBAN_OBJECT, EL_CUSTOM_4 }, // box
6697 { '.', EL_SOKOBAN_FIELD_EMPTY, EL_CUSTOM_5 }, // goal square
6698 { '*', EL_SOKOBAN_FIELD_FULL, EL_CUSTOM_6 }, // box on goal square
6699 { '+', EL_SOKOBAN_FIELD_PLAYER, EL_CUSTOM_7 }, // player on goal square
6700 { '_', EL_INVISIBLE_STEELWALL, EL_FROM_LEVEL_TEMPLATE }, // floor beyond border
6707 for (i = 0; sb_element_mapping[i].ascii != 0; i++)
6708 if (element_ascii == sb_element_mapping[i].ascii)
6709 return (use_ces ? sb_element_mapping[i].ce : sb_element_mapping[i].sb);
6711 return EL_UNDEFINED;
6714 static void SetLevelSettings_SB(struct LevelInfo *level)
6718 level->use_step_counter = TRUE;
6721 level->score[SC_TIME_BONUS] = 0;
6722 level->time_score_base = 1;
6723 level->rate_time_over_score = TRUE;
6726 level->auto_exit_sokoban = TRUE;
6729 static void LoadLevelFromFileInfo_SB(struct LevelInfo *level,
6730 struct LevelFileInfo *level_file_info,
6731 boolean level_info_only)
6733 char *filename = level_file_info->filename;
6734 char line[MAX_LINE_LEN], line_raw[MAX_LINE_LEN], previous_line[MAX_LINE_LEN];
6735 char last_comment[MAX_LINE_LEN];
6736 char level_name[MAX_LINE_LEN];
6739 int num_levels_to_skip = level_file_info->nr - leveldir_current->first_level;
6740 boolean read_continued_line = FALSE;
6741 boolean reading_playfield = FALSE;
6742 boolean got_valid_playfield_line = FALSE;
6743 boolean invalid_playfield_char = FALSE;
6744 boolean load_xsb_to_ces = check_special_flags("load_xsb_to_ces");
6745 int file_level_nr = 0;
6746 int x = 0, y = 0; // initialized to make compilers happy
6748 last_comment[0] = '\0';
6749 level_name[0] = '\0';
6751 if (!(file = openFile(filename, MODE_READ)))
6753 level->no_valid_file = TRUE;
6755 if (!level_info_only)
6756 Warn("cannot read level '%s' -- using empty level", filename);
6761 while (!checkEndOfFile(file))
6763 // level successfully read, but next level may follow here
6764 if (!got_valid_playfield_line && reading_playfield)
6766 // read playfield from single level file -- skip remaining file
6767 if (!level_file_info->packed)
6770 if (file_level_nr >= num_levels_to_skip)
6775 last_comment[0] = '\0';
6776 level_name[0] = '\0';
6778 reading_playfield = FALSE;
6781 got_valid_playfield_line = FALSE;
6783 // read next line of input file
6784 if (!getStringFromFile(file, line, MAX_LINE_LEN))
6787 // cut trailing line break (this can be newline and/or carriage return)
6788 for (line_ptr = &line[strlen(line)]; line_ptr >= line; line_ptr--)
6789 if ((*line_ptr == '\n' || *line_ptr == '\r') && *(line_ptr + 1) == '\0')
6792 // copy raw input line for later use (mainly debugging output)
6793 strcpy(line_raw, line);
6795 if (read_continued_line)
6797 // append new line to existing line, if there is enough space
6798 if (strlen(previous_line) + strlen(line_ptr) < MAX_LINE_LEN)
6799 strcat(previous_line, line_ptr);
6801 strcpy(line, previous_line); // copy storage buffer to line
6803 read_continued_line = FALSE;
6806 // if the last character is '\', continue at next line
6807 if (strlen(line) > 0 && line[strlen(line) - 1] == '\\')
6809 line[strlen(line) - 1] = '\0'; // cut off trailing backslash
6810 strcpy(previous_line, line); // copy line to storage buffer
6812 read_continued_line = TRUE;
6818 if (line[0] == '\0')
6821 // extract comment text from comment line
6824 for (line_ptr = line; *line_ptr; line_ptr++)
6825 if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != ';')
6828 strcpy(last_comment, line_ptr);
6833 // extract level title text from line containing level title
6834 if (line[0] == '\'')
6836 strcpy(level_name, &line[1]);
6838 if (strlen(level_name) > 0 && level_name[strlen(level_name) - 1] == '\'')
6839 level_name[strlen(level_name) - 1] = '\0';
6844 // skip lines containing only spaces (or empty lines)
6845 for (line_ptr = line; *line_ptr; line_ptr++)
6846 if (*line_ptr != ' ')
6848 if (*line_ptr == '\0')
6851 // at this point, we have found a line containing part of a playfield
6853 got_valid_playfield_line = TRUE;
6855 if (!reading_playfield)
6857 reading_playfield = TRUE;
6858 invalid_playfield_char = FALSE;
6860 for (x = 0; x < MAX_LEV_FIELDX; x++)
6861 for (y = 0; y < MAX_LEV_FIELDY; y++)
6862 level->field[x][y] = getMappedElement_SB(' ', load_xsb_to_ces);
6867 // start with topmost tile row
6871 // skip playfield line if larger row than allowed
6872 if (y >= MAX_LEV_FIELDY)
6875 // start with leftmost tile column
6878 // read playfield elements from line
6879 for (line_ptr = line; *line_ptr; line_ptr++)
6881 int mapped_sb_element = getMappedElement_SB(*line_ptr, load_xsb_to_ces);
6883 // stop parsing playfield line if larger column than allowed
6884 if (x >= MAX_LEV_FIELDX)
6887 if (mapped_sb_element == EL_UNDEFINED)
6889 invalid_playfield_char = TRUE;
6894 level->field[x][y] = mapped_sb_element;
6896 // continue with next tile column
6899 level->fieldx = MAX(x, level->fieldx);
6902 if (invalid_playfield_char)
6904 // if first playfield line, treat invalid lines as comment lines
6906 reading_playfield = FALSE;
6911 // continue with next tile row
6919 level->fieldx = MIN(MAX(MIN_LEV_FIELDX, level->fieldx), MAX_LEV_FIELDX);
6920 level->fieldy = MIN(MAX(MIN_LEV_FIELDY, level->fieldy), MAX_LEV_FIELDY);
6922 if (!reading_playfield)
6924 level->no_valid_file = TRUE;
6926 Warn("cannot read level '%s' -- using empty level", filename);
6931 if (*level_name != '\0')
6933 strncpy(level->name, level_name, MAX_LEVEL_NAME_LEN);
6934 level->name[MAX_LEVEL_NAME_LEN] = '\0';
6936 else if (*last_comment != '\0')
6938 strncpy(level->name, last_comment, MAX_LEVEL_NAME_LEN);
6939 level->name[MAX_LEVEL_NAME_LEN] = '\0';
6943 sprintf(level->name, "--> Level %d <--", level_file_info->nr);
6946 // set all empty fields beyond the border walls to invisible steel wall
6947 for (y = 0; y < level->fieldy; y++) for (x = 0; x < level->fieldx; x++)
6949 if ((x == 0 || x == level->fieldx - 1 ||
6950 y == 0 || y == level->fieldy - 1) &&
6951 level->field[x][y] == getMappedElement_SB(' ', load_xsb_to_ces))
6952 FloodFillLevel(x, y, getMappedElement_SB('_', load_xsb_to_ces),
6953 level->field, level->fieldx, level->fieldy);
6956 // set special level settings for Sokoban levels
6957 SetLevelSettings_SB(level);
6959 if (load_xsb_to_ces)
6961 // special global settings can now be set in level template
6962 level->use_custom_template = TRUE;
6967 // -------------------------------------------------------------------------
6968 // functions for handling native levels
6969 // -------------------------------------------------------------------------
6971 static void LoadLevelFromFileInfo_BD(struct LevelInfo *level,
6972 struct LevelFileInfo *level_file_info,
6973 boolean level_info_only)
6977 // determine position of requested level inside level package
6978 if (level_file_info->packed)
6979 pos = level_file_info->nr - leveldir_current->first_level;
6981 if (!LoadNativeLevel_BD(level_file_info->filename, pos, level_info_only))
6982 level->no_valid_file = TRUE;
6985 static void LoadLevelFromFileInfo_EM(struct LevelInfo *level,
6986 struct LevelFileInfo *level_file_info,
6987 boolean level_info_only)
6989 if (!LoadNativeLevel_EM(level_file_info->filename, level_info_only))
6990 level->no_valid_file = TRUE;
6993 static void LoadLevelFromFileInfo_SP(struct LevelInfo *level,
6994 struct LevelFileInfo *level_file_info,
6995 boolean level_info_only)
6999 // determine position of requested level inside level package
7000 if (level_file_info->packed)
7001 pos = level_file_info->nr - leveldir_current->first_level;
7003 if (!LoadNativeLevel_SP(level_file_info->filename, pos, level_info_only))
7004 level->no_valid_file = TRUE;
7007 static void LoadLevelFromFileInfo_MM(struct LevelInfo *level,
7008 struct LevelFileInfo *level_file_info,
7009 boolean level_info_only)
7011 if (!LoadNativeLevel_MM(level_file_info->filename, level_info_only))
7012 level->no_valid_file = TRUE;
7015 void CopyNativeLevel_RND_to_Native(struct LevelInfo *level)
7017 if (level->game_engine_type == GAME_ENGINE_TYPE_BD)
7018 CopyNativeLevel_RND_to_BD(level);
7019 else if (level->game_engine_type == GAME_ENGINE_TYPE_EM)
7020 CopyNativeLevel_RND_to_EM(level);
7021 else if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
7022 CopyNativeLevel_RND_to_SP(level);
7023 else if (level->game_engine_type == GAME_ENGINE_TYPE_MM)
7024 CopyNativeLevel_RND_to_MM(level);
7027 void CopyNativeLevel_Native_to_RND(struct LevelInfo *level)
7029 if (level->game_engine_type == GAME_ENGINE_TYPE_BD)
7030 CopyNativeLevel_BD_to_RND(level);
7031 else if (level->game_engine_type == GAME_ENGINE_TYPE_EM)
7032 CopyNativeLevel_EM_to_RND(level);
7033 else if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
7034 CopyNativeLevel_SP_to_RND(level);
7035 else if (level->game_engine_type == GAME_ENGINE_TYPE_MM)
7036 CopyNativeLevel_MM_to_RND(level);
7039 void SaveNativeLevel(struct LevelInfo *level)
7041 // saving native level files only supported for some game engines
7042 if (level->game_engine_type != GAME_ENGINE_TYPE_BD &&
7043 level->game_engine_type != GAME_ENGINE_TYPE_SP)
7046 char *file_ext = (level->game_engine_type == GAME_ENGINE_TYPE_BD ? "bd" :
7047 level->game_engine_type == GAME_ENGINE_TYPE_SP ? "sp" : "");
7048 char *basename = getSingleLevelBasenameExt(level->file_info.nr, file_ext);
7049 char *filename = getLevelFilenameFromBasename(basename);
7051 if (fileExists(filename) && !Request("Native level file already exists! Overwrite it?", REQ_ASK))
7054 boolean success = FALSE;
7056 if (level->game_engine_type == GAME_ENGINE_TYPE_BD)
7058 CopyNativeLevel_RND_to_BD(level);
7059 // CopyNativeTape_RND_to_BD(level);
7061 success = SaveNativeLevel_BD(filename);
7063 else if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
7065 CopyNativeLevel_RND_to_SP(level);
7066 CopyNativeTape_RND_to_SP(level);
7068 success = SaveNativeLevel_SP(filename);
7072 Request("Native level file saved!", REQ_CONFIRM);
7074 Request("Failed to save native level file!", REQ_CONFIRM);
7078 // ----------------------------------------------------------------------------
7079 // functions for loading generic level
7080 // ----------------------------------------------------------------------------
7082 static void LoadLevelFromFileInfo(struct LevelInfo *level,
7083 struct LevelFileInfo *level_file_info,
7084 boolean level_info_only)
7086 // always start with reliable default values
7087 setLevelInfoToDefaults(level, level_info_only, TRUE);
7089 switch (level_file_info->type)
7091 case LEVEL_FILE_TYPE_RND:
7092 LoadLevelFromFileInfo_RND(level, level_file_info, level_info_only);
7095 case LEVEL_FILE_TYPE_BD:
7096 LoadLevelFromFileInfo_BD(level, level_file_info, level_info_only);
7097 level->game_engine_type = GAME_ENGINE_TYPE_BD;
7100 case LEVEL_FILE_TYPE_EM:
7101 LoadLevelFromFileInfo_EM(level, level_file_info, level_info_only);
7102 level->game_engine_type = GAME_ENGINE_TYPE_EM;
7105 case LEVEL_FILE_TYPE_SP:
7106 LoadLevelFromFileInfo_SP(level, level_file_info, level_info_only);
7107 level->game_engine_type = GAME_ENGINE_TYPE_SP;
7110 case LEVEL_FILE_TYPE_MM:
7111 LoadLevelFromFileInfo_MM(level, level_file_info, level_info_only);
7112 level->game_engine_type = GAME_ENGINE_TYPE_MM;
7115 case LEVEL_FILE_TYPE_DC:
7116 LoadLevelFromFileInfo_DC(level, level_file_info, level_info_only);
7119 case LEVEL_FILE_TYPE_SB:
7120 LoadLevelFromFileInfo_SB(level, level_file_info, level_info_only);
7124 LoadLevelFromFileInfo_RND(level, level_file_info, level_info_only);
7128 // if level file is invalid, restore level structure to default values
7129 if (level->no_valid_file)
7130 setLevelInfoToDefaults(level, level_info_only, FALSE);
7132 if (check_special_flags("use_native_bd_game_engine"))
7133 level->game_engine_type = GAME_ENGINE_TYPE_BD;
7135 if (level->game_engine_type == GAME_ENGINE_TYPE_UNKNOWN)
7136 level->game_engine_type = GAME_ENGINE_TYPE_RND;
7138 if (level_file_info->type != LEVEL_FILE_TYPE_RND)
7139 CopyNativeLevel_Native_to_RND(level);
7142 void LoadLevelFromFilename(struct LevelInfo *level, char *filename)
7144 static struct LevelFileInfo level_file_info;
7146 // always start with reliable default values
7147 setFileInfoToDefaults(&level_file_info);
7149 level_file_info.nr = 0; // unknown level number
7150 level_file_info.type = LEVEL_FILE_TYPE_RND; // no others supported yet
7152 setString(&level_file_info.filename, filename);
7154 LoadLevelFromFileInfo(level, &level_file_info, FALSE);
7157 static void LoadLevel_InitVersion(struct LevelInfo *level)
7161 if (leveldir_current == NULL) // only when dumping level
7164 // all engine modifications also valid for levels which use latest engine
7165 if (level->game_version < VERSION_IDENT(3,2,0,5))
7167 // time bonus score was given for 10 s instead of 1 s before 3.2.0-5
7168 level->time_score_base = 10;
7171 if (leveldir_current->latest_engine)
7173 // ---------- use latest game engine --------------------------------------
7175 /* For all levels which are forced to use the latest game engine version
7176 (normally all but user contributed, private and undefined levels), set
7177 the game engine version to the actual version; this allows for actual
7178 corrections in the game engine to take effect for existing, converted
7179 levels (from "classic" or other existing games) to make the emulation
7180 of the corresponding game more accurate, while (hopefully) not breaking
7181 existing levels created from other players. */
7183 level->game_version = GAME_VERSION_ACTUAL;
7185 /* Set special EM style gems behaviour: EM style gems slip down from
7186 normal, steel and growing wall. As this is a more fundamental change,
7187 it seems better to set the default behaviour to "off" (as it is more
7188 natural) and make it configurable in the level editor (as a property
7189 of gem style elements). Already existing converted levels (neither
7190 private nor contributed levels) are changed to the new behaviour. */
7192 if (level->file_version < FILE_VERSION_2_0)
7193 level->em_slippery_gems = TRUE;
7198 // ---------- use game engine the level was created with --------------------
7200 /* For all levels which are not forced to use the latest game engine
7201 version (normally user contributed, private and undefined levels),
7202 use the version of the game engine the levels were created for.
7204 Since 2.0.1, the game engine version is now directly stored
7205 in the level file (chunk "VERS"), so there is no need anymore
7206 to set the game version from the file version (except for old,
7207 pre-2.0 levels, where the game version is still taken from the
7208 file format version used to store the level -- see above). */
7210 // player was faster than enemies in 1.0.0 and before
7211 if (level->file_version == FILE_VERSION_1_0)
7212 for (i = 0; i < MAX_PLAYERS; i++)
7213 level->initial_player_stepsize[i] = STEPSIZE_FAST;
7215 // default behaviour for EM style gems was "slippery" only in 2.0.1
7216 if (level->game_version == VERSION_IDENT(2,0,1,0))
7217 level->em_slippery_gems = TRUE;
7219 // springs could be pushed over pits before (pre-release version) 2.2.0
7220 if (level->game_version < VERSION_IDENT(2,2,0,0))
7221 level->use_spring_bug = TRUE;
7223 if (level->game_version < VERSION_IDENT(3,2,0,5))
7225 // time orb caused limited time in endless time levels before 3.2.0-5
7226 level->use_time_orb_bug = TRUE;
7228 // default behaviour for snapping was "no snap delay" before 3.2.0-5
7229 level->block_snap_field = FALSE;
7231 // extra time score was same value as time left score before 3.2.0-5
7232 level->extra_time_score = level->score[SC_TIME_BONUS];
7235 if (level->game_version < VERSION_IDENT(3,2,0,7))
7237 // default behaviour for snapping was "not continuous" before 3.2.0-7
7238 level->continuous_snapping = FALSE;
7241 // only few elements were able to actively move into acid before 3.1.0
7242 // trigger settings did not exist before 3.1.0; set to default "any"
7243 if (level->game_version < VERSION_IDENT(3,1,0,0))
7245 // correct "can move into acid" settings (all zero in old levels)
7247 level->can_move_into_acid_bits = 0; // nothing can move into acid
7248 level->dont_collide_with_bits = 0; // nothing is deadly when colliding
7250 setMoveIntoAcidProperty(level, EL_ROBOT, TRUE);
7251 setMoveIntoAcidProperty(level, EL_SATELLITE, TRUE);
7252 setMoveIntoAcidProperty(level, EL_PENGUIN, TRUE);
7253 setMoveIntoAcidProperty(level, EL_BALLOON, TRUE);
7255 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7256 SET_PROPERTY(EL_CUSTOM_START + i, EP_CAN_MOVE_INTO_ACID, TRUE);
7258 // correct trigger settings (stored as zero == "none" in old levels)
7260 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7262 int element = EL_CUSTOM_START + i;
7263 struct ElementInfo *ei = &element_info[element];
7265 for (j = 0; j < ei->num_change_pages; j++)
7267 struct ElementChangeInfo *change = &ei->change_page[j];
7269 change->trigger_player = CH_PLAYER_ANY;
7270 change->trigger_page = CH_PAGE_ANY;
7275 // try to detect and fix "Snake Bite" levels, which are broken with 3.2.0
7277 int element = EL_CUSTOM_256;
7278 struct ElementInfo *ei = &element_info[element];
7279 struct ElementChangeInfo *change = &ei->change_page[0];
7281 /* This is needed to fix a problem that was caused by a bugfix in function
7282 game.c/CreateFieldExt() introduced with 3.2.0 that corrects the behaviour
7283 when a custom element changes to EL_SOKOBAN_FIELD_PLAYER (before, it did
7284 not replace walkable elements, but instead just placed the player on it,
7285 without placing the Sokoban field under the player). Unfortunately, this
7286 breaks "Snake Bite" style levels when the snake is halfway through a door
7287 that just closes (the snake head is still alive and can be moved in this
7288 case). This can be fixed by replacing the EL_SOKOBAN_FIELD_PLAYER by the
7289 player (without Sokoban element) which then gets killed as designed). */
7291 if ((strncmp(leveldir_current->identifier, "snake_bite", 10) == 0 ||
7292 strncmp(ei->description, "pause b4 death", 14) == 0) &&
7293 change->target_element == EL_SOKOBAN_FIELD_PLAYER)
7294 change->target_element = EL_PLAYER_1;
7297 // try to detect and fix "Zelda" style levels, which are broken with 3.2.5
7298 if (level->game_version < VERSION_IDENT(3,2,5,0))
7300 /* This is needed to fix a problem that was caused by a bugfix in function
7301 game.c/CheckTriggeredElementChangeExt() introduced with 3.2.5 that
7302 corrects the behaviour when a custom element changes to another custom
7303 element with a higher element number that has change actions defined.
7304 Normally, only one change per frame is allowed for custom elements.
7305 Therefore, it is checked if a custom element already changed in the
7306 current frame; if it did, subsequent changes are suppressed.
7307 Unfortunately, this is only checked for element changes, but not for
7308 change actions, which are still executed. As the function above loops
7309 through all custom elements from lower to higher, an element change
7310 resulting in a lower CE number won't be checked again, while a target
7311 element with a higher number will also be checked, and potential change
7312 actions will get executed for this CE, too (which is wrong), while
7313 further changes are ignored (which is correct). As this bugfix breaks
7314 Zelda II (and introduces graphical bugs to Zelda I, and also breaks a
7315 few other levels like Alan Bond's "FMV"), allow the previous, incorrect
7316 behaviour for existing levels and tapes that make use of this bug */
7318 level->use_action_after_change_bug = TRUE;
7321 // not centering level after relocating player was default only in 3.2.3
7322 if (level->game_version == VERSION_IDENT(3,2,3,0)) // (no pre-releases)
7323 level->shifted_relocation = TRUE;
7325 // EM style elements always chain-exploded in R'n'D engine before 3.2.6
7326 if (level->game_version < VERSION_IDENT(3,2,6,0))
7327 level->em_explodes_by_fire = TRUE;
7329 // levels were solved by the first player entering an exit up to 4.1.0.0
7330 if (level->game_version <= VERSION_IDENT(4,1,0,0))
7331 level->solved_by_one_player = TRUE;
7333 // game logic of "game of life" and "biomaze" was buggy before 4.1.1.1
7334 if (level->game_version < VERSION_IDENT(4,1,1,1))
7335 level->use_life_bugs = TRUE;
7337 // only Sokoban fields (but not objects) had to be solved before 4.1.1.1
7338 if (level->game_version < VERSION_IDENT(4,1,1,1))
7339 level->sb_objects_needed = FALSE;
7341 // CE actions were triggered by unfinished digging/collecting up to 4.2.2.0
7342 if (level->game_version <= VERSION_IDENT(4,2,2,0))
7343 level->finish_dig_collect = FALSE;
7345 // CE changing to player was kept under the player if walkable up to 4.2.3.1
7346 if (level->game_version <= VERSION_IDENT(4,2,3,1))
7347 level->keep_walkable_ce = TRUE;
7350 static void LoadLevel_InitSettings_SB(struct LevelInfo *level)
7352 boolean is_sokoban_level = TRUE; // unless non-Sokoban elements found
7355 // check if this level is (not) a Sokoban level
7356 for (y = 0; y < level->fieldy; y++)
7357 for (x = 0; x < level->fieldx; x++)
7358 if (!IS_SB_ELEMENT(Tile[x][y]))
7359 is_sokoban_level = FALSE;
7361 if (is_sokoban_level)
7363 // set special level settings for Sokoban levels
7364 SetLevelSettings_SB(level);
7368 static void LoadLevel_InitSettings(struct LevelInfo *level)
7370 // adjust level settings for (non-native) Sokoban-style levels
7371 LoadLevel_InitSettings_SB(level);
7373 // rename levels with title "nameless level" or if renaming is forced
7374 if (leveldir_current->empty_level_name != NULL &&
7375 (strEqual(level->name, NAMELESS_LEVEL_NAME) ||
7376 leveldir_current->force_level_name))
7377 snprintf(level->name, MAX_LEVEL_NAME_LEN + 1,
7378 leveldir_current->empty_level_name, level_nr);
7381 static void LoadLevel_InitStandardElements(struct LevelInfo *level)
7385 // map elements that have changed in newer versions
7386 level->amoeba_content = getMappedElementByVersion(level->amoeba_content,
7387 level->game_version);
7388 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
7389 for (x = 0; x < 3; x++)
7390 for (y = 0; y < 3; y++)
7391 level->yamyam_content[i].e[x][y] =
7392 getMappedElementByVersion(level->yamyam_content[i].e[x][y],
7393 level->game_version);
7397 static void LoadLevel_InitCustomElements(struct LevelInfo *level)
7401 // map custom element change events that have changed in newer versions
7402 // (these following values were accidentally changed in version 3.0.1)
7403 // (this seems to be needed only for 'ab_levelset3' and 'ab_levelset4')
7404 if (level->game_version <= VERSION_IDENT(3,0,0,0))
7406 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7408 int element = EL_CUSTOM_START + i;
7410 // order of checking and copying events to be mapped is important
7411 // (do not change the start and end value -- they are constant)
7412 for (j = CE_BY_OTHER_ACTION; j >= CE_VALUE_GETS_ZERO; j--)
7414 if (HAS_CHANGE_EVENT(element, j - 2))
7416 SET_CHANGE_EVENT(element, j - 2, FALSE);
7417 SET_CHANGE_EVENT(element, j, TRUE);
7421 // order of checking and copying events to be mapped is important
7422 // (do not change the start and end value -- they are constant)
7423 for (j = CE_PLAYER_COLLECTS_X; j >= CE_HITTING_SOMETHING; j--)
7425 if (HAS_CHANGE_EVENT(element, j - 1))
7427 SET_CHANGE_EVENT(element, j - 1, FALSE);
7428 SET_CHANGE_EVENT(element, j, TRUE);
7434 // initialize "can_change" field for old levels with only one change page
7435 if (level->game_version <= VERSION_IDENT(3,0,2,0))
7437 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7439 int element = EL_CUSTOM_START + i;
7441 if (CAN_CHANGE(element))
7442 element_info[element].change->can_change = TRUE;
7446 // correct custom element values (for old levels without these options)
7447 if (level->game_version < VERSION_IDENT(3,1,1,0))
7449 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7451 int element = EL_CUSTOM_START + i;
7452 struct ElementInfo *ei = &element_info[element];
7454 if (ei->access_direction == MV_NO_DIRECTION)
7455 ei->access_direction = MV_ALL_DIRECTIONS;
7459 // correct custom element values (fix invalid values for all versions)
7462 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7464 int element = EL_CUSTOM_START + i;
7465 struct ElementInfo *ei = &element_info[element];
7467 for (j = 0; j < ei->num_change_pages; j++)
7469 struct ElementChangeInfo *change = &ei->change_page[j];
7471 if (change->trigger_player == CH_PLAYER_NONE)
7472 change->trigger_player = CH_PLAYER_ANY;
7474 if (change->trigger_side == CH_SIDE_NONE)
7475 change->trigger_side = CH_SIDE_ANY;
7480 // initialize "can_explode" field for old levels which did not store this
7481 // !!! CHECK THIS -- "<= 3,1,0,0" IS PROBABLY WRONG !!!
7482 if (level->game_version <= VERSION_IDENT(3,1,0,0))
7484 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7486 int element = EL_CUSTOM_START + i;
7488 if (EXPLODES_1X1_OLD(element))
7489 element_info[element].explosion_type = EXPLODES_1X1;
7491 SET_PROPERTY(element, EP_CAN_EXPLODE, (EXPLODES_BY_FIRE(element) ||
7492 EXPLODES_SMASHED(element) ||
7493 EXPLODES_IMPACT(element)));
7497 // correct previously hard-coded move delay values for maze runner style
7498 if (level->game_version < VERSION_IDENT(3,1,1,0))
7500 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7502 int element = EL_CUSTOM_START + i;
7504 if (element_info[element].move_pattern & MV_MAZE_RUNNER_STYLE)
7506 // previously hard-coded and therefore ignored
7507 element_info[element].move_delay_fixed = 9;
7508 element_info[element].move_delay_random = 0;
7513 // set some other uninitialized values of custom elements in older levels
7514 if (level->game_version < VERSION_IDENT(3,1,0,0))
7516 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7518 int element = EL_CUSTOM_START + i;
7520 element_info[element].access_direction = MV_ALL_DIRECTIONS;
7522 element_info[element].explosion_delay = 17;
7523 element_info[element].ignition_delay = 8;
7527 // set mouse click change events to work for left/middle/right mouse button
7528 if (level->game_version < VERSION_IDENT(4,2,3,0))
7530 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7532 int element = EL_CUSTOM_START + i;
7533 struct ElementInfo *ei = &element_info[element];
7535 for (j = 0; j < ei->num_change_pages; j++)
7537 struct ElementChangeInfo *change = &ei->change_page[j];
7539 if (change->has_event[CE_CLICKED_BY_MOUSE] ||
7540 change->has_event[CE_PRESSED_BY_MOUSE] ||
7541 change->has_event[CE_MOUSE_CLICKED_ON_X] ||
7542 change->has_event[CE_MOUSE_PRESSED_ON_X])
7543 change->trigger_side = CH_SIDE_ANY;
7549 static void LoadLevel_InitElements(struct LevelInfo *level)
7551 LoadLevel_InitStandardElements(level);
7553 if (level->file_has_custom_elements)
7554 LoadLevel_InitCustomElements(level);
7556 // initialize element properties for level editor etc.
7557 InitElementPropertiesEngine(level->game_version);
7558 InitElementPropertiesGfxElement();
7561 static void LoadLevel_InitPlayfield(struct LevelInfo *level)
7565 // map elements that have changed in newer versions
7566 for (y = 0; y < level->fieldy; y++)
7567 for (x = 0; x < level->fieldx; x++)
7568 level->field[x][y] = getMappedElementByVersion(level->field[x][y],
7569 level->game_version);
7571 // clear unused playfield data (nicer if level gets resized in editor)
7572 for (x = 0; x < MAX_LEV_FIELDX; x++)
7573 for (y = 0; y < MAX_LEV_FIELDY; y++)
7574 if (x >= level->fieldx || y >= level->fieldy)
7575 level->field[x][y] = EL_EMPTY;
7577 // copy elements to runtime playfield array
7578 for (x = 0; x < MAX_LEV_FIELDX; x++)
7579 for (y = 0; y < MAX_LEV_FIELDY; y++)
7580 Tile[x][y] = level->field[x][y];
7582 // initialize level size variables for faster access
7583 lev_fieldx = level->fieldx;
7584 lev_fieldy = level->fieldy;
7586 // determine border element for this level
7587 if (level->file_info.type == LEVEL_FILE_TYPE_DC)
7588 BorderElement = EL_EMPTY; // (in editor, SetBorderElement() is used)
7593 static void LoadLevel_InitNativeEngines(struct LevelInfo *level)
7595 struct LevelFileInfo *level_file_info = &level->file_info;
7597 if (level_file_info->type == LEVEL_FILE_TYPE_RND)
7598 CopyNativeLevel_RND_to_Native(level);
7601 static void LoadLevelTemplate_LoadAndInit(void)
7603 LoadLevelFromFileInfo(&level_template, &level_template.file_info, FALSE);
7605 LoadLevel_InitVersion(&level_template);
7606 LoadLevel_InitElements(&level_template);
7607 LoadLevel_InitSettings(&level_template);
7609 ActivateLevelTemplate();
7612 void LoadLevelTemplate(int nr)
7614 if (!fileExists(getGlobalLevelTemplateFilename()))
7616 Warn("no level template found for this level");
7621 setLevelFileInfo(&level_template.file_info, nr);
7623 LoadLevelTemplate_LoadAndInit();
7626 static void LoadNetworkLevelTemplate(struct NetworkLevelInfo *network_level)
7628 copyLevelFileInfo(&network_level->tmpl_info, &level_template.file_info);
7630 LoadLevelTemplate_LoadAndInit();
7633 static void LoadLevel_LoadAndInit(struct NetworkLevelInfo *network_level)
7635 LoadLevelFromFileInfo(&level, &level.file_info, FALSE);
7637 if (level.use_custom_template)
7639 if (network_level != NULL)
7640 LoadNetworkLevelTemplate(network_level);
7642 LoadLevelTemplate(-1);
7645 LoadLevel_InitVersion(&level);
7646 LoadLevel_InitElements(&level);
7647 LoadLevel_InitPlayfield(&level);
7648 LoadLevel_InitSettings(&level);
7650 LoadLevel_InitNativeEngines(&level);
7653 void LoadLevel(int nr)
7655 SetLevelSetInfo(leveldir_current->identifier, nr);
7657 setLevelFileInfo(&level.file_info, nr);
7659 LoadLevel_LoadAndInit(NULL);
7662 void LoadLevelInfoOnly(int nr)
7664 setLevelFileInfo(&level.file_info, nr);
7666 LoadLevelFromFileInfo(&level, &level.file_info, TRUE);
7669 void LoadNetworkLevel(struct NetworkLevelInfo *network_level)
7671 SetLevelSetInfo(network_level->leveldir_identifier,
7672 network_level->file_info.nr);
7674 copyLevelFileInfo(&network_level->file_info, &level.file_info);
7676 LoadLevel_LoadAndInit(network_level);
7679 static int SaveLevel_VERS(FILE *file, struct LevelInfo *level)
7683 chunk_size += putFileVersion(file, level->file_version);
7684 chunk_size += putFileVersion(file, level->game_version);
7689 static int SaveLevel_DATE(FILE *file, struct LevelInfo *level)
7693 chunk_size += putFile16BitBE(file, level->creation_date.year);
7694 chunk_size += putFile8Bit(file, level->creation_date.month);
7695 chunk_size += putFile8Bit(file, level->creation_date.day);
7700 #if ENABLE_HISTORIC_CHUNKS
7701 static void SaveLevel_HEAD(FILE *file, struct LevelInfo *level)
7705 putFile8Bit(file, level->fieldx);
7706 putFile8Bit(file, level->fieldy);
7708 putFile16BitBE(file, level->time);
7709 putFile16BitBE(file, level->gems_needed);
7711 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
7712 putFile8Bit(file, level->name[i]);
7714 for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
7715 putFile8Bit(file, level->score[i]);
7717 for (i = 0; i < STD_ELEMENT_CONTENTS; i++)
7718 for (y = 0; y < 3; y++)
7719 for (x = 0; x < 3; x++)
7720 putFile8Bit(file, (level->encoding_16bit_yamyam ? EL_EMPTY :
7721 level->yamyam_content[i].e[x][y]));
7722 putFile8Bit(file, level->amoeba_speed);
7723 putFile8Bit(file, level->time_magic_wall);
7724 putFile8Bit(file, level->time_wheel);
7725 putFile8Bit(file, (level->encoding_16bit_amoeba ? EL_EMPTY :
7726 level->amoeba_content));
7727 putFile8Bit(file, (level->initial_player_stepsize == STEPSIZE_FAST ? 1 : 0));
7728 putFile8Bit(file, (level->initial_gravity ? 1 : 0));
7729 putFile8Bit(file, (level->encoding_16bit_field ? 1 : 0));
7730 putFile8Bit(file, (level->em_slippery_gems ? 1 : 0));
7732 putFile8Bit(file, (level->use_custom_template ? 1 : 0));
7734 putFile8Bit(file, (level->block_last_field ? 1 : 0));
7735 putFile8Bit(file, (level->sp_block_last_field ? 1 : 0));
7736 putFile32BitBE(file, level->can_move_into_acid_bits);
7737 putFile8Bit(file, level->dont_collide_with_bits);
7739 putFile8Bit(file, (level->use_spring_bug ? 1 : 0));
7740 putFile8Bit(file, (level->use_step_counter ? 1 : 0));
7742 putFile8Bit(file, (level->instant_relocation ? 1 : 0));
7743 putFile8Bit(file, (level->can_pass_to_walkable ? 1 : 0));
7744 putFile8Bit(file, (level->grow_into_diggable ? 1 : 0));
7746 putFile8Bit(file, level->game_engine_type);
7748 WriteUnusedBytesToFile(file, LEVEL_CHUNK_HEAD_UNUSED);
7752 static int SaveLevel_NAME(FILE *file, struct LevelInfo *level)
7757 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
7758 chunk_size += putFile8Bit(file, level->name[i]);
7763 static int SaveLevel_AUTH(FILE *file, struct LevelInfo *level)
7768 for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
7769 chunk_size += putFile8Bit(file, level->author[i]);
7774 #if ENABLE_HISTORIC_CHUNKS
7775 static int SaveLevel_BODY(FILE *file, struct LevelInfo *level)
7780 for (y = 0; y < level->fieldy; y++)
7781 for (x = 0; x < level->fieldx; x++)
7782 if (level->encoding_16bit_field)
7783 chunk_size += putFile16BitBE(file, level->field[x][y]);
7785 chunk_size += putFile8Bit(file, level->field[x][y]);
7791 static int SaveLevel_BODY(FILE *file, struct LevelInfo *level)
7796 for (y = 0; y < level->fieldy; y++)
7797 for (x = 0; x < level->fieldx; x++)
7798 chunk_size += putFile16BitBE(file, level->field[x][y]);
7803 #if ENABLE_HISTORIC_CHUNKS
7804 static void SaveLevel_CONT(FILE *file, struct LevelInfo *level)
7808 putFile8Bit(file, EL_YAMYAM);
7809 putFile8Bit(file, level->num_yamyam_contents);
7810 putFile8Bit(file, 0);
7811 putFile8Bit(file, 0);
7813 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
7814 for (y = 0; y < 3; y++)
7815 for (x = 0; x < 3; x++)
7816 if (level->encoding_16bit_field)
7817 putFile16BitBE(file, level->yamyam_content[i].e[x][y]);
7819 putFile8Bit(file, level->yamyam_content[i].e[x][y]);
7823 #if ENABLE_HISTORIC_CHUNKS
7824 static void SaveLevel_CNT2(FILE *file, struct LevelInfo *level, int element)
7827 int num_contents, content_xsize, content_ysize;
7828 int content_array[MAX_ELEMENT_CONTENTS][3][3];
7830 if (element == EL_YAMYAM)
7832 num_contents = level->num_yamyam_contents;
7836 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
7837 for (y = 0; y < 3; y++)
7838 for (x = 0; x < 3; x++)
7839 content_array[i][x][y] = level->yamyam_content[i].e[x][y];
7841 else if (element == EL_BD_AMOEBA)
7847 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
7848 for (y = 0; y < 3; y++)
7849 for (x = 0; x < 3; x++)
7850 content_array[i][x][y] = EL_EMPTY;
7851 content_array[0][0][0] = level->amoeba_content;
7855 // chunk header already written -- write empty chunk data
7856 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_SIZE);
7858 Warn("cannot save content for element '%d'", element);
7863 putFile16BitBE(file, element);
7864 putFile8Bit(file, num_contents);
7865 putFile8Bit(file, content_xsize);
7866 putFile8Bit(file, content_ysize);
7868 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_UNUSED);
7870 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
7871 for (y = 0; y < 3; y++)
7872 for (x = 0; x < 3; x++)
7873 putFile16BitBE(file, content_array[i][x][y]);
7877 #if ENABLE_HISTORIC_CHUNKS
7878 static int SaveLevel_CNT3(FILE *file, struct LevelInfo *level, int element)
7880 int envelope_nr = element - EL_ENVELOPE_1;
7881 int envelope_len = strlen(level->envelope_text[envelope_nr]) + 1;
7885 chunk_size += putFile16BitBE(file, element);
7886 chunk_size += putFile16BitBE(file, envelope_len);
7887 chunk_size += putFile8Bit(file, level->envelope_xsize[envelope_nr]);
7888 chunk_size += putFile8Bit(file, level->envelope_ysize[envelope_nr]);
7890 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT3_UNUSED);
7891 chunk_size += LEVEL_CHUNK_CNT3_UNUSED;
7893 for (i = 0; i < envelope_len; i++)
7894 chunk_size += putFile8Bit(file, level->envelope_text[envelope_nr][i]);
7900 #if ENABLE_HISTORIC_CHUNKS
7901 static void SaveLevel_CUS1(FILE *file, struct LevelInfo *level,
7902 int num_changed_custom_elements)
7906 putFile16BitBE(file, num_changed_custom_elements);
7908 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7910 int element = EL_CUSTOM_START + i;
7912 struct ElementInfo *ei = &element_info[element];
7914 if (ei->properties[EP_BITFIELD_BASE_NR] != EP_BITMASK_DEFAULT)
7916 if (check < num_changed_custom_elements)
7918 putFile16BitBE(file, element);
7919 putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
7926 if (check != num_changed_custom_elements) // should not happen
7927 Warn("inconsistent number of custom element properties");
7931 #if ENABLE_HISTORIC_CHUNKS
7932 static void SaveLevel_CUS2(FILE *file, struct LevelInfo *level,
7933 int num_changed_custom_elements)
7937 putFile16BitBE(file, num_changed_custom_elements);
7939 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7941 int element = EL_CUSTOM_START + i;
7943 if (element_info[element].change->target_element != EL_EMPTY_SPACE)
7945 if (check < num_changed_custom_elements)
7947 putFile16BitBE(file, element);
7948 putFile16BitBE(file, element_info[element].change->target_element);
7955 if (check != num_changed_custom_elements) // should not happen
7956 Warn("inconsistent number of custom target elements");
7960 #if ENABLE_HISTORIC_CHUNKS
7961 static void SaveLevel_CUS3(FILE *file, struct LevelInfo *level,
7962 int num_changed_custom_elements)
7964 int i, j, x, y, check = 0;
7966 putFile16BitBE(file, num_changed_custom_elements);
7968 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7970 int element = EL_CUSTOM_START + i;
7971 struct ElementInfo *ei = &element_info[element];
7973 if (ei->modified_settings)
7975 if (check < num_changed_custom_elements)
7977 putFile16BitBE(file, element);
7979 for (j = 0; j < MAX_ELEMENT_NAME_LEN; j++)
7980 putFile8Bit(file, ei->description[j]);
7982 putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
7984 // some free bytes for future properties and padding
7985 WriteUnusedBytesToFile(file, 7);
7987 putFile8Bit(file, ei->use_gfx_element);
7988 putFile16BitBE(file, ei->gfx_element_initial);
7990 putFile8Bit(file, ei->collect_score_initial);
7991 putFile8Bit(file, ei->collect_count_initial);
7993 putFile16BitBE(file, ei->push_delay_fixed);
7994 putFile16BitBE(file, ei->push_delay_random);
7995 putFile16BitBE(file, ei->move_delay_fixed);
7996 putFile16BitBE(file, ei->move_delay_random);
7998 putFile16BitBE(file, ei->move_pattern);
7999 putFile8Bit(file, ei->move_direction_initial);
8000 putFile8Bit(file, ei->move_stepsize);
8002 for (y = 0; y < 3; y++)
8003 for (x = 0; x < 3; x++)
8004 putFile16BitBE(file, ei->content.e[x][y]);
8006 putFile32BitBE(file, ei->change->events);
8008 putFile16BitBE(file, ei->change->target_element);
8010 putFile16BitBE(file, ei->change->delay_fixed);
8011 putFile16BitBE(file, ei->change->delay_random);
8012 putFile16BitBE(file, ei->change->delay_frames);
8014 putFile16BitBE(file, ei->change->initial_trigger_element);
8016 putFile8Bit(file, ei->change->explode);
8017 putFile8Bit(file, ei->change->use_target_content);
8018 putFile8Bit(file, ei->change->only_if_complete);
8019 putFile8Bit(file, ei->change->use_random_replace);
8021 putFile8Bit(file, ei->change->random_percentage);
8022 putFile8Bit(file, ei->change->replace_when);
8024 for (y = 0; y < 3; y++)
8025 for (x = 0; x < 3; x++)
8026 putFile16BitBE(file, ei->change->content.e[x][y]);
8028 putFile8Bit(file, ei->slippery_type);
8030 // some free bytes for future properties and padding
8031 WriteUnusedBytesToFile(file, LEVEL_CPART_CUS3_UNUSED);
8038 if (check != num_changed_custom_elements) // should not happen
8039 Warn("inconsistent number of custom element properties");
8043 #if ENABLE_HISTORIC_CHUNKS
8044 static void SaveLevel_CUS4(FILE *file, struct LevelInfo *level, int element)
8046 struct ElementInfo *ei = &element_info[element];
8049 // ---------- custom element base property values (96 bytes) ----------------
8051 putFile16BitBE(file, element);
8053 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
8054 putFile8Bit(file, ei->description[i]);
8056 putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
8058 WriteUnusedBytesToFile(file, 4); // reserved for more base properties
8060 putFile8Bit(file, ei->num_change_pages);
8062 putFile16BitBE(file, ei->ce_value_fixed_initial);
8063 putFile16BitBE(file, ei->ce_value_random_initial);
8064 putFile8Bit(file, ei->use_last_ce_value);
8066 putFile8Bit(file, ei->use_gfx_element);
8067 putFile16BitBE(file, ei->gfx_element_initial);
8069 putFile8Bit(file, ei->collect_score_initial);
8070 putFile8Bit(file, ei->collect_count_initial);
8072 putFile8Bit(file, ei->drop_delay_fixed);
8073 putFile8Bit(file, ei->push_delay_fixed);
8074 putFile8Bit(file, ei->drop_delay_random);
8075 putFile8Bit(file, ei->push_delay_random);
8076 putFile16BitBE(file, ei->move_delay_fixed);
8077 putFile16BitBE(file, ei->move_delay_random);
8079 // bits 0 - 15 of "move_pattern" ...
8080 putFile16BitBE(file, ei->move_pattern & 0xffff);
8081 putFile8Bit(file, ei->move_direction_initial);
8082 putFile8Bit(file, ei->move_stepsize);
8084 putFile8Bit(file, ei->slippery_type);
8086 for (y = 0; y < 3; y++)
8087 for (x = 0; x < 3; x++)
8088 putFile16BitBE(file, ei->content.e[x][y]);
8090 putFile16BitBE(file, ei->move_enter_element);
8091 putFile16BitBE(file, ei->move_leave_element);
8092 putFile8Bit(file, ei->move_leave_type);
8094 // ... bits 16 - 31 of "move_pattern" (not nice, but downward compatible)
8095 putFile16BitBE(file, (ei->move_pattern >> 16) & 0xffff);
8097 putFile8Bit(file, ei->access_direction);
8099 putFile8Bit(file, ei->explosion_delay);
8100 putFile8Bit(file, ei->ignition_delay);
8101 putFile8Bit(file, ei->explosion_type);
8103 // some free bytes for future custom property values and padding
8104 WriteUnusedBytesToFile(file, 1);
8106 // ---------- change page property values (48 bytes) ------------------------
8108 for (i = 0; i < ei->num_change_pages; i++)
8110 struct ElementChangeInfo *change = &ei->change_page[i];
8111 unsigned int event_bits;
8113 // bits 0 - 31 of "has_event[]" ...
8115 for (j = 0; j < MIN(NUM_CHANGE_EVENTS, 32); j++)
8116 if (change->has_event[j])
8117 event_bits |= (1u << j);
8118 putFile32BitBE(file, event_bits);
8120 putFile16BitBE(file, change->target_element);
8122 putFile16BitBE(file, change->delay_fixed);
8123 putFile16BitBE(file, change->delay_random);
8124 putFile16BitBE(file, change->delay_frames);
8126 putFile16BitBE(file, change->initial_trigger_element);
8128 putFile8Bit(file, change->explode);
8129 putFile8Bit(file, change->use_target_content);
8130 putFile8Bit(file, change->only_if_complete);
8131 putFile8Bit(file, change->use_random_replace);
8133 putFile8Bit(file, change->random_percentage);
8134 putFile8Bit(file, change->replace_when);
8136 for (y = 0; y < 3; y++)
8137 for (x = 0; x < 3; x++)
8138 putFile16BitBE(file, change->target_content.e[x][y]);
8140 putFile8Bit(file, change->can_change);
8142 putFile8Bit(file, change->trigger_side);
8144 putFile8Bit(file, change->trigger_player);
8145 putFile8Bit(file, (change->trigger_page == CH_PAGE_ANY ? CH_PAGE_ANY_FILE :
8146 log_2(change->trigger_page)));
8148 putFile8Bit(file, change->has_action);
8149 putFile8Bit(file, change->action_type);
8150 putFile8Bit(file, change->action_mode);
8151 putFile16BitBE(file, change->action_arg);
8153 // ... bits 32 - 39 of "has_event[]" (not nice, but downward compatible)
8155 for (j = 32; j < NUM_CHANGE_EVENTS; j++)
8156 if (change->has_event[j])
8157 event_bits |= (1u << (j - 32));
8158 putFile8Bit(file, event_bits);
8163 #if ENABLE_HISTORIC_CHUNKS
8164 static void SaveLevel_GRP1(FILE *file, struct LevelInfo *level, int element)
8166 struct ElementInfo *ei = &element_info[element];
8167 struct ElementGroupInfo *group = ei->group;
8170 putFile16BitBE(file, element);
8172 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
8173 putFile8Bit(file, ei->description[i]);
8175 putFile8Bit(file, group->num_elements);
8177 putFile8Bit(file, ei->use_gfx_element);
8178 putFile16BitBE(file, ei->gfx_element_initial);
8180 putFile8Bit(file, group->choice_mode);
8182 // some free bytes for future values and padding
8183 WriteUnusedBytesToFile(file, 3);
8185 for (i = 0; i < MAX_ELEMENTS_IN_GROUP; i++)
8186 putFile16BitBE(file, group->element[i]);
8190 static int SaveLevel_MicroChunk(FILE *file, struct LevelFileConfigInfo *entry,
8191 boolean write_element)
8193 int save_type = entry->save_type;
8194 int data_type = entry->data_type;
8195 int conf_type = entry->conf_type;
8196 int byte_mask = conf_type & CONF_MASK_BYTES;
8197 int element = entry->element;
8198 int default_value = entry->default_value;
8200 boolean modified = FALSE;
8202 if (byte_mask != CONF_MASK_MULTI_BYTES)
8204 void *value_ptr = entry->value;
8205 int value = (data_type == TYPE_BOOLEAN ? *(boolean *)value_ptr :
8208 // check if any settings have been modified before saving them
8209 if (value != default_value)
8212 // do not save if explicitly told or if unmodified default settings
8213 if ((save_type == SAVE_CONF_NEVER) ||
8214 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
8218 num_bytes += putFile16BitBE(file, element);
8220 num_bytes += putFile8Bit(file, conf_type);
8221 num_bytes += (byte_mask == CONF_MASK_1_BYTE ? putFile8Bit (file, value) :
8222 byte_mask == CONF_MASK_2_BYTE ? putFile16BitBE(file, value) :
8223 byte_mask == CONF_MASK_4_BYTE ? putFile32BitBE(file, value) :
8226 else if (data_type == TYPE_STRING)
8228 char *default_string = entry->default_string;
8229 char *string = (char *)(entry->value);
8230 int string_length = strlen(string);
8233 // check if any settings have been modified before saving them
8234 if (!strEqual(string, default_string))
8237 // do not save if explicitly told or if unmodified default settings
8238 if ((save_type == SAVE_CONF_NEVER) ||
8239 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
8243 num_bytes += putFile16BitBE(file, element);
8245 num_bytes += putFile8Bit(file, conf_type);
8246 num_bytes += putFile16BitBE(file, string_length);
8248 for (i = 0; i < string_length; i++)
8249 num_bytes += putFile8Bit(file, string[i]);
8251 else if (data_type == TYPE_ELEMENT_LIST)
8253 int *element_array = (int *)(entry->value);
8254 int num_elements = *(int *)(entry->num_entities);
8257 // check if any settings have been modified before saving them
8258 for (i = 0; i < num_elements; i++)
8259 if (element_array[i] != default_value)
8262 // do not save if explicitly told or if unmodified default settings
8263 if ((save_type == SAVE_CONF_NEVER) ||
8264 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
8268 num_bytes += putFile16BitBE(file, element);
8270 num_bytes += putFile8Bit(file, conf_type);
8271 num_bytes += putFile16BitBE(file, num_elements * CONF_ELEMENT_NUM_BYTES);
8273 for (i = 0; i < num_elements; i++)
8274 num_bytes += putFile16BitBE(file, element_array[i]);
8276 else if (data_type == TYPE_CONTENT_LIST)
8278 struct Content *content = (struct Content *)(entry->value);
8279 int num_contents = *(int *)(entry->num_entities);
8282 // check if any settings have been modified before saving them
8283 for (i = 0; i < num_contents; i++)
8284 for (y = 0; y < 3; y++)
8285 for (x = 0; x < 3; x++)
8286 if (content[i].e[x][y] != default_value)
8289 // do not save if explicitly told or if unmodified default settings
8290 if ((save_type == SAVE_CONF_NEVER) ||
8291 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
8295 num_bytes += putFile16BitBE(file, element);
8297 num_bytes += putFile8Bit(file, conf_type);
8298 num_bytes += putFile16BitBE(file, num_contents * CONF_CONTENT_NUM_BYTES);
8300 for (i = 0; i < num_contents; i++)
8301 for (y = 0; y < 3; y++)
8302 for (x = 0; x < 3; x++)
8303 num_bytes += putFile16BitBE(file, content[i].e[x][y]);
8309 static int SaveLevel_INFO(FILE *file, struct LevelInfo *level)
8314 li = *level; // copy level data into temporary buffer
8316 for (i = 0; chunk_config_INFO[i].data_type != -1; i++)
8317 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_INFO[i], FALSE);
8322 static int SaveLevel_ELEM(FILE *file, struct LevelInfo *level)
8327 li = *level; // copy level data into temporary buffer
8329 for (i = 0; chunk_config_ELEM[i].data_type != -1; i++)
8330 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_ELEM[i], TRUE);
8335 static int SaveLevel_NOTE(FILE *file, struct LevelInfo *level, int element)
8337 int envelope_nr = element - EL_ENVELOPE_1;
8341 chunk_size += putFile16BitBE(file, element);
8343 // copy envelope data into temporary buffer
8344 xx_envelope = level->envelope[envelope_nr];
8346 for (i = 0; chunk_config_NOTE[i].data_type != -1; i++)
8347 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_NOTE[i], FALSE);
8352 static int SaveLevel_CUSX(FILE *file, struct LevelInfo *level, int element)
8354 struct ElementInfo *ei = &element_info[element];
8358 chunk_size += putFile16BitBE(file, element);
8360 xx_ei = *ei; // copy element data into temporary buffer
8362 // set default description string for this specific element
8363 strcpy(xx_default_description, getDefaultElementDescription(ei));
8365 for (i = 0; chunk_config_CUSX_base[i].data_type != -1; i++)
8366 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_CUSX_base[i], FALSE);
8368 for (i = 0; i < ei->num_change_pages; i++)
8370 struct ElementChangeInfo *change = &ei->change_page[i];
8372 xx_current_change_page = i;
8374 xx_change = *change; // copy change data into temporary buffer
8377 setEventBitsFromEventFlags(change);
8379 for (j = 0; chunk_config_CUSX_change[j].data_type != -1; j++)
8380 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_CUSX_change[j],
8387 static int SaveLevel_GRPX(FILE *file, struct LevelInfo *level, int element)
8389 struct ElementInfo *ei = &element_info[element];
8390 struct ElementGroupInfo *group = ei->group;
8394 chunk_size += putFile16BitBE(file, element);
8396 xx_ei = *ei; // copy element data into temporary buffer
8397 xx_group = *group; // copy group data into temporary buffer
8399 // set default description string for this specific element
8400 strcpy(xx_default_description, getDefaultElementDescription(ei));
8402 for (i = 0; chunk_config_GRPX[i].data_type != -1; i++)
8403 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_GRPX[i], FALSE);
8408 static int SaveLevel_EMPX(FILE *file, struct LevelInfo *level, int element)
8410 struct ElementInfo *ei = &element_info[element];
8414 chunk_size += putFile16BitBE(file, element);
8416 xx_ei = *ei; // copy element data into temporary buffer
8418 for (i = 0; chunk_config_EMPX[i].data_type != -1; i++)
8419 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_EMPX[i], FALSE);
8424 static void SaveLevelFromFilename(struct LevelInfo *level, char *filename,
8425 boolean save_as_template)
8431 if (!(file = fopen(filename, MODE_WRITE)))
8433 Warn("cannot save level file '%s'", filename);
8438 level->file_version = FILE_VERSION_ACTUAL;
8439 level->game_version = GAME_VERSION_ACTUAL;
8441 level->creation_date = getCurrentDate();
8443 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
8444 putFileChunkBE(file, "CAVE", CHUNK_SIZE_NONE);
8446 chunk_size = SaveLevel_VERS(NULL, level);
8447 putFileChunkBE(file, "VERS", chunk_size);
8448 SaveLevel_VERS(file, level);
8450 chunk_size = SaveLevel_DATE(NULL, level);
8451 putFileChunkBE(file, "DATE", chunk_size);
8452 SaveLevel_DATE(file, level);
8454 chunk_size = SaveLevel_NAME(NULL, level);
8455 putFileChunkBE(file, "NAME", chunk_size);
8456 SaveLevel_NAME(file, level);
8458 chunk_size = SaveLevel_AUTH(NULL, level);
8459 putFileChunkBE(file, "AUTH", chunk_size);
8460 SaveLevel_AUTH(file, level);
8462 chunk_size = SaveLevel_INFO(NULL, level);
8463 putFileChunkBE(file, "INFO", chunk_size);
8464 SaveLevel_INFO(file, level);
8466 chunk_size = SaveLevel_BODY(NULL, level);
8467 putFileChunkBE(file, "BODY", chunk_size);
8468 SaveLevel_BODY(file, level);
8470 chunk_size = SaveLevel_ELEM(NULL, level);
8471 if (chunk_size > LEVEL_CHUNK_ELEM_UNCHANGED) // save if changed
8473 putFileChunkBE(file, "ELEM", chunk_size);
8474 SaveLevel_ELEM(file, level);
8477 for (i = 0; i < NUM_ENVELOPES; i++)
8479 int element = EL_ENVELOPE_1 + i;
8481 chunk_size = SaveLevel_NOTE(NULL, level, element);
8482 if (chunk_size > LEVEL_CHUNK_NOTE_UNCHANGED) // save if changed
8484 putFileChunkBE(file, "NOTE", chunk_size);
8485 SaveLevel_NOTE(file, level, element);
8489 // if not using template level, check for non-default custom/group elements
8490 if (!level->use_custom_template || save_as_template)
8492 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
8494 int element = EL_CUSTOM_START + i;
8496 chunk_size = SaveLevel_CUSX(NULL, level, element);
8497 if (chunk_size > LEVEL_CHUNK_CUSX_UNCHANGED) // save if changed
8499 putFileChunkBE(file, "CUSX", chunk_size);
8500 SaveLevel_CUSX(file, level, element);
8504 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
8506 int element = EL_GROUP_START + i;
8508 chunk_size = SaveLevel_GRPX(NULL, level, element);
8509 if (chunk_size > LEVEL_CHUNK_GRPX_UNCHANGED) // save if changed
8511 putFileChunkBE(file, "GRPX", chunk_size);
8512 SaveLevel_GRPX(file, level, element);
8516 for (i = 0; i < NUM_EMPTY_ELEMENTS_ALL; i++)
8518 int element = GET_EMPTY_ELEMENT(i);
8520 chunk_size = SaveLevel_EMPX(NULL, level, element);
8521 if (chunk_size > LEVEL_CHUNK_EMPX_UNCHANGED) // save if changed
8523 putFileChunkBE(file, "EMPX", chunk_size);
8524 SaveLevel_EMPX(file, level, element);
8531 SetFilePermissions(filename, PERMS_PRIVATE);
8534 void SaveLevel(int nr)
8536 char *filename = getDefaultLevelFilename(nr);
8538 SaveLevelFromFilename(&level, filename, FALSE);
8541 void SaveLevelTemplate(void)
8543 char *filename = getLocalLevelTemplateFilename();
8545 SaveLevelFromFilename(&level, filename, TRUE);
8548 boolean SaveLevelChecked(int nr)
8550 char *filename = getDefaultLevelFilename(nr);
8551 boolean new_level = !fileExists(filename);
8552 boolean level_saved = FALSE;
8554 if (new_level || Request("Save this level and kill the old?", REQ_ASK))
8559 Request("Level saved!", REQ_CONFIRM);
8567 void DumpLevel(struct LevelInfo *level)
8569 if (level->no_level_file || level->no_valid_file)
8571 Warn("cannot dump -- no valid level file found");
8577 Print("Level xxx (file version %08d, game version %08d)\n",
8578 level->file_version, level->game_version);
8581 Print("Level author: '%s'\n", level->author);
8582 Print("Level title: '%s'\n", level->name);
8584 Print("Playfield size: %d x %d\n", level->fieldx, level->fieldy);
8586 Print("Level time: %d seconds\n", level->time);
8587 Print("Gems needed: %d\n", level->gems_needed);
8589 Print("Time for magic wall: %d seconds\n", level->time_magic_wall);
8590 Print("Time for wheel: %d seconds\n", level->time_wheel);
8591 Print("Time for light: %d seconds\n", level->time_light);
8592 Print("Time for timegate: %d seconds\n", level->time_timegate);
8594 Print("Amoeba speed: %d\n", level->amoeba_speed);
8597 Print("EM style slippery gems: %s\n", (level->em_slippery_gems ? "yes" : "no"));
8598 Print("Player blocks last field: %s\n", (level->block_last_field ? "yes" : "no"));
8599 Print("SP player blocks last field: %s\n", (level->sp_block_last_field ? "yes" : "no"));
8600 Print("use spring bug: %s\n", (level->use_spring_bug ? "yes" : "no"));
8601 Print("use step counter: %s\n", (level->use_step_counter ? "yes" : "no"));
8602 Print("rate time over score: %s\n", (level->rate_time_over_score ? "yes" : "no"));
8608 for (i = 0; i < NUM_ENVELOPES; i++)
8610 char *text = level->envelope[i].text;
8611 int text_len = strlen(text);
8612 boolean has_text = FALSE;
8614 for (j = 0; j < text_len; j++)
8615 if (text[j] != ' ' && text[j] != '\n')
8621 Print("Envelope %d:\n'%s'\n", i + 1, text);
8629 void DumpLevels(void)
8631 static LevelDirTree *dumplevel_leveldir = NULL;
8633 dumplevel_leveldir = getTreeInfoFromIdentifier(leveldir_first,
8634 global.dumplevel_leveldir);
8636 if (dumplevel_leveldir == NULL)
8637 Fail("no such level identifier: '%s'", global.dumplevel_leveldir);
8639 if (global.dumplevel_level_nr < dumplevel_leveldir->first_level ||
8640 global.dumplevel_level_nr > dumplevel_leveldir->last_level)
8641 Fail("no such level number: %d", global.dumplevel_level_nr);
8643 leveldir_current = dumplevel_leveldir;
8645 LoadLevel(global.dumplevel_level_nr);
8652 // ============================================================================
8653 // tape file functions
8654 // ============================================================================
8656 static void setTapeInfoToDefaults(void)
8660 // always start with reliable default values (empty tape)
8663 // default values (also for pre-1.2 tapes) with only the first player
8664 tape.player_participates[0] = TRUE;
8665 for (i = 1; i < MAX_PLAYERS; i++)
8666 tape.player_participates[i] = FALSE;
8668 // at least one (default: the first) player participates in every tape
8669 tape.num_participating_players = 1;
8671 tape.property_bits = TAPE_PROPERTY_NONE;
8673 tape.level_nr = level_nr;
8675 tape.changed = FALSE;
8676 tape.solved = FALSE;
8678 tape.recording = FALSE;
8679 tape.playing = FALSE;
8680 tape.pausing = FALSE;
8682 tape.scr_fieldx = SCR_FIELDX_DEFAULT;
8683 tape.scr_fieldy = SCR_FIELDY_DEFAULT;
8685 tape.no_info_chunk = TRUE;
8686 tape.no_valid_file = FALSE;
8689 static int getTapePosSize(struct TapeInfo *tape)
8691 int tape_pos_size = 0;
8693 if (tape->use_key_actions)
8694 tape_pos_size += tape->num_participating_players;
8696 if (tape->use_mouse_actions)
8697 tape_pos_size += 3; // x and y position and mouse button mask
8699 tape_pos_size += 1; // tape action delay value
8701 return tape_pos_size;
8704 static void setTapeActionFlags(struct TapeInfo *tape, int value)
8706 tape->use_key_actions = FALSE;
8707 tape->use_mouse_actions = FALSE;
8709 if (value != TAPE_USE_MOUSE_ACTIONS_ONLY)
8710 tape->use_key_actions = TRUE;
8712 if (value != TAPE_USE_KEY_ACTIONS_ONLY)
8713 tape->use_mouse_actions = TRUE;
8716 static int getTapeActionValue(struct TapeInfo *tape)
8718 return (tape->use_key_actions &&
8719 tape->use_mouse_actions ? TAPE_USE_KEY_AND_MOUSE_ACTIONS :
8720 tape->use_key_actions ? TAPE_USE_KEY_ACTIONS_ONLY :
8721 tape->use_mouse_actions ? TAPE_USE_MOUSE_ACTIONS_ONLY :
8722 TAPE_ACTIONS_DEFAULT);
8725 static int LoadTape_VERS(File *file, int chunk_size, struct TapeInfo *tape)
8727 tape->file_version = getFileVersion(file);
8728 tape->game_version = getFileVersion(file);
8733 static int LoadTape_HEAD(File *file, int chunk_size, struct TapeInfo *tape)
8737 tape->random_seed = getFile32BitBE(file);
8738 tape->date = getFile32BitBE(file);
8739 tape->length = getFile32BitBE(file);
8741 // read header fields that are new since version 1.2
8742 if (tape->file_version >= FILE_VERSION_1_2)
8744 byte store_participating_players = getFile8Bit(file);
8747 // since version 1.2, tapes store which players participate in the tape
8748 tape->num_participating_players = 0;
8749 for (i = 0; i < MAX_PLAYERS; i++)
8751 tape->player_participates[i] = FALSE;
8753 if (store_participating_players & (1 << i))
8755 tape->player_participates[i] = TRUE;
8756 tape->num_participating_players++;
8760 setTapeActionFlags(tape, getFile8Bit(file));
8762 tape->property_bits = getFile8Bit(file);
8763 tape->solved = getFile8Bit(file);
8765 engine_version = getFileVersion(file);
8766 if (engine_version > 0)
8767 tape->engine_version = engine_version;
8769 tape->engine_version = tape->game_version;
8775 static int LoadTape_SCRN(File *file, int chunk_size, struct TapeInfo *tape)
8777 tape->scr_fieldx = getFile8Bit(file);
8778 tape->scr_fieldy = getFile8Bit(file);
8783 static int LoadTape_INFO(File *file, int chunk_size, struct TapeInfo *tape)
8785 char *level_identifier = NULL;
8786 int level_identifier_size;
8789 tape->no_info_chunk = FALSE;
8791 level_identifier_size = getFile16BitBE(file);
8793 level_identifier = checked_malloc(level_identifier_size);
8795 for (i = 0; i < level_identifier_size; i++)
8796 level_identifier[i] = getFile8Bit(file);
8798 strncpy(tape->level_identifier, level_identifier, MAX_FILENAME_LEN);
8799 tape->level_identifier[MAX_FILENAME_LEN] = '\0';
8801 checked_free(level_identifier);
8803 tape->level_nr = getFile16BitBE(file);
8805 chunk_size = 2 + level_identifier_size + 2;
8810 static int LoadTape_BODY(File *file, int chunk_size, struct TapeInfo *tape)
8813 int tape_pos_size = getTapePosSize(tape);
8814 int chunk_size_expected = tape_pos_size * tape->length;
8816 if (chunk_size_expected != chunk_size)
8818 ReadUnusedBytesFromFile(file, chunk_size);
8819 return chunk_size_expected;
8822 for (i = 0; i < tape->length; i++)
8824 if (i >= MAX_TAPE_LEN)
8826 Warn("tape truncated -- size exceeds maximum tape size %d",
8829 // tape too large; read and ignore remaining tape data from this chunk
8830 for (;i < tape->length; i++)
8831 ReadUnusedBytesFromFile(file, tape_pos_size);
8836 if (tape->use_key_actions)
8838 for (j = 0; j < MAX_PLAYERS; j++)
8840 tape->pos[i].action[j] = MV_NONE;
8842 if (tape->player_participates[j])
8843 tape->pos[i].action[j] = getFile8Bit(file);
8847 if (tape->use_mouse_actions)
8849 tape->pos[i].action[TAPE_ACTION_LX] = getFile8Bit(file);
8850 tape->pos[i].action[TAPE_ACTION_LY] = getFile8Bit(file);
8851 tape->pos[i].action[TAPE_ACTION_BUTTON] = getFile8Bit(file);
8854 tape->pos[i].delay = getFile8Bit(file);
8856 if (tape->file_version == FILE_VERSION_1_0)
8858 // eliminate possible diagonal moves in old tapes
8859 // this is only for backward compatibility
8861 byte joy_dir[4] = { JOY_LEFT, JOY_RIGHT, JOY_UP, JOY_DOWN };
8862 byte action = tape->pos[i].action[0];
8863 int k, num_moves = 0;
8865 for (k = 0; k < 4; k++)
8867 if (action & joy_dir[k])
8869 tape->pos[i + num_moves].action[0] = joy_dir[k];
8871 tape->pos[i + num_moves].delay = 0;
8880 tape->length += num_moves;
8883 else if (tape->file_version < FILE_VERSION_2_0)
8885 // convert pre-2.0 tapes to new tape format
8887 if (tape->pos[i].delay > 1)
8890 tape->pos[i + 1] = tape->pos[i];
8891 tape->pos[i + 1].delay = 1;
8894 for (j = 0; j < MAX_PLAYERS; j++)
8895 tape->pos[i].action[j] = MV_NONE;
8896 tape->pos[i].delay--;
8903 if (checkEndOfFile(file))
8907 if (i != tape->length)
8908 chunk_size = tape_pos_size * i;
8913 static void LoadTape_SokobanSolution(char *filename)
8916 int move_delay = TILESIZE / level.initial_player_stepsize[0];
8918 if (!(file = openFile(filename, MODE_READ)))
8920 tape.no_valid_file = TRUE;
8925 while (!checkEndOfFile(file))
8927 unsigned char c = getByteFromFile(file);
8929 if (checkEndOfFile(file))
8936 tape.pos[tape.length].action[0] = MV_UP;
8937 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
8943 tape.pos[tape.length].action[0] = MV_DOWN;
8944 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
8950 tape.pos[tape.length].action[0] = MV_LEFT;
8951 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
8957 tape.pos[tape.length].action[0] = MV_RIGHT;
8958 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
8966 // ignore white-space characters
8970 tape.no_valid_file = TRUE;
8972 Warn("unsupported Sokoban solution file '%s' ['%d']", filename, c);
8980 if (tape.no_valid_file)
8983 tape.length_frames = GetTapeLengthFrames();
8984 tape.length_seconds = GetTapeLengthSeconds();
8987 void LoadTapeFromFilename(char *filename)
8989 char cookie[MAX_LINE_LEN];
8990 char chunk_name[CHUNK_ID_LEN + 1];
8994 // always start with reliable default values
8995 setTapeInfoToDefaults();
8997 if (strSuffix(filename, ".sln"))
8999 LoadTape_SokobanSolution(filename);
9004 if (!(file = openFile(filename, MODE_READ)))
9006 tape.no_valid_file = TRUE;
9011 getFileChunkBE(file, chunk_name, NULL);
9012 if (strEqual(chunk_name, "RND1"))
9014 getFile32BitBE(file); // not used
9016 getFileChunkBE(file, chunk_name, NULL);
9017 if (!strEqual(chunk_name, "TAPE"))
9019 tape.no_valid_file = TRUE;
9021 Warn("unknown format of tape file '%s'", filename);
9028 else // check for pre-2.0 file format with cookie string
9030 strcpy(cookie, chunk_name);
9031 if (getStringFromFile(file, &cookie[4], MAX_LINE_LEN - 4) == NULL)
9033 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
9034 cookie[strlen(cookie) - 1] = '\0';
9036 if (!checkCookieString(cookie, TAPE_COOKIE_TMPL))
9038 tape.no_valid_file = TRUE;
9040 Warn("unknown format of tape file '%s'", filename);
9047 if ((tape.file_version = getFileVersionFromCookieString(cookie)) == -1)
9049 tape.no_valid_file = TRUE;
9051 Warn("unsupported version of tape file '%s'", filename);
9058 // pre-2.0 tape files have no game version, so use file version here
9059 tape.game_version = tape.file_version;
9062 if (tape.file_version < FILE_VERSION_1_2)
9064 // tape files from versions before 1.2.0 without chunk structure
9065 LoadTape_HEAD(file, TAPE_CHUNK_HEAD_SIZE, &tape);
9066 LoadTape_BODY(file, 2 * tape.length, &tape);
9074 int (*loader)(File *, int, struct TapeInfo *);
9078 { "VERS", TAPE_CHUNK_VERS_SIZE, LoadTape_VERS },
9079 { "HEAD", TAPE_CHUNK_HEAD_SIZE, LoadTape_HEAD },
9080 { "SCRN", TAPE_CHUNK_SCRN_SIZE, LoadTape_SCRN },
9081 { "INFO", -1, LoadTape_INFO },
9082 { "BODY", -1, LoadTape_BODY },
9086 while (getFileChunkBE(file, chunk_name, &chunk_size))
9090 while (chunk_info[i].name != NULL &&
9091 !strEqual(chunk_name, chunk_info[i].name))
9094 if (chunk_info[i].name == NULL)
9096 Warn("unknown chunk '%s' in tape file '%s'",
9097 chunk_name, filename);
9099 ReadUnusedBytesFromFile(file, chunk_size);
9101 else if (chunk_info[i].size != -1 &&
9102 chunk_info[i].size != chunk_size)
9104 Warn("wrong size (%d) of chunk '%s' in tape file '%s'",
9105 chunk_size, chunk_name, filename);
9107 ReadUnusedBytesFromFile(file, chunk_size);
9111 // call function to load this tape chunk
9112 int chunk_size_expected =
9113 (chunk_info[i].loader)(file, chunk_size, &tape);
9115 // the size of some chunks cannot be checked before reading other
9116 // chunks first (like "HEAD" and "BODY") that contain some header
9117 // information, so check them here
9118 if (chunk_size_expected != chunk_size)
9120 Warn("wrong size (%d) of chunk '%s' in tape file '%s'",
9121 chunk_size, chunk_name, filename);
9129 tape.length_frames = GetTapeLengthFrames();
9130 tape.length_seconds = GetTapeLengthSeconds();
9133 Debug("files:LoadTapeFromFilename", "tape file version: %d",
9135 Debug("files:LoadTapeFromFilename", "tape game version: %d",
9137 Debug("files:LoadTapeFromFilename", "tape engine version: %d",
9138 tape.engine_version);
9142 void LoadTape(int nr)
9144 char *filename = getTapeFilename(nr);
9146 LoadTapeFromFilename(filename);
9149 void LoadSolutionTape(int nr)
9151 char *filename = getSolutionTapeFilename(nr);
9153 LoadTapeFromFilename(filename);
9155 if (TAPE_IS_EMPTY(tape))
9157 if (level.game_engine_type == GAME_ENGINE_TYPE_BD &&
9158 level.native_bd_level->replay != NULL)
9159 CopyNativeTape_BD_to_RND(&level);
9160 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP &&
9161 level.native_sp_level->demo.is_available)
9162 CopyNativeTape_SP_to_RND(&level);
9166 void LoadScoreTape(char *score_tape_basename, int nr)
9168 char *filename = getScoreTapeFilename(score_tape_basename, nr);
9170 LoadTapeFromFilename(filename);
9173 void LoadScoreCacheTape(char *score_tape_basename, int nr)
9175 char *filename = getScoreCacheTapeFilename(score_tape_basename, nr);
9177 LoadTapeFromFilename(filename);
9180 static boolean checkSaveTape_SCRN(struct TapeInfo *tape)
9182 // chunk required for team mode tapes with non-default screen size
9183 return (tape->num_participating_players > 1 &&
9184 (tape->scr_fieldx != SCR_FIELDX_DEFAULT ||
9185 tape->scr_fieldy != SCR_FIELDY_DEFAULT));
9188 static void SaveTape_VERS(FILE *file, struct TapeInfo *tape)
9190 putFileVersion(file, tape->file_version);
9191 putFileVersion(file, tape->game_version);
9194 static void SaveTape_HEAD(FILE *file, struct TapeInfo *tape)
9197 byte store_participating_players = 0;
9199 // set bits for participating players for compact storage
9200 for (i = 0; i < MAX_PLAYERS; i++)
9201 if (tape->player_participates[i])
9202 store_participating_players |= (1 << i);
9204 putFile32BitBE(file, tape->random_seed);
9205 putFile32BitBE(file, tape->date);
9206 putFile32BitBE(file, tape->length);
9208 putFile8Bit(file, store_participating_players);
9210 putFile8Bit(file, getTapeActionValue(tape));
9212 putFile8Bit(file, tape->property_bits);
9213 putFile8Bit(file, tape->solved);
9215 putFileVersion(file, tape->engine_version);
9218 static void SaveTape_SCRN(FILE *file, struct TapeInfo *tape)
9220 putFile8Bit(file, tape->scr_fieldx);
9221 putFile8Bit(file, tape->scr_fieldy);
9224 static void SaveTape_INFO(FILE *file, struct TapeInfo *tape)
9226 int level_identifier_size = strlen(tape->level_identifier) + 1;
9229 putFile16BitBE(file, level_identifier_size);
9231 for (i = 0; i < level_identifier_size; i++)
9232 putFile8Bit(file, tape->level_identifier[i]);
9234 putFile16BitBE(file, tape->level_nr);
9237 static void SaveTape_BODY(FILE *file, struct TapeInfo *tape)
9241 for (i = 0; i < tape->length; i++)
9243 if (tape->use_key_actions)
9245 for (j = 0; j < MAX_PLAYERS; j++)
9246 if (tape->player_participates[j])
9247 putFile8Bit(file, tape->pos[i].action[j]);
9250 if (tape->use_mouse_actions)
9252 putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_LX]);
9253 putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_LY]);
9254 putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_BUTTON]);
9257 putFile8Bit(file, tape->pos[i].delay);
9261 void SaveTapeToFilename(char *filename)
9265 int info_chunk_size;
9266 int body_chunk_size;
9268 if (!(file = fopen(filename, MODE_WRITE)))
9270 Warn("cannot save level recording file '%s'", filename);
9275 tape_pos_size = getTapePosSize(&tape);
9277 info_chunk_size = 2 + (strlen(tape.level_identifier) + 1) + 2;
9278 body_chunk_size = tape_pos_size * tape.length;
9280 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
9281 putFileChunkBE(file, "TAPE", CHUNK_SIZE_NONE);
9283 putFileChunkBE(file, "VERS", TAPE_CHUNK_VERS_SIZE);
9284 SaveTape_VERS(file, &tape);
9286 putFileChunkBE(file, "HEAD", TAPE_CHUNK_HEAD_SIZE);
9287 SaveTape_HEAD(file, &tape);
9289 if (checkSaveTape_SCRN(&tape))
9291 putFileChunkBE(file, "SCRN", TAPE_CHUNK_SCRN_SIZE);
9292 SaveTape_SCRN(file, &tape);
9295 putFileChunkBE(file, "INFO", info_chunk_size);
9296 SaveTape_INFO(file, &tape);
9298 putFileChunkBE(file, "BODY", body_chunk_size);
9299 SaveTape_BODY(file, &tape);
9303 SetFilePermissions(filename, PERMS_PRIVATE);
9306 static void SaveTapeExt(char *filename)
9310 tape.file_version = FILE_VERSION_ACTUAL;
9311 tape.game_version = GAME_VERSION_ACTUAL;
9313 tape.num_participating_players = 0;
9315 // count number of participating players
9316 for (i = 0; i < MAX_PLAYERS; i++)
9317 if (tape.player_participates[i])
9318 tape.num_participating_players++;
9320 SaveTapeToFilename(filename);
9322 tape.changed = FALSE;
9325 void SaveTape(int nr)
9327 char *filename = getTapeFilename(nr);
9329 InitTapeDirectory(leveldir_current->subdir);
9331 SaveTapeExt(filename);
9334 void SaveScoreTape(int nr)
9336 char *filename = getScoreTapeFilename(tape.score_tape_basename, nr);
9338 // used instead of "leveldir_current->subdir" (for network games)
9339 InitScoreTapeDirectory(levelset.identifier, nr);
9341 SaveTapeExt(filename);
9344 static boolean SaveTapeCheckedExt(int nr, char *msg_replace, char *msg_saved,
9345 unsigned int req_state_added)
9347 char *filename = getTapeFilename(nr);
9348 boolean new_tape = !fileExists(filename);
9349 boolean tape_saved = FALSE;
9351 if (new_tape || Request(msg_replace, REQ_ASK | req_state_added))
9356 Request(msg_saved, REQ_CONFIRM | req_state_added);
9364 boolean SaveTapeChecked(int nr)
9366 return SaveTapeCheckedExt(nr, "Replace old tape?", "Tape saved!", 0);
9369 boolean SaveTapeChecked_LevelSolved(int nr)
9371 return SaveTapeCheckedExt(nr, "Level solved! Replace old tape?",
9372 "Level solved! Tape saved!", REQ_STAY_OPEN);
9375 void DumpTape(struct TapeInfo *tape)
9377 int tape_frame_counter;
9380 if (tape->no_valid_file)
9382 Warn("cannot dump -- no valid tape file found");
9389 Print("Tape of Level %03d (file version %08d, game version %08d)\n",
9390 tape->level_nr, tape->file_version, tape->game_version);
9391 Print(" (effective engine version %08d)\n",
9392 tape->engine_version);
9393 Print("Level series identifier: '%s'\n", tape->level_identifier);
9395 Print("Solution tape: %s\n",
9396 tape->solved ? "yes" :
9397 tape->game_version < VERSION_IDENT(4,3,2,3) ? "unknown" : "no");
9399 Print("Special tape properties: ");
9400 if (tape->property_bits == TAPE_PROPERTY_NONE)
9402 if (tape->property_bits & TAPE_PROPERTY_EM_RANDOM_BUG)
9403 Print("[em_random_bug]");
9404 if (tape->property_bits & TAPE_PROPERTY_GAME_SPEED)
9405 Print("[game_speed]");
9406 if (tape->property_bits & TAPE_PROPERTY_PAUSE_MODE)
9408 if (tape->property_bits & TAPE_PROPERTY_SINGLE_STEP)
9409 Print("[single_step]");
9410 if (tape->property_bits & TAPE_PROPERTY_SNAPSHOT)
9411 Print("[snapshot]");
9412 if (tape->property_bits & TAPE_PROPERTY_REPLAYED)
9413 Print("[replayed]");
9414 if (tape->property_bits & TAPE_PROPERTY_TAS_KEYS)
9415 Print("[tas_keys]");
9416 if (tape->property_bits & TAPE_PROPERTY_SMALL_GRAPHICS)
9417 Print("[small_graphics]");
9420 int year2 = tape->date / 10000;
9421 int year4 = (year2 < 70 ? 2000 + year2 : 1900 + year2);
9422 int month_index_raw = (tape->date / 100) % 100;
9423 int month_index = month_index_raw % 12; // prevent invalid index
9424 int month = month_index + 1;
9425 int day = tape->date % 100;
9427 Print("Tape date: %04d-%02d-%02d\n", year4, month, day);
9431 tape_frame_counter = 0;
9433 for (i = 0; i < tape->length; i++)
9435 if (i >= MAX_TAPE_LEN)
9440 for (j = 0; j < MAX_PLAYERS; j++)
9442 if (tape->player_participates[j])
9444 int action = tape->pos[i].action[j];
9446 Print("%d:%02x ", j, action);
9447 Print("[%c%c%c%c|%c%c] - ",
9448 (action & JOY_LEFT ? '<' : ' '),
9449 (action & JOY_RIGHT ? '>' : ' '),
9450 (action & JOY_UP ? '^' : ' '),
9451 (action & JOY_DOWN ? 'v' : ' '),
9452 (action & JOY_BUTTON_1 ? '1' : ' '),
9453 (action & JOY_BUTTON_2 ? '2' : ' '));
9457 Print("(%03d) ", tape->pos[i].delay);
9458 Print("[%05d]\n", tape_frame_counter);
9460 tape_frame_counter += tape->pos[i].delay;
9466 void DumpTapes(void)
9468 static LevelDirTree *dumptape_leveldir = NULL;
9470 dumptape_leveldir = getTreeInfoFromIdentifier(leveldir_first,
9471 global.dumptape_leveldir);
9473 if (dumptape_leveldir == NULL)
9474 Fail("no such level identifier: '%s'", global.dumptape_leveldir);
9476 if (global.dumptape_level_nr < dumptape_leveldir->first_level ||
9477 global.dumptape_level_nr > dumptape_leveldir->last_level)
9478 Fail("no such level number: %d", global.dumptape_level_nr);
9480 leveldir_current = dumptape_leveldir;
9482 if (options.mytapes)
9483 LoadTape(global.dumptape_level_nr);
9485 LoadSolutionTape(global.dumptape_level_nr);
9493 // ============================================================================
9494 // score file functions
9495 // ============================================================================
9497 static void setScoreInfoToDefaultsExt(struct ScoreInfo *scores)
9501 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9503 strcpy(scores->entry[i].tape_basename, UNDEFINED_FILENAME);
9504 strcpy(scores->entry[i].name, EMPTY_PLAYER_NAME);
9505 scores->entry[i].score = 0;
9506 scores->entry[i].time = 0;
9508 scores->entry[i].id = -1;
9509 strcpy(scores->entry[i].tape_date, UNKNOWN_NAME);
9510 strcpy(scores->entry[i].platform, UNKNOWN_NAME);
9511 strcpy(scores->entry[i].version, UNKNOWN_NAME);
9512 strcpy(scores->entry[i].country_name, UNKNOWN_NAME);
9513 strcpy(scores->entry[i].country_code, "??");
9516 scores->num_entries = 0;
9517 scores->last_added = -1;
9518 scores->last_added_local = -1;
9520 scores->updated = FALSE;
9521 scores->uploaded = FALSE;
9522 scores->tape_downloaded = FALSE;
9523 scores->force_last_added = FALSE;
9525 // The following values are intentionally not reset here:
9529 // - continue_playing
9530 // - continue_on_return
9533 static void setScoreInfoToDefaults(void)
9535 setScoreInfoToDefaultsExt(&scores);
9538 static void setServerScoreInfoToDefaults(void)
9540 setScoreInfoToDefaultsExt(&server_scores);
9543 static void LoadScore_OLD(int nr)
9546 char *filename = getScoreFilename(nr);
9547 char cookie[MAX_LINE_LEN];
9548 char line[MAX_LINE_LEN];
9552 if (!(file = fopen(filename, MODE_READ)))
9555 // check file identifier
9556 if (fgets(cookie, MAX_LINE_LEN, file) == NULL)
9558 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
9559 cookie[strlen(cookie) - 1] = '\0';
9561 if (!checkCookieString(cookie, SCORE_COOKIE_TMPL))
9563 Warn("unknown format of score file '%s'", filename);
9570 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9572 if (fscanf(file, "%d", &scores.entry[i].score) == EOF)
9573 Warn("fscanf() failed; %s", strerror(errno));
9575 if (fgets(line, MAX_LINE_LEN, file) == NULL)
9578 if (strlen(line) > 0 && line[strlen(line) - 1] == '\n')
9579 line[strlen(line) - 1] = '\0';
9581 for (line_ptr = line; *line_ptr; line_ptr++)
9583 if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != '\0')
9585 strncpy(scores.entry[i].name, line_ptr, MAX_PLAYER_NAME_LEN);
9586 scores.entry[i].name[MAX_PLAYER_NAME_LEN] = '\0';
9595 static void ConvertScore_OLD(void)
9597 // only convert score to time for levels that rate playing time over score
9598 if (!level.rate_time_over_score)
9601 // convert old score to playing time for score-less levels (like Supaplex)
9602 int time_final_max = 999;
9605 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9607 int score = scores.entry[i].score;
9609 if (score > 0 && score < time_final_max)
9610 scores.entry[i].time = (time_final_max - score - 1) * FRAMES_PER_SECOND;
9614 static int LoadScore_VERS(File *file, int chunk_size, struct ScoreInfo *scores)
9616 scores->file_version = getFileVersion(file);
9617 scores->game_version = getFileVersion(file);
9622 static int LoadScore_INFO(File *file, int chunk_size, struct ScoreInfo *scores)
9624 char *level_identifier = NULL;
9625 int level_identifier_size;
9628 level_identifier_size = getFile16BitBE(file);
9630 level_identifier = checked_malloc(level_identifier_size);
9632 for (i = 0; i < level_identifier_size; i++)
9633 level_identifier[i] = getFile8Bit(file);
9635 strncpy(scores->level_identifier, level_identifier, MAX_FILENAME_LEN);
9636 scores->level_identifier[MAX_FILENAME_LEN] = '\0';
9638 checked_free(level_identifier);
9640 scores->level_nr = getFile16BitBE(file);
9641 scores->num_entries = getFile16BitBE(file);
9643 chunk_size = 2 + level_identifier_size + 2 + 2;
9648 static int LoadScore_NAME(File *file, int chunk_size, struct ScoreInfo *scores)
9652 for (i = 0; i < scores->num_entries; i++)
9654 for (j = 0; j < MAX_PLAYER_NAME_LEN; j++)
9655 scores->entry[i].name[j] = getFile8Bit(file);
9657 scores->entry[i].name[MAX_PLAYER_NAME_LEN] = '\0';
9660 chunk_size = scores->num_entries * MAX_PLAYER_NAME_LEN;
9665 static int LoadScore_SCOR(File *file, int chunk_size, struct ScoreInfo *scores)
9669 for (i = 0; i < scores->num_entries; i++)
9670 scores->entry[i].score = getFile16BitBE(file);
9672 chunk_size = scores->num_entries * 2;
9677 static int LoadScore_SC4R(File *file, int chunk_size, struct ScoreInfo *scores)
9681 for (i = 0; i < scores->num_entries; i++)
9682 scores->entry[i].score = getFile32BitBE(file);
9684 chunk_size = scores->num_entries * 4;
9689 static int LoadScore_TIME(File *file, int chunk_size, struct ScoreInfo *scores)
9693 for (i = 0; i < scores->num_entries; i++)
9694 scores->entry[i].time = getFile32BitBE(file);
9696 chunk_size = scores->num_entries * 4;
9701 static int LoadScore_TAPE(File *file, int chunk_size, struct ScoreInfo *scores)
9705 for (i = 0; i < scores->num_entries; i++)
9707 for (j = 0; j < MAX_SCORE_TAPE_BASENAME_LEN; j++)
9708 scores->entry[i].tape_basename[j] = getFile8Bit(file);
9710 scores->entry[i].tape_basename[MAX_SCORE_TAPE_BASENAME_LEN] = '\0';
9713 chunk_size = scores->num_entries * MAX_SCORE_TAPE_BASENAME_LEN;
9718 void LoadScore(int nr)
9720 char *filename = getScoreFilename(nr);
9721 char cookie[MAX_LINE_LEN];
9722 char chunk_name[CHUNK_ID_LEN + 1];
9724 boolean old_score_file_format = FALSE;
9727 // always start with reliable default values
9728 setScoreInfoToDefaults();
9730 if (!(file = openFile(filename, MODE_READ)))
9733 getFileChunkBE(file, chunk_name, NULL);
9734 if (strEqual(chunk_name, "RND1"))
9736 getFile32BitBE(file); // not used
9738 getFileChunkBE(file, chunk_name, NULL);
9739 if (!strEqual(chunk_name, "SCOR"))
9741 Warn("unknown format of score file '%s'", filename);
9748 else // check for old file format with cookie string
9750 strcpy(cookie, chunk_name);
9751 if (getStringFromFile(file, &cookie[4], MAX_LINE_LEN - 4) == NULL)
9753 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
9754 cookie[strlen(cookie) - 1] = '\0';
9756 if (!checkCookieString(cookie, SCORE_COOKIE_TMPL))
9758 Warn("unknown format of score file '%s'", filename);
9765 old_score_file_format = TRUE;
9768 if (old_score_file_format)
9770 // score files from versions before 4.2.4.0 without chunk structure
9773 // convert score to time, if possible (mainly for Supaplex levels)
9782 int (*loader)(File *, int, struct ScoreInfo *);
9786 { "VERS", SCORE_CHUNK_VERS_SIZE, LoadScore_VERS },
9787 { "INFO", -1, LoadScore_INFO },
9788 { "NAME", -1, LoadScore_NAME },
9789 { "SCOR", -1, LoadScore_SCOR },
9790 { "SC4R", -1, LoadScore_SC4R },
9791 { "TIME", -1, LoadScore_TIME },
9792 { "TAPE", -1, LoadScore_TAPE },
9797 while (getFileChunkBE(file, chunk_name, &chunk_size))
9801 while (chunk_info[i].name != NULL &&
9802 !strEqual(chunk_name, chunk_info[i].name))
9805 if (chunk_info[i].name == NULL)
9807 Warn("unknown chunk '%s' in score file '%s'",
9808 chunk_name, filename);
9810 ReadUnusedBytesFromFile(file, chunk_size);
9812 else if (chunk_info[i].size != -1 &&
9813 chunk_info[i].size != chunk_size)
9815 Warn("wrong size (%d) of chunk '%s' in score file '%s'",
9816 chunk_size, chunk_name, filename);
9818 ReadUnusedBytesFromFile(file, chunk_size);
9822 // call function to load this score chunk
9823 int chunk_size_expected =
9824 (chunk_info[i].loader)(file, chunk_size, &scores);
9826 // the size of some chunks cannot be checked before reading other
9827 // chunks first (like "HEAD" and "BODY") that contain some header
9828 // information, so check them here
9829 if (chunk_size_expected != chunk_size)
9831 Warn("wrong size (%d) of chunk '%s' in score file '%s'",
9832 chunk_size, chunk_name, filename);
9841 #if ENABLE_HISTORIC_CHUNKS
9842 void SaveScore_OLD(int nr)
9845 char *filename = getScoreFilename(nr);
9848 // used instead of "leveldir_current->subdir" (for network games)
9849 InitScoreDirectory(levelset.identifier);
9851 if (!(file = fopen(filename, MODE_WRITE)))
9853 Warn("cannot save score for level %d", nr);
9858 fprintf(file, "%s\n\n", SCORE_COOKIE);
9860 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9861 fprintf(file, "%d %s\n", scores.entry[i].score, scores.entry[i].name);
9865 SetFilePermissions(filename, PERMS_PRIVATE);
9869 static void SaveScore_VERS(FILE *file, struct ScoreInfo *scores)
9871 putFileVersion(file, scores->file_version);
9872 putFileVersion(file, scores->game_version);
9875 static void SaveScore_INFO(FILE *file, struct ScoreInfo *scores)
9877 int level_identifier_size = strlen(scores->level_identifier) + 1;
9880 putFile16BitBE(file, level_identifier_size);
9882 for (i = 0; i < level_identifier_size; i++)
9883 putFile8Bit(file, scores->level_identifier[i]);
9885 putFile16BitBE(file, scores->level_nr);
9886 putFile16BitBE(file, scores->num_entries);
9889 static void SaveScore_NAME(FILE *file, struct ScoreInfo *scores)
9893 for (i = 0; i < scores->num_entries; i++)
9895 int name_size = strlen(scores->entry[i].name);
9897 for (j = 0; j < MAX_PLAYER_NAME_LEN; j++)
9898 putFile8Bit(file, (j < name_size ? scores->entry[i].name[j] : 0));
9902 static void SaveScore_SCOR(FILE *file, struct ScoreInfo *scores)
9906 for (i = 0; i < scores->num_entries; i++)
9907 putFile16BitBE(file, scores->entry[i].score);
9910 static void SaveScore_SC4R(FILE *file, struct ScoreInfo *scores)
9914 for (i = 0; i < scores->num_entries; i++)
9915 putFile32BitBE(file, scores->entry[i].score);
9918 static void SaveScore_TIME(FILE *file, struct ScoreInfo *scores)
9922 for (i = 0; i < scores->num_entries; i++)
9923 putFile32BitBE(file, scores->entry[i].time);
9926 static void SaveScore_TAPE(FILE *file, struct ScoreInfo *scores)
9930 for (i = 0; i < scores->num_entries; i++)
9932 int size = strlen(scores->entry[i].tape_basename);
9934 for (j = 0; j < MAX_SCORE_TAPE_BASENAME_LEN; j++)
9935 putFile8Bit(file, (j < size ? scores->entry[i].tape_basename[j] : 0));
9939 static void SaveScoreToFilename(char *filename)
9942 int info_chunk_size;
9943 int name_chunk_size;
9944 int scor_chunk_size;
9945 int sc4r_chunk_size;
9946 int time_chunk_size;
9947 int tape_chunk_size;
9948 boolean has_large_score_values;
9951 if (!(file = fopen(filename, MODE_WRITE)))
9953 Warn("cannot save score file '%s'", filename);
9958 info_chunk_size = 2 + (strlen(scores.level_identifier) + 1) + 2 + 2;
9959 name_chunk_size = scores.num_entries * MAX_PLAYER_NAME_LEN;
9960 scor_chunk_size = scores.num_entries * 2;
9961 sc4r_chunk_size = scores.num_entries * 4;
9962 time_chunk_size = scores.num_entries * 4;
9963 tape_chunk_size = scores.num_entries * MAX_SCORE_TAPE_BASENAME_LEN;
9965 has_large_score_values = FALSE;
9966 for (i = 0; i < scores.num_entries; i++)
9967 if (scores.entry[i].score > 0xffff)
9968 has_large_score_values = TRUE;
9970 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
9971 putFileChunkBE(file, "SCOR", CHUNK_SIZE_NONE);
9973 putFileChunkBE(file, "VERS", SCORE_CHUNK_VERS_SIZE);
9974 SaveScore_VERS(file, &scores);
9976 putFileChunkBE(file, "INFO", info_chunk_size);
9977 SaveScore_INFO(file, &scores);
9979 putFileChunkBE(file, "NAME", name_chunk_size);
9980 SaveScore_NAME(file, &scores);
9982 if (has_large_score_values)
9984 putFileChunkBE(file, "SC4R", sc4r_chunk_size);
9985 SaveScore_SC4R(file, &scores);
9989 putFileChunkBE(file, "SCOR", scor_chunk_size);
9990 SaveScore_SCOR(file, &scores);
9993 putFileChunkBE(file, "TIME", time_chunk_size);
9994 SaveScore_TIME(file, &scores);
9996 putFileChunkBE(file, "TAPE", tape_chunk_size);
9997 SaveScore_TAPE(file, &scores);
10001 SetFilePermissions(filename, PERMS_PRIVATE);
10004 void SaveScore(int nr)
10006 char *filename = getScoreFilename(nr);
10009 // used instead of "leveldir_current->subdir" (for network games)
10010 InitScoreDirectory(levelset.identifier);
10012 scores.file_version = FILE_VERSION_ACTUAL;
10013 scores.game_version = GAME_VERSION_ACTUAL;
10015 strncpy(scores.level_identifier, levelset.identifier, MAX_FILENAME_LEN);
10016 scores.level_identifier[MAX_FILENAME_LEN] = '\0';
10017 scores.level_nr = level_nr;
10019 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
10020 if (scores.entry[i].score == 0 &&
10021 scores.entry[i].time == 0 &&
10022 strEqual(scores.entry[i].name, EMPTY_PLAYER_NAME))
10025 scores.num_entries = i;
10027 if (scores.num_entries == 0)
10030 SaveScoreToFilename(filename);
10033 static void LoadServerScoreFromCache(int nr)
10035 struct ScoreEntry score_entry;
10044 { &score_entry.score, FALSE, 0 },
10045 { &score_entry.time, FALSE, 0 },
10046 { score_entry.name, TRUE, MAX_PLAYER_NAME_LEN },
10047 { score_entry.tape_basename, TRUE, MAX_FILENAME_LEN },
10048 { score_entry.tape_date, TRUE, MAX_ISO_DATE_LEN },
10049 { &score_entry.id, FALSE, 0 },
10050 { score_entry.platform, TRUE, MAX_PLATFORM_TEXT_LEN },
10051 { score_entry.version, TRUE, MAX_VERSION_TEXT_LEN },
10052 { score_entry.country_code, TRUE, MAX_COUNTRY_CODE_LEN },
10053 { score_entry.country_name, TRUE, MAX_COUNTRY_NAME_LEN },
10057 char *filename = getScoreCacheFilename(nr);
10058 SetupFileHash *score_hash = loadSetupFileHash(filename);
10061 server_scores.num_entries = 0;
10063 if (score_hash == NULL)
10066 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
10068 score_entry = server_scores.entry[i];
10070 for (j = 0; score_mapping[j].value != NULL; j++)
10074 sprintf(token, "%02d.%d", i, j);
10076 char *value = getHashEntry(score_hash, token);
10081 if (score_mapping[j].is_string)
10083 char *score_value = (char *)score_mapping[j].value;
10084 int value_size = score_mapping[j].string_size;
10086 strncpy(score_value, value, value_size);
10087 score_value[value_size] = '\0';
10091 int *score_value = (int *)score_mapping[j].value;
10093 *score_value = atoi(value);
10096 server_scores.num_entries = i + 1;
10099 server_scores.entry[i] = score_entry;
10102 freeSetupFileHash(score_hash);
10105 void LoadServerScore(int nr, boolean download_score)
10107 if (!setup.use_api_server)
10110 // always start with reliable default values
10111 setServerScoreInfoToDefaults();
10113 // 1st step: load server scores from cache file (which may not exist)
10114 // (this should prevent reading it while the thread is writing to it)
10115 LoadServerScoreFromCache(nr);
10117 if (download_score && runtime.use_api_server)
10119 // 2nd step: download server scores from score server to cache file
10120 // (as thread, as it might time out if the server is not reachable)
10121 ApiGetScoreAsThread(nr);
10125 void PrepareScoreTapesForUpload(char *leveldir_subdir)
10127 MarkTapeDirectoryUploadsAsIncomplete(leveldir_subdir);
10129 // if score tape not uploaded, ask for uploading missing tapes later
10130 if (!setup.has_remaining_tapes)
10131 setup.ask_for_remaining_tapes = TRUE;
10133 setup.provide_uploading_tapes = TRUE;
10134 setup.has_remaining_tapes = TRUE;
10136 SaveSetup_ServerSetup();
10139 void SaveServerScore(int nr, boolean tape_saved)
10141 if (!runtime.use_api_server)
10143 PrepareScoreTapesForUpload(leveldir_current->subdir);
10148 ApiAddScoreAsThread(nr, tape_saved, NULL);
10151 void SaveServerScoreFromFile(int nr, boolean tape_saved,
10152 char *score_tape_filename)
10154 if (!runtime.use_api_server)
10157 ApiAddScoreAsThread(nr, tape_saved, score_tape_filename);
10160 void LoadLocalAndServerScore(int nr, boolean download_score)
10162 int last_added_local = scores.last_added_local;
10163 boolean force_last_added = scores.force_last_added;
10165 // needed if only showing server scores
10166 setScoreInfoToDefaults();
10168 if (!strEqual(setup.scores_in_highscore_list, STR_SCORES_TYPE_SERVER_ONLY))
10171 // restore last added local score entry (before merging server scores)
10172 scores.last_added = scores.last_added_local = last_added_local;
10174 if (setup.use_api_server &&
10175 !strEqual(setup.scores_in_highscore_list, STR_SCORES_TYPE_LOCAL_ONLY))
10177 // load server scores from cache file and trigger update from server
10178 LoadServerScore(nr, download_score);
10180 // merge local scores with scores from server
10181 MergeServerScore();
10184 if (force_last_added)
10185 scores.force_last_added = force_last_added;
10189 // ============================================================================
10190 // setup file functions
10191 // ============================================================================
10193 #define TOKEN_STR_PLAYER_PREFIX "player_"
10196 static struct TokenInfo global_setup_tokens[] =
10200 &setup.player_name, "player_name"
10204 &setup.multiple_users, "multiple_users"
10208 &setup.sound, "sound"
10212 &setup.sound_loops, "repeating_sound_loops"
10216 &setup.sound_music, "background_music"
10220 &setup.sound_simple, "simple_sound_effects"
10224 &setup.toons, "toons"
10228 &setup.global_animations, "global_animations"
10232 &setup.scroll_delay, "scroll_delay"
10236 &setup.forced_scroll_delay, "forced_scroll_delay"
10240 &setup.scroll_delay_value, "scroll_delay_value"
10244 &setup.engine_snapshot_mode, "engine_snapshot_mode"
10248 &setup.engine_snapshot_memory, "engine_snapshot_memory"
10252 &setup.fade_screens, "fade_screens"
10256 &setup.autorecord, "automatic_tape_recording"
10260 &setup.autorecord_after_replay, "autorecord_after_replay"
10264 &setup.auto_pause_on_start, "auto_pause_on_start"
10268 &setup.show_titlescreen, "show_titlescreen"
10272 &setup.quick_doors, "quick_doors"
10276 &setup.team_mode, "team_mode"
10280 &setup.handicap, "handicap"
10284 &setup.skip_levels, "skip_levels"
10288 &setup.increment_levels, "increment_levels"
10292 &setup.auto_play_next_level, "auto_play_next_level"
10296 &setup.count_score_after_game, "count_score_after_game"
10300 &setup.show_scores_after_game, "show_scores_after_game"
10304 &setup.time_limit, "time_limit"
10308 &setup.fullscreen, "fullscreen"
10312 &setup.window_scaling_percent, "window_scaling_percent"
10316 &setup.window_scaling_quality, "window_scaling_quality"
10320 &setup.screen_rendering_mode, "screen_rendering_mode"
10324 &setup.vsync_mode, "vsync_mode"
10328 &setup.ask_on_escape, "ask_on_escape"
10332 &setup.ask_on_escape_editor, "ask_on_escape_editor"
10336 &setup.ask_on_game_over, "ask_on_game_over"
10340 &setup.ask_on_quit_game, "ask_on_quit_game"
10344 &setup.ask_on_quit_program, "ask_on_quit_program"
10348 &setup.quick_switch, "quick_player_switch"
10352 &setup.input_on_focus, "input_on_focus"
10356 &setup.prefer_aga_graphics, "prefer_aga_graphics"
10360 &setup.prefer_lowpass_sounds, "prefer_lowpass_sounds"
10364 &setup.prefer_extra_panel_items, "prefer_extra_panel_items"
10368 &setup.game_speed_extended, "game_speed_extended"
10372 &setup.game_frame_delay, "game_frame_delay"
10376 &setup.bd_skip_uncovering, "bd_skip_uncovering"
10380 &setup.bd_skip_hatching, "bd_skip_hatching"
10384 &setup.bd_scroll_delay, "bd_scroll_delay"
10388 &setup.bd_smooth_movements, "bd_smooth_movements"
10392 &setup.sp_show_border_elements, "sp_show_border_elements"
10396 &setup.small_game_graphics, "small_game_graphics"
10400 &setup.show_load_save_buttons, "show_load_save_buttons"
10404 &setup.show_undo_redo_buttons, "show_undo_redo_buttons"
10408 &setup.scores_in_highscore_list, "scores_in_highscore_list"
10412 &setup.graphics_set, "graphics_set"
10416 &setup.sounds_set, "sounds_set"
10420 &setup.music_set, "music_set"
10424 &setup.override_level_graphics, "override_level_graphics"
10428 &setup.override_level_sounds, "override_level_sounds"
10432 &setup.override_level_music, "override_level_music"
10436 &setup.volume_simple, "volume_simple"
10440 &setup.volume_loops, "volume_loops"
10444 &setup.volume_music, "volume_music"
10448 &setup.network_mode, "network_mode"
10452 &setup.network_player_nr, "network_player"
10456 &setup.network_server_hostname, "network_server_hostname"
10460 &setup.touch.control_type, "touch.control_type"
10464 &setup.touch.move_distance, "touch.move_distance"
10468 &setup.touch.drop_distance, "touch.drop_distance"
10472 &setup.touch.transparency, "touch.transparency"
10476 &setup.touch.draw_outlined, "touch.draw_outlined"
10480 &setup.touch.draw_pressed, "touch.draw_pressed"
10484 &setup.touch.grid_xsize[0], "touch.virtual_buttons.0.xsize"
10488 &setup.touch.grid_ysize[0], "touch.virtual_buttons.0.ysize"
10492 &setup.touch.grid_xsize[1], "touch.virtual_buttons.1.xsize"
10496 &setup.touch.grid_ysize[1], "touch.virtual_buttons.1.ysize"
10500 &setup.touch.overlay_buttons, "touch.overlay_buttons"
10504 static struct TokenInfo auto_setup_tokens[] =
10508 &setup.auto_setup.editor_zoom_tilesize, "editor.zoom_tilesize"
10512 static struct TokenInfo server_setup_tokens[] =
10516 &setup.player_uuid, "player_uuid"
10520 &setup.player_version, "player_version"
10524 &setup.use_api_server, TEST_PREFIX "use_api_server"
10528 &setup.api_server_hostname, TEST_PREFIX "api_server_hostname"
10532 &setup.api_server_password, TEST_PREFIX "api_server_password"
10536 &setup.ask_for_uploading_tapes, TEST_PREFIX "ask_for_uploading_tapes"
10540 &setup.ask_for_remaining_tapes, TEST_PREFIX "ask_for_remaining_tapes"
10544 &setup.provide_uploading_tapes, TEST_PREFIX "provide_uploading_tapes"
10548 &setup.ask_for_using_api_server,TEST_PREFIX "ask_for_using_api_server"
10552 &setup.has_remaining_tapes, TEST_PREFIX "has_remaining_tapes"
10556 static struct TokenInfo editor_setup_tokens[] =
10560 &setup.editor.el_classic, "editor.el_classic"
10564 &setup.editor.el_custom, "editor.el_custom"
10568 &setup.editor.el_user_defined, "editor.el_user_defined"
10572 &setup.editor.el_dynamic, "editor.el_dynamic"
10576 &setup.editor.el_headlines, "editor.el_headlines"
10580 &setup.editor.show_element_token, "editor.show_element_token"
10584 &setup.editor.show_read_only_warning, "editor.show_read_only_warning"
10588 static struct TokenInfo editor_cascade_setup_tokens[] =
10592 &setup.editor_cascade.el_bd, "editor.cascade.el_bd"
10596 &setup.editor_cascade.el_bd_native, "editor.cascade.el_bd_native"
10600 &setup.editor_cascade.el_bd_effects, "editor.cascade.el_bd_effects"
10604 &setup.editor_cascade.el_em, "editor.cascade.el_em"
10608 &setup.editor_cascade.el_emc, "editor.cascade.el_emc"
10612 &setup.editor_cascade.el_rnd, "editor.cascade.el_rnd"
10616 &setup.editor_cascade.el_sb, "editor.cascade.el_sb"
10620 &setup.editor_cascade.el_sp, "editor.cascade.el_sp"
10624 &setup.editor_cascade.el_dc, "editor.cascade.el_dc"
10628 &setup.editor_cascade.el_dx, "editor.cascade.el_dx"
10632 &setup.editor_cascade.el_mm, "editor.cascade.el_mm"
10636 &setup.editor_cascade.el_df, "editor.cascade.el_df"
10640 &setup.editor_cascade.el_chars, "editor.cascade.el_chars"
10644 &setup.editor_cascade.el_steel_chars, "editor.cascade.el_steel_chars"
10648 &setup.editor_cascade.el_ce, "editor.cascade.el_ce"
10652 &setup.editor_cascade.el_ge, "editor.cascade.el_ge"
10656 &setup.editor_cascade.el_es, "editor.cascade.el_es"
10660 &setup.editor_cascade.el_ref, "editor.cascade.el_ref"
10664 &setup.editor_cascade.el_user, "editor.cascade.el_user"
10668 &setup.editor_cascade.el_dynamic, "editor.cascade.el_dynamic"
10672 static struct TokenInfo shortcut_setup_tokens[] =
10676 &setup.shortcut.save_game, "shortcut.save_game"
10680 &setup.shortcut.load_game, "shortcut.load_game"
10684 &setup.shortcut.restart_game, "shortcut.restart_game"
10688 &setup.shortcut.pause_before_end, "shortcut.pause_before_end"
10692 &setup.shortcut.toggle_pause, "shortcut.toggle_pause"
10696 &setup.shortcut.focus_player[0], "shortcut.focus_player_1"
10700 &setup.shortcut.focus_player[1], "shortcut.focus_player_2"
10704 &setup.shortcut.focus_player[2], "shortcut.focus_player_3"
10708 &setup.shortcut.focus_player[3], "shortcut.focus_player_4"
10712 &setup.shortcut.focus_player_all, "shortcut.focus_player_all"
10716 &setup.shortcut.tape_eject, "shortcut.tape_eject"
10720 &setup.shortcut.tape_extra, "shortcut.tape_extra"
10724 &setup.shortcut.tape_stop, "shortcut.tape_stop"
10728 &setup.shortcut.tape_pause, "shortcut.tape_pause"
10732 &setup.shortcut.tape_record, "shortcut.tape_record"
10736 &setup.shortcut.tape_play, "shortcut.tape_play"
10740 &setup.shortcut.sound_simple, "shortcut.sound_simple"
10744 &setup.shortcut.sound_loops, "shortcut.sound_loops"
10748 &setup.shortcut.sound_music, "shortcut.sound_music"
10752 &setup.shortcut.snap_left, "shortcut.snap_left"
10756 &setup.shortcut.snap_right, "shortcut.snap_right"
10760 &setup.shortcut.snap_up, "shortcut.snap_up"
10764 &setup.shortcut.snap_down, "shortcut.snap_down"
10768 static struct SetupInputInfo setup_input;
10769 static struct TokenInfo player_setup_tokens[] =
10773 &setup_input.use_joystick, ".use_joystick"
10777 &setup_input.joy.device_name, ".joy.device_name"
10781 &setup_input.joy.xleft, ".joy.xleft"
10785 &setup_input.joy.xmiddle, ".joy.xmiddle"
10789 &setup_input.joy.xright, ".joy.xright"
10793 &setup_input.joy.yupper, ".joy.yupper"
10797 &setup_input.joy.ymiddle, ".joy.ymiddle"
10801 &setup_input.joy.ylower, ".joy.ylower"
10805 &setup_input.joy.snap, ".joy.snap_field"
10809 &setup_input.joy.drop, ".joy.place_bomb"
10813 &setup_input.key.left, ".key.move_left"
10817 &setup_input.key.right, ".key.move_right"
10821 &setup_input.key.up, ".key.move_up"
10825 &setup_input.key.down, ".key.move_down"
10829 &setup_input.key.snap, ".key.snap_field"
10833 &setup_input.key.drop, ".key.place_bomb"
10837 static struct TokenInfo system_setup_tokens[] =
10841 &setup.system.sdl_renderdriver, "system.sdl_renderdriver"
10845 &setup.system.sdl_videodriver, "system.sdl_videodriver"
10849 &setup.system.sdl_audiodriver, "system.sdl_audiodriver"
10853 &setup.system.audio_fragment_size, "system.audio_fragment_size"
10857 static struct TokenInfo internal_setup_tokens[] =
10861 &setup.internal.program_title, "program_title"
10865 &setup.internal.program_version, "program_version"
10869 &setup.internal.program_author, "program_author"
10873 &setup.internal.program_email, "program_email"
10877 &setup.internal.program_website, "program_website"
10881 &setup.internal.program_copyright, "program_copyright"
10885 &setup.internal.program_company, "program_company"
10889 &setup.internal.program_icon_file, "program_icon_file"
10893 &setup.internal.default_graphics_set, "default_graphics_set"
10897 &setup.internal.default_sounds_set, "default_sounds_set"
10901 &setup.internal.default_music_set, "default_music_set"
10905 &setup.internal.fallback_graphics_file, "fallback_graphics_file"
10909 &setup.internal.fallback_sounds_file, "fallback_sounds_file"
10913 &setup.internal.fallback_music_file, "fallback_music_file"
10917 &setup.internal.default_level_series, "default_level_series"
10921 &setup.internal.default_window_width, "default_window_width"
10925 &setup.internal.default_window_height, "default_window_height"
10929 &setup.internal.choose_from_top_leveldir, "choose_from_top_leveldir"
10933 &setup.internal.show_scaling_in_title, "show_scaling_in_title"
10937 &setup.internal.create_user_levelset, "create_user_levelset"
10941 &setup.internal.info_screens_from_main, "info_screens_from_main"
10945 &setup.internal.menu_game, "menu_game"
10949 &setup.internal.menu_engines, "menu_engines"
10953 &setup.internal.menu_editor, "menu_editor"
10957 &setup.internal.menu_graphics, "menu_graphics"
10961 &setup.internal.menu_sound, "menu_sound"
10965 &setup.internal.menu_artwork, "menu_artwork"
10969 &setup.internal.menu_input, "menu_input"
10973 &setup.internal.menu_touch, "menu_touch"
10977 &setup.internal.menu_shortcuts, "menu_shortcuts"
10981 &setup.internal.menu_exit, "menu_exit"
10985 &setup.internal.menu_save_and_exit, "menu_save_and_exit"
10989 &setup.internal.menu_shortcuts_various, "menu_shortcuts_various"
10993 &setup.internal.menu_shortcuts_focus, "menu_shortcuts_focus"
10997 &setup.internal.menu_shortcuts_tape, "menu_shortcuts_tape"
11001 &setup.internal.menu_shortcuts_sound, "menu_shortcuts_sound"
11005 &setup.internal.menu_shortcuts_snap, "menu_shortcuts_snap"
11009 &setup.internal.info_title, "info_title"
11013 &setup.internal.info_elements, "info_elements"
11017 &setup.internal.info_music, "info_music"
11021 &setup.internal.info_credits, "info_credits"
11025 &setup.internal.info_program, "info_program"
11029 &setup.internal.info_version, "info_version"
11033 &setup.internal.info_levelset, "info_levelset"
11037 &setup.internal.info_exit, "info_exit"
11041 static struct TokenInfo debug_setup_tokens[] =
11045 &setup.debug.frame_delay[0], "debug.frame_delay_0"
11049 &setup.debug.frame_delay[1], "debug.frame_delay_1"
11053 &setup.debug.frame_delay[2], "debug.frame_delay_2"
11057 &setup.debug.frame_delay[3], "debug.frame_delay_3"
11061 &setup.debug.frame_delay[4], "debug.frame_delay_4"
11065 &setup.debug.frame_delay[5], "debug.frame_delay_5"
11069 &setup.debug.frame_delay[6], "debug.frame_delay_6"
11073 &setup.debug.frame_delay[7], "debug.frame_delay_7"
11077 &setup.debug.frame_delay[8], "debug.frame_delay_8"
11081 &setup.debug.frame_delay[9], "debug.frame_delay_9"
11085 &setup.debug.frame_delay_key[0], "debug.key.frame_delay_0"
11089 &setup.debug.frame_delay_key[1], "debug.key.frame_delay_1"
11093 &setup.debug.frame_delay_key[2], "debug.key.frame_delay_2"
11097 &setup.debug.frame_delay_key[3], "debug.key.frame_delay_3"
11101 &setup.debug.frame_delay_key[4], "debug.key.frame_delay_4"
11105 &setup.debug.frame_delay_key[5], "debug.key.frame_delay_5"
11109 &setup.debug.frame_delay_key[6], "debug.key.frame_delay_6"
11113 &setup.debug.frame_delay_key[7], "debug.key.frame_delay_7"
11117 &setup.debug.frame_delay_key[8], "debug.key.frame_delay_8"
11121 &setup.debug.frame_delay_key[9], "debug.key.frame_delay_9"
11125 &setup.debug.frame_delay_use_mod_key, "debug.frame_delay.use_mod_key"},
11128 &setup.debug.frame_delay_game_only, "debug.frame_delay.game_only"
11132 &setup.debug.show_frames_per_second, "debug.show_frames_per_second"
11136 &setup.debug.xsn_mode, "debug.xsn_mode"
11140 &setup.debug.xsn_percent, "debug.xsn_percent"
11144 static struct TokenInfo options_setup_tokens[] =
11148 &setup.options.verbose, "options.verbose"
11152 &setup.options.debug, "options.debug"
11156 &setup.options.debug_mode, "options.debug_mode"
11160 static void setSetupInfoToDefaults(struct SetupInfo *si)
11164 si->player_name = getStringCopy(getDefaultUserName(user.nr));
11166 si->multiple_users = TRUE;
11169 si->sound_loops = TRUE;
11170 si->sound_music = TRUE;
11171 si->sound_simple = TRUE;
11173 si->global_animations = TRUE;
11174 si->scroll_delay = TRUE;
11175 si->forced_scroll_delay = FALSE;
11176 si->scroll_delay_value = STD_SCROLL_DELAY;
11177 si->engine_snapshot_mode = getStringCopy(STR_SNAPSHOT_MODE_DEFAULT);
11178 si->engine_snapshot_memory = SNAPSHOT_MEMORY_DEFAULT;
11179 si->fade_screens = TRUE;
11180 si->autorecord = TRUE;
11181 si->autorecord_after_replay = TRUE;
11182 si->auto_pause_on_start = FALSE;
11183 si->show_titlescreen = TRUE;
11184 si->quick_doors = FALSE;
11185 si->team_mode = FALSE;
11186 si->handicap = TRUE;
11187 si->skip_levels = TRUE;
11188 si->increment_levels = TRUE;
11189 si->auto_play_next_level = TRUE;
11190 si->count_score_after_game = TRUE;
11191 si->show_scores_after_game = TRUE;
11192 si->time_limit = TRUE;
11193 si->fullscreen = FALSE;
11194 si->window_scaling_percent = STD_WINDOW_SCALING_PERCENT;
11195 si->window_scaling_quality = getStringCopy(SCALING_QUALITY_DEFAULT);
11196 si->screen_rendering_mode = getStringCopy(STR_SPECIAL_RENDERING_DEFAULT);
11197 si->vsync_mode = getStringCopy(STR_VSYNC_MODE_DEFAULT);
11198 si->ask_on_escape = TRUE;
11199 si->ask_on_escape_editor = TRUE;
11200 si->ask_on_game_over = TRUE;
11201 si->ask_on_quit_game = TRUE;
11202 si->ask_on_quit_program = TRUE;
11203 si->quick_switch = FALSE;
11204 si->input_on_focus = FALSE;
11205 si->prefer_aga_graphics = TRUE;
11206 si->prefer_lowpass_sounds = FALSE;
11207 si->prefer_extra_panel_items = TRUE;
11208 si->game_speed_extended = FALSE;
11209 si->game_frame_delay = GAME_FRAME_DELAY;
11210 si->bd_skip_uncovering = FALSE;
11211 si->bd_skip_hatching = FALSE;
11212 si->bd_scroll_delay = TRUE;
11213 si->bd_smooth_movements = AUTO;
11214 si->sp_show_border_elements = FALSE;
11215 si->small_game_graphics = FALSE;
11216 si->show_load_save_buttons = FALSE;
11217 si->show_undo_redo_buttons = FALSE;
11218 si->scores_in_highscore_list = getStringCopy(STR_SCORES_TYPE_DEFAULT);
11220 si->graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
11221 si->sounds_set = getStringCopy(SND_CLASSIC_SUBDIR);
11222 si->music_set = getStringCopy(MUS_CLASSIC_SUBDIR);
11224 si->override_level_graphics = FALSE;
11225 si->override_level_sounds = FALSE;
11226 si->override_level_music = FALSE;
11228 si->volume_simple = 100; // percent
11229 si->volume_loops = 100; // percent
11230 si->volume_music = 100; // percent
11232 si->network_mode = FALSE;
11233 si->network_player_nr = 0; // first player
11234 si->network_server_hostname = getStringCopy(STR_NETWORK_AUTO_DETECT);
11236 si->touch.control_type = getStringCopy(TOUCH_CONTROL_DEFAULT);
11237 si->touch.move_distance = TOUCH_MOVE_DISTANCE_DEFAULT; // percent
11238 si->touch.drop_distance = TOUCH_DROP_DISTANCE_DEFAULT; // percent
11239 si->touch.transparency = TOUCH_TRANSPARENCY_DEFAULT; // percent
11240 si->touch.draw_outlined = TRUE;
11241 si->touch.draw_pressed = TRUE;
11243 for (i = 0; i < 2; i++)
11245 char *default_grid_button[6][2] =
11251 { "111222", " vv " },
11252 { "111222", " vv " }
11254 int grid_xsize = DEFAULT_GRID_XSIZE(i);
11255 int grid_ysize = DEFAULT_GRID_YSIZE(i);
11256 int min_xsize = MIN(6, grid_xsize);
11257 int min_ysize = MIN(6, grid_ysize);
11258 int startx = grid_xsize - min_xsize;
11259 int starty = grid_ysize - min_ysize;
11262 // virtual buttons grid can only be set to defaults if video is initialized
11263 // (this will be repeated if virtual buttons are not loaded from setup file)
11264 if (video.initialized)
11266 si->touch.grid_xsize[i] = grid_xsize;
11267 si->touch.grid_ysize[i] = grid_ysize;
11271 si->touch.grid_xsize[i] = -1;
11272 si->touch.grid_ysize[i] = -1;
11275 for (x = 0; x < MAX_GRID_XSIZE; x++)
11276 for (y = 0; y < MAX_GRID_YSIZE; y++)
11277 si->touch.grid_button[i][x][y] = CHAR_GRID_BUTTON_NONE;
11279 for (x = 0; x < min_xsize; x++)
11280 for (y = 0; y < min_ysize; y++)
11281 si->touch.grid_button[i][x][starty + y] =
11282 default_grid_button[y][0][x];
11284 for (x = 0; x < min_xsize; x++)
11285 for (y = 0; y < min_ysize; y++)
11286 si->touch.grid_button[i][startx + x][starty + y] =
11287 default_grid_button[y][1][x];
11290 si->touch.grid_initialized = video.initialized;
11292 si->touch.overlay_buttons = FALSE;
11294 si->editor.el_boulderdash = TRUE;
11295 si->editor.el_boulderdash_native = TRUE;
11296 si->editor.el_boulderdash_effects = TRUE;
11297 si->editor.el_emerald_mine = TRUE;
11298 si->editor.el_emerald_mine_club = TRUE;
11299 si->editor.el_more = TRUE;
11300 si->editor.el_sokoban = TRUE;
11301 si->editor.el_supaplex = TRUE;
11302 si->editor.el_diamond_caves = TRUE;
11303 si->editor.el_dx_boulderdash = TRUE;
11305 si->editor.el_mirror_magic = TRUE;
11306 si->editor.el_deflektor = TRUE;
11308 si->editor.el_chars = TRUE;
11309 si->editor.el_steel_chars = TRUE;
11311 si->editor.el_classic = TRUE;
11312 si->editor.el_custom = TRUE;
11314 si->editor.el_user_defined = FALSE;
11315 si->editor.el_dynamic = TRUE;
11317 si->editor.el_headlines = TRUE;
11319 si->editor.show_element_token = FALSE;
11321 si->editor.show_read_only_warning = TRUE;
11323 si->editor.use_template_for_new_levels = TRUE;
11325 si->shortcut.save_game = DEFAULT_KEY_SAVE_GAME;
11326 si->shortcut.load_game = DEFAULT_KEY_LOAD_GAME;
11327 si->shortcut.restart_game = DEFAULT_KEY_RESTART_GAME;
11328 si->shortcut.pause_before_end = DEFAULT_KEY_PAUSE_BEFORE_END;
11329 si->shortcut.toggle_pause = DEFAULT_KEY_TOGGLE_PAUSE;
11331 si->shortcut.focus_player[0] = DEFAULT_KEY_FOCUS_PLAYER_1;
11332 si->shortcut.focus_player[1] = DEFAULT_KEY_FOCUS_PLAYER_2;
11333 si->shortcut.focus_player[2] = DEFAULT_KEY_FOCUS_PLAYER_3;
11334 si->shortcut.focus_player[3] = DEFAULT_KEY_FOCUS_PLAYER_4;
11335 si->shortcut.focus_player_all = DEFAULT_KEY_FOCUS_PLAYER_ALL;
11337 si->shortcut.tape_eject = DEFAULT_KEY_TAPE_EJECT;
11338 si->shortcut.tape_extra = DEFAULT_KEY_TAPE_EXTRA;
11339 si->shortcut.tape_stop = DEFAULT_KEY_TAPE_STOP;
11340 si->shortcut.tape_pause = DEFAULT_KEY_TAPE_PAUSE;
11341 si->shortcut.tape_record = DEFAULT_KEY_TAPE_RECORD;
11342 si->shortcut.tape_play = DEFAULT_KEY_TAPE_PLAY;
11344 si->shortcut.sound_simple = DEFAULT_KEY_SOUND_SIMPLE;
11345 si->shortcut.sound_loops = DEFAULT_KEY_SOUND_LOOPS;
11346 si->shortcut.sound_music = DEFAULT_KEY_SOUND_MUSIC;
11348 si->shortcut.snap_left = DEFAULT_KEY_SNAP_LEFT;
11349 si->shortcut.snap_right = DEFAULT_KEY_SNAP_RIGHT;
11350 si->shortcut.snap_up = DEFAULT_KEY_SNAP_UP;
11351 si->shortcut.snap_down = DEFAULT_KEY_SNAP_DOWN;
11353 for (i = 0; i < MAX_PLAYERS; i++)
11355 si->input[i].use_joystick = FALSE;
11356 si->input[i].joy.device_name = getStringCopy(getDeviceNameFromJoystickNr(i));
11357 si->input[i].joy.xleft = JOYSTICK_XLEFT;
11358 si->input[i].joy.xmiddle = JOYSTICK_XMIDDLE;
11359 si->input[i].joy.xright = JOYSTICK_XRIGHT;
11360 si->input[i].joy.yupper = JOYSTICK_YUPPER;
11361 si->input[i].joy.ymiddle = JOYSTICK_YMIDDLE;
11362 si->input[i].joy.ylower = JOYSTICK_YLOWER;
11363 si->input[i].joy.snap = (i == 0 ? JOY_BUTTON_1 : 0);
11364 si->input[i].joy.drop = (i == 0 ? JOY_BUTTON_2 : 0);
11365 si->input[i].key.left = (i == 0 ? DEFAULT_KEY_LEFT : KSYM_UNDEFINED);
11366 si->input[i].key.right = (i == 0 ? DEFAULT_KEY_RIGHT : KSYM_UNDEFINED);
11367 si->input[i].key.up = (i == 0 ? DEFAULT_KEY_UP : KSYM_UNDEFINED);
11368 si->input[i].key.down = (i == 0 ? DEFAULT_KEY_DOWN : KSYM_UNDEFINED);
11369 si->input[i].key.snap = (i == 0 ? DEFAULT_KEY_SNAP : KSYM_UNDEFINED);
11370 si->input[i].key.drop = (i == 0 ? DEFAULT_KEY_DROP : KSYM_UNDEFINED);
11373 si->system.sdl_renderdriver = getStringCopy(ARG_DEFAULT);
11374 si->system.sdl_videodriver = getStringCopy(ARG_DEFAULT);
11375 si->system.sdl_audiodriver = getStringCopy(ARG_DEFAULT);
11376 si->system.audio_fragment_size = DEFAULT_AUDIO_FRAGMENT_SIZE;
11378 si->internal.program_title = getStringCopy(PROGRAM_TITLE_STRING);
11379 si->internal.program_version = getStringCopy(getProgramRealVersionString());
11380 si->internal.program_author = getStringCopy(PROGRAM_AUTHOR_STRING);
11381 si->internal.program_email = getStringCopy(PROGRAM_EMAIL_STRING);
11382 si->internal.program_website = getStringCopy(PROGRAM_WEBSITE_STRING);
11383 si->internal.program_copyright = getStringCopy(PROGRAM_COPYRIGHT_STRING);
11384 si->internal.program_company = getStringCopy(PROGRAM_COMPANY_STRING);
11386 si->internal.program_icon_file = getStringCopy(PROGRAM_ICON_FILENAME);
11388 si->internal.default_graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
11389 si->internal.default_sounds_set = getStringCopy(SND_CLASSIC_SUBDIR);
11390 si->internal.default_music_set = getStringCopy(MUS_CLASSIC_SUBDIR);
11392 si->internal.fallback_graphics_file = getStringCopy(UNDEFINED_FILENAME);
11393 si->internal.fallback_sounds_file = getStringCopy(UNDEFINED_FILENAME);
11394 si->internal.fallback_music_file = getStringCopy(UNDEFINED_FILENAME);
11396 si->internal.default_level_series = getStringCopy(UNDEFINED_LEVELSET);
11397 si->internal.choose_from_top_leveldir = FALSE;
11398 si->internal.show_scaling_in_title = TRUE;
11399 si->internal.create_user_levelset = TRUE;
11400 si->internal.info_screens_from_main = FALSE;
11402 si->internal.default_window_width = WIN_XSIZE_DEFAULT;
11403 si->internal.default_window_height = WIN_YSIZE_DEFAULT;
11405 si->debug.frame_delay[0] = DEFAULT_FRAME_DELAY_0;
11406 si->debug.frame_delay[1] = DEFAULT_FRAME_DELAY_1;
11407 si->debug.frame_delay[2] = DEFAULT_FRAME_DELAY_2;
11408 si->debug.frame_delay[3] = DEFAULT_FRAME_DELAY_3;
11409 si->debug.frame_delay[4] = DEFAULT_FRAME_DELAY_4;
11410 si->debug.frame_delay[5] = DEFAULT_FRAME_DELAY_5;
11411 si->debug.frame_delay[6] = DEFAULT_FRAME_DELAY_6;
11412 si->debug.frame_delay[7] = DEFAULT_FRAME_DELAY_7;
11413 si->debug.frame_delay[8] = DEFAULT_FRAME_DELAY_8;
11414 si->debug.frame_delay[9] = DEFAULT_FRAME_DELAY_9;
11416 si->debug.frame_delay_key[0] = DEFAULT_KEY_FRAME_DELAY_0;
11417 si->debug.frame_delay_key[1] = DEFAULT_KEY_FRAME_DELAY_1;
11418 si->debug.frame_delay_key[2] = DEFAULT_KEY_FRAME_DELAY_2;
11419 si->debug.frame_delay_key[3] = DEFAULT_KEY_FRAME_DELAY_3;
11420 si->debug.frame_delay_key[4] = DEFAULT_KEY_FRAME_DELAY_4;
11421 si->debug.frame_delay_key[5] = DEFAULT_KEY_FRAME_DELAY_5;
11422 si->debug.frame_delay_key[6] = DEFAULT_KEY_FRAME_DELAY_6;
11423 si->debug.frame_delay_key[7] = DEFAULT_KEY_FRAME_DELAY_7;
11424 si->debug.frame_delay_key[8] = DEFAULT_KEY_FRAME_DELAY_8;
11425 si->debug.frame_delay_key[9] = DEFAULT_KEY_FRAME_DELAY_9;
11427 si->debug.frame_delay_use_mod_key = DEFAULT_FRAME_DELAY_USE_MOD_KEY;
11428 si->debug.frame_delay_game_only = DEFAULT_FRAME_DELAY_GAME_ONLY;
11430 si->debug.show_frames_per_second = FALSE;
11432 si->debug.xsn_mode = AUTO;
11433 si->debug.xsn_percent = 0;
11435 si->options.verbose = FALSE;
11436 si->options.debug = FALSE;
11437 si->options.debug_mode = getStringCopy(ARG_UNDEFINED_STRING);
11439 #if defined(PLATFORM_ANDROID)
11440 si->fullscreen = TRUE;
11441 si->touch.overlay_buttons = TRUE;
11444 setHideSetupEntry(&setup.debug.xsn_mode);
11447 static void setSetupInfoToDefaults_AutoSetup(struct SetupInfo *si)
11449 si->auto_setup.editor_zoom_tilesize = MINI_TILESIZE;
11452 static void setSetupInfoToDefaults_ServerSetup(struct SetupInfo *si)
11454 si->player_uuid = NULL; // (will be set later)
11455 si->player_version = 1; // (will be set later)
11457 si->use_api_server = TRUE;
11458 si->api_server_hostname = getStringCopy(API_SERVER_HOSTNAME);
11459 si->api_server_password = getStringCopy(UNDEFINED_PASSWORD);
11460 si->ask_for_uploading_tapes = TRUE;
11461 si->ask_for_remaining_tapes = FALSE;
11462 si->provide_uploading_tapes = TRUE;
11463 si->ask_for_using_api_server = TRUE;
11464 si->has_remaining_tapes = FALSE;
11467 static void setSetupInfoToDefaults_EditorCascade(struct SetupInfo *si)
11469 si->editor_cascade.el_bd = TRUE;
11470 si->editor_cascade.el_bd_native = TRUE;
11471 si->editor_cascade.el_bd_effects = FALSE;
11472 si->editor_cascade.el_em = TRUE;
11473 si->editor_cascade.el_emc = TRUE;
11474 si->editor_cascade.el_rnd = TRUE;
11475 si->editor_cascade.el_sb = TRUE;
11476 si->editor_cascade.el_sp = TRUE;
11477 si->editor_cascade.el_dc = TRUE;
11478 si->editor_cascade.el_dx = TRUE;
11480 si->editor_cascade.el_mm = TRUE;
11481 si->editor_cascade.el_df = TRUE;
11483 si->editor_cascade.el_chars = FALSE;
11484 si->editor_cascade.el_steel_chars = FALSE;
11485 si->editor_cascade.el_ce = FALSE;
11486 si->editor_cascade.el_ge = FALSE;
11487 si->editor_cascade.el_es = FALSE;
11488 si->editor_cascade.el_ref = FALSE;
11489 si->editor_cascade.el_user = FALSE;
11490 si->editor_cascade.el_dynamic = FALSE;
11493 #define MAX_HIDE_SETUP_TOKEN_SIZE 20
11495 static char *getHideSetupToken(void *setup_value)
11497 static char hide_setup_token[MAX_HIDE_SETUP_TOKEN_SIZE];
11499 if (setup_value != NULL)
11500 snprintf(hide_setup_token, MAX_HIDE_SETUP_TOKEN_SIZE, "%p", setup_value);
11502 return hide_setup_token;
11505 void setHideSetupEntry(void *setup_value)
11507 char *hide_setup_token = getHideSetupToken(setup_value);
11509 if (hide_setup_hash == NULL)
11510 hide_setup_hash = newSetupFileHash();
11512 if (setup_value != NULL)
11513 setHashEntry(hide_setup_hash, hide_setup_token, "");
11516 void removeHideSetupEntry(void *setup_value)
11518 char *hide_setup_token = getHideSetupToken(setup_value);
11520 if (setup_value != NULL)
11521 removeHashEntry(hide_setup_hash, hide_setup_token);
11524 boolean hideSetupEntry(void *setup_value)
11526 char *hide_setup_token = getHideSetupToken(setup_value);
11528 return (setup_value != NULL &&
11529 getHashEntry(hide_setup_hash, hide_setup_token) != NULL);
11532 static void setSetupInfoFromTokenText(SetupFileHash *setup_file_hash,
11533 struct TokenInfo *token_info,
11534 int token_nr, char *token_text)
11536 char *token_hide_text = getStringCat2(token_text, ".hide");
11537 char *token_hide_value = getHashEntry(setup_file_hash, token_hide_text);
11539 // set the value of this setup option in the setup option structure
11540 setSetupInfo(token_info, token_nr, getHashEntry(setup_file_hash, token_text));
11542 // check if this setup option should be hidden in the setup menu
11543 if (token_hide_value != NULL && get_boolean_from_string(token_hide_value))
11544 setHideSetupEntry(token_info[token_nr].value);
11546 free(token_hide_text);
11549 static void setSetupInfoFromTokenInfo(SetupFileHash *setup_file_hash,
11550 struct TokenInfo *token_info,
11553 setSetupInfoFromTokenText(setup_file_hash, token_info, token_nr,
11554 token_info[token_nr].text);
11557 static void decodeSetupFileHash_Default(SetupFileHash *setup_file_hash)
11561 if (!setup_file_hash)
11564 for (i = 0; i < ARRAY_SIZE(global_setup_tokens); i++)
11565 setSetupInfoFromTokenInfo(setup_file_hash, global_setup_tokens, i);
11567 setup.touch.grid_initialized = TRUE;
11568 for (i = 0; i < 2; i++)
11570 int grid_xsize = setup.touch.grid_xsize[i];
11571 int grid_ysize = setup.touch.grid_ysize[i];
11574 // if virtual buttons are not loaded from setup file, repeat initializing
11575 // virtual buttons grid with default values later when video is initialized
11576 if (grid_xsize == -1 ||
11579 setup.touch.grid_initialized = FALSE;
11584 for (y = 0; y < grid_ysize; y++)
11586 char token_string[MAX_LINE_LEN];
11588 sprintf(token_string, "touch.virtual_buttons.%d.%02d", i, y);
11590 char *value_string = getHashEntry(setup_file_hash, token_string);
11592 if (value_string == NULL)
11595 for (x = 0; x < grid_xsize; x++)
11597 char c = value_string[x];
11599 setup.touch.grid_button[i][x][y] =
11600 (c == '.' ? CHAR_GRID_BUTTON_NONE : c);
11605 for (i = 0; i < ARRAY_SIZE(editor_setup_tokens); i++)
11606 setSetupInfoFromTokenInfo(setup_file_hash, editor_setup_tokens, i);
11608 for (i = 0; i < ARRAY_SIZE(shortcut_setup_tokens); i++)
11609 setSetupInfoFromTokenInfo(setup_file_hash, shortcut_setup_tokens, i);
11611 for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
11615 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
11617 setup_input = setup.input[pnr];
11618 for (i = 0; i < ARRAY_SIZE(player_setup_tokens); i++)
11620 char full_token[100];
11622 sprintf(full_token, "%s%s", prefix, player_setup_tokens[i].text);
11623 setSetupInfoFromTokenText(setup_file_hash, player_setup_tokens, i,
11626 setup.input[pnr] = setup_input;
11629 for (i = 0; i < ARRAY_SIZE(system_setup_tokens); i++)
11630 setSetupInfoFromTokenInfo(setup_file_hash, system_setup_tokens, i);
11632 for (i = 0; i < ARRAY_SIZE(internal_setup_tokens); i++)
11633 setSetupInfoFromTokenInfo(setup_file_hash, internal_setup_tokens, i);
11635 for (i = 0; i < ARRAY_SIZE(debug_setup_tokens); i++)
11636 setSetupInfoFromTokenInfo(setup_file_hash, debug_setup_tokens, i);
11638 for (i = 0; i < ARRAY_SIZE(options_setup_tokens); i++)
11639 setSetupInfoFromTokenInfo(setup_file_hash, options_setup_tokens, i);
11641 setHideRelatedSetupEntries();
11644 static void decodeSetupFileHash_AutoSetup(SetupFileHash *setup_file_hash)
11648 if (!setup_file_hash)
11651 for (i = 0; i < ARRAY_SIZE(auto_setup_tokens); i++)
11652 setSetupInfo(auto_setup_tokens, i,
11653 getHashEntry(setup_file_hash,
11654 auto_setup_tokens[i].text));
11657 static void decodeSetupFileHash_ServerSetup(SetupFileHash *setup_file_hash)
11661 if (!setup_file_hash)
11664 for (i = 0; i < ARRAY_SIZE(server_setup_tokens); i++)
11665 setSetupInfo(server_setup_tokens, i,
11666 getHashEntry(setup_file_hash,
11667 server_setup_tokens[i].text));
11670 static void decodeSetupFileHash_EditorCascade(SetupFileHash *setup_file_hash)
11674 if (!setup_file_hash)
11677 for (i = 0; i < ARRAY_SIZE(editor_cascade_setup_tokens); i++)
11678 setSetupInfo(editor_cascade_setup_tokens, i,
11679 getHashEntry(setup_file_hash,
11680 editor_cascade_setup_tokens[i].text));
11683 void LoadUserNames(void)
11685 int last_user_nr = user.nr;
11688 if (global.user_names != NULL)
11690 for (i = 0; i < MAX_PLAYER_NAMES; i++)
11691 checked_free(global.user_names[i]);
11693 checked_free(global.user_names);
11696 global.user_names = checked_calloc(MAX_PLAYER_NAMES * sizeof(char *));
11698 for (i = 0; i < MAX_PLAYER_NAMES; i++)
11702 SetupFileHash *setup_file_hash = loadSetupFileHash(getSetupFilename());
11704 if (setup_file_hash)
11706 char *player_name = getHashEntry(setup_file_hash, "player_name");
11708 global.user_names[i] = getFixedUserName(player_name);
11710 freeSetupFileHash(setup_file_hash);
11713 if (global.user_names[i] == NULL)
11714 global.user_names[i] = getStringCopy(getDefaultUserName(i));
11717 user.nr = last_user_nr;
11720 void LoadSetupFromFilename(char *filename)
11722 SetupFileHash *setup_file_hash = loadSetupFileHash(filename);
11724 if (setup_file_hash)
11726 decodeSetupFileHash_Default(setup_file_hash);
11728 freeSetupFileHash(setup_file_hash);
11732 Debug("setup", "using default setup values");
11736 static void LoadSetup_SpecialPostProcessing(void)
11738 char *player_name_new;
11740 // needed to work around problems with fixed length strings
11741 player_name_new = getFixedUserName(setup.player_name);
11742 free(setup.player_name);
11743 setup.player_name = player_name_new;
11745 // "scroll_delay: on(3) / off(0)" was replaced by scroll delay value
11746 if (setup.scroll_delay == FALSE)
11748 setup.scroll_delay_value = MIN_SCROLL_DELAY;
11749 setup.scroll_delay = TRUE; // now always "on"
11752 // make sure that scroll delay value stays inside valid range
11753 setup.scroll_delay_value =
11754 MIN(MAX(MIN_SCROLL_DELAY, setup.scroll_delay_value), MAX_SCROLL_DELAY);
11757 void LoadSetup_Default(void)
11761 // always start with reliable default values
11762 setSetupInfoToDefaults(&setup);
11764 // try to load setup values from default setup file
11765 filename = getDefaultSetupFilename();
11767 if (fileExists(filename))
11768 LoadSetupFromFilename(filename);
11770 // try to load setup values from platform setup file
11771 filename = getPlatformSetupFilename();
11773 if (fileExists(filename))
11774 LoadSetupFromFilename(filename);
11776 // try to load setup values from user setup file
11777 filename = getSetupFilename();
11779 LoadSetupFromFilename(filename);
11781 LoadSetup_SpecialPostProcessing();
11784 void LoadSetup_AutoSetup(void)
11786 char *filename = getPath2(getSetupDir(), AUTOSETUP_FILENAME);
11787 SetupFileHash *setup_file_hash = NULL;
11789 // always start with reliable default values
11790 setSetupInfoToDefaults_AutoSetup(&setup);
11792 setup_file_hash = loadSetupFileHash(filename);
11794 if (setup_file_hash)
11796 decodeSetupFileHash_AutoSetup(setup_file_hash);
11798 freeSetupFileHash(setup_file_hash);
11804 void LoadSetup_ServerSetup(void)
11806 char *filename = getPath2(getSetupDir(), SERVERSETUP_FILENAME);
11807 SetupFileHash *setup_file_hash = NULL;
11809 // always start with reliable default values
11810 setSetupInfoToDefaults_ServerSetup(&setup);
11812 setup_file_hash = loadSetupFileHash(filename);
11814 if (setup_file_hash)
11816 decodeSetupFileHash_ServerSetup(setup_file_hash);
11818 freeSetupFileHash(setup_file_hash);
11823 if (setup.player_uuid == NULL)
11825 // player UUID does not yet exist in setup file
11826 setup.player_uuid = getStringCopy(getUUID());
11827 setup.player_version = 2;
11829 SaveSetup_ServerSetup();
11833 void LoadSetup_EditorCascade(void)
11835 char *filename = getPath2(getSetupDir(), EDITORCASCADE_FILENAME);
11836 SetupFileHash *setup_file_hash = NULL;
11838 // always start with reliable default values
11839 setSetupInfoToDefaults_EditorCascade(&setup);
11841 setup_file_hash = loadSetupFileHash(filename);
11843 if (setup_file_hash)
11845 decodeSetupFileHash_EditorCascade(setup_file_hash);
11847 freeSetupFileHash(setup_file_hash);
11853 void LoadSetup(void)
11855 LoadSetup_Default();
11856 LoadSetup_AutoSetup();
11857 LoadSetup_ServerSetup();
11858 LoadSetup_EditorCascade();
11861 static void addGameControllerMappingToHash(SetupFileHash *mappings_hash,
11862 char *mapping_line)
11864 char mapping_guid[MAX_LINE_LEN];
11865 char *mapping_start, *mapping_end;
11867 // get GUID from game controller mapping line: copy complete line
11868 strncpy(mapping_guid, mapping_line, MAX_LINE_LEN - 1);
11869 mapping_guid[MAX_LINE_LEN - 1] = '\0';
11871 // get GUID from game controller mapping line: cut after GUID part
11872 mapping_start = strchr(mapping_guid, ',');
11873 if (mapping_start != NULL)
11874 *mapping_start = '\0';
11876 // cut newline from game controller mapping line
11877 mapping_end = strchr(mapping_line, '\n');
11878 if (mapping_end != NULL)
11879 *mapping_end = '\0';
11881 // add mapping entry to game controller mappings hash
11882 setHashEntry(mappings_hash, mapping_guid, mapping_line);
11885 static void LoadSetup_ReadGameControllerMappings(SetupFileHash *mappings_hash,
11890 if (!(file = fopen(filename, MODE_READ)))
11892 Warn("cannot read game controller mappings file '%s'", filename);
11897 while (!feof(file))
11899 char line[MAX_LINE_LEN];
11901 if (!fgets(line, MAX_LINE_LEN, file))
11904 addGameControllerMappingToHash(mappings_hash, line);
11910 void SaveSetup_Default(void)
11912 char *filename = getSetupFilename();
11916 InitUserDataDirectory();
11918 if (!(file = fopen(filename, MODE_WRITE)))
11920 Warn("cannot write setup file '%s'", filename);
11925 fprintFileHeader(file, SETUP_FILENAME);
11927 for (i = 0; i < ARRAY_SIZE(global_setup_tokens); i++)
11929 // just to make things nicer :)
11930 if (global_setup_tokens[i].value == &setup.multiple_users ||
11931 global_setup_tokens[i].value == &setup.sound ||
11932 global_setup_tokens[i].value == &setup.graphics_set ||
11933 global_setup_tokens[i].value == &setup.volume_simple ||
11934 global_setup_tokens[i].value == &setup.network_mode ||
11935 global_setup_tokens[i].value == &setup.touch.control_type ||
11936 global_setup_tokens[i].value == &setup.touch.grid_xsize[0] ||
11937 global_setup_tokens[i].value == &setup.touch.grid_xsize[1])
11938 fprintf(file, "\n");
11940 fprintf(file, "%s\n", getSetupLine(global_setup_tokens, "", i));
11943 for (i = 0; i < 2; i++)
11945 int grid_xsize = setup.touch.grid_xsize[i];
11946 int grid_ysize = setup.touch.grid_ysize[i];
11949 fprintf(file, "\n");
11951 for (y = 0; y < grid_ysize; y++)
11953 char token_string[MAX_LINE_LEN];
11954 char value_string[MAX_LINE_LEN];
11956 sprintf(token_string, "touch.virtual_buttons.%d.%02d", i, y);
11958 for (x = 0; x < grid_xsize; x++)
11960 char c = setup.touch.grid_button[i][x][y];
11962 value_string[x] = (c == CHAR_GRID_BUTTON_NONE ? '.' : c);
11965 value_string[grid_xsize] = '\0';
11967 fprintf(file, "%s\n", getFormattedSetupEntry(token_string, value_string));
11971 fprintf(file, "\n");
11972 for (i = 0; i < ARRAY_SIZE(editor_setup_tokens); i++)
11973 fprintf(file, "%s\n", getSetupLine(editor_setup_tokens, "", i));
11975 fprintf(file, "\n");
11976 for (i = 0; i < ARRAY_SIZE(shortcut_setup_tokens); i++)
11977 fprintf(file, "%s\n", getSetupLine(shortcut_setup_tokens, "", i));
11979 for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
11983 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
11984 fprintf(file, "\n");
11986 setup_input = setup.input[pnr];
11987 for (i = 0; i < ARRAY_SIZE(player_setup_tokens); i++)
11988 fprintf(file, "%s\n", getSetupLine(player_setup_tokens, prefix, i));
11991 fprintf(file, "\n");
11992 for (i = 0; i < ARRAY_SIZE(system_setup_tokens); i++)
11993 fprintf(file, "%s\n", getSetupLine(system_setup_tokens, "", i));
11995 // (internal setup values not saved to user setup file)
11997 fprintf(file, "\n");
11998 for (i = 0; i < ARRAY_SIZE(debug_setup_tokens); i++)
11999 if (!strPrefix(debug_setup_tokens[i].text, "debug.xsn_") ||
12000 setup.debug.xsn_mode != AUTO)
12001 fprintf(file, "%s\n", getSetupLine(debug_setup_tokens, "", i));
12003 fprintf(file, "\n");
12004 for (i = 0; i < ARRAY_SIZE(options_setup_tokens); i++)
12005 fprintf(file, "%s\n", getSetupLine(options_setup_tokens, "", i));
12009 SetFilePermissions(filename, PERMS_PRIVATE);
12012 void SaveSetup_AutoSetup(void)
12014 char *filename = getPath2(getSetupDir(), AUTOSETUP_FILENAME);
12018 InitUserDataDirectory();
12020 if (!(file = fopen(filename, MODE_WRITE)))
12022 Warn("cannot write auto setup file '%s'", filename);
12029 fprintFileHeader(file, AUTOSETUP_FILENAME);
12031 for (i = 0; i < ARRAY_SIZE(auto_setup_tokens); i++)
12032 fprintf(file, "%s\n", getSetupLine(auto_setup_tokens, "", i));
12036 SetFilePermissions(filename, PERMS_PRIVATE);
12041 void SaveSetup_ServerSetup(void)
12043 char *filename = getPath2(getSetupDir(), SERVERSETUP_FILENAME);
12047 InitUserDataDirectory();
12049 if (!(file = fopen(filename, MODE_WRITE)))
12051 Warn("cannot write server setup file '%s'", filename);
12058 fprintFileHeader(file, SERVERSETUP_FILENAME);
12060 for (i = 0; i < ARRAY_SIZE(server_setup_tokens); i++)
12062 // just to make things nicer :)
12063 if (server_setup_tokens[i].value == &setup.use_api_server)
12064 fprintf(file, "\n");
12066 fprintf(file, "%s\n", getSetupLine(server_setup_tokens, "", i));
12071 SetFilePermissions(filename, PERMS_PRIVATE);
12076 void SaveSetup_EditorCascade(void)
12078 char *filename = getPath2(getSetupDir(), EDITORCASCADE_FILENAME);
12082 InitUserDataDirectory();
12084 if (!(file = fopen(filename, MODE_WRITE)))
12086 Warn("cannot write editor cascade state file '%s'", filename);
12093 fprintFileHeader(file, EDITORCASCADE_FILENAME);
12095 for (i = 0; i < ARRAY_SIZE(editor_cascade_setup_tokens); i++)
12096 fprintf(file, "%s\n", getSetupLine(editor_cascade_setup_tokens, "", i));
12100 SetFilePermissions(filename, PERMS_PRIVATE);
12105 void SaveSetup(void)
12107 SaveSetup_Default();
12108 SaveSetup_AutoSetup();
12109 SaveSetup_ServerSetup();
12110 SaveSetup_EditorCascade();
12113 static void SaveSetup_WriteGameControllerMappings(SetupFileHash *mappings_hash,
12118 if (!(file = fopen(filename, MODE_WRITE)))
12120 Warn("cannot write game controller mappings file '%s'", filename);
12125 BEGIN_HASH_ITERATION(mappings_hash, itr)
12127 fprintf(file, "%s\n", HASH_ITERATION_VALUE(itr));
12129 END_HASH_ITERATION(mappings_hash, itr)
12134 void SaveSetup_AddGameControllerMapping(char *mapping)
12136 char *filename = getPath2(getSetupDir(), GAMECONTROLLER_BASENAME);
12137 SetupFileHash *mappings_hash = newSetupFileHash();
12139 InitUserDataDirectory();
12141 // load existing personal game controller mappings
12142 LoadSetup_ReadGameControllerMappings(mappings_hash, filename);
12144 // add new mapping to personal game controller mappings
12145 addGameControllerMappingToHash(mappings_hash, mapping);
12147 // save updated personal game controller mappings
12148 SaveSetup_WriteGameControllerMappings(mappings_hash, filename);
12150 freeSetupFileHash(mappings_hash);
12154 void LoadCustomElementDescriptions(void)
12156 char *filename = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
12157 SetupFileHash *setup_file_hash;
12160 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
12162 if (element_info[i].custom_description != NULL)
12164 free(element_info[i].custom_description);
12165 element_info[i].custom_description = NULL;
12169 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
12172 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
12174 char *token = getStringCat2(element_info[i].token_name, ".name");
12175 char *value = getHashEntry(setup_file_hash, token);
12178 element_info[i].custom_description = getStringCopy(value);
12183 freeSetupFileHash(setup_file_hash);
12186 static int getElementFromToken(char *token)
12188 char *value = getHashEntry(element_token_hash, token);
12191 return atoi(value);
12193 Warn("unknown element token '%s'", token);
12195 return EL_UNDEFINED;
12198 void FreeGlobalAnimEventInfo(void)
12200 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
12202 if (gaei->event_list == NULL)
12207 for (i = 0; i < gaei->num_event_lists; i++)
12209 checked_free(gaei->event_list[i]->event_value);
12210 checked_free(gaei->event_list[i]);
12213 checked_free(gaei->event_list);
12215 gaei->event_list = NULL;
12216 gaei->num_event_lists = 0;
12219 static int AddGlobalAnimEventList(void)
12221 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
12222 int list_pos = gaei->num_event_lists++;
12224 gaei->event_list = checked_realloc(gaei->event_list, gaei->num_event_lists *
12225 sizeof(struct GlobalAnimEventListInfo *));
12227 gaei->event_list[list_pos] =
12228 checked_calloc(sizeof(struct GlobalAnimEventListInfo));
12230 struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
12232 gaeli->event_value = NULL;
12233 gaeli->num_event_values = 0;
12238 static int AddGlobalAnimEventValue(int list_pos, int event_value)
12240 // do not add empty global animation events
12241 if (event_value == ANIM_EVENT_NONE)
12244 // if list position is undefined, create new list
12245 if (list_pos == ANIM_EVENT_UNDEFINED)
12246 list_pos = AddGlobalAnimEventList();
12248 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
12249 struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
12250 int value_pos = gaeli->num_event_values++;
12252 gaeli->event_value = checked_realloc(gaeli->event_value,
12253 gaeli->num_event_values * sizeof(int *));
12255 gaeli->event_value[value_pos] = event_value;
12260 int GetGlobalAnimEventValue(int list_pos, int value_pos)
12262 if (list_pos == ANIM_EVENT_UNDEFINED)
12263 return ANIM_EVENT_NONE;
12265 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
12266 struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
12268 return gaeli->event_value[value_pos];
12271 int GetGlobalAnimEventValueCount(int list_pos)
12273 if (list_pos == ANIM_EVENT_UNDEFINED)
12276 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
12277 struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
12279 return gaeli->num_event_values;
12282 // This function checks if a string <s> of the format "string1, string2, ..."
12283 // exactly contains a string <s_contained>.
12285 static boolean string_has_parameter(char *s, char *s_contained)
12289 if (s == NULL || s_contained == NULL)
12292 if (strlen(s_contained) > strlen(s))
12295 if (strncmp(s, s_contained, strlen(s_contained)) == 0)
12297 char next_char = s[strlen(s_contained)];
12299 // check if next character is delimiter or whitespace
12300 if (next_char == ',' || next_char == '\0' ||
12301 next_char == ' ' || next_char == '\t')
12305 // check if string contains another parameter string after a comma
12306 substring = strchr(s, ',');
12307 if (substring == NULL) // string does not contain a comma
12310 // advance string pointer to next character after the comma
12313 // skip potential whitespaces after the comma
12314 while (*substring == ' ' || *substring == '\t')
12317 return string_has_parameter(substring, s_contained);
12320 static int get_anim_parameter_value_ce(char *s)
12323 char *pattern_1 = "ce_change:custom_";
12324 char *pattern_2 = ".page_";
12325 int pattern_1_len = strlen(pattern_1);
12326 char *matching_char = strstr(s_ptr, pattern_1);
12327 int result = ANIM_EVENT_NONE;
12329 if (matching_char == NULL)
12330 return ANIM_EVENT_NONE;
12332 result = ANIM_EVENT_CE_CHANGE;
12334 s_ptr = matching_char + pattern_1_len;
12336 // check for custom element number ("custom_X", "custom_XX" or "custom_XXX")
12337 if (*s_ptr >= '0' && *s_ptr <= '9')
12339 int gic_ce_nr = (*s_ptr++ - '0');
12341 if (*s_ptr >= '0' && *s_ptr <= '9')
12343 gic_ce_nr = 10 * gic_ce_nr + (*s_ptr++ - '0');
12345 if (*s_ptr >= '0' && *s_ptr <= '9')
12346 gic_ce_nr = 10 * gic_ce_nr + (*s_ptr++ - '0');
12349 if (gic_ce_nr < 1 || gic_ce_nr > NUM_CUSTOM_ELEMENTS)
12350 return ANIM_EVENT_NONE;
12352 // custom element stored as 0 to 255
12355 result |= gic_ce_nr << ANIM_EVENT_CE_BIT;
12359 // invalid custom element number specified
12361 return ANIM_EVENT_NONE;
12364 // check for change page number ("page_X" or "page_XX") (optional)
12365 if (strPrefix(s_ptr, pattern_2))
12367 s_ptr += strlen(pattern_2);
12369 if (*s_ptr >= '0' && *s_ptr <= '9')
12371 int gic_page_nr = (*s_ptr++ - '0');
12373 if (*s_ptr >= '0' && *s_ptr <= '9')
12374 gic_page_nr = 10 * gic_page_nr + (*s_ptr++ - '0');
12376 if (gic_page_nr < 1 || gic_page_nr > MAX_CHANGE_PAGES)
12377 return ANIM_EVENT_NONE;
12379 // change page stored as 1 to 32 (0 means "all change pages")
12381 result |= gic_page_nr << ANIM_EVENT_PAGE_BIT;
12385 // invalid animation part number specified
12387 return ANIM_EVENT_NONE;
12391 // discard result if next character is neither delimiter nor whitespace
12392 if (!(*s_ptr == ',' || *s_ptr == '\0' ||
12393 *s_ptr == ' ' || *s_ptr == '\t'))
12394 return ANIM_EVENT_NONE;
12399 static int get_anim_parameter_value(char *s)
12401 int event_value[] =
12409 char *pattern_1[] =
12417 char *pattern_2 = ".part_";
12418 char *matching_char = NULL;
12420 int pattern_1_len = 0;
12421 int result = ANIM_EVENT_NONE;
12424 result = get_anim_parameter_value_ce(s);
12426 if (result != ANIM_EVENT_NONE)
12429 for (i = 0; i < ARRAY_SIZE(event_value); i++)
12431 matching_char = strstr(s_ptr, pattern_1[i]);
12432 pattern_1_len = strlen(pattern_1[i]);
12433 result = event_value[i];
12435 if (matching_char != NULL)
12439 if (matching_char == NULL)
12440 return ANIM_EVENT_NONE;
12442 s_ptr = matching_char + pattern_1_len;
12444 // check for main animation number ("anim_X" or "anim_XX")
12445 if (*s_ptr >= '0' && *s_ptr <= '9')
12447 int gic_anim_nr = (*s_ptr++ - '0');
12449 if (*s_ptr >= '0' && *s_ptr <= '9')
12450 gic_anim_nr = 10 * gic_anim_nr + (*s_ptr++ - '0');
12452 if (gic_anim_nr < 1 || gic_anim_nr > MAX_GLOBAL_ANIMS)
12453 return ANIM_EVENT_NONE;
12455 result |= gic_anim_nr << ANIM_EVENT_ANIM_BIT;
12459 // invalid main animation number specified
12461 return ANIM_EVENT_NONE;
12464 // check for animation part number ("part_X" or "part_XX") (optional)
12465 if (strPrefix(s_ptr, pattern_2))
12467 s_ptr += strlen(pattern_2);
12469 if (*s_ptr >= '0' && *s_ptr <= '9')
12471 int gic_part_nr = (*s_ptr++ - '0');
12473 if (*s_ptr >= '0' && *s_ptr <= '9')
12474 gic_part_nr = 10 * gic_part_nr + (*s_ptr++ - '0');
12476 if (gic_part_nr < 1 || gic_part_nr > MAX_GLOBAL_ANIM_PARTS)
12477 return ANIM_EVENT_NONE;
12479 result |= gic_part_nr << ANIM_EVENT_PART_BIT;
12483 // invalid animation part number specified
12485 return ANIM_EVENT_NONE;
12489 // discard result if next character is neither delimiter nor whitespace
12490 if (!(*s_ptr == ',' || *s_ptr == '\0' ||
12491 *s_ptr == ' ' || *s_ptr == '\t'))
12492 return ANIM_EVENT_NONE;
12497 static int get_anim_parameter_values(char *s)
12499 int list_pos = ANIM_EVENT_UNDEFINED;
12500 int event_value = ANIM_EVENT_DEFAULT;
12502 if (string_has_parameter(s, "any"))
12503 event_value |= ANIM_EVENT_ANY;
12505 if (string_has_parameter(s, "click:self") ||
12506 string_has_parameter(s, "click") ||
12507 string_has_parameter(s, "self"))
12508 event_value |= ANIM_EVENT_SELF;
12510 if (string_has_parameter(s, "unclick:any"))
12511 event_value |= ANIM_EVENT_UNCLICK_ANY;
12513 // if animation event found, add it to global animation event list
12514 if (event_value != ANIM_EVENT_NONE)
12515 list_pos = AddGlobalAnimEventValue(list_pos, event_value);
12519 // add optional "click:anim_X" or "click:anim_X.part_X" parameter
12520 event_value = get_anim_parameter_value(s);
12522 // if animation event found, add it to global animation event list
12523 if (event_value != ANIM_EVENT_NONE)
12524 list_pos = AddGlobalAnimEventValue(list_pos, event_value);
12526 // continue with next part of the string, starting with next comma
12527 s = strchr(s + 1, ',');
12533 static int get_anim_action_parameter_value(char *token)
12535 // check most common default case first to massively speed things up
12536 if (strEqual(token, ARG_UNDEFINED))
12537 return ANIM_EVENT_ACTION_NONE;
12539 int result = getImageIDFromToken(token);
12543 char *gfx_token = getStringCat2("gfx.", token);
12545 result = getImageIDFromToken(gfx_token);
12547 checked_free(gfx_token);
12552 Key key = getKeyFromX11KeyName(token);
12554 if (key != KSYM_UNDEFINED)
12555 result = -(int)key;
12562 result = get_hash_from_string(token); // unsigned int => int
12563 result = ABS(result); // may be negative now
12564 result += (result < MAX_IMAGE_FILES ? MAX_IMAGE_FILES : 0);
12566 setHashEntry(anim_url_hash, int2str(result, 0), token);
12571 result = ANIM_EVENT_ACTION_NONE;
12576 int get_parameter_value(char *value_raw, char *suffix, int type)
12578 char *value = getStringToLower(value_raw);
12579 int result = 0; // probably a save default value
12581 if (strEqual(suffix, ".direction"))
12583 result = (strEqual(value, "left") ? MV_LEFT :
12584 strEqual(value, "right") ? MV_RIGHT :
12585 strEqual(value, "up") ? MV_UP :
12586 strEqual(value, "down") ? MV_DOWN : MV_NONE);
12588 else if (strEqual(suffix, ".position"))
12590 result = (strEqual(value, "left") ? POS_LEFT :
12591 strEqual(value, "right") ? POS_RIGHT :
12592 strEqual(value, "top") ? POS_TOP :
12593 strEqual(value, "upper") ? POS_UPPER :
12594 strEqual(value, "middle") ? POS_MIDDLE :
12595 strEqual(value, "lower") ? POS_LOWER :
12596 strEqual(value, "bottom") ? POS_BOTTOM :
12597 strEqual(value, "any") ? POS_ANY :
12598 strEqual(value, "ce") ? POS_CE :
12599 strEqual(value, "ce_trigger") ? POS_CE_TRIGGER :
12600 strEqual(value, "last") ? POS_LAST : POS_UNDEFINED);
12602 else if (strEqual(suffix, ".align"))
12604 result = (strEqual(value, "left") ? ALIGN_LEFT :
12605 strEqual(value, "right") ? ALIGN_RIGHT :
12606 strEqual(value, "center") ? ALIGN_CENTER :
12607 strEqual(value, "middle") ? ALIGN_CENTER : ALIGN_DEFAULT);
12609 else if (strEqual(suffix, ".valign"))
12611 result = (strEqual(value, "top") ? VALIGN_TOP :
12612 strEqual(value, "bottom") ? VALIGN_BOTTOM :
12613 strEqual(value, "middle") ? VALIGN_MIDDLE :
12614 strEqual(value, "center") ? VALIGN_MIDDLE : VALIGN_DEFAULT);
12616 else if (strEqual(suffix, ".anim_mode"))
12618 result = (string_has_parameter(value, "none") ? ANIM_NONE :
12619 string_has_parameter(value, "loop") ? ANIM_LOOP :
12620 string_has_parameter(value, "linear") ? ANIM_LINEAR :
12621 string_has_parameter(value, "pingpong") ? ANIM_PINGPONG :
12622 string_has_parameter(value, "pingpong2") ? ANIM_PINGPONG2 :
12623 string_has_parameter(value, "random") ? ANIM_RANDOM :
12624 string_has_parameter(value, "random_static") ? ANIM_RANDOM_STATIC :
12625 string_has_parameter(value, "ce_value") ? ANIM_CE_VALUE :
12626 string_has_parameter(value, "ce_score") ? ANIM_CE_SCORE :
12627 string_has_parameter(value, "ce_delay") ? ANIM_CE_DELAY :
12628 string_has_parameter(value, "horizontal") ? ANIM_HORIZONTAL :
12629 string_has_parameter(value, "vertical") ? ANIM_VERTICAL :
12630 string_has_parameter(value, "centered") ? ANIM_CENTERED :
12631 string_has_parameter(value, "all") ? ANIM_ALL :
12632 string_has_parameter(value, "tiled") ? ANIM_TILED :
12633 string_has_parameter(value, "level_nr") ? ANIM_LEVEL_NR :
12636 if (string_has_parameter(value, "once"))
12637 result |= ANIM_ONCE;
12639 if (string_has_parameter(value, "reverse"))
12640 result |= ANIM_REVERSE;
12642 if (string_has_parameter(value, "opaque_player"))
12643 result |= ANIM_OPAQUE_PLAYER;
12645 if (string_has_parameter(value, "static_panel"))
12646 result |= ANIM_STATIC_PANEL;
12648 else if (strEqual(suffix, ".init_event") ||
12649 strEqual(suffix, ".anim_event"))
12651 result = get_anim_parameter_values(value);
12653 else if (strEqual(suffix, ".init_delay_action") ||
12654 strEqual(suffix, ".anim_delay_action") ||
12655 strEqual(suffix, ".post_delay_action") ||
12656 strEqual(suffix, ".init_event_action") ||
12657 strEqual(suffix, ".anim_event_action"))
12659 result = get_anim_action_parameter_value(value_raw);
12661 else if (strEqual(suffix, ".class"))
12663 result = (strEqual(value, ARG_UNDEFINED) ? ARG_UNDEFINED_VALUE :
12664 get_hash_from_string(value));
12666 else if (strEqual(suffix, ".style"))
12668 result = STYLE_DEFAULT;
12670 if (string_has_parameter(value, "accurate_borders"))
12671 result |= STYLE_ACCURATE_BORDERS;
12673 if (string_has_parameter(value, "inner_corners"))
12674 result |= STYLE_INNER_CORNERS;
12676 if (string_has_parameter(value, "reverse"))
12677 result |= STYLE_REVERSE;
12679 if (string_has_parameter(value, "leftmost_position"))
12680 result |= STYLE_LEFTMOST_POSITION;
12682 if (string_has_parameter(value, "block_clicks"))
12683 result |= STYLE_BLOCK;
12685 if (string_has_parameter(value, "passthrough_clicks"))
12686 result |= STYLE_PASSTHROUGH;
12688 if (string_has_parameter(value, "multiple_actions"))
12689 result |= STYLE_MULTIPLE_ACTIONS;
12691 if (string_has_parameter(value, "consume_ce_event"))
12692 result |= STYLE_CONSUME_CE_EVENT;
12694 else if (strEqual(suffix, ".fade_mode"))
12696 result = (string_has_parameter(value, "none") ? FADE_MODE_NONE :
12697 string_has_parameter(value, "fade") ? FADE_MODE_FADE :
12698 string_has_parameter(value, "fade_in") ? FADE_MODE_FADE_IN :
12699 string_has_parameter(value, "fade_out") ? FADE_MODE_FADE_OUT :
12700 string_has_parameter(value, "crossfade") ? FADE_MODE_CROSSFADE :
12701 string_has_parameter(value, "melt") ? FADE_MODE_MELT :
12702 string_has_parameter(value, "curtain") ? FADE_MODE_CURTAIN :
12703 FADE_MODE_DEFAULT);
12705 else if (strEqual(suffix, ".auto_delay_unit"))
12707 result = (string_has_parameter(value, "ms") ? AUTO_DELAY_UNIT_MS :
12708 string_has_parameter(value, "frames") ? AUTO_DELAY_UNIT_FRAMES :
12709 AUTO_DELAY_UNIT_DEFAULT);
12711 else if (strPrefix(suffix, ".font")) // (may also be ".font_xyz")
12713 result = gfx.get_font_from_token_function(value);
12715 else // generic parameter of type integer or boolean
12717 result = (strEqual(value, ARG_UNDEFINED) ? ARG_UNDEFINED_VALUE :
12718 type == TYPE_INTEGER ? get_integer_from_string(value) :
12719 type == TYPE_BOOLEAN ? get_boolean_from_string(value) :
12720 ARG_UNDEFINED_VALUE);
12728 static int get_token_parameter_value(char *token, char *value_raw)
12732 if (token == NULL || value_raw == NULL)
12733 return ARG_UNDEFINED_VALUE;
12735 suffix = strrchr(token, '.');
12736 if (suffix == NULL)
12739 if (strEqual(suffix, ".element"))
12740 return getElementFromToken(value_raw);
12742 // !!! USE CORRECT VALUE TYPE (currently works also for TYPE_BOOLEAN) !!!
12743 return get_parameter_value(value_raw, suffix, TYPE_INTEGER);
12746 void InitMenuDesignSettings_FromHash(SetupFileHash *setup_file_hash,
12747 boolean ignore_defaults)
12751 for (i = 0; image_config_vars[i].token != NULL; i++)
12753 char *value = getHashEntry(setup_file_hash, image_config_vars[i].token);
12755 // (ignore definitions set to "[DEFAULT]" which are already initialized)
12756 if (ignore_defaults && strEqual(value, ARG_DEFAULT))
12760 *image_config_vars[i].value =
12761 get_token_parameter_value(image_config_vars[i].token, value);
12765 void InitMenuDesignSettings_Static(void)
12767 // always start with reliable default values from static default config
12768 InitMenuDesignSettings_FromHash(image_config_hash, FALSE);
12771 static void InitMenuDesignSettings_SpecialPreProcessing(void)
12775 // the following initializes hierarchical values from static configuration
12777 // special case: initialize "ARG_DEFAULT" values in static default config
12778 // (e.g., initialize "[titlemessage].fade_mode" from "[title].fade_mode")
12779 titlescreen_initial_first_default.fade_mode =
12780 title_initial_first_default.fade_mode;
12781 titlescreen_initial_first_default.fade_delay =
12782 title_initial_first_default.fade_delay;
12783 titlescreen_initial_first_default.post_delay =
12784 title_initial_first_default.post_delay;
12785 titlescreen_initial_first_default.auto_delay =
12786 title_initial_first_default.auto_delay;
12787 titlescreen_initial_first_default.auto_delay_unit =
12788 title_initial_first_default.auto_delay_unit;
12789 titlescreen_first_default.fade_mode = title_first_default.fade_mode;
12790 titlescreen_first_default.fade_delay = title_first_default.fade_delay;
12791 titlescreen_first_default.post_delay = title_first_default.post_delay;
12792 titlescreen_first_default.auto_delay = title_first_default.auto_delay;
12793 titlescreen_first_default.auto_delay_unit =
12794 title_first_default.auto_delay_unit;
12795 titlemessage_initial_first_default.fade_mode =
12796 title_initial_first_default.fade_mode;
12797 titlemessage_initial_first_default.fade_delay =
12798 title_initial_first_default.fade_delay;
12799 titlemessage_initial_first_default.post_delay =
12800 title_initial_first_default.post_delay;
12801 titlemessage_initial_first_default.auto_delay =
12802 title_initial_first_default.auto_delay;
12803 titlemessage_initial_first_default.auto_delay_unit =
12804 title_initial_first_default.auto_delay_unit;
12805 titlemessage_first_default.fade_mode = title_first_default.fade_mode;
12806 titlemessage_first_default.fade_delay = title_first_default.fade_delay;
12807 titlemessage_first_default.post_delay = title_first_default.post_delay;
12808 titlemessage_first_default.auto_delay = title_first_default.auto_delay;
12809 titlemessage_first_default.auto_delay_unit =
12810 title_first_default.auto_delay_unit;
12812 titlescreen_initial_default.fade_mode = title_initial_default.fade_mode;
12813 titlescreen_initial_default.fade_delay = title_initial_default.fade_delay;
12814 titlescreen_initial_default.post_delay = title_initial_default.post_delay;
12815 titlescreen_initial_default.auto_delay = title_initial_default.auto_delay;
12816 titlescreen_initial_default.auto_delay_unit =
12817 title_initial_default.auto_delay_unit;
12818 titlescreen_default.fade_mode = title_default.fade_mode;
12819 titlescreen_default.fade_delay = title_default.fade_delay;
12820 titlescreen_default.post_delay = title_default.post_delay;
12821 titlescreen_default.auto_delay = title_default.auto_delay;
12822 titlescreen_default.auto_delay_unit = title_default.auto_delay_unit;
12823 titlemessage_initial_default.fade_mode = title_initial_default.fade_mode;
12824 titlemessage_initial_default.fade_delay = title_initial_default.fade_delay;
12825 titlemessage_initial_default.post_delay = title_initial_default.post_delay;
12826 titlemessage_initial_default.auto_delay_unit =
12827 title_initial_default.auto_delay_unit;
12828 titlemessage_default.fade_mode = title_default.fade_mode;
12829 titlemessage_default.fade_delay = title_default.fade_delay;
12830 titlemessage_default.post_delay = title_default.post_delay;
12831 titlemessage_default.auto_delay = title_default.auto_delay;
12832 titlemessage_default.auto_delay_unit = title_default.auto_delay_unit;
12834 // special case: initialize "ARG_DEFAULT" values in static default config
12835 // (e.g., init "titlemessage_1.fade_mode" from "[titlemessage].fade_mode")
12836 for (i = 0; i < MAX_NUM_TITLE_MESSAGES; i++)
12838 titlescreen_initial_first[i] = titlescreen_initial_first_default;
12839 titlescreen_first[i] = titlescreen_first_default;
12840 titlemessage_initial_first[i] = titlemessage_initial_first_default;
12841 titlemessage_first[i] = titlemessage_first_default;
12843 titlescreen_initial[i] = titlescreen_initial_default;
12844 titlescreen[i] = titlescreen_default;
12845 titlemessage_initial[i] = titlemessage_initial_default;
12846 titlemessage[i] = titlemessage_default;
12849 // special case: initialize "ARG_DEFAULT" values in static default config
12850 // (eg, init "menu.enter_screen.SCORES.xyz" from "menu.enter_screen.xyz")
12851 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
12853 if (i == GFX_SPECIAL_ARG_TITLE) // title values already initialized
12856 menu.enter_screen[i] = menu.enter_screen[GFX_SPECIAL_ARG_DEFAULT];
12857 menu.leave_screen[i] = menu.leave_screen[GFX_SPECIAL_ARG_DEFAULT];
12858 menu.next_screen[i] = menu.next_screen[GFX_SPECIAL_ARG_DEFAULT];
12861 // special case: initialize "ARG_DEFAULT" values in static default config
12862 // (eg, init "viewport.door_1.MAIN.xyz" from "viewport.door_1.xyz")
12863 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
12865 viewport.window[i] = viewport.window[GFX_SPECIAL_ARG_DEFAULT];
12866 viewport.playfield[i] = viewport.playfield[GFX_SPECIAL_ARG_DEFAULT];
12867 viewport.door_1[i] = viewport.door_1[GFX_SPECIAL_ARG_DEFAULT];
12869 if (i == GFX_SPECIAL_ARG_EDITOR) // editor values already initialized
12872 viewport.door_2[i] = viewport.door_2[GFX_SPECIAL_ARG_DEFAULT];
12876 static void InitMenuDesignSettings_SpecialPostProcessing(void)
12880 struct XY *dst, *src;
12882 game_buttons_xy[] =
12884 { &game.button.save, &game.button.stop },
12885 { &game.button.pause2, &game.button.pause },
12886 { &game.button.load, &game.button.play },
12887 { &game.button.undo, &game.button.stop },
12888 { &game.button.redo, &game.button.play },
12894 // special case: initialize later added SETUP list size from LEVELS value
12895 if (menu.list_size[GAME_MODE_SETUP] == -1)
12896 menu.list_size[GAME_MODE_SETUP] = menu.list_size[GAME_MODE_LEVELS];
12898 // set default position for snapshot buttons to stop/pause/play buttons
12899 for (i = 0; game_buttons_xy[i].dst != NULL; i++)
12900 if ((*game_buttons_xy[i].dst).x == -1 &&
12901 (*game_buttons_xy[i].dst).y == -1)
12902 *game_buttons_xy[i].dst = *game_buttons_xy[i].src;
12904 // --------------------------------------------------------------------------
12905 // dynamic viewports (including playfield margins, borders and alignments)
12906 // --------------------------------------------------------------------------
12908 // dynamic viewports currently only supported for landscape mode
12909 int display_width = MAX(video.display_width, video.display_height);
12910 int display_height = MIN(video.display_width, video.display_height);
12912 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
12914 struct RectWithBorder *vp_window = &viewport.window[i];
12915 struct RectWithBorder *vp_playfield = &viewport.playfield[i];
12916 struct RectWithBorder *vp_door_1 = &viewport.door_1[i];
12917 struct RectWithBorder *vp_door_2 = &viewport.door_2[i];
12918 boolean dynamic_window_width = (vp_window->min_width != -1);
12919 boolean dynamic_window_height = (vp_window->min_height != -1);
12920 boolean dynamic_playfield_width = (vp_playfield->min_width != -1);
12921 boolean dynamic_playfield_height = (vp_playfield->min_height != -1);
12923 // adjust window size if min/max width/height is specified
12925 if (vp_window->min_width != -1)
12927 int window_width = display_width;
12929 // when using static window height, use aspect ratio of display
12930 if (vp_window->min_height == -1)
12931 window_width = vp_window->height * display_width / display_height;
12933 vp_window->width = MAX(vp_window->min_width, window_width);
12936 if (vp_window->min_height != -1)
12938 int window_height = display_height;
12940 // when using static window width, use aspect ratio of display
12941 if (vp_window->min_width == -1)
12942 window_height = vp_window->width * display_height / display_width;
12944 vp_window->height = MAX(vp_window->min_height, window_height);
12947 if (vp_window->max_width != -1)
12948 vp_window->width = MIN(vp_window->width, vp_window->max_width);
12950 if (vp_window->max_height != -1)
12951 vp_window->height = MIN(vp_window->height, vp_window->max_height);
12953 int playfield_width = vp_window->width;
12954 int playfield_height = vp_window->height;
12956 // adjust playfield size and position according to specified margins
12958 playfield_width -= vp_playfield->margin_left;
12959 playfield_width -= vp_playfield->margin_right;
12961 playfield_height -= vp_playfield->margin_top;
12962 playfield_height -= vp_playfield->margin_bottom;
12964 // adjust playfield size if min/max width/height is specified
12966 if (vp_playfield->min_width != -1)
12967 vp_playfield->width = MAX(vp_playfield->min_width, playfield_width);
12969 if (vp_playfield->min_height != -1)
12970 vp_playfield->height = MAX(vp_playfield->min_height, playfield_height);
12972 if (vp_playfield->max_width != -1)
12973 vp_playfield->width = MIN(vp_playfield->width, vp_playfield->max_width);
12975 if (vp_playfield->max_height != -1)
12976 vp_playfield->height = MIN(vp_playfield->height, vp_playfield->max_height);
12978 // adjust playfield position according to specified alignment
12980 if (vp_playfield->align == ALIGN_LEFT || vp_playfield->x > 0)
12981 vp_playfield->x = ALIGNED_VP_XPOS(vp_playfield);
12982 else if (vp_playfield->align == ALIGN_CENTER)
12983 vp_playfield->x = playfield_width / 2 - vp_playfield->width / 2;
12984 else if (vp_playfield->align == ALIGN_RIGHT)
12985 vp_playfield->x += playfield_width - vp_playfield->width;
12987 if (vp_playfield->valign == VALIGN_TOP || vp_playfield->y > 0)
12988 vp_playfield->y = ALIGNED_VP_YPOS(vp_playfield);
12989 else if (vp_playfield->valign == VALIGN_MIDDLE)
12990 vp_playfield->y = playfield_height / 2 - vp_playfield->height / 2;
12991 else if (vp_playfield->valign == VALIGN_BOTTOM)
12992 vp_playfield->y += playfield_height - vp_playfield->height;
12994 vp_playfield->x += vp_playfield->margin_left;
12995 vp_playfield->y += vp_playfield->margin_top;
12997 // adjust individual playfield borders if only default border is specified
12999 if (vp_playfield->border_left == -1)
13000 vp_playfield->border_left = vp_playfield->border_size;
13001 if (vp_playfield->border_right == -1)
13002 vp_playfield->border_right = vp_playfield->border_size;
13003 if (vp_playfield->border_top == -1)
13004 vp_playfield->border_top = vp_playfield->border_size;
13005 if (vp_playfield->border_bottom == -1)
13006 vp_playfield->border_bottom = vp_playfield->border_size;
13008 // set dynamic playfield borders if borders are specified as undefined
13009 // (but only if window size was dynamic and playfield size was static)
13011 if (dynamic_window_width && !dynamic_playfield_width)
13013 if (vp_playfield->border_left == -1)
13015 vp_playfield->border_left = (vp_playfield->x -
13016 vp_playfield->margin_left);
13017 vp_playfield->x -= vp_playfield->border_left;
13018 vp_playfield->width += vp_playfield->border_left;
13021 if (vp_playfield->border_right == -1)
13023 vp_playfield->border_right = (vp_window->width -
13025 vp_playfield->width -
13026 vp_playfield->margin_right);
13027 vp_playfield->width += vp_playfield->border_right;
13031 if (dynamic_window_height && !dynamic_playfield_height)
13033 if (vp_playfield->border_top == -1)
13035 vp_playfield->border_top = (vp_playfield->y -
13036 vp_playfield->margin_top);
13037 vp_playfield->y -= vp_playfield->border_top;
13038 vp_playfield->height += vp_playfield->border_top;
13041 if (vp_playfield->border_bottom == -1)
13043 vp_playfield->border_bottom = (vp_window->height -
13045 vp_playfield->height -
13046 vp_playfield->margin_bottom);
13047 vp_playfield->height += vp_playfield->border_bottom;
13051 // adjust playfield size to be a multiple of a defined alignment tile size
13053 int align_size = vp_playfield->align_size;
13054 int playfield_xtiles = vp_playfield->width / align_size;
13055 int playfield_ytiles = vp_playfield->height / align_size;
13056 int playfield_width_corrected = playfield_xtiles * align_size;
13057 int playfield_height_corrected = playfield_ytiles * align_size;
13058 boolean is_playfield_mode = (i == GFX_SPECIAL_ARG_PLAYING ||
13059 i == GFX_SPECIAL_ARG_EDITOR);
13061 if (is_playfield_mode &&
13062 dynamic_playfield_width &&
13063 vp_playfield->width != playfield_width_corrected)
13065 int playfield_xdiff = vp_playfield->width - playfield_width_corrected;
13067 vp_playfield->width = playfield_width_corrected;
13069 if (vp_playfield->align == ALIGN_LEFT)
13071 vp_playfield->border_left += playfield_xdiff;
13073 else if (vp_playfield->align == ALIGN_RIGHT)
13075 vp_playfield->border_right += playfield_xdiff;
13077 else if (vp_playfield->align == ALIGN_CENTER)
13079 int border_left_diff = playfield_xdiff / 2;
13080 int border_right_diff = playfield_xdiff - border_left_diff;
13082 vp_playfield->border_left += border_left_diff;
13083 vp_playfield->border_right += border_right_diff;
13087 if (is_playfield_mode &&
13088 dynamic_playfield_height &&
13089 vp_playfield->height != playfield_height_corrected)
13091 int playfield_ydiff = vp_playfield->height - playfield_height_corrected;
13093 vp_playfield->height = playfield_height_corrected;
13095 if (vp_playfield->valign == VALIGN_TOP)
13097 vp_playfield->border_top += playfield_ydiff;
13099 else if (vp_playfield->align == VALIGN_BOTTOM)
13101 vp_playfield->border_right += playfield_ydiff;
13103 else if (vp_playfield->align == VALIGN_MIDDLE)
13105 int border_top_diff = playfield_ydiff / 2;
13106 int border_bottom_diff = playfield_ydiff - border_top_diff;
13108 vp_playfield->border_top += border_top_diff;
13109 vp_playfield->border_bottom += border_bottom_diff;
13113 // adjust door positions according to specified alignment
13115 for (j = 0; j < 2; j++)
13117 struct RectWithBorder *vp_door = (j == 0 ? vp_door_1 : vp_door_2);
13119 if (vp_door->align == ALIGN_LEFT || vp_door->x > 0)
13120 vp_door->x = ALIGNED_VP_XPOS(vp_door);
13121 else if (vp_door->align == ALIGN_CENTER)
13122 vp_door->x = vp_window->width / 2 - vp_door->width / 2;
13123 else if (vp_door->align == ALIGN_RIGHT)
13124 vp_door->x += vp_window->width - vp_door->width;
13126 if (vp_door->valign == VALIGN_TOP || vp_door->y > 0)
13127 vp_door->y = ALIGNED_VP_YPOS(vp_door);
13128 else if (vp_door->valign == VALIGN_MIDDLE)
13129 vp_door->y = vp_window->height / 2 - vp_door->height / 2;
13130 else if (vp_door->valign == VALIGN_BOTTOM)
13131 vp_door->y += vp_window->height - vp_door->height;
13136 static void InitMenuDesignSettings_SpecialPostProcessing_AfterGraphics(void)
13140 struct XYTileSize *dst, *src;
13143 editor_buttons_xy[] =
13146 &editor.button.element_left, &editor.palette.element_left,
13147 IMG_GFX_EDITOR_BUTTON_ELEMENT_LEFT
13150 &editor.button.element_middle, &editor.palette.element_middle,
13151 IMG_GFX_EDITOR_BUTTON_ELEMENT_MIDDLE
13154 &editor.button.element_right, &editor.palette.element_right,
13155 IMG_GFX_EDITOR_BUTTON_ELEMENT_RIGHT
13162 // set default position for element buttons to element graphics
13163 for (i = 0; editor_buttons_xy[i].dst != NULL; i++)
13165 if ((*editor_buttons_xy[i].dst).x == -1 &&
13166 (*editor_buttons_xy[i].dst).y == -1)
13168 struct GraphicInfo *gd = &graphic_info[editor_buttons_xy[i].graphic];
13170 gd->width = gd->height = editor_buttons_xy[i].src->tile_size;
13172 *editor_buttons_xy[i].dst = *editor_buttons_xy[i].src;
13176 // adjust editor palette rows and columns if specified to be dynamic
13178 if (editor.palette.cols == -1)
13180 int vp_width = viewport.playfield[GFX_SPECIAL_ARG_EDITOR].width;
13181 int bt_width = graphic_info[IMG_EDITOR_PALETTE_BUTTON].width;
13182 int sc_width = graphic_info[IMG_EDITOR_PALETTE_SCROLLBAR].width;
13184 editor.palette.cols = (vp_width - sc_width) / bt_width;
13186 if (editor.palette.x == -1)
13188 int palette_width = editor.palette.cols * bt_width + sc_width;
13190 editor.palette.x = (vp_width - palette_width) / 2;
13194 if (editor.palette.rows == -1)
13196 int vp_height = viewport.playfield[GFX_SPECIAL_ARG_EDITOR].height;
13197 int bt_height = graphic_info[IMG_EDITOR_PALETTE_BUTTON].height;
13198 int tx_height = getFontHeight(FONT_TEXT_2);
13200 editor.palette.rows = (vp_height - tx_height) / bt_height;
13202 if (editor.palette.y == -1)
13204 int palette_height = editor.palette.rows * bt_height + tx_height;
13206 editor.palette.y = (vp_height - palette_height) / 2;
13211 static void InitMenuDesignSettings_PreviewPlayers_Ext(SetupFileHash *hash,
13212 boolean initialize)
13214 // special case: check if network and preview player positions are redefined,
13215 // to compare this later against the main menu level preview being redefined
13216 struct TokenIntPtrInfo menu_config_players[] =
13218 { "main.network_players.x", &menu.main.network_players.redefined },
13219 { "main.network_players.y", &menu.main.network_players.redefined },
13220 { "main.preview_players.x", &menu.main.preview_players.redefined },
13221 { "main.preview_players.y", &menu.main.preview_players.redefined },
13222 { "preview.x", &preview.redefined },
13223 { "preview.y", &preview.redefined }
13229 for (i = 0; i < ARRAY_SIZE(menu_config_players); i++)
13230 *menu_config_players[i].value = FALSE;
13234 for (i = 0; i < ARRAY_SIZE(menu_config_players); i++)
13235 if (getHashEntry(hash, menu_config_players[i].token) != NULL)
13236 *menu_config_players[i].value = TRUE;
13240 static void InitMenuDesignSettings_PreviewPlayers(void)
13242 InitMenuDesignSettings_PreviewPlayers_Ext(NULL, TRUE);
13245 static void InitMenuDesignSettings_PreviewPlayers_FromHash(SetupFileHash *hash)
13247 InitMenuDesignSettings_PreviewPlayers_Ext(hash, FALSE);
13250 static void LoadMenuDesignSettingsFromFilename(char *filename)
13252 static struct TitleFadingInfo tfi;
13253 static struct TitleMessageInfo tmi;
13254 static struct TokenInfo title_tokens[] =
13256 { TYPE_INTEGER, &tfi.fade_mode, ".fade_mode" },
13257 { TYPE_INTEGER, &tfi.fade_delay, ".fade_delay" },
13258 { TYPE_INTEGER, &tfi.post_delay, ".post_delay" },
13259 { TYPE_INTEGER, &tfi.auto_delay, ".auto_delay" },
13260 { TYPE_INTEGER, &tfi.auto_delay_unit, ".auto_delay_unit" },
13264 static struct TokenInfo titlemessage_tokens[] =
13266 { TYPE_INTEGER, &tmi.x, ".x" },
13267 { TYPE_INTEGER, &tmi.y, ".y" },
13268 { TYPE_INTEGER, &tmi.width, ".width" },
13269 { TYPE_INTEGER, &tmi.height, ".height" },
13270 { TYPE_INTEGER, &tmi.chars, ".chars" },
13271 { TYPE_INTEGER, &tmi.lines, ".lines" },
13272 { TYPE_INTEGER, &tmi.align, ".align" },
13273 { TYPE_INTEGER, &tmi.valign, ".valign" },
13274 { TYPE_INTEGER, &tmi.font, ".font" },
13275 { TYPE_BOOLEAN, &tmi.autowrap, ".autowrap" },
13276 { TYPE_BOOLEAN, &tmi.centered, ".centered" },
13277 { TYPE_BOOLEAN, &tmi.parse_comments, ".parse_comments" },
13278 { TYPE_INTEGER, &tmi.sort_priority, ".sort_priority" },
13279 { TYPE_INTEGER, &tmi.fade_mode, ".fade_mode" },
13280 { TYPE_INTEGER, &tmi.fade_delay, ".fade_delay" },
13281 { TYPE_INTEGER, &tmi.post_delay, ".post_delay" },
13282 { TYPE_INTEGER, &tmi.auto_delay, ".auto_delay" },
13283 { TYPE_INTEGER, &tmi.auto_delay_unit, ".auto_delay_unit" },
13289 struct TitleFadingInfo *info;
13294 // initialize first titles from "enter screen" definitions, if defined
13295 { &title_initial_first_default, "menu.enter_screen.TITLE" },
13296 { &title_first_default, "menu.enter_screen.TITLE" },
13298 // initialize title screens from "next screen" definitions, if defined
13299 { &title_initial_default, "menu.next_screen.TITLE" },
13300 { &title_default, "menu.next_screen.TITLE" },
13306 struct TitleMessageInfo *array;
13309 titlemessage_arrays[] =
13311 // initialize first titles from "enter screen" definitions, if defined
13312 { titlescreen_initial_first, "menu.enter_screen.TITLE" },
13313 { titlescreen_first, "menu.enter_screen.TITLE" },
13314 { titlemessage_initial_first, "menu.enter_screen.TITLE" },
13315 { titlemessage_first, "menu.enter_screen.TITLE" },
13317 // initialize titles from "next screen" definitions, if defined
13318 { titlescreen_initial, "menu.next_screen.TITLE" },
13319 { titlescreen, "menu.next_screen.TITLE" },
13320 { titlemessage_initial, "menu.next_screen.TITLE" },
13321 { titlemessage, "menu.next_screen.TITLE" },
13323 // overwrite titles with title definitions, if defined
13324 { titlescreen_initial_first, "[title_initial]" },
13325 { titlescreen_first, "[title]" },
13326 { titlemessage_initial_first, "[title_initial]" },
13327 { titlemessage_first, "[title]" },
13329 { titlescreen_initial, "[title_initial]" },
13330 { titlescreen, "[title]" },
13331 { titlemessage_initial, "[title_initial]" },
13332 { titlemessage, "[title]" },
13334 // overwrite titles with title screen/message definitions, if defined
13335 { titlescreen_initial_first, "[titlescreen_initial]" },
13336 { titlescreen_first, "[titlescreen]" },
13337 { titlemessage_initial_first, "[titlemessage_initial]" },
13338 { titlemessage_first, "[titlemessage]" },
13340 { titlescreen_initial, "[titlescreen_initial]" },
13341 { titlescreen, "[titlescreen]" },
13342 { titlemessage_initial, "[titlemessage_initial]" },
13343 { titlemessage, "[titlemessage]" },
13347 SetupFileHash *setup_file_hash;
13350 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
13353 // the following initializes hierarchical values from dynamic configuration
13355 // special case: initialize with default values that may be overwritten
13356 // (e.g., init "menu.draw_xoffset.INFO" from "menu.draw_xoffset")
13357 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
13359 struct TokenIntPtrInfo menu_config[] =
13361 { "menu.draw_xoffset", &menu.draw_xoffset[i] },
13362 { "menu.draw_yoffset", &menu.draw_yoffset[i] },
13363 { "menu.list_size", &menu.list_size[i] }
13366 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
13368 char *token = menu_config[j].token;
13369 char *value = getHashEntry(setup_file_hash, token);
13372 *menu_config[j].value = get_integer_from_string(value);
13376 // special case: initialize with default values that may be overwritten
13377 // (eg, init "menu.draw_xoffset.INFO[XXX]" from "menu.draw_xoffset.INFO")
13378 for (i = 0; i < NUM_SPECIAL_GFX_INFO_ARGS; i++)
13380 struct TokenIntPtrInfo menu_config[] =
13382 { "menu.draw_xoffset.INFO", &menu.draw_xoffset_info[i] },
13383 { "menu.draw_yoffset.INFO", &menu.draw_yoffset_info[i] },
13384 { "menu.list_size.INFO", &menu.list_size_info[i] },
13385 { "menu.list_entry_size.INFO", &menu.list_entry_size_info[i] },
13386 { "menu.tile_size.INFO", &menu.tile_size_info[i] }
13389 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
13391 char *token = menu_config[j].token;
13392 char *value = getHashEntry(setup_file_hash, token);
13395 *menu_config[j].value = get_integer_from_string(value);
13399 // special case: initialize with default values that may be overwritten
13400 // (eg, init "menu.draw_xoffset.SETUP[XXX]" from "menu.draw_xoffset.SETUP")
13401 for (i = 0; i < NUM_SPECIAL_GFX_SETUP_ARGS; i++)
13403 struct TokenIntPtrInfo menu_config[] =
13405 { "menu.draw_xoffset.SETUP", &menu.draw_xoffset_setup[i] },
13406 { "menu.draw_yoffset.SETUP", &menu.draw_yoffset_setup[i] }
13409 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
13411 char *token = menu_config[j].token;
13412 char *value = getHashEntry(setup_file_hash, token);
13415 *menu_config[j].value = get_integer_from_string(value);
13419 // special case: initialize with default values that may be overwritten
13420 // (eg, init "menu.line_spacing.INFO[XXX]" from "menu.line_spacing.INFO")
13421 for (i = 0; i < NUM_SPECIAL_GFX_INFO_ARGS; i++)
13423 struct TokenIntPtrInfo menu_config[] =
13425 { "menu.left_spacing.INFO", &menu.left_spacing_info[i] },
13426 { "menu.middle_spacing.INFO", &menu.middle_spacing_info[i] },
13427 { "menu.right_spacing.INFO", &menu.right_spacing_info[i] },
13428 { "menu.top_spacing.INFO", &menu.top_spacing_info[i] },
13429 { "menu.bottom_spacing.INFO", &menu.bottom_spacing_info[i] },
13430 { "menu.paragraph_spacing.INFO", &menu.paragraph_spacing_info[i] },
13431 { "menu.headline1_spacing.INFO", &menu.headline1_spacing_info[i] },
13432 { "menu.headline2_spacing.INFO", &menu.headline2_spacing_info[i] },
13433 { "menu.line_spacing.INFO", &menu.line_spacing_info[i] },
13434 { "menu.extra_spacing.INFO", &menu.extra_spacing_info[i] },
13437 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
13439 char *token = menu_config[j].token;
13440 char *value = getHashEntry(setup_file_hash, token);
13443 *menu_config[j].value = get_integer_from_string(value);
13447 // special case: initialize with default values that may be overwritten
13448 // (eg, init "menu.enter_screen.SCORES.xyz" from "menu.enter_screen.xyz")
13449 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
13451 struct TokenIntPtrInfo menu_config[] =
13453 { "menu.enter_screen.fade_mode", &menu.enter_screen[i].fade_mode },
13454 { "menu.enter_screen.fade_delay", &menu.enter_screen[i].fade_delay },
13455 { "menu.enter_screen.post_delay", &menu.enter_screen[i].post_delay },
13456 { "menu.leave_screen.fade_mode", &menu.leave_screen[i].fade_mode },
13457 { "menu.leave_screen.fade_delay", &menu.leave_screen[i].fade_delay },
13458 { "menu.leave_screen.post_delay", &menu.leave_screen[i].post_delay },
13459 { "menu.next_screen.fade_mode", &menu.next_screen[i].fade_mode },
13460 { "menu.next_screen.fade_delay", &menu.next_screen[i].fade_delay },
13461 { "menu.next_screen.post_delay", &menu.next_screen[i].post_delay }
13464 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
13466 char *token = menu_config[j].token;
13467 char *value = getHashEntry(setup_file_hash, token);
13470 *menu_config[j].value = get_token_parameter_value(token, value);
13474 // special case: initialize with default values that may be overwritten
13475 // (eg, init "viewport.door_1.MAIN.xyz" from "viewport.door_1.xyz")
13476 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
13480 char *token_prefix;
13481 struct RectWithBorder *struct_ptr;
13485 { "viewport.window", &viewport.window[i] },
13486 { "viewport.playfield", &viewport.playfield[i] },
13487 { "viewport.door_1", &viewport.door_1[i] },
13488 { "viewport.door_2", &viewport.door_2[i] }
13491 for (j = 0; j < ARRAY_SIZE(vp_struct); j++)
13493 struct TokenIntPtrInfo vp_config[] =
13495 { ".x", &vp_struct[j].struct_ptr->x },
13496 { ".y", &vp_struct[j].struct_ptr->y },
13497 { ".width", &vp_struct[j].struct_ptr->width },
13498 { ".height", &vp_struct[j].struct_ptr->height },
13499 { ".min_width", &vp_struct[j].struct_ptr->min_width },
13500 { ".min_height", &vp_struct[j].struct_ptr->min_height },
13501 { ".max_width", &vp_struct[j].struct_ptr->max_width },
13502 { ".max_height", &vp_struct[j].struct_ptr->max_height },
13503 { ".margin_left", &vp_struct[j].struct_ptr->margin_left },
13504 { ".margin_right", &vp_struct[j].struct_ptr->margin_right },
13505 { ".margin_top", &vp_struct[j].struct_ptr->margin_top },
13506 { ".margin_bottom", &vp_struct[j].struct_ptr->margin_bottom },
13507 { ".border_left", &vp_struct[j].struct_ptr->border_left },
13508 { ".border_right", &vp_struct[j].struct_ptr->border_right },
13509 { ".border_top", &vp_struct[j].struct_ptr->border_top },
13510 { ".border_bottom", &vp_struct[j].struct_ptr->border_bottom },
13511 { ".border_size", &vp_struct[j].struct_ptr->border_size },
13512 { ".align_size", &vp_struct[j].struct_ptr->align_size },
13513 { ".align", &vp_struct[j].struct_ptr->align },
13514 { ".valign", &vp_struct[j].struct_ptr->valign }
13517 for (k = 0; k < ARRAY_SIZE(vp_config); k++)
13519 char *token = getStringCat2(vp_struct[j].token_prefix,
13520 vp_config[k].token);
13521 char *value = getHashEntry(setup_file_hash, token);
13524 *vp_config[k].value = get_token_parameter_value(token, value);
13531 // special case: initialize with default values that may be overwritten
13532 // (e.g., init "[title].fade_mode" from "menu.next_screen.TITLE.fade_mode")
13533 for (i = 0; title_info[i].info != NULL; i++)
13535 struct TitleFadingInfo *info = title_info[i].info;
13536 char *base_token = title_info[i].text;
13538 for (j = 0; title_tokens[j].type != -1; j++)
13540 char *token = getStringCat2(base_token, title_tokens[j].text);
13541 char *value = getHashEntry(setup_file_hash, token);
13545 int parameter_value = get_token_parameter_value(token, value);
13549 *(int *)title_tokens[j].value = (int)parameter_value;
13558 // special case: initialize with default values that may be overwritten
13559 // (e.g., init "titlemessage_1.fade_mode" from "[titlemessage].fade_mode")
13560 for (i = 0; titlemessage_arrays[i].array != NULL; i++)
13562 struct TitleMessageInfo *array = titlemessage_arrays[i].array;
13563 char *base_token = titlemessage_arrays[i].text;
13565 for (j = 0; titlemessage_tokens[j].type != -1; j++)
13567 char *token = getStringCat2(base_token, titlemessage_tokens[j].text);
13568 char *value = getHashEntry(setup_file_hash, token);
13572 int parameter_value = get_token_parameter_value(token, value);
13574 for (k = 0; k < MAX_NUM_TITLE_MESSAGES; k++)
13578 if (titlemessage_tokens[j].type == TYPE_INTEGER)
13579 *(int *)titlemessage_tokens[j].value = (int)parameter_value;
13581 *(boolean *)titlemessage_tokens[j].value = (boolean)parameter_value;
13591 // read (and overwrite with) values that may be specified in config file
13592 InitMenuDesignSettings_FromHash(setup_file_hash, TRUE);
13594 // special case: check if network and preview player positions are redefined
13595 InitMenuDesignSettings_PreviewPlayers_FromHash(setup_file_hash);
13597 freeSetupFileHash(setup_file_hash);
13600 void LoadMenuDesignSettings(void)
13602 char *filename_base = UNDEFINED_FILENAME, *filename_local;
13604 InitMenuDesignSettings_Static();
13605 InitMenuDesignSettings_SpecialPreProcessing();
13606 InitMenuDesignSettings_PreviewPlayers();
13608 if (!GFX_OVERRIDE_ARTWORK(ARTWORK_TYPE_GRAPHICS))
13610 // first look for special settings configured in level series config
13611 filename_base = getCustomArtworkLevelConfigFilename(ARTWORK_TYPE_GRAPHICS);
13613 if (fileExists(filename_base))
13614 LoadMenuDesignSettingsFromFilename(filename_base);
13617 filename_local = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
13619 if (filename_local != NULL && !strEqual(filename_base, filename_local))
13620 LoadMenuDesignSettingsFromFilename(filename_local);
13622 InitMenuDesignSettings_SpecialPostProcessing();
13625 void LoadMenuDesignSettings_AfterGraphics(void)
13627 InitMenuDesignSettings_SpecialPostProcessing_AfterGraphics();
13630 void InitSoundSettings_FromHash(SetupFileHash *setup_file_hash,
13631 boolean ignore_defaults)
13635 for (i = 0; sound_config_vars[i].token != NULL; i++)
13637 char *value = getHashEntry(setup_file_hash, sound_config_vars[i].token);
13639 // (ignore definitions set to "[DEFAULT]" which are already initialized)
13640 if (ignore_defaults && strEqual(value, ARG_DEFAULT))
13644 *sound_config_vars[i].value =
13645 get_token_parameter_value(sound_config_vars[i].token, value);
13649 void InitSoundSettings_Static(void)
13651 // always start with reliable default values from static default config
13652 InitSoundSettings_FromHash(sound_config_hash, FALSE);
13655 static void LoadSoundSettingsFromFilename(char *filename)
13657 SetupFileHash *setup_file_hash;
13659 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
13662 // read (and overwrite with) values that may be specified in config file
13663 InitSoundSettings_FromHash(setup_file_hash, TRUE);
13665 freeSetupFileHash(setup_file_hash);
13668 void LoadSoundSettings(void)
13670 char *filename_base = UNDEFINED_FILENAME, *filename_local;
13672 InitSoundSettings_Static();
13674 if (!GFX_OVERRIDE_ARTWORK(ARTWORK_TYPE_SOUNDS))
13676 // first look for special settings configured in level series config
13677 filename_base = getCustomArtworkLevelConfigFilename(ARTWORK_TYPE_SOUNDS);
13679 if (fileExists(filename_base))
13680 LoadSoundSettingsFromFilename(filename_base);
13683 filename_local = getCustomArtworkConfigFilename(ARTWORK_TYPE_SOUNDS);
13685 if (filename_local != NULL && !strEqual(filename_base, filename_local))
13686 LoadSoundSettingsFromFilename(filename_local);
13689 void LoadUserDefinedEditorElementList(int **elements, int *num_elements)
13691 char *filename = getEditorSetupFilename();
13692 SetupFileList *setup_file_list, *list;
13693 SetupFileHash *element_hash;
13694 int num_unknown_tokens = 0;
13697 if ((setup_file_list = loadSetupFileList(filename)) == NULL)
13700 element_hash = newSetupFileHash();
13702 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
13703 setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
13705 // determined size may be larger than needed (due to unknown elements)
13707 for (list = setup_file_list; list != NULL; list = list->next)
13710 // add space for up to 3 more elements for padding that may be needed
13711 *num_elements += 3;
13713 // free memory for old list of elements, if needed
13714 checked_free(*elements);
13716 // allocate memory for new list of elements
13717 *elements = checked_malloc(*num_elements * sizeof(int));
13720 for (list = setup_file_list; list != NULL; list = list->next)
13722 char *value = getHashEntry(element_hash, list->token);
13724 if (value == NULL) // try to find obsolete token mapping
13726 char *mapped_token = get_mapped_token(list->token);
13728 if (mapped_token != NULL)
13730 value = getHashEntry(element_hash, mapped_token);
13732 free(mapped_token);
13738 (*elements)[(*num_elements)++] = atoi(value);
13742 if (num_unknown_tokens == 0)
13745 Warn("unknown token(s) found in config file:");
13746 Warn("- config file: '%s'", filename);
13748 num_unknown_tokens++;
13751 Warn("- token: '%s'", list->token);
13755 if (num_unknown_tokens > 0)
13758 while (*num_elements % 4) // pad with empty elements, if needed
13759 (*elements)[(*num_elements)++] = EL_EMPTY;
13761 freeSetupFileList(setup_file_list);
13762 freeSetupFileHash(element_hash);
13765 for (i = 0; i < *num_elements; i++)
13766 Debug("editor", "element '%s' [%d]\n",
13767 element_info[(*elements)[i]].token_name, (*elements)[i]);
13771 static struct MusicFileInfo *get_music_file_info_ext(char *basename, int music,
13774 SetupFileHash *setup_file_hash = NULL;
13775 struct MusicFileInfo tmp_music_file_info, *new_music_file_info;
13776 char *filename_music, *filename_prefix, *filename_info;
13782 token_to_value_ptr[] =
13784 { "title_header", &tmp_music_file_info.title_header },
13785 { "artist_header", &tmp_music_file_info.artist_header },
13786 { "album_header", &tmp_music_file_info.album_header },
13787 { "year_header", &tmp_music_file_info.year_header },
13788 { "played_header", &tmp_music_file_info.played_header },
13790 { "title", &tmp_music_file_info.title },
13791 { "artist", &tmp_music_file_info.artist },
13792 { "album", &tmp_music_file_info.album },
13793 { "year", &tmp_music_file_info.year },
13794 { "played", &tmp_music_file_info.played },
13800 filename_music = (is_sound ? getCustomSoundFilename(basename) :
13801 getCustomMusicFilename(basename));
13803 if (filename_music == NULL)
13806 // ---------- try to replace file extension ----------
13808 filename_prefix = getStringCopy(filename_music);
13809 if (strrchr(filename_prefix, '.') != NULL)
13810 *strrchr(filename_prefix, '.') = '\0';
13811 filename_info = getStringCat2(filename_prefix, ".txt");
13813 if (fileExists(filename_info))
13814 setup_file_hash = loadSetupFileHash(filename_info);
13816 free(filename_prefix);
13817 free(filename_info);
13819 if (setup_file_hash == NULL)
13821 // ---------- try to add file extension ----------
13823 filename_prefix = getStringCopy(filename_music);
13824 filename_info = getStringCat2(filename_prefix, ".txt");
13826 if (fileExists(filename_info))
13827 setup_file_hash = loadSetupFileHash(filename_info);
13829 free(filename_prefix);
13830 free(filename_info);
13833 if (setup_file_hash == NULL)
13836 // ---------- music file info found ----------
13838 clear_mem(&tmp_music_file_info, sizeof(struct MusicFileInfo));
13840 for (i = 0; token_to_value_ptr[i].token != NULL; i++)
13842 char *value = getHashEntry(setup_file_hash, token_to_value_ptr[i].token);
13844 *token_to_value_ptr[i].value_ptr =
13845 getStringCopy(value != NULL && *value != '\0' ? value : UNKNOWN_NAME);
13848 tmp_music_file_info.basename = getStringCopy(basename);
13849 tmp_music_file_info.music = music;
13850 tmp_music_file_info.is_sound = is_sound;
13852 new_music_file_info = checked_malloc(sizeof(struct MusicFileInfo));
13853 *new_music_file_info = tmp_music_file_info;
13855 return new_music_file_info;
13858 static struct MusicFileInfo *get_music_file_info(char *basename, int music)
13860 return get_music_file_info_ext(basename, music, FALSE);
13863 static struct MusicFileInfo *get_sound_file_info(char *basename, int sound)
13865 return get_music_file_info_ext(basename, sound, TRUE);
13868 static boolean music_info_listed_ext(struct MusicFileInfo *list,
13869 char *basename, boolean is_sound)
13871 for (; list != NULL; list = list->next)
13872 if (list->is_sound == is_sound && strEqual(list->basename, basename))
13878 static boolean music_info_listed(struct MusicFileInfo *list, char *basename)
13880 return music_info_listed_ext(list, basename, FALSE);
13883 static boolean sound_info_listed(struct MusicFileInfo *list, char *basename)
13885 return music_info_listed_ext(list, basename, TRUE);
13888 void LoadMusicInfo(void)
13890 int num_music_noconf = getMusicListSize_NoConf();
13891 int num_music = getMusicListSize();
13892 int num_sounds = getSoundListSize();
13893 struct FileInfo *music, *sound;
13894 struct MusicFileInfo *next, **new;
13898 while (music_file_info != NULL)
13900 next = music_file_info->next;
13902 checked_free(music_file_info->basename);
13904 checked_free(music_file_info->title_header);
13905 checked_free(music_file_info->artist_header);
13906 checked_free(music_file_info->album_header);
13907 checked_free(music_file_info->year_header);
13908 checked_free(music_file_info->played_header);
13910 checked_free(music_file_info->title);
13911 checked_free(music_file_info->artist);
13912 checked_free(music_file_info->album);
13913 checked_free(music_file_info->year);
13914 checked_free(music_file_info->played);
13916 free(music_file_info);
13918 music_file_info = next;
13921 new = &music_file_info;
13923 // get (configured or unconfigured) music file info for all levels
13924 for (i = leveldir_current->first_level;
13925 i <= leveldir_current->last_level; i++)
13929 if (levelset.music[i] != MUS_UNDEFINED)
13931 // get music file info for configured level music
13932 music_nr = levelset.music[i];
13934 else if (num_music_noconf > 0)
13936 // get music file info for unconfigured level music
13937 int level_pos = i - leveldir_current->first_level;
13939 music_nr = MAP_NOCONF_MUSIC(level_pos % num_music_noconf);
13946 char *basename = getMusicInfoEntryFilename(music_nr);
13948 if (basename == NULL)
13951 if (!music_info_listed(music_file_info, basename))
13953 *new = get_music_file_info(basename, music_nr);
13956 new = &(*new)->next;
13960 // get music file info for all remaining configured music files
13961 for (i = 0; i < num_music; i++)
13963 music = getMusicListEntry(i);
13965 if (music->filename == NULL)
13968 if (strEqual(music->filename, UNDEFINED_FILENAME))
13971 // a configured file may be not recognized as music
13972 if (!FileIsMusic(music->filename))
13975 if (!music_info_listed(music_file_info, music->filename))
13977 *new = get_music_file_info(music->filename, i);
13980 new = &(*new)->next;
13984 // get sound file info for all configured sound files
13985 for (i = 0; i < num_sounds; i++)
13987 sound = getSoundListEntry(i);
13989 if (sound->filename == NULL)
13992 if (strEqual(sound->filename, UNDEFINED_FILENAME))
13995 // a configured file may be not recognized as sound
13996 if (!FileIsSound(sound->filename))
13999 if (!sound_info_listed(music_file_info, sound->filename))
14001 *new = get_sound_file_info(sound->filename, i);
14003 new = &(*new)->next;
14007 // add pointers to previous list nodes
14009 struct MusicFileInfo *node = music_file_info;
14011 while (node != NULL)
14014 node->next->prev = node;
14020 static void add_helpanim_entry(int element, int action, int direction,
14021 int delay, int *num_list_entries)
14023 struct HelpAnimInfo *new_list_entry;
14024 (*num_list_entries)++;
14027 checked_realloc(helpanim_info,
14028 *num_list_entries * sizeof(struct HelpAnimInfo));
14029 new_list_entry = &helpanim_info[*num_list_entries - 1];
14031 new_list_entry->element = element;
14032 new_list_entry->action = action;
14033 new_list_entry->direction = direction;
14034 new_list_entry->delay = delay;
14037 static void print_unknown_token(char *filename, char *token, int token_nr)
14042 Warn("unknown token(s) found in config file:");
14043 Warn("- config file: '%s'", filename);
14046 Warn("- token: '%s'", token);
14049 static void print_unknown_token_end(int token_nr)
14055 void LoadHelpAnimInfo(void)
14057 char *filename = getHelpAnimFilename();
14058 SetupFileList *setup_file_list = NULL, *list;
14059 SetupFileHash *element_hash, *action_hash, *direction_hash;
14060 int num_list_entries = 0;
14061 int num_unknown_tokens = 0;
14064 if (fileExists(filename))
14065 setup_file_list = loadSetupFileList(filename);
14067 if (setup_file_list == NULL)
14069 // use reliable default values from static configuration
14070 SetupFileList *insert_ptr;
14072 insert_ptr = setup_file_list =
14073 newSetupFileList(helpanim_config[0].token,
14074 helpanim_config[0].value);
14076 for (i = 1; helpanim_config[i].token; i++)
14077 insert_ptr = addListEntry(insert_ptr,
14078 helpanim_config[i].token,
14079 helpanim_config[i].value);
14082 element_hash = newSetupFileHash();
14083 action_hash = newSetupFileHash();
14084 direction_hash = newSetupFileHash();
14086 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
14087 setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
14089 for (i = 0; i < NUM_ACTIONS; i++)
14090 setHashEntry(action_hash, element_action_info[i].suffix,
14091 i_to_a(element_action_info[i].value));
14093 // do not store direction index (bit) here, but direction value!
14094 for (i = 0; i < NUM_DIRECTIONS_FULL; i++)
14095 setHashEntry(direction_hash, element_direction_info[i].suffix,
14096 i_to_a(1 << element_direction_info[i].value));
14098 for (list = setup_file_list; list != NULL; list = list->next)
14100 char *element_token, *action_token, *direction_token;
14101 char *element_value, *action_value, *direction_value;
14102 int delay = atoi(list->value);
14104 if (strEqual(list->token, "end"))
14106 add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
14111 /* first try to break element into element/action/direction parts;
14112 if this does not work, also accept combined "element[.act][.dir]"
14113 elements (like "dynamite.active"), which are unique elements */
14115 if (strchr(list->token, '.') == NULL) // token contains no '.'
14117 element_value = getHashEntry(element_hash, list->token);
14118 if (element_value != NULL) // element found
14119 add_helpanim_entry(atoi(element_value), -1, -1, delay,
14120 &num_list_entries);
14123 // no further suffixes found -- this is not an element
14124 print_unknown_token(filename, list->token, num_unknown_tokens++);
14130 // token has format "<prefix>.<something>"
14132 action_token = strchr(list->token, '.'); // suffix may be action ...
14133 direction_token = action_token; // ... or direction
14135 element_token = getStringCopy(list->token);
14136 *strchr(element_token, '.') = '\0';
14138 element_value = getHashEntry(element_hash, element_token);
14140 if (element_value == NULL) // this is no element
14142 element_value = getHashEntry(element_hash, list->token);
14143 if (element_value != NULL) // combined element found
14144 add_helpanim_entry(atoi(element_value), -1, -1, delay,
14145 &num_list_entries);
14147 print_unknown_token(filename, list->token, num_unknown_tokens++);
14149 free(element_token);
14154 action_value = getHashEntry(action_hash, action_token);
14156 if (action_value != NULL) // action found
14158 add_helpanim_entry(atoi(element_value), atoi(action_value), -1, delay,
14159 &num_list_entries);
14161 free(element_token);
14166 direction_value = getHashEntry(direction_hash, direction_token);
14168 if (direction_value != NULL) // direction found
14170 add_helpanim_entry(atoi(element_value), -1, atoi(direction_value), delay,
14171 &num_list_entries);
14173 free(element_token);
14178 if (strchr(action_token + 1, '.') == NULL)
14180 // no further suffixes found -- this is not an action nor direction
14182 element_value = getHashEntry(element_hash, list->token);
14183 if (element_value != NULL) // combined element found
14184 add_helpanim_entry(atoi(element_value), -1, -1, delay,
14185 &num_list_entries);
14187 print_unknown_token(filename, list->token, num_unknown_tokens++);
14189 free(element_token);
14194 // token has format "<prefix>.<suffix>.<something>"
14196 direction_token = strchr(action_token + 1, '.');
14198 action_token = getStringCopy(action_token);
14199 *strchr(action_token + 1, '.') = '\0';
14201 action_value = getHashEntry(action_hash, action_token);
14203 if (action_value == NULL) // this is no action
14205 element_value = getHashEntry(element_hash, list->token);
14206 if (element_value != NULL) // combined element found
14207 add_helpanim_entry(atoi(element_value), -1, -1, delay,
14208 &num_list_entries);
14210 print_unknown_token(filename, list->token, num_unknown_tokens++);
14212 free(element_token);
14213 free(action_token);
14218 direction_value = getHashEntry(direction_hash, direction_token);
14220 if (direction_value != NULL) // direction found
14222 add_helpanim_entry(atoi(element_value), atoi(action_value),
14223 atoi(direction_value), delay, &num_list_entries);
14225 free(element_token);
14226 free(action_token);
14231 // this is no direction
14233 element_value = getHashEntry(element_hash, list->token);
14234 if (element_value != NULL) // combined element found
14235 add_helpanim_entry(atoi(element_value), -1, -1, delay,
14236 &num_list_entries);
14238 print_unknown_token(filename, list->token, num_unknown_tokens++);
14240 free(element_token);
14241 free(action_token);
14244 print_unknown_token_end(num_unknown_tokens);
14246 add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
14247 add_helpanim_entry(HELPANIM_LIST_END, -1, -1, -1, &num_list_entries);
14249 freeSetupFileList(setup_file_list);
14250 freeSetupFileHash(element_hash);
14251 freeSetupFileHash(action_hash);
14252 freeSetupFileHash(direction_hash);
14255 for (i = 0; i < num_list_entries; i++)
14256 Debug("files:LoadHelpAnimInfo", "'%s': %d, %d, %d => %d",
14257 EL_NAME(helpanim_info[i].element),
14258 helpanim_info[i].element,
14259 helpanim_info[i].action,
14260 helpanim_info[i].direction,
14261 helpanim_info[i].delay);
14265 void LoadHelpTextInfo(void)
14267 char *filename = getHelpTextFilename();
14270 if (helptext_info != NULL)
14272 freeSetupFileHash(helptext_info);
14273 helptext_info = NULL;
14276 if (fileExists(filename))
14277 helptext_info = loadSetupFileHash(filename);
14279 if (helptext_info == NULL)
14281 // use reliable default values from static configuration
14282 helptext_info = newSetupFileHash();
14284 for (i = 0; helptext_config[i].token; i++)
14285 setHashEntry(helptext_info,
14286 helptext_config[i].token,
14287 helptext_config[i].value);
14291 BEGIN_HASH_ITERATION(helptext_info, itr)
14293 Debug("files:LoadHelpTextInfo", "'%s' => '%s'",
14294 HASH_ITERATION_TOKEN(itr), HASH_ITERATION_VALUE(itr));
14296 END_HASH_ITERATION(hash, itr)
14301 // ----------------------------------------------------------------------------
14303 // ----------------------------------------------------------------------------
14305 #define MAX_NUM_CONVERT_LEVELS 1000
14307 void ConvertLevels(void)
14309 static LevelDirTree *convert_leveldir = NULL;
14310 static int convert_level_nr = -1;
14311 static int num_levels_handled = 0;
14312 static int num_levels_converted = 0;
14313 static boolean levels_failed[MAX_NUM_CONVERT_LEVELS];
14316 convert_leveldir = getTreeInfoFromIdentifier(leveldir_first,
14317 global.convert_leveldir);
14319 if (convert_leveldir == NULL)
14320 Fail("no such level identifier: '%s'", global.convert_leveldir);
14322 leveldir_current = convert_leveldir;
14324 if (global.convert_level_nr != -1)
14326 convert_leveldir->first_level = global.convert_level_nr;
14327 convert_leveldir->last_level = global.convert_level_nr;
14330 convert_level_nr = convert_leveldir->first_level;
14332 PrintLine("=", 79);
14333 Print("Converting levels\n");
14334 PrintLine("-", 79);
14335 Print("Level series identifier: '%s'\n", convert_leveldir->identifier);
14336 Print("Level series name: '%s'\n", convert_leveldir->name);
14337 Print("Level series author: '%s'\n", convert_leveldir->author);
14338 Print("Number of levels: %d\n", convert_leveldir->levels);
14339 PrintLine("=", 79);
14342 for (i = 0; i < MAX_NUM_CONVERT_LEVELS; i++)
14343 levels_failed[i] = FALSE;
14345 while (convert_level_nr <= convert_leveldir->last_level)
14347 char *level_filename;
14350 level_nr = convert_level_nr++;
14352 Print("Level %03d: ", level_nr);
14354 LoadLevel(level_nr);
14355 if (level.no_level_file || level.no_valid_file)
14357 Print("(no level)\n");
14361 Print("converting level ... ");
14364 // special case: conversion of some EMC levels as requested by ACME
14365 level.game_engine_type = GAME_ENGINE_TYPE_RND;
14368 level_filename = getDefaultLevelFilename(level_nr);
14369 new_level = !fileExists(level_filename);
14373 SaveLevel(level_nr);
14375 num_levels_converted++;
14377 Print("converted.\n");
14381 if (level_nr >= 0 && level_nr < MAX_NUM_CONVERT_LEVELS)
14382 levels_failed[level_nr] = TRUE;
14384 Print("NOT CONVERTED -- LEVEL ALREADY EXISTS.\n");
14387 num_levels_handled++;
14391 PrintLine("=", 79);
14392 Print("Number of levels handled: %d\n", num_levels_handled);
14393 Print("Number of levels converted: %d (%d%%)\n", num_levels_converted,
14394 (num_levels_handled ?
14395 num_levels_converted * 100 / num_levels_handled : 0));
14396 PrintLine("-", 79);
14397 Print("Summary (for automatic parsing by scripts):\n");
14398 Print("LEVELDIR '%s', CONVERTED %d/%d (%d%%)",
14399 convert_leveldir->identifier, num_levels_converted,
14400 num_levels_handled,
14401 (num_levels_handled ?
14402 num_levels_converted * 100 / num_levels_handled : 0));
14404 if (num_levels_handled != num_levels_converted)
14406 Print(", FAILED:");
14407 for (i = 0; i < MAX_NUM_CONVERT_LEVELS; i++)
14408 if (levels_failed[i])
14413 PrintLine("=", 79);
14415 CloseAllAndExit(0);
14419 // ----------------------------------------------------------------------------
14420 // create and save images for use in level sketches (raw BMP format)
14421 // ----------------------------------------------------------------------------
14423 void CreateLevelSketchImages(void)
14429 InitElementPropertiesGfxElement();
14431 bitmap1 = CreateBitmap(TILEX, TILEY, DEFAULT_DEPTH);
14432 bitmap2 = CreateBitmap(MINI_TILEX, MINI_TILEY, DEFAULT_DEPTH);
14434 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
14436 int element = getMappedElement(i);
14437 char basename1[16];
14438 char basename2[16];
14442 sprintf(basename1, "%04d.bmp", i);
14443 sprintf(basename2, "%04ds.bmp", i);
14445 filename1 = getPath2(global.create_sketch_images_dir, basename1);
14446 filename2 = getPath2(global.create_sketch_images_dir, basename2);
14448 DrawSizedElement(0, 0, element, TILESIZE);
14449 BlitBitmap(drawto, bitmap1, SX, SY, TILEX, TILEY, 0, 0);
14451 if (SDL_SaveBMP(bitmap1->surface, filename1) != 0)
14452 Fail("cannot save level sketch image file '%s'", filename1);
14454 DrawSizedElement(0, 0, element, MINI_TILESIZE);
14455 BlitBitmap(drawto, bitmap2, SX, SY, MINI_TILEX, MINI_TILEY, 0, 0);
14457 if (SDL_SaveBMP(bitmap2->surface, filename2) != 0)
14458 Fail("cannot save level sketch image file '%s'", filename2);
14463 // create corresponding SQL statements (for normal and small images)
14466 printf("insert into phpbb_words values (NULL, '`%03d', '<IMG class=\"levelsketch\" src=\"/I/%04d.png\"/>');\n", i, i);
14467 printf("insert into phpbb_words values (NULL, '¸%03d', '<IMG class=\"levelsketch\" src=\"/I/%04ds.png\"/>');\n", i, i);
14470 printf("insert into phpbb_words values (NULL, '`%04d', '<IMG class=\"levelsketch\" src=\"/I/%04d.png\"/>');\n", i, i);
14471 printf("insert into phpbb_words values (NULL, '¸%04d', '<IMG class=\"levelsketch\" src=\"/I/%04ds.png\"/>');\n", i, i);
14473 // optional: create content for forum level sketch demonstration post
14475 fprintf(stderr, "%03d `%03d%c", i, i, (i % 10 < 9 ? ' ' : '\n'));
14478 FreeBitmap(bitmap1);
14479 FreeBitmap(bitmap2);
14482 fprintf(stderr, "\n");
14484 Info("%d normal and small images created", NUM_FILE_ELEMENTS);
14486 CloseAllAndExit(0);
14490 // ----------------------------------------------------------------------------
14491 // create and save images for element collecting animations (raw BMP format)
14492 // ----------------------------------------------------------------------------
14494 static boolean createCollectImage(int element)
14496 return (IS_COLLECTIBLE(element) && !IS_SP_ELEMENT(element));
14499 void CreateCollectElementImages(void)
14503 int anim_frames = num_steps - 1;
14504 int tile_size = TILESIZE;
14505 int anim_width = tile_size * anim_frames;
14506 int anim_height = tile_size;
14507 int num_collect_images = 0;
14508 int pos_collect_images = 0;
14510 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
14511 if (createCollectImage(i))
14512 num_collect_images++;
14514 Info("Creating %d element collecting animation images ...",
14515 num_collect_images);
14517 int dst_width = anim_width * 2;
14518 int dst_height = anim_height * num_collect_images / 2;
14519 Bitmap *dst_bitmap = CreateBitmap(dst_width, dst_height, DEFAULT_DEPTH);
14520 char *basename_bmp = "RocksCollect.bmp";
14521 char *basename_png = "RocksCollect.png";
14522 char *filename_bmp = getPath2(global.create_collect_images_dir, basename_bmp);
14523 char *filename_png = getPath2(global.create_collect_images_dir, basename_png);
14524 int len_filename_bmp = strlen(filename_bmp);
14525 int len_filename_png = strlen(filename_png);
14526 int max_command_len = MAX_FILENAME_LEN + len_filename_bmp + len_filename_png;
14527 char cmd_convert[max_command_len];
14529 snprintf(cmd_convert, max_command_len, "convert \"%s\" \"%s\"",
14533 // force using RGBA surface for destination bitmap
14534 SDL_SetColorKey(dst_bitmap->surface, SET_TRANSPARENT_PIXEL,
14535 SDL_MapRGB(dst_bitmap->surface->format, 0x00, 0x00, 0x00));
14537 dst_bitmap->surface =
14538 SDL_ConvertSurfaceFormat(dst_bitmap->surface, SDL_PIXELFORMAT_ARGB8888, 0);
14540 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
14542 if (!createCollectImage(i))
14545 int dst_x = (pos_collect_images / (num_collect_images / 2)) * anim_width;
14546 int dst_y = (pos_collect_images % (num_collect_images / 2)) * anim_height;
14547 int graphic = el2img(i);
14548 char *token_name = element_info[i].token_name;
14549 Bitmap *tmp_bitmap = CreateBitmap(tile_size, tile_size, DEFAULT_DEPTH);
14550 Bitmap *src_bitmap;
14553 Info("- creating collecting image for '%s' ...", token_name);
14555 getGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
14557 BlitBitmap(src_bitmap, tmp_bitmap, src_x, src_y,
14558 tile_size, tile_size, 0, 0);
14560 // force using RGBA surface for temporary bitmap (using transparent black)
14561 SDL_SetColorKey(tmp_bitmap->surface, SET_TRANSPARENT_PIXEL,
14562 SDL_MapRGB(tmp_bitmap->surface->format, 0x00, 0x00, 0x00));
14564 tmp_bitmap->surface =
14565 SDL_ConvertSurfaceFormat(tmp_bitmap->surface, SDL_PIXELFORMAT_ARGB8888, 0);
14567 tmp_bitmap->surface_masked = tmp_bitmap->surface;
14569 for (j = 0; j < anim_frames; j++)
14571 int frame_size_final = tile_size * (anim_frames - j) / num_steps;
14572 int frame_size = frame_size_final * num_steps;
14573 int offset = (tile_size - frame_size_final) / 2;
14574 Bitmap *frame_bitmap = ZoomBitmap(tmp_bitmap, frame_size, frame_size);
14576 while (frame_size > frame_size_final)
14580 Bitmap *half_bitmap = ZoomBitmap(frame_bitmap, frame_size, frame_size);
14582 FreeBitmap(frame_bitmap);
14584 frame_bitmap = half_bitmap;
14587 BlitBitmapMasked(frame_bitmap, dst_bitmap, 0, 0,
14588 frame_size_final, frame_size_final,
14589 dst_x + j * tile_size + offset, dst_y + offset);
14591 FreeBitmap(frame_bitmap);
14594 tmp_bitmap->surface_masked = NULL;
14596 FreeBitmap(tmp_bitmap);
14598 pos_collect_images++;
14601 if (SDL_SaveBMP(dst_bitmap->surface, filename_bmp) != 0)
14602 Fail("cannot save element collecting image file '%s'", filename_bmp);
14604 FreeBitmap(dst_bitmap);
14606 Info("Converting image file from BMP to PNG ...");
14608 if (system(cmd_convert) != 0)
14609 Fail("converting image file failed");
14611 unlink(filename_bmp);
14615 CloseAllAndExit(0);
14619 // ----------------------------------------------------------------------------
14620 // create and save images for custom and group elements (raw BMP format)
14621 // ----------------------------------------------------------------------------
14623 void CreateCustomElementImages(char *directory)
14625 char *src_basename = "RocksCE-template.ilbm";
14626 char *dst_basename = "RocksCE.bmp";
14627 char *src_filename = getPath2(directory, src_basename);
14628 char *dst_filename = getPath2(directory, dst_basename);
14629 Bitmap *src_bitmap;
14631 int yoffset_ce = 0;
14632 int yoffset_ge = (TILEY * NUM_CUSTOM_ELEMENTS / 16);
14635 InitVideoDefaults();
14637 ReCreateBitmap(&backbuffer, video.width, video.height);
14639 src_bitmap = LoadImage(src_filename);
14641 bitmap = CreateBitmap(TILEX * 16 * 2,
14642 TILEY * (NUM_CUSTOM_ELEMENTS + NUM_GROUP_ELEMENTS) / 16,
14645 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
14652 BlitBitmap(src_bitmap, bitmap, 0, 0, TILEX, TILEY,
14653 TILEX * x, TILEY * y + yoffset_ce);
14655 BlitBitmap(src_bitmap, bitmap, 0, TILEY,
14657 TILEX * x + TILEX * 16,
14658 TILEY * y + yoffset_ce);
14660 for (j = 2; j >= 0; j--)
14664 BlitBitmap(src_bitmap, bitmap,
14665 TILEX + c * 7, 0, 6, 10,
14666 TILEX * x + 6 + j * 7,
14667 TILEY * y + 11 + yoffset_ce);
14669 BlitBitmap(src_bitmap, bitmap,
14670 TILEX + c * 8, TILEY, 6, 10,
14671 TILEX * 16 + TILEX * x + 6 + j * 8,
14672 TILEY * y + 10 + yoffset_ce);
14678 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
14685 BlitBitmap(src_bitmap, bitmap, 0, 0, TILEX, TILEY,
14686 TILEX * x, TILEY * y + yoffset_ge);
14688 BlitBitmap(src_bitmap, bitmap, 0, TILEY,
14690 TILEX * x + TILEX * 16,
14691 TILEY * y + yoffset_ge);
14693 for (j = 1; j >= 0; j--)
14697 BlitBitmap(src_bitmap, bitmap, TILEX + c * 10, 11, 10, 10,
14698 TILEX * x + 6 + j * 10,
14699 TILEY * y + 11 + yoffset_ge);
14701 BlitBitmap(src_bitmap, bitmap,
14702 TILEX + c * 8, TILEY + 12, 6, 10,
14703 TILEX * 16 + TILEX * x + 10 + j * 8,
14704 TILEY * y + 10 + yoffset_ge);
14710 if (SDL_SaveBMP(bitmap->surface, dst_filename) != 0)
14711 Fail("cannot save CE graphics file '%s'", dst_filename);
14713 FreeBitmap(bitmap);
14715 CloseAllAndExit(0);