1 // ============================================================================
2 // Rocks'n'Diamonds - McDuffin Strikes Back!
3 // ----------------------------------------------------------------------------
4 // (c) 1995-2014 by Artsoft Entertainment
7 // https://www.artsoft.org/
8 // ----------------------------------------------------------------------------
10 // ============================================================================
17 #include "libgame/libgame.h"
27 #define ENABLE_UNUSED_CODE 0 // currently unused functions
28 #define ENABLE_HISTORIC_CHUNKS 0 // only for historic reference
29 #define ENABLE_RESERVED_CODE 0 // reserved for later use
31 #define CHUNK_ID_LEN 4 // IFF style chunk id length
32 #define CHUNK_SIZE_UNDEFINED 0 // undefined chunk size == 0
33 #define CHUNK_SIZE_NONE -1 // do not write chunk size
35 #define LEVEL_CHUNK_NAME_SIZE MAX_LEVEL_NAME_LEN
36 #define LEVEL_CHUNK_AUTH_SIZE MAX_LEVEL_AUTHOR_LEN
38 #define LEVEL_CHUNK_VERS_SIZE 8 // size of file version chunk
39 #define LEVEL_CHUNK_DATE_SIZE 4 // size of file date chunk
40 #define LEVEL_CHUNK_HEAD_SIZE 80 // size of level file header
41 #define LEVEL_CHUNK_HEAD_UNUSED 0 // unused level header bytes
42 #define LEVEL_CHUNK_CNT2_SIZE 160 // size of level CNT2 chunk
43 #define LEVEL_CHUNK_CNT2_UNUSED 11 // unused CNT2 chunk bytes
44 #define LEVEL_CHUNK_CNT3_HEADER 16 // size of level CNT3 header
45 #define LEVEL_CHUNK_CNT3_UNUSED 10 // unused CNT3 chunk bytes
46 #define LEVEL_CPART_CUS3_SIZE 134 // size of CUS3 chunk part
47 #define LEVEL_CPART_CUS3_UNUSED 15 // unused CUS3 bytes / part
48 #define LEVEL_CHUNK_GRP1_SIZE 74 // size of level GRP1 chunk
50 // (element number, number of change pages, change page number)
51 #define LEVEL_CHUNK_CUSX_UNCHANGED (2 + (1 + 1) + (1 + 1))
53 // (element number only)
54 #define LEVEL_CHUNK_GRPX_UNCHANGED 2
55 #define LEVEL_CHUNK_EMPX_UNCHANGED 2
56 #define LEVEL_CHUNK_NOTE_UNCHANGED 2
58 // (nothing at all if unchanged)
59 #define LEVEL_CHUNK_ELEM_UNCHANGED 0
61 #define TAPE_CHUNK_VERS_SIZE 8 // size of file version chunk
62 #define TAPE_CHUNK_HEAD_SIZE 20 // size of tape file header
63 #define TAPE_CHUNK_SCRN_SIZE 2 // size of screen size chunk
65 #define SCORE_CHUNK_VERS_SIZE 8 // size of file version chunk
67 #define LEVEL_CHUNK_CNT3_SIZE(x) (LEVEL_CHUNK_CNT3_HEADER + (x))
68 #define LEVEL_CHUNK_CUS3_SIZE(x) (2 + (x) * LEVEL_CPART_CUS3_SIZE)
69 #define LEVEL_CHUNK_CUS4_SIZE(x) (96 + (x) * 48)
71 // file identifier strings
72 #define LEVEL_COOKIE_TMPL "ROCKSNDIAMONDS_LEVEL_FILE_VERSION_x.x"
73 #define TAPE_COOKIE_TMPL "ROCKSNDIAMONDS_TAPE_FILE_VERSION_x.x"
74 #define SCORE_COOKIE_TMPL "ROCKSNDIAMONDS_SCORE_FILE_VERSION_x.x"
76 // values for deciding when (not) to save configuration data
77 #define SAVE_CONF_NEVER 0
78 #define SAVE_CONF_ALWAYS 1
79 #define SAVE_CONF_WHEN_CHANGED -1
81 // values for chunks using micro chunks
82 #define CONF_MASK_1_BYTE 0x00
83 #define CONF_MASK_2_BYTE 0x40
84 #define CONF_MASK_4_BYTE 0x80
85 #define CONF_MASK_MULTI_BYTES 0xc0
87 #define CONF_MASK_BYTES 0xc0
88 #define CONF_MASK_TOKEN 0x3f
90 #define CONF_VALUE_1_BYTE(x) (CONF_MASK_1_BYTE | (x))
91 #define CONF_VALUE_2_BYTE(x) (CONF_MASK_2_BYTE | (x))
92 #define CONF_VALUE_4_BYTE(x) (CONF_MASK_4_BYTE | (x))
93 #define CONF_VALUE_MULTI_BYTES(x) (CONF_MASK_MULTI_BYTES | (x))
95 // these definitions are just for convenience of use and readability
96 #define CONF_VALUE_8_BIT(x) CONF_VALUE_1_BYTE(x)
97 #define CONF_VALUE_16_BIT(x) CONF_VALUE_2_BYTE(x)
98 #define CONF_VALUE_32_BIT(x) CONF_VALUE_4_BYTE(x)
99 #define CONF_VALUE_BYTES(x) CONF_VALUE_MULTI_BYTES(x)
101 #define CONF_VALUE_NUM_BYTES(x) ((x) == CONF_MASK_1_BYTE ? 1 : \
102 (x) == CONF_MASK_2_BYTE ? 2 : \
103 (x) == CONF_MASK_4_BYTE ? 4 : 0)
105 #define CONF_CONTENT_NUM_ELEMENTS (3 * 3)
106 #define CONF_CONTENT_NUM_BYTES (CONF_CONTENT_NUM_ELEMENTS * 2)
107 #define CONF_ELEMENT_NUM_BYTES (2)
109 #define CONF_ENTITY_NUM_BYTES(t) ((t) == TYPE_ELEMENT || \
110 (t) == TYPE_ELEMENT_LIST ? \
111 CONF_ELEMENT_NUM_BYTES : \
112 (t) == TYPE_CONTENT || \
113 (t) == TYPE_CONTENT_LIST ? \
114 CONF_CONTENT_NUM_BYTES : 1)
116 #define CONF_ELEMENT_BYTE_POS(i) ((i) * CONF_ELEMENT_NUM_BYTES)
117 #define CONF_ELEMENTS_ELEMENT(b, i) ((b[CONF_ELEMENT_BYTE_POS(i)] << 8) | \
118 (b[CONF_ELEMENT_BYTE_POS(i) + 1]))
120 #define CONF_CONTENT_ELEMENT_POS(c,x,y) ((c) * CONF_CONTENT_NUM_ELEMENTS + \
122 #define CONF_CONTENT_BYTE_POS(c,x,y) (CONF_CONTENT_ELEMENT_POS(c,x,y) * \
123 CONF_ELEMENT_NUM_BYTES)
124 #define CONF_CONTENTS_ELEMENT(b,c,x,y) ((b[CONF_CONTENT_BYTE_POS(c,x,y)]<< 8)|\
125 (b[CONF_CONTENT_BYTE_POS(c,x,y) + 1]))
127 // temporary variables used to store pointers to structure members
128 static struct LevelInfo li;
129 static struct ElementInfo xx_ei, yy_ei;
130 static struct ElementChangeInfo xx_change;
131 static struct ElementGroupInfo xx_group;
132 static struct EnvelopeInfo xx_envelope;
133 static unsigned int xx_event_bits[NUM_CE_BITFIELDS];
134 static char xx_default_description[MAX_ELEMENT_NAME_LEN + 1];
135 static int xx_num_contents;
136 static int xx_current_change_page;
137 static char xx_default_string_empty[1] = "";
138 static int xx_string_length_unused;
140 struct LevelFileConfigInfo
142 int element; // element for which data is to be stored
143 int save_type; // save data always, never or when changed
144 int data_type; // data type (used internally, not stored)
145 int conf_type; // micro chunk identifier (stored in file)
148 void *value; // variable that holds the data to be stored
149 int default_value; // initial default value for this variable
152 void *value_copy; // variable that holds the data to be copied
153 void *num_entities; // number of entities for multi-byte data
154 int default_num_entities; // default number of entities for this data
155 int max_num_entities; // maximal number of entities for this data
156 char *default_string; // optional default string for string data
159 static struct LevelFileConfigInfo chunk_config_INFO[] =
161 // ---------- values not related to single elements -------------------------
164 -1, SAVE_CONF_ALWAYS,
165 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
166 &li.game_engine_type, GAME_ENGINE_TYPE_RND
169 -1, SAVE_CONF_ALWAYS,
170 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
171 &li.fieldx, STD_LEV_FIELDX
174 -1, SAVE_CONF_ALWAYS,
175 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
176 &li.fieldy, STD_LEV_FIELDY
179 -1, SAVE_CONF_ALWAYS,
180 TYPE_INTEGER, CONF_VALUE_16_BIT(3),
184 -1, SAVE_CONF_ALWAYS,
185 TYPE_INTEGER, CONF_VALUE_16_BIT(4),
190 TYPE_INTEGER, CONF_VALUE_32_BIT(2),
195 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
196 &li.use_step_counter, FALSE
200 TYPE_BITFIELD, CONF_VALUE_8_BIT(4),
201 &li.wind_direction_initial, MV_NONE
205 TYPE_BOOLEAN, CONF_VALUE_8_BIT(5),
206 &li.em_slippery_gems, FALSE
210 TYPE_BOOLEAN, CONF_VALUE_8_BIT(6),
211 &li.use_custom_template, FALSE
215 TYPE_BITFIELD, CONF_VALUE_32_BIT(1),
216 &li.can_move_into_acid_bits, ~0 // default: everything can
220 TYPE_BITFIELD, CONF_VALUE_8_BIT(7),
221 &li.dont_collide_with_bits, ~0 // default: always deadly
225 TYPE_BOOLEAN, CONF_VALUE_8_BIT(8),
226 &li.em_explodes_by_fire, FALSE
230 TYPE_INTEGER, CONF_VALUE_16_BIT(5),
231 &li.score[SC_TIME_BONUS], 1
235 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
236 &li.auto_exit_sokoban, FALSE
240 TYPE_BOOLEAN, CONF_VALUE_8_BIT(10),
241 &li.auto_count_gems, FALSE
245 TYPE_BOOLEAN, CONF_VALUE_8_BIT(11),
246 &li.solved_by_one_player, FALSE
250 TYPE_INTEGER, CONF_VALUE_8_BIT(12),
251 &li.time_score_base, 1
255 TYPE_BOOLEAN, CONF_VALUE_8_BIT(13),
256 &li.rate_time_over_score, FALSE
260 TYPE_BOOLEAN, CONF_VALUE_8_BIT(14),
261 &li.bd_intermission, FALSE
265 TYPE_INTEGER, CONF_VALUE_8_BIT(15),
266 &li.bd_scheduling_type, GD_SCHEDULING_MILLISECONDS
270 TYPE_BOOLEAN, CONF_VALUE_8_BIT(16),
271 &li.bd_pal_timing, FALSE
275 TYPE_INTEGER, CONF_VALUE_16_BIT(6),
276 &li.bd_cycle_delay_ms, 200
280 TYPE_INTEGER, CONF_VALUE_8_BIT(17),
281 &li.bd_cycle_delay_c64, 0
285 TYPE_INTEGER, CONF_VALUE_8_BIT(18),
286 &li.bd_hatching_delay_cycles, 21
290 TYPE_INTEGER, CONF_VALUE_8_BIT(19),
291 &li.bd_hatching_delay_seconds, 2
295 TYPE_BOOLEAN, CONF_VALUE_8_BIT(20),
296 &li.bd_line_shifting_borders, FALSE
300 TYPE_BOOLEAN, CONF_VALUE_8_BIT(21),
301 &li.bd_scan_first_and_last_row, TRUE
305 TYPE_BOOLEAN, CONF_VALUE_8_BIT(22),
306 &li.bd_short_explosions, TRUE
310 TYPE_BOOLEAN, CONF_VALUE_8_BIT(23),
311 &li.bd_gravity_affects_all, TRUE
315 TYPE_INTEGER, CONF_VALUE_8_BIT(24),
316 &li.bd_cave_random_seed_c64, 0
326 static struct LevelFileConfigInfo chunk_config_ELEM[] =
328 // (these values are the same for each player)
331 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
332 &li.block_last_field, FALSE // default case for EM levels
336 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
337 &li.sp_block_last_field, TRUE // default case for SP levels
341 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
342 &li.instant_relocation, FALSE
346 TYPE_BOOLEAN, CONF_VALUE_8_BIT(4),
347 &li.can_pass_to_walkable, FALSE
351 TYPE_BOOLEAN, CONF_VALUE_8_BIT(5),
352 &li.block_snap_field, TRUE
356 TYPE_BOOLEAN, CONF_VALUE_8_BIT(6),
357 &li.continuous_snapping, TRUE
361 TYPE_BOOLEAN, CONF_VALUE_8_BIT(12),
362 &li.shifted_relocation, FALSE
366 TYPE_BOOLEAN, CONF_VALUE_8_BIT(15),
367 &li.lazy_relocation, FALSE
371 TYPE_BOOLEAN, CONF_VALUE_8_BIT(16),
372 &li.finish_dig_collect, TRUE
376 TYPE_BOOLEAN, CONF_VALUE_8_BIT(17),
377 &li.keep_walkable_ce, FALSE
380 // (these values are different for each player)
383 TYPE_INTEGER, CONF_VALUE_8_BIT(7),
384 &li.initial_player_stepsize[0], STEPSIZE_NORMAL
388 TYPE_BOOLEAN, CONF_VALUE_8_BIT(8),
389 &li.initial_player_gravity[0], FALSE
393 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
394 &li.use_start_element[0], FALSE
398 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
399 &li.start_element[0], EL_PLAYER_1
403 TYPE_BOOLEAN, CONF_VALUE_8_BIT(10),
404 &li.use_artwork_element[0], FALSE
408 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
409 &li.artwork_element[0], EL_PLAYER_1
413 TYPE_BOOLEAN, CONF_VALUE_8_BIT(11),
414 &li.use_explosion_element[0], FALSE
418 TYPE_ELEMENT, CONF_VALUE_16_BIT(3),
419 &li.explosion_element[0], EL_PLAYER_1
423 TYPE_BOOLEAN, CONF_VALUE_8_BIT(13),
424 &li.use_initial_inventory[0], FALSE
428 TYPE_BOOLEAN, CONF_VALUE_8_BIT(14),
429 &li.initial_inventory_size[0], 1
433 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(1),
434 &li.initial_inventory_content[0][0],EL_EMPTY, NULL,
435 &li.initial_inventory_size[0], 1, MAX_INITIAL_INVENTORY_SIZE
440 TYPE_INTEGER, CONF_VALUE_8_BIT(7),
441 &li.initial_player_stepsize[1], STEPSIZE_NORMAL
445 TYPE_BOOLEAN, CONF_VALUE_8_BIT(8),
446 &li.initial_player_gravity[1], FALSE
450 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
451 &li.use_start_element[1], FALSE
455 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
456 &li.start_element[1], EL_PLAYER_2
460 TYPE_BOOLEAN, CONF_VALUE_8_BIT(10),
461 &li.use_artwork_element[1], FALSE
465 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
466 &li.artwork_element[1], EL_PLAYER_2
470 TYPE_BOOLEAN, CONF_VALUE_8_BIT(11),
471 &li.use_explosion_element[1], FALSE
475 TYPE_ELEMENT, CONF_VALUE_16_BIT(3),
476 &li.explosion_element[1], EL_PLAYER_2
480 TYPE_BOOLEAN, CONF_VALUE_8_BIT(13),
481 &li.use_initial_inventory[1], FALSE
485 TYPE_BOOLEAN, CONF_VALUE_8_BIT(14),
486 &li.initial_inventory_size[1], 1
490 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(1),
491 &li.initial_inventory_content[1][0],EL_EMPTY, NULL,
492 &li.initial_inventory_size[1], 1, MAX_INITIAL_INVENTORY_SIZE
497 TYPE_INTEGER, CONF_VALUE_8_BIT(7),
498 &li.initial_player_stepsize[2], STEPSIZE_NORMAL
502 TYPE_BOOLEAN, CONF_VALUE_8_BIT(8),
503 &li.initial_player_gravity[2], FALSE
507 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
508 &li.use_start_element[2], FALSE
512 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
513 &li.start_element[2], EL_PLAYER_3
517 TYPE_BOOLEAN, CONF_VALUE_8_BIT(10),
518 &li.use_artwork_element[2], FALSE
522 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
523 &li.artwork_element[2], EL_PLAYER_3
527 TYPE_BOOLEAN, CONF_VALUE_8_BIT(11),
528 &li.use_explosion_element[2], FALSE
532 TYPE_ELEMENT, CONF_VALUE_16_BIT(3),
533 &li.explosion_element[2], EL_PLAYER_3
537 TYPE_BOOLEAN, CONF_VALUE_8_BIT(13),
538 &li.use_initial_inventory[2], FALSE
542 TYPE_BOOLEAN, CONF_VALUE_8_BIT(14),
543 &li.initial_inventory_size[2], 1
547 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(1),
548 &li.initial_inventory_content[2][0],EL_EMPTY, NULL,
549 &li.initial_inventory_size[2], 1, MAX_INITIAL_INVENTORY_SIZE
554 TYPE_INTEGER, CONF_VALUE_8_BIT(7),
555 &li.initial_player_stepsize[3], STEPSIZE_NORMAL
559 TYPE_BOOLEAN, CONF_VALUE_8_BIT(8),
560 &li.initial_player_gravity[3], FALSE
564 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
565 &li.use_start_element[3], FALSE
569 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
570 &li.start_element[3], EL_PLAYER_4
574 TYPE_BOOLEAN, CONF_VALUE_8_BIT(10),
575 &li.use_artwork_element[3], FALSE
579 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
580 &li.artwork_element[3], EL_PLAYER_4
584 TYPE_BOOLEAN, CONF_VALUE_8_BIT(11),
585 &li.use_explosion_element[3], FALSE
589 TYPE_ELEMENT, CONF_VALUE_16_BIT(3),
590 &li.explosion_element[3], EL_PLAYER_4
594 TYPE_BOOLEAN, CONF_VALUE_8_BIT(13),
595 &li.use_initial_inventory[3], FALSE
599 TYPE_BOOLEAN, CONF_VALUE_8_BIT(14),
600 &li.initial_inventory_size[3], 1
604 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(1),
605 &li.initial_inventory_content[3][0],EL_EMPTY, NULL,
606 &li.initial_inventory_size[3], 1, MAX_INITIAL_INVENTORY_SIZE
609 // (these values are only valid for BD style levels)
610 // (some values for BD style amoeba following below)
613 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
614 &li.bd_diagonal_movements, FALSE
618 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
619 &li.bd_topmost_player_active, TRUE
623 TYPE_INTEGER, CONF_VALUE_8_BIT(3),
624 &li.bd_pushing_prob, 25
628 TYPE_INTEGER, CONF_VALUE_8_BIT(4),
629 &li.bd_pushing_prob_with_sweet, 100
633 TYPE_BOOLEAN, CONF_VALUE_8_BIT(5),
634 &li.bd_push_mega_rock_with_sweet, FALSE
638 TYPE_INTEGER, CONF_VALUE_8_BIT(6),
639 &li.bd_snap_element, EL_EMPTY
644 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
645 &li.score[SC_DIAMOND_EXTRA], 20
649 EL_BD_MAGIC_WALL, -1,
650 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
651 &li.bd_magic_wall_wait_hatching, FALSE
654 EL_BD_MAGIC_WALL, -1,
655 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
656 &li.bd_magic_wall_stops_amoeba, TRUE
661 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
662 &li.bd_clock_extra_time, 30
666 EL_BD_VOODOO_DOLL, -1,
667 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
668 &li.bd_voodoo_collects_diamonds, FALSE
671 EL_BD_VOODOO_DOLL, -1,
672 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
673 &li.bd_voodoo_hurt_kills_player, FALSE
676 EL_BD_VOODOO_DOLL, -1,
677 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
678 &li.bd_voodoo_dies_by_rock, FALSE
681 EL_BD_VOODOO_DOLL, -1,
682 TYPE_BOOLEAN, CONF_VALUE_8_BIT(4),
683 &li.bd_voodoo_vanish_by_explosion, TRUE
686 EL_BD_VOODOO_DOLL, -1,
687 TYPE_INTEGER, CONF_VALUE_8_BIT(5),
688 &li.bd_voodoo_penalty_time, 30
693 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
694 &li.bd_slime_is_predictable, TRUE
698 TYPE_INTEGER, CONF_VALUE_8_BIT(2),
699 &li.bd_slime_permeability_rate, 100
703 TYPE_INTEGER, CONF_VALUE_8_BIT(3),
704 &li.bd_slime_permeability_bits_c64, 0
708 TYPE_INTEGER, CONF_VALUE_32_BIT(1),
709 &li.bd_slime_random_seed_c64, -1
712 // (the following values are related to various game elements)
716 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
717 &li.score[SC_EMERALD], 10
722 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
723 &li.score[SC_DIAMOND], 10
728 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
729 &li.score[SC_BUG], 10
734 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
735 &li.score[SC_SPACESHIP], 10
740 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
741 &li.score[SC_PACMAN], 10
746 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
747 &li.score[SC_NUT], 10
752 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
753 &li.score[SC_DYNAMITE], 10
758 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
759 &li.score[SC_KEY], 10
764 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
765 &li.score[SC_PEARL], 10
770 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
771 &li.score[SC_CRYSTAL], 10
774 // (amoeba values used by R'n'D game engine only)
777 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
778 &li.amoeba_content, EL_DIAMOND
782 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
787 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
788 &li.grow_into_diggable, TRUE
790 // (amoeba values used by BD game engine only)
793 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
794 &li.bd_amoeba_wait_for_hatching, FALSE
798 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
799 &li.bd_amoeba_start_immediately, TRUE
803 TYPE_BOOLEAN, CONF_VALUE_8_BIT(4),
804 &li.bd_amoeba_2_explode_by_amoeba, TRUE
808 TYPE_INTEGER, CONF_VALUE_16_BIT(3),
809 &li.bd_amoeba_threshold_too_big, 200
813 TYPE_INTEGER, CONF_VALUE_16_BIT(4),
814 &li.bd_amoeba_slow_growth_time, 200
818 TYPE_INTEGER, CONF_VALUE_8_BIT(5),
819 &li.bd_amoeba_slow_growth_rate, 3
823 TYPE_INTEGER, CONF_VALUE_8_BIT(6),
824 &li.bd_amoeba_fast_growth_rate, 25
828 TYPE_ELEMENT, CONF_VALUE_16_BIT(5),
829 &li.bd_amoeba_content_too_big, EL_BD_ROCK
833 TYPE_ELEMENT, CONF_VALUE_16_BIT(6),
834 &li.bd_amoeba_content_enclosed, EL_BD_DIAMOND
839 TYPE_INTEGER, CONF_VALUE_16_BIT(3),
840 &li.bd_amoeba_2_threshold_too_big, 200
844 TYPE_INTEGER, CONF_VALUE_16_BIT(4),
845 &li.bd_amoeba_2_slow_growth_time, 200
849 TYPE_INTEGER, CONF_VALUE_8_BIT(5),
850 &li.bd_amoeba_2_slow_growth_rate, 3
854 TYPE_INTEGER, CONF_VALUE_8_BIT(6),
855 &li.bd_amoeba_2_fast_growth_rate, 25
859 TYPE_ELEMENT, CONF_VALUE_16_BIT(5),
860 &li.bd_amoeba_2_content_too_big, EL_BD_ROCK
864 TYPE_ELEMENT, CONF_VALUE_16_BIT(6),
865 &li.bd_amoeba_2_content_enclosed, EL_BD_DIAMOND
869 TYPE_ELEMENT, CONF_VALUE_16_BIT(7),
870 &li.bd_amoeba_2_content_exploding, EL_EMPTY
874 TYPE_ELEMENT, CONF_VALUE_16_BIT(8),
875 &li.bd_amoeba_2_content_looks_like, EL_BD_AMOEBA_2
880 TYPE_CONTENT_LIST, CONF_VALUE_BYTES(1),
881 &li.yamyam_content, EL_ROCK, NULL,
882 &li.num_yamyam_contents, 4, MAX_ELEMENT_CONTENTS
886 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
887 &li.score[SC_YAMYAM], 10
892 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
893 &li.score[SC_ROBOT], 10
897 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
903 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
909 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
910 &li.time_magic_wall, 10
915 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
916 &li.game_of_life[0], 2
920 TYPE_INTEGER, CONF_VALUE_8_BIT(2),
921 &li.game_of_life[1], 3
925 TYPE_INTEGER, CONF_VALUE_8_BIT(3),
926 &li.game_of_life[2], 3
930 TYPE_INTEGER, CONF_VALUE_8_BIT(4),
931 &li.game_of_life[3], 3
935 TYPE_BOOLEAN, CONF_VALUE_8_BIT(5),
936 &li.use_life_bugs, FALSE
941 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
946 TYPE_INTEGER, CONF_VALUE_8_BIT(2),
951 TYPE_INTEGER, CONF_VALUE_8_BIT(3),
956 TYPE_INTEGER, CONF_VALUE_8_BIT(4),
961 EL_TIMEGATE_SWITCH, -1,
962 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
963 &li.time_timegate, 10
967 EL_LIGHT_SWITCH_ACTIVE, -1,
968 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
973 EL_SHIELD_NORMAL, -1,
974 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
975 &li.shield_normal_time, 10
978 EL_SHIELD_NORMAL, -1,
979 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
980 &li.score[SC_SHIELD], 10
984 EL_SHIELD_DEADLY, -1,
985 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
986 &li.shield_deadly_time, 10
989 EL_SHIELD_DEADLY, -1,
990 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
991 &li.score[SC_SHIELD], 10
996 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1001 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
1002 &li.extra_time_score, 10
1006 EL_TIME_ORB_FULL, -1,
1007 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1008 &li.time_orb_time, 10
1011 EL_TIME_ORB_FULL, -1,
1012 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1013 &li.use_time_orb_bug, FALSE
1018 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1019 &li.use_spring_bug, FALSE
1024 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1025 &li.android_move_time, 10
1029 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
1030 &li.android_clone_time, 10
1033 EL_EMC_ANDROID, SAVE_CONF_NEVER,
1034 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(1),
1035 &li.android_clone_element[0], EL_EMPTY, NULL,
1036 &li.num_android_clone_elements, 1, MAX_ANDROID_ELEMENTS_OLD
1040 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(2),
1041 &li.android_clone_element[0], EL_EMPTY, NULL,
1042 &li.num_android_clone_elements, 1, MAX_ANDROID_ELEMENTS
1047 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1048 &li.lenses_score, 10
1052 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
1057 EL_EMC_MAGNIFIER, -1,
1058 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1059 &li.magnify_score, 10
1062 EL_EMC_MAGNIFIER, -1,
1063 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
1064 &li.magnify_time, 10
1068 EL_EMC_MAGIC_BALL, -1,
1069 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1073 EL_EMC_MAGIC_BALL, -1,
1074 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1075 &li.ball_random, FALSE
1078 EL_EMC_MAGIC_BALL, -1,
1079 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
1080 &li.ball_active_initial, FALSE
1083 EL_EMC_MAGIC_BALL, -1,
1084 TYPE_CONTENT_LIST, CONF_VALUE_BYTES(1),
1085 &li.ball_content, EL_EMPTY, NULL,
1086 &li.num_ball_contents, 4, MAX_ELEMENT_CONTENTS
1090 EL_SOKOBAN_FIELD_EMPTY, -1,
1091 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1092 &li.sb_fields_needed, TRUE
1096 EL_SOKOBAN_OBJECT, -1,
1097 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1098 &li.sb_objects_needed, TRUE
1103 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1104 &li.mm_laser_red, FALSE
1108 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
1109 &li.mm_laser_green, FALSE
1113 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
1114 &li.mm_laser_blue, TRUE
1119 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1120 &li.df_laser_red, TRUE
1124 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
1125 &li.df_laser_green, TRUE
1129 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
1130 &li.df_laser_blue, FALSE
1134 EL_MM_FUSE_ACTIVE, -1,
1135 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1136 &li.mm_time_fuse, 25
1140 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1141 &li.mm_time_bomb, 75
1145 EL_MM_GRAY_BALL, -1,
1146 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1147 &li.mm_time_ball, 75
1150 EL_MM_GRAY_BALL, -1,
1151 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
1152 &li.mm_ball_choice_mode, ANIM_RANDOM
1155 EL_MM_GRAY_BALL, -1,
1156 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(1),
1157 &li.mm_ball_content, EL_EMPTY, NULL,
1158 &li.num_mm_ball_contents, 8, MAX_MM_BALL_CONTENTS
1161 EL_MM_GRAY_BALL, -1,
1162 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
1163 &li.rotate_mm_ball_content, TRUE
1166 EL_MM_GRAY_BALL, -1,
1167 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
1168 &li.explode_mm_ball, FALSE
1172 EL_MM_STEEL_BLOCK, -1,
1173 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1174 &li.mm_time_block, 75
1177 EL_MM_LIGHTBALL, -1,
1178 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1179 &li.score[SC_ELEM_BONUS], 10
1189 static struct LevelFileConfigInfo chunk_config_NOTE[] =
1193 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
1194 &xx_envelope.xsize, MAX_ENVELOPE_XSIZE,
1198 TYPE_INTEGER, CONF_VALUE_8_BIT(2),
1199 &xx_envelope.ysize, MAX_ENVELOPE_YSIZE,
1204 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
1205 &xx_envelope.autowrap, FALSE
1209 TYPE_BOOLEAN, CONF_VALUE_8_BIT(4),
1210 &xx_envelope.centered, FALSE
1215 TYPE_STRING, CONF_VALUE_BYTES(1),
1216 &xx_envelope.text, -1, NULL,
1217 &xx_string_length_unused, -1, MAX_ENVELOPE_TEXT_LEN,
1218 &xx_default_string_empty[0]
1228 static struct LevelFileConfigInfo chunk_config_CUSX_base[] =
1232 TYPE_STRING, CONF_VALUE_BYTES(1),
1233 &xx_ei.description[0], -1,
1234 &yy_ei.description[0],
1235 &xx_string_length_unused, -1, MAX_ELEMENT_NAME_LEN,
1236 &xx_default_description[0]
1241 TYPE_BITFIELD, CONF_VALUE_32_BIT(1),
1242 &xx_ei.properties[EP_BITFIELD_BASE_NR], EP_BITMASK_BASE_DEFAULT,
1243 &yy_ei.properties[EP_BITFIELD_BASE_NR]
1245 #if ENABLE_RESERVED_CODE
1246 // (reserved for later use)
1249 TYPE_BITFIELD, CONF_VALUE_32_BIT(2),
1250 &xx_ei.properties[EP_BITFIELD_BASE_NR + 1], EP_BITMASK_DEFAULT,
1251 &yy_ei.properties[EP_BITFIELD_BASE_NR + 1]
1257 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1258 &xx_ei.use_gfx_element, FALSE,
1259 &yy_ei.use_gfx_element
1263 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
1264 &xx_ei.gfx_element_initial, EL_EMPTY_SPACE,
1265 &yy_ei.gfx_element_initial
1270 TYPE_BITFIELD, CONF_VALUE_8_BIT(2),
1271 &xx_ei.access_direction, MV_ALL_DIRECTIONS,
1272 &yy_ei.access_direction
1277 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
1278 &xx_ei.collect_score_initial, 10,
1279 &yy_ei.collect_score_initial
1283 TYPE_INTEGER, CONF_VALUE_16_BIT(3),
1284 &xx_ei.collect_count_initial, 1,
1285 &yy_ei.collect_count_initial
1290 TYPE_INTEGER, CONF_VALUE_16_BIT(4),
1291 &xx_ei.ce_value_fixed_initial, 0,
1292 &yy_ei.ce_value_fixed_initial
1296 TYPE_INTEGER, CONF_VALUE_16_BIT(5),
1297 &xx_ei.ce_value_random_initial, 0,
1298 &yy_ei.ce_value_random_initial
1302 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
1303 &xx_ei.use_last_ce_value, FALSE,
1304 &yy_ei.use_last_ce_value
1309 TYPE_INTEGER, CONF_VALUE_16_BIT(6),
1310 &xx_ei.push_delay_fixed, 8,
1311 &yy_ei.push_delay_fixed
1315 TYPE_INTEGER, CONF_VALUE_16_BIT(7),
1316 &xx_ei.push_delay_random, 8,
1317 &yy_ei.push_delay_random
1321 TYPE_INTEGER, CONF_VALUE_16_BIT(8),
1322 &xx_ei.drop_delay_fixed, 0,
1323 &yy_ei.drop_delay_fixed
1327 TYPE_INTEGER, CONF_VALUE_16_BIT(9),
1328 &xx_ei.drop_delay_random, 0,
1329 &yy_ei.drop_delay_random
1333 TYPE_INTEGER, CONF_VALUE_16_BIT(10),
1334 &xx_ei.move_delay_fixed, 0,
1335 &yy_ei.move_delay_fixed
1339 TYPE_INTEGER, CONF_VALUE_16_BIT(11),
1340 &xx_ei.move_delay_random, 0,
1341 &yy_ei.move_delay_random
1345 TYPE_INTEGER, CONF_VALUE_16_BIT(16),
1346 &xx_ei.step_delay_fixed, 0,
1347 &yy_ei.step_delay_fixed
1351 TYPE_INTEGER, CONF_VALUE_16_BIT(17),
1352 &xx_ei.step_delay_random, 0,
1353 &yy_ei.step_delay_random
1358 TYPE_BITFIELD, CONF_VALUE_32_BIT(3),
1359 &xx_ei.move_pattern, MV_ALL_DIRECTIONS,
1364 TYPE_BITFIELD, CONF_VALUE_8_BIT(4),
1365 &xx_ei.move_direction_initial, MV_START_AUTOMATIC,
1366 &yy_ei.move_direction_initial
1370 TYPE_INTEGER, CONF_VALUE_8_BIT(5),
1371 &xx_ei.move_stepsize, TILEX / 8,
1372 &yy_ei.move_stepsize
1377 TYPE_ELEMENT, CONF_VALUE_16_BIT(12),
1378 &xx_ei.move_enter_element, EL_EMPTY_SPACE,
1379 &yy_ei.move_enter_element
1383 TYPE_ELEMENT, CONF_VALUE_16_BIT(13),
1384 &xx_ei.move_leave_element, EL_EMPTY_SPACE,
1385 &yy_ei.move_leave_element
1389 TYPE_INTEGER, CONF_VALUE_8_BIT(6),
1390 &xx_ei.move_leave_type, LEAVE_TYPE_UNLIMITED,
1391 &yy_ei.move_leave_type
1396 TYPE_INTEGER, CONF_VALUE_8_BIT(7),
1397 &xx_ei.slippery_type, SLIPPERY_ANY_RANDOM,
1398 &yy_ei.slippery_type
1403 TYPE_INTEGER, CONF_VALUE_8_BIT(8),
1404 &xx_ei.explosion_type, EXPLODES_3X3,
1405 &yy_ei.explosion_type
1409 TYPE_INTEGER, CONF_VALUE_16_BIT(14),
1410 &xx_ei.explosion_delay, 16,
1411 &yy_ei.explosion_delay
1415 TYPE_INTEGER, CONF_VALUE_16_BIT(15),
1416 &xx_ei.ignition_delay, 8,
1417 &yy_ei.ignition_delay
1422 TYPE_CONTENT_LIST, CONF_VALUE_BYTES(2),
1423 &xx_ei.content, EL_EMPTY_SPACE,
1425 &xx_num_contents, 1, 1
1428 // ---------- "num_change_pages" must be the last entry ---------------------
1431 -1, SAVE_CONF_ALWAYS,
1432 TYPE_INTEGER, CONF_VALUE_8_BIT(9),
1433 &xx_ei.num_change_pages, 1,
1434 &yy_ei.num_change_pages
1445 static struct LevelFileConfigInfo chunk_config_CUSX_change[] =
1447 // ---------- "current_change_page" must be the first entry -----------------
1450 -1, SAVE_CONF_ALWAYS,
1451 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
1452 &xx_current_change_page, -1
1455 // ---------- (the remaining entries can be in any order) -------------------
1459 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
1460 &xx_change.can_change, FALSE
1465 TYPE_BITFIELD, CONF_VALUE_32_BIT(1),
1466 &xx_event_bits[0], 0
1470 TYPE_BITFIELD, CONF_VALUE_32_BIT(2),
1471 &xx_event_bits[1], 0
1476 TYPE_BITFIELD, CONF_VALUE_8_BIT(3),
1477 &xx_change.trigger_player, CH_PLAYER_ANY
1481 TYPE_BITFIELD, CONF_VALUE_8_BIT(4),
1482 &xx_change.trigger_side, CH_SIDE_ANY
1486 TYPE_BITFIELD, CONF_VALUE_32_BIT(3),
1487 &xx_change.trigger_page, CH_PAGE_ANY
1492 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
1493 &xx_change.target_element, EL_EMPTY_SPACE
1498 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
1499 &xx_change.delay_fixed, 0
1503 TYPE_INTEGER, CONF_VALUE_16_BIT(3),
1504 &xx_change.delay_random, 0
1508 TYPE_INTEGER, CONF_VALUE_16_BIT(4),
1509 &xx_change.delay_frames, FRAMES_PER_SECOND
1514 TYPE_ELEMENT, CONF_VALUE_16_BIT(5),
1515 &xx_change.initial_trigger_element, EL_EMPTY_SPACE
1520 TYPE_BOOLEAN, CONF_VALUE_8_BIT(6),
1521 &xx_change.explode, FALSE
1525 TYPE_BOOLEAN, CONF_VALUE_8_BIT(7),
1526 &xx_change.use_target_content, FALSE
1530 TYPE_BOOLEAN, CONF_VALUE_8_BIT(8),
1531 &xx_change.only_if_complete, FALSE
1535 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
1536 &xx_change.use_random_replace, FALSE
1540 TYPE_INTEGER, CONF_VALUE_8_BIT(10),
1541 &xx_change.random_percentage, 100
1545 TYPE_INTEGER, CONF_VALUE_8_BIT(11),
1546 &xx_change.replace_when, CP_WHEN_EMPTY
1551 TYPE_BOOLEAN, CONF_VALUE_8_BIT(12),
1552 &xx_change.has_action, FALSE
1556 TYPE_INTEGER, CONF_VALUE_8_BIT(13),
1557 &xx_change.action_type, CA_NO_ACTION
1561 TYPE_INTEGER, CONF_VALUE_8_BIT(14),
1562 &xx_change.action_mode, CA_MODE_UNDEFINED
1566 TYPE_INTEGER, CONF_VALUE_16_BIT(6),
1567 &xx_change.action_arg, CA_ARG_UNDEFINED
1572 TYPE_ELEMENT, CONF_VALUE_16_BIT(7),
1573 &xx_change.action_element, EL_EMPTY_SPACE
1578 TYPE_CONTENT_LIST, CONF_VALUE_BYTES(1),
1579 &xx_change.target_content, EL_EMPTY_SPACE, NULL,
1580 &xx_num_contents, 1, 1
1590 static struct LevelFileConfigInfo chunk_config_GRPX[] =
1594 TYPE_STRING, CONF_VALUE_BYTES(1),
1595 &xx_ei.description[0], -1, NULL,
1596 &xx_string_length_unused, -1, MAX_ELEMENT_NAME_LEN,
1597 &xx_default_description[0]
1602 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1603 &xx_ei.use_gfx_element, FALSE
1607 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
1608 &xx_ei.gfx_element_initial, EL_EMPTY_SPACE
1613 TYPE_INTEGER, CONF_VALUE_8_BIT(2),
1614 &xx_group.choice_mode, ANIM_RANDOM
1619 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(2),
1620 &xx_group.element[0], EL_EMPTY_SPACE, NULL,
1621 &xx_group.num_elements, 1, MAX_ELEMENTS_IN_GROUP
1631 static struct LevelFileConfigInfo chunk_config_EMPX[] =
1635 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1636 &xx_ei.use_gfx_element, FALSE
1640 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
1641 &xx_ei.gfx_element_initial, EL_EMPTY_SPACE
1651 static struct LevelFileConfigInfo chunk_config_CONF[] = // (OBSOLETE)
1655 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
1656 &li.block_snap_field, TRUE
1660 TYPE_BOOLEAN, CONF_VALUE_8_BIT(13),
1661 &li.continuous_snapping, TRUE
1665 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
1666 &li.initial_player_stepsize[0], STEPSIZE_NORMAL
1670 TYPE_BOOLEAN, CONF_VALUE_8_BIT(10),
1671 &li.use_start_element[0], FALSE
1675 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
1676 &li.start_element[0], EL_PLAYER_1
1680 TYPE_BOOLEAN, CONF_VALUE_8_BIT(11),
1681 &li.use_artwork_element[0], FALSE
1685 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
1686 &li.artwork_element[0], EL_PLAYER_1
1690 TYPE_BOOLEAN, CONF_VALUE_8_BIT(12),
1691 &li.use_explosion_element[0], FALSE
1695 TYPE_ELEMENT, CONF_VALUE_16_BIT(3),
1696 &li.explosion_element[0], EL_PLAYER_1
1711 filetype_id_list[] =
1713 { LEVEL_FILE_TYPE_RND, "RND" },
1714 { LEVEL_FILE_TYPE_BD, "BD" },
1715 { LEVEL_FILE_TYPE_EM, "EM" },
1716 { LEVEL_FILE_TYPE_SP, "SP" },
1717 { LEVEL_FILE_TYPE_DX, "DX" },
1718 { LEVEL_FILE_TYPE_SB, "SB" },
1719 { LEVEL_FILE_TYPE_DC, "DC" },
1720 { LEVEL_FILE_TYPE_MM, "MM" },
1721 { LEVEL_FILE_TYPE_MM, "DF" },
1726 // ============================================================================
1727 // level file functions
1728 // ============================================================================
1730 static boolean check_special_flags(char *flag)
1732 if (strEqual(options.special_flags, flag) ||
1733 strEqual(leveldir_current->special_flags, flag))
1739 static struct DateInfo getCurrentDate(void)
1741 time_t epoch_seconds = time(NULL);
1742 struct tm *now = localtime(&epoch_seconds);
1743 struct DateInfo date;
1745 date.year = now->tm_year + 1900;
1746 date.month = now->tm_mon + 1;
1747 date.day = now->tm_mday;
1749 date.src = DATE_SRC_CLOCK;
1754 static void resetEventFlags(struct ElementChangeInfo *change)
1758 for (i = 0; i < NUM_CHANGE_EVENTS; i++)
1759 change->has_event[i] = FALSE;
1762 static void resetEventBits(void)
1766 for (i = 0; i < NUM_CE_BITFIELDS; i++)
1767 xx_event_bits[i] = 0;
1770 static void setEventFlagsFromEventBits(struct ElementChangeInfo *change)
1774 /* important: only change event flag if corresponding event bit is set
1775 (this is because all xx_event_bits[] values are loaded separately,
1776 and all xx_event_bits[] values are set back to zero before loading
1777 another value xx_event_bits[x] (each value representing 32 flags)) */
1779 for (i = 0; i < NUM_CHANGE_EVENTS; i++)
1780 if (xx_event_bits[CH_EVENT_BITFIELD_NR(i)] & CH_EVENT_BIT(i))
1781 change->has_event[i] = TRUE;
1784 static void setEventBitsFromEventFlags(struct ElementChangeInfo *change)
1788 /* in contrast to the above function setEventFlagsFromEventBits(), it
1789 would also be possible to set all bits in xx_event_bits[] to 0 or 1
1790 depending on the corresponding change->has_event[i] values here, as
1791 all xx_event_bits[] values are reset in resetEventBits() before */
1793 for (i = 0; i < NUM_CHANGE_EVENTS; i++)
1794 if (change->has_event[i])
1795 xx_event_bits[CH_EVENT_BITFIELD_NR(i)] |= CH_EVENT_BIT(i);
1798 static char *getDefaultElementDescription(struct ElementInfo *ei)
1800 static char description[MAX_ELEMENT_NAME_LEN + 1];
1801 char *default_description = (ei->custom_description != NULL ?
1802 ei->custom_description :
1803 ei->editor_description);
1806 // always start with reliable default values
1807 for (i = 0; i < MAX_ELEMENT_NAME_LEN + 1; i++)
1808 description[i] = '\0';
1810 // truncate element description to MAX_ELEMENT_NAME_LEN bytes
1811 strncpy(description, default_description, MAX_ELEMENT_NAME_LEN);
1813 return &description[0];
1816 static void setElementDescriptionToDefault(struct ElementInfo *ei)
1818 char *default_description = getDefaultElementDescription(ei);
1821 for (i = 0; i < MAX_ELEMENT_NAME_LEN + 1; i++)
1822 ei->description[i] = default_description[i];
1825 static void setConfigToDefaultsFromConfigList(struct LevelFileConfigInfo *conf)
1829 for (i = 0; conf[i].data_type != -1; i++)
1831 int default_value = conf[i].default_value;
1832 int data_type = conf[i].data_type;
1833 int conf_type = conf[i].conf_type;
1834 int byte_mask = conf_type & CONF_MASK_BYTES;
1836 if (byte_mask == CONF_MASK_MULTI_BYTES)
1838 int default_num_entities = conf[i].default_num_entities;
1839 int max_num_entities = conf[i].max_num_entities;
1841 *(int *)(conf[i].num_entities) = default_num_entities;
1843 if (data_type == TYPE_STRING)
1845 char *default_string = conf[i].default_string;
1846 char *string = (char *)(conf[i].value);
1848 strncpy(string, default_string, max_num_entities);
1850 else if (data_type == TYPE_ELEMENT_LIST)
1852 int *element_array = (int *)(conf[i].value);
1855 for (j = 0; j < max_num_entities; j++)
1856 element_array[j] = default_value;
1858 else if (data_type == TYPE_CONTENT_LIST)
1860 struct Content *content = (struct Content *)(conf[i].value);
1863 for (c = 0; c < max_num_entities; c++)
1864 for (y = 0; y < 3; y++)
1865 for (x = 0; x < 3; x++)
1866 content[c].e[x][y] = default_value;
1869 else // constant size configuration data (1, 2 or 4 bytes)
1871 if (data_type == TYPE_BOOLEAN)
1872 *(boolean *)(conf[i].value) = default_value;
1874 *(int *) (conf[i].value) = default_value;
1879 static void copyConfigFromConfigList(struct LevelFileConfigInfo *conf)
1883 for (i = 0; conf[i].data_type != -1; i++)
1885 int data_type = conf[i].data_type;
1886 int conf_type = conf[i].conf_type;
1887 int byte_mask = conf_type & CONF_MASK_BYTES;
1889 if (byte_mask == CONF_MASK_MULTI_BYTES)
1891 int max_num_entities = conf[i].max_num_entities;
1893 if (data_type == TYPE_STRING)
1895 char *string = (char *)(conf[i].value);
1896 char *string_copy = (char *)(conf[i].value_copy);
1898 strncpy(string_copy, string, max_num_entities);
1900 else if (data_type == TYPE_ELEMENT_LIST)
1902 int *element_array = (int *)(conf[i].value);
1903 int *element_array_copy = (int *)(conf[i].value_copy);
1906 for (j = 0; j < max_num_entities; j++)
1907 element_array_copy[j] = element_array[j];
1909 else if (data_type == TYPE_CONTENT_LIST)
1911 struct Content *content = (struct Content *)(conf[i].value);
1912 struct Content *content_copy = (struct Content *)(conf[i].value_copy);
1915 for (c = 0; c < max_num_entities; c++)
1916 for (y = 0; y < 3; y++)
1917 for (x = 0; x < 3; x++)
1918 content_copy[c].e[x][y] = content[c].e[x][y];
1921 else // constant size configuration data (1, 2 or 4 bytes)
1923 if (data_type == TYPE_BOOLEAN)
1924 *(boolean *)(conf[i].value_copy) = *(boolean *)(conf[i].value);
1926 *(int *) (conf[i].value_copy) = *(int *) (conf[i].value);
1931 void copyElementInfo(struct ElementInfo *ei_from, struct ElementInfo *ei_to)
1935 xx_ei = *ei_from; // copy element data into temporary buffer
1936 yy_ei = *ei_to; // copy element data into temporary buffer
1938 copyConfigFromConfigList(chunk_config_CUSX_base);
1943 // ---------- reinitialize and copy change pages ----------
1945 ei_to->num_change_pages = ei_from->num_change_pages;
1946 ei_to->current_change_page = ei_from->current_change_page;
1948 setElementChangePages(ei_to, ei_to->num_change_pages);
1950 for (i = 0; i < ei_to->num_change_pages; i++)
1951 ei_to->change_page[i] = ei_from->change_page[i];
1953 // ---------- copy group element info ----------
1954 if (ei_from->group != NULL && ei_to->group != NULL) // group or internal
1955 *ei_to->group = *ei_from->group;
1957 // mark this custom element as modified
1958 ei_to->modified_settings = TRUE;
1961 void setElementChangePages(struct ElementInfo *ei, int change_pages)
1963 int change_page_size = sizeof(struct ElementChangeInfo);
1965 ei->num_change_pages = MAX(1, change_pages);
1968 checked_realloc(ei->change_page, ei->num_change_pages * change_page_size);
1970 if (ei->current_change_page >= ei->num_change_pages)
1971 ei->current_change_page = ei->num_change_pages - 1;
1973 ei->change = &ei->change_page[ei->current_change_page];
1976 void setElementChangeInfoToDefaults(struct ElementChangeInfo *change)
1978 xx_change = *change; // copy change data into temporary buffer
1980 setConfigToDefaultsFromConfigList(chunk_config_CUSX_change);
1982 *change = xx_change;
1984 resetEventFlags(change);
1986 change->direct_action = 0;
1987 change->other_action = 0;
1989 change->pre_change_function = NULL;
1990 change->change_function = NULL;
1991 change->post_change_function = NULL;
1994 static void setLevelInfoToDefaults_Level(struct LevelInfo *level)
1998 li = *level; // copy level data into temporary buffer
1999 setConfigToDefaultsFromConfigList(chunk_config_INFO);
2000 *level = li; // copy temporary buffer back to level data
2002 setLevelInfoToDefaults_BD();
2003 setLevelInfoToDefaults_EM();
2004 setLevelInfoToDefaults_SP();
2005 setLevelInfoToDefaults_MM();
2007 level->native_bd_level = &native_bd_level;
2008 level->native_em_level = &native_em_level;
2009 level->native_sp_level = &native_sp_level;
2010 level->native_mm_level = &native_mm_level;
2012 level->file_version = FILE_VERSION_ACTUAL;
2013 level->game_version = GAME_VERSION_ACTUAL;
2015 level->creation_date = getCurrentDate();
2017 level->encoding_16bit_field = TRUE;
2018 level->encoding_16bit_yamyam = TRUE;
2019 level->encoding_16bit_amoeba = TRUE;
2021 // clear level name and level author string buffers
2022 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
2023 level->name[i] = '\0';
2024 for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
2025 level->author[i] = '\0';
2027 // set level name and level author to default values
2028 strcpy(level->name, NAMELESS_LEVEL_NAME);
2029 strcpy(level->author, ANONYMOUS_NAME);
2031 // set level playfield to playable default level with player and exit
2032 for (x = 0; x < MAX_LEV_FIELDX; x++)
2033 for (y = 0; y < MAX_LEV_FIELDY; y++)
2034 level->field[x][y] = EL_SAND;
2036 level->field[0][0] = EL_PLAYER_1;
2037 level->field[STD_LEV_FIELDX - 1][STD_LEV_FIELDY - 1] = EL_EXIT_CLOSED;
2039 BorderElement = EL_STEELWALL;
2041 // detect custom elements when loading them
2042 level->file_has_custom_elements = FALSE;
2044 // set all bug compatibility flags to "false" => do not emulate this bug
2045 level->use_action_after_change_bug = FALSE;
2047 if (leveldir_current)
2049 // try to determine better author name than 'anonymous'
2050 if (!strEqual(leveldir_current->author, ANONYMOUS_NAME))
2052 strncpy(level->author, leveldir_current->author, MAX_LEVEL_AUTHOR_LEN);
2053 level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
2057 switch (LEVELCLASS(leveldir_current))
2059 case LEVELCLASS_TUTORIAL:
2060 strcpy(level->author, PROGRAM_AUTHOR_STRING);
2063 case LEVELCLASS_CONTRIB:
2064 strncpy(level->author, leveldir_current->name, MAX_LEVEL_AUTHOR_LEN);
2065 level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
2068 case LEVELCLASS_PRIVATE:
2069 strncpy(level->author, getRealName(), MAX_LEVEL_AUTHOR_LEN);
2070 level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
2074 // keep default value
2081 static void setLevelInfoToDefaults_Elements(struct LevelInfo *level)
2083 static boolean clipboard_elements_initialized = FALSE;
2086 InitElementPropertiesStatic();
2088 li = *level; // copy level data into temporary buffer
2089 setConfigToDefaultsFromConfigList(chunk_config_ELEM);
2090 *level = li; // copy temporary buffer back to level data
2092 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2095 struct ElementInfo *ei = &element_info[element];
2097 if (element == EL_MM_GRAY_BALL)
2099 struct LevelInfo_MM *level_mm = level->native_mm_level;
2102 for (j = 0; j < level->num_mm_ball_contents; j++)
2103 level->mm_ball_content[j] =
2104 map_element_MM_to_RND(level_mm->ball_content[j]);
2107 // never initialize clipboard elements after the very first time
2108 // (to be able to use clipboard elements between several levels)
2109 if (IS_CLIPBOARD_ELEMENT(element) && clipboard_elements_initialized)
2112 if (IS_ENVELOPE(element))
2114 int envelope_nr = element - EL_ENVELOPE_1;
2116 setConfigToDefaultsFromConfigList(chunk_config_NOTE);
2118 level->envelope[envelope_nr] = xx_envelope;
2121 if (IS_CUSTOM_ELEMENT(element) ||
2122 IS_GROUP_ELEMENT(element) ||
2123 IS_INTERNAL_ELEMENT(element))
2125 xx_ei = *ei; // copy element data into temporary buffer
2127 setConfigToDefaultsFromConfigList(chunk_config_CUSX_base);
2132 setElementChangePages(ei, 1);
2133 setElementChangeInfoToDefaults(ei->change);
2135 if (IS_CUSTOM_ELEMENT(element) ||
2136 IS_GROUP_ELEMENT(element))
2138 setElementDescriptionToDefault(ei);
2140 ei->modified_settings = FALSE;
2143 if (IS_CUSTOM_ELEMENT(element) ||
2144 IS_INTERNAL_ELEMENT(element))
2146 // internal values used in level editor
2148 ei->access_type = 0;
2149 ei->access_layer = 0;
2150 ei->access_protected = 0;
2151 ei->walk_to_action = 0;
2152 ei->smash_targets = 0;
2155 ei->can_explode_by_fire = FALSE;
2156 ei->can_explode_smashed = FALSE;
2157 ei->can_explode_impact = FALSE;
2159 ei->current_change_page = 0;
2162 if (IS_GROUP_ELEMENT(element) ||
2163 IS_INTERNAL_ELEMENT(element))
2165 struct ElementGroupInfo *group;
2167 // initialize memory for list of elements in group
2168 if (ei->group == NULL)
2169 ei->group = checked_malloc(sizeof(struct ElementGroupInfo));
2173 xx_group = *group; // copy group data into temporary buffer
2175 setConfigToDefaultsFromConfigList(chunk_config_GRPX);
2180 if (IS_EMPTY_ELEMENT(element) ||
2181 IS_INTERNAL_ELEMENT(element))
2183 xx_ei = *ei; // copy element data into temporary buffer
2185 setConfigToDefaultsFromConfigList(chunk_config_EMPX);
2191 clipboard_elements_initialized = TRUE;
2194 static void setLevelInfoToDefaults(struct LevelInfo *level,
2195 boolean level_info_only,
2196 boolean reset_file_status)
2198 setLevelInfoToDefaults_Level(level);
2200 if (!level_info_only)
2201 setLevelInfoToDefaults_Elements(level);
2203 if (reset_file_status)
2205 level->no_valid_file = FALSE;
2206 level->no_level_file = FALSE;
2209 level->changed = FALSE;
2212 static void setFileInfoToDefaults(struct LevelFileInfo *level_file_info)
2214 level_file_info->nr = 0;
2215 level_file_info->type = LEVEL_FILE_TYPE_UNKNOWN;
2216 level_file_info->packed = FALSE;
2218 setString(&level_file_info->basename, NULL);
2219 setString(&level_file_info->filename, NULL);
2222 int getMappedElement_SB(int, boolean);
2224 static void ActivateLevelTemplate(void)
2228 if (check_special_flags("load_xsb_to_ces"))
2230 // fill smaller playfields with padding "beyond border wall" elements
2231 if (level.fieldx < level_template.fieldx ||
2232 level.fieldy < level_template.fieldy)
2234 short field[level.fieldx][level.fieldy];
2235 int new_fieldx = MAX(level.fieldx, level_template.fieldx);
2236 int new_fieldy = MAX(level.fieldy, level_template.fieldy);
2237 int pos_fieldx = (new_fieldx - level.fieldx) / 2;
2238 int pos_fieldy = (new_fieldy - level.fieldy) / 2;
2240 // copy old playfield (which is smaller than the visible area)
2241 for (y = 0; y < level.fieldy; y++) for (x = 0; x < level.fieldx; x++)
2242 field[x][y] = level.field[x][y];
2244 // fill new, larger playfield with "beyond border wall" elements
2245 for (y = 0; y < new_fieldy; y++) for (x = 0; x < new_fieldx; x++)
2246 level.field[x][y] = getMappedElement_SB('_', TRUE);
2248 // copy the old playfield to the middle of the new playfield
2249 for (y = 0; y < level.fieldy; y++) for (x = 0; x < level.fieldx; x++)
2250 level.field[pos_fieldx + x][pos_fieldy + y] = field[x][y];
2252 level.fieldx = new_fieldx;
2253 level.fieldy = new_fieldy;
2257 // Currently there is no special action needed to activate the template
2258 // data, because 'element_info' property settings overwrite the original
2259 // level data, while all other variables do not change.
2261 // Exception: 'from_level_template' elements in the original level playfield
2262 // are overwritten with the corresponding elements at the same position in
2263 // playfield from the level template.
2265 for (x = 0; x < level.fieldx; x++)
2266 for (y = 0; y < level.fieldy; y++)
2267 if (level.field[x][y] == EL_FROM_LEVEL_TEMPLATE)
2268 level.field[x][y] = level_template.field[x][y];
2270 if (check_special_flags("load_xsb_to_ces"))
2272 struct LevelInfo level_backup = level;
2274 // overwrite all individual level settings from template level settings
2275 level = level_template;
2277 // restore level file info
2278 level.file_info = level_backup.file_info;
2280 // restore playfield size
2281 level.fieldx = level_backup.fieldx;
2282 level.fieldy = level_backup.fieldy;
2284 // restore playfield content
2285 for (x = 0; x < level.fieldx; x++)
2286 for (y = 0; y < level.fieldy; y++)
2287 level.field[x][y] = level_backup.field[x][y];
2289 // restore name and author from individual level
2290 strcpy(level.name, level_backup.name);
2291 strcpy(level.author, level_backup.author);
2293 // restore flag "use_custom_template"
2294 level.use_custom_template = level_backup.use_custom_template;
2298 static boolean checkForPackageFromBasename_BD(char *basename)
2300 // check for native BD level file extensions
2301 if (!strSuffixLower(basename, ".bd") &&
2302 !strSuffixLower(basename, ".bdr") &&
2303 !strSuffixLower(basename, ".brc") &&
2304 !strSuffixLower(basename, ".gds"))
2307 // check for standard single-level BD files (like "001.bd")
2308 if (strSuffixLower(basename, ".bd") &&
2309 strlen(basename) == 6 &&
2310 basename[0] >= '0' && basename[0] <= '9' &&
2311 basename[1] >= '0' && basename[1] <= '9' &&
2312 basename[2] >= '0' && basename[2] <= '9')
2315 // this is a level package in native BD file format
2319 static char *getLevelFilenameFromBasename(char *basename)
2321 static char *filename = NULL;
2323 checked_free(filename);
2325 filename = getPath2(getCurrentLevelDir(), basename);
2330 static int getFileTypeFromBasename(char *basename)
2332 // !!! ALSO SEE COMMENT IN checkForPackageFromBasename() !!!
2334 static char *filename = NULL;
2335 struct stat file_status;
2337 // ---------- try to determine file type from filename ----------
2339 // check for typical filename of a Supaplex level package file
2340 if (strlen(basename) == 10 && strPrefixLower(basename, "levels.d"))
2341 return LEVEL_FILE_TYPE_SP;
2343 // check for typical filename of a Diamond Caves II level package file
2344 if (strSuffixLower(basename, ".dc") ||
2345 strSuffixLower(basename, ".dc2"))
2346 return LEVEL_FILE_TYPE_DC;
2348 // check for typical filename of a Sokoban level package file
2349 if (strSuffixLower(basename, ".xsb") &&
2350 strchr(basename, '%') == NULL)
2351 return LEVEL_FILE_TYPE_SB;
2353 // check for typical filename of a Boulder Dash (GDash) level package file
2354 if (checkForPackageFromBasename_BD(basename))
2355 return LEVEL_FILE_TYPE_BD;
2357 // ---------- try to determine file type from filesize ----------
2359 checked_free(filename);
2360 filename = getPath2(getCurrentLevelDir(), basename);
2362 if (stat(filename, &file_status) == 0)
2364 // check for typical filesize of a Supaplex level package file
2365 if (file_status.st_size == 170496)
2366 return LEVEL_FILE_TYPE_SP;
2369 return LEVEL_FILE_TYPE_UNKNOWN;
2372 static int getFileTypeFromMagicBytes(char *filename, int type)
2376 if ((file = openFile(filename, MODE_READ)))
2378 char chunk_name[CHUNK_ID_LEN + 1];
2380 getFileChunkBE(file, chunk_name, NULL);
2382 if (strEqual(chunk_name, "MMII") ||
2383 strEqual(chunk_name, "MIRR"))
2384 type = LEVEL_FILE_TYPE_MM;
2392 static boolean checkForPackageFromBasename(char *basename)
2394 // !!! WON'T WORK ANYMORE IF getFileTypeFromBasename() ALSO DETECTS !!!
2395 // !!! SINGLE LEVELS (CURRENTLY ONLY DETECTS LEVEL PACKAGES !!!
2397 return (getFileTypeFromBasename(basename) != LEVEL_FILE_TYPE_UNKNOWN);
2400 static char *getSingleLevelBasenameExt(int nr, char *extension)
2402 static char basename[MAX_FILENAME_LEN];
2405 sprintf(basename, "%s", LEVELTEMPLATE_FILENAME);
2407 sprintf(basename, "%03d.%s", nr, extension);
2412 static char *getSingleLevelBasename(int nr)
2414 return getSingleLevelBasenameExt(nr, LEVELFILE_EXTENSION);
2417 static char *getPackedLevelBasename(int type)
2419 static char basename[MAX_FILENAME_LEN];
2420 char *directory = getCurrentLevelDir();
2422 DirectoryEntry *dir_entry;
2424 strcpy(basename, UNDEFINED_FILENAME); // default: undefined file
2426 if ((dir = openDirectory(directory)) == NULL)
2428 Warn("cannot read current level directory '%s'", directory);
2433 while ((dir_entry = readDirectory(dir)) != NULL) // loop all entries
2435 char *entry_basename = dir_entry->basename;
2436 int entry_type = getFileTypeFromBasename(entry_basename);
2438 if (entry_type != LEVEL_FILE_TYPE_UNKNOWN) // found valid level package
2440 if (type == LEVEL_FILE_TYPE_UNKNOWN ||
2443 strcpy(basename, entry_basename);
2450 closeDirectory(dir);
2455 static char *getSingleLevelFilename(int nr)
2457 return getLevelFilenameFromBasename(getSingleLevelBasename(nr));
2460 #if ENABLE_UNUSED_CODE
2461 static char *getPackedLevelFilename(int type)
2463 return getLevelFilenameFromBasename(getPackedLevelBasename(type));
2467 char *getDefaultLevelFilename(int nr)
2469 return getSingleLevelFilename(nr);
2472 #if ENABLE_UNUSED_CODE
2473 static void setLevelFileInfo_SingleLevelFilename(struct LevelFileInfo *lfi,
2477 lfi->packed = FALSE;
2479 setString(&lfi->basename, getSingleLevelBasename(lfi->nr, lfi->type));
2480 setString(&lfi->filename, getLevelFilenameFromBasename(lfi->basename));
2484 static void setLevelFileInfo_FormatLevelFilename(struct LevelFileInfo *lfi,
2485 int type, char *format, ...)
2487 static char basename[MAX_FILENAME_LEN];
2490 va_start(ap, format);
2491 vsprintf(basename, format, ap);
2495 lfi->packed = FALSE;
2497 setString(&lfi->basename, basename);
2498 setString(&lfi->filename, getLevelFilenameFromBasename(lfi->basename));
2501 static void setLevelFileInfo_PackedLevelFilename(struct LevelFileInfo *lfi,
2507 setString(&lfi->basename, getPackedLevelBasename(lfi->type));
2508 setString(&lfi->filename, getLevelFilenameFromBasename(lfi->basename));
2511 static int getFiletypeFromID(char *filetype_id)
2513 char *filetype_id_lower;
2514 int filetype = LEVEL_FILE_TYPE_UNKNOWN;
2517 if (filetype_id == NULL)
2518 return LEVEL_FILE_TYPE_UNKNOWN;
2520 filetype_id_lower = getStringToLower(filetype_id);
2522 for (i = 0; filetype_id_list[i].id != NULL; i++)
2524 char *id_lower = getStringToLower(filetype_id_list[i].id);
2526 if (strEqual(filetype_id_lower, id_lower))
2527 filetype = filetype_id_list[i].filetype;
2531 if (filetype != LEVEL_FILE_TYPE_UNKNOWN)
2535 free(filetype_id_lower);
2540 char *getLocalLevelTemplateFilename(void)
2542 return getDefaultLevelFilename(-1);
2545 char *getGlobalLevelTemplateFilename(void)
2547 // global variable "leveldir_current" must be modified in the loop below
2548 LevelDirTree *leveldir_current_last = leveldir_current;
2549 char *filename = NULL;
2551 // check for template level in path from current to topmost tree node
2553 while (leveldir_current != NULL)
2555 filename = getDefaultLevelFilename(-1);
2557 if (fileExists(filename))
2560 leveldir_current = leveldir_current->node_parent;
2563 // restore global variable "leveldir_current" modified in above loop
2564 leveldir_current = leveldir_current_last;
2569 static void determineLevelFileInfo_Filename(struct LevelFileInfo *lfi)
2573 // special case: level number is negative => check for level template file
2576 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
2577 getSingleLevelBasename(-1));
2579 // replace local level template filename with global template filename
2580 setString(&lfi->filename, getGlobalLevelTemplateFilename());
2582 // no fallback if template file not existing
2586 // special case: check for file name/pattern specified in "levelinfo.conf"
2587 if (leveldir_current->level_filename != NULL)
2589 int filetype = getFiletypeFromID(leveldir_current->level_filetype);
2591 setLevelFileInfo_FormatLevelFilename(lfi, filetype,
2592 leveldir_current->level_filename, nr);
2594 lfi->packed = checkForPackageFromBasename(leveldir_current->level_filename);
2596 if (fileExists(lfi->filename))
2599 else if (leveldir_current->level_filetype != NULL)
2601 int filetype = getFiletypeFromID(leveldir_current->level_filetype);
2603 // check for specified native level file with standard file name
2604 setLevelFileInfo_FormatLevelFilename(lfi, filetype,
2605 "%03d.%s", nr, LEVELFILE_EXTENSION);
2606 if (fileExists(lfi->filename))
2610 // check for native Rocks'n'Diamonds level file
2611 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
2612 "%03d.%s", nr, LEVELFILE_EXTENSION);
2613 if (fileExists(lfi->filename))
2616 // check for native Boulder Dash level file
2617 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_BD, "%03d.bd", nr);
2618 if (fileExists(lfi->filename))
2621 // check for Emerald Mine level file (V1)
2622 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "a%c%c",
2623 'a' + (nr / 10) % 26, '0' + nr % 10);
2624 if (fileExists(lfi->filename))
2626 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "A%c%c",
2627 'A' + (nr / 10) % 26, '0' + nr % 10);
2628 if (fileExists(lfi->filename))
2631 // check for Emerald Mine level file (V2 to V5)
2632 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%d", nr);
2633 if (fileExists(lfi->filename))
2636 // check for Emerald Mine level file (V6 / single mode)
2637 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02ds", nr);
2638 if (fileExists(lfi->filename))
2640 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02dS", nr);
2641 if (fileExists(lfi->filename))
2644 // check for Emerald Mine level file (V6 / teamwork mode)
2645 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02dt", nr);
2646 if (fileExists(lfi->filename))
2648 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02dT", nr);
2649 if (fileExists(lfi->filename))
2652 // check for various packed level file formats
2653 setLevelFileInfo_PackedLevelFilename(lfi, LEVEL_FILE_TYPE_UNKNOWN);
2654 if (fileExists(lfi->filename))
2657 // no known level file found -- use default values (and fail later)
2658 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
2659 "%03d.%s", nr, LEVELFILE_EXTENSION);
2662 static void determineLevelFileInfo_Filetype(struct LevelFileInfo *lfi)
2664 if (lfi->type == LEVEL_FILE_TYPE_UNKNOWN)
2665 lfi->type = getFileTypeFromBasename(lfi->basename);
2667 if (lfi->type == LEVEL_FILE_TYPE_RND)
2668 lfi->type = getFileTypeFromMagicBytes(lfi->filename, lfi->type);
2671 static void setLevelFileInfo(struct LevelFileInfo *level_file_info, int nr)
2673 // always start with reliable default values
2674 setFileInfoToDefaults(level_file_info);
2676 level_file_info->nr = nr; // set requested level number
2678 determineLevelFileInfo_Filename(level_file_info);
2679 determineLevelFileInfo_Filetype(level_file_info);
2682 static void copyLevelFileInfo(struct LevelFileInfo *lfi_from,
2683 struct LevelFileInfo *lfi_to)
2685 lfi_to->nr = lfi_from->nr;
2686 lfi_to->type = lfi_from->type;
2687 lfi_to->packed = lfi_from->packed;
2689 setString(&lfi_to->basename, lfi_from->basename);
2690 setString(&lfi_to->filename, lfi_from->filename);
2693 // ----------------------------------------------------------------------------
2694 // functions for loading R'n'D level
2695 // ----------------------------------------------------------------------------
2697 int getMappedElement(int element)
2699 // remap some (historic, now obsolete) elements
2703 case EL_PLAYER_OBSOLETE:
2704 element = EL_PLAYER_1;
2707 case EL_KEY_OBSOLETE:
2711 case EL_EM_KEY_1_FILE_OBSOLETE:
2712 element = EL_EM_KEY_1;
2715 case EL_EM_KEY_2_FILE_OBSOLETE:
2716 element = EL_EM_KEY_2;
2719 case EL_EM_KEY_3_FILE_OBSOLETE:
2720 element = EL_EM_KEY_3;
2723 case EL_EM_KEY_4_FILE_OBSOLETE:
2724 element = EL_EM_KEY_4;
2727 case EL_ENVELOPE_OBSOLETE:
2728 element = EL_ENVELOPE_1;
2736 if (element >= NUM_FILE_ELEMENTS)
2738 Warn("invalid level element %d", element);
2740 element = EL_UNKNOWN;
2748 static int getMappedElementByVersion(int element, int game_version)
2750 // remap some elements due to certain game version
2752 if (game_version <= VERSION_IDENT(2,2,0,0))
2754 // map game font elements
2755 element = (element == EL_CHAR('[') ? EL_CHAR_AUMLAUT :
2756 element == EL_CHAR('\\') ? EL_CHAR_OUMLAUT :
2757 element == EL_CHAR(']') ? EL_CHAR_UUMLAUT :
2758 element == EL_CHAR('^') ? EL_CHAR_COPYRIGHT : element);
2761 if (game_version < VERSION_IDENT(3,0,0,0))
2763 // map Supaplex gravity tube elements
2764 element = (element == EL_SP_GRAVITY_PORT_LEFT ? EL_SP_PORT_LEFT :
2765 element == EL_SP_GRAVITY_PORT_RIGHT ? EL_SP_PORT_RIGHT :
2766 element == EL_SP_GRAVITY_PORT_UP ? EL_SP_PORT_UP :
2767 element == EL_SP_GRAVITY_PORT_DOWN ? EL_SP_PORT_DOWN :
2774 static int LoadLevel_VERS(File *file, int chunk_size, struct LevelInfo *level)
2776 level->file_version = getFileVersion(file);
2777 level->game_version = getFileVersion(file);
2782 static int LoadLevel_DATE(File *file, int chunk_size, struct LevelInfo *level)
2784 level->creation_date.year = getFile16BitBE(file);
2785 level->creation_date.month = getFile8Bit(file);
2786 level->creation_date.day = getFile8Bit(file);
2788 level->creation_date.src = DATE_SRC_LEVELFILE;
2793 static int LoadLevel_HEAD(File *file, int chunk_size, struct LevelInfo *level)
2795 int initial_player_stepsize;
2796 int initial_player_gravity;
2799 level->fieldx = getFile8Bit(file);
2800 level->fieldy = getFile8Bit(file);
2802 level->time = getFile16BitBE(file);
2803 level->gems_needed = getFile16BitBE(file);
2805 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
2806 level->name[i] = getFile8Bit(file);
2807 level->name[MAX_LEVEL_NAME_LEN] = 0;
2809 for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
2810 level->score[i] = getFile8Bit(file);
2812 level->num_yamyam_contents = STD_ELEMENT_CONTENTS;
2813 for (i = 0; i < STD_ELEMENT_CONTENTS; i++)
2814 for (y = 0; y < 3; y++)
2815 for (x = 0; x < 3; x++)
2816 level->yamyam_content[i].e[x][y] = getMappedElement(getFile8Bit(file));
2818 level->amoeba_speed = getFile8Bit(file);
2819 level->time_magic_wall = getFile8Bit(file);
2820 level->time_wheel = getFile8Bit(file);
2821 level->amoeba_content = getMappedElement(getFile8Bit(file));
2823 initial_player_stepsize = (getFile8Bit(file) == 1 ? STEPSIZE_FAST :
2826 for (i = 0; i < MAX_PLAYERS; i++)
2827 level->initial_player_stepsize[i] = initial_player_stepsize;
2829 initial_player_gravity = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2831 for (i = 0; i < MAX_PLAYERS; i++)
2832 level->initial_player_gravity[i] = initial_player_gravity;
2834 level->encoding_16bit_field = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2835 level->em_slippery_gems = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2837 level->use_custom_template = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2839 level->block_last_field = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2840 level->sp_block_last_field = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2841 level->can_move_into_acid_bits = getFile32BitBE(file);
2842 level->dont_collide_with_bits = getFile8Bit(file);
2844 level->use_spring_bug = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2845 level->use_step_counter = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2847 level->instant_relocation = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2848 level->can_pass_to_walkable = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2849 level->grow_into_diggable = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2851 level->game_engine_type = getFile8Bit(file);
2853 ReadUnusedBytesFromFile(file, LEVEL_CHUNK_HEAD_UNUSED);
2858 static int LoadLevel_NAME(File *file, int chunk_size, struct LevelInfo *level)
2862 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
2863 level->name[i] = getFile8Bit(file);
2864 level->name[MAX_LEVEL_NAME_LEN] = 0;
2869 static int LoadLevel_AUTH(File *file, int chunk_size, struct LevelInfo *level)
2873 for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
2874 level->author[i] = getFile8Bit(file);
2875 level->author[MAX_LEVEL_AUTHOR_LEN] = 0;
2880 static int LoadLevel_BODY(File *file, int chunk_size, struct LevelInfo *level)
2883 int chunk_size_expected = level->fieldx * level->fieldy;
2885 /* Note: "chunk_size" was wrong before version 2.0 when elements are
2886 stored with 16-bit encoding (and should be twice as big then).
2887 Even worse, playfield data was stored 16-bit when only yamyam content
2888 contained 16-bit elements and vice versa. */
2890 if (level->encoding_16bit_field && level->file_version >= FILE_VERSION_2_0)
2891 chunk_size_expected *= 2;
2893 if (chunk_size_expected != chunk_size)
2895 ReadUnusedBytesFromFile(file, chunk_size);
2896 return chunk_size_expected;
2899 for (y = 0; y < level->fieldy; y++)
2900 for (x = 0; x < level->fieldx; x++)
2901 level->field[x][y] =
2902 getMappedElement(level->encoding_16bit_field ? getFile16BitBE(file) :
2907 static int LoadLevel_CONT(File *file, int chunk_size, struct LevelInfo *level)
2910 int header_size = 4;
2911 int content_size = MAX_ELEMENT_CONTENTS * 3 * 3;
2912 int chunk_size_expected = header_size + content_size;
2914 /* Note: "chunk_size" was wrong before version 2.0 when elements are
2915 stored with 16-bit encoding (and should be twice as big then).
2916 Even worse, playfield data was stored 16-bit when only yamyam content
2917 contained 16-bit elements and vice versa. */
2919 if (level->encoding_16bit_field && level->file_version >= FILE_VERSION_2_0)
2920 chunk_size_expected += content_size;
2922 if (chunk_size_expected != chunk_size)
2924 ReadUnusedBytesFromFile(file, chunk_size);
2925 return chunk_size_expected;
2929 level->num_yamyam_contents = getFile8Bit(file);
2933 // correct invalid number of content fields -- should never happen
2934 if (level->num_yamyam_contents < 1 ||
2935 level->num_yamyam_contents > MAX_ELEMENT_CONTENTS)
2936 level->num_yamyam_contents = STD_ELEMENT_CONTENTS;
2938 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
2939 for (y = 0; y < 3; y++)
2940 for (x = 0; x < 3; x++)
2941 level->yamyam_content[i].e[x][y] =
2942 getMappedElement(level->encoding_16bit_field ?
2943 getFile16BitBE(file) : getFile8Bit(file));
2947 static int LoadLevel_CNT2(File *file, int chunk_size, struct LevelInfo *level)
2952 int content_array[MAX_ELEMENT_CONTENTS][3][3];
2954 element = getMappedElement(getFile16BitBE(file));
2955 num_contents = getFile8Bit(file);
2957 getFile8Bit(file); // content x size (unused)
2958 getFile8Bit(file); // content y size (unused)
2960 ReadUnusedBytesFromFile(file, LEVEL_CHUNK_CNT2_UNUSED);
2962 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
2963 for (y = 0; y < 3; y++)
2964 for (x = 0; x < 3; x++)
2965 content_array[i][x][y] = getMappedElement(getFile16BitBE(file));
2967 // correct invalid number of content fields -- should never happen
2968 if (num_contents < 1 || num_contents > MAX_ELEMENT_CONTENTS)
2969 num_contents = STD_ELEMENT_CONTENTS;
2971 if (element == EL_YAMYAM)
2973 level->num_yamyam_contents = num_contents;
2975 for (i = 0; i < num_contents; i++)
2976 for (y = 0; y < 3; y++)
2977 for (x = 0; x < 3; x++)
2978 level->yamyam_content[i].e[x][y] = content_array[i][x][y];
2980 else if (element == EL_BD_AMOEBA)
2982 level->amoeba_content = content_array[0][0][0];
2986 Warn("cannot load content for element '%d'", element);
2992 static int LoadLevel_CNT3(File *file, int chunk_size, struct LevelInfo *level)
2998 int chunk_size_expected;
3000 element = getMappedElement(getFile16BitBE(file));
3001 if (!IS_ENVELOPE(element))
3002 element = EL_ENVELOPE_1;
3004 envelope_nr = element - EL_ENVELOPE_1;
3006 envelope_len = getFile16BitBE(file);
3008 level->envelope[envelope_nr].xsize = getFile8Bit(file);
3009 level->envelope[envelope_nr].ysize = getFile8Bit(file);
3011 ReadUnusedBytesFromFile(file, LEVEL_CHUNK_CNT3_UNUSED);
3013 chunk_size_expected = LEVEL_CHUNK_CNT3_SIZE(envelope_len);
3014 if (chunk_size_expected != chunk_size)
3016 ReadUnusedBytesFromFile(file, chunk_size - LEVEL_CHUNK_CNT3_HEADER);
3017 return chunk_size_expected;
3020 for (i = 0; i < envelope_len; i++)
3021 level->envelope[envelope_nr].text[i] = getFile8Bit(file);
3026 static int LoadLevel_CUS1(File *file, int chunk_size, struct LevelInfo *level)
3028 int num_changed_custom_elements = getFile16BitBE(file);
3029 int chunk_size_expected = 2 + num_changed_custom_elements * 6;
3032 if (chunk_size_expected != chunk_size)
3034 ReadUnusedBytesFromFile(file, chunk_size - 2);
3035 return chunk_size_expected;
3038 for (i = 0; i < num_changed_custom_elements; i++)
3040 int element = getMappedElement(getFile16BitBE(file));
3041 int properties = getFile32BitBE(file);
3043 if (IS_CUSTOM_ELEMENT(element))
3044 element_info[element].properties[EP_BITFIELD_BASE_NR] = properties;
3046 Warn("invalid custom element number %d", element);
3048 // older game versions that wrote level files with CUS1 chunks used
3049 // different default push delay values (not yet stored in level file)
3050 element_info[element].push_delay_fixed = 2;
3051 element_info[element].push_delay_random = 8;
3054 level->file_has_custom_elements = TRUE;
3059 static int LoadLevel_CUS2(File *file, int chunk_size, struct LevelInfo *level)
3061 int num_changed_custom_elements = getFile16BitBE(file);
3062 int chunk_size_expected = 2 + num_changed_custom_elements * 4;
3065 if (chunk_size_expected != chunk_size)
3067 ReadUnusedBytesFromFile(file, chunk_size - 2);
3068 return chunk_size_expected;
3071 for (i = 0; i < num_changed_custom_elements; i++)
3073 int element = getMappedElement(getFile16BitBE(file));
3074 int custom_target_element = getMappedElement(getFile16BitBE(file));
3076 if (IS_CUSTOM_ELEMENT(element))
3077 element_info[element].change->target_element = custom_target_element;
3079 Warn("invalid custom element number %d", element);
3082 level->file_has_custom_elements = TRUE;
3087 static int LoadLevel_CUS3(File *file, int chunk_size, struct LevelInfo *level)
3089 int num_changed_custom_elements = getFile16BitBE(file);
3090 int chunk_size_expected = LEVEL_CHUNK_CUS3_SIZE(num_changed_custom_elements);
3093 if (chunk_size_expected != chunk_size)
3095 ReadUnusedBytesFromFile(file, chunk_size - 2);
3096 return chunk_size_expected;
3099 for (i = 0; i < num_changed_custom_elements; i++)
3101 int element = getMappedElement(getFile16BitBE(file));
3102 struct ElementInfo *ei = &element_info[element];
3103 unsigned int event_bits;
3105 if (!IS_CUSTOM_ELEMENT(element))
3107 Warn("invalid custom element number %d", element);
3109 element = EL_INTERNAL_DUMMY;
3112 for (j = 0; j < MAX_ELEMENT_NAME_LEN; j++)
3113 ei->description[j] = getFile8Bit(file);
3114 ei->description[MAX_ELEMENT_NAME_LEN] = 0;
3116 ei->properties[EP_BITFIELD_BASE_NR] = getFile32BitBE(file);
3118 // some free bytes for future properties and padding
3119 ReadUnusedBytesFromFile(file, 7);
3121 ei->use_gfx_element = getFile8Bit(file);
3122 ei->gfx_element_initial = getMappedElement(getFile16BitBE(file));
3124 ei->collect_score_initial = getFile8Bit(file);
3125 ei->collect_count_initial = getFile8Bit(file);
3127 ei->push_delay_fixed = getFile16BitBE(file);
3128 ei->push_delay_random = getFile16BitBE(file);
3129 ei->move_delay_fixed = getFile16BitBE(file);
3130 ei->move_delay_random = getFile16BitBE(file);
3132 ei->move_pattern = getFile16BitBE(file);
3133 ei->move_direction_initial = getFile8Bit(file);
3134 ei->move_stepsize = getFile8Bit(file);
3136 for (y = 0; y < 3; y++)
3137 for (x = 0; x < 3; x++)
3138 ei->content.e[x][y] = getMappedElement(getFile16BitBE(file));
3140 // bits 0 - 31 of "has_event[]"
3141 event_bits = getFile32BitBE(file);
3142 for (j = 0; j < MIN(NUM_CHANGE_EVENTS, 32); j++)
3143 if (event_bits & (1u << j))
3144 ei->change->has_event[j] = TRUE;
3146 ei->change->target_element = getMappedElement(getFile16BitBE(file));
3148 ei->change->delay_fixed = getFile16BitBE(file);
3149 ei->change->delay_random = getFile16BitBE(file);
3150 ei->change->delay_frames = getFile16BitBE(file);
3152 ei->change->initial_trigger_element = getMappedElement(getFile16BitBE(file));
3154 ei->change->explode = getFile8Bit(file);
3155 ei->change->use_target_content = getFile8Bit(file);
3156 ei->change->only_if_complete = getFile8Bit(file);
3157 ei->change->use_random_replace = getFile8Bit(file);
3159 ei->change->random_percentage = getFile8Bit(file);
3160 ei->change->replace_when = getFile8Bit(file);
3162 for (y = 0; y < 3; y++)
3163 for (x = 0; x < 3; x++)
3164 ei->change->target_content.e[x][y] =
3165 getMappedElement(getFile16BitBE(file));
3167 ei->slippery_type = getFile8Bit(file);
3169 // some free bytes for future properties and padding
3170 ReadUnusedBytesFromFile(file, LEVEL_CPART_CUS3_UNUSED);
3172 // mark that this custom element has been modified
3173 ei->modified_settings = TRUE;
3176 level->file_has_custom_elements = TRUE;
3181 static int LoadLevel_CUS4(File *file, int chunk_size, struct LevelInfo *level)
3183 struct ElementInfo *ei;
3184 int chunk_size_expected;
3188 // ---------- custom element base property values (96 bytes) ----------------
3190 element = getMappedElement(getFile16BitBE(file));
3192 if (!IS_CUSTOM_ELEMENT(element))
3194 Warn("invalid custom element number %d", element);
3196 ReadUnusedBytesFromFile(file, chunk_size - 2);
3201 ei = &element_info[element];
3203 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
3204 ei->description[i] = getFile8Bit(file);
3205 ei->description[MAX_ELEMENT_NAME_LEN] = 0;
3207 ei->properties[EP_BITFIELD_BASE_NR] = getFile32BitBE(file);
3209 ReadUnusedBytesFromFile(file, 4); // reserved for more base properties
3211 ei->num_change_pages = getFile8Bit(file);
3213 chunk_size_expected = LEVEL_CHUNK_CUS4_SIZE(ei->num_change_pages);
3214 if (chunk_size_expected != chunk_size)
3216 ReadUnusedBytesFromFile(file, chunk_size - 43);
3217 return chunk_size_expected;
3220 ei->ce_value_fixed_initial = getFile16BitBE(file);
3221 ei->ce_value_random_initial = getFile16BitBE(file);
3222 ei->use_last_ce_value = getFile8Bit(file);
3224 ei->use_gfx_element = getFile8Bit(file);
3225 ei->gfx_element_initial = getMappedElement(getFile16BitBE(file));
3227 ei->collect_score_initial = getFile8Bit(file);
3228 ei->collect_count_initial = getFile8Bit(file);
3230 ei->drop_delay_fixed = getFile8Bit(file);
3231 ei->push_delay_fixed = getFile8Bit(file);
3232 ei->drop_delay_random = getFile8Bit(file);
3233 ei->push_delay_random = getFile8Bit(file);
3234 ei->move_delay_fixed = getFile16BitBE(file);
3235 ei->move_delay_random = getFile16BitBE(file);
3237 // bits 0 - 15 of "move_pattern" ...
3238 ei->move_pattern = getFile16BitBE(file);
3239 ei->move_direction_initial = getFile8Bit(file);
3240 ei->move_stepsize = getFile8Bit(file);
3242 ei->slippery_type = getFile8Bit(file);
3244 for (y = 0; y < 3; y++)
3245 for (x = 0; x < 3; x++)
3246 ei->content.e[x][y] = getMappedElement(getFile16BitBE(file));
3248 ei->move_enter_element = getMappedElement(getFile16BitBE(file));
3249 ei->move_leave_element = getMappedElement(getFile16BitBE(file));
3250 ei->move_leave_type = getFile8Bit(file);
3252 // ... bits 16 - 31 of "move_pattern" (not nice, but downward compatible)
3253 ei->move_pattern |= (getFile16BitBE(file) << 16);
3255 ei->access_direction = getFile8Bit(file);
3257 ei->explosion_delay = getFile8Bit(file);
3258 ei->ignition_delay = getFile8Bit(file);
3259 ei->explosion_type = getFile8Bit(file);
3261 // some free bytes for future custom property values and padding
3262 ReadUnusedBytesFromFile(file, 1);
3264 // ---------- change page property values (48 bytes) ------------------------
3266 setElementChangePages(ei, ei->num_change_pages);
3268 for (i = 0; i < ei->num_change_pages; i++)
3270 struct ElementChangeInfo *change = &ei->change_page[i];
3271 unsigned int event_bits;
3273 // always start with reliable default values
3274 setElementChangeInfoToDefaults(change);
3276 // bits 0 - 31 of "has_event[]" ...
3277 event_bits = getFile32BitBE(file);
3278 for (j = 0; j < MIN(NUM_CHANGE_EVENTS, 32); j++)
3279 if (event_bits & (1u << j))
3280 change->has_event[j] = TRUE;
3282 change->target_element = getMappedElement(getFile16BitBE(file));
3284 change->delay_fixed = getFile16BitBE(file);
3285 change->delay_random = getFile16BitBE(file);
3286 change->delay_frames = getFile16BitBE(file);
3288 change->initial_trigger_element = getMappedElement(getFile16BitBE(file));
3290 change->explode = getFile8Bit(file);
3291 change->use_target_content = getFile8Bit(file);
3292 change->only_if_complete = getFile8Bit(file);
3293 change->use_random_replace = getFile8Bit(file);
3295 change->random_percentage = getFile8Bit(file);
3296 change->replace_when = getFile8Bit(file);
3298 for (y = 0; y < 3; y++)
3299 for (x = 0; x < 3; x++)
3300 change->target_content.e[x][y]= getMappedElement(getFile16BitBE(file));
3302 change->can_change = getFile8Bit(file);
3304 change->trigger_side = getFile8Bit(file);
3306 change->trigger_player = getFile8Bit(file);
3307 change->trigger_page = getFile8Bit(file);
3309 change->trigger_page = (change->trigger_page == CH_PAGE_ANY_FILE ?
3310 CH_PAGE_ANY : (1 << change->trigger_page));
3312 change->has_action = getFile8Bit(file);
3313 change->action_type = getFile8Bit(file);
3314 change->action_mode = getFile8Bit(file);
3315 change->action_arg = getFile16BitBE(file);
3317 // ... bits 32 - 39 of "has_event[]" (not nice, but downward compatible)
3318 event_bits = getFile8Bit(file);
3319 for (j = 32; j < NUM_CHANGE_EVENTS; j++)
3320 if (event_bits & (1u << (j - 32)))
3321 change->has_event[j] = TRUE;
3324 // mark this custom element as modified
3325 ei->modified_settings = TRUE;
3327 level->file_has_custom_elements = TRUE;
3332 static int LoadLevel_GRP1(File *file, int chunk_size, struct LevelInfo *level)
3334 struct ElementInfo *ei;
3335 struct ElementGroupInfo *group;
3339 element = getMappedElement(getFile16BitBE(file));
3341 if (!IS_GROUP_ELEMENT(element))
3343 Warn("invalid group element number %d", element);
3345 ReadUnusedBytesFromFile(file, chunk_size - 2);
3350 ei = &element_info[element];
3352 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
3353 ei->description[i] = getFile8Bit(file);
3354 ei->description[MAX_ELEMENT_NAME_LEN] = 0;
3356 group = element_info[element].group;
3358 group->num_elements = getFile8Bit(file);
3360 ei->use_gfx_element = getFile8Bit(file);
3361 ei->gfx_element_initial = getMappedElement(getFile16BitBE(file));
3363 group->choice_mode = getFile8Bit(file);
3365 // some free bytes for future values and padding
3366 ReadUnusedBytesFromFile(file, 3);
3368 for (i = 0; i < MAX_ELEMENTS_IN_GROUP; i++)
3369 group->element[i] = getMappedElement(getFile16BitBE(file));
3371 // mark this group element as modified
3372 element_info[element].modified_settings = TRUE;
3374 level->file_has_custom_elements = TRUE;
3379 static int LoadLevel_MicroChunk(File *file, struct LevelFileConfigInfo *conf,
3380 int element, int real_element)
3382 int micro_chunk_size = 0;
3383 int conf_type = getFile8Bit(file);
3384 int byte_mask = conf_type & CONF_MASK_BYTES;
3385 boolean element_found = FALSE;
3388 micro_chunk_size += 1;
3390 if (byte_mask == CONF_MASK_MULTI_BYTES)
3392 int num_bytes = getFile16BitBE(file);
3393 byte *buffer = checked_malloc(num_bytes);
3395 ReadBytesFromFile(file, buffer, num_bytes);
3397 for (i = 0; conf[i].data_type != -1; i++)
3399 if (conf[i].element == element &&
3400 conf[i].conf_type == conf_type)
3402 int data_type = conf[i].data_type;
3403 int num_entities = num_bytes / CONF_ENTITY_NUM_BYTES(data_type);
3404 int max_num_entities = conf[i].max_num_entities;
3406 if (num_entities > max_num_entities)
3408 Warn("truncating number of entities for element %d from %d to %d",
3409 element, num_entities, max_num_entities);
3411 num_entities = max_num_entities;
3414 if (num_entities == 0 && (data_type == TYPE_ELEMENT_LIST ||
3415 data_type == TYPE_CONTENT_LIST))
3417 // for element and content lists, zero entities are not allowed
3418 Warn("found empty list of entities for element %d", element);
3420 // do not set "num_entities" here to prevent reading behind buffer
3422 *(int *)(conf[i].num_entities) = 1; // at least one is required
3426 *(int *)(conf[i].num_entities) = num_entities;
3429 element_found = TRUE;
3431 if (data_type == TYPE_STRING)
3433 char *string = (char *)(conf[i].value);
3436 for (j = 0; j < max_num_entities; j++)
3437 string[j] = (j < num_entities ? buffer[j] : '\0');
3439 else if (data_type == TYPE_ELEMENT_LIST)
3441 int *element_array = (int *)(conf[i].value);
3444 for (j = 0; j < num_entities; j++)
3446 getMappedElement(CONF_ELEMENTS_ELEMENT(buffer, j));
3448 else if (data_type == TYPE_CONTENT_LIST)
3450 struct Content *content= (struct Content *)(conf[i].value);
3453 for (c = 0; c < num_entities; c++)
3454 for (y = 0; y < 3; y++)
3455 for (x = 0; x < 3; x++)
3456 content[c].e[x][y] =
3457 getMappedElement(CONF_CONTENTS_ELEMENT(buffer, c, x, y));
3460 element_found = FALSE;
3466 checked_free(buffer);
3468 micro_chunk_size += 2 + num_bytes;
3470 else // constant size configuration data (1, 2 or 4 bytes)
3472 int value = (byte_mask == CONF_MASK_1_BYTE ? getFile8Bit (file) :
3473 byte_mask == CONF_MASK_2_BYTE ? getFile16BitBE(file) :
3474 byte_mask == CONF_MASK_4_BYTE ? getFile32BitBE(file) : 0);
3476 for (i = 0; conf[i].data_type != -1; i++)
3478 if (conf[i].element == element &&
3479 conf[i].conf_type == conf_type)
3481 int data_type = conf[i].data_type;
3483 if (data_type == TYPE_ELEMENT)
3484 value = getMappedElement(value);
3486 if (data_type == TYPE_BOOLEAN)
3487 *(boolean *)(conf[i].value) = (value ? TRUE : FALSE);
3489 *(int *) (conf[i].value) = value;
3491 element_found = TRUE;
3497 micro_chunk_size += CONF_VALUE_NUM_BYTES(byte_mask);
3502 char *error_conf_chunk_bytes =
3503 (byte_mask == CONF_MASK_1_BYTE ? "CONF_VALUE_8_BIT" :
3504 byte_mask == CONF_MASK_2_BYTE ? "CONF_VALUE_16_BIT" :
3505 byte_mask == CONF_MASK_4_BYTE ? "CONF_VALUE_32_BIT" :"CONF_VALUE_BYTES");
3506 int error_conf_chunk_token = conf_type & CONF_MASK_TOKEN;
3507 int error_element = real_element;
3509 Warn("cannot load micro chunk '%s(%d)' value for element %d ['%s']",
3510 error_conf_chunk_bytes, error_conf_chunk_token,
3511 error_element, EL_NAME(error_element));
3514 return micro_chunk_size;
3517 static int LoadLevel_INFO(File *file, int chunk_size, struct LevelInfo *level)
3519 int real_chunk_size = 0;
3521 li = *level; // copy level data into temporary buffer
3523 while (!checkEndOfFile(file))
3525 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_INFO, -1, -1);
3527 if (real_chunk_size >= chunk_size)
3531 *level = li; // copy temporary buffer back to level data
3533 return real_chunk_size;
3536 static int LoadLevel_CONF(File *file, int chunk_size, struct LevelInfo *level)
3538 int real_chunk_size = 0;
3540 li = *level; // copy level data into temporary buffer
3542 while (!checkEndOfFile(file))
3544 int element = getMappedElement(getFile16BitBE(file));
3546 real_chunk_size += 2;
3547 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_CONF,
3549 if (real_chunk_size >= chunk_size)
3553 *level = li; // copy temporary buffer back to level data
3555 return real_chunk_size;
3558 static int LoadLevel_ELEM(File *file, int chunk_size, struct LevelInfo *level)
3560 int real_chunk_size = 0;
3562 li = *level; // copy level data into temporary buffer
3564 while (!checkEndOfFile(file))
3566 int element = getMappedElement(getFile16BitBE(file));
3568 real_chunk_size += 2;
3569 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_ELEM,
3571 if (real_chunk_size >= chunk_size)
3575 *level = li; // copy temporary buffer back to level data
3577 return real_chunk_size;
3580 static int LoadLevel_NOTE(File *file, int chunk_size, struct LevelInfo *level)
3582 int element = getMappedElement(getFile16BitBE(file));
3583 int envelope_nr = element - EL_ENVELOPE_1;
3584 int real_chunk_size = 2;
3586 xx_envelope = level->envelope[envelope_nr]; // copy into temporary buffer
3588 while (!checkEndOfFile(file))
3590 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_NOTE,
3593 if (real_chunk_size >= chunk_size)
3597 level->envelope[envelope_nr] = xx_envelope; // copy from temporary buffer
3599 return real_chunk_size;
3602 static int LoadLevel_CUSX(File *file, int chunk_size, struct LevelInfo *level)
3604 int element = getMappedElement(getFile16BitBE(file));
3605 int real_chunk_size = 2;
3606 struct ElementInfo *ei = &element_info[element];
3609 xx_ei = *ei; // copy element data into temporary buffer
3611 xx_ei.num_change_pages = -1;
3613 while (!checkEndOfFile(file))
3615 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_CUSX_base,
3617 if (xx_ei.num_change_pages != -1)
3620 if (real_chunk_size >= chunk_size)
3626 if (ei->num_change_pages == -1)
3628 Warn("LoadLevel_CUSX(): missing 'num_change_pages' for '%s'",
3631 ei->num_change_pages = 1;
3633 setElementChangePages(ei, 1);
3634 setElementChangeInfoToDefaults(ei->change);
3636 return real_chunk_size;
3639 // initialize number of change pages stored for this custom element
3640 setElementChangePages(ei, ei->num_change_pages);
3641 for (i = 0; i < ei->num_change_pages; i++)
3642 setElementChangeInfoToDefaults(&ei->change_page[i]);
3644 // start with reading properties for the first change page
3645 xx_current_change_page = 0;
3647 while (!checkEndOfFile(file))
3649 // level file might contain invalid change page number
3650 if (xx_current_change_page >= ei->num_change_pages)
3653 struct ElementChangeInfo *change = &ei->change_page[xx_current_change_page];
3655 xx_change = *change; // copy change data into temporary buffer
3657 resetEventBits(); // reset bits; change page might have changed
3659 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_CUSX_change,
3662 *change = xx_change;
3664 setEventFlagsFromEventBits(change);
3666 if (real_chunk_size >= chunk_size)
3670 level->file_has_custom_elements = TRUE;
3672 return real_chunk_size;
3675 static int LoadLevel_GRPX(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];
3680 struct ElementGroupInfo *group = ei->group;
3685 xx_ei = *ei; // copy element data into temporary buffer
3686 xx_group = *group; // copy group data into temporary buffer
3688 while (!checkEndOfFile(file))
3690 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_GRPX,
3693 if (real_chunk_size >= chunk_size)
3700 level->file_has_custom_elements = TRUE;
3702 return real_chunk_size;
3705 static int LoadLevel_EMPX(File *file, int chunk_size, struct LevelInfo *level)
3707 int element = getMappedElement(getFile16BitBE(file));
3708 int real_chunk_size = 2;
3709 struct ElementInfo *ei = &element_info[element];
3711 xx_ei = *ei; // copy element data into temporary buffer
3713 while (!checkEndOfFile(file))
3715 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_EMPX,
3718 if (real_chunk_size >= chunk_size)
3724 level->file_has_custom_elements = TRUE;
3726 return real_chunk_size;
3729 static void LoadLevelFromFileInfo_RND(struct LevelInfo *level,
3730 struct LevelFileInfo *level_file_info,
3731 boolean level_info_only)
3733 char *filename = level_file_info->filename;
3734 char cookie[MAX_LINE_LEN];
3735 char chunk_name[CHUNK_ID_LEN + 1];
3739 if (!(file = openFile(filename, MODE_READ)))
3741 level->no_valid_file = TRUE;
3742 level->no_level_file = TRUE;
3744 if (level_info_only)
3747 Warn("cannot read level '%s' -- using empty level", filename);
3749 if (!setup.editor.use_template_for_new_levels)
3752 // if level file not found, try to initialize level data from template
3753 filename = getGlobalLevelTemplateFilename();
3755 if (!(file = openFile(filename, MODE_READ)))
3758 // default: for empty levels, use level template for custom elements
3759 level->use_custom_template = TRUE;
3761 level->no_valid_file = FALSE;
3764 getFileChunkBE(file, chunk_name, NULL);
3765 if (strEqual(chunk_name, "RND1"))
3767 getFile32BitBE(file); // not used
3769 getFileChunkBE(file, chunk_name, NULL);
3770 if (!strEqual(chunk_name, "CAVE"))
3772 level->no_valid_file = TRUE;
3774 Warn("unknown format of level file '%s'", filename);
3781 else // check for pre-2.0 file format with cookie string
3783 strcpy(cookie, chunk_name);
3784 if (getStringFromFile(file, &cookie[4], MAX_LINE_LEN - 4) == NULL)
3786 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
3787 cookie[strlen(cookie) - 1] = '\0';
3789 if (!checkCookieString(cookie, LEVEL_COOKIE_TMPL))
3791 level->no_valid_file = TRUE;
3793 Warn("unknown format of level file '%s'", filename);
3800 if ((level->file_version = getFileVersionFromCookieString(cookie)) == -1)
3802 level->no_valid_file = TRUE;
3804 Warn("unsupported version of level file '%s'", filename);
3811 // pre-2.0 level files have no game version, so use file version here
3812 level->game_version = level->file_version;
3815 if (level->file_version < FILE_VERSION_1_2)
3817 // level files from versions before 1.2.0 without chunk structure
3818 LoadLevel_HEAD(file, LEVEL_CHUNK_HEAD_SIZE, level);
3819 LoadLevel_BODY(file, level->fieldx * level->fieldy, level);
3827 int (*loader)(File *, int, struct LevelInfo *);
3831 { "VERS", LEVEL_CHUNK_VERS_SIZE, LoadLevel_VERS },
3832 { "DATE", LEVEL_CHUNK_DATE_SIZE, LoadLevel_DATE },
3833 { "HEAD", LEVEL_CHUNK_HEAD_SIZE, LoadLevel_HEAD },
3834 { "NAME", LEVEL_CHUNK_NAME_SIZE, LoadLevel_NAME },
3835 { "AUTH", LEVEL_CHUNK_AUTH_SIZE, LoadLevel_AUTH },
3836 { "INFO", -1, LoadLevel_INFO },
3837 { "BODY", -1, LoadLevel_BODY },
3838 { "CONT", -1, LoadLevel_CONT },
3839 { "CNT2", LEVEL_CHUNK_CNT2_SIZE, LoadLevel_CNT2 },
3840 { "CNT3", -1, LoadLevel_CNT3 },
3841 { "CUS1", -1, LoadLevel_CUS1 },
3842 { "CUS2", -1, LoadLevel_CUS2 },
3843 { "CUS3", -1, LoadLevel_CUS3 },
3844 { "CUS4", -1, LoadLevel_CUS4 },
3845 { "GRP1", -1, LoadLevel_GRP1 },
3846 { "CONF", -1, LoadLevel_CONF },
3847 { "ELEM", -1, LoadLevel_ELEM },
3848 { "NOTE", -1, LoadLevel_NOTE },
3849 { "CUSX", -1, LoadLevel_CUSX },
3850 { "GRPX", -1, LoadLevel_GRPX },
3851 { "EMPX", -1, LoadLevel_EMPX },
3856 while (getFileChunkBE(file, chunk_name, &chunk_size))
3860 while (chunk_info[i].name != NULL &&
3861 !strEqual(chunk_name, chunk_info[i].name))
3864 if (chunk_info[i].name == NULL)
3866 Warn("unknown chunk '%s' in level file '%s'",
3867 chunk_name, filename);
3869 ReadUnusedBytesFromFile(file, chunk_size);
3871 else if (chunk_info[i].size != -1 &&
3872 chunk_info[i].size != chunk_size)
3874 Warn("wrong size (%d) of chunk '%s' in level file '%s'",
3875 chunk_size, chunk_name, filename);
3877 ReadUnusedBytesFromFile(file, chunk_size);
3881 // call function to load this level chunk
3882 int chunk_size_expected =
3883 (chunk_info[i].loader)(file, chunk_size, level);
3885 if (chunk_size_expected < 0)
3887 Warn("error reading chunk '%s' in level file '%s'",
3888 chunk_name, filename);
3893 // the size of some chunks cannot be checked before reading other
3894 // chunks first (like "HEAD" and "BODY") that contain some header
3895 // information, so check them here
3896 if (chunk_size_expected != chunk_size)
3898 Warn("wrong size (%d) of chunk '%s' in level file '%s'",
3899 chunk_size, chunk_name, filename);
3911 // ----------------------------------------------------------------------------
3912 // functions for loading BD level
3913 // ----------------------------------------------------------------------------
3915 static void CopyNativeLevel_RND_to_BD(struct LevelInfo *level)
3917 struct LevelInfo_BD *level_bd = level->native_bd_level;
3918 GdCave *cave = NULL; // will be changed below
3919 int cave_w = MIN(level->fieldx, MAX_PLAYFIELD_WIDTH);
3920 int cave_h = MIN(level->fieldy, MAX_PLAYFIELD_HEIGHT);
3923 setLevelInfoToDefaults_BD_Ext(cave_w, cave_h);
3925 // cave and map newly allocated when set to defaults above
3926 cave = level_bd->cave;
3929 cave->intermission = level->bd_intermission;
3932 cave->level_time[0] = level->time;
3933 cave->level_diamonds[0] = level->gems_needed;
3936 cave->scheduling = level->bd_scheduling_type;
3937 cave->pal_timing = level->bd_pal_timing;
3938 cave->level_speed[0] = level->bd_cycle_delay_ms;
3939 cave->level_ckdelay[0] = level->bd_cycle_delay_c64;
3940 cave->level_hatching_delay_frame[0] = level->bd_hatching_delay_cycles;
3941 cave->level_hatching_delay_time[0] = level->bd_hatching_delay_seconds;
3944 cave->level_timevalue[0] = level->score[SC_TIME_BONUS];
3945 cave->diamond_value = level->score[SC_EMERALD];
3946 cave->extra_diamond_value = level->score[SC_DIAMOND_EXTRA];
3948 // compatibility settings
3949 cave->lineshift = level->bd_line_shifting_borders;
3950 cave->border_scan_first_and_last = level->bd_scan_first_and_last_row;
3951 cave->short_explosions = level->bd_short_explosions;
3952 cave->gravity_affects_all = level->bd_gravity_affects_all;
3954 // player properties
3955 cave->diagonal_movements = level->bd_diagonal_movements;
3956 cave->active_is_first_found = level->bd_topmost_player_active;
3957 cave->pushing_stone_prob = level->bd_pushing_prob * 10000;
3958 cave->pushing_stone_prob_sweet = level->bd_pushing_prob_with_sweet * 10000;
3959 cave->mega_stones_pushable_with_sweet = level->bd_push_mega_rock_with_sweet;
3960 cave->snap_element = map_element_RND_to_BD(level->bd_snap_element);
3962 // element properties
3963 cave->level_bonus_time[0] = level->bd_clock_extra_time;
3964 cave->voodoo_collects_diamonds = level->bd_voodoo_collects_diamonds;
3965 cave->voodoo_any_hurt_kills_player = level->bd_voodoo_hurt_kills_player;
3966 cave->voodoo_dies_by_stone = level->bd_voodoo_dies_by_rock;
3967 cave->voodoo_disappear_in_explosion = level->bd_voodoo_vanish_by_explosion;
3968 cave->level_penalty_time[0] = level->bd_voodoo_penalty_time;
3969 cave->level_magic_wall_time[0] = level->time_magic_wall;
3970 cave->magic_timer_wait_for_hatching = level->bd_magic_wall_wait_hatching;
3971 cave->magic_wall_stops_amoeba = level->bd_magic_wall_stops_amoeba;
3972 cave->amoeba_timer_wait_for_hatching = level->bd_amoeba_wait_for_hatching;
3973 cave->amoeba_timer_started_immediately= level->bd_amoeba_start_immediately;
3974 cave->amoeba_2_explodes_by_amoeba = level->bd_amoeba_2_explode_by_amoeba;
3975 cave->level_amoeba_threshold[0] = level->bd_amoeba_threshold_too_big;
3976 cave->level_amoeba_time[0] = level->bd_amoeba_slow_growth_time;
3977 cave->amoeba_growth_prob = level->bd_amoeba_slow_growth_rate * 10000;
3978 cave->amoeba_fast_growth_prob = level->bd_amoeba_fast_growth_rate * 10000;
3979 cave->level_amoeba_2_threshold[0] = level->bd_amoeba_2_threshold_too_big;
3980 cave->level_amoeba_2_time[0] = level->bd_amoeba_2_slow_growth_time;
3981 cave->amoeba_2_growth_prob = level->bd_amoeba_2_slow_growth_rate * 10000;
3982 cave->amoeba_2_fast_growth_prob = level->bd_amoeba_2_fast_growth_rate * 10000;
3984 cave->amoeba_too_big_effect = map_element_RND_to_BD(level->bd_amoeba_content_too_big);
3985 cave->amoeba_enclosed_effect = map_element_RND_to_BD(level->bd_amoeba_content_enclosed);
3986 cave->amoeba_2_too_big_effect = map_element_RND_to_BD(level->bd_amoeba_2_content_too_big);
3987 cave->amoeba_2_enclosed_effect = map_element_RND_to_BD(level->bd_amoeba_2_content_enclosed);
3988 cave->amoeba_2_explosion_effect = map_element_RND_to_BD(level->bd_amoeba_2_content_exploding);
3989 cave->amoeba_2_looks_like = map_element_RND_to_BD(level->bd_amoeba_2_content_looks_like);
3991 cave->slime_predictable = level->bd_slime_is_predictable;
3992 cave->slime_correct_random = level->bd_slime_correct_random;
3993 cave->level_slime_permeability[0] = level->bd_slime_permeability_rate * 10000;
3994 cave->level_slime_permeability_c64[0] = level->bd_slime_permeability_bits_c64;
3995 cave->level_slime_seed_c64[0] = level->bd_slime_random_seed_c64;
3996 cave->level_rand[0] = level->bd_cave_random_seed_c64;
3999 strncpy(cave->name, level->name, sizeof(GdString));
4000 cave->name[sizeof(GdString) - 1] = '\0';
4002 // playfield elements
4003 for (x = 0; x < cave->w; x++)
4004 for (y = 0; y < cave->h; y++)
4005 cave->map[y][x] = map_element_RND_to_BD(level->field[x][y]);
4008 static void CopyNativeLevel_BD_to_RND(struct LevelInfo *level)
4010 struct LevelInfo_BD *level_bd = level->native_bd_level;
4011 GdCave *cave = level_bd->cave;
4012 int bd_level_nr = level_bd->level_nr;
4015 level->fieldx = MIN(cave->w, MAX_LEV_FIELDX);
4016 level->fieldy = MIN(cave->h, MAX_LEV_FIELDY);
4019 level->bd_intermission = cave->intermission;
4022 level->time = cave->level_time[bd_level_nr];
4023 level->gems_needed = cave->level_diamonds[bd_level_nr];
4026 level->bd_scheduling_type = cave->scheduling;
4027 level->bd_pal_timing = cave->pal_timing;
4028 level->bd_cycle_delay_ms = cave->level_speed[bd_level_nr];
4029 level->bd_cycle_delay_c64 = cave->level_ckdelay[bd_level_nr];
4030 level->bd_hatching_delay_cycles = cave->level_hatching_delay_frame[bd_level_nr];
4031 level->bd_hatching_delay_seconds = cave->level_hatching_delay_time[bd_level_nr];
4034 level->score[SC_TIME_BONUS] = cave->level_timevalue[bd_level_nr];
4035 level->score[SC_EMERALD] = cave->diamond_value;
4036 level->score[SC_DIAMOND_EXTRA] = cave->extra_diamond_value;
4038 // compatibility settings
4039 level->bd_line_shifting_borders = cave->lineshift;
4040 level->bd_scan_first_and_last_row = cave->border_scan_first_and_last;
4041 level->bd_short_explosions = cave->short_explosions;
4042 level->bd_gravity_affects_all = cave->gravity_affects_all;
4044 // player properties
4045 level->bd_diagonal_movements = cave->diagonal_movements;
4046 level->bd_topmost_player_active = cave->active_is_first_found;
4047 level->bd_pushing_prob = cave->pushing_stone_prob / 10000;
4048 level->bd_pushing_prob_with_sweet = cave->pushing_stone_prob_sweet / 10000;
4049 level->bd_push_mega_rock_with_sweet = cave->mega_stones_pushable_with_sweet;
4050 level->bd_snap_element = map_element_BD_to_RND(cave->snap_element);
4052 // element properties
4053 level->bd_clock_extra_time = cave->level_bonus_time[bd_level_nr];
4054 level->bd_voodoo_collects_diamonds = cave->voodoo_collects_diamonds;
4055 level->bd_voodoo_hurt_kills_player = cave->voodoo_any_hurt_kills_player;
4056 level->bd_voodoo_dies_by_rock = cave->voodoo_dies_by_stone;
4057 level->bd_voodoo_vanish_by_explosion = cave->voodoo_disappear_in_explosion;
4058 level->bd_voodoo_penalty_time = cave->level_penalty_time[bd_level_nr];
4059 level->time_magic_wall = cave->level_magic_wall_time[bd_level_nr];
4060 level->bd_magic_wall_wait_hatching = cave->magic_timer_wait_for_hatching;
4061 level->bd_magic_wall_stops_amoeba = cave->magic_wall_stops_amoeba;
4062 level->bd_amoeba_wait_for_hatching = cave->amoeba_timer_wait_for_hatching;
4063 level->bd_amoeba_start_immediately = cave->amoeba_timer_started_immediately;
4064 level->bd_amoeba_2_explode_by_amoeba = cave->amoeba_2_explodes_by_amoeba;
4065 level->bd_amoeba_threshold_too_big = cave->level_amoeba_threshold[bd_level_nr];
4066 level->bd_amoeba_slow_growth_time = cave->level_amoeba_time[bd_level_nr];
4067 level->bd_amoeba_slow_growth_rate = cave->amoeba_growth_prob / 10000;
4068 level->bd_amoeba_fast_growth_rate = cave->amoeba_fast_growth_prob / 10000;
4069 level->bd_amoeba_2_threshold_too_big = cave->level_amoeba_2_threshold[bd_level_nr];
4070 level->bd_amoeba_2_slow_growth_time = cave->level_amoeba_2_time[bd_level_nr];
4071 level->bd_amoeba_2_slow_growth_rate = cave->amoeba_2_growth_prob / 10000;
4072 level->bd_amoeba_2_fast_growth_rate = cave->amoeba_2_fast_growth_prob / 10000;
4074 level->bd_amoeba_content_too_big = map_element_BD_to_RND(cave->amoeba_too_big_effect);
4075 level->bd_amoeba_content_enclosed = map_element_BD_to_RND(cave->amoeba_enclosed_effect);
4076 level->bd_amoeba_2_content_too_big = map_element_BD_to_RND(cave->amoeba_2_too_big_effect);
4077 level->bd_amoeba_2_content_enclosed = map_element_BD_to_RND(cave->amoeba_2_enclosed_effect);
4078 level->bd_amoeba_2_content_exploding = map_element_BD_to_RND(cave->amoeba_2_explosion_effect);
4079 level->bd_amoeba_2_content_looks_like = map_element_BD_to_RND(cave->amoeba_2_looks_like);
4081 level->bd_slime_is_predictable = cave->slime_predictable;
4082 level->bd_slime_correct_random = cave->slime_correct_random;
4083 level->bd_slime_permeability_rate = cave->level_slime_permeability[bd_level_nr] / 10000;
4084 level->bd_slime_permeability_bits_c64 = cave->level_slime_permeability_c64[bd_level_nr];
4085 level->bd_slime_random_seed_c64 = cave->level_slime_seed_c64[bd_level_nr];
4086 level->bd_cave_random_seed_c64 = cave->level_rand[bd_level_nr];
4089 char *cave_name = getStringPrint("%s / %d", cave->name, bd_level_nr + 1);
4091 strncpy(level->name, cave_name, MAX_LEVEL_NAME_LEN);
4092 level->name[MAX_LEVEL_NAME_LEN] = '\0';
4094 // playfield elements
4095 for (x = 0; x < level->fieldx; x++)
4096 for (y = 0; y < level->fieldy; y++)
4097 level->field[x][y] = map_element_BD_to_RND(cave->map[y][x]);
4099 checked_free(cave_name);
4102 static void setTapeInfoToDefaults(void);
4104 static void CopyNativeTape_BD_to_RND(struct LevelInfo *level)
4106 struct LevelInfo_BD *level_bd = level->native_bd_level;
4107 GdCave *cave = level_bd->cave;
4108 GdReplay *replay = level_bd->replay;
4114 // always start with reliable default values
4115 setTapeInfoToDefaults();
4117 tape.level_nr = level_nr; // (currently not used)
4118 tape.random_seed = replay->seed;
4120 TapeSetDateFromIsoDateString(replay->date);
4123 tape.pos[tape.counter].delay = 0;
4125 tape.bd_replay = TRUE;
4127 // all time calculations only used to display approximate tape time
4128 int cave_speed = cave->speed;
4129 int milliseconds_game = 0;
4130 int milliseconds_elapsed = 20;
4132 for (i = 0; i < replay->movements->len; i++)
4134 int replay_action = replay->movements->data[i];
4135 int tape_action = map_action_BD_to_RND(replay_action);
4136 byte action[MAX_TAPE_ACTIONS] = { tape_action };
4137 boolean success = 0;
4141 success = TapeAddAction(action);
4143 milliseconds_game += milliseconds_elapsed;
4145 if (milliseconds_game >= cave_speed)
4147 milliseconds_game -= cave_speed;
4154 tape.pos[tape.counter].delay = 0;
4155 tape.pos[tape.counter].action[0] = 0;
4159 Warn("BD replay truncated: size exceeds maximum tape size %d", MAX_TAPE_LEN);
4165 TapeHaltRecording();
4169 // ----------------------------------------------------------------------------
4170 // functions for loading EM level
4171 // ----------------------------------------------------------------------------
4173 static void CopyNativeLevel_RND_to_EM(struct LevelInfo *level)
4175 static int ball_xy[8][2] =
4186 struct LevelInfo_EM *level_em = level->native_em_level;
4187 struct CAVE *cav = level_em->cav;
4190 cav->width = MIN(level->fieldx, MAX_PLAYFIELD_WIDTH);
4191 cav->height = MIN(level->fieldy, MAX_PLAYFIELD_HEIGHT);
4193 cav->time_seconds = level->time;
4194 cav->gems_needed = level->gems_needed;
4196 cav->emerald_score = level->score[SC_EMERALD];
4197 cav->diamond_score = level->score[SC_DIAMOND];
4198 cav->alien_score = level->score[SC_ROBOT];
4199 cav->tank_score = level->score[SC_SPACESHIP];
4200 cav->bug_score = level->score[SC_BUG];
4201 cav->eater_score = level->score[SC_YAMYAM];
4202 cav->nut_score = level->score[SC_NUT];
4203 cav->dynamite_score = level->score[SC_DYNAMITE];
4204 cav->key_score = level->score[SC_KEY];
4205 cav->exit_score = level->score[SC_TIME_BONUS];
4207 cav->num_eater_arrays = level->num_yamyam_contents;
4209 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
4210 for (y = 0; y < 3; y++)
4211 for (x = 0; x < 3; x++)
4212 cav->eater_array[i][y * 3 + x] =
4213 map_element_RND_to_EM_cave(level->yamyam_content[i].e[x][y]);
4215 cav->amoeba_time = level->amoeba_speed;
4216 cav->wonderwall_time = level->time_magic_wall;
4217 cav->wheel_time = level->time_wheel;
4219 cav->android_move_time = level->android_move_time;
4220 cav->android_clone_time = level->android_clone_time;
4221 cav->ball_random = level->ball_random;
4222 cav->ball_active = level->ball_active_initial;
4223 cav->ball_time = level->ball_time;
4224 cav->num_ball_arrays = level->num_ball_contents;
4226 cav->lenses_score = level->lenses_score;
4227 cav->magnify_score = level->magnify_score;
4228 cav->slurp_score = level->slurp_score;
4230 cav->lenses_time = level->lenses_time;
4231 cav->magnify_time = level->magnify_time;
4233 cav->wind_time = 9999;
4234 cav->wind_direction =
4235 map_direction_RND_to_EM(level->wind_direction_initial);
4237 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
4238 for (j = 0; j < 8; j++)
4239 cav->ball_array[i][j] =
4240 map_element_RND_to_EM_cave(level->ball_content[i].
4241 e[ball_xy[j][0]][ball_xy[j][1]]);
4243 map_android_clone_elements_RND_to_EM(level);
4245 // first fill the complete playfield with the empty space element
4246 for (y = 0; y < EM_MAX_CAVE_HEIGHT; y++)
4247 for (x = 0; x < EM_MAX_CAVE_WIDTH; x++)
4248 cav->cave[x][y] = Cblank;
4250 // then copy the real level contents from level file into the playfield
4251 for (y = 0; y < cav->height; y++) for (x = 0; x < cav->width; x++)
4253 int new_element = map_element_RND_to_EM_cave(level->field[x][y]);
4255 if (level->field[x][y] == EL_AMOEBA_DEAD)
4256 new_element = map_element_RND_to_EM_cave(EL_AMOEBA_WET);
4258 cav->cave[x][y] = new_element;
4261 for (i = 0; i < MAX_PLAYERS; i++)
4263 cav->player_x[i] = -1;
4264 cav->player_y[i] = -1;
4267 // initialize player positions and delete players from the playfield
4268 for (y = 0; y < cav->height; y++) for (x = 0; x < cav->width; x++)
4270 if (IS_PLAYER_ELEMENT(level->field[x][y]))
4272 int player_nr = GET_PLAYER_NR(level->field[x][y]);
4274 cav->player_x[player_nr] = x;
4275 cav->player_y[player_nr] = y;
4277 cav->cave[x][y] = map_element_RND_to_EM_cave(EL_EMPTY);
4282 static void CopyNativeLevel_EM_to_RND(struct LevelInfo *level)
4284 static int ball_xy[8][2] =
4295 struct LevelInfo_EM *level_em = level->native_em_level;
4296 struct CAVE *cav = level_em->cav;
4299 level->fieldx = MIN(cav->width, MAX_LEV_FIELDX);
4300 level->fieldy = MIN(cav->height, MAX_LEV_FIELDY);
4302 level->time = cav->time_seconds;
4303 level->gems_needed = cav->gems_needed;
4305 sprintf(level->name, "Level %d", level->file_info.nr);
4307 level->score[SC_EMERALD] = cav->emerald_score;
4308 level->score[SC_DIAMOND] = cav->diamond_score;
4309 level->score[SC_ROBOT] = cav->alien_score;
4310 level->score[SC_SPACESHIP] = cav->tank_score;
4311 level->score[SC_BUG] = cav->bug_score;
4312 level->score[SC_YAMYAM] = cav->eater_score;
4313 level->score[SC_NUT] = cav->nut_score;
4314 level->score[SC_DYNAMITE] = cav->dynamite_score;
4315 level->score[SC_KEY] = cav->key_score;
4316 level->score[SC_TIME_BONUS] = cav->exit_score;
4318 level->num_yamyam_contents = cav->num_eater_arrays;
4320 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
4321 for (y = 0; y < 3; y++)
4322 for (x = 0; x < 3; x++)
4323 level->yamyam_content[i].e[x][y] =
4324 map_element_EM_to_RND_cave(cav->eater_array[i][y * 3 + x]);
4326 level->amoeba_speed = cav->amoeba_time;
4327 level->time_magic_wall = cav->wonderwall_time;
4328 level->time_wheel = cav->wheel_time;
4330 level->android_move_time = cav->android_move_time;
4331 level->android_clone_time = cav->android_clone_time;
4332 level->ball_random = cav->ball_random;
4333 level->ball_active_initial = cav->ball_active;
4334 level->ball_time = cav->ball_time;
4335 level->num_ball_contents = cav->num_ball_arrays;
4337 level->lenses_score = cav->lenses_score;
4338 level->magnify_score = cav->magnify_score;
4339 level->slurp_score = cav->slurp_score;
4341 level->lenses_time = cav->lenses_time;
4342 level->magnify_time = cav->magnify_time;
4344 level->wind_direction_initial =
4345 map_direction_EM_to_RND(cav->wind_direction);
4347 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
4348 for (j = 0; j < 8; j++)
4349 level->ball_content[i].e[ball_xy[j][0]][ball_xy[j][1]] =
4350 map_element_EM_to_RND_cave(cav->ball_array[i][j]);
4352 map_android_clone_elements_EM_to_RND(level);
4354 // convert the playfield (some elements need special treatment)
4355 for (y = 0; y < level->fieldy; y++) for (x = 0; x < level->fieldx; x++)
4357 int new_element = map_element_EM_to_RND_cave(cav->cave[x][y]);
4359 if (new_element == EL_AMOEBA_WET && level->amoeba_speed == 0)
4360 new_element = EL_AMOEBA_DEAD;
4362 level->field[x][y] = new_element;
4365 for (i = 0; i < MAX_PLAYERS; i++)
4367 // in case of all players set to the same field, use the first player
4368 int nr = MAX_PLAYERS - i - 1;
4369 int jx = cav->player_x[nr];
4370 int jy = cav->player_y[nr];
4372 if (jx != -1 && jy != -1)
4373 level->field[jx][jy] = EL_PLAYER_1 + nr;
4376 // time score is counted for each 10 seconds left in Emerald Mine levels
4377 level->time_score_base = 10;
4381 // ----------------------------------------------------------------------------
4382 // functions for loading SP level
4383 // ----------------------------------------------------------------------------
4385 static void CopyNativeLevel_RND_to_SP(struct LevelInfo *level)
4387 struct LevelInfo_SP *level_sp = level->native_sp_level;
4388 LevelInfoType *header = &level_sp->header;
4391 level_sp->width = level->fieldx;
4392 level_sp->height = level->fieldy;
4394 for (x = 0; x < level->fieldx; x++)
4395 for (y = 0; y < level->fieldy; y++)
4396 level_sp->playfield[x][y] = map_element_RND_to_SP(level->field[x][y]);
4398 header->InitialGravity = (level->initial_player_gravity[0] ? 1 : 0);
4400 for (i = 0; i < SP_LEVEL_NAME_LEN; i++)
4401 header->LevelTitle[i] = level->name[i];
4402 // !!! NO STRING TERMINATION IN SUPAPLEX VB CODE YET -- FIX THIS !!!
4404 header->InfotronsNeeded = level->gems_needed;
4406 header->SpecialPortCount = 0;
4408 for (x = 0; x < level->fieldx; x++) for (y = 0; y < level->fieldy; y++)
4410 boolean gravity_port_found = FALSE;
4411 boolean gravity_port_valid = FALSE;
4412 int gravity_port_flag;
4413 int gravity_port_base_element;
4414 int element = level->field[x][y];
4416 if (element >= EL_SP_GRAVITY_ON_PORT_RIGHT &&
4417 element <= EL_SP_GRAVITY_ON_PORT_UP)
4419 gravity_port_found = TRUE;
4420 gravity_port_valid = TRUE;
4421 gravity_port_flag = 1;
4422 gravity_port_base_element = EL_SP_GRAVITY_ON_PORT_RIGHT;
4424 else if (element >= EL_SP_GRAVITY_OFF_PORT_RIGHT &&
4425 element <= EL_SP_GRAVITY_OFF_PORT_UP)
4427 gravity_port_found = TRUE;
4428 gravity_port_valid = TRUE;
4429 gravity_port_flag = 0;
4430 gravity_port_base_element = EL_SP_GRAVITY_OFF_PORT_RIGHT;
4432 else if (element >= EL_SP_GRAVITY_PORT_RIGHT &&
4433 element <= EL_SP_GRAVITY_PORT_UP)
4435 // change R'n'D style gravity inverting special port to normal port
4436 // (there are no gravity inverting ports in native Supaplex engine)
4438 gravity_port_found = TRUE;
4439 gravity_port_valid = FALSE;
4440 gravity_port_base_element = EL_SP_GRAVITY_PORT_RIGHT;
4443 if (gravity_port_found)
4445 if (gravity_port_valid &&
4446 header->SpecialPortCount < SP_MAX_SPECIAL_PORTS)
4448 SpecialPortType *port = &header->SpecialPort[header->SpecialPortCount];
4450 port->PortLocation = (y * level->fieldx + x) * 2;
4451 port->Gravity = gravity_port_flag;
4453 element += EL_SP_GRAVITY_PORT_RIGHT - gravity_port_base_element;
4455 header->SpecialPortCount++;
4459 // change special gravity port to normal port
4461 element += EL_SP_PORT_RIGHT - gravity_port_base_element;
4464 level_sp->playfield[x][y] = element - EL_SP_START;
4469 static void CopyNativeLevel_SP_to_RND(struct LevelInfo *level)
4471 struct LevelInfo_SP *level_sp = level->native_sp_level;
4472 LevelInfoType *header = &level_sp->header;
4473 boolean num_invalid_elements = 0;
4476 level->fieldx = level_sp->width;
4477 level->fieldy = level_sp->height;
4479 for (x = 0; x < level->fieldx; x++)
4481 for (y = 0; y < level->fieldy; y++)
4483 int element_old = level_sp->playfield[x][y];
4484 int element_new = getMappedElement(map_element_SP_to_RND(element_old));
4486 if (element_new == EL_UNKNOWN)
4488 num_invalid_elements++;
4490 Debug("level:native:SP", "invalid element %d at position %d, %d",
4494 level->field[x][y] = element_new;
4498 if (num_invalid_elements > 0)
4499 Warn("found %d invalid elements%s", num_invalid_elements,
4500 (!options.debug ? " (use '--debug' for more details)" : ""));
4502 for (i = 0; i < MAX_PLAYERS; i++)
4503 level->initial_player_gravity[i] =
4504 (header->InitialGravity == 1 ? TRUE : FALSE);
4506 // skip leading spaces
4507 for (i = 0; i < SP_LEVEL_NAME_LEN; i++)
4508 if (header->LevelTitle[i] != ' ')
4512 for (j = 0; i < SP_LEVEL_NAME_LEN; i++, j++)
4513 level->name[j] = header->LevelTitle[i];
4514 level->name[j] = '\0';
4516 // cut trailing spaces
4518 if (level->name[j - 1] == ' ' && level->name[j] == '\0')
4519 level->name[j - 1] = '\0';
4521 level->gems_needed = header->InfotronsNeeded;
4523 for (i = 0; i < header->SpecialPortCount; i++)
4525 SpecialPortType *port = &header->SpecialPort[i];
4526 int port_location = port->PortLocation;
4527 int gravity = port->Gravity;
4528 int port_x, port_y, port_element;
4530 port_x = (port_location / 2) % level->fieldx;
4531 port_y = (port_location / 2) / level->fieldx;
4533 if (port_x < 0 || port_x >= level->fieldx ||
4534 port_y < 0 || port_y >= level->fieldy)
4536 Warn("special port position (%d, %d) out of bounds", port_x, port_y);
4541 port_element = level->field[port_x][port_y];
4543 if (port_element < EL_SP_GRAVITY_PORT_RIGHT ||
4544 port_element > EL_SP_GRAVITY_PORT_UP)
4546 Warn("no special port at position (%d, %d)", port_x, port_y);
4551 // change previous (wrong) gravity inverting special port to either
4552 // gravity enabling special port or gravity disabling special port
4553 level->field[port_x][port_y] +=
4554 (gravity == 1 ? EL_SP_GRAVITY_ON_PORT_RIGHT :
4555 EL_SP_GRAVITY_OFF_PORT_RIGHT) - EL_SP_GRAVITY_PORT_RIGHT;
4558 // change special gravity ports without database entries to normal ports
4559 for (x = 0; x < level->fieldx; x++)
4560 for (y = 0; y < level->fieldy; y++)
4561 if (level->field[x][y] >= EL_SP_GRAVITY_PORT_RIGHT &&
4562 level->field[x][y] <= EL_SP_GRAVITY_PORT_UP)
4563 level->field[x][y] += EL_SP_PORT_RIGHT - EL_SP_GRAVITY_PORT_RIGHT;
4565 level->time = 0; // no time limit
4566 level->amoeba_speed = 0;
4567 level->time_magic_wall = 0;
4568 level->time_wheel = 0;
4569 level->amoeba_content = EL_EMPTY;
4571 // original Supaplex does not use score values -- rate by playing time
4572 for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
4573 level->score[i] = 0;
4575 level->rate_time_over_score = TRUE;
4577 // there are no yamyams in supaplex levels
4578 for (i = 0; i < level->num_yamyam_contents; i++)
4579 for (x = 0; x < 3; x++)
4580 for (y = 0; y < 3; y++)
4581 level->yamyam_content[i].e[x][y] = EL_EMPTY;
4584 static void CopyNativeTape_RND_to_SP(struct LevelInfo *level)
4586 struct LevelInfo_SP *level_sp = level->native_sp_level;
4587 struct DemoInfo_SP *demo = &level_sp->demo;
4590 // always start with reliable default values
4591 demo->is_available = FALSE;
4594 if (TAPE_IS_EMPTY(tape))
4597 demo->level_nr = tape.level_nr; // (currently not used)
4599 level_sp->header.DemoRandomSeed = tape.random_seed;
4603 for (i = 0; i < tape.length; i++)
4605 int demo_action = map_key_RND_to_SP(tape.pos[i].action[0]);
4606 int demo_repeat = tape.pos[i].delay;
4607 int demo_entries = (demo_repeat + 15) / 16;
4609 if (demo->length + demo_entries >= SP_MAX_TAPE_LEN)
4611 Warn("tape truncated: size exceeds maximum SP demo size %d",
4617 for (j = 0; j < demo_repeat / 16; j++)
4618 demo->data[demo->length++] = 0xf0 | demo_action;
4620 if (demo_repeat % 16)
4621 demo->data[demo->length++] = ((demo_repeat % 16 - 1) << 4) | demo_action;
4624 demo->is_available = TRUE;
4627 static void CopyNativeTape_SP_to_RND(struct LevelInfo *level)
4629 struct LevelInfo_SP *level_sp = level->native_sp_level;
4630 struct DemoInfo_SP *demo = &level_sp->demo;
4631 char *filename = level->file_info.filename;
4634 // always start with reliable default values
4635 setTapeInfoToDefaults();
4637 if (!demo->is_available)
4640 tape.level_nr = demo->level_nr; // (currently not used)
4641 tape.random_seed = level_sp->header.DemoRandomSeed;
4643 TapeSetDateFromEpochSeconds(getFileTimestampEpochSeconds(filename));
4646 tape.pos[tape.counter].delay = 0;
4648 for (i = 0; i < demo->length; i++)
4650 int demo_action = demo->data[i] & 0x0f;
4651 int demo_repeat = (demo->data[i] & 0xf0) >> 4;
4652 int tape_action = map_key_SP_to_RND(demo_action);
4653 int tape_repeat = demo_repeat + 1;
4654 byte action[MAX_TAPE_ACTIONS] = { tape_action };
4655 boolean success = 0;
4658 for (j = 0; j < tape_repeat; j++)
4659 success = TapeAddAction(action);
4663 Warn("SP demo truncated: size exceeds maximum tape size %d",
4670 TapeHaltRecording();
4674 // ----------------------------------------------------------------------------
4675 // functions for loading MM level
4676 // ----------------------------------------------------------------------------
4678 static void CopyNativeLevel_RND_to_MM(struct LevelInfo *level)
4680 struct LevelInfo_MM *level_mm = level->native_mm_level;
4683 level_mm->fieldx = MIN(level->fieldx, MM_MAX_PLAYFIELD_WIDTH);
4684 level_mm->fieldy = MIN(level->fieldy, MM_MAX_PLAYFIELD_HEIGHT);
4686 level_mm->time = level->time;
4687 level_mm->kettles_needed = level->gems_needed;
4688 level_mm->auto_count_kettles = level->auto_count_gems;
4690 level_mm->mm_laser_red = level->mm_laser_red;
4691 level_mm->mm_laser_green = level->mm_laser_green;
4692 level_mm->mm_laser_blue = level->mm_laser_blue;
4694 level_mm->df_laser_red = level->df_laser_red;
4695 level_mm->df_laser_green = level->df_laser_green;
4696 level_mm->df_laser_blue = level->df_laser_blue;
4698 strcpy(level_mm->name, level->name);
4699 strcpy(level_mm->author, level->author);
4701 level_mm->score[SC_EMERALD] = level->score[SC_EMERALD];
4702 level_mm->score[SC_PACMAN] = level->score[SC_PACMAN];
4703 level_mm->score[SC_KEY] = level->score[SC_KEY];
4704 level_mm->score[SC_TIME_BONUS] = level->score[SC_TIME_BONUS];
4705 level_mm->score[SC_ELEM_BONUS] = level->score[SC_ELEM_BONUS];
4707 level_mm->amoeba_speed = level->amoeba_speed;
4708 level_mm->time_fuse = level->mm_time_fuse;
4709 level_mm->time_bomb = level->mm_time_bomb;
4710 level_mm->time_ball = level->mm_time_ball;
4711 level_mm->time_block = level->mm_time_block;
4713 level_mm->num_ball_contents = level->num_mm_ball_contents;
4714 level_mm->ball_choice_mode = level->mm_ball_choice_mode;
4715 level_mm->rotate_ball_content = level->rotate_mm_ball_content;
4716 level_mm->explode_ball = level->explode_mm_ball;
4718 for (i = 0; i < level->num_mm_ball_contents; i++)
4719 level_mm->ball_content[i] =
4720 map_element_RND_to_MM(level->mm_ball_content[i]);
4722 for (x = 0; x < level->fieldx; x++)
4723 for (y = 0; y < level->fieldy; y++)
4725 level_mm->field[x][y] = map_element_RND_to_MM(level->field[x][y]);
4728 static void CopyNativeLevel_MM_to_RND(struct LevelInfo *level)
4730 struct LevelInfo_MM *level_mm = level->native_mm_level;
4733 level->fieldx = MIN(level_mm->fieldx, MAX_LEV_FIELDX);
4734 level->fieldy = MIN(level_mm->fieldy, MAX_LEV_FIELDY);
4736 level->time = level_mm->time;
4737 level->gems_needed = level_mm->kettles_needed;
4738 level->auto_count_gems = level_mm->auto_count_kettles;
4740 level->mm_laser_red = level_mm->mm_laser_red;
4741 level->mm_laser_green = level_mm->mm_laser_green;
4742 level->mm_laser_blue = level_mm->mm_laser_blue;
4744 level->df_laser_red = level_mm->df_laser_red;
4745 level->df_laser_green = level_mm->df_laser_green;
4746 level->df_laser_blue = level_mm->df_laser_blue;
4748 strcpy(level->name, level_mm->name);
4750 // only overwrite author from 'levelinfo.conf' if author defined in level
4751 if (!strEqual(level_mm->author, ANONYMOUS_NAME))
4752 strcpy(level->author, level_mm->author);
4754 level->score[SC_EMERALD] = level_mm->score[SC_EMERALD];
4755 level->score[SC_PACMAN] = level_mm->score[SC_PACMAN];
4756 level->score[SC_KEY] = level_mm->score[SC_KEY];
4757 level->score[SC_TIME_BONUS] = level_mm->score[SC_TIME_BONUS];
4758 level->score[SC_ELEM_BONUS] = level_mm->score[SC_ELEM_BONUS];
4760 level->amoeba_speed = level_mm->amoeba_speed;
4761 level->mm_time_fuse = level_mm->time_fuse;
4762 level->mm_time_bomb = level_mm->time_bomb;
4763 level->mm_time_ball = level_mm->time_ball;
4764 level->mm_time_block = level_mm->time_block;
4766 level->num_mm_ball_contents = level_mm->num_ball_contents;
4767 level->mm_ball_choice_mode = level_mm->ball_choice_mode;
4768 level->rotate_mm_ball_content = level_mm->rotate_ball_content;
4769 level->explode_mm_ball = level_mm->explode_ball;
4771 for (i = 0; i < level->num_mm_ball_contents; i++)
4772 level->mm_ball_content[i] =
4773 map_element_MM_to_RND(level_mm->ball_content[i]);
4775 for (x = 0; x < level->fieldx; x++)
4776 for (y = 0; y < level->fieldy; y++)
4777 level->field[x][y] = map_element_MM_to_RND(level_mm->field[x][y]);
4781 // ----------------------------------------------------------------------------
4782 // functions for loading DC level
4783 // ----------------------------------------------------------------------------
4785 #define DC_LEVEL_HEADER_SIZE 344
4787 static unsigned short getDecodedWord_DC(unsigned short data_encoded,
4790 static int last_data_encoded;
4794 int diff_hi, diff_lo;
4795 int data_hi, data_lo;
4796 unsigned short data_decoded;
4800 last_data_encoded = 0;
4807 diff = data_encoded - last_data_encoded;
4808 diff_hi = diff & ~0xff;
4809 diff_lo = diff & 0xff;
4813 data_hi = diff_hi - (offset1 << 8) + (offset2 & 0xff00);
4814 data_lo = (diff_lo + (data_hi >> 16)) & 0x00ff;
4815 data_hi = data_hi & 0xff00;
4817 data_decoded = data_hi | data_lo;
4819 last_data_encoded = data_encoded;
4821 offset1 = (offset1 + 1) % 31;
4822 offset2 = offset2 & 0xff;
4824 return data_decoded;
4827 static int getMappedElement_DC(int element)
4835 // 0x0117 - 0x036e: (?)
4838 // 0x042d - 0x0684: (?)
4854 element = EL_CRYSTAL;
4857 case 0x0e77: // quicksand (boulder)
4858 element = EL_QUICKSAND_FAST_FULL;
4861 case 0x0e99: // slow quicksand (boulder)
4862 element = EL_QUICKSAND_FULL;
4866 element = EL_EM_EXIT_OPEN;
4870 element = EL_EM_EXIT_CLOSED;
4874 element = EL_EM_STEEL_EXIT_OPEN;
4878 element = EL_EM_STEEL_EXIT_CLOSED;
4881 case 0x0f4f: // dynamite (lit 1)
4882 element = EL_EM_DYNAMITE_ACTIVE;
4885 case 0x0f57: // dynamite (lit 2)
4886 element = EL_EM_DYNAMITE_ACTIVE;
4889 case 0x0f5f: // dynamite (lit 3)
4890 element = EL_EM_DYNAMITE_ACTIVE;
4893 case 0x0f67: // dynamite (lit 4)
4894 element = EL_EM_DYNAMITE_ACTIVE;
4901 element = EL_AMOEBA_WET;
4905 element = EL_AMOEBA_DROP;
4909 element = EL_DC_MAGIC_WALL;
4913 element = EL_SPACESHIP_UP;
4917 element = EL_SPACESHIP_DOWN;
4921 element = EL_SPACESHIP_LEFT;
4925 element = EL_SPACESHIP_RIGHT;
4929 element = EL_BUG_UP;
4933 element = EL_BUG_DOWN;
4937 element = EL_BUG_LEFT;
4941 element = EL_BUG_RIGHT;
4945 element = EL_MOLE_UP;
4949 element = EL_MOLE_DOWN;
4953 element = EL_MOLE_LEFT;
4957 element = EL_MOLE_RIGHT;
4965 element = EL_YAMYAM_UP;
4969 element = EL_SWITCHGATE_OPEN;
4973 element = EL_SWITCHGATE_CLOSED;
4977 element = EL_DC_SWITCHGATE_SWITCH_UP;
4981 element = EL_TIMEGATE_CLOSED;
4984 case 0x144c: // conveyor belt switch (green)
4985 element = EL_CONVEYOR_BELT_3_SWITCH_MIDDLE;
4988 case 0x144f: // conveyor belt switch (red)
4989 element = EL_CONVEYOR_BELT_1_SWITCH_MIDDLE;
4992 case 0x1452: // conveyor belt switch (blue)
4993 element = EL_CONVEYOR_BELT_4_SWITCH_MIDDLE;
4997 element = EL_CONVEYOR_BELT_3_MIDDLE;
5001 element = EL_CONVEYOR_BELT_3_LEFT;
5005 element = EL_CONVEYOR_BELT_3_RIGHT;
5009 element = EL_CONVEYOR_BELT_1_MIDDLE;
5013 element = EL_CONVEYOR_BELT_1_LEFT;
5017 element = EL_CONVEYOR_BELT_1_RIGHT;
5021 element = EL_CONVEYOR_BELT_4_MIDDLE;
5025 element = EL_CONVEYOR_BELT_4_LEFT;
5029 element = EL_CONVEYOR_BELT_4_RIGHT;
5033 element = EL_EXPANDABLE_WALL_HORIZONTAL;
5037 element = EL_EXPANDABLE_WALL_VERTICAL;
5041 element = EL_EXPANDABLE_WALL_ANY;
5044 case 0x14ce: // growing steel wall (left/right)
5045 element = EL_EXPANDABLE_STEELWALL_HORIZONTAL;
5048 case 0x14df: // growing steel wall (up/down)
5049 element = EL_EXPANDABLE_STEELWALL_VERTICAL;
5052 case 0x14e8: // growing steel wall (up/down/left/right)
5053 element = EL_EXPANDABLE_STEELWALL_ANY;
5057 element = EL_SHIELD_DEADLY;
5061 element = EL_EXTRA_TIME;
5069 element = EL_EMPTY_SPACE;
5072 case 0x1578: // quicksand (empty)
5073 element = EL_QUICKSAND_FAST_EMPTY;
5076 case 0x1579: // slow quicksand (empty)
5077 element = EL_QUICKSAND_EMPTY;
5087 element = EL_EM_DYNAMITE;
5090 case 0x15a1: // key (red)
5091 element = EL_EM_KEY_1;
5094 case 0x15a2: // key (yellow)
5095 element = EL_EM_KEY_2;
5098 case 0x15a3: // key (blue)
5099 element = EL_EM_KEY_4;
5102 case 0x15a4: // key (green)
5103 element = EL_EM_KEY_3;
5106 case 0x15a5: // key (white)
5107 element = EL_DC_KEY_WHITE;
5111 element = EL_WALL_SLIPPERY;
5118 case 0x15a8: // wall (not round)
5122 case 0x15a9: // (blue)
5123 element = EL_CHAR_A;
5126 case 0x15aa: // (blue)
5127 element = EL_CHAR_B;
5130 case 0x15ab: // (blue)
5131 element = EL_CHAR_C;
5134 case 0x15ac: // (blue)
5135 element = EL_CHAR_D;
5138 case 0x15ad: // (blue)
5139 element = EL_CHAR_E;
5142 case 0x15ae: // (blue)
5143 element = EL_CHAR_F;
5146 case 0x15af: // (blue)
5147 element = EL_CHAR_G;
5150 case 0x15b0: // (blue)
5151 element = EL_CHAR_H;
5154 case 0x15b1: // (blue)
5155 element = EL_CHAR_I;
5158 case 0x15b2: // (blue)
5159 element = EL_CHAR_J;
5162 case 0x15b3: // (blue)
5163 element = EL_CHAR_K;
5166 case 0x15b4: // (blue)
5167 element = EL_CHAR_L;
5170 case 0x15b5: // (blue)
5171 element = EL_CHAR_M;
5174 case 0x15b6: // (blue)
5175 element = EL_CHAR_N;
5178 case 0x15b7: // (blue)
5179 element = EL_CHAR_O;
5182 case 0x15b8: // (blue)
5183 element = EL_CHAR_P;
5186 case 0x15b9: // (blue)
5187 element = EL_CHAR_Q;
5190 case 0x15ba: // (blue)
5191 element = EL_CHAR_R;
5194 case 0x15bb: // (blue)
5195 element = EL_CHAR_S;
5198 case 0x15bc: // (blue)
5199 element = EL_CHAR_T;
5202 case 0x15bd: // (blue)
5203 element = EL_CHAR_U;
5206 case 0x15be: // (blue)
5207 element = EL_CHAR_V;
5210 case 0x15bf: // (blue)
5211 element = EL_CHAR_W;
5214 case 0x15c0: // (blue)
5215 element = EL_CHAR_X;
5218 case 0x15c1: // (blue)
5219 element = EL_CHAR_Y;
5222 case 0x15c2: // (blue)
5223 element = EL_CHAR_Z;
5226 case 0x15c3: // (blue)
5227 element = EL_CHAR_AUMLAUT;
5230 case 0x15c4: // (blue)
5231 element = EL_CHAR_OUMLAUT;
5234 case 0x15c5: // (blue)
5235 element = EL_CHAR_UUMLAUT;
5238 case 0x15c6: // (blue)
5239 element = EL_CHAR_0;
5242 case 0x15c7: // (blue)
5243 element = EL_CHAR_1;
5246 case 0x15c8: // (blue)
5247 element = EL_CHAR_2;
5250 case 0x15c9: // (blue)
5251 element = EL_CHAR_3;
5254 case 0x15ca: // (blue)
5255 element = EL_CHAR_4;
5258 case 0x15cb: // (blue)
5259 element = EL_CHAR_5;
5262 case 0x15cc: // (blue)
5263 element = EL_CHAR_6;
5266 case 0x15cd: // (blue)
5267 element = EL_CHAR_7;
5270 case 0x15ce: // (blue)
5271 element = EL_CHAR_8;
5274 case 0x15cf: // (blue)
5275 element = EL_CHAR_9;
5278 case 0x15d0: // (blue)
5279 element = EL_CHAR_PERIOD;
5282 case 0x15d1: // (blue)
5283 element = EL_CHAR_EXCLAM;
5286 case 0x15d2: // (blue)
5287 element = EL_CHAR_COLON;
5290 case 0x15d3: // (blue)
5291 element = EL_CHAR_LESS;
5294 case 0x15d4: // (blue)
5295 element = EL_CHAR_GREATER;
5298 case 0x15d5: // (blue)
5299 element = EL_CHAR_QUESTION;
5302 case 0x15d6: // (blue)
5303 element = EL_CHAR_COPYRIGHT;
5306 case 0x15d7: // (blue)
5307 element = EL_CHAR_UP;
5310 case 0x15d8: // (blue)
5311 element = EL_CHAR_DOWN;
5314 case 0x15d9: // (blue)
5315 element = EL_CHAR_BUTTON;
5318 case 0x15da: // (blue)
5319 element = EL_CHAR_PLUS;
5322 case 0x15db: // (blue)
5323 element = EL_CHAR_MINUS;
5326 case 0x15dc: // (blue)
5327 element = EL_CHAR_APOSTROPHE;
5330 case 0x15dd: // (blue)
5331 element = EL_CHAR_PARENLEFT;
5334 case 0x15de: // (blue)
5335 element = EL_CHAR_PARENRIGHT;
5338 case 0x15df: // (green)
5339 element = EL_CHAR_A;
5342 case 0x15e0: // (green)
5343 element = EL_CHAR_B;
5346 case 0x15e1: // (green)
5347 element = EL_CHAR_C;
5350 case 0x15e2: // (green)
5351 element = EL_CHAR_D;
5354 case 0x15e3: // (green)
5355 element = EL_CHAR_E;
5358 case 0x15e4: // (green)
5359 element = EL_CHAR_F;
5362 case 0x15e5: // (green)
5363 element = EL_CHAR_G;
5366 case 0x15e6: // (green)
5367 element = EL_CHAR_H;
5370 case 0x15e7: // (green)
5371 element = EL_CHAR_I;
5374 case 0x15e8: // (green)
5375 element = EL_CHAR_J;
5378 case 0x15e9: // (green)
5379 element = EL_CHAR_K;
5382 case 0x15ea: // (green)
5383 element = EL_CHAR_L;
5386 case 0x15eb: // (green)
5387 element = EL_CHAR_M;
5390 case 0x15ec: // (green)
5391 element = EL_CHAR_N;
5394 case 0x15ed: // (green)
5395 element = EL_CHAR_O;
5398 case 0x15ee: // (green)
5399 element = EL_CHAR_P;
5402 case 0x15ef: // (green)
5403 element = EL_CHAR_Q;
5406 case 0x15f0: // (green)
5407 element = EL_CHAR_R;
5410 case 0x15f1: // (green)
5411 element = EL_CHAR_S;
5414 case 0x15f2: // (green)
5415 element = EL_CHAR_T;
5418 case 0x15f3: // (green)
5419 element = EL_CHAR_U;
5422 case 0x15f4: // (green)
5423 element = EL_CHAR_V;
5426 case 0x15f5: // (green)
5427 element = EL_CHAR_W;
5430 case 0x15f6: // (green)
5431 element = EL_CHAR_X;
5434 case 0x15f7: // (green)
5435 element = EL_CHAR_Y;
5438 case 0x15f8: // (green)
5439 element = EL_CHAR_Z;
5442 case 0x15f9: // (green)
5443 element = EL_CHAR_AUMLAUT;
5446 case 0x15fa: // (green)
5447 element = EL_CHAR_OUMLAUT;
5450 case 0x15fb: // (green)
5451 element = EL_CHAR_UUMLAUT;
5454 case 0x15fc: // (green)
5455 element = EL_CHAR_0;
5458 case 0x15fd: // (green)
5459 element = EL_CHAR_1;
5462 case 0x15fe: // (green)
5463 element = EL_CHAR_2;
5466 case 0x15ff: // (green)
5467 element = EL_CHAR_3;
5470 case 0x1600: // (green)
5471 element = EL_CHAR_4;
5474 case 0x1601: // (green)
5475 element = EL_CHAR_5;
5478 case 0x1602: // (green)
5479 element = EL_CHAR_6;
5482 case 0x1603: // (green)
5483 element = EL_CHAR_7;
5486 case 0x1604: // (green)
5487 element = EL_CHAR_8;
5490 case 0x1605: // (green)
5491 element = EL_CHAR_9;
5494 case 0x1606: // (green)
5495 element = EL_CHAR_PERIOD;
5498 case 0x1607: // (green)
5499 element = EL_CHAR_EXCLAM;
5502 case 0x1608: // (green)
5503 element = EL_CHAR_COLON;
5506 case 0x1609: // (green)
5507 element = EL_CHAR_LESS;
5510 case 0x160a: // (green)
5511 element = EL_CHAR_GREATER;
5514 case 0x160b: // (green)
5515 element = EL_CHAR_QUESTION;
5518 case 0x160c: // (green)
5519 element = EL_CHAR_COPYRIGHT;
5522 case 0x160d: // (green)
5523 element = EL_CHAR_UP;
5526 case 0x160e: // (green)
5527 element = EL_CHAR_DOWN;
5530 case 0x160f: // (green)
5531 element = EL_CHAR_BUTTON;
5534 case 0x1610: // (green)
5535 element = EL_CHAR_PLUS;
5538 case 0x1611: // (green)
5539 element = EL_CHAR_MINUS;
5542 case 0x1612: // (green)
5543 element = EL_CHAR_APOSTROPHE;
5546 case 0x1613: // (green)
5547 element = EL_CHAR_PARENLEFT;
5550 case 0x1614: // (green)
5551 element = EL_CHAR_PARENRIGHT;
5554 case 0x1615: // (blue steel)
5555 element = EL_STEEL_CHAR_A;
5558 case 0x1616: // (blue steel)
5559 element = EL_STEEL_CHAR_B;
5562 case 0x1617: // (blue steel)
5563 element = EL_STEEL_CHAR_C;
5566 case 0x1618: // (blue steel)
5567 element = EL_STEEL_CHAR_D;
5570 case 0x1619: // (blue steel)
5571 element = EL_STEEL_CHAR_E;
5574 case 0x161a: // (blue steel)
5575 element = EL_STEEL_CHAR_F;
5578 case 0x161b: // (blue steel)
5579 element = EL_STEEL_CHAR_G;
5582 case 0x161c: // (blue steel)
5583 element = EL_STEEL_CHAR_H;
5586 case 0x161d: // (blue steel)
5587 element = EL_STEEL_CHAR_I;
5590 case 0x161e: // (blue steel)
5591 element = EL_STEEL_CHAR_J;
5594 case 0x161f: // (blue steel)
5595 element = EL_STEEL_CHAR_K;
5598 case 0x1620: // (blue steel)
5599 element = EL_STEEL_CHAR_L;
5602 case 0x1621: // (blue steel)
5603 element = EL_STEEL_CHAR_M;
5606 case 0x1622: // (blue steel)
5607 element = EL_STEEL_CHAR_N;
5610 case 0x1623: // (blue steel)
5611 element = EL_STEEL_CHAR_O;
5614 case 0x1624: // (blue steel)
5615 element = EL_STEEL_CHAR_P;
5618 case 0x1625: // (blue steel)
5619 element = EL_STEEL_CHAR_Q;
5622 case 0x1626: // (blue steel)
5623 element = EL_STEEL_CHAR_R;
5626 case 0x1627: // (blue steel)
5627 element = EL_STEEL_CHAR_S;
5630 case 0x1628: // (blue steel)
5631 element = EL_STEEL_CHAR_T;
5634 case 0x1629: // (blue steel)
5635 element = EL_STEEL_CHAR_U;
5638 case 0x162a: // (blue steel)
5639 element = EL_STEEL_CHAR_V;
5642 case 0x162b: // (blue steel)
5643 element = EL_STEEL_CHAR_W;
5646 case 0x162c: // (blue steel)
5647 element = EL_STEEL_CHAR_X;
5650 case 0x162d: // (blue steel)
5651 element = EL_STEEL_CHAR_Y;
5654 case 0x162e: // (blue steel)
5655 element = EL_STEEL_CHAR_Z;
5658 case 0x162f: // (blue steel)
5659 element = EL_STEEL_CHAR_AUMLAUT;
5662 case 0x1630: // (blue steel)
5663 element = EL_STEEL_CHAR_OUMLAUT;
5666 case 0x1631: // (blue steel)
5667 element = EL_STEEL_CHAR_UUMLAUT;
5670 case 0x1632: // (blue steel)
5671 element = EL_STEEL_CHAR_0;
5674 case 0x1633: // (blue steel)
5675 element = EL_STEEL_CHAR_1;
5678 case 0x1634: // (blue steel)
5679 element = EL_STEEL_CHAR_2;
5682 case 0x1635: // (blue steel)
5683 element = EL_STEEL_CHAR_3;
5686 case 0x1636: // (blue steel)
5687 element = EL_STEEL_CHAR_4;
5690 case 0x1637: // (blue steel)
5691 element = EL_STEEL_CHAR_5;
5694 case 0x1638: // (blue steel)
5695 element = EL_STEEL_CHAR_6;
5698 case 0x1639: // (blue steel)
5699 element = EL_STEEL_CHAR_7;
5702 case 0x163a: // (blue steel)
5703 element = EL_STEEL_CHAR_8;
5706 case 0x163b: // (blue steel)
5707 element = EL_STEEL_CHAR_9;
5710 case 0x163c: // (blue steel)
5711 element = EL_STEEL_CHAR_PERIOD;
5714 case 0x163d: // (blue steel)
5715 element = EL_STEEL_CHAR_EXCLAM;
5718 case 0x163e: // (blue steel)
5719 element = EL_STEEL_CHAR_COLON;
5722 case 0x163f: // (blue steel)
5723 element = EL_STEEL_CHAR_LESS;
5726 case 0x1640: // (blue steel)
5727 element = EL_STEEL_CHAR_GREATER;
5730 case 0x1641: // (blue steel)
5731 element = EL_STEEL_CHAR_QUESTION;
5734 case 0x1642: // (blue steel)
5735 element = EL_STEEL_CHAR_COPYRIGHT;
5738 case 0x1643: // (blue steel)
5739 element = EL_STEEL_CHAR_UP;
5742 case 0x1644: // (blue steel)
5743 element = EL_STEEL_CHAR_DOWN;
5746 case 0x1645: // (blue steel)
5747 element = EL_STEEL_CHAR_BUTTON;
5750 case 0x1646: // (blue steel)
5751 element = EL_STEEL_CHAR_PLUS;
5754 case 0x1647: // (blue steel)
5755 element = EL_STEEL_CHAR_MINUS;
5758 case 0x1648: // (blue steel)
5759 element = EL_STEEL_CHAR_APOSTROPHE;
5762 case 0x1649: // (blue steel)
5763 element = EL_STEEL_CHAR_PARENLEFT;
5766 case 0x164a: // (blue steel)
5767 element = EL_STEEL_CHAR_PARENRIGHT;
5770 case 0x164b: // (green steel)
5771 element = EL_STEEL_CHAR_A;
5774 case 0x164c: // (green steel)
5775 element = EL_STEEL_CHAR_B;
5778 case 0x164d: // (green steel)
5779 element = EL_STEEL_CHAR_C;
5782 case 0x164e: // (green steel)
5783 element = EL_STEEL_CHAR_D;
5786 case 0x164f: // (green steel)
5787 element = EL_STEEL_CHAR_E;
5790 case 0x1650: // (green steel)
5791 element = EL_STEEL_CHAR_F;
5794 case 0x1651: // (green steel)
5795 element = EL_STEEL_CHAR_G;
5798 case 0x1652: // (green steel)
5799 element = EL_STEEL_CHAR_H;
5802 case 0x1653: // (green steel)
5803 element = EL_STEEL_CHAR_I;
5806 case 0x1654: // (green steel)
5807 element = EL_STEEL_CHAR_J;
5810 case 0x1655: // (green steel)
5811 element = EL_STEEL_CHAR_K;
5814 case 0x1656: // (green steel)
5815 element = EL_STEEL_CHAR_L;
5818 case 0x1657: // (green steel)
5819 element = EL_STEEL_CHAR_M;
5822 case 0x1658: // (green steel)
5823 element = EL_STEEL_CHAR_N;
5826 case 0x1659: // (green steel)
5827 element = EL_STEEL_CHAR_O;
5830 case 0x165a: // (green steel)
5831 element = EL_STEEL_CHAR_P;
5834 case 0x165b: // (green steel)
5835 element = EL_STEEL_CHAR_Q;
5838 case 0x165c: // (green steel)
5839 element = EL_STEEL_CHAR_R;
5842 case 0x165d: // (green steel)
5843 element = EL_STEEL_CHAR_S;
5846 case 0x165e: // (green steel)
5847 element = EL_STEEL_CHAR_T;
5850 case 0x165f: // (green steel)
5851 element = EL_STEEL_CHAR_U;
5854 case 0x1660: // (green steel)
5855 element = EL_STEEL_CHAR_V;
5858 case 0x1661: // (green steel)
5859 element = EL_STEEL_CHAR_W;
5862 case 0x1662: // (green steel)
5863 element = EL_STEEL_CHAR_X;
5866 case 0x1663: // (green steel)
5867 element = EL_STEEL_CHAR_Y;
5870 case 0x1664: // (green steel)
5871 element = EL_STEEL_CHAR_Z;
5874 case 0x1665: // (green steel)
5875 element = EL_STEEL_CHAR_AUMLAUT;
5878 case 0x1666: // (green steel)
5879 element = EL_STEEL_CHAR_OUMLAUT;
5882 case 0x1667: // (green steel)
5883 element = EL_STEEL_CHAR_UUMLAUT;
5886 case 0x1668: // (green steel)
5887 element = EL_STEEL_CHAR_0;
5890 case 0x1669: // (green steel)
5891 element = EL_STEEL_CHAR_1;
5894 case 0x166a: // (green steel)
5895 element = EL_STEEL_CHAR_2;
5898 case 0x166b: // (green steel)
5899 element = EL_STEEL_CHAR_3;
5902 case 0x166c: // (green steel)
5903 element = EL_STEEL_CHAR_4;
5906 case 0x166d: // (green steel)
5907 element = EL_STEEL_CHAR_5;
5910 case 0x166e: // (green steel)
5911 element = EL_STEEL_CHAR_6;
5914 case 0x166f: // (green steel)
5915 element = EL_STEEL_CHAR_7;
5918 case 0x1670: // (green steel)
5919 element = EL_STEEL_CHAR_8;
5922 case 0x1671: // (green steel)
5923 element = EL_STEEL_CHAR_9;
5926 case 0x1672: // (green steel)
5927 element = EL_STEEL_CHAR_PERIOD;
5930 case 0x1673: // (green steel)
5931 element = EL_STEEL_CHAR_EXCLAM;
5934 case 0x1674: // (green steel)
5935 element = EL_STEEL_CHAR_COLON;
5938 case 0x1675: // (green steel)
5939 element = EL_STEEL_CHAR_LESS;
5942 case 0x1676: // (green steel)
5943 element = EL_STEEL_CHAR_GREATER;
5946 case 0x1677: // (green steel)
5947 element = EL_STEEL_CHAR_QUESTION;
5950 case 0x1678: // (green steel)
5951 element = EL_STEEL_CHAR_COPYRIGHT;
5954 case 0x1679: // (green steel)
5955 element = EL_STEEL_CHAR_UP;
5958 case 0x167a: // (green steel)
5959 element = EL_STEEL_CHAR_DOWN;
5962 case 0x167b: // (green steel)
5963 element = EL_STEEL_CHAR_BUTTON;
5966 case 0x167c: // (green steel)
5967 element = EL_STEEL_CHAR_PLUS;
5970 case 0x167d: // (green steel)
5971 element = EL_STEEL_CHAR_MINUS;
5974 case 0x167e: // (green steel)
5975 element = EL_STEEL_CHAR_APOSTROPHE;
5978 case 0x167f: // (green steel)
5979 element = EL_STEEL_CHAR_PARENLEFT;
5982 case 0x1680: // (green steel)
5983 element = EL_STEEL_CHAR_PARENRIGHT;
5986 case 0x1681: // gate (red)
5987 element = EL_EM_GATE_1;
5990 case 0x1682: // secret gate (red)
5991 element = EL_EM_GATE_1_GRAY;
5994 case 0x1683: // gate (yellow)
5995 element = EL_EM_GATE_2;
5998 case 0x1684: // secret gate (yellow)
5999 element = EL_EM_GATE_2_GRAY;
6002 case 0x1685: // gate (blue)
6003 element = EL_EM_GATE_4;
6006 case 0x1686: // secret gate (blue)
6007 element = EL_EM_GATE_4_GRAY;
6010 case 0x1687: // gate (green)
6011 element = EL_EM_GATE_3;
6014 case 0x1688: // secret gate (green)
6015 element = EL_EM_GATE_3_GRAY;
6018 case 0x1689: // gate (white)
6019 element = EL_DC_GATE_WHITE;
6022 case 0x168a: // secret gate (white)
6023 element = EL_DC_GATE_WHITE_GRAY;
6026 case 0x168b: // secret gate (no key)
6027 element = EL_DC_GATE_FAKE_GRAY;
6031 element = EL_ROBOT_WHEEL;
6035 element = EL_DC_TIMEGATE_SWITCH;
6039 element = EL_ACID_POOL_BOTTOM;
6043 element = EL_ACID_POOL_TOPLEFT;
6047 element = EL_ACID_POOL_TOPRIGHT;
6051 element = EL_ACID_POOL_BOTTOMLEFT;
6055 element = EL_ACID_POOL_BOTTOMRIGHT;
6059 element = EL_STEELWALL;
6063 element = EL_STEELWALL_SLIPPERY;
6066 case 0x1695: // steel wall (not round)
6067 element = EL_STEELWALL;
6070 case 0x1696: // steel wall (left)
6071 element = EL_DC_STEELWALL_1_LEFT;
6074 case 0x1697: // steel wall (bottom)
6075 element = EL_DC_STEELWALL_1_BOTTOM;
6078 case 0x1698: // steel wall (right)
6079 element = EL_DC_STEELWALL_1_RIGHT;
6082 case 0x1699: // steel wall (top)
6083 element = EL_DC_STEELWALL_1_TOP;
6086 case 0x169a: // steel wall (left/bottom)
6087 element = EL_DC_STEELWALL_1_BOTTOMLEFT;
6090 case 0x169b: // steel wall (right/bottom)
6091 element = EL_DC_STEELWALL_1_BOTTOMRIGHT;
6094 case 0x169c: // steel wall (right/top)
6095 element = EL_DC_STEELWALL_1_TOPRIGHT;
6098 case 0x169d: // steel wall (left/top)
6099 element = EL_DC_STEELWALL_1_TOPLEFT;
6102 case 0x169e: // steel wall (right/bottom small)
6103 element = EL_DC_STEELWALL_1_BOTTOMRIGHT_2;
6106 case 0x169f: // steel wall (left/bottom small)
6107 element = EL_DC_STEELWALL_1_BOTTOMLEFT_2;
6110 case 0x16a0: // steel wall (right/top small)
6111 element = EL_DC_STEELWALL_1_TOPRIGHT_2;
6114 case 0x16a1: // steel wall (left/top small)
6115 element = EL_DC_STEELWALL_1_TOPLEFT_2;
6118 case 0x16a2: // steel wall (left/right)
6119 element = EL_DC_STEELWALL_1_VERTICAL;
6122 case 0x16a3: // steel wall (top/bottom)
6123 element = EL_DC_STEELWALL_1_HORIZONTAL;
6126 case 0x16a4: // steel wall 2 (left end)
6127 element = EL_DC_STEELWALL_2_LEFT;
6130 case 0x16a5: // steel wall 2 (right end)
6131 element = EL_DC_STEELWALL_2_RIGHT;
6134 case 0x16a6: // steel wall 2 (top end)
6135 element = EL_DC_STEELWALL_2_TOP;
6138 case 0x16a7: // steel wall 2 (bottom end)
6139 element = EL_DC_STEELWALL_2_BOTTOM;
6142 case 0x16a8: // steel wall 2 (left/right)
6143 element = EL_DC_STEELWALL_2_HORIZONTAL;
6146 case 0x16a9: // steel wall 2 (up/down)
6147 element = EL_DC_STEELWALL_2_VERTICAL;
6150 case 0x16aa: // steel wall 2 (mid)
6151 element = EL_DC_STEELWALL_2_MIDDLE;
6155 element = EL_SIGN_EXCLAMATION;
6159 element = EL_SIGN_RADIOACTIVITY;
6163 element = EL_SIGN_STOP;
6167 element = EL_SIGN_WHEELCHAIR;
6171 element = EL_SIGN_PARKING;
6175 element = EL_SIGN_NO_ENTRY;
6179 element = EL_SIGN_HEART;
6183 element = EL_SIGN_GIVE_WAY;
6187 element = EL_SIGN_ENTRY_FORBIDDEN;
6191 element = EL_SIGN_EMERGENCY_EXIT;
6195 element = EL_SIGN_YIN_YANG;
6199 element = EL_WALL_EMERALD;
6203 element = EL_WALL_DIAMOND;
6207 element = EL_WALL_PEARL;
6211 element = EL_WALL_CRYSTAL;
6215 element = EL_INVISIBLE_WALL;
6219 element = EL_INVISIBLE_STEELWALL;
6223 // EL_INVISIBLE_SAND
6226 element = EL_LIGHT_SWITCH;
6230 element = EL_ENVELOPE_1;
6234 if (element >= 0x0117 && element <= 0x036e) // (?)
6235 element = EL_DIAMOND;
6236 else if (element >= 0x042d && element <= 0x0684) // (?)
6237 element = EL_EMERALD;
6238 else if (element >= 0x157c && element <= 0x158b)
6240 else if (element >= 0x1590 && element <= 0x159f)
6241 element = EL_DC_LANDMINE;
6242 else if (element >= 0x16bc && element <= 0x16cb)
6243 element = EL_INVISIBLE_SAND;
6246 Warn("unknown Diamond Caves element 0x%04x", element);
6248 element = EL_UNKNOWN;
6253 return getMappedElement(element);
6256 static void LoadLevelFromFileStream_DC(File *file, struct LevelInfo *level)
6258 byte header[DC_LEVEL_HEADER_SIZE];
6260 int envelope_header_pos = 62;
6261 int envelope_content_pos = 94;
6262 int level_name_pos = 251;
6263 int level_author_pos = 292;
6264 int envelope_header_len;
6265 int envelope_content_len;
6267 int level_author_len;
6269 int num_yamyam_contents;
6272 getDecodedWord_DC(0, TRUE); // initialize DC2 decoding engine
6274 for (i = 0; i < DC_LEVEL_HEADER_SIZE / 2; i++)
6276 unsigned short header_word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
6278 header[i * 2 + 0] = header_word >> 8;
6279 header[i * 2 + 1] = header_word & 0xff;
6282 // read some values from level header to check level decoding integrity
6283 fieldx = header[6] | (header[7] << 8);
6284 fieldy = header[8] | (header[9] << 8);
6285 num_yamyam_contents = header[60] | (header[61] << 8);
6287 // do some simple sanity checks to ensure that level was correctly decoded
6288 if (fieldx < 1 || fieldx > 256 ||
6289 fieldy < 1 || fieldy > 256 ||
6290 num_yamyam_contents < 1 || num_yamyam_contents > 8)
6292 level->no_valid_file = TRUE;
6294 Warn("cannot decode level from stream -- using empty level");
6299 // maximum envelope header size is 31 bytes
6300 envelope_header_len = header[envelope_header_pos];
6301 // maximum envelope content size is 110 (156?) bytes
6302 envelope_content_len = header[envelope_content_pos];
6304 // maximum level title size is 40 bytes
6305 level_name_len = MIN(header[level_name_pos], MAX_LEVEL_NAME_LEN);
6306 // maximum level author size is 30 (51?) bytes
6307 level_author_len = MIN(header[level_author_pos], MAX_LEVEL_AUTHOR_LEN);
6311 for (i = 0; i < envelope_header_len; i++)
6312 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
6313 level->envelope[0].text[envelope_size++] =
6314 header[envelope_header_pos + 1 + i];
6316 if (envelope_header_len > 0 && envelope_content_len > 0)
6318 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
6319 level->envelope[0].text[envelope_size++] = '\n';
6320 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
6321 level->envelope[0].text[envelope_size++] = '\n';
6324 for (i = 0; i < envelope_content_len; i++)
6325 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
6326 level->envelope[0].text[envelope_size++] =
6327 header[envelope_content_pos + 1 + i];
6329 level->envelope[0].text[envelope_size] = '\0';
6331 level->envelope[0].xsize = MAX_ENVELOPE_XSIZE;
6332 level->envelope[0].ysize = 10;
6333 level->envelope[0].autowrap = TRUE;
6334 level->envelope[0].centered = TRUE;
6336 for (i = 0; i < level_name_len; i++)
6337 level->name[i] = header[level_name_pos + 1 + i];
6338 level->name[level_name_len] = '\0';
6340 for (i = 0; i < level_author_len; i++)
6341 level->author[i] = header[level_author_pos + 1 + i];
6342 level->author[level_author_len] = '\0';
6344 num_yamyam_contents = header[60] | (header[61] << 8);
6345 level->num_yamyam_contents =
6346 MIN(MAX(MIN_ELEMENT_CONTENTS, num_yamyam_contents), MAX_ELEMENT_CONTENTS);
6348 for (i = 0; i < num_yamyam_contents; i++)
6350 for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
6352 unsigned short word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
6353 int element_dc = ((word & 0xff) << 8) | ((word >> 8) & 0xff);
6355 if (i < MAX_ELEMENT_CONTENTS)
6356 level->yamyam_content[i].e[x][y] = getMappedElement_DC(element_dc);
6360 fieldx = header[6] | (header[7] << 8);
6361 fieldy = header[8] | (header[9] << 8);
6362 level->fieldx = MIN(MAX(MIN_LEV_FIELDX, fieldx), MAX_LEV_FIELDX);
6363 level->fieldy = MIN(MAX(MIN_LEV_FIELDY, fieldy), MAX_LEV_FIELDY);
6365 for (y = 0; y < fieldy; y++) for (x = 0; x < fieldx; x++)
6367 unsigned short word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
6368 int element_dc = ((word & 0xff) << 8) | ((word >> 8) & 0xff);
6370 if (x < MAX_LEV_FIELDX && y < MAX_LEV_FIELDY)
6371 level->field[x][y] = getMappedElement_DC(element_dc);
6374 x = MIN(MAX(0, (header[10] | (header[11] << 8)) - 1), MAX_LEV_FIELDX - 1);
6375 y = MIN(MAX(0, (header[12] | (header[13] << 8)) - 1), MAX_LEV_FIELDY - 1);
6376 level->field[x][y] = EL_PLAYER_1;
6378 x = MIN(MAX(0, (header[14] | (header[15] << 8)) - 1), MAX_LEV_FIELDX - 1);
6379 y = MIN(MAX(0, (header[16] | (header[17] << 8)) - 1), MAX_LEV_FIELDY - 1);
6380 level->field[x][y] = EL_PLAYER_2;
6382 level->gems_needed = header[18] | (header[19] << 8);
6384 level->score[SC_EMERALD] = header[20] | (header[21] << 8);
6385 level->score[SC_DIAMOND] = header[22] | (header[23] << 8);
6386 level->score[SC_PEARL] = header[24] | (header[25] << 8);
6387 level->score[SC_CRYSTAL] = header[26] | (header[27] << 8);
6388 level->score[SC_NUT] = header[28] | (header[29] << 8);
6389 level->score[SC_ROBOT] = header[30] | (header[31] << 8);
6390 level->score[SC_SPACESHIP] = header[32] | (header[33] << 8);
6391 level->score[SC_BUG] = header[34] | (header[35] << 8);
6392 level->score[SC_YAMYAM] = header[36] | (header[37] << 8);
6393 level->score[SC_DYNAMITE] = header[38] | (header[39] << 8);
6394 level->score[SC_KEY] = header[40] | (header[41] << 8);
6395 level->score[SC_TIME_BONUS] = header[42] | (header[43] << 8);
6397 level->time = header[44] | (header[45] << 8);
6399 level->amoeba_speed = header[46] | (header[47] << 8);
6400 level->time_light = header[48] | (header[49] << 8);
6401 level->time_timegate = header[50] | (header[51] << 8);
6402 level->time_wheel = header[52] | (header[53] << 8);
6403 level->time_magic_wall = header[54] | (header[55] << 8);
6404 level->extra_time = header[56] | (header[57] << 8);
6405 level->shield_normal_time = header[58] | (header[59] << 8);
6407 // shield and extra time elements do not have a score
6408 level->score[SC_SHIELD] = 0;
6409 level->extra_time_score = 0;
6411 // set time for normal and deadly shields to the same value
6412 level->shield_deadly_time = level->shield_normal_time;
6414 // Diamond Caves has the same (strange) behaviour as Emerald Mine that gems
6415 // can slip down from flat walls, like normal walls and steel walls
6416 level->em_slippery_gems = TRUE;
6418 // time score is counted for each 10 seconds left in Diamond Caves levels
6419 level->time_score_base = 10;
6422 static void LoadLevelFromFileInfo_DC(struct LevelInfo *level,
6423 struct LevelFileInfo *level_file_info,
6424 boolean level_info_only)
6426 char *filename = level_file_info->filename;
6428 int num_magic_bytes = 8;
6429 char magic_bytes[num_magic_bytes + 1];
6430 int num_levels_to_skip = level_file_info->nr - leveldir_current->first_level;
6432 if (!(file = openFile(filename, MODE_READ)))
6434 level->no_valid_file = TRUE;
6436 if (!level_info_only)
6437 Warn("cannot read level '%s' -- using empty level", filename);
6442 // fseek(file, 0x0000, SEEK_SET);
6444 if (level_file_info->packed)
6446 // read "magic bytes" from start of file
6447 if (getStringFromFile(file, magic_bytes, num_magic_bytes + 1) == NULL)
6448 magic_bytes[0] = '\0';
6450 // check "magic bytes" for correct file format
6451 if (!strPrefix(magic_bytes, "DC2"))
6453 level->no_valid_file = TRUE;
6455 Warn("unknown DC level file '%s' -- using empty level", filename);
6460 if (strPrefix(magic_bytes, "DC2Win95") ||
6461 strPrefix(magic_bytes, "DC2Win98"))
6463 int position_first_level = 0x00fa;
6464 int extra_bytes = 4;
6467 // advance file stream to first level inside the level package
6468 skip_bytes = position_first_level - num_magic_bytes - extra_bytes;
6470 // each block of level data is followed by block of non-level data
6471 num_levels_to_skip *= 2;
6473 // at least skip header bytes, therefore use ">= 0" instead of "> 0"
6474 while (num_levels_to_skip >= 0)
6476 // advance file stream to next level inside the level package
6477 if (seekFile(file, skip_bytes, SEEK_CUR) != 0)
6479 level->no_valid_file = TRUE;
6481 Warn("cannot fseek in file '%s' -- using empty level", filename);
6486 // skip apparently unused extra bytes following each level
6487 ReadUnusedBytesFromFile(file, extra_bytes);
6489 // read size of next level in level package
6490 skip_bytes = getFile32BitLE(file);
6492 num_levels_to_skip--;
6497 level->no_valid_file = TRUE;
6499 Warn("unknown DC2 level file '%s' -- using empty level", filename);
6505 LoadLevelFromFileStream_DC(file, level);
6511 // ----------------------------------------------------------------------------
6512 // functions for loading SB level
6513 // ----------------------------------------------------------------------------
6515 int getMappedElement_SB(int element_ascii, boolean use_ces)
6523 sb_element_mapping[] =
6525 { ' ', EL_EMPTY, EL_CUSTOM_1 }, // floor (space)
6526 { '#', EL_STEELWALL, EL_CUSTOM_2 }, // wall
6527 { '@', EL_PLAYER_1, EL_CUSTOM_3 }, // player
6528 { '$', EL_SOKOBAN_OBJECT, EL_CUSTOM_4 }, // box
6529 { '.', EL_SOKOBAN_FIELD_EMPTY, EL_CUSTOM_5 }, // goal square
6530 { '*', EL_SOKOBAN_FIELD_FULL, EL_CUSTOM_6 }, // box on goal square
6531 { '+', EL_SOKOBAN_FIELD_PLAYER, EL_CUSTOM_7 }, // player on goal square
6532 { '_', EL_INVISIBLE_STEELWALL, EL_FROM_LEVEL_TEMPLATE }, // floor beyond border
6539 for (i = 0; sb_element_mapping[i].ascii != 0; i++)
6540 if (element_ascii == sb_element_mapping[i].ascii)
6541 return (use_ces ? sb_element_mapping[i].ce : sb_element_mapping[i].sb);
6543 return EL_UNDEFINED;
6546 static void SetLevelSettings_SB(struct LevelInfo *level)
6550 level->use_step_counter = TRUE;
6553 level->score[SC_TIME_BONUS] = 0;
6554 level->time_score_base = 1;
6555 level->rate_time_over_score = TRUE;
6558 level->auto_exit_sokoban = TRUE;
6561 static void LoadLevelFromFileInfo_SB(struct LevelInfo *level,
6562 struct LevelFileInfo *level_file_info,
6563 boolean level_info_only)
6565 char *filename = level_file_info->filename;
6566 char line[MAX_LINE_LEN], line_raw[MAX_LINE_LEN], previous_line[MAX_LINE_LEN];
6567 char last_comment[MAX_LINE_LEN];
6568 char level_name[MAX_LINE_LEN];
6571 int num_levels_to_skip = level_file_info->nr - leveldir_current->first_level;
6572 boolean read_continued_line = FALSE;
6573 boolean reading_playfield = FALSE;
6574 boolean got_valid_playfield_line = FALSE;
6575 boolean invalid_playfield_char = FALSE;
6576 boolean load_xsb_to_ces = check_special_flags("load_xsb_to_ces");
6577 int file_level_nr = 0;
6578 int x = 0, y = 0; // initialized to make compilers happy
6580 last_comment[0] = '\0';
6581 level_name[0] = '\0';
6583 if (!(file = openFile(filename, MODE_READ)))
6585 level->no_valid_file = TRUE;
6587 if (!level_info_only)
6588 Warn("cannot read level '%s' -- using empty level", filename);
6593 while (!checkEndOfFile(file))
6595 // level successfully read, but next level may follow here
6596 if (!got_valid_playfield_line && reading_playfield)
6598 // read playfield from single level file -- skip remaining file
6599 if (!level_file_info->packed)
6602 if (file_level_nr >= num_levels_to_skip)
6607 last_comment[0] = '\0';
6608 level_name[0] = '\0';
6610 reading_playfield = FALSE;
6613 got_valid_playfield_line = FALSE;
6615 // read next line of input file
6616 if (!getStringFromFile(file, line, MAX_LINE_LEN))
6619 // cut trailing line break (this can be newline and/or carriage return)
6620 for (line_ptr = &line[strlen(line)]; line_ptr >= line; line_ptr--)
6621 if ((*line_ptr == '\n' || *line_ptr == '\r') && *(line_ptr + 1) == '\0')
6624 // copy raw input line for later use (mainly debugging output)
6625 strcpy(line_raw, line);
6627 if (read_continued_line)
6629 // append new line to existing line, if there is enough space
6630 if (strlen(previous_line) + strlen(line_ptr) < MAX_LINE_LEN)
6631 strcat(previous_line, line_ptr);
6633 strcpy(line, previous_line); // copy storage buffer to line
6635 read_continued_line = FALSE;
6638 // if the last character is '\', continue at next line
6639 if (strlen(line) > 0 && line[strlen(line) - 1] == '\\')
6641 line[strlen(line) - 1] = '\0'; // cut off trailing backslash
6642 strcpy(previous_line, line); // copy line to storage buffer
6644 read_continued_line = TRUE;
6650 if (line[0] == '\0')
6653 // extract comment text from comment line
6656 for (line_ptr = line; *line_ptr; line_ptr++)
6657 if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != ';')
6660 strcpy(last_comment, line_ptr);
6665 // extract level title text from line containing level title
6666 if (line[0] == '\'')
6668 strcpy(level_name, &line[1]);
6670 if (strlen(level_name) > 0 && level_name[strlen(level_name) - 1] == '\'')
6671 level_name[strlen(level_name) - 1] = '\0';
6676 // skip lines containing only spaces (or empty lines)
6677 for (line_ptr = line; *line_ptr; line_ptr++)
6678 if (*line_ptr != ' ')
6680 if (*line_ptr == '\0')
6683 // at this point, we have found a line containing part of a playfield
6685 got_valid_playfield_line = TRUE;
6687 if (!reading_playfield)
6689 reading_playfield = TRUE;
6690 invalid_playfield_char = FALSE;
6692 for (x = 0; x < MAX_LEV_FIELDX; x++)
6693 for (y = 0; y < MAX_LEV_FIELDY; y++)
6694 level->field[x][y] = getMappedElement_SB(' ', load_xsb_to_ces);
6699 // start with topmost tile row
6703 // skip playfield line if larger row than allowed
6704 if (y >= MAX_LEV_FIELDY)
6707 // start with leftmost tile column
6710 // read playfield elements from line
6711 for (line_ptr = line; *line_ptr; line_ptr++)
6713 int mapped_sb_element = getMappedElement_SB(*line_ptr, load_xsb_to_ces);
6715 // stop parsing playfield line if larger column than allowed
6716 if (x >= MAX_LEV_FIELDX)
6719 if (mapped_sb_element == EL_UNDEFINED)
6721 invalid_playfield_char = TRUE;
6726 level->field[x][y] = mapped_sb_element;
6728 // continue with next tile column
6731 level->fieldx = MAX(x, level->fieldx);
6734 if (invalid_playfield_char)
6736 // if first playfield line, treat invalid lines as comment lines
6738 reading_playfield = FALSE;
6743 // continue with next tile row
6751 level->fieldx = MIN(MAX(MIN_LEV_FIELDX, level->fieldx), MAX_LEV_FIELDX);
6752 level->fieldy = MIN(MAX(MIN_LEV_FIELDY, level->fieldy), MAX_LEV_FIELDY);
6754 if (!reading_playfield)
6756 level->no_valid_file = TRUE;
6758 Warn("cannot read level '%s' -- using empty level", filename);
6763 if (*level_name != '\0')
6765 strncpy(level->name, level_name, MAX_LEVEL_NAME_LEN);
6766 level->name[MAX_LEVEL_NAME_LEN] = '\0';
6768 else if (*last_comment != '\0')
6770 strncpy(level->name, last_comment, MAX_LEVEL_NAME_LEN);
6771 level->name[MAX_LEVEL_NAME_LEN] = '\0';
6775 sprintf(level->name, "--> Level %d <--", level_file_info->nr);
6778 // set all empty fields beyond the border walls to invisible steel wall
6779 for (y = 0; y < level->fieldy; y++) for (x = 0; x < level->fieldx; x++)
6781 if ((x == 0 || x == level->fieldx - 1 ||
6782 y == 0 || y == level->fieldy - 1) &&
6783 level->field[x][y] == getMappedElement_SB(' ', load_xsb_to_ces))
6784 FloodFillLevel(x, y, getMappedElement_SB('_', load_xsb_to_ces),
6785 level->field, level->fieldx, level->fieldy);
6788 // set special level settings for Sokoban levels
6789 SetLevelSettings_SB(level);
6791 if (load_xsb_to_ces)
6793 // special global settings can now be set in level template
6794 level->use_custom_template = TRUE;
6799 // -------------------------------------------------------------------------
6800 // functions for handling native levels
6801 // -------------------------------------------------------------------------
6803 static void LoadLevelFromFileInfo_BD(struct LevelInfo *level,
6804 struct LevelFileInfo *level_file_info,
6805 boolean level_info_only)
6809 // determine position of requested level inside level package
6810 if (level_file_info->packed)
6811 pos = level_file_info->nr - leveldir_current->first_level;
6813 if (!LoadNativeLevel_BD(level_file_info->filename, pos, level_info_only))
6814 level->no_valid_file = TRUE;
6817 static void LoadLevelFromFileInfo_EM(struct LevelInfo *level,
6818 struct LevelFileInfo *level_file_info,
6819 boolean level_info_only)
6821 if (!LoadNativeLevel_EM(level_file_info->filename, level_info_only))
6822 level->no_valid_file = TRUE;
6825 static void LoadLevelFromFileInfo_SP(struct LevelInfo *level,
6826 struct LevelFileInfo *level_file_info,
6827 boolean level_info_only)
6831 // determine position of requested level inside level package
6832 if (level_file_info->packed)
6833 pos = level_file_info->nr - leveldir_current->first_level;
6835 if (!LoadNativeLevel_SP(level_file_info->filename, pos, level_info_only))
6836 level->no_valid_file = TRUE;
6839 static void LoadLevelFromFileInfo_MM(struct LevelInfo *level,
6840 struct LevelFileInfo *level_file_info,
6841 boolean level_info_only)
6843 if (!LoadNativeLevel_MM(level_file_info->filename, level_info_only))
6844 level->no_valid_file = TRUE;
6847 void CopyNativeLevel_RND_to_Native(struct LevelInfo *level)
6849 if (level->game_engine_type == GAME_ENGINE_TYPE_BD)
6850 CopyNativeLevel_RND_to_BD(level);
6851 else if (level->game_engine_type == GAME_ENGINE_TYPE_EM)
6852 CopyNativeLevel_RND_to_EM(level);
6853 else if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
6854 CopyNativeLevel_RND_to_SP(level);
6855 else if (level->game_engine_type == GAME_ENGINE_TYPE_MM)
6856 CopyNativeLevel_RND_to_MM(level);
6859 void CopyNativeLevel_Native_to_RND(struct LevelInfo *level)
6861 if (level->game_engine_type == GAME_ENGINE_TYPE_BD)
6862 CopyNativeLevel_BD_to_RND(level);
6863 else if (level->game_engine_type == GAME_ENGINE_TYPE_EM)
6864 CopyNativeLevel_EM_to_RND(level);
6865 else if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
6866 CopyNativeLevel_SP_to_RND(level);
6867 else if (level->game_engine_type == GAME_ENGINE_TYPE_MM)
6868 CopyNativeLevel_MM_to_RND(level);
6871 void SaveNativeLevel(struct LevelInfo *level)
6873 // saving native level files only supported for some game engines
6874 if (level->game_engine_type != GAME_ENGINE_TYPE_BD &&
6875 level->game_engine_type != GAME_ENGINE_TYPE_SP)
6878 char *file_ext = (level->game_engine_type == GAME_ENGINE_TYPE_BD ? "bd" :
6879 level->game_engine_type == GAME_ENGINE_TYPE_SP ? "sp" : "");
6880 char *basename = getSingleLevelBasenameExt(level->file_info.nr, file_ext);
6881 char *filename = getLevelFilenameFromBasename(basename);
6883 if (fileExists(filename) && !Request("Native level file already exists! Overwrite it?", REQ_ASK))
6886 boolean success = FALSE;
6888 if (level->game_engine_type == GAME_ENGINE_TYPE_BD)
6890 CopyNativeLevel_RND_to_BD(level);
6891 // CopyNativeTape_RND_to_BD(level);
6893 success = SaveNativeLevel_BD(filename);
6895 else if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
6897 CopyNativeLevel_RND_to_SP(level);
6898 CopyNativeTape_RND_to_SP(level);
6900 success = SaveNativeLevel_SP(filename);
6904 Request("Native level file saved!", REQ_CONFIRM);
6906 Request("Failed to save native level file!", REQ_CONFIRM);
6910 // ----------------------------------------------------------------------------
6911 // functions for loading generic level
6912 // ----------------------------------------------------------------------------
6914 static void LoadLevelFromFileInfo(struct LevelInfo *level,
6915 struct LevelFileInfo *level_file_info,
6916 boolean level_info_only)
6918 // always start with reliable default values
6919 setLevelInfoToDefaults(level, level_info_only, TRUE);
6921 switch (level_file_info->type)
6923 case LEVEL_FILE_TYPE_RND:
6924 LoadLevelFromFileInfo_RND(level, level_file_info, level_info_only);
6927 case LEVEL_FILE_TYPE_BD:
6928 LoadLevelFromFileInfo_BD(level, level_file_info, level_info_only);
6929 level->game_engine_type = GAME_ENGINE_TYPE_BD;
6932 case LEVEL_FILE_TYPE_EM:
6933 LoadLevelFromFileInfo_EM(level, level_file_info, level_info_only);
6934 level->game_engine_type = GAME_ENGINE_TYPE_EM;
6937 case LEVEL_FILE_TYPE_SP:
6938 LoadLevelFromFileInfo_SP(level, level_file_info, level_info_only);
6939 level->game_engine_type = GAME_ENGINE_TYPE_SP;
6942 case LEVEL_FILE_TYPE_MM:
6943 LoadLevelFromFileInfo_MM(level, level_file_info, level_info_only);
6944 level->game_engine_type = GAME_ENGINE_TYPE_MM;
6947 case LEVEL_FILE_TYPE_DC:
6948 LoadLevelFromFileInfo_DC(level, level_file_info, level_info_only);
6951 case LEVEL_FILE_TYPE_SB:
6952 LoadLevelFromFileInfo_SB(level, level_file_info, level_info_only);
6956 LoadLevelFromFileInfo_RND(level, level_file_info, level_info_only);
6960 // if level file is invalid, restore level structure to default values
6961 if (level->no_valid_file)
6962 setLevelInfoToDefaults(level, level_info_only, FALSE);
6964 if (check_special_flags("use_native_bd_game_engine"))
6965 level->game_engine_type = GAME_ENGINE_TYPE_BD;
6967 if (level->game_engine_type == GAME_ENGINE_TYPE_UNKNOWN)
6968 level->game_engine_type = GAME_ENGINE_TYPE_RND;
6970 if (level_file_info->type != LEVEL_FILE_TYPE_RND)
6971 CopyNativeLevel_Native_to_RND(level);
6974 void LoadLevelFromFilename(struct LevelInfo *level, char *filename)
6976 static struct LevelFileInfo level_file_info;
6978 // always start with reliable default values
6979 setFileInfoToDefaults(&level_file_info);
6981 level_file_info.nr = 0; // unknown level number
6982 level_file_info.type = LEVEL_FILE_TYPE_RND; // no others supported yet
6984 setString(&level_file_info.filename, filename);
6986 LoadLevelFromFileInfo(level, &level_file_info, FALSE);
6989 static void LoadLevel_InitVersion(struct LevelInfo *level)
6993 if (leveldir_current == NULL) // only when dumping level
6996 // all engine modifications also valid for levels which use latest engine
6997 if (level->game_version < VERSION_IDENT(3,2,0,5))
6999 // time bonus score was given for 10 s instead of 1 s before 3.2.0-5
7000 level->time_score_base = 10;
7003 if (leveldir_current->latest_engine)
7005 // ---------- use latest game engine --------------------------------------
7007 /* For all levels which are forced to use the latest game engine version
7008 (normally all but user contributed, private and undefined levels), set
7009 the game engine version to the actual version; this allows for actual
7010 corrections in the game engine to take effect for existing, converted
7011 levels (from "classic" or other existing games) to make the emulation
7012 of the corresponding game more accurate, while (hopefully) not breaking
7013 existing levels created from other players. */
7015 level->game_version = GAME_VERSION_ACTUAL;
7017 /* Set special EM style gems behaviour: EM style gems slip down from
7018 normal, steel and growing wall. As this is a more fundamental change,
7019 it seems better to set the default behaviour to "off" (as it is more
7020 natural) and make it configurable in the level editor (as a property
7021 of gem style elements). Already existing converted levels (neither
7022 private nor contributed levels) are changed to the new behaviour. */
7024 if (level->file_version < FILE_VERSION_2_0)
7025 level->em_slippery_gems = TRUE;
7030 // ---------- use game engine the level was created with --------------------
7032 /* For all levels which are not forced to use the latest game engine
7033 version (normally user contributed, private and undefined levels),
7034 use the version of the game engine the levels were created for.
7036 Since 2.0.1, the game engine version is now directly stored
7037 in the level file (chunk "VERS"), so there is no need anymore
7038 to set the game version from the file version (except for old,
7039 pre-2.0 levels, where the game version is still taken from the
7040 file format version used to store the level -- see above). */
7042 // player was faster than enemies in 1.0.0 and before
7043 if (level->file_version == FILE_VERSION_1_0)
7044 for (i = 0; i < MAX_PLAYERS; i++)
7045 level->initial_player_stepsize[i] = STEPSIZE_FAST;
7047 // default behaviour for EM style gems was "slippery" only in 2.0.1
7048 if (level->game_version == VERSION_IDENT(2,0,1,0))
7049 level->em_slippery_gems = TRUE;
7051 // springs could be pushed over pits before (pre-release version) 2.2.0
7052 if (level->game_version < VERSION_IDENT(2,2,0,0))
7053 level->use_spring_bug = TRUE;
7055 if (level->game_version < VERSION_IDENT(3,2,0,5))
7057 // time orb caused limited time in endless time levels before 3.2.0-5
7058 level->use_time_orb_bug = TRUE;
7060 // default behaviour for snapping was "no snap delay" before 3.2.0-5
7061 level->block_snap_field = FALSE;
7063 // extra time score was same value as time left score before 3.2.0-5
7064 level->extra_time_score = level->score[SC_TIME_BONUS];
7067 if (level->game_version < VERSION_IDENT(3,2,0,7))
7069 // default behaviour for snapping was "not continuous" before 3.2.0-7
7070 level->continuous_snapping = FALSE;
7073 // only few elements were able to actively move into acid before 3.1.0
7074 // trigger settings did not exist before 3.1.0; set to default "any"
7075 if (level->game_version < VERSION_IDENT(3,1,0,0))
7077 // correct "can move into acid" settings (all zero in old levels)
7079 level->can_move_into_acid_bits = 0; // nothing can move into acid
7080 level->dont_collide_with_bits = 0; // nothing is deadly when colliding
7082 setMoveIntoAcidProperty(level, EL_ROBOT, TRUE);
7083 setMoveIntoAcidProperty(level, EL_SATELLITE, TRUE);
7084 setMoveIntoAcidProperty(level, EL_PENGUIN, TRUE);
7085 setMoveIntoAcidProperty(level, EL_BALLOON, TRUE);
7087 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7088 SET_PROPERTY(EL_CUSTOM_START + i, EP_CAN_MOVE_INTO_ACID, TRUE);
7090 // correct trigger settings (stored as zero == "none" in old levels)
7092 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7094 int element = EL_CUSTOM_START + i;
7095 struct ElementInfo *ei = &element_info[element];
7097 for (j = 0; j < ei->num_change_pages; j++)
7099 struct ElementChangeInfo *change = &ei->change_page[j];
7101 change->trigger_player = CH_PLAYER_ANY;
7102 change->trigger_page = CH_PAGE_ANY;
7107 // try to detect and fix "Snake Bite" levels, which are broken with 3.2.0
7109 int element = EL_CUSTOM_256;
7110 struct ElementInfo *ei = &element_info[element];
7111 struct ElementChangeInfo *change = &ei->change_page[0];
7113 /* This is needed to fix a problem that was caused by a bugfix in function
7114 game.c/CreateFieldExt() introduced with 3.2.0 that corrects the behaviour
7115 when a custom element changes to EL_SOKOBAN_FIELD_PLAYER (before, it did
7116 not replace walkable elements, but instead just placed the player on it,
7117 without placing the Sokoban field under the player). Unfortunately, this
7118 breaks "Snake Bite" style levels when the snake is halfway through a door
7119 that just closes (the snake head is still alive and can be moved in this
7120 case). This can be fixed by replacing the EL_SOKOBAN_FIELD_PLAYER by the
7121 player (without Sokoban element) which then gets killed as designed). */
7123 if ((strncmp(leveldir_current->identifier, "snake_bite", 10) == 0 ||
7124 strncmp(ei->description, "pause b4 death", 14) == 0) &&
7125 change->target_element == EL_SOKOBAN_FIELD_PLAYER)
7126 change->target_element = EL_PLAYER_1;
7129 // try to detect and fix "Zelda" style levels, which are broken with 3.2.5
7130 if (level->game_version < VERSION_IDENT(3,2,5,0))
7132 /* This is needed to fix a problem that was caused by a bugfix in function
7133 game.c/CheckTriggeredElementChangeExt() introduced with 3.2.5 that
7134 corrects the behaviour when a custom element changes to another custom
7135 element with a higher element number that has change actions defined.
7136 Normally, only one change per frame is allowed for custom elements.
7137 Therefore, it is checked if a custom element already changed in the
7138 current frame; if it did, subsequent changes are suppressed.
7139 Unfortunately, this is only checked for element changes, but not for
7140 change actions, which are still executed. As the function above loops
7141 through all custom elements from lower to higher, an element change
7142 resulting in a lower CE number won't be checked again, while a target
7143 element with a higher number will also be checked, and potential change
7144 actions will get executed for this CE, too (which is wrong), while
7145 further changes are ignored (which is correct). As this bugfix breaks
7146 Zelda II (and introduces graphical bugs to Zelda I, and also breaks a
7147 few other levels like Alan Bond's "FMV"), allow the previous, incorrect
7148 behaviour for existing levels and tapes that make use of this bug */
7150 level->use_action_after_change_bug = TRUE;
7153 // not centering level after relocating player was default only in 3.2.3
7154 if (level->game_version == VERSION_IDENT(3,2,3,0)) // (no pre-releases)
7155 level->shifted_relocation = TRUE;
7157 // EM style elements always chain-exploded in R'n'D engine before 3.2.6
7158 if (level->game_version < VERSION_IDENT(3,2,6,0))
7159 level->em_explodes_by_fire = TRUE;
7161 // levels were solved by the first player entering an exit up to 4.1.0.0
7162 if (level->game_version <= VERSION_IDENT(4,1,0,0))
7163 level->solved_by_one_player = TRUE;
7165 // game logic of "game of life" and "biomaze" was buggy before 4.1.1.1
7166 if (level->game_version < VERSION_IDENT(4,1,1,1))
7167 level->use_life_bugs = TRUE;
7169 // only Sokoban fields (but not objects) had to be solved before 4.1.1.1
7170 if (level->game_version < VERSION_IDENT(4,1,1,1))
7171 level->sb_objects_needed = FALSE;
7173 // CE actions were triggered by unfinished digging/collecting up to 4.2.2.0
7174 if (level->game_version <= VERSION_IDENT(4,2,2,0))
7175 level->finish_dig_collect = FALSE;
7177 // CE changing to player was kept under the player if walkable up to 4.2.3.1
7178 if (level->game_version <= VERSION_IDENT(4,2,3,1))
7179 level->keep_walkable_ce = TRUE;
7182 static void LoadLevel_InitSettings_SB(struct LevelInfo *level)
7184 boolean is_sokoban_level = TRUE; // unless non-Sokoban elements found
7187 // check if this level is (not) a Sokoban level
7188 for (y = 0; y < level->fieldy; y++)
7189 for (x = 0; x < level->fieldx; x++)
7190 if (!IS_SB_ELEMENT(Tile[x][y]))
7191 is_sokoban_level = FALSE;
7193 if (is_sokoban_level)
7195 // set special level settings for Sokoban levels
7196 SetLevelSettings_SB(level);
7200 static void LoadLevel_InitSettings(struct LevelInfo *level)
7202 // adjust level settings for (non-native) Sokoban-style levels
7203 LoadLevel_InitSettings_SB(level);
7205 // rename levels with title "nameless level" or if renaming is forced
7206 if (leveldir_current->empty_level_name != NULL &&
7207 (strEqual(level->name, NAMELESS_LEVEL_NAME) ||
7208 leveldir_current->force_level_name))
7209 snprintf(level->name, MAX_LEVEL_NAME_LEN + 1,
7210 leveldir_current->empty_level_name, level_nr);
7213 static void LoadLevel_InitStandardElements(struct LevelInfo *level)
7217 // map elements that have changed in newer versions
7218 level->amoeba_content = getMappedElementByVersion(level->amoeba_content,
7219 level->game_version);
7220 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
7221 for (x = 0; x < 3; x++)
7222 for (y = 0; y < 3; y++)
7223 level->yamyam_content[i].e[x][y] =
7224 getMappedElementByVersion(level->yamyam_content[i].e[x][y],
7225 level->game_version);
7229 static void LoadLevel_InitCustomElements(struct LevelInfo *level)
7233 // map custom element change events that have changed in newer versions
7234 // (these following values were accidentally changed in version 3.0.1)
7235 // (this seems to be needed only for 'ab_levelset3' and 'ab_levelset4')
7236 if (level->game_version <= VERSION_IDENT(3,0,0,0))
7238 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7240 int element = EL_CUSTOM_START + i;
7242 // order of checking and copying events to be mapped is important
7243 // (do not change the start and end value -- they are constant)
7244 for (j = CE_BY_OTHER_ACTION; j >= CE_VALUE_GETS_ZERO; j--)
7246 if (HAS_CHANGE_EVENT(element, j - 2))
7248 SET_CHANGE_EVENT(element, j - 2, FALSE);
7249 SET_CHANGE_EVENT(element, j, TRUE);
7253 // order of checking and copying events to be mapped is important
7254 // (do not change the start and end value -- they are constant)
7255 for (j = CE_PLAYER_COLLECTS_X; j >= CE_HITTING_SOMETHING; j--)
7257 if (HAS_CHANGE_EVENT(element, j - 1))
7259 SET_CHANGE_EVENT(element, j - 1, FALSE);
7260 SET_CHANGE_EVENT(element, j, TRUE);
7266 // initialize "can_change" field for old levels with only one change page
7267 if (level->game_version <= VERSION_IDENT(3,0,2,0))
7269 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7271 int element = EL_CUSTOM_START + i;
7273 if (CAN_CHANGE(element))
7274 element_info[element].change->can_change = TRUE;
7278 // correct custom element values (for old levels without these options)
7279 if (level->game_version < VERSION_IDENT(3,1,1,0))
7281 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7283 int element = EL_CUSTOM_START + i;
7284 struct ElementInfo *ei = &element_info[element];
7286 if (ei->access_direction == MV_NO_DIRECTION)
7287 ei->access_direction = MV_ALL_DIRECTIONS;
7291 // correct custom element values (fix invalid values for all versions)
7294 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7296 int element = EL_CUSTOM_START + i;
7297 struct ElementInfo *ei = &element_info[element];
7299 for (j = 0; j < ei->num_change_pages; j++)
7301 struct ElementChangeInfo *change = &ei->change_page[j];
7303 if (change->trigger_player == CH_PLAYER_NONE)
7304 change->trigger_player = CH_PLAYER_ANY;
7306 if (change->trigger_side == CH_SIDE_NONE)
7307 change->trigger_side = CH_SIDE_ANY;
7312 // initialize "can_explode" field for old levels which did not store this
7313 // !!! CHECK THIS -- "<= 3,1,0,0" IS PROBABLY WRONG !!!
7314 if (level->game_version <= VERSION_IDENT(3,1,0,0))
7316 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7318 int element = EL_CUSTOM_START + i;
7320 if (EXPLODES_1X1_OLD(element))
7321 element_info[element].explosion_type = EXPLODES_1X1;
7323 SET_PROPERTY(element, EP_CAN_EXPLODE, (EXPLODES_BY_FIRE(element) ||
7324 EXPLODES_SMASHED(element) ||
7325 EXPLODES_IMPACT(element)));
7329 // correct previously hard-coded move delay values for maze runner style
7330 if (level->game_version < VERSION_IDENT(3,1,1,0))
7332 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7334 int element = EL_CUSTOM_START + i;
7336 if (element_info[element].move_pattern & MV_MAZE_RUNNER_STYLE)
7338 // previously hard-coded and therefore ignored
7339 element_info[element].move_delay_fixed = 9;
7340 element_info[element].move_delay_random = 0;
7345 // set some other uninitialized values of custom elements in older levels
7346 if (level->game_version < VERSION_IDENT(3,1,0,0))
7348 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7350 int element = EL_CUSTOM_START + i;
7352 element_info[element].access_direction = MV_ALL_DIRECTIONS;
7354 element_info[element].explosion_delay = 17;
7355 element_info[element].ignition_delay = 8;
7359 // set mouse click change events to work for left/middle/right mouse button
7360 if (level->game_version < VERSION_IDENT(4,2,3,0))
7362 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7364 int element = EL_CUSTOM_START + i;
7365 struct ElementInfo *ei = &element_info[element];
7367 for (j = 0; j < ei->num_change_pages; j++)
7369 struct ElementChangeInfo *change = &ei->change_page[j];
7371 if (change->has_event[CE_CLICKED_BY_MOUSE] ||
7372 change->has_event[CE_PRESSED_BY_MOUSE] ||
7373 change->has_event[CE_MOUSE_CLICKED_ON_X] ||
7374 change->has_event[CE_MOUSE_PRESSED_ON_X])
7375 change->trigger_side = CH_SIDE_ANY;
7381 static void LoadLevel_InitElements(struct LevelInfo *level)
7383 LoadLevel_InitStandardElements(level);
7385 if (level->file_has_custom_elements)
7386 LoadLevel_InitCustomElements(level);
7388 // initialize element properties for level editor etc.
7389 InitElementPropertiesEngine(level->game_version);
7390 InitElementPropertiesGfxElement();
7393 static void LoadLevel_InitPlayfield(struct LevelInfo *level)
7397 // map elements that have changed in newer versions
7398 for (y = 0; y < level->fieldy; y++)
7399 for (x = 0; x < level->fieldx; x++)
7400 level->field[x][y] = getMappedElementByVersion(level->field[x][y],
7401 level->game_version);
7403 // clear unused playfield data (nicer if level gets resized in editor)
7404 for (x = 0; x < MAX_LEV_FIELDX; x++)
7405 for (y = 0; y < MAX_LEV_FIELDY; y++)
7406 if (x >= level->fieldx || y >= level->fieldy)
7407 level->field[x][y] = EL_EMPTY;
7409 // copy elements to runtime playfield array
7410 for (x = 0; x < MAX_LEV_FIELDX; x++)
7411 for (y = 0; y < MAX_LEV_FIELDY; y++)
7412 Tile[x][y] = level->field[x][y];
7414 // initialize level size variables for faster access
7415 lev_fieldx = level->fieldx;
7416 lev_fieldy = level->fieldy;
7418 // determine border element for this level
7419 if (level->file_info.type == LEVEL_FILE_TYPE_DC)
7420 BorderElement = EL_EMPTY; // (in editor, SetBorderElement() is used)
7425 static void LoadLevel_InitNativeEngines(struct LevelInfo *level)
7427 struct LevelFileInfo *level_file_info = &level->file_info;
7429 if (level_file_info->type == LEVEL_FILE_TYPE_RND)
7430 CopyNativeLevel_RND_to_Native(level);
7433 static void LoadLevelTemplate_LoadAndInit(void)
7435 LoadLevelFromFileInfo(&level_template, &level_template.file_info, FALSE);
7437 LoadLevel_InitVersion(&level_template);
7438 LoadLevel_InitElements(&level_template);
7439 LoadLevel_InitSettings(&level_template);
7441 ActivateLevelTemplate();
7444 void LoadLevelTemplate(int nr)
7446 if (!fileExists(getGlobalLevelTemplateFilename()))
7448 Warn("no level template found for this level");
7453 setLevelFileInfo(&level_template.file_info, nr);
7455 LoadLevelTemplate_LoadAndInit();
7458 static void LoadNetworkLevelTemplate(struct NetworkLevelInfo *network_level)
7460 copyLevelFileInfo(&network_level->tmpl_info, &level_template.file_info);
7462 LoadLevelTemplate_LoadAndInit();
7465 static void LoadLevel_LoadAndInit(struct NetworkLevelInfo *network_level)
7467 LoadLevelFromFileInfo(&level, &level.file_info, FALSE);
7469 if (level.use_custom_template)
7471 if (network_level != NULL)
7472 LoadNetworkLevelTemplate(network_level);
7474 LoadLevelTemplate(-1);
7477 LoadLevel_InitVersion(&level);
7478 LoadLevel_InitElements(&level);
7479 LoadLevel_InitPlayfield(&level);
7480 LoadLevel_InitSettings(&level);
7482 LoadLevel_InitNativeEngines(&level);
7485 void LoadLevel(int nr)
7487 SetLevelSetInfo(leveldir_current->identifier, nr);
7489 setLevelFileInfo(&level.file_info, nr);
7491 LoadLevel_LoadAndInit(NULL);
7494 void LoadLevelInfoOnly(int nr)
7496 setLevelFileInfo(&level.file_info, nr);
7498 LoadLevelFromFileInfo(&level, &level.file_info, TRUE);
7501 void LoadNetworkLevel(struct NetworkLevelInfo *network_level)
7503 SetLevelSetInfo(network_level->leveldir_identifier,
7504 network_level->file_info.nr);
7506 copyLevelFileInfo(&network_level->file_info, &level.file_info);
7508 LoadLevel_LoadAndInit(network_level);
7511 static int SaveLevel_VERS(FILE *file, struct LevelInfo *level)
7515 chunk_size += putFileVersion(file, level->file_version);
7516 chunk_size += putFileVersion(file, level->game_version);
7521 static int SaveLevel_DATE(FILE *file, struct LevelInfo *level)
7525 chunk_size += putFile16BitBE(file, level->creation_date.year);
7526 chunk_size += putFile8Bit(file, level->creation_date.month);
7527 chunk_size += putFile8Bit(file, level->creation_date.day);
7532 #if ENABLE_HISTORIC_CHUNKS
7533 static void SaveLevel_HEAD(FILE *file, struct LevelInfo *level)
7537 putFile8Bit(file, level->fieldx);
7538 putFile8Bit(file, level->fieldy);
7540 putFile16BitBE(file, level->time);
7541 putFile16BitBE(file, level->gems_needed);
7543 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
7544 putFile8Bit(file, level->name[i]);
7546 for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
7547 putFile8Bit(file, level->score[i]);
7549 for (i = 0; i < STD_ELEMENT_CONTENTS; i++)
7550 for (y = 0; y < 3; y++)
7551 for (x = 0; x < 3; x++)
7552 putFile8Bit(file, (level->encoding_16bit_yamyam ? EL_EMPTY :
7553 level->yamyam_content[i].e[x][y]));
7554 putFile8Bit(file, level->amoeba_speed);
7555 putFile8Bit(file, level->time_magic_wall);
7556 putFile8Bit(file, level->time_wheel);
7557 putFile8Bit(file, (level->encoding_16bit_amoeba ? EL_EMPTY :
7558 level->amoeba_content));
7559 putFile8Bit(file, (level->initial_player_stepsize == STEPSIZE_FAST ? 1 : 0));
7560 putFile8Bit(file, (level->initial_gravity ? 1 : 0));
7561 putFile8Bit(file, (level->encoding_16bit_field ? 1 : 0));
7562 putFile8Bit(file, (level->em_slippery_gems ? 1 : 0));
7564 putFile8Bit(file, (level->use_custom_template ? 1 : 0));
7566 putFile8Bit(file, (level->block_last_field ? 1 : 0));
7567 putFile8Bit(file, (level->sp_block_last_field ? 1 : 0));
7568 putFile32BitBE(file, level->can_move_into_acid_bits);
7569 putFile8Bit(file, level->dont_collide_with_bits);
7571 putFile8Bit(file, (level->use_spring_bug ? 1 : 0));
7572 putFile8Bit(file, (level->use_step_counter ? 1 : 0));
7574 putFile8Bit(file, (level->instant_relocation ? 1 : 0));
7575 putFile8Bit(file, (level->can_pass_to_walkable ? 1 : 0));
7576 putFile8Bit(file, (level->grow_into_diggable ? 1 : 0));
7578 putFile8Bit(file, level->game_engine_type);
7580 WriteUnusedBytesToFile(file, LEVEL_CHUNK_HEAD_UNUSED);
7584 static int SaveLevel_NAME(FILE *file, struct LevelInfo *level)
7589 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
7590 chunk_size += putFile8Bit(file, level->name[i]);
7595 static int SaveLevel_AUTH(FILE *file, struct LevelInfo *level)
7600 for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
7601 chunk_size += putFile8Bit(file, level->author[i]);
7606 #if ENABLE_HISTORIC_CHUNKS
7607 static int SaveLevel_BODY(FILE *file, struct LevelInfo *level)
7612 for (y = 0; y < level->fieldy; y++)
7613 for (x = 0; x < level->fieldx; x++)
7614 if (level->encoding_16bit_field)
7615 chunk_size += putFile16BitBE(file, level->field[x][y]);
7617 chunk_size += putFile8Bit(file, level->field[x][y]);
7623 static int SaveLevel_BODY(FILE *file, struct LevelInfo *level)
7628 for (y = 0; y < level->fieldy; y++)
7629 for (x = 0; x < level->fieldx; x++)
7630 chunk_size += putFile16BitBE(file, level->field[x][y]);
7635 #if ENABLE_HISTORIC_CHUNKS
7636 static void SaveLevel_CONT(FILE *file, struct LevelInfo *level)
7640 putFile8Bit(file, EL_YAMYAM);
7641 putFile8Bit(file, level->num_yamyam_contents);
7642 putFile8Bit(file, 0);
7643 putFile8Bit(file, 0);
7645 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
7646 for (y = 0; y < 3; y++)
7647 for (x = 0; x < 3; x++)
7648 if (level->encoding_16bit_field)
7649 putFile16BitBE(file, level->yamyam_content[i].e[x][y]);
7651 putFile8Bit(file, level->yamyam_content[i].e[x][y]);
7655 #if ENABLE_HISTORIC_CHUNKS
7656 static void SaveLevel_CNT2(FILE *file, struct LevelInfo *level, int element)
7659 int num_contents, content_xsize, content_ysize;
7660 int content_array[MAX_ELEMENT_CONTENTS][3][3];
7662 if (element == EL_YAMYAM)
7664 num_contents = level->num_yamyam_contents;
7668 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
7669 for (y = 0; y < 3; y++)
7670 for (x = 0; x < 3; x++)
7671 content_array[i][x][y] = level->yamyam_content[i].e[x][y];
7673 else if (element == EL_BD_AMOEBA)
7679 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
7680 for (y = 0; y < 3; y++)
7681 for (x = 0; x < 3; x++)
7682 content_array[i][x][y] = EL_EMPTY;
7683 content_array[0][0][0] = level->amoeba_content;
7687 // chunk header already written -- write empty chunk data
7688 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_SIZE);
7690 Warn("cannot save content for element '%d'", element);
7695 putFile16BitBE(file, element);
7696 putFile8Bit(file, num_contents);
7697 putFile8Bit(file, content_xsize);
7698 putFile8Bit(file, content_ysize);
7700 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_UNUSED);
7702 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
7703 for (y = 0; y < 3; y++)
7704 for (x = 0; x < 3; x++)
7705 putFile16BitBE(file, content_array[i][x][y]);
7709 #if ENABLE_HISTORIC_CHUNKS
7710 static int SaveLevel_CNT3(FILE *file, struct LevelInfo *level, int element)
7712 int envelope_nr = element - EL_ENVELOPE_1;
7713 int envelope_len = strlen(level->envelope_text[envelope_nr]) + 1;
7717 chunk_size += putFile16BitBE(file, element);
7718 chunk_size += putFile16BitBE(file, envelope_len);
7719 chunk_size += putFile8Bit(file, level->envelope_xsize[envelope_nr]);
7720 chunk_size += putFile8Bit(file, level->envelope_ysize[envelope_nr]);
7722 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT3_UNUSED);
7723 chunk_size += LEVEL_CHUNK_CNT3_UNUSED;
7725 for (i = 0; i < envelope_len; i++)
7726 chunk_size += putFile8Bit(file, level->envelope_text[envelope_nr][i]);
7732 #if ENABLE_HISTORIC_CHUNKS
7733 static void SaveLevel_CUS1(FILE *file, struct LevelInfo *level,
7734 int num_changed_custom_elements)
7738 putFile16BitBE(file, num_changed_custom_elements);
7740 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7742 int element = EL_CUSTOM_START + i;
7744 struct ElementInfo *ei = &element_info[element];
7746 if (ei->properties[EP_BITFIELD_BASE_NR] != EP_BITMASK_DEFAULT)
7748 if (check < num_changed_custom_elements)
7750 putFile16BitBE(file, element);
7751 putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
7758 if (check != num_changed_custom_elements) // should not happen
7759 Warn("inconsistent number of custom element properties");
7763 #if ENABLE_HISTORIC_CHUNKS
7764 static void SaveLevel_CUS2(FILE *file, struct LevelInfo *level,
7765 int num_changed_custom_elements)
7769 putFile16BitBE(file, num_changed_custom_elements);
7771 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7773 int element = EL_CUSTOM_START + i;
7775 if (element_info[element].change->target_element != EL_EMPTY_SPACE)
7777 if (check < num_changed_custom_elements)
7779 putFile16BitBE(file, element);
7780 putFile16BitBE(file, element_info[element].change->target_element);
7787 if (check != num_changed_custom_elements) // should not happen
7788 Warn("inconsistent number of custom target elements");
7792 #if ENABLE_HISTORIC_CHUNKS
7793 static void SaveLevel_CUS3(FILE *file, struct LevelInfo *level,
7794 int num_changed_custom_elements)
7796 int i, j, x, y, check = 0;
7798 putFile16BitBE(file, num_changed_custom_elements);
7800 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7802 int element = EL_CUSTOM_START + i;
7803 struct ElementInfo *ei = &element_info[element];
7805 if (ei->modified_settings)
7807 if (check < num_changed_custom_elements)
7809 putFile16BitBE(file, element);
7811 for (j = 0; j < MAX_ELEMENT_NAME_LEN; j++)
7812 putFile8Bit(file, ei->description[j]);
7814 putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
7816 // some free bytes for future properties and padding
7817 WriteUnusedBytesToFile(file, 7);
7819 putFile8Bit(file, ei->use_gfx_element);
7820 putFile16BitBE(file, ei->gfx_element_initial);
7822 putFile8Bit(file, ei->collect_score_initial);
7823 putFile8Bit(file, ei->collect_count_initial);
7825 putFile16BitBE(file, ei->push_delay_fixed);
7826 putFile16BitBE(file, ei->push_delay_random);
7827 putFile16BitBE(file, ei->move_delay_fixed);
7828 putFile16BitBE(file, ei->move_delay_random);
7830 putFile16BitBE(file, ei->move_pattern);
7831 putFile8Bit(file, ei->move_direction_initial);
7832 putFile8Bit(file, ei->move_stepsize);
7834 for (y = 0; y < 3; y++)
7835 for (x = 0; x < 3; x++)
7836 putFile16BitBE(file, ei->content.e[x][y]);
7838 putFile32BitBE(file, ei->change->events);
7840 putFile16BitBE(file, ei->change->target_element);
7842 putFile16BitBE(file, ei->change->delay_fixed);
7843 putFile16BitBE(file, ei->change->delay_random);
7844 putFile16BitBE(file, ei->change->delay_frames);
7846 putFile16BitBE(file, ei->change->initial_trigger_element);
7848 putFile8Bit(file, ei->change->explode);
7849 putFile8Bit(file, ei->change->use_target_content);
7850 putFile8Bit(file, ei->change->only_if_complete);
7851 putFile8Bit(file, ei->change->use_random_replace);
7853 putFile8Bit(file, ei->change->random_percentage);
7854 putFile8Bit(file, ei->change->replace_when);
7856 for (y = 0; y < 3; y++)
7857 for (x = 0; x < 3; x++)
7858 putFile16BitBE(file, ei->change->content.e[x][y]);
7860 putFile8Bit(file, ei->slippery_type);
7862 // some free bytes for future properties and padding
7863 WriteUnusedBytesToFile(file, LEVEL_CPART_CUS3_UNUSED);
7870 if (check != num_changed_custom_elements) // should not happen
7871 Warn("inconsistent number of custom element properties");
7875 #if ENABLE_HISTORIC_CHUNKS
7876 static void SaveLevel_CUS4(FILE *file, struct LevelInfo *level, int element)
7878 struct ElementInfo *ei = &element_info[element];
7881 // ---------- custom element base property values (96 bytes) ----------------
7883 putFile16BitBE(file, element);
7885 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
7886 putFile8Bit(file, ei->description[i]);
7888 putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
7890 WriteUnusedBytesToFile(file, 4); // reserved for more base properties
7892 putFile8Bit(file, ei->num_change_pages);
7894 putFile16BitBE(file, ei->ce_value_fixed_initial);
7895 putFile16BitBE(file, ei->ce_value_random_initial);
7896 putFile8Bit(file, ei->use_last_ce_value);
7898 putFile8Bit(file, ei->use_gfx_element);
7899 putFile16BitBE(file, ei->gfx_element_initial);
7901 putFile8Bit(file, ei->collect_score_initial);
7902 putFile8Bit(file, ei->collect_count_initial);
7904 putFile8Bit(file, ei->drop_delay_fixed);
7905 putFile8Bit(file, ei->push_delay_fixed);
7906 putFile8Bit(file, ei->drop_delay_random);
7907 putFile8Bit(file, ei->push_delay_random);
7908 putFile16BitBE(file, ei->move_delay_fixed);
7909 putFile16BitBE(file, ei->move_delay_random);
7911 // bits 0 - 15 of "move_pattern" ...
7912 putFile16BitBE(file, ei->move_pattern & 0xffff);
7913 putFile8Bit(file, ei->move_direction_initial);
7914 putFile8Bit(file, ei->move_stepsize);
7916 putFile8Bit(file, ei->slippery_type);
7918 for (y = 0; y < 3; y++)
7919 for (x = 0; x < 3; x++)
7920 putFile16BitBE(file, ei->content.e[x][y]);
7922 putFile16BitBE(file, ei->move_enter_element);
7923 putFile16BitBE(file, ei->move_leave_element);
7924 putFile8Bit(file, ei->move_leave_type);
7926 // ... bits 16 - 31 of "move_pattern" (not nice, but downward compatible)
7927 putFile16BitBE(file, (ei->move_pattern >> 16) & 0xffff);
7929 putFile8Bit(file, ei->access_direction);
7931 putFile8Bit(file, ei->explosion_delay);
7932 putFile8Bit(file, ei->ignition_delay);
7933 putFile8Bit(file, ei->explosion_type);
7935 // some free bytes for future custom property values and padding
7936 WriteUnusedBytesToFile(file, 1);
7938 // ---------- change page property values (48 bytes) ------------------------
7940 for (i = 0; i < ei->num_change_pages; i++)
7942 struct ElementChangeInfo *change = &ei->change_page[i];
7943 unsigned int event_bits;
7945 // bits 0 - 31 of "has_event[]" ...
7947 for (j = 0; j < MIN(NUM_CHANGE_EVENTS, 32); j++)
7948 if (change->has_event[j])
7949 event_bits |= (1u << j);
7950 putFile32BitBE(file, event_bits);
7952 putFile16BitBE(file, change->target_element);
7954 putFile16BitBE(file, change->delay_fixed);
7955 putFile16BitBE(file, change->delay_random);
7956 putFile16BitBE(file, change->delay_frames);
7958 putFile16BitBE(file, change->initial_trigger_element);
7960 putFile8Bit(file, change->explode);
7961 putFile8Bit(file, change->use_target_content);
7962 putFile8Bit(file, change->only_if_complete);
7963 putFile8Bit(file, change->use_random_replace);
7965 putFile8Bit(file, change->random_percentage);
7966 putFile8Bit(file, change->replace_when);
7968 for (y = 0; y < 3; y++)
7969 for (x = 0; x < 3; x++)
7970 putFile16BitBE(file, change->target_content.e[x][y]);
7972 putFile8Bit(file, change->can_change);
7974 putFile8Bit(file, change->trigger_side);
7976 putFile8Bit(file, change->trigger_player);
7977 putFile8Bit(file, (change->trigger_page == CH_PAGE_ANY ? CH_PAGE_ANY_FILE :
7978 log_2(change->trigger_page)));
7980 putFile8Bit(file, change->has_action);
7981 putFile8Bit(file, change->action_type);
7982 putFile8Bit(file, change->action_mode);
7983 putFile16BitBE(file, change->action_arg);
7985 // ... bits 32 - 39 of "has_event[]" (not nice, but downward compatible)
7987 for (j = 32; j < NUM_CHANGE_EVENTS; j++)
7988 if (change->has_event[j])
7989 event_bits |= (1u << (j - 32));
7990 putFile8Bit(file, event_bits);
7995 #if ENABLE_HISTORIC_CHUNKS
7996 static void SaveLevel_GRP1(FILE *file, struct LevelInfo *level, int element)
7998 struct ElementInfo *ei = &element_info[element];
7999 struct ElementGroupInfo *group = ei->group;
8002 putFile16BitBE(file, element);
8004 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
8005 putFile8Bit(file, ei->description[i]);
8007 putFile8Bit(file, group->num_elements);
8009 putFile8Bit(file, ei->use_gfx_element);
8010 putFile16BitBE(file, ei->gfx_element_initial);
8012 putFile8Bit(file, group->choice_mode);
8014 // some free bytes for future values and padding
8015 WriteUnusedBytesToFile(file, 3);
8017 for (i = 0; i < MAX_ELEMENTS_IN_GROUP; i++)
8018 putFile16BitBE(file, group->element[i]);
8022 static int SaveLevel_MicroChunk(FILE *file, struct LevelFileConfigInfo *entry,
8023 boolean write_element)
8025 int save_type = entry->save_type;
8026 int data_type = entry->data_type;
8027 int conf_type = entry->conf_type;
8028 int byte_mask = conf_type & CONF_MASK_BYTES;
8029 int element = entry->element;
8030 int default_value = entry->default_value;
8032 boolean modified = FALSE;
8034 if (byte_mask != CONF_MASK_MULTI_BYTES)
8036 void *value_ptr = entry->value;
8037 int value = (data_type == TYPE_BOOLEAN ? *(boolean *)value_ptr :
8040 // check if any settings have been modified before saving them
8041 if (value != default_value)
8044 // do not save if explicitly told or if unmodified default settings
8045 if ((save_type == SAVE_CONF_NEVER) ||
8046 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
8050 num_bytes += putFile16BitBE(file, element);
8052 num_bytes += putFile8Bit(file, conf_type);
8053 num_bytes += (byte_mask == CONF_MASK_1_BYTE ? putFile8Bit (file, value) :
8054 byte_mask == CONF_MASK_2_BYTE ? putFile16BitBE(file, value) :
8055 byte_mask == CONF_MASK_4_BYTE ? putFile32BitBE(file, value) :
8058 else if (data_type == TYPE_STRING)
8060 char *default_string = entry->default_string;
8061 char *string = (char *)(entry->value);
8062 int string_length = strlen(string);
8065 // check if any settings have been modified before saving them
8066 if (!strEqual(string, default_string))
8069 // do not save if explicitly told or if unmodified default settings
8070 if ((save_type == SAVE_CONF_NEVER) ||
8071 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
8075 num_bytes += putFile16BitBE(file, element);
8077 num_bytes += putFile8Bit(file, conf_type);
8078 num_bytes += putFile16BitBE(file, string_length);
8080 for (i = 0; i < string_length; i++)
8081 num_bytes += putFile8Bit(file, string[i]);
8083 else if (data_type == TYPE_ELEMENT_LIST)
8085 int *element_array = (int *)(entry->value);
8086 int num_elements = *(int *)(entry->num_entities);
8089 // check if any settings have been modified before saving them
8090 for (i = 0; i < num_elements; i++)
8091 if (element_array[i] != default_value)
8094 // do not save if explicitly told or if unmodified default settings
8095 if ((save_type == SAVE_CONF_NEVER) ||
8096 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
8100 num_bytes += putFile16BitBE(file, element);
8102 num_bytes += putFile8Bit(file, conf_type);
8103 num_bytes += putFile16BitBE(file, num_elements * CONF_ELEMENT_NUM_BYTES);
8105 for (i = 0; i < num_elements; i++)
8106 num_bytes += putFile16BitBE(file, element_array[i]);
8108 else if (data_type == TYPE_CONTENT_LIST)
8110 struct Content *content = (struct Content *)(entry->value);
8111 int num_contents = *(int *)(entry->num_entities);
8114 // check if any settings have been modified before saving them
8115 for (i = 0; i < num_contents; i++)
8116 for (y = 0; y < 3; y++)
8117 for (x = 0; x < 3; x++)
8118 if (content[i].e[x][y] != default_value)
8121 // do not save if explicitly told or if unmodified default settings
8122 if ((save_type == SAVE_CONF_NEVER) ||
8123 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
8127 num_bytes += putFile16BitBE(file, element);
8129 num_bytes += putFile8Bit(file, conf_type);
8130 num_bytes += putFile16BitBE(file, num_contents * CONF_CONTENT_NUM_BYTES);
8132 for (i = 0; i < num_contents; i++)
8133 for (y = 0; y < 3; y++)
8134 for (x = 0; x < 3; x++)
8135 num_bytes += putFile16BitBE(file, content[i].e[x][y]);
8141 static int SaveLevel_INFO(FILE *file, struct LevelInfo *level)
8146 li = *level; // copy level data into temporary buffer
8148 for (i = 0; chunk_config_INFO[i].data_type != -1; i++)
8149 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_INFO[i], FALSE);
8154 static int SaveLevel_ELEM(FILE *file, struct LevelInfo *level)
8159 li = *level; // copy level data into temporary buffer
8161 for (i = 0; chunk_config_ELEM[i].data_type != -1; i++)
8162 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_ELEM[i], TRUE);
8167 static int SaveLevel_NOTE(FILE *file, struct LevelInfo *level, int element)
8169 int envelope_nr = element - EL_ENVELOPE_1;
8173 chunk_size += putFile16BitBE(file, element);
8175 // copy envelope data into temporary buffer
8176 xx_envelope = level->envelope[envelope_nr];
8178 for (i = 0; chunk_config_NOTE[i].data_type != -1; i++)
8179 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_NOTE[i], FALSE);
8184 static int SaveLevel_CUSX(FILE *file, struct LevelInfo *level, int element)
8186 struct ElementInfo *ei = &element_info[element];
8190 chunk_size += putFile16BitBE(file, element);
8192 xx_ei = *ei; // copy element data into temporary buffer
8194 // set default description string for this specific element
8195 strcpy(xx_default_description, getDefaultElementDescription(ei));
8197 for (i = 0; chunk_config_CUSX_base[i].data_type != -1; i++)
8198 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_CUSX_base[i], FALSE);
8200 for (i = 0; i < ei->num_change_pages; i++)
8202 struct ElementChangeInfo *change = &ei->change_page[i];
8204 xx_current_change_page = i;
8206 xx_change = *change; // copy change data into temporary buffer
8209 setEventBitsFromEventFlags(change);
8211 for (j = 0; chunk_config_CUSX_change[j].data_type != -1; j++)
8212 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_CUSX_change[j],
8219 static int SaveLevel_GRPX(FILE *file, struct LevelInfo *level, int element)
8221 struct ElementInfo *ei = &element_info[element];
8222 struct ElementGroupInfo *group = ei->group;
8226 chunk_size += putFile16BitBE(file, element);
8228 xx_ei = *ei; // copy element data into temporary buffer
8229 xx_group = *group; // copy group data into temporary buffer
8231 // set default description string for this specific element
8232 strcpy(xx_default_description, getDefaultElementDescription(ei));
8234 for (i = 0; chunk_config_GRPX[i].data_type != -1; i++)
8235 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_GRPX[i], FALSE);
8240 static int SaveLevel_EMPX(FILE *file, struct LevelInfo *level, int element)
8242 struct ElementInfo *ei = &element_info[element];
8246 chunk_size += putFile16BitBE(file, element);
8248 xx_ei = *ei; // copy element data into temporary buffer
8250 for (i = 0; chunk_config_EMPX[i].data_type != -1; i++)
8251 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_EMPX[i], FALSE);
8256 static void SaveLevelFromFilename(struct LevelInfo *level, char *filename,
8257 boolean save_as_template)
8263 if (!(file = fopen(filename, MODE_WRITE)))
8265 Warn("cannot save level file '%s'", filename);
8270 level->file_version = FILE_VERSION_ACTUAL;
8271 level->game_version = GAME_VERSION_ACTUAL;
8273 level->creation_date = getCurrentDate();
8275 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
8276 putFileChunkBE(file, "CAVE", CHUNK_SIZE_NONE);
8278 chunk_size = SaveLevel_VERS(NULL, level);
8279 putFileChunkBE(file, "VERS", chunk_size);
8280 SaveLevel_VERS(file, level);
8282 chunk_size = SaveLevel_DATE(NULL, level);
8283 putFileChunkBE(file, "DATE", chunk_size);
8284 SaveLevel_DATE(file, level);
8286 chunk_size = SaveLevel_NAME(NULL, level);
8287 putFileChunkBE(file, "NAME", chunk_size);
8288 SaveLevel_NAME(file, level);
8290 chunk_size = SaveLevel_AUTH(NULL, level);
8291 putFileChunkBE(file, "AUTH", chunk_size);
8292 SaveLevel_AUTH(file, level);
8294 chunk_size = SaveLevel_INFO(NULL, level);
8295 putFileChunkBE(file, "INFO", chunk_size);
8296 SaveLevel_INFO(file, level);
8298 chunk_size = SaveLevel_BODY(NULL, level);
8299 putFileChunkBE(file, "BODY", chunk_size);
8300 SaveLevel_BODY(file, level);
8302 chunk_size = SaveLevel_ELEM(NULL, level);
8303 if (chunk_size > LEVEL_CHUNK_ELEM_UNCHANGED) // save if changed
8305 putFileChunkBE(file, "ELEM", chunk_size);
8306 SaveLevel_ELEM(file, level);
8309 for (i = 0; i < NUM_ENVELOPES; i++)
8311 int element = EL_ENVELOPE_1 + i;
8313 chunk_size = SaveLevel_NOTE(NULL, level, element);
8314 if (chunk_size > LEVEL_CHUNK_NOTE_UNCHANGED) // save if changed
8316 putFileChunkBE(file, "NOTE", chunk_size);
8317 SaveLevel_NOTE(file, level, element);
8321 // if not using template level, check for non-default custom/group elements
8322 if (!level->use_custom_template || save_as_template)
8324 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
8326 int element = EL_CUSTOM_START + i;
8328 chunk_size = SaveLevel_CUSX(NULL, level, element);
8329 if (chunk_size > LEVEL_CHUNK_CUSX_UNCHANGED) // save if changed
8331 putFileChunkBE(file, "CUSX", chunk_size);
8332 SaveLevel_CUSX(file, level, element);
8336 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
8338 int element = EL_GROUP_START + i;
8340 chunk_size = SaveLevel_GRPX(NULL, level, element);
8341 if (chunk_size > LEVEL_CHUNK_GRPX_UNCHANGED) // save if changed
8343 putFileChunkBE(file, "GRPX", chunk_size);
8344 SaveLevel_GRPX(file, level, element);
8348 for (i = 0; i < NUM_EMPTY_ELEMENTS_ALL; i++)
8350 int element = GET_EMPTY_ELEMENT(i);
8352 chunk_size = SaveLevel_EMPX(NULL, level, element);
8353 if (chunk_size > LEVEL_CHUNK_EMPX_UNCHANGED) // save if changed
8355 putFileChunkBE(file, "EMPX", chunk_size);
8356 SaveLevel_EMPX(file, level, element);
8363 SetFilePermissions(filename, PERMS_PRIVATE);
8366 void SaveLevel(int nr)
8368 char *filename = getDefaultLevelFilename(nr);
8370 SaveLevelFromFilename(&level, filename, FALSE);
8373 void SaveLevelTemplate(void)
8375 char *filename = getLocalLevelTemplateFilename();
8377 SaveLevelFromFilename(&level, filename, TRUE);
8380 boolean SaveLevelChecked(int nr)
8382 char *filename = getDefaultLevelFilename(nr);
8383 boolean new_level = !fileExists(filename);
8384 boolean level_saved = FALSE;
8386 if (new_level || Request("Save this level and kill the old?", REQ_ASK))
8391 Request("Level saved!", REQ_CONFIRM);
8399 void DumpLevel(struct LevelInfo *level)
8401 if (level->no_level_file || level->no_valid_file)
8403 Warn("cannot dump -- no valid level file found");
8409 Print("Level xxx (file version %08d, game version %08d)\n",
8410 level->file_version, level->game_version);
8413 Print("Level author: '%s'\n", level->author);
8414 Print("Level title: '%s'\n", level->name);
8416 Print("Playfield size: %d x %d\n", level->fieldx, level->fieldy);
8418 Print("Level time: %d seconds\n", level->time);
8419 Print("Gems needed: %d\n", level->gems_needed);
8421 Print("Time for magic wall: %d seconds\n", level->time_magic_wall);
8422 Print("Time for wheel: %d seconds\n", level->time_wheel);
8423 Print("Time for light: %d seconds\n", level->time_light);
8424 Print("Time for timegate: %d seconds\n", level->time_timegate);
8426 Print("Amoeba speed: %d\n", level->amoeba_speed);
8429 Print("EM style slippery gems: %s\n", (level->em_slippery_gems ? "yes" : "no"));
8430 Print("Player blocks last field: %s\n", (level->block_last_field ? "yes" : "no"));
8431 Print("SP player blocks last field: %s\n", (level->sp_block_last_field ? "yes" : "no"));
8432 Print("use spring bug: %s\n", (level->use_spring_bug ? "yes" : "no"));
8433 Print("use step counter: %s\n", (level->use_step_counter ? "yes" : "no"));
8434 Print("rate time over score: %s\n", (level->rate_time_over_score ? "yes" : "no"));
8440 for (i = 0; i < NUM_ENVELOPES; i++)
8442 char *text = level->envelope[i].text;
8443 int text_len = strlen(text);
8444 boolean has_text = FALSE;
8446 for (j = 0; j < text_len; j++)
8447 if (text[j] != ' ' && text[j] != '\n')
8453 Print("Envelope %d:\n'%s'\n", i + 1, text);
8461 void DumpLevels(void)
8463 static LevelDirTree *dumplevel_leveldir = NULL;
8465 dumplevel_leveldir = getTreeInfoFromIdentifier(leveldir_first,
8466 global.dumplevel_leveldir);
8468 if (dumplevel_leveldir == NULL)
8469 Fail("no such level identifier: '%s'", global.dumplevel_leveldir);
8471 if (global.dumplevel_level_nr < dumplevel_leveldir->first_level ||
8472 global.dumplevel_level_nr > dumplevel_leveldir->last_level)
8473 Fail("no such level number: %d", global.dumplevel_level_nr);
8475 leveldir_current = dumplevel_leveldir;
8477 LoadLevel(global.dumplevel_level_nr);
8484 // ============================================================================
8485 // tape file functions
8486 // ============================================================================
8488 static void setTapeInfoToDefaults(void)
8492 // always start with reliable default values (empty tape)
8495 // default values (also for pre-1.2 tapes) with only the first player
8496 tape.player_participates[0] = TRUE;
8497 for (i = 1; i < MAX_PLAYERS; i++)
8498 tape.player_participates[i] = FALSE;
8500 // at least one (default: the first) player participates in every tape
8501 tape.num_participating_players = 1;
8503 tape.property_bits = TAPE_PROPERTY_NONE;
8505 tape.level_nr = level_nr;
8507 tape.changed = FALSE;
8508 tape.solved = FALSE;
8510 tape.recording = FALSE;
8511 tape.playing = FALSE;
8512 tape.pausing = FALSE;
8514 tape.scr_fieldx = SCR_FIELDX_DEFAULT;
8515 tape.scr_fieldy = SCR_FIELDY_DEFAULT;
8517 tape.no_info_chunk = TRUE;
8518 tape.no_valid_file = FALSE;
8521 static int getTapePosSize(struct TapeInfo *tape)
8523 int tape_pos_size = 0;
8525 if (tape->use_key_actions)
8526 tape_pos_size += tape->num_participating_players;
8528 if (tape->use_mouse_actions)
8529 tape_pos_size += 3; // x and y position and mouse button mask
8531 tape_pos_size += 1; // tape action delay value
8533 return tape_pos_size;
8536 static void setTapeActionFlags(struct TapeInfo *tape, int value)
8538 tape->use_key_actions = FALSE;
8539 tape->use_mouse_actions = FALSE;
8541 if (value != TAPE_USE_MOUSE_ACTIONS_ONLY)
8542 tape->use_key_actions = TRUE;
8544 if (value != TAPE_USE_KEY_ACTIONS_ONLY)
8545 tape->use_mouse_actions = TRUE;
8548 static int getTapeActionValue(struct TapeInfo *tape)
8550 return (tape->use_key_actions &&
8551 tape->use_mouse_actions ? TAPE_USE_KEY_AND_MOUSE_ACTIONS :
8552 tape->use_key_actions ? TAPE_USE_KEY_ACTIONS_ONLY :
8553 tape->use_mouse_actions ? TAPE_USE_MOUSE_ACTIONS_ONLY :
8554 TAPE_ACTIONS_DEFAULT);
8557 static int LoadTape_VERS(File *file, int chunk_size, struct TapeInfo *tape)
8559 tape->file_version = getFileVersion(file);
8560 tape->game_version = getFileVersion(file);
8565 static int LoadTape_HEAD(File *file, int chunk_size, struct TapeInfo *tape)
8569 tape->random_seed = getFile32BitBE(file);
8570 tape->date = getFile32BitBE(file);
8571 tape->length = getFile32BitBE(file);
8573 // read header fields that are new since version 1.2
8574 if (tape->file_version >= FILE_VERSION_1_2)
8576 byte store_participating_players = getFile8Bit(file);
8579 // since version 1.2, tapes store which players participate in the tape
8580 tape->num_participating_players = 0;
8581 for (i = 0; i < MAX_PLAYERS; i++)
8583 tape->player_participates[i] = FALSE;
8585 if (store_participating_players & (1 << i))
8587 tape->player_participates[i] = TRUE;
8588 tape->num_participating_players++;
8592 setTapeActionFlags(tape, getFile8Bit(file));
8594 tape->property_bits = getFile8Bit(file);
8595 tape->solved = getFile8Bit(file);
8597 engine_version = getFileVersion(file);
8598 if (engine_version > 0)
8599 tape->engine_version = engine_version;
8601 tape->engine_version = tape->game_version;
8607 static int LoadTape_SCRN(File *file, int chunk_size, struct TapeInfo *tape)
8609 tape->scr_fieldx = getFile8Bit(file);
8610 tape->scr_fieldy = getFile8Bit(file);
8615 static int LoadTape_INFO(File *file, int chunk_size, struct TapeInfo *tape)
8617 char *level_identifier = NULL;
8618 int level_identifier_size;
8621 tape->no_info_chunk = FALSE;
8623 level_identifier_size = getFile16BitBE(file);
8625 level_identifier = checked_malloc(level_identifier_size);
8627 for (i = 0; i < level_identifier_size; i++)
8628 level_identifier[i] = getFile8Bit(file);
8630 strncpy(tape->level_identifier, level_identifier, MAX_FILENAME_LEN);
8631 tape->level_identifier[MAX_FILENAME_LEN] = '\0';
8633 checked_free(level_identifier);
8635 tape->level_nr = getFile16BitBE(file);
8637 chunk_size = 2 + level_identifier_size + 2;
8642 static int LoadTape_BODY(File *file, int chunk_size, struct TapeInfo *tape)
8645 int tape_pos_size = getTapePosSize(tape);
8646 int chunk_size_expected = tape_pos_size * tape->length;
8648 if (chunk_size_expected != chunk_size)
8650 ReadUnusedBytesFromFile(file, chunk_size);
8651 return chunk_size_expected;
8654 for (i = 0; i < tape->length; i++)
8656 if (i >= MAX_TAPE_LEN)
8658 Warn("tape truncated -- size exceeds maximum tape size %d",
8661 // tape too large; read and ignore remaining tape data from this chunk
8662 for (;i < tape->length; i++)
8663 ReadUnusedBytesFromFile(file, tape_pos_size);
8668 if (tape->use_key_actions)
8670 for (j = 0; j < MAX_PLAYERS; j++)
8672 tape->pos[i].action[j] = MV_NONE;
8674 if (tape->player_participates[j])
8675 tape->pos[i].action[j] = getFile8Bit(file);
8679 if (tape->use_mouse_actions)
8681 tape->pos[i].action[TAPE_ACTION_LX] = getFile8Bit(file);
8682 tape->pos[i].action[TAPE_ACTION_LY] = getFile8Bit(file);
8683 tape->pos[i].action[TAPE_ACTION_BUTTON] = getFile8Bit(file);
8686 tape->pos[i].delay = getFile8Bit(file);
8688 if (tape->file_version == FILE_VERSION_1_0)
8690 // eliminate possible diagonal moves in old tapes
8691 // this is only for backward compatibility
8693 byte joy_dir[4] = { JOY_LEFT, JOY_RIGHT, JOY_UP, JOY_DOWN };
8694 byte action = tape->pos[i].action[0];
8695 int k, num_moves = 0;
8697 for (k = 0; k < 4; k++)
8699 if (action & joy_dir[k])
8701 tape->pos[i + num_moves].action[0] = joy_dir[k];
8703 tape->pos[i + num_moves].delay = 0;
8712 tape->length += num_moves;
8715 else if (tape->file_version < FILE_VERSION_2_0)
8717 // convert pre-2.0 tapes to new tape format
8719 if (tape->pos[i].delay > 1)
8722 tape->pos[i + 1] = tape->pos[i];
8723 tape->pos[i + 1].delay = 1;
8726 for (j = 0; j < MAX_PLAYERS; j++)
8727 tape->pos[i].action[j] = MV_NONE;
8728 tape->pos[i].delay--;
8735 if (checkEndOfFile(file))
8739 if (i != tape->length)
8740 chunk_size = tape_pos_size * i;
8745 static void LoadTape_SokobanSolution(char *filename)
8748 int move_delay = TILESIZE / level.initial_player_stepsize[0];
8750 if (!(file = openFile(filename, MODE_READ)))
8752 tape.no_valid_file = TRUE;
8757 while (!checkEndOfFile(file))
8759 unsigned char c = getByteFromFile(file);
8761 if (checkEndOfFile(file))
8768 tape.pos[tape.length].action[0] = MV_UP;
8769 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
8775 tape.pos[tape.length].action[0] = MV_DOWN;
8776 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
8782 tape.pos[tape.length].action[0] = MV_LEFT;
8783 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
8789 tape.pos[tape.length].action[0] = MV_RIGHT;
8790 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
8798 // ignore white-space characters
8802 tape.no_valid_file = TRUE;
8804 Warn("unsupported Sokoban solution file '%s' ['%d']", filename, c);
8812 if (tape.no_valid_file)
8815 tape.length_frames = GetTapeLengthFrames();
8816 tape.length_seconds = GetTapeLengthSeconds();
8819 void LoadTapeFromFilename(char *filename)
8821 char cookie[MAX_LINE_LEN];
8822 char chunk_name[CHUNK_ID_LEN + 1];
8826 // always start with reliable default values
8827 setTapeInfoToDefaults();
8829 if (strSuffix(filename, ".sln"))
8831 LoadTape_SokobanSolution(filename);
8836 if (!(file = openFile(filename, MODE_READ)))
8838 tape.no_valid_file = TRUE;
8843 getFileChunkBE(file, chunk_name, NULL);
8844 if (strEqual(chunk_name, "RND1"))
8846 getFile32BitBE(file); // not used
8848 getFileChunkBE(file, chunk_name, NULL);
8849 if (!strEqual(chunk_name, "TAPE"))
8851 tape.no_valid_file = TRUE;
8853 Warn("unknown format of tape file '%s'", filename);
8860 else // check for pre-2.0 file format with cookie string
8862 strcpy(cookie, chunk_name);
8863 if (getStringFromFile(file, &cookie[4], MAX_LINE_LEN - 4) == NULL)
8865 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
8866 cookie[strlen(cookie) - 1] = '\0';
8868 if (!checkCookieString(cookie, TAPE_COOKIE_TMPL))
8870 tape.no_valid_file = TRUE;
8872 Warn("unknown format of tape file '%s'", filename);
8879 if ((tape.file_version = getFileVersionFromCookieString(cookie)) == -1)
8881 tape.no_valid_file = TRUE;
8883 Warn("unsupported version of tape file '%s'", filename);
8890 // pre-2.0 tape files have no game version, so use file version here
8891 tape.game_version = tape.file_version;
8894 if (tape.file_version < FILE_VERSION_1_2)
8896 // tape files from versions before 1.2.0 without chunk structure
8897 LoadTape_HEAD(file, TAPE_CHUNK_HEAD_SIZE, &tape);
8898 LoadTape_BODY(file, 2 * tape.length, &tape);
8906 int (*loader)(File *, int, struct TapeInfo *);
8910 { "VERS", TAPE_CHUNK_VERS_SIZE, LoadTape_VERS },
8911 { "HEAD", TAPE_CHUNK_HEAD_SIZE, LoadTape_HEAD },
8912 { "SCRN", TAPE_CHUNK_SCRN_SIZE, LoadTape_SCRN },
8913 { "INFO", -1, LoadTape_INFO },
8914 { "BODY", -1, LoadTape_BODY },
8918 while (getFileChunkBE(file, chunk_name, &chunk_size))
8922 while (chunk_info[i].name != NULL &&
8923 !strEqual(chunk_name, chunk_info[i].name))
8926 if (chunk_info[i].name == NULL)
8928 Warn("unknown chunk '%s' in tape file '%s'",
8929 chunk_name, filename);
8931 ReadUnusedBytesFromFile(file, chunk_size);
8933 else if (chunk_info[i].size != -1 &&
8934 chunk_info[i].size != chunk_size)
8936 Warn("wrong size (%d) of chunk '%s' in tape file '%s'",
8937 chunk_size, chunk_name, filename);
8939 ReadUnusedBytesFromFile(file, chunk_size);
8943 // call function to load this tape chunk
8944 int chunk_size_expected =
8945 (chunk_info[i].loader)(file, chunk_size, &tape);
8947 // the size of some chunks cannot be checked before reading other
8948 // chunks first (like "HEAD" and "BODY") that contain some header
8949 // information, so check them here
8950 if (chunk_size_expected != chunk_size)
8952 Warn("wrong size (%d) of chunk '%s' in tape file '%s'",
8953 chunk_size, chunk_name, filename);
8961 tape.length_frames = GetTapeLengthFrames();
8962 tape.length_seconds = GetTapeLengthSeconds();
8965 Debug("files:LoadTapeFromFilename", "tape file version: %d",
8967 Debug("files:LoadTapeFromFilename", "tape game version: %d",
8969 Debug("files:LoadTapeFromFilename", "tape engine version: %d",
8970 tape.engine_version);
8974 void LoadTape(int nr)
8976 char *filename = getTapeFilename(nr);
8978 LoadTapeFromFilename(filename);
8981 void LoadSolutionTape(int nr)
8983 char *filename = getSolutionTapeFilename(nr);
8985 LoadTapeFromFilename(filename);
8987 if (TAPE_IS_EMPTY(tape))
8989 if (level.game_engine_type == GAME_ENGINE_TYPE_BD &&
8990 level.native_bd_level->replay != NULL)
8991 CopyNativeTape_BD_to_RND(&level);
8992 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP &&
8993 level.native_sp_level->demo.is_available)
8994 CopyNativeTape_SP_to_RND(&level);
8998 void LoadScoreTape(char *score_tape_basename, int nr)
9000 char *filename = getScoreTapeFilename(score_tape_basename, nr);
9002 LoadTapeFromFilename(filename);
9005 void LoadScoreCacheTape(char *score_tape_basename, int nr)
9007 char *filename = getScoreCacheTapeFilename(score_tape_basename, nr);
9009 LoadTapeFromFilename(filename);
9012 static boolean checkSaveTape_SCRN(struct TapeInfo *tape)
9014 // chunk required for team mode tapes with non-default screen size
9015 return (tape->num_participating_players > 1 &&
9016 (tape->scr_fieldx != SCR_FIELDX_DEFAULT ||
9017 tape->scr_fieldy != SCR_FIELDY_DEFAULT));
9020 static void SaveTape_VERS(FILE *file, struct TapeInfo *tape)
9022 putFileVersion(file, tape->file_version);
9023 putFileVersion(file, tape->game_version);
9026 static void SaveTape_HEAD(FILE *file, struct TapeInfo *tape)
9029 byte store_participating_players = 0;
9031 // set bits for participating players for compact storage
9032 for (i = 0; i < MAX_PLAYERS; i++)
9033 if (tape->player_participates[i])
9034 store_participating_players |= (1 << i);
9036 putFile32BitBE(file, tape->random_seed);
9037 putFile32BitBE(file, tape->date);
9038 putFile32BitBE(file, tape->length);
9040 putFile8Bit(file, store_participating_players);
9042 putFile8Bit(file, getTapeActionValue(tape));
9044 putFile8Bit(file, tape->property_bits);
9045 putFile8Bit(file, tape->solved);
9047 putFileVersion(file, tape->engine_version);
9050 static void SaveTape_SCRN(FILE *file, struct TapeInfo *tape)
9052 putFile8Bit(file, tape->scr_fieldx);
9053 putFile8Bit(file, tape->scr_fieldy);
9056 static void SaveTape_INFO(FILE *file, struct TapeInfo *tape)
9058 int level_identifier_size = strlen(tape->level_identifier) + 1;
9061 putFile16BitBE(file, level_identifier_size);
9063 for (i = 0; i < level_identifier_size; i++)
9064 putFile8Bit(file, tape->level_identifier[i]);
9066 putFile16BitBE(file, tape->level_nr);
9069 static void SaveTape_BODY(FILE *file, struct TapeInfo *tape)
9073 for (i = 0; i < tape->length; i++)
9075 if (tape->use_key_actions)
9077 for (j = 0; j < MAX_PLAYERS; j++)
9078 if (tape->player_participates[j])
9079 putFile8Bit(file, tape->pos[i].action[j]);
9082 if (tape->use_mouse_actions)
9084 putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_LX]);
9085 putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_LY]);
9086 putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_BUTTON]);
9089 putFile8Bit(file, tape->pos[i].delay);
9093 void SaveTapeToFilename(char *filename)
9097 int info_chunk_size;
9098 int body_chunk_size;
9100 if (!(file = fopen(filename, MODE_WRITE)))
9102 Warn("cannot save level recording file '%s'", filename);
9107 tape_pos_size = getTapePosSize(&tape);
9109 info_chunk_size = 2 + (strlen(tape.level_identifier) + 1) + 2;
9110 body_chunk_size = tape_pos_size * tape.length;
9112 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
9113 putFileChunkBE(file, "TAPE", CHUNK_SIZE_NONE);
9115 putFileChunkBE(file, "VERS", TAPE_CHUNK_VERS_SIZE);
9116 SaveTape_VERS(file, &tape);
9118 putFileChunkBE(file, "HEAD", TAPE_CHUNK_HEAD_SIZE);
9119 SaveTape_HEAD(file, &tape);
9121 if (checkSaveTape_SCRN(&tape))
9123 putFileChunkBE(file, "SCRN", TAPE_CHUNK_SCRN_SIZE);
9124 SaveTape_SCRN(file, &tape);
9127 putFileChunkBE(file, "INFO", info_chunk_size);
9128 SaveTape_INFO(file, &tape);
9130 putFileChunkBE(file, "BODY", body_chunk_size);
9131 SaveTape_BODY(file, &tape);
9135 SetFilePermissions(filename, PERMS_PRIVATE);
9138 static void SaveTapeExt(char *filename)
9142 tape.file_version = FILE_VERSION_ACTUAL;
9143 tape.game_version = GAME_VERSION_ACTUAL;
9145 tape.num_participating_players = 0;
9147 // count number of participating players
9148 for (i = 0; i < MAX_PLAYERS; i++)
9149 if (tape.player_participates[i])
9150 tape.num_participating_players++;
9152 SaveTapeToFilename(filename);
9154 tape.changed = FALSE;
9157 void SaveTape(int nr)
9159 char *filename = getTapeFilename(nr);
9161 InitTapeDirectory(leveldir_current->subdir);
9163 SaveTapeExt(filename);
9166 void SaveScoreTape(int nr)
9168 char *filename = getScoreTapeFilename(tape.score_tape_basename, nr);
9170 // used instead of "leveldir_current->subdir" (for network games)
9171 InitScoreTapeDirectory(levelset.identifier, nr);
9173 SaveTapeExt(filename);
9176 static boolean SaveTapeCheckedExt(int nr, char *msg_replace, char *msg_saved,
9177 unsigned int req_state_added)
9179 char *filename = getTapeFilename(nr);
9180 boolean new_tape = !fileExists(filename);
9181 boolean tape_saved = FALSE;
9183 if (new_tape || Request(msg_replace, REQ_ASK | req_state_added))
9188 Request(msg_saved, REQ_CONFIRM | req_state_added);
9196 boolean SaveTapeChecked(int nr)
9198 return SaveTapeCheckedExt(nr, "Replace old tape?", "Tape saved!", 0);
9201 boolean SaveTapeChecked_LevelSolved(int nr)
9203 return SaveTapeCheckedExt(nr, "Level solved! Replace old tape?",
9204 "Level solved! Tape saved!", REQ_STAY_OPEN);
9207 void DumpTape(struct TapeInfo *tape)
9209 int tape_frame_counter;
9212 if (tape->no_valid_file)
9214 Warn("cannot dump -- no valid tape file found");
9221 Print("Tape of Level %03d (file version %08d, game version %08d)\n",
9222 tape->level_nr, tape->file_version, tape->game_version);
9223 Print(" (effective engine version %08d)\n",
9224 tape->engine_version);
9225 Print("Level series identifier: '%s'\n", tape->level_identifier);
9227 Print("Solution tape: %s\n",
9228 tape->solved ? "yes" :
9229 tape->game_version < VERSION_IDENT(4,3,2,3) ? "unknown" : "no");
9231 Print("Special tape properties: ");
9232 if (tape->property_bits == TAPE_PROPERTY_NONE)
9234 if (tape->property_bits & TAPE_PROPERTY_EM_RANDOM_BUG)
9235 Print("[em_random_bug]");
9236 if (tape->property_bits & TAPE_PROPERTY_GAME_SPEED)
9237 Print("[game_speed]");
9238 if (tape->property_bits & TAPE_PROPERTY_PAUSE_MODE)
9240 if (tape->property_bits & TAPE_PROPERTY_SINGLE_STEP)
9241 Print("[single_step]");
9242 if (tape->property_bits & TAPE_PROPERTY_SNAPSHOT)
9243 Print("[snapshot]");
9244 if (tape->property_bits & TAPE_PROPERTY_REPLAYED)
9245 Print("[replayed]");
9246 if (tape->property_bits & TAPE_PROPERTY_TAS_KEYS)
9247 Print("[tas_keys]");
9248 if (tape->property_bits & TAPE_PROPERTY_SMALL_GRAPHICS)
9249 Print("[small_graphics]");
9252 int year2 = tape->date / 10000;
9253 int year4 = (year2 < 70 ? 2000 + year2 : 1900 + year2);
9254 int month_index_raw = (tape->date / 100) % 100;
9255 int month_index = month_index_raw % 12; // prevent invalid index
9256 int month = month_index + 1;
9257 int day = tape->date % 100;
9259 Print("Tape date: %04d-%02d-%02d\n", year4, month, day);
9263 tape_frame_counter = 0;
9265 for (i = 0; i < tape->length; i++)
9267 if (i >= MAX_TAPE_LEN)
9272 for (j = 0; j < MAX_PLAYERS; j++)
9274 if (tape->player_participates[j])
9276 int action = tape->pos[i].action[j];
9278 Print("%d:%02x ", j, action);
9279 Print("[%c%c%c%c|%c%c] - ",
9280 (action & JOY_LEFT ? '<' : ' '),
9281 (action & JOY_RIGHT ? '>' : ' '),
9282 (action & JOY_UP ? '^' : ' '),
9283 (action & JOY_DOWN ? 'v' : ' '),
9284 (action & JOY_BUTTON_1 ? '1' : ' '),
9285 (action & JOY_BUTTON_2 ? '2' : ' '));
9289 Print("(%03d) ", tape->pos[i].delay);
9290 Print("[%05d]\n", tape_frame_counter);
9292 tape_frame_counter += tape->pos[i].delay;
9298 void DumpTapes(void)
9300 static LevelDirTree *dumptape_leveldir = NULL;
9302 dumptape_leveldir = getTreeInfoFromIdentifier(leveldir_first,
9303 global.dumptape_leveldir);
9305 if (dumptape_leveldir == NULL)
9306 Fail("no such level identifier: '%s'", global.dumptape_leveldir);
9308 if (global.dumptape_level_nr < dumptape_leveldir->first_level ||
9309 global.dumptape_level_nr > dumptape_leveldir->last_level)
9310 Fail("no such level number: %d", global.dumptape_level_nr);
9312 leveldir_current = dumptape_leveldir;
9314 if (options.mytapes)
9315 LoadTape(global.dumptape_level_nr);
9317 LoadSolutionTape(global.dumptape_level_nr);
9325 // ============================================================================
9326 // score file functions
9327 // ============================================================================
9329 static void setScoreInfoToDefaultsExt(struct ScoreInfo *scores)
9333 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9335 strcpy(scores->entry[i].tape_basename, UNDEFINED_FILENAME);
9336 strcpy(scores->entry[i].name, EMPTY_PLAYER_NAME);
9337 scores->entry[i].score = 0;
9338 scores->entry[i].time = 0;
9340 scores->entry[i].id = -1;
9341 strcpy(scores->entry[i].tape_date, UNKNOWN_NAME);
9342 strcpy(scores->entry[i].platform, UNKNOWN_NAME);
9343 strcpy(scores->entry[i].version, UNKNOWN_NAME);
9344 strcpy(scores->entry[i].country_name, UNKNOWN_NAME);
9345 strcpy(scores->entry[i].country_code, "??");
9348 scores->num_entries = 0;
9349 scores->last_added = -1;
9350 scores->last_added_local = -1;
9352 scores->updated = FALSE;
9353 scores->uploaded = FALSE;
9354 scores->tape_downloaded = FALSE;
9355 scores->force_last_added = FALSE;
9357 // The following values are intentionally not reset here:
9361 // - continue_playing
9362 // - continue_on_return
9365 static void setScoreInfoToDefaults(void)
9367 setScoreInfoToDefaultsExt(&scores);
9370 static void setServerScoreInfoToDefaults(void)
9372 setScoreInfoToDefaultsExt(&server_scores);
9375 static void LoadScore_OLD(int nr)
9378 char *filename = getScoreFilename(nr);
9379 char cookie[MAX_LINE_LEN];
9380 char line[MAX_LINE_LEN];
9384 if (!(file = fopen(filename, MODE_READ)))
9387 // check file identifier
9388 if (fgets(cookie, MAX_LINE_LEN, file) == NULL)
9390 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
9391 cookie[strlen(cookie) - 1] = '\0';
9393 if (!checkCookieString(cookie, SCORE_COOKIE_TMPL))
9395 Warn("unknown format of score file '%s'", filename);
9402 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9404 if (fscanf(file, "%d", &scores.entry[i].score) == EOF)
9405 Warn("fscanf() failed; %s", strerror(errno));
9407 if (fgets(line, MAX_LINE_LEN, file) == NULL)
9410 if (strlen(line) > 0 && line[strlen(line) - 1] == '\n')
9411 line[strlen(line) - 1] = '\0';
9413 for (line_ptr = line; *line_ptr; line_ptr++)
9415 if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != '\0')
9417 strncpy(scores.entry[i].name, line_ptr, MAX_PLAYER_NAME_LEN);
9418 scores.entry[i].name[MAX_PLAYER_NAME_LEN] = '\0';
9427 static void ConvertScore_OLD(void)
9429 // only convert score to time for levels that rate playing time over score
9430 if (!level.rate_time_over_score)
9433 // convert old score to playing time for score-less levels (like Supaplex)
9434 int time_final_max = 999;
9437 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9439 int score = scores.entry[i].score;
9441 if (score > 0 && score < time_final_max)
9442 scores.entry[i].time = (time_final_max - score - 1) * FRAMES_PER_SECOND;
9446 static int LoadScore_VERS(File *file, int chunk_size, struct ScoreInfo *scores)
9448 scores->file_version = getFileVersion(file);
9449 scores->game_version = getFileVersion(file);
9454 static int LoadScore_INFO(File *file, int chunk_size, struct ScoreInfo *scores)
9456 char *level_identifier = NULL;
9457 int level_identifier_size;
9460 level_identifier_size = getFile16BitBE(file);
9462 level_identifier = checked_malloc(level_identifier_size);
9464 for (i = 0; i < level_identifier_size; i++)
9465 level_identifier[i] = getFile8Bit(file);
9467 strncpy(scores->level_identifier, level_identifier, MAX_FILENAME_LEN);
9468 scores->level_identifier[MAX_FILENAME_LEN] = '\0';
9470 checked_free(level_identifier);
9472 scores->level_nr = getFile16BitBE(file);
9473 scores->num_entries = getFile16BitBE(file);
9475 chunk_size = 2 + level_identifier_size + 2 + 2;
9480 static int LoadScore_NAME(File *file, int chunk_size, struct ScoreInfo *scores)
9484 for (i = 0; i < scores->num_entries; i++)
9486 for (j = 0; j < MAX_PLAYER_NAME_LEN; j++)
9487 scores->entry[i].name[j] = getFile8Bit(file);
9489 scores->entry[i].name[MAX_PLAYER_NAME_LEN] = '\0';
9492 chunk_size = scores->num_entries * MAX_PLAYER_NAME_LEN;
9497 static int LoadScore_SCOR(File *file, int chunk_size, struct ScoreInfo *scores)
9501 for (i = 0; i < scores->num_entries; i++)
9502 scores->entry[i].score = getFile16BitBE(file);
9504 chunk_size = scores->num_entries * 2;
9509 static int LoadScore_SC4R(File *file, int chunk_size, struct ScoreInfo *scores)
9513 for (i = 0; i < scores->num_entries; i++)
9514 scores->entry[i].score = getFile32BitBE(file);
9516 chunk_size = scores->num_entries * 4;
9521 static int LoadScore_TIME(File *file, int chunk_size, struct ScoreInfo *scores)
9525 for (i = 0; i < scores->num_entries; i++)
9526 scores->entry[i].time = getFile32BitBE(file);
9528 chunk_size = scores->num_entries * 4;
9533 static int LoadScore_TAPE(File *file, int chunk_size, struct ScoreInfo *scores)
9537 for (i = 0; i < scores->num_entries; i++)
9539 for (j = 0; j < MAX_SCORE_TAPE_BASENAME_LEN; j++)
9540 scores->entry[i].tape_basename[j] = getFile8Bit(file);
9542 scores->entry[i].tape_basename[MAX_SCORE_TAPE_BASENAME_LEN] = '\0';
9545 chunk_size = scores->num_entries * MAX_SCORE_TAPE_BASENAME_LEN;
9550 void LoadScore(int nr)
9552 char *filename = getScoreFilename(nr);
9553 char cookie[MAX_LINE_LEN];
9554 char chunk_name[CHUNK_ID_LEN + 1];
9556 boolean old_score_file_format = FALSE;
9559 // always start with reliable default values
9560 setScoreInfoToDefaults();
9562 if (!(file = openFile(filename, MODE_READ)))
9565 getFileChunkBE(file, chunk_name, NULL);
9566 if (strEqual(chunk_name, "RND1"))
9568 getFile32BitBE(file); // not used
9570 getFileChunkBE(file, chunk_name, NULL);
9571 if (!strEqual(chunk_name, "SCOR"))
9573 Warn("unknown format of score file '%s'", filename);
9580 else // check for old file format with cookie string
9582 strcpy(cookie, chunk_name);
9583 if (getStringFromFile(file, &cookie[4], MAX_LINE_LEN - 4) == NULL)
9585 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
9586 cookie[strlen(cookie) - 1] = '\0';
9588 if (!checkCookieString(cookie, SCORE_COOKIE_TMPL))
9590 Warn("unknown format of score file '%s'", filename);
9597 old_score_file_format = TRUE;
9600 if (old_score_file_format)
9602 // score files from versions before 4.2.4.0 without chunk structure
9605 // convert score to time, if possible (mainly for Supaplex levels)
9614 int (*loader)(File *, int, struct ScoreInfo *);
9618 { "VERS", SCORE_CHUNK_VERS_SIZE, LoadScore_VERS },
9619 { "INFO", -1, LoadScore_INFO },
9620 { "NAME", -1, LoadScore_NAME },
9621 { "SCOR", -1, LoadScore_SCOR },
9622 { "SC4R", -1, LoadScore_SC4R },
9623 { "TIME", -1, LoadScore_TIME },
9624 { "TAPE", -1, LoadScore_TAPE },
9629 while (getFileChunkBE(file, chunk_name, &chunk_size))
9633 while (chunk_info[i].name != NULL &&
9634 !strEqual(chunk_name, chunk_info[i].name))
9637 if (chunk_info[i].name == NULL)
9639 Warn("unknown chunk '%s' in score file '%s'",
9640 chunk_name, filename);
9642 ReadUnusedBytesFromFile(file, chunk_size);
9644 else if (chunk_info[i].size != -1 &&
9645 chunk_info[i].size != chunk_size)
9647 Warn("wrong size (%d) of chunk '%s' in score file '%s'",
9648 chunk_size, chunk_name, filename);
9650 ReadUnusedBytesFromFile(file, chunk_size);
9654 // call function to load this score chunk
9655 int chunk_size_expected =
9656 (chunk_info[i].loader)(file, chunk_size, &scores);
9658 // the size of some chunks cannot be checked before reading other
9659 // chunks first (like "HEAD" and "BODY") that contain some header
9660 // information, so check them here
9661 if (chunk_size_expected != chunk_size)
9663 Warn("wrong size (%d) of chunk '%s' in score file '%s'",
9664 chunk_size, chunk_name, filename);
9673 #if ENABLE_HISTORIC_CHUNKS
9674 void SaveScore_OLD(int nr)
9677 char *filename = getScoreFilename(nr);
9680 // used instead of "leveldir_current->subdir" (for network games)
9681 InitScoreDirectory(levelset.identifier);
9683 if (!(file = fopen(filename, MODE_WRITE)))
9685 Warn("cannot save score for level %d", nr);
9690 fprintf(file, "%s\n\n", SCORE_COOKIE);
9692 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9693 fprintf(file, "%d %s\n", scores.entry[i].score, scores.entry[i].name);
9697 SetFilePermissions(filename, PERMS_PRIVATE);
9701 static void SaveScore_VERS(FILE *file, struct ScoreInfo *scores)
9703 putFileVersion(file, scores->file_version);
9704 putFileVersion(file, scores->game_version);
9707 static void SaveScore_INFO(FILE *file, struct ScoreInfo *scores)
9709 int level_identifier_size = strlen(scores->level_identifier) + 1;
9712 putFile16BitBE(file, level_identifier_size);
9714 for (i = 0; i < level_identifier_size; i++)
9715 putFile8Bit(file, scores->level_identifier[i]);
9717 putFile16BitBE(file, scores->level_nr);
9718 putFile16BitBE(file, scores->num_entries);
9721 static void SaveScore_NAME(FILE *file, struct ScoreInfo *scores)
9725 for (i = 0; i < scores->num_entries; i++)
9727 int name_size = strlen(scores->entry[i].name);
9729 for (j = 0; j < MAX_PLAYER_NAME_LEN; j++)
9730 putFile8Bit(file, (j < name_size ? scores->entry[i].name[j] : 0));
9734 static void SaveScore_SCOR(FILE *file, struct ScoreInfo *scores)
9738 for (i = 0; i < scores->num_entries; i++)
9739 putFile16BitBE(file, scores->entry[i].score);
9742 static void SaveScore_SC4R(FILE *file, struct ScoreInfo *scores)
9746 for (i = 0; i < scores->num_entries; i++)
9747 putFile32BitBE(file, scores->entry[i].score);
9750 static void SaveScore_TIME(FILE *file, struct ScoreInfo *scores)
9754 for (i = 0; i < scores->num_entries; i++)
9755 putFile32BitBE(file, scores->entry[i].time);
9758 static void SaveScore_TAPE(FILE *file, struct ScoreInfo *scores)
9762 for (i = 0; i < scores->num_entries; i++)
9764 int size = strlen(scores->entry[i].tape_basename);
9766 for (j = 0; j < MAX_SCORE_TAPE_BASENAME_LEN; j++)
9767 putFile8Bit(file, (j < size ? scores->entry[i].tape_basename[j] : 0));
9771 static void SaveScoreToFilename(char *filename)
9774 int info_chunk_size;
9775 int name_chunk_size;
9776 int scor_chunk_size;
9777 int sc4r_chunk_size;
9778 int time_chunk_size;
9779 int tape_chunk_size;
9780 boolean has_large_score_values;
9783 if (!(file = fopen(filename, MODE_WRITE)))
9785 Warn("cannot save score file '%s'", filename);
9790 info_chunk_size = 2 + (strlen(scores.level_identifier) + 1) + 2 + 2;
9791 name_chunk_size = scores.num_entries * MAX_PLAYER_NAME_LEN;
9792 scor_chunk_size = scores.num_entries * 2;
9793 sc4r_chunk_size = scores.num_entries * 4;
9794 time_chunk_size = scores.num_entries * 4;
9795 tape_chunk_size = scores.num_entries * MAX_SCORE_TAPE_BASENAME_LEN;
9797 has_large_score_values = FALSE;
9798 for (i = 0; i < scores.num_entries; i++)
9799 if (scores.entry[i].score > 0xffff)
9800 has_large_score_values = TRUE;
9802 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
9803 putFileChunkBE(file, "SCOR", CHUNK_SIZE_NONE);
9805 putFileChunkBE(file, "VERS", SCORE_CHUNK_VERS_SIZE);
9806 SaveScore_VERS(file, &scores);
9808 putFileChunkBE(file, "INFO", info_chunk_size);
9809 SaveScore_INFO(file, &scores);
9811 putFileChunkBE(file, "NAME", name_chunk_size);
9812 SaveScore_NAME(file, &scores);
9814 if (has_large_score_values)
9816 putFileChunkBE(file, "SC4R", sc4r_chunk_size);
9817 SaveScore_SC4R(file, &scores);
9821 putFileChunkBE(file, "SCOR", scor_chunk_size);
9822 SaveScore_SCOR(file, &scores);
9825 putFileChunkBE(file, "TIME", time_chunk_size);
9826 SaveScore_TIME(file, &scores);
9828 putFileChunkBE(file, "TAPE", tape_chunk_size);
9829 SaveScore_TAPE(file, &scores);
9833 SetFilePermissions(filename, PERMS_PRIVATE);
9836 void SaveScore(int nr)
9838 char *filename = getScoreFilename(nr);
9841 // used instead of "leveldir_current->subdir" (for network games)
9842 InitScoreDirectory(levelset.identifier);
9844 scores.file_version = FILE_VERSION_ACTUAL;
9845 scores.game_version = GAME_VERSION_ACTUAL;
9847 strncpy(scores.level_identifier, levelset.identifier, MAX_FILENAME_LEN);
9848 scores.level_identifier[MAX_FILENAME_LEN] = '\0';
9849 scores.level_nr = level_nr;
9851 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9852 if (scores.entry[i].score == 0 &&
9853 scores.entry[i].time == 0 &&
9854 strEqual(scores.entry[i].name, EMPTY_PLAYER_NAME))
9857 scores.num_entries = i;
9859 if (scores.num_entries == 0)
9862 SaveScoreToFilename(filename);
9865 static void LoadServerScoreFromCache(int nr)
9867 struct ScoreEntry score_entry;
9876 { &score_entry.score, FALSE, 0 },
9877 { &score_entry.time, FALSE, 0 },
9878 { score_entry.name, TRUE, MAX_PLAYER_NAME_LEN },
9879 { score_entry.tape_basename, TRUE, MAX_FILENAME_LEN },
9880 { score_entry.tape_date, TRUE, MAX_ISO_DATE_LEN },
9881 { &score_entry.id, FALSE, 0 },
9882 { score_entry.platform, TRUE, MAX_PLATFORM_TEXT_LEN },
9883 { score_entry.version, TRUE, MAX_VERSION_TEXT_LEN },
9884 { score_entry.country_code, TRUE, MAX_COUNTRY_CODE_LEN },
9885 { score_entry.country_name, TRUE, MAX_COUNTRY_NAME_LEN },
9889 char *filename = getScoreCacheFilename(nr);
9890 SetupFileHash *score_hash = loadSetupFileHash(filename);
9893 server_scores.num_entries = 0;
9895 if (score_hash == NULL)
9898 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9900 score_entry = server_scores.entry[i];
9902 for (j = 0; score_mapping[j].value != NULL; j++)
9906 sprintf(token, "%02d.%d", i, j);
9908 char *value = getHashEntry(score_hash, token);
9913 if (score_mapping[j].is_string)
9915 char *score_value = (char *)score_mapping[j].value;
9916 int value_size = score_mapping[j].string_size;
9918 strncpy(score_value, value, value_size);
9919 score_value[value_size] = '\0';
9923 int *score_value = (int *)score_mapping[j].value;
9925 *score_value = atoi(value);
9928 server_scores.num_entries = i + 1;
9931 server_scores.entry[i] = score_entry;
9934 freeSetupFileHash(score_hash);
9937 void LoadServerScore(int nr, boolean download_score)
9939 if (!setup.use_api_server)
9942 // always start with reliable default values
9943 setServerScoreInfoToDefaults();
9945 // 1st step: load server scores from cache file (which may not exist)
9946 // (this should prevent reading it while the thread is writing to it)
9947 LoadServerScoreFromCache(nr);
9949 if (download_score && runtime.use_api_server)
9951 // 2nd step: download server scores from score server to cache file
9952 // (as thread, as it might time out if the server is not reachable)
9953 ApiGetScoreAsThread(nr);
9957 void PrepareScoreTapesForUpload(char *leveldir_subdir)
9959 MarkTapeDirectoryUploadsAsIncomplete(leveldir_subdir);
9961 // if score tape not uploaded, ask for uploading missing tapes later
9962 if (!setup.has_remaining_tapes)
9963 setup.ask_for_remaining_tapes = TRUE;
9965 setup.provide_uploading_tapes = TRUE;
9966 setup.has_remaining_tapes = TRUE;
9968 SaveSetup_ServerSetup();
9971 void SaveServerScore(int nr, boolean tape_saved)
9973 if (!runtime.use_api_server)
9975 PrepareScoreTapesForUpload(leveldir_current->subdir);
9980 ApiAddScoreAsThread(nr, tape_saved, NULL);
9983 void SaveServerScoreFromFile(int nr, boolean tape_saved,
9984 char *score_tape_filename)
9986 if (!runtime.use_api_server)
9989 ApiAddScoreAsThread(nr, tape_saved, score_tape_filename);
9992 void LoadLocalAndServerScore(int nr, boolean download_score)
9994 int last_added_local = scores.last_added_local;
9995 boolean force_last_added = scores.force_last_added;
9997 // needed if only showing server scores
9998 setScoreInfoToDefaults();
10000 if (!strEqual(setup.scores_in_highscore_list, STR_SCORES_TYPE_SERVER_ONLY))
10003 // restore last added local score entry (before merging server scores)
10004 scores.last_added = scores.last_added_local = last_added_local;
10006 if (setup.use_api_server &&
10007 !strEqual(setup.scores_in_highscore_list, STR_SCORES_TYPE_LOCAL_ONLY))
10009 // load server scores from cache file and trigger update from server
10010 LoadServerScore(nr, download_score);
10012 // merge local scores with scores from server
10013 MergeServerScore();
10016 if (force_last_added)
10017 scores.force_last_added = force_last_added;
10021 // ============================================================================
10022 // setup file functions
10023 // ============================================================================
10025 #define TOKEN_STR_PLAYER_PREFIX "player_"
10028 static struct TokenInfo global_setup_tokens[] =
10032 &setup.player_name, "player_name"
10036 &setup.multiple_users, "multiple_users"
10040 &setup.sound, "sound"
10044 &setup.sound_loops, "repeating_sound_loops"
10048 &setup.sound_music, "background_music"
10052 &setup.sound_simple, "simple_sound_effects"
10056 &setup.toons, "toons"
10060 &setup.global_animations, "global_animations"
10064 &setup.scroll_delay, "scroll_delay"
10068 &setup.forced_scroll_delay, "forced_scroll_delay"
10072 &setup.scroll_delay_value, "scroll_delay_value"
10076 &setup.engine_snapshot_mode, "engine_snapshot_mode"
10080 &setup.engine_snapshot_memory, "engine_snapshot_memory"
10084 &setup.fade_screens, "fade_screens"
10088 &setup.autorecord, "automatic_tape_recording"
10092 &setup.autorecord_after_replay, "autorecord_after_replay"
10096 &setup.auto_pause_on_start, "auto_pause_on_start"
10100 &setup.show_titlescreen, "show_titlescreen"
10104 &setup.quick_doors, "quick_doors"
10108 &setup.team_mode, "team_mode"
10112 &setup.handicap, "handicap"
10116 &setup.skip_levels, "skip_levels"
10120 &setup.increment_levels, "increment_levels"
10124 &setup.auto_play_next_level, "auto_play_next_level"
10128 &setup.count_score_after_game, "count_score_after_game"
10132 &setup.show_scores_after_game, "show_scores_after_game"
10136 &setup.time_limit, "time_limit"
10140 &setup.fullscreen, "fullscreen"
10144 &setup.window_scaling_percent, "window_scaling_percent"
10148 &setup.window_scaling_quality, "window_scaling_quality"
10152 &setup.screen_rendering_mode, "screen_rendering_mode"
10156 &setup.vsync_mode, "vsync_mode"
10160 &setup.ask_on_escape, "ask_on_escape"
10164 &setup.ask_on_escape_editor, "ask_on_escape_editor"
10168 &setup.ask_on_game_over, "ask_on_game_over"
10172 &setup.ask_on_quit_game, "ask_on_quit_game"
10176 &setup.ask_on_quit_program, "ask_on_quit_program"
10180 &setup.quick_switch, "quick_player_switch"
10184 &setup.input_on_focus, "input_on_focus"
10188 &setup.prefer_aga_graphics, "prefer_aga_graphics"
10192 &setup.prefer_lowpass_sounds, "prefer_lowpass_sounds"
10196 &setup.prefer_extra_panel_items, "prefer_extra_panel_items"
10200 &setup.game_speed_extended, "game_speed_extended"
10204 &setup.game_frame_delay, "game_frame_delay"
10208 &setup.bd_skip_uncovering, "bd_skip_uncovering"
10212 &setup.bd_skip_hatching, "bd_skip_hatching"
10216 &setup.bd_scroll_delay, "bd_scroll_delay"
10220 &setup.bd_smooth_movements, "bd_smooth_movements"
10224 &setup.sp_show_border_elements, "sp_show_border_elements"
10228 &setup.small_game_graphics, "small_game_graphics"
10232 &setup.show_load_save_buttons, "show_load_save_buttons"
10236 &setup.show_undo_redo_buttons, "show_undo_redo_buttons"
10240 &setup.scores_in_highscore_list, "scores_in_highscore_list"
10244 &setup.graphics_set, "graphics_set"
10248 &setup.sounds_set, "sounds_set"
10252 &setup.music_set, "music_set"
10256 &setup.override_level_graphics, "override_level_graphics"
10260 &setup.override_level_sounds, "override_level_sounds"
10264 &setup.override_level_music, "override_level_music"
10268 &setup.volume_simple, "volume_simple"
10272 &setup.volume_loops, "volume_loops"
10276 &setup.volume_music, "volume_music"
10280 &setup.network_mode, "network_mode"
10284 &setup.network_player_nr, "network_player"
10288 &setup.network_server_hostname, "network_server_hostname"
10292 &setup.touch.control_type, "touch.control_type"
10296 &setup.touch.move_distance, "touch.move_distance"
10300 &setup.touch.drop_distance, "touch.drop_distance"
10304 &setup.touch.transparency, "touch.transparency"
10308 &setup.touch.draw_outlined, "touch.draw_outlined"
10312 &setup.touch.draw_pressed, "touch.draw_pressed"
10316 &setup.touch.grid_xsize[0], "touch.virtual_buttons.0.xsize"
10320 &setup.touch.grid_ysize[0], "touch.virtual_buttons.0.ysize"
10324 &setup.touch.grid_xsize[1], "touch.virtual_buttons.1.xsize"
10328 &setup.touch.grid_ysize[1], "touch.virtual_buttons.1.ysize"
10332 &setup.touch.overlay_buttons, "touch.overlay_buttons"
10336 static struct TokenInfo auto_setup_tokens[] =
10340 &setup.auto_setup.editor_zoom_tilesize, "editor.zoom_tilesize"
10344 static struct TokenInfo server_setup_tokens[] =
10348 &setup.player_uuid, "player_uuid"
10352 &setup.player_version, "player_version"
10356 &setup.use_api_server, TEST_PREFIX "use_api_server"
10360 &setup.api_server_hostname, TEST_PREFIX "api_server_hostname"
10364 &setup.api_server_password, TEST_PREFIX "api_server_password"
10368 &setup.ask_for_uploading_tapes, TEST_PREFIX "ask_for_uploading_tapes"
10372 &setup.ask_for_remaining_tapes, TEST_PREFIX "ask_for_remaining_tapes"
10376 &setup.provide_uploading_tapes, TEST_PREFIX "provide_uploading_tapes"
10380 &setup.ask_for_using_api_server,TEST_PREFIX "ask_for_using_api_server"
10384 &setup.has_remaining_tapes, TEST_PREFIX "has_remaining_tapes"
10388 static struct TokenInfo editor_setup_tokens[] =
10392 &setup.editor.el_classic, "editor.el_classic"
10396 &setup.editor.el_custom, "editor.el_custom"
10400 &setup.editor.el_user_defined, "editor.el_user_defined"
10404 &setup.editor.el_dynamic, "editor.el_dynamic"
10408 &setup.editor.el_headlines, "editor.el_headlines"
10412 &setup.editor.show_element_token, "editor.show_element_token"
10416 &setup.editor.show_read_only_warning, "editor.show_read_only_warning"
10420 static struct TokenInfo editor_cascade_setup_tokens[] =
10424 &setup.editor_cascade.el_bd, "editor.cascade.el_bd"
10428 &setup.editor_cascade.el_bd_native, "editor.cascade.el_bd_native"
10432 &setup.editor_cascade.el_em, "editor.cascade.el_em"
10436 &setup.editor_cascade.el_emc, "editor.cascade.el_emc"
10440 &setup.editor_cascade.el_rnd, "editor.cascade.el_rnd"
10444 &setup.editor_cascade.el_sb, "editor.cascade.el_sb"
10448 &setup.editor_cascade.el_sp, "editor.cascade.el_sp"
10452 &setup.editor_cascade.el_dc, "editor.cascade.el_dc"
10456 &setup.editor_cascade.el_dx, "editor.cascade.el_dx"
10460 &setup.editor_cascade.el_mm, "editor.cascade.el_mm"
10464 &setup.editor_cascade.el_df, "editor.cascade.el_df"
10468 &setup.editor_cascade.el_chars, "editor.cascade.el_chars"
10472 &setup.editor_cascade.el_steel_chars, "editor.cascade.el_steel_chars"
10476 &setup.editor_cascade.el_ce, "editor.cascade.el_ce"
10480 &setup.editor_cascade.el_ge, "editor.cascade.el_ge"
10484 &setup.editor_cascade.el_es, "editor.cascade.el_es"
10488 &setup.editor_cascade.el_ref, "editor.cascade.el_ref"
10492 &setup.editor_cascade.el_user, "editor.cascade.el_user"
10496 &setup.editor_cascade.el_dynamic, "editor.cascade.el_dynamic"
10500 static struct TokenInfo shortcut_setup_tokens[] =
10504 &setup.shortcut.save_game, "shortcut.save_game"
10508 &setup.shortcut.load_game, "shortcut.load_game"
10512 &setup.shortcut.restart_game, "shortcut.restart_game"
10516 &setup.shortcut.pause_before_end, "shortcut.pause_before_end"
10520 &setup.shortcut.toggle_pause, "shortcut.toggle_pause"
10524 &setup.shortcut.focus_player[0], "shortcut.focus_player_1"
10528 &setup.shortcut.focus_player[1], "shortcut.focus_player_2"
10532 &setup.shortcut.focus_player[2], "shortcut.focus_player_3"
10536 &setup.shortcut.focus_player[3], "shortcut.focus_player_4"
10540 &setup.shortcut.focus_player_all, "shortcut.focus_player_all"
10544 &setup.shortcut.tape_eject, "shortcut.tape_eject"
10548 &setup.shortcut.tape_extra, "shortcut.tape_extra"
10552 &setup.shortcut.tape_stop, "shortcut.tape_stop"
10556 &setup.shortcut.tape_pause, "shortcut.tape_pause"
10560 &setup.shortcut.tape_record, "shortcut.tape_record"
10564 &setup.shortcut.tape_play, "shortcut.tape_play"
10568 &setup.shortcut.sound_simple, "shortcut.sound_simple"
10572 &setup.shortcut.sound_loops, "shortcut.sound_loops"
10576 &setup.shortcut.sound_music, "shortcut.sound_music"
10580 &setup.shortcut.snap_left, "shortcut.snap_left"
10584 &setup.shortcut.snap_right, "shortcut.snap_right"
10588 &setup.shortcut.snap_up, "shortcut.snap_up"
10592 &setup.shortcut.snap_down, "shortcut.snap_down"
10596 static struct SetupInputInfo setup_input;
10597 static struct TokenInfo player_setup_tokens[] =
10601 &setup_input.use_joystick, ".use_joystick"
10605 &setup_input.joy.device_name, ".joy.device_name"
10609 &setup_input.joy.xleft, ".joy.xleft"
10613 &setup_input.joy.xmiddle, ".joy.xmiddle"
10617 &setup_input.joy.xright, ".joy.xright"
10621 &setup_input.joy.yupper, ".joy.yupper"
10625 &setup_input.joy.ymiddle, ".joy.ymiddle"
10629 &setup_input.joy.ylower, ".joy.ylower"
10633 &setup_input.joy.snap, ".joy.snap_field"
10637 &setup_input.joy.drop, ".joy.place_bomb"
10641 &setup_input.key.left, ".key.move_left"
10645 &setup_input.key.right, ".key.move_right"
10649 &setup_input.key.up, ".key.move_up"
10653 &setup_input.key.down, ".key.move_down"
10657 &setup_input.key.snap, ".key.snap_field"
10661 &setup_input.key.drop, ".key.place_bomb"
10665 static struct TokenInfo system_setup_tokens[] =
10669 &setup.system.sdl_renderdriver, "system.sdl_renderdriver"
10673 &setup.system.sdl_videodriver, "system.sdl_videodriver"
10677 &setup.system.sdl_audiodriver, "system.sdl_audiodriver"
10681 &setup.system.audio_fragment_size, "system.audio_fragment_size"
10685 static struct TokenInfo internal_setup_tokens[] =
10689 &setup.internal.program_title, "program_title"
10693 &setup.internal.program_version, "program_version"
10697 &setup.internal.program_author, "program_author"
10701 &setup.internal.program_email, "program_email"
10705 &setup.internal.program_website, "program_website"
10709 &setup.internal.program_copyright, "program_copyright"
10713 &setup.internal.program_company, "program_company"
10717 &setup.internal.program_icon_file, "program_icon_file"
10721 &setup.internal.default_graphics_set, "default_graphics_set"
10725 &setup.internal.default_sounds_set, "default_sounds_set"
10729 &setup.internal.default_music_set, "default_music_set"
10733 &setup.internal.fallback_graphics_file, "fallback_graphics_file"
10737 &setup.internal.fallback_sounds_file, "fallback_sounds_file"
10741 &setup.internal.fallback_music_file, "fallback_music_file"
10745 &setup.internal.default_level_series, "default_level_series"
10749 &setup.internal.default_window_width, "default_window_width"
10753 &setup.internal.default_window_height, "default_window_height"
10757 &setup.internal.choose_from_top_leveldir, "choose_from_top_leveldir"
10761 &setup.internal.show_scaling_in_title, "show_scaling_in_title"
10765 &setup.internal.create_user_levelset, "create_user_levelset"
10769 &setup.internal.info_screens_from_main, "info_screens_from_main"
10773 &setup.internal.menu_game, "menu_game"
10777 &setup.internal.menu_engines, "menu_engines"
10781 &setup.internal.menu_editor, "menu_editor"
10785 &setup.internal.menu_graphics, "menu_graphics"
10789 &setup.internal.menu_sound, "menu_sound"
10793 &setup.internal.menu_artwork, "menu_artwork"
10797 &setup.internal.menu_input, "menu_input"
10801 &setup.internal.menu_touch, "menu_touch"
10805 &setup.internal.menu_shortcuts, "menu_shortcuts"
10809 &setup.internal.menu_exit, "menu_exit"
10813 &setup.internal.menu_save_and_exit, "menu_save_and_exit"
10817 &setup.internal.menu_shortcuts_various, "menu_shortcuts_various"
10821 &setup.internal.menu_shortcuts_focus, "menu_shortcuts_focus"
10825 &setup.internal.menu_shortcuts_tape, "menu_shortcuts_tape"
10829 &setup.internal.menu_shortcuts_sound, "menu_shortcuts_sound"
10833 &setup.internal.menu_shortcuts_snap, "menu_shortcuts_snap"
10837 &setup.internal.info_title, "info_title"
10841 &setup.internal.info_elements, "info_elements"
10845 &setup.internal.info_music, "info_music"
10849 &setup.internal.info_credits, "info_credits"
10853 &setup.internal.info_program, "info_program"
10857 &setup.internal.info_version, "info_version"
10861 &setup.internal.info_levelset, "info_levelset"
10865 &setup.internal.info_exit, "info_exit"
10869 static struct TokenInfo debug_setup_tokens[] =
10873 &setup.debug.frame_delay[0], "debug.frame_delay_0"
10877 &setup.debug.frame_delay[1], "debug.frame_delay_1"
10881 &setup.debug.frame_delay[2], "debug.frame_delay_2"
10885 &setup.debug.frame_delay[3], "debug.frame_delay_3"
10889 &setup.debug.frame_delay[4], "debug.frame_delay_4"
10893 &setup.debug.frame_delay[5], "debug.frame_delay_5"
10897 &setup.debug.frame_delay[6], "debug.frame_delay_6"
10901 &setup.debug.frame_delay[7], "debug.frame_delay_7"
10905 &setup.debug.frame_delay[8], "debug.frame_delay_8"
10909 &setup.debug.frame_delay[9], "debug.frame_delay_9"
10913 &setup.debug.frame_delay_key[0], "debug.key.frame_delay_0"
10917 &setup.debug.frame_delay_key[1], "debug.key.frame_delay_1"
10921 &setup.debug.frame_delay_key[2], "debug.key.frame_delay_2"
10925 &setup.debug.frame_delay_key[3], "debug.key.frame_delay_3"
10929 &setup.debug.frame_delay_key[4], "debug.key.frame_delay_4"
10933 &setup.debug.frame_delay_key[5], "debug.key.frame_delay_5"
10937 &setup.debug.frame_delay_key[6], "debug.key.frame_delay_6"
10941 &setup.debug.frame_delay_key[7], "debug.key.frame_delay_7"
10945 &setup.debug.frame_delay_key[8], "debug.key.frame_delay_8"
10949 &setup.debug.frame_delay_key[9], "debug.key.frame_delay_9"
10953 &setup.debug.frame_delay_use_mod_key, "debug.frame_delay.use_mod_key"},
10956 &setup.debug.frame_delay_game_only, "debug.frame_delay.game_only"
10960 &setup.debug.show_frames_per_second, "debug.show_frames_per_second"
10964 &setup.debug.xsn_mode, "debug.xsn_mode"
10968 &setup.debug.xsn_percent, "debug.xsn_percent"
10972 static struct TokenInfo options_setup_tokens[] =
10976 &setup.options.verbose, "options.verbose"
10980 &setup.options.debug, "options.debug"
10984 &setup.options.debug_mode, "options.debug_mode"
10988 static void setSetupInfoToDefaults(struct SetupInfo *si)
10992 si->player_name = getStringCopy(getDefaultUserName(user.nr));
10994 si->multiple_users = TRUE;
10997 si->sound_loops = TRUE;
10998 si->sound_music = TRUE;
10999 si->sound_simple = TRUE;
11001 si->global_animations = TRUE;
11002 si->scroll_delay = TRUE;
11003 si->forced_scroll_delay = FALSE;
11004 si->scroll_delay_value = STD_SCROLL_DELAY;
11005 si->engine_snapshot_mode = getStringCopy(STR_SNAPSHOT_MODE_DEFAULT);
11006 si->engine_snapshot_memory = SNAPSHOT_MEMORY_DEFAULT;
11007 si->fade_screens = TRUE;
11008 si->autorecord = TRUE;
11009 si->autorecord_after_replay = TRUE;
11010 si->auto_pause_on_start = FALSE;
11011 si->show_titlescreen = TRUE;
11012 si->quick_doors = FALSE;
11013 si->team_mode = FALSE;
11014 si->handicap = TRUE;
11015 si->skip_levels = TRUE;
11016 si->increment_levels = TRUE;
11017 si->auto_play_next_level = TRUE;
11018 si->count_score_after_game = TRUE;
11019 si->show_scores_after_game = TRUE;
11020 si->time_limit = TRUE;
11021 si->fullscreen = FALSE;
11022 si->window_scaling_percent = STD_WINDOW_SCALING_PERCENT;
11023 si->window_scaling_quality = getStringCopy(SCALING_QUALITY_DEFAULT);
11024 si->screen_rendering_mode = getStringCopy(STR_SPECIAL_RENDERING_DEFAULT);
11025 si->vsync_mode = getStringCopy(STR_VSYNC_MODE_DEFAULT);
11026 si->ask_on_escape = TRUE;
11027 si->ask_on_escape_editor = TRUE;
11028 si->ask_on_game_over = TRUE;
11029 si->ask_on_quit_game = TRUE;
11030 si->ask_on_quit_program = TRUE;
11031 si->quick_switch = FALSE;
11032 si->input_on_focus = FALSE;
11033 si->prefer_aga_graphics = TRUE;
11034 si->prefer_lowpass_sounds = FALSE;
11035 si->prefer_extra_panel_items = TRUE;
11036 si->game_speed_extended = FALSE;
11037 si->game_frame_delay = GAME_FRAME_DELAY;
11038 si->bd_skip_uncovering = FALSE;
11039 si->bd_skip_hatching = FALSE;
11040 si->bd_scroll_delay = TRUE;
11041 si->bd_smooth_movements = AUTO;
11042 si->sp_show_border_elements = FALSE;
11043 si->small_game_graphics = FALSE;
11044 si->show_load_save_buttons = FALSE;
11045 si->show_undo_redo_buttons = FALSE;
11046 si->scores_in_highscore_list = getStringCopy(STR_SCORES_TYPE_DEFAULT);
11048 si->graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
11049 si->sounds_set = getStringCopy(SND_CLASSIC_SUBDIR);
11050 si->music_set = getStringCopy(MUS_CLASSIC_SUBDIR);
11052 si->override_level_graphics = FALSE;
11053 si->override_level_sounds = FALSE;
11054 si->override_level_music = FALSE;
11056 si->volume_simple = 100; // percent
11057 si->volume_loops = 100; // percent
11058 si->volume_music = 100; // percent
11060 si->network_mode = FALSE;
11061 si->network_player_nr = 0; // first player
11062 si->network_server_hostname = getStringCopy(STR_NETWORK_AUTO_DETECT);
11064 si->touch.control_type = getStringCopy(TOUCH_CONTROL_DEFAULT);
11065 si->touch.move_distance = TOUCH_MOVE_DISTANCE_DEFAULT; // percent
11066 si->touch.drop_distance = TOUCH_DROP_DISTANCE_DEFAULT; // percent
11067 si->touch.transparency = TOUCH_TRANSPARENCY_DEFAULT; // percent
11068 si->touch.draw_outlined = TRUE;
11069 si->touch.draw_pressed = TRUE;
11071 for (i = 0; i < 2; i++)
11073 char *default_grid_button[6][2] =
11079 { "111222", " vv " },
11080 { "111222", " vv " }
11082 int grid_xsize = DEFAULT_GRID_XSIZE(i);
11083 int grid_ysize = DEFAULT_GRID_YSIZE(i);
11084 int min_xsize = MIN(6, grid_xsize);
11085 int min_ysize = MIN(6, grid_ysize);
11086 int startx = grid_xsize - min_xsize;
11087 int starty = grid_ysize - min_ysize;
11090 // virtual buttons grid can only be set to defaults if video is initialized
11091 // (this will be repeated if virtual buttons are not loaded from setup file)
11092 if (video.initialized)
11094 si->touch.grid_xsize[i] = grid_xsize;
11095 si->touch.grid_ysize[i] = grid_ysize;
11099 si->touch.grid_xsize[i] = -1;
11100 si->touch.grid_ysize[i] = -1;
11103 for (x = 0; x < MAX_GRID_XSIZE; x++)
11104 for (y = 0; y < MAX_GRID_YSIZE; y++)
11105 si->touch.grid_button[i][x][y] = CHAR_GRID_BUTTON_NONE;
11107 for (x = 0; x < min_xsize; x++)
11108 for (y = 0; y < min_ysize; y++)
11109 si->touch.grid_button[i][x][starty + y] =
11110 default_grid_button[y][0][x];
11112 for (x = 0; x < min_xsize; x++)
11113 for (y = 0; y < min_ysize; y++)
11114 si->touch.grid_button[i][startx + x][starty + y] =
11115 default_grid_button[y][1][x];
11118 si->touch.grid_initialized = video.initialized;
11120 si->touch.overlay_buttons = FALSE;
11122 si->editor.el_boulderdash = TRUE;
11123 si->editor.el_boulderdash_native = TRUE;
11124 si->editor.el_emerald_mine = TRUE;
11125 si->editor.el_emerald_mine_club = TRUE;
11126 si->editor.el_more = TRUE;
11127 si->editor.el_sokoban = TRUE;
11128 si->editor.el_supaplex = TRUE;
11129 si->editor.el_diamond_caves = TRUE;
11130 si->editor.el_dx_boulderdash = TRUE;
11132 si->editor.el_mirror_magic = TRUE;
11133 si->editor.el_deflektor = TRUE;
11135 si->editor.el_chars = TRUE;
11136 si->editor.el_steel_chars = TRUE;
11138 si->editor.el_classic = TRUE;
11139 si->editor.el_custom = TRUE;
11141 si->editor.el_user_defined = FALSE;
11142 si->editor.el_dynamic = TRUE;
11144 si->editor.el_headlines = TRUE;
11146 si->editor.show_element_token = FALSE;
11148 si->editor.show_read_only_warning = TRUE;
11150 si->editor.use_template_for_new_levels = TRUE;
11152 si->shortcut.save_game = DEFAULT_KEY_SAVE_GAME;
11153 si->shortcut.load_game = DEFAULT_KEY_LOAD_GAME;
11154 si->shortcut.restart_game = DEFAULT_KEY_RESTART_GAME;
11155 si->shortcut.pause_before_end = DEFAULT_KEY_PAUSE_BEFORE_END;
11156 si->shortcut.toggle_pause = DEFAULT_KEY_TOGGLE_PAUSE;
11158 si->shortcut.focus_player[0] = DEFAULT_KEY_FOCUS_PLAYER_1;
11159 si->shortcut.focus_player[1] = DEFAULT_KEY_FOCUS_PLAYER_2;
11160 si->shortcut.focus_player[2] = DEFAULT_KEY_FOCUS_PLAYER_3;
11161 si->shortcut.focus_player[3] = DEFAULT_KEY_FOCUS_PLAYER_4;
11162 si->shortcut.focus_player_all = DEFAULT_KEY_FOCUS_PLAYER_ALL;
11164 si->shortcut.tape_eject = DEFAULT_KEY_TAPE_EJECT;
11165 si->shortcut.tape_extra = DEFAULT_KEY_TAPE_EXTRA;
11166 si->shortcut.tape_stop = DEFAULT_KEY_TAPE_STOP;
11167 si->shortcut.tape_pause = DEFAULT_KEY_TAPE_PAUSE;
11168 si->shortcut.tape_record = DEFAULT_KEY_TAPE_RECORD;
11169 si->shortcut.tape_play = DEFAULT_KEY_TAPE_PLAY;
11171 si->shortcut.sound_simple = DEFAULT_KEY_SOUND_SIMPLE;
11172 si->shortcut.sound_loops = DEFAULT_KEY_SOUND_LOOPS;
11173 si->shortcut.sound_music = DEFAULT_KEY_SOUND_MUSIC;
11175 si->shortcut.snap_left = DEFAULT_KEY_SNAP_LEFT;
11176 si->shortcut.snap_right = DEFAULT_KEY_SNAP_RIGHT;
11177 si->shortcut.snap_up = DEFAULT_KEY_SNAP_UP;
11178 si->shortcut.snap_down = DEFAULT_KEY_SNAP_DOWN;
11180 for (i = 0; i < MAX_PLAYERS; i++)
11182 si->input[i].use_joystick = FALSE;
11183 si->input[i].joy.device_name = getStringCopy(getDeviceNameFromJoystickNr(i));
11184 si->input[i].joy.xleft = JOYSTICK_XLEFT;
11185 si->input[i].joy.xmiddle = JOYSTICK_XMIDDLE;
11186 si->input[i].joy.xright = JOYSTICK_XRIGHT;
11187 si->input[i].joy.yupper = JOYSTICK_YUPPER;
11188 si->input[i].joy.ymiddle = JOYSTICK_YMIDDLE;
11189 si->input[i].joy.ylower = JOYSTICK_YLOWER;
11190 si->input[i].joy.snap = (i == 0 ? JOY_BUTTON_1 : 0);
11191 si->input[i].joy.drop = (i == 0 ? JOY_BUTTON_2 : 0);
11192 si->input[i].key.left = (i == 0 ? DEFAULT_KEY_LEFT : KSYM_UNDEFINED);
11193 si->input[i].key.right = (i == 0 ? DEFAULT_KEY_RIGHT : KSYM_UNDEFINED);
11194 si->input[i].key.up = (i == 0 ? DEFAULT_KEY_UP : KSYM_UNDEFINED);
11195 si->input[i].key.down = (i == 0 ? DEFAULT_KEY_DOWN : KSYM_UNDEFINED);
11196 si->input[i].key.snap = (i == 0 ? DEFAULT_KEY_SNAP : KSYM_UNDEFINED);
11197 si->input[i].key.drop = (i == 0 ? DEFAULT_KEY_DROP : KSYM_UNDEFINED);
11200 si->system.sdl_renderdriver = getStringCopy(ARG_DEFAULT);
11201 si->system.sdl_videodriver = getStringCopy(ARG_DEFAULT);
11202 si->system.sdl_audiodriver = getStringCopy(ARG_DEFAULT);
11203 si->system.audio_fragment_size = DEFAULT_AUDIO_FRAGMENT_SIZE;
11205 si->internal.program_title = getStringCopy(PROGRAM_TITLE_STRING);
11206 si->internal.program_version = getStringCopy(getProgramRealVersionString());
11207 si->internal.program_author = getStringCopy(PROGRAM_AUTHOR_STRING);
11208 si->internal.program_email = getStringCopy(PROGRAM_EMAIL_STRING);
11209 si->internal.program_website = getStringCopy(PROGRAM_WEBSITE_STRING);
11210 si->internal.program_copyright = getStringCopy(PROGRAM_COPYRIGHT_STRING);
11211 si->internal.program_company = getStringCopy(PROGRAM_COMPANY_STRING);
11213 si->internal.program_icon_file = getStringCopy(PROGRAM_ICON_FILENAME);
11215 si->internal.default_graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
11216 si->internal.default_sounds_set = getStringCopy(SND_CLASSIC_SUBDIR);
11217 si->internal.default_music_set = getStringCopy(MUS_CLASSIC_SUBDIR);
11219 si->internal.fallback_graphics_file = getStringCopy(UNDEFINED_FILENAME);
11220 si->internal.fallback_sounds_file = getStringCopy(UNDEFINED_FILENAME);
11221 si->internal.fallback_music_file = getStringCopy(UNDEFINED_FILENAME);
11223 si->internal.default_level_series = getStringCopy(UNDEFINED_LEVELSET);
11224 si->internal.choose_from_top_leveldir = FALSE;
11225 si->internal.show_scaling_in_title = TRUE;
11226 si->internal.create_user_levelset = TRUE;
11227 si->internal.info_screens_from_main = FALSE;
11229 si->internal.default_window_width = WIN_XSIZE_DEFAULT;
11230 si->internal.default_window_height = WIN_YSIZE_DEFAULT;
11232 si->debug.frame_delay[0] = DEFAULT_FRAME_DELAY_0;
11233 si->debug.frame_delay[1] = DEFAULT_FRAME_DELAY_1;
11234 si->debug.frame_delay[2] = DEFAULT_FRAME_DELAY_2;
11235 si->debug.frame_delay[3] = DEFAULT_FRAME_DELAY_3;
11236 si->debug.frame_delay[4] = DEFAULT_FRAME_DELAY_4;
11237 si->debug.frame_delay[5] = DEFAULT_FRAME_DELAY_5;
11238 si->debug.frame_delay[6] = DEFAULT_FRAME_DELAY_6;
11239 si->debug.frame_delay[7] = DEFAULT_FRAME_DELAY_7;
11240 si->debug.frame_delay[8] = DEFAULT_FRAME_DELAY_8;
11241 si->debug.frame_delay[9] = DEFAULT_FRAME_DELAY_9;
11243 si->debug.frame_delay_key[0] = DEFAULT_KEY_FRAME_DELAY_0;
11244 si->debug.frame_delay_key[1] = DEFAULT_KEY_FRAME_DELAY_1;
11245 si->debug.frame_delay_key[2] = DEFAULT_KEY_FRAME_DELAY_2;
11246 si->debug.frame_delay_key[3] = DEFAULT_KEY_FRAME_DELAY_3;
11247 si->debug.frame_delay_key[4] = DEFAULT_KEY_FRAME_DELAY_4;
11248 si->debug.frame_delay_key[5] = DEFAULT_KEY_FRAME_DELAY_5;
11249 si->debug.frame_delay_key[6] = DEFAULT_KEY_FRAME_DELAY_6;
11250 si->debug.frame_delay_key[7] = DEFAULT_KEY_FRAME_DELAY_7;
11251 si->debug.frame_delay_key[8] = DEFAULT_KEY_FRAME_DELAY_8;
11252 si->debug.frame_delay_key[9] = DEFAULT_KEY_FRAME_DELAY_9;
11254 si->debug.frame_delay_use_mod_key = DEFAULT_FRAME_DELAY_USE_MOD_KEY;
11255 si->debug.frame_delay_game_only = DEFAULT_FRAME_DELAY_GAME_ONLY;
11257 si->debug.show_frames_per_second = FALSE;
11259 si->debug.xsn_mode = AUTO;
11260 si->debug.xsn_percent = 0;
11262 si->options.verbose = FALSE;
11263 si->options.debug = FALSE;
11264 si->options.debug_mode = getStringCopy(ARG_UNDEFINED_STRING);
11266 #if defined(PLATFORM_ANDROID)
11267 si->fullscreen = TRUE;
11268 si->touch.overlay_buttons = TRUE;
11271 setHideSetupEntry(&setup.debug.xsn_mode);
11274 static void setSetupInfoToDefaults_AutoSetup(struct SetupInfo *si)
11276 si->auto_setup.editor_zoom_tilesize = MINI_TILESIZE;
11279 static void setSetupInfoToDefaults_ServerSetup(struct SetupInfo *si)
11281 si->player_uuid = NULL; // (will be set later)
11282 si->player_version = 1; // (will be set later)
11284 si->use_api_server = TRUE;
11285 si->api_server_hostname = getStringCopy(API_SERVER_HOSTNAME);
11286 si->api_server_password = getStringCopy(UNDEFINED_PASSWORD);
11287 si->ask_for_uploading_tapes = TRUE;
11288 si->ask_for_remaining_tapes = FALSE;
11289 si->provide_uploading_tapes = TRUE;
11290 si->ask_for_using_api_server = TRUE;
11291 si->has_remaining_tapes = FALSE;
11294 static void setSetupInfoToDefaults_EditorCascade(struct SetupInfo *si)
11296 si->editor_cascade.el_bd = TRUE;
11297 si->editor_cascade.el_bd_native = TRUE;
11298 si->editor_cascade.el_em = TRUE;
11299 si->editor_cascade.el_emc = TRUE;
11300 si->editor_cascade.el_rnd = TRUE;
11301 si->editor_cascade.el_sb = TRUE;
11302 si->editor_cascade.el_sp = TRUE;
11303 si->editor_cascade.el_dc = TRUE;
11304 si->editor_cascade.el_dx = TRUE;
11306 si->editor_cascade.el_mm = TRUE;
11307 si->editor_cascade.el_df = TRUE;
11309 si->editor_cascade.el_chars = FALSE;
11310 si->editor_cascade.el_steel_chars = FALSE;
11311 si->editor_cascade.el_ce = FALSE;
11312 si->editor_cascade.el_ge = FALSE;
11313 si->editor_cascade.el_es = FALSE;
11314 si->editor_cascade.el_ref = FALSE;
11315 si->editor_cascade.el_user = FALSE;
11316 si->editor_cascade.el_dynamic = FALSE;
11319 #define MAX_HIDE_SETUP_TOKEN_SIZE 20
11321 static char *getHideSetupToken(void *setup_value)
11323 static char hide_setup_token[MAX_HIDE_SETUP_TOKEN_SIZE];
11325 if (setup_value != NULL)
11326 snprintf(hide_setup_token, MAX_HIDE_SETUP_TOKEN_SIZE, "%p", setup_value);
11328 return hide_setup_token;
11331 void setHideSetupEntry(void *setup_value)
11333 char *hide_setup_token = getHideSetupToken(setup_value);
11335 if (hide_setup_hash == NULL)
11336 hide_setup_hash = newSetupFileHash();
11338 if (setup_value != NULL)
11339 setHashEntry(hide_setup_hash, hide_setup_token, "");
11342 void removeHideSetupEntry(void *setup_value)
11344 char *hide_setup_token = getHideSetupToken(setup_value);
11346 if (setup_value != NULL)
11347 removeHashEntry(hide_setup_hash, hide_setup_token);
11350 boolean hideSetupEntry(void *setup_value)
11352 char *hide_setup_token = getHideSetupToken(setup_value);
11354 return (setup_value != NULL &&
11355 getHashEntry(hide_setup_hash, hide_setup_token) != NULL);
11358 static void setSetupInfoFromTokenText(SetupFileHash *setup_file_hash,
11359 struct TokenInfo *token_info,
11360 int token_nr, char *token_text)
11362 char *token_hide_text = getStringCat2(token_text, ".hide");
11363 char *token_hide_value = getHashEntry(setup_file_hash, token_hide_text);
11365 // set the value of this setup option in the setup option structure
11366 setSetupInfo(token_info, token_nr, getHashEntry(setup_file_hash, token_text));
11368 // check if this setup option should be hidden in the setup menu
11369 if (token_hide_value != NULL && get_boolean_from_string(token_hide_value))
11370 setHideSetupEntry(token_info[token_nr].value);
11372 free(token_hide_text);
11375 static void setSetupInfoFromTokenInfo(SetupFileHash *setup_file_hash,
11376 struct TokenInfo *token_info,
11379 setSetupInfoFromTokenText(setup_file_hash, token_info, token_nr,
11380 token_info[token_nr].text);
11383 static void decodeSetupFileHash_Default(SetupFileHash *setup_file_hash)
11387 if (!setup_file_hash)
11390 for (i = 0; i < ARRAY_SIZE(global_setup_tokens); i++)
11391 setSetupInfoFromTokenInfo(setup_file_hash, global_setup_tokens, i);
11393 setup.touch.grid_initialized = TRUE;
11394 for (i = 0; i < 2; i++)
11396 int grid_xsize = setup.touch.grid_xsize[i];
11397 int grid_ysize = setup.touch.grid_ysize[i];
11400 // if virtual buttons are not loaded from setup file, repeat initializing
11401 // virtual buttons grid with default values later when video is initialized
11402 if (grid_xsize == -1 ||
11405 setup.touch.grid_initialized = FALSE;
11410 for (y = 0; y < grid_ysize; y++)
11412 char token_string[MAX_LINE_LEN];
11414 sprintf(token_string, "touch.virtual_buttons.%d.%02d", i, y);
11416 char *value_string = getHashEntry(setup_file_hash, token_string);
11418 if (value_string == NULL)
11421 for (x = 0; x < grid_xsize; x++)
11423 char c = value_string[x];
11425 setup.touch.grid_button[i][x][y] =
11426 (c == '.' ? CHAR_GRID_BUTTON_NONE : c);
11431 for (i = 0; i < ARRAY_SIZE(editor_setup_tokens); i++)
11432 setSetupInfoFromTokenInfo(setup_file_hash, editor_setup_tokens, i);
11434 for (i = 0; i < ARRAY_SIZE(shortcut_setup_tokens); i++)
11435 setSetupInfoFromTokenInfo(setup_file_hash, shortcut_setup_tokens, i);
11437 for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
11441 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
11443 setup_input = setup.input[pnr];
11444 for (i = 0; i < ARRAY_SIZE(player_setup_tokens); i++)
11446 char full_token[100];
11448 sprintf(full_token, "%s%s", prefix, player_setup_tokens[i].text);
11449 setSetupInfoFromTokenText(setup_file_hash, player_setup_tokens, i,
11452 setup.input[pnr] = setup_input;
11455 for (i = 0; i < ARRAY_SIZE(system_setup_tokens); i++)
11456 setSetupInfoFromTokenInfo(setup_file_hash, system_setup_tokens, i);
11458 for (i = 0; i < ARRAY_SIZE(internal_setup_tokens); i++)
11459 setSetupInfoFromTokenInfo(setup_file_hash, internal_setup_tokens, i);
11461 for (i = 0; i < ARRAY_SIZE(debug_setup_tokens); i++)
11462 setSetupInfoFromTokenInfo(setup_file_hash, debug_setup_tokens, i);
11464 for (i = 0; i < ARRAY_SIZE(options_setup_tokens); i++)
11465 setSetupInfoFromTokenInfo(setup_file_hash, options_setup_tokens, i);
11467 setHideRelatedSetupEntries();
11470 static void decodeSetupFileHash_AutoSetup(SetupFileHash *setup_file_hash)
11474 if (!setup_file_hash)
11477 for (i = 0; i < ARRAY_SIZE(auto_setup_tokens); i++)
11478 setSetupInfo(auto_setup_tokens, i,
11479 getHashEntry(setup_file_hash,
11480 auto_setup_tokens[i].text));
11483 static void decodeSetupFileHash_ServerSetup(SetupFileHash *setup_file_hash)
11487 if (!setup_file_hash)
11490 for (i = 0; i < ARRAY_SIZE(server_setup_tokens); i++)
11491 setSetupInfo(server_setup_tokens, i,
11492 getHashEntry(setup_file_hash,
11493 server_setup_tokens[i].text));
11496 static void decodeSetupFileHash_EditorCascade(SetupFileHash *setup_file_hash)
11500 if (!setup_file_hash)
11503 for (i = 0; i < ARRAY_SIZE(editor_cascade_setup_tokens); i++)
11504 setSetupInfo(editor_cascade_setup_tokens, i,
11505 getHashEntry(setup_file_hash,
11506 editor_cascade_setup_tokens[i].text));
11509 void LoadUserNames(void)
11511 int last_user_nr = user.nr;
11514 if (global.user_names != NULL)
11516 for (i = 0; i < MAX_PLAYER_NAMES; i++)
11517 checked_free(global.user_names[i]);
11519 checked_free(global.user_names);
11522 global.user_names = checked_calloc(MAX_PLAYER_NAMES * sizeof(char *));
11524 for (i = 0; i < MAX_PLAYER_NAMES; i++)
11528 SetupFileHash *setup_file_hash = loadSetupFileHash(getSetupFilename());
11530 if (setup_file_hash)
11532 char *player_name = getHashEntry(setup_file_hash, "player_name");
11534 global.user_names[i] = getFixedUserName(player_name);
11536 freeSetupFileHash(setup_file_hash);
11539 if (global.user_names[i] == NULL)
11540 global.user_names[i] = getStringCopy(getDefaultUserName(i));
11543 user.nr = last_user_nr;
11546 void LoadSetupFromFilename(char *filename)
11548 SetupFileHash *setup_file_hash = loadSetupFileHash(filename);
11550 if (setup_file_hash)
11552 decodeSetupFileHash_Default(setup_file_hash);
11554 freeSetupFileHash(setup_file_hash);
11558 Debug("setup", "using default setup values");
11562 static void LoadSetup_SpecialPostProcessing(void)
11564 char *player_name_new;
11566 // needed to work around problems with fixed length strings
11567 player_name_new = getFixedUserName(setup.player_name);
11568 free(setup.player_name);
11569 setup.player_name = player_name_new;
11571 // "scroll_delay: on(3) / off(0)" was replaced by scroll delay value
11572 if (setup.scroll_delay == FALSE)
11574 setup.scroll_delay_value = MIN_SCROLL_DELAY;
11575 setup.scroll_delay = TRUE; // now always "on"
11578 // make sure that scroll delay value stays inside valid range
11579 setup.scroll_delay_value =
11580 MIN(MAX(MIN_SCROLL_DELAY, setup.scroll_delay_value), MAX_SCROLL_DELAY);
11583 void LoadSetup_Default(void)
11587 // always start with reliable default values
11588 setSetupInfoToDefaults(&setup);
11590 // try to load setup values from default setup file
11591 filename = getDefaultSetupFilename();
11593 if (fileExists(filename))
11594 LoadSetupFromFilename(filename);
11596 // try to load setup values from platform setup file
11597 filename = getPlatformSetupFilename();
11599 if (fileExists(filename))
11600 LoadSetupFromFilename(filename);
11602 // try to load setup values from user setup file
11603 filename = getSetupFilename();
11605 LoadSetupFromFilename(filename);
11607 LoadSetup_SpecialPostProcessing();
11610 void LoadSetup_AutoSetup(void)
11612 char *filename = getPath2(getSetupDir(), AUTOSETUP_FILENAME);
11613 SetupFileHash *setup_file_hash = NULL;
11615 // always start with reliable default values
11616 setSetupInfoToDefaults_AutoSetup(&setup);
11618 setup_file_hash = loadSetupFileHash(filename);
11620 if (setup_file_hash)
11622 decodeSetupFileHash_AutoSetup(setup_file_hash);
11624 freeSetupFileHash(setup_file_hash);
11630 void LoadSetup_ServerSetup(void)
11632 char *filename = getPath2(getSetupDir(), SERVERSETUP_FILENAME);
11633 SetupFileHash *setup_file_hash = NULL;
11635 // always start with reliable default values
11636 setSetupInfoToDefaults_ServerSetup(&setup);
11638 setup_file_hash = loadSetupFileHash(filename);
11640 if (setup_file_hash)
11642 decodeSetupFileHash_ServerSetup(setup_file_hash);
11644 freeSetupFileHash(setup_file_hash);
11649 if (setup.player_uuid == NULL)
11651 // player UUID does not yet exist in setup file
11652 setup.player_uuid = getStringCopy(getUUID());
11653 setup.player_version = 2;
11655 SaveSetup_ServerSetup();
11659 void LoadSetup_EditorCascade(void)
11661 char *filename = getPath2(getSetupDir(), EDITORCASCADE_FILENAME);
11662 SetupFileHash *setup_file_hash = NULL;
11664 // always start with reliable default values
11665 setSetupInfoToDefaults_EditorCascade(&setup);
11667 setup_file_hash = loadSetupFileHash(filename);
11669 if (setup_file_hash)
11671 decodeSetupFileHash_EditorCascade(setup_file_hash);
11673 freeSetupFileHash(setup_file_hash);
11679 void LoadSetup(void)
11681 LoadSetup_Default();
11682 LoadSetup_AutoSetup();
11683 LoadSetup_ServerSetup();
11684 LoadSetup_EditorCascade();
11687 static void addGameControllerMappingToHash(SetupFileHash *mappings_hash,
11688 char *mapping_line)
11690 char mapping_guid[MAX_LINE_LEN];
11691 char *mapping_start, *mapping_end;
11693 // get GUID from game controller mapping line: copy complete line
11694 strncpy(mapping_guid, mapping_line, MAX_LINE_LEN - 1);
11695 mapping_guid[MAX_LINE_LEN - 1] = '\0';
11697 // get GUID from game controller mapping line: cut after GUID part
11698 mapping_start = strchr(mapping_guid, ',');
11699 if (mapping_start != NULL)
11700 *mapping_start = '\0';
11702 // cut newline from game controller mapping line
11703 mapping_end = strchr(mapping_line, '\n');
11704 if (mapping_end != NULL)
11705 *mapping_end = '\0';
11707 // add mapping entry to game controller mappings hash
11708 setHashEntry(mappings_hash, mapping_guid, mapping_line);
11711 static void LoadSetup_ReadGameControllerMappings(SetupFileHash *mappings_hash,
11716 if (!(file = fopen(filename, MODE_READ)))
11718 Warn("cannot read game controller mappings file '%s'", filename);
11723 while (!feof(file))
11725 char line[MAX_LINE_LEN];
11727 if (!fgets(line, MAX_LINE_LEN, file))
11730 addGameControllerMappingToHash(mappings_hash, line);
11736 void SaveSetup_Default(void)
11738 char *filename = getSetupFilename();
11742 InitUserDataDirectory();
11744 if (!(file = fopen(filename, MODE_WRITE)))
11746 Warn("cannot write setup file '%s'", filename);
11751 fprintFileHeader(file, SETUP_FILENAME);
11753 for (i = 0; i < ARRAY_SIZE(global_setup_tokens); i++)
11755 // just to make things nicer :)
11756 if (global_setup_tokens[i].value == &setup.multiple_users ||
11757 global_setup_tokens[i].value == &setup.sound ||
11758 global_setup_tokens[i].value == &setup.graphics_set ||
11759 global_setup_tokens[i].value == &setup.volume_simple ||
11760 global_setup_tokens[i].value == &setup.network_mode ||
11761 global_setup_tokens[i].value == &setup.touch.control_type ||
11762 global_setup_tokens[i].value == &setup.touch.grid_xsize[0] ||
11763 global_setup_tokens[i].value == &setup.touch.grid_xsize[1])
11764 fprintf(file, "\n");
11766 fprintf(file, "%s\n", getSetupLine(global_setup_tokens, "", i));
11769 for (i = 0; i < 2; i++)
11771 int grid_xsize = setup.touch.grid_xsize[i];
11772 int grid_ysize = setup.touch.grid_ysize[i];
11775 fprintf(file, "\n");
11777 for (y = 0; y < grid_ysize; y++)
11779 char token_string[MAX_LINE_LEN];
11780 char value_string[MAX_LINE_LEN];
11782 sprintf(token_string, "touch.virtual_buttons.%d.%02d", i, y);
11784 for (x = 0; x < grid_xsize; x++)
11786 char c = setup.touch.grid_button[i][x][y];
11788 value_string[x] = (c == CHAR_GRID_BUTTON_NONE ? '.' : c);
11791 value_string[grid_xsize] = '\0';
11793 fprintf(file, "%s\n", getFormattedSetupEntry(token_string, value_string));
11797 fprintf(file, "\n");
11798 for (i = 0; i < ARRAY_SIZE(editor_setup_tokens); i++)
11799 fprintf(file, "%s\n", getSetupLine(editor_setup_tokens, "", i));
11801 fprintf(file, "\n");
11802 for (i = 0; i < ARRAY_SIZE(shortcut_setup_tokens); i++)
11803 fprintf(file, "%s\n", getSetupLine(shortcut_setup_tokens, "", i));
11805 for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
11809 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
11810 fprintf(file, "\n");
11812 setup_input = setup.input[pnr];
11813 for (i = 0; i < ARRAY_SIZE(player_setup_tokens); i++)
11814 fprintf(file, "%s\n", getSetupLine(player_setup_tokens, prefix, i));
11817 fprintf(file, "\n");
11818 for (i = 0; i < ARRAY_SIZE(system_setup_tokens); i++)
11819 fprintf(file, "%s\n", getSetupLine(system_setup_tokens, "", i));
11821 // (internal setup values not saved to user setup file)
11823 fprintf(file, "\n");
11824 for (i = 0; i < ARRAY_SIZE(debug_setup_tokens); i++)
11825 if (!strPrefix(debug_setup_tokens[i].text, "debug.xsn_") ||
11826 setup.debug.xsn_mode != AUTO)
11827 fprintf(file, "%s\n", getSetupLine(debug_setup_tokens, "", i));
11829 fprintf(file, "\n");
11830 for (i = 0; i < ARRAY_SIZE(options_setup_tokens); i++)
11831 fprintf(file, "%s\n", getSetupLine(options_setup_tokens, "", i));
11835 SetFilePermissions(filename, PERMS_PRIVATE);
11838 void SaveSetup_AutoSetup(void)
11840 char *filename = getPath2(getSetupDir(), AUTOSETUP_FILENAME);
11844 InitUserDataDirectory();
11846 if (!(file = fopen(filename, MODE_WRITE)))
11848 Warn("cannot write auto setup file '%s'", filename);
11855 fprintFileHeader(file, AUTOSETUP_FILENAME);
11857 for (i = 0; i < ARRAY_SIZE(auto_setup_tokens); i++)
11858 fprintf(file, "%s\n", getSetupLine(auto_setup_tokens, "", i));
11862 SetFilePermissions(filename, PERMS_PRIVATE);
11867 void SaveSetup_ServerSetup(void)
11869 char *filename = getPath2(getSetupDir(), SERVERSETUP_FILENAME);
11873 InitUserDataDirectory();
11875 if (!(file = fopen(filename, MODE_WRITE)))
11877 Warn("cannot write server setup file '%s'", filename);
11884 fprintFileHeader(file, SERVERSETUP_FILENAME);
11886 for (i = 0; i < ARRAY_SIZE(server_setup_tokens); i++)
11888 // just to make things nicer :)
11889 if (server_setup_tokens[i].value == &setup.use_api_server)
11890 fprintf(file, "\n");
11892 fprintf(file, "%s\n", getSetupLine(server_setup_tokens, "", i));
11897 SetFilePermissions(filename, PERMS_PRIVATE);
11902 void SaveSetup_EditorCascade(void)
11904 char *filename = getPath2(getSetupDir(), EDITORCASCADE_FILENAME);
11908 InitUserDataDirectory();
11910 if (!(file = fopen(filename, MODE_WRITE)))
11912 Warn("cannot write editor cascade state file '%s'", filename);
11919 fprintFileHeader(file, EDITORCASCADE_FILENAME);
11921 for (i = 0; i < ARRAY_SIZE(editor_cascade_setup_tokens); i++)
11922 fprintf(file, "%s\n", getSetupLine(editor_cascade_setup_tokens, "", i));
11926 SetFilePermissions(filename, PERMS_PRIVATE);
11931 void SaveSetup(void)
11933 SaveSetup_Default();
11934 SaveSetup_AutoSetup();
11935 SaveSetup_ServerSetup();
11936 SaveSetup_EditorCascade();
11939 static void SaveSetup_WriteGameControllerMappings(SetupFileHash *mappings_hash,
11944 if (!(file = fopen(filename, MODE_WRITE)))
11946 Warn("cannot write game controller mappings file '%s'", filename);
11951 BEGIN_HASH_ITERATION(mappings_hash, itr)
11953 fprintf(file, "%s\n", HASH_ITERATION_VALUE(itr));
11955 END_HASH_ITERATION(mappings_hash, itr)
11960 void SaveSetup_AddGameControllerMapping(char *mapping)
11962 char *filename = getPath2(getSetupDir(), GAMECONTROLLER_BASENAME);
11963 SetupFileHash *mappings_hash = newSetupFileHash();
11965 InitUserDataDirectory();
11967 // load existing personal game controller mappings
11968 LoadSetup_ReadGameControllerMappings(mappings_hash, filename);
11970 // add new mapping to personal game controller mappings
11971 addGameControllerMappingToHash(mappings_hash, mapping);
11973 // save updated personal game controller mappings
11974 SaveSetup_WriteGameControllerMappings(mappings_hash, filename);
11976 freeSetupFileHash(mappings_hash);
11980 void LoadCustomElementDescriptions(void)
11982 char *filename = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
11983 SetupFileHash *setup_file_hash;
11986 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
11988 if (element_info[i].custom_description != NULL)
11990 free(element_info[i].custom_description);
11991 element_info[i].custom_description = NULL;
11995 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
11998 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
12000 char *token = getStringCat2(element_info[i].token_name, ".name");
12001 char *value = getHashEntry(setup_file_hash, token);
12004 element_info[i].custom_description = getStringCopy(value);
12009 freeSetupFileHash(setup_file_hash);
12012 static int getElementFromToken(char *token)
12014 char *value = getHashEntry(element_token_hash, token);
12017 return atoi(value);
12019 Warn("unknown element token '%s'", token);
12021 return EL_UNDEFINED;
12024 void FreeGlobalAnimEventInfo(void)
12026 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
12028 if (gaei->event_list == NULL)
12033 for (i = 0; i < gaei->num_event_lists; i++)
12035 checked_free(gaei->event_list[i]->event_value);
12036 checked_free(gaei->event_list[i]);
12039 checked_free(gaei->event_list);
12041 gaei->event_list = NULL;
12042 gaei->num_event_lists = 0;
12045 static int AddGlobalAnimEventList(void)
12047 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
12048 int list_pos = gaei->num_event_lists++;
12050 gaei->event_list = checked_realloc(gaei->event_list, gaei->num_event_lists *
12051 sizeof(struct GlobalAnimEventListInfo *));
12053 gaei->event_list[list_pos] =
12054 checked_calloc(sizeof(struct GlobalAnimEventListInfo));
12056 struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
12058 gaeli->event_value = NULL;
12059 gaeli->num_event_values = 0;
12064 static int AddGlobalAnimEventValue(int list_pos, int event_value)
12066 // do not add empty global animation events
12067 if (event_value == ANIM_EVENT_NONE)
12070 // if list position is undefined, create new list
12071 if (list_pos == ANIM_EVENT_UNDEFINED)
12072 list_pos = AddGlobalAnimEventList();
12074 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
12075 struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
12076 int value_pos = gaeli->num_event_values++;
12078 gaeli->event_value = checked_realloc(gaeli->event_value,
12079 gaeli->num_event_values * sizeof(int *));
12081 gaeli->event_value[value_pos] = event_value;
12086 int GetGlobalAnimEventValue(int list_pos, int value_pos)
12088 if (list_pos == ANIM_EVENT_UNDEFINED)
12089 return ANIM_EVENT_NONE;
12091 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
12092 struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
12094 return gaeli->event_value[value_pos];
12097 int GetGlobalAnimEventValueCount(int list_pos)
12099 if (list_pos == ANIM_EVENT_UNDEFINED)
12102 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
12103 struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
12105 return gaeli->num_event_values;
12108 // This function checks if a string <s> of the format "string1, string2, ..."
12109 // exactly contains a string <s_contained>.
12111 static boolean string_has_parameter(char *s, char *s_contained)
12115 if (s == NULL || s_contained == NULL)
12118 if (strlen(s_contained) > strlen(s))
12121 if (strncmp(s, s_contained, strlen(s_contained)) == 0)
12123 char next_char = s[strlen(s_contained)];
12125 // check if next character is delimiter or whitespace
12126 if (next_char == ',' || next_char == '\0' ||
12127 next_char == ' ' || next_char == '\t')
12131 // check if string contains another parameter string after a comma
12132 substring = strchr(s, ',');
12133 if (substring == NULL) // string does not contain a comma
12136 // advance string pointer to next character after the comma
12139 // skip potential whitespaces after the comma
12140 while (*substring == ' ' || *substring == '\t')
12143 return string_has_parameter(substring, s_contained);
12146 static int get_anim_parameter_value_ce(char *s)
12149 char *pattern_1 = "ce_change:custom_";
12150 char *pattern_2 = ".page_";
12151 int pattern_1_len = strlen(pattern_1);
12152 char *matching_char = strstr(s_ptr, pattern_1);
12153 int result = ANIM_EVENT_NONE;
12155 if (matching_char == NULL)
12156 return ANIM_EVENT_NONE;
12158 result = ANIM_EVENT_CE_CHANGE;
12160 s_ptr = matching_char + pattern_1_len;
12162 // check for custom element number ("custom_X", "custom_XX" or "custom_XXX")
12163 if (*s_ptr >= '0' && *s_ptr <= '9')
12165 int gic_ce_nr = (*s_ptr++ - '0');
12167 if (*s_ptr >= '0' && *s_ptr <= '9')
12169 gic_ce_nr = 10 * gic_ce_nr + (*s_ptr++ - '0');
12171 if (*s_ptr >= '0' && *s_ptr <= '9')
12172 gic_ce_nr = 10 * gic_ce_nr + (*s_ptr++ - '0');
12175 if (gic_ce_nr < 1 || gic_ce_nr > NUM_CUSTOM_ELEMENTS)
12176 return ANIM_EVENT_NONE;
12178 // custom element stored as 0 to 255
12181 result |= gic_ce_nr << ANIM_EVENT_CE_BIT;
12185 // invalid custom element number specified
12187 return ANIM_EVENT_NONE;
12190 // check for change page number ("page_X" or "page_XX") (optional)
12191 if (strPrefix(s_ptr, pattern_2))
12193 s_ptr += strlen(pattern_2);
12195 if (*s_ptr >= '0' && *s_ptr <= '9')
12197 int gic_page_nr = (*s_ptr++ - '0');
12199 if (*s_ptr >= '0' && *s_ptr <= '9')
12200 gic_page_nr = 10 * gic_page_nr + (*s_ptr++ - '0');
12202 if (gic_page_nr < 1 || gic_page_nr > MAX_CHANGE_PAGES)
12203 return ANIM_EVENT_NONE;
12205 // change page stored as 1 to 32 (0 means "all change pages")
12207 result |= gic_page_nr << ANIM_EVENT_PAGE_BIT;
12211 // invalid animation part number specified
12213 return ANIM_EVENT_NONE;
12217 // discard result if next character is neither delimiter nor whitespace
12218 if (!(*s_ptr == ',' || *s_ptr == '\0' ||
12219 *s_ptr == ' ' || *s_ptr == '\t'))
12220 return ANIM_EVENT_NONE;
12225 static int get_anim_parameter_value(char *s)
12227 int event_value[] =
12235 char *pattern_1[] =
12243 char *pattern_2 = ".part_";
12244 char *matching_char = NULL;
12246 int pattern_1_len = 0;
12247 int result = ANIM_EVENT_NONE;
12250 result = get_anim_parameter_value_ce(s);
12252 if (result != ANIM_EVENT_NONE)
12255 for (i = 0; i < ARRAY_SIZE(event_value); i++)
12257 matching_char = strstr(s_ptr, pattern_1[i]);
12258 pattern_1_len = strlen(pattern_1[i]);
12259 result = event_value[i];
12261 if (matching_char != NULL)
12265 if (matching_char == NULL)
12266 return ANIM_EVENT_NONE;
12268 s_ptr = matching_char + pattern_1_len;
12270 // check for main animation number ("anim_X" or "anim_XX")
12271 if (*s_ptr >= '0' && *s_ptr <= '9')
12273 int gic_anim_nr = (*s_ptr++ - '0');
12275 if (*s_ptr >= '0' && *s_ptr <= '9')
12276 gic_anim_nr = 10 * gic_anim_nr + (*s_ptr++ - '0');
12278 if (gic_anim_nr < 1 || gic_anim_nr > MAX_GLOBAL_ANIMS)
12279 return ANIM_EVENT_NONE;
12281 result |= gic_anim_nr << ANIM_EVENT_ANIM_BIT;
12285 // invalid main animation number specified
12287 return ANIM_EVENT_NONE;
12290 // check for animation part number ("part_X" or "part_XX") (optional)
12291 if (strPrefix(s_ptr, pattern_2))
12293 s_ptr += strlen(pattern_2);
12295 if (*s_ptr >= '0' && *s_ptr <= '9')
12297 int gic_part_nr = (*s_ptr++ - '0');
12299 if (*s_ptr >= '0' && *s_ptr <= '9')
12300 gic_part_nr = 10 * gic_part_nr + (*s_ptr++ - '0');
12302 if (gic_part_nr < 1 || gic_part_nr > MAX_GLOBAL_ANIM_PARTS)
12303 return ANIM_EVENT_NONE;
12305 result |= gic_part_nr << ANIM_EVENT_PART_BIT;
12309 // invalid animation part number specified
12311 return ANIM_EVENT_NONE;
12315 // discard result if next character is neither delimiter nor whitespace
12316 if (!(*s_ptr == ',' || *s_ptr == '\0' ||
12317 *s_ptr == ' ' || *s_ptr == '\t'))
12318 return ANIM_EVENT_NONE;
12323 static int get_anim_parameter_values(char *s)
12325 int list_pos = ANIM_EVENT_UNDEFINED;
12326 int event_value = ANIM_EVENT_DEFAULT;
12328 if (string_has_parameter(s, "any"))
12329 event_value |= ANIM_EVENT_ANY;
12331 if (string_has_parameter(s, "click:self") ||
12332 string_has_parameter(s, "click") ||
12333 string_has_parameter(s, "self"))
12334 event_value |= ANIM_EVENT_SELF;
12336 if (string_has_parameter(s, "unclick:any"))
12337 event_value |= ANIM_EVENT_UNCLICK_ANY;
12339 // if animation event found, add it to global animation event list
12340 if (event_value != ANIM_EVENT_NONE)
12341 list_pos = AddGlobalAnimEventValue(list_pos, event_value);
12345 // add optional "click:anim_X" or "click:anim_X.part_X" parameter
12346 event_value = get_anim_parameter_value(s);
12348 // if animation event found, add it to global animation event list
12349 if (event_value != ANIM_EVENT_NONE)
12350 list_pos = AddGlobalAnimEventValue(list_pos, event_value);
12352 // continue with next part of the string, starting with next comma
12353 s = strchr(s + 1, ',');
12359 static int get_anim_action_parameter_value(char *token)
12361 // check most common default case first to massively speed things up
12362 if (strEqual(token, ARG_UNDEFINED))
12363 return ANIM_EVENT_ACTION_NONE;
12365 int result = getImageIDFromToken(token);
12369 char *gfx_token = getStringCat2("gfx.", token);
12371 result = getImageIDFromToken(gfx_token);
12373 checked_free(gfx_token);
12378 Key key = getKeyFromX11KeyName(token);
12380 if (key != KSYM_UNDEFINED)
12381 result = -(int)key;
12388 result = get_hash_from_string(token); // unsigned int => int
12389 result = ABS(result); // may be negative now
12390 result += (result < MAX_IMAGE_FILES ? MAX_IMAGE_FILES : 0);
12392 setHashEntry(anim_url_hash, int2str(result, 0), token);
12397 result = ANIM_EVENT_ACTION_NONE;
12402 int get_parameter_value(char *value_raw, char *suffix, int type)
12404 char *value = getStringToLower(value_raw);
12405 int result = 0; // probably a save default value
12407 if (strEqual(suffix, ".direction"))
12409 result = (strEqual(value, "left") ? MV_LEFT :
12410 strEqual(value, "right") ? MV_RIGHT :
12411 strEqual(value, "up") ? MV_UP :
12412 strEqual(value, "down") ? MV_DOWN : MV_NONE);
12414 else if (strEqual(suffix, ".position"))
12416 result = (strEqual(value, "left") ? POS_LEFT :
12417 strEqual(value, "right") ? POS_RIGHT :
12418 strEqual(value, "top") ? POS_TOP :
12419 strEqual(value, "upper") ? POS_UPPER :
12420 strEqual(value, "middle") ? POS_MIDDLE :
12421 strEqual(value, "lower") ? POS_LOWER :
12422 strEqual(value, "bottom") ? POS_BOTTOM :
12423 strEqual(value, "any") ? POS_ANY :
12424 strEqual(value, "ce") ? POS_CE :
12425 strEqual(value, "ce_trigger") ? POS_CE_TRIGGER :
12426 strEqual(value, "last") ? POS_LAST : POS_UNDEFINED);
12428 else if (strEqual(suffix, ".align"))
12430 result = (strEqual(value, "left") ? ALIGN_LEFT :
12431 strEqual(value, "right") ? ALIGN_RIGHT :
12432 strEqual(value, "center") ? ALIGN_CENTER :
12433 strEqual(value, "middle") ? ALIGN_CENTER : ALIGN_DEFAULT);
12435 else if (strEqual(suffix, ".valign"))
12437 result = (strEqual(value, "top") ? VALIGN_TOP :
12438 strEqual(value, "bottom") ? VALIGN_BOTTOM :
12439 strEqual(value, "middle") ? VALIGN_MIDDLE :
12440 strEqual(value, "center") ? VALIGN_MIDDLE : VALIGN_DEFAULT);
12442 else if (strEqual(suffix, ".anim_mode"))
12444 result = (string_has_parameter(value, "none") ? ANIM_NONE :
12445 string_has_parameter(value, "loop") ? ANIM_LOOP :
12446 string_has_parameter(value, "linear") ? ANIM_LINEAR :
12447 string_has_parameter(value, "pingpong") ? ANIM_PINGPONG :
12448 string_has_parameter(value, "pingpong2") ? ANIM_PINGPONG2 :
12449 string_has_parameter(value, "random") ? ANIM_RANDOM :
12450 string_has_parameter(value, "random_static") ? ANIM_RANDOM_STATIC :
12451 string_has_parameter(value, "ce_value") ? ANIM_CE_VALUE :
12452 string_has_parameter(value, "ce_score") ? ANIM_CE_SCORE :
12453 string_has_parameter(value, "ce_delay") ? ANIM_CE_DELAY :
12454 string_has_parameter(value, "horizontal") ? ANIM_HORIZONTAL :
12455 string_has_parameter(value, "vertical") ? ANIM_VERTICAL :
12456 string_has_parameter(value, "centered") ? ANIM_CENTERED :
12457 string_has_parameter(value, "all") ? ANIM_ALL :
12458 string_has_parameter(value, "tiled") ? ANIM_TILED :
12459 string_has_parameter(value, "level_nr") ? ANIM_LEVEL_NR :
12462 if (string_has_parameter(value, "once"))
12463 result |= ANIM_ONCE;
12465 if (string_has_parameter(value, "reverse"))
12466 result |= ANIM_REVERSE;
12468 if (string_has_parameter(value, "opaque_player"))
12469 result |= ANIM_OPAQUE_PLAYER;
12471 if (string_has_parameter(value, "static_panel"))
12472 result |= ANIM_STATIC_PANEL;
12474 else if (strEqual(suffix, ".init_event") ||
12475 strEqual(suffix, ".anim_event"))
12477 result = get_anim_parameter_values(value);
12479 else if (strEqual(suffix, ".init_delay_action") ||
12480 strEqual(suffix, ".anim_delay_action") ||
12481 strEqual(suffix, ".post_delay_action") ||
12482 strEqual(suffix, ".init_event_action") ||
12483 strEqual(suffix, ".anim_event_action"))
12485 result = get_anim_action_parameter_value(value_raw);
12487 else if (strEqual(suffix, ".class"))
12489 result = (strEqual(value, ARG_UNDEFINED) ? ARG_UNDEFINED_VALUE :
12490 get_hash_from_string(value));
12492 else if (strEqual(suffix, ".style"))
12494 result = STYLE_DEFAULT;
12496 if (string_has_parameter(value, "accurate_borders"))
12497 result |= STYLE_ACCURATE_BORDERS;
12499 if (string_has_parameter(value, "inner_corners"))
12500 result |= STYLE_INNER_CORNERS;
12502 if (string_has_parameter(value, "reverse"))
12503 result |= STYLE_REVERSE;
12505 if (string_has_parameter(value, "leftmost_position"))
12506 result |= STYLE_LEFTMOST_POSITION;
12508 if (string_has_parameter(value, "block_clicks"))
12509 result |= STYLE_BLOCK;
12511 if (string_has_parameter(value, "passthrough_clicks"))
12512 result |= STYLE_PASSTHROUGH;
12514 if (string_has_parameter(value, "multiple_actions"))
12515 result |= STYLE_MULTIPLE_ACTIONS;
12517 if (string_has_parameter(value, "consume_ce_event"))
12518 result |= STYLE_CONSUME_CE_EVENT;
12520 else if (strEqual(suffix, ".fade_mode"))
12522 result = (string_has_parameter(value, "none") ? FADE_MODE_NONE :
12523 string_has_parameter(value, "fade") ? FADE_MODE_FADE :
12524 string_has_parameter(value, "fade_in") ? FADE_MODE_FADE_IN :
12525 string_has_parameter(value, "fade_out") ? FADE_MODE_FADE_OUT :
12526 string_has_parameter(value, "crossfade") ? FADE_MODE_CROSSFADE :
12527 string_has_parameter(value, "melt") ? FADE_MODE_MELT :
12528 string_has_parameter(value, "curtain") ? FADE_MODE_CURTAIN :
12529 FADE_MODE_DEFAULT);
12531 else if (strEqual(suffix, ".auto_delay_unit"))
12533 result = (string_has_parameter(value, "ms") ? AUTO_DELAY_UNIT_MS :
12534 string_has_parameter(value, "frames") ? AUTO_DELAY_UNIT_FRAMES :
12535 AUTO_DELAY_UNIT_DEFAULT);
12537 else if (strPrefix(suffix, ".font")) // (may also be ".font_xyz")
12539 result = gfx.get_font_from_token_function(value);
12541 else // generic parameter of type integer or boolean
12543 result = (strEqual(value, ARG_UNDEFINED) ? ARG_UNDEFINED_VALUE :
12544 type == TYPE_INTEGER ? get_integer_from_string(value) :
12545 type == TYPE_BOOLEAN ? get_boolean_from_string(value) :
12546 ARG_UNDEFINED_VALUE);
12554 static int get_token_parameter_value(char *token, char *value_raw)
12558 if (token == NULL || value_raw == NULL)
12559 return ARG_UNDEFINED_VALUE;
12561 suffix = strrchr(token, '.');
12562 if (suffix == NULL)
12565 if (strEqual(suffix, ".element"))
12566 return getElementFromToken(value_raw);
12568 // !!! USE CORRECT VALUE TYPE (currently works also for TYPE_BOOLEAN) !!!
12569 return get_parameter_value(value_raw, suffix, TYPE_INTEGER);
12572 void InitMenuDesignSettings_FromHash(SetupFileHash *setup_file_hash,
12573 boolean ignore_defaults)
12577 for (i = 0; image_config_vars[i].token != NULL; i++)
12579 char *value = getHashEntry(setup_file_hash, image_config_vars[i].token);
12581 // (ignore definitions set to "[DEFAULT]" which are already initialized)
12582 if (ignore_defaults && strEqual(value, ARG_DEFAULT))
12586 *image_config_vars[i].value =
12587 get_token_parameter_value(image_config_vars[i].token, value);
12591 void InitMenuDesignSettings_Static(void)
12593 // always start with reliable default values from static default config
12594 InitMenuDesignSettings_FromHash(image_config_hash, FALSE);
12597 static void InitMenuDesignSettings_SpecialPreProcessing(void)
12601 // the following initializes hierarchical values from static configuration
12603 // special case: initialize "ARG_DEFAULT" values in static default config
12604 // (e.g., initialize "[titlemessage].fade_mode" from "[title].fade_mode")
12605 titlescreen_initial_first_default.fade_mode =
12606 title_initial_first_default.fade_mode;
12607 titlescreen_initial_first_default.fade_delay =
12608 title_initial_first_default.fade_delay;
12609 titlescreen_initial_first_default.post_delay =
12610 title_initial_first_default.post_delay;
12611 titlescreen_initial_first_default.auto_delay =
12612 title_initial_first_default.auto_delay;
12613 titlescreen_initial_first_default.auto_delay_unit =
12614 title_initial_first_default.auto_delay_unit;
12615 titlescreen_first_default.fade_mode = title_first_default.fade_mode;
12616 titlescreen_first_default.fade_delay = title_first_default.fade_delay;
12617 titlescreen_first_default.post_delay = title_first_default.post_delay;
12618 titlescreen_first_default.auto_delay = title_first_default.auto_delay;
12619 titlescreen_first_default.auto_delay_unit =
12620 title_first_default.auto_delay_unit;
12621 titlemessage_initial_first_default.fade_mode =
12622 title_initial_first_default.fade_mode;
12623 titlemessage_initial_first_default.fade_delay =
12624 title_initial_first_default.fade_delay;
12625 titlemessage_initial_first_default.post_delay =
12626 title_initial_first_default.post_delay;
12627 titlemessage_initial_first_default.auto_delay =
12628 title_initial_first_default.auto_delay;
12629 titlemessage_initial_first_default.auto_delay_unit =
12630 title_initial_first_default.auto_delay_unit;
12631 titlemessage_first_default.fade_mode = title_first_default.fade_mode;
12632 titlemessage_first_default.fade_delay = title_first_default.fade_delay;
12633 titlemessage_first_default.post_delay = title_first_default.post_delay;
12634 titlemessage_first_default.auto_delay = title_first_default.auto_delay;
12635 titlemessage_first_default.auto_delay_unit =
12636 title_first_default.auto_delay_unit;
12638 titlescreen_initial_default.fade_mode = title_initial_default.fade_mode;
12639 titlescreen_initial_default.fade_delay = title_initial_default.fade_delay;
12640 titlescreen_initial_default.post_delay = title_initial_default.post_delay;
12641 titlescreen_initial_default.auto_delay = title_initial_default.auto_delay;
12642 titlescreen_initial_default.auto_delay_unit =
12643 title_initial_default.auto_delay_unit;
12644 titlescreen_default.fade_mode = title_default.fade_mode;
12645 titlescreen_default.fade_delay = title_default.fade_delay;
12646 titlescreen_default.post_delay = title_default.post_delay;
12647 titlescreen_default.auto_delay = title_default.auto_delay;
12648 titlescreen_default.auto_delay_unit = title_default.auto_delay_unit;
12649 titlemessage_initial_default.fade_mode = title_initial_default.fade_mode;
12650 titlemessage_initial_default.fade_delay = title_initial_default.fade_delay;
12651 titlemessage_initial_default.post_delay = title_initial_default.post_delay;
12652 titlemessage_initial_default.auto_delay_unit =
12653 title_initial_default.auto_delay_unit;
12654 titlemessage_default.fade_mode = title_default.fade_mode;
12655 titlemessage_default.fade_delay = title_default.fade_delay;
12656 titlemessage_default.post_delay = title_default.post_delay;
12657 titlemessage_default.auto_delay = title_default.auto_delay;
12658 titlemessage_default.auto_delay_unit = title_default.auto_delay_unit;
12660 // special case: initialize "ARG_DEFAULT" values in static default config
12661 // (e.g., init "titlemessage_1.fade_mode" from "[titlemessage].fade_mode")
12662 for (i = 0; i < MAX_NUM_TITLE_MESSAGES; i++)
12664 titlescreen_initial_first[i] = titlescreen_initial_first_default;
12665 titlescreen_first[i] = titlescreen_first_default;
12666 titlemessage_initial_first[i] = titlemessage_initial_first_default;
12667 titlemessage_first[i] = titlemessage_first_default;
12669 titlescreen_initial[i] = titlescreen_initial_default;
12670 titlescreen[i] = titlescreen_default;
12671 titlemessage_initial[i] = titlemessage_initial_default;
12672 titlemessage[i] = titlemessage_default;
12675 // special case: initialize "ARG_DEFAULT" values in static default config
12676 // (eg, init "menu.enter_screen.SCORES.xyz" from "menu.enter_screen.xyz")
12677 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
12679 if (i == GFX_SPECIAL_ARG_TITLE) // title values already initialized
12682 menu.enter_screen[i] = menu.enter_screen[GFX_SPECIAL_ARG_DEFAULT];
12683 menu.leave_screen[i] = menu.leave_screen[GFX_SPECIAL_ARG_DEFAULT];
12684 menu.next_screen[i] = menu.next_screen[GFX_SPECIAL_ARG_DEFAULT];
12687 // special case: initialize "ARG_DEFAULT" values in static default config
12688 // (eg, init "viewport.door_1.MAIN.xyz" from "viewport.door_1.xyz")
12689 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
12691 viewport.window[i] = viewport.window[GFX_SPECIAL_ARG_DEFAULT];
12692 viewport.playfield[i] = viewport.playfield[GFX_SPECIAL_ARG_DEFAULT];
12693 viewport.door_1[i] = viewport.door_1[GFX_SPECIAL_ARG_DEFAULT];
12695 if (i == GFX_SPECIAL_ARG_EDITOR) // editor values already initialized
12698 viewport.door_2[i] = viewport.door_2[GFX_SPECIAL_ARG_DEFAULT];
12702 static void InitMenuDesignSettings_SpecialPostProcessing(void)
12706 struct XY *dst, *src;
12708 game_buttons_xy[] =
12710 { &game.button.save, &game.button.stop },
12711 { &game.button.pause2, &game.button.pause },
12712 { &game.button.load, &game.button.play },
12713 { &game.button.undo, &game.button.stop },
12714 { &game.button.redo, &game.button.play },
12720 // special case: initialize later added SETUP list size from LEVELS value
12721 if (menu.list_size[GAME_MODE_SETUP] == -1)
12722 menu.list_size[GAME_MODE_SETUP] = menu.list_size[GAME_MODE_LEVELS];
12724 // set default position for snapshot buttons to stop/pause/play buttons
12725 for (i = 0; game_buttons_xy[i].dst != NULL; i++)
12726 if ((*game_buttons_xy[i].dst).x == -1 &&
12727 (*game_buttons_xy[i].dst).y == -1)
12728 *game_buttons_xy[i].dst = *game_buttons_xy[i].src;
12730 // --------------------------------------------------------------------------
12731 // dynamic viewports (including playfield margins, borders and alignments)
12732 // --------------------------------------------------------------------------
12734 // dynamic viewports currently only supported for landscape mode
12735 int display_width = MAX(video.display_width, video.display_height);
12736 int display_height = MIN(video.display_width, video.display_height);
12738 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
12740 struct RectWithBorder *vp_window = &viewport.window[i];
12741 struct RectWithBorder *vp_playfield = &viewport.playfield[i];
12742 struct RectWithBorder *vp_door_1 = &viewport.door_1[i];
12743 struct RectWithBorder *vp_door_2 = &viewport.door_2[i];
12744 boolean dynamic_window_width = (vp_window->min_width != -1);
12745 boolean dynamic_window_height = (vp_window->min_height != -1);
12746 boolean dynamic_playfield_width = (vp_playfield->min_width != -1);
12747 boolean dynamic_playfield_height = (vp_playfield->min_height != -1);
12749 // adjust window size if min/max width/height is specified
12751 if (vp_window->min_width != -1)
12753 int window_width = display_width;
12755 // when using static window height, use aspect ratio of display
12756 if (vp_window->min_height == -1)
12757 window_width = vp_window->height * display_width / display_height;
12759 vp_window->width = MAX(vp_window->min_width, window_width);
12762 if (vp_window->min_height != -1)
12764 int window_height = display_height;
12766 // when using static window width, use aspect ratio of display
12767 if (vp_window->min_width == -1)
12768 window_height = vp_window->width * display_height / display_width;
12770 vp_window->height = MAX(vp_window->min_height, window_height);
12773 if (vp_window->max_width != -1)
12774 vp_window->width = MIN(vp_window->width, vp_window->max_width);
12776 if (vp_window->max_height != -1)
12777 vp_window->height = MIN(vp_window->height, vp_window->max_height);
12779 int playfield_width = vp_window->width;
12780 int playfield_height = vp_window->height;
12782 // adjust playfield size and position according to specified margins
12784 playfield_width -= vp_playfield->margin_left;
12785 playfield_width -= vp_playfield->margin_right;
12787 playfield_height -= vp_playfield->margin_top;
12788 playfield_height -= vp_playfield->margin_bottom;
12790 // adjust playfield size if min/max width/height is specified
12792 if (vp_playfield->min_width != -1)
12793 vp_playfield->width = MAX(vp_playfield->min_width, playfield_width);
12795 if (vp_playfield->min_height != -1)
12796 vp_playfield->height = MAX(vp_playfield->min_height, playfield_height);
12798 if (vp_playfield->max_width != -1)
12799 vp_playfield->width = MIN(vp_playfield->width, vp_playfield->max_width);
12801 if (vp_playfield->max_height != -1)
12802 vp_playfield->height = MIN(vp_playfield->height, vp_playfield->max_height);
12804 // adjust playfield position according to specified alignment
12806 if (vp_playfield->align == ALIGN_LEFT || vp_playfield->x > 0)
12807 vp_playfield->x = ALIGNED_VP_XPOS(vp_playfield);
12808 else if (vp_playfield->align == ALIGN_CENTER)
12809 vp_playfield->x = playfield_width / 2 - vp_playfield->width / 2;
12810 else if (vp_playfield->align == ALIGN_RIGHT)
12811 vp_playfield->x += playfield_width - vp_playfield->width;
12813 if (vp_playfield->valign == VALIGN_TOP || vp_playfield->y > 0)
12814 vp_playfield->y = ALIGNED_VP_YPOS(vp_playfield);
12815 else if (vp_playfield->valign == VALIGN_MIDDLE)
12816 vp_playfield->y = playfield_height / 2 - vp_playfield->height / 2;
12817 else if (vp_playfield->valign == VALIGN_BOTTOM)
12818 vp_playfield->y += playfield_height - vp_playfield->height;
12820 vp_playfield->x += vp_playfield->margin_left;
12821 vp_playfield->y += vp_playfield->margin_top;
12823 // adjust individual playfield borders if only default border is specified
12825 if (vp_playfield->border_left == -1)
12826 vp_playfield->border_left = vp_playfield->border_size;
12827 if (vp_playfield->border_right == -1)
12828 vp_playfield->border_right = vp_playfield->border_size;
12829 if (vp_playfield->border_top == -1)
12830 vp_playfield->border_top = vp_playfield->border_size;
12831 if (vp_playfield->border_bottom == -1)
12832 vp_playfield->border_bottom = vp_playfield->border_size;
12834 // set dynamic playfield borders if borders are specified as undefined
12835 // (but only if window size was dynamic and playfield size was static)
12837 if (dynamic_window_width && !dynamic_playfield_width)
12839 if (vp_playfield->border_left == -1)
12841 vp_playfield->border_left = (vp_playfield->x -
12842 vp_playfield->margin_left);
12843 vp_playfield->x -= vp_playfield->border_left;
12844 vp_playfield->width += vp_playfield->border_left;
12847 if (vp_playfield->border_right == -1)
12849 vp_playfield->border_right = (vp_window->width -
12851 vp_playfield->width -
12852 vp_playfield->margin_right);
12853 vp_playfield->width += vp_playfield->border_right;
12857 if (dynamic_window_height && !dynamic_playfield_height)
12859 if (vp_playfield->border_top == -1)
12861 vp_playfield->border_top = (vp_playfield->y -
12862 vp_playfield->margin_top);
12863 vp_playfield->y -= vp_playfield->border_top;
12864 vp_playfield->height += vp_playfield->border_top;
12867 if (vp_playfield->border_bottom == -1)
12869 vp_playfield->border_bottom = (vp_window->height -
12871 vp_playfield->height -
12872 vp_playfield->margin_bottom);
12873 vp_playfield->height += vp_playfield->border_bottom;
12877 // adjust playfield size to be a multiple of a defined alignment tile size
12879 int align_size = vp_playfield->align_size;
12880 int playfield_xtiles = vp_playfield->width / align_size;
12881 int playfield_ytiles = vp_playfield->height / align_size;
12882 int playfield_width_corrected = playfield_xtiles * align_size;
12883 int playfield_height_corrected = playfield_ytiles * align_size;
12884 boolean is_playfield_mode = (i == GFX_SPECIAL_ARG_PLAYING ||
12885 i == GFX_SPECIAL_ARG_EDITOR);
12887 if (is_playfield_mode &&
12888 dynamic_playfield_width &&
12889 vp_playfield->width != playfield_width_corrected)
12891 int playfield_xdiff = vp_playfield->width - playfield_width_corrected;
12893 vp_playfield->width = playfield_width_corrected;
12895 if (vp_playfield->align == ALIGN_LEFT)
12897 vp_playfield->border_left += playfield_xdiff;
12899 else if (vp_playfield->align == ALIGN_RIGHT)
12901 vp_playfield->border_right += playfield_xdiff;
12903 else if (vp_playfield->align == ALIGN_CENTER)
12905 int border_left_diff = playfield_xdiff / 2;
12906 int border_right_diff = playfield_xdiff - border_left_diff;
12908 vp_playfield->border_left += border_left_diff;
12909 vp_playfield->border_right += border_right_diff;
12913 if (is_playfield_mode &&
12914 dynamic_playfield_height &&
12915 vp_playfield->height != playfield_height_corrected)
12917 int playfield_ydiff = vp_playfield->height - playfield_height_corrected;
12919 vp_playfield->height = playfield_height_corrected;
12921 if (vp_playfield->valign == VALIGN_TOP)
12923 vp_playfield->border_top += playfield_ydiff;
12925 else if (vp_playfield->align == VALIGN_BOTTOM)
12927 vp_playfield->border_right += playfield_ydiff;
12929 else if (vp_playfield->align == VALIGN_MIDDLE)
12931 int border_top_diff = playfield_ydiff / 2;
12932 int border_bottom_diff = playfield_ydiff - border_top_diff;
12934 vp_playfield->border_top += border_top_diff;
12935 vp_playfield->border_bottom += border_bottom_diff;
12939 // adjust door positions according to specified alignment
12941 for (j = 0; j < 2; j++)
12943 struct RectWithBorder *vp_door = (j == 0 ? vp_door_1 : vp_door_2);
12945 if (vp_door->align == ALIGN_LEFT || vp_door->x > 0)
12946 vp_door->x = ALIGNED_VP_XPOS(vp_door);
12947 else if (vp_door->align == ALIGN_CENTER)
12948 vp_door->x = vp_window->width / 2 - vp_door->width / 2;
12949 else if (vp_door->align == ALIGN_RIGHT)
12950 vp_door->x += vp_window->width - vp_door->width;
12952 if (vp_door->valign == VALIGN_TOP || vp_door->y > 0)
12953 vp_door->y = ALIGNED_VP_YPOS(vp_door);
12954 else if (vp_door->valign == VALIGN_MIDDLE)
12955 vp_door->y = vp_window->height / 2 - vp_door->height / 2;
12956 else if (vp_door->valign == VALIGN_BOTTOM)
12957 vp_door->y += vp_window->height - vp_door->height;
12962 static void InitMenuDesignSettings_SpecialPostProcessing_AfterGraphics(void)
12966 struct XYTileSize *dst, *src;
12969 editor_buttons_xy[] =
12972 &editor.button.element_left, &editor.palette.element_left,
12973 IMG_GFX_EDITOR_BUTTON_ELEMENT_LEFT
12976 &editor.button.element_middle, &editor.palette.element_middle,
12977 IMG_GFX_EDITOR_BUTTON_ELEMENT_MIDDLE
12980 &editor.button.element_right, &editor.palette.element_right,
12981 IMG_GFX_EDITOR_BUTTON_ELEMENT_RIGHT
12988 // set default position for element buttons to element graphics
12989 for (i = 0; editor_buttons_xy[i].dst != NULL; i++)
12991 if ((*editor_buttons_xy[i].dst).x == -1 &&
12992 (*editor_buttons_xy[i].dst).y == -1)
12994 struct GraphicInfo *gd = &graphic_info[editor_buttons_xy[i].graphic];
12996 gd->width = gd->height = editor_buttons_xy[i].src->tile_size;
12998 *editor_buttons_xy[i].dst = *editor_buttons_xy[i].src;
13002 // adjust editor palette rows and columns if specified to be dynamic
13004 if (editor.palette.cols == -1)
13006 int vp_width = viewport.playfield[GFX_SPECIAL_ARG_EDITOR].width;
13007 int bt_width = graphic_info[IMG_EDITOR_PALETTE_BUTTON].width;
13008 int sc_width = graphic_info[IMG_EDITOR_PALETTE_SCROLLBAR].width;
13010 editor.palette.cols = (vp_width - sc_width) / bt_width;
13012 if (editor.palette.x == -1)
13014 int palette_width = editor.palette.cols * bt_width + sc_width;
13016 editor.palette.x = (vp_width - palette_width) / 2;
13020 if (editor.palette.rows == -1)
13022 int vp_height = viewport.playfield[GFX_SPECIAL_ARG_EDITOR].height;
13023 int bt_height = graphic_info[IMG_EDITOR_PALETTE_BUTTON].height;
13024 int tx_height = getFontHeight(FONT_TEXT_2);
13026 editor.palette.rows = (vp_height - tx_height) / bt_height;
13028 if (editor.palette.y == -1)
13030 int palette_height = editor.palette.rows * bt_height + tx_height;
13032 editor.palette.y = (vp_height - palette_height) / 2;
13037 static void InitMenuDesignSettings_PreviewPlayers_Ext(SetupFileHash *hash,
13038 boolean initialize)
13040 // special case: check if network and preview player positions are redefined,
13041 // to compare this later against the main menu level preview being redefined
13042 struct TokenIntPtrInfo menu_config_players[] =
13044 { "main.network_players.x", &menu.main.network_players.redefined },
13045 { "main.network_players.y", &menu.main.network_players.redefined },
13046 { "main.preview_players.x", &menu.main.preview_players.redefined },
13047 { "main.preview_players.y", &menu.main.preview_players.redefined },
13048 { "preview.x", &preview.redefined },
13049 { "preview.y", &preview.redefined }
13055 for (i = 0; i < ARRAY_SIZE(menu_config_players); i++)
13056 *menu_config_players[i].value = FALSE;
13060 for (i = 0; i < ARRAY_SIZE(menu_config_players); i++)
13061 if (getHashEntry(hash, menu_config_players[i].token) != NULL)
13062 *menu_config_players[i].value = TRUE;
13066 static void InitMenuDesignSettings_PreviewPlayers(void)
13068 InitMenuDesignSettings_PreviewPlayers_Ext(NULL, TRUE);
13071 static void InitMenuDesignSettings_PreviewPlayers_FromHash(SetupFileHash *hash)
13073 InitMenuDesignSettings_PreviewPlayers_Ext(hash, FALSE);
13076 static void LoadMenuDesignSettingsFromFilename(char *filename)
13078 static struct TitleFadingInfo tfi;
13079 static struct TitleMessageInfo tmi;
13080 static struct TokenInfo title_tokens[] =
13082 { TYPE_INTEGER, &tfi.fade_mode, ".fade_mode" },
13083 { TYPE_INTEGER, &tfi.fade_delay, ".fade_delay" },
13084 { TYPE_INTEGER, &tfi.post_delay, ".post_delay" },
13085 { TYPE_INTEGER, &tfi.auto_delay, ".auto_delay" },
13086 { TYPE_INTEGER, &tfi.auto_delay_unit, ".auto_delay_unit" },
13090 static struct TokenInfo titlemessage_tokens[] =
13092 { TYPE_INTEGER, &tmi.x, ".x" },
13093 { TYPE_INTEGER, &tmi.y, ".y" },
13094 { TYPE_INTEGER, &tmi.width, ".width" },
13095 { TYPE_INTEGER, &tmi.height, ".height" },
13096 { TYPE_INTEGER, &tmi.chars, ".chars" },
13097 { TYPE_INTEGER, &tmi.lines, ".lines" },
13098 { TYPE_INTEGER, &tmi.align, ".align" },
13099 { TYPE_INTEGER, &tmi.valign, ".valign" },
13100 { TYPE_INTEGER, &tmi.font, ".font" },
13101 { TYPE_BOOLEAN, &tmi.autowrap, ".autowrap" },
13102 { TYPE_BOOLEAN, &tmi.centered, ".centered" },
13103 { TYPE_BOOLEAN, &tmi.parse_comments, ".parse_comments" },
13104 { TYPE_INTEGER, &tmi.sort_priority, ".sort_priority" },
13105 { TYPE_INTEGER, &tmi.fade_mode, ".fade_mode" },
13106 { TYPE_INTEGER, &tmi.fade_delay, ".fade_delay" },
13107 { TYPE_INTEGER, &tmi.post_delay, ".post_delay" },
13108 { TYPE_INTEGER, &tmi.auto_delay, ".auto_delay" },
13109 { TYPE_INTEGER, &tmi.auto_delay_unit, ".auto_delay_unit" },
13115 struct TitleFadingInfo *info;
13120 // initialize first titles from "enter screen" definitions, if defined
13121 { &title_initial_first_default, "menu.enter_screen.TITLE" },
13122 { &title_first_default, "menu.enter_screen.TITLE" },
13124 // initialize title screens from "next screen" definitions, if defined
13125 { &title_initial_default, "menu.next_screen.TITLE" },
13126 { &title_default, "menu.next_screen.TITLE" },
13132 struct TitleMessageInfo *array;
13135 titlemessage_arrays[] =
13137 // initialize first titles from "enter screen" definitions, if defined
13138 { titlescreen_initial_first, "menu.enter_screen.TITLE" },
13139 { titlescreen_first, "menu.enter_screen.TITLE" },
13140 { titlemessage_initial_first, "menu.enter_screen.TITLE" },
13141 { titlemessage_first, "menu.enter_screen.TITLE" },
13143 // initialize titles from "next screen" definitions, if defined
13144 { titlescreen_initial, "menu.next_screen.TITLE" },
13145 { titlescreen, "menu.next_screen.TITLE" },
13146 { titlemessage_initial, "menu.next_screen.TITLE" },
13147 { titlemessage, "menu.next_screen.TITLE" },
13149 // overwrite titles with title definitions, if defined
13150 { titlescreen_initial_first, "[title_initial]" },
13151 { titlescreen_first, "[title]" },
13152 { titlemessage_initial_first, "[title_initial]" },
13153 { titlemessage_first, "[title]" },
13155 { titlescreen_initial, "[title_initial]" },
13156 { titlescreen, "[title]" },
13157 { titlemessage_initial, "[title_initial]" },
13158 { titlemessage, "[title]" },
13160 // overwrite titles with title screen/message definitions, if defined
13161 { titlescreen_initial_first, "[titlescreen_initial]" },
13162 { titlescreen_first, "[titlescreen]" },
13163 { titlemessage_initial_first, "[titlemessage_initial]" },
13164 { titlemessage_first, "[titlemessage]" },
13166 { titlescreen_initial, "[titlescreen_initial]" },
13167 { titlescreen, "[titlescreen]" },
13168 { titlemessage_initial, "[titlemessage_initial]" },
13169 { titlemessage, "[titlemessage]" },
13173 SetupFileHash *setup_file_hash;
13176 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
13179 // the following initializes hierarchical values from dynamic configuration
13181 // special case: initialize with default values that may be overwritten
13182 // (e.g., init "menu.draw_xoffset.INFO" from "menu.draw_xoffset")
13183 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
13185 struct TokenIntPtrInfo menu_config[] =
13187 { "menu.draw_xoffset", &menu.draw_xoffset[i] },
13188 { "menu.draw_yoffset", &menu.draw_yoffset[i] },
13189 { "menu.list_size", &menu.list_size[i] }
13192 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
13194 char *token = menu_config[j].token;
13195 char *value = getHashEntry(setup_file_hash, token);
13198 *menu_config[j].value = get_integer_from_string(value);
13202 // special case: initialize with default values that may be overwritten
13203 // (eg, init "menu.draw_xoffset.INFO[XXX]" from "menu.draw_xoffset.INFO")
13204 for (i = 0; i < NUM_SPECIAL_GFX_INFO_ARGS; i++)
13206 struct TokenIntPtrInfo menu_config[] =
13208 { "menu.draw_xoffset.INFO", &menu.draw_xoffset_info[i] },
13209 { "menu.draw_yoffset.INFO", &menu.draw_yoffset_info[i] },
13210 { "menu.list_size.INFO", &menu.list_size_info[i] },
13211 { "menu.list_entry_size.INFO", &menu.list_entry_size_info[i] },
13212 { "menu.tile_size.INFO", &menu.tile_size_info[i] }
13215 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
13217 char *token = menu_config[j].token;
13218 char *value = getHashEntry(setup_file_hash, token);
13221 *menu_config[j].value = get_integer_from_string(value);
13225 // special case: initialize with default values that may be overwritten
13226 // (eg, init "menu.draw_xoffset.SETUP[XXX]" from "menu.draw_xoffset.SETUP")
13227 for (i = 0; i < NUM_SPECIAL_GFX_SETUP_ARGS; i++)
13229 struct TokenIntPtrInfo menu_config[] =
13231 { "menu.draw_xoffset.SETUP", &menu.draw_xoffset_setup[i] },
13232 { "menu.draw_yoffset.SETUP", &menu.draw_yoffset_setup[i] }
13235 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
13237 char *token = menu_config[j].token;
13238 char *value = getHashEntry(setup_file_hash, token);
13241 *menu_config[j].value = get_integer_from_string(value);
13245 // special case: initialize with default values that may be overwritten
13246 // (eg, init "menu.line_spacing.INFO[XXX]" from "menu.line_spacing.INFO")
13247 for (i = 0; i < NUM_SPECIAL_GFX_INFO_ARGS; i++)
13249 struct TokenIntPtrInfo menu_config[] =
13251 { "menu.left_spacing.INFO", &menu.left_spacing_info[i] },
13252 { "menu.middle_spacing.INFO", &menu.middle_spacing_info[i] },
13253 { "menu.right_spacing.INFO", &menu.right_spacing_info[i] },
13254 { "menu.top_spacing.INFO", &menu.top_spacing_info[i] },
13255 { "menu.bottom_spacing.INFO", &menu.bottom_spacing_info[i] },
13256 { "menu.paragraph_spacing.INFO", &menu.paragraph_spacing_info[i] },
13257 { "menu.headline1_spacing.INFO", &menu.headline1_spacing_info[i] },
13258 { "menu.headline2_spacing.INFO", &menu.headline2_spacing_info[i] },
13259 { "menu.line_spacing.INFO", &menu.line_spacing_info[i] },
13260 { "menu.extra_spacing.INFO", &menu.extra_spacing_info[i] },
13263 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
13265 char *token = menu_config[j].token;
13266 char *value = getHashEntry(setup_file_hash, token);
13269 *menu_config[j].value = get_integer_from_string(value);
13273 // special case: initialize with default values that may be overwritten
13274 // (eg, init "menu.enter_screen.SCORES.xyz" from "menu.enter_screen.xyz")
13275 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
13277 struct TokenIntPtrInfo menu_config[] =
13279 { "menu.enter_screen.fade_mode", &menu.enter_screen[i].fade_mode },
13280 { "menu.enter_screen.fade_delay", &menu.enter_screen[i].fade_delay },
13281 { "menu.enter_screen.post_delay", &menu.enter_screen[i].post_delay },
13282 { "menu.leave_screen.fade_mode", &menu.leave_screen[i].fade_mode },
13283 { "menu.leave_screen.fade_delay", &menu.leave_screen[i].fade_delay },
13284 { "menu.leave_screen.post_delay", &menu.leave_screen[i].post_delay },
13285 { "menu.next_screen.fade_mode", &menu.next_screen[i].fade_mode },
13286 { "menu.next_screen.fade_delay", &menu.next_screen[i].fade_delay },
13287 { "menu.next_screen.post_delay", &menu.next_screen[i].post_delay }
13290 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
13292 char *token = menu_config[j].token;
13293 char *value = getHashEntry(setup_file_hash, token);
13296 *menu_config[j].value = get_token_parameter_value(token, value);
13300 // special case: initialize with default values that may be overwritten
13301 // (eg, init "viewport.door_1.MAIN.xyz" from "viewport.door_1.xyz")
13302 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
13306 char *token_prefix;
13307 struct RectWithBorder *struct_ptr;
13311 { "viewport.window", &viewport.window[i] },
13312 { "viewport.playfield", &viewport.playfield[i] },
13313 { "viewport.door_1", &viewport.door_1[i] },
13314 { "viewport.door_2", &viewport.door_2[i] }
13317 for (j = 0; j < ARRAY_SIZE(vp_struct); j++)
13319 struct TokenIntPtrInfo vp_config[] =
13321 { ".x", &vp_struct[j].struct_ptr->x },
13322 { ".y", &vp_struct[j].struct_ptr->y },
13323 { ".width", &vp_struct[j].struct_ptr->width },
13324 { ".height", &vp_struct[j].struct_ptr->height },
13325 { ".min_width", &vp_struct[j].struct_ptr->min_width },
13326 { ".min_height", &vp_struct[j].struct_ptr->min_height },
13327 { ".max_width", &vp_struct[j].struct_ptr->max_width },
13328 { ".max_height", &vp_struct[j].struct_ptr->max_height },
13329 { ".margin_left", &vp_struct[j].struct_ptr->margin_left },
13330 { ".margin_right", &vp_struct[j].struct_ptr->margin_right },
13331 { ".margin_top", &vp_struct[j].struct_ptr->margin_top },
13332 { ".margin_bottom", &vp_struct[j].struct_ptr->margin_bottom },
13333 { ".border_left", &vp_struct[j].struct_ptr->border_left },
13334 { ".border_right", &vp_struct[j].struct_ptr->border_right },
13335 { ".border_top", &vp_struct[j].struct_ptr->border_top },
13336 { ".border_bottom", &vp_struct[j].struct_ptr->border_bottom },
13337 { ".border_size", &vp_struct[j].struct_ptr->border_size },
13338 { ".align_size", &vp_struct[j].struct_ptr->align_size },
13339 { ".align", &vp_struct[j].struct_ptr->align },
13340 { ".valign", &vp_struct[j].struct_ptr->valign }
13343 for (k = 0; k < ARRAY_SIZE(vp_config); k++)
13345 char *token = getStringCat2(vp_struct[j].token_prefix,
13346 vp_config[k].token);
13347 char *value = getHashEntry(setup_file_hash, token);
13350 *vp_config[k].value = get_token_parameter_value(token, value);
13357 // special case: initialize with default values that may be overwritten
13358 // (e.g., init "[title].fade_mode" from "menu.next_screen.TITLE.fade_mode")
13359 for (i = 0; title_info[i].info != NULL; i++)
13361 struct TitleFadingInfo *info = title_info[i].info;
13362 char *base_token = title_info[i].text;
13364 for (j = 0; title_tokens[j].type != -1; j++)
13366 char *token = getStringCat2(base_token, title_tokens[j].text);
13367 char *value = getHashEntry(setup_file_hash, token);
13371 int parameter_value = get_token_parameter_value(token, value);
13375 *(int *)title_tokens[j].value = (int)parameter_value;
13384 // special case: initialize with default values that may be overwritten
13385 // (e.g., init "titlemessage_1.fade_mode" from "[titlemessage].fade_mode")
13386 for (i = 0; titlemessage_arrays[i].array != NULL; i++)
13388 struct TitleMessageInfo *array = titlemessage_arrays[i].array;
13389 char *base_token = titlemessage_arrays[i].text;
13391 for (j = 0; titlemessage_tokens[j].type != -1; j++)
13393 char *token = getStringCat2(base_token, titlemessage_tokens[j].text);
13394 char *value = getHashEntry(setup_file_hash, token);
13398 int parameter_value = get_token_parameter_value(token, value);
13400 for (k = 0; k < MAX_NUM_TITLE_MESSAGES; k++)
13404 if (titlemessage_tokens[j].type == TYPE_INTEGER)
13405 *(int *)titlemessage_tokens[j].value = (int)parameter_value;
13407 *(boolean *)titlemessage_tokens[j].value = (boolean)parameter_value;
13417 // read (and overwrite with) values that may be specified in config file
13418 InitMenuDesignSettings_FromHash(setup_file_hash, TRUE);
13420 // special case: check if network and preview player positions are redefined
13421 InitMenuDesignSettings_PreviewPlayers_FromHash(setup_file_hash);
13423 freeSetupFileHash(setup_file_hash);
13426 void LoadMenuDesignSettings(void)
13428 char *filename_base = UNDEFINED_FILENAME, *filename_local;
13430 InitMenuDesignSettings_Static();
13431 InitMenuDesignSettings_SpecialPreProcessing();
13432 InitMenuDesignSettings_PreviewPlayers();
13434 if (!GFX_OVERRIDE_ARTWORK(ARTWORK_TYPE_GRAPHICS))
13436 // first look for special settings configured in level series config
13437 filename_base = getCustomArtworkLevelConfigFilename(ARTWORK_TYPE_GRAPHICS);
13439 if (fileExists(filename_base))
13440 LoadMenuDesignSettingsFromFilename(filename_base);
13443 filename_local = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
13445 if (filename_local != NULL && !strEqual(filename_base, filename_local))
13446 LoadMenuDesignSettingsFromFilename(filename_local);
13448 InitMenuDesignSettings_SpecialPostProcessing();
13451 void LoadMenuDesignSettings_AfterGraphics(void)
13453 InitMenuDesignSettings_SpecialPostProcessing_AfterGraphics();
13456 void InitSoundSettings_FromHash(SetupFileHash *setup_file_hash,
13457 boolean ignore_defaults)
13461 for (i = 0; sound_config_vars[i].token != NULL; i++)
13463 char *value = getHashEntry(setup_file_hash, sound_config_vars[i].token);
13465 // (ignore definitions set to "[DEFAULT]" which are already initialized)
13466 if (ignore_defaults && strEqual(value, ARG_DEFAULT))
13470 *sound_config_vars[i].value =
13471 get_token_parameter_value(sound_config_vars[i].token, value);
13475 void InitSoundSettings_Static(void)
13477 // always start with reliable default values from static default config
13478 InitSoundSettings_FromHash(sound_config_hash, FALSE);
13481 static void LoadSoundSettingsFromFilename(char *filename)
13483 SetupFileHash *setup_file_hash;
13485 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
13488 // read (and overwrite with) values that may be specified in config file
13489 InitSoundSettings_FromHash(setup_file_hash, TRUE);
13491 freeSetupFileHash(setup_file_hash);
13494 void LoadSoundSettings(void)
13496 char *filename_base = UNDEFINED_FILENAME, *filename_local;
13498 InitSoundSettings_Static();
13500 if (!GFX_OVERRIDE_ARTWORK(ARTWORK_TYPE_SOUNDS))
13502 // first look for special settings configured in level series config
13503 filename_base = getCustomArtworkLevelConfigFilename(ARTWORK_TYPE_SOUNDS);
13505 if (fileExists(filename_base))
13506 LoadSoundSettingsFromFilename(filename_base);
13509 filename_local = getCustomArtworkConfigFilename(ARTWORK_TYPE_SOUNDS);
13511 if (filename_local != NULL && !strEqual(filename_base, filename_local))
13512 LoadSoundSettingsFromFilename(filename_local);
13515 void LoadUserDefinedEditorElementList(int **elements, int *num_elements)
13517 char *filename = getEditorSetupFilename();
13518 SetupFileList *setup_file_list, *list;
13519 SetupFileHash *element_hash;
13520 int num_unknown_tokens = 0;
13523 if ((setup_file_list = loadSetupFileList(filename)) == NULL)
13526 element_hash = newSetupFileHash();
13528 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
13529 setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
13531 // determined size may be larger than needed (due to unknown elements)
13533 for (list = setup_file_list; list != NULL; list = list->next)
13536 // add space for up to 3 more elements for padding that may be needed
13537 *num_elements += 3;
13539 // free memory for old list of elements, if needed
13540 checked_free(*elements);
13542 // allocate memory for new list of elements
13543 *elements = checked_malloc(*num_elements * sizeof(int));
13546 for (list = setup_file_list; list != NULL; list = list->next)
13548 char *value = getHashEntry(element_hash, list->token);
13550 if (value == NULL) // try to find obsolete token mapping
13552 char *mapped_token = get_mapped_token(list->token);
13554 if (mapped_token != NULL)
13556 value = getHashEntry(element_hash, mapped_token);
13558 free(mapped_token);
13564 (*elements)[(*num_elements)++] = atoi(value);
13568 if (num_unknown_tokens == 0)
13571 Warn("unknown token(s) found in config file:");
13572 Warn("- config file: '%s'", filename);
13574 num_unknown_tokens++;
13577 Warn("- token: '%s'", list->token);
13581 if (num_unknown_tokens > 0)
13584 while (*num_elements % 4) // pad with empty elements, if needed
13585 (*elements)[(*num_elements)++] = EL_EMPTY;
13587 freeSetupFileList(setup_file_list);
13588 freeSetupFileHash(element_hash);
13591 for (i = 0; i < *num_elements; i++)
13592 Debug("editor", "element '%s' [%d]\n",
13593 element_info[(*elements)[i]].token_name, (*elements)[i]);
13597 static struct MusicFileInfo *get_music_file_info_ext(char *basename, int music,
13600 SetupFileHash *setup_file_hash = NULL;
13601 struct MusicFileInfo tmp_music_file_info, *new_music_file_info;
13602 char *filename_music, *filename_prefix, *filename_info;
13608 token_to_value_ptr[] =
13610 { "title_header", &tmp_music_file_info.title_header },
13611 { "artist_header", &tmp_music_file_info.artist_header },
13612 { "album_header", &tmp_music_file_info.album_header },
13613 { "year_header", &tmp_music_file_info.year_header },
13614 { "played_header", &tmp_music_file_info.played_header },
13616 { "title", &tmp_music_file_info.title },
13617 { "artist", &tmp_music_file_info.artist },
13618 { "album", &tmp_music_file_info.album },
13619 { "year", &tmp_music_file_info.year },
13620 { "played", &tmp_music_file_info.played },
13626 filename_music = (is_sound ? getCustomSoundFilename(basename) :
13627 getCustomMusicFilename(basename));
13629 if (filename_music == NULL)
13632 // ---------- try to replace file extension ----------
13634 filename_prefix = getStringCopy(filename_music);
13635 if (strrchr(filename_prefix, '.') != NULL)
13636 *strrchr(filename_prefix, '.') = '\0';
13637 filename_info = getStringCat2(filename_prefix, ".txt");
13639 if (fileExists(filename_info))
13640 setup_file_hash = loadSetupFileHash(filename_info);
13642 free(filename_prefix);
13643 free(filename_info);
13645 if (setup_file_hash == NULL)
13647 // ---------- try to add file extension ----------
13649 filename_prefix = getStringCopy(filename_music);
13650 filename_info = getStringCat2(filename_prefix, ".txt");
13652 if (fileExists(filename_info))
13653 setup_file_hash = loadSetupFileHash(filename_info);
13655 free(filename_prefix);
13656 free(filename_info);
13659 if (setup_file_hash == NULL)
13662 // ---------- music file info found ----------
13664 clear_mem(&tmp_music_file_info, sizeof(struct MusicFileInfo));
13666 for (i = 0; token_to_value_ptr[i].token != NULL; i++)
13668 char *value = getHashEntry(setup_file_hash, token_to_value_ptr[i].token);
13670 *token_to_value_ptr[i].value_ptr =
13671 getStringCopy(value != NULL && *value != '\0' ? value : UNKNOWN_NAME);
13674 tmp_music_file_info.basename = getStringCopy(basename);
13675 tmp_music_file_info.music = music;
13676 tmp_music_file_info.is_sound = is_sound;
13678 new_music_file_info = checked_malloc(sizeof(struct MusicFileInfo));
13679 *new_music_file_info = tmp_music_file_info;
13681 return new_music_file_info;
13684 static struct MusicFileInfo *get_music_file_info(char *basename, int music)
13686 return get_music_file_info_ext(basename, music, FALSE);
13689 static struct MusicFileInfo *get_sound_file_info(char *basename, int sound)
13691 return get_music_file_info_ext(basename, sound, TRUE);
13694 static boolean music_info_listed_ext(struct MusicFileInfo *list,
13695 char *basename, boolean is_sound)
13697 for (; list != NULL; list = list->next)
13698 if (list->is_sound == is_sound && strEqual(list->basename, basename))
13704 static boolean music_info_listed(struct MusicFileInfo *list, char *basename)
13706 return music_info_listed_ext(list, basename, FALSE);
13709 static boolean sound_info_listed(struct MusicFileInfo *list, char *basename)
13711 return music_info_listed_ext(list, basename, TRUE);
13714 void LoadMusicInfo(void)
13716 int num_music_noconf = getMusicListSize_NoConf();
13717 int num_music = getMusicListSize();
13718 int num_sounds = getSoundListSize();
13719 struct FileInfo *music, *sound;
13720 struct MusicFileInfo *next, **new;
13724 while (music_file_info != NULL)
13726 next = music_file_info->next;
13728 checked_free(music_file_info->basename);
13730 checked_free(music_file_info->title_header);
13731 checked_free(music_file_info->artist_header);
13732 checked_free(music_file_info->album_header);
13733 checked_free(music_file_info->year_header);
13734 checked_free(music_file_info->played_header);
13736 checked_free(music_file_info->title);
13737 checked_free(music_file_info->artist);
13738 checked_free(music_file_info->album);
13739 checked_free(music_file_info->year);
13740 checked_free(music_file_info->played);
13742 free(music_file_info);
13744 music_file_info = next;
13747 new = &music_file_info;
13749 // get (configured or unconfigured) music file info for all levels
13750 for (i = leveldir_current->first_level;
13751 i <= leveldir_current->last_level; i++)
13755 if (levelset.music[i] != MUS_UNDEFINED)
13757 // get music file info for configured level music
13758 music_nr = levelset.music[i];
13760 else if (num_music_noconf > 0)
13762 // get music file info for unconfigured level music
13763 int level_pos = i - leveldir_current->first_level;
13765 music_nr = MAP_NOCONF_MUSIC(level_pos % num_music_noconf);
13772 char *basename = getMusicInfoEntryFilename(music_nr);
13774 if (basename == NULL)
13777 if (!music_info_listed(music_file_info, basename))
13779 *new = get_music_file_info(basename, music_nr);
13782 new = &(*new)->next;
13786 // get music file info for all remaining configured music files
13787 for (i = 0; i < num_music; i++)
13789 music = getMusicListEntry(i);
13791 if (music->filename == NULL)
13794 if (strEqual(music->filename, UNDEFINED_FILENAME))
13797 // a configured file may be not recognized as music
13798 if (!FileIsMusic(music->filename))
13801 if (!music_info_listed(music_file_info, music->filename))
13803 *new = get_music_file_info(music->filename, i);
13806 new = &(*new)->next;
13810 // get sound file info for all configured sound files
13811 for (i = 0; i < num_sounds; i++)
13813 sound = getSoundListEntry(i);
13815 if (sound->filename == NULL)
13818 if (strEqual(sound->filename, UNDEFINED_FILENAME))
13821 // a configured file may be not recognized as sound
13822 if (!FileIsSound(sound->filename))
13825 if (!sound_info_listed(music_file_info, sound->filename))
13827 *new = get_sound_file_info(sound->filename, i);
13829 new = &(*new)->next;
13833 // add pointers to previous list nodes
13835 struct MusicFileInfo *node = music_file_info;
13837 while (node != NULL)
13840 node->next->prev = node;
13846 static void add_helpanim_entry(int element, int action, int direction,
13847 int delay, int *num_list_entries)
13849 struct HelpAnimInfo *new_list_entry;
13850 (*num_list_entries)++;
13853 checked_realloc(helpanim_info,
13854 *num_list_entries * sizeof(struct HelpAnimInfo));
13855 new_list_entry = &helpanim_info[*num_list_entries - 1];
13857 new_list_entry->element = element;
13858 new_list_entry->action = action;
13859 new_list_entry->direction = direction;
13860 new_list_entry->delay = delay;
13863 static void print_unknown_token(char *filename, char *token, int token_nr)
13868 Warn("unknown token(s) found in config file:");
13869 Warn("- config file: '%s'", filename);
13872 Warn("- token: '%s'", token);
13875 static void print_unknown_token_end(int token_nr)
13881 void LoadHelpAnimInfo(void)
13883 char *filename = getHelpAnimFilename();
13884 SetupFileList *setup_file_list = NULL, *list;
13885 SetupFileHash *element_hash, *action_hash, *direction_hash;
13886 int num_list_entries = 0;
13887 int num_unknown_tokens = 0;
13890 if (fileExists(filename))
13891 setup_file_list = loadSetupFileList(filename);
13893 if (setup_file_list == NULL)
13895 // use reliable default values from static configuration
13896 SetupFileList *insert_ptr;
13898 insert_ptr = setup_file_list =
13899 newSetupFileList(helpanim_config[0].token,
13900 helpanim_config[0].value);
13902 for (i = 1; helpanim_config[i].token; i++)
13903 insert_ptr = addListEntry(insert_ptr,
13904 helpanim_config[i].token,
13905 helpanim_config[i].value);
13908 element_hash = newSetupFileHash();
13909 action_hash = newSetupFileHash();
13910 direction_hash = newSetupFileHash();
13912 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
13913 setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
13915 for (i = 0; i < NUM_ACTIONS; i++)
13916 setHashEntry(action_hash, element_action_info[i].suffix,
13917 i_to_a(element_action_info[i].value));
13919 // do not store direction index (bit) here, but direction value!
13920 for (i = 0; i < NUM_DIRECTIONS_FULL; i++)
13921 setHashEntry(direction_hash, element_direction_info[i].suffix,
13922 i_to_a(1 << element_direction_info[i].value));
13924 for (list = setup_file_list; list != NULL; list = list->next)
13926 char *element_token, *action_token, *direction_token;
13927 char *element_value, *action_value, *direction_value;
13928 int delay = atoi(list->value);
13930 if (strEqual(list->token, "end"))
13932 add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
13937 /* first try to break element into element/action/direction parts;
13938 if this does not work, also accept combined "element[.act][.dir]"
13939 elements (like "dynamite.active"), which are unique elements */
13941 if (strchr(list->token, '.') == NULL) // token contains no '.'
13943 element_value = getHashEntry(element_hash, list->token);
13944 if (element_value != NULL) // element found
13945 add_helpanim_entry(atoi(element_value), -1, -1, delay,
13946 &num_list_entries);
13949 // no further suffixes found -- this is not an element
13950 print_unknown_token(filename, list->token, num_unknown_tokens++);
13956 // token has format "<prefix>.<something>"
13958 action_token = strchr(list->token, '.'); // suffix may be action ...
13959 direction_token = action_token; // ... or direction
13961 element_token = getStringCopy(list->token);
13962 *strchr(element_token, '.') = '\0';
13964 element_value = getHashEntry(element_hash, element_token);
13966 if (element_value == NULL) // this is no element
13968 element_value = getHashEntry(element_hash, list->token);
13969 if (element_value != NULL) // combined element found
13970 add_helpanim_entry(atoi(element_value), -1, -1, delay,
13971 &num_list_entries);
13973 print_unknown_token(filename, list->token, num_unknown_tokens++);
13975 free(element_token);
13980 action_value = getHashEntry(action_hash, action_token);
13982 if (action_value != NULL) // action found
13984 add_helpanim_entry(atoi(element_value), atoi(action_value), -1, delay,
13985 &num_list_entries);
13987 free(element_token);
13992 direction_value = getHashEntry(direction_hash, direction_token);
13994 if (direction_value != NULL) // direction found
13996 add_helpanim_entry(atoi(element_value), -1, atoi(direction_value), delay,
13997 &num_list_entries);
13999 free(element_token);
14004 if (strchr(action_token + 1, '.') == NULL)
14006 // no further suffixes found -- this is not an action nor direction
14008 element_value = getHashEntry(element_hash, list->token);
14009 if (element_value != NULL) // combined element found
14010 add_helpanim_entry(atoi(element_value), -1, -1, delay,
14011 &num_list_entries);
14013 print_unknown_token(filename, list->token, num_unknown_tokens++);
14015 free(element_token);
14020 // token has format "<prefix>.<suffix>.<something>"
14022 direction_token = strchr(action_token + 1, '.');
14024 action_token = getStringCopy(action_token);
14025 *strchr(action_token + 1, '.') = '\0';
14027 action_value = getHashEntry(action_hash, action_token);
14029 if (action_value == NULL) // this is no action
14031 element_value = getHashEntry(element_hash, list->token);
14032 if (element_value != NULL) // combined element found
14033 add_helpanim_entry(atoi(element_value), -1, -1, delay,
14034 &num_list_entries);
14036 print_unknown_token(filename, list->token, num_unknown_tokens++);
14038 free(element_token);
14039 free(action_token);
14044 direction_value = getHashEntry(direction_hash, direction_token);
14046 if (direction_value != NULL) // direction found
14048 add_helpanim_entry(atoi(element_value), atoi(action_value),
14049 atoi(direction_value), delay, &num_list_entries);
14051 free(element_token);
14052 free(action_token);
14057 // this is no direction
14059 element_value = getHashEntry(element_hash, list->token);
14060 if (element_value != NULL) // combined element found
14061 add_helpanim_entry(atoi(element_value), -1, -1, delay,
14062 &num_list_entries);
14064 print_unknown_token(filename, list->token, num_unknown_tokens++);
14066 free(element_token);
14067 free(action_token);
14070 print_unknown_token_end(num_unknown_tokens);
14072 add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
14073 add_helpanim_entry(HELPANIM_LIST_END, -1, -1, -1, &num_list_entries);
14075 freeSetupFileList(setup_file_list);
14076 freeSetupFileHash(element_hash);
14077 freeSetupFileHash(action_hash);
14078 freeSetupFileHash(direction_hash);
14081 for (i = 0; i < num_list_entries; i++)
14082 Debug("files:LoadHelpAnimInfo", "'%s': %d, %d, %d => %d",
14083 EL_NAME(helpanim_info[i].element),
14084 helpanim_info[i].element,
14085 helpanim_info[i].action,
14086 helpanim_info[i].direction,
14087 helpanim_info[i].delay);
14091 void LoadHelpTextInfo(void)
14093 char *filename = getHelpTextFilename();
14096 if (helptext_info != NULL)
14098 freeSetupFileHash(helptext_info);
14099 helptext_info = NULL;
14102 if (fileExists(filename))
14103 helptext_info = loadSetupFileHash(filename);
14105 if (helptext_info == NULL)
14107 // use reliable default values from static configuration
14108 helptext_info = newSetupFileHash();
14110 for (i = 0; helptext_config[i].token; i++)
14111 setHashEntry(helptext_info,
14112 helptext_config[i].token,
14113 helptext_config[i].value);
14117 BEGIN_HASH_ITERATION(helptext_info, itr)
14119 Debug("files:LoadHelpTextInfo", "'%s' => '%s'",
14120 HASH_ITERATION_TOKEN(itr), HASH_ITERATION_VALUE(itr));
14122 END_HASH_ITERATION(hash, itr)
14127 // ----------------------------------------------------------------------------
14129 // ----------------------------------------------------------------------------
14131 #define MAX_NUM_CONVERT_LEVELS 1000
14133 void ConvertLevels(void)
14135 static LevelDirTree *convert_leveldir = NULL;
14136 static int convert_level_nr = -1;
14137 static int num_levels_handled = 0;
14138 static int num_levels_converted = 0;
14139 static boolean levels_failed[MAX_NUM_CONVERT_LEVELS];
14142 convert_leveldir = getTreeInfoFromIdentifier(leveldir_first,
14143 global.convert_leveldir);
14145 if (convert_leveldir == NULL)
14146 Fail("no such level identifier: '%s'", global.convert_leveldir);
14148 leveldir_current = convert_leveldir;
14150 if (global.convert_level_nr != -1)
14152 convert_leveldir->first_level = global.convert_level_nr;
14153 convert_leveldir->last_level = global.convert_level_nr;
14156 convert_level_nr = convert_leveldir->first_level;
14158 PrintLine("=", 79);
14159 Print("Converting levels\n");
14160 PrintLine("-", 79);
14161 Print("Level series identifier: '%s'\n", convert_leveldir->identifier);
14162 Print("Level series name: '%s'\n", convert_leveldir->name);
14163 Print("Level series author: '%s'\n", convert_leveldir->author);
14164 Print("Number of levels: %d\n", convert_leveldir->levels);
14165 PrintLine("=", 79);
14168 for (i = 0; i < MAX_NUM_CONVERT_LEVELS; i++)
14169 levels_failed[i] = FALSE;
14171 while (convert_level_nr <= convert_leveldir->last_level)
14173 char *level_filename;
14176 level_nr = convert_level_nr++;
14178 Print("Level %03d: ", level_nr);
14180 LoadLevel(level_nr);
14181 if (level.no_level_file || level.no_valid_file)
14183 Print("(no level)\n");
14187 Print("converting level ... ");
14190 // special case: conversion of some EMC levels as requested by ACME
14191 level.game_engine_type = GAME_ENGINE_TYPE_RND;
14194 level_filename = getDefaultLevelFilename(level_nr);
14195 new_level = !fileExists(level_filename);
14199 SaveLevel(level_nr);
14201 num_levels_converted++;
14203 Print("converted.\n");
14207 if (level_nr >= 0 && level_nr < MAX_NUM_CONVERT_LEVELS)
14208 levels_failed[level_nr] = TRUE;
14210 Print("NOT CONVERTED -- LEVEL ALREADY EXISTS.\n");
14213 num_levels_handled++;
14217 PrintLine("=", 79);
14218 Print("Number of levels handled: %d\n", num_levels_handled);
14219 Print("Number of levels converted: %d (%d%%)\n", num_levels_converted,
14220 (num_levels_handled ?
14221 num_levels_converted * 100 / num_levels_handled : 0));
14222 PrintLine("-", 79);
14223 Print("Summary (for automatic parsing by scripts):\n");
14224 Print("LEVELDIR '%s', CONVERTED %d/%d (%d%%)",
14225 convert_leveldir->identifier, num_levels_converted,
14226 num_levels_handled,
14227 (num_levels_handled ?
14228 num_levels_converted * 100 / num_levels_handled : 0));
14230 if (num_levels_handled != num_levels_converted)
14232 Print(", FAILED:");
14233 for (i = 0; i < MAX_NUM_CONVERT_LEVELS; i++)
14234 if (levels_failed[i])
14239 PrintLine("=", 79);
14241 CloseAllAndExit(0);
14245 // ----------------------------------------------------------------------------
14246 // create and save images for use in level sketches (raw BMP format)
14247 // ----------------------------------------------------------------------------
14249 void CreateLevelSketchImages(void)
14255 InitElementPropertiesGfxElement();
14257 bitmap1 = CreateBitmap(TILEX, TILEY, DEFAULT_DEPTH);
14258 bitmap2 = CreateBitmap(MINI_TILEX, MINI_TILEY, DEFAULT_DEPTH);
14260 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
14262 int element = getMappedElement(i);
14263 char basename1[16];
14264 char basename2[16];
14268 sprintf(basename1, "%04d.bmp", i);
14269 sprintf(basename2, "%04ds.bmp", i);
14271 filename1 = getPath2(global.create_sketch_images_dir, basename1);
14272 filename2 = getPath2(global.create_sketch_images_dir, basename2);
14274 DrawSizedElement(0, 0, element, TILESIZE);
14275 BlitBitmap(drawto, bitmap1, SX, SY, TILEX, TILEY, 0, 0);
14277 if (SDL_SaveBMP(bitmap1->surface, filename1) != 0)
14278 Fail("cannot save level sketch image file '%s'", filename1);
14280 DrawSizedElement(0, 0, element, MINI_TILESIZE);
14281 BlitBitmap(drawto, bitmap2, SX, SY, MINI_TILEX, MINI_TILEY, 0, 0);
14283 if (SDL_SaveBMP(bitmap2->surface, filename2) != 0)
14284 Fail("cannot save level sketch image file '%s'", filename2);
14289 // create corresponding SQL statements (for normal and small images)
14292 printf("insert into phpbb_words values (NULL, '`%03d', '<IMG class=\"levelsketch\" src=\"/I/%04d.png\"/>');\n", i, i);
14293 printf("insert into phpbb_words values (NULL, '¸%03d', '<IMG class=\"levelsketch\" src=\"/I/%04ds.png\"/>');\n", i, i);
14296 printf("insert into phpbb_words values (NULL, '`%04d', '<IMG class=\"levelsketch\" src=\"/I/%04d.png\"/>');\n", i, i);
14297 printf("insert into phpbb_words values (NULL, '¸%04d', '<IMG class=\"levelsketch\" src=\"/I/%04ds.png\"/>');\n", i, i);
14299 // optional: create content for forum level sketch demonstration post
14301 fprintf(stderr, "%03d `%03d%c", i, i, (i % 10 < 9 ? ' ' : '\n'));
14304 FreeBitmap(bitmap1);
14305 FreeBitmap(bitmap2);
14308 fprintf(stderr, "\n");
14310 Info("%d normal and small images created", NUM_FILE_ELEMENTS);
14312 CloseAllAndExit(0);
14316 // ----------------------------------------------------------------------------
14317 // create and save images for element collecting animations (raw BMP format)
14318 // ----------------------------------------------------------------------------
14320 static boolean createCollectImage(int element)
14322 return (IS_COLLECTIBLE(element) && !IS_SP_ELEMENT(element));
14325 void CreateCollectElementImages(void)
14329 int anim_frames = num_steps - 1;
14330 int tile_size = TILESIZE;
14331 int anim_width = tile_size * anim_frames;
14332 int anim_height = tile_size;
14333 int num_collect_images = 0;
14334 int pos_collect_images = 0;
14336 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
14337 if (createCollectImage(i))
14338 num_collect_images++;
14340 Info("Creating %d element collecting animation images ...",
14341 num_collect_images);
14343 int dst_width = anim_width * 2;
14344 int dst_height = anim_height * num_collect_images / 2;
14345 Bitmap *dst_bitmap = CreateBitmap(dst_width, dst_height, DEFAULT_DEPTH);
14346 char *basename_bmp = "RocksCollect.bmp";
14347 char *basename_png = "RocksCollect.png";
14348 char *filename_bmp = getPath2(global.create_collect_images_dir, basename_bmp);
14349 char *filename_png = getPath2(global.create_collect_images_dir, basename_png);
14350 int len_filename_bmp = strlen(filename_bmp);
14351 int len_filename_png = strlen(filename_png);
14352 int max_command_len = MAX_FILENAME_LEN + len_filename_bmp + len_filename_png;
14353 char cmd_convert[max_command_len];
14355 snprintf(cmd_convert, max_command_len, "convert \"%s\" \"%s\"",
14359 // force using RGBA surface for destination bitmap
14360 SDL_SetColorKey(dst_bitmap->surface, SET_TRANSPARENT_PIXEL,
14361 SDL_MapRGB(dst_bitmap->surface->format, 0x00, 0x00, 0x00));
14363 dst_bitmap->surface =
14364 SDL_ConvertSurfaceFormat(dst_bitmap->surface, SDL_PIXELFORMAT_ARGB8888, 0);
14366 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
14368 if (!createCollectImage(i))
14371 int dst_x = (pos_collect_images / (num_collect_images / 2)) * anim_width;
14372 int dst_y = (pos_collect_images % (num_collect_images / 2)) * anim_height;
14373 int graphic = el2img(i);
14374 char *token_name = element_info[i].token_name;
14375 Bitmap *tmp_bitmap = CreateBitmap(tile_size, tile_size, DEFAULT_DEPTH);
14376 Bitmap *src_bitmap;
14379 Info("- creating collecting image for '%s' ...", token_name);
14381 getGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
14383 BlitBitmap(src_bitmap, tmp_bitmap, src_x, src_y,
14384 tile_size, tile_size, 0, 0);
14386 // force using RGBA surface for temporary bitmap (using transparent black)
14387 SDL_SetColorKey(tmp_bitmap->surface, SET_TRANSPARENT_PIXEL,
14388 SDL_MapRGB(tmp_bitmap->surface->format, 0x00, 0x00, 0x00));
14390 tmp_bitmap->surface =
14391 SDL_ConvertSurfaceFormat(tmp_bitmap->surface, SDL_PIXELFORMAT_ARGB8888, 0);
14393 tmp_bitmap->surface_masked = tmp_bitmap->surface;
14395 for (j = 0; j < anim_frames; j++)
14397 int frame_size_final = tile_size * (anim_frames - j) / num_steps;
14398 int frame_size = frame_size_final * num_steps;
14399 int offset = (tile_size - frame_size_final) / 2;
14400 Bitmap *frame_bitmap = ZoomBitmap(tmp_bitmap, frame_size, frame_size);
14402 while (frame_size > frame_size_final)
14406 Bitmap *half_bitmap = ZoomBitmap(frame_bitmap, frame_size, frame_size);
14408 FreeBitmap(frame_bitmap);
14410 frame_bitmap = half_bitmap;
14413 BlitBitmapMasked(frame_bitmap, dst_bitmap, 0, 0,
14414 frame_size_final, frame_size_final,
14415 dst_x + j * tile_size + offset, dst_y + offset);
14417 FreeBitmap(frame_bitmap);
14420 tmp_bitmap->surface_masked = NULL;
14422 FreeBitmap(tmp_bitmap);
14424 pos_collect_images++;
14427 if (SDL_SaveBMP(dst_bitmap->surface, filename_bmp) != 0)
14428 Fail("cannot save element collecting image file '%s'", filename_bmp);
14430 FreeBitmap(dst_bitmap);
14432 Info("Converting image file from BMP to PNG ...");
14434 if (system(cmd_convert) != 0)
14435 Fail("converting image file failed");
14437 unlink(filename_bmp);
14441 CloseAllAndExit(0);
14445 // ----------------------------------------------------------------------------
14446 // create and save images for custom and group elements (raw BMP format)
14447 // ----------------------------------------------------------------------------
14449 void CreateCustomElementImages(char *directory)
14451 char *src_basename = "RocksCE-template.ilbm";
14452 char *dst_basename = "RocksCE.bmp";
14453 char *src_filename = getPath2(directory, src_basename);
14454 char *dst_filename = getPath2(directory, dst_basename);
14455 Bitmap *src_bitmap;
14457 int yoffset_ce = 0;
14458 int yoffset_ge = (TILEY * NUM_CUSTOM_ELEMENTS / 16);
14461 InitVideoDefaults();
14463 ReCreateBitmap(&backbuffer, video.width, video.height);
14465 src_bitmap = LoadImage(src_filename);
14467 bitmap = CreateBitmap(TILEX * 16 * 2,
14468 TILEY * (NUM_CUSTOM_ELEMENTS + NUM_GROUP_ELEMENTS) / 16,
14471 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
14478 BlitBitmap(src_bitmap, bitmap, 0, 0, TILEX, TILEY,
14479 TILEX * x, TILEY * y + yoffset_ce);
14481 BlitBitmap(src_bitmap, bitmap, 0, TILEY,
14483 TILEX * x + TILEX * 16,
14484 TILEY * y + yoffset_ce);
14486 for (j = 2; j >= 0; j--)
14490 BlitBitmap(src_bitmap, bitmap,
14491 TILEX + c * 7, 0, 6, 10,
14492 TILEX * x + 6 + j * 7,
14493 TILEY * y + 11 + yoffset_ce);
14495 BlitBitmap(src_bitmap, bitmap,
14496 TILEX + c * 8, TILEY, 6, 10,
14497 TILEX * 16 + TILEX * x + 6 + j * 8,
14498 TILEY * y + 10 + yoffset_ce);
14504 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
14511 BlitBitmap(src_bitmap, bitmap, 0, 0, TILEX, TILEY,
14512 TILEX * x, TILEY * y + yoffset_ge);
14514 BlitBitmap(src_bitmap, bitmap, 0, TILEY,
14516 TILEX * x + TILEX * 16,
14517 TILEY * y + yoffset_ge);
14519 for (j = 1; j >= 0; j--)
14523 BlitBitmap(src_bitmap, bitmap, TILEX + c * 10, 11, 10, 10,
14524 TILEX * x + 6 + j * 10,
14525 TILEY * y + 11 + yoffset_ge);
14527 BlitBitmap(src_bitmap, bitmap,
14528 TILEX + c * 8, TILEY + 12, 6, 10,
14529 TILEX * 16 + TILEX * x + 10 + j * 8,
14530 TILEY * y + 10 + yoffset_ge);
14536 if (SDL_SaveBMP(bitmap->surface, dst_filename) != 0)
14537 Fail("cannot save CE graphics file '%s'", dst_filename);
14539 FreeBitmap(bitmap);
14541 CloseAllAndExit(0);