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
814 // (the following values are related to various game elements)
818 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
819 &li.score[SC_EMERALD], 10
824 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
825 &li.score[SC_DIAMOND], 10
830 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
831 &li.score[SC_BUG], 10
836 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
837 &li.score[SC_SPACESHIP], 10
842 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
843 &li.score[SC_PACMAN], 10
848 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
849 &li.score[SC_NUT], 10
854 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
855 &li.score[SC_DYNAMITE], 10
860 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
861 &li.score[SC_KEY], 10
866 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
867 &li.score[SC_PEARL], 10
872 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
873 &li.score[SC_CRYSTAL], 10
876 // (amoeba values used by R'n'D game engine only)
879 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
880 &li.amoeba_content, EL_DIAMOND
884 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
889 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
890 &li.grow_into_diggable, TRUE
892 // (amoeba values used by BD game engine only)
895 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
896 &li.bd_amoeba_wait_for_hatching, FALSE
900 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
901 &li.bd_amoeba_start_immediately, TRUE
905 TYPE_BOOLEAN, CONF_VALUE_8_BIT(4),
906 &li.bd_amoeba_2_explode_by_amoeba, TRUE
910 TYPE_INTEGER, CONF_VALUE_16_BIT(3),
911 &li.bd_amoeba_threshold_too_big, 200
915 TYPE_INTEGER, CONF_VALUE_16_BIT(4),
916 &li.bd_amoeba_slow_growth_time, 200
920 TYPE_INTEGER, CONF_VALUE_8_BIT(5),
921 &li.bd_amoeba_slow_growth_rate, 3
925 TYPE_INTEGER, CONF_VALUE_8_BIT(6),
926 &li.bd_amoeba_fast_growth_rate, 25
930 TYPE_ELEMENT, CONF_VALUE_16_BIT(5),
931 &li.bd_amoeba_content_too_big, EL_BD_ROCK
935 TYPE_ELEMENT, CONF_VALUE_16_BIT(6),
936 &li.bd_amoeba_content_enclosed, EL_BD_DIAMOND
941 TYPE_INTEGER, CONF_VALUE_16_BIT(3),
942 &li.bd_amoeba_2_threshold_too_big, 200
946 TYPE_INTEGER, CONF_VALUE_16_BIT(4),
947 &li.bd_amoeba_2_slow_growth_time, 200
951 TYPE_INTEGER, CONF_VALUE_8_BIT(5),
952 &li.bd_amoeba_2_slow_growth_rate, 3
956 TYPE_INTEGER, CONF_VALUE_8_BIT(6),
957 &li.bd_amoeba_2_fast_growth_rate, 25
961 TYPE_ELEMENT, CONF_VALUE_16_BIT(5),
962 &li.bd_amoeba_2_content_too_big, EL_BD_ROCK
966 TYPE_ELEMENT, CONF_VALUE_16_BIT(6),
967 &li.bd_amoeba_2_content_enclosed, EL_BD_DIAMOND
971 TYPE_ELEMENT, CONF_VALUE_16_BIT(7),
972 &li.bd_amoeba_2_content_exploding, EL_EMPTY
976 TYPE_ELEMENT, CONF_VALUE_16_BIT(8),
977 &li.bd_amoeba_2_content_looks_like, EL_BD_AMOEBA_2
982 TYPE_CONTENT_LIST, CONF_VALUE_BYTES(1),
983 &li.yamyam_content, EL_ROCK, NULL,
984 &li.num_yamyam_contents, 4, MAX_ELEMENT_CONTENTS
988 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
989 &li.score[SC_YAMYAM], 10
994 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
995 &li.score[SC_ROBOT], 10
999 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
1005 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1011 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1012 &li.time_magic_wall, 10
1016 EL_GAME_OF_LIFE, -1,
1017 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
1018 &li.game_of_life[0], 2
1021 EL_GAME_OF_LIFE, -1,
1022 TYPE_INTEGER, CONF_VALUE_8_BIT(2),
1023 &li.game_of_life[1], 3
1026 EL_GAME_OF_LIFE, -1,
1027 TYPE_INTEGER, CONF_VALUE_8_BIT(3),
1028 &li.game_of_life[2], 3
1031 EL_GAME_OF_LIFE, -1,
1032 TYPE_INTEGER, CONF_VALUE_8_BIT(4),
1033 &li.game_of_life[3], 3
1036 EL_GAME_OF_LIFE, -1,
1037 TYPE_BOOLEAN, CONF_VALUE_8_BIT(5),
1038 &li.use_life_bugs, FALSE
1043 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
1048 TYPE_INTEGER, CONF_VALUE_8_BIT(2),
1053 TYPE_INTEGER, CONF_VALUE_8_BIT(3),
1058 TYPE_INTEGER, CONF_VALUE_8_BIT(4),
1063 EL_TIMEGATE_SWITCH, -1,
1064 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1065 &li.time_timegate, 10
1069 EL_LIGHT_SWITCH_ACTIVE, -1,
1070 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1075 EL_SHIELD_NORMAL, -1,
1076 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1077 &li.shield_normal_time, 10
1080 EL_SHIELD_NORMAL, -1,
1081 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
1082 &li.score[SC_SHIELD], 10
1086 EL_SHIELD_DEADLY, -1,
1087 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1088 &li.shield_deadly_time, 10
1091 EL_SHIELD_DEADLY, -1,
1092 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
1093 &li.score[SC_SHIELD], 10
1098 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1103 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
1104 &li.extra_time_score, 10
1108 EL_TIME_ORB_FULL, -1,
1109 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1110 &li.time_orb_time, 10
1113 EL_TIME_ORB_FULL, -1,
1114 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1115 &li.use_time_orb_bug, FALSE
1120 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1121 &li.use_spring_bug, FALSE
1126 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1127 &li.android_move_time, 10
1131 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
1132 &li.android_clone_time, 10
1135 EL_EMC_ANDROID, SAVE_CONF_NEVER,
1136 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(1),
1137 &li.android_clone_element[0], EL_EMPTY, NULL,
1138 &li.num_android_clone_elements, 1, MAX_ANDROID_ELEMENTS_OLD
1142 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(2),
1143 &li.android_clone_element[0], EL_EMPTY, NULL,
1144 &li.num_android_clone_elements, 1, MAX_ANDROID_ELEMENTS
1149 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1150 &li.lenses_score, 10
1154 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
1159 EL_EMC_MAGNIFIER, -1,
1160 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1161 &li.magnify_score, 10
1164 EL_EMC_MAGNIFIER, -1,
1165 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
1166 &li.magnify_time, 10
1170 EL_EMC_MAGIC_BALL, -1,
1171 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1175 EL_EMC_MAGIC_BALL, -1,
1176 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1177 &li.ball_random, FALSE
1180 EL_EMC_MAGIC_BALL, -1,
1181 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
1182 &li.ball_active_initial, FALSE
1185 EL_EMC_MAGIC_BALL, -1,
1186 TYPE_CONTENT_LIST, CONF_VALUE_BYTES(1),
1187 &li.ball_content, EL_EMPTY, NULL,
1188 &li.num_ball_contents, 4, MAX_ELEMENT_CONTENTS
1192 EL_SOKOBAN_FIELD_EMPTY, -1,
1193 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1194 &li.sb_fields_needed, TRUE
1198 EL_SOKOBAN_OBJECT, -1,
1199 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1200 &li.sb_objects_needed, TRUE
1205 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1206 &li.mm_laser_red, FALSE
1210 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
1211 &li.mm_laser_green, FALSE
1215 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
1216 &li.mm_laser_blue, TRUE
1221 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1222 &li.df_laser_red, TRUE
1226 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
1227 &li.df_laser_green, TRUE
1231 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
1232 &li.df_laser_blue, FALSE
1236 EL_MM_FUSE_ACTIVE, -1,
1237 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1238 &li.mm_time_fuse, 25
1242 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1243 &li.mm_time_bomb, 75
1247 EL_MM_GRAY_BALL, -1,
1248 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1249 &li.mm_time_ball, 75
1252 EL_MM_GRAY_BALL, -1,
1253 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
1254 &li.mm_ball_choice_mode, ANIM_RANDOM
1257 EL_MM_GRAY_BALL, -1,
1258 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(1),
1259 &li.mm_ball_content, EL_EMPTY, NULL,
1260 &li.num_mm_ball_contents, 8, MAX_MM_BALL_CONTENTS
1263 EL_MM_GRAY_BALL, -1,
1264 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
1265 &li.rotate_mm_ball_content, TRUE
1268 EL_MM_GRAY_BALL, -1,
1269 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
1270 &li.explode_mm_ball, FALSE
1274 EL_MM_STEEL_BLOCK, -1,
1275 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1276 &li.mm_time_block, 75
1279 EL_MM_LIGHTBALL, -1,
1280 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1281 &li.score[SC_ELEM_BONUS], 10
1291 static struct LevelFileConfigInfo chunk_config_NOTE[] =
1295 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
1296 &xx_envelope.xsize, MAX_ENVELOPE_XSIZE,
1300 TYPE_INTEGER, CONF_VALUE_8_BIT(2),
1301 &xx_envelope.ysize, MAX_ENVELOPE_YSIZE,
1306 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
1307 &xx_envelope.autowrap, FALSE
1311 TYPE_BOOLEAN, CONF_VALUE_8_BIT(4),
1312 &xx_envelope.centered, FALSE
1317 TYPE_STRING, CONF_VALUE_BYTES(1),
1318 &xx_envelope.text, -1, NULL,
1319 &xx_string_length_unused, -1, MAX_ENVELOPE_TEXT_LEN,
1320 &xx_default_string_empty[0]
1330 static struct LevelFileConfigInfo chunk_config_CUSX_base[] =
1334 TYPE_STRING, CONF_VALUE_BYTES(1),
1335 &xx_ei.description[0], -1,
1336 &yy_ei.description[0],
1337 &xx_string_length_unused, -1, MAX_ELEMENT_NAME_LEN,
1338 &xx_default_description[0]
1343 TYPE_BITFIELD, CONF_VALUE_32_BIT(1),
1344 &xx_ei.properties[EP_BITFIELD_BASE_NR], EP_BITMASK_BASE_DEFAULT,
1345 &yy_ei.properties[EP_BITFIELD_BASE_NR]
1347 #if ENABLE_RESERVED_CODE
1348 // (reserved for later use)
1351 TYPE_BITFIELD, CONF_VALUE_32_BIT(2),
1352 &xx_ei.properties[EP_BITFIELD_BASE_NR + 1], EP_BITMASK_DEFAULT,
1353 &yy_ei.properties[EP_BITFIELD_BASE_NR + 1]
1359 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1360 &xx_ei.use_gfx_element, FALSE,
1361 &yy_ei.use_gfx_element
1365 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
1366 &xx_ei.gfx_element_initial, EL_EMPTY_SPACE,
1367 &yy_ei.gfx_element_initial
1372 TYPE_BITFIELD, CONF_VALUE_8_BIT(2),
1373 &xx_ei.access_direction, MV_ALL_DIRECTIONS,
1374 &yy_ei.access_direction
1379 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
1380 &xx_ei.collect_score_initial, 10,
1381 &yy_ei.collect_score_initial
1385 TYPE_INTEGER, CONF_VALUE_16_BIT(3),
1386 &xx_ei.collect_count_initial, 1,
1387 &yy_ei.collect_count_initial
1392 TYPE_INTEGER, CONF_VALUE_16_BIT(4),
1393 &xx_ei.ce_value_fixed_initial, 0,
1394 &yy_ei.ce_value_fixed_initial
1398 TYPE_INTEGER, CONF_VALUE_16_BIT(5),
1399 &xx_ei.ce_value_random_initial, 0,
1400 &yy_ei.ce_value_random_initial
1404 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
1405 &xx_ei.use_last_ce_value, FALSE,
1406 &yy_ei.use_last_ce_value
1411 TYPE_INTEGER, CONF_VALUE_16_BIT(6),
1412 &xx_ei.push_delay_fixed, 8,
1413 &yy_ei.push_delay_fixed
1417 TYPE_INTEGER, CONF_VALUE_16_BIT(7),
1418 &xx_ei.push_delay_random, 8,
1419 &yy_ei.push_delay_random
1423 TYPE_INTEGER, CONF_VALUE_16_BIT(8),
1424 &xx_ei.drop_delay_fixed, 0,
1425 &yy_ei.drop_delay_fixed
1429 TYPE_INTEGER, CONF_VALUE_16_BIT(9),
1430 &xx_ei.drop_delay_random, 0,
1431 &yy_ei.drop_delay_random
1435 TYPE_INTEGER, CONF_VALUE_16_BIT(10),
1436 &xx_ei.move_delay_fixed, 0,
1437 &yy_ei.move_delay_fixed
1441 TYPE_INTEGER, CONF_VALUE_16_BIT(11),
1442 &xx_ei.move_delay_random, 0,
1443 &yy_ei.move_delay_random
1447 TYPE_INTEGER, CONF_VALUE_16_BIT(16),
1448 &xx_ei.step_delay_fixed, 0,
1449 &yy_ei.step_delay_fixed
1453 TYPE_INTEGER, CONF_VALUE_16_BIT(17),
1454 &xx_ei.step_delay_random, 0,
1455 &yy_ei.step_delay_random
1460 TYPE_BITFIELD, CONF_VALUE_32_BIT(3),
1461 &xx_ei.move_pattern, MV_ALL_DIRECTIONS,
1466 TYPE_BITFIELD, CONF_VALUE_8_BIT(4),
1467 &xx_ei.move_direction_initial, MV_START_AUTOMATIC,
1468 &yy_ei.move_direction_initial
1472 TYPE_INTEGER, CONF_VALUE_8_BIT(5),
1473 &xx_ei.move_stepsize, TILEX / 8,
1474 &yy_ei.move_stepsize
1479 TYPE_ELEMENT, CONF_VALUE_16_BIT(12),
1480 &xx_ei.move_enter_element, EL_EMPTY_SPACE,
1481 &yy_ei.move_enter_element
1485 TYPE_ELEMENT, CONF_VALUE_16_BIT(13),
1486 &xx_ei.move_leave_element, EL_EMPTY_SPACE,
1487 &yy_ei.move_leave_element
1491 TYPE_INTEGER, CONF_VALUE_8_BIT(6),
1492 &xx_ei.move_leave_type, LEAVE_TYPE_UNLIMITED,
1493 &yy_ei.move_leave_type
1498 TYPE_INTEGER, CONF_VALUE_8_BIT(7),
1499 &xx_ei.slippery_type, SLIPPERY_ANY_RANDOM,
1500 &yy_ei.slippery_type
1505 TYPE_INTEGER, CONF_VALUE_8_BIT(8),
1506 &xx_ei.explosion_type, EXPLODES_3X3,
1507 &yy_ei.explosion_type
1511 TYPE_INTEGER, CONF_VALUE_16_BIT(14),
1512 &xx_ei.explosion_delay, 16,
1513 &yy_ei.explosion_delay
1517 TYPE_INTEGER, CONF_VALUE_16_BIT(15),
1518 &xx_ei.ignition_delay, 8,
1519 &yy_ei.ignition_delay
1524 TYPE_CONTENT_LIST, CONF_VALUE_BYTES(2),
1525 &xx_ei.content, EL_EMPTY_SPACE,
1527 &xx_num_contents, 1, 1
1530 // ---------- "num_change_pages" must be the last entry ---------------------
1533 -1, SAVE_CONF_ALWAYS,
1534 TYPE_INTEGER, CONF_VALUE_8_BIT(9),
1535 &xx_ei.num_change_pages, 1,
1536 &yy_ei.num_change_pages
1547 static struct LevelFileConfigInfo chunk_config_CUSX_change[] =
1549 // ---------- "current_change_page" must be the first entry -----------------
1552 -1, SAVE_CONF_ALWAYS,
1553 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
1554 &xx_current_change_page, -1
1557 // ---------- (the remaining entries can be in any order) -------------------
1561 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
1562 &xx_change.can_change, FALSE
1567 TYPE_BITFIELD, CONF_VALUE_32_BIT(1),
1568 &xx_event_bits[0], 0
1572 TYPE_BITFIELD, CONF_VALUE_32_BIT(2),
1573 &xx_event_bits[1], 0
1578 TYPE_BITFIELD, CONF_VALUE_8_BIT(3),
1579 &xx_change.trigger_player, CH_PLAYER_ANY
1583 TYPE_BITFIELD, CONF_VALUE_8_BIT(4),
1584 &xx_change.trigger_side, CH_SIDE_ANY
1588 TYPE_BITFIELD, CONF_VALUE_32_BIT(3),
1589 &xx_change.trigger_page, CH_PAGE_ANY
1594 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
1595 &xx_change.target_element, EL_EMPTY_SPACE
1600 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
1601 &xx_change.delay_fixed, 0
1605 TYPE_INTEGER, CONF_VALUE_16_BIT(3),
1606 &xx_change.delay_random, 0
1610 TYPE_INTEGER, CONF_VALUE_16_BIT(4),
1611 &xx_change.delay_frames, FRAMES_PER_SECOND
1616 TYPE_ELEMENT, CONF_VALUE_16_BIT(5),
1617 &xx_change.initial_trigger_element, EL_EMPTY_SPACE
1622 TYPE_BOOLEAN, CONF_VALUE_8_BIT(6),
1623 &xx_change.explode, FALSE
1627 TYPE_BOOLEAN, CONF_VALUE_8_BIT(7),
1628 &xx_change.use_target_content, FALSE
1632 TYPE_BOOLEAN, CONF_VALUE_8_BIT(8),
1633 &xx_change.only_if_complete, FALSE
1637 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
1638 &xx_change.use_random_replace, FALSE
1642 TYPE_INTEGER, CONF_VALUE_8_BIT(10),
1643 &xx_change.random_percentage, 100
1647 TYPE_INTEGER, CONF_VALUE_8_BIT(11),
1648 &xx_change.replace_when, CP_WHEN_EMPTY
1653 TYPE_BOOLEAN, CONF_VALUE_8_BIT(12),
1654 &xx_change.has_action, FALSE
1658 TYPE_INTEGER, CONF_VALUE_8_BIT(13),
1659 &xx_change.action_type, CA_NO_ACTION
1663 TYPE_INTEGER, CONF_VALUE_8_BIT(14),
1664 &xx_change.action_mode, CA_MODE_UNDEFINED
1668 TYPE_INTEGER, CONF_VALUE_16_BIT(6),
1669 &xx_change.action_arg, CA_ARG_UNDEFINED
1674 TYPE_ELEMENT, CONF_VALUE_16_BIT(7),
1675 &xx_change.action_element, EL_EMPTY_SPACE
1680 TYPE_CONTENT_LIST, CONF_VALUE_BYTES(1),
1681 &xx_change.target_content, EL_EMPTY_SPACE, NULL,
1682 &xx_num_contents, 1, 1
1692 static struct LevelFileConfigInfo chunk_config_GRPX[] =
1696 TYPE_STRING, CONF_VALUE_BYTES(1),
1697 &xx_ei.description[0], -1, NULL,
1698 &xx_string_length_unused, -1, MAX_ELEMENT_NAME_LEN,
1699 &xx_default_description[0]
1704 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1705 &xx_ei.use_gfx_element, FALSE
1709 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
1710 &xx_ei.gfx_element_initial, EL_EMPTY_SPACE
1715 TYPE_INTEGER, CONF_VALUE_8_BIT(2),
1716 &xx_group.choice_mode, ANIM_RANDOM
1721 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(2),
1722 &xx_group.element[0], EL_EMPTY_SPACE, NULL,
1723 &xx_group.num_elements, 1, MAX_ELEMENTS_IN_GROUP
1733 static struct LevelFileConfigInfo chunk_config_EMPX[] =
1737 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1738 &xx_ei.use_gfx_element, FALSE
1742 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
1743 &xx_ei.gfx_element_initial, EL_EMPTY_SPACE
1753 static struct LevelFileConfigInfo chunk_config_CONF[] = // (OBSOLETE)
1757 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
1758 &li.block_snap_field, TRUE
1762 TYPE_BOOLEAN, CONF_VALUE_8_BIT(13),
1763 &li.continuous_snapping, TRUE
1767 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
1768 &li.initial_player_stepsize[0], STEPSIZE_NORMAL
1772 TYPE_BOOLEAN, CONF_VALUE_8_BIT(10),
1773 &li.use_start_element[0], FALSE
1777 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
1778 &li.start_element[0], EL_PLAYER_1
1782 TYPE_BOOLEAN, CONF_VALUE_8_BIT(11),
1783 &li.use_artwork_element[0], FALSE
1787 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
1788 &li.artwork_element[0], EL_PLAYER_1
1792 TYPE_BOOLEAN, CONF_VALUE_8_BIT(12),
1793 &li.use_explosion_element[0], FALSE
1797 TYPE_ELEMENT, CONF_VALUE_16_BIT(3),
1798 &li.explosion_element[0], EL_PLAYER_1
1813 filetype_id_list[] =
1815 { LEVEL_FILE_TYPE_RND, "RND" },
1816 { LEVEL_FILE_TYPE_BD, "BD" },
1817 { LEVEL_FILE_TYPE_EM, "EM" },
1818 { LEVEL_FILE_TYPE_SP, "SP" },
1819 { LEVEL_FILE_TYPE_DX, "DX" },
1820 { LEVEL_FILE_TYPE_SB, "SB" },
1821 { LEVEL_FILE_TYPE_DC, "DC" },
1822 { LEVEL_FILE_TYPE_MM, "MM" },
1823 { LEVEL_FILE_TYPE_MM, "DF" },
1828 // ============================================================================
1829 // level file functions
1830 // ============================================================================
1832 static boolean check_special_flags(char *flag)
1834 if (strEqual(options.special_flags, flag) ||
1835 strEqual(leveldir_current->special_flags, flag))
1841 static struct DateInfo getCurrentDate(void)
1843 time_t epoch_seconds = time(NULL);
1844 struct tm *now = localtime(&epoch_seconds);
1845 struct DateInfo date;
1847 date.year = now->tm_year + 1900;
1848 date.month = now->tm_mon + 1;
1849 date.day = now->tm_mday;
1851 date.src = DATE_SRC_CLOCK;
1856 static void resetEventFlags(struct ElementChangeInfo *change)
1860 for (i = 0; i < NUM_CHANGE_EVENTS; i++)
1861 change->has_event[i] = FALSE;
1864 static void resetEventBits(void)
1868 for (i = 0; i < NUM_CE_BITFIELDS; i++)
1869 xx_event_bits[i] = 0;
1872 static void setEventFlagsFromEventBits(struct ElementChangeInfo *change)
1876 /* important: only change event flag if corresponding event bit is set
1877 (this is because all xx_event_bits[] values are loaded separately,
1878 and all xx_event_bits[] values are set back to zero before loading
1879 another value xx_event_bits[x] (each value representing 32 flags)) */
1881 for (i = 0; i < NUM_CHANGE_EVENTS; i++)
1882 if (xx_event_bits[CH_EVENT_BITFIELD_NR(i)] & CH_EVENT_BIT(i))
1883 change->has_event[i] = TRUE;
1886 static void setEventBitsFromEventFlags(struct ElementChangeInfo *change)
1890 /* in contrast to the above function setEventFlagsFromEventBits(), it
1891 would also be possible to set all bits in xx_event_bits[] to 0 or 1
1892 depending on the corresponding change->has_event[i] values here, as
1893 all xx_event_bits[] values are reset in resetEventBits() before */
1895 for (i = 0; i < NUM_CHANGE_EVENTS; i++)
1896 if (change->has_event[i])
1897 xx_event_bits[CH_EVENT_BITFIELD_NR(i)] |= CH_EVENT_BIT(i);
1900 static char *getDefaultElementDescription(struct ElementInfo *ei)
1902 static char description[MAX_ELEMENT_NAME_LEN + 1];
1903 char *default_description = (ei->custom_description != NULL ?
1904 ei->custom_description :
1905 ei->editor_description);
1908 // always start with reliable default values
1909 for (i = 0; i < MAX_ELEMENT_NAME_LEN + 1; i++)
1910 description[i] = '\0';
1912 // truncate element description to MAX_ELEMENT_NAME_LEN bytes
1913 strncpy(description, default_description, MAX_ELEMENT_NAME_LEN);
1915 return &description[0];
1918 static void setElementDescriptionToDefault(struct ElementInfo *ei)
1920 char *default_description = getDefaultElementDescription(ei);
1923 for (i = 0; i < MAX_ELEMENT_NAME_LEN + 1; i++)
1924 ei->description[i] = default_description[i];
1927 static void setConfigToDefaultsFromConfigList(struct LevelFileConfigInfo *conf)
1931 for (i = 0; conf[i].data_type != -1; i++)
1933 int default_value = conf[i].default_value;
1934 int data_type = conf[i].data_type;
1935 int conf_type = conf[i].conf_type;
1936 int byte_mask = conf_type & CONF_MASK_BYTES;
1938 if (byte_mask == CONF_MASK_MULTI_BYTES)
1940 int default_num_entities = conf[i].default_num_entities;
1941 int max_num_entities = conf[i].max_num_entities;
1943 *(int *)(conf[i].num_entities) = default_num_entities;
1945 if (data_type == TYPE_STRING)
1947 char *default_string = conf[i].default_string;
1948 char *string = (char *)(conf[i].value);
1950 strncpy(string, default_string, max_num_entities);
1952 else if (data_type == TYPE_ELEMENT_LIST)
1954 int *element_array = (int *)(conf[i].value);
1957 for (j = 0; j < max_num_entities; j++)
1958 element_array[j] = default_value;
1960 else if (data_type == TYPE_CONTENT_LIST)
1962 struct Content *content = (struct Content *)(conf[i].value);
1965 for (c = 0; c < max_num_entities; c++)
1966 for (y = 0; y < 3; y++)
1967 for (x = 0; x < 3; x++)
1968 content[c].e[x][y] = default_value;
1971 else // constant size configuration data (1, 2 or 4 bytes)
1973 if (data_type == TYPE_BOOLEAN)
1974 *(boolean *)(conf[i].value) = default_value;
1976 *(int *) (conf[i].value) = default_value;
1981 static void copyConfigFromConfigList(struct LevelFileConfigInfo *conf)
1985 for (i = 0; conf[i].data_type != -1; i++)
1987 int data_type = conf[i].data_type;
1988 int conf_type = conf[i].conf_type;
1989 int byte_mask = conf_type & CONF_MASK_BYTES;
1991 if (byte_mask == CONF_MASK_MULTI_BYTES)
1993 int max_num_entities = conf[i].max_num_entities;
1995 if (data_type == TYPE_STRING)
1997 char *string = (char *)(conf[i].value);
1998 char *string_copy = (char *)(conf[i].value_copy);
2000 strncpy(string_copy, string, max_num_entities);
2002 else if (data_type == TYPE_ELEMENT_LIST)
2004 int *element_array = (int *)(conf[i].value);
2005 int *element_array_copy = (int *)(conf[i].value_copy);
2008 for (j = 0; j < max_num_entities; j++)
2009 element_array_copy[j] = element_array[j];
2011 else if (data_type == TYPE_CONTENT_LIST)
2013 struct Content *content = (struct Content *)(conf[i].value);
2014 struct Content *content_copy = (struct Content *)(conf[i].value_copy);
2017 for (c = 0; c < max_num_entities; c++)
2018 for (y = 0; y < 3; y++)
2019 for (x = 0; x < 3; x++)
2020 content_copy[c].e[x][y] = content[c].e[x][y];
2023 else // constant size configuration data (1, 2 or 4 bytes)
2025 if (data_type == TYPE_BOOLEAN)
2026 *(boolean *)(conf[i].value_copy) = *(boolean *)(conf[i].value);
2028 *(int *) (conf[i].value_copy) = *(int *) (conf[i].value);
2033 void copyElementInfo(struct ElementInfo *ei_from, struct ElementInfo *ei_to)
2037 xx_ei = *ei_from; // copy element data into temporary buffer
2038 yy_ei = *ei_to; // copy element data into temporary buffer
2040 copyConfigFromConfigList(chunk_config_CUSX_base);
2045 // ---------- reinitialize and copy change pages ----------
2047 ei_to->num_change_pages = ei_from->num_change_pages;
2048 ei_to->current_change_page = ei_from->current_change_page;
2050 setElementChangePages(ei_to, ei_to->num_change_pages);
2052 for (i = 0; i < ei_to->num_change_pages; i++)
2053 ei_to->change_page[i] = ei_from->change_page[i];
2055 // ---------- copy group element info ----------
2056 if (ei_from->group != NULL && ei_to->group != NULL) // group or internal
2057 *ei_to->group = *ei_from->group;
2059 // mark this custom element as modified
2060 ei_to->modified_settings = TRUE;
2063 void setElementChangePages(struct ElementInfo *ei, int change_pages)
2065 int change_page_size = sizeof(struct ElementChangeInfo);
2067 ei->num_change_pages = MAX(1, change_pages);
2070 checked_realloc(ei->change_page, ei->num_change_pages * change_page_size);
2072 if (ei->current_change_page >= ei->num_change_pages)
2073 ei->current_change_page = ei->num_change_pages - 1;
2075 ei->change = &ei->change_page[ei->current_change_page];
2078 void setElementChangeInfoToDefaults(struct ElementChangeInfo *change)
2080 xx_change = *change; // copy change data into temporary buffer
2082 setConfigToDefaultsFromConfigList(chunk_config_CUSX_change);
2084 *change = xx_change;
2086 resetEventFlags(change);
2088 change->direct_action = 0;
2089 change->other_action = 0;
2091 change->pre_change_function = NULL;
2092 change->change_function = NULL;
2093 change->post_change_function = NULL;
2096 static void setLevelInfoToDefaults_Level(struct LevelInfo *level)
2100 li = *level; // copy level data into temporary buffer
2101 setConfigToDefaultsFromConfigList(chunk_config_INFO);
2102 *level = li; // copy temporary buffer back to level data
2104 setLevelInfoToDefaults_BD();
2105 setLevelInfoToDefaults_EM();
2106 setLevelInfoToDefaults_SP();
2107 setLevelInfoToDefaults_MM();
2109 level->native_bd_level = &native_bd_level;
2110 level->native_em_level = &native_em_level;
2111 level->native_sp_level = &native_sp_level;
2112 level->native_mm_level = &native_mm_level;
2114 level->file_version = FILE_VERSION_ACTUAL;
2115 level->game_version = GAME_VERSION_ACTUAL;
2117 level->creation_date = getCurrentDate();
2119 level->encoding_16bit_field = TRUE;
2120 level->encoding_16bit_yamyam = TRUE;
2121 level->encoding_16bit_amoeba = TRUE;
2123 // clear level name and level author string buffers
2124 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
2125 level->name[i] = '\0';
2126 for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
2127 level->author[i] = '\0';
2129 // set level name and level author to default values
2130 strcpy(level->name, NAMELESS_LEVEL_NAME);
2131 strcpy(level->author, ANONYMOUS_NAME);
2133 // set level playfield to playable default level with player and exit
2134 for (x = 0; x < MAX_LEV_FIELDX; x++)
2135 for (y = 0; y < MAX_LEV_FIELDY; y++)
2136 level->field[x][y] = EL_SAND;
2138 level->field[0][0] = EL_PLAYER_1;
2139 level->field[STD_LEV_FIELDX - 1][STD_LEV_FIELDY - 1] = EL_EXIT_CLOSED;
2141 BorderElement = EL_STEELWALL;
2143 // detect custom elements when loading them
2144 level->file_has_custom_elements = FALSE;
2146 // set all bug compatibility flags to "false" => do not emulate this bug
2147 level->use_action_after_change_bug = FALSE;
2149 if (leveldir_current)
2151 // try to determine better author name than 'anonymous'
2152 if (!strEqual(leveldir_current->author, ANONYMOUS_NAME))
2154 strncpy(level->author, leveldir_current->author, MAX_LEVEL_AUTHOR_LEN);
2155 level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
2159 switch (LEVELCLASS(leveldir_current))
2161 case LEVELCLASS_TUTORIAL:
2162 strcpy(level->author, PROGRAM_AUTHOR_STRING);
2165 case LEVELCLASS_CONTRIB:
2166 strncpy(level->author, leveldir_current->name, MAX_LEVEL_AUTHOR_LEN);
2167 level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
2170 case LEVELCLASS_PRIVATE:
2171 strncpy(level->author, getRealName(), MAX_LEVEL_AUTHOR_LEN);
2172 level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
2176 // keep default value
2183 static void setLevelInfoToDefaults_Elements(struct LevelInfo *level)
2185 static boolean clipboard_elements_initialized = FALSE;
2188 InitElementPropertiesStatic();
2190 li = *level; // copy level data into temporary buffer
2191 setConfigToDefaultsFromConfigList(chunk_config_ELEM);
2192 *level = li; // copy temporary buffer back to level data
2194 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2197 struct ElementInfo *ei = &element_info[element];
2199 if (element == EL_MM_GRAY_BALL)
2201 struct LevelInfo_MM *level_mm = level->native_mm_level;
2204 for (j = 0; j < level->num_mm_ball_contents; j++)
2205 level->mm_ball_content[j] =
2206 map_element_MM_to_RND(level_mm->ball_content[j]);
2209 // never initialize clipboard elements after the very first time
2210 // (to be able to use clipboard elements between several levels)
2211 if (IS_CLIPBOARD_ELEMENT(element) && clipboard_elements_initialized)
2214 if (IS_ENVELOPE(element))
2216 int envelope_nr = element - EL_ENVELOPE_1;
2218 setConfigToDefaultsFromConfigList(chunk_config_NOTE);
2220 level->envelope[envelope_nr] = xx_envelope;
2223 if (IS_CUSTOM_ELEMENT(element) ||
2224 IS_GROUP_ELEMENT(element) ||
2225 IS_INTERNAL_ELEMENT(element))
2227 xx_ei = *ei; // copy element data into temporary buffer
2229 setConfigToDefaultsFromConfigList(chunk_config_CUSX_base);
2234 setElementChangePages(ei, 1);
2235 setElementChangeInfoToDefaults(ei->change);
2237 if (IS_CUSTOM_ELEMENT(element) ||
2238 IS_GROUP_ELEMENT(element))
2240 setElementDescriptionToDefault(ei);
2242 ei->modified_settings = FALSE;
2245 if (IS_CUSTOM_ELEMENT(element) ||
2246 IS_INTERNAL_ELEMENT(element))
2248 // internal values used in level editor
2250 ei->access_type = 0;
2251 ei->access_layer = 0;
2252 ei->access_protected = 0;
2253 ei->walk_to_action = 0;
2254 ei->smash_targets = 0;
2257 ei->can_explode_by_fire = FALSE;
2258 ei->can_explode_smashed = FALSE;
2259 ei->can_explode_impact = FALSE;
2261 ei->current_change_page = 0;
2264 if (IS_GROUP_ELEMENT(element) ||
2265 IS_INTERNAL_ELEMENT(element))
2267 struct ElementGroupInfo *group;
2269 // initialize memory for list of elements in group
2270 if (ei->group == NULL)
2271 ei->group = checked_malloc(sizeof(struct ElementGroupInfo));
2275 xx_group = *group; // copy group data into temporary buffer
2277 setConfigToDefaultsFromConfigList(chunk_config_GRPX);
2282 if (IS_EMPTY_ELEMENT(element) ||
2283 IS_INTERNAL_ELEMENT(element))
2285 xx_ei = *ei; // copy element data into temporary buffer
2287 setConfigToDefaultsFromConfigList(chunk_config_EMPX);
2293 clipboard_elements_initialized = TRUE;
2296 static void setLevelInfoToDefaults(struct LevelInfo *level,
2297 boolean level_info_only,
2298 boolean reset_file_status)
2300 setLevelInfoToDefaults_Level(level);
2302 if (!level_info_only)
2303 setLevelInfoToDefaults_Elements(level);
2305 if (reset_file_status)
2307 level->no_valid_file = FALSE;
2308 level->no_level_file = FALSE;
2311 level->changed = FALSE;
2314 static void setFileInfoToDefaults(struct LevelFileInfo *level_file_info)
2316 level_file_info->nr = 0;
2317 level_file_info->type = LEVEL_FILE_TYPE_UNKNOWN;
2318 level_file_info->packed = FALSE;
2320 setString(&level_file_info->basename, NULL);
2321 setString(&level_file_info->filename, NULL);
2324 int getMappedElement_SB(int, boolean);
2326 static void ActivateLevelTemplate(void)
2330 if (check_special_flags("load_xsb_to_ces"))
2332 // fill smaller playfields with padding "beyond border wall" elements
2333 if (level.fieldx < level_template.fieldx ||
2334 level.fieldy < level_template.fieldy)
2336 short field[level.fieldx][level.fieldy];
2337 int new_fieldx = MAX(level.fieldx, level_template.fieldx);
2338 int new_fieldy = MAX(level.fieldy, level_template.fieldy);
2339 int pos_fieldx = (new_fieldx - level.fieldx) / 2;
2340 int pos_fieldy = (new_fieldy - level.fieldy) / 2;
2342 // copy old playfield (which is smaller than the visible area)
2343 for (y = 0; y < level.fieldy; y++) for (x = 0; x < level.fieldx; x++)
2344 field[x][y] = level.field[x][y];
2346 // fill new, larger playfield with "beyond border wall" elements
2347 for (y = 0; y < new_fieldy; y++) for (x = 0; x < new_fieldx; x++)
2348 level.field[x][y] = getMappedElement_SB('_', TRUE);
2350 // copy the old playfield to the middle of the new playfield
2351 for (y = 0; y < level.fieldy; y++) for (x = 0; x < level.fieldx; x++)
2352 level.field[pos_fieldx + x][pos_fieldy + y] = field[x][y];
2354 level.fieldx = new_fieldx;
2355 level.fieldy = new_fieldy;
2359 // Currently there is no special action needed to activate the template
2360 // data, because 'element_info' property settings overwrite the original
2361 // level data, while all other variables do not change.
2363 // Exception: 'from_level_template' elements in the original level playfield
2364 // are overwritten with the corresponding elements at the same position in
2365 // playfield from the level template.
2367 for (x = 0; x < level.fieldx; x++)
2368 for (y = 0; y < level.fieldy; y++)
2369 if (level.field[x][y] == EL_FROM_LEVEL_TEMPLATE)
2370 level.field[x][y] = level_template.field[x][y];
2372 if (check_special_flags("load_xsb_to_ces"))
2374 struct LevelInfo level_backup = level;
2376 // overwrite all individual level settings from template level settings
2377 level = level_template;
2379 // restore level file info
2380 level.file_info = level_backup.file_info;
2382 // restore playfield size
2383 level.fieldx = level_backup.fieldx;
2384 level.fieldy = level_backup.fieldy;
2386 // restore playfield content
2387 for (x = 0; x < level.fieldx; x++)
2388 for (y = 0; y < level.fieldy; y++)
2389 level.field[x][y] = level_backup.field[x][y];
2391 // restore name and author from individual level
2392 strcpy(level.name, level_backup.name);
2393 strcpy(level.author, level_backup.author);
2395 // restore flag "use_custom_template"
2396 level.use_custom_template = level_backup.use_custom_template;
2400 static boolean checkForPackageFromBasename_BD(char *basename)
2402 // check for native BD level file extensions
2403 if (!strSuffixLower(basename, ".bd") &&
2404 !strSuffixLower(basename, ".bdr") &&
2405 !strSuffixLower(basename, ".brc") &&
2406 !strSuffixLower(basename, ".gds"))
2409 // check for standard single-level BD files (like "001.bd")
2410 if (strSuffixLower(basename, ".bd") &&
2411 strlen(basename) == 6 &&
2412 basename[0] >= '0' && basename[0] <= '9' &&
2413 basename[1] >= '0' && basename[1] <= '9' &&
2414 basename[2] >= '0' && basename[2] <= '9')
2417 // this is a level package in native BD file format
2421 static char *getLevelFilenameFromBasename(char *basename)
2423 static char *filename = NULL;
2425 checked_free(filename);
2427 filename = getPath2(getCurrentLevelDir(), basename);
2432 static int getFileTypeFromBasename(char *basename)
2434 // !!! ALSO SEE COMMENT IN checkForPackageFromBasename() !!!
2436 static char *filename = NULL;
2437 struct stat file_status;
2439 // ---------- try to determine file type from filename ----------
2441 // check for typical filename of a Supaplex level package file
2442 if (strlen(basename) == 10 && strPrefixLower(basename, "levels.d"))
2443 return LEVEL_FILE_TYPE_SP;
2445 // check for typical filename of a Diamond Caves II level package file
2446 if (strSuffixLower(basename, ".dc") ||
2447 strSuffixLower(basename, ".dc2"))
2448 return LEVEL_FILE_TYPE_DC;
2450 // check for typical filename of a Sokoban level package file
2451 if (strSuffixLower(basename, ".xsb") &&
2452 strchr(basename, '%') == NULL)
2453 return LEVEL_FILE_TYPE_SB;
2455 // check for typical filename of a Boulder Dash (GDash) level package file
2456 if (checkForPackageFromBasename_BD(basename))
2457 return LEVEL_FILE_TYPE_BD;
2459 // ---------- try to determine file type from filesize ----------
2461 checked_free(filename);
2462 filename = getPath2(getCurrentLevelDir(), basename);
2464 if (stat(filename, &file_status) == 0)
2466 // check for typical filesize of a Supaplex level package file
2467 if (file_status.st_size == 170496)
2468 return LEVEL_FILE_TYPE_SP;
2471 return LEVEL_FILE_TYPE_UNKNOWN;
2474 static int getFileTypeFromMagicBytes(char *filename, int type)
2478 if ((file = openFile(filename, MODE_READ)))
2480 char chunk_name[CHUNK_ID_LEN + 1];
2482 getFileChunkBE(file, chunk_name, NULL);
2484 if (strEqual(chunk_name, "MMII") ||
2485 strEqual(chunk_name, "MIRR"))
2486 type = LEVEL_FILE_TYPE_MM;
2494 static boolean checkForPackageFromBasename(char *basename)
2496 // !!! WON'T WORK ANYMORE IF getFileTypeFromBasename() ALSO DETECTS !!!
2497 // !!! SINGLE LEVELS (CURRENTLY ONLY DETECTS LEVEL PACKAGES !!!
2499 return (getFileTypeFromBasename(basename) != LEVEL_FILE_TYPE_UNKNOWN);
2502 static char *getSingleLevelBasenameExt(int nr, char *extension)
2504 static char basename[MAX_FILENAME_LEN];
2507 sprintf(basename, "%s", LEVELTEMPLATE_FILENAME);
2509 sprintf(basename, "%03d.%s", nr, extension);
2514 static char *getSingleLevelBasename(int nr)
2516 return getSingleLevelBasenameExt(nr, LEVELFILE_EXTENSION);
2519 static char *getPackedLevelBasename(int type)
2521 static char basename[MAX_FILENAME_LEN];
2522 char *directory = getCurrentLevelDir();
2524 DirectoryEntry *dir_entry;
2526 strcpy(basename, UNDEFINED_FILENAME); // default: undefined file
2528 if ((dir = openDirectory(directory)) == NULL)
2530 Warn("cannot read current level directory '%s'", directory);
2535 while ((dir_entry = readDirectory(dir)) != NULL) // loop all entries
2537 char *entry_basename = dir_entry->basename;
2538 int entry_type = getFileTypeFromBasename(entry_basename);
2540 if (entry_type != LEVEL_FILE_TYPE_UNKNOWN) // found valid level package
2542 if (type == LEVEL_FILE_TYPE_UNKNOWN ||
2545 strcpy(basename, entry_basename);
2552 closeDirectory(dir);
2557 static char *getSingleLevelFilename(int nr)
2559 return getLevelFilenameFromBasename(getSingleLevelBasename(nr));
2562 #if ENABLE_UNUSED_CODE
2563 static char *getPackedLevelFilename(int type)
2565 return getLevelFilenameFromBasename(getPackedLevelBasename(type));
2569 char *getDefaultLevelFilename(int nr)
2571 return getSingleLevelFilename(nr);
2574 #if ENABLE_UNUSED_CODE
2575 static void setLevelFileInfo_SingleLevelFilename(struct LevelFileInfo *lfi,
2579 lfi->packed = FALSE;
2581 setString(&lfi->basename, getSingleLevelBasename(lfi->nr, lfi->type));
2582 setString(&lfi->filename, getLevelFilenameFromBasename(lfi->basename));
2586 static void setLevelFileInfo_FormatLevelFilename(struct LevelFileInfo *lfi,
2587 int type, char *format, ...)
2589 static char basename[MAX_FILENAME_LEN];
2592 va_start(ap, format);
2593 vsprintf(basename, format, ap);
2597 lfi->packed = FALSE;
2599 setString(&lfi->basename, basename);
2600 setString(&lfi->filename, getLevelFilenameFromBasename(lfi->basename));
2603 static void setLevelFileInfo_PackedLevelFilename(struct LevelFileInfo *lfi,
2609 setString(&lfi->basename, getPackedLevelBasename(lfi->type));
2610 setString(&lfi->filename, getLevelFilenameFromBasename(lfi->basename));
2613 static int getFiletypeFromID(char *filetype_id)
2615 char *filetype_id_lower;
2616 int filetype = LEVEL_FILE_TYPE_UNKNOWN;
2619 if (filetype_id == NULL)
2620 return LEVEL_FILE_TYPE_UNKNOWN;
2622 filetype_id_lower = getStringToLower(filetype_id);
2624 for (i = 0; filetype_id_list[i].id != NULL; i++)
2626 char *id_lower = getStringToLower(filetype_id_list[i].id);
2628 if (strEqual(filetype_id_lower, id_lower))
2629 filetype = filetype_id_list[i].filetype;
2633 if (filetype != LEVEL_FILE_TYPE_UNKNOWN)
2637 free(filetype_id_lower);
2642 char *getLocalLevelTemplateFilename(void)
2644 return getDefaultLevelFilename(-1);
2647 char *getGlobalLevelTemplateFilename(void)
2649 // global variable "leveldir_current" must be modified in the loop below
2650 LevelDirTree *leveldir_current_last = leveldir_current;
2651 char *filename = NULL;
2653 // check for template level in path from current to topmost tree node
2655 while (leveldir_current != NULL)
2657 filename = getDefaultLevelFilename(-1);
2659 if (fileExists(filename))
2662 leveldir_current = leveldir_current->node_parent;
2665 // restore global variable "leveldir_current" modified in above loop
2666 leveldir_current = leveldir_current_last;
2671 static void determineLevelFileInfo_Filename(struct LevelFileInfo *lfi)
2675 // special case: level number is negative => check for level template file
2678 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
2679 getSingleLevelBasename(-1));
2681 // replace local level template filename with global template filename
2682 setString(&lfi->filename, getGlobalLevelTemplateFilename());
2684 // no fallback if template file not existing
2688 // special case: check for file name/pattern specified in "levelinfo.conf"
2689 if (leveldir_current->level_filename != NULL)
2691 int filetype = getFiletypeFromID(leveldir_current->level_filetype);
2693 setLevelFileInfo_FormatLevelFilename(lfi, filetype,
2694 leveldir_current->level_filename, nr);
2696 lfi->packed = checkForPackageFromBasename(leveldir_current->level_filename);
2698 if (fileExists(lfi->filename))
2701 else if (leveldir_current->level_filetype != NULL)
2703 int filetype = getFiletypeFromID(leveldir_current->level_filetype);
2705 // check for specified native level file with standard file name
2706 setLevelFileInfo_FormatLevelFilename(lfi, filetype,
2707 "%03d.%s", nr, LEVELFILE_EXTENSION);
2708 if (fileExists(lfi->filename))
2712 // check for native Rocks'n'Diamonds level file
2713 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
2714 "%03d.%s", nr, LEVELFILE_EXTENSION);
2715 if (fileExists(lfi->filename))
2718 // check for native Boulder Dash level file
2719 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_BD, "%03d.bd", nr);
2720 if (fileExists(lfi->filename))
2723 // check for Emerald Mine level file (V1)
2724 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "a%c%c",
2725 'a' + (nr / 10) % 26, '0' + nr % 10);
2726 if (fileExists(lfi->filename))
2728 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "A%c%c",
2729 'A' + (nr / 10) % 26, '0' + nr % 10);
2730 if (fileExists(lfi->filename))
2733 // check for Emerald Mine level file (V2 to V5)
2734 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%d", nr);
2735 if (fileExists(lfi->filename))
2738 // check for Emerald Mine level file (V6 / single mode)
2739 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02ds", nr);
2740 if (fileExists(lfi->filename))
2742 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02dS", nr);
2743 if (fileExists(lfi->filename))
2746 // check for Emerald Mine level file (V6 / teamwork mode)
2747 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02dt", nr);
2748 if (fileExists(lfi->filename))
2750 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02dT", nr);
2751 if (fileExists(lfi->filename))
2754 // check for various packed level file formats
2755 setLevelFileInfo_PackedLevelFilename(lfi, LEVEL_FILE_TYPE_UNKNOWN);
2756 if (fileExists(lfi->filename))
2759 // no known level file found -- use default values (and fail later)
2760 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
2761 "%03d.%s", nr, LEVELFILE_EXTENSION);
2764 static void determineLevelFileInfo_Filetype(struct LevelFileInfo *lfi)
2766 if (lfi->type == LEVEL_FILE_TYPE_UNKNOWN)
2767 lfi->type = getFileTypeFromBasename(lfi->basename);
2769 if (lfi->type == LEVEL_FILE_TYPE_RND)
2770 lfi->type = getFileTypeFromMagicBytes(lfi->filename, lfi->type);
2773 static void setLevelFileInfo(struct LevelFileInfo *level_file_info, int nr)
2775 // always start with reliable default values
2776 setFileInfoToDefaults(level_file_info);
2778 level_file_info->nr = nr; // set requested level number
2780 determineLevelFileInfo_Filename(level_file_info);
2781 determineLevelFileInfo_Filetype(level_file_info);
2784 static void copyLevelFileInfo(struct LevelFileInfo *lfi_from,
2785 struct LevelFileInfo *lfi_to)
2787 lfi_to->nr = lfi_from->nr;
2788 lfi_to->type = lfi_from->type;
2789 lfi_to->packed = lfi_from->packed;
2791 setString(&lfi_to->basename, lfi_from->basename);
2792 setString(&lfi_to->filename, lfi_from->filename);
2795 // ----------------------------------------------------------------------------
2796 // functions for loading R'n'D level
2797 // ----------------------------------------------------------------------------
2799 int getMappedElement(int element)
2801 // remap some (historic, now obsolete) elements
2805 case EL_PLAYER_OBSOLETE:
2806 element = EL_PLAYER_1;
2809 case EL_KEY_OBSOLETE:
2813 case EL_EM_KEY_1_FILE_OBSOLETE:
2814 element = EL_EM_KEY_1;
2817 case EL_EM_KEY_2_FILE_OBSOLETE:
2818 element = EL_EM_KEY_2;
2821 case EL_EM_KEY_3_FILE_OBSOLETE:
2822 element = EL_EM_KEY_3;
2825 case EL_EM_KEY_4_FILE_OBSOLETE:
2826 element = EL_EM_KEY_4;
2829 case EL_ENVELOPE_OBSOLETE:
2830 element = EL_ENVELOPE_1;
2838 if (element >= NUM_FILE_ELEMENTS)
2840 Warn("invalid level element %d", element);
2842 element = EL_UNKNOWN;
2850 static int getMappedElementByVersion(int element, int game_version)
2852 // remap some elements due to certain game version
2854 if (game_version <= VERSION_IDENT(2,2,0,0))
2856 // map game font elements
2857 element = (element == EL_CHAR('[') ? EL_CHAR_AUMLAUT :
2858 element == EL_CHAR('\\') ? EL_CHAR_OUMLAUT :
2859 element == EL_CHAR(']') ? EL_CHAR_UUMLAUT :
2860 element == EL_CHAR('^') ? EL_CHAR_COPYRIGHT : element);
2863 if (game_version < VERSION_IDENT(3,0,0,0))
2865 // map Supaplex gravity tube elements
2866 element = (element == EL_SP_GRAVITY_PORT_LEFT ? EL_SP_PORT_LEFT :
2867 element == EL_SP_GRAVITY_PORT_RIGHT ? EL_SP_PORT_RIGHT :
2868 element == EL_SP_GRAVITY_PORT_UP ? EL_SP_PORT_UP :
2869 element == EL_SP_GRAVITY_PORT_DOWN ? EL_SP_PORT_DOWN :
2876 static int LoadLevel_VERS(File *file, int chunk_size, struct LevelInfo *level)
2878 level->file_version = getFileVersion(file);
2879 level->game_version = getFileVersion(file);
2884 static int LoadLevel_DATE(File *file, int chunk_size, struct LevelInfo *level)
2886 level->creation_date.year = getFile16BitBE(file);
2887 level->creation_date.month = getFile8Bit(file);
2888 level->creation_date.day = getFile8Bit(file);
2890 level->creation_date.src = DATE_SRC_LEVELFILE;
2895 static int LoadLevel_HEAD(File *file, int chunk_size, struct LevelInfo *level)
2897 int initial_player_stepsize;
2898 int initial_player_gravity;
2901 level->fieldx = getFile8Bit(file);
2902 level->fieldy = getFile8Bit(file);
2904 level->time = getFile16BitBE(file);
2905 level->gems_needed = getFile16BitBE(file);
2907 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
2908 level->name[i] = getFile8Bit(file);
2909 level->name[MAX_LEVEL_NAME_LEN] = 0;
2911 for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
2912 level->score[i] = getFile8Bit(file);
2914 level->num_yamyam_contents = STD_ELEMENT_CONTENTS;
2915 for (i = 0; i < STD_ELEMENT_CONTENTS; i++)
2916 for (y = 0; y < 3; y++)
2917 for (x = 0; x < 3; x++)
2918 level->yamyam_content[i].e[x][y] = getMappedElement(getFile8Bit(file));
2920 level->amoeba_speed = getFile8Bit(file);
2921 level->time_magic_wall = getFile8Bit(file);
2922 level->time_wheel = getFile8Bit(file);
2923 level->amoeba_content = getMappedElement(getFile8Bit(file));
2925 initial_player_stepsize = (getFile8Bit(file) == 1 ? STEPSIZE_FAST :
2928 for (i = 0; i < MAX_PLAYERS; i++)
2929 level->initial_player_stepsize[i] = initial_player_stepsize;
2931 initial_player_gravity = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2933 for (i = 0; i < MAX_PLAYERS; i++)
2934 level->initial_player_gravity[i] = initial_player_gravity;
2936 level->encoding_16bit_field = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2937 level->em_slippery_gems = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2939 level->use_custom_template = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2941 level->block_last_field = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2942 level->sp_block_last_field = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2943 level->can_move_into_acid_bits = getFile32BitBE(file);
2944 level->dont_collide_with_bits = getFile8Bit(file);
2946 level->use_spring_bug = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2947 level->use_step_counter = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2949 level->instant_relocation = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2950 level->can_pass_to_walkable = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2951 level->grow_into_diggable = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2953 level->game_engine_type = getFile8Bit(file);
2955 ReadUnusedBytesFromFile(file, LEVEL_CHUNK_HEAD_UNUSED);
2960 static int LoadLevel_NAME(File *file, int chunk_size, struct LevelInfo *level)
2964 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
2965 level->name[i] = getFile8Bit(file);
2966 level->name[MAX_LEVEL_NAME_LEN] = 0;
2971 static int LoadLevel_AUTH(File *file, int chunk_size, struct LevelInfo *level)
2975 for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
2976 level->author[i] = getFile8Bit(file);
2977 level->author[MAX_LEVEL_AUTHOR_LEN] = 0;
2982 static int LoadLevel_BODY(File *file, int chunk_size, struct LevelInfo *level)
2985 int chunk_size_expected = level->fieldx * level->fieldy;
2987 /* Note: "chunk_size" was wrong before version 2.0 when elements are
2988 stored with 16-bit encoding (and should be twice as big then).
2989 Even worse, playfield data was stored 16-bit when only yamyam content
2990 contained 16-bit elements and vice versa. */
2992 if (level->encoding_16bit_field && level->file_version >= FILE_VERSION_2_0)
2993 chunk_size_expected *= 2;
2995 if (chunk_size_expected != chunk_size)
2997 ReadUnusedBytesFromFile(file, chunk_size);
2998 return chunk_size_expected;
3001 for (y = 0; y < level->fieldy; y++)
3002 for (x = 0; x < level->fieldx; x++)
3003 level->field[x][y] =
3004 getMappedElement(level->encoding_16bit_field ? getFile16BitBE(file) :
3009 static int LoadLevel_CONT(File *file, int chunk_size, struct LevelInfo *level)
3012 int header_size = 4;
3013 int content_size = MAX_ELEMENT_CONTENTS * 3 * 3;
3014 int chunk_size_expected = header_size + content_size;
3016 /* Note: "chunk_size" was wrong before version 2.0 when elements are
3017 stored with 16-bit encoding (and should be twice as big then).
3018 Even worse, playfield data was stored 16-bit when only yamyam content
3019 contained 16-bit elements and vice versa. */
3021 if (level->encoding_16bit_field && level->file_version >= FILE_VERSION_2_0)
3022 chunk_size_expected += content_size;
3024 if (chunk_size_expected != chunk_size)
3026 ReadUnusedBytesFromFile(file, chunk_size);
3027 return chunk_size_expected;
3031 level->num_yamyam_contents = getFile8Bit(file);
3035 // correct invalid number of content fields -- should never happen
3036 if (level->num_yamyam_contents < 1 ||
3037 level->num_yamyam_contents > MAX_ELEMENT_CONTENTS)
3038 level->num_yamyam_contents = STD_ELEMENT_CONTENTS;
3040 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
3041 for (y = 0; y < 3; y++)
3042 for (x = 0; x < 3; x++)
3043 level->yamyam_content[i].e[x][y] =
3044 getMappedElement(level->encoding_16bit_field ?
3045 getFile16BitBE(file) : getFile8Bit(file));
3049 static int LoadLevel_CNT2(File *file, int chunk_size, struct LevelInfo *level)
3054 int content_array[MAX_ELEMENT_CONTENTS][3][3];
3056 element = getMappedElement(getFile16BitBE(file));
3057 num_contents = getFile8Bit(file);
3059 getFile8Bit(file); // content x size (unused)
3060 getFile8Bit(file); // content y size (unused)
3062 ReadUnusedBytesFromFile(file, LEVEL_CHUNK_CNT2_UNUSED);
3064 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
3065 for (y = 0; y < 3; y++)
3066 for (x = 0; x < 3; x++)
3067 content_array[i][x][y] = getMappedElement(getFile16BitBE(file));
3069 // correct invalid number of content fields -- should never happen
3070 if (num_contents < 1 || num_contents > MAX_ELEMENT_CONTENTS)
3071 num_contents = STD_ELEMENT_CONTENTS;
3073 if (element == EL_YAMYAM)
3075 level->num_yamyam_contents = num_contents;
3077 for (i = 0; i < num_contents; i++)
3078 for (y = 0; y < 3; y++)
3079 for (x = 0; x < 3; x++)
3080 level->yamyam_content[i].e[x][y] = content_array[i][x][y];
3082 else if (element == EL_BD_AMOEBA)
3084 level->amoeba_content = content_array[0][0][0];
3088 Warn("cannot load content for element '%d'", element);
3094 static int LoadLevel_CNT3(File *file, int chunk_size, struct LevelInfo *level)
3100 int chunk_size_expected;
3102 element = getMappedElement(getFile16BitBE(file));
3103 if (!IS_ENVELOPE(element))
3104 element = EL_ENVELOPE_1;
3106 envelope_nr = element - EL_ENVELOPE_1;
3108 envelope_len = getFile16BitBE(file);
3110 level->envelope[envelope_nr].xsize = getFile8Bit(file);
3111 level->envelope[envelope_nr].ysize = getFile8Bit(file);
3113 ReadUnusedBytesFromFile(file, LEVEL_CHUNK_CNT3_UNUSED);
3115 chunk_size_expected = LEVEL_CHUNK_CNT3_SIZE(envelope_len);
3116 if (chunk_size_expected != chunk_size)
3118 ReadUnusedBytesFromFile(file, chunk_size - LEVEL_CHUNK_CNT3_HEADER);
3119 return chunk_size_expected;
3122 for (i = 0; i < envelope_len; i++)
3123 level->envelope[envelope_nr].text[i] = getFile8Bit(file);
3128 static int LoadLevel_CUS1(File *file, int chunk_size, struct LevelInfo *level)
3130 int num_changed_custom_elements = getFile16BitBE(file);
3131 int chunk_size_expected = 2 + num_changed_custom_elements * 6;
3134 if (chunk_size_expected != chunk_size)
3136 ReadUnusedBytesFromFile(file, chunk_size - 2);
3137 return chunk_size_expected;
3140 for (i = 0; i < num_changed_custom_elements; i++)
3142 int element = getMappedElement(getFile16BitBE(file));
3143 int properties = getFile32BitBE(file);
3145 if (IS_CUSTOM_ELEMENT(element))
3146 element_info[element].properties[EP_BITFIELD_BASE_NR] = properties;
3148 Warn("invalid custom element number %d", element);
3150 // older game versions that wrote level files with CUS1 chunks used
3151 // different default push delay values (not yet stored in level file)
3152 element_info[element].push_delay_fixed = 2;
3153 element_info[element].push_delay_random = 8;
3156 level->file_has_custom_elements = TRUE;
3161 static int LoadLevel_CUS2(File *file, int chunk_size, struct LevelInfo *level)
3163 int num_changed_custom_elements = getFile16BitBE(file);
3164 int chunk_size_expected = 2 + num_changed_custom_elements * 4;
3167 if (chunk_size_expected != chunk_size)
3169 ReadUnusedBytesFromFile(file, chunk_size - 2);
3170 return chunk_size_expected;
3173 for (i = 0; i < num_changed_custom_elements; i++)
3175 int element = getMappedElement(getFile16BitBE(file));
3176 int custom_target_element = getMappedElement(getFile16BitBE(file));
3178 if (IS_CUSTOM_ELEMENT(element))
3179 element_info[element].change->target_element = custom_target_element;
3181 Warn("invalid custom element number %d", element);
3184 level->file_has_custom_elements = TRUE;
3189 static int LoadLevel_CUS3(File *file, int chunk_size, struct LevelInfo *level)
3191 int num_changed_custom_elements = getFile16BitBE(file);
3192 int chunk_size_expected = LEVEL_CHUNK_CUS3_SIZE(num_changed_custom_elements);
3195 if (chunk_size_expected != chunk_size)
3197 ReadUnusedBytesFromFile(file, chunk_size - 2);
3198 return chunk_size_expected;
3201 for (i = 0; i < num_changed_custom_elements; i++)
3203 int element = getMappedElement(getFile16BitBE(file));
3204 struct ElementInfo *ei = &element_info[element];
3205 unsigned int event_bits;
3207 if (!IS_CUSTOM_ELEMENT(element))
3209 Warn("invalid custom element number %d", element);
3211 element = EL_INTERNAL_DUMMY;
3214 for (j = 0; j < MAX_ELEMENT_NAME_LEN; j++)
3215 ei->description[j] = getFile8Bit(file);
3216 ei->description[MAX_ELEMENT_NAME_LEN] = 0;
3218 ei->properties[EP_BITFIELD_BASE_NR] = getFile32BitBE(file);
3220 // some free bytes for future properties and padding
3221 ReadUnusedBytesFromFile(file, 7);
3223 ei->use_gfx_element = getFile8Bit(file);
3224 ei->gfx_element_initial = getMappedElement(getFile16BitBE(file));
3226 ei->collect_score_initial = getFile8Bit(file);
3227 ei->collect_count_initial = getFile8Bit(file);
3229 ei->push_delay_fixed = getFile16BitBE(file);
3230 ei->push_delay_random = getFile16BitBE(file);
3231 ei->move_delay_fixed = getFile16BitBE(file);
3232 ei->move_delay_random = getFile16BitBE(file);
3234 ei->move_pattern = getFile16BitBE(file);
3235 ei->move_direction_initial = getFile8Bit(file);
3236 ei->move_stepsize = getFile8Bit(file);
3238 for (y = 0; y < 3; y++)
3239 for (x = 0; x < 3; x++)
3240 ei->content.e[x][y] = getMappedElement(getFile16BitBE(file));
3242 // bits 0 - 31 of "has_event[]"
3243 event_bits = getFile32BitBE(file);
3244 for (j = 0; j < MIN(NUM_CHANGE_EVENTS, 32); j++)
3245 if (event_bits & (1u << j))
3246 ei->change->has_event[j] = TRUE;
3248 ei->change->target_element = getMappedElement(getFile16BitBE(file));
3250 ei->change->delay_fixed = getFile16BitBE(file);
3251 ei->change->delay_random = getFile16BitBE(file);
3252 ei->change->delay_frames = getFile16BitBE(file);
3254 ei->change->initial_trigger_element = getMappedElement(getFile16BitBE(file));
3256 ei->change->explode = getFile8Bit(file);
3257 ei->change->use_target_content = getFile8Bit(file);
3258 ei->change->only_if_complete = getFile8Bit(file);
3259 ei->change->use_random_replace = getFile8Bit(file);
3261 ei->change->random_percentage = getFile8Bit(file);
3262 ei->change->replace_when = getFile8Bit(file);
3264 for (y = 0; y < 3; y++)
3265 for (x = 0; x < 3; x++)
3266 ei->change->target_content.e[x][y] =
3267 getMappedElement(getFile16BitBE(file));
3269 ei->slippery_type = getFile8Bit(file);
3271 // some free bytes for future properties and padding
3272 ReadUnusedBytesFromFile(file, LEVEL_CPART_CUS3_UNUSED);
3274 // mark that this custom element has been modified
3275 ei->modified_settings = TRUE;
3278 level->file_has_custom_elements = TRUE;
3283 static int LoadLevel_CUS4(File *file, int chunk_size, struct LevelInfo *level)
3285 struct ElementInfo *ei;
3286 int chunk_size_expected;
3290 // ---------- custom element base property values (96 bytes) ----------------
3292 element = getMappedElement(getFile16BitBE(file));
3294 if (!IS_CUSTOM_ELEMENT(element))
3296 Warn("invalid custom element number %d", element);
3298 ReadUnusedBytesFromFile(file, chunk_size - 2);
3303 ei = &element_info[element];
3305 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
3306 ei->description[i] = getFile8Bit(file);
3307 ei->description[MAX_ELEMENT_NAME_LEN] = 0;
3309 ei->properties[EP_BITFIELD_BASE_NR] = getFile32BitBE(file);
3311 ReadUnusedBytesFromFile(file, 4); // reserved for more base properties
3313 ei->num_change_pages = getFile8Bit(file);
3315 chunk_size_expected = LEVEL_CHUNK_CUS4_SIZE(ei->num_change_pages);
3316 if (chunk_size_expected != chunk_size)
3318 ReadUnusedBytesFromFile(file, chunk_size - 43);
3319 return chunk_size_expected;
3322 ei->ce_value_fixed_initial = getFile16BitBE(file);
3323 ei->ce_value_random_initial = getFile16BitBE(file);
3324 ei->use_last_ce_value = getFile8Bit(file);
3326 ei->use_gfx_element = getFile8Bit(file);
3327 ei->gfx_element_initial = getMappedElement(getFile16BitBE(file));
3329 ei->collect_score_initial = getFile8Bit(file);
3330 ei->collect_count_initial = getFile8Bit(file);
3332 ei->drop_delay_fixed = getFile8Bit(file);
3333 ei->push_delay_fixed = getFile8Bit(file);
3334 ei->drop_delay_random = getFile8Bit(file);
3335 ei->push_delay_random = getFile8Bit(file);
3336 ei->move_delay_fixed = getFile16BitBE(file);
3337 ei->move_delay_random = getFile16BitBE(file);
3339 // bits 0 - 15 of "move_pattern" ...
3340 ei->move_pattern = getFile16BitBE(file);
3341 ei->move_direction_initial = getFile8Bit(file);
3342 ei->move_stepsize = getFile8Bit(file);
3344 ei->slippery_type = getFile8Bit(file);
3346 for (y = 0; y < 3; y++)
3347 for (x = 0; x < 3; x++)
3348 ei->content.e[x][y] = getMappedElement(getFile16BitBE(file));
3350 ei->move_enter_element = getMappedElement(getFile16BitBE(file));
3351 ei->move_leave_element = getMappedElement(getFile16BitBE(file));
3352 ei->move_leave_type = getFile8Bit(file);
3354 // ... bits 16 - 31 of "move_pattern" (not nice, but downward compatible)
3355 ei->move_pattern |= (getFile16BitBE(file) << 16);
3357 ei->access_direction = getFile8Bit(file);
3359 ei->explosion_delay = getFile8Bit(file);
3360 ei->ignition_delay = getFile8Bit(file);
3361 ei->explosion_type = getFile8Bit(file);
3363 // some free bytes for future custom property values and padding
3364 ReadUnusedBytesFromFile(file, 1);
3366 // ---------- change page property values (48 bytes) ------------------------
3368 setElementChangePages(ei, ei->num_change_pages);
3370 for (i = 0; i < ei->num_change_pages; i++)
3372 struct ElementChangeInfo *change = &ei->change_page[i];
3373 unsigned int event_bits;
3375 // always start with reliable default values
3376 setElementChangeInfoToDefaults(change);
3378 // bits 0 - 31 of "has_event[]" ...
3379 event_bits = getFile32BitBE(file);
3380 for (j = 0; j < MIN(NUM_CHANGE_EVENTS, 32); j++)
3381 if (event_bits & (1u << j))
3382 change->has_event[j] = TRUE;
3384 change->target_element = getMappedElement(getFile16BitBE(file));
3386 change->delay_fixed = getFile16BitBE(file);
3387 change->delay_random = getFile16BitBE(file);
3388 change->delay_frames = getFile16BitBE(file);
3390 change->initial_trigger_element = getMappedElement(getFile16BitBE(file));
3392 change->explode = getFile8Bit(file);
3393 change->use_target_content = getFile8Bit(file);
3394 change->only_if_complete = getFile8Bit(file);
3395 change->use_random_replace = getFile8Bit(file);
3397 change->random_percentage = getFile8Bit(file);
3398 change->replace_when = getFile8Bit(file);
3400 for (y = 0; y < 3; y++)
3401 for (x = 0; x < 3; x++)
3402 change->target_content.e[x][y]= getMappedElement(getFile16BitBE(file));
3404 change->can_change = getFile8Bit(file);
3406 change->trigger_side = getFile8Bit(file);
3408 change->trigger_player = getFile8Bit(file);
3409 change->trigger_page = getFile8Bit(file);
3411 change->trigger_page = (change->trigger_page == CH_PAGE_ANY_FILE ?
3412 CH_PAGE_ANY : (1 << change->trigger_page));
3414 change->has_action = getFile8Bit(file);
3415 change->action_type = getFile8Bit(file);
3416 change->action_mode = getFile8Bit(file);
3417 change->action_arg = getFile16BitBE(file);
3419 // ... bits 32 - 39 of "has_event[]" (not nice, but downward compatible)
3420 event_bits = getFile8Bit(file);
3421 for (j = 32; j < NUM_CHANGE_EVENTS; j++)
3422 if (event_bits & (1u << (j - 32)))
3423 change->has_event[j] = TRUE;
3426 // mark this custom element as modified
3427 ei->modified_settings = TRUE;
3429 level->file_has_custom_elements = TRUE;
3434 static int LoadLevel_GRP1(File *file, int chunk_size, struct LevelInfo *level)
3436 struct ElementInfo *ei;
3437 struct ElementGroupInfo *group;
3441 element = getMappedElement(getFile16BitBE(file));
3443 if (!IS_GROUP_ELEMENT(element))
3445 Warn("invalid group element number %d", element);
3447 ReadUnusedBytesFromFile(file, chunk_size - 2);
3452 ei = &element_info[element];
3454 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
3455 ei->description[i] = getFile8Bit(file);
3456 ei->description[MAX_ELEMENT_NAME_LEN] = 0;
3458 group = element_info[element].group;
3460 group->num_elements = getFile8Bit(file);
3462 ei->use_gfx_element = getFile8Bit(file);
3463 ei->gfx_element_initial = getMappedElement(getFile16BitBE(file));
3465 group->choice_mode = getFile8Bit(file);
3467 // some free bytes for future values and padding
3468 ReadUnusedBytesFromFile(file, 3);
3470 for (i = 0; i < MAX_ELEMENTS_IN_GROUP; i++)
3471 group->element[i] = getMappedElement(getFile16BitBE(file));
3473 // mark this group element as modified
3474 element_info[element].modified_settings = TRUE;
3476 level->file_has_custom_elements = TRUE;
3481 static int LoadLevel_MicroChunk(File *file, struct LevelFileConfigInfo *conf,
3482 int element, int real_element)
3484 int micro_chunk_size = 0;
3485 int conf_type = getFile8Bit(file);
3486 int byte_mask = conf_type & CONF_MASK_BYTES;
3487 boolean element_found = FALSE;
3490 micro_chunk_size += 1;
3492 if (byte_mask == CONF_MASK_MULTI_BYTES)
3494 int num_bytes = getFile16BitBE(file);
3495 byte *buffer = checked_malloc(num_bytes);
3497 ReadBytesFromFile(file, buffer, num_bytes);
3499 for (i = 0; conf[i].data_type != -1; i++)
3501 if (conf[i].element == element &&
3502 conf[i].conf_type == conf_type)
3504 int data_type = conf[i].data_type;
3505 int num_entities = num_bytes / CONF_ENTITY_NUM_BYTES(data_type);
3506 int max_num_entities = conf[i].max_num_entities;
3508 if (num_entities > max_num_entities)
3510 Warn("truncating number of entities for element %d from %d to %d",
3511 element, num_entities, max_num_entities);
3513 num_entities = max_num_entities;
3516 if (num_entities == 0 && (data_type == TYPE_ELEMENT_LIST ||
3517 data_type == TYPE_CONTENT_LIST))
3519 // for element and content lists, zero entities are not allowed
3520 Warn("found empty list of entities for element %d", element);
3522 // do not set "num_entities" here to prevent reading behind buffer
3524 *(int *)(conf[i].num_entities) = 1; // at least one is required
3528 *(int *)(conf[i].num_entities) = num_entities;
3531 element_found = TRUE;
3533 if (data_type == TYPE_STRING)
3535 char *string = (char *)(conf[i].value);
3538 for (j = 0; j < max_num_entities; j++)
3539 string[j] = (j < num_entities ? buffer[j] : '\0');
3541 else if (data_type == TYPE_ELEMENT_LIST)
3543 int *element_array = (int *)(conf[i].value);
3546 for (j = 0; j < num_entities; j++)
3548 getMappedElement(CONF_ELEMENTS_ELEMENT(buffer, j));
3550 else if (data_type == TYPE_CONTENT_LIST)
3552 struct Content *content= (struct Content *)(conf[i].value);
3555 for (c = 0; c < num_entities; c++)
3556 for (y = 0; y < 3; y++)
3557 for (x = 0; x < 3; x++)
3558 content[c].e[x][y] =
3559 getMappedElement(CONF_CONTENTS_ELEMENT(buffer, c, x, y));
3562 element_found = FALSE;
3568 checked_free(buffer);
3570 micro_chunk_size += 2 + num_bytes;
3572 else // constant size configuration data (1, 2 or 4 bytes)
3574 int value = (byte_mask == CONF_MASK_1_BYTE ? getFile8Bit (file) :
3575 byte_mask == CONF_MASK_2_BYTE ? getFile16BitBE(file) :
3576 byte_mask == CONF_MASK_4_BYTE ? getFile32BitBE(file) : 0);
3578 for (i = 0; conf[i].data_type != -1; i++)
3580 if (conf[i].element == element &&
3581 conf[i].conf_type == conf_type)
3583 int data_type = conf[i].data_type;
3585 if (data_type == TYPE_ELEMENT)
3586 value = getMappedElement(value);
3588 if (data_type == TYPE_BOOLEAN)
3589 *(boolean *)(conf[i].value) = (value ? TRUE : FALSE);
3591 *(int *) (conf[i].value) = value;
3593 element_found = TRUE;
3599 micro_chunk_size += CONF_VALUE_NUM_BYTES(byte_mask);
3604 char *error_conf_chunk_bytes =
3605 (byte_mask == CONF_MASK_1_BYTE ? "CONF_VALUE_8_BIT" :
3606 byte_mask == CONF_MASK_2_BYTE ? "CONF_VALUE_16_BIT" :
3607 byte_mask == CONF_MASK_4_BYTE ? "CONF_VALUE_32_BIT" :"CONF_VALUE_BYTES");
3608 int error_conf_chunk_token = conf_type & CONF_MASK_TOKEN;
3609 int error_element = real_element;
3611 Warn("cannot load micro chunk '%s(%d)' value for element %d ['%s']",
3612 error_conf_chunk_bytes, error_conf_chunk_token,
3613 error_element, EL_NAME(error_element));
3616 return micro_chunk_size;
3619 static int LoadLevel_INFO(File *file, int chunk_size, struct LevelInfo *level)
3621 int real_chunk_size = 0;
3623 li = *level; // copy level data into temporary buffer
3625 while (!checkEndOfFile(file))
3627 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_INFO, -1, -1);
3629 if (real_chunk_size >= chunk_size)
3633 *level = li; // copy temporary buffer back to level data
3635 return real_chunk_size;
3638 static int LoadLevel_CONF(File *file, int chunk_size, struct LevelInfo *level)
3640 int real_chunk_size = 0;
3642 li = *level; // copy level data into temporary buffer
3644 while (!checkEndOfFile(file))
3646 int element = getMappedElement(getFile16BitBE(file));
3648 real_chunk_size += 2;
3649 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_CONF,
3651 if (real_chunk_size >= chunk_size)
3655 *level = li; // copy temporary buffer back to level data
3657 return real_chunk_size;
3660 static int LoadLevel_ELEM(File *file, int chunk_size, struct LevelInfo *level)
3662 int real_chunk_size = 0;
3664 li = *level; // copy level data into temporary buffer
3666 while (!checkEndOfFile(file))
3668 int element = getMappedElement(getFile16BitBE(file));
3670 real_chunk_size += 2;
3671 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_ELEM,
3673 if (real_chunk_size >= chunk_size)
3677 *level = li; // copy temporary buffer back to level data
3679 return real_chunk_size;
3682 static int LoadLevel_NOTE(File *file, int chunk_size, struct LevelInfo *level)
3684 int element = getMappedElement(getFile16BitBE(file));
3685 int envelope_nr = element - EL_ENVELOPE_1;
3686 int real_chunk_size = 2;
3688 xx_envelope = level->envelope[envelope_nr]; // copy into temporary buffer
3690 while (!checkEndOfFile(file))
3692 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_NOTE,
3695 if (real_chunk_size >= chunk_size)
3699 level->envelope[envelope_nr] = xx_envelope; // copy from temporary buffer
3701 return real_chunk_size;
3704 static int LoadLevel_CUSX(File *file, int chunk_size, struct LevelInfo *level)
3706 int element = getMappedElement(getFile16BitBE(file));
3707 int real_chunk_size = 2;
3708 struct ElementInfo *ei = &element_info[element];
3711 xx_ei = *ei; // copy element data into temporary buffer
3713 xx_ei.num_change_pages = -1;
3715 while (!checkEndOfFile(file))
3717 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_CUSX_base,
3719 if (xx_ei.num_change_pages != -1)
3722 if (real_chunk_size >= chunk_size)
3728 if (ei->num_change_pages == -1)
3730 Warn("LoadLevel_CUSX(): missing 'num_change_pages' for '%s'",
3733 ei->num_change_pages = 1;
3735 setElementChangePages(ei, 1);
3736 setElementChangeInfoToDefaults(ei->change);
3738 return real_chunk_size;
3741 // initialize number of change pages stored for this custom element
3742 setElementChangePages(ei, ei->num_change_pages);
3743 for (i = 0; i < ei->num_change_pages; i++)
3744 setElementChangeInfoToDefaults(&ei->change_page[i]);
3746 // start with reading properties for the first change page
3747 xx_current_change_page = 0;
3749 while (!checkEndOfFile(file))
3751 // level file might contain invalid change page number
3752 if (xx_current_change_page >= ei->num_change_pages)
3755 struct ElementChangeInfo *change = &ei->change_page[xx_current_change_page];
3757 xx_change = *change; // copy change data into temporary buffer
3759 resetEventBits(); // reset bits; change page might have changed
3761 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_CUSX_change,
3764 *change = xx_change;
3766 setEventFlagsFromEventBits(change);
3768 if (real_chunk_size >= chunk_size)
3772 level->file_has_custom_elements = TRUE;
3774 return real_chunk_size;
3777 static int LoadLevel_GRPX(File *file, int chunk_size, struct LevelInfo *level)
3779 int element = getMappedElement(getFile16BitBE(file));
3780 int real_chunk_size = 2;
3781 struct ElementInfo *ei = &element_info[element];
3782 struct ElementGroupInfo *group = ei->group;
3787 xx_ei = *ei; // copy element data into temporary buffer
3788 xx_group = *group; // copy group data into temporary buffer
3790 while (!checkEndOfFile(file))
3792 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_GRPX,
3795 if (real_chunk_size >= chunk_size)
3802 level->file_has_custom_elements = TRUE;
3804 return real_chunk_size;
3807 static int LoadLevel_EMPX(File *file, int chunk_size, struct LevelInfo *level)
3809 int element = getMappedElement(getFile16BitBE(file));
3810 int real_chunk_size = 2;
3811 struct ElementInfo *ei = &element_info[element];
3813 xx_ei = *ei; // copy element data into temporary buffer
3815 while (!checkEndOfFile(file))
3817 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_EMPX,
3820 if (real_chunk_size >= chunk_size)
3826 level->file_has_custom_elements = TRUE;
3828 return real_chunk_size;
3831 static void LoadLevelFromFileInfo_RND(struct LevelInfo *level,
3832 struct LevelFileInfo *level_file_info,
3833 boolean level_info_only)
3835 char *filename = level_file_info->filename;
3836 char cookie[MAX_LINE_LEN];
3837 char chunk_name[CHUNK_ID_LEN + 1];
3841 if (!(file = openFile(filename, MODE_READ)))
3843 level->no_valid_file = TRUE;
3844 level->no_level_file = TRUE;
3846 if (level_info_only)
3849 Warn("cannot read level '%s' -- using empty level", filename);
3851 if (!setup.editor.use_template_for_new_levels)
3854 // if level file not found, try to initialize level data from template
3855 filename = getGlobalLevelTemplateFilename();
3857 if (!(file = openFile(filename, MODE_READ)))
3860 // default: for empty levels, use level template for custom elements
3861 level->use_custom_template = TRUE;
3863 level->no_valid_file = FALSE;
3866 getFileChunkBE(file, chunk_name, NULL);
3867 if (strEqual(chunk_name, "RND1"))
3869 getFile32BitBE(file); // not used
3871 getFileChunkBE(file, chunk_name, NULL);
3872 if (!strEqual(chunk_name, "CAVE"))
3874 level->no_valid_file = TRUE;
3876 Warn("unknown format of level file '%s'", filename);
3883 else // check for pre-2.0 file format with cookie string
3885 strcpy(cookie, chunk_name);
3886 if (getStringFromFile(file, &cookie[4], MAX_LINE_LEN - 4) == NULL)
3888 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
3889 cookie[strlen(cookie) - 1] = '\0';
3891 if (!checkCookieString(cookie, LEVEL_COOKIE_TMPL))
3893 level->no_valid_file = TRUE;
3895 Warn("unknown format of level file '%s'", filename);
3902 if ((level->file_version = getFileVersionFromCookieString(cookie)) == -1)
3904 level->no_valid_file = TRUE;
3906 Warn("unsupported version of level file '%s'", filename);
3913 // pre-2.0 level files have no game version, so use file version here
3914 level->game_version = level->file_version;
3917 if (level->file_version < FILE_VERSION_1_2)
3919 // level files from versions before 1.2.0 without chunk structure
3920 LoadLevel_HEAD(file, LEVEL_CHUNK_HEAD_SIZE, level);
3921 LoadLevel_BODY(file, level->fieldx * level->fieldy, level);
3929 int (*loader)(File *, int, struct LevelInfo *);
3933 { "VERS", LEVEL_CHUNK_VERS_SIZE, LoadLevel_VERS },
3934 { "DATE", LEVEL_CHUNK_DATE_SIZE, LoadLevel_DATE },
3935 { "HEAD", LEVEL_CHUNK_HEAD_SIZE, LoadLevel_HEAD },
3936 { "NAME", LEVEL_CHUNK_NAME_SIZE, LoadLevel_NAME },
3937 { "AUTH", LEVEL_CHUNK_AUTH_SIZE, LoadLevel_AUTH },
3938 { "INFO", -1, LoadLevel_INFO },
3939 { "BODY", -1, LoadLevel_BODY },
3940 { "CONT", -1, LoadLevel_CONT },
3941 { "CNT2", LEVEL_CHUNK_CNT2_SIZE, LoadLevel_CNT2 },
3942 { "CNT3", -1, LoadLevel_CNT3 },
3943 { "CUS1", -1, LoadLevel_CUS1 },
3944 { "CUS2", -1, LoadLevel_CUS2 },
3945 { "CUS3", -1, LoadLevel_CUS3 },
3946 { "CUS4", -1, LoadLevel_CUS4 },
3947 { "GRP1", -1, LoadLevel_GRP1 },
3948 { "CONF", -1, LoadLevel_CONF },
3949 { "ELEM", -1, LoadLevel_ELEM },
3950 { "NOTE", -1, LoadLevel_NOTE },
3951 { "CUSX", -1, LoadLevel_CUSX },
3952 { "GRPX", -1, LoadLevel_GRPX },
3953 { "EMPX", -1, LoadLevel_EMPX },
3958 while (getFileChunkBE(file, chunk_name, &chunk_size))
3962 while (chunk_info[i].name != NULL &&
3963 !strEqual(chunk_name, chunk_info[i].name))
3966 if (chunk_info[i].name == NULL)
3968 Warn("unknown chunk '%s' in level file '%s'",
3969 chunk_name, filename);
3971 ReadUnusedBytesFromFile(file, chunk_size);
3973 else if (chunk_info[i].size != -1 &&
3974 chunk_info[i].size != chunk_size)
3976 Warn("wrong size (%d) of chunk '%s' in level file '%s'",
3977 chunk_size, chunk_name, filename);
3979 ReadUnusedBytesFromFile(file, chunk_size);
3983 // call function to load this level chunk
3984 int chunk_size_expected =
3985 (chunk_info[i].loader)(file, chunk_size, level);
3987 if (chunk_size_expected < 0)
3989 Warn("error reading chunk '%s' in level file '%s'",
3990 chunk_name, filename);
3995 // the size of some chunks cannot be checked before reading other
3996 // chunks first (like "HEAD" and "BODY") that contain some header
3997 // information, so check them here
3998 if (chunk_size_expected != chunk_size)
4000 Warn("wrong size (%d) of chunk '%s' in level file '%s'",
4001 chunk_size, chunk_name, filename);
4013 // ----------------------------------------------------------------------------
4014 // functions for loading BD level
4015 // ----------------------------------------------------------------------------
4017 static void CopyNativeLevel_RND_to_BD(struct LevelInfo *level)
4019 struct LevelInfo_BD *level_bd = level->native_bd_level;
4020 GdCave *cave = NULL; // will be changed below
4021 int cave_w = MIN(level->fieldx, MAX_PLAYFIELD_WIDTH);
4022 int cave_h = MIN(level->fieldy, MAX_PLAYFIELD_HEIGHT);
4025 setLevelInfoToDefaults_BD_Ext(cave_w, cave_h);
4027 // cave and map newly allocated when set to defaults above
4028 cave = level_bd->cave;
4031 cave->intermission = level->bd_intermission;
4034 cave->level_time[0] = level->time;
4035 cave->level_diamonds[0] = level->gems_needed;
4038 cave->scheduling = level->bd_scheduling_type;
4039 cave->pal_timing = level->bd_pal_timing;
4040 cave->level_speed[0] = level->bd_cycle_delay_ms;
4041 cave->level_ckdelay[0] = level->bd_cycle_delay_c64;
4042 cave->level_hatching_delay_frame[0] = level->bd_hatching_delay_cycles;
4043 cave->level_hatching_delay_time[0] = level->bd_hatching_delay_seconds;
4046 cave->level_timevalue[0] = level->score[SC_TIME_BONUS];
4047 cave->diamond_value = level->score[SC_EMERALD];
4048 cave->extra_diamond_value = level->score[SC_DIAMOND_EXTRA];
4050 // compatibility settings
4051 cave->lineshift = level->bd_line_shifting_borders;
4052 cave->border_scan_first_and_last = level->bd_scan_first_and_last_row;
4053 cave->short_explosions = level->bd_short_explosions;
4054 cave->gravity_affects_all = level->bd_gravity_affects_all;
4056 // player properties
4057 cave->diagonal_movements = level->bd_diagonal_movements;
4058 cave->active_is_first_found = level->bd_topmost_player_active;
4059 cave->pushing_stone_prob = level->bd_pushing_prob * 10000;
4060 cave->pushing_stone_prob_sweet = level->bd_pushing_prob_with_sweet * 10000;
4061 cave->mega_stones_pushable_with_sweet = level->bd_push_mega_rock_with_sweet;
4062 cave->snap_element = map_element_RND_to_BD_cave(level->bd_snap_element);
4064 // element properties
4065 cave->level_bonus_time[0] = level->bd_clock_extra_time;
4066 cave->voodoo_collects_diamonds = level->bd_voodoo_collects_diamonds;
4067 cave->voodoo_any_hurt_kills_player = level->bd_voodoo_hurt_kills_player;
4068 cave->voodoo_dies_by_stone = level->bd_voodoo_dies_by_rock;
4069 cave->voodoo_disappear_in_explosion = level->bd_voodoo_vanish_by_explosion;
4070 cave->level_penalty_time[0] = level->bd_voodoo_penalty_time;
4071 cave->level_magic_wall_time[0] = level->time_magic_wall;
4072 cave->magic_timer_wait_for_hatching = level->bd_magic_wall_wait_hatching;
4073 cave->magic_wall_stops_amoeba = level->bd_magic_wall_stops_amoeba;
4075 cave->magic_diamond_to = map_element_RND_to_BD_cave(level->bd_magic_wall_diamond_to);
4076 cave->magic_stone_to = map_element_RND_to_BD_cave(level->bd_magic_wall_rock_to);
4077 cave->magic_mega_stone_to = map_element_RND_to_BD_cave(level->bd_magic_wall_mega_rock_to);
4078 cave->magic_nut_to = map_element_RND_to_BD_cave(level->bd_magic_wall_nut_to);
4079 cave->magic_nitro_pack_to = map_element_RND_to_BD_cave(level->bd_magic_wall_nitro_pack_to);
4080 cave->magic_flying_diamond_to = map_element_RND_to_BD_cave(level->bd_magic_wall_flying_diamond_to);
4081 cave->magic_flying_stone_to = map_element_RND_to_BD_cave(level->bd_magic_wall_flying_rock_to);
4083 cave->amoeba_timer_wait_for_hatching = level->bd_amoeba_wait_for_hatching;
4084 cave->amoeba_timer_started_immediately= level->bd_amoeba_start_immediately;
4085 cave->amoeba_2_explodes_by_amoeba = level->bd_amoeba_2_explode_by_amoeba;
4086 cave->level_amoeba_threshold[0] = level->bd_amoeba_threshold_too_big;
4087 cave->level_amoeba_time[0] = level->bd_amoeba_slow_growth_time;
4088 cave->amoeba_growth_prob = level->bd_amoeba_slow_growth_rate * 10000;
4089 cave->amoeba_fast_growth_prob = level->bd_amoeba_fast_growth_rate * 10000;
4090 cave->level_amoeba_2_threshold[0] = level->bd_amoeba_2_threshold_too_big;
4091 cave->level_amoeba_2_time[0] = level->bd_amoeba_2_slow_growth_time;
4092 cave->amoeba_2_growth_prob = level->bd_amoeba_2_slow_growth_rate * 10000;
4093 cave->amoeba_2_fast_growth_prob = level->bd_amoeba_2_fast_growth_rate * 10000;
4095 cave->amoeba_too_big_effect = map_element_RND_to_BD_cave(level->bd_amoeba_content_too_big);
4096 cave->amoeba_enclosed_effect = map_element_RND_to_BD_cave(level->bd_amoeba_content_enclosed);
4097 cave->amoeba_2_too_big_effect = map_element_RND_to_BD_cave(level->bd_amoeba_2_content_too_big);
4098 cave->amoeba_2_enclosed_effect = map_element_RND_to_BD_cave(level->bd_amoeba_2_content_enclosed);
4099 cave->amoeba_2_explosion_effect = map_element_RND_to_BD_cave(level->bd_amoeba_2_content_exploding);
4100 cave->amoeba_2_looks_like = map_element_RND_to_BD_cave(level->bd_amoeba_2_content_looks_like);
4102 cave->slime_predictable = level->bd_slime_is_predictable;
4103 cave->slime_correct_random = level->bd_slime_correct_random;
4104 cave->level_slime_permeability[0] = level->bd_slime_permeability_rate * 10000;
4105 cave->level_slime_permeability_c64[0] = level->bd_slime_permeability_bits_c64;
4106 cave->level_slime_seed_c64[0] = level->bd_slime_random_seed_c64;
4107 cave->level_rand[0] = level->bd_cave_random_seed_c64;
4109 cave->acid_eats_this = map_element_RND_to_BD_cave(level->bd_acid_eats_element);
4110 cave->acid_spread_ratio = level->bd_acid_spread_rate * 10000;
4111 cave->acid_turns_to = map_element_RND_to_BD_cave(level->bd_acid_turns_to_element);
4113 cave->biter_delay_frame = level->bd_biter_move_delay;
4114 cave->biter_eat = map_element_RND_to_BD_cave(level->bd_biter_eats_element);
4116 cave->bladder_converts_by = map_element_RND_to_BD_cave(level->bd_bladder_converts_by_element);
4118 cave->expanding_wall_changed = level->bd_change_expanding_wall;
4120 cave->replicators_active = level->bd_replicators_active;
4121 cave->replicator_delay_frame = level->bd_replicator_create_delay;
4123 cave->conveyor_belts_active = level->bd_conveyor_belts_active;
4124 cave->conveyor_belts_direction_changed= level->bd_conveyor_belts_changed;
4126 cave->water_does_not_flow_down = level->bd_water_cannot_flow_down;
4129 strncpy(cave->name, level->name, sizeof(GdString));
4130 cave->name[sizeof(GdString) - 1] = '\0';
4132 // playfield elements
4133 for (x = 0; x < cave->w; x++)
4134 for (y = 0; y < cave->h; y++)
4135 cave->map[y][x] = map_element_RND_to_BD_cave(level->field[x][y]);
4138 static void CopyNativeLevel_BD_to_RND(struct LevelInfo *level)
4140 struct LevelInfo_BD *level_bd = level->native_bd_level;
4141 GdCave *cave = level_bd->cave;
4142 int bd_level_nr = level_bd->level_nr;
4145 level->fieldx = MIN(cave->w, MAX_LEV_FIELDX);
4146 level->fieldy = MIN(cave->h, MAX_LEV_FIELDY);
4149 level->bd_intermission = cave->intermission;
4152 level->time = cave->level_time[bd_level_nr];
4153 level->gems_needed = cave->level_diamonds[bd_level_nr];
4156 level->bd_scheduling_type = cave->scheduling;
4157 level->bd_pal_timing = cave->pal_timing;
4158 level->bd_cycle_delay_ms = cave->level_speed[bd_level_nr];
4159 level->bd_cycle_delay_c64 = cave->level_ckdelay[bd_level_nr];
4160 level->bd_hatching_delay_cycles = cave->level_hatching_delay_frame[bd_level_nr];
4161 level->bd_hatching_delay_seconds = cave->level_hatching_delay_time[bd_level_nr];
4164 level->score[SC_TIME_BONUS] = cave->level_timevalue[bd_level_nr];
4165 level->score[SC_EMERALD] = cave->diamond_value;
4166 level->score[SC_DIAMOND_EXTRA] = cave->extra_diamond_value;
4168 // compatibility settings
4169 level->bd_line_shifting_borders = cave->lineshift;
4170 level->bd_scan_first_and_last_row = cave->border_scan_first_and_last;
4171 level->bd_short_explosions = cave->short_explosions;
4172 level->bd_gravity_affects_all = cave->gravity_affects_all;
4174 // player properties
4175 level->bd_diagonal_movements = cave->diagonal_movements;
4176 level->bd_topmost_player_active = cave->active_is_first_found;
4177 level->bd_pushing_prob = cave->pushing_stone_prob / 10000;
4178 level->bd_pushing_prob_with_sweet = cave->pushing_stone_prob_sweet / 10000;
4179 level->bd_push_mega_rock_with_sweet = cave->mega_stones_pushable_with_sweet;
4180 level->bd_snap_element = map_element_BD_to_RND_cave(cave->snap_element);
4182 // element properties
4183 level->bd_clock_extra_time = cave->level_bonus_time[bd_level_nr];
4184 level->bd_voodoo_collects_diamonds = cave->voodoo_collects_diamonds;
4185 level->bd_voodoo_hurt_kills_player = cave->voodoo_any_hurt_kills_player;
4186 level->bd_voodoo_dies_by_rock = cave->voodoo_dies_by_stone;
4187 level->bd_voodoo_vanish_by_explosion = cave->voodoo_disappear_in_explosion;
4188 level->bd_voodoo_penalty_time = cave->level_penalty_time[bd_level_nr];
4189 level->time_magic_wall = cave->level_magic_wall_time[bd_level_nr];
4190 level->bd_magic_wall_wait_hatching = cave->magic_timer_wait_for_hatching;
4191 level->bd_magic_wall_stops_amoeba = cave->magic_wall_stops_amoeba;
4193 level->bd_magic_wall_diamond_to = map_element_BD_to_RND_cave(cave->magic_diamond_to);
4194 level->bd_magic_wall_rock_to = map_element_BD_to_RND_cave(cave->magic_stone_to);
4195 level->bd_magic_wall_mega_rock_to = map_element_BD_to_RND_cave(cave->magic_mega_stone_to);
4196 level->bd_magic_wall_nut_to = map_element_BD_to_RND_cave(cave->magic_nut_to);
4197 level->bd_magic_wall_nitro_pack_to = map_element_BD_to_RND_cave(cave->magic_nitro_pack_to);
4198 level->bd_magic_wall_flying_diamond_to= map_element_BD_to_RND_cave(cave->magic_flying_diamond_to);
4199 level->bd_magic_wall_flying_rock_to = map_element_BD_to_RND_cave(cave->magic_flying_stone_to);
4201 level->bd_amoeba_wait_for_hatching = cave->amoeba_timer_wait_for_hatching;
4202 level->bd_amoeba_start_immediately = cave->amoeba_timer_started_immediately;
4203 level->bd_amoeba_2_explode_by_amoeba = cave->amoeba_2_explodes_by_amoeba;
4204 level->bd_amoeba_threshold_too_big = cave->level_amoeba_threshold[bd_level_nr];
4205 level->bd_amoeba_slow_growth_time = cave->level_amoeba_time[bd_level_nr];
4206 level->bd_amoeba_slow_growth_rate = cave->amoeba_growth_prob / 10000;
4207 level->bd_amoeba_fast_growth_rate = cave->amoeba_fast_growth_prob / 10000;
4208 level->bd_amoeba_2_threshold_too_big = cave->level_amoeba_2_threshold[bd_level_nr];
4209 level->bd_amoeba_2_slow_growth_time = cave->level_amoeba_2_time[bd_level_nr];
4210 level->bd_amoeba_2_slow_growth_rate = cave->amoeba_2_growth_prob / 10000;
4211 level->bd_amoeba_2_fast_growth_rate = cave->amoeba_2_fast_growth_prob / 10000;
4213 level->bd_amoeba_content_too_big = map_element_BD_to_RND_cave(cave->amoeba_too_big_effect);
4214 level->bd_amoeba_content_enclosed = map_element_BD_to_RND_cave(cave->amoeba_enclosed_effect);
4215 level->bd_amoeba_2_content_too_big = map_element_BD_to_RND_cave(cave->amoeba_2_too_big_effect);
4216 level->bd_amoeba_2_content_enclosed = map_element_BD_to_RND_cave(cave->amoeba_2_enclosed_effect);
4217 level->bd_amoeba_2_content_exploding = map_element_BD_to_RND_cave(cave->amoeba_2_explosion_effect);
4218 level->bd_amoeba_2_content_looks_like = map_element_BD_to_RND_cave(cave->amoeba_2_looks_like);
4220 level->bd_slime_is_predictable = cave->slime_predictable;
4221 level->bd_slime_correct_random = cave->slime_correct_random;
4222 level->bd_slime_permeability_rate = cave->level_slime_permeability[bd_level_nr] / 10000;
4223 level->bd_slime_permeability_bits_c64 = cave->level_slime_permeability_c64[bd_level_nr];
4224 level->bd_slime_random_seed_c64 = cave->level_slime_seed_c64[bd_level_nr];
4225 level->bd_cave_random_seed_c64 = cave->level_rand[bd_level_nr];
4227 level->bd_acid_eats_element = map_element_BD_to_RND_cave(cave->acid_eats_this);
4228 level->bd_acid_spread_rate = cave->acid_spread_ratio / 10000;
4229 level->bd_acid_turns_to_element = map_element_BD_to_RND_cave(cave->acid_turns_to);
4231 level->bd_biter_move_delay = cave->biter_delay_frame;
4232 level->bd_biter_eats_element = map_element_BD_to_RND_cave(cave->biter_eat);
4234 level->bd_bladder_converts_by_element = map_element_BD_to_RND_cave(cave->bladder_converts_by);
4236 level->bd_change_expanding_wall = cave->expanding_wall_changed;
4238 level->bd_replicators_active = cave->replicators_active;
4239 level->bd_replicator_create_delay = cave->replicator_delay_frame;
4241 level->bd_conveyor_belts_active = cave->conveyor_belts_active;
4242 level->bd_conveyor_belts_changed = cave->conveyor_belts_direction_changed;
4244 level->bd_water_cannot_flow_down = cave->water_does_not_flow_down;
4247 char *cave_name = getStringPrint("%s / %d", cave->name, bd_level_nr + 1);
4249 strncpy(level->name, cave_name, MAX_LEVEL_NAME_LEN);
4250 level->name[MAX_LEVEL_NAME_LEN] = '\0';
4252 // playfield elements
4253 for (x = 0; x < level->fieldx; x++)
4254 for (y = 0; y < level->fieldy; y++)
4255 level->field[x][y] = map_element_BD_to_RND_cave(cave->map[y][x]);
4257 checked_free(cave_name);
4260 static void setTapeInfoToDefaults(void);
4262 static void CopyNativeTape_BD_to_RND(struct LevelInfo *level)
4264 struct LevelInfo_BD *level_bd = level->native_bd_level;
4265 GdCave *cave = level_bd->cave;
4266 GdReplay *replay = level_bd->replay;
4272 // always start with reliable default values
4273 setTapeInfoToDefaults();
4275 tape.level_nr = level_nr; // (currently not used)
4276 tape.random_seed = replay->seed;
4278 TapeSetDateFromIsoDateString(replay->date);
4281 tape.pos[tape.counter].delay = 0;
4283 tape.bd_replay = TRUE;
4285 // all time calculations only used to display approximate tape time
4286 int cave_speed = cave->speed;
4287 int milliseconds_game = 0;
4288 int milliseconds_elapsed = 20;
4290 for (i = 0; i < replay->movements->len; i++)
4292 int replay_action = replay->movements->data[i];
4293 int tape_action = map_action_BD_to_RND(replay_action);
4294 byte action[MAX_TAPE_ACTIONS] = { tape_action };
4295 boolean success = 0;
4299 success = TapeAddAction(action);
4301 milliseconds_game += milliseconds_elapsed;
4303 if (milliseconds_game >= cave_speed)
4305 milliseconds_game -= cave_speed;
4312 tape.pos[tape.counter].delay = 0;
4313 tape.pos[tape.counter].action[0] = 0;
4317 Warn("BD replay truncated: size exceeds maximum tape size %d", MAX_TAPE_LEN);
4323 TapeHaltRecording();
4327 // ----------------------------------------------------------------------------
4328 // functions for loading EM level
4329 // ----------------------------------------------------------------------------
4331 static void CopyNativeLevel_RND_to_EM(struct LevelInfo *level)
4333 static int ball_xy[8][2] =
4344 struct LevelInfo_EM *level_em = level->native_em_level;
4345 struct CAVE *cav = level_em->cav;
4348 cav->width = MIN(level->fieldx, MAX_PLAYFIELD_WIDTH);
4349 cav->height = MIN(level->fieldy, MAX_PLAYFIELD_HEIGHT);
4351 cav->time_seconds = level->time;
4352 cav->gems_needed = level->gems_needed;
4354 cav->emerald_score = level->score[SC_EMERALD];
4355 cav->diamond_score = level->score[SC_DIAMOND];
4356 cav->alien_score = level->score[SC_ROBOT];
4357 cav->tank_score = level->score[SC_SPACESHIP];
4358 cav->bug_score = level->score[SC_BUG];
4359 cav->eater_score = level->score[SC_YAMYAM];
4360 cav->nut_score = level->score[SC_NUT];
4361 cav->dynamite_score = level->score[SC_DYNAMITE];
4362 cav->key_score = level->score[SC_KEY];
4363 cav->exit_score = level->score[SC_TIME_BONUS];
4365 cav->num_eater_arrays = level->num_yamyam_contents;
4367 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
4368 for (y = 0; y < 3; y++)
4369 for (x = 0; x < 3; x++)
4370 cav->eater_array[i][y * 3 + x] =
4371 map_element_RND_to_EM_cave(level->yamyam_content[i].e[x][y]);
4373 cav->amoeba_time = level->amoeba_speed;
4374 cav->wonderwall_time = level->time_magic_wall;
4375 cav->wheel_time = level->time_wheel;
4377 cav->android_move_time = level->android_move_time;
4378 cav->android_clone_time = level->android_clone_time;
4379 cav->ball_random = level->ball_random;
4380 cav->ball_active = level->ball_active_initial;
4381 cav->ball_time = level->ball_time;
4382 cav->num_ball_arrays = level->num_ball_contents;
4384 cav->lenses_score = level->lenses_score;
4385 cav->magnify_score = level->magnify_score;
4386 cav->slurp_score = level->slurp_score;
4388 cav->lenses_time = level->lenses_time;
4389 cav->magnify_time = level->magnify_time;
4391 cav->wind_time = 9999;
4392 cav->wind_direction =
4393 map_direction_RND_to_EM(level->wind_direction_initial);
4395 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
4396 for (j = 0; j < 8; j++)
4397 cav->ball_array[i][j] =
4398 map_element_RND_to_EM_cave(level->ball_content[i].
4399 e[ball_xy[j][0]][ball_xy[j][1]]);
4401 map_android_clone_elements_RND_to_EM(level);
4403 // first fill the complete playfield with the empty space element
4404 for (y = 0; y < EM_MAX_CAVE_HEIGHT; y++)
4405 for (x = 0; x < EM_MAX_CAVE_WIDTH; x++)
4406 cav->cave[x][y] = Cblank;
4408 // then copy the real level contents from level file into the playfield
4409 for (y = 0; y < cav->height; y++) for (x = 0; x < cav->width; x++)
4411 int new_element = map_element_RND_to_EM_cave(level->field[x][y]);
4413 if (level->field[x][y] == EL_AMOEBA_DEAD)
4414 new_element = map_element_RND_to_EM_cave(EL_AMOEBA_WET);
4416 cav->cave[x][y] = new_element;
4419 for (i = 0; i < MAX_PLAYERS; i++)
4421 cav->player_x[i] = -1;
4422 cav->player_y[i] = -1;
4425 // initialize player positions and delete players from the playfield
4426 for (y = 0; y < cav->height; y++) for (x = 0; x < cav->width; x++)
4428 if (IS_PLAYER_ELEMENT(level->field[x][y]))
4430 int player_nr = GET_PLAYER_NR(level->field[x][y]);
4432 cav->player_x[player_nr] = x;
4433 cav->player_y[player_nr] = y;
4435 cav->cave[x][y] = map_element_RND_to_EM_cave(EL_EMPTY);
4440 static void CopyNativeLevel_EM_to_RND(struct LevelInfo *level)
4442 static int ball_xy[8][2] =
4453 struct LevelInfo_EM *level_em = level->native_em_level;
4454 struct CAVE *cav = level_em->cav;
4457 level->fieldx = MIN(cav->width, MAX_LEV_FIELDX);
4458 level->fieldy = MIN(cav->height, MAX_LEV_FIELDY);
4460 level->time = cav->time_seconds;
4461 level->gems_needed = cav->gems_needed;
4463 sprintf(level->name, "Level %d", level->file_info.nr);
4465 level->score[SC_EMERALD] = cav->emerald_score;
4466 level->score[SC_DIAMOND] = cav->diamond_score;
4467 level->score[SC_ROBOT] = cav->alien_score;
4468 level->score[SC_SPACESHIP] = cav->tank_score;
4469 level->score[SC_BUG] = cav->bug_score;
4470 level->score[SC_YAMYAM] = cav->eater_score;
4471 level->score[SC_NUT] = cav->nut_score;
4472 level->score[SC_DYNAMITE] = cav->dynamite_score;
4473 level->score[SC_KEY] = cav->key_score;
4474 level->score[SC_TIME_BONUS] = cav->exit_score;
4476 level->num_yamyam_contents = cav->num_eater_arrays;
4478 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
4479 for (y = 0; y < 3; y++)
4480 for (x = 0; x < 3; x++)
4481 level->yamyam_content[i].e[x][y] =
4482 map_element_EM_to_RND_cave(cav->eater_array[i][y * 3 + x]);
4484 level->amoeba_speed = cav->amoeba_time;
4485 level->time_magic_wall = cav->wonderwall_time;
4486 level->time_wheel = cav->wheel_time;
4488 level->android_move_time = cav->android_move_time;
4489 level->android_clone_time = cav->android_clone_time;
4490 level->ball_random = cav->ball_random;
4491 level->ball_active_initial = cav->ball_active;
4492 level->ball_time = cav->ball_time;
4493 level->num_ball_contents = cav->num_ball_arrays;
4495 level->lenses_score = cav->lenses_score;
4496 level->magnify_score = cav->magnify_score;
4497 level->slurp_score = cav->slurp_score;
4499 level->lenses_time = cav->lenses_time;
4500 level->magnify_time = cav->magnify_time;
4502 level->wind_direction_initial =
4503 map_direction_EM_to_RND(cav->wind_direction);
4505 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
4506 for (j = 0; j < 8; j++)
4507 level->ball_content[i].e[ball_xy[j][0]][ball_xy[j][1]] =
4508 map_element_EM_to_RND_cave(cav->ball_array[i][j]);
4510 map_android_clone_elements_EM_to_RND(level);
4512 // convert the playfield (some elements need special treatment)
4513 for (y = 0; y < level->fieldy; y++) for (x = 0; x < level->fieldx; x++)
4515 int new_element = map_element_EM_to_RND_cave(cav->cave[x][y]);
4517 if (new_element == EL_AMOEBA_WET && level->amoeba_speed == 0)
4518 new_element = EL_AMOEBA_DEAD;
4520 level->field[x][y] = new_element;
4523 for (i = 0; i < MAX_PLAYERS; i++)
4525 // in case of all players set to the same field, use the first player
4526 int nr = MAX_PLAYERS - i - 1;
4527 int jx = cav->player_x[nr];
4528 int jy = cav->player_y[nr];
4530 if (jx != -1 && jy != -1)
4531 level->field[jx][jy] = EL_PLAYER_1 + nr;
4534 // time score is counted for each 10 seconds left in Emerald Mine levels
4535 level->time_score_base = 10;
4539 // ----------------------------------------------------------------------------
4540 // functions for loading SP level
4541 // ----------------------------------------------------------------------------
4543 static void CopyNativeLevel_RND_to_SP(struct LevelInfo *level)
4545 struct LevelInfo_SP *level_sp = level->native_sp_level;
4546 LevelInfoType *header = &level_sp->header;
4549 level_sp->width = level->fieldx;
4550 level_sp->height = level->fieldy;
4552 for (x = 0; x < level->fieldx; x++)
4553 for (y = 0; y < level->fieldy; y++)
4554 level_sp->playfield[x][y] = map_element_RND_to_SP(level->field[x][y]);
4556 header->InitialGravity = (level->initial_player_gravity[0] ? 1 : 0);
4558 for (i = 0; i < SP_LEVEL_NAME_LEN; i++)
4559 header->LevelTitle[i] = level->name[i];
4560 // !!! NO STRING TERMINATION IN SUPAPLEX VB CODE YET -- FIX THIS !!!
4562 header->InfotronsNeeded = level->gems_needed;
4564 header->SpecialPortCount = 0;
4566 for (x = 0; x < level->fieldx; x++) for (y = 0; y < level->fieldy; y++)
4568 boolean gravity_port_found = FALSE;
4569 boolean gravity_port_valid = FALSE;
4570 int gravity_port_flag;
4571 int gravity_port_base_element;
4572 int element = level->field[x][y];
4574 if (element >= EL_SP_GRAVITY_ON_PORT_RIGHT &&
4575 element <= EL_SP_GRAVITY_ON_PORT_UP)
4577 gravity_port_found = TRUE;
4578 gravity_port_valid = TRUE;
4579 gravity_port_flag = 1;
4580 gravity_port_base_element = EL_SP_GRAVITY_ON_PORT_RIGHT;
4582 else if (element >= EL_SP_GRAVITY_OFF_PORT_RIGHT &&
4583 element <= EL_SP_GRAVITY_OFF_PORT_UP)
4585 gravity_port_found = TRUE;
4586 gravity_port_valid = TRUE;
4587 gravity_port_flag = 0;
4588 gravity_port_base_element = EL_SP_GRAVITY_OFF_PORT_RIGHT;
4590 else if (element >= EL_SP_GRAVITY_PORT_RIGHT &&
4591 element <= EL_SP_GRAVITY_PORT_UP)
4593 // change R'n'D style gravity inverting special port to normal port
4594 // (there are no gravity inverting ports in native Supaplex engine)
4596 gravity_port_found = TRUE;
4597 gravity_port_valid = FALSE;
4598 gravity_port_base_element = EL_SP_GRAVITY_PORT_RIGHT;
4601 if (gravity_port_found)
4603 if (gravity_port_valid &&
4604 header->SpecialPortCount < SP_MAX_SPECIAL_PORTS)
4606 SpecialPortType *port = &header->SpecialPort[header->SpecialPortCount];
4608 port->PortLocation = (y * level->fieldx + x) * 2;
4609 port->Gravity = gravity_port_flag;
4611 element += EL_SP_GRAVITY_PORT_RIGHT - gravity_port_base_element;
4613 header->SpecialPortCount++;
4617 // change special gravity port to normal port
4619 element += EL_SP_PORT_RIGHT - gravity_port_base_element;
4622 level_sp->playfield[x][y] = element - EL_SP_START;
4627 static void CopyNativeLevel_SP_to_RND(struct LevelInfo *level)
4629 struct LevelInfo_SP *level_sp = level->native_sp_level;
4630 LevelInfoType *header = &level_sp->header;
4631 boolean num_invalid_elements = 0;
4634 level->fieldx = level_sp->width;
4635 level->fieldy = level_sp->height;
4637 for (x = 0; x < level->fieldx; x++)
4639 for (y = 0; y < level->fieldy; y++)
4641 int element_old = level_sp->playfield[x][y];
4642 int element_new = getMappedElement(map_element_SP_to_RND(element_old));
4644 if (element_new == EL_UNKNOWN)
4646 num_invalid_elements++;
4648 Debug("level:native:SP", "invalid element %d at position %d, %d",
4652 level->field[x][y] = element_new;
4656 if (num_invalid_elements > 0)
4657 Warn("found %d invalid elements%s", num_invalid_elements,
4658 (!options.debug ? " (use '--debug' for more details)" : ""));
4660 for (i = 0; i < MAX_PLAYERS; i++)
4661 level->initial_player_gravity[i] =
4662 (header->InitialGravity == 1 ? TRUE : FALSE);
4664 // skip leading spaces
4665 for (i = 0; i < SP_LEVEL_NAME_LEN; i++)
4666 if (header->LevelTitle[i] != ' ')
4670 for (j = 0; i < SP_LEVEL_NAME_LEN; i++, j++)
4671 level->name[j] = header->LevelTitle[i];
4672 level->name[j] = '\0';
4674 // cut trailing spaces
4676 if (level->name[j - 1] == ' ' && level->name[j] == '\0')
4677 level->name[j - 1] = '\0';
4679 level->gems_needed = header->InfotronsNeeded;
4681 for (i = 0; i < header->SpecialPortCount; i++)
4683 SpecialPortType *port = &header->SpecialPort[i];
4684 int port_location = port->PortLocation;
4685 int gravity = port->Gravity;
4686 int port_x, port_y, port_element;
4688 port_x = (port_location / 2) % level->fieldx;
4689 port_y = (port_location / 2) / level->fieldx;
4691 if (port_x < 0 || port_x >= level->fieldx ||
4692 port_y < 0 || port_y >= level->fieldy)
4694 Warn("special port position (%d, %d) out of bounds", port_x, port_y);
4699 port_element = level->field[port_x][port_y];
4701 if (port_element < EL_SP_GRAVITY_PORT_RIGHT ||
4702 port_element > EL_SP_GRAVITY_PORT_UP)
4704 Warn("no special port at position (%d, %d)", port_x, port_y);
4709 // change previous (wrong) gravity inverting special port to either
4710 // gravity enabling special port or gravity disabling special port
4711 level->field[port_x][port_y] +=
4712 (gravity == 1 ? EL_SP_GRAVITY_ON_PORT_RIGHT :
4713 EL_SP_GRAVITY_OFF_PORT_RIGHT) - EL_SP_GRAVITY_PORT_RIGHT;
4716 // change special gravity ports without database entries to normal ports
4717 for (x = 0; x < level->fieldx; x++)
4718 for (y = 0; y < level->fieldy; y++)
4719 if (level->field[x][y] >= EL_SP_GRAVITY_PORT_RIGHT &&
4720 level->field[x][y] <= EL_SP_GRAVITY_PORT_UP)
4721 level->field[x][y] += EL_SP_PORT_RIGHT - EL_SP_GRAVITY_PORT_RIGHT;
4723 level->time = 0; // no time limit
4724 level->amoeba_speed = 0;
4725 level->time_magic_wall = 0;
4726 level->time_wheel = 0;
4727 level->amoeba_content = EL_EMPTY;
4729 // original Supaplex does not use score values -- rate by playing time
4730 for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
4731 level->score[i] = 0;
4733 level->rate_time_over_score = TRUE;
4735 // there are no yamyams in supaplex levels
4736 for (i = 0; i < level->num_yamyam_contents; i++)
4737 for (x = 0; x < 3; x++)
4738 for (y = 0; y < 3; y++)
4739 level->yamyam_content[i].e[x][y] = EL_EMPTY;
4742 static void CopyNativeTape_RND_to_SP(struct LevelInfo *level)
4744 struct LevelInfo_SP *level_sp = level->native_sp_level;
4745 struct DemoInfo_SP *demo = &level_sp->demo;
4748 // always start with reliable default values
4749 demo->is_available = FALSE;
4752 if (TAPE_IS_EMPTY(tape))
4755 demo->level_nr = tape.level_nr; // (currently not used)
4757 level_sp->header.DemoRandomSeed = tape.random_seed;
4761 for (i = 0; i < tape.length; i++)
4763 int demo_action = map_key_RND_to_SP(tape.pos[i].action[0]);
4764 int demo_repeat = tape.pos[i].delay;
4765 int demo_entries = (demo_repeat + 15) / 16;
4767 if (demo->length + demo_entries >= SP_MAX_TAPE_LEN)
4769 Warn("tape truncated: size exceeds maximum SP demo size %d",
4775 for (j = 0; j < demo_repeat / 16; j++)
4776 demo->data[demo->length++] = 0xf0 | demo_action;
4778 if (demo_repeat % 16)
4779 demo->data[demo->length++] = ((demo_repeat % 16 - 1) << 4) | demo_action;
4782 demo->is_available = TRUE;
4785 static void CopyNativeTape_SP_to_RND(struct LevelInfo *level)
4787 struct LevelInfo_SP *level_sp = level->native_sp_level;
4788 struct DemoInfo_SP *demo = &level_sp->demo;
4789 char *filename = level->file_info.filename;
4792 // always start with reliable default values
4793 setTapeInfoToDefaults();
4795 if (!demo->is_available)
4798 tape.level_nr = demo->level_nr; // (currently not used)
4799 tape.random_seed = level_sp->header.DemoRandomSeed;
4801 TapeSetDateFromEpochSeconds(getFileTimestampEpochSeconds(filename));
4804 tape.pos[tape.counter].delay = 0;
4806 for (i = 0; i < demo->length; i++)
4808 int demo_action = demo->data[i] & 0x0f;
4809 int demo_repeat = (demo->data[i] & 0xf0) >> 4;
4810 int tape_action = map_key_SP_to_RND(demo_action);
4811 int tape_repeat = demo_repeat + 1;
4812 byte action[MAX_TAPE_ACTIONS] = { tape_action };
4813 boolean success = 0;
4816 for (j = 0; j < tape_repeat; j++)
4817 success = TapeAddAction(action);
4821 Warn("SP demo truncated: size exceeds maximum tape size %d",
4828 TapeHaltRecording();
4832 // ----------------------------------------------------------------------------
4833 // functions for loading MM level
4834 // ----------------------------------------------------------------------------
4836 static void CopyNativeLevel_RND_to_MM(struct LevelInfo *level)
4838 struct LevelInfo_MM *level_mm = level->native_mm_level;
4841 level_mm->fieldx = MIN(level->fieldx, MM_MAX_PLAYFIELD_WIDTH);
4842 level_mm->fieldy = MIN(level->fieldy, MM_MAX_PLAYFIELD_HEIGHT);
4844 level_mm->time = level->time;
4845 level_mm->kettles_needed = level->gems_needed;
4846 level_mm->auto_count_kettles = level->auto_count_gems;
4848 level_mm->mm_laser_red = level->mm_laser_red;
4849 level_mm->mm_laser_green = level->mm_laser_green;
4850 level_mm->mm_laser_blue = level->mm_laser_blue;
4852 level_mm->df_laser_red = level->df_laser_red;
4853 level_mm->df_laser_green = level->df_laser_green;
4854 level_mm->df_laser_blue = level->df_laser_blue;
4856 strcpy(level_mm->name, level->name);
4857 strcpy(level_mm->author, level->author);
4859 level_mm->score[SC_EMERALD] = level->score[SC_EMERALD];
4860 level_mm->score[SC_PACMAN] = level->score[SC_PACMAN];
4861 level_mm->score[SC_KEY] = level->score[SC_KEY];
4862 level_mm->score[SC_TIME_BONUS] = level->score[SC_TIME_BONUS];
4863 level_mm->score[SC_ELEM_BONUS] = level->score[SC_ELEM_BONUS];
4865 level_mm->amoeba_speed = level->amoeba_speed;
4866 level_mm->time_fuse = level->mm_time_fuse;
4867 level_mm->time_bomb = level->mm_time_bomb;
4868 level_mm->time_ball = level->mm_time_ball;
4869 level_mm->time_block = level->mm_time_block;
4871 level_mm->num_ball_contents = level->num_mm_ball_contents;
4872 level_mm->ball_choice_mode = level->mm_ball_choice_mode;
4873 level_mm->rotate_ball_content = level->rotate_mm_ball_content;
4874 level_mm->explode_ball = level->explode_mm_ball;
4876 for (i = 0; i < level->num_mm_ball_contents; i++)
4877 level_mm->ball_content[i] =
4878 map_element_RND_to_MM(level->mm_ball_content[i]);
4880 for (x = 0; x < level->fieldx; x++)
4881 for (y = 0; y < level->fieldy; y++)
4883 level_mm->field[x][y] = map_element_RND_to_MM(level->field[x][y]);
4886 static void CopyNativeLevel_MM_to_RND(struct LevelInfo *level)
4888 struct LevelInfo_MM *level_mm = level->native_mm_level;
4891 level->fieldx = MIN(level_mm->fieldx, MAX_LEV_FIELDX);
4892 level->fieldy = MIN(level_mm->fieldy, MAX_LEV_FIELDY);
4894 level->time = level_mm->time;
4895 level->gems_needed = level_mm->kettles_needed;
4896 level->auto_count_gems = level_mm->auto_count_kettles;
4898 level->mm_laser_red = level_mm->mm_laser_red;
4899 level->mm_laser_green = level_mm->mm_laser_green;
4900 level->mm_laser_blue = level_mm->mm_laser_blue;
4902 level->df_laser_red = level_mm->df_laser_red;
4903 level->df_laser_green = level_mm->df_laser_green;
4904 level->df_laser_blue = level_mm->df_laser_blue;
4906 strcpy(level->name, level_mm->name);
4908 // only overwrite author from 'levelinfo.conf' if author defined in level
4909 if (!strEqual(level_mm->author, ANONYMOUS_NAME))
4910 strcpy(level->author, level_mm->author);
4912 level->score[SC_EMERALD] = level_mm->score[SC_EMERALD];
4913 level->score[SC_PACMAN] = level_mm->score[SC_PACMAN];
4914 level->score[SC_KEY] = level_mm->score[SC_KEY];
4915 level->score[SC_TIME_BONUS] = level_mm->score[SC_TIME_BONUS];
4916 level->score[SC_ELEM_BONUS] = level_mm->score[SC_ELEM_BONUS];
4918 level->amoeba_speed = level_mm->amoeba_speed;
4919 level->mm_time_fuse = level_mm->time_fuse;
4920 level->mm_time_bomb = level_mm->time_bomb;
4921 level->mm_time_ball = level_mm->time_ball;
4922 level->mm_time_block = level_mm->time_block;
4924 level->num_mm_ball_contents = level_mm->num_ball_contents;
4925 level->mm_ball_choice_mode = level_mm->ball_choice_mode;
4926 level->rotate_mm_ball_content = level_mm->rotate_ball_content;
4927 level->explode_mm_ball = level_mm->explode_ball;
4929 for (i = 0; i < level->num_mm_ball_contents; i++)
4930 level->mm_ball_content[i] =
4931 map_element_MM_to_RND(level_mm->ball_content[i]);
4933 for (x = 0; x < level->fieldx; x++)
4934 for (y = 0; y < level->fieldy; y++)
4935 level->field[x][y] = map_element_MM_to_RND(level_mm->field[x][y]);
4939 // ----------------------------------------------------------------------------
4940 // functions for loading DC level
4941 // ----------------------------------------------------------------------------
4943 #define DC_LEVEL_HEADER_SIZE 344
4945 static unsigned short getDecodedWord_DC(unsigned short data_encoded,
4948 static int last_data_encoded;
4952 int diff_hi, diff_lo;
4953 int data_hi, data_lo;
4954 unsigned short data_decoded;
4958 last_data_encoded = 0;
4965 diff = data_encoded - last_data_encoded;
4966 diff_hi = diff & ~0xff;
4967 diff_lo = diff & 0xff;
4971 data_hi = diff_hi - (offset1 << 8) + (offset2 & 0xff00);
4972 data_lo = (diff_lo + (data_hi >> 16)) & 0x00ff;
4973 data_hi = data_hi & 0xff00;
4975 data_decoded = data_hi | data_lo;
4977 last_data_encoded = data_encoded;
4979 offset1 = (offset1 + 1) % 31;
4980 offset2 = offset2 & 0xff;
4982 return data_decoded;
4985 static int getMappedElement_DC(int element)
4993 // 0x0117 - 0x036e: (?)
4996 // 0x042d - 0x0684: (?)
5012 element = EL_CRYSTAL;
5015 case 0x0e77: // quicksand (boulder)
5016 element = EL_QUICKSAND_FAST_FULL;
5019 case 0x0e99: // slow quicksand (boulder)
5020 element = EL_QUICKSAND_FULL;
5024 element = EL_EM_EXIT_OPEN;
5028 element = EL_EM_EXIT_CLOSED;
5032 element = EL_EM_STEEL_EXIT_OPEN;
5036 element = EL_EM_STEEL_EXIT_CLOSED;
5039 case 0x0f4f: // dynamite (lit 1)
5040 element = EL_EM_DYNAMITE_ACTIVE;
5043 case 0x0f57: // dynamite (lit 2)
5044 element = EL_EM_DYNAMITE_ACTIVE;
5047 case 0x0f5f: // dynamite (lit 3)
5048 element = EL_EM_DYNAMITE_ACTIVE;
5051 case 0x0f67: // dynamite (lit 4)
5052 element = EL_EM_DYNAMITE_ACTIVE;
5059 element = EL_AMOEBA_WET;
5063 element = EL_AMOEBA_DROP;
5067 element = EL_DC_MAGIC_WALL;
5071 element = EL_SPACESHIP_UP;
5075 element = EL_SPACESHIP_DOWN;
5079 element = EL_SPACESHIP_LEFT;
5083 element = EL_SPACESHIP_RIGHT;
5087 element = EL_BUG_UP;
5091 element = EL_BUG_DOWN;
5095 element = EL_BUG_LEFT;
5099 element = EL_BUG_RIGHT;
5103 element = EL_MOLE_UP;
5107 element = EL_MOLE_DOWN;
5111 element = EL_MOLE_LEFT;
5115 element = EL_MOLE_RIGHT;
5123 element = EL_YAMYAM_UP;
5127 element = EL_SWITCHGATE_OPEN;
5131 element = EL_SWITCHGATE_CLOSED;
5135 element = EL_DC_SWITCHGATE_SWITCH_UP;
5139 element = EL_TIMEGATE_CLOSED;
5142 case 0x144c: // conveyor belt switch (green)
5143 element = EL_CONVEYOR_BELT_3_SWITCH_MIDDLE;
5146 case 0x144f: // conveyor belt switch (red)
5147 element = EL_CONVEYOR_BELT_1_SWITCH_MIDDLE;
5150 case 0x1452: // conveyor belt switch (blue)
5151 element = EL_CONVEYOR_BELT_4_SWITCH_MIDDLE;
5155 element = EL_CONVEYOR_BELT_3_MIDDLE;
5159 element = EL_CONVEYOR_BELT_3_LEFT;
5163 element = EL_CONVEYOR_BELT_3_RIGHT;
5167 element = EL_CONVEYOR_BELT_1_MIDDLE;
5171 element = EL_CONVEYOR_BELT_1_LEFT;
5175 element = EL_CONVEYOR_BELT_1_RIGHT;
5179 element = EL_CONVEYOR_BELT_4_MIDDLE;
5183 element = EL_CONVEYOR_BELT_4_LEFT;
5187 element = EL_CONVEYOR_BELT_4_RIGHT;
5191 element = EL_EXPANDABLE_WALL_HORIZONTAL;
5195 element = EL_EXPANDABLE_WALL_VERTICAL;
5199 element = EL_EXPANDABLE_WALL_ANY;
5202 case 0x14ce: // growing steel wall (left/right)
5203 element = EL_EXPANDABLE_STEELWALL_HORIZONTAL;
5206 case 0x14df: // growing steel wall (up/down)
5207 element = EL_EXPANDABLE_STEELWALL_VERTICAL;
5210 case 0x14e8: // growing steel wall (up/down/left/right)
5211 element = EL_EXPANDABLE_STEELWALL_ANY;
5215 element = EL_SHIELD_DEADLY;
5219 element = EL_EXTRA_TIME;
5227 element = EL_EMPTY_SPACE;
5230 case 0x1578: // quicksand (empty)
5231 element = EL_QUICKSAND_FAST_EMPTY;
5234 case 0x1579: // slow quicksand (empty)
5235 element = EL_QUICKSAND_EMPTY;
5245 element = EL_EM_DYNAMITE;
5248 case 0x15a1: // key (red)
5249 element = EL_EM_KEY_1;
5252 case 0x15a2: // key (yellow)
5253 element = EL_EM_KEY_2;
5256 case 0x15a3: // key (blue)
5257 element = EL_EM_KEY_4;
5260 case 0x15a4: // key (green)
5261 element = EL_EM_KEY_3;
5264 case 0x15a5: // key (white)
5265 element = EL_DC_KEY_WHITE;
5269 element = EL_WALL_SLIPPERY;
5276 case 0x15a8: // wall (not round)
5280 case 0x15a9: // (blue)
5281 element = EL_CHAR_A;
5284 case 0x15aa: // (blue)
5285 element = EL_CHAR_B;
5288 case 0x15ab: // (blue)
5289 element = EL_CHAR_C;
5292 case 0x15ac: // (blue)
5293 element = EL_CHAR_D;
5296 case 0x15ad: // (blue)
5297 element = EL_CHAR_E;
5300 case 0x15ae: // (blue)
5301 element = EL_CHAR_F;
5304 case 0x15af: // (blue)
5305 element = EL_CHAR_G;
5308 case 0x15b0: // (blue)
5309 element = EL_CHAR_H;
5312 case 0x15b1: // (blue)
5313 element = EL_CHAR_I;
5316 case 0x15b2: // (blue)
5317 element = EL_CHAR_J;
5320 case 0x15b3: // (blue)
5321 element = EL_CHAR_K;
5324 case 0x15b4: // (blue)
5325 element = EL_CHAR_L;
5328 case 0x15b5: // (blue)
5329 element = EL_CHAR_M;
5332 case 0x15b6: // (blue)
5333 element = EL_CHAR_N;
5336 case 0x15b7: // (blue)
5337 element = EL_CHAR_O;
5340 case 0x15b8: // (blue)
5341 element = EL_CHAR_P;
5344 case 0x15b9: // (blue)
5345 element = EL_CHAR_Q;
5348 case 0x15ba: // (blue)
5349 element = EL_CHAR_R;
5352 case 0x15bb: // (blue)
5353 element = EL_CHAR_S;
5356 case 0x15bc: // (blue)
5357 element = EL_CHAR_T;
5360 case 0x15bd: // (blue)
5361 element = EL_CHAR_U;
5364 case 0x15be: // (blue)
5365 element = EL_CHAR_V;
5368 case 0x15bf: // (blue)
5369 element = EL_CHAR_W;
5372 case 0x15c0: // (blue)
5373 element = EL_CHAR_X;
5376 case 0x15c1: // (blue)
5377 element = EL_CHAR_Y;
5380 case 0x15c2: // (blue)
5381 element = EL_CHAR_Z;
5384 case 0x15c3: // (blue)
5385 element = EL_CHAR_AUMLAUT;
5388 case 0x15c4: // (blue)
5389 element = EL_CHAR_OUMLAUT;
5392 case 0x15c5: // (blue)
5393 element = EL_CHAR_UUMLAUT;
5396 case 0x15c6: // (blue)
5397 element = EL_CHAR_0;
5400 case 0x15c7: // (blue)
5401 element = EL_CHAR_1;
5404 case 0x15c8: // (blue)
5405 element = EL_CHAR_2;
5408 case 0x15c9: // (blue)
5409 element = EL_CHAR_3;
5412 case 0x15ca: // (blue)
5413 element = EL_CHAR_4;
5416 case 0x15cb: // (blue)
5417 element = EL_CHAR_5;
5420 case 0x15cc: // (blue)
5421 element = EL_CHAR_6;
5424 case 0x15cd: // (blue)
5425 element = EL_CHAR_7;
5428 case 0x15ce: // (blue)
5429 element = EL_CHAR_8;
5432 case 0x15cf: // (blue)
5433 element = EL_CHAR_9;
5436 case 0x15d0: // (blue)
5437 element = EL_CHAR_PERIOD;
5440 case 0x15d1: // (blue)
5441 element = EL_CHAR_EXCLAM;
5444 case 0x15d2: // (blue)
5445 element = EL_CHAR_COLON;
5448 case 0x15d3: // (blue)
5449 element = EL_CHAR_LESS;
5452 case 0x15d4: // (blue)
5453 element = EL_CHAR_GREATER;
5456 case 0x15d5: // (blue)
5457 element = EL_CHAR_QUESTION;
5460 case 0x15d6: // (blue)
5461 element = EL_CHAR_COPYRIGHT;
5464 case 0x15d7: // (blue)
5465 element = EL_CHAR_UP;
5468 case 0x15d8: // (blue)
5469 element = EL_CHAR_DOWN;
5472 case 0x15d9: // (blue)
5473 element = EL_CHAR_BUTTON;
5476 case 0x15da: // (blue)
5477 element = EL_CHAR_PLUS;
5480 case 0x15db: // (blue)
5481 element = EL_CHAR_MINUS;
5484 case 0x15dc: // (blue)
5485 element = EL_CHAR_APOSTROPHE;
5488 case 0x15dd: // (blue)
5489 element = EL_CHAR_PARENLEFT;
5492 case 0x15de: // (blue)
5493 element = EL_CHAR_PARENRIGHT;
5496 case 0x15df: // (green)
5497 element = EL_CHAR_A;
5500 case 0x15e0: // (green)
5501 element = EL_CHAR_B;
5504 case 0x15e1: // (green)
5505 element = EL_CHAR_C;
5508 case 0x15e2: // (green)
5509 element = EL_CHAR_D;
5512 case 0x15e3: // (green)
5513 element = EL_CHAR_E;
5516 case 0x15e4: // (green)
5517 element = EL_CHAR_F;
5520 case 0x15e5: // (green)
5521 element = EL_CHAR_G;
5524 case 0x15e6: // (green)
5525 element = EL_CHAR_H;
5528 case 0x15e7: // (green)
5529 element = EL_CHAR_I;
5532 case 0x15e8: // (green)
5533 element = EL_CHAR_J;
5536 case 0x15e9: // (green)
5537 element = EL_CHAR_K;
5540 case 0x15ea: // (green)
5541 element = EL_CHAR_L;
5544 case 0x15eb: // (green)
5545 element = EL_CHAR_M;
5548 case 0x15ec: // (green)
5549 element = EL_CHAR_N;
5552 case 0x15ed: // (green)
5553 element = EL_CHAR_O;
5556 case 0x15ee: // (green)
5557 element = EL_CHAR_P;
5560 case 0x15ef: // (green)
5561 element = EL_CHAR_Q;
5564 case 0x15f0: // (green)
5565 element = EL_CHAR_R;
5568 case 0x15f1: // (green)
5569 element = EL_CHAR_S;
5572 case 0x15f2: // (green)
5573 element = EL_CHAR_T;
5576 case 0x15f3: // (green)
5577 element = EL_CHAR_U;
5580 case 0x15f4: // (green)
5581 element = EL_CHAR_V;
5584 case 0x15f5: // (green)
5585 element = EL_CHAR_W;
5588 case 0x15f6: // (green)
5589 element = EL_CHAR_X;
5592 case 0x15f7: // (green)
5593 element = EL_CHAR_Y;
5596 case 0x15f8: // (green)
5597 element = EL_CHAR_Z;
5600 case 0x15f9: // (green)
5601 element = EL_CHAR_AUMLAUT;
5604 case 0x15fa: // (green)
5605 element = EL_CHAR_OUMLAUT;
5608 case 0x15fb: // (green)
5609 element = EL_CHAR_UUMLAUT;
5612 case 0x15fc: // (green)
5613 element = EL_CHAR_0;
5616 case 0x15fd: // (green)
5617 element = EL_CHAR_1;
5620 case 0x15fe: // (green)
5621 element = EL_CHAR_2;
5624 case 0x15ff: // (green)
5625 element = EL_CHAR_3;
5628 case 0x1600: // (green)
5629 element = EL_CHAR_4;
5632 case 0x1601: // (green)
5633 element = EL_CHAR_5;
5636 case 0x1602: // (green)
5637 element = EL_CHAR_6;
5640 case 0x1603: // (green)
5641 element = EL_CHAR_7;
5644 case 0x1604: // (green)
5645 element = EL_CHAR_8;
5648 case 0x1605: // (green)
5649 element = EL_CHAR_9;
5652 case 0x1606: // (green)
5653 element = EL_CHAR_PERIOD;
5656 case 0x1607: // (green)
5657 element = EL_CHAR_EXCLAM;
5660 case 0x1608: // (green)
5661 element = EL_CHAR_COLON;
5664 case 0x1609: // (green)
5665 element = EL_CHAR_LESS;
5668 case 0x160a: // (green)
5669 element = EL_CHAR_GREATER;
5672 case 0x160b: // (green)
5673 element = EL_CHAR_QUESTION;
5676 case 0x160c: // (green)
5677 element = EL_CHAR_COPYRIGHT;
5680 case 0x160d: // (green)
5681 element = EL_CHAR_UP;
5684 case 0x160e: // (green)
5685 element = EL_CHAR_DOWN;
5688 case 0x160f: // (green)
5689 element = EL_CHAR_BUTTON;
5692 case 0x1610: // (green)
5693 element = EL_CHAR_PLUS;
5696 case 0x1611: // (green)
5697 element = EL_CHAR_MINUS;
5700 case 0x1612: // (green)
5701 element = EL_CHAR_APOSTROPHE;
5704 case 0x1613: // (green)
5705 element = EL_CHAR_PARENLEFT;
5708 case 0x1614: // (green)
5709 element = EL_CHAR_PARENRIGHT;
5712 case 0x1615: // (blue steel)
5713 element = EL_STEEL_CHAR_A;
5716 case 0x1616: // (blue steel)
5717 element = EL_STEEL_CHAR_B;
5720 case 0x1617: // (blue steel)
5721 element = EL_STEEL_CHAR_C;
5724 case 0x1618: // (blue steel)
5725 element = EL_STEEL_CHAR_D;
5728 case 0x1619: // (blue steel)
5729 element = EL_STEEL_CHAR_E;
5732 case 0x161a: // (blue steel)
5733 element = EL_STEEL_CHAR_F;
5736 case 0x161b: // (blue steel)
5737 element = EL_STEEL_CHAR_G;
5740 case 0x161c: // (blue steel)
5741 element = EL_STEEL_CHAR_H;
5744 case 0x161d: // (blue steel)
5745 element = EL_STEEL_CHAR_I;
5748 case 0x161e: // (blue steel)
5749 element = EL_STEEL_CHAR_J;
5752 case 0x161f: // (blue steel)
5753 element = EL_STEEL_CHAR_K;
5756 case 0x1620: // (blue steel)
5757 element = EL_STEEL_CHAR_L;
5760 case 0x1621: // (blue steel)
5761 element = EL_STEEL_CHAR_M;
5764 case 0x1622: // (blue steel)
5765 element = EL_STEEL_CHAR_N;
5768 case 0x1623: // (blue steel)
5769 element = EL_STEEL_CHAR_O;
5772 case 0x1624: // (blue steel)
5773 element = EL_STEEL_CHAR_P;
5776 case 0x1625: // (blue steel)
5777 element = EL_STEEL_CHAR_Q;
5780 case 0x1626: // (blue steel)
5781 element = EL_STEEL_CHAR_R;
5784 case 0x1627: // (blue steel)
5785 element = EL_STEEL_CHAR_S;
5788 case 0x1628: // (blue steel)
5789 element = EL_STEEL_CHAR_T;
5792 case 0x1629: // (blue steel)
5793 element = EL_STEEL_CHAR_U;
5796 case 0x162a: // (blue steel)
5797 element = EL_STEEL_CHAR_V;
5800 case 0x162b: // (blue steel)
5801 element = EL_STEEL_CHAR_W;
5804 case 0x162c: // (blue steel)
5805 element = EL_STEEL_CHAR_X;
5808 case 0x162d: // (blue steel)
5809 element = EL_STEEL_CHAR_Y;
5812 case 0x162e: // (blue steel)
5813 element = EL_STEEL_CHAR_Z;
5816 case 0x162f: // (blue steel)
5817 element = EL_STEEL_CHAR_AUMLAUT;
5820 case 0x1630: // (blue steel)
5821 element = EL_STEEL_CHAR_OUMLAUT;
5824 case 0x1631: // (blue steel)
5825 element = EL_STEEL_CHAR_UUMLAUT;
5828 case 0x1632: // (blue steel)
5829 element = EL_STEEL_CHAR_0;
5832 case 0x1633: // (blue steel)
5833 element = EL_STEEL_CHAR_1;
5836 case 0x1634: // (blue steel)
5837 element = EL_STEEL_CHAR_2;
5840 case 0x1635: // (blue steel)
5841 element = EL_STEEL_CHAR_3;
5844 case 0x1636: // (blue steel)
5845 element = EL_STEEL_CHAR_4;
5848 case 0x1637: // (blue steel)
5849 element = EL_STEEL_CHAR_5;
5852 case 0x1638: // (blue steel)
5853 element = EL_STEEL_CHAR_6;
5856 case 0x1639: // (blue steel)
5857 element = EL_STEEL_CHAR_7;
5860 case 0x163a: // (blue steel)
5861 element = EL_STEEL_CHAR_8;
5864 case 0x163b: // (blue steel)
5865 element = EL_STEEL_CHAR_9;
5868 case 0x163c: // (blue steel)
5869 element = EL_STEEL_CHAR_PERIOD;
5872 case 0x163d: // (blue steel)
5873 element = EL_STEEL_CHAR_EXCLAM;
5876 case 0x163e: // (blue steel)
5877 element = EL_STEEL_CHAR_COLON;
5880 case 0x163f: // (blue steel)
5881 element = EL_STEEL_CHAR_LESS;
5884 case 0x1640: // (blue steel)
5885 element = EL_STEEL_CHAR_GREATER;
5888 case 0x1641: // (blue steel)
5889 element = EL_STEEL_CHAR_QUESTION;
5892 case 0x1642: // (blue steel)
5893 element = EL_STEEL_CHAR_COPYRIGHT;
5896 case 0x1643: // (blue steel)
5897 element = EL_STEEL_CHAR_UP;
5900 case 0x1644: // (blue steel)
5901 element = EL_STEEL_CHAR_DOWN;
5904 case 0x1645: // (blue steel)
5905 element = EL_STEEL_CHAR_BUTTON;
5908 case 0x1646: // (blue steel)
5909 element = EL_STEEL_CHAR_PLUS;
5912 case 0x1647: // (blue steel)
5913 element = EL_STEEL_CHAR_MINUS;
5916 case 0x1648: // (blue steel)
5917 element = EL_STEEL_CHAR_APOSTROPHE;
5920 case 0x1649: // (blue steel)
5921 element = EL_STEEL_CHAR_PARENLEFT;
5924 case 0x164a: // (blue steel)
5925 element = EL_STEEL_CHAR_PARENRIGHT;
5928 case 0x164b: // (green steel)
5929 element = EL_STEEL_CHAR_A;
5932 case 0x164c: // (green steel)
5933 element = EL_STEEL_CHAR_B;
5936 case 0x164d: // (green steel)
5937 element = EL_STEEL_CHAR_C;
5940 case 0x164e: // (green steel)
5941 element = EL_STEEL_CHAR_D;
5944 case 0x164f: // (green steel)
5945 element = EL_STEEL_CHAR_E;
5948 case 0x1650: // (green steel)
5949 element = EL_STEEL_CHAR_F;
5952 case 0x1651: // (green steel)
5953 element = EL_STEEL_CHAR_G;
5956 case 0x1652: // (green steel)
5957 element = EL_STEEL_CHAR_H;
5960 case 0x1653: // (green steel)
5961 element = EL_STEEL_CHAR_I;
5964 case 0x1654: // (green steel)
5965 element = EL_STEEL_CHAR_J;
5968 case 0x1655: // (green steel)
5969 element = EL_STEEL_CHAR_K;
5972 case 0x1656: // (green steel)
5973 element = EL_STEEL_CHAR_L;
5976 case 0x1657: // (green steel)
5977 element = EL_STEEL_CHAR_M;
5980 case 0x1658: // (green steel)
5981 element = EL_STEEL_CHAR_N;
5984 case 0x1659: // (green steel)
5985 element = EL_STEEL_CHAR_O;
5988 case 0x165a: // (green steel)
5989 element = EL_STEEL_CHAR_P;
5992 case 0x165b: // (green steel)
5993 element = EL_STEEL_CHAR_Q;
5996 case 0x165c: // (green steel)
5997 element = EL_STEEL_CHAR_R;
6000 case 0x165d: // (green steel)
6001 element = EL_STEEL_CHAR_S;
6004 case 0x165e: // (green steel)
6005 element = EL_STEEL_CHAR_T;
6008 case 0x165f: // (green steel)
6009 element = EL_STEEL_CHAR_U;
6012 case 0x1660: // (green steel)
6013 element = EL_STEEL_CHAR_V;
6016 case 0x1661: // (green steel)
6017 element = EL_STEEL_CHAR_W;
6020 case 0x1662: // (green steel)
6021 element = EL_STEEL_CHAR_X;
6024 case 0x1663: // (green steel)
6025 element = EL_STEEL_CHAR_Y;
6028 case 0x1664: // (green steel)
6029 element = EL_STEEL_CHAR_Z;
6032 case 0x1665: // (green steel)
6033 element = EL_STEEL_CHAR_AUMLAUT;
6036 case 0x1666: // (green steel)
6037 element = EL_STEEL_CHAR_OUMLAUT;
6040 case 0x1667: // (green steel)
6041 element = EL_STEEL_CHAR_UUMLAUT;
6044 case 0x1668: // (green steel)
6045 element = EL_STEEL_CHAR_0;
6048 case 0x1669: // (green steel)
6049 element = EL_STEEL_CHAR_1;
6052 case 0x166a: // (green steel)
6053 element = EL_STEEL_CHAR_2;
6056 case 0x166b: // (green steel)
6057 element = EL_STEEL_CHAR_3;
6060 case 0x166c: // (green steel)
6061 element = EL_STEEL_CHAR_4;
6064 case 0x166d: // (green steel)
6065 element = EL_STEEL_CHAR_5;
6068 case 0x166e: // (green steel)
6069 element = EL_STEEL_CHAR_6;
6072 case 0x166f: // (green steel)
6073 element = EL_STEEL_CHAR_7;
6076 case 0x1670: // (green steel)
6077 element = EL_STEEL_CHAR_8;
6080 case 0x1671: // (green steel)
6081 element = EL_STEEL_CHAR_9;
6084 case 0x1672: // (green steel)
6085 element = EL_STEEL_CHAR_PERIOD;
6088 case 0x1673: // (green steel)
6089 element = EL_STEEL_CHAR_EXCLAM;
6092 case 0x1674: // (green steel)
6093 element = EL_STEEL_CHAR_COLON;
6096 case 0x1675: // (green steel)
6097 element = EL_STEEL_CHAR_LESS;
6100 case 0x1676: // (green steel)
6101 element = EL_STEEL_CHAR_GREATER;
6104 case 0x1677: // (green steel)
6105 element = EL_STEEL_CHAR_QUESTION;
6108 case 0x1678: // (green steel)
6109 element = EL_STEEL_CHAR_COPYRIGHT;
6112 case 0x1679: // (green steel)
6113 element = EL_STEEL_CHAR_UP;
6116 case 0x167a: // (green steel)
6117 element = EL_STEEL_CHAR_DOWN;
6120 case 0x167b: // (green steel)
6121 element = EL_STEEL_CHAR_BUTTON;
6124 case 0x167c: // (green steel)
6125 element = EL_STEEL_CHAR_PLUS;
6128 case 0x167d: // (green steel)
6129 element = EL_STEEL_CHAR_MINUS;
6132 case 0x167e: // (green steel)
6133 element = EL_STEEL_CHAR_APOSTROPHE;
6136 case 0x167f: // (green steel)
6137 element = EL_STEEL_CHAR_PARENLEFT;
6140 case 0x1680: // (green steel)
6141 element = EL_STEEL_CHAR_PARENRIGHT;
6144 case 0x1681: // gate (red)
6145 element = EL_EM_GATE_1;
6148 case 0x1682: // secret gate (red)
6149 element = EL_EM_GATE_1_GRAY;
6152 case 0x1683: // gate (yellow)
6153 element = EL_EM_GATE_2;
6156 case 0x1684: // secret gate (yellow)
6157 element = EL_EM_GATE_2_GRAY;
6160 case 0x1685: // gate (blue)
6161 element = EL_EM_GATE_4;
6164 case 0x1686: // secret gate (blue)
6165 element = EL_EM_GATE_4_GRAY;
6168 case 0x1687: // gate (green)
6169 element = EL_EM_GATE_3;
6172 case 0x1688: // secret gate (green)
6173 element = EL_EM_GATE_3_GRAY;
6176 case 0x1689: // gate (white)
6177 element = EL_DC_GATE_WHITE;
6180 case 0x168a: // secret gate (white)
6181 element = EL_DC_GATE_WHITE_GRAY;
6184 case 0x168b: // secret gate (no key)
6185 element = EL_DC_GATE_FAKE_GRAY;
6189 element = EL_ROBOT_WHEEL;
6193 element = EL_DC_TIMEGATE_SWITCH;
6197 element = EL_ACID_POOL_BOTTOM;
6201 element = EL_ACID_POOL_TOPLEFT;
6205 element = EL_ACID_POOL_TOPRIGHT;
6209 element = EL_ACID_POOL_BOTTOMLEFT;
6213 element = EL_ACID_POOL_BOTTOMRIGHT;
6217 element = EL_STEELWALL;
6221 element = EL_STEELWALL_SLIPPERY;
6224 case 0x1695: // steel wall (not round)
6225 element = EL_STEELWALL;
6228 case 0x1696: // steel wall (left)
6229 element = EL_DC_STEELWALL_1_LEFT;
6232 case 0x1697: // steel wall (bottom)
6233 element = EL_DC_STEELWALL_1_BOTTOM;
6236 case 0x1698: // steel wall (right)
6237 element = EL_DC_STEELWALL_1_RIGHT;
6240 case 0x1699: // steel wall (top)
6241 element = EL_DC_STEELWALL_1_TOP;
6244 case 0x169a: // steel wall (left/bottom)
6245 element = EL_DC_STEELWALL_1_BOTTOMLEFT;
6248 case 0x169b: // steel wall (right/bottom)
6249 element = EL_DC_STEELWALL_1_BOTTOMRIGHT;
6252 case 0x169c: // steel wall (right/top)
6253 element = EL_DC_STEELWALL_1_TOPRIGHT;
6256 case 0x169d: // steel wall (left/top)
6257 element = EL_DC_STEELWALL_1_TOPLEFT;
6260 case 0x169e: // steel wall (right/bottom small)
6261 element = EL_DC_STEELWALL_1_BOTTOMRIGHT_2;
6264 case 0x169f: // steel wall (left/bottom small)
6265 element = EL_DC_STEELWALL_1_BOTTOMLEFT_2;
6268 case 0x16a0: // steel wall (right/top small)
6269 element = EL_DC_STEELWALL_1_TOPRIGHT_2;
6272 case 0x16a1: // steel wall (left/top small)
6273 element = EL_DC_STEELWALL_1_TOPLEFT_2;
6276 case 0x16a2: // steel wall (left/right)
6277 element = EL_DC_STEELWALL_1_VERTICAL;
6280 case 0x16a3: // steel wall (top/bottom)
6281 element = EL_DC_STEELWALL_1_HORIZONTAL;
6284 case 0x16a4: // steel wall 2 (left end)
6285 element = EL_DC_STEELWALL_2_LEFT;
6288 case 0x16a5: // steel wall 2 (right end)
6289 element = EL_DC_STEELWALL_2_RIGHT;
6292 case 0x16a6: // steel wall 2 (top end)
6293 element = EL_DC_STEELWALL_2_TOP;
6296 case 0x16a7: // steel wall 2 (bottom end)
6297 element = EL_DC_STEELWALL_2_BOTTOM;
6300 case 0x16a8: // steel wall 2 (left/right)
6301 element = EL_DC_STEELWALL_2_HORIZONTAL;
6304 case 0x16a9: // steel wall 2 (up/down)
6305 element = EL_DC_STEELWALL_2_VERTICAL;
6308 case 0x16aa: // steel wall 2 (mid)
6309 element = EL_DC_STEELWALL_2_MIDDLE;
6313 element = EL_SIGN_EXCLAMATION;
6317 element = EL_SIGN_RADIOACTIVITY;
6321 element = EL_SIGN_STOP;
6325 element = EL_SIGN_WHEELCHAIR;
6329 element = EL_SIGN_PARKING;
6333 element = EL_SIGN_NO_ENTRY;
6337 element = EL_SIGN_HEART;
6341 element = EL_SIGN_GIVE_WAY;
6345 element = EL_SIGN_ENTRY_FORBIDDEN;
6349 element = EL_SIGN_EMERGENCY_EXIT;
6353 element = EL_SIGN_YIN_YANG;
6357 element = EL_WALL_EMERALD;
6361 element = EL_WALL_DIAMOND;
6365 element = EL_WALL_PEARL;
6369 element = EL_WALL_CRYSTAL;
6373 element = EL_INVISIBLE_WALL;
6377 element = EL_INVISIBLE_STEELWALL;
6381 // EL_INVISIBLE_SAND
6384 element = EL_LIGHT_SWITCH;
6388 element = EL_ENVELOPE_1;
6392 if (element >= 0x0117 && element <= 0x036e) // (?)
6393 element = EL_DIAMOND;
6394 else if (element >= 0x042d && element <= 0x0684) // (?)
6395 element = EL_EMERALD;
6396 else if (element >= 0x157c && element <= 0x158b)
6398 else if (element >= 0x1590 && element <= 0x159f)
6399 element = EL_DC_LANDMINE;
6400 else if (element >= 0x16bc && element <= 0x16cb)
6401 element = EL_INVISIBLE_SAND;
6404 Warn("unknown Diamond Caves element 0x%04x", element);
6406 element = EL_UNKNOWN;
6411 return getMappedElement(element);
6414 static void LoadLevelFromFileStream_DC(File *file, struct LevelInfo *level)
6416 byte header[DC_LEVEL_HEADER_SIZE];
6418 int envelope_header_pos = 62;
6419 int envelope_content_pos = 94;
6420 int level_name_pos = 251;
6421 int level_author_pos = 292;
6422 int envelope_header_len;
6423 int envelope_content_len;
6425 int level_author_len;
6427 int num_yamyam_contents;
6430 getDecodedWord_DC(0, TRUE); // initialize DC2 decoding engine
6432 for (i = 0; i < DC_LEVEL_HEADER_SIZE / 2; i++)
6434 unsigned short header_word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
6436 header[i * 2 + 0] = header_word >> 8;
6437 header[i * 2 + 1] = header_word & 0xff;
6440 // read some values from level header to check level decoding integrity
6441 fieldx = header[6] | (header[7] << 8);
6442 fieldy = header[8] | (header[9] << 8);
6443 num_yamyam_contents = header[60] | (header[61] << 8);
6445 // do some simple sanity checks to ensure that level was correctly decoded
6446 if (fieldx < 1 || fieldx > 256 ||
6447 fieldy < 1 || fieldy > 256 ||
6448 num_yamyam_contents < 1 || num_yamyam_contents > 8)
6450 level->no_valid_file = TRUE;
6452 Warn("cannot decode level from stream -- using empty level");
6457 // maximum envelope header size is 31 bytes
6458 envelope_header_len = header[envelope_header_pos];
6459 // maximum envelope content size is 110 (156?) bytes
6460 envelope_content_len = header[envelope_content_pos];
6462 // maximum level title size is 40 bytes
6463 level_name_len = MIN(header[level_name_pos], MAX_LEVEL_NAME_LEN);
6464 // maximum level author size is 30 (51?) bytes
6465 level_author_len = MIN(header[level_author_pos], MAX_LEVEL_AUTHOR_LEN);
6469 for (i = 0; i < envelope_header_len; i++)
6470 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
6471 level->envelope[0].text[envelope_size++] =
6472 header[envelope_header_pos + 1 + i];
6474 if (envelope_header_len > 0 && envelope_content_len > 0)
6476 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
6477 level->envelope[0].text[envelope_size++] = '\n';
6478 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
6479 level->envelope[0].text[envelope_size++] = '\n';
6482 for (i = 0; i < envelope_content_len; i++)
6483 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
6484 level->envelope[0].text[envelope_size++] =
6485 header[envelope_content_pos + 1 + i];
6487 level->envelope[0].text[envelope_size] = '\0';
6489 level->envelope[0].xsize = MAX_ENVELOPE_XSIZE;
6490 level->envelope[0].ysize = 10;
6491 level->envelope[0].autowrap = TRUE;
6492 level->envelope[0].centered = TRUE;
6494 for (i = 0; i < level_name_len; i++)
6495 level->name[i] = header[level_name_pos + 1 + i];
6496 level->name[level_name_len] = '\0';
6498 for (i = 0; i < level_author_len; i++)
6499 level->author[i] = header[level_author_pos + 1 + i];
6500 level->author[level_author_len] = '\0';
6502 num_yamyam_contents = header[60] | (header[61] << 8);
6503 level->num_yamyam_contents =
6504 MIN(MAX(MIN_ELEMENT_CONTENTS, num_yamyam_contents), MAX_ELEMENT_CONTENTS);
6506 for (i = 0; i < num_yamyam_contents; i++)
6508 for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
6510 unsigned short word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
6511 int element_dc = ((word & 0xff) << 8) | ((word >> 8) & 0xff);
6513 if (i < MAX_ELEMENT_CONTENTS)
6514 level->yamyam_content[i].e[x][y] = getMappedElement_DC(element_dc);
6518 fieldx = header[6] | (header[7] << 8);
6519 fieldy = header[8] | (header[9] << 8);
6520 level->fieldx = MIN(MAX(MIN_LEV_FIELDX, fieldx), MAX_LEV_FIELDX);
6521 level->fieldy = MIN(MAX(MIN_LEV_FIELDY, fieldy), MAX_LEV_FIELDY);
6523 for (y = 0; y < fieldy; y++) for (x = 0; x < fieldx; x++)
6525 unsigned short word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
6526 int element_dc = ((word & 0xff) << 8) | ((word >> 8) & 0xff);
6528 if (x < MAX_LEV_FIELDX && y < MAX_LEV_FIELDY)
6529 level->field[x][y] = getMappedElement_DC(element_dc);
6532 x = MIN(MAX(0, (header[10] | (header[11] << 8)) - 1), MAX_LEV_FIELDX - 1);
6533 y = MIN(MAX(0, (header[12] | (header[13] << 8)) - 1), MAX_LEV_FIELDY - 1);
6534 level->field[x][y] = EL_PLAYER_1;
6536 x = MIN(MAX(0, (header[14] | (header[15] << 8)) - 1), MAX_LEV_FIELDX - 1);
6537 y = MIN(MAX(0, (header[16] | (header[17] << 8)) - 1), MAX_LEV_FIELDY - 1);
6538 level->field[x][y] = EL_PLAYER_2;
6540 level->gems_needed = header[18] | (header[19] << 8);
6542 level->score[SC_EMERALD] = header[20] | (header[21] << 8);
6543 level->score[SC_DIAMOND] = header[22] | (header[23] << 8);
6544 level->score[SC_PEARL] = header[24] | (header[25] << 8);
6545 level->score[SC_CRYSTAL] = header[26] | (header[27] << 8);
6546 level->score[SC_NUT] = header[28] | (header[29] << 8);
6547 level->score[SC_ROBOT] = header[30] | (header[31] << 8);
6548 level->score[SC_SPACESHIP] = header[32] | (header[33] << 8);
6549 level->score[SC_BUG] = header[34] | (header[35] << 8);
6550 level->score[SC_YAMYAM] = header[36] | (header[37] << 8);
6551 level->score[SC_DYNAMITE] = header[38] | (header[39] << 8);
6552 level->score[SC_KEY] = header[40] | (header[41] << 8);
6553 level->score[SC_TIME_BONUS] = header[42] | (header[43] << 8);
6555 level->time = header[44] | (header[45] << 8);
6557 level->amoeba_speed = header[46] | (header[47] << 8);
6558 level->time_light = header[48] | (header[49] << 8);
6559 level->time_timegate = header[50] | (header[51] << 8);
6560 level->time_wheel = header[52] | (header[53] << 8);
6561 level->time_magic_wall = header[54] | (header[55] << 8);
6562 level->extra_time = header[56] | (header[57] << 8);
6563 level->shield_normal_time = header[58] | (header[59] << 8);
6565 // shield and extra time elements do not have a score
6566 level->score[SC_SHIELD] = 0;
6567 level->extra_time_score = 0;
6569 // set time for normal and deadly shields to the same value
6570 level->shield_deadly_time = level->shield_normal_time;
6572 // Diamond Caves has the same (strange) behaviour as Emerald Mine that gems
6573 // can slip down from flat walls, like normal walls and steel walls
6574 level->em_slippery_gems = TRUE;
6576 // time score is counted for each 10 seconds left in Diamond Caves levels
6577 level->time_score_base = 10;
6580 static void LoadLevelFromFileInfo_DC(struct LevelInfo *level,
6581 struct LevelFileInfo *level_file_info,
6582 boolean level_info_only)
6584 char *filename = level_file_info->filename;
6586 int num_magic_bytes = 8;
6587 char magic_bytes[num_magic_bytes + 1];
6588 int num_levels_to_skip = level_file_info->nr - leveldir_current->first_level;
6590 if (!(file = openFile(filename, MODE_READ)))
6592 level->no_valid_file = TRUE;
6594 if (!level_info_only)
6595 Warn("cannot read level '%s' -- using empty level", filename);
6600 // fseek(file, 0x0000, SEEK_SET);
6602 if (level_file_info->packed)
6604 // read "magic bytes" from start of file
6605 if (getStringFromFile(file, magic_bytes, num_magic_bytes + 1) == NULL)
6606 magic_bytes[0] = '\0';
6608 // check "magic bytes" for correct file format
6609 if (!strPrefix(magic_bytes, "DC2"))
6611 level->no_valid_file = TRUE;
6613 Warn("unknown DC level file '%s' -- using empty level", filename);
6618 if (strPrefix(magic_bytes, "DC2Win95") ||
6619 strPrefix(magic_bytes, "DC2Win98"))
6621 int position_first_level = 0x00fa;
6622 int extra_bytes = 4;
6625 // advance file stream to first level inside the level package
6626 skip_bytes = position_first_level - num_magic_bytes - extra_bytes;
6628 // each block of level data is followed by block of non-level data
6629 num_levels_to_skip *= 2;
6631 // at least skip header bytes, therefore use ">= 0" instead of "> 0"
6632 while (num_levels_to_skip >= 0)
6634 // advance file stream to next level inside the level package
6635 if (seekFile(file, skip_bytes, SEEK_CUR) != 0)
6637 level->no_valid_file = TRUE;
6639 Warn("cannot fseek in file '%s' -- using empty level", filename);
6644 // skip apparently unused extra bytes following each level
6645 ReadUnusedBytesFromFile(file, extra_bytes);
6647 // read size of next level in level package
6648 skip_bytes = getFile32BitLE(file);
6650 num_levels_to_skip--;
6655 level->no_valid_file = TRUE;
6657 Warn("unknown DC2 level file '%s' -- using empty level", filename);
6663 LoadLevelFromFileStream_DC(file, level);
6669 // ----------------------------------------------------------------------------
6670 // functions for loading SB level
6671 // ----------------------------------------------------------------------------
6673 int getMappedElement_SB(int element_ascii, boolean use_ces)
6681 sb_element_mapping[] =
6683 { ' ', EL_EMPTY, EL_CUSTOM_1 }, // floor (space)
6684 { '#', EL_STEELWALL, EL_CUSTOM_2 }, // wall
6685 { '@', EL_PLAYER_1, EL_CUSTOM_3 }, // player
6686 { '$', EL_SOKOBAN_OBJECT, EL_CUSTOM_4 }, // box
6687 { '.', EL_SOKOBAN_FIELD_EMPTY, EL_CUSTOM_5 }, // goal square
6688 { '*', EL_SOKOBAN_FIELD_FULL, EL_CUSTOM_6 }, // box on goal square
6689 { '+', EL_SOKOBAN_FIELD_PLAYER, EL_CUSTOM_7 }, // player on goal square
6690 { '_', EL_INVISIBLE_STEELWALL, EL_FROM_LEVEL_TEMPLATE }, // floor beyond border
6697 for (i = 0; sb_element_mapping[i].ascii != 0; i++)
6698 if (element_ascii == sb_element_mapping[i].ascii)
6699 return (use_ces ? sb_element_mapping[i].ce : sb_element_mapping[i].sb);
6701 return EL_UNDEFINED;
6704 static void SetLevelSettings_SB(struct LevelInfo *level)
6708 level->use_step_counter = TRUE;
6711 level->score[SC_TIME_BONUS] = 0;
6712 level->time_score_base = 1;
6713 level->rate_time_over_score = TRUE;
6716 level->auto_exit_sokoban = TRUE;
6719 static void LoadLevelFromFileInfo_SB(struct LevelInfo *level,
6720 struct LevelFileInfo *level_file_info,
6721 boolean level_info_only)
6723 char *filename = level_file_info->filename;
6724 char line[MAX_LINE_LEN], line_raw[MAX_LINE_LEN], previous_line[MAX_LINE_LEN];
6725 char last_comment[MAX_LINE_LEN];
6726 char level_name[MAX_LINE_LEN];
6729 int num_levels_to_skip = level_file_info->nr - leveldir_current->first_level;
6730 boolean read_continued_line = FALSE;
6731 boolean reading_playfield = FALSE;
6732 boolean got_valid_playfield_line = FALSE;
6733 boolean invalid_playfield_char = FALSE;
6734 boolean load_xsb_to_ces = check_special_flags("load_xsb_to_ces");
6735 int file_level_nr = 0;
6736 int x = 0, y = 0; // initialized to make compilers happy
6738 last_comment[0] = '\0';
6739 level_name[0] = '\0';
6741 if (!(file = openFile(filename, MODE_READ)))
6743 level->no_valid_file = TRUE;
6745 if (!level_info_only)
6746 Warn("cannot read level '%s' -- using empty level", filename);
6751 while (!checkEndOfFile(file))
6753 // level successfully read, but next level may follow here
6754 if (!got_valid_playfield_line && reading_playfield)
6756 // read playfield from single level file -- skip remaining file
6757 if (!level_file_info->packed)
6760 if (file_level_nr >= num_levels_to_skip)
6765 last_comment[0] = '\0';
6766 level_name[0] = '\0';
6768 reading_playfield = FALSE;
6771 got_valid_playfield_line = FALSE;
6773 // read next line of input file
6774 if (!getStringFromFile(file, line, MAX_LINE_LEN))
6777 // cut trailing line break (this can be newline and/or carriage return)
6778 for (line_ptr = &line[strlen(line)]; line_ptr >= line; line_ptr--)
6779 if ((*line_ptr == '\n' || *line_ptr == '\r') && *(line_ptr + 1) == '\0')
6782 // copy raw input line for later use (mainly debugging output)
6783 strcpy(line_raw, line);
6785 if (read_continued_line)
6787 // append new line to existing line, if there is enough space
6788 if (strlen(previous_line) + strlen(line_ptr) < MAX_LINE_LEN)
6789 strcat(previous_line, line_ptr);
6791 strcpy(line, previous_line); // copy storage buffer to line
6793 read_continued_line = FALSE;
6796 // if the last character is '\', continue at next line
6797 if (strlen(line) > 0 && line[strlen(line) - 1] == '\\')
6799 line[strlen(line) - 1] = '\0'; // cut off trailing backslash
6800 strcpy(previous_line, line); // copy line to storage buffer
6802 read_continued_line = TRUE;
6808 if (line[0] == '\0')
6811 // extract comment text from comment line
6814 for (line_ptr = line; *line_ptr; line_ptr++)
6815 if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != ';')
6818 strcpy(last_comment, line_ptr);
6823 // extract level title text from line containing level title
6824 if (line[0] == '\'')
6826 strcpy(level_name, &line[1]);
6828 if (strlen(level_name) > 0 && level_name[strlen(level_name) - 1] == '\'')
6829 level_name[strlen(level_name) - 1] = '\0';
6834 // skip lines containing only spaces (or empty lines)
6835 for (line_ptr = line; *line_ptr; line_ptr++)
6836 if (*line_ptr != ' ')
6838 if (*line_ptr == '\0')
6841 // at this point, we have found a line containing part of a playfield
6843 got_valid_playfield_line = TRUE;
6845 if (!reading_playfield)
6847 reading_playfield = TRUE;
6848 invalid_playfield_char = FALSE;
6850 for (x = 0; x < MAX_LEV_FIELDX; x++)
6851 for (y = 0; y < MAX_LEV_FIELDY; y++)
6852 level->field[x][y] = getMappedElement_SB(' ', load_xsb_to_ces);
6857 // start with topmost tile row
6861 // skip playfield line if larger row than allowed
6862 if (y >= MAX_LEV_FIELDY)
6865 // start with leftmost tile column
6868 // read playfield elements from line
6869 for (line_ptr = line; *line_ptr; line_ptr++)
6871 int mapped_sb_element = getMappedElement_SB(*line_ptr, load_xsb_to_ces);
6873 // stop parsing playfield line if larger column than allowed
6874 if (x >= MAX_LEV_FIELDX)
6877 if (mapped_sb_element == EL_UNDEFINED)
6879 invalid_playfield_char = TRUE;
6884 level->field[x][y] = mapped_sb_element;
6886 // continue with next tile column
6889 level->fieldx = MAX(x, level->fieldx);
6892 if (invalid_playfield_char)
6894 // if first playfield line, treat invalid lines as comment lines
6896 reading_playfield = FALSE;
6901 // continue with next tile row
6909 level->fieldx = MIN(MAX(MIN_LEV_FIELDX, level->fieldx), MAX_LEV_FIELDX);
6910 level->fieldy = MIN(MAX(MIN_LEV_FIELDY, level->fieldy), MAX_LEV_FIELDY);
6912 if (!reading_playfield)
6914 level->no_valid_file = TRUE;
6916 Warn("cannot read level '%s' -- using empty level", filename);
6921 if (*level_name != '\0')
6923 strncpy(level->name, level_name, MAX_LEVEL_NAME_LEN);
6924 level->name[MAX_LEVEL_NAME_LEN] = '\0';
6926 else if (*last_comment != '\0')
6928 strncpy(level->name, last_comment, MAX_LEVEL_NAME_LEN);
6929 level->name[MAX_LEVEL_NAME_LEN] = '\0';
6933 sprintf(level->name, "--> Level %d <--", level_file_info->nr);
6936 // set all empty fields beyond the border walls to invisible steel wall
6937 for (y = 0; y < level->fieldy; y++) for (x = 0; x < level->fieldx; x++)
6939 if ((x == 0 || x == level->fieldx - 1 ||
6940 y == 0 || y == level->fieldy - 1) &&
6941 level->field[x][y] == getMappedElement_SB(' ', load_xsb_to_ces))
6942 FloodFillLevel(x, y, getMappedElement_SB('_', load_xsb_to_ces),
6943 level->field, level->fieldx, level->fieldy);
6946 // set special level settings for Sokoban levels
6947 SetLevelSettings_SB(level);
6949 if (load_xsb_to_ces)
6951 // special global settings can now be set in level template
6952 level->use_custom_template = TRUE;
6957 // -------------------------------------------------------------------------
6958 // functions for handling native levels
6959 // -------------------------------------------------------------------------
6961 static void LoadLevelFromFileInfo_BD(struct LevelInfo *level,
6962 struct LevelFileInfo *level_file_info,
6963 boolean level_info_only)
6967 // determine position of requested level inside level package
6968 if (level_file_info->packed)
6969 pos = level_file_info->nr - leveldir_current->first_level;
6971 if (!LoadNativeLevel_BD(level_file_info->filename, pos, level_info_only))
6972 level->no_valid_file = TRUE;
6975 static void LoadLevelFromFileInfo_EM(struct LevelInfo *level,
6976 struct LevelFileInfo *level_file_info,
6977 boolean level_info_only)
6979 if (!LoadNativeLevel_EM(level_file_info->filename, level_info_only))
6980 level->no_valid_file = TRUE;
6983 static void LoadLevelFromFileInfo_SP(struct LevelInfo *level,
6984 struct LevelFileInfo *level_file_info,
6985 boolean level_info_only)
6989 // determine position of requested level inside level package
6990 if (level_file_info->packed)
6991 pos = level_file_info->nr - leveldir_current->first_level;
6993 if (!LoadNativeLevel_SP(level_file_info->filename, pos, level_info_only))
6994 level->no_valid_file = TRUE;
6997 static void LoadLevelFromFileInfo_MM(struct LevelInfo *level,
6998 struct LevelFileInfo *level_file_info,
6999 boolean level_info_only)
7001 if (!LoadNativeLevel_MM(level_file_info->filename, level_info_only))
7002 level->no_valid_file = TRUE;
7005 void CopyNativeLevel_RND_to_Native(struct LevelInfo *level)
7007 if (level->game_engine_type == GAME_ENGINE_TYPE_BD)
7008 CopyNativeLevel_RND_to_BD(level);
7009 else if (level->game_engine_type == GAME_ENGINE_TYPE_EM)
7010 CopyNativeLevel_RND_to_EM(level);
7011 else if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
7012 CopyNativeLevel_RND_to_SP(level);
7013 else if (level->game_engine_type == GAME_ENGINE_TYPE_MM)
7014 CopyNativeLevel_RND_to_MM(level);
7017 void CopyNativeLevel_Native_to_RND(struct LevelInfo *level)
7019 if (level->game_engine_type == GAME_ENGINE_TYPE_BD)
7020 CopyNativeLevel_BD_to_RND(level);
7021 else if (level->game_engine_type == GAME_ENGINE_TYPE_EM)
7022 CopyNativeLevel_EM_to_RND(level);
7023 else if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
7024 CopyNativeLevel_SP_to_RND(level);
7025 else if (level->game_engine_type == GAME_ENGINE_TYPE_MM)
7026 CopyNativeLevel_MM_to_RND(level);
7029 void SaveNativeLevel(struct LevelInfo *level)
7031 // saving native level files only supported for some game engines
7032 if (level->game_engine_type != GAME_ENGINE_TYPE_BD &&
7033 level->game_engine_type != GAME_ENGINE_TYPE_SP)
7036 char *file_ext = (level->game_engine_type == GAME_ENGINE_TYPE_BD ? "bd" :
7037 level->game_engine_type == GAME_ENGINE_TYPE_SP ? "sp" : "");
7038 char *basename = getSingleLevelBasenameExt(level->file_info.nr, file_ext);
7039 char *filename = getLevelFilenameFromBasename(basename);
7041 if (fileExists(filename) && !Request("Native level file already exists! Overwrite it?", REQ_ASK))
7044 boolean success = FALSE;
7046 if (level->game_engine_type == GAME_ENGINE_TYPE_BD)
7048 CopyNativeLevel_RND_to_BD(level);
7049 // CopyNativeTape_RND_to_BD(level);
7051 success = SaveNativeLevel_BD(filename);
7053 else if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
7055 CopyNativeLevel_RND_to_SP(level);
7056 CopyNativeTape_RND_to_SP(level);
7058 success = SaveNativeLevel_SP(filename);
7062 Request("Native level file saved!", REQ_CONFIRM);
7064 Request("Failed to save native level file!", REQ_CONFIRM);
7068 // ----------------------------------------------------------------------------
7069 // functions for loading generic level
7070 // ----------------------------------------------------------------------------
7072 static void LoadLevelFromFileInfo(struct LevelInfo *level,
7073 struct LevelFileInfo *level_file_info,
7074 boolean level_info_only)
7076 // always start with reliable default values
7077 setLevelInfoToDefaults(level, level_info_only, TRUE);
7079 switch (level_file_info->type)
7081 case LEVEL_FILE_TYPE_RND:
7082 LoadLevelFromFileInfo_RND(level, level_file_info, level_info_only);
7085 case LEVEL_FILE_TYPE_BD:
7086 LoadLevelFromFileInfo_BD(level, level_file_info, level_info_only);
7087 level->game_engine_type = GAME_ENGINE_TYPE_BD;
7090 case LEVEL_FILE_TYPE_EM:
7091 LoadLevelFromFileInfo_EM(level, level_file_info, level_info_only);
7092 level->game_engine_type = GAME_ENGINE_TYPE_EM;
7095 case LEVEL_FILE_TYPE_SP:
7096 LoadLevelFromFileInfo_SP(level, level_file_info, level_info_only);
7097 level->game_engine_type = GAME_ENGINE_TYPE_SP;
7100 case LEVEL_FILE_TYPE_MM:
7101 LoadLevelFromFileInfo_MM(level, level_file_info, level_info_only);
7102 level->game_engine_type = GAME_ENGINE_TYPE_MM;
7105 case LEVEL_FILE_TYPE_DC:
7106 LoadLevelFromFileInfo_DC(level, level_file_info, level_info_only);
7109 case LEVEL_FILE_TYPE_SB:
7110 LoadLevelFromFileInfo_SB(level, level_file_info, level_info_only);
7114 LoadLevelFromFileInfo_RND(level, level_file_info, level_info_only);
7118 // if level file is invalid, restore level structure to default values
7119 if (level->no_valid_file)
7120 setLevelInfoToDefaults(level, level_info_only, FALSE);
7122 if (check_special_flags("use_native_bd_game_engine"))
7123 level->game_engine_type = GAME_ENGINE_TYPE_BD;
7125 if (level->game_engine_type == GAME_ENGINE_TYPE_UNKNOWN)
7126 level->game_engine_type = GAME_ENGINE_TYPE_RND;
7128 if (level_file_info->type != LEVEL_FILE_TYPE_RND)
7129 CopyNativeLevel_Native_to_RND(level);
7132 void LoadLevelFromFilename(struct LevelInfo *level, char *filename)
7134 static struct LevelFileInfo level_file_info;
7136 // always start with reliable default values
7137 setFileInfoToDefaults(&level_file_info);
7139 level_file_info.nr = 0; // unknown level number
7140 level_file_info.type = LEVEL_FILE_TYPE_RND; // no others supported yet
7142 setString(&level_file_info.filename, filename);
7144 LoadLevelFromFileInfo(level, &level_file_info, FALSE);
7147 static void LoadLevel_InitVersion(struct LevelInfo *level)
7151 if (leveldir_current == NULL) // only when dumping level
7154 // all engine modifications also valid for levels which use latest engine
7155 if (level->game_version < VERSION_IDENT(3,2,0,5))
7157 // time bonus score was given for 10 s instead of 1 s before 3.2.0-5
7158 level->time_score_base = 10;
7161 if (leveldir_current->latest_engine)
7163 // ---------- use latest game engine --------------------------------------
7165 /* For all levels which are forced to use the latest game engine version
7166 (normally all but user contributed, private and undefined levels), set
7167 the game engine version to the actual version; this allows for actual
7168 corrections in the game engine to take effect for existing, converted
7169 levels (from "classic" or other existing games) to make the emulation
7170 of the corresponding game more accurate, while (hopefully) not breaking
7171 existing levels created from other players. */
7173 level->game_version = GAME_VERSION_ACTUAL;
7175 /* Set special EM style gems behaviour: EM style gems slip down from
7176 normal, steel and growing wall. As this is a more fundamental change,
7177 it seems better to set the default behaviour to "off" (as it is more
7178 natural) and make it configurable in the level editor (as a property
7179 of gem style elements). Already existing converted levels (neither
7180 private nor contributed levels) are changed to the new behaviour. */
7182 if (level->file_version < FILE_VERSION_2_0)
7183 level->em_slippery_gems = TRUE;
7188 // ---------- use game engine the level was created with --------------------
7190 /* For all levels which are not forced to use the latest game engine
7191 version (normally user contributed, private and undefined levels),
7192 use the version of the game engine the levels were created for.
7194 Since 2.0.1, the game engine version is now directly stored
7195 in the level file (chunk "VERS"), so there is no need anymore
7196 to set the game version from the file version (except for old,
7197 pre-2.0 levels, where the game version is still taken from the
7198 file format version used to store the level -- see above). */
7200 // player was faster than enemies in 1.0.0 and before
7201 if (level->file_version == FILE_VERSION_1_0)
7202 for (i = 0; i < MAX_PLAYERS; i++)
7203 level->initial_player_stepsize[i] = STEPSIZE_FAST;
7205 // default behaviour for EM style gems was "slippery" only in 2.0.1
7206 if (level->game_version == VERSION_IDENT(2,0,1,0))
7207 level->em_slippery_gems = TRUE;
7209 // springs could be pushed over pits before (pre-release version) 2.2.0
7210 if (level->game_version < VERSION_IDENT(2,2,0,0))
7211 level->use_spring_bug = TRUE;
7213 if (level->game_version < VERSION_IDENT(3,2,0,5))
7215 // time orb caused limited time in endless time levels before 3.2.0-5
7216 level->use_time_orb_bug = TRUE;
7218 // default behaviour for snapping was "no snap delay" before 3.2.0-5
7219 level->block_snap_field = FALSE;
7221 // extra time score was same value as time left score before 3.2.0-5
7222 level->extra_time_score = level->score[SC_TIME_BONUS];
7225 if (level->game_version < VERSION_IDENT(3,2,0,7))
7227 // default behaviour for snapping was "not continuous" before 3.2.0-7
7228 level->continuous_snapping = FALSE;
7231 // only few elements were able to actively move into acid before 3.1.0
7232 // trigger settings did not exist before 3.1.0; set to default "any"
7233 if (level->game_version < VERSION_IDENT(3,1,0,0))
7235 // correct "can move into acid" settings (all zero in old levels)
7237 level->can_move_into_acid_bits = 0; // nothing can move into acid
7238 level->dont_collide_with_bits = 0; // nothing is deadly when colliding
7240 setMoveIntoAcidProperty(level, EL_ROBOT, TRUE);
7241 setMoveIntoAcidProperty(level, EL_SATELLITE, TRUE);
7242 setMoveIntoAcidProperty(level, EL_PENGUIN, TRUE);
7243 setMoveIntoAcidProperty(level, EL_BALLOON, TRUE);
7245 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7246 SET_PROPERTY(EL_CUSTOM_START + i, EP_CAN_MOVE_INTO_ACID, TRUE);
7248 // correct trigger settings (stored as zero == "none" in old levels)
7250 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7252 int element = EL_CUSTOM_START + i;
7253 struct ElementInfo *ei = &element_info[element];
7255 for (j = 0; j < ei->num_change_pages; j++)
7257 struct ElementChangeInfo *change = &ei->change_page[j];
7259 change->trigger_player = CH_PLAYER_ANY;
7260 change->trigger_page = CH_PAGE_ANY;
7265 // try to detect and fix "Snake Bite" levels, which are broken with 3.2.0
7267 int element = EL_CUSTOM_256;
7268 struct ElementInfo *ei = &element_info[element];
7269 struct ElementChangeInfo *change = &ei->change_page[0];
7271 /* This is needed to fix a problem that was caused by a bugfix in function
7272 game.c/CreateFieldExt() introduced with 3.2.0 that corrects the behaviour
7273 when a custom element changes to EL_SOKOBAN_FIELD_PLAYER (before, it did
7274 not replace walkable elements, but instead just placed the player on it,
7275 without placing the Sokoban field under the player). Unfortunately, this
7276 breaks "Snake Bite" style levels when the snake is halfway through a door
7277 that just closes (the snake head is still alive and can be moved in this
7278 case). This can be fixed by replacing the EL_SOKOBAN_FIELD_PLAYER by the
7279 player (without Sokoban element) which then gets killed as designed). */
7281 if ((strncmp(leveldir_current->identifier, "snake_bite", 10) == 0 ||
7282 strncmp(ei->description, "pause b4 death", 14) == 0) &&
7283 change->target_element == EL_SOKOBAN_FIELD_PLAYER)
7284 change->target_element = EL_PLAYER_1;
7287 // try to detect and fix "Zelda" style levels, which are broken with 3.2.5
7288 if (level->game_version < VERSION_IDENT(3,2,5,0))
7290 /* This is needed to fix a problem that was caused by a bugfix in function
7291 game.c/CheckTriggeredElementChangeExt() introduced with 3.2.5 that
7292 corrects the behaviour when a custom element changes to another custom
7293 element with a higher element number that has change actions defined.
7294 Normally, only one change per frame is allowed for custom elements.
7295 Therefore, it is checked if a custom element already changed in the
7296 current frame; if it did, subsequent changes are suppressed.
7297 Unfortunately, this is only checked for element changes, but not for
7298 change actions, which are still executed. As the function above loops
7299 through all custom elements from lower to higher, an element change
7300 resulting in a lower CE number won't be checked again, while a target
7301 element with a higher number will also be checked, and potential change
7302 actions will get executed for this CE, too (which is wrong), while
7303 further changes are ignored (which is correct). As this bugfix breaks
7304 Zelda II (and introduces graphical bugs to Zelda I, and also breaks a
7305 few other levels like Alan Bond's "FMV"), allow the previous, incorrect
7306 behaviour for existing levels and tapes that make use of this bug */
7308 level->use_action_after_change_bug = TRUE;
7311 // not centering level after relocating player was default only in 3.2.3
7312 if (level->game_version == VERSION_IDENT(3,2,3,0)) // (no pre-releases)
7313 level->shifted_relocation = TRUE;
7315 // EM style elements always chain-exploded in R'n'D engine before 3.2.6
7316 if (level->game_version < VERSION_IDENT(3,2,6,0))
7317 level->em_explodes_by_fire = TRUE;
7319 // levels were solved by the first player entering an exit up to 4.1.0.0
7320 if (level->game_version <= VERSION_IDENT(4,1,0,0))
7321 level->solved_by_one_player = TRUE;
7323 // game logic of "game of life" and "biomaze" was buggy before 4.1.1.1
7324 if (level->game_version < VERSION_IDENT(4,1,1,1))
7325 level->use_life_bugs = TRUE;
7327 // only Sokoban fields (but not objects) had to be solved before 4.1.1.1
7328 if (level->game_version < VERSION_IDENT(4,1,1,1))
7329 level->sb_objects_needed = FALSE;
7331 // CE actions were triggered by unfinished digging/collecting up to 4.2.2.0
7332 if (level->game_version <= VERSION_IDENT(4,2,2,0))
7333 level->finish_dig_collect = FALSE;
7335 // CE changing to player was kept under the player if walkable up to 4.2.3.1
7336 if (level->game_version <= VERSION_IDENT(4,2,3,1))
7337 level->keep_walkable_ce = TRUE;
7340 static void LoadLevel_InitSettings_SB(struct LevelInfo *level)
7342 boolean is_sokoban_level = TRUE; // unless non-Sokoban elements found
7345 // check if this level is (not) a Sokoban level
7346 for (y = 0; y < level->fieldy; y++)
7347 for (x = 0; x < level->fieldx; x++)
7348 if (!IS_SB_ELEMENT(Tile[x][y]))
7349 is_sokoban_level = FALSE;
7351 if (is_sokoban_level)
7353 // set special level settings for Sokoban levels
7354 SetLevelSettings_SB(level);
7358 static void LoadLevel_InitSettings(struct LevelInfo *level)
7360 // adjust level settings for (non-native) Sokoban-style levels
7361 LoadLevel_InitSettings_SB(level);
7363 // rename levels with title "nameless level" or if renaming is forced
7364 if (leveldir_current->empty_level_name != NULL &&
7365 (strEqual(level->name, NAMELESS_LEVEL_NAME) ||
7366 leveldir_current->force_level_name))
7367 snprintf(level->name, MAX_LEVEL_NAME_LEN + 1,
7368 leveldir_current->empty_level_name, level_nr);
7371 static void LoadLevel_InitStandardElements(struct LevelInfo *level)
7375 // map elements that have changed in newer versions
7376 level->amoeba_content = getMappedElementByVersion(level->amoeba_content,
7377 level->game_version);
7378 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
7379 for (x = 0; x < 3; x++)
7380 for (y = 0; y < 3; y++)
7381 level->yamyam_content[i].e[x][y] =
7382 getMappedElementByVersion(level->yamyam_content[i].e[x][y],
7383 level->game_version);
7387 static void LoadLevel_InitCustomElements(struct LevelInfo *level)
7391 // map custom element change events that have changed in newer versions
7392 // (these following values were accidentally changed in version 3.0.1)
7393 // (this seems to be needed only for 'ab_levelset3' and 'ab_levelset4')
7394 if (level->game_version <= VERSION_IDENT(3,0,0,0))
7396 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7398 int element = EL_CUSTOM_START + i;
7400 // order of checking and copying events to be mapped is important
7401 // (do not change the start and end value -- they are constant)
7402 for (j = CE_BY_OTHER_ACTION; j >= CE_VALUE_GETS_ZERO; j--)
7404 if (HAS_CHANGE_EVENT(element, j - 2))
7406 SET_CHANGE_EVENT(element, j - 2, FALSE);
7407 SET_CHANGE_EVENT(element, j, TRUE);
7411 // order of checking and copying events to be mapped is important
7412 // (do not change the start and end value -- they are constant)
7413 for (j = CE_PLAYER_COLLECTS_X; j >= CE_HITTING_SOMETHING; j--)
7415 if (HAS_CHANGE_EVENT(element, j - 1))
7417 SET_CHANGE_EVENT(element, j - 1, FALSE);
7418 SET_CHANGE_EVENT(element, j, TRUE);
7424 // initialize "can_change" field for old levels with only one change page
7425 if (level->game_version <= VERSION_IDENT(3,0,2,0))
7427 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7429 int element = EL_CUSTOM_START + i;
7431 if (CAN_CHANGE(element))
7432 element_info[element].change->can_change = TRUE;
7436 // correct custom element values (for old levels without these options)
7437 if (level->game_version < VERSION_IDENT(3,1,1,0))
7439 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7441 int element = EL_CUSTOM_START + i;
7442 struct ElementInfo *ei = &element_info[element];
7444 if (ei->access_direction == MV_NO_DIRECTION)
7445 ei->access_direction = MV_ALL_DIRECTIONS;
7449 // correct custom element values (fix invalid values for all versions)
7452 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7454 int element = EL_CUSTOM_START + i;
7455 struct ElementInfo *ei = &element_info[element];
7457 for (j = 0; j < ei->num_change_pages; j++)
7459 struct ElementChangeInfo *change = &ei->change_page[j];
7461 if (change->trigger_player == CH_PLAYER_NONE)
7462 change->trigger_player = CH_PLAYER_ANY;
7464 if (change->trigger_side == CH_SIDE_NONE)
7465 change->trigger_side = CH_SIDE_ANY;
7470 // initialize "can_explode" field for old levels which did not store this
7471 // !!! CHECK THIS -- "<= 3,1,0,0" IS PROBABLY WRONG !!!
7472 if (level->game_version <= VERSION_IDENT(3,1,0,0))
7474 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7476 int element = EL_CUSTOM_START + i;
7478 if (EXPLODES_1X1_OLD(element))
7479 element_info[element].explosion_type = EXPLODES_1X1;
7481 SET_PROPERTY(element, EP_CAN_EXPLODE, (EXPLODES_BY_FIRE(element) ||
7482 EXPLODES_SMASHED(element) ||
7483 EXPLODES_IMPACT(element)));
7487 // correct previously hard-coded move delay values for maze runner style
7488 if (level->game_version < VERSION_IDENT(3,1,1,0))
7490 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7492 int element = EL_CUSTOM_START + i;
7494 if (element_info[element].move_pattern & MV_MAZE_RUNNER_STYLE)
7496 // previously hard-coded and therefore ignored
7497 element_info[element].move_delay_fixed = 9;
7498 element_info[element].move_delay_random = 0;
7503 // set some other uninitialized values of custom elements in older levels
7504 if (level->game_version < VERSION_IDENT(3,1,0,0))
7506 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7508 int element = EL_CUSTOM_START + i;
7510 element_info[element].access_direction = MV_ALL_DIRECTIONS;
7512 element_info[element].explosion_delay = 17;
7513 element_info[element].ignition_delay = 8;
7517 // set mouse click change events to work for left/middle/right mouse button
7518 if (level->game_version < VERSION_IDENT(4,2,3,0))
7520 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7522 int element = EL_CUSTOM_START + i;
7523 struct ElementInfo *ei = &element_info[element];
7525 for (j = 0; j < ei->num_change_pages; j++)
7527 struct ElementChangeInfo *change = &ei->change_page[j];
7529 if (change->has_event[CE_CLICKED_BY_MOUSE] ||
7530 change->has_event[CE_PRESSED_BY_MOUSE] ||
7531 change->has_event[CE_MOUSE_CLICKED_ON_X] ||
7532 change->has_event[CE_MOUSE_PRESSED_ON_X])
7533 change->trigger_side = CH_SIDE_ANY;
7539 static void LoadLevel_InitElements(struct LevelInfo *level)
7541 LoadLevel_InitStandardElements(level);
7543 if (level->file_has_custom_elements)
7544 LoadLevel_InitCustomElements(level);
7546 // initialize element properties for level editor etc.
7547 InitElementPropertiesEngine(level->game_version);
7548 InitElementPropertiesGfxElement();
7551 static void LoadLevel_InitPlayfield(struct LevelInfo *level)
7555 // map elements that have changed in newer versions
7556 for (y = 0; y < level->fieldy; y++)
7557 for (x = 0; x < level->fieldx; x++)
7558 level->field[x][y] = getMappedElementByVersion(level->field[x][y],
7559 level->game_version);
7561 // clear unused playfield data (nicer if level gets resized in editor)
7562 for (x = 0; x < MAX_LEV_FIELDX; x++)
7563 for (y = 0; y < MAX_LEV_FIELDY; y++)
7564 if (x >= level->fieldx || y >= level->fieldy)
7565 level->field[x][y] = EL_EMPTY;
7567 // copy elements to runtime playfield array
7568 for (x = 0; x < MAX_LEV_FIELDX; x++)
7569 for (y = 0; y < MAX_LEV_FIELDY; y++)
7570 Tile[x][y] = level->field[x][y];
7572 // initialize level size variables for faster access
7573 lev_fieldx = level->fieldx;
7574 lev_fieldy = level->fieldy;
7576 // determine border element for this level
7577 if (level->file_info.type == LEVEL_FILE_TYPE_DC)
7578 BorderElement = EL_EMPTY; // (in editor, SetBorderElement() is used)
7583 static void LoadLevel_InitNativeEngines(struct LevelInfo *level)
7585 struct LevelFileInfo *level_file_info = &level->file_info;
7587 if (level_file_info->type == LEVEL_FILE_TYPE_RND)
7588 CopyNativeLevel_RND_to_Native(level);
7591 static void LoadLevelTemplate_LoadAndInit(void)
7593 LoadLevelFromFileInfo(&level_template, &level_template.file_info, FALSE);
7595 LoadLevel_InitVersion(&level_template);
7596 LoadLevel_InitElements(&level_template);
7597 LoadLevel_InitSettings(&level_template);
7599 ActivateLevelTemplate();
7602 void LoadLevelTemplate(int nr)
7604 if (!fileExists(getGlobalLevelTemplateFilename()))
7606 Warn("no level template found for this level");
7611 setLevelFileInfo(&level_template.file_info, nr);
7613 LoadLevelTemplate_LoadAndInit();
7616 static void LoadNetworkLevelTemplate(struct NetworkLevelInfo *network_level)
7618 copyLevelFileInfo(&network_level->tmpl_info, &level_template.file_info);
7620 LoadLevelTemplate_LoadAndInit();
7623 static void LoadLevel_LoadAndInit(struct NetworkLevelInfo *network_level)
7625 LoadLevelFromFileInfo(&level, &level.file_info, FALSE);
7627 if (level.use_custom_template)
7629 if (network_level != NULL)
7630 LoadNetworkLevelTemplate(network_level);
7632 LoadLevelTemplate(-1);
7635 LoadLevel_InitVersion(&level);
7636 LoadLevel_InitElements(&level);
7637 LoadLevel_InitPlayfield(&level);
7638 LoadLevel_InitSettings(&level);
7640 LoadLevel_InitNativeEngines(&level);
7643 void LoadLevel(int nr)
7645 SetLevelSetInfo(leveldir_current->identifier, nr);
7647 setLevelFileInfo(&level.file_info, nr);
7649 LoadLevel_LoadAndInit(NULL);
7652 void LoadLevelInfoOnly(int nr)
7654 setLevelFileInfo(&level.file_info, nr);
7656 LoadLevelFromFileInfo(&level, &level.file_info, TRUE);
7659 void LoadNetworkLevel(struct NetworkLevelInfo *network_level)
7661 SetLevelSetInfo(network_level->leveldir_identifier,
7662 network_level->file_info.nr);
7664 copyLevelFileInfo(&network_level->file_info, &level.file_info);
7666 LoadLevel_LoadAndInit(network_level);
7669 static int SaveLevel_VERS(FILE *file, struct LevelInfo *level)
7673 chunk_size += putFileVersion(file, level->file_version);
7674 chunk_size += putFileVersion(file, level->game_version);
7679 static int SaveLevel_DATE(FILE *file, struct LevelInfo *level)
7683 chunk_size += putFile16BitBE(file, level->creation_date.year);
7684 chunk_size += putFile8Bit(file, level->creation_date.month);
7685 chunk_size += putFile8Bit(file, level->creation_date.day);
7690 #if ENABLE_HISTORIC_CHUNKS
7691 static void SaveLevel_HEAD(FILE *file, struct LevelInfo *level)
7695 putFile8Bit(file, level->fieldx);
7696 putFile8Bit(file, level->fieldy);
7698 putFile16BitBE(file, level->time);
7699 putFile16BitBE(file, level->gems_needed);
7701 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
7702 putFile8Bit(file, level->name[i]);
7704 for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
7705 putFile8Bit(file, level->score[i]);
7707 for (i = 0; i < STD_ELEMENT_CONTENTS; i++)
7708 for (y = 0; y < 3; y++)
7709 for (x = 0; x < 3; x++)
7710 putFile8Bit(file, (level->encoding_16bit_yamyam ? EL_EMPTY :
7711 level->yamyam_content[i].e[x][y]));
7712 putFile8Bit(file, level->amoeba_speed);
7713 putFile8Bit(file, level->time_magic_wall);
7714 putFile8Bit(file, level->time_wheel);
7715 putFile8Bit(file, (level->encoding_16bit_amoeba ? EL_EMPTY :
7716 level->amoeba_content));
7717 putFile8Bit(file, (level->initial_player_stepsize == STEPSIZE_FAST ? 1 : 0));
7718 putFile8Bit(file, (level->initial_gravity ? 1 : 0));
7719 putFile8Bit(file, (level->encoding_16bit_field ? 1 : 0));
7720 putFile8Bit(file, (level->em_slippery_gems ? 1 : 0));
7722 putFile8Bit(file, (level->use_custom_template ? 1 : 0));
7724 putFile8Bit(file, (level->block_last_field ? 1 : 0));
7725 putFile8Bit(file, (level->sp_block_last_field ? 1 : 0));
7726 putFile32BitBE(file, level->can_move_into_acid_bits);
7727 putFile8Bit(file, level->dont_collide_with_bits);
7729 putFile8Bit(file, (level->use_spring_bug ? 1 : 0));
7730 putFile8Bit(file, (level->use_step_counter ? 1 : 0));
7732 putFile8Bit(file, (level->instant_relocation ? 1 : 0));
7733 putFile8Bit(file, (level->can_pass_to_walkable ? 1 : 0));
7734 putFile8Bit(file, (level->grow_into_diggable ? 1 : 0));
7736 putFile8Bit(file, level->game_engine_type);
7738 WriteUnusedBytesToFile(file, LEVEL_CHUNK_HEAD_UNUSED);
7742 static int SaveLevel_NAME(FILE *file, struct LevelInfo *level)
7747 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
7748 chunk_size += putFile8Bit(file, level->name[i]);
7753 static int SaveLevel_AUTH(FILE *file, struct LevelInfo *level)
7758 for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
7759 chunk_size += putFile8Bit(file, level->author[i]);
7764 #if ENABLE_HISTORIC_CHUNKS
7765 static int SaveLevel_BODY(FILE *file, struct LevelInfo *level)
7770 for (y = 0; y < level->fieldy; y++)
7771 for (x = 0; x < level->fieldx; x++)
7772 if (level->encoding_16bit_field)
7773 chunk_size += putFile16BitBE(file, level->field[x][y]);
7775 chunk_size += putFile8Bit(file, level->field[x][y]);
7781 static int SaveLevel_BODY(FILE *file, struct LevelInfo *level)
7786 for (y = 0; y < level->fieldy; y++)
7787 for (x = 0; x < level->fieldx; x++)
7788 chunk_size += putFile16BitBE(file, level->field[x][y]);
7793 #if ENABLE_HISTORIC_CHUNKS
7794 static void SaveLevel_CONT(FILE *file, struct LevelInfo *level)
7798 putFile8Bit(file, EL_YAMYAM);
7799 putFile8Bit(file, level->num_yamyam_contents);
7800 putFile8Bit(file, 0);
7801 putFile8Bit(file, 0);
7803 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
7804 for (y = 0; y < 3; y++)
7805 for (x = 0; x < 3; x++)
7806 if (level->encoding_16bit_field)
7807 putFile16BitBE(file, level->yamyam_content[i].e[x][y]);
7809 putFile8Bit(file, level->yamyam_content[i].e[x][y]);
7813 #if ENABLE_HISTORIC_CHUNKS
7814 static void SaveLevel_CNT2(FILE *file, struct LevelInfo *level, int element)
7817 int num_contents, content_xsize, content_ysize;
7818 int content_array[MAX_ELEMENT_CONTENTS][3][3];
7820 if (element == EL_YAMYAM)
7822 num_contents = level->num_yamyam_contents;
7826 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
7827 for (y = 0; y < 3; y++)
7828 for (x = 0; x < 3; x++)
7829 content_array[i][x][y] = level->yamyam_content[i].e[x][y];
7831 else if (element == EL_BD_AMOEBA)
7837 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
7838 for (y = 0; y < 3; y++)
7839 for (x = 0; x < 3; x++)
7840 content_array[i][x][y] = EL_EMPTY;
7841 content_array[0][0][0] = level->amoeba_content;
7845 // chunk header already written -- write empty chunk data
7846 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_SIZE);
7848 Warn("cannot save content for element '%d'", element);
7853 putFile16BitBE(file, element);
7854 putFile8Bit(file, num_contents);
7855 putFile8Bit(file, content_xsize);
7856 putFile8Bit(file, content_ysize);
7858 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_UNUSED);
7860 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
7861 for (y = 0; y < 3; y++)
7862 for (x = 0; x < 3; x++)
7863 putFile16BitBE(file, content_array[i][x][y]);
7867 #if ENABLE_HISTORIC_CHUNKS
7868 static int SaveLevel_CNT3(FILE *file, struct LevelInfo *level, int element)
7870 int envelope_nr = element - EL_ENVELOPE_1;
7871 int envelope_len = strlen(level->envelope_text[envelope_nr]) + 1;
7875 chunk_size += putFile16BitBE(file, element);
7876 chunk_size += putFile16BitBE(file, envelope_len);
7877 chunk_size += putFile8Bit(file, level->envelope_xsize[envelope_nr]);
7878 chunk_size += putFile8Bit(file, level->envelope_ysize[envelope_nr]);
7880 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT3_UNUSED);
7881 chunk_size += LEVEL_CHUNK_CNT3_UNUSED;
7883 for (i = 0; i < envelope_len; i++)
7884 chunk_size += putFile8Bit(file, level->envelope_text[envelope_nr][i]);
7890 #if ENABLE_HISTORIC_CHUNKS
7891 static void SaveLevel_CUS1(FILE *file, struct LevelInfo *level,
7892 int num_changed_custom_elements)
7896 putFile16BitBE(file, num_changed_custom_elements);
7898 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7900 int element = EL_CUSTOM_START + i;
7902 struct ElementInfo *ei = &element_info[element];
7904 if (ei->properties[EP_BITFIELD_BASE_NR] != EP_BITMASK_DEFAULT)
7906 if (check < num_changed_custom_elements)
7908 putFile16BitBE(file, element);
7909 putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
7916 if (check != num_changed_custom_elements) // should not happen
7917 Warn("inconsistent number of custom element properties");
7921 #if ENABLE_HISTORIC_CHUNKS
7922 static void SaveLevel_CUS2(FILE *file, struct LevelInfo *level,
7923 int num_changed_custom_elements)
7927 putFile16BitBE(file, num_changed_custom_elements);
7929 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7931 int element = EL_CUSTOM_START + i;
7933 if (element_info[element].change->target_element != EL_EMPTY_SPACE)
7935 if (check < num_changed_custom_elements)
7937 putFile16BitBE(file, element);
7938 putFile16BitBE(file, element_info[element].change->target_element);
7945 if (check != num_changed_custom_elements) // should not happen
7946 Warn("inconsistent number of custom target elements");
7950 #if ENABLE_HISTORIC_CHUNKS
7951 static void SaveLevel_CUS3(FILE *file, struct LevelInfo *level,
7952 int num_changed_custom_elements)
7954 int i, j, x, y, check = 0;
7956 putFile16BitBE(file, num_changed_custom_elements);
7958 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7960 int element = EL_CUSTOM_START + i;
7961 struct ElementInfo *ei = &element_info[element];
7963 if (ei->modified_settings)
7965 if (check < num_changed_custom_elements)
7967 putFile16BitBE(file, element);
7969 for (j = 0; j < MAX_ELEMENT_NAME_LEN; j++)
7970 putFile8Bit(file, ei->description[j]);
7972 putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
7974 // some free bytes for future properties and padding
7975 WriteUnusedBytesToFile(file, 7);
7977 putFile8Bit(file, ei->use_gfx_element);
7978 putFile16BitBE(file, ei->gfx_element_initial);
7980 putFile8Bit(file, ei->collect_score_initial);
7981 putFile8Bit(file, ei->collect_count_initial);
7983 putFile16BitBE(file, ei->push_delay_fixed);
7984 putFile16BitBE(file, ei->push_delay_random);
7985 putFile16BitBE(file, ei->move_delay_fixed);
7986 putFile16BitBE(file, ei->move_delay_random);
7988 putFile16BitBE(file, ei->move_pattern);
7989 putFile8Bit(file, ei->move_direction_initial);
7990 putFile8Bit(file, ei->move_stepsize);
7992 for (y = 0; y < 3; y++)
7993 for (x = 0; x < 3; x++)
7994 putFile16BitBE(file, ei->content.e[x][y]);
7996 putFile32BitBE(file, ei->change->events);
7998 putFile16BitBE(file, ei->change->target_element);
8000 putFile16BitBE(file, ei->change->delay_fixed);
8001 putFile16BitBE(file, ei->change->delay_random);
8002 putFile16BitBE(file, ei->change->delay_frames);
8004 putFile16BitBE(file, ei->change->initial_trigger_element);
8006 putFile8Bit(file, ei->change->explode);
8007 putFile8Bit(file, ei->change->use_target_content);
8008 putFile8Bit(file, ei->change->only_if_complete);
8009 putFile8Bit(file, ei->change->use_random_replace);
8011 putFile8Bit(file, ei->change->random_percentage);
8012 putFile8Bit(file, ei->change->replace_when);
8014 for (y = 0; y < 3; y++)
8015 for (x = 0; x < 3; x++)
8016 putFile16BitBE(file, ei->change->content.e[x][y]);
8018 putFile8Bit(file, ei->slippery_type);
8020 // some free bytes for future properties and padding
8021 WriteUnusedBytesToFile(file, LEVEL_CPART_CUS3_UNUSED);
8028 if (check != num_changed_custom_elements) // should not happen
8029 Warn("inconsistent number of custom element properties");
8033 #if ENABLE_HISTORIC_CHUNKS
8034 static void SaveLevel_CUS4(FILE *file, struct LevelInfo *level, int element)
8036 struct ElementInfo *ei = &element_info[element];
8039 // ---------- custom element base property values (96 bytes) ----------------
8041 putFile16BitBE(file, element);
8043 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
8044 putFile8Bit(file, ei->description[i]);
8046 putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
8048 WriteUnusedBytesToFile(file, 4); // reserved for more base properties
8050 putFile8Bit(file, ei->num_change_pages);
8052 putFile16BitBE(file, ei->ce_value_fixed_initial);
8053 putFile16BitBE(file, ei->ce_value_random_initial);
8054 putFile8Bit(file, ei->use_last_ce_value);
8056 putFile8Bit(file, ei->use_gfx_element);
8057 putFile16BitBE(file, ei->gfx_element_initial);
8059 putFile8Bit(file, ei->collect_score_initial);
8060 putFile8Bit(file, ei->collect_count_initial);
8062 putFile8Bit(file, ei->drop_delay_fixed);
8063 putFile8Bit(file, ei->push_delay_fixed);
8064 putFile8Bit(file, ei->drop_delay_random);
8065 putFile8Bit(file, ei->push_delay_random);
8066 putFile16BitBE(file, ei->move_delay_fixed);
8067 putFile16BitBE(file, ei->move_delay_random);
8069 // bits 0 - 15 of "move_pattern" ...
8070 putFile16BitBE(file, ei->move_pattern & 0xffff);
8071 putFile8Bit(file, ei->move_direction_initial);
8072 putFile8Bit(file, ei->move_stepsize);
8074 putFile8Bit(file, ei->slippery_type);
8076 for (y = 0; y < 3; y++)
8077 for (x = 0; x < 3; x++)
8078 putFile16BitBE(file, ei->content.e[x][y]);
8080 putFile16BitBE(file, ei->move_enter_element);
8081 putFile16BitBE(file, ei->move_leave_element);
8082 putFile8Bit(file, ei->move_leave_type);
8084 // ... bits 16 - 31 of "move_pattern" (not nice, but downward compatible)
8085 putFile16BitBE(file, (ei->move_pattern >> 16) & 0xffff);
8087 putFile8Bit(file, ei->access_direction);
8089 putFile8Bit(file, ei->explosion_delay);
8090 putFile8Bit(file, ei->ignition_delay);
8091 putFile8Bit(file, ei->explosion_type);
8093 // some free bytes for future custom property values and padding
8094 WriteUnusedBytesToFile(file, 1);
8096 // ---------- change page property values (48 bytes) ------------------------
8098 for (i = 0; i < ei->num_change_pages; i++)
8100 struct ElementChangeInfo *change = &ei->change_page[i];
8101 unsigned int event_bits;
8103 // bits 0 - 31 of "has_event[]" ...
8105 for (j = 0; j < MIN(NUM_CHANGE_EVENTS, 32); j++)
8106 if (change->has_event[j])
8107 event_bits |= (1u << j);
8108 putFile32BitBE(file, event_bits);
8110 putFile16BitBE(file, change->target_element);
8112 putFile16BitBE(file, change->delay_fixed);
8113 putFile16BitBE(file, change->delay_random);
8114 putFile16BitBE(file, change->delay_frames);
8116 putFile16BitBE(file, change->initial_trigger_element);
8118 putFile8Bit(file, change->explode);
8119 putFile8Bit(file, change->use_target_content);
8120 putFile8Bit(file, change->only_if_complete);
8121 putFile8Bit(file, change->use_random_replace);
8123 putFile8Bit(file, change->random_percentage);
8124 putFile8Bit(file, change->replace_when);
8126 for (y = 0; y < 3; y++)
8127 for (x = 0; x < 3; x++)
8128 putFile16BitBE(file, change->target_content.e[x][y]);
8130 putFile8Bit(file, change->can_change);
8132 putFile8Bit(file, change->trigger_side);
8134 putFile8Bit(file, change->trigger_player);
8135 putFile8Bit(file, (change->trigger_page == CH_PAGE_ANY ? CH_PAGE_ANY_FILE :
8136 log_2(change->trigger_page)));
8138 putFile8Bit(file, change->has_action);
8139 putFile8Bit(file, change->action_type);
8140 putFile8Bit(file, change->action_mode);
8141 putFile16BitBE(file, change->action_arg);
8143 // ... bits 32 - 39 of "has_event[]" (not nice, but downward compatible)
8145 for (j = 32; j < NUM_CHANGE_EVENTS; j++)
8146 if (change->has_event[j])
8147 event_bits |= (1u << (j - 32));
8148 putFile8Bit(file, event_bits);
8153 #if ENABLE_HISTORIC_CHUNKS
8154 static void SaveLevel_GRP1(FILE *file, struct LevelInfo *level, int element)
8156 struct ElementInfo *ei = &element_info[element];
8157 struct ElementGroupInfo *group = ei->group;
8160 putFile16BitBE(file, element);
8162 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
8163 putFile8Bit(file, ei->description[i]);
8165 putFile8Bit(file, group->num_elements);
8167 putFile8Bit(file, ei->use_gfx_element);
8168 putFile16BitBE(file, ei->gfx_element_initial);
8170 putFile8Bit(file, group->choice_mode);
8172 // some free bytes for future values and padding
8173 WriteUnusedBytesToFile(file, 3);
8175 for (i = 0; i < MAX_ELEMENTS_IN_GROUP; i++)
8176 putFile16BitBE(file, group->element[i]);
8180 static int SaveLevel_MicroChunk(FILE *file, struct LevelFileConfigInfo *entry,
8181 boolean write_element)
8183 int save_type = entry->save_type;
8184 int data_type = entry->data_type;
8185 int conf_type = entry->conf_type;
8186 int byte_mask = conf_type & CONF_MASK_BYTES;
8187 int element = entry->element;
8188 int default_value = entry->default_value;
8190 boolean modified = FALSE;
8192 if (byte_mask != CONF_MASK_MULTI_BYTES)
8194 void *value_ptr = entry->value;
8195 int value = (data_type == TYPE_BOOLEAN ? *(boolean *)value_ptr :
8198 // check if any settings have been modified before saving them
8199 if (value != default_value)
8202 // do not save if explicitly told or if unmodified default settings
8203 if ((save_type == SAVE_CONF_NEVER) ||
8204 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
8208 num_bytes += putFile16BitBE(file, element);
8210 num_bytes += putFile8Bit(file, conf_type);
8211 num_bytes += (byte_mask == CONF_MASK_1_BYTE ? putFile8Bit (file, value) :
8212 byte_mask == CONF_MASK_2_BYTE ? putFile16BitBE(file, value) :
8213 byte_mask == CONF_MASK_4_BYTE ? putFile32BitBE(file, value) :
8216 else if (data_type == TYPE_STRING)
8218 char *default_string = entry->default_string;
8219 char *string = (char *)(entry->value);
8220 int string_length = strlen(string);
8223 // check if any settings have been modified before saving them
8224 if (!strEqual(string, default_string))
8227 // do not save if explicitly told or if unmodified default settings
8228 if ((save_type == SAVE_CONF_NEVER) ||
8229 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
8233 num_bytes += putFile16BitBE(file, element);
8235 num_bytes += putFile8Bit(file, conf_type);
8236 num_bytes += putFile16BitBE(file, string_length);
8238 for (i = 0; i < string_length; i++)
8239 num_bytes += putFile8Bit(file, string[i]);
8241 else if (data_type == TYPE_ELEMENT_LIST)
8243 int *element_array = (int *)(entry->value);
8244 int num_elements = *(int *)(entry->num_entities);
8247 // check if any settings have been modified before saving them
8248 for (i = 0; i < num_elements; i++)
8249 if (element_array[i] != default_value)
8252 // do not save if explicitly told or if unmodified default settings
8253 if ((save_type == SAVE_CONF_NEVER) ||
8254 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
8258 num_bytes += putFile16BitBE(file, element);
8260 num_bytes += putFile8Bit(file, conf_type);
8261 num_bytes += putFile16BitBE(file, num_elements * CONF_ELEMENT_NUM_BYTES);
8263 for (i = 0; i < num_elements; i++)
8264 num_bytes += putFile16BitBE(file, element_array[i]);
8266 else if (data_type == TYPE_CONTENT_LIST)
8268 struct Content *content = (struct Content *)(entry->value);
8269 int num_contents = *(int *)(entry->num_entities);
8272 // check if any settings have been modified before saving them
8273 for (i = 0; i < num_contents; i++)
8274 for (y = 0; y < 3; y++)
8275 for (x = 0; x < 3; x++)
8276 if (content[i].e[x][y] != default_value)
8279 // do not save if explicitly told or if unmodified default settings
8280 if ((save_type == SAVE_CONF_NEVER) ||
8281 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
8285 num_bytes += putFile16BitBE(file, element);
8287 num_bytes += putFile8Bit(file, conf_type);
8288 num_bytes += putFile16BitBE(file, num_contents * CONF_CONTENT_NUM_BYTES);
8290 for (i = 0; i < num_contents; i++)
8291 for (y = 0; y < 3; y++)
8292 for (x = 0; x < 3; x++)
8293 num_bytes += putFile16BitBE(file, content[i].e[x][y]);
8299 static int SaveLevel_INFO(FILE *file, struct LevelInfo *level)
8304 li = *level; // copy level data into temporary buffer
8306 for (i = 0; chunk_config_INFO[i].data_type != -1; i++)
8307 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_INFO[i], FALSE);
8312 static int SaveLevel_ELEM(FILE *file, struct LevelInfo *level)
8317 li = *level; // copy level data into temporary buffer
8319 for (i = 0; chunk_config_ELEM[i].data_type != -1; i++)
8320 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_ELEM[i], TRUE);
8325 static int SaveLevel_NOTE(FILE *file, struct LevelInfo *level, int element)
8327 int envelope_nr = element - EL_ENVELOPE_1;
8331 chunk_size += putFile16BitBE(file, element);
8333 // copy envelope data into temporary buffer
8334 xx_envelope = level->envelope[envelope_nr];
8336 for (i = 0; chunk_config_NOTE[i].data_type != -1; i++)
8337 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_NOTE[i], FALSE);
8342 static int SaveLevel_CUSX(FILE *file, struct LevelInfo *level, int element)
8344 struct ElementInfo *ei = &element_info[element];
8348 chunk_size += putFile16BitBE(file, element);
8350 xx_ei = *ei; // copy element data into temporary buffer
8352 // set default description string for this specific element
8353 strcpy(xx_default_description, getDefaultElementDescription(ei));
8355 for (i = 0; chunk_config_CUSX_base[i].data_type != -1; i++)
8356 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_CUSX_base[i], FALSE);
8358 for (i = 0; i < ei->num_change_pages; i++)
8360 struct ElementChangeInfo *change = &ei->change_page[i];
8362 xx_current_change_page = i;
8364 xx_change = *change; // copy change data into temporary buffer
8367 setEventBitsFromEventFlags(change);
8369 for (j = 0; chunk_config_CUSX_change[j].data_type != -1; j++)
8370 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_CUSX_change[j],
8377 static int SaveLevel_GRPX(FILE *file, struct LevelInfo *level, int element)
8379 struct ElementInfo *ei = &element_info[element];
8380 struct ElementGroupInfo *group = ei->group;
8384 chunk_size += putFile16BitBE(file, element);
8386 xx_ei = *ei; // copy element data into temporary buffer
8387 xx_group = *group; // copy group data into temporary buffer
8389 // set default description string for this specific element
8390 strcpy(xx_default_description, getDefaultElementDescription(ei));
8392 for (i = 0; chunk_config_GRPX[i].data_type != -1; i++)
8393 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_GRPX[i], FALSE);
8398 static int SaveLevel_EMPX(FILE *file, struct LevelInfo *level, int element)
8400 struct ElementInfo *ei = &element_info[element];
8404 chunk_size += putFile16BitBE(file, element);
8406 xx_ei = *ei; // copy element data into temporary buffer
8408 for (i = 0; chunk_config_EMPX[i].data_type != -1; i++)
8409 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_EMPX[i], FALSE);
8414 static void SaveLevelFromFilename(struct LevelInfo *level, char *filename,
8415 boolean save_as_template)
8421 if (!(file = fopen(filename, MODE_WRITE)))
8423 Warn("cannot save level file '%s'", filename);
8428 level->file_version = FILE_VERSION_ACTUAL;
8429 level->game_version = GAME_VERSION_ACTUAL;
8431 level->creation_date = getCurrentDate();
8433 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
8434 putFileChunkBE(file, "CAVE", CHUNK_SIZE_NONE);
8436 chunk_size = SaveLevel_VERS(NULL, level);
8437 putFileChunkBE(file, "VERS", chunk_size);
8438 SaveLevel_VERS(file, level);
8440 chunk_size = SaveLevel_DATE(NULL, level);
8441 putFileChunkBE(file, "DATE", chunk_size);
8442 SaveLevel_DATE(file, level);
8444 chunk_size = SaveLevel_NAME(NULL, level);
8445 putFileChunkBE(file, "NAME", chunk_size);
8446 SaveLevel_NAME(file, level);
8448 chunk_size = SaveLevel_AUTH(NULL, level);
8449 putFileChunkBE(file, "AUTH", chunk_size);
8450 SaveLevel_AUTH(file, level);
8452 chunk_size = SaveLevel_INFO(NULL, level);
8453 putFileChunkBE(file, "INFO", chunk_size);
8454 SaveLevel_INFO(file, level);
8456 chunk_size = SaveLevel_BODY(NULL, level);
8457 putFileChunkBE(file, "BODY", chunk_size);
8458 SaveLevel_BODY(file, level);
8460 chunk_size = SaveLevel_ELEM(NULL, level);
8461 if (chunk_size > LEVEL_CHUNK_ELEM_UNCHANGED) // save if changed
8463 putFileChunkBE(file, "ELEM", chunk_size);
8464 SaveLevel_ELEM(file, level);
8467 for (i = 0; i < NUM_ENVELOPES; i++)
8469 int element = EL_ENVELOPE_1 + i;
8471 chunk_size = SaveLevel_NOTE(NULL, level, element);
8472 if (chunk_size > LEVEL_CHUNK_NOTE_UNCHANGED) // save if changed
8474 putFileChunkBE(file, "NOTE", chunk_size);
8475 SaveLevel_NOTE(file, level, element);
8479 // if not using template level, check for non-default custom/group elements
8480 if (!level->use_custom_template || save_as_template)
8482 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
8484 int element = EL_CUSTOM_START + i;
8486 chunk_size = SaveLevel_CUSX(NULL, level, element);
8487 if (chunk_size > LEVEL_CHUNK_CUSX_UNCHANGED) // save if changed
8489 putFileChunkBE(file, "CUSX", chunk_size);
8490 SaveLevel_CUSX(file, level, element);
8494 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
8496 int element = EL_GROUP_START + i;
8498 chunk_size = SaveLevel_GRPX(NULL, level, element);
8499 if (chunk_size > LEVEL_CHUNK_GRPX_UNCHANGED) // save if changed
8501 putFileChunkBE(file, "GRPX", chunk_size);
8502 SaveLevel_GRPX(file, level, element);
8506 for (i = 0; i < NUM_EMPTY_ELEMENTS_ALL; i++)
8508 int element = GET_EMPTY_ELEMENT(i);
8510 chunk_size = SaveLevel_EMPX(NULL, level, element);
8511 if (chunk_size > LEVEL_CHUNK_EMPX_UNCHANGED) // save if changed
8513 putFileChunkBE(file, "EMPX", chunk_size);
8514 SaveLevel_EMPX(file, level, element);
8521 SetFilePermissions(filename, PERMS_PRIVATE);
8524 void SaveLevel(int nr)
8526 char *filename = getDefaultLevelFilename(nr);
8528 SaveLevelFromFilename(&level, filename, FALSE);
8531 void SaveLevelTemplate(void)
8533 char *filename = getLocalLevelTemplateFilename();
8535 SaveLevelFromFilename(&level, filename, TRUE);
8538 boolean SaveLevelChecked(int nr)
8540 char *filename = getDefaultLevelFilename(nr);
8541 boolean new_level = !fileExists(filename);
8542 boolean level_saved = FALSE;
8544 if (new_level || Request("Save this level and kill the old?", REQ_ASK))
8549 Request("Level saved!", REQ_CONFIRM);
8557 void DumpLevel(struct LevelInfo *level)
8559 if (level->no_level_file || level->no_valid_file)
8561 Warn("cannot dump -- no valid level file found");
8567 Print("Level xxx (file version %08d, game version %08d)\n",
8568 level->file_version, level->game_version);
8571 Print("Level author: '%s'\n", level->author);
8572 Print("Level title: '%s'\n", level->name);
8574 Print("Playfield size: %d x %d\n", level->fieldx, level->fieldy);
8576 Print("Level time: %d seconds\n", level->time);
8577 Print("Gems needed: %d\n", level->gems_needed);
8579 Print("Time for magic wall: %d seconds\n", level->time_magic_wall);
8580 Print("Time for wheel: %d seconds\n", level->time_wheel);
8581 Print("Time for light: %d seconds\n", level->time_light);
8582 Print("Time for timegate: %d seconds\n", level->time_timegate);
8584 Print("Amoeba speed: %d\n", level->amoeba_speed);
8587 Print("EM style slippery gems: %s\n", (level->em_slippery_gems ? "yes" : "no"));
8588 Print("Player blocks last field: %s\n", (level->block_last_field ? "yes" : "no"));
8589 Print("SP player blocks last field: %s\n", (level->sp_block_last_field ? "yes" : "no"));
8590 Print("use spring bug: %s\n", (level->use_spring_bug ? "yes" : "no"));
8591 Print("use step counter: %s\n", (level->use_step_counter ? "yes" : "no"));
8592 Print("rate time over score: %s\n", (level->rate_time_over_score ? "yes" : "no"));
8598 for (i = 0; i < NUM_ENVELOPES; i++)
8600 char *text = level->envelope[i].text;
8601 int text_len = strlen(text);
8602 boolean has_text = FALSE;
8604 for (j = 0; j < text_len; j++)
8605 if (text[j] != ' ' && text[j] != '\n')
8611 Print("Envelope %d:\n'%s'\n", i + 1, text);
8619 void DumpLevels(void)
8621 static LevelDirTree *dumplevel_leveldir = NULL;
8623 dumplevel_leveldir = getTreeInfoFromIdentifier(leveldir_first,
8624 global.dumplevel_leveldir);
8626 if (dumplevel_leveldir == NULL)
8627 Fail("no such level identifier: '%s'", global.dumplevel_leveldir);
8629 if (global.dumplevel_level_nr < dumplevel_leveldir->first_level ||
8630 global.dumplevel_level_nr > dumplevel_leveldir->last_level)
8631 Fail("no such level number: %d", global.dumplevel_level_nr);
8633 leveldir_current = dumplevel_leveldir;
8635 LoadLevel(global.dumplevel_level_nr);
8642 // ============================================================================
8643 // tape file functions
8644 // ============================================================================
8646 static void setTapeInfoToDefaults(void)
8650 // always start with reliable default values (empty tape)
8653 // default values (also for pre-1.2 tapes) with only the first player
8654 tape.player_participates[0] = TRUE;
8655 for (i = 1; i < MAX_PLAYERS; i++)
8656 tape.player_participates[i] = FALSE;
8658 // at least one (default: the first) player participates in every tape
8659 tape.num_participating_players = 1;
8661 tape.property_bits = TAPE_PROPERTY_NONE;
8663 tape.level_nr = level_nr;
8665 tape.changed = FALSE;
8666 tape.solved = FALSE;
8668 tape.recording = FALSE;
8669 tape.playing = FALSE;
8670 tape.pausing = FALSE;
8672 tape.scr_fieldx = SCR_FIELDX_DEFAULT;
8673 tape.scr_fieldy = SCR_FIELDY_DEFAULT;
8675 tape.no_info_chunk = TRUE;
8676 tape.no_valid_file = FALSE;
8679 static int getTapePosSize(struct TapeInfo *tape)
8681 int tape_pos_size = 0;
8683 if (tape->use_key_actions)
8684 tape_pos_size += tape->num_participating_players;
8686 if (tape->use_mouse_actions)
8687 tape_pos_size += 3; // x and y position and mouse button mask
8689 tape_pos_size += 1; // tape action delay value
8691 return tape_pos_size;
8694 static void setTapeActionFlags(struct TapeInfo *tape, int value)
8696 tape->use_key_actions = FALSE;
8697 tape->use_mouse_actions = FALSE;
8699 if (value != TAPE_USE_MOUSE_ACTIONS_ONLY)
8700 tape->use_key_actions = TRUE;
8702 if (value != TAPE_USE_KEY_ACTIONS_ONLY)
8703 tape->use_mouse_actions = TRUE;
8706 static int getTapeActionValue(struct TapeInfo *tape)
8708 return (tape->use_key_actions &&
8709 tape->use_mouse_actions ? TAPE_USE_KEY_AND_MOUSE_ACTIONS :
8710 tape->use_key_actions ? TAPE_USE_KEY_ACTIONS_ONLY :
8711 tape->use_mouse_actions ? TAPE_USE_MOUSE_ACTIONS_ONLY :
8712 TAPE_ACTIONS_DEFAULT);
8715 static int LoadTape_VERS(File *file, int chunk_size, struct TapeInfo *tape)
8717 tape->file_version = getFileVersion(file);
8718 tape->game_version = getFileVersion(file);
8723 static int LoadTape_HEAD(File *file, int chunk_size, struct TapeInfo *tape)
8727 tape->random_seed = getFile32BitBE(file);
8728 tape->date = getFile32BitBE(file);
8729 tape->length = getFile32BitBE(file);
8731 // read header fields that are new since version 1.2
8732 if (tape->file_version >= FILE_VERSION_1_2)
8734 byte store_participating_players = getFile8Bit(file);
8737 // since version 1.2, tapes store which players participate in the tape
8738 tape->num_participating_players = 0;
8739 for (i = 0; i < MAX_PLAYERS; i++)
8741 tape->player_participates[i] = FALSE;
8743 if (store_participating_players & (1 << i))
8745 tape->player_participates[i] = TRUE;
8746 tape->num_participating_players++;
8750 setTapeActionFlags(tape, getFile8Bit(file));
8752 tape->property_bits = getFile8Bit(file);
8753 tape->solved = getFile8Bit(file);
8755 engine_version = getFileVersion(file);
8756 if (engine_version > 0)
8757 tape->engine_version = engine_version;
8759 tape->engine_version = tape->game_version;
8765 static int LoadTape_SCRN(File *file, int chunk_size, struct TapeInfo *tape)
8767 tape->scr_fieldx = getFile8Bit(file);
8768 tape->scr_fieldy = getFile8Bit(file);
8773 static int LoadTape_INFO(File *file, int chunk_size, struct TapeInfo *tape)
8775 char *level_identifier = NULL;
8776 int level_identifier_size;
8779 tape->no_info_chunk = FALSE;
8781 level_identifier_size = getFile16BitBE(file);
8783 level_identifier = checked_malloc(level_identifier_size);
8785 for (i = 0; i < level_identifier_size; i++)
8786 level_identifier[i] = getFile8Bit(file);
8788 strncpy(tape->level_identifier, level_identifier, MAX_FILENAME_LEN);
8789 tape->level_identifier[MAX_FILENAME_LEN] = '\0';
8791 checked_free(level_identifier);
8793 tape->level_nr = getFile16BitBE(file);
8795 chunk_size = 2 + level_identifier_size + 2;
8800 static int LoadTape_BODY(File *file, int chunk_size, struct TapeInfo *tape)
8803 int tape_pos_size = getTapePosSize(tape);
8804 int chunk_size_expected = tape_pos_size * tape->length;
8806 if (chunk_size_expected != chunk_size)
8808 ReadUnusedBytesFromFile(file, chunk_size);
8809 return chunk_size_expected;
8812 for (i = 0; i < tape->length; i++)
8814 if (i >= MAX_TAPE_LEN)
8816 Warn("tape truncated -- size exceeds maximum tape size %d",
8819 // tape too large; read and ignore remaining tape data from this chunk
8820 for (;i < tape->length; i++)
8821 ReadUnusedBytesFromFile(file, tape_pos_size);
8826 if (tape->use_key_actions)
8828 for (j = 0; j < MAX_PLAYERS; j++)
8830 tape->pos[i].action[j] = MV_NONE;
8832 if (tape->player_participates[j])
8833 tape->pos[i].action[j] = getFile8Bit(file);
8837 if (tape->use_mouse_actions)
8839 tape->pos[i].action[TAPE_ACTION_LX] = getFile8Bit(file);
8840 tape->pos[i].action[TAPE_ACTION_LY] = getFile8Bit(file);
8841 tape->pos[i].action[TAPE_ACTION_BUTTON] = getFile8Bit(file);
8844 tape->pos[i].delay = getFile8Bit(file);
8846 if (tape->file_version == FILE_VERSION_1_0)
8848 // eliminate possible diagonal moves in old tapes
8849 // this is only for backward compatibility
8851 byte joy_dir[4] = { JOY_LEFT, JOY_RIGHT, JOY_UP, JOY_DOWN };
8852 byte action = tape->pos[i].action[0];
8853 int k, num_moves = 0;
8855 for (k = 0; k < 4; k++)
8857 if (action & joy_dir[k])
8859 tape->pos[i + num_moves].action[0] = joy_dir[k];
8861 tape->pos[i + num_moves].delay = 0;
8870 tape->length += num_moves;
8873 else if (tape->file_version < FILE_VERSION_2_0)
8875 // convert pre-2.0 tapes to new tape format
8877 if (tape->pos[i].delay > 1)
8880 tape->pos[i + 1] = tape->pos[i];
8881 tape->pos[i + 1].delay = 1;
8884 for (j = 0; j < MAX_PLAYERS; j++)
8885 tape->pos[i].action[j] = MV_NONE;
8886 tape->pos[i].delay--;
8893 if (checkEndOfFile(file))
8897 if (i != tape->length)
8898 chunk_size = tape_pos_size * i;
8903 static void LoadTape_SokobanSolution(char *filename)
8906 int move_delay = TILESIZE / level.initial_player_stepsize[0];
8908 if (!(file = openFile(filename, MODE_READ)))
8910 tape.no_valid_file = TRUE;
8915 while (!checkEndOfFile(file))
8917 unsigned char c = getByteFromFile(file);
8919 if (checkEndOfFile(file))
8926 tape.pos[tape.length].action[0] = MV_UP;
8927 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
8933 tape.pos[tape.length].action[0] = MV_DOWN;
8934 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
8940 tape.pos[tape.length].action[0] = MV_LEFT;
8941 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
8947 tape.pos[tape.length].action[0] = MV_RIGHT;
8948 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
8956 // ignore white-space characters
8960 tape.no_valid_file = TRUE;
8962 Warn("unsupported Sokoban solution file '%s' ['%d']", filename, c);
8970 if (tape.no_valid_file)
8973 tape.length_frames = GetTapeLengthFrames();
8974 tape.length_seconds = GetTapeLengthSeconds();
8977 void LoadTapeFromFilename(char *filename)
8979 char cookie[MAX_LINE_LEN];
8980 char chunk_name[CHUNK_ID_LEN + 1];
8984 // always start with reliable default values
8985 setTapeInfoToDefaults();
8987 if (strSuffix(filename, ".sln"))
8989 LoadTape_SokobanSolution(filename);
8994 if (!(file = openFile(filename, MODE_READ)))
8996 tape.no_valid_file = TRUE;
9001 getFileChunkBE(file, chunk_name, NULL);
9002 if (strEqual(chunk_name, "RND1"))
9004 getFile32BitBE(file); // not used
9006 getFileChunkBE(file, chunk_name, NULL);
9007 if (!strEqual(chunk_name, "TAPE"))
9009 tape.no_valid_file = TRUE;
9011 Warn("unknown format of tape file '%s'", filename);
9018 else // check for pre-2.0 file format with cookie string
9020 strcpy(cookie, chunk_name);
9021 if (getStringFromFile(file, &cookie[4], MAX_LINE_LEN - 4) == NULL)
9023 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
9024 cookie[strlen(cookie) - 1] = '\0';
9026 if (!checkCookieString(cookie, TAPE_COOKIE_TMPL))
9028 tape.no_valid_file = TRUE;
9030 Warn("unknown format of tape file '%s'", filename);
9037 if ((tape.file_version = getFileVersionFromCookieString(cookie)) == -1)
9039 tape.no_valid_file = TRUE;
9041 Warn("unsupported version of tape file '%s'", filename);
9048 // pre-2.0 tape files have no game version, so use file version here
9049 tape.game_version = tape.file_version;
9052 if (tape.file_version < FILE_VERSION_1_2)
9054 // tape files from versions before 1.2.0 without chunk structure
9055 LoadTape_HEAD(file, TAPE_CHUNK_HEAD_SIZE, &tape);
9056 LoadTape_BODY(file, 2 * tape.length, &tape);
9064 int (*loader)(File *, int, struct TapeInfo *);
9068 { "VERS", TAPE_CHUNK_VERS_SIZE, LoadTape_VERS },
9069 { "HEAD", TAPE_CHUNK_HEAD_SIZE, LoadTape_HEAD },
9070 { "SCRN", TAPE_CHUNK_SCRN_SIZE, LoadTape_SCRN },
9071 { "INFO", -1, LoadTape_INFO },
9072 { "BODY", -1, LoadTape_BODY },
9076 while (getFileChunkBE(file, chunk_name, &chunk_size))
9080 while (chunk_info[i].name != NULL &&
9081 !strEqual(chunk_name, chunk_info[i].name))
9084 if (chunk_info[i].name == NULL)
9086 Warn("unknown chunk '%s' in tape file '%s'",
9087 chunk_name, filename);
9089 ReadUnusedBytesFromFile(file, chunk_size);
9091 else if (chunk_info[i].size != -1 &&
9092 chunk_info[i].size != chunk_size)
9094 Warn("wrong size (%d) of chunk '%s' in tape file '%s'",
9095 chunk_size, chunk_name, filename);
9097 ReadUnusedBytesFromFile(file, chunk_size);
9101 // call function to load this tape chunk
9102 int chunk_size_expected =
9103 (chunk_info[i].loader)(file, chunk_size, &tape);
9105 // the size of some chunks cannot be checked before reading other
9106 // chunks first (like "HEAD" and "BODY") that contain some header
9107 // information, so check them here
9108 if (chunk_size_expected != chunk_size)
9110 Warn("wrong size (%d) of chunk '%s' in tape file '%s'",
9111 chunk_size, chunk_name, filename);
9119 tape.length_frames = GetTapeLengthFrames();
9120 tape.length_seconds = GetTapeLengthSeconds();
9123 Debug("files:LoadTapeFromFilename", "tape file version: %d",
9125 Debug("files:LoadTapeFromFilename", "tape game version: %d",
9127 Debug("files:LoadTapeFromFilename", "tape engine version: %d",
9128 tape.engine_version);
9132 void LoadTape(int nr)
9134 char *filename = getTapeFilename(nr);
9136 LoadTapeFromFilename(filename);
9139 void LoadSolutionTape(int nr)
9141 char *filename = getSolutionTapeFilename(nr);
9143 LoadTapeFromFilename(filename);
9145 if (TAPE_IS_EMPTY(tape))
9147 if (level.game_engine_type == GAME_ENGINE_TYPE_BD &&
9148 level.native_bd_level->replay != NULL)
9149 CopyNativeTape_BD_to_RND(&level);
9150 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP &&
9151 level.native_sp_level->demo.is_available)
9152 CopyNativeTape_SP_to_RND(&level);
9156 void LoadScoreTape(char *score_tape_basename, int nr)
9158 char *filename = getScoreTapeFilename(score_tape_basename, nr);
9160 LoadTapeFromFilename(filename);
9163 void LoadScoreCacheTape(char *score_tape_basename, int nr)
9165 char *filename = getScoreCacheTapeFilename(score_tape_basename, nr);
9167 LoadTapeFromFilename(filename);
9170 static boolean checkSaveTape_SCRN(struct TapeInfo *tape)
9172 // chunk required for team mode tapes with non-default screen size
9173 return (tape->num_participating_players > 1 &&
9174 (tape->scr_fieldx != SCR_FIELDX_DEFAULT ||
9175 tape->scr_fieldy != SCR_FIELDY_DEFAULT));
9178 static void SaveTape_VERS(FILE *file, struct TapeInfo *tape)
9180 putFileVersion(file, tape->file_version);
9181 putFileVersion(file, tape->game_version);
9184 static void SaveTape_HEAD(FILE *file, struct TapeInfo *tape)
9187 byte store_participating_players = 0;
9189 // set bits for participating players for compact storage
9190 for (i = 0; i < MAX_PLAYERS; i++)
9191 if (tape->player_participates[i])
9192 store_participating_players |= (1 << i);
9194 putFile32BitBE(file, tape->random_seed);
9195 putFile32BitBE(file, tape->date);
9196 putFile32BitBE(file, tape->length);
9198 putFile8Bit(file, store_participating_players);
9200 putFile8Bit(file, getTapeActionValue(tape));
9202 putFile8Bit(file, tape->property_bits);
9203 putFile8Bit(file, tape->solved);
9205 putFileVersion(file, tape->engine_version);
9208 static void SaveTape_SCRN(FILE *file, struct TapeInfo *tape)
9210 putFile8Bit(file, tape->scr_fieldx);
9211 putFile8Bit(file, tape->scr_fieldy);
9214 static void SaveTape_INFO(FILE *file, struct TapeInfo *tape)
9216 int level_identifier_size = strlen(tape->level_identifier) + 1;
9219 putFile16BitBE(file, level_identifier_size);
9221 for (i = 0; i < level_identifier_size; i++)
9222 putFile8Bit(file, tape->level_identifier[i]);
9224 putFile16BitBE(file, tape->level_nr);
9227 static void SaveTape_BODY(FILE *file, struct TapeInfo *tape)
9231 for (i = 0; i < tape->length; i++)
9233 if (tape->use_key_actions)
9235 for (j = 0; j < MAX_PLAYERS; j++)
9236 if (tape->player_participates[j])
9237 putFile8Bit(file, tape->pos[i].action[j]);
9240 if (tape->use_mouse_actions)
9242 putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_LX]);
9243 putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_LY]);
9244 putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_BUTTON]);
9247 putFile8Bit(file, tape->pos[i].delay);
9251 void SaveTapeToFilename(char *filename)
9255 int info_chunk_size;
9256 int body_chunk_size;
9258 if (!(file = fopen(filename, MODE_WRITE)))
9260 Warn("cannot save level recording file '%s'", filename);
9265 tape_pos_size = getTapePosSize(&tape);
9267 info_chunk_size = 2 + (strlen(tape.level_identifier) + 1) + 2;
9268 body_chunk_size = tape_pos_size * tape.length;
9270 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
9271 putFileChunkBE(file, "TAPE", CHUNK_SIZE_NONE);
9273 putFileChunkBE(file, "VERS", TAPE_CHUNK_VERS_SIZE);
9274 SaveTape_VERS(file, &tape);
9276 putFileChunkBE(file, "HEAD", TAPE_CHUNK_HEAD_SIZE);
9277 SaveTape_HEAD(file, &tape);
9279 if (checkSaveTape_SCRN(&tape))
9281 putFileChunkBE(file, "SCRN", TAPE_CHUNK_SCRN_SIZE);
9282 SaveTape_SCRN(file, &tape);
9285 putFileChunkBE(file, "INFO", info_chunk_size);
9286 SaveTape_INFO(file, &tape);
9288 putFileChunkBE(file, "BODY", body_chunk_size);
9289 SaveTape_BODY(file, &tape);
9293 SetFilePermissions(filename, PERMS_PRIVATE);
9296 static void SaveTapeExt(char *filename)
9300 tape.file_version = FILE_VERSION_ACTUAL;
9301 tape.game_version = GAME_VERSION_ACTUAL;
9303 tape.num_participating_players = 0;
9305 // count number of participating players
9306 for (i = 0; i < MAX_PLAYERS; i++)
9307 if (tape.player_participates[i])
9308 tape.num_participating_players++;
9310 SaveTapeToFilename(filename);
9312 tape.changed = FALSE;
9315 void SaveTape(int nr)
9317 char *filename = getTapeFilename(nr);
9319 InitTapeDirectory(leveldir_current->subdir);
9321 SaveTapeExt(filename);
9324 void SaveScoreTape(int nr)
9326 char *filename = getScoreTapeFilename(tape.score_tape_basename, nr);
9328 // used instead of "leveldir_current->subdir" (for network games)
9329 InitScoreTapeDirectory(levelset.identifier, nr);
9331 SaveTapeExt(filename);
9334 static boolean SaveTapeCheckedExt(int nr, char *msg_replace, char *msg_saved,
9335 unsigned int req_state_added)
9337 char *filename = getTapeFilename(nr);
9338 boolean new_tape = !fileExists(filename);
9339 boolean tape_saved = FALSE;
9341 if (new_tape || Request(msg_replace, REQ_ASK | req_state_added))
9346 Request(msg_saved, REQ_CONFIRM | req_state_added);
9354 boolean SaveTapeChecked(int nr)
9356 return SaveTapeCheckedExt(nr, "Replace old tape?", "Tape saved!", 0);
9359 boolean SaveTapeChecked_LevelSolved(int nr)
9361 return SaveTapeCheckedExt(nr, "Level solved! Replace old tape?",
9362 "Level solved! Tape saved!", REQ_STAY_OPEN);
9365 void DumpTape(struct TapeInfo *tape)
9367 int tape_frame_counter;
9370 if (tape->no_valid_file)
9372 Warn("cannot dump -- no valid tape file found");
9379 Print("Tape of Level %03d (file version %08d, game version %08d)\n",
9380 tape->level_nr, tape->file_version, tape->game_version);
9381 Print(" (effective engine version %08d)\n",
9382 tape->engine_version);
9383 Print("Level series identifier: '%s'\n", tape->level_identifier);
9385 Print("Solution tape: %s\n",
9386 tape->solved ? "yes" :
9387 tape->game_version < VERSION_IDENT(4,3,2,3) ? "unknown" : "no");
9389 Print("Special tape properties: ");
9390 if (tape->property_bits == TAPE_PROPERTY_NONE)
9392 if (tape->property_bits & TAPE_PROPERTY_EM_RANDOM_BUG)
9393 Print("[em_random_bug]");
9394 if (tape->property_bits & TAPE_PROPERTY_GAME_SPEED)
9395 Print("[game_speed]");
9396 if (tape->property_bits & TAPE_PROPERTY_PAUSE_MODE)
9398 if (tape->property_bits & TAPE_PROPERTY_SINGLE_STEP)
9399 Print("[single_step]");
9400 if (tape->property_bits & TAPE_PROPERTY_SNAPSHOT)
9401 Print("[snapshot]");
9402 if (tape->property_bits & TAPE_PROPERTY_REPLAYED)
9403 Print("[replayed]");
9404 if (tape->property_bits & TAPE_PROPERTY_TAS_KEYS)
9405 Print("[tas_keys]");
9406 if (tape->property_bits & TAPE_PROPERTY_SMALL_GRAPHICS)
9407 Print("[small_graphics]");
9410 int year2 = tape->date / 10000;
9411 int year4 = (year2 < 70 ? 2000 + year2 : 1900 + year2);
9412 int month_index_raw = (tape->date / 100) % 100;
9413 int month_index = month_index_raw % 12; // prevent invalid index
9414 int month = month_index + 1;
9415 int day = tape->date % 100;
9417 Print("Tape date: %04d-%02d-%02d\n", year4, month, day);
9421 tape_frame_counter = 0;
9423 for (i = 0; i < tape->length; i++)
9425 if (i >= MAX_TAPE_LEN)
9430 for (j = 0; j < MAX_PLAYERS; j++)
9432 if (tape->player_participates[j])
9434 int action = tape->pos[i].action[j];
9436 Print("%d:%02x ", j, action);
9437 Print("[%c%c%c%c|%c%c] - ",
9438 (action & JOY_LEFT ? '<' : ' '),
9439 (action & JOY_RIGHT ? '>' : ' '),
9440 (action & JOY_UP ? '^' : ' '),
9441 (action & JOY_DOWN ? 'v' : ' '),
9442 (action & JOY_BUTTON_1 ? '1' : ' '),
9443 (action & JOY_BUTTON_2 ? '2' : ' '));
9447 Print("(%03d) ", tape->pos[i].delay);
9448 Print("[%05d]\n", tape_frame_counter);
9450 tape_frame_counter += tape->pos[i].delay;
9456 void DumpTapes(void)
9458 static LevelDirTree *dumptape_leveldir = NULL;
9460 dumptape_leveldir = getTreeInfoFromIdentifier(leveldir_first,
9461 global.dumptape_leveldir);
9463 if (dumptape_leveldir == NULL)
9464 Fail("no such level identifier: '%s'", global.dumptape_leveldir);
9466 if (global.dumptape_level_nr < dumptape_leveldir->first_level ||
9467 global.dumptape_level_nr > dumptape_leveldir->last_level)
9468 Fail("no such level number: %d", global.dumptape_level_nr);
9470 leveldir_current = dumptape_leveldir;
9472 if (options.mytapes)
9473 LoadTape(global.dumptape_level_nr);
9475 LoadSolutionTape(global.dumptape_level_nr);
9483 // ============================================================================
9484 // score file functions
9485 // ============================================================================
9487 static void setScoreInfoToDefaultsExt(struct ScoreInfo *scores)
9491 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9493 strcpy(scores->entry[i].tape_basename, UNDEFINED_FILENAME);
9494 strcpy(scores->entry[i].name, EMPTY_PLAYER_NAME);
9495 scores->entry[i].score = 0;
9496 scores->entry[i].time = 0;
9498 scores->entry[i].id = -1;
9499 strcpy(scores->entry[i].tape_date, UNKNOWN_NAME);
9500 strcpy(scores->entry[i].platform, UNKNOWN_NAME);
9501 strcpy(scores->entry[i].version, UNKNOWN_NAME);
9502 strcpy(scores->entry[i].country_name, UNKNOWN_NAME);
9503 strcpy(scores->entry[i].country_code, "??");
9506 scores->num_entries = 0;
9507 scores->last_added = -1;
9508 scores->last_added_local = -1;
9510 scores->updated = FALSE;
9511 scores->uploaded = FALSE;
9512 scores->tape_downloaded = FALSE;
9513 scores->force_last_added = FALSE;
9515 // The following values are intentionally not reset here:
9519 // - continue_playing
9520 // - continue_on_return
9523 static void setScoreInfoToDefaults(void)
9525 setScoreInfoToDefaultsExt(&scores);
9528 static void setServerScoreInfoToDefaults(void)
9530 setScoreInfoToDefaultsExt(&server_scores);
9533 static void LoadScore_OLD(int nr)
9536 char *filename = getScoreFilename(nr);
9537 char cookie[MAX_LINE_LEN];
9538 char line[MAX_LINE_LEN];
9542 if (!(file = fopen(filename, MODE_READ)))
9545 // check file identifier
9546 if (fgets(cookie, MAX_LINE_LEN, file) == NULL)
9548 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
9549 cookie[strlen(cookie) - 1] = '\0';
9551 if (!checkCookieString(cookie, SCORE_COOKIE_TMPL))
9553 Warn("unknown format of score file '%s'", filename);
9560 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9562 if (fscanf(file, "%d", &scores.entry[i].score) == EOF)
9563 Warn("fscanf() failed; %s", strerror(errno));
9565 if (fgets(line, MAX_LINE_LEN, file) == NULL)
9568 if (strlen(line) > 0 && line[strlen(line) - 1] == '\n')
9569 line[strlen(line) - 1] = '\0';
9571 for (line_ptr = line; *line_ptr; line_ptr++)
9573 if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != '\0')
9575 strncpy(scores.entry[i].name, line_ptr, MAX_PLAYER_NAME_LEN);
9576 scores.entry[i].name[MAX_PLAYER_NAME_LEN] = '\0';
9585 static void ConvertScore_OLD(void)
9587 // only convert score to time for levels that rate playing time over score
9588 if (!level.rate_time_over_score)
9591 // convert old score to playing time for score-less levels (like Supaplex)
9592 int time_final_max = 999;
9595 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9597 int score = scores.entry[i].score;
9599 if (score > 0 && score < time_final_max)
9600 scores.entry[i].time = (time_final_max - score - 1) * FRAMES_PER_SECOND;
9604 static int LoadScore_VERS(File *file, int chunk_size, struct ScoreInfo *scores)
9606 scores->file_version = getFileVersion(file);
9607 scores->game_version = getFileVersion(file);
9612 static int LoadScore_INFO(File *file, int chunk_size, struct ScoreInfo *scores)
9614 char *level_identifier = NULL;
9615 int level_identifier_size;
9618 level_identifier_size = getFile16BitBE(file);
9620 level_identifier = checked_malloc(level_identifier_size);
9622 for (i = 0; i < level_identifier_size; i++)
9623 level_identifier[i] = getFile8Bit(file);
9625 strncpy(scores->level_identifier, level_identifier, MAX_FILENAME_LEN);
9626 scores->level_identifier[MAX_FILENAME_LEN] = '\0';
9628 checked_free(level_identifier);
9630 scores->level_nr = getFile16BitBE(file);
9631 scores->num_entries = getFile16BitBE(file);
9633 chunk_size = 2 + level_identifier_size + 2 + 2;
9638 static int LoadScore_NAME(File *file, int chunk_size, struct ScoreInfo *scores)
9642 for (i = 0; i < scores->num_entries; i++)
9644 for (j = 0; j < MAX_PLAYER_NAME_LEN; j++)
9645 scores->entry[i].name[j] = getFile8Bit(file);
9647 scores->entry[i].name[MAX_PLAYER_NAME_LEN] = '\0';
9650 chunk_size = scores->num_entries * MAX_PLAYER_NAME_LEN;
9655 static int LoadScore_SCOR(File *file, int chunk_size, struct ScoreInfo *scores)
9659 for (i = 0; i < scores->num_entries; i++)
9660 scores->entry[i].score = getFile16BitBE(file);
9662 chunk_size = scores->num_entries * 2;
9667 static int LoadScore_SC4R(File *file, int chunk_size, struct ScoreInfo *scores)
9671 for (i = 0; i < scores->num_entries; i++)
9672 scores->entry[i].score = getFile32BitBE(file);
9674 chunk_size = scores->num_entries * 4;
9679 static int LoadScore_TIME(File *file, int chunk_size, struct ScoreInfo *scores)
9683 for (i = 0; i < scores->num_entries; i++)
9684 scores->entry[i].time = getFile32BitBE(file);
9686 chunk_size = scores->num_entries * 4;
9691 static int LoadScore_TAPE(File *file, int chunk_size, struct ScoreInfo *scores)
9695 for (i = 0; i < scores->num_entries; i++)
9697 for (j = 0; j < MAX_SCORE_TAPE_BASENAME_LEN; j++)
9698 scores->entry[i].tape_basename[j] = getFile8Bit(file);
9700 scores->entry[i].tape_basename[MAX_SCORE_TAPE_BASENAME_LEN] = '\0';
9703 chunk_size = scores->num_entries * MAX_SCORE_TAPE_BASENAME_LEN;
9708 void LoadScore(int nr)
9710 char *filename = getScoreFilename(nr);
9711 char cookie[MAX_LINE_LEN];
9712 char chunk_name[CHUNK_ID_LEN + 1];
9714 boolean old_score_file_format = FALSE;
9717 // always start with reliable default values
9718 setScoreInfoToDefaults();
9720 if (!(file = openFile(filename, MODE_READ)))
9723 getFileChunkBE(file, chunk_name, NULL);
9724 if (strEqual(chunk_name, "RND1"))
9726 getFile32BitBE(file); // not used
9728 getFileChunkBE(file, chunk_name, NULL);
9729 if (!strEqual(chunk_name, "SCOR"))
9731 Warn("unknown format of score file '%s'", filename);
9738 else // check for old file format with cookie string
9740 strcpy(cookie, chunk_name);
9741 if (getStringFromFile(file, &cookie[4], MAX_LINE_LEN - 4) == NULL)
9743 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
9744 cookie[strlen(cookie) - 1] = '\0';
9746 if (!checkCookieString(cookie, SCORE_COOKIE_TMPL))
9748 Warn("unknown format of score file '%s'", filename);
9755 old_score_file_format = TRUE;
9758 if (old_score_file_format)
9760 // score files from versions before 4.2.4.0 without chunk structure
9763 // convert score to time, if possible (mainly for Supaplex levels)
9772 int (*loader)(File *, int, struct ScoreInfo *);
9776 { "VERS", SCORE_CHUNK_VERS_SIZE, LoadScore_VERS },
9777 { "INFO", -1, LoadScore_INFO },
9778 { "NAME", -1, LoadScore_NAME },
9779 { "SCOR", -1, LoadScore_SCOR },
9780 { "SC4R", -1, LoadScore_SC4R },
9781 { "TIME", -1, LoadScore_TIME },
9782 { "TAPE", -1, LoadScore_TAPE },
9787 while (getFileChunkBE(file, chunk_name, &chunk_size))
9791 while (chunk_info[i].name != NULL &&
9792 !strEqual(chunk_name, chunk_info[i].name))
9795 if (chunk_info[i].name == NULL)
9797 Warn("unknown chunk '%s' in score file '%s'",
9798 chunk_name, filename);
9800 ReadUnusedBytesFromFile(file, chunk_size);
9802 else if (chunk_info[i].size != -1 &&
9803 chunk_info[i].size != chunk_size)
9805 Warn("wrong size (%d) of chunk '%s' in score file '%s'",
9806 chunk_size, chunk_name, filename);
9808 ReadUnusedBytesFromFile(file, chunk_size);
9812 // call function to load this score chunk
9813 int chunk_size_expected =
9814 (chunk_info[i].loader)(file, chunk_size, &scores);
9816 // the size of some chunks cannot be checked before reading other
9817 // chunks first (like "HEAD" and "BODY") that contain some header
9818 // information, so check them here
9819 if (chunk_size_expected != chunk_size)
9821 Warn("wrong size (%d) of chunk '%s' in score file '%s'",
9822 chunk_size, chunk_name, filename);
9831 #if ENABLE_HISTORIC_CHUNKS
9832 void SaveScore_OLD(int nr)
9835 char *filename = getScoreFilename(nr);
9838 // used instead of "leveldir_current->subdir" (for network games)
9839 InitScoreDirectory(levelset.identifier);
9841 if (!(file = fopen(filename, MODE_WRITE)))
9843 Warn("cannot save score for level %d", nr);
9848 fprintf(file, "%s\n\n", SCORE_COOKIE);
9850 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9851 fprintf(file, "%d %s\n", scores.entry[i].score, scores.entry[i].name);
9855 SetFilePermissions(filename, PERMS_PRIVATE);
9859 static void SaveScore_VERS(FILE *file, struct ScoreInfo *scores)
9861 putFileVersion(file, scores->file_version);
9862 putFileVersion(file, scores->game_version);
9865 static void SaveScore_INFO(FILE *file, struct ScoreInfo *scores)
9867 int level_identifier_size = strlen(scores->level_identifier) + 1;
9870 putFile16BitBE(file, level_identifier_size);
9872 for (i = 0; i < level_identifier_size; i++)
9873 putFile8Bit(file, scores->level_identifier[i]);
9875 putFile16BitBE(file, scores->level_nr);
9876 putFile16BitBE(file, scores->num_entries);
9879 static void SaveScore_NAME(FILE *file, struct ScoreInfo *scores)
9883 for (i = 0; i < scores->num_entries; i++)
9885 int name_size = strlen(scores->entry[i].name);
9887 for (j = 0; j < MAX_PLAYER_NAME_LEN; j++)
9888 putFile8Bit(file, (j < name_size ? scores->entry[i].name[j] : 0));
9892 static void SaveScore_SCOR(FILE *file, struct ScoreInfo *scores)
9896 for (i = 0; i < scores->num_entries; i++)
9897 putFile16BitBE(file, scores->entry[i].score);
9900 static void SaveScore_SC4R(FILE *file, struct ScoreInfo *scores)
9904 for (i = 0; i < scores->num_entries; i++)
9905 putFile32BitBE(file, scores->entry[i].score);
9908 static void SaveScore_TIME(FILE *file, struct ScoreInfo *scores)
9912 for (i = 0; i < scores->num_entries; i++)
9913 putFile32BitBE(file, scores->entry[i].time);
9916 static void SaveScore_TAPE(FILE *file, struct ScoreInfo *scores)
9920 for (i = 0; i < scores->num_entries; i++)
9922 int size = strlen(scores->entry[i].tape_basename);
9924 for (j = 0; j < MAX_SCORE_TAPE_BASENAME_LEN; j++)
9925 putFile8Bit(file, (j < size ? scores->entry[i].tape_basename[j] : 0));
9929 static void SaveScoreToFilename(char *filename)
9932 int info_chunk_size;
9933 int name_chunk_size;
9934 int scor_chunk_size;
9935 int sc4r_chunk_size;
9936 int time_chunk_size;
9937 int tape_chunk_size;
9938 boolean has_large_score_values;
9941 if (!(file = fopen(filename, MODE_WRITE)))
9943 Warn("cannot save score file '%s'", filename);
9948 info_chunk_size = 2 + (strlen(scores.level_identifier) + 1) + 2 + 2;
9949 name_chunk_size = scores.num_entries * MAX_PLAYER_NAME_LEN;
9950 scor_chunk_size = scores.num_entries * 2;
9951 sc4r_chunk_size = scores.num_entries * 4;
9952 time_chunk_size = scores.num_entries * 4;
9953 tape_chunk_size = scores.num_entries * MAX_SCORE_TAPE_BASENAME_LEN;
9955 has_large_score_values = FALSE;
9956 for (i = 0; i < scores.num_entries; i++)
9957 if (scores.entry[i].score > 0xffff)
9958 has_large_score_values = TRUE;
9960 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
9961 putFileChunkBE(file, "SCOR", CHUNK_SIZE_NONE);
9963 putFileChunkBE(file, "VERS", SCORE_CHUNK_VERS_SIZE);
9964 SaveScore_VERS(file, &scores);
9966 putFileChunkBE(file, "INFO", info_chunk_size);
9967 SaveScore_INFO(file, &scores);
9969 putFileChunkBE(file, "NAME", name_chunk_size);
9970 SaveScore_NAME(file, &scores);
9972 if (has_large_score_values)
9974 putFileChunkBE(file, "SC4R", sc4r_chunk_size);
9975 SaveScore_SC4R(file, &scores);
9979 putFileChunkBE(file, "SCOR", scor_chunk_size);
9980 SaveScore_SCOR(file, &scores);
9983 putFileChunkBE(file, "TIME", time_chunk_size);
9984 SaveScore_TIME(file, &scores);
9986 putFileChunkBE(file, "TAPE", tape_chunk_size);
9987 SaveScore_TAPE(file, &scores);
9991 SetFilePermissions(filename, PERMS_PRIVATE);
9994 void SaveScore(int nr)
9996 char *filename = getScoreFilename(nr);
9999 // used instead of "leveldir_current->subdir" (for network games)
10000 InitScoreDirectory(levelset.identifier);
10002 scores.file_version = FILE_VERSION_ACTUAL;
10003 scores.game_version = GAME_VERSION_ACTUAL;
10005 strncpy(scores.level_identifier, levelset.identifier, MAX_FILENAME_LEN);
10006 scores.level_identifier[MAX_FILENAME_LEN] = '\0';
10007 scores.level_nr = level_nr;
10009 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
10010 if (scores.entry[i].score == 0 &&
10011 scores.entry[i].time == 0 &&
10012 strEqual(scores.entry[i].name, EMPTY_PLAYER_NAME))
10015 scores.num_entries = i;
10017 if (scores.num_entries == 0)
10020 SaveScoreToFilename(filename);
10023 static void LoadServerScoreFromCache(int nr)
10025 struct ScoreEntry score_entry;
10034 { &score_entry.score, FALSE, 0 },
10035 { &score_entry.time, FALSE, 0 },
10036 { score_entry.name, TRUE, MAX_PLAYER_NAME_LEN },
10037 { score_entry.tape_basename, TRUE, MAX_FILENAME_LEN },
10038 { score_entry.tape_date, TRUE, MAX_ISO_DATE_LEN },
10039 { &score_entry.id, FALSE, 0 },
10040 { score_entry.platform, TRUE, MAX_PLATFORM_TEXT_LEN },
10041 { score_entry.version, TRUE, MAX_VERSION_TEXT_LEN },
10042 { score_entry.country_code, TRUE, MAX_COUNTRY_CODE_LEN },
10043 { score_entry.country_name, TRUE, MAX_COUNTRY_NAME_LEN },
10047 char *filename = getScoreCacheFilename(nr);
10048 SetupFileHash *score_hash = loadSetupFileHash(filename);
10051 server_scores.num_entries = 0;
10053 if (score_hash == NULL)
10056 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
10058 score_entry = server_scores.entry[i];
10060 for (j = 0; score_mapping[j].value != NULL; j++)
10064 sprintf(token, "%02d.%d", i, j);
10066 char *value = getHashEntry(score_hash, token);
10071 if (score_mapping[j].is_string)
10073 char *score_value = (char *)score_mapping[j].value;
10074 int value_size = score_mapping[j].string_size;
10076 strncpy(score_value, value, value_size);
10077 score_value[value_size] = '\0';
10081 int *score_value = (int *)score_mapping[j].value;
10083 *score_value = atoi(value);
10086 server_scores.num_entries = i + 1;
10089 server_scores.entry[i] = score_entry;
10092 freeSetupFileHash(score_hash);
10095 void LoadServerScore(int nr, boolean download_score)
10097 if (!setup.use_api_server)
10100 // always start with reliable default values
10101 setServerScoreInfoToDefaults();
10103 // 1st step: load server scores from cache file (which may not exist)
10104 // (this should prevent reading it while the thread is writing to it)
10105 LoadServerScoreFromCache(nr);
10107 if (download_score && runtime.use_api_server)
10109 // 2nd step: download server scores from score server to cache file
10110 // (as thread, as it might time out if the server is not reachable)
10111 ApiGetScoreAsThread(nr);
10115 void PrepareScoreTapesForUpload(char *leveldir_subdir)
10117 MarkTapeDirectoryUploadsAsIncomplete(leveldir_subdir);
10119 // if score tape not uploaded, ask for uploading missing tapes later
10120 if (!setup.has_remaining_tapes)
10121 setup.ask_for_remaining_tapes = TRUE;
10123 setup.provide_uploading_tapes = TRUE;
10124 setup.has_remaining_tapes = TRUE;
10126 SaveSetup_ServerSetup();
10129 void SaveServerScore(int nr, boolean tape_saved)
10131 if (!runtime.use_api_server)
10133 PrepareScoreTapesForUpload(leveldir_current->subdir);
10138 ApiAddScoreAsThread(nr, tape_saved, NULL);
10141 void SaveServerScoreFromFile(int nr, boolean tape_saved,
10142 char *score_tape_filename)
10144 if (!runtime.use_api_server)
10147 ApiAddScoreAsThread(nr, tape_saved, score_tape_filename);
10150 void LoadLocalAndServerScore(int nr, boolean download_score)
10152 int last_added_local = scores.last_added_local;
10153 boolean force_last_added = scores.force_last_added;
10155 // needed if only showing server scores
10156 setScoreInfoToDefaults();
10158 if (!strEqual(setup.scores_in_highscore_list, STR_SCORES_TYPE_SERVER_ONLY))
10161 // restore last added local score entry (before merging server scores)
10162 scores.last_added = scores.last_added_local = last_added_local;
10164 if (setup.use_api_server &&
10165 !strEqual(setup.scores_in_highscore_list, STR_SCORES_TYPE_LOCAL_ONLY))
10167 // load server scores from cache file and trigger update from server
10168 LoadServerScore(nr, download_score);
10170 // merge local scores with scores from server
10171 MergeServerScore();
10174 if (force_last_added)
10175 scores.force_last_added = force_last_added;
10179 // ============================================================================
10180 // setup file functions
10181 // ============================================================================
10183 #define TOKEN_STR_PLAYER_PREFIX "player_"
10186 static struct TokenInfo global_setup_tokens[] =
10190 &setup.player_name, "player_name"
10194 &setup.multiple_users, "multiple_users"
10198 &setup.sound, "sound"
10202 &setup.sound_loops, "repeating_sound_loops"
10206 &setup.sound_music, "background_music"
10210 &setup.sound_simple, "simple_sound_effects"
10214 &setup.toons, "toons"
10218 &setup.global_animations, "global_animations"
10222 &setup.scroll_delay, "scroll_delay"
10226 &setup.forced_scroll_delay, "forced_scroll_delay"
10230 &setup.scroll_delay_value, "scroll_delay_value"
10234 &setup.engine_snapshot_mode, "engine_snapshot_mode"
10238 &setup.engine_snapshot_memory, "engine_snapshot_memory"
10242 &setup.fade_screens, "fade_screens"
10246 &setup.autorecord, "automatic_tape_recording"
10250 &setup.autorecord_after_replay, "autorecord_after_replay"
10254 &setup.auto_pause_on_start, "auto_pause_on_start"
10258 &setup.show_titlescreen, "show_titlescreen"
10262 &setup.quick_doors, "quick_doors"
10266 &setup.team_mode, "team_mode"
10270 &setup.handicap, "handicap"
10274 &setup.skip_levels, "skip_levels"
10278 &setup.increment_levels, "increment_levels"
10282 &setup.auto_play_next_level, "auto_play_next_level"
10286 &setup.count_score_after_game, "count_score_after_game"
10290 &setup.show_scores_after_game, "show_scores_after_game"
10294 &setup.time_limit, "time_limit"
10298 &setup.fullscreen, "fullscreen"
10302 &setup.window_scaling_percent, "window_scaling_percent"
10306 &setup.window_scaling_quality, "window_scaling_quality"
10310 &setup.screen_rendering_mode, "screen_rendering_mode"
10314 &setup.vsync_mode, "vsync_mode"
10318 &setup.ask_on_escape, "ask_on_escape"
10322 &setup.ask_on_escape_editor, "ask_on_escape_editor"
10326 &setup.ask_on_game_over, "ask_on_game_over"
10330 &setup.ask_on_quit_game, "ask_on_quit_game"
10334 &setup.ask_on_quit_program, "ask_on_quit_program"
10338 &setup.quick_switch, "quick_player_switch"
10342 &setup.input_on_focus, "input_on_focus"
10346 &setup.prefer_aga_graphics, "prefer_aga_graphics"
10350 &setup.prefer_lowpass_sounds, "prefer_lowpass_sounds"
10354 &setup.prefer_extra_panel_items, "prefer_extra_panel_items"
10358 &setup.game_speed_extended, "game_speed_extended"
10362 &setup.game_frame_delay, "game_frame_delay"
10366 &setup.bd_skip_uncovering, "bd_skip_uncovering"
10370 &setup.bd_skip_hatching, "bd_skip_hatching"
10374 &setup.bd_scroll_delay, "bd_scroll_delay"
10378 &setup.bd_smooth_movements, "bd_smooth_movements"
10382 &setup.sp_show_border_elements, "sp_show_border_elements"
10386 &setup.small_game_graphics, "small_game_graphics"
10390 &setup.show_load_save_buttons, "show_load_save_buttons"
10394 &setup.show_undo_redo_buttons, "show_undo_redo_buttons"
10398 &setup.scores_in_highscore_list, "scores_in_highscore_list"
10402 &setup.graphics_set, "graphics_set"
10406 &setup.sounds_set, "sounds_set"
10410 &setup.music_set, "music_set"
10414 &setup.override_level_graphics, "override_level_graphics"
10418 &setup.override_level_sounds, "override_level_sounds"
10422 &setup.override_level_music, "override_level_music"
10426 &setup.volume_simple, "volume_simple"
10430 &setup.volume_loops, "volume_loops"
10434 &setup.volume_music, "volume_music"
10438 &setup.network_mode, "network_mode"
10442 &setup.network_player_nr, "network_player"
10446 &setup.network_server_hostname, "network_server_hostname"
10450 &setup.touch.control_type, "touch.control_type"
10454 &setup.touch.move_distance, "touch.move_distance"
10458 &setup.touch.drop_distance, "touch.drop_distance"
10462 &setup.touch.transparency, "touch.transparency"
10466 &setup.touch.draw_outlined, "touch.draw_outlined"
10470 &setup.touch.draw_pressed, "touch.draw_pressed"
10474 &setup.touch.grid_xsize[0], "touch.virtual_buttons.0.xsize"
10478 &setup.touch.grid_ysize[0], "touch.virtual_buttons.0.ysize"
10482 &setup.touch.grid_xsize[1], "touch.virtual_buttons.1.xsize"
10486 &setup.touch.grid_ysize[1], "touch.virtual_buttons.1.ysize"
10490 &setup.touch.overlay_buttons, "touch.overlay_buttons"
10494 static struct TokenInfo auto_setup_tokens[] =
10498 &setup.auto_setup.editor_zoom_tilesize, "editor.zoom_tilesize"
10502 static struct TokenInfo server_setup_tokens[] =
10506 &setup.player_uuid, "player_uuid"
10510 &setup.player_version, "player_version"
10514 &setup.use_api_server, TEST_PREFIX "use_api_server"
10518 &setup.api_server_hostname, TEST_PREFIX "api_server_hostname"
10522 &setup.api_server_password, TEST_PREFIX "api_server_password"
10526 &setup.ask_for_uploading_tapes, TEST_PREFIX "ask_for_uploading_tapes"
10530 &setup.ask_for_remaining_tapes, TEST_PREFIX "ask_for_remaining_tapes"
10534 &setup.provide_uploading_tapes, TEST_PREFIX "provide_uploading_tapes"
10538 &setup.ask_for_using_api_server,TEST_PREFIX "ask_for_using_api_server"
10542 &setup.has_remaining_tapes, TEST_PREFIX "has_remaining_tapes"
10546 static struct TokenInfo editor_setup_tokens[] =
10550 &setup.editor.el_classic, "editor.el_classic"
10554 &setup.editor.el_custom, "editor.el_custom"
10558 &setup.editor.el_user_defined, "editor.el_user_defined"
10562 &setup.editor.el_dynamic, "editor.el_dynamic"
10566 &setup.editor.el_headlines, "editor.el_headlines"
10570 &setup.editor.show_element_token, "editor.show_element_token"
10574 &setup.editor.show_read_only_warning, "editor.show_read_only_warning"
10578 static struct TokenInfo editor_cascade_setup_tokens[] =
10582 &setup.editor_cascade.el_bd, "editor.cascade.el_bd"
10586 &setup.editor_cascade.el_bd_native, "editor.cascade.el_bd_native"
10590 &setup.editor_cascade.el_bd_effects, "editor.cascade.el_bd_effects"
10594 &setup.editor_cascade.el_em, "editor.cascade.el_em"
10598 &setup.editor_cascade.el_emc, "editor.cascade.el_emc"
10602 &setup.editor_cascade.el_rnd, "editor.cascade.el_rnd"
10606 &setup.editor_cascade.el_sb, "editor.cascade.el_sb"
10610 &setup.editor_cascade.el_sp, "editor.cascade.el_sp"
10614 &setup.editor_cascade.el_dc, "editor.cascade.el_dc"
10618 &setup.editor_cascade.el_dx, "editor.cascade.el_dx"
10622 &setup.editor_cascade.el_mm, "editor.cascade.el_mm"
10626 &setup.editor_cascade.el_df, "editor.cascade.el_df"
10630 &setup.editor_cascade.el_chars, "editor.cascade.el_chars"
10634 &setup.editor_cascade.el_steel_chars, "editor.cascade.el_steel_chars"
10638 &setup.editor_cascade.el_ce, "editor.cascade.el_ce"
10642 &setup.editor_cascade.el_ge, "editor.cascade.el_ge"
10646 &setup.editor_cascade.el_es, "editor.cascade.el_es"
10650 &setup.editor_cascade.el_ref, "editor.cascade.el_ref"
10654 &setup.editor_cascade.el_user, "editor.cascade.el_user"
10658 &setup.editor_cascade.el_dynamic, "editor.cascade.el_dynamic"
10662 static struct TokenInfo shortcut_setup_tokens[] =
10666 &setup.shortcut.save_game, "shortcut.save_game"
10670 &setup.shortcut.load_game, "shortcut.load_game"
10674 &setup.shortcut.restart_game, "shortcut.restart_game"
10678 &setup.shortcut.pause_before_end, "shortcut.pause_before_end"
10682 &setup.shortcut.toggle_pause, "shortcut.toggle_pause"
10686 &setup.shortcut.focus_player[0], "shortcut.focus_player_1"
10690 &setup.shortcut.focus_player[1], "shortcut.focus_player_2"
10694 &setup.shortcut.focus_player[2], "shortcut.focus_player_3"
10698 &setup.shortcut.focus_player[3], "shortcut.focus_player_4"
10702 &setup.shortcut.focus_player_all, "shortcut.focus_player_all"
10706 &setup.shortcut.tape_eject, "shortcut.tape_eject"
10710 &setup.shortcut.tape_extra, "shortcut.tape_extra"
10714 &setup.shortcut.tape_stop, "shortcut.tape_stop"
10718 &setup.shortcut.tape_pause, "shortcut.tape_pause"
10722 &setup.shortcut.tape_record, "shortcut.tape_record"
10726 &setup.shortcut.tape_play, "shortcut.tape_play"
10730 &setup.shortcut.sound_simple, "shortcut.sound_simple"
10734 &setup.shortcut.sound_loops, "shortcut.sound_loops"
10738 &setup.shortcut.sound_music, "shortcut.sound_music"
10742 &setup.shortcut.snap_left, "shortcut.snap_left"
10746 &setup.shortcut.snap_right, "shortcut.snap_right"
10750 &setup.shortcut.snap_up, "shortcut.snap_up"
10754 &setup.shortcut.snap_down, "shortcut.snap_down"
10758 static struct SetupInputInfo setup_input;
10759 static struct TokenInfo player_setup_tokens[] =
10763 &setup_input.use_joystick, ".use_joystick"
10767 &setup_input.joy.device_name, ".joy.device_name"
10771 &setup_input.joy.xleft, ".joy.xleft"
10775 &setup_input.joy.xmiddle, ".joy.xmiddle"
10779 &setup_input.joy.xright, ".joy.xright"
10783 &setup_input.joy.yupper, ".joy.yupper"
10787 &setup_input.joy.ymiddle, ".joy.ymiddle"
10791 &setup_input.joy.ylower, ".joy.ylower"
10795 &setup_input.joy.snap, ".joy.snap_field"
10799 &setup_input.joy.drop, ".joy.place_bomb"
10803 &setup_input.key.left, ".key.move_left"
10807 &setup_input.key.right, ".key.move_right"
10811 &setup_input.key.up, ".key.move_up"
10815 &setup_input.key.down, ".key.move_down"
10819 &setup_input.key.snap, ".key.snap_field"
10823 &setup_input.key.drop, ".key.place_bomb"
10827 static struct TokenInfo system_setup_tokens[] =
10831 &setup.system.sdl_renderdriver, "system.sdl_renderdriver"
10835 &setup.system.sdl_videodriver, "system.sdl_videodriver"
10839 &setup.system.sdl_audiodriver, "system.sdl_audiodriver"
10843 &setup.system.audio_fragment_size, "system.audio_fragment_size"
10847 static struct TokenInfo internal_setup_tokens[] =
10851 &setup.internal.program_title, "program_title"
10855 &setup.internal.program_version, "program_version"
10859 &setup.internal.program_author, "program_author"
10863 &setup.internal.program_email, "program_email"
10867 &setup.internal.program_website, "program_website"
10871 &setup.internal.program_copyright, "program_copyright"
10875 &setup.internal.program_company, "program_company"
10879 &setup.internal.program_icon_file, "program_icon_file"
10883 &setup.internal.default_graphics_set, "default_graphics_set"
10887 &setup.internal.default_sounds_set, "default_sounds_set"
10891 &setup.internal.default_music_set, "default_music_set"
10895 &setup.internal.fallback_graphics_file, "fallback_graphics_file"
10899 &setup.internal.fallback_sounds_file, "fallback_sounds_file"
10903 &setup.internal.fallback_music_file, "fallback_music_file"
10907 &setup.internal.default_level_series, "default_level_series"
10911 &setup.internal.default_window_width, "default_window_width"
10915 &setup.internal.default_window_height, "default_window_height"
10919 &setup.internal.choose_from_top_leveldir, "choose_from_top_leveldir"
10923 &setup.internal.show_scaling_in_title, "show_scaling_in_title"
10927 &setup.internal.create_user_levelset, "create_user_levelset"
10931 &setup.internal.info_screens_from_main, "info_screens_from_main"
10935 &setup.internal.menu_game, "menu_game"
10939 &setup.internal.menu_engines, "menu_engines"
10943 &setup.internal.menu_editor, "menu_editor"
10947 &setup.internal.menu_graphics, "menu_graphics"
10951 &setup.internal.menu_sound, "menu_sound"
10955 &setup.internal.menu_artwork, "menu_artwork"
10959 &setup.internal.menu_input, "menu_input"
10963 &setup.internal.menu_touch, "menu_touch"
10967 &setup.internal.menu_shortcuts, "menu_shortcuts"
10971 &setup.internal.menu_exit, "menu_exit"
10975 &setup.internal.menu_save_and_exit, "menu_save_and_exit"
10979 &setup.internal.menu_shortcuts_various, "menu_shortcuts_various"
10983 &setup.internal.menu_shortcuts_focus, "menu_shortcuts_focus"
10987 &setup.internal.menu_shortcuts_tape, "menu_shortcuts_tape"
10991 &setup.internal.menu_shortcuts_sound, "menu_shortcuts_sound"
10995 &setup.internal.menu_shortcuts_snap, "menu_shortcuts_snap"
10999 &setup.internal.info_title, "info_title"
11003 &setup.internal.info_elements, "info_elements"
11007 &setup.internal.info_music, "info_music"
11011 &setup.internal.info_credits, "info_credits"
11015 &setup.internal.info_program, "info_program"
11019 &setup.internal.info_version, "info_version"
11023 &setup.internal.info_levelset, "info_levelset"
11027 &setup.internal.info_exit, "info_exit"
11031 static struct TokenInfo debug_setup_tokens[] =
11035 &setup.debug.frame_delay[0], "debug.frame_delay_0"
11039 &setup.debug.frame_delay[1], "debug.frame_delay_1"
11043 &setup.debug.frame_delay[2], "debug.frame_delay_2"
11047 &setup.debug.frame_delay[3], "debug.frame_delay_3"
11051 &setup.debug.frame_delay[4], "debug.frame_delay_4"
11055 &setup.debug.frame_delay[5], "debug.frame_delay_5"
11059 &setup.debug.frame_delay[6], "debug.frame_delay_6"
11063 &setup.debug.frame_delay[7], "debug.frame_delay_7"
11067 &setup.debug.frame_delay[8], "debug.frame_delay_8"
11071 &setup.debug.frame_delay[9], "debug.frame_delay_9"
11075 &setup.debug.frame_delay_key[0], "debug.key.frame_delay_0"
11079 &setup.debug.frame_delay_key[1], "debug.key.frame_delay_1"
11083 &setup.debug.frame_delay_key[2], "debug.key.frame_delay_2"
11087 &setup.debug.frame_delay_key[3], "debug.key.frame_delay_3"
11091 &setup.debug.frame_delay_key[4], "debug.key.frame_delay_4"
11095 &setup.debug.frame_delay_key[5], "debug.key.frame_delay_5"
11099 &setup.debug.frame_delay_key[6], "debug.key.frame_delay_6"
11103 &setup.debug.frame_delay_key[7], "debug.key.frame_delay_7"
11107 &setup.debug.frame_delay_key[8], "debug.key.frame_delay_8"
11111 &setup.debug.frame_delay_key[9], "debug.key.frame_delay_9"
11115 &setup.debug.frame_delay_use_mod_key, "debug.frame_delay.use_mod_key"},
11118 &setup.debug.frame_delay_game_only, "debug.frame_delay.game_only"
11122 &setup.debug.show_frames_per_second, "debug.show_frames_per_second"
11126 &setup.debug.xsn_mode, "debug.xsn_mode"
11130 &setup.debug.xsn_percent, "debug.xsn_percent"
11134 static struct TokenInfo options_setup_tokens[] =
11138 &setup.options.verbose, "options.verbose"
11142 &setup.options.debug, "options.debug"
11146 &setup.options.debug_mode, "options.debug_mode"
11150 static void setSetupInfoToDefaults(struct SetupInfo *si)
11154 si->player_name = getStringCopy(getDefaultUserName(user.nr));
11156 si->multiple_users = TRUE;
11159 si->sound_loops = TRUE;
11160 si->sound_music = TRUE;
11161 si->sound_simple = TRUE;
11163 si->global_animations = TRUE;
11164 si->scroll_delay = TRUE;
11165 si->forced_scroll_delay = FALSE;
11166 si->scroll_delay_value = STD_SCROLL_DELAY;
11167 si->engine_snapshot_mode = getStringCopy(STR_SNAPSHOT_MODE_DEFAULT);
11168 si->engine_snapshot_memory = SNAPSHOT_MEMORY_DEFAULT;
11169 si->fade_screens = TRUE;
11170 si->autorecord = TRUE;
11171 si->autorecord_after_replay = TRUE;
11172 si->auto_pause_on_start = FALSE;
11173 si->show_titlescreen = TRUE;
11174 si->quick_doors = FALSE;
11175 si->team_mode = FALSE;
11176 si->handicap = TRUE;
11177 si->skip_levels = TRUE;
11178 si->increment_levels = TRUE;
11179 si->auto_play_next_level = TRUE;
11180 si->count_score_after_game = TRUE;
11181 si->show_scores_after_game = TRUE;
11182 si->time_limit = TRUE;
11183 si->fullscreen = FALSE;
11184 si->window_scaling_percent = STD_WINDOW_SCALING_PERCENT;
11185 si->window_scaling_quality = getStringCopy(SCALING_QUALITY_DEFAULT);
11186 si->screen_rendering_mode = getStringCopy(STR_SPECIAL_RENDERING_DEFAULT);
11187 si->vsync_mode = getStringCopy(STR_VSYNC_MODE_DEFAULT);
11188 si->ask_on_escape = TRUE;
11189 si->ask_on_escape_editor = TRUE;
11190 si->ask_on_game_over = TRUE;
11191 si->ask_on_quit_game = TRUE;
11192 si->ask_on_quit_program = TRUE;
11193 si->quick_switch = FALSE;
11194 si->input_on_focus = FALSE;
11195 si->prefer_aga_graphics = TRUE;
11196 si->prefer_lowpass_sounds = FALSE;
11197 si->prefer_extra_panel_items = TRUE;
11198 si->game_speed_extended = FALSE;
11199 si->game_frame_delay = GAME_FRAME_DELAY;
11200 si->bd_skip_uncovering = FALSE;
11201 si->bd_skip_hatching = FALSE;
11202 si->bd_scroll_delay = TRUE;
11203 si->bd_smooth_movements = AUTO;
11204 si->sp_show_border_elements = FALSE;
11205 si->small_game_graphics = FALSE;
11206 si->show_load_save_buttons = FALSE;
11207 si->show_undo_redo_buttons = FALSE;
11208 si->scores_in_highscore_list = getStringCopy(STR_SCORES_TYPE_DEFAULT);
11210 si->graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
11211 si->sounds_set = getStringCopy(SND_CLASSIC_SUBDIR);
11212 si->music_set = getStringCopy(MUS_CLASSIC_SUBDIR);
11214 si->override_level_graphics = FALSE;
11215 si->override_level_sounds = FALSE;
11216 si->override_level_music = FALSE;
11218 si->volume_simple = 100; // percent
11219 si->volume_loops = 100; // percent
11220 si->volume_music = 100; // percent
11222 si->network_mode = FALSE;
11223 si->network_player_nr = 0; // first player
11224 si->network_server_hostname = getStringCopy(STR_NETWORK_AUTO_DETECT);
11226 si->touch.control_type = getStringCopy(TOUCH_CONTROL_DEFAULT);
11227 si->touch.move_distance = TOUCH_MOVE_DISTANCE_DEFAULT; // percent
11228 si->touch.drop_distance = TOUCH_DROP_DISTANCE_DEFAULT; // percent
11229 si->touch.transparency = TOUCH_TRANSPARENCY_DEFAULT; // percent
11230 si->touch.draw_outlined = TRUE;
11231 si->touch.draw_pressed = TRUE;
11233 for (i = 0; i < 2; i++)
11235 char *default_grid_button[6][2] =
11241 { "111222", " vv " },
11242 { "111222", " vv " }
11244 int grid_xsize = DEFAULT_GRID_XSIZE(i);
11245 int grid_ysize = DEFAULT_GRID_YSIZE(i);
11246 int min_xsize = MIN(6, grid_xsize);
11247 int min_ysize = MIN(6, grid_ysize);
11248 int startx = grid_xsize - min_xsize;
11249 int starty = grid_ysize - min_ysize;
11252 // virtual buttons grid can only be set to defaults if video is initialized
11253 // (this will be repeated if virtual buttons are not loaded from setup file)
11254 if (video.initialized)
11256 si->touch.grid_xsize[i] = grid_xsize;
11257 si->touch.grid_ysize[i] = grid_ysize;
11261 si->touch.grid_xsize[i] = -1;
11262 si->touch.grid_ysize[i] = -1;
11265 for (x = 0; x < MAX_GRID_XSIZE; x++)
11266 for (y = 0; y < MAX_GRID_YSIZE; y++)
11267 si->touch.grid_button[i][x][y] = CHAR_GRID_BUTTON_NONE;
11269 for (x = 0; x < min_xsize; x++)
11270 for (y = 0; y < min_ysize; y++)
11271 si->touch.grid_button[i][x][starty + y] =
11272 default_grid_button[y][0][x];
11274 for (x = 0; x < min_xsize; x++)
11275 for (y = 0; y < min_ysize; y++)
11276 si->touch.grid_button[i][startx + x][starty + y] =
11277 default_grid_button[y][1][x];
11280 si->touch.grid_initialized = video.initialized;
11282 si->touch.overlay_buttons = FALSE;
11284 si->editor.el_boulderdash = TRUE;
11285 si->editor.el_boulderdash_native = TRUE;
11286 si->editor.el_boulderdash_effects = TRUE;
11287 si->editor.el_emerald_mine = TRUE;
11288 si->editor.el_emerald_mine_club = TRUE;
11289 si->editor.el_more = TRUE;
11290 si->editor.el_sokoban = TRUE;
11291 si->editor.el_supaplex = TRUE;
11292 si->editor.el_diamond_caves = TRUE;
11293 si->editor.el_dx_boulderdash = TRUE;
11295 si->editor.el_mirror_magic = TRUE;
11296 si->editor.el_deflektor = TRUE;
11298 si->editor.el_chars = TRUE;
11299 si->editor.el_steel_chars = TRUE;
11301 si->editor.el_classic = TRUE;
11302 si->editor.el_custom = TRUE;
11304 si->editor.el_user_defined = FALSE;
11305 si->editor.el_dynamic = TRUE;
11307 si->editor.el_headlines = TRUE;
11309 si->editor.show_element_token = FALSE;
11311 si->editor.show_read_only_warning = TRUE;
11313 si->editor.use_template_for_new_levels = TRUE;
11315 si->shortcut.save_game = DEFAULT_KEY_SAVE_GAME;
11316 si->shortcut.load_game = DEFAULT_KEY_LOAD_GAME;
11317 si->shortcut.restart_game = DEFAULT_KEY_RESTART_GAME;
11318 si->shortcut.pause_before_end = DEFAULT_KEY_PAUSE_BEFORE_END;
11319 si->shortcut.toggle_pause = DEFAULT_KEY_TOGGLE_PAUSE;
11321 si->shortcut.focus_player[0] = DEFAULT_KEY_FOCUS_PLAYER_1;
11322 si->shortcut.focus_player[1] = DEFAULT_KEY_FOCUS_PLAYER_2;
11323 si->shortcut.focus_player[2] = DEFAULT_KEY_FOCUS_PLAYER_3;
11324 si->shortcut.focus_player[3] = DEFAULT_KEY_FOCUS_PLAYER_4;
11325 si->shortcut.focus_player_all = DEFAULT_KEY_FOCUS_PLAYER_ALL;
11327 si->shortcut.tape_eject = DEFAULT_KEY_TAPE_EJECT;
11328 si->shortcut.tape_extra = DEFAULT_KEY_TAPE_EXTRA;
11329 si->shortcut.tape_stop = DEFAULT_KEY_TAPE_STOP;
11330 si->shortcut.tape_pause = DEFAULT_KEY_TAPE_PAUSE;
11331 si->shortcut.tape_record = DEFAULT_KEY_TAPE_RECORD;
11332 si->shortcut.tape_play = DEFAULT_KEY_TAPE_PLAY;
11334 si->shortcut.sound_simple = DEFAULT_KEY_SOUND_SIMPLE;
11335 si->shortcut.sound_loops = DEFAULT_KEY_SOUND_LOOPS;
11336 si->shortcut.sound_music = DEFAULT_KEY_SOUND_MUSIC;
11338 si->shortcut.snap_left = DEFAULT_KEY_SNAP_LEFT;
11339 si->shortcut.snap_right = DEFAULT_KEY_SNAP_RIGHT;
11340 si->shortcut.snap_up = DEFAULT_KEY_SNAP_UP;
11341 si->shortcut.snap_down = DEFAULT_KEY_SNAP_DOWN;
11343 for (i = 0; i < MAX_PLAYERS; i++)
11345 si->input[i].use_joystick = FALSE;
11346 si->input[i].joy.device_name = getStringCopy(getDeviceNameFromJoystickNr(i));
11347 si->input[i].joy.xleft = JOYSTICK_XLEFT;
11348 si->input[i].joy.xmiddle = JOYSTICK_XMIDDLE;
11349 si->input[i].joy.xright = JOYSTICK_XRIGHT;
11350 si->input[i].joy.yupper = JOYSTICK_YUPPER;
11351 si->input[i].joy.ymiddle = JOYSTICK_YMIDDLE;
11352 si->input[i].joy.ylower = JOYSTICK_YLOWER;
11353 si->input[i].joy.snap = (i == 0 ? JOY_BUTTON_1 : 0);
11354 si->input[i].joy.drop = (i == 0 ? JOY_BUTTON_2 : 0);
11355 si->input[i].key.left = (i == 0 ? DEFAULT_KEY_LEFT : KSYM_UNDEFINED);
11356 si->input[i].key.right = (i == 0 ? DEFAULT_KEY_RIGHT : KSYM_UNDEFINED);
11357 si->input[i].key.up = (i == 0 ? DEFAULT_KEY_UP : KSYM_UNDEFINED);
11358 si->input[i].key.down = (i == 0 ? DEFAULT_KEY_DOWN : KSYM_UNDEFINED);
11359 si->input[i].key.snap = (i == 0 ? DEFAULT_KEY_SNAP : KSYM_UNDEFINED);
11360 si->input[i].key.drop = (i == 0 ? DEFAULT_KEY_DROP : KSYM_UNDEFINED);
11363 si->system.sdl_renderdriver = getStringCopy(ARG_DEFAULT);
11364 si->system.sdl_videodriver = getStringCopy(ARG_DEFAULT);
11365 si->system.sdl_audiodriver = getStringCopy(ARG_DEFAULT);
11366 si->system.audio_fragment_size = DEFAULT_AUDIO_FRAGMENT_SIZE;
11368 si->internal.program_title = getStringCopy(PROGRAM_TITLE_STRING);
11369 si->internal.program_version = getStringCopy(getProgramRealVersionString());
11370 si->internal.program_author = getStringCopy(PROGRAM_AUTHOR_STRING);
11371 si->internal.program_email = getStringCopy(PROGRAM_EMAIL_STRING);
11372 si->internal.program_website = getStringCopy(PROGRAM_WEBSITE_STRING);
11373 si->internal.program_copyright = getStringCopy(PROGRAM_COPYRIGHT_STRING);
11374 si->internal.program_company = getStringCopy(PROGRAM_COMPANY_STRING);
11376 si->internal.program_icon_file = getStringCopy(PROGRAM_ICON_FILENAME);
11378 si->internal.default_graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
11379 si->internal.default_sounds_set = getStringCopy(SND_CLASSIC_SUBDIR);
11380 si->internal.default_music_set = getStringCopy(MUS_CLASSIC_SUBDIR);
11382 si->internal.fallback_graphics_file = getStringCopy(UNDEFINED_FILENAME);
11383 si->internal.fallback_sounds_file = getStringCopy(UNDEFINED_FILENAME);
11384 si->internal.fallback_music_file = getStringCopy(UNDEFINED_FILENAME);
11386 si->internal.default_level_series = getStringCopy(UNDEFINED_LEVELSET);
11387 si->internal.choose_from_top_leveldir = FALSE;
11388 si->internal.show_scaling_in_title = TRUE;
11389 si->internal.create_user_levelset = TRUE;
11390 si->internal.info_screens_from_main = FALSE;
11392 si->internal.default_window_width = WIN_XSIZE_DEFAULT;
11393 si->internal.default_window_height = WIN_YSIZE_DEFAULT;
11395 si->debug.frame_delay[0] = DEFAULT_FRAME_DELAY_0;
11396 si->debug.frame_delay[1] = DEFAULT_FRAME_DELAY_1;
11397 si->debug.frame_delay[2] = DEFAULT_FRAME_DELAY_2;
11398 si->debug.frame_delay[3] = DEFAULT_FRAME_DELAY_3;
11399 si->debug.frame_delay[4] = DEFAULT_FRAME_DELAY_4;
11400 si->debug.frame_delay[5] = DEFAULT_FRAME_DELAY_5;
11401 si->debug.frame_delay[6] = DEFAULT_FRAME_DELAY_6;
11402 si->debug.frame_delay[7] = DEFAULT_FRAME_DELAY_7;
11403 si->debug.frame_delay[8] = DEFAULT_FRAME_DELAY_8;
11404 si->debug.frame_delay[9] = DEFAULT_FRAME_DELAY_9;
11406 si->debug.frame_delay_key[0] = DEFAULT_KEY_FRAME_DELAY_0;
11407 si->debug.frame_delay_key[1] = DEFAULT_KEY_FRAME_DELAY_1;
11408 si->debug.frame_delay_key[2] = DEFAULT_KEY_FRAME_DELAY_2;
11409 si->debug.frame_delay_key[3] = DEFAULT_KEY_FRAME_DELAY_3;
11410 si->debug.frame_delay_key[4] = DEFAULT_KEY_FRAME_DELAY_4;
11411 si->debug.frame_delay_key[5] = DEFAULT_KEY_FRAME_DELAY_5;
11412 si->debug.frame_delay_key[6] = DEFAULT_KEY_FRAME_DELAY_6;
11413 si->debug.frame_delay_key[7] = DEFAULT_KEY_FRAME_DELAY_7;
11414 si->debug.frame_delay_key[8] = DEFAULT_KEY_FRAME_DELAY_8;
11415 si->debug.frame_delay_key[9] = DEFAULT_KEY_FRAME_DELAY_9;
11417 si->debug.frame_delay_use_mod_key = DEFAULT_FRAME_DELAY_USE_MOD_KEY;
11418 si->debug.frame_delay_game_only = DEFAULT_FRAME_DELAY_GAME_ONLY;
11420 si->debug.show_frames_per_second = FALSE;
11422 si->debug.xsn_mode = AUTO;
11423 si->debug.xsn_percent = 0;
11425 si->options.verbose = FALSE;
11426 si->options.debug = FALSE;
11427 si->options.debug_mode = getStringCopy(ARG_UNDEFINED_STRING);
11429 #if defined(PLATFORM_ANDROID)
11430 si->fullscreen = TRUE;
11431 si->touch.overlay_buttons = TRUE;
11434 setHideSetupEntry(&setup.debug.xsn_mode);
11437 static void setSetupInfoToDefaults_AutoSetup(struct SetupInfo *si)
11439 si->auto_setup.editor_zoom_tilesize = MINI_TILESIZE;
11442 static void setSetupInfoToDefaults_ServerSetup(struct SetupInfo *si)
11444 si->player_uuid = NULL; // (will be set later)
11445 si->player_version = 1; // (will be set later)
11447 si->use_api_server = TRUE;
11448 si->api_server_hostname = getStringCopy(API_SERVER_HOSTNAME);
11449 si->api_server_password = getStringCopy(UNDEFINED_PASSWORD);
11450 si->ask_for_uploading_tapes = TRUE;
11451 si->ask_for_remaining_tapes = FALSE;
11452 si->provide_uploading_tapes = TRUE;
11453 si->ask_for_using_api_server = TRUE;
11454 si->has_remaining_tapes = FALSE;
11457 static void setSetupInfoToDefaults_EditorCascade(struct SetupInfo *si)
11459 si->editor_cascade.el_bd = TRUE;
11460 si->editor_cascade.el_bd_native = TRUE;
11461 si->editor_cascade.el_bd_effects = FALSE;
11462 si->editor_cascade.el_em = TRUE;
11463 si->editor_cascade.el_emc = TRUE;
11464 si->editor_cascade.el_rnd = TRUE;
11465 si->editor_cascade.el_sb = TRUE;
11466 si->editor_cascade.el_sp = TRUE;
11467 si->editor_cascade.el_dc = TRUE;
11468 si->editor_cascade.el_dx = TRUE;
11470 si->editor_cascade.el_mm = TRUE;
11471 si->editor_cascade.el_df = TRUE;
11473 si->editor_cascade.el_chars = FALSE;
11474 si->editor_cascade.el_steel_chars = FALSE;
11475 si->editor_cascade.el_ce = FALSE;
11476 si->editor_cascade.el_ge = FALSE;
11477 si->editor_cascade.el_es = FALSE;
11478 si->editor_cascade.el_ref = FALSE;
11479 si->editor_cascade.el_user = FALSE;
11480 si->editor_cascade.el_dynamic = FALSE;
11483 #define MAX_HIDE_SETUP_TOKEN_SIZE 20
11485 static char *getHideSetupToken(void *setup_value)
11487 static char hide_setup_token[MAX_HIDE_SETUP_TOKEN_SIZE];
11489 if (setup_value != NULL)
11490 snprintf(hide_setup_token, MAX_HIDE_SETUP_TOKEN_SIZE, "%p", setup_value);
11492 return hide_setup_token;
11495 void setHideSetupEntry(void *setup_value)
11497 char *hide_setup_token = getHideSetupToken(setup_value);
11499 if (hide_setup_hash == NULL)
11500 hide_setup_hash = newSetupFileHash();
11502 if (setup_value != NULL)
11503 setHashEntry(hide_setup_hash, hide_setup_token, "");
11506 void removeHideSetupEntry(void *setup_value)
11508 char *hide_setup_token = getHideSetupToken(setup_value);
11510 if (setup_value != NULL)
11511 removeHashEntry(hide_setup_hash, hide_setup_token);
11514 boolean hideSetupEntry(void *setup_value)
11516 char *hide_setup_token = getHideSetupToken(setup_value);
11518 return (setup_value != NULL &&
11519 getHashEntry(hide_setup_hash, hide_setup_token) != NULL);
11522 static void setSetupInfoFromTokenText(SetupFileHash *setup_file_hash,
11523 struct TokenInfo *token_info,
11524 int token_nr, char *token_text)
11526 char *token_hide_text = getStringCat2(token_text, ".hide");
11527 char *token_hide_value = getHashEntry(setup_file_hash, token_hide_text);
11529 // set the value of this setup option in the setup option structure
11530 setSetupInfo(token_info, token_nr, getHashEntry(setup_file_hash, token_text));
11532 // check if this setup option should be hidden in the setup menu
11533 if (token_hide_value != NULL && get_boolean_from_string(token_hide_value))
11534 setHideSetupEntry(token_info[token_nr].value);
11536 free(token_hide_text);
11539 static void setSetupInfoFromTokenInfo(SetupFileHash *setup_file_hash,
11540 struct TokenInfo *token_info,
11543 setSetupInfoFromTokenText(setup_file_hash, token_info, token_nr,
11544 token_info[token_nr].text);
11547 static void decodeSetupFileHash_Default(SetupFileHash *setup_file_hash)
11551 if (!setup_file_hash)
11554 for (i = 0; i < ARRAY_SIZE(global_setup_tokens); i++)
11555 setSetupInfoFromTokenInfo(setup_file_hash, global_setup_tokens, i);
11557 setup.touch.grid_initialized = TRUE;
11558 for (i = 0; i < 2; i++)
11560 int grid_xsize = setup.touch.grid_xsize[i];
11561 int grid_ysize = setup.touch.grid_ysize[i];
11564 // if virtual buttons are not loaded from setup file, repeat initializing
11565 // virtual buttons grid with default values later when video is initialized
11566 if (grid_xsize == -1 ||
11569 setup.touch.grid_initialized = FALSE;
11574 for (y = 0; y < grid_ysize; y++)
11576 char token_string[MAX_LINE_LEN];
11578 sprintf(token_string, "touch.virtual_buttons.%d.%02d", i, y);
11580 char *value_string = getHashEntry(setup_file_hash, token_string);
11582 if (value_string == NULL)
11585 for (x = 0; x < grid_xsize; x++)
11587 char c = value_string[x];
11589 setup.touch.grid_button[i][x][y] =
11590 (c == '.' ? CHAR_GRID_BUTTON_NONE : c);
11595 for (i = 0; i < ARRAY_SIZE(editor_setup_tokens); i++)
11596 setSetupInfoFromTokenInfo(setup_file_hash, editor_setup_tokens, i);
11598 for (i = 0; i < ARRAY_SIZE(shortcut_setup_tokens); i++)
11599 setSetupInfoFromTokenInfo(setup_file_hash, shortcut_setup_tokens, i);
11601 for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
11605 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
11607 setup_input = setup.input[pnr];
11608 for (i = 0; i < ARRAY_SIZE(player_setup_tokens); i++)
11610 char full_token[100];
11612 sprintf(full_token, "%s%s", prefix, player_setup_tokens[i].text);
11613 setSetupInfoFromTokenText(setup_file_hash, player_setup_tokens, i,
11616 setup.input[pnr] = setup_input;
11619 for (i = 0; i < ARRAY_SIZE(system_setup_tokens); i++)
11620 setSetupInfoFromTokenInfo(setup_file_hash, system_setup_tokens, i);
11622 for (i = 0; i < ARRAY_SIZE(internal_setup_tokens); i++)
11623 setSetupInfoFromTokenInfo(setup_file_hash, internal_setup_tokens, i);
11625 for (i = 0; i < ARRAY_SIZE(debug_setup_tokens); i++)
11626 setSetupInfoFromTokenInfo(setup_file_hash, debug_setup_tokens, i);
11628 for (i = 0; i < ARRAY_SIZE(options_setup_tokens); i++)
11629 setSetupInfoFromTokenInfo(setup_file_hash, options_setup_tokens, i);
11631 setHideRelatedSetupEntries();
11634 static void decodeSetupFileHash_AutoSetup(SetupFileHash *setup_file_hash)
11638 if (!setup_file_hash)
11641 for (i = 0; i < ARRAY_SIZE(auto_setup_tokens); i++)
11642 setSetupInfo(auto_setup_tokens, i,
11643 getHashEntry(setup_file_hash,
11644 auto_setup_tokens[i].text));
11647 static void decodeSetupFileHash_ServerSetup(SetupFileHash *setup_file_hash)
11651 if (!setup_file_hash)
11654 for (i = 0; i < ARRAY_SIZE(server_setup_tokens); i++)
11655 setSetupInfo(server_setup_tokens, i,
11656 getHashEntry(setup_file_hash,
11657 server_setup_tokens[i].text));
11660 static void decodeSetupFileHash_EditorCascade(SetupFileHash *setup_file_hash)
11664 if (!setup_file_hash)
11667 for (i = 0; i < ARRAY_SIZE(editor_cascade_setup_tokens); i++)
11668 setSetupInfo(editor_cascade_setup_tokens, i,
11669 getHashEntry(setup_file_hash,
11670 editor_cascade_setup_tokens[i].text));
11673 void LoadUserNames(void)
11675 int last_user_nr = user.nr;
11678 if (global.user_names != NULL)
11680 for (i = 0; i < MAX_PLAYER_NAMES; i++)
11681 checked_free(global.user_names[i]);
11683 checked_free(global.user_names);
11686 global.user_names = checked_calloc(MAX_PLAYER_NAMES * sizeof(char *));
11688 for (i = 0; i < MAX_PLAYER_NAMES; i++)
11692 SetupFileHash *setup_file_hash = loadSetupFileHash(getSetupFilename());
11694 if (setup_file_hash)
11696 char *player_name = getHashEntry(setup_file_hash, "player_name");
11698 global.user_names[i] = getFixedUserName(player_name);
11700 freeSetupFileHash(setup_file_hash);
11703 if (global.user_names[i] == NULL)
11704 global.user_names[i] = getStringCopy(getDefaultUserName(i));
11707 user.nr = last_user_nr;
11710 void LoadSetupFromFilename(char *filename)
11712 SetupFileHash *setup_file_hash = loadSetupFileHash(filename);
11714 if (setup_file_hash)
11716 decodeSetupFileHash_Default(setup_file_hash);
11718 freeSetupFileHash(setup_file_hash);
11722 Debug("setup", "using default setup values");
11726 static void LoadSetup_SpecialPostProcessing(void)
11728 char *player_name_new;
11730 // needed to work around problems with fixed length strings
11731 player_name_new = getFixedUserName(setup.player_name);
11732 free(setup.player_name);
11733 setup.player_name = player_name_new;
11735 // "scroll_delay: on(3) / off(0)" was replaced by scroll delay value
11736 if (setup.scroll_delay == FALSE)
11738 setup.scroll_delay_value = MIN_SCROLL_DELAY;
11739 setup.scroll_delay = TRUE; // now always "on"
11742 // make sure that scroll delay value stays inside valid range
11743 setup.scroll_delay_value =
11744 MIN(MAX(MIN_SCROLL_DELAY, setup.scroll_delay_value), MAX_SCROLL_DELAY);
11747 void LoadSetup_Default(void)
11751 // always start with reliable default values
11752 setSetupInfoToDefaults(&setup);
11754 // try to load setup values from default setup file
11755 filename = getDefaultSetupFilename();
11757 if (fileExists(filename))
11758 LoadSetupFromFilename(filename);
11760 // try to load setup values from platform setup file
11761 filename = getPlatformSetupFilename();
11763 if (fileExists(filename))
11764 LoadSetupFromFilename(filename);
11766 // try to load setup values from user setup file
11767 filename = getSetupFilename();
11769 LoadSetupFromFilename(filename);
11771 LoadSetup_SpecialPostProcessing();
11774 void LoadSetup_AutoSetup(void)
11776 char *filename = getPath2(getSetupDir(), AUTOSETUP_FILENAME);
11777 SetupFileHash *setup_file_hash = NULL;
11779 // always start with reliable default values
11780 setSetupInfoToDefaults_AutoSetup(&setup);
11782 setup_file_hash = loadSetupFileHash(filename);
11784 if (setup_file_hash)
11786 decodeSetupFileHash_AutoSetup(setup_file_hash);
11788 freeSetupFileHash(setup_file_hash);
11794 void LoadSetup_ServerSetup(void)
11796 char *filename = getPath2(getSetupDir(), SERVERSETUP_FILENAME);
11797 SetupFileHash *setup_file_hash = NULL;
11799 // always start with reliable default values
11800 setSetupInfoToDefaults_ServerSetup(&setup);
11802 setup_file_hash = loadSetupFileHash(filename);
11804 if (setup_file_hash)
11806 decodeSetupFileHash_ServerSetup(setup_file_hash);
11808 freeSetupFileHash(setup_file_hash);
11813 if (setup.player_uuid == NULL)
11815 // player UUID does not yet exist in setup file
11816 setup.player_uuid = getStringCopy(getUUID());
11817 setup.player_version = 2;
11819 SaveSetup_ServerSetup();
11823 void LoadSetup_EditorCascade(void)
11825 char *filename = getPath2(getSetupDir(), EDITORCASCADE_FILENAME);
11826 SetupFileHash *setup_file_hash = NULL;
11828 // always start with reliable default values
11829 setSetupInfoToDefaults_EditorCascade(&setup);
11831 setup_file_hash = loadSetupFileHash(filename);
11833 if (setup_file_hash)
11835 decodeSetupFileHash_EditorCascade(setup_file_hash);
11837 freeSetupFileHash(setup_file_hash);
11843 void LoadSetup(void)
11845 LoadSetup_Default();
11846 LoadSetup_AutoSetup();
11847 LoadSetup_ServerSetup();
11848 LoadSetup_EditorCascade();
11851 static void addGameControllerMappingToHash(SetupFileHash *mappings_hash,
11852 char *mapping_line)
11854 char mapping_guid[MAX_LINE_LEN];
11855 char *mapping_start, *mapping_end;
11857 // get GUID from game controller mapping line: copy complete line
11858 strncpy(mapping_guid, mapping_line, MAX_LINE_LEN - 1);
11859 mapping_guid[MAX_LINE_LEN - 1] = '\0';
11861 // get GUID from game controller mapping line: cut after GUID part
11862 mapping_start = strchr(mapping_guid, ',');
11863 if (mapping_start != NULL)
11864 *mapping_start = '\0';
11866 // cut newline from game controller mapping line
11867 mapping_end = strchr(mapping_line, '\n');
11868 if (mapping_end != NULL)
11869 *mapping_end = '\0';
11871 // add mapping entry to game controller mappings hash
11872 setHashEntry(mappings_hash, mapping_guid, mapping_line);
11875 static void LoadSetup_ReadGameControllerMappings(SetupFileHash *mappings_hash,
11880 if (!(file = fopen(filename, MODE_READ)))
11882 Warn("cannot read game controller mappings file '%s'", filename);
11887 while (!feof(file))
11889 char line[MAX_LINE_LEN];
11891 if (!fgets(line, MAX_LINE_LEN, file))
11894 addGameControllerMappingToHash(mappings_hash, line);
11900 void SaveSetup_Default(void)
11902 char *filename = getSetupFilename();
11906 InitUserDataDirectory();
11908 if (!(file = fopen(filename, MODE_WRITE)))
11910 Warn("cannot write setup file '%s'", filename);
11915 fprintFileHeader(file, SETUP_FILENAME);
11917 for (i = 0; i < ARRAY_SIZE(global_setup_tokens); i++)
11919 // just to make things nicer :)
11920 if (global_setup_tokens[i].value == &setup.multiple_users ||
11921 global_setup_tokens[i].value == &setup.sound ||
11922 global_setup_tokens[i].value == &setup.graphics_set ||
11923 global_setup_tokens[i].value == &setup.volume_simple ||
11924 global_setup_tokens[i].value == &setup.network_mode ||
11925 global_setup_tokens[i].value == &setup.touch.control_type ||
11926 global_setup_tokens[i].value == &setup.touch.grid_xsize[0] ||
11927 global_setup_tokens[i].value == &setup.touch.grid_xsize[1])
11928 fprintf(file, "\n");
11930 fprintf(file, "%s\n", getSetupLine(global_setup_tokens, "", i));
11933 for (i = 0; i < 2; i++)
11935 int grid_xsize = setup.touch.grid_xsize[i];
11936 int grid_ysize = setup.touch.grid_ysize[i];
11939 fprintf(file, "\n");
11941 for (y = 0; y < grid_ysize; y++)
11943 char token_string[MAX_LINE_LEN];
11944 char value_string[MAX_LINE_LEN];
11946 sprintf(token_string, "touch.virtual_buttons.%d.%02d", i, y);
11948 for (x = 0; x < grid_xsize; x++)
11950 char c = setup.touch.grid_button[i][x][y];
11952 value_string[x] = (c == CHAR_GRID_BUTTON_NONE ? '.' : c);
11955 value_string[grid_xsize] = '\0';
11957 fprintf(file, "%s\n", getFormattedSetupEntry(token_string, value_string));
11961 fprintf(file, "\n");
11962 for (i = 0; i < ARRAY_SIZE(editor_setup_tokens); i++)
11963 fprintf(file, "%s\n", getSetupLine(editor_setup_tokens, "", i));
11965 fprintf(file, "\n");
11966 for (i = 0; i < ARRAY_SIZE(shortcut_setup_tokens); i++)
11967 fprintf(file, "%s\n", getSetupLine(shortcut_setup_tokens, "", i));
11969 for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
11973 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
11974 fprintf(file, "\n");
11976 setup_input = setup.input[pnr];
11977 for (i = 0; i < ARRAY_SIZE(player_setup_tokens); i++)
11978 fprintf(file, "%s\n", getSetupLine(player_setup_tokens, prefix, i));
11981 fprintf(file, "\n");
11982 for (i = 0; i < ARRAY_SIZE(system_setup_tokens); i++)
11983 fprintf(file, "%s\n", getSetupLine(system_setup_tokens, "", i));
11985 // (internal setup values not saved to user setup file)
11987 fprintf(file, "\n");
11988 for (i = 0; i < ARRAY_SIZE(debug_setup_tokens); i++)
11989 if (!strPrefix(debug_setup_tokens[i].text, "debug.xsn_") ||
11990 setup.debug.xsn_mode != AUTO)
11991 fprintf(file, "%s\n", getSetupLine(debug_setup_tokens, "", i));
11993 fprintf(file, "\n");
11994 for (i = 0; i < ARRAY_SIZE(options_setup_tokens); i++)
11995 fprintf(file, "%s\n", getSetupLine(options_setup_tokens, "", i));
11999 SetFilePermissions(filename, PERMS_PRIVATE);
12002 void SaveSetup_AutoSetup(void)
12004 char *filename = getPath2(getSetupDir(), AUTOSETUP_FILENAME);
12008 InitUserDataDirectory();
12010 if (!(file = fopen(filename, MODE_WRITE)))
12012 Warn("cannot write auto setup file '%s'", filename);
12019 fprintFileHeader(file, AUTOSETUP_FILENAME);
12021 for (i = 0; i < ARRAY_SIZE(auto_setup_tokens); i++)
12022 fprintf(file, "%s\n", getSetupLine(auto_setup_tokens, "", i));
12026 SetFilePermissions(filename, PERMS_PRIVATE);
12031 void SaveSetup_ServerSetup(void)
12033 char *filename = getPath2(getSetupDir(), SERVERSETUP_FILENAME);
12037 InitUserDataDirectory();
12039 if (!(file = fopen(filename, MODE_WRITE)))
12041 Warn("cannot write server setup file '%s'", filename);
12048 fprintFileHeader(file, SERVERSETUP_FILENAME);
12050 for (i = 0; i < ARRAY_SIZE(server_setup_tokens); i++)
12052 // just to make things nicer :)
12053 if (server_setup_tokens[i].value == &setup.use_api_server)
12054 fprintf(file, "\n");
12056 fprintf(file, "%s\n", getSetupLine(server_setup_tokens, "", i));
12061 SetFilePermissions(filename, PERMS_PRIVATE);
12066 void SaveSetup_EditorCascade(void)
12068 char *filename = getPath2(getSetupDir(), EDITORCASCADE_FILENAME);
12072 InitUserDataDirectory();
12074 if (!(file = fopen(filename, MODE_WRITE)))
12076 Warn("cannot write editor cascade state file '%s'", filename);
12083 fprintFileHeader(file, EDITORCASCADE_FILENAME);
12085 for (i = 0; i < ARRAY_SIZE(editor_cascade_setup_tokens); i++)
12086 fprintf(file, "%s\n", getSetupLine(editor_cascade_setup_tokens, "", i));
12090 SetFilePermissions(filename, PERMS_PRIVATE);
12095 void SaveSetup(void)
12097 SaveSetup_Default();
12098 SaveSetup_AutoSetup();
12099 SaveSetup_ServerSetup();
12100 SaveSetup_EditorCascade();
12103 static void SaveSetup_WriteGameControllerMappings(SetupFileHash *mappings_hash,
12108 if (!(file = fopen(filename, MODE_WRITE)))
12110 Warn("cannot write game controller mappings file '%s'", filename);
12115 BEGIN_HASH_ITERATION(mappings_hash, itr)
12117 fprintf(file, "%s\n", HASH_ITERATION_VALUE(itr));
12119 END_HASH_ITERATION(mappings_hash, itr)
12124 void SaveSetup_AddGameControllerMapping(char *mapping)
12126 char *filename = getPath2(getSetupDir(), GAMECONTROLLER_BASENAME);
12127 SetupFileHash *mappings_hash = newSetupFileHash();
12129 InitUserDataDirectory();
12131 // load existing personal game controller mappings
12132 LoadSetup_ReadGameControllerMappings(mappings_hash, filename);
12134 // add new mapping to personal game controller mappings
12135 addGameControllerMappingToHash(mappings_hash, mapping);
12137 // save updated personal game controller mappings
12138 SaveSetup_WriteGameControllerMappings(mappings_hash, filename);
12140 freeSetupFileHash(mappings_hash);
12144 void LoadCustomElementDescriptions(void)
12146 char *filename = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
12147 SetupFileHash *setup_file_hash;
12150 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
12152 if (element_info[i].custom_description != NULL)
12154 free(element_info[i].custom_description);
12155 element_info[i].custom_description = NULL;
12159 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
12162 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
12164 char *token = getStringCat2(element_info[i].token_name, ".name");
12165 char *value = getHashEntry(setup_file_hash, token);
12168 element_info[i].custom_description = getStringCopy(value);
12173 freeSetupFileHash(setup_file_hash);
12176 static int getElementFromToken(char *token)
12178 char *value = getHashEntry(element_token_hash, token);
12181 return atoi(value);
12183 Warn("unknown element token '%s'", token);
12185 return EL_UNDEFINED;
12188 void FreeGlobalAnimEventInfo(void)
12190 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
12192 if (gaei->event_list == NULL)
12197 for (i = 0; i < gaei->num_event_lists; i++)
12199 checked_free(gaei->event_list[i]->event_value);
12200 checked_free(gaei->event_list[i]);
12203 checked_free(gaei->event_list);
12205 gaei->event_list = NULL;
12206 gaei->num_event_lists = 0;
12209 static int AddGlobalAnimEventList(void)
12211 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
12212 int list_pos = gaei->num_event_lists++;
12214 gaei->event_list = checked_realloc(gaei->event_list, gaei->num_event_lists *
12215 sizeof(struct GlobalAnimEventListInfo *));
12217 gaei->event_list[list_pos] =
12218 checked_calloc(sizeof(struct GlobalAnimEventListInfo));
12220 struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
12222 gaeli->event_value = NULL;
12223 gaeli->num_event_values = 0;
12228 static int AddGlobalAnimEventValue(int list_pos, int event_value)
12230 // do not add empty global animation events
12231 if (event_value == ANIM_EVENT_NONE)
12234 // if list position is undefined, create new list
12235 if (list_pos == ANIM_EVENT_UNDEFINED)
12236 list_pos = AddGlobalAnimEventList();
12238 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
12239 struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
12240 int value_pos = gaeli->num_event_values++;
12242 gaeli->event_value = checked_realloc(gaeli->event_value,
12243 gaeli->num_event_values * sizeof(int *));
12245 gaeli->event_value[value_pos] = event_value;
12250 int GetGlobalAnimEventValue(int list_pos, int value_pos)
12252 if (list_pos == ANIM_EVENT_UNDEFINED)
12253 return ANIM_EVENT_NONE;
12255 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
12256 struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
12258 return gaeli->event_value[value_pos];
12261 int GetGlobalAnimEventValueCount(int list_pos)
12263 if (list_pos == ANIM_EVENT_UNDEFINED)
12266 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
12267 struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
12269 return gaeli->num_event_values;
12272 // This function checks if a string <s> of the format "string1, string2, ..."
12273 // exactly contains a string <s_contained>.
12275 static boolean string_has_parameter(char *s, char *s_contained)
12279 if (s == NULL || s_contained == NULL)
12282 if (strlen(s_contained) > strlen(s))
12285 if (strncmp(s, s_contained, strlen(s_contained)) == 0)
12287 char next_char = s[strlen(s_contained)];
12289 // check if next character is delimiter or whitespace
12290 if (next_char == ',' || next_char == '\0' ||
12291 next_char == ' ' || next_char == '\t')
12295 // check if string contains another parameter string after a comma
12296 substring = strchr(s, ',');
12297 if (substring == NULL) // string does not contain a comma
12300 // advance string pointer to next character after the comma
12303 // skip potential whitespaces after the comma
12304 while (*substring == ' ' || *substring == '\t')
12307 return string_has_parameter(substring, s_contained);
12310 static int get_anim_parameter_value_ce(char *s)
12313 char *pattern_1 = "ce_change:custom_";
12314 char *pattern_2 = ".page_";
12315 int pattern_1_len = strlen(pattern_1);
12316 char *matching_char = strstr(s_ptr, pattern_1);
12317 int result = ANIM_EVENT_NONE;
12319 if (matching_char == NULL)
12320 return ANIM_EVENT_NONE;
12322 result = ANIM_EVENT_CE_CHANGE;
12324 s_ptr = matching_char + pattern_1_len;
12326 // check for custom element number ("custom_X", "custom_XX" or "custom_XXX")
12327 if (*s_ptr >= '0' && *s_ptr <= '9')
12329 int gic_ce_nr = (*s_ptr++ - '0');
12331 if (*s_ptr >= '0' && *s_ptr <= '9')
12333 gic_ce_nr = 10 * gic_ce_nr + (*s_ptr++ - '0');
12335 if (*s_ptr >= '0' && *s_ptr <= '9')
12336 gic_ce_nr = 10 * gic_ce_nr + (*s_ptr++ - '0');
12339 if (gic_ce_nr < 1 || gic_ce_nr > NUM_CUSTOM_ELEMENTS)
12340 return ANIM_EVENT_NONE;
12342 // custom element stored as 0 to 255
12345 result |= gic_ce_nr << ANIM_EVENT_CE_BIT;
12349 // invalid custom element number specified
12351 return ANIM_EVENT_NONE;
12354 // check for change page number ("page_X" or "page_XX") (optional)
12355 if (strPrefix(s_ptr, pattern_2))
12357 s_ptr += strlen(pattern_2);
12359 if (*s_ptr >= '0' && *s_ptr <= '9')
12361 int gic_page_nr = (*s_ptr++ - '0');
12363 if (*s_ptr >= '0' && *s_ptr <= '9')
12364 gic_page_nr = 10 * gic_page_nr + (*s_ptr++ - '0');
12366 if (gic_page_nr < 1 || gic_page_nr > MAX_CHANGE_PAGES)
12367 return ANIM_EVENT_NONE;
12369 // change page stored as 1 to 32 (0 means "all change pages")
12371 result |= gic_page_nr << ANIM_EVENT_PAGE_BIT;
12375 // invalid animation part number specified
12377 return ANIM_EVENT_NONE;
12381 // discard result if next character is neither delimiter nor whitespace
12382 if (!(*s_ptr == ',' || *s_ptr == '\0' ||
12383 *s_ptr == ' ' || *s_ptr == '\t'))
12384 return ANIM_EVENT_NONE;
12389 static int get_anim_parameter_value(char *s)
12391 int event_value[] =
12399 char *pattern_1[] =
12407 char *pattern_2 = ".part_";
12408 char *matching_char = NULL;
12410 int pattern_1_len = 0;
12411 int result = ANIM_EVENT_NONE;
12414 result = get_anim_parameter_value_ce(s);
12416 if (result != ANIM_EVENT_NONE)
12419 for (i = 0; i < ARRAY_SIZE(event_value); i++)
12421 matching_char = strstr(s_ptr, pattern_1[i]);
12422 pattern_1_len = strlen(pattern_1[i]);
12423 result = event_value[i];
12425 if (matching_char != NULL)
12429 if (matching_char == NULL)
12430 return ANIM_EVENT_NONE;
12432 s_ptr = matching_char + pattern_1_len;
12434 // check for main animation number ("anim_X" or "anim_XX")
12435 if (*s_ptr >= '0' && *s_ptr <= '9')
12437 int gic_anim_nr = (*s_ptr++ - '0');
12439 if (*s_ptr >= '0' && *s_ptr <= '9')
12440 gic_anim_nr = 10 * gic_anim_nr + (*s_ptr++ - '0');
12442 if (gic_anim_nr < 1 || gic_anim_nr > MAX_GLOBAL_ANIMS)
12443 return ANIM_EVENT_NONE;
12445 result |= gic_anim_nr << ANIM_EVENT_ANIM_BIT;
12449 // invalid main animation number specified
12451 return ANIM_EVENT_NONE;
12454 // check for animation part number ("part_X" or "part_XX") (optional)
12455 if (strPrefix(s_ptr, pattern_2))
12457 s_ptr += strlen(pattern_2);
12459 if (*s_ptr >= '0' && *s_ptr <= '9')
12461 int gic_part_nr = (*s_ptr++ - '0');
12463 if (*s_ptr >= '0' && *s_ptr <= '9')
12464 gic_part_nr = 10 * gic_part_nr + (*s_ptr++ - '0');
12466 if (gic_part_nr < 1 || gic_part_nr > MAX_GLOBAL_ANIM_PARTS)
12467 return ANIM_EVENT_NONE;
12469 result |= gic_part_nr << ANIM_EVENT_PART_BIT;
12473 // invalid animation part number specified
12475 return ANIM_EVENT_NONE;
12479 // discard result if next character is neither delimiter nor whitespace
12480 if (!(*s_ptr == ',' || *s_ptr == '\0' ||
12481 *s_ptr == ' ' || *s_ptr == '\t'))
12482 return ANIM_EVENT_NONE;
12487 static int get_anim_parameter_values(char *s)
12489 int list_pos = ANIM_EVENT_UNDEFINED;
12490 int event_value = ANIM_EVENT_DEFAULT;
12492 if (string_has_parameter(s, "any"))
12493 event_value |= ANIM_EVENT_ANY;
12495 if (string_has_parameter(s, "click:self") ||
12496 string_has_parameter(s, "click") ||
12497 string_has_parameter(s, "self"))
12498 event_value |= ANIM_EVENT_SELF;
12500 if (string_has_parameter(s, "unclick:any"))
12501 event_value |= ANIM_EVENT_UNCLICK_ANY;
12503 // if animation event found, add it to global animation event list
12504 if (event_value != ANIM_EVENT_NONE)
12505 list_pos = AddGlobalAnimEventValue(list_pos, event_value);
12509 // add optional "click:anim_X" or "click:anim_X.part_X" parameter
12510 event_value = get_anim_parameter_value(s);
12512 // if animation event found, add it to global animation event list
12513 if (event_value != ANIM_EVENT_NONE)
12514 list_pos = AddGlobalAnimEventValue(list_pos, event_value);
12516 // continue with next part of the string, starting with next comma
12517 s = strchr(s + 1, ',');
12523 static int get_anim_action_parameter_value(char *token)
12525 // check most common default case first to massively speed things up
12526 if (strEqual(token, ARG_UNDEFINED))
12527 return ANIM_EVENT_ACTION_NONE;
12529 int result = getImageIDFromToken(token);
12533 char *gfx_token = getStringCat2("gfx.", token);
12535 result = getImageIDFromToken(gfx_token);
12537 checked_free(gfx_token);
12542 Key key = getKeyFromX11KeyName(token);
12544 if (key != KSYM_UNDEFINED)
12545 result = -(int)key;
12552 result = get_hash_from_string(token); // unsigned int => int
12553 result = ABS(result); // may be negative now
12554 result += (result < MAX_IMAGE_FILES ? MAX_IMAGE_FILES : 0);
12556 setHashEntry(anim_url_hash, int2str(result, 0), token);
12561 result = ANIM_EVENT_ACTION_NONE;
12566 int get_parameter_value(char *value_raw, char *suffix, int type)
12568 char *value = getStringToLower(value_raw);
12569 int result = 0; // probably a save default value
12571 if (strEqual(suffix, ".direction"))
12573 result = (strEqual(value, "left") ? MV_LEFT :
12574 strEqual(value, "right") ? MV_RIGHT :
12575 strEqual(value, "up") ? MV_UP :
12576 strEqual(value, "down") ? MV_DOWN : MV_NONE);
12578 else if (strEqual(suffix, ".position"))
12580 result = (strEqual(value, "left") ? POS_LEFT :
12581 strEqual(value, "right") ? POS_RIGHT :
12582 strEqual(value, "top") ? POS_TOP :
12583 strEqual(value, "upper") ? POS_UPPER :
12584 strEqual(value, "middle") ? POS_MIDDLE :
12585 strEqual(value, "lower") ? POS_LOWER :
12586 strEqual(value, "bottom") ? POS_BOTTOM :
12587 strEqual(value, "any") ? POS_ANY :
12588 strEqual(value, "ce") ? POS_CE :
12589 strEqual(value, "ce_trigger") ? POS_CE_TRIGGER :
12590 strEqual(value, "last") ? POS_LAST : POS_UNDEFINED);
12592 else if (strEqual(suffix, ".align"))
12594 result = (strEqual(value, "left") ? ALIGN_LEFT :
12595 strEqual(value, "right") ? ALIGN_RIGHT :
12596 strEqual(value, "center") ? ALIGN_CENTER :
12597 strEqual(value, "middle") ? ALIGN_CENTER : ALIGN_DEFAULT);
12599 else if (strEqual(suffix, ".valign"))
12601 result = (strEqual(value, "top") ? VALIGN_TOP :
12602 strEqual(value, "bottom") ? VALIGN_BOTTOM :
12603 strEqual(value, "middle") ? VALIGN_MIDDLE :
12604 strEqual(value, "center") ? VALIGN_MIDDLE : VALIGN_DEFAULT);
12606 else if (strEqual(suffix, ".anim_mode"))
12608 result = (string_has_parameter(value, "none") ? ANIM_NONE :
12609 string_has_parameter(value, "loop") ? ANIM_LOOP :
12610 string_has_parameter(value, "linear") ? ANIM_LINEAR :
12611 string_has_parameter(value, "pingpong") ? ANIM_PINGPONG :
12612 string_has_parameter(value, "pingpong2") ? ANIM_PINGPONG2 :
12613 string_has_parameter(value, "random") ? ANIM_RANDOM :
12614 string_has_parameter(value, "random_static") ? ANIM_RANDOM_STATIC :
12615 string_has_parameter(value, "ce_value") ? ANIM_CE_VALUE :
12616 string_has_parameter(value, "ce_score") ? ANIM_CE_SCORE :
12617 string_has_parameter(value, "ce_delay") ? ANIM_CE_DELAY :
12618 string_has_parameter(value, "horizontal") ? ANIM_HORIZONTAL :
12619 string_has_parameter(value, "vertical") ? ANIM_VERTICAL :
12620 string_has_parameter(value, "centered") ? ANIM_CENTERED :
12621 string_has_parameter(value, "all") ? ANIM_ALL :
12622 string_has_parameter(value, "tiled") ? ANIM_TILED :
12623 string_has_parameter(value, "level_nr") ? ANIM_LEVEL_NR :
12626 if (string_has_parameter(value, "once"))
12627 result |= ANIM_ONCE;
12629 if (string_has_parameter(value, "reverse"))
12630 result |= ANIM_REVERSE;
12632 if (string_has_parameter(value, "opaque_player"))
12633 result |= ANIM_OPAQUE_PLAYER;
12635 if (string_has_parameter(value, "static_panel"))
12636 result |= ANIM_STATIC_PANEL;
12638 else if (strEqual(suffix, ".init_event") ||
12639 strEqual(suffix, ".anim_event"))
12641 result = get_anim_parameter_values(value);
12643 else if (strEqual(suffix, ".init_delay_action") ||
12644 strEqual(suffix, ".anim_delay_action") ||
12645 strEqual(suffix, ".post_delay_action") ||
12646 strEqual(suffix, ".init_event_action") ||
12647 strEqual(suffix, ".anim_event_action"))
12649 result = get_anim_action_parameter_value(value_raw);
12651 else if (strEqual(suffix, ".class"))
12653 result = (strEqual(value, ARG_UNDEFINED) ? ARG_UNDEFINED_VALUE :
12654 get_hash_from_string(value));
12656 else if (strEqual(suffix, ".style"))
12658 result = STYLE_DEFAULT;
12660 if (string_has_parameter(value, "accurate_borders"))
12661 result |= STYLE_ACCURATE_BORDERS;
12663 if (string_has_parameter(value, "inner_corners"))
12664 result |= STYLE_INNER_CORNERS;
12666 if (string_has_parameter(value, "reverse"))
12667 result |= STYLE_REVERSE;
12669 if (string_has_parameter(value, "leftmost_position"))
12670 result |= STYLE_LEFTMOST_POSITION;
12672 if (string_has_parameter(value, "block_clicks"))
12673 result |= STYLE_BLOCK;
12675 if (string_has_parameter(value, "passthrough_clicks"))
12676 result |= STYLE_PASSTHROUGH;
12678 if (string_has_parameter(value, "multiple_actions"))
12679 result |= STYLE_MULTIPLE_ACTIONS;
12681 if (string_has_parameter(value, "consume_ce_event"))
12682 result |= STYLE_CONSUME_CE_EVENT;
12684 else if (strEqual(suffix, ".fade_mode"))
12686 result = (string_has_parameter(value, "none") ? FADE_MODE_NONE :
12687 string_has_parameter(value, "fade") ? FADE_MODE_FADE :
12688 string_has_parameter(value, "fade_in") ? FADE_MODE_FADE_IN :
12689 string_has_parameter(value, "fade_out") ? FADE_MODE_FADE_OUT :
12690 string_has_parameter(value, "crossfade") ? FADE_MODE_CROSSFADE :
12691 string_has_parameter(value, "melt") ? FADE_MODE_MELT :
12692 string_has_parameter(value, "curtain") ? FADE_MODE_CURTAIN :
12693 FADE_MODE_DEFAULT);
12695 else if (strEqual(suffix, ".auto_delay_unit"))
12697 result = (string_has_parameter(value, "ms") ? AUTO_DELAY_UNIT_MS :
12698 string_has_parameter(value, "frames") ? AUTO_DELAY_UNIT_FRAMES :
12699 AUTO_DELAY_UNIT_DEFAULT);
12701 else if (strPrefix(suffix, ".font")) // (may also be ".font_xyz")
12703 result = gfx.get_font_from_token_function(value);
12705 else // generic parameter of type integer or boolean
12707 result = (strEqual(value, ARG_UNDEFINED) ? ARG_UNDEFINED_VALUE :
12708 type == TYPE_INTEGER ? get_integer_from_string(value) :
12709 type == TYPE_BOOLEAN ? get_boolean_from_string(value) :
12710 ARG_UNDEFINED_VALUE);
12718 static int get_token_parameter_value(char *token, char *value_raw)
12722 if (token == NULL || value_raw == NULL)
12723 return ARG_UNDEFINED_VALUE;
12725 suffix = strrchr(token, '.');
12726 if (suffix == NULL)
12729 if (strEqual(suffix, ".element"))
12730 return getElementFromToken(value_raw);
12732 // !!! USE CORRECT VALUE TYPE (currently works also for TYPE_BOOLEAN) !!!
12733 return get_parameter_value(value_raw, suffix, TYPE_INTEGER);
12736 void InitMenuDesignSettings_FromHash(SetupFileHash *setup_file_hash,
12737 boolean ignore_defaults)
12741 for (i = 0; image_config_vars[i].token != NULL; i++)
12743 char *value = getHashEntry(setup_file_hash, image_config_vars[i].token);
12745 // (ignore definitions set to "[DEFAULT]" which are already initialized)
12746 if (ignore_defaults && strEqual(value, ARG_DEFAULT))
12750 *image_config_vars[i].value =
12751 get_token_parameter_value(image_config_vars[i].token, value);
12755 void InitMenuDesignSettings_Static(void)
12757 // always start with reliable default values from static default config
12758 InitMenuDesignSettings_FromHash(image_config_hash, FALSE);
12761 static void InitMenuDesignSettings_SpecialPreProcessing(void)
12765 // the following initializes hierarchical values from static configuration
12767 // special case: initialize "ARG_DEFAULT" values in static default config
12768 // (e.g., initialize "[titlemessage].fade_mode" from "[title].fade_mode")
12769 titlescreen_initial_first_default.fade_mode =
12770 title_initial_first_default.fade_mode;
12771 titlescreen_initial_first_default.fade_delay =
12772 title_initial_first_default.fade_delay;
12773 titlescreen_initial_first_default.post_delay =
12774 title_initial_first_default.post_delay;
12775 titlescreen_initial_first_default.auto_delay =
12776 title_initial_first_default.auto_delay;
12777 titlescreen_initial_first_default.auto_delay_unit =
12778 title_initial_first_default.auto_delay_unit;
12779 titlescreen_first_default.fade_mode = title_first_default.fade_mode;
12780 titlescreen_first_default.fade_delay = title_first_default.fade_delay;
12781 titlescreen_first_default.post_delay = title_first_default.post_delay;
12782 titlescreen_first_default.auto_delay = title_first_default.auto_delay;
12783 titlescreen_first_default.auto_delay_unit =
12784 title_first_default.auto_delay_unit;
12785 titlemessage_initial_first_default.fade_mode =
12786 title_initial_first_default.fade_mode;
12787 titlemessage_initial_first_default.fade_delay =
12788 title_initial_first_default.fade_delay;
12789 titlemessage_initial_first_default.post_delay =
12790 title_initial_first_default.post_delay;
12791 titlemessage_initial_first_default.auto_delay =
12792 title_initial_first_default.auto_delay;
12793 titlemessage_initial_first_default.auto_delay_unit =
12794 title_initial_first_default.auto_delay_unit;
12795 titlemessage_first_default.fade_mode = title_first_default.fade_mode;
12796 titlemessage_first_default.fade_delay = title_first_default.fade_delay;
12797 titlemessage_first_default.post_delay = title_first_default.post_delay;
12798 titlemessage_first_default.auto_delay = title_first_default.auto_delay;
12799 titlemessage_first_default.auto_delay_unit =
12800 title_first_default.auto_delay_unit;
12802 titlescreen_initial_default.fade_mode = title_initial_default.fade_mode;
12803 titlescreen_initial_default.fade_delay = title_initial_default.fade_delay;
12804 titlescreen_initial_default.post_delay = title_initial_default.post_delay;
12805 titlescreen_initial_default.auto_delay = title_initial_default.auto_delay;
12806 titlescreen_initial_default.auto_delay_unit =
12807 title_initial_default.auto_delay_unit;
12808 titlescreen_default.fade_mode = title_default.fade_mode;
12809 titlescreen_default.fade_delay = title_default.fade_delay;
12810 titlescreen_default.post_delay = title_default.post_delay;
12811 titlescreen_default.auto_delay = title_default.auto_delay;
12812 titlescreen_default.auto_delay_unit = title_default.auto_delay_unit;
12813 titlemessage_initial_default.fade_mode = title_initial_default.fade_mode;
12814 titlemessage_initial_default.fade_delay = title_initial_default.fade_delay;
12815 titlemessage_initial_default.post_delay = title_initial_default.post_delay;
12816 titlemessage_initial_default.auto_delay_unit =
12817 title_initial_default.auto_delay_unit;
12818 titlemessage_default.fade_mode = title_default.fade_mode;
12819 titlemessage_default.fade_delay = title_default.fade_delay;
12820 titlemessage_default.post_delay = title_default.post_delay;
12821 titlemessage_default.auto_delay = title_default.auto_delay;
12822 titlemessage_default.auto_delay_unit = title_default.auto_delay_unit;
12824 // special case: initialize "ARG_DEFAULT" values in static default config
12825 // (e.g., init "titlemessage_1.fade_mode" from "[titlemessage].fade_mode")
12826 for (i = 0; i < MAX_NUM_TITLE_MESSAGES; i++)
12828 titlescreen_initial_first[i] = titlescreen_initial_first_default;
12829 titlescreen_first[i] = titlescreen_first_default;
12830 titlemessage_initial_first[i] = titlemessage_initial_first_default;
12831 titlemessage_first[i] = titlemessage_first_default;
12833 titlescreen_initial[i] = titlescreen_initial_default;
12834 titlescreen[i] = titlescreen_default;
12835 titlemessage_initial[i] = titlemessage_initial_default;
12836 titlemessage[i] = titlemessage_default;
12839 // special case: initialize "ARG_DEFAULT" values in static default config
12840 // (eg, init "menu.enter_screen.SCORES.xyz" from "menu.enter_screen.xyz")
12841 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
12843 if (i == GFX_SPECIAL_ARG_TITLE) // title values already initialized
12846 menu.enter_screen[i] = menu.enter_screen[GFX_SPECIAL_ARG_DEFAULT];
12847 menu.leave_screen[i] = menu.leave_screen[GFX_SPECIAL_ARG_DEFAULT];
12848 menu.next_screen[i] = menu.next_screen[GFX_SPECIAL_ARG_DEFAULT];
12851 // special case: initialize "ARG_DEFAULT" values in static default config
12852 // (eg, init "viewport.door_1.MAIN.xyz" from "viewport.door_1.xyz")
12853 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
12855 viewport.window[i] = viewport.window[GFX_SPECIAL_ARG_DEFAULT];
12856 viewport.playfield[i] = viewport.playfield[GFX_SPECIAL_ARG_DEFAULT];
12857 viewport.door_1[i] = viewport.door_1[GFX_SPECIAL_ARG_DEFAULT];
12859 if (i == GFX_SPECIAL_ARG_EDITOR) // editor values already initialized
12862 viewport.door_2[i] = viewport.door_2[GFX_SPECIAL_ARG_DEFAULT];
12866 static void InitMenuDesignSettings_SpecialPostProcessing(void)
12870 struct XY *dst, *src;
12872 game_buttons_xy[] =
12874 { &game.button.save, &game.button.stop },
12875 { &game.button.pause2, &game.button.pause },
12876 { &game.button.load, &game.button.play },
12877 { &game.button.undo, &game.button.stop },
12878 { &game.button.redo, &game.button.play },
12884 // special case: initialize later added SETUP list size from LEVELS value
12885 if (menu.list_size[GAME_MODE_SETUP] == -1)
12886 menu.list_size[GAME_MODE_SETUP] = menu.list_size[GAME_MODE_LEVELS];
12888 // set default position for snapshot buttons to stop/pause/play buttons
12889 for (i = 0; game_buttons_xy[i].dst != NULL; i++)
12890 if ((*game_buttons_xy[i].dst).x == -1 &&
12891 (*game_buttons_xy[i].dst).y == -1)
12892 *game_buttons_xy[i].dst = *game_buttons_xy[i].src;
12894 // --------------------------------------------------------------------------
12895 // dynamic viewports (including playfield margins, borders and alignments)
12896 // --------------------------------------------------------------------------
12898 // dynamic viewports currently only supported for landscape mode
12899 int display_width = MAX(video.display_width, video.display_height);
12900 int display_height = MIN(video.display_width, video.display_height);
12902 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
12904 struct RectWithBorder *vp_window = &viewport.window[i];
12905 struct RectWithBorder *vp_playfield = &viewport.playfield[i];
12906 struct RectWithBorder *vp_door_1 = &viewport.door_1[i];
12907 struct RectWithBorder *vp_door_2 = &viewport.door_2[i];
12908 boolean dynamic_window_width = (vp_window->min_width != -1);
12909 boolean dynamic_window_height = (vp_window->min_height != -1);
12910 boolean dynamic_playfield_width = (vp_playfield->min_width != -1);
12911 boolean dynamic_playfield_height = (vp_playfield->min_height != -1);
12913 // adjust window size if min/max width/height is specified
12915 if (vp_window->min_width != -1)
12917 int window_width = display_width;
12919 // when using static window height, use aspect ratio of display
12920 if (vp_window->min_height == -1)
12921 window_width = vp_window->height * display_width / display_height;
12923 vp_window->width = MAX(vp_window->min_width, window_width);
12926 if (vp_window->min_height != -1)
12928 int window_height = display_height;
12930 // when using static window width, use aspect ratio of display
12931 if (vp_window->min_width == -1)
12932 window_height = vp_window->width * display_height / display_width;
12934 vp_window->height = MAX(vp_window->min_height, window_height);
12937 if (vp_window->max_width != -1)
12938 vp_window->width = MIN(vp_window->width, vp_window->max_width);
12940 if (vp_window->max_height != -1)
12941 vp_window->height = MIN(vp_window->height, vp_window->max_height);
12943 int playfield_width = vp_window->width;
12944 int playfield_height = vp_window->height;
12946 // adjust playfield size and position according to specified margins
12948 playfield_width -= vp_playfield->margin_left;
12949 playfield_width -= vp_playfield->margin_right;
12951 playfield_height -= vp_playfield->margin_top;
12952 playfield_height -= vp_playfield->margin_bottom;
12954 // adjust playfield size if min/max width/height is specified
12956 if (vp_playfield->min_width != -1)
12957 vp_playfield->width = MAX(vp_playfield->min_width, playfield_width);
12959 if (vp_playfield->min_height != -1)
12960 vp_playfield->height = MAX(vp_playfield->min_height, playfield_height);
12962 if (vp_playfield->max_width != -1)
12963 vp_playfield->width = MIN(vp_playfield->width, vp_playfield->max_width);
12965 if (vp_playfield->max_height != -1)
12966 vp_playfield->height = MIN(vp_playfield->height, vp_playfield->max_height);
12968 // adjust playfield position according to specified alignment
12970 if (vp_playfield->align == ALIGN_LEFT || vp_playfield->x > 0)
12971 vp_playfield->x = ALIGNED_VP_XPOS(vp_playfield);
12972 else if (vp_playfield->align == ALIGN_CENTER)
12973 vp_playfield->x = playfield_width / 2 - vp_playfield->width / 2;
12974 else if (vp_playfield->align == ALIGN_RIGHT)
12975 vp_playfield->x += playfield_width - vp_playfield->width;
12977 if (vp_playfield->valign == VALIGN_TOP || vp_playfield->y > 0)
12978 vp_playfield->y = ALIGNED_VP_YPOS(vp_playfield);
12979 else if (vp_playfield->valign == VALIGN_MIDDLE)
12980 vp_playfield->y = playfield_height / 2 - vp_playfield->height / 2;
12981 else if (vp_playfield->valign == VALIGN_BOTTOM)
12982 vp_playfield->y += playfield_height - vp_playfield->height;
12984 vp_playfield->x += vp_playfield->margin_left;
12985 vp_playfield->y += vp_playfield->margin_top;
12987 // adjust individual playfield borders if only default border is specified
12989 if (vp_playfield->border_left == -1)
12990 vp_playfield->border_left = vp_playfield->border_size;
12991 if (vp_playfield->border_right == -1)
12992 vp_playfield->border_right = vp_playfield->border_size;
12993 if (vp_playfield->border_top == -1)
12994 vp_playfield->border_top = vp_playfield->border_size;
12995 if (vp_playfield->border_bottom == -1)
12996 vp_playfield->border_bottom = vp_playfield->border_size;
12998 // set dynamic playfield borders if borders are specified as undefined
12999 // (but only if window size was dynamic and playfield size was static)
13001 if (dynamic_window_width && !dynamic_playfield_width)
13003 if (vp_playfield->border_left == -1)
13005 vp_playfield->border_left = (vp_playfield->x -
13006 vp_playfield->margin_left);
13007 vp_playfield->x -= vp_playfield->border_left;
13008 vp_playfield->width += vp_playfield->border_left;
13011 if (vp_playfield->border_right == -1)
13013 vp_playfield->border_right = (vp_window->width -
13015 vp_playfield->width -
13016 vp_playfield->margin_right);
13017 vp_playfield->width += vp_playfield->border_right;
13021 if (dynamic_window_height && !dynamic_playfield_height)
13023 if (vp_playfield->border_top == -1)
13025 vp_playfield->border_top = (vp_playfield->y -
13026 vp_playfield->margin_top);
13027 vp_playfield->y -= vp_playfield->border_top;
13028 vp_playfield->height += vp_playfield->border_top;
13031 if (vp_playfield->border_bottom == -1)
13033 vp_playfield->border_bottom = (vp_window->height -
13035 vp_playfield->height -
13036 vp_playfield->margin_bottom);
13037 vp_playfield->height += vp_playfield->border_bottom;
13041 // adjust playfield size to be a multiple of a defined alignment tile size
13043 int align_size = vp_playfield->align_size;
13044 int playfield_xtiles = vp_playfield->width / align_size;
13045 int playfield_ytiles = vp_playfield->height / align_size;
13046 int playfield_width_corrected = playfield_xtiles * align_size;
13047 int playfield_height_corrected = playfield_ytiles * align_size;
13048 boolean is_playfield_mode = (i == GFX_SPECIAL_ARG_PLAYING ||
13049 i == GFX_SPECIAL_ARG_EDITOR);
13051 if (is_playfield_mode &&
13052 dynamic_playfield_width &&
13053 vp_playfield->width != playfield_width_corrected)
13055 int playfield_xdiff = vp_playfield->width - playfield_width_corrected;
13057 vp_playfield->width = playfield_width_corrected;
13059 if (vp_playfield->align == ALIGN_LEFT)
13061 vp_playfield->border_left += playfield_xdiff;
13063 else if (vp_playfield->align == ALIGN_RIGHT)
13065 vp_playfield->border_right += playfield_xdiff;
13067 else if (vp_playfield->align == ALIGN_CENTER)
13069 int border_left_diff = playfield_xdiff / 2;
13070 int border_right_diff = playfield_xdiff - border_left_diff;
13072 vp_playfield->border_left += border_left_diff;
13073 vp_playfield->border_right += border_right_diff;
13077 if (is_playfield_mode &&
13078 dynamic_playfield_height &&
13079 vp_playfield->height != playfield_height_corrected)
13081 int playfield_ydiff = vp_playfield->height - playfield_height_corrected;
13083 vp_playfield->height = playfield_height_corrected;
13085 if (vp_playfield->valign == VALIGN_TOP)
13087 vp_playfield->border_top += playfield_ydiff;
13089 else if (vp_playfield->align == VALIGN_BOTTOM)
13091 vp_playfield->border_right += playfield_ydiff;
13093 else if (vp_playfield->align == VALIGN_MIDDLE)
13095 int border_top_diff = playfield_ydiff / 2;
13096 int border_bottom_diff = playfield_ydiff - border_top_diff;
13098 vp_playfield->border_top += border_top_diff;
13099 vp_playfield->border_bottom += border_bottom_diff;
13103 // adjust door positions according to specified alignment
13105 for (j = 0; j < 2; j++)
13107 struct RectWithBorder *vp_door = (j == 0 ? vp_door_1 : vp_door_2);
13109 if (vp_door->align == ALIGN_LEFT || vp_door->x > 0)
13110 vp_door->x = ALIGNED_VP_XPOS(vp_door);
13111 else if (vp_door->align == ALIGN_CENTER)
13112 vp_door->x = vp_window->width / 2 - vp_door->width / 2;
13113 else if (vp_door->align == ALIGN_RIGHT)
13114 vp_door->x += vp_window->width - vp_door->width;
13116 if (vp_door->valign == VALIGN_TOP || vp_door->y > 0)
13117 vp_door->y = ALIGNED_VP_YPOS(vp_door);
13118 else if (vp_door->valign == VALIGN_MIDDLE)
13119 vp_door->y = vp_window->height / 2 - vp_door->height / 2;
13120 else if (vp_door->valign == VALIGN_BOTTOM)
13121 vp_door->y += vp_window->height - vp_door->height;
13126 static void InitMenuDesignSettings_SpecialPostProcessing_AfterGraphics(void)
13130 struct XYTileSize *dst, *src;
13133 editor_buttons_xy[] =
13136 &editor.button.element_left, &editor.palette.element_left,
13137 IMG_GFX_EDITOR_BUTTON_ELEMENT_LEFT
13140 &editor.button.element_middle, &editor.palette.element_middle,
13141 IMG_GFX_EDITOR_BUTTON_ELEMENT_MIDDLE
13144 &editor.button.element_right, &editor.palette.element_right,
13145 IMG_GFX_EDITOR_BUTTON_ELEMENT_RIGHT
13152 // set default position for element buttons to element graphics
13153 for (i = 0; editor_buttons_xy[i].dst != NULL; i++)
13155 if ((*editor_buttons_xy[i].dst).x == -1 &&
13156 (*editor_buttons_xy[i].dst).y == -1)
13158 struct GraphicInfo *gd = &graphic_info[editor_buttons_xy[i].graphic];
13160 gd->width = gd->height = editor_buttons_xy[i].src->tile_size;
13162 *editor_buttons_xy[i].dst = *editor_buttons_xy[i].src;
13166 // adjust editor palette rows and columns if specified to be dynamic
13168 if (editor.palette.cols == -1)
13170 int vp_width = viewport.playfield[GFX_SPECIAL_ARG_EDITOR].width;
13171 int bt_width = graphic_info[IMG_EDITOR_PALETTE_BUTTON].width;
13172 int sc_width = graphic_info[IMG_EDITOR_PALETTE_SCROLLBAR].width;
13174 editor.palette.cols = (vp_width - sc_width) / bt_width;
13176 if (editor.palette.x == -1)
13178 int palette_width = editor.palette.cols * bt_width + sc_width;
13180 editor.palette.x = (vp_width - palette_width) / 2;
13184 if (editor.palette.rows == -1)
13186 int vp_height = viewport.playfield[GFX_SPECIAL_ARG_EDITOR].height;
13187 int bt_height = graphic_info[IMG_EDITOR_PALETTE_BUTTON].height;
13188 int tx_height = getFontHeight(FONT_TEXT_2);
13190 editor.palette.rows = (vp_height - tx_height) / bt_height;
13192 if (editor.palette.y == -1)
13194 int palette_height = editor.palette.rows * bt_height + tx_height;
13196 editor.palette.y = (vp_height - palette_height) / 2;
13201 static void InitMenuDesignSettings_PreviewPlayers_Ext(SetupFileHash *hash,
13202 boolean initialize)
13204 // special case: check if network and preview player positions are redefined,
13205 // to compare this later against the main menu level preview being redefined
13206 struct TokenIntPtrInfo menu_config_players[] =
13208 { "main.network_players.x", &menu.main.network_players.redefined },
13209 { "main.network_players.y", &menu.main.network_players.redefined },
13210 { "main.preview_players.x", &menu.main.preview_players.redefined },
13211 { "main.preview_players.y", &menu.main.preview_players.redefined },
13212 { "preview.x", &preview.redefined },
13213 { "preview.y", &preview.redefined }
13219 for (i = 0; i < ARRAY_SIZE(menu_config_players); i++)
13220 *menu_config_players[i].value = FALSE;
13224 for (i = 0; i < ARRAY_SIZE(menu_config_players); i++)
13225 if (getHashEntry(hash, menu_config_players[i].token) != NULL)
13226 *menu_config_players[i].value = TRUE;
13230 static void InitMenuDesignSettings_PreviewPlayers(void)
13232 InitMenuDesignSettings_PreviewPlayers_Ext(NULL, TRUE);
13235 static void InitMenuDesignSettings_PreviewPlayers_FromHash(SetupFileHash *hash)
13237 InitMenuDesignSettings_PreviewPlayers_Ext(hash, FALSE);
13240 static void LoadMenuDesignSettingsFromFilename(char *filename)
13242 static struct TitleFadingInfo tfi;
13243 static struct TitleMessageInfo tmi;
13244 static struct TokenInfo title_tokens[] =
13246 { TYPE_INTEGER, &tfi.fade_mode, ".fade_mode" },
13247 { TYPE_INTEGER, &tfi.fade_delay, ".fade_delay" },
13248 { TYPE_INTEGER, &tfi.post_delay, ".post_delay" },
13249 { TYPE_INTEGER, &tfi.auto_delay, ".auto_delay" },
13250 { TYPE_INTEGER, &tfi.auto_delay_unit, ".auto_delay_unit" },
13254 static struct TokenInfo titlemessage_tokens[] =
13256 { TYPE_INTEGER, &tmi.x, ".x" },
13257 { TYPE_INTEGER, &tmi.y, ".y" },
13258 { TYPE_INTEGER, &tmi.width, ".width" },
13259 { TYPE_INTEGER, &tmi.height, ".height" },
13260 { TYPE_INTEGER, &tmi.chars, ".chars" },
13261 { TYPE_INTEGER, &tmi.lines, ".lines" },
13262 { TYPE_INTEGER, &tmi.align, ".align" },
13263 { TYPE_INTEGER, &tmi.valign, ".valign" },
13264 { TYPE_INTEGER, &tmi.font, ".font" },
13265 { TYPE_BOOLEAN, &tmi.autowrap, ".autowrap" },
13266 { TYPE_BOOLEAN, &tmi.centered, ".centered" },
13267 { TYPE_BOOLEAN, &tmi.parse_comments, ".parse_comments" },
13268 { TYPE_INTEGER, &tmi.sort_priority, ".sort_priority" },
13269 { TYPE_INTEGER, &tmi.fade_mode, ".fade_mode" },
13270 { TYPE_INTEGER, &tmi.fade_delay, ".fade_delay" },
13271 { TYPE_INTEGER, &tmi.post_delay, ".post_delay" },
13272 { TYPE_INTEGER, &tmi.auto_delay, ".auto_delay" },
13273 { TYPE_INTEGER, &tmi.auto_delay_unit, ".auto_delay_unit" },
13279 struct TitleFadingInfo *info;
13284 // initialize first titles from "enter screen" definitions, if defined
13285 { &title_initial_first_default, "menu.enter_screen.TITLE" },
13286 { &title_first_default, "menu.enter_screen.TITLE" },
13288 // initialize title screens from "next screen" definitions, if defined
13289 { &title_initial_default, "menu.next_screen.TITLE" },
13290 { &title_default, "menu.next_screen.TITLE" },
13296 struct TitleMessageInfo *array;
13299 titlemessage_arrays[] =
13301 // initialize first titles from "enter screen" definitions, if defined
13302 { titlescreen_initial_first, "menu.enter_screen.TITLE" },
13303 { titlescreen_first, "menu.enter_screen.TITLE" },
13304 { titlemessage_initial_first, "menu.enter_screen.TITLE" },
13305 { titlemessage_first, "menu.enter_screen.TITLE" },
13307 // initialize titles from "next screen" definitions, if defined
13308 { titlescreen_initial, "menu.next_screen.TITLE" },
13309 { titlescreen, "menu.next_screen.TITLE" },
13310 { titlemessage_initial, "menu.next_screen.TITLE" },
13311 { titlemessage, "menu.next_screen.TITLE" },
13313 // overwrite titles with title definitions, if defined
13314 { titlescreen_initial_first, "[title_initial]" },
13315 { titlescreen_first, "[title]" },
13316 { titlemessage_initial_first, "[title_initial]" },
13317 { titlemessage_first, "[title]" },
13319 { titlescreen_initial, "[title_initial]" },
13320 { titlescreen, "[title]" },
13321 { titlemessage_initial, "[title_initial]" },
13322 { titlemessage, "[title]" },
13324 // overwrite titles with title screen/message definitions, if defined
13325 { titlescreen_initial_first, "[titlescreen_initial]" },
13326 { titlescreen_first, "[titlescreen]" },
13327 { titlemessage_initial_first, "[titlemessage_initial]" },
13328 { titlemessage_first, "[titlemessage]" },
13330 { titlescreen_initial, "[titlescreen_initial]" },
13331 { titlescreen, "[titlescreen]" },
13332 { titlemessage_initial, "[titlemessage_initial]" },
13333 { titlemessage, "[titlemessage]" },
13337 SetupFileHash *setup_file_hash;
13340 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
13343 // the following initializes hierarchical values from dynamic configuration
13345 // special case: initialize with default values that may be overwritten
13346 // (e.g., init "menu.draw_xoffset.INFO" from "menu.draw_xoffset")
13347 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
13349 struct TokenIntPtrInfo menu_config[] =
13351 { "menu.draw_xoffset", &menu.draw_xoffset[i] },
13352 { "menu.draw_yoffset", &menu.draw_yoffset[i] },
13353 { "menu.list_size", &menu.list_size[i] }
13356 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
13358 char *token = menu_config[j].token;
13359 char *value = getHashEntry(setup_file_hash, token);
13362 *menu_config[j].value = get_integer_from_string(value);
13366 // special case: initialize with default values that may be overwritten
13367 // (eg, init "menu.draw_xoffset.INFO[XXX]" from "menu.draw_xoffset.INFO")
13368 for (i = 0; i < NUM_SPECIAL_GFX_INFO_ARGS; i++)
13370 struct TokenIntPtrInfo menu_config[] =
13372 { "menu.draw_xoffset.INFO", &menu.draw_xoffset_info[i] },
13373 { "menu.draw_yoffset.INFO", &menu.draw_yoffset_info[i] },
13374 { "menu.list_size.INFO", &menu.list_size_info[i] },
13375 { "menu.list_entry_size.INFO", &menu.list_entry_size_info[i] },
13376 { "menu.tile_size.INFO", &menu.tile_size_info[i] }
13379 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
13381 char *token = menu_config[j].token;
13382 char *value = getHashEntry(setup_file_hash, token);
13385 *menu_config[j].value = get_integer_from_string(value);
13389 // special case: initialize with default values that may be overwritten
13390 // (eg, init "menu.draw_xoffset.SETUP[XXX]" from "menu.draw_xoffset.SETUP")
13391 for (i = 0; i < NUM_SPECIAL_GFX_SETUP_ARGS; i++)
13393 struct TokenIntPtrInfo menu_config[] =
13395 { "menu.draw_xoffset.SETUP", &menu.draw_xoffset_setup[i] },
13396 { "menu.draw_yoffset.SETUP", &menu.draw_yoffset_setup[i] }
13399 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
13401 char *token = menu_config[j].token;
13402 char *value = getHashEntry(setup_file_hash, token);
13405 *menu_config[j].value = get_integer_from_string(value);
13409 // special case: initialize with default values that may be overwritten
13410 // (eg, init "menu.line_spacing.INFO[XXX]" from "menu.line_spacing.INFO")
13411 for (i = 0; i < NUM_SPECIAL_GFX_INFO_ARGS; i++)
13413 struct TokenIntPtrInfo menu_config[] =
13415 { "menu.left_spacing.INFO", &menu.left_spacing_info[i] },
13416 { "menu.middle_spacing.INFO", &menu.middle_spacing_info[i] },
13417 { "menu.right_spacing.INFO", &menu.right_spacing_info[i] },
13418 { "menu.top_spacing.INFO", &menu.top_spacing_info[i] },
13419 { "menu.bottom_spacing.INFO", &menu.bottom_spacing_info[i] },
13420 { "menu.paragraph_spacing.INFO", &menu.paragraph_spacing_info[i] },
13421 { "menu.headline1_spacing.INFO", &menu.headline1_spacing_info[i] },
13422 { "menu.headline2_spacing.INFO", &menu.headline2_spacing_info[i] },
13423 { "menu.line_spacing.INFO", &menu.line_spacing_info[i] },
13424 { "menu.extra_spacing.INFO", &menu.extra_spacing_info[i] },
13427 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
13429 char *token = menu_config[j].token;
13430 char *value = getHashEntry(setup_file_hash, token);
13433 *menu_config[j].value = get_integer_from_string(value);
13437 // special case: initialize with default values that may be overwritten
13438 // (eg, init "menu.enter_screen.SCORES.xyz" from "menu.enter_screen.xyz")
13439 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
13441 struct TokenIntPtrInfo menu_config[] =
13443 { "menu.enter_screen.fade_mode", &menu.enter_screen[i].fade_mode },
13444 { "menu.enter_screen.fade_delay", &menu.enter_screen[i].fade_delay },
13445 { "menu.enter_screen.post_delay", &menu.enter_screen[i].post_delay },
13446 { "menu.leave_screen.fade_mode", &menu.leave_screen[i].fade_mode },
13447 { "menu.leave_screen.fade_delay", &menu.leave_screen[i].fade_delay },
13448 { "menu.leave_screen.post_delay", &menu.leave_screen[i].post_delay },
13449 { "menu.next_screen.fade_mode", &menu.next_screen[i].fade_mode },
13450 { "menu.next_screen.fade_delay", &menu.next_screen[i].fade_delay },
13451 { "menu.next_screen.post_delay", &menu.next_screen[i].post_delay }
13454 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
13456 char *token = menu_config[j].token;
13457 char *value = getHashEntry(setup_file_hash, token);
13460 *menu_config[j].value = get_token_parameter_value(token, value);
13464 // special case: initialize with default values that may be overwritten
13465 // (eg, init "viewport.door_1.MAIN.xyz" from "viewport.door_1.xyz")
13466 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
13470 char *token_prefix;
13471 struct RectWithBorder *struct_ptr;
13475 { "viewport.window", &viewport.window[i] },
13476 { "viewport.playfield", &viewport.playfield[i] },
13477 { "viewport.door_1", &viewport.door_1[i] },
13478 { "viewport.door_2", &viewport.door_2[i] }
13481 for (j = 0; j < ARRAY_SIZE(vp_struct); j++)
13483 struct TokenIntPtrInfo vp_config[] =
13485 { ".x", &vp_struct[j].struct_ptr->x },
13486 { ".y", &vp_struct[j].struct_ptr->y },
13487 { ".width", &vp_struct[j].struct_ptr->width },
13488 { ".height", &vp_struct[j].struct_ptr->height },
13489 { ".min_width", &vp_struct[j].struct_ptr->min_width },
13490 { ".min_height", &vp_struct[j].struct_ptr->min_height },
13491 { ".max_width", &vp_struct[j].struct_ptr->max_width },
13492 { ".max_height", &vp_struct[j].struct_ptr->max_height },
13493 { ".margin_left", &vp_struct[j].struct_ptr->margin_left },
13494 { ".margin_right", &vp_struct[j].struct_ptr->margin_right },
13495 { ".margin_top", &vp_struct[j].struct_ptr->margin_top },
13496 { ".margin_bottom", &vp_struct[j].struct_ptr->margin_bottom },
13497 { ".border_left", &vp_struct[j].struct_ptr->border_left },
13498 { ".border_right", &vp_struct[j].struct_ptr->border_right },
13499 { ".border_top", &vp_struct[j].struct_ptr->border_top },
13500 { ".border_bottom", &vp_struct[j].struct_ptr->border_bottom },
13501 { ".border_size", &vp_struct[j].struct_ptr->border_size },
13502 { ".align_size", &vp_struct[j].struct_ptr->align_size },
13503 { ".align", &vp_struct[j].struct_ptr->align },
13504 { ".valign", &vp_struct[j].struct_ptr->valign }
13507 for (k = 0; k < ARRAY_SIZE(vp_config); k++)
13509 char *token = getStringCat2(vp_struct[j].token_prefix,
13510 vp_config[k].token);
13511 char *value = getHashEntry(setup_file_hash, token);
13514 *vp_config[k].value = get_token_parameter_value(token, value);
13521 // special case: initialize with default values that may be overwritten
13522 // (e.g., init "[title].fade_mode" from "menu.next_screen.TITLE.fade_mode")
13523 for (i = 0; title_info[i].info != NULL; i++)
13525 struct TitleFadingInfo *info = title_info[i].info;
13526 char *base_token = title_info[i].text;
13528 for (j = 0; title_tokens[j].type != -1; j++)
13530 char *token = getStringCat2(base_token, title_tokens[j].text);
13531 char *value = getHashEntry(setup_file_hash, token);
13535 int parameter_value = get_token_parameter_value(token, value);
13539 *(int *)title_tokens[j].value = (int)parameter_value;
13548 // special case: initialize with default values that may be overwritten
13549 // (e.g., init "titlemessage_1.fade_mode" from "[titlemessage].fade_mode")
13550 for (i = 0; titlemessage_arrays[i].array != NULL; i++)
13552 struct TitleMessageInfo *array = titlemessage_arrays[i].array;
13553 char *base_token = titlemessage_arrays[i].text;
13555 for (j = 0; titlemessage_tokens[j].type != -1; j++)
13557 char *token = getStringCat2(base_token, titlemessage_tokens[j].text);
13558 char *value = getHashEntry(setup_file_hash, token);
13562 int parameter_value = get_token_parameter_value(token, value);
13564 for (k = 0; k < MAX_NUM_TITLE_MESSAGES; k++)
13568 if (titlemessage_tokens[j].type == TYPE_INTEGER)
13569 *(int *)titlemessage_tokens[j].value = (int)parameter_value;
13571 *(boolean *)titlemessage_tokens[j].value = (boolean)parameter_value;
13581 // read (and overwrite with) values that may be specified in config file
13582 InitMenuDesignSettings_FromHash(setup_file_hash, TRUE);
13584 // special case: check if network and preview player positions are redefined
13585 InitMenuDesignSettings_PreviewPlayers_FromHash(setup_file_hash);
13587 freeSetupFileHash(setup_file_hash);
13590 void LoadMenuDesignSettings(void)
13592 char *filename_base = UNDEFINED_FILENAME, *filename_local;
13594 InitMenuDesignSettings_Static();
13595 InitMenuDesignSettings_SpecialPreProcessing();
13596 InitMenuDesignSettings_PreviewPlayers();
13598 if (!GFX_OVERRIDE_ARTWORK(ARTWORK_TYPE_GRAPHICS))
13600 // first look for special settings configured in level series config
13601 filename_base = getCustomArtworkLevelConfigFilename(ARTWORK_TYPE_GRAPHICS);
13603 if (fileExists(filename_base))
13604 LoadMenuDesignSettingsFromFilename(filename_base);
13607 filename_local = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
13609 if (filename_local != NULL && !strEqual(filename_base, filename_local))
13610 LoadMenuDesignSettingsFromFilename(filename_local);
13612 InitMenuDesignSettings_SpecialPostProcessing();
13615 void LoadMenuDesignSettings_AfterGraphics(void)
13617 InitMenuDesignSettings_SpecialPostProcessing_AfterGraphics();
13620 void InitSoundSettings_FromHash(SetupFileHash *setup_file_hash,
13621 boolean ignore_defaults)
13625 for (i = 0; sound_config_vars[i].token != NULL; i++)
13627 char *value = getHashEntry(setup_file_hash, sound_config_vars[i].token);
13629 // (ignore definitions set to "[DEFAULT]" which are already initialized)
13630 if (ignore_defaults && strEqual(value, ARG_DEFAULT))
13634 *sound_config_vars[i].value =
13635 get_token_parameter_value(sound_config_vars[i].token, value);
13639 void InitSoundSettings_Static(void)
13641 // always start with reliable default values from static default config
13642 InitSoundSettings_FromHash(sound_config_hash, FALSE);
13645 static void LoadSoundSettingsFromFilename(char *filename)
13647 SetupFileHash *setup_file_hash;
13649 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
13652 // read (and overwrite with) values that may be specified in config file
13653 InitSoundSettings_FromHash(setup_file_hash, TRUE);
13655 freeSetupFileHash(setup_file_hash);
13658 void LoadSoundSettings(void)
13660 char *filename_base = UNDEFINED_FILENAME, *filename_local;
13662 InitSoundSettings_Static();
13664 if (!GFX_OVERRIDE_ARTWORK(ARTWORK_TYPE_SOUNDS))
13666 // first look for special settings configured in level series config
13667 filename_base = getCustomArtworkLevelConfigFilename(ARTWORK_TYPE_SOUNDS);
13669 if (fileExists(filename_base))
13670 LoadSoundSettingsFromFilename(filename_base);
13673 filename_local = getCustomArtworkConfigFilename(ARTWORK_TYPE_SOUNDS);
13675 if (filename_local != NULL && !strEqual(filename_base, filename_local))
13676 LoadSoundSettingsFromFilename(filename_local);
13679 void LoadUserDefinedEditorElementList(int **elements, int *num_elements)
13681 char *filename = getEditorSetupFilename();
13682 SetupFileList *setup_file_list, *list;
13683 SetupFileHash *element_hash;
13684 int num_unknown_tokens = 0;
13687 if ((setup_file_list = loadSetupFileList(filename)) == NULL)
13690 element_hash = newSetupFileHash();
13692 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
13693 setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
13695 // determined size may be larger than needed (due to unknown elements)
13697 for (list = setup_file_list; list != NULL; list = list->next)
13700 // add space for up to 3 more elements for padding that may be needed
13701 *num_elements += 3;
13703 // free memory for old list of elements, if needed
13704 checked_free(*elements);
13706 // allocate memory for new list of elements
13707 *elements = checked_malloc(*num_elements * sizeof(int));
13710 for (list = setup_file_list; list != NULL; list = list->next)
13712 char *value = getHashEntry(element_hash, list->token);
13714 if (value == NULL) // try to find obsolete token mapping
13716 char *mapped_token = get_mapped_token(list->token);
13718 if (mapped_token != NULL)
13720 value = getHashEntry(element_hash, mapped_token);
13722 free(mapped_token);
13728 (*elements)[(*num_elements)++] = atoi(value);
13732 if (num_unknown_tokens == 0)
13735 Warn("unknown token(s) found in config file:");
13736 Warn("- config file: '%s'", filename);
13738 num_unknown_tokens++;
13741 Warn("- token: '%s'", list->token);
13745 if (num_unknown_tokens > 0)
13748 while (*num_elements % 4) // pad with empty elements, if needed
13749 (*elements)[(*num_elements)++] = EL_EMPTY;
13751 freeSetupFileList(setup_file_list);
13752 freeSetupFileHash(element_hash);
13755 for (i = 0; i < *num_elements; i++)
13756 Debug("editor", "element '%s' [%d]\n",
13757 element_info[(*elements)[i]].token_name, (*elements)[i]);
13761 static struct MusicFileInfo *get_music_file_info_ext(char *basename, int music,
13764 SetupFileHash *setup_file_hash = NULL;
13765 struct MusicFileInfo tmp_music_file_info, *new_music_file_info;
13766 char *filename_music, *filename_prefix, *filename_info;
13772 token_to_value_ptr[] =
13774 { "title_header", &tmp_music_file_info.title_header },
13775 { "artist_header", &tmp_music_file_info.artist_header },
13776 { "album_header", &tmp_music_file_info.album_header },
13777 { "year_header", &tmp_music_file_info.year_header },
13778 { "played_header", &tmp_music_file_info.played_header },
13780 { "title", &tmp_music_file_info.title },
13781 { "artist", &tmp_music_file_info.artist },
13782 { "album", &tmp_music_file_info.album },
13783 { "year", &tmp_music_file_info.year },
13784 { "played", &tmp_music_file_info.played },
13790 filename_music = (is_sound ? getCustomSoundFilename(basename) :
13791 getCustomMusicFilename(basename));
13793 if (filename_music == NULL)
13796 // ---------- try to replace file extension ----------
13798 filename_prefix = getStringCopy(filename_music);
13799 if (strrchr(filename_prefix, '.') != NULL)
13800 *strrchr(filename_prefix, '.') = '\0';
13801 filename_info = getStringCat2(filename_prefix, ".txt");
13803 if (fileExists(filename_info))
13804 setup_file_hash = loadSetupFileHash(filename_info);
13806 free(filename_prefix);
13807 free(filename_info);
13809 if (setup_file_hash == NULL)
13811 // ---------- try to add file extension ----------
13813 filename_prefix = getStringCopy(filename_music);
13814 filename_info = getStringCat2(filename_prefix, ".txt");
13816 if (fileExists(filename_info))
13817 setup_file_hash = loadSetupFileHash(filename_info);
13819 free(filename_prefix);
13820 free(filename_info);
13823 if (setup_file_hash == NULL)
13826 // ---------- music file info found ----------
13828 clear_mem(&tmp_music_file_info, sizeof(struct MusicFileInfo));
13830 for (i = 0; token_to_value_ptr[i].token != NULL; i++)
13832 char *value = getHashEntry(setup_file_hash, token_to_value_ptr[i].token);
13834 *token_to_value_ptr[i].value_ptr =
13835 getStringCopy(value != NULL && *value != '\0' ? value : UNKNOWN_NAME);
13838 tmp_music_file_info.basename = getStringCopy(basename);
13839 tmp_music_file_info.music = music;
13840 tmp_music_file_info.is_sound = is_sound;
13842 new_music_file_info = checked_malloc(sizeof(struct MusicFileInfo));
13843 *new_music_file_info = tmp_music_file_info;
13845 return new_music_file_info;
13848 static struct MusicFileInfo *get_music_file_info(char *basename, int music)
13850 return get_music_file_info_ext(basename, music, FALSE);
13853 static struct MusicFileInfo *get_sound_file_info(char *basename, int sound)
13855 return get_music_file_info_ext(basename, sound, TRUE);
13858 static boolean music_info_listed_ext(struct MusicFileInfo *list,
13859 char *basename, boolean is_sound)
13861 for (; list != NULL; list = list->next)
13862 if (list->is_sound == is_sound && strEqual(list->basename, basename))
13868 static boolean music_info_listed(struct MusicFileInfo *list, char *basename)
13870 return music_info_listed_ext(list, basename, FALSE);
13873 static boolean sound_info_listed(struct MusicFileInfo *list, char *basename)
13875 return music_info_listed_ext(list, basename, TRUE);
13878 void LoadMusicInfo(void)
13880 int num_music_noconf = getMusicListSize_NoConf();
13881 int num_music = getMusicListSize();
13882 int num_sounds = getSoundListSize();
13883 struct FileInfo *music, *sound;
13884 struct MusicFileInfo *next, **new;
13888 while (music_file_info != NULL)
13890 next = music_file_info->next;
13892 checked_free(music_file_info->basename);
13894 checked_free(music_file_info->title_header);
13895 checked_free(music_file_info->artist_header);
13896 checked_free(music_file_info->album_header);
13897 checked_free(music_file_info->year_header);
13898 checked_free(music_file_info->played_header);
13900 checked_free(music_file_info->title);
13901 checked_free(music_file_info->artist);
13902 checked_free(music_file_info->album);
13903 checked_free(music_file_info->year);
13904 checked_free(music_file_info->played);
13906 free(music_file_info);
13908 music_file_info = next;
13911 new = &music_file_info;
13913 // get (configured or unconfigured) music file info for all levels
13914 for (i = leveldir_current->first_level;
13915 i <= leveldir_current->last_level; i++)
13919 if (levelset.music[i] != MUS_UNDEFINED)
13921 // get music file info for configured level music
13922 music_nr = levelset.music[i];
13924 else if (num_music_noconf > 0)
13926 // get music file info for unconfigured level music
13927 int level_pos = i - leveldir_current->first_level;
13929 music_nr = MAP_NOCONF_MUSIC(level_pos % num_music_noconf);
13936 char *basename = getMusicInfoEntryFilename(music_nr);
13938 if (basename == NULL)
13941 if (!music_info_listed(music_file_info, basename))
13943 *new = get_music_file_info(basename, music_nr);
13946 new = &(*new)->next;
13950 // get music file info for all remaining configured music files
13951 for (i = 0; i < num_music; i++)
13953 music = getMusicListEntry(i);
13955 if (music->filename == NULL)
13958 if (strEqual(music->filename, UNDEFINED_FILENAME))
13961 // a configured file may be not recognized as music
13962 if (!FileIsMusic(music->filename))
13965 if (!music_info_listed(music_file_info, music->filename))
13967 *new = get_music_file_info(music->filename, i);
13970 new = &(*new)->next;
13974 // get sound file info for all configured sound files
13975 for (i = 0; i < num_sounds; i++)
13977 sound = getSoundListEntry(i);
13979 if (sound->filename == NULL)
13982 if (strEqual(sound->filename, UNDEFINED_FILENAME))
13985 // a configured file may be not recognized as sound
13986 if (!FileIsSound(sound->filename))
13989 if (!sound_info_listed(music_file_info, sound->filename))
13991 *new = get_sound_file_info(sound->filename, i);
13993 new = &(*new)->next;
13997 // add pointers to previous list nodes
13999 struct MusicFileInfo *node = music_file_info;
14001 while (node != NULL)
14004 node->next->prev = node;
14010 static void add_helpanim_entry(int element, int action, int direction,
14011 int delay, int *num_list_entries)
14013 struct HelpAnimInfo *new_list_entry;
14014 (*num_list_entries)++;
14017 checked_realloc(helpanim_info,
14018 *num_list_entries * sizeof(struct HelpAnimInfo));
14019 new_list_entry = &helpanim_info[*num_list_entries - 1];
14021 new_list_entry->element = element;
14022 new_list_entry->action = action;
14023 new_list_entry->direction = direction;
14024 new_list_entry->delay = delay;
14027 static void print_unknown_token(char *filename, char *token, int token_nr)
14032 Warn("unknown token(s) found in config file:");
14033 Warn("- config file: '%s'", filename);
14036 Warn("- token: '%s'", token);
14039 static void print_unknown_token_end(int token_nr)
14045 void LoadHelpAnimInfo(void)
14047 char *filename = getHelpAnimFilename();
14048 SetupFileList *setup_file_list = NULL, *list;
14049 SetupFileHash *element_hash, *action_hash, *direction_hash;
14050 int num_list_entries = 0;
14051 int num_unknown_tokens = 0;
14054 if (fileExists(filename))
14055 setup_file_list = loadSetupFileList(filename);
14057 if (setup_file_list == NULL)
14059 // use reliable default values from static configuration
14060 SetupFileList *insert_ptr;
14062 insert_ptr = setup_file_list =
14063 newSetupFileList(helpanim_config[0].token,
14064 helpanim_config[0].value);
14066 for (i = 1; helpanim_config[i].token; i++)
14067 insert_ptr = addListEntry(insert_ptr,
14068 helpanim_config[i].token,
14069 helpanim_config[i].value);
14072 element_hash = newSetupFileHash();
14073 action_hash = newSetupFileHash();
14074 direction_hash = newSetupFileHash();
14076 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
14077 setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
14079 for (i = 0; i < NUM_ACTIONS; i++)
14080 setHashEntry(action_hash, element_action_info[i].suffix,
14081 i_to_a(element_action_info[i].value));
14083 // do not store direction index (bit) here, but direction value!
14084 for (i = 0; i < NUM_DIRECTIONS_FULL; i++)
14085 setHashEntry(direction_hash, element_direction_info[i].suffix,
14086 i_to_a(1 << element_direction_info[i].value));
14088 for (list = setup_file_list; list != NULL; list = list->next)
14090 char *element_token, *action_token, *direction_token;
14091 char *element_value, *action_value, *direction_value;
14092 int delay = atoi(list->value);
14094 if (strEqual(list->token, "end"))
14096 add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
14101 /* first try to break element into element/action/direction parts;
14102 if this does not work, also accept combined "element[.act][.dir]"
14103 elements (like "dynamite.active"), which are unique elements */
14105 if (strchr(list->token, '.') == NULL) // token contains no '.'
14107 element_value = getHashEntry(element_hash, list->token);
14108 if (element_value != NULL) // element found
14109 add_helpanim_entry(atoi(element_value), -1, -1, delay,
14110 &num_list_entries);
14113 // no further suffixes found -- this is not an element
14114 print_unknown_token(filename, list->token, num_unknown_tokens++);
14120 // token has format "<prefix>.<something>"
14122 action_token = strchr(list->token, '.'); // suffix may be action ...
14123 direction_token = action_token; // ... or direction
14125 element_token = getStringCopy(list->token);
14126 *strchr(element_token, '.') = '\0';
14128 element_value = getHashEntry(element_hash, element_token);
14130 if (element_value == NULL) // this is no element
14132 element_value = getHashEntry(element_hash, list->token);
14133 if (element_value != NULL) // combined element found
14134 add_helpanim_entry(atoi(element_value), -1, -1, delay,
14135 &num_list_entries);
14137 print_unknown_token(filename, list->token, num_unknown_tokens++);
14139 free(element_token);
14144 action_value = getHashEntry(action_hash, action_token);
14146 if (action_value != NULL) // action found
14148 add_helpanim_entry(atoi(element_value), atoi(action_value), -1, delay,
14149 &num_list_entries);
14151 free(element_token);
14156 direction_value = getHashEntry(direction_hash, direction_token);
14158 if (direction_value != NULL) // direction found
14160 add_helpanim_entry(atoi(element_value), -1, atoi(direction_value), delay,
14161 &num_list_entries);
14163 free(element_token);
14168 if (strchr(action_token + 1, '.') == NULL)
14170 // no further suffixes found -- this is not an action nor direction
14172 element_value = getHashEntry(element_hash, list->token);
14173 if (element_value != NULL) // combined element found
14174 add_helpanim_entry(atoi(element_value), -1, -1, delay,
14175 &num_list_entries);
14177 print_unknown_token(filename, list->token, num_unknown_tokens++);
14179 free(element_token);
14184 // token has format "<prefix>.<suffix>.<something>"
14186 direction_token = strchr(action_token + 1, '.');
14188 action_token = getStringCopy(action_token);
14189 *strchr(action_token + 1, '.') = '\0';
14191 action_value = getHashEntry(action_hash, action_token);
14193 if (action_value == NULL) // this is no action
14195 element_value = getHashEntry(element_hash, list->token);
14196 if (element_value != NULL) // combined element found
14197 add_helpanim_entry(atoi(element_value), -1, -1, delay,
14198 &num_list_entries);
14200 print_unknown_token(filename, list->token, num_unknown_tokens++);
14202 free(element_token);
14203 free(action_token);
14208 direction_value = getHashEntry(direction_hash, direction_token);
14210 if (direction_value != NULL) // direction found
14212 add_helpanim_entry(atoi(element_value), atoi(action_value),
14213 atoi(direction_value), delay, &num_list_entries);
14215 free(element_token);
14216 free(action_token);
14221 // this is no direction
14223 element_value = getHashEntry(element_hash, list->token);
14224 if (element_value != NULL) // combined element found
14225 add_helpanim_entry(atoi(element_value), -1, -1, delay,
14226 &num_list_entries);
14228 print_unknown_token(filename, list->token, num_unknown_tokens++);
14230 free(element_token);
14231 free(action_token);
14234 print_unknown_token_end(num_unknown_tokens);
14236 add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
14237 add_helpanim_entry(HELPANIM_LIST_END, -1, -1, -1, &num_list_entries);
14239 freeSetupFileList(setup_file_list);
14240 freeSetupFileHash(element_hash);
14241 freeSetupFileHash(action_hash);
14242 freeSetupFileHash(direction_hash);
14245 for (i = 0; i < num_list_entries; i++)
14246 Debug("files:LoadHelpAnimInfo", "'%s': %d, %d, %d => %d",
14247 EL_NAME(helpanim_info[i].element),
14248 helpanim_info[i].element,
14249 helpanim_info[i].action,
14250 helpanim_info[i].direction,
14251 helpanim_info[i].delay);
14255 void LoadHelpTextInfo(void)
14257 char *filename = getHelpTextFilename();
14260 if (helptext_info != NULL)
14262 freeSetupFileHash(helptext_info);
14263 helptext_info = NULL;
14266 if (fileExists(filename))
14267 helptext_info = loadSetupFileHash(filename);
14269 if (helptext_info == NULL)
14271 // use reliable default values from static configuration
14272 helptext_info = newSetupFileHash();
14274 for (i = 0; helptext_config[i].token; i++)
14275 setHashEntry(helptext_info,
14276 helptext_config[i].token,
14277 helptext_config[i].value);
14281 BEGIN_HASH_ITERATION(helptext_info, itr)
14283 Debug("files:LoadHelpTextInfo", "'%s' => '%s'",
14284 HASH_ITERATION_TOKEN(itr), HASH_ITERATION_VALUE(itr));
14286 END_HASH_ITERATION(hash, itr)
14291 // ----------------------------------------------------------------------------
14293 // ----------------------------------------------------------------------------
14295 #define MAX_NUM_CONVERT_LEVELS 1000
14297 void ConvertLevels(void)
14299 static LevelDirTree *convert_leveldir = NULL;
14300 static int convert_level_nr = -1;
14301 static int num_levels_handled = 0;
14302 static int num_levels_converted = 0;
14303 static boolean levels_failed[MAX_NUM_CONVERT_LEVELS];
14306 convert_leveldir = getTreeInfoFromIdentifier(leveldir_first,
14307 global.convert_leveldir);
14309 if (convert_leveldir == NULL)
14310 Fail("no such level identifier: '%s'", global.convert_leveldir);
14312 leveldir_current = convert_leveldir;
14314 if (global.convert_level_nr != -1)
14316 convert_leveldir->first_level = global.convert_level_nr;
14317 convert_leveldir->last_level = global.convert_level_nr;
14320 convert_level_nr = convert_leveldir->first_level;
14322 PrintLine("=", 79);
14323 Print("Converting levels\n");
14324 PrintLine("-", 79);
14325 Print("Level series identifier: '%s'\n", convert_leveldir->identifier);
14326 Print("Level series name: '%s'\n", convert_leveldir->name);
14327 Print("Level series author: '%s'\n", convert_leveldir->author);
14328 Print("Number of levels: %d\n", convert_leveldir->levels);
14329 PrintLine("=", 79);
14332 for (i = 0; i < MAX_NUM_CONVERT_LEVELS; i++)
14333 levels_failed[i] = FALSE;
14335 while (convert_level_nr <= convert_leveldir->last_level)
14337 char *level_filename;
14340 level_nr = convert_level_nr++;
14342 Print("Level %03d: ", level_nr);
14344 LoadLevel(level_nr);
14345 if (level.no_level_file || level.no_valid_file)
14347 Print("(no level)\n");
14351 Print("converting level ... ");
14354 // special case: conversion of some EMC levels as requested by ACME
14355 level.game_engine_type = GAME_ENGINE_TYPE_RND;
14358 level_filename = getDefaultLevelFilename(level_nr);
14359 new_level = !fileExists(level_filename);
14363 SaveLevel(level_nr);
14365 num_levels_converted++;
14367 Print("converted.\n");
14371 if (level_nr >= 0 && level_nr < MAX_NUM_CONVERT_LEVELS)
14372 levels_failed[level_nr] = TRUE;
14374 Print("NOT CONVERTED -- LEVEL ALREADY EXISTS.\n");
14377 num_levels_handled++;
14381 PrintLine("=", 79);
14382 Print("Number of levels handled: %d\n", num_levels_handled);
14383 Print("Number of levels converted: %d (%d%%)\n", num_levels_converted,
14384 (num_levels_handled ?
14385 num_levels_converted * 100 / num_levels_handled : 0));
14386 PrintLine("-", 79);
14387 Print("Summary (for automatic parsing by scripts):\n");
14388 Print("LEVELDIR '%s', CONVERTED %d/%d (%d%%)",
14389 convert_leveldir->identifier, num_levels_converted,
14390 num_levels_handled,
14391 (num_levels_handled ?
14392 num_levels_converted * 100 / num_levels_handled : 0));
14394 if (num_levels_handled != num_levels_converted)
14396 Print(", FAILED:");
14397 for (i = 0; i < MAX_NUM_CONVERT_LEVELS; i++)
14398 if (levels_failed[i])
14403 PrintLine("=", 79);
14405 CloseAllAndExit(0);
14409 // ----------------------------------------------------------------------------
14410 // create and save images for use in level sketches (raw BMP format)
14411 // ----------------------------------------------------------------------------
14413 void CreateLevelSketchImages(void)
14419 InitElementPropertiesGfxElement();
14421 bitmap1 = CreateBitmap(TILEX, TILEY, DEFAULT_DEPTH);
14422 bitmap2 = CreateBitmap(MINI_TILEX, MINI_TILEY, DEFAULT_DEPTH);
14424 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
14426 int element = getMappedElement(i);
14427 char basename1[16];
14428 char basename2[16];
14432 sprintf(basename1, "%04d.bmp", i);
14433 sprintf(basename2, "%04ds.bmp", i);
14435 filename1 = getPath2(global.create_sketch_images_dir, basename1);
14436 filename2 = getPath2(global.create_sketch_images_dir, basename2);
14438 DrawSizedElement(0, 0, element, TILESIZE);
14439 BlitBitmap(drawto, bitmap1, SX, SY, TILEX, TILEY, 0, 0);
14441 if (SDL_SaveBMP(bitmap1->surface, filename1) != 0)
14442 Fail("cannot save level sketch image file '%s'", filename1);
14444 DrawSizedElement(0, 0, element, MINI_TILESIZE);
14445 BlitBitmap(drawto, bitmap2, SX, SY, MINI_TILEX, MINI_TILEY, 0, 0);
14447 if (SDL_SaveBMP(bitmap2->surface, filename2) != 0)
14448 Fail("cannot save level sketch image file '%s'", filename2);
14453 // create corresponding SQL statements (for normal and small images)
14456 printf("insert into phpbb_words values (NULL, '`%03d', '<IMG class=\"levelsketch\" src=\"/I/%04d.png\"/>');\n", i, i);
14457 printf("insert into phpbb_words values (NULL, '¸%03d', '<IMG class=\"levelsketch\" src=\"/I/%04ds.png\"/>');\n", i, i);
14460 printf("insert into phpbb_words values (NULL, '`%04d', '<IMG class=\"levelsketch\" src=\"/I/%04d.png\"/>');\n", i, i);
14461 printf("insert into phpbb_words values (NULL, '¸%04d', '<IMG class=\"levelsketch\" src=\"/I/%04ds.png\"/>');\n", i, i);
14463 // optional: create content for forum level sketch demonstration post
14465 fprintf(stderr, "%03d `%03d%c", i, i, (i % 10 < 9 ? ' ' : '\n'));
14468 FreeBitmap(bitmap1);
14469 FreeBitmap(bitmap2);
14472 fprintf(stderr, "\n");
14474 Info("%d normal and small images created", NUM_FILE_ELEMENTS);
14476 CloseAllAndExit(0);
14480 // ----------------------------------------------------------------------------
14481 // create and save images for element collecting animations (raw BMP format)
14482 // ----------------------------------------------------------------------------
14484 static boolean createCollectImage(int element)
14486 return (IS_COLLECTIBLE(element) && !IS_SP_ELEMENT(element));
14489 void CreateCollectElementImages(void)
14493 int anim_frames = num_steps - 1;
14494 int tile_size = TILESIZE;
14495 int anim_width = tile_size * anim_frames;
14496 int anim_height = tile_size;
14497 int num_collect_images = 0;
14498 int pos_collect_images = 0;
14500 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
14501 if (createCollectImage(i))
14502 num_collect_images++;
14504 Info("Creating %d element collecting animation images ...",
14505 num_collect_images);
14507 int dst_width = anim_width * 2;
14508 int dst_height = anim_height * num_collect_images / 2;
14509 Bitmap *dst_bitmap = CreateBitmap(dst_width, dst_height, DEFAULT_DEPTH);
14510 char *basename_bmp = "RocksCollect.bmp";
14511 char *basename_png = "RocksCollect.png";
14512 char *filename_bmp = getPath2(global.create_collect_images_dir, basename_bmp);
14513 char *filename_png = getPath2(global.create_collect_images_dir, basename_png);
14514 int len_filename_bmp = strlen(filename_bmp);
14515 int len_filename_png = strlen(filename_png);
14516 int max_command_len = MAX_FILENAME_LEN + len_filename_bmp + len_filename_png;
14517 char cmd_convert[max_command_len];
14519 snprintf(cmd_convert, max_command_len, "convert \"%s\" \"%s\"",
14523 // force using RGBA surface for destination bitmap
14524 SDL_SetColorKey(dst_bitmap->surface, SET_TRANSPARENT_PIXEL,
14525 SDL_MapRGB(dst_bitmap->surface->format, 0x00, 0x00, 0x00));
14527 dst_bitmap->surface =
14528 SDL_ConvertSurfaceFormat(dst_bitmap->surface, SDL_PIXELFORMAT_ARGB8888, 0);
14530 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
14532 if (!createCollectImage(i))
14535 int dst_x = (pos_collect_images / (num_collect_images / 2)) * anim_width;
14536 int dst_y = (pos_collect_images % (num_collect_images / 2)) * anim_height;
14537 int graphic = el2img(i);
14538 char *token_name = element_info[i].token_name;
14539 Bitmap *tmp_bitmap = CreateBitmap(tile_size, tile_size, DEFAULT_DEPTH);
14540 Bitmap *src_bitmap;
14543 Info("- creating collecting image for '%s' ...", token_name);
14545 getGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
14547 BlitBitmap(src_bitmap, tmp_bitmap, src_x, src_y,
14548 tile_size, tile_size, 0, 0);
14550 // force using RGBA surface for temporary bitmap (using transparent black)
14551 SDL_SetColorKey(tmp_bitmap->surface, SET_TRANSPARENT_PIXEL,
14552 SDL_MapRGB(tmp_bitmap->surface->format, 0x00, 0x00, 0x00));
14554 tmp_bitmap->surface =
14555 SDL_ConvertSurfaceFormat(tmp_bitmap->surface, SDL_PIXELFORMAT_ARGB8888, 0);
14557 tmp_bitmap->surface_masked = tmp_bitmap->surface;
14559 for (j = 0; j < anim_frames; j++)
14561 int frame_size_final = tile_size * (anim_frames - j) / num_steps;
14562 int frame_size = frame_size_final * num_steps;
14563 int offset = (tile_size - frame_size_final) / 2;
14564 Bitmap *frame_bitmap = ZoomBitmap(tmp_bitmap, frame_size, frame_size);
14566 while (frame_size > frame_size_final)
14570 Bitmap *half_bitmap = ZoomBitmap(frame_bitmap, frame_size, frame_size);
14572 FreeBitmap(frame_bitmap);
14574 frame_bitmap = half_bitmap;
14577 BlitBitmapMasked(frame_bitmap, dst_bitmap, 0, 0,
14578 frame_size_final, frame_size_final,
14579 dst_x + j * tile_size + offset, dst_y + offset);
14581 FreeBitmap(frame_bitmap);
14584 tmp_bitmap->surface_masked = NULL;
14586 FreeBitmap(tmp_bitmap);
14588 pos_collect_images++;
14591 if (SDL_SaveBMP(dst_bitmap->surface, filename_bmp) != 0)
14592 Fail("cannot save element collecting image file '%s'", filename_bmp);
14594 FreeBitmap(dst_bitmap);
14596 Info("Converting image file from BMP to PNG ...");
14598 if (system(cmd_convert) != 0)
14599 Fail("converting image file failed");
14601 unlink(filename_bmp);
14605 CloseAllAndExit(0);
14609 // ----------------------------------------------------------------------------
14610 // create and save images for custom and group elements (raw BMP format)
14611 // ----------------------------------------------------------------------------
14613 void CreateCustomElementImages(char *directory)
14615 char *src_basename = "RocksCE-template.ilbm";
14616 char *dst_basename = "RocksCE.bmp";
14617 char *src_filename = getPath2(directory, src_basename);
14618 char *dst_filename = getPath2(directory, dst_basename);
14619 Bitmap *src_bitmap;
14621 int yoffset_ce = 0;
14622 int yoffset_ge = (TILEY * NUM_CUSTOM_ELEMENTS / 16);
14625 InitVideoDefaults();
14627 ReCreateBitmap(&backbuffer, video.width, video.height);
14629 src_bitmap = LoadImage(src_filename);
14631 bitmap = CreateBitmap(TILEX * 16 * 2,
14632 TILEY * (NUM_CUSTOM_ELEMENTS + NUM_GROUP_ELEMENTS) / 16,
14635 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
14642 BlitBitmap(src_bitmap, bitmap, 0, 0, TILEX, TILEY,
14643 TILEX * x, TILEY * y + yoffset_ce);
14645 BlitBitmap(src_bitmap, bitmap, 0, TILEY,
14647 TILEX * x + TILEX * 16,
14648 TILEY * y + yoffset_ce);
14650 for (j = 2; j >= 0; j--)
14654 BlitBitmap(src_bitmap, bitmap,
14655 TILEX + c * 7, 0, 6, 10,
14656 TILEX * x + 6 + j * 7,
14657 TILEY * y + 11 + yoffset_ce);
14659 BlitBitmap(src_bitmap, bitmap,
14660 TILEX + c * 8, TILEY, 6, 10,
14661 TILEX * 16 + TILEX * x + 6 + j * 8,
14662 TILEY * y + 10 + yoffset_ce);
14668 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
14675 BlitBitmap(src_bitmap, bitmap, 0, 0, TILEX, TILEY,
14676 TILEX * x, TILEY * y + yoffset_ge);
14678 BlitBitmap(src_bitmap, bitmap, 0, TILEY,
14680 TILEX * x + TILEX * 16,
14681 TILEY * y + yoffset_ge);
14683 for (j = 1; j >= 0; j--)
14687 BlitBitmap(src_bitmap, bitmap, TILEX + c * 10, 11, 10, 10,
14688 TILEX * x + 6 + j * 10,
14689 TILEY * y + 11 + yoffset_ge);
14691 BlitBitmap(src_bitmap, bitmap,
14692 TILEX + c * 8, TILEY + 12, 6, 10,
14693 TILEX * 16 + TILEX * x + 10 + j * 8,
14694 TILEY * y + 10 + yoffset_ge);
14700 if (SDL_SaveBMP(bitmap->surface, dst_filename) != 0)
14701 Fail("cannot save CE graphics file '%s'", dst_filename);
14703 FreeBitmap(bitmap);
14705 CloseAllAndExit(0);