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
321 static struct LevelFileConfigInfo chunk_config_ELEM[] =
323 // (these values are the same for each player)
326 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
327 &li.block_last_field, FALSE // default case for EM levels
331 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
332 &li.sp_block_last_field, TRUE // default case for SP levels
336 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
337 &li.instant_relocation, FALSE
341 TYPE_BOOLEAN, CONF_VALUE_8_BIT(4),
342 &li.can_pass_to_walkable, FALSE
346 TYPE_BOOLEAN, CONF_VALUE_8_BIT(5),
347 &li.block_snap_field, TRUE
351 TYPE_BOOLEAN, CONF_VALUE_8_BIT(6),
352 &li.continuous_snapping, TRUE
356 TYPE_BOOLEAN, CONF_VALUE_8_BIT(12),
357 &li.shifted_relocation, FALSE
361 TYPE_BOOLEAN, CONF_VALUE_8_BIT(15),
362 &li.lazy_relocation, FALSE
366 TYPE_BOOLEAN, CONF_VALUE_8_BIT(16),
367 &li.finish_dig_collect, TRUE
371 TYPE_BOOLEAN, CONF_VALUE_8_BIT(17),
372 &li.keep_walkable_ce, FALSE
375 // (these values are different for each player)
378 TYPE_INTEGER, CONF_VALUE_8_BIT(7),
379 &li.initial_player_stepsize[0], STEPSIZE_NORMAL
383 TYPE_BOOLEAN, CONF_VALUE_8_BIT(8),
384 &li.initial_player_gravity[0], FALSE
388 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
389 &li.use_start_element[0], FALSE
393 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
394 &li.start_element[0], EL_PLAYER_1
398 TYPE_BOOLEAN, CONF_VALUE_8_BIT(10),
399 &li.use_artwork_element[0], FALSE
403 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
404 &li.artwork_element[0], EL_PLAYER_1
408 TYPE_BOOLEAN, CONF_VALUE_8_BIT(11),
409 &li.use_explosion_element[0], FALSE
413 TYPE_ELEMENT, CONF_VALUE_16_BIT(3),
414 &li.explosion_element[0], EL_PLAYER_1
418 TYPE_BOOLEAN, CONF_VALUE_8_BIT(13),
419 &li.use_initial_inventory[0], FALSE
423 TYPE_BOOLEAN, CONF_VALUE_8_BIT(14),
424 &li.initial_inventory_size[0], 1
428 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(1),
429 &li.initial_inventory_content[0][0],EL_EMPTY, NULL,
430 &li.initial_inventory_size[0], 1, MAX_INITIAL_INVENTORY_SIZE
435 TYPE_INTEGER, CONF_VALUE_8_BIT(7),
436 &li.initial_player_stepsize[1], STEPSIZE_NORMAL
440 TYPE_BOOLEAN, CONF_VALUE_8_BIT(8),
441 &li.initial_player_gravity[1], FALSE
445 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
446 &li.use_start_element[1], FALSE
450 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
451 &li.start_element[1], EL_PLAYER_2
455 TYPE_BOOLEAN, CONF_VALUE_8_BIT(10),
456 &li.use_artwork_element[1], FALSE
460 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
461 &li.artwork_element[1], EL_PLAYER_2
465 TYPE_BOOLEAN, CONF_VALUE_8_BIT(11),
466 &li.use_explosion_element[1], FALSE
470 TYPE_ELEMENT, CONF_VALUE_16_BIT(3),
471 &li.explosion_element[1], EL_PLAYER_2
475 TYPE_BOOLEAN, CONF_VALUE_8_BIT(13),
476 &li.use_initial_inventory[1], FALSE
480 TYPE_BOOLEAN, CONF_VALUE_8_BIT(14),
481 &li.initial_inventory_size[1], 1
485 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(1),
486 &li.initial_inventory_content[1][0],EL_EMPTY, NULL,
487 &li.initial_inventory_size[1], 1, MAX_INITIAL_INVENTORY_SIZE
492 TYPE_INTEGER, CONF_VALUE_8_BIT(7),
493 &li.initial_player_stepsize[2], STEPSIZE_NORMAL
497 TYPE_BOOLEAN, CONF_VALUE_8_BIT(8),
498 &li.initial_player_gravity[2], FALSE
502 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
503 &li.use_start_element[2], FALSE
507 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
508 &li.start_element[2], EL_PLAYER_3
512 TYPE_BOOLEAN, CONF_VALUE_8_BIT(10),
513 &li.use_artwork_element[2], FALSE
517 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
518 &li.artwork_element[2], EL_PLAYER_3
522 TYPE_BOOLEAN, CONF_VALUE_8_BIT(11),
523 &li.use_explosion_element[2], FALSE
527 TYPE_ELEMENT, CONF_VALUE_16_BIT(3),
528 &li.explosion_element[2], EL_PLAYER_3
532 TYPE_BOOLEAN, CONF_VALUE_8_BIT(13),
533 &li.use_initial_inventory[2], FALSE
537 TYPE_BOOLEAN, CONF_VALUE_8_BIT(14),
538 &li.initial_inventory_size[2], 1
542 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(1),
543 &li.initial_inventory_content[2][0],EL_EMPTY, NULL,
544 &li.initial_inventory_size[2], 1, MAX_INITIAL_INVENTORY_SIZE
549 TYPE_INTEGER, CONF_VALUE_8_BIT(7),
550 &li.initial_player_stepsize[3], STEPSIZE_NORMAL
554 TYPE_BOOLEAN, CONF_VALUE_8_BIT(8),
555 &li.initial_player_gravity[3], FALSE
559 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
560 &li.use_start_element[3], FALSE
564 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
565 &li.start_element[3], EL_PLAYER_4
569 TYPE_BOOLEAN, CONF_VALUE_8_BIT(10),
570 &li.use_artwork_element[3], FALSE
574 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
575 &li.artwork_element[3], EL_PLAYER_4
579 TYPE_BOOLEAN, CONF_VALUE_8_BIT(11),
580 &li.use_explosion_element[3], FALSE
584 TYPE_ELEMENT, CONF_VALUE_16_BIT(3),
585 &li.explosion_element[3], EL_PLAYER_4
589 TYPE_BOOLEAN, CONF_VALUE_8_BIT(13),
590 &li.use_initial_inventory[3], FALSE
594 TYPE_BOOLEAN, CONF_VALUE_8_BIT(14),
595 &li.initial_inventory_size[3], 1
599 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(1),
600 &li.initial_inventory_content[3][0],EL_EMPTY, NULL,
601 &li.initial_inventory_size[3], 1, MAX_INITIAL_INVENTORY_SIZE
604 // (these values are only valid for BD style levels)
605 // (some values for BD style amoeba following below)
608 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
609 &li.bd_diagonal_movements, FALSE
613 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
614 &li.bd_topmost_player_active, TRUE
618 TYPE_INTEGER, CONF_VALUE_8_BIT(3),
619 &li.bd_pushing_prob, 25
623 TYPE_INTEGER, CONF_VALUE_8_BIT(4),
624 &li.bd_pushing_prob_with_sweet, 100
628 TYPE_BOOLEAN, CONF_VALUE_8_BIT(5),
629 &li.bd_push_mega_rock_with_sweet, FALSE
633 TYPE_INTEGER, CONF_VALUE_8_BIT(6),
634 &li.bd_snap_element, EL_EMPTY
639 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
640 &li.score[SC_DIAMOND_EXTRA], 20
644 EL_BD_MAGIC_WALL, -1,
645 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
646 &li.bd_magic_wall_wait_hatching, FALSE
649 EL_BD_MAGIC_WALL, -1,
650 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
651 &li.bd_magic_wall_stops_amoeba, TRUE
656 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
657 &li.bd_clock_extra_time, 30
661 EL_BD_VOODOO_DOLL, -1,
662 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
663 &li.bd_voodoo_collects_diamonds, FALSE
666 EL_BD_VOODOO_DOLL, -1,
667 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
668 &li.bd_voodoo_hurt_kills_player, FALSE
671 EL_BD_VOODOO_DOLL, -1,
672 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
673 &li.bd_voodoo_dies_by_rock, FALSE
676 EL_BD_VOODOO_DOLL, -1,
677 TYPE_BOOLEAN, CONF_VALUE_8_BIT(4),
678 &li.bd_voodoo_vanish_by_explosion, TRUE
681 EL_BD_VOODOO_DOLL, -1,
682 TYPE_INTEGER, CONF_VALUE_8_BIT(5),
683 &li.bd_voodoo_penalty_time, 30
687 // (the following values are related to various game elements)
691 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
692 &li.score[SC_EMERALD], 10
697 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
698 &li.score[SC_DIAMOND], 10
703 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
704 &li.score[SC_BUG], 10
709 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
710 &li.score[SC_SPACESHIP], 10
715 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
716 &li.score[SC_PACMAN], 10
721 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
722 &li.score[SC_NUT], 10
727 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
728 &li.score[SC_DYNAMITE], 10
733 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
734 &li.score[SC_KEY], 10
739 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
740 &li.score[SC_PEARL], 10
745 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
746 &li.score[SC_CRYSTAL], 10
749 // (amoeba values used by R'n'D game engine only)
752 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
753 &li.amoeba_content, EL_DIAMOND
757 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
762 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
763 &li.grow_into_diggable, TRUE
765 // (amoeba values used by BD game engine only)
768 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
769 &li.bd_amoeba_wait_for_hatching, FALSE
773 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
774 &li.bd_amoeba_start_immediately, TRUE
778 TYPE_BOOLEAN, CONF_VALUE_8_BIT(4),
779 &li.bd_amoeba_2_explode_by_amoeba, TRUE
783 TYPE_INTEGER, CONF_VALUE_16_BIT(3),
784 &li.bd_amoeba_threshold_too_big, 200
788 TYPE_INTEGER, CONF_VALUE_16_BIT(4),
789 &li.bd_amoeba_slow_growth_time, 200
793 TYPE_INTEGER, CONF_VALUE_8_BIT(5),
794 &li.bd_amoeba_slow_growth_rate, 3
798 TYPE_INTEGER, CONF_VALUE_8_BIT(6),
799 &li.bd_amoeba_fast_growth_rate, 25
803 TYPE_ELEMENT, CONF_VALUE_16_BIT(5),
804 &li.bd_amoeba_content_too_big, EL_BD_ROCK
808 TYPE_ELEMENT, CONF_VALUE_16_BIT(6),
809 &li.bd_amoeba_content_enclosed, EL_BD_DIAMOND
814 TYPE_INTEGER, CONF_VALUE_16_BIT(3),
815 &li.bd_amoeba_2_threshold_too_big, 200
819 TYPE_INTEGER, CONF_VALUE_16_BIT(4),
820 &li.bd_amoeba_2_slow_growth_time, 200
824 TYPE_INTEGER, CONF_VALUE_8_BIT(5),
825 &li.bd_amoeba_2_slow_growth_rate, 3
829 TYPE_INTEGER, CONF_VALUE_8_BIT(6),
830 &li.bd_amoeba_2_fast_growth_rate, 25
834 TYPE_ELEMENT, CONF_VALUE_16_BIT(5),
835 &li.bd_amoeba_2_content_too_big, EL_BD_ROCK
839 TYPE_ELEMENT, CONF_VALUE_16_BIT(6),
840 &li.bd_amoeba_2_content_enclosed, EL_BD_DIAMOND
844 TYPE_ELEMENT, CONF_VALUE_16_BIT(7),
845 &li.bd_amoeba_2_content_exploding, EL_EMPTY
849 TYPE_ELEMENT, CONF_VALUE_16_BIT(8),
850 &li.bd_amoeba_2_content_looks_like, EL_BD_AMOEBA_2
855 TYPE_CONTENT_LIST, CONF_VALUE_BYTES(1),
856 &li.yamyam_content, EL_ROCK, NULL,
857 &li.num_yamyam_contents, 4, MAX_ELEMENT_CONTENTS
861 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
862 &li.score[SC_YAMYAM], 10
867 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
868 &li.score[SC_ROBOT], 10
872 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
878 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
884 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
885 &li.time_magic_wall, 10
890 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
891 &li.game_of_life[0], 2
895 TYPE_INTEGER, CONF_VALUE_8_BIT(2),
896 &li.game_of_life[1], 3
900 TYPE_INTEGER, CONF_VALUE_8_BIT(3),
901 &li.game_of_life[2], 3
905 TYPE_INTEGER, CONF_VALUE_8_BIT(4),
906 &li.game_of_life[3], 3
910 TYPE_BOOLEAN, CONF_VALUE_8_BIT(5),
911 &li.use_life_bugs, FALSE
916 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
921 TYPE_INTEGER, CONF_VALUE_8_BIT(2),
926 TYPE_INTEGER, CONF_VALUE_8_BIT(3),
931 TYPE_INTEGER, CONF_VALUE_8_BIT(4),
936 EL_TIMEGATE_SWITCH, -1,
937 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
938 &li.time_timegate, 10
942 EL_LIGHT_SWITCH_ACTIVE, -1,
943 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
948 EL_SHIELD_NORMAL, -1,
949 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
950 &li.shield_normal_time, 10
953 EL_SHIELD_NORMAL, -1,
954 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
955 &li.score[SC_SHIELD], 10
959 EL_SHIELD_DEADLY, -1,
960 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
961 &li.shield_deadly_time, 10
964 EL_SHIELD_DEADLY, -1,
965 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
966 &li.score[SC_SHIELD], 10
971 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
976 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
977 &li.extra_time_score, 10
981 EL_TIME_ORB_FULL, -1,
982 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
983 &li.time_orb_time, 10
986 EL_TIME_ORB_FULL, -1,
987 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
988 &li.use_time_orb_bug, FALSE
993 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
994 &li.use_spring_bug, FALSE
999 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1000 &li.android_move_time, 10
1004 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
1005 &li.android_clone_time, 10
1008 EL_EMC_ANDROID, SAVE_CONF_NEVER,
1009 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(1),
1010 &li.android_clone_element[0], EL_EMPTY, NULL,
1011 &li.num_android_clone_elements, 1, MAX_ANDROID_ELEMENTS_OLD
1015 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(2),
1016 &li.android_clone_element[0], EL_EMPTY, NULL,
1017 &li.num_android_clone_elements, 1, MAX_ANDROID_ELEMENTS
1022 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1023 &li.lenses_score, 10
1027 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
1032 EL_EMC_MAGNIFIER, -1,
1033 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1034 &li.magnify_score, 10
1037 EL_EMC_MAGNIFIER, -1,
1038 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
1039 &li.magnify_time, 10
1043 EL_EMC_MAGIC_BALL, -1,
1044 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1048 EL_EMC_MAGIC_BALL, -1,
1049 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1050 &li.ball_random, FALSE
1053 EL_EMC_MAGIC_BALL, -1,
1054 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
1055 &li.ball_active_initial, FALSE
1058 EL_EMC_MAGIC_BALL, -1,
1059 TYPE_CONTENT_LIST, CONF_VALUE_BYTES(1),
1060 &li.ball_content, EL_EMPTY, NULL,
1061 &li.num_ball_contents, 4, MAX_ELEMENT_CONTENTS
1065 EL_SOKOBAN_FIELD_EMPTY, -1,
1066 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1067 &li.sb_fields_needed, TRUE
1071 EL_SOKOBAN_OBJECT, -1,
1072 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1073 &li.sb_objects_needed, TRUE
1078 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1079 &li.mm_laser_red, FALSE
1083 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
1084 &li.mm_laser_green, FALSE
1088 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
1089 &li.mm_laser_blue, TRUE
1094 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1095 &li.df_laser_red, TRUE
1099 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
1100 &li.df_laser_green, TRUE
1104 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
1105 &li.df_laser_blue, FALSE
1109 EL_MM_FUSE_ACTIVE, -1,
1110 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1111 &li.mm_time_fuse, 25
1115 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1116 &li.mm_time_bomb, 75
1120 EL_MM_GRAY_BALL, -1,
1121 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1122 &li.mm_time_ball, 75
1125 EL_MM_GRAY_BALL, -1,
1126 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
1127 &li.mm_ball_choice_mode, ANIM_RANDOM
1130 EL_MM_GRAY_BALL, -1,
1131 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(1),
1132 &li.mm_ball_content, EL_EMPTY, NULL,
1133 &li.num_mm_ball_contents, 8, MAX_MM_BALL_CONTENTS
1136 EL_MM_GRAY_BALL, -1,
1137 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
1138 &li.rotate_mm_ball_content, TRUE
1141 EL_MM_GRAY_BALL, -1,
1142 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
1143 &li.explode_mm_ball, FALSE
1147 EL_MM_STEEL_BLOCK, -1,
1148 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1149 &li.mm_time_block, 75
1152 EL_MM_LIGHTBALL, -1,
1153 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1154 &li.score[SC_ELEM_BONUS], 10
1164 static struct LevelFileConfigInfo chunk_config_NOTE[] =
1168 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
1169 &xx_envelope.xsize, MAX_ENVELOPE_XSIZE,
1173 TYPE_INTEGER, CONF_VALUE_8_BIT(2),
1174 &xx_envelope.ysize, MAX_ENVELOPE_YSIZE,
1179 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
1180 &xx_envelope.autowrap, FALSE
1184 TYPE_BOOLEAN, CONF_VALUE_8_BIT(4),
1185 &xx_envelope.centered, FALSE
1190 TYPE_STRING, CONF_VALUE_BYTES(1),
1191 &xx_envelope.text, -1, NULL,
1192 &xx_string_length_unused, -1, MAX_ENVELOPE_TEXT_LEN,
1193 &xx_default_string_empty[0]
1203 static struct LevelFileConfigInfo chunk_config_CUSX_base[] =
1207 TYPE_STRING, CONF_VALUE_BYTES(1),
1208 &xx_ei.description[0], -1,
1209 &yy_ei.description[0],
1210 &xx_string_length_unused, -1, MAX_ELEMENT_NAME_LEN,
1211 &xx_default_description[0]
1216 TYPE_BITFIELD, CONF_VALUE_32_BIT(1),
1217 &xx_ei.properties[EP_BITFIELD_BASE_NR], EP_BITMASK_BASE_DEFAULT,
1218 &yy_ei.properties[EP_BITFIELD_BASE_NR]
1220 #if ENABLE_RESERVED_CODE
1221 // (reserved for later use)
1224 TYPE_BITFIELD, CONF_VALUE_32_BIT(2),
1225 &xx_ei.properties[EP_BITFIELD_BASE_NR + 1], EP_BITMASK_DEFAULT,
1226 &yy_ei.properties[EP_BITFIELD_BASE_NR + 1]
1232 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1233 &xx_ei.use_gfx_element, FALSE,
1234 &yy_ei.use_gfx_element
1238 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
1239 &xx_ei.gfx_element_initial, EL_EMPTY_SPACE,
1240 &yy_ei.gfx_element_initial
1245 TYPE_BITFIELD, CONF_VALUE_8_BIT(2),
1246 &xx_ei.access_direction, MV_ALL_DIRECTIONS,
1247 &yy_ei.access_direction
1252 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
1253 &xx_ei.collect_score_initial, 10,
1254 &yy_ei.collect_score_initial
1258 TYPE_INTEGER, CONF_VALUE_16_BIT(3),
1259 &xx_ei.collect_count_initial, 1,
1260 &yy_ei.collect_count_initial
1265 TYPE_INTEGER, CONF_VALUE_16_BIT(4),
1266 &xx_ei.ce_value_fixed_initial, 0,
1267 &yy_ei.ce_value_fixed_initial
1271 TYPE_INTEGER, CONF_VALUE_16_BIT(5),
1272 &xx_ei.ce_value_random_initial, 0,
1273 &yy_ei.ce_value_random_initial
1277 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
1278 &xx_ei.use_last_ce_value, FALSE,
1279 &yy_ei.use_last_ce_value
1284 TYPE_INTEGER, CONF_VALUE_16_BIT(6),
1285 &xx_ei.push_delay_fixed, 8,
1286 &yy_ei.push_delay_fixed
1290 TYPE_INTEGER, CONF_VALUE_16_BIT(7),
1291 &xx_ei.push_delay_random, 8,
1292 &yy_ei.push_delay_random
1296 TYPE_INTEGER, CONF_VALUE_16_BIT(8),
1297 &xx_ei.drop_delay_fixed, 0,
1298 &yy_ei.drop_delay_fixed
1302 TYPE_INTEGER, CONF_VALUE_16_BIT(9),
1303 &xx_ei.drop_delay_random, 0,
1304 &yy_ei.drop_delay_random
1308 TYPE_INTEGER, CONF_VALUE_16_BIT(10),
1309 &xx_ei.move_delay_fixed, 0,
1310 &yy_ei.move_delay_fixed
1314 TYPE_INTEGER, CONF_VALUE_16_BIT(11),
1315 &xx_ei.move_delay_random, 0,
1316 &yy_ei.move_delay_random
1320 TYPE_INTEGER, CONF_VALUE_16_BIT(16),
1321 &xx_ei.step_delay_fixed, 0,
1322 &yy_ei.step_delay_fixed
1326 TYPE_INTEGER, CONF_VALUE_16_BIT(17),
1327 &xx_ei.step_delay_random, 0,
1328 &yy_ei.step_delay_random
1333 TYPE_BITFIELD, CONF_VALUE_32_BIT(3),
1334 &xx_ei.move_pattern, MV_ALL_DIRECTIONS,
1339 TYPE_BITFIELD, CONF_VALUE_8_BIT(4),
1340 &xx_ei.move_direction_initial, MV_START_AUTOMATIC,
1341 &yy_ei.move_direction_initial
1345 TYPE_INTEGER, CONF_VALUE_8_BIT(5),
1346 &xx_ei.move_stepsize, TILEX / 8,
1347 &yy_ei.move_stepsize
1352 TYPE_ELEMENT, CONF_VALUE_16_BIT(12),
1353 &xx_ei.move_enter_element, EL_EMPTY_SPACE,
1354 &yy_ei.move_enter_element
1358 TYPE_ELEMENT, CONF_VALUE_16_BIT(13),
1359 &xx_ei.move_leave_element, EL_EMPTY_SPACE,
1360 &yy_ei.move_leave_element
1364 TYPE_INTEGER, CONF_VALUE_8_BIT(6),
1365 &xx_ei.move_leave_type, LEAVE_TYPE_UNLIMITED,
1366 &yy_ei.move_leave_type
1371 TYPE_INTEGER, CONF_VALUE_8_BIT(7),
1372 &xx_ei.slippery_type, SLIPPERY_ANY_RANDOM,
1373 &yy_ei.slippery_type
1378 TYPE_INTEGER, CONF_VALUE_8_BIT(8),
1379 &xx_ei.explosion_type, EXPLODES_3X3,
1380 &yy_ei.explosion_type
1384 TYPE_INTEGER, CONF_VALUE_16_BIT(14),
1385 &xx_ei.explosion_delay, 16,
1386 &yy_ei.explosion_delay
1390 TYPE_INTEGER, CONF_VALUE_16_BIT(15),
1391 &xx_ei.ignition_delay, 8,
1392 &yy_ei.ignition_delay
1397 TYPE_CONTENT_LIST, CONF_VALUE_BYTES(2),
1398 &xx_ei.content, EL_EMPTY_SPACE,
1400 &xx_num_contents, 1, 1
1403 // ---------- "num_change_pages" must be the last entry ---------------------
1406 -1, SAVE_CONF_ALWAYS,
1407 TYPE_INTEGER, CONF_VALUE_8_BIT(9),
1408 &xx_ei.num_change_pages, 1,
1409 &yy_ei.num_change_pages
1420 static struct LevelFileConfigInfo chunk_config_CUSX_change[] =
1422 // ---------- "current_change_page" must be the first entry -----------------
1425 -1, SAVE_CONF_ALWAYS,
1426 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
1427 &xx_current_change_page, -1
1430 // ---------- (the remaining entries can be in any order) -------------------
1434 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
1435 &xx_change.can_change, FALSE
1440 TYPE_BITFIELD, CONF_VALUE_32_BIT(1),
1441 &xx_event_bits[0], 0
1445 TYPE_BITFIELD, CONF_VALUE_32_BIT(2),
1446 &xx_event_bits[1], 0
1451 TYPE_BITFIELD, CONF_VALUE_8_BIT(3),
1452 &xx_change.trigger_player, CH_PLAYER_ANY
1456 TYPE_BITFIELD, CONF_VALUE_8_BIT(4),
1457 &xx_change.trigger_side, CH_SIDE_ANY
1461 TYPE_BITFIELD, CONF_VALUE_32_BIT(3),
1462 &xx_change.trigger_page, CH_PAGE_ANY
1467 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
1468 &xx_change.target_element, EL_EMPTY_SPACE
1473 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
1474 &xx_change.delay_fixed, 0
1478 TYPE_INTEGER, CONF_VALUE_16_BIT(3),
1479 &xx_change.delay_random, 0
1483 TYPE_INTEGER, CONF_VALUE_16_BIT(4),
1484 &xx_change.delay_frames, FRAMES_PER_SECOND
1489 TYPE_ELEMENT, CONF_VALUE_16_BIT(5),
1490 &xx_change.initial_trigger_element, EL_EMPTY_SPACE
1495 TYPE_BOOLEAN, CONF_VALUE_8_BIT(6),
1496 &xx_change.explode, FALSE
1500 TYPE_BOOLEAN, CONF_VALUE_8_BIT(7),
1501 &xx_change.use_target_content, FALSE
1505 TYPE_BOOLEAN, CONF_VALUE_8_BIT(8),
1506 &xx_change.only_if_complete, FALSE
1510 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
1511 &xx_change.use_random_replace, FALSE
1515 TYPE_INTEGER, CONF_VALUE_8_BIT(10),
1516 &xx_change.random_percentage, 100
1520 TYPE_INTEGER, CONF_VALUE_8_BIT(11),
1521 &xx_change.replace_when, CP_WHEN_EMPTY
1526 TYPE_BOOLEAN, CONF_VALUE_8_BIT(12),
1527 &xx_change.has_action, FALSE
1531 TYPE_INTEGER, CONF_VALUE_8_BIT(13),
1532 &xx_change.action_type, CA_NO_ACTION
1536 TYPE_INTEGER, CONF_VALUE_8_BIT(14),
1537 &xx_change.action_mode, CA_MODE_UNDEFINED
1541 TYPE_INTEGER, CONF_VALUE_16_BIT(6),
1542 &xx_change.action_arg, CA_ARG_UNDEFINED
1547 TYPE_ELEMENT, CONF_VALUE_16_BIT(7),
1548 &xx_change.action_element, EL_EMPTY_SPACE
1553 TYPE_CONTENT_LIST, CONF_VALUE_BYTES(1),
1554 &xx_change.target_content, EL_EMPTY_SPACE, NULL,
1555 &xx_num_contents, 1, 1
1565 static struct LevelFileConfigInfo chunk_config_GRPX[] =
1569 TYPE_STRING, CONF_VALUE_BYTES(1),
1570 &xx_ei.description[0], -1, NULL,
1571 &xx_string_length_unused, -1, MAX_ELEMENT_NAME_LEN,
1572 &xx_default_description[0]
1577 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1578 &xx_ei.use_gfx_element, FALSE
1582 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
1583 &xx_ei.gfx_element_initial, EL_EMPTY_SPACE
1588 TYPE_INTEGER, CONF_VALUE_8_BIT(2),
1589 &xx_group.choice_mode, ANIM_RANDOM
1594 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(2),
1595 &xx_group.element[0], EL_EMPTY_SPACE, NULL,
1596 &xx_group.num_elements, 1, MAX_ELEMENTS_IN_GROUP
1606 static struct LevelFileConfigInfo chunk_config_EMPX[] =
1610 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1611 &xx_ei.use_gfx_element, FALSE
1615 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
1616 &xx_ei.gfx_element_initial, EL_EMPTY_SPACE
1626 static struct LevelFileConfigInfo chunk_config_CONF[] = // (OBSOLETE)
1630 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
1631 &li.block_snap_field, TRUE
1635 TYPE_BOOLEAN, CONF_VALUE_8_BIT(13),
1636 &li.continuous_snapping, TRUE
1640 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
1641 &li.initial_player_stepsize[0], STEPSIZE_NORMAL
1645 TYPE_BOOLEAN, CONF_VALUE_8_BIT(10),
1646 &li.use_start_element[0], FALSE
1650 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
1651 &li.start_element[0], EL_PLAYER_1
1655 TYPE_BOOLEAN, CONF_VALUE_8_BIT(11),
1656 &li.use_artwork_element[0], FALSE
1660 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
1661 &li.artwork_element[0], EL_PLAYER_1
1665 TYPE_BOOLEAN, CONF_VALUE_8_BIT(12),
1666 &li.use_explosion_element[0], FALSE
1670 TYPE_ELEMENT, CONF_VALUE_16_BIT(3),
1671 &li.explosion_element[0], EL_PLAYER_1
1686 filetype_id_list[] =
1688 { LEVEL_FILE_TYPE_RND, "RND" },
1689 { LEVEL_FILE_TYPE_BD, "BD" },
1690 { LEVEL_FILE_TYPE_EM, "EM" },
1691 { LEVEL_FILE_TYPE_SP, "SP" },
1692 { LEVEL_FILE_TYPE_DX, "DX" },
1693 { LEVEL_FILE_TYPE_SB, "SB" },
1694 { LEVEL_FILE_TYPE_DC, "DC" },
1695 { LEVEL_FILE_TYPE_MM, "MM" },
1696 { LEVEL_FILE_TYPE_MM, "DF" },
1701 // ============================================================================
1702 // level file functions
1703 // ============================================================================
1705 static boolean check_special_flags(char *flag)
1707 if (strEqual(options.special_flags, flag) ||
1708 strEqual(leveldir_current->special_flags, flag))
1714 static struct DateInfo getCurrentDate(void)
1716 time_t epoch_seconds = time(NULL);
1717 struct tm *now = localtime(&epoch_seconds);
1718 struct DateInfo date;
1720 date.year = now->tm_year + 1900;
1721 date.month = now->tm_mon + 1;
1722 date.day = now->tm_mday;
1724 date.src = DATE_SRC_CLOCK;
1729 static void resetEventFlags(struct ElementChangeInfo *change)
1733 for (i = 0; i < NUM_CHANGE_EVENTS; i++)
1734 change->has_event[i] = FALSE;
1737 static void resetEventBits(void)
1741 for (i = 0; i < NUM_CE_BITFIELDS; i++)
1742 xx_event_bits[i] = 0;
1745 static void setEventFlagsFromEventBits(struct ElementChangeInfo *change)
1749 /* important: only change event flag if corresponding event bit is set
1750 (this is because all xx_event_bits[] values are loaded separately,
1751 and all xx_event_bits[] values are set back to zero before loading
1752 another value xx_event_bits[x] (each value representing 32 flags)) */
1754 for (i = 0; i < NUM_CHANGE_EVENTS; i++)
1755 if (xx_event_bits[CH_EVENT_BITFIELD_NR(i)] & CH_EVENT_BIT(i))
1756 change->has_event[i] = TRUE;
1759 static void setEventBitsFromEventFlags(struct ElementChangeInfo *change)
1763 /* in contrast to the above function setEventFlagsFromEventBits(), it
1764 would also be possible to set all bits in xx_event_bits[] to 0 or 1
1765 depending on the corresponding change->has_event[i] values here, as
1766 all xx_event_bits[] values are reset in resetEventBits() before */
1768 for (i = 0; i < NUM_CHANGE_EVENTS; i++)
1769 if (change->has_event[i])
1770 xx_event_bits[CH_EVENT_BITFIELD_NR(i)] |= CH_EVENT_BIT(i);
1773 static char *getDefaultElementDescription(struct ElementInfo *ei)
1775 static char description[MAX_ELEMENT_NAME_LEN + 1];
1776 char *default_description = (ei->custom_description != NULL ?
1777 ei->custom_description :
1778 ei->editor_description);
1781 // always start with reliable default values
1782 for (i = 0; i < MAX_ELEMENT_NAME_LEN + 1; i++)
1783 description[i] = '\0';
1785 // truncate element description to MAX_ELEMENT_NAME_LEN bytes
1786 strncpy(description, default_description, MAX_ELEMENT_NAME_LEN);
1788 return &description[0];
1791 static void setElementDescriptionToDefault(struct ElementInfo *ei)
1793 char *default_description = getDefaultElementDescription(ei);
1796 for (i = 0; i < MAX_ELEMENT_NAME_LEN + 1; i++)
1797 ei->description[i] = default_description[i];
1800 static void setConfigToDefaultsFromConfigList(struct LevelFileConfigInfo *conf)
1804 for (i = 0; conf[i].data_type != -1; i++)
1806 int default_value = conf[i].default_value;
1807 int data_type = conf[i].data_type;
1808 int conf_type = conf[i].conf_type;
1809 int byte_mask = conf_type & CONF_MASK_BYTES;
1811 if (byte_mask == CONF_MASK_MULTI_BYTES)
1813 int default_num_entities = conf[i].default_num_entities;
1814 int max_num_entities = conf[i].max_num_entities;
1816 *(int *)(conf[i].num_entities) = default_num_entities;
1818 if (data_type == TYPE_STRING)
1820 char *default_string = conf[i].default_string;
1821 char *string = (char *)(conf[i].value);
1823 strncpy(string, default_string, max_num_entities);
1825 else if (data_type == TYPE_ELEMENT_LIST)
1827 int *element_array = (int *)(conf[i].value);
1830 for (j = 0; j < max_num_entities; j++)
1831 element_array[j] = default_value;
1833 else if (data_type == TYPE_CONTENT_LIST)
1835 struct Content *content = (struct Content *)(conf[i].value);
1838 for (c = 0; c < max_num_entities; c++)
1839 for (y = 0; y < 3; y++)
1840 for (x = 0; x < 3; x++)
1841 content[c].e[x][y] = default_value;
1844 else // constant size configuration data (1, 2 or 4 bytes)
1846 if (data_type == TYPE_BOOLEAN)
1847 *(boolean *)(conf[i].value) = default_value;
1849 *(int *) (conf[i].value) = default_value;
1854 static void copyConfigFromConfigList(struct LevelFileConfigInfo *conf)
1858 for (i = 0; conf[i].data_type != -1; i++)
1860 int data_type = conf[i].data_type;
1861 int conf_type = conf[i].conf_type;
1862 int byte_mask = conf_type & CONF_MASK_BYTES;
1864 if (byte_mask == CONF_MASK_MULTI_BYTES)
1866 int max_num_entities = conf[i].max_num_entities;
1868 if (data_type == TYPE_STRING)
1870 char *string = (char *)(conf[i].value);
1871 char *string_copy = (char *)(conf[i].value_copy);
1873 strncpy(string_copy, string, max_num_entities);
1875 else if (data_type == TYPE_ELEMENT_LIST)
1877 int *element_array = (int *)(conf[i].value);
1878 int *element_array_copy = (int *)(conf[i].value_copy);
1881 for (j = 0; j < max_num_entities; j++)
1882 element_array_copy[j] = element_array[j];
1884 else if (data_type == TYPE_CONTENT_LIST)
1886 struct Content *content = (struct Content *)(conf[i].value);
1887 struct Content *content_copy = (struct Content *)(conf[i].value_copy);
1890 for (c = 0; c < max_num_entities; c++)
1891 for (y = 0; y < 3; y++)
1892 for (x = 0; x < 3; x++)
1893 content_copy[c].e[x][y] = content[c].e[x][y];
1896 else // constant size configuration data (1, 2 or 4 bytes)
1898 if (data_type == TYPE_BOOLEAN)
1899 *(boolean *)(conf[i].value_copy) = *(boolean *)(conf[i].value);
1901 *(int *) (conf[i].value_copy) = *(int *) (conf[i].value);
1906 void copyElementInfo(struct ElementInfo *ei_from, struct ElementInfo *ei_to)
1910 xx_ei = *ei_from; // copy element data into temporary buffer
1911 yy_ei = *ei_to; // copy element data into temporary buffer
1913 copyConfigFromConfigList(chunk_config_CUSX_base);
1918 // ---------- reinitialize and copy change pages ----------
1920 ei_to->num_change_pages = ei_from->num_change_pages;
1921 ei_to->current_change_page = ei_from->current_change_page;
1923 setElementChangePages(ei_to, ei_to->num_change_pages);
1925 for (i = 0; i < ei_to->num_change_pages; i++)
1926 ei_to->change_page[i] = ei_from->change_page[i];
1928 // ---------- copy group element info ----------
1929 if (ei_from->group != NULL && ei_to->group != NULL) // group or internal
1930 *ei_to->group = *ei_from->group;
1932 // mark this custom element as modified
1933 ei_to->modified_settings = TRUE;
1936 void setElementChangePages(struct ElementInfo *ei, int change_pages)
1938 int change_page_size = sizeof(struct ElementChangeInfo);
1940 ei->num_change_pages = MAX(1, change_pages);
1943 checked_realloc(ei->change_page, ei->num_change_pages * change_page_size);
1945 if (ei->current_change_page >= ei->num_change_pages)
1946 ei->current_change_page = ei->num_change_pages - 1;
1948 ei->change = &ei->change_page[ei->current_change_page];
1951 void setElementChangeInfoToDefaults(struct ElementChangeInfo *change)
1953 xx_change = *change; // copy change data into temporary buffer
1955 setConfigToDefaultsFromConfigList(chunk_config_CUSX_change);
1957 *change = xx_change;
1959 resetEventFlags(change);
1961 change->direct_action = 0;
1962 change->other_action = 0;
1964 change->pre_change_function = NULL;
1965 change->change_function = NULL;
1966 change->post_change_function = NULL;
1969 static void setLevelInfoToDefaults_Level(struct LevelInfo *level)
1973 li = *level; // copy level data into temporary buffer
1974 setConfigToDefaultsFromConfigList(chunk_config_INFO);
1975 *level = li; // copy temporary buffer back to level data
1977 setLevelInfoToDefaults_BD();
1978 setLevelInfoToDefaults_EM();
1979 setLevelInfoToDefaults_SP();
1980 setLevelInfoToDefaults_MM();
1982 level->native_bd_level = &native_bd_level;
1983 level->native_em_level = &native_em_level;
1984 level->native_sp_level = &native_sp_level;
1985 level->native_mm_level = &native_mm_level;
1987 level->file_version = FILE_VERSION_ACTUAL;
1988 level->game_version = GAME_VERSION_ACTUAL;
1990 level->creation_date = getCurrentDate();
1992 level->encoding_16bit_field = TRUE;
1993 level->encoding_16bit_yamyam = TRUE;
1994 level->encoding_16bit_amoeba = TRUE;
1996 // clear level name and level author string buffers
1997 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
1998 level->name[i] = '\0';
1999 for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
2000 level->author[i] = '\0';
2002 // set level name and level author to default values
2003 strcpy(level->name, NAMELESS_LEVEL_NAME);
2004 strcpy(level->author, ANONYMOUS_NAME);
2006 // set level playfield to playable default level with player and exit
2007 for (x = 0; x < MAX_LEV_FIELDX; x++)
2008 for (y = 0; y < MAX_LEV_FIELDY; y++)
2009 level->field[x][y] = EL_SAND;
2011 level->field[0][0] = EL_PLAYER_1;
2012 level->field[STD_LEV_FIELDX - 1][STD_LEV_FIELDY - 1] = EL_EXIT_CLOSED;
2014 BorderElement = EL_STEELWALL;
2016 // detect custom elements when loading them
2017 level->file_has_custom_elements = FALSE;
2019 // set all bug compatibility flags to "false" => do not emulate this bug
2020 level->use_action_after_change_bug = FALSE;
2022 if (leveldir_current)
2024 // try to determine better author name than 'anonymous'
2025 if (!strEqual(leveldir_current->author, ANONYMOUS_NAME))
2027 strncpy(level->author, leveldir_current->author, MAX_LEVEL_AUTHOR_LEN);
2028 level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
2032 switch (LEVELCLASS(leveldir_current))
2034 case LEVELCLASS_TUTORIAL:
2035 strcpy(level->author, PROGRAM_AUTHOR_STRING);
2038 case LEVELCLASS_CONTRIB:
2039 strncpy(level->author, leveldir_current->name, MAX_LEVEL_AUTHOR_LEN);
2040 level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
2043 case LEVELCLASS_PRIVATE:
2044 strncpy(level->author, getRealName(), MAX_LEVEL_AUTHOR_LEN);
2045 level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
2049 // keep default value
2056 static void setLevelInfoToDefaults_Elements(struct LevelInfo *level)
2058 static boolean clipboard_elements_initialized = FALSE;
2061 InitElementPropertiesStatic();
2063 li = *level; // copy level data into temporary buffer
2064 setConfigToDefaultsFromConfigList(chunk_config_ELEM);
2065 *level = li; // copy temporary buffer back to level data
2067 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2070 struct ElementInfo *ei = &element_info[element];
2072 if (element == EL_MM_GRAY_BALL)
2074 struct LevelInfo_MM *level_mm = level->native_mm_level;
2077 for (j = 0; j < level->num_mm_ball_contents; j++)
2078 level->mm_ball_content[j] =
2079 map_element_MM_to_RND(level_mm->ball_content[j]);
2082 // never initialize clipboard elements after the very first time
2083 // (to be able to use clipboard elements between several levels)
2084 if (IS_CLIPBOARD_ELEMENT(element) && clipboard_elements_initialized)
2087 if (IS_ENVELOPE(element))
2089 int envelope_nr = element - EL_ENVELOPE_1;
2091 setConfigToDefaultsFromConfigList(chunk_config_NOTE);
2093 level->envelope[envelope_nr] = xx_envelope;
2096 if (IS_CUSTOM_ELEMENT(element) ||
2097 IS_GROUP_ELEMENT(element) ||
2098 IS_INTERNAL_ELEMENT(element))
2100 xx_ei = *ei; // copy element data into temporary buffer
2102 setConfigToDefaultsFromConfigList(chunk_config_CUSX_base);
2107 setElementChangePages(ei, 1);
2108 setElementChangeInfoToDefaults(ei->change);
2110 if (IS_CUSTOM_ELEMENT(element) ||
2111 IS_GROUP_ELEMENT(element))
2113 setElementDescriptionToDefault(ei);
2115 ei->modified_settings = FALSE;
2118 if (IS_CUSTOM_ELEMENT(element) ||
2119 IS_INTERNAL_ELEMENT(element))
2121 // internal values used in level editor
2123 ei->access_type = 0;
2124 ei->access_layer = 0;
2125 ei->access_protected = 0;
2126 ei->walk_to_action = 0;
2127 ei->smash_targets = 0;
2130 ei->can_explode_by_fire = FALSE;
2131 ei->can_explode_smashed = FALSE;
2132 ei->can_explode_impact = FALSE;
2134 ei->current_change_page = 0;
2137 if (IS_GROUP_ELEMENT(element) ||
2138 IS_INTERNAL_ELEMENT(element))
2140 struct ElementGroupInfo *group;
2142 // initialize memory for list of elements in group
2143 if (ei->group == NULL)
2144 ei->group = checked_malloc(sizeof(struct ElementGroupInfo));
2148 xx_group = *group; // copy group data into temporary buffer
2150 setConfigToDefaultsFromConfigList(chunk_config_GRPX);
2155 if (IS_EMPTY_ELEMENT(element) ||
2156 IS_INTERNAL_ELEMENT(element))
2158 xx_ei = *ei; // copy element data into temporary buffer
2160 setConfigToDefaultsFromConfigList(chunk_config_EMPX);
2166 clipboard_elements_initialized = TRUE;
2169 static void setLevelInfoToDefaults(struct LevelInfo *level,
2170 boolean level_info_only,
2171 boolean reset_file_status)
2173 setLevelInfoToDefaults_Level(level);
2175 if (!level_info_only)
2176 setLevelInfoToDefaults_Elements(level);
2178 if (reset_file_status)
2180 level->no_valid_file = FALSE;
2181 level->no_level_file = FALSE;
2184 level->changed = FALSE;
2187 static void setFileInfoToDefaults(struct LevelFileInfo *level_file_info)
2189 level_file_info->nr = 0;
2190 level_file_info->type = LEVEL_FILE_TYPE_UNKNOWN;
2191 level_file_info->packed = FALSE;
2193 setString(&level_file_info->basename, NULL);
2194 setString(&level_file_info->filename, NULL);
2197 int getMappedElement_SB(int, boolean);
2199 static void ActivateLevelTemplate(void)
2203 if (check_special_flags("load_xsb_to_ces"))
2205 // fill smaller playfields with padding "beyond border wall" elements
2206 if (level.fieldx < level_template.fieldx ||
2207 level.fieldy < level_template.fieldy)
2209 short field[level.fieldx][level.fieldy];
2210 int new_fieldx = MAX(level.fieldx, level_template.fieldx);
2211 int new_fieldy = MAX(level.fieldy, level_template.fieldy);
2212 int pos_fieldx = (new_fieldx - level.fieldx) / 2;
2213 int pos_fieldy = (new_fieldy - level.fieldy) / 2;
2215 // copy old playfield (which is smaller than the visible area)
2216 for (y = 0; y < level.fieldy; y++) for (x = 0; x < level.fieldx; x++)
2217 field[x][y] = level.field[x][y];
2219 // fill new, larger playfield with "beyond border wall" elements
2220 for (y = 0; y < new_fieldy; y++) for (x = 0; x < new_fieldx; x++)
2221 level.field[x][y] = getMappedElement_SB('_', TRUE);
2223 // copy the old playfield to the middle of the new playfield
2224 for (y = 0; y < level.fieldy; y++) for (x = 0; x < level.fieldx; x++)
2225 level.field[pos_fieldx + x][pos_fieldy + y] = field[x][y];
2227 level.fieldx = new_fieldx;
2228 level.fieldy = new_fieldy;
2232 // Currently there is no special action needed to activate the template
2233 // data, because 'element_info' property settings overwrite the original
2234 // level data, while all other variables do not change.
2236 // Exception: 'from_level_template' elements in the original level playfield
2237 // are overwritten with the corresponding elements at the same position in
2238 // playfield from the level template.
2240 for (x = 0; x < level.fieldx; x++)
2241 for (y = 0; y < level.fieldy; y++)
2242 if (level.field[x][y] == EL_FROM_LEVEL_TEMPLATE)
2243 level.field[x][y] = level_template.field[x][y];
2245 if (check_special_flags("load_xsb_to_ces"))
2247 struct LevelInfo level_backup = level;
2249 // overwrite all individual level settings from template level settings
2250 level = level_template;
2252 // restore level file info
2253 level.file_info = level_backup.file_info;
2255 // restore playfield size
2256 level.fieldx = level_backup.fieldx;
2257 level.fieldy = level_backup.fieldy;
2259 // restore playfield content
2260 for (x = 0; x < level.fieldx; x++)
2261 for (y = 0; y < level.fieldy; y++)
2262 level.field[x][y] = level_backup.field[x][y];
2264 // restore name and author from individual level
2265 strcpy(level.name, level_backup.name);
2266 strcpy(level.author, level_backup.author);
2268 // restore flag "use_custom_template"
2269 level.use_custom_template = level_backup.use_custom_template;
2273 static boolean checkForPackageFromBasename_BD(char *basename)
2275 // check for native BD level file extensions
2276 if (!strSuffixLower(basename, ".bd") &&
2277 !strSuffixLower(basename, ".bdr") &&
2278 !strSuffixLower(basename, ".brc") &&
2279 !strSuffixLower(basename, ".gds"))
2282 // check for standard single-level BD files (like "001.bd")
2283 if (strSuffixLower(basename, ".bd") &&
2284 strlen(basename) == 6 &&
2285 basename[0] >= '0' && basename[0] <= '9' &&
2286 basename[1] >= '0' && basename[1] <= '9' &&
2287 basename[2] >= '0' && basename[2] <= '9')
2290 // this is a level package in native BD file format
2294 static char *getLevelFilenameFromBasename(char *basename)
2296 static char *filename = NULL;
2298 checked_free(filename);
2300 filename = getPath2(getCurrentLevelDir(), basename);
2305 static int getFileTypeFromBasename(char *basename)
2307 // !!! ALSO SEE COMMENT IN checkForPackageFromBasename() !!!
2309 static char *filename = NULL;
2310 struct stat file_status;
2312 // ---------- try to determine file type from filename ----------
2314 // check for typical filename of a Supaplex level package file
2315 if (strlen(basename) == 10 && strPrefixLower(basename, "levels.d"))
2316 return LEVEL_FILE_TYPE_SP;
2318 // check for typical filename of a Diamond Caves II level package file
2319 if (strSuffixLower(basename, ".dc") ||
2320 strSuffixLower(basename, ".dc2"))
2321 return LEVEL_FILE_TYPE_DC;
2323 // check for typical filename of a Sokoban level package file
2324 if (strSuffixLower(basename, ".xsb") &&
2325 strchr(basename, '%') == NULL)
2326 return LEVEL_FILE_TYPE_SB;
2328 // check for typical filename of a Boulder Dash (GDash) level package file
2329 if (checkForPackageFromBasename_BD(basename))
2330 return LEVEL_FILE_TYPE_BD;
2332 // ---------- try to determine file type from filesize ----------
2334 checked_free(filename);
2335 filename = getPath2(getCurrentLevelDir(), basename);
2337 if (stat(filename, &file_status) == 0)
2339 // check for typical filesize of a Supaplex level package file
2340 if (file_status.st_size == 170496)
2341 return LEVEL_FILE_TYPE_SP;
2344 return LEVEL_FILE_TYPE_UNKNOWN;
2347 static int getFileTypeFromMagicBytes(char *filename, int type)
2351 if ((file = openFile(filename, MODE_READ)))
2353 char chunk_name[CHUNK_ID_LEN + 1];
2355 getFileChunkBE(file, chunk_name, NULL);
2357 if (strEqual(chunk_name, "MMII") ||
2358 strEqual(chunk_name, "MIRR"))
2359 type = LEVEL_FILE_TYPE_MM;
2367 static boolean checkForPackageFromBasename(char *basename)
2369 // !!! WON'T WORK ANYMORE IF getFileTypeFromBasename() ALSO DETECTS !!!
2370 // !!! SINGLE LEVELS (CURRENTLY ONLY DETECTS LEVEL PACKAGES !!!
2372 return (getFileTypeFromBasename(basename) != LEVEL_FILE_TYPE_UNKNOWN);
2375 static char *getSingleLevelBasenameExt(int nr, char *extension)
2377 static char basename[MAX_FILENAME_LEN];
2380 sprintf(basename, "%s", LEVELTEMPLATE_FILENAME);
2382 sprintf(basename, "%03d.%s", nr, extension);
2387 static char *getSingleLevelBasename(int nr)
2389 return getSingleLevelBasenameExt(nr, LEVELFILE_EXTENSION);
2392 static char *getPackedLevelBasename(int type)
2394 static char basename[MAX_FILENAME_LEN];
2395 char *directory = getCurrentLevelDir();
2397 DirectoryEntry *dir_entry;
2399 strcpy(basename, UNDEFINED_FILENAME); // default: undefined file
2401 if ((dir = openDirectory(directory)) == NULL)
2403 Warn("cannot read current level directory '%s'", directory);
2408 while ((dir_entry = readDirectory(dir)) != NULL) // loop all entries
2410 char *entry_basename = dir_entry->basename;
2411 int entry_type = getFileTypeFromBasename(entry_basename);
2413 if (entry_type != LEVEL_FILE_TYPE_UNKNOWN) // found valid level package
2415 if (type == LEVEL_FILE_TYPE_UNKNOWN ||
2418 strcpy(basename, entry_basename);
2425 closeDirectory(dir);
2430 static char *getSingleLevelFilename(int nr)
2432 return getLevelFilenameFromBasename(getSingleLevelBasename(nr));
2435 #if ENABLE_UNUSED_CODE
2436 static char *getPackedLevelFilename(int type)
2438 return getLevelFilenameFromBasename(getPackedLevelBasename(type));
2442 char *getDefaultLevelFilename(int nr)
2444 return getSingleLevelFilename(nr);
2447 #if ENABLE_UNUSED_CODE
2448 static void setLevelFileInfo_SingleLevelFilename(struct LevelFileInfo *lfi,
2452 lfi->packed = FALSE;
2454 setString(&lfi->basename, getSingleLevelBasename(lfi->nr, lfi->type));
2455 setString(&lfi->filename, getLevelFilenameFromBasename(lfi->basename));
2459 static void setLevelFileInfo_FormatLevelFilename(struct LevelFileInfo *lfi,
2460 int type, char *format, ...)
2462 static char basename[MAX_FILENAME_LEN];
2465 va_start(ap, format);
2466 vsprintf(basename, format, ap);
2470 lfi->packed = FALSE;
2472 setString(&lfi->basename, basename);
2473 setString(&lfi->filename, getLevelFilenameFromBasename(lfi->basename));
2476 static void setLevelFileInfo_PackedLevelFilename(struct LevelFileInfo *lfi,
2482 setString(&lfi->basename, getPackedLevelBasename(lfi->type));
2483 setString(&lfi->filename, getLevelFilenameFromBasename(lfi->basename));
2486 static int getFiletypeFromID(char *filetype_id)
2488 char *filetype_id_lower;
2489 int filetype = LEVEL_FILE_TYPE_UNKNOWN;
2492 if (filetype_id == NULL)
2493 return LEVEL_FILE_TYPE_UNKNOWN;
2495 filetype_id_lower = getStringToLower(filetype_id);
2497 for (i = 0; filetype_id_list[i].id != NULL; i++)
2499 char *id_lower = getStringToLower(filetype_id_list[i].id);
2501 if (strEqual(filetype_id_lower, id_lower))
2502 filetype = filetype_id_list[i].filetype;
2506 if (filetype != LEVEL_FILE_TYPE_UNKNOWN)
2510 free(filetype_id_lower);
2515 char *getLocalLevelTemplateFilename(void)
2517 return getDefaultLevelFilename(-1);
2520 char *getGlobalLevelTemplateFilename(void)
2522 // global variable "leveldir_current" must be modified in the loop below
2523 LevelDirTree *leveldir_current_last = leveldir_current;
2524 char *filename = NULL;
2526 // check for template level in path from current to topmost tree node
2528 while (leveldir_current != NULL)
2530 filename = getDefaultLevelFilename(-1);
2532 if (fileExists(filename))
2535 leveldir_current = leveldir_current->node_parent;
2538 // restore global variable "leveldir_current" modified in above loop
2539 leveldir_current = leveldir_current_last;
2544 static void determineLevelFileInfo_Filename(struct LevelFileInfo *lfi)
2548 // special case: level number is negative => check for level template file
2551 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
2552 getSingleLevelBasename(-1));
2554 // replace local level template filename with global template filename
2555 setString(&lfi->filename, getGlobalLevelTemplateFilename());
2557 // no fallback if template file not existing
2561 // special case: check for file name/pattern specified in "levelinfo.conf"
2562 if (leveldir_current->level_filename != NULL)
2564 int filetype = getFiletypeFromID(leveldir_current->level_filetype);
2566 setLevelFileInfo_FormatLevelFilename(lfi, filetype,
2567 leveldir_current->level_filename, nr);
2569 lfi->packed = checkForPackageFromBasename(leveldir_current->level_filename);
2571 if (fileExists(lfi->filename))
2574 else if (leveldir_current->level_filetype != NULL)
2576 int filetype = getFiletypeFromID(leveldir_current->level_filetype);
2578 // check for specified native level file with standard file name
2579 setLevelFileInfo_FormatLevelFilename(lfi, filetype,
2580 "%03d.%s", nr, LEVELFILE_EXTENSION);
2581 if (fileExists(lfi->filename))
2585 // check for native Rocks'n'Diamonds level file
2586 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
2587 "%03d.%s", nr, LEVELFILE_EXTENSION);
2588 if (fileExists(lfi->filename))
2591 // check for native Boulder Dash level file
2592 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_BD, "%03d.bd", nr);
2593 if (fileExists(lfi->filename))
2596 // check for Emerald Mine level file (V1)
2597 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "a%c%c",
2598 'a' + (nr / 10) % 26, '0' + nr % 10);
2599 if (fileExists(lfi->filename))
2601 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "A%c%c",
2602 'A' + (nr / 10) % 26, '0' + nr % 10);
2603 if (fileExists(lfi->filename))
2606 // check for Emerald Mine level file (V2 to V5)
2607 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%d", nr);
2608 if (fileExists(lfi->filename))
2611 // check for Emerald Mine level file (V6 / single mode)
2612 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02ds", nr);
2613 if (fileExists(lfi->filename))
2615 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02dS", nr);
2616 if (fileExists(lfi->filename))
2619 // check for Emerald Mine level file (V6 / teamwork mode)
2620 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02dt", nr);
2621 if (fileExists(lfi->filename))
2623 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02dT", nr);
2624 if (fileExists(lfi->filename))
2627 // check for various packed level file formats
2628 setLevelFileInfo_PackedLevelFilename(lfi, LEVEL_FILE_TYPE_UNKNOWN);
2629 if (fileExists(lfi->filename))
2632 // no known level file found -- use default values (and fail later)
2633 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
2634 "%03d.%s", nr, LEVELFILE_EXTENSION);
2637 static void determineLevelFileInfo_Filetype(struct LevelFileInfo *lfi)
2639 if (lfi->type == LEVEL_FILE_TYPE_UNKNOWN)
2640 lfi->type = getFileTypeFromBasename(lfi->basename);
2642 if (lfi->type == LEVEL_FILE_TYPE_RND)
2643 lfi->type = getFileTypeFromMagicBytes(lfi->filename, lfi->type);
2646 static void setLevelFileInfo(struct LevelFileInfo *level_file_info, int nr)
2648 // always start with reliable default values
2649 setFileInfoToDefaults(level_file_info);
2651 level_file_info->nr = nr; // set requested level number
2653 determineLevelFileInfo_Filename(level_file_info);
2654 determineLevelFileInfo_Filetype(level_file_info);
2657 static void copyLevelFileInfo(struct LevelFileInfo *lfi_from,
2658 struct LevelFileInfo *lfi_to)
2660 lfi_to->nr = lfi_from->nr;
2661 lfi_to->type = lfi_from->type;
2662 lfi_to->packed = lfi_from->packed;
2664 setString(&lfi_to->basename, lfi_from->basename);
2665 setString(&lfi_to->filename, lfi_from->filename);
2668 // ----------------------------------------------------------------------------
2669 // functions for loading R'n'D level
2670 // ----------------------------------------------------------------------------
2672 int getMappedElement(int element)
2674 // remap some (historic, now obsolete) elements
2678 case EL_PLAYER_OBSOLETE:
2679 element = EL_PLAYER_1;
2682 case EL_KEY_OBSOLETE:
2686 case EL_EM_KEY_1_FILE_OBSOLETE:
2687 element = EL_EM_KEY_1;
2690 case EL_EM_KEY_2_FILE_OBSOLETE:
2691 element = EL_EM_KEY_2;
2694 case EL_EM_KEY_3_FILE_OBSOLETE:
2695 element = EL_EM_KEY_3;
2698 case EL_EM_KEY_4_FILE_OBSOLETE:
2699 element = EL_EM_KEY_4;
2702 case EL_ENVELOPE_OBSOLETE:
2703 element = EL_ENVELOPE_1;
2711 if (element >= NUM_FILE_ELEMENTS)
2713 Warn("invalid level element %d", element);
2715 element = EL_UNKNOWN;
2723 static int getMappedElementByVersion(int element, int game_version)
2725 // remap some elements due to certain game version
2727 if (game_version <= VERSION_IDENT(2,2,0,0))
2729 // map game font elements
2730 element = (element == EL_CHAR('[') ? EL_CHAR_AUMLAUT :
2731 element == EL_CHAR('\\') ? EL_CHAR_OUMLAUT :
2732 element == EL_CHAR(']') ? EL_CHAR_UUMLAUT :
2733 element == EL_CHAR('^') ? EL_CHAR_COPYRIGHT : element);
2736 if (game_version < VERSION_IDENT(3,0,0,0))
2738 // map Supaplex gravity tube elements
2739 element = (element == EL_SP_GRAVITY_PORT_LEFT ? EL_SP_PORT_LEFT :
2740 element == EL_SP_GRAVITY_PORT_RIGHT ? EL_SP_PORT_RIGHT :
2741 element == EL_SP_GRAVITY_PORT_UP ? EL_SP_PORT_UP :
2742 element == EL_SP_GRAVITY_PORT_DOWN ? EL_SP_PORT_DOWN :
2749 static int LoadLevel_VERS(File *file, int chunk_size, struct LevelInfo *level)
2751 level->file_version = getFileVersion(file);
2752 level->game_version = getFileVersion(file);
2757 static int LoadLevel_DATE(File *file, int chunk_size, struct LevelInfo *level)
2759 level->creation_date.year = getFile16BitBE(file);
2760 level->creation_date.month = getFile8Bit(file);
2761 level->creation_date.day = getFile8Bit(file);
2763 level->creation_date.src = DATE_SRC_LEVELFILE;
2768 static int LoadLevel_HEAD(File *file, int chunk_size, struct LevelInfo *level)
2770 int initial_player_stepsize;
2771 int initial_player_gravity;
2774 level->fieldx = getFile8Bit(file);
2775 level->fieldy = getFile8Bit(file);
2777 level->time = getFile16BitBE(file);
2778 level->gems_needed = getFile16BitBE(file);
2780 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
2781 level->name[i] = getFile8Bit(file);
2782 level->name[MAX_LEVEL_NAME_LEN] = 0;
2784 for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
2785 level->score[i] = getFile8Bit(file);
2787 level->num_yamyam_contents = STD_ELEMENT_CONTENTS;
2788 for (i = 0; i < STD_ELEMENT_CONTENTS; i++)
2789 for (y = 0; y < 3; y++)
2790 for (x = 0; x < 3; x++)
2791 level->yamyam_content[i].e[x][y] = getMappedElement(getFile8Bit(file));
2793 level->amoeba_speed = getFile8Bit(file);
2794 level->time_magic_wall = getFile8Bit(file);
2795 level->time_wheel = getFile8Bit(file);
2796 level->amoeba_content = getMappedElement(getFile8Bit(file));
2798 initial_player_stepsize = (getFile8Bit(file) == 1 ? STEPSIZE_FAST :
2801 for (i = 0; i < MAX_PLAYERS; i++)
2802 level->initial_player_stepsize[i] = initial_player_stepsize;
2804 initial_player_gravity = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2806 for (i = 0; i < MAX_PLAYERS; i++)
2807 level->initial_player_gravity[i] = initial_player_gravity;
2809 level->encoding_16bit_field = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2810 level->em_slippery_gems = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2812 level->use_custom_template = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2814 level->block_last_field = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2815 level->sp_block_last_field = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2816 level->can_move_into_acid_bits = getFile32BitBE(file);
2817 level->dont_collide_with_bits = getFile8Bit(file);
2819 level->use_spring_bug = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2820 level->use_step_counter = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2822 level->instant_relocation = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2823 level->can_pass_to_walkable = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2824 level->grow_into_diggable = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2826 level->game_engine_type = getFile8Bit(file);
2828 ReadUnusedBytesFromFile(file, LEVEL_CHUNK_HEAD_UNUSED);
2833 static int LoadLevel_NAME(File *file, int chunk_size, struct LevelInfo *level)
2837 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
2838 level->name[i] = getFile8Bit(file);
2839 level->name[MAX_LEVEL_NAME_LEN] = 0;
2844 static int LoadLevel_AUTH(File *file, int chunk_size, struct LevelInfo *level)
2848 for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
2849 level->author[i] = getFile8Bit(file);
2850 level->author[MAX_LEVEL_AUTHOR_LEN] = 0;
2855 static int LoadLevel_BODY(File *file, int chunk_size, struct LevelInfo *level)
2858 int chunk_size_expected = level->fieldx * level->fieldy;
2860 /* Note: "chunk_size" was wrong before version 2.0 when elements are
2861 stored with 16-bit encoding (and should be twice as big then).
2862 Even worse, playfield data was stored 16-bit when only yamyam content
2863 contained 16-bit elements and vice versa. */
2865 if (level->encoding_16bit_field && level->file_version >= FILE_VERSION_2_0)
2866 chunk_size_expected *= 2;
2868 if (chunk_size_expected != chunk_size)
2870 ReadUnusedBytesFromFile(file, chunk_size);
2871 return chunk_size_expected;
2874 for (y = 0; y < level->fieldy; y++)
2875 for (x = 0; x < level->fieldx; x++)
2876 level->field[x][y] =
2877 getMappedElement(level->encoding_16bit_field ? getFile16BitBE(file) :
2882 static int LoadLevel_CONT(File *file, int chunk_size, struct LevelInfo *level)
2885 int header_size = 4;
2886 int content_size = MAX_ELEMENT_CONTENTS * 3 * 3;
2887 int chunk_size_expected = header_size + content_size;
2889 /* Note: "chunk_size" was wrong before version 2.0 when elements are
2890 stored with 16-bit encoding (and should be twice as big then).
2891 Even worse, playfield data was stored 16-bit when only yamyam content
2892 contained 16-bit elements and vice versa. */
2894 if (level->encoding_16bit_field && level->file_version >= FILE_VERSION_2_0)
2895 chunk_size_expected += content_size;
2897 if (chunk_size_expected != chunk_size)
2899 ReadUnusedBytesFromFile(file, chunk_size);
2900 return chunk_size_expected;
2904 level->num_yamyam_contents = getFile8Bit(file);
2908 // correct invalid number of content fields -- should never happen
2909 if (level->num_yamyam_contents < 1 ||
2910 level->num_yamyam_contents > MAX_ELEMENT_CONTENTS)
2911 level->num_yamyam_contents = STD_ELEMENT_CONTENTS;
2913 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
2914 for (y = 0; y < 3; y++)
2915 for (x = 0; x < 3; x++)
2916 level->yamyam_content[i].e[x][y] =
2917 getMappedElement(level->encoding_16bit_field ?
2918 getFile16BitBE(file) : getFile8Bit(file));
2922 static int LoadLevel_CNT2(File *file, int chunk_size, struct LevelInfo *level)
2927 int content_array[MAX_ELEMENT_CONTENTS][3][3];
2929 element = getMappedElement(getFile16BitBE(file));
2930 num_contents = getFile8Bit(file);
2932 getFile8Bit(file); // content x size (unused)
2933 getFile8Bit(file); // content y size (unused)
2935 ReadUnusedBytesFromFile(file, LEVEL_CHUNK_CNT2_UNUSED);
2937 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
2938 for (y = 0; y < 3; y++)
2939 for (x = 0; x < 3; x++)
2940 content_array[i][x][y] = getMappedElement(getFile16BitBE(file));
2942 // correct invalid number of content fields -- should never happen
2943 if (num_contents < 1 || num_contents > MAX_ELEMENT_CONTENTS)
2944 num_contents = STD_ELEMENT_CONTENTS;
2946 if (element == EL_YAMYAM)
2948 level->num_yamyam_contents = num_contents;
2950 for (i = 0; i < num_contents; i++)
2951 for (y = 0; y < 3; y++)
2952 for (x = 0; x < 3; x++)
2953 level->yamyam_content[i].e[x][y] = content_array[i][x][y];
2955 else if (element == EL_BD_AMOEBA)
2957 level->amoeba_content = content_array[0][0][0];
2961 Warn("cannot load content for element '%d'", element);
2967 static int LoadLevel_CNT3(File *file, int chunk_size, struct LevelInfo *level)
2973 int chunk_size_expected;
2975 element = getMappedElement(getFile16BitBE(file));
2976 if (!IS_ENVELOPE(element))
2977 element = EL_ENVELOPE_1;
2979 envelope_nr = element - EL_ENVELOPE_1;
2981 envelope_len = getFile16BitBE(file);
2983 level->envelope[envelope_nr].xsize = getFile8Bit(file);
2984 level->envelope[envelope_nr].ysize = getFile8Bit(file);
2986 ReadUnusedBytesFromFile(file, LEVEL_CHUNK_CNT3_UNUSED);
2988 chunk_size_expected = LEVEL_CHUNK_CNT3_SIZE(envelope_len);
2989 if (chunk_size_expected != chunk_size)
2991 ReadUnusedBytesFromFile(file, chunk_size - LEVEL_CHUNK_CNT3_HEADER);
2992 return chunk_size_expected;
2995 for (i = 0; i < envelope_len; i++)
2996 level->envelope[envelope_nr].text[i] = getFile8Bit(file);
3001 static int LoadLevel_CUS1(File *file, int chunk_size, struct LevelInfo *level)
3003 int num_changed_custom_elements = getFile16BitBE(file);
3004 int chunk_size_expected = 2 + num_changed_custom_elements * 6;
3007 if (chunk_size_expected != chunk_size)
3009 ReadUnusedBytesFromFile(file, chunk_size - 2);
3010 return chunk_size_expected;
3013 for (i = 0; i < num_changed_custom_elements; i++)
3015 int element = getMappedElement(getFile16BitBE(file));
3016 int properties = getFile32BitBE(file);
3018 if (IS_CUSTOM_ELEMENT(element))
3019 element_info[element].properties[EP_BITFIELD_BASE_NR] = properties;
3021 Warn("invalid custom element number %d", element);
3023 // older game versions that wrote level files with CUS1 chunks used
3024 // different default push delay values (not yet stored in level file)
3025 element_info[element].push_delay_fixed = 2;
3026 element_info[element].push_delay_random = 8;
3029 level->file_has_custom_elements = TRUE;
3034 static int LoadLevel_CUS2(File *file, int chunk_size, struct LevelInfo *level)
3036 int num_changed_custom_elements = getFile16BitBE(file);
3037 int chunk_size_expected = 2 + num_changed_custom_elements * 4;
3040 if (chunk_size_expected != chunk_size)
3042 ReadUnusedBytesFromFile(file, chunk_size - 2);
3043 return chunk_size_expected;
3046 for (i = 0; i < num_changed_custom_elements; i++)
3048 int element = getMappedElement(getFile16BitBE(file));
3049 int custom_target_element = getMappedElement(getFile16BitBE(file));
3051 if (IS_CUSTOM_ELEMENT(element))
3052 element_info[element].change->target_element = custom_target_element;
3054 Warn("invalid custom element number %d", element);
3057 level->file_has_custom_elements = TRUE;
3062 static int LoadLevel_CUS3(File *file, int chunk_size, struct LevelInfo *level)
3064 int num_changed_custom_elements = getFile16BitBE(file);
3065 int chunk_size_expected = LEVEL_CHUNK_CUS3_SIZE(num_changed_custom_elements);
3068 if (chunk_size_expected != chunk_size)
3070 ReadUnusedBytesFromFile(file, chunk_size - 2);
3071 return chunk_size_expected;
3074 for (i = 0; i < num_changed_custom_elements; i++)
3076 int element = getMappedElement(getFile16BitBE(file));
3077 struct ElementInfo *ei = &element_info[element];
3078 unsigned int event_bits;
3080 if (!IS_CUSTOM_ELEMENT(element))
3082 Warn("invalid custom element number %d", element);
3084 element = EL_INTERNAL_DUMMY;
3087 for (j = 0; j < MAX_ELEMENT_NAME_LEN; j++)
3088 ei->description[j] = getFile8Bit(file);
3089 ei->description[MAX_ELEMENT_NAME_LEN] = 0;
3091 ei->properties[EP_BITFIELD_BASE_NR] = getFile32BitBE(file);
3093 // some free bytes for future properties and padding
3094 ReadUnusedBytesFromFile(file, 7);
3096 ei->use_gfx_element = getFile8Bit(file);
3097 ei->gfx_element_initial = getMappedElement(getFile16BitBE(file));
3099 ei->collect_score_initial = getFile8Bit(file);
3100 ei->collect_count_initial = getFile8Bit(file);
3102 ei->push_delay_fixed = getFile16BitBE(file);
3103 ei->push_delay_random = getFile16BitBE(file);
3104 ei->move_delay_fixed = getFile16BitBE(file);
3105 ei->move_delay_random = getFile16BitBE(file);
3107 ei->move_pattern = getFile16BitBE(file);
3108 ei->move_direction_initial = getFile8Bit(file);
3109 ei->move_stepsize = getFile8Bit(file);
3111 for (y = 0; y < 3; y++)
3112 for (x = 0; x < 3; x++)
3113 ei->content.e[x][y] = getMappedElement(getFile16BitBE(file));
3115 // bits 0 - 31 of "has_event[]"
3116 event_bits = getFile32BitBE(file);
3117 for (j = 0; j < MIN(NUM_CHANGE_EVENTS, 32); j++)
3118 if (event_bits & (1u << j))
3119 ei->change->has_event[j] = TRUE;
3121 ei->change->target_element = getMappedElement(getFile16BitBE(file));
3123 ei->change->delay_fixed = getFile16BitBE(file);
3124 ei->change->delay_random = getFile16BitBE(file);
3125 ei->change->delay_frames = getFile16BitBE(file);
3127 ei->change->initial_trigger_element = getMappedElement(getFile16BitBE(file));
3129 ei->change->explode = getFile8Bit(file);
3130 ei->change->use_target_content = getFile8Bit(file);
3131 ei->change->only_if_complete = getFile8Bit(file);
3132 ei->change->use_random_replace = getFile8Bit(file);
3134 ei->change->random_percentage = getFile8Bit(file);
3135 ei->change->replace_when = getFile8Bit(file);
3137 for (y = 0; y < 3; y++)
3138 for (x = 0; x < 3; x++)
3139 ei->change->target_content.e[x][y] =
3140 getMappedElement(getFile16BitBE(file));
3142 ei->slippery_type = getFile8Bit(file);
3144 // some free bytes for future properties and padding
3145 ReadUnusedBytesFromFile(file, LEVEL_CPART_CUS3_UNUSED);
3147 // mark that this custom element has been modified
3148 ei->modified_settings = TRUE;
3151 level->file_has_custom_elements = TRUE;
3156 static int LoadLevel_CUS4(File *file, int chunk_size, struct LevelInfo *level)
3158 struct ElementInfo *ei;
3159 int chunk_size_expected;
3163 // ---------- custom element base property values (96 bytes) ----------------
3165 element = getMappedElement(getFile16BitBE(file));
3167 if (!IS_CUSTOM_ELEMENT(element))
3169 Warn("invalid custom element number %d", element);
3171 ReadUnusedBytesFromFile(file, chunk_size - 2);
3176 ei = &element_info[element];
3178 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
3179 ei->description[i] = getFile8Bit(file);
3180 ei->description[MAX_ELEMENT_NAME_LEN] = 0;
3182 ei->properties[EP_BITFIELD_BASE_NR] = getFile32BitBE(file);
3184 ReadUnusedBytesFromFile(file, 4); // reserved for more base properties
3186 ei->num_change_pages = getFile8Bit(file);
3188 chunk_size_expected = LEVEL_CHUNK_CUS4_SIZE(ei->num_change_pages);
3189 if (chunk_size_expected != chunk_size)
3191 ReadUnusedBytesFromFile(file, chunk_size - 43);
3192 return chunk_size_expected;
3195 ei->ce_value_fixed_initial = getFile16BitBE(file);
3196 ei->ce_value_random_initial = getFile16BitBE(file);
3197 ei->use_last_ce_value = getFile8Bit(file);
3199 ei->use_gfx_element = getFile8Bit(file);
3200 ei->gfx_element_initial = getMappedElement(getFile16BitBE(file));
3202 ei->collect_score_initial = getFile8Bit(file);
3203 ei->collect_count_initial = getFile8Bit(file);
3205 ei->drop_delay_fixed = getFile8Bit(file);
3206 ei->push_delay_fixed = getFile8Bit(file);
3207 ei->drop_delay_random = getFile8Bit(file);
3208 ei->push_delay_random = getFile8Bit(file);
3209 ei->move_delay_fixed = getFile16BitBE(file);
3210 ei->move_delay_random = getFile16BitBE(file);
3212 // bits 0 - 15 of "move_pattern" ...
3213 ei->move_pattern = getFile16BitBE(file);
3214 ei->move_direction_initial = getFile8Bit(file);
3215 ei->move_stepsize = getFile8Bit(file);
3217 ei->slippery_type = getFile8Bit(file);
3219 for (y = 0; y < 3; y++)
3220 for (x = 0; x < 3; x++)
3221 ei->content.e[x][y] = getMappedElement(getFile16BitBE(file));
3223 ei->move_enter_element = getMappedElement(getFile16BitBE(file));
3224 ei->move_leave_element = getMappedElement(getFile16BitBE(file));
3225 ei->move_leave_type = getFile8Bit(file);
3227 // ... bits 16 - 31 of "move_pattern" (not nice, but downward compatible)
3228 ei->move_pattern |= (getFile16BitBE(file) << 16);
3230 ei->access_direction = getFile8Bit(file);
3232 ei->explosion_delay = getFile8Bit(file);
3233 ei->ignition_delay = getFile8Bit(file);
3234 ei->explosion_type = getFile8Bit(file);
3236 // some free bytes for future custom property values and padding
3237 ReadUnusedBytesFromFile(file, 1);
3239 // ---------- change page property values (48 bytes) ------------------------
3241 setElementChangePages(ei, ei->num_change_pages);
3243 for (i = 0; i < ei->num_change_pages; i++)
3245 struct ElementChangeInfo *change = &ei->change_page[i];
3246 unsigned int event_bits;
3248 // always start with reliable default values
3249 setElementChangeInfoToDefaults(change);
3251 // bits 0 - 31 of "has_event[]" ...
3252 event_bits = getFile32BitBE(file);
3253 for (j = 0; j < MIN(NUM_CHANGE_EVENTS, 32); j++)
3254 if (event_bits & (1u << j))
3255 change->has_event[j] = TRUE;
3257 change->target_element = getMappedElement(getFile16BitBE(file));
3259 change->delay_fixed = getFile16BitBE(file);
3260 change->delay_random = getFile16BitBE(file);
3261 change->delay_frames = getFile16BitBE(file);
3263 change->initial_trigger_element = getMappedElement(getFile16BitBE(file));
3265 change->explode = getFile8Bit(file);
3266 change->use_target_content = getFile8Bit(file);
3267 change->only_if_complete = getFile8Bit(file);
3268 change->use_random_replace = getFile8Bit(file);
3270 change->random_percentage = getFile8Bit(file);
3271 change->replace_when = getFile8Bit(file);
3273 for (y = 0; y < 3; y++)
3274 for (x = 0; x < 3; x++)
3275 change->target_content.e[x][y]= getMappedElement(getFile16BitBE(file));
3277 change->can_change = getFile8Bit(file);
3279 change->trigger_side = getFile8Bit(file);
3281 change->trigger_player = getFile8Bit(file);
3282 change->trigger_page = getFile8Bit(file);
3284 change->trigger_page = (change->trigger_page == CH_PAGE_ANY_FILE ?
3285 CH_PAGE_ANY : (1 << change->trigger_page));
3287 change->has_action = getFile8Bit(file);
3288 change->action_type = getFile8Bit(file);
3289 change->action_mode = getFile8Bit(file);
3290 change->action_arg = getFile16BitBE(file);
3292 // ... bits 32 - 39 of "has_event[]" (not nice, but downward compatible)
3293 event_bits = getFile8Bit(file);
3294 for (j = 32; j < NUM_CHANGE_EVENTS; j++)
3295 if (event_bits & (1u << (j - 32)))
3296 change->has_event[j] = TRUE;
3299 // mark this custom element as modified
3300 ei->modified_settings = TRUE;
3302 level->file_has_custom_elements = TRUE;
3307 static int LoadLevel_GRP1(File *file, int chunk_size, struct LevelInfo *level)
3309 struct ElementInfo *ei;
3310 struct ElementGroupInfo *group;
3314 element = getMappedElement(getFile16BitBE(file));
3316 if (!IS_GROUP_ELEMENT(element))
3318 Warn("invalid group element number %d", element);
3320 ReadUnusedBytesFromFile(file, chunk_size - 2);
3325 ei = &element_info[element];
3327 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
3328 ei->description[i] = getFile8Bit(file);
3329 ei->description[MAX_ELEMENT_NAME_LEN] = 0;
3331 group = element_info[element].group;
3333 group->num_elements = getFile8Bit(file);
3335 ei->use_gfx_element = getFile8Bit(file);
3336 ei->gfx_element_initial = getMappedElement(getFile16BitBE(file));
3338 group->choice_mode = getFile8Bit(file);
3340 // some free bytes for future values and padding
3341 ReadUnusedBytesFromFile(file, 3);
3343 for (i = 0; i < MAX_ELEMENTS_IN_GROUP; i++)
3344 group->element[i] = getMappedElement(getFile16BitBE(file));
3346 // mark this group element as modified
3347 element_info[element].modified_settings = TRUE;
3349 level->file_has_custom_elements = TRUE;
3354 static int LoadLevel_MicroChunk(File *file, struct LevelFileConfigInfo *conf,
3355 int element, int real_element)
3357 int micro_chunk_size = 0;
3358 int conf_type = getFile8Bit(file);
3359 int byte_mask = conf_type & CONF_MASK_BYTES;
3360 boolean element_found = FALSE;
3363 micro_chunk_size += 1;
3365 if (byte_mask == CONF_MASK_MULTI_BYTES)
3367 int num_bytes = getFile16BitBE(file);
3368 byte *buffer = checked_malloc(num_bytes);
3370 ReadBytesFromFile(file, buffer, num_bytes);
3372 for (i = 0; conf[i].data_type != -1; i++)
3374 if (conf[i].element == element &&
3375 conf[i].conf_type == conf_type)
3377 int data_type = conf[i].data_type;
3378 int num_entities = num_bytes / CONF_ENTITY_NUM_BYTES(data_type);
3379 int max_num_entities = conf[i].max_num_entities;
3381 if (num_entities > max_num_entities)
3383 Warn("truncating number of entities for element %d from %d to %d",
3384 element, num_entities, max_num_entities);
3386 num_entities = max_num_entities;
3389 if (num_entities == 0 && (data_type == TYPE_ELEMENT_LIST ||
3390 data_type == TYPE_CONTENT_LIST))
3392 // for element and content lists, zero entities are not allowed
3393 Warn("found empty list of entities for element %d", element);
3395 // do not set "num_entities" here to prevent reading behind buffer
3397 *(int *)(conf[i].num_entities) = 1; // at least one is required
3401 *(int *)(conf[i].num_entities) = num_entities;
3404 element_found = TRUE;
3406 if (data_type == TYPE_STRING)
3408 char *string = (char *)(conf[i].value);
3411 for (j = 0; j < max_num_entities; j++)
3412 string[j] = (j < num_entities ? buffer[j] : '\0');
3414 else if (data_type == TYPE_ELEMENT_LIST)
3416 int *element_array = (int *)(conf[i].value);
3419 for (j = 0; j < num_entities; j++)
3421 getMappedElement(CONF_ELEMENTS_ELEMENT(buffer, j));
3423 else if (data_type == TYPE_CONTENT_LIST)
3425 struct Content *content= (struct Content *)(conf[i].value);
3428 for (c = 0; c < num_entities; c++)
3429 for (y = 0; y < 3; y++)
3430 for (x = 0; x < 3; x++)
3431 content[c].e[x][y] =
3432 getMappedElement(CONF_CONTENTS_ELEMENT(buffer, c, x, y));
3435 element_found = FALSE;
3441 checked_free(buffer);
3443 micro_chunk_size += 2 + num_bytes;
3445 else // constant size configuration data (1, 2 or 4 bytes)
3447 int value = (byte_mask == CONF_MASK_1_BYTE ? getFile8Bit (file) :
3448 byte_mask == CONF_MASK_2_BYTE ? getFile16BitBE(file) :
3449 byte_mask == CONF_MASK_4_BYTE ? getFile32BitBE(file) : 0);
3451 for (i = 0; conf[i].data_type != -1; i++)
3453 if (conf[i].element == element &&
3454 conf[i].conf_type == conf_type)
3456 int data_type = conf[i].data_type;
3458 if (data_type == TYPE_ELEMENT)
3459 value = getMappedElement(value);
3461 if (data_type == TYPE_BOOLEAN)
3462 *(boolean *)(conf[i].value) = (value ? TRUE : FALSE);
3464 *(int *) (conf[i].value) = value;
3466 element_found = TRUE;
3472 micro_chunk_size += CONF_VALUE_NUM_BYTES(byte_mask);
3477 char *error_conf_chunk_bytes =
3478 (byte_mask == CONF_MASK_1_BYTE ? "CONF_VALUE_8_BIT" :
3479 byte_mask == CONF_MASK_2_BYTE ? "CONF_VALUE_16_BIT" :
3480 byte_mask == CONF_MASK_4_BYTE ? "CONF_VALUE_32_BIT" :"CONF_VALUE_BYTES");
3481 int error_conf_chunk_token = conf_type & CONF_MASK_TOKEN;
3482 int error_element = real_element;
3484 Warn("cannot load micro chunk '%s(%d)' value for element %d ['%s']",
3485 error_conf_chunk_bytes, error_conf_chunk_token,
3486 error_element, EL_NAME(error_element));
3489 return micro_chunk_size;
3492 static int LoadLevel_INFO(File *file, int chunk_size, struct LevelInfo *level)
3494 int real_chunk_size = 0;
3496 li = *level; // copy level data into temporary buffer
3498 while (!checkEndOfFile(file))
3500 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_INFO, -1, -1);
3502 if (real_chunk_size >= chunk_size)
3506 *level = li; // copy temporary buffer back to level data
3508 return real_chunk_size;
3511 static int LoadLevel_CONF(File *file, int chunk_size, struct LevelInfo *level)
3513 int real_chunk_size = 0;
3515 li = *level; // copy level data into temporary buffer
3517 while (!checkEndOfFile(file))
3519 int element = getMappedElement(getFile16BitBE(file));
3521 real_chunk_size += 2;
3522 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_CONF,
3524 if (real_chunk_size >= chunk_size)
3528 *level = li; // copy temporary buffer back to level data
3530 return real_chunk_size;
3533 static int LoadLevel_ELEM(File *file, int chunk_size, struct LevelInfo *level)
3535 int real_chunk_size = 0;
3537 li = *level; // copy level data into temporary buffer
3539 while (!checkEndOfFile(file))
3541 int element = getMappedElement(getFile16BitBE(file));
3543 real_chunk_size += 2;
3544 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_ELEM,
3546 if (real_chunk_size >= chunk_size)
3550 *level = li; // copy temporary buffer back to level data
3552 return real_chunk_size;
3555 static int LoadLevel_NOTE(File *file, int chunk_size, struct LevelInfo *level)
3557 int element = getMappedElement(getFile16BitBE(file));
3558 int envelope_nr = element - EL_ENVELOPE_1;
3559 int real_chunk_size = 2;
3561 xx_envelope = level->envelope[envelope_nr]; // copy into temporary buffer
3563 while (!checkEndOfFile(file))
3565 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_NOTE,
3568 if (real_chunk_size >= chunk_size)
3572 level->envelope[envelope_nr] = xx_envelope; // copy from temporary buffer
3574 return real_chunk_size;
3577 static int LoadLevel_CUSX(File *file, int chunk_size, struct LevelInfo *level)
3579 int element = getMappedElement(getFile16BitBE(file));
3580 int real_chunk_size = 2;
3581 struct ElementInfo *ei = &element_info[element];
3584 xx_ei = *ei; // copy element data into temporary buffer
3586 xx_ei.num_change_pages = -1;
3588 while (!checkEndOfFile(file))
3590 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_CUSX_base,
3592 if (xx_ei.num_change_pages != -1)
3595 if (real_chunk_size >= chunk_size)
3601 if (ei->num_change_pages == -1)
3603 Warn("LoadLevel_CUSX(): missing 'num_change_pages' for '%s'",
3606 ei->num_change_pages = 1;
3608 setElementChangePages(ei, 1);
3609 setElementChangeInfoToDefaults(ei->change);
3611 return real_chunk_size;
3614 // initialize number of change pages stored for this custom element
3615 setElementChangePages(ei, ei->num_change_pages);
3616 for (i = 0; i < ei->num_change_pages; i++)
3617 setElementChangeInfoToDefaults(&ei->change_page[i]);
3619 // start with reading properties for the first change page
3620 xx_current_change_page = 0;
3622 while (!checkEndOfFile(file))
3624 // level file might contain invalid change page number
3625 if (xx_current_change_page >= ei->num_change_pages)
3628 struct ElementChangeInfo *change = &ei->change_page[xx_current_change_page];
3630 xx_change = *change; // copy change data into temporary buffer
3632 resetEventBits(); // reset bits; change page might have changed
3634 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_CUSX_change,
3637 *change = xx_change;
3639 setEventFlagsFromEventBits(change);
3641 if (real_chunk_size >= chunk_size)
3645 level->file_has_custom_elements = TRUE;
3647 return real_chunk_size;
3650 static int LoadLevel_GRPX(File *file, int chunk_size, struct LevelInfo *level)
3652 int element = getMappedElement(getFile16BitBE(file));
3653 int real_chunk_size = 2;
3654 struct ElementInfo *ei = &element_info[element];
3655 struct ElementGroupInfo *group = ei->group;
3660 xx_ei = *ei; // copy element data into temporary buffer
3661 xx_group = *group; // copy group data into temporary buffer
3663 while (!checkEndOfFile(file))
3665 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_GRPX,
3668 if (real_chunk_size >= chunk_size)
3675 level->file_has_custom_elements = TRUE;
3677 return real_chunk_size;
3680 static int LoadLevel_EMPX(File *file, int chunk_size, struct LevelInfo *level)
3682 int element = getMappedElement(getFile16BitBE(file));
3683 int real_chunk_size = 2;
3684 struct ElementInfo *ei = &element_info[element];
3686 xx_ei = *ei; // copy element data into temporary buffer
3688 while (!checkEndOfFile(file))
3690 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_EMPX,
3693 if (real_chunk_size >= chunk_size)
3699 level->file_has_custom_elements = TRUE;
3701 return real_chunk_size;
3704 static void LoadLevelFromFileInfo_RND(struct LevelInfo *level,
3705 struct LevelFileInfo *level_file_info,
3706 boolean level_info_only)
3708 char *filename = level_file_info->filename;
3709 char cookie[MAX_LINE_LEN];
3710 char chunk_name[CHUNK_ID_LEN + 1];
3714 if (!(file = openFile(filename, MODE_READ)))
3716 level->no_valid_file = TRUE;
3717 level->no_level_file = TRUE;
3719 if (level_info_only)
3722 Warn("cannot read level '%s' -- using empty level", filename);
3724 if (!setup.editor.use_template_for_new_levels)
3727 // if level file not found, try to initialize level data from template
3728 filename = getGlobalLevelTemplateFilename();
3730 if (!(file = openFile(filename, MODE_READ)))
3733 // default: for empty levels, use level template for custom elements
3734 level->use_custom_template = TRUE;
3736 level->no_valid_file = FALSE;
3739 getFileChunkBE(file, chunk_name, NULL);
3740 if (strEqual(chunk_name, "RND1"))
3742 getFile32BitBE(file); // not used
3744 getFileChunkBE(file, chunk_name, NULL);
3745 if (!strEqual(chunk_name, "CAVE"))
3747 level->no_valid_file = TRUE;
3749 Warn("unknown format of level file '%s'", filename);
3756 else // check for pre-2.0 file format with cookie string
3758 strcpy(cookie, chunk_name);
3759 if (getStringFromFile(file, &cookie[4], MAX_LINE_LEN - 4) == NULL)
3761 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
3762 cookie[strlen(cookie) - 1] = '\0';
3764 if (!checkCookieString(cookie, LEVEL_COOKIE_TMPL))
3766 level->no_valid_file = TRUE;
3768 Warn("unknown format of level file '%s'", filename);
3775 if ((level->file_version = getFileVersionFromCookieString(cookie)) == -1)
3777 level->no_valid_file = TRUE;
3779 Warn("unsupported version of level file '%s'", filename);
3786 // pre-2.0 level files have no game version, so use file version here
3787 level->game_version = level->file_version;
3790 if (level->file_version < FILE_VERSION_1_2)
3792 // level files from versions before 1.2.0 without chunk structure
3793 LoadLevel_HEAD(file, LEVEL_CHUNK_HEAD_SIZE, level);
3794 LoadLevel_BODY(file, level->fieldx * level->fieldy, level);
3802 int (*loader)(File *, int, struct LevelInfo *);
3806 { "VERS", LEVEL_CHUNK_VERS_SIZE, LoadLevel_VERS },
3807 { "DATE", LEVEL_CHUNK_DATE_SIZE, LoadLevel_DATE },
3808 { "HEAD", LEVEL_CHUNK_HEAD_SIZE, LoadLevel_HEAD },
3809 { "NAME", LEVEL_CHUNK_NAME_SIZE, LoadLevel_NAME },
3810 { "AUTH", LEVEL_CHUNK_AUTH_SIZE, LoadLevel_AUTH },
3811 { "INFO", -1, LoadLevel_INFO },
3812 { "BODY", -1, LoadLevel_BODY },
3813 { "CONT", -1, LoadLevel_CONT },
3814 { "CNT2", LEVEL_CHUNK_CNT2_SIZE, LoadLevel_CNT2 },
3815 { "CNT3", -1, LoadLevel_CNT3 },
3816 { "CUS1", -1, LoadLevel_CUS1 },
3817 { "CUS2", -1, LoadLevel_CUS2 },
3818 { "CUS3", -1, LoadLevel_CUS3 },
3819 { "CUS4", -1, LoadLevel_CUS4 },
3820 { "GRP1", -1, LoadLevel_GRP1 },
3821 { "CONF", -1, LoadLevel_CONF },
3822 { "ELEM", -1, LoadLevel_ELEM },
3823 { "NOTE", -1, LoadLevel_NOTE },
3824 { "CUSX", -1, LoadLevel_CUSX },
3825 { "GRPX", -1, LoadLevel_GRPX },
3826 { "EMPX", -1, LoadLevel_EMPX },
3831 while (getFileChunkBE(file, chunk_name, &chunk_size))
3835 while (chunk_info[i].name != NULL &&
3836 !strEqual(chunk_name, chunk_info[i].name))
3839 if (chunk_info[i].name == NULL)
3841 Warn("unknown chunk '%s' in level file '%s'",
3842 chunk_name, filename);
3844 ReadUnusedBytesFromFile(file, chunk_size);
3846 else if (chunk_info[i].size != -1 &&
3847 chunk_info[i].size != chunk_size)
3849 Warn("wrong size (%d) of chunk '%s' in level file '%s'",
3850 chunk_size, chunk_name, filename);
3852 ReadUnusedBytesFromFile(file, chunk_size);
3856 // call function to load this level chunk
3857 int chunk_size_expected =
3858 (chunk_info[i].loader)(file, chunk_size, level);
3860 if (chunk_size_expected < 0)
3862 Warn("error reading chunk '%s' in level file '%s'",
3863 chunk_name, filename);
3868 // the size of some chunks cannot be checked before reading other
3869 // chunks first (like "HEAD" and "BODY") that contain some header
3870 // information, so check them here
3871 if (chunk_size_expected != chunk_size)
3873 Warn("wrong size (%d) of chunk '%s' in level file '%s'",
3874 chunk_size, chunk_name, filename);
3886 // ----------------------------------------------------------------------------
3887 // functions for loading BD level
3888 // ----------------------------------------------------------------------------
3890 static void CopyNativeLevel_RND_to_BD(struct LevelInfo *level)
3892 struct LevelInfo_BD *level_bd = level->native_bd_level;
3893 GdCave *cave = NULL; // will be changed below
3894 int cave_w = MIN(level->fieldx, MAX_PLAYFIELD_WIDTH);
3895 int cave_h = MIN(level->fieldy, MAX_PLAYFIELD_HEIGHT);
3898 setLevelInfoToDefaults_BD_Ext(cave_w, cave_h);
3900 // cave and map newly allocated when set to defaults above
3901 cave = level_bd->cave;
3904 cave->intermission = level->bd_intermission;
3907 cave->level_time[0] = level->time;
3908 cave->level_diamonds[0] = level->gems_needed;
3911 cave->scheduling = level->bd_scheduling_type;
3912 cave->pal_timing = level->bd_pal_timing;
3913 cave->level_speed[0] = level->bd_cycle_delay_ms;
3914 cave->level_ckdelay[0] = level->bd_cycle_delay_c64;
3915 cave->level_hatching_delay_frame[0] = level->bd_hatching_delay_cycles;
3916 cave->level_hatching_delay_time[0] = level->bd_hatching_delay_seconds;
3919 cave->level_timevalue[0] = level->score[SC_TIME_BONUS];
3920 cave->diamond_value = level->score[SC_EMERALD];
3921 cave->extra_diamond_value = level->score[SC_DIAMOND_EXTRA];
3923 // compatibility settings
3924 cave->lineshift = level->bd_line_shifting_borders;
3925 cave->border_scan_first_and_last = level->bd_scan_first_and_last_row;
3926 cave->short_explosions = level->bd_short_explosions;
3927 cave->gravity_affects_all = level->bd_gravity_affects_all;
3929 // player properties
3930 cave->diagonal_movements = level->bd_diagonal_movements;
3931 cave->active_is_first_found = level->bd_topmost_player_active;
3932 cave->pushing_stone_prob = level->bd_pushing_prob * 10000;
3933 cave->pushing_stone_prob_sweet = level->bd_pushing_prob_with_sweet * 10000;
3934 cave->mega_stones_pushable_with_sweet = level->bd_push_mega_rock_with_sweet;
3935 cave->snap_element = map_element_RND_to_BD(level->bd_snap_element);
3937 // element properties
3938 cave->level_bonus_time[0] = level->bd_clock_extra_time;
3939 cave->voodoo_collects_diamonds = level->bd_voodoo_collects_diamonds;
3940 cave->voodoo_any_hurt_kills_player = level->bd_voodoo_hurt_kills_player;
3941 cave->voodoo_dies_by_stone = level->bd_voodoo_dies_by_rock;
3942 cave->voodoo_disappear_in_explosion = level->bd_voodoo_vanish_by_explosion;
3943 cave->level_penalty_time[0] = level->bd_voodoo_penalty_time;
3944 cave->level_magic_wall_time[0] = level->time_magic_wall;
3945 cave->magic_timer_wait_for_hatching = level->bd_magic_wall_wait_hatching;
3946 cave->magic_wall_stops_amoeba = level->bd_magic_wall_stops_amoeba;
3947 cave->amoeba_timer_wait_for_hatching = level->bd_amoeba_wait_for_hatching;
3948 cave->amoeba_timer_started_immediately= level->bd_amoeba_start_immediately;
3949 cave->amoeba_2_explodes_by_amoeba = level->bd_amoeba_2_explode_by_amoeba;
3950 cave->level_amoeba_threshold[0] = level->bd_amoeba_threshold_too_big;
3951 cave->level_amoeba_time[0] = level->bd_amoeba_slow_growth_time;
3952 cave->amoeba_growth_prob = level->bd_amoeba_slow_growth_rate * 10000;
3953 cave->amoeba_fast_growth_prob = level->bd_amoeba_fast_growth_rate * 10000;
3954 cave->level_amoeba_2_threshold[0] = level->bd_amoeba_2_threshold_too_big;
3955 cave->level_amoeba_2_time[0] = level->bd_amoeba_2_slow_growth_time;
3956 cave->amoeba_2_growth_prob = level->bd_amoeba_2_slow_growth_rate * 10000;
3957 cave->amoeba_2_fast_growth_prob = level->bd_amoeba_2_fast_growth_rate * 10000;
3959 cave->amoeba_too_big_effect = map_element_RND_to_BD(level->bd_amoeba_content_too_big);
3960 cave->amoeba_enclosed_effect = map_element_RND_to_BD(level->bd_amoeba_content_enclosed);
3961 cave->amoeba_2_too_big_effect = map_element_RND_to_BD(level->bd_amoeba_2_content_too_big);
3962 cave->amoeba_2_enclosed_effect = map_element_RND_to_BD(level->bd_amoeba_2_content_enclosed);
3963 cave->amoeba_2_explosion_effect = map_element_RND_to_BD(level->bd_amoeba_2_content_exploding);
3964 cave->amoeba_2_looks_like = map_element_RND_to_BD(level->bd_amoeba_2_content_looks_like);
3967 strncpy(cave->name, level->name, sizeof(GdString));
3968 cave->name[sizeof(GdString) - 1] = '\0';
3970 // playfield elements
3971 for (x = 0; x < cave->w; x++)
3972 for (y = 0; y < cave->h; y++)
3973 cave->map[y][x] = map_element_RND_to_BD(level->field[x][y]);
3976 static void CopyNativeLevel_BD_to_RND(struct LevelInfo *level)
3978 struct LevelInfo_BD *level_bd = level->native_bd_level;
3979 GdCave *cave = level_bd->cave;
3980 int bd_level_nr = level_bd->level_nr;
3983 level->fieldx = MIN(cave->w, MAX_LEV_FIELDX);
3984 level->fieldy = MIN(cave->h, MAX_LEV_FIELDY);
3987 level->bd_intermission = cave->intermission;
3990 level->time = cave->level_time[bd_level_nr];
3991 level->gems_needed = cave->level_diamonds[bd_level_nr];
3994 level->bd_scheduling_type = cave->scheduling;
3995 level->bd_pal_timing = cave->pal_timing;
3996 level->bd_cycle_delay_ms = cave->level_speed[bd_level_nr];
3997 level->bd_cycle_delay_c64 = cave->level_ckdelay[bd_level_nr];
3998 level->bd_hatching_delay_cycles = cave->level_hatching_delay_frame[bd_level_nr];
3999 level->bd_hatching_delay_seconds = cave->level_hatching_delay_time[bd_level_nr];
4002 level->score[SC_TIME_BONUS] = cave->level_timevalue[bd_level_nr];
4003 level->score[SC_EMERALD] = cave->diamond_value;
4004 level->score[SC_DIAMOND_EXTRA] = cave->extra_diamond_value;
4006 // compatibility settings
4007 level->bd_line_shifting_borders = cave->lineshift;
4008 level->bd_scan_first_and_last_row = cave->border_scan_first_and_last;
4009 level->bd_short_explosions = cave->short_explosions;
4010 level->bd_gravity_affects_all = cave->gravity_affects_all;
4012 // player properties
4013 level->bd_diagonal_movements = cave->diagonal_movements;
4014 level->bd_topmost_player_active = cave->active_is_first_found;
4015 level->bd_pushing_prob = cave->pushing_stone_prob / 10000;
4016 level->bd_pushing_prob_with_sweet = cave->pushing_stone_prob_sweet / 10000;
4017 level->bd_push_mega_rock_with_sweet = cave->mega_stones_pushable_with_sweet;
4018 level->bd_snap_element = map_element_BD_to_RND(cave->snap_element);
4020 // element properties
4021 level->bd_clock_extra_time = cave->level_bonus_time[0];
4022 level->bd_voodoo_collects_diamonds = cave->voodoo_collects_diamonds;
4023 level->bd_voodoo_hurt_kills_player = cave->voodoo_any_hurt_kills_player;
4024 level->bd_voodoo_dies_by_rock = cave->voodoo_dies_by_stone;
4025 level->bd_voodoo_vanish_by_explosion = cave->voodoo_disappear_in_explosion;
4026 level->bd_voodoo_penalty_time = cave->level_penalty_time[0];
4027 level->time_magic_wall = cave->level_magic_wall_time[bd_level_nr];
4028 level->bd_magic_wall_wait_hatching = cave->magic_timer_wait_for_hatching;
4029 level->bd_magic_wall_stops_amoeba = cave->magic_wall_stops_amoeba;
4030 level->bd_amoeba_wait_for_hatching = cave->amoeba_timer_wait_for_hatching;
4031 level->bd_amoeba_start_immediately = cave->amoeba_timer_started_immediately;
4032 level->bd_amoeba_2_explode_by_amoeba = cave->amoeba_2_explodes_by_amoeba;
4033 level->bd_amoeba_threshold_too_big = cave->level_amoeba_threshold[0];
4034 level->bd_amoeba_slow_growth_time = cave->level_amoeba_time[0];
4035 level->bd_amoeba_slow_growth_rate = cave->amoeba_growth_prob / 10000;
4036 level->bd_amoeba_fast_growth_rate = cave->amoeba_fast_growth_prob / 10000;
4037 level->bd_amoeba_2_threshold_too_big = cave->level_amoeba_2_threshold[0];
4038 level->bd_amoeba_2_slow_growth_time = cave->level_amoeba_2_time[0];
4039 level->bd_amoeba_2_slow_growth_rate = cave->amoeba_2_growth_prob / 10000;
4040 level->bd_amoeba_2_fast_growth_rate = cave->amoeba_2_fast_growth_prob / 10000;
4042 level->bd_amoeba_content_too_big = map_element_BD_to_RND(cave->amoeba_too_big_effect);
4043 level->bd_amoeba_content_enclosed = map_element_BD_to_RND(cave->amoeba_enclosed_effect);
4044 level->bd_amoeba_2_content_too_big = map_element_BD_to_RND(cave->amoeba_2_too_big_effect);
4045 level->bd_amoeba_2_content_enclosed = map_element_BD_to_RND(cave->amoeba_2_enclosed_effect);
4046 level->bd_amoeba_2_content_exploding = map_element_BD_to_RND(cave->amoeba_2_explosion_effect);
4047 level->bd_amoeba_2_content_looks_like = map_element_BD_to_RND(cave->amoeba_2_looks_like);
4050 char *cave_name = getStringPrint("%s / %d", cave->name, bd_level_nr + 1);
4052 strncpy(level->name, cave_name, MAX_LEVEL_NAME_LEN);
4053 level->name[MAX_LEVEL_NAME_LEN] = '\0';
4055 // playfield elements
4056 for (x = 0; x < level->fieldx; x++)
4057 for (y = 0; y < level->fieldy; y++)
4058 level->field[x][y] = map_element_BD_to_RND(cave->map[y][x]);
4060 checked_free(cave_name);
4063 static void setTapeInfoToDefaults(void);
4065 static void CopyNativeTape_BD_to_RND(struct LevelInfo *level)
4067 struct LevelInfo_BD *level_bd = level->native_bd_level;
4068 GdCave *cave = level_bd->cave;
4069 GdReplay *replay = level_bd->replay;
4075 // always start with reliable default values
4076 setTapeInfoToDefaults();
4078 tape.level_nr = level_nr; // (currently not used)
4079 tape.random_seed = replay->seed;
4081 TapeSetDateFromIsoDateString(replay->date);
4084 tape.pos[tape.counter].delay = 0;
4086 tape.bd_replay = TRUE;
4088 // all time calculations only used to display approximate tape time
4089 int cave_speed = cave->speed;
4090 int milliseconds_game = 0;
4091 int milliseconds_elapsed = 20;
4093 for (i = 0; i < replay->movements->len; i++)
4095 int replay_action = replay->movements->data[i];
4096 int tape_action = map_action_BD_to_RND(replay_action);
4097 byte action[MAX_TAPE_ACTIONS] = { tape_action };
4098 boolean success = 0;
4102 success = TapeAddAction(action);
4104 milliseconds_game += milliseconds_elapsed;
4106 if (milliseconds_game >= cave_speed)
4108 milliseconds_game -= cave_speed;
4115 tape.pos[tape.counter].delay = 0;
4116 tape.pos[tape.counter].action[0] = 0;
4120 Warn("BD replay truncated: size exceeds maximum tape size %d", MAX_TAPE_LEN);
4126 TapeHaltRecording();
4130 // ----------------------------------------------------------------------------
4131 // functions for loading EM level
4132 // ----------------------------------------------------------------------------
4134 static void CopyNativeLevel_RND_to_EM(struct LevelInfo *level)
4136 static int ball_xy[8][2] =
4147 struct LevelInfo_EM *level_em = level->native_em_level;
4148 struct CAVE *cav = level_em->cav;
4151 cav->width = MIN(level->fieldx, MAX_PLAYFIELD_WIDTH);
4152 cav->height = MIN(level->fieldy, MAX_PLAYFIELD_HEIGHT);
4154 cav->time_seconds = level->time;
4155 cav->gems_needed = level->gems_needed;
4157 cav->emerald_score = level->score[SC_EMERALD];
4158 cav->diamond_score = level->score[SC_DIAMOND];
4159 cav->alien_score = level->score[SC_ROBOT];
4160 cav->tank_score = level->score[SC_SPACESHIP];
4161 cav->bug_score = level->score[SC_BUG];
4162 cav->eater_score = level->score[SC_YAMYAM];
4163 cav->nut_score = level->score[SC_NUT];
4164 cav->dynamite_score = level->score[SC_DYNAMITE];
4165 cav->key_score = level->score[SC_KEY];
4166 cav->exit_score = level->score[SC_TIME_BONUS];
4168 cav->num_eater_arrays = level->num_yamyam_contents;
4170 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
4171 for (y = 0; y < 3; y++)
4172 for (x = 0; x < 3; x++)
4173 cav->eater_array[i][y * 3 + x] =
4174 map_element_RND_to_EM_cave(level->yamyam_content[i].e[x][y]);
4176 cav->amoeba_time = level->amoeba_speed;
4177 cav->wonderwall_time = level->time_magic_wall;
4178 cav->wheel_time = level->time_wheel;
4180 cav->android_move_time = level->android_move_time;
4181 cav->android_clone_time = level->android_clone_time;
4182 cav->ball_random = level->ball_random;
4183 cav->ball_active = level->ball_active_initial;
4184 cav->ball_time = level->ball_time;
4185 cav->num_ball_arrays = level->num_ball_contents;
4187 cav->lenses_score = level->lenses_score;
4188 cav->magnify_score = level->magnify_score;
4189 cav->slurp_score = level->slurp_score;
4191 cav->lenses_time = level->lenses_time;
4192 cav->magnify_time = level->magnify_time;
4194 cav->wind_time = 9999;
4195 cav->wind_direction =
4196 map_direction_RND_to_EM(level->wind_direction_initial);
4198 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
4199 for (j = 0; j < 8; j++)
4200 cav->ball_array[i][j] =
4201 map_element_RND_to_EM_cave(level->ball_content[i].
4202 e[ball_xy[j][0]][ball_xy[j][1]]);
4204 map_android_clone_elements_RND_to_EM(level);
4206 // first fill the complete playfield with the empty space element
4207 for (y = 0; y < EM_MAX_CAVE_HEIGHT; y++)
4208 for (x = 0; x < EM_MAX_CAVE_WIDTH; x++)
4209 cav->cave[x][y] = Cblank;
4211 // then copy the real level contents from level file into the playfield
4212 for (y = 0; y < cav->height; y++) for (x = 0; x < cav->width; x++)
4214 int new_element = map_element_RND_to_EM_cave(level->field[x][y]);
4216 if (level->field[x][y] == EL_AMOEBA_DEAD)
4217 new_element = map_element_RND_to_EM_cave(EL_AMOEBA_WET);
4219 cav->cave[x][y] = new_element;
4222 for (i = 0; i < MAX_PLAYERS; i++)
4224 cav->player_x[i] = -1;
4225 cav->player_y[i] = -1;
4228 // initialize player positions and delete players from the playfield
4229 for (y = 0; y < cav->height; y++) for (x = 0; x < cav->width; x++)
4231 if (IS_PLAYER_ELEMENT(level->field[x][y]))
4233 int player_nr = GET_PLAYER_NR(level->field[x][y]);
4235 cav->player_x[player_nr] = x;
4236 cav->player_y[player_nr] = y;
4238 cav->cave[x][y] = map_element_RND_to_EM_cave(EL_EMPTY);
4243 static void CopyNativeLevel_EM_to_RND(struct LevelInfo *level)
4245 static int ball_xy[8][2] =
4256 struct LevelInfo_EM *level_em = level->native_em_level;
4257 struct CAVE *cav = level_em->cav;
4260 level->fieldx = MIN(cav->width, MAX_LEV_FIELDX);
4261 level->fieldy = MIN(cav->height, MAX_LEV_FIELDY);
4263 level->time = cav->time_seconds;
4264 level->gems_needed = cav->gems_needed;
4266 sprintf(level->name, "Level %d", level->file_info.nr);
4268 level->score[SC_EMERALD] = cav->emerald_score;
4269 level->score[SC_DIAMOND] = cav->diamond_score;
4270 level->score[SC_ROBOT] = cav->alien_score;
4271 level->score[SC_SPACESHIP] = cav->tank_score;
4272 level->score[SC_BUG] = cav->bug_score;
4273 level->score[SC_YAMYAM] = cav->eater_score;
4274 level->score[SC_NUT] = cav->nut_score;
4275 level->score[SC_DYNAMITE] = cav->dynamite_score;
4276 level->score[SC_KEY] = cav->key_score;
4277 level->score[SC_TIME_BONUS] = cav->exit_score;
4279 level->num_yamyam_contents = cav->num_eater_arrays;
4281 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
4282 for (y = 0; y < 3; y++)
4283 for (x = 0; x < 3; x++)
4284 level->yamyam_content[i].e[x][y] =
4285 map_element_EM_to_RND_cave(cav->eater_array[i][y * 3 + x]);
4287 level->amoeba_speed = cav->amoeba_time;
4288 level->time_magic_wall = cav->wonderwall_time;
4289 level->time_wheel = cav->wheel_time;
4291 level->android_move_time = cav->android_move_time;
4292 level->android_clone_time = cav->android_clone_time;
4293 level->ball_random = cav->ball_random;
4294 level->ball_active_initial = cav->ball_active;
4295 level->ball_time = cav->ball_time;
4296 level->num_ball_contents = cav->num_ball_arrays;
4298 level->lenses_score = cav->lenses_score;
4299 level->magnify_score = cav->magnify_score;
4300 level->slurp_score = cav->slurp_score;
4302 level->lenses_time = cav->lenses_time;
4303 level->magnify_time = cav->magnify_time;
4305 level->wind_direction_initial =
4306 map_direction_EM_to_RND(cav->wind_direction);
4308 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
4309 for (j = 0; j < 8; j++)
4310 level->ball_content[i].e[ball_xy[j][0]][ball_xy[j][1]] =
4311 map_element_EM_to_RND_cave(cav->ball_array[i][j]);
4313 map_android_clone_elements_EM_to_RND(level);
4315 // convert the playfield (some elements need special treatment)
4316 for (y = 0; y < level->fieldy; y++) for (x = 0; x < level->fieldx; x++)
4318 int new_element = map_element_EM_to_RND_cave(cav->cave[x][y]);
4320 if (new_element == EL_AMOEBA_WET && level->amoeba_speed == 0)
4321 new_element = EL_AMOEBA_DEAD;
4323 level->field[x][y] = new_element;
4326 for (i = 0; i < MAX_PLAYERS; i++)
4328 // in case of all players set to the same field, use the first player
4329 int nr = MAX_PLAYERS - i - 1;
4330 int jx = cav->player_x[nr];
4331 int jy = cav->player_y[nr];
4333 if (jx != -1 && jy != -1)
4334 level->field[jx][jy] = EL_PLAYER_1 + nr;
4337 // time score is counted for each 10 seconds left in Emerald Mine levels
4338 level->time_score_base = 10;
4342 // ----------------------------------------------------------------------------
4343 // functions for loading SP level
4344 // ----------------------------------------------------------------------------
4346 static void CopyNativeLevel_RND_to_SP(struct LevelInfo *level)
4348 struct LevelInfo_SP *level_sp = level->native_sp_level;
4349 LevelInfoType *header = &level_sp->header;
4352 level_sp->width = level->fieldx;
4353 level_sp->height = level->fieldy;
4355 for (x = 0; x < level->fieldx; x++)
4356 for (y = 0; y < level->fieldy; y++)
4357 level_sp->playfield[x][y] = map_element_RND_to_SP(level->field[x][y]);
4359 header->InitialGravity = (level->initial_player_gravity[0] ? 1 : 0);
4361 for (i = 0; i < SP_LEVEL_NAME_LEN; i++)
4362 header->LevelTitle[i] = level->name[i];
4363 // !!! NO STRING TERMINATION IN SUPAPLEX VB CODE YET -- FIX THIS !!!
4365 header->InfotronsNeeded = level->gems_needed;
4367 header->SpecialPortCount = 0;
4369 for (x = 0; x < level->fieldx; x++) for (y = 0; y < level->fieldy; y++)
4371 boolean gravity_port_found = FALSE;
4372 boolean gravity_port_valid = FALSE;
4373 int gravity_port_flag;
4374 int gravity_port_base_element;
4375 int element = level->field[x][y];
4377 if (element >= EL_SP_GRAVITY_ON_PORT_RIGHT &&
4378 element <= EL_SP_GRAVITY_ON_PORT_UP)
4380 gravity_port_found = TRUE;
4381 gravity_port_valid = TRUE;
4382 gravity_port_flag = 1;
4383 gravity_port_base_element = EL_SP_GRAVITY_ON_PORT_RIGHT;
4385 else if (element >= EL_SP_GRAVITY_OFF_PORT_RIGHT &&
4386 element <= EL_SP_GRAVITY_OFF_PORT_UP)
4388 gravity_port_found = TRUE;
4389 gravity_port_valid = TRUE;
4390 gravity_port_flag = 0;
4391 gravity_port_base_element = EL_SP_GRAVITY_OFF_PORT_RIGHT;
4393 else if (element >= EL_SP_GRAVITY_PORT_RIGHT &&
4394 element <= EL_SP_GRAVITY_PORT_UP)
4396 // change R'n'D style gravity inverting special port to normal port
4397 // (there are no gravity inverting ports in native Supaplex engine)
4399 gravity_port_found = TRUE;
4400 gravity_port_valid = FALSE;
4401 gravity_port_base_element = EL_SP_GRAVITY_PORT_RIGHT;
4404 if (gravity_port_found)
4406 if (gravity_port_valid &&
4407 header->SpecialPortCount < SP_MAX_SPECIAL_PORTS)
4409 SpecialPortType *port = &header->SpecialPort[header->SpecialPortCount];
4411 port->PortLocation = (y * level->fieldx + x) * 2;
4412 port->Gravity = gravity_port_flag;
4414 element += EL_SP_GRAVITY_PORT_RIGHT - gravity_port_base_element;
4416 header->SpecialPortCount++;
4420 // change special gravity port to normal port
4422 element += EL_SP_PORT_RIGHT - gravity_port_base_element;
4425 level_sp->playfield[x][y] = element - EL_SP_START;
4430 static void CopyNativeLevel_SP_to_RND(struct LevelInfo *level)
4432 struct LevelInfo_SP *level_sp = level->native_sp_level;
4433 LevelInfoType *header = &level_sp->header;
4434 boolean num_invalid_elements = 0;
4437 level->fieldx = level_sp->width;
4438 level->fieldy = level_sp->height;
4440 for (x = 0; x < level->fieldx; x++)
4442 for (y = 0; y < level->fieldy; y++)
4444 int element_old = level_sp->playfield[x][y];
4445 int element_new = getMappedElement(map_element_SP_to_RND(element_old));
4447 if (element_new == EL_UNKNOWN)
4449 num_invalid_elements++;
4451 Debug("level:native:SP", "invalid element %d at position %d, %d",
4455 level->field[x][y] = element_new;
4459 if (num_invalid_elements > 0)
4460 Warn("found %d invalid elements%s", num_invalid_elements,
4461 (!options.debug ? " (use '--debug' for more details)" : ""));
4463 for (i = 0; i < MAX_PLAYERS; i++)
4464 level->initial_player_gravity[i] =
4465 (header->InitialGravity == 1 ? TRUE : FALSE);
4467 // skip leading spaces
4468 for (i = 0; i < SP_LEVEL_NAME_LEN; i++)
4469 if (header->LevelTitle[i] != ' ')
4473 for (j = 0; i < SP_LEVEL_NAME_LEN; i++, j++)
4474 level->name[j] = header->LevelTitle[i];
4475 level->name[j] = '\0';
4477 // cut trailing spaces
4479 if (level->name[j - 1] == ' ' && level->name[j] == '\0')
4480 level->name[j - 1] = '\0';
4482 level->gems_needed = header->InfotronsNeeded;
4484 for (i = 0; i < header->SpecialPortCount; i++)
4486 SpecialPortType *port = &header->SpecialPort[i];
4487 int port_location = port->PortLocation;
4488 int gravity = port->Gravity;
4489 int port_x, port_y, port_element;
4491 port_x = (port_location / 2) % level->fieldx;
4492 port_y = (port_location / 2) / level->fieldx;
4494 if (port_x < 0 || port_x >= level->fieldx ||
4495 port_y < 0 || port_y >= level->fieldy)
4497 Warn("special port position (%d, %d) out of bounds", port_x, port_y);
4502 port_element = level->field[port_x][port_y];
4504 if (port_element < EL_SP_GRAVITY_PORT_RIGHT ||
4505 port_element > EL_SP_GRAVITY_PORT_UP)
4507 Warn("no special port at position (%d, %d)", port_x, port_y);
4512 // change previous (wrong) gravity inverting special port to either
4513 // gravity enabling special port or gravity disabling special port
4514 level->field[port_x][port_y] +=
4515 (gravity == 1 ? EL_SP_GRAVITY_ON_PORT_RIGHT :
4516 EL_SP_GRAVITY_OFF_PORT_RIGHT) - EL_SP_GRAVITY_PORT_RIGHT;
4519 // change special gravity ports without database entries to normal ports
4520 for (x = 0; x < level->fieldx; x++)
4521 for (y = 0; y < level->fieldy; y++)
4522 if (level->field[x][y] >= EL_SP_GRAVITY_PORT_RIGHT &&
4523 level->field[x][y] <= EL_SP_GRAVITY_PORT_UP)
4524 level->field[x][y] += EL_SP_PORT_RIGHT - EL_SP_GRAVITY_PORT_RIGHT;
4526 level->time = 0; // no time limit
4527 level->amoeba_speed = 0;
4528 level->time_magic_wall = 0;
4529 level->time_wheel = 0;
4530 level->amoeba_content = EL_EMPTY;
4532 // original Supaplex does not use score values -- rate by playing time
4533 for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
4534 level->score[i] = 0;
4536 level->rate_time_over_score = TRUE;
4538 // there are no yamyams in supaplex levels
4539 for (i = 0; i < level->num_yamyam_contents; i++)
4540 for (x = 0; x < 3; x++)
4541 for (y = 0; y < 3; y++)
4542 level->yamyam_content[i].e[x][y] = EL_EMPTY;
4545 static void CopyNativeTape_RND_to_SP(struct LevelInfo *level)
4547 struct LevelInfo_SP *level_sp = level->native_sp_level;
4548 struct DemoInfo_SP *demo = &level_sp->demo;
4551 // always start with reliable default values
4552 demo->is_available = FALSE;
4555 if (TAPE_IS_EMPTY(tape))
4558 demo->level_nr = tape.level_nr; // (currently not used)
4560 level_sp->header.DemoRandomSeed = tape.random_seed;
4564 for (i = 0; i < tape.length; i++)
4566 int demo_action = map_key_RND_to_SP(tape.pos[i].action[0]);
4567 int demo_repeat = tape.pos[i].delay;
4568 int demo_entries = (demo_repeat + 15) / 16;
4570 if (demo->length + demo_entries >= SP_MAX_TAPE_LEN)
4572 Warn("tape truncated: size exceeds maximum SP demo size %d",
4578 for (j = 0; j < demo_repeat / 16; j++)
4579 demo->data[demo->length++] = 0xf0 | demo_action;
4581 if (demo_repeat % 16)
4582 demo->data[demo->length++] = ((demo_repeat % 16 - 1) << 4) | demo_action;
4585 demo->is_available = TRUE;
4588 static void CopyNativeTape_SP_to_RND(struct LevelInfo *level)
4590 struct LevelInfo_SP *level_sp = level->native_sp_level;
4591 struct DemoInfo_SP *demo = &level_sp->demo;
4592 char *filename = level->file_info.filename;
4595 // always start with reliable default values
4596 setTapeInfoToDefaults();
4598 if (!demo->is_available)
4601 tape.level_nr = demo->level_nr; // (currently not used)
4602 tape.random_seed = level_sp->header.DemoRandomSeed;
4604 TapeSetDateFromEpochSeconds(getFileTimestampEpochSeconds(filename));
4607 tape.pos[tape.counter].delay = 0;
4609 for (i = 0; i < demo->length; i++)
4611 int demo_action = demo->data[i] & 0x0f;
4612 int demo_repeat = (demo->data[i] & 0xf0) >> 4;
4613 int tape_action = map_key_SP_to_RND(demo_action);
4614 int tape_repeat = demo_repeat + 1;
4615 byte action[MAX_TAPE_ACTIONS] = { tape_action };
4616 boolean success = 0;
4619 for (j = 0; j < tape_repeat; j++)
4620 success = TapeAddAction(action);
4624 Warn("SP demo truncated: size exceeds maximum tape size %d",
4631 TapeHaltRecording();
4635 // ----------------------------------------------------------------------------
4636 // functions for loading MM level
4637 // ----------------------------------------------------------------------------
4639 static void CopyNativeLevel_RND_to_MM(struct LevelInfo *level)
4641 struct LevelInfo_MM *level_mm = level->native_mm_level;
4644 level_mm->fieldx = MIN(level->fieldx, MM_MAX_PLAYFIELD_WIDTH);
4645 level_mm->fieldy = MIN(level->fieldy, MM_MAX_PLAYFIELD_HEIGHT);
4647 level_mm->time = level->time;
4648 level_mm->kettles_needed = level->gems_needed;
4649 level_mm->auto_count_kettles = level->auto_count_gems;
4651 level_mm->mm_laser_red = level->mm_laser_red;
4652 level_mm->mm_laser_green = level->mm_laser_green;
4653 level_mm->mm_laser_blue = level->mm_laser_blue;
4655 level_mm->df_laser_red = level->df_laser_red;
4656 level_mm->df_laser_green = level->df_laser_green;
4657 level_mm->df_laser_blue = level->df_laser_blue;
4659 strcpy(level_mm->name, level->name);
4660 strcpy(level_mm->author, level->author);
4662 level_mm->score[SC_EMERALD] = level->score[SC_EMERALD];
4663 level_mm->score[SC_PACMAN] = level->score[SC_PACMAN];
4664 level_mm->score[SC_KEY] = level->score[SC_KEY];
4665 level_mm->score[SC_TIME_BONUS] = level->score[SC_TIME_BONUS];
4666 level_mm->score[SC_ELEM_BONUS] = level->score[SC_ELEM_BONUS];
4668 level_mm->amoeba_speed = level->amoeba_speed;
4669 level_mm->time_fuse = level->mm_time_fuse;
4670 level_mm->time_bomb = level->mm_time_bomb;
4671 level_mm->time_ball = level->mm_time_ball;
4672 level_mm->time_block = level->mm_time_block;
4674 level_mm->num_ball_contents = level->num_mm_ball_contents;
4675 level_mm->ball_choice_mode = level->mm_ball_choice_mode;
4676 level_mm->rotate_ball_content = level->rotate_mm_ball_content;
4677 level_mm->explode_ball = level->explode_mm_ball;
4679 for (i = 0; i < level->num_mm_ball_contents; i++)
4680 level_mm->ball_content[i] =
4681 map_element_RND_to_MM(level->mm_ball_content[i]);
4683 for (x = 0; x < level->fieldx; x++)
4684 for (y = 0; y < level->fieldy; y++)
4686 level_mm->field[x][y] = map_element_RND_to_MM(level->field[x][y]);
4689 static void CopyNativeLevel_MM_to_RND(struct LevelInfo *level)
4691 struct LevelInfo_MM *level_mm = level->native_mm_level;
4694 level->fieldx = MIN(level_mm->fieldx, MAX_LEV_FIELDX);
4695 level->fieldy = MIN(level_mm->fieldy, MAX_LEV_FIELDY);
4697 level->time = level_mm->time;
4698 level->gems_needed = level_mm->kettles_needed;
4699 level->auto_count_gems = level_mm->auto_count_kettles;
4701 level->mm_laser_red = level_mm->mm_laser_red;
4702 level->mm_laser_green = level_mm->mm_laser_green;
4703 level->mm_laser_blue = level_mm->mm_laser_blue;
4705 level->df_laser_red = level_mm->df_laser_red;
4706 level->df_laser_green = level_mm->df_laser_green;
4707 level->df_laser_blue = level_mm->df_laser_blue;
4709 strcpy(level->name, level_mm->name);
4711 // only overwrite author from 'levelinfo.conf' if author defined in level
4712 if (!strEqual(level_mm->author, ANONYMOUS_NAME))
4713 strcpy(level->author, level_mm->author);
4715 level->score[SC_EMERALD] = level_mm->score[SC_EMERALD];
4716 level->score[SC_PACMAN] = level_mm->score[SC_PACMAN];
4717 level->score[SC_KEY] = level_mm->score[SC_KEY];
4718 level->score[SC_TIME_BONUS] = level_mm->score[SC_TIME_BONUS];
4719 level->score[SC_ELEM_BONUS] = level_mm->score[SC_ELEM_BONUS];
4721 level->amoeba_speed = level_mm->amoeba_speed;
4722 level->mm_time_fuse = level_mm->time_fuse;
4723 level->mm_time_bomb = level_mm->time_bomb;
4724 level->mm_time_ball = level_mm->time_ball;
4725 level->mm_time_block = level_mm->time_block;
4727 level->num_mm_ball_contents = level_mm->num_ball_contents;
4728 level->mm_ball_choice_mode = level_mm->ball_choice_mode;
4729 level->rotate_mm_ball_content = level_mm->rotate_ball_content;
4730 level->explode_mm_ball = level_mm->explode_ball;
4732 for (i = 0; i < level->num_mm_ball_contents; i++)
4733 level->mm_ball_content[i] =
4734 map_element_MM_to_RND(level_mm->ball_content[i]);
4736 for (x = 0; x < level->fieldx; x++)
4737 for (y = 0; y < level->fieldy; y++)
4738 level->field[x][y] = map_element_MM_to_RND(level_mm->field[x][y]);
4742 // ----------------------------------------------------------------------------
4743 // functions for loading DC level
4744 // ----------------------------------------------------------------------------
4746 #define DC_LEVEL_HEADER_SIZE 344
4748 static unsigned short getDecodedWord_DC(unsigned short data_encoded,
4751 static int last_data_encoded;
4755 int diff_hi, diff_lo;
4756 int data_hi, data_lo;
4757 unsigned short data_decoded;
4761 last_data_encoded = 0;
4768 diff = data_encoded - last_data_encoded;
4769 diff_hi = diff & ~0xff;
4770 diff_lo = diff & 0xff;
4774 data_hi = diff_hi - (offset1 << 8) + (offset2 & 0xff00);
4775 data_lo = (diff_lo + (data_hi >> 16)) & 0x00ff;
4776 data_hi = data_hi & 0xff00;
4778 data_decoded = data_hi | data_lo;
4780 last_data_encoded = data_encoded;
4782 offset1 = (offset1 + 1) % 31;
4783 offset2 = offset2 & 0xff;
4785 return data_decoded;
4788 static int getMappedElement_DC(int element)
4796 // 0x0117 - 0x036e: (?)
4799 // 0x042d - 0x0684: (?)
4815 element = EL_CRYSTAL;
4818 case 0x0e77: // quicksand (boulder)
4819 element = EL_QUICKSAND_FAST_FULL;
4822 case 0x0e99: // slow quicksand (boulder)
4823 element = EL_QUICKSAND_FULL;
4827 element = EL_EM_EXIT_OPEN;
4831 element = EL_EM_EXIT_CLOSED;
4835 element = EL_EM_STEEL_EXIT_OPEN;
4839 element = EL_EM_STEEL_EXIT_CLOSED;
4842 case 0x0f4f: // dynamite (lit 1)
4843 element = EL_EM_DYNAMITE_ACTIVE;
4846 case 0x0f57: // dynamite (lit 2)
4847 element = EL_EM_DYNAMITE_ACTIVE;
4850 case 0x0f5f: // dynamite (lit 3)
4851 element = EL_EM_DYNAMITE_ACTIVE;
4854 case 0x0f67: // dynamite (lit 4)
4855 element = EL_EM_DYNAMITE_ACTIVE;
4862 element = EL_AMOEBA_WET;
4866 element = EL_AMOEBA_DROP;
4870 element = EL_DC_MAGIC_WALL;
4874 element = EL_SPACESHIP_UP;
4878 element = EL_SPACESHIP_DOWN;
4882 element = EL_SPACESHIP_LEFT;
4886 element = EL_SPACESHIP_RIGHT;
4890 element = EL_BUG_UP;
4894 element = EL_BUG_DOWN;
4898 element = EL_BUG_LEFT;
4902 element = EL_BUG_RIGHT;
4906 element = EL_MOLE_UP;
4910 element = EL_MOLE_DOWN;
4914 element = EL_MOLE_LEFT;
4918 element = EL_MOLE_RIGHT;
4926 element = EL_YAMYAM_UP;
4930 element = EL_SWITCHGATE_OPEN;
4934 element = EL_SWITCHGATE_CLOSED;
4938 element = EL_DC_SWITCHGATE_SWITCH_UP;
4942 element = EL_TIMEGATE_CLOSED;
4945 case 0x144c: // conveyor belt switch (green)
4946 element = EL_CONVEYOR_BELT_3_SWITCH_MIDDLE;
4949 case 0x144f: // conveyor belt switch (red)
4950 element = EL_CONVEYOR_BELT_1_SWITCH_MIDDLE;
4953 case 0x1452: // conveyor belt switch (blue)
4954 element = EL_CONVEYOR_BELT_4_SWITCH_MIDDLE;
4958 element = EL_CONVEYOR_BELT_3_MIDDLE;
4962 element = EL_CONVEYOR_BELT_3_LEFT;
4966 element = EL_CONVEYOR_BELT_3_RIGHT;
4970 element = EL_CONVEYOR_BELT_1_MIDDLE;
4974 element = EL_CONVEYOR_BELT_1_LEFT;
4978 element = EL_CONVEYOR_BELT_1_RIGHT;
4982 element = EL_CONVEYOR_BELT_4_MIDDLE;
4986 element = EL_CONVEYOR_BELT_4_LEFT;
4990 element = EL_CONVEYOR_BELT_4_RIGHT;
4994 element = EL_EXPANDABLE_WALL_HORIZONTAL;
4998 element = EL_EXPANDABLE_WALL_VERTICAL;
5002 element = EL_EXPANDABLE_WALL_ANY;
5005 case 0x14ce: // growing steel wall (left/right)
5006 element = EL_EXPANDABLE_STEELWALL_HORIZONTAL;
5009 case 0x14df: // growing steel wall (up/down)
5010 element = EL_EXPANDABLE_STEELWALL_VERTICAL;
5013 case 0x14e8: // growing steel wall (up/down/left/right)
5014 element = EL_EXPANDABLE_STEELWALL_ANY;
5018 element = EL_SHIELD_DEADLY;
5022 element = EL_EXTRA_TIME;
5030 element = EL_EMPTY_SPACE;
5033 case 0x1578: // quicksand (empty)
5034 element = EL_QUICKSAND_FAST_EMPTY;
5037 case 0x1579: // slow quicksand (empty)
5038 element = EL_QUICKSAND_EMPTY;
5048 element = EL_EM_DYNAMITE;
5051 case 0x15a1: // key (red)
5052 element = EL_EM_KEY_1;
5055 case 0x15a2: // key (yellow)
5056 element = EL_EM_KEY_2;
5059 case 0x15a3: // key (blue)
5060 element = EL_EM_KEY_4;
5063 case 0x15a4: // key (green)
5064 element = EL_EM_KEY_3;
5067 case 0x15a5: // key (white)
5068 element = EL_DC_KEY_WHITE;
5072 element = EL_WALL_SLIPPERY;
5079 case 0x15a8: // wall (not round)
5083 case 0x15a9: // (blue)
5084 element = EL_CHAR_A;
5087 case 0x15aa: // (blue)
5088 element = EL_CHAR_B;
5091 case 0x15ab: // (blue)
5092 element = EL_CHAR_C;
5095 case 0x15ac: // (blue)
5096 element = EL_CHAR_D;
5099 case 0x15ad: // (blue)
5100 element = EL_CHAR_E;
5103 case 0x15ae: // (blue)
5104 element = EL_CHAR_F;
5107 case 0x15af: // (blue)
5108 element = EL_CHAR_G;
5111 case 0x15b0: // (blue)
5112 element = EL_CHAR_H;
5115 case 0x15b1: // (blue)
5116 element = EL_CHAR_I;
5119 case 0x15b2: // (blue)
5120 element = EL_CHAR_J;
5123 case 0x15b3: // (blue)
5124 element = EL_CHAR_K;
5127 case 0x15b4: // (blue)
5128 element = EL_CHAR_L;
5131 case 0x15b5: // (blue)
5132 element = EL_CHAR_M;
5135 case 0x15b6: // (blue)
5136 element = EL_CHAR_N;
5139 case 0x15b7: // (blue)
5140 element = EL_CHAR_O;
5143 case 0x15b8: // (blue)
5144 element = EL_CHAR_P;
5147 case 0x15b9: // (blue)
5148 element = EL_CHAR_Q;
5151 case 0x15ba: // (blue)
5152 element = EL_CHAR_R;
5155 case 0x15bb: // (blue)
5156 element = EL_CHAR_S;
5159 case 0x15bc: // (blue)
5160 element = EL_CHAR_T;
5163 case 0x15bd: // (blue)
5164 element = EL_CHAR_U;
5167 case 0x15be: // (blue)
5168 element = EL_CHAR_V;
5171 case 0x15bf: // (blue)
5172 element = EL_CHAR_W;
5175 case 0x15c0: // (blue)
5176 element = EL_CHAR_X;
5179 case 0x15c1: // (blue)
5180 element = EL_CHAR_Y;
5183 case 0x15c2: // (blue)
5184 element = EL_CHAR_Z;
5187 case 0x15c3: // (blue)
5188 element = EL_CHAR_AUMLAUT;
5191 case 0x15c4: // (blue)
5192 element = EL_CHAR_OUMLAUT;
5195 case 0x15c5: // (blue)
5196 element = EL_CHAR_UUMLAUT;
5199 case 0x15c6: // (blue)
5200 element = EL_CHAR_0;
5203 case 0x15c7: // (blue)
5204 element = EL_CHAR_1;
5207 case 0x15c8: // (blue)
5208 element = EL_CHAR_2;
5211 case 0x15c9: // (blue)
5212 element = EL_CHAR_3;
5215 case 0x15ca: // (blue)
5216 element = EL_CHAR_4;
5219 case 0x15cb: // (blue)
5220 element = EL_CHAR_5;
5223 case 0x15cc: // (blue)
5224 element = EL_CHAR_6;
5227 case 0x15cd: // (blue)
5228 element = EL_CHAR_7;
5231 case 0x15ce: // (blue)
5232 element = EL_CHAR_8;
5235 case 0x15cf: // (blue)
5236 element = EL_CHAR_9;
5239 case 0x15d0: // (blue)
5240 element = EL_CHAR_PERIOD;
5243 case 0x15d1: // (blue)
5244 element = EL_CHAR_EXCLAM;
5247 case 0x15d2: // (blue)
5248 element = EL_CHAR_COLON;
5251 case 0x15d3: // (blue)
5252 element = EL_CHAR_LESS;
5255 case 0x15d4: // (blue)
5256 element = EL_CHAR_GREATER;
5259 case 0x15d5: // (blue)
5260 element = EL_CHAR_QUESTION;
5263 case 0x15d6: // (blue)
5264 element = EL_CHAR_COPYRIGHT;
5267 case 0x15d7: // (blue)
5268 element = EL_CHAR_UP;
5271 case 0x15d8: // (blue)
5272 element = EL_CHAR_DOWN;
5275 case 0x15d9: // (blue)
5276 element = EL_CHAR_BUTTON;
5279 case 0x15da: // (blue)
5280 element = EL_CHAR_PLUS;
5283 case 0x15db: // (blue)
5284 element = EL_CHAR_MINUS;
5287 case 0x15dc: // (blue)
5288 element = EL_CHAR_APOSTROPHE;
5291 case 0x15dd: // (blue)
5292 element = EL_CHAR_PARENLEFT;
5295 case 0x15de: // (blue)
5296 element = EL_CHAR_PARENRIGHT;
5299 case 0x15df: // (green)
5300 element = EL_CHAR_A;
5303 case 0x15e0: // (green)
5304 element = EL_CHAR_B;
5307 case 0x15e1: // (green)
5308 element = EL_CHAR_C;
5311 case 0x15e2: // (green)
5312 element = EL_CHAR_D;
5315 case 0x15e3: // (green)
5316 element = EL_CHAR_E;
5319 case 0x15e4: // (green)
5320 element = EL_CHAR_F;
5323 case 0x15e5: // (green)
5324 element = EL_CHAR_G;
5327 case 0x15e6: // (green)
5328 element = EL_CHAR_H;
5331 case 0x15e7: // (green)
5332 element = EL_CHAR_I;
5335 case 0x15e8: // (green)
5336 element = EL_CHAR_J;
5339 case 0x15e9: // (green)
5340 element = EL_CHAR_K;
5343 case 0x15ea: // (green)
5344 element = EL_CHAR_L;
5347 case 0x15eb: // (green)
5348 element = EL_CHAR_M;
5351 case 0x15ec: // (green)
5352 element = EL_CHAR_N;
5355 case 0x15ed: // (green)
5356 element = EL_CHAR_O;
5359 case 0x15ee: // (green)
5360 element = EL_CHAR_P;
5363 case 0x15ef: // (green)
5364 element = EL_CHAR_Q;
5367 case 0x15f0: // (green)
5368 element = EL_CHAR_R;
5371 case 0x15f1: // (green)
5372 element = EL_CHAR_S;
5375 case 0x15f2: // (green)
5376 element = EL_CHAR_T;
5379 case 0x15f3: // (green)
5380 element = EL_CHAR_U;
5383 case 0x15f4: // (green)
5384 element = EL_CHAR_V;
5387 case 0x15f5: // (green)
5388 element = EL_CHAR_W;
5391 case 0x15f6: // (green)
5392 element = EL_CHAR_X;
5395 case 0x15f7: // (green)
5396 element = EL_CHAR_Y;
5399 case 0x15f8: // (green)
5400 element = EL_CHAR_Z;
5403 case 0x15f9: // (green)
5404 element = EL_CHAR_AUMLAUT;
5407 case 0x15fa: // (green)
5408 element = EL_CHAR_OUMLAUT;
5411 case 0x15fb: // (green)
5412 element = EL_CHAR_UUMLAUT;
5415 case 0x15fc: // (green)
5416 element = EL_CHAR_0;
5419 case 0x15fd: // (green)
5420 element = EL_CHAR_1;
5423 case 0x15fe: // (green)
5424 element = EL_CHAR_2;
5427 case 0x15ff: // (green)
5428 element = EL_CHAR_3;
5431 case 0x1600: // (green)
5432 element = EL_CHAR_4;
5435 case 0x1601: // (green)
5436 element = EL_CHAR_5;
5439 case 0x1602: // (green)
5440 element = EL_CHAR_6;
5443 case 0x1603: // (green)
5444 element = EL_CHAR_7;
5447 case 0x1604: // (green)
5448 element = EL_CHAR_8;
5451 case 0x1605: // (green)
5452 element = EL_CHAR_9;
5455 case 0x1606: // (green)
5456 element = EL_CHAR_PERIOD;
5459 case 0x1607: // (green)
5460 element = EL_CHAR_EXCLAM;
5463 case 0x1608: // (green)
5464 element = EL_CHAR_COLON;
5467 case 0x1609: // (green)
5468 element = EL_CHAR_LESS;
5471 case 0x160a: // (green)
5472 element = EL_CHAR_GREATER;
5475 case 0x160b: // (green)
5476 element = EL_CHAR_QUESTION;
5479 case 0x160c: // (green)
5480 element = EL_CHAR_COPYRIGHT;
5483 case 0x160d: // (green)
5484 element = EL_CHAR_UP;
5487 case 0x160e: // (green)
5488 element = EL_CHAR_DOWN;
5491 case 0x160f: // (green)
5492 element = EL_CHAR_BUTTON;
5495 case 0x1610: // (green)
5496 element = EL_CHAR_PLUS;
5499 case 0x1611: // (green)
5500 element = EL_CHAR_MINUS;
5503 case 0x1612: // (green)
5504 element = EL_CHAR_APOSTROPHE;
5507 case 0x1613: // (green)
5508 element = EL_CHAR_PARENLEFT;
5511 case 0x1614: // (green)
5512 element = EL_CHAR_PARENRIGHT;
5515 case 0x1615: // (blue steel)
5516 element = EL_STEEL_CHAR_A;
5519 case 0x1616: // (blue steel)
5520 element = EL_STEEL_CHAR_B;
5523 case 0x1617: // (blue steel)
5524 element = EL_STEEL_CHAR_C;
5527 case 0x1618: // (blue steel)
5528 element = EL_STEEL_CHAR_D;
5531 case 0x1619: // (blue steel)
5532 element = EL_STEEL_CHAR_E;
5535 case 0x161a: // (blue steel)
5536 element = EL_STEEL_CHAR_F;
5539 case 0x161b: // (blue steel)
5540 element = EL_STEEL_CHAR_G;
5543 case 0x161c: // (blue steel)
5544 element = EL_STEEL_CHAR_H;
5547 case 0x161d: // (blue steel)
5548 element = EL_STEEL_CHAR_I;
5551 case 0x161e: // (blue steel)
5552 element = EL_STEEL_CHAR_J;
5555 case 0x161f: // (blue steel)
5556 element = EL_STEEL_CHAR_K;
5559 case 0x1620: // (blue steel)
5560 element = EL_STEEL_CHAR_L;
5563 case 0x1621: // (blue steel)
5564 element = EL_STEEL_CHAR_M;
5567 case 0x1622: // (blue steel)
5568 element = EL_STEEL_CHAR_N;
5571 case 0x1623: // (blue steel)
5572 element = EL_STEEL_CHAR_O;
5575 case 0x1624: // (blue steel)
5576 element = EL_STEEL_CHAR_P;
5579 case 0x1625: // (blue steel)
5580 element = EL_STEEL_CHAR_Q;
5583 case 0x1626: // (blue steel)
5584 element = EL_STEEL_CHAR_R;
5587 case 0x1627: // (blue steel)
5588 element = EL_STEEL_CHAR_S;
5591 case 0x1628: // (blue steel)
5592 element = EL_STEEL_CHAR_T;
5595 case 0x1629: // (blue steel)
5596 element = EL_STEEL_CHAR_U;
5599 case 0x162a: // (blue steel)
5600 element = EL_STEEL_CHAR_V;
5603 case 0x162b: // (blue steel)
5604 element = EL_STEEL_CHAR_W;
5607 case 0x162c: // (blue steel)
5608 element = EL_STEEL_CHAR_X;
5611 case 0x162d: // (blue steel)
5612 element = EL_STEEL_CHAR_Y;
5615 case 0x162e: // (blue steel)
5616 element = EL_STEEL_CHAR_Z;
5619 case 0x162f: // (blue steel)
5620 element = EL_STEEL_CHAR_AUMLAUT;
5623 case 0x1630: // (blue steel)
5624 element = EL_STEEL_CHAR_OUMLAUT;
5627 case 0x1631: // (blue steel)
5628 element = EL_STEEL_CHAR_UUMLAUT;
5631 case 0x1632: // (blue steel)
5632 element = EL_STEEL_CHAR_0;
5635 case 0x1633: // (blue steel)
5636 element = EL_STEEL_CHAR_1;
5639 case 0x1634: // (blue steel)
5640 element = EL_STEEL_CHAR_2;
5643 case 0x1635: // (blue steel)
5644 element = EL_STEEL_CHAR_3;
5647 case 0x1636: // (blue steel)
5648 element = EL_STEEL_CHAR_4;
5651 case 0x1637: // (blue steel)
5652 element = EL_STEEL_CHAR_5;
5655 case 0x1638: // (blue steel)
5656 element = EL_STEEL_CHAR_6;
5659 case 0x1639: // (blue steel)
5660 element = EL_STEEL_CHAR_7;
5663 case 0x163a: // (blue steel)
5664 element = EL_STEEL_CHAR_8;
5667 case 0x163b: // (blue steel)
5668 element = EL_STEEL_CHAR_9;
5671 case 0x163c: // (blue steel)
5672 element = EL_STEEL_CHAR_PERIOD;
5675 case 0x163d: // (blue steel)
5676 element = EL_STEEL_CHAR_EXCLAM;
5679 case 0x163e: // (blue steel)
5680 element = EL_STEEL_CHAR_COLON;
5683 case 0x163f: // (blue steel)
5684 element = EL_STEEL_CHAR_LESS;
5687 case 0x1640: // (blue steel)
5688 element = EL_STEEL_CHAR_GREATER;
5691 case 0x1641: // (blue steel)
5692 element = EL_STEEL_CHAR_QUESTION;
5695 case 0x1642: // (blue steel)
5696 element = EL_STEEL_CHAR_COPYRIGHT;
5699 case 0x1643: // (blue steel)
5700 element = EL_STEEL_CHAR_UP;
5703 case 0x1644: // (blue steel)
5704 element = EL_STEEL_CHAR_DOWN;
5707 case 0x1645: // (blue steel)
5708 element = EL_STEEL_CHAR_BUTTON;
5711 case 0x1646: // (blue steel)
5712 element = EL_STEEL_CHAR_PLUS;
5715 case 0x1647: // (blue steel)
5716 element = EL_STEEL_CHAR_MINUS;
5719 case 0x1648: // (blue steel)
5720 element = EL_STEEL_CHAR_APOSTROPHE;
5723 case 0x1649: // (blue steel)
5724 element = EL_STEEL_CHAR_PARENLEFT;
5727 case 0x164a: // (blue steel)
5728 element = EL_STEEL_CHAR_PARENRIGHT;
5731 case 0x164b: // (green steel)
5732 element = EL_STEEL_CHAR_A;
5735 case 0x164c: // (green steel)
5736 element = EL_STEEL_CHAR_B;
5739 case 0x164d: // (green steel)
5740 element = EL_STEEL_CHAR_C;
5743 case 0x164e: // (green steel)
5744 element = EL_STEEL_CHAR_D;
5747 case 0x164f: // (green steel)
5748 element = EL_STEEL_CHAR_E;
5751 case 0x1650: // (green steel)
5752 element = EL_STEEL_CHAR_F;
5755 case 0x1651: // (green steel)
5756 element = EL_STEEL_CHAR_G;
5759 case 0x1652: // (green steel)
5760 element = EL_STEEL_CHAR_H;
5763 case 0x1653: // (green steel)
5764 element = EL_STEEL_CHAR_I;
5767 case 0x1654: // (green steel)
5768 element = EL_STEEL_CHAR_J;
5771 case 0x1655: // (green steel)
5772 element = EL_STEEL_CHAR_K;
5775 case 0x1656: // (green steel)
5776 element = EL_STEEL_CHAR_L;
5779 case 0x1657: // (green steel)
5780 element = EL_STEEL_CHAR_M;
5783 case 0x1658: // (green steel)
5784 element = EL_STEEL_CHAR_N;
5787 case 0x1659: // (green steel)
5788 element = EL_STEEL_CHAR_O;
5791 case 0x165a: // (green steel)
5792 element = EL_STEEL_CHAR_P;
5795 case 0x165b: // (green steel)
5796 element = EL_STEEL_CHAR_Q;
5799 case 0x165c: // (green steel)
5800 element = EL_STEEL_CHAR_R;
5803 case 0x165d: // (green steel)
5804 element = EL_STEEL_CHAR_S;
5807 case 0x165e: // (green steel)
5808 element = EL_STEEL_CHAR_T;
5811 case 0x165f: // (green steel)
5812 element = EL_STEEL_CHAR_U;
5815 case 0x1660: // (green steel)
5816 element = EL_STEEL_CHAR_V;
5819 case 0x1661: // (green steel)
5820 element = EL_STEEL_CHAR_W;
5823 case 0x1662: // (green steel)
5824 element = EL_STEEL_CHAR_X;
5827 case 0x1663: // (green steel)
5828 element = EL_STEEL_CHAR_Y;
5831 case 0x1664: // (green steel)
5832 element = EL_STEEL_CHAR_Z;
5835 case 0x1665: // (green steel)
5836 element = EL_STEEL_CHAR_AUMLAUT;
5839 case 0x1666: // (green steel)
5840 element = EL_STEEL_CHAR_OUMLAUT;
5843 case 0x1667: // (green steel)
5844 element = EL_STEEL_CHAR_UUMLAUT;
5847 case 0x1668: // (green steel)
5848 element = EL_STEEL_CHAR_0;
5851 case 0x1669: // (green steel)
5852 element = EL_STEEL_CHAR_1;
5855 case 0x166a: // (green steel)
5856 element = EL_STEEL_CHAR_2;
5859 case 0x166b: // (green steel)
5860 element = EL_STEEL_CHAR_3;
5863 case 0x166c: // (green steel)
5864 element = EL_STEEL_CHAR_4;
5867 case 0x166d: // (green steel)
5868 element = EL_STEEL_CHAR_5;
5871 case 0x166e: // (green steel)
5872 element = EL_STEEL_CHAR_6;
5875 case 0x166f: // (green steel)
5876 element = EL_STEEL_CHAR_7;
5879 case 0x1670: // (green steel)
5880 element = EL_STEEL_CHAR_8;
5883 case 0x1671: // (green steel)
5884 element = EL_STEEL_CHAR_9;
5887 case 0x1672: // (green steel)
5888 element = EL_STEEL_CHAR_PERIOD;
5891 case 0x1673: // (green steel)
5892 element = EL_STEEL_CHAR_EXCLAM;
5895 case 0x1674: // (green steel)
5896 element = EL_STEEL_CHAR_COLON;
5899 case 0x1675: // (green steel)
5900 element = EL_STEEL_CHAR_LESS;
5903 case 0x1676: // (green steel)
5904 element = EL_STEEL_CHAR_GREATER;
5907 case 0x1677: // (green steel)
5908 element = EL_STEEL_CHAR_QUESTION;
5911 case 0x1678: // (green steel)
5912 element = EL_STEEL_CHAR_COPYRIGHT;
5915 case 0x1679: // (green steel)
5916 element = EL_STEEL_CHAR_UP;
5919 case 0x167a: // (green steel)
5920 element = EL_STEEL_CHAR_DOWN;
5923 case 0x167b: // (green steel)
5924 element = EL_STEEL_CHAR_BUTTON;
5927 case 0x167c: // (green steel)
5928 element = EL_STEEL_CHAR_PLUS;
5931 case 0x167d: // (green steel)
5932 element = EL_STEEL_CHAR_MINUS;
5935 case 0x167e: // (green steel)
5936 element = EL_STEEL_CHAR_APOSTROPHE;
5939 case 0x167f: // (green steel)
5940 element = EL_STEEL_CHAR_PARENLEFT;
5943 case 0x1680: // (green steel)
5944 element = EL_STEEL_CHAR_PARENRIGHT;
5947 case 0x1681: // gate (red)
5948 element = EL_EM_GATE_1;
5951 case 0x1682: // secret gate (red)
5952 element = EL_EM_GATE_1_GRAY;
5955 case 0x1683: // gate (yellow)
5956 element = EL_EM_GATE_2;
5959 case 0x1684: // secret gate (yellow)
5960 element = EL_EM_GATE_2_GRAY;
5963 case 0x1685: // gate (blue)
5964 element = EL_EM_GATE_4;
5967 case 0x1686: // secret gate (blue)
5968 element = EL_EM_GATE_4_GRAY;
5971 case 0x1687: // gate (green)
5972 element = EL_EM_GATE_3;
5975 case 0x1688: // secret gate (green)
5976 element = EL_EM_GATE_3_GRAY;
5979 case 0x1689: // gate (white)
5980 element = EL_DC_GATE_WHITE;
5983 case 0x168a: // secret gate (white)
5984 element = EL_DC_GATE_WHITE_GRAY;
5987 case 0x168b: // secret gate (no key)
5988 element = EL_DC_GATE_FAKE_GRAY;
5992 element = EL_ROBOT_WHEEL;
5996 element = EL_DC_TIMEGATE_SWITCH;
6000 element = EL_ACID_POOL_BOTTOM;
6004 element = EL_ACID_POOL_TOPLEFT;
6008 element = EL_ACID_POOL_TOPRIGHT;
6012 element = EL_ACID_POOL_BOTTOMLEFT;
6016 element = EL_ACID_POOL_BOTTOMRIGHT;
6020 element = EL_STEELWALL;
6024 element = EL_STEELWALL_SLIPPERY;
6027 case 0x1695: // steel wall (not round)
6028 element = EL_STEELWALL;
6031 case 0x1696: // steel wall (left)
6032 element = EL_DC_STEELWALL_1_LEFT;
6035 case 0x1697: // steel wall (bottom)
6036 element = EL_DC_STEELWALL_1_BOTTOM;
6039 case 0x1698: // steel wall (right)
6040 element = EL_DC_STEELWALL_1_RIGHT;
6043 case 0x1699: // steel wall (top)
6044 element = EL_DC_STEELWALL_1_TOP;
6047 case 0x169a: // steel wall (left/bottom)
6048 element = EL_DC_STEELWALL_1_BOTTOMLEFT;
6051 case 0x169b: // steel wall (right/bottom)
6052 element = EL_DC_STEELWALL_1_BOTTOMRIGHT;
6055 case 0x169c: // steel wall (right/top)
6056 element = EL_DC_STEELWALL_1_TOPRIGHT;
6059 case 0x169d: // steel wall (left/top)
6060 element = EL_DC_STEELWALL_1_TOPLEFT;
6063 case 0x169e: // steel wall (right/bottom small)
6064 element = EL_DC_STEELWALL_1_BOTTOMRIGHT_2;
6067 case 0x169f: // steel wall (left/bottom small)
6068 element = EL_DC_STEELWALL_1_BOTTOMLEFT_2;
6071 case 0x16a0: // steel wall (right/top small)
6072 element = EL_DC_STEELWALL_1_TOPRIGHT_2;
6075 case 0x16a1: // steel wall (left/top small)
6076 element = EL_DC_STEELWALL_1_TOPLEFT_2;
6079 case 0x16a2: // steel wall (left/right)
6080 element = EL_DC_STEELWALL_1_VERTICAL;
6083 case 0x16a3: // steel wall (top/bottom)
6084 element = EL_DC_STEELWALL_1_HORIZONTAL;
6087 case 0x16a4: // steel wall 2 (left end)
6088 element = EL_DC_STEELWALL_2_LEFT;
6091 case 0x16a5: // steel wall 2 (right end)
6092 element = EL_DC_STEELWALL_2_RIGHT;
6095 case 0x16a6: // steel wall 2 (top end)
6096 element = EL_DC_STEELWALL_2_TOP;
6099 case 0x16a7: // steel wall 2 (bottom end)
6100 element = EL_DC_STEELWALL_2_BOTTOM;
6103 case 0x16a8: // steel wall 2 (left/right)
6104 element = EL_DC_STEELWALL_2_HORIZONTAL;
6107 case 0x16a9: // steel wall 2 (up/down)
6108 element = EL_DC_STEELWALL_2_VERTICAL;
6111 case 0x16aa: // steel wall 2 (mid)
6112 element = EL_DC_STEELWALL_2_MIDDLE;
6116 element = EL_SIGN_EXCLAMATION;
6120 element = EL_SIGN_RADIOACTIVITY;
6124 element = EL_SIGN_STOP;
6128 element = EL_SIGN_WHEELCHAIR;
6132 element = EL_SIGN_PARKING;
6136 element = EL_SIGN_NO_ENTRY;
6140 element = EL_SIGN_HEART;
6144 element = EL_SIGN_GIVE_WAY;
6148 element = EL_SIGN_ENTRY_FORBIDDEN;
6152 element = EL_SIGN_EMERGENCY_EXIT;
6156 element = EL_SIGN_YIN_YANG;
6160 element = EL_WALL_EMERALD;
6164 element = EL_WALL_DIAMOND;
6168 element = EL_WALL_PEARL;
6172 element = EL_WALL_CRYSTAL;
6176 element = EL_INVISIBLE_WALL;
6180 element = EL_INVISIBLE_STEELWALL;
6184 // EL_INVISIBLE_SAND
6187 element = EL_LIGHT_SWITCH;
6191 element = EL_ENVELOPE_1;
6195 if (element >= 0x0117 && element <= 0x036e) // (?)
6196 element = EL_DIAMOND;
6197 else if (element >= 0x042d && element <= 0x0684) // (?)
6198 element = EL_EMERALD;
6199 else if (element >= 0x157c && element <= 0x158b)
6201 else if (element >= 0x1590 && element <= 0x159f)
6202 element = EL_DC_LANDMINE;
6203 else if (element >= 0x16bc && element <= 0x16cb)
6204 element = EL_INVISIBLE_SAND;
6207 Warn("unknown Diamond Caves element 0x%04x", element);
6209 element = EL_UNKNOWN;
6214 return getMappedElement(element);
6217 static void LoadLevelFromFileStream_DC(File *file, struct LevelInfo *level)
6219 byte header[DC_LEVEL_HEADER_SIZE];
6221 int envelope_header_pos = 62;
6222 int envelope_content_pos = 94;
6223 int level_name_pos = 251;
6224 int level_author_pos = 292;
6225 int envelope_header_len;
6226 int envelope_content_len;
6228 int level_author_len;
6230 int num_yamyam_contents;
6233 getDecodedWord_DC(0, TRUE); // initialize DC2 decoding engine
6235 for (i = 0; i < DC_LEVEL_HEADER_SIZE / 2; i++)
6237 unsigned short header_word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
6239 header[i * 2 + 0] = header_word >> 8;
6240 header[i * 2 + 1] = header_word & 0xff;
6243 // read some values from level header to check level decoding integrity
6244 fieldx = header[6] | (header[7] << 8);
6245 fieldy = header[8] | (header[9] << 8);
6246 num_yamyam_contents = header[60] | (header[61] << 8);
6248 // do some simple sanity checks to ensure that level was correctly decoded
6249 if (fieldx < 1 || fieldx > 256 ||
6250 fieldy < 1 || fieldy > 256 ||
6251 num_yamyam_contents < 1 || num_yamyam_contents > 8)
6253 level->no_valid_file = TRUE;
6255 Warn("cannot decode level from stream -- using empty level");
6260 // maximum envelope header size is 31 bytes
6261 envelope_header_len = header[envelope_header_pos];
6262 // maximum envelope content size is 110 (156?) bytes
6263 envelope_content_len = header[envelope_content_pos];
6265 // maximum level title size is 40 bytes
6266 level_name_len = MIN(header[level_name_pos], MAX_LEVEL_NAME_LEN);
6267 // maximum level author size is 30 (51?) bytes
6268 level_author_len = MIN(header[level_author_pos], MAX_LEVEL_AUTHOR_LEN);
6272 for (i = 0; i < envelope_header_len; i++)
6273 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
6274 level->envelope[0].text[envelope_size++] =
6275 header[envelope_header_pos + 1 + i];
6277 if (envelope_header_len > 0 && envelope_content_len > 0)
6279 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
6280 level->envelope[0].text[envelope_size++] = '\n';
6281 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
6282 level->envelope[0].text[envelope_size++] = '\n';
6285 for (i = 0; i < envelope_content_len; i++)
6286 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
6287 level->envelope[0].text[envelope_size++] =
6288 header[envelope_content_pos + 1 + i];
6290 level->envelope[0].text[envelope_size] = '\0';
6292 level->envelope[0].xsize = MAX_ENVELOPE_XSIZE;
6293 level->envelope[0].ysize = 10;
6294 level->envelope[0].autowrap = TRUE;
6295 level->envelope[0].centered = TRUE;
6297 for (i = 0; i < level_name_len; i++)
6298 level->name[i] = header[level_name_pos + 1 + i];
6299 level->name[level_name_len] = '\0';
6301 for (i = 0; i < level_author_len; i++)
6302 level->author[i] = header[level_author_pos + 1 + i];
6303 level->author[level_author_len] = '\0';
6305 num_yamyam_contents = header[60] | (header[61] << 8);
6306 level->num_yamyam_contents =
6307 MIN(MAX(MIN_ELEMENT_CONTENTS, num_yamyam_contents), MAX_ELEMENT_CONTENTS);
6309 for (i = 0; i < num_yamyam_contents; i++)
6311 for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
6313 unsigned short word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
6314 int element_dc = ((word & 0xff) << 8) | ((word >> 8) & 0xff);
6316 if (i < MAX_ELEMENT_CONTENTS)
6317 level->yamyam_content[i].e[x][y] = getMappedElement_DC(element_dc);
6321 fieldx = header[6] | (header[7] << 8);
6322 fieldy = header[8] | (header[9] << 8);
6323 level->fieldx = MIN(MAX(MIN_LEV_FIELDX, fieldx), MAX_LEV_FIELDX);
6324 level->fieldy = MIN(MAX(MIN_LEV_FIELDY, fieldy), MAX_LEV_FIELDY);
6326 for (y = 0; y < fieldy; y++) for (x = 0; x < fieldx; x++)
6328 unsigned short word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
6329 int element_dc = ((word & 0xff) << 8) | ((word >> 8) & 0xff);
6331 if (x < MAX_LEV_FIELDX && y < MAX_LEV_FIELDY)
6332 level->field[x][y] = getMappedElement_DC(element_dc);
6335 x = MIN(MAX(0, (header[10] | (header[11] << 8)) - 1), MAX_LEV_FIELDX - 1);
6336 y = MIN(MAX(0, (header[12] | (header[13] << 8)) - 1), MAX_LEV_FIELDY - 1);
6337 level->field[x][y] = EL_PLAYER_1;
6339 x = MIN(MAX(0, (header[14] | (header[15] << 8)) - 1), MAX_LEV_FIELDX - 1);
6340 y = MIN(MAX(0, (header[16] | (header[17] << 8)) - 1), MAX_LEV_FIELDY - 1);
6341 level->field[x][y] = EL_PLAYER_2;
6343 level->gems_needed = header[18] | (header[19] << 8);
6345 level->score[SC_EMERALD] = header[20] | (header[21] << 8);
6346 level->score[SC_DIAMOND] = header[22] | (header[23] << 8);
6347 level->score[SC_PEARL] = header[24] | (header[25] << 8);
6348 level->score[SC_CRYSTAL] = header[26] | (header[27] << 8);
6349 level->score[SC_NUT] = header[28] | (header[29] << 8);
6350 level->score[SC_ROBOT] = header[30] | (header[31] << 8);
6351 level->score[SC_SPACESHIP] = header[32] | (header[33] << 8);
6352 level->score[SC_BUG] = header[34] | (header[35] << 8);
6353 level->score[SC_YAMYAM] = header[36] | (header[37] << 8);
6354 level->score[SC_DYNAMITE] = header[38] | (header[39] << 8);
6355 level->score[SC_KEY] = header[40] | (header[41] << 8);
6356 level->score[SC_TIME_BONUS] = header[42] | (header[43] << 8);
6358 level->time = header[44] | (header[45] << 8);
6360 level->amoeba_speed = header[46] | (header[47] << 8);
6361 level->time_light = header[48] | (header[49] << 8);
6362 level->time_timegate = header[50] | (header[51] << 8);
6363 level->time_wheel = header[52] | (header[53] << 8);
6364 level->time_magic_wall = header[54] | (header[55] << 8);
6365 level->extra_time = header[56] | (header[57] << 8);
6366 level->shield_normal_time = header[58] | (header[59] << 8);
6368 // shield and extra time elements do not have a score
6369 level->score[SC_SHIELD] = 0;
6370 level->extra_time_score = 0;
6372 // set time for normal and deadly shields to the same value
6373 level->shield_deadly_time = level->shield_normal_time;
6375 // Diamond Caves has the same (strange) behaviour as Emerald Mine that gems
6376 // can slip down from flat walls, like normal walls and steel walls
6377 level->em_slippery_gems = TRUE;
6379 // time score is counted for each 10 seconds left in Diamond Caves levels
6380 level->time_score_base = 10;
6383 static void LoadLevelFromFileInfo_DC(struct LevelInfo *level,
6384 struct LevelFileInfo *level_file_info,
6385 boolean level_info_only)
6387 char *filename = level_file_info->filename;
6389 int num_magic_bytes = 8;
6390 char magic_bytes[num_magic_bytes + 1];
6391 int num_levels_to_skip = level_file_info->nr - leveldir_current->first_level;
6393 if (!(file = openFile(filename, MODE_READ)))
6395 level->no_valid_file = TRUE;
6397 if (!level_info_only)
6398 Warn("cannot read level '%s' -- using empty level", filename);
6403 // fseek(file, 0x0000, SEEK_SET);
6405 if (level_file_info->packed)
6407 // read "magic bytes" from start of file
6408 if (getStringFromFile(file, magic_bytes, num_magic_bytes + 1) == NULL)
6409 magic_bytes[0] = '\0';
6411 // check "magic bytes" for correct file format
6412 if (!strPrefix(magic_bytes, "DC2"))
6414 level->no_valid_file = TRUE;
6416 Warn("unknown DC level file '%s' -- using empty level", filename);
6421 if (strPrefix(magic_bytes, "DC2Win95") ||
6422 strPrefix(magic_bytes, "DC2Win98"))
6424 int position_first_level = 0x00fa;
6425 int extra_bytes = 4;
6428 // advance file stream to first level inside the level package
6429 skip_bytes = position_first_level - num_magic_bytes - extra_bytes;
6431 // each block of level data is followed by block of non-level data
6432 num_levels_to_skip *= 2;
6434 // at least skip header bytes, therefore use ">= 0" instead of "> 0"
6435 while (num_levels_to_skip >= 0)
6437 // advance file stream to next level inside the level package
6438 if (seekFile(file, skip_bytes, SEEK_CUR) != 0)
6440 level->no_valid_file = TRUE;
6442 Warn("cannot fseek in file '%s' -- using empty level", filename);
6447 // skip apparently unused extra bytes following each level
6448 ReadUnusedBytesFromFile(file, extra_bytes);
6450 // read size of next level in level package
6451 skip_bytes = getFile32BitLE(file);
6453 num_levels_to_skip--;
6458 level->no_valid_file = TRUE;
6460 Warn("unknown DC2 level file '%s' -- using empty level", filename);
6466 LoadLevelFromFileStream_DC(file, level);
6472 // ----------------------------------------------------------------------------
6473 // functions for loading SB level
6474 // ----------------------------------------------------------------------------
6476 int getMappedElement_SB(int element_ascii, boolean use_ces)
6484 sb_element_mapping[] =
6486 { ' ', EL_EMPTY, EL_CUSTOM_1 }, // floor (space)
6487 { '#', EL_STEELWALL, EL_CUSTOM_2 }, // wall
6488 { '@', EL_PLAYER_1, EL_CUSTOM_3 }, // player
6489 { '$', EL_SOKOBAN_OBJECT, EL_CUSTOM_4 }, // box
6490 { '.', EL_SOKOBAN_FIELD_EMPTY, EL_CUSTOM_5 }, // goal square
6491 { '*', EL_SOKOBAN_FIELD_FULL, EL_CUSTOM_6 }, // box on goal square
6492 { '+', EL_SOKOBAN_FIELD_PLAYER, EL_CUSTOM_7 }, // player on goal square
6493 { '_', EL_INVISIBLE_STEELWALL, EL_FROM_LEVEL_TEMPLATE }, // floor beyond border
6500 for (i = 0; sb_element_mapping[i].ascii != 0; i++)
6501 if (element_ascii == sb_element_mapping[i].ascii)
6502 return (use_ces ? sb_element_mapping[i].ce : sb_element_mapping[i].sb);
6504 return EL_UNDEFINED;
6507 static void SetLevelSettings_SB(struct LevelInfo *level)
6511 level->use_step_counter = TRUE;
6514 level->score[SC_TIME_BONUS] = 0;
6515 level->time_score_base = 1;
6516 level->rate_time_over_score = TRUE;
6519 level->auto_exit_sokoban = TRUE;
6522 static void LoadLevelFromFileInfo_SB(struct LevelInfo *level,
6523 struct LevelFileInfo *level_file_info,
6524 boolean level_info_only)
6526 char *filename = level_file_info->filename;
6527 char line[MAX_LINE_LEN], line_raw[MAX_LINE_LEN], previous_line[MAX_LINE_LEN];
6528 char last_comment[MAX_LINE_LEN];
6529 char level_name[MAX_LINE_LEN];
6532 int num_levels_to_skip = level_file_info->nr - leveldir_current->first_level;
6533 boolean read_continued_line = FALSE;
6534 boolean reading_playfield = FALSE;
6535 boolean got_valid_playfield_line = FALSE;
6536 boolean invalid_playfield_char = FALSE;
6537 boolean load_xsb_to_ces = check_special_flags("load_xsb_to_ces");
6538 int file_level_nr = 0;
6539 int x = 0, y = 0; // initialized to make compilers happy
6541 last_comment[0] = '\0';
6542 level_name[0] = '\0';
6544 if (!(file = openFile(filename, MODE_READ)))
6546 level->no_valid_file = TRUE;
6548 if (!level_info_only)
6549 Warn("cannot read level '%s' -- using empty level", filename);
6554 while (!checkEndOfFile(file))
6556 // level successfully read, but next level may follow here
6557 if (!got_valid_playfield_line && reading_playfield)
6559 // read playfield from single level file -- skip remaining file
6560 if (!level_file_info->packed)
6563 if (file_level_nr >= num_levels_to_skip)
6568 last_comment[0] = '\0';
6569 level_name[0] = '\0';
6571 reading_playfield = FALSE;
6574 got_valid_playfield_line = FALSE;
6576 // read next line of input file
6577 if (!getStringFromFile(file, line, MAX_LINE_LEN))
6580 // cut trailing line break (this can be newline and/or carriage return)
6581 for (line_ptr = &line[strlen(line)]; line_ptr >= line; line_ptr--)
6582 if ((*line_ptr == '\n' || *line_ptr == '\r') && *(line_ptr + 1) == '\0')
6585 // copy raw input line for later use (mainly debugging output)
6586 strcpy(line_raw, line);
6588 if (read_continued_line)
6590 // append new line to existing line, if there is enough space
6591 if (strlen(previous_line) + strlen(line_ptr) < MAX_LINE_LEN)
6592 strcat(previous_line, line_ptr);
6594 strcpy(line, previous_line); // copy storage buffer to line
6596 read_continued_line = FALSE;
6599 // if the last character is '\', continue at next line
6600 if (strlen(line) > 0 && line[strlen(line) - 1] == '\\')
6602 line[strlen(line) - 1] = '\0'; // cut off trailing backslash
6603 strcpy(previous_line, line); // copy line to storage buffer
6605 read_continued_line = TRUE;
6611 if (line[0] == '\0')
6614 // extract comment text from comment line
6617 for (line_ptr = line; *line_ptr; line_ptr++)
6618 if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != ';')
6621 strcpy(last_comment, line_ptr);
6626 // extract level title text from line containing level title
6627 if (line[0] == '\'')
6629 strcpy(level_name, &line[1]);
6631 if (strlen(level_name) > 0 && level_name[strlen(level_name) - 1] == '\'')
6632 level_name[strlen(level_name) - 1] = '\0';
6637 // skip lines containing only spaces (or empty lines)
6638 for (line_ptr = line; *line_ptr; line_ptr++)
6639 if (*line_ptr != ' ')
6641 if (*line_ptr == '\0')
6644 // at this point, we have found a line containing part of a playfield
6646 got_valid_playfield_line = TRUE;
6648 if (!reading_playfield)
6650 reading_playfield = TRUE;
6651 invalid_playfield_char = FALSE;
6653 for (x = 0; x < MAX_LEV_FIELDX; x++)
6654 for (y = 0; y < MAX_LEV_FIELDY; y++)
6655 level->field[x][y] = getMappedElement_SB(' ', load_xsb_to_ces);
6660 // start with topmost tile row
6664 // skip playfield line if larger row than allowed
6665 if (y >= MAX_LEV_FIELDY)
6668 // start with leftmost tile column
6671 // read playfield elements from line
6672 for (line_ptr = line; *line_ptr; line_ptr++)
6674 int mapped_sb_element = getMappedElement_SB(*line_ptr, load_xsb_to_ces);
6676 // stop parsing playfield line if larger column than allowed
6677 if (x >= MAX_LEV_FIELDX)
6680 if (mapped_sb_element == EL_UNDEFINED)
6682 invalid_playfield_char = TRUE;
6687 level->field[x][y] = mapped_sb_element;
6689 // continue with next tile column
6692 level->fieldx = MAX(x, level->fieldx);
6695 if (invalid_playfield_char)
6697 // if first playfield line, treat invalid lines as comment lines
6699 reading_playfield = FALSE;
6704 // continue with next tile row
6712 level->fieldx = MIN(MAX(MIN_LEV_FIELDX, level->fieldx), MAX_LEV_FIELDX);
6713 level->fieldy = MIN(MAX(MIN_LEV_FIELDY, level->fieldy), MAX_LEV_FIELDY);
6715 if (!reading_playfield)
6717 level->no_valid_file = TRUE;
6719 Warn("cannot read level '%s' -- using empty level", filename);
6724 if (*level_name != '\0')
6726 strncpy(level->name, level_name, MAX_LEVEL_NAME_LEN);
6727 level->name[MAX_LEVEL_NAME_LEN] = '\0';
6729 else if (*last_comment != '\0')
6731 strncpy(level->name, last_comment, MAX_LEVEL_NAME_LEN);
6732 level->name[MAX_LEVEL_NAME_LEN] = '\0';
6736 sprintf(level->name, "--> Level %d <--", level_file_info->nr);
6739 // set all empty fields beyond the border walls to invisible steel wall
6740 for (y = 0; y < level->fieldy; y++) for (x = 0; x < level->fieldx; x++)
6742 if ((x == 0 || x == level->fieldx - 1 ||
6743 y == 0 || y == level->fieldy - 1) &&
6744 level->field[x][y] == getMappedElement_SB(' ', load_xsb_to_ces))
6745 FloodFillLevel(x, y, getMappedElement_SB('_', load_xsb_to_ces),
6746 level->field, level->fieldx, level->fieldy);
6749 // set special level settings for Sokoban levels
6750 SetLevelSettings_SB(level);
6752 if (load_xsb_to_ces)
6754 // special global settings can now be set in level template
6755 level->use_custom_template = TRUE;
6760 // -------------------------------------------------------------------------
6761 // functions for handling native levels
6762 // -------------------------------------------------------------------------
6764 static void LoadLevelFromFileInfo_BD(struct LevelInfo *level,
6765 struct LevelFileInfo *level_file_info,
6766 boolean level_info_only)
6770 // determine position of requested level inside level package
6771 if (level_file_info->packed)
6772 pos = level_file_info->nr - leveldir_current->first_level;
6774 if (!LoadNativeLevel_BD(level_file_info->filename, pos, level_info_only))
6775 level->no_valid_file = TRUE;
6778 static void LoadLevelFromFileInfo_EM(struct LevelInfo *level,
6779 struct LevelFileInfo *level_file_info,
6780 boolean level_info_only)
6782 if (!LoadNativeLevel_EM(level_file_info->filename, level_info_only))
6783 level->no_valid_file = TRUE;
6786 static void LoadLevelFromFileInfo_SP(struct LevelInfo *level,
6787 struct LevelFileInfo *level_file_info,
6788 boolean level_info_only)
6792 // determine position of requested level inside level package
6793 if (level_file_info->packed)
6794 pos = level_file_info->nr - leveldir_current->first_level;
6796 if (!LoadNativeLevel_SP(level_file_info->filename, pos, level_info_only))
6797 level->no_valid_file = TRUE;
6800 static void LoadLevelFromFileInfo_MM(struct LevelInfo *level,
6801 struct LevelFileInfo *level_file_info,
6802 boolean level_info_only)
6804 if (!LoadNativeLevel_MM(level_file_info->filename, level_info_only))
6805 level->no_valid_file = TRUE;
6808 void CopyNativeLevel_RND_to_Native(struct LevelInfo *level)
6810 if (level->game_engine_type == GAME_ENGINE_TYPE_BD)
6811 CopyNativeLevel_RND_to_BD(level);
6812 else if (level->game_engine_type == GAME_ENGINE_TYPE_EM)
6813 CopyNativeLevel_RND_to_EM(level);
6814 else if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
6815 CopyNativeLevel_RND_to_SP(level);
6816 else if (level->game_engine_type == GAME_ENGINE_TYPE_MM)
6817 CopyNativeLevel_RND_to_MM(level);
6820 void CopyNativeLevel_Native_to_RND(struct LevelInfo *level)
6822 if (level->game_engine_type == GAME_ENGINE_TYPE_BD)
6823 CopyNativeLevel_BD_to_RND(level);
6824 else if (level->game_engine_type == GAME_ENGINE_TYPE_EM)
6825 CopyNativeLevel_EM_to_RND(level);
6826 else if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
6827 CopyNativeLevel_SP_to_RND(level);
6828 else if (level->game_engine_type == GAME_ENGINE_TYPE_MM)
6829 CopyNativeLevel_MM_to_RND(level);
6832 void SaveNativeLevel(struct LevelInfo *level)
6834 // saving native level files only supported for some game engines
6835 if (level->game_engine_type != GAME_ENGINE_TYPE_BD &&
6836 level->game_engine_type != GAME_ENGINE_TYPE_SP)
6839 char *file_ext = (level->game_engine_type == GAME_ENGINE_TYPE_BD ? "bd" :
6840 level->game_engine_type == GAME_ENGINE_TYPE_SP ? "sp" : "");
6841 char *basename = getSingleLevelBasenameExt(level->file_info.nr, file_ext);
6842 char *filename = getLevelFilenameFromBasename(basename);
6844 if (fileExists(filename) && !Request("Native level file already exists! Overwrite it?", REQ_ASK))
6847 boolean success = FALSE;
6849 if (level->game_engine_type == GAME_ENGINE_TYPE_BD)
6851 CopyNativeLevel_RND_to_BD(level);
6852 // CopyNativeTape_RND_to_BD(level);
6854 success = SaveNativeLevel_BD(filename);
6856 else if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
6858 CopyNativeLevel_RND_to_SP(level);
6859 CopyNativeTape_RND_to_SP(level);
6861 success = SaveNativeLevel_SP(filename);
6865 Request("Native level file saved!", REQ_CONFIRM);
6867 Request("Failed to save native level file!", REQ_CONFIRM);
6871 // ----------------------------------------------------------------------------
6872 // functions for loading generic level
6873 // ----------------------------------------------------------------------------
6875 static void LoadLevelFromFileInfo(struct LevelInfo *level,
6876 struct LevelFileInfo *level_file_info,
6877 boolean level_info_only)
6879 // always start with reliable default values
6880 setLevelInfoToDefaults(level, level_info_only, TRUE);
6882 switch (level_file_info->type)
6884 case LEVEL_FILE_TYPE_RND:
6885 LoadLevelFromFileInfo_RND(level, level_file_info, level_info_only);
6888 case LEVEL_FILE_TYPE_BD:
6889 LoadLevelFromFileInfo_BD(level, level_file_info, level_info_only);
6890 level->game_engine_type = GAME_ENGINE_TYPE_BD;
6893 case LEVEL_FILE_TYPE_EM:
6894 LoadLevelFromFileInfo_EM(level, level_file_info, level_info_only);
6895 level->game_engine_type = GAME_ENGINE_TYPE_EM;
6898 case LEVEL_FILE_TYPE_SP:
6899 LoadLevelFromFileInfo_SP(level, level_file_info, level_info_only);
6900 level->game_engine_type = GAME_ENGINE_TYPE_SP;
6903 case LEVEL_FILE_TYPE_MM:
6904 LoadLevelFromFileInfo_MM(level, level_file_info, level_info_only);
6905 level->game_engine_type = GAME_ENGINE_TYPE_MM;
6908 case LEVEL_FILE_TYPE_DC:
6909 LoadLevelFromFileInfo_DC(level, level_file_info, level_info_only);
6912 case LEVEL_FILE_TYPE_SB:
6913 LoadLevelFromFileInfo_SB(level, level_file_info, level_info_only);
6917 LoadLevelFromFileInfo_RND(level, level_file_info, level_info_only);
6921 // if level file is invalid, restore level structure to default values
6922 if (level->no_valid_file)
6923 setLevelInfoToDefaults(level, level_info_only, FALSE);
6925 if (check_special_flags("use_native_bd_game_engine"))
6926 level->game_engine_type = GAME_ENGINE_TYPE_BD;
6928 if (level->game_engine_type == GAME_ENGINE_TYPE_UNKNOWN)
6929 level->game_engine_type = GAME_ENGINE_TYPE_RND;
6931 if (level_file_info->type != LEVEL_FILE_TYPE_RND)
6932 CopyNativeLevel_Native_to_RND(level);
6935 void LoadLevelFromFilename(struct LevelInfo *level, char *filename)
6937 static struct LevelFileInfo level_file_info;
6939 // always start with reliable default values
6940 setFileInfoToDefaults(&level_file_info);
6942 level_file_info.nr = 0; // unknown level number
6943 level_file_info.type = LEVEL_FILE_TYPE_RND; // no others supported yet
6945 setString(&level_file_info.filename, filename);
6947 LoadLevelFromFileInfo(level, &level_file_info, FALSE);
6950 static void LoadLevel_InitVersion(struct LevelInfo *level)
6954 if (leveldir_current == NULL) // only when dumping level
6957 // all engine modifications also valid for levels which use latest engine
6958 if (level->game_version < VERSION_IDENT(3,2,0,5))
6960 // time bonus score was given for 10 s instead of 1 s before 3.2.0-5
6961 level->time_score_base = 10;
6964 if (leveldir_current->latest_engine)
6966 // ---------- use latest game engine --------------------------------------
6968 /* For all levels which are forced to use the latest game engine version
6969 (normally all but user contributed, private and undefined levels), set
6970 the game engine version to the actual version; this allows for actual
6971 corrections in the game engine to take effect for existing, converted
6972 levels (from "classic" or other existing games) to make the emulation
6973 of the corresponding game more accurate, while (hopefully) not breaking
6974 existing levels created from other players. */
6976 level->game_version = GAME_VERSION_ACTUAL;
6978 /* Set special EM style gems behaviour: EM style gems slip down from
6979 normal, steel and growing wall. As this is a more fundamental change,
6980 it seems better to set the default behaviour to "off" (as it is more
6981 natural) and make it configurable in the level editor (as a property
6982 of gem style elements). Already existing converted levels (neither
6983 private nor contributed levels) are changed to the new behaviour. */
6985 if (level->file_version < FILE_VERSION_2_0)
6986 level->em_slippery_gems = TRUE;
6991 // ---------- use game engine the level was created with --------------------
6993 /* For all levels which are not forced to use the latest game engine
6994 version (normally user contributed, private and undefined levels),
6995 use the version of the game engine the levels were created for.
6997 Since 2.0.1, the game engine version is now directly stored
6998 in the level file (chunk "VERS"), so there is no need anymore
6999 to set the game version from the file version (except for old,
7000 pre-2.0 levels, where the game version is still taken from the
7001 file format version used to store the level -- see above). */
7003 // player was faster than enemies in 1.0.0 and before
7004 if (level->file_version == FILE_VERSION_1_0)
7005 for (i = 0; i < MAX_PLAYERS; i++)
7006 level->initial_player_stepsize[i] = STEPSIZE_FAST;
7008 // default behaviour for EM style gems was "slippery" only in 2.0.1
7009 if (level->game_version == VERSION_IDENT(2,0,1,0))
7010 level->em_slippery_gems = TRUE;
7012 // springs could be pushed over pits before (pre-release version) 2.2.0
7013 if (level->game_version < VERSION_IDENT(2,2,0,0))
7014 level->use_spring_bug = TRUE;
7016 if (level->game_version < VERSION_IDENT(3,2,0,5))
7018 // time orb caused limited time in endless time levels before 3.2.0-5
7019 level->use_time_orb_bug = TRUE;
7021 // default behaviour for snapping was "no snap delay" before 3.2.0-5
7022 level->block_snap_field = FALSE;
7024 // extra time score was same value as time left score before 3.2.0-5
7025 level->extra_time_score = level->score[SC_TIME_BONUS];
7028 if (level->game_version < VERSION_IDENT(3,2,0,7))
7030 // default behaviour for snapping was "not continuous" before 3.2.0-7
7031 level->continuous_snapping = FALSE;
7034 // only few elements were able to actively move into acid before 3.1.0
7035 // trigger settings did not exist before 3.1.0; set to default "any"
7036 if (level->game_version < VERSION_IDENT(3,1,0,0))
7038 // correct "can move into acid" settings (all zero in old levels)
7040 level->can_move_into_acid_bits = 0; // nothing can move into acid
7041 level->dont_collide_with_bits = 0; // nothing is deadly when colliding
7043 setMoveIntoAcidProperty(level, EL_ROBOT, TRUE);
7044 setMoveIntoAcidProperty(level, EL_SATELLITE, TRUE);
7045 setMoveIntoAcidProperty(level, EL_PENGUIN, TRUE);
7046 setMoveIntoAcidProperty(level, EL_BALLOON, TRUE);
7048 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7049 SET_PROPERTY(EL_CUSTOM_START + i, EP_CAN_MOVE_INTO_ACID, TRUE);
7051 // correct trigger settings (stored as zero == "none" in old levels)
7053 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7055 int element = EL_CUSTOM_START + i;
7056 struct ElementInfo *ei = &element_info[element];
7058 for (j = 0; j < ei->num_change_pages; j++)
7060 struct ElementChangeInfo *change = &ei->change_page[j];
7062 change->trigger_player = CH_PLAYER_ANY;
7063 change->trigger_page = CH_PAGE_ANY;
7068 // try to detect and fix "Snake Bite" levels, which are broken with 3.2.0
7070 int element = EL_CUSTOM_256;
7071 struct ElementInfo *ei = &element_info[element];
7072 struct ElementChangeInfo *change = &ei->change_page[0];
7074 /* This is needed to fix a problem that was caused by a bugfix in function
7075 game.c/CreateFieldExt() introduced with 3.2.0 that corrects the behaviour
7076 when a custom element changes to EL_SOKOBAN_FIELD_PLAYER (before, it did
7077 not replace walkable elements, but instead just placed the player on it,
7078 without placing the Sokoban field under the player). Unfortunately, this
7079 breaks "Snake Bite" style levels when the snake is halfway through a door
7080 that just closes (the snake head is still alive and can be moved in this
7081 case). This can be fixed by replacing the EL_SOKOBAN_FIELD_PLAYER by the
7082 player (without Sokoban element) which then gets killed as designed). */
7084 if ((strncmp(leveldir_current->identifier, "snake_bite", 10) == 0 ||
7085 strncmp(ei->description, "pause b4 death", 14) == 0) &&
7086 change->target_element == EL_SOKOBAN_FIELD_PLAYER)
7087 change->target_element = EL_PLAYER_1;
7090 // try to detect and fix "Zelda" style levels, which are broken with 3.2.5
7091 if (level->game_version < VERSION_IDENT(3,2,5,0))
7093 /* This is needed to fix a problem that was caused by a bugfix in function
7094 game.c/CheckTriggeredElementChangeExt() introduced with 3.2.5 that
7095 corrects the behaviour when a custom element changes to another custom
7096 element with a higher element number that has change actions defined.
7097 Normally, only one change per frame is allowed for custom elements.
7098 Therefore, it is checked if a custom element already changed in the
7099 current frame; if it did, subsequent changes are suppressed.
7100 Unfortunately, this is only checked for element changes, but not for
7101 change actions, which are still executed. As the function above loops
7102 through all custom elements from lower to higher, an element change
7103 resulting in a lower CE number won't be checked again, while a target
7104 element with a higher number will also be checked, and potential change
7105 actions will get executed for this CE, too (which is wrong), while
7106 further changes are ignored (which is correct). As this bugfix breaks
7107 Zelda II (and introduces graphical bugs to Zelda I, and also breaks a
7108 few other levels like Alan Bond's "FMV"), allow the previous, incorrect
7109 behaviour for existing levels and tapes that make use of this bug */
7111 level->use_action_after_change_bug = TRUE;
7114 // not centering level after relocating player was default only in 3.2.3
7115 if (level->game_version == VERSION_IDENT(3,2,3,0)) // (no pre-releases)
7116 level->shifted_relocation = TRUE;
7118 // EM style elements always chain-exploded in R'n'D engine before 3.2.6
7119 if (level->game_version < VERSION_IDENT(3,2,6,0))
7120 level->em_explodes_by_fire = TRUE;
7122 // levels were solved by the first player entering an exit up to 4.1.0.0
7123 if (level->game_version <= VERSION_IDENT(4,1,0,0))
7124 level->solved_by_one_player = TRUE;
7126 // game logic of "game of life" and "biomaze" was buggy before 4.1.1.1
7127 if (level->game_version < VERSION_IDENT(4,1,1,1))
7128 level->use_life_bugs = TRUE;
7130 // only Sokoban fields (but not objects) had to be solved before 4.1.1.1
7131 if (level->game_version < VERSION_IDENT(4,1,1,1))
7132 level->sb_objects_needed = FALSE;
7134 // CE actions were triggered by unfinished digging/collecting up to 4.2.2.0
7135 if (level->game_version <= VERSION_IDENT(4,2,2,0))
7136 level->finish_dig_collect = FALSE;
7138 // CE changing to player was kept under the player if walkable up to 4.2.3.1
7139 if (level->game_version <= VERSION_IDENT(4,2,3,1))
7140 level->keep_walkable_ce = TRUE;
7143 static void LoadLevel_InitSettings_SB(struct LevelInfo *level)
7145 boolean is_sokoban_level = TRUE; // unless non-Sokoban elements found
7148 // check if this level is (not) a Sokoban level
7149 for (y = 0; y < level->fieldy; y++)
7150 for (x = 0; x < level->fieldx; x++)
7151 if (!IS_SB_ELEMENT(Tile[x][y]))
7152 is_sokoban_level = FALSE;
7154 if (is_sokoban_level)
7156 // set special level settings for Sokoban levels
7157 SetLevelSettings_SB(level);
7161 static void LoadLevel_InitSettings(struct LevelInfo *level)
7163 // adjust level settings for (non-native) Sokoban-style levels
7164 LoadLevel_InitSettings_SB(level);
7166 // rename levels with title "nameless level" or if renaming is forced
7167 if (leveldir_current->empty_level_name != NULL &&
7168 (strEqual(level->name, NAMELESS_LEVEL_NAME) ||
7169 leveldir_current->force_level_name))
7170 snprintf(level->name, MAX_LEVEL_NAME_LEN + 1,
7171 leveldir_current->empty_level_name, level_nr);
7174 static void LoadLevel_InitStandardElements(struct LevelInfo *level)
7178 // map elements that have changed in newer versions
7179 level->amoeba_content = getMappedElementByVersion(level->amoeba_content,
7180 level->game_version);
7181 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
7182 for (x = 0; x < 3; x++)
7183 for (y = 0; y < 3; y++)
7184 level->yamyam_content[i].e[x][y] =
7185 getMappedElementByVersion(level->yamyam_content[i].e[x][y],
7186 level->game_version);
7190 static void LoadLevel_InitCustomElements(struct LevelInfo *level)
7194 // map custom element change events that have changed in newer versions
7195 // (these following values were accidentally changed in version 3.0.1)
7196 // (this seems to be needed only for 'ab_levelset3' and 'ab_levelset4')
7197 if (level->game_version <= VERSION_IDENT(3,0,0,0))
7199 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7201 int element = EL_CUSTOM_START + i;
7203 // order of checking and copying events to be mapped is important
7204 // (do not change the start and end value -- they are constant)
7205 for (j = CE_BY_OTHER_ACTION; j >= CE_VALUE_GETS_ZERO; j--)
7207 if (HAS_CHANGE_EVENT(element, j - 2))
7209 SET_CHANGE_EVENT(element, j - 2, FALSE);
7210 SET_CHANGE_EVENT(element, j, TRUE);
7214 // order of checking and copying events to be mapped is important
7215 // (do not change the start and end value -- they are constant)
7216 for (j = CE_PLAYER_COLLECTS_X; j >= CE_HITTING_SOMETHING; j--)
7218 if (HAS_CHANGE_EVENT(element, j - 1))
7220 SET_CHANGE_EVENT(element, j - 1, FALSE);
7221 SET_CHANGE_EVENT(element, j, TRUE);
7227 // initialize "can_change" field for old levels with only one change page
7228 if (level->game_version <= VERSION_IDENT(3,0,2,0))
7230 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7232 int element = EL_CUSTOM_START + i;
7234 if (CAN_CHANGE(element))
7235 element_info[element].change->can_change = TRUE;
7239 // correct custom element values (for old levels without these options)
7240 if (level->game_version < VERSION_IDENT(3,1,1,0))
7242 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7244 int element = EL_CUSTOM_START + i;
7245 struct ElementInfo *ei = &element_info[element];
7247 if (ei->access_direction == MV_NO_DIRECTION)
7248 ei->access_direction = MV_ALL_DIRECTIONS;
7252 // correct custom element values (fix invalid values for all versions)
7255 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7257 int element = EL_CUSTOM_START + i;
7258 struct ElementInfo *ei = &element_info[element];
7260 for (j = 0; j < ei->num_change_pages; j++)
7262 struct ElementChangeInfo *change = &ei->change_page[j];
7264 if (change->trigger_player == CH_PLAYER_NONE)
7265 change->trigger_player = CH_PLAYER_ANY;
7267 if (change->trigger_side == CH_SIDE_NONE)
7268 change->trigger_side = CH_SIDE_ANY;
7273 // initialize "can_explode" field for old levels which did not store this
7274 // !!! CHECK THIS -- "<= 3,1,0,0" IS PROBABLY WRONG !!!
7275 if (level->game_version <= VERSION_IDENT(3,1,0,0))
7277 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7279 int element = EL_CUSTOM_START + i;
7281 if (EXPLODES_1X1_OLD(element))
7282 element_info[element].explosion_type = EXPLODES_1X1;
7284 SET_PROPERTY(element, EP_CAN_EXPLODE, (EXPLODES_BY_FIRE(element) ||
7285 EXPLODES_SMASHED(element) ||
7286 EXPLODES_IMPACT(element)));
7290 // correct previously hard-coded move delay values for maze runner style
7291 if (level->game_version < VERSION_IDENT(3,1,1,0))
7293 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7295 int element = EL_CUSTOM_START + i;
7297 if (element_info[element].move_pattern & MV_MAZE_RUNNER_STYLE)
7299 // previously hard-coded and therefore ignored
7300 element_info[element].move_delay_fixed = 9;
7301 element_info[element].move_delay_random = 0;
7306 // set some other uninitialized values of custom elements in older levels
7307 if (level->game_version < VERSION_IDENT(3,1,0,0))
7309 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7311 int element = EL_CUSTOM_START + i;
7313 element_info[element].access_direction = MV_ALL_DIRECTIONS;
7315 element_info[element].explosion_delay = 17;
7316 element_info[element].ignition_delay = 8;
7320 // set mouse click change events to work for left/middle/right mouse button
7321 if (level->game_version < VERSION_IDENT(4,2,3,0))
7323 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7325 int element = EL_CUSTOM_START + i;
7326 struct ElementInfo *ei = &element_info[element];
7328 for (j = 0; j < ei->num_change_pages; j++)
7330 struct ElementChangeInfo *change = &ei->change_page[j];
7332 if (change->has_event[CE_CLICKED_BY_MOUSE] ||
7333 change->has_event[CE_PRESSED_BY_MOUSE] ||
7334 change->has_event[CE_MOUSE_CLICKED_ON_X] ||
7335 change->has_event[CE_MOUSE_PRESSED_ON_X])
7336 change->trigger_side = CH_SIDE_ANY;
7342 static void LoadLevel_InitElements(struct LevelInfo *level)
7344 LoadLevel_InitStandardElements(level);
7346 if (level->file_has_custom_elements)
7347 LoadLevel_InitCustomElements(level);
7349 // initialize element properties for level editor etc.
7350 InitElementPropertiesEngine(level->game_version);
7351 InitElementPropertiesGfxElement();
7354 static void LoadLevel_InitPlayfield(struct LevelInfo *level)
7358 // map elements that have changed in newer versions
7359 for (y = 0; y < level->fieldy; y++)
7360 for (x = 0; x < level->fieldx; x++)
7361 level->field[x][y] = getMappedElementByVersion(level->field[x][y],
7362 level->game_version);
7364 // clear unused playfield data (nicer if level gets resized in editor)
7365 for (x = 0; x < MAX_LEV_FIELDX; x++)
7366 for (y = 0; y < MAX_LEV_FIELDY; y++)
7367 if (x >= level->fieldx || y >= level->fieldy)
7368 level->field[x][y] = EL_EMPTY;
7370 // copy elements to runtime playfield array
7371 for (x = 0; x < MAX_LEV_FIELDX; x++)
7372 for (y = 0; y < MAX_LEV_FIELDY; y++)
7373 Tile[x][y] = level->field[x][y];
7375 // initialize level size variables for faster access
7376 lev_fieldx = level->fieldx;
7377 lev_fieldy = level->fieldy;
7379 // determine border element for this level
7380 if (level->file_info.type == LEVEL_FILE_TYPE_DC)
7381 BorderElement = EL_EMPTY; // (in editor, SetBorderElement() is used)
7386 static void LoadLevel_InitNativeEngines(struct LevelInfo *level)
7388 struct LevelFileInfo *level_file_info = &level->file_info;
7390 if (level_file_info->type == LEVEL_FILE_TYPE_RND)
7391 CopyNativeLevel_RND_to_Native(level);
7394 static void LoadLevelTemplate_LoadAndInit(void)
7396 LoadLevelFromFileInfo(&level_template, &level_template.file_info, FALSE);
7398 LoadLevel_InitVersion(&level_template);
7399 LoadLevel_InitElements(&level_template);
7400 LoadLevel_InitSettings(&level_template);
7402 ActivateLevelTemplate();
7405 void LoadLevelTemplate(int nr)
7407 if (!fileExists(getGlobalLevelTemplateFilename()))
7409 Warn("no level template found for this level");
7414 setLevelFileInfo(&level_template.file_info, nr);
7416 LoadLevelTemplate_LoadAndInit();
7419 static void LoadNetworkLevelTemplate(struct NetworkLevelInfo *network_level)
7421 copyLevelFileInfo(&network_level->tmpl_info, &level_template.file_info);
7423 LoadLevelTemplate_LoadAndInit();
7426 static void LoadLevel_LoadAndInit(struct NetworkLevelInfo *network_level)
7428 LoadLevelFromFileInfo(&level, &level.file_info, FALSE);
7430 if (level.use_custom_template)
7432 if (network_level != NULL)
7433 LoadNetworkLevelTemplate(network_level);
7435 LoadLevelTemplate(-1);
7438 LoadLevel_InitVersion(&level);
7439 LoadLevel_InitElements(&level);
7440 LoadLevel_InitPlayfield(&level);
7441 LoadLevel_InitSettings(&level);
7443 LoadLevel_InitNativeEngines(&level);
7446 void LoadLevel(int nr)
7448 SetLevelSetInfo(leveldir_current->identifier, nr);
7450 setLevelFileInfo(&level.file_info, nr);
7452 LoadLevel_LoadAndInit(NULL);
7455 void LoadLevelInfoOnly(int nr)
7457 setLevelFileInfo(&level.file_info, nr);
7459 LoadLevelFromFileInfo(&level, &level.file_info, TRUE);
7462 void LoadNetworkLevel(struct NetworkLevelInfo *network_level)
7464 SetLevelSetInfo(network_level->leveldir_identifier,
7465 network_level->file_info.nr);
7467 copyLevelFileInfo(&network_level->file_info, &level.file_info);
7469 LoadLevel_LoadAndInit(network_level);
7472 static int SaveLevel_VERS(FILE *file, struct LevelInfo *level)
7476 chunk_size += putFileVersion(file, level->file_version);
7477 chunk_size += putFileVersion(file, level->game_version);
7482 static int SaveLevel_DATE(FILE *file, struct LevelInfo *level)
7486 chunk_size += putFile16BitBE(file, level->creation_date.year);
7487 chunk_size += putFile8Bit(file, level->creation_date.month);
7488 chunk_size += putFile8Bit(file, level->creation_date.day);
7493 #if ENABLE_HISTORIC_CHUNKS
7494 static void SaveLevel_HEAD(FILE *file, struct LevelInfo *level)
7498 putFile8Bit(file, level->fieldx);
7499 putFile8Bit(file, level->fieldy);
7501 putFile16BitBE(file, level->time);
7502 putFile16BitBE(file, level->gems_needed);
7504 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
7505 putFile8Bit(file, level->name[i]);
7507 for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
7508 putFile8Bit(file, level->score[i]);
7510 for (i = 0; i < STD_ELEMENT_CONTENTS; i++)
7511 for (y = 0; y < 3; y++)
7512 for (x = 0; x < 3; x++)
7513 putFile8Bit(file, (level->encoding_16bit_yamyam ? EL_EMPTY :
7514 level->yamyam_content[i].e[x][y]));
7515 putFile8Bit(file, level->amoeba_speed);
7516 putFile8Bit(file, level->time_magic_wall);
7517 putFile8Bit(file, level->time_wheel);
7518 putFile8Bit(file, (level->encoding_16bit_amoeba ? EL_EMPTY :
7519 level->amoeba_content));
7520 putFile8Bit(file, (level->initial_player_stepsize == STEPSIZE_FAST ? 1 : 0));
7521 putFile8Bit(file, (level->initial_gravity ? 1 : 0));
7522 putFile8Bit(file, (level->encoding_16bit_field ? 1 : 0));
7523 putFile8Bit(file, (level->em_slippery_gems ? 1 : 0));
7525 putFile8Bit(file, (level->use_custom_template ? 1 : 0));
7527 putFile8Bit(file, (level->block_last_field ? 1 : 0));
7528 putFile8Bit(file, (level->sp_block_last_field ? 1 : 0));
7529 putFile32BitBE(file, level->can_move_into_acid_bits);
7530 putFile8Bit(file, level->dont_collide_with_bits);
7532 putFile8Bit(file, (level->use_spring_bug ? 1 : 0));
7533 putFile8Bit(file, (level->use_step_counter ? 1 : 0));
7535 putFile8Bit(file, (level->instant_relocation ? 1 : 0));
7536 putFile8Bit(file, (level->can_pass_to_walkable ? 1 : 0));
7537 putFile8Bit(file, (level->grow_into_diggable ? 1 : 0));
7539 putFile8Bit(file, level->game_engine_type);
7541 WriteUnusedBytesToFile(file, LEVEL_CHUNK_HEAD_UNUSED);
7545 static int SaveLevel_NAME(FILE *file, struct LevelInfo *level)
7550 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
7551 chunk_size += putFile8Bit(file, level->name[i]);
7556 static int SaveLevel_AUTH(FILE *file, struct LevelInfo *level)
7561 for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
7562 chunk_size += putFile8Bit(file, level->author[i]);
7567 #if ENABLE_HISTORIC_CHUNKS
7568 static int SaveLevel_BODY(FILE *file, struct LevelInfo *level)
7573 for (y = 0; y < level->fieldy; y++)
7574 for (x = 0; x < level->fieldx; x++)
7575 if (level->encoding_16bit_field)
7576 chunk_size += putFile16BitBE(file, level->field[x][y]);
7578 chunk_size += putFile8Bit(file, level->field[x][y]);
7584 static int SaveLevel_BODY(FILE *file, struct LevelInfo *level)
7589 for (y = 0; y < level->fieldy; y++)
7590 for (x = 0; x < level->fieldx; x++)
7591 chunk_size += putFile16BitBE(file, level->field[x][y]);
7596 #if ENABLE_HISTORIC_CHUNKS
7597 static void SaveLevel_CONT(FILE *file, struct LevelInfo *level)
7601 putFile8Bit(file, EL_YAMYAM);
7602 putFile8Bit(file, level->num_yamyam_contents);
7603 putFile8Bit(file, 0);
7604 putFile8Bit(file, 0);
7606 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
7607 for (y = 0; y < 3; y++)
7608 for (x = 0; x < 3; x++)
7609 if (level->encoding_16bit_field)
7610 putFile16BitBE(file, level->yamyam_content[i].e[x][y]);
7612 putFile8Bit(file, level->yamyam_content[i].e[x][y]);
7616 #if ENABLE_HISTORIC_CHUNKS
7617 static void SaveLevel_CNT2(FILE *file, struct LevelInfo *level, int element)
7620 int num_contents, content_xsize, content_ysize;
7621 int content_array[MAX_ELEMENT_CONTENTS][3][3];
7623 if (element == EL_YAMYAM)
7625 num_contents = level->num_yamyam_contents;
7629 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
7630 for (y = 0; y < 3; y++)
7631 for (x = 0; x < 3; x++)
7632 content_array[i][x][y] = level->yamyam_content[i].e[x][y];
7634 else if (element == EL_BD_AMOEBA)
7640 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
7641 for (y = 0; y < 3; y++)
7642 for (x = 0; x < 3; x++)
7643 content_array[i][x][y] = EL_EMPTY;
7644 content_array[0][0][0] = level->amoeba_content;
7648 // chunk header already written -- write empty chunk data
7649 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_SIZE);
7651 Warn("cannot save content for element '%d'", element);
7656 putFile16BitBE(file, element);
7657 putFile8Bit(file, num_contents);
7658 putFile8Bit(file, content_xsize);
7659 putFile8Bit(file, content_ysize);
7661 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_UNUSED);
7663 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
7664 for (y = 0; y < 3; y++)
7665 for (x = 0; x < 3; x++)
7666 putFile16BitBE(file, content_array[i][x][y]);
7670 #if ENABLE_HISTORIC_CHUNKS
7671 static int SaveLevel_CNT3(FILE *file, struct LevelInfo *level, int element)
7673 int envelope_nr = element - EL_ENVELOPE_1;
7674 int envelope_len = strlen(level->envelope_text[envelope_nr]) + 1;
7678 chunk_size += putFile16BitBE(file, element);
7679 chunk_size += putFile16BitBE(file, envelope_len);
7680 chunk_size += putFile8Bit(file, level->envelope_xsize[envelope_nr]);
7681 chunk_size += putFile8Bit(file, level->envelope_ysize[envelope_nr]);
7683 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT3_UNUSED);
7684 chunk_size += LEVEL_CHUNK_CNT3_UNUSED;
7686 for (i = 0; i < envelope_len; i++)
7687 chunk_size += putFile8Bit(file, level->envelope_text[envelope_nr][i]);
7693 #if ENABLE_HISTORIC_CHUNKS
7694 static void SaveLevel_CUS1(FILE *file, struct LevelInfo *level,
7695 int num_changed_custom_elements)
7699 putFile16BitBE(file, num_changed_custom_elements);
7701 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7703 int element = EL_CUSTOM_START + i;
7705 struct ElementInfo *ei = &element_info[element];
7707 if (ei->properties[EP_BITFIELD_BASE_NR] != EP_BITMASK_DEFAULT)
7709 if (check < num_changed_custom_elements)
7711 putFile16BitBE(file, element);
7712 putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
7719 if (check != num_changed_custom_elements) // should not happen
7720 Warn("inconsistent number of custom element properties");
7724 #if ENABLE_HISTORIC_CHUNKS
7725 static void SaveLevel_CUS2(FILE *file, struct LevelInfo *level,
7726 int num_changed_custom_elements)
7730 putFile16BitBE(file, num_changed_custom_elements);
7732 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7734 int element = EL_CUSTOM_START + i;
7736 if (element_info[element].change->target_element != EL_EMPTY_SPACE)
7738 if (check < num_changed_custom_elements)
7740 putFile16BitBE(file, element);
7741 putFile16BitBE(file, element_info[element].change->target_element);
7748 if (check != num_changed_custom_elements) // should not happen
7749 Warn("inconsistent number of custom target elements");
7753 #if ENABLE_HISTORIC_CHUNKS
7754 static void SaveLevel_CUS3(FILE *file, struct LevelInfo *level,
7755 int num_changed_custom_elements)
7757 int i, j, x, y, check = 0;
7759 putFile16BitBE(file, num_changed_custom_elements);
7761 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7763 int element = EL_CUSTOM_START + i;
7764 struct ElementInfo *ei = &element_info[element];
7766 if (ei->modified_settings)
7768 if (check < num_changed_custom_elements)
7770 putFile16BitBE(file, element);
7772 for (j = 0; j < MAX_ELEMENT_NAME_LEN; j++)
7773 putFile8Bit(file, ei->description[j]);
7775 putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
7777 // some free bytes for future properties and padding
7778 WriteUnusedBytesToFile(file, 7);
7780 putFile8Bit(file, ei->use_gfx_element);
7781 putFile16BitBE(file, ei->gfx_element_initial);
7783 putFile8Bit(file, ei->collect_score_initial);
7784 putFile8Bit(file, ei->collect_count_initial);
7786 putFile16BitBE(file, ei->push_delay_fixed);
7787 putFile16BitBE(file, ei->push_delay_random);
7788 putFile16BitBE(file, ei->move_delay_fixed);
7789 putFile16BitBE(file, ei->move_delay_random);
7791 putFile16BitBE(file, ei->move_pattern);
7792 putFile8Bit(file, ei->move_direction_initial);
7793 putFile8Bit(file, ei->move_stepsize);
7795 for (y = 0; y < 3; y++)
7796 for (x = 0; x < 3; x++)
7797 putFile16BitBE(file, ei->content.e[x][y]);
7799 putFile32BitBE(file, ei->change->events);
7801 putFile16BitBE(file, ei->change->target_element);
7803 putFile16BitBE(file, ei->change->delay_fixed);
7804 putFile16BitBE(file, ei->change->delay_random);
7805 putFile16BitBE(file, ei->change->delay_frames);
7807 putFile16BitBE(file, ei->change->initial_trigger_element);
7809 putFile8Bit(file, ei->change->explode);
7810 putFile8Bit(file, ei->change->use_target_content);
7811 putFile8Bit(file, ei->change->only_if_complete);
7812 putFile8Bit(file, ei->change->use_random_replace);
7814 putFile8Bit(file, ei->change->random_percentage);
7815 putFile8Bit(file, ei->change->replace_when);
7817 for (y = 0; y < 3; y++)
7818 for (x = 0; x < 3; x++)
7819 putFile16BitBE(file, ei->change->content.e[x][y]);
7821 putFile8Bit(file, ei->slippery_type);
7823 // some free bytes for future properties and padding
7824 WriteUnusedBytesToFile(file, LEVEL_CPART_CUS3_UNUSED);
7831 if (check != num_changed_custom_elements) // should not happen
7832 Warn("inconsistent number of custom element properties");
7836 #if ENABLE_HISTORIC_CHUNKS
7837 static void SaveLevel_CUS4(FILE *file, struct LevelInfo *level, int element)
7839 struct ElementInfo *ei = &element_info[element];
7842 // ---------- custom element base property values (96 bytes) ----------------
7844 putFile16BitBE(file, element);
7846 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
7847 putFile8Bit(file, ei->description[i]);
7849 putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
7851 WriteUnusedBytesToFile(file, 4); // reserved for more base properties
7853 putFile8Bit(file, ei->num_change_pages);
7855 putFile16BitBE(file, ei->ce_value_fixed_initial);
7856 putFile16BitBE(file, ei->ce_value_random_initial);
7857 putFile8Bit(file, ei->use_last_ce_value);
7859 putFile8Bit(file, ei->use_gfx_element);
7860 putFile16BitBE(file, ei->gfx_element_initial);
7862 putFile8Bit(file, ei->collect_score_initial);
7863 putFile8Bit(file, ei->collect_count_initial);
7865 putFile8Bit(file, ei->drop_delay_fixed);
7866 putFile8Bit(file, ei->push_delay_fixed);
7867 putFile8Bit(file, ei->drop_delay_random);
7868 putFile8Bit(file, ei->push_delay_random);
7869 putFile16BitBE(file, ei->move_delay_fixed);
7870 putFile16BitBE(file, ei->move_delay_random);
7872 // bits 0 - 15 of "move_pattern" ...
7873 putFile16BitBE(file, ei->move_pattern & 0xffff);
7874 putFile8Bit(file, ei->move_direction_initial);
7875 putFile8Bit(file, ei->move_stepsize);
7877 putFile8Bit(file, ei->slippery_type);
7879 for (y = 0; y < 3; y++)
7880 for (x = 0; x < 3; x++)
7881 putFile16BitBE(file, ei->content.e[x][y]);
7883 putFile16BitBE(file, ei->move_enter_element);
7884 putFile16BitBE(file, ei->move_leave_element);
7885 putFile8Bit(file, ei->move_leave_type);
7887 // ... bits 16 - 31 of "move_pattern" (not nice, but downward compatible)
7888 putFile16BitBE(file, (ei->move_pattern >> 16) & 0xffff);
7890 putFile8Bit(file, ei->access_direction);
7892 putFile8Bit(file, ei->explosion_delay);
7893 putFile8Bit(file, ei->ignition_delay);
7894 putFile8Bit(file, ei->explosion_type);
7896 // some free bytes for future custom property values and padding
7897 WriteUnusedBytesToFile(file, 1);
7899 // ---------- change page property values (48 bytes) ------------------------
7901 for (i = 0; i < ei->num_change_pages; i++)
7903 struct ElementChangeInfo *change = &ei->change_page[i];
7904 unsigned int event_bits;
7906 // bits 0 - 31 of "has_event[]" ...
7908 for (j = 0; j < MIN(NUM_CHANGE_EVENTS, 32); j++)
7909 if (change->has_event[j])
7910 event_bits |= (1u << j);
7911 putFile32BitBE(file, event_bits);
7913 putFile16BitBE(file, change->target_element);
7915 putFile16BitBE(file, change->delay_fixed);
7916 putFile16BitBE(file, change->delay_random);
7917 putFile16BitBE(file, change->delay_frames);
7919 putFile16BitBE(file, change->initial_trigger_element);
7921 putFile8Bit(file, change->explode);
7922 putFile8Bit(file, change->use_target_content);
7923 putFile8Bit(file, change->only_if_complete);
7924 putFile8Bit(file, change->use_random_replace);
7926 putFile8Bit(file, change->random_percentage);
7927 putFile8Bit(file, change->replace_when);
7929 for (y = 0; y < 3; y++)
7930 for (x = 0; x < 3; x++)
7931 putFile16BitBE(file, change->target_content.e[x][y]);
7933 putFile8Bit(file, change->can_change);
7935 putFile8Bit(file, change->trigger_side);
7937 putFile8Bit(file, change->trigger_player);
7938 putFile8Bit(file, (change->trigger_page == CH_PAGE_ANY ? CH_PAGE_ANY_FILE :
7939 log_2(change->trigger_page)));
7941 putFile8Bit(file, change->has_action);
7942 putFile8Bit(file, change->action_type);
7943 putFile8Bit(file, change->action_mode);
7944 putFile16BitBE(file, change->action_arg);
7946 // ... bits 32 - 39 of "has_event[]" (not nice, but downward compatible)
7948 for (j = 32; j < NUM_CHANGE_EVENTS; j++)
7949 if (change->has_event[j])
7950 event_bits |= (1u << (j - 32));
7951 putFile8Bit(file, event_bits);
7956 #if ENABLE_HISTORIC_CHUNKS
7957 static void SaveLevel_GRP1(FILE *file, struct LevelInfo *level, int element)
7959 struct ElementInfo *ei = &element_info[element];
7960 struct ElementGroupInfo *group = ei->group;
7963 putFile16BitBE(file, element);
7965 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
7966 putFile8Bit(file, ei->description[i]);
7968 putFile8Bit(file, group->num_elements);
7970 putFile8Bit(file, ei->use_gfx_element);
7971 putFile16BitBE(file, ei->gfx_element_initial);
7973 putFile8Bit(file, group->choice_mode);
7975 // some free bytes for future values and padding
7976 WriteUnusedBytesToFile(file, 3);
7978 for (i = 0; i < MAX_ELEMENTS_IN_GROUP; i++)
7979 putFile16BitBE(file, group->element[i]);
7983 static int SaveLevel_MicroChunk(FILE *file, struct LevelFileConfigInfo *entry,
7984 boolean write_element)
7986 int save_type = entry->save_type;
7987 int data_type = entry->data_type;
7988 int conf_type = entry->conf_type;
7989 int byte_mask = conf_type & CONF_MASK_BYTES;
7990 int element = entry->element;
7991 int default_value = entry->default_value;
7993 boolean modified = FALSE;
7995 if (byte_mask != CONF_MASK_MULTI_BYTES)
7997 void *value_ptr = entry->value;
7998 int value = (data_type == TYPE_BOOLEAN ? *(boolean *)value_ptr :
8001 // check if any settings have been modified before saving them
8002 if (value != default_value)
8005 // do not save if explicitly told or if unmodified default settings
8006 if ((save_type == SAVE_CONF_NEVER) ||
8007 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
8011 num_bytes += putFile16BitBE(file, element);
8013 num_bytes += putFile8Bit(file, conf_type);
8014 num_bytes += (byte_mask == CONF_MASK_1_BYTE ? putFile8Bit (file, value) :
8015 byte_mask == CONF_MASK_2_BYTE ? putFile16BitBE(file, value) :
8016 byte_mask == CONF_MASK_4_BYTE ? putFile32BitBE(file, value) :
8019 else if (data_type == TYPE_STRING)
8021 char *default_string = entry->default_string;
8022 char *string = (char *)(entry->value);
8023 int string_length = strlen(string);
8026 // check if any settings have been modified before saving them
8027 if (!strEqual(string, default_string))
8030 // do not save if explicitly told or if unmodified default settings
8031 if ((save_type == SAVE_CONF_NEVER) ||
8032 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
8036 num_bytes += putFile16BitBE(file, element);
8038 num_bytes += putFile8Bit(file, conf_type);
8039 num_bytes += putFile16BitBE(file, string_length);
8041 for (i = 0; i < string_length; i++)
8042 num_bytes += putFile8Bit(file, string[i]);
8044 else if (data_type == TYPE_ELEMENT_LIST)
8046 int *element_array = (int *)(entry->value);
8047 int num_elements = *(int *)(entry->num_entities);
8050 // check if any settings have been modified before saving them
8051 for (i = 0; i < num_elements; i++)
8052 if (element_array[i] != default_value)
8055 // do not save if explicitly told or if unmodified default settings
8056 if ((save_type == SAVE_CONF_NEVER) ||
8057 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
8061 num_bytes += putFile16BitBE(file, element);
8063 num_bytes += putFile8Bit(file, conf_type);
8064 num_bytes += putFile16BitBE(file, num_elements * CONF_ELEMENT_NUM_BYTES);
8066 for (i = 0; i < num_elements; i++)
8067 num_bytes += putFile16BitBE(file, element_array[i]);
8069 else if (data_type == TYPE_CONTENT_LIST)
8071 struct Content *content = (struct Content *)(entry->value);
8072 int num_contents = *(int *)(entry->num_entities);
8075 // check if any settings have been modified before saving them
8076 for (i = 0; i < num_contents; i++)
8077 for (y = 0; y < 3; y++)
8078 for (x = 0; x < 3; x++)
8079 if (content[i].e[x][y] != default_value)
8082 // do not save if explicitly told or if unmodified default settings
8083 if ((save_type == SAVE_CONF_NEVER) ||
8084 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
8088 num_bytes += putFile16BitBE(file, element);
8090 num_bytes += putFile8Bit(file, conf_type);
8091 num_bytes += putFile16BitBE(file, num_contents * CONF_CONTENT_NUM_BYTES);
8093 for (i = 0; i < num_contents; i++)
8094 for (y = 0; y < 3; y++)
8095 for (x = 0; x < 3; x++)
8096 num_bytes += putFile16BitBE(file, content[i].e[x][y]);
8102 static int SaveLevel_INFO(FILE *file, struct LevelInfo *level)
8107 li = *level; // copy level data into temporary buffer
8109 for (i = 0; chunk_config_INFO[i].data_type != -1; i++)
8110 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_INFO[i], FALSE);
8115 static int SaveLevel_ELEM(FILE *file, struct LevelInfo *level)
8120 li = *level; // copy level data into temporary buffer
8122 for (i = 0; chunk_config_ELEM[i].data_type != -1; i++)
8123 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_ELEM[i], TRUE);
8128 static int SaveLevel_NOTE(FILE *file, struct LevelInfo *level, int element)
8130 int envelope_nr = element - EL_ENVELOPE_1;
8134 chunk_size += putFile16BitBE(file, element);
8136 // copy envelope data into temporary buffer
8137 xx_envelope = level->envelope[envelope_nr];
8139 for (i = 0; chunk_config_NOTE[i].data_type != -1; i++)
8140 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_NOTE[i], FALSE);
8145 static int SaveLevel_CUSX(FILE *file, struct LevelInfo *level, int element)
8147 struct ElementInfo *ei = &element_info[element];
8151 chunk_size += putFile16BitBE(file, element);
8153 xx_ei = *ei; // copy element data into temporary buffer
8155 // set default description string for this specific element
8156 strcpy(xx_default_description, getDefaultElementDescription(ei));
8158 for (i = 0; chunk_config_CUSX_base[i].data_type != -1; i++)
8159 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_CUSX_base[i], FALSE);
8161 for (i = 0; i < ei->num_change_pages; i++)
8163 struct ElementChangeInfo *change = &ei->change_page[i];
8165 xx_current_change_page = i;
8167 xx_change = *change; // copy change data into temporary buffer
8170 setEventBitsFromEventFlags(change);
8172 for (j = 0; chunk_config_CUSX_change[j].data_type != -1; j++)
8173 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_CUSX_change[j],
8180 static int SaveLevel_GRPX(FILE *file, struct LevelInfo *level, int element)
8182 struct ElementInfo *ei = &element_info[element];
8183 struct ElementGroupInfo *group = ei->group;
8187 chunk_size += putFile16BitBE(file, element);
8189 xx_ei = *ei; // copy element data into temporary buffer
8190 xx_group = *group; // copy group data into temporary buffer
8192 // set default description string for this specific element
8193 strcpy(xx_default_description, getDefaultElementDescription(ei));
8195 for (i = 0; chunk_config_GRPX[i].data_type != -1; i++)
8196 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_GRPX[i], FALSE);
8201 static int SaveLevel_EMPX(FILE *file, struct LevelInfo *level, int element)
8203 struct ElementInfo *ei = &element_info[element];
8207 chunk_size += putFile16BitBE(file, element);
8209 xx_ei = *ei; // copy element data into temporary buffer
8211 for (i = 0; chunk_config_EMPX[i].data_type != -1; i++)
8212 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_EMPX[i], FALSE);
8217 static void SaveLevelFromFilename(struct LevelInfo *level, char *filename,
8218 boolean save_as_template)
8224 if (!(file = fopen(filename, MODE_WRITE)))
8226 Warn("cannot save level file '%s'", filename);
8231 level->file_version = FILE_VERSION_ACTUAL;
8232 level->game_version = GAME_VERSION_ACTUAL;
8234 level->creation_date = getCurrentDate();
8236 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
8237 putFileChunkBE(file, "CAVE", CHUNK_SIZE_NONE);
8239 chunk_size = SaveLevel_VERS(NULL, level);
8240 putFileChunkBE(file, "VERS", chunk_size);
8241 SaveLevel_VERS(file, level);
8243 chunk_size = SaveLevel_DATE(NULL, level);
8244 putFileChunkBE(file, "DATE", chunk_size);
8245 SaveLevel_DATE(file, level);
8247 chunk_size = SaveLevel_NAME(NULL, level);
8248 putFileChunkBE(file, "NAME", chunk_size);
8249 SaveLevel_NAME(file, level);
8251 chunk_size = SaveLevel_AUTH(NULL, level);
8252 putFileChunkBE(file, "AUTH", chunk_size);
8253 SaveLevel_AUTH(file, level);
8255 chunk_size = SaveLevel_INFO(NULL, level);
8256 putFileChunkBE(file, "INFO", chunk_size);
8257 SaveLevel_INFO(file, level);
8259 chunk_size = SaveLevel_BODY(NULL, level);
8260 putFileChunkBE(file, "BODY", chunk_size);
8261 SaveLevel_BODY(file, level);
8263 chunk_size = SaveLevel_ELEM(NULL, level);
8264 if (chunk_size > LEVEL_CHUNK_ELEM_UNCHANGED) // save if changed
8266 putFileChunkBE(file, "ELEM", chunk_size);
8267 SaveLevel_ELEM(file, level);
8270 for (i = 0; i < NUM_ENVELOPES; i++)
8272 int element = EL_ENVELOPE_1 + i;
8274 chunk_size = SaveLevel_NOTE(NULL, level, element);
8275 if (chunk_size > LEVEL_CHUNK_NOTE_UNCHANGED) // save if changed
8277 putFileChunkBE(file, "NOTE", chunk_size);
8278 SaveLevel_NOTE(file, level, element);
8282 // if not using template level, check for non-default custom/group elements
8283 if (!level->use_custom_template || save_as_template)
8285 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
8287 int element = EL_CUSTOM_START + i;
8289 chunk_size = SaveLevel_CUSX(NULL, level, element);
8290 if (chunk_size > LEVEL_CHUNK_CUSX_UNCHANGED) // save if changed
8292 putFileChunkBE(file, "CUSX", chunk_size);
8293 SaveLevel_CUSX(file, level, element);
8297 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
8299 int element = EL_GROUP_START + i;
8301 chunk_size = SaveLevel_GRPX(NULL, level, element);
8302 if (chunk_size > LEVEL_CHUNK_GRPX_UNCHANGED) // save if changed
8304 putFileChunkBE(file, "GRPX", chunk_size);
8305 SaveLevel_GRPX(file, level, element);
8309 for (i = 0; i < NUM_EMPTY_ELEMENTS_ALL; i++)
8311 int element = GET_EMPTY_ELEMENT(i);
8313 chunk_size = SaveLevel_EMPX(NULL, level, element);
8314 if (chunk_size > LEVEL_CHUNK_EMPX_UNCHANGED) // save if changed
8316 putFileChunkBE(file, "EMPX", chunk_size);
8317 SaveLevel_EMPX(file, level, element);
8324 SetFilePermissions(filename, PERMS_PRIVATE);
8327 void SaveLevel(int nr)
8329 char *filename = getDefaultLevelFilename(nr);
8331 SaveLevelFromFilename(&level, filename, FALSE);
8334 void SaveLevelTemplate(void)
8336 char *filename = getLocalLevelTemplateFilename();
8338 SaveLevelFromFilename(&level, filename, TRUE);
8341 boolean SaveLevelChecked(int nr)
8343 char *filename = getDefaultLevelFilename(nr);
8344 boolean new_level = !fileExists(filename);
8345 boolean level_saved = FALSE;
8347 if (new_level || Request("Save this level and kill the old?", REQ_ASK))
8352 Request("Level saved!", REQ_CONFIRM);
8360 void DumpLevel(struct LevelInfo *level)
8362 if (level->no_level_file || level->no_valid_file)
8364 Warn("cannot dump -- no valid level file found");
8370 Print("Level xxx (file version %08d, game version %08d)\n",
8371 level->file_version, level->game_version);
8374 Print("Level author: '%s'\n", level->author);
8375 Print("Level title: '%s'\n", level->name);
8377 Print("Playfield size: %d x %d\n", level->fieldx, level->fieldy);
8379 Print("Level time: %d seconds\n", level->time);
8380 Print("Gems needed: %d\n", level->gems_needed);
8382 Print("Time for magic wall: %d seconds\n", level->time_magic_wall);
8383 Print("Time for wheel: %d seconds\n", level->time_wheel);
8384 Print("Time for light: %d seconds\n", level->time_light);
8385 Print("Time for timegate: %d seconds\n", level->time_timegate);
8387 Print("Amoeba speed: %d\n", level->amoeba_speed);
8390 Print("EM style slippery gems: %s\n", (level->em_slippery_gems ? "yes" : "no"));
8391 Print("Player blocks last field: %s\n", (level->block_last_field ? "yes" : "no"));
8392 Print("SP player blocks last field: %s\n", (level->sp_block_last_field ? "yes" : "no"));
8393 Print("use spring bug: %s\n", (level->use_spring_bug ? "yes" : "no"));
8394 Print("use step counter: %s\n", (level->use_step_counter ? "yes" : "no"));
8395 Print("rate time over score: %s\n", (level->rate_time_over_score ? "yes" : "no"));
8401 for (i = 0; i < NUM_ENVELOPES; i++)
8403 char *text = level->envelope[i].text;
8404 int text_len = strlen(text);
8405 boolean has_text = FALSE;
8407 for (j = 0; j < text_len; j++)
8408 if (text[j] != ' ' && text[j] != '\n')
8414 Print("Envelope %d:\n'%s'\n", i + 1, text);
8422 void DumpLevels(void)
8424 static LevelDirTree *dumplevel_leveldir = NULL;
8426 dumplevel_leveldir = getTreeInfoFromIdentifier(leveldir_first,
8427 global.dumplevel_leveldir);
8429 if (dumplevel_leveldir == NULL)
8430 Fail("no such level identifier: '%s'", global.dumplevel_leveldir);
8432 if (global.dumplevel_level_nr < dumplevel_leveldir->first_level ||
8433 global.dumplevel_level_nr > dumplevel_leveldir->last_level)
8434 Fail("no such level number: %d", global.dumplevel_level_nr);
8436 leveldir_current = dumplevel_leveldir;
8438 LoadLevel(global.dumplevel_level_nr);
8445 // ============================================================================
8446 // tape file functions
8447 // ============================================================================
8449 static void setTapeInfoToDefaults(void)
8453 // always start with reliable default values (empty tape)
8456 // default values (also for pre-1.2 tapes) with only the first player
8457 tape.player_participates[0] = TRUE;
8458 for (i = 1; i < MAX_PLAYERS; i++)
8459 tape.player_participates[i] = FALSE;
8461 // at least one (default: the first) player participates in every tape
8462 tape.num_participating_players = 1;
8464 tape.property_bits = TAPE_PROPERTY_NONE;
8466 tape.level_nr = level_nr;
8468 tape.changed = FALSE;
8469 tape.solved = FALSE;
8471 tape.recording = FALSE;
8472 tape.playing = FALSE;
8473 tape.pausing = FALSE;
8475 tape.scr_fieldx = SCR_FIELDX_DEFAULT;
8476 tape.scr_fieldy = SCR_FIELDY_DEFAULT;
8478 tape.no_info_chunk = TRUE;
8479 tape.no_valid_file = FALSE;
8482 static int getTapePosSize(struct TapeInfo *tape)
8484 int tape_pos_size = 0;
8486 if (tape->use_key_actions)
8487 tape_pos_size += tape->num_participating_players;
8489 if (tape->use_mouse_actions)
8490 tape_pos_size += 3; // x and y position and mouse button mask
8492 tape_pos_size += 1; // tape action delay value
8494 return tape_pos_size;
8497 static void setTapeActionFlags(struct TapeInfo *tape, int value)
8499 tape->use_key_actions = FALSE;
8500 tape->use_mouse_actions = FALSE;
8502 if (value != TAPE_USE_MOUSE_ACTIONS_ONLY)
8503 tape->use_key_actions = TRUE;
8505 if (value != TAPE_USE_KEY_ACTIONS_ONLY)
8506 tape->use_mouse_actions = TRUE;
8509 static int getTapeActionValue(struct TapeInfo *tape)
8511 return (tape->use_key_actions &&
8512 tape->use_mouse_actions ? TAPE_USE_KEY_AND_MOUSE_ACTIONS :
8513 tape->use_key_actions ? TAPE_USE_KEY_ACTIONS_ONLY :
8514 tape->use_mouse_actions ? TAPE_USE_MOUSE_ACTIONS_ONLY :
8515 TAPE_ACTIONS_DEFAULT);
8518 static int LoadTape_VERS(File *file, int chunk_size, struct TapeInfo *tape)
8520 tape->file_version = getFileVersion(file);
8521 tape->game_version = getFileVersion(file);
8526 static int LoadTape_HEAD(File *file, int chunk_size, struct TapeInfo *tape)
8530 tape->random_seed = getFile32BitBE(file);
8531 tape->date = getFile32BitBE(file);
8532 tape->length = getFile32BitBE(file);
8534 // read header fields that are new since version 1.2
8535 if (tape->file_version >= FILE_VERSION_1_2)
8537 byte store_participating_players = getFile8Bit(file);
8540 // since version 1.2, tapes store which players participate in the tape
8541 tape->num_participating_players = 0;
8542 for (i = 0; i < MAX_PLAYERS; i++)
8544 tape->player_participates[i] = FALSE;
8546 if (store_participating_players & (1 << i))
8548 tape->player_participates[i] = TRUE;
8549 tape->num_participating_players++;
8553 setTapeActionFlags(tape, getFile8Bit(file));
8555 tape->property_bits = getFile8Bit(file);
8556 tape->solved = getFile8Bit(file);
8558 engine_version = getFileVersion(file);
8559 if (engine_version > 0)
8560 tape->engine_version = engine_version;
8562 tape->engine_version = tape->game_version;
8568 static int LoadTape_SCRN(File *file, int chunk_size, struct TapeInfo *tape)
8570 tape->scr_fieldx = getFile8Bit(file);
8571 tape->scr_fieldy = getFile8Bit(file);
8576 static int LoadTape_INFO(File *file, int chunk_size, struct TapeInfo *tape)
8578 char *level_identifier = NULL;
8579 int level_identifier_size;
8582 tape->no_info_chunk = FALSE;
8584 level_identifier_size = getFile16BitBE(file);
8586 level_identifier = checked_malloc(level_identifier_size);
8588 for (i = 0; i < level_identifier_size; i++)
8589 level_identifier[i] = getFile8Bit(file);
8591 strncpy(tape->level_identifier, level_identifier, MAX_FILENAME_LEN);
8592 tape->level_identifier[MAX_FILENAME_LEN] = '\0';
8594 checked_free(level_identifier);
8596 tape->level_nr = getFile16BitBE(file);
8598 chunk_size = 2 + level_identifier_size + 2;
8603 static int LoadTape_BODY(File *file, int chunk_size, struct TapeInfo *tape)
8606 int tape_pos_size = getTapePosSize(tape);
8607 int chunk_size_expected = tape_pos_size * tape->length;
8609 if (chunk_size_expected != chunk_size)
8611 ReadUnusedBytesFromFile(file, chunk_size);
8612 return chunk_size_expected;
8615 for (i = 0; i < tape->length; i++)
8617 if (i >= MAX_TAPE_LEN)
8619 Warn("tape truncated -- size exceeds maximum tape size %d",
8622 // tape too large; read and ignore remaining tape data from this chunk
8623 for (;i < tape->length; i++)
8624 ReadUnusedBytesFromFile(file, tape_pos_size);
8629 if (tape->use_key_actions)
8631 for (j = 0; j < MAX_PLAYERS; j++)
8633 tape->pos[i].action[j] = MV_NONE;
8635 if (tape->player_participates[j])
8636 tape->pos[i].action[j] = getFile8Bit(file);
8640 if (tape->use_mouse_actions)
8642 tape->pos[i].action[TAPE_ACTION_LX] = getFile8Bit(file);
8643 tape->pos[i].action[TAPE_ACTION_LY] = getFile8Bit(file);
8644 tape->pos[i].action[TAPE_ACTION_BUTTON] = getFile8Bit(file);
8647 tape->pos[i].delay = getFile8Bit(file);
8649 if (tape->file_version == FILE_VERSION_1_0)
8651 // eliminate possible diagonal moves in old tapes
8652 // this is only for backward compatibility
8654 byte joy_dir[4] = { JOY_LEFT, JOY_RIGHT, JOY_UP, JOY_DOWN };
8655 byte action = tape->pos[i].action[0];
8656 int k, num_moves = 0;
8658 for (k = 0; k < 4; k++)
8660 if (action & joy_dir[k])
8662 tape->pos[i + num_moves].action[0] = joy_dir[k];
8664 tape->pos[i + num_moves].delay = 0;
8673 tape->length += num_moves;
8676 else if (tape->file_version < FILE_VERSION_2_0)
8678 // convert pre-2.0 tapes to new tape format
8680 if (tape->pos[i].delay > 1)
8683 tape->pos[i + 1] = tape->pos[i];
8684 tape->pos[i + 1].delay = 1;
8687 for (j = 0; j < MAX_PLAYERS; j++)
8688 tape->pos[i].action[j] = MV_NONE;
8689 tape->pos[i].delay--;
8696 if (checkEndOfFile(file))
8700 if (i != tape->length)
8701 chunk_size = tape_pos_size * i;
8706 static void LoadTape_SokobanSolution(char *filename)
8709 int move_delay = TILESIZE / level.initial_player_stepsize[0];
8711 if (!(file = openFile(filename, MODE_READ)))
8713 tape.no_valid_file = TRUE;
8718 while (!checkEndOfFile(file))
8720 unsigned char c = getByteFromFile(file);
8722 if (checkEndOfFile(file))
8729 tape.pos[tape.length].action[0] = MV_UP;
8730 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
8736 tape.pos[tape.length].action[0] = MV_DOWN;
8737 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
8743 tape.pos[tape.length].action[0] = MV_LEFT;
8744 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
8750 tape.pos[tape.length].action[0] = MV_RIGHT;
8751 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
8759 // ignore white-space characters
8763 tape.no_valid_file = TRUE;
8765 Warn("unsupported Sokoban solution file '%s' ['%d']", filename, c);
8773 if (tape.no_valid_file)
8776 tape.length_frames = GetTapeLengthFrames();
8777 tape.length_seconds = GetTapeLengthSeconds();
8780 void LoadTapeFromFilename(char *filename)
8782 char cookie[MAX_LINE_LEN];
8783 char chunk_name[CHUNK_ID_LEN + 1];
8787 // always start with reliable default values
8788 setTapeInfoToDefaults();
8790 if (strSuffix(filename, ".sln"))
8792 LoadTape_SokobanSolution(filename);
8797 if (!(file = openFile(filename, MODE_READ)))
8799 tape.no_valid_file = TRUE;
8804 getFileChunkBE(file, chunk_name, NULL);
8805 if (strEqual(chunk_name, "RND1"))
8807 getFile32BitBE(file); // not used
8809 getFileChunkBE(file, chunk_name, NULL);
8810 if (!strEqual(chunk_name, "TAPE"))
8812 tape.no_valid_file = TRUE;
8814 Warn("unknown format of tape file '%s'", filename);
8821 else // check for pre-2.0 file format with cookie string
8823 strcpy(cookie, chunk_name);
8824 if (getStringFromFile(file, &cookie[4], MAX_LINE_LEN - 4) == NULL)
8826 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
8827 cookie[strlen(cookie) - 1] = '\0';
8829 if (!checkCookieString(cookie, TAPE_COOKIE_TMPL))
8831 tape.no_valid_file = TRUE;
8833 Warn("unknown format of tape file '%s'", filename);
8840 if ((tape.file_version = getFileVersionFromCookieString(cookie)) == -1)
8842 tape.no_valid_file = TRUE;
8844 Warn("unsupported version of tape file '%s'", filename);
8851 // pre-2.0 tape files have no game version, so use file version here
8852 tape.game_version = tape.file_version;
8855 if (tape.file_version < FILE_VERSION_1_2)
8857 // tape files from versions before 1.2.0 without chunk structure
8858 LoadTape_HEAD(file, TAPE_CHUNK_HEAD_SIZE, &tape);
8859 LoadTape_BODY(file, 2 * tape.length, &tape);
8867 int (*loader)(File *, int, struct TapeInfo *);
8871 { "VERS", TAPE_CHUNK_VERS_SIZE, LoadTape_VERS },
8872 { "HEAD", TAPE_CHUNK_HEAD_SIZE, LoadTape_HEAD },
8873 { "SCRN", TAPE_CHUNK_SCRN_SIZE, LoadTape_SCRN },
8874 { "INFO", -1, LoadTape_INFO },
8875 { "BODY", -1, LoadTape_BODY },
8879 while (getFileChunkBE(file, chunk_name, &chunk_size))
8883 while (chunk_info[i].name != NULL &&
8884 !strEqual(chunk_name, chunk_info[i].name))
8887 if (chunk_info[i].name == NULL)
8889 Warn("unknown chunk '%s' in tape file '%s'",
8890 chunk_name, filename);
8892 ReadUnusedBytesFromFile(file, chunk_size);
8894 else if (chunk_info[i].size != -1 &&
8895 chunk_info[i].size != chunk_size)
8897 Warn("wrong size (%d) of chunk '%s' in tape file '%s'",
8898 chunk_size, chunk_name, filename);
8900 ReadUnusedBytesFromFile(file, chunk_size);
8904 // call function to load this tape chunk
8905 int chunk_size_expected =
8906 (chunk_info[i].loader)(file, chunk_size, &tape);
8908 // the size of some chunks cannot be checked before reading other
8909 // chunks first (like "HEAD" and "BODY") that contain some header
8910 // information, so check them here
8911 if (chunk_size_expected != chunk_size)
8913 Warn("wrong size (%d) of chunk '%s' in tape file '%s'",
8914 chunk_size, chunk_name, filename);
8922 tape.length_frames = GetTapeLengthFrames();
8923 tape.length_seconds = GetTapeLengthSeconds();
8926 Debug("files:LoadTapeFromFilename", "tape file version: %d",
8928 Debug("files:LoadTapeFromFilename", "tape game version: %d",
8930 Debug("files:LoadTapeFromFilename", "tape engine version: %d",
8931 tape.engine_version);
8935 void LoadTape(int nr)
8937 char *filename = getTapeFilename(nr);
8939 LoadTapeFromFilename(filename);
8942 void LoadSolutionTape(int nr)
8944 char *filename = getSolutionTapeFilename(nr);
8946 LoadTapeFromFilename(filename);
8948 if (TAPE_IS_EMPTY(tape))
8950 if (level.game_engine_type == GAME_ENGINE_TYPE_BD &&
8951 level.native_bd_level->replay != NULL)
8952 CopyNativeTape_BD_to_RND(&level);
8953 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP &&
8954 level.native_sp_level->demo.is_available)
8955 CopyNativeTape_SP_to_RND(&level);
8959 void LoadScoreTape(char *score_tape_basename, int nr)
8961 char *filename = getScoreTapeFilename(score_tape_basename, nr);
8963 LoadTapeFromFilename(filename);
8966 void LoadScoreCacheTape(char *score_tape_basename, int nr)
8968 char *filename = getScoreCacheTapeFilename(score_tape_basename, nr);
8970 LoadTapeFromFilename(filename);
8973 static boolean checkSaveTape_SCRN(struct TapeInfo *tape)
8975 // chunk required for team mode tapes with non-default screen size
8976 return (tape->num_participating_players > 1 &&
8977 (tape->scr_fieldx != SCR_FIELDX_DEFAULT ||
8978 tape->scr_fieldy != SCR_FIELDY_DEFAULT));
8981 static void SaveTape_VERS(FILE *file, struct TapeInfo *tape)
8983 putFileVersion(file, tape->file_version);
8984 putFileVersion(file, tape->game_version);
8987 static void SaveTape_HEAD(FILE *file, struct TapeInfo *tape)
8990 byte store_participating_players = 0;
8992 // set bits for participating players for compact storage
8993 for (i = 0; i < MAX_PLAYERS; i++)
8994 if (tape->player_participates[i])
8995 store_participating_players |= (1 << i);
8997 putFile32BitBE(file, tape->random_seed);
8998 putFile32BitBE(file, tape->date);
8999 putFile32BitBE(file, tape->length);
9001 putFile8Bit(file, store_participating_players);
9003 putFile8Bit(file, getTapeActionValue(tape));
9005 putFile8Bit(file, tape->property_bits);
9006 putFile8Bit(file, tape->solved);
9008 putFileVersion(file, tape->engine_version);
9011 static void SaveTape_SCRN(FILE *file, struct TapeInfo *tape)
9013 putFile8Bit(file, tape->scr_fieldx);
9014 putFile8Bit(file, tape->scr_fieldy);
9017 static void SaveTape_INFO(FILE *file, struct TapeInfo *tape)
9019 int level_identifier_size = strlen(tape->level_identifier) + 1;
9022 putFile16BitBE(file, level_identifier_size);
9024 for (i = 0; i < level_identifier_size; i++)
9025 putFile8Bit(file, tape->level_identifier[i]);
9027 putFile16BitBE(file, tape->level_nr);
9030 static void SaveTape_BODY(FILE *file, struct TapeInfo *tape)
9034 for (i = 0; i < tape->length; i++)
9036 if (tape->use_key_actions)
9038 for (j = 0; j < MAX_PLAYERS; j++)
9039 if (tape->player_participates[j])
9040 putFile8Bit(file, tape->pos[i].action[j]);
9043 if (tape->use_mouse_actions)
9045 putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_LX]);
9046 putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_LY]);
9047 putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_BUTTON]);
9050 putFile8Bit(file, tape->pos[i].delay);
9054 void SaveTapeToFilename(char *filename)
9058 int info_chunk_size;
9059 int body_chunk_size;
9061 if (!(file = fopen(filename, MODE_WRITE)))
9063 Warn("cannot save level recording file '%s'", filename);
9068 tape_pos_size = getTapePosSize(&tape);
9070 info_chunk_size = 2 + (strlen(tape.level_identifier) + 1) + 2;
9071 body_chunk_size = tape_pos_size * tape.length;
9073 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
9074 putFileChunkBE(file, "TAPE", CHUNK_SIZE_NONE);
9076 putFileChunkBE(file, "VERS", TAPE_CHUNK_VERS_SIZE);
9077 SaveTape_VERS(file, &tape);
9079 putFileChunkBE(file, "HEAD", TAPE_CHUNK_HEAD_SIZE);
9080 SaveTape_HEAD(file, &tape);
9082 if (checkSaveTape_SCRN(&tape))
9084 putFileChunkBE(file, "SCRN", TAPE_CHUNK_SCRN_SIZE);
9085 SaveTape_SCRN(file, &tape);
9088 putFileChunkBE(file, "INFO", info_chunk_size);
9089 SaveTape_INFO(file, &tape);
9091 putFileChunkBE(file, "BODY", body_chunk_size);
9092 SaveTape_BODY(file, &tape);
9096 SetFilePermissions(filename, PERMS_PRIVATE);
9099 static void SaveTapeExt(char *filename)
9103 tape.file_version = FILE_VERSION_ACTUAL;
9104 tape.game_version = GAME_VERSION_ACTUAL;
9106 tape.num_participating_players = 0;
9108 // count number of participating players
9109 for (i = 0; i < MAX_PLAYERS; i++)
9110 if (tape.player_participates[i])
9111 tape.num_participating_players++;
9113 SaveTapeToFilename(filename);
9115 tape.changed = FALSE;
9118 void SaveTape(int nr)
9120 char *filename = getTapeFilename(nr);
9122 InitTapeDirectory(leveldir_current->subdir);
9124 SaveTapeExt(filename);
9127 void SaveScoreTape(int nr)
9129 char *filename = getScoreTapeFilename(tape.score_tape_basename, nr);
9131 // used instead of "leveldir_current->subdir" (for network games)
9132 InitScoreTapeDirectory(levelset.identifier, nr);
9134 SaveTapeExt(filename);
9137 static boolean SaveTapeCheckedExt(int nr, char *msg_replace, char *msg_saved,
9138 unsigned int req_state_added)
9140 char *filename = getTapeFilename(nr);
9141 boolean new_tape = !fileExists(filename);
9142 boolean tape_saved = FALSE;
9144 if (new_tape || Request(msg_replace, REQ_ASK | req_state_added))
9149 Request(msg_saved, REQ_CONFIRM | req_state_added);
9157 boolean SaveTapeChecked(int nr)
9159 return SaveTapeCheckedExt(nr, "Replace old tape?", "Tape saved!", 0);
9162 boolean SaveTapeChecked_LevelSolved(int nr)
9164 return SaveTapeCheckedExt(nr, "Level solved! Replace old tape?",
9165 "Level solved! Tape saved!", REQ_STAY_OPEN);
9168 void DumpTape(struct TapeInfo *tape)
9170 int tape_frame_counter;
9173 if (tape->no_valid_file)
9175 Warn("cannot dump -- no valid tape file found");
9182 Print("Tape of Level %03d (file version %08d, game version %08d)\n",
9183 tape->level_nr, tape->file_version, tape->game_version);
9184 Print(" (effective engine version %08d)\n",
9185 tape->engine_version);
9186 Print("Level series identifier: '%s'\n", tape->level_identifier);
9188 Print("Solution tape: %s\n",
9189 tape->solved ? "yes" :
9190 tape->game_version < VERSION_IDENT(4,3,2,3) ? "unknown" : "no");
9192 Print("Special tape properties: ");
9193 if (tape->property_bits == TAPE_PROPERTY_NONE)
9195 if (tape->property_bits & TAPE_PROPERTY_EM_RANDOM_BUG)
9196 Print("[em_random_bug]");
9197 if (tape->property_bits & TAPE_PROPERTY_GAME_SPEED)
9198 Print("[game_speed]");
9199 if (tape->property_bits & TAPE_PROPERTY_PAUSE_MODE)
9201 if (tape->property_bits & TAPE_PROPERTY_SINGLE_STEP)
9202 Print("[single_step]");
9203 if (tape->property_bits & TAPE_PROPERTY_SNAPSHOT)
9204 Print("[snapshot]");
9205 if (tape->property_bits & TAPE_PROPERTY_REPLAYED)
9206 Print("[replayed]");
9207 if (tape->property_bits & TAPE_PROPERTY_TAS_KEYS)
9208 Print("[tas_keys]");
9209 if (tape->property_bits & TAPE_PROPERTY_SMALL_GRAPHICS)
9210 Print("[small_graphics]");
9213 int year2 = tape->date / 10000;
9214 int year4 = (year2 < 70 ? 2000 + year2 : 1900 + year2);
9215 int month_index_raw = (tape->date / 100) % 100;
9216 int month_index = month_index_raw % 12; // prevent invalid index
9217 int month = month_index + 1;
9218 int day = tape->date % 100;
9220 Print("Tape date: %04d-%02d-%02d\n", year4, month, day);
9224 tape_frame_counter = 0;
9226 for (i = 0; i < tape->length; i++)
9228 if (i >= MAX_TAPE_LEN)
9233 for (j = 0; j < MAX_PLAYERS; j++)
9235 if (tape->player_participates[j])
9237 int action = tape->pos[i].action[j];
9239 Print("%d:%02x ", j, action);
9240 Print("[%c%c%c%c|%c%c] - ",
9241 (action & JOY_LEFT ? '<' : ' '),
9242 (action & JOY_RIGHT ? '>' : ' '),
9243 (action & JOY_UP ? '^' : ' '),
9244 (action & JOY_DOWN ? 'v' : ' '),
9245 (action & JOY_BUTTON_1 ? '1' : ' '),
9246 (action & JOY_BUTTON_2 ? '2' : ' '));
9250 Print("(%03d) ", tape->pos[i].delay);
9251 Print("[%05d]\n", tape_frame_counter);
9253 tape_frame_counter += tape->pos[i].delay;
9259 void DumpTapes(void)
9261 static LevelDirTree *dumptape_leveldir = NULL;
9263 dumptape_leveldir = getTreeInfoFromIdentifier(leveldir_first,
9264 global.dumptape_leveldir);
9266 if (dumptape_leveldir == NULL)
9267 Fail("no such level identifier: '%s'", global.dumptape_leveldir);
9269 if (global.dumptape_level_nr < dumptape_leveldir->first_level ||
9270 global.dumptape_level_nr > dumptape_leveldir->last_level)
9271 Fail("no such level number: %d", global.dumptape_level_nr);
9273 leveldir_current = dumptape_leveldir;
9275 if (options.mytapes)
9276 LoadTape(global.dumptape_level_nr);
9278 LoadSolutionTape(global.dumptape_level_nr);
9286 // ============================================================================
9287 // score file functions
9288 // ============================================================================
9290 static void setScoreInfoToDefaultsExt(struct ScoreInfo *scores)
9294 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9296 strcpy(scores->entry[i].tape_basename, UNDEFINED_FILENAME);
9297 strcpy(scores->entry[i].name, EMPTY_PLAYER_NAME);
9298 scores->entry[i].score = 0;
9299 scores->entry[i].time = 0;
9301 scores->entry[i].id = -1;
9302 strcpy(scores->entry[i].tape_date, UNKNOWN_NAME);
9303 strcpy(scores->entry[i].platform, UNKNOWN_NAME);
9304 strcpy(scores->entry[i].version, UNKNOWN_NAME);
9305 strcpy(scores->entry[i].country_name, UNKNOWN_NAME);
9306 strcpy(scores->entry[i].country_code, "??");
9309 scores->num_entries = 0;
9310 scores->last_added = -1;
9311 scores->last_added_local = -1;
9313 scores->updated = FALSE;
9314 scores->uploaded = FALSE;
9315 scores->tape_downloaded = FALSE;
9316 scores->force_last_added = FALSE;
9318 // The following values are intentionally not reset here:
9322 // - continue_playing
9323 // - continue_on_return
9326 static void setScoreInfoToDefaults(void)
9328 setScoreInfoToDefaultsExt(&scores);
9331 static void setServerScoreInfoToDefaults(void)
9333 setScoreInfoToDefaultsExt(&server_scores);
9336 static void LoadScore_OLD(int nr)
9339 char *filename = getScoreFilename(nr);
9340 char cookie[MAX_LINE_LEN];
9341 char line[MAX_LINE_LEN];
9345 if (!(file = fopen(filename, MODE_READ)))
9348 // check file identifier
9349 if (fgets(cookie, MAX_LINE_LEN, file) == NULL)
9351 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
9352 cookie[strlen(cookie) - 1] = '\0';
9354 if (!checkCookieString(cookie, SCORE_COOKIE_TMPL))
9356 Warn("unknown format of score file '%s'", filename);
9363 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9365 if (fscanf(file, "%d", &scores.entry[i].score) == EOF)
9366 Warn("fscanf() failed; %s", strerror(errno));
9368 if (fgets(line, MAX_LINE_LEN, file) == NULL)
9371 if (strlen(line) > 0 && line[strlen(line) - 1] == '\n')
9372 line[strlen(line) - 1] = '\0';
9374 for (line_ptr = line; *line_ptr; line_ptr++)
9376 if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != '\0')
9378 strncpy(scores.entry[i].name, line_ptr, MAX_PLAYER_NAME_LEN);
9379 scores.entry[i].name[MAX_PLAYER_NAME_LEN] = '\0';
9388 static void ConvertScore_OLD(void)
9390 // only convert score to time for levels that rate playing time over score
9391 if (!level.rate_time_over_score)
9394 // convert old score to playing time for score-less levels (like Supaplex)
9395 int time_final_max = 999;
9398 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9400 int score = scores.entry[i].score;
9402 if (score > 0 && score < time_final_max)
9403 scores.entry[i].time = (time_final_max - score - 1) * FRAMES_PER_SECOND;
9407 static int LoadScore_VERS(File *file, int chunk_size, struct ScoreInfo *scores)
9409 scores->file_version = getFileVersion(file);
9410 scores->game_version = getFileVersion(file);
9415 static int LoadScore_INFO(File *file, int chunk_size, struct ScoreInfo *scores)
9417 char *level_identifier = NULL;
9418 int level_identifier_size;
9421 level_identifier_size = getFile16BitBE(file);
9423 level_identifier = checked_malloc(level_identifier_size);
9425 for (i = 0; i < level_identifier_size; i++)
9426 level_identifier[i] = getFile8Bit(file);
9428 strncpy(scores->level_identifier, level_identifier, MAX_FILENAME_LEN);
9429 scores->level_identifier[MAX_FILENAME_LEN] = '\0';
9431 checked_free(level_identifier);
9433 scores->level_nr = getFile16BitBE(file);
9434 scores->num_entries = getFile16BitBE(file);
9436 chunk_size = 2 + level_identifier_size + 2 + 2;
9441 static int LoadScore_NAME(File *file, int chunk_size, struct ScoreInfo *scores)
9445 for (i = 0; i < scores->num_entries; i++)
9447 for (j = 0; j < MAX_PLAYER_NAME_LEN; j++)
9448 scores->entry[i].name[j] = getFile8Bit(file);
9450 scores->entry[i].name[MAX_PLAYER_NAME_LEN] = '\0';
9453 chunk_size = scores->num_entries * MAX_PLAYER_NAME_LEN;
9458 static int LoadScore_SCOR(File *file, int chunk_size, struct ScoreInfo *scores)
9462 for (i = 0; i < scores->num_entries; i++)
9463 scores->entry[i].score = getFile16BitBE(file);
9465 chunk_size = scores->num_entries * 2;
9470 static int LoadScore_SC4R(File *file, int chunk_size, struct ScoreInfo *scores)
9474 for (i = 0; i < scores->num_entries; i++)
9475 scores->entry[i].score = getFile32BitBE(file);
9477 chunk_size = scores->num_entries * 4;
9482 static int LoadScore_TIME(File *file, int chunk_size, struct ScoreInfo *scores)
9486 for (i = 0; i < scores->num_entries; i++)
9487 scores->entry[i].time = getFile32BitBE(file);
9489 chunk_size = scores->num_entries * 4;
9494 static int LoadScore_TAPE(File *file, int chunk_size, struct ScoreInfo *scores)
9498 for (i = 0; i < scores->num_entries; i++)
9500 for (j = 0; j < MAX_SCORE_TAPE_BASENAME_LEN; j++)
9501 scores->entry[i].tape_basename[j] = getFile8Bit(file);
9503 scores->entry[i].tape_basename[MAX_SCORE_TAPE_BASENAME_LEN] = '\0';
9506 chunk_size = scores->num_entries * MAX_SCORE_TAPE_BASENAME_LEN;
9511 void LoadScore(int nr)
9513 char *filename = getScoreFilename(nr);
9514 char cookie[MAX_LINE_LEN];
9515 char chunk_name[CHUNK_ID_LEN + 1];
9517 boolean old_score_file_format = FALSE;
9520 // always start with reliable default values
9521 setScoreInfoToDefaults();
9523 if (!(file = openFile(filename, MODE_READ)))
9526 getFileChunkBE(file, chunk_name, NULL);
9527 if (strEqual(chunk_name, "RND1"))
9529 getFile32BitBE(file); // not used
9531 getFileChunkBE(file, chunk_name, NULL);
9532 if (!strEqual(chunk_name, "SCOR"))
9534 Warn("unknown format of score file '%s'", filename);
9541 else // check for old file format with cookie string
9543 strcpy(cookie, chunk_name);
9544 if (getStringFromFile(file, &cookie[4], MAX_LINE_LEN - 4) == NULL)
9546 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
9547 cookie[strlen(cookie) - 1] = '\0';
9549 if (!checkCookieString(cookie, SCORE_COOKIE_TMPL))
9551 Warn("unknown format of score file '%s'", filename);
9558 old_score_file_format = TRUE;
9561 if (old_score_file_format)
9563 // score files from versions before 4.2.4.0 without chunk structure
9566 // convert score to time, if possible (mainly for Supaplex levels)
9575 int (*loader)(File *, int, struct ScoreInfo *);
9579 { "VERS", SCORE_CHUNK_VERS_SIZE, LoadScore_VERS },
9580 { "INFO", -1, LoadScore_INFO },
9581 { "NAME", -1, LoadScore_NAME },
9582 { "SCOR", -1, LoadScore_SCOR },
9583 { "SC4R", -1, LoadScore_SC4R },
9584 { "TIME", -1, LoadScore_TIME },
9585 { "TAPE", -1, LoadScore_TAPE },
9590 while (getFileChunkBE(file, chunk_name, &chunk_size))
9594 while (chunk_info[i].name != NULL &&
9595 !strEqual(chunk_name, chunk_info[i].name))
9598 if (chunk_info[i].name == NULL)
9600 Warn("unknown chunk '%s' in score file '%s'",
9601 chunk_name, filename);
9603 ReadUnusedBytesFromFile(file, chunk_size);
9605 else if (chunk_info[i].size != -1 &&
9606 chunk_info[i].size != chunk_size)
9608 Warn("wrong size (%d) of chunk '%s' in score file '%s'",
9609 chunk_size, chunk_name, filename);
9611 ReadUnusedBytesFromFile(file, chunk_size);
9615 // call function to load this score chunk
9616 int chunk_size_expected =
9617 (chunk_info[i].loader)(file, chunk_size, &scores);
9619 // the size of some chunks cannot be checked before reading other
9620 // chunks first (like "HEAD" and "BODY") that contain some header
9621 // information, so check them here
9622 if (chunk_size_expected != chunk_size)
9624 Warn("wrong size (%d) of chunk '%s' in score file '%s'",
9625 chunk_size, chunk_name, filename);
9634 #if ENABLE_HISTORIC_CHUNKS
9635 void SaveScore_OLD(int nr)
9638 char *filename = getScoreFilename(nr);
9641 // used instead of "leveldir_current->subdir" (for network games)
9642 InitScoreDirectory(levelset.identifier);
9644 if (!(file = fopen(filename, MODE_WRITE)))
9646 Warn("cannot save score for level %d", nr);
9651 fprintf(file, "%s\n\n", SCORE_COOKIE);
9653 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9654 fprintf(file, "%d %s\n", scores.entry[i].score, scores.entry[i].name);
9658 SetFilePermissions(filename, PERMS_PRIVATE);
9662 static void SaveScore_VERS(FILE *file, struct ScoreInfo *scores)
9664 putFileVersion(file, scores->file_version);
9665 putFileVersion(file, scores->game_version);
9668 static void SaveScore_INFO(FILE *file, struct ScoreInfo *scores)
9670 int level_identifier_size = strlen(scores->level_identifier) + 1;
9673 putFile16BitBE(file, level_identifier_size);
9675 for (i = 0; i < level_identifier_size; i++)
9676 putFile8Bit(file, scores->level_identifier[i]);
9678 putFile16BitBE(file, scores->level_nr);
9679 putFile16BitBE(file, scores->num_entries);
9682 static void SaveScore_NAME(FILE *file, struct ScoreInfo *scores)
9686 for (i = 0; i < scores->num_entries; i++)
9688 int name_size = strlen(scores->entry[i].name);
9690 for (j = 0; j < MAX_PLAYER_NAME_LEN; j++)
9691 putFile8Bit(file, (j < name_size ? scores->entry[i].name[j] : 0));
9695 static void SaveScore_SCOR(FILE *file, struct ScoreInfo *scores)
9699 for (i = 0; i < scores->num_entries; i++)
9700 putFile16BitBE(file, scores->entry[i].score);
9703 static void SaveScore_SC4R(FILE *file, struct ScoreInfo *scores)
9707 for (i = 0; i < scores->num_entries; i++)
9708 putFile32BitBE(file, scores->entry[i].score);
9711 static void SaveScore_TIME(FILE *file, struct ScoreInfo *scores)
9715 for (i = 0; i < scores->num_entries; i++)
9716 putFile32BitBE(file, scores->entry[i].time);
9719 static void SaveScore_TAPE(FILE *file, struct ScoreInfo *scores)
9723 for (i = 0; i < scores->num_entries; i++)
9725 int size = strlen(scores->entry[i].tape_basename);
9727 for (j = 0; j < MAX_SCORE_TAPE_BASENAME_LEN; j++)
9728 putFile8Bit(file, (j < size ? scores->entry[i].tape_basename[j] : 0));
9732 static void SaveScoreToFilename(char *filename)
9735 int info_chunk_size;
9736 int name_chunk_size;
9737 int scor_chunk_size;
9738 int sc4r_chunk_size;
9739 int time_chunk_size;
9740 int tape_chunk_size;
9741 boolean has_large_score_values;
9744 if (!(file = fopen(filename, MODE_WRITE)))
9746 Warn("cannot save score file '%s'", filename);
9751 info_chunk_size = 2 + (strlen(scores.level_identifier) + 1) + 2 + 2;
9752 name_chunk_size = scores.num_entries * MAX_PLAYER_NAME_LEN;
9753 scor_chunk_size = scores.num_entries * 2;
9754 sc4r_chunk_size = scores.num_entries * 4;
9755 time_chunk_size = scores.num_entries * 4;
9756 tape_chunk_size = scores.num_entries * MAX_SCORE_TAPE_BASENAME_LEN;
9758 has_large_score_values = FALSE;
9759 for (i = 0; i < scores.num_entries; i++)
9760 if (scores.entry[i].score > 0xffff)
9761 has_large_score_values = TRUE;
9763 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
9764 putFileChunkBE(file, "SCOR", CHUNK_SIZE_NONE);
9766 putFileChunkBE(file, "VERS", SCORE_CHUNK_VERS_SIZE);
9767 SaveScore_VERS(file, &scores);
9769 putFileChunkBE(file, "INFO", info_chunk_size);
9770 SaveScore_INFO(file, &scores);
9772 putFileChunkBE(file, "NAME", name_chunk_size);
9773 SaveScore_NAME(file, &scores);
9775 if (has_large_score_values)
9777 putFileChunkBE(file, "SC4R", sc4r_chunk_size);
9778 SaveScore_SC4R(file, &scores);
9782 putFileChunkBE(file, "SCOR", scor_chunk_size);
9783 SaveScore_SCOR(file, &scores);
9786 putFileChunkBE(file, "TIME", time_chunk_size);
9787 SaveScore_TIME(file, &scores);
9789 putFileChunkBE(file, "TAPE", tape_chunk_size);
9790 SaveScore_TAPE(file, &scores);
9794 SetFilePermissions(filename, PERMS_PRIVATE);
9797 void SaveScore(int nr)
9799 char *filename = getScoreFilename(nr);
9802 // used instead of "leveldir_current->subdir" (for network games)
9803 InitScoreDirectory(levelset.identifier);
9805 scores.file_version = FILE_VERSION_ACTUAL;
9806 scores.game_version = GAME_VERSION_ACTUAL;
9808 strncpy(scores.level_identifier, levelset.identifier, MAX_FILENAME_LEN);
9809 scores.level_identifier[MAX_FILENAME_LEN] = '\0';
9810 scores.level_nr = level_nr;
9812 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9813 if (scores.entry[i].score == 0 &&
9814 scores.entry[i].time == 0 &&
9815 strEqual(scores.entry[i].name, EMPTY_PLAYER_NAME))
9818 scores.num_entries = i;
9820 if (scores.num_entries == 0)
9823 SaveScoreToFilename(filename);
9826 static void LoadServerScoreFromCache(int nr)
9828 struct ScoreEntry score_entry;
9837 { &score_entry.score, FALSE, 0 },
9838 { &score_entry.time, FALSE, 0 },
9839 { score_entry.name, TRUE, MAX_PLAYER_NAME_LEN },
9840 { score_entry.tape_basename, TRUE, MAX_FILENAME_LEN },
9841 { score_entry.tape_date, TRUE, MAX_ISO_DATE_LEN },
9842 { &score_entry.id, FALSE, 0 },
9843 { score_entry.platform, TRUE, MAX_PLATFORM_TEXT_LEN },
9844 { score_entry.version, TRUE, MAX_VERSION_TEXT_LEN },
9845 { score_entry.country_code, TRUE, MAX_COUNTRY_CODE_LEN },
9846 { score_entry.country_name, TRUE, MAX_COUNTRY_NAME_LEN },
9850 char *filename = getScoreCacheFilename(nr);
9851 SetupFileHash *score_hash = loadSetupFileHash(filename);
9854 server_scores.num_entries = 0;
9856 if (score_hash == NULL)
9859 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9861 score_entry = server_scores.entry[i];
9863 for (j = 0; score_mapping[j].value != NULL; j++)
9867 sprintf(token, "%02d.%d", i, j);
9869 char *value = getHashEntry(score_hash, token);
9874 if (score_mapping[j].is_string)
9876 char *score_value = (char *)score_mapping[j].value;
9877 int value_size = score_mapping[j].string_size;
9879 strncpy(score_value, value, value_size);
9880 score_value[value_size] = '\0';
9884 int *score_value = (int *)score_mapping[j].value;
9886 *score_value = atoi(value);
9889 server_scores.num_entries = i + 1;
9892 server_scores.entry[i] = score_entry;
9895 freeSetupFileHash(score_hash);
9898 void LoadServerScore(int nr, boolean download_score)
9900 if (!setup.use_api_server)
9903 // always start with reliable default values
9904 setServerScoreInfoToDefaults();
9906 // 1st step: load server scores from cache file (which may not exist)
9907 // (this should prevent reading it while the thread is writing to it)
9908 LoadServerScoreFromCache(nr);
9910 if (download_score && runtime.use_api_server)
9912 // 2nd step: download server scores from score server to cache file
9913 // (as thread, as it might time out if the server is not reachable)
9914 ApiGetScoreAsThread(nr);
9918 void PrepareScoreTapesForUpload(char *leveldir_subdir)
9920 MarkTapeDirectoryUploadsAsIncomplete(leveldir_subdir);
9922 // if score tape not uploaded, ask for uploading missing tapes later
9923 if (!setup.has_remaining_tapes)
9924 setup.ask_for_remaining_tapes = TRUE;
9926 setup.provide_uploading_tapes = TRUE;
9927 setup.has_remaining_tapes = TRUE;
9929 SaveSetup_ServerSetup();
9932 void SaveServerScore(int nr, boolean tape_saved)
9934 if (!runtime.use_api_server)
9936 PrepareScoreTapesForUpload(leveldir_current->subdir);
9941 ApiAddScoreAsThread(nr, tape_saved, NULL);
9944 void SaveServerScoreFromFile(int nr, boolean tape_saved,
9945 char *score_tape_filename)
9947 if (!runtime.use_api_server)
9950 ApiAddScoreAsThread(nr, tape_saved, score_tape_filename);
9953 void LoadLocalAndServerScore(int nr, boolean download_score)
9955 int last_added_local = scores.last_added_local;
9956 boolean force_last_added = scores.force_last_added;
9958 // needed if only showing server scores
9959 setScoreInfoToDefaults();
9961 if (!strEqual(setup.scores_in_highscore_list, STR_SCORES_TYPE_SERVER_ONLY))
9964 // restore last added local score entry (before merging server scores)
9965 scores.last_added = scores.last_added_local = last_added_local;
9967 if (setup.use_api_server &&
9968 !strEqual(setup.scores_in_highscore_list, STR_SCORES_TYPE_LOCAL_ONLY))
9970 // load server scores from cache file and trigger update from server
9971 LoadServerScore(nr, download_score);
9973 // merge local scores with scores from server
9977 if (force_last_added)
9978 scores.force_last_added = force_last_added;
9982 // ============================================================================
9983 // setup file functions
9984 // ============================================================================
9986 #define TOKEN_STR_PLAYER_PREFIX "player_"
9989 static struct TokenInfo global_setup_tokens[] =
9993 &setup.player_name, "player_name"
9997 &setup.multiple_users, "multiple_users"
10001 &setup.sound, "sound"
10005 &setup.sound_loops, "repeating_sound_loops"
10009 &setup.sound_music, "background_music"
10013 &setup.sound_simple, "simple_sound_effects"
10017 &setup.toons, "toons"
10021 &setup.global_animations, "global_animations"
10025 &setup.scroll_delay, "scroll_delay"
10029 &setup.forced_scroll_delay, "forced_scroll_delay"
10033 &setup.scroll_delay_value, "scroll_delay_value"
10037 &setup.engine_snapshot_mode, "engine_snapshot_mode"
10041 &setup.engine_snapshot_memory, "engine_snapshot_memory"
10045 &setup.fade_screens, "fade_screens"
10049 &setup.autorecord, "automatic_tape_recording"
10053 &setup.autorecord_after_replay, "autorecord_after_replay"
10057 &setup.auto_pause_on_start, "auto_pause_on_start"
10061 &setup.show_titlescreen, "show_titlescreen"
10065 &setup.quick_doors, "quick_doors"
10069 &setup.team_mode, "team_mode"
10073 &setup.handicap, "handicap"
10077 &setup.skip_levels, "skip_levels"
10081 &setup.increment_levels, "increment_levels"
10085 &setup.auto_play_next_level, "auto_play_next_level"
10089 &setup.count_score_after_game, "count_score_after_game"
10093 &setup.show_scores_after_game, "show_scores_after_game"
10097 &setup.time_limit, "time_limit"
10101 &setup.fullscreen, "fullscreen"
10105 &setup.window_scaling_percent, "window_scaling_percent"
10109 &setup.window_scaling_quality, "window_scaling_quality"
10113 &setup.screen_rendering_mode, "screen_rendering_mode"
10117 &setup.vsync_mode, "vsync_mode"
10121 &setup.ask_on_escape, "ask_on_escape"
10125 &setup.ask_on_escape_editor, "ask_on_escape_editor"
10129 &setup.ask_on_game_over, "ask_on_game_over"
10133 &setup.ask_on_quit_game, "ask_on_quit_game"
10137 &setup.ask_on_quit_program, "ask_on_quit_program"
10141 &setup.quick_switch, "quick_player_switch"
10145 &setup.input_on_focus, "input_on_focus"
10149 &setup.prefer_aga_graphics, "prefer_aga_graphics"
10153 &setup.prefer_lowpass_sounds, "prefer_lowpass_sounds"
10157 &setup.prefer_extra_panel_items, "prefer_extra_panel_items"
10161 &setup.game_speed_extended, "game_speed_extended"
10165 &setup.game_frame_delay, "game_frame_delay"
10169 &setup.bd_skip_uncovering, "bd_skip_uncovering"
10173 &setup.bd_skip_hatching, "bd_skip_hatching"
10177 &setup.bd_scroll_delay, "bd_scroll_delay"
10181 &setup.bd_smooth_movements, "bd_smooth_movements"
10185 &setup.sp_show_border_elements, "sp_show_border_elements"
10189 &setup.small_game_graphics, "small_game_graphics"
10193 &setup.show_load_save_buttons, "show_load_save_buttons"
10197 &setup.show_undo_redo_buttons, "show_undo_redo_buttons"
10201 &setup.scores_in_highscore_list, "scores_in_highscore_list"
10205 &setup.graphics_set, "graphics_set"
10209 &setup.sounds_set, "sounds_set"
10213 &setup.music_set, "music_set"
10217 &setup.override_level_graphics, "override_level_graphics"
10221 &setup.override_level_sounds, "override_level_sounds"
10225 &setup.override_level_music, "override_level_music"
10229 &setup.volume_simple, "volume_simple"
10233 &setup.volume_loops, "volume_loops"
10237 &setup.volume_music, "volume_music"
10241 &setup.network_mode, "network_mode"
10245 &setup.network_player_nr, "network_player"
10249 &setup.network_server_hostname, "network_server_hostname"
10253 &setup.touch.control_type, "touch.control_type"
10257 &setup.touch.move_distance, "touch.move_distance"
10261 &setup.touch.drop_distance, "touch.drop_distance"
10265 &setup.touch.transparency, "touch.transparency"
10269 &setup.touch.draw_outlined, "touch.draw_outlined"
10273 &setup.touch.draw_pressed, "touch.draw_pressed"
10277 &setup.touch.grid_xsize[0], "touch.virtual_buttons.0.xsize"
10281 &setup.touch.grid_ysize[0], "touch.virtual_buttons.0.ysize"
10285 &setup.touch.grid_xsize[1], "touch.virtual_buttons.1.xsize"
10289 &setup.touch.grid_ysize[1], "touch.virtual_buttons.1.ysize"
10293 &setup.touch.overlay_buttons, "touch.overlay_buttons"
10297 static struct TokenInfo auto_setup_tokens[] =
10301 &setup.auto_setup.editor_zoom_tilesize, "editor.zoom_tilesize"
10305 static struct TokenInfo server_setup_tokens[] =
10309 &setup.player_uuid, "player_uuid"
10313 &setup.player_version, "player_version"
10317 &setup.use_api_server, TEST_PREFIX "use_api_server"
10321 &setup.api_server_hostname, TEST_PREFIX "api_server_hostname"
10325 &setup.api_server_password, TEST_PREFIX "api_server_password"
10329 &setup.ask_for_uploading_tapes, TEST_PREFIX "ask_for_uploading_tapes"
10333 &setup.ask_for_remaining_tapes, TEST_PREFIX "ask_for_remaining_tapes"
10337 &setup.provide_uploading_tapes, TEST_PREFIX "provide_uploading_tapes"
10341 &setup.ask_for_using_api_server,TEST_PREFIX "ask_for_using_api_server"
10345 &setup.has_remaining_tapes, TEST_PREFIX "has_remaining_tapes"
10349 static struct TokenInfo editor_setup_tokens[] =
10353 &setup.editor.el_classic, "editor.el_classic"
10357 &setup.editor.el_custom, "editor.el_custom"
10361 &setup.editor.el_user_defined, "editor.el_user_defined"
10365 &setup.editor.el_dynamic, "editor.el_dynamic"
10369 &setup.editor.el_headlines, "editor.el_headlines"
10373 &setup.editor.show_element_token, "editor.show_element_token"
10377 &setup.editor.show_read_only_warning, "editor.show_read_only_warning"
10381 static struct TokenInfo editor_cascade_setup_tokens[] =
10385 &setup.editor_cascade.el_bd, "editor.cascade.el_bd"
10389 &setup.editor_cascade.el_bd_native, "editor.cascade.el_bd_native"
10393 &setup.editor_cascade.el_em, "editor.cascade.el_em"
10397 &setup.editor_cascade.el_emc, "editor.cascade.el_emc"
10401 &setup.editor_cascade.el_rnd, "editor.cascade.el_rnd"
10405 &setup.editor_cascade.el_sb, "editor.cascade.el_sb"
10409 &setup.editor_cascade.el_sp, "editor.cascade.el_sp"
10413 &setup.editor_cascade.el_dc, "editor.cascade.el_dc"
10417 &setup.editor_cascade.el_dx, "editor.cascade.el_dx"
10421 &setup.editor_cascade.el_mm, "editor.cascade.el_mm"
10425 &setup.editor_cascade.el_df, "editor.cascade.el_df"
10429 &setup.editor_cascade.el_chars, "editor.cascade.el_chars"
10433 &setup.editor_cascade.el_steel_chars, "editor.cascade.el_steel_chars"
10437 &setup.editor_cascade.el_ce, "editor.cascade.el_ce"
10441 &setup.editor_cascade.el_ge, "editor.cascade.el_ge"
10445 &setup.editor_cascade.el_es, "editor.cascade.el_es"
10449 &setup.editor_cascade.el_ref, "editor.cascade.el_ref"
10453 &setup.editor_cascade.el_user, "editor.cascade.el_user"
10457 &setup.editor_cascade.el_dynamic, "editor.cascade.el_dynamic"
10461 static struct TokenInfo shortcut_setup_tokens[] =
10465 &setup.shortcut.save_game, "shortcut.save_game"
10469 &setup.shortcut.load_game, "shortcut.load_game"
10473 &setup.shortcut.restart_game, "shortcut.restart_game"
10477 &setup.shortcut.pause_before_end, "shortcut.pause_before_end"
10481 &setup.shortcut.toggle_pause, "shortcut.toggle_pause"
10485 &setup.shortcut.focus_player[0], "shortcut.focus_player_1"
10489 &setup.shortcut.focus_player[1], "shortcut.focus_player_2"
10493 &setup.shortcut.focus_player[2], "shortcut.focus_player_3"
10497 &setup.shortcut.focus_player[3], "shortcut.focus_player_4"
10501 &setup.shortcut.focus_player_all, "shortcut.focus_player_all"
10505 &setup.shortcut.tape_eject, "shortcut.tape_eject"
10509 &setup.shortcut.tape_extra, "shortcut.tape_extra"
10513 &setup.shortcut.tape_stop, "shortcut.tape_stop"
10517 &setup.shortcut.tape_pause, "shortcut.tape_pause"
10521 &setup.shortcut.tape_record, "shortcut.tape_record"
10525 &setup.shortcut.tape_play, "shortcut.tape_play"
10529 &setup.shortcut.sound_simple, "shortcut.sound_simple"
10533 &setup.shortcut.sound_loops, "shortcut.sound_loops"
10537 &setup.shortcut.sound_music, "shortcut.sound_music"
10541 &setup.shortcut.snap_left, "shortcut.snap_left"
10545 &setup.shortcut.snap_right, "shortcut.snap_right"
10549 &setup.shortcut.snap_up, "shortcut.snap_up"
10553 &setup.shortcut.snap_down, "shortcut.snap_down"
10557 static struct SetupInputInfo setup_input;
10558 static struct TokenInfo player_setup_tokens[] =
10562 &setup_input.use_joystick, ".use_joystick"
10566 &setup_input.joy.device_name, ".joy.device_name"
10570 &setup_input.joy.xleft, ".joy.xleft"
10574 &setup_input.joy.xmiddle, ".joy.xmiddle"
10578 &setup_input.joy.xright, ".joy.xright"
10582 &setup_input.joy.yupper, ".joy.yupper"
10586 &setup_input.joy.ymiddle, ".joy.ymiddle"
10590 &setup_input.joy.ylower, ".joy.ylower"
10594 &setup_input.joy.snap, ".joy.snap_field"
10598 &setup_input.joy.drop, ".joy.place_bomb"
10602 &setup_input.key.left, ".key.move_left"
10606 &setup_input.key.right, ".key.move_right"
10610 &setup_input.key.up, ".key.move_up"
10614 &setup_input.key.down, ".key.move_down"
10618 &setup_input.key.snap, ".key.snap_field"
10622 &setup_input.key.drop, ".key.place_bomb"
10626 static struct TokenInfo system_setup_tokens[] =
10630 &setup.system.sdl_renderdriver, "system.sdl_renderdriver"
10634 &setup.system.sdl_videodriver, "system.sdl_videodriver"
10638 &setup.system.sdl_audiodriver, "system.sdl_audiodriver"
10642 &setup.system.audio_fragment_size, "system.audio_fragment_size"
10646 static struct TokenInfo internal_setup_tokens[] =
10650 &setup.internal.program_title, "program_title"
10654 &setup.internal.program_version, "program_version"
10658 &setup.internal.program_author, "program_author"
10662 &setup.internal.program_email, "program_email"
10666 &setup.internal.program_website, "program_website"
10670 &setup.internal.program_copyright, "program_copyright"
10674 &setup.internal.program_company, "program_company"
10678 &setup.internal.program_icon_file, "program_icon_file"
10682 &setup.internal.default_graphics_set, "default_graphics_set"
10686 &setup.internal.default_sounds_set, "default_sounds_set"
10690 &setup.internal.default_music_set, "default_music_set"
10694 &setup.internal.fallback_graphics_file, "fallback_graphics_file"
10698 &setup.internal.fallback_sounds_file, "fallback_sounds_file"
10702 &setup.internal.fallback_music_file, "fallback_music_file"
10706 &setup.internal.default_level_series, "default_level_series"
10710 &setup.internal.default_window_width, "default_window_width"
10714 &setup.internal.default_window_height, "default_window_height"
10718 &setup.internal.choose_from_top_leveldir, "choose_from_top_leveldir"
10722 &setup.internal.show_scaling_in_title, "show_scaling_in_title"
10726 &setup.internal.create_user_levelset, "create_user_levelset"
10730 &setup.internal.info_screens_from_main, "info_screens_from_main"
10734 &setup.internal.menu_game, "menu_game"
10738 &setup.internal.menu_engines, "menu_engines"
10742 &setup.internal.menu_editor, "menu_editor"
10746 &setup.internal.menu_graphics, "menu_graphics"
10750 &setup.internal.menu_sound, "menu_sound"
10754 &setup.internal.menu_artwork, "menu_artwork"
10758 &setup.internal.menu_input, "menu_input"
10762 &setup.internal.menu_touch, "menu_touch"
10766 &setup.internal.menu_shortcuts, "menu_shortcuts"
10770 &setup.internal.menu_exit, "menu_exit"
10774 &setup.internal.menu_save_and_exit, "menu_save_and_exit"
10778 &setup.internal.menu_shortcuts_various, "menu_shortcuts_various"
10782 &setup.internal.menu_shortcuts_focus, "menu_shortcuts_focus"
10786 &setup.internal.menu_shortcuts_tape, "menu_shortcuts_tape"
10790 &setup.internal.menu_shortcuts_sound, "menu_shortcuts_sound"
10794 &setup.internal.menu_shortcuts_snap, "menu_shortcuts_snap"
10798 &setup.internal.info_title, "info_title"
10802 &setup.internal.info_elements, "info_elements"
10806 &setup.internal.info_music, "info_music"
10810 &setup.internal.info_credits, "info_credits"
10814 &setup.internal.info_program, "info_program"
10818 &setup.internal.info_version, "info_version"
10822 &setup.internal.info_levelset, "info_levelset"
10826 &setup.internal.info_exit, "info_exit"
10830 static struct TokenInfo debug_setup_tokens[] =
10834 &setup.debug.frame_delay[0], "debug.frame_delay_0"
10838 &setup.debug.frame_delay[1], "debug.frame_delay_1"
10842 &setup.debug.frame_delay[2], "debug.frame_delay_2"
10846 &setup.debug.frame_delay[3], "debug.frame_delay_3"
10850 &setup.debug.frame_delay[4], "debug.frame_delay_4"
10854 &setup.debug.frame_delay[5], "debug.frame_delay_5"
10858 &setup.debug.frame_delay[6], "debug.frame_delay_6"
10862 &setup.debug.frame_delay[7], "debug.frame_delay_7"
10866 &setup.debug.frame_delay[8], "debug.frame_delay_8"
10870 &setup.debug.frame_delay[9], "debug.frame_delay_9"
10874 &setup.debug.frame_delay_key[0], "debug.key.frame_delay_0"
10878 &setup.debug.frame_delay_key[1], "debug.key.frame_delay_1"
10882 &setup.debug.frame_delay_key[2], "debug.key.frame_delay_2"
10886 &setup.debug.frame_delay_key[3], "debug.key.frame_delay_3"
10890 &setup.debug.frame_delay_key[4], "debug.key.frame_delay_4"
10894 &setup.debug.frame_delay_key[5], "debug.key.frame_delay_5"
10898 &setup.debug.frame_delay_key[6], "debug.key.frame_delay_6"
10902 &setup.debug.frame_delay_key[7], "debug.key.frame_delay_7"
10906 &setup.debug.frame_delay_key[8], "debug.key.frame_delay_8"
10910 &setup.debug.frame_delay_key[9], "debug.key.frame_delay_9"
10914 &setup.debug.frame_delay_use_mod_key, "debug.frame_delay.use_mod_key"},
10917 &setup.debug.frame_delay_game_only, "debug.frame_delay.game_only"
10921 &setup.debug.show_frames_per_second, "debug.show_frames_per_second"
10925 &setup.debug.xsn_mode, "debug.xsn_mode"
10929 &setup.debug.xsn_percent, "debug.xsn_percent"
10933 static struct TokenInfo options_setup_tokens[] =
10937 &setup.options.verbose, "options.verbose"
10941 &setup.options.debug, "options.debug"
10945 &setup.options.debug_mode, "options.debug_mode"
10949 static void setSetupInfoToDefaults(struct SetupInfo *si)
10953 si->player_name = getStringCopy(getDefaultUserName(user.nr));
10955 si->multiple_users = TRUE;
10958 si->sound_loops = TRUE;
10959 si->sound_music = TRUE;
10960 si->sound_simple = TRUE;
10962 si->global_animations = TRUE;
10963 si->scroll_delay = TRUE;
10964 si->forced_scroll_delay = FALSE;
10965 si->scroll_delay_value = STD_SCROLL_DELAY;
10966 si->engine_snapshot_mode = getStringCopy(STR_SNAPSHOT_MODE_DEFAULT);
10967 si->engine_snapshot_memory = SNAPSHOT_MEMORY_DEFAULT;
10968 si->fade_screens = TRUE;
10969 si->autorecord = TRUE;
10970 si->autorecord_after_replay = TRUE;
10971 si->auto_pause_on_start = FALSE;
10972 si->show_titlescreen = TRUE;
10973 si->quick_doors = FALSE;
10974 si->team_mode = FALSE;
10975 si->handicap = TRUE;
10976 si->skip_levels = TRUE;
10977 si->increment_levels = TRUE;
10978 si->auto_play_next_level = TRUE;
10979 si->count_score_after_game = TRUE;
10980 si->show_scores_after_game = TRUE;
10981 si->time_limit = TRUE;
10982 si->fullscreen = FALSE;
10983 si->window_scaling_percent = STD_WINDOW_SCALING_PERCENT;
10984 si->window_scaling_quality = getStringCopy(SCALING_QUALITY_DEFAULT);
10985 si->screen_rendering_mode = getStringCopy(STR_SPECIAL_RENDERING_DEFAULT);
10986 si->vsync_mode = getStringCopy(STR_VSYNC_MODE_DEFAULT);
10987 si->ask_on_escape = TRUE;
10988 si->ask_on_escape_editor = TRUE;
10989 si->ask_on_game_over = TRUE;
10990 si->ask_on_quit_game = TRUE;
10991 si->ask_on_quit_program = TRUE;
10992 si->quick_switch = FALSE;
10993 si->input_on_focus = FALSE;
10994 si->prefer_aga_graphics = TRUE;
10995 si->prefer_lowpass_sounds = FALSE;
10996 si->prefer_extra_panel_items = TRUE;
10997 si->game_speed_extended = FALSE;
10998 si->game_frame_delay = GAME_FRAME_DELAY;
10999 si->bd_skip_uncovering = FALSE;
11000 si->bd_skip_hatching = FALSE;
11001 si->bd_scroll_delay = TRUE;
11002 si->bd_smooth_movements = AUTO;
11003 si->sp_show_border_elements = FALSE;
11004 si->small_game_graphics = FALSE;
11005 si->show_load_save_buttons = FALSE;
11006 si->show_undo_redo_buttons = FALSE;
11007 si->scores_in_highscore_list = getStringCopy(STR_SCORES_TYPE_DEFAULT);
11009 si->graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
11010 si->sounds_set = getStringCopy(SND_CLASSIC_SUBDIR);
11011 si->music_set = getStringCopy(MUS_CLASSIC_SUBDIR);
11013 si->override_level_graphics = FALSE;
11014 si->override_level_sounds = FALSE;
11015 si->override_level_music = FALSE;
11017 si->volume_simple = 100; // percent
11018 si->volume_loops = 100; // percent
11019 si->volume_music = 100; // percent
11021 si->network_mode = FALSE;
11022 si->network_player_nr = 0; // first player
11023 si->network_server_hostname = getStringCopy(STR_NETWORK_AUTO_DETECT);
11025 si->touch.control_type = getStringCopy(TOUCH_CONTROL_DEFAULT);
11026 si->touch.move_distance = TOUCH_MOVE_DISTANCE_DEFAULT; // percent
11027 si->touch.drop_distance = TOUCH_DROP_DISTANCE_DEFAULT; // percent
11028 si->touch.transparency = TOUCH_TRANSPARENCY_DEFAULT; // percent
11029 si->touch.draw_outlined = TRUE;
11030 si->touch.draw_pressed = TRUE;
11032 for (i = 0; i < 2; i++)
11034 char *default_grid_button[6][2] =
11040 { "111222", " vv " },
11041 { "111222", " vv " }
11043 int grid_xsize = DEFAULT_GRID_XSIZE(i);
11044 int grid_ysize = DEFAULT_GRID_YSIZE(i);
11045 int min_xsize = MIN(6, grid_xsize);
11046 int min_ysize = MIN(6, grid_ysize);
11047 int startx = grid_xsize - min_xsize;
11048 int starty = grid_ysize - min_ysize;
11051 // virtual buttons grid can only be set to defaults if video is initialized
11052 // (this will be repeated if virtual buttons are not loaded from setup file)
11053 if (video.initialized)
11055 si->touch.grid_xsize[i] = grid_xsize;
11056 si->touch.grid_ysize[i] = grid_ysize;
11060 si->touch.grid_xsize[i] = -1;
11061 si->touch.grid_ysize[i] = -1;
11064 for (x = 0; x < MAX_GRID_XSIZE; x++)
11065 for (y = 0; y < MAX_GRID_YSIZE; y++)
11066 si->touch.grid_button[i][x][y] = CHAR_GRID_BUTTON_NONE;
11068 for (x = 0; x < min_xsize; x++)
11069 for (y = 0; y < min_ysize; y++)
11070 si->touch.grid_button[i][x][starty + y] =
11071 default_grid_button[y][0][x];
11073 for (x = 0; x < min_xsize; x++)
11074 for (y = 0; y < min_ysize; y++)
11075 si->touch.grid_button[i][startx + x][starty + y] =
11076 default_grid_button[y][1][x];
11079 si->touch.grid_initialized = video.initialized;
11081 si->touch.overlay_buttons = FALSE;
11083 si->editor.el_boulderdash = TRUE;
11084 si->editor.el_boulderdash_native = TRUE;
11085 si->editor.el_emerald_mine = TRUE;
11086 si->editor.el_emerald_mine_club = TRUE;
11087 si->editor.el_more = TRUE;
11088 si->editor.el_sokoban = TRUE;
11089 si->editor.el_supaplex = TRUE;
11090 si->editor.el_diamond_caves = TRUE;
11091 si->editor.el_dx_boulderdash = TRUE;
11093 si->editor.el_mirror_magic = TRUE;
11094 si->editor.el_deflektor = TRUE;
11096 si->editor.el_chars = TRUE;
11097 si->editor.el_steel_chars = TRUE;
11099 si->editor.el_classic = TRUE;
11100 si->editor.el_custom = TRUE;
11102 si->editor.el_user_defined = FALSE;
11103 si->editor.el_dynamic = TRUE;
11105 si->editor.el_headlines = TRUE;
11107 si->editor.show_element_token = FALSE;
11109 si->editor.show_read_only_warning = TRUE;
11111 si->editor.use_template_for_new_levels = TRUE;
11113 si->shortcut.save_game = DEFAULT_KEY_SAVE_GAME;
11114 si->shortcut.load_game = DEFAULT_KEY_LOAD_GAME;
11115 si->shortcut.restart_game = DEFAULT_KEY_RESTART_GAME;
11116 si->shortcut.pause_before_end = DEFAULT_KEY_PAUSE_BEFORE_END;
11117 si->shortcut.toggle_pause = DEFAULT_KEY_TOGGLE_PAUSE;
11119 si->shortcut.focus_player[0] = DEFAULT_KEY_FOCUS_PLAYER_1;
11120 si->shortcut.focus_player[1] = DEFAULT_KEY_FOCUS_PLAYER_2;
11121 si->shortcut.focus_player[2] = DEFAULT_KEY_FOCUS_PLAYER_3;
11122 si->shortcut.focus_player[3] = DEFAULT_KEY_FOCUS_PLAYER_4;
11123 si->shortcut.focus_player_all = DEFAULT_KEY_FOCUS_PLAYER_ALL;
11125 si->shortcut.tape_eject = DEFAULT_KEY_TAPE_EJECT;
11126 si->shortcut.tape_extra = DEFAULT_KEY_TAPE_EXTRA;
11127 si->shortcut.tape_stop = DEFAULT_KEY_TAPE_STOP;
11128 si->shortcut.tape_pause = DEFAULT_KEY_TAPE_PAUSE;
11129 si->shortcut.tape_record = DEFAULT_KEY_TAPE_RECORD;
11130 si->shortcut.tape_play = DEFAULT_KEY_TAPE_PLAY;
11132 si->shortcut.sound_simple = DEFAULT_KEY_SOUND_SIMPLE;
11133 si->shortcut.sound_loops = DEFAULT_KEY_SOUND_LOOPS;
11134 si->shortcut.sound_music = DEFAULT_KEY_SOUND_MUSIC;
11136 si->shortcut.snap_left = DEFAULT_KEY_SNAP_LEFT;
11137 si->shortcut.snap_right = DEFAULT_KEY_SNAP_RIGHT;
11138 si->shortcut.snap_up = DEFAULT_KEY_SNAP_UP;
11139 si->shortcut.snap_down = DEFAULT_KEY_SNAP_DOWN;
11141 for (i = 0; i < MAX_PLAYERS; i++)
11143 si->input[i].use_joystick = FALSE;
11144 si->input[i].joy.device_name = getStringCopy(getDeviceNameFromJoystickNr(i));
11145 si->input[i].joy.xleft = JOYSTICK_XLEFT;
11146 si->input[i].joy.xmiddle = JOYSTICK_XMIDDLE;
11147 si->input[i].joy.xright = JOYSTICK_XRIGHT;
11148 si->input[i].joy.yupper = JOYSTICK_YUPPER;
11149 si->input[i].joy.ymiddle = JOYSTICK_YMIDDLE;
11150 si->input[i].joy.ylower = JOYSTICK_YLOWER;
11151 si->input[i].joy.snap = (i == 0 ? JOY_BUTTON_1 : 0);
11152 si->input[i].joy.drop = (i == 0 ? JOY_BUTTON_2 : 0);
11153 si->input[i].key.left = (i == 0 ? DEFAULT_KEY_LEFT : KSYM_UNDEFINED);
11154 si->input[i].key.right = (i == 0 ? DEFAULT_KEY_RIGHT : KSYM_UNDEFINED);
11155 si->input[i].key.up = (i == 0 ? DEFAULT_KEY_UP : KSYM_UNDEFINED);
11156 si->input[i].key.down = (i == 0 ? DEFAULT_KEY_DOWN : KSYM_UNDEFINED);
11157 si->input[i].key.snap = (i == 0 ? DEFAULT_KEY_SNAP : KSYM_UNDEFINED);
11158 si->input[i].key.drop = (i == 0 ? DEFAULT_KEY_DROP : KSYM_UNDEFINED);
11161 si->system.sdl_renderdriver = getStringCopy(ARG_DEFAULT);
11162 si->system.sdl_videodriver = getStringCopy(ARG_DEFAULT);
11163 si->system.sdl_audiodriver = getStringCopy(ARG_DEFAULT);
11164 si->system.audio_fragment_size = DEFAULT_AUDIO_FRAGMENT_SIZE;
11166 si->internal.program_title = getStringCopy(PROGRAM_TITLE_STRING);
11167 si->internal.program_version = getStringCopy(getProgramRealVersionString());
11168 si->internal.program_author = getStringCopy(PROGRAM_AUTHOR_STRING);
11169 si->internal.program_email = getStringCopy(PROGRAM_EMAIL_STRING);
11170 si->internal.program_website = getStringCopy(PROGRAM_WEBSITE_STRING);
11171 si->internal.program_copyright = getStringCopy(PROGRAM_COPYRIGHT_STRING);
11172 si->internal.program_company = getStringCopy(PROGRAM_COMPANY_STRING);
11174 si->internal.program_icon_file = getStringCopy(PROGRAM_ICON_FILENAME);
11176 si->internal.default_graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
11177 si->internal.default_sounds_set = getStringCopy(SND_CLASSIC_SUBDIR);
11178 si->internal.default_music_set = getStringCopy(MUS_CLASSIC_SUBDIR);
11180 si->internal.fallback_graphics_file = getStringCopy(UNDEFINED_FILENAME);
11181 si->internal.fallback_sounds_file = getStringCopy(UNDEFINED_FILENAME);
11182 si->internal.fallback_music_file = getStringCopy(UNDEFINED_FILENAME);
11184 si->internal.default_level_series = getStringCopy(UNDEFINED_LEVELSET);
11185 si->internal.choose_from_top_leveldir = FALSE;
11186 si->internal.show_scaling_in_title = TRUE;
11187 si->internal.create_user_levelset = TRUE;
11188 si->internal.info_screens_from_main = FALSE;
11190 si->internal.default_window_width = WIN_XSIZE_DEFAULT;
11191 si->internal.default_window_height = WIN_YSIZE_DEFAULT;
11193 si->debug.frame_delay[0] = DEFAULT_FRAME_DELAY_0;
11194 si->debug.frame_delay[1] = DEFAULT_FRAME_DELAY_1;
11195 si->debug.frame_delay[2] = DEFAULT_FRAME_DELAY_2;
11196 si->debug.frame_delay[3] = DEFAULT_FRAME_DELAY_3;
11197 si->debug.frame_delay[4] = DEFAULT_FRAME_DELAY_4;
11198 si->debug.frame_delay[5] = DEFAULT_FRAME_DELAY_5;
11199 si->debug.frame_delay[6] = DEFAULT_FRAME_DELAY_6;
11200 si->debug.frame_delay[7] = DEFAULT_FRAME_DELAY_7;
11201 si->debug.frame_delay[8] = DEFAULT_FRAME_DELAY_8;
11202 si->debug.frame_delay[9] = DEFAULT_FRAME_DELAY_9;
11204 si->debug.frame_delay_key[0] = DEFAULT_KEY_FRAME_DELAY_0;
11205 si->debug.frame_delay_key[1] = DEFAULT_KEY_FRAME_DELAY_1;
11206 si->debug.frame_delay_key[2] = DEFAULT_KEY_FRAME_DELAY_2;
11207 si->debug.frame_delay_key[3] = DEFAULT_KEY_FRAME_DELAY_3;
11208 si->debug.frame_delay_key[4] = DEFAULT_KEY_FRAME_DELAY_4;
11209 si->debug.frame_delay_key[5] = DEFAULT_KEY_FRAME_DELAY_5;
11210 si->debug.frame_delay_key[6] = DEFAULT_KEY_FRAME_DELAY_6;
11211 si->debug.frame_delay_key[7] = DEFAULT_KEY_FRAME_DELAY_7;
11212 si->debug.frame_delay_key[8] = DEFAULT_KEY_FRAME_DELAY_8;
11213 si->debug.frame_delay_key[9] = DEFAULT_KEY_FRAME_DELAY_9;
11215 si->debug.frame_delay_use_mod_key = DEFAULT_FRAME_DELAY_USE_MOD_KEY;
11216 si->debug.frame_delay_game_only = DEFAULT_FRAME_DELAY_GAME_ONLY;
11218 si->debug.show_frames_per_second = FALSE;
11220 si->debug.xsn_mode = AUTO;
11221 si->debug.xsn_percent = 0;
11223 si->options.verbose = FALSE;
11224 si->options.debug = FALSE;
11225 si->options.debug_mode = getStringCopy(ARG_UNDEFINED_STRING);
11227 #if defined(PLATFORM_ANDROID)
11228 si->fullscreen = TRUE;
11229 si->touch.overlay_buttons = TRUE;
11232 setHideSetupEntry(&setup.debug.xsn_mode);
11235 static void setSetupInfoToDefaults_AutoSetup(struct SetupInfo *si)
11237 si->auto_setup.editor_zoom_tilesize = MINI_TILESIZE;
11240 static void setSetupInfoToDefaults_ServerSetup(struct SetupInfo *si)
11242 si->player_uuid = NULL; // (will be set later)
11243 si->player_version = 1; // (will be set later)
11245 si->use_api_server = TRUE;
11246 si->api_server_hostname = getStringCopy(API_SERVER_HOSTNAME);
11247 si->api_server_password = getStringCopy(UNDEFINED_PASSWORD);
11248 si->ask_for_uploading_tapes = TRUE;
11249 si->ask_for_remaining_tapes = FALSE;
11250 si->provide_uploading_tapes = TRUE;
11251 si->ask_for_using_api_server = TRUE;
11252 si->has_remaining_tapes = FALSE;
11255 static void setSetupInfoToDefaults_EditorCascade(struct SetupInfo *si)
11257 si->editor_cascade.el_bd = TRUE;
11258 si->editor_cascade.el_bd_native = TRUE;
11259 si->editor_cascade.el_em = TRUE;
11260 si->editor_cascade.el_emc = TRUE;
11261 si->editor_cascade.el_rnd = TRUE;
11262 si->editor_cascade.el_sb = TRUE;
11263 si->editor_cascade.el_sp = TRUE;
11264 si->editor_cascade.el_dc = TRUE;
11265 si->editor_cascade.el_dx = TRUE;
11267 si->editor_cascade.el_mm = TRUE;
11268 si->editor_cascade.el_df = TRUE;
11270 si->editor_cascade.el_chars = FALSE;
11271 si->editor_cascade.el_steel_chars = FALSE;
11272 si->editor_cascade.el_ce = FALSE;
11273 si->editor_cascade.el_ge = FALSE;
11274 si->editor_cascade.el_es = FALSE;
11275 si->editor_cascade.el_ref = FALSE;
11276 si->editor_cascade.el_user = FALSE;
11277 si->editor_cascade.el_dynamic = FALSE;
11280 #define MAX_HIDE_SETUP_TOKEN_SIZE 20
11282 static char *getHideSetupToken(void *setup_value)
11284 static char hide_setup_token[MAX_HIDE_SETUP_TOKEN_SIZE];
11286 if (setup_value != NULL)
11287 snprintf(hide_setup_token, MAX_HIDE_SETUP_TOKEN_SIZE, "%p", setup_value);
11289 return hide_setup_token;
11292 void setHideSetupEntry(void *setup_value)
11294 char *hide_setup_token = getHideSetupToken(setup_value);
11296 if (hide_setup_hash == NULL)
11297 hide_setup_hash = newSetupFileHash();
11299 if (setup_value != NULL)
11300 setHashEntry(hide_setup_hash, hide_setup_token, "");
11303 void removeHideSetupEntry(void *setup_value)
11305 char *hide_setup_token = getHideSetupToken(setup_value);
11307 if (setup_value != NULL)
11308 removeHashEntry(hide_setup_hash, hide_setup_token);
11311 boolean hideSetupEntry(void *setup_value)
11313 char *hide_setup_token = getHideSetupToken(setup_value);
11315 return (setup_value != NULL &&
11316 getHashEntry(hide_setup_hash, hide_setup_token) != NULL);
11319 static void setSetupInfoFromTokenText(SetupFileHash *setup_file_hash,
11320 struct TokenInfo *token_info,
11321 int token_nr, char *token_text)
11323 char *token_hide_text = getStringCat2(token_text, ".hide");
11324 char *token_hide_value = getHashEntry(setup_file_hash, token_hide_text);
11326 // set the value of this setup option in the setup option structure
11327 setSetupInfo(token_info, token_nr, getHashEntry(setup_file_hash, token_text));
11329 // check if this setup option should be hidden in the setup menu
11330 if (token_hide_value != NULL && get_boolean_from_string(token_hide_value))
11331 setHideSetupEntry(token_info[token_nr].value);
11333 free(token_hide_text);
11336 static void setSetupInfoFromTokenInfo(SetupFileHash *setup_file_hash,
11337 struct TokenInfo *token_info,
11340 setSetupInfoFromTokenText(setup_file_hash, token_info, token_nr,
11341 token_info[token_nr].text);
11344 static void decodeSetupFileHash_Default(SetupFileHash *setup_file_hash)
11348 if (!setup_file_hash)
11351 for (i = 0; i < ARRAY_SIZE(global_setup_tokens); i++)
11352 setSetupInfoFromTokenInfo(setup_file_hash, global_setup_tokens, i);
11354 setup.touch.grid_initialized = TRUE;
11355 for (i = 0; i < 2; i++)
11357 int grid_xsize = setup.touch.grid_xsize[i];
11358 int grid_ysize = setup.touch.grid_ysize[i];
11361 // if virtual buttons are not loaded from setup file, repeat initializing
11362 // virtual buttons grid with default values later when video is initialized
11363 if (grid_xsize == -1 ||
11366 setup.touch.grid_initialized = FALSE;
11371 for (y = 0; y < grid_ysize; y++)
11373 char token_string[MAX_LINE_LEN];
11375 sprintf(token_string, "touch.virtual_buttons.%d.%02d", i, y);
11377 char *value_string = getHashEntry(setup_file_hash, token_string);
11379 if (value_string == NULL)
11382 for (x = 0; x < grid_xsize; x++)
11384 char c = value_string[x];
11386 setup.touch.grid_button[i][x][y] =
11387 (c == '.' ? CHAR_GRID_BUTTON_NONE : c);
11392 for (i = 0; i < ARRAY_SIZE(editor_setup_tokens); i++)
11393 setSetupInfoFromTokenInfo(setup_file_hash, editor_setup_tokens, i);
11395 for (i = 0; i < ARRAY_SIZE(shortcut_setup_tokens); i++)
11396 setSetupInfoFromTokenInfo(setup_file_hash, shortcut_setup_tokens, i);
11398 for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
11402 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
11404 setup_input = setup.input[pnr];
11405 for (i = 0; i < ARRAY_SIZE(player_setup_tokens); i++)
11407 char full_token[100];
11409 sprintf(full_token, "%s%s", prefix, player_setup_tokens[i].text);
11410 setSetupInfoFromTokenText(setup_file_hash, player_setup_tokens, i,
11413 setup.input[pnr] = setup_input;
11416 for (i = 0; i < ARRAY_SIZE(system_setup_tokens); i++)
11417 setSetupInfoFromTokenInfo(setup_file_hash, system_setup_tokens, i);
11419 for (i = 0; i < ARRAY_SIZE(internal_setup_tokens); i++)
11420 setSetupInfoFromTokenInfo(setup_file_hash, internal_setup_tokens, i);
11422 for (i = 0; i < ARRAY_SIZE(debug_setup_tokens); i++)
11423 setSetupInfoFromTokenInfo(setup_file_hash, debug_setup_tokens, i);
11425 for (i = 0; i < ARRAY_SIZE(options_setup_tokens); i++)
11426 setSetupInfoFromTokenInfo(setup_file_hash, options_setup_tokens, i);
11428 setHideRelatedSetupEntries();
11431 static void decodeSetupFileHash_AutoSetup(SetupFileHash *setup_file_hash)
11435 if (!setup_file_hash)
11438 for (i = 0; i < ARRAY_SIZE(auto_setup_tokens); i++)
11439 setSetupInfo(auto_setup_tokens, i,
11440 getHashEntry(setup_file_hash,
11441 auto_setup_tokens[i].text));
11444 static void decodeSetupFileHash_ServerSetup(SetupFileHash *setup_file_hash)
11448 if (!setup_file_hash)
11451 for (i = 0; i < ARRAY_SIZE(server_setup_tokens); i++)
11452 setSetupInfo(server_setup_tokens, i,
11453 getHashEntry(setup_file_hash,
11454 server_setup_tokens[i].text));
11457 static void decodeSetupFileHash_EditorCascade(SetupFileHash *setup_file_hash)
11461 if (!setup_file_hash)
11464 for (i = 0; i < ARRAY_SIZE(editor_cascade_setup_tokens); i++)
11465 setSetupInfo(editor_cascade_setup_tokens, i,
11466 getHashEntry(setup_file_hash,
11467 editor_cascade_setup_tokens[i].text));
11470 void LoadUserNames(void)
11472 int last_user_nr = user.nr;
11475 if (global.user_names != NULL)
11477 for (i = 0; i < MAX_PLAYER_NAMES; i++)
11478 checked_free(global.user_names[i]);
11480 checked_free(global.user_names);
11483 global.user_names = checked_calloc(MAX_PLAYER_NAMES * sizeof(char *));
11485 for (i = 0; i < MAX_PLAYER_NAMES; i++)
11489 SetupFileHash *setup_file_hash = loadSetupFileHash(getSetupFilename());
11491 if (setup_file_hash)
11493 char *player_name = getHashEntry(setup_file_hash, "player_name");
11495 global.user_names[i] = getFixedUserName(player_name);
11497 freeSetupFileHash(setup_file_hash);
11500 if (global.user_names[i] == NULL)
11501 global.user_names[i] = getStringCopy(getDefaultUserName(i));
11504 user.nr = last_user_nr;
11507 void LoadSetupFromFilename(char *filename)
11509 SetupFileHash *setup_file_hash = loadSetupFileHash(filename);
11511 if (setup_file_hash)
11513 decodeSetupFileHash_Default(setup_file_hash);
11515 freeSetupFileHash(setup_file_hash);
11519 Debug("setup", "using default setup values");
11523 static void LoadSetup_SpecialPostProcessing(void)
11525 char *player_name_new;
11527 // needed to work around problems with fixed length strings
11528 player_name_new = getFixedUserName(setup.player_name);
11529 free(setup.player_name);
11530 setup.player_name = player_name_new;
11532 // "scroll_delay: on(3) / off(0)" was replaced by scroll delay value
11533 if (setup.scroll_delay == FALSE)
11535 setup.scroll_delay_value = MIN_SCROLL_DELAY;
11536 setup.scroll_delay = TRUE; // now always "on"
11539 // make sure that scroll delay value stays inside valid range
11540 setup.scroll_delay_value =
11541 MIN(MAX(MIN_SCROLL_DELAY, setup.scroll_delay_value), MAX_SCROLL_DELAY);
11544 void LoadSetup_Default(void)
11548 // always start with reliable default values
11549 setSetupInfoToDefaults(&setup);
11551 // try to load setup values from default setup file
11552 filename = getDefaultSetupFilename();
11554 if (fileExists(filename))
11555 LoadSetupFromFilename(filename);
11557 // try to load setup values from platform setup file
11558 filename = getPlatformSetupFilename();
11560 if (fileExists(filename))
11561 LoadSetupFromFilename(filename);
11563 // try to load setup values from user setup file
11564 filename = getSetupFilename();
11566 LoadSetupFromFilename(filename);
11568 LoadSetup_SpecialPostProcessing();
11571 void LoadSetup_AutoSetup(void)
11573 char *filename = getPath2(getSetupDir(), AUTOSETUP_FILENAME);
11574 SetupFileHash *setup_file_hash = NULL;
11576 // always start with reliable default values
11577 setSetupInfoToDefaults_AutoSetup(&setup);
11579 setup_file_hash = loadSetupFileHash(filename);
11581 if (setup_file_hash)
11583 decodeSetupFileHash_AutoSetup(setup_file_hash);
11585 freeSetupFileHash(setup_file_hash);
11591 void LoadSetup_ServerSetup(void)
11593 char *filename = getPath2(getSetupDir(), SERVERSETUP_FILENAME);
11594 SetupFileHash *setup_file_hash = NULL;
11596 // always start with reliable default values
11597 setSetupInfoToDefaults_ServerSetup(&setup);
11599 setup_file_hash = loadSetupFileHash(filename);
11601 if (setup_file_hash)
11603 decodeSetupFileHash_ServerSetup(setup_file_hash);
11605 freeSetupFileHash(setup_file_hash);
11610 if (setup.player_uuid == NULL)
11612 // player UUID does not yet exist in setup file
11613 setup.player_uuid = getStringCopy(getUUID());
11614 setup.player_version = 2;
11616 SaveSetup_ServerSetup();
11620 void LoadSetup_EditorCascade(void)
11622 char *filename = getPath2(getSetupDir(), EDITORCASCADE_FILENAME);
11623 SetupFileHash *setup_file_hash = NULL;
11625 // always start with reliable default values
11626 setSetupInfoToDefaults_EditorCascade(&setup);
11628 setup_file_hash = loadSetupFileHash(filename);
11630 if (setup_file_hash)
11632 decodeSetupFileHash_EditorCascade(setup_file_hash);
11634 freeSetupFileHash(setup_file_hash);
11640 void LoadSetup(void)
11642 LoadSetup_Default();
11643 LoadSetup_AutoSetup();
11644 LoadSetup_ServerSetup();
11645 LoadSetup_EditorCascade();
11648 static void addGameControllerMappingToHash(SetupFileHash *mappings_hash,
11649 char *mapping_line)
11651 char mapping_guid[MAX_LINE_LEN];
11652 char *mapping_start, *mapping_end;
11654 // get GUID from game controller mapping line: copy complete line
11655 strncpy(mapping_guid, mapping_line, MAX_LINE_LEN - 1);
11656 mapping_guid[MAX_LINE_LEN - 1] = '\0';
11658 // get GUID from game controller mapping line: cut after GUID part
11659 mapping_start = strchr(mapping_guid, ',');
11660 if (mapping_start != NULL)
11661 *mapping_start = '\0';
11663 // cut newline from game controller mapping line
11664 mapping_end = strchr(mapping_line, '\n');
11665 if (mapping_end != NULL)
11666 *mapping_end = '\0';
11668 // add mapping entry to game controller mappings hash
11669 setHashEntry(mappings_hash, mapping_guid, mapping_line);
11672 static void LoadSetup_ReadGameControllerMappings(SetupFileHash *mappings_hash,
11677 if (!(file = fopen(filename, MODE_READ)))
11679 Warn("cannot read game controller mappings file '%s'", filename);
11684 while (!feof(file))
11686 char line[MAX_LINE_LEN];
11688 if (!fgets(line, MAX_LINE_LEN, file))
11691 addGameControllerMappingToHash(mappings_hash, line);
11697 void SaveSetup_Default(void)
11699 char *filename = getSetupFilename();
11703 InitUserDataDirectory();
11705 if (!(file = fopen(filename, MODE_WRITE)))
11707 Warn("cannot write setup file '%s'", filename);
11712 fprintFileHeader(file, SETUP_FILENAME);
11714 for (i = 0; i < ARRAY_SIZE(global_setup_tokens); i++)
11716 // just to make things nicer :)
11717 if (global_setup_tokens[i].value == &setup.multiple_users ||
11718 global_setup_tokens[i].value == &setup.sound ||
11719 global_setup_tokens[i].value == &setup.graphics_set ||
11720 global_setup_tokens[i].value == &setup.volume_simple ||
11721 global_setup_tokens[i].value == &setup.network_mode ||
11722 global_setup_tokens[i].value == &setup.touch.control_type ||
11723 global_setup_tokens[i].value == &setup.touch.grid_xsize[0] ||
11724 global_setup_tokens[i].value == &setup.touch.grid_xsize[1])
11725 fprintf(file, "\n");
11727 fprintf(file, "%s\n", getSetupLine(global_setup_tokens, "", i));
11730 for (i = 0; i < 2; i++)
11732 int grid_xsize = setup.touch.grid_xsize[i];
11733 int grid_ysize = setup.touch.grid_ysize[i];
11736 fprintf(file, "\n");
11738 for (y = 0; y < grid_ysize; y++)
11740 char token_string[MAX_LINE_LEN];
11741 char value_string[MAX_LINE_LEN];
11743 sprintf(token_string, "touch.virtual_buttons.%d.%02d", i, y);
11745 for (x = 0; x < grid_xsize; x++)
11747 char c = setup.touch.grid_button[i][x][y];
11749 value_string[x] = (c == CHAR_GRID_BUTTON_NONE ? '.' : c);
11752 value_string[grid_xsize] = '\0';
11754 fprintf(file, "%s\n", getFormattedSetupEntry(token_string, value_string));
11758 fprintf(file, "\n");
11759 for (i = 0; i < ARRAY_SIZE(editor_setup_tokens); i++)
11760 fprintf(file, "%s\n", getSetupLine(editor_setup_tokens, "", i));
11762 fprintf(file, "\n");
11763 for (i = 0; i < ARRAY_SIZE(shortcut_setup_tokens); i++)
11764 fprintf(file, "%s\n", getSetupLine(shortcut_setup_tokens, "", i));
11766 for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
11770 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
11771 fprintf(file, "\n");
11773 setup_input = setup.input[pnr];
11774 for (i = 0; i < ARRAY_SIZE(player_setup_tokens); i++)
11775 fprintf(file, "%s\n", getSetupLine(player_setup_tokens, prefix, i));
11778 fprintf(file, "\n");
11779 for (i = 0; i < ARRAY_SIZE(system_setup_tokens); i++)
11780 fprintf(file, "%s\n", getSetupLine(system_setup_tokens, "", i));
11782 // (internal setup values not saved to user setup file)
11784 fprintf(file, "\n");
11785 for (i = 0; i < ARRAY_SIZE(debug_setup_tokens); i++)
11786 if (!strPrefix(debug_setup_tokens[i].text, "debug.xsn_") ||
11787 setup.debug.xsn_mode != AUTO)
11788 fprintf(file, "%s\n", getSetupLine(debug_setup_tokens, "", i));
11790 fprintf(file, "\n");
11791 for (i = 0; i < ARRAY_SIZE(options_setup_tokens); i++)
11792 fprintf(file, "%s\n", getSetupLine(options_setup_tokens, "", i));
11796 SetFilePermissions(filename, PERMS_PRIVATE);
11799 void SaveSetup_AutoSetup(void)
11801 char *filename = getPath2(getSetupDir(), AUTOSETUP_FILENAME);
11805 InitUserDataDirectory();
11807 if (!(file = fopen(filename, MODE_WRITE)))
11809 Warn("cannot write auto setup file '%s'", filename);
11816 fprintFileHeader(file, AUTOSETUP_FILENAME);
11818 for (i = 0; i < ARRAY_SIZE(auto_setup_tokens); i++)
11819 fprintf(file, "%s\n", getSetupLine(auto_setup_tokens, "", i));
11823 SetFilePermissions(filename, PERMS_PRIVATE);
11828 void SaveSetup_ServerSetup(void)
11830 char *filename = getPath2(getSetupDir(), SERVERSETUP_FILENAME);
11834 InitUserDataDirectory();
11836 if (!(file = fopen(filename, MODE_WRITE)))
11838 Warn("cannot write server setup file '%s'", filename);
11845 fprintFileHeader(file, SERVERSETUP_FILENAME);
11847 for (i = 0; i < ARRAY_SIZE(server_setup_tokens); i++)
11849 // just to make things nicer :)
11850 if (server_setup_tokens[i].value == &setup.use_api_server)
11851 fprintf(file, "\n");
11853 fprintf(file, "%s\n", getSetupLine(server_setup_tokens, "", i));
11858 SetFilePermissions(filename, PERMS_PRIVATE);
11863 void SaveSetup_EditorCascade(void)
11865 char *filename = getPath2(getSetupDir(), EDITORCASCADE_FILENAME);
11869 InitUserDataDirectory();
11871 if (!(file = fopen(filename, MODE_WRITE)))
11873 Warn("cannot write editor cascade state file '%s'", filename);
11880 fprintFileHeader(file, EDITORCASCADE_FILENAME);
11882 for (i = 0; i < ARRAY_SIZE(editor_cascade_setup_tokens); i++)
11883 fprintf(file, "%s\n", getSetupLine(editor_cascade_setup_tokens, "", i));
11887 SetFilePermissions(filename, PERMS_PRIVATE);
11892 void SaveSetup(void)
11894 SaveSetup_Default();
11895 SaveSetup_AutoSetup();
11896 SaveSetup_ServerSetup();
11897 SaveSetup_EditorCascade();
11900 static void SaveSetup_WriteGameControllerMappings(SetupFileHash *mappings_hash,
11905 if (!(file = fopen(filename, MODE_WRITE)))
11907 Warn("cannot write game controller mappings file '%s'", filename);
11912 BEGIN_HASH_ITERATION(mappings_hash, itr)
11914 fprintf(file, "%s\n", HASH_ITERATION_VALUE(itr));
11916 END_HASH_ITERATION(mappings_hash, itr)
11921 void SaveSetup_AddGameControllerMapping(char *mapping)
11923 char *filename = getPath2(getSetupDir(), GAMECONTROLLER_BASENAME);
11924 SetupFileHash *mappings_hash = newSetupFileHash();
11926 InitUserDataDirectory();
11928 // load existing personal game controller mappings
11929 LoadSetup_ReadGameControllerMappings(mappings_hash, filename);
11931 // add new mapping to personal game controller mappings
11932 addGameControllerMappingToHash(mappings_hash, mapping);
11934 // save updated personal game controller mappings
11935 SaveSetup_WriteGameControllerMappings(mappings_hash, filename);
11937 freeSetupFileHash(mappings_hash);
11941 void LoadCustomElementDescriptions(void)
11943 char *filename = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
11944 SetupFileHash *setup_file_hash;
11947 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
11949 if (element_info[i].custom_description != NULL)
11951 free(element_info[i].custom_description);
11952 element_info[i].custom_description = NULL;
11956 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
11959 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
11961 char *token = getStringCat2(element_info[i].token_name, ".name");
11962 char *value = getHashEntry(setup_file_hash, token);
11965 element_info[i].custom_description = getStringCopy(value);
11970 freeSetupFileHash(setup_file_hash);
11973 static int getElementFromToken(char *token)
11975 char *value = getHashEntry(element_token_hash, token);
11978 return atoi(value);
11980 Warn("unknown element token '%s'", token);
11982 return EL_UNDEFINED;
11985 void FreeGlobalAnimEventInfo(void)
11987 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
11989 if (gaei->event_list == NULL)
11994 for (i = 0; i < gaei->num_event_lists; i++)
11996 checked_free(gaei->event_list[i]->event_value);
11997 checked_free(gaei->event_list[i]);
12000 checked_free(gaei->event_list);
12002 gaei->event_list = NULL;
12003 gaei->num_event_lists = 0;
12006 static int AddGlobalAnimEventList(void)
12008 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
12009 int list_pos = gaei->num_event_lists++;
12011 gaei->event_list = checked_realloc(gaei->event_list, gaei->num_event_lists *
12012 sizeof(struct GlobalAnimEventListInfo *));
12014 gaei->event_list[list_pos] =
12015 checked_calloc(sizeof(struct GlobalAnimEventListInfo));
12017 struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
12019 gaeli->event_value = NULL;
12020 gaeli->num_event_values = 0;
12025 static int AddGlobalAnimEventValue(int list_pos, int event_value)
12027 // do not add empty global animation events
12028 if (event_value == ANIM_EVENT_NONE)
12031 // if list position is undefined, create new list
12032 if (list_pos == ANIM_EVENT_UNDEFINED)
12033 list_pos = AddGlobalAnimEventList();
12035 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
12036 struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
12037 int value_pos = gaeli->num_event_values++;
12039 gaeli->event_value = checked_realloc(gaeli->event_value,
12040 gaeli->num_event_values * sizeof(int *));
12042 gaeli->event_value[value_pos] = event_value;
12047 int GetGlobalAnimEventValue(int list_pos, int value_pos)
12049 if (list_pos == ANIM_EVENT_UNDEFINED)
12050 return ANIM_EVENT_NONE;
12052 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
12053 struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
12055 return gaeli->event_value[value_pos];
12058 int GetGlobalAnimEventValueCount(int list_pos)
12060 if (list_pos == ANIM_EVENT_UNDEFINED)
12063 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
12064 struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
12066 return gaeli->num_event_values;
12069 // This function checks if a string <s> of the format "string1, string2, ..."
12070 // exactly contains a string <s_contained>.
12072 static boolean string_has_parameter(char *s, char *s_contained)
12076 if (s == NULL || s_contained == NULL)
12079 if (strlen(s_contained) > strlen(s))
12082 if (strncmp(s, s_contained, strlen(s_contained)) == 0)
12084 char next_char = s[strlen(s_contained)];
12086 // check if next character is delimiter or whitespace
12087 if (next_char == ',' || next_char == '\0' ||
12088 next_char == ' ' || next_char == '\t')
12092 // check if string contains another parameter string after a comma
12093 substring = strchr(s, ',');
12094 if (substring == NULL) // string does not contain a comma
12097 // advance string pointer to next character after the comma
12100 // skip potential whitespaces after the comma
12101 while (*substring == ' ' || *substring == '\t')
12104 return string_has_parameter(substring, s_contained);
12107 static int get_anim_parameter_value_ce(char *s)
12110 char *pattern_1 = "ce_change:custom_";
12111 char *pattern_2 = ".page_";
12112 int pattern_1_len = strlen(pattern_1);
12113 char *matching_char = strstr(s_ptr, pattern_1);
12114 int result = ANIM_EVENT_NONE;
12116 if (matching_char == NULL)
12117 return ANIM_EVENT_NONE;
12119 result = ANIM_EVENT_CE_CHANGE;
12121 s_ptr = matching_char + pattern_1_len;
12123 // check for custom element number ("custom_X", "custom_XX" or "custom_XXX")
12124 if (*s_ptr >= '0' && *s_ptr <= '9')
12126 int gic_ce_nr = (*s_ptr++ - '0');
12128 if (*s_ptr >= '0' && *s_ptr <= '9')
12130 gic_ce_nr = 10 * gic_ce_nr + (*s_ptr++ - '0');
12132 if (*s_ptr >= '0' && *s_ptr <= '9')
12133 gic_ce_nr = 10 * gic_ce_nr + (*s_ptr++ - '0');
12136 if (gic_ce_nr < 1 || gic_ce_nr > NUM_CUSTOM_ELEMENTS)
12137 return ANIM_EVENT_NONE;
12139 // custom element stored as 0 to 255
12142 result |= gic_ce_nr << ANIM_EVENT_CE_BIT;
12146 // invalid custom element number specified
12148 return ANIM_EVENT_NONE;
12151 // check for change page number ("page_X" or "page_XX") (optional)
12152 if (strPrefix(s_ptr, pattern_2))
12154 s_ptr += strlen(pattern_2);
12156 if (*s_ptr >= '0' && *s_ptr <= '9')
12158 int gic_page_nr = (*s_ptr++ - '0');
12160 if (*s_ptr >= '0' && *s_ptr <= '9')
12161 gic_page_nr = 10 * gic_page_nr + (*s_ptr++ - '0');
12163 if (gic_page_nr < 1 || gic_page_nr > MAX_CHANGE_PAGES)
12164 return ANIM_EVENT_NONE;
12166 // change page stored as 1 to 32 (0 means "all change pages")
12168 result |= gic_page_nr << ANIM_EVENT_PAGE_BIT;
12172 // invalid animation part number specified
12174 return ANIM_EVENT_NONE;
12178 // discard result if next character is neither delimiter nor whitespace
12179 if (!(*s_ptr == ',' || *s_ptr == '\0' ||
12180 *s_ptr == ' ' || *s_ptr == '\t'))
12181 return ANIM_EVENT_NONE;
12186 static int get_anim_parameter_value(char *s)
12188 int event_value[] =
12196 char *pattern_1[] =
12204 char *pattern_2 = ".part_";
12205 char *matching_char = NULL;
12207 int pattern_1_len = 0;
12208 int result = ANIM_EVENT_NONE;
12211 result = get_anim_parameter_value_ce(s);
12213 if (result != ANIM_EVENT_NONE)
12216 for (i = 0; i < ARRAY_SIZE(event_value); i++)
12218 matching_char = strstr(s_ptr, pattern_1[i]);
12219 pattern_1_len = strlen(pattern_1[i]);
12220 result = event_value[i];
12222 if (matching_char != NULL)
12226 if (matching_char == NULL)
12227 return ANIM_EVENT_NONE;
12229 s_ptr = matching_char + pattern_1_len;
12231 // check for main animation number ("anim_X" or "anim_XX")
12232 if (*s_ptr >= '0' && *s_ptr <= '9')
12234 int gic_anim_nr = (*s_ptr++ - '0');
12236 if (*s_ptr >= '0' && *s_ptr <= '9')
12237 gic_anim_nr = 10 * gic_anim_nr + (*s_ptr++ - '0');
12239 if (gic_anim_nr < 1 || gic_anim_nr > MAX_GLOBAL_ANIMS)
12240 return ANIM_EVENT_NONE;
12242 result |= gic_anim_nr << ANIM_EVENT_ANIM_BIT;
12246 // invalid main animation number specified
12248 return ANIM_EVENT_NONE;
12251 // check for animation part number ("part_X" or "part_XX") (optional)
12252 if (strPrefix(s_ptr, pattern_2))
12254 s_ptr += strlen(pattern_2);
12256 if (*s_ptr >= '0' && *s_ptr <= '9')
12258 int gic_part_nr = (*s_ptr++ - '0');
12260 if (*s_ptr >= '0' && *s_ptr <= '9')
12261 gic_part_nr = 10 * gic_part_nr + (*s_ptr++ - '0');
12263 if (gic_part_nr < 1 || gic_part_nr > MAX_GLOBAL_ANIM_PARTS)
12264 return ANIM_EVENT_NONE;
12266 result |= gic_part_nr << ANIM_EVENT_PART_BIT;
12270 // invalid animation part number specified
12272 return ANIM_EVENT_NONE;
12276 // discard result if next character is neither delimiter nor whitespace
12277 if (!(*s_ptr == ',' || *s_ptr == '\0' ||
12278 *s_ptr == ' ' || *s_ptr == '\t'))
12279 return ANIM_EVENT_NONE;
12284 static int get_anim_parameter_values(char *s)
12286 int list_pos = ANIM_EVENT_UNDEFINED;
12287 int event_value = ANIM_EVENT_DEFAULT;
12289 if (string_has_parameter(s, "any"))
12290 event_value |= ANIM_EVENT_ANY;
12292 if (string_has_parameter(s, "click:self") ||
12293 string_has_parameter(s, "click") ||
12294 string_has_parameter(s, "self"))
12295 event_value |= ANIM_EVENT_SELF;
12297 if (string_has_parameter(s, "unclick:any"))
12298 event_value |= ANIM_EVENT_UNCLICK_ANY;
12300 // if animation event found, add it to global animation event list
12301 if (event_value != ANIM_EVENT_NONE)
12302 list_pos = AddGlobalAnimEventValue(list_pos, event_value);
12306 // add optional "click:anim_X" or "click:anim_X.part_X" parameter
12307 event_value = get_anim_parameter_value(s);
12309 // if animation event found, add it to global animation event list
12310 if (event_value != ANIM_EVENT_NONE)
12311 list_pos = AddGlobalAnimEventValue(list_pos, event_value);
12313 // continue with next part of the string, starting with next comma
12314 s = strchr(s + 1, ',');
12320 static int get_anim_action_parameter_value(char *token)
12322 // check most common default case first to massively speed things up
12323 if (strEqual(token, ARG_UNDEFINED))
12324 return ANIM_EVENT_ACTION_NONE;
12326 int result = getImageIDFromToken(token);
12330 char *gfx_token = getStringCat2("gfx.", token);
12332 result = getImageIDFromToken(gfx_token);
12334 checked_free(gfx_token);
12339 Key key = getKeyFromX11KeyName(token);
12341 if (key != KSYM_UNDEFINED)
12342 result = -(int)key;
12349 result = get_hash_from_string(token); // unsigned int => int
12350 result = ABS(result); // may be negative now
12351 result += (result < MAX_IMAGE_FILES ? MAX_IMAGE_FILES : 0);
12353 setHashEntry(anim_url_hash, int2str(result, 0), token);
12358 result = ANIM_EVENT_ACTION_NONE;
12363 int get_parameter_value(char *value_raw, char *suffix, int type)
12365 char *value = getStringToLower(value_raw);
12366 int result = 0; // probably a save default value
12368 if (strEqual(suffix, ".direction"))
12370 result = (strEqual(value, "left") ? MV_LEFT :
12371 strEqual(value, "right") ? MV_RIGHT :
12372 strEqual(value, "up") ? MV_UP :
12373 strEqual(value, "down") ? MV_DOWN : MV_NONE);
12375 else if (strEqual(suffix, ".position"))
12377 result = (strEqual(value, "left") ? POS_LEFT :
12378 strEqual(value, "right") ? POS_RIGHT :
12379 strEqual(value, "top") ? POS_TOP :
12380 strEqual(value, "upper") ? POS_UPPER :
12381 strEqual(value, "middle") ? POS_MIDDLE :
12382 strEqual(value, "lower") ? POS_LOWER :
12383 strEqual(value, "bottom") ? POS_BOTTOM :
12384 strEqual(value, "any") ? POS_ANY :
12385 strEqual(value, "ce") ? POS_CE :
12386 strEqual(value, "ce_trigger") ? POS_CE_TRIGGER :
12387 strEqual(value, "last") ? POS_LAST : POS_UNDEFINED);
12389 else if (strEqual(suffix, ".align"))
12391 result = (strEqual(value, "left") ? ALIGN_LEFT :
12392 strEqual(value, "right") ? ALIGN_RIGHT :
12393 strEqual(value, "center") ? ALIGN_CENTER :
12394 strEqual(value, "middle") ? ALIGN_CENTER : ALIGN_DEFAULT);
12396 else if (strEqual(suffix, ".valign"))
12398 result = (strEqual(value, "top") ? VALIGN_TOP :
12399 strEqual(value, "bottom") ? VALIGN_BOTTOM :
12400 strEqual(value, "middle") ? VALIGN_MIDDLE :
12401 strEqual(value, "center") ? VALIGN_MIDDLE : VALIGN_DEFAULT);
12403 else if (strEqual(suffix, ".anim_mode"))
12405 result = (string_has_parameter(value, "none") ? ANIM_NONE :
12406 string_has_parameter(value, "loop") ? ANIM_LOOP :
12407 string_has_parameter(value, "linear") ? ANIM_LINEAR :
12408 string_has_parameter(value, "pingpong") ? ANIM_PINGPONG :
12409 string_has_parameter(value, "pingpong2") ? ANIM_PINGPONG2 :
12410 string_has_parameter(value, "random") ? ANIM_RANDOM :
12411 string_has_parameter(value, "random_static") ? ANIM_RANDOM_STATIC :
12412 string_has_parameter(value, "ce_value") ? ANIM_CE_VALUE :
12413 string_has_parameter(value, "ce_score") ? ANIM_CE_SCORE :
12414 string_has_parameter(value, "ce_delay") ? ANIM_CE_DELAY :
12415 string_has_parameter(value, "horizontal") ? ANIM_HORIZONTAL :
12416 string_has_parameter(value, "vertical") ? ANIM_VERTICAL :
12417 string_has_parameter(value, "centered") ? ANIM_CENTERED :
12418 string_has_parameter(value, "all") ? ANIM_ALL :
12419 string_has_parameter(value, "tiled") ? ANIM_TILED :
12420 string_has_parameter(value, "level_nr") ? ANIM_LEVEL_NR :
12423 if (string_has_parameter(value, "once"))
12424 result |= ANIM_ONCE;
12426 if (string_has_parameter(value, "reverse"))
12427 result |= ANIM_REVERSE;
12429 if (string_has_parameter(value, "opaque_player"))
12430 result |= ANIM_OPAQUE_PLAYER;
12432 if (string_has_parameter(value, "static_panel"))
12433 result |= ANIM_STATIC_PANEL;
12435 else if (strEqual(suffix, ".init_event") ||
12436 strEqual(suffix, ".anim_event"))
12438 result = get_anim_parameter_values(value);
12440 else if (strEqual(suffix, ".init_delay_action") ||
12441 strEqual(suffix, ".anim_delay_action") ||
12442 strEqual(suffix, ".post_delay_action") ||
12443 strEqual(suffix, ".init_event_action") ||
12444 strEqual(suffix, ".anim_event_action"))
12446 result = get_anim_action_parameter_value(value_raw);
12448 else if (strEqual(suffix, ".class"))
12450 result = (strEqual(value, ARG_UNDEFINED) ? ARG_UNDEFINED_VALUE :
12451 get_hash_from_string(value));
12453 else if (strEqual(suffix, ".style"))
12455 result = STYLE_DEFAULT;
12457 if (string_has_parameter(value, "accurate_borders"))
12458 result |= STYLE_ACCURATE_BORDERS;
12460 if (string_has_parameter(value, "inner_corners"))
12461 result |= STYLE_INNER_CORNERS;
12463 if (string_has_parameter(value, "reverse"))
12464 result |= STYLE_REVERSE;
12466 if (string_has_parameter(value, "leftmost_position"))
12467 result |= STYLE_LEFTMOST_POSITION;
12469 if (string_has_parameter(value, "block_clicks"))
12470 result |= STYLE_BLOCK;
12472 if (string_has_parameter(value, "passthrough_clicks"))
12473 result |= STYLE_PASSTHROUGH;
12475 if (string_has_parameter(value, "multiple_actions"))
12476 result |= STYLE_MULTIPLE_ACTIONS;
12478 if (string_has_parameter(value, "consume_ce_event"))
12479 result |= STYLE_CONSUME_CE_EVENT;
12481 else if (strEqual(suffix, ".fade_mode"))
12483 result = (string_has_parameter(value, "none") ? FADE_MODE_NONE :
12484 string_has_parameter(value, "fade") ? FADE_MODE_FADE :
12485 string_has_parameter(value, "fade_in") ? FADE_MODE_FADE_IN :
12486 string_has_parameter(value, "fade_out") ? FADE_MODE_FADE_OUT :
12487 string_has_parameter(value, "crossfade") ? FADE_MODE_CROSSFADE :
12488 string_has_parameter(value, "melt") ? FADE_MODE_MELT :
12489 string_has_parameter(value, "curtain") ? FADE_MODE_CURTAIN :
12490 FADE_MODE_DEFAULT);
12492 else if (strEqual(suffix, ".auto_delay_unit"))
12494 result = (string_has_parameter(value, "ms") ? AUTO_DELAY_UNIT_MS :
12495 string_has_parameter(value, "frames") ? AUTO_DELAY_UNIT_FRAMES :
12496 AUTO_DELAY_UNIT_DEFAULT);
12498 else if (strPrefix(suffix, ".font")) // (may also be ".font_xyz")
12500 result = gfx.get_font_from_token_function(value);
12502 else // generic parameter of type integer or boolean
12504 result = (strEqual(value, ARG_UNDEFINED) ? ARG_UNDEFINED_VALUE :
12505 type == TYPE_INTEGER ? get_integer_from_string(value) :
12506 type == TYPE_BOOLEAN ? get_boolean_from_string(value) :
12507 ARG_UNDEFINED_VALUE);
12515 static int get_token_parameter_value(char *token, char *value_raw)
12519 if (token == NULL || value_raw == NULL)
12520 return ARG_UNDEFINED_VALUE;
12522 suffix = strrchr(token, '.');
12523 if (suffix == NULL)
12526 if (strEqual(suffix, ".element"))
12527 return getElementFromToken(value_raw);
12529 // !!! USE CORRECT VALUE TYPE (currently works also for TYPE_BOOLEAN) !!!
12530 return get_parameter_value(value_raw, suffix, TYPE_INTEGER);
12533 void InitMenuDesignSettings_FromHash(SetupFileHash *setup_file_hash,
12534 boolean ignore_defaults)
12538 for (i = 0; image_config_vars[i].token != NULL; i++)
12540 char *value = getHashEntry(setup_file_hash, image_config_vars[i].token);
12542 // (ignore definitions set to "[DEFAULT]" which are already initialized)
12543 if (ignore_defaults && strEqual(value, ARG_DEFAULT))
12547 *image_config_vars[i].value =
12548 get_token_parameter_value(image_config_vars[i].token, value);
12552 void InitMenuDesignSettings_Static(void)
12554 // always start with reliable default values from static default config
12555 InitMenuDesignSettings_FromHash(image_config_hash, FALSE);
12558 static void InitMenuDesignSettings_SpecialPreProcessing(void)
12562 // the following initializes hierarchical values from static configuration
12564 // special case: initialize "ARG_DEFAULT" values in static default config
12565 // (e.g., initialize "[titlemessage].fade_mode" from "[title].fade_mode")
12566 titlescreen_initial_first_default.fade_mode =
12567 title_initial_first_default.fade_mode;
12568 titlescreen_initial_first_default.fade_delay =
12569 title_initial_first_default.fade_delay;
12570 titlescreen_initial_first_default.post_delay =
12571 title_initial_first_default.post_delay;
12572 titlescreen_initial_first_default.auto_delay =
12573 title_initial_first_default.auto_delay;
12574 titlescreen_initial_first_default.auto_delay_unit =
12575 title_initial_first_default.auto_delay_unit;
12576 titlescreen_first_default.fade_mode = title_first_default.fade_mode;
12577 titlescreen_first_default.fade_delay = title_first_default.fade_delay;
12578 titlescreen_first_default.post_delay = title_first_default.post_delay;
12579 titlescreen_first_default.auto_delay = title_first_default.auto_delay;
12580 titlescreen_first_default.auto_delay_unit =
12581 title_first_default.auto_delay_unit;
12582 titlemessage_initial_first_default.fade_mode =
12583 title_initial_first_default.fade_mode;
12584 titlemessage_initial_first_default.fade_delay =
12585 title_initial_first_default.fade_delay;
12586 titlemessage_initial_first_default.post_delay =
12587 title_initial_first_default.post_delay;
12588 titlemessage_initial_first_default.auto_delay =
12589 title_initial_first_default.auto_delay;
12590 titlemessage_initial_first_default.auto_delay_unit =
12591 title_initial_first_default.auto_delay_unit;
12592 titlemessage_first_default.fade_mode = title_first_default.fade_mode;
12593 titlemessage_first_default.fade_delay = title_first_default.fade_delay;
12594 titlemessage_first_default.post_delay = title_first_default.post_delay;
12595 titlemessage_first_default.auto_delay = title_first_default.auto_delay;
12596 titlemessage_first_default.auto_delay_unit =
12597 title_first_default.auto_delay_unit;
12599 titlescreen_initial_default.fade_mode = title_initial_default.fade_mode;
12600 titlescreen_initial_default.fade_delay = title_initial_default.fade_delay;
12601 titlescreen_initial_default.post_delay = title_initial_default.post_delay;
12602 titlescreen_initial_default.auto_delay = title_initial_default.auto_delay;
12603 titlescreen_initial_default.auto_delay_unit =
12604 title_initial_default.auto_delay_unit;
12605 titlescreen_default.fade_mode = title_default.fade_mode;
12606 titlescreen_default.fade_delay = title_default.fade_delay;
12607 titlescreen_default.post_delay = title_default.post_delay;
12608 titlescreen_default.auto_delay = title_default.auto_delay;
12609 titlescreen_default.auto_delay_unit = title_default.auto_delay_unit;
12610 titlemessage_initial_default.fade_mode = title_initial_default.fade_mode;
12611 titlemessage_initial_default.fade_delay = title_initial_default.fade_delay;
12612 titlemessage_initial_default.post_delay = title_initial_default.post_delay;
12613 titlemessage_initial_default.auto_delay_unit =
12614 title_initial_default.auto_delay_unit;
12615 titlemessage_default.fade_mode = title_default.fade_mode;
12616 titlemessage_default.fade_delay = title_default.fade_delay;
12617 titlemessage_default.post_delay = title_default.post_delay;
12618 titlemessage_default.auto_delay = title_default.auto_delay;
12619 titlemessage_default.auto_delay_unit = title_default.auto_delay_unit;
12621 // special case: initialize "ARG_DEFAULT" values in static default config
12622 // (e.g., init "titlemessage_1.fade_mode" from "[titlemessage].fade_mode")
12623 for (i = 0; i < MAX_NUM_TITLE_MESSAGES; i++)
12625 titlescreen_initial_first[i] = titlescreen_initial_first_default;
12626 titlescreen_first[i] = titlescreen_first_default;
12627 titlemessage_initial_first[i] = titlemessage_initial_first_default;
12628 titlemessage_first[i] = titlemessage_first_default;
12630 titlescreen_initial[i] = titlescreen_initial_default;
12631 titlescreen[i] = titlescreen_default;
12632 titlemessage_initial[i] = titlemessage_initial_default;
12633 titlemessage[i] = titlemessage_default;
12636 // special case: initialize "ARG_DEFAULT" values in static default config
12637 // (eg, init "menu.enter_screen.SCORES.xyz" from "menu.enter_screen.xyz")
12638 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
12640 if (i == GFX_SPECIAL_ARG_TITLE) // title values already initialized
12643 menu.enter_screen[i] = menu.enter_screen[GFX_SPECIAL_ARG_DEFAULT];
12644 menu.leave_screen[i] = menu.leave_screen[GFX_SPECIAL_ARG_DEFAULT];
12645 menu.next_screen[i] = menu.next_screen[GFX_SPECIAL_ARG_DEFAULT];
12648 // special case: initialize "ARG_DEFAULT" values in static default config
12649 // (eg, init "viewport.door_1.MAIN.xyz" from "viewport.door_1.xyz")
12650 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
12652 viewport.window[i] = viewport.window[GFX_SPECIAL_ARG_DEFAULT];
12653 viewport.playfield[i] = viewport.playfield[GFX_SPECIAL_ARG_DEFAULT];
12654 viewport.door_1[i] = viewport.door_1[GFX_SPECIAL_ARG_DEFAULT];
12656 if (i == GFX_SPECIAL_ARG_EDITOR) // editor values already initialized
12659 viewport.door_2[i] = viewport.door_2[GFX_SPECIAL_ARG_DEFAULT];
12663 static void InitMenuDesignSettings_SpecialPostProcessing(void)
12667 struct XY *dst, *src;
12669 game_buttons_xy[] =
12671 { &game.button.save, &game.button.stop },
12672 { &game.button.pause2, &game.button.pause },
12673 { &game.button.load, &game.button.play },
12674 { &game.button.undo, &game.button.stop },
12675 { &game.button.redo, &game.button.play },
12681 // special case: initialize later added SETUP list size from LEVELS value
12682 if (menu.list_size[GAME_MODE_SETUP] == -1)
12683 menu.list_size[GAME_MODE_SETUP] = menu.list_size[GAME_MODE_LEVELS];
12685 // set default position for snapshot buttons to stop/pause/play buttons
12686 for (i = 0; game_buttons_xy[i].dst != NULL; i++)
12687 if ((*game_buttons_xy[i].dst).x == -1 &&
12688 (*game_buttons_xy[i].dst).y == -1)
12689 *game_buttons_xy[i].dst = *game_buttons_xy[i].src;
12691 // --------------------------------------------------------------------------
12692 // dynamic viewports (including playfield margins, borders and alignments)
12693 // --------------------------------------------------------------------------
12695 // dynamic viewports currently only supported for landscape mode
12696 int display_width = MAX(video.display_width, video.display_height);
12697 int display_height = MIN(video.display_width, video.display_height);
12699 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
12701 struct RectWithBorder *vp_window = &viewport.window[i];
12702 struct RectWithBorder *vp_playfield = &viewport.playfield[i];
12703 struct RectWithBorder *vp_door_1 = &viewport.door_1[i];
12704 struct RectWithBorder *vp_door_2 = &viewport.door_2[i];
12705 boolean dynamic_window_width = (vp_window->min_width != -1);
12706 boolean dynamic_window_height = (vp_window->min_height != -1);
12707 boolean dynamic_playfield_width = (vp_playfield->min_width != -1);
12708 boolean dynamic_playfield_height = (vp_playfield->min_height != -1);
12710 // adjust window size if min/max width/height is specified
12712 if (vp_window->min_width != -1)
12714 int window_width = display_width;
12716 // when using static window height, use aspect ratio of display
12717 if (vp_window->min_height == -1)
12718 window_width = vp_window->height * display_width / display_height;
12720 vp_window->width = MAX(vp_window->min_width, window_width);
12723 if (vp_window->min_height != -1)
12725 int window_height = display_height;
12727 // when using static window width, use aspect ratio of display
12728 if (vp_window->min_width == -1)
12729 window_height = vp_window->width * display_height / display_width;
12731 vp_window->height = MAX(vp_window->min_height, window_height);
12734 if (vp_window->max_width != -1)
12735 vp_window->width = MIN(vp_window->width, vp_window->max_width);
12737 if (vp_window->max_height != -1)
12738 vp_window->height = MIN(vp_window->height, vp_window->max_height);
12740 int playfield_width = vp_window->width;
12741 int playfield_height = vp_window->height;
12743 // adjust playfield size and position according to specified margins
12745 playfield_width -= vp_playfield->margin_left;
12746 playfield_width -= vp_playfield->margin_right;
12748 playfield_height -= vp_playfield->margin_top;
12749 playfield_height -= vp_playfield->margin_bottom;
12751 // adjust playfield size if min/max width/height is specified
12753 if (vp_playfield->min_width != -1)
12754 vp_playfield->width = MAX(vp_playfield->min_width, playfield_width);
12756 if (vp_playfield->min_height != -1)
12757 vp_playfield->height = MAX(vp_playfield->min_height, playfield_height);
12759 if (vp_playfield->max_width != -1)
12760 vp_playfield->width = MIN(vp_playfield->width, vp_playfield->max_width);
12762 if (vp_playfield->max_height != -1)
12763 vp_playfield->height = MIN(vp_playfield->height, vp_playfield->max_height);
12765 // adjust playfield position according to specified alignment
12767 if (vp_playfield->align == ALIGN_LEFT || vp_playfield->x > 0)
12768 vp_playfield->x = ALIGNED_VP_XPOS(vp_playfield);
12769 else if (vp_playfield->align == ALIGN_CENTER)
12770 vp_playfield->x = playfield_width / 2 - vp_playfield->width / 2;
12771 else if (vp_playfield->align == ALIGN_RIGHT)
12772 vp_playfield->x += playfield_width - vp_playfield->width;
12774 if (vp_playfield->valign == VALIGN_TOP || vp_playfield->y > 0)
12775 vp_playfield->y = ALIGNED_VP_YPOS(vp_playfield);
12776 else if (vp_playfield->valign == VALIGN_MIDDLE)
12777 vp_playfield->y = playfield_height / 2 - vp_playfield->height / 2;
12778 else if (vp_playfield->valign == VALIGN_BOTTOM)
12779 vp_playfield->y += playfield_height - vp_playfield->height;
12781 vp_playfield->x += vp_playfield->margin_left;
12782 vp_playfield->y += vp_playfield->margin_top;
12784 // adjust individual playfield borders if only default border is specified
12786 if (vp_playfield->border_left == -1)
12787 vp_playfield->border_left = vp_playfield->border_size;
12788 if (vp_playfield->border_right == -1)
12789 vp_playfield->border_right = vp_playfield->border_size;
12790 if (vp_playfield->border_top == -1)
12791 vp_playfield->border_top = vp_playfield->border_size;
12792 if (vp_playfield->border_bottom == -1)
12793 vp_playfield->border_bottom = vp_playfield->border_size;
12795 // set dynamic playfield borders if borders are specified as undefined
12796 // (but only if window size was dynamic and playfield size was static)
12798 if (dynamic_window_width && !dynamic_playfield_width)
12800 if (vp_playfield->border_left == -1)
12802 vp_playfield->border_left = (vp_playfield->x -
12803 vp_playfield->margin_left);
12804 vp_playfield->x -= vp_playfield->border_left;
12805 vp_playfield->width += vp_playfield->border_left;
12808 if (vp_playfield->border_right == -1)
12810 vp_playfield->border_right = (vp_window->width -
12812 vp_playfield->width -
12813 vp_playfield->margin_right);
12814 vp_playfield->width += vp_playfield->border_right;
12818 if (dynamic_window_height && !dynamic_playfield_height)
12820 if (vp_playfield->border_top == -1)
12822 vp_playfield->border_top = (vp_playfield->y -
12823 vp_playfield->margin_top);
12824 vp_playfield->y -= vp_playfield->border_top;
12825 vp_playfield->height += vp_playfield->border_top;
12828 if (vp_playfield->border_bottom == -1)
12830 vp_playfield->border_bottom = (vp_window->height -
12832 vp_playfield->height -
12833 vp_playfield->margin_bottom);
12834 vp_playfield->height += vp_playfield->border_bottom;
12838 // adjust playfield size to be a multiple of a defined alignment tile size
12840 int align_size = vp_playfield->align_size;
12841 int playfield_xtiles = vp_playfield->width / align_size;
12842 int playfield_ytiles = vp_playfield->height / align_size;
12843 int playfield_width_corrected = playfield_xtiles * align_size;
12844 int playfield_height_corrected = playfield_ytiles * align_size;
12845 boolean is_playfield_mode = (i == GFX_SPECIAL_ARG_PLAYING ||
12846 i == GFX_SPECIAL_ARG_EDITOR);
12848 if (is_playfield_mode &&
12849 dynamic_playfield_width &&
12850 vp_playfield->width != playfield_width_corrected)
12852 int playfield_xdiff = vp_playfield->width - playfield_width_corrected;
12854 vp_playfield->width = playfield_width_corrected;
12856 if (vp_playfield->align == ALIGN_LEFT)
12858 vp_playfield->border_left += playfield_xdiff;
12860 else if (vp_playfield->align == ALIGN_RIGHT)
12862 vp_playfield->border_right += playfield_xdiff;
12864 else if (vp_playfield->align == ALIGN_CENTER)
12866 int border_left_diff = playfield_xdiff / 2;
12867 int border_right_diff = playfield_xdiff - border_left_diff;
12869 vp_playfield->border_left += border_left_diff;
12870 vp_playfield->border_right += border_right_diff;
12874 if (is_playfield_mode &&
12875 dynamic_playfield_height &&
12876 vp_playfield->height != playfield_height_corrected)
12878 int playfield_ydiff = vp_playfield->height - playfield_height_corrected;
12880 vp_playfield->height = playfield_height_corrected;
12882 if (vp_playfield->valign == VALIGN_TOP)
12884 vp_playfield->border_top += playfield_ydiff;
12886 else if (vp_playfield->align == VALIGN_BOTTOM)
12888 vp_playfield->border_right += playfield_ydiff;
12890 else if (vp_playfield->align == VALIGN_MIDDLE)
12892 int border_top_diff = playfield_ydiff / 2;
12893 int border_bottom_diff = playfield_ydiff - border_top_diff;
12895 vp_playfield->border_top += border_top_diff;
12896 vp_playfield->border_bottom += border_bottom_diff;
12900 // adjust door positions according to specified alignment
12902 for (j = 0; j < 2; j++)
12904 struct RectWithBorder *vp_door = (j == 0 ? vp_door_1 : vp_door_2);
12906 if (vp_door->align == ALIGN_LEFT || vp_door->x > 0)
12907 vp_door->x = ALIGNED_VP_XPOS(vp_door);
12908 else if (vp_door->align == ALIGN_CENTER)
12909 vp_door->x = vp_window->width / 2 - vp_door->width / 2;
12910 else if (vp_door->align == ALIGN_RIGHT)
12911 vp_door->x += vp_window->width - vp_door->width;
12913 if (vp_door->valign == VALIGN_TOP || vp_door->y > 0)
12914 vp_door->y = ALIGNED_VP_YPOS(vp_door);
12915 else if (vp_door->valign == VALIGN_MIDDLE)
12916 vp_door->y = vp_window->height / 2 - vp_door->height / 2;
12917 else if (vp_door->valign == VALIGN_BOTTOM)
12918 vp_door->y += vp_window->height - vp_door->height;
12923 static void InitMenuDesignSettings_SpecialPostProcessing_AfterGraphics(void)
12927 struct XYTileSize *dst, *src;
12930 editor_buttons_xy[] =
12933 &editor.button.element_left, &editor.palette.element_left,
12934 IMG_GFX_EDITOR_BUTTON_ELEMENT_LEFT
12937 &editor.button.element_middle, &editor.palette.element_middle,
12938 IMG_GFX_EDITOR_BUTTON_ELEMENT_MIDDLE
12941 &editor.button.element_right, &editor.palette.element_right,
12942 IMG_GFX_EDITOR_BUTTON_ELEMENT_RIGHT
12949 // set default position for element buttons to element graphics
12950 for (i = 0; editor_buttons_xy[i].dst != NULL; i++)
12952 if ((*editor_buttons_xy[i].dst).x == -1 &&
12953 (*editor_buttons_xy[i].dst).y == -1)
12955 struct GraphicInfo *gd = &graphic_info[editor_buttons_xy[i].graphic];
12957 gd->width = gd->height = editor_buttons_xy[i].src->tile_size;
12959 *editor_buttons_xy[i].dst = *editor_buttons_xy[i].src;
12963 // adjust editor palette rows and columns if specified to be dynamic
12965 if (editor.palette.cols == -1)
12967 int vp_width = viewport.playfield[GFX_SPECIAL_ARG_EDITOR].width;
12968 int bt_width = graphic_info[IMG_EDITOR_PALETTE_BUTTON].width;
12969 int sc_width = graphic_info[IMG_EDITOR_PALETTE_SCROLLBAR].width;
12971 editor.palette.cols = (vp_width - sc_width) / bt_width;
12973 if (editor.palette.x == -1)
12975 int palette_width = editor.palette.cols * bt_width + sc_width;
12977 editor.palette.x = (vp_width - palette_width) / 2;
12981 if (editor.palette.rows == -1)
12983 int vp_height = viewport.playfield[GFX_SPECIAL_ARG_EDITOR].height;
12984 int bt_height = graphic_info[IMG_EDITOR_PALETTE_BUTTON].height;
12985 int tx_height = getFontHeight(FONT_TEXT_2);
12987 editor.palette.rows = (vp_height - tx_height) / bt_height;
12989 if (editor.palette.y == -1)
12991 int palette_height = editor.palette.rows * bt_height + tx_height;
12993 editor.palette.y = (vp_height - palette_height) / 2;
12998 static void InitMenuDesignSettings_PreviewPlayers_Ext(SetupFileHash *hash,
12999 boolean initialize)
13001 // special case: check if network and preview player positions are redefined,
13002 // to compare this later against the main menu level preview being redefined
13003 struct TokenIntPtrInfo menu_config_players[] =
13005 { "main.network_players.x", &menu.main.network_players.redefined },
13006 { "main.network_players.y", &menu.main.network_players.redefined },
13007 { "main.preview_players.x", &menu.main.preview_players.redefined },
13008 { "main.preview_players.y", &menu.main.preview_players.redefined },
13009 { "preview.x", &preview.redefined },
13010 { "preview.y", &preview.redefined }
13016 for (i = 0; i < ARRAY_SIZE(menu_config_players); i++)
13017 *menu_config_players[i].value = FALSE;
13021 for (i = 0; i < ARRAY_SIZE(menu_config_players); i++)
13022 if (getHashEntry(hash, menu_config_players[i].token) != NULL)
13023 *menu_config_players[i].value = TRUE;
13027 static void InitMenuDesignSettings_PreviewPlayers(void)
13029 InitMenuDesignSettings_PreviewPlayers_Ext(NULL, TRUE);
13032 static void InitMenuDesignSettings_PreviewPlayers_FromHash(SetupFileHash *hash)
13034 InitMenuDesignSettings_PreviewPlayers_Ext(hash, FALSE);
13037 static void LoadMenuDesignSettingsFromFilename(char *filename)
13039 static struct TitleFadingInfo tfi;
13040 static struct TitleMessageInfo tmi;
13041 static struct TokenInfo title_tokens[] =
13043 { TYPE_INTEGER, &tfi.fade_mode, ".fade_mode" },
13044 { TYPE_INTEGER, &tfi.fade_delay, ".fade_delay" },
13045 { TYPE_INTEGER, &tfi.post_delay, ".post_delay" },
13046 { TYPE_INTEGER, &tfi.auto_delay, ".auto_delay" },
13047 { TYPE_INTEGER, &tfi.auto_delay_unit, ".auto_delay_unit" },
13051 static struct TokenInfo titlemessage_tokens[] =
13053 { TYPE_INTEGER, &tmi.x, ".x" },
13054 { TYPE_INTEGER, &tmi.y, ".y" },
13055 { TYPE_INTEGER, &tmi.width, ".width" },
13056 { TYPE_INTEGER, &tmi.height, ".height" },
13057 { TYPE_INTEGER, &tmi.chars, ".chars" },
13058 { TYPE_INTEGER, &tmi.lines, ".lines" },
13059 { TYPE_INTEGER, &tmi.align, ".align" },
13060 { TYPE_INTEGER, &tmi.valign, ".valign" },
13061 { TYPE_INTEGER, &tmi.font, ".font" },
13062 { TYPE_BOOLEAN, &tmi.autowrap, ".autowrap" },
13063 { TYPE_BOOLEAN, &tmi.centered, ".centered" },
13064 { TYPE_BOOLEAN, &tmi.parse_comments, ".parse_comments" },
13065 { TYPE_INTEGER, &tmi.sort_priority, ".sort_priority" },
13066 { TYPE_INTEGER, &tmi.fade_mode, ".fade_mode" },
13067 { TYPE_INTEGER, &tmi.fade_delay, ".fade_delay" },
13068 { TYPE_INTEGER, &tmi.post_delay, ".post_delay" },
13069 { TYPE_INTEGER, &tmi.auto_delay, ".auto_delay" },
13070 { TYPE_INTEGER, &tmi.auto_delay_unit, ".auto_delay_unit" },
13076 struct TitleFadingInfo *info;
13081 // initialize first titles from "enter screen" definitions, if defined
13082 { &title_initial_first_default, "menu.enter_screen.TITLE" },
13083 { &title_first_default, "menu.enter_screen.TITLE" },
13085 // initialize title screens from "next screen" definitions, if defined
13086 { &title_initial_default, "menu.next_screen.TITLE" },
13087 { &title_default, "menu.next_screen.TITLE" },
13093 struct TitleMessageInfo *array;
13096 titlemessage_arrays[] =
13098 // initialize first titles from "enter screen" definitions, if defined
13099 { titlescreen_initial_first, "menu.enter_screen.TITLE" },
13100 { titlescreen_first, "menu.enter_screen.TITLE" },
13101 { titlemessage_initial_first, "menu.enter_screen.TITLE" },
13102 { titlemessage_first, "menu.enter_screen.TITLE" },
13104 // initialize titles from "next screen" definitions, if defined
13105 { titlescreen_initial, "menu.next_screen.TITLE" },
13106 { titlescreen, "menu.next_screen.TITLE" },
13107 { titlemessage_initial, "menu.next_screen.TITLE" },
13108 { titlemessage, "menu.next_screen.TITLE" },
13110 // overwrite titles with title definitions, if defined
13111 { titlescreen_initial_first, "[title_initial]" },
13112 { titlescreen_first, "[title]" },
13113 { titlemessage_initial_first, "[title_initial]" },
13114 { titlemessage_first, "[title]" },
13116 { titlescreen_initial, "[title_initial]" },
13117 { titlescreen, "[title]" },
13118 { titlemessage_initial, "[title_initial]" },
13119 { titlemessage, "[title]" },
13121 // overwrite titles with title screen/message definitions, if defined
13122 { titlescreen_initial_first, "[titlescreen_initial]" },
13123 { titlescreen_first, "[titlescreen]" },
13124 { titlemessage_initial_first, "[titlemessage_initial]" },
13125 { titlemessage_first, "[titlemessage]" },
13127 { titlescreen_initial, "[titlescreen_initial]" },
13128 { titlescreen, "[titlescreen]" },
13129 { titlemessage_initial, "[titlemessage_initial]" },
13130 { titlemessage, "[titlemessage]" },
13134 SetupFileHash *setup_file_hash;
13137 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
13140 // the following initializes hierarchical values from dynamic configuration
13142 // special case: initialize with default values that may be overwritten
13143 // (e.g., init "menu.draw_xoffset.INFO" from "menu.draw_xoffset")
13144 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
13146 struct TokenIntPtrInfo menu_config[] =
13148 { "menu.draw_xoffset", &menu.draw_xoffset[i] },
13149 { "menu.draw_yoffset", &menu.draw_yoffset[i] },
13150 { "menu.list_size", &menu.list_size[i] }
13153 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
13155 char *token = menu_config[j].token;
13156 char *value = getHashEntry(setup_file_hash, token);
13159 *menu_config[j].value = get_integer_from_string(value);
13163 // special case: initialize with default values that may be overwritten
13164 // (eg, init "menu.draw_xoffset.INFO[XXX]" from "menu.draw_xoffset.INFO")
13165 for (i = 0; i < NUM_SPECIAL_GFX_INFO_ARGS; i++)
13167 struct TokenIntPtrInfo menu_config[] =
13169 { "menu.draw_xoffset.INFO", &menu.draw_xoffset_info[i] },
13170 { "menu.draw_yoffset.INFO", &menu.draw_yoffset_info[i] },
13171 { "menu.list_size.INFO", &menu.list_size_info[i] },
13172 { "menu.list_entry_size.INFO", &menu.list_entry_size_info[i] },
13173 { "menu.tile_size.INFO", &menu.tile_size_info[i] }
13176 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
13178 char *token = menu_config[j].token;
13179 char *value = getHashEntry(setup_file_hash, token);
13182 *menu_config[j].value = get_integer_from_string(value);
13186 // special case: initialize with default values that may be overwritten
13187 // (eg, init "menu.draw_xoffset.SETUP[XXX]" from "menu.draw_xoffset.SETUP")
13188 for (i = 0; i < NUM_SPECIAL_GFX_SETUP_ARGS; i++)
13190 struct TokenIntPtrInfo menu_config[] =
13192 { "menu.draw_xoffset.SETUP", &menu.draw_xoffset_setup[i] },
13193 { "menu.draw_yoffset.SETUP", &menu.draw_yoffset_setup[i] }
13196 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
13198 char *token = menu_config[j].token;
13199 char *value = getHashEntry(setup_file_hash, token);
13202 *menu_config[j].value = get_integer_from_string(value);
13206 // special case: initialize with default values that may be overwritten
13207 // (eg, init "menu.line_spacing.INFO[XXX]" from "menu.line_spacing.INFO")
13208 for (i = 0; i < NUM_SPECIAL_GFX_INFO_ARGS; i++)
13210 struct TokenIntPtrInfo menu_config[] =
13212 { "menu.left_spacing.INFO", &menu.left_spacing_info[i] },
13213 { "menu.middle_spacing.INFO", &menu.middle_spacing_info[i] },
13214 { "menu.right_spacing.INFO", &menu.right_spacing_info[i] },
13215 { "menu.top_spacing.INFO", &menu.top_spacing_info[i] },
13216 { "menu.bottom_spacing.INFO", &menu.bottom_spacing_info[i] },
13217 { "menu.paragraph_spacing.INFO", &menu.paragraph_spacing_info[i] },
13218 { "menu.headline1_spacing.INFO", &menu.headline1_spacing_info[i] },
13219 { "menu.headline2_spacing.INFO", &menu.headline2_spacing_info[i] },
13220 { "menu.line_spacing.INFO", &menu.line_spacing_info[i] },
13221 { "menu.extra_spacing.INFO", &menu.extra_spacing_info[i] },
13224 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
13226 char *token = menu_config[j].token;
13227 char *value = getHashEntry(setup_file_hash, token);
13230 *menu_config[j].value = get_integer_from_string(value);
13234 // special case: initialize with default values that may be overwritten
13235 // (eg, init "menu.enter_screen.SCORES.xyz" from "menu.enter_screen.xyz")
13236 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
13238 struct TokenIntPtrInfo menu_config[] =
13240 { "menu.enter_screen.fade_mode", &menu.enter_screen[i].fade_mode },
13241 { "menu.enter_screen.fade_delay", &menu.enter_screen[i].fade_delay },
13242 { "menu.enter_screen.post_delay", &menu.enter_screen[i].post_delay },
13243 { "menu.leave_screen.fade_mode", &menu.leave_screen[i].fade_mode },
13244 { "menu.leave_screen.fade_delay", &menu.leave_screen[i].fade_delay },
13245 { "menu.leave_screen.post_delay", &menu.leave_screen[i].post_delay },
13246 { "menu.next_screen.fade_mode", &menu.next_screen[i].fade_mode },
13247 { "menu.next_screen.fade_delay", &menu.next_screen[i].fade_delay },
13248 { "menu.next_screen.post_delay", &menu.next_screen[i].post_delay }
13251 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
13253 char *token = menu_config[j].token;
13254 char *value = getHashEntry(setup_file_hash, token);
13257 *menu_config[j].value = get_token_parameter_value(token, value);
13261 // special case: initialize with default values that may be overwritten
13262 // (eg, init "viewport.door_1.MAIN.xyz" from "viewport.door_1.xyz")
13263 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
13267 char *token_prefix;
13268 struct RectWithBorder *struct_ptr;
13272 { "viewport.window", &viewport.window[i] },
13273 { "viewport.playfield", &viewport.playfield[i] },
13274 { "viewport.door_1", &viewport.door_1[i] },
13275 { "viewport.door_2", &viewport.door_2[i] }
13278 for (j = 0; j < ARRAY_SIZE(vp_struct); j++)
13280 struct TokenIntPtrInfo vp_config[] =
13282 { ".x", &vp_struct[j].struct_ptr->x },
13283 { ".y", &vp_struct[j].struct_ptr->y },
13284 { ".width", &vp_struct[j].struct_ptr->width },
13285 { ".height", &vp_struct[j].struct_ptr->height },
13286 { ".min_width", &vp_struct[j].struct_ptr->min_width },
13287 { ".min_height", &vp_struct[j].struct_ptr->min_height },
13288 { ".max_width", &vp_struct[j].struct_ptr->max_width },
13289 { ".max_height", &vp_struct[j].struct_ptr->max_height },
13290 { ".margin_left", &vp_struct[j].struct_ptr->margin_left },
13291 { ".margin_right", &vp_struct[j].struct_ptr->margin_right },
13292 { ".margin_top", &vp_struct[j].struct_ptr->margin_top },
13293 { ".margin_bottom", &vp_struct[j].struct_ptr->margin_bottom },
13294 { ".border_left", &vp_struct[j].struct_ptr->border_left },
13295 { ".border_right", &vp_struct[j].struct_ptr->border_right },
13296 { ".border_top", &vp_struct[j].struct_ptr->border_top },
13297 { ".border_bottom", &vp_struct[j].struct_ptr->border_bottom },
13298 { ".border_size", &vp_struct[j].struct_ptr->border_size },
13299 { ".align_size", &vp_struct[j].struct_ptr->align_size },
13300 { ".align", &vp_struct[j].struct_ptr->align },
13301 { ".valign", &vp_struct[j].struct_ptr->valign }
13304 for (k = 0; k < ARRAY_SIZE(vp_config); k++)
13306 char *token = getStringCat2(vp_struct[j].token_prefix,
13307 vp_config[k].token);
13308 char *value = getHashEntry(setup_file_hash, token);
13311 *vp_config[k].value = get_token_parameter_value(token, value);
13318 // special case: initialize with default values that may be overwritten
13319 // (e.g., init "[title].fade_mode" from "menu.next_screen.TITLE.fade_mode")
13320 for (i = 0; title_info[i].info != NULL; i++)
13322 struct TitleFadingInfo *info = title_info[i].info;
13323 char *base_token = title_info[i].text;
13325 for (j = 0; title_tokens[j].type != -1; j++)
13327 char *token = getStringCat2(base_token, title_tokens[j].text);
13328 char *value = getHashEntry(setup_file_hash, token);
13332 int parameter_value = get_token_parameter_value(token, value);
13336 *(int *)title_tokens[j].value = (int)parameter_value;
13345 // special case: initialize with default values that may be overwritten
13346 // (e.g., init "titlemessage_1.fade_mode" from "[titlemessage].fade_mode")
13347 for (i = 0; titlemessage_arrays[i].array != NULL; i++)
13349 struct TitleMessageInfo *array = titlemessage_arrays[i].array;
13350 char *base_token = titlemessage_arrays[i].text;
13352 for (j = 0; titlemessage_tokens[j].type != -1; j++)
13354 char *token = getStringCat2(base_token, titlemessage_tokens[j].text);
13355 char *value = getHashEntry(setup_file_hash, token);
13359 int parameter_value = get_token_parameter_value(token, value);
13361 for (k = 0; k < MAX_NUM_TITLE_MESSAGES; k++)
13365 if (titlemessage_tokens[j].type == TYPE_INTEGER)
13366 *(int *)titlemessage_tokens[j].value = (int)parameter_value;
13368 *(boolean *)titlemessage_tokens[j].value = (boolean)parameter_value;
13378 // read (and overwrite with) values that may be specified in config file
13379 InitMenuDesignSettings_FromHash(setup_file_hash, TRUE);
13381 // special case: check if network and preview player positions are redefined
13382 InitMenuDesignSettings_PreviewPlayers_FromHash(setup_file_hash);
13384 freeSetupFileHash(setup_file_hash);
13387 void LoadMenuDesignSettings(void)
13389 char *filename_base = UNDEFINED_FILENAME, *filename_local;
13391 InitMenuDesignSettings_Static();
13392 InitMenuDesignSettings_SpecialPreProcessing();
13393 InitMenuDesignSettings_PreviewPlayers();
13395 if (!GFX_OVERRIDE_ARTWORK(ARTWORK_TYPE_GRAPHICS))
13397 // first look for special settings configured in level series config
13398 filename_base = getCustomArtworkLevelConfigFilename(ARTWORK_TYPE_GRAPHICS);
13400 if (fileExists(filename_base))
13401 LoadMenuDesignSettingsFromFilename(filename_base);
13404 filename_local = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
13406 if (filename_local != NULL && !strEqual(filename_base, filename_local))
13407 LoadMenuDesignSettingsFromFilename(filename_local);
13409 InitMenuDesignSettings_SpecialPostProcessing();
13412 void LoadMenuDesignSettings_AfterGraphics(void)
13414 InitMenuDesignSettings_SpecialPostProcessing_AfterGraphics();
13417 void InitSoundSettings_FromHash(SetupFileHash *setup_file_hash,
13418 boolean ignore_defaults)
13422 for (i = 0; sound_config_vars[i].token != NULL; i++)
13424 char *value = getHashEntry(setup_file_hash, sound_config_vars[i].token);
13426 // (ignore definitions set to "[DEFAULT]" which are already initialized)
13427 if (ignore_defaults && strEqual(value, ARG_DEFAULT))
13431 *sound_config_vars[i].value =
13432 get_token_parameter_value(sound_config_vars[i].token, value);
13436 void InitSoundSettings_Static(void)
13438 // always start with reliable default values from static default config
13439 InitSoundSettings_FromHash(sound_config_hash, FALSE);
13442 static void LoadSoundSettingsFromFilename(char *filename)
13444 SetupFileHash *setup_file_hash;
13446 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
13449 // read (and overwrite with) values that may be specified in config file
13450 InitSoundSettings_FromHash(setup_file_hash, TRUE);
13452 freeSetupFileHash(setup_file_hash);
13455 void LoadSoundSettings(void)
13457 char *filename_base = UNDEFINED_FILENAME, *filename_local;
13459 InitSoundSettings_Static();
13461 if (!GFX_OVERRIDE_ARTWORK(ARTWORK_TYPE_SOUNDS))
13463 // first look for special settings configured in level series config
13464 filename_base = getCustomArtworkLevelConfigFilename(ARTWORK_TYPE_SOUNDS);
13466 if (fileExists(filename_base))
13467 LoadSoundSettingsFromFilename(filename_base);
13470 filename_local = getCustomArtworkConfigFilename(ARTWORK_TYPE_SOUNDS);
13472 if (filename_local != NULL && !strEqual(filename_base, filename_local))
13473 LoadSoundSettingsFromFilename(filename_local);
13476 void LoadUserDefinedEditorElementList(int **elements, int *num_elements)
13478 char *filename = getEditorSetupFilename();
13479 SetupFileList *setup_file_list, *list;
13480 SetupFileHash *element_hash;
13481 int num_unknown_tokens = 0;
13484 if ((setup_file_list = loadSetupFileList(filename)) == NULL)
13487 element_hash = newSetupFileHash();
13489 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
13490 setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
13492 // determined size may be larger than needed (due to unknown elements)
13494 for (list = setup_file_list; list != NULL; list = list->next)
13497 // add space for up to 3 more elements for padding that may be needed
13498 *num_elements += 3;
13500 // free memory for old list of elements, if needed
13501 checked_free(*elements);
13503 // allocate memory for new list of elements
13504 *elements = checked_malloc(*num_elements * sizeof(int));
13507 for (list = setup_file_list; list != NULL; list = list->next)
13509 char *value = getHashEntry(element_hash, list->token);
13511 if (value == NULL) // try to find obsolete token mapping
13513 char *mapped_token = get_mapped_token(list->token);
13515 if (mapped_token != NULL)
13517 value = getHashEntry(element_hash, mapped_token);
13519 free(mapped_token);
13525 (*elements)[(*num_elements)++] = atoi(value);
13529 if (num_unknown_tokens == 0)
13532 Warn("unknown token(s) found in config file:");
13533 Warn("- config file: '%s'", filename);
13535 num_unknown_tokens++;
13538 Warn("- token: '%s'", list->token);
13542 if (num_unknown_tokens > 0)
13545 while (*num_elements % 4) // pad with empty elements, if needed
13546 (*elements)[(*num_elements)++] = EL_EMPTY;
13548 freeSetupFileList(setup_file_list);
13549 freeSetupFileHash(element_hash);
13552 for (i = 0; i < *num_elements; i++)
13553 Debug("editor", "element '%s' [%d]\n",
13554 element_info[(*elements)[i]].token_name, (*elements)[i]);
13558 static struct MusicFileInfo *get_music_file_info_ext(char *basename, int music,
13561 SetupFileHash *setup_file_hash = NULL;
13562 struct MusicFileInfo tmp_music_file_info, *new_music_file_info;
13563 char *filename_music, *filename_prefix, *filename_info;
13569 token_to_value_ptr[] =
13571 { "title_header", &tmp_music_file_info.title_header },
13572 { "artist_header", &tmp_music_file_info.artist_header },
13573 { "album_header", &tmp_music_file_info.album_header },
13574 { "year_header", &tmp_music_file_info.year_header },
13575 { "played_header", &tmp_music_file_info.played_header },
13577 { "title", &tmp_music_file_info.title },
13578 { "artist", &tmp_music_file_info.artist },
13579 { "album", &tmp_music_file_info.album },
13580 { "year", &tmp_music_file_info.year },
13581 { "played", &tmp_music_file_info.played },
13587 filename_music = (is_sound ? getCustomSoundFilename(basename) :
13588 getCustomMusicFilename(basename));
13590 if (filename_music == NULL)
13593 // ---------- try to replace file extension ----------
13595 filename_prefix = getStringCopy(filename_music);
13596 if (strrchr(filename_prefix, '.') != NULL)
13597 *strrchr(filename_prefix, '.') = '\0';
13598 filename_info = getStringCat2(filename_prefix, ".txt");
13600 if (fileExists(filename_info))
13601 setup_file_hash = loadSetupFileHash(filename_info);
13603 free(filename_prefix);
13604 free(filename_info);
13606 if (setup_file_hash == NULL)
13608 // ---------- try to add file extension ----------
13610 filename_prefix = getStringCopy(filename_music);
13611 filename_info = getStringCat2(filename_prefix, ".txt");
13613 if (fileExists(filename_info))
13614 setup_file_hash = loadSetupFileHash(filename_info);
13616 free(filename_prefix);
13617 free(filename_info);
13620 if (setup_file_hash == NULL)
13623 // ---------- music file info found ----------
13625 clear_mem(&tmp_music_file_info, sizeof(struct MusicFileInfo));
13627 for (i = 0; token_to_value_ptr[i].token != NULL; i++)
13629 char *value = getHashEntry(setup_file_hash, token_to_value_ptr[i].token);
13631 *token_to_value_ptr[i].value_ptr =
13632 getStringCopy(value != NULL && *value != '\0' ? value : UNKNOWN_NAME);
13635 tmp_music_file_info.basename = getStringCopy(basename);
13636 tmp_music_file_info.music = music;
13637 tmp_music_file_info.is_sound = is_sound;
13639 new_music_file_info = checked_malloc(sizeof(struct MusicFileInfo));
13640 *new_music_file_info = tmp_music_file_info;
13642 return new_music_file_info;
13645 static struct MusicFileInfo *get_music_file_info(char *basename, int music)
13647 return get_music_file_info_ext(basename, music, FALSE);
13650 static struct MusicFileInfo *get_sound_file_info(char *basename, int sound)
13652 return get_music_file_info_ext(basename, sound, TRUE);
13655 static boolean music_info_listed_ext(struct MusicFileInfo *list,
13656 char *basename, boolean is_sound)
13658 for (; list != NULL; list = list->next)
13659 if (list->is_sound == is_sound && strEqual(list->basename, basename))
13665 static boolean music_info_listed(struct MusicFileInfo *list, char *basename)
13667 return music_info_listed_ext(list, basename, FALSE);
13670 static boolean sound_info_listed(struct MusicFileInfo *list, char *basename)
13672 return music_info_listed_ext(list, basename, TRUE);
13675 void LoadMusicInfo(void)
13677 int num_music_noconf = getMusicListSize_NoConf();
13678 int num_music = getMusicListSize();
13679 int num_sounds = getSoundListSize();
13680 struct FileInfo *music, *sound;
13681 struct MusicFileInfo *next, **new;
13685 while (music_file_info != NULL)
13687 next = music_file_info->next;
13689 checked_free(music_file_info->basename);
13691 checked_free(music_file_info->title_header);
13692 checked_free(music_file_info->artist_header);
13693 checked_free(music_file_info->album_header);
13694 checked_free(music_file_info->year_header);
13695 checked_free(music_file_info->played_header);
13697 checked_free(music_file_info->title);
13698 checked_free(music_file_info->artist);
13699 checked_free(music_file_info->album);
13700 checked_free(music_file_info->year);
13701 checked_free(music_file_info->played);
13703 free(music_file_info);
13705 music_file_info = next;
13708 new = &music_file_info;
13710 // get (configured or unconfigured) music file info for all levels
13711 for (i = leveldir_current->first_level;
13712 i <= leveldir_current->last_level; i++)
13716 if (levelset.music[i] != MUS_UNDEFINED)
13718 // get music file info for configured level music
13719 music_nr = levelset.music[i];
13721 else if (num_music_noconf > 0)
13723 // get music file info for unconfigured level music
13724 int level_pos = i - leveldir_current->first_level;
13726 music_nr = MAP_NOCONF_MUSIC(level_pos % num_music_noconf);
13733 char *basename = getMusicInfoEntryFilename(music_nr);
13735 if (basename == NULL)
13738 if (!music_info_listed(music_file_info, basename))
13740 *new = get_music_file_info(basename, music_nr);
13743 new = &(*new)->next;
13747 // get music file info for all remaining configured music files
13748 for (i = 0; i < num_music; i++)
13750 music = getMusicListEntry(i);
13752 if (music->filename == NULL)
13755 if (strEqual(music->filename, UNDEFINED_FILENAME))
13758 // a configured file may be not recognized as music
13759 if (!FileIsMusic(music->filename))
13762 if (!music_info_listed(music_file_info, music->filename))
13764 *new = get_music_file_info(music->filename, i);
13767 new = &(*new)->next;
13771 // get sound file info for all configured sound files
13772 for (i = 0; i < num_sounds; i++)
13774 sound = getSoundListEntry(i);
13776 if (sound->filename == NULL)
13779 if (strEqual(sound->filename, UNDEFINED_FILENAME))
13782 // a configured file may be not recognized as sound
13783 if (!FileIsSound(sound->filename))
13786 if (!sound_info_listed(music_file_info, sound->filename))
13788 *new = get_sound_file_info(sound->filename, i);
13790 new = &(*new)->next;
13794 // add pointers to previous list nodes
13796 struct MusicFileInfo *node = music_file_info;
13798 while (node != NULL)
13801 node->next->prev = node;
13807 static void add_helpanim_entry(int element, int action, int direction,
13808 int delay, int *num_list_entries)
13810 struct HelpAnimInfo *new_list_entry;
13811 (*num_list_entries)++;
13814 checked_realloc(helpanim_info,
13815 *num_list_entries * sizeof(struct HelpAnimInfo));
13816 new_list_entry = &helpanim_info[*num_list_entries - 1];
13818 new_list_entry->element = element;
13819 new_list_entry->action = action;
13820 new_list_entry->direction = direction;
13821 new_list_entry->delay = delay;
13824 static void print_unknown_token(char *filename, char *token, int token_nr)
13829 Warn("unknown token(s) found in config file:");
13830 Warn("- config file: '%s'", filename);
13833 Warn("- token: '%s'", token);
13836 static void print_unknown_token_end(int token_nr)
13842 void LoadHelpAnimInfo(void)
13844 char *filename = getHelpAnimFilename();
13845 SetupFileList *setup_file_list = NULL, *list;
13846 SetupFileHash *element_hash, *action_hash, *direction_hash;
13847 int num_list_entries = 0;
13848 int num_unknown_tokens = 0;
13851 if (fileExists(filename))
13852 setup_file_list = loadSetupFileList(filename);
13854 if (setup_file_list == NULL)
13856 // use reliable default values from static configuration
13857 SetupFileList *insert_ptr;
13859 insert_ptr = setup_file_list =
13860 newSetupFileList(helpanim_config[0].token,
13861 helpanim_config[0].value);
13863 for (i = 1; helpanim_config[i].token; i++)
13864 insert_ptr = addListEntry(insert_ptr,
13865 helpanim_config[i].token,
13866 helpanim_config[i].value);
13869 element_hash = newSetupFileHash();
13870 action_hash = newSetupFileHash();
13871 direction_hash = newSetupFileHash();
13873 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
13874 setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
13876 for (i = 0; i < NUM_ACTIONS; i++)
13877 setHashEntry(action_hash, element_action_info[i].suffix,
13878 i_to_a(element_action_info[i].value));
13880 // do not store direction index (bit) here, but direction value!
13881 for (i = 0; i < NUM_DIRECTIONS_FULL; i++)
13882 setHashEntry(direction_hash, element_direction_info[i].suffix,
13883 i_to_a(1 << element_direction_info[i].value));
13885 for (list = setup_file_list; list != NULL; list = list->next)
13887 char *element_token, *action_token, *direction_token;
13888 char *element_value, *action_value, *direction_value;
13889 int delay = atoi(list->value);
13891 if (strEqual(list->token, "end"))
13893 add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
13898 /* first try to break element into element/action/direction parts;
13899 if this does not work, also accept combined "element[.act][.dir]"
13900 elements (like "dynamite.active"), which are unique elements */
13902 if (strchr(list->token, '.') == NULL) // token contains no '.'
13904 element_value = getHashEntry(element_hash, list->token);
13905 if (element_value != NULL) // element found
13906 add_helpanim_entry(atoi(element_value), -1, -1, delay,
13907 &num_list_entries);
13910 // no further suffixes found -- this is not an element
13911 print_unknown_token(filename, list->token, num_unknown_tokens++);
13917 // token has format "<prefix>.<something>"
13919 action_token = strchr(list->token, '.'); // suffix may be action ...
13920 direction_token = action_token; // ... or direction
13922 element_token = getStringCopy(list->token);
13923 *strchr(element_token, '.') = '\0';
13925 element_value = getHashEntry(element_hash, element_token);
13927 if (element_value == NULL) // this is no element
13929 element_value = getHashEntry(element_hash, list->token);
13930 if (element_value != NULL) // combined element found
13931 add_helpanim_entry(atoi(element_value), -1, -1, delay,
13932 &num_list_entries);
13934 print_unknown_token(filename, list->token, num_unknown_tokens++);
13936 free(element_token);
13941 action_value = getHashEntry(action_hash, action_token);
13943 if (action_value != NULL) // action found
13945 add_helpanim_entry(atoi(element_value), atoi(action_value), -1, delay,
13946 &num_list_entries);
13948 free(element_token);
13953 direction_value = getHashEntry(direction_hash, direction_token);
13955 if (direction_value != NULL) // direction found
13957 add_helpanim_entry(atoi(element_value), -1, atoi(direction_value), delay,
13958 &num_list_entries);
13960 free(element_token);
13965 if (strchr(action_token + 1, '.') == NULL)
13967 // no further suffixes found -- this is not an action nor direction
13969 element_value = getHashEntry(element_hash, list->token);
13970 if (element_value != NULL) // combined element found
13971 add_helpanim_entry(atoi(element_value), -1, -1, delay,
13972 &num_list_entries);
13974 print_unknown_token(filename, list->token, num_unknown_tokens++);
13976 free(element_token);
13981 // token has format "<prefix>.<suffix>.<something>"
13983 direction_token = strchr(action_token + 1, '.');
13985 action_token = getStringCopy(action_token);
13986 *strchr(action_token + 1, '.') = '\0';
13988 action_value = getHashEntry(action_hash, action_token);
13990 if (action_value == NULL) // this is no action
13992 element_value = getHashEntry(element_hash, list->token);
13993 if (element_value != NULL) // combined element found
13994 add_helpanim_entry(atoi(element_value), -1, -1, delay,
13995 &num_list_entries);
13997 print_unknown_token(filename, list->token, num_unknown_tokens++);
13999 free(element_token);
14000 free(action_token);
14005 direction_value = getHashEntry(direction_hash, direction_token);
14007 if (direction_value != NULL) // direction found
14009 add_helpanim_entry(atoi(element_value), atoi(action_value),
14010 atoi(direction_value), delay, &num_list_entries);
14012 free(element_token);
14013 free(action_token);
14018 // this is no direction
14020 element_value = getHashEntry(element_hash, list->token);
14021 if (element_value != NULL) // combined element found
14022 add_helpanim_entry(atoi(element_value), -1, -1, delay,
14023 &num_list_entries);
14025 print_unknown_token(filename, list->token, num_unknown_tokens++);
14027 free(element_token);
14028 free(action_token);
14031 print_unknown_token_end(num_unknown_tokens);
14033 add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
14034 add_helpanim_entry(HELPANIM_LIST_END, -1, -1, -1, &num_list_entries);
14036 freeSetupFileList(setup_file_list);
14037 freeSetupFileHash(element_hash);
14038 freeSetupFileHash(action_hash);
14039 freeSetupFileHash(direction_hash);
14042 for (i = 0; i < num_list_entries; i++)
14043 Debug("files:LoadHelpAnimInfo", "'%s': %d, %d, %d => %d",
14044 EL_NAME(helpanim_info[i].element),
14045 helpanim_info[i].element,
14046 helpanim_info[i].action,
14047 helpanim_info[i].direction,
14048 helpanim_info[i].delay);
14052 void LoadHelpTextInfo(void)
14054 char *filename = getHelpTextFilename();
14057 if (helptext_info != NULL)
14059 freeSetupFileHash(helptext_info);
14060 helptext_info = NULL;
14063 if (fileExists(filename))
14064 helptext_info = loadSetupFileHash(filename);
14066 if (helptext_info == NULL)
14068 // use reliable default values from static configuration
14069 helptext_info = newSetupFileHash();
14071 for (i = 0; helptext_config[i].token; i++)
14072 setHashEntry(helptext_info,
14073 helptext_config[i].token,
14074 helptext_config[i].value);
14078 BEGIN_HASH_ITERATION(helptext_info, itr)
14080 Debug("files:LoadHelpTextInfo", "'%s' => '%s'",
14081 HASH_ITERATION_TOKEN(itr), HASH_ITERATION_VALUE(itr));
14083 END_HASH_ITERATION(hash, itr)
14088 // ----------------------------------------------------------------------------
14090 // ----------------------------------------------------------------------------
14092 #define MAX_NUM_CONVERT_LEVELS 1000
14094 void ConvertLevels(void)
14096 static LevelDirTree *convert_leveldir = NULL;
14097 static int convert_level_nr = -1;
14098 static int num_levels_handled = 0;
14099 static int num_levels_converted = 0;
14100 static boolean levels_failed[MAX_NUM_CONVERT_LEVELS];
14103 convert_leveldir = getTreeInfoFromIdentifier(leveldir_first,
14104 global.convert_leveldir);
14106 if (convert_leveldir == NULL)
14107 Fail("no such level identifier: '%s'", global.convert_leveldir);
14109 leveldir_current = convert_leveldir;
14111 if (global.convert_level_nr != -1)
14113 convert_leveldir->first_level = global.convert_level_nr;
14114 convert_leveldir->last_level = global.convert_level_nr;
14117 convert_level_nr = convert_leveldir->first_level;
14119 PrintLine("=", 79);
14120 Print("Converting levels\n");
14121 PrintLine("-", 79);
14122 Print("Level series identifier: '%s'\n", convert_leveldir->identifier);
14123 Print("Level series name: '%s'\n", convert_leveldir->name);
14124 Print("Level series author: '%s'\n", convert_leveldir->author);
14125 Print("Number of levels: %d\n", convert_leveldir->levels);
14126 PrintLine("=", 79);
14129 for (i = 0; i < MAX_NUM_CONVERT_LEVELS; i++)
14130 levels_failed[i] = FALSE;
14132 while (convert_level_nr <= convert_leveldir->last_level)
14134 char *level_filename;
14137 level_nr = convert_level_nr++;
14139 Print("Level %03d: ", level_nr);
14141 LoadLevel(level_nr);
14142 if (level.no_level_file || level.no_valid_file)
14144 Print("(no level)\n");
14148 Print("converting level ... ");
14151 // special case: conversion of some EMC levels as requested by ACME
14152 level.game_engine_type = GAME_ENGINE_TYPE_RND;
14155 level_filename = getDefaultLevelFilename(level_nr);
14156 new_level = !fileExists(level_filename);
14160 SaveLevel(level_nr);
14162 num_levels_converted++;
14164 Print("converted.\n");
14168 if (level_nr >= 0 && level_nr < MAX_NUM_CONVERT_LEVELS)
14169 levels_failed[level_nr] = TRUE;
14171 Print("NOT CONVERTED -- LEVEL ALREADY EXISTS.\n");
14174 num_levels_handled++;
14178 PrintLine("=", 79);
14179 Print("Number of levels handled: %d\n", num_levels_handled);
14180 Print("Number of levels converted: %d (%d%%)\n", num_levels_converted,
14181 (num_levels_handled ?
14182 num_levels_converted * 100 / num_levels_handled : 0));
14183 PrintLine("-", 79);
14184 Print("Summary (for automatic parsing by scripts):\n");
14185 Print("LEVELDIR '%s', CONVERTED %d/%d (%d%%)",
14186 convert_leveldir->identifier, num_levels_converted,
14187 num_levels_handled,
14188 (num_levels_handled ?
14189 num_levels_converted * 100 / num_levels_handled : 0));
14191 if (num_levels_handled != num_levels_converted)
14193 Print(", FAILED:");
14194 for (i = 0; i < MAX_NUM_CONVERT_LEVELS; i++)
14195 if (levels_failed[i])
14200 PrintLine("=", 79);
14202 CloseAllAndExit(0);
14206 // ----------------------------------------------------------------------------
14207 // create and save images for use in level sketches (raw BMP format)
14208 // ----------------------------------------------------------------------------
14210 void CreateLevelSketchImages(void)
14216 InitElementPropertiesGfxElement();
14218 bitmap1 = CreateBitmap(TILEX, TILEY, DEFAULT_DEPTH);
14219 bitmap2 = CreateBitmap(MINI_TILEX, MINI_TILEY, DEFAULT_DEPTH);
14221 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
14223 int element = getMappedElement(i);
14224 char basename1[16];
14225 char basename2[16];
14229 sprintf(basename1, "%04d.bmp", i);
14230 sprintf(basename2, "%04ds.bmp", i);
14232 filename1 = getPath2(global.create_sketch_images_dir, basename1);
14233 filename2 = getPath2(global.create_sketch_images_dir, basename2);
14235 DrawSizedElement(0, 0, element, TILESIZE);
14236 BlitBitmap(drawto, bitmap1, SX, SY, TILEX, TILEY, 0, 0);
14238 if (SDL_SaveBMP(bitmap1->surface, filename1) != 0)
14239 Fail("cannot save level sketch image file '%s'", filename1);
14241 DrawSizedElement(0, 0, element, MINI_TILESIZE);
14242 BlitBitmap(drawto, bitmap2, SX, SY, MINI_TILEX, MINI_TILEY, 0, 0);
14244 if (SDL_SaveBMP(bitmap2->surface, filename2) != 0)
14245 Fail("cannot save level sketch image file '%s'", filename2);
14250 // create corresponding SQL statements (for normal and small images)
14253 printf("insert into phpbb_words values (NULL, '`%03d', '<IMG class=\"levelsketch\" src=\"/I/%04d.png\"/>');\n", i, i);
14254 printf("insert into phpbb_words values (NULL, '¸%03d', '<IMG class=\"levelsketch\" src=\"/I/%04ds.png\"/>');\n", i, i);
14257 printf("insert into phpbb_words values (NULL, '`%04d', '<IMG class=\"levelsketch\" src=\"/I/%04d.png\"/>');\n", i, i);
14258 printf("insert into phpbb_words values (NULL, '¸%04d', '<IMG class=\"levelsketch\" src=\"/I/%04ds.png\"/>');\n", i, i);
14260 // optional: create content for forum level sketch demonstration post
14262 fprintf(stderr, "%03d `%03d%c", i, i, (i % 10 < 9 ? ' ' : '\n'));
14265 FreeBitmap(bitmap1);
14266 FreeBitmap(bitmap2);
14269 fprintf(stderr, "\n");
14271 Info("%d normal and small images created", NUM_FILE_ELEMENTS);
14273 CloseAllAndExit(0);
14277 // ----------------------------------------------------------------------------
14278 // create and save images for element collecting animations (raw BMP format)
14279 // ----------------------------------------------------------------------------
14281 static boolean createCollectImage(int element)
14283 return (IS_COLLECTIBLE(element) && !IS_SP_ELEMENT(element));
14286 void CreateCollectElementImages(void)
14290 int anim_frames = num_steps - 1;
14291 int tile_size = TILESIZE;
14292 int anim_width = tile_size * anim_frames;
14293 int anim_height = tile_size;
14294 int num_collect_images = 0;
14295 int pos_collect_images = 0;
14297 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
14298 if (createCollectImage(i))
14299 num_collect_images++;
14301 Info("Creating %d element collecting animation images ...",
14302 num_collect_images);
14304 int dst_width = anim_width * 2;
14305 int dst_height = anim_height * num_collect_images / 2;
14306 Bitmap *dst_bitmap = CreateBitmap(dst_width, dst_height, DEFAULT_DEPTH);
14307 char *basename_bmp = "RocksCollect.bmp";
14308 char *basename_png = "RocksCollect.png";
14309 char *filename_bmp = getPath2(global.create_collect_images_dir, basename_bmp);
14310 char *filename_png = getPath2(global.create_collect_images_dir, basename_png);
14311 int len_filename_bmp = strlen(filename_bmp);
14312 int len_filename_png = strlen(filename_png);
14313 int max_command_len = MAX_FILENAME_LEN + len_filename_bmp + len_filename_png;
14314 char cmd_convert[max_command_len];
14316 snprintf(cmd_convert, max_command_len, "convert \"%s\" \"%s\"",
14320 // force using RGBA surface for destination bitmap
14321 SDL_SetColorKey(dst_bitmap->surface, SET_TRANSPARENT_PIXEL,
14322 SDL_MapRGB(dst_bitmap->surface->format, 0x00, 0x00, 0x00));
14324 dst_bitmap->surface =
14325 SDL_ConvertSurfaceFormat(dst_bitmap->surface, SDL_PIXELFORMAT_ARGB8888, 0);
14327 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
14329 if (!createCollectImage(i))
14332 int dst_x = (pos_collect_images / (num_collect_images / 2)) * anim_width;
14333 int dst_y = (pos_collect_images % (num_collect_images / 2)) * anim_height;
14334 int graphic = el2img(i);
14335 char *token_name = element_info[i].token_name;
14336 Bitmap *tmp_bitmap = CreateBitmap(tile_size, tile_size, DEFAULT_DEPTH);
14337 Bitmap *src_bitmap;
14340 Info("- creating collecting image for '%s' ...", token_name);
14342 getGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
14344 BlitBitmap(src_bitmap, tmp_bitmap, src_x, src_y,
14345 tile_size, tile_size, 0, 0);
14347 // force using RGBA surface for temporary bitmap (using transparent black)
14348 SDL_SetColorKey(tmp_bitmap->surface, SET_TRANSPARENT_PIXEL,
14349 SDL_MapRGB(tmp_bitmap->surface->format, 0x00, 0x00, 0x00));
14351 tmp_bitmap->surface =
14352 SDL_ConvertSurfaceFormat(tmp_bitmap->surface, SDL_PIXELFORMAT_ARGB8888, 0);
14354 tmp_bitmap->surface_masked = tmp_bitmap->surface;
14356 for (j = 0; j < anim_frames; j++)
14358 int frame_size_final = tile_size * (anim_frames - j) / num_steps;
14359 int frame_size = frame_size_final * num_steps;
14360 int offset = (tile_size - frame_size_final) / 2;
14361 Bitmap *frame_bitmap = ZoomBitmap(tmp_bitmap, frame_size, frame_size);
14363 while (frame_size > frame_size_final)
14367 Bitmap *half_bitmap = ZoomBitmap(frame_bitmap, frame_size, frame_size);
14369 FreeBitmap(frame_bitmap);
14371 frame_bitmap = half_bitmap;
14374 BlitBitmapMasked(frame_bitmap, dst_bitmap, 0, 0,
14375 frame_size_final, frame_size_final,
14376 dst_x + j * tile_size + offset, dst_y + offset);
14378 FreeBitmap(frame_bitmap);
14381 tmp_bitmap->surface_masked = NULL;
14383 FreeBitmap(tmp_bitmap);
14385 pos_collect_images++;
14388 if (SDL_SaveBMP(dst_bitmap->surface, filename_bmp) != 0)
14389 Fail("cannot save element collecting image file '%s'", filename_bmp);
14391 FreeBitmap(dst_bitmap);
14393 Info("Converting image file from BMP to PNG ...");
14395 if (system(cmd_convert) != 0)
14396 Fail("converting image file failed");
14398 unlink(filename_bmp);
14402 CloseAllAndExit(0);
14406 // ----------------------------------------------------------------------------
14407 // create and save images for custom and group elements (raw BMP format)
14408 // ----------------------------------------------------------------------------
14410 void CreateCustomElementImages(char *directory)
14412 char *src_basename = "RocksCE-template.ilbm";
14413 char *dst_basename = "RocksCE.bmp";
14414 char *src_filename = getPath2(directory, src_basename);
14415 char *dst_filename = getPath2(directory, dst_basename);
14416 Bitmap *src_bitmap;
14418 int yoffset_ce = 0;
14419 int yoffset_ge = (TILEY * NUM_CUSTOM_ELEMENTS / 16);
14422 InitVideoDefaults();
14424 ReCreateBitmap(&backbuffer, video.width, video.height);
14426 src_bitmap = LoadImage(src_filename);
14428 bitmap = CreateBitmap(TILEX * 16 * 2,
14429 TILEY * (NUM_CUSTOM_ELEMENTS + NUM_GROUP_ELEMENTS) / 16,
14432 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
14439 BlitBitmap(src_bitmap, bitmap, 0, 0, TILEX, TILEY,
14440 TILEX * x, TILEY * y + yoffset_ce);
14442 BlitBitmap(src_bitmap, bitmap, 0, TILEY,
14444 TILEX * x + TILEX * 16,
14445 TILEY * y + yoffset_ce);
14447 for (j = 2; j >= 0; j--)
14451 BlitBitmap(src_bitmap, bitmap,
14452 TILEX + c * 7, 0, 6, 10,
14453 TILEX * x + 6 + j * 7,
14454 TILEY * y + 11 + yoffset_ce);
14456 BlitBitmap(src_bitmap, bitmap,
14457 TILEX + c * 8, TILEY, 6, 10,
14458 TILEX * 16 + TILEX * x + 6 + j * 8,
14459 TILEY * y + 10 + yoffset_ce);
14465 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
14472 BlitBitmap(src_bitmap, bitmap, 0, 0, TILEX, TILEY,
14473 TILEX * x, TILEY * y + yoffset_ge);
14475 BlitBitmap(src_bitmap, bitmap, 0, TILEY,
14477 TILEX * x + TILEX * 16,
14478 TILEY * y + yoffset_ge);
14480 for (j = 1; j >= 0; j--)
14484 BlitBitmap(src_bitmap, bitmap, TILEX + c * 10, 11, 10, 10,
14485 TILEX * x + 6 + j * 10,
14486 TILEY * y + 11 + yoffset_ge);
14488 BlitBitmap(src_bitmap, bitmap,
14489 TILEX + c * 8, TILEY + 12, 6, 10,
14490 TILEX * 16 + TILEX * x + 10 + j * 8,
14491 TILEY * y + 10 + yoffset_ge);
14497 if (SDL_SaveBMP(bitmap->surface, dst_filename) != 0)
14498 Fail("cannot save CE graphics file '%s'", dst_filename);
14500 FreeBitmap(bitmap);
14502 CloseAllAndExit(0);