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
634 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
635 &li.score[SC_DIAMOND_EXTRA], 20
639 EL_BD_MAGIC_WALL, -1,
640 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
641 &li.bd_magic_wall_wait_hatching, FALSE
644 EL_BD_MAGIC_WALL, -1,
645 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
646 &li.bd_magic_wall_stops_amoeba, TRUE
651 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
652 &li.bd_clock_extra_time, 30
656 EL_BD_VOODOO_DOLL, -1,
657 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
658 &li.bd_voodoo_collects_diamonds, FALSE
661 EL_BD_VOODOO_DOLL, -1,
662 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
663 &li.bd_voodoo_hurt_kills_player, FALSE
666 EL_BD_VOODOO_DOLL, -1,
667 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
668 &li.bd_voodoo_dies_by_rock, FALSE
671 EL_BD_VOODOO_DOLL, -1,
672 TYPE_BOOLEAN, CONF_VALUE_8_BIT(4),
673 &li.bd_voodoo_vanish_by_explosion, TRUE
676 EL_BD_VOODOO_DOLL, -1,
677 TYPE_INTEGER, CONF_VALUE_8_BIT(5),
678 &li.bd_voodoo_penalty_time, 30
682 // (the following values are related to various game elements)
686 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
687 &li.score[SC_EMERALD], 10
692 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
693 &li.score[SC_DIAMOND], 10
698 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
699 &li.score[SC_BUG], 10
704 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
705 &li.score[SC_SPACESHIP], 10
710 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
711 &li.score[SC_PACMAN], 10
716 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
717 &li.score[SC_NUT], 10
722 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
723 &li.score[SC_DYNAMITE], 10
728 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
729 &li.score[SC_KEY], 10
734 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
735 &li.score[SC_PEARL], 10
740 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
741 &li.score[SC_CRYSTAL], 10
744 // (amoeba values used by R'n'D game engine only)
747 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
748 &li.amoeba_content, EL_DIAMOND
752 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
757 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
758 &li.grow_into_diggable, TRUE
760 // (amoeba values used by BD game engine only)
763 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
764 &li.bd_amoeba_wait_for_hatching, FALSE
768 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
769 &li.bd_amoeba_start_immediately, TRUE
773 TYPE_BOOLEAN, CONF_VALUE_8_BIT(4),
774 &li.bd_amoeba_2_explode_by_amoeba, TRUE
778 TYPE_INTEGER, CONF_VALUE_16_BIT(3),
779 &li.bd_amoeba_threshold_too_big, 200
783 TYPE_INTEGER, CONF_VALUE_16_BIT(4),
784 &li.bd_amoeba_slow_growth_time, 200
788 TYPE_INTEGER, CONF_VALUE_8_BIT(5),
789 &li.bd_amoeba_slow_growth_rate, 3
793 TYPE_INTEGER, CONF_VALUE_8_BIT(6),
794 &li.bd_amoeba_fast_growth_rate, 25
798 TYPE_ELEMENT, CONF_VALUE_16_BIT(5),
799 &li.bd_amoeba_content_too_big, EL_BD_ROCK
803 TYPE_ELEMENT, CONF_VALUE_16_BIT(6),
804 &li.bd_amoeba_content_enclosed, EL_BD_DIAMOND
809 TYPE_INTEGER, CONF_VALUE_16_BIT(3),
810 &li.bd_amoeba_2_threshold_too_big, 200
814 TYPE_INTEGER, CONF_VALUE_16_BIT(4),
815 &li.bd_amoeba_2_slow_growth_time, 200
819 TYPE_INTEGER, CONF_VALUE_8_BIT(5),
820 &li.bd_amoeba_2_slow_growth_rate, 3
824 TYPE_INTEGER, CONF_VALUE_8_BIT(6),
825 &li.bd_amoeba_2_fast_growth_rate, 25
829 TYPE_ELEMENT, CONF_VALUE_16_BIT(5),
830 &li.bd_amoeba_2_content_too_big, EL_BD_ROCK
834 TYPE_ELEMENT, CONF_VALUE_16_BIT(6),
835 &li.bd_amoeba_2_content_enclosed, EL_BD_DIAMOND
839 TYPE_ELEMENT, CONF_VALUE_16_BIT(7),
840 &li.bd_amoeba_2_content_exploding, EL_EMPTY
844 TYPE_ELEMENT, CONF_VALUE_16_BIT(8),
845 &li.bd_amoeba_2_content_looks_like, EL_BD_AMOEBA_2
850 TYPE_CONTENT_LIST, CONF_VALUE_BYTES(1),
851 &li.yamyam_content, EL_ROCK, NULL,
852 &li.num_yamyam_contents, 4, MAX_ELEMENT_CONTENTS
856 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
857 &li.score[SC_YAMYAM], 10
862 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
863 &li.score[SC_ROBOT], 10
867 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
873 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
879 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
880 &li.time_magic_wall, 10
885 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
886 &li.game_of_life[0], 2
890 TYPE_INTEGER, CONF_VALUE_8_BIT(2),
891 &li.game_of_life[1], 3
895 TYPE_INTEGER, CONF_VALUE_8_BIT(3),
896 &li.game_of_life[2], 3
900 TYPE_INTEGER, CONF_VALUE_8_BIT(4),
901 &li.game_of_life[3], 3
905 TYPE_BOOLEAN, CONF_VALUE_8_BIT(5),
906 &li.use_life_bugs, FALSE
911 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
916 TYPE_INTEGER, CONF_VALUE_8_BIT(2),
921 TYPE_INTEGER, CONF_VALUE_8_BIT(3),
926 TYPE_INTEGER, CONF_VALUE_8_BIT(4),
931 EL_TIMEGATE_SWITCH, -1,
932 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
933 &li.time_timegate, 10
937 EL_LIGHT_SWITCH_ACTIVE, -1,
938 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
943 EL_SHIELD_NORMAL, -1,
944 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
945 &li.shield_normal_time, 10
948 EL_SHIELD_NORMAL, -1,
949 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
950 &li.score[SC_SHIELD], 10
954 EL_SHIELD_DEADLY, -1,
955 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
956 &li.shield_deadly_time, 10
959 EL_SHIELD_DEADLY, -1,
960 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
961 &li.score[SC_SHIELD], 10
966 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
971 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
972 &li.extra_time_score, 10
976 EL_TIME_ORB_FULL, -1,
977 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
978 &li.time_orb_time, 10
981 EL_TIME_ORB_FULL, -1,
982 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
983 &li.use_time_orb_bug, FALSE
988 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
989 &li.use_spring_bug, FALSE
994 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
995 &li.android_move_time, 10
999 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
1000 &li.android_clone_time, 10
1003 EL_EMC_ANDROID, SAVE_CONF_NEVER,
1004 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(1),
1005 &li.android_clone_element[0], EL_EMPTY, NULL,
1006 &li.num_android_clone_elements, 1, MAX_ANDROID_ELEMENTS_OLD
1010 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(2),
1011 &li.android_clone_element[0], EL_EMPTY, NULL,
1012 &li.num_android_clone_elements, 1, MAX_ANDROID_ELEMENTS
1017 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1018 &li.lenses_score, 10
1022 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
1027 EL_EMC_MAGNIFIER, -1,
1028 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1029 &li.magnify_score, 10
1032 EL_EMC_MAGNIFIER, -1,
1033 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
1034 &li.magnify_time, 10
1038 EL_EMC_MAGIC_BALL, -1,
1039 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1043 EL_EMC_MAGIC_BALL, -1,
1044 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1045 &li.ball_random, FALSE
1048 EL_EMC_MAGIC_BALL, -1,
1049 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
1050 &li.ball_active_initial, FALSE
1053 EL_EMC_MAGIC_BALL, -1,
1054 TYPE_CONTENT_LIST, CONF_VALUE_BYTES(1),
1055 &li.ball_content, EL_EMPTY, NULL,
1056 &li.num_ball_contents, 4, MAX_ELEMENT_CONTENTS
1060 EL_SOKOBAN_FIELD_EMPTY, -1,
1061 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1062 &li.sb_fields_needed, TRUE
1066 EL_SOKOBAN_OBJECT, -1,
1067 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1068 &li.sb_objects_needed, TRUE
1073 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1074 &li.mm_laser_red, FALSE
1078 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
1079 &li.mm_laser_green, FALSE
1083 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
1084 &li.mm_laser_blue, TRUE
1089 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1090 &li.df_laser_red, TRUE
1094 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
1095 &li.df_laser_green, TRUE
1099 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
1100 &li.df_laser_blue, FALSE
1104 EL_MM_FUSE_ACTIVE, -1,
1105 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1106 &li.mm_time_fuse, 25
1110 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1111 &li.mm_time_bomb, 75
1115 EL_MM_GRAY_BALL, -1,
1116 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1117 &li.mm_time_ball, 75
1120 EL_MM_GRAY_BALL, -1,
1121 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
1122 &li.mm_ball_choice_mode, ANIM_RANDOM
1125 EL_MM_GRAY_BALL, -1,
1126 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(1),
1127 &li.mm_ball_content, EL_EMPTY, NULL,
1128 &li.num_mm_ball_contents, 8, MAX_MM_BALL_CONTENTS
1131 EL_MM_GRAY_BALL, -1,
1132 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
1133 &li.rotate_mm_ball_content, TRUE
1136 EL_MM_GRAY_BALL, -1,
1137 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
1138 &li.explode_mm_ball, FALSE
1142 EL_MM_STEEL_BLOCK, -1,
1143 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1144 &li.mm_time_block, 75
1147 EL_MM_LIGHTBALL, -1,
1148 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1149 &li.score[SC_ELEM_BONUS], 10
1159 static struct LevelFileConfigInfo chunk_config_NOTE[] =
1163 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
1164 &xx_envelope.xsize, MAX_ENVELOPE_XSIZE,
1168 TYPE_INTEGER, CONF_VALUE_8_BIT(2),
1169 &xx_envelope.ysize, MAX_ENVELOPE_YSIZE,
1174 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
1175 &xx_envelope.autowrap, FALSE
1179 TYPE_BOOLEAN, CONF_VALUE_8_BIT(4),
1180 &xx_envelope.centered, FALSE
1185 TYPE_STRING, CONF_VALUE_BYTES(1),
1186 &xx_envelope.text, -1, NULL,
1187 &xx_string_length_unused, -1, MAX_ENVELOPE_TEXT_LEN,
1188 &xx_default_string_empty[0]
1198 static struct LevelFileConfigInfo chunk_config_CUSX_base[] =
1202 TYPE_STRING, CONF_VALUE_BYTES(1),
1203 &xx_ei.description[0], -1,
1204 &yy_ei.description[0],
1205 &xx_string_length_unused, -1, MAX_ELEMENT_NAME_LEN,
1206 &xx_default_description[0]
1211 TYPE_BITFIELD, CONF_VALUE_32_BIT(1),
1212 &xx_ei.properties[EP_BITFIELD_BASE_NR], EP_BITMASK_BASE_DEFAULT,
1213 &yy_ei.properties[EP_BITFIELD_BASE_NR]
1215 #if ENABLE_RESERVED_CODE
1216 // (reserved for later use)
1219 TYPE_BITFIELD, CONF_VALUE_32_BIT(2),
1220 &xx_ei.properties[EP_BITFIELD_BASE_NR + 1], EP_BITMASK_DEFAULT,
1221 &yy_ei.properties[EP_BITFIELD_BASE_NR + 1]
1227 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1228 &xx_ei.use_gfx_element, FALSE,
1229 &yy_ei.use_gfx_element
1233 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
1234 &xx_ei.gfx_element_initial, EL_EMPTY_SPACE,
1235 &yy_ei.gfx_element_initial
1240 TYPE_BITFIELD, CONF_VALUE_8_BIT(2),
1241 &xx_ei.access_direction, MV_ALL_DIRECTIONS,
1242 &yy_ei.access_direction
1247 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
1248 &xx_ei.collect_score_initial, 10,
1249 &yy_ei.collect_score_initial
1253 TYPE_INTEGER, CONF_VALUE_16_BIT(3),
1254 &xx_ei.collect_count_initial, 1,
1255 &yy_ei.collect_count_initial
1260 TYPE_INTEGER, CONF_VALUE_16_BIT(4),
1261 &xx_ei.ce_value_fixed_initial, 0,
1262 &yy_ei.ce_value_fixed_initial
1266 TYPE_INTEGER, CONF_VALUE_16_BIT(5),
1267 &xx_ei.ce_value_random_initial, 0,
1268 &yy_ei.ce_value_random_initial
1272 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
1273 &xx_ei.use_last_ce_value, FALSE,
1274 &yy_ei.use_last_ce_value
1279 TYPE_INTEGER, CONF_VALUE_16_BIT(6),
1280 &xx_ei.push_delay_fixed, 8,
1281 &yy_ei.push_delay_fixed
1285 TYPE_INTEGER, CONF_VALUE_16_BIT(7),
1286 &xx_ei.push_delay_random, 8,
1287 &yy_ei.push_delay_random
1291 TYPE_INTEGER, CONF_VALUE_16_BIT(8),
1292 &xx_ei.drop_delay_fixed, 0,
1293 &yy_ei.drop_delay_fixed
1297 TYPE_INTEGER, CONF_VALUE_16_BIT(9),
1298 &xx_ei.drop_delay_random, 0,
1299 &yy_ei.drop_delay_random
1303 TYPE_INTEGER, CONF_VALUE_16_BIT(10),
1304 &xx_ei.move_delay_fixed, 0,
1305 &yy_ei.move_delay_fixed
1309 TYPE_INTEGER, CONF_VALUE_16_BIT(11),
1310 &xx_ei.move_delay_random, 0,
1311 &yy_ei.move_delay_random
1315 TYPE_INTEGER, CONF_VALUE_16_BIT(16),
1316 &xx_ei.step_delay_fixed, 0,
1317 &yy_ei.step_delay_fixed
1321 TYPE_INTEGER, CONF_VALUE_16_BIT(17),
1322 &xx_ei.step_delay_random, 0,
1323 &yy_ei.step_delay_random
1328 TYPE_BITFIELD, CONF_VALUE_32_BIT(3),
1329 &xx_ei.move_pattern, MV_ALL_DIRECTIONS,
1334 TYPE_BITFIELD, CONF_VALUE_8_BIT(4),
1335 &xx_ei.move_direction_initial, MV_START_AUTOMATIC,
1336 &yy_ei.move_direction_initial
1340 TYPE_INTEGER, CONF_VALUE_8_BIT(5),
1341 &xx_ei.move_stepsize, TILEX / 8,
1342 &yy_ei.move_stepsize
1347 TYPE_ELEMENT, CONF_VALUE_16_BIT(12),
1348 &xx_ei.move_enter_element, EL_EMPTY_SPACE,
1349 &yy_ei.move_enter_element
1353 TYPE_ELEMENT, CONF_VALUE_16_BIT(13),
1354 &xx_ei.move_leave_element, EL_EMPTY_SPACE,
1355 &yy_ei.move_leave_element
1359 TYPE_INTEGER, CONF_VALUE_8_BIT(6),
1360 &xx_ei.move_leave_type, LEAVE_TYPE_UNLIMITED,
1361 &yy_ei.move_leave_type
1366 TYPE_INTEGER, CONF_VALUE_8_BIT(7),
1367 &xx_ei.slippery_type, SLIPPERY_ANY_RANDOM,
1368 &yy_ei.slippery_type
1373 TYPE_INTEGER, CONF_VALUE_8_BIT(8),
1374 &xx_ei.explosion_type, EXPLODES_3X3,
1375 &yy_ei.explosion_type
1379 TYPE_INTEGER, CONF_VALUE_16_BIT(14),
1380 &xx_ei.explosion_delay, 16,
1381 &yy_ei.explosion_delay
1385 TYPE_INTEGER, CONF_VALUE_16_BIT(15),
1386 &xx_ei.ignition_delay, 8,
1387 &yy_ei.ignition_delay
1392 TYPE_CONTENT_LIST, CONF_VALUE_BYTES(2),
1393 &xx_ei.content, EL_EMPTY_SPACE,
1395 &xx_num_contents, 1, 1
1398 // ---------- "num_change_pages" must be the last entry ---------------------
1401 -1, SAVE_CONF_ALWAYS,
1402 TYPE_INTEGER, CONF_VALUE_8_BIT(9),
1403 &xx_ei.num_change_pages, 1,
1404 &yy_ei.num_change_pages
1415 static struct LevelFileConfigInfo chunk_config_CUSX_change[] =
1417 // ---------- "current_change_page" must be the first entry -----------------
1420 -1, SAVE_CONF_ALWAYS,
1421 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
1422 &xx_current_change_page, -1
1425 // ---------- (the remaining entries can be in any order) -------------------
1429 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
1430 &xx_change.can_change, FALSE
1435 TYPE_BITFIELD, CONF_VALUE_32_BIT(1),
1436 &xx_event_bits[0], 0
1440 TYPE_BITFIELD, CONF_VALUE_32_BIT(2),
1441 &xx_event_bits[1], 0
1446 TYPE_BITFIELD, CONF_VALUE_8_BIT(3),
1447 &xx_change.trigger_player, CH_PLAYER_ANY
1451 TYPE_BITFIELD, CONF_VALUE_8_BIT(4),
1452 &xx_change.trigger_side, CH_SIDE_ANY
1456 TYPE_BITFIELD, CONF_VALUE_32_BIT(3),
1457 &xx_change.trigger_page, CH_PAGE_ANY
1462 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
1463 &xx_change.target_element, EL_EMPTY_SPACE
1468 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
1469 &xx_change.delay_fixed, 0
1473 TYPE_INTEGER, CONF_VALUE_16_BIT(3),
1474 &xx_change.delay_random, 0
1478 TYPE_INTEGER, CONF_VALUE_16_BIT(4),
1479 &xx_change.delay_frames, FRAMES_PER_SECOND
1484 TYPE_ELEMENT, CONF_VALUE_16_BIT(5),
1485 &xx_change.initial_trigger_element, EL_EMPTY_SPACE
1490 TYPE_BOOLEAN, CONF_VALUE_8_BIT(6),
1491 &xx_change.explode, FALSE
1495 TYPE_BOOLEAN, CONF_VALUE_8_BIT(7),
1496 &xx_change.use_target_content, FALSE
1500 TYPE_BOOLEAN, CONF_VALUE_8_BIT(8),
1501 &xx_change.only_if_complete, FALSE
1505 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
1506 &xx_change.use_random_replace, FALSE
1510 TYPE_INTEGER, CONF_VALUE_8_BIT(10),
1511 &xx_change.random_percentage, 100
1515 TYPE_INTEGER, CONF_VALUE_8_BIT(11),
1516 &xx_change.replace_when, CP_WHEN_EMPTY
1521 TYPE_BOOLEAN, CONF_VALUE_8_BIT(12),
1522 &xx_change.has_action, FALSE
1526 TYPE_INTEGER, CONF_VALUE_8_BIT(13),
1527 &xx_change.action_type, CA_NO_ACTION
1531 TYPE_INTEGER, CONF_VALUE_8_BIT(14),
1532 &xx_change.action_mode, CA_MODE_UNDEFINED
1536 TYPE_INTEGER, CONF_VALUE_16_BIT(6),
1537 &xx_change.action_arg, CA_ARG_UNDEFINED
1542 TYPE_ELEMENT, CONF_VALUE_16_BIT(7),
1543 &xx_change.action_element, EL_EMPTY_SPACE
1548 TYPE_CONTENT_LIST, CONF_VALUE_BYTES(1),
1549 &xx_change.target_content, EL_EMPTY_SPACE, NULL,
1550 &xx_num_contents, 1, 1
1560 static struct LevelFileConfigInfo chunk_config_GRPX[] =
1564 TYPE_STRING, CONF_VALUE_BYTES(1),
1565 &xx_ei.description[0], -1, NULL,
1566 &xx_string_length_unused, -1, MAX_ELEMENT_NAME_LEN,
1567 &xx_default_description[0]
1572 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1573 &xx_ei.use_gfx_element, FALSE
1577 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
1578 &xx_ei.gfx_element_initial, EL_EMPTY_SPACE
1583 TYPE_INTEGER, CONF_VALUE_8_BIT(2),
1584 &xx_group.choice_mode, ANIM_RANDOM
1589 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(2),
1590 &xx_group.element[0], EL_EMPTY_SPACE, NULL,
1591 &xx_group.num_elements, 1, MAX_ELEMENTS_IN_GROUP
1601 static struct LevelFileConfigInfo chunk_config_EMPX[] =
1605 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1606 &xx_ei.use_gfx_element, FALSE
1610 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
1611 &xx_ei.gfx_element_initial, EL_EMPTY_SPACE
1621 static struct LevelFileConfigInfo chunk_config_CONF[] = // (OBSOLETE)
1625 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
1626 &li.block_snap_field, TRUE
1630 TYPE_BOOLEAN, CONF_VALUE_8_BIT(13),
1631 &li.continuous_snapping, TRUE
1635 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
1636 &li.initial_player_stepsize[0], STEPSIZE_NORMAL
1640 TYPE_BOOLEAN, CONF_VALUE_8_BIT(10),
1641 &li.use_start_element[0], FALSE
1645 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
1646 &li.start_element[0], EL_PLAYER_1
1650 TYPE_BOOLEAN, CONF_VALUE_8_BIT(11),
1651 &li.use_artwork_element[0], FALSE
1655 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
1656 &li.artwork_element[0], EL_PLAYER_1
1660 TYPE_BOOLEAN, CONF_VALUE_8_BIT(12),
1661 &li.use_explosion_element[0], FALSE
1665 TYPE_ELEMENT, CONF_VALUE_16_BIT(3),
1666 &li.explosion_element[0], EL_PLAYER_1
1681 filetype_id_list[] =
1683 { LEVEL_FILE_TYPE_RND, "RND" },
1684 { LEVEL_FILE_TYPE_BD, "BD" },
1685 { LEVEL_FILE_TYPE_EM, "EM" },
1686 { LEVEL_FILE_TYPE_SP, "SP" },
1687 { LEVEL_FILE_TYPE_DX, "DX" },
1688 { LEVEL_FILE_TYPE_SB, "SB" },
1689 { LEVEL_FILE_TYPE_DC, "DC" },
1690 { LEVEL_FILE_TYPE_MM, "MM" },
1691 { LEVEL_FILE_TYPE_MM, "DF" },
1696 // ============================================================================
1697 // level file functions
1698 // ============================================================================
1700 static boolean check_special_flags(char *flag)
1702 if (strEqual(options.special_flags, flag) ||
1703 strEqual(leveldir_current->special_flags, flag))
1709 static struct DateInfo getCurrentDate(void)
1711 time_t epoch_seconds = time(NULL);
1712 struct tm *now = localtime(&epoch_seconds);
1713 struct DateInfo date;
1715 date.year = now->tm_year + 1900;
1716 date.month = now->tm_mon + 1;
1717 date.day = now->tm_mday;
1719 date.src = DATE_SRC_CLOCK;
1724 static void resetEventFlags(struct ElementChangeInfo *change)
1728 for (i = 0; i < NUM_CHANGE_EVENTS; i++)
1729 change->has_event[i] = FALSE;
1732 static void resetEventBits(void)
1736 for (i = 0; i < NUM_CE_BITFIELDS; i++)
1737 xx_event_bits[i] = 0;
1740 static void setEventFlagsFromEventBits(struct ElementChangeInfo *change)
1744 /* important: only change event flag if corresponding event bit is set
1745 (this is because all xx_event_bits[] values are loaded separately,
1746 and all xx_event_bits[] values are set back to zero before loading
1747 another value xx_event_bits[x] (each value representing 32 flags)) */
1749 for (i = 0; i < NUM_CHANGE_EVENTS; i++)
1750 if (xx_event_bits[CH_EVENT_BITFIELD_NR(i)] & CH_EVENT_BIT(i))
1751 change->has_event[i] = TRUE;
1754 static void setEventBitsFromEventFlags(struct ElementChangeInfo *change)
1758 /* in contrast to the above function setEventFlagsFromEventBits(), it
1759 would also be possible to set all bits in xx_event_bits[] to 0 or 1
1760 depending on the corresponding change->has_event[i] values here, as
1761 all xx_event_bits[] values are reset in resetEventBits() before */
1763 for (i = 0; i < NUM_CHANGE_EVENTS; i++)
1764 if (change->has_event[i])
1765 xx_event_bits[CH_EVENT_BITFIELD_NR(i)] |= CH_EVENT_BIT(i);
1768 static char *getDefaultElementDescription(struct ElementInfo *ei)
1770 static char description[MAX_ELEMENT_NAME_LEN + 1];
1771 char *default_description = (ei->custom_description != NULL ?
1772 ei->custom_description :
1773 ei->editor_description);
1776 // always start with reliable default values
1777 for (i = 0; i < MAX_ELEMENT_NAME_LEN + 1; i++)
1778 description[i] = '\0';
1780 // truncate element description to MAX_ELEMENT_NAME_LEN bytes
1781 strncpy(description, default_description, MAX_ELEMENT_NAME_LEN);
1783 return &description[0];
1786 static void setElementDescriptionToDefault(struct ElementInfo *ei)
1788 char *default_description = getDefaultElementDescription(ei);
1791 for (i = 0; i < MAX_ELEMENT_NAME_LEN + 1; i++)
1792 ei->description[i] = default_description[i];
1795 static void setConfigToDefaultsFromConfigList(struct LevelFileConfigInfo *conf)
1799 for (i = 0; conf[i].data_type != -1; i++)
1801 int default_value = conf[i].default_value;
1802 int data_type = conf[i].data_type;
1803 int conf_type = conf[i].conf_type;
1804 int byte_mask = conf_type & CONF_MASK_BYTES;
1806 if (byte_mask == CONF_MASK_MULTI_BYTES)
1808 int default_num_entities = conf[i].default_num_entities;
1809 int max_num_entities = conf[i].max_num_entities;
1811 *(int *)(conf[i].num_entities) = default_num_entities;
1813 if (data_type == TYPE_STRING)
1815 char *default_string = conf[i].default_string;
1816 char *string = (char *)(conf[i].value);
1818 strncpy(string, default_string, max_num_entities);
1820 else if (data_type == TYPE_ELEMENT_LIST)
1822 int *element_array = (int *)(conf[i].value);
1825 for (j = 0; j < max_num_entities; j++)
1826 element_array[j] = default_value;
1828 else if (data_type == TYPE_CONTENT_LIST)
1830 struct Content *content = (struct Content *)(conf[i].value);
1833 for (c = 0; c < max_num_entities; c++)
1834 for (y = 0; y < 3; y++)
1835 for (x = 0; x < 3; x++)
1836 content[c].e[x][y] = default_value;
1839 else // constant size configuration data (1, 2 or 4 bytes)
1841 if (data_type == TYPE_BOOLEAN)
1842 *(boolean *)(conf[i].value) = default_value;
1844 *(int *) (conf[i].value) = default_value;
1849 static void copyConfigFromConfigList(struct LevelFileConfigInfo *conf)
1853 for (i = 0; conf[i].data_type != -1; i++)
1855 int data_type = conf[i].data_type;
1856 int conf_type = conf[i].conf_type;
1857 int byte_mask = conf_type & CONF_MASK_BYTES;
1859 if (byte_mask == CONF_MASK_MULTI_BYTES)
1861 int max_num_entities = conf[i].max_num_entities;
1863 if (data_type == TYPE_STRING)
1865 char *string = (char *)(conf[i].value);
1866 char *string_copy = (char *)(conf[i].value_copy);
1868 strncpy(string_copy, string, max_num_entities);
1870 else if (data_type == TYPE_ELEMENT_LIST)
1872 int *element_array = (int *)(conf[i].value);
1873 int *element_array_copy = (int *)(conf[i].value_copy);
1876 for (j = 0; j < max_num_entities; j++)
1877 element_array_copy[j] = element_array[j];
1879 else if (data_type == TYPE_CONTENT_LIST)
1881 struct Content *content = (struct Content *)(conf[i].value);
1882 struct Content *content_copy = (struct Content *)(conf[i].value_copy);
1885 for (c = 0; c < max_num_entities; c++)
1886 for (y = 0; y < 3; y++)
1887 for (x = 0; x < 3; x++)
1888 content_copy[c].e[x][y] = content[c].e[x][y];
1891 else // constant size configuration data (1, 2 or 4 bytes)
1893 if (data_type == TYPE_BOOLEAN)
1894 *(boolean *)(conf[i].value_copy) = *(boolean *)(conf[i].value);
1896 *(int *) (conf[i].value_copy) = *(int *) (conf[i].value);
1901 void copyElementInfo(struct ElementInfo *ei_from, struct ElementInfo *ei_to)
1905 xx_ei = *ei_from; // copy element data into temporary buffer
1906 yy_ei = *ei_to; // copy element data into temporary buffer
1908 copyConfigFromConfigList(chunk_config_CUSX_base);
1913 // ---------- reinitialize and copy change pages ----------
1915 ei_to->num_change_pages = ei_from->num_change_pages;
1916 ei_to->current_change_page = ei_from->current_change_page;
1918 setElementChangePages(ei_to, ei_to->num_change_pages);
1920 for (i = 0; i < ei_to->num_change_pages; i++)
1921 ei_to->change_page[i] = ei_from->change_page[i];
1923 // ---------- copy group element info ----------
1924 if (ei_from->group != NULL && ei_to->group != NULL) // group or internal
1925 *ei_to->group = *ei_from->group;
1927 // mark this custom element as modified
1928 ei_to->modified_settings = TRUE;
1931 void setElementChangePages(struct ElementInfo *ei, int change_pages)
1933 int change_page_size = sizeof(struct ElementChangeInfo);
1935 ei->num_change_pages = MAX(1, change_pages);
1938 checked_realloc(ei->change_page, ei->num_change_pages * change_page_size);
1940 if (ei->current_change_page >= ei->num_change_pages)
1941 ei->current_change_page = ei->num_change_pages - 1;
1943 ei->change = &ei->change_page[ei->current_change_page];
1946 void setElementChangeInfoToDefaults(struct ElementChangeInfo *change)
1948 xx_change = *change; // copy change data into temporary buffer
1950 setConfigToDefaultsFromConfigList(chunk_config_CUSX_change);
1952 *change = xx_change;
1954 resetEventFlags(change);
1956 change->direct_action = 0;
1957 change->other_action = 0;
1959 change->pre_change_function = NULL;
1960 change->change_function = NULL;
1961 change->post_change_function = NULL;
1964 static void setLevelInfoToDefaults_Level(struct LevelInfo *level)
1968 li = *level; // copy level data into temporary buffer
1969 setConfigToDefaultsFromConfigList(chunk_config_INFO);
1970 *level = li; // copy temporary buffer back to level data
1972 setLevelInfoToDefaults_BD();
1973 setLevelInfoToDefaults_EM();
1974 setLevelInfoToDefaults_SP();
1975 setLevelInfoToDefaults_MM();
1977 level->native_bd_level = &native_bd_level;
1978 level->native_em_level = &native_em_level;
1979 level->native_sp_level = &native_sp_level;
1980 level->native_mm_level = &native_mm_level;
1982 level->file_version = FILE_VERSION_ACTUAL;
1983 level->game_version = GAME_VERSION_ACTUAL;
1985 level->creation_date = getCurrentDate();
1987 level->encoding_16bit_field = TRUE;
1988 level->encoding_16bit_yamyam = TRUE;
1989 level->encoding_16bit_amoeba = TRUE;
1991 // clear level name and level author string buffers
1992 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
1993 level->name[i] = '\0';
1994 for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
1995 level->author[i] = '\0';
1997 // set level name and level author to default values
1998 strcpy(level->name, NAMELESS_LEVEL_NAME);
1999 strcpy(level->author, ANONYMOUS_NAME);
2001 // set level playfield to playable default level with player and exit
2002 for (x = 0; x < MAX_LEV_FIELDX; x++)
2003 for (y = 0; y < MAX_LEV_FIELDY; y++)
2004 level->field[x][y] = EL_SAND;
2006 level->field[0][0] = EL_PLAYER_1;
2007 level->field[STD_LEV_FIELDX - 1][STD_LEV_FIELDY - 1] = EL_EXIT_CLOSED;
2009 BorderElement = EL_STEELWALL;
2011 // detect custom elements when loading them
2012 level->file_has_custom_elements = FALSE;
2014 // set all bug compatibility flags to "false" => do not emulate this bug
2015 level->use_action_after_change_bug = FALSE;
2017 if (leveldir_current)
2019 // try to determine better author name than 'anonymous'
2020 if (!strEqual(leveldir_current->author, ANONYMOUS_NAME))
2022 strncpy(level->author, leveldir_current->author, MAX_LEVEL_AUTHOR_LEN);
2023 level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
2027 switch (LEVELCLASS(leveldir_current))
2029 case LEVELCLASS_TUTORIAL:
2030 strcpy(level->author, PROGRAM_AUTHOR_STRING);
2033 case LEVELCLASS_CONTRIB:
2034 strncpy(level->author, leveldir_current->name, MAX_LEVEL_AUTHOR_LEN);
2035 level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
2038 case LEVELCLASS_PRIVATE:
2039 strncpy(level->author, getRealName(), MAX_LEVEL_AUTHOR_LEN);
2040 level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
2044 // keep default value
2051 static void setLevelInfoToDefaults_Elements(struct LevelInfo *level)
2053 static boolean clipboard_elements_initialized = FALSE;
2056 InitElementPropertiesStatic();
2058 li = *level; // copy level data into temporary buffer
2059 setConfigToDefaultsFromConfigList(chunk_config_ELEM);
2060 *level = li; // copy temporary buffer back to level data
2062 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2065 struct ElementInfo *ei = &element_info[element];
2067 if (element == EL_MM_GRAY_BALL)
2069 struct LevelInfo_MM *level_mm = level->native_mm_level;
2072 for (j = 0; j < level->num_mm_ball_contents; j++)
2073 level->mm_ball_content[j] =
2074 map_element_MM_to_RND(level_mm->ball_content[j]);
2077 // never initialize clipboard elements after the very first time
2078 // (to be able to use clipboard elements between several levels)
2079 if (IS_CLIPBOARD_ELEMENT(element) && clipboard_elements_initialized)
2082 if (IS_ENVELOPE(element))
2084 int envelope_nr = element - EL_ENVELOPE_1;
2086 setConfigToDefaultsFromConfigList(chunk_config_NOTE);
2088 level->envelope[envelope_nr] = xx_envelope;
2091 if (IS_CUSTOM_ELEMENT(element) ||
2092 IS_GROUP_ELEMENT(element) ||
2093 IS_INTERNAL_ELEMENT(element))
2095 xx_ei = *ei; // copy element data into temporary buffer
2097 setConfigToDefaultsFromConfigList(chunk_config_CUSX_base);
2102 setElementChangePages(ei, 1);
2103 setElementChangeInfoToDefaults(ei->change);
2105 if (IS_CUSTOM_ELEMENT(element) ||
2106 IS_GROUP_ELEMENT(element))
2108 setElementDescriptionToDefault(ei);
2110 ei->modified_settings = FALSE;
2113 if (IS_CUSTOM_ELEMENT(element) ||
2114 IS_INTERNAL_ELEMENT(element))
2116 // internal values used in level editor
2118 ei->access_type = 0;
2119 ei->access_layer = 0;
2120 ei->access_protected = 0;
2121 ei->walk_to_action = 0;
2122 ei->smash_targets = 0;
2125 ei->can_explode_by_fire = FALSE;
2126 ei->can_explode_smashed = FALSE;
2127 ei->can_explode_impact = FALSE;
2129 ei->current_change_page = 0;
2132 if (IS_GROUP_ELEMENT(element) ||
2133 IS_INTERNAL_ELEMENT(element))
2135 struct ElementGroupInfo *group;
2137 // initialize memory for list of elements in group
2138 if (ei->group == NULL)
2139 ei->group = checked_malloc(sizeof(struct ElementGroupInfo));
2143 xx_group = *group; // copy group data into temporary buffer
2145 setConfigToDefaultsFromConfigList(chunk_config_GRPX);
2150 if (IS_EMPTY_ELEMENT(element) ||
2151 IS_INTERNAL_ELEMENT(element))
2153 xx_ei = *ei; // copy element data into temporary buffer
2155 setConfigToDefaultsFromConfigList(chunk_config_EMPX);
2161 clipboard_elements_initialized = TRUE;
2164 static void setLevelInfoToDefaults(struct LevelInfo *level,
2165 boolean level_info_only,
2166 boolean reset_file_status)
2168 setLevelInfoToDefaults_Level(level);
2170 if (!level_info_only)
2171 setLevelInfoToDefaults_Elements(level);
2173 if (reset_file_status)
2175 level->no_valid_file = FALSE;
2176 level->no_level_file = FALSE;
2179 level->changed = FALSE;
2182 static void setFileInfoToDefaults(struct LevelFileInfo *level_file_info)
2184 level_file_info->nr = 0;
2185 level_file_info->type = LEVEL_FILE_TYPE_UNKNOWN;
2186 level_file_info->packed = FALSE;
2188 setString(&level_file_info->basename, NULL);
2189 setString(&level_file_info->filename, NULL);
2192 int getMappedElement_SB(int, boolean);
2194 static void ActivateLevelTemplate(void)
2198 if (check_special_flags("load_xsb_to_ces"))
2200 // fill smaller playfields with padding "beyond border wall" elements
2201 if (level.fieldx < level_template.fieldx ||
2202 level.fieldy < level_template.fieldy)
2204 short field[level.fieldx][level.fieldy];
2205 int new_fieldx = MAX(level.fieldx, level_template.fieldx);
2206 int new_fieldy = MAX(level.fieldy, level_template.fieldy);
2207 int pos_fieldx = (new_fieldx - level.fieldx) / 2;
2208 int pos_fieldy = (new_fieldy - level.fieldy) / 2;
2210 // copy old playfield (which is smaller than the visible area)
2211 for (y = 0; y < level.fieldy; y++) for (x = 0; x < level.fieldx; x++)
2212 field[x][y] = level.field[x][y];
2214 // fill new, larger playfield with "beyond border wall" elements
2215 for (y = 0; y < new_fieldy; y++) for (x = 0; x < new_fieldx; x++)
2216 level.field[x][y] = getMappedElement_SB('_', TRUE);
2218 // copy the old playfield to the middle of the new playfield
2219 for (y = 0; y < level.fieldy; y++) for (x = 0; x < level.fieldx; x++)
2220 level.field[pos_fieldx + x][pos_fieldy + y] = field[x][y];
2222 level.fieldx = new_fieldx;
2223 level.fieldy = new_fieldy;
2227 // Currently there is no special action needed to activate the template
2228 // data, because 'element_info' property settings overwrite the original
2229 // level data, while all other variables do not change.
2231 // Exception: 'from_level_template' elements in the original level playfield
2232 // are overwritten with the corresponding elements at the same position in
2233 // playfield from the level template.
2235 for (x = 0; x < level.fieldx; x++)
2236 for (y = 0; y < level.fieldy; y++)
2237 if (level.field[x][y] == EL_FROM_LEVEL_TEMPLATE)
2238 level.field[x][y] = level_template.field[x][y];
2240 if (check_special_flags("load_xsb_to_ces"))
2242 struct LevelInfo level_backup = level;
2244 // overwrite all individual level settings from template level settings
2245 level = level_template;
2247 // restore level file info
2248 level.file_info = level_backup.file_info;
2250 // restore playfield size
2251 level.fieldx = level_backup.fieldx;
2252 level.fieldy = level_backup.fieldy;
2254 // restore playfield content
2255 for (x = 0; x < level.fieldx; x++)
2256 for (y = 0; y < level.fieldy; y++)
2257 level.field[x][y] = level_backup.field[x][y];
2259 // restore name and author from individual level
2260 strcpy(level.name, level_backup.name);
2261 strcpy(level.author, level_backup.author);
2263 // restore flag "use_custom_template"
2264 level.use_custom_template = level_backup.use_custom_template;
2268 static boolean checkForPackageFromBasename_BD(char *basename)
2270 // check for native BD level file extensions
2271 if (!strSuffixLower(basename, ".bd") &&
2272 !strSuffixLower(basename, ".bdr") &&
2273 !strSuffixLower(basename, ".brc") &&
2274 !strSuffixLower(basename, ".gds"))
2277 // check for standard single-level BD files (like "001.bd")
2278 if (strSuffixLower(basename, ".bd") &&
2279 strlen(basename) == 6 &&
2280 basename[0] >= '0' && basename[0] <= '9' &&
2281 basename[1] >= '0' && basename[1] <= '9' &&
2282 basename[2] >= '0' && basename[2] <= '9')
2285 // this is a level package in native BD file format
2289 static char *getLevelFilenameFromBasename(char *basename)
2291 static char *filename = NULL;
2293 checked_free(filename);
2295 filename = getPath2(getCurrentLevelDir(), basename);
2300 static int getFileTypeFromBasename(char *basename)
2302 // !!! ALSO SEE COMMENT IN checkForPackageFromBasename() !!!
2304 static char *filename = NULL;
2305 struct stat file_status;
2307 // ---------- try to determine file type from filename ----------
2309 // check for typical filename of a Supaplex level package file
2310 if (strlen(basename) == 10 && strPrefixLower(basename, "levels.d"))
2311 return LEVEL_FILE_TYPE_SP;
2313 // check for typical filename of a Diamond Caves II level package file
2314 if (strSuffixLower(basename, ".dc") ||
2315 strSuffixLower(basename, ".dc2"))
2316 return LEVEL_FILE_TYPE_DC;
2318 // check for typical filename of a Sokoban level package file
2319 if (strSuffixLower(basename, ".xsb") &&
2320 strchr(basename, '%') == NULL)
2321 return LEVEL_FILE_TYPE_SB;
2323 // check for typical filename of a Boulder Dash (GDash) level package file
2324 if (checkForPackageFromBasename_BD(basename))
2325 return LEVEL_FILE_TYPE_BD;
2327 // ---------- try to determine file type from filesize ----------
2329 checked_free(filename);
2330 filename = getPath2(getCurrentLevelDir(), basename);
2332 if (stat(filename, &file_status) == 0)
2334 // check for typical filesize of a Supaplex level package file
2335 if (file_status.st_size == 170496)
2336 return LEVEL_FILE_TYPE_SP;
2339 return LEVEL_FILE_TYPE_UNKNOWN;
2342 static int getFileTypeFromMagicBytes(char *filename, int type)
2346 if ((file = openFile(filename, MODE_READ)))
2348 char chunk_name[CHUNK_ID_LEN + 1];
2350 getFileChunkBE(file, chunk_name, NULL);
2352 if (strEqual(chunk_name, "MMII") ||
2353 strEqual(chunk_name, "MIRR"))
2354 type = LEVEL_FILE_TYPE_MM;
2362 static boolean checkForPackageFromBasename(char *basename)
2364 // !!! WON'T WORK ANYMORE IF getFileTypeFromBasename() ALSO DETECTS !!!
2365 // !!! SINGLE LEVELS (CURRENTLY ONLY DETECTS LEVEL PACKAGES !!!
2367 return (getFileTypeFromBasename(basename) != LEVEL_FILE_TYPE_UNKNOWN);
2370 static char *getSingleLevelBasenameExt(int nr, char *extension)
2372 static char basename[MAX_FILENAME_LEN];
2375 sprintf(basename, "%s", LEVELTEMPLATE_FILENAME);
2377 sprintf(basename, "%03d.%s", nr, extension);
2382 static char *getSingleLevelBasename(int nr)
2384 return getSingleLevelBasenameExt(nr, LEVELFILE_EXTENSION);
2387 static char *getPackedLevelBasename(int type)
2389 static char basename[MAX_FILENAME_LEN];
2390 char *directory = getCurrentLevelDir();
2392 DirectoryEntry *dir_entry;
2394 strcpy(basename, UNDEFINED_FILENAME); // default: undefined file
2396 if ((dir = openDirectory(directory)) == NULL)
2398 Warn("cannot read current level directory '%s'", directory);
2403 while ((dir_entry = readDirectory(dir)) != NULL) // loop all entries
2405 char *entry_basename = dir_entry->basename;
2406 int entry_type = getFileTypeFromBasename(entry_basename);
2408 if (entry_type != LEVEL_FILE_TYPE_UNKNOWN) // found valid level package
2410 if (type == LEVEL_FILE_TYPE_UNKNOWN ||
2413 strcpy(basename, entry_basename);
2420 closeDirectory(dir);
2425 static char *getSingleLevelFilename(int nr)
2427 return getLevelFilenameFromBasename(getSingleLevelBasename(nr));
2430 #if ENABLE_UNUSED_CODE
2431 static char *getPackedLevelFilename(int type)
2433 return getLevelFilenameFromBasename(getPackedLevelBasename(type));
2437 char *getDefaultLevelFilename(int nr)
2439 return getSingleLevelFilename(nr);
2442 #if ENABLE_UNUSED_CODE
2443 static void setLevelFileInfo_SingleLevelFilename(struct LevelFileInfo *lfi,
2447 lfi->packed = FALSE;
2449 setString(&lfi->basename, getSingleLevelBasename(lfi->nr, lfi->type));
2450 setString(&lfi->filename, getLevelFilenameFromBasename(lfi->basename));
2454 static void setLevelFileInfo_FormatLevelFilename(struct LevelFileInfo *lfi,
2455 int type, char *format, ...)
2457 static char basename[MAX_FILENAME_LEN];
2460 va_start(ap, format);
2461 vsprintf(basename, format, ap);
2465 lfi->packed = FALSE;
2467 setString(&lfi->basename, basename);
2468 setString(&lfi->filename, getLevelFilenameFromBasename(lfi->basename));
2471 static void setLevelFileInfo_PackedLevelFilename(struct LevelFileInfo *lfi,
2477 setString(&lfi->basename, getPackedLevelBasename(lfi->type));
2478 setString(&lfi->filename, getLevelFilenameFromBasename(lfi->basename));
2481 static int getFiletypeFromID(char *filetype_id)
2483 char *filetype_id_lower;
2484 int filetype = LEVEL_FILE_TYPE_UNKNOWN;
2487 if (filetype_id == NULL)
2488 return LEVEL_FILE_TYPE_UNKNOWN;
2490 filetype_id_lower = getStringToLower(filetype_id);
2492 for (i = 0; filetype_id_list[i].id != NULL; i++)
2494 char *id_lower = getStringToLower(filetype_id_list[i].id);
2496 if (strEqual(filetype_id_lower, id_lower))
2497 filetype = filetype_id_list[i].filetype;
2501 if (filetype != LEVEL_FILE_TYPE_UNKNOWN)
2505 free(filetype_id_lower);
2510 char *getLocalLevelTemplateFilename(void)
2512 return getDefaultLevelFilename(-1);
2515 char *getGlobalLevelTemplateFilename(void)
2517 // global variable "leveldir_current" must be modified in the loop below
2518 LevelDirTree *leveldir_current_last = leveldir_current;
2519 char *filename = NULL;
2521 // check for template level in path from current to topmost tree node
2523 while (leveldir_current != NULL)
2525 filename = getDefaultLevelFilename(-1);
2527 if (fileExists(filename))
2530 leveldir_current = leveldir_current->node_parent;
2533 // restore global variable "leveldir_current" modified in above loop
2534 leveldir_current = leveldir_current_last;
2539 static void determineLevelFileInfo_Filename(struct LevelFileInfo *lfi)
2543 // special case: level number is negative => check for level template file
2546 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
2547 getSingleLevelBasename(-1));
2549 // replace local level template filename with global template filename
2550 setString(&lfi->filename, getGlobalLevelTemplateFilename());
2552 // no fallback if template file not existing
2556 // special case: check for file name/pattern specified in "levelinfo.conf"
2557 if (leveldir_current->level_filename != NULL)
2559 int filetype = getFiletypeFromID(leveldir_current->level_filetype);
2561 setLevelFileInfo_FormatLevelFilename(lfi, filetype,
2562 leveldir_current->level_filename, nr);
2564 lfi->packed = checkForPackageFromBasename(leveldir_current->level_filename);
2566 if (fileExists(lfi->filename))
2569 else if (leveldir_current->level_filetype != NULL)
2571 int filetype = getFiletypeFromID(leveldir_current->level_filetype);
2573 // check for specified native level file with standard file name
2574 setLevelFileInfo_FormatLevelFilename(lfi, filetype,
2575 "%03d.%s", nr, LEVELFILE_EXTENSION);
2576 if (fileExists(lfi->filename))
2580 // check for native Rocks'n'Diamonds level file
2581 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
2582 "%03d.%s", nr, LEVELFILE_EXTENSION);
2583 if (fileExists(lfi->filename))
2586 // check for native Boulder Dash level file
2587 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_BD, "%03d.bd", nr);
2588 if (fileExists(lfi->filename))
2591 // check for Emerald Mine level file (V1)
2592 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "a%c%c",
2593 'a' + (nr / 10) % 26, '0' + nr % 10);
2594 if (fileExists(lfi->filename))
2596 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "A%c%c",
2597 'A' + (nr / 10) % 26, '0' + nr % 10);
2598 if (fileExists(lfi->filename))
2601 // check for Emerald Mine level file (V2 to V5)
2602 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%d", nr);
2603 if (fileExists(lfi->filename))
2606 // check for Emerald Mine level file (V6 / single mode)
2607 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02ds", nr);
2608 if (fileExists(lfi->filename))
2610 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02dS", nr);
2611 if (fileExists(lfi->filename))
2614 // check for Emerald Mine level file (V6 / teamwork mode)
2615 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02dt", nr);
2616 if (fileExists(lfi->filename))
2618 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02dT", nr);
2619 if (fileExists(lfi->filename))
2622 // check for various packed level file formats
2623 setLevelFileInfo_PackedLevelFilename(lfi, LEVEL_FILE_TYPE_UNKNOWN);
2624 if (fileExists(lfi->filename))
2627 // no known level file found -- use default values (and fail later)
2628 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
2629 "%03d.%s", nr, LEVELFILE_EXTENSION);
2632 static void determineLevelFileInfo_Filetype(struct LevelFileInfo *lfi)
2634 if (lfi->type == LEVEL_FILE_TYPE_UNKNOWN)
2635 lfi->type = getFileTypeFromBasename(lfi->basename);
2637 if (lfi->type == LEVEL_FILE_TYPE_RND)
2638 lfi->type = getFileTypeFromMagicBytes(lfi->filename, lfi->type);
2641 static void setLevelFileInfo(struct LevelFileInfo *level_file_info, int nr)
2643 // always start with reliable default values
2644 setFileInfoToDefaults(level_file_info);
2646 level_file_info->nr = nr; // set requested level number
2648 determineLevelFileInfo_Filename(level_file_info);
2649 determineLevelFileInfo_Filetype(level_file_info);
2652 static void copyLevelFileInfo(struct LevelFileInfo *lfi_from,
2653 struct LevelFileInfo *lfi_to)
2655 lfi_to->nr = lfi_from->nr;
2656 lfi_to->type = lfi_from->type;
2657 lfi_to->packed = lfi_from->packed;
2659 setString(&lfi_to->basename, lfi_from->basename);
2660 setString(&lfi_to->filename, lfi_from->filename);
2663 // ----------------------------------------------------------------------------
2664 // functions for loading R'n'D level
2665 // ----------------------------------------------------------------------------
2667 int getMappedElement(int element)
2669 // remap some (historic, now obsolete) elements
2673 case EL_PLAYER_OBSOLETE:
2674 element = EL_PLAYER_1;
2677 case EL_KEY_OBSOLETE:
2681 case EL_EM_KEY_1_FILE_OBSOLETE:
2682 element = EL_EM_KEY_1;
2685 case EL_EM_KEY_2_FILE_OBSOLETE:
2686 element = EL_EM_KEY_2;
2689 case EL_EM_KEY_3_FILE_OBSOLETE:
2690 element = EL_EM_KEY_3;
2693 case EL_EM_KEY_4_FILE_OBSOLETE:
2694 element = EL_EM_KEY_4;
2697 case EL_ENVELOPE_OBSOLETE:
2698 element = EL_ENVELOPE_1;
2706 if (element >= NUM_FILE_ELEMENTS)
2708 Warn("invalid level element %d", element);
2710 element = EL_UNKNOWN;
2718 static int getMappedElementByVersion(int element, int game_version)
2720 // remap some elements due to certain game version
2722 if (game_version <= VERSION_IDENT(2,2,0,0))
2724 // map game font elements
2725 element = (element == EL_CHAR('[') ? EL_CHAR_AUMLAUT :
2726 element == EL_CHAR('\\') ? EL_CHAR_OUMLAUT :
2727 element == EL_CHAR(']') ? EL_CHAR_UUMLAUT :
2728 element == EL_CHAR('^') ? EL_CHAR_COPYRIGHT : element);
2731 if (game_version < VERSION_IDENT(3,0,0,0))
2733 // map Supaplex gravity tube elements
2734 element = (element == EL_SP_GRAVITY_PORT_LEFT ? EL_SP_PORT_LEFT :
2735 element == EL_SP_GRAVITY_PORT_RIGHT ? EL_SP_PORT_RIGHT :
2736 element == EL_SP_GRAVITY_PORT_UP ? EL_SP_PORT_UP :
2737 element == EL_SP_GRAVITY_PORT_DOWN ? EL_SP_PORT_DOWN :
2744 static int LoadLevel_VERS(File *file, int chunk_size, struct LevelInfo *level)
2746 level->file_version = getFileVersion(file);
2747 level->game_version = getFileVersion(file);
2752 static int LoadLevel_DATE(File *file, int chunk_size, struct LevelInfo *level)
2754 level->creation_date.year = getFile16BitBE(file);
2755 level->creation_date.month = getFile8Bit(file);
2756 level->creation_date.day = getFile8Bit(file);
2758 level->creation_date.src = DATE_SRC_LEVELFILE;
2763 static int LoadLevel_HEAD(File *file, int chunk_size, struct LevelInfo *level)
2765 int initial_player_stepsize;
2766 int initial_player_gravity;
2769 level->fieldx = getFile8Bit(file);
2770 level->fieldy = getFile8Bit(file);
2772 level->time = getFile16BitBE(file);
2773 level->gems_needed = getFile16BitBE(file);
2775 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
2776 level->name[i] = getFile8Bit(file);
2777 level->name[MAX_LEVEL_NAME_LEN] = 0;
2779 for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
2780 level->score[i] = getFile8Bit(file);
2782 level->num_yamyam_contents = STD_ELEMENT_CONTENTS;
2783 for (i = 0; i < STD_ELEMENT_CONTENTS; i++)
2784 for (y = 0; y < 3; y++)
2785 for (x = 0; x < 3; x++)
2786 level->yamyam_content[i].e[x][y] = getMappedElement(getFile8Bit(file));
2788 level->amoeba_speed = getFile8Bit(file);
2789 level->time_magic_wall = getFile8Bit(file);
2790 level->time_wheel = getFile8Bit(file);
2791 level->amoeba_content = getMappedElement(getFile8Bit(file));
2793 initial_player_stepsize = (getFile8Bit(file) == 1 ? STEPSIZE_FAST :
2796 for (i = 0; i < MAX_PLAYERS; i++)
2797 level->initial_player_stepsize[i] = initial_player_stepsize;
2799 initial_player_gravity = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2801 for (i = 0; i < MAX_PLAYERS; i++)
2802 level->initial_player_gravity[i] = initial_player_gravity;
2804 level->encoding_16bit_field = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2805 level->em_slippery_gems = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2807 level->use_custom_template = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2809 level->block_last_field = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2810 level->sp_block_last_field = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2811 level->can_move_into_acid_bits = getFile32BitBE(file);
2812 level->dont_collide_with_bits = getFile8Bit(file);
2814 level->use_spring_bug = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2815 level->use_step_counter = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2817 level->instant_relocation = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2818 level->can_pass_to_walkable = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2819 level->grow_into_diggable = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2821 level->game_engine_type = getFile8Bit(file);
2823 ReadUnusedBytesFromFile(file, LEVEL_CHUNK_HEAD_UNUSED);
2828 static int LoadLevel_NAME(File *file, int chunk_size, struct LevelInfo *level)
2832 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
2833 level->name[i] = getFile8Bit(file);
2834 level->name[MAX_LEVEL_NAME_LEN] = 0;
2839 static int LoadLevel_AUTH(File *file, int chunk_size, struct LevelInfo *level)
2843 for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
2844 level->author[i] = getFile8Bit(file);
2845 level->author[MAX_LEVEL_AUTHOR_LEN] = 0;
2850 static int LoadLevel_BODY(File *file, int chunk_size, struct LevelInfo *level)
2853 int chunk_size_expected = level->fieldx * level->fieldy;
2855 /* Note: "chunk_size" was wrong before version 2.0 when elements are
2856 stored with 16-bit encoding (and should be twice as big then).
2857 Even worse, playfield data was stored 16-bit when only yamyam content
2858 contained 16-bit elements and vice versa. */
2860 if (level->encoding_16bit_field && level->file_version >= FILE_VERSION_2_0)
2861 chunk_size_expected *= 2;
2863 if (chunk_size_expected != chunk_size)
2865 ReadUnusedBytesFromFile(file, chunk_size);
2866 return chunk_size_expected;
2869 for (y = 0; y < level->fieldy; y++)
2870 for (x = 0; x < level->fieldx; x++)
2871 level->field[x][y] =
2872 getMappedElement(level->encoding_16bit_field ? getFile16BitBE(file) :
2877 static int LoadLevel_CONT(File *file, int chunk_size, struct LevelInfo *level)
2880 int header_size = 4;
2881 int content_size = MAX_ELEMENT_CONTENTS * 3 * 3;
2882 int chunk_size_expected = header_size + content_size;
2884 /* Note: "chunk_size" was wrong before version 2.0 when elements are
2885 stored with 16-bit encoding (and should be twice as big then).
2886 Even worse, playfield data was stored 16-bit when only yamyam content
2887 contained 16-bit elements and vice versa. */
2889 if (level->encoding_16bit_field && level->file_version >= FILE_VERSION_2_0)
2890 chunk_size_expected += content_size;
2892 if (chunk_size_expected != chunk_size)
2894 ReadUnusedBytesFromFile(file, chunk_size);
2895 return chunk_size_expected;
2899 level->num_yamyam_contents = getFile8Bit(file);
2903 // correct invalid number of content fields -- should never happen
2904 if (level->num_yamyam_contents < 1 ||
2905 level->num_yamyam_contents > MAX_ELEMENT_CONTENTS)
2906 level->num_yamyam_contents = STD_ELEMENT_CONTENTS;
2908 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
2909 for (y = 0; y < 3; y++)
2910 for (x = 0; x < 3; x++)
2911 level->yamyam_content[i].e[x][y] =
2912 getMappedElement(level->encoding_16bit_field ?
2913 getFile16BitBE(file) : getFile8Bit(file));
2917 static int LoadLevel_CNT2(File *file, int chunk_size, struct LevelInfo *level)
2922 int content_array[MAX_ELEMENT_CONTENTS][3][3];
2924 element = getMappedElement(getFile16BitBE(file));
2925 num_contents = getFile8Bit(file);
2927 getFile8Bit(file); // content x size (unused)
2928 getFile8Bit(file); // content y size (unused)
2930 ReadUnusedBytesFromFile(file, LEVEL_CHUNK_CNT2_UNUSED);
2932 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
2933 for (y = 0; y < 3; y++)
2934 for (x = 0; x < 3; x++)
2935 content_array[i][x][y] = getMappedElement(getFile16BitBE(file));
2937 // correct invalid number of content fields -- should never happen
2938 if (num_contents < 1 || num_contents > MAX_ELEMENT_CONTENTS)
2939 num_contents = STD_ELEMENT_CONTENTS;
2941 if (element == EL_YAMYAM)
2943 level->num_yamyam_contents = num_contents;
2945 for (i = 0; i < num_contents; i++)
2946 for (y = 0; y < 3; y++)
2947 for (x = 0; x < 3; x++)
2948 level->yamyam_content[i].e[x][y] = content_array[i][x][y];
2950 else if (element == EL_BD_AMOEBA)
2952 level->amoeba_content = content_array[0][0][0];
2956 Warn("cannot load content for element '%d'", element);
2962 static int LoadLevel_CNT3(File *file, int chunk_size, struct LevelInfo *level)
2968 int chunk_size_expected;
2970 element = getMappedElement(getFile16BitBE(file));
2971 if (!IS_ENVELOPE(element))
2972 element = EL_ENVELOPE_1;
2974 envelope_nr = element - EL_ENVELOPE_1;
2976 envelope_len = getFile16BitBE(file);
2978 level->envelope[envelope_nr].xsize = getFile8Bit(file);
2979 level->envelope[envelope_nr].ysize = getFile8Bit(file);
2981 ReadUnusedBytesFromFile(file, LEVEL_CHUNK_CNT3_UNUSED);
2983 chunk_size_expected = LEVEL_CHUNK_CNT3_SIZE(envelope_len);
2984 if (chunk_size_expected != chunk_size)
2986 ReadUnusedBytesFromFile(file, chunk_size - LEVEL_CHUNK_CNT3_HEADER);
2987 return chunk_size_expected;
2990 for (i = 0; i < envelope_len; i++)
2991 level->envelope[envelope_nr].text[i] = getFile8Bit(file);
2996 static int LoadLevel_CUS1(File *file, int chunk_size, struct LevelInfo *level)
2998 int num_changed_custom_elements = getFile16BitBE(file);
2999 int chunk_size_expected = 2 + num_changed_custom_elements * 6;
3002 if (chunk_size_expected != chunk_size)
3004 ReadUnusedBytesFromFile(file, chunk_size - 2);
3005 return chunk_size_expected;
3008 for (i = 0; i < num_changed_custom_elements; i++)
3010 int element = getMappedElement(getFile16BitBE(file));
3011 int properties = getFile32BitBE(file);
3013 if (IS_CUSTOM_ELEMENT(element))
3014 element_info[element].properties[EP_BITFIELD_BASE_NR] = properties;
3016 Warn("invalid custom element number %d", element);
3018 // older game versions that wrote level files with CUS1 chunks used
3019 // different default push delay values (not yet stored in level file)
3020 element_info[element].push_delay_fixed = 2;
3021 element_info[element].push_delay_random = 8;
3024 level->file_has_custom_elements = TRUE;
3029 static int LoadLevel_CUS2(File *file, int chunk_size, struct LevelInfo *level)
3031 int num_changed_custom_elements = getFile16BitBE(file);
3032 int chunk_size_expected = 2 + num_changed_custom_elements * 4;
3035 if (chunk_size_expected != chunk_size)
3037 ReadUnusedBytesFromFile(file, chunk_size - 2);
3038 return chunk_size_expected;
3041 for (i = 0; i < num_changed_custom_elements; i++)
3043 int element = getMappedElement(getFile16BitBE(file));
3044 int custom_target_element = getMappedElement(getFile16BitBE(file));
3046 if (IS_CUSTOM_ELEMENT(element))
3047 element_info[element].change->target_element = custom_target_element;
3049 Warn("invalid custom element number %d", element);
3052 level->file_has_custom_elements = TRUE;
3057 static int LoadLevel_CUS3(File *file, int chunk_size, struct LevelInfo *level)
3059 int num_changed_custom_elements = getFile16BitBE(file);
3060 int chunk_size_expected = LEVEL_CHUNK_CUS3_SIZE(num_changed_custom_elements);
3063 if (chunk_size_expected != chunk_size)
3065 ReadUnusedBytesFromFile(file, chunk_size - 2);
3066 return chunk_size_expected;
3069 for (i = 0; i < num_changed_custom_elements; i++)
3071 int element = getMappedElement(getFile16BitBE(file));
3072 struct ElementInfo *ei = &element_info[element];
3073 unsigned int event_bits;
3075 if (!IS_CUSTOM_ELEMENT(element))
3077 Warn("invalid custom element number %d", element);
3079 element = EL_INTERNAL_DUMMY;
3082 for (j = 0; j < MAX_ELEMENT_NAME_LEN; j++)
3083 ei->description[j] = getFile8Bit(file);
3084 ei->description[MAX_ELEMENT_NAME_LEN] = 0;
3086 ei->properties[EP_BITFIELD_BASE_NR] = getFile32BitBE(file);
3088 // some free bytes for future properties and padding
3089 ReadUnusedBytesFromFile(file, 7);
3091 ei->use_gfx_element = getFile8Bit(file);
3092 ei->gfx_element_initial = getMappedElement(getFile16BitBE(file));
3094 ei->collect_score_initial = getFile8Bit(file);
3095 ei->collect_count_initial = getFile8Bit(file);
3097 ei->push_delay_fixed = getFile16BitBE(file);
3098 ei->push_delay_random = getFile16BitBE(file);
3099 ei->move_delay_fixed = getFile16BitBE(file);
3100 ei->move_delay_random = getFile16BitBE(file);
3102 ei->move_pattern = getFile16BitBE(file);
3103 ei->move_direction_initial = getFile8Bit(file);
3104 ei->move_stepsize = getFile8Bit(file);
3106 for (y = 0; y < 3; y++)
3107 for (x = 0; x < 3; x++)
3108 ei->content.e[x][y] = getMappedElement(getFile16BitBE(file));
3110 // bits 0 - 31 of "has_event[]"
3111 event_bits = getFile32BitBE(file);
3112 for (j = 0; j < MIN(NUM_CHANGE_EVENTS, 32); j++)
3113 if (event_bits & (1u << j))
3114 ei->change->has_event[j] = TRUE;
3116 ei->change->target_element = getMappedElement(getFile16BitBE(file));
3118 ei->change->delay_fixed = getFile16BitBE(file);
3119 ei->change->delay_random = getFile16BitBE(file);
3120 ei->change->delay_frames = getFile16BitBE(file);
3122 ei->change->initial_trigger_element = getMappedElement(getFile16BitBE(file));
3124 ei->change->explode = getFile8Bit(file);
3125 ei->change->use_target_content = getFile8Bit(file);
3126 ei->change->only_if_complete = getFile8Bit(file);
3127 ei->change->use_random_replace = getFile8Bit(file);
3129 ei->change->random_percentage = getFile8Bit(file);
3130 ei->change->replace_when = getFile8Bit(file);
3132 for (y = 0; y < 3; y++)
3133 for (x = 0; x < 3; x++)
3134 ei->change->target_content.e[x][y] =
3135 getMappedElement(getFile16BitBE(file));
3137 ei->slippery_type = getFile8Bit(file);
3139 // some free bytes for future properties and padding
3140 ReadUnusedBytesFromFile(file, LEVEL_CPART_CUS3_UNUSED);
3142 // mark that this custom element has been modified
3143 ei->modified_settings = TRUE;
3146 level->file_has_custom_elements = TRUE;
3151 static int LoadLevel_CUS4(File *file, int chunk_size, struct LevelInfo *level)
3153 struct ElementInfo *ei;
3154 int chunk_size_expected;
3158 // ---------- custom element base property values (96 bytes) ----------------
3160 element = getMappedElement(getFile16BitBE(file));
3162 if (!IS_CUSTOM_ELEMENT(element))
3164 Warn("invalid custom element number %d", element);
3166 ReadUnusedBytesFromFile(file, chunk_size - 2);
3171 ei = &element_info[element];
3173 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
3174 ei->description[i] = getFile8Bit(file);
3175 ei->description[MAX_ELEMENT_NAME_LEN] = 0;
3177 ei->properties[EP_BITFIELD_BASE_NR] = getFile32BitBE(file);
3179 ReadUnusedBytesFromFile(file, 4); // reserved for more base properties
3181 ei->num_change_pages = getFile8Bit(file);
3183 chunk_size_expected = LEVEL_CHUNK_CUS4_SIZE(ei->num_change_pages);
3184 if (chunk_size_expected != chunk_size)
3186 ReadUnusedBytesFromFile(file, chunk_size - 43);
3187 return chunk_size_expected;
3190 ei->ce_value_fixed_initial = getFile16BitBE(file);
3191 ei->ce_value_random_initial = getFile16BitBE(file);
3192 ei->use_last_ce_value = getFile8Bit(file);
3194 ei->use_gfx_element = getFile8Bit(file);
3195 ei->gfx_element_initial = getMappedElement(getFile16BitBE(file));
3197 ei->collect_score_initial = getFile8Bit(file);
3198 ei->collect_count_initial = getFile8Bit(file);
3200 ei->drop_delay_fixed = getFile8Bit(file);
3201 ei->push_delay_fixed = getFile8Bit(file);
3202 ei->drop_delay_random = getFile8Bit(file);
3203 ei->push_delay_random = getFile8Bit(file);
3204 ei->move_delay_fixed = getFile16BitBE(file);
3205 ei->move_delay_random = getFile16BitBE(file);
3207 // bits 0 - 15 of "move_pattern" ...
3208 ei->move_pattern = getFile16BitBE(file);
3209 ei->move_direction_initial = getFile8Bit(file);
3210 ei->move_stepsize = getFile8Bit(file);
3212 ei->slippery_type = getFile8Bit(file);
3214 for (y = 0; y < 3; y++)
3215 for (x = 0; x < 3; x++)
3216 ei->content.e[x][y] = getMappedElement(getFile16BitBE(file));
3218 ei->move_enter_element = getMappedElement(getFile16BitBE(file));
3219 ei->move_leave_element = getMappedElement(getFile16BitBE(file));
3220 ei->move_leave_type = getFile8Bit(file);
3222 // ... bits 16 - 31 of "move_pattern" (not nice, but downward compatible)
3223 ei->move_pattern |= (getFile16BitBE(file) << 16);
3225 ei->access_direction = getFile8Bit(file);
3227 ei->explosion_delay = getFile8Bit(file);
3228 ei->ignition_delay = getFile8Bit(file);
3229 ei->explosion_type = getFile8Bit(file);
3231 // some free bytes for future custom property values and padding
3232 ReadUnusedBytesFromFile(file, 1);
3234 // ---------- change page property values (48 bytes) ------------------------
3236 setElementChangePages(ei, ei->num_change_pages);
3238 for (i = 0; i < ei->num_change_pages; i++)
3240 struct ElementChangeInfo *change = &ei->change_page[i];
3241 unsigned int event_bits;
3243 // always start with reliable default values
3244 setElementChangeInfoToDefaults(change);
3246 // bits 0 - 31 of "has_event[]" ...
3247 event_bits = getFile32BitBE(file);
3248 for (j = 0; j < MIN(NUM_CHANGE_EVENTS, 32); j++)
3249 if (event_bits & (1u << j))
3250 change->has_event[j] = TRUE;
3252 change->target_element = getMappedElement(getFile16BitBE(file));
3254 change->delay_fixed = getFile16BitBE(file);
3255 change->delay_random = getFile16BitBE(file);
3256 change->delay_frames = getFile16BitBE(file);
3258 change->initial_trigger_element = getMappedElement(getFile16BitBE(file));
3260 change->explode = getFile8Bit(file);
3261 change->use_target_content = getFile8Bit(file);
3262 change->only_if_complete = getFile8Bit(file);
3263 change->use_random_replace = getFile8Bit(file);
3265 change->random_percentage = getFile8Bit(file);
3266 change->replace_when = getFile8Bit(file);
3268 for (y = 0; y < 3; y++)
3269 for (x = 0; x < 3; x++)
3270 change->target_content.e[x][y]= getMappedElement(getFile16BitBE(file));
3272 change->can_change = getFile8Bit(file);
3274 change->trigger_side = getFile8Bit(file);
3276 change->trigger_player = getFile8Bit(file);
3277 change->trigger_page = getFile8Bit(file);
3279 change->trigger_page = (change->trigger_page == CH_PAGE_ANY_FILE ?
3280 CH_PAGE_ANY : (1 << change->trigger_page));
3282 change->has_action = getFile8Bit(file);
3283 change->action_type = getFile8Bit(file);
3284 change->action_mode = getFile8Bit(file);
3285 change->action_arg = getFile16BitBE(file);
3287 // ... bits 32 - 39 of "has_event[]" (not nice, but downward compatible)
3288 event_bits = getFile8Bit(file);
3289 for (j = 32; j < NUM_CHANGE_EVENTS; j++)
3290 if (event_bits & (1u << (j - 32)))
3291 change->has_event[j] = TRUE;
3294 // mark this custom element as modified
3295 ei->modified_settings = TRUE;
3297 level->file_has_custom_elements = TRUE;
3302 static int LoadLevel_GRP1(File *file, int chunk_size, struct LevelInfo *level)
3304 struct ElementInfo *ei;
3305 struct ElementGroupInfo *group;
3309 element = getMappedElement(getFile16BitBE(file));
3311 if (!IS_GROUP_ELEMENT(element))
3313 Warn("invalid group element number %d", element);
3315 ReadUnusedBytesFromFile(file, chunk_size - 2);
3320 ei = &element_info[element];
3322 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
3323 ei->description[i] = getFile8Bit(file);
3324 ei->description[MAX_ELEMENT_NAME_LEN] = 0;
3326 group = element_info[element].group;
3328 group->num_elements = getFile8Bit(file);
3330 ei->use_gfx_element = getFile8Bit(file);
3331 ei->gfx_element_initial = getMappedElement(getFile16BitBE(file));
3333 group->choice_mode = getFile8Bit(file);
3335 // some free bytes for future values and padding
3336 ReadUnusedBytesFromFile(file, 3);
3338 for (i = 0; i < MAX_ELEMENTS_IN_GROUP; i++)
3339 group->element[i] = getMappedElement(getFile16BitBE(file));
3341 // mark this group element as modified
3342 element_info[element].modified_settings = TRUE;
3344 level->file_has_custom_elements = TRUE;
3349 static int LoadLevel_MicroChunk(File *file, struct LevelFileConfigInfo *conf,
3350 int element, int real_element)
3352 int micro_chunk_size = 0;
3353 int conf_type = getFile8Bit(file);
3354 int byte_mask = conf_type & CONF_MASK_BYTES;
3355 boolean element_found = FALSE;
3358 micro_chunk_size += 1;
3360 if (byte_mask == CONF_MASK_MULTI_BYTES)
3362 int num_bytes = getFile16BitBE(file);
3363 byte *buffer = checked_malloc(num_bytes);
3365 ReadBytesFromFile(file, buffer, num_bytes);
3367 for (i = 0; conf[i].data_type != -1; i++)
3369 if (conf[i].element == element &&
3370 conf[i].conf_type == conf_type)
3372 int data_type = conf[i].data_type;
3373 int num_entities = num_bytes / CONF_ENTITY_NUM_BYTES(data_type);
3374 int max_num_entities = conf[i].max_num_entities;
3376 if (num_entities > max_num_entities)
3378 Warn("truncating number of entities for element %d from %d to %d",
3379 element, num_entities, max_num_entities);
3381 num_entities = max_num_entities;
3384 if (num_entities == 0 && (data_type == TYPE_ELEMENT_LIST ||
3385 data_type == TYPE_CONTENT_LIST))
3387 // for element and content lists, zero entities are not allowed
3388 Warn("found empty list of entities for element %d", element);
3390 // do not set "num_entities" here to prevent reading behind buffer
3392 *(int *)(conf[i].num_entities) = 1; // at least one is required
3396 *(int *)(conf[i].num_entities) = num_entities;
3399 element_found = TRUE;
3401 if (data_type == TYPE_STRING)
3403 char *string = (char *)(conf[i].value);
3406 for (j = 0; j < max_num_entities; j++)
3407 string[j] = (j < num_entities ? buffer[j] : '\0');
3409 else if (data_type == TYPE_ELEMENT_LIST)
3411 int *element_array = (int *)(conf[i].value);
3414 for (j = 0; j < num_entities; j++)
3416 getMappedElement(CONF_ELEMENTS_ELEMENT(buffer, j));
3418 else if (data_type == TYPE_CONTENT_LIST)
3420 struct Content *content= (struct Content *)(conf[i].value);
3423 for (c = 0; c < num_entities; c++)
3424 for (y = 0; y < 3; y++)
3425 for (x = 0; x < 3; x++)
3426 content[c].e[x][y] =
3427 getMappedElement(CONF_CONTENTS_ELEMENT(buffer, c, x, y));
3430 element_found = FALSE;
3436 checked_free(buffer);
3438 micro_chunk_size += 2 + num_bytes;
3440 else // constant size configuration data (1, 2 or 4 bytes)
3442 int value = (byte_mask == CONF_MASK_1_BYTE ? getFile8Bit (file) :
3443 byte_mask == CONF_MASK_2_BYTE ? getFile16BitBE(file) :
3444 byte_mask == CONF_MASK_4_BYTE ? getFile32BitBE(file) : 0);
3446 for (i = 0; conf[i].data_type != -1; i++)
3448 if (conf[i].element == element &&
3449 conf[i].conf_type == conf_type)
3451 int data_type = conf[i].data_type;
3453 if (data_type == TYPE_ELEMENT)
3454 value = getMappedElement(value);
3456 if (data_type == TYPE_BOOLEAN)
3457 *(boolean *)(conf[i].value) = (value ? TRUE : FALSE);
3459 *(int *) (conf[i].value) = value;
3461 element_found = TRUE;
3467 micro_chunk_size += CONF_VALUE_NUM_BYTES(byte_mask);
3472 char *error_conf_chunk_bytes =
3473 (byte_mask == CONF_MASK_1_BYTE ? "CONF_VALUE_8_BIT" :
3474 byte_mask == CONF_MASK_2_BYTE ? "CONF_VALUE_16_BIT" :
3475 byte_mask == CONF_MASK_4_BYTE ? "CONF_VALUE_32_BIT" :"CONF_VALUE_BYTES");
3476 int error_conf_chunk_token = conf_type & CONF_MASK_TOKEN;
3477 int error_element = real_element;
3479 Warn("cannot load micro chunk '%s(%d)' value for element %d ['%s']",
3480 error_conf_chunk_bytes, error_conf_chunk_token,
3481 error_element, EL_NAME(error_element));
3484 return micro_chunk_size;
3487 static int LoadLevel_INFO(File *file, int chunk_size, struct LevelInfo *level)
3489 int real_chunk_size = 0;
3491 li = *level; // copy level data into temporary buffer
3493 while (!checkEndOfFile(file))
3495 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_INFO, -1, -1);
3497 if (real_chunk_size >= chunk_size)
3501 *level = li; // copy temporary buffer back to level data
3503 return real_chunk_size;
3506 static int LoadLevel_CONF(File *file, int chunk_size, struct LevelInfo *level)
3508 int real_chunk_size = 0;
3510 li = *level; // copy level data into temporary buffer
3512 while (!checkEndOfFile(file))
3514 int element = getMappedElement(getFile16BitBE(file));
3516 real_chunk_size += 2;
3517 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_CONF,
3519 if (real_chunk_size >= chunk_size)
3523 *level = li; // copy temporary buffer back to level data
3525 return real_chunk_size;
3528 static int LoadLevel_ELEM(File *file, int chunk_size, struct LevelInfo *level)
3530 int real_chunk_size = 0;
3532 li = *level; // copy level data into temporary buffer
3534 while (!checkEndOfFile(file))
3536 int element = getMappedElement(getFile16BitBE(file));
3538 real_chunk_size += 2;
3539 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_ELEM,
3541 if (real_chunk_size >= chunk_size)
3545 *level = li; // copy temporary buffer back to level data
3547 return real_chunk_size;
3550 static int LoadLevel_NOTE(File *file, int chunk_size, struct LevelInfo *level)
3552 int element = getMappedElement(getFile16BitBE(file));
3553 int envelope_nr = element - EL_ENVELOPE_1;
3554 int real_chunk_size = 2;
3556 xx_envelope = level->envelope[envelope_nr]; // copy into temporary buffer
3558 while (!checkEndOfFile(file))
3560 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_NOTE,
3563 if (real_chunk_size >= chunk_size)
3567 level->envelope[envelope_nr] = xx_envelope; // copy from temporary buffer
3569 return real_chunk_size;
3572 static int LoadLevel_CUSX(File *file, int chunk_size, struct LevelInfo *level)
3574 int element = getMappedElement(getFile16BitBE(file));
3575 int real_chunk_size = 2;
3576 struct ElementInfo *ei = &element_info[element];
3579 xx_ei = *ei; // copy element data into temporary buffer
3581 xx_ei.num_change_pages = -1;
3583 while (!checkEndOfFile(file))
3585 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_CUSX_base,
3587 if (xx_ei.num_change_pages != -1)
3590 if (real_chunk_size >= chunk_size)
3596 if (ei->num_change_pages == -1)
3598 Warn("LoadLevel_CUSX(): missing 'num_change_pages' for '%s'",
3601 ei->num_change_pages = 1;
3603 setElementChangePages(ei, 1);
3604 setElementChangeInfoToDefaults(ei->change);
3606 return real_chunk_size;
3609 // initialize number of change pages stored for this custom element
3610 setElementChangePages(ei, ei->num_change_pages);
3611 for (i = 0; i < ei->num_change_pages; i++)
3612 setElementChangeInfoToDefaults(&ei->change_page[i]);
3614 // start with reading properties for the first change page
3615 xx_current_change_page = 0;
3617 while (!checkEndOfFile(file))
3619 // level file might contain invalid change page number
3620 if (xx_current_change_page >= ei->num_change_pages)
3623 struct ElementChangeInfo *change = &ei->change_page[xx_current_change_page];
3625 xx_change = *change; // copy change data into temporary buffer
3627 resetEventBits(); // reset bits; change page might have changed
3629 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_CUSX_change,
3632 *change = xx_change;
3634 setEventFlagsFromEventBits(change);
3636 if (real_chunk_size >= chunk_size)
3640 level->file_has_custom_elements = TRUE;
3642 return real_chunk_size;
3645 static int LoadLevel_GRPX(File *file, int chunk_size, struct LevelInfo *level)
3647 int element = getMappedElement(getFile16BitBE(file));
3648 int real_chunk_size = 2;
3649 struct ElementInfo *ei = &element_info[element];
3650 struct ElementGroupInfo *group = ei->group;
3655 xx_ei = *ei; // copy element data into temporary buffer
3656 xx_group = *group; // copy group data into temporary buffer
3658 while (!checkEndOfFile(file))
3660 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_GRPX,
3663 if (real_chunk_size >= chunk_size)
3670 level->file_has_custom_elements = TRUE;
3672 return real_chunk_size;
3675 static int LoadLevel_EMPX(File *file, int chunk_size, struct LevelInfo *level)
3677 int element = getMappedElement(getFile16BitBE(file));
3678 int real_chunk_size = 2;
3679 struct ElementInfo *ei = &element_info[element];
3681 xx_ei = *ei; // copy element data into temporary buffer
3683 while (!checkEndOfFile(file))
3685 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_EMPX,
3688 if (real_chunk_size >= chunk_size)
3694 level->file_has_custom_elements = TRUE;
3696 return real_chunk_size;
3699 static void LoadLevelFromFileInfo_RND(struct LevelInfo *level,
3700 struct LevelFileInfo *level_file_info,
3701 boolean level_info_only)
3703 char *filename = level_file_info->filename;
3704 char cookie[MAX_LINE_LEN];
3705 char chunk_name[CHUNK_ID_LEN + 1];
3709 if (!(file = openFile(filename, MODE_READ)))
3711 level->no_valid_file = TRUE;
3712 level->no_level_file = TRUE;
3714 if (level_info_only)
3717 Warn("cannot read level '%s' -- using empty level", filename);
3719 if (!setup.editor.use_template_for_new_levels)
3722 // if level file not found, try to initialize level data from template
3723 filename = getGlobalLevelTemplateFilename();
3725 if (!(file = openFile(filename, MODE_READ)))
3728 // default: for empty levels, use level template for custom elements
3729 level->use_custom_template = TRUE;
3731 level->no_valid_file = FALSE;
3734 getFileChunkBE(file, chunk_name, NULL);
3735 if (strEqual(chunk_name, "RND1"))
3737 getFile32BitBE(file); // not used
3739 getFileChunkBE(file, chunk_name, NULL);
3740 if (!strEqual(chunk_name, "CAVE"))
3742 level->no_valid_file = TRUE;
3744 Warn("unknown format of level file '%s'", filename);
3751 else // check for pre-2.0 file format with cookie string
3753 strcpy(cookie, chunk_name);
3754 if (getStringFromFile(file, &cookie[4], MAX_LINE_LEN - 4) == NULL)
3756 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
3757 cookie[strlen(cookie) - 1] = '\0';
3759 if (!checkCookieString(cookie, LEVEL_COOKIE_TMPL))
3761 level->no_valid_file = TRUE;
3763 Warn("unknown format of level file '%s'", filename);
3770 if ((level->file_version = getFileVersionFromCookieString(cookie)) == -1)
3772 level->no_valid_file = TRUE;
3774 Warn("unsupported version of level file '%s'", filename);
3781 // pre-2.0 level files have no game version, so use file version here
3782 level->game_version = level->file_version;
3785 if (level->file_version < FILE_VERSION_1_2)
3787 // level files from versions before 1.2.0 without chunk structure
3788 LoadLevel_HEAD(file, LEVEL_CHUNK_HEAD_SIZE, level);
3789 LoadLevel_BODY(file, level->fieldx * level->fieldy, level);
3797 int (*loader)(File *, int, struct LevelInfo *);
3801 { "VERS", LEVEL_CHUNK_VERS_SIZE, LoadLevel_VERS },
3802 { "DATE", LEVEL_CHUNK_DATE_SIZE, LoadLevel_DATE },
3803 { "HEAD", LEVEL_CHUNK_HEAD_SIZE, LoadLevel_HEAD },
3804 { "NAME", LEVEL_CHUNK_NAME_SIZE, LoadLevel_NAME },
3805 { "AUTH", LEVEL_CHUNK_AUTH_SIZE, LoadLevel_AUTH },
3806 { "INFO", -1, LoadLevel_INFO },
3807 { "BODY", -1, LoadLevel_BODY },
3808 { "CONT", -1, LoadLevel_CONT },
3809 { "CNT2", LEVEL_CHUNK_CNT2_SIZE, LoadLevel_CNT2 },
3810 { "CNT3", -1, LoadLevel_CNT3 },
3811 { "CUS1", -1, LoadLevel_CUS1 },
3812 { "CUS2", -1, LoadLevel_CUS2 },
3813 { "CUS3", -1, LoadLevel_CUS3 },
3814 { "CUS4", -1, LoadLevel_CUS4 },
3815 { "GRP1", -1, LoadLevel_GRP1 },
3816 { "CONF", -1, LoadLevel_CONF },
3817 { "ELEM", -1, LoadLevel_ELEM },
3818 { "NOTE", -1, LoadLevel_NOTE },
3819 { "CUSX", -1, LoadLevel_CUSX },
3820 { "GRPX", -1, LoadLevel_GRPX },
3821 { "EMPX", -1, LoadLevel_EMPX },
3826 while (getFileChunkBE(file, chunk_name, &chunk_size))
3830 while (chunk_info[i].name != NULL &&
3831 !strEqual(chunk_name, chunk_info[i].name))
3834 if (chunk_info[i].name == NULL)
3836 Warn("unknown chunk '%s' in level file '%s'",
3837 chunk_name, filename);
3839 ReadUnusedBytesFromFile(file, chunk_size);
3841 else if (chunk_info[i].size != -1 &&
3842 chunk_info[i].size != chunk_size)
3844 Warn("wrong size (%d) of chunk '%s' in level file '%s'",
3845 chunk_size, chunk_name, filename);
3847 ReadUnusedBytesFromFile(file, chunk_size);
3851 // call function to load this level chunk
3852 int chunk_size_expected =
3853 (chunk_info[i].loader)(file, chunk_size, level);
3855 if (chunk_size_expected < 0)
3857 Warn("error reading chunk '%s' in level file '%s'",
3858 chunk_name, filename);
3863 // the size of some chunks cannot be checked before reading other
3864 // chunks first (like "HEAD" and "BODY") that contain some header
3865 // information, so check them here
3866 if (chunk_size_expected != chunk_size)
3868 Warn("wrong size (%d) of chunk '%s' in level file '%s'",
3869 chunk_size, chunk_name, filename);
3881 // ----------------------------------------------------------------------------
3882 // functions for loading BD level
3883 // ----------------------------------------------------------------------------
3885 static void CopyNativeLevel_RND_to_BD(struct LevelInfo *level)
3887 struct LevelInfo_BD *level_bd = level->native_bd_level;
3888 GdCave *cave = NULL; // will be changed below
3889 int cave_w = MIN(level->fieldx, MAX_PLAYFIELD_WIDTH);
3890 int cave_h = MIN(level->fieldy, MAX_PLAYFIELD_HEIGHT);
3893 setLevelInfoToDefaults_BD_Ext(cave_w, cave_h);
3895 // cave and map newly allocated when set to defaults above
3896 cave = level_bd->cave;
3899 cave->intermission = level->bd_intermission;
3902 cave->level_time[0] = level->time;
3903 cave->level_diamonds[0] = level->gems_needed;
3906 cave->scheduling = level->bd_scheduling_type;
3907 cave->pal_timing = level->bd_pal_timing;
3908 cave->level_speed[0] = level->bd_cycle_delay_ms;
3909 cave->level_ckdelay[0] = level->bd_cycle_delay_c64;
3910 cave->level_hatching_delay_frame[0] = level->bd_hatching_delay_cycles;
3911 cave->level_hatching_delay_time[0] = level->bd_hatching_delay_seconds;
3914 cave->level_timevalue[0] = level->score[SC_TIME_BONUS];
3915 cave->diamond_value = level->score[SC_EMERALD];
3916 cave->extra_diamond_value = level->score[SC_DIAMOND_EXTRA];
3918 // compatibility settings
3919 cave->lineshift = level->bd_line_shifting_borders;
3920 cave->border_scan_first_and_last = level->bd_scan_first_and_last_row;
3921 cave->short_explosions = level->bd_short_explosions;
3922 cave->gravity_affects_all = level->bd_gravity_affects_all;
3924 // player properties
3925 cave->diagonal_movements = level->bd_diagonal_movements;
3926 cave->active_is_first_found = level->bd_topmost_player_active;
3927 cave->pushing_stone_prob = level->bd_pushing_prob * 10000;
3928 cave->pushing_stone_prob_sweet = level->bd_pushing_prob_with_sweet * 10000;
3929 cave->mega_stones_pushable_with_sweet = level->bd_push_mega_rock_with_sweet;
3931 // element properties
3932 cave->level_bonus_time[0] = level->bd_clock_extra_time;
3933 cave->voodoo_collects_diamonds = level->bd_voodoo_collects_diamonds;
3934 cave->voodoo_any_hurt_kills_player = level->bd_voodoo_hurt_kills_player;
3935 cave->voodoo_dies_by_stone = level->bd_voodoo_dies_by_rock;
3936 cave->voodoo_disappear_in_explosion = level->bd_voodoo_vanish_by_explosion;
3937 cave->level_penalty_time[0] = level->bd_voodoo_penalty_time;
3938 cave->level_magic_wall_time[0] = level->time_magic_wall;
3939 cave->magic_timer_wait_for_hatching = level->bd_magic_wall_wait_hatching;
3940 cave->magic_wall_stops_amoeba = level->bd_magic_wall_stops_amoeba;
3941 cave->amoeba_timer_wait_for_hatching = level->bd_amoeba_wait_for_hatching;
3942 cave->amoeba_timer_started_immediately= level->bd_amoeba_start_immediately;
3943 cave->amoeba_2_explodes_by_amoeba = level->bd_amoeba_2_explode_by_amoeba;
3944 cave->level_amoeba_threshold[0] = level->bd_amoeba_threshold_too_big;
3945 cave->level_amoeba_time[0] = level->bd_amoeba_slow_growth_time;
3946 cave->amoeba_growth_prob = level->bd_amoeba_slow_growth_rate * 10000;
3947 cave->amoeba_fast_growth_prob = level->bd_amoeba_fast_growth_rate * 10000;
3948 cave->level_amoeba_2_threshold[0] = level->bd_amoeba_2_threshold_too_big;
3949 cave->level_amoeba_2_time[0] = level->bd_amoeba_2_slow_growth_time;
3950 cave->amoeba_2_growth_prob = level->bd_amoeba_2_slow_growth_rate * 10000;
3951 cave->amoeba_2_fast_growth_prob = level->bd_amoeba_2_fast_growth_rate * 10000;
3953 cave->amoeba_too_big_effect = map_element_RND_to_BD(level->bd_amoeba_content_too_big);
3954 cave->amoeba_enclosed_effect = map_element_RND_to_BD(level->bd_amoeba_content_enclosed);
3955 cave->amoeba_2_too_big_effect = map_element_RND_to_BD(level->bd_amoeba_2_content_too_big);
3956 cave->amoeba_2_enclosed_effect = map_element_RND_to_BD(level->bd_amoeba_2_content_enclosed);
3957 cave->amoeba_2_explosion_effect = map_element_RND_to_BD(level->bd_amoeba_2_content_exploding);
3958 cave->amoeba_2_looks_like = map_element_RND_to_BD(level->bd_amoeba_2_content_looks_like);
3961 strncpy(cave->name, level->name, sizeof(GdString));
3962 cave->name[sizeof(GdString) - 1] = '\0';
3964 // playfield elements
3965 for (x = 0; x < cave->w; x++)
3966 for (y = 0; y < cave->h; y++)
3967 cave->map[y][x] = map_element_RND_to_BD(level->field[x][y]);
3970 static void CopyNativeLevel_BD_to_RND(struct LevelInfo *level)
3972 struct LevelInfo_BD *level_bd = level->native_bd_level;
3973 GdCave *cave = level_bd->cave;
3974 int bd_level_nr = level_bd->level_nr;
3977 level->fieldx = MIN(cave->w, MAX_LEV_FIELDX);
3978 level->fieldy = MIN(cave->h, MAX_LEV_FIELDY);
3981 level->bd_intermission = cave->intermission;
3984 level->time = cave->level_time[bd_level_nr];
3985 level->gems_needed = cave->level_diamonds[bd_level_nr];
3988 level->bd_scheduling_type = cave->scheduling;
3989 level->bd_pal_timing = cave->pal_timing;
3990 level->bd_cycle_delay_ms = cave->level_speed[bd_level_nr];
3991 level->bd_cycle_delay_c64 = cave->level_ckdelay[bd_level_nr];
3992 level->bd_hatching_delay_cycles = cave->level_hatching_delay_frame[bd_level_nr];
3993 level->bd_hatching_delay_seconds = cave->level_hatching_delay_time[bd_level_nr];
3996 level->score[SC_TIME_BONUS] = cave->level_timevalue[bd_level_nr];
3997 level->score[SC_EMERALD] = cave->diamond_value;
3998 level->score[SC_DIAMOND_EXTRA] = cave->extra_diamond_value;
4000 // compatibility settings
4001 level->bd_line_shifting_borders = cave->lineshift;
4002 level->bd_scan_first_and_last_row = cave->border_scan_first_and_last;
4003 level->bd_short_explosions = cave->short_explosions;
4004 level->bd_gravity_affects_all = cave->gravity_affects_all;
4006 // player properties
4007 level->bd_diagonal_movements = cave->diagonal_movements;
4008 level->bd_topmost_player_active = cave->active_is_first_found;
4009 level->bd_pushing_prob = cave->pushing_stone_prob / 10000;
4010 level->bd_pushing_prob_with_sweet = cave->pushing_stone_prob_sweet / 10000;
4011 level->bd_push_mega_rock_with_sweet = cave->mega_stones_pushable_with_sweet;
4013 // element properties
4014 level->bd_clock_extra_time = cave->level_bonus_time[0];
4015 level->bd_voodoo_collects_diamonds = cave->voodoo_collects_diamonds;
4016 level->bd_voodoo_hurt_kills_player = cave->voodoo_any_hurt_kills_player;
4017 level->bd_voodoo_dies_by_rock = cave->voodoo_dies_by_stone;
4018 level->bd_voodoo_vanish_by_explosion = cave->voodoo_disappear_in_explosion;
4019 level->bd_voodoo_penalty_time = cave->level_penalty_time[0];
4020 level->time_magic_wall = cave->level_magic_wall_time[bd_level_nr];
4021 level->bd_magic_wall_wait_hatching = cave->magic_timer_wait_for_hatching;
4022 level->bd_magic_wall_stops_amoeba = cave->magic_wall_stops_amoeba;
4023 level->bd_amoeba_wait_for_hatching = cave->amoeba_timer_wait_for_hatching;
4024 level->bd_amoeba_start_immediately = cave->amoeba_timer_started_immediately;
4025 level->bd_amoeba_2_explode_by_amoeba = cave->amoeba_2_explodes_by_amoeba;
4026 level->bd_amoeba_threshold_too_big = cave->level_amoeba_threshold[0];
4027 level->bd_amoeba_slow_growth_time = cave->level_amoeba_time[0];
4028 level->bd_amoeba_slow_growth_rate = cave->amoeba_growth_prob / 10000;
4029 level->bd_amoeba_fast_growth_rate = cave->amoeba_fast_growth_prob / 10000;
4030 level->bd_amoeba_2_threshold_too_big = cave->level_amoeba_2_threshold[0];
4031 level->bd_amoeba_2_slow_growth_time = cave->level_amoeba_2_time[0];
4032 level->bd_amoeba_2_slow_growth_rate = cave->amoeba_2_growth_prob / 10000;
4033 level->bd_amoeba_2_fast_growth_rate = cave->amoeba_2_fast_growth_prob / 10000;
4035 level->bd_amoeba_content_too_big = map_element_BD_to_RND(cave->amoeba_too_big_effect);
4036 level->bd_amoeba_content_enclosed = map_element_BD_to_RND(cave->amoeba_enclosed_effect);
4037 level->bd_amoeba_2_content_too_big = map_element_BD_to_RND(cave->amoeba_2_too_big_effect);
4038 level->bd_amoeba_2_content_enclosed = map_element_BD_to_RND(cave->amoeba_2_enclosed_effect);
4039 level->bd_amoeba_2_content_exploding = map_element_BD_to_RND(cave->amoeba_2_explosion_effect);
4040 level->bd_amoeba_2_content_looks_like = map_element_BD_to_RND(cave->amoeba_2_looks_like);
4043 char *cave_name = getStringPrint("%s / %d", cave->name, bd_level_nr + 1);
4045 strncpy(level->name, cave_name, MAX_LEVEL_NAME_LEN);
4046 level->name[MAX_LEVEL_NAME_LEN] = '\0';
4048 // playfield elements
4049 for (x = 0; x < level->fieldx; x++)
4050 for (y = 0; y < level->fieldy; y++)
4051 level->field[x][y] = map_element_BD_to_RND(cave->map[y][x]);
4053 checked_free(cave_name);
4056 static void setTapeInfoToDefaults(void);
4058 static void CopyNativeTape_BD_to_RND(struct LevelInfo *level)
4060 struct LevelInfo_BD *level_bd = level->native_bd_level;
4061 GdCave *cave = level_bd->cave;
4062 GdReplay *replay = level_bd->replay;
4068 // always start with reliable default values
4069 setTapeInfoToDefaults();
4071 tape.level_nr = level_nr; // (currently not used)
4072 tape.random_seed = replay->seed;
4074 TapeSetDateFromIsoDateString(replay->date);
4077 tape.pos[tape.counter].delay = 0;
4079 tape.bd_replay = TRUE;
4081 // all time calculations only used to display approximate tape time
4082 int cave_speed = cave->speed;
4083 int milliseconds_game = 0;
4084 int milliseconds_elapsed = 20;
4086 for (i = 0; i < replay->movements->len; i++)
4088 int replay_action = replay->movements->data[i];
4089 int tape_action = map_action_BD_to_RND(replay_action);
4090 byte action[MAX_TAPE_ACTIONS] = { tape_action };
4091 boolean success = 0;
4095 success = TapeAddAction(action);
4097 milliseconds_game += milliseconds_elapsed;
4099 if (milliseconds_game >= cave_speed)
4101 milliseconds_game -= cave_speed;
4108 tape.pos[tape.counter].delay = 0;
4109 tape.pos[tape.counter].action[0] = 0;
4113 Warn("BD replay truncated: size exceeds maximum tape size %d", MAX_TAPE_LEN);
4119 TapeHaltRecording();
4123 // ----------------------------------------------------------------------------
4124 // functions for loading EM level
4125 // ----------------------------------------------------------------------------
4127 static void CopyNativeLevel_RND_to_EM(struct LevelInfo *level)
4129 static int ball_xy[8][2] =
4140 struct LevelInfo_EM *level_em = level->native_em_level;
4141 struct CAVE *cav = level_em->cav;
4144 cav->width = MIN(level->fieldx, MAX_PLAYFIELD_WIDTH);
4145 cav->height = MIN(level->fieldy, MAX_PLAYFIELD_HEIGHT);
4147 cav->time_seconds = level->time;
4148 cav->gems_needed = level->gems_needed;
4150 cav->emerald_score = level->score[SC_EMERALD];
4151 cav->diamond_score = level->score[SC_DIAMOND];
4152 cav->alien_score = level->score[SC_ROBOT];
4153 cav->tank_score = level->score[SC_SPACESHIP];
4154 cav->bug_score = level->score[SC_BUG];
4155 cav->eater_score = level->score[SC_YAMYAM];
4156 cav->nut_score = level->score[SC_NUT];
4157 cav->dynamite_score = level->score[SC_DYNAMITE];
4158 cav->key_score = level->score[SC_KEY];
4159 cav->exit_score = level->score[SC_TIME_BONUS];
4161 cav->num_eater_arrays = level->num_yamyam_contents;
4163 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
4164 for (y = 0; y < 3; y++)
4165 for (x = 0; x < 3; x++)
4166 cav->eater_array[i][y * 3 + x] =
4167 map_element_RND_to_EM_cave(level->yamyam_content[i].e[x][y]);
4169 cav->amoeba_time = level->amoeba_speed;
4170 cav->wonderwall_time = level->time_magic_wall;
4171 cav->wheel_time = level->time_wheel;
4173 cav->android_move_time = level->android_move_time;
4174 cav->android_clone_time = level->android_clone_time;
4175 cav->ball_random = level->ball_random;
4176 cav->ball_active = level->ball_active_initial;
4177 cav->ball_time = level->ball_time;
4178 cav->num_ball_arrays = level->num_ball_contents;
4180 cav->lenses_score = level->lenses_score;
4181 cav->magnify_score = level->magnify_score;
4182 cav->slurp_score = level->slurp_score;
4184 cav->lenses_time = level->lenses_time;
4185 cav->magnify_time = level->magnify_time;
4187 cav->wind_time = 9999;
4188 cav->wind_direction =
4189 map_direction_RND_to_EM(level->wind_direction_initial);
4191 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
4192 for (j = 0; j < 8; j++)
4193 cav->ball_array[i][j] =
4194 map_element_RND_to_EM_cave(level->ball_content[i].
4195 e[ball_xy[j][0]][ball_xy[j][1]]);
4197 map_android_clone_elements_RND_to_EM(level);
4199 // first fill the complete playfield with the empty space element
4200 for (y = 0; y < EM_MAX_CAVE_HEIGHT; y++)
4201 for (x = 0; x < EM_MAX_CAVE_WIDTH; x++)
4202 cav->cave[x][y] = Cblank;
4204 // then copy the real level contents from level file into the playfield
4205 for (y = 0; y < cav->height; y++) for (x = 0; x < cav->width; x++)
4207 int new_element = map_element_RND_to_EM_cave(level->field[x][y]);
4209 if (level->field[x][y] == EL_AMOEBA_DEAD)
4210 new_element = map_element_RND_to_EM_cave(EL_AMOEBA_WET);
4212 cav->cave[x][y] = new_element;
4215 for (i = 0; i < MAX_PLAYERS; i++)
4217 cav->player_x[i] = -1;
4218 cav->player_y[i] = -1;
4221 // initialize player positions and delete players from the playfield
4222 for (y = 0; y < cav->height; y++) for (x = 0; x < cav->width; x++)
4224 if (IS_PLAYER_ELEMENT(level->field[x][y]))
4226 int player_nr = GET_PLAYER_NR(level->field[x][y]);
4228 cav->player_x[player_nr] = x;
4229 cav->player_y[player_nr] = y;
4231 cav->cave[x][y] = map_element_RND_to_EM_cave(EL_EMPTY);
4236 static void CopyNativeLevel_EM_to_RND(struct LevelInfo *level)
4238 static int ball_xy[8][2] =
4249 struct LevelInfo_EM *level_em = level->native_em_level;
4250 struct CAVE *cav = level_em->cav;
4253 level->fieldx = MIN(cav->width, MAX_LEV_FIELDX);
4254 level->fieldy = MIN(cav->height, MAX_LEV_FIELDY);
4256 level->time = cav->time_seconds;
4257 level->gems_needed = cav->gems_needed;
4259 sprintf(level->name, "Level %d", level->file_info.nr);
4261 level->score[SC_EMERALD] = cav->emerald_score;
4262 level->score[SC_DIAMOND] = cav->diamond_score;
4263 level->score[SC_ROBOT] = cav->alien_score;
4264 level->score[SC_SPACESHIP] = cav->tank_score;
4265 level->score[SC_BUG] = cav->bug_score;
4266 level->score[SC_YAMYAM] = cav->eater_score;
4267 level->score[SC_NUT] = cav->nut_score;
4268 level->score[SC_DYNAMITE] = cav->dynamite_score;
4269 level->score[SC_KEY] = cav->key_score;
4270 level->score[SC_TIME_BONUS] = cav->exit_score;
4272 level->num_yamyam_contents = cav->num_eater_arrays;
4274 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
4275 for (y = 0; y < 3; y++)
4276 for (x = 0; x < 3; x++)
4277 level->yamyam_content[i].e[x][y] =
4278 map_element_EM_to_RND_cave(cav->eater_array[i][y * 3 + x]);
4280 level->amoeba_speed = cav->amoeba_time;
4281 level->time_magic_wall = cav->wonderwall_time;
4282 level->time_wheel = cav->wheel_time;
4284 level->android_move_time = cav->android_move_time;
4285 level->android_clone_time = cav->android_clone_time;
4286 level->ball_random = cav->ball_random;
4287 level->ball_active_initial = cav->ball_active;
4288 level->ball_time = cav->ball_time;
4289 level->num_ball_contents = cav->num_ball_arrays;
4291 level->lenses_score = cav->lenses_score;
4292 level->magnify_score = cav->magnify_score;
4293 level->slurp_score = cav->slurp_score;
4295 level->lenses_time = cav->lenses_time;
4296 level->magnify_time = cav->magnify_time;
4298 level->wind_direction_initial =
4299 map_direction_EM_to_RND(cav->wind_direction);
4301 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
4302 for (j = 0; j < 8; j++)
4303 level->ball_content[i].e[ball_xy[j][0]][ball_xy[j][1]] =
4304 map_element_EM_to_RND_cave(cav->ball_array[i][j]);
4306 map_android_clone_elements_EM_to_RND(level);
4308 // convert the playfield (some elements need special treatment)
4309 for (y = 0; y < level->fieldy; y++) for (x = 0; x < level->fieldx; x++)
4311 int new_element = map_element_EM_to_RND_cave(cav->cave[x][y]);
4313 if (new_element == EL_AMOEBA_WET && level->amoeba_speed == 0)
4314 new_element = EL_AMOEBA_DEAD;
4316 level->field[x][y] = new_element;
4319 for (i = 0; i < MAX_PLAYERS; i++)
4321 // in case of all players set to the same field, use the first player
4322 int nr = MAX_PLAYERS - i - 1;
4323 int jx = cav->player_x[nr];
4324 int jy = cav->player_y[nr];
4326 if (jx != -1 && jy != -1)
4327 level->field[jx][jy] = EL_PLAYER_1 + nr;
4330 // time score is counted for each 10 seconds left in Emerald Mine levels
4331 level->time_score_base = 10;
4335 // ----------------------------------------------------------------------------
4336 // functions for loading SP level
4337 // ----------------------------------------------------------------------------
4339 static void CopyNativeLevel_RND_to_SP(struct LevelInfo *level)
4341 struct LevelInfo_SP *level_sp = level->native_sp_level;
4342 LevelInfoType *header = &level_sp->header;
4345 level_sp->width = level->fieldx;
4346 level_sp->height = level->fieldy;
4348 for (x = 0; x < level->fieldx; x++)
4349 for (y = 0; y < level->fieldy; y++)
4350 level_sp->playfield[x][y] = map_element_RND_to_SP(level->field[x][y]);
4352 header->InitialGravity = (level->initial_player_gravity[0] ? 1 : 0);
4354 for (i = 0; i < SP_LEVEL_NAME_LEN; i++)
4355 header->LevelTitle[i] = level->name[i];
4356 // !!! NO STRING TERMINATION IN SUPAPLEX VB CODE YET -- FIX THIS !!!
4358 header->InfotronsNeeded = level->gems_needed;
4360 header->SpecialPortCount = 0;
4362 for (x = 0; x < level->fieldx; x++) for (y = 0; y < level->fieldy; y++)
4364 boolean gravity_port_found = FALSE;
4365 boolean gravity_port_valid = FALSE;
4366 int gravity_port_flag;
4367 int gravity_port_base_element;
4368 int element = level->field[x][y];
4370 if (element >= EL_SP_GRAVITY_ON_PORT_RIGHT &&
4371 element <= EL_SP_GRAVITY_ON_PORT_UP)
4373 gravity_port_found = TRUE;
4374 gravity_port_valid = TRUE;
4375 gravity_port_flag = 1;
4376 gravity_port_base_element = EL_SP_GRAVITY_ON_PORT_RIGHT;
4378 else if (element >= EL_SP_GRAVITY_OFF_PORT_RIGHT &&
4379 element <= EL_SP_GRAVITY_OFF_PORT_UP)
4381 gravity_port_found = TRUE;
4382 gravity_port_valid = TRUE;
4383 gravity_port_flag = 0;
4384 gravity_port_base_element = EL_SP_GRAVITY_OFF_PORT_RIGHT;
4386 else if (element >= EL_SP_GRAVITY_PORT_RIGHT &&
4387 element <= EL_SP_GRAVITY_PORT_UP)
4389 // change R'n'D style gravity inverting special port to normal port
4390 // (there are no gravity inverting ports in native Supaplex engine)
4392 gravity_port_found = TRUE;
4393 gravity_port_valid = FALSE;
4394 gravity_port_base_element = EL_SP_GRAVITY_PORT_RIGHT;
4397 if (gravity_port_found)
4399 if (gravity_port_valid &&
4400 header->SpecialPortCount < SP_MAX_SPECIAL_PORTS)
4402 SpecialPortType *port = &header->SpecialPort[header->SpecialPortCount];
4404 port->PortLocation = (y * level->fieldx + x) * 2;
4405 port->Gravity = gravity_port_flag;
4407 element += EL_SP_GRAVITY_PORT_RIGHT - gravity_port_base_element;
4409 header->SpecialPortCount++;
4413 // change special gravity port to normal port
4415 element += EL_SP_PORT_RIGHT - gravity_port_base_element;
4418 level_sp->playfield[x][y] = element - EL_SP_START;
4423 static void CopyNativeLevel_SP_to_RND(struct LevelInfo *level)
4425 struct LevelInfo_SP *level_sp = level->native_sp_level;
4426 LevelInfoType *header = &level_sp->header;
4427 boolean num_invalid_elements = 0;
4430 level->fieldx = level_sp->width;
4431 level->fieldy = level_sp->height;
4433 for (x = 0; x < level->fieldx; x++)
4435 for (y = 0; y < level->fieldy; y++)
4437 int element_old = level_sp->playfield[x][y];
4438 int element_new = getMappedElement(map_element_SP_to_RND(element_old));
4440 if (element_new == EL_UNKNOWN)
4442 num_invalid_elements++;
4444 Debug("level:native:SP", "invalid element %d at position %d, %d",
4448 level->field[x][y] = element_new;
4452 if (num_invalid_elements > 0)
4453 Warn("found %d invalid elements%s", num_invalid_elements,
4454 (!options.debug ? " (use '--debug' for more details)" : ""));
4456 for (i = 0; i < MAX_PLAYERS; i++)
4457 level->initial_player_gravity[i] =
4458 (header->InitialGravity == 1 ? TRUE : FALSE);
4460 // skip leading spaces
4461 for (i = 0; i < SP_LEVEL_NAME_LEN; i++)
4462 if (header->LevelTitle[i] != ' ')
4466 for (j = 0; i < SP_LEVEL_NAME_LEN; i++, j++)
4467 level->name[j] = header->LevelTitle[i];
4468 level->name[j] = '\0';
4470 // cut trailing spaces
4472 if (level->name[j - 1] == ' ' && level->name[j] == '\0')
4473 level->name[j - 1] = '\0';
4475 level->gems_needed = header->InfotronsNeeded;
4477 for (i = 0; i < header->SpecialPortCount; i++)
4479 SpecialPortType *port = &header->SpecialPort[i];
4480 int port_location = port->PortLocation;
4481 int gravity = port->Gravity;
4482 int port_x, port_y, port_element;
4484 port_x = (port_location / 2) % level->fieldx;
4485 port_y = (port_location / 2) / level->fieldx;
4487 if (port_x < 0 || port_x >= level->fieldx ||
4488 port_y < 0 || port_y >= level->fieldy)
4490 Warn("special port position (%d, %d) out of bounds", port_x, port_y);
4495 port_element = level->field[port_x][port_y];
4497 if (port_element < EL_SP_GRAVITY_PORT_RIGHT ||
4498 port_element > EL_SP_GRAVITY_PORT_UP)
4500 Warn("no special port at position (%d, %d)", port_x, port_y);
4505 // change previous (wrong) gravity inverting special port to either
4506 // gravity enabling special port or gravity disabling special port
4507 level->field[port_x][port_y] +=
4508 (gravity == 1 ? EL_SP_GRAVITY_ON_PORT_RIGHT :
4509 EL_SP_GRAVITY_OFF_PORT_RIGHT) - EL_SP_GRAVITY_PORT_RIGHT;
4512 // change special gravity ports without database entries to normal ports
4513 for (x = 0; x < level->fieldx; x++)
4514 for (y = 0; y < level->fieldy; y++)
4515 if (level->field[x][y] >= EL_SP_GRAVITY_PORT_RIGHT &&
4516 level->field[x][y] <= EL_SP_GRAVITY_PORT_UP)
4517 level->field[x][y] += EL_SP_PORT_RIGHT - EL_SP_GRAVITY_PORT_RIGHT;
4519 level->time = 0; // no time limit
4520 level->amoeba_speed = 0;
4521 level->time_magic_wall = 0;
4522 level->time_wheel = 0;
4523 level->amoeba_content = EL_EMPTY;
4525 // original Supaplex does not use score values -- rate by playing time
4526 for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
4527 level->score[i] = 0;
4529 level->rate_time_over_score = TRUE;
4531 // there are no yamyams in supaplex levels
4532 for (i = 0; i < level->num_yamyam_contents; i++)
4533 for (x = 0; x < 3; x++)
4534 for (y = 0; y < 3; y++)
4535 level->yamyam_content[i].e[x][y] = EL_EMPTY;
4538 static void CopyNativeTape_RND_to_SP(struct LevelInfo *level)
4540 struct LevelInfo_SP *level_sp = level->native_sp_level;
4541 struct DemoInfo_SP *demo = &level_sp->demo;
4544 // always start with reliable default values
4545 demo->is_available = FALSE;
4548 if (TAPE_IS_EMPTY(tape))
4551 demo->level_nr = tape.level_nr; // (currently not used)
4553 level_sp->header.DemoRandomSeed = tape.random_seed;
4557 for (i = 0; i < tape.length; i++)
4559 int demo_action = map_key_RND_to_SP(tape.pos[i].action[0]);
4560 int demo_repeat = tape.pos[i].delay;
4561 int demo_entries = (demo_repeat + 15) / 16;
4563 if (demo->length + demo_entries >= SP_MAX_TAPE_LEN)
4565 Warn("tape truncated: size exceeds maximum SP demo size %d",
4571 for (j = 0; j < demo_repeat / 16; j++)
4572 demo->data[demo->length++] = 0xf0 | demo_action;
4574 if (demo_repeat % 16)
4575 demo->data[demo->length++] = ((demo_repeat % 16 - 1) << 4) | demo_action;
4578 demo->is_available = TRUE;
4581 static void CopyNativeTape_SP_to_RND(struct LevelInfo *level)
4583 struct LevelInfo_SP *level_sp = level->native_sp_level;
4584 struct DemoInfo_SP *demo = &level_sp->demo;
4585 char *filename = level->file_info.filename;
4588 // always start with reliable default values
4589 setTapeInfoToDefaults();
4591 if (!demo->is_available)
4594 tape.level_nr = demo->level_nr; // (currently not used)
4595 tape.random_seed = level_sp->header.DemoRandomSeed;
4597 TapeSetDateFromEpochSeconds(getFileTimestampEpochSeconds(filename));
4600 tape.pos[tape.counter].delay = 0;
4602 for (i = 0; i < demo->length; i++)
4604 int demo_action = demo->data[i] & 0x0f;
4605 int demo_repeat = (demo->data[i] & 0xf0) >> 4;
4606 int tape_action = map_key_SP_to_RND(demo_action);
4607 int tape_repeat = demo_repeat + 1;
4608 byte action[MAX_TAPE_ACTIONS] = { tape_action };
4609 boolean success = 0;
4612 for (j = 0; j < tape_repeat; j++)
4613 success = TapeAddAction(action);
4617 Warn("SP demo truncated: size exceeds maximum tape size %d",
4624 TapeHaltRecording();
4628 // ----------------------------------------------------------------------------
4629 // functions for loading MM level
4630 // ----------------------------------------------------------------------------
4632 static void CopyNativeLevel_RND_to_MM(struct LevelInfo *level)
4634 struct LevelInfo_MM *level_mm = level->native_mm_level;
4637 level_mm->fieldx = MIN(level->fieldx, MM_MAX_PLAYFIELD_WIDTH);
4638 level_mm->fieldy = MIN(level->fieldy, MM_MAX_PLAYFIELD_HEIGHT);
4640 level_mm->time = level->time;
4641 level_mm->kettles_needed = level->gems_needed;
4642 level_mm->auto_count_kettles = level->auto_count_gems;
4644 level_mm->mm_laser_red = level->mm_laser_red;
4645 level_mm->mm_laser_green = level->mm_laser_green;
4646 level_mm->mm_laser_blue = level->mm_laser_blue;
4648 level_mm->df_laser_red = level->df_laser_red;
4649 level_mm->df_laser_green = level->df_laser_green;
4650 level_mm->df_laser_blue = level->df_laser_blue;
4652 strcpy(level_mm->name, level->name);
4653 strcpy(level_mm->author, level->author);
4655 level_mm->score[SC_EMERALD] = level->score[SC_EMERALD];
4656 level_mm->score[SC_PACMAN] = level->score[SC_PACMAN];
4657 level_mm->score[SC_KEY] = level->score[SC_KEY];
4658 level_mm->score[SC_TIME_BONUS] = level->score[SC_TIME_BONUS];
4659 level_mm->score[SC_ELEM_BONUS] = level->score[SC_ELEM_BONUS];
4661 level_mm->amoeba_speed = level->amoeba_speed;
4662 level_mm->time_fuse = level->mm_time_fuse;
4663 level_mm->time_bomb = level->mm_time_bomb;
4664 level_mm->time_ball = level->mm_time_ball;
4665 level_mm->time_block = level->mm_time_block;
4667 level_mm->num_ball_contents = level->num_mm_ball_contents;
4668 level_mm->ball_choice_mode = level->mm_ball_choice_mode;
4669 level_mm->rotate_ball_content = level->rotate_mm_ball_content;
4670 level_mm->explode_ball = level->explode_mm_ball;
4672 for (i = 0; i < level->num_mm_ball_contents; i++)
4673 level_mm->ball_content[i] =
4674 map_element_RND_to_MM(level->mm_ball_content[i]);
4676 for (x = 0; x < level->fieldx; x++)
4677 for (y = 0; y < level->fieldy; y++)
4679 level_mm->field[x][y] = map_element_RND_to_MM(level->field[x][y]);
4682 static void CopyNativeLevel_MM_to_RND(struct LevelInfo *level)
4684 struct LevelInfo_MM *level_mm = level->native_mm_level;
4687 level->fieldx = MIN(level_mm->fieldx, MAX_LEV_FIELDX);
4688 level->fieldy = MIN(level_mm->fieldy, MAX_LEV_FIELDY);
4690 level->time = level_mm->time;
4691 level->gems_needed = level_mm->kettles_needed;
4692 level->auto_count_gems = level_mm->auto_count_kettles;
4694 level->mm_laser_red = level_mm->mm_laser_red;
4695 level->mm_laser_green = level_mm->mm_laser_green;
4696 level->mm_laser_blue = level_mm->mm_laser_blue;
4698 level->df_laser_red = level_mm->df_laser_red;
4699 level->df_laser_green = level_mm->df_laser_green;
4700 level->df_laser_blue = level_mm->df_laser_blue;
4702 strcpy(level->name, level_mm->name);
4704 // only overwrite author from 'levelinfo.conf' if author defined in level
4705 if (!strEqual(level_mm->author, ANONYMOUS_NAME))
4706 strcpy(level->author, level_mm->author);
4708 level->score[SC_EMERALD] = level_mm->score[SC_EMERALD];
4709 level->score[SC_PACMAN] = level_mm->score[SC_PACMAN];
4710 level->score[SC_KEY] = level_mm->score[SC_KEY];
4711 level->score[SC_TIME_BONUS] = level_mm->score[SC_TIME_BONUS];
4712 level->score[SC_ELEM_BONUS] = level_mm->score[SC_ELEM_BONUS];
4714 level->amoeba_speed = level_mm->amoeba_speed;
4715 level->mm_time_fuse = level_mm->time_fuse;
4716 level->mm_time_bomb = level_mm->time_bomb;
4717 level->mm_time_ball = level_mm->time_ball;
4718 level->mm_time_block = level_mm->time_block;
4720 level->num_mm_ball_contents = level_mm->num_ball_contents;
4721 level->mm_ball_choice_mode = level_mm->ball_choice_mode;
4722 level->rotate_mm_ball_content = level_mm->rotate_ball_content;
4723 level->explode_mm_ball = level_mm->explode_ball;
4725 for (i = 0; i < level->num_mm_ball_contents; i++)
4726 level->mm_ball_content[i] =
4727 map_element_MM_to_RND(level_mm->ball_content[i]);
4729 for (x = 0; x < level->fieldx; x++)
4730 for (y = 0; y < level->fieldy; y++)
4731 level->field[x][y] = map_element_MM_to_RND(level_mm->field[x][y]);
4735 // ----------------------------------------------------------------------------
4736 // functions for loading DC level
4737 // ----------------------------------------------------------------------------
4739 #define DC_LEVEL_HEADER_SIZE 344
4741 static unsigned short getDecodedWord_DC(unsigned short data_encoded,
4744 static int last_data_encoded;
4748 int diff_hi, diff_lo;
4749 int data_hi, data_lo;
4750 unsigned short data_decoded;
4754 last_data_encoded = 0;
4761 diff = data_encoded - last_data_encoded;
4762 diff_hi = diff & ~0xff;
4763 diff_lo = diff & 0xff;
4767 data_hi = diff_hi - (offset1 << 8) + (offset2 & 0xff00);
4768 data_lo = (diff_lo + (data_hi >> 16)) & 0x00ff;
4769 data_hi = data_hi & 0xff00;
4771 data_decoded = data_hi | data_lo;
4773 last_data_encoded = data_encoded;
4775 offset1 = (offset1 + 1) % 31;
4776 offset2 = offset2 & 0xff;
4778 return data_decoded;
4781 static int getMappedElement_DC(int element)
4789 // 0x0117 - 0x036e: (?)
4792 // 0x042d - 0x0684: (?)
4808 element = EL_CRYSTAL;
4811 case 0x0e77: // quicksand (boulder)
4812 element = EL_QUICKSAND_FAST_FULL;
4815 case 0x0e99: // slow quicksand (boulder)
4816 element = EL_QUICKSAND_FULL;
4820 element = EL_EM_EXIT_OPEN;
4824 element = EL_EM_EXIT_CLOSED;
4828 element = EL_EM_STEEL_EXIT_OPEN;
4832 element = EL_EM_STEEL_EXIT_CLOSED;
4835 case 0x0f4f: // dynamite (lit 1)
4836 element = EL_EM_DYNAMITE_ACTIVE;
4839 case 0x0f57: // dynamite (lit 2)
4840 element = EL_EM_DYNAMITE_ACTIVE;
4843 case 0x0f5f: // dynamite (lit 3)
4844 element = EL_EM_DYNAMITE_ACTIVE;
4847 case 0x0f67: // dynamite (lit 4)
4848 element = EL_EM_DYNAMITE_ACTIVE;
4855 element = EL_AMOEBA_WET;
4859 element = EL_AMOEBA_DROP;
4863 element = EL_DC_MAGIC_WALL;
4867 element = EL_SPACESHIP_UP;
4871 element = EL_SPACESHIP_DOWN;
4875 element = EL_SPACESHIP_LEFT;
4879 element = EL_SPACESHIP_RIGHT;
4883 element = EL_BUG_UP;
4887 element = EL_BUG_DOWN;
4891 element = EL_BUG_LEFT;
4895 element = EL_BUG_RIGHT;
4899 element = EL_MOLE_UP;
4903 element = EL_MOLE_DOWN;
4907 element = EL_MOLE_LEFT;
4911 element = EL_MOLE_RIGHT;
4919 element = EL_YAMYAM_UP;
4923 element = EL_SWITCHGATE_OPEN;
4927 element = EL_SWITCHGATE_CLOSED;
4931 element = EL_DC_SWITCHGATE_SWITCH_UP;
4935 element = EL_TIMEGATE_CLOSED;
4938 case 0x144c: // conveyor belt switch (green)
4939 element = EL_CONVEYOR_BELT_3_SWITCH_MIDDLE;
4942 case 0x144f: // conveyor belt switch (red)
4943 element = EL_CONVEYOR_BELT_1_SWITCH_MIDDLE;
4946 case 0x1452: // conveyor belt switch (blue)
4947 element = EL_CONVEYOR_BELT_4_SWITCH_MIDDLE;
4951 element = EL_CONVEYOR_BELT_3_MIDDLE;
4955 element = EL_CONVEYOR_BELT_3_LEFT;
4959 element = EL_CONVEYOR_BELT_3_RIGHT;
4963 element = EL_CONVEYOR_BELT_1_MIDDLE;
4967 element = EL_CONVEYOR_BELT_1_LEFT;
4971 element = EL_CONVEYOR_BELT_1_RIGHT;
4975 element = EL_CONVEYOR_BELT_4_MIDDLE;
4979 element = EL_CONVEYOR_BELT_4_LEFT;
4983 element = EL_CONVEYOR_BELT_4_RIGHT;
4987 element = EL_EXPANDABLE_WALL_HORIZONTAL;
4991 element = EL_EXPANDABLE_WALL_VERTICAL;
4995 element = EL_EXPANDABLE_WALL_ANY;
4998 case 0x14ce: // growing steel wall (left/right)
4999 element = EL_EXPANDABLE_STEELWALL_HORIZONTAL;
5002 case 0x14df: // growing steel wall (up/down)
5003 element = EL_EXPANDABLE_STEELWALL_VERTICAL;
5006 case 0x14e8: // growing steel wall (up/down/left/right)
5007 element = EL_EXPANDABLE_STEELWALL_ANY;
5011 element = EL_SHIELD_DEADLY;
5015 element = EL_EXTRA_TIME;
5023 element = EL_EMPTY_SPACE;
5026 case 0x1578: // quicksand (empty)
5027 element = EL_QUICKSAND_FAST_EMPTY;
5030 case 0x1579: // slow quicksand (empty)
5031 element = EL_QUICKSAND_EMPTY;
5041 element = EL_EM_DYNAMITE;
5044 case 0x15a1: // key (red)
5045 element = EL_EM_KEY_1;
5048 case 0x15a2: // key (yellow)
5049 element = EL_EM_KEY_2;
5052 case 0x15a3: // key (blue)
5053 element = EL_EM_KEY_4;
5056 case 0x15a4: // key (green)
5057 element = EL_EM_KEY_3;
5060 case 0x15a5: // key (white)
5061 element = EL_DC_KEY_WHITE;
5065 element = EL_WALL_SLIPPERY;
5072 case 0x15a8: // wall (not round)
5076 case 0x15a9: // (blue)
5077 element = EL_CHAR_A;
5080 case 0x15aa: // (blue)
5081 element = EL_CHAR_B;
5084 case 0x15ab: // (blue)
5085 element = EL_CHAR_C;
5088 case 0x15ac: // (blue)
5089 element = EL_CHAR_D;
5092 case 0x15ad: // (blue)
5093 element = EL_CHAR_E;
5096 case 0x15ae: // (blue)
5097 element = EL_CHAR_F;
5100 case 0x15af: // (blue)
5101 element = EL_CHAR_G;
5104 case 0x15b0: // (blue)
5105 element = EL_CHAR_H;
5108 case 0x15b1: // (blue)
5109 element = EL_CHAR_I;
5112 case 0x15b2: // (blue)
5113 element = EL_CHAR_J;
5116 case 0x15b3: // (blue)
5117 element = EL_CHAR_K;
5120 case 0x15b4: // (blue)
5121 element = EL_CHAR_L;
5124 case 0x15b5: // (blue)
5125 element = EL_CHAR_M;
5128 case 0x15b6: // (blue)
5129 element = EL_CHAR_N;
5132 case 0x15b7: // (blue)
5133 element = EL_CHAR_O;
5136 case 0x15b8: // (blue)
5137 element = EL_CHAR_P;
5140 case 0x15b9: // (blue)
5141 element = EL_CHAR_Q;
5144 case 0x15ba: // (blue)
5145 element = EL_CHAR_R;
5148 case 0x15bb: // (blue)
5149 element = EL_CHAR_S;
5152 case 0x15bc: // (blue)
5153 element = EL_CHAR_T;
5156 case 0x15bd: // (blue)
5157 element = EL_CHAR_U;
5160 case 0x15be: // (blue)
5161 element = EL_CHAR_V;
5164 case 0x15bf: // (blue)
5165 element = EL_CHAR_W;
5168 case 0x15c0: // (blue)
5169 element = EL_CHAR_X;
5172 case 0x15c1: // (blue)
5173 element = EL_CHAR_Y;
5176 case 0x15c2: // (blue)
5177 element = EL_CHAR_Z;
5180 case 0x15c3: // (blue)
5181 element = EL_CHAR_AUMLAUT;
5184 case 0x15c4: // (blue)
5185 element = EL_CHAR_OUMLAUT;
5188 case 0x15c5: // (blue)
5189 element = EL_CHAR_UUMLAUT;
5192 case 0x15c6: // (blue)
5193 element = EL_CHAR_0;
5196 case 0x15c7: // (blue)
5197 element = EL_CHAR_1;
5200 case 0x15c8: // (blue)
5201 element = EL_CHAR_2;
5204 case 0x15c9: // (blue)
5205 element = EL_CHAR_3;
5208 case 0x15ca: // (blue)
5209 element = EL_CHAR_4;
5212 case 0x15cb: // (blue)
5213 element = EL_CHAR_5;
5216 case 0x15cc: // (blue)
5217 element = EL_CHAR_6;
5220 case 0x15cd: // (blue)
5221 element = EL_CHAR_7;
5224 case 0x15ce: // (blue)
5225 element = EL_CHAR_8;
5228 case 0x15cf: // (blue)
5229 element = EL_CHAR_9;
5232 case 0x15d0: // (blue)
5233 element = EL_CHAR_PERIOD;
5236 case 0x15d1: // (blue)
5237 element = EL_CHAR_EXCLAM;
5240 case 0x15d2: // (blue)
5241 element = EL_CHAR_COLON;
5244 case 0x15d3: // (blue)
5245 element = EL_CHAR_LESS;
5248 case 0x15d4: // (blue)
5249 element = EL_CHAR_GREATER;
5252 case 0x15d5: // (blue)
5253 element = EL_CHAR_QUESTION;
5256 case 0x15d6: // (blue)
5257 element = EL_CHAR_COPYRIGHT;
5260 case 0x15d7: // (blue)
5261 element = EL_CHAR_UP;
5264 case 0x15d8: // (blue)
5265 element = EL_CHAR_DOWN;
5268 case 0x15d9: // (blue)
5269 element = EL_CHAR_BUTTON;
5272 case 0x15da: // (blue)
5273 element = EL_CHAR_PLUS;
5276 case 0x15db: // (blue)
5277 element = EL_CHAR_MINUS;
5280 case 0x15dc: // (blue)
5281 element = EL_CHAR_APOSTROPHE;
5284 case 0x15dd: // (blue)
5285 element = EL_CHAR_PARENLEFT;
5288 case 0x15de: // (blue)
5289 element = EL_CHAR_PARENRIGHT;
5292 case 0x15df: // (green)
5293 element = EL_CHAR_A;
5296 case 0x15e0: // (green)
5297 element = EL_CHAR_B;
5300 case 0x15e1: // (green)
5301 element = EL_CHAR_C;
5304 case 0x15e2: // (green)
5305 element = EL_CHAR_D;
5308 case 0x15e3: // (green)
5309 element = EL_CHAR_E;
5312 case 0x15e4: // (green)
5313 element = EL_CHAR_F;
5316 case 0x15e5: // (green)
5317 element = EL_CHAR_G;
5320 case 0x15e6: // (green)
5321 element = EL_CHAR_H;
5324 case 0x15e7: // (green)
5325 element = EL_CHAR_I;
5328 case 0x15e8: // (green)
5329 element = EL_CHAR_J;
5332 case 0x15e9: // (green)
5333 element = EL_CHAR_K;
5336 case 0x15ea: // (green)
5337 element = EL_CHAR_L;
5340 case 0x15eb: // (green)
5341 element = EL_CHAR_M;
5344 case 0x15ec: // (green)
5345 element = EL_CHAR_N;
5348 case 0x15ed: // (green)
5349 element = EL_CHAR_O;
5352 case 0x15ee: // (green)
5353 element = EL_CHAR_P;
5356 case 0x15ef: // (green)
5357 element = EL_CHAR_Q;
5360 case 0x15f0: // (green)
5361 element = EL_CHAR_R;
5364 case 0x15f1: // (green)
5365 element = EL_CHAR_S;
5368 case 0x15f2: // (green)
5369 element = EL_CHAR_T;
5372 case 0x15f3: // (green)
5373 element = EL_CHAR_U;
5376 case 0x15f4: // (green)
5377 element = EL_CHAR_V;
5380 case 0x15f5: // (green)
5381 element = EL_CHAR_W;
5384 case 0x15f6: // (green)
5385 element = EL_CHAR_X;
5388 case 0x15f7: // (green)
5389 element = EL_CHAR_Y;
5392 case 0x15f8: // (green)
5393 element = EL_CHAR_Z;
5396 case 0x15f9: // (green)
5397 element = EL_CHAR_AUMLAUT;
5400 case 0x15fa: // (green)
5401 element = EL_CHAR_OUMLAUT;
5404 case 0x15fb: // (green)
5405 element = EL_CHAR_UUMLAUT;
5408 case 0x15fc: // (green)
5409 element = EL_CHAR_0;
5412 case 0x15fd: // (green)
5413 element = EL_CHAR_1;
5416 case 0x15fe: // (green)
5417 element = EL_CHAR_2;
5420 case 0x15ff: // (green)
5421 element = EL_CHAR_3;
5424 case 0x1600: // (green)
5425 element = EL_CHAR_4;
5428 case 0x1601: // (green)
5429 element = EL_CHAR_5;
5432 case 0x1602: // (green)
5433 element = EL_CHAR_6;
5436 case 0x1603: // (green)
5437 element = EL_CHAR_7;
5440 case 0x1604: // (green)
5441 element = EL_CHAR_8;
5444 case 0x1605: // (green)
5445 element = EL_CHAR_9;
5448 case 0x1606: // (green)
5449 element = EL_CHAR_PERIOD;
5452 case 0x1607: // (green)
5453 element = EL_CHAR_EXCLAM;
5456 case 0x1608: // (green)
5457 element = EL_CHAR_COLON;
5460 case 0x1609: // (green)
5461 element = EL_CHAR_LESS;
5464 case 0x160a: // (green)
5465 element = EL_CHAR_GREATER;
5468 case 0x160b: // (green)
5469 element = EL_CHAR_QUESTION;
5472 case 0x160c: // (green)
5473 element = EL_CHAR_COPYRIGHT;
5476 case 0x160d: // (green)
5477 element = EL_CHAR_UP;
5480 case 0x160e: // (green)
5481 element = EL_CHAR_DOWN;
5484 case 0x160f: // (green)
5485 element = EL_CHAR_BUTTON;
5488 case 0x1610: // (green)
5489 element = EL_CHAR_PLUS;
5492 case 0x1611: // (green)
5493 element = EL_CHAR_MINUS;
5496 case 0x1612: // (green)
5497 element = EL_CHAR_APOSTROPHE;
5500 case 0x1613: // (green)
5501 element = EL_CHAR_PARENLEFT;
5504 case 0x1614: // (green)
5505 element = EL_CHAR_PARENRIGHT;
5508 case 0x1615: // (blue steel)
5509 element = EL_STEEL_CHAR_A;
5512 case 0x1616: // (blue steel)
5513 element = EL_STEEL_CHAR_B;
5516 case 0x1617: // (blue steel)
5517 element = EL_STEEL_CHAR_C;
5520 case 0x1618: // (blue steel)
5521 element = EL_STEEL_CHAR_D;
5524 case 0x1619: // (blue steel)
5525 element = EL_STEEL_CHAR_E;
5528 case 0x161a: // (blue steel)
5529 element = EL_STEEL_CHAR_F;
5532 case 0x161b: // (blue steel)
5533 element = EL_STEEL_CHAR_G;
5536 case 0x161c: // (blue steel)
5537 element = EL_STEEL_CHAR_H;
5540 case 0x161d: // (blue steel)
5541 element = EL_STEEL_CHAR_I;
5544 case 0x161e: // (blue steel)
5545 element = EL_STEEL_CHAR_J;
5548 case 0x161f: // (blue steel)
5549 element = EL_STEEL_CHAR_K;
5552 case 0x1620: // (blue steel)
5553 element = EL_STEEL_CHAR_L;
5556 case 0x1621: // (blue steel)
5557 element = EL_STEEL_CHAR_M;
5560 case 0x1622: // (blue steel)
5561 element = EL_STEEL_CHAR_N;
5564 case 0x1623: // (blue steel)
5565 element = EL_STEEL_CHAR_O;
5568 case 0x1624: // (blue steel)
5569 element = EL_STEEL_CHAR_P;
5572 case 0x1625: // (blue steel)
5573 element = EL_STEEL_CHAR_Q;
5576 case 0x1626: // (blue steel)
5577 element = EL_STEEL_CHAR_R;
5580 case 0x1627: // (blue steel)
5581 element = EL_STEEL_CHAR_S;
5584 case 0x1628: // (blue steel)
5585 element = EL_STEEL_CHAR_T;
5588 case 0x1629: // (blue steel)
5589 element = EL_STEEL_CHAR_U;
5592 case 0x162a: // (blue steel)
5593 element = EL_STEEL_CHAR_V;
5596 case 0x162b: // (blue steel)
5597 element = EL_STEEL_CHAR_W;
5600 case 0x162c: // (blue steel)
5601 element = EL_STEEL_CHAR_X;
5604 case 0x162d: // (blue steel)
5605 element = EL_STEEL_CHAR_Y;
5608 case 0x162e: // (blue steel)
5609 element = EL_STEEL_CHAR_Z;
5612 case 0x162f: // (blue steel)
5613 element = EL_STEEL_CHAR_AUMLAUT;
5616 case 0x1630: // (blue steel)
5617 element = EL_STEEL_CHAR_OUMLAUT;
5620 case 0x1631: // (blue steel)
5621 element = EL_STEEL_CHAR_UUMLAUT;
5624 case 0x1632: // (blue steel)
5625 element = EL_STEEL_CHAR_0;
5628 case 0x1633: // (blue steel)
5629 element = EL_STEEL_CHAR_1;
5632 case 0x1634: // (blue steel)
5633 element = EL_STEEL_CHAR_2;
5636 case 0x1635: // (blue steel)
5637 element = EL_STEEL_CHAR_3;
5640 case 0x1636: // (blue steel)
5641 element = EL_STEEL_CHAR_4;
5644 case 0x1637: // (blue steel)
5645 element = EL_STEEL_CHAR_5;
5648 case 0x1638: // (blue steel)
5649 element = EL_STEEL_CHAR_6;
5652 case 0x1639: // (blue steel)
5653 element = EL_STEEL_CHAR_7;
5656 case 0x163a: // (blue steel)
5657 element = EL_STEEL_CHAR_8;
5660 case 0x163b: // (blue steel)
5661 element = EL_STEEL_CHAR_9;
5664 case 0x163c: // (blue steel)
5665 element = EL_STEEL_CHAR_PERIOD;
5668 case 0x163d: // (blue steel)
5669 element = EL_STEEL_CHAR_EXCLAM;
5672 case 0x163e: // (blue steel)
5673 element = EL_STEEL_CHAR_COLON;
5676 case 0x163f: // (blue steel)
5677 element = EL_STEEL_CHAR_LESS;
5680 case 0x1640: // (blue steel)
5681 element = EL_STEEL_CHAR_GREATER;
5684 case 0x1641: // (blue steel)
5685 element = EL_STEEL_CHAR_QUESTION;
5688 case 0x1642: // (blue steel)
5689 element = EL_STEEL_CHAR_COPYRIGHT;
5692 case 0x1643: // (blue steel)
5693 element = EL_STEEL_CHAR_UP;
5696 case 0x1644: // (blue steel)
5697 element = EL_STEEL_CHAR_DOWN;
5700 case 0x1645: // (blue steel)
5701 element = EL_STEEL_CHAR_BUTTON;
5704 case 0x1646: // (blue steel)
5705 element = EL_STEEL_CHAR_PLUS;
5708 case 0x1647: // (blue steel)
5709 element = EL_STEEL_CHAR_MINUS;
5712 case 0x1648: // (blue steel)
5713 element = EL_STEEL_CHAR_APOSTROPHE;
5716 case 0x1649: // (blue steel)
5717 element = EL_STEEL_CHAR_PARENLEFT;
5720 case 0x164a: // (blue steel)
5721 element = EL_STEEL_CHAR_PARENRIGHT;
5724 case 0x164b: // (green steel)
5725 element = EL_STEEL_CHAR_A;
5728 case 0x164c: // (green steel)
5729 element = EL_STEEL_CHAR_B;
5732 case 0x164d: // (green steel)
5733 element = EL_STEEL_CHAR_C;
5736 case 0x164e: // (green steel)
5737 element = EL_STEEL_CHAR_D;
5740 case 0x164f: // (green steel)
5741 element = EL_STEEL_CHAR_E;
5744 case 0x1650: // (green steel)
5745 element = EL_STEEL_CHAR_F;
5748 case 0x1651: // (green steel)
5749 element = EL_STEEL_CHAR_G;
5752 case 0x1652: // (green steel)
5753 element = EL_STEEL_CHAR_H;
5756 case 0x1653: // (green steel)
5757 element = EL_STEEL_CHAR_I;
5760 case 0x1654: // (green steel)
5761 element = EL_STEEL_CHAR_J;
5764 case 0x1655: // (green steel)
5765 element = EL_STEEL_CHAR_K;
5768 case 0x1656: // (green steel)
5769 element = EL_STEEL_CHAR_L;
5772 case 0x1657: // (green steel)
5773 element = EL_STEEL_CHAR_M;
5776 case 0x1658: // (green steel)
5777 element = EL_STEEL_CHAR_N;
5780 case 0x1659: // (green steel)
5781 element = EL_STEEL_CHAR_O;
5784 case 0x165a: // (green steel)
5785 element = EL_STEEL_CHAR_P;
5788 case 0x165b: // (green steel)
5789 element = EL_STEEL_CHAR_Q;
5792 case 0x165c: // (green steel)
5793 element = EL_STEEL_CHAR_R;
5796 case 0x165d: // (green steel)
5797 element = EL_STEEL_CHAR_S;
5800 case 0x165e: // (green steel)
5801 element = EL_STEEL_CHAR_T;
5804 case 0x165f: // (green steel)
5805 element = EL_STEEL_CHAR_U;
5808 case 0x1660: // (green steel)
5809 element = EL_STEEL_CHAR_V;
5812 case 0x1661: // (green steel)
5813 element = EL_STEEL_CHAR_W;
5816 case 0x1662: // (green steel)
5817 element = EL_STEEL_CHAR_X;
5820 case 0x1663: // (green steel)
5821 element = EL_STEEL_CHAR_Y;
5824 case 0x1664: // (green steel)
5825 element = EL_STEEL_CHAR_Z;
5828 case 0x1665: // (green steel)
5829 element = EL_STEEL_CHAR_AUMLAUT;
5832 case 0x1666: // (green steel)
5833 element = EL_STEEL_CHAR_OUMLAUT;
5836 case 0x1667: // (green steel)
5837 element = EL_STEEL_CHAR_UUMLAUT;
5840 case 0x1668: // (green steel)
5841 element = EL_STEEL_CHAR_0;
5844 case 0x1669: // (green steel)
5845 element = EL_STEEL_CHAR_1;
5848 case 0x166a: // (green steel)
5849 element = EL_STEEL_CHAR_2;
5852 case 0x166b: // (green steel)
5853 element = EL_STEEL_CHAR_3;
5856 case 0x166c: // (green steel)
5857 element = EL_STEEL_CHAR_4;
5860 case 0x166d: // (green steel)
5861 element = EL_STEEL_CHAR_5;
5864 case 0x166e: // (green steel)
5865 element = EL_STEEL_CHAR_6;
5868 case 0x166f: // (green steel)
5869 element = EL_STEEL_CHAR_7;
5872 case 0x1670: // (green steel)
5873 element = EL_STEEL_CHAR_8;
5876 case 0x1671: // (green steel)
5877 element = EL_STEEL_CHAR_9;
5880 case 0x1672: // (green steel)
5881 element = EL_STEEL_CHAR_PERIOD;
5884 case 0x1673: // (green steel)
5885 element = EL_STEEL_CHAR_EXCLAM;
5888 case 0x1674: // (green steel)
5889 element = EL_STEEL_CHAR_COLON;
5892 case 0x1675: // (green steel)
5893 element = EL_STEEL_CHAR_LESS;
5896 case 0x1676: // (green steel)
5897 element = EL_STEEL_CHAR_GREATER;
5900 case 0x1677: // (green steel)
5901 element = EL_STEEL_CHAR_QUESTION;
5904 case 0x1678: // (green steel)
5905 element = EL_STEEL_CHAR_COPYRIGHT;
5908 case 0x1679: // (green steel)
5909 element = EL_STEEL_CHAR_UP;
5912 case 0x167a: // (green steel)
5913 element = EL_STEEL_CHAR_DOWN;
5916 case 0x167b: // (green steel)
5917 element = EL_STEEL_CHAR_BUTTON;
5920 case 0x167c: // (green steel)
5921 element = EL_STEEL_CHAR_PLUS;
5924 case 0x167d: // (green steel)
5925 element = EL_STEEL_CHAR_MINUS;
5928 case 0x167e: // (green steel)
5929 element = EL_STEEL_CHAR_APOSTROPHE;
5932 case 0x167f: // (green steel)
5933 element = EL_STEEL_CHAR_PARENLEFT;
5936 case 0x1680: // (green steel)
5937 element = EL_STEEL_CHAR_PARENRIGHT;
5940 case 0x1681: // gate (red)
5941 element = EL_EM_GATE_1;
5944 case 0x1682: // secret gate (red)
5945 element = EL_EM_GATE_1_GRAY;
5948 case 0x1683: // gate (yellow)
5949 element = EL_EM_GATE_2;
5952 case 0x1684: // secret gate (yellow)
5953 element = EL_EM_GATE_2_GRAY;
5956 case 0x1685: // gate (blue)
5957 element = EL_EM_GATE_4;
5960 case 0x1686: // secret gate (blue)
5961 element = EL_EM_GATE_4_GRAY;
5964 case 0x1687: // gate (green)
5965 element = EL_EM_GATE_3;
5968 case 0x1688: // secret gate (green)
5969 element = EL_EM_GATE_3_GRAY;
5972 case 0x1689: // gate (white)
5973 element = EL_DC_GATE_WHITE;
5976 case 0x168a: // secret gate (white)
5977 element = EL_DC_GATE_WHITE_GRAY;
5980 case 0x168b: // secret gate (no key)
5981 element = EL_DC_GATE_FAKE_GRAY;
5985 element = EL_ROBOT_WHEEL;
5989 element = EL_DC_TIMEGATE_SWITCH;
5993 element = EL_ACID_POOL_BOTTOM;
5997 element = EL_ACID_POOL_TOPLEFT;
6001 element = EL_ACID_POOL_TOPRIGHT;
6005 element = EL_ACID_POOL_BOTTOMLEFT;
6009 element = EL_ACID_POOL_BOTTOMRIGHT;
6013 element = EL_STEELWALL;
6017 element = EL_STEELWALL_SLIPPERY;
6020 case 0x1695: // steel wall (not round)
6021 element = EL_STEELWALL;
6024 case 0x1696: // steel wall (left)
6025 element = EL_DC_STEELWALL_1_LEFT;
6028 case 0x1697: // steel wall (bottom)
6029 element = EL_DC_STEELWALL_1_BOTTOM;
6032 case 0x1698: // steel wall (right)
6033 element = EL_DC_STEELWALL_1_RIGHT;
6036 case 0x1699: // steel wall (top)
6037 element = EL_DC_STEELWALL_1_TOP;
6040 case 0x169a: // steel wall (left/bottom)
6041 element = EL_DC_STEELWALL_1_BOTTOMLEFT;
6044 case 0x169b: // steel wall (right/bottom)
6045 element = EL_DC_STEELWALL_1_BOTTOMRIGHT;
6048 case 0x169c: // steel wall (right/top)
6049 element = EL_DC_STEELWALL_1_TOPRIGHT;
6052 case 0x169d: // steel wall (left/top)
6053 element = EL_DC_STEELWALL_1_TOPLEFT;
6056 case 0x169e: // steel wall (right/bottom small)
6057 element = EL_DC_STEELWALL_1_BOTTOMRIGHT_2;
6060 case 0x169f: // steel wall (left/bottom small)
6061 element = EL_DC_STEELWALL_1_BOTTOMLEFT_2;
6064 case 0x16a0: // steel wall (right/top small)
6065 element = EL_DC_STEELWALL_1_TOPRIGHT_2;
6068 case 0x16a1: // steel wall (left/top small)
6069 element = EL_DC_STEELWALL_1_TOPLEFT_2;
6072 case 0x16a2: // steel wall (left/right)
6073 element = EL_DC_STEELWALL_1_VERTICAL;
6076 case 0x16a3: // steel wall (top/bottom)
6077 element = EL_DC_STEELWALL_1_HORIZONTAL;
6080 case 0x16a4: // steel wall 2 (left end)
6081 element = EL_DC_STEELWALL_2_LEFT;
6084 case 0x16a5: // steel wall 2 (right end)
6085 element = EL_DC_STEELWALL_2_RIGHT;
6088 case 0x16a6: // steel wall 2 (top end)
6089 element = EL_DC_STEELWALL_2_TOP;
6092 case 0x16a7: // steel wall 2 (bottom end)
6093 element = EL_DC_STEELWALL_2_BOTTOM;
6096 case 0x16a8: // steel wall 2 (left/right)
6097 element = EL_DC_STEELWALL_2_HORIZONTAL;
6100 case 0x16a9: // steel wall 2 (up/down)
6101 element = EL_DC_STEELWALL_2_VERTICAL;
6104 case 0x16aa: // steel wall 2 (mid)
6105 element = EL_DC_STEELWALL_2_MIDDLE;
6109 element = EL_SIGN_EXCLAMATION;
6113 element = EL_SIGN_RADIOACTIVITY;
6117 element = EL_SIGN_STOP;
6121 element = EL_SIGN_WHEELCHAIR;
6125 element = EL_SIGN_PARKING;
6129 element = EL_SIGN_NO_ENTRY;
6133 element = EL_SIGN_HEART;
6137 element = EL_SIGN_GIVE_WAY;
6141 element = EL_SIGN_ENTRY_FORBIDDEN;
6145 element = EL_SIGN_EMERGENCY_EXIT;
6149 element = EL_SIGN_YIN_YANG;
6153 element = EL_WALL_EMERALD;
6157 element = EL_WALL_DIAMOND;
6161 element = EL_WALL_PEARL;
6165 element = EL_WALL_CRYSTAL;
6169 element = EL_INVISIBLE_WALL;
6173 element = EL_INVISIBLE_STEELWALL;
6177 // EL_INVISIBLE_SAND
6180 element = EL_LIGHT_SWITCH;
6184 element = EL_ENVELOPE_1;
6188 if (element >= 0x0117 && element <= 0x036e) // (?)
6189 element = EL_DIAMOND;
6190 else if (element >= 0x042d && element <= 0x0684) // (?)
6191 element = EL_EMERALD;
6192 else if (element >= 0x157c && element <= 0x158b)
6194 else if (element >= 0x1590 && element <= 0x159f)
6195 element = EL_DC_LANDMINE;
6196 else if (element >= 0x16bc && element <= 0x16cb)
6197 element = EL_INVISIBLE_SAND;
6200 Warn("unknown Diamond Caves element 0x%04x", element);
6202 element = EL_UNKNOWN;
6207 return getMappedElement(element);
6210 static void LoadLevelFromFileStream_DC(File *file, struct LevelInfo *level)
6212 byte header[DC_LEVEL_HEADER_SIZE];
6214 int envelope_header_pos = 62;
6215 int envelope_content_pos = 94;
6216 int level_name_pos = 251;
6217 int level_author_pos = 292;
6218 int envelope_header_len;
6219 int envelope_content_len;
6221 int level_author_len;
6223 int num_yamyam_contents;
6226 getDecodedWord_DC(0, TRUE); // initialize DC2 decoding engine
6228 for (i = 0; i < DC_LEVEL_HEADER_SIZE / 2; i++)
6230 unsigned short header_word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
6232 header[i * 2 + 0] = header_word >> 8;
6233 header[i * 2 + 1] = header_word & 0xff;
6236 // read some values from level header to check level decoding integrity
6237 fieldx = header[6] | (header[7] << 8);
6238 fieldy = header[8] | (header[9] << 8);
6239 num_yamyam_contents = header[60] | (header[61] << 8);
6241 // do some simple sanity checks to ensure that level was correctly decoded
6242 if (fieldx < 1 || fieldx > 256 ||
6243 fieldy < 1 || fieldy > 256 ||
6244 num_yamyam_contents < 1 || num_yamyam_contents > 8)
6246 level->no_valid_file = TRUE;
6248 Warn("cannot decode level from stream -- using empty level");
6253 // maximum envelope header size is 31 bytes
6254 envelope_header_len = header[envelope_header_pos];
6255 // maximum envelope content size is 110 (156?) bytes
6256 envelope_content_len = header[envelope_content_pos];
6258 // maximum level title size is 40 bytes
6259 level_name_len = MIN(header[level_name_pos], MAX_LEVEL_NAME_LEN);
6260 // maximum level author size is 30 (51?) bytes
6261 level_author_len = MIN(header[level_author_pos], MAX_LEVEL_AUTHOR_LEN);
6265 for (i = 0; i < envelope_header_len; i++)
6266 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
6267 level->envelope[0].text[envelope_size++] =
6268 header[envelope_header_pos + 1 + i];
6270 if (envelope_header_len > 0 && envelope_content_len > 0)
6272 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
6273 level->envelope[0].text[envelope_size++] = '\n';
6274 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
6275 level->envelope[0].text[envelope_size++] = '\n';
6278 for (i = 0; i < envelope_content_len; i++)
6279 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
6280 level->envelope[0].text[envelope_size++] =
6281 header[envelope_content_pos + 1 + i];
6283 level->envelope[0].text[envelope_size] = '\0';
6285 level->envelope[0].xsize = MAX_ENVELOPE_XSIZE;
6286 level->envelope[0].ysize = 10;
6287 level->envelope[0].autowrap = TRUE;
6288 level->envelope[0].centered = TRUE;
6290 for (i = 0; i < level_name_len; i++)
6291 level->name[i] = header[level_name_pos + 1 + i];
6292 level->name[level_name_len] = '\0';
6294 for (i = 0; i < level_author_len; i++)
6295 level->author[i] = header[level_author_pos + 1 + i];
6296 level->author[level_author_len] = '\0';
6298 num_yamyam_contents = header[60] | (header[61] << 8);
6299 level->num_yamyam_contents =
6300 MIN(MAX(MIN_ELEMENT_CONTENTS, num_yamyam_contents), MAX_ELEMENT_CONTENTS);
6302 for (i = 0; i < num_yamyam_contents; i++)
6304 for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
6306 unsigned short word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
6307 int element_dc = ((word & 0xff) << 8) | ((word >> 8) & 0xff);
6309 if (i < MAX_ELEMENT_CONTENTS)
6310 level->yamyam_content[i].e[x][y] = getMappedElement_DC(element_dc);
6314 fieldx = header[6] | (header[7] << 8);
6315 fieldy = header[8] | (header[9] << 8);
6316 level->fieldx = MIN(MAX(MIN_LEV_FIELDX, fieldx), MAX_LEV_FIELDX);
6317 level->fieldy = MIN(MAX(MIN_LEV_FIELDY, fieldy), MAX_LEV_FIELDY);
6319 for (y = 0; y < fieldy; y++) for (x = 0; x < fieldx; x++)
6321 unsigned short word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
6322 int element_dc = ((word & 0xff) << 8) | ((word >> 8) & 0xff);
6324 if (x < MAX_LEV_FIELDX && y < MAX_LEV_FIELDY)
6325 level->field[x][y] = getMappedElement_DC(element_dc);
6328 x = MIN(MAX(0, (header[10] | (header[11] << 8)) - 1), MAX_LEV_FIELDX - 1);
6329 y = MIN(MAX(0, (header[12] | (header[13] << 8)) - 1), MAX_LEV_FIELDY - 1);
6330 level->field[x][y] = EL_PLAYER_1;
6332 x = MIN(MAX(0, (header[14] | (header[15] << 8)) - 1), MAX_LEV_FIELDX - 1);
6333 y = MIN(MAX(0, (header[16] | (header[17] << 8)) - 1), MAX_LEV_FIELDY - 1);
6334 level->field[x][y] = EL_PLAYER_2;
6336 level->gems_needed = header[18] | (header[19] << 8);
6338 level->score[SC_EMERALD] = header[20] | (header[21] << 8);
6339 level->score[SC_DIAMOND] = header[22] | (header[23] << 8);
6340 level->score[SC_PEARL] = header[24] | (header[25] << 8);
6341 level->score[SC_CRYSTAL] = header[26] | (header[27] << 8);
6342 level->score[SC_NUT] = header[28] | (header[29] << 8);
6343 level->score[SC_ROBOT] = header[30] | (header[31] << 8);
6344 level->score[SC_SPACESHIP] = header[32] | (header[33] << 8);
6345 level->score[SC_BUG] = header[34] | (header[35] << 8);
6346 level->score[SC_YAMYAM] = header[36] | (header[37] << 8);
6347 level->score[SC_DYNAMITE] = header[38] | (header[39] << 8);
6348 level->score[SC_KEY] = header[40] | (header[41] << 8);
6349 level->score[SC_TIME_BONUS] = header[42] | (header[43] << 8);
6351 level->time = header[44] | (header[45] << 8);
6353 level->amoeba_speed = header[46] | (header[47] << 8);
6354 level->time_light = header[48] | (header[49] << 8);
6355 level->time_timegate = header[50] | (header[51] << 8);
6356 level->time_wheel = header[52] | (header[53] << 8);
6357 level->time_magic_wall = header[54] | (header[55] << 8);
6358 level->extra_time = header[56] | (header[57] << 8);
6359 level->shield_normal_time = header[58] | (header[59] << 8);
6361 // shield and extra time elements do not have a score
6362 level->score[SC_SHIELD] = 0;
6363 level->extra_time_score = 0;
6365 // set time for normal and deadly shields to the same value
6366 level->shield_deadly_time = level->shield_normal_time;
6368 // Diamond Caves has the same (strange) behaviour as Emerald Mine that gems
6369 // can slip down from flat walls, like normal walls and steel walls
6370 level->em_slippery_gems = TRUE;
6372 // time score is counted for each 10 seconds left in Diamond Caves levels
6373 level->time_score_base = 10;
6376 static void LoadLevelFromFileInfo_DC(struct LevelInfo *level,
6377 struct LevelFileInfo *level_file_info,
6378 boolean level_info_only)
6380 char *filename = level_file_info->filename;
6382 int num_magic_bytes = 8;
6383 char magic_bytes[num_magic_bytes + 1];
6384 int num_levels_to_skip = level_file_info->nr - leveldir_current->first_level;
6386 if (!(file = openFile(filename, MODE_READ)))
6388 level->no_valid_file = TRUE;
6390 if (!level_info_only)
6391 Warn("cannot read level '%s' -- using empty level", filename);
6396 // fseek(file, 0x0000, SEEK_SET);
6398 if (level_file_info->packed)
6400 // read "magic bytes" from start of file
6401 if (getStringFromFile(file, magic_bytes, num_magic_bytes + 1) == NULL)
6402 magic_bytes[0] = '\0';
6404 // check "magic bytes" for correct file format
6405 if (!strPrefix(magic_bytes, "DC2"))
6407 level->no_valid_file = TRUE;
6409 Warn("unknown DC level file '%s' -- using empty level", filename);
6414 if (strPrefix(magic_bytes, "DC2Win95") ||
6415 strPrefix(magic_bytes, "DC2Win98"))
6417 int position_first_level = 0x00fa;
6418 int extra_bytes = 4;
6421 // advance file stream to first level inside the level package
6422 skip_bytes = position_first_level - num_magic_bytes - extra_bytes;
6424 // each block of level data is followed by block of non-level data
6425 num_levels_to_skip *= 2;
6427 // at least skip header bytes, therefore use ">= 0" instead of "> 0"
6428 while (num_levels_to_skip >= 0)
6430 // advance file stream to next level inside the level package
6431 if (seekFile(file, skip_bytes, SEEK_CUR) != 0)
6433 level->no_valid_file = TRUE;
6435 Warn("cannot fseek in file '%s' -- using empty level", filename);
6440 // skip apparently unused extra bytes following each level
6441 ReadUnusedBytesFromFile(file, extra_bytes);
6443 // read size of next level in level package
6444 skip_bytes = getFile32BitLE(file);
6446 num_levels_to_skip--;
6451 level->no_valid_file = TRUE;
6453 Warn("unknown DC2 level file '%s' -- using empty level", filename);
6459 LoadLevelFromFileStream_DC(file, level);
6465 // ----------------------------------------------------------------------------
6466 // functions for loading SB level
6467 // ----------------------------------------------------------------------------
6469 int getMappedElement_SB(int element_ascii, boolean use_ces)
6477 sb_element_mapping[] =
6479 { ' ', EL_EMPTY, EL_CUSTOM_1 }, // floor (space)
6480 { '#', EL_STEELWALL, EL_CUSTOM_2 }, // wall
6481 { '@', EL_PLAYER_1, EL_CUSTOM_3 }, // player
6482 { '$', EL_SOKOBAN_OBJECT, EL_CUSTOM_4 }, // box
6483 { '.', EL_SOKOBAN_FIELD_EMPTY, EL_CUSTOM_5 }, // goal square
6484 { '*', EL_SOKOBAN_FIELD_FULL, EL_CUSTOM_6 }, // box on goal square
6485 { '+', EL_SOKOBAN_FIELD_PLAYER, EL_CUSTOM_7 }, // player on goal square
6486 { '_', EL_INVISIBLE_STEELWALL, EL_FROM_LEVEL_TEMPLATE }, // floor beyond border
6493 for (i = 0; sb_element_mapping[i].ascii != 0; i++)
6494 if (element_ascii == sb_element_mapping[i].ascii)
6495 return (use_ces ? sb_element_mapping[i].ce : sb_element_mapping[i].sb);
6497 return EL_UNDEFINED;
6500 static void SetLevelSettings_SB(struct LevelInfo *level)
6504 level->use_step_counter = TRUE;
6507 level->score[SC_TIME_BONUS] = 0;
6508 level->time_score_base = 1;
6509 level->rate_time_over_score = TRUE;
6512 level->auto_exit_sokoban = TRUE;
6515 static void LoadLevelFromFileInfo_SB(struct LevelInfo *level,
6516 struct LevelFileInfo *level_file_info,
6517 boolean level_info_only)
6519 char *filename = level_file_info->filename;
6520 char line[MAX_LINE_LEN], line_raw[MAX_LINE_LEN], previous_line[MAX_LINE_LEN];
6521 char last_comment[MAX_LINE_LEN];
6522 char level_name[MAX_LINE_LEN];
6525 int num_levels_to_skip = level_file_info->nr - leveldir_current->first_level;
6526 boolean read_continued_line = FALSE;
6527 boolean reading_playfield = FALSE;
6528 boolean got_valid_playfield_line = FALSE;
6529 boolean invalid_playfield_char = FALSE;
6530 boolean load_xsb_to_ces = check_special_flags("load_xsb_to_ces");
6531 int file_level_nr = 0;
6532 int x = 0, y = 0; // initialized to make compilers happy
6534 last_comment[0] = '\0';
6535 level_name[0] = '\0';
6537 if (!(file = openFile(filename, MODE_READ)))
6539 level->no_valid_file = TRUE;
6541 if (!level_info_only)
6542 Warn("cannot read level '%s' -- using empty level", filename);
6547 while (!checkEndOfFile(file))
6549 // level successfully read, but next level may follow here
6550 if (!got_valid_playfield_line && reading_playfield)
6552 // read playfield from single level file -- skip remaining file
6553 if (!level_file_info->packed)
6556 if (file_level_nr >= num_levels_to_skip)
6561 last_comment[0] = '\0';
6562 level_name[0] = '\0';
6564 reading_playfield = FALSE;
6567 got_valid_playfield_line = FALSE;
6569 // read next line of input file
6570 if (!getStringFromFile(file, line, MAX_LINE_LEN))
6573 // cut trailing line break (this can be newline and/or carriage return)
6574 for (line_ptr = &line[strlen(line)]; line_ptr >= line; line_ptr--)
6575 if ((*line_ptr == '\n' || *line_ptr == '\r') && *(line_ptr + 1) == '\0')
6578 // copy raw input line for later use (mainly debugging output)
6579 strcpy(line_raw, line);
6581 if (read_continued_line)
6583 // append new line to existing line, if there is enough space
6584 if (strlen(previous_line) + strlen(line_ptr) < MAX_LINE_LEN)
6585 strcat(previous_line, line_ptr);
6587 strcpy(line, previous_line); // copy storage buffer to line
6589 read_continued_line = FALSE;
6592 // if the last character is '\', continue at next line
6593 if (strlen(line) > 0 && line[strlen(line) - 1] == '\\')
6595 line[strlen(line) - 1] = '\0'; // cut off trailing backslash
6596 strcpy(previous_line, line); // copy line to storage buffer
6598 read_continued_line = TRUE;
6604 if (line[0] == '\0')
6607 // extract comment text from comment line
6610 for (line_ptr = line; *line_ptr; line_ptr++)
6611 if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != ';')
6614 strcpy(last_comment, line_ptr);
6619 // extract level title text from line containing level title
6620 if (line[0] == '\'')
6622 strcpy(level_name, &line[1]);
6624 if (strlen(level_name) > 0 && level_name[strlen(level_name) - 1] == '\'')
6625 level_name[strlen(level_name) - 1] = '\0';
6630 // skip lines containing only spaces (or empty lines)
6631 for (line_ptr = line; *line_ptr; line_ptr++)
6632 if (*line_ptr != ' ')
6634 if (*line_ptr == '\0')
6637 // at this point, we have found a line containing part of a playfield
6639 got_valid_playfield_line = TRUE;
6641 if (!reading_playfield)
6643 reading_playfield = TRUE;
6644 invalid_playfield_char = FALSE;
6646 for (x = 0; x < MAX_LEV_FIELDX; x++)
6647 for (y = 0; y < MAX_LEV_FIELDY; y++)
6648 level->field[x][y] = getMappedElement_SB(' ', load_xsb_to_ces);
6653 // start with topmost tile row
6657 // skip playfield line if larger row than allowed
6658 if (y >= MAX_LEV_FIELDY)
6661 // start with leftmost tile column
6664 // read playfield elements from line
6665 for (line_ptr = line; *line_ptr; line_ptr++)
6667 int mapped_sb_element = getMappedElement_SB(*line_ptr, load_xsb_to_ces);
6669 // stop parsing playfield line if larger column than allowed
6670 if (x >= MAX_LEV_FIELDX)
6673 if (mapped_sb_element == EL_UNDEFINED)
6675 invalid_playfield_char = TRUE;
6680 level->field[x][y] = mapped_sb_element;
6682 // continue with next tile column
6685 level->fieldx = MAX(x, level->fieldx);
6688 if (invalid_playfield_char)
6690 // if first playfield line, treat invalid lines as comment lines
6692 reading_playfield = FALSE;
6697 // continue with next tile row
6705 level->fieldx = MIN(MAX(MIN_LEV_FIELDX, level->fieldx), MAX_LEV_FIELDX);
6706 level->fieldy = MIN(MAX(MIN_LEV_FIELDY, level->fieldy), MAX_LEV_FIELDY);
6708 if (!reading_playfield)
6710 level->no_valid_file = TRUE;
6712 Warn("cannot read level '%s' -- using empty level", filename);
6717 if (*level_name != '\0')
6719 strncpy(level->name, level_name, MAX_LEVEL_NAME_LEN);
6720 level->name[MAX_LEVEL_NAME_LEN] = '\0';
6722 else if (*last_comment != '\0')
6724 strncpy(level->name, last_comment, MAX_LEVEL_NAME_LEN);
6725 level->name[MAX_LEVEL_NAME_LEN] = '\0';
6729 sprintf(level->name, "--> Level %d <--", level_file_info->nr);
6732 // set all empty fields beyond the border walls to invisible steel wall
6733 for (y = 0; y < level->fieldy; y++) for (x = 0; x < level->fieldx; x++)
6735 if ((x == 0 || x == level->fieldx - 1 ||
6736 y == 0 || y == level->fieldy - 1) &&
6737 level->field[x][y] == getMappedElement_SB(' ', load_xsb_to_ces))
6738 FloodFillLevel(x, y, getMappedElement_SB('_', load_xsb_to_ces),
6739 level->field, level->fieldx, level->fieldy);
6742 // set special level settings for Sokoban levels
6743 SetLevelSettings_SB(level);
6745 if (load_xsb_to_ces)
6747 // special global settings can now be set in level template
6748 level->use_custom_template = TRUE;
6753 // -------------------------------------------------------------------------
6754 // functions for handling native levels
6755 // -------------------------------------------------------------------------
6757 static void LoadLevelFromFileInfo_BD(struct LevelInfo *level,
6758 struct LevelFileInfo *level_file_info,
6759 boolean level_info_only)
6763 // determine position of requested level inside level package
6764 if (level_file_info->packed)
6765 pos = level_file_info->nr - leveldir_current->first_level;
6767 if (!LoadNativeLevel_BD(level_file_info->filename, pos, level_info_only))
6768 level->no_valid_file = TRUE;
6771 static void LoadLevelFromFileInfo_EM(struct LevelInfo *level,
6772 struct LevelFileInfo *level_file_info,
6773 boolean level_info_only)
6775 if (!LoadNativeLevel_EM(level_file_info->filename, level_info_only))
6776 level->no_valid_file = TRUE;
6779 static void LoadLevelFromFileInfo_SP(struct LevelInfo *level,
6780 struct LevelFileInfo *level_file_info,
6781 boolean level_info_only)
6785 // determine position of requested level inside level package
6786 if (level_file_info->packed)
6787 pos = level_file_info->nr - leveldir_current->first_level;
6789 if (!LoadNativeLevel_SP(level_file_info->filename, pos, level_info_only))
6790 level->no_valid_file = TRUE;
6793 static void LoadLevelFromFileInfo_MM(struct LevelInfo *level,
6794 struct LevelFileInfo *level_file_info,
6795 boolean level_info_only)
6797 if (!LoadNativeLevel_MM(level_file_info->filename, level_info_only))
6798 level->no_valid_file = TRUE;
6801 void CopyNativeLevel_RND_to_Native(struct LevelInfo *level)
6803 if (level->game_engine_type == GAME_ENGINE_TYPE_BD)
6804 CopyNativeLevel_RND_to_BD(level);
6805 else if (level->game_engine_type == GAME_ENGINE_TYPE_EM)
6806 CopyNativeLevel_RND_to_EM(level);
6807 else if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
6808 CopyNativeLevel_RND_to_SP(level);
6809 else if (level->game_engine_type == GAME_ENGINE_TYPE_MM)
6810 CopyNativeLevel_RND_to_MM(level);
6813 void CopyNativeLevel_Native_to_RND(struct LevelInfo *level)
6815 if (level->game_engine_type == GAME_ENGINE_TYPE_BD)
6816 CopyNativeLevel_BD_to_RND(level);
6817 else if (level->game_engine_type == GAME_ENGINE_TYPE_EM)
6818 CopyNativeLevel_EM_to_RND(level);
6819 else if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
6820 CopyNativeLevel_SP_to_RND(level);
6821 else if (level->game_engine_type == GAME_ENGINE_TYPE_MM)
6822 CopyNativeLevel_MM_to_RND(level);
6825 void SaveNativeLevel(struct LevelInfo *level)
6827 // saving native level files only supported for some game engines
6828 if (level->game_engine_type != GAME_ENGINE_TYPE_BD &&
6829 level->game_engine_type != GAME_ENGINE_TYPE_SP)
6832 char *file_ext = (level->game_engine_type == GAME_ENGINE_TYPE_BD ? "bd" :
6833 level->game_engine_type == GAME_ENGINE_TYPE_SP ? "sp" : "");
6834 char *basename = getSingleLevelBasenameExt(level->file_info.nr, file_ext);
6835 char *filename = getLevelFilenameFromBasename(basename);
6837 if (fileExists(filename) && !Request("Native level file already exists! Overwrite it?", REQ_ASK))
6840 boolean success = FALSE;
6842 if (level->game_engine_type == GAME_ENGINE_TYPE_BD)
6844 CopyNativeLevel_RND_to_BD(level);
6845 // CopyNativeTape_RND_to_BD(level);
6847 success = SaveNativeLevel_BD(filename);
6849 else if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
6851 CopyNativeLevel_RND_to_SP(level);
6852 CopyNativeTape_RND_to_SP(level);
6854 success = SaveNativeLevel_SP(filename);
6858 Request("Native level file saved!", REQ_CONFIRM);
6860 Request("Failed to save native level file!", REQ_CONFIRM);
6864 // ----------------------------------------------------------------------------
6865 // functions for loading generic level
6866 // ----------------------------------------------------------------------------
6868 static void LoadLevelFromFileInfo(struct LevelInfo *level,
6869 struct LevelFileInfo *level_file_info,
6870 boolean level_info_only)
6872 // always start with reliable default values
6873 setLevelInfoToDefaults(level, level_info_only, TRUE);
6875 switch (level_file_info->type)
6877 case LEVEL_FILE_TYPE_RND:
6878 LoadLevelFromFileInfo_RND(level, level_file_info, level_info_only);
6881 case LEVEL_FILE_TYPE_BD:
6882 LoadLevelFromFileInfo_BD(level, level_file_info, level_info_only);
6883 level->game_engine_type = GAME_ENGINE_TYPE_BD;
6886 case LEVEL_FILE_TYPE_EM:
6887 LoadLevelFromFileInfo_EM(level, level_file_info, level_info_only);
6888 level->game_engine_type = GAME_ENGINE_TYPE_EM;
6891 case LEVEL_FILE_TYPE_SP:
6892 LoadLevelFromFileInfo_SP(level, level_file_info, level_info_only);
6893 level->game_engine_type = GAME_ENGINE_TYPE_SP;
6896 case LEVEL_FILE_TYPE_MM:
6897 LoadLevelFromFileInfo_MM(level, level_file_info, level_info_only);
6898 level->game_engine_type = GAME_ENGINE_TYPE_MM;
6901 case LEVEL_FILE_TYPE_DC:
6902 LoadLevelFromFileInfo_DC(level, level_file_info, level_info_only);
6905 case LEVEL_FILE_TYPE_SB:
6906 LoadLevelFromFileInfo_SB(level, level_file_info, level_info_only);
6910 LoadLevelFromFileInfo_RND(level, level_file_info, level_info_only);
6914 // if level file is invalid, restore level structure to default values
6915 if (level->no_valid_file)
6916 setLevelInfoToDefaults(level, level_info_only, FALSE);
6918 if (check_special_flags("use_native_bd_game_engine"))
6919 level->game_engine_type = GAME_ENGINE_TYPE_BD;
6921 if (level->game_engine_type == GAME_ENGINE_TYPE_UNKNOWN)
6922 level->game_engine_type = GAME_ENGINE_TYPE_RND;
6924 if (level_file_info->type != LEVEL_FILE_TYPE_RND)
6925 CopyNativeLevel_Native_to_RND(level);
6928 void LoadLevelFromFilename(struct LevelInfo *level, char *filename)
6930 static struct LevelFileInfo level_file_info;
6932 // always start with reliable default values
6933 setFileInfoToDefaults(&level_file_info);
6935 level_file_info.nr = 0; // unknown level number
6936 level_file_info.type = LEVEL_FILE_TYPE_RND; // no others supported yet
6938 setString(&level_file_info.filename, filename);
6940 LoadLevelFromFileInfo(level, &level_file_info, FALSE);
6943 static void LoadLevel_InitVersion(struct LevelInfo *level)
6947 if (leveldir_current == NULL) // only when dumping level
6950 // all engine modifications also valid for levels which use latest engine
6951 if (level->game_version < VERSION_IDENT(3,2,0,5))
6953 // time bonus score was given for 10 s instead of 1 s before 3.2.0-5
6954 level->time_score_base = 10;
6957 if (leveldir_current->latest_engine)
6959 // ---------- use latest game engine --------------------------------------
6961 /* For all levels which are forced to use the latest game engine version
6962 (normally all but user contributed, private and undefined levels), set
6963 the game engine version to the actual version; this allows for actual
6964 corrections in the game engine to take effect for existing, converted
6965 levels (from "classic" or other existing games) to make the emulation
6966 of the corresponding game more accurate, while (hopefully) not breaking
6967 existing levels created from other players. */
6969 level->game_version = GAME_VERSION_ACTUAL;
6971 /* Set special EM style gems behaviour: EM style gems slip down from
6972 normal, steel and growing wall. As this is a more fundamental change,
6973 it seems better to set the default behaviour to "off" (as it is more
6974 natural) and make it configurable in the level editor (as a property
6975 of gem style elements). Already existing converted levels (neither
6976 private nor contributed levels) are changed to the new behaviour. */
6978 if (level->file_version < FILE_VERSION_2_0)
6979 level->em_slippery_gems = TRUE;
6984 // ---------- use game engine the level was created with --------------------
6986 /* For all levels which are not forced to use the latest game engine
6987 version (normally user contributed, private and undefined levels),
6988 use the version of the game engine the levels were created for.
6990 Since 2.0.1, the game engine version is now directly stored
6991 in the level file (chunk "VERS"), so there is no need anymore
6992 to set the game version from the file version (except for old,
6993 pre-2.0 levels, where the game version is still taken from the
6994 file format version used to store the level -- see above). */
6996 // player was faster than enemies in 1.0.0 and before
6997 if (level->file_version == FILE_VERSION_1_0)
6998 for (i = 0; i < MAX_PLAYERS; i++)
6999 level->initial_player_stepsize[i] = STEPSIZE_FAST;
7001 // default behaviour for EM style gems was "slippery" only in 2.0.1
7002 if (level->game_version == VERSION_IDENT(2,0,1,0))
7003 level->em_slippery_gems = TRUE;
7005 // springs could be pushed over pits before (pre-release version) 2.2.0
7006 if (level->game_version < VERSION_IDENT(2,2,0,0))
7007 level->use_spring_bug = TRUE;
7009 if (level->game_version < VERSION_IDENT(3,2,0,5))
7011 // time orb caused limited time in endless time levels before 3.2.0-5
7012 level->use_time_orb_bug = TRUE;
7014 // default behaviour for snapping was "no snap delay" before 3.2.0-5
7015 level->block_snap_field = FALSE;
7017 // extra time score was same value as time left score before 3.2.0-5
7018 level->extra_time_score = level->score[SC_TIME_BONUS];
7021 if (level->game_version < VERSION_IDENT(3,2,0,7))
7023 // default behaviour for snapping was "not continuous" before 3.2.0-7
7024 level->continuous_snapping = FALSE;
7027 // only few elements were able to actively move into acid before 3.1.0
7028 // trigger settings did not exist before 3.1.0; set to default "any"
7029 if (level->game_version < VERSION_IDENT(3,1,0,0))
7031 // correct "can move into acid" settings (all zero in old levels)
7033 level->can_move_into_acid_bits = 0; // nothing can move into acid
7034 level->dont_collide_with_bits = 0; // nothing is deadly when colliding
7036 setMoveIntoAcidProperty(level, EL_ROBOT, TRUE);
7037 setMoveIntoAcidProperty(level, EL_SATELLITE, TRUE);
7038 setMoveIntoAcidProperty(level, EL_PENGUIN, TRUE);
7039 setMoveIntoAcidProperty(level, EL_BALLOON, TRUE);
7041 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7042 SET_PROPERTY(EL_CUSTOM_START + i, EP_CAN_MOVE_INTO_ACID, TRUE);
7044 // correct trigger settings (stored as zero == "none" in old levels)
7046 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7048 int element = EL_CUSTOM_START + i;
7049 struct ElementInfo *ei = &element_info[element];
7051 for (j = 0; j < ei->num_change_pages; j++)
7053 struct ElementChangeInfo *change = &ei->change_page[j];
7055 change->trigger_player = CH_PLAYER_ANY;
7056 change->trigger_page = CH_PAGE_ANY;
7061 // try to detect and fix "Snake Bite" levels, which are broken with 3.2.0
7063 int element = EL_CUSTOM_256;
7064 struct ElementInfo *ei = &element_info[element];
7065 struct ElementChangeInfo *change = &ei->change_page[0];
7067 /* This is needed to fix a problem that was caused by a bugfix in function
7068 game.c/CreateFieldExt() introduced with 3.2.0 that corrects the behaviour
7069 when a custom element changes to EL_SOKOBAN_FIELD_PLAYER (before, it did
7070 not replace walkable elements, but instead just placed the player on it,
7071 without placing the Sokoban field under the player). Unfortunately, this
7072 breaks "Snake Bite" style levels when the snake is halfway through a door
7073 that just closes (the snake head is still alive and can be moved in this
7074 case). This can be fixed by replacing the EL_SOKOBAN_FIELD_PLAYER by the
7075 player (without Sokoban element) which then gets killed as designed). */
7077 if ((strncmp(leveldir_current->identifier, "snake_bite", 10) == 0 ||
7078 strncmp(ei->description, "pause b4 death", 14) == 0) &&
7079 change->target_element == EL_SOKOBAN_FIELD_PLAYER)
7080 change->target_element = EL_PLAYER_1;
7083 // try to detect and fix "Zelda" style levels, which are broken with 3.2.5
7084 if (level->game_version < VERSION_IDENT(3,2,5,0))
7086 /* This is needed to fix a problem that was caused by a bugfix in function
7087 game.c/CheckTriggeredElementChangeExt() introduced with 3.2.5 that
7088 corrects the behaviour when a custom element changes to another custom
7089 element with a higher element number that has change actions defined.
7090 Normally, only one change per frame is allowed for custom elements.
7091 Therefore, it is checked if a custom element already changed in the
7092 current frame; if it did, subsequent changes are suppressed.
7093 Unfortunately, this is only checked for element changes, but not for
7094 change actions, which are still executed. As the function above loops
7095 through all custom elements from lower to higher, an element change
7096 resulting in a lower CE number won't be checked again, while a target
7097 element with a higher number will also be checked, and potential change
7098 actions will get executed for this CE, too (which is wrong), while
7099 further changes are ignored (which is correct). As this bugfix breaks
7100 Zelda II (and introduces graphical bugs to Zelda I, and also breaks a
7101 few other levels like Alan Bond's "FMV"), allow the previous, incorrect
7102 behaviour for existing levels and tapes that make use of this bug */
7104 level->use_action_after_change_bug = TRUE;
7107 // not centering level after relocating player was default only in 3.2.3
7108 if (level->game_version == VERSION_IDENT(3,2,3,0)) // (no pre-releases)
7109 level->shifted_relocation = TRUE;
7111 // EM style elements always chain-exploded in R'n'D engine before 3.2.6
7112 if (level->game_version < VERSION_IDENT(3,2,6,0))
7113 level->em_explodes_by_fire = TRUE;
7115 // levels were solved by the first player entering an exit up to 4.1.0.0
7116 if (level->game_version <= VERSION_IDENT(4,1,0,0))
7117 level->solved_by_one_player = TRUE;
7119 // game logic of "game of life" and "biomaze" was buggy before 4.1.1.1
7120 if (level->game_version < VERSION_IDENT(4,1,1,1))
7121 level->use_life_bugs = TRUE;
7123 // only Sokoban fields (but not objects) had to be solved before 4.1.1.1
7124 if (level->game_version < VERSION_IDENT(4,1,1,1))
7125 level->sb_objects_needed = FALSE;
7127 // CE actions were triggered by unfinished digging/collecting up to 4.2.2.0
7128 if (level->game_version <= VERSION_IDENT(4,2,2,0))
7129 level->finish_dig_collect = FALSE;
7131 // CE changing to player was kept under the player if walkable up to 4.2.3.1
7132 if (level->game_version <= VERSION_IDENT(4,2,3,1))
7133 level->keep_walkable_ce = TRUE;
7136 static void LoadLevel_InitSettings_SB(struct LevelInfo *level)
7138 boolean is_sokoban_level = TRUE; // unless non-Sokoban elements found
7141 // check if this level is (not) a Sokoban level
7142 for (y = 0; y < level->fieldy; y++)
7143 for (x = 0; x < level->fieldx; x++)
7144 if (!IS_SB_ELEMENT(Tile[x][y]))
7145 is_sokoban_level = FALSE;
7147 if (is_sokoban_level)
7149 // set special level settings for Sokoban levels
7150 SetLevelSettings_SB(level);
7154 static void LoadLevel_InitSettings(struct LevelInfo *level)
7156 // adjust level settings for (non-native) Sokoban-style levels
7157 LoadLevel_InitSettings_SB(level);
7159 // rename levels with title "nameless level" or if renaming is forced
7160 if (leveldir_current->empty_level_name != NULL &&
7161 (strEqual(level->name, NAMELESS_LEVEL_NAME) ||
7162 leveldir_current->force_level_name))
7163 snprintf(level->name, MAX_LEVEL_NAME_LEN + 1,
7164 leveldir_current->empty_level_name, level_nr);
7167 static void LoadLevel_InitStandardElements(struct LevelInfo *level)
7171 // map elements that have changed in newer versions
7172 level->amoeba_content = getMappedElementByVersion(level->amoeba_content,
7173 level->game_version);
7174 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
7175 for (x = 0; x < 3; x++)
7176 for (y = 0; y < 3; y++)
7177 level->yamyam_content[i].e[x][y] =
7178 getMappedElementByVersion(level->yamyam_content[i].e[x][y],
7179 level->game_version);
7183 static void LoadLevel_InitCustomElements(struct LevelInfo *level)
7187 // map custom element change events that have changed in newer versions
7188 // (these following values were accidentally changed in version 3.0.1)
7189 // (this seems to be needed only for 'ab_levelset3' and 'ab_levelset4')
7190 if (level->game_version <= VERSION_IDENT(3,0,0,0))
7192 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7194 int element = EL_CUSTOM_START + i;
7196 // order of checking and copying events to be mapped is important
7197 // (do not change the start and end value -- they are constant)
7198 for (j = CE_BY_OTHER_ACTION; j >= CE_VALUE_GETS_ZERO; j--)
7200 if (HAS_CHANGE_EVENT(element, j - 2))
7202 SET_CHANGE_EVENT(element, j - 2, FALSE);
7203 SET_CHANGE_EVENT(element, j, TRUE);
7207 // order of checking and copying events to be mapped is important
7208 // (do not change the start and end value -- they are constant)
7209 for (j = CE_PLAYER_COLLECTS_X; j >= CE_HITTING_SOMETHING; j--)
7211 if (HAS_CHANGE_EVENT(element, j - 1))
7213 SET_CHANGE_EVENT(element, j - 1, FALSE);
7214 SET_CHANGE_EVENT(element, j, TRUE);
7220 // initialize "can_change" field for old levels with only one change page
7221 if (level->game_version <= VERSION_IDENT(3,0,2,0))
7223 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7225 int element = EL_CUSTOM_START + i;
7227 if (CAN_CHANGE(element))
7228 element_info[element].change->can_change = TRUE;
7232 // correct custom element values (for old levels without these options)
7233 if (level->game_version < VERSION_IDENT(3,1,1,0))
7235 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7237 int element = EL_CUSTOM_START + i;
7238 struct ElementInfo *ei = &element_info[element];
7240 if (ei->access_direction == MV_NO_DIRECTION)
7241 ei->access_direction = MV_ALL_DIRECTIONS;
7245 // correct custom element values (fix invalid values for all versions)
7248 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7250 int element = EL_CUSTOM_START + i;
7251 struct ElementInfo *ei = &element_info[element];
7253 for (j = 0; j < ei->num_change_pages; j++)
7255 struct ElementChangeInfo *change = &ei->change_page[j];
7257 if (change->trigger_player == CH_PLAYER_NONE)
7258 change->trigger_player = CH_PLAYER_ANY;
7260 if (change->trigger_side == CH_SIDE_NONE)
7261 change->trigger_side = CH_SIDE_ANY;
7266 // initialize "can_explode" field for old levels which did not store this
7267 // !!! CHECK THIS -- "<= 3,1,0,0" IS PROBABLY WRONG !!!
7268 if (level->game_version <= VERSION_IDENT(3,1,0,0))
7270 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7272 int element = EL_CUSTOM_START + i;
7274 if (EXPLODES_1X1_OLD(element))
7275 element_info[element].explosion_type = EXPLODES_1X1;
7277 SET_PROPERTY(element, EP_CAN_EXPLODE, (EXPLODES_BY_FIRE(element) ||
7278 EXPLODES_SMASHED(element) ||
7279 EXPLODES_IMPACT(element)));
7283 // correct previously hard-coded move delay values for maze runner style
7284 if (level->game_version < VERSION_IDENT(3,1,1,0))
7286 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7288 int element = EL_CUSTOM_START + i;
7290 if (element_info[element].move_pattern & MV_MAZE_RUNNER_STYLE)
7292 // previously hard-coded and therefore ignored
7293 element_info[element].move_delay_fixed = 9;
7294 element_info[element].move_delay_random = 0;
7299 // set some other uninitialized values of custom elements in older levels
7300 if (level->game_version < VERSION_IDENT(3,1,0,0))
7302 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7304 int element = EL_CUSTOM_START + i;
7306 element_info[element].access_direction = MV_ALL_DIRECTIONS;
7308 element_info[element].explosion_delay = 17;
7309 element_info[element].ignition_delay = 8;
7313 // set mouse click change events to work for left/middle/right mouse button
7314 if (level->game_version < VERSION_IDENT(4,2,3,0))
7316 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7318 int element = EL_CUSTOM_START + i;
7319 struct ElementInfo *ei = &element_info[element];
7321 for (j = 0; j < ei->num_change_pages; j++)
7323 struct ElementChangeInfo *change = &ei->change_page[j];
7325 if (change->has_event[CE_CLICKED_BY_MOUSE] ||
7326 change->has_event[CE_PRESSED_BY_MOUSE] ||
7327 change->has_event[CE_MOUSE_CLICKED_ON_X] ||
7328 change->has_event[CE_MOUSE_PRESSED_ON_X])
7329 change->trigger_side = CH_SIDE_ANY;
7335 static void LoadLevel_InitElements(struct LevelInfo *level)
7337 LoadLevel_InitStandardElements(level);
7339 if (level->file_has_custom_elements)
7340 LoadLevel_InitCustomElements(level);
7342 // initialize element properties for level editor etc.
7343 InitElementPropertiesEngine(level->game_version);
7344 InitElementPropertiesGfxElement();
7347 static void LoadLevel_InitPlayfield(struct LevelInfo *level)
7351 // map elements that have changed in newer versions
7352 for (y = 0; y < level->fieldy; y++)
7353 for (x = 0; x < level->fieldx; x++)
7354 level->field[x][y] = getMappedElementByVersion(level->field[x][y],
7355 level->game_version);
7357 // clear unused playfield data (nicer if level gets resized in editor)
7358 for (x = 0; x < MAX_LEV_FIELDX; x++)
7359 for (y = 0; y < MAX_LEV_FIELDY; y++)
7360 if (x >= level->fieldx || y >= level->fieldy)
7361 level->field[x][y] = EL_EMPTY;
7363 // copy elements to runtime playfield array
7364 for (x = 0; x < MAX_LEV_FIELDX; x++)
7365 for (y = 0; y < MAX_LEV_FIELDY; y++)
7366 Tile[x][y] = level->field[x][y];
7368 // initialize level size variables for faster access
7369 lev_fieldx = level->fieldx;
7370 lev_fieldy = level->fieldy;
7372 // determine border element for this level
7373 if (level->file_info.type == LEVEL_FILE_TYPE_DC)
7374 BorderElement = EL_EMPTY; // (in editor, SetBorderElement() is used)
7379 static void LoadLevel_InitNativeEngines(struct LevelInfo *level)
7381 struct LevelFileInfo *level_file_info = &level->file_info;
7383 if (level_file_info->type == LEVEL_FILE_TYPE_RND)
7384 CopyNativeLevel_RND_to_Native(level);
7387 static void LoadLevelTemplate_LoadAndInit(void)
7389 LoadLevelFromFileInfo(&level_template, &level_template.file_info, FALSE);
7391 LoadLevel_InitVersion(&level_template);
7392 LoadLevel_InitElements(&level_template);
7393 LoadLevel_InitSettings(&level_template);
7395 ActivateLevelTemplate();
7398 void LoadLevelTemplate(int nr)
7400 if (!fileExists(getGlobalLevelTemplateFilename()))
7402 Warn("no level template found for this level");
7407 setLevelFileInfo(&level_template.file_info, nr);
7409 LoadLevelTemplate_LoadAndInit();
7412 static void LoadNetworkLevelTemplate(struct NetworkLevelInfo *network_level)
7414 copyLevelFileInfo(&network_level->tmpl_info, &level_template.file_info);
7416 LoadLevelTemplate_LoadAndInit();
7419 static void LoadLevel_LoadAndInit(struct NetworkLevelInfo *network_level)
7421 LoadLevelFromFileInfo(&level, &level.file_info, FALSE);
7423 if (level.use_custom_template)
7425 if (network_level != NULL)
7426 LoadNetworkLevelTemplate(network_level);
7428 LoadLevelTemplate(-1);
7431 LoadLevel_InitVersion(&level);
7432 LoadLevel_InitElements(&level);
7433 LoadLevel_InitPlayfield(&level);
7434 LoadLevel_InitSettings(&level);
7436 LoadLevel_InitNativeEngines(&level);
7439 void LoadLevel(int nr)
7441 SetLevelSetInfo(leveldir_current->identifier, nr);
7443 setLevelFileInfo(&level.file_info, nr);
7445 LoadLevel_LoadAndInit(NULL);
7448 void LoadLevelInfoOnly(int nr)
7450 setLevelFileInfo(&level.file_info, nr);
7452 LoadLevelFromFileInfo(&level, &level.file_info, TRUE);
7455 void LoadNetworkLevel(struct NetworkLevelInfo *network_level)
7457 SetLevelSetInfo(network_level->leveldir_identifier,
7458 network_level->file_info.nr);
7460 copyLevelFileInfo(&network_level->file_info, &level.file_info);
7462 LoadLevel_LoadAndInit(network_level);
7465 static int SaveLevel_VERS(FILE *file, struct LevelInfo *level)
7469 chunk_size += putFileVersion(file, level->file_version);
7470 chunk_size += putFileVersion(file, level->game_version);
7475 static int SaveLevel_DATE(FILE *file, struct LevelInfo *level)
7479 chunk_size += putFile16BitBE(file, level->creation_date.year);
7480 chunk_size += putFile8Bit(file, level->creation_date.month);
7481 chunk_size += putFile8Bit(file, level->creation_date.day);
7486 #if ENABLE_HISTORIC_CHUNKS
7487 static void SaveLevel_HEAD(FILE *file, struct LevelInfo *level)
7491 putFile8Bit(file, level->fieldx);
7492 putFile8Bit(file, level->fieldy);
7494 putFile16BitBE(file, level->time);
7495 putFile16BitBE(file, level->gems_needed);
7497 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
7498 putFile8Bit(file, level->name[i]);
7500 for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
7501 putFile8Bit(file, level->score[i]);
7503 for (i = 0; i < STD_ELEMENT_CONTENTS; i++)
7504 for (y = 0; y < 3; y++)
7505 for (x = 0; x < 3; x++)
7506 putFile8Bit(file, (level->encoding_16bit_yamyam ? EL_EMPTY :
7507 level->yamyam_content[i].e[x][y]));
7508 putFile8Bit(file, level->amoeba_speed);
7509 putFile8Bit(file, level->time_magic_wall);
7510 putFile8Bit(file, level->time_wheel);
7511 putFile8Bit(file, (level->encoding_16bit_amoeba ? EL_EMPTY :
7512 level->amoeba_content));
7513 putFile8Bit(file, (level->initial_player_stepsize == STEPSIZE_FAST ? 1 : 0));
7514 putFile8Bit(file, (level->initial_gravity ? 1 : 0));
7515 putFile8Bit(file, (level->encoding_16bit_field ? 1 : 0));
7516 putFile8Bit(file, (level->em_slippery_gems ? 1 : 0));
7518 putFile8Bit(file, (level->use_custom_template ? 1 : 0));
7520 putFile8Bit(file, (level->block_last_field ? 1 : 0));
7521 putFile8Bit(file, (level->sp_block_last_field ? 1 : 0));
7522 putFile32BitBE(file, level->can_move_into_acid_bits);
7523 putFile8Bit(file, level->dont_collide_with_bits);
7525 putFile8Bit(file, (level->use_spring_bug ? 1 : 0));
7526 putFile8Bit(file, (level->use_step_counter ? 1 : 0));
7528 putFile8Bit(file, (level->instant_relocation ? 1 : 0));
7529 putFile8Bit(file, (level->can_pass_to_walkable ? 1 : 0));
7530 putFile8Bit(file, (level->grow_into_diggable ? 1 : 0));
7532 putFile8Bit(file, level->game_engine_type);
7534 WriteUnusedBytesToFile(file, LEVEL_CHUNK_HEAD_UNUSED);
7538 static int SaveLevel_NAME(FILE *file, struct LevelInfo *level)
7543 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
7544 chunk_size += putFile8Bit(file, level->name[i]);
7549 static int SaveLevel_AUTH(FILE *file, struct LevelInfo *level)
7554 for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
7555 chunk_size += putFile8Bit(file, level->author[i]);
7560 #if ENABLE_HISTORIC_CHUNKS
7561 static int SaveLevel_BODY(FILE *file, struct LevelInfo *level)
7566 for (y = 0; y < level->fieldy; y++)
7567 for (x = 0; x < level->fieldx; x++)
7568 if (level->encoding_16bit_field)
7569 chunk_size += putFile16BitBE(file, level->field[x][y]);
7571 chunk_size += putFile8Bit(file, level->field[x][y]);
7577 static int SaveLevel_BODY(FILE *file, struct LevelInfo *level)
7582 for (y = 0; y < level->fieldy; y++)
7583 for (x = 0; x < level->fieldx; x++)
7584 chunk_size += putFile16BitBE(file, level->field[x][y]);
7589 #if ENABLE_HISTORIC_CHUNKS
7590 static void SaveLevel_CONT(FILE *file, struct LevelInfo *level)
7594 putFile8Bit(file, EL_YAMYAM);
7595 putFile8Bit(file, level->num_yamyam_contents);
7596 putFile8Bit(file, 0);
7597 putFile8Bit(file, 0);
7599 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
7600 for (y = 0; y < 3; y++)
7601 for (x = 0; x < 3; x++)
7602 if (level->encoding_16bit_field)
7603 putFile16BitBE(file, level->yamyam_content[i].e[x][y]);
7605 putFile8Bit(file, level->yamyam_content[i].e[x][y]);
7609 #if ENABLE_HISTORIC_CHUNKS
7610 static void SaveLevel_CNT2(FILE *file, struct LevelInfo *level, int element)
7613 int num_contents, content_xsize, content_ysize;
7614 int content_array[MAX_ELEMENT_CONTENTS][3][3];
7616 if (element == EL_YAMYAM)
7618 num_contents = level->num_yamyam_contents;
7622 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
7623 for (y = 0; y < 3; y++)
7624 for (x = 0; x < 3; x++)
7625 content_array[i][x][y] = level->yamyam_content[i].e[x][y];
7627 else if (element == EL_BD_AMOEBA)
7633 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
7634 for (y = 0; y < 3; y++)
7635 for (x = 0; x < 3; x++)
7636 content_array[i][x][y] = EL_EMPTY;
7637 content_array[0][0][0] = level->amoeba_content;
7641 // chunk header already written -- write empty chunk data
7642 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_SIZE);
7644 Warn("cannot save content for element '%d'", element);
7649 putFile16BitBE(file, element);
7650 putFile8Bit(file, num_contents);
7651 putFile8Bit(file, content_xsize);
7652 putFile8Bit(file, content_ysize);
7654 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_UNUSED);
7656 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
7657 for (y = 0; y < 3; y++)
7658 for (x = 0; x < 3; x++)
7659 putFile16BitBE(file, content_array[i][x][y]);
7663 #if ENABLE_HISTORIC_CHUNKS
7664 static int SaveLevel_CNT3(FILE *file, struct LevelInfo *level, int element)
7666 int envelope_nr = element - EL_ENVELOPE_1;
7667 int envelope_len = strlen(level->envelope_text[envelope_nr]) + 1;
7671 chunk_size += putFile16BitBE(file, element);
7672 chunk_size += putFile16BitBE(file, envelope_len);
7673 chunk_size += putFile8Bit(file, level->envelope_xsize[envelope_nr]);
7674 chunk_size += putFile8Bit(file, level->envelope_ysize[envelope_nr]);
7676 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT3_UNUSED);
7677 chunk_size += LEVEL_CHUNK_CNT3_UNUSED;
7679 for (i = 0; i < envelope_len; i++)
7680 chunk_size += putFile8Bit(file, level->envelope_text[envelope_nr][i]);
7686 #if ENABLE_HISTORIC_CHUNKS
7687 static void SaveLevel_CUS1(FILE *file, struct LevelInfo *level,
7688 int num_changed_custom_elements)
7692 putFile16BitBE(file, num_changed_custom_elements);
7694 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7696 int element = EL_CUSTOM_START + i;
7698 struct ElementInfo *ei = &element_info[element];
7700 if (ei->properties[EP_BITFIELD_BASE_NR] != EP_BITMASK_DEFAULT)
7702 if (check < num_changed_custom_elements)
7704 putFile16BitBE(file, element);
7705 putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
7712 if (check != num_changed_custom_elements) // should not happen
7713 Warn("inconsistent number of custom element properties");
7717 #if ENABLE_HISTORIC_CHUNKS
7718 static void SaveLevel_CUS2(FILE *file, struct LevelInfo *level,
7719 int num_changed_custom_elements)
7723 putFile16BitBE(file, num_changed_custom_elements);
7725 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7727 int element = EL_CUSTOM_START + i;
7729 if (element_info[element].change->target_element != EL_EMPTY_SPACE)
7731 if (check < num_changed_custom_elements)
7733 putFile16BitBE(file, element);
7734 putFile16BitBE(file, element_info[element].change->target_element);
7741 if (check != num_changed_custom_elements) // should not happen
7742 Warn("inconsistent number of custom target elements");
7746 #if ENABLE_HISTORIC_CHUNKS
7747 static void SaveLevel_CUS3(FILE *file, struct LevelInfo *level,
7748 int num_changed_custom_elements)
7750 int i, j, x, y, check = 0;
7752 putFile16BitBE(file, num_changed_custom_elements);
7754 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7756 int element = EL_CUSTOM_START + i;
7757 struct ElementInfo *ei = &element_info[element];
7759 if (ei->modified_settings)
7761 if (check < num_changed_custom_elements)
7763 putFile16BitBE(file, element);
7765 for (j = 0; j < MAX_ELEMENT_NAME_LEN; j++)
7766 putFile8Bit(file, ei->description[j]);
7768 putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
7770 // some free bytes for future properties and padding
7771 WriteUnusedBytesToFile(file, 7);
7773 putFile8Bit(file, ei->use_gfx_element);
7774 putFile16BitBE(file, ei->gfx_element_initial);
7776 putFile8Bit(file, ei->collect_score_initial);
7777 putFile8Bit(file, ei->collect_count_initial);
7779 putFile16BitBE(file, ei->push_delay_fixed);
7780 putFile16BitBE(file, ei->push_delay_random);
7781 putFile16BitBE(file, ei->move_delay_fixed);
7782 putFile16BitBE(file, ei->move_delay_random);
7784 putFile16BitBE(file, ei->move_pattern);
7785 putFile8Bit(file, ei->move_direction_initial);
7786 putFile8Bit(file, ei->move_stepsize);
7788 for (y = 0; y < 3; y++)
7789 for (x = 0; x < 3; x++)
7790 putFile16BitBE(file, ei->content.e[x][y]);
7792 putFile32BitBE(file, ei->change->events);
7794 putFile16BitBE(file, ei->change->target_element);
7796 putFile16BitBE(file, ei->change->delay_fixed);
7797 putFile16BitBE(file, ei->change->delay_random);
7798 putFile16BitBE(file, ei->change->delay_frames);
7800 putFile16BitBE(file, ei->change->initial_trigger_element);
7802 putFile8Bit(file, ei->change->explode);
7803 putFile8Bit(file, ei->change->use_target_content);
7804 putFile8Bit(file, ei->change->only_if_complete);
7805 putFile8Bit(file, ei->change->use_random_replace);
7807 putFile8Bit(file, ei->change->random_percentage);
7808 putFile8Bit(file, ei->change->replace_when);
7810 for (y = 0; y < 3; y++)
7811 for (x = 0; x < 3; x++)
7812 putFile16BitBE(file, ei->change->content.e[x][y]);
7814 putFile8Bit(file, ei->slippery_type);
7816 // some free bytes for future properties and padding
7817 WriteUnusedBytesToFile(file, LEVEL_CPART_CUS3_UNUSED);
7824 if (check != num_changed_custom_elements) // should not happen
7825 Warn("inconsistent number of custom element properties");
7829 #if ENABLE_HISTORIC_CHUNKS
7830 static void SaveLevel_CUS4(FILE *file, struct LevelInfo *level, int element)
7832 struct ElementInfo *ei = &element_info[element];
7835 // ---------- custom element base property values (96 bytes) ----------------
7837 putFile16BitBE(file, element);
7839 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
7840 putFile8Bit(file, ei->description[i]);
7842 putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
7844 WriteUnusedBytesToFile(file, 4); // reserved for more base properties
7846 putFile8Bit(file, ei->num_change_pages);
7848 putFile16BitBE(file, ei->ce_value_fixed_initial);
7849 putFile16BitBE(file, ei->ce_value_random_initial);
7850 putFile8Bit(file, ei->use_last_ce_value);
7852 putFile8Bit(file, ei->use_gfx_element);
7853 putFile16BitBE(file, ei->gfx_element_initial);
7855 putFile8Bit(file, ei->collect_score_initial);
7856 putFile8Bit(file, ei->collect_count_initial);
7858 putFile8Bit(file, ei->drop_delay_fixed);
7859 putFile8Bit(file, ei->push_delay_fixed);
7860 putFile8Bit(file, ei->drop_delay_random);
7861 putFile8Bit(file, ei->push_delay_random);
7862 putFile16BitBE(file, ei->move_delay_fixed);
7863 putFile16BitBE(file, ei->move_delay_random);
7865 // bits 0 - 15 of "move_pattern" ...
7866 putFile16BitBE(file, ei->move_pattern & 0xffff);
7867 putFile8Bit(file, ei->move_direction_initial);
7868 putFile8Bit(file, ei->move_stepsize);
7870 putFile8Bit(file, ei->slippery_type);
7872 for (y = 0; y < 3; y++)
7873 for (x = 0; x < 3; x++)
7874 putFile16BitBE(file, ei->content.e[x][y]);
7876 putFile16BitBE(file, ei->move_enter_element);
7877 putFile16BitBE(file, ei->move_leave_element);
7878 putFile8Bit(file, ei->move_leave_type);
7880 // ... bits 16 - 31 of "move_pattern" (not nice, but downward compatible)
7881 putFile16BitBE(file, (ei->move_pattern >> 16) & 0xffff);
7883 putFile8Bit(file, ei->access_direction);
7885 putFile8Bit(file, ei->explosion_delay);
7886 putFile8Bit(file, ei->ignition_delay);
7887 putFile8Bit(file, ei->explosion_type);
7889 // some free bytes for future custom property values and padding
7890 WriteUnusedBytesToFile(file, 1);
7892 // ---------- change page property values (48 bytes) ------------------------
7894 for (i = 0; i < ei->num_change_pages; i++)
7896 struct ElementChangeInfo *change = &ei->change_page[i];
7897 unsigned int event_bits;
7899 // bits 0 - 31 of "has_event[]" ...
7901 for (j = 0; j < MIN(NUM_CHANGE_EVENTS, 32); j++)
7902 if (change->has_event[j])
7903 event_bits |= (1u << j);
7904 putFile32BitBE(file, event_bits);
7906 putFile16BitBE(file, change->target_element);
7908 putFile16BitBE(file, change->delay_fixed);
7909 putFile16BitBE(file, change->delay_random);
7910 putFile16BitBE(file, change->delay_frames);
7912 putFile16BitBE(file, change->initial_trigger_element);
7914 putFile8Bit(file, change->explode);
7915 putFile8Bit(file, change->use_target_content);
7916 putFile8Bit(file, change->only_if_complete);
7917 putFile8Bit(file, change->use_random_replace);
7919 putFile8Bit(file, change->random_percentage);
7920 putFile8Bit(file, change->replace_when);
7922 for (y = 0; y < 3; y++)
7923 for (x = 0; x < 3; x++)
7924 putFile16BitBE(file, change->target_content.e[x][y]);
7926 putFile8Bit(file, change->can_change);
7928 putFile8Bit(file, change->trigger_side);
7930 putFile8Bit(file, change->trigger_player);
7931 putFile8Bit(file, (change->trigger_page == CH_PAGE_ANY ? CH_PAGE_ANY_FILE :
7932 log_2(change->trigger_page)));
7934 putFile8Bit(file, change->has_action);
7935 putFile8Bit(file, change->action_type);
7936 putFile8Bit(file, change->action_mode);
7937 putFile16BitBE(file, change->action_arg);
7939 // ... bits 32 - 39 of "has_event[]" (not nice, but downward compatible)
7941 for (j = 32; j < NUM_CHANGE_EVENTS; j++)
7942 if (change->has_event[j])
7943 event_bits |= (1u << (j - 32));
7944 putFile8Bit(file, event_bits);
7949 #if ENABLE_HISTORIC_CHUNKS
7950 static void SaveLevel_GRP1(FILE *file, struct LevelInfo *level, int element)
7952 struct ElementInfo *ei = &element_info[element];
7953 struct ElementGroupInfo *group = ei->group;
7956 putFile16BitBE(file, element);
7958 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
7959 putFile8Bit(file, ei->description[i]);
7961 putFile8Bit(file, group->num_elements);
7963 putFile8Bit(file, ei->use_gfx_element);
7964 putFile16BitBE(file, ei->gfx_element_initial);
7966 putFile8Bit(file, group->choice_mode);
7968 // some free bytes for future values and padding
7969 WriteUnusedBytesToFile(file, 3);
7971 for (i = 0; i < MAX_ELEMENTS_IN_GROUP; i++)
7972 putFile16BitBE(file, group->element[i]);
7976 static int SaveLevel_MicroChunk(FILE *file, struct LevelFileConfigInfo *entry,
7977 boolean write_element)
7979 int save_type = entry->save_type;
7980 int data_type = entry->data_type;
7981 int conf_type = entry->conf_type;
7982 int byte_mask = conf_type & CONF_MASK_BYTES;
7983 int element = entry->element;
7984 int default_value = entry->default_value;
7986 boolean modified = FALSE;
7988 if (byte_mask != CONF_MASK_MULTI_BYTES)
7990 void *value_ptr = entry->value;
7991 int value = (data_type == TYPE_BOOLEAN ? *(boolean *)value_ptr :
7994 // check if any settings have been modified before saving them
7995 if (value != default_value)
7998 // do not save if explicitly told or if unmodified default settings
7999 if ((save_type == SAVE_CONF_NEVER) ||
8000 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
8004 num_bytes += putFile16BitBE(file, element);
8006 num_bytes += putFile8Bit(file, conf_type);
8007 num_bytes += (byte_mask == CONF_MASK_1_BYTE ? putFile8Bit (file, value) :
8008 byte_mask == CONF_MASK_2_BYTE ? putFile16BitBE(file, value) :
8009 byte_mask == CONF_MASK_4_BYTE ? putFile32BitBE(file, value) :
8012 else if (data_type == TYPE_STRING)
8014 char *default_string = entry->default_string;
8015 char *string = (char *)(entry->value);
8016 int string_length = strlen(string);
8019 // check if any settings have been modified before saving them
8020 if (!strEqual(string, default_string))
8023 // do not save if explicitly told or if unmodified default settings
8024 if ((save_type == SAVE_CONF_NEVER) ||
8025 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
8029 num_bytes += putFile16BitBE(file, element);
8031 num_bytes += putFile8Bit(file, conf_type);
8032 num_bytes += putFile16BitBE(file, string_length);
8034 for (i = 0; i < string_length; i++)
8035 num_bytes += putFile8Bit(file, string[i]);
8037 else if (data_type == TYPE_ELEMENT_LIST)
8039 int *element_array = (int *)(entry->value);
8040 int num_elements = *(int *)(entry->num_entities);
8043 // check if any settings have been modified before saving them
8044 for (i = 0; i < num_elements; i++)
8045 if (element_array[i] != default_value)
8048 // do not save if explicitly told or if unmodified default settings
8049 if ((save_type == SAVE_CONF_NEVER) ||
8050 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
8054 num_bytes += putFile16BitBE(file, element);
8056 num_bytes += putFile8Bit(file, conf_type);
8057 num_bytes += putFile16BitBE(file, num_elements * CONF_ELEMENT_NUM_BYTES);
8059 for (i = 0; i < num_elements; i++)
8060 num_bytes += putFile16BitBE(file, element_array[i]);
8062 else if (data_type == TYPE_CONTENT_LIST)
8064 struct Content *content = (struct Content *)(entry->value);
8065 int num_contents = *(int *)(entry->num_entities);
8068 // check if any settings have been modified before saving them
8069 for (i = 0; i < num_contents; i++)
8070 for (y = 0; y < 3; y++)
8071 for (x = 0; x < 3; x++)
8072 if (content[i].e[x][y] != default_value)
8075 // do not save if explicitly told or if unmodified default settings
8076 if ((save_type == SAVE_CONF_NEVER) ||
8077 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
8081 num_bytes += putFile16BitBE(file, element);
8083 num_bytes += putFile8Bit(file, conf_type);
8084 num_bytes += putFile16BitBE(file, num_contents * CONF_CONTENT_NUM_BYTES);
8086 for (i = 0; i < num_contents; i++)
8087 for (y = 0; y < 3; y++)
8088 for (x = 0; x < 3; x++)
8089 num_bytes += putFile16BitBE(file, content[i].e[x][y]);
8095 static int SaveLevel_INFO(FILE *file, struct LevelInfo *level)
8100 li = *level; // copy level data into temporary buffer
8102 for (i = 0; chunk_config_INFO[i].data_type != -1; i++)
8103 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_INFO[i], FALSE);
8108 static int SaveLevel_ELEM(FILE *file, struct LevelInfo *level)
8113 li = *level; // copy level data into temporary buffer
8115 for (i = 0; chunk_config_ELEM[i].data_type != -1; i++)
8116 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_ELEM[i], TRUE);
8121 static int SaveLevel_NOTE(FILE *file, struct LevelInfo *level, int element)
8123 int envelope_nr = element - EL_ENVELOPE_1;
8127 chunk_size += putFile16BitBE(file, element);
8129 // copy envelope data into temporary buffer
8130 xx_envelope = level->envelope[envelope_nr];
8132 for (i = 0; chunk_config_NOTE[i].data_type != -1; i++)
8133 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_NOTE[i], FALSE);
8138 static int SaveLevel_CUSX(FILE *file, struct LevelInfo *level, int element)
8140 struct ElementInfo *ei = &element_info[element];
8144 chunk_size += putFile16BitBE(file, element);
8146 xx_ei = *ei; // copy element data into temporary buffer
8148 // set default description string for this specific element
8149 strcpy(xx_default_description, getDefaultElementDescription(ei));
8151 for (i = 0; chunk_config_CUSX_base[i].data_type != -1; i++)
8152 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_CUSX_base[i], FALSE);
8154 for (i = 0; i < ei->num_change_pages; i++)
8156 struct ElementChangeInfo *change = &ei->change_page[i];
8158 xx_current_change_page = i;
8160 xx_change = *change; // copy change data into temporary buffer
8163 setEventBitsFromEventFlags(change);
8165 for (j = 0; chunk_config_CUSX_change[j].data_type != -1; j++)
8166 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_CUSX_change[j],
8173 static int SaveLevel_GRPX(FILE *file, struct LevelInfo *level, int element)
8175 struct ElementInfo *ei = &element_info[element];
8176 struct ElementGroupInfo *group = ei->group;
8180 chunk_size += putFile16BitBE(file, element);
8182 xx_ei = *ei; // copy element data into temporary buffer
8183 xx_group = *group; // copy group data into temporary buffer
8185 // set default description string for this specific element
8186 strcpy(xx_default_description, getDefaultElementDescription(ei));
8188 for (i = 0; chunk_config_GRPX[i].data_type != -1; i++)
8189 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_GRPX[i], FALSE);
8194 static int SaveLevel_EMPX(FILE *file, struct LevelInfo *level, int element)
8196 struct ElementInfo *ei = &element_info[element];
8200 chunk_size += putFile16BitBE(file, element);
8202 xx_ei = *ei; // copy element data into temporary buffer
8204 for (i = 0; chunk_config_EMPX[i].data_type != -1; i++)
8205 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_EMPX[i], FALSE);
8210 static void SaveLevelFromFilename(struct LevelInfo *level, char *filename,
8211 boolean save_as_template)
8217 if (!(file = fopen(filename, MODE_WRITE)))
8219 Warn("cannot save level file '%s'", filename);
8224 level->file_version = FILE_VERSION_ACTUAL;
8225 level->game_version = GAME_VERSION_ACTUAL;
8227 level->creation_date = getCurrentDate();
8229 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
8230 putFileChunkBE(file, "CAVE", CHUNK_SIZE_NONE);
8232 chunk_size = SaveLevel_VERS(NULL, level);
8233 putFileChunkBE(file, "VERS", chunk_size);
8234 SaveLevel_VERS(file, level);
8236 chunk_size = SaveLevel_DATE(NULL, level);
8237 putFileChunkBE(file, "DATE", chunk_size);
8238 SaveLevel_DATE(file, level);
8240 chunk_size = SaveLevel_NAME(NULL, level);
8241 putFileChunkBE(file, "NAME", chunk_size);
8242 SaveLevel_NAME(file, level);
8244 chunk_size = SaveLevel_AUTH(NULL, level);
8245 putFileChunkBE(file, "AUTH", chunk_size);
8246 SaveLevel_AUTH(file, level);
8248 chunk_size = SaveLevel_INFO(NULL, level);
8249 putFileChunkBE(file, "INFO", chunk_size);
8250 SaveLevel_INFO(file, level);
8252 chunk_size = SaveLevel_BODY(NULL, level);
8253 putFileChunkBE(file, "BODY", chunk_size);
8254 SaveLevel_BODY(file, level);
8256 chunk_size = SaveLevel_ELEM(NULL, level);
8257 if (chunk_size > LEVEL_CHUNK_ELEM_UNCHANGED) // save if changed
8259 putFileChunkBE(file, "ELEM", chunk_size);
8260 SaveLevel_ELEM(file, level);
8263 for (i = 0; i < NUM_ENVELOPES; i++)
8265 int element = EL_ENVELOPE_1 + i;
8267 chunk_size = SaveLevel_NOTE(NULL, level, element);
8268 if (chunk_size > LEVEL_CHUNK_NOTE_UNCHANGED) // save if changed
8270 putFileChunkBE(file, "NOTE", chunk_size);
8271 SaveLevel_NOTE(file, level, element);
8275 // if not using template level, check for non-default custom/group elements
8276 if (!level->use_custom_template || save_as_template)
8278 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
8280 int element = EL_CUSTOM_START + i;
8282 chunk_size = SaveLevel_CUSX(NULL, level, element);
8283 if (chunk_size > LEVEL_CHUNK_CUSX_UNCHANGED) // save if changed
8285 putFileChunkBE(file, "CUSX", chunk_size);
8286 SaveLevel_CUSX(file, level, element);
8290 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
8292 int element = EL_GROUP_START + i;
8294 chunk_size = SaveLevel_GRPX(NULL, level, element);
8295 if (chunk_size > LEVEL_CHUNK_GRPX_UNCHANGED) // save if changed
8297 putFileChunkBE(file, "GRPX", chunk_size);
8298 SaveLevel_GRPX(file, level, element);
8302 for (i = 0; i < NUM_EMPTY_ELEMENTS_ALL; i++)
8304 int element = GET_EMPTY_ELEMENT(i);
8306 chunk_size = SaveLevel_EMPX(NULL, level, element);
8307 if (chunk_size > LEVEL_CHUNK_EMPX_UNCHANGED) // save if changed
8309 putFileChunkBE(file, "EMPX", chunk_size);
8310 SaveLevel_EMPX(file, level, element);
8317 SetFilePermissions(filename, PERMS_PRIVATE);
8320 void SaveLevel(int nr)
8322 char *filename = getDefaultLevelFilename(nr);
8324 SaveLevelFromFilename(&level, filename, FALSE);
8327 void SaveLevelTemplate(void)
8329 char *filename = getLocalLevelTemplateFilename();
8331 SaveLevelFromFilename(&level, filename, TRUE);
8334 boolean SaveLevelChecked(int nr)
8336 char *filename = getDefaultLevelFilename(nr);
8337 boolean new_level = !fileExists(filename);
8338 boolean level_saved = FALSE;
8340 if (new_level || Request("Save this level and kill the old?", REQ_ASK))
8345 Request("Level saved!", REQ_CONFIRM);
8353 void DumpLevel(struct LevelInfo *level)
8355 if (level->no_level_file || level->no_valid_file)
8357 Warn("cannot dump -- no valid level file found");
8363 Print("Level xxx (file version %08d, game version %08d)\n",
8364 level->file_version, level->game_version);
8367 Print("Level author: '%s'\n", level->author);
8368 Print("Level title: '%s'\n", level->name);
8370 Print("Playfield size: %d x %d\n", level->fieldx, level->fieldy);
8372 Print("Level time: %d seconds\n", level->time);
8373 Print("Gems needed: %d\n", level->gems_needed);
8375 Print("Time for magic wall: %d seconds\n", level->time_magic_wall);
8376 Print("Time for wheel: %d seconds\n", level->time_wheel);
8377 Print("Time for light: %d seconds\n", level->time_light);
8378 Print("Time for timegate: %d seconds\n", level->time_timegate);
8380 Print("Amoeba speed: %d\n", level->amoeba_speed);
8383 Print("EM style slippery gems: %s\n", (level->em_slippery_gems ? "yes" : "no"));
8384 Print("Player blocks last field: %s\n", (level->block_last_field ? "yes" : "no"));
8385 Print("SP player blocks last field: %s\n", (level->sp_block_last_field ? "yes" : "no"));
8386 Print("use spring bug: %s\n", (level->use_spring_bug ? "yes" : "no"));
8387 Print("use step counter: %s\n", (level->use_step_counter ? "yes" : "no"));
8388 Print("rate time over score: %s\n", (level->rate_time_over_score ? "yes" : "no"));
8394 for (i = 0; i < NUM_ENVELOPES; i++)
8396 char *text = level->envelope[i].text;
8397 int text_len = strlen(text);
8398 boolean has_text = FALSE;
8400 for (j = 0; j < text_len; j++)
8401 if (text[j] != ' ' && text[j] != '\n')
8407 Print("Envelope %d:\n'%s'\n", i + 1, text);
8415 void DumpLevels(void)
8417 static LevelDirTree *dumplevel_leveldir = NULL;
8419 dumplevel_leveldir = getTreeInfoFromIdentifier(leveldir_first,
8420 global.dumplevel_leveldir);
8422 if (dumplevel_leveldir == NULL)
8423 Fail("no such level identifier: '%s'", global.dumplevel_leveldir);
8425 if (global.dumplevel_level_nr < dumplevel_leveldir->first_level ||
8426 global.dumplevel_level_nr > dumplevel_leveldir->last_level)
8427 Fail("no such level number: %d", global.dumplevel_level_nr);
8429 leveldir_current = dumplevel_leveldir;
8431 LoadLevel(global.dumplevel_level_nr);
8438 // ============================================================================
8439 // tape file functions
8440 // ============================================================================
8442 static void setTapeInfoToDefaults(void)
8446 // always start with reliable default values (empty tape)
8449 // default values (also for pre-1.2 tapes) with only the first player
8450 tape.player_participates[0] = TRUE;
8451 for (i = 1; i < MAX_PLAYERS; i++)
8452 tape.player_participates[i] = FALSE;
8454 // at least one (default: the first) player participates in every tape
8455 tape.num_participating_players = 1;
8457 tape.property_bits = TAPE_PROPERTY_NONE;
8459 tape.level_nr = level_nr;
8461 tape.changed = FALSE;
8462 tape.solved = FALSE;
8464 tape.recording = FALSE;
8465 tape.playing = FALSE;
8466 tape.pausing = FALSE;
8468 tape.scr_fieldx = SCR_FIELDX_DEFAULT;
8469 tape.scr_fieldy = SCR_FIELDY_DEFAULT;
8471 tape.no_info_chunk = TRUE;
8472 tape.no_valid_file = FALSE;
8475 static int getTapePosSize(struct TapeInfo *tape)
8477 int tape_pos_size = 0;
8479 if (tape->use_key_actions)
8480 tape_pos_size += tape->num_participating_players;
8482 if (tape->use_mouse_actions)
8483 tape_pos_size += 3; // x and y position and mouse button mask
8485 tape_pos_size += 1; // tape action delay value
8487 return tape_pos_size;
8490 static void setTapeActionFlags(struct TapeInfo *tape, int value)
8492 tape->use_key_actions = FALSE;
8493 tape->use_mouse_actions = FALSE;
8495 if (value != TAPE_USE_MOUSE_ACTIONS_ONLY)
8496 tape->use_key_actions = TRUE;
8498 if (value != TAPE_USE_KEY_ACTIONS_ONLY)
8499 tape->use_mouse_actions = TRUE;
8502 static int getTapeActionValue(struct TapeInfo *tape)
8504 return (tape->use_key_actions &&
8505 tape->use_mouse_actions ? TAPE_USE_KEY_AND_MOUSE_ACTIONS :
8506 tape->use_key_actions ? TAPE_USE_KEY_ACTIONS_ONLY :
8507 tape->use_mouse_actions ? TAPE_USE_MOUSE_ACTIONS_ONLY :
8508 TAPE_ACTIONS_DEFAULT);
8511 static int LoadTape_VERS(File *file, int chunk_size, struct TapeInfo *tape)
8513 tape->file_version = getFileVersion(file);
8514 tape->game_version = getFileVersion(file);
8519 static int LoadTape_HEAD(File *file, int chunk_size, struct TapeInfo *tape)
8523 tape->random_seed = getFile32BitBE(file);
8524 tape->date = getFile32BitBE(file);
8525 tape->length = getFile32BitBE(file);
8527 // read header fields that are new since version 1.2
8528 if (tape->file_version >= FILE_VERSION_1_2)
8530 byte store_participating_players = getFile8Bit(file);
8533 // since version 1.2, tapes store which players participate in the tape
8534 tape->num_participating_players = 0;
8535 for (i = 0; i < MAX_PLAYERS; i++)
8537 tape->player_participates[i] = FALSE;
8539 if (store_participating_players & (1 << i))
8541 tape->player_participates[i] = TRUE;
8542 tape->num_participating_players++;
8546 setTapeActionFlags(tape, getFile8Bit(file));
8548 tape->property_bits = getFile8Bit(file);
8549 tape->solved = getFile8Bit(file);
8551 engine_version = getFileVersion(file);
8552 if (engine_version > 0)
8553 tape->engine_version = engine_version;
8555 tape->engine_version = tape->game_version;
8561 static int LoadTape_SCRN(File *file, int chunk_size, struct TapeInfo *tape)
8563 tape->scr_fieldx = getFile8Bit(file);
8564 tape->scr_fieldy = getFile8Bit(file);
8569 static int LoadTape_INFO(File *file, int chunk_size, struct TapeInfo *tape)
8571 char *level_identifier = NULL;
8572 int level_identifier_size;
8575 tape->no_info_chunk = FALSE;
8577 level_identifier_size = getFile16BitBE(file);
8579 level_identifier = checked_malloc(level_identifier_size);
8581 for (i = 0; i < level_identifier_size; i++)
8582 level_identifier[i] = getFile8Bit(file);
8584 strncpy(tape->level_identifier, level_identifier, MAX_FILENAME_LEN);
8585 tape->level_identifier[MAX_FILENAME_LEN] = '\0';
8587 checked_free(level_identifier);
8589 tape->level_nr = getFile16BitBE(file);
8591 chunk_size = 2 + level_identifier_size + 2;
8596 static int LoadTape_BODY(File *file, int chunk_size, struct TapeInfo *tape)
8599 int tape_pos_size = getTapePosSize(tape);
8600 int chunk_size_expected = tape_pos_size * tape->length;
8602 if (chunk_size_expected != chunk_size)
8604 ReadUnusedBytesFromFile(file, chunk_size);
8605 return chunk_size_expected;
8608 for (i = 0; i < tape->length; i++)
8610 if (i >= MAX_TAPE_LEN)
8612 Warn("tape truncated -- size exceeds maximum tape size %d",
8615 // tape too large; read and ignore remaining tape data from this chunk
8616 for (;i < tape->length; i++)
8617 ReadUnusedBytesFromFile(file, tape_pos_size);
8622 if (tape->use_key_actions)
8624 for (j = 0; j < MAX_PLAYERS; j++)
8626 tape->pos[i].action[j] = MV_NONE;
8628 if (tape->player_participates[j])
8629 tape->pos[i].action[j] = getFile8Bit(file);
8633 if (tape->use_mouse_actions)
8635 tape->pos[i].action[TAPE_ACTION_LX] = getFile8Bit(file);
8636 tape->pos[i].action[TAPE_ACTION_LY] = getFile8Bit(file);
8637 tape->pos[i].action[TAPE_ACTION_BUTTON] = getFile8Bit(file);
8640 tape->pos[i].delay = getFile8Bit(file);
8642 if (tape->file_version == FILE_VERSION_1_0)
8644 // eliminate possible diagonal moves in old tapes
8645 // this is only for backward compatibility
8647 byte joy_dir[4] = { JOY_LEFT, JOY_RIGHT, JOY_UP, JOY_DOWN };
8648 byte action = tape->pos[i].action[0];
8649 int k, num_moves = 0;
8651 for (k = 0; k < 4; k++)
8653 if (action & joy_dir[k])
8655 tape->pos[i + num_moves].action[0] = joy_dir[k];
8657 tape->pos[i + num_moves].delay = 0;
8666 tape->length += num_moves;
8669 else if (tape->file_version < FILE_VERSION_2_0)
8671 // convert pre-2.0 tapes to new tape format
8673 if (tape->pos[i].delay > 1)
8676 tape->pos[i + 1] = tape->pos[i];
8677 tape->pos[i + 1].delay = 1;
8680 for (j = 0; j < MAX_PLAYERS; j++)
8681 tape->pos[i].action[j] = MV_NONE;
8682 tape->pos[i].delay--;
8689 if (checkEndOfFile(file))
8693 if (i != tape->length)
8694 chunk_size = tape_pos_size * i;
8699 static void LoadTape_SokobanSolution(char *filename)
8702 int move_delay = TILESIZE / level.initial_player_stepsize[0];
8704 if (!(file = openFile(filename, MODE_READ)))
8706 tape.no_valid_file = TRUE;
8711 while (!checkEndOfFile(file))
8713 unsigned char c = getByteFromFile(file);
8715 if (checkEndOfFile(file))
8722 tape.pos[tape.length].action[0] = MV_UP;
8723 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
8729 tape.pos[tape.length].action[0] = MV_DOWN;
8730 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
8736 tape.pos[tape.length].action[0] = MV_LEFT;
8737 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
8743 tape.pos[tape.length].action[0] = MV_RIGHT;
8744 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
8752 // ignore white-space characters
8756 tape.no_valid_file = TRUE;
8758 Warn("unsupported Sokoban solution file '%s' ['%d']", filename, c);
8766 if (tape.no_valid_file)
8769 tape.length_frames = GetTapeLengthFrames();
8770 tape.length_seconds = GetTapeLengthSeconds();
8773 void LoadTapeFromFilename(char *filename)
8775 char cookie[MAX_LINE_LEN];
8776 char chunk_name[CHUNK_ID_LEN + 1];
8780 // always start with reliable default values
8781 setTapeInfoToDefaults();
8783 if (strSuffix(filename, ".sln"))
8785 LoadTape_SokobanSolution(filename);
8790 if (!(file = openFile(filename, MODE_READ)))
8792 tape.no_valid_file = TRUE;
8797 getFileChunkBE(file, chunk_name, NULL);
8798 if (strEqual(chunk_name, "RND1"))
8800 getFile32BitBE(file); // not used
8802 getFileChunkBE(file, chunk_name, NULL);
8803 if (!strEqual(chunk_name, "TAPE"))
8805 tape.no_valid_file = TRUE;
8807 Warn("unknown format of tape file '%s'", filename);
8814 else // check for pre-2.0 file format with cookie string
8816 strcpy(cookie, chunk_name);
8817 if (getStringFromFile(file, &cookie[4], MAX_LINE_LEN - 4) == NULL)
8819 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
8820 cookie[strlen(cookie) - 1] = '\0';
8822 if (!checkCookieString(cookie, TAPE_COOKIE_TMPL))
8824 tape.no_valid_file = TRUE;
8826 Warn("unknown format of tape file '%s'", filename);
8833 if ((tape.file_version = getFileVersionFromCookieString(cookie)) == -1)
8835 tape.no_valid_file = TRUE;
8837 Warn("unsupported version of tape file '%s'", filename);
8844 // pre-2.0 tape files have no game version, so use file version here
8845 tape.game_version = tape.file_version;
8848 if (tape.file_version < FILE_VERSION_1_2)
8850 // tape files from versions before 1.2.0 without chunk structure
8851 LoadTape_HEAD(file, TAPE_CHUNK_HEAD_SIZE, &tape);
8852 LoadTape_BODY(file, 2 * tape.length, &tape);
8860 int (*loader)(File *, int, struct TapeInfo *);
8864 { "VERS", TAPE_CHUNK_VERS_SIZE, LoadTape_VERS },
8865 { "HEAD", TAPE_CHUNK_HEAD_SIZE, LoadTape_HEAD },
8866 { "SCRN", TAPE_CHUNK_SCRN_SIZE, LoadTape_SCRN },
8867 { "INFO", -1, LoadTape_INFO },
8868 { "BODY", -1, LoadTape_BODY },
8872 while (getFileChunkBE(file, chunk_name, &chunk_size))
8876 while (chunk_info[i].name != NULL &&
8877 !strEqual(chunk_name, chunk_info[i].name))
8880 if (chunk_info[i].name == NULL)
8882 Warn("unknown chunk '%s' in tape file '%s'",
8883 chunk_name, filename);
8885 ReadUnusedBytesFromFile(file, chunk_size);
8887 else if (chunk_info[i].size != -1 &&
8888 chunk_info[i].size != chunk_size)
8890 Warn("wrong size (%d) of chunk '%s' in tape file '%s'",
8891 chunk_size, chunk_name, filename);
8893 ReadUnusedBytesFromFile(file, chunk_size);
8897 // call function to load this tape chunk
8898 int chunk_size_expected =
8899 (chunk_info[i].loader)(file, chunk_size, &tape);
8901 // the size of some chunks cannot be checked before reading other
8902 // chunks first (like "HEAD" and "BODY") that contain some header
8903 // information, so check them here
8904 if (chunk_size_expected != chunk_size)
8906 Warn("wrong size (%d) of chunk '%s' in tape file '%s'",
8907 chunk_size, chunk_name, filename);
8915 tape.length_frames = GetTapeLengthFrames();
8916 tape.length_seconds = GetTapeLengthSeconds();
8919 Debug("files:LoadTapeFromFilename", "tape file version: %d",
8921 Debug("files:LoadTapeFromFilename", "tape game version: %d",
8923 Debug("files:LoadTapeFromFilename", "tape engine version: %d",
8924 tape.engine_version);
8928 void LoadTape(int nr)
8930 char *filename = getTapeFilename(nr);
8932 LoadTapeFromFilename(filename);
8935 void LoadSolutionTape(int nr)
8937 char *filename = getSolutionTapeFilename(nr);
8939 LoadTapeFromFilename(filename);
8941 if (TAPE_IS_EMPTY(tape))
8943 if (level.game_engine_type == GAME_ENGINE_TYPE_BD &&
8944 level.native_bd_level->replay != NULL)
8945 CopyNativeTape_BD_to_RND(&level);
8946 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP &&
8947 level.native_sp_level->demo.is_available)
8948 CopyNativeTape_SP_to_RND(&level);
8952 void LoadScoreTape(char *score_tape_basename, int nr)
8954 char *filename = getScoreTapeFilename(score_tape_basename, nr);
8956 LoadTapeFromFilename(filename);
8959 void LoadScoreCacheTape(char *score_tape_basename, int nr)
8961 char *filename = getScoreCacheTapeFilename(score_tape_basename, nr);
8963 LoadTapeFromFilename(filename);
8966 static boolean checkSaveTape_SCRN(struct TapeInfo *tape)
8968 // chunk required for team mode tapes with non-default screen size
8969 return (tape->num_participating_players > 1 &&
8970 (tape->scr_fieldx != SCR_FIELDX_DEFAULT ||
8971 tape->scr_fieldy != SCR_FIELDY_DEFAULT));
8974 static void SaveTape_VERS(FILE *file, struct TapeInfo *tape)
8976 putFileVersion(file, tape->file_version);
8977 putFileVersion(file, tape->game_version);
8980 static void SaveTape_HEAD(FILE *file, struct TapeInfo *tape)
8983 byte store_participating_players = 0;
8985 // set bits for participating players for compact storage
8986 for (i = 0; i < MAX_PLAYERS; i++)
8987 if (tape->player_participates[i])
8988 store_participating_players |= (1 << i);
8990 putFile32BitBE(file, tape->random_seed);
8991 putFile32BitBE(file, tape->date);
8992 putFile32BitBE(file, tape->length);
8994 putFile8Bit(file, store_participating_players);
8996 putFile8Bit(file, getTapeActionValue(tape));
8998 putFile8Bit(file, tape->property_bits);
8999 putFile8Bit(file, tape->solved);
9001 putFileVersion(file, tape->engine_version);
9004 static void SaveTape_SCRN(FILE *file, struct TapeInfo *tape)
9006 putFile8Bit(file, tape->scr_fieldx);
9007 putFile8Bit(file, tape->scr_fieldy);
9010 static void SaveTape_INFO(FILE *file, struct TapeInfo *tape)
9012 int level_identifier_size = strlen(tape->level_identifier) + 1;
9015 putFile16BitBE(file, level_identifier_size);
9017 for (i = 0; i < level_identifier_size; i++)
9018 putFile8Bit(file, tape->level_identifier[i]);
9020 putFile16BitBE(file, tape->level_nr);
9023 static void SaveTape_BODY(FILE *file, struct TapeInfo *tape)
9027 for (i = 0; i < tape->length; i++)
9029 if (tape->use_key_actions)
9031 for (j = 0; j < MAX_PLAYERS; j++)
9032 if (tape->player_participates[j])
9033 putFile8Bit(file, tape->pos[i].action[j]);
9036 if (tape->use_mouse_actions)
9038 putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_LX]);
9039 putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_LY]);
9040 putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_BUTTON]);
9043 putFile8Bit(file, tape->pos[i].delay);
9047 void SaveTapeToFilename(char *filename)
9051 int info_chunk_size;
9052 int body_chunk_size;
9054 if (!(file = fopen(filename, MODE_WRITE)))
9056 Warn("cannot save level recording file '%s'", filename);
9061 tape_pos_size = getTapePosSize(&tape);
9063 info_chunk_size = 2 + (strlen(tape.level_identifier) + 1) + 2;
9064 body_chunk_size = tape_pos_size * tape.length;
9066 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
9067 putFileChunkBE(file, "TAPE", CHUNK_SIZE_NONE);
9069 putFileChunkBE(file, "VERS", TAPE_CHUNK_VERS_SIZE);
9070 SaveTape_VERS(file, &tape);
9072 putFileChunkBE(file, "HEAD", TAPE_CHUNK_HEAD_SIZE);
9073 SaveTape_HEAD(file, &tape);
9075 if (checkSaveTape_SCRN(&tape))
9077 putFileChunkBE(file, "SCRN", TAPE_CHUNK_SCRN_SIZE);
9078 SaveTape_SCRN(file, &tape);
9081 putFileChunkBE(file, "INFO", info_chunk_size);
9082 SaveTape_INFO(file, &tape);
9084 putFileChunkBE(file, "BODY", body_chunk_size);
9085 SaveTape_BODY(file, &tape);
9089 SetFilePermissions(filename, PERMS_PRIVATE);
9092 static void SaveTapeExt(char *filename)
9096 tape.file_version = FILE_VERSION_ACTUAL;
9097 tape.game_version = GAME_VERSION_ACTUAL;
9099 tape.num_participating_players = 0;
9101 // count number of participating players
9102 for (i = 0; i < MAX_PLAYERS; i++)
9103 if (tape.player_participates[i])
9104 tape.num_participating_players++;
9106 SaveTapeToFilename(filename);
9108 tape.changed = FALSE;
9111 void SaveTape(int nr)
9113 char *filename = getTapeFilename(nr);
9115 InitTapeDirectory(leveldir_current->subdir);
9117 SaveTapeExt(filename);
9120 void SaveScoreTape(int nr)
9122 char *filename = getScoreTapeFilename(tape.score_tape_basename, nr);
9124 // used instead of "leveldir_current->subdir" (for network games)
9125 InitScoreTapeDirectory(levelset.identifier, nr);
9127 SaveTapeExt(filename);
9130 static boolean SaveTapeCheckedExt(int nr, char *msg_replace, char *msg_saved,
9131 unsigned int req_state_added)
9133 char *filename = getTapeFilename(nr);
9134 boolean new_tape = !fileExists(filename);
9135 boolean tape_saved = FALSE;
9137 if (new_tape || Request(msg_replace, REQ_ASK | req_state_added))
9142 Request(msg_saved, REQ_CONFIRM | req_state_added);
9150 boolean SaveTapeChecked(int nr)
9152 return SaveTapeCheckedExt(nr, "Replace old tape?", "Tape saved!", 0);
9155 boolean SaveTapeChecked_LevelSolved(int nr)
9157 return SaveTapeCheckedExt(nr, "Level solved! Replace old tape?",
9158 "Level solved! Tape saved!", REQ_STAY_OPEN);
9161 void DumpTape(struct TapeInfo *tape)
9163 int tape_frame_counter;
9166 if (tape->no_valid_file)
9168 Warn("cannot dump -- no valid tape file found");
9175 Print("Tape of Level %03d (file version %08d, game version %08d)\n",
9176 tape->level_nr, tape->file_version, tape->game_version);
9177 Print(" (effective engine version %08d)\n",
9178 tape->engine_version);
9179 Print("Level series identifier: '%s'\n", tape->level_identifier);
9181 Print("Solution tape: %s\n",
9182 tape->solved ? "yes" :
9183 tape->game_version < VERSION_IDENT(4,3,2,3) ? "unknown" : "no");
9185 Print("Special tape properties: ");
9186 if (tape->property_bits == TAPE_PROPERTY_NONE)
9188 if (tape->property_bits & TAPE_PROPERTY_EM_RANDOM_BUG)
9189 Print("[em_random_bug]");
9190 if (tape->property_bits & TAPE_PROPERTY_GAME_SPEED)
9191 Print("[game_speed]");
9192 if (tape->property_bits & TAPE_PROPERTY_PAUSE_MODE)
9194 if (tape->property_bits & TAPE_PROPERTY_SINGLE_STEP)
9195 Print("[single_step]");
9196 if (tape->property_bits & TAPE_PROPERTY_SNAPSHOT)
9197 Print("[snapshot]");
9198 if (tape->property_bits & TAPE_PROPERTY_REPLAYED)
9199 Print("[replayed]");
9200 if (tape->property_bits & TAPE_PROPERTY_TAS_KEYS)
9201 Print("[tas_keys]");
9202 if (tape->property_bits & TAPE_PROPERTY_SMALL_GRAPHICS)
9203 Print("[small_graphics]");
9206 int year2 = tape->date / 10000;
9207 int year4 = (year2 < 70 ? 2000 + year2 : 1900 + year2);
9208 int month_index_raw = (tape->date / 100) % 100;
9209 int month_index = month_index_raw % 12; // prevent invalid index
9210 int month = month_index + 1;
9211 int day = tape->date % 100;
9213 Print("Tape date: %04d-%02d-%02d\n", year4, month, day);
9217 tape_frame_counter = 0;
9219 for (i = 0; i < tape->length; i++)
9221 if (i >= MAX_TAPE_LEN)
9226 for (j = 0; j < MAX_PLAYERS; j++)
9228 if (tape->player_participates[j])
9230 int action = tape->pos[i].action[j];
9232 Print("%d:%02x ", j, action);
9233 Print("[%c%c%c%c|%c%c] - ",
9234 (action & JOY_LEFT ? '<' : ' '),
9235 (action & JOY_RIGHT ? '>' : ' '),
9236 (action & JOY_UP ? '^' : ' '),
9237 (action & JOY_DOWN ? 'v' : ' '),
9238 (action & JOY_BUTTON_1 ? '1' : ' '),
9239 (action & JOY_BUTTON_2 ? '2' : ' '));
9243 Print("(%03d) ", tape->pos[i].delay);
9244 Print("[%05d]\n", tape_frame_counter);
9246 tape_frame_counter += tape->pos[i].delay;
9252 void DumpTapes(void)
9254 static LevelDirTree *dumptape_leveldir = NULL;
9256 dumptape_leveldir = getTreeInfoFromIdentifier(leveldir_first,
9257 global.dumptape_leveldir);
9259 if (dumptape_leveldir == NULL)
9260 Fail("no such level identifier: '%s'", global.dumptape_leveldir);
9262 if (global.dumptape_level_nr < dumptape_leveldir->first_level ||
9263 global.dumptape_level_nr > dumptape_leveldir->last_level)
9264 Fail("no such level number: %d", global.dumptape_level_nr);
9266 leveldir_current = dumptape_leveldir;
9268 if (options.mytapes)
9269 LoadTape(global.dumptape_level_nr);
9271 LoadSolutionTape(global.dumptape_level_nr);
9279 // ============================================================================
9280 // score file functions
9281 // ============================================================================
9283 static void setScoreInfoToDefaultsExt(struct ScoreInfo *scores)
9287 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9289 strcpy(scores->entry[i].tape_basename, UNDEFINED_FILENAME);
9290 strcpy(scores->entry[i].name, EMPTY_PLAYER_NAME);
9291 scores->entry[i].score = 0;
9292 scores->entry[i].time = 0;
9294 scores->entry[i].id = -1;
9295 strcpy(scores->entry[i].tape_date, UNKNOWN_NAME);
9296 strcpy(scores->entry[i].platform, UNKNOWN_NAME);
9297 strcpy(scores->entry[i].version, UNKNOWN_NAME);
9298 strcpy(scores->entry[i].country_name, UNKNOWN_NAME);
9299 strcpy(scores->entry[i].country_code, "??");
9302 scores->num_entries = 0;
9303 scores->last_added = -1;
9304 scores->last_added_local = -1;
9306 scores->updated = FALSE;
9307 scores->uploaded = FALSE;
9308 scores->tape_downloaded = FALSE;
9309 scores->force_last_added = FALSE;
9311 // The following values are intentionally not reset here:
9315 // - continue_playing
9316 // - continue_on_return
9319 static void setScoreInfoToDefaults(void)
9321 setScoreInfoToDefaultsExt(&scores);
9324 static void setServerScoreInfoToDefaults(void)
9326 setScoreInfoToDefaultsExt(&server_scores);
9329 static void LoadScore_OLD(int nr)
9332 char *filename = getScoreFilename(nr);
9333 char cookie[MAX_LINE_LEN];
9334 char line[MAX_LINE_LEN];
9338 if (!(file = fopen(filename, MODE_READ)))
9341 // check file identifier
9342 if (fgets(cookie, MAX_LINE_LEN, file) == NULL)
9344 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
9345 cookie[strlen(cookie) - 1] = '\0';
9347 if (!checkCookieString(cookie, SCORE_COOKIE_TMPL))
9349 Warn("unknown format of score file '%s'", filename);
9356 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9358 if (fscanf(file, "%d", &scores.entry[i].score) == EOF)
9359 Warn("fscanf() failed; %s", strerror(errno));
9361 if (fgets(line, MAX_LINE_LEN, file) == NULL)
9364 if (strlen(line) > 0 && line[strlen(line) - 1] == '\n')
9365 line[strlen(line) - 1] = '\0';
9367 for (line_ptr = line; *line_ptr; line_ptr++)
9369 if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != '\0')
9371 strncpy(scores.entry[i].name, line_ptr, MAX_PLAYER_NAME_LEN);
9372 scores.entry[i].name[MAX_PLAYER_NAME_LEN] = '\0';
9381 static void ConvertScore_OLD(void)
9383 // only convert score to time for levels that rate playing time over score
9384 if (!level.rate_time_over_score)
9387 // convert old score to playing time for score-less levels (like Supaplex)
9388 int time_final_max = 999;
9391 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9393 int score = scores.entry[i].score;
9395 if (score > 0 && score < time_final_max)
9396 scores.entry[i].time = (time_final_max - score - 1) * FRAMES_PER_SECOND;
9400 static int LoadScore_VERS(File *file, int chunk_size, struct ScoreInfo *scores)
9402 scores->file_version = getFileVersion(file);
9403 scores->game_version = getFileVersion(file);
9408 static int LoadScore_INFO(File *file, int chunk_size, struct ScoreInfo *scores)
9410 char *level_identifier = NULL;
9411 int level_identifier_size;
9414 level_identifier_size = getFile16BitBE(file);
9416 level_identifier = checked_malloc(level_identifier_size);
9418 for (i = 0; i < level_identifier_size; i++)
9419 level_identifier[i] = getFile8Bit(file);
9421 strncpy(scores->level_identifier, level_identifier, MAX_FILENAME_LEN);
9422 scores->level_identifier[MAX_FILENAME_LEN] = '\0';
9424 checked_free(level_identifier);
9426 scores->level_nr = getFile16BitBE(file);
9427 scores->num_entries = getFile16BitBE(file);
9429 chunk_size = 2 + level_identifier_size + 2 + 2;
9434 static int LoadScore_NAME(File *file, int chunk_size, struct ScoreInfo *scores)
9438 for (i = 0; i < scores->num_entries; i++)
9440 for (j = 0; j < MAX_PLAYER_NAME_LEN; j++)
9441 scores->entry[i].name[j] = getFile8Bit(file);
9443 scores->entry[i].name[MAX_PLAYER_NAME_LEN] = '\0';
9446 chunk_size = scores->num_entries * MAX_PLAYER_NAME_LEN;
9451 static int LoadScore_SCOR(File *file, int chunk_size, struct ScoreInfo *scores)
9455 for (i = 0; i < scores->num_entries; i++)
9456 scores->entry[i].score = getFile16BitBE(file);
9458 chunk_size = scores->num_entries * 2;
9463 static int LoadScore_SC4R(File *file, int chunk_size, struct ScoreInfo *scores)
9467 for (i = 0; i < scores->num_entries; i++)
9468 scores->entry[i].score = getFile32BitBE(file);
9470 chunk_size = scores->num_entries * 4;
9475 static int LoadScore_TIME(File *file, int chunk_size, struct ScoreInfo *scores)
9479 for (i = 0; i < scores->num_entries; i++)
9480 scores->entry[i].time = getFile32BitBE(file);
9482 chunk_size = scores->num_entries * 4;
9487 static int LoadScore_TAPE(File *file, int chunk_size, struct ScoreInfo *scores)
9491 for (i = 0; i < scores->num_entries; i++)
9493 for (j = 0; j < MAX_SCORE_TAPE_BASENAME_LEN; j++)
9494 scores->entry[i].tape_basename[j] = getFile8Bit(file);
9496 scores->entry[i].tape_basename[MAX_SCORE_TAPE_BASENAME_LEN] = '\0';
9499 chunk_size = scores->num_entries * MAX_SCORE_TAPE_BASENAME_LEN;
9504 void LoadScore(int nr)
9506 char *filename = getScoreFilename(nr);
9507 char cookie[MAX_LINE_LEN];
9508 char chunk_name[CHUNK_ID_LEN + 1];
9510 boolean old_score_file_format = FALSE;
9513 // always start with reliable default values
9514 setScoreInfoToDefaults();
9516 if (!(file = openFile(filename, MODE_READ)))
9519 getFileChunkBE(file, chunk_name, NULL);
9520 if (strEqual(chunk_name, "RND1"))
9522 getFile32BitBE(file); // not used
9524 getFileChunkBE(file, chunk_name, NULL);
9525 if (!strEqual(chunk_name, "SCOR"))
9527 Warn("unknown format of score file '%s'", filename);
9534 else // check for old file format with cookie string
9536 strcpy(cookie, chunk_name);
9537 if (getStringFromFile(file, &cookie[4], MAX_LINE_LEN - 4) == NULL)
9539 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
9540 cookie[strlen(cookie) - 1] = '\0';
9542 if (!checkCookieString(cookie, SCORE_COOKIE_TMPL))
9544 Warn("unknown format of score file '%s'", filename);
9551 old_score_file_format = TRUE;
9554 if (old_score_file_format)
9556 // score files from versions before 4.2.4.0 without chunk structure
9559 // convert score to time, if possible (mainly for Supaplex levels)
9568 int (*loader)(File *, int, struct ScoreInfo *);
9572 { "VERS", SCORE_CHUNK_VERS_SIZE, LoadScore_VERS },
9573 { "INFO", -1, LoadScore_INFO },
9574 { "NAME", -1, LoadScore_NAME },
9575 { "SCOR", -1, LoadScore_SCOR },
9576 { "SC4R", -1, LoadScore_SC4R },
9577 { "TIME", -1, LoadScore_TIME },
9578 { "TAPE", -1, LoadScore_TAPE },
9583 while (getFileChunkBE(file, chunk_name, &chunk_size))
9587 while (chunk_info[i].name != NULL &&
9588 !strEqual(chunk_name, chunk_info[i].name))
9591 if (chunk_info[i].name == NULL)
9593 Warn("unknown chunk '%s' in score file '%s'",
9594 chunk_name, filename);
9596 ReadUnusedBytesFromFile(file, chunk_size);
9598 else if (chunk_info[i].size != -1 &&
9599 chunk_info[i].size != chunk_size)
9601 Warn("wrong size (%d) of chunk '%s' in score file '%s'",
9602 chunk_size, chunk_name, filename);
9604 ReadUnusedBytesFromFile(file, chunk_size);
9608 // call function to load this score chunk
9609 int chunk_size_expected =
9610 (chunk_info[i].loader)(file, chunk_size, &scores);
9612 // the size of some chunks cannot be checked before reading other
9613 // chunks first (like "HEAD" and "BODY") that contain some header
9614 // information, so check them here
9615 if (chunk_size_expected != chunk_size)
9617 Warn("wrong size (%d) of chunk '%s' in score file '%s'",
9618 chunk_size, chunk_name, filename);
9627 #if ENABLE_HISTORIC_CHUNKS
9628 void SaveScore_OLD(int nr)
9631 char *filename = getScoreFilename(nr);
9634 // used instead of "leveldir_current->subdir" (for network games)
9635 InitScoreDirectory(levelset.identifier);
9637 if (!(file = fopen(filename, MODE_WRITE)))
9639 Warn("cannot save score for level %d", nr);
9644 fprintf(file, "%s\n\n", SCORE_COOKIE);
9646 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9647 fprintf(file, "%d %s\n", scores.entry[i].score, scores.entry[i].name);
9651 SetFilePermissions(filename, PERMS_PRIVATE);
9655 static void SaveScore_VERS(FILE *file, struct ScoreInfo *scores)
9657 putFileVersion(file, scores->file_version);
9658 putFileVersion(file, scores->game_version);
9661 static void SaveScore_INFO(FILE *file, struct ScoreInfo *scores)
9663 int level_identifier_size = strlen(scores->level_identifier) + 1;
9666 putFile16BitBE(file, level_identifier_size);
9668 for (i = 0; i < level_identifier_size; i++)
9669 putFile8Bit(file, scores->level_identifier[i]);
9671 putFile16BitBE(file, scores->level_nr);
9672 putFile16BitBE(file, scores->num_entries);
9675 static void SaveScore_NAME(FILE *file, struct ScoreInfo *scores)
9679 for (i = 0; i < scores->num_entries; i++)
9681 int name_size = strlen(scores->entry[i].name);
9683 for (j = 0; j < MAX_PLAYER_NAME_LEN; j++)
9684 putFile8Bit(file, (j < name_size ? scores->entry[i].name[j] : 0));
9688 static void SaveScore_SCOR(FILE *file, struct ScoreInfo *scores)
9692 for (i = 0; i < scores->num_entries; i++)
9693 putFile16BitBE(file, scores->entry[i].score);
9696 static void SaveScore_SC4R(FILE *file, struct ScoreInfo *scores)
9700 for (i = 0; i < scores->num_entries; i++)
9701 putFile32BitBE(file, scores->entry[i].score);
9704 static void SaveScore_TIME(FILE *file, struct ScoreInfo *scores)
9708 for (i = 0; i < scores->num_entries; i++)
9709 putFile32BitBE(file, scores->entry[i].time);
9712 static void SaveScore_TAPE(FILE *file, struct ScoreInfo *scores)
9716 for (i = 0; i < scores->num_entries; i++)
9718 int size = strlen(scores->entry[i].tape_basename);
9720 for (j = 0; j < MAX_SCORE_TAPE_BASENAME_LEN; j++)
9721 putFile8Bit(file, (j < size ? scores->entry[i].tape_basename[j] : 0));
9725 static void SaveScoreToFilename(char *filename)
9728 int info_chunk_size;
9729 int name_chunk_size;
9730 int scor_chunk_size;
9731 int sc4r_chunk_size;
9732 int time_chunk_size;
9733 int tape_chunk_size;
9734 boolean has_large_score_values;
9737 if (!(file = fopen(filename, MODE_WRITE)))
9739 Warn("cannot save score file '%s'", filename);
9744 info_chunk_size = 2 + (strlen(scores.level_identifier) + 1) + 2 + 2;
9745 name_chunk_size = scores.num_entries * MAX_PLAYER_NAME_LEN;
9746 scor_chunk_size = scores.num_entries * 2;
9747 sc4r_chunk_size = scores.num_entries * 4;
9748 time_chunk_size = scores.num_entries * 4;
9749 tape_chunk_size = scores.num_entries * MAX_SCORE_TAPE_BASENAME_LEN;
9751 has_large_score_values = FALSE;
9752 for (i = 0; i < scores.num_entries; i++)
9753 if (scores.entry[i].score > 0xffff)
9754 has_large_score_values = TRUE;
9756 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
9757 putFileChunkBE(file, "SCOR", CHUNK_SIZE_NONE);
9759 putFileChunkBE(file, "VERS", SCORE_CHUNK_VERS_SIZE);
9760 SaveScore_VERS(file, &scores);
9762 putFileChunkBE(file, "INFO", info_chunk_size);
9763 SaveScore_INFO(file, &scores);
9765 putFileChunkBE(file, "NAME", name_chunk_size);
9766 SaveScore_NAME(file, &scores);
9768 if (has_large_score_values)
9770 putFileChunkBE(file, "SC4R", sc4r_chunk_size);
9771 SaveScore_SC4R(file, &scores);
9775 putFileChunkBE(file, "SCOR", scor_chunk_size);
9776 SaveScore_SCOR(file, &scores);
9779 putFileChunkBE(file, "TIME", time_chunk_size);
9780 SaveScore_TIME(file, &scores);
9782 putFileChunkBE(file, "TAPE", tape_chunk_size);
9783 SaveScore_TAPE(file, &scores);
9787 SetFilePermissions(filename, PERMS_PRIVATE);
9790 void SaveScore(int nr)
9792 char *filename = getScoreFilename(nr);
9795 // used instead of "leveldir_current->subdir" (for network games)
9796 InitScoreDirectory(levelset.identifier);
9798 scores.file_version = FILE_VERSION_ACTUAL;
9799 scores.game_version = GAME_VERSION_ACTUAL;
9801 strncpy(scores.level_identifier, levelset.identifier, MAX_FILENAME_LEN);
9802 scores.level_identifier[MAX_FILENAME_LEN] = '\0';
9803 scores.level_nr = level_nr;
9805 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9806 if (scores.entry[i].score == 0 &&
9807 scores.entry[i].time == 0 &&
9808 strEqual(scores.entry[i].name, EMPTY_PLAYER_NAME))
9811 scores.num_entries = i;
9813 if (scores.num_entries == 0)
9816 SaveScoreToFilename(filename);
9819 static void LoadServerScoreFromCache(int nr)
9821 struct ScoreEntry score_entry;
9830 { &score_entry.score, FALSE, 0 },
9831 { &score_entry.time, FALSE, 0 },
9832 { score_entry.name, TRUE, MAX_PLAYER_NAME_LEN },
9833 { score_entry.tape_basename, TRUE, MAX_FILENAME_LEN },
9834 { score_entry.tape_date, TRUE, MAX_ISO_DATE_LEN },
9835 { &score_entry.id, FALSE, 0 },
9836 { score_entry.platform, TRUE, MAX_PLATFORM_TEXT_LEN },
9837 { score_entry.version, TRUE, MAX_VERSION_TEXT_LEN },
9838 { score_entry.country_code, TRUE, MAX_COUNTRY_CODE_LEN },
9839 { score_entry.country_name, TRUE, MAX_COUNTRY_NAME_LEN },
9843 char *filename = getScoreCacheFilename(nr);
9844 SetupFileHash *score_hash = loadSetupFileHash(filename);
9847 server_scores.num_entries = 0;
9849 if (score_hash == NULL)
9852 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9854 score_entry = server_scores.entry[i];
9856 for (j = 0; score_mapping[j].value != NULL; j++)
9860 sprintf(token, "%02d.%d", i, j);
9862 char *value = getHashEntry(score_hash, token);
9867 if (score_mapping[j].is_string)
9869 char *score_value = (char *)score_mapping[j].value;
9870 int value_size = score_mapping[j].string_size;
9872 strncpy(score_value, value, value_size);
9873 score_value[value_size] = '\0';
9877 int *score_value = (int *)score_mapping[j].value;
9879 *score_value = atoi(value);
9882 server_scores.num_entries = i + 1;
9885 server_scores.entry[i] = score_entry;
9888 freeSetupFileHash(score_hash);
9891 void LoadServerScore(int nr, boolean download_score)
9893 if (!setup.use_api_server)
9896 // always start with reliable default values
9897 setServerScoreInfoToDefaults();
9899 // 1st step: load server scores from cache file (which may not exist)
9900 // (this should prevent reading it while the thread is writing to it)
9901 LoadServerScoreFromCache(nr);
9903 if (download_score && runtime.use_api_server)
9905 // 2nd step: download server scores from score server to cache file
9906 // (as thread, as it might time out if the server is not reachable)
9907 ApiGetScoreAsThread(nr);
9911 void PrepareScoreTapesForUpload(char *leveldir_subdir)
9913 MarkTapeDirectoryUploadsAsIncomplete(leveldir_subdir);
9915 // if score tape not uploaded, ask for uploading missing tapes later
9916 if (!setup.has_remaining_tapes)
9917 setup.ask_for_remaining_tapes = TRUE;
9919 setup.provide_uploading_tapes = TRUE;
9920 setup.has_remaining_tapes = TRUE;
9922 SaveSetup_ServerSetup();
9925 void SaveServerScore(int nr, boolean tape_saved)
9927 if (!runtime.use_api_server)
9929 PrepareScoreTapesForUpload(leveldir_current->subdir);
9934 ApiAddScoreAsThread(nr, tape_saved, NULL);
9937 void SaveServerScoreFromFile(int nr, boolean tape_saved,
9938 char *score_tape_filename)
9940 if (!runtime.use_api_server)
9943 ApiAddScoreAsThread(nr, tape_saved, score_tape_filename);
9946 void LoadLocalAndServerScore(int nr, boolean download_score)
9948 int last_added_local = scores.last_added_local;
9949 boolean force_last_added = scores.force_last_added;
9951 // needed if only showing server scores
9952 setScoreInfoToDefaults();
9954 if (!strEqual(setup.scores_in_highscore_list, STR_SCORES_TYPE_SERVER_ONLY))
9957 // restore last added local score entry (before merging server scores)
9958 scores.last_added = scores.last_added_local = last_added_local;
9960 if (setup.use_api_server &&
9961 !strEqual(setup.scores_in_highscore_list, STR_SCORES_TYPE_LOCAL_ONLY))
9963 // load server scores from cache file and trigger update from server
9964 LoadServerScore(nr, download_score);
9966 // merge local scores with scores from server
9970 if (force_last_added)
9971 scores.force_last_added = force_last_added;
9975 // ============================================================================
9976 // setup file functions
9977 // ============================================================================
9979 #define TOKEN_STR_PLAYER_PREFIX "player_"
9982 static struct TokenInfo global_setup_tokens[] =
9986 &setup.player_name, "player_name"
9990 &setup.multiple_users, "multiple_users"
9994 &setup.sound, "sound"
9998 &setup.sound_loops, "repeating_sound_loops"
10002 &setup.sound_music, "background_music"
10006 &setup.sound_simple, "simple_sound_effects"
10010 &setup.toons, "toons"
10014 &setup.global_animations, "global_animations"
10018 &setup.scroll_delay, "scroll_delay"
10022 &setup.forced_scroll_delay, "forced_scroll_delay"
10026 &setup.scroll_delay_value, "scroll_delay_value"
10030 &setup.engine_snapshot_mode, "engine_snapshot_mode"
10034 &setup.engine_snapshot_memory, "engine_snapshot_memory"
10038 &setup.fade_screens, "fade_screens"
10042 &setup.autorecord, "automatic_tape_recording"
10046 &setup.autorecord_after_replay, "autorecord_after_replay"
10050 &setup.auto_pause_on_start, "auto_pause_on_start"
10054 &setup.show_titlescreen, "show_titlescreen"
10058 &setup.quick_doors, "quick_doors"
10062 &setup.team_mode, "team_mode"
10066 &setup.handicap, "handicap"
10070 &setup.skip_levels, "skip_levels"
10074 &setup.increment_levels, "increment_levels"
10078 &setup.auto_play_next_level, "auto_play_next_level"
10082 &setup.count_score_after_game, "count_score_after_game"
10086 &setup.show_scores_after_game, "show_scores_after_game"
10090 &setup.time_limit, "time_limit"
10094 &setup.fullscreen, "fullscreen"
10098 &setup.window_scaling_percent, "window_scaling_percent"
10102 &setup.window_scaling_quality, "window_scaling_quality"
10106 &setup.screen_rendering_mode, "screen_rendering_mode"
10110 &setup.vsync_mode, "vsync_mode"
10114 &setup.ask_on_escape, "ask_on_escape"
10118 &setup.ask_on_escape_editor, "ask_on_escape_editor"
10122 &setup.ask_on_game_over, "ask_on_game_over"
10126 &setup.ask_on_quit_game, "ask_on_quit_game"
10130 &setup.ask_on_quit_program, "ask_on_quit_program"
10134 &setup.quick_switch, "quick_player_switch"
10138 &setup.input_on_focus, "input_on_focus"
10142 &setup.prefer_aga_graphics, "prefer_aga_graphics"
10146 &setup.prefer_lowpass_sounds, "prefer_lowpass_sounds"
10150 &setup.prefer_extra_panel_items, "prefer_extra_panel_items"
10154 &setup.game_speed_extended, "game_speed_extended"
10158 &setup.game_frame_delay, "game_frame_delay"
10162 &setup.bd_skip_uncovering, "bd_skip_uncovering"
10166 &setup.bd_skip_hatching, "bd_skip_hatching"
10170 &setup.bd_scroll_delay, "bd_scroll_delay"
10174 &setup.bd_smooth_movements, "bd_smooth_movements"
10178 &setup.sp_show_border_elements, "sp_show_border_elements"
10182 &setup.small_game_graphics, "small_game_graphics"
10186 &setup.show_load_save_buttons, "show_load_save_buttons"
10190 &setup.show_undo_redo_buttons, "show_undo_redo_buttons"
10194 &setup.scores_in_highscore_list, "scores_in_highscore_list"
10198 &setup.graphics_set, "graphics_set"
10202 &setup.sounds_set, "sounds_set"
10206 &setup.music_set, "music_set"
10210 &setup.override_level_graphics, "override_level_graphics"
10214 &setup.override_level_sounds, "override_level_sounds"
10218 &setup.override_level_music, "override_level_music"
10222 &setup.volume_simple, "volume_simple"
10226 &setup.volume_loops, "volume_loops"
10230 &setup.volume_music, "volume_music"
10234 &setup.network_mode, "network_mode"
10238 &setup.network_player_nr, "network_player"
10242 &setup.network_server_hostname, "network_server_hostname"
10246 &setup.touch.control_type, "touch.control_type"
10250 &setup.touch.move_distance, "touch.move_distance"
10254 &setup.touch.drop_distance, "touch.drop_distance"
10258 &setup.touch.transparency, "touch.transparency"
10262 &setup.touch.draw_outlined, "touch.draw_outlined"
10266 &setup.touch.draw_pressed, "touch.draw_pressed"
10270 &setup.touch.grid_xsize[0], "touch.virtual_buttons.0.xsize"
10274 &setup.touch.grid_ysize[0], "touch.virtual_buttons.0.ysize"
10278 &setup.touch.grid_xsize[1], "touch.virtual_buttons.1.xsize"
10282 &setup.touch.grid_ysize[1], "touch.virtual_buttons.1.ysize"
10286 &setup.touch.overlay_buttons, "touch.overlay_buttons"
10290 static struct TokenInfo auto_setup_tokens[] =
10294 &setup.auto_setup.editor_zoom_tilesize, "editor.zoom_tilesize"
10298 static struct TokenInfo server_setup_tokens[] =
10302 &setup.player_uuid, "player_uuid"
10306 &setup.player_version, "player_version"
10310 &setup.use_api_server, TEST_PREFIX "use_api_server"
10314 &setup.api_server_hostname, TEST_PREFIX "api_server_hostname"
10318 &setup.api_server_password, TEST_PREFIX "api_server_password"
10322 &setup.ask_for_uploading_tapes, TEST_PREFIX "ask_for_uploading_tapes"
10326 &setup.ask_for_remaining_tapes, TEST_PREFIX "ask_for_remaining_tapes"
10330 &setup.provide_uploading_tapes, TEST_PREFIX "provide_uploading_tapes"
10334 &setup.ask_for_using_api_server,TEST_PREFIX "ask_for_using_api_server"
10338 &setup.has_remaining_tapes, TEST_PREFIX "has_remaining_tapes"
10342 static struct TokenInfo editor_setup_tokens[] =
10346 &setup.editor.el_classic, "editor.el_classic"
10350 &setup.editor.el_custom, "editor.el_custom"
10354 &setup.editor.el_user_defined, "editor.el_user_defined"
10358 &setup.editor.el_dynamic, "editor.el_dynamic"
10362 &setup.editor.el_headlines, "editor.el_headlines"
10366 &setup.editor.show_element_token, "editor.show_element_token"
10370 &setup.editor.show_read_only_warning, "editor.show_read_only_warning"
10374 static struct TokenInfo editor_cascade_setup_tokens[] =
10378 &setup.editor_cascade.el_bd, "editor.cascade.el_bd"
10382 &setup.editor_cascade.el_bd_native, "editor.cascade.el_bd_native"
10386 &setup.editor_cascade.el_em, "editor.cascade.el_em"
10390 &setup.editor_cascade.el_emc, "editor.cascade.el_emc"
10394 &setup.editor_cascade.el_rnd, "editor.cascade.el_rnd"
10398 &setup.editor_cascade.el_sb, "editor.cascade.el_sb"
10402 &setup.editor_cascade.el_sp, "editor.cascade.el_sp"
10406 &setup.editor_cascade.el_dc, "editor.cascade.el_dc"
10410 &setup.editor_cascade.el_dx, "editor.cascade.el_dx"
10414 &setup.editor_cascade.el_mm, "editor.cascade.el_mm"
10418 &setup.editor_cascade.el_df, "editor.cascade.el_df"
10422 &setup.editor_cascade.el_chars, "editor.cascade.el_chars"
10426 &setup.editor_cascade.el_steel_chars, "editor.cascade.el_steel_chars"
10430 &setup.editor_cascade.el_ce, "editor.cascade.el_ce"
10434 &setup.editor_cascade.el_ge, "editor.cascade.el_ge"
10438 &setup.editor_cascade.el_es, "editor.cascade.el_es"
10442 &setup.editor_cascade.el_ref, "editor.cascade.el_ref"
10446 &setup.editor_cascade.el_user, "editor.cascade.el_user"
10450 &setup.editor_cascade.el_dynamic, "editor.cascade.el_dynamic"
10454 static struct TokenInfo shortcut_setup_tokens[] =
10458 &setup.shortcut.save_game, "shortcut.save_game"
10462 &setup.shortcut.load_game, "shortcut.load_game"
10466 &setup.shortcut.restart_game, "shortcut.restart_game"
10470 &setup.shortcut.pause_before_end, "shortcut.pause_before_end"
10474 &setup.shortcut.toggle_pause, "shortcut.toggle_pause"
10478 &setup.shortcut.focus_player[0], "shortcut.focus_player_1"
10482 &setup.shortcut.focus_player[1], "shortcut.focus_player_2"
10486 &setup.shortcut.focus_player[2], "shortcut.focus_player_3"
10490 &setup.shortcut.focus_player[3], "shortcut.focus_player_4"
10494 &setup.shortcut.focus_player_all, "shortcut.focus_player_all"
10498 &setup.shortcut.tape_eject, "shortcut.tape_eject"
10502 &setup.shortcut.tape_extra, "shortcut.tape_extra"
10506 &setup.shortcut.tape_stop, "shortcut.tape_stop"
10510 &setup.shortcut.tape_pause, "shortcut.tape_pause"
10514 &setup.shortcut.tape_record, "shortcut.tape_record"
10518 &setup.shortcut.tape_play, "shortcut.tape_play"
10522 &setup.shortcut.sound_simple, "shortcut.sound_simple"
10526 &setup.shortcut.sound_loops, "shortcut.sound_loops"
10530 &setup.shortcut.sound_music, "shortcut.sound_music"
10534 &setup.shortcut.snap_left, "shortcut.snap_left"
10538 &setup.shortcut.snap_right, "shortcut.snap_right"
10542 &setup.shortcut.snap_up, "shortcut.snap_up"
10546 &setup.shortcut.snap_down, "shortcut.snap_down"
10550 static struct SetupInputInfo setup_input;
10551 static struct TokenInfo player_setup_tokens[] =
10555 &setup_input.use_joystick, ".use_joystick"
10559 &setup_input.joy.device_name, ".joy.device_name"
10563 &setup_input.joy.xleft, ".joy.xleft"
10567 &setup_input.joy.xmiddle, ".joy.xmiddle"
10571 &setup_input.joy.xright, ".joy.xright"
10575 &setup_input.joy.yupper, ".joy.yupper"
10579 &setup_input.joy.ymiddle, ".joy.ymiddle"
10583 &setup_input.joy.ylower, ".joy.ylower"
10587 &setup_input.joy.snap, ".joy.snap_field"
10591 &setup_input.joy.drop, ".joy.place_bomb"
10595 &setup_input.key.left, ".key.move_left"
10599 &setup_input.key.right, ".key.move_right"
10603 &setup_input.key.up, ".key.move_up"
10607 &setup_input.key.down, ".key.move_down"
10611 &setup_input.key.snap, ".key.snap_field"
10615 &setup_input.key.drop, ".key.place_bomb"
10619 static struct TokenInfo system_setup_tokens[] =
10623 &setup.system.sdl_renderdriver, "system.sdl_renderdriver"
10627 &setup.system.sdl_videodriver, "system.sdl_videodriver"
10631 &setup.system.sdl_audiodriver, "system.sdl_audiodriver"
10635 &setup.system.audio_fragment_size, "system.audio_fragment_size"
10639 static struct TokenInfo internal_setup_tokens[] =
10643 &setup.internal.program_title, "program_title"
10647 &setup.internal.program_version, "program_version"
10651 &setup.internal.program_author, "program_author"
10655 &setup.internal.program_email, "program_email"
10659 &setup.internal.program_website, "program_website"
10663 &setup.internal.program_copyright, "program_copyright"
10667 &setup.internal.program_company, "program_company"
10671 &setup.internal.program_icon_file, "program_icon_file"
10675 &setup.internal.default_graphics_set, "default_graphics_set"
10679 &setup.internal.default_sounds_set, "default_sounds_set"
10683 &setup.internal.default_music_set, "default_music_set"
10687 &setup.internal.fallback_graphics_file, "fallback_graphics_file"
10691 &setup.internal.fallback_sounds_file, "fallback_sounds_file"
10695 &setup.internal.fallback_music_file, "fallback_music_file"
10699 &setup.internal.default_level_series, "default_level_series"
10703 &setup.internal.default_window_width, "default_window_width"
10707 &setup.internal.default_window_height, "default_window_height"
10711 &setup.internal.choose_from_top_leveldir, "choose_from_top_leveldir"
10715 &setup.internal.show_scaling_in_title, "show_scaling_in_title"
10719 &setup.internal.create_user_levelset, "create_user_levelset"
10723 &setup.internal.info_screens_from_main, "info_screens_from_main"
10727 &setup.internal.menu_game, "menu_game"
10731 &setup.internal.menu_engines, "menu_engines"
10735 &setup.internal.menu_editor, "menu_editor"
10739 &setup.internal.menu_graphics, "menu_graphics"
10743 &setup.internal.menu_sound, "menu_sound"
10747 &setup.internal.menu_artwork, "menu_artwork"
10751 &setup.internal.menu_input, "menu_input"
10755 &setup.internal.menu_touch, "menu_touch"
10759 &setup.internal.menu_shortcuts, "menu_shortcuts"
10763 &setup.internal.menu_exit, "menu_exit"
10767 &setup.internal.menu_save_and_exit, "menu_save_and_exit"
10771 &setup.internal.menu_shortcuts_various, "menu_shortcuts_various"
10775 &setup.internal.menu_shortcuts_focus, "menu_shortcuts_focus"
10779 &setup.internal.menu_shortcuts_tape, "menu_shortcuts_tape"
10783 &setup.internal.menu_shortcuts_sound, "menu_shortcuts_sound"
10787 &setup.internal.menu_shortcuts_snap, "menu_shortcuts_snap"
10791 &setup.internal.info_title, "info_title"
10795 &setup.internal.info_elements, "info_elements"
10799 &setup.internal.info_music, "info_music"
10803 &setup.internal.info_credits, "info_credits"
10807 &setup.internal.info_program, "info_program"
10811 &setup.internal.info_version, "info_version"
10815 &setup.internal.info_levelset, "info_levelset"
10819 &setup.internal.info_exit, "info_exit"
10823 static struct TokenInfo debug_setup_tokens[] =
10827 &setup.debug.frame_delay[0], "debug.frame_delay_0"
10831 &setup.debug.frame_delay[1], "debug.frame_delay_1"
10835 &setup.debug.frame_delay[2], "debug.frame_delay_2"
10839 &setup.debug.frame_delay[3], "debug.frame_delay_3"
10843 &setup.debug.frame_delay[4], "debug.frame_delay_4"
10847 &setup.debug.frame_delay[5], "debug.frame_delay_5"
10851 &setup.debug.frame_delay[6], "debug.frame_delay_6"
10855 &setup.debug.frame_delay[7], "debug.frame_delay_7"
10859 &setup.debug.frame_delay[8], "debug.frame_delay_8"
10863 &setup.debug.frame_delay[9], "debug.frame_delay_9"
10867 &setup.debug.frame_delay_key[0], "debug.key.frame_delay_0"
10871 &setup.debug.frame_delay_key[1], "debug.key.frame_delay_1"
10875 &setup.debug.frame_delay_key[2], "debug.key.frame_delay_2"
10879 &setup.debug.frame_delay_key[3], "debug.key.frame_delay_3"
10883 &setup.debug.frame_delay_key[4], "debug.key.frame_delay_4"
10887 &setup.debug.frame_delay_key[5], "debug.key.frame_delay_5"
10891 &setup.debug.frame_delay_key[6], "debug.key.frame_delay_6"
10895 &setup.debug.frame_delay_key[7], "debug.key.frame_delay_7"
10899 &setup.debug.frame_delay_key[8], "debug.key.frame_delay_8"
10903 &setup.debug.frame_delay_key[9], "debug.key.frame_delay_9"
10907 &setup.debug.frame_delay_use_mod_key, "debug.frame_delay.use_mod_key"},
10910 &setup.debug.frame_delay_game_only, "debug.frame_delay.game_only"
10914 &setup.debug.show_frames_per_second, "debug.show_frames_per_second"
10918 &setup.debug.xsn_mode, "debug.xsn_mode"
10922 &setup.debug.xsn_percent, "debug.xsn_percent"
10926 static struct TokenInfo options_setup_tokens[] =
10930 &setup.options.verbose, "options.verbose"
10934 &setup.options.debug, "options.debug"
10938 &setup.options.debug_mode, "options.debug_mode"
10942 static void setSetupInfoToDefaults(struct SetupInfo *si)
10946 si->player_name = getStringCopy(getDefaultUserName(user.nr));
10948 si->multiple_users = TRUE;
10951 si->sound_loops = TRUE;
10952 si->sound_music = TRUE;
10953 si->sound_simple = TRUE;
10955 si->global_animations = TRUE;
10956 si->scroll_delay = TRUE;
10957 si->forced_scroll_delay = FALSE;
10958 si->scroll_delay_value = STD_SCROLL_DELAY;
10959 si->engine_snapshot_mode = getStringCopy(STR_SNAPSHOT_MODE_DEFAULT);
10960 si->engine_snapshot_memory = SNAPSHOT_MEMORY_DEFAULT;
10961 si->fade_screens = TRUE;
10962 si->autorecord = TRUE;
10963 si->autorecord_after_replay = TRUE;
10964 si->auto_pause_on_start = FALSE;
10965 si->show_titlescreen = TRUE;
10966 si->quick_doors = FALSE;
10967 si->team_mode = FALSE;
10968 si->handicap = TRUE;
10969 si->skip_levels = TRUE;
10970 si->increment_levels = TRUE;
10971 si->auto_play_next_level = TRUE;
10972 si->count_score_after_game = TRUE;
10973 si->show_scores_after_game = TRUE;
10974 si->time_limit = TRUE;
10975 si->fullscreen = FALSE;
10976 si->window_scaling_percent = STD_WINDOW_SCALING_PERCENT;
10977 si->window_scaling_quality = getStringCopy(SCALING_QUALITY_DEFAULT);
10978 si->screen_rendering_mode = getStringCopy(STR_SPECIAL_RENDERING_DEFAULT);
10979 si->vsync_mode = getStringCopy(STR_VSYNC_MODE_DEFAULT);
10980 si->ask_on_escape = TRUE;
10981 si->ask_on_escape_editor = TRUE;
10982 si->ask_on_game_over = TRUE;
10983 si->ask_on_quit_game = TRUE;
10984 si->ask_on_quit_program = TRUE;
10985 si->quick_switch = FALSE;
10986 si->input_on_focus = FALSE;
10987 si->prefer_aga_graphics = TRUE;
10988 si->prefer_lowpass_sounds = FALSE;
10989 si->prefer_extra_panel_items = TRUE;
10990 si->game_speed_extended = FALSE;
10991 si->game_frame_delay = GAME_FRAME_DELAY;
10992 si->bd_skip_uncovering = FALSE;
10993 si->bd_skip_hatching = FALSE;
10994 si->bd_scroll_delay = TRUE;
10995 si->bd_smooth_movements = AUTO;
10996 si->sp_show_border_elements = FALSE;
10997 si->small_game_graphics = FALSE;
10998 si->show_load_save_buttons = FALSE;
10999 si->show_undo_redo_buttons = FALSE;
11000 si->scores_in_highscore_list = getStringCopy(STR_SCORES_TYPE_DEFAULT);
11002 si->graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
11003 si->sounds_set = getStringCopy(SND_CLASSIC_SUBDIR);
11004 si->music_set = getStringCopy(MUS_CLASSIC_SUBDIR);
11006 si->override_level_graphics = FALSE;
11007 si->override_level_sounds = FALSE;
11008 si->override_level_music = FALSE;
11010 si->volume_simple = 100; // percent
11011 si->volume_loops = 100; // percent
11012 si->volume_music = 100; // percent
11014 si->network_mode = FALSE;
11015 si->network_player_nr = 0; // first player
11016 si->network_server_hostname = getStringCopy(STR_NETWORK_AUTO_DETECT);
11018 si->touch.control_type = getStringCopy(TOUCH_CONTROL_DEFAULT);
11019 si->touch.move_distance = TOUCH_MOVE_DISTANCE_DEFAULT; // percent
11020 si->touch.drop_distance = TOUCH_DROP_DISTANCE_DEFAULT; // percent
11021 si->touch.transparency = TOUCH_TRANSPARENCY_DEFAULT; // percent
11022 si->touch.draw_outlined = TRUE;
11023 si->touch.draw_pressed = TRUE;
11025 for (i = 0; i < 2; i++)
11027 char *default_grid_button[6][2] =
11033 { "111222", " vv " },
11034 { "111222", " vv " }
11036 int grid_xsize = DEFAULT_GRID_XSIZE(i);
11037 int grid_ysize = DEFAULT_GRID_YSIZE(i);
11038 int min_xsize = MIN(6, grid_xsize);
11039 int min_ysize = MIN(6, grid_ysize);
11040 int startx = grid_xsize - min_xsize;
11041 int starty = grid_ysize - min_ysize;
11044 // virtual buttons grid can only be set to defaults if video is initialized
11045 // (this will be repeated if virtual buttons are not loaded from setup file)
11046 if (video.initialized)
11048 si->touch.grid_xsize[i] = grid_xsize;
11049 si->touch.grid_ysize[i] = grid_ysize;
11053 si->touch.grid_xsize[i] = -1;
11054 si->touch.grid_ysize[i] = -1;
11057 for (x = 0; x < MAX_GRID_XSIZE; x++)
11058 for (y = 0; y < MAX_GRID_YSIZE; y++)
11059 si->touch.grid_button[i][x][y] = CHAR_GRID_BUTTON_NONE;
11061 for (x = 0; x < min_xsize; x++)
11062 for (y = 0; y < min_ysize; y++)
11063 si->touch.grid_button[i][x][starty + y] =
11064 default_grid_button[y][0][x];
11066 for (x = 0; x < min_xsize; x++)
11067 for (y = 0; y < min_ysize; y++)
11068 si->touch.grid_button[i][startx + x][starty + y] =
11069 default_grid_button[y][1][x];
11072 si->touch.grid_initialized = video.initialized;
11074 si->touch.overlay_buttons = FALSE;
11076 si->editor.el_boulderdash = TRUE;
11077 si->editor.el_boulderdash_native = TRUE;
11078 si->editor.el_emerald_mine = TRUE;
11079 si->editor.el_emerald_mine_club = TRUE;
11080 si->editor.el_more = TRUE;
11081 si->editor.el_sokoban = TRUE;
11082 si->editor.el_supaplex = TRUE;
11083 si->editor.el_diamond_caves = TRUE;
11084 si->editor.el_dx_boulderdash = TRUE;
11086 si->editor.el_mirror_magic = TRUE;
11087 si->editor.el_deflektor = TRUE;
11089 si->editor.el_chars = TRUE;
11090 si->editor.el_steel_chars = TRUE;
11092 si->editor.el_classic = TRUE;
11093 si->editor.el_custom = TRUE;
11095 si->editor.el_user_defined = FALSE;
11096 si->editor.el_dynamic = TRUE;
11098 si->editor.el_headlines = TRUE;
11100 si->editor.show_element_token = FALSE;
11102 si->editor.show_read_only_warning = TRUE;
11104 si->editor.use_template_for_new_levels = TRUE;
11106 si->shortcut.save_game = DEFAULT_KEY_SAVE_GAME;
11107 si->shortcut.load_game = DEFAULT_KEY_LOAD_GAME;
11108 si->shortcut.restart_game = DEFAULT_KEY_RESTART_GAME;
11109 si->shortcut.pause_before_end = DEFAULT_KEY_PAUSE_BEFORE_END;
11110 si->shortcut.toggle_pause = DEFAULT_KEY_TOGGLE_PAUSE;
11112 si->shortcut.focus_player[0] = DEFAULT_KEY_FOCUS_PLAYER_1;
11113 si->shortcut.focus_player[1] = DEFAULT_KEY_FOCUS_PLAYER_2;
11114 si->shortcut.focus_player[2] = DEFAULT_KEY_FOCUS_PLAYER_3;
11115 si->shortcut.focus_player[3] = DEFAULT_KEY_FOCUS_PLAYER_4;
11116 si->shortcut.focus_player_all = DEFAULT_KEY_FOCUS_PLAYER_ALL;
11118 si->shortcut.tape_eject = DEFAULT_KEY_TAPE_EJECT;
11119 si->shortcut.tape_extra = DEFAULT_KEY_TAPE_EXTRA;
11120 si->shortcut.tape_stop = DEFAULT_KEY_TAPE_STOP;
11121 si->shortcut.tape_pause = DEFAULT_KEY_TAPE_PAUSE;
11122 si->shortcut.tape_record = DEFAULT_KEY_TAPE_RECORD;
11123 si->shortcut.tape_play = DEFAULT_KEY_TAPE_PLAY;
11125 si->shortcut.sound_simple = DEFAULT_KEY_SOUND_SIMPLE;
11126 si->shortcut.sound_loops = DEFAULT_KEY_SOUND_LOOPS;
11127 si->shortcut.sound_music = DEFAULT_KEY_SOUND_MUSIC;
11129 si->shortcut.snap_left = DEFAULT_KEY_SNAP_LEFT;
11130 si->shortcut.snap_right = DEFAULT_KEY_SNAP_RIGHT;
11131 si->shortcut.snap_up = DEFAULT_KEY_SNAP_UP;
11132 si->shortcut.snap_down = DEFAULT_KEY_SNAP_DOWN;
11134 for (i = 0; i < MAX_PLAYERS; i++)
11136 si->input[i].use_joystick = FALSE;
11137 si->input[i].joy.device_name = getStringCopy(getDeviceNameFromJoystickNr(i));
11138 si->input[i].joy.xleft = JOYSTICK_XLEFT;
11139 si->input[i].joy.xmiddle = JOYSTICK_XMIDDLE;
11140 si->input[i].joy.xright = JOYSTICK_XRIGHT;
11141 si->input[i].joy.yupper = JOYSTICK_YUPPER;
11142 si->input[i].joy.ymiddle = JOYSTICK_YMIDDLE;
11143 si->input[i].joy.ylower = JOYSTICK_YLOWER;
11144 si->input[i].joy.snap = (i == 0 ? JOY_BUTTON_1 : 0);
11145 si->input[i].joy.drop = (i == 0 ? JOY_BUTTON_2 : 0);
11146 si->input[i].key.left = (i == 0 ? DEFAULT_KEY_LEFT : KSYM_UNDEFINED);
11147 si->input[i].key.right = (i == 0 ? DEFAULT_KEY_RIGHT : KSYM_UNDEFINED);
11148 si->input[i].key.up = (i == 0 ? DEFAULT_KEY_UP : KSYM_UNDEFINED);
11149 si->input[i].key.down = (i == 0 ? DEFAULT_KEY_DOWN : KSYM_UNDEFINED);
11150 si->input[i].key.snap = (i == 0 ? DEFAULT_KEY_SNAP : KSYM_UNDEFINED);
11151 si->input[i].key.drop = (i == 0 ? DEFAULT_KEY_DROP : KSYM_UNDEFINED);
11154 si->system.sdl_renderdriver = getStringCopy(ARG_DEFAULT);
11155 si->system.sdl_videodriver = getStringCopy(ARG_DEFAULT);
11156 si->system.sdl_audiodriver = getStringCopy(ARG_DEFAULT);
11157 si->system.audio_fragment_size = DEFAULT_AUDIO_FRAGMENT_SIZE;
11159 si->internal.program_title = getStringCopy(PROGRAM_TITLE_STRING);
11160 si->internal.program_version = getStringCopy(getProgramRealVersionString());
11161 si->internal.program_author = getStringCopy(PROGRAM_AUTHOR_STRING);
11162 si->internal.program_email = getStringCopy(PROGRAM_EMAIL_STRING);
11163 si->internal.program_website = getStringCopy(PROGRAM_WEBSITE_STRING);
11164 si->internal.program_copyright = getStringCopy(PROGRAM_COPYRIGHT_STRING);
11165 si->internal.program_company = getStringCopy(PROGRAM_COMPANY_STRING);
11167 si->internal.program_icon_file = getStringCopy(PROGRAM_ICON_FILENAME);
11169 si->internal.default_graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
11170 si->internal.default_sounds_set = getStringCopy(SND_CLASSIC_SUBDIR);
11171 si->internal.default_music_set = getStringCopy(MUS_CLASSIC_SUBDIR);
11173 si->internal.fallback_graphics_file = getStringCopy(UNDEFINED_FILENAME);
11174 si->internal.fallback_sounds_file = getStringCopy(UNDEFINED_FILENAME);
11175 si->internal.fallback_music_file = getStringCopy(UNDEFINED_FILENAME);
11177 si->internal.default_level_series = getStringCopy(UNDEFINED_LEVELSET);
11178 si->internal.choose_from_top_leveldir = FALSE;
11179 si->internal.show_scaling_in_title = TRUE;
11180 si->internal.create_user_levelset = TRUE;
11181 si->internal.info_screens_from_main = FALSE;
11183 si->internal.default_window_width = WIN_XSIZE_DEFAULT;
11184 si->internal.default_window_height = WIN_YSIZE_DEFAULT;
11186 si->debug.frame_delay[0] = DEFAULT_FRAME_DELAY_0;
11187 si->debug.frame_delay[1] = DEFAULT_FRAME_DELAY_1;
11188 si->debug.frame_delay[2] = DEFAULT_FRAME_DELAY_2;
11189 si->debug.frame_delay[3] = DEFAULT_FRAME_DELAY_3;
11190 si->debug.frame_delay[4] = DEFAULT_FRAME_DELAY_4;
11191 si->debug.frame_delay[5] = DEFAULT_FRAME_DELAY_5;
11192 si->debug.frame_delay[6] = DEFAULT_FRAME_DELAY_6;
11193 si->debug.frame_delay[7] = DEFAULT_FRAME_DELAY_7;
11194 si->debug.frame_delay[8] = DEFAULT_FRAME_DELAY_8;
11195 si->debug.frame_delay[9] = DEFAULT_FRAME_DELAY_9;
11197 si->debug.frame_delay_key[0] = DEFAULT_KEY_FRAME_DELAY_0;
11198 si->debug.frame_delay_key[1] = DEFAULT_KEY_FRAME_DELAY_1;
11199 si->debug.frame_delay_key[2] = DEFAULT_KEY_FRAME_DELAY_2;
11200 si->debug.frame_delay_key[3] = DEFAULT_KEY_FRAME_DELAY_3;
11201 si->debug.frame_delay_key[4] = DEFAULT_KEY_FRAME_DELAY_4;
11202 si->debug.frame_delay_key[5] = DEFAULT_KEY_FRAME_DELAY_5;
11203 si->debug.frame_delay_key[6] = DEFAULT_KEY_FRAME_DELAY_6;
11204 si->debug.frame_delay_key[7] = DEFAULT_KEY_FRAME_DELAY_7;
11205 si->debug.frame_delay_key[8] = DEFAULT_KEY_FRAME_DELAY_8;
11206 si->debug.frame_delay_key[9] = DEFAULT_KEY_FRAME_DELAY_9;
11208 si->debug.frame_delay_use_mod_key = DEFAULT_FRAME_DELAY_USE_MOD_KEY;
11209 si->debug.frame_delay_game_only = DEFAULT_FRAME_DELAY_GAME_ONLY;
11211 si->debug.show_frames_per_second = FALSE;
11213 si->debug.xsn_mode = AUTO;
11214 si->debug.xsn_percent = 0;
11216 si->options.verbose = FALSE;
11217 si->options.debug = FALSE;
11218 si->options.debug_mode = getStringCopy(ARG_UNDEFINED_STRING);
11220 #if defined(PLATFORM_ANDROID)
11221 si->fullscreen = TRUE;
11222 si->touch.overlay_buttons = TRUE;
11225 setHideSetupEntry(&setup.debug.xsn_mode);
11228 static void setSetupInfoToDefaults_AutoSetup(struct SetupInfo *si)
11230 si->auto_setup.editor_zoom_tilesize = MINI_TILESIZE;
11233 static void setSetupInfoToDefaults_ServerSetup(struct SetupInfo *si)
11235 si->player_uuid = NULL; // (will be set later)
11236 si->player_version = 1; // (will be set later)
11238 si->use_api_server = TRUE;
11239 si->api_server_hostname = getStringCopy(API_SERVER_HOSTNAME);
11240 si->api_server_password = getStringCopy(UNDEFINED_PASSWORD);
11241 si->ask_for_uploading_tapes = TRUE;
11242 si->ask_for_remaining_tapes = FALSE;
11243 si->provide_uploading_tapes = TRUE;
11244 si->ask_for_using_api_server = TRUE;
11245 si->has_remaining_tapes = FALSE;
11248 static void setSetupInfoToDefaults_EditorCascade(struct SetupInfo *si)
11250 si->editor_cascade.el_bd = TRUE;
11251 si->editor_cascade.el_bd_native = TRUE;
11252 si->editor_cascade.el_em = TRUE;
11253 si->editor_cascade.el_emc = TRUE;
11254 si->editor_cascade.el_rnd = TRUE;
11255 si->editor_cascade.el_sb = TRUE;
11256 si->editor_cascade.el_sp = TRUE;
11257 si->editor_cascade.el_dc = TRUE;
11258 si->editor_cascade.el_dx = TRUE;
11260 si->editor_cascade.el_mm = TRUE;
11261 si->editor_cascade.el_df = TRUE;
11263 si->editor_cascade.el_chars = FALSE;
11264 si->editor_cascade.el_steel_chars = FALSE;
11265 si->editor_cascade.el_ce = FALSE;
11266 si->editor_cascade.el_ge = FALSE;
11267 si->editor_cascade.el_es = FALSE;
11268 si->editor_cascade.el_ref = FALSE;
11269 si->editor_cascade.el_user = FALSE;
11270 si->editor_cascade.el_dynamic = FALSE;
11273 #define MAX_HIDE_SETUP_TOKEN_SIZE 20
11275 static char *getHideSetupToken(void *setup_value)
11277 static char hide_setup_token[MAX_HIDE_SETUP_TOKEN_SIZE];
11279 if (setup_value != NULL)
11280 snprintf(hide_setup_token, MAX_HIDE_SETUP_TOKEN_SIZE, "%p", setup_value);
11282 return hide_setup_token;
11285 void setHideSetupEntry(void *setup_value)
11287 char *hide_setup_token = getHideSetupToken(setup_value);
11289 if (hide_setup_hash == NULL)
11290 hide_setup_hash = newSetupFileHash();
11292 if (setup_value != NULL)
11293 setHashEntry(hide_setup_hash, hide_setup_token, "");
11296 void removeHideSetupEntry(void *setup_value)
11298 char *hide_setup_token = getHideSetupToken(setup_value);
11300 if (setup_value != NULL)
11301 removeHashEntry(hide_setup_hash, hide_setup_token);
11304 boolean hideSetupEntry(void *setup_value)
11306 char *hide_setup_token = getHideSetupToken(setup_value);
11308 return (setup_value != NULL &&
11309 getHashEntry(hide_setup_hash, hide_setup_token) != NULL);
11312 static void setSetupInfoFromTokenText(SetupFileHash *setup_file_hash,
11313 struct TokenInfo *token_info,
11314 int token_nr, char *token_text)
11316 char *token_hide_text = getStringCat2(token_text, ".hide");
11317 char *token_hide_value = getHashEntry(setup_file_hash, token_hide_text);
11319 // set the value of this setup option in the setup option structure
11320 setSetupInfo(token_info, token_nr, getHashEntry(setup_file_hash, token_text));
11322 // check if this setup option should be hidden in the setup menu
11323 if (token_hide_value != NULL && get_boolean_from_string(token_hide_value))
11324 setHideSetupEntry(token_info[token_nr].value);
11326 free(token_hide_text);
11329 static void setSetupInfoFromTokenInfo(SetupFileHash *setup_file_hash,
11330 struct TokenInfo *token_info,
11333 setSetupInfoFromTokenText(setup_file_hash, token_info, token_nr,
11334 token_info[token_nr].text);
11337 static void decodeSetupFileHash_Default(SetupFileHash *setup_file_hash)
11341 if (!setup_file_hash)
11344 for (i = 0; i < ARRAY_SIZE(global_setup_tokens); i++)
11345 setSetupInfoFromTokenInfo(setup_file_hash, global_setup_tokens, i);
11347 setup.touch.grid_initialized = TRUE;
11348 for (i = 0; i < 2; i++)
11350 int grid_xsize = setup.touch.grid_xsize[i];
11351 int grid_ysize = setup.touch.grid_ysize[i];
11354 // if virtual buttons are not loaded from setup file, repeat initializing
11355 // virtual buttons grid with default values later when video is initialized
11356 if (grid_xsize == -1 ||
11359 setup.touch.grid_initialized = FALSE;
11364 for (y = 0; y < grid_ysize; y++)
11366 char token_string[MAX_LINE_LEN];
11368 sprintf(token_string, "touch.virtual_buttons.%d.%02d", i, y);
11370 char *value_string = getHashEntry(setup_file_hash, token_string);
11372 if (value_string == NULL)
11375 for (x = 0; x < grid_xsize; x++)
11377 char c = value_string[x];
11379 setup.touch.grid_button[i][x][y] =
11380 (c == '.' ? CHAR_GRID_BUTTON_NONE : c);
11385 for (i = 0; i < ARRAY_SIZE(editor_setup_tokens); i++)
11386 setSetupInfoFromTokenInfo(setup_file_hash, editor_setup_tokens, i);
11388 for (i = 0; i < ARRAY_SIZE(shortcut_setup_tokens); i++)
11389 setSetupInfoFromTokenInfo(setup_file_hash, shortcut_setup_tokens, i);
11391 for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
11395 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
11397 setup_input = setup.input[pnr];
11398 for (i = 0; i < ARRAY_SIZE(player_setup_tokens); i++)
11400 char full_token[100];
11402 sprintf(full_token, "%s%s", prefix, player_setup_tokens[i].text);
11403 setSetupInfoFromTokenText(setup_file_hash, player_setup_tokens, i,
11406 setup.input[pnr] = setup_input;
11409 for (i = 0; i < ARRAY_SIZE(system_setup_tokens); i++)
11410 setSetupInfoFromTokenInfo(setup_file_hash, system_setup_tokens, i);
11412 for (i = 0; i < ARRAY_SIZE(internal_setup_tokens); i++)
11413 setSetupInfoFromTokenInfo(setup_file_hash, internal_setup_tokens, i);
11415 for (i = 0; i < ARRAY_SIZE(debug_setup_tokens); i++)
11416 setSetupInfoFromTokenInfo(setup_file_hash, debug_setup_tokens, i);
11418 for (i = 0; i < ARRAY_SIZE(options_setup_tokens); i++)
11419 setSetupInfoFromTokenInfo(setup_file_hash, options_setup_tokens, i);
11421 setHideRelatedSetupEntries();
11424 static void decodeSetupFileHash_AutoSetup(SetupFileHash *setup_file_hash)
11428 if (!setup_file_hash)
11431 for (i = 0; i < ARRAY_SIZE(auto_setup_tokens); i++)
11432 setSetupInfo(auto_setup_tokens, i,
11433 getHashEntry(setup_file_hash,
11434 auto_setup_tokens[i].text));
11437 static void decodeSetupFileHash_ServerSetup(SetupFileHash *setup_file_hash)
11441 if (!setup_file_hash)
11444 for (i = 0; i < ARRAY_SIZE(server_setup_tokens); i++)
11445 setSetupInfo(server_setup_tokens, i,
11446 getHashEntry(setup_file_hash,
11447 server_setup_tokens[i].text));
11450 static void decodeSetupFileHash_EditorCascade(SetupFileHash *setup_file_hash)
11454 if (!setup_file_hash)
11457 for (i = 0; i < ARRAY_SIZE(editor_cascade_setup_tokens); i++)
11458 setSetupInfo(editor_cascade_setup_tokens, i,
11459 getHashEntry(setup_file_hash,
11460 editor_cascade_setup_tokens[i].text));
11463 void LoadUserNames(void)
11465 int last_user_nr = user.nr;
11468 if (global.user_names != NULL)
11470 for (i = 0; i < MAX_PLAYER_NAMES; i++)
11471 checked_free(global.user_names[i]);
11473 checked_free(global.user_names);
11476 global.user_names = checked_calloc(MAX_PLAYER_NAMES * sizeof(char *));
11478 for (i = 0; i < MAX_PLAYER_NAMES; i++)
11482 SetupFileHash *setup_file_hash = loadSetupFileHash(getSetupFilename());
11484 if (setup_file_hash)
11486 char *player_name = getHashEntry(setup_file_hash, "player_name");
11488 global.user_names[i] = getFixedUserName(player_name);
11490 freeSetupFileHash(setup_file_hash);
11493 if (global.user_names[i] == NULL)
11494 global.user_names[i] = getStringCopy(getDefaultUserName(i));
11497 user.nr = last_user_nr;
11500 void LoadSetupFromFilename(char *filename)
11502 SetupFileHash *setup_file_hash = loadSetupFileHash(filename);
11504 if (setup_file_hash)
11506 decodeSetupFileHash_Default(setup_file_hash);
11508 freeSetupFileHash(setup_file_hash);
11512 Debug("setup", "using default setup values");
11516 static void LoadSetup_SpecialPostProcessing(void)
11518 char *player_name_new;
11520 // needed to work around problems with fixed length strings
11521 player_name_new = getFixedUserName(setup.player_name);
11522 free(setup.player_name);
11523 setup.player_name = player_name_new;
11525 // "scroll_delay: on(3) / off(0)" was replaced by scroll delay value
11526 if (setup.scroll_delay == FALSE)
11528 setup.scroll_delay_value = MIN_SCROLL_DELAY;
11529 setup.scroll_delay = TRUE; // now always "on"
11532 // make sure that scroll delay value stays inside valid range
11533 setup.scroll_delay_value =
11534 MIN(MAX(MIN_SCROLL_DELAY, setup.scroll_delay_value), MAX_SCROLL_DELAY);
11537 void LoadSetup_Default(void)
11541 // always start with reliable default values
11542 setSetupInfoToDefaults(&setup);
11544 // try to load setup values from default setup file
11545 filename = getDefaultSetupFilename();
11547 if (fileExists(filename))
11548 LoadSetupFromFilename(filename);
11550 // try to load setup values from platform setup file
11551 filename = getPlatformSetupFilename();
11553 if (fileExists(filename))
11554 LoadSetupFromFilename(filename);
11556 // try to load setup values from user setup file
11557 filename = getSetupFilename();
11559 LoadSetupFromFilename(filename);
11561 LoadSetup_SpecialPostProcessing();
11564 void LoadSetup_AutoSetup(void)
11566 char *filename = getPath2(getSetupDir(), AUTOSETUP_FILENAME);
11567 SetupFileHash *setup_file_hash = NULL;
11569 // always start with reliable default values
11570 setSetupInfoToDefaults_AutoSetup(&setup);
11572 setup_file_hash = loadSetupFileHash(filename);
11574 if (setup_file_hash)
11576 decodeSetupFileHash_AutoSetup(setup_file_hash);
11578 freeSetupFileHash(setup_file_hash);
11584 void LoadSetup_ServerSetup(void)
11586 char *filename = getPath2(getSetupDir(), SERVERSETUP_FILENAME);
11587 SetupFileHash *setup_file_hash = NULL;
11589 // always start with reliable default values
11590 setSetupInfoToDefaults_ServerSetup(&setup);
11592 setup_file_hash = loadSetupFileHash(filename);
11594 if (setup_file_hash)
11596 decodeSetupFileHash_ServerSetup(setup_file_hash);
11598 freeSetupFileHash(setup_file_hash);
11603 if (setup.player_uuid == NULL)
11605 // player UUID does not yet exist in setup file
11606 setup.player_uuid = getStringCopy(getUUID());
11607 setup.player_version = 2;
11609 SaveSetup_ServerSetup();
11613 void LoadSetup_EditorCascade(void)
11615 char *filename = getPath2(getSetupDir(), EDITORCASCADE_FILENAME);
11616 SetupFileHash *setup_file_hash = NULL;
11618 // always start with reliable default values
11619 setSetupInfoToDefaults_EditorCascade(&setup);
11621 setup_file_hash = loadSetupFileHash(filename);
11623 if (setup_file_hash)
11625 decodeSetupFileHash_EditorCascade(setup_file_hash);
11627 freeSetupFileHash(setup_file_hash);
11633 void LoadSetup(void)
11635 LoadSetup_Default();
11636 LoadSetup_AutoSetup();
11637 LoadSetup_ServerSetup();
11638 LoadSetup_EditorCascade();
11641 static void addGameControllerMappingToHash(SetupFileHash *mappings_hash,
11642 char *mapping_line)
11644 char mapping_guid[MAX_LINE_LEN];
11645 char *mapping_start, *mapping_end;
11647 // get GUID from game controller mapping line: copy complete line
11648 strncpy(mapping_guid, mapping_line, MAX_LINE_LEN - 1);
11649 mapping_guid[MAX_LINE_LEN - 1] = '\0';
11651 // get GUID from game controller mapping line: cut after GUID part
11652 mapping_start = strchr(mapping_guid, ',');
11653 if (mapping_start != NULL)
11654 *mapping_start = '\0';
11656 // cut newline from game controller mapping line
11657 mapping_end = strchr(mapping_line, '\n');
11658 if (mapping_end != NULL)
11659 *mapping_end = '\0';
11661 // add mapping entry to game controller mappings hash
11662 setHashEntry(mappings_hash, mapping_guid, mapping_line);
11665 static void LoadSetup_ReadGameControllerMappings(SetupFileHash *mappings_hash,
11670 if (!(file = fopen(filename, MODE_READ)))
11672 Warn("cannot read game controller mappings file '%s'", filename);
11677 while (!feof(file))
11679 char line[MAX_LINE_LEN];
11681 if (!fgets(line, MAX_LINE_LEN, file))
11684 addGameControllerMappingToHash(mappings_hash, line);
11690 void SaveSetup_Default(void)
11692 char *filename = getSetupFilename();
11696 InitUserDataDirectory();
11698 if (!(file = fopen(filename, MODE_WRITE)))
11700 Warn("cannot write setup file '%s'", filename);
11705 fprintFileHeader(file, SETUP_FILENAME);
11707 for (i = 0; i < ARRAY_SIZE(global_setup_tokens); i++)
11709 // just to make things nicer :)
11710 if (global_setup_tokens[i].value == &setup.multiple_users ||
11711 global_setup_tokens[i].value == &setup.sound ||
11712 global_setup_tokens[i].value == &setup.graphics_set ||
11713 global_setup_tokens[i].value == &setup.volume_simple ||
11714 global_setup_tokens[i].value == &setup.network_mode ||
11715 global_setup_tokens[i].value == &setup.touch.control_type ||
11716 global_setup_tokens[i].value == &setup.touch.grid_xsize[0] ||
11717 global_setup_tokens[i].value == &setup.touch.grid_xsize[1])
11718 fprintf(file, "\n");
11720 fprintf(file, "%s\n", getSetupLine(global_setup_tokens, "", i));
11723 for (i = 0; i < 2; i++)
11725 int grid_xsize = setup.touch.grid_xsize[i];
11726 int grid_ysize = setup.touch.grid_ysize[i];
11729 fprintf(file, "\n");
11731 for (y = 0; y < grid_ysize; y++)
11733 char token_string[MAX_LINE_LEN];
11734 char value_string[MAX_LINE_LEN];
11736 sprintf(token_string, "touch.virtual_buttons.%d.%02d", i, y);
11738 for (x = 0; x < grid_xsize; x++)
11740 char c = setup.touch.grid_button[i][x][y];
11742 value_string[x] = (c == CHAR_GRID_BUTTON_NONE ? '.' : c);
11745 value_string[grid_xsize] = '\0';
11747 fprintf(file, "%s\n", getFormattedSetupEntry(token_string, value_string));
11751 fprintf(file, "\n");
11752 for (i = 0; i < ARRAY_SIZE(editor_setup_tokens); i++)
11753 fprintf(file, "%s\n", getSetupLine(editor_setup_tokens, "", i));
11755 fprintf(file, "\n");
11756 for (i = 0; i < ARRAY_SIZE(shortcut_setup_tokens); i++)
11757 fprintf(file, "%s\n", getSetupLine(shortcut_setup_tokens, "", i));
11759 for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
11763 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
11764 fprintf(file, "\n");
11766 setup_input = setup.input[pnr];
11767 for (i = 0; i < ARRAY_SIZE(player_setup_tokens); i++)
11768 fprintf(file, "%s\n", getSetupLine(player_setup_tokens, prefix, i));
11771 fprintf(file, "\n");
11772 for (i = 0; i < ARRAY_SIZE(system_setup_tokens); i++)
11773 fprintf(file, "%s\n", getSetupLine(system_setup_tokens, "", i));
11775 // (internal setup values not saved to user setup file)
11777 fprintf(file, "\n");
11778 for (i = 0; i < ARRAY_SIZE(debug_setup_tokens); i++)
11779 if (!strPrefix(debug_setup_tokens[i].text, "debug.xsn_") ||
11780 setup.debug.xsn_mode != AUTO)
11781 fprintf(file, "%s\n", getSetupLine(debug_setup_tokens, "", i));
11783 fprintf(file, "\n");
11784 for (i = 0; i < ARRAY_SIZE(options_setup_tokens); i++)
11785 fprintf(file, "%s\n", getSetupLine(options_setup_tokens, "", i));
11789 SetFilePermissions(filename, PERMS_PRIVATE);
11792 void SaveSetup_AutoSetup(void)
11794 char *filename = getPath2(getSetupDir(), AUTOSETUP_FILENAME);
11798 InitUserDataDirectory();
11800 if (!(file = fopen(filename, MODE_WRITE)))
11802 Warn("cannot write auto setup file '%s'", filename);
11809 fprintFileHeader(file, AUTOSETUP_FILENAME);
11811 for (i = 0; i < ARRAY_SIZE(auto_setup_tokens); i++)
11812 fprintf(file, "%s\n", getSetupLine(auto_setup_tokens, "", i));
11816 SetFilePermissions(filename, PERMS_PRIVATE);
11821 void SaveSetup_ServerSetup(void)
11823 char *filename = getPath2(getSetupDir(), SERVERSETUP_FILENAME);
11827 InitUserDataDirectory();
11829 if (!(file = fopen(filename, MODE_WRITE)))
11831 Warn("cannot write server setup file '%s'", filename);
11838 fprintFileHeader(file, SERVERSETUP_FILENAME);
11840 for (i = 0; i < ARRAY_SIZE(server_setup_tokens); i++)
11842 // just to make things nicer :)
11843 if (server_setup_tokens[i].value == &setup.use_api_server)
11844 fprintf(file, "\n");
11846 fprintf(file, "%s\n", getSetupLine(server_setup_tokens, "", i));
11851 SetFilePermissions(filename, PERMS_PRIVATE);
11856 void SaveSetup_EditorCascade(void)
11858 char *filename = getPath2(getSetupDir(), EDITORCASCADE_FILENAME);
11862 InitUserDataDirectory();
11864 if (!(file = fopen(filename, MODE_WRITE)))
11866 Warn("cannot write editor cascade state file '%s'", filename);
11873 fprintFileHeader(file, EDITORCASCADE_FILENAME);
11875 for (i = 0; i < ARRAY_SIZE(editor_cascade_setup_tokens); i++)
11876 fprintf(file, "%s\n", getSetupLine(editor_cascade_setup_tokens, "", i));
11880 SetFilePermissions(filename, PERMS_PRIVATE);
11885 void SaveSetup(void)
11887 SaveSetup_Default();
11888 SaveSetup_AutoSetup();
11889 SaveSetup_ServerSetup();
11890 SaveSetup_EditorCascade();
11893 static void SaveSetup_WriteGameControllerMappings(SetupFileHash *mappings_hash,
11898 if (!(file = fopen(filename, MODE_WRITE)))
11900 Warn("cannot write game controller mappings file '%s'", filename);
11905 BEGIN_HASH_ITERATION(mappings_hash, itr)
11907 fprintf(file, "%s\n", HASH_ITERATION_VALUE(itr));
11909 END_HASH_ITERATION(mappings_hash, itr)
11914 void SaveSetup_AddGameControllerMapping(char *mapping)
11916 char *filename = getPath2(getSetupDir(), GAMECONTROLLER_BASENAME);
11917 SetupFileHash *mappings_hash = newSetupFileHash();
11919 InitUserDataDirectory();
11921 // load existing personal game controller mappings
11922 LoadSetup_ReadGameControllerMappings(mappings_hash, filename);
11924 // add new mapping to personal game controller mappings
11925 addGameControllerMappingToHash(mappings_hash, mapping);
11927 // save updated personal game controller mappings
11928 SaveSetup_WriteGameControllerMappings(mappings_hash, filename);
11930 freeSetupFileHash(mappings_hash);
11934 void LoadCustomElementDescriptions(void)
11936 char *filename = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
11937 SetupFileHash *setup_file_hash;
11940 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
11942 if (element_info[i].custom_description != NULL)
11944 free(element_info[i].custom_description);
11945 element_info[i].custom_description = NULL;
11949 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
11952 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
11954 char *token = getStringCat2(element_info[i].token_name, ".name");
11955 char *value = getHashEntry(setup_file_hash, token);
11958 element_info[i].custom_description = getStringCopy(value);
11963 freeSetupFileHash(setup_file_hash);
11966 static int getElementFromToken(char *token)
11968 char *value = getHashEntry(element_token_hash, token);
11971 return atoi(value);
11973 Warn("unknown element token '%s'", token);
11975 return EL_UNDEFINED;
11978 void FreeGlobalAnimEventInfo(void)
11980 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
11982 if (gaei->event_list == NULL)
11987 for (i = 0; i < gaei->num_event_lists; i++)
11989 checked_free(gaei->event_list[i]->event_value);
11990 checked_free(gaei->event_list[i]);
11993 checked_free(gaei->event_list);
11995 gaei->event_list = NULL;
11996 gaei->num_event_lists = 0;
11999 static int AddGlobalAnimEventList(void)
12001 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
12002 int list_pos = gaei->num_event_lists++;
12004 gaei->event_list = checked_realloc(gaei->event_list, gaei->num_event_lists *
12005 sizeof(struct GlobalAnimEventListInfo *));
12007 gaei->event_list[list_pos] =
12008 checked_calloc(sizeof(struct GlobalAnimEventListInfo));
12010 struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
12012 gaeli->event_value = NULL;
12013 gaeli->num_event_values = 0;
12018 static int AddGlobalAnimEventValue(int list_pos, int event_value)
12020 // do not add empty global animation events
12021 if (event_value == ANIM_EVENT_NONE)
12024 // if list position is undefined, create new list
12025 if (list_pos == ANIM_EVENT_UNDEFINED)
12026 list_pos = AddGlobalAnimEventList();
12028 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
12029 struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
12030 int value_pos = gaeli->num_event_values++;
12032 gaeli->event_value = checked_realloc(gaeli->event_value,
12033 gaeli->num_event_values * sizeof(int *));
12035 gaeli->event_value[value_pos] = event_value;
12040 int GetGlobalAnimEventValue(int list_pos, int value_pos)
12042 if (list_pos == ANIM_EVENT_UNDEFINED)
12043 return ANIM_EVENT_NONE;
12045 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
12046 struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
12048 return gaeli->event_value[value_pos];
12051 int GetGlobalAnimEventValueCount(int list_pos)
12053 if (list_pos == ANIM_EVENT_UNDEFINED)
12056 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
12057 struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
12059 return gaeli->num_event_values;
12062 // This function checks if a string <s> of the format "string1, string2, ..."
12063 // exactly contains a string <s_contained>.
12065 static boolean string_has_parameter(char *s, char *s_contained)
12069 if (s == NULL || s_contained == NULL)
12072 if (strlen(s_contained) > strlen(s))
12075 if (strncmp(s, s_contained, strlen(s_contained)) == 0)
12077 char next_char = s[strlen(s_contained)];
12079 // check if next character is delimiter or whitespace
12080 if (next_char == ',' || next_char == '\0' ||
12081 next_char == ' ' || next_char == '\t')
12085 // check if string contains another parameter string after a comma
12086 substring = strchr(s, ',');
12087 if (substring == NULL) // string does not contain a comma
12090 // advance string pointer to next character after the comma
12093 // skip potential whitespaces after the comma
12094 while (*substring == ' ' || *substring == '\t')
12097 return string_has_parameter(substring, s_contained);
12100 static int get_anim_parameter_value_ce(char *s)
12103 char *pattern_1 = "ce_change:custom_";
12104 char *pattern_2 = ".page_";
12105 int pattern_1_len = strlen(pattern_1);
12106 char *matching_char = strstr(s_ptr, pattern_1);
12107 int result = ANIM_EVENT_NONE;
12109 if (matching_char == NULL)
12110 return ANIM_EVENT_NONE;
12112 result = ANIM_EVENT_CE_CHANGE;
12114 s_ptr = matching_char + pattern_1_len;
12116 // check for custom element number ("custom_X", "custom_XX" or "custom_XXX")
12117 if (*s_ptr >= '0' && *s_ptr <= '9')
12119 int gic_ce_nr = (*s_ptr++ - '0');
12121 if (*s_ptr >= '0' && *s_ptr <= '9')
12123 gic_ce_nr = 10 * gic_ce_nr + (*s_ptr++ - '0');
12125 if (*s_ptr >= '0' && *s_ptr <= '9')
12126 gic_ce_nr = 10 * gic_ce_nr + (*s_ptr++ - '0');
12129 if (gic_ce_nr < 1 || gic_ce_nr > NUM_CUSTOM_ELEMENTS)
12130 return ANIM_EVENT_NONE;
12132 // custom element stored as 0 to 255
12135 result |= gic_ce_nr << ANIM_EVENT_CE_BIT;
12139 // invalid custom element number specified
12141 return ANIM_EVENT_NONE;
12144 // check for change page number ("page_X" or "page_XX") (optional)
12145 if (strPrefix(s_ptr, pattern_2))
12147 s_ptr += strlen(pattern_2);
12149 if (*s_ptr >= '0' && *s_ptr <= '9')
12151 int gic_page_nr = (*s_ptr++ - '0');
12153 if (*s_ptr >= '0' && *s_ptr <= '9')
12154 gic_page_nr = 10 * gic_page_nr + (*s_ptr++ - '0');
12156 if (gic_page_nr < 1 || gic_page_nr > MAX_CHANGE_PAGES)
12157 return ANIM_EVENT_NONE;
12159 // change page stored as 1 to 32 (0 means "all change pages")
12161 result |= gic_page_nr << ANIM_EVENT_PAGE_BIT;
12165 // invalid animation part number specified
12167 return ANIM_EVENT_NONE;
12171 // discard result if next character is neither delimiter nor whitespace
12172 if (!(*s_ptr == ',' || *s_ptr == '\0' ||
12173 *s_ptr == ' ' || *s_ptr == '\t'))
12174 return ANIM_EVENT_NONE;
12179 static int get_anim_parameter_value(char *s)
12181 int event_value[] =
12189 char *pattern_1[] =
12197 char *pattern_2 = ".part_";
12198 char *matching_char = NULL;
12200 int pattern_1_len = 0;
12201 int result = ANIM_EVENT_NONE;
12204 result = get_anim_parameter_value_ce(s);
12206 if (result != ANIM_EVENT_NONE)
12209 for (i = 0; i < ARRAY_SIZE(event_value); i++)
12211 matching_char = strstr(s_ptr, pattern_1[i]);
12212 pattern_1_len = strlen(pattern_1[i]);
12213 result = event_value[i];
12215 if (matching_char != NULL)
12219 if (matching_char == NULL)
12220 return ANIM_EVENT_NONE;
12222 s_ptr = matching_char + pattern_1_len;
12224 // check for main animation number ("anim_X" or "anim_XX")
12225 if (*s_ptr >= '0' && *s_ptr <= '9')
12227 int gic_anim_nr = (*s_ptr++ - '0');
12229 if (*s_ptr >= '0' && *s_ptr <= '9')
12230 gic_anim_nr = 10 * gic_anim_nr + (*s_ptr++ - '0');
12232 if (gic_anim_nr < 1 || gic_anim_nr > MAX_GLOBAL_ANIMS)
12233 return ANIM_EVENT_NONE;
12235 result |= gic_anim_nr << ANIM_EVENT_ANIM_BIT;
12239 // invalid main animation number specified
12241 return ANIM_EVENT_NONE;
12244 // check for animation part number ("part_X" or "part_XX") (optional)
12245 if (strPrefix(s_ptr, pattern_2))
12247 s_ptr += strlen(pattern_2);
12249 if (*s_ptr >= '0' && *s_ptr <= '9')
12251 int gic_part_nr = (*s_ptr++ - '0');
12253 if (*s_ptr >= '0' && *s_ptr <= '9')
12254 gic_part_nr = 10 * gic_part_nr + (*s_ptr++ - '0');
12256 if (gic_part_nr < 1 || gic_part_nr > MAX_GLOBAL_ANIM_PARTS)
12257 return ANIM_EVENT_NONE;
12259 result |= gic_part_nr << ANIM_EVENT_PART_BIT;
12263 // invalid animation part number specified
12265 return ANIM_EVENT_NONE;
12269 // discard result if next character is neither delimiter nor whitespace
12270 if (!(*s_ptr == ',' || *s_ptr == '\0' ||
12271 *s_ptr == ' ' || *s_ptr == '\t'))
12272 return ANIM_EVENT_NONE;
12277 static int get_anim_parameter_values(char *s)
12279 int list_pos = ANIM_EVENT_UNDEFINED;
12280 int event_value = ANIM_EVENT_DEFAULT;
12282 if (string_has_parameter(s, "any"))
12283 event_value |= ANIM_EVENT_ANY;
12285 if (string_has_parameter(s, "click:self") ||
12286 string_has_parameter(s, "click") ||
12287 string_has_parameter(s, "self"))
12288 event_value |= ANIM_EVENT_SELF;
12290 if (string_has_parameter(s, "unclick:any"))
12291 event_value |= ANIM_EVENT_UNCLICK_ANY;
12293 // if animation event found, add it to global animation event list
12294 if (event_value != ANIM_EVENT_NONE)
12295 list_pos = AddGlobalAnimEventValue(list_pos, event_value);
12299 // add optional "click:anim_X" or "click:anim_X.part_X" parameter
12300 event_value = get_anim_parameter_value(s);
12302 // if animation event found, add it to global animation event list
12303 if (event_value != ANIM_EVENT_NONE)
12304 list_pos = AddGlobalAnimEventValue(list_pos, event_value);
12306 // continue with next part of the string, starting with next comma
12307 s = strchr(s + 1, ',');
12313 static int get_anim_action_parameter_value(char *token)
12315 // check most common default case first to massively speed things up
12316 if (strEqual(token, ARG_UNDEFINED))
12317 return ANIM_EVENT_ACTION_NONE;
12319 int result = getImageIDFromToken(token);
12323 char *gfx_token = getStringCat2("gfx.", token);
12325 result = getImageIDFromToken(gfx_token);
12327 checked_free(gfx_token);
12332 Key key = getKeyFromX11KeyName(token);
12334 if (key != KSYM_UNDEFINED)
12335 result = -(int)key;
12342 result = get_hash_from_string(token); // unsigned int => int
12343 result = ABS(result); // may be negative now
12344 result += (result < MAX_IMAGE_FILES ? MAX_IMAGE_FILES : 0);
12346 setHashEntry(anim_url_hash, int2str(result, 0), token);
12351 result = ANIM_EVENT_ACTION_NONE;
12356 int get_parameter_value(char *value_raw, char *suffix, int type)
12358 char *value = getStringToLower(value_raw);
12359 int result = 0; // probably a save default value
12361 if (strEqual(suffix, ".direction"))
12363 result = (strEqual(value, "left") ? MV_LEFT :
12364 strEqual(value, "right") ? MV_RIGHT :
12365 strEqual(value, "up") ? MV_UP :
12366 strEqual(value, "down") ? MV_DOWN : MV_NONE);
12368 else if (strEqual(suffix, ".position"))
12370 result = (strEqual(value, "left") ? POS_LEFT :
12371 strEqual(value, "right") ? POS_RIGHT :
12372 strEqual(value, "top") ? POS_TOP :
12373 strEqual(value, "upper") ? POS_UPPER :
12374 strEqual(value, "middle") ? POS_MIDDLE :
12375 strEqual(value, "lower") ? POS_LOWER :
12376 strEqual(value, "bottom") ? POS_BOTTOM :
12377 strEqual(value, "any") ? POS_ANY :
12378 strEqual(value, "ce") ? POS_CE :
12379 strEqual(value, "ce_trigger") ? POS_CE_TRIGGER :
12380 strEqual(value, "last") ? POS_LAST : POS_UNDEFINED);
12382 else if (strEqual(suffix, ".align"))
12384 result = (strEqual(value, "left") ? ALIGN_LEFT :
12385 strEqual(value, "right") ? ALIGN_RIGHT :
12386 strEqual(value, "center") ? ALIGN_CENTER :
12387 strEqual(value, "middle") ? ALIGN_CENTER : ALIGN_DEFAULT);
12389 else if (strEqual(suffix, ".valign"))
12391 result = (strEqual(value, "top") ? VALIGN_TOP :
12392 strEqual(value, "bottom") ? VALIGN_BOTTOM :
12393 strEqual(value, "middle") ? VALIGN_MIDDLE :
12394 strEqual(value, "center") ? VALIGN_MIDDLE : VALIGN_DEFAULT);
12396 else if (strEqual(suffix, ".anim_mode"))
12398 result = (string_has_parameter(value, "none") ? ANIM_NONE :
12399 string_has_parameter(value, "loop") ? ANIM_LOOP :
12400 string_has_parameter(value, "linear") ? ANIM_LINEAR :
12401 string_has_parameter(value, "pingpong") ? ANIM_PINGPONG :
12402 string_has_parameter(value, "pingpong2") ? ANIM_PINGPONG2 :
12403 string_has_parameter(value, "random") ? ANIM_RANDOM :
12404 string_has_parameter(value, "random_static") ? ANIM_RANDOM_STATIC :
12405 string_has_parameter(value, "ce_value") ? ANIM_CE_VALUE :
12406 string_has_parameter(value, "ce_score") ? ANIM_CE_SCORE :
12407 string_has_parameter(value, "ce_delay") ? ANIM_CE_DELAY :
12408 string_has_parameter(value, "horizontal") ? ANIM_HORIZONTAL :
12409 string_has_parameter(value, "vertical") ? ANIM_VERTICAL :
12410 string_has_parameter(value, "centered") ? ANIM_CENTERED :
12411 string_has_parameter(value, "all") ? ANIM_ALL :
12412 string_has_parameter(value, "tiled") ? ANIM_TILED :
12413 string_has_parameter(value, "level_nr") ? ANIM_LEVEL_NR :
12416 if (string_has_parameter(value, "once"))
12417 result |= ANIM_ONCE;
12419 if (string_has_parameter(value, "reverse"))
12420 result |= ANIM_REVERSE;
12422 if (string_has_parameter(value, "opaque_player"))
12423 result |= ANIM_OPAQUE_PLAYER;
12425 if (string_has_parameter(value, "static_panel"))
12426 result |= ANIM_STATIC_PANEL;
12428 else if (strEqual(suffix, ".init_event") ||
12429 strEqual(suffix, ".anim_event"))
12431 result = get_anim_parameter_values(value);
12433 else if (strEqual(suffix, ".init_delay_action") ||
12434 strEqual(suffix, ".anim_delay_action") ||
12435 strEqual(suffix, ".post_delay_action") ||
12436 strEqual(suffix, ".init_event_action") ||
12437 strEqual(suffix, ".anim_event_action"))
12439 result = get_anim_action_parameter_value(value_raw);
12441 else if (strEqual(suffix, ".class"))
12443 result = (strEqual(value, ARG_UNDEFINED) ? ARG_UNDEFINED_VALUE :
12444 get_hash_from_string(value));
12446 else if (strEqual(suffix, ".style"))
12448 result = STYLE_DEFAULT;
12450 if (string_has_parameter(value, "accurate_borders"))
12451 result |= STYLE_ACCURATE_BORDERS;
12453 if (string_has_parameter(value, "inner_corners"))
12454 result |= STYLE_INNER_CORNERS;
12456 if (string_has_parameter(value, "reverse"))
12457 result |= STYLE_REVERSE;
12459 if (string_has_parameter(value, "leftmost_position"))
12460 result |= STYLE_LEFTMOST_POSITION;
12462 if (string_has_parameter(value, "block_clicks"))
12463 result |= STYLE_BLOCK;
12465 if (string_has_parameter(value, "passthrough_clicks"))
12466 result |= STYLE_PASSTHROUGH;
12468 if (string_has_parameter(value, "multiple_actions"))
12469 result |= STYLE_MULTIPLE_ACTIONS;
12471 if (string_has_parameter(value, "consume_ce_event"))
12472 result |= STYLE_CONSUME_CE_EVENT;
12474 else if (strEqual(suffix, ".fade_mode"))
12476 result = (string_has_parameter(value, "none") ? FADE_MODE_NONE :
12477 string_has_parameter(value, "fade") ? FADE_MODE_FADE :
12478 string_has_parameter(value, "fade_in") ? FADE_MODE_FADE_IN :
12479 string_has_parameter(value, "fade_out") ? FADE_MODE_FADE_OUT :
12480 string_has_parameter(value, "crossfade") ? FADE_MODE_CROSSFADE :
12481 string_has_parameter(value, "melt") ? FADE_MODE_MELT :
12482 string_has_parameter(value, "curtain") ? FADE_MODE_CURTAIN :
12483 FADE_MODE_DEFAULT);
12485 else if (strEqual(suffix, ".auto_delay_unit"))
12487 result = (string_has_parameter(value, "ms") ? AUTO_DELAY_UNIT_MS :
12488 string_has_parameter(value, "frames") ? AUTO_DELAY_UNIT_FRAMES :
12489 AUTO_DELAY_UNIT_DEFAULT);
12491 else if (strPrefix(suffix, ".font")) // (may also be ".font_xyz")
12493 result = gfx.get_font_from_token_function(value);
12495 else // generic parameter of type integer or boolean
12497 result = (strEqual(value, ARG_UNDEFINED) ? ARG_UNDEFINED_VALUE :
12498 type == TYPE_INTEGER ? get_integer_from_string(value) :
12499 type == TYPE_BOOLEAN ? get_boolean_from_string(value) :
12500 ARG_UNDEFINED_VALUE);
12508 static int get_token_parameter_value(char *token, char *value_raw)
12512 if (token == NULL || value_raw == NULL)
12513 return ARG_UNDEFINED_VALUE;
12515 suffix = strrchr(token, '.');
12516 if (suffix == NULL)
12519 if (strEqual(suffix, ".element"))
12520 return getElementFromToken(value_raw);
12522 // !!! USE CORRECT VALUE TYPE (currently works also for TYPE_BOOLEAN) !!!
12523 return get_parameter_value(value_raw, suffix, TYPE_INTEGER);
12526 void InitMenuDesignSettings_FromHash(SetupFileHash *setup_file_hash,
12527 boolean ignore_defaults)
12531 for (i = 0; image_config_vars[i].token != NULL; i++)
12533 char *value = getHashEntry(setup_file_hash, image_config_vars[i].token);
12535 // (ignore definitions set to "[DEFAULT]" which are already initialized)
12536 if (ignore_defaults && strEqual(value, ARG_DEFAULT))
12540 *image_config_vars[i].value =
12541 get_token_parameter_value(image_config_vars[i].token, value);
12545 void InitMenuDesignSettings_Static(void)
12547 // always start with reliable default values from static default config
12548 InitMenuDesignSettings_FromHash(image_config_hash, FALSE);
12551 static void InitMenuDesignSettings_SpecialPreProcessing(void)
12555 // the following initializes hierarchical values from static configuration
12557 // special case: initialize "ARG_DEFAULT" values in static default config
12558 // (e.g., initialize "[titlemessage].fade_mode" from "[title].fade_mode")
12559 titlescreen_initial_first_default.fade_mode =
12560 title_initial_first_default.fade_mode;
12561 titlescreen_initial_first_default.fade_delay =
12562 title_initial_first_default.fade_delay;
12563 titlescreen_initial_first_default.post_delay =
12564 title_initial_first_default.post_delay;
12565 titlescreen_initial_first_default.auto_delay =
12566 title_initial_first_default.auto_delay;
12567 titlescreen_initial_first_default.auto_delay_unit =
12568 title_initial_first_default.auto_delay_unit;
12569 titlescreen_first_default.fade_mode = title_first_default.fade_mode;
12570 titlescreen_first_default.fade_delay = title_first_default.fade_delay;
12571 titlescreen_first_default.post_delay = title_first_default.post_delay;
12572 titlescreen_first_default.auto_delay = title_first_default.auto_delay;
12573 titlescreen_first_default.auto_delay_unit =
12574 title_first_default.auto_delay_unit;
12575 titlemessage_initial_first_default.fade_mode =
12576 title_initial_first_default.fade_mode;
12577 titlemessage_initial_first_default.fade_delay =
12578 title_initial_first_default.fade_delay;
12579 titlemessage_initial_first_default.post_delay =
12580 title_initial_first_default.post_delay;
12581 titlemessage_initial_first_default.auto_delay =
12582 title_initial_first_default.auto_delay;
12583 titlemessage_initial_first_default.auto_delay_unit =
12584 title_initial_first_default.auto_delay_unit;
12585 titlemessage_first_default.fade_mode = title_first_default.fade_mode;
12586 titlemessage_first_default.fade_delay = title_first_default.fade_delay;
12587 titlemessage_first_default.post_delay = title_first_default.post_delay;
12588 titlemessage_first_default.auto_delay = title_first_default.auto_delay;
12589 titlemessage_first_default.auto_delay_unit =
12590 title_first_default.auto_delay_unit;
12592 titlescreen_initial_default.fade_mode = title_initial_default.fade_mode;
12593 titlescreen_initial_default.fade_delay = title_initial_default.fade_delay;
12594 titlescreen_initial_default.post_delay = title_initial_default.post_delay;
12595 titlescreen_initial_default.auto_delay = title_initial_default.auto_delay;
12596 titlescreen_initial_default.auto_delay_unit =
12597 title_initial_default.auto_delay_unit;
12598 titlescreen_default.fade_mode = title_default.fade_mode;
12599 titlescreen_default.fade_delay = title_default.fade_delay;
12600 titlescreen_default.post_delay = title_default.post_delay;
12601 titlescreen_default.auto_delay = title_default.auto_delay;
12602 titlescreen_default.auto_delay_unit = title_default.auto_delay_unit;
12603 titlemessage_initial_default.fade_mode = title_initial_default.fade_mode;
12604 titlemessage_initial_default.fade_delay = title_initial_default.fade_delay;
12605 titlemessage_initial_default.post_delay = title_initial_default.post_delay;
12606 titlemessage_initial_default.auto_delay_unit =
12607 title_initial_default.auto_delay_unit;
12608 titlemessage_default.fade_mode = title_default.fade_mode;
12609 titlemessage_default.fade_delay = title_default.fade_delay;
12610 titlemessage_default.post_delay = title_default.post_delay;
12611 titlemessage_default.auto_delay = title_default.auto_delay;
12612 titlemessage_default.auto_delay_unit = title_default.auto_delay_unit;
12614 // special case: initialize "ARG_DEFAULT" values in static default config
12615 // (e.g., init "titlemessage_1.fade_mode" from "[titlemessage].fade_mode")
12616 for (i = 0; i < MAX_NUM_TITLE_MESSAGES; i++)
12618 titlescreen_initial_first[i] = titlescreen_initial_first_default;
12619 titlescreen_first[i] = titlescreen_first_default;
12620 titlemessage_initial_first[i] = titlemessage_initial_first_default;
12621 titlemessage_first[i] = titlemessage_first_default;
12623 titlescreen_initial[i] = titlescreen_initial_default;
12624 titlescreen[i] = titlescreen_default;
12625 titlemessage_initial[i] = titlemessage_initial_default;
12626 titlemessage[i] = titlemessage_default;
12629 // special case: initialize "ARG_DEFAULT" values in static default config
12630 // (eg, init "menu.enter_screen.SCORES.xyz" from "menu.enter_screen.xyz")
12631 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
12633 if (i == GFX_SPECIAL_ARG_TITLE) // title values already initialized
12636 menu.enter_screen[i] = menu.enter_screen[GFX_SPECIAL_ARG_DEFAULT];
12637 menu.leave_screen[i] = menu.leave_screen[GFX_SPECIAL_ARG_DEFAULT];
12638 menu.next_screen[i] = menu.next_screen[GFX_SPECIAL_ARG_DEFAULT];
12641 // special case: initialize "ARG_DEFAULT" values in static default config
12642 // (eg, init "viewport.door_1.MAIN.xyz" from "viewport.door_1.xyz")
12643 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
12645 viewport.window[i] = viewport.window[GFX_SPECIAL_ARG_DEFAULT];
12646 viewport.playfield[i] = viewport.playfield[GFX_SPECIAL_ARG_DEFAULT];
12647 viewport.door_1[i] = viewport.door_1[GFX_SPECIAL_ARG_DEFAULT];
12649 if (i == GFX_SPECIAL_ARG_EDITOR) // editor values already initialized
12652 viewport.door_2[i] = viewport.door_2[GFX_SPECIAL_ARG_DEFAULT];
12656 static void InitMenuDesignSettings_SpecialPostProcessing(void)
12660 struct XY *dst, *src;
12662 game_buttons_xy[] =
12664 { &game.button.save, &game.button.stop },
12665 { &game.button.pause2, &game.button.pause },
12666 { &game.button.load, &game.button.play },
12667 { &game.button.undo, &game.button.stop },
12668 { &game.button.redo, &game.button.play },
12674 // special case: initialize later added SETUP list size from LEVELS value
12675 if (menu.list_size[GAME_MODE_SETUP] == -1)
12676 menu.list_size[GAME_MODE_SETUP] = menu.list_size[GAME_MODE_LEVELS];
12678 // set default position for snapshot buttons to stop/pause/play buttons
12679 for (i = 0; game_buttons_xy[i].dst != NULL; i++)
12680 if ((*game_buttons_xy[i].dst).x == -1 &&
12681 (*game_buttons_xy[i].dst).y == -1)
12682 *game_buttons_xy[i].dst = *game_buttons_xy[i].src;
12684 // --------------------------------------------------------------------------
12685 // dynamic viewports (including playfield margins, borders and alignments)
12686 // --------------------------------------------------------------------------
12688 // dynamic viewports currently only supported for landscape mode
12689 int display_width = MAX(video.display_width, video.display_height);
12690 int display_height = MIN(video.display_width, video.display_height);
12692 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
12694 struct RectWithBorder *vp_window = &viewport.window[i];
12695 struct RectWithBorder *vp_playfield = &viewport.playfield[i];
12696 struct RectWithBorder *vp_door_1 = &viewport.door_1[i];
12697 struct RectWithBorder *vp_door_2 = &viewport.door_2[i];
12698 boolean dynamic_window_width = (vp_window->min_width != -1);
12699 boolean dynamic_window_height = (vp_window->min_height != -1);
12700 boolean dynamic_playfield_width = (vp_playfield->min_width != -1);
12701 boolean dynamic_playfield_height = (vp_playfield->min_height != -1);
12703 // adjust window size if min/max width/height is specified
12705 if (vp_window->min_width != -1)
12707 int window_width = display_width;
12709 // when using static window height, use aspect ratio of display
12710 if (vp_window->min_height == -1)
12711 window_width = vp_window->height * display_width / display_height;
12713 vp_window->width = MAX(vp_window->min_width, window_width);
12716 if (vp_window->min_height != -1)
12718 int window_height = display_height;
12720 // when using static window width, use aspect ratio of display
12721 if (vp_window->min_width == -1)
12722 window_height = vp_window->width * display_height / display_width;
12724 vp_window->height = MAX(vp_window->min_height, window_height);
12727 if (vp_window->max_width != -1)
12728 vp_window->width = MIN(vp_window->width, vp_window->max_width);
12730 if (vp_window->max_height != -1)
12731 vp_window->height = MIN(vp_window->height, vp_window->max_height);
12733 int playfield_width = vp_window->width;
12734 int playfield_height = vp_window->height;
12736 // adjust playfield size and position according to specified margins
12738 playfield_width -= vp_playfield->margin_left;
12739 playfield_width -= vp_playfield->margin_right;
12741 playfield_height -= vp_playfield->margin_top;
12742 playfield_height -= vp_playfield->margin_bottom;
12744 // adjust playfield size if min/max width/height is specified
12746 if (vp_playfield->min_width != -1)
12747 vp_playfield->width = MAX(vp_playfield->min_width, playfield_width);
12749 if (vp_playfield->min_height != -1)
12750 vp_playfield->height = MAX(vp_playfield->min_height, playfield_height);
12752 if (vp_playfield->max_width != -1)
12753 vp_playfield->width = MIN(vp_playfield->width, vp_playfield->max_width);
12755 if (vp_playfield->max_height != -1)
12756 vp_playfield->height = MIN(vp_playfield->height, vp_playfield->max_height);
12758 // adjust playfield position according to specified alignment
12760 if (vp_playfield->align == ALIGN_LEFT || vp_playfield->x > 0)
12761 vp_playfield->x = ALIGNED_VP_XPOS(vp_playfield);
12762 else if (vp_playfield->align == ALIGN_CENTER)
12763 vp_playfield->x = playfield_width / 2 - vp_playfield->width / 2;
12764 else if (vp_playfield->align == ALIGN_RIGHT)
12765 vp_playfield->x += playfield_width - vp_playfield->width;
12767 if (vp_playfield->valign == VALIGN_TOP || vp_playfield->y > 0)
12768 vp_playfield->y = ALIGNED_VP_YPOS(vp_playfield);
12769 else if (vp_playfield->valign == VALIGN_MIDDLE)
12770 vp_playfield->y = playfield_height / 2 - vp_playfield->height / 2;
12771 else if (vp_playfield->valign == VALIGN_BOTTOM)
12772 vp_playfield->y += playfield_height - vp_playfield->height;
12774 vp_playfield->x += vp_playfield->margin_left;
12775 vp_playfield->y += vp_playfield->margin_top;
12777 // adjust individual playfield borders if only default border is specified
12779 if (vp_playfield->border_left == -1)
12780 vp_playfield->border_left = vp_playfield->border_size;
12781 if (vp_playfield->border_right == -1)
12782 vp_playfield->border_right = vp_playfield->border_size;
12783 if (vp_playfield->border_top == -1)
12784 vp_playfield->border_top = vp_playfield->border_size;
12785 if (vp_playfield->border_bottom == -1)
12786 vp_playfield->border_bottom = vp_playfield->border_size;
12788 // set dynamic playfield borders if borders are specified as undefined
12789 // (but only if window size was dynamic and playfield size was static)
12791 if (dynamic_window_width && !dynamic_playfield_width)
12793 if (vp_playfield->border_left == -1)
12795 vp_playfield->border_left = (vp_playfield->x -
12796 vp_playfield->margin_left);
12797 vp_playfield->x -= vp_playfield->border_left;
12798 vp_playfield->width += vp_playfield->border_left;
12801 if (vp_playfield->border_right == -1)
12803 vp_playfield->border_right = (vp_window->width -
12805 vp_playfield->width -
12806 vp_playfield->margin_right);
12807 vp_playfield->width += vp_playfield->border_right;
12811 if (dynamic_window_height && !dynamic_playfield_height)
12813 if (vp_playfield->border_top == -1)
12815 vp_playfield->border_top = (vp_playfield->y -
12816 vp_playfield->margin_top);
12817 vp_playfield->y -= vp_playfield->border_top;
12818 vp_playfield->height += vp_playfield->border_top;
12821 if (vp_playfield->border_bottom == -1)
12823 vp_playfield->border_bottom = (vp_window->height -
12825 vp_playfield->height -
12826 vp_playfield->margin_bottom);
12827 vp_playfield->height += vp_playfield->border_bottom;
12831 // adjust playfield size to be a multiple of a defined alignment tile size
12833 int align_size = vp_playfield->align_size;
12834 int playfield_xtiles = vp_playfield->width / align_size;
12835 int playfield_ytiles = vp_playfield->height / align_size;
12836 int playfield_width_corrected = playfield_xtiles * align_size;
12837 int playfield_height_corrected = playfield_ytiles * align_size;
12838 boolean is_playfield_mode = (i == GFX_SPECIAL_ARG_PLAYING ||
12839 i == GFX_SPECIAL_ARG_EDITOR);
12841 if (is_playfield_mode &&
12842 dynamic_playfield_width &&
12843 vp_playfield->width != playfield_width_corrected)
12845 int playfield_xdiff = vp_playfield->width - playfield_width_corrected;
12847 vp_playfield->width = playfield_width_corrected;
12849 if (vp_playfield->align == ALIGN_LEFT)
12851 vp_playfield->border_left += playfield_xdiff;
12853 else if (vp_playfield->align == ALIGN_RIGHT)
12855 vp_playfield->border_right += playfield_xdiff;
12857 else if (vp_playfield->align == ALIGN_CENTER)
12859 int border_left_diff = playfield_xdiff / 2;
12860 int border_right_diff = playfield_xdiff - border_left_diff;
12862 vp_playfield->border_left += border_left_diff;
12863 vp_playfield->border_right += border_right_diff;
12867 if (is_playfield_mode &&
12868 dynamic_playfield_height &&
12869 vp_playfield->height != playfield_height_corrected)
12871 int playfield_ydiff = vp_playfield->height - playfield_height_corrected;
12873 vp_playfield->height = playfield_height_corrected;
12875 if (vp_playfield->valign == VALIGN_TOP)
12877 vp_playfield->border_top += playfield_ydiff;
12879 else if (vp_playfield->align == VALIGN_BOTTOM)
12881 vp_playfield->border_right += playfield_ydiff;
12883 else if (vp_playfield->align == VALIGN_MIDDLE)
12885 int border_top_diff = playfield_ydiff / 2;
12886 int border_bottom_diff = playfield_ydiff - border_top_diff;
12888 vp_playfield->border_top += border_top_diff;
12889 vp_playfield->border_bottom += border_bottom_diff;
12893 // adjust door positions according to specified alignment
12895 for (j = 0; j < 2; j++)
12897 struct RectWithBorder *vp_door = (j == 0 ? vp_door_1 : vp_door_2);
12899 if (vp_door->align == ALIGN_LEFT || vp_door->x > 0)
12900 vp_door->x = ALIGNED_VP_XPOS(vp_door);
12901 else if (vp_door->align == ALIGN_CENTER)
12902 vp_door->x = vp_window->width / 2 - vp_door->width / 2;
12903 else if (vp_door->align == ALIGN_RIGHT)
12904 vp_door->x += vp_window->width - vp_door->width;
12906 if (vp_door->valign == VALIGN_TOP || vp_door->y > 0)
12907 vp_door->y = ALIGNED_VP_YPOS(vp_door);
12908 else if (vp_door->valign == VALIGN_MIDDLE)
12909 vp_door->y = vp_window->height / 2 - vp_door->height / 2;
12910 else if (vp_door->valign == VALIGN_BOTTOM)
12911 vp_door->y += vp_window->height - vp_door->height;
12916 static void InitMenuDesignSettings_SpecialPostProcessing_AfterGraphics(void)
12920 struct XYTileSize *dst, *src;
12923 editor_buttons_xy[] =
12926 &editor.button.element_left, &editor.palette.element_left,
12927 IMG_GFX_EDITOR_BUTTON_ELEMENT_LEFT
12930 &editor.button.element_middle, &editor.palette.element_middle,
12931 IMG_GFX_EDITOR_BUTTON_ELEMENT_MIDDLE
12934 &editor.button.element_right, &editor.palette.element_right,
12935 IMG_GFX_EDITOR_BUTTON_ELEMENT_RIGHT
12942 // set default position for element buttons to element graphics
12943 for (i = 0; editor_buttons_xy[i].dst != NULL; i++)
12945 if ((*editor_buttons_xy[i].dst).x == -1 &&
12946 (*editor_buttons_xy[i].dst).y == -1)
12948 struct GraphicInfo *gd = &graphic_info[editor_buttons_xy[i].graphic];
12950 gd->width = gd->height = editor_buttons_xy[i].src->tile_size;
12952 *editor_buttons_xy[i].dst = *editor_buttons_xy[i].src;
12956 // adjust editor palette rows and columns if specified to be dynamic
12958 if (editor.palette.cols == -1)
12960 int vp_width = viewport.playfield[GFX_SPECIAL_ARG_EDITOR].width;
12961 int bt_width = graphic_info[IMG_EDITOR_PALETTE_BUTTON].width;
12962 int sc_width = graphic_info[IMG_EDITOR_PALETTE_SCROLLBAR].width;
12964 editor.palette.cols = (vp_width - sc_width) / bt_width;
12966 if (editor.palette.x == -1)
12968 int palette_width = editor.palette.cols * bt_width + sc_width;
12970 editor.palette.x = (vp_width - palette_width) / 2;
12974 if (editor.palette.rows == -1)
12976 int vp_height = viewport.playfield[GFX_SPECIAL_ARG_EDITOR].height;
12977 int bt_height = graphic_info[IMG_EDITOR_PALETTE_BUTTON].height;
12978 int tx_height = getFontHeight(FONT_TEXT_2);
12980 editor.palette.rows = (vp_height - tx_height) / bt_height;
12982 if (editor.palette.y == -1)
12984 int palette_height = editor.palette.rows * bt_height + tx_height;
12986 editor.palette.y = (vp_height - palette_height) / 2;
12991 static void InitMenuDesignSettings_PreviewPlayers_Ext(SetupFileHash *hash,
12992 boolean initialize)
12994 // special case: check if network and preview player positions are redefined,
12995 // to compare this later against the main menu level preview being redefined
12996 struct TokenIntPtrInfo menu_config_players[] =
12998 { "main.network_players.x", &menu.main.network_players.redefined },
12999 { "main.network_players.y", &menu.main.network_players.redefined },
13000 { "main.preview_players.x", &menu.main.preview_players.redefined },
13001 { "main.preview_players.y", &menu.main.preview_players.redefined },
13002 { "preview.x", &preview.redefined },
13003 { "preview.y", &preview.redefined }
13009 for (i = 0; i < ARRAY_SIZE(menu_config_players); i++)
13010 *menu_config_players[i].value = FALSE;
13014 for (i = 0; i < ARRAY_SIZE(menu_config_players); i++)
13015 if (getHashEntry(hash, menu_config_players[i].token) != NULL)
13016 *menu_config_players[i].value = TRUE;
13020 static void InitMenuDesignSettings_PreviewPlayers(void)
13022 InitMenuDesignSettings_PreviewPlayers_Ext(NULL, TRUE);
13025 static void InitMenuDesignSettings_PreviewPlayers_FromHash(SetupFileHash *hash)
13027 InitMenuDesignSettings_PreviewPlayers_Ext(hash, FALSE);
13030 static void LoadMenuDesignSettingsFromFilename(char *filename)
13032 static struct TitleFadingInfo tfi;
13033 static struct TitleMessageInfo tmi;
13034 static struct TokenInfo title_tokens[] =
13036 { TYPE_INTEGER, &tfi.fade_mode, ".fade_mode" },
13037 { TYPE_INTEGER, &tfi.fade_delay, ".fade_delay" },
13038 { TYPE_INTEGER, &tfi.post_delay, ".post_delay" },
13039 { TYPE_INTEGER, &tfi.auto_delay, ".auto_delay" },
13040 { TYPE_INTEGER, &tfi.auto_delay_unit, ".auto_delay_unit" },
13044 static struct TokenInfo titlemessage_tokens[] =
13046 { TYPE_INTEGER, &tmi.x, ".x" },
13047 { TYPE_INTEGER, &tmi.y, ".y" },
13048 { TYPE_INTEGER, &tmi.width, ".width" },
13049 { TYPE_INTEGER, &tmi.height, ".height" },
13050 { TYPE_INTEGER, &tmi.chars, ".chars" },
13051 { TYPE_INTEGER, &tmi.lines, ".lines" },
13052 { TYPE_INTEGER, &tmi.align, ".align" },
13053 { TYPE_INTEGER, &tmi.valign, ".valign" },
13054 { TYPE_INTEGER, &tmi.font, ".font" },
13055 { TYPE_BOOLEAN, &tmi.autowrap, ".autowrap" },
13056 { TYPE_BOOLEAN, &tmi.centered, ".centered" },
13057 { TYPE_BOOLEAN, &tmi.parse_comments, ".parse_comments" },
13058 { TYPE_INTEGER, &tmi.sort_priority, ".sort_priority" },
13059 { TYPE_INTEGER, &tmi.fade_mode, ".fade_mode" },
13060 { TYPE_INTEGER, &tmi.fade_delay, ".fade_delay" },
13061 { TYPE_INTEGER, &tmi.post_delay, ".post_delay" },
13062 { TYPE_INTEGER, &tmi.auto_delay, ".auto_delay" },
13063 { TYPE_INTEGER, &tmi.auto_delay_unit, ".auto_delay_unit" },
13069 struct TitleFadingInfo *info;
13074 // initialize first titles from "enter screen" definitions, if defined
13075 { &title_initial_first_default, "menu.enter_screen.TITLE" },
13076 { &title_first_default, "menu.enter_screen.TITLE" },
13078 // initialize title screens from "next screen" definitions, if defined
13079 { &title_initial_default, "menu.next_screen.TITLE" },
13080 { &title_default, "menu.next_screen.TITLE" },
13086 struct TitleMessageInfo *array;
13089 titlemessage_arrays[] =
13091 // initialize first titles from "enter screen" definitions, if defined
13092 { titlescreen_initial_first, "menu.enter_screen.TITLE" },
13093 { titlescreen_first, "menu.enter_screen.TITLE" },
13094 { titlemessage_initial_first, "menu.enter_screen.TITLE" },
13095 { titlemessage_first, "menu.enter_screen.TITLE" },
13097 // initialize titles from "next screen" definitions, if defined
13098 { titlescreen_initial, "menu.next_screen.TITLE" },
13099 { titlescreen, "menu.next_screen.TITLE" },
13100 { titlemessage_initial, "menu.next_screen.TITLE" },
13101 { titlemessage, "menu.next_screen.TITLE" },
13103 // overwrite titles with title definitions, if defined
13104 { titlescreen_initial_first, "[title_initial]" },
13105 { titlescreen_first, "[title]" },
13106 { titlemessage_initial_first, "[title_initial]" },
13107 { titlemessage_first, "[title]" },
13109 { titlescreen_initial, "[title_initial]" },
13110 { titlescreen, "[title]" },
13111 { titlemessage_initial, "[title_initial]" },
13112 { titlemessage, "[title]" },
13114 // overwrite titles with title screen/message definitions, if defined
13115 { titlescreen_initial_first, "[titlescreen_initial]" },
13116 { titlescreen_first, "[titlescreen]" },
13117 { titlemessage_initial_first, "[titlemessage_initial]" },
13118 { titlemessage_first, "[titlemessage]" },
13120 { titlescreen_initial, "[titlescreen_initial]" },
13121 { titlescreen, "[titlescreen]" },
13122 { titlemessage_initial, "[titlemessage_initial]" },
13123 { titlemessage, "[titlemessage]" },
13127 SetupFileHash *setup_file_hash;
13130 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
13133 // the following initializes hierarchical values from dynamic configuration
13135 // special case: initialize with default values that may be overwritten
13136 // (e.g., init "menu.draw_xoffset.INFO" from "menu.draw_xoffset")
13137 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
13139 struct TokenIntPtrInfo menu_config[] =
13141 { "menu.draw_xoffset", &menu.draw_xoffset[i] },
13142 { "menu.draw_yoffset", &menu.draw_yoffset[i] },
13143 { "menu.list_size", &menu.list_size[i] }
13146 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
13148 char *token = menu_config[j].token;
13149 char *value = getHashEntry(setup_file_hash, token);
13152 *menu_config[j].value = get_integer_from_string(value);
13156 // special case: initialize with default values that may be overwritten
13157 // (eg, init "menu.draw_xoffset.INFO[XXX]" from "menu.draw_xoffset.INFO")
13158 for (i = 0; i < NUM_SPECIAL_GFX_INFO_ARGS; i++)
13160 struct TokenIntPtrInfo menu_config[] =
13162 { "menu.draw_xoffset.INFO", &menu.draw_xoffset_info[i] },
13163 { "menu.draw_yoffset.INFO", &menu.draw_yoffset_info[i] },
13164 { "menu.list_size.INFO", &menu.list_size_info[i] },
13165 { "menu.list_entry_size.INFO", &menu.list_entry_size_info[i] },
13166 { "menu.tile_size.INFO", &menu.tile_size_info[i] }
13169 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
13171 char *token = menu_config[j].token;
13172 char *value = getHashEntry(setup_file_hash, token);
13175 *menu_config[j].value = get_integer_from_string(value);
13179 // special case: initialize with default values that may be overwritten
13180 // (eg, init "menu.draw_xoffset.SETUP[XXX]" from "menu.draw_xoffset.SETUP")
13181 for (i = 0; i < NUM_SPECIAL_GFX_SETUP_ARGS; i++)
13183 struct TokenIntPtrInfo menu_config[] =
13185 { "menu.draw_xoffset.SETUP", &menu.draw_xoffset_setup[i] },
13186 { "menu.draw_yoffset.SETUP", &menu.draw_yoffset_setup[i] }
13189 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
13191 char *token = menu_config[j].token;
13192 char *value = getHashEntry(setup_file_hash, token);
13195 *menu_config[j].value = get_integer_from_string(value);
13199 // special case: initialize with default values that may be overwritten
13200 // (eg, init "menu.line_spacing.INFO[XXX]" from "menu.line_spacing.INFO")
13201 for (i = 0; i < NUM_SPECIAL_GFX_INFO_ARGS; i++)
13203 struct TokenIntPtrInfo menu_config[] =
13205 { "menu.left_spacing.INFO", &menu.left_spacing_info[i] },
13206 { "menu.middle_spacing.INFO", &menu.middle_spacing_info[i] },
13207 { "menu.right_spacing.INFO", &menu.right_spacing_info[i] },
13208 { "menu.top_spacing.INFO", &menu.top_spacing_info[i] },
13209 { "menu.bottom_spacing.INFO", &menu.bottom_spacing_info[i] },
13210 { "menu.paragraph_spacing.INFO", &menu.paragraph_spacing_info[i] },
13211 { "menu.headline1_spacing.INFO", &menu.headline1_spacing_info[i] },
13212 { "menu.headline2_spacing.INFO", &menu.headline2_spacing_info[i] },
13213 { "menu.line_spacing.INFO", &menu.line_spacing_info[i] },
13214 { "menu.extra_spacing.INFO", &menu.extra_spacing_info[i] },
13217 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
13219 char *token = menu_config[j].token;
13220 char *value = getHashEntry(setup_file_hash, token);
13223 *menu_config[j].value = get_integer_from_string(value);
13227 // special case: initialize with default values that may be overwritten
13228 // (eg, init "menu.enter_screen.SCORES.xyz" from "menu.enter_screen.xyz")
13229 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
13231 struct TokenIntPtrInfo menu_config[] =
13233 { "menu.enter_screen.fade_mode", &menu.enter_screen[i].fade_mode },
13234 { "menu.enter_screen.fade_delay", &menu.enter_screen[i].fade_delay },
13235 { "menu.enter_screen.post_delay", &menu.enter_screen[i].post_delay },
13236 { "menu.leave_screen.fade_mode", &menu.leave_screen[i].fade_mode },
13237 { "menu.leave_screen.fade_delay", &menu.leave_screen[i].fade_delay },
13238 { "menu.leave_screen.post_delay", &menu.leave_screen[i].post_delay },
13239 { "menu.next_screen.fade_mode", &menu.next_screen[i].fade_mode },
13240 { "menu.next_screen.fade_delay", &menu.next_screen[i].fade_delay },
13241 { "menu.next_screen.post_delay", &menu.next_screen[i].post_delay }
13244 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
13246 char *token = menu_config[j].token;
13247 char *value = getHashEntry(setup_file_hash, token);
13250 *menu_config[j].value = get_token_parameter_value(token, value);
13254 // special case: initialize with default values that may be overwritten
13255 // (eg, init "viewport.door_1.MAIN.xyz" from "viewport.door_1.xyz")
13256 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
13260 char *token_prefix;
13261 struct RectWithBorder *struct_ptr;
13265 { "viewport.window", &viewport.window[i] },
13266 { "viewport.playfield", &viewport.playfield[i] },
13267 { "viewport.door_1", &viewport.door_1[i] },
13268 { "viewport.door_2", &viewport.door_2[i] }
13271 for (j = 0; j < ARRAY_SIZE(vp_struct); j++)
13273 struct TokenIntPtrInfo vp_config[] =
13275 { ".x", &vp_struct[j].struct_ptr->x },
13276 { ".y", &vp_struct[j].struct_ptr->y },
13277 { ".width", &vp_struct[j].struct_ptr->width },
13278 { ".height", &vp_struct[j].struct_ptr->height },
13279 { ".min_width", &vp_struct[j].struct_ptr->min_width },
13280 { ".min_height", &vp_struct[j].struct_ptr->min_height },
13281 { ".max_width", &vp_struct[j].struct_ptr->max_width },
13282 { ".max_height", &vp_struct[j].struct_ptr->max_height },
13283 { ".margin_left", &vp_struct[j].struct_ptr->margin_left },
13284 { ".margin_right", &vp_struct[j].struct_ptr->margin_right },
13285 { ".margin_top", &vp_struct[j].struct_ptr->margin_top },
13286 { ".margin_bottom", &vp_struct[j].struct_ptr->margin_bottom },
13287 { ".border_left", &vp_struct[j].struct_ptr->border_left },
13288 { ".border_right", &vp_struct[j].struct_ptr->border_right },
13289 { ".border_top", &vp_struct[j].struct_ptr->border_top },
13290 { ".border_bottom", &vp_struct[j].struct_ptr->border_bottom },
13291 { ".border_size", &vp_struct[j].struct_ptr->border_size },
13292 { ".align_size", &vp_struct[j].struct_ptr->align_size },
13293 { ".align", &vp_struct[j].struct_ptr->align },
13294 { ".valign", &vp_struct[j].struct_ptr->valign }
13297 for (k = 0; k < ARRAY_SIZE(vp_config); k++)
13299 char *token = getStringCat2(vp_struct[j].token_prefix,
13300 vp_config[k].token);
13301 char *value = getHashEntry(setup_file_hash, token);
13304 *vp_config[k].value = get_token_parameter_value(token, value);
13311 // special case: initialize with default values that may be overwritten
13312 // (e.g., init "[title].fade_mode" from "menu.next_screen.TITLE.fade_mode")
13313 for (i = 0; title_info[i].info != NULL; i++)
13315 struct TitleFadingInfo *info = title_info[i].info;
13316 char *base_token = title_info[i].text;
13318 for (j = 0; title_tokens[j].type != -1; j++)
13320 char *token = getStringCat2(base_token, title_tokens[j].text);
13321 char *value = getHashEntry(setup_file_hash, token);
13325 int parameter_value = get_token_parameter_value(token, value);
13329 *(int *)title_tokens[j].value = (int)parameter_value;
13338 // special case: initialize with default values that may be overwritten
13339 // (e.g., init "titlemessage_1.fade_mode" from "[titlemessage].fade_mode")
13340 for (i = 0; titlemessage_arrays[i].array != NULL; i++)
13342 struct TitleMessageInfo *array = titlemessage_arrays[i].array;
13343 char *base_token = titlemessage_arrays[i].text;
13345 for (j = 0; titlemessage_tokens[j].type != -1; j++)
13347 char *token = getStringCat2(base_token, titlemessage_tokens[j].text);
13348 char *value = getHashEntry(setup_file_hash, token);
13352 int parameter_value = get_token_parameter_value(token, value);
13354 for (k = 0; k < MAX_NUM_TITLE_MESSAGES; k++)
13358 if (titlemessage_tokens[j].type == TYPE_INTEGER)
13359 *(int *)titlemessage_tokens[j].value = (int)parameter_value;
13361 *(boolean *)titlemessage_tokens[j].value = (boolean)parameter_value;
13371 // read (and overwrite with) values that may be specified in config file
13372 InitMenuDesignSettings_FromHash(setup_file_hash, TRUE);
13374 // special case: check if network and preview player positions are redefined
13375 InitMenuDesignSettings_PreviewPlayers_FromHash(setup_file_hash);
13377 freeSetupFileHash(setup_file_hash);
13380 void LoadMenuDesignSettings(void)
13382 char *filename_base = UNDEFINED_FILENAME, *filename_local;
13384 InitMenuDesignSettings_Static();
13385 InitMenuDesignSettings_SpecialPreProcessing();
13386 InitMenuDesignSettings_PreviewPlayers();
13388 if (!GFX_OVERRIDE_ARTWORK(ARTWORK_TYPE_GRAPHICS))
13390 // first look for special settings configured in level series config
13391 filename_base = getCustomArtworkLevelConfigFilename(ARTWORK_TYPE_GRAPHICS);
13393 if (fileExists(filename_base))
13394 LoadMenuDesignSettingsFromFilename(filename_base);
13397 filename_local = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
13399 if (filename_local != NULL && !strEqual(filename_base, filename_local))
13400 LoadMenuDesignSettingsFromFilename(filename_local);
13402 InitMenuDesignSettings_SpecialPostProcessing();
13405 void LoadMenuDesignSettings_AfterGraphics(void)
13407 InitMenuDesignSettings_SpecialPostProcessing_AfterGraphics();
13410 void InitSoundSettings_FromHash(SetupFileHash *setup_file_hash,
13411 boolean ignore_defaults)
13415 for (i = 0; sound_config_vars[i].token != NULL; i++)
13417 char *value = getHashEntry(setup_file_hash, sound_config_vars[i].token);
13419 // (ignore definitions set to "[DEFAULT]" which are already initialized)
13420 if (ignore_defaults && strEqual(value, ARG_DEFAULT))
13424 *sound_config_vars[i].value =
13425 get_token_parameter_value(sound_config_vars[i].token, value);
13429 void InitSoundSettings_Static(void)
13431 // always start with reliable default values from static default config
13432 InitSoundSettings_FromHash(sound_config_hash, FALSE);
13435 static void LoadSoundSettingsFromFilename(char *filename)
13437 SetupFileHash *setup_file_hash;
13439 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
13442 // read (and overwrite with) values that may be specified in config file
13443 InitSoundSettings_FromHash(setup_file_hash, TRUE);
13445 freeSetupFileHash(setup_file_hash);
13448 void LoadSoundSettings(void)
13450 char *filename_base = UNDEFINED_FILENAME, *filename_local;
13452 InitSoundSettings_Static();
13454 if (!GFX_OVERRIDE_ARTWORK(ARTWORK_TYPE_SOUNDS))
13456 // first look for special settings configured in level series config
13457 filename_base = getCustomArtworkLevelConfigFilename(ARTWORK_TYPE_SOUNDS);
13459 if (fileExists(filename_base))
13460 LoadSoundSettingsFromFilename(filename_base);
13463 filename_local = getCustomArtworkConfigFilename(ARTWORK_TYPE_SOUNDS);
13465 if (filename_local != NULL && !strEqual(filename_base, filename_local))
13466 LoadSoundSettingsFromFilename(filename_local);
13469 void LoadUserDefinedEditorElementList(int **elements, int *num_elements)
13471 char *filename = getEditorSetupFilename();
13472 SetupFileList *setup_file_list, *list;
13473 SetupFileHash *element_hash;
13474 int num_unknown_tokens = 0;
13477 if ((setup_file_list = loadSetupFileList(filename)) == NULL)
13480 element_hash = newSetupFileHash();
13482 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
13483 setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
13485 // determined size may be larger than needed (due to unknown elements)
13487 for (list = setup_file_list; list != NULL; list = list->next)
13490 // add space for up to 3 more elements for padding that may be needed
13491 *num_elements += 3;
13493 // free memory for old list of elements, if needed
13494 checked_free(*elements);
13496 // allocate memory for new list of elements
13497 *elements = checked_malloc(*num_elements * sizeof(int));
13500 for (list = setup_file_list; list != NULL; list = list->next)
13502 char *value = getHashEntry(element_hash, list->token);
13504 if (value == NULL) // try to find obsolete token mapping
13506 char *mapped_token = get_mapped_token(list->token);
13508 if (mapped_token != NULL)
13510 value = getHashEntry(element_hash, mapped_token);
13512 free(mapped_token);
13518 (*elements)[(*num_elements)++] = atoi(value);
13522 if (num_unknown_tokens == 0)
13525 Warn("unknown token(s) found in config file:");
13526 Warn("- config file: '%s'", filename);
13528 num_unknown_tokens++;
13531 Warn("- token: '%s'", list->token);
13535 if (num_unknown_tokens > 0)
13538 while (*num_elements % 4) // pad with empty elements, if needed
13539 (*elements)[(*num_elements)++] = EL_EMPTY;
13541 freeSetupFileList(setup_file_list);
13542 freeSetupFileHash(element_hash);
13545 for (i = 0; i < *num_elements; i++)
13546 Debug("editor", "element '%s' [%d]\n",
13547 element_info[(*elements)[i]].token_name, (*elements)[i]);
13551 static struct MusicFileInfo *get_music_file_info_ext(char *basename, int music,
13554 SetupFileHash *setup_file_hash = NULL;
13555 struct MusicFileInfo tmp_music_file_info, *new_music_file_info;
13556 char *filename_music, *filename_prefix, *filename_info;
13562 token_to_value_ptr[] =
13564 { "title_header", &tmp_music_file_info.title_header },
13565 { "artist_header", &tmp_music_file_info.artist_header },
13566 { "album_header", &tmp_music_file_info.album_header },
13567 { "year_header", &tmp_music_file_info.year_header },
13568 { "played_header", &tmp_music_file_info.played_header },
13570 { "title", &tmp_music_file_info.title },
13571 { "artist", &tmp_music_file_info.artist },
13572 { "album", &tmp_music_file_info.album },
13573 { "year", &tmp_music_file_info.year },
13574 { "played", &tmp_music_file_info.played },
13580 filename_music = (is_sound ? getCustomSoundFilename(basename) :
13581 getCustomMusicFilename(basename));
13583 if (filename_music == NULL)
13586 // ---------- try to replace file extension ----------
13588 filename_prefix = getStringCopy(filename_music);
13589 if (strrchr(filename_prefix, '.') != NULL)
13590 *strrchr(filename_prefix, '.') = '\0';
13591 filename_info = getStringCat2(filename_prefix, ".txt");
13593 if (fileExists(filename_info))
13594 setup_file_hash = loadSetupFileHash(filename_info);
13596 free(filename_prefix);
13597 free(filename_info);
13599 if (setup_file_hash == NULL)
13601 // ---------- try to add file extension ----------
13603 filename_prefix = getStringCopy(filename_music);
13604 filename_info = getStringCat2(filename_prefix, ".txt");
13606 if (fileExists(filename_info))
13607 setup_file_hash = loadSetupFileHash(filename_info);
13609 free(filename_prefix);
13610 free(filename_info);
13613 if (setup_file_hash == NULL)
13616 // ---------- music file info found ----------
13618 clear_mem(&tmp_music_file_info, sizeof(struct MusicFileInfo));
13620 for (i = 0; token_to_value_ptr[i].token != NULL; i++)
13622 char *value = getHashEntry(setup_file_hash, token_to_value_ptr[i].token);
13624 *token_to_value_ptr[i].value_ptr =
13625 getStringCopy(value != NULL && *value != '\0' ? value : UNKNOWN_NAME);
13628 tmp_music_file_info.basename = getStringCopy(basename);
13629 tmp_music_file_info.music = music;
13630 tmp_music_file_info.is_sound = is_sound;
13632 new_music_file_info = checked_malloc(sizeof(struct MusicFileInfo));
13633 *new_music_file_info = tmp_music_file_info;
13635 return new_music_file_info;
13638 static struct MusicFileInfo *get_music_file_info(char *basename, int music)
13640 return get_music_file_info_ext(basename, music, FALSE);
13643 static struct MusicFileInfo *get_sound_file_info(char *basename, int sound)
13645 return get_music_file_info_ext(basename, sound, TRUE);
13648 static boolean music_info_listed_ext(struct MusicFileInfo *list,
13649 char *basename, boolean is_sound)
13651 for (; list != NULL; list = list->next)
13652 if (list->is_sound == is_sound && strEqual(list->basename, basename))
13658 static boolean music_info_listed(struct MusicFileInfo *list, char *basename)
13660 return music_info_listed_ext(list, basename, FALSE);
13663 static boolean sound_info_listed(struct MusicFileInfo *list, char *basename)
13665 return music_info_listed_ext(list, basename, TRUE);
13668 void LoadMusicInfo(void)
13670 int num_music_noconf = getMusicListSize_NoConf();
13671 int num_music = getMusicListSize();
13672 int num_sounds = getSoundListSize();
13673 struct FileInfo *music, *sound;
13674 struct MusicFileInfo *next, **new;
13678 while (music_file_info != NULL)
13680 next = music_file_info->next;
13682 checked_free(music_file_info->basename);
13684 checked_free(music_file_info->title_header);
13685 checked_free(music_file_info->artist_header);
13686 checked_free(music_file_info->album_header);
13687 checked_free(music_file_info->year_header);
13688 checked_free(music_file_info->played_header);
13690 checked_free(music_file_info->title);
13691 checked_free(music_file_info->artist);
13692 checked_free(music_file_info->album);
13693 checked_free(music_file_info->year);
13694 checked_free(music_file_info->played);
13696 free(music_file_info);
13698 music_file_info = next;
13701 new = &music_file_info;
13703 // get (configured or unconfigured) music file info for all levels
13704 for (i = leveldir_current->first_level;
13705 i <= leveldir_current->last_level; i++)
13709 if (levelset.music[i] != MUS_UNDEFINED)
13711 // get music file info for configured level music
13712 music_nr = levelset.music[i];
13714 else if (num_music_noconf > 0)
13716 // get music file info for unconfigured level music
13717 int level_pos = i - leveldir_current->first_level;
13719 music_nr = MAP_NOCONF_MUSIC(level_pos % num_music_noconf);
13726 char *basename = getMusicInfoEntryFilename(music_nr);
13728 if (basename == NULL)
13731 if (!music_info_listed(music_file_info, basename))
13733 *new = get_music_file_info(basename, music_nr);
13736 new = &(*new)->next;
13740 // get music file info for all remaining configured music files
13741 for (i = 0; i < num_music; i++)
13743 music = getMusicListEntry(i);
13745 if (music->filename == NULL)
13748 if (strEqual(music->filename, UNDEFINED_FILENAME))
13751 // a configured file may be not recognized as music
13752 if (!FileIsMusic(music->filename))
13755 if (!music_info_listed(music_file_info, music->filename))
13757 *new = get_music_file_info(music->filename, i);
13760 new = &(*new)->next;
13764 // get sound file info for all configured sound files
13765 for (i = 0; i < num_sounds; i++)
13767 sound = getSoundListEntry(i);
13769 if (sound->filename == NULL)
13772 if (strEqual(sound->filename, UNDEFINED_FILENAME))
13775 // a configured file may be not recognized as sound
13776 if (!FileIsSound(sound->filename))
13779 if (!sound_info_listed(music_file_info, sound->filename))
13781 *new = get_sound_file_info(sound->filename, i);
13783 new = &(*new)->next;
13787 // add pointers to previous list nodes
13789 struct MusicFileInfo *node = music_file_info;
13791 while (node != NULL)
13794 node->next->prev = node;
13800 static void add_helpanim_entry(int element, int action, int direction,
13801 int delay, int *num_list_entries)
13803 struct HelpAnimInfo *new_list_entry;
13804 (*num_list_entries)++;
13807 checked_realloc(helpanim_info,
13808 *num_list_entries * sizeof(struct HelpAnimInfo));
13809 new_list_entry = &helpanim_info[*num_list_entries - 1];
13811 new_list_entry->element = element;
13812 new_list_entry->action = action;
13813 new_list_entry->direction = direction;
13814 new_list_entry->delay = delay;
13817 static void print_unknown_token(char *filename, char *token, int token_nr)
13822 Warn("unknown token(s) found in config file:");
13823 Warn("- config file: '%s'", filename);
13826 Warn("- token: '%s'", token);
13829 static void print_unknown_token_end(int token_nr)
13835 void LoadHelpAnimInfo(void)
13837 char *filename = getHelpAnimFilename();
13838 SetupFileList *setup_file_list = NULL, *list;
13839 SetupFileHash *element_hash, *action_hash, *direction_hash;
13840 int num_list_entries = 0;
13841 int num_unknown_tokens = 0;
13844 if (fileExists(filename))
13845 setup_file_list = loadSetupFileList(filename);
13847 if (setup_file_list == NULL)
13849 // use reliable default values from static configuration
13850 SetupFileList *insert_ptr;
13852 insert_ptr = setup_file_list =
13853 newSetupFileList(helpanim_config[0].token,
13854 helpanim_config[0].value);
13856 for (i = 1; helpanim_config[i].token; i++)
13857 insert_ptr = addListEntry(insert_ptr,
13858 helpanim_config[i].token,
13859 helpanim_config[i].value);
13862 element_hash = newSetupFileHash();
13863 action_hash = newSetupFileHash();
13864 direction_hash = newSetupFileHash();
13866 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
13867 setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
13869 for (i = 0; i < NUM_ACTIONS; i++)
13870 setHashEntry(action_hash, element_action_info[i].suffix,
13871 i_to_a(element_action_info[i].value));
13873 // do not store direction index (bit) here, but direction value!
13874 for (i = 0; i < NUM_DIRECTIONS_FULL; i++)
13875 setHashEntry(direction_hash, element_direction_info[i].suffix,
13876 i_to_a(1 << element_direction_info[i].value));
13878 for (list = setup_file_list; list != NULL; list = list->next)
13880 char *element_token, *action_token, *direction_token;
13881 char *element_value, *action_value, *direction_value;
13882 int delay = atoi(list->value);
13884 if (strEqual(list->token, "end"))
13886 add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
13891 /* first try to break element into element/action/direction parts;
13892 if this does not work, also accept combined "element[.act][.dir]"
13893 elements (like "dynamite.active"), which are unique elements */
13895 if (strchr(list->token, '.') == NULL) // token contains no '.'
13897 element_value = getHashEntry(element_hash, list->token);
13898 if (element_value != NULL) // element found
13899 add_helpanim_entry(atoi(element_value), -1, -1, delay,
13900 &num_list_entries);
13903 // no further suffixes found -- this is not an element
13904 print_unknown_token(filename, list->token, num_unknown_tokens++);
13910 // token has format "<prefix>.<something>"
13912 action_token = strchr(list->token, '.'); // suffix may be action ...
13913 direction_token = action_token; // ... or direction
13915 element_token = getStringCopy(list->token);
13916 *strchr(element_token, '.') = '\0';
13918 element_value = getHashEntry(element_hash, element_token);
13920 if (element_value == NULL) // this is no element
13922 element_value = getHashEntry(element_hash, list->token);
13923 if (element_value != NULL) // combined element found
13924 add_helpanim_entry(atoi(element_value), -1, -1, delay,
13925 &num_list_entries);
13927 print_unknown_token(filename, list->token, num_unknown_tokens++);
13929 free(element_token);
13934 action_value = getHashEntry(action_hash, action_token);
13936 if (action_value != NULL) // action found
13938 add_helpanim_entry(atoi(element_value), atoi(action_value), -1, delay,
13939 &num_list_entries);
13941 free(element_token);
13946 direction_value = getHashEntry(direction_hash, direction_token);
13948 if (direction_value != NULL) // direction found
13950 add_helpanim_entry(atoi(element_value), -1, atoi(direction_value), delay,
13951 &num_list_entries);
13953 free(element_token);
13958 if (strchr(action_token + 1, '.') == NULL)
13960 // no further suffixes found -- this is not an action nor direction
13962 element_value = getHashEntry(element_hash, list->token);
13963 if (element_value != NULL) // combined element found
13964 add_helpanim_entry(atoi(element_value), -1, -1, delay,
13965 &num_list_entries);
13967 print_unknown_token(filename, list->token, num_unknown_tokens++);
13969 free(element_token);
13974 // token has format "<prefix>.<suffix>.<something>"
13976 direction_token = strchr(action_token + 1, '.');
13978 action_token = getStringCopy(action_token);
13979 *strchr(action_token + 1, '.') = '\0';
13981 action_value = getHashEntry(action_hash, action_token);
13983 if (action_value == NULL) // this is no action
13985 element_value = getHashEntry(element_hash, list->token);
13986 if (element_value != NULL) // combined element found
13987 add_helpanim_entry(atoi(element_value), -1, -1, delay,
13988 &num_list_entries);
13990 print_unknown_token(filename, list->token, num_unknown_tokens++);
13992 free(element_token);
13993 free(action_token);
13998 direction_value = getHashEntry(direction_hash, direction_token);
14000 if (direction_value != NULL) // direction found
14002 add_helpanim_entry(atoi(element_value), atoi(action_value),
14003 atoi(direction_value), delay, &num_list_entries);
14005 free(element_token);
14006 free(action_token);
14011 // this is no direction
14013 element_value = getHashEntry(element_hash, list->token);
14014 if (element_value != NULL) // combined element found
14015 add_helpanim_entry(atoi(element_value), -1, -1, delay,
14016 &num_list_entries);
14018 print_unknown_token(filename, list->token, num_unknown_tokens++);
14020 free(element_token);
14021 free(action_token);
14024 print_unknown_token_end(num_unknown_tokens);
14026 add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
14027 add_helpanim_entry(HELPANIM_LIST_END, -1, -1, -1, &num_list_entries);
14029 freeSetupFileList(setup_file_list);
14030 freeSetupFileHash(element_hash);
14031 freeSetupFileHash(action_hash);
14032 freeSetupFileHash(direction_hash);
14035 for (i = 0; i < num_list_entries; i++)
14036 Debug("files:LoadHelpAnimInfo", "'%s': %d, %d, %d => %d",
14037 EL_NAME(helpanim_info[i].element),
14038 helpanim_info[i].element,
14039 helpanim_info[i].action,
14040 helpanim_info[i].direction,
14041 helpanim_info[i].delay);
14045 void LoadHelpTextInfo(void)
14047 char *filename = getHelpTextFilename();
14050 if (helptext_info != NULL)
14052 freeSetupFileHash(helptext_info);
14053 helptext_info = NULL;
14056 if (fileExists(filename))
14057 helptext_info = loadSetupFileHash(filename);
14059 if (helptext_info == NULL)
14061 // use reliable default values from static configuration
14062 helptext_info = newSetupFileHash();
14064 for (i = 0; helptext_config[i].token; i++)
14065 setHashEntry(helptext_info,
14066 helptext_config[i].token,
14067 helptext_config[i].value);
14071 BEGIN_HASH_ITERATION(helptext_info, itr)
14073 Debug("files:LoadHelpTextInfo", "'%s' => '%s'",
14074 HASH_ITERATION_TOKEN(itr), HASH_ITERATION_VALUE(itr));
14076 END_HASH_ITERATION(hash, itr)
14081 // ----------------------------------------------------------------------------
14083 // ----------------------------------------------------------------------------
14085 #define MAX_NUM_CONVERT_LEVELS 1000
14087 void ConvertLevels(void)
14089 static LevelDirTree *convert_leveldir = NULL;
14090 static int convert_level_nr = -1;
14091 static int num_levels_handled = 0;
14092 static int num_levels_converted = 0;
14093 static boolean levels_failed[MAX_NUM_CONVERT_LEVELS];
14096 convert_leveldir = getTreeInfoFromIdentifier(leveldir_first,
14097 global.convert_leveldir);
14099 if (convert_leveldir == NULL)
14100 Fail("no such level identifier: '%s'", global.convert_leveldir);
14102 leveldir_current = convert_leveldir;
14104 if (global.convert_level_nr != -1)
14106 convert_leveldir->first_level = global.convert_level_nr;
14107 convert_leveldir->last_level = global.convert_level_nr;
14110 convert_level_nr = convert_leveldir->first_level;
14112 PrintLine("=", 79);
14113 Print("Converting levels\n");
14114 PrintLine("-", 79);
14115 Print("Level series identifier: '%s'\n", convert_leveldir->identifier);
14116 Print("Level series name: '%s'\n", convert_leveldir->name);
14117 Print("Level series author: '%s'\n", convert_leveldir->author);
14118 Print("Number of levels: %d\n", convert_leveldir->levels);
14119 PrintLine("=", 79);
14122 for (i = 0; i < MAX_NUM_CONVERT_LEVELS; i++)
14123 levels_failed[i] = FALSE;
14125 while (convert_level_nr <= convert_leveldir->last_level)
14127 char *level_filename;
14130 level_nr = convert_level_nr++;
14132 Print("Level %03d: ", level_nr);
14134 LoadLevel(level_nr);
14135 if (level.no_level_file || level.no_valid_file)
14137 Print("(no level)\n");
14141 Print("converting level ... ");
14144 // special case: conversion of some EMC levels as requested by ACME
14145 level.game_engine_type = GAME_ENGINE_TYPE_RND;
14148 level_filename = getDefaultLevelFilename(level_nr);
14149 new_level = !fileExists(level_filename);
14153 SaveLevel(level_nr);
14155 num_levels_converted++;
14157 Print("converted.\n");
14161 if (level_nr >= 0 && level_nr < MAX_NUM_CONVERT_LEVELS)
14162 levels_failed[level_nr] = TRUE;
14164 Print("NOT CONVERTED -- LEVEL ALREADY EXISTS.\n");
14167 num_levels_handled++;
14171 PrintLine("=", 79);
14172 Print("Number of levels handled: %d\n", num_levels_handled);
14173 Print("Number of levels converted: %d (%d%%)\n", num_levels_converted,
14174 (num_levels_handled ?
14175 num_levels_converted * 100 / num_levels_handled : 0));
14176 PrintLine("-", 79);
14177 Print("Summary (for automatic parsing by scripts):\n");
14178 Print("LEVELDIR '%s', CONVERTED %d/%d (%d%%)",
14179 convert_leveldir->identifier, num_levels_converted,
14180 num_levels_handled,
14181 (num_levels_handled ?
14182 num_levels_converted * 100 / num_levels_handled : 0));
14184 if (num_levels_handled != num_levels_converted)
14186 Print(", FAILED:");
14187 for (i = 0; i < MAX_NUM_CONVERT_LEVELS; i++)
14188 if (levels_failed[i])
14193 PrintLine("=", 79);
14195 CloseAllAndExit(0);
14199 // ----------------------------------------------------------------------------
14200 // create and save images for use in level sketches (raw BMP format)
14201 // ----------------------------------------------------------------------------
14203 void CreateLevelSketchImages(void)
14209 InitElementPropertiesGfxElement();
14211 bitmap1 = CreateBitmap(TILEX, TILEY, DEFAULT_DEPTH);
14212 bitmap2 = CreateBitmap(MINI_TILEX, MINI_TILEY, DEFAULT_DEPTH);
14214 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
14216 int element = getMappedElement(i);
14217 char basename1[16];
14218 char basename2[16];
14222 sprintf(basename1, "%04d.bmp", i);
14223 sprintf(basename2, "%04ds.bmp", i);
14225 filename1 = getPath2(global.create_sketch_images_dir, basename1);
14226 filename2 = getPath2(global.create_sketch_images_dir, basename2);
14228 DrawSizedElement(0, 0, element, TILESIZE);
14229 BlitBitmap(drawto, bitmap1, SX, SY, TILEX, TILEY, 0, 0);
14231 if (SDL_SaveBMP(bitmap1->surface, filename1) != 0)
14232 Fail("cannot save level sketch image file '%s'", filename1);
14234 DrawSizedElement(0, 0, element, MINI_TILESIZE);
14235 BlitBitmap(drawto, bitmap2, SX, SY, MINI_TILEX, MINI_TILEY, 0, 0);
14237 if (SDL_SaveBMP(bitmap2->surface, filename2) != 0)
14238 Fail("cannot save level sketch image file '%s'", filename2);
14243 // create corresponding SQL statements (for normal and small images)
14246 printf("insert into phpbb_words values (NULL, '`%03d', '<IMG class=\"levelsketch\" src=\"/I/%04d.png\"/>');\n", i, i);
14247 printf("insert into phpbb_words values (NULL, '¸%03d', '<IMG class=\"levelsketch\" src=\"/I/%04ds.png\"/>');\n", i, i);
14250 printf("insert into phpbb_words values (NULL, '`%04d', '<IMG class=\"levelsketch\" src=\"/I/%04d.png\"/>');\n", i, i);
14251 printf("insert into phpbb_words values (NULL, '¸%04d', '<IMG class=\"levelsketch\" src=\"/I/%04ds.png\"/>');\n", i, i);
14253 // optional: create content for forum level sketch demonstration post
14255 fprintf(stderr, "%03d `%03d%c", i, i, (i % 10 < 9 ? ' ' : '\n'));
14258 FreeBitmap(bitmap1);
14259 FreeBitmap(bitmap2);
14262 fprintf(stderr, "\n");
14264 Info("%d normal and small images created", NUM_FILE_ELEMENTS);
14266 CloseAllAndExit(0);
14270 // ----------------------------------------------------------------------------
14271 // create and save images for element collecting animations (raw BMP format)
14272 // ----------------------------------------------------------------------------
14274 static boolean createCollectImage(int element)
14276 return (IS_COLLECTIBLE(element) && !IS_SP_ELEMENT(element));
14279 void CreateCollectElementImages(void)
14283 int anim_frames = num_steps - 1;
14284 int tile_size = TILESIZE;
14285 int anim_width = tile_size * anim_frames;
14286 int anim_height = tile_size;
14287 int num_collect_images = 0;
14288 int pos_collect_images = 0;
14290 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
14291 if (createCollectImage(i))
14292 num_collect_images++;
14294 Info("Creating %d element collecting animation images ...",
14295 num_collect_images);
14297 int dst_width = anim_width * 2;
14298 int dst_height = anim_height * num_collect_images / 2;
14299 Bitmap *dst_bitmap = CreateBitmap(dst_width, dst_height, DEFAULT_DEPTH);
14300 char *basename_bmp = "RocksCollect.bmp";
14301 char *basename_png = "RocksCollect.png";
14302 char *filename_bmp = getPath2(global.create_collect_images_dir, basename_bmp);
14303 char *filename_png = getPath2(global.create_collect_images_dir, basename_png);
14304 int len_filename_bmp = strlen(filename_bmp);
14305 int len_filename_png = strlen(filename_png);
14306 int max_command_len = MAX_FILENAME_LEN + len_filename_bmp + len_filename_png;
14307 char cmd_convert[max_command_len];
14309 snprintf(cmd_convert, max_command_len, "convert \"%s\" \"%s\"",
14313 // force using RGBA surface for destination bitmap
14314 SDL_SetColorKey(dst_bitmap->surface, SET_TRANSPARENT_PIXEL,
14315 SDL_MapRGB(dst_bitmap->surface->format, 0x00, 0x00, 0x00));
14317 dst_bitmap->surface =
14318 SDL_ConvertSurfaceFormat(dst_bitmap->surface, SDL_PIXELFORMAT_ARGB8888, 0);
14320 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
14322 if (!createCollectImage(i))
14325 int dst_x = (pos_collect_images / (num_collect_images / 2)) * anim_width;
14326 int dst_y = (pos_collect_images % (num_collect_images / 2)) * anim_height;
14327 int graphic = el2img(i);
14328 char *token_name = element_info[i].token_name;
14329 Bitmap *tmp_bitmap = CreateBitmap(tile_size, tile_size, DEFAULT_DEPTH);
14330 Bitmap *src_bitmap;
14333 Info("- creating collecting image for '%s' ...", token_name);
14335 getGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
14337 BlitBitmap(src_bitmap, tmp_bitmap, src_x, src_y,
14338 tile_size, tile_size, 0, 0);
14340 // force using RGBA surface for temporary bitmap (using transparent black)
14341 SDL_SetColorKey(tmp_bitmap->surface, SET_TRANSPARENT_PIXEL,
14342 SDL_MapRGB(tmp_bitmap->surface->format, 0x00, 0x00, 0x00));
14344 tmp_bitmap->surface =
14345 SDL_ConvertSurfaceFormat(tmp_bitmap->surface, SDL_PIXELFORMAT_ARGB8888, 0);
14347 tmp_bitmap->surface_masked = tmp_bitmap->surface;
14349 for (j = 0; j < anim_frames; j++)
14351 int frame_size_final = tile_size * (anim_frames - j) / num_steps;
14352 int frame_size = frame_size_final * num_steps;
14353 int offset = (tile_size - frame_size_final) / 2;
14354 Bitmap *frame_bitmap = ZoomBitmap(tmp_bitmap, frame_size, frame_size);
14356 while (frame_size > frame_size_final)
14360 Bitmap *half_bitmap = ZoomBitmap(frame_bitmap, frame_size, frame_size);
14362 FreeBitmap(frame_bitmap);
14364 frame_bitmap = half_bitmap;
14367 BlitBitmapMasked(frame_bitmap, dst_bitmap, 0, 0,
14368 frame_size_final, frame_size_final,
14369 dst_x + j * tile_size + offset, dst_y + offset);
14371 FreeBitmap(frame_bitmap);
14374 tmp_bitmap->surface_masked = NULL;
14376 FreeBitmap(tmp_bitmap);
14378 pos_collect_images++;
14381 if (SDL_SaveBMP(dst_bitmap->surface, filename_bmp) != 0)
14382 Fail("cannot save element collecting image file '%s'", filename_bmp);
14384 FreeBitmap(dst_bitmap);
14386 Info("Converting image file from BMP to PNG ...");
14388 if (system(cmd_convert) != 0)
14389 Fail("converting image file failed");
14391 unlink(filename_bmp);
14395 CloseAllAndExit(0);
14399 // ----------------------------------------------------------------------------
14400 // create and save images for custom and group elements (raw BMP format)
14401 // ----------------------------------------------------------------------------
14403 void CreateCustomElementImages(char *directory)
14405 char *src_basename = "RocksCE-template.ilbm";
14406 char *dst_basename = "RocksCE.bmp";
14407 char *src_filename = getPath2(directory, src_basename);
14408 char *dst_filename = getPath2(directory, dst_basename);
14409 Bitmap *src_bitmap;
14411 int yoffset_ce = 0;
14412 int yoffset_ge = (TILEY * NUM_CUSTOM_ELEMENTS / 16);
14415 InitVideoDefaults();
14417 ReCreateBitmap(&backbuffer, video.width, video.height);
14419 src_bitmap = LoadImage(src_filename);
14421 bitmap = CreateBitmap(TILEX * 16 * 2,
14422 TILEY * (NUM_CUSTOM_ELEMENTS + NUM_GROUP_ELEMENTS) / 16,
14425 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
14432 BlitBitmap(src_bitmap, bitmap, 0, 0, TILEX, TILEY,
14433 TILEX * x, TILEY * y + yoffset_ce);
14435 BlitBitmap(src_bitmap, bitmap, 0, TILEY,
14437 TILEX * x + TILEX * 16,
14438 TILEY * y + yoffset_ce);
14440 for (j = 2; j >= 0; j--)
14444 BlitBitmap(src_bitmap, bitmap,
14445 TILEX + c * 7, 0, 6, 10,
14446 TILEX * x + 6 + j * 7,
14447 TILEY * y + 11 + yoffset_ce);
14449 BlitBitmap(src_bitmap, bitmap,
14450 TILEX + c * 8, TILEY, 6, 10,
14451 TILEX * 16 + TILEX * x + 6 + j * 8,
14452 TILEY * y + 10 + yoffset_ce);
14458 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
14465 BlitBitmap(src_bitmap, bitmap, 0, 0, TILEX, TILEY,
14466 TILEX * x, TILEY * y + yoffset_ge);
14468 BlitBitmap(src_bitmap, bitmap, 0, TILEY,
14470 TILEX * x + TILEX * 16,
14471 TILEY * y + yoffset_ge);
14473 for (j = 1; j >= 0; j--)
14477 BlitBitmap(src_bitmap, bitmap, TILEX + c * 10, 11, 10, 10,
14478 TILEX * x + 6 + j * 10,
14479 TILEY * y + 11 + yoffset_ge);
14481 BlitBitmap(src_bitmap, bitmap,
14482 TILEX + c * 8, TILEY + 12, 6, 10,
14483 TILEX * 16 + TILEX * x + 10 + j * 8,
14484 TILEY * y + 10 + yoffset_ge);
14490 if (SDL_SaveBMP(bitmap->surface, dst_filename) != 0)
14491 Fail("cannot save CE graphics file '%s'", dst_filename);
14493 FreeBitmap(bitmap);
14495 CloseAllAndExit(0);