1 // ============================================================================
2 // Rocks'n'Diamonds - McDuffin Strikes Back!
3 // ----------------------------------------------------------------------------
4 // (c) 1995-2014 by Artsoft Entertainment
7 // https://www.artsoft.org/
8 // ----------------------------------------------------------------------------
10 // ============================================================================
17 #include "libgame/libgame.h"
27 #define ENABLE_UNUSED_CODE 0 // currently unused functions
28 #define ENABLE_HISTORIC_CHUNKS 0 // only for historic reference
29 #define ENABLE_RESERVED_CODE 0 // reserved for later use
31 #define CHUNK_ID_LEN 4 // IFF style chunk id length
32 #define CHUNK_SIZE_UNDEFINED 0 // undefined chunk size == 0
33 #define CHUNK_SIZE_NONE -1 // do not write chunk size
35 #define LEVEL_CHUNK_NAME_SIZE MAX_LEVEL_NAME_LEN
36 #define LEVEL_CHUNK_AUTH_SIZE MAX_LEVEL_AUTHOR_LEN
38 #define LEVEL_CHUNK_VERS_SIZE 8 // size of file version chunk
39 #define LEVEL_CHUNK_DATE_SIZE 4 // size of file date chunk
40 #define LEVEL_CHUNK_HEAD_SIZE 80 // size of level file header
41 #define LEVEL_CHUNK_HEAD_UNUSED 0 // unused level header bytes
42 #define LEVEL_CHUNK_CNT2_SIZE 160 // size of level CNT2 chunk
43 #define LEVEL_CHUNK_CNT2_UNUSED 11 // unused CNT2 chunk bytes
44 #define LEVEL_CHUNK_CNT3_HEADER 16 // size of level CNT3 header
45 #define LEVEL_CHUNK_CNT3_UNUSED 10 // unused CNT3 chunk bytes
46 #define LEVEL_CPART_CUS3_SIZE 134 // size of CUS3 chunk part
47 #define LEVEL_CPART_CUS3_UNUSED 15 // unused CUS3 bytes / part
48 #define LEVEL_CHUNK_GRP1_SIZE 74 // size of level GRP1 chunk
50 // (element number, number of change pages, change page number)
51 #define LEVEL_CHUNK_CUSX_UNCHANGED (2 + (1 + 1) + (1 + 1))
53 // (element number only)
54 #define LEVEL_CHUNK_GRPX_UNCHANGED 2
55 #define LEVEL_CHUNK_EMPX_UNCHANGED 2
56 #define LEVEL_CHUNK_NOTE_UNCHANGED 2
58 // (nothing at all if unchanged)
59 #define LEVEL_CHUNK_ELEM_UNCHANGED 0
61 #define TAPE_CHUNK_VERS_SIZE 8 // size of file version chunk
62 #define TAPE_CHUNK_HEAD_SIZE 20 // size of tape file header
63 #define TAPE_CHUNK_SCRN_SIZE 2 // size of screen size chunk
65 #define SCORE_CHUNK_VERS_SIZE 8 // size of file version chunk
67 #define LEVEL_CHUNK_CNT3_SIZE(x) (LEVEL_CHUNK_CNT3_HEADER + (x))
68 #define LEVEL_CHUNK_CUS3_SIZE(x) (2 + (x) * LEVEL_CPART_CUS3_SIZE)
69 #define LEVEL_CHUNK_CUS4_SIZE(x) (96 + (x) * 48)
71 // file identifier strings
72 #define LEVEL_COOKIE_TMPL "ROCKSNDIAMONDS_LEVEL_FILE_VERSION_x.x"
73 #define TAPE_COOKIE_TMPL "ROCKSNDIAMONDS_TAPE_FILE_VERSION_x.x"
74 #define SCORE_COOKIE_TMPL "ROCKSNDIAMONDS_SCORE_FILE_VERSION_x.x"
76 // values for deciding when (not) to save configuration data
77 #define SAVE_CONF_NEVER 0
78 #define SAVE_CONF_ALWAYS 1
79 #define SAVE_CONF_WHEN_CHANGED -1
81 // values for chunks using micro chunks
82 #define CONF_MASK_1_BYTE 0x00
83 #define CONF_MASK_2_BYTE 0x40
84 #define CONF_MASK_4_BYTE 0x80
85 #define CONF_MASK_MULTI_BYTES 0xc0
87 #define CONF_MASK_BYTES 0xc0
88 #define CONF_MASK_TOKEN 0x3f
90 #define CONF_VALUE_1_BYTE(x) (CONF_MASK_1_BYTE | (x))
91 #define CONF_VALUE_2_BYTE(x) (CONF_MASK_2_BYTE | (x))
92 #define CONF_VALUE_4_BYTE(x) (CONF_MASK_4_BYTE | (x))
93 #define CONF_VALUE_MULTI_BYTES(x) (CONF_MASK_MULTI_BYTES | (x))
95 // these definitions are just for convenience of use and readability
96 #define CONF_VALUE_8_BIT(x) CONF_VALUE_1_BYTE(x)
97 #define CONF_VALUE_16_BIT(x) CONF_VALUE_2_BYTE(x)
98 #define CONF_VALUE_32_BIT(x) CONF_VALUE_4_BYTE(x)
99 #define CONF_VALUE_BYTES(x) CONF_VALUE_MULTI_BYTES(x)
101 #define CONF_VALUE_NUM_BYTES(x) ((x) == CONF_MASK_1_BYTE ? 1 : \
102 (x) == CONF_MASK_2_BYTE ? 2 : \
103 (x) == CONF_MASK_4_BYTE ? 4 : 0)
105 #define CONF_CONTENT_NUM_ELEMENTS (3 * 3)
106 #define CONF_CONTENT_NUM_BYTES (CONF_CONTENT_NUM_ELEMENTS * 2)
107 #define CONF_ELEMENT_NUM_BYTES (2)
109 #define CONF_ENTITY_NUM_BYTES(t) ((t) == TYPE_ELEMENT || \
110 (t) == TYPE_ELEMENT_LIST ? \
111 CONF_ELEMENT_NUM_BYTES : \
112 (t) == TYPE_CONTENT || \
113 (t) == TYPE_CONTENT_LIST ? \
114 CONF_CONTENT_NUM_BYTES : 1)
116 #define CONF_ELEMENT_BYTE_POS(i) ((i) * CONF_ELEMENT_NUM_BYTES)
117 #define CONF_ELEMENTS_ELEMENT(b, i) ((b[CONF_ELEMENT_BYTE_POS(i)] << 8) | \
118 (b[CONF_ELEMENT_BYTE_POS(i) + 1]))
120 #define CONF_CONTENT_ELEMENT_POS(c,x,y) ((c) * CONF_CONTENT_NUM_ELEMENTS + \
122 #define CONF_CONTENT_BYTE_POS(c,x,y) (CONF_CONTENT_ELEMENT_POS(c,x,y) * \
123 CONF_ELEMENT_NUM_BYTES)
124 #define CONF_CONTENTS_ELEMENT(b,c,x,y) ((b[CONF_CONTENT_BYTE_POS(c,x,y)]<< 8)|\
125 (b[CONF_CONTENT_BYTE_POS(c,x,y) + 1]))
127 // temporary variables used to store pointers to structure members
128 static struct LevelInfo li;
129 static struct ElementInfo xx_ei, yy_ei;
130 static struct ElementChangeInfo xx_change;
131 static struct ElementGroupInfo xx_group;
132 static struct EnvelopeInfo xx_envelope;
133 static unsigned int xx_event_bits[NUM_CE_BITFIELDS];
134 static char xx_default_description[MAX_ELEMENT_NAME_LEN + 1];
135 static int xx_num_contents;
136 static int xx_current_change_page;
137 static char xx_default_string_empty[1] = "";
138 static int xx_string_length_unused;
140 struct LevelFileConfigInfo
142 int element; // element for which data is to be stored
143 int save_type; // save data always, never or when changed
144 int data_type; // data type (used internally, not stored)
145 int conf_type; // micro chunk identifier (stored in file)
148 void *value; // variable that holds the data to be stored
149 int default_value; // initial default value for this variable
152 void *value_copy; // variable that holds the data to be copied
153 void *num_entities; // number of entities for multi-byte data
154 int default_num_entities; // default number of entities for this data
155 int max_num_entities; // maximal number of entities for this data
156 char *default_string; // optional default string for string data
159 static struct LevelFileConfigInfo chunk_config_INFO[] =
161 // ---------- values not related to single elements -------------------------
164 -1, SAVE_CONF_ALWAYS,
165 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
166 &li.game_engine_type, GAME_ENGINE_TYPE_RND
169 -1, SAVE_CONF_ALWAYS,
170 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
171 &li.fieldx, STD_LEV_FIELDX
174 -1, SAVE_CONF_ALWAYS,
175 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
176 &li.fieldy, STD_LEV_FIELDY
179 -1, SAVE_CONF_ALWAYS,
180 TYPE_INTEGER, CONF_VALUE_16_BIT(3),
184 -1, SAVE_CONF_ALWAYS,
185 TYPE_INTEGER, CONF_VALUE_16_BIT(4),
190 TYPE_INTEGER, CONF_VALUE_32_BIT(2),
195 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
196 &li.use_step_counter, FALSE
200 TYPE_BITFIELD, CONF_VALUE_8_BIT(4),
201 &li.wind_direction_initial, MV_NONE
205 TYPE_BOOLEAN, CONF_VALUE_8_BIT(5),
206 &li.em_slippery_gems, FALSE
210 TYPE_BOOLEAN, CONF_VALUE_8_BIT(6),
211 &li.use_custom_template, FALSE
215 TYPE_BITFIELD, CONF_VALUE_32_BIT(1),
216 &li.can_move_into_acid_bits, ~0 // default: everything can
220 TYPE_BITFIELD, CONF_VALUE_8_BIT(7),
221 &li.dont_collide_with_bits, ~0 // default: always deadly
225 TYPE_BOOLEAN, CONF_VALUE_8_BIT(8),
226 &li.em_explodes_by_fire, FALSE
230 TYPE_INTEGER, CONF_VALUE_16_BIT(5),
231 &li.score[SC_TIME_BONUS], 1
235 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
236 &li.auto_exit_sokoban, FALSE
240 TYPE_BOOLEAN, CONF_VALUE_8_BIT(10),
241 &li.auto_count_gems, FALSE
245 TYPE_BOOLEAN, CONF_VALUE_8_BIT(11),
246 &li.solved_by_one_player, FALSE
250 TYPE_INTEGER, CONF_VALUE_8_BIT(12),
251 &li.time_score_base, 1
255 TYPE_BOOLEAN, CONF_VALUE_8_BIT(13),
256 &li.rate_time_over_score, FALSE
260 TYPE_BOOLEAN, CONF_VALUE_8_BIT(14),
261 &li.bd_intermission, FALSE
265 TYPE_INTEGER, CONF_VALUE_8_BIT(15),
266 &li.bd_scheduling_type, GD_SCHEDULING_MILLISECONDS
270 TYPE_BOOLEAN, CONF_VALUE_8_BIT(16),
271 &li.bd_pal_timing, FALSE
275 TYPE_INTEGER, CONF_VALUE_16_BIT(6),
276 &li.bd_cycle_delay_ms, 200
280 TYPE_INTEGER, CONF_VALUE_8_BIT(17),
281 &li.bd_cycle_delay_c64, 0
285 TYPE_INTEGER, CONF_VALUE_8_BIT(18),
286 &li.bd_hatching_delay_cycles, 21
290 TYPE_INTEGER, CONF_VALUE_8_BIT(19),
291 &li.bd_hatching_delay_seconds, 2
295 TYPE_BOOLEAN, CONF_VALUE_8_BIT(20),
296 &li.bd_line_shifting_borders, FALSE
300 TYPE_BOOLEAN, CONF_VALUE_8_BIT(21),
301 &li.bd_scan_first_and_last_row, TRUE
305 TYPE_BOOLEAN, CONF_VALUE_8_BIT(22),
306 &li.bd_short_explosions, TRUE
310 TYPE_BOOLEAN, CONF_VALUE_8_BIT(23),
311 &li.bd_gravity_affects_all, TRUE
321 static struct LevelFileConfigInfo chunk_config_ELEM[] =
323 // (these values are the same for each player)
326 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
327 &li.block_last_field, FALSE // default case for EM levels
331 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
332 &li.sp_block_last_field, TRUE // default case for SP levels
336 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
337 &li.instant_relocation, FALSE
341 TYPE_BOOLEAN, CONF_VALUE_8_BIT(4),
342 &li.can_pass_to_walkable, FALSE
346 TYPE_BOOLEAN, CONF_VALUE_8_BIT(5),
347 &li.block_snap_field, TRUE
351 TYPE_BOOLEAN, CONF_VALUE_8_BIT(6),
352 &li.continuous_snapping, TRUE
356 TYPE_BOOLEAN, CONF_VALUE_8_BIT(12),
357 &li.shifted_relocation, FALSE
361 TYPE_BOOLEAN, CONF_VALUE_8_BIT(15),
362 &li.lazy_relocation, FALSE
366 TYPE_BOOLEAN, CONF_VALUE_8_BIT(16),
367 &li.finish_dig_collect, TRUE
371 TYPE_BOOLEAN, CONF_VALUE_8_BIT(17),
372 &li.keep_walkable_ce, FALSE
375 // (these values are different for each player)
378 TYPE_INTEGER, CONF_VALUE_8_BIT(7),
379 &li.initial_player_stepsize[0], STEPSIZE_NORMAL
383 TYPE_BOOLEAN, CONF_VALUE_8_BIT(8),
384 &li.initial_player_gravity[0], FALSE
388 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
389 &li.use_start_element[0], FALSE
393 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
394 &li.start_element[0], EL_PLAYER_1
398 TYPE_BOOLEAN, CONF_VALUE_8_BIT(10),
399 &li.use_artwork_element[0], FALSE
403 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
404 &li.artwork_element[0], EL_PLAYER_1
408 TYPE_BOOLEAN, CONF_VALUE_8_BIT(11),
409 &li.use_explosion_element[0], FALSE
413 TYPE_ELEMENT, CONF_VALUE_16_BIT(3),
414 &li.explosion_element[0], EL_PLAYER_1
418 TYPE_BOOLEAN, CONF_VALUE_8_BIT(13),
419 &li.use_initial_inventory[0], FALSE
423 TYPE_BOOLEAN, CONF_VALUE_8_BIT(14),
424 &li.initial_inventory_size[0], 1
428 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(1),
429 &li.initial_inventory_content[0][0],EL_EMPTY, NULL,
430 &li.initial_inventory_size[0], 1, MAX_INITIAL_INVENTORY_SIZE
435 TYPE_INTEGER, CONF_VALUE_8_BIT(7),
436 &li.initial_player_stepsize[1], STEPSIZE_NORMAL
440 TYPE_BOOLEAN, CONF_VALUE_8_BIT(8),
441 &li.initial_player_gravity[1], FALSE
445 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
446 &li.use_start_element[1], FALSE
450 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
451 &li.start_element[1], EL_PLAYER_2
455 TYPE_BOOLEAN, CONF_VALUE_8_BIT(10),
456 &li.use_artwork_element[1], FALSE
460 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
461 &li.artwork_element[1], EL_PLAYER_2
465 TYPE_BOOLEAN, CONF_VALUE_8_BIT(11),
466 &li.use_explosion_element[1], FALSE
470 TYPE_ELEMENT, CONF_VALUE_16_BIT(3),
471 &li.explosion_element[1], EL_PLAYER_2
475 TYPE_BOOLEAN, CONF_VALUE_8_BIT(13),
476 &li.use_initial_inventory[1], FALSE
480 TYPE_BOOLEAN, CONF_VALUE_8_BIT(14),
481 &li.initial_inventory_size[1], 1
485 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(1),
486 &li.initial_inventory_content[1][0],EL_EMPTY, NULL,
487 &li.initial_inventory_size[1], 1, MAX_INITIAL_INVENTORY_SIZE
492 TYPE_INTEGER, CONF_VALUE_8_BIT(7),
493 &li.initial_player_stepsize[2], STEPSIZE_NORMAL
497 TYPE_BOOLEAN, CONF_VALUE_8_BIT(8),
498 &li.initial_player_gravity[2], FALSE
502 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
503 &li.use_start_element[2], FALSE
507 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
508 &li.start_element[2], EL_PLAYER_3
512 TYPE_BOOLEAN, CONF_VALUE_8_BIT(10),
513 &li.use_artwork_element[2], FALSE
517 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
518 &li.artwork_element[2], EL_PLAYER_3
522 TYPE_BOOLEAN, CONF_VALUE_8_BIT(11),
523 &li.use_explosion_element[2], FALSE
527 TYPE_ELEMENT, CONF_VALUE_16_BIT(3),
528 &li.explosion_element[2], EL_PLAYER_3
532 TYPE_BOOLEAN, CONF_VALUE_8_BIT(13),
533 &li.use_initial_inventory[2], FALSE
537 TYPE_BOOLEAN, CONF_VALUE_8_BIT(14),
538 &li.initial_inventory_size[2], 1
542 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(1),
543 &li.initial_inventory_content[2][0],EL_EMPTY, NULL,
544 &li.initial_inventory_size[2], 1, MAX_INITIAL_INVENTORY_SIZE
549 TYPE_INTEGER, CONF_VALUE_8_BIT(7),
550 &li.initial_player_stepsize[3], STEPSIZE_NORMAL
554 TYPE_BOOLEAN, CONF_VALUE_8_BIT(8),
555 &li.initial_player_gravity[3], FALSE
559 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
560 &li.use_start_element[3], FALSE
564 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
565 &li.start_element[3], EL_PLAYER_4
569 TYPE_BOOLEAN, CONF_VALUE_8_BIT(10),
570 &li.use_artwork_element[3], FALSE
574 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
575 &li.artwork_element[3], EL_PLAYER_4
579 TYPE_BOOLEAN, CONF_VALUE_8_BIT(11),
580 &li.use_explosion_element[3], FALSE
584 TYPE_ELEMENT, CONF_VALUE_16_BIT(3),
585 &li.explosion_element[3], EL_PLAYER_4
589 TYPE_BOOLEAN, CONF_VALUE_8_BIT(13),
590 &li.use_initial_inventory[3], FALSE
594 TYPE_BOOLEAN, CONF_VALUE_8_BIT(14),
595 &li.initial_inventory_size[3], 1
599 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(1),
600 &li.initial_inventory_content[3][0],EL_EMPTY, NULL,
601 &li.initial_inventory_size[3], 1, MAX_INITIAL_INVENTORY_SIZE
604 // (these values are only valid for BD style levels)
605 // (some values for BD style amoeba following below)
608 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
609 &li.bd_diagonal_movements, FALSE
613 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
614 &li.bd_topmost_player_active, TRUE
618 TYPE_INTEGER, CONF_VALUE_8_BIT(3),
619 &li.bd_pushing_prob, 25
623 TYPE_INTEGER, CONF_VALUE_8_BIT(4),
624 &li.bd_pushing_prob_with_sweet, 100
628 TYPE_BOOLEAN, CONF_VALUE_8_BIT(5),
629 &li.bd_push_mega_rock_with_sweet, FALSE
633 TYPE_INTEGER, CONF_VALUE_8_BIT(6),
634 &li.bd_snap_element, EL_EMPTY
639 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
640 &li.score[SC_DIAMOND_EXTRA], 20
644 EL_BD_MAGIC_WALL, -1,
645 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
646 &li.bd_magic_wall_wait_hatching, FALSE
649 EL_BD_MAGIC_WALL, -1,
650 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
651 &li.bd_magic_wall_stops_amoeba, TRUE
656 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
657 &li.bd_clock_extra_time, 30
661 EL_BD_VOODOO_DOLL, -1,
662 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
663 &li.bd_voodoo_collects_diamonds, FALSE
666 EL_BD_VOODOO_DOLL, -1,
667 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
668 &li.bd_voodoo_hurt_kills_player, FALSE
671 EL_BD_VOODOO_DOLL, -1,
672 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
673 &li.bd_voodoo_dies_by_rock, FALSE
676 EL_BD_VOODOO_DOLL, -1,
677 TYPE_BOOLEAN, CONF_VALUE_8_BIT(4),
678 &li.bd_voodoo_vanish_by_explosion, TRUE
681 EL_BD_VOODOO_DOLL, -1,
682 TYPE_INTEGER, CONF_VALUE_8_BIT(5),
683 &li.bd_voodoo_penalty_time, 30
688 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
689 &li.bd_slime_is_predictable, TRUE
693 TYPE_INTEGER, CONF_VALUE_8_BIT(2),
694 &li.bd_slime_permeability_rate, 100
698 TYPE_INTEGER, CONF_VALUE_8_BIT(3),
699 &li.bd_slime_permeability_bits_c64, 0
703 TYPE_INTEGER, CONF_VALUE_32_BIT(1),
704 &li.bd_slime_random_seed_c64, -1
707 // (the following values are related to various game elements)
711 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
712 &li.score[SC_EMERALD], 10
717 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
718 &li.score[SC_DIAMOND], 10
723 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
724 &li.score[SC_BUG], 10
729 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
730 &li.score[SC_SPACESHIP], 10
735 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
736 &li.score[SC_PACMAN], 10
741 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
742 &li.score[SC_NUT], 10
747 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
748 &li.score[SC_DYNAMITE], 10
753 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
754 &li.score[SC_KEY], 10
759 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
760 &li.score[SC_PEARL], 10
765 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
766 &li.score[SC_CRYSTAL], 10
769 // (amoeba values used by R'n'D game engine only)
772 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
773 &li.amoeba_content, EL_DIAMOND
777 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
782 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
783 &li.grow_into_diggable, TRUE
785 // (amoeba values used by BD game engine only)
788 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
789 &li.bd_amoeba_wait_for_hatching, FALSE
793 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
794 &li.bd_amoeba_start_immediately, TRUE
798 TYPE_BOOLEAN, CONF_VALUE_8_BIT(4),
799 &li.bd_amoeba_2_explode_by_amoeba, TRUE
803 TYPE_INTEGER, CONF_VALUE_16_BIT(3),
804 &li.bd_amoeba_threshold_too_big, 200
808 TYPE_INTEGER, CONF_VALUE_16_BIT(4),
809 &li.bd_amoeba_slow_growth_time, 200
813 TYPE_INTEGER, CONF_VALUE_8_BIT(5),
814 &li.bd_amoeba_slow_growth_rate, 3
818 TYPE_INTEGER, CONF_VALUE_8_BIT(6),
819 &li.bd_amoeba_fast_growth_rate, 25
823 TYPE_ELEMENT, CONF_VALUE_16_BIT(5),
824 &li.bd_amoeba_content_too_big, EL_BD_ROCK
828 TYPE_ELEMENT, CONF_VALUE_16_BIT(6),
829 &li.bd_amoeba_content_enclosed, EL_BD_DIAMOND
834 TYPE_INTEGER, CONF_VALUE_16_BIT(3),
835 &li.bd_amoeba_2_threshold_too_big, 200
839 TYPE_INTEGER, CONF_VALUE_16_BIT(4),
840 &li.bd_amoeba_2_slow_growth_time, 200
844 TYPE_INTEGER, CONF_VALUE_8_BIT(5),
845 &li.bd_amoeba_2_slow_growth_rate, 3
849 TYPE_INTEGER, CONF_VALUE_8_BIT(6),
850 &li.bd_amoeba_2_fast_growth_rate, 25
854 TYPE_ELEMENT, CONF_VALUE_16_BIT(5),
855 &li.bd_amoeba_2_content_too_big, EL_BD_ROCK
859 TYPE_ELEMENT, CONF_VALUE_16_BIT(6),
860 &li.bd_amoeba_2_content_enclosed, EL_BD_DIAMOND
864 TYPE_ELEMENT, CONF_VALUE_16_BIT(7),
865 &li.bd_amoeba_2_content_exploding, EL_EMPTY
869 TYPE_ELEMENT, CONF_VALUE_16_BIT(8),
870 &li.bd_amoeba_2_content_looks_like, EL_BD_AMOEBA_2
875 TYPE_CONTENT_LIST, CONF_VALUE_BYTES(1),
876 &li.yamyam_content, EL_ROCK, NULL,
877 &li.num_yamyam_contents, 4, MAX_ELEMENT_CONTENTS
881 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
882 &li.score[SC_YAMYAM], 10
887 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
888 &li.score[SC_ROBOT], 10
892 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
898 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
904 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
905 &li.time_magic_wall, 10
910 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
911 &li.game_of_life[0], 2
915 TYPE_INTEGER, CONF_VALUE_8_BIT(2),
916 &li.game_of_life[1], 3
920 TYPE_INTEGER, CONF_VALUE_8_BIT(3),
921 &li.game_of_life[2], 3
925 TYPE_INTEGER, CONF_VALUE_8_BIT(4),
926 &li.game_of_life[3], 3
930 TYPE_BOOLEAN, CONF_VALUE_8_BIT(5),
931 &li.use_life_bugs, FALSE
936 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
941 TYPE_INTEGER, CONF_VALUE_8_BIT(2),
946 TYPE_INTEGER, CONF_VALUE_8_BIT(3),
951 TYPE_INTEGER, CONF_VALUE_8_BIT(4),
956 EL_TIMEGATE_SWITCH, -1,
957 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
958 &li.time_timegate, 10
962 EL_LIGHT_SWITCH_ACTIVE, -1,
963 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
968 EL_SHIELD_NORMAL, -1,
969 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
970 &li.shield_normal_time, 10
973 EL_SHIELD_NORMAL, -1,
974 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
975 &li.score[SC_SHIELD], 10
979 EL_SHIELD_DEADLY, -1,
980 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
981 &li.shield_deadly_time, 10
984 EL_SHIELD_DEADLY, -1,
985 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
986 &li.score[SC_SHIELD], 10
991 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
996 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
997 &li.extra_time_score, 10
1001 EL_TIME_ORB_FULL, -1,
1002 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1003 &li.time_orb_time, 10
1006 EL_TIME_ORB_FULL, -1,
1007 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1008 &li.use_time_orb_bug, FALSE
1013 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1014 &li.use_spring_bug, FALSE
1019 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1020 &li.android_move_time, 10
1024 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
1025 &li.android_clone_time, 10
1028 EL_EMC_ANDROID, SAVE_CONF_NEVER,
1029 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(1),
1030 &li.android_clone_element[0], EL_EMPTY, NULL,
1031 &li.num_android_clone_elements, 1, MAX_ANDROID_ELEMENTS_OLD
1035 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(2),
1036 &li.android_clone_element[0], EL_EMPTY, NULL,
1037 &li.num_android_clone_elements, 1, MAX_ANDROID_ELEMENTS
1042 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1043 &li.lenses_score, 10
1047 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
1052 EL_EMC_MAGNIFIER, -1,
1053 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1054 &li.magnify_score, 10
1057 EL_EMC_MAGNIFIER, -1,
1058 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
1059 &li.magnify_time, 10
1063 EL_EMC_MAGIC_BALL, -1,
1064 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1068 EL_EMC_MAGIC_BALL, -1,
1069 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1070 &li.ball_random, FALSE
1073 EL_EMC_MAGIC_BALL, -1,
1074 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
1075 &li.ball_active_initial, FALSE
1078 EL_EMC_MAGIC_BALL, -1,
1079 TYPE_CONTENT_LIST, CONF_VALUE_BYTES(1),
1080 &li.ball_content, EL_EMPTY, NULL,
1081 &li.num_ball_contents, 4, MAX_ELEMENT_CONTENTS
1085 EL_SOKOBAN_FIELD_EMPTY, -1,
1086 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1087 &li.sb_fields_needed, TRUE
1091 EL_SOKOBAN_OBJECT, -1,
1092 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1093 &li.sb_objects_needed, TRUE
1098 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1099 &li.mm_laser_red, FALSE
1103 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
1104 &li.mm_laser_green, FALSE
1108 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
1109 &li.mm_laser_blue, TRUE
1114 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1115 &li.df_laser_red, TRUE
1119 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
1120 &li.df_laser_green, TRUE
1124 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
1125 &li.df_laser_blue, FALSE
1129 EL_MM_FUSE_ACTIVE, -1,
1130 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1131 &li.mm_time_fuse, 25
1135 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1136 &li.mm_time_bomb, 75
1140 EL_MM_GRAY_BALL, -1,
1141 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1142 &li.mm_time_ball, 75
1145 EL_MM_GRAY_BALL, -1,
1146 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
1147 &li.mm_ball_choice_mode, ANIM_RANDOM
1150 EL_MM_GRAY_BALL, -1,
1151 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(1),
1152 &li.mm_ball_content, EL_EMPTY, NULL,
1153 &li.num_mm_ball_contents, 8, MAX_MM_BALL_CONTENTS
1156 EL_MM_GRAY_BALL, -1,
1157 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
1158 &li.rotate_mm_ball_content, TRUE
1161 EL_MM_GRAY_BALL, -1,
1162 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
1163 &li.explode_mm_ball, FALSE
1167 EL_MM_STEEL_BLOCK, -1,
1168 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1169 &li.mm_time_block, 75
1172 EL_MM_LIGHTBALL, -1,
1173 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1174 &li.score[SC_ELEM_BONUS], 10
1184 static struct LevelFileConfigInfo chunk_config_NOTE[] =
1188 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
1189 &xx_envelope.xsize, MAX_ENVELOPE_XSIZE,
1193 TYPE_INTEGER, CONF_VALUE_8_BIT(2),
1194 &xx_envelope.ysize, MAX_ENVELOPE_YSIZE,
1199 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
1200 &xx_envelope.autowrap, FALSE
1204 TYPE_BOOLEAN, CONF_VALUE_8_BIT(4),
1205 &xx_envelope.centered, FALSE
1210 TYPE_STRING, CONF_VALUE_BYTES(1),
1211 &xx_envelope.text, -1, NULL,
1212 &xx_string_length_unused, -1, MAX_ENVELOPE_TEXT_LEN,
1213 &xx_default_string_empty[0]
1223 static struct LevelFileConfigInfo chunk_config_CUSX_base[] =
1227 TYPE_STRING, CONF_VALUE_BYTES(1),
1228 &xx_ei.description[0], -1,
1229 &yy_ei.description[0],
1230 &xx_string_length_unused, -1, MAX_ELEMENT_NAME_LEN,
1231 &xx_default_description[0]
1236 TYPE_BITFIELD, CONF_VALUE_32_BIT(1),
1237 &xx_ei.properties[EP_BITFIELD_BASE_NR], EP_BITMASK_BASE_DEFAULT,
1238 &yy_ei.properties[EP_BITFIELD_BASE_NR]
1240 #if ENABLE_RESERVED_CODE
1241 // (reserved for later use)
1244 TYPE_BITFIELD, CONF_VALUE_32_BIT(2),
1245 &xx_ei.properties[EP_BITFIELD_BASE_NR + 1], EP_BITMASK_DEFAULT,
1246 &yy_ei.properties[EP_BITFIELD_BASE_NR + 1]
1252 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1253 &xx_ei.use_gfx_element, FALSE,
1254 &yy_ei.use_gfx_element
1258 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
1259 &xx_ei.gfx_element_initial, EL_EMPTY_SPACE,
1260 &yy_ei.gfx_element_initial
1265 TYPE_BITFIELD, CONF_VALUE_8_BIT(2),
1266 &xx_ei.access_direction, MV_ALL_DIRECTIONS,
1267 &yy_ei.access_direction
1272 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
1273 &xx_ei.collect_score_initial, 10,
1274 &yy_ei.collect_score_initial
1278 TYPE_INTEGER, CONF_VALUE_16_BIT(3),
1279 &xx_ei.collect_count_initial, 1,
1280 &yy_ei.collect_count_initial
1285 TYPE_INTEGER, CONF_VALUE_16_BIT(4),
1286 &xx_ei.ce_value_fixed_initial, 0,
1287 &yy_ei.ce_value_fixed_initial
1291 TYPE_INTEGER, CONF_VALUE_16_BIT(5),
1292 &xx_ei.ce_value_random_initial, 0,
1293 &yy_ei.ce_value_random_initial
1297 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
1298 &xx_ei.use_last_ce_value, FALSE,
1299 &yy_ei.use_last_ce_value
1304 TYPE_INTEGER, CONF_VALUE_16_BIT(6),
1305 &xx_ei.push_delay_fixed, 8,
1306 &yy_ei.push_delay_fixed
1310 TYPE_INTEGER, CONF_VALUE_16_BIT(7),
1311 &xx_ei.push_delay_random, 8,
1312 &yy_ei.push_delay_random
1316 TYPE_INTEGER, CONF_VALUE_16_BIT(8),
1317 &xx_ei.drop_delay_fixed, 0,
1318 &yy_ei.drop_delay_fixed
1322 TYPE_INTEGER, CONF_VALUE_16_BIT(9),
1323 &xx_ei.drop_delay_random, 0,
1324 &yy_ei.drop_delay_random
1328 TYPE_INTEGER, CONF_VALUE_16_BIT(10),
1329 &xx_ei.move_delay_fixed, 0,
1330 &yy_ei.move_delay_fixed
1334 TYPE_INTEGER, CONF_VALUE_16_BIT(11),
1335 &xx_ei.move_delay_random, 0,
1336 &yy_ei.move_delay_random
1340 TYPE_INTEGER, CONF_VALUE_16_BIT(16),
1341 &xx_ei.step_delay_fixed, 0,
1342 &yy_ei.step_delay_fixed
1346 TYPE_INTEGER, CONF_VALUE_16_BIT(17),
1347 &xx_ei.step_delay_random, 0,
1348 &yy_ei.step_delay_random
1353 TYPE_BITFIELD, CONF_VALUE_32_BIT(3),
1354 &xx_ei.move_pattern, MV_ALL_DIRECTIONS,
1359 TYPE_BITFIELD, CONF_VALUE_8_BIT(4),
1360 &xx_ei.move_direction_initial, MV_START_AUTOMATIC,
1361 &yy_ei.move_direction_initial
1365 TYPE_INTEGER, CONF_VALUE_8_BIT(5),
1366 &xx_ei.move_stepsize, TILEX / 8,
1367 &yy_ei.move_stepsize
1372 TYPE_ELEMENT, CONF_VALUE_16_BIT(12),
1373 &xx_ei.move_enter_element, EL_EMPTY_SPACE,
1374 &yy_ei.move_enter_element
1378 TYPE_ELEMENT, CONF_VALUE_16_BIT(13),
1379 &xx_ei.move_leave_element, EL_EMPTY_SPACE,
1380 &yy_ei.move_leave_element
1384 TYPE_INTEGER, CONF_VALUE_8_BIT(6),
1385 &xx_ei.move_leave_type, LEAVE_TYPE_UNLIMITED,
1386 &yy_ei.move_leave_type
1391 TYPE_INTEGER, CONF_VALUE_8_BIT(7),
1392 &xx_ei.slippery_type, SLIPPERY_ANY_RANDOM,
1393 &yy_ei.slippery_type
1398 TYPE_INTEGER, CONF_VALUE_8_BIT(8),
1399 &xx_ei.explosion_type, EXPLODES_3X3,
1400 &yy_ei.explosion_type
1404 TYPE_INTEGER, CONF_VALUE_16_BIT(14),
1405 &xx_ei.explosion_delay, 16,
1406 &yy_ei.explosion_delay
1410 TYPE_INTEGER, CONF_VALUE_16_BIT(15),
1411 &xx_ei.ignition_delay, 8,
1412 &yy_ei.ignition_delay
1417 TYPE_CONTENT_LIST, CONF_VALUE_BYTES(2),
1418 &xx_ei.content, EL_EMPTY_SPACE,
1420 &xx_num_contents, 1, 1
1423 // ---------- "num_change_pages" must be the last entry ---------------------
1426 -1, SAVE_CONF_ALWAYS,
1427 TYPE_INTEGER, CONF_VALUE_8_BIT(9),
1428 &xx_ei.num_change_pages, 1,
1429 &yy_ei.num_change_pages
1440 static struct LevelFileConfigInfo chunk_config_CUSX_change[] =
1442 // ---------- "current_change_page" must be the first entry -----------------
1445 -1, SAVE_CONF_ALWAYS,
1446 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
1447 &xx_current_change_page, -1
1450 // ---------- (the remaining entries can be in any order) -------------------
1454 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
1455 &xx_change.can_change, FALSE
1460 TYPE_BITFIELD, CONF_VALUE_32_BIT(1),
1461 &xx_event_bits[0], 0
1465 TYPE_BITFIELD, CONF_VALUE_32_BIT(2),
1466 &xx_event_bits[1], 0
1471 TYPE_BITFIELD, CONF_VALUE_8_BIT(3),
1472 &xx_change.trigger_player, CH_PLAYER_ANY
1476 TYPE_BITFIELD, CONF_VALUE_8_BIT(4),
1477 &xx_change.trigger_side, CH_SIDE_ANY
1481 TYPE_BITFIELD, CONF_VALUE_32_BIT(3),
1482 &xx_change.trigger_page, CH_PAGE_ANY
1487 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
1488 &xx_change.target_element, EL_EMPTY_SPACE
1493 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
1494 &xx_change.delay_fixed, 0
1498 TYPE_INTEGER, CONF_VALUE_16_BIT(3),
1499 &xx_change.delay_random, 0
1503 TYPE_INTEGER, CONF_VALUE_16_BIT(4),
1504 &xx_change.delay_frames, FRAMES_PER_SECOND
1509 TYPE_ELEMENT, CONF_VALUE_16_BIT(5),
1510 &xx_change.initial_trigger_element, EL_EMPTY_SPACE
1515 TYPE_BOOLEAN, CONF_VALUE_8_BIT(6),
1516 &xx_change.explode, FALSE
1520 TYPE_BOOLEAN, CONF_VALUE_8_BIT(7),
1521 &xx_change.use_target_content, FALSE
1525 TYPE_BOOLEAN, CONF_VALUE_8_BIT(8),
1526 &xx_change.only_if_complete, FALSE
1530 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
1531 &xx_change.use_random_replace, FALSE
1535 TYPE_INTEGER, CONF_VALUE_8_BIT(10),
1536 &xx_change.random_percentage, 100
1540 TYPE_INTEGER, CONF_VALUE_8_BIT(11),
1541 &xx_change.replace_when, CP_WHEN_EMPTY
1546 TYPE_BOOLEAN, CONF_VALUE_8_BIT(12),
1547 &xx_change.has_action, FALSE
1551 TYPE_INTEGER, CONF_VALUE_8_BIT(13),
1552 &xx_change.action_type, CA_NO_ACTION
1556 TYPE_INTEGER, CONF_VALUE_8_BIT(14),
1557 &xx_change.action_mode, CA_MODE_UNDEFINED
1561 TYPE_INTEGER, CONF_VALUE_16_BIT(6),
1562 &xx_change.action_arg, CA_ARG_UNDEFINED
1567 TYPE_ELEMENT, CONF_VALUE_16_BIT(7),
1568 &xx_change.action_element, EL_EMPTY_SPACE
1573 TYPE_CONTENT_LIST, CONF_VALUE_BYTES(1),
1574 &xx_change.target_content, EL_EMPTY_SPACE, NULL,
1575 &xx_num_contents, 1, 1
1585 static struct LevelFileConfigInfo chunk_config_GRPX[] =
1589 TYPE_STRING, CONF_VALUE_BYTES(1),
1590 &xx_ei.description[0], -1, NULL,
1591 &xx_string_length_unused, -1, MAX_ELEMENT_NAME_LEN,
1592 &xx_default_description[0]
1597 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1598 &xx_ei.use_gfx_element, FALSE
1602 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
1603 &xx_ei.gfx_element_initial, EL_EMPTY_SPACE
1608 TYPE_INTEGER, CONF_VALUE_8_BIT(2),
1609 &xx_group.choice_mode, ANIM_RANDOM
1614 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(2),
1615 &xx_group.element[0], EL_EMPTY_SPACE, NULL,
1616 &xx_group.num_elements, 1, MAX_ELEMENTS_IN_GROUP
1626 static struct LevelFileConfigInfo chunk_config_EMPX[] =
1630 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1631 &xx_ei.use_gfx_element, FALSE
1635 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
1636 &xx_ei.gfx_element_initial, EL_EMPTY_SPACE
1646 static struct LevelFileConfigInfo chunk_config_CONF[] = // (OBSOLETE)
1650 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
1651 &li.block_snap_field, TRUE
1655 TYPE_BOOLEAN, CONF_VALUE_8_BIT(13),
1656 &li.continuous_snapping, TRUE
1660 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
1661 &li.initial_player_stepsize[0], STEPSIZE_NORMAL
1665 TYPE_BOOLEAN, CONF_VALUE_8_BIT(10),
1666 &li.use_start_element[0], FALSE
1670 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
1671 &li.start_element[0], EL_PLAYER_1
1675 TYPE_BOOLEAN, CONF_VALUE_8_BIT(11),
1676 &li.use_artwork_element[0], FALSE
1680 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
1681 &li.artwork_element[0], EL_PLAYER_1
1685 TYPE_BOOLEAN, CONF_VALUE_8_BIT(12),
1686 &li.use_explosion_element[0], FALSE
1690 TYPE_ELEMENT, CONF_VALUE_16_BIT(3),
1691 &li.explosion_element[0], EL_PLAYER_1
1706 filetype_id_list[] =
1708 { LEVEL_FILE_TYPE_RND, "RND" },
1709 { LEVEL_FILE_TYPE_BD, "BD" },
1710 { LEVEL_FILE_TYPE_EM, "EM" },
1711 { LEVEL_FILE_TYPE_SP, "SP" },
1712 { LEVEL_FILE_TYPE_DX, "DX" },
1713 { LEVEL_FILE_TYPE_SB, "SB" },
1714 { LEVEL_FILE_TYPE_DC, "DC" },
1715 { LEVEL_FILE_TYPE_MM, "MM" },
1716 { LEVEL_FILE_TYPE_MM, "DF" },
1721 // ============================================================================
1722 // level file functions
1723 // ============================================================================
1725 static boolean check_special_flags(char *flag)
1727 if (strEqual(options.special_flags, flag) ||
1728 strEqual(leveldir_current->special_flags, flag))
1734 static struct DateInfo getCurrentDate(void)
1736 time_t epoch_seconds = time(NULL);
1737 struct tm *now = localtime(&epoch_seconds);
1738 struct DateInfo date;
1740 date.year = now->tm_year + 1900;
1741 date.month = now->tm_mon + 1;
1742 date.day = now->tm_mday;
1744 date.src = DATE_SRC_CLOCK;
1749 static void resetEventFlags(struct ElementChangeInfo *change)
1753 for (i = 0; i < NUM_CHANGE_EVENTS; i++)
1754 change->has_event[i] = FALSE;
1757 static void resetEventBits(void)
1761 for (i = 0; i < NUM_CE_BITFIELDS; i++)
1762 xx_event_bits[i] = 0;
1765 static void setEventFlagsFromEventBits(struct ElementChangeInfo *change)
1769 /* important: only change event flag if corresponding event bit is set
1770 (this is because all xx_event_bits[] values are loaded separately,
1771 and all xx_event_bits[] values are set back to zero before loading
1772 another value xx_event_bits[x] (each value representing 32 flags)) */
1774 for (i = 0; i < NUM_CHANGE_EVENTS; i++)
1775 if (xx_event_bits[CH_EVENT_BITFIELD_NR(i)] & CH_EVENT_BIT(i))
1776 change->has_event[i] = TRUE;
1779 static void setEventBitsFromEventFlags(struct ElementChangeInfo *change)
1783 /* in contrast to the above function setEventFlagsFromEventBits(), it
1784 would also be possible to set all bits in xx_event_bits[] to 0 or 1
1785 depending on the corresponding change->has_event[i] values here, as
1786 all xx_event_bits[] values are reset in resetEventBits() before */
1788 for (i = 0; i < NUM_CHANGE_EVENTS; i++)
1789 if (change->has_event[i])
1790 xx_event_bits[CH_EVENT_BITFIELD_NR(i)] |= CH_EVENT_BIT(i);
1793 static char *getDefaultElementDescription(struct ElementInfo *ei)
1795 static char description[MAX_ELEMENT_NAME_LEN + 1];
1796 char *default_description = (ei->custom_description != NULL ?
1797 ei->custom_description :
1798 ei->editor_description);
1801 // always start with reliable default values
1802 for (i = 0; i < MAX_ELEMENT_NAME_LEN + 1; i++)
1803 description[i] = '\0';
1805 // truncate element description to MAX_ELEMENT_NAME_LEN bytes
1806 strncpy(description, default_description, MAX_ELEMENT_NAME_LEN);
1808 return &description[0];
1811 static void setElementDescriptionToDefault(struct ElementInfo *ei)
1813 char *default_description = getDefaultElementDescription(ei);
1816 for (i = 0; i < MAX_ELEMENT_NAME_LEN + 1; i++)
1817 ei->description[i] = default_description[i];
1820 static void setConfigToDefaultsFromConfigList(struct LevelFileConfigInfo *conf)
1824 for (i = 0; conf[i].data_type != -1; i++)
1826 int default_value = conf[i].default_value;
1827 int data_type = conf[i].data_type;
1828 int conf_type = conf[i].conf_type;
1829 int byte_mask = conf_type & CONF_MASK_BYTES;
1831 if (byte_mask == CONF_MASK_MULTI_BYTES)
1833 int default_num_entities = conf[i].default_num_entities;
1834 int max_num_entities = conf[i].max_num_entities;
1836 *(int *)(conf[i].num_entities) = default_num_entities;
1838 if (data_type == TYPE_STRING)
1840 char *default_string = conf[i].default_string;
1841 char *string = (char *)(conf[i].value);
1843 strncpy(string, default_string, max_num_entities);
1845 else if (data_type == TYPE_ELEMENT_LIST)
1847 int *element_array = (int *)(conf[i].value);
1850 for (j = 0; j < max_num_entities; j++)
1851 element_array[j] = default_value;
1853 else if (data_type == TYPE_CONTENT_LIST)
1855 struct Content *content = (struct Content *)(conf[i].value);
1858 for (c = 0; c < max_num_entities; c++)
1859 for (y = 0; y < 3; y++)
1860 for (x = 0; x < 3; x++)
1861 content[c].e[x][y] = default_value;
1864 else // constant size configuration data (1, 2 or 4 bytes)
1866 if (data_type == TYPE_BOOLEAN)
1867 *(boolean *)(conf[i].value) = default_value;
1869 *(int *) (conf[i].value) = default_value;
1874 static void copyConfigFromConfigList(struct LevelFileConfigInfo *conf)
1878 for (i = 0; conf[i].data_type != -1; i++)
1880 int data_type = conf[i].data_type;
1881 int conf_type = conf[i].conf_type;
1882 int byte_mask = conf_type & CONF_MASK_BYTES;
1884 if (byte_mask == CONF_MASK_MULTI_BYTES)
1886 int max_num_entities = conf[i].max_num_entities;
1888 if (data_type == TYPE_STRING)
1890 char *string = (char *)(conf[i].value);
1891 char *string_copy = (char *)(conf[i].value_copy);
1893 strncpy(string_copy, string, max_num_entities);
1895 else if (data_type == TYPE_ELEMENT_LIST)
1897 int *element_array = (int *)(conf[i].value);
1898 int *element_array_copy = (int *)(conf[i].value_copy);
1901 for (j = 0; j < max_num_entities; j++)
1902 element_array_copy[j] = element_array[j];
1904 else if (data_type == TYPE_CONTENT_LIST)
1906 struct Content *content = (struct Content *)(conf[i].value);
1907 struct Content *content_copy = (struct Content *)(conf[i].value_copy);
1910 for (c = 0; c < max_num_entities; c++)
1911 for (y = 0; y < 3; y++)
1912 for (x = 0; x < 3; x++)
1913 content_copy[c].e[x][y] = content[c].e[x][y];
1916 else // constant size configuration data (1, 2 or 4 bytes)
1918 if (data_type == TYPE_BOOLEAN)
1919 *(boolean *)(conf[i].value_copy) = *(boolean *)(conf[i].value);
1921 *(int *) (conf[i].value_copy) = *(int *) (conf[i].value);
1926 void copyElementInfo(struct ElementInfo *ei_from, struct ElementInfo *ei_to)
1930 xx_ei = *ei_from; // copy element data into temporary buffer
1931 yy_ei = *ei_to; // copy element data into temporary buffer
1933 copyConfigFromConfigList(chunk_config_CUSX_base);
1938 // ---------- reinitialize and copy change pages ----------
1940 ei_to->num_change_pages = ei_from->num_change_pages;
1941 ei_to->current_change_page = ei_from->current_change_page;
1943 setElementChangePages(ei_to, ei_to->num_change_pages);
1945 for (i = 0; i < ei_to->num_change_pages; i++)
1946 ei_to->change_page[i] = ei_from->change_page[i];
1948 // ---------- copy group element info ----------
1949 if (ei_from->group != NULL && ei_to->group != NULL) // group or internal
1950 *ei_to->group = *ei_from->group;
1952 // mark this custom element as modified
1953 ei_to->modified_settings = TRUE;
1956 void setElementChangePages(struct ElementInfo *ei, int change_pages)
1958 int change_page_size = sizeof(struct ElementChangeInfo);
1960 ei->num_change_pages = MAX(1, change_pages);
1963 checked_realloc(ei->change_page, ei->num_change_pages * change_page_size);
1965 if (ei->current_change_page >= ei->num_change_pages)
1966 ei->current_change_page = ei->num_change_pages - 1;
1968 ei->change = &ei->change_page[ei->current_change_page];
1971 void setElementChangeInfoToDefaults(struct ElementChangeInfo *change)
1973 xx_change = *change; // copy change data into temporary buffer
1975 setConfigToDefaultsFromConfigList(chunk_config_CUSX_change);
1977 *change = xx_change;
1979 resetEventFlags(change);
1981 change->direct_action = 0;
1982 change->other_action = 0;
1984 change->pre_change_function = NULL;
1985 change->change_function = NULL;
1986 change->post_change_function = NULL;
1989 static void setLevelInfoToDefaults_Level(struct LevelInfo *level)
1993 li = *level; // copy level data into temporary buffer
1994 setConfigToDefaultsFromConfigList(chunk_config_INFO);
1995 *level = li; // copy temporary buffer back to level data
1997 setLevelInfoToDefaults_BD();
1998 setLevelInfoToDefaults_EM();
1999 setLevelInfoToDefaults_SP();
2000 setLevelInfoToDefaults_MM();
2002 level->native_bd_level = &native_bd_level;
2003 level->native_em_level = &native_em_level;
2004 level->native_sp_level = &native_sp_level;
2005 level->native_mm_level = &native_mm_level;
2007 level->file_version = FILE_VERSION_ACTUAL;
2008 level->game_version = GAME_VERSION_ACTUAL;
2010 level->creation_date = getCurrentDate();
2012 level->encoding_16bit_field = TRUE;
2013 level->encoding_16bit_yamyam = TRUE;
2014 level->encoding_16bit_amoeba = TRUE;
2016 // clear level name and level author string buffers
2017 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
2018 level->name[i] = '\0';
2019 for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
2020 level->author[i] = '\0';
2022 // set level name and level author to default values
2023 strcpy(level->name, NAMELESS_LEVEL_NAME);
2024 strcpy(level->author, ANONYMOUS_NAME);
2026 // set level playfield to playable default level with player and exit
2027 for (x = 0; x < MAX_LEV_FIELDX; x++)
2028 for (y = 0; y < MAX_LEV_FIELDY; y++)
2029 level->field[x][y] = EL_SAND;
2031 level->field[0][0] = EL_PLAYER_1;
2032 level->field[STD_LEV_FIELDX - 1][STD_LEV_FIELDY - 1] = EL_EXIT_CLOSED;
2034 BorderElement = EL_STEELWALL;
2036 // detect custom elements when loading them
2037 level->file_has_custom_elements = FALSE;
2039 // set all bug compatibility flags to "false" => do not emulate this bug
2040 level->use_action_after_change_bug = FALSE;
2042 if (leveldir_current)
2044 // try to determine better author name than 'anonymous'
2045 if (!strEqual(leveldir_current->author, ANONYMOUS_NAME))
2047 strncpy(level->author, leveldir_current->author, MAX_LEVEL_AUTHOR_LEN);
2048 level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
2052 switch (LEVELCLASS(leveldir_current))
2054 case LEVELCLASS_TUTORIAL:
2055 strcpy(level->author, PROGRAM_AUTHOR_STRING);
2058 case LEVELCLASS_CONTRIB:
2059 strncpy(level->author, leveldir_current->name, MAX_LEVEL_AUTHOR_LEN);
2060 level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
2063 case LEVELCLASS_PRIVATE:
2064 strncpy(level->author, getRealName(), MAX_LEVEL_AUTHOR_LEN);
2065 level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
2069 // keep default value
2076 static void setLevelInfoToDefaults_Elements(struct LevelInfo *level)
2078 static boolean clipboard_elements_initialized = FALSE;
2081 InitElementPropertiesStatic();
2083 li = *level; // copy level data into temporary buffer
2084 setConfigToDefaultsFromConfigList(chunk_config_ELEM);
2085 *level = li; // copy temporary buffer back to level data
2087 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2090 struct ElementInfo *ei = &element_info[element];
2092 if (element == EL_MM_GRAY_BALL)
2094 struct LevelInfo_MM *level_mm = level->native_mm_level;
2097 for (j = 0; j < level->num_mm_ball_contents; j++)
2098 level->mm_ball_content[j] =
2099 map_element_MM_to_RND(level_mm->ball_content[j]);
2102 // never initialize clipboard elements after the very first time
2103 // (to be able to use clipboard elements between several levels)
2104 if (IS_CLIPBOARD_ELEMENT(element) && clipboard_elements_initialized)
2107 if (IS_ENVELOPE(element))
2109 int envelope_nr = element - EL_ENVELOPE_1;
2111 setConfigToDefaultsFromConfigList(chunk_config_NOTE);
2113 level->envelope[envelope_nr] = xx_envelope;
2116 if (IS_CUSTOM_ELEMENT(element) ||
2117 IS_GROUP_ELEMENT(element) ||
2118 IS_INTERNAL_ELEMENT(element))
2120 xx_ei = *ei; // copy element data into temporary buffer
2122 setConfigToDefaultsFromConfigList(chunk_config_CUSX_base);
2127 setElementChangePages(ei, 1);
2128 setElementChangeInfoToDefaults(ei->change);
2130 if (IS_CUSTOM_ELEMENT(element) ||
2131 IS_GROUP_ELEMENT(element))
2133 setElementDescriptionToDefault(ei);
2135 ei->modified_settings = FALSE;
2138 if (IS_CUSTOM_ELEMENT(element) ||
2139 IS_INTERNAL_ELEMENT(element))
2141 // internal values used in level editor
2143 ei->access_type = 0;
2144 ei->access_layer = 0;
2145 ei->access_protected = 0;
2146 ei->walk_to_action = 0;
2147 ei->smash_targets = 0;
2150 ei->can_explode_by_fire = FALSE;
2151 ei->can_explode_smashed = FALSE;
2152 ei->can_explode_impact = FALSE;
2154 ei->current_change_page = 0;
2157 if (IS_GROUP_ELEMENT(element) ||
2158 IS_INTERNAL_ELEMENT(element))
2160 struct ElementGroupInfo *group;
2162 // initialize memory for list of elements in group
2163 if (ei->group == NULL)
2164 ei->group = checked_malloc(sizeof(struct ElementGroupInfo));
2168 xx_group = *group; // copy group data into temporary buffer
2170 setConfigToDefaultsFromConfigList(chunk_config_GRPX);
2175 if (IS_EMPTY_ELEMENT(element) ||
2176 IS_INTERNAL_ELEMENT(element))
2178 xx_ei = *ei; // copy element data into temporary buffer
2180 setConfigToDefaultsFromConfigList(chunk_config_EMPX);
2186 clipboard_elements_initialized = TRUE;
2189 static void setLevelInfoToDefaults(struct LevelInfo *level,
2190 boolean level_info_only,
2191 boolean reset_file_status)
2193 setLevelInfoToDefaults_Level(level);
2195 if (!level_info_only)
2196 setLevelInfoToDefaults_Elements(level);
2198 if (reset_file_status)
2200 level->no_valid_file = FALSE;
2201 level->no_level_file = FALSE;
2204 level->changed = FALSE;
2207 static void setFileInfoToDefaults(struct LevelFileInfo *level_file_info)
2209 level_file_info->nr = 0;
2210 level_file_info->type = LEVEL_FILE_TYPE_UNKNOWN;
2211 level_file_info->packed = FALSE;
2213 setString(&level_file_info->basename, NULL);
2214 setString(&level_file_info->filename, NULL);
2217 int getMappedElement_SB(int, boolean);
2219 static void ActivateLevelTemplate(void)
2223 if (check_special_flags("load_xsb_to_ces"))
2225 // fill smaller playfields with padding "beyond border wall" elements
2226 if (level.fieldx < level_template.fieldx ||
2227 level.fieldy < level_template.fieldy)
2229 short field[level.fieldx][level.fieldy];
2230 int new_fieldx = MAX(level.fieldx, level_template.fieldx);
2231 int new_fieldy = MAX(level.fieldy, level_template.fieldy);
2232 int pos_fieldx = (new_fieldx - level.fieldx) / 2;
2233 int pos_fieldy = (new_fieldy - level.fieldy) / 2;
2235 // copy old playfield (which is smaller than the visible area)
2236 for (y = 0; y < level.fieldy; y++) for (x = 0; x < level.fieldx; x++)
2237 field[x][y] = level.field[x][y];
2239 // fill new, larger playfield with "beyond border wall" elements
2240 for (y = 0; y < new_fieldy; y++) for (x = 0; x < new_fieldx; x++)
2241 level.field[x][y] = getMappedElement_SB('_', TRUE);
2243 // copy the old playfield to the middle of the new playfield
2244 for (y = 0; y < level.fieldy; y++) for (x = 0; x < level.fieldx; x++)
2245 level.field[pos_fieldx + x][pos_fieldy + y] = field[x][y];
2247 level.fieldx = new_fieldx;
2248 level.fieldy = new_fieldy;
2252 // Currently there is no special action needed to activate the template
2253 // data, because 'element_info' property settings overwrite the original
2254 // level data, while all other variables do not change.
2256 // Exception: 'from_level_template' elements in the original level playfield
2257 // are overwritten with the corresponding elements at the same position in
2258 // playfield from the level template.
2260 for (x = 0; x < level.fieldx; x++)
2261 for (y = 0; y < level.fieldy; y++)
2262 if (level.field[x][y] == EL_FROM_LEVEL_TEMPLATE)
2263 level.field[x][y] = level_template.field[x][y];
2265 if (check_special_flags("load_xsb_to_ces"))
2267 struct LevelInfo level_backup = level;
2269 // overwrite all individual level settings from template level settings
2270 level = level_template;
2272 // restore level file info
2273 level.file_info = level_backup.file_info;
2275 // restore playfield size
2276 level.fieldx = level_backup.fieldx;
2277 level.fieldy = level_backup.fieldy;
2279 // restore playfield content
2280 for (x = 0; x < level.fieldx; x++)
2281 for (y = 0; y < level.fieldy; y++)
2282 level.field[x][y] = level_backup.field[x][y];
2284 // restore name and author from individual level
2285 strcpy(level.name, level_backup.name);
2286 strcpy(level.author, level_backup.author);
2288 // restore flag "use_custom_template"
2289 level.use_custom_template = level_backup.use_custom_template;
2293 static boolean checkForPackageFromBasename_BD(char *basename)
2295 // check for native BD level file extensions
2296 if (!strSuffixLower(basename, ".bd") &&
2297 !strSuffixLower(basename, ".bdr") &&
2298 !strSuffixLower(basename, ".brc") &&
2299 !strSuffixLower(basename, ".gds"))
2302 // check for standard single-level BD files (like "001.bd")
2303 if (strSuffixLower(basename, ".bd") &&
2304 strlen(basename) == 6 &&
2305 basename[0] >= '0' && basename[0] <= '9' &&
2306 basename[1] >= '0' && basename[1] <= '9' &&
2307 basename[2] >= '0' && basename[2] <= '9')
2310 // this is a level package in native BD file format
2314 static char *getLevelFilenameFromBasename(char *basename)
2316 static char *filename = NULL;
2318 checked_free(filename);
2320 filename = getPath2(getCurrentLevelDir(), basename);
2325 static int getFileTypeFromBasename(char *basename)
2327 // !!! ALSO SEE COMMENT IN checkForPackageFromBasename() !!!
2329 static char *filename = NULL;
2330 struct stat file_status;
2332 // ---------- try to determine file type from filename ----------
2334 // check for typical filename of a Supaplex level package file
2335 if (strlen(basename) == 10 && strPrefixLower(basename, "levels.d"))
2336 return LEVEL_FILE_TYPE_SP;
2338 // check for typical filename of a Diamond Caves II level package file
2339 if (strSuffixLower(basename, ".dc") ||
2340 strSuffixLower(basename, ".dc2"))
2341 return LEVEL_FILE_TYPE_DC;
2343 // check for typical filename of a Sokoban level package file
2344 if (strSuffixLower(basename, ".xsb") &&
2345 strchr(basename, '%') == NULL)
2346 return LEVEL_FILE_TYPE_SB;
2348 // check for typical filename of a Boulder Dash (GDash) level package file
2349 if (checkForPackageFromBasename_BD(basename))
2350 return LEVEL_FILE_TYPE_BD;
2352 // ---------- try to determine file type from filesize ----------
2354 checked_free(filename);
2355 filename = getPath2(getCurrentLevelDir(), basename);
2357 if (stat(filename, &file_status) == 0)
2359 // check for typical filesize of a Supaplex level package file
2360 if (file_status.st_size == 170496)
2361 return LEVEL_FILE_TYPE_SP;
2364 return LEVEL_FILE_TYPE_UNKNOWN;
2367 static int getFileTypeFromMagicBytes(char *filename, int type)
2371 if ((file = openFile(filename, MODE_READ)))
2373 char chunk_name[CHUNK_ID_LEN + 1];
2375 getFileChunkBE(file, chunk_name, NULL);
2377 if (strEqual(chunk_name, "MMII") ||
2378 strEqual(chunk_name, "MIRR"))
2379 type = LEVEL_FILE_TYPE_MM;
2387 static boolean checkForPackageFromBasename(char *basename)
2389 // !!! WON'T WORK ANYMORE IF getFileTypeFromBasename() ALSO DETECTS !!!
2390 // !!! SINGLE LEVELS (CURRENTLY ONLY DETECTS LEVEL PACKAGES !!!
2392 return (getFileTypeFromBasename(basename) != LEVEL_FILE_TYPE_UNKNOWN);
2395 static char *getSingleLevelBasenameExt(int nr, char *extension)
2397 static char basename[MAX_FILENAME_LEN];
2400 sprintf(basename, "%s", LEVELTEMPLATE_FILENAME);
2402 sprintf(basename, "%03d.%s", nr, extension);
2407 static char *getSingleLevelBasename(int nr)
2409 return getSingleLevelBasenameExt(nr, LEVELFILE_EXTENSION);
2412 static char *getPackedLevelBasename(int type)
2414 static char basename[MAX_FILENAME_LEN];
2415 char *directory = getCurrentLevelDir();
2417 DirectoryEntry *dir_entry;
2419 strcpy(basename, UNDEFINED_FILENAME); // default: undefined file
2421 if ((dir = openDirectory(directory)) == NULL)
2423 Warn("cannot read current level directory '%s'", directory);
2428 while ((dir_entry = readDirectory(dir)) != NULL) // loop all entries
2430 char *entry_basename = dir_entry->basename;
2431 int entry_type = getFileTypeFromBasename(entry_basename);
2433 if (entry_type != LEVEL_FILE_TYPE_UNKNOWN) // found valid level package
2435 if (type == LEVEL_FILE_TYPE_UNKNOWN ||
2438 strcpy(basename, entry_basename);
2445 closeDirectory(dir);
2450 static char *getSingleLevelFilename(int nr)
2452 return getLevelFilenameFromBasename(getSingleLevelBasename(nr));
2455 #if ENABLE_UNUSED_CODE
2456 static char *getPackedLevelFilename(int type)
2458 return getLevelFilenameFromBasename(getPackedLevelBasename(type));
2462 char *getDefaultLevelFilename(int nr)
2464 return getSingleLevelFilename(nr);
2467 #if ENABLE_UNUSED_CODE
2468 static void setLevelFileInfo_SingleLevelFilename(struct LevelFileInfo *lfi,
2472 lfi->packed = FALSE;
2474 setString(&lfi->basename, getSingleLevelBasename(lfi->nr, lfi->type));
2475 setString(&lfi->filename, getLevelFilenameFromBasename(lfi->basename));
2479 static void setLevelFileInfo_FormatLevelFilename(struct LevelFileInfo *lfi,
2480 int type, char *format, ...)
2482 static char basename[MAX_FILENAME_LEN];
2485 va_start(ap, format);
2486 vsprintf(basename, format, ap);
2490 lfi->packed = FALSE;
2492 setString(&lfi->basename, basename);
2493 setString(&lfi->filename, getLevelFilenameFromBasename(lfi->basename));
2496 static void setLevelFileInfo_PackedLevelFilename(struct LevelFileInfo *lfi,
2502 setString(&lfi->basename, getPackedLevelBasename(lfi->type));
2503 setString(&lfi->filename, getLevelFilenameFromBasename(lfi->basename));
2506 static int getFiletypeFromID(char *filetype_id)
2508 char *filetype_id_lower;
2509 int filetype = LEVEL_FILE_TYPE_UNKNOWN;
2512 if (filetype_id == NULL)
2513 return LEVEL_FILE_TYPE_UNKNOWN;
2515 filetype_id_lower = getStringToLower(filetype_id);
2517 for (i = 0; filetype_id_list[i].id != NULL; i++)
2519 char *id_lower = getStringToLower(filetype_id_list[i].id);
2521 if (strEqual(filetype_id_lower, id_lower))
2522 filetype = filetype_id_list[i].filetype;
2526 if (filetype != LEVEL_FILE_TYPE_UNKNOWN)
2530 free(filetype_id_lower);
2535 char *getLocalLevelTemplateFilename(void)
2537 return getDefaultLevelFilename(-1);
2540 char *getGlobalLevelTemplateFilename(void)
2542 // global variable "leveldir_current" must be modified in the loop below
2543 LevelDirTree *leveldir_current_last = leveldir_current;
2544 char *filename = NULL;
2546 // check for template level in path from current to topmost tree node
2548 while (leveldir_current != NULL)
2550 filename = getDefaultLevelFilename(-1);
2552 if (fileExists(filename))
2555 leveldir_current = leveldir_current->node_parent;
2558 // restore global variable "leveldir_current" modified in above loop
2559 leveldir_current = leveldir_current_last;
2564 static void determineLevelFileInfo_Filename(struct LevelFileInfo *lfi)
2568 // special case: level number is negative => check for level template file
2571 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
2572 getSingleLevelBasename(-1));
2574 // replace local level template filename with global template filename
2575 setString(&lfi->filename, getGlobalLevelTemplateFilename());
2577 // no fallback if template file not existing
2581 // special case: check for file name/pattern specified in "levelinfo.conf"
2582 if (leveldir_current->level_filename != NULL)
2584 int filetype = getFiletypeFromID(leveldir_current->level_filetype);
2586 setLevelFileInfo_FormatLevelFilename(lfi, filetype,
2587 leveldir_current->level_filename, nr);
2589 lfi->packed = checkForPackageFromBasename(leveldir_current->level_filename);
2591 if (fileExists(lfi->filename))
2594 else if (leveldir_current->level_filetype != NULL)
2596 int filetype = getFiletypeFromID(leveldir_current->level_filetype);
2598 // check for specified native level file with standard file name
2599 setLevelFileInfo_FormatLevelFilename(lfi, filetype,
2600 "%03d.%s", nr, LEVELFILE_EXTENSION);
2601 if (fileExists(lfi->filename))
2605 // check for native Rocks'n'Diamonds level file
2606 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
2607 "%03d.%s", nr, LEVELFILE_EXTENSION);
2608 if (fileExists(lfi->filename))
2611 // check for native Boulder Dash level file
2612 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_BD, "%03d.bd", nr);
2613 if (fileExists(lfi->filename))
2616 // check for Emerald Mine level file (V1)
2617 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "a%c%c",
2618 'a' + (nr / 10) % 26, '0' + nr % 10);
2619 if (fileExists(lfi->filename))
2621 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "A%c%c",
2622 'A' + (nr / 10) % 26, '0' + nr % 10);
2623 if (fileExists(lfi->filename))
2626 // check for Emerald Mine level file (V2 to V5)
2627 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%d", nr);
2628 if (fileExists(lfi->filename))
2631 // check for Emerald Mine level file (V6 / single mode)
2632 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02ds", nr);
2633 if (fileExists(lfi->filename))
2635 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02dS", nr);
2636 if (fileExists(lfi->filename))
2639 // check for Emerald Mine level file (V6 / teamwork mode)
2640 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02dt", nr);
2641 if (fileExists(lfi->filename))
2643 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02dT", nr);
2644 if (fileExists(lfi->filename))
2647 // check for various packed level file formats
2648 setLevelFileInfo_PackedLevelFilename(lfi, LEVEL_FILE_TYPE_UNKNOWN);
2649 if (fileExists(lfi->filename))
2652 // no known level file found -- use default values (and fail later)
2653 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
2654 "%03d.%s", nr, LEVELFILE_EXTENSION);
2657 static void determineLevelFileInfo_Filetype(struct LevelFileInfo *lfi)
2659 if (lfi->type == LEVEL_FILE_TYPE_UNKNOWN)
2660 lfi->type = getFileTypeFromBasename(lfi->basename);
2662 if (lfi->type == LEVEL_FILE_TYPE_RND)
2663 lfi->type = getFileTypeFromMagicBytes(lfi->filename, lfi->type);
2666 static void setLevelFileInfo(struct LevelFileInfo *level_file_info, int nr)
2668 // always start with reliable default values
2669 setFileInfoToDefaults(level_file_info);
2671 level_file_info->nr = nr; // set requested level number
2673 determineLevelFileInfo_Filename(level_file_info);
2674 determineLevelFileInfo_Filetype(level_file_info);
2677 static void copyLevelFileInfo(struct LevelFileInfo *lfi_from,
2678 struct LevelFileInfo *lfi_to)
2680 lfi_to->nr = lfi_from->nr;
2681 lfi_to->type = lfi_from->type;
2682 lfi_to->packed = lfi_from->packed;
2684 setString(&lfi_to->basename, lfi_from->basename);
2685 setString(&lfi_to->filename, lfi_from->filename);
2688 // ----------------------------------------------------------------------------
2689 // functions for loading R'n'D level
2690 // ----------------------------------------------------------------------------
2692 int getMappedElement(int element)
2694 // remap some (historic, now obsolete) elements
2698 case EL_PLAYER_OBSOLETE:
2699 element = EL_PLAYER_1;
2702 case EL_KEY_OBSOLETE:
2706 case EL_EM_KEY_1_FILE_OBSOLETE:
2707 element = EL_EM_KEY_1;
2710 case EL_EM_KEY_2_FILE_OBSOLETE:
2711 element = EL_EM_KEY_2;
2714 case EL_EM_KEY_3_FILE_OBSOLETE:
2715 element = EL_EM_KEY_3;
2718 case EL_EM_KEY_4_FILE_OBSOLETE:
2719 element = EL_EM_KEY_4;
2722 case EL_ENVELOPE_OBSOLETE:
2723 element = EL_ENVELOPE_1;
2731 if (element >= NUM_FILE_ELEMENTS)
2733 Warn("invalid level element %d", element);
2735 element = EL_UNKNOWN;
2743 static int getMappedElementByVersion(int element, int game_version)
2745 // remap some elements due to certain game version
2747 if (game_version <= VERSION_IDENT(2,2,0,0))
2749 // map game font elements
2750 element = (element == EL_CHAR('[') ? EL_CHAR_AUMLAUT :
2751 element == EL_CHAR('\\') ? EL_CHAR_OUMLAUT :
2752 element == EL_CHAR(']') ? EL_CHAR_UUMLAUT :
2753 element == EL_CHAR('^') ? EL_CHAR_COPYRIGHT : element);
2756 if (game_version < VERSION_IDENT(3,0,0,0))
2758 // map Supaplex gravity tube elements
2759 element = (element == EL_SP_GRAVITY_PORT_LEFT ? EL_SP_PORT_LEFT :
2760 element == EL_SP_GRAVITY_PORT_RIGHT ? EL_SP_PORT_RIGHT :
2761 element == EL_SP_GRAVITY_PORT_UP ? EL_SP_PORT_UP :
2762 element == EL_SP_GRAVITY_PORT_DOWN ? EL_SP_PORT_DOWN :
2769 static int LoadLevel_VERS(File *file, int chunk_size, struct LevelInfo *level)
2771 level->file_version = getFileVersion(file);
2772 level->game_version = getFileVersion(file);
2777 static int LoadLevel_DATE(File *file, int chunk_size, struct LevelInfo *level)
2779 level->creation_date.year = getFile16BitBE(file);
2780 level->creation_date.month = getFile8Bit(file);
2781 level->creation_date.day = getFile8Bit(file);
2783 level->creation_date.src = DATE_SRC_LEVELFILE;
2788 static int LoadLevel_HEAD(File *file, int chunk_size, struct LevelInfo *level)
2790 int initial_player_stepsize;
2791 int initial_player_gravity;
2794 level->fieldx = getFile8Bit(file);
2795 level->fieldy = getFile8Bit(file);
2797 level->time = getFile16BitBE(file);
2798 level->gems_needed = getFile16BitBE(file);
2800 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
2801 level->name[i] = getFile8Bit(file);
2802 level->name[MAX_LEVEL_NAME_LEN] = 0;
2804 for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
2805 level->score[i] = getFile8Bit(file);
2807 level->num_yamyam_contents = STD_ELEMENT_CONTENTS;
2808 for (i = 0; i < STD_ELEMENT_CONTENTS; i++)
2809 for (y = 0; y < 3; y++)
2810 for (x = 0; x < 3; x++)
2811 level->yamyam_content[i].e[x][y] = getMappedElement(getFile8Bit(file));
2813 level->amoeba_speed = getFile8Bit(file);
2814 level->time_magic_wall = getFile8Bit(file);
2815 level->time_wheel = getFile8Bit(file);
2816 level->amoeba_content = getMappedElement(getFile8Bit(file));
2818 initial_player_stepsize = (getFile8Bit(file) == 1 ? STEPSIZE_FAST :
2821 for (i = 0; i < MAX_PLAYERS; i++)
2822 level->initial_player_stepsize[i] = initial_player_stepsize;
2824 initial_player_gravity = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2826 for (i = 0; i < MAX_PLAYERS; i++)
2827 level->initial_player_gravity[i] = initial_player_gravity;
2829 level->encoding_16bit_field = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2830 level->em_slippery_gems = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2832 level->use_custom_template = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2834 level->block_last_field = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2835 level->sp_block_last_field = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2836 level->can_move_into_acid_bits = getFile32BitBE(file);
2837 level->dont_collide_with_bits = getFile8Bit(file);
2839 level->use_spring_bug = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2840 level->use_step_counter = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2842 level->instant_relocation = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2843 level->can_pass_to_walkable = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2844 level->grow_into_diggable = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2846 level->game_engine_type = getFile8Bit(file);
2848 ReadUnusedBytesFromFile(file, LEVEL_CHUNK_HEAD_UNUSED);
2853 static int LoadLevel_NAME(File *file, int chunk_size, struct LevelInfo *level)
2857 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
2858 level->name[i] = getFile8Bit(file);
2859 level->name[MAX_LEVEL_NAME_LEN] = 0;
2864 static int LoadLevel_AUTH(File *file, int chunk_size, struct LevelInfo *level)
2868 for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
2869 level->author[i] = getFile8Bit(file);
2870 level->author[MAX_LEVEL_AUTHOR_LEN] = 0;
2875 static int LoadLevel_BODY(File *file, int chunk_size, struct LevelInfo *level)
2878 int chunk_size_expected = level->fieldx * level->fieldy;
2880 /* Note: "chunk_size" was wrong before version 2.0 when elements are
2881 stored with 16-bit encoding (and should be twice as big then).
2882 Even worse, playfield data was stored 16-bit when only yamyam content
2883 contained 16-bit elements and vice versa. */
2885 if (level->encoding_16bit_field && level->file_version >= FILE_VERSION_2_0)
2886 chunk_size_expected *= 2;
2888 if (chunk_size_expected != chunk_size)
2890 ReadUnusedBytesFromFile(file, chunk_size);
2891 return chunk_size_expected;
2894 for (y = 0; y < level->fieldy; y++)
2895 for (x = 0; x < level->fieldx; x++)
2896 level->field[x][y] =
2897 getMappedElement(level->encoding_16bit_field ? getFile16BitBE(file) :
2902 static int LoadLevel_CONT(File *file, int chunk_size, struct LevelInfo *level)
2905 int header_size = 4;
2906 int content_size = MAX_ELEMENT_CONTENTS * 3 * 3;
2907 int chunk_size_expected = header_size + content_size;
2909 /* Note: "chunk_size" was wrong before version 2.0 when elements are
2910 stored with 16-bit encoding (and should be twice as big then).
2911 Even worse, playfield data was stored 16-bit when only yamyam content
2912 contained 16-bit elements and vice versa. */
2914 if (level->encoding_16bit_field && level->file_version >= FILE_VERSION_2_0)
2915 chunk_size_expected += content_size;
2917 if (chunk_size_expected != chunk_size)
2919 ReadUnusedBytesFromFile(file, chunk_size);
2920 return chunk_size_expected;
2924 level->num_yamyam_contents = getFile8Bit(file);
2928 // correct invalid number of content fields -- should never happen
2929 if (level->num_yamyam_contents < 1 ||
2930 level->num_yamyam_contents > MAX_ELEMENT_CONTENTS)
2931 level->num_yamyam_contents = STD_ELEMENT_CONTENTS;
2933 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
2934 for (y = 0; y < 3; y++)
2935 for (x = 0; x < 3; x++)
2936 level->yamyam_content[i].e[x][y] =
2937 getMappedElement(level->encoding_16bit_field ?
2938 getFile16BitBE(file) : getFile8Bit(file));
2942 static int LoadLevel_CNT2(File *file, int chunk_size, struct LevelInfo *level)
2947 int content_array[MAX_ELEMENT_CONTENTS][3][3];
2949 element = getMappedElement(getFile16BitBE(file));
2950 num_contents = getFile8Bit(file);
2952 getFile8Bit(file); // content x size (unused)
2953 getFile8Bit(file); // content y size (unused)
2955 ReadUnusedBytesFromFile(file, LEVEL_CHUNK_CNT2_UNUSED);
2957 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
2958 for (y = 0; y < 3; y++)
2959 for (x = 0; x < 3; x++)
2960 content_array[i][x][y] = getMappedElement(getFile16BitBE(file));
2962 // correct invalid number of content fields -- should never happen
2963 if (num_contents < 1 || num_contents > MAX_ELEMENT_CONTENTS)
2964 num_contents = STD_ELEMENT_CONTENTS;
2966 if (element == EL_YAMYAM)
2968 level->num_yamyam_contents = num_contents;
2970 for (i = 0; i < num_contents; i++)
2971 for (y = 0; y < 3; y++)
2972 for (x = 0; x < 3; x++)
2973 level->yamyam_content[i].e[x][y] = content_array[i][x][y];
2975 else if (element == EL_BD_AMOEBA)
2977 level->amoeba_content = content_array[0][0][0];
2981 Warn("cannot load content for element '%d'", element);
2987 static int LoadLevel_CNT3(File *file, int chunk_size, struct LevelInfo *level)
2993 int chunk_size_expected;
2995 element = getMappedElement(getFile16BitBE(file));
2996 if (!IS_ENVELOPE(element))
2997 element = EL_ENVELOPE_1;
2999 envelope_nr = element - EL_ENVELOPE_1;
3001 envelope_len = getFile16BitBE(file);
3003 level->envelope[envelope_nr].xsize = getFile8Bit(file);
3004 level->envelope[envelope_nr].ysize = getFile8Bit(file);
3006 ReadUnusedBytesFromFile(file, LEVEL_CHUNK_CNT3_UNUSED);
3008 chunk_size_expected = LEVEL_CHUNK_CNT3_SIZE(envelope_len);
3009 if (chunk_size_expected != chunk_size)
3011 ReadUnusedBytesFromFile(file, chunk_size - LEVEL_CHUNK_CNT3_HEADER);
3012 return chunk_size_expected;
3015 for (i = 0; i < envelope_len; i++)
3016 level->envelope[envelope_nr].text[i] = getFile8Bit(file);
3021 static int LoadLevel_CUS1(File *file, int chunk_size, struct LevelInfo *level)
3023 int num_changed_custom_elements = getFile16BitBE(file);
3024 int chunk_size_expected = 2 + num_changed_custom_elements * 6;
3027 if (chunk_size_expected != chunk_size)
3029 ReadUnusedBytesFromFile(file, chunk_size - 2);
3030 return chunk_size_expected;
3033 for (i = 0; i < num_changed_custom_elements; i++)
3035 int element = getMappedElement(getFile16BitBE(file));
3036 int properties = getFile32BitBE(file);
3038 if (IS_CUSTOM_ELEMENT(element))
3039 element_info[element].properties[EP_BITFIELD_BASE_NR] = properties;
3041 Warn("invalid custom element number %d", element);
3043 // older game versions that wrote level files with CUS1 chunks used
3044 // different default push delay values (not yet stored in level file)
3045 element_info[element].push_delay_fixed = 2;
3046 element_info[element].push_delay_random = 8;
3049 level->file_has_custom_elements = TRUE;
3054 static int LoadLevel_CUS2(File *file, int chunk_size, struct LevelInfo *level)
3056 int num_changed_custom_elements = getFile16BitBE(file);
3057 int chunk_size_expected = 2 + num_changed_custom_elements * 4;
3060 if (chunk_size_expected != chunk_size)
3062 ReadUnusedBytesFromFile(file, chunk_size - 2);
3063 return chunk_size_expected;
3066 for (i = 0; i < num_changed_custom_elements; i++)
3068 int element = getMappedElement(getFile16BitBE(file));
3069 int custom_target_element = getMappedElement(getFile16BitBE(file));
3071 if (IS_CUSTOM_ELEMENT(element))
3072 element_info[element].change->target_element = custom_target_element;
3074 Warn("invalid custom element number %d", element);
3077 level->file_has_custom_elements = TRUE;
3082 static int LoadLevel_CUS3(File *file, int chunk_size, struct LevelInfo *level)
3084 int num_changed_custom_elements = getFile16BitBE(file);
3085 int chunk_size_expected = LEVEL_CHUNK_CUS3_SIZE(num_changed_custom_elements);
3088 if (chunk_size_expected != chunk_size)
3090 ReadUnusedBytesFromFile(file, chunk_size - 2);
3091 return chunk_size_expected;
3094 for (i = 0; i < num_changed_custom_elements; i++)
3096 int element = getMappedElement(getFile16BitBE(file));
3097 struct ElementInfo *ei = &element_info[element];
3098 unsigned int event_bits;
3100 if (!IS_CUSTOM_ELEMENT(element))
3102 Warn("invalid custom element number %d", element);
3104 element = EL_INTERNAL_DUMMY;
3107 for (j = 0; j < MAX_ELEMENT_NAME_LEN; j++)
3108 ei->description[j] = getFile8Bit(file);
3109 ei->description[MAX_ELEMENT_NAME_LEN] = 0;
3111 ei->properties[EP_BITFIELD_BASE_NR] = getFile32BitBE(file);
3113 // some free bytes for future properties and padding
3114 ReadUnusedBytesFromFile(file, 7);
3116 ei->use_gfx_element = getFile8Bit(file);
3117 ei->gfx_element_initial = getMappedElement(getFile16BitBE(file));
3119 ei->collect_score_initial = getFile8Bit(file);
3120 ei->collect_count_initial = getFile8Bit(file);
3122 ei->push_delay_fixed = getFile16BitBE(file);
3123 ei->push_delay_random = getFile16BitBE(file);
3124 ei->move_delay_fixed = getFile16BitBE(file);
3125 ei->move_delay_random = getFile16BitBE(file);
3127 ei->move_pattern = getFile16BitBE(file);
3128 ei->move_direction_initial = getFile8Bit(file);
3129 ei->move_stepsize = getFile8Bit(file);
3131 for (y = 0; y < 3; y++)
3132 for (x = 0; x < 3; x++)
3133 ei->content.e[x][y] = getMappedElement(getFile16BitBE(file));
3135 // bits 0 - 31 of "has_event[]"
3136 event_bits = getFile32BitBE(file);
3137 for (j = 0; j < MIN(NUM_CHANGE_EVENTS, 32); j++)
3138 if (event_bits & (1u << j))
3139 ei->change->has_event[j] = TRUE;
3141 ei->change->target_element = getMappedElement(getFile16BitBE(file));
3143 ei->change->delay_fixed = getFile16BitBE(file);
3144 ei->change->delay_random = getFile16BitBE(file);
3145 ei->change->delay_frames = getFile16BitBE(file);
3147 ei->change->initial_trigger_element = getMappedElement(getFile16BitBE(file));
3149 ei->change->explode = getFile8Bit(file);
3150 ei->change->use_target_content = getFile8Bit(file);
3151 ei->change->only_if_complete = getFile8Bit(file);
3152 ei->change->use_random_replace = getFile8Bit(file);
3154 ei->change->random_percentage = getFile8Bit(file);
3155 ei->change->replace_when = getFile8Bit(file);
3157 for (y = 0; y < 3; y++)
3158 for (x = 0; x < 3; x++)
3159 ei->change->target_content.e[x][y] =
3160 getMappedElement(getFile16BitBE(file));
3162 ei->slippery_type = getFile8Bit(file);
3164 // some free bytes for future properties and padding
3165 ReadUnusedBytesFromFile(file, LEVEL_CPART_CUS3_UNUSED);
3167 // mark that this custom element has been modified
3168 ei->modified_settings = TRUE;
3171 level->file_has_custom_elements = TRUE;
3176 static int LoadLevel_CUS4(File *file, int chunk_size, struct LevelInfo *level)
3178 struct ElementInfo *ei;
3179 int chunk_size_expected;
3183 // ---------- custom element base property values (96 bytes) ----------------
3185 element = getMappedElement(getFile16BitBE(file));
3187 if (!IS_CUSTOM_ELEMENT(element))
3189 Warn("invalid custom element number %d", element);
3191 ReadUnusedBytesFromFile(file, chunk_size - 2);
3196 ei = &element_info[element];
3198 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
3199 ei->description[i] = getFile8Bit(file);
3200 ei->description[MAX_ELEMENT_NAME_LEN] = 0;
3202 ei->properties[EP_BITFIELD_BASE_NR] = getFile32BitBE(file);
3204 ReadUnusedBytesFromFile(file, 4); // reserved for more base properties
3206 ei->num_change_pages = getFile8Bit(file);
3208 chunk_size_expected = LEVEL_CHUNK_CUS4_SIZE(ei->num_change_pages);
3209 if (chunk_size_expected != chunk_size)
3211 ReadUnusedBytesFromFile(file, chunk_size - 43);
3212 return chunk_size_expected;
3215 ei->ce_value_fixed_initial = getFile16BitBE(file);
3216 ei->ce_value_random_initial = getFile16BitBE(file);
3217 ei->use_last_ce_value = getFile8Bit(file);
3219 ei->use_gfx_element = getFile8Bit(file);
3220 ei->gfx_element_initial = getMappedElement(getFile16BitBE(file));
3222 ei->collect_score_initial = getFile8Bit(file);
3223 ei->collect_count_initial = getFile8Bit(file);
3225 ei->drop_delay_fixed = getFile8Bit(file);
3226 ei->push_delay_fixed = getFile8Bit(file);
3227 ei->drop_delay_random = getFile8Bit(file);
3228 ei->push_delay_random = getFile8Bit(file);
3229 ei->move_delay_fixed = getFile16BitBE(file);
3230 ei->move_delay_random = getFile16BitBE(file);
3232 // bits 0 - 15 of "move_pattern" ...
3233 ei->move_pattern = getFile16BitBE(file);
3234 ei->move_direction_initial = getFile8Bit(file);
3235 ei->move_stepsize = getFile8Bit(file);
3237 ei->slippery_type = getFile8Bit(file);
3239 for (y = 0; y < 3; y++)
3240 for (x = 0; x < 3; x++)
3241 ei->content.e[x][y] = getMappedElement(getFile16BitBE(file));
3243 ei->move_enter_element = getMappedElement(getFile16BitBE(file));
3244 ei->move_leave_element = getMappedElement(getFile16BitBE(file));
3245 ei->move_leave_type = getFile8Bit(file);
3247 // ... bits 16 - 31 of "move_pattern" (not nice, but downward compatible)
3248 ei->move_pattern |= (getFile16BitBE(file) << 16);
3250 ei->access_direction = getFile8Bit(file);
3252 ei->explosion_delay = getFile8Bit(file);
3253 ei->ignition_delay = getFile8Bit(file);
3254 ei->explosion_type = getFile8Bit(file);
3256 // some free bytes for future custom property values and padding
3257 ReadUnusedBytesFromFile(file, 1);
3259 // ---------- change page property values (48 bytes) ------------------------
3261 setElementChangePages(ei, ei->num_change_pages);
3263 for (i = 0; i < ei->num_change_pages; i++)
3265 struct ElementChangeInfo *change = &ei->change_page[i];
3266 unsigned int event_bits;
3268 // always start with reliable default values
3269 setElementChangeInfoToDefaults(change);
3271 // bits 0 - 31 of "has_event[]" ...
3272 event_bits = getFile32BitBE(file);
3273 for (j = 0; j < MIN(NUM_CHANGE_EVENTS, 32); j++)
3274 if (event_bits & (1u << j))
3275 change->has_event[j] = TRUE;
3277 change->target_element = getMappedElement(getFile16BitBE(file));
3279 change->delay_fixed = getFile16BitBE(file);
3280 change->delay_random = getFile16BitBE(file);
3281 change->delay_frames = getFile16BitBE(file);
3283 change->initial_trigger_element = getMappedElement(getFile16BitBE(file));
3285 change->explode = getFile8Bit(file);
3286 change->use_target_content = getFile8Bit(file);
3287 change->only_if_complete = getFile8Bit(file);
3288 change->use_random_replace = getFile8Bit(file);
3290 change->random_percentage = getFile8Bit(file);
3291 change->replace_when = getFile8Bit(file);
3293 for (y = 0; y < 3; y++)
3294 for (x = 0; x < 3; x++)
3295 change->target_content.e[x][y]= getMappedElement(getFile16BitBE(file));
3297 change->can_change = getFile8Bit(file);
3299 change->trigger_side = getFile8Bit(file);
3301 change->trigger_player = getFile8Bit(file);
3302 change->trigger_page = getFile8Bit(file);
3304 change->trigger_page = (change->trigger_page == CH_PAGE_ANY_FILE ?
3305 CH_PAGE_ANY : (1 << change->trigger_page));
3307 change->has_action = getFile8Bit(file);
3308 change->action_type = getFile8Bit(file);
3309 change->action_mode = getFile8Bit(file);
3310 change->action_arg = getFile16BitBE(file);
3312 // ... bits 32 - 39 of "has_event[]" (not nice, but downward compatible)
3313 event_bits = getFile8Bit(file);
3314 for (j = 32; j < NUM_CHANGE_EVENTS; j++)
3315 if (event_bits & (1u << (j - 32)))
3316 change->has_event[j] = TRUE;
3319 // mark this custom element as modified
3320 ei->modified_settings = TRUE;
3322 level->file_has_custom_elements = TRUE;
3327 static int LoadLevel_GRP1(File *file, int chunk_size, struct LevelInfo *level)
3329 struct ElementInfo *ei;
3330 struct ElementGroupInfo *group;
3334 element = getMappedElement(getFile16BitBE(file));
3336 if (!IS_GROUP_ELEMENT(element))
3338 Warn("invalid group element number %d", element);
3340 ReadUnusedBytesFromFile(file, chunk_size - 2);
3345 ei = &element_info[element];
3347 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
3348 ei->description[i] = getFile8Bit(file);
3349 ei->description[MAX_ELEMENT_NAME_LEN] = 0;
3351 group = element_info[element].group;
3353 group->num_elements = getFile8Bit(file);
3355 ei->use_gfx_element = getFile8Bit(file);
3356 ei->gfx_element_initial = getMappedElement(getFile16BitBE(file));
3358 group->choice_mode = getFile8Bit(file);
3360 // some free bytes for future values and padding
3361 ReadUnusedBytesFromFile(file, 3);
3363 for (i = 0; i < MAX_ELEMENTS_IN_GROUP; i++)
3364 group->element[i] = getMappedElement(getFile16BitBE(file));
3366 // mark this group element as modified
3367 element_info[element].modified_settings = TRUE;
3369 level->file_has_custom_elements = TRUE;
3374 static int LoadLevel_MicroChunk(File *file, struct LevelFileConfigInfo *conf,
3375 int element, int real_element)
3377 int micro_chunk_size = 0;
3378 int conf_type = getFile8Bit(file);
3379 int byte_mask = conf_type & CONF_MASK_BYTES;
3380 boolean element_found = FALSE;
3383 micro_chunk_size += 1;
3385 if (byte_mask == CONF_MASK_MULTI_BYTES)
3387 int num_bytes = getFile16BitBE(file);
3388 byte *buffer = checked_malloc(num_bytes);
3390 ReadBytesFromFile(file, buffer, num_bytes);
3392 for (i = 0; conf[i].data_type != -1; i++)
3394 if (conf[i].element == element &&
3395 conf[i].conf_type == conf_type)
3397 int data_type = conf[i].data_type;
3398 int num_entities = num_bytes / CONF_ENTITY_NUM_BYTES(data_type);
3399 int max_num_entities = conf[i].max_num_entities;
3401 if (num_entities > max_num_entities)
3403 Warn("truncating number of entities for element %d from %d to %d",
3404 element, num_entities, max_num_entities);
3406 num_entities = max_num_entities;
3409 if (num_entities == 0 && (data_type == TYPE_ELEMENT_LIST ||
3410 data_type == TYPE_CONTENT_LIST))
3412 // for element and content lists, zero entities are not allowed
3413 Warn("found empty list of entities for element %d", element);
3415 // do not set "num_entities" here to prevent reading behind buffer
3417 *(int *)(conf[i].num_entities) = 1; // at least one is required
3421 *(int *)(conf[i].num_entities) = num_entities;
3424 element_found = TRUE;
3426 if (data_type == TYPE_STRING)
3428 char *string = (char *)(conf[i].value);
3431 for (j = 0; j < max_num_entities; j++)
3432 string[j] = (j < num_entities ? buffer[j] : '\0');
3434 else if (data_type == TYPE_ELEMENT_LIST)
3436 int *element_array = (int *)(conf[i].value);
3439 for (j = 0; j < num_entities; j++)
3441 getMappedElement(CONF_ELEMENTS_ELEMENT(buffer, j));
3443 else if (data_type == TYPE_CONTENT_LIST)
3445 struct Content *content= (struct Content *)(conf[i].value);
3448 for (c = 0; c < num_entities; c++)
3449 for (y = 0; y < 3; y++)
3450 for (x = 0; x < 3; x++)
3451 content[c].e[x][y] =
3452 getMappedElement(CONF_CONTENTS_ELEMENT(buffer, c, x, y));
3455 element_found = FALSE;
3461 checked_free(buffer);
3463 micro_chunk_size += 2 + num_bytes;
3465 else // constant size configuration data (1, 2 or 4 bytes)
3467 int value = (byte_mask == CONF_MASK_1_BYTE ? getFile8Bit (file) :
3468 byte_mask == CONF_MASK_2_BYTE ? getFile16BitBE(file) :
3469 byte_mask == CONF_MASK_4_BYTE ? getFile32BitBE(file) : 0);
3471 for (i = 0; conf[i].data_type != -1; i++)
3473 if (conf[i].element == element &&
3474 conf[i].conf_type == conf_type)
3476 int data_type = conf[i].data_type;
3478 if (data_type == TYPE_ELEMENT)
3479 value = getMappedElement(value);
3481 if (data_type == TYPE_BOOLEAN)
3482 *(boolean *)(conf[i].value) = (value ? TRUE : FALSE);
3484 *(int *) (conf[i].value) = value;
3486 element_found = TRUE;
3492 micro_chunk_size += CONF_VALUE_NUM_BYTES(byte_mask);
3497 char *error_conf_chunk_bytes =
3498 (byte_mask == CONF_MASK_1_BYTE ? "CONF_VALUE_8_BIT" :
3499 byte_mask == CONF_MASK_2_BYTE ? "CONF_VALUE_16_BIT" :
3500 byte_mask == CONF_MASK_4_BYTE ? "CONF_VALUE_32_BIT" :"CONF_VALUE_BYTES");
3501 int error_conf_chunk_token = conf_type & CONF_MASK_TOKEN;
3502 int error_element = real_element;
3504 Warn("cannot load micro chunk '%s(%d)' value for element %d ['%s']",
3505 error_conf_chunk_bytes, error_conf_chunk_token,
3506 error_element, EL_NAME(error_element));
3509 return micro_chunk_size;
3512 static int LoadLevel_INFO(File *file, int chunk_size, struct LevelInfo *level)
3514 int real_chunk_size = 0;
3516 li = *level; // copy level data into temporary buffer
3518 while (!checkEndOfFile(file))
3520 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_INFO, -1, -1);
3522 if (real_chunk_size >= chunk_size)
3526 *level = li; // copy temporary buffer back to level data
3528 return real_chunk_size;
3531 static int LoadLevel_CONF(File *file, int chunk_size, struct LevelInfo *level)
3533 int real_chunk_size = 0;
3535 li = *level; // copy level data into temporary buffer
3537 while (!checkEndOfFile(file))
3539 int element = getMappedElement(getFile16BitBE(file));
3541 real_chunk_size += 2;
3542 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_CONF,
3544 if (real_chunk_size >= chunk_size)
3548 *level = li; // copy temporary buffer back to level data
3550 return real_chunk_size;
3553 static int LoadLevel_ELEM(File *file, int chunk_size, struct LevelInfo *level)
3555 int real_chunk_size = 0;
3557 li = *level; // copy level data into temporary buffer
3559 while (!checkEndOfFile(file))
3561 int element = getMappedElement(getFile16BitBE(file));
3563 real_chunk_size += 2;
3564 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_ELEM,
3566 if (real_chunk_size >= chunk_size)
3570 *level = li; // copy temporary buffer back to level data
3572 return real_chunk_size;
3575 static int LoadLevel_NOTE(File *file, int chunk_size, struct LevelInfo *level)
3577 int element = getMappedElement(getFile16BitBE(file));
3578 int envelope_nr = element - EL_ENVELOPE_1;
3579 int real_chunk_size = 2;
3581 xx_envelope = level->envelope[envelope_nr]; // copy into temporary buffer
3583 while (!checkEndOfFile(file))
3585 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_NOTE,
3588 if (real_chunk_size >= chunk_size)
3592 level->envelope[envelope_nr] = xx_envelope; // copy from temporary buffer
3594 return real_chunk_size;
3597 static int LoadLevel_CUSX(File *file, int chunk_size, struct LevelInfo *level)
3599 int element = getMappedElement(getFile16BitBE(file));
3600 int real_chunk_size = 2;
3601 struct ElementInfo *ei = &element_info[element];
3604 xx_ei = *ei; // copy element data into temporary buffer
3606 xx_ei.num_change_pages = -1;
3608 while (!checkEndOfFile(file))
3610 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_CUSX_base,
3612 if (xx_ei.num_change_pages != -1)
3615 if (real_chunk_size >= chunk_size)
3621 if (ei->num_change_pages == -1)
3623 Warn("LoadLevel_CUSX(): missing 'num_change_pages' for '%s'",
3626 ei->num_change_pages = 1;
3628 setElementChangePages(ei, 1);
3629 setElementChangeInfoToDefaults(ei->change);
3631 return real_chunk_size;
3634 // initialize number of change pages stored for this custom element
3635 setElementChangePages(ei, ei->num_change_pages);
3636 for (i = 0; i < ei->num_change_pages; i++)
3637 setElementChangeInfoToDefaults(&ei->change_page[i]);
3639 // start with reading properties for the first change page
3640 xx_current_change_page = 0;
3642 while (!checkEndOfFile(file))
3644 // level file might contain invalid change page number
3645 if (xx_current_change_page >= ei->num_change_pages)
3648 struct ElementChangeInfo *change = &ei->change_page[xx_current_change_page];
3650 xx_change = *change; // copy change data into temporary buffer
3652 resetEventBits(); // reset bits; change page might have changed
3654 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_CUSX_change,
3657 *change = xx_change;
3659 setEventFlagsFromEventBits(change);
3661 if (real_chunk_size >= chunk_size)
3665 level->file_has_custom_elements = TRUE;
3667 return real_chunk_size;
3670 static int LoadLevel_GRPX(File *file, int chunk_size, struct LevelInfo *level)
3672 int element = getMappedElement(getFile16BitBE(file));
3673 int real_chunk_size = 2;
3674 struct ElementInfo *ei = &element_info[element];
3675 struct ElementGroupInfo *group = ei->group;
3680 xx_ei = *ei; // copy element data into temporary buffer
3681 xx_group = *group; // copy group data into temporary buffer
3683 while (!checkEndOfFile(file))
3685 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_GRPX,
3688 if (real_chunk_size >= chunk_size)
3695 level->file_has_custom_elements = TRUE;
3697 return real_chunk_size;
3700 static int LoadLevel_EMPX(File *file, int chunk_size, struct LevelInfo *level)
3702 int element = getMappedElement(getFile16BitBE(file));
3703 int real_chunk_size = 2;
3704 struct ElementInfo *ei = &element_info[element];
3706 xx_ei = *ei; // copy element data into temporary buffer
3708 while (!checkEndOfFile(file))
3710 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_EMPX,
3713 if (real_chunk_size >= chunk_size)
3719 level->file_has_custom_elements = TRUE;
3721 return real_chunk_size;
3724 static void LoadLevelFromFileInfo_RND(struct LevelInfo *level,
3725 struct LevelFileInfo *level_file_info,
3726 boolean level_info_only)
3728 char *filename = level_file_info->filename;
3729 char cookie[MAX_LINE_LEN];
3730 char chunk_name[CHUNK_ID_LEN + 1];
3734 if (!(file = openFile(filename, MODE_READ)))
3736 level->no_valid_file = TRUE;
3737 level->no_level_file = TRUE;
3739 if (level_info_only)
3742 Warn("cannot read level '%s' -- using empty level", filename);
3744 if (!setup.editor.use_template_for_new_levels)
3747 // if level file not found, try to initialize level data from template
3748 filename = getGlobalLevelTemplateFilename();
3750 if (!(file = openFile(filename, MODE_READ)))
3753 // default: for empty levels, use level template for custom elements
3754 level->use_custom_template = TRUE;
3756 level->no_valid_file = FALSE;
3759 getFileChunkBE(file, chunk_name, NULL);
3760 if (strEqual(chunk_name, "RND1"))
3762 getFile32BitBE(file); // not used
3764 getFileChunkBE(file, chunk_name, NULL);
3765 if (!strEqual(chunk_name, "CAVE"))
3767 level->no_valid_file = TRUE;
3769 Warn("unknown format of level file '%s'", filename);
3776 else // check for pre-2.0 file format with cookie string
3778 strcpy(cookie, chunk_name);
3779 if (getStringFromFile(file, &cookie[4], MAX_LINE_LEN - 4) == NULL)
3781 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
3782 cookie[strlen(cookie) - 1] = '\0';
3784 if (!checkCookieString(cookie, LEVEL_COOKIE_TMPL))
3786 level->no_valid_file = TRUE;
3788 Warn("unknown format of level file '%s'", filename);
3795 if ((level->file_version = getFileVersionFromCookieString(cookie)) == -1)
3797 level->no_valid_file = TRUE;
3799 Warn("unsupported version of level file '%s'", filename);
3806 // pre-2.0 level files have no game version, so use file version here
3807 level->game_version = level->file_version;
3810 if (level->file_version < FILE_VERSION_1_2)
3812 // level files from versions before 1.2.0 without chunk structure
3813 LoadLevel_HEAD(file, LEVEL_CHUNK_HEAD_SIZE, level);
3814 LoadLevel_BODY(file, level->fieldx * level->fieldy, level);
3822 int (*loader)(File *, int, struct LevelInfo *);
3826 { "VERS", LEVEL_CHUNK_VERS_SIZE, LoadLevel_VERS },
3827 { "DATE", LEVEL_CHUNK_DATE_SIZE, LoadLevel_DATE },
3828 { "HEAD", LEVEL_CHUNK_HEAD_SIZE, LoadLevel_HEAD },
3829 { "NAME", LEVEL_CHUNK_NAME_SIZE, LoadLevel_NAME },
3830 { "AUTH", LEVEL_CHUNK_AUTH_SIZE, LoadLevel_AUTH },
3831 { "INFO", -1, LoadLevel_INFO },
3832 { "BODY", -1, LoadLevel_BODY },
3833 { "CONT", -1, LoadLevel_CONT },
3834 { "CNT2", LEVEL_CHUNK_CNT2_SIZE, LoadLevel_CNT2 },
3835 { "CNT3", -1, LoadLevel_CNT3 },
3836 { "CUS1", -1, LoadLevel_CUS1 },
3837 { "CUS2", -1, LoadLevel_CUS2 },
3838 { "CUS3", -1, LoadLevel_CUS3 },
3839 { "CUS4", -1, LoadLevel_CUS4 },
3840 { "GRP1", -1, LoadLevel_GRP1 },
3841 { "CONF", -1, LoadLevel_CONF },
3842 { "ELEM", -1, LoadLevel_ELEM },
3843 { "NOTE", -1, LoadLevel_NOTE },
3844 { "CUSX", -1, LoadLevel_CUSX },
3845 { "GRPX", -1, LoadLevel_GRPX },
3846 { "EMPX", -1, LoadLevel_EMPX },
3851 while (getFileChunkBE(file, chunk_name, &chunk_size))
3855 while (chunk_info[i].name != NULL &&
3856 !strEqual(chunk_name, chunk_info[i].name))
3859 if (chunk_info[i].name == NULL)
3861 Warn("unknown chunk '%s' in level file '%s'",
3862 chunk_name, filename);
3864 ReadUnusedBytesFromFile(file, chunk_size);
3866 else if (chunk_info[i].size != -1 &&
3867 chunk_info[i].size != chunk_size)
3869 Warn("wrong size (%d) of chunk '%s' in level file '%s'",
3870 chunk_size, chunk_name, filename);
3872 ReadUnusedBytesFromFile(file, chunk_size);
3876 // call function to load this level chunk
3877 int chunk_size_expected =
3878 (chunk_info[i].loader)(file, chunk_size, level);
3880 if (chunk_size_expected < 0)
3882 Warn("error reading chunk '%s' in level file '%s'",
3883 chunk_name, filename);
3888 // the size of some chunks cannot be checked before reading other
3889 // chunks first (like "HEAD" and "BODY") that contain some header
3890 // information, so check them here
3891 if (chunk_size_expected != chunk_size)
3893 Warn("wrong size (%d) of chunk '%s' in level file '%s'",
3894 chunk_size, chunk_name, filename);
3906 // ----------------------------------------------------------------------------
3907 // functions for loading BD level
3908 // ----------------------------------------------------------------------------
3910 static void CopyNativeLevel_RND_to_BD(struct LevelInfo *level)
3912 struct LevelInfo_BD *level_bd = level->native_bd_level;
3913 GdCave *cave = NULL; // will be changed below
3914 int cave_w = MIN(level->fieldx, MAX_PLAYFIELD_WIDTH);
3915 int cave_h = MIN(level->fieldy, MAX_PLAYFIELD_HEIGHT);
3918 setLevelInfoToDefaults_BD_Ext(cave_w, cave_h);
3920 // cave and map newly allocated when set to defaults above
3921 cave = level_bd->cave;
3924 cave->intermission = level->bd_intermission;
3927 cave->level_time[0] = level->time;
3928 cave->level_diamonds[0] = level->gems_needed;
3931 cave->scheduling = level->bd_scheduling_type;
3932 cave->pal_timing = level->bd_pal_timing;
3933 cave->level_speed[0] = level->bd_cycle_delay_ms;
3934 cave->level_ckdelay[0] = level->bd_cycle_delay_c64;
3935 cave->level_hatching_delay_frame[0] = level->bd_hatching_delay_cycles;
3936 cave->level_hatching_delay_time[0] = level->bd_hatching_delay_seconds;
3939 cave->level_timevalue[0] = level->score[SC_TIME_BONUS];
3940 cave->diamond_value = level->score[SC_EMERALD];
3941 cave->extra_diamond_value = level->score[SC_DIAMOND_EXTRA];
3943 // compatibility settings
3944 cave->lineshift = level->bd_line_shifting_borders;
3945 cave->border_scan_first_and_last = level->bd_scan_first_and_last_row;
3946 cave->short_explosions = level->bd_short_explosions;
3947 cave->gravity_affects_all = level->bd_gravity_affects_all;
3949 // player properties
3950 cave->diagonal_movements = level->bd_diagonal_movements;
3951 cave->active_is_first_found = level->bd_topmost_player_active;
3952 cave->pushing_stone_prob = level->bd_pushing_prob * 10000;
3953 cave->pushing_stone_prob_sweet = level->bd_pushing_prob_with_sweet * 10000;
3954 cave->mega_stones_pushable_with_sweet = level->bd_push_mega_rock_with_sweet;
3955 cave->snap_element = map_element_RND_to_BD(level->bd_snap_element);
3957 // element properties
3958 cave->level_bonus_time[0] = level->bd_clock_extra_time;
3959 cave->voodoo_collects_diamonds = level->bd_voodoo_collects_diamonds;
3960 cave->voodoo_any_hurt_kills_player = level->bd_voodoo_hurt_kills_player;
3961 cave->voodoo_dies_by_stone = level->bd_voodoo_dies_by_rock;
3962 cave->voodoo_disappear_in_explosion = level->bd_voodoo_vanish_by_explosion;
3963 cave->level_penalty_time[0] = level->bd_voodoo_penalty_time;
3964 cave->level_magic_wall_time[0] = level->time_magic_wall;
3965 cave->magic_timer_wait_for_hatching = level->bd_magic_wall_wait_hatching;
3966 cave->magic_wall_stops_amoeba = level->bd_magic_wall_stops_amoeba;
3967 cave->amoeba_timer_wait_for_hatching = level->bd_amoeba_wait_for_hatching;
3968 cave->amoeba_timer_started_immediately= level->bd_amoeba_start_immediately;
3969 cave->amoeba_2_explodes_by_amoeba = level->bd_amoeba_2_explode_by_amoeba;
3970 cave->level_amoeba_threshold[0] = level->bd_amoeba_threshold_too_big;
3971 cave->level_amoeba_time[0] = level->bd_amoeba_slow_growth_time;
3972 cave->amoeba_growth_prob = level->bd_amoeba_slow_growth_rate * 10000;
3973 cave->amoeba_fast_growth_prob = level->bd_amoeba_fast_growth_rate * 10000;
3974 cave->level_amoeba_2_threshold[0] = level->bd_amoeba_2_threshold_too_big;
3975 cave->level_amoeba_2_time[0] = level->bd_amoeba_2_slow_growth_time;
3976 cave->amoeba_2_growth_prob = level->bd_amoeba_2_slow_growth_rate * 10000;
3977 cave->amoeba_2_fast_growth_prob = level->bd_amoeba_2_fast_growth_rate * 10000;
3979 cave->amoeba_too_big_effect = map_element_RND_to_BD(level->bd_amoeba_content_too_big);
3980 cave->amoeba_enclosed_effect = map_element_RND_to_BD(level->bd_amoeba_content_enclosed);
3981 cave->amoeba_2_too_big_effect = map_element_RND_to_BD(level->bd_amoeba_2_content_too_big);
3982 cave->amoeba_2_enclosed_effect = map_element_RND_to_BD(level->bd_amoeba_2_content_enclosed);
3983 cave->amoeba_2_explosion_effect = map_element_RND_to_BD(level->bd_amoeba_2_content_exploding);
3984 cave->amoeba_2_looks_like = map_element_RND_to_BD(level->bd_amoeba_2_content_looks_like);
3986 cave->slime_predictable = level->bd_slime_is_predictable;
3987 cave->level_slime_permeability[0] = level->bd_slime_permeability_rate * 10000;
3988 cave->level_slime_permeability_c64[0] = level->bd_slime_permeability_bits_c64;
3989 cave->level_slime_seed_c64[0] = level->bd_slime_random_seed_c64;
3992 strncpy(cave->name, level->name, sizeof(GdString));
3993 cave->name[sizeof(GdString) - 1] = '\0';
3995 // playfield elements
3996 for (x = 0; x < cave->w; x++)
3997 for (y = 0; y < cave->h; y++)
3998 cave->map[y][x] = map_element_RND_to_BD(level->field[x][y]);
4001 static void CopyNativeLevel_BD_to_RND(struct LevelInfo *level)
4003 struct LevelInfo_BD *level_bd = level->native_bd_level;
4004 GdCave *cave = level_bd->cave;
4005 int bd_level_nr = level_bd->level_nr;
4008 level->fieldx = MIN(cave->w, MAX_LEV_FIELDX);
4009 level->fieldy = MIN(cave->h, MAX_LEV_FIELDY);
4012 level->bd_intermission = cave->intermission;
4015 level->time = cave->level_time[bd_level_nr];
4016 level->gems_needed = cave->level_diamonds[bd_level_nr];
4019 level->bd_scheduling_type = cave->scheduling;
4020 level->bd_pal_timing = cave->pal_timing;
4021 level->bd_cycle_delay_ms = cave->level_speed[bd_level_nr];
4022 level->bd_cycle_delay_c64 = cave->level_ckdelay[bd_level_nr];
4023 level->bd_hatching_delay_cycles = cave->level_hatching_delay_frame[bd_level_nr];
4024 level->bd_hatching_delay_seconds = cave->level_hatching_delay_time[bd_level_nr];
4027 level->score[SC_TIME_BONUS] = cave->level_timevalue[bd_level_nr];
4028 level->score[SC_EMERALD] = cave->diamond_value;
4029 level->score[SC_DIAMOND_EXTRA] = cave->extra_diamond_value;
4031 // compatibility settings
4032 level->bd_line_shifting_borders = cave->lineshift;
4033 level->bd_scan_first_and_last_row = cave->border_scan_first_and_last;
4034 level->bd_short_explosions = cave->short_explosions;
4035 level->bd_gravity_affects_all = cave->gravity_affects_all;
4037 // player properties
4038 level->bd_diagonal_movements = cave->diagonal_movements;
4039 level->bd_topmost_player_active = cave->active_is_first_found;
4040 level->bd_pushing_prob = cave->pushing_stone_prob / 10000;
4041 level->bd_pushing_prob_with_sweet = cave->pushing_stone_prob_sweet / 10000;
4042 level->bd_push_mega_rock_with_sweet = cave->mega_stones_pushable_with_sweet;
4043 level->bd_snap_element = map_element_BD_to_RND(cave->snap_element);
4045 // element properties
4046 level->bd_clock_extra_time = cave->level_bonus_time[0];
4047 level->bd_voodoo_collects_diamonds = cave->voodoo_collects_diamonds;
4048 level->bd_voodoo_hurt_kills_player = cave->voodoo_any_hurt_kills_player;
4049 level->bd_voodoo_dies_by_rock = cave->voodoo_dies_by_stone;
4050 level->bd_voodoo_vanish_by_explosion = cave->voodoo_disappear_in_explosion;
4051 level->bd_voodoo_penalty_time = cave->level_penalty_time[0];
4052 level->time_magic_wall = cave->level_magic_wall_time[bd_level_nr];
4053 level->bd_magic_wall_wait_hatching = cave->magic_timer_wait_for_hatching;
4054 level->bd_magic_wall_stops_amoeba = cave->magic_wall_stops_amoeba;
4055 level->bd_amoeba_wait_for_hatching = cave->amoeba_timer_wait_for_hatching;
4056 level->bd_amoeba_start_immediately = cave->amoeba_timer_started_immediately;
4057 level->bd_amoeba_2_explode_by_amoeba = cave->amoeba_2_explodes_by_amoeba;
4058 level->bd_amoeba_threshold_too_big = cave->level_amoeba_threshold[0];
4059 level->bd_amoeba_slow_growth_time = cave->level_amoeba_time[0];
4060 level->bd_amoeba_slow_growth_rate = cave->amoeba_growth_prob / 10000;
4061 level->bd_amoeba_fast_growth_rate = cave->amoeba_fast_growth_prob / 10000;
4062 level->bd_amoeba_2_threshold_too_big = cave->level_amoeba_2_threshold[0];
4063 level->bd_amoeba_2_slow_growth_time = cave->level_amoeba_2_time[0];
4064 level->bd_amoeba_2_slow_growth_rate = cave->amoeba_2_growth_prob / 10000;
4065 level->bd_amoeba_2_fast_growth_rate = cave->amoeba_2_fast_growth_prob / 10000;
4067 level->bd_amoeba_content_too_big = map_element_BD_to_RND(cave->amoeba_too_big_effect);
4068 level->bd_amoeba_content_enclosed = map_element_BD_to_RND(cave->amoeba_enclosed_effect);
4069 level->bd_amoeba_2_content_too_big = map_element_BD_to_RND(cave->amoeba_2_too_big_effect);
4070 level->bd_amoeba_2_content_enclosed = map_element_BD_to_RND(cave->amoeba_2_enclosed_effect);
4071 level->bd_amoeba_2_content_exploding = map_element_BD_to_RND(cave->amoeba_2_explosion_effect);
4072 level->bd_amoeba_2_content_looks_like = map_element_BD_to_RND(cave->amoeba_2_looks_like);
4074 level->bd_slime_is_predictable = cave->slime_predictable;
4075 level->bd_slime_permeability_rate = cave->level_slime_permeability[0] / 10000;
4076 level->bd_slime_permeability_bits_c64 = cave->level_slime_permeability_c64[0];
4077 level->bd_slime_random_seed_c64 = cave->level_slime_seed_c64[0];
4080 char *cave_name = getStringPrint("%s / %d", cave->name, bd_level_nr + 1);
4082 strncpy(level->name, cave_name, MAX_LEVEL_NAME_LEN);
4083 level->name[MAX_LEVEL_NAME_LEN] = '\0';
4085 // playfield elements
4086 for (x = 0; x < level->fieldx; x++)
4087 for (y = 0; y < level->fieldy; y++)
4088 level->field[x][y] = map_element_BD_to_RND(cave->map[y][x]);
4090 checked_free(cave_name);
4093 static void setTapeInfoToDefaults(void);
4095 static void CopyNativeTape_BD_to_RND(struct LevelInfo *level)
4097 struct LevelInfo_BD *level_bd = level->native_bd_level;
4098 GdCave *cave = level_bd->cave;
4099 GdReplay *replay = level_bd->replay;
4105 // always start with reliable default values
4106 setTapeInfoToDefaults();
4108 tape.level_nr = level_nr; // (currently not used)
4109 tape.random_seed = replay->seed;
4111 TapeSetDateFromIsoDateString(replay->date);
4114 tape.pos[tape.counter].delay = 0;
4116 tape.bd_replay = TRUE;
4118 // all time calculations only used to display approximate tape time
4119 int cave_speed = cave->speed;
4120 int milliseconds_game = 0;
4121 int milliseconds_elapsed = 20;
4123 for (i = 0; i < replay->movements->len; i++)
4125 int replay_action = replay->movements->data[i];
4126 int tape_action = map_action_BD_to_RND(replay_action);
4127 byte action[MAX_TAPE_ACTIONS] = { tape_action };
4128 boolean success = 0;
4132 success = TapeAddAction(action);
4134 milliseconds_game += milliseconds_elapsed;
4136 if (milliseconds_game >= cave_speed)
4138 milliseconds_game -= cave_speed;
4145 tape.pos[tape.counter].delay = 0;
4146 tape.pos[tape.counter].action[0] = 0;
4150 Warn("BD replay truncated: size exceeds maximum tape size %d", MAX_TAPE_LEN);
4156 TapeHaltRecording();
4160 // ----------------------------------------------------------------------------
4161 // functions for loading EM level
4162 // ----------------------------------------------------------------------------
4164 static void CopyNativeLevel_RND_to_EM(struct LevelInfo *level)
4166 static int ball_xy[8][2] =
4177 struct LevelInfo_EM *level_em = level->native_em_level;
4178 struct CAVE *cav = level_em->cav;
4181 cav->width = MIN(level->fieldx, MAX_PLAYFIELD_WIDTH);
4182 cav->height = MIN(level->fieldy, MAX_PLAYFIELD_HEIGHT);
4184 cav->time_seconds = level->time;
4185 cav->gems_needed = level->gems_needed;
4187 cav->emerald_score = level->score[SC_EMERALD];
4188 cav->diamond_score = level->score[SC_DIAMOND];
4189 cav->alien_score = level->score[SC_ROBOT];
4190 cav->tank_score = level->score[SC_SPACESHIP];
4191 cav->bug_score = level->score[SC_BUG];
4192 cav->eater_score = level->score[SC_YAMYAM];
4193 cav->nut_score = level->score[SC_NUT];
4194 cav->dynamite_score = level->score[SC_DYNAMITE];
4195 cav->key_score = level->score[SC_KEY];
4196 cav->exit_score = level->score[SC_TIME_BONUS];
4198 cav->num_eater_arrays = level->num_yamyam_contents;
4200 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
4201 for (y = 0; y < 3; y++)
4202 for (x = 0; x < 3; x++)
4203 cav->eater_array[i][y * 3 + x] =
4204 map_element_RND_to_EM_cave(level->yamyam_content[i].e[x][y]);
4206 cav->amoeba_time = level->amoeba_speed;
4207 cav->wonderwall_time = level->time_magic_wall;
4208 cav->wheel_time = level->time_wheel;
4210 cav->android_move_time = level->android_move_time;
4211 cav->android_clone_time = level->android_clone_time;
4212 cav->ball_random = level->ball_random;
4213 cav->ball_active = level->ball_active_initial;
4214 cav->ball_time = level->ball_time;
4215 cav->num_ball_arrays = level->num_ball_contents;
4217 cav->lenses_score = level->lenses_score;
4218 cav->magnify_score = level->magnify_score;
4219 cav->slurp_score = level->slurp_score;
4221 cav->lenses_time = level->lenses_time;
4222 cav->magnify_time = level->magnify_time;
4224 cav->wind_time = 9999;
4225 cav->wind_direction =
4226 map_direction_RND_to_EM(level->wind_direction_initial);
4228 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
4229 for (j = 0; j < 8; j++)
4230 cav->ball_array[i][j] =
4231 map_element_RND_to_EM_cave(level->ball_content[i].
4232 e[ball_xy[j][0]][ball_xy[j][1]]);
4234 map_android_clone_elements_RND_to_EM(level);
4236 // first fill the complete playfield with the empty space element
4237 for (y = 0; y < EM_MAX_CAVE_HEIGHT; y++)
4238 for (x = 0; x < EM_MAX_CAVE_WIDTH; x++)
4239 cav->cave[x][y] = Cblank;
4241 // then copy the real level contents from level file into the playfield
4242 for (y = 0; y < cav->height; y++) for (x = 0; x < cav->width; x++)
4244 int new_element = map_element_RND_to_EM_cave(level->field[x][y]);
4246 if (level->field[x][y] == EL_AMOEBA_DEAD)
4247 new_element = map_element_RND_to_EM_cave(EL_AMOEBA_WET);
4249 cav->cave[x][y] = new_element;
4252 for (i = 0; i < MAX_PLAYERS; i++)
4254 cav->player_x[i] = -1;
4255 cav->player_y[i] = -1;
4258 // initialize player positions and delete players from the playfield
4259 for (y = 0; y < cav->height; y++) for (x = 0; x < cav->width; x++)
4261 if (IS_PLAYER_ELEMENT(level->field[x][y]))
4263 int player_nr = GET_PLAYER_NR(level->field[x][y]);
4265 cav->player_x[player_nr] = x;
4266 cav->player_y[player_nr] = y;
4268 cav->cave[x][y] = map_element_RND_to_EM_cave(EL_EMPTY);
4273 static void CopyNativeLevel_EM_to_RND(struct LevelInfo *level)
4275 static int ball_xy[8][2] =
4286 struct LevelInfo_EM *level_em = level->native_em_level;
4287 struct CAVE *cav = level_em->cav;
4290 level->fieldx = MIN(cav->width, MAX_LEV_FIELDX);
4291 level->fieldy = MIN(cav->height, MAX_LEV_FIELDY);
4293 level->time = cav->time_seconds;
4294 level->gems_needed = cav->gems_needed;
4296 sprintf(level->name, "Level %d", level->file_info.nr);
4298 level->score[SC_EMERALD] = cav->emerald_score;
4299 level->score[SC_DIAMOND] = cav->diamond_score;
4300 level->score[SC_ROBOT] = cav->alien_score;
4301 level->score[SC_SPACESHIP] = cav->tank_score;
4302 level->score[SC_BUG] = cav->bug_score;
4303 level->score[SC_YAMYAM] = cav->eater_score;
4304 level->score[SC_NUT] = cav->nut_score;
4305 level->score[SC_DYNAMITE] = cav->dynamite_score;
4306 level->score[SC_KEY] = cav->key_score;
4307 level->score[SC_TIME_BONUS] = cav->exit_score;
4309 level->num_yamyam_contents = cav->num_eater_arrays;
4311 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
4312 for (y = 0; y < 3; y++)
4313 for (x = 0; x < 3; x++)
4314 level->yamyam_content[i].e[x][y] =
4315 map_element_EM_to_RND_cave(cav->eater_array[i][y * 3 + x]);
4317 level->amoeba_speed = cav->amoeba_time;
4318 level->time_magic_wall = cav->wonderwall_time;
4319 level->time_wheel = cav->wheel_time;
4321 level->android_move_time = cav->android_move_time;
4322 level->android_clone_time = cav->android_clone_time;
4323 level->ball_random = cav->ball_random;
4324 level->ball_active_initial = cav->ball_active;
4325 level->ball_time = cav->ball_time;
4326 level->num_ball_contents = cav->num_ball_arrays;
4328 level->lenses_score = cav->lenses_score;
4329 level->magnify_score = cav->magnify_score;
4330 level->slurp_score = cav->slurp_score;
4332 level->lenses_time = cav->lenses_time;
4333 level->magnify_time = cav->magnify_time;
4335 level->wind_direction_initial =
4336 map_direction_EM_to_RND(cav->wind_direction);
4338 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
4339 for (j = 0; j < 8; j++)
4340 level->ball_content[i].e[ball_xy[j][0]][ball_xy[j][1]] =
4341 map_element_EM_to_RND_cave(cav->ball_array[i][j]);
4343 map_android_clone_elements_EM_to_RND(level);
4345 // convert the playfield (some elements need special treatment)
4346 for (y = 0; y < level->fieldy; y++) for (x = 0; x < level->fieldx; x++)
4348 int new_element = map_element_EM_to_RND_cave(cav->cave[x][y]);
4350 if (new_element == EL_AMOEBA_WET && level->amoeba_speed == 0)
4351 new_element = EL_AMOEBA_DEAD;
4353 level->field[x][y] = new_element;
4356 for (i = 0; i < MAX_PLAYERS; i++)
4358 // in case of all players set to the same field, use the first player
4359 int nr = MAX_PLAYERS - i - 1;
4360 int jx = cav->player_x[nr];
4361 int jy = cav->player_y[nr];
4363 if (jx != -1 && jy != -1)
4364 level->field[jx][jy] = EL_PLAYER_1 + nr;
4367 // time score is counted for each 10 seconds left in Emerald Mine levels
4368 level->time_score_base = 10;
4372 // ----------------------------------------------------------------------------
4373 // functions for loading SP level
4374 // ----------------------------------------------------------------------------
4376 static void CopyNativeLevel_RND_to_SP(struct LevelInfo *level)
4378 struct LevelInfo_SP *level_sp = level->native_sp_level;
4379 LevelInfoType *header = &level_sp->header;
4382 level_sp->width = level->fieldx;
4383 level_sp->height = level->fieldy;
4385 for (x = 0; x < level->fieldx; x++)
4386 for (y = 0; y < level->fieldy; y++)
4387 level_sp->playfield[x][y] = map_element_RND_to_SP(level->field[x][y]);
4389 header->InitialGravity = (level->initial_player_gravity[0] ? 1 : 0);
4391 for (i = 0; i < SP_LEVEL_NAME_LEN; i++)
4392 header->LevelTitle[i] = level->name[i];
4393 // !!! NO STRING TERMINATION IN SUPAPLEX VB CODE YET -- FIX THIS !!!
4395 header->InfotronsNeeded = level->gems_needed;
4397 header->SpecialPortCount = 0;
4399 for (x = 0; x < level->fieldx; x++) for (y = 0; y < level->fieldy; y++)
4401 boolean gravity_port_found = FALSE;
4402 boolean gravity_port_valid = FALSE;
4403 int gravity_port_flag;
4404 int gravity_port_base_element;
4405 int element = level->field[x][y];
4407 if (element >= EL_SP_GRAVITY_ON_PORT_RIGHT &&
4408 element <= EL_SP_GRAVITY_ON_PORT_UP)
4410 gravity_port_found = TRUE;
4411 gravity_port_valid = TRUE;
4412 gravity_port_flag = 1;
4413 gravity_port_base_element = EL_SP_GRAVITY_ON_PORT_RIGHT;
4415 else if (element >= EL_SP_GRAVITY_OFF_PORT_RIGHT &&
4416 element <= EL_SP_GRAVITY_OFF_PORT_UP)
4418 gravity_port_found = TRUE;
4419 gravity_port_valid = TRUE;
4420 gravity_port_flag = 0;
4421 gravity_port_base_element = EL_SP_GRAVITY_OFF_PORT_RIGHT;
4423 else if (element >= EL_SP_GRAVITY_PORT_RIGHT &&
4424 element <= EL_SP_GRAVITY_PORT_UP)
4426 // change R'n'D style gravity inverting special port to normal port
4427 // (there are no gravity inverting ports in native Supaplex engine)
4429 gravity_port_found = TRUE;
4430 gravity_port_valid = FALSE;
4431 gravity_port_base_element = EL_SP_GRAVITY_PORT_RIGHT;
4434 if (gravity_port_found)
4436 if (gravity_port_valid &&
4437 header->SpecialPortCount < SP_MAX_SPECIAL_PORTS)
4439 SpecialPortType *port = &header->SpecialPort[header->SpecialPortCount];
4441 port->PortLocation = (y * level->fieldx + x) * 2;
4442 port->Gravity = gravity_port_flag;
4444 element += EL_SP_GRAVITY_PORT_RIGHT - gravity_port_base_element;
4446 header->SpecialPortCount++;
4450 // change special gravity port to normal port
4452 element += EL_SP_PORT_RIGHT - gravity_port_base_element;
4455 level_sp->playfield[x][y] = element - EL_SP_START;
4460 static void CopyNativeLevel_SP_to_RND(struct LevelInfo *level)
4462 struct LevelInfo_SP *level_sp = level->native_sp_level;
4463 LevelInfoType *header = &level_sp->header;
4464 boolean num_invalid_elements = 0;
4467 level->fieldx = level_sp->width;
4468 level->fieldy = level_sp->height;
4470 for (x = 0; x < level->fieldx; x++)
4472 for (y = 0; y < level->fieldy; y++)
4474 int element_old = level_sp->playfield[x][y];
4475 int element_new = getMappedElement(map_element_SP_to_RND(element_old));
4477 if (element_new == EL_UNKNOWN)
4479 num_invalid_elements++;
4481 Debug("level:native:SP", "invalid element %d at position %d, %d",
4485 level->field[x][y] = element_new;
4489 if (num_invalid_elements > 0)
4490 Warn("found %d invalid elements%s", num_invalid_elements,
4491 (!options.debug ? " (use '--debug' for more details)" : ""));
4493 for (i = 0; i < MAX_PLAYERS; i++)
4494 level->initial_player_gravity[i] =
4495 (header->InitialGravity == 1 ? TRUE : FALSE);
4497 // skip leading spaces
4498 for (i = 0; i < SP_LEVEL_NAME_LEN; i++)
4499 if (header->LevelTitle[i] != ' ')
4503 for (j = 0; i < SP_LEVEL_NAME_LEN; i++, j++)
4504 level->name[j] = header->LevelTitle[i];
4505 level->name[j] = '\0';
4507 // cut trailing spaces
4509 if (level->name[j - 1] == ' ' && level->name[j] == '\0')
4510 level->name[j - 1] = '\0';
4512 level->gems_needed = header->InfotronsNeeded;
4514 for (i = 0; i < header->SpecialPortCount; i++)
4516 SpecialPortType *port = &header->SpecialPort[i];
4517 int port_location = port->PortLocation;
4518 int gravity = port->Gravity;
4519 int port_x, port_y, port_element;
4521 port_x = (port_location / 2) % level->fieldx;
4522 port_y = (port_location / 2) / level->fieldx;
4524 if (port_x < 0 || port_x >= level->fieldx ||
4525 port_y < 0 || port_y >= level->fieldy)
4527 Warn("special port position (%d, %d) out of bounds", port_x, port_y);
4532 port_element = level->field[port_x][port_y];
4534 if (port_element < EL_SP_GRAVITY_PORT_RIGHT ||
4535 port_element > EL_SP_GRAVITY_PORT_UP)
4537 Warn("no special port at position (%d, %d)", port_x, port_y);
4542 // change previous (wrong) gravity inverting special port to either
4543 // gravity enabling special port or gravity disabling special port
4544 level->field[port_x][port_y] +=
4545 (gravity == 1 ? EL_SP_GRAVITY_ON_PORT_RIGHT :
4546 EL_SP_GRAVITY_OFF_PORT_RIGHT) - EL_SP_GRAVITY_PORT_RIGHT;
4549 // change special gravity ports without database entries to normal ports
4550 for (x = 0; x < level->fieldx; x++)
4551 for (y = 0; y < level->fieldy; y++)
4552 if (level->field[x][y] >= EL_SP_GRAVITY_PORT_RIGHT &&
4553 level->field[x][y] <= EL_SP_GRAVITY_PORT_UP)
4554 level->field[x][y] += EL_SP_PORT_RIGHT - EL_SP_GRAVITY_PORT_RIGHT;
4556 level->time = 0; // no time limit
4557 level->amoeba_speed = 0;
4558 level->time_magic_wall = 0;
4559 level->time_wheel = 0;
4560 level->amoeba_content = EL_EMPTY;
4562 // original Supaplex does not use score values -- rate by playing time
4563 for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
4564 level->score[i] = 0;
4566 level->rate_time_over_score = TRUE;
4568 // there are no yamyams in supaplex levels
4569 for (i = 0; i < level->num_yamyam_contents; i++)
4570 for (x = 0; x < 3; x++)
4571 for (y = 0; y < 3; y++)
4572 level->yamyam_content[i].e[x][y] = EL_EMPTY;
4575 static void CopyNativeTape_RND_to_SP(struct LevelInfo *level)
4577 struct LevelInfo_SP *level_sp = level->native_sp_level;
4578 struct DemoInfo_SP *demo = &level_sp->demo;
4581 // always start with reliable default values
4582 demo->is_available = FALSE;
4585 if (TAPE_IS_EMPTY(tape))
4588 demo->level_nr = tape.level_nr; // (currently not used)
4590 level_sp->header.DemoRandomSeed = tape.random_seed;
4594 for (i = 0; i < tape.length; i++)
4596 int demo_action = map_key_RND_to_SP(tape.pos[i].action[0]);
4597 int demo_repeat = tape.pos[i].delay;
4598 int demo_entries = (demo_repeat + 15) / 16;
4600 if (demo->length + demo_entries >= SP_MAX_TAPE_LEN)
4602 Warn("tape truncated: size exceeds maximum SP demo size %d",
4608 for (j = 0; j < demo_repeat / 16; j++)
4609 demo->data[demo->length++] = 0xf0 | demo_action;
4611 if (demo_repeat % 16)
4612 demo->data[demo->length++] = ((demo_repeat % 16 - 1) << 4) | demo_action;
4615 demo->is_available = TRUE;
4618 static void CopyNativeTape_SP_to_RND(struct LevelInfo *level)
4620 struct LevelInfo_SP *level_sp = level->native_sp_level;
4621 struct DemoInfo_SP *demo = &level_sp->demo;
4622 char *filename = level->file_info.filename;
4625 // always start with reliable default values
4626 setTapeInfoToDefaults();
4628 if (!demo->is_available)
4631 tape.level_nr = demo->level_nr; // (currently not used)
4632 tape.random_seed = level_sp->header.DemoRandomSeed;
4634 TapeSetDateFromEpochSeconds(getFileTimestampEpochSeconds(filename));
4637 tape.pos[tape.counter].delay = 0;
4639 for (i = 0; i < demo->length; i++)
4641 int demo_action = demo->data[i] & 0x0f;
4642 int demo_repeat = (demo->data[i] & 0xf0) >> 4;
4643 int tape_action = map_key_SP_to_RND(demo_action);
4644 int tape_repeat = demo_repeat + 1;
4645 byte action[MAX_TAPE_ACTIONS] = { tape_action };
4646 boolean success = 0;
4649 for (j = 0; j < tape_repeat; j++)
4650 success = TapeAddAction(action);
4654 Warn("SP demo truncated: size exceeds maximum tape size %d",
4661 TapeHaltRecording();
4665 // ----------------------------------------------------------------------------
4666 // functions for loading MM level
4667 // ----------------------------------------------------------------------------
4669 static void CopyNativeLevel_RND_to_MM(struct LevelInfo *level)
4671 struct LevelInfo_MM *level_mm = level->native_mm_level;
4674 level_mm->fieldx = MIN(level->fieldx, MM_MAX_PLAYFIELD_WIDTH);
4675 level_mm->fieldy = MIN(level->fieldy, MM_MAX_PLAYFIELD_HEIGHT);
4677 level_mm->time = level->time;
4678 level_mm->kettles_needed = level->gems_needed;
4679 level_mm->auto_count_kettles = level->auto_count_gems;
4681 level_mm->mm_laser_red = level->mm_laser_red;
4682 level_mm->mm_laser_green = level->mm_laser_green;
4683 level_mm->mm_laser_blue = level->mm_laser_blue;
4685 level_mm->df_laser_red = level->df_laser_red;
4686 level_mm->df_laser_green = level->df_laser_green;
4687 level_mm->df_laser_blue = level->df_laser_blue;
4689 strcpy(level_mm->name, level->name);
4690 strcpy(level_mm->author, level->author);
4692 level_mm->score[SC_EMERALD] = level->score[SC_EMERALD];
4693 level_mm->score[SC_PACMAN] = level->score[SC_PACMAN];
4694 level_mm->score[SC_KEY] = level->score[SC_KEY];
4695 level_mm->score[SC_TIME_BONUS] = level->score[SC_TIME_BONUS];
4696 level_mm->score[SC_ELEM_BONUS] = level->score[SC_ELEM_BONUS];
4698 level_mm->amoeba_speed = level->amoeba_speed;
4699 level_mm->time_fuse = level->mm_time_fuse;
4700 level_mm->time_bomb = level->mm_time_bomb;
4701 level_mm->time_ball = level->mm_time_ball;
4702 level_mm->time_block = level->mm_time_block;
4704 level_mm->num_ball_contents = level->num_mm_ball_contents;
4705 level_mm->ball_choice_mode = level->mm_ball_choice_mode;
4706 level_mm->rotate_ball_content = level->rotate_mm_ball_content;
4707 level_mm->explode_ball = level->explode_mm_ball;
4709 for (i = 0; i < level->num_mm_ball_contents; i++)
4710 level_mm->ball_content[i] =
4711 map_element_RND_to_MM(level->mm_ball_content[i]);
4713 for (x = 0; x < level->fieldx; x++)
4714 for (y = 0; y < level->fieldy; y++)
4716 level_mm->field[x][y] = map_element_RND_to_MM(level->field[x][y]);
4719 static void CopyNativeLevel_MM_to_RND(struct LevelInfo *level)
4721 struct LevelInfo_MM *level_mm = level->native_mm_level;
4724 level->fieldx = MIN(level_mm->fieldx, MAX_LEV_FIELDX);
4725 level->fieldy = MIN(level_mm->fieldy, MAX_LEV_FIELDY);
4727 level->time = level_mm->time;
4728 level->gems_needed = level_mm->kettles_needed;
4729 level->auto_count_gems = level_mm->auto_count_kettles;
4731 level->mm_laser_red = level_mm->mm_laser_red;
4732 level->mm_laser_green = level_mm->mm_laser_green;
4733 level->mm_laser_blue = level_mm->mm_laser_blue;
4735 level->df_laser_red = level_mm->df_laser_red;
4736 level->df_laser_green = level_mm->df_laser_green;
4737 level->df_laser_blue = level_mm->df_laser_blue;
4739 strcpy(level->name, level_mm->name);
4741 // only overwrite author from 'levelinfo.conf' if author defined in level
4742 if (!strEqual(level_mm->author, ANONYMOUS_NAME))
4743 strcpy(level->author, level_mm->author);
4745 level->score[SC_EMERALD] = level_mm->score[SC_EMERALD];
4746 level->score[SC_PACMAN] = level_mm->score[SC_PACMAN];
4747 level->score[SC_KEY] = level_mm->score[SC_KEY];
4748 level->score[SC_TIME_BONUS] = level_mm->score[SC_TIME_BONUS];
4749 level->score[SC_ELEM_BONUS] = level_mm->score[SC_ELEM_BONUS];
4751 level->amoeba_speed = level_mm->amoeba_speed;
4752 level->mm_time_fuse = level_mm->time_fuse;
4753 level->mm_time_bomb = level_mm->time_bomb;
4754 level->mm_time_ball = level_mm->time_ball;
4755 level->mm_time_block = level_mm->time_block;
4757 level->num_mm_ball_contents = level_mm->num_ball_contents;
4758 level->mm_ball_choice_mode = level_mm->ball_choice_mode;
4759 level->rotate_mm_ball_content = level_mm->rotate_ball_content;
4760 level->explode_mm_ball = level_mm->explode_ball;
4762 for (i = 0; i < level->num_mm_ball_contents; i++)
4763 level->mm_ball_content[i] =
4764 map_element_MM_to_RND(level_mm->ball_content[i]);
4766 for (x = 0; x < level->fieldx; x++)
4767 for (y = 0; y < level->fieldy; y++)
4768 level->field[x][y] = map_element_MM_to_RND(level_mm->field[x][y]);
4772 // ----------------------------------------------------------------------------
4773 // functions for loading DC level
4774 // ----------------------------------------------------------------------------
4776 #define DC_LEVEL_HEADER_SIZE 344
4778 static unsigned short getDecodedWord_DC(unsigned short data_encoded,
4781 static int last_data_encoded;
4785 int diff_hi, diff_lo;
4786 int data_hi, data_lo;
4787 unsigned short data_decoded;
4791 last_data_encoded = 0;
4798 diff = data_encoded - last_data_encoded;
4799 diff_hi = diff & ~0xff;
4800 diff_lo = diff & 0xff;
4804 data_hi = diff_hi - (offset1 << 8) + (offset2 & 0xff00);
4805 data_lo = (diff_lo + (data_hi >> 16)) & 0x00ff;
4806 data_hi = data_hi & 0xff00;
4808 data_decoded = data_hi | data_lo;
4810 last_data_encoded = data_encoded;
4812 offset1 = (offset1 + 1) % 31;
4813 offset2 = offset2 & 0xff;
4815 return data_decoded;
4818 static int getMappedElement_DC(int element)
4826 // 0x0117 - 0x036e: (?)
4829 // 0x042d - 0x0684: (?)
4845 element = EL_CRYSTAL;
4848 case 0x0e77: // quicksand (boulder)
4849 element = EL_QUICKSAND_FAST_FULL;
4852 case 0x0e99: // slow quicksand (boulder)
4853 element = EL_QUICKSAND_FULL;
4857 element = EL_EM_EXIT_OPEN;
4861 element = EL_EM_EXIT_CLOSED;
4865 element = EL_EM_STEEL_EXIT_OPEN;
4869 element = EL_EM_STEEL_EXIT_CLOSED;
4872 case 0x0f4f: // dynamite (lit 1)
4873 element = EL_EM_DYNAMITE_ACTIVE;
4876 case 0x0f57: // dynamite (lit 2)
4877 element = EL_EM_DYNAMITE_ACTIVE;
4880 case 0x0f5f: // dynamite (lit 3)
4881 element = EL_EM_DYNAMITE_ACTIVE;
4884 case 0x0f67: // dynamite (lit 4)
4885 element = EL_EM_DYNAMITE_ACTIVE;
4892 element = EL_AMOEBA_WET;
4896 element = EL_AMOEBA_DROP;
4900 element = EL_DC_MAGIC_WALL;
4904 element = EL_SPACESHIP_UP;
4908 element = EL_SPACESHIP_DOWN;
4912 element = EL_SPACESHIP_LEFT;
4916 element = EL_SPACESHIP_RIGHT;
4920 element = EL_BUG_UP;
4924 element = EL_BUG_DOWN;
4928 element = EL_BUG_LEFT;
4932 element = EL_BUG_RIGHT;
4936 element = EL_MOLE_UP;
4940 element = EL_MOLE_DOWN;
4944 element = EL_MOLE_LEFT;
4948 element = EL_MOLE_RIGHT;
4956 element = EL_YAMYAM_UP;
4960 element = EL_SWITCHGATE_OPEN;
4964 element = EL_SWITCHGATE_CLOSED;
4968 element = EL_DC_SWITCHGATE_SWITCH_UP;
4972 element = EL_TIMEGATE_CLOSED;
4975 case 0x144c: // conveyor belt switch (green)
4976 element = EL_CONVEYOR_BELT_3_SWITCH_MIDDLE;
4979 case 0x144f: // conveyor belt switch (red)
4980 element = EL_CONVEYOR_BELT_1_SWITCH_MIDDLE;
4983 case 0x1452: // conveyor belt switch (blue)
4984 element = EL_CONVEYOR_BELT_4_SWITCH_MIDDLE;
4988 element = EL_CONVEYOR_BELT_3_MIDDLE;
4992 element = EL_CONVEYOR_BELT_3_LEFT;
4996 element = EL_CONVEYOR_BELT_3_RIGHT;
5000 element = EL_CONVEYOR_BELT_1_MIDDLE;
5004 element = EL_CONVEYOR_BELT_1_LEFT;
5008 element = EL_CONVEYOR_BELT_1_RIGHT;
5012 element = EL_CONVEYOR_BELT_4_MIDDLE;
5016 element = EL_CONVEYOR_BELT_4_LEFT;
5020 element = EL_CONVEYOR_BELT_4_RIGHT;
5024 element = EL_EXPANDABLE_WALL_HORIZONTAL;
5028 element = EL_EXPANDABLE_WALL_VERTICAL;
5032 element = EL_EXPANDABLE_WALL_ANY;
5035 case 0x14ce: // growing steel wall (left/right)
5036 element = EL_EXPANDABLE_STEELWALL_HORIZONTAL;
5039 case 0x14df: // growing steel wall (up/down)
5040 element = EL_EXPANDABLE_STEELWALL_VERTICAL;
5043 case 0x14e8: // growing steel wall (up/down/left/right)
5044 element = EL_EXPANDABLE_STEELWALL_ANY;
5048 element = EL_SHIELD_DEADLY;
5052 element = EL_EXTRA_TIME;
5060 element = EL_EMPTY_SPACE;
5063 case 0x1578: // quicksand (empty)
5064 element = EL_QUICKSAND_FAST_EMPTY;
5067 case 0x1579: // slow quicksand (empty)
5068 element = EL_QUICKSAND_EMPTY;
5078 element = EL_EM_DYNAMITE;
5081 case 0x15a1: // key (red)
5082 element = EL_EM_KEY_1;
5085 case 0x15a2: // key (yellow)
5086 element = EL_EM_KEY_2;
5089 case 0x15a3: // key (blue)
5090 element = EL_EM_KEY_4;
5093 case 0x15a4: // key (green)
5094 element = EL_EM_KEY_3;
5097 case 0x15a5: // key (white)
5098 element = EL_DC_KEY_WHITE;
5102 element = EL_WALL_SLIPPERY;
5109 case 0x15a8: // wall (not round)
5113 case 0x15a9: // (blue)
5114 element = EL_CHAR_A;
5117 case 0x15aa: // (blue)
5118 element = EL_CHAR_B;
5121 case 0x15ab: // (blue)
5122 element = EL_CHAR_C;
5125 case 0x15ac: // (blue)
5126 element = EL_CHAR_D;
5129 case 0x15ad: // (blue)
5130 element = EL_CHAR_E;
5133 case 0x15ae: // (blue)
5134 element = EL_CHAR_F;
5137 case 0x15af: // (blue)
5138 element = EL_CHAR_G;
5141 case 0x15b0: // (blue)
5142 element = EL_CHAR_H;
5145 case 0x15b1: // (blue)
5146 element = EL_CHAR_I;
5149 case 0x15b2: // (blue)
5150 element = EL_CHAR_J;
5153 case 0x15b3: // (blue)
5154 element = EL_CHAR_K;
5157 case 0x15b4: // (blue)
5158 element = EL_CHAR_L;
5161 case 0x15b5: // (blue)
5162 element = EL_CHAR_M;
5165 case 0x15b6: // (blue)
5166 element = EL_CHAR_N;
5169 case 0x15b7: // (blue)
5170 element = EL_CHAR_O;
5173 case 0x15b8: // (blue)
5174 element = EL_CHAR_P;
5177 case 0x15b9: // (blue)
5178 element = EL_CHAR_Q;
5181 case 0x15ba: // (blue)
5182 element = EL_CHAR_R;
5185 case 0x15bb: // (blue)
5186 element = EL_CHAR_S;
5189 case 0x15bc: // (blue)
5190 element = EL_CHAR_T;
5193 case 0x15bd: // (blue)
5194 element = EL_CHAR_U;
5197 case 0x15be: // (blue)
5198 element = EL_CHAR_V;
5201 case 0x15bf: // (blue)
5202 element = EL_CHAR_W;
5205 case 0x15c0: // (blue)
5206 element = EL_CHAR_X;
5209 case 0x15c1: // (blue)
5210 element = EL_CHAR_Y;
5213 case 0x15c2: // (blue)
5214 element = EL_CHAR_Z;
5217 case 0x15c3: // (blue)
5218 element = EL_CHAR_AUMLAUT;
5221 case 0x15c4: // (blue)
5222 element = EL_CHAR_OUMLAUT;
5225 case 0x15c5: // (blue)
5226 element = EL_CHAR_UUMLAUT;
5229 case 0x15c6: // (blue)
5230 element = EL_CHAR_0;
5233 case 0x15c7: // (blue)
5234 element = EL_CHAR_1;
5237 case 0x15c8: // (blue)
5238 element = EL_CHAR_2;
5241 case 0x15c9: // (blue)
5242 element = EL_CHAR_3;
5245 case 0x15ca: // (blue)
5246 element = EL_CHAR_4;
5249 case 0x15cb: // (blue)
5250 element = EL_CHAR_5;
5253 case 0x15cc: // (blue)
5254 element = EL_CHAR_6;
5257 case 0x15cd: // (blue)
5258 element = EL_CHAR_7;
5261 case 0x15ce: // (blue)
5262 element = EL_CHAR_8;
5265 case 0x15cf: // (blue)
5266 element = EL_CHAR_9;
5269 case 0x15d0: // (blue)
5270 element = EL_CHAR_PERIOD;
5273 case 0x15d1: // (blue)
5274 element = EL_CHAR_EXCLAM;
5277 case 0x15d2: // (blue)
5278 element = EL_CHAR_COLON;
5281 case 0x15d3: // (blue)
5282 element = EL_CHAR_LESS;
5285 case 0x15d4: // (blue)
5286 element = EL_CHAR_GREATER;
5289 case 0x15d5: // (blue)
5290 element = EL_CHAR_QUESTION;
5293 case 0x15d6: // (blue)
5294 element = EL_CHAR_COPYRIGHT;
5297 case 0x15d7: // (blue)
5298 element = EL_CHAR_UP;
5301 case 0x15d8: // (blue)
5302 element = EL_CHAR_DOWN;
5305 case 0x15d9: // (blue)
5306 element = EL_CHAR_BUTTON;
5309 case 0x15da: // (blue)
5310 element = EL_CHAR_PLUS;
5313 case 0x15db: // (blue)
5314 element = EL_CHAR_MINUS;
5317 case 0x15dc: // (blue)
5318 element = EL_CHAR_APOSTROPHE;
5321 case 0x15dd: // (blue)
5322 element = EL_CHAR_PARENLEFT;
5325 case 0x15de: // (blue)
5326 element = EL_CHAR_PARENRIGHT;
5329 case 0x15df: // (green)
5330 element = EL_CHAR_A;
5333 case 0x15e0: // (green)
5334 element = EL_CHAR_B;
5337 case 0x15e1: // (green)
5338 element = EL_CHAR_C;
5341 case 0x15e2: // (green)
5342 element = EL_CHAR_D;
5345 case 0x15e3: // (green)
5346 element = EL_CHAR_E;
5349 case 0x15e4: // (green)
5350 element = EL_CHAR_F;
5353 case 0x15e5: // (green)
5354 element = EL_CHAR_G;
5357 case 0x15e6: // (green)
5358 element = EL_CHAR_H;
5361 case 0x15e7: // (green)
5362 element = EL_CHAR_I;
5365 case 0x15e8: // (green)
5366 element = EL_CHAR_J;
5369 case 0x15e9: // (green)
5370 element = EL_CHAR_K;
5373 case 0x15ea: // (green)
5374 element = EL_CHAR_L;
5377 case 0x15eb: // (green)
5378 element = EL_CHAR_M;
5381 case 0x15ec: // (green)
5382 element = EL_CHAR_N;
5385 case 0x15ed: // (green)
5386 element = EL_CHAR_O;
5389 case 0x15ee: // (green)
5390 element = EL_CHAR_P;
5393 case 0x15ef: // (green)
5394 element = EL_CHAR_Q;
5397 case 0x15f0: // (green)
5398 element = EL_CHAR_R;
5401 case 0x15f1: // (green)
5402 element = EL_CHAR_S;
5405 case 0x15f2: // (green)
5406 element = EL_CHAR_T;
5409 case 0x15f3: // (green)
5410 element = EL_CHAR_U;
5413 case 0x15f4: // (green)
5414 element = EL_CHAR_V;
5417 case 0x15f5: // (green)
5418 element = EL_CHAR_W;
5421 case 0x15f6: // (green)
5422 element = EL_CHAR_X;
5425 case 0x15f7: // (green)
5426 element = EL_CHAR_Y;
5429 case 0x15f8: // (green)
5430 element = EL_CHAR_Z;
5433 case 0x15f9: // (green)
5434 element = EL_CHAR_AUMLAUT;
5437 case 0x15fa: // (green)
5438 element = EL_CHAR_OUMLAUT;
5441 case 0x15fb: // (green)
5442 element = EL_CHAR_UUMLAUT;
5445 case 0x15fc: // (green)
5446 element = EL_CHAR_0;
5449 case 0x15fd: // (green)
5450 element = EL_CHAR_1;
5453 case 0x15fe: // (green)
5454 element = EL_CHAR_2;
5457 case 0x15ff: // (green)
5458 element = EL_CHAR_3;
5461 case 0x1600: // (green)
5462 element = EL_CHAR_4;
5465 case 0x1601: // (green)
5466 element = EL_CHAR_5;
5469 case 0x1602: // (green)
5470 element = EL_CHAR_6;
5473 case 0x1603: // (green)
5474 element = EL_CHAR_7;
5477 case 0x1604: // (green)
5478 element = EL_CHAR_8;
5481 case 0x1605: // (green)
5482 element = EL_CHAR_9;
5485 case 0x1606: // (green)
5486 element = EL_CHAR_PERIOD;
5489 case 0x1607: // (green)
5490 element = EL_CHAR_EXCLAM;
5493 case 0x1608: // (green)
5494 element = EL_CHAR_COLON;
5497 case 0x1609: // (green)
5498 element = EL_CHAR_LESS;
5501 case 0x160a: // (green)
5502 element = EL_CHAR_GREATER;
5505 case 0x160b: // (green)
5506 element = EL_CHAR_QUESTION;
5509 case 0x160c: // (green)
5510 element = EL_CHAR_COPYRIGHT;
5513 case 0x160d: // (green)
5514 element = EL_CHAR_UP;
5517 case 0x160e: // (green)
5518 element = EL_CHAR_DOWN;
5521 case 0x160f: // (green)
5522 element = EL_CHAR_BUTTON;
5525 case 0x1610: // (green)
5526 element = EL_CHAR_PLUS;
5529 case 0x1611: // (green)
5530 element = EL_CHAR_MINUS;
5533 case 0x1612: // (green)
5534 element = EL_CHAR_APOSTROPHE;
5537 case 0x1613: // (green)
5538 element = EL_CHAR_PARENLEFT;
5541 case 0x1614: // (green)
5542 element = EL_CHAR_PARENRIGHT;
5545 case 0x1615: // (blue steel)
5546 element = EL_STEEL_CHAR_A;
5549 case 0x1616: // (blue steel)
5550 element = EL_STEEL_CHAR_B;
5553 case 0x1617: // (blue steel)
5554 element = EL_STEEL_CHAR_C;
5557 case 0x1618: // (blue steel)
5558 element = EL_STEEL_CHAR_D;
5561 case 0x1619: // (blue steel)
5562 element = EL_STEEL_CHAR_E;
5565 case 0x161a: // (blue steel)
5566 element = EL_STEEL_CHAR_F;
5569 case 0x161b: // (blue steel)
5570 element = EL_STEEL_CHAR_G;
5573 case 0x161c: // (blue steel)
5574 element = EL_STEEL_CHAR_H;
5577 case 0x161d: // (blue steel)
5578 element = EL_STEEL_CHAR_I;
5581 case 0x161e: // (blue steel)
5582 element = EL_STEEL_CHAR_J;
5585 case 0x161f: // (blue steel)
5586 element = EL_STEEL_CHAR_K;
5589 case 0x1620: // (blue steel)
5590 element = EL_STEEL_CHAR_L;
5593 case 0x1621: // (blue steel)
5594 element = EL_STEEL_CHAR_M;
5597 case 0x1622: // (blue steel)
5598 element = EL_STEEL_CHAR_N;
5601 case 0x1623: // (blue steel)
5602 element = EL_STEEL_CHAR_O;
5605 case 0x1624: // (blue steel)
5606 element = EL_STEEL_CHAR_P;
5609 case 0x1625: // (blue steel)
5610 element = EL_STEEL_CHAR_Q;
5613 case 0x1626: // (blue steel)
5614 element = EL_STEEL_CHAR_R;
5617 case 0x1627: // (blue steel)
5618 element = EL_STEEL_CHAR_S;
5621 case 0x1628: // (blue steel)
5622 element = EL_STEEL_CHAR_T;
5625 case 0x1629: // (blue steel)
5626 element = EL_STEEL_CHAR_U;
5629 case 0x162a: // (blue steel)
5630 element = EL_STEEL_CHAR_V;
5633 case 0x162b: // (blue steel)
5634 element = EL_STEEL_CHAR_W;
5637 case 0x162c: // (blue steel)
5638 element = EL_STEEL_CHAR_X;
5641 case 0x162d: // (blue steel)
5642 element = EL_STEEL_CHAR_Y;
5645 case 0x162e: // (blue steel)
5646 element = EL_STEEL_CHAR_Z;
5649 case 0x162f: // (blue steel)
5650 element = EL_STEEL_CHAR_AUMLAUT;
5653 case 0x1630: // (blue steel)
5654 element = EL_STEEL_CHAR_OUMLAUT;
5657 case 0x1631: // (blue steel)
5658 element = EL_STEEL_CHAR_UUMLAUT;
5661 case 0x1632: // (blue steel)
5662 element = EL_STEEL_CHAR_0;
5665 case 0x1633: // (blue steel)
5666 element = EL_STEEL_CHAR_1;
5669 case 0x1634: // (blue steel)
5670 element = EL_STEEL_CHAR_2;
5673 case 0x1635: // (blue steel)
5674 element = EL_STEEL_CHAR_3;
5677 case 0x1636: // (blue steel)
5678 element = EL_STEEL_CHAR_4;
5681 case 0x1637: // (blue steel)
5682 element = EL_STEEL_CHAR_5;
5685 case 0x1638: // (blue steel)
5686 element = EL_STEEL_CHAR_6;
5689 case 0x1639: // (blue steel)
5690 element = EL_STEEL_CHAR_7;
5693 case 0x163a: // (blue steel)
5694 element = EL_STEEL_CHAR_8;
5697 case 0x163b: // (blue steel)
5698 element = EL_STEEL_CHAR_9;
5701 case 0x163c: // (blue steel)
5702 element = EL_STEEL_CHAR_PERIOD;
5705 case 0x163d: // (blue steel)
5706 element = EL_STEEL_CHAR_EXCLAM;
5709 case 0x163e: // (blue steel)
5710 element = EL_STEEL_CHAR_COLON;
5713 case 0x163f: // (blue steel)
5714 element = EL_STEEL_CHAR_LESS;
5717 case 0x1640: // (blue steel)
5718 element = EL_STEEL_CHAR_GREATER;
5721 case 0x1641: // (blue steel)
5722 element = EL_STEEL_CHAR_QUESTION;
5725 case 0x1642: // (blue steel)
5726 element = EL_STEEL_CHAR_COPYRIGHT;
5729 case 0x1643: // (blue steel)
5730 element = EL_STEEL_CHAR_UP;
5733 case 0x1644: // (blue steel)
5734 element = EL_STEEL_CHAR_DOWN;
5737 case 0x1645: // (blue steel)
5738 element = EL_STEEL_CHAR_BUTTON;
5741 case 0x1646: // (blue steel)
5742 element = EL_STEEL_CHAR_PLUS;
5745 case 0x1647: // (blue steel)
5746 element = EL_STEEL_CHAR_MINUS;
5749 case 0x1648: // (blue steel)
5750 element = EL_STEEL_CHAR_APOSTROPHE;
5753 case 0x1649: // (blue steel)
5754 element = EL_STEEL_CHAR_PARENLEFT;
5757 case 0x164a: // (blue steel)
5758 element = EL_STEEL_CHAR_PARENRIGHT;
5761 case 0x164b: // (green steel)
5762 element = EL_STEEL_CHAR_A;
5765 case 0x164c: // (green steel)
5766 element = EL_STEEL_CHAR_B;
5769 case 0x164d: // (green steel)
5770 element = EL_STEEL_CHAR_C;
5773 case 0x164e: // (green steel)
5774 element = EL_STEEL_CHAR_D;
5777 case 0x164f: // (green steel)
5778 element = EL_STEEL_CHAR_E;
5781 case 0x1650: // (green steel)
5782 element = EL_STEEL_CHAR_F;
5785 case 0x1651: // (green steel)
5786 element = EL_STEEL_CHAR_G;
5789 case 0x1652: // (green steel)
5790 element = EL_STEEL_CHAR_H;
5793 case 0x1653: // (green steel)
5794 element = EL_STEEL_CHAR_I;
5797 case 0x1654: // (green steel)
5798 element = EL_STEEL_CHAR_J;
5801 case 0x1655: // (green steel)
5802 element = EL_STEEL_CHAR_K;
5805 case 0x1656: // (green steel)
5806 element = EL_STEEL_CHAR_L;
5809 case 0x1657: // (green steel)
5810 element = EL_STEEL_CHAR_M;
5813 case 0x1658: // (green steel)
5814 element = EL_STEEL_CHAR_N;
5817 case 0x1659: // (green steel)
5818 element = EL_STEEL_CHAR_O;
5821 case 0x165a: // (green steel)
5822 element = EL_STEEL_CHAR_P;
5825 case 0x165b: // (green steel)
5826 element = EL_STEEL_CHAR_Q;
5829 case 0x165c: // (green steel)
5830 element = EL_STEEL_CHAR_R;
5833 case 0x165d: // (green steel)
5834 element = EL_STEEL_CHAR_S;
5837 case 0x165e: // (green steel)
5838 element = EL_STEEL_CHAR_T;
5841 case 0x165f: // (green steel)
5842 element = EL_STEEL_CHAR_U;
5845 case 0x1660: // (green steel)
5846 element = EL_STEEL_CHAR_V;
5849 case 0x1661: // (green steel)
5850 element = EL_STEEL_CHAR_W;
5853 case 0x1662: // (green steel)
5854 element = EL_STEEL_CHAR_X;
5857 case 0x1663: // (green steel)
5858 element = EL_STEEL_CHAR_Y;
5861 case 0x1664: // (green steel)
5862 element = EL_STEEL_CHAR_Z;
5865 case 0x1665: // (green steel)
5866 element = EL_STEEL_CHAR_AUMLAUT;
5869 case 0x1666: // (green steel)
5870 element = EL_STEEL_CHAR_OUMLAUT;
5873 case 0x1667: // (green steel)
5874 element = EL_STEEL_CHAR_UUMLAUT;
5877 case 0x1668: // (green steel)
5878 element = EL_STEEL_CHAR_0;
5881 case 0x1669: // (green steel)
5882 element = EL_STEEL_CHAR_1;
5885 case 0x166a: // (green steel)
5886 element = EL_STEEL_CHAR_2;
5889 case 0x166b: // (green steel)
5890 element = EL_STEEL_CHAR_3;
5893 case 0x166c: // (green steel)
5894 element = EL_STEEL_CHAR_4;
5897 case 0x166d: // (green steel)
5898 element = EL_STEEL_CHAR_5;
5901 case 0x166e: // (green steel)
5902 element = EL_STEEL_CHAR_6;
5905 case 0x166f: // (green steel)
5906 element = EL_STEEL_CHAR_7;
5909 case 0x1670: // (green steel)
5910 element = EL_STEEL_CHAR_8;
5913 case 0x1671: // (green steel)
5914 element = EL_STEEL_CHAR_9;
5917 case 0x1672: // (green steel)
5918 element = EL_STEEL_CHAR_PERIOD;
5921 case 0x1673: // (green steel)
5922 element = EL_STEEL_CHAR_EXCLAM;
5925 case 0x1674: // (green steel)
5926 element = EL_STEEL_CHAR_COLON;
5929 case 0x1675: // (green steel)
5930 element = EL_STEEL_CHAR_LESS;
5933 case 0x1676: // (green steel)
5934 element = EL_STEEL_CHAR_GREATER;
5937 case 0x1677: // (green steel)
5938 element = EL_STEEL_CHAR_QUESTION;
5941 case 0x1678: // (green steel)
5942 element = EL_STEEL_CHAR_COPYRIGHT;
5945 case 0x1679: // (green steel)
5946 element = EL_STEEL_CHAR_UP;
5949 case 0x167a: // (green steel)
5950 element = EL_STEEL_CHAR_DOWN;
5953 case 0x167b: // (green steel)
5954 element = EL_STEEL_CHAR_BUTTON;
5957 case 0x167c: // (green steel)
5958 element = EL_STEEL_CHAR_PLUS;
5961 case 0x167d: // (green steel)
5962 element = EL_STEEL_CHAR_MINUS;
5965 case 0x167e: // (green steel)
5966 element = EL_STEEL_CHAR_APOSTROPHE;
5969 case 0x167f: // (green steel)
5970 element = EL_STEEL_CHAR_PARENLEFT;
5973 case 0x1680: // (green steel)
5974 element = EL_STEEL_CHAR_PARENRIGHT;
5977 case 0x1681: // gate (red)
5978 element = EL_EM_GATE_1;
5981 case 0x1682: // secret gate (red)
5982 element = EL_EM_GATE_1_GRAY;
5985 case 0x1683: // gate (yellow)
5986 element = EL_EM_GATE_2;
5989 case 0x1684: // secret gate (yellow)
5990 element = EL_EM_GATE_2_GRAY;
5993 case 0x1685: // gate (blue)
5994 element = EL_EM_GATE_4;
5997 case 0x1686: // secret gate (blue)
5998 element = EL_EM_GATE_4_GRAY;
6001 case 0x1687: // gate (green)
6002 element = EL_EM_GATE_3;
6005 case 0x1688: // secret gate (green)
6006 element = EL_EM_GATE_3_GRAY;
6009 case 0x1689: // gate (white)
6010 element = EL_DC_GATE_WHITE;
6013 case 0x168a: // secret gate (white)
6014 element = EL_DC_GATE_WHITE_GRAY;
6017 case 0x168b: // secret gate (no key)
6018 element = EL_DC_GATE_FAKE_GRAY;
6022 element = EL_ROBOT_WHEEL;
6026 element = EL_DC_TIMEGATE_SWITCH;
6030 element = EL_ACID_POOL_BOTTOM;
6034 element = EL_ACID_POOL_TOPLEFT;
6038 element = EL_ACID_POOL_TOPRIGHT;
6042 element = EL_ACID_POOL_BOTTOMLEFT;
6046 element = EL_ACID_POOL_BOTTOMRIGHT;
6050 element = EL_STEELWALL;
6054 element = EL_STEELWALL_SLIPPERY;
6057 case 0x1695: // steel wall (not round)
6058 element = EL_STEELWALL;
6061 case 0x1696: // steel wall (left)
6062 element = EL_DC_STEELWALL_1_LEFT;
6065 case 0x1697: // steel wall (bottom)
6066 element = EL_DC_STEELWALL_1_BOTTOM;
6069 case 0x1698: // steel wall (right)
6070 element = EL_DC_STEELWALL_1_RIGHT;
6073 case 0x1699: // steel wall (top)
6074 element = EL_DC_STEELWALL_1_TOP;
6077 case 0x169a: // steel wall (left/bottom)
6078 element = EL_DC_STEELWALL_1_BOTTOMLEFT;
6081 case 0x169b: // steel wall (right/bottom)
6082 element = EL_DC_STEELWALL_1_BOTTOMRIGHT;
6085 case 0x169c: // steel wall (right/top)
6086 element = EL_DC_STEELWALL_1_TOPRIGHT;
6089 case 0x169d: // steel wall (left/top)
6090 element = EL_DC_STEELWALL_1_TOPLEFT;
6093 case 0x169e: // steel wall (right/bottom small)
6094 element = EL_DC_STEELWALL_1_BOTTOMRIGHT_2;
6097 case 0x169f: // steel wall (left/bottom small)
6098 element = EL_DC_STEELWALL_1_BOTTOMLEFT_2;
6101 case 0x16a0: // steel wall (right/top small)
6102 element = EL_DC_STEELWALL_1_TOPRIGHT_2;
6105 case 0x16a1: // steel wall (left/top small)
6106 element = EL_DC_STEELWALL_1_TOPLEFT_2;
6109 case 0x16a2: // steel wall (left/right)
6110 element = EL_DC_STEELWALL_1_VERTICAL;
6113 case 0x16a3: // steel wall (top/bottom)
6114 element = EL_DC_STEELWALL_1_HORIZONTAL;
6117 case 0x16a4: // steel wall 2 (left end)
6118 element = EL_DC_STEELWALL_2_LEFT;
6121 case 0x16a5: // steel wall 2 (right end)
6122 element = EL_DC_STEELWALL_2_RIGHT;
6125 case 0x16a6: // steel wall 2 (top end)
6126 element = EL_DC_STEELWALL_2_TOP;
6129 case 0x16a7: // steel wall 2 (bottom end)
6130 element = EL_DC_STEELWALL_2_BOTTOM;
6133 case 0x16a8: // steel wall 2 (left/right)
6134 element = EL_DC_STEELWALL_2_HORIZONTAL;
6137 case 0x16a9: // steel wall 2 (up/down)
6138 element = EL_DC_STEELWALL_2_VERTICAL;
6141 case 0x16aa: // steel wall 2 (mid)
6142 element = EL_DC_STEELWALL_2_MIDDLE;
6146 element = EL_SIGN_EXCLAMATION;
6150 element = EL_SIGN_RADIOACTIVITY;
6154 element = EL_SIGN_STOP;
6158 element = EL_SIGN_WHEELCHAIR;
6162 element = EL_SIGN_PARKING;
6166 element = EL_SIGN_NO_ENTRY;
6170 element = EL_SIGN_HEART;
6174 element = EL_SIGN_GIVE_WAY;
6178 element = EL_SIGN_ENTRY_FORBIDDEN;
6182 element = EL_SIGN_EMERGENCY_EXIT;
6186 element = EL_SIGN_YIN_YANG;
6190 element = EL_WALL_EMERALD;
6194 element = EL_WALL_DIAMOND;
6198 element = EL_WALL_PEARL;
6202 element = EL_WALL_CRYSTAL;
6206 element = EL_INVISIBLE_WALL;
6210 element = EL_INVISIBLE_STEELWALL;
6214 // EL_INVISIBLE_SAND
6217 element = EL_LIGHT_SWITCH;
6221 element = EL_ENVELOPE_1;
6225 if (element >= 0x0117 && element <= 0x036e) // (?)
6226 element = EL_DIAMOND;
6227 else if (element >= 0x042d && element <= 0x0684) // (?)
6228 element = EL_EMERALD;
6229 else if (element >= 0x157c && element <= 0x158b)
6231 else if (element >= 0x1590 && element <= 0x159f)
6232 element = EL_DC_LANDMINE;
6233 else if (element >= 0x16bc && element <= 0x16cb)
6234 element = EL_INVISIBLE_SAND;
6237 Warn("unknown Diamond Caves element 0x%04x", element);
6239 element = EL_UNKNOWN;
6244 return getMappedElement(element);
6247 static void LoadLevelFromFileStream_DC(File *file, struct LevelInfo *level)
6249 byte header[DC_LEVEL_HEADER_SIZE];
6251 int envelope_header_pos = 62;
6252 int envelope_content_pos = 94;
6253 int level_name_pos = 251;
6254 int level_author_pos = 292;
6255 int envelope_header_len;
6256 int envelope_content_len;
6258 int level_author_len;
6260 int num_yamyam_contents;
6263 getDecodedWord_DC(0, TRUE); // initialize DC2 decoding engine
6265 for (i = 0; i < DC_LEVEL_HEADER_SIZE / 2; i++)
6267 unsigned short header_word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
6269 header[i * 2 + 0] = header_word >> 8;
6270 header[i * 2 + 1] = header_word & 0xff;
6273 // read some values from level header to check level decoding integrity
6274 fieldx = header[6] | (header[7] << 8);
6275 fieldy = header[8] | (header[9] << 8);
6276 num_yamyam_contents = header[60] | (header[61] << 8);
6278 // do some simple sanity checks to ensure that level was correctly decoded
6279 if (fieldx < 1 || fieldx > 256 ||
6280 fieldy < 1 || fieldy > 256 ||
6281 num_yamyam_contents < 1 || num_yamyam_contents > 8)
6283 level->no_valid_file = TRUE;
6285 Warn("cannot decode level from stream -- using empty level");
6290 // maximum envelope header size is 31 bytes
6291 envelope_header_len = header[envelope_header_pos];
6292 // maximum envelope content size is 110 (156?) bytes
6293 envelope_content_len = header[envelope_content_pos];
6295 // maximum level title size is 40 bytes
6296 level_name_len = MIN(header[level_name_pos], MAX_LEVEL_NAME_LEN);
6297 // maximum level author size is 30 (51?) bytes
6298 level_author_len = MIN(header[level_author_pos], MAX_LEVEL_AUTHOR_LEN);
6302 for (i = 0; i < envelope_header_len; i++)
6303 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
6304 level->envelope[0].text[envelope_size++] =
6305 header[envelope_header_pos + 1 + i];
6307 if (envelope_header_len > 0 && envelope_content_len > 0)
6309 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
6310 level->envelope[0].text[envelope_size++] = '\n';
6311 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
6312 level->envelope[0].text[envelope_size++] = '\n';
6315 for (i = 0; i < envelope_content_len; i++)
6316 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
6317 level->envelope[0].text[envelope_size++] =
6318 header[envelope_content_pos + 1 + i];
6320 level->envelope[0].text[envelope_size] = '\0';
6322 level->envelope[0].xsize = MAX_ENVELOPE_XSIZE;
6323 level->envelope[0].ysize = 10;
6324 level->envelope[0].autowrap = TRUE;
6325 level->envelope[0].centered = TRUE;
6327 for (i = 0; i < level_name_len; i++)
6328 level->name[i] = header[level_name_pos + 1 + i];
6329 level->name[level_name_len] = '\0';
6331 for (i = 0; i < level_author_len; i++)
6332 level->author[i] = header[level_author_pos + 1 + i];
6333 level->author[level_author_len] = '\0';
6335 num_yamyam_contents = header[60] | (header[61] << 8);
6336 level->num_yamyam_contents =
6337 MIN(MAX(MIN_ELEMENT_CONTENTS, num_yamyam_contents), MAX_ELEMENT_CONTENTS);
6339 for (i = 0; i < num_yamyam_contents; i++)
6341 for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
6343 unsigned short word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
6344 int element_dc = ((word & 0xff) << 8) | ((word >> 8) & 0xff);
6346 if (i < MAX_ELEMENT_CONTENTS)
6347 level->yamyam_content[i].e[x][y] = getMappedElement_DC(element_dc);
6351 fieldx = header[6] | (header[7] << 8);
6352 fieldy = header[8] | (header[9] << 8);
6353 level->fieldx = MIN(MAX(MIN_LEV_FIELDX, fieldx), MAX_LEV_FIELDX);
6354 level->fieldy = MIN(MAX(MIN_LEV_FIELDY, fieldy), MAX_LEV_FIELDY);
6356 for (y = 0; y < fieldy; y++) for (x = 0; x < fieldx; x++)
6358 unsigned short word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
6359 int element_dc = ((word & 0xff) << 8) | ((word >> 8) & 0xff);
6361 if (x < MAX_LEV_FIELDX && y < MAX_LEV_FIELDY)
6362 level->field[x][y] = getMappedElement_DC(element_dc);
6365 x = MIN(MAX(0, (header[10] | (header[11] << 8)) - 1), MAX_LEV_FIELDX - 1);
6366 y = MIN(MAX(0, (header[12] | (header[13] << 8)) - 1), MAX_LEV_FIELDY - 1);
6367 level->field[x][y] = EL_PLAYER_1;
6369 x = MIN(MAX(0, (header[14] | (header[15] << 8)) - 1), MAX_LEV_FIELDX - 1);
6370 y = MIN(MAX(0, (header[16] | (header[17] << 8)) - 1), MAX_LEV_FIELDY - 1);
6371 level->field[x][y] = EL_PLAYER_2;
6373 level->gems_needed = header[18] | (header[19] << 8);
6375 level->score[SC_EMERALD] = header[20] | (header[21] << 8);
6376 level->score[SC_DIAMOND] = header[22] | (header[23] << 8);
6377 level->score[SC_PEARL] = header[24] | (header[25] << 8);
6378 level->score[SC_CRYSTAL] = header[26] | (header[27] << 8);
6379 level->score[SC_NUT] = header[28] | (header[29] << 8);
6380 level->score[SC_ROBOT] = header[30] | (header[31] << 8);
6381 level->score[SC_SPACESHIP] = header[32] | (header[33] << 8);
6382 level->score[SC_BUG] = header[34] | (header[35] << 8);
6383 level->score[SC_YAMYAM] = header[36] | (header[37] << 8);
6384 level->score[SC_DYNAMITE] = header[38] | (header[39] << 8);
6385 level->score[SC_KEY] = header[40] | (header[41] << 8);
6386 level->score[SC_TIME_BONUS] = header[42] | (header[43] << 8);
6388 level->time = header[44] | (header[45] << 8);
6390 level->amoeba_speed = header[46] | (header[47] << 8);
6391 level->time_light = header[48] | (header[49] << 8);
6392 level->time_timegate = header[50] | (header[51] << 8);
6393 level->time_wheel = header[52] | (header[53] << 8);
6394 level->time_magic_wall = header[54] | (header[55] << 8);
6395 level->extra_time = header[56] | (header[57] << 8);
6396 level->shield_normal_time = header[58] | (header[59] << 8);
6398 // shield and extra time elements do not have a score
6399 level->score[SC_SHIELD] = 0;
6400 level->extra_time_score = 0;
6402 // set time for normal and deadly shields to the same value
6403 level->shield_deadly_time = level->shield_normal_time;
6405 // Diamond Caves has the same (strange) behaviour as Emerald Mine that gems
6406 // can slip down from flat walls, like normal walls and steel walls
6407 level->em_slippery_gems = TRUE;
6409 // time score is counted for each 10 seconds left in Diamond Caves levels
6410 level->time_score_base = 10;
6413 static void LoadLevelFromFileInfo_DC(struct LevelInfo *level,
6414 struct LevelFileInfo *level_file_info,
6415 boolean level_info_only)
6417 char *filename = level_file_info->filename;
6419 int num_magic_bytes = 8;
6420 char magic_bytes[num_magic_bytes + 1];
6421 int num_levels_to_skip = level_file_info->nr - leveldir_current->first_level;
6423 if (!(file = openFile(filename, MODE_READ)))
6425 level->no_valid_file = TRUE;
6427 if (!level_info_only)
6428 Warn("cannot read level '%s' -- using empty level", filename);
6433 // fseek(file, 0x0000, SEEK_SET);
6435 if (level_file_info->packed)
6437 // read "magic bytes" from start of file
6438 if (getStringFromFile(file, magic_bytes, num_magic_bytes + 1) == NULL)
6439 magic_bytes[0] = '\0';
6441 // check "magic bytes" for correct file format
6442 if (!strPrefix(magic_bytes, "DC2"))
6444 level->no_valid_file = TRUE;
6446 Warn("unknown DC level file '%s' -- using empty level", filename);
6451 if (strPrefix(magic_bytes, "DC2Win95") ||
6452 strPrefix(magic_bytes, "DC2Win98"))
6454 int position_first_level = 0x00fa;
6455 int extra_bytes = 4;
6458 // advance file stream to first level inside the level package
6459 skip_bytes = position_first_level - num_magic_bytes - extra_bytes;
6461 // each block of level data is followed by block of non-level data
6462 num_levels_to_skip *= 2;
6464 // at least skip header bytes, therefore use ">= 0" instead of "> 0"
6465 while (num_levels_to_skip >= 0)
6467 // advance file stream to next level inside the level package
6468 if (seekFile(file, skip_bytes, SEEK_CUR) != 0)
6470 level->no_valid_file = TRUE;
6472 Warn("cannot fseek in file '%s' -- using empty level", filename);
6477 // skip apparently unused extra bytes following each level
6478 ReadUnusedBytesFromFile(file, extra_bytes);
6480 // read size of next level in level package
6481 skip_bytes = getFile32BitLE(file);
6483 num_levels_to_skip--;
6488 level->no_valid_file = TRUE;
6490 Warn("unknown DC2 level file '%s' -- using empty level", filename);
6496 LoadLevelFromFileStream_DC(file, level);
6502 // ----------------------------------------------------------------------------
6503 // functions for loading SB level
6504 // ----------------------------------------------------------------------------
6506 int getMappedElement_SB(int element_ascii, boolean use_ces)
6514 sb_element_mapping[] =
6516 { ' ', EL_EMPTY, EL_CUSTOM_1 }, // floor (space)
6517 { '#', EL_STEELWALL, EL_CUSTOM_2 }, // wall
6518 { '@', EL_PLAYER_1, EL_CUSTOM_3 }, // player
6519 { '$', EL_SOKOBAN_OBJECT, EL_CUSTOM_4 }, // box
6520 { '.', EL_SOKOBAN_FIELD_EMPTY, EL_CUSTOM_5 }, // goal square
6521 { '*', EL_SOKOBAN_FIELD_FULL, EL_CUSTOM_6 }, // box on goal square
6522 { '+', EL_SOKOBAN_FIELD_PLAYER, EL_CUSTOM_7 }, // player on goal square
6523 { '_', EL_INVISIBLE_STEELWALL, EL_FROM_LEVEL_TEMPLATE }, // floor beyond border
6530 for (i = 0; sb_element_mapping[i].ascii != 0; i++)
6531 if (element_ascii == sb_element_mapping[i].ascii)
6532 return (use_ces ? sb_element_mapping[i].ce : sb_element_mapping[i].sb);
6534 return EL_UNDEFINED;
6537 static void SetLevelSettings_SB(struct LevelInfo *level)
6541 level->use_step_counter = TRUE;
6544 level->score[SC_TIME_BONUS] = 0;
6545 level->time_score_base = 1;
6546 level->rate_time_over_score = TRUE;
6549 level->auto_exit_sokoban = TRUE;
6552 static void LoadLevelFromFileInfo_SB(struct LevelInfo *level,
6553 struct LevelFileInfo *level_file_info,
6554 boolean level_info_only)
6556 char *filename = level_file_info->filename;
6557 char line[MAX_LINE_LEN], line_raw[MAX_LINE_LEN], previous_line[MAX_LINE_LEN];
6558 char last_comment[MAX_LINE_LEN];
6559 char level_name[MAX_LINE_LEN];
6562 int num_levels_to_skip = level_file_info->nr - leveldir_current->first_level;
6563 boolean read_continued_line = FALSE;
6564 boolean reading_playfield = FALSE;
6565 boolean got_valid_playfield_line = FALSE;
6566 boolean invalid_playfield_char = FALSE;
6567 boolean load_xsb_to_ces = check_special_flags("load_xsb_to_ces");
6568 int file_level_nr = 0;
6569 int x = 0, y = 0; // initialized to make compilers happy
6571 last_comment[0] = '\0';
6572 level_name[0] = '\0';
6574 if (!(file = openFile(filename, MODE_READ)))
6576 level->no_valid_file = TRUE;
6578 if (!level_info_only)
6579 Warn("cannot read level '%s' -- using empty level", filename);
6584 while (!checkEndOfFile(file))
6586 // level successfully read, but next level may follow here
6587 if (!got_valid_playfield_line && reading_playfield)
6589 // read playfield from single level file -- skip remaining file
6590 if (!level_file_info->packed)
6593 if (file_level_nr >= num_levels_to_skip)
6598 last_comment[0] = '\0';
6599 level_name[0] = '\0';
6601 reading_playfield = FALSE;
6604 got_valid_playfield_line = FALSE;
6606 // read next line of input file
6607 if (!getStringFromFile(file, line, MAX_LINE_LEN))
6610 // cut trailing line break (this can be newline and/or carriage return)
6611 for (line_ptr = &line[strlen(line)]; line_ptr >= line; line_ptr--)
6612 if ((*line_ptr == '\n' || *line_ptr == '\r') && *(line_ptr + 1) == '\0')
6615 // copy raw input line for later use (mainly debugging output)
6616 strcpy(line_raw, line);
6618 if (read_continued_line)
6620 // append new line to existing line, if there is enough space
6621 if (strlen(previous_line) + strlen(line_ptr) < MAX_LINE_LEN)
6622 strcat(previous_line, line_ptr);
6624 strcpy(line, previous_line); // copy storage buffer to line
6626 read_continued_line = FALSE;
6629 // if the last character is '\', continue at next line
6630 if (strlen(line) > 0 && line[strlen(line) - 1] == '\\')
6632 line[strlen(line) - 1] = '\0'; // cut off trailing backslash
6633 strcpy(previous_line, line); // copy line to storage buffer
6635 read_continued_line = TRUE;
6641 if (line[0] == '\0')
6644 // extract comment text from comment line
6647 for (line_ptr = line; *line_ptr; line_ptr++)
6648 if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != ';')
6651 strcpy(last_comment, line_ptr);
6656 // extract level title text from line containing level title
6657 if (line[0] == '\'')
6659 strcpy(level_name, &line[1]);
6661 if (strlen(level_name) > 0 && level_name[strlen(level_name) - 1] == '\'')
6662 level_name[strlen(level_name) - 1] = '\0';
6667 // skip lines containing only spaces (or empty lines)
6668 for (line_ptr = line; *line_ptr; line_ptr++)
6669 if (*line_ptr != ' ')
6671 if (*line_ptr == '\0')
6674 // at this point, we have found a line containing part of a playfield
6676 got_valid_playfield_line = TRUE;
6678 if (!reading_playfield)
6680 reading_playfield = TRUE;
6681 invalid_playfield_char = FALSE;
6683 for (x = 0; x < MAX_LEV_FIELDX; x++)
6684 for (y = 0; y < MAX_LEV_FIELDY; y++)
6685 level->field[x][y] = getMappedElement_SB(' ', load_xsb_to_ces);
6690 // start with topmost tile row
6694 // skip playfield line if larger row than allowed
6695 if (y >= MAX_LEV_FIELDY)
6698 // start with leftmost tile column
6701 // read playfield elements from line
6702 for (line_ptr = line; *line_ptr; line_ptr++)
6704 int mapped_sb_element = getMappedElement_SB(*line_ptr, load_xsb_to_ces);
6706 // stop parsing playfield line if larger column than allowed
6707 if (x >= MAX_LEV_FIELDX)
6710 if (mapped_sb_element == EL_UNDEFINED)
6712 invalid_playfield_char = TRUE;
6717 level->field[x][y] = mapped_sb_element;
6719 // continue with next tile column
6722 level->fieldx = MAX(x, level->fieldx);
6725 if (invalid_playfield_char)
6727 // if first playfield line, treat invalid lines as comment lines
6729 reading_playfield = FALSE;
6734 // continue with next tile row
6742 level->fieldx = MIN(MAX(MIN_LEV_FIELDX, level->fieldx), MAX_LEV_FIELDX);
6743 level->fieldy = MIN(MAX(MIN_LEV_FIELDY, level->fieldy), MAX_LEV_FIELDY);
6745 if (!reading_playfield)
6747 level->no_valid_file = TRUE;
6749 Warn("cannot read level '%s' -- using empty level", filename);
6754 if (*level_name != '\0')
6756 strncpy(level->name, level_name, MAX_LEVEL_NAME_LEN);
6757 level->name[MAX_LEVEL_NAME_LEN] = '\0';
6759 else if (*last_comment != '\0')
6761 strncpy(level->name, last_comment, MAX_LEVEL_NAME_LEN);
6762 level->name[MAX_LEVEL_NAME_LEN] = '\0';
6766 sprintf(level->name, "--> Level %d <--", level_file_info->nr);
6769 // set all empty fields beyond the border walls to invisible steel wall
6770 for (y = 0; y < level->fieldy; y++) for (x = 0; x < level->fieldx; x++)
6772 if ((x == 0 || x == level->fieldx - 1 ||
6773 y == 0 || y == level->fieldy - 1) &&
6774 level->field[x][y] == getMappedElement_SB(' ', load_xsb_to_ces))
6775 FloodFillLevel(x, y, getMappedElement_SB('_', load_xsb_to_ces),
6776 level->field, level->fieldx, level->fieldy);
6779 // set special level settings for Sokoban levels
6780 SetLevelSettings_SB(level);
6782 if (load_xsb_to_ces)
6784 // special global settings can now be set in level template
6785 level->use_custom_template = TRUE;
6790 // -------------------------------------------------------------------------
6791 // functions for handling native levels
6792 // -------------------------------------------------------------------------
6794 static void LoadLevelFromFileInfo_BD(struct LevelInfo *level,
6795 struct LevelFileInfo *level_file_info,
6796 boolean level_info_only)
6800 // determine position of requested level inside level package
6801 if (level_file_info->packed)
6802 pos = level_file_info->nr - leveldir_current->first_level;
6804 if (!LoadNativeLevel_BD(level_file_info->filename, pos, level_info_only))
6805 level->no_valid_file = TRUE;
6808 static void LoadLevelFromFileInfo_EM(struct LevelInfo *level,
6809 struct LevelFileInfo *level_file_info,
6810 boolean level_info_only)
6812 if (!LoadNativeLevel_EM(level_file_info->filename, level_info_only))
6813 level->no_valid_file = TRUE;
6816 static void LoadLevelFromFileInfo_SP(struct LevelInfo *level,
6817 struct LevelFileInfo *level_file_info,
6818 boolean level_info_only)
6822 // determine position of requested level inside level package
6823 if (level_file_info->packed)
6824 pos = level_file_info->nr - leveldir_current->first_level;
6826 if (!LoadNativeLevel_SP(level_file_info->filename, pos, level_info_only))
6827 level->no_valid_file = TRUE;
6830 static void LoadLevelFromFileInfo_MM(struct LevelInfo *level,
6831 struct LevelFileInfo *level_file_info,
6832 boolean level_info_only)
6834 if (!LoadNativeLevel_MM(level_file_info->filename, level_info_only))
6835 level->no_valid_file = TRUE;
6838 void CopyNativeLevel_RND_to_Native(struct LevelInfo *level)
6840 if (level->game_engine_type == GAME_ENGINE_TYPE_BD)
6841 CopyNativeLevel_RND_to_BD(level);
6842 else if (level->game_engine_type == GAME_ENGINE_TYPE_EM)
6843 CopyNativeLevel_RND_to_EM(level);
6844 else if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
6845 CopyNativeLevel_RND_to_SP(level);
6846 else if (level->game_engine_type == GAME_ENGINE_TYPE_MM)
6847 CopyNativeLevel_RND_to_MM(level);
6850 void CopyNativeLevel_Native_to_RND(struct LevelInfo *level)
6852 if (level->game_engine_type == GAME_ENGINE_TYPE_BD)
6853 CopyNativeLevel_BD_to_RND(level);
6854 else if (level->game_engine_type == GAME_ENGINE_TYPE_EM)
6855 CopyNativeLevel_EM_to_RND(level);
6856 else if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
6857 CopyNativeLevel_SP_to_RND(level);
6858 else if (level->game_engine_type == GAME_ENGINE_TYPE_MM)
6859 CopyNativeLevel_MM_to_RND(level);
6862 void SaveNativeLevel(struct LevelInfo *level)
6864 // saving native level files only supported for some game engines
6865 if (level->game_engine_type != GAME_ENGINE_TYPE_BD &&
6866 level->game_engine_type != GAME_ENGINE_TYPE_SP)
6869 char *file_ext = (level->game_engine_type == GAME_ENGINE_TYPE_BD ? "bd" :
6870 level->game_engine_type == GAME_ENGINE_TYPE_SP ? "sp" : "");
6871 char *basename = getSingleLevelBasenameExt(level->file_info.nr, file_ext);
6872 char *filename = getLevelFilenameFromBasename(basename);
6874 if (fileExists(filename) && !Request("Native level file already exists! Overwrite it?", REQ_ASK))
6877 boolean success = FALSE;
6879 if (level->game_engine_type == GAME_ENGINE_TYPE_BD)
6881 CopyNativeLevel_RND_to_BD(level);
6882 // CopyNativeTape_RND_to_BD(level);
6884 success = SaveNativeLevel_BD(filename);
6886 else if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
6888 CopyNativeLevel_RND_to_SP(level);
6889 CopyNativeTape_RND_to_SP(level);
6891 success = SaveNativeLevel_SP(filename);
6895 Request("Native level file saved!", REQ_CONFIRM);
6897 Request("Failed to save native level file!", REQ_CONFIRM);
6901 // ----------------------------------------------------------------------------
6902 // functions for loading generic level
6903 // ----------------------------------------------------------------------------
6905 static void LoadLevelFromFileInfo(struct LevelInfo *level,
6906 struct LevelFileInfo *level_file_info,
6907 boolean level_info_only)
6909 // always start with reliable default values
6910 setLevelInfoToDefaults(level, level_info_only, TRUE);
6912 switch (level_file_info->type)
6914 case LEVEL_FILE_TYPE_RND:
6915 LoadLevelFromFileInfo_RND(level, level_file_info, level_info_only);
6918 case LEVEL_FILE_TYPE_BD:
6919 LoadLevelFromFileInfo_BD(level, level_file_info, level_info_only);
6920 level->game_engine_type = GAME_ENGINE_TYPE_BD;
6923 case LEVEL_FILE_TYPE_EM:
6924 LoadLevelFromFileInfo_EM(level, level_file_info, level_info_only);
6925 level->game_engine_type = GAME_ENGINE_TYPE_EM;
6928 case LEVEL_FILE_TYPE_SP:
6929 LoadLevelFromFileInfo_SP(level, level_file_info, level_info_only);
6930 level->game_engine_type = GAME_ENGINE_TYPE_SP;
6933 case LEVEL_FILE_TYPE_MM:
6934 LoadLevelFromFileInfo_MM(level, level_file_info, level_info_only);
6935 level->game_engine_type = GAME_ENGINE_TYPE_MM;
6938 case LEVEL_FILE_TYPE_DC:
6939 LoadLevelFromFileInfo_DC(level, level_file_info, level_info_only);
6942 case LEVEL_FILE_TYPE_SB:
6943 LoadLevelFromFileInfo_SB(level, level_file_info, level_info_only);
6947 LoadLevelFromFileInfo_RND(level, level_file_info, level_info_only);
6951 // if level file is invalid, restore level structure to default values
6952 if (level->no_valid_file)
6953 setLevelInfoToDefaults(level, level_info_only, FALSE);
6955 if (check_special_flags("use_native_bd_game_engine"))
6956 level->game_engine_type = GAME_ENGINE_TYPE_BD;
6958 if (level->game_engine_type == GAME_ENGINE_TYPE_UNKNOWN)
6959 level->game_engine_type = GAME_ENGINE_TYPE_RND;
6961 if (level_file_info->type != LEVEL_FILE_TYPE_RND)
6962 CopyNativeLevel_Native_to_RND(level);
6965 void LoadLevelFromFilename(struct LevelInfo *level, char *filename)
6967 static struct LevelFileInfo level_file_info;
6969 // always start with reliable default values
6970 setFileInfoToDefaults(&level_file_info);
6972 level_file_info.nr = 0; // unknown level number
6973 level_file_info.type = LEVEL_FILE_TYPE_RND; // no others supported yet
6975 setString(&level_file_info.filename, filename);
6977 LoadLevelFromFileInfo(level, &level_file_info, FALSE);
6980 static void LoadLevel_InitVersion(struct LevelInfo *level)
6984 if (leveldir_current == NULL) // only when dumping level
6987 // all engine modifications also valid for levels which use latest engine
6988 if (level->game_version < VERSION_IDENT(3,2,0,5))
6990 // time bonus score was given for 10 s instead of 1 s before 3.2.0-5
6991 level->time_score_base = 10;
6994 if (leveldir_current->latest_engine)
6996 // ---------- use latest game engine --------------------------------------
6998 /* For all levels which are forced to use the latest game engine version
6999 (normally all but user contributed, private and undefined levels), set
7000 the game engine version to the actual version; this allows for actual
7001 corrections in the game engine to take effect for existing, converted
7002 levels (from "classic" or other existing games) to make the emulation
7003 of the corresponding game more accurate, while (hopefully) not breaking
7004 existing levels created from other players. */
7006 level->game_version = GAME_VERSION_ACTUAL;
7008 /* Set special EM style gems behaviour: EM style gems slip down from
7009 normal, steel and growing wall. As this is a more fundamental change,
7010 it seems better to set the default behaviour to "off" (as it is more
7011 natural) and make it configurable in the level editor (as a property
7012 of gem style elements). Already existing converted levels (neither
7013 private nor contributed levels) are changed to the new behaviour. */
7015 if (level->file_version < FILE_VERSION_2_0)
7016 level->em_slippery_gems = TRUE;
7021 // ---------- use game engine the level was created with --------------------
7023 /* For all levels which are not forced to use the latest game engine
7024 version (normally user contributed, private and undefined levels),
7025 use the version of the game engine the levels were created for.
7027 Since 2.0.1, the game engine version is now directly stored
7028 in the level file (chunk "VERS"), so there is no need anymore
7029 to set the game version from the file version (except for old,
7030 pre-2.0 levels, where the game version is still taken from the
7031 file format version used to store the level -- see above). */
7033 // player was faster than enemies in 1.0.0 and before
7034 if (level->file_version == FILE_VERSION_1_0)
7035 for (i = 0; i < MAX_PLAYERS; i++)
7036 level->initial_player_stepsize[i] = STEPSIZE_FAST;
7038 // default behaviour for EM style gems was "slippery" only in 2.0.1
7039 if (level->game_version == VERSION_IDENT(2,0,1,0))
7040 level->em_slippery_gems = TRUE;
7042 // springs could be pushed over pits before (pre-release version) 2.2.0
7043 if (level->game_version < VERSION_IDENT(2,2,0,0))
7044 level->use_spring_bug = TRUE;
7046 if (level->game_version < VERSION_IDENT(3,2,0,5))
7048 // time orb caused limited time in endless time levels before 3.2.0-5
7049 level->use_time_orb_bug = TRUE;
7051 // default behaviour for snapping was "no snap delay" before 3.2.0-5
7052 level->block_snap_field = FALSE;
7054 // extra time score was same value as time left score before 3.2.0-5
7055 level->extra_time_score = level->score[SC_TIME_BONUS];
7058 if (level->game_version < VERSION_IDENT(3,2,0,7))
7060 // default behaviour for snapping was "not continuous" before 3.2.0-7
7061 level->continuous_snapping = FALSE;
7064 // only few elements were able to actively move into acid before 3.1.0
7065 // trigger settings did not exist before 3.1.0; set to default "any"
7066 if (level->game_version < VERSION_IDENT(3,1,0,0))
7068 // correct "can move into acid" settings (all zero in old levels)
7070 level->can_move_into_acid_bits = 0; // nothing can move into acid
7071 level->dont_collide_with_bits = 0; // nothing is deadly when colliding
7073 setMoveIntoAcidProperty(level, EL_ROBOT, TRUE);
7074 setMoveIntoAcidProperty(level, EL_SATELLITE, TRUE);
7075 setMoveIntoAcidProperty(level, EL_PENGUIN, TRUE);
7076 setMoveIntoAcidProperty(level, EL_BALLOON, TRUE);
7078 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7079 SET_PROPERTY(EL_CUSTOM_START + i, EP_CAN_MOVE_INTO_ACID, TRUE);
7081 // correct trigger settings (stored as zero == "none" in old levels)
7083 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7085 int element = EL_CUSTOM_START + i;
7086 struct ElementInfo *ei = &element_info[element];
7088 for (j = 0; j < ei->num_change_pages; j++)
7090 struct ElementChangeInfo *change = &ei->change_page[j];
7092 change->trigger_player = CH_PLAYER_ANY;
7093 change->trigger_page = CH_PAGE_ANY;
7098 // try to detect and fix "Snake Bite" levels, which are broken with 3.2.0
7100 int element = EL_CUSTOM_256;
7101 struct ElementInfo *ei = &element_info[element];
7102 struct ElementChangeInfo *change = &ei->change_page[0];
7104 /* This is needed to fix a problem that was caused by a bugfix in function
7105 game.c/CreateFieldExt() introduced with 3.2.0 that corrects the behaviour
7106 when a custom element changes to EL_SOKOBAN_FIELD_PLAYER (before, it did
7107 not replace walkable elements, but instead just placed the player on it,
7108 without placing the Sokoban field under the player). Unfortunately, this
7109 breaks "Snake Bite" style levels when the snake is halfway through a door
7110 that just closes (the snake head is still alive and can be moved in this
7111 case). This can be fixed by replacing the EL_SOKOBAN_FIELD_PLAYER by the
7112 player (without Sokoban element) which then gets killed as designed). */
7114 if ((strncmp(leveldir_current->identifier, "snake_bite", 10) == 0 ||
7115 strncmp(ei->description, "pause b4 death", 14) == 0) &&
7116 change->target_element == EL_SOKOBAN_FIELD_PLAYER)
7117 change->target_element = EL_PLAYER_1;
7120 // try to detect and fix "Zelda" style levels, which are broken with 3.2.5
7121 if (level->game_version < VERSION_IDENT(3,2,5,0))
7123 /* This is needed to fix a problem that was caused by a bugfix in function
7124 game.c/CheckTriggeredElementChangeExt() introduced with 3.2.5 that
7125 corrects the behaviour when a custom element changes to another custom
7126 element with a higher element number that has change actions defined.
7127 Normally, only one change per frame is allowed for custom elements.
7128 Therefore, it is checked if a custom element already changed in the
7129 current frame; if it did, subsequent changes are suppressed.
7130 Unfortunately, this is only checked for element changes, but not for
7131 change actions, which are still executed. As the function above loops
7132 through all custom elements from lower to higher, an element change
7133 resulting in a lower CE number won't be checked again, while a target
7134 element with a higher number will also be checked, and potential change
7135 actions will get executed for this CE, too (which is wrong), while
7136 further changes are ignored (which is correct). As this bugfix breaks
7137 Zelda II (and introduces graphical bugs to Zelda I, and also breaks a
7138 few other levels like Alan Bond's "FMV"), allow the previous, incorrect
7139 behaviour for existing levels and tapes that make use of this bug */
7141 level->use_action_after_change_bug = TRUE;
7144 // not centering level after relocating player was default only in 3.2.3
7145 if (level->game_version == VERSION_IDENT(3,2,3,0)) // (no pre-releases)
7146 level->shifted_relocation = TRUE;
7148 // EM style elements always chain-exploded in R'n'D engine before 3.2.6
7149 if (level->game_version < VERSION_IDENT(3,2,6,0))
7150 level->em_explodes_by_fire = TRUE;
7152 // levels were solved by the first player entering an exit up to 4.1.0.0
7153 if (level->game_version <= VERSION_IDENT(4,1,0,0))
7154 level->solved_by_one_player = TRUE;
7156 // game logic of "game of life" and "biomaze" was buggy before 4.1.1.1
7157 if (level->game_version < VERSION_IDENT(4,1,1,1))
7158 level->use_life_bugs = TRUE;
7160 // only Sokoban fields (but not objects) had to be solved before 4.1.1.1
7161 if (level->game_version < VERSION_IDENT(4,1,1,1))
7162 level->sb_objects_needed = FALSE;
7164 // CE actions were triggered by unfinished digging/collecting up to 4.2.2.0
7165 if (level->game_version <= VERSION_IDENT(4,2,2,0))
7166 level->finish_dig_collect = FALSE;
7168 // CE changing to player was kept under the player if walkable up to 4.2.3.1
7169 if (level->game_version <= VERSION_IDENT(4,2,3,1))
7170 level->keep_walkable_ce = TRUE;
7173 static void LoadLevel_InitSettings_SB(struct LevelInfo *level)
7175 boolean is_sokoban_level = TRUE; // unless non-Sokoban elements found
7178 // check if this level is (not) a Sokoban level
7179 for (y = 0; y < level->fieldy; y++)
7180 for (x = 0; x < level->fieldx; x++)
7181 if (!IS_SB_ELEMENT(Tile[x][y]))
7182 is_sokoban_level = FALSE;
7184 if (is_sokoban_level)
7186 // set special level settings for Sokoban levels
7187 SetLevelSettings_SB(level);
7191 static void LoadLevel_InitSettings(struct LevelInfo *level)
7193 // adjust level settings for (non-native) Sokoban-style levels
7194 LoadLevel_InitSettings_SB(level);
7196 // rename levels with title "nameless level" or if renaming is forced
7197 if (leveldir_current->empty_level_name != NULL &&
7198 (strEqual(level->name, NAMELESS_LEVEL_NAME) ||
7199 leveldir_current->force_level_name))
7200 snprintf(level->name, MAX_LEVEL_NAME_LEN + 1,
7201 leveldir_current->empty_level_name, level_nr);
7204 static void LoadLevel_InitStandardElements(struct LevelInfo *level)
7208 // map elements that have changed in newer versions
7209 level->amoeba_content = getMappedElementByVersion(level->amoeba_content,
7210 level->game_version);
7211 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
7212 for (x = 0; x < 3; x++)
7213 for (y = 0; y < 3; y++)
7214 level->yamyam_content[i].e[x][y] =
7215 getMappedElementByVersion(level->yamyam_content[i].e[x][y],
7216 level->game_version);
7220 static void LoadLevel_InitCustomElements(struct LevelInfo *level)
7224 // map custom element change events that have changed in newer versions
7225 // (these following values were accidentally changed in version 3.0.1)
7226 // (this seems to be needed only for 'ab_levelset3' and 'ab_levelset4')
7227 if (level->game_version <= VERSION_IDENT(3,0,0,0))
7229 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7231 int element = EL_CUSTOM_START + i;
7233 // order of checking and copying events to be mapped is important
7234 // (do not change the start and end value -- they are constant)
7235 for (j = CE_BY_OTHER_ACTION; j >= CE_VALUE_GETS_ZERO; j--)
7237 if (HAS_CHANGE_EVENT(element, j - 2))
7239 SET_CHANGE_EVENT(element, j - 2, FALSE);
7240 SET_CHANGE_EVENT(element, j, TRUE);
7244 // order of checking and copying events to be mapped is important
7245 // (do not change the start and end value -- they are constant)
7246 for (j = CE_PLAYER_COLLECTS_X; j >= CE_HITTING_SOMETHING; j--)
7248 if (HAS_CHANGE_EVENT(element, j - 1))
7250 SET_CHANGE_EVENT(element, j - 1, FALSE);
7251 SET_CHANGE_EVENT(element, j, TRUE);
7257 // initialize "can_change" field for old levels with only one change page
7258 if (level->game_version <= VERSION_IDENT(3,0,2,0))
7260 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7262 int element = EL_CUSTOM_START + i;
7264 if (CAN_CHANGE(element))
7265 element_info[element].change->can_change = TRUE;
7269 // correct custom element values (for old levels without these options)
7270 if (level->game_version < VERSION_IDENT(3,1,1,0))
7272 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7274 int element = EL_CUSTOM_START + i;
7275 struct ElementInfo *ei = &element_info[element];
7277 if (ei->access_direction == MV_NO_DIRECTION)
7278 ei->access_direction = MV_ALL_DIRECTIONS;
7282 // correct custom element values (fix invalid values for all versions)
7285 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7287 int element = EL_CUSTOM_START + i;
7288 struct ElementInfo *ei = &element_info[element];
7290 for (j = 0; j < ei->num_change_pages; j++)
7292 struct ElementChangeInfo *change = &ei->change_page[j];
7294 if (change->trigger_player == CH_PLAYER_NONE)
7295 change->trigger_player = CH_PLAYER_ANY;
7297 if (change->trigger_side == CH_SIDE_NONE)
7298 change->trigger_side = CH_SIDE_ANY;
7303 // initialize "can_explode" field for old levels which did not store this
7304 // !!! CHECK THIS -- "<= 3,1,0,0" IS PROBABLY WRONG !!!
7305 if (level->game_version <= VERSION_IDENT(3,1,0,0))
7307 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7309 int element = EL_CUSTOM_START + i;
7311 if (EXPLODES_1X1_OLD(element))
7312 element_info[element].explosion_type = EXPLODES_1X1;
7314 SET_PROPERTY(element, EP_CAN_EXPLODE, (EXPLODES_BY_FIRE(element) ||
7315 EXPLODES_SMASHED(element) ||
7316 EXPLODES_IMPACT(element)));
7320 // correct previously hard-coded move delay values for maze runner style
7321 if (level->game_version < VERSION_IDENT(3,1,1,0))
7323 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7325 int element = EL_CUSTOM_START + i;
7327 if (element_info[element].move_pattern & MV_MAZE_RUNNER_STYLE)
7329 // previously hard-coded and therefore ignored
7330 element_info[element].move_delay_fixed = 9;
7331 element_info[element].move_delay_random = 0;
7336 // set some other uninitialized values of custom elements in older levels
7337 if (level->game_version < VERSION_IDENT(3,1,0,0))
7339 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7341 int element = EL_CUSTOM_START + i;
7343 element_info[element].access_direction = MV_ALL_DIRECTIONS;
7345 element_info[element].explosion_delay = 17;
7346 element_info[element].ignition_delay = 8;
7350 // set mouse click change events to work for left/middle/right mouse button
7351 if (level->game_version < VERSION_IDENT(4,2,3,0))
7353 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7355 int element = EL_CUSTOM_START + i;
7356 struct ElementInfo *ei = &element_info[element];
7358 for (j = 0; j < ei->num_change_pages; j++)
7360 struct ElementChangeInfo *change = &ei->change_page[j];
7362 if (change->has_event[CE_CLICKED_BY_MOUSE] ||
7363 change->has_event[CE_PRESSED_BY_MOUSE] ||
7364 change->has_event[CE_MOUSE_CLICKED_ON_X] ||
7365 change->has_event[CE_MOUSE_PRESSED_ON_X])
7366 change->trigger_side = CH_SIDE_ANY;
7372 static void LoadLevel_InitElements(struct LevelInfo *level)
7374 LoadLevel_InitStandardElements(level);
7376 if (level->file_has_custom_elements)
7377 LoadLevel_InitCustomElements(level);
7379 // initialize element properties for level editor etc.
7380 InitElementPropertiesEngine(level->game_version);
7381 InitElementPropertiesGfxElement();
7384 static void LoadLevel_InitPlayfield(struct LevelInfo *level)
7388 // map elements that have changed in newer versions
7389 for (y = 0; y < level->fieldy; y++)
7390 for (x = 0; x < level->fieldx; x++)
7391 level->field[x][y] = getMappedElementByVersion(level->field[x][y],
7392 level->game_version);
7394 // clear unused playfield data (nicer if level gets resized in editor)
7395 for (x = 0; x < MAX_LEV_FIELDX; x++)
7396 for (y = 0; y < MAX_LEV_FIELDY; y++)
7397 if (x >= level->fieldx || y >= level->fieldy)
7398 level->field[x][y] = EL_EMPTY;
7400 // copy elements to runtime playfield array
7401 for (x = 0; x < MAX_LEV_FIELDX; x++)
7402 for (y = 0; y < MAX_LEV_FIELDY; y++)
7403 Tile[x][y] = level->field[x][y];
7405 // initialize level size variables for faster access
7406 lev_fieldx = level->fieldx;
7407 lev_fieldy = level->fieldy;
7409 // determine border element for this level
7410 if (level->file_info.type == LEVEL_FILE_TYPE_DC)
7411 BorderElement = EL_EMPTY; // (in editor, SetBorderElement() is used)
7416 static void LoadLevel_InitNativeEngines(struct LevelInfo *level)
7418 struct LevelFileInfo *level_file_info = &level->file_info;
7420 if (level_file_info->type == LEVEL_FILE_TYPE_RND)
7421 CopyNativeLevel_RND_to_Native(level);
7424 static void LoadLevelTemplate_LoadAndInit(void)
7426 LoadLevelFromFileInfo(&level_template, &level_template.file_info, FALSE);
7428 LoadLevel_InitVersion(&level_template);
7429 LoadLevel_InitElements(&level_template);
7430 LoadLevel_InitSettings(&level_template);
7432 ActivateLevelTemplate();
7435 void LoadLevelTemplate(int nr)
7437 if (!fileExists(getGlobalLevelTemplateFilename()))
7439 Warn("no level template found for this level");
7444 setLevelFileInfo(&level_template.file_info, nr);
7446 LoadLevelTemplate_LoadAndInit();
7449 static void LoadNetworkLevelTemplate(struct NetworkLevelInfo *network_level)
7451 copyLevelFileInfo(&network_level->tmpl_info, &level_template.file_info);
7453 LoadLevelTemplate_LoadAndInit();
7456 static void LoadLevel_LoadAndInit(struct NetworkLevelInfo *network_level)
7458 LoadLevelFromFileInfo(&level, &level.file_info, FALSE);
7460 if (level.use_custom_template)
7462 if (network_level != NULL)
7463 LoadNetworkLevelTemplate(network_level);
7465 LoadLevelTemplate(-1);
7468 LoadLevel_InitVersion(&level);
7469 LoadLevel_InitElements(&level);
7470 LoadLevel_InitPlayfield(&level);
7471 LoadLevel_InitSettings(&level);
7473 LoadLevel_InitNativeEngines(&level);
7476 void LoadLevel(int nr)
7478 SetLevelSetInfo(leveldir_current->identifier, nr);
7480 setLevelFileInfo(&level.file_info, nr);
7482 LoadLevel_LoadAndInit(NULL);
7485 void LoadLevelInfoOnly(int nr)
7487 setLevelFileInfo(&level.file_info, nr);
7489 LoadLevelFromFileInfo(&level, &level.file_info, TRUE);
7492 void LoadNetworkLevel(struct NetworkLevelInfo *network_level)
7494 SetLevelSetInfo(network_level->leveldir_identifier,
7495 network_level->file_info.nr);
7497 copyLevelFileInfo(&network_level->file_info, &level.file_info);
7499 LoadLevel_LoadAndInit(network_level);
7502 static int SaveLevel_VERS(FILE *file, struct LevelInfo *level)
7506 chunk_size += putFileVersion(file, level->file_version);
7507 chunk_size += putFileVersion(file, level->game_version);
7512 static int SaveLevel_DATE(FILE *file, struct LevelInfo *level)
7516 chunk_size += putFile16BitBE(file, level->creation_date.year);
7517 chunk_size += putFile8Bit(file, level->creation_date.month);
7518 chunk_size += putFile8Bit(file, level->creation_date.day);
7523 #if ENABLE_HISTORIC_CHUNKS
7524 static void SaveLevel_HEAD(FILE *file, struct LevelInfo *level)
7528 putFile8Bit(file, level->fieldx);
7529 putFile8Bit(file, level->fieldy);
7531 putFile16BitBE(file, level->time);
7532 putFile16BitBE(file, level->gems_needed);
7534 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
7535 putFile8Bit(file, level->name[i]);
7537 for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
7538 putFile8Bit(file, level->score[i]);
7540 for (i = 0; i < STD_ELEMENT_CONTENTS; i++)
7541 for (y = 0; y < 3; y++)
7542 for (x = 0; x < 3; x++)
7543 putFile8Bit(file, (level->encoding_16bit_yamyam ? EL_EMPTY :
7544 level->yamyam_content[i].e[x][y]));
7545 putFile8Bit(file, level->amoeba_speed);
7546 putFile8Bit(file, level->time_magic_wall);
7547 putFile8Bit(file, level->time_wheel);
7548 putFile8Bit(file, (level->encoding_16bit_amoeba ? EL_EMPTY :
7549 level->amoeba_content));
7550 putFile8Bit(file, (level->initial_player_stepsize == STEPSIZE_FAST ? 1 : 0));
7551 putFile8Bit(file, (level->initial_gravity ? 1 : 0));
7552 putFile8Bit(file, (level->encoding_16bit_field ? 1 : 0));
7553 putFile8Bit(file, (level->em_slippery_gems ? 1 : 0));
7555 putFile8Bit(file, (level->use_custom_template ? 1 : 0));
7557 putFile8Bit(file, (level->block_last_field ? 1 : 0));
7558 putFile8Bit(file, (level->sp_block_last_field ? 1 : 0));
7559 putFile32BitBE(file, level->can_move_into_acid_bits);
7560 putFile8Bit(file, level->dont_collide_with_bits);
7562 putFile8Bit(file, (level->use_spring_bug ? 1 : 0));
7563 putFile8Bit(file, (level->use_step_counter ? 1 : 0));
7565 putFile8Bit(file, (level->instant_relocation ? 1 : 0));
7566 putFile8Bit(file, (level->can_pass_to_walkable ? 1 : 0));
7567 putFile8Bit(file, (level->grow_into_diggable ? 1 : 0));
7569 putFile8Bit(file, level->game_engine_type);
7571 WriteUnusedBytesToFile(file, LEVEL_CHUNK_HEAD_UNUSED);
7575 static int SaveLevel_NAME(FILE *file, struct LevelInfo *level)
7580 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
7581 chunk_size += putFile8Bit(file, level->name[i]);
7586 static int SaveLevel_AUTH(FILE *file, struct LevelInfo *level)
7591 for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
7592 chunk_size += putFile8Bit(file, level->author[i]);
7597 #if ENABLE_HISTORIC_CHUNKS
7598 static int SaveLevel_BODY(FILE *file, struct LevelInfo *level)
7603 for (y = 0; y < level->fieldy; y++)
7604 for (x = 0; x < level->fieldx; x++)
7605 if (level->encoding_16bit_field)
7606 chunk_size += putFile16BitBE(file, level->field[x][y]);
7608 chunk_size += putFile8Bit(file, level->field[x][y]);
7614 static int SaveLevel_BODY(FILE *file, struct LevelInfo *level)
7619 for (y = 0; y < level->fieldy; y++)
7620 for (x = 0; x < level->fieldx; x++)
7621 chunk_size += putFile16BitBE(file, level->field[x][y]);
7626 #if ENABLE_HISTORIC_CHUNKS
7627 static void SaveLevel_CONT(FILE *file, struct LevelInfo *level)
7631 putFile8Bit(file, EL_YAMYAM);
7632 putFile8Bit(file, level->num_yamyam_contents);
7633 putFile8Bit(file, 0);
7634 putFile8Bit(file, 0);
7636 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
7637 for (y = 0; y < 3; y++)
7638 for (x = 0; x < 3; x++)
7639 if (level->encoding_16bit_field)
7640 putFile16BitBE(file, level->yamyam_content[i].e[x][y]);
7642 putFile8Bit(file, level->yamyam_content[i].e[x][y]);
7646 #if ENABLE_HISTORIC_CHUNKS
7647 static void SaveLevel_CNT2(FILE *file, struct LevelInfo *level, int element)
7650 int num_contents, content_xsize, content_ysize;
7651 int content_array[MAX_ELEMENT_CONTENTS][3][3];
7653 if (element == EL_YAMYAM)
7655 num_contents = level->num_yamyam_contents;
7659 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
7660 for (y = 0; y < 3; y++)
7661 for (x = 0; x < 3; x++)
7662 content_array[i][x][y] = level->yamyam_content[i].e[x][y];
7664 else if (element == EL_BD_AMOEBA)
7670 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
7671 for (y = 0; y < 3; y++)
7672 for (x = 0; x < 3; x++)
7673 content_array[i][x][y] = EL_EMPTY;
7674 content_array[0][0][0] = level->amoeba_content;
7678 // chunk header already written -- write empty chunk data
7679 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_SIZE);
7681 Warn("cannot save content for element '%d'", element);
7686 putFile16BitBE(file, element);
7687 putFile8Bit(file, num_contents);
7688 putFile8Bit(file, content_xsize);
7689 putFile8Bit(file, content_ysize);
7691 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_UNUSED);
7693 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
7694 for (y = 0; y < 3; y++)
7695 for (x = 0; x < 3; x++)
7696 putFile16BitBE(file, content_array[i][x][y]);
7700 #if ENABLE_HISTORIC_CHUNKS
7701 static int SaveLevel_CNT3(FILE *file, struct LevelInfo *level, int element)
7703 int envelope_nr = element - EL_ENVELOPE_1;
7704 int envelope_len = strlen(level->envelope_text[envelope_nr]) + 1;
7708 chunk_size += putFile16BitBE(file, element);
7709 chunk_size += putFile16BitBE(file, envelope_len);
7710 chunk_size += putFile8Bit(file, level->envelope_xsize[envelope_nr]);
7711 chunk_size += putFile8Bit(file, level->envelope_ysize[envelope_nr]);
7713 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT3_UNUSED);
7714 chunk_size += LEVEL_CHUNK_CNT3_UNUSED;
7716 for (i = 0; i < envelope_len; i++)
7717 chunk_size += putFile8Bit(file, level->envelope_text[envelope_nr][i]);
7723 #if ENABLE_HISTORIC_CHUNKS
7724 static void SaveLevel_CUS1(FILE *file, struct LevelInfo *level,
7725 int num_changed_custom_elements)
7729 putFile16BitBE(file, num_changed_custom_elements);
7731 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7733 int element = EL_CUSTOM_START + i;
7735 struct ElementInfo *ei = &element_info[element];
7737 if (ei->properties[EP_BITFIELD_BASE_NR] != EP_BITMASK_DEFAULT)
7739 if (check < num_changed_custom_elements)
7741 putFile16BitBE(file, element);
7742 putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
7749 if (check != num_changed_custom_elements) // should not happen
7750 Warn("inconsistent number of custom element properties");
7754 #if ENABLE_HISTORIC_CHUNKS
7755 static void SaveLevel_CUS2(FILE *file, struct LevelInfo *level,
7756 int num_changed_custom_elements)
7760 putFile16BitBE(file, num_changed_custom_elements);
7762 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7764 int element = EL_CUSTOM_START + i;
7766 if (element_info[element].change->target_element != EL_EMPTY_SPACE)
7768 if (check < num_changed_custom_elements)
7770 putFile16BitBE(file, element);
7771 putFile16BitBE(file, element_info[element].change->target_element);
7778 if (check != num_changed_custom_elements) // should not happen
7779 Warn("inconsistent number of custom target elements");
7783 #if ENABLE_HISTORIC_CHUNKS
7784 static void SaveLevel_CUS3(FILE *file, struct LevelInfo *level,
7785 int num_changed_custom_elements)
7787 int i, j, x, y, check = 0;
7789 putFile16BitBE(file, num_changed_custom_elements);
7791 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7793 int element = EL_CUSTOM_START + i;
7794 struct ElementInfo *ei = &element_info[element];
7796 if (ei->modified_settings)
7798 if (check < num_changed_custom_elements)
7800 putFile16BitBE(file, element);
7802 for (j = 0; j < MAX_ELEMENT_NAME_LEN; j++)
7803 putFile8Bit(file, ei->description[j]);
7805 putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
7807 // some free bytes for future properties and padding
7808 WriteUnusedBytesToFile(file, 7);
7810 putFile8Bit(file, ei->use_gfx_element);
7811 putFile16BitBE(file, ei->gfx_element_initial);
7813 putFile8Bit(file, ei->collect_score_initial);
7814 putFile8Bit(file, ei->collect_count_initial);
7816 putFile16BitBE(file, ei->push_delay_fixed);
7817 putFile16BitBE(file, ei->push_delay_random);
7818 putFile16BitBE(file, ei->move_delay_fixed);
7819 putFile16BitBE(file, ei->move_delay_random);
7821 putFile16BitBE(file, ei->move_pattern);
7822 putFile8Bit(file, ei->move_direction_initial);
7823 putFile8Bit(file, ei->move_stepsize);
7825 for (y = 0; y < 3; y++)
7826 for (x = 0; x < 3; x++)
7827 putFile16BitBE(file, ei->content.e[x][y]);
7829 putFile32BitBE(file, ei->change->events);
7831 putFile16BitBE(file, ei->change->target_element);
7833 putFile16BitBE(file, ei->change->delay_fixed);
7834 putFile16BitBE(file, ei->change->delay_random);
7835 putFile16BitBE(file, ei->change->delay_frames);
7837 putFile16BitBE(file, ei->change->initial_trigger_element);
7839 putFile8Bit(file, ei->change->explode);
7840 putFile8Bit(file, ei->change->use_target_content);
7841 putFile8Bit(file, ei->change->only_if_complete);
7842 putFile8Bit(file, ei->change->use_random_replace);
7844 putFile8Bit(file, ei->change->random_percentage);
7845 putFile8Bit(file, ei->change->replace_when);
7847 for (y = 0; y < 3; y++)
7848 for (x = 0; x < 3; x++)
7849 putFile16BitBE(file, ei->change->content.e[x][y]);
7851 putFile8Bit(file, ei->slippery_type);
7853 // some free bytes for future properties and padding
7854 WriteUnusedBytesToFile(file, LEVEL_CPART_CUS3_UNUSED);
7861 if (check != num_changed_custom_elements) // should not happen
7862 Warn("inconsistent number of custom element properties");
7866 #if ENABLE_HISTORIC_CHUNKS
7867 static void SaveLevel_CUS4(FILE *file, struct LevelInfo *level, int element)
7869 struct ElementInfo *ei = &element_info[element];
7872 // ---------- custom element base property values (96 bytes) ----------------
7874 putFile16BitBE(file, element);
7876 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
7877 putFile8Bit(file, ei->description[i]);
7879 putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
7881 WriteUnusedBytesToFile(file, 4); // reserved for more base properties
7883 putFile8Bit(file, ei->num_change_pages);
7885 putFile16BitBE(file, ei->ce_value_fixed_initial);
7886 putFile16BitBE(file, ei->ce_value_random_initial);
7887 putFile8Bit(file, ei->use_last_ce_value);
7889 putFile8Bit(file, ei->use_gfx_element);
7890 putFile16BitBE(file, ei->gfx_element_initial);
7892 putFile8Bit(file, ei->collect_score_initial);
7893 putFile8Bit(file, ei->collect_count_initial);
7895 putFile8Bit(file, ei->drop_delay_fixed);
7896 putFile8Bit(file, ei->push_delay_fixed);
7897 putFile8Bit(file, ei->drop_delay_random);
7898 putFile8Bit(file, ei->push_delay_random);
7899 putFile16BitBE(file, ei->move_delay_fixed);
7900 putFile16BitBE(file, ei->move_delay_random);
7902 // bits 0 - 15 of "move_pattern" ...
7903 putFile16BitBE(file, ei->move_pattern & 0xffff);
7904 putFile8Bit(file, ei->move_direction_initial);
7905 putFile8Bit(file, ei->move_stepsize);
7907 putFile8Bit(file, ei->slippery_type);
7909 for (y = 0; y < 3; y++)
7910 for (x = 0; x < 3; x++)
7911 putFile16BitBE(file, ei->content.e[x][y]);
7913 putFile16BitBE(file, ei->move_enter_element);
7914 putFile16BitBE(file, ei->move_leave_element);
7915 putFile8Bit(file, ei->move_leave_type);
7917 // ... bits 16 - 31 of "move_pattern" (not nice, but downward compatible)
7918 putFile16BitBE(file, (ei->move_pattern >> 16) & 0xffff);
7920 putFile8Bit(file, ei->access_direction);
7922 putFile8Bit(file, ei->explosion_delay);
7923 putFile8Bit(file, ei->ignition_delay);
7924 putFile8Bit(file, ei->explosion_type);
7926 // some free bytes for future custom property values and padding
7927 WriteUnusedBytesToFile(file, 1);
7929 // ---------- change page property values (48 bytes) ------------------------
7931 for (i = 0; i < ei->num_change_pages; i++)
7933 struct ElementChangeInfo *change = &ei->change_page[i];
7934 unsigned int event_bits;
7936 // bits 0 - 31 of "has_event[]" ...
7938 for (j = 0; j < MIN(NUM_CHANGE_EVENTS, 32); j++)
7939 if (change->has_event[j])
7940 event_bits |= (1u << j);
7941 putFile32BitBE(file, event_bits);
7943 putFile16BitBE(file, change->target_element);
7945 putFile16BitBE(file, change->delay_fixed);
7946 putFile16BitBE(file, change->delay_random);
7947 putFile16BitBE(file, change->delay_frames);
7949 putFile16BitBE(file, change->initial_trigger_element);
7951 putFile8Bit(file, change->explode);
7952 putFile8Bit(file, change->use_target_content);
7953 putFile8Bit(file, change->only_if_complete);
7954 putFile8Bit(file, change->use_random_replace);
7956 putFile8Bit(file, change->random_percentage);
7957 putFile8Bit(file, change->replace_when);
7959 for (y = 0; y < 3; y++)
7960 for (x = 0; x < 3; x++)
7961 putFile16BitBE(file, change->target_content.e[x][y]);
7963 putFile8Bit(file, change->can_change);
7965 putFile8Bit(file, change->trigger_side);
7967 putFile8Bit(file, change->trigger_player);
7968 putFile8Bit(file, (change->trigger_page == CH_PAGE_ANY ? CH_PAGE_ANY_FILE :
7969 log_2(change->trigger_page)));
7971 putFile8Bit(file, change->has_action);
7972 putFile8Bit(file, change->action_type);
7973 putFile8Bit(file, change->action_mode);
7974 putFile16BitBE(file, change->action_arg);
7976 // ... bits 32 - 39 of "has_event[]" (not nice, but downward compatible)
7978 for (j = 32; j < NUM_CHANGE_EVENTS; j++)
7979 if (change->has_event[j])
7980 event_bits |= (1u << (j - 32));
7981 putFile8Bit(file, event_bits);
7986 #if ENABLE_HISTORIC_CHUNKS
7987 static void SaveLevel_GRP1(FILE *file, struct LevelInfo *level, int element)
7989 struct ElementInfo *ei = &element_info[element];
7990 struct ElementGroupInfo *group = ei->group;
7993 putFile16BitBE(file, element);
7995 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
7996 putFile8Bit(file, ei->description[i]);
7998 putFile8Bit(file, group->num_elements);
8000 putFile8Bit(file, ei->use_gfx_element);
8001 putFile16BitBE(file, ei->gfx_element_initial);
8003 putFile8Bit(file, group->choice_mode);
8005 // some free bytes for future values and padding
8006 WriteUnusedBytesToFile(file, 3);
8008 for (i = 0; i < MAX_ELEMENTS_IN_GROUP; i++)
8009 putFile16BitBE(file, group->element[i]);
8013 static int SaveLevel_MicroChunk(FILE *file, struct LevelFileConfigInfo *entry,
8014 boolean write_element)
8016 int save_type = entry->save_type;
8017 int data_type = entry->data_type;
8018 int conf_type = entry->conf_type;
8019 int byte_mask = conf_type & CONF_MASK_BYTES;
8020 int element = entry->element;
8021 int default_value = entry->default_value;
8023 boolean modified = FALSE;
8025 if (byte_mask != CONF_MASK_MULTI_BYTES)
8027 void *value_ptr = entry->value;
8028 int value = (data_type == TYPE_BOOLEAN ? *(boolean *)value_ptr :
8031 // check if any settings have been modified before saving them
8032 if (value != default_value)
8035 // do not save if explicitly told or if unmodified default settings
8036 if ((save_type == SAVE_CONF_NEVER) ||
8037 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
8041 num_bytes += putFile16BitBE(file, element);
8043 num_bytes += putFile8Bit(file, conf_type);
8044 num_bytes += (byte_mask == CONF_MASK_1_BYTE ? putFile8Bit (file, value) :
8045 byte_mask == CONF_MASK_2_BYTE ? putFile16BitBE(file, value) :
8046 byte_mask == CONF_MASK_4_BYTE ? putFile32BitBE(file, value) :
8049 else if (data_type == TYPE_STRING)
8051 char *default_string = entry->default_string;
8052 char *string = (char *)(entry->value);
8053 int string_length = strlen(string);
8056 // check if any settings have been modified before saving them
8057 if (!strEqual(string, default_string))
8060 // do not save if explicitly told or if unmodified default settings
8061 if ((save_type == SAVE_CONF_NEVER) ||
8062 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
8066 num_bytes += putFile16BitBE(file, element);
8068 num_bytes += putFile8Bit(file, conf_type);
8069 num_bytes += putFile16BitBE(file, string_length);
8071 for (i = 0; i < string_length; i++)
8072 num_bytes += putFile8Bit(file, string[i]);
8074 else if (data_type == TYPE_ELEMENT_LIST)
8076 int *element_array = (int *)(entry->value);
8077 int num_elements = *(int *)(entry->num_entities);
8080 // check if any settings have been modified before saving them
8081 for (i = 0; i < num_elements; i++)
8082 if (element_array[i] != default_value)
8085 // do not save if explicitly told or if unmodified default settings
8086 if ((save_type == SAVE_CONF_NEVER) ||
8087 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
8091 num_bytes += putFile16BitBE(file, element);
8093 num_bytes += putFile8Bit(file, conf_type);
8094 num_bytes += putFile16BitBE(file, num_elements * CONF_ELEMENT_NUM_BYTES);
8096 for (i = 0; i < num_elements; i++)
8097 num_bytes += putFile16BitBE(file, element_array[i]);
8099 else if (data_type == TYPE_CONTENT_LIST)
8101 struct Content *content = (struct Content *)(entry->value);
8102 int num_contents = *(int *)(entry->num_entities);
8105 // check if any settings have been modified before saving them
8106 for (i = 0; i < num_contents; i++)
8107 for (y = 0; y < 3; y++)
8108 for (x = 0; x < 3; x++)
8109 if (content[i].e[x][y] != default_value)
8112 // do not save if explicitly told or if unmodified default settings
8113 if ((save_type == SAVE_CONF_NEVER) ||
8114 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
8118 num_bytes += putFile16BitBE(file, element);
8120 num_bytes += putFile8Bit(file, conf_type);
8121 num_bytes += putFile16BitBE(file, num_contents * CONF_CONTENT_NUM_BYTES);
8123 for (i = 0; i < num_contents; i++)
8124 for (y = 0; y < 3; y++)
8125 for (x = 0; x < 3; x++)
8126 num_bytes += putFile16BitBE(file, content[i].e[x][y]);
8132 static int SaveLevel_INFO(FILE *file, struct LevelInfo *level)
8137 li = *level; // copy level data into temporary buffer
8139 for (i = 0; chunk_config_INFO[i].data_type != -1; i++)
8140 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_INFO[i], FALSE);
8145 static int SaveLevel_ELEM(FILE *file, struct LevelInfo *level)
8150 li = *level; // copy level data into temporary buffer
8152 for (i = 0; chunk_config_ELEM[i].data_type != -1; i++)
8153 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_ELEM[i], TRUE);
8158 static int SaveLevel_NOTE(FILE *file, struct LevelInfo *level, int element)
8160 int envelope_nr = element - EL_ENVELOPE_1;
8164 chunk_size += putFile16BitBE(file, element);
8166 // copy envelope data into temporary buffer
8167 xx_envelope = level->envelope[envelope_nr];
8169 for (i = 0; chunk_config_NOTE[i].data_type != -1; i++)
8170 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_NOTE[i], FALSE);
8175 static int SaveLevel_CUSX(FILE *file, struct LevelInfo *level, int element)
8177 struct ElementInfo *ei = &element_info[element];
8181 chunk_size += putFile16BitBE(file, element);
8183 xx_ei = *ei; // copy element data into temporary buffer
8185 // set default description string for this specific element
8186 strcpy(xx_default_description, getDefaultElementDescription(ei));
8188 for (i = 0; chunk_config_CUSX_base[i].data_type != -1; i++)
8189 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_CUSX_base[i], FALSE);
8191 for (i = 0; i < ei->num_change_pages; i++)
8193 struct ElementChangeInfo *change = &ei->change_page[i];
8195 xx_current_change_page = i;
8197 xx_change = *change; // copy change data into temporary buffer
8200 setEventBitsFromEventFlags(change);
8202 for (j = 0; chunk_config_CUSX_change[j].data_type != -1; j++)
8203 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_CUSX_change[j],
8210 static int SaveLevel_GRPX(FILE *file, struct LevelInfo *level, int element)
8212 struct ElementInfo *ei = &element_info[element];
8213 struct ElementGroupInfo *group = ei->group;
8217 chunk_size += putFile16BitBE(file, element);
8219 xx_ei = *ei; // copy element data into temporary buffer
8220 xx_group = *group; // copy group data into temporary buffer
8222 // set default description string for this specific element
8223 strcpy(xx_default_description, getDefaultElementDescription(ei));
8225 for (i = 0; chunk_config_GRPX[i].data_type != -1; i++)
8226 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_GRPX[i], FALSE);
8231 static int SaveLevel_EMPX(FILE *file, struct LevelInfo *level, int element)
8233 struct ElementInfo *ei = &element_info[element];
8237 chunk_size += putFile16BitBE(file, element);
8239 xx_ei = *ei; // copy element data into temporary buffer
8241 for (i = 0; chunk_config_EMPX[i].data_type != -1; i++)
8242 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_EMPX[i], FALSE);
8247 static void SaveLevelFromFilename(struct LevelInfo *level, char *filename,
8248 boolean save_as_template)
8254 if (!(file = fopen(filename, MODE_WRITE)))
8256 Warn("cannot save level file '%s'", filename);
8261 level->file_version = FILE_VERSION_ACTUAL;
8262 level->game_version = GAME_VERSION_ACTUAL;
8264 level->creation_date = getCurrentDate();
8266 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
8267 putFileChunkBE(file, "CAVE", CHUNK_SIZE_NONE);
8269 chunk_size = SaveLevel_VERS(NULL, level);
8270 putFileChunkBE(file, "VERS", chunk_size);
8271 SaveLevel_VERS(file, level);
8273 chunk_size = SaveLevel_DATE(NULL, level);
8274 putFileChunkBE(file, "DATE", chunk_size);
8275 SaveLevel_DATE(file, level);
8277 chunk_size = SaveLevel_NAME(NULL, level);
8278 putFileChunkBE(file, "NAME", chunk_size);
8279 SaveLevel_NAME(file, level);
8281 chunk_size = SaveLevel_AUTH(NULL, level);
8282 putFileChunkBE(file, "AUTH", chunk_size);
8283 SaveLevel_AUTH(file, level);
8285 chunk_size = SaveLevel_INFO(NULL, level);
8286 putFileChunkBE(file, "INFO", chunk_size);
8287 SaveLevel_INFO(file, level);
8289 chunk_size = SaveLevel_BODY(NULL, level);
8290 putFileChunkBE(file, "BODY", chunk_size);
8291 SaveLevel_BODY(file, level);
8293 chunk_size = SaveLevel_ELEM(NULL, level);
8294 if (chunk_size > LEVEL_CHUNK_ELEM_UNCHANGED) // save if changed
8296 putFileChunkBE(file, "ELEM", chunk_size);
8297 SaveLevel_ELEM(file, level);
8300 for (i = 0; i < NUM_ENVELOPES; i++)
8302 int element = EL_ENVELOPE_1 + i;
8304 chunk_size = SaveLevel_NOTE(NULL, level, element);
8305 if (chunk_size > LEVEL_CHUNK_NOTE_UNCHANGED) // save if changed
8307 putFileChunkBE(file, "NOTE", chunk_size);
8308 SaveLevel_NOTE(file, level, element);
8312 // if not using template level, check for non-default custom/group elements
8313 if (!level->use_custom_template || save_as_template)
8315 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
8317 int element = EL_CUSTOM_START + i;
8319 chunk_size = SaveLevel_CUSX(NULL, level, element);
8320 if (chunk_size > LEVEL_CHUNK_CUSX_UNCHANGED) // save if changed
8322 putFileChunkBE(file, "CUSX", chunk_size);
8323 SaveLevel_CUSX(file, level, element);
8327 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
8329 int element = EL_GROUP_START + i;
8331 chunk_size = SaveLevel_GRPX(NULL, level, element);
8332 if (chunk_size > LEVEL_CHUNK_GRPX_UNCHANGED) // save if changed
8334 putFileChunkBE(file, "GRPX", chunk_size);
8335 SaveLevel_GRPX(file, level, element);
8339 for (i = 0; i < NUM_EMPTY_ELEMENTS_ALL; i++)
8341 int element = GET_EMPTY_ELEMENT(i);
8343 chunk_size = SaveLevel_EMPX(NULL, level, element);
8344 if (chunk_size > LEVEL_CHUNK_EMPX_UNCHANGED) // save if changed
8346 putFileChunkBE(file, "EMPX", chunk_size);
8347 SaveLevel_EMPX(file, level, element);
8354 SetFilePermissions(filename, PERMS_PRIVATE);
8357 void SaveLevel(int nr)
8359 char *filename = getDefaultLevelFilename(nr);
8361 SaveLevelFromFilename(&level, filename, FALSE);
8364 void SaveLevelTemplate(void)
8366 char *filename = getLocalLevelTemplateFilename();
8368 SaveLevelFromFilename(&level, filename, TRUE);
8371 boolean SaveLevelChecked(int nr)
8373 char *filename = getDefaultLevelFilename(nr);
8374 boolean new_level = !fileExists(filename);
8375 boolean level_saved = FALSE;
8377 if (new_level || Request("Save this level and kill the old?", REQ_ASK))
8382 Request("Level saved!", REQ_CONFIRM);
8390 void DumpLevel(struct LevelInfo *level)
8392 if (level->no_level_file || level->no_valid_file)
8394 Warn("cannot dump -- no valid level file found");
8400 Print("Level xxx (file version %08d, game version %08d)\n",
8401 level->file_version, level->game_version);
8404 Print("Level author: '%s'\n", level->author);
8405 Print("Level title: '%s'\n", level->name);
8407 Print("Playfield size: %d x %d\n", level->fieldx, level->fieldy);
8409 Print("Level time: %d seconds\n", level->time);
8410 Print("Gems needed: %d\n", level->gems_needed);
8412 Print("Time for magic wall: %d seconds\n", level->time_magic_wall);
8413 Print("Time for wheel: %d seconds\n", level->time_wheel);
8414 Print("Time for light: %d seconds\n", level->time_light);
8415 Print("Time for timegate: %d seconds\n", level->time_timegate);
8417 Print("Amoeba speed: %d\n", level->amoeba_speed);
8420 Print("EM style slippery gems: %s\n", (level->em_slippery_gems ? "yes" : "no"));
8421 Print("Player blocks last field: %s\n", (level->block_last_field ? "yes" : "no"));
8422 Print("SP player blocks last field: %s\n", (level->sp_block_last_field ? "yes" : "no"));
8423 Print("use spring bug: %s\n", (level->use_spring_bug ? "yes" : "no"));
8424 Print("use step counter: %s\n", (level->use_step_counter ? "yes" : "no"));
8425 Print("rate time over score: %s\n", (level->rate_time_over_score ? "yes" : "no"));
8431 for (i = 0; i < NUM_ENVELOPES; i++)
8433 char *text = level->envelope[i].text;
8434 int text_len = strlen(text);
8435 boolean has_text = FALSE;
8437 for (j = 0; j < text_len; j++)
8438 if (text[j] != ' ' && text[j] != '\n')
8444 Print("Envelope %d:\n'%s'\n", i + 1, text);
8452 void DumpLevels(void)
8454 static LevelDirTree *dumplevel_leveldir = NULL;
8456 dumplevel_leveldir = getTreeInfoFromIdentifier(leveldir_first,
8457 global.dumplevel_leveldir);
8459 if (dumplevel_leveldir == NULL)
8460 Fail("no such level identifier: '%s'", global.dumplevel_leveldir);
8462 if (global.dumplevel_level_nr < dumplevel_leveldir->first_level ||
8463 global.dumplevel_level_nr > dumplevel_leveldir->last_level)
8464 Fail("no such level number: %d", global.dumplevel_level_nr);
8466 leveldir_current = dumplevel_leveldir;
8468 LoadLevel(global.dumplevel_level_nr);
8475 // ============================================================================
8476 // tape file functions
8477 // ============================================================================
8479 static void setTapeInfoToDefaults(void)
8483 // always start with reliable default values (empty tape)
8486 // default values (also for pre-1.2 tapes) with only the first player
8487 tape.player_participates[0] = TRUE;
8488 for (i = 1; i < MAX_PLAYERS; i++)
8489 tape.player_participates[i] = FALSE;
8491 // at least one (default: the first) player participates in every tape
8492 tape.num_participating_players = 1;
8494 tape.property_bits = TAPE_PROPERTY_NONE;
8496 tape.level_nr = level_nr;
8498 tape.changed = FALSE;
8499 tape.solved = FALSE;
8501 tape.recording = FALSE;
8502 tape.playing = FALSE;
8503 tape.pausing = FALSE;
8505 tape.scr_fieldx = SCR_FIELDX_DEFAULT;
8506 tape.scr_fieldy = SCR_FIELDY_DEFAULT;
8508 tape.no_info_chunk = TRUE;
8509 tape.no_valid_file = FALSE;
8512 static int getTapePosSize(struct TapeInfo *tape)
8514 int tape_pos_size = 0;
8516 if (tape->use_key_actions)
8517 tape_pos_size += tape->num_participating_players;
8519 if (tape->use_mouse_actions)
8520 tape_pos_size += 3; // x and y position and mouse button mask
8522 tape_pos_size += 1; // tape action delay value
8524 return tape_pos_size;
8527 static void setTapeActionFlags(struct TapeInfo *tape, int value)
8529 tape->use_key_actions = FALSE;
8530 tape->use_mouse_actions = FALSE;
8532 if (value != TAPE_USE_MOUSE_ACTIONS_ONLY)
8533 tape->use_key_actions = TRUE;
8535 if (value != TAPE_USE_KEY_ACTIONS_ONLY)
8536 tape->use_mouse_actions = TRUE;
8539 static int getTapeActionValue(struct TapeInfo *tape)
8541 return (tape->use_key_actions &&
8542 tape->use_mouse_actions ? TAPE_USE_KEY_AND_MOUSE_ACTIONS :
8543 tape->use_key_actions ? TAPE_USE_KEY_ACTIONS_ONLY :
8544 tape->use_mouse_actions ? TAPE_USE_MOUSE_ACTIONS_ONLY :
8545 TAPE_ACTIONS_DEFAULT);
8548 static int LoadTape_VERS(File *file, int chunk_size, struct TapeInfo *tape)
8550 tape->file_version = getFileVersion(file);
8551 tape->game_version = getFileVersion(file);
8556 static int LoadTape_HEAD(File *file, int chunk_size, struct TapeInfo *tape)
8560 tape->random_seed = getFile32BitBE(file);
8561 tape->date = getFile32BitBE(file);
8562 tape->length = getFile32BitBE(file);
8564 // read header fields that are new since version 1.2
8565 if (tape->file_version >= FILE_VERSION_1_2)
8567 byte store_participating_players = getFile8Bit(file);
8570 // since version 1.2, tapes store which players participate in the tape
8571 tape->num_participating_players = 0;
8572 for (i = 0; i < MAX_PLAYERS; i++)
8574 tape->player_participates[i] = FALSE;
8576 if (store_participating_players & (1 << i))
8578 tape->player_participates[i] = TRUE;
8579 tape->num_participating_players++;
8583 setTapeActionFlags(tape, getFile8Bit(file));
8585 tape->property_bits = getFile8Bit(file);
8586 tape->solved = getFile8Bit(file);
8588 engine_version = getFileVersion(file);
8589 if (engine_version > 0)
8590 tape->engine_version = engine_version;
8592 tape->engine_version = tape->game_version;
8598 static int LoadTape_SCRN(File *file, int chunk_size, struct TapeInfo *tape)
8600 tape->scr_fieldx = getFile8Bit(file);
8601 tape->scr_fieldy = getFile8Bit(file);
8606 static int LoadTape_INFO(File *file, int chunk_size, struct TapeInfo *tape)
8608 char *level_identifier = NULL;
8609 int level_identifier_size;
8612 tape->no_info_chunk = FALSE;
8614 level_identifier_size = getFile16BitBE(file);
8616 level_identifier = checked_malloc(level_identifier_size);
8618 for (i = 0; i < level_identifier_size; i++)
8619 level_identifier[i] = getFile8Bit(file);
8621 strncpy(tape->level_identifier, level_identifier, MAX_FILENAME_LEN);
8622 tape->level_identifier[MAX_FILENAME_LEN] = '\0';
8624 checked_free(level_identifier);
8626 tape->level_nr = getFile16BitBE(file);
8628 chunk_size = 2 + level_identifier_size + 2;
8633 static int LoadTape_BODY(File *file, int chunk_size, struct TapeInfo *tape)
8636 int tape_pos_size = getTapePosSize(tape);
8637 int chunk_size_expected = tape_pos_size * tape->length;
8639 if (chunk_size_expected != chunk_size)
8641 ReadUnusedBytesFromFile(file, chunk_size);
8642 return chunk_size_expected;
8645 for (i = 0; i < tape->length; i++)
8647 if (i >= MAX_TAPE_LEN)
8649 Warn("tape truncated -- size exceeds maximum tape size %d",
8652 // tape too large; read and ignore remaining tape data from this chunk
8653 for (;i < tape->length; i++)
8654 ReadUnusedBytesFromFile(file, tape_pos_size);
8659 if (tape->use_key_actions)
8661 for (j = 0; j < MAX_PLAYERS; j++)
8663 tape->pos[i].action[j] = MV_NONE;
8665 if (tape->player_participates[j])
8666 tape->pos[i].action[j] = getFile8Bit(file);
8670 if (tape->use_mouse_actions)
8672 tape->pos[i].action[TAPE_ACTION_LX] = getFile8Bit(file);
8673 tape->pos[i].action[TAPE_ACTION_LY] = getFile8Bit(file);
8674 tape->pos[i].action[TAPE_ACTION_BUTTON] = getFile8Bit(file);
8677 tape->pos[i].delay = getFile8Bit(file);
8679 if (tape->file_version == FILE_VERSION_1_0)
8681 // eliminate possible diagonal moves in old tapes
8682 // this is only for backward compatibility
8684 byte joy_dir[4] = { JOY_LEFT, JOY_RIGHT, JOY_UP, JOY_DOWN };
8685 byte action = tape->pos[i].action[0];
8686 int k, num_moves = 0;
8688 for (k = 0; k < 4; k++)
8690 if (action & joy_dir[k])
8692 tape->pos[i + num_moves].action[0] = joy_dir[k];
8694 tape->pos[i + num_moves].delay = 0;
8703 tape->length += num_moves;
8706 else if (tape->file_version < FILE_VERSION_2_0)
8708 // convert pre-2.0 tapes to new tape format
8710 if (tape->pos[i].delay > 1)
8713 tape->pos[i + 1] = tape->pos[i];
8714 tape->pos[i + 1].delay = 1;
8717 for (j = 0; j < MAX_PLAYERS; j++)
8718 tape->pos[i].action[j] = MV_NONE;
8719 tape->pos[i].delay--;
8726 if (checkEndOfFile(file))
8730 if (i != tape->length)
8731 chunk_size = tape_pos_size * i;
8736 static void LoadTape_SokobanSolution(char *filename)
8739 int move_delay = TILESIZE / level.initial_player_stepsize[0];
8741 if (!(file = openFile(filename, MODE_READ)))
8743 tape.no_valid_file = TRUE;
8748 while (!checkEndOfFile(file))
8750 unsigned char c = getByteFromFile(file);
8752 if (checkEndOfFile(file))
8759 tape.pos[tape.length].action[0] = MV_UP;
8760 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
8766 tape.pos[tape.length].action[0] = MV_DOWN;
8767 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
8773 tape.pos[tape.length].action[0] = MV_LEFT;
8774 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
8780 tape.pos[tape.length].action[0] = MV_RIGHT;
8781 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
8789 // ignore white-space characters
8793 tape.no_valid_file = TRUE;
8795 Warn("unsupported Sokoban solution file '%s' ['%d']", filename, c);
8803 if (tape.no_valid_file)
8806 tape.length_frames = GetTapeLengthFrames();
8807 tape.length_seconds = GetTapeLengthSeconds();
8810 void LoadTapeFromFilename(char *filename)
8812 char cookie[MAX_LINE_LEN];
8813 char chunk_name[CHUNK_ID_LEN + 1];
8817 // always start with reliable default values
8818 setTapeInfoToDefaults();
8820 if (strSuffix(filename, ".sln"))
8822 LoadTape_SokobanSolution(filename);
8827 if (!(file = openFile(filename, MODE_READ)))
8829 tape.no_valid_file = TRUE;
8834 getFileChunkBE(file, chunk_name, NULL);
8835 if (strEqual(chunk_name, "RND1"))
8837 getFile32BitBE(file); // not used
8839 getFileChunkBE(file, chunk_name, NULL);
8840 if (!strEqual(chunk_name, "TAPE"))
8842 tape.no_valid_file = TRUE;
8844 Warn("unknown format of tape file '%s'", filename);
8851 else // check for pre-2.0 file format with cookie string
8853 strcpy(cookie, chunk_name);
8854 if (getStringFromFile(file, &cookie[4], MAX_LINE_LEN - 4) == NULL)
8856 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
8857 cookie[strlen(cookie) - 1] = '\0';
8859 if (!checkCookieString(cookie, TAPE_COOKIE_TMPL))
8861 tape.no_valid_file = TRUE;
8863 Warn("unknown format of tape file '%s'", filename);
8870 if ((tape.file_version = getFileVersionFromCookieString(cookie)) == -1)
8872 tape.no_valid_file = TRUE;
8874 Warn("unsupported version of tape file '%s'", filename);
8881 // pre-2.0 tape files have no game version, so use file version here
8882 tape.game_version = tape.file_version;
8885 if (tape.file_version < FILE_VERSION_1_2)
8887 // tape files from versions before 1.2.0 without chunk structure
8888 LoadTape_HEAD(file, TAPE_CHUNK_HEAD_SIZE, &tape);
8889 LoadTape_BODY(file, 2 * tape.length, &tape);
8897 int (*loader)(File *, int, struct TapeInfo *);
8901 { "VERS", TAPE_CHUNK_VERS_SIZE, LoadTape_VERS },
8902 { "HEAD", TAPE_CHUNK_HEAD_SIZE, LoadTape_HEAD },
8903 { "SCRN", TAPE_CHUNK_SCRN_SIZE, LoadTape_SCRN },
8904 { "INFO", -1, LoadTape_INFO },
8905 { "BODY", -1, LoadTape_BODY },
8909 while (getFileChunkBE(file, chunk_name, &chunk_size))
8913 while (chunk_info[i].name != NULL &&
8914 !strEqual(chunk_name, chunk_info[i].name))
8917 if (chunk_info[i].name == NULL)
8919 Warn("unknown chunk '%s' in tape file '%s'",
8920 chunk_name, filename);
8922 ReadUnusedBytesFromFile(file, chunk_size);
8924 else if (chunk_info[i].size != -1 &&
8925 chunk_info[i].size != chunk_size)
8927 Warn("wrong size (%d) of chunk '%s' in tape file '%s'",
8928 chunk_size, chunk_name, filename);
8930 ReadUnusedBytesFromFile(file, chunk_size);
8934 // call function to load this tape chunk
8935 int chunk_size_expected =
8936 (chunk_info[i].loader)(file, chunk_size, &tape);
8938 // the size of some chunks cannot be checked before reading other
8939 // chunks first (like "HEAD" and "BODY") that contain some header
8940 // information, so check them here
8941 if (chunk_size_expected != chunk_size)
8943 Warn("wrong size (%d) of chunk '%s' in tape file '%s'",
8944 chunk_size, chunk_name, filename);
8952 tape.length_frames = GetTapeLengthFrames();
8953 tape.length_seconds = GetTapeLengthSeconds();
8956 Debug("files:LoadTapeFromFilename", "tape file version: %d",
8958 Debug("files:LoadTapeFromFilename", "tape game version: %d",
8960 Debug("files:LoadTapeFromFilename", "tape engine version: %d",
8961 tape.engine_version);
8965 void LoadTape(int nr)
8967 char *filename = getTapeFilename(nr);
8969 LoadTapeFromFilename(filename);
8972 void LoadSolutionTape(int nr)
8974 char *filename = getSolutionTapeFilename(nr);
8976 LoadTapeFromFilename(filename);
8978 if (TAPE_IS_EMPTY(tape))
8980 if (level.game_engine_type == GAME_ENGINE_TYPE_BD &&
8981 level.native_bd_level->replay != NULL)
8982 CopyNativeTape_BD_to_RND(&level);
8983 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP &&
8984 level.native_sp_level->demo.is_available)
8985 CopyNativeTape_SP_to_RND(&level);
8989 void LoadScoreTape(char *score_tape_basename, int nr)
8991 char *filename = getScoreTapeFilename(score_tape_basename, nr);
8993 LoadTapeFromFilename(filename);
8996 void LoadScoreCacheTape(char *score_tape_basename, int nr)
8998 char *filename = getScoreCacheTapeFilename(score_tape_basename, nr);
9000 LoadTapeFromFilename(filename);
9003 static boolean checkSaveTape_SCRN(struct TapeInfo *tape)
9005 // chunk required for team mode tapes with non-default screen size
9006 return (tape->num_participating_players > 1 &&
9007 (tape->scr_fieldx != SCR_FIELDX_DEFAULT ||
9008 tape->scr_fieldy != SCR_FIELDY_DEFAULT));
9011 static void SaveTape_VERS(FILE *file, struct TapeInfo *tape)
9013 putFileVersion(file, tape->file_version);
9014 putFileVersion(file, tape->game_version);
9017 static void SaveTape_HEAD(FILE *file, struct TapeInfo *tape)
9020 byte store_participating_players = 0;
9022 // set bits for participating players for compact storage
9023 for (i = 0; i < MAX_PLAYERS; i++)
9024 if (tape->player_participates[i])
9025 store_participating_players |= (1 << i);
9027 putFile32BitBE(file, tape->random_seed);
9028 putFile32BitBE(file, tape->date);
9029 putFile32BitBE(file, tape->length);
9031 putFile8Bit(file, store_participating_players);
9033 putFile8Bit(file, getTapeActionValue(tape));
9035 putFile8Bit(file, tape->property_bits);
9036 putFile8Bit(file, tape->solved);
9038 putFileVersion(file, tape->engine_version);
9041 static void SaveTape_SCRN(FILE *file, struct TapeInfo *tape)
9043 putFile8Bit(file, tape->scr_fieldx);
9044 putFile8Bit(file, tape->scr_fieldy);
9047 static void SaveTape_INFO(FILE *file, struct TapeInfo *tape)
9049 int level_identifier_size = strlen(tape->level_identifier) + 1;
9052 putFile16BitBE(file, level_identifier_size);
9054 for (i = 0; i < level_identifier_size; i++)
9055 putFile8Bit(file, tape->level_identifier[i]);
9057 putFile16BitBE(file, tape->level_nr);
9060 static void SaveTape_BODY(FILE *file, struct TapeInfo *tape)
9064 for (i = 0; i < tape->length; i++)
9066 if (tape->use_key_actions)
9068 for (j = 0; j < MAX_PLAYERS; j++)
9069 if (tape->player_participates[j])
9070 putFile8Bit(file, tape->pos[i].action[j]);
9073 if (tape->use_mouse_actions)
9075 putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_LX]);
9076 putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_LY]);
9077 putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_BUTTON]);
9080 putFile8Bit(file, tape->pos[i].delay);
9084 void SaveTapeToFilename(char *filename)
9088 int info_chunk_size;
9089 int body_chunk_size;
9091 if (!(file = fopen(filename, MODE_WRITE)))
9093 Warn("cannot save level recording file '%s'", filename);
9098 tape_pos_size = getTapePosSize(&tape);
9100 info_chunk_size = 2 + (strlen(tape.level_identifier) + 1) + 2;
9101 body_chunk_size = tape_pos_size * tape.length;
9103 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
9104 putFileChunkBE(file, "TAPE", CHUNK_SIZE_NONE);
9106 putFileChunkBE(file, "VERS", TAPE_CHUNK_VERS_SIZE);
9107 SaveTape_VERS(file, &tape);
9109 putFileChunkBE(file, "HEAD", TAPE_CHUNK_HEAD_SIZE);
9110 SaveTape_HEAD(file, &tape);
9112 if (checkSaveTape_SCRN(&tape))
9114 putFileChunkBE(file, "SCRN", TAPE_CHUNK_SCRN_SIZE);
9115 SaveTape_SCRN(file, &tape);
9118 putFileChunkBE(file, "INFO", info_chunk_size);
9119 SaveTape_INFO(file, &tape);
9121 putFileChunkBE(file, "BODY", body_chunk_size);
9122 SaveTape_BODY(file, &tape);
9126 SetFilePermissions(filename, PERMS_PRIVATE);
9129 static void SaveTapeExt(char *filename)
9133 tape.file_version = FILE_VERSION_ACTUAL;
9134 tape.game_version = GAME_VERSION_ACTUAL;
9136 tape.num_participating_players = 0;
9138 // count number of participating players
9139 for (i = 0; i < MAX_PLAYERS; i++)
9140 if (tape.player_participates[i])
9141 tape.num_participating_players++;
9143 SaveTapeToFilename(filename);
9145 tape.changed = FALSE;
9148 void SaveTape(int nr)
9150 char *filename = getTapeFilename(nr);
9152 InitTapeDirectory(leveldir_current->subdir);
9154 SaveTapeExt(filename);
9157 void SaveScoreTape(int nr)
9159 char *filename = getScoreTapeFilename(tape.score_tape_basename, nr);
9161 // used instead of "leveldir_current->subdir" (for network games)
9162 InitScoreTapeDirectory(levelset.identifier, nr);
9164 SaveTapeExt(filename);
9167 static boolean SaveTapeCheckedExt(int nr, char *msg_replace, char *msg_saved,
9168 unsigned int req_state_added)
9170 char *filename = getTapeFilename(nr);
9171 boolean new_tape = !fileExists(filename);
9172 boolean tape_saved = FALSE;
9174 if (new_tape || Request(msg_replace, REQ_ASK | req_state_added))
9179 Request(msg_saved, REQ_CONFIRM | req_state_added);
9187 boolean SaveTapeChecked(int nr)
9189 return SaveTapeCheckedExt(nr, "Replace old tape?", "Tape saved!", 0);
9192 boolean SaveTapeChecked_LevelSolved(int nr)
9194 return SaveTapeCheckedExt(nr, "Level solved! Replace old tape?",
9195 "Level solved! Tape saved!", REQ_STAY_OPEN);
9198 void DumpTape(struct TapeInfo *tape)
9200 int tape_frame_counter;
9203 if (tape->no_valid_file)
9205 Warn("cannot dump -- no valid tape file found");
9212 Print("Tape of Level %03d (file version %08d, game version %08d)\n",
9213 tape->level_nr, tape->file_version, tape->game_version);
9214 Print(" (effective engine version %08d)\n",
9215 tape->engine_version);
9216 Print("Level series identifier: '%s'\n", tape->level_identifier);
9218 Print("Solution tape: %s\n",
9219 tape->solved ? "yes" :
9220 tape->game_version < VERSION_IDENT(4,3,2,3) ? "unknown" : "no");
9222 Print("Special tape properties: ");
9223 if (tape->property_bits == TAPE_PROPERTY_NONE)
9225 if (tape->property_bits & TAPE_PROPERTY_EM_RANDOM_BUG)
9226 Print("[em_random_bug]");
9227 if (tape->property_bits & TAPE_PROPERTY_GAME_SPEED)
9228 Print("[game_speed]");
9229 if (tape->property_bits & TAPE_PROPERTY_PAUSE_MODE)
9231 if (tape->property_bits & TAPE_PROPERTY_SINGLE_STEP)
9232 Print("[single_step]");
9233 if (tape->property_bits & TAPE_PROPERTY_SNAPSHOT)
9234 Print("[snapshot]");
9235 if (tape->property_bits & TAPE_PROPERTY_REPLAYED)
9236 Print("[replayed]");
9237 if (tape->property_bits & TAPE_PROPERTY_TAS_KEYS)
9238 Print("[tas_keys]");
9239 if (tape->property_bits & TAPE_PROPERTY_SMALL_GRAPHICS)
9240 Print("[small_graphics]");
9243 int year2 = tape->date / 10000;
9244 int year4 = (year2 < 70 ? 2000 + year2 : 1900 + year2);
9245 int month_index_raw = (tape->date / 100) % 100;
9246 int month_index = month_index_raw % 12; // prevent invalid index
9247 int month = month_index + 1;
9248 int day = tape->date % 100;
9250 Print("Tape date: %04d-%02d-%02d\n", year4, month, day);
9254 tape_frame_counter = 0;
9256 for (i = 0; i < tape->length; i++)
9258 if (i >= MAX_TAPE_LEN)
9263 for (j = 0; j < MAX_PLAYERS; j++)
9265 if (tape->player_participates[j])
9267 int action = tape->pos[i].action[j];
9269 Print("%d:%02x ", j, action);
9270 Print("[%c%c%c%c|%c%c] - ",
9271 (action & JOY_LEFT ? '<' : ' '),
9272 (action & JOY_RIGHT ? '>' : ' '),
9273 (action & JOY_UP ? '^' : ' '),
9274 (action & JOY_DOWN ? 'v' : ' '),
9275 (action & JOY_BUTTON_1 ? '1' : ' '),
9276 (action & JOY_BUTTON_2 ? '2' : ' '));
9280 Print("(%03d) ", tape->pos[i].delay);
9281 Print("[%05d]\n", tape_frame_counter);
9283 tape_frame_counter += tape->pos[i].delay;
9289 void DumpTapes(void)
9291 static LevelDirTree *dumptape_leveldir = NULL;
9293 dumptape_leveldir = getTreeInfoFromIdentifier(leveldir_first,
9294 global.dumptape_leveldir);
9296 if (dumptape_leveldir == NULL)
9297 Fail("no such level identifier: '%s'", global.dumptape_leveldir);
9299 if (global.dumptape_level_nr < dumptape_leveldir->first_level ||
9300 global.dumptape_level_nr > dumptape_leveldir->last_level)
9301 Fail("no such level number: %d", global.dumptape_level_nr);
9303 leveldir_current = dumptape_leveldir;
9305 if (options.mytapes)
9306 LoadTape(global.dumptape_level_nr);
9308 LoadSolutionTape(global.dumptape_level_nr);
9316 // ============================================================================
9317 // score file functions
9318 // ============================================================================
9320 static void setScoreInfoToDefaultsExt(struct ScoreInfo *scores)
9324 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9326 strcpy(scores->entry[i].tape_basename, UNDEFINED_FILENAME);
9327 strcpy(scores->entry[i].name, EMPTY_PLAYER_NAME);
9328 scores->entry[i].score = 0;
9329 scores->entry[i].time = 0;
9331 scores->entry[i].id = -1;
9332 strcpy(scores->entry[i].tape_date, UNKNOWN_NAME);
9333 strcpy(scores->entry[i].platform, UNKNOWN_NAME);
9334 strcpy(scores->entry[i].version, UNKNOWN_NAME);
9335 strcpy(scores->entry[i].country_name, UNKNOWN_NAME);
9336 strcpy(scores->entry[i].country_code, "??");
9339 scores->num_entries = 0;
9340 scores->last_added = -1;
9341 scores->last_added_local = -1;
9343 scores->updated = FALSE;
9344 scores->uploaded = FALSE;
9345 scores->tape_downloaded = FALSE;
9346 scores->force_last_added = FALSE;
9348 // The following values are intentionally not reset here:
9352 // - continue_playing
9353 // - continue_on_return
9356 static void setScoreInfoToDefaults(void)
9358 setScoreInfoToDefaultsExt(&scores);
9361 static void setServerScoreInfoToDefaults(void)
9363 setScoreInfoToDefaultsExt(&server_scores);
9366 static void LoadScore_OLD(int nr)
9369 char *filename = getScoreFilename(nr);
9370 char cookie[MAX_LINE_LEN];
9371 char line[MAX_LINE_LEN];
9375 if (!(file = fopen(filename, MODE_READ)))
9378 // check file identifier
9379 if (fgets(cookie, MAX_LINE_LEN, file) == NULL)
9381 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
9382 cookie[strlen(cookie) - 1] = '\0';
9384 if (!checkCookieString(cookie, SCORE_COOKIE_TMPL))
9386 Warn("unknown format of score file '%s'", filename);
9393 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9395 if (fscanf(file, "%d", &scores.entry[i].score) == EOF)
9396 Warn("fscanf() failed; %s", strerror(errno));
9398 if (fgets(line, MAX_LINE_LEN, file) == NULL)
9401 if (strlen(line) > 0 && line[strlen(line) - 1] == '\n')
9402 line[strlen(line) - 1] = '\0';
9404 for (line_ptr = line; *line_ptr; line_ptr++)
9406 if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != '\0')
9408 strncpy(scores.entry[i].name, line_ptr, MAX_PLAYER_NAME_LEN);
9409 scores.entry[i].name[MAX_PLAYER_NAME_LEN] = '\0';
9418 static void ConvertScore_OLD(void)
9420 // only convert score to time for levels that rate playing time over score
9421 if (!level.rate_time_over_score)
9424 // convert old score to playing time for score-less levels (like Supaplex)
9425 int time_final_max = 999;
9428 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9430 int score = scores.entry[i].score;
9432 if (score > 0 && score < time_final_max)
9433 scores.entry[i].time = (time_final_max - score - 1) * FRAMES_PER_SECOND;
9437 static int LoadScore_VERS(File *file, int chunk_size, struct ScoreInfo *scores)
9439 scores->file_version = getFileVersion(file);
9440 scores->game_version = getFileVersion(file);
9445 static int LoadScore_INFO(File *file, int chunk_size, struct ScoreInfo *scores)
9447 char *level_identifier = NULL;
9448 int level_identifier_size;
9451 level_identifier_size = getFile16BitBE(file);
9453 level_identifier = checked_malloc(level_identifier_size);
9455 for (i = 0; i < level_identifier_size; i++)
9456 level_identifier[i] = getFile8Bit(file);
9458 strncpy(scores->level_identifier, level_identifier, MAX_FILENAME_LEN);
9459 scores->level_identifier[MAX_FILENAME_LEN] = '\0';
9461 checked_free(level_identifier);
9463 scores->level_nr = getFile16BitBE(file);
9464 scores->num_entries = getFile16BitBE(file);
9466 chunk_size = 2 + level_identifier_size + 2 + 2;
9471 static int LoadScore_NAME(File *file, int chunk_size, struct ScoreInfo *scores)
9475 for (i = 0; i < scores->num_entries; i++)
9477 for (j = 0; j < MAX_PLAYER_NAME_LEN; j++)
9478 scores->entry[i].name[j] = getFile8Bit(file);
9480 scores->entry[i].name[MAX_PLAYER_NAME_LEN] = '\0';
9483 chunk_size = scores->num_entries * MAX_PLAYER_NAME_LEN;
9488 static int LoadScore_SCOR(File *file, int chunk_size, struct ScoreInfo *scores)
9492 for (i = 0; i < scores->num_entries; i++)
9493 scores->entry[i].score = getFile16BitBE(file);
9495 chunk_size = scores->num_entries * 2;
9500 static int LoadScore_SC4R(File *file, int chunk_size, struct ScoreInfo *scores)
9504 for (i = 0; i < scores->num_entries; i++)
9505 scores->entry[i].score = getFile32BitBE(file);
9507 chunk_size = scores->num_entries * 4;
9512 static int LoadScore_TIME(File *file, int chunk_size, struct ScoreInfo *scores)
9516 for (i = 0; i < scores->num_entries; i++)
9517 scores->entry[i].time = getFile32BitBE(file);
9519 chunk_size = scores->num_entries * 4;
9524 static int LoadScore_TAPE(File *file, int chunk_size, struct ScoreInfo *scores)
9528 for (i = 0; i < scores->num_entries; i++)
9530 for (j = 0; j < MAX_SCORE_TAPE_BASENAME_LEN; j++)
9531 scores->entry[i].tape_basename[j] = getFile8Bit(file);
9533 scores->entry[i].tape_basename[MAX_SCORE_TAPE_BASENAME_LEN] = '\0';
9536 chunk_size = scores->num_entries * MAX_SCORE_TAPE_BASENAME_LEN;
9541 void LoadScore(int nr)
9543 char *filename = getScoreFilename(nr);
9544 char cookie[MAX_LINE_LEN];
9545 char chunk_name[CHUNK_ID_LEN + 1];
9547 boolean old_score_file_format = FALSE;
9550 // always start with reliable default values
9551 setScoreInfoToDefaults();
9553 if (!(file = openFile(filename, MODE_READ)))
9556 getFileChunkBE(file, chunk_name, NULL);
9557 if (strEqual(chunk_name, "RND1"))
9559 getFile32BitBE(file); // not used
9561 getFileChunkBE(file, chunk_name, NULL);
9562 if (!strEqual(chunk_name, "SCOR"))
9564 Warn("unknown format of score file '%s'", filename);
9571 else // check for old file format with cookie string
9573 strcpy(cookie, chunk_name);
9574 if (getStringFromFile(file, &cookie[4], MAX_LINE_LEN - 4) == NULL)
9576 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
9577 cookie[strlen(cookie) - 1] = '\0';
9579 if (!checkCookieString(cookie, SCORE_COOKIE_TMPL))
9581 Warn("unknown format of score file '%s'", filename);
9588 old_score_file_format = TRUE;
9591 if (old_score_file_format)
9593 // score files from versions before 4.2.4.0 without chunk structure
9596 // convert score to time, if possible (mainly for Supaplex levels)
9605 int (*loader)(File *, int, struct ScoreInfo *);
9609 { "VERS", SCORE_CHUNK_VERS_SIZE, LoadScore_VERS },
9610 { "INFO", -1, LoadScore_INFO },
9611 { "NAME", -1, LoadScore_NAME },
9612 { "SCOR", -1, LoadScore_SCOR },
9613 { "SC4R", -1, LoadScore_SC4R },
9614 { "TIME", -1, LoadScore_TIME },
9615 { "TAPE", -1, LoadScore_TAPE },
9620 while (getFileChunkBE(file, chunk_name, &chunk_size))
9624 while (chunk_info[i].name != NULL &&
9625 !strEqual(chunk_name, chunk_info[i].name))
9628 if (chunk_info[i].name == NULL)
9630 Warn("unknown chunk '%s' in score file '%s'",
9631 chunk_name, filename);
9633 ReadUnusedBytesFromFile(file, chunk_size);
9635 else if (chunk_info[i].size != -1 &&
9636 chunk_info[i].size != chunk_size)
9638 Warn("wrong size (%d) of chunk '%s' in score file '%s'",
9639 chunk_size, chunk_name, filename);
9641 ReadUnusedBytesFromFile(file, chunk_size);
9645 // call function to load this score chunk
9646 int chunk_size_expected =
9647 (chunk_info[i].loader)(file, chunk_size, &scores);
9649 // the size of some chunks cannot be checked before reading other
9650 // chunks first (like "HEAD" and "BODY") that contain some header
9651 // information, so check them here
9652 if (chunk_size_expected != chunk_size)
9654 Warn("wrong size (%d) of chunk '%s' in score file '%s'",
9655 chunk_size, chunk_name, filename);
9664 #if ENABLE_HISTORIC_CHUNKS
9665 void SaveScore_OLD(int nr)
9668 char *filename = getScoreFilename(nr);
9671 // used instead of "leveldir_current->subdir" (for network games)
9672 InitScoreDirectory(levelset.identifier);
9674 if (!(file = fopen(filename, MODE_WRITE)))
9676 Warn("cannot save score for level %d", nr);
9681 fprintf(file, "%s\n\n", SCORE_COOKIE);
9683 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9684 fprintf(file, "%d %s\n", scores.entry[i].score, scores.entry[i].name);
9688 SetFilePermissions(filename, PERMS_PRIVATE);
9692 static void SaveScore_VERS(FILE *file, struct ScoreInfo *scores)
9694 putFileVersion(file, scores->file_version);
9695 putFileVersion(file, scores->game_version);
9698 static void SaveScore_INFO(FILE *file, struct ScoreInfo *scores)
9700 int level_identifier_size = strlen(scores->level_identifier) + 1;
9703 putFile16BitBE(file, level_identifier_size);
9705 for (i = 0; i < level_identifier_size; i++)
9706 putFile8Bit(file, scores->level_identifier[i]);
9708 putFile16BitBE(file, scores->level_nr);
9709 putFile16BitBE(file, scores->num_entries);
9712 static void SaveScore_NAME(FILE *file, struct ScoreInfo *scores)
9716 for (i = 0; i < scores->num_entries; i++)
9718 int name_size = strlen(scores->entry[i].name);
9720 for (j = 0; j < MAX_PLAYER_NAME_LEN; j++)
9721 putFile8Bit(file, (j < name_size ? scores->entry[i].name[j] : 0));
9725 static void SaveScore_SCOR(FILE *file, struct ScoreInfo *scores)
9729 for (i = 0; i < scores->num_entries; i++)
9730 putFile16BitBE(file, scores->entry[i].score);
9733 static void SaveScore_SC4R(FILE *file, struct ScoreInfo *scores)
9737 for (i = 0; i < scores->num_entries; i++)
9738 putFile32BitBE(file, scores->entry[i].score);
9741 static void SaveScore_TIME(FILE *file, struct ScoreInfo *scores)
9745 for (i = 0; i < scores->num_entries; i++)
9746 putFile32BitBE(file, scores->entry[i].time);
9749 static void SaveScore_TAPE(FILE *file, struct ScoreInfo *scores)
9753 for (i = 0; i < scores->num_entries; i++)
9755 int size = strlen(scores->entry[i].tape_basename);
9757 for (j = 0; j < MAX_SCORE_TAPE_BASENAME_LEN; j++)
9758 putFile8Bit(file, (j < size ? scores->entry[i].tape_basename[j] : 0));
9762 static void SaveScoreToFilename(char *filename)
9765 int info_chunk_size;
9766 int name_chunk_size;
9767 int scor_chunk_size;
9768 int sc4r_chunk_size;
9769 int time_chunk_size;
9770 int tape_chunk_size;
9771 boolean has_large_score_values;
9774 if (!(file = fopen(filename, MODE_WRITE)))
9776 Warn("cannot save score file '%s'", filename);
9781 info_chunk_size = 2 + (strlen(scores.level_identifier) + 1) + 2 + 2;
9782 name_chunk_size = scores.num_entries * MAX_PLAYER_NAME_LEN;
9783 scor_chunk_size = scores.num_entries * 2;
9784 sc4r_chunk_size = scores.num_entries * 4;
9785 time_chunk_size = scores.num_entries * 4;
9786 tape_chunk_size = scores.num_entries * MAX_SCORE_TAPE_BASENAME_LEN;
9788 has_large_score_values = FALSE;
9789 for (i = 0; i < scores.num_entries; i++)
9790 if (scores.entry[i].score > 0xffff)
9791 has_large_score_values = TRUE;
9793 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
9794 putFileChunkBE(file, "SCOR", CHUNK_SIZE_NONE);
9796 putFileChunkBE(file, "VERS", SCORE_CHUNK_VERS_SIZE);
9797 SaveScore_VERS(file, &scores);
9799 putFileChunkBE(file, "INFO", info_chunk_size);
9800 SaveScore_INFO(file, &scores);
9802 putFileChunkBE(file, "NAME", name_chunk_size);
9803 SaveScore_NAME(file, &scores);
9805 if (has_large_score_values)
9807 putFileChunkBE(file, "SC4R", sc4r_chunk_size);
9808 SaveScore_SC4R(file, &scores);
9812 putFileChunkBE(file, "SCOR", scor_chunk_size);
9813 SaveScore_SCOR(file, &scores);
9816 putFileChunkBE(file, "TIME", time_chunk_size);
9817 SaveScore_TIME(file, &scores);
9819 putFileChunkBE(file, "TAPE", tape_chunk_size);
9820 SaveScore_TAPE(file, &scores);
9824 SetFilePermissions(filename, PERMS_PRIVATE);
9827 void SaveScore(int nr)
9829 char *filename = getScoreFilename(nr);
9832 // used instead of "leveldir_current->subdir" (for network games)
9833 InitScoreDirectory(levelset.identifier);
9835 scores.file_version = FILE_VERSION_ACTUAL;
9836 scores.game_version = GAME_VERSION_ACTUAL;
9838 strncpy(scores.level_identifier, levelset.identifier, MAX_FILENAME_LEN);
9839 scores.level_identifier[MAX_FILENAME_LEN] = '\0';
9840 scores.level_nr = level_nr;
9842 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9843 if (scores.entry[i].score == 0 &&
9844 scores.entry[i].time == 0 &&
9845 strEqual(scores.entry[i].name, EMPTY_PLAYER_NAME))
9848 scores.num_entries = i;
9850 if (scores.num_entries == 0)
9853 SaveScoreToFilename(filename);
9856 static void LoadServerScoreFromCache(int nr)
9858 struct ScoreEntry score_entry;
9867 { &score_entry.score, FALSE, 0 },
9868 { &score_entry.time, FALSE, 0 },
9869 { score_entry.name, TRUE, MAX_PLAYER_NAME_LEN },
9870 { score_entry.tape_basename, TRUE, MAX_FILENAME_LEN },
9871 { score_entry.tape_date, TRUE, MAX_ISO_DATE_LEN },
9872 { &score_entry.id, FALSE, 0 },
9873 { score_entry.platform, TRUE, MAX_PLATFORM_TEXT_LEN },
9874 { score_entry.version, TRUE, MAX_VERSION_TEXT_LEN },
9875 { score_entry.country_code, TRUE, MAX_COUNTRY_CODE_LEN },
9876 { score_entry.country_name, TRUE, MAX_COUNTRY_NAME_LEN },
9880 char *filename = getScoreCacheFilename(nr);
9881 SetupFileHash *score_hash = loadSetupFileHash(filename);
9884 server_scores.num_entries = 0;
9886 if (score_hash == NULL)
9889 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9891 score_entry = server_scores.entry[i];
9893 for (j = 0; score_mapping[j].value != NULL; j++)
9897 sprintf(token, "%02d.%d", i, j);
9899 char *value = getHashEntry(score_hash, token);
9904 if (score_mapping[j].is_string)
9906 char *score_value = (char *)score_mapping[j].value;
9907 int value_size = score_mapping[j].string_size;
9909 strncpy(score_value, value, value_size);
9910 score_value[value_size] = '\0';
9914 int *score_value = (int *)score_mapping[j].value;
9916 *score_value = atoi(value);
9919 server_scores.num_entries = i + 1;
9922 server_scores.entry[i] = score_entry;
9925 freeSetupFileHash(score_hash);
9928 void LoadServerScore(int nr, boolean download_score)
9930 if (!setup.use_api_server)
9933 // always start with reliable default values
9934 setServerScoreInfoToDefaults();
9936 // 1st step: load server scores from cache file (which may not exist)
9937 // (this should prevent reading it while the thread is writing to it)
9938 LoadServerScoreFromCache(nr);
9940 if (download_score && runtime.use_api_server)
9942 // 2nd step: download server scores from score server to cache file
9943 // (as thread, as it might time out if the server is not reachable)
9944 ApiGetScoreAsThread(nr);
9948 void PrepareScoreTapesForUpload(char *leveldir_subdir)
9950 MarkTapeDirectoryUploadsAsIncomplete(leveldir_subdir);
9952 // if score tape not uploaded, ask for uploading missing tapes later
9953 if (!setup.has_remaining_tapes)
9954 setup.ask_for_remaining_tapes = TRUE;
9956 setup.provide_uploading_tapes = TRUE;
9957 setup.has_remaining_tapes = TRUE;
9959 SaveSetup_ServerSetup();
9962 void SaveServerScore(int nr, boolean tape_saved)
9964 if (!runtime.use_api_server)
9966 PrepareScoreTapesForUpload(leveldir_current->subdir);
9971 ApiAddScoreAsThread(nr, tape_saved, NULL);
9974 void SaveServerScoreFromFile(int nr, boolean tape_saved,
9975 char *score_tape_filename)
9977 if (!runtime.use_api_server)
9980 ApiAddScoreAsThread(nr, tape_saved, score_tape_filename);
9983 void LoadLocalAndServerScore(int nr, boolean download_score)
9985 int last_added_local = scores.last_added_local;
9986 boolean force_last_added = scores.force_last_added;
9988 // needed if only showing server scores
9989 setScoreInfoToDefaults();
9991 if (!strEqual(setup.scores_in_highscore_list, STR_SCORES_TYPE_SERVER_ONLY))
9994 // restore last added local score entry (before merging server scores)
9995 scores.last_added = scores.last_added_local = last_added_local;
9997 if (setup.use_api_server &&
9998 !strEqual(setup.scores_in_highscore_list, STR_SCORES_TYPE_LOCAL_ONLY))
10000 // load server scores from cache file and trigger update from server
10001 LoadServerScore(nr, download_score);
10003 // merge local scores with scores from server
10004 MergeServerScore();
10007 if (force_last_added)
10008 scores.force_last_added = force_last_added;
10012 // ============================================================================
10013 // setup file functions
10014 // ============================================================================
10016 #define TOKEN_STR_PLAYER_PREFIX "player_"
10019 static struct TokenInfo global_setup_tokens[] =
10023 &setup.player_name, "player_name"
10027 &setup.multiple_users, "multiple_users"
10031 &setup.sound, "sound"
10035 &setup.sound_loops, "repeating_sound_loops"
10039 &setup.sound_music, "background_music"
10043 &setup.sound_simple, "simple_sound_effects"
10047 &setup.toons, "toons"
10051 &setup.global_animations, "global_animations"
10055 &setup.scroll_delay, "scroll_delay"
10059 &setup.forced_scroll_delay, "forced_scroll_delay"
10063 &setup.scroll_delay_value, "scroll_delay_value"
10067 &setup.engine_snapshot_mode, "engine_snapshot_mode"
10071 &setup.engine_snapshot_memory, "engine_snapshot_memory"
10075 &setup.fade_screens, "fade_screens"
10079 &setup.autorecord, "automatic_tape_recording"
10083 &setup.autorecord_after_replay, "autorecord_after_replay"
10087 &setup.auto_pause_on_start, "auto_pause_on_start"
10091 &setup.show_titlescreen, "show_titlescreen"
10095 &setup.quick_doors, "quick_doors"
10099 &setup.team_mode, "team_mode"
10103 &setup.handicap, "handicap"
10107 &setup.skip_levels, "skip_levels"
10111 &setup.increment_levels, "increment_levels"
10115 &setup.auto_play_next_level, "auto_play_next_level"
10119 &setup.count_score_after_game, "count_score_after_game"
10123 &setup.show_scores_after_game, "show_scores_after_game"
10127 &setup.time_limit, "time_limit"
10131 &setup.fullscreen, "fullscreen"
10135 &setup.window_scaling_percent, "window_scaling_percent"
10139 &setup.window_scaling_quality, "window_scaling_quality"
10143 &setup.screen_rendering_mode, "screen_rendering_mode"
10147 &setup.vsync_mode, "vsync_mode"
10151 &setup.ask_on_escape, "ask_on_escape"
10155 &setup.ask_on_escape_editor, "ask_on_escape_editor"
10159 &setup.ask_on_game_over, "ask_on_game_over"
10163 &setup.ask_on_quit_game, "ask_on_quit_game"
10167 &setup.ask_on_quit_program, "ask_on_quit_program"
10171 &setup.quick_switch, "quick_player_switch"
10175 &setup.input_on_focus, "input_on_focus"
10179 &setup.prefer_aga_graphics, "prefer_aga_graphics"
10183 &setup.prefer_lowpass_sounds, "prefer_lowpass_sounds"
10187 &setup.prefer_extra_panel_items, "prefer_extra_panel_items"
10191 &setup.game_speed_extended, "game_speed_extended"
10195 &setup.game_frame_delay, "game_frame_delay"
10199 &setup.bd_skip_uncovering, "bd_skip_uncovering"
10203 &setup.bd_skip_hatching, "bd_skip_hatching"
10207 &setup.bd_scroll_delay, "bd_scroll_delay"
10211 &setup.bd_smooth_movements, "bd_smooth_movements"
10215 &setup.sp_show_border_elements, "sp_show_border_elements"
10219 &setup.small_game_graphics, "small_game_graphics"
10223 &setup.show_load_save_buttons, "show_load_save_buttons"
10227 &setup.show_undo_redo_buttons, "show_undo_redo_buttons"
10231 &setup.scores_in_highscore_list, "scores_in_highscore_list"
10235 &setup.graphics_set, "graphics_set"
10239 &setup.sounds_set, "sounds_set"
10243 &setup.music_set, "music_set"
10247 &setup.override_level_graphics, "override_level_graphics"
10251 &setup.override_level_sounds, "override_level_sounds"
10255 &setup.override_level_music, "override_level_music"
10259 &setup.volume_simple, "volume_simple"
10263 &setup.volume_loops, "volume_loops"
10267 &setup.volume_music, "volume_music"
10271 &setup.network_mode, "network_mode"
10275 &setup.network_player_nr, "network_player"
10279 &setup.network_server_hostname, "network_server_hostname"
10283 &setup.touch.control_type, "touch.control_type"
10287 &setup.touch.move_distance, "touch.move_distance"
10291 &setup.touch.drop_distance, "touch.drop_distance"
10295 &setup.touch.transparency, "touch.transparency"
10299 &setup.touch.draw_outlined, "touch.draw_outlined"
10303 &setup.touch.draw_pressed, "touch.draw_pressed"
10307 &setup.touch.grid_xsize[0], "touch.virtual_buttons.0.xsize"
10311 &setup.touch.grid_ysize[0], "touch.virtual_buttons.0.ysize"
10315 &setup.touch.grid_xsize[1], "touch.virtual_buttons.1.xsize"
10319 &setup.touch.grid_ysize[1], "touch.virtual_buttons.1.ysize"
10323 &setup.touch.overlay_buttons, "touch.overlay_buttons"
10327 static struct TokenInfo auto_setup_tokens[] =
10331 &setup.auto_setup.editor_zoom_tilesize, "editor.zoom_tilesize"
10335 static struct TokenInfo server_setup_tokens[] =
10339 &setup.player_uuid, "player_uuid"
10343 &setup.player_version, "player_version"
10347 &setup.use_api_server, TEST_PREFIX "use_api_server"
10351 &setup.api_server_hostname, TEST_PREFIX "api_server_hostname"
10355 &setup.api_server_password, TEST_PREFIX "api_server_password"
10359 &setup.ask_for_uploading_tapes, TEST_PREFIX "ask_for_uploading_tapes"
10363 &setup.ask_for_remaining_tapes, TEST_PREFIX "ask_for_remaining_tapes"
10367 &setup.provide_uploading_tapes, TEST_PREFIX "provide_uploading_tapes"
10371 &setup.ask_for_using_api_server,TEST_PREFIX "ask_for_using_api_server"
10375 &setup.has_remaining_tapes, TEST_PREFIX "has_remaining_tapes"
10379 static struct TokenInfo editor_setup_tokens[] =
10383 &setup.editor.el_classic, "editor.el_classic"
10387 &setup.editor.el_custom, "editor.el_custom"
10391 &setup.editor.el_user_defined, "editor.el_user_defined"
10395 &setup.editor.el_dynamic, "editor.el_dynamic"
10399 &setup.editor.el_headlines, "editor.el_headlines"
10403 &setup.editor.show_element_token, "editor.show_element_token"
10407 &setup.editor.show_read_only_warning, "editor.show_read_only_warning"
10411 static struct TokenInfo editor_cascade_setup_tokens[] =
10415 &setup.editor_cascade.el_bd, "editor.cascade.el_bd"
10419 &setup.editor_cascade.el_bd_native, "editor.cascade.el_bd_native"
10423 &setup.editor_cascade.el_em, "editor.cascade.el_em"
10427 &setup.editor_cascade.el_emc, "editor.cascade.el_emc"
10431 &setup.editor_cascade.el_rnd, "editor.cascade.el_rnd"
10435 &setup.editor_cascade.el_sb, "editor.cascade.el_sb"
10439 &setup.editor_cascade.el_sp, "editor.cascade.el_sp"
10443 &setup.editor_cascade.el_dc, "editor.cascade.el_dc"
10447 &setup.editor_cascade.el_dx, "editor.cascade.el_dx"
10451 &setup.editor_cascade.el_mm, "editor.cascade.el_mm"
10455 &setup.editor_cascade.el_df, "editor.cascade.el_df"
10459 &setup.editor_cascade.el_chars, "editor.cascade.el_chars"
10463 &setup.editor_cascade.el_steel_chars, "editor.cascade.el_steel_chars"
10467 &setup.editor_cascade.el_ce, "editor.cascade.el_ce"
10471 &setup.editor_cascade.el_ge, "editor.cascade.el_ge"
10475 &setup.editor_cascade.el_es, "editor.cascade.el_es"
10479 &setup.editor_cascade.el_ref, "editor.cascade.el_ref"
10483 &setup.editor_cascade.el_user, "editor.cascade.el_user"
10487 &setup.editor_cascade.el_dynamic, "editor.cascade.el_dynamic"
10491 static struct TokenInfo shortcut_setup_tokens[] =
10495 &setup.shortcut.save_game, "shortcut.save_game"
10499 &setup.shortcut.load_game, "shortcut.load_game"
10503 &setup.shortcut.restart_game, "shortcut.restart_game"
10507 &setup.shortcut.pause_before_end, "shortcut.pause_before_end"
10511 &setup.shortcut.toggle_pause, "shortcut.toggle_pause"
10515 &setup.shortcut.focus_player[0], "shortcut.focus_player_1"
10519 &setup.shortcut.focus_player[1], "shortcut.focus_player_2"
10523 &setup.shortcut.focus_player[2], "shortcut.focus_player_3"
10527 &setup.shortcut.focus_player[3], "shortcut.focus_player_4"
10531 &setup.shortcut.focus_player_all, "shortcut.focus_player_all"
10535 &setup.shortcut.tape_eject, "shortcut.tape_eject"
10539 &setup.shortcut.tape_extra, "shortcut.tape_extra"
10543 &setup.shortcut.tape_stop, "shortcut.tape_stop"
10547 &setup.shortcut.tape_pause, "shortcut.tape_pause"
10551 &setup.shortcut.tape_record, "shortcut.tape_record"
10555 &setup.shortcut.tape_play, "shortcut.tape_play"
10559 &setup.shortcut.sound_simple, "shortcut.sound_simple"
10563 &setup.shortcut.sound_loops, "shortcut.sound_loops"
10567 &setup.shortcut.sound_music, "shortcut.sound_music"
10571 &setup.shortcut.snap_left, "shortcut.snap_left"
10575 &setup.shortcut.snap_right, "shortcut.snap_right"
10579 &setup.shortcut.snap_up, "shortcut.snap_up"
10583 &setup.shortcut.snap_down, "shortcut.snap_down"
10587 static struct SetupInputInfo setup_input;
10588 static struct TokenInfo player_setup_tokens[] =
10592 &setup_input.use_joystick, ".use_joystick"
10596 &setup_input.joy.device_name, ".joy.device_name"
10600 &setup_input.joy.xleft, ".joy.xleft"
10604 &setup_input.joy.xmiddle, ".joy.xmiddle"
10608 &setup_input.joy.xright, ".joy.xright"
10612 &setup_input.joy.yupper, ".joy.yupper"
10616 &setup_input.joy.ymiddle, ".joy.ymiddle"
10620 &setup_input.joy.ylower, ".joy.ylower"
10624 &setup_input.joy.snap, ".joy.snap_field"
10628 &setup_input.joy.drop, ".joy.place_bomb"
10632 &setup_input.key.left, ".key.move_left"
10636 &setup_input.key.right, ".key.move_right"
10640 &setup_input.key.up, ".key.move_up"
10644 &setup_input.key.down, ".key.move_down"
10648 &setup_input.key.snap, ".key.snap_field"
10652 &setup_input.key.drop, ".key.place_bomb"
10656 static struct TokenInfo system_setup_tokens[] =
10660 &setup.system.sdl_renderdriver, "system.sdl_renderdriver"
10664 &setup.system.sdl_videodriver, "system.sdl_videodriver"
10668 &setup.system.sdl_audiodriver, "system.sdl_audiodriver"
10672 &setup.system.audio_fragment_size, "system.audio_fragment_size"
10676 static struct TokenInfo internal_setup_tokens[] =
10680 &setup.internal.program_title, "program_title"
10684 &setup.internal.program_version, "program_version"
10688 &setup.internal.program_author, "program_author"
10692 &setup.internal.program_email, "program_email"
10696 &setup.internal.program_website, "program_website"
10700 &setup.internal.program_copyright, "program_copyright"
10704 &setup.internal.program_company, "program_company"
10708 &setup.internal.program_icon_file, "program_icon_file"
10712 &setup.internal.default_graphics_set, "default_graphics_set"
10716 &setup.internal.default_sounds_set, "default_sounds_set"
10720 &setup.internal.default_music_set, "default_music_set"
10724 &setup.internal.fallback_graphics_file, "fallback_graphics_file"
10728 &setup.internal.fallback_sounds_file, "fallback_sounds_file"
10732 &setup.internal.fallback_music_file, "fallback_music_file"
10736 &setup.internal.default_level_series, "default_level_series"
10740 &setup.internal.default_window_width, "default_window_width"
10744 &setup.internal.default_window_height, "default_window_height"
10748 &setup.internal.choose_from_top_leveldir, "choose_from_top_leveldir"
10752 &setup.internal.show_scaling_in_title, "show_scaling_in_title"
10756 &setup.internal.create_user_levelset, "create_user_levelset"
10760 &setup.internal.info_screens_from_main, "info_screens_from_main"
10764 &setup.internal.menu_game, "menu_game"
10768 &setup.internal.menu_engines, "menu_engines"
10772 &setup.internal.menu_editor, "menu_editor"
10776 &setup.internal.menu_graphics, "menu_graphics"
10780 &setup.internal.menu_sound, "menu_sound"
10784 &setup.internal.menu_artwork, "menu_artwork"
10788 &setup.internal.menu_input, "menu_input"
10792 &setup.internal.menu_touch, "menu_touch"
10796 &setup.internal.menu_shortcuts, "menu_shortcuts"
10800 &setup.internal.menu_exit, "menu_exit"
10804 &setup.internal.menu_save_and_exit, "menu_save_and_exit"
10808 &setup.internal.menu_shortcuts_various, "menu_shortcuts_various"
10812 &setup.internal.menu_shortcuts_focus, "menu_shortcuts_focus"
10816 &setup.internal.menu_shortcuts_tape, "menu_shortcuts_tape"
10820 &setup.internal.menu_shortcuts_sound, "menu_shortcuts_sound"
10824 &setup.internal.menu_shortcuts_snap, "menu_shortcuts_snap"
10828 &setup.internal.info_title, "info_title"
10832 &setup.internal.info_elements, "info_elements"
10836 &setup.internal.info_music, "info_music"
10840 &setup.internal.info_credits, "info_credits"
10844 &setup.internal.info_program, "info_program"
10848 &setup.internal.info_version, "info_version"
10852 &setup.internal.info_levelset, "info_levelset"
10856 &setup.internal.info_exit, "info_exit"
10860 static struct TokenInfo debug_setup_tokens[] =
10864 &setup.debug.frame_delay[0], "debug.frame_delay_0"
10868 &setup.debug.frame_delay[1], "debug.frame_delay_1"
10872 &setup.debug.frame_delay[2], "debug.frame_delay_2"
10876 &setup.debug.frame_delay[3], "debug.frame_delay_3"
10880 &setup.debug.frame_delay[4], "debug.frame_delay_4"
10884 &setup.debug.frame_delay[5], "debug.frame_delay_5"
10888 &setup.debug.frame_delay[6], "debug.frame_delay_6"
10892 &setup.debug.frame_delay[7], "debug.frame_delay_7"
10896 &setup.debug.frame_delay[8], "debug.frame_delay_8"
10900 &setup.debug.frame_delay[9], "debug.frame_delay_9"
10904 &setup.debug.frame_delay_key[0], "debug.key.frame_delay_0"
10908 &setup.debug.frame_delay_key[1], "debug.key.frame_delay_1"
10912 &setup.debug.frame_delay_key[2], "debug.key.frame_delay_2"
10916 &setup.debug.frame_delay_key[3], "debug.key.frame_delay_3"
10920 &setup.debug.frame_delay_key[4], "debug.key.frame_delay_4"
10924 &setup.debug.frame_delay_key[5], "debug.key.frame_delay_5"
10928 &setup.debug.frame_delay_key[6], "debug.key.frame_delay_6"
10932 &setup.debug.frame_delay_key[7], "debug.key.frame_delay_7"
10936 &setup.debug.frame_delay_key[8], "debug.key.frame_delay_8"
10940 &setup.debug.frame_delay_key[9], "debug.key.frame_delay_9"
10944 &setup.debug.frame_delay_use_mod_key, "debug.frame_delay.use_mod_key"},
10947 &setup.debug.frame_delay_game_only, "debug.frame_delay.game_only"
10951 &setup.debug.show_frames_per_second, "debug.show_frames_per_second"
10955 &setup.debug.xsn_mode, "debug.xsn_mode"
10959 &setup.debug.xsn_percent, "debug.xsn_percent"
10963 static struct TokenInfo options_setup_tokens[] =
10967 &setup.options.verbose, "options.verbose"
10971 &setup.options.debug, "options.debug"
10975 &setup.options.debug_mode, "options.debug_mode"
10979 static void setSetupInfoToDefaults(struct SetupInfo *si)
10983 si->player_name = getStringCopy(getDefaultUserName(user.nr));
10985 si->multiple_users = TRUE;
10988 si->sound_loops = TRUE;
10989 si->sound_music = TRUE;
10990 si->sound_simple = TRUE;
10992 si->global_animations = TRUE;
10993 si->scroll_delay = TRUE;
10994 si->forced_scroll_delay = FALSE;
10995 si->scroll_delay_value = STD_SCROLL_DELAY;
10996 si->engine_snapshot_mode = getStringCopy(STR_SNAPSHOT_MODE_DEFAULT);
10997 si->engine_snapshot_memory = SNAPSHOT_MEMORY_DEFAULT;
10998 si->fade_screens = TRUE;
10999 si->autorecord = TRUE;
11000 si->autorecord_after_replay = TRUE;
11001 si->auto_pause_on_start = FALSE;
11002 si->show_titlescreen = TRUE;
11003 si->quick_doors = FALSE;
11004 si->team_mode = FALSE;
11005 si->handicap = TRUE;
11006 si->skip_levels = TRUE;
11007 si->increment_levels = TRUE;
11008 si->auto_play_next_level = TRUE;
11009 si->count_score_after_game = TRUE;
11010 si->show_scores_after_game = TRUE;
11011 si->time_limit = TRUE;
11012 si->fullscreen = FALSE;
11013 si->window_scaling_percent = STD_WINDOW_SCALING_PERCENT;
11014 si->window_scaling_quality = getStringCopy(SCALING_QUALITY_DEFAULT);
11015 si->screen_rendering_mode = getStringCopy(STR_SPECIAL_RENDERING_DEFAULT);
11016 si->vsync_mode = getStringCopy(STR_VSYNC_MODE_DEFAULT);
11017 si->ask_on_escape = TRUE;
11018 si->ask_on_escape_editor = TRUE;
11019 si->ask_on_game_over = TRUE;
11020 si->ask_on_quit_game = TRUE;
11021 si->ask_on_quit_program = TRUE;
11022 si->quick_switch = FALSE;
11023 si->input_on_focus = FALSE;
11024 si->prefer_aga_graphics = TRUE;
11025 si->prefer_lowpass_sounds = FALSE;
11026 si->prefer_extra_panel_items = TRUE;
11027 si->game_speed_extended = FALSE;
11028 si->game_frame_delay = GAME_FRAME_DELAY;
11029 si->bd_skip_uncovering = FALSE;
11030 si->bd_skip_hatching = FALSE;
11031 si->bd_scroll_delay = TRUE;
11032 si->bd_smooth_movements = AUTO;
11033 si->sp_show_border_elements = FALSE;
11034 si->small_game_graphics = FALSE;
11035 si->show_load_save_buttons = FALSE;
11036 si->show_undo_redo_buttons = FALSE;
11037 si->scores_in_highscore_list = getStringCopy(STR_SCORES_TYPE_DEFAULT);
11039 si->graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
11040 si->sounds_set = getStringCopy(SND_CLASSIC_SUBDIR);
11041 si->music_set = getStringCopy(MUS_CLASSIC_SUBDIR);
11043 si->override_level_graphics = FALSE;
11044 si->override_level_sounds = FALSE;
11045 si->override_level_music = FALSE;
11047 si->volume_simple = 100; // percent
11048 si->volume_loops = 100; // percent
11049 si->volume_music = 100; // percent
11051 si->network_mode = FALSE;
11052 si->network_player_nr = 0; // first player
11053 si->network_server_hostname = getStringCopy(STR_NETWORK_AUTO_DETECT);
11055 si->touch.control_type = getStringCopy(TOUCH_CONTROL_DEFAULT);
11056 si->touch.move_distance = TOUCH_MOVE_DISTANCE_DEFAULT; // percent
11057 si->touch.drop_distance = TOUCH_DROP_DISTANCE_DEFAULT; // percent
11058 si->touch.transparency = TOUCH_TRANSPARENCY_DEFAULT; // percent
11059 si->touch.draw_outlined = TRUE;
11060 si->touch.draw_pressed = TRUE;
11062 for (i = 0; i < 2; i++)
11064 char *default_grid_button[6][2] =
11070 { "111222", " vv " },
11071 { "111222", " vv " }
11073 int grid_xsize = DEFAULT_GRID_XSIZE(i);
11074 int grid_ysize = DEFAULT_GRID_YSIZE(i);
11075 int min_xsize = MIN(6, grid_xsize);
11076 int min_ysize = MIN(6, grid_ysize);
11077 int startx = grid_xsize - min_xsize;
11078 int starty = grid_ysize - min_ysize;
11081 // virtual buttons grid can only be set to defaults if video is initialized
11082 // (this will be repeated if virtual buttons are not loaded from setup file)
11083 if (video.initialized)
11085 si->touch.grid_xsize[i] = grid_xsize;
11086 si->touch.grid_ysize[i] = grid_ysize;
11090 si->touch.grid_xsize[i] = -1;
11091 si->touch.grid_ysize[i] = -1;
11094 for (x = 0; x < MAX_GRID_XSIZE; x++)
11095 for (y = 0; y < MAX_GRID_YSIZE; y++)
11096 si->touch.grid_button[i][x][y] = CHAR_GRID_BUTTON_NONE;
11098 for (x = 0; x < min_xsize; x++)
11099 for (y = 0; y < min_ysize; y++)
11100 si->touch.grid_button[i][x][starty + y] =
11101 default_grid_button[y][0][x];
11103 for (x = 0; x < min_xsize; x++)
11104 for (y = 0; y < min_ysize; y++)
11105 si->touch.grid_button[i][startx + x][starty + y] =
11106 default_grid_button[y][1][x];
11109 si->touch.grid_initialized = video.initialized;
11111 si->touch.overlay_buttons = FALSE;
11113 si->editor.el_boulderdash = TRUE;
11114 si->editor.el_boulderdash_native = TRUE;
11115 si->editor.el_emerald_mine = TRUE;
11116 si->editor.el_emerald_mine_club = TRUE;
11117 si->editor.el_more = TRUE;
11118 si->editor.el_sokoban = TRUE;
11119 si->editor.el_supaplex = TRUE;
11120 si->editor.el_diamond_caves = TRUE;
11121 si->editor.el_dx_boulderdash = TRUE;
11123 si->editor.el_mirror_magic = TRUE;
11124 si->editor.el_deflektor = TRUE;
11126 si->editor.el_chars = TRUE;
11127 si->editor.el_steel_chars = TRUE;
11129 si->editor.el_classic = TRUE;
11130 si->editor.el_custom = TRUE;
11132 si->editor.el_user_defined = FALSE;
11133 si->editor.el_dynamic = TRUE;
11135 si->editor.el_headlines = TRUE;
11137 si->editor.show_element_token = FALSE;
11139 si->editor.show_read_only_warning = TRUE;
11141 si->editor.use_template_for_new_levels = TRUE;
11143 si->shortcut.save_game = DEFAULT_KEY_SAVE_GAME;
11144 si->shortcut.load_game = DEFAULT_KEY_LOAD_GAME;
11145 si->shortcut.restart_game = DEFAULT_KEY_RESTART_GAME;
11146 si->shortcut.pause_before_end = DEFAULT_KEY_PAUSE_BEFORE_END;
11147 si->shortcut.toggle_pause = DEFAULT_KEY_TOGGLE_PAUSE;
11149 si->shortcut.focus_player[0] = DEFAULT_KEY_FOCUS_PLAYER_1;
11150 si->shortcut.focus_player[1] = DEFAULT_KEY_FOCUS_PLAYER_2;
11151 si->shortcut.focus_player[2] = DEFAULT_KEY_FOCUS_PLAYER_3;
11152 si->shortcut.focus_player[3] = DEFAULT_KEY_FOCUS_PLAYER_4;
11153 si->shortcut.focus_player_all = DEFAULT_KEY_FOCUS_PLAYER_ALL;
11155 si->shortcut.tape_eject = DEFAULT_KEY_TAPE_EJECT;
11156 si->shortcut.tape_extra = DEFAULT_KEY_TAPE_EXTRA;
11157 si->shortcut.tape_stop = DEFAULT_KEY_TAPE_STOP;
11158 si->shortcut.tape_pause = DEFAULT_KEY_TAPE_PAUSE;
11159 si->shortcut.tape_record = DEFAULT_KEY_TAPE_RECORD;
11160 si->shortcut.tape_play = DEFAULT_KEY_TAPE_PLAY;
11162 si->shortcut.sound_simple = DEFAULT_KEY_SOUND_SIMPLE;
11163 si->shortcut.sound_loops = DEFAULT_KEY_SOUND_LOOPS;
11164 si->shortcut.sound_music = DEFAULT_KEY_SOUND_MUSIC;
11166 si->shortcut.snap_left = DEFAULT_KEY_SNAP_LEFT;
11167 si->shortcut.snap_right = DEFAULT_KEY_SNAP_RIGHT;
11168 si->shortcut.snap_up = DEFAULT_KEY_SNAP_UP;
11169 si->shortcut.snap_down = DEFAULT_KEY_SNAP_DOWN;
11171 for (i = 0; i < MAX_PLAYERS; i++)
11173 si->input[i].use_joystick = FALSE;
11174 si->input[i].joy.device_name = getStringCopy(getDeviceNameFromJoystickNr(i));
11175 si->input[i].joy.xleft = JOYSTICK_XLEFT;
11176 si->input[i].joy.xmiddle = JOYSTICK_XMIDDLE;
11177 si->input[i].joy.xright = JOYSTICK_XRIGHT;
11178 si->input[i].joy.yupper = JOYSTICK_YUPPER;
11179 si->input[i].joy.ymiddle = JOYSTICK_YMIDDLE;
11180 si->input[i].joy.ylower = JOYSTICK_YLOWER;
11181 si->input[i].joy.snap = (i == 0 ? JOY_BUTTON_1 : 0);
11182 si->input[i].joy.drop = (i == 0 ? JOY_BUTTON_2 : 0);
11183 si->input[i].key.left = (i == 0 ? DEFAULT_KEY_LEFT : KSYM_UNDEFINED);
11184 si->input[i].key.right = (i == 0 ? DEFAULT_KEY_RIGHT : KSYM_UNDEFINED);
11185 si->input[i].key.up = (i == 0 ? DEFAULT_KEY_UP : KSYM_UNDEFINED);
11186 si->input[i].key.down = (i == 0 ? DEFAULT_KEY_DOWN : KSYM_UNDEFINED);
11187 si->input[i].key.snap = (i == 0 ? DEFAULT_KEY_SNAP : KSYM_UNDEFINED);
11188 si->input[i].key.drop = (i == 0 ? DEFAULT_KEY_DROP : KSYM_UNDEFINED);
11191 si->system.sdl_renderdriver = getStringCopy(ARG_DEFAULT);
11192 si->system.sdl_videodriver = getStringCopy(ARG_DEFAULT);
11193 si->system.sdl_audiodriver = getStringCopy(ARG_DEFAULT);
11194 si->system.audio_fragment_size = DEFAULT_AUDIO_FRAGMENT_SIZE;
11196 si->internal.program_title = getStringCopy(PROGRAM_TITLE_STRING);
11197 si->internal.program_version = getStringCopy(getProgramRealVersionString());
11198 si->internal.program_author = getStringCopy(PROGRAM_AUTHOR_STRING);
11199 si->internal.program_email = getStringCopy(PROGRAM_EMAIL_STRING);
11200 si->internal.program_website = getStringCopy(PROGRAM_WEBSITE_STRING);
11201 si->internal.program_copyright = getStringCopy(PROGRAM_COPYRIGHT_STRING);
11202 si->internal.program_company = getStringCopy(PROGRAM_COMPANY_STRING);
11204 si->internal.program_icon_file = getStringCopy(PROGRAM_ICON_FILENAME);
11206 si->internal.default_graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
11207 si->internal.default_sounds_set = getStringCopy(SND_CLASSIC_SUBDIR);
11208 si->internal.default_music_set = getStringCopy(MUS_CLASSIC_SUBDIR);
11210 si->internal.fallback_graphics_file = getStringCopy(UNDEFINED_FILENAME);
11211 si->internal.fallback_sounds_file = getStringCopy(UNDEFINED_FILENAME);
11212 si->internal.fallback_music_file = getStringCopy(UNDEFINED_FILENAME);
11214 si->internal.default_level_series = getStringCopy(UNDEFINED_LEVELSET);
11215 si->internal.choose_from_top_leveldir = FALSE;
11216 si->internal.show_scaling_in_title = TRUE;
11217 si->internal.create_user_levelset = TRUE;
11218 si->internal.info_screens_from_main = FALSE;
11220 si->internal.default_window_width = WIN_XSIZE_DEFAULT;
11221 si->internal.default_window_height = WIN_YSIZE_DEFAULT;
11223 si->debug.frame_delay[0] = DEFAULT_FRAME_DELAY_0;
11224 si->debug.frame_delay[1] = DEFAULT_FRAME_DELAY_1;
11225 si->debug.frame_delay[2] = DEFAULT_FRAME_DELAY_2;
11226 si->debug.frame_delay[3] = DEFAULT_FRAME_DELAY_3;
11227 si->debug.frame_delay[4] = DEFAULT_FRAME_DELAY_4;
11228 si->debug.frame_delay[5] = DEFAULT_FRAME_DELAY_5;
11229 si->debug.frame_delay[6] = DEFAULT_FRAME_DELAY_6;
11230 si->debug.frame_delay[7] = DEFAULT_FRAME_DELAY_7;
11231 si->debug.frame_delay[8] = DEFAULT_FRAME_DELAY_8;
11232 si->debug.frame_delay[9] = DEFAULT_FRAME_DELAY_9;
11234 si->debug.frame_delay_key[0] = DEFAULT_KEY_FRAME_DELAY_0;
11235 si->debug.frame_delay_key[1] = DEFAULT_KEY_FRAME_DELAY_1;
11236 si->debug.frame_delay_key[2] = DEFAULT_KEY_FRAME_DELAY_2;
11237 si->debug.frame_delay_key[3] = DEFAULT_KEY_FRAME_DELAY_3;
11238 si->debug.frame_delay_key[4] = DEFAULT_KEY_FRAME_DELAY_4;
11239 si->debug.frame_delay_key[5] = DEFAULT_KEY_FRAME_DELAY_5;
11240 si->debug.frame_delay_key[6] = DEFAULT_KEY_FRAME_DELAY_6;
11241 si->debug.frame_delay_key[7] = DEFAULT_KEY_FRAME_DELAY_7;
11242 si->debug.frame_delay_key[8] = DEFAULT_KEY_FRAME_DELAY_8;
11243 si->debug.frame_delay_key[9] = DEFAULT_KEY_FRAME_DELAY_9;
11245 si->debug.frame_delay_use_mod_key = DEFAULT_FRAME_DELAY_USE_MOD_KEY;
11246 si->debug.frame_delay_game_only = DEFAULT_FRAME_DELAY_GAME_ONLY;
11248 si->debug.show_frames_per_second = FALSE;
11250 si->debug.xsn_mode = AUTO;
11251 si->debug.xsn_percent = 0;
11253 si->options.verbose = FALSE;
11254 si->options.debug = FALSE;
11255 si->options.debug_mode = getStringCopy(ARG_UNDEFINED_STRING);
11257 #if defined(PLATFORM_ANDROID)
11258 si->fullscreen = TRUE;
11259 si->touch.overlay_buttons = TRUE;
11262 setHideSetupEntry(&setup.debug.xsn_mode);
11265 static void setSetupInfoToDefaults_AutoSetup(struct SetupInfo *si)
11267 si->auto_setup.editor_zoom_tilesize = MINI_TILESIZE;
11270 static void setSetupInfoToDefaults_ServerSetup(struct SetupInfo *si)
11272 si->player_uuid = NULL; // (will be set later)
11273 si->player_version = 1; // (will be set later)
11275 si->use_api_server = TRUE;
11276 si->api_server_hostname = getStringCopy(API_SERVER_HOSTNAME);
11277 si->api_server_password = getStringCopy(UNDEFINED_PASSWORD);
11278 si->ask_for_uploading_tapes = TRUE;
11279 si->ask_for_remaining_tapes = FALSE;
11280 si->provide_uploading_tapes = TRUE;
11281 si->ask_for_using_api_server = TRUE;
11282 si->has_remaining_tapes = FALSE;
11285 static void setSetupInfoToDefaults_EditorCascade(struct SetupInfo *si)
11287 si->editor_cascade.el_bd = TRUE;
11288 si->editor_cascade.el_bd_native = TRUE;
11289 si->editor_cascade.el_em = TRUE;
11290 si->editor_cascade.el_emc = TRUE;
11291 si->editor_cascade.el_rnd = TRUE;
11292 si->editor_cascade.el_sb = TRUE;
11293 si->editor_cascade.el_sp = TRUE;
11294 si->editor_cascade.el_dc = TRUE;
11295 si->editor_cascade.el_dx = TRUE;
11297 si->editor_cascade.el_mm = TRUE;
11298 si->editor_cascade.el_df = TRUE;
11300 si->editor_cascade.el_chars = FALSE;
11301 si->editor_cascade.el_steel_chars = FALSE;
11302 si->editor_cascade.el_ce = FALSE;
11303 si->editor_cascade.el_ge = FALSE;
11304 si->editor_cascade.el_es = FALSE;
11305 si->editor_cascade.el_ref = FALSE;
11306 si->editor_cascade.el_user = FALSE;
11307 si->editor_cascade.el_dynamic = FALSE;
11310 #define MAX_HIDE_SETUP_TOKEN_SIZE 20
11312 static char *getHideSetupToken(void *setup_value)
11314 static char hide_setup_token[MAX_HIDE_SETUP_TOKEN_SIZE];
11316 if (setup_value != NULL)
11317 snprintf(hide_setup_token, MAX_HIDE_SETUP_TOKEN_SIZE, "%p", setup_value);
11319 return hide_setup_token;
11322 void setHideSetupEntry(void *setup_value)
11324 char *hide_setup_token = getHideSetupToken(setup_value);
11326 if (hide_setup_hash == NULL)
11327 hide_setup_hash = newSetupFileHash();
11329 if (setup_value != NULL)
11330 setHashEntry(hide_setup_hash, hide_setup_token, "");
11333 void removeHideSetupEntry(void *setup_value)
11335 char *hide_setup_token = getHideSetupToken(setup_value);
11337 if (setup_value != NULL)
11338 removeHashEntry(hide_setup_hash, hide_setup_token);
11341 boolean hideSetupEntry(void *setup_value)
11343 char *hide_setup_token = getHideSetupToken(setup_value);
11345 return (setup_value != NULL &&
11346 getHashEntry(hide_setup_hash, hide_setup_token) != NULL);
11349 static void setSetupInfoFromTokenText(SetupFileHash *setup_file_hash,
11350 struct TokenInfo *token_info,
11351 int token_nr, char *token_text)
11353 char *token_hide_text = getStringCat2(token_text, ".hide");
11354 char *token_hide_value = getHashEntry(setup_file_hash, token_hide_text);
11356 // set the value of this setup option in the setup option structure
11357 setSetupInfo(token_info, token_nr, getHashEntry(setup_file_hash, token_text));
11359 // check if this setup option should be hidden in the setup menu
11360 if (token_hide_value != NULL && get_boolean_from_string(token_hide_value))
11361 setHideSetupEntry(token_info[token_nr].value);
11363 free(token_hide_text);
11366 static void setSetupInfoFromTokenInfo(SetupFileHash *setup_file_hash,
11367 struct TokenInfo *token_info,
11370 setSetupInfoFromTokenText(setup_file_hash, token_info, token_nr,
11371 token_info[token_nr].text);
11374 static void decodeSetupFileHash_Default(SetupFileHash *setup_file_hash)
11378 if (!setup_file_hash)
11381 for (i = 0; i < ARRAY_SIZE(global_setup_tokens); i++)
11382 setSetupInfoFromTokenInfo(setup_file_hash, global_setup_tokens, i);
11384 setup.touch.grid_initialized = TRUE;
11385 for (i = 0; i < 2; i++)
11387 int grid_xsize = setup.touch.grid_xsize[i];
11388 int grid_ysize = setup.touch.grid_ysize[i];
11391 // if virtual buttons are not loaded from setup file, repeat initializing
11392 // virtual buttons grid with default values later when video is initialized
11393 if (grid_xsize == -1 ||
11396 setup.touch.grid_initialized = FALSE;
11401 for (y = 0; y < grid_ysize; y++)
11403 char token_string[MAX_LINE_LEN];
11405 sprintf(token_string, "touch.virtual_buttons.%d.%02d", i, y);
11407 char *value_string = getHashEntry(setup_file_hash, token_string);
11409 if (value_string == NULL)
11412 for (x = 0; x < grid_xsize; x++)
11414 char c = value_string[x];
11416 setup.touch.grid_button[i][x][y] =
11417 (c == '.' ? CHAR_GRID_BUTTON_NONE : c);
11422 for (i = 0; i < ARRAY_SIZE(editor_setup_tokens); i++)
11423 setSetupInfoFromTokenInfo(setup_file_hash, editor_setup_tokens, i);
11425 for (i = 0; i < ARRAY_SIZE(shortcut_setup_tokens); i++)
11426 setSetupInfoFromTokenInfo(setup_file_hash, shortcut_setup_tokens, i);
11428 for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
11432 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
11434 setup_input = setup.input[pnr];
11435 for (i = 0; i < ARRAY_SIZE(player_setup_tokens); i++)
11437 char full_token[100];
11439 sprintf(full_token, "%s%s", prefix, player_setup_tokens[i].text);
11440 setSetupInfoFromTokenText(setup_file_hash, player_setup_tokens, i,
11443 setup.input[pnr] = setup_input;
11446 for (i = 0; i < ARRAY_SIZE(system_setup_tokens); i++)
11447 setSetupInfoFromTokenInfo(setup_file_hash, system_setup_tokens, i);
11449 for (i = 0; i < ARRAY_SIZE(internal_setup_tokens); i++)
11450 setSetupInfoFromTokenInfo(setup_file_hash, internal_setup_tokens, i);
11452 for (i = 0; i < ARRAY_SIZE(debug_setup_tokens); i++)
11453 setSetupInfoFromTokenInfo(setup_file_hash, debug_setup_tokens, i);
11455 for (i = 0; i < ARRAY_SIZE(options_setup_tokens); i++)
11456 setSetupInfoFromTokenInfo(setup_file_hash, options_setup_tokens, i);
11458 setHideRelatedSetupEntries();
11461 static void decodeSetupFileHash_AutoSetup(SetupFileHash *setup_file_hash)
11465 if (!setup_file_hash)
11468 for (i = 0; i < ARRAY_SIZE(auto_setup_tokens); i++)
11469 setSetupInfo(auto_setup_tokens, i,
11470 getHashEntry(setup_file_hash,
11471 auto_setup_tokens[i].text));
11474 static void decodeSetupFileHash_ServerSetup(SetupFileHash *setup_file_hash)
11478 if (!setup_file_hash)
11481 for (i = 0; i < ARRAY_SIZE(server_setup_tokens); i++)
11482 setSetupInfo(server_setup_tokens, i,
11483 getHashEntry(setup_file_hash,
11484 server_setup_tokens[i].text));
11487 static void decodeSetupFileHash_EditorCascade(SetupFileHash *setup_file_hash)
11491 if (!setup_file_hash)
11494 for (i = 0; i < ARRAY_SIZE(editor_cascade_setup_tokens); i++)
11495 setSetupInfo(editor_cascade_setup_tokens, i,
11496 getHashEntry(setup_file_hash,
11497 editor_cascade_setup_tokens[i].text));
11500 void LoadUserNames(void)
11502 int last_user_nr = user.nr;
11505 if (global.user_names != NULL)
11507 for (i = 0; i < MAX_PLAYER_NAMES; i++)
11508 checked_free(global.user_names[i]);
11510 checked_free(global.user_names);
11513 global.user_names = checked_calloc(MAX_PLAYER_NAMES * sizeof(char *));
11515 for (i = 0; i < MAX_PLAYER_NAMES; i++)
11519 SetupFileHash *setup_file_hash = loadSetupFileHash(getSetupFilename());
11521 if (setup_file_hash)
11523 char *player_name = getHashEntry(setup_file_hash, "player_name");
11525 global.user_names[i] = getFixedUserName(player_name);
11527 freeSetupFileHash(setup_file_hash);
11530 if (global.user_names[i] == NULL)
11531 global.user_names[i] = getStringCopy(getDefaultUserName(i));
11534 user.nr = last_user_nr;
11537 void LoadSetupFromFilename(char *filename)
11539 SetupFileHash *setup_file_hash = loadSetupFileHash(filename);
11541 if (setup_file_hash)
11543 decodeSetupFileHash_Default(setup_file_hash);
11545 freeSetupFileHash(setup_file_hash);
11549 Debug("setup", "using default setup values");
11553 static void LoadSetup_SpecialPostProcessing(void)
11555 char *player_name_new;
11557 // needed to work around problems with fixed length strings
11558 player_name_new = getFixedUserName(setup.player_name);
11559 free(setup.player_name);
11560 setup.player_name = player_name_new;
11562 // "scroll_delay: on(3) / off(0)" was replaced by scroll delay value
11563 if (setup.scroll_delay == FALSE)
11565 setup.scroll_delay_value = MIN_SCROLL_DELAY;
11566 setup.scroll_delay = TRUE; // now always "on"
11569 // make sure that scroll delay value stays inside valid range
11570 setup.scroll_delay_value =
11571 MIN(MAX(MIN_SCROLL_DELAY, setup.scroll_delay_value), MAX_SCROLL_DELAY);
11574 void LoadSetup_Default(void)
11578 // always start with reliable default values
11579 setSetupInfoToDefaults(&setup);
11581 // try to load setup values from default setup file
11582 filename = getDefaultSetupFilename();
11584 if (fileExists(filename))
11585 LoadSetupFromFilename(filename);
11587 // try to load setup values from platform setup file
11588 filename = getPlatformSetupFilename();
11590 if (fileExists(filename))
11591 LoadSetupFromFilename(filename);
11593 // try to load setup values from user setup file
11594 filename = getSetupFilename();
11596 LoadSetupFromFilename(filename);
11598 LoadSetup_SpecialPostProcessing();
11601 void LoadSetup_AutoSetup(void)
11603 char *filename = getPath2(getSetupDir(), AUTOSETUP_FILENAME);
11604 SetupFileHash *setup_file_hash = NULL;
11606 // always start with reliable default values
11607 setSetupInfoToDefaults_AutoSetup(&setup);
11609 setup_file_hash = loadSetupFileHash(filename);
11611 if (setup_file_hash)
11613 decodeSetupFileHash_AutoSetup(setup_file_hash);
11615 freeSetupFileHash(setup_file_hash);
11621 void LoadSetup_ServerSetup(void)
11623 char *filename = getPath2(getSetupDir(), SERVERSETUP_FILENAME);
11624 SetupFileHash *setup_file_hash = NULL;
11626 // always start with reliable default values
11627 setSetupInfoToDefaults_ServerSetup(&setup);
11629 setup_file_hash = loadSetupFileHash(filename);
11631 if (setup_file_hash)
11633 decodeSetupFileHash_ServerSetup(setup_file_hash);
11635 freeSetupFileHash(setup_file_hash);
11640 if (setup.player_uuid == NULL)
11642 // player UUID does not yet exist in setup file
11643 setup.player_uuid = getStringCopy(getUUID());
11644 setup.player_version = 2;
11646 SaveSetup_ServerSetup();
11650 void LoadSetup_EditorCascade(void)
11652 char *filename = getPath2(getSetupDir(), EDITORCASCADE_FILENAME);
11653 SetupFileHash *setup_file_hash = NULL;
11655 // always start with reliable default values
11656 setSetupInfoToDefaults_EditorCascade(&setup);
11658 setup_file_hash = loadSetupFileHash(filename);
11660 if (setup_file_hash)
11662 decodeSetupFileHash_EditorCascade(setup_file_hash);
11664 freeSetupFileHash(setup_file_hash);
11670 void LoadSetup(void)
11672 LoadSetup_Default();
11673 LoadSetup_AutoSetup();
11674 LoadSetup_ServerSetup();
11675 LoadSetup_EditorCascade();
11678 static void addGameControllerMappingToHash(SetupFileHash *mappings_hash,
11679 char *mapping_line)
11681 char mapping_guid[MAX_LINE_LEN];
11682 char *mapping_start, *mapping_end;
11684 // get GUID from game controller mapping line: copy complete line
11685 strncpy(mapping_guid, mapping_line, MAX_LINE_LEN - 1);
11686 mapping_guid[MAX_LINE_LEN - 1] = '\0';
11688 // get GUID from game controller mapping line: cut after GUID part
11689 mapping_start = strchr(mapping_guid, ',');
11690 if (mapping_start != NULL)
11691 *mapping_start = '\0';
11693 // cut newline from game controller mapping line
11694 mapping_end = strchr(mapping_line, '\n');
11695 if (mapping_end != NULL)
11696 *mapping_end = '\0';
11698 // add mapping entry to game controller mappings hash
11699 setHashEntry(mappings_hash, mapping_guid, mapping_line);
11702 static void LoadSetup_ReadGameControllerMappings(SetupFileHash *mappings_hash,
11707 if (!(file = fopen(filename, MODE_READ)))
11709 Warn("cannot read game controller mappings file '%s'", filename);
11714 while (!feof(file))
11716 char line[MAX_LINE_LEN];
11718 if (!fgets(line, MAX_LINE_LEN, file))
11721 addGameControllerMappingToHash(mappings_hash, line);
11727 void SaveSetup_Default(void)
11729 char *filename = getSetupFilename();
11733 InitUserDataDirectory();
11735 if (!(file = fopen(filename, MODE_WRITE)))
11737 Warn("cannot write setup file '%s'", filename);
11742 fprintFileHeader(file, SETUP_FILENAME);
11744 for (i = 0; i < ARRAY_SIZE(global_setup_tokens); i++)
11746 // just to make things nicer :)
11747 if (global_setup_tokens[i].value == &setup.multiple_users ||
11748 global_setup_tokens[i].value == &setup.sound ||
11749 global_setup_tokens[i].value == &setup.graphics_set ||
11750 global_setup_tokens[i].value == &setup.volume_simple ||
11751 global_setup_tokens[i].value == &setup.network_mode ||
11752 global_setup_tokens[i].value == &setup.touch.control_type ||
11753 global_setup_tokens[i].value == &setup.touch.grid_xsize[0] ||
11754 global_setup_tokens[i].value == &setup.touch.grid_xsize[1])
11755 fprintf(file, "\n");
11757 fprintf(file, "%s\n", getSetupLine(global_setup_tokens, "", i));
11760 for (i = 0; i < 2; i++)
11762 int grid_xsize = setup.touch.grid_xsize[i];
11763 int grid_ysize = setup.touch.grid_ysize[i];
11766 fprintf(file, "\n");
11768 for (y = 0; y < grid_ysize; y++)
11770 char token_string[MAX_LINE_LEN];
11771 char value_string[MAX_LINE_LEN];
11773 sprintf(token_string, "touch.virtual_buttons.%d.%02d", i, y);
11775 for (x = 0; x < grid_xsize; x++)
11777 char c = setup.touch.grid_button[i][x][y];
11779 value_string[x] = (c == CHAR_GRID_BUTTON_NONE ? '.' : c);
11782 value_string[grid_xsize] = '\0';
11784 fprintf(file, "%s\n", getFormattedSetupEntry(token_string, value_string));
11788 fprintf(file, "\n");
11789 for (i = 0; i < ARRAY_SIZE(editor_setup_tokens); i++)
11790 fprintf(file, "%s\n", getSetupLine(editor_setup_tokens, "", i));
11792 fprintf(file, "\n");
11793 for (i = 0; i < ARRAY_SIZE(shortcut_setup_tokens); i++)
11794 fprintf(file, "%s\n", getSetupLine(shortcut_setup_tokens, "", i));
11796 for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
11800 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
11801 fprintf(file, "\n");
11803 setup_input = setup.input[pnr];
11804 for (i = 0; i < ARRAY_SIZE(player_setup_tokens); i++)
11805 fprintf(file, "%s\n", getSetupLine(player_setup_tokens, prefix, i));
11808 fprintf(file, "\n");
11809 for (i = 0; i < ARRAY_SIZE(system_setup_tokens); i++)
11810 fprintf(file, "%s\n", getSetupLine(system_setup_tokens, "", i));
11812 // (internal setup values not saved to user setup file)
11814 fprintf(file, "\n");
11815 for (i = 0; i < ARRAY_SIZE(debug_setup_tokens); i++)
11816 if (!strPrefix(debug_setup_tokens[i].text, "debug.xsn_") ||
11817 setup.debug.xsn_mode != AUTO)
11818 fprintf(file, "%s\n", getSetupLine(debug_setup_tokens, "", i));
11820 fprintf(file, "\n");
11821 for (i = 0; i < ARRAY_SIZE(options_setup_tokens); i++)
11822 fprintf(file, "%s\n", getSetupLine(options_setup_tokens, "", i));
11826 SetFilePermissions(filename, PERMS_PRIVATE);
11829 void SaveSetup_AutoSetup(void)
11831 char *filename = getPath2(getSetupDir(), AUTOSETUP_FILENAME);
11835 InitUserDataDirectory();
11837 if (!(file = fopen(filename, MODE_WRITE)))
11839 Warn("cannot write auto setup file '%s'", filename);
11846 fprintFileHeader(file, AUTOSETUP_FILENAME);
11848 for (i = 0; i < ARRAY_SIZE(auto_setup_tokens); i++)
11849 fprintf(file, "%s\n", getSetupLine(auto_setup_tokens, "", i));
11853 SetFilePermissions(filename, PERMS_PRIVATE);
11858 void SaveSetup_ServerSetup(void)
11860 char *filename = getPath2(getSetupDir(), SERVERSETUP_FILENAME);
11864 InitUserDataDirectory();
11866 if (!(file = fopen(filename, MODE_WRITE)))
11868 Warn("cannot write server setup file '%s'", filename);
11875 fprintFileHeader(file, SERVERSETUP_FILENAME);
11877 for (i = 0; i < ARRAY_SIZE(server_setup_tokens); i++)
11879 // just to make things nicer :)
11880 if (server_setup_tokens[i].value == &setup.use_api_server)
11881 fprintf(file, "\n");
11883 fprintf(file, "%s\n", getSetupLine(server_setup_tokens, "", i));
11888 SetFilePermissions(filename, PERMS_PRIVATE);
11893 void SaveSetup_EditorCascade(void)
11895 char *filename = getPath2(getSetupDir(), EDITORCASCADE_FILENAME);
11899 InitUserDataDirectory();
11901 if (!(file = fopen(filename, MODE_WRITE)))
11903 Warn("cannot write editor cascade state file '%s'", filename);
11910 fprintFileHeader(file, EDITORCASCADE_FILENAME);
11912 for (i = 0; i < ARRAY_SIZE(editor_cascade_setup_tokens); i++)
11913 fprintf(file, "%s\n", getSetupLine(editor_cascade_setup_tokens, "", i));
11917 SetFilePermissions(filename, PERMS_PRIVATE);
11922 void SaveSetup(void)
11924 SaveSetup_Default();
11925 SaveSetup_AutoSetup();
11926 SaveSetup_ServerSetup();
11927 SaveSetup_EditorCascade();
11930 static void SaveSetup_WriteGameControllerMappings(SetupFileHash *mappings_hash,
11935 if (!(file = fopen(filename, MODE_WRITE)))
11937 Warn("cannot write game controller mappings file '%s'", filename);
11942 BEGIN_HASH_ITERATION(mappings_hash, itr)
11944 fprintf(file, "%s\n", HASH_ITERATION_VALUE(itr));
11946 END_HASH_ITERATION(mappings_hash, itr)
11951 void SaveSetup_AddGameControllerMapping(char *mapping)
11953 char *filename = getPath2(getSetupDir(), GAMECONTROLLER_BASENAME);
11954 SetupFileHash *mappings_hash = newSetupFileHash();
11956 InitUserDataDirectory();
11958 // load existing personal game controller mappings
11959 LoadSetup_ReadGameControllerMappings(mappings_hash, filename);
11961 // add new mapping to personal game controller mappings
11962 addGameControllerMappingToHash(mappings_hash, mapping);
11964 // save updated personal game controller mappings
11965 SaveSetup_WriteGameControllerMappings(mappings_hash, filename);
11967 freeSetupFileHash(mappings_hash);
11971 void LoadCustomElementDescriptions(void)
11973 char *filename = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
11974 SetupFileHash *setup_file_hash;
11977 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
11979 if (element_info[i].custom_description != NULL)
11981 free(element_info[i].custom_description);
11982 element_info[i].custom_description = NULL;
11986 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
11989 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
11991 char *token = getStringCat2(element_info[i].token_name, ".name");
11992 char *value = getHashEntry(setup_file_hash, token);
11995 element_info[i].custom_description = getStringCopy(value);
12000 freeSetupFileHash(setup_file_hash);
12003 static int getElementFromToken(char *token)
12005 char *value = getHashEntry(element_token_hash, token);
12008 return atoi(value);
12010 Warn("unknown element token '%s'", token);
12012 return EL_UNDEFINED;
12015 void FreeGlobalAnimEventInfo(void)
12017 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
12019 if (gaei->event_list == NULL)
12024 for (i = 0; i < gaei->num_event_lists; i++)
12026 checked_free(gaei->event_list[i]->event_value);
12027 checked_free(gaei->event_list[i]);
12030 checked_free(gaei->event_list);
12032 gaei->event_list = NULL;
12033 gaei->num_event_lists = 0;
12036 static int AddGlobalAnimEventList(void)
12038 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
12039 int list_pos = gaei->num_event_lists++;
12041 gaei->event_list = checked_realloc(gaei->event_list, gaei->num_event_lists *
12042 sizeof(struct GlobalAnimEventListInfo *));
12044 gaei->event_list[list_pos] =
12045 checked_calloc(sizeof(struct GlobalAnimEventListInfo));
12047 struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
12049 gaeli->event_value = NULL;
12050 gaeli->num_event_values = 0;
12055 static int AddGlobalAnimEventValue(int list_pos, int event_value)
12057 // do not add empty global animation events
12058 if (event_value == ANIM_EVENT_NONE)
12061 // if list position is undefined, create new list
12062 if (list_pos == ANIM_EVENT_UNDEFINED)
12063 list_pos = AddGlobalAnimEventList();
12065 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
12066 struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
12067 int value_pos = gaeli->num_event_values++;
12069 gaeli->event_value = checked_realloc(gaeli->event_value,
12070 gaeli->num_event_values * sizeof(int *));
12072 gaeli->event_value[value_pos] = event_value;
12077 int GetGlobalAnimEventValue(int list_pos, int value_pos)
12079 if (list_pos == ANIM_EVENT_UNDEFINED)
12080 return ANIM_EVENT_NONE;
12082 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
12083 struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
12085 return gaeli->event_value[value_pos];
12088 int GetGlobalAnimEventValueCount(int list_pos)
12090 if (list_pos == ANIM_EVENT_UNDEFINED)
12093 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
12094 struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
12096 return gaeli->num_event_values;
12099 // This function checks if a string <s> of the format "string1, string2, ..."
12100 // exactly contains a string <s_contained>.
12102 static boolean string_has_parameter(char *s, char *s_contained)
12106 if (s == NULL || s_contained == NULL)
12109 if (strlen(s_contained) > strlen(s))
12112 if (strncmp(s, s_contained, strlen(s_contained)) == 0)
12114 char next_char = s[strlen(s_contained)];
12116 // check if next character is delimiter or whitespace
12117 if (next_char == ',' || next_char == '\0' ||
12118 next_char == ' ' || next_char == '\t')
12122 // check if string contains another parameter string after a comma
12123 substring = strchr(s, ',');
12124 if (substring == NULL) // string does not contain a comma
12127 // advance string pointer to next character after the comma
12130 // skip potential whitespaces after the comma
12131 while (*substring == ' ' || *substring == '\t')
12134 return string_has_parameter(substring, s_contained);
12137 static int get_anim_parameter_value_ce(char *s)
12140 char *pattern_1 = "ce_change:custom_";
12141 char *pattern_2 = ".page_";
12142 int pattern_1_len = strlen(pattern_1);
12143 char *matching_char = strstr(s_ptr, pattern_1);
12144 int result = ANIM_EVENT_NONE;
12146 if (matching_char == NULL)
12147 return ANIM_EVENT_NONE;
12149 result = ANIM_EVENT_CE_CHANGE;
12151 s_ptr = matching_char + pattern_1_len;
12153 // check for custom element number ("custom_X", "custom_XX" or "custom_XXX")
12154 if (*s_ptr >= '0' && *s_ptr <= '9')
12156 int gic_ce_nr = (*s_ptr++ - '0');
12158 if (*s_ptr >= '0' && *s_ptr <= '9')
12160 gic_ce_nr = 10 * gic_ce_nr + (*s_ptr++ - '0');
12162 if (*s_ptr >= '0' && *s_ptr <= '9')
12163 gic_ce_nr = 10 * gic_ce_nr + (*s_ptr++ - '0');
12166 if (gic_ce_nr < 1 || gic_ce_nr > NUM_CUSTOM_ELEMENTS)
12167 return ANIM_EVENT_NONE;
12169 // custom element stored as 0 to 255
12172 result |= gic_ce_nr << ANIM_EVENT_CE_BIT;
12176 // invalid custom element number specified
12178 return ANIM_EVENT_NONE;
12181 // check for change page number ("page_X" or "page_XX") (optional)
12182 if (strPrefix(s_ptr, pattern_2))
12184 s_ptr += strlen(pattern_2);
12186 if (*s_ptr >= '0' && *s_ptr <= '9')
12188 int gic_page_nr = (*s_ptr++ - '0');
12190 if (*s_ptr >= '0' && *s_ptr <= '9')
12191 gic_page_nr = 10 * gic_page_nr + (*s_ptr++ - '0');
12193 if (gic_page_nr < 1 || gic_page_nr > MAX_CHANGE_PAGES)
12194 return ANIM_EVENT_NONE;
12196 // change page stored as 1 to 32 (0 means "all change pages")
12198 result |= gic_page_nr << ANIM_EVENT_PAGE_BIT;
12202 // invalid animation part number specified
12204 return ANIM_EVENT_NONE;
12208 // discard result if next character is neither delimiter nor whitespace
12209 if (!(*s_ptr == ',' || *s_ptr == '\0' ||
12210 *s_ptr == ' ' || *s_ptr == '\t'))
12211 return ANIM_EVENT_NONE;
12216 static int get_anim_parameter_value(char *s)
12218 int event_value[] =
12226 char *pattern_1[] =
12234 char *pattern_2 = ".part_";
12235 char *matching_char = NULL;
12237 int pattern_1_len = 0;
12238 int result = ANIM_EVENT_NONE;
12241 result = get_anim_parameter_value_ce(s);
12243 if (result != ANIM_EVENT_NONE)
12246 for (i = 0; i < ARRAY_SIZE(event_value); i++)
12248 matching_char = strstr(s_ptr, pattern_1[i]);
12249 pattern_1_len = strlen(pattern_1[i]);
12250 result = event_value[i];
12252 if (matching_char != NULL)
12256 if (matching_char == NULL)
12257 return ANIM_EVENT_NONE;
12259 s_ptr = matching_char + pattern_1_len;
12261 // check for main animation number ("anim_X" or "anim_XX")
12262 if (*s_ptr >= '0' && *s_ptr <= '9')
12264 int gic_anim_nr = (*s_ptr++ - '0');
12266 if (*s_ptr >= '0' && *s_ptr <= '9')
12267 gic_anim_nr = 10 * gic_anim_nr + (*s_ptr++ - '0');
12269 if (gic_anim_nr < 1 || gic_anim_nr > MAX_GLOBAL_ANIMS)
12270 return ANIM_EVENT_NONE;
12272 result |= gic_anim_nr << ANIM_EVENT_ANIM_BIT;
12276 // invalid main animation number specified
12278 return ANIM_EVENT_NONE;
12281 // check for animation part number ("part_X" or "part_XX") (optional)
12282 if (strPrefix(s_ptr, pattern_2))
12284 s_ptr += strlen(pattern_2);
12286 if (*s_ptr >= '0' && *s_ptr <= '9')
12288 int gic_part_nr = (*s_ptr++ - '0');
12290 if (*s_ptr >= '0' && *s_ptr <= '9')
12291 gic_part_nr = 10 * gic_part_nr + (*s_ptr++ - '0');
12293 if (gic_part_nr < 1 || gic_part_nr > MAX_GLOBAL_ANIM_PARTS)
12294 return ANIM_EVENT_NONE;
12296 result |= gic_part_nr << ANIM_EVENT_PART_BIT;
12300 // invalid animation part number specified
12302 return ANIM_EVENT_NONE;
12306 // discard result if next character is neither delimiter nor whitespace
12307 if (!(*s_ptr == ',' || *s_ptr == '\0' ||
12308 *s_ptr == ' ' || *s_ptr == '\t'))
12309 return ANIM_EVENT_NONE;
12314 static int get_anim_parameter_values(char *s)
12316 int list_pos = ANIM_EVENT_UNDEFINED;
12317 int event_value = ANIM_EVENT_DEFAULT;
12319 if (string_has_parameter(s, "any"))
12320 event_value |= ANIM_EVENT_ANY;
12322 if (string_has_parameter(s, "click:self") ||
12323 string_has_parameter(s, "click") ||
12324 string_has_parameter(s, "self"))
12325 event_value |= ANIM_EVENT_SELF;
12327 if (string_has_parameter(s, "unclick:any"))
12328 event_value |= ANIM_EVENT_UNCLICK_ANY;
12330 // if animation event found, add it to global animation event list
12331 if (event_value != ANIM_EVENT_NONE)
12332 list_pos = AddGlobalAnimEventValue(list_pos, event_value);
12336 // add optional "click:anim_X" or "click:anim_X.part_X" parameter
12337 event_value = get_anim_parameter_value(s);
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);
12343 // continue with next part of the string, starting with next comma
12344 s = strchr(s + 1, ',');
12350 static int get_anim_action_parameter_value(char *token)
12352 // check most common default case first to massively speed things up
12353 if (strEqual(token, ARG_UNDEFINED))
12354 return ANIM_EVENT_ACTION_NONE;
12356 int result = getImageIDFromToken(token);
12360 char *gfx_token = getStringCat2("gfx.", token);
12362 result = getImageIDFromToken(gfx_token);
12364 checked_free(gfx_token);
12369 Key key = getKeyFromX11KeyName(token);
12371 if (key != KSYM_UNDEFINED)
12372 result = -(int)key;
12379 result = get_hash_from_string(token); // unsigned int => int
12380 result = ABS(result); // may be negative now
12381 result += (result < MAX_IMAGE_FILES ? MAX_IMAGE_FILES : 0);
12383 setHashEntry(anim_url_hash, int2str(result, 0), token);
12388 result = ANIM_EVENT_ACTION_NONE;
12393 int get_parameter_value(char *value_raw, char *suffix, int type)
12395 char *value = getStringToLower(value_raw);
12396 int result = 0; // probably a save default value
12398 if (strEqual(suffix, ".direction"))
12400 result = (strEqual(value, "left") ? MV_LEFT :
12401 strEqual(value, "right") ? MV_RIGHT :
12402 strEqual(value, "up") ? MV_UP :
12403 strEqual(value, "down") ? MV_DOWN : MV_NONE);
12405 else if (strEqual(suffix, ".position"))
12407 result = (strEqual(value, "left") ? POS_LEFT :
12408 strEqual(value, "right") ? POS_RIGHT :
12409 strEqual(value, "top") ? POS_TOP :
12410 strEqual(value, "upper") ? POS_UPPER :
12411 strEqual(value, "middle") ? POS_MIDDLE :
12412 strEqual(value, "lower") ? POS_LOWER :
12413 strEqual(value, "bottom") ? POS_BOTTOM :
12414 strEqual(value, "any") ? POS_ANY :
12415 strEqual(value, "ce") ? POS_CE :
12416 strEqual(value, "ce_trigger") ? POS_CE_TRIGGER :
12417 strEqual(value, "last") ? POS_LAST : POS_UNDEFINED);
12419 else if (strEqual(suffix, ".align"))
12421 result = (strEqual(value, "left") ? ALIGN_LEFT :
12422 strEqual(value, "right") ? ALIGN_RIGHT :
12423 strEqual(value, "center") ? ALIGN_CENTER :
12424 strEqual(value, "middle") ? ALIGN_CENTER : ALIGN_DEFAULT);
12426 else if (strEqual(suffix, ".valign"))
12428 result = (strEqual(value, "top") ? VALIGN_TOP :
12429 strEqual(value, "bottom") ? VALIGN_BOTTOM :
12430 strEqual(value, "middle") ? VALIGN_MIDDLE :
12431 strEqual(value, "center") ? VALIGN_MIDDLE : VALIGN_DEFAULT);
12433 else if (strEqual(suffix, ".anim_mode"))
12435 result = (string_has_parameter(value, "none") ? ANIM_NONE :
12436 string_has_parameter(value, "loop") ? ANIM_LOOP :
12437 string_has_parameter(value, "linear") ? ANIM_LINEAR :
12438 string_has_parameter(value, "pingpong") ? ANIM_PINGPONG :
12439 string_has_parameter(value, "pingpong2") ? ANIM_PINGPONG2 :
12440 string_has_parameter(value, "random") ? ANIM_RANDOM :
12441 string_has_parameter(value, "random_static") ? ANIM_RANDOM_STATIC :
12442 string_has_parameter(value, "ce_value") ? ANIM_CE_VALUE :
12443 string_has_parameter(value, "ce_score") ? ANIM_CE_SCORE :
12444 string_has_parameter(value, "ce_delay") ? ANIM_CE_DELAY :
12445 string_has_parameter(value, "horizontal") ? ANIM_HORIZONTAL :
12446 string_has_parameter(value, "vertical") ? ANIM_VERTICAL :
12447 string_has_parameter(value, "centered") ? ANIM_CENTERED :
12448 string_has_parameter(value, "all") ? ANIM_ALL :
12449 string_has_parameter(value, "tiled") ? ANIM_TILED :
12450 string_has_parameter(value, "level_nr") ? ANIM_LEVEL_NR :
12453 if (string_has_parameter(value, "once"))
12454 result |= ANIM_ONCE;
12456 if (string_has_parameter(value, "reverse"))
12457 result |= ANIM_REVERSE;
12459 if (string_has_parameter(value, "opaque_player"))
12460 result |= ANIM_OPAQUE_PLAYER;
12462 if (string_has_parameter(value, "static_panel"))
12463 result |= ANIM_STATIC_PANEL;
12465 else if (strEqual(suffix, ".init_event") ||
12466 strEqual(suffix, ".anim_event"))
12468 result = get_anim_parameter_values(value);
12470 else if (strEqual(suffix, ".init_delay_action") ||
12471 strEqual(suffix, ".anim_delay_action") ||
12472 strEqual(suffix, ".post_delay_action") ||
12473 strEqual(suffix, ".init_event_action") ||
12474 strEqual(suffix, ".anim_event_action"))
12476 result = get_anim_action_parameter_value(value_raw);
12478 else if (strEqual(suffix, ".class"))
12480 result = (strEqual(value, ARG_UNDEFINED) ? ARG_UNDEFINED_VALUE :
12481 get_hash_from_string(value));
12483 else if (strEqual(suffix, ".style"))
12485 result = STYLE_DEFAULT;
12487 if (string_has_parameter(value, "accurate_borders"))
12488 result |= STYLE_ACCURATE_BORDERS;
12490 if (string_has_parameter(value, "inner_corners"))
12491 result |= STYLE_INNER_CORNERS;
12493 if (string_has_parameter(value, "reverse"))
12494 result |= STYLE_REVERSE;
12496 if (string_has_parameter(value, "leftmost_position"))
12497 result |= STYLE_LEFTMOST_POSITION;
12499 if (string_has_parameter(value, "block_clicks"))
12500 result |= STYLE_BLOCK;
12502 if (string_has_parameter(value, "passthrough_clicks"))
12503 result |= STYLE_PASSTHROUGH;
12505 if (string_has_parameter(value, "multiple_actions"))
12506 result |= STYLE_MULTIPLE_ACTIONS;
12508 if (string_has_parameter(value, "consume_ce_event"))
12509 result |= STYLE_CONSUME_CE_EVENT;
12511 else if (strEqual(suffix, ".fade_mode"))
12513 result = (string_has_parameter(value, "none") ? FADE_MODE_NONE :
12514 string_has_parameter(value, "fade") ? FADE_MODE_FADE :
12515 string_has_parameter(value, "fade_in") ? FADE_MODE_FADE_IN :
12516 string_has_parameter(value, "fade_out") ? FADE_MODE_FADE_OUT :
12517 string_has_parameter(value, "crossfade") ? FADE_MODE_CROSSFADE :
12518 string_has_parameter(value, "melt") ? FADE_MODE_MELT :
12519 string_has_parameter(value, "curtain") ? FADE_MODE_CURTAIN :
12520 FADE_MODE_DEFAULT);
12522 else if (strEqual(suffix, ".auto_delay_unit"))
12524 result = (string_has_parameter(value, "ms") ? AUTO_DELAY_UNIT_MS :
12525 string_has_parameter(value, "frames") ? AUTO_DELAY_UNIT_FRAMES :
12526 AUTO_DELAY_UNIT_DEFAULT);
12528 else if (strPrefix(suffix, ".font")) // (may also be ".font_xyz")
12530 result = gfx.get_font_from_token_function(value);
12532 else // generic parameter of type integer or boolean
12534 result = (strEqual(value, ARG_UNDEFINED) ? ARG_UNDEFINED_VALUE :
12535 type == TYPE_INTEGER ? get_integer_from_string(value) :
12536 type == TYPE_BOOLEAN ? get_boolean_from_string(value) :
12537 ARG_UNDEFINED_VALUE);
12545 static int get_token_parameter_value(char *token, char *value_raw)
12549 if (token == NULL || value_raw == NULL)
12550 return ARG_UNDEFINED_VALUE;
12552 suffix = strrchr(token, '.');
12553 if (suffix == NULL)
12556 if (strEqual(suffix, ".element"))
12557 return getElementFromToken(value_raw);
12559 // !!! USE CORRECT VALUE TYPE (currently works also for TYPE_BOOLEAN) !!!
12560 return get_parameter_value(value_raw, suffix, TYPE_INTEGER);
12563 void InitMenuDesignSettings_FromHash(SetupFileHash *setup_file_hash,
12564 boolean ignore_defaults)
12568 for (i = 0; image_config_vars[i].token != NULL; i++)
12570 char *value = getHashEntry(setup_file_hash, image_config_vars[i].token);
12572 // (ignore definitions set to "[DEFAULT]" which are already initialized)
12573 if (ignore_defaults && strEqual(value, ARG_DEFAULT))
12577 *image_config_vars[i].value =
12578 get_token_parameter_value(image_config_vars[i].token, value);
12582 void InitMenuDesignSettings_Static(void)
12584 // always start with reliable default values from static default config
12585 InitMenuDesignSettings_FromHash(image_config_hash, FALSE);
12588 static void InitMenuDesignSettings_SpecialPreProcessing(void)
12592 // the following initializes hierarchical values from static configuration
12594 // special case: initialize "ARG_DEFAULT" values in static default config
12595 // (e.g., initialize "[titlemessage].fade_mode" from "[title].fade_mode")
12596 titlescreen_initial_first_default.fade_mode =
12597 title_initial_first_default.fade_mode;
12598 titlescreen_initial_first_default.fade_delay =
12599 title_initial_first_default.fade_delay;
12600 titlescreen_initial_first_default.post_delay =
12601 title_initial_first_default.post_delay;
12602 titlescreen_initial_first_default.auto_delay =
12603 title_initial_first_default.auto_delay;
12604 titlescreen_initial_first_default.auto_delay_unit =
12605 title_initial_first_default.auto_delay_unit;
12606 titlescreen_first_default.fade_mode = title_first_default.fade_mode;
12607 titlescreen_first_default.fade_delay = title_first_default.fade_delay;
12608 titlescreen_first_default.post_delay = title_first_default.post_delay;
12609 titlescreen_first_default.auto_delay = title_first_default.auto_delay;
12610 titlescreen_first_default.auto_delay_unit =
12611 title_first_default.auto_delay_unit;
12612 titlemessage_initial_first_default.fade_mode =
12613 title_initial_first_default.fade_mode;
12614 titlemessage_initial_first_default.fade_delay =
12615 title_initial_first_default.fade_delay;
12616 titlemessage_initial_first_default.post_delay =
12617 title_initial_first_default.post_delay;
12618 titlemessage_initial_first_default.auto_delay =
12619 title_initial_first_default.auto_delay;
12620 titlemessage_initial_first_default.auto_delay_unit =
12621 title_initial_first_default.auto_delay_unit;
12622 titlemessage_first_default.fade_mode = title_first_default.fade_mode;
12623 titlemessage_first_default.fade_delay = title_first_default.fade_delay;
12624 titlemessage_first_default.post_delay = title_first_default.post_delay;
12625 titlemessage_first_default.auto_delay = title_first_default.auto_delay;
12626 titlemessage_first_default.auto_delay_unit =
12627 title_first_default.auto_delay_unit;
12629 titlescreen_initial_default.fade_mode = title_initial_default.fade_mode;
12630 titlescreen_initial_default.fade_delay = title_initial_default.fade_delay;
12631 titlescreen_initial_default.post_delay = title_initial_default.post_delay;
12632 titlescreen_initial_default.auto_delay = title_initial_default.auto_delay;
12633 titlescreen_initial_default.auto_delay_unit =
12634 title_initial_default.auto_delay_unit;
12635 titlescreen_default.fade_mode = title_default.fade_mode;
12636 titlescreen_default.fade_delay = title_default.fade_delay;
12637 titlescreen_default.post_delay = title_default.post_delay;
12638 titlescreen_default.auto_delay = title_default.auto_delay;
12639 titlescreen_default.auto_delay_unit = title_default.auto_delay_unit;
12640 titlemessage_initial_default.fade_mode = title_initial_default.fade_mode;
12641 titlemessage_initial_default.fade_delay = title_initial_default.fade_delay;
12642 titlemessage_initial_default.post_delay = title_initial_default.post_delay;
12643 titlemessage_initial_default.auto_delay_unit =
12644 title_initial_default.auto_delay_unit;
12645 titlemessage_default.fade_mode = title_default.fade_mode;
12646 titlemessage_default.fade_delay = title_default.fade_delay;
12647 titlemessage_default.post_delay = title_default.post_delay;
12648 titlemessage_default.auto_delay = title_default.auto_delay;
12649 titlemessage_default.auto_delay_unit = title_default.auto_delay_unit;
12651 // special case: initialize "ARG_DEFAULT" values in static default config
12652 // (e.g., init "titlemessage_1.fade_mode" from "[titlemessage].fade_mode")
12653 for (i = 0; i < MAX_NUM_TITLE_MESSAGES; i++)
12655 titlescreen_initial_first[i] = titlescreen_initial_first_default;
12656 titlescreen_first[i] = titlescreen_first_default;
12657 titlemessage_initial_first[i] = titlemessage_initial_first_default;
12658 titlemessage_first[i] = titlemessage_first_default;
12660 titlescreen_initial[i] = titlescreen_initial_default;
12661 titlescreen[i] = titlescreen_default;
12662 titlemessage_initial[i] = titlemessage_initial_default;
12663 titlemessage[i] = titlemessage_default;
12666 // special case: initialize "ARG_DEFAULT" values in static default config
12667 // (eg, init "menu.enter_screen.SCORES.xyz" from "menu.enter_screen.xyz")
12668 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
12670 if (i == GFX_SPECIAL_ARG_TITLE) // title values already initialized
12673 menu.enter_screen[i] = menu.enter_screen[GFX_SPECIAL_ARG_DEFAULT];
12674 menu.leave_screen[i] = menu.leave_screen[GFX_SPECIAL_ARG_DEFAULT];
12675 menu.next_screen[i] = menu.next_screen[GFX_SPECIAL_ARG_DEFAULT];
12678 // special case: initialize "ARG_DEFAULT" values in static default config
12679 // (eg, init "viewport.door_1.MAIN.xyz" from "viewport.door_1.xyz")
12680 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
12682 viewport.window[i] = viewport.window[GFX_SPECIAL_ARG_DEFAULT];
12683 viewport.playfield[i] = viewport.playfield[GFX_SPECIAL_ARG_DEFAULT];
12684 viewport.door_1[i] = viewport.door_1[GFX_SPECIAL_ARG_DEFAULT];
12686 if (i == GFX_SPECIAL_ARG_EDITOR) // editor values already initialized
12689 viewport.door_2[i] = viewport.door_2[GFX_SPECIAL_ARG_DEFAULT];
12693 static void InitMenuDesignSettings_SpecialPostProcessing(void)
12697 struct XY *dst, *src;
12699 game_buttons_xy[] =
12701 { &game.button.save, &game.button.stop },
12702 { &game.button.pause2, &game.button.pause },
12703 { &game.button.load, &game.button.play },
12704 { &game.button.undo, &game.button.stop },
12705 { &game.button.redo, &game.button.play },
12711 // special case: initialize later added SETUP list size from LEVELS value
12712 if (menu.list_size[GAME_MODE_SETUP] == -1)
12713 menu.list_size[GAME_MODE_SETUP] = menu.list_size[GAME_MODE_LEVELS];
12715 // set default position for snapshot buttons to stop/pause/play buttons
12716 for (i = 0; game_buttons_xy[i].dst != NULL; i++)
12717 if ((*game_buttons_xy[i].dst).x == -1 &&
12718 (*game_buttons_xy[i].dst).y == -1)
12719 *game_buttons_xy[i].dst = *game_buttons_xy[i].src;
12721 // --------------------------------------------------------------------------
12722 // dynamic viewports (including playfield margins, borders and alignments)
12723 // --------------------------------------------------------------------------
12725 // dynamic viewports currently only supported for landscape mode
12726 int display_width = MAX(video.display_width, video.display_height);
12727 int display_height = MIN(video.display_width, video.display_height);
12729 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
12731 struct RectWithBorder *vp_window = &viewport.window[i];
12732 struct RectWithBorder *vp_playfield = &viewport.playfield[i];
12733 struct RectWithBorder *vp_door_1 = &viewport.door_1[i];
12734 struct RectWithBorder *vp_door_2 = &viewport.door_2[i];
12735 boolean dynamic_window_width = (vp_window->min_width != -1);
12736 boolean dynamic_window_height = (vp_window->min_height != -1);
12737 boolean dynamic_playfield_width = (vp_playfield->min_width != -1);
12738 boolean dynamic_playfield_height = (vp_playfield->min_height != -1);
12740 // adjust window size if min/max width/height is specified
12742 if (vp_window->min_width != -1)
12744 int window_width = display_width;
12746 // when using static window height, use aspect ratio of display
12747 if (vp_window->min_height == -1)
12748 window_width = vp_window->height * display_width / display_height;
12750 vp_window->width = MAX(vp_window->min_width, window_width);
12753 if (vp_window->min_height != -1)
12755 int window_height = display_height;
12757 // when using static window width, use aspect ratio of display
12758 if (vp_window->min_width == -1)
12759 window_height = vp_window->width * display_height / display_width;
12761 vp_window->height = MAX(vp_window->min_height, window_height);
12764 if (vp_window->max_width != -1)
12765 vp_window->width = MIN(vp_window->width, vp_window->max_width);
12767 if (vp_window->max_height != -1)
12768 vp_window->height = MIN(vp_window->height, vp_window->max_height);
12770 int playfield_width = vp_window->width;
12771 int playfield_height = vp_window->height;
12773 // adjust playfield size and position according to specified margins
12775 playfield_width -= vp_playfield->margin_left;
12776 playfield_width -= vp_playfield->margin_right;
12778 playfield_height -= vp_playfield->margin_top;
12779 playfield_height -= vp_playfield->margin_bottom;
12781 // adjust playfield size if min/max width/height is specified
12783 if (vp_playfield->min_width != -1)
12784 vp_playfield->width = MAX(vp_playfield->min_width, playfield_width);
12786 if (vp_playfield->min_height != -1)
12787 vp_playfield->height = MAX(vp_playfield->min_height, playfield_height);
12789 if (vp_playfield->max_width != -1)
12790 vp_playfield->width = MIN(vp_playfield->width, vp_playfield->max_width);
12792 if (vp_playfield->max_height != -1)
12793 vp_playfield->height = MIN(vp_playfield->height, vp_playfield->max_height);
12795 // adjust playfield position according to specified alignment
12797 if (vp_playfield->align == ALIGN_LEFT || vp_playfield->x > 0)
12798 vp_playfield->x = ALIGNED_VP_XPOS(vp_playfield);
12799 else if (vp_playfield->align == ALIGN_CENTER)
12800 vp_playfield->x = playfield_width / 2 - vp_playfield->width / 2;
12801 else if (vp_playfield->align == ALIGN_RIGHT)
12802 vp_playfield->x += playfield_width - vp_playfield->width;
12804 if (vp_playfield->valign == VALIGN_TOP || vp_playfield->y > 0)
12805 vp_playfield->y = ALIGNED_VP_YPOS(vp_playfield);
12806 else if (vp_playfield->valign == VALIGN_MIDDLE)
12807 vp_playfield->y = playfield_height / 2 - vp_playfield->height / 2;
12808 else if (vp_playfield->valign == VALIGN_BOTTOM)
12809 vp_playfield->y += playfield_height - vp_playfield->height;
12811 vp_playfield->x += vp_playfield->margin_left;
12812 vp_playfield->y += vp_playfield->margin_top;
12814 // adjust individual playfield borders if only default border is specified
12816 if (vp_playfield->border_left == -1)
12817 vp_playfield->border_left = vp_playfield->border_size;
12818 if (vp_playfield->border_right == -1)
12819 vp_playfield->border_right = vp_playfield->border_size;
12820 if (vp_playfield->border_top == -1)
12821 vp_playfield->border_top = vp_playfield->border_size;
12822 if (vp_playfield->border_bottom == -1)
12823 vp_playfield->border_bottom = vp_playfield->border_size;
12825 // set dynamic playfield borders if borders are specified as undefined
12826 // (but only if window size was dynamic and playfield size was static)
12828 if (dynamic_window_width && !dynamic_playfield_width)
12830 if (vp_playfield->border_left == -1)
12832 vp_playfield->border_left = (vp_playfield->x -
12833 vp_playfield->margin_left);
12834 vp_playfield->x -= vp_playfield->border_left;
12835 vp_playfield->width += vp_playfield->border_left;
12838 if (vp_playfield->border_right == -1)
12840 vp_playfield->border_right = (vp_window->width -
12842 vp_playfield->width -
12843 vp_playfield->margin_right);
12844 vp_playfield->width += vp_playfield->border_right;
12848 if (dynamic_window_height && !dynamic_playfield_height)
12850 if (vp_playfield->border_top == -1)
12852 vp_playfield->border_top = (vp_playfield->y -
12853 vp_playfield->margin_top);
12854 vp_playfield->y -= vp_playfield->border_top;
12855 vp_playfield->height += vp_playfield->border_top;
12858 if (vp_playfield->border_bottom == -1)
12860 vp_playfield->border_bottom = (vp_window->height -
12862 vp_playfield->height -
12863 vp_playfield->margin_bottom);
12864 vp_playfield->height += vp_playfield->border_bottom;
12868 // adjust playfield size to be a multiple of a defined alignment tile size
12870 int align_size = vp_playfield->align_size;
12871 int playfield_xtiles = vp_playfield->width / align_size;
12872 int playfield_ytiles = vp_playfield->height / align_size;
12873 int playfield_width_corrected = playfield_xtiles * align_size;
12874 int playfield_height_corrected = playfield_ytiles * align_size;
12875 boolean is_playfield_mode = (i == GFX_SPECIAL_ARG_PLAYING ||
12876 i == GFX_SPECIAL_ARG_EDITOR);
12878 if (is_playfield_mode &&
12879 dynamic_playfield_width &&
12880 vp_playfield->width != playfield_width_corrected)
12882 int playfield_xdiff = vp_playfield->width - playfield_width_corrected;
12884 vp_playfield->width = playfield_width_corrected;
12886 if (vp_playfield->align == ALIGN_LEFT)
12888 vp_playfield->border_left += playfield_xdiff;
12890 else if (vp_playfield->align == ALIGN_RIGHT)
12892 vp_playfield->border_right += playfield_xdiff;
12894 else if (vp_playfield->align == ALIGN_CENTER)
12896 int border_left_diff = playfield_xdiff / 2;
12897 int border_right_diff = playfield_xdiff - border_left_diff;
12899 vp_playfield->border_left += border_left_diff;
12900 vp_playfield->border_right += border_right_diff;
12904 if (is_playfield_mode &&
12905 dynamic_playfield_height &&
12906 vp_playfield->height != playfield_height_corrected)
12908 int playfield_ydiff = vp_playfield->height - playfield_height_corrected;
12910 vp_playfield->height = playfield_height_corrected;
12912 if (vp_playfield->valign == VALIGN_TOP)
12914 vp_playfield->border_top += playfield_ydiff;
12916 else if (vp_playfield->align == VALIGN_BOTTOM)
12918 vp_playfield->border_right += playfield_ydiff;
12920 else if (vp_playfield->align == VALIGN_MIDDLE)
12922 int border_top_diff = playfield_ydiff / 2;
12923 int border_bottom_diff = playfield_ydiff - border_top_diff;
12925 vp_playfield->border_top += border_top_diff;
12926 vp_playfield->border_bottom += border_bottom_diff;
12930 // adjust door positions according to specified alignment
12932 for (j = 0; j < 2; j++)
12934 struct RectWithBorder *vp_door = (j == 0 ? vp_door_1 : vp_door_2);
12936 if (vp_door->align == ALIGN_LEFT || vp_door->x > 0)
12937 vp_door->x = ALIGNED_VP_XPOS(vp_door);
12938 else if (vp_door->align == ALIGN_CENTER)
12939 vp_door->x = vp_window->width / 2 - vp_door->width / 2;
12940 else if (vp_door->align == ALIGN_RIGHT)
12941 vp_door->x += vp_window->width - vp_door->width;
12943 if (vp_door->valign == VALIGN_TOP || vp_door->y > 0)
12944 vp_door->y = ALIGNED_VP_YPOS(vp_door);
12945 else if (vp_door->valign == VALIGN_MIDDLE)
12946 vp_door->y = vp_window->height / 2 - vp_door->height / 2;
12947 else if (vp_door->valign == VALIGN_BOTTOM)
12948 vp_door->y += vp_window->height - vp_door->height;
12953 static void InitMenuDesignSettings_SpecialPostProcessing_AfterGraphics(void)
12957 struct XYTileSize *dst, *src;
12960 editor_buttons_xy[] =
12963 &editor.button.element_left, &editor.palette.element_left,
12964 IMG_GFX_EDITOR_BUTTON_ELEMENT_LEFT
12967 &editor.button.element_middle, &editor.palette.element_middle,
12968 IMG_GFX_EDITOR_BUTTON_ELEMENT_MIDDLE
12971 &editor.button.element_right, &editor.palette.element_right,
12972 IMG_GFX_EDITOR_BUTTON_ELEMENT_RIGHT
12979 // set default position for element buttons to element graphics
12980 for (i = 0; editor_buttons_xy[i].dst != NULL; i++)
12982 if ((*editor_buttons_xy[i].dst).x == -1 &&
12983 (*editor_buttons_xy[i].dst).y == -1)
12985 struct GraphicInfo *gd = &graphic_info[editor_buttons_xy[i].graphic];
12987 gd->width = gd->height = editor_buttons_xy[i].src->tile_size;
12989 *editor_buttons_xy[i].dst = *editor_buttons_xy[i].src;
12993 // adjust editor palette rows and columns if specified to be dynamic
12995 if (editor.palette.cols == -1)
12997 int vp_width = viewport.playfield[GFX_SPECIAL_ARG_EDITOR].width;
12998 int bt_width = graphic_info[IMG_EDITOR_PALETTE_BUTTON].width;
12999 int sc_width = graphic_info[IMG_EDITOR_PALETTE_SCROLLBAR].width;
13001 editor.palette.cols = (vp_width - sc_width) / bt_width;
13003 if (editor.palette.x == -1)
13005 int palette_width = editor.palette.cols * bt_width + sc_width;
13007 editor.palette.x = (vp_width - palette_width) / 2;
13011 if (editor.palette.rows == -1)
13013 int vp_height = viewport.playfield[GFX_SPECIAL_ARG_EDITOR].height;
13014 int bt_height = graphic_info[IMG_EDITOR_PALETTE_BUTTON].height;
13015 int tx_height = getFontHeight(FONT_TEXT_2);
13017 editor.palette.rows = (vp_height - tx_height) / bt_height;
13019 if (editor.palette.y == -1)
13021 int palette_height = editor.palette.rows * bt_height + tx_height;
13023 editor.palette.y = (vp_height - palette_height) / 2;
13028 static void InitMenuDesignSettings_PreviewPlayers_Ext(SetupFileHash *hash,
13029 boolean initialize)
13031 // special case: check if network and preview player positions are redefined,
13032 // to compare this later against the main menu level preview being redefined
13033 struct TokenIntPtrInfo menu_config_players[] =
13035 { "main.network_players.x", &menu.main.network_players.redefined },
13036 { "main.network_players.y", &menu.main.network_players.redefined },
13037 { "main.preview_players.x", &menu.main.preview_players.redefined },
13038 { "main.preview_players.y", &menu.main.preview_players.redefined },
13039 { "preview.x", &preview.redefined },
13040 { "preview.y", &preview.redefined }
13046 for (i = 0; i < ARRAY_SIZE(menu_config_players); i++)
13047 *menu_config_players[i].value = FALSE;
13051 for (i = 0; i < ARRAY_SIZE(menu_config_players); i++)
13052 if (getHashEntry(hash, menu_config_players[i].token) != NULL)
13053 *menu_config_players[i].value = TRUE;
13057 static void InitMenuDesignSettings_PreviewPlayers(void)
13059 InitMenuDesignSettings_PreviewPlayers_Ext(NULL, TRUE);
13062 static void InitMenuDesignSettings_PreviewPlayers_FromHash(SetupFileHash *hash)
13064 InitMenuDesignSettings_PreviewPlayers_Ext(hash, FALSE);
13067 static void LoadMenuDesignSettingsFromFilename(char *filename)
13069 static struct TitleFadingInfo tfi;
13070 static struct TitleMessageInfo tmi;
13071 static struct TokenInfo title_tokens[] =
13073 { TYPE_INTEGER, &tfi.fade_mode, ".fade_mode" },
13074 { TYPE_INTEGER, &tfi.fade_delay, ".fade_delay" },
13075 { TYPE_INTEGER, &tfi.post_delay, ".post_delay" },
13076 { TYPE_INTEGER, &tfi.auto_delay, ".auto_delay" },
13077 { TYPE_INTEGER, &tfi.auto_delay_unit, ".auto_delay_unit" },
13081 static struct TokenInfo titlemessage_tokens[] =
13083 { TYPE_INTEGER, &tmi.x, ".x" },
13084 { TYPE_INTEGER, &tmi.y, ".y" },
13085 { TYPE_INTEGER, &tmi.width, ".width" },
13086 { TYPE_INTEGER, &tmi.height, ".height" },
13087 { TYPE_INTEGER, &tmi.chars, ".chars" },
13088 { TYPE_INTEGER, &tmi.lines, ".lines" },
13089 { TYPE_INTEGER, &tmi.align, ".align" },
13090 { TYPE_INTEGER, &tmi.valign, ".valign" },
13091 { TYPE_INTEGER, &tmi.font, ".font" },
13092 { TYPE_BOOLEAN, &tmi.autowrap, ".autowrap" },
13093 { TYPE_BOOLEAN, &tmi.centered, ".centered" },
13094 { TYPE_BOOLEAN, &tmi.parse_comments, ".parse_comments" },
13095 { TYPE_INTEGER, &tmi.sort_priority, ".sort_priority" },
13096 { TYPE_INTEGER, &tmi.fade_mode, ".fade_mode" },
13097 { TYPE_INTEGER, &tmi.fade_delay, ".fade_delay" },
13098 { TYPE_INTEGER, &tmi.post_delay, ".post_delay" },
13099 { TYPE_INTEGER, &tmi.auto_delay, ".auto_delay" },
13100 { TYPE_INTEGER, &tmi.auto_delay_unit, ".auto_delay_unit" },
13106 struct TitleFadingInfo *info;
13111 // initialize first titles from "enter screen" definitions, if defined
13112 { &title_initial_first_default, "menu.enter_screen.TITLE" },
13113 { &title_first_default, "menu.enter_screen.TITLE" },
13115 // initialize title screens from "next screen" definitions, if defined
13116 { &title_initial_default, "menu.next_screen.TITLE" },
13117 { &title_default, "menu.next_screen.TITLE" },
13123 struct TitleMessageInfo *array;
13126 titlemessage_arrays[] =
13128 // initialize first titles from "enter screen" definitions, if defined
13129 { titlescreen_initial_first, "menu.enter_screen.TITLE" },
13130 { titlescreen_first, "menu.enter_screen.TITLE" },
13131 { titlemessage_initial_first, "menu.enter_screen.TITLE" },
13132 { titlemessage_first, "menu.enter_screen.TITLE" },
13134 // initialize titles from "next screen" definitions, if defined
13135 { titlescreen_initial, "menu.next_screen.TITLE" },
13136 { titlescreen, "menu.next_screen.TITLE" },
13137 { titlemessage_initial, "menu.next_screen.TITLE" },
13138 { titlemessage, "menu.next_screen.TITLE" },
13140 // overwrite titles with title definitions, if defined
13141 { titlescreen_initial_first, "[title_initial]" },
13142 { titlescreen_first, "[title]" },
13143 { titlemessage_initial_first, "[title_initial]" },
13144 { titlemessage_first, "[title]" },
13146 { titlescreen_initial, "[title_initial]" },
13147 { titlescreen, "[title]" },
13148 { titlemessage_initial, "[title_initial]" },
13149 { titlemessage, "[title]" },
13151 // overwrite titles with title screen/message definitions, if defined
13152 { titlescreen_initial_first, "[titlescreen_initial]" },
13153 { titlescreen_first, "[titlescreen]" },
13154 { titlemessage_initial_first, "[titlemessage_initial]" },
13155 { titlemessage_first, "[titlemessage]" },
13157 { titlescreen_initial, "[titlescreen_initial]" },
13158 { titlescreen, "[titlescreen]" },
13159 { titlemessage_initial, "[titlemessage_initial]" },
13160 { titlemessage, "[titlemessage]" },
13164 SetupFileHash *setup_file_hash;
13167 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
13170 // the following initializes hierarchical values from dynamic configuration
13172 // special case: initialize with default values that may be overwritten
13173 // (e.g., init "menu.draw_xoffset.INFO" from "menu.draw_xoffset")
13174 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
13176 struct TokenIntPtrInfo menu_config[] =
13178 { "menu.draw_xoffset", &menu.draw_xoffset[i] },
13179 { "menu.draw_yoffset", &menu.draw_yoffset[i] },
13180 { "menu.list_size", &menu.list_size[i] }
13183 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
13185 char *token = menu_config[j].token;
13186 char *value = getHashEntry(setup_file_hash, token);
13189 *menu_config[j].value = get_integer_from_string(value);
13193 // special case: initialize with default values that may be overwritten
13194 // (eg, init "menu.draw_xoffset.INFO[XXX]" from "menu.draw_xoffset.INFO")
13195 for (i = 0; i < NUM_SPECIAL_GFX_INFO_ARGS; i++)
13197 struct TokenIntPtrInfo menu_config[] =
13199 { "menu.draw_xoffset.INFO", &menu.draw_xoffset_info[i] },
13200 { "menu.draw_yoffset.INFO", &menu.draw_yoffset_info[i] },
13201 { "menu.list_size.INFO", &menu.list_size_info[i] },
13202 { "menu.list_entry_size.INFO", &menu.list_entry_size_info[i] },
13203 { "menu.tile_size.INFO", &menu.tile_size_info[i] }
13206 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
13208 char *token = menu_config[j].token;
13209 char *value = getHashEntry(setup_file_hash, token);
13212 *menu_config[j].value = get_integer_from_string(value);
13216 // special case: initialize with default values that may be overwritten
13217 // (eg, init "menu.draw_xoffset.SETUP[XXX]" from "menu.draw_xoffset.SETUP")
13218 for (i = 0; i < NUM_SPECIAL_GFX_SETUP_ARGS; i++)
13220 struct TokenIntPtrInfo menu_config[] =
13222 { "menu.draw_xoffset.SETUP", &menu.draw_xoffset_setup[i] },
13223 { "menu.draw_yoffset.SETUP", &menu.draw_yoffset_setup[i] }
13226 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
13228 char *token = menu_config[j].token;
13229 char *value = getHashEntry(setup_file_hash, token);
13232 *menu_config[j].value = get_integer_from_string(value);
13236 // special case: initialize with default values that may be overwritten
13237 // (eg, init "menu.line_spacing.INFO[XXX]" from "menu.line_spacing.INFO")
13238 for (i = 0; i < NUM_SPECIAL_GFX_INFO_ARGS; i++)
13240 struct TokenIntPtrInfo menu_config[] =
13242 { "menu.left_spacing.INFO", &menu.left_spacing_info[i] },
13243 { "menu.middle_spacing.INFO", &menu.middle_spacing_info[i] },
13244 { "menu.right_spacing.INFO", &menu.right_spacing_info[i] },
13245 { "menu.top_spacing.INFO", &menu.top_spacing_info[i] },
13246 { "menu.bottom_spacing.INFO", &menu.bottom_spacing_info[i] },
13247 { "menu.paragraph_spacing.INFO", &menu.paragraph_spacing_info[i] },
13248 { "menu.headline1_spacing.INFO", &menu.headline1_spacing_info[i] },
13249 { "menu.headline2_spacing.INFO", &menu.headline2_spacing_info[i] },
13250 { "menu.line_spacing.INFO", &menu.line_spacing_info[i] },
13251 { "menu.extra_spacing.INFO", &menu.extra_spacing_info[i] },
13254 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
13256 char *token = menu_config[j].token;
13257 char *value = getHashEntry(setup_file_hash, token);
13260 *menu_config[j].value = get_integer_from_string(value);
13264 // special case: initialize with default values that may be overwritten
13265 // (eg, init "menu.enter_screen.SCORES.xyz" from "menu.enter_screen.xyz")
13266 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
13268 struct TokenIntPtrInfo menu_config[] =
13270 { "menu.enter_screen.fade_mode", &menu.enter_screen[i].fade_mode },
13271 { "menu.enter_screen.fade_delay", &menu.enter_screen[i].fade_delay },
13272 { "menu.enter_screen.post_delay", &menu.enter_screen[i].post_delay },
13273 { "menu.leave_screen.fade_mode", &menu.leave_screen[i].fade_mode },
13274 { "menu.leave_screen.fade_delay", &menu.leave_screen[i].fade_delay },
13275 { "menu.leave_screen.post_delay", &menu.leave_screen[i].post_delay },
13276 { "menu.next_screen.fade_mode", &menu.next_screen[i].fade_mode },
13277 { "menu.next_screen.fade_delay", &menu.next_screen[i].fade_delay },
13278 { "menu.next_screen.post_delay", &menu.next_screen[i].post_delay }
13281 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
13283 char *token = menu_config[j].token;
13284 char *value = getHashEntry(setup_file_hash, token);
13287 *menu_config[j].value = get_token_parameter_value(token, value);
13291 // special case: initialize with default values that may be overwritten
13292 // (eg, init "viewport.door_1.MAIN.xyz" from "viewport.door_1.xyz")
13293 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
13297 char *token_prefix;
13298 struct RectWithBorder *struct_ptr;
13302 { "viewport.window", &viewport.window[i] },
13303 { "viewport.playfield", &viewport.playfield[i] },
13304 { "viewport.door_1", &viewport.door_1[i] },
13305 { "viewport.door_2", &viewport.door_2[i] }
13308 for (j = 0; j < ARRAY_SIZE(vp_struct); j++)
13310 struct TokenIntPtrInfo vp_config[] =
13312 { ".x", &vp_struct[j].struct_ptr->x },
13313 { ".y", &vp_struct[j].struct_ptr->y },
13314 { ".width", &vp_struct[j].struct_ptr->width },
13315 { ".height", &vp_struct[j].struct_ptr->height },
13316 { ".min_width", &vp_struct[j].struct_ptr->min_width },
13317 { ".min_height", &vp_struct[j].struct_ptr->min_height },
13318 { ".max_width", &vp_struct[j].struct_ptr->max_width },
13319 { ".max_height", &vp_struct[j].struct_ptr->max_height },
13320 { ".margin_left", &vp_struct[j].struct_ptr->margin_left },
13321 { ".margin_right", &vp_struct[j].struct_ptr->margin_right },
13322 { ".margin_top", &vp_struct[j].struct_ptr->margin_top },
13323 { ".margin_bottom", &vp_struct[j].struct_ptr->margin_bottom },
13324 { ".border_left", &vp_struct[j].struct_ptr->border_left },
13325 { ".border_right", &vp_struct[j].struct_ptr->border_right },
13326 { ".border_top", &vp_struct[j].struct_ptr->border_top },
13327 { ".border_bottom", &vp_struct[j].struct_ptr->border_bottom },
13328 { ".border_size", &vp_struct[j].struct_ptr->border_size },
13329 { ".align_size", &vp_struct[j].struct_ptr->align_size },
13330 { ".align", &vp_struct[j].struct_ptr->align },
13331 { ".valign", &vp_struct[j].struct_ptr->valign }
13334 for (k = 0; k < ARRAY_SIZE(vp_config); k++)
13336 char *token = getStringCat2(vp_struct[j].token_prefix,
13337 vp_config[k].token);
13338 char *value = getHashEntry(setup_file_hash, token);
13341 *vp_config[k].value = get_token_parameter_value(token, value);
13348 // special case: initialize with default values that may be overwritten
13349 // (e.g., init "[title].fade_mode" from "menu.next_screen.TITLE.fade_mode")
13350 for (i = 0; title_info[i].info != NULL; i++)
13352 struct TitleFadingInfo *info = title_info[i].info;
13353 char *base_token = title_info[i].text;
13355 for (j = 0; title_tokens[j].type != -1; j++)
13357 char *token = getStringCat2(base_token, title_tokens[j].text);
13358 char *value = getHashEntry(setup_file_hash, token);
13362 int parameter_value = get_token_parameter_value(token, value);
13366 *(int *)title_tokens[j].value = (int)parameter_value;
13375 // special case: initialize with default values that may be overwritten
13376 // (e.g., init "titlemessage_1.fade_mode" from "[titlemessage].fade_mode")
13377 for (i = 0; titlemessage_arrays[i].array != NULL; i++)
13379 struct TitleMessageInfo *array = titlemessage_arrays[i].array;
13380 char *base_token = titlemessage_arrays[i].text;
13382 for (j = 0; titlemessage_tokens[j].type != -1; j++)
13384 char *token = getStringCat2(base_token, titlemessage_tokens[j].text);
13385 char *value = getHashEntry(setup_file_hash, token);
13389 int parameter_value = get_token_parameter_value(token, value);
13391 for (k = 0; k < MAX_NUM_TITLE_MESSAGES; k++)
13395 if (titlemessage_tokens[j].type == TYPE_INTEGER)
13396 *(int *)titlemessage_tokens[j].value = (int)parameter_value;
13398 *(boolean *)titlemessage_tokens[j].value = (boolean)parameter_value;
13408 // read (and overwrite with) values that may be specified in config file
13409 InitMenuDesignSettings_FromHash(setup_file_hash, TRUE);
13411 // special case: check if network and preview player positions are redefined
13412 InitMenuDesignSettings_PreviewPlayers_FromHash(setup_file_hash);
13414 freeSetupFileHash(setup_file_hash);
13417 void LoadMenuDesignSettings(void)
13419 char *filename_base = UNDEFINED_FILENAME, *filename_local;
13421 InitMenuDesignSettings_Static();
13422 InitMenuDesignSettings_SpecialPreProcessing();
13423 InitMenuDesignSettings_PreviewPlayers();
13425 if (!GFX_OVERRIDE_ARTWORK(ARTWORK_TYPE_GRAPHICS))
13427 // first look for special settings configured in level series config
13428 filename_base = getCustomArtworkLevelConfigFilename(ARTWORK_TYPE_GRAPHICS);
13430 if (fileExists(filename_base))
13431 LoadMenuDesignSettingsFromFilename(filename_base);
13434 filename_local = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
13436 if (filename_local != NULL && !strEqual(filename_base, filename_local))
13437 LoadMenuDesignSettingsFromFilename(filename_local);
13439 InitMenuDesignSettings_SpecialPostProcessing();
13442 void LoadMenuDesignSettings_AfterGraphics(void)
13444 InitMenuDesignSettings_SpecialPostProcessing_AfterGraphics();
13447 void InitSoundSettings_FromHash(SetupFileHash *setup_file_hash,
13448 boolean ignore_defaults)
13452 for (i = 0; sound_config_vars[i].token != NULL; i++)
13454 char *value = getHashEntry(setup_file_hash, sound_config_vars[i].token);
13456 // (ignore definitions set to "[DEFAULT]" which are already initialized)
13457 if (ignore_defaults && strEqual(value, ARG_DEFAULT))
13461 *sound_config_vars[i].value =
13462 get_token_parameter_value(sound_config_vars[i].token, value);
13466 void InitSoundSettings_Static(void)
13468 // always start with reliable default values from static default config
13469 InitSoundSettings_FromHash(sound_config_hash, FALSE);
13472 static void LoadSoundSettingsFromFilename(char *filename)
13474 SetupFileHash *setup_file_hash;
13476 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
13479 // read (and overwrite with) values that may be specified in config file
13480 InitSoundSettings_FromHash(setup_file_hash, TRUE);
13482 freeSetupFileHash(setup_file_hash);
13485 void LoadSoundSettings(void)
13487 char *filename_base = UNDEFINED_FILENAME, *filename_local;
13489 InitSoundSettings_Static();
13491 if (!GFX_OVERRIDE_ARTWORK(ARTWORK_TYPE_SOUNDS))
13493 // first look for special settings configured in level series config
13494 filename_base = getCustomArtworkLevelConfigFilename(ARTWORK_TYPE_SOUNDS);
13496 if (fileExists(filename_base))
13497 LoadSoundSettingsFromFilename(filename_base);
13500 filename_local = getCustomArtworkConfigFilename(ARTWORK_TYPE_SOUNDS);
13502 if (filename_local != NULL && !strEqual(filename_base, filename_local))
13503 LoadSoundSettingsFromFilename(filename_local);
13506 void LoadUserDefinedEditorElementList(int **elements, int *num_elements)
13508 char *filename = getEditorSetupFilename();
13509 SetupFileList *setup_file_list, *list;
13510 SetupFileHash *element_hash;
13511 int num_unknown_tokens = 0;
13514 if ((setup_file_list = loadSetupFileList(filename)) == NULL)
13517 element_hash = newSetupFileHash();
13519 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
13520 setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
13522 // determined size may be larger than needed (due to unknown elements)
13524 for (list = setup_file_list; list != NULL; list = list->next)
13527 // add space for up to 3 more elements for padding that may be needed
13528 *num_elements += 3;
13530 // free memory for old list of elements, if needed
13531 checked_free(*elements);
13533 // allocate memory for new list of elements
13534 *elements = checked_malloc(*num_elements * sizeof(int));
13537 for (list = setup_file_list; list != NULL; list = list->next)
13539 char *value = getHashEntry(element_hash, list->token);
13541 if (value == NULL) // try to find obsolete token mapping
13543 char *mapped_token = get_mapped_token(list->token);
13545 if (mapped_token != NULL)
13547 value = getHashEntry(element_hash, mapped_token);
13549 free(mapped_token);
13555 (*elements)[(*num_elements)++] = atoi(value);
13559 if (num_unknown_tokens == 0)
13562 Warn("unknown token(s) found in config file:");
13563 Warn("- config file: '%s'", filename);
13565 num_unknown_tokens++;
13568 Warn("- token: '%s'", list->token);
13572 if (num_unknown_tokens > 0)
13575 while (*num_elements % 4) // pad with empty elements, if needed
13576 (*elements)[(*num_elements)++] = EL_EMPTY;
13578 freeSetupFileList(setup_file_list);
13579 freeSetupFileHash(element_hash);
13582 for (i = 0; i < *num_elements; i++)
13583 Debug("editor", "element '%s' [%d]\n",
13584 element_info[(*elements)[i]].token_name, (*elements)[i]);
13588 static struct MusicFileInfo *get_music_file_info_ext(char *basename, int music,
13591 SetupFileHash *setup_file_hash = NULL;
13592 struct MusicFileInfo tmp_music_file_info, *new_music_file_info;
13593 char *filename_music, *filename_prefix, *filename_info;
13599 token_to_value_ptr[] =
13601 { "title_header", &tmp_music_file_info.title_header },
13602 { "artist_header", &tmp_music_file_info.artist_header },
13603 { "album_header", &tmp_music_file_info.album_header },
13604 { "year_header", &tmp_music_file_info.year_header },
13605 { "played_header", &tmp_music_file_info.played_header },
13607 { "title", &tmp_music_file_info.title },
13608 { "artist", &tmp_music_file_info.artist },
13609 { "album", &tmp_music_file_info.album },
13610 { "year", &tmp_music_file_info.year },
13611 { "played", &tmp_music_file_info.played },
13617 filename_music = (is_sound ? getCustomSoundFilename(basename) :
13618 getCustomMusicFilename(basename));
13620 if (filename_music == NULL)
13623 // ---------- try to replace file extension ----------
13625 filename_prefix = getStringCopy(filename_music);
13626 if (strrchr(filename_prefix, '.') != NULL)
13627 *strrchr(filename_prefix, '.') = '\0';
13628 filename_info = getStringCat2(filename_prefix, ".txt");
13630 if (fileExists(filename_info))
13631 setup_file_hash = loadSetupFileHash(filename_info);
13633 free(filename_prefix);
13634 free(filename_info);
13636 if (setup_file_hash == NULL)
13638 // ---------- try to add file extension ----------
13640 filename_prefix = getStringCopy(filename_music);
13641 filename_info = getStringCat2(filename_prefix, ".txt");
13643 if (fileExists(filename_info))
13644 setup_file_hash = loadSetupFileHash(filename_info);
13646 free(filename_prefix);
13647 free(filename_info);
13650 if (setup_file_hash == NULL)
13653 // ---------- music file info found ----------
13655 clear_mem(&tmp_music_file_info, sizeof(struct MusicFileInfo));
13657 for (i = 0; token_to_value_ptr[i].token != NULL; i++)
13659 char *value = getHashEntry(setup_file_hash, token_to_value_ptr[i].token);
13661 *token_to_value_ptr[i].value_ptr =
13662 getStringCopy(value != NULL && *value != '\0' ? value : UNKNOWN_NAME);
13665 tmp_music_file_info.basename = getStringCopy(basename);
13666 tmp_music_file_info.music = music;
13667 tmp_music_file_info.is_sound = is_sound;
13669 new_music_file_info = checked_malloc(sizeof(struct MusicFileInfo));
13670 *new_music_file_info = tmp_music_file_info;
13672 return new_music_file_info;
13675 static struct MusicFileInfo *get_music_file_info(char *basename, int music)
13677 return get_music_file_info_ext(basename, music, FALSE);
13680 static struct MusicFileInfo *get_sound_file_info(char *basename, int sound)
13682 return get_music_file_info_ext(basename, sound, TRUE);
13685 static boolean music_info_listed_ext(struct MusicFileInfo *list,
13686 char *basename, boolean is_sound)
13688 for (; list != NULL; list = list->next)
13689 if (list->is_sound == is_sound && strEqual(list->basename, basename))
13695 static boolean music_info_listed(struct MusicFileInfo *list, char *basename)
13697 return music_info_listed_ext(list, basename, FALSE);
13700 static boolean sound_info_listed(struct MusicFileInfo *list, char *basename)
13702 return music_info_listed_ext(list, basename, TRUE);
13705 void LoadMusicInfo(void)
13707 int num_music_noconf = getMusicListSize_NoConf();
13708 int num_music = getMusicListSize();
13709 int num_sounds = getSoundListSize();
13710 struct FileInfo *music, *sound;
13711 struct MusicFileInfo *next, **new;
13715 while (music_file_info != NULL)
13717 next = music_file_info->next;
13719 checked_free(music_file_info->basename);
13721 checked_free(music_file_info->title_header);
13722 checked_free(music_file_info->artist_header);
13723 checked_free(music_file_info->album_header);
13724 checked_free(music_file_info->year_header);
13725 checked_free(music_file_info->played_header);
13727 checked_free(music_file_info->title);
13728 checked_free(music_file_info->artist);
13729 checked_free(music_file_info->album);
13730 checked_free(music_file_info->year);
13731 checked_free(music_file_info->played);
13733 free(music_file_info);
13735 music_file_info = next;
13738 new = &music_file_info;
13740 // get (configured or unconfigured) music file info for all levels
13741 for (i = leveldir_current->first_level;
13742 i <= leveldir_current->last_level; i++)
13746 if (levelset.music[i] != MUS_UNDEFINED)
13748 // get music file info for configured level music
13749 music_nr = levelset.music[i];
13751 else if (num_music_noconf > 0)
13753 // get music file info for unconfigured level music
13754 int level_pos = i - leveldir_current->first_level;
13756 music_nr = MAP_NOCONF_MUSIC(level_pos % num_music_noconf);
13763 char *basename = getMusicInfoEntryFilename(music_nr);
13765 if (basename == NULL)
13768 if (!music_info_listed(music_file_info, basename))
13770 *new = get_music_file_info(basename, music_nr);
13773 new = &(*new)->next;
13777 // get music file info for all remaining configured music files
13778 for (i = 0; i < num_music; i++)
13780 music = getMusicListEntry(i);
13782 if (music->filename == NULL)
13785 if (strEqual(music->filename, UNDEFINED_FILENAME))
13788 // a configured file may be not recognized as music
13789 if (!FileIsMusic(music->filename))
13792 if (!music_info_listed(music_file_info, music->filename))
13794 *new = get_music_file_info(music->filename, i);
13797 new = &(*new)->next;
13801 // get sound file info for all configured sound files
13802 for (i = 0; i < num_sounds; i++)
13804 sound = getSoundListEntry(i);
13806 if (sound->filename == NULL)
13809 if (strEqual(sound->filename, UNDEFINED_FILENAME))
13812 // a configured file may be not recognized as sound
13813 if (!FileIsSound(sound->filename))
13816 if (!sound_info_listed(music_file_info, sound->filename))
13818 *new = get_sound_file_info(sound->filename, i);
13820 new = &(*new)->next;
13824 // add pointers to previous list nodes
13826 struct MusicFileInfo *node = music_file_info;
13828 while (node != NULL)
13831 node->next->prev = node;
13837 static void add_helpanim_entry(int element, int action, int direction,
13838 int delay, int *num_list_entries)
13840 struct HelpAnimInfo *new_list_entry;
13841 (*num_list_entries)++;
13844 checked_realloc(helpanim_info,
13845 *num_list_entries * sizeof(struct HelpAnimInfo));
13846 new_list_entry = &helpanim_info[*num_list_entries - 1];
13848 new_list_entry->element = element;
13849 new_list_entry->action = action;
13850 new_list_entry->direction = direction;
13851 new_list_entry->delay = delay;
13854 static void print_unknown_token(char *filename, char *token, int token_nr)
13859 Warn("unknown token(s) found in config file:");
13860 Warn("- config file: '%s'", filename);
13863 Warn("- token: '%s'", token);
13866 static void print_unknown_token_end(int token_nr)
13872 void LoadHelpAnimInfo(void)
13874 char *filename = getHelpAnimFilename();
13875 SetupFileList *setup_file_list = NULL, *list;
13876 SetupFileHash *element_hash, *action_hash, *direction_hash;
13877 int num_list_entries = 0;
13878 int num_unknown_tokens = 0;
13881 if (fileExists(filename))
13882 setup_file_list = loadSetupFileList(filename);
13884 if (setup_file_list == NULL)
13886 // use reliable default values from static configuration
13887 SetupFileList *insert_ptr;
13889 insert_ptr = setup_file_list =
13890 newSetupFileList(helpanim_config[0].token,
13891 helpanim_config[0].value);
13893 for (i = 1; helpanim_config[i].token; i++)
13894 insert_ptr = addListEntry(insert_ptr,
13895 helpanim_config[i].token,
13896 helpanim_config[i].value);
13899 element_hash = newSetupFileHash();
13900 action_hash = newSetupFileHash();
13901 direction_hash = newSetupFileHash();
13903 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
13904 setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
13906 for (i = 0; i < NUM_ACTIONS; i++)
13907 setHashEntry(action_hash, element_action_info[i].suffix,
13908 i_to_a(element_action_info[i].value));
13910 // do not store direction index (bit) here, but direction value!
13911 for (i = 0; i < NUM_DIRECTIONS_FULL; i++)
13912 setHashEntry(direction_hash, element_direction_info[i].suffix,
13913 i_to_a(1 << element_direction_info[i].value));
13915 for (list = setup_file_list; list != NULL; list = list->next)
13917 char *element_token, *action_token, *direction_token;
13918 char *element_value, *action_value, *direction_value;
13919 int delay = atoi(list->value);
13921 if (strEqual(list->token, "end"))
13923 add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
13928 /* first try to break element into element/action/direction parts;
13929 if this does not work, also accept combined "element[.act][.dir]"
13930 elements (like "dynamite.active"), which are unique elements */
13932 if (strchr(list->token, '.') == NULL) // token contains no '.'
13934 element_value = getHashEntry(element_hash, list->token);
13935 if (element_value != NULL) // element found
13936 add_helpanim_entry(atoi(element_value), -1, -1, delay,
13937 &num_list_entries);
13940 // no further suffixes found -- this is not an element
13941 print_unknown_token(filename, list->token, num_unknown_tokens++);
13947 // token has format "<prefix>.<something>"
13949 action_token = strchr(list->token, '.'); // suffix may be action ...
13950 direction_token = action_token; // ... or direction
13952 element_token = getStringCopy(list->token);
13953 *strchr(element_token, '.') = '\0';
13955 element_value = getHashEntry(element_hash, element_token);
13957 if (element_value == NULL) // this is no element
13959 element_value = getHashEntry(element_hash, list->token);
13960 if (element_value != NULL) // combined element found
13961 add_helpanim_entry(atoi(element_value), -1, -1, delay,
13962 &num_list_entries);
13964 print_unknown_token(filename, list->token, num_unknown_tokens++);
13966 free(element_token);
13971 action_value = getHashEntry(action_hash, action_token);
13973 if (action_value != NULL) // action found
13975 add_helpanim_entry(atoi(element_value), atoi(action_value), -1, delay,
13976 &num_list_entries);
13978 free(element_token);
13983 direction_value = getHashEntry(direction_hash, direction_token);
13985 if (direction_value != NULL) // direction found
13987 add_helpanim_entry(atoi(element_value), -1, atoi(direction_value), delay,
13988 &num_list_entries);
13990 free(element_token);
13995 if (strchr(action_token + 1, '.') == NULL)
13997 // no further suffixes found -- this is not an action nor direction
13999 element_value = getHashEntry(element_hash, list->token);
14000 if (element_value != NULL) // combined element found
14001 add_helpanim_entry(atoi(element_value), -1, -1, delay,
14002 &num_list_entries);
14004 print_unknown_token(filename, list->token, num_unknown_tokens++);
14006 free(element_token);
14011 // token has format "<prefix>.<suffix>.<something>"
14013 direction_token = strchr(action_token + 1, '.');
14015 action_token = getStringCopy(action_token);
14016 *strchr(action_token + 1, '.') = '\0';
14018 action_value = getHashEntry(action_hash, action_token);
14020 if (action_value == NULL) // this is no action
14022 element_value = getHashEntry(element_hash, list->token);
14023 if (element_value != NULL) // combined element found
14024 add_helpanim_entry(atoi(element_value), -1, -1, delay,
14025 &num_list_entries);
14027 print_unknown_token(filename, list->token, num_unknown_tokens++);
14029 free(element_token);
14030 free(action_token);
14035 direction_value = getHashEntry(direction_hash, direction_token);
14037 if (direction_value != NULL) // direction found
14039 add_helpanim_entry(atoi(element_value), atoi(action_value),
14040 atoi(direction_value), delay, &num_list_entries);
14042 free(element_token);
14043 free(action_token);
14048 // this is no direction
14050 element_value = getHashEntry(element_hash, list->token);
14051 if (element_value != NULL) // combined element found
14052 add_helpanim_entry(atoi(element_value), -1, -1, delay,
14053 &num_list_entries);
14055 print_unknown_token(filename, list->token, num_unknown_tokens++);
14057 free(element_token);
14058 free(action_token);
14061 print_unknown_token_end(num_unknown_tokens);
14063 add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
14064 add_helpanim_entry(HELPANIM_LIST_END, -1, -1, -1, &num_list_entries);
14066 freeSetupFileList(setup_file_list);
14067 freeSetupFileHash(element_hash);
14068 freeSetupFileHash(action_hash);
14069 freeSetupFileHash(direction_hash);
14072 for (i = 0; i < num_list_entries; i++)
14073 Debug("files:LoadHelpAnimInfo", "'%s': %d, %d, %d => %d",
14074 EL_NAME(helpanim_info[i].element),
14075 helpanim_info[i].element,
14076 helpanim_info[i].action,
14077 helpanim_info[i].direction,
14078 helpanim_info[i].delay);
14082 void LoadHelpTextInfo(void)
14084 char *filename = getHelpTextFilename();
14087 if (helptext_info != NULL)
14089 freeSetupFileHash(helptext_info);
14090 helptext_info = NULL;
14093 if (fileExists(filename))
14094 helptext_info = loadSetupFileHash(filename);
14096 if (helptext_info == NULL)
14098 // use reliable default values from static configuration
14099 helptext_info = newSetupFileHash();
14101 for (i = 0; helptext_config[i].token; i++)
14102 setHashEntry(helptext_info,
14103 helptext_config[i].token,
14104 helptext_config[i].value);
14108 BEGIN_HASH_ITERATION(helptext_info, itr)
14110 Debug("files:LoadHelpTextInfo", "'%s' => '%s'",
14111 HASH_ITERATION_TOKEN(itr), HASH_ITERATION_VALUE(itr));
14113 END_HASH_ITERATION(hash, itr)
14118 // ----------------------------------------------------------------------------
14120 // ----------------------------------------------------------------------------
14122 #define MAX_NUM_CONVERT_LEVELS 1000
14124 void ConvertLevels(void)
14126 static LevelDirTree *convert_leveldir = NULL;
14127 static int convert_level_nr = -1;
14128 static int num_levels_handled = 0;
14129 static int num_levels_converted = 0;
14130 static boolean levels_failed[MAX_NUM_CONVERT_LEVELS];
14133 convert_leveldir = getTreeInfoFromIdentifier(leveldir_first,
14134 global.convert_leveldir);
14136 if (convert_leveldir == NULL)
14137 Fail("no such level identifier: '%s'", global.convert_leveldir);
14139 leveldir_current = convert_leveldir;
14141 if (global.convert_level_nr != -1)
14143 convert_leveldir->first_level = global.convert_level_nr;
14144 convert_leveldir->last_level = global.convert_level_nr;
14147 convert_level_nr = convert_leveldir->first_level;
14149 PrintLine("=", 79);
14150 Print("Converting levels\n");
14151 PrintLine("-", 79);
14152 Print("Level series identifier: '%s'\n", convert_leveldir->identifier);
14153 Print("Level series name: '%s'\n", convert_leveldir->name);
14154 Print("Level series author: '%s'\n", convert_leveldir->author);
14155 Print("Number of levels: %d\n", convert_leveldir->levels);
14156 PrintLine("=", 79);
14159 for (i = 0; i < MAX_NUM_CONVERT_LEVELS; i++)
14160 levels_failed[i] = FALSE;
14162 while (convert_level_nr <= convert_leveldir->last_level)
14164 char *level_filename;
14167 level_nr = convert_level_nr++;
14169 Print("Level %03d: ", level_nr);
14171 LoadLevel(level_nr);
14172 if (level.no_level_file || level.no_valid_file)
14174 Print("(no level)\n");
14178 Print("converting level ... ");
14181 // special case: conversion of some EMC levels as requested by ACME
14182 level.game_engine_type = GAME_ENGINE_TYPE_RND;
14185 level_filename = getDefaultLevelFilename(level_nr);
14186 new_level = !fileExists(level_filename);
14190 SaveLevel(level_nr);
14192 num_levels_converted++;
14194 Print("converted.\n");
14198 if (level_nr >= 0 && level_nr < MAX_NUM_CONVERT_LEVELS)
14199 levels_failed[level_nr] = TRUE;
14201 Print("NOT CONVERTED -- LEVEL ALREADY EXISTS.\n");
14204 num_levels_handled++;
14208 PrintLine("=", 79);
14209 Print("Number of levels handled: %d\n", num_levels_handled);
14210 Print("Number of levels converted: %d (%d%%)\n", num_levels_converted,
14211 (num_levels_handled ?
14212 num_levels_converted * 100 / num_levels_handled : 0));
14213 PrintLine("-", 79);
14214 Print("Summary (for automatic parsing by scripts):\n");
14215 Print("LEVELDIR '%s', CONVERTED %d/%d (%d%%)",
14216 convert_leveldir->identifier, num_levels_converted,
14217 num_levels_handled,
14218 (num_levels_handled ?
14219 num_levels_converted * 100 / num_levels_handled : 0));
14221 if (num_levels_handled != num_levels_converted)
14223 Print(", FAILED:");
14224 for (i = 0; i < MAX_NUM_CONVERT_LEVELS; i++)
14225 if (levels_failed[i])
14230 PrintLine("=", 79);
14232 CloseAllAndExit(0);
14236 // ----------------------------------------------------------------------------
14237 // create and save images for use in level sketches (raw BMP format)
14238 // ----------------------------------------------------------------------------
14240 void CreateLevelSketchImages(void)
14246 InitElementPropertiesGfxElement();
14248 bitmap1 = CreateBitmap(TILEX, TILEY, DEFAULT_DEPTH);
14249 bitmap2 = CreateBitmap(MINI_TILEX, MINI_TILEY, DEFAULT_DEPTH);
14251 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
14253 int element = getMappedElement(i);
14254 char basename1[16];
14255 char basename2[16];
14259 sprintf(basename1, "%04d.bmp", i);
14260 sprintf(basename2, "%04ds.bmp", i);
14262 filename1 = getPath2(global.create_sketch_images_dir, basename1);
14263 filename2 = getPath2(global.create_sketch_images_dir, basename2);
14265 DrawSizedElement(0, 0, element, TILESIZE);
14266 BlitBitmap(drawto, bitmap1, SX, SY, TILEX, TILEY, 0, 0);
14268 if (SDL_SaveBMP(bitmap1->surface, filename1) != 0)
14269 Fail("cannot save level sketch image file '%s'", filename1);
14271 DrawSizedElement(0, 0, element, MINI_TILESIZE);
14272 BlitBitmap(drawto, bitmap2, SX, SY, MINI_TILEX, MINI_TILEY, 0, 0);
14274 if (SDL_SaveBMP(bitmap2->surface, filename2) != 0)
14275 Fail("cannot save level sketch image file '%s'", filename2);
14280 // create corresponding SQL statements (for normal and small images)
14283 printf("insert into phpbb_words values (NULL, '`%03d', '<IMG class=\"levelsketch\" src=\"/I/%04d.png\"/>');\n", i, i);
14284 printf("insert into phpbb_words values (NULL, '¸%03d', '<IMG class=\"levelsketch\" src=\"/I/%04ds.png\"/>');\n", i, i);
14287 printf("insert into phpbb_words values (NULL, '`%04d', '<IMG class=\"levelsketch\" src=\"/I/%04d.png\"/>');\n", i, i);
14288 printf("insert into phpbb_words values (NULL, '¸%04d', '<IMG class=\"levelsketch\" src=\"/I/%04ds.png\"/>');\n", i, i);
14290 // optional: create content for forum level sketch demonstration post
14292 fprintf(stderr, "%03d `%03d%c", i, i, (i % 10 < 9 ? ' ' : '\n'));
14295 FreeBitmap(bitmap1);
14296 FreeBitmap(bitmap2);
14299 fprintf(stderr, "\n");
14301 Info("%d normal and small images created", NUM_FILE_ELEMENTS);
14303 CloseAllAndExit(0);
14307 // ----------------------------------------------------------------------------
14308 // create and save images for element collecting animations (raw BMP format)
14309 // ----------------------------------------------------------------------------
14311 static boolean createCollectImage(int element)
14313 return (IS_COLLECTIBLE(element) && !IS_SP_ELEMENT(element));
14316 void CreateCollectElementImages(void)
14320 int anim_frames = num_steps - 1;
14321 int tile_size = TILESIZE;
14322 int anim_width = tile_size * anim_frames;
14323 int anim_height = tile_size;
14324 int num_collect_images = 0;
14325 int pos_collect_images = 0;
14327 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
14328 if (createCollectImage(i))
14329 num_collect_images++;
14331 Info("Creating %d element collecting animation images ...",
14332 num_collect_images);
14334 int dst_width = anim_width * 2;
14335 int dst_height = anim_height * num_collect_images / 2;
14336 Bitmap *dst_bitmap = CreateBitmap(dst_width, dst_height, DEFAULT_DEPTH);
14337 char *basename_bmp = "RocksCollect.bmp";
14338 char *basename_png = "RocksCollect.png";
14339 char *filename_bmp = getPath2(global.create_collect_images_dir, basename_bmp);
14340 char *filename_png = getPath2(global.create_collect_images_dir, basename_png);
14341 int len_filename_bmp = strlen(filename_bmp);
14342 int len_filename_png = strlen(filename_png);
14343 int max_command_len = MAX_FILENAME_LEN + len_filename_bmp + len_filename_png;
14344 char cmd_convert[max_command_len];
14346 snprintf(cmd_convert, max_command_len, "convert \"%s\" \"%s\"",
14350 // force using RGBA surface for destination bitmap
14351 SDL_SetColorKey(dst_bitmap->surface, SET_TRANSPARENT_PIXEL,
14352 SDL_MapRGB(dst_bitmap->surface->format, 0x00, 0x00, 0x00));
14354 dst_bitmap->surface =
14355 SDL_ConvertSurfaceFormat(dst_bitmap->surface, SDL_PIXELFORMAT_ARGB8888, 0);
14357 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
14359 if (!createCollectImage(i))
14362 int dst_x = (pos_collect_images / (num_collect_images / 2)) * anim_width;
14363 int dst_y = (pos_collect_images % (num_collect_images / 2)) * anim_height;
14364 int graphic = el2img(i);
14365 char *token_name = element_info[i].token_name;
14366 Bitmap *tmp_bitmap = CreateBitmap(tile_size, tile_size, DEFAULT_DEPTH);
14367 Bitmap *src_bitmap;
14370 Info("- creating collecting image for '%s' ...", token_name);
14372 getGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
14374 BlitBitmap(src_bitmap, tmp_bitmap, src_x, src_y,
14375 tile_size, tile_size, 0, 0);
14377 // force using RGBA surface for temporary bitmap (using transparent black)
14378 SDL_SetColorKey(tmp_bitmap->surface, SET_TRANSPARENT_PIXEL,
14379 SDL_MapRGB(tmp_bitmap->surface->format, 0x00, 0x00, 0x00));
14381 tmp_bitmap->surface =
14382 SDL_ConvertSurfaceFormat(tmp_bitmap->surface, SDL_PIXELFORMAT_ARGB8888, 0);
14384 tmp_bitmap->surface_masked = tmp_bitmap->surface;
14386 for (j = 0; j < anim_frames; j++)
14388 int frame_size_final = tile_size * (anim_frames - j) / num_steps;
14389 int frame_size = frame_size_final * num_steps;
14390 int offset = (tile_size - frame_size_final) / 2;
14391 Bitmap *frame_bitmap = ZoomBitmap(tmp_bitmap, frame_size, frame_size);
14393 while (frame_size > frame_size_final)
14397 Bitmap *half_bitmap = ZoomBitmap(frame_bitmap, frame_size, frame_size);
14399 FreeBitmap(frame_bitmap);
14401 frame_bitmap = half_bitmap;
14404 BlitBitmapMasked(frame_bitmap, dst_bitmap, 0, 0,
14405 frame_size_final, frame_size_final,
14406 dst_x + j * tile_size + offset, dst_y + offset);
14408 FreeBitmap(frame_bitmap);
14411 tmp_bitmap->surface_masked = NULL;
14413 FreeBitmap(tmp_bitmap);
14415 pos_collect_images++;
14418 if (SDL_SaveBMP(dst_bitmap->surface, filename_bmp) != 0)
14419 Fail("cannot save element collecting image file '%s'", filename_bmp);
14421 FreeBitmap(dst_bitmap);
14423 Info("Converting image file from BMP to PNG ...");
14425 if (system(cmd_convert) != 0)
14426 Fail("converting image file failed");
14428 unlink(filename_bmp);
14432 CloseAllAndExit(0);
14436 // ----------------------------------------------------------------------------
14437 // create and save images for custom and group elements (raw BMP format)
14438 // ----------------------------------------------------------------------------
14440 void CreateCustomElementImages(char *directory)
14442 char *src_basename = "RocksCE-template.ilbm";
14443 char *dst_basename = "RocksCE.bmp";
14444 char *src_filename = getPath2(directory, src_basename);
14445 char *dst_filename = getPath2(directory, dst_basename);
14446 Bitmap *src_bitmap;
14448 int yoffset_ce = 0;
14449 int yoffset_ge = (TILEY * NUM_CUSTOM_ELEMENTS / 16);
14452 InitVideoDefaults();
14454 ReCreateBitmap(&backbuffer, video.width, video.height);
14456 src_bitmap = LoadImage(src_filename);
14458 bitmap = CreateBitmap(TILEX * 16 * 2,
14459 TILEY * (NUM_CUSTOM_ELEMENTS + NUM_GROUP_ELEMENTS) / 16,
14462 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
14469 BlitBitmap(src_bitmap, bitmap, 0, 0, TILEX, TILEY,
14470 TILEX * x, TILEY * y + yoffset_ce);
14472 BlitBitmap(src_bitmap, bitmap, 0, TILEY,
14474 TILEX * x + TILEX * 16,
14475 TILEY * y + yoffset_ce);
14477 for (j = 2; j >= 0; j--)
14481 BlitBitmap(src_bitmap, bitmap,
14482 TILEX + c * 7, 0, 6, 10,
14483 TILEX * x + 6 + j * 7,
14484 TILEY * y + 11 + yoffset_ce);
14486 BlitBitmap(src_bitmap, bitmap,
14487 TILEX + c * 8, TILEY, 6, 10,
14488 TILEX * 16 + TILEX * x + 6 + j * 8,
14489 TILEY * y + 10 + yoffset_ce);
14495 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
14502 BlitBitmap(src_bitmap, bitmap, 0, 0, TILEX, TILEY,
14503 TILEX * x, TILEY * y + yoffset_ge);
14505 BlitBitmap(src_bitmap, bitmap, 0, TILEY,
14507 TILEX * x + TILEX * 16,
14508 TILEY * y + yoffset_ge);
14510 for (j = 1; j >= 0; j--)
14514 BlitBitmap(src_bitmap, bitmap, TILEX + c * 10, 11, 10, 10,
14515 TILEX * x + 6 + j * 10,
14516 TILEY * y + 11 + yoffset_ge);
14518 BlitBitmap(src_bitmap, bitmap,
14519 TILEX + c * 8, TILEY + 12, 6, 10,
14520 TILEX * 16 + TILEX * x + 10 + j * 8,
14521 TILEY * y + 10 + yoffset_ge);
14527 if (SDL_SaveBMP(bitmap->surface, dst_filename) != 0)
14528 Fail("cannot save CE graphics file '%s'", dst_filename);
14530 FreeBitmap(bitmap);
14532 CloseAllAndExit(0);