1 // ============================================================================
2 // Rocks'n'Diamonds - McDuffin Strikes Back!
3 // ----------------------------------------------------------------------------
4 // (c) 1995-2014 by Artsoft Entertainment
7 // https://www.artsoft.org/
8 // ----------------------------------------------------------------------------
10 // ============================================================================
17 #include "libgame/libgame.h"
27 #define ENABLE_UNUSED_CODE 0 // currently unused functions
28 #define ENABLE_HISTORIC_CHUNKS 0 // only for historic reference
29 #define ENABLE_RESERVED_CODE 0 // reserved for later use
31 #define CHUNK_ID_LEN 4 // IFF style chunk id length
32 #define CHUNK_SIZE_UNDEFINED 0 // undefined chunk size == 0
33 #define CHUNK_SIZE_NONE -1 // do not write chunk size
35 #define LEVEL_CHUNK_NAME_SIZE MAX_LEVEL_NAME_LEN
36 #define LEVEL_CHUNK_AUTH_SIZE MAX_LEVEL_AUTHOR_LEN
38 #define LEVEL_CHUNK_VERS_SIZE 8 // size of file version chunk
39 #define LEVEL_CHUNK_DATE_SIZE 4 // size of file date chunk
40 #define LEVEL_CHUNK_HEAD_SIZE 80 // size of level file header
41 #define LEVEL_CHUNK_HEAD_UNUSED 0 // unused level header bytes
42 #define LEVEL_CHUNK_CNT2_SIZE 160 // size of level CNT2 chunk
43 #define LEVEL_CHUNK_CNT2_UNUSED 11 // unused CNT2 chunk bytes
44 #define LEVEL_CHUNK_CNT3_HEADER 16 // size of level CNT3 header
45 #define LEVEL_CHUNK_CNT3_UNUSED 10 // unused CNT3 chunk bytes
46 #define LEVEL_CPART_CUS3_SIZE 134 // size of CUS3 chunk part
47 #define LEVEL_CPART_CUS3_UNUSED 15 // unused CUS3 bytes / part
48 #define LEVEL_CHUNK_GRP1_SIZE 74 // size of level GRP1 chunk
50 // (element number, number of change pages, change page number)
51 #define LEVEL_CHUNK_CUSX_UNCHANGED (2 + (1 + 1) + (1 + 1))
53 // (element number only)
54 #define LEVEL_CHUNK_GRPX_UNCHANGED 2
55 #define LEVEL_CHUNK_EMPX_UNCHANGED 2
56 #define LEVEL_CHUNK_NOTE_UNCHANGED 2
58 // (nothing at all if unchanged)
59 #define LEVEL_CHUNK_ELEM_UNCHANGED 0
61 #define TAPE_CHUNK_VERS_SIZE 8 // size of file version chunk
62 #define TAPE_CHUNK_HEAD_SIZE 20 // size of tape file header
63 #define TAPE_CHUNK_SCRN_SIZE 2 // size of screen size chunk
65 #define SCORE_CHUNK_VERS_SIZE 8 // size of file version chunk
67 #define LEVEL_CHUNK_CNT3_SIZE(x) (LEVEL_CHUNK_CNT3_HEADER + (x))
68 #define LEVEL_CHUNK_CUS3_SIZE(x) (2 + (x) * LEVEL_CPART_CUS3_SIZE)
69 #define LEVEL_CHUNK_CUS4_SIZE(x) (96 + (x) * 48)
71 // file identifier strings
72 #define LEVEL_COOKIE_TMPL "ROCKSNDIAMONDS_LEVEL_FILE_VERSION_x.x"
73 #define TAPE_COOKIE_TMPL "ROCKSNDIAMONDS_TAPE_FILE_VERSION_x.x"
74 #define SCORE_COOKIE_TMPL "ROCKSNDIAMONDS_SCORE_FILE_VERSION_x.x"
76 // values for deciding when (not) to save configuration data
77 #define SAVE_CONF_NEVER 0
78 #define SAVE_CONF_ALWAYS 1
79 #define SAVE_CONF_WHEN_CHANGED -1
81 // values for chunks using micro chunks
82 #define CONF_MASK_1_BYTE 0x00
83 #define CONF_MASK_2_BYTE 0x40
84 #define CONF_MASK_4_BYTE 0x80
85 #define CONF_MASK_MULTI_BYTES 0xc0
87 #define CONF_MASK_BYTES 0xc0
88 #define CONF_MASK_TOKEN 0x3f
90 #define CONF_VALUE_1_BYTE(x) (CONF_MASK_1_BYTE | (x))
91 #define CONF_VALUE_2_BYTE(x) (CONF_MASK_2_BYTE | (x))
92 #define CONF_VALUE_4_BYTE(x) (CONF_MASK_4_BYTE | (x))
93 #define CONF_VALUE_MULTI_BYTES(x) (CONF_MASK_MULTI_BYTES | (x))
95 // these definitions are just for convenience of use and readability
96 #define CONF_VALUE_8_BIT(x) CONF_VALUE_1_BYTE(x)
97 #define CONF_VALUE_16_BIT(x) CONF_VALUE_2_BYTE(x)
98 #define CONF_VALUE_32_BIT(x) CONF_VALUE_4_BYTE(x)
99 #define CONF_VALUE_BYTES(x) CONF_VALUE_MULTI_BYTES(x)
101 #define CONF_VALUE_NUM_BYTES(x) ((x) == CONF_MASK_1_BYTE ? 1 : \
102 (x) == CONF_MASK_2_BYTE ? 2 : \
103 (x) == CONF_MASK_4_BYTE ? 4 : 0)
105 #define CONF_CONTENT_NUM_ELEMENTS (3 * 3)
106 #define CONF_CONTENT_NUM_BYTES (CONF_CONTENT_NUM_ELEMENTS * 2)
107 #define CONF_ELEMENT_NUM_BYTES (2)
109 #define CONF_ENTITY_NUM_BYTES(t) ((t) == TYPE_ELEMENT || \
110 (t) == TYPE_ELEMENT_LIST ? \
111 CONF_ELEMENT_NUM_BYTES : \
112 (t) == TYPE_CONTENT || \
113 (t) == TYPE_CONTENT_LIST ? \
114 CONF_CONTENT_NUM_BYTES : 1)
116 #define CONF_ELEMENT_BYTE_POS(i) ((i) * CONF_ELEMENT_NUM_BYTES)
117 #define CONF_ELEMENTS_ELEMENT(b, i) ((b[CONF_ELEMENT_BYTE_POS(i)] << 8) | \
118 (b[CONF_ELEMENT_BYTE_POS(i) + 1]))
120 #define CONF_CONTENT_ELEMENT_POS(c,x,y) ((c) * CONF_CONTENT_NUM_ELEMENTS + \
122 #define CONF_CONTENT_BYTE_POS(c,x,y) (CONF_CONTENT_ELEMENT_POS(c,x,y) * \
123 CONF_ELEMENT_NUM_BYTES)
124 #define CONF_CONTENTS_ELEMENT(b,c,x,y) ((b[CONF_CONTENT_BYTE_POS(c,x,y)]<< 8)|\
125 (b[CONF_CONTENT_BYTE_POS(c,x,y) + 1]))
127 // temporary variables used to store pointers to structure members
128 static struct LevelInfo li;
129 static struct ElementInfo xx_ei, yy_ei;
130 static struct ElementChangeInfo xx_change;
131 static struct ElementGroupInfo xx_group;
132 static struct EnvelopeInfo xx_envelope;
133 static unsigned int xx_event_bits[NUM_CE_BITFIELDS];
134 static char xx_default_description[MAX_ELEMENT_NAME_LEN + 1];
135 static int xx_num_contents;
136 static int xx_current_change_page;
137 static char xx_default_string_empty[1] = "";
138 static int xx_string_length_unused;
140 struct LevelFileConfigInfo
142 int element; // element for which data is to be stored
143 int save_type; // save data always, never or when changed
144 int data_type; // data type (used internally, not stored)
145 int conf_type; // micro chunk identifier (stored in file)
148 void *value; // variable that holds the data to be stored
149 int default_value; // initial default value for this variable
152 void *value_copy; // variable that holds the data to be copied
153 void *num_entities; // number of entities for multi-byte data
154 int default_num_entities; // default number of entities for this data
155 int max_num_entities; // maximal number of entities for this data
156 char *default_string; // optional default string for string data
159 static struct LevelFileConfigInfo chunk_config_INFO[] =
161 // ---------- values not related to single elements -------------------------
164 -1, SAVE_CONF_ALWAYS,
165 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
166 &li.game_engine_type, GAME_ENGINE_TYPE_RND
169 -1, SAVE_CONF_ALWAYS,
170 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
171 &li.fieldx, STD_LEV_FIELDX
174 -1, SAVE_CONF_ALWAYS,
175 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
176 &li.fieldy, STD_LEV_FIELDY
179 -1, SAVE_CONF_ALWAYS,
180 TYPE_INTEGER, CONF_VALUE_16_BIT(3),
184 -1, SAVE_CONF_ALWAYS,
185 TYPE_INTEGER, CONF_VALUE_16_BIT(4),
190 TYPE_INTEGER, CONF_VALUE_32_BIT(2),
195 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
196 &li.use_step_counter, FALSE
200 TYPE_BITFIELD, CONF_VALUE_8_BIT(4),
201 &li.wind_direction_initial, MV_NONE
205 TYPE_BOOLEAN, CONF_VALUE_8_BIT(5),
206 &li.em_slippery_gems, FALSE
210 TYPE_BOOLEAN, CONF_VALUE_8_BIT(6),
211 &li.use_custom_template, FALSE
215 TYPE_BITFIELD, CONF_VALUE_32_BIT(1),
216 &li.can_move_into_acid_bits, ~0 // default: everything can
220 TYPE_BITFIELD, CONF_VALUE_8_BIT(7),
221 &li.dont_collide_with_bits, ~0 // default: always deadly
225 TYPE_BOOLEAN, CONF_VALUE_8_BIT(8),
226 &li.em_explodes_by_fire, FALSE
230 TYPE_INTEGER, CONF_VALUE_16_BIT(5),
231 &li.score[SC_TIME_BONUS], 1
235 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
236 &li.auto_exit_sokoban, FALSE
240 TYPE_BOOLEAN, CONF_VALUE_8_BIT(10),
241 &li.auto_count_gems, FALSE
245 TYPE_BOOLEAN, CONF_VALUE_8_BIT(11),
246 &li.solved_by_one_player, FALSE
250 TYPE_INTEGER, CONF_VALUE_8_BIT(12),
251 &li.time_score_base, 1
255 TYPE_BOOLEAN, CONF_VALUE_8_BIT(13),
256 &li.rate_time_over_score, FALSE
260 TYPE_BOOLEAN, CONF_VALUE_8_BIT(14),
261 &li.bd_intermission, FALSE
265 TYPE_INTEGER, CONF_VALUE_8_BIT(15),
266 &li.bd_scheduling_type, GD_SCHEDULING_MILLISECONDS
270 TYPE_BOOLEAN, CONF_VALUE_8_BIT(16),
271 &li.bd_pal_timing, FALSE
275 TYPE_INTEGER, CONF_VALUE_16_BIT(6),
276 &li.bd_cycle_delay_ms, 200
280 TYPE_INTEGER, CONF_VALUE_8_BIT(17),
281 &li.bd_cycle_delay_c64, 0
285 TYPE_INTEGER, CONF_VALUE_8_BIT(18),
286 &li.bd_hatching_delay_cycles, 21
290 TYPE_INTEGER, CONF_VALUE_8_BIT(19),
291 &li.bd_hatching_delay_seconds, 2
295 TYPE_BOOLEAN, CONF_VALUE_8_BIT(20),
296 &li.bd_line_shifting_borders, FALSE
300 TYPE_BOOLEAN, CONF_VALUE_8_BIT(21),
301 &li.bd_scan_first_and_last_row, TRUE
305 TYPE_BOOLEAN, CONF_VALUE_8_BIT(22),
306 &li.bd_short_explosions, TRUE
310 TYPE_BOOLEAN, CONF_VALUE_8_BIT(23),
311 &li.bd_gravity_affects_all, TRUE
321 static struct LevelFileConfigInfo chunk_config_ELEM[] =
323 // (these values are the same for each player)
326 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
327 &li.block_last_field, FALSE // default case for EM levels
331 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
332 &li.sp_block_last_field, TRUE // default case for SP levels
336 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
337 &li.instant_relocation, FALSE
341 TYPE_BOOLEAN, CONF_VALUE_8_BIT(4),
342 &li.can_pass_to_walkable, FALSE
346 TYPE_BOOLEAN, CONF_VALUE_8_BIT(5),
347 &li.block_snap_field, TRUE
351 TYPE_BOOLEAN, CONF_VALUE_8_BIT(6),
352 &li.continuous_snapping, TRUE
356 TYPE_BOOLEAN, CONF_VALUE_8_BIT(12),
357 &li.shifted_relocation, FALSE
361 TYPE_BOOLEAN, CONF_VALUE_8_BIT(15),
362 &li.lazy_relocation, FALSE
366 TYPE_BOOLEAN, CONF_VALUE_8_BIT(16),
367 &li.finish_dig_collect, TRUE
371 TYPE_BOOLEAN, CONF_VALUE_8_BIT(17),
372 &li.keep_walkable_ce, FALSE
375 // (these values are different for each player)
378 TYPE_INTEGER, CONF_VALUE_8_BIT(7),
379 &li.initial_player_stepsize[0], STEPSIZE_NORMAL
383 TYPE_BOOLEAN, CONF_VALUE_8_BIT(8),
384 &li.initial_player_gravity[0], FALSE
388 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
389 &li.use_start_element[0], FALSE
393 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
394 &li.start_element[0], EL_PLAYER_1
398 TYPE_BOOLEAN, CONF_VALUE_8_BIT(10),
399 &li.use_artwork_element[0], FALSE
403 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
404 &li.artwork_element[0], EL_PLAYER_1
408 TYPE_BOOLEAN, CONF_VALUE_8_BIT(11),
409 &li.use_explosion_element[0], FALSE
413 TYPE_ELEMENT, CONF_VALUE_16_BIT(3),
414 &li.explosion_element[0], EL_PLAYER_1
418 TYPE_BOOLEAN, CONF_VALUE_8_BIT(13),
419 &li.use_initial_inventory[0], FALSE
423 TYPE_BOOLEAN, CONF_VALUE_8_BIT(14),
424 &li.initial_inventory_size[0], 1
428 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(1),
429 &li.initial_inventory_content[0][0],EL_EMPTY, NULL,
430 &li.initial_inventory_size[0], 1, MAX_INITIAL_INVENTORY_SIZE
435 TYPE_INTEGER, CONF_VALUE_8_BIT(7),
436 &li.initial_player_stepsize[1], STEPSIZE_NORMAL
440 TYPE_BOOLEAN, CONF_VALUE_8_BIT(8),
441 &li.initial_player_gravity[1], FALSE
445 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
446 &li.use_start_element[1], FALSE
450 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
451 &li.start_element[1], EL_PLAYER_2
455 TYPE_BOOLEAN, CONF_VALUE_8_BIT(10),
456 &li.use_artwork_element[1], FALSE
460 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
461 &li.artwork_element[1], EL_PLAYER_2
465 TYPE_BOOLEAN, CONF_VALUE_8_BIT(11),
466 &li.use_explosion_element[1], FALSE
470 TYPE_ELEMENT, CONF_VALUE_16_BIT(3),
471 &li.explosion_element[1], EL_PLAYER_2
475 TYPE_BOOLEAN, CONF_VALUE_8_BIT(13),
476 &li.use_initial_inventory[1], FALSE
480 TYPE_BOOLEAN, CONF_VALUE_8_BIT(14),
481 &li.initial_inventory_size[1], 1
485 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(1),
486 &li.initial_inventory_content[1][0],EL_EMPTY, NULL,
487 &li.initial_inventory_size[1], 1, MAX_INITIAL_INVENTORY_SIZE
492 TYPE_INTEGER, CONF_VALUE_8_BIT(7),
493 &li.initial_player_stepsize[2], STEPSIZE_NORMAL
497 TYPE_BOOLEAN, CONF_VALUE_8_BIT(8),
498 &li.initial_player_gravity[2], FALSE
502 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
503 &li.use_start_element[2], FALSE
507 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
508 &li.start_element[2], EL_PLAYER_3
512 TYPE_BOOLEAN, CONF_VALUE_8_BIT(10),
513 &li.use_artwork_element[2], FALSE
517 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
518 &li.artwork_element[2], EL_PLAYER_3
522 TYPE_BOOLEAN, CONF_VALUE_8_BIT(11),
523 &li.use_explosion_element[2], FALSE
527 TYPE_ELEMENT, CONF_VALUE_16_BIT(3),
528 &li.explosion_element[2], EL_PLAYER_3
532 TYPE_BOOLEAN, CONF_VALUE_8_BIT(13),
533 &li.use_initial_inventory[2], FALSE
537 TYPE_BOOLEAN, CONF_VALUE_8_BIT(14),
538 &li.initial_inventory_size[2], 1
542 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(1),
543 &li.initial_inventory_content[2][0],EL_EMPTY, NULL,
544 &li.initial_inventory_size[2], 1, MAX_INITIAL_INVENTORY_SIZE
549 TYPE_INTEGER, CONF_VALUE_8_BIT(7),
550 &li.initial_player_stepsize[3], STEPSIZE_NORMAL
554 TYPE_BOOLEAN, CONF_VALUE_8_BIT(8),
555 &li.initial_player_gravity[3], FALSE
559 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
560 &li.use_start_element[3], FALSE
564 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
565 &li.start_element[3], EL_PLAYER_4
569 TYPE_BOOLEAN, CONF_VALUE_8_BIT(10),
570 &li.use_artwork_element[3], FALSE
574 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
575 &li.artwork_element[3], EL_PLAYER_4
579 TYPE_BOOLEAN, CONF_VALUE_8_BIT(11),
580 &li.use_explosion_element[3], FALSE
584 TYPE_ELEMENT, CONF_VALUE_16_BIT(3),
585 &li.explosion_element[3], EL_PLAYER_4
589 TYPE_BOOLEAN, CONF_VALUE_8_BIT(13),
590 &li.use_initial_inventory[3], FALSE
594 TYPE_BOOLEAN, CONF_VALUE_8_BIT(14),
595 &li.initial_inventory_size[3], 1
599 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(1),
600 &li.initial_inventory_content[3][0],EL_EMPTY, NULL,
601 &li.initial_inventory_size[3], 1, MAX_INITIAL_INVENTORY_SIZE
604 // (these values are only valid for BD style levels)
605 // (some values for BD style amoeba following below)
608 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
609 &li.bd_diagonal_movements, FALSE
613 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
614 &li.bd_topmost_player_active, TRUE
618 TYPE_INTEGER, CONF_VALUE_8_BIT(3),
619 &li.bd_pushing_prob, 25
623 TYPE_INTEGER, CONF_VALUE_8_BIT(4),
624 &li.bd_pushing_prob_with_sweet, 100
628 TYPE_BOOLEAN, CONF_VALUE_8_BIT(5),
629 &li.bd_push_mega_rock_with_sweet, FALSE
634 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
635 &li.score[SC_DIAMOND_EXTRA], 20
639 EL_BD_MAGIC_WALL, -1,
640 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
641 &li.bd_magic_wall_wait_hatching, FALSE
644 EL_BD_MAGIC_WALL, -1,
645 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
646 &li.bd_magic_wall_stops_amoeba, TRUE
651 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
652 &li.bd_clock_extra_time, 30
655 // (the following values are related to various game elements)
659 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
660 &li.score[SC_EMERALD], 10
665 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
666 &li.score[SC_DIAMOND], 10
671 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
672 &li.score[SC_BUG], 10
677 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
678 &li.score[SC_SPACESHIP], 10
683 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
684 &li.score[SC_PACMAN], 10
689 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
690 &li.score[SC_NUT], 10
695 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
696 &li.score[SC_DYNAMITE], 10
701 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
702 &li.score[SC_KEY], 10
707 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
708 &li.score[SC_PEARL], 10
713 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
714 &li.score[SC_CRYSTAL], 10
717 // (amoeba values used by R'n'D game engine only)
720 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
721 &li.amoeba_content, EL_DIAMOND
725 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
730 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
731 &li.grow_into_diggable, TRUE
733 // (amoeba values used by BD game engine only)
736 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
737 &li.bd_amoeba_wait_for_hatching, FALSE
741 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
742 &li.bd_amoeba_start_immediately, TRUE
746 TYPE_BOOLEAN, CONF_VALUE_8_BIT(4),
747 &li.bd_amoeba_2_explode_by_amoeba, TRUE
751 TYPE_INTEGER, CONF_VALUE_16_BIT(3),
752 &li.bd_amoeba_threshold_too_big, 200
756 TYPE_INTEGER, CONF_VALUE_16_BIT(4),
757 &li.bd_amoeba_slow_growth_time, 200
761 TYPE_INTEGER, CONF_VALUE_8_BIT(5),
762 &li.bd_amoeba_slow_growth_rate, 3
766 TYPE_INTEGER, CONF_VALUE_8_BIT(6),
767 &li.bd_amoeba_fast_growth_rate, 25
771 TYPE_ELEMENT, CONF_VALUE_16_BIT(5),
772 &li.bd_amoeba_content_too_big, EL_BD_ROCK
776 TYPE_ELEMENT, CONF_VALUE_16_BIT(6),
777 &li.bd_amoeba_content_enclosed, EL_BD_DIAMOND
782 TYPE_INTEGER, CONF_VALUE_16_BIT(3),
783 &li.bd_amoeba_2_threshold_too_big, 200
787 TYPE_INTEGER, CONF_VALUE_16_BIT(4),
788 &li.bd_amoeba_2_slow_growth_time, 200
792 TYPE_INTEGER, CONF_VALUE_8_BIT(5),
793 &li.bd_amoeba_2_slow_growth_rate, 3
797 TYPE_INTEGER, CONF_VALUE_8_BIT(6),
798 &li.bd_amoeba_2_fast_growth_rate, 25
802 TYPE_ELEMENT, CONF_VALUE_16_BIT(5),
803 &li.bd_amoeba_2_content_too_big, EL_BD_ROCK
807 TYPE_ELEMENT, CONF_VALUE_16_BIT(6),
808 &li.bd_amoeba_2_content_enclosed, EL_BD_DIAMOND
812 TYPE_ELEMENT, CONF_VALUE_16_BIT(7),
813 &li.bd_amoeba_2_content_exploding, EL_EMPTY
817 TYPE_ELEMENT, CONF_VALUE_16_BIT(8),
818 &li.bd_amoeba_2_content_looks_like, EL_BD_AMOEBA_2
823 TYPE_CONTENT_LIST, CONF_VALUE_BYTES(1),
824 &li.yamyam_content, EL_ROCK, NULL,
825 &li.num_yamyam_contents, 4, MAX_ELEMENT_CONTENTS
829 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
830 &li.score[SC_YAMYAM], 10
835 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
836 &li.score[SC_ROBOT], 10
840 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
846 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
852 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
853 &li.time_magic_wall, 10
858 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
859 &li.game_of_life[0], 2
863 TYPE_INTEGER, CONF_VALUE_8_BIT(2),
864 &li.game_of_life[1], 3
868 TYPE_INTEGER, CONF_VALUE_8_BIT(3),
869 &li.game_of_life[2], 3
873 TYPE_INTEGER, CONF_VALUE_8_BIT(4),
874 &li.game_of_life[3], 3
878 TYPE_BOOLEAN, CONF_VALUE_8_BIT(5),
879 &li.use_life_bugs, FALSE
884 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
889 TYPE_INTEGER, CONF_VALUE_8_BIT(2),
894 TYPE_INTEGER, CONF_VALUE_8_BIT(3),
899 TYPE_INTEGER, CONF_VALUE_8_BIT(4),
904 EL_TIMEGATE_SWITCH, -1,
905 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
906 &li.time_timegate, 10
910 EL_LIGHT_SWITCH_ACTIVE, -1,
911 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
916 EL_SHIELD_NORMAL, -1,
917 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
918 &li.shield_normal_time, 10
921 EL_SHIELD_NORMAL, -1,
922 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
923 &li.score[SC_SHIELD], 10
927 EL_SHIELD_DEADLY, -1,
928 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
929 &li.shield_deadly_time, 10
932 EL_SHIELD_DEADLY, -1,
933 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
934 &li.score[SC_SHIELD], 10
939 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
944 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
945 &li.extra_time_score, 10
949 EL_TIME_ORB_FULL, -1,
950 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
951 &li.time_orb_time, 10
954 EL_TIME_ORB_FULL, -1,
955 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
956 &li.use_time_orb_bug, FALSE
961 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
962 &li.use_spring_bug, FALSE
967 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
968 &li.android_move_time, 10
972 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
973 &li.android_clone_time, 10
976 EL_EMC_ANDROID, SAVE_CONF_NEVER,
977 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(1),
978 &li.android_clone_element[0], EL_EMPTY, NULL,
979 &li.num_android_clone_elements, 1, MAX_ANDROID_ELEMENTS_OLD
983 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(2),
984 &li.android_clone_element[0], EL_EMPTY, NULL,
985 &li.num_android_clone_elements, 1, MAX_ANDROID_ELEMENTS
990 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
995 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
1000 EL_EMC_MAGNIFIER, -1,
1001 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1002 &li.magnify_score, 10
1005 EL_EMC_MAGNIFIER, -1,
1006 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
1007 &li.magnify_time, 10
1011 EL_EMC_MAGIC_BALL, -1,
1012 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1016 EL_EMC_MAGIC_BALL, -1,
1017 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1018 &li.ball_random, FALSE
1021 EL_EMC_MAGIC_BALL, -1,
1022 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
1023 &li.ball_active_initial, FALSE
1026 EL_EMC_MAGIC_BALL, -1,
1027 TYPE_CONTENT_LIST, CONF_VALUE_BYTES(1),
1028 &li.ball_content, EL_EMPTY, NULL,
1029 &li.num_ball_contents, 4, MAX_ELEMENT_CONTENTS
1033 EL_SOKOBAN_FIELD_EMPTY, -1,
1034 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1035 &li.sb_fields_needed, TRUE
1039 EL_SOKOBAN_OBJECT, -1,
1040 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1041 &li.sb_objects_needed, TRUE
1046 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1047 &li.mm_laser_red, FALSE
1051 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
1052 &li.mm_laser_green, FALSE
1056 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
1057 &li.mm_laser_blue, TRUE
1062 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1063 &li.df_laser_red, TRUE
1067 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
1068 &li.df_laser_green, TRUE
1072 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
1073 &li.df_laser_blue, FALSE
1077 EL_MM_FUSE_ACTIVE, -1,
1078 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1079 &li.mm_time_fuse, 25
1083 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1084 &li.mm_time_bomb, 75
1088 EL_MM_GRAY_BALL, -1,
1089 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1090 &li.mm_time_ball, 75
1093 EL_MM_GRAY_BALL, -1,
1094 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
1095 &li.mm_ball_choice_mode, ANIM_RANDOM
1098 EL_MM_GRAY_BALL, -1,
1099 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(1),
1100 &li.mm_ball_content, EL_EMPTY, NULL,
1101 &li.num_mm_ball_contents, 8, MAX_MM_BALL_CONTENTS
1104 EL_MM_GRAY_BALL, -1,
1105 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
1106 &li.rotate_mm_ball_content, TRUE
1109 EL_MM_GRAY_BALL, -1,
1110 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
1111 &li.explode_mm_ball, FALSE
1115 EL_MM_STEEL_BLOCK, -1,
1116 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1117 &li.mm_time_block, 75
1120 EL_MM_LIGHTBALL, -1,
1121 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1122 &li.score[SC_ELEM_BONUS], 10
1132 static struct LevelFileConfigInfo chunk_config_NOTE[] =
1136 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
1137 &xx_envelope.xsize, MAX_ENVELOPE_XSIZE,
1141 TYPE_INTEGER, CONF_VALUE_8_BIT(2),
1142 &xx_envelope.ysize, MAX_ENVELOPE_YSIZE,
1147 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
1148 &xx_envelope.autowrap, FALSE
1152 TYPE_BOOLEAN, CONF_VALUE_8_BIT(4),
1153 &xx_envelope.centered, FALSE
1158 TYPE_STRING, CONF_VALUE_BYTES(1),
1159 &xx_envelope.text, -1, NULL,
1160 &xx_string_length_unused, -1, MAX_ENVELOPE_TEXT_LEN,
1161 &xx_default_string_empty[0]
1171 static struct LevelFileConfigInfo chunk_config_CUSX_base[] =
1175 TYPE_STRING, CONF_VALUE_BYTES(1),
1176 &xx_ei.description[0], -1,
1177 &yy_ei.description[0],
1178 &xx_string_length_unused, -1, MAX_ELEMENT_NAME_LEN,
1179 &xx_default_description[0]
1184 TYPE_BITFIELD, CONF_VALUE_32_BIT(1),
1185 &xx_ei.properties[EP_BITFIELD_BASE_NR], EP_BITMASK_BASE_DEFAULT,
1186 &yy_ei.properties[EP_BITFIELD_BASE_NR]
1188 #if ENABLE_RESERVED_CODE
1189 // (reserved for later use)
1192 TYPE_BITFIELD, CONF_VALUE_32_BIT(2),
1193 &xx_ei.properties[EP_BITFIELD_BASE_NR + 1], EP_BITMASK_DEFAULT,
1194 &yy_ei.properties[EP_BITFIELD_BASE_NR + 1]
1200 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1201 &xx_ei.use_gfx_element, FALSE,
1202 &yy_ei.use_gfx_element
1206 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
1207 &xx_ei.gfx_element_initial, EL_EMPTY_SPACE,
1208 &yy_ei.gfx_element_initial
1213 TYPE_BITFIELD, CONF_VALUE_8_BIT(2),
1214 &xx_ei.access_direction, MV_ALL_DIRECTIONS,
1215 &yy_ei.access_direction
1220 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
1221 &xx_ei.collect_score_initial, 10,
1222 &yy_ei.collect_score_initial
1226 TYPE_INTEGER, CONF_VALUE_16_BIT(3),
1227 &xx_ei.collect_count_initial, 1,
1228 &yy_ei.collect_count_initial
1233 TYPE_INTEGER, CONF_VALUE_16_BIT(4),
1234 &xx_ei.ce_value_fixed_initial, 0,
1235 &yy_ei.ce_value_fixed_initial
1239 TYPE_INTEGER, CONF_VALUE_16_BIT(5),
1240 &xx_ei.ce_value_random_initial, 0,
1241 &yy_ei.ce_value_random_initial
1245 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
1246 &xx_ei.use_last_ce_value, FALSE,
1247 &yy_ei.use_last_ce_value
1252 TYPE_INTEGER, CONF_VALUE_16_BIT(6),
1253 &xx_ei.push_delay_fixed, 8,
1254 &yy_ei.push_delay_fixed
1258 TYPE_INTEGER, CONF_VALUE_16_BIT(7),
1259 &xx_ei.push_delay_random, 8,
1260 &yy_ei.push_delay_random
1264 TYPE_INTEGER, CONF_VALUE_16_BIT(8),
1265 &xx_ei.drop_delay_fixed, 0,
1266 &yy_ei.drop_delay_fixed
1270 TYPE_INTEGER, CONF_VALUE_16_BIT(9),
1271 &xx_ei.drop_delay_random, 0,
1272 &yy_ei.drop_delay_random
1276 TYPE_INTEGER, CONF_VALUE_16_BIT(10),
1277 &xx_ei.move_delay_fixed, 0,
1278 &yy_ei.move_delay_fixed
1282 TYPE_INTEGER, CONF_VALUE_16_BIT(11),
1283 &xx_ei.move_delay_random, 0,
1284 &yy_ei.move_delay_random
1288 TYPE_INTEGER, CONF_VALUE_16_BIT(16),
1289 &xx_ei.step_delay_fixed, 0,
1290 &yy_ei.step_delay_fixed
1294 TYPE_INTEGER, CONF_VALUE_16_BIT(17),
1295 &xx_ei.step_delay_random, 0,
1296 &yy_ei.step_delay_random
1301 TYPE_BITFIELD, CONF_VALUE_32_BIT(3),
1302 &xx_ei.move_pattern, MV_ALL_DIRECTIONS,
1307 TYPE_BITFIELD, CONF_VALUE_8_BIT(4),
1308 &xx_ei.move_direction_initial, MV_START_AUTOMATIC,
1309 &yy_ei.move_direction_initial
1313 TYPE_INTEGER, CONF_VALUE_8_BIT(5),
1314 &xx_ei.move_stepsize, TILEX / 8,
1315 &yy_ei.move_stepsize
1320 TYPE_ELEMENT, CONF_VALUE_16_BIT(12),
1321 &xx_ei.move_enter_element, EL_EMPTY_SPACE,
1322 &yy_ei.move_enter_element
1326 TYPE_ELEMENT, CONF_VALUE_16_BIT(13),
1327 &xx_ei.move_leave_element, EL_EMPTY_SPACE,
1328 &yy_ei.move_leave_element
1332 TYPE_INTEGER, CONF_VALUE_8_BIT(6),
1333 &xx_ei.move_leave_type, LEAVE_TYPE_UNLIMITED,
1334 &yy_ei.move_leave_type
1339 TYPE_INTEGER, CONF_VALUE_8_BIT(7),
1340 &xx_ei.slippery_type, SLIPPERY_ANY_RANDOM,
1341 &yy_ei.slippery_type
1346 TYPE_INTEGER, CONF_VALUE_8_BIT(8),
1347 &xx_ei.explosion_type, EXPLODES_3X3,
1348 &yy_ei.explosion_type
1352 TYPE_INTEGER, CONF_VALUE_16_BIT(14),
1353 &xx_ei.explosion_delay, 16,
1354 &yy_ei.explosion_delay
1358 TYPE_INTEGER, CONF_VALUE_16_BIT(15),
1359 &xx_ei.ignition_delay, 8,
1360 &yy_ei.ignition_delay
1365 TYPE_CONTENT_LIST, CONF_VALUE_BYTES(2),
1366 &xx_ei.content, EL_EMPTY_SPACE,
1368 &xx_num_contents, 1, 1
1371 // ---------- "num_change_pages" must be the last entry ---------------------
1374 -1, SAVE_CONF_ALWAYS,
1375 TYPE_INTEGER, CONF_VALUE_8_BIT(9),
1376 &xx_ei.num_change_pages, 1,
1377 &yy_ei.num_change_pages
1388 static struct LevelFileConfigInfo chunk_config_CUSX_change[] =
1390 // ---------- "current_change_page" must be the first entry -----------------
1393 -1, SAVE_CONF_ALWAYS,
1394 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
1395 &xx_current_change_page, -1
1398 // ---------- (the remaining entries can be in any order) -------------------
1402 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
1403 &xx_change.can_change, FALSE
1408 TYPE_BITFIELD, CONF_VALUE_32_BIT(1),
1409 &xx_event_bits[0], 0
1413 TYPE_BITFIELD, CONF_VALUE_32_BIT(2),
1414 &xx_event_bits[1], 0
1419 TYPE_BITFIELD, CONF_VALUE_8_BIT(3),
1420 &xx_change.trigger_player, CH_PLAYER_ANY
1424 TYPE_BITFIELD, CONF_VALUE_8_BIT(4),
1425 &xx_change.trigger_side, CH_SIDE_ANY
1429 TYPE_BITFIELD, CONF_VALUE_32_BIT(3),
1430 &xx_change.trigger_page, CH_PAGE_ANY
1435 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
1436 &xx_change.target_element, EL_EMPTY_SPACE
1441 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
1442 &xx_change.delay_fixed, 0
1446 TYPE_INTEGER, CONF_VALUE_16_BIT(3),
1447 &xx_change.delay_random, 0
1451 TYPE_INTEGER, CONF_VALUE_16_BIT(4),
1452 &xx_change.delay_frames, FRAMES_PER_SECOND
1457 TYPE_ELEMENT, CONF_VALUE_16_BIT(5),
1458 &xx_change.initial_trigger_element, EL_EMPTY_SPACE
1463 TYPE_BOOLEAN, CONF_VALUE_8_BIT(6),
1464 &xx_change.explode, FALSE
1468 TYPE_BOOLEAN, CONF_VALUE_8_BIT(7),
1469 &xx_change.use_target_content, FALSE
1473 TYPE_BOOLEAN, CONF_VALUE_8_BIT(8),
1474 &xx_change.only_if_complete, FALSE
1478 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
1479 &xx_change.use_random_replace, FALSE
1483 TYPE_INTEGER, CONF_VALUE_8_BIT(10),
1484 &xx_change.random_percentage, 100
1488 TYPE_INTEGER, CONF_VALUE_8_BIT(11),
1489 &xx_change.replace_when, CP_WHEN_EMPTY
1494 TYPE_BOOLEAN, CONF_VALUE_8_BIT(12),
1495 &xx_change.has_action, FALSE
1499 TYPE_INTEGER, CONF_VALUE_8_BIT(13),
1500 &xx_change.action_type, CA_NO_ACTION
1504 TYPE_INTEGER, CONF_VALUE_8_BIT(14),
1505 &xx_change.action_mode, CA_MODE_UNDEFINED
1509 TYPE_INTEGER, CONF_VALUE_16_BIT(6),
1510 &xx_change.action_arg, CA_ARG_UNDEFINED
1515 TYPE_ELEMENT, CONF_VALUE_16_BIT(7),
1516 &xx_change.action_element, EL_EMPTY_SPACE
1521 TYPE_CONTENT_LIST, CONF_VALUE_BYTES(1),
1522 &xx_change.target_content, EL_EMPTY_SPACE, NULL,
1523 &xx_num_contents, 1, 1
1533 static struct LevelFileConfigInfo chunk_config_GRPX[] =
1537 TYPE_STRING, CONF_VALUE_BYTES(1),
1538 &xx_ei.description[0], -1, NULL,
1539 &xx_string_length_unused, -1, MAX_ELEMENT_NAME_LEN,
1540 &xx_default_description[0]
1545 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1546 &xx_ei.use_gfx_element, FALSE
1550 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
1551 &xx_ei.gfx_element_initial, EL_EMPTY_SPACE
1556 TYPE_INTEGER, CONF_VALUE_8_BIT(2),
1557 &xx_group.choice_mode, ANIM_RANDOM
1562 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(2),
1563 &xx_group.element[0], EL_EMPTY_SPACE, NULL,
1564 &xx_group.num_elements, 1, MAX_ELEMENTS_IN_GROUP
1574 static struct LevelFileConfigInfo chunk_config_EMPX[] =
1578 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1579 &xx_ei.use_gfx_element, FALSE
1583 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
1584 &xx_ei.gfx_element_initial, EL_EMPTY_SPACE
1594 static struct LevelFileConfigInfo chunk_config_CONF[] = // (OBSOLETE)
1598 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
1599 &li.block_snap_field, TRUE
1603 TYPE_BOOLEAN, CONF_VALUE_8_BIT(13),
1604 &li.continuous_snapping, TRUE
1608 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
1609 &li.initial_player_stepsize[0], STEPSIZE_NORMAL
1613 TYPE_BOOLEAN, CONF_VALUE_8_BIT(10),
1614 &li.use_start_element[0], FALSE
1618 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
1619 &li.start_element[0], EL_PLAYER_1
1623 TYPE_BOOLEAN, CONF_VALUE_8_BIT(11),
1624 &li.use_artwork_element[0], FALSE
1628 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
1629 &li.artwork_element[0], EL_PLAYER_1
1633 TYPE_BOOLEAN, CONF_VALUE_8_BIT(12),
1634 &li.use_explosion_element[0], FALSE
1638 TYPE_ELEMENT, CONF_VALUE_16_BIT(3),
1639 &li.explosion_element[0], EL_PLAYER_1
1654 filetype_id_list[] =
1656 { LEVEL_FILE_TYPE_RND, "RND" },
1657 { LEVEL_FILE_TYPE_BD, "BD" },
1658 { LEVEL_FILE_TYPE_EM, "EM" },
1659 { LEVEL_FILE_TYPE_SP, "SP" },
1660 { LEVEL_FILE_TYPE_DX, "DX" },
1661 { LEVEL_FILE_TYPE_SB, "SB" },
1662 { LEVEL_FILE_TYPE_DC, "DC" },
1663 { LEVEL_FILE_TYPE_MM, "MM" },
1664 { LEVEL_FILE_TYPE_MM, "DF" },
1669 // ============================================================================
1670 // level file functions
1671 // ============================================================================
1673 static boolean check_special_flags(char *flag)
1675 if (strEqual(options.special_flags, flag) ||
1676 strEqual(leveldir_current->special_flags, flag))
1682 static struct DateInfo getCurrentDate(void)
1684 time_t epoch_seconds = time(NULL);
1685 struct tm *now = localtime(&epoch_seconds);
1686 struct DateInfo date;
1688 date.year = now->tm_year + 1900;
1689 date.month = now->tm_mon + 1;
1690 date.day = now->tm_mday;
1692 date.src = DATE_SRC_CLOCK;
1697 static void resetEventFlags(struct ElementChangeInfo *change)
1701 for (i = 0; i < NUM_CHANGE_EVENTS; i++)
1702 change->has_event[i] = FALSE;
1705 static void resetEventBits(void)
1709 for (i = 0; i < NUM_CE_BITFIELDS; i++)
1710 xx_event_bits[i] = 0;
1713 static void setEventFlagsFromEventBits(struct ElementChangeInfo *change)
1717 /* important: only change event flag if corresponding event bit is set
1718 (this is because all xx_event_bits[] values are loaded separately,
1719 and all xx_event_bits[] values are set back to zero before loading
1720 another value xx_event_bits[x] (each value representing 32 flags)) */
1722 for (i = 0; i < NUM_CHANGE_EVENTS; i++)
1723 if (xx_event_bits[CH_EVENT_BITFIELD_NR(i)] & CH_EVENT_BIT(i))
1724 change->has_event[i] = TRUE;
1727 static void setEventBitsFromEventFlags(struct ElementChangeInfo *change)
1731 /* in contrast to the above function setEventFlagsFromEventBits(), it
1732 would also be possible to set all bits in xx_event_bits[] to 0 or 1
1733 depending on the corresponding change->has_event[i] values here, as
1734 all xx_event_bits[] values are reset in resetEventBits() before */
1736 for (i = 0; i < NUM_CHANGE_EVENTS; i++)
1737 if (change->has_event[i])
1738 xx_event_bits[CH_EVENT_BITFIELD_NR(i)] |= CH_EVENT_BIT(i);
1741 static char *getDefaultElementDescription(struct ElementInfo *ei)
1743 static char description[MAX_ELEMENT_NAME_LEN + 1];
1744 char *default_description = (ei->custom_description != NULL ?
1745 ei->custom_description :
1746 ei->editor_description);
1749 // always start with reliable default values
1750 for (i = 0; i < MAX_ELEMENT_NAME_LEN + 1; i++)
1751 description[i] = '\0';
1753 // truncate element description to MAX_ELEMENT_NAME_LEN bytes
1754 strncpy(description, default_description, MAX_ELEMENT_NAME_LEN);
1756 return &description[0];
1759 static void setElementDescriptionToDefault(struct ElementInfo *ei)
1761 char *default_description = getDefaultElementDescription(ei);
1764 for (i = 0; i < MAX_ELEMENT_NAME_LEN + 1; i++)
1765 ei->description[i] = default_description[i];
1768 static void setConfigToDefaultsFromConfigList(struct LevelFileConfigInfo *conf)
1772 for (i = 0; conf[i].data_type != -1; i++)
1774 int default_value = conf[i].default_value;
1775 int data_type = conf[i].data_type;
1776 int conf_type = conf[i].conf_type;
1777 int byte_mask = conf_type & CONF_MASK_BYTES;
1779 if (byte_mask == CONF_MASK_MULTI_BYTES)
1781 int default_num_entities = conf[i].default_num_entities;
1782 int max_num_entities = conf[i].max_num_entities;
1784 *(int *)(conf[i].num_entities) = default_num_entities;
1786 if (data_type == TYPE_STRING)
1788 char *default_string = conf[i].default_string;
1789 char *string = (char *)(conf[i].value);
1791 strncpy(string, default_string, max_num_entities);
1793 else if (data_type == TYPE_ELEMENT_LIST)
1795 int *element_array = (int *)(conf[i].value);
1798 for (j = 0; j < max_num_entities; j++)
1799 element_array[j] = default_value;
1801 else if (data_type == TYPE_CONTENT_LIST)
1803 struct Content *content = (struct Content *)(conf[i].value);
1806 for (c = 0; c < max_num_entities; c++)
1807 for (y = 0; y < 3; y++)
1808 for (x = 0; x < 3; x++)
1809 content[c].e[x][y] = default_value;
1812 else // constant size configuration data (1, 2 or 4 bytes)
1814 if (data_type == TYPE_BOOLEAN)
1815 *(boolean *)(conf[i].value) = default_value;
1817 *(int *) (conf[i].value) = default_value;
1822 static void copyConfigFromConfigList(struct LevelFileConfigInfo *conf)
1826 for (i = 0; conf[i].data_type != -1; i++)
1828 int data_type = conf[i].data_type;
1829 int conf_type = conf[i].conf_type;
1830 int byte_mask = conf_type & CONF_MASK_BYTES;
1832 if (byte_mask == CONF_MASK_MULTI_BYTES)
1834 int max_num_entities = conf[i].max_num_entities;
1836 if (data_type == TYPE_STRING)
1838 char *string = (char *)(conf[i].value);
1839 char *string_copy = (char *)(conf[i].value_copy);
1841 strncpy(string_copy, string, max_num_entities);
1843 else if (data_type == TYPE_ELEMENT_LIST)
1845 int *element_array = (int *)(conf[i].value);
1846 int *element_array_copy = (int *)(conf[i].value_copy);
1849 for (j = 0; j < max_num_entities; j++)
1850 element_array_copy[j] = element_array[j];
1852 else if (data_type == TYPE_CONTENT_LIST)
1854 struct Content *content = (struct Content *)(conf[i].value);
1855 struct Content *content_copy = (struct Content *)(conf[i].value_copy);
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_copy[c].e[x][y] = content[c].e[x][y];
1864 else // constant size configuration data (1, 2 or 4 bytes)
1866 if (data_type == TYPE_BOOLEAN)
1867 *(boolean *)(conf[i].value_copy) = *(boolean *)(conf[i].value);
1869 *(int *) (conf[i].value_copy) = *(int *) (conf[i].value);
1874 void copyElementInfo(struct ElementInfo *ei_from, struct ElementInfo *ei_to)
1878 xx_ei = *ei_from; // copy element data into temporary buffer
1879 yy_ei = *ei_to; // copy element data into temporary buffer
1881 copyConfigFromConfigList(chunk_config_CUSX_base);
1886 // ---------- reinitialize and copy change pages ----------
1888 ei_to->num_change_pages = ei_from->num_change_pages;
1889 ei_to->current_change_page = ei_from->current_change_page;
1891 setElementChangePages(ei_to, ei_to->num_change_pages);
1893 for (i = 0; i < ei_to->num_change_pages; i++)
1894 ei_to->change_page[i] = ei_from->change_page[i];
1896 // ---------- copy group element info ----------
1897 if (ei_from->group != NULL && ei_to->group != NULL) // group or internal
1898 *ei_to->group = *ei_from->group;
1900 // mark this custom element as modified
1901 ei_to->modified_settings = TRUE;
1904 void setElementChangePages(struct ElementInfo *ei, int change_pages)
1906 int change_page_size = sizeof(struct ElementChangeInfo);
1908 ei->num_change_pages = MAX(1, change_pages);
1911 checked_realloc(ei->change_page, ei->num_change_pages * change_page_size);
1913 if (ei->current_change_page >= ei->num_change_pages)
1914 ei->current_change_page = ei->num_change_pages - 1;
1916 ei->change = &ei->change_page[ei->current_change_page];
1919 void setElementChangeInfoToDefaults(struct ElementChangeInfo *change)
1921 xx_change = *change; // copy change data into temporary buffer
1923 setConfigToDefaultsFromConfigList(chunk_config_CUSX_change);
1925 *change = xx_change;
1927 resetEventFlags(change);
1929 change->direct_action = 0;
1930 change->other_action = 0;
1932 change->pre_change_function = NULL;
1933 change->change_function = NULL;
1934 change->post_change_function = NULL;
1937 static void setLevelInfoToDefaults_Level(struct LevelInfo *level)
1941 li = *level; // copy level data into temporary buffer
1942 setConfigToDefaultsFromConfigList(chunk_config_INFO);
1943 *level = li; // copy temporary buffer back to level data
1945 setLevelInfoToDefaults_BD();
1946 setLevelInfoToDefaults_EM();
1947 setLevelInfoToDefaults_SP();
1948 setLevelInfoToDefaults_MM();
1950 level->native_bd_level = &native_bd_level;
1951 level->native_em_level = &native_em_level;
1952 level->native_sp_level = &native_sp_level;
1953 level->native_mm_level = &native_mm_level;
1955 level->file_version = FILE_VERSION_ACTUAL;
1956 level->game_version = GAME_VERSION_ACTUAL;
1958 level->creation_date = getCurrentDate();
1960 level->encoding_16bit_field = TRUE;
1961 level->encoding_16bit_yamyam = TRUE;
1962 level->encoding_16bit_amoeba = TRUE;
1964 // clear level name and level author string buffers
1965 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
1966 level->name[i] = '\0';
1967 for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
1968 level->author[i] = '\0';
1970 // set level name and level author to default values
1971 strcpy(level->name, NAMELESS_LEVEL_NAME);
1972 strcpy(level->author, ANONYMOUS_NAME);
1974 // set level playfield to playable default level with player and exit
1975 for (x = 0; x < MAX_LEV_FIELDX; x++)
1976 for (y = 0; y < MAX_LEV_FIELDY; y++)
1977 level->field[x][y] = EL_SAND;
1979 level->field[0][0] = EL_PLAYER_1;
1980 level->field[STD_LEV_FIELDX - 1][STD_LEV_FIELDY - 1] = EL_EXIT_CLOSED;
1982 BorderElement = EL_STEELWALL;
1984 // detect custom elements when loading them
1985 level->file_has_custom_elements = FALSE;
1987 // set all bug compatibility flags to "false" => do not emulate this bug
1988 level->use_action_after_change_bug = FALSE;
1990 if (leveldir_current)
1992 // try to determine better author name than 'anonymous'
1993 if (!strEqual(leveldir_current->author, ANONYMOUS_NAME))
1995 strncpy(level->author, leveldir_current->author, MAX_LEVEL_AUTHOR_LEN);
1996 level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
2000 switch (LEVELCLASS(leveldir_current))
2002 case LEVELCLASS_TUTORIAL:
2003 strcpy(level->author, PROGRAM_AUTHOR_STRING);
2006 case LEVELCLASS_CONTRIB:
2007 strncpy(level->author, leveldir_current->name, MAX_LEVEL_AUTHOR_LEN);
2008 level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
2011 case LEVELCLASS_PRIVATE:
2012 strncpy(level->author, getRealName(), MAX_LEVEL_AUTHOR_LEN);
2013 level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
2017 // keep default value
2024 static void setLevelInfoToDefaults_Elements(struct LevelInfo *level)
2026 static boolean clipboard_elements_initialized = FALSE;
2029 InitElementPropertiesStatic();
2031 li = *level; // copy level data into temporary buffer
2032 setConfigToDefaultsFromConfigList(chunk_config_ELEM);
2033 *level = li; // copy temporary buffer back to level data
2035 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2038 struct ElementInfo *ei = &element_info[element];
2040 if (element == EL_MM_GRAY_BALL)
2042 struct LevelInfo_MM *level_mm = level->native_mm_level;
2045 for (j = 0; j < level->num_mm_ball_contents; j++)
2046 level->mm_ball_content[j] =
2047 map_element_MM_to_RND(level_mm->ball_content[j]);
2050 // never initialize clipboard elements after the very first time
2051 // (to be able to use clipboard elements between several levels)
2052 if (IS_CLIPBOARD_ELEMENT(element) && clipboard_elements_initialized)
2055 if (IS_ENVELOPE(element))
2057 int envelope_nr = element - EL_ENVELOPE_1;
2059 setConfigToDefaultsFromConfigList(chunk_config_NOTE);
2061 level->envelope[envelope_nr] = xx_envelope;
2064 if (IS_CUSTOM_ELEMENT(element) ||
2065 IS_GROUP_ELEMENT(element) ||
2066 IS_INTERNAL_ELEMENT(element))
2068 xx_ei = *ei; // copy element data into temporary buffer
2070 setConfigToDefaultsFromConfigList(chunk_config_CUSX_base);
2075 setElementChangePages(ei, 1);
2076 setElementChangeInfoToDefaults(ei->change);
2078 if (IS_CUSTOM_ELEMENT(element) ||
2079 IS_GROUP_ELEMENT(element))
2081 setElementDescriptionToDefault(ei);
2083 ei->modified_settings = FALSE;
2086 if (IS_CUSTOM_ELEMENT(element) ||
2087 IS_INTERNAL_ELEMENT(element))
2089 // internal values used in level editor
2091 ei->access_type = 0;
2092 ei->access_layer = 0;
2093 ei->access_protected = 0;
2094 ei->walk_to_action = 0;
2095 ei->smash_targets = 0;
2098 ei->can_explode_by_fire = FALSE;
2099 ei->can_explode_smashed = FALSE;
2100 ei->can_explode_impact = FALSE;
2102 ei->current_change_page = 0;
2105 if (IS_GROUP_ELEMENT(element) ||
2106 IS_INTERNAL_ELEMENT(element))
2108 struct ElementGroupInfo *group;
2110 // initialize memory for list of elements in group
2111 if (ei->group == NULL)
2112 ei->group = checked_malloc(sizeof(struct ElementGroupInfo));
2116 xx_group = *group; // copy group data into temporary buffer
2118 setConfigToDefaultsFromConfigList(chunk_config_GRPX);
2123 if (IS_EMPTY_ELEMENT(element) ||
2124 IS_INTERNAL_ELEMENT(element))
2126 xx_ei = *ei; // copy element data into temporary buffer
2128 setConfigToDefaultsFromConfigList(chunk_config_EMPX);
2134 clipboard_elements_initialized = TRUE;
2137 static void setLevelInfoToDefaults(struct LevelInfo *level,
2138 boolean level_info_only,
2139 boolean reset_file_status)
2141 setLevelInfoToDefaults_Level(level);
2143 if (!level_info_only)
2144 setLevelInfoToDefaults_Elements(level);
2146 if (reset_file_status)
2148 level->no_valid_file = FALSE;
2149 level->no_level_file = FALSE;
2152 level->changed = FALSE;
2155 static void setFileInfoToDefaults(struct LevelFileInfo *level_file_info)
2157 level_file_info->nr = 0;
2158 level_file_info->type = LEVEL_FILE_TYPE_UNKNOWN;
2159 level_file_info->packed = FALSE;
2161 setString(&level_file_info->basename, NULL);
2162 setString(&level_file_info->filename, NULL);
2165 int getMappedElement_SB(int, boolean);
2167 static void ActivateLevelTemplate(void)
2171 if (check_special_flags("load_xsb_to_ces"))
2173 // fill smaller playfields with padding "beyond border wall" elements
2174 if (level.fieldx < level_template.fieldx ||
2175 level.fieldy < level_template.fieldy)
2177 short field[level.fieldx][level.fieldy];
2178 int new_fieldx = MAX(level.fieldx, level_template.fieldx);
2179 int new_fieldy = MAX(level.fieldy, level_template.fieldy);
2180 int pos_fieldx = (new_fieldx - level.fieldx) / 2;
2181 int pos_fieldy = (new_fieldy - level.fieldy) / 2;
2183 // copy old playfield (which is smaller than the visible area)
2184 for (y = 0; y < level.fieldy; y++) for (x = 0; x < level.fieldx; x++)
2185 field[x][y] = level.field[x][y];
2187 // fill new, larger playfield with "beyond border wall" elements
2188 for (y = 0; y < new_fieldy; y++) for (x = 0; x < new_fieldx; x++)
2189 level.field[x][y] = getMappedElement_SB('_', TRUE);
2191 // copy the old playfield to the middle of the new playfield
2192 for (y = 0; y < level.fieldy; y++) for (x = 0; x < level.fieldx; x++)
2193 level.field[pos_fieldx + x][pos_fieldy + y] = field[x][y];
2195 level.fieldx = new_fieldx;
2196 level.fieldy = new_fieldy;
2200 // Currently there is no special action needed to activate the template
2201 // data, because 'element_info' property settings overwrite the original
2202 // level data, while all other variables do not change.
2204 // Exception: 'from_level_template' elements in the original level playfield
2205 // are overwritten with the corresponding elements at the same position in
2206 // playfield from the level template.
2208 for (x = 0; x < level.fieldx; x++)
2209 for (y = 0; y < level.fieldy; y++)
2210 if (level.field[x][y] == EL_FROM_LEVEL_TEMPLATE)
2211 level.field[x][y] = level_template.field[x][y];
2213 if (check_special_flags("load_xsb_to_ces"))
2215 struct LevelInfo level_backup = level;
2217 // overwrite all individual level settings from template level settings
2218 level = level_template;
2220 // restore level file info
2221 level.file_info = level_backup.file_info;
2223 // restore playfield size
2224 level.fieldx = level_backup.fieldx;
2225 level.fieldy = level_backup.fieldy;
2227 // restore playfield content
2228 for (x = 0; x < level.fieldx; x++)
2229 for (y = 0; y < level.fieldy; y++)
2230 level.field[x][y] = level_backup.field[x][y];
2232 // restore name and author from individual level
2233 strcpy(level.name, level_backup.name);
2234 strcpy(level.author, level_backup.author);
2236 // restore flag "use_custom_template"
2237 level.use_custom_template = level_backup.use_custom_template;
2241 static boolean checkForPackageFromBasename_BD(char *basename)
2243 // check for native BD level file extensions
2244 if (!strSuffixLower(basename, ".bd") &&
2245 !strSuffixLower(basename, ".bdr") &&
2246 !strSuffixLower(basename, ".brc") &&
2247 !strSuffixLower(basename, ".gds"))
2250 // check for standard single-level BD files (like "001.bd")
2251 if (strSuffixLower(basename, ".bd") &&
2252 strlen(basename) == 6 &&
2253 basename[0] >= '0' && basename[0] <= '9' &&
2254 basename[1] >= '0' && basename[1] <= '9' &&
2255 basename[2] >= '0' && basename[2] <= '9')
2258 // this is a level package in native BD file format
2262 static char *getLevelFilenameFromBasename(char *basename)
2264 static char *filename = NULL;
2266 checked_free(filename);
2268 filename = getPath2(getCurrentLevelDir(), basename);
2273 static int getFileTypeFromBasename(char *basename)
2275 // !!! ALSO SEE COMMENT IN checkForPackageFromBasename() !!!
2277 static char *filename = NULL;
2278 struct stat file_status;
2280 // ---------- try to determine file type from filename ----------
2282 // check for typical filename of a Supaplex level package file
2283 if (strlen(basename) == 10 && strPrefixLower(basename, "levels.d"))
2284 return LEVEL_FILE_TYPE_SP;
2286 // check for typical filename of a Diamond Caves II level package file
2287 if (strSuffixLower(basename, ".dc") ||
2288 strSuffixLower(basename, ".dc2"))
2289 return LEVEL_FILE_TYPE_DC;
2291 // check for typical filename of a Sokoban level package file
2292 if (strSuffixLower(basename, ".xsb") &&
2293 strchr(basename, '%') == NULL)
2294 return LEVEL_FILE_TYPE_SB;
2296 // check for typical filename of a Boulder Dash (GDash) level package file
2297 if (checkForPackageFromBasename_BD(basename))
2298 return LEVEL_FILE_TYPE_BD;
2300 // ---------- try to determine file type from filesize ----------
2302 checked_free(filename);
2303 filename = getPath2(getCurrentLevelDir(), basename);
2305 if (stat(filename, &file_status) == 0)
2307 // check for typical filesize of a Supaplex level package file
2308 if (file_status.st_size == 170496)
2309 return LEVEL_FILE_TYPE_SP;
2312 return LEVEL_FILE_TYPE_UNKNOWN;
2315 static int getFileTypeFromMagicBytes(char *filename, int type)
2319 if ((file = openFile(filename, MODE_READ)))
2321 char chunk_name[CHUNK_ID_LEN + 1];
2323 getFileChunkBE(file, chunk_name, NULL);
2325 if (strEqual(chunk_name, "MMII") ||
2326 strEqual(chunk_name, "MIRR"))
2327 type = LEVEL_FILE_TYPE_MM;
2335 static boolean checkForPackageFromBasename(char *basename)
2337 // !!! WON'T WORK ANYMORE IF getFileTypeFromBasename() ALSO DETECTS !!!
2338 // !!! SINGLE LEVELS (CURRENTLY ONLY DETECTS LEVEL PACKAGES !!!
2340 return (getFileTypeFromBasename(basename) != LEVEL_FILE_TYPE_UNKNOWN);
2343 static char *getSingleLevelBasenameExt(int nr, char *extension)
2345 static char basename[MAX_FILENAME_LEN];
2348 sprintf(basename, "%s", LEVELTEMPLATE_FILENAME);
2350 sprintf(basename, "%03d.%s", nr, extension);
2355 static char *getSingleLevelBasename(int nr)
2357 return getSingleLevelBasenameExt(nr, LEVELFILE_EXTENSION);
2360 static char *getPackedLevelBasename(int type)
2362 static char basename[MAX_FILENAME_LEN];
2363 char *directory = getCurrentLevelDir();
2365 DirectoryEntry *dir_entry;
2367 strcpy(basename, UNDEFINED_FILENAME); // default: undefined file
2369 if ((dir = openDirectory(directory)) == NULL)
2371 Warn("cannot read current level directory '%s'", directory);
2376 while ((dir_entry = readDirectory(dir)) != NULL) // loop all entries
2378 char *entry_basename = dir_entry->basename;
2379 int entry_type = getFileTypeFromBasename(entry_basename);
2381 if (entry_type != LEVEL_FILE_TYPE_UNKNOWN) // found valid level package
2383 if (type == LEVEL_FILE_TYPE_UNKNOWN ||
2386 strcpy(basename, entry_basename);
2393 closeDirectory(dir);
2398 static char *getSingleLevelFilename(int nr)
2400 return getLevelFilenameFromBasename(getSingleLevelBasename(nr));
2403 #if ENABLE_UNUSED_CODE
2404 static char *getPackedLevelFilename(int type)
2406 return getLevelFilenameFromBasename(getPackedLevelBasename(type));
2410 char *getDefaultLevelFilename(int nr)
2412 return getSingleLevelFilename(nr);
2415 #if ENABLE_UNUSED_CODE
2416 static void setLevelFileInfo_SingleLevelFilename(struct LevelFileInfo *lfi,
2420 lfi->packed = FALSE;
2422 setString(&lfi->basename, getSingleLevelBasename(lfi->nr, lfi->type));
2423 setString(&lfi->filename, getLevelFilenameFromBasename(lfi->basename));
2427 static void setLevelFileInfo_FormatLevelFilename(struct LevelFileInfo *lfi,
2428 int type, char *format, ...)
2430 static char basename[MAX_FILENAME_LEN];
2433 va_start(ap, format);
2434 vsprintf(basename, format, ap);
2438 lfi->packed = FALSE;
2440 setString(&lfi->basename, basename);
2441 setString(&lfi->filename, getLevelFilenameFromBasename(lfi->basename));
2444 static void setLevelFileInfo_PackedLevelFilename(struct LevelFileInfo *lfi,
2450 setString(&lfi->basename, getPackedLevelBasename(lfi->type));
2451 setString(&lfi->filename, getLevelFilenameFromBasename(lfi->basename));
2454 static int getFiletypeFromID(char *filetype_id)
2456 char *filetype_id_lower;
2457 int filetype = LEVEL_FILE_TYPE_UNKNOWN;
2460 if (filetype_id == NULL)
2461 return LEVEL_FILE_TYPE_UNKNOWN;
2463 filetype_id_lower = getStringToLower(filetype_id);
2465 for (i = 0; filetype_id_list[i].id != NULL; i++)
2467 char *id_lower = getStringToLower(filetype_id_list[i].id);
2469 if (strEqual(filetype_id_lower, id_lower))
2470 filetype = filetype_id_list[i].filetype;
2474 if (filetype != LEVEL_FILE_TYPE_UNKNOWN)
2478 free(filetype_id_lower);
2483 char *getLocalLevelTemplateFilename(void)
2485 return getDefaultLevelFilename(-1);
2488 char *getGlobalLevelTemplateFilename(void)
2490 // global variable "leveldir_current" must be modified in the loop below
2491 LevelDirTree *leveldir_current_last = leveldir_current;
2492 char *filename = NULL;
2494 // check for template level in path from current to topmost tree node
2496 while (leveldir_current != NULL)
2498 filename = getDefaultLevelFilename(-1);
2500 if (fileExists(filename))
2503 leveldir_current = leveldir_current->node_parent;
2506 // restore global variable "leveldir_current" modified in above loop
2507 leveldir_current = leveldir_current_last;
2512 static void determineLevelFileInfo_Filename(struct LevelFileInfo *lfi)
2516 // special case: level number is negative => check for level template file
2519 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
2520 getSingleLevelBasename(-1));
2522 // replace local level template filename with global template filename
2523 setString(&lfi->filename, getGlobalLevelTemplateFilename());
2525 // no fallback if template file not existing
2529 // special case: check for file name/pattern specified in "levelinfo.conf"
2530 if (leveldir_current->level_filename != NULL)
2532 int filetype = getFiletypeFromID(leveldir_current->level_filetype);
2534 setLevelFileInfo_FormatLevelFilename(lfi, filetype,
2535 leveldir_current->level_filename, nr);
2537 lfi->packed = checkForPackageFromBasename(leveldir_current->level_filename);
2539 if (fileExists(lfi->filename))
2542 else if (leveldir_current->level_filetype != NULL)
2544 int filetype = getFiletypeFromID(leveldir_current->level_filetype);
2546 // check for specified native level file with standard file name
2547 setLevelFileInfo_FormatLevelFilename(lfi, filetype,
2548 "%03d.%s", nr, LEVELFILE_EXTENSION);
2549 if (fileExists(lfi->filename))
2553 // check for native Rocks'n'Diamonds level file
2554 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
2555 "%03d.%s", nr, LEVELFILE_EXTENSION);
2556 if (fileExists(lfi->filename))
2559 // check for native Boulder Dash level file
2560 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_BD, "%03d.bd", nr);
2561 if (fileExists(lfi->filename))
2564 // check for Emerald Mine level file (V1)
2565 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "a%c%c",
2566 'a' + (nr / 10) % 26, '0' + nr % 10);
2567 if (fileExists(lfi->filename))
2569 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "A%c%c",
2570 'A' + (nr / 10) % 26, '0' + nr % 10);
2571 if (fileExists(lfi->filename))
2574 // check for Emerald Mine level file (V2 to V5)
2575 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%d", nr);
2576 if (fileExists(lfi->filename))
2579 // check for Emerald Mine level file (V6 / single mode)
2580 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02ds", nr);
2581 if (fileExists(lfi->filename))
2583 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02dS", nr);
2584 if (fileExists(lfi->filename))
2587 // check for Emerald Mine level file (V6 / teamwork mode)
2588 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02dt", nr);
2589 if (fileExists(lfi->filename))
2591 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02dT", nr);
2592 if (fileExists(lfi->filename))
2595 // check for various packed level file formats
2596 setLevelFileInfo_PackedLevelFilename(lfi, LEVEL_FILE_TYPE_UNKNOWN);
2597 if (fileExists(lfi->filename))
2600 // no known level file found -- use default values (and fail later)
2601 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
2602 "%03d.%s", nr, LEVELFILE_EXTENSION);
2605 static void determineLevelFileInfo_Filetype(struct LevelFileInfo *lfi)
2607 if (lfi->type == LEVEL_FILE_TYPE_UNKNOWN)
2608 lfi->type = getFileTypeFromBasename(lfi->basename);
2610 if (lfi->type == LEVEL_FILE_TYPE_RND)
2611 lfi->type = getFileTypeFromMagicBytes(lfi->filename, lfi->type);
2614 static void setLevelFileInfo(struct LevelFileInfo *level_file_info, int nr)
2616 // always start with reliable default values
2617 setFileInfoToDefaults(level_file_info);
2619 level_file_info->nr = nr; // set requested level number
2621 determineLevelFileInfo_Filename(level_file_info);
2622 determineLevelFileInfo_Filetype(level_file_info);
2625 static void copyLevelFileInfo(struct LevelFileInfo *lfi_from,
2626 struct LevelFileInfo *lfi_to)
2628 lfi_to->nr = lfi_from->nr;
2629 lfi_to->type = lfi_from->type;
2630 lfi_to->packed = lfi_from->packed;
2632 setString(&lfi_to->basename, lfi_from->basename);
2633 setString(&lfi_to->filename, lfi_from->filename);
2636 // ----------------------------------------------------------------------------
2637 // functions for loading R'n'D level
2638 // ----------------------------------------------------------------------------
2640 int getMappedElement(int element)
2642 // remap some (historic, now obsolete) elements
2646 case EL_PLAYER_OBSOLETE:
2647 element = EL_PLAYER_1;
2650 case EL_KEY_OBSOLETE:
2654 case EL_EM_KEY_1_FILE_OBSOLETE:
2655 element = EL_EM_KEY_1;
2658 case EL_EM_KEY_2_FILE_OBSOLETE:
2659 element = EL_EM_KEY_2;
2662 case EL_EM_KEY_3_FILE_OBSOLETE:
2663 element = EL_EM_KEY_3;
2666 case EL_EM_KEY_4_FILE_OBSOLETE:
2667 element = EL_EM_KEY_4;
2670 case EL_ENVELOPE_OBSOLETE:
2671 element = EL_ENVELOPE_1;
2679 if (element >= NUM_FILE_ELEMENTS)
2681 Warn("invalid level element %d", element);
2683 element = EL_UNKNOWN;
2691 static int getMappedElementByVersion(int element, int game_version)
2693 // remap some elements due to certain game version
2695 if (game_version <= VERSION_IDENT(2,2,0,0))
2697 // map game font elements
2698 element = (element == EL_CHAR('[') ? EL_CHAR_AUMLAUT :
2699 element == EL_CHAR('\\') ? EL_CHAR_OUMLAUT :
2700 element == EL_CHAR(']') ? EL_CHAR_UUMLAUT :
2701 element == EL_CHAR('^') ? EL_CHAR_COPYRIGHT : element);
2704 if (game_version < VERSION_IDENT(3,0,0,0))
2706 // map Supaplex gravity tube elements
2707 element = (element == EL_SP_GRAVITY_PORT_LEFT ? EL_SP_PORT_LEFT :
2708 element == EL_SP_GRAVITY_PORT_RIGHT ? EL_SP_PORT_RIGHT :
2709 element == EL_SP_GRAVITY_PORT_UP ? EL_SP_PORT_UP :
2710 element == EL_SP_GRAVITY_PORT_DOWN ? EL_SP_PORT_DOWN :
2717 static int LoadLevel_VERS(File *file, int chunk_size, struct LevelInfo *level)
2719 level->file_version = getFileVersion(file);
2720 level->game_version = getFileVersion(file);
2725 static int LoadLevel_DATE(File *file, int chunk_size, struct LevelInfo *level)
2727 level->creation_date.year = getFile16BitBE(file);
2728 level->creation_date.month = getFile8Bit(file);
2729 level->creation_date.day = getFile8Bit(file);
2731 level->creation_date.src = DATE_SRC_LEVELFILE;
2736 static int LoadLevel_HEAD(File *file, int chunk_size, struct LevelInfo *level)
2738 int initial_player_stepsize;
2739 int initial_player_gravity;
2742 level->fieldx = getFile8Bit(file);
2743 level->fieldy = getFile8Bit(file);
2745 level->time = getFile16BitBE(file);
2746 level->gems_needed = getFile16BitBE(file);
2748 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
2749 level->name[i] = getFile8Bit(file);
2750 level->name[MAX_LEVEL_NAME_LEN] = 0;
2752 for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
2753 level->score[i] = getFile8Bit(file);
2755 level->num_yamyam_contents = STD_ELEMENT_CONTENTS;
2756 for (i = 0; i < STD_ELEMENT_CONTENTS; i++)
2757 for (y = 0; y < 3; y++)
2758 for (x = 0; x < 3; x++)
2759 level->yamyam_content[i].e[x][y] = getMappedElement(getFile8Bit(file));
2761 level->amoeba_speed = getFile8Bit(file);
2762 level->time_magic_wall = getFile8Bit(file);
2763 level->time_wheel = getFile8Bit(file);
2764 level->amoeba_content = getMappedElement(getFile8Bit(file));
2766 initial_player_stepsize = (getFile8Bit(file) == 1 ? STEPSIZE_FAST :
2769 for (i = 0; i < MAX_PLAYERS; i++)
2770 level->initial_player_stepsize[i] = initial_player_stepsize;
2772 initial_player_gravity = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2774 for (i = 0; i < MAX_PLAYERS; i++)
2775 level->initial_player_gravity[i] = initial_player_gravity;
2777 level->encoding_16bit_field = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2778 level->em_slippery_gems = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2780 level->use_custom_template = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2782 level->block_last_field = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2783 level->sp_block_last_field = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2784 level->can_move_into_acid_bits = getFile32BitBE(file);
2785 level->dont_collide_with_bits = getFile8Bit(file);
2787 level->use_spring_bug = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2788 level->use_step_counter = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2790 level->instant_relocation = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2791 level->can_pass_to_walkable = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2792 level->grow_into_diggable = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2794 level->game_engine_type = getFile8Bit(file);
2796 ReadUnusedBytesFromFile(file, LEVEL_CHUNK_HEAD_UNUSED);
2801 static int LoadLevel_NAME(File *file, int chunk_size, struct LevelInfo *level)
2805 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
2806 level->name[i] = getFile8Bit(file);
2807 level->name[MAX_LEVEL_NAME_LEN] = 0;
2812 static int LoadLevel_AUTH(File *file, int chunk_size, struct LevelInfo *level)
2816 for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
2817 level->author[i] = getFile8Bit(file);
2818 level->author[MAX_LEVEL_AUTHOR_LEN] = 0;
2823 static int LoadLevel_BODY(File *file, int chunk_size, struct LevelInfo *level)
2826 int chunk_size_expected = level->fieldx * level->fieldy;
2828 /* Note: "chunk_size" was wrong before version 2.0 when elements are
2829 stored with 16-bit encoding (and should be twice as big then).
2830 Even worse, playfield data was stored 16-bit when only yamyam content
2831 contained 16-bit elements and vice versa. */
2833 if (level->encoding_16bit_field && level->file_version >= FILE_VERSION_2_0)
2834 chunk_size_expected *= 2;
2836 if (chunk_size_expected != chunk_size)
2838 ReadUnusedBytesFromFile(file, chunk_size);
2839 return chunk_size_expected;
2842 for (y = 0; y < level->fieldy; y++)
2843 for (x = 0; x < level->fieldx; x++)
2844 level->field[x][y] =
2845 getMappedElement(level->encoding_16bit_field ? getFile16BitBE(file) :
2850 static int LoadLevel_CONT(File *file, int chunk_size, struct LevelInfo *level)
2853 int header_size = 4;
2854 int content_size = MAX_ELEMENT_CONTENTS * 3 * 3;
2855 int chunk_size_expected = header_size + content_size;
2857 /* Note: "chunk_size" was wrong before version 2.0 when elements are
2858 stored with 16-bit encoding (and should be twice as big then).
2859 Even worse, playfield data was stored 16-bit when only yamyam content
2860 contained 16-bit elements and vice versa. */
2862 if (level->encoding_16bit_field && level->file_version >= FILE_VERSION_2_0)
2863 chunk_size_expected += content_size;
2865 if (chunk_size_expected != chunk_size)
2867 ReadUnusedBytesFromFile(file, chunk_size);
2868 return chunk_size_expected;
2872 level->num_yamyam_contents = getFile8Bit(file);
2876 // correct invalid number of content fields -- should never happen
2877 if (level->num_yamyam_contents < 1 ||
2878 level->num_yamyam_contents > MAX_ELEMENT_CONTENTS)
2879 level->num_yamyam_contents = STD_ELEMENT_CONTENTS;
2881 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
2882 for (y = 0; y < 3; y++)
2883 for (x = 0; x < 3; x++)
2884 level->yamyam_content[i].e[x][y] =
2885 getMappedElement(level->encoding_16bit_field ?
2886 getFile16BitBE(file) : getFile8Bit(file));
2890 static int LoadLevel_CNT2(File *file, int chunk_size, struct LevelInfo *level)
2895 int content_array[MAX_ELEMENT_CONTENTS][3][3];
2897 element = getMappedElement(getFile16BitBE(file));
2898 num_contents = getFile8Bit(file);
2900 getFile8Bit(file); // content x size (unused)
2901 getFile8Bit(file); // content y size (unused)
2903 ReadUnusedBytesFromFile(file, LEVEL_CHUNK_CNT2_UNUSED);
2905 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
2906 for (y = 0; y < 3; y++)
2907 for (x = 0; x < 3; x++)
2908 content_array[i][x][y] = getMappedElement(getFile16BitBE(file));
2910 // correct invalid number of content fields -- should never happen
2911 if (num_contents < 1 || num_contents > MAX_ELEMENT_CONTENTS)
2912 num_contents = STD_ELEMENT_CONTENTS;
2914 if (element == EL_YAMYAM)
2916 level->num_yamyam_contents = num_contents;
2918 for (i = 0; i < num_contents; i++)
2919 for (y = 0; y < 3; y++)
2920 for (x = 0; x < 3; x++)
2921 level->yamyam_content[i].e[x][y] = content_array[i][x][y];
2923 else if (element == EL_BD_AMOEBA)
2925 level->amoeba_content = content_array[0][0][0];
2929 Warn("cannot load content for element '%d'", element);
2935 static int LoadLevel_CNT3(File *file, int chunk_size, struct LevelInfo *level)
2941 int chunk_size_expected;
2943 element = getMappedElement(getFile16BitBE(file));
2944 if (!IS_ENVELOPE(element))
2945 element = EL_ENVELOPE_1;
2947 envelope_nr = element - EL_ENVELOPE_1;
2949 envelope_len = getFile16BitBE(file);
2951 level->envelope[envelope_nr].xsize = getFile8Bit(file);
2952 level->envelope[envelope_nr].ysize = getFile8Bit(file);
2954 ReadUnusedBytesFromFile(file, LEVEL_CHUNK_CNT3_UNUSED);
2956 chunk_size_expected = LEVEL_CHUNK_CNT3_SIZE(envelope_len);
2957 if (chunk_size_expected != chunk_size)
2959 ReadUnusedBytesFromFile(file, chunk_size - LEVEL_CHUNK_CNT3_HEADER);
2960 return chunk_size_expected;
2963 for (i = 0; i < envelope_len; i++)
2964 level->envelope[envelope_nr].text[i] = getFile8Bit(file);
2969 static int LoadLevel_CUS1(File *file, int chunk_size, struct LevelInfo *level)
2971 int num_changed_custom_elements = getFile16BitBE(file);
2972 int chunk_size_expected = 2 + num_changed_custom_elements * 6;
2975 if (chunk_size_expected != chunk_size)
2977 ReadUnusedBytesFromFile(file, chunk_size - 2);
2978 return chunk_size_expected;
2981 for (i = 0; i < num_changed_custom_elements; i++)
2983 int element = getMappedElement(getFile16BitBE(file));
2984 int properties = getFile32BitBE(file);
2986 if (IS_CUSTOM_ELEMENT(element))
2987 element_info[element].properties[EP_BITFIELD_BASE_NR] = properties;
2989 Warn("invalid custom element number %d", element);
2991 // older game versions that wrote level files with CUS1 chunks used
2992 // different default push delay values (not yet stored in level file)
2993 element_info[element].push_delay_fixed = 2;
2994 element_info[element].push_delay_random = 8;
2997 level->file_has_custom_elements = TRUE;
3002 static int LoadLevel_CUS2(File *file, int chunk_size, struct LevelInfo *level)
3004 int num_changed_custom_elements = getFile16BitBE(file);
3005 int chunk_size_expected = 2 + num_changed_custom_elements * 4;
3008 if (chunk_size_expected != chunk_size)
3010 ReadUnusedBytesFromFile(file, chunk_size - 2);
3011 return chunk_size_expected;
3014 for (i = 0; i < num_changed_custom_elements; i++)
3016 int element = getMappedElement(getFile16BitBE(file));
3017 int custom_target_element = getMappedElement(getFile16BitBE(file));
3019 if (IS_CUSTOM_ELEMENT(element))
3020 element_info[element].change->target_element = custom_target_element;
3022 Warn("invalid custom element number %d", element);
3025 level->file_has_custom_elements = TRUE;
3030 static int LoadLevel_CUS3(File *file, int chunk_size, struct LevelInfo *level)
3032 int num_changed_custom_elements = getFile16BitBE(file);
3033 int chunk_size_expected = LEVEL_CHUNK_CUS3_SIZE(num_changed_custom_elements);
3036 if (chunk_size_expected != chunk_size)
3038 ReadUnusedBytesFromFile(file, chunk_size - 2);
3039 return chunk_size_expected;
3042 for (i = 0; i < num_changed_custom_elements; i++)
3044 int element = getMappedElement(getFile16BitBE(file));
3045 struct ElementInfo *ei = &element_info[element];
3046 unsigned int event_bits;
3048 if (!IS_CUSTOM_ELEMENT(element))
3050 Warn("invalid custom element number %d", element);
3052 element = EL_INTERNAL_DUMMY;
3055 for (j = 0; j < MAX_ELEMENT_NAME_LEN; j++)
3056 ei->description[j] = getFile8Bit(file);
3057 ei->description[MAX_ELEMENT_NAME_LEN] = 0;
3059 ei->properties[EP_BITFIELD_BASE_NR] = getFile32BitBE(file);
3061 // some free bytes for future properties and padding
3062 ReadUnusedBytesFromFile(file, 7);
3064 ei->use_gfx_element = getFile8Bit(file);
3065 ei->gfx_element_initial = getMappedElement(getFile16BitBE(file));
3067 ei->collect_score_initial = getFile8Bit(file);
3068 ei->collect_count_initial = getFile8Bit(file);
3070 ei->push_delay_fixed = getFile16BitBE(file);
3071 ei->push_delay_random = getFile16BitBE(file);
3072 ei->move_delay_fixed = getFile16BitBE(file);
3073 ei->move_delay_random = getFile16BitBE(file);
3075 ei->move_pattern = getFile16BitBE(file);
3076 ei->move_direction_initial = getFile8Bit(file);
3077 ei->move_stepsize = getFile8Bit(file);
3079 for (y = 0; y < 3; y++)
3080 for (x = 0; x < 3; x++)
3081 ei->content.e[x][y] = getMappedElement(getFile16BitBE(file));
3083 // bits 0 - 31 of "has_event[]"
3084 event_bits = getFile32BitBE(file);
3085 for (j = 0; j < MIN(NUM_CHANGE_EVENTS, 32); j++)
3086 if (event_bits & (1u << j))
3087 ei->change->has_event[j] = TRUE;
3089 ei->change->target_element = getMappedElement(getFile16BitBE(file));
3091 ei->change->delay_fixed = getFile16BitBE(file);
3092 ei->change->delay_random = getFile16BitBE(file);
3093 ei->change->delay_frames = getFile16BitBE(file);
3095 ei->change->initial_trigger_element = getMappedElement(getFile16BitBE(file));
3097 ei->change->explode = getFile8Bit(file);
3098 ei->change->use_target_content = getFile8Bit(file);
3099 ei->change->only_if_complete = getFile8Bit(file);
3100 ei->change->use_random_replace = getFile8Bit(file);
3102 ei->change->random_percentage = getFile8Bit(file);
3103 ei->change->replace_when = getFile8Bit(file);
3105 for (y = 0; y < 3; y++)
3106 for (x = 0; x < 3; x++)
3107 ei->change->target_content.e[x][y] =
3108 getMappedElement(getFile16BitBE(file));
3110 ei->slippery_type = getFile8Bit(file);
3112 // some free bytes for future properties and padding
3113 ReadUnusedBytesFromFile(file, LEVEL_CPART_CUS3_UNUSED);
3115 // mark that this custom element has been modified
3116 ei->modified_settings = TRUE;
3119 level->file_has_custom_elements = TRUE;
3124 static int LoadLevel_CUS4(File *file, int chunk_size, struct LevelInfo *level)
3126 struct ElementInfo *ei;
3127 int chunk_size_expected;
3131 // ---------- custom element base property values (96 bytes) ----------------
3133 element = getMappedElement(getFile16BitBE(file));
3135 if (!IS_CUSTOM_ELEMENT(element))
3137 Warn("invalid custom element number %d", element);
3139 ReadUnusedBytesFromFile(file, chunk_size - 2);
3144 ei = &element_info[element];
3146 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
3147 ei->description[i] = getFile8Bit(file);
3148 ei->description[MAX_ELEMENT_NAME_LEN] = 0;
3150 ei->properties[EP_BITFIELD_BASE_NR] = getFile32BitBE(file);
3152 ReadUnusedBytesFromFile(file, 4); // reserved for more base properties
3154 ei->num_change_pages = getFile8Bit(file);
3156 chunk_size_expected = LEVEL_CHUNK_CUS4_SIZE(ei->num_change_pages);
3157 if (chunk_size_expected != chunk_size)
3159 ReadUnusedBytesFromFile(file, chunk_size - 43);
3160 return chunk_size_expected;
3163 ei->ce_value_fixed_initial = getFile16BitBE(file);
3164 ei->ce_value_random_initial = getFile16BitBE(file);
3165 ei->use_last_ce_value = getFile8Bit(file);
3167 ei->use_gfx_element = getFile8Bit(file);
3168 ei->gfx_element_initial = getMappedElement(getFile16BitBE(file));
3170 ei->collect_score_initial = getFile8Bit(file);
3171 ei->collect_count_initial = getFile8Bit(file);
3173 ei->drop_delay_fixed = getFile8Bit(file);
3174 ei->push_delay_fixed = getFile8Bit(file);
3175 ei->drop_delay_random = getFile8Bit(file);
3176 ei->push_delay_random = getFile8Bit(file);
3177 ei->move_delay_fixed = getFile16BitBE(file);
3178 ei->move_delay_random = getFile16BitBE(file);
3180 // bits 0 - 15 of "move_pattern" ...
3181 ei->move_pattern = getFile16BitBE(file);
3182 ei->move_direction_initial = getFile8Bit(file);
3183 ei->move_stepsize = getFile8Bit(file);
3185 ei->slippery_type = getFile8Bit(file);
3187 for (y = 0; y < 3; y++)
3188 for (x = 0; x < 3; x++)
3189 ei->content.e[x][y] = getMappedElement(getFile16BitBE(file));
3191 ei->move_enter_element = getMappedElement(getFile16BitBE(file));
3192 ei->move_leave_element = getMappedElement(getFile16BitBE(file));
3193 ei->move_leave_type = getFile8Bit(file);
3195 // ... bits 16 - 31 of "move_pattern" (not nice, but downward compatible)
3196 ei->move_pattern |= (getFile16BitBE(file) << 16);
3198 ei->access_direction = getFile8Bit(file);
3200 ei->explosion_delay = getFile8Bit(file);
3201 ei->ignition_delay = getFile8Bit(file);
3202 ei->explosion_type = getFile8Bit(file);
3204 // some free bytes for future custom property values and padding
3205 ReadUnusedBytesFromFile(file, 1);
3207 // ---------- change page property values (48 bytes) ------------------------
3209 setElementChangePages(ei, ei->num_change_pages);
3211 for (i = 0; i < ei->num_change_pages; i++)
3213 struct ElementChangeInfo *change = &ei->change_page[i];
3214 unsigned int event_bits;
3216 // always start with reliable default values
3217 setElementChangeInfoToDefaults(change);
3219 // bits 0 - 31 of "has_event[]" ...
3220 event_bits = getFile32BitBE(file);
3221 for (j = 0; j < MIN(NUM_CHANGE_EVENTS, 32); j++)
3222 if (event_bits & (1u << j))
3223 change->has_event[j] = TRUE;
3225 change->target_element = getMappedElement(getFile16BitBE(file));
3227 change->delay_fixed = getFile16BitBE(file);
3228 change->delay_random = getFile16BitBE(file);
3229 change->delay_frames = getFile16BitBE(file);
3231 change->initial_trigger_element = getMappedElement(getFile16BitBE(file));
3233 change->explode = getFile8Bit(file);
3234 change->use_target_content = getFile8Bit(file);
3235 change->only_if_complete = getFile8Bit(file);
3236 change->use_random_replace = getFile8Bit(file);
3238 change->random_percentage = getFile8Bit(file);
3239 change->replace_when = getFile8Bit(file);
3241 for (y = 0; y < 3; y++)
3242 for (x = 0; x < 3; x++)
3243 change->target_content.e[x][y]= getMappedElement(getFile16BitBE(file));
3245 change->can_change = getFile8Bit(file);
3247 change->trigger_side = getFile8Bit(file);
3249 change->trigger_player = getFile8Bit(file);
3250 change->trigger_page = getFile8Bit(file);
3252 change->trigger_page = (change->trigger_page == CH_PAGE_ANY_FILE ?
3253 CH_PAGE_ANY : (1 << change->trigger_page));
3255 change->has_action = getFile8Bit(file);
3256 change->action_type = getFile8Bit(file);
3257 change->action_mode = getFile8Bit(file);
3258 change->action_arg = getFile16BitBE(file);
3260 // ... bits 32 - 39 of "has_event[]" (not nice, but downward compatible)
3261 event_bits = getFile8Bit(file);
3262 for (j = 32; j < NUM_CHANGE_EVENTS; j++)
3263 if (event_bits & (1u << (j - 32)))
3264 change->has_event[j] = TRUE;
3267 // mark this custom element as modified
3268 ei->modified_settings = TRUE;
3270 level->file_has_custom_elements = TRUE;
3275 static int LoadLevel_GRP1(File *file, int chunk_size, struct LevelInfo *level)
3277 struct ElementInfo *ei;
3278 struct ElementGroupInfo *group;
3282 element = getMappedElement(getFile16BitBE(file));
3284 if (!IS_GROUP_ELEMENT(element))
3286 Warn("invalid group element number %d", element);
3288 ReadUnusedBytesFromFile(file, chunk_size - 2);
3293 ei = &element_info[element];
3295 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
3296 ei->description[i] = getFile8Bit(file);
3297 ei->description[MAX_ELEMENT_NAME_LEN] = 0;
3299 group = element_info[element].group;
3301 group->num_elements = getFile8Bit(file);
3303 ei->use_gfx_element = getFile8Bit(file);
3304 ei->gfx_element_initial = getMappedElement(getFile16BitBE(file));
3306 group->choice_mode = getFile8Bit(file);
3308 // some free bytes for future values and padding
3309 ReadUnusedBytesFromFile(file, 3);
3311 for (i = 0; i < MAX_ELEMENTS_IN_GROUP; i++)
3312 group->element[i] = getMappedElement(getFile16BitBE(file));
3314 // mark this group element as modified
3315 element_info[element].modified_settings = TRUE;
3317 level->file_has_custom_elements = TRUE;
3322 static int LoadLevel_MicroChunk(File *file, struct LevelFileConfigInfo *conf,
3323 int element, int real_element)
3325 int micro_chunk_size = 0;
3326 int conf_type = getFile8Bit(file);
3327 int byte_mask = conf_type & CONF_MASK_BYTES;
3328 boolean element_found = FALSE;
3331 micro_chunk_size += 1;
3333 if (byte_mask == CONF_MASK_MULTI_BYTES)
3335 int num_bytes = getFile16BitBE(file);
3336 byte *buffer = checked_malloc(num_bytes);
3338 ReadBytesFromFile(file, buffer, num_bytes);
3340 for (i = 0; conf[i].data_type != -1; i++)
3342 if (conf[i].element == element &&
3343 conf[i].conf_type == conf_type)
3345 int data_type = conf[i].data_type;
3346 int num_entities = num_bytes / CONF_ENTITY_NUM_BYTES(data_type);
3347 int max_num_entities = conf[i].max_num_entities;
3349 if (num_entities > max_num_entities)
3351 Warn("truncating number of entities for element %d from %d to %d",
3352 element, num_entities, max_num_entities);
3354 num_entities = max_num_entities;
3357 if (num_entities == 0 && (data_type == TYPE_ELEMENT_LIST ||
3358 data_type == TYPE_CONTENT_LIST))
3360 // for element and content lists, zero entities are not allowed
3361 Warn("found empty list of entities for element %d", element);
3363 // do not set "num_entities" here to prevent reading behind buffer
3365 *(int *)(conf[i].num_entities) = 1; // at least one is required
3369 *(int *)(conf[i].num_entities) = num_entities;
3372 element_found = TRUE;
3374 if (data_type == TYPE_STRING)
3376 char *string = (char *)(conf[i].value);
3379 for (j = 0; j < max_num_entities; j++)
3380 string[j] = (j < num_entities ? buffer[j] : '\0');
3382 else if (data_type == TYPE_ELEMENT_LIST)
3384 int *element_array = (int *)(conf[i].value);
3387 for (j = 0; j < num_entities; j++)
3389 getMappedElement(CONF_ELEMENTS_ELEMENT(buffer, j));
3391 else if (data_type == TYPE_CONTENT_LIST)
3393 struct Content *content= (struct Content *)(conf[i].value);
3396 for (c = 0; c < num_entities; c++)
3397 for (y = 0; y < 3; y++)
3398 for (x = 0; x < 3; x++)
3399 content[c].e[x][y] =
3400 getMappedElement(CONF_CONTENTS_ELEMENT(buffer, c, x, y));
3403 element_found = FALSE;
3409 checked_free(buffer);
3411 micro_chunk_size += 2 + num_bytes;
3413 else // constant size configuration data (1, 2 or 4 bytes)
3415 int value = (byte_mask == CONF_MASK_1_BYTE ? getFile8Bit (file) :
3416 byte_mask == CONF_MASK_2_BYTE ? getFile16BitBE(file) :
3417 byte_mask == CONF_MASK_4_BYTE ? getFile32BitBE(file) : 0);
3419 for (i = 0; conf[i].data_type != -1; i++)
3421 if (conf[i].element == element &&
3422 conf[i].conf_type == conf_type)
3424 int data_type = conf[i].data_type;
3426 if (data_type == TYPE_ELEMENT)
3427 value = getMappedElement(value);
3429 if (data_type == TYPE_BOOLEAN)
3430 *(boolean *)(conf[i].value) = (value ? TRUE : FALSE);
3432 *(int *) (conf[i].value) = value;
3434 element_found = TRUE;
3440 micro_chunk_size += CONF_VALUE_NUM_BYTES(byte_mask);
3445 char *error_conf_chunk_bytes =
3446 (byte_mask == CONF_MASK_1_BYTE ? "CONF_VALUE_8_BIT" :
3447 byte_mask == CONF_MASK_2_BYTE ? "CONF_VALUE_16_BIT" :
3448 byte_mask == CONF_MASK_4_BYTE ? "CONF_VALUE_32_BIT" :"CONF_VALUE_BYTES");
3449 int error_conf_chunk_token = conf_type & CONF_MASK_TOKEN;
3450 int error_element = real_element;
3452 Warn("cannot load micro chunk '%s(%d)' value for element %d ['%s']",
3453 error_conf_chunk_bytes, error_conf_chunk_token,
3454 error_element, EL_NAME(error_element));
3457 return micro_chunk_size;
3460 static int LoadLevel_INFO(File *file, int chunk_size, struct LevelInfo *level)
3462 int real_chunk_size = 0;
3464 li = *level; // copy level data into temporary buffer
3466 while (!checkEndOfFile(file))
3468 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_INFO, -1, -1);
3470 if (real_chunk_size >= chunk_size)
3474 *level = li; // copy temporary buffer back to level data
3476 return real_chunk_size;
3479 static int LoadLevel_CONF(File *file, int chunk_size, struct LevelInfo *level)
3481 int real_chunk_size = 0;
3483 li = *level; // copy level data into temporary buffer
3485 while (!checkEndOfFile(file))
3487 int element = getMappedElement(getFile16BitBE(file));
3489 real_chunk_size += 2;
3490 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_CONF,
3492 if (real_chunk_size >= chunk_size)
3496 *level = li; // copy temporary buffer back to level data
3498 return real_chunk_size;
3501 static int LoadLevel_ELEM(File *file, int chunk_size, struct LevelInfo *level)
3503 int real_chunk_size = 0;
3505 li = *level; // copy level data into temporary buffer
3507 while (!checkEndOfFile(file))
3509 int element = getMappedElement(getFile16BitBE(file));
3511 real_chunk_size += 2;
3512 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_ELEM,
3514 if (real_chunk_size >= chunk_size)
3518 *level = li; // copy temporary buffer back to level data
3520 return real_chunk_size;
3523 static int LoadLevel_NOTE(File *file, int chunk_size, struct LevelInfo *level)
3525 int element = getMappedElement(getFile16BitBE(file));
3526 int envelope_nr = element - EL_ENVELOPE_1;
3527 int real_chunk_size = 2;
3529 xx_envelope = level->envelope[envelope_nr]; // copy into temporary buffer
3531 while (!checkEndOfFile(file))
3533 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_NOTE,
3536 if (real_chunk_size >= chunk_size)
3540 level->envelope[envelope_nr] = xx_envelope; // copy from temporary buffer
3542 return real_chunk_size;
3545 static int LoadLevel_CUSX(File *file, int chunk_size, struct LevelInfo *level)
3547 int element = getMappedElement(getFile16BitBE(file));
3548 int real_chunk_size = 2;
3549 struct ElementInfo *ei = &element_info[element];
3552 xx_ei = *ei; // copy element data into temporary buffer
3554 xx_ei.num_change_pages = -1;
3556 while (!checkEndOfFile(file))
3558 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_CUSX_base,
3560 if (xx_ei.num_change_pages != -1)
3563 if (real_chunk_size >= chunk_size)
3569 if (ei->num_change_pages == -1)
3571 Warn("LoadLevel_CUSX(): missing 'num_change_pages' for '%s'",
3574 ei->num_change_pages = 1;
3576 setElementChangePages(ei, 1);
3577 setElementChangeInfoToDefaults(ei->change);
3579 return real_chunk_size;
3582 // initialize number of change pages stored for this custom element
3583 setElementChangePages(ei, ei->num_change_pages);
3584 for (i = 0; i < ei->num_change_pages; i++)
3585 setElementChangeInfoToDefaults(&ei->change_page[i]);
3587 // start with reading properties for the first change page
3588 xx_current_change_page = 0;
3590 while (!checkEndOfFile(file))
3592 // level file might contain invalid change page number
3593 if (xx_current_change_page >= ei->num_change_pages)
3596 struct ElementChangeInfo *change = &ei->change_page[xx_current_change_page];
3598 xx_change = *change; // copy change data into temporary buffer
3600 resetEventBits(); // reset bits; change page might have changed
3602 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_CUSX_change,
3605 *change = xx_change;
3607 setEventFlagsFromEventBits(change);
3609 if (real_chunk_size >= chunk_size)
3613 level->file_has_custom_elements = TRUE;
3615 return real_chunk_size;
3618 static int LoadLevel_GRPX(File *file, int chunk_size, struct LevelInfo *level)
3620 int element = getMappedElement(getFile16BitBE(file));
3621 int real_chunk_size = 2;
3622 struct ElementInfo *ei = &element_info[element];
3623 struct ElementGroupInfo *group = ei->group;
3628 xx_ei = *ei; // copy element data into temporary buffer
3629 xx_group = *group; // copy group data into temporary buffer
3631 while (!checkEndOfFile(file))
3633 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_GRPX,
3636 if (real_chunk_size >= chunk_size)
3643 level->file_has_custom_elements = TRUE;
3645 return real_chunk_size;
3648 static int LoadLevel_EMPX(File *file, int chunk_size, struct LevelInfo *level)
3650 int element = getMappedElement(getFile16BitBE(file));
3651 int real_chunk_size = 2;
3652 struct ElementInfo *ei = &element_info[element];
3654 xx_ei = *ei; // copy element data into temporary buffer
3656 while (!checkEndOfFile(file))
3658 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_EMPX,
3661 if (real_chunk_size >= chunk_size)
3667 level->file_has_custom_elements = TRUE;
3669 return real_chunk_size;
3672 static void LoadLevelFromFileInfo_RND(struct LevelInfo *level,
3673 struct LevelFileInfo *level_file_info,
3674 boolean level_info_only)
3676 char *filename = level_file_info->filename;
3677 char cookie[MAX_LINE_LEN];
3678 char chunk_name[CHUNK_ID_LEN + 1];
3682 if (!(file = openFile(filename, MODE_READ)))
3684 level->no_valid_file = TRUE;
3685 level->no_level_file = TRUE;
3687 if (level_info_only)
3690 Warn("cannot read level '%s' -- using empty level", filename);
3692 if (!setup.editor.use_template_for_new_levels)
3695 // if level file not found, try to initialize level data from template
3696 filename = getGlobalLevelTemplateFilename();
3698 if (!(file = openFile(filename, MODE_READ)))
3701 // default: for empty levels, use level template for custom elements
3702 level->use_custom_template = TRUE;
3704 level->no_valid_file = FALSE;
3707 getFileChunkBE(file, chunk_name, NULL);
3708 if (strEqual(chunk_name, "RND1"))
3710 getFile32BitBE(file); // not used
3712 getFileChunkBE(file, chunk_name, NULL);
3713 if (!strEqual(chunk_name, "CAVE"))
3715 level->no_valid_file = TRUE;
3717 Warn("unknown format of level file '%s'", filename);
3724 else // check for pre-2.0 file format with cookie string
3726 strcpy(cookie, chunk_name);
3727 if (getStringFromFile(file, &cookie[4], MAX_LINE_LEN - 4) == NULL)
3729 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
3730 cookie[strlen(cookie) - 1] = '\0';
3732 if (!checkCookieString(cookie, LEVEL_COOKIE_TMPL))
3734 level->no_valid_file = TRUE;
3736 Warn("unknown format of level file '%s'", filename);
3743 if ((level->file_version = getFileVersionFromCookieString(cookie)) == -1)
3745 level->no_valid_file = TRUE;
3747 Warn("unsupported version of level file '%s'", filename);
3754 // pre-2.0 level files have no game version, so use file version here
3755 level->game_version = level->file_version;
3758 if (level->file_version < FILE_VERSION_1_2)
3760 // level files from versions before 1.2.0 without chunk structure
3761 LoadLevel_HEAD(file, LEVEL_CHUNK_HEAD_SIZE, level);
3762 LoadLevel_BODY(file, level->fieldx * level->fieldy, level);
3770 int (*loader)(File *, int, struct LevelInfo *);
3774 { "VERS", LEVEL_CHUNK_VERS_SIZE, LoadLevel_VERS },
3775 { "DATE", LEVEL_CHUNK_DATE_SIZE, LoadLevel_DATE },
3776 { "HEAD", LEVEL_CHUNK_HEAD_SIZE, LoadLevel_HEAD },
3777 { "NAME", LEVEL_CHUNK_NAME_SIZE, LoadLevel_NAME },
3778 { "AUTH", LEVEL_CHUNK_AUTH_SIZE, LoadLevel_AUTH },
3779 { "INFO", -1, LoadLevel_INFO },
3780 { "BODY", -1, LoadLevel_BODY },
3781 { "CONT", -1, LoadLevel_CONT },
3782 { "CNT2", LEVEL_CHUNK_CNT2_SIZE, LoadLevel_CNT2 },
3783 { "CNT3", -1, LoadLevel_CNT3 },
3784 { "CUS1", -1, LoadLevel_CUS1 },
3785 { "CUS2", -1, LoadLevel_CUS2 },
3786 { "CUS3", -1, LoadLevel_CUS3 },
3787 { "CUS4", -1, LoadLevel_CUS4 },
3788 { "GRP1", -1, LoadLevel_GRP1 },
3789 { "CONF", -1, LoadLevel_CONF },
3790 { "ELEM", -1, LoadLevel_ELEM },
3791 { "NOTE", -1, LoadLevel_NOTE },
3792 { "CUSX", -1, LoadLevel_CUSX },
3793 { "GRPX", -1, LoadLevel_GRPX },
3794 { "EMPX", -1, LoadLevel_EMPX },
3799 while (getFileChunkBE(file, chunk_name, &chunk_size))
3803 while (chunk_info[i].name != NULL &&
3804 !strEqual(chunk_name, chunk_info[i].name))
3807 if (chunk_info[i].name == NULL)
3809 Warn("unknown chunk '%s' in level file '%s'",
3810 chunk_name, filename);
3812 ReadUnusedBytesFromFile(file, chunk_size);
3814 else if (chunk_info[i].size != -1 &&
3815 chunk_info[i].size != chunk_size)
3817 Warn("wrong size (%d) of chunk '%s' in level file '%s'",
3818 chunk_size, chunk_name, filename);
3820 ReadUnusedBytesFromFile(file, chunk_size);
3824 // call function to load this level chunk
3825 int chunk_size_expected =
3826 (chunk_info[i].loader)(file, chunk_size, level);
3828 if (chunk_size_expected < 0)
3830 Warn("error reading chunk '%s' in level file '%s'",
3831 chunk_name, filename);
3836 // the size of some chunks cannot be checked before reading other
3837 // chunks first (like "HEAD" and "BODY") that contain some header
3838 // information, so check them here
3839 if (chunk_size_expected != chunk_size)
3841 Warn("wrong size (%d) of chunk '%s' in level file '%s'",
3842 chunk_size, chunk_name, filename);
3854 // ----------------------------------------------------------------------------
3855 // functions for loading BD level
3856 // ----------------------------------------------------------------------------
3858 static void CopyNativeLevel_RND_to_BD(struct LevelInfo *level)
3860 struct LevelInfo_BD *level_bd = level->native_bd_level;
3861 GdCave *cave = NULL; // will be changed below
3862 int cave_w = MIN(level->fieldx, MAX_PLAYFIELD_WIDTH);
3863 int cave_h = MIN(level->fieldy, MAX_PLAYFIELD_HEIGHT);
3866 setLevelInfoToDefaults_BD_Ext(cave_w, cave_h);
3868 // cave and map newly allocated when set to defaults above
3869 cave = level_bd->cave;
3872 cave->intermission = level->bd_intermission;
3875 cave->level_time[0] = level->time;
3876 cave->level_diamonds[0] = level->gems_needed;
3879 cave->scheduling = level->bd_scheduling_type;
3880 cave->pal_timing = level->bd_pal_timing;
3881 cave->level_speed[0] = level->bd_cycle_delay_ms;
3882 cave->level_ckdelay[0] = level->bd_cycle_delay_c64;
3883 cave->level_hatching_delay_frame[0] = level->bd_hatching_delay_cycles;
3884 cave->level_hatching_delay_time[0] = level->bd_hatching_delay_seconds;
3887 cave->level_timevalue[0] = level->score[SC_TIME_BONUS];
3888 cave->diamond_value = level->score[SC_EMERALD];
3889 cave->extra_diamond_value = level->score[SC_DIAMOND_EXTRA];
3891 // compatibility settings
3892 cave->lineshift = level->bd_line_shifting_borders;
3893 cave->border_scan_first_and_last = level->bd_scan_first_and_last_row;
3894 cave->short_explosions = level->bd_short_explosions;
3895 cave->gravity_affects_all = level->bd_gravity_affects_all;
3897 // player properties
3898 cave->diagonal_movements = level->bd_diagonal_movements;
3899 cave->active_is_first_found = level->bd_topmost_player_active;
3900 cave->pushing_stone_prob = level->bd_pushing_prob * 10000;
3901 cave->pushing_stone_prob_sweet = level->bd_pushing_prob_with_sweet * 10000;
3902 cave->mega_stones_pushable_with_sweet = level->bd_push_mega_rock_with_sweet;
3904 // element properties
3905 cave->level_bonus_time[0] = level->bd_clock_extra_time;
3906 cave->level_magic_wall_time[0] = level->time_magic_wall;
3907 cave->magic_timer_wait_for_hatching = level->bd_magic_wall_wait_hatching;
3908 cave->magic_wall_stops_amoeba = level->bd_magic_wall_stops_amoeba;
3909 cave->amoeba_timer_wait_for_hatching = level->bd_amoeba_wait_for_hatching;
3910 cave->amoeba_timer_started_immediately= level->bd_amoeba_start_immediately;
3911 cave->amoeba_2_explodes_by_amoeba = level->bd_amoeba_2_explode_by_amoeba;
3912 cave->level_amoeba_threshold[0] = level->bd_amoeba_threshold_too_big;
3913 cave->level_amoeba_time[0] = level->bd_amoeba_slow_growth_time;
3914 cave->amoeba_growth_prob = level->bd_amoeba_slow_growth_rate * 10000;
3915 cave->amoeba_fast_growth_prob = level->bd_amoeba_fast_growth_rate * 10000;
3916 cave->level_amoeba_2_threshold[0] = level->bd_amoeba_2_threshold_too_big;
3917 cave->level_amoeba_2_time[0] = level->bd_amoeba_2_slow_growth_time;
3918 cave->amoeba_2_growth_prob = level->bd_amoeba_2_slow_growth_rate * 10000;
3919 cave->amoeba_2_fast_growth_prob = level->bd_amoeba_2_fast_growth_rate * 10000;
3921 cave->amoeba_too_big_effect = map_element_RND_to_BD(level->bd_amoeba_content_too_big);
3922 cave->amoeba_enclosed_effect = map_element_RND_to_BD(level->bd_amoeba_content_enclosed);
3923 cave->amoeba_2_too_big_effect = map_element_RND_to_BD(level->bd_amoeba_2_content_too_big);
3924 cave->amoeba_2_enclosed_effect = map_element_RND_to_BD(level->bd_amoeba_2_content_enclosed);
3925 cave->amoeba_2_explosion_effect = map_element_RND_to_BD(level->bd_amoeba_2_content_exploding);
3926 cave->amoeba_2_looks_like = map_element_RND_to_BD(level->bd_amoeba_2_content_looks_like);
3929 strncpy(cave->name, level->name, sizeof(GdString));
3930 cave->name[sizeof(GdString) - 1] = '\0';
3932 // playfield elements
3933 for (x = 0; x < cave->w; x++)
3934 for (y = 0; y < cave->h; y++)
3935 cave->map[y][x] = map_element_RND_to_BD(level->field[x][y]);
3938 static void CopyNativeLevel_BD_to_RND(struct LevelInfo *level)
3940 struct LevelInfo_BD *level_bd = level->native_bd_level;
3941 GdCave *cave = level_bd->cave;
3942 int bd_level_nr = level_bd->level_nr;
3945 level->fieldx = MIN(cave->w, MAX_LEV_FIELDX);
3946 level->fieldy = MIN(cave->h, MAX_LEV_FIELDY);
3949 level->bd_intermission = cave->intermission;
3952 level->time = cave->level_time[bd_level_nr];
3953 level->gems_needed = cave->level_diamonds[bd_level_nr];
3956 level->bd_scheduling_type = cave->scheduling;
3957 level->bd_pal_timing = cave->pal_timing;
3958 level->bd_cycle_delay_ms = cave->level_speed[bd_level_nr];
3959 level->bd_cycle_delay_c64 = cave->level_ckdelay[bd_level_nr];
3960 level->bd_hatching_delay_cycles = cave->level_hatching_delay_frame[bd_level_nr];
3961 level->bd_hatching_delay_seconds = cave->level_hatching_delay_time[bd_level_nr];
3964 level->score[SC_TIME_BONUS] = cave->level_timevalue[bd_level_nr];
3965 level->score[SC_EMERALD] = cave->diamond_value;
3966 level->score[SC_DIAMOND_EXTRA] = cave->extra_diamond_value;
3968 // compatibility settings
3969 level->bd_line_shifting_borders = cave->lineshift;
3970 level->bd_scan_first_and_last_row = cave->border_scan_first_and_last;
3971 level->bd_short_explosions = cave->short_explosions;
3972 level->bd_gravity_affects_all = cave->gravity_affects_all;
3974 // player properties
3975 level->bd_diagonal_movements = cave->diagonal_movements;
3976 level->bd_topmost_player_active = cave->active_is_first_found;
3977 level->bd_pushing_prob = cave->pushing_stone_prob / 10000;
3978 level->bd_pushing_prob_with_sweet = cave->pushing_stone_prob_sweet / 10000;
3979 level->bd_push_mega_rock_with_sweet = cave->mega_stones_pushable_with_sweet;
3981 // element properties
3982 level->bd_clock_extra_time = cave->level_bonus_time[0];
3983 level->time_magic_wall = cave->level_magic_wall_time[bd_level_nr];
3984 level->bd_magic_wall_wait_hatching = cave->magic_timer_wait_for_hatching;
3985 level->bd_magic_wall_stops_amoeba = cave->magic_wall_stops_amoeba;
3986 level->bd_amoeba_wait_for_hatching = cave->amoeba_timer_wait_for_hatching;
3987 level->bd_amoeba_start_immediately = cave->amoeba_timer_started_immediately;
3988 level->bd_amoeba_2_explode_by_amoeba = cave->amoeba_2_explodes_by_amoeba;
3989 level->bd_amoeba_threshold_too_big = cave->level_amoeba_threshold[0];
3990 level->bd_amoeba_slow_growth_time = cave->level_amoeba_time[0];
3991 level->bd_amoeba_slow_growth_rate = cave->amoeba_growth_prob / 10000;
3992 level->bd_amoeba_fast_growth_rate = cave->amoeba_fast_growth_prob / 10000;
3993 level->bd_amoeba_2_threshold_too_big = cave->level_amoeba_2_threshold[0];
3994 level->bd_amoeba_2_slow_growth_time = cave->level_amoeba_2_time[0];
3995 level->bd_amoeba_2_slow_growth_rate = cave->amoeba_2_growth_prob / 10000;
3996 level->bd_amoeba_2_fast_growth_rate = cave->amoeba_2_fast_growth_prob / 10000;
3998 level->bd_amoeba_content_too_big = map_element_BD_to_RND(cave->amoeba_too_big_effect);
3999 level->bd_amoeba_content_enclosed = map_element_BD_to_RND(cave->amoeba_enclosed_effect);
4000 level->bd_amoeba_2_content_too_big = map_element_BD_to_RND(cave->amoeba_2_too_big_effect);
4001 level->bd_amoeba_2_content_enclosed = map_element_BD_to_RND(cave->amoeba_2_enclosed_effect);
4002 level->bd_amoeba_2_content_exploding = map_element_BD_to_RND(cave->amoeba_2_explosion_effect);
4003 level->bd_amoeba_2_content_looks_like = map_element_BD_to_RND(cave->amoeba_2_looks_like);
4006 char *cave_name = getStringPrint("%s / %d", cave->name, bd_level_nr + 1);
4008 strncpy(level->name, cave_name, MAX_LEVEL_NAME_LEN);
4009 level->name[MAX_LEVEL_NAME_LEN] = '\0';
4011 // playfield elements
4012 for (x = 0; x < level->fieldx; x++)
4013 for (y = 0; y < level->fieldy; y++)
4014 level->field[x][y] = map_element_BD_to_RND(cave->map[y][x]);
4016 checked_free(cave_name);
4019 static void setTapeInfoToDefaults(void);
4021 static void CopyNativeTape_BD_to_RND(struct LevelInfo *level)
4023 struct LevelInfo_BD *level_bd = level->native_bd_level;
4024 GdCave *cave = level_bd->cave;
4025 GdReplay *replay = level_bd->replay;
4031 // always start with reliable default values
4032 setTapeInfoToDefaults();
4034 tape.level_nr = level_nr; // (currently not used)
4035 tape.random_seed = replay->seed;
4037 TapeSetDateFromIsoDateString(replay->date);
4040 tape.pos[tape.counter].delay = 0;
4042 tape.bd_replay = TRUE;
4044 // all time calculations only used to display approximate tape time
4045 int cave_speed = cave->speed;
4046 int milliseconds_game = 0;
4047 int milliseconds_elapsed = 20;
4049 for (i = 0; i < replay->movements->len; i++)
4051 int replay_action = replay->movements->data[i];
4052 int tape_action = map_action_BD_to_RND(replay_action);
4053 byte action[MAX_TAPE_ACTIONS] = { tape_action };
4054 boolean success = 0;
4058 success = TapeAddAction(action);
4060 milliseconds_game += milliseconds_elapsed;
4062 if (milliseconds_game >= cave_speed)
4064 milliseconds_game -= cave_speed;
4071 tape.pos[tape.counter].delay = 0;
4072 tape.pos[tape.counter].action[0] = 0;
4076 Warn("BD replay truncated: size exceeds maximum tape size %d", MAX_TAPE_LEN);
4082 TapeHaltRecording();
4086 // ----------------------------------------------------------------------------
4087 // functions for loading EM level
4088 // ----------------------------------------------------------------------------
4090 static void CopyNativeLevel_RND_to_EM(struct LevelInfo *level)
4092 static int ball_xy[8][2] =
4103 struct LevelInfo_EM *level_em = level->native_em_level;
4104 struct CAVE *cav = level_em->cav;
4107 cav->width = MIN(level->fieldx, MAX_PLAYFIELD_WIDTH);
4108 cav->height = MIN(level->fieldy, MAX_PLAYFIELD_HEIGHT);
4110 cav->time_seconds = level->time;
4111 cav->gems_needed = level->gems_needed;
4113 cav->emerald_score = level->score[SC_EMERALD];
4114 cav->diamond_score = level->score[SC_DIAMOND];
4115 cav->alien_score = level->score[SC_ROBOT];
4116 cav->tank_score = level->score[SC_SPACESHIP];
4117 cav->bug_score = level->score[SC_BUG];
4118 cav->eater_score = level->score[SC_YAMYAM];
4119 cav->nut_score = level->score[SC_NUT];
4120 cav->dynamite_score = level->score[SC_DYNAMITE];
4121 cav->key_score = level->score[SC_KEY];
4122 cav->exit_score = level->score[SC_TIME_BONUS];
4124 cav->num_eater_arrays = level->num_yamyam_contents;
4126 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
4127 for (y = 0; y < 3; y++)
4128 for (x = 0; x < 3; x++)
4129 cav->eater_array[i][y * 3 + x] =
4130 map_element_RND_to_EM_cave(level->yamyam_content[i].e[x][y]);
4132 cav->amoeba_time = level->amoeba_speed;
4133 cav->wonderwall_time = level->time_magic_wall;
4134 cav->wheel_time = level->time_wheel;
4136 cav->android_move_time = level->android_move_time;
4137 cav->android_clone_time = level->android_clone_time;
4138 cav->ball_random = level->ball_random;
4139 cav->ball_active = level->ball_active_initial;
4140 cav->ball_time = level->ball_time;
4141 cav->num_ball_arrays = level->num_ball_contents;
4143 cav->lenses_score = level->lenses_score;
4144 cav->magnify_score = level->magnify_score;
4145 cav->slurp_score = level->slurp_score;
4147 cav->lenses_time = level->lenses_time;
4148 cav->magnify_time = level->magnify_time;
4150 cav->wind_time = 9999;
4151 cav->wind_direction =
4152 map_direction_RND_to_EM(level->wind_direction_initial);
4154 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
4155 for (j = 0; j < 8; j++)
4156 cav->ball_array[i][j] =
4157 map_element_RND_to_EM_cave(level->ball_content[i].
4158 e[ball_xy[j][0]][ball_xy[j][1]]);
4160 map_android_clone_elements_RND_to_EM(level);
4162 // first fill the complete playfield with the empty space element
4163 for (y = 0; y < EM_MAX_CAVE_HEIGHT; y++)
4164 for (x = 0; x < EM_MAX_CAVE_WIDTH; x++)
4165 cav->cave[x][y] = Cblank;
4167 // then copy the real level contents from level file into the playfield
4168 for (y = 0; y < cav->height; y++) for (x = 0; x < cav->width; x++)
4170 int new_element = map_element_RND_to_EM_cave(level->field[x][y]);
4172 if (level->field[x][y] == EL_AMOEBA_DEAD)
4173 new_element = map_element_RND_to_EM_cave(EL_AMOEBA_WET);
4175 cav->cave[x][y] = new_element;
4178 for (i = 0; i < MAX_PLAYERS; i++)
4180 cav->player_x[i] = -1;
4181 cav->player_y[i] = -1;
4184 // initialize player positions and delete players from the playfield
4185 for (y = 0; y < cav->height; y++) for (x = 0; x < cav->width; x++)
4187 if (IS_PLAYER_ELEMENT(level->field[x][y]))
4189 int player_nr = GET_PLAYER_NR(level->field[x][y]);
4191 cav->player_x[player_nr] = x;
4192 cav->player_y[player_nr] = y;
4194 cav->cave[x][y] = map_element_RND_to_EM_cave(EL_EMPTY);
4199 static void CopyNativeLevel_EM_to_RND(struct LevelInfo *level)
4201 static int ball_xy[8][2] =
4212 struct LevelInfo_EM *level_em = level->native_em_level;
4213 struct CAVE *cav = level_em->cav;
4216 level->fieldx = MIN(cav->width, MAX_LEV_FIELDX);
4217 level->fieldy = MIN(cav->height, MAX_LEV_FIELDY);
4219 level->time = cav->time_seconds;
4220 level->gems_needed = cav->gems_needed;
4222 sprintf(level->name, "Level %d", level->file_info.nr);
4224 level->score[SC_EMERALD] = cav->emerald_score;
4225 level->score[SC_DIAMOND] = cav->diamond_score;
4226 level->score[SC_ROBOT] = cav->alien_score;
4227 level->score[SC_SPACESHIP] = cav->tank_score;
4228 level->score[SC_BUG] = cav->bug_score;
4229 level->score[SC_YAMYAM] = cav->eater_score;
4230 level->score[SC_NUT] = cav->nut_score;
4231 level->score[SC_DYNAMITE] = cav->dynamite_score;
4232 level->score[SC_KEY] = cav->key_score;
4233 level->score[SC_TIME_BONUS] = cav->exit_score;
4235 level->num_yamyam_contents = cav->num_eater_arrays;
4237 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
4238 for (y = 0; y < 3; y++)
4239 for (x = 0; x < 3; x++)
4240 level->yamyam_content[i].e[x][y] =
4241 map_element_EM_to_RND_cave(cav->eater_array[i][y * 3 + x]);
4243 level->amoeba_speed = cav->amoeba_time;
4244 level->time_magic_wall = cav->wonderwall_time;
4245 level->time_wheel = cav->wheel_time;
4247 level->android_move_time = cav->android_move_time;
4248 level->android_clone_time = cav->android_clone_time;
4249 level->ball_random = cav->ball_random;
4250 level->ball_active_initial = cav->ball_active;
4251 level->ball_time = cav->ball_time;
4252 level->num_ball_contents = cav->num_ball_arrays;
4254 level->lenses_score = cav->lenses_score;
4255 level->magnify_score = cav->magnify_score;
4256 level->slurp_score = cav->slurp_score;
4258 level->lenses_time = cav->lenses_time;
4259 level->magnify_time = cav->magnify_time;
4261 level->wind_direction_initial =
4262 map_direction_EM_to_RND(cav->wind_direction);
4264 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
4265 for (j = 0; j < 8; j++)
4266 level->ball_content[i].e[ball_xy[j][0]][ball_xy[j][1]] =
4267 map_element_EM_to_RND_cave(cav->ball_array[i][j]);
4269 map_android_clone_elements_EM_to_RND(level);
4271 // convert the playfield (some elements need special treatment)
4272 for (y = 0; y < level->fieldy; y++) for (x = 0; x < level->fieldx; x++)
4274 int new_element = map_element_EM_to_RND_cave(cav->cave[x][y]);
4276 if (new_element == EL_AMOEBA_WET && level->amoeba_speed == 0)
4277 new_element = EL_AMOEBA_DEAD;
4279 level->field[x][y] = new_element;
4282 for (i = 0; i < MAX_PLAYERS; i++)
4284 // in case of all players set to the same field, use the first player
4285 int nr = MAX_PLAYERS - i - 1;
4286 int jx = cav->player_x[nr];
4287 int jy = cav->player_y[nr];
4289 if (jx != -1 && jy != -1)
4290 level->field[jx][jy] = EL_PLAYER_1 + nr;
4293 // time score is counted for each 10 seconds left in Emerald Mine levels
4294 level->time_score_base = 10;
4298 // ----------------------------------------------------------------------------
4299 // functions for loading SP level
4300 // ----------------------------------------------------------------------------
4302 static void CopyNativeLevel_RND_to_SP(struct LevelInfo *level)
4304 struct LevelInfo_SP *level_sp = level->native_sp_level;
4305 LevelInfoType *header = &level_sp->header;
4308 level_sp->width = level->fieldx;
4309 level_sp->height = level->fieldy;
4311 for (x = 0; x < level->fieldx; x++)
4312 for (y = 0; y < level->fieldy; y++)
4313 level_sp->playfield[x][y] = map_element_RND_to_SP(level->field[x][y]);
4315 header->InitialGravity = (level->initial_player_gravity[0] ? 1 : 0);
4317 for (i = 0; i < SP_LEVEL_NAME_LEN; i++)
4318 header->LevelTitle[i] = level->name[i];
4319 // !!! NO STRING TERMINATION IN SUPAPLEX VB CODE YET -- FIX THIS !!!
4321 header->InfotronsNeeded = level->gems_needed;
4323 header->SpecialPortCount = 0;
4325 for (x = 0; x < level->fieldx; x++) for (y = 0; y < level->fieldy; y++)
4327 boolean gravity_port_found = FALSE;
4328 boolean gravity_port_valid = FALSE;
4329 int gravity_port_flag;
4330 int gravity_port_base_element;
4331 int element = level->field[x][y];
4333 if (element >= EL_SP_GRAVITY_ON_PORT_RIGHT &&
4334 element <= EL_SP_GRAVITY_ON_PORT_UP)
4336 gravity_port_found = TRUE;
4337 gravity_port_valid = TRUE;
4338 gravity_port_flag = 1;
4339 gravity_port_base_element = EL_SP_GRAVITY_ON_PORT_RIGHT;
4341 else if (element >= EL_SP_GRAVITY_OFF_PORT_RIGHT &&
4342 element <= EL_SP_GRAVITY_OFF_PORT_UP)
4344 gravity_port_found = TRUE;
4345 gravity_port_valid = TRUE;
4346 gravity_port_flag = 0;
4347 gravity_port_base_element = EL_SP_GRAVITY_OFF_PORT_RIGHT;
4349 else if (element >= EL_SP_GRAVITY_PORT_RIGHT &&
4350 element <= EL_SP_GRAVITY_PORT_UP)
4352 // change R'n'D style gravity inverting special port to normal port
4353 // (there are no gravity inverting ports in native Supaplex engine)
4355 gravity_port_found = TRUE;
4356 gravity_port_valid = FALSE;
4357 gravity_port_base_element = EL_SP_GRAVITY_PORT_RIGHT;
4360 if (gravity_port_found)
4362 if (gravity_port_valid &&
4363 header->SpecialPortCount < SP_MAX_SPECIAL_PORTS)
4365 SpecialPortType *port = &header->SpecialPort[header->SpecialPortCount];
4367 port->PortLocation = (y * level->fieldx + x) * 2;
4368 port->Gravity = gravity_port_flag;
4370 element += EL_SP_GRAVITY_PORT_RIGHT - gravity_port_base_element;
4372 header->SpecialPortCount++;
4376 // change special gravity port to normal port
4378 element += EL_SP_PORT_RIGHT - gravity_port_base_element;
4381 level_sp->playfield[x][y] = element - EL_SP_START;
4386 static void CopyNativeLevel_SP_to_RND(struct LevelInfo *level)
4388 struct LevelInfo_SP *level_sp = level->native_sp_level;
4389 LevelInfoType *header = &level_sp->header;
4390 boolean num_invalid_elements = 0;
4393 level->fieldx = level_sp->width;
4394 level->fieldy = level_sp->height;
4396 for (x = 0; x < level->fieldx; x++)
4398 for (y = 0; y < level->fieldy; y++)
4400 int element_old = level_sp->playfield[x][y];
4401 int element_new = getMappedElement(map_element_SP_to_RND(element_old));
4403 if (element_new == EL_UNKNOWN)
4405 num_invalid_elements++;
4407 Debug("level:native:SP", "invalid element %d at position %d, %d",
4411 level->field[x][y] = element_new;
4415 if (num_invalid_elements > 0)
4416 Warn("found %d invalid elements%s", num_invalid_elements,
4417 (!options.debug ? " (use '--debug' for more details)" : ""));
4419 for (i = 0; i < MAX_PLAYERS; i++)
4420 level->initial_player_gravity[i] =
4421 (header->InitialGravity == 1 ? TRUE : FALSE);
4423 // skip leading spaces
4424 for (i = 0; i < SP_LEVEL_NAME_LEN; i++)
4425 if (header->LevelTitle[i] != ' ')
4429 for (j = 0; i < SP_LEVEL_NAME_LEN; i++, j++)
4430 level->name[j] = header->LevelTitle[i];
4431 level->name[j] = '\0';
4433 // cut trailing spaces
4435 if (level->name[j - 1] == ' ' && level->name[j] == '\0')
4436 level->name[j - 1] = '\0';
4438 level->gems_needed = header->InfotronsNeeded;
4440 for (i = 0; i < header->SpecialPortCount; i++)
4442 SpecialPortType *port = &header->SpecialPort[i];
4443 int port_location = port->PortLocation;
4444 int gravity = port->Gravity;
4445 int port_x, port_y, port_element;
4447 port_x = (port_location / 2) % level->fieldx;
4448 port_y = (port_location / 2) / level->fieldx;
4450 if (port_x < 0 || port_x >= level->fieldx ||
4451 port_y < 0 || port_y >= level->fieldy)
4453 Warn("special port position (%d, %d) out of bounds", port_x, port_y);
4458 port_element = level->field[port_x][port_y];
4460 if (port_element < EL_SP_GRAVITY_PORT_RIGHT ||
4461 port_element > EL_SP_GRAVITY_PORT_UP)
4463 Warn("no special port at position (%d, %d)", port_x, port_y);
4468 // change previous (wrong) gravity inverting special port to either
4469 // gravity enabling special port or gravity disabling special port
4470 level->field[port_x][port_y] +=
4471 (gravity == 1 ? EL_SP_GRAVITY_ON_PORT_RIGHT :
4472 EL_SP_GRAVITY_OFF_PORT_RIGHT) - EL_SP_GRAVITY_PORT_RIGHT;
4475 // change special gravity ports without database entries to normal ports
4476 for (x = 0; x < level->fieldx; x++)
4477 for (y = 0; y < level->fieldy; y++)
4478 if (level->field[x][y] >= EL_SP_GRAVITY_PORT_RIGHT &&
4479 level->field[x][y] <= EL_SP_GRAVITY_PORT_UP)
4480 level->field[x][y] += EL_SP_PORT_RIGHT - EL_SP_GRAVITY_PORT_RIGHT;
4482 level->time = 0; // no time limit
4483 level->amoeba_speed = 0;
4484 level->time_magic_wall = 0;
4485 level->time_wheel = 0;
4486 level->amoeba_content = EL_EMPTY;
4488 // original Supaplex does not use score values -- rate by playing time
4489 for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
4490 level->score[i] = 0;
4492 level->rate_time_over_score = TRUE;
4494 // there are no yamyams in supaplex levels
4495 for (i = 0; i < level->num_yamyam_contents; i++)
4496 for (x = 0; x < 3; x++)
4497 for (y = 0; y < 3; y++)
4498 level->yamyam_content[i].e[x][y] = EL_EMPTY;
4501 static void CopyNativeTape_RND_to_SP(struct LevelInfo *level)
4503 struct LevelInfo_SP *level_sp = level->native_sp_level;
4504 struct DemoInfo_SP *demo = &level_sp->demo;
4507 // always start with reliable default values
4508 demo->is_available = FALSE;
4511 if (TAPE_IS_EMPTY(tape))
4514 demo->level_nr = tape.level_nr; // (currently not used)
4516 level_sp->header.DemoRandomSeed = tape.random_seed;
4520 for (i = 0; i < tape.length; i++)
4522 int demo_action = map_key_RND_to_SP(tape.pos[i].action[0]);
4523 int demo_repeat = tape.pos[i].delay;
4524 int demo_entries = (demo_repeat + 15) / 16;
4526 if (demo->length + demo_entries >= SP_MAX_TAPE_LEN)
4528 Warn("tape truncated: size exceeds maximum SP demo size %d",
4534 for (j = 0; j < demo_repeat / 16; j++)
4535 demo->data[demo->length++] = 0xf0 | demo_action;
4537 if (demo_repeat % 16)
4538 demo->data[demo->length++] = ((demo_repeat % 16 - 1) << 4) | demo_action;
4541 demo->is_available = TRUE;
4544 static void CopyNativeTape_SP_to_RND(struct LevelInfo *level)
4546 struct LevelInfo_SP *level_sp = level->native_sp_level;
4547 struct DemoInfo_SP *demo = &level_sp->demo;
4548 char *filename = level->file_info.filename;
4551 // always start with reliable default values
4552 setTapeInfoToDefaults();
4554 if (!demo->is_available)
4557 tape.level_nr = demo->level_nr; // (currently not used)
4558 tape.random_seed = level_sp->header.DemoRandomSeed;
4560 TapeSetDateFromEpochSeconds(getFileTimestampEpochSeconds(filename));
4563 tape.pos[tape.counter].delay = 0;
4565 for (i = 0; i < demo->length; i++)
4567 int demo_action = demo->data[i] & 0x0f;
4568 int demo_repeat = (demo->data[i] & 0xf0) >> 4;
4569 int tape_action = map_key_SP_to_RND(demo_action);
4570 int tape_repeat = demo_repeat + 1;
4571 byte action[MAX_TAPE_ACTIONS] = { tape_action };
4572 boolean success = 0;
4575 for (j = 0; j < tape_repeat; j++)
4576 success = TapeAddAction(action);
4580 Warn("SP demo truncated: size exceeds maximum tape size %d",
4587 TapeHaltRecording();
4591 // ----------------------------------------------------------------------------
4592 // functions for loading MM level
4593 // ----------------------------------------------------------------------------
4595 static void CopyNativeLevel_RND_to_MM(struct LevelInfo *level)
4597 struct LevelInfo_MM *level_mm = level->native_mm_level;
4600 level_mm->fieldx = MIN(level->fieldx, MM_MAX_PLAYFIELD_WIDTH);
4601 level_mm->fieldy = MIN(level->fieldy, MM_MAX_PLAYFIELD_HEIGHT);
4603 level_mm->time = level->time;
4604 level_mm->kettles_needed = level->gems_needed;
4605 level_mm->auto_count_kettles = level->auto_count_gems;
4607 level_mm->mm_laser_red = level->mm_laser_red;
4608 level_mm->mm_laser_green = level->mm_laser_green;
4609 level_mm->mm_laser_blue = level->mm_laser_blue;
4611 level_mm->df_laser_red = level->df_laser_red;
4612 level_mm->df_laser_green = level->df_laser_green;
4613 level_mm->df_laser_blue = level->df_laser_blue;
4615 strcpy(level_mm->name, level->name);
4616 strcpy(level_mm->author, level->author);
4618 level_mm->score[SC_EMERALD] = level->score[SC_EMERALD];
4619 level_mm->score[SC_PACMAN] = level->score[SC_PACMAN];
4620 level_mm->score[SC_KEY] = level->score[SC_KEY];
4621 level_mm->score[SC_TIME_BONUS] = level->score[SC_TIME_BONUS];
4622 level_mm->score[SC_ELEM_BONUS] = level->score[SC_ELEM_BONUS];
4624 level_mm->amoeba_speed = level->amoeba_speed;
4625 level_mm->time_fuse = level->mm_time_fuse;
4626 level_mm->time_bomb = level->mm_time_bomb;
4627 level_mm->time_ball = level->mm_time_ball;
4628 level_mm->time_block = level->mm_time_block;
4630 level_mm->num_ball_contents = level->num_mm_ball_contents;
4631 level_mm->ball_choice_mode = level->mm_ball_choice_mode;
4632 level_mm->rotate_ball_content = level->rotate_mm_ball_content;
4633 level_mm->explode_ball = level->explode_mm_ball;
4635 for (i = 0; i < level->num_mm_ball_contents; i++)
4636 level_mm->ball_content[i] =
4637 map_element_RND_to_MM(level->mm_ball_content[i]);
4639 for (x = 0; x < level->fieldx; x++)
4640 for (y = 0; y < level->fieldy; y++)
4642 level_mm->field[x][y] = map_element_RND_to_MM(level->field[x][y]);
4645 static void CopyNativeLevel_MM_to_RND(struct LevelInfo *level)
4647 struct LevelInfo_MM *level_mm = level->native_mm_level;
4650 level->fieldx = MIN(level_mm->fieldx, MAX_LEV_FIELDX);
4651 level->fieldy = MIN(level_mm->fieldy, MAX_LEV_FIELDY);
4653 level->time = level_mm->time;
4654 level->gems_needed = level_mm->kettles_needed;
4655 level->auto_count_gems = level_mm->auto_count_kettles;
4657 level->mm_laser_red = level_mm->mm_laser_red;
4658 level->mm_laser_green = level_mm->mm_laser_green;
4659 level->mm_laser_blue = level_mm->mm_laser_blue;
4661 level->df_laser_red = level_mm->df_laser_red;
4662 level->df_laser_green = level_mm->df_laser_green;
4663 level->df_laser_blue = level_mm->df_laser_blue;
4665 strcpy(level->name, level_mm->name);
4667 // only overwrite author from 'levelinfo.conf' if author defined in level
4668 if (!strEqual(level_mm->author, ANONYMOUS_NAME))
4669 strcpy(level->author, level_mm->author);
4671 level->score[SC_EMERALD] = level_mm->score[SC_EMERALD];
4672 level->score[SC_PACMAN] = level_mm->score[SC_PACMAN];
4673 level->score[SC_KEY] = level_mm->score[SC_KEY];
4674 level->score[SC_TIME_BONUS] = level_mm->score[SC_TIME_BONUS];
4675 level->score[SC_ELEM_BONUS] = level_mm->score[SC_ELEM_BONUS];
4677 level->amoeba_speed = level_mm->amoeba_speed;
4678 level->mm_time_fuse = level_mm->time_fuse;
4679 level->mm_time_bomb = level_mm->time_bomb;
4680 level->mm_time_ball = level_mm->time_ball;
4681 level->mm_time_block = level_mm->time_block;
4683 level->num_mm_ball_contents = level_mm->num_ball_contents;
4684 level->mm_ball_choice_mode = level_mm->ball_choice_mode;
4685 level->rotate_mm_ball_content = level_mm->rotate_ball_content;
4686 level->explode_mm_ball = level_mm->explode_ball;
4688 for (i = 0; i < level->num_mm_ball_contents; i++)
4689 level->mm_ball_content[i] =
4690 map_element_MM_to_RND(level_mm->ball_content[i]);
4692 for (x = 0; x < level->fieldx; x++)
4693 for (y = 0; y < level->fieldy; y++)
4694 level->field[x][y] = map_element_MM_to_RND(level_mm->field[x][y]);
4698 // ----------------------------------------------------------------------------
4699 // functions for loading DC level
4700 // ----------------------------------------------------------------------------
4702 #define DC_LEVEL_HEADER_SIZE 344
4704 static unsigned short getDecodedWord_DC(unsigned short data_encoded,
4707 static int last_data_encoded;
4711 int diff_hi, diff_lo;
4712 int data_hi, data_lo;
4713 unsigned short data_decoded;
4717 last_data_encoded = 0;
4724 diff = data_encoded - last_data_encoded;
4725 diff_hi = diff & ~0xff;
4726 diff_lo = diff & 0xff;
4730 data_hi = diff_hi - (offset1 << 8) + (offset2 & 0xff00);
4731 data_lo = (diff_lo + (data_hi >> 16)) & 0x00ff;
4732 data_hi = data_hi & 0xff00;
4734 data_decoded = data_hi | data_lo;
4736 last_data_encoded = data_encoded;
4738 offset1 = (offset1 + 1) % 31;
4739 offset2 = offset2 & 0xff;
4741 return data_decoded;
4744 static int getMappedElement_DC(int element)
4752 // 0x0117 - 0x036e: (?)
4755 // 0x042d - 0x0684: (?)
4771 element = EL_CRYSTAL;
4774 case 0x0e77: // quicksand (boulder)
4775 element = EL_QUICKSAND_FAST_FULL;
4778 case 0x0e99: // slow quicksand (boulder)
4779 element = EL_QUICKSAND_FULL;
4783 element = EL_EM_EXIT_OPEN;
4787 element = EL_EM_EXIT_CLOSED;
4791 element = EL_EM_STEEL_EXIT_OPEN;
4795 element = EL_EM_STEEL_EXIT_CLOSED;
4798 case 0x0f4f: // dynamite (lit 1)
4799 element = EL_EM_DYNAMITE_ACTIVE;
4802 case 0x0f57: // dynamite (lit 2)
4803 element = EL_EM_DYNAMITE_ACTIVE;
4806 case 0x0f5f: // dynamite (lit 3)
4807 element = EL_EM_DYNAMITE_ACTIVE;
4810 case 0x0f67: // dynamite (lit 4)
4811 element = EL_EM_DYNAMITE_ACTIVE;
4818 element = EL_AMOEBA_WET;
4822 element = EL_AMOEBA_DROP;
4826 element = EL_DC_MAGIC_WALL;
4830 element = EL_SPACESHIP_UP;
4834 element = EL_SPACESHIP_DOWN;
4838 element = EL_SPACESHIP_LEFT;
4842 element = EL_SPACESHIP_RIGHT;
4846 element = EL_BUG_UP;
4850 element = EL_BUG_DOWN;
4854 element = EL_BUG_LEFT;
4858 element = EL_BUG_RIGHT;
4862 element = EL_MOLE_UP;
4866 element = EL_MOLE_DOWN;
4870 element = EL_MOLE_LEFT;
4874 element = EL_MOLE_RIGHT;
4882 element = EL_YAMYAM_UP;
4886 element = EL_SWITCHGATE_OPEN;
4890 element = EL_SWITCHGATE_CLOSED;
4894 element = EL_DC_SWITCHGATE_SWITCH_UP;
4898 element = EL_TIMEGATE_CLOSED;
4901 case 0x144c: // conveyor belt switch (green)
4902 element = EL_CONVEYOR_BELT_3_SWITCH_MIDDLE;
4905 case 0x144f: // conveyor belt switch (red)
4906 element = EL_CONVEYOR_BELT_1_SWITCH_MIDDLE;
4909 case 0x1452: // conveyor belt switch (blue)
4910 element = EL_CONVEYOR_BELT_4_SWITCH_MIDDLE;
4914 element = EL_CONVEYOR_BELT_3_MIDDLE;
4918 element = EL_CONVEYOR_BELT_3_LEFT;
4922 element = EL_CONVEYOR_BELT_3_RIGHT;
4926 element = EL_CONVEYOR_BELT_1_MIDDLE;
4930 element = EL_CONVEYOR_BELT_1_LEFT;
4934 element = EL_CONVEYOR_BELT_1_RIGHT;
4938 element = EL_CONVEYOR_BELT_4_MIDDLE;
4942 element = EL_CONVEYOR_BELT_4_LEFT;
4946 element = EL_CONVEYOR_BELT_4_RIGHT;
4950 element = EL_EXPANDABLE_WALL_HORIZONTAL;
4954 element = EL_EXPANDABLE_WALL_VERTICAL;
4958 element = EL_EXPANDABLE_WALL_ANY;
4961 case 0x14ce: // growing steel wall (left/right)
4962 element = EL_EXPANDABLE_STEELWALL_HORIZONTAL;
4965 case 0x14df: // growing steel wall (up/down)
4966 element = EL_EXPANDABLE_STEELWALL_VERTICAL;
4969 case 0x14e8: // growing steel wall (up/down/left/right)
4970 element = EL_EXPANDABLE_STEELWALL_ANY;
4974 element = EL_SHIELD_DEADLY;
4978 element = EL_EXTRA_TIME;
4986 element = EL_EMPTY_SPACE;
4989 case 0x1578: // quicksand (empty)
4990 element = EL_QUICKSAND_FAST_EMPTY;
4993 case 0x1579: // slow quicksand (empty)
4994 element = EL_QUICKSAND_EMPTY;
5004 element = EL_EM_DYNAMITE;
5007 case 0x15a1: // key (red)
5008 element = EL_EM_KEY_1;
5011 case 0x15a2: // key (yellow)
5012 element = EL_EM_KEY_2;
5015 case 0x15a3: // key (blue)
5016 element = EL_EM_KEY_4;
5019 case 0x15a4: // key (green)
5020 element = EL_EM_KEY_3;
5023 case 0x15a5: // key (white)
5024 element = EL_DC_KEY_WHITE;
5028 element = EL_WALL_SLIPPERY;
5035 case 0x15a8: // wall (not round)
5039 case 0x15a9: // (blue)
5040 element = EL_CHAR_A;
5043 case 0x15aa: // (blue)
5044 element = EL_CHAR_B;
5047 case 0x15ab: // (blue)
5048 element = EL_CHAR_C;
5051 case 0x15ac: // (blue)
5052 element = EL_CHAR_D;
5055 case 0x15ad: // (blue)
5056 element = EL_CHAR_E;
5059 case 0x15ae: // (blue)
5060 element = EL_CHAR_F;
5063 case 0x15af: // (blue)
5064 element = EL_CHAR_G;
5067 case 0x15b0: // (blue)
5068 element = EL_CHAR_H;
5071 case 0x15b1: // (blue)
5072 element = EL_CHAR_I;
5075 case 0x15b2: // (blue)
5076 element = EL_CHAR_J;
5079 case 0x15b3: // (blue)
5080 element = EL_CHAR_K;
5083 case 0x15b4: // (blue)
5084 element = EL_CHAR_L;
5087 case 0x15b5: // (blue)
5088 element = EL_CHAR_M;
5091 case 0x15b6: // (blue)
5092 element = EL_CHAR_N;
5095 case 0x15b7: // (blue)
5096 element = EL_CHAR_O;
5099 case 0x15b8: // (blue)
5100 element = EL_CHAR_P;
5103 case 0x15b9: // (blue)
5104 element = EL_CHAR_Q;
5107 case 0x15ba: // (blue)
5108 element = EL_CHAR_R;
5111 case 0x15bb: // (blue)
5112 element = EL_CHAR_S;
5115 case 0x15bc: // (blue)
5116 element = EL_CHAR_T;
5119 case 0x15bd: // (blue)
5120 element = EL_CHAR_U;
5123 case 0x15be: // (blue)
5124 element = EL_CHAR_V;
5127 case 0x15bf: // (blue)
5128 element = EL_CHAR_W;
5131 case 0x15c0: // (blue)
5132 element = EL_CHAR_X;
5135 case 0x15c1: // (blue)
5136 element = EL_CHAR_Y;
5139 case 0x15c2: // (blue)
5140 element = EL_CHAR_Z;
5143 case 0x15c3: // (blue)
5144 element = EL_CHAR_AUMLAUT;
5147 case 0x15c4: // (blue)
5148 element = EL_CHAR_OUMLAUT;
5151 case 0x15c5: // (blue)
5152 element = EL_CHAR_UUMLAUT;
5155 case 0x15c6: // (blue)
5156 element = EL_CHAR_0;
5159 case 0x15c7: // (blue)
5160 element = EL_CHAR_1;
5163 case 0x15c8: // (blue)
5164 element = EL_CHAR_2;
5167 case 0x15c9: // (blue)
5168 element = EL_CHAR_3;
5171 case 0x15ca: // (blue)
5172 element = EL_CHAR_4;
5175 case 0x15cb: // (blue)
5176 element = EL_CHAR_5;
5179 case 0x15cc: // (blue)
5180 element = EL_CHAR_6;
5183 case 0x15cd: // (blue)
5184 element = EL_CHAR_7;
5187 case 0x15ce: // (blue)
5188 element = EL_CHAR_8;
5191 case 0x15cf: // (blue)
5192 element = EL_CHAR_9;
5195 case 0x15d0: // (blue)
5196 element = EL_CHAR_PERIOD;
5199 case 0x15d1: // (blue)
5200 element = EL_CHAR_EXCLAM;
5203 case 0x15d2: // (blue)
5204 element = EL_CHAR_COLON;
5207 case 0x15d3: // (blue)
5208 element = EL_CHAR_LESS;
5211 case 0x15d4: // (blue)
5212 element = EL_CHAR_GREATER;
5215 case 0x15d5: // (blue)
5216 element = EL_CHAR_QUESTION;
5219 case 0x15d6: // (blue)
5220 element = EL_CHAR_COPYRIGHT;
5223 case 0x15d7: // (blue)
5224 element = EL_CHAR_UP;
5227 case 0x15d8: // (blue)
5228 element = EL_CHAR_DOWN;
5231 case 0x15d9: // (blue)
5232 element = EL_CHAR_BUTTON;
5235 case 0x15da: // (blue)
5236 element = EL_CHAR_PLUS;
5239 case 0x15db: // (blue)
5240 element = EL_CHAR_MINUS;
5243 case 0x15dc: // (blue)
5244 element = EL_CHAR_APOSTROPHE;
5247 case 0x15dd: // (blue)
5248 element = EL_CHAR_PARENLEFT;
5251 case 0x15de: // (blue)
5252 element = EL_CHAR_PARENRIGHT;
5255 case 0x15df: // (green)
5256 element = EL_CHAR_A;
5259 case 0x15e0: // (green)
5260 element = EL_CHAR_B;
5263 case 0x15e1: // (green)
5264 element = EL_CHAR_C;
5267 case 0x15e2: // (green)
5268 element = EL_CHAR_D;
5271 case 0x15e3: // (green)
5272 element = EL_CHAR_E;
5275 case 0x15e4: // (green)
5276 element = EL_CHAR_F;
5279 case 0x15e5: // (green)
5280 element = EL_CHAR_G;
5283 case 0x15e6: // (green)
5284 element = EL_CHAR_H;
5287 case 0x15e7: // (green)
5288 element = EL_CHAR_I;
5291 case 0x15e8: // (green)
5292 element = EL_CHAR_J;
5295 case 0x15e9: // (green)
5296 element = EL_CHAR_K;
5299 case 0x15ea: // (green)
5300 element = EL_CHAR_L;
5303 case 0x15eb: // (green)
5304 element = EL_CHAR_M;
5307 case 0x15ec: // (green)
5308 element = EL_CHAR_N;
5311 case 0x15ed: // (green)
5312 element = EL_CHAR_O;
5315 case 0x15ee: // (green)
5316 element = EL_CHAR_P;
5319 case 0x15ef: // (green)
5320 element = EL_CHAR_Q;
5323 case 0x15f0: // (green)
5324 element = EL_CHAR_R;
5327 case 0x15f1: // (green)
5328 element = EL_CHAR_S;
5331 case 0x15f2: // (green)
5332 element = EL_CHAR_T;
5335 case 0x15f3: // (green)
5336 element = EL_CHAR_U;
5339 case 0x15f4: // (green)
5340 element = EL_CHAR_V;
5343 case 0x15f5: // (green)
5344 element = EL_CHAR_W;
5347 case 0x15f6: // (green)
5348 element = EL_CHAR_X;
5351 case 0x15f7: // (green)
5352 element = EL_CHAR_Y;
5355 case 0x15f8: // (green)
5356 element = EL_CHAR_Z;
5359 case 0x15f9: // (green)
5360 element = EL_CHAR_AUMLAUT;
5363 case 0x15fa: // (green)
5364 element = EL_CHAR_OUMLAUT;
5367 case 0x15fb: // (green)
5368 element = EL_CHAR_UUMLAUT;
5371 case 0x15fc: // (green)
5372 element = EL_CHAR_0;
5375 case 0x15fd: // (green)
5376 element = EL_CHAR_1;
5379 case 0x15fe: // (green)
5380 element = EL_CHAR_2;
5383 case 0x15ff: // (green)
5384 element = EL_CHAR_3;
5387 case 0x1600: // (green)
5388 element = EL_CHAR_4;
5391 case 0x1601: // (green)
5392 element = EL_CHAR_5;
5395 case 0x1602: // (green)
5396 element = EL_CHAR_6;
5399 case 0x1603: // (green)
5400 element = EL_CHAR_7;
5403 case 0x1604: // (green)
5404 element = EL_CHAR_8;
5407 case 0x1605: // (green)
5408 element = EL_CHAR_9;
5411 case 0x1606: // (green)
5412 element = EL_CHAR_PERIOD;
5415 case 0x1607: // (green)
5416 element = EL_CHAR_EXCLAM;
5419 case 0x1608: // (green)
5420 element = EL_CHAR_COLON;
5423 case 0x1609: // (green)
5424 element = EL_CHAR_LESS;
5427 case 0x160a: // (green)
5428 element = EL_CHAR_GREATER;
5431 case 0x160b: // (green)
5432 element = EL_CHAR_QUESTION;
5435 case 0x160c: // (green)
5436 element = EL_CHAR_COPYRIGHT;
5439 case 0x160d: // (green)
5440 element = EL_CHAR_UP;
5443 case 0x160e: // (green)
5444 element = EL_CHAR_DOWN;
5447 case 0x160f: // (green)
5448 element = EL_CHAR_BUTTON;
5451 case 0x1610: // (green)
5452 element = EL_CHAR_PLUS;
5455 case 0x1611: // (green)
5456 element = EL_CHAR_MINUS;
5459 case 0x1612: // (green)
5460 element = EL_CHAR_APOSTROPHE;
5463 case 0x1613: // (green)
5464 element = EL_CHAR_PARENLEFT;
5467 case 0x1614: // (green)
5468 element = EL_CHAR_PARENRIGHT;
5471 case 0x1615: // (blue steel)
5472 element = EL_STEEL_CHAR_A;
5475 case 0x1616: // (blue steel)
5476 element = EL_STEEL_CHAR_B;
5479 case 0x1617: // (blue steel)
5480 element = EL_STEEL_CHAR_C;
5483 case 0x1618: // (blue steel)
5484 element = EL_STEEL_CHAR_D;
5487 case 0x1619: // (blue steel)
5488 element = EL_STEEL_CHAR_E;
5491 case 0x161a: // (blue steel)
5492 element = EL_STEEL_CHAR_F;
5495 case 0x161b: // (blue steel)
5496 element = EL_STEEL_CHAR_G;
5499 case 0x161c: // (blue steel)
5500 element = EL_STEEL_CHAR_H;
5503 case 0x161d: // (blue steel)
5504 element = EL_STEEL_CHAR_I;
5507 case 0x161e: // (blue steel)
5508 element = EL_STEEL_CHAR_J;
5511 case 0x161f: // (blue steel)
5512 element = EL_STEEL_CHAR_K;
5515 case 0x1620: // (blue steel)
5516 element = EL_STEEL_CHAR_L;
5519 case 0x1621: // (blue steel)
5520 element = EL_STEEL_CHAR_M;
5523 case 0x1622: // (blue steel)
5524 element = EL_STEEL_CHAR_N;
5527 case 0x1623: // (blue steel)
5528 element = EL_STEEL_CHAR_O;
5531 case 0x1624: // (blue steel)
5532 element = EL_STEEL_CHAR_P;
5535 case 0x1625: // (blue steel)
5536 element = EL_STEEL_CHAR_Q;
5539 case 0x1626: // (blue steel)
5540 element = EL_STEEL_CHAR_R;
5543 case 0x1627: // (blue steel)
5544 element = EL_STEEL_CHAR_S;
5547 case 0x1628: // (blue steel)
5548 element = EL_STEEL_CHAR_T;
5551 case 0x1629: // (blue steel)
5552 element = EL_STEEL_CHAR_U;
5555 case 0x162a: // (blue steel)
5556 element = EL_STEEL_CHAR_V;
5559 case 0x162b: // (blue steel)
5560 element = EL_STEEL_CHAR_W;
5563 case 0x162c: // (blue steel)
5564 element = EL_STEEL_CHAR_X;
5567 case 0x162d: // (blue steel)
5568 element = EL_STEEL_CHAR_Y;
5571 case 0x162e: // (blue steel)
5572 element = EL_STEEL_CHAR_Z;
5575 case 0x162f: // (blue steel)
5576 element = EL_STEEL_CHAR_AUMLAUT;
5579 case 0x1630: // (blue steel)
5580 element = EL_STEEL_CHAR_OUMLAUT;
5583 case 0x1631: // (blue steel)
5584 element = EL_STEEL_CHAR_UUMLAUT;
5587 case 0x1632: // (blue steel)
5588 element = EL_STEEL_CHAR_0;
5591 case 0x1633: // (blue steel)
5592 element = EL_STEEL_CHAR_1;
5595 case 0x1634: // (blue steel)
5596 element = EL_STEEL_CHAR_2;
5599 case 0x1635: // (blue steel)
5600 element = EL_STEEL_CHAR_3;
5603 case 0x1636: // (blue steel)
5604 element = EL_STEEL_CHAR_4;
5607 case 0x1637: // (blue steel)
5608 element = EL_STEEL_CHAR_5;
5611 case 0x1638: // (blue steel)
5612 element = EL_STEEL_CHAR_6;
5615 case 0x1639: // (blue steel)
5616 element = EL_STEEL_CHAR_7;
5619 case 0x163a: // (blue steel)
5620 element = EL_STEEL_CHAR_8;
5623 case 0x163b: // (blue steel)
5624 element = EL_STEEL_CHAR_9;
5627 case 0x163c: // (blue steel)
5628 element = EL_STEEL_CHAR_PERIOD;
5631 case 0x163d: // (blue steel)
5632 element = EL_STEEL_CHAR_EXCLAM;
5635 case 0x163e: // (blue steel)
5636 element = EL_STEEL_CHAR_COLON;
5639 case 0x163f: // (blue steel)
5640 element = EL_STEEL_CHAR_LESS;
5643 case 0x1640: // (blue steel)
5644 element = EL_STEEL_CHAR_GREATER;
5647 case 0x1641: // (blue steel)
5648 element = EL_STEEL_CHAR_QUESTION;
5651 case 0x1642: // (blue steel)
5652 element = EL_STEEL_CHAR_COPYRIGHT;
5655 case 0x1643: // (blue steel)
5656 element = EL_STEEL_CHAR_UP;
5659 case 0x1644: // (blue steel)
5660 element = EL_STEEL_CHAR_DOWN;
5663 case 0x1645: // (blue steel)
5664 element = EL_STEEL_CHAR_BUTTON;
5667 case 0x1646: // (blue steel)
5668 element = EL_STEEL_CHAR_PLUS;
5671 case 0x1647: // (blue steel)
5672 element = EL_STEEL_CHAR_MINUS;
5675 case 0x1648: // (blue steel)
5676 element = EL_STEEL_CHAR_APOSTROPHE;
5679 case 0x1649: // (blue steel)
5680 element = EL_STEEL_CHAR_PARENLEFT;
5683 case 0x164a: // (blue steel)
5684 element = EL_STEEL_CHAR_PARENRIGHT;
5687 case 0x164b: // (green steel)
5688 element = EL_STEEL_CHAR_A;
5691 case 0x164c: // (green steel)
5692 element = EL_STEEL_CHAR_B;
5695 case 0x164d: // (green steel)
5696 element = EL_STEEL_CHAR_C;
5699 case 0x164e: // (green steel)
5700 element = EL_STEEL_CHAR_D;
5703 case 0x164f: // (green steel)
5704 element = EL_STEEL_CHAR_E;
5707 case 0x1650: // (green steel)
5708 element = EL_STEEL_CHAR_F;
5711 case 0x1651: // (green steel)
5712 element = EL_STEEL_CHAR_G;
5715 case 0x1652: // (green steel)
5716 element = EL_STEEL_CHAR_H;
5719 case 0x1653: // (green steel)
5720 element = EL_STEEL_CHAR_I;
5723 case 0x1654: // (green steel)
5724 element = EL_STEEL_CHAR_J;
5727 case 0x1655: // (green steel)
5728 element = EL_STEEL_CHAR_K;
5731 case 0x1656: // (green steel)
5732 element = EL_STEEL_CHAR_L;
5735 case 0x1657: // (green steel)
5736 element = EL_STEEL_CHAR_M;
5739 case 0x1658: // (green steel)
5740 element = EL_STEEL_CHAR_N;
5743 case 0x1659: // (green steel)
5744 element = EL_STEEL_CHAR_O;
5747 case 0x165a: // (green steel)
5748 element = EL_STEEL_CHAR_P;
5751 case 0x165b: // (green steel)
5752 element = EL_STEEL_CHAR_Q;
5755 case 0x165c: // (green steel)
5756 element = EL_STEEL_CHAR_R;
5759 case 0x165d: // (green steel)
5760 element = EL_STEEL_CHAR_S;
5763 case 0x165e: // (green steel)
5764 element = EL_STEEL_CHAR_T;
5767 case 0x165f: // (green steel)
5768 element = EL_STEEL_CHAR_U;
5771 case 0x1660: // (green steel)
5772 element = EL_STEEL_CHAR_V;
5775 case 0x1661: // (green steel)
5776 element = EL_STEEL_CHAR_W;
5779 case 0x1662: // (green steel)
5780 element = EL_STEEL_CHAR_X;
5783 case 0x1663: // (green steel)
5784 element = EL_STEEL_CHAR_Y;
5787 case 0x1664: // (green steel)
5788 element = EL_STEEL_CHAR_Z;
5791 case 0x1665: // (green steel)
5792 element = EL_STEEL_CHAR_AUMLAUT;
5795 case 0x1666: // (green steel)
5796 element = EL_STEEL_CHAR_OUMLAUT;
5799 case 0x1667: // (green steel)
5800 element = EL_STEEL_CHAR_UUMLAUT;
5803 case 0x1668: // (green steel)
5804 element = EL_STEEL_CHAR_0;
5807 case 0x1669: // (green steel)
5808 element = EL_STEEL_CHAR_1;
5811 case 0x166a: // (green steel)
5812 element = EL_STEEL_CHAR_2;
5815 case 0x166b: // (green steel)
5816 element = EL_STEEL_CHAR_3;
5819 case 0x166c: // (green steel)
5820 element = EL_STEEL_CHAR_4;
5823 case 0x166d: // (green steel)
5824 element = EL_STEEL_CHAR_5;
5827 case 0x166e: // (green steel)
5828 element = EL_STEEL_CHAR_6;
5831 case 0x166f: // (green steel)
5832 element = EL_STEEL_CHAR_7;
5835 case 0x1670: // (green steel)
5836 element = EL_STEEL_CHAR_8;
5839 case 0x1671: // (green steel)
5840 element = EL_STEEL_CHAR_9;
5843 case 0x1672: // (green steel)
5844 element = EL_STEEL_CHAR_PERIOD;
5847 case 0x1673: // (green steel)
5848 element = EL_STEEL_CHAR_EXCLAM;
5851 case 0x1674: // (green steel)
5852 element = EL_STEEL_CHAR_COLON;
5855 case 0x1675: // (green steel)
5856 element = EL_STEEL_CHAR_LESS;
5859 case 0x1676: // (green steel)
5860 element = EL_STEEL_CHAR_GREATER;
5863 case 0x1677: // (green steel)
5864 element = EL_STEEL_CHAR_QUESTION;
5867 case 0x1678: // (green steel)
5868 element = EL_STEEL_CHAR_COPYRIGHT;
5871 case 0x1679: // (green steel)
5872 element = EL_STEEL_CHAR_UP;
5875 case 0x167a: // (green steel)
5876 element = EL_STEEL_CHAR_DOWN;
5879 case 0x167b: // (green steel)
5880 element = EL_STEEL_CHAR_BUTTON;
5883 case 0x167c: // (green steel)
5884 element = EL_STEEL_CHAR_PLUS;
5887 case 0x167d: // (green steel)
5888 element = EL_STEEL_CHAR_MINUS;
5891 case 0x167e: // (green steel)
5892 element = EL_STEEL_CHAR_APOSTROPHE;
5895 case 0x167f: // (green steel)
5896 element = EL_STEEL_CHAR_PARENLEFT;
5899 case 0x1680: // (green steel)
5900 element = EL_STEEL_CHAR_PARENRIGHT;
5903 case 0x1681: // gate (red)
5904 element = EL_EM_GATE_1;
5907 case 0x1682: // secret gate (red)
5908 element = EL_EM_GATE_1_GRAY;
5911 case 0x1683: // gate (yellow)
5912 element = EL_EM_GATE_2;
5915 case 0x1684: // secret gate (yellow)
5916 element = EL_EM_GATE_2_GRAY;
5919 case 0x1685: // gate (blue)
5920 element = EL_EM_GATE_4;
5923 case 0x1686: // secret gate (blue)
5924 element = EL_EM_GATE_4_GRAY;
5927 case 0x1687: // gate (green)
5928 element = EL_EM_GATE_3;
5931 case 0x1688: // secret gate (green)
5932 element = EL_EM_GATE_3_GRAY;
5935 case 0x1689: // gate (white)
5936 element = EL_DC_GATE_WHITE;
5939 case 0x168a: // secret gate (white)
5940 element = EL_DC_GATE_WHITE_GRAY;
5943 case 0x168b: // secret gate (no key)
5944 element = EL_DC_GATE_FAKE_GRAY;
5948 element = EL_ROBOT_WHEEL;
5952 element = EL_DC_TIMEGATE_SWITCH;
5956 element = EL_ACID_POOL_BOTTOM;
5960 element = EL_ACID_POOL_TOPLEFT;
5964 element = EL_ACID_POOL_TOPRIGHT;
5968 element = EL_ACID_POOL_BOTTOMLEFT;
5972 element = EL_ACID_POOL_BOTTOMRIGHT;
5976 element = EL_STEELWALL;
5980 element = EL_STEELWALL_SLIPPERY;
5983 case 0x1695: // steel wall (not round)
5984 element = EL_STEELWALL;
5987 case 0x1696: // steel wall (left)
5988 element = EL_DC_STEELWALL_1_LEFT;
5991 case 0x1697: // steel wall (bottom)
5992 element = EL_DC_STEELWALL_1_BOTTOM;
5995 case 0x1698: // steel wall (right)
5996 element = EL_DC_STEELWALL_1_RIGHT;
5999 case 0x1699: // steel wall (top)
6000 element = EL_DC_STEELWALL_1_TOP;
6003 case 0x169a: // steel wall (left/bottom)
6004 element = EL_DC_STEELWALL_1_BOTTOMLEFT;
6007 case 0x169b: // steel wall (right/bottom)
6008 element = EL_DC_STEELWALL_1_BOTTOMRIGHT;
6011 case 0x169c: // steel wall (right/top)
6012 element = EL_DC_STEELWALL_1_TOPRIGHT;
6015 case 0x169d: // steel wall (left/top)
6016 element = EL_DC_STEELWALL_1_TOPLEFT;
6019 case 0x169e: // steel wall (right/bottom small)
6020 element = EL_DC_STEELWALL_1_BOTTOMRIGHT_2;
6023 case 0x169f: // steel wall (left/bottom small)
6024 element = EL_DC_STEELWALL_1_BOTTOMLEFT_2;
6027 case 0x16a0: // steel wall (right/top small)
6028 element = EL_DC_STEELWALL_1_TOPRIGHT_2;
6031 case 0x16a1: // steel wall (left/top small)
6032 element = EL_DC_STEELWALL_1_TOPLEFT_2;
6035 case 0x16a2: // steel wall (left/right)
6036 element = EL_DC_STEELWALL_1_VERTICAL;
6039 case 0x16a3: // steel wall (top/bottom)
6040 element = EL_DC_STEELWALL_1_HORIZONTAL;
6043 case 0x16a4: // steel wall 2 (left end)
6044 element = EL_DC_STEELWALL_2_LEFT;
6047 case 0x16a5: // steel wall 2 (right end)
6048 element = EL_DC_STEELWALL_2_RIGHT;
6051 case 0x16a6: // steel wall 2 (top end)
6052 element = EL_DC_STEELWALL_2_TOP;
6055 case 0x16a7: // steel wall 2 (bottom end)
6056 element = EL_DC_STEELWALL_2_BOTTOM;
6059 case 0x16a8: // steel wall 2 (left/right)
6060 element = EL_DC_STEELWALL_2_HORIZONTAL;
6063 case 0x16a9: // steel wall 2 (up/down)
6064 element = EL_DC_STEELWALL_2_VERTICAL;
6067 case 0x16aa: // steel wall 2 (mid)
6068 element = EL_DC_STEELWALL_2_MIDDLE;
6072 element = EL_SIGN_EXCLAMATION;
6076 element = EL_SIGN_RADIOACTIVITY;
6080 element = EL_SIGN_STOP;
6084 element = EL_SIGN_WHEELCHAIR;
6088 element = EL_SIGN_PARKING;
6092 element = EL_SIGN_NO_ENTRY;
6096 element = EL_SIGN_HEART;
6100 element = EL_SIGN_GIVE_WAY;
6104 element = EL_SIGN_ENTRY_FORBIDDEN;
6108 element = EL_SIGN_EMERGENCY_EXIT;
6112 element = EL_SIGN_YIN_YANG;
6116 element = EL_WALL_EMERALD;
6120 element = EL_WALL_DIAMOND;
6124 element = EL_WALL_PEARL;
6128 element = EL_WALL_CRYSTAL;
6132 element = EL_INVISIBLE_WALL;
6136 element = EL_INVISIBLE_STEELWALL;
6140 // EL_INVISIBLE_SAND
6143 element = EL_LIGHT_SWITCH;
6147 element = EL_ENVELOPE_1;
6151 if (element >= 0x0117 && element <= 0x036e) // (?)
6152 element = EL_DIAMOND;
6153 else if (element >= 0x042d && element <= 0x0684) // (?)
6154 element = EL_EMERALD;
6155 else if (element >= 0x157c && element <= 0x158b)
6157 else if (element >= 0x1590 && element <= 0x159f)
6158 element = EL_DC_LANDMINE;
6159 else if (element >= 0x16bc && element <= 0x16cb)
6160 element = EL_INVISIBLE_SAND;
6163 Warn("unknown Diamond Caves element 0x%04x", element);
6165 element = EL_UNKNOWN;
6170 return getMappedElement(element);
6173 static void LoadLevelFromFileStream_DC(File *file, struct LevelInfo *level)
6175 byte header[DC_LEVEL_HEADER_SIZE];
6177 int envelope_header_pos = 62;
6178 int envelope_content_pos = 94;
6179 int level_name_pos = 251;
6180 int level_author_pos = 292;
6181 int envelope_header_len;
6182 int envelope_content_len;
6184 int level_author_len;
6186 int num_yamyam_contents;
6189 getDecodedWord_DC(0, TRUE); // initialize DC2 decoding engine
6191 for (i = 0; i < DC_LEVEL_HEADER_SIZE / 2; i++)
6193 unsigned short header_word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
6195 header[i * 2 + 0] = header_word >> 8;
6196 header[i * 2 + 1] = header_word & 0xff;
6199 // read some values from level header to check level decoding integrity
6200 fieldx = header[6] | (header[7] << 8);
6201 fieldy = header[8] | (header[9] << 8);
6202 num_yamyam_contents = header[60] | (header[61] << 8);
6204 // do some simple sanity checks to ensure that level was correctly decoded
6205 if (fieldx < 1 || fieldx > 256 ||
6206 fieldy < 1 || fieldy > 256 ||
6207 num_yamyam_contents < 1 || num_yamyam_contents > 8)
6209 level->no_valid_file = TRUE;
6211 Warn("cannot decode level from stream -- using empty level");
6216 // maximum envelope header size is 31 bytes
6217 envelope_header_len = header[envelope_header_pos];
6218 // maximum envelope content size is 110 (156?) bytes
6219 envelope_content_len = header[envelope_content_pos];
6221 // maximum level title size is 40 bytes
6222 level_name_len = MIN(header[level_name_pos], MAX_LEVEL_NAME_LEN);
6223 // maximum level author size is 30 (51?) bytes
6224 level_author_len = MIN(header[level_author_pos], MAX_LEVEL_AUTHOR_LEN);
6228 for (i = 0; i < envelope_header_len; i++)
6229 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
6230 level->envelope[0].text[envelope_size++] =
6231 header[envelope_header_pos + 1 + i];
6233 if (envelope_header_len > 0 && envelope_content_len > 0)
6235 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
6236 level->envelope[0].text[envelope_size++] = '\n';
6237 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
6238 level->envelope[0].text[envelope_size++] = '\n';
6241 for (i = 0; i < envelope_content_len; i++)
6242 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
6243 level->envelope[0].text[envelope_size++] =
6244 header[envelope_content_pos + 1 + i];
6246 level->envelope[0].text[envelope_size] = '\0';
6248 level->envelope[0].xsize = MAX_ENVELOPE_XSIZE;
6249 level->envelope[0].ysize = 10;
6250 level->envelope[0].autowrap = TRUE;
6251 level->envelope[0].centered = TRUE;
6253 for (i = 0; i < level_name_len; i++)
6254 level->name[i] = header[level_name_pos + 1 + i];
6255 level->name[level_name_len] = '\0';
6257 for (i = 0; i < level_author_len; i++)
6258 level->author[i] = header[level_author_pos + 1 + i];
6259 level->author[level_author_len] = '\0';
6261 num_yamyam_contents = header[60] | (header[61] << 8);
6262 level->num_yamyam_contents =
6263 MIN(MAX(MIN_ELEMENT_CONTENTS, num_yamyam_contents), MAX_ELEMENT_CONTENTS);
6265 for (i = 0; i < num_yamyam_contents; i++)
6267 for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
6269 unsigned short word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
6270 int element_dc = ((word & 0xff) << 8) | ((word >> 8) & 0xff);
6272 if (i < MAX_ELEMENT_CONTENTS)
6273 level->yamyam_content[i].e[x][y] = getMappedElement_DC(element_dc);
6277 fieldx = header[6] | (header[7] << 8);
6278 fieldy = header[8] | (header[9] << 8);
6279 level->fieldx = MIN(MAX(MIN_LEV_FIELDX, fieldx), MAX_LEV_FIELDX);
6280 level->fieldy = MIN(MAX(MIN_LEV_FIELDY, fieldy), MAX_LEV_FIELDY);
6282 for (y = 0; y < fieldy; y++) for (x = 0; x < fieldx; x++)
6284 unsigned short word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
6285 int element_dc = ((word & 0xff) << 8) | ((word >> 8) & 0xff);
6287 if (x < MAX_LEV_FIELDX && y < MAX_LEV_FIELDY)
6288 level->field[x][y] = getMappedElement_DC(element_dc);
6291 x = MIN(MAX(0, (header[10] | (header[11] << 8)) - 1), MAX_LEV_FIELDX - 1);
6292 y = MIN(MAX(0, (header[12] | (header[13] << 8)) - 1), MAX_LEV_FIELDY - 1);
6293 level->field[x][y] = EL_PLAYER_1;
6295 x = MIN(MAX(0, (header[14] | (header[15] << 8)) - 1), MAX_LEV_FIELDX - 1);
6296 y = MIN(MAX(0, (header[16] | (header[17] << 8)) - 1), MAX_LEV_FIELDY - 1);
6297 level->field[x][y] = EL_PLAYER_2;
6299 level->gems_needed = header[18] | (header[19] << 8);
6301 level->score[SC_EMERALD] = header[20] | (header[21] << 8);
6302 level->score[SC_DIAMOND] = header[22] | (header[23] << 8);
6303 level->score[SC_PEARL] = header[24] | (header[25] << 8);
6304 level->score[SC_CRYSTAL] = header[26] | (header[27] << 8);
6305 level->score[SC_NUT] = header[28] | (header[29] << 8);
6306 level->score[SC_ROBOT] = header[30] | (header[31] << 8);
6307 level->score[SC_SPACESHIP] = header[32] | (header[33] << 8);
6308 level->score[SC_BUG] = header[34] | (header[35] << 8);
6309 level->score[SC_YAMYAM] = header[36] | (header[37] << 8);
6310 level->score[SC_DYNAMITE] = header[38] | (header[39] << 8);
6311 level->score[SC_KEY] = header[40] | (header[41] << 8);
6312 level->score[SC_TIME_BONUS] = header[42] | (header[43] << 8);
6314 level->time = header[44] | (header[45] << 8);
6316 level->amoeba_speed = header[46] | (header[47] << 8);
6317 level->time_light = header[48] | (header[49] << 8);
6318 level->time_timegate = header[50] | (header[51] << 8);
6319 level->time_wheel = header[52] | (header[53] << 8);
6320 level->time_magic_wall = header[54] | (header[55] << 8);
6321 level->extra_time = header[56] | (header[57] << 8);
6322 level->shield_normal_time = header[58] | (header[59] << 8);
6324 // shield and extra time elements do not have a score
6325 level->score[SC_SHIELD] = 0;
6326 level->extra_time_score = 0;
6328 // set time for normal and deadly shields to the same value
6329 level->shield_deadly_time = level->shield_normal_time;
6331 // Diamond Caves has the same (strange) behaviour as Emerald Mine that gems
6332 // can slip down from flat walls, like normal walls and steel walls
6333 level->em_slippery_gems = TRUE;
6335 // time score is counted for each 10 seconds left in Diamond Caves levels
6336 level->time_score_base = 10;
6339 static void LoadLevelFromFileInfo_DC(struct LevelInfo *level,
6340 struct LevelFileInfo *level_file_info,
6341 boolean level_info_only)
6343 char *filename = level_file_info->filename;
6345 int num_magic_bytes = 8;
6346 char magic_bytes[num_magic_bytes + 1];
6347 int num_levels_to_skip = level_file_info->nr - leveldir_current->first_level;
6349 if (!(file = openFile(filename, MODE_READ)))
6351 level->no_valid_file = TRUE;
6353 if (!level_info_only)
6354 Warn("cannot read level '%s' -- using empty level", filename);
6359 // fseek(file, 0x0000, SEEK_SET);
6361 if (level_file_info->packed)
6363 // read "magic bytes" from start of file
6364 if (getStringFromFile(file, magic_bytes, num_magic_bytes + 1) == NULL)
6365 magic_bytes[0] = '\0';
6367 // check "magic bytes" for correct file format
6368 if (!strPrefix(magic_bytes, "DC2"))
6370 level->no_valid_file = TRUE;
6372 Warn("unknown DC level file '%s' -- using empty level", filename);
6377 if (strPrefix(magic_bytes, "DC2Win95") ||
6378 strPrefix(magic_bytes, "DC2Win98"))
6380 int position_first_level = 0x00fa;
6381 int extra_bytes = 4;
6384 // advance file stream to first level inside the level package
6385 skip_bytes = position_first_level - num_magic_bytes - extra_bytes;
6387 // each block of level data is followed by block of non-level data
6388 num_levels_to_skip *= 2;
6390 // at least skip header bytes, therefore use ">= 0" instead of "> 0"
6391 while (num_levels_to_skip >= 0)
6393 // advance file stream to next level inside the level package
6394 if (seekFile(file, skip_bytes, SEEK_CUR) != 0)
6396 level->no_valid_file = TRUE;
6398 Warn("cannot fseek in file '%s' -- using empty level", filename);
6403 // skip apparently unused extra bytes following each level
6404 ReadUnusedBytesFromFile(file, extra_bytes);
6406 // read size of next level in level package
6407 skip_bytes = getFile32BitLE(file);
6409 num_levels_to_skip--;
6414 level->no_valid_file = TRUE;
6416 Warn("unknown DC2 level file '%s' -- using empty level", filename);
6422 LoadLevelFromFileStream_DC(file, level);
6428 // ----------------------------------------------------------------------------
6429 // functions for loading SB level
6430 // ----------------------------------------------------------------------------
6432 int getMappedElement_SB(int element_ascii, boolean use_ces)
6440 sb_element_mapping[] =
6442 { ' ', EL_EMPTY, EL_CUSTOM_1 }, // floor (space)
6443 { '#', EL_STEELWALL, EL_CUSTOM_2 }, // wall
6444 { '@', EL_PLAYER_1, EL_CUSTOM_3 }, // player
6445 { '$', EL_SOKOBAN_OBJECT, EL_CUSTOM_4 }, // box
6446 { '.', EL_SOKOBAN_FIELD_EMPTY, EL_CUSTOM_5 }, // goal square
6447 { '*', EL_SOKOBAN_FIELD_FULL, EL_CUSTOM_6 }, // box on goal square
6448 { '+', EL_SOKOBAN_FIELD_PLAYER, EL_CUSTOM_7 }, // player on goal square
6449 { '_', EL_INVISIBLE_STEELWALL, EL_FROM_LEVEL_TEMPLATE }, // floor beyond border
6456 for (i = 0; sb_element_mapping[i].ascii != 0; i++)
6457 if (element_ascii == sb_element_mapping[i].ascii)
6458 return (use_ces ? sb_element_mapping[i].ce : sb_element_mapping[i].sb);
6460 return EL_UNDEFINED;
6463 static void SetLevelSettings_SB(struct LevelInfo *level)
6467 level->use_step_counter = TRUE;
6470 level->score[SC_TIME_BONUS] = 0;
6471 level->time_score_base = 1;
6472 level->rate_time_over_score = TRUE;
6475 level->auto_exit_sokoban = TRUE;
6478 static void LoadLevelFromFileInfo_SB(struct LevelInfo *level,
6479 struct LevelFileInfo *level_file_info,
6480 boolean level_info_only)
6482 char *filename = level_file_info->filename;
6483 char line[MAX_LINE_LEN], line_raw[MAX_LINE_LEN], previous_line[MAX_LINE_LEN];
6484 char last_comment[MAX_LINE_LEN];
6485 char level_name[MAX_LINE_LEN];
6488 int num_levels_to_skip = level_file_info->nr - leveldir_current->first_level;
6489 boolean read_continued_line = FALSE;
6490 boolean reading_playfield = FALSE;
6491 boolean got_valid_playfield_line = FALSE;
6492 boolean invalid_playfield_char = FALSE;
6493 boolean load_xsb_to_ces = check_special_flags("load_xsb_to_ces");
6494 int file_level_nr = 0;
6495 int x = 0, y = 0; // initialized to make compilers happy
6497 last_comment[0] = '\0';
6498 level_name[0] = '\0';
6500 if (!(file = openFile(filename, MODE_READ)))
6502 level->no_valid_file = TRUE;
6504 if (!level_info_only)
6505 Warn("cannot read level '%s' -- using empty level", filename);
6510 while (!checkEndOfFile(file))
6512 // level successfully read, but next level may follow here
6513 if (!got_valid_playfield_line && reading_playfield)
6515 // read playfield from single level file -- skip remaining file
6516 if (!level_file_info->packed)
6519 if (file_level_nr >= num_levels_to_skip)
6524 last_comment[0] = '\0';
6525 level_name[0] = '\0';
6527 reading_playfield = FALSE;
6530 got_valid_playfield_line = FALSE;
6532 // read next line of input file
6533 if (!getStringFromFile(file, line, MAX_LINE_LEN))
6536 // cut trailing line break (this can be newline and/or carriage return)
6537 for (line_ptr = &line[strlen(line)]; line_ptr >= line; line_ptr--)
6538 if ((*line_ptr == '\n' || *line_ptr == '\r') && *(line_ptr + 1) == '\0')
6541 // copy raw input line for later use (mainly debugging output)
6542 strcpy(line_raw, line);
6544 if (read_continued_line)
6546 // append new line to existing line, if there is enough space
6547 if (strlen(previous_line) + strlen(line_ptr) < MAX_LINE_LEN)
6548 strcat(previous_line, line_ptr);
6550 strcpy(line, previous_line); // copy storage buffer to line
6552 read_continued_line = FALSE;
6555 // if the last character is '\', continue at next line
6556 if (strlen(line) > 0 && line[strlen(line) - 1] == '\\')
6558 line[strlen(line) - 1] = '\0'; // cut off trailing backslash
6559 strcpy(previous_line, line); // copy line to storage buffer
6561 read_continued_line = TRUE;
6567 if (line[0] == '\0')
6570 // extract comment text from comment line
6573 for (line_ptr = line; *line_ptr; line_ptr++)
6574 if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != ';')
6577 strcpy(last_comment, line_ptr);
6582 // extract level title text from line containing level title
6583 if (line[0] == '\'')
6585 strcpy(level_name, &line[1]);
6587 if (strlen(level_name) > 0 && level_name[strlen(level_name) - 1] == '\'')
6588 level_name[strlen(level_name) - 1] = '\0';
6593 // skip lines containing only spaces (or empty lines)
6594 for (line_ptr = line; *line_ptr; line_ptr++)
6595 if (*line_ptr != ' ')
6597 if (*line_ptr == '\0')
6600 // at this point, we have found a line containing part of a playfield
6602 got_valid_playfield_line = TRUE;
6604 if (!reading_playfield)
6606 reading_playfield = TRUE;
6607 invalid_playfield_char = FALSE;
6609 for (x = 0; x < MAX_LEV_FIELDX; x++)
6610 for (y = 0; y < MAX_LEV_FIELDY; y++)
6611 level->field[x][y] = getMappedElement_SB(' ', load_xsb_to_ces);
6616 // start with topmost tile row
6620 // skip playfield line if larger row than allowed
6621 if (y >= MAX_LEV_FIELDY)
6624 // start with leftmost tile column
6627 // read playfield elements from line
6628 for (line_ptr = line; *line_ptr; line_ptr++)
6630 int mapped_sb_element = getMappedElement_SB(*line_ptr, load_xsb_to_ces);
6632 // stop parsing playfield line if larger column than allowed
6633 if (x >= MAX_LEV_FIELDX)
6636 if (mapped_sb_element == EL_UNDEFINED)
6638 invalid_playfield_char = TRUE;
6643 level->field[x][y] = mapped_sb_element;
6645 // continue with next tile column
6648 level->fieldx = MAX(x, level->fieldx);
6651 if (invalid_playfield_char)
6653 // if first playfield line, treat invalid lines as comment lines
6655 reading_playfield = FALSE;
6660 // continue with next tile row
6668 level->fieldx = MIN(MAX(MIN_LEV_FIELDX, level->fieldx), MAX_LEV_FIELDX);
6669 level->fieldy = MIN(MAX(MIN_LEV_FIELDY, level->fieldy), MAX_LEV_FIELDY);
6671 if (!reading_playfield)
6673 level->no_valid_file = TRUE;
6675 Warn("cannot read level '%s' -- using empty level", filename);
6680 if (*level_name != '\0')
6682 strncpy(level->name, level_name, MAX_LEVEL_NAME_LEN);
6683 level->name[MAX_LEVEL_NAME_LEN] = '\0';
6685 else if (*last_comment != '\0')
6687 strncpy(level->name, last_comment, MAX_LEVEL_NAME_LEN);
6688 level->name[MAX_LEVEL_NAME_LEN] = '\0';
6692 sprintf(level->name, "--> Level %d <--", level_file_info->nr);
6695 // set all empty fields beyond the border walls to invisible steel wall
6696 for (y = 0; y < level->fieldy; y++) for (x = 0; x < level->fieldx; x++)
6698 if ((x == 0 || x == level->fieldx - 1 ||
6699 y == 0 || y == level->fieldy - 1) &&
6700 level->field[x][y] == getMappedElement_SB(' ', load_xsb_to_ces))
6701 FloodFillLevel(x, y, getMappedElement_SB('_', load_xsb_to_ces),
6702 level->field, level->fieldx, level->fieldy);
6705 // set special level settings for Sokoban levels
6706 SetLevelSettings_SB(level);
6708 if (load_xsb_to_ces)
6710 // special global settings can now be set in level template
6711 level->use_custom_template = TRUE;
6716 // -------------------------------------------------------------------------
6717 // functions for handling native levels
6718 // -------------------------------------------------------------------------
6720 static void LoadLevelFromFileInfo_BD(struct LevelInfo *level,
6721 struct LevelFileInfo *level_file_info,
6722 boolean level_info_only)
6726 // determine position of requested level inside level package
6727 if (level_file_info->packed)
6728 pos = level_file_info->nr - leveldir_current->first_level;
6730 if (!LoadNativeLevel_BD(level_file_info->filename, pos, level_info_only))
6731 level->no_valid_file = TRUE;
6734 static void LoadLevelFromFileInfo_EM(struct LevelInfo *level,
6735 struct LevelFileInfo *level_file_info,
6736 boolean level_info_only)
6738 if (!LoadNativeLevel_EM(level_file_info->filename, level_info_only))
6739 level->no_valid_file = TRUE;
6742 static void LoadLevelFromFileInfo_SP(struct LevelInfo *level,
6743 struct LevelFileInfo *level_file_info,
6744 boolean level_info_only)
6748 // determine position of requested level inside level package
6749 if (level_file_info->packed)
6750 pos = level_file_info->nr - leveldir_current->first_level;
6752 if (!LoadNativeLevel_SP(level_file_info->filename, pos, level_info_only))
6753 level->no_valid_file = TRUE;
6756 static void LoadLevelFromFileInfo_MM(struct LevelInfo *level,
6757 struct LevelFileInfo *level_file_info,
6758 boolean level_info_only)
6760 if (!LoadNativeLevel_MM(level_file_info->filename, level_info_only))
6761 level->no_valid_file = TRUE;
6764 void CopyNativeLevel_RND_to_Native(struct LevelInfo *level)
6766 if (level->game_engine_type == GAME_ENGINE_TYPE_BD)
6767 CopyNativeLevel_RND_to_BD(level);
6768 else if (level->game_engine_type == GAME_ENGINE_TYPE_EM)
6769 CopyNativeLevel_RND_to_EM(level);
6770 else if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
6771 CopyNativeLevel_RND_to_SP(level);
6772 else if (level->game_engine_type == GAME_ENGINE_TYPE_MM)
6773 CopyNativeLevel_RND_to_MM(level);
6776 void CopyNativeLevel_Native_to_RND(struct LevelInfo *level)
6778 if (level->game_engine_type == GAME_ENGINE_TYPE_BD)
6779 CopyNativeLevel_BD_to_RND(level);
6780 else if (level->game_engine_type == GAME_ENGINE_TYPE_EM)
6781 CopyNativeLevel_EM_to_RND(level);
6782 else if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
6783 CopyNativeLevel_SP_to_RND(level);
6784 else if (level->game_engine_type == GAME_ENGINE_TYPE_MM)
6785 CopyNativeLevel_MM_to_RND(level);
6788 void SaveNativeLevel(struct LevelInfo *level)
6790 // saving native level files only supported for some game engines
6791 if (level->game_engine_type != GAME_ENGINE_TYPE_BD &&
6792 level->game_engine_type != GAME_ENGINE_TYPE_SP)
6795 char *file_ext = (level->game_engine_type == GAME_ENGINE_TYPE_BD ? "bd" :
6796 level->game_engine_type == GAME_ENGINE_TYPE_SP ? "sp" : "");
6797 char *basename = getSingleLevelBasenameExt(level->file_info.nr, file_ext);
6798 char *filename = getLevelFilenameFromBasename(basename);
6800 if (fileExists(filename) && !Request("Native level file already exists! Overwrite it?", REQ_ASK))
6803 boolean success = FALSE;
6805 if (level->game_engine_type == GAME_ENGINE_TYPE_BD)
6807 CopyNativeLevel_RND_to_BD(level);
6808 // CopyNativeTape_RND_to_BD(level);
6810 success = SaveNativeLevel_BD(filename);
6812 else if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
6814 CopyNativeLevel_RND_to_SP(level);
6815 CopyNativeTape_RND_to_SP(level);
6817 success = SaveNativeLevel_SP(filename);
6821 Request("Native level file saved!", REQ_CONFIRM);
6823 Request("Failed to save native level file!", REQ_CONFIRM);
6827 // ----------------------------------------------------------------------------
6828 // functions for loading generic level
6829 // ----------------------------------------------------------------------------
6831 static void LoadLevelFromFileInfo(struct LevelInfo *level,
6832 struct LevelFileInfo *level_file_info,
6833 boolean level_info_only)
6835 // always start with reliable default values
6836 setLevelInfoToDefaults(level, level_info_only, TRUE);
6838 switch (level_file_info->type)
6840 case LEVEL_FILE_TYPE_RND:
6841 LoadLevelFromFileInfo_RND(level, level_file_info, level_info_only);
6844 case LEVEL_FILE_TYPE_BD:
6845 LoadLevelFromFileInfo_BD(level, level_file_info, level_info_only);
6846 level->game_engine_type = GAME_ENGINE_TYPE_BD;
6849 case LEVEL_FILE_TYPE_EM:
6850 LoadLevelFromFileInfo_EM(level, level_file_info, level_info_only);
6851 level->game_engine_type = GAME_ENGINE_TYPE_EM;
6854 case LEVEL_FILE_TYPE_SP:
6855 LoadLevelFromFileInfo_SP(level, level_file_info, level_info_only);
6856 level->game_engine_type = GAME_ENGINE_TYPE_SP;
6859 case LEVEL_FILE_TYPE_MM:
6860 LoadLevelFromFileInfo_MM(level, level_file_info, level_info_only);
6861 level->game_engine_type = GAME_ENGINE_TYPE_MM;
6864 case LEVEL_FILE_TYPE_DC:
6865 LoadLevelFromFileInfo_DC(level, level_file_info, level_info_only);
6868 case LEVEL_FILE_TYPE_SB:
6869 LoadLevelFromFileInfo_SB(level, level_file_info, level_info_only);
6873 LoadLevelFromFileInfo_RND(level, level_file_info, level_info_only);
6877 // if level file is invalid, restore level structure to default values
6878 if (level->no_valid_file)
6879 setLevelInfoToDefaults(level, level_info_only, FALSE);
6881 if (check_special_flags("use_native_bd_game_engine"))
6882 level->game_engine_type = GAME_ENGINE_TYPE_BD;
6884 if (level->game_engine_type == GAME_ENGINE_TYPE_UNKNOWN)
6885 level->game_engine_type = GAME_ENGINE_TYPE_RND;
6887 if (level_file_info->type != LEVEL_FILE_TYPE_RND)
6888 CopyNativeLevel_Native_to_RND(level);
6891 void LoadLevelFromFilename(struct LevelInfo *level, char *filename)
6893 static struct LevelFileInfo level_file_info;
6895 // always start with reliable default values
6896 setFileInfoToDefaults(&level_file_info);
6898 level_file_info.nr = 0; // unknown level number
6899 level_file_info.type = LEVEL_FILE_TYPE_RND; // no others supported yet
6901 setString(&level_file_info.filename, filename);
6903 LoadLevelFromFileInfo(level, &level_file_info, FALSE);
6906 static void LoadLevel_InitVersion(struct LevelInfo *level)
6910 if (leveldir_current == NULL) // only when dumping level
6913 // all engine modifications also valid for levels which use latest engine
6914 if (level->game_version < VERSION_IDENT(3,2,0,5))
6916 // time bonus score was given for 10 s instead of 1 s before 3.2.0-5
6917 level->time_score_base = 10;
6920 if (leveldir_current->latest_engine)
6922 // ---------- use latest game engine --------------------------------------
6924 /* For all levels which are forced to use the latest game engine version
6925 (normally all but user contributed, private and undefined levels), set
6926 the game engine version to the actual version; this allows for actual
6927 corrections in the game engine to take effect for existing, converted
6928 levels (from "classic" or other existing games) to make the emulation
6929 of the corresponding game more accurate, while (hopefully) not breaking
6930 existing levels created from other players. */
6932 level->game_version = GAME_VERSION_ACTUAL;
6934 /* Set special EM style gems behaviour: EM style gems slip down from
6935 normal, steel and growing wall. As this is a more fundamental change,
6936 it seems better to set the default behaviour to "off" (as it is more
6937 natural) and make it configurable in the level editor (as a property
6938 of gem style elements). Already existing converted levels (neither
6939 private nor contributed levels) are changed to the new behaviour. */
6941 if (level->file_version < FILE_VERSION_2_0)
6942 level->em_slippery_gems = TRUE;
6947 // ---------- use game engine the level was created with --------------------
6949 /* For all levels which are not forced to use the latest game engine
6950 version (normally user contributed, private and undefined levels),
6951 use the version of the game engine the levels were created for.
6953 Since 2.0.1, the game engine version is now directly stored
6954 in the level file (chunk "VERS"), so there is no need anymore
6955 to set the game version from the file version (except for old,
6956 pre-2.0 levels, where the game version is still taken from the
6957 file format version used to store the level -- see above). */
6959 // player was faster than enemies in 1.0.0 and before
6960 if (level->file_version == FILE_VERSION_1_0)
6961 for (i = 0; i < MAX_PLAYERS; i++)
6962 level->initial_player_stepsize[i] = STEPSIZE_FAST;
6964 // default behaviour for EM style gems was "slippery" only in 2.0.1
6965 if (level->game_version == VERSION_IDENT(2,0,1,0))
6966 level->em_slippery_gems = TRUE;
6968 // springs could be pushed over pits before (pre-release version) 2.2.0
6969 if (level->game_version < VERSION_IDENT(2,2,0,0))
6970 level->use_spring_bug = TRUE;
6972 if (level->game_version < VERSION_IDENT(3,2,0,5))
6974 // time orb caused limited time in endless time levels before 3.2.0-5
6975 level->use_time_orb_bug = TRUE;
6977 // default behaviour for snapping was "no snap delay" before 3.2.0-5
6978 level->block_snap_field = FALSE;
6980 // extra time score was same value as time left score before 3.2.0-5
6981 level->extra_time_score = level->score[SC_TIME_BONUS];
6984 if (level->game_version < VERSION_IDENT(3,2,0,7))
6986 // default behaviour for snapping was "not continuous" before 3.2.0-7
6987 level->continuous_snapping = FALSE;
6990 // only few elements were able to actively move into acid before 3.1.0
6991 // trigger settings did not exist before 3.1.0; set to default "any"
6992 if (level->game_version < VERSION_IDENT(3,1,0,0))
6994 // correct "can move into acid" settings (all zero in old levels)
6996 level->can_move_into_acid_bits = 0; // nothing can move into acid
6997 level->dont_collide_with_bits = 0; // nothing is deadly when colliding
6999 setMoveIntoAcidProperty(level, EL_ROBOT, TRUE);
7000 setMoveIntoAcidProperty(level, EL_SATELLITE, TRUE);
7001 setMoveIntoAcidProperty(level, EL_PENGUIN, TRUE);
7002 setMoveIntoAcidProperty(level, EL_BALLOON, TRUE);
7004 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7005 SET_PROPERTY(EL_CUSTOM_START + i, EP_CAN_MOVE_INTO_ACID, TRUE);
7007 // correct trigger settings (stored as zero == "none" in old levels)
7009 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7011 int element = EL_CUSTOM_START + i;
7012 struct ElementInfo *ei = &element_info[element];
7014 for (j = 0; j < ei->num_change_pages; j++)
7016 struct ElementChangeInfo *change = &ei->change_page[j];
7018 change->trigger_player = CH_PLAYER_ANY;
7019 change->trigger_page = CH_PAGE_ANY;
7024 // try to detect and fix "Snake Bite" levels, which are broken with 3.2.0
7026 int element = EL_CUSTOM_256;
7027 struct ElementInfo *ei = &element_info[element];
7028 struct ElementChangeInfo *change = &ei->change_page[0];
7030 /* This is needed to fix a problem that was caused by a bugfix in function
7031 game.c/CreateFieldExt() introduced with 3.2.0 that corrects the behaviour
7032 when a custom element changes to EL_SOKOBAN_FIELD_PLAYER (before, it did
7033 not replace walkable elements, but instead just placed the player on it,
7034 without placing the Sokoban field under the player). Unfortunately, this
7035 breaks "Snake Bite" style levels when the snake is halfway through a door
7036 that just closes (the snake head is still alive and can be moved in this
7037 case). This can be fixed by replacing the EL_SOKOBAN_FIELD_PLAYER by the
7038 player (without Sokoban element) which then gets killed as designed). */
7040 if ((strncmp(leveldir_current->identifier, "snake_bite", 10) == 0 ||
7041 strncmp(ei->description, "pause b4 death", 14) == 0) &&
7042 change->target_element == EL_SOKOBAN_FIELD_PLAYER)
7043 change->target_element = EL_PLAYER_1;
7046 // try to detect and fix "Zelda" style levels, which are broken with 3.2.5
7047 if (level->game_version < VERSION_IDENT(3,2,5,0))
7049 /* This is needed to fix a problem that was caused by a bugfix in function
7050 game.c/CheckTriggeredElementChangeExt() introduced with 3.2.5 that
7051 corrects the behaviour when a custom element changes to another custom
7052 element with a higher element number that has change actions defined.
7053 Normally, only one change per frame is allowed for custom elements.
7054 Therefore, it is checked if a custom element already changed in the
7055 current frame; if it did, subsequent changes are suppressed.
7056 Unfortunately, this is only checked for element changes, but not for
7057 change actions, which are still executed. As the function above loops
7058 through all custom elements from lower to higher, an element change
7059 resulting in a lower CE number won't be checked again, while a target
7060 element with a higher number will also be checked, and potential change
7061 actions will get executed for this CE, too (which is wrong), while
7062 further changes are ignored (which is correct). As this bugfix breaks
7063 Zelda II (and introduces graphical bugs to Zelda I, and also breaks a
7064 few other levels like Alan Bond's "FMV"), allow the previous, incorrect
7065 behaviour for existing levels and tapes that make use of this bug */
7067 level->use_action_after_change_bug = TRUE;
7070 // not centering level after relocating player was default only in 3.2.3
7071 if (level->game_version == VERSION_IDENT(3,2,3,0)) // (no pre-releases)
7072 level->shifted_relocation = TRUE;
7074 // EM style elements always chain-exploded in R'n'D engine before 3.2.6
7075 if (level->game_version < VERSION_IDENT(3,2,6,0))
7076 level->em_explodes_by_fire = TRUE;
7078 // levels were solved by the first player entering an exit up to 4.1.0.0
7079 if (level->game_version <= VERSION_IDENT(4,1,0,0))
7080 level->solved_by_one_player = TRUE;
7082 // game logic of "game of life" and "biomaze" was buggy before 4.1.1.1
7083 if (level->game_version < VERSION_IDENT(4,1,1,1))
7084 level->use_life_bugs = TRUE;
7086 // only Sokoban fields (but not objects) had to be solved before 4.1.1.1
7087 if (level->game_version < VERSION_IDENT(4,1,1,1))
7088 level->sb_objects_needed = FALSE;
7090 // CE actions were triggered by unfinished digging/collecting up to 4.2.2.0
7091 if (level->game_version <= VERSION_IDENT(4,2,2,0))
7092 level->finish_dig_collect = FALSE;
7094 // CE changing to player was kept under the player if walkable up to 4.2.3.1
7095 if (level->game_version <= VERSION_IDENT(4,2,3,1))
7096 level->keep_walkable_ce = TRUE;
7099 static void LoadLevel_InitSettings_SB(struct LevelInfo *level)
7101 boolean is_sokoban_level = TRUE; // unless non-Sokoban elements found
7104 // check if this level is (not) a Sokoban level
7105 for (y = 0; y < level->fieldy; y++)
7106 for (x = 0; x < level->fieldx; x++)
7107 if (!IS_SB_ELEMENT(Tile[x][y]))
7108 is_sokoban_level = FALSE;
7110 if (is_sokoban_level)
7112 // set special level settings for Sokoban levels
7113 SetLevelSettings_SB(level);
7117 static void LoadLevel_InitSettings(struct LevelInfo *level)
7119 // adjust level settings for (non-native) Sokoban-style levels
7120 LoadLevel_InitSettings_SB(level);
7122 // rename levels with title "nameless level" or if renaming is forced
7123 if (leveldir_current->empty_level_name != NULL &&
7124 (strEqual(level->name, NAMELESS_LEVEL_NAME) ||
7125 leveldir_current->force_level_name))
7126 snprintf(level->name, MAX_LEVEL_NAME_LEN + 1,
7127 leveldir_current->empty_level_name, level_nr);
7130 static void LoadLevel_InitStandardElements(struct LevelInfo *level)
7134 // map elements that have changed in newer versions
7135 level->amoeba_content = getMappedElementByVersion(level->amoeba_content,
7136 level->game_version);
7137 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
7138 for (x = 0; x < 3; x++)
7139 for (y = 0; y < 3; y++)
7140 level->yamyam_content[i].e[x][y] =
7141 getMappedElementByVersion(level->yamyam_content[i].e[x][y],
7142 level->game_version);
7146 static void LoadLevel_InitCustomElements(struct LevelInfo *level)
7150 // map custom element change events that have changed in newer versions
7151 // (these following values were accidentally changed in version 3.0.1)
7152 // (this seems to be needed only for 'ab_levelset3' and 'ab_levelset4')
7153 if (level->game_version <= VERSION_IDENT(3,0,0,0))
7155 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7157 int element = EL_CUSTOM_START + i;
7159 // order of checking and copying events to be mapped is important
7160 // (do not change the start and end value -- they are constant)
7161 for (j = CE_BY_OTHER_ACTION; j >= CE_VALUE_GETS_ZERO; j--)
7163 if (HAS_CHANGE_EVENT(element, j - 2))
7165 SET_CHANGE_EVENT(element, j - 2, FALSE);
7166 SET_CHANGE_EVENT(element, j, TRUE);
7170 // order of checking and copying events to be mapped is important
7171 // (do not change the start and end value -- they are constant)
7172 for (j = CE_PLAYER_COLLECTS_X; j >= CE_HITTING_SOMETHING; j--)
7174 if (HAS_CHANGE_EVENT(element, j - 1))
7176 SET_CHANGE_EVENT(element, j - 1, FALSE);
7177 SET_CHANGE_EVENT(element, j, TRUE);
7183 // initialize "can_change" field for old levels with only one change page
7184 if (level->game_version <= VERSION_IDENT(3,0,2,0))
7186 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7188 int element = EL_CUSTOM_START + i;
7190 if (CAN_CHANGE(element))
7191 element_info[element].change->can_change = TRUE;
7195 // correct custom element values (for old levels without these options)
7196 if (level->game_version < VERSION_IDENT(3,1,1,0))
7198 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7200 int element = EL_CUSTOM_START + i;
7201 struct ElementInfo *ei = &element_info[element];
7203 if (ei->access_direction == MV_NO_DIRECTION)
7204 ei->access_direction = MV_ALL_DIRECTIONS;
7208 // correct custom element values (fix invalid values for all versions)
7211 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7213 int element = EL_CUSTOM_START + i;
7214 struct ElementInfo *ei = &element_info[element];
7216 for (j = 0; j < ei->num_change_pages; j++)
7218 struct ElementChangeInfo *change = &ei->change_page[j];
7220 if (change->trigger_player == CH_PLAYER_NONE)
7221 change->trigger_player = CH_PLAYER_ANY;
7223 if (change->trigger_side == CH_SIDE_NONE)
7224 change->trigger_side = CH_SIDE_ANY;
7229 // initialize "can_explode" field for old levels which did not store this
7230 // !!! CHECK THIS -- "<= 3,1,0,0" IS PROBABLY WRONG !!!
7231 if (level->game_version <= VERSION_IDENT(3,1,0,0))
7233 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7235 int element = EL_CUSTOM_START + i;
7237 if (EXPLODES_1X1_OLD(element))
7238 element_info[element].explosion_type = EXPLODES_1X1;
7240 SET_PROPERTY(element, EP_CAN_EXPLODE, (EXPLODES_BY_FIRE(element) ||
7241 EXPLODES_SMASHED(element) ||
7242 EXPLODES_IMPACT(element)));
7246 // correct previously hard-coded move delay values for maze runner style
7247 if (level->game_version < VERSION_IDENT(3,1,1,0))
7249 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7251 int element = EL_CUSTOM_START + i;
7253 if (element_info[element].move_pattern & MV_MAZE_RUNNER_STYLE)
7255 // previously hard-coded and therefore ignored
7256 element_info[element].move_delay_fixed = 9;
7257 element_info[element].move_delay_random = 0;
7262 // set some other uninitialized values of custom elements in older levels
7263 if (level->game_version < VERSION_IDENT(3,1,0,0))
7265 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7267 int element = EL_CUSTOM_START + i;
7269 element_info[element].access_direction = MV_ALL_DIRECTIONS;
7271 element_info[element].explosion_delay = 17;
7272 element_info[element].ignition_delay = 8;
7276 // set mouse click change events to work for left/middle/right mouse button
7277 if (level->game_version < VERSION_IDENT(4,2,3,0))
7279 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7281 int element = EL_CUSTOM_START + i;
7282 struct ElementInfo *ei = &element_info[element];
7284 for (j = 0; j < ei->num_change_pages; j++)
7286 struct ElementChangeInfo *change = &ei->change_page[j];
7288 if (change->has_event[CE_CLICKED_BY_MOUSE] ||
7289 change->has_event[CE_PRESSED_BY_MOUSE] ||
7290 change->has_event[CE_MOUSE_CLICKED_ON_X] ||
7291 change->has_event[CE_MOUSE_PRESSED_ON_X])
7292 change->trigger_side = CH_SIDE_ANY;
7298 static void LoadLevel_InitElements(struct LevelInfo *level)
7300 LoadLevel_InitStandardElements(level);
7302 if (level->file_has_custom_elements)
7303 LoadLevel_InitCustomElements(level);
7305 // initialize element properties for level editor etc.
7306 InitElementPropertiesEngine(level->game_version);
7307 InitElementPropertiesGfxElement();
7310 static void LoadLevel_InitPlayfield(struct LevelInfo *level)
7314 // map elements that have changed in newer versions
7315 for (y = 0; y < level->fieldy; y++)
7316 for (x = 0; x < level->fieldx; x++)
7317 level->field[x][y] = getMappedElementByVersion(level->field[x][y],
7318 level->game_version);
7320 // clear unused playfield data (nicer if level gets resized in editor)
7321 for (x = 0; x < MAX_LEV_FIELDX; x++)
7322 for (y = 0; y < MAX_LEV_FIELDY; y++)
7323 if (x >= level->fieldx || y >= level->fieldy)
7324 level->field[x][y] = EL_EMPTY;
7326 // copy elements to runtime playfield array
7327 for (x = 0; x < MAX_LEV_FIELDX; x++)
7328 for (y = 0; y < MAX_LEV_FIELDY; y++)
7329 Tile[x][y] = level->field[x][y];
7331 // initialize level size variables for faster access
7332 lev_fieldx = level->fieldx;
7333 lev_fieldy = level->fieldy;
7335 // determine border element for this level
7336 if (level->file_info.type == LEVEL_FILE_TYPE_DC)
7337 BorderElement = EL_EMPTY; // (in editor, SetBorderElement() is used)
7342 static void LoadLevel_InitNativeEngines(struct LevelInfo *level)
7344 struct LevelFileInfo *level_file_info = &level->file_info;
7346 if (level_file_info->type == LEVEL_FILE_TYPE_RND)
7347 CopyNativeLevel_RND_to_Native(level);
7350 static void LoadLevelTemplate_LoadAndInit(void)
7352 LoadLevelFromFileInfo(&level_template, &level_template.file_info, FALSE);
7354 LoadLevel_InitVersion(&level_template);
7355 LoadLevel_InitElements(&level_template);
7356 LoadLevel_InitSettings(&level_template);
7358 ActivateLevelTemplate();
7361 void LoadLevelTemplate(int nr)
7363 if (!fileExists(getGlobalLevelTemplateFilename()))
7365 Warn("no level template found for this level");
7370 setLevelFileInfo(&level_template.file_info, nr);
7372 LoadLevelTemplate_LoadAndInit();
7375 static void LoadNetworkLevelTemplate(struct NetworkLevelInfo *network_level)
7377 copyLevelFileInfo(&network_level->tmpl_info, &level_template.file_info);
7379 LoadLevelTemplate_LoadAndInit();
7382 static void LoadLevel_LoadAndInit(struct NetworkLevelInfo *network_level)
7384 LoadLevelFromFileInfo(&level, &level.file_info, FALSE);
7386 if (level.use_custom_template)
7388 if (network_level != NULL)
7389 LoadNetworkLevelTemplate(network_level);
7391 LoadLevelTemplate(-1);
7394 LoadLevel_InitVersion(&level);
7395 LoadLevel_InitElements(&level);
7396 LoadLevel_InitPlayfield(&level);
7397 LoadLevel_InitSettings(&level);
7399 LoadLevel_InitNativeEngines(&level);
7402 void LoadLevel(int nr)
7404 SetLevelSetInfo(leveldir_current->identifier, nr);
7406 setLevelFileInfo(&level.file_info, nr);
7408 LoadLevel_LoadAndInit(NULL);
7411 void LoadLevelInfoOnly(int nr)
7413 setLevelFileInfo(&level.file_info, nr);
7415 LoadLevelFromFileInfo(&level, &level.file_info, TRUE);
7418 void LoadNetworkLevel(struct NetworkLevelInfo *network_level)
7420 SetLevelSetInfo(network_level->leveldir_identifier,
7421 network_level->file_info.nr);
7423 copyLevelFileInfo(&network_level->file_info, &level.file_info);
7425 LoadLevel_LoadAndInit(network_level);
7428 static int SaveLevel_VERS(FILE *file, struct LevelInfo *level)
7432 chunk_size += putFileVersion(file, level->file_version);
7433 chunk_size += putFileVersion(file, level->game_version);
7438 static int SaveLevel_DATE(FILE *file, struct LevelInfo *level)
7442 chunk_size += putFile16BitBE(file, level->creation_date.year);
7443 chunk_size += putFile8Bit(file, level->creation_date.month);
7444 chunk_size += putFile8Bit(file, level->creation_date.day);
7449 #if ENABLE_HISTORIC_CHUNKS
7450 static void SaveLevel_HEAD(FILE *file, struct LevelInfo *level)
7454 putFile8Bit(file, level->fieldx);
7455 putFile8Bit(file, level->fieldy);
7457 putFile16BitBE(file, level->time);
7458 putFile16BitBE(file, level->gems_needed);
7460 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
7461 putFile8Bit(file, level->name[i]);
7463 for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
7464 putFile8Bit(file, level->score[i]);
7466 for (i = 0; i < STD_ELEMENT_CONTENTS; i++)
7467 for (y = 0; y < 3; y++)
7468 for (x = 0; x < 3; x++)
7469 putFile8Bit(file, (level->encoding_16bit_yamyam ? EL_EMPTY :
7470 level->yamyam_content[i].e[x][y]));
7471 putFile8Bit(file, level->amoeba_speed);
7472 putFile8Bit(file, level->time_magic_wall);
7473 putFile8Bit(file, level->time_wheel);
7474 putFile8Bit(file, (level->encoding_16bit_amoeba ? EL_EMPTY :
7475 level->amoeba_content));
7476 putFile8Bit(file, (level->initial_player_stepsize == STEPSIZE_FAST ? 1 : 0));
7477 putFile8Bit(file, (level->initial_gravity ? 1 : 0));
7478 putFile8Bit(file, (level->encoding_16bit_field ? 1 : 0));
7479 putFile8Bit(file, (level->em_slippery_gems ? 1 : 0));
7481 putFile8Bit(file, (level->use_custom_template ? 1 : 0));
7483 putFile8Bit(file, (level->block_last_field ? 1 : 0));
7484 putFile8Bit(file, (level->sp_block_last_field ? 1 : 0));
7485 putFile32BitBE(file, level->can_move_into_acid_bits);
7486 putFile8Bit(file, level->dont_collide_with_bits);
7488 putFile8Bit(file, (level->use_spring_bug ? 1 : 0));
7489 putFile8Bit(file, (level->use_step_counter ? 1 : 0));
7491 putFile8Bit(file, (level->instant_relocation ? 1 : 0));
7492 putFile8Bit(file, (level->can_pass_to_walkable ? 1 : 0));
7493 putFile8Bit(file, (level->grow_into_diggable ? 1 : 0));
7495 putFile8Bit(file, level->game_engine_type);
7497 WriteUnusedBytesToFile(file, LEVEL_CHUNK_HEAD_UNUSED);
7501 static int SaveLevel_NAME(FILE *file, struct LevelInfo *level)
7506 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
7507 chunk_size += putFile8Bit(file, level->name[i]);
7512 static int SaveLevel_AUTH(FILE *file, struct LevelInfo *level)
7517 for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
7518 chunk_size += putFile8Bit(file, level->author[i]);
7523 #if ENABLE_HISTORIC_CHUNKS
7524 static int SaveLevel_BODY(FILE *file, struct LevelInfo *level)
7529 for (y = 0; y < level->fieldy; y++)
7530 for (x = 0; x < level->fieldx; x++)
7531 if (level->encoding_16bit_field)
7532 chunk_size += putFile16BitBE(file, level->field[x][y]);
7534 chunk_size += putFile8Bit(file, level->field[x][y]);
7540 static int SaveLevel_BODY(FILE *file, struct LevelInfo *level)
7545 for (y = 0; y < level->fieldy; y++)
7546 for (x = 0; x < level->fieldx; x++)
7547 chunk_size += putFile16BitBE(file, level->field[x][y]);
7552 #if ENABLE_HISTORIC_CHUNKS
7553 static void SaveLevel_CONT(FILE *file, struct LevelInfo *level)
7557 putFile8Bit(file, EL_YAMYAM);
7558 putFile8Bit(file, level->num_yamyam_contents);
7559 putFile8Bit(file, 0);
7560 putFile8Bit(file, 0);
7562 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
7563 for (y = 0; y < 3; y++)
7564 for (x = 0; x < 3; x++)
7565 if (level->encoding_16bit_field)
7566 putFile16BitBE(file, level->yamyam_content[i].e[x][y]);
7568 putFile8Bit(file, level->yamyam_content[i].e[x][y]);
7572 #if ENABLE_HISTORIC_CHUNKS
7573 static void SaveLevel_CNT2(FILE *file, struct LevelInfo *level, int element)
7576 int num_contents, content_xsize, content_ysize;
7577 int content_array[MAX_ELEMENT_CONTENTS][3][3];
7579 if (element == EL_YAMYAM)
7581 num_contents = level->num_yamyam_contents;
7585 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
7586 for (y = 0; y < 3; y++)
7587 for (x = 0; x < 3; x++)
7588 content_array[i][x][y] = level->yamyam_content[i].e[x][y];
7590 else if (element == EL_BD_AMOEBA)
7596 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
7597 for (y = 0; y < 3; y++)
7598 for (x = 0; x < 3; x++)
7599 content_array[i][x][y] = EL_EMPTY;
7600 content_array[0][0][0] = level->amoeba_content;
7604 // chunk header already written -- write empty chunk data
7605 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_SIZE);
7607 Warn("cannot save content for element '%d'", element);
7612 putFile16BitBE(file, element);
7613 putFile8Bit(file, num_contents);
7614 putFile8Bit(file, content_xsize);
7615 putFile8Bit(file, content_ysize);
7617 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_UNUSED);
7619 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
7620 for (y = 0; y < 3; y++)
7621 for (x = 0; x < 3; x++)
7622 putFile16BitBE(file, content_array[i][x][y]);
7626 #if ENABLE_HISTORIC_CHUNKS
7627 static int SaveLevel_CNT3(FILE *file, struct LevelInfo *level, int element)
7629 int envelope_nr = element - EL_ENVELOPE_1;
7630 int envelope_len = strlen(level->envelope_text[envelope_nr]) + 1;
7634 chunk_size += putFile16BitBE(file, element);
7635 chunk_size += putFile16BitBE(file, envelope_len);
7636 chunk_size += putFile8Bit(file, level->envelope_xsize[envelope_nr]);
7637 chunk_size += putFile8Bit(file, level->envelope_ysize[envelope_nr]);
7639 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT3_UNUSED);
7640 chunk_size += LEVEL_CHUNK_CNT3_UNUSED;
7642 for (i = 0; i < envelope_len; i++)
7643 chunk_size += putFile8Bit(file, level->envelope_text[envelope_nr][i]);
7649 #if ENABLE_HISTORIC_CHUNKS
7650 static void SaveLevel_CUS1(FILE *file, struct LevelInfo *level,
7651 int num_changed_custom_elements)
7655 putFile16BitBE(file, num_changed_custom_elements);
7657 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7659 int element = EL_CUSTOM_START + i;
7661 struct ElementInfo *ei = &element_info[element];
7663 if (ei->properties[EP_BITFIELD_BASE_NR] != EP_BITMASK_DEFAULT)
7665 if (check < num_changed_custom_elements)
7667 putFile16BitBE(file, element);
7668 putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
7675 if (check != num_changed_custom_elements) // should not happen
7676 Warn("inconsistent number of custom element properties");
7680 #if ENABLE_HISTORIC_CHUNKS
7681 static void SaveLevel_CUS2(FILE *file, struct LevelInfo *level,
7682 int num_changed_custom_elements)
7686 putFile16BitBE(file, num_changed_custom_elements);
7688 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7690 int element = EL_CUSTOM_START + i;
7692 if (element_info[element].change->target_element != EL_EMPTY_SPACE)
7694 if (check < num_changed_custom_elements)
7696 putFile16BitBE(file, element);
7697 putFile16BitBE(file, element_info[element].change->target_element);
7704 if (check != num_changed_custom_elements) // should not happen
7705 Warn("inconsistent number of custom target elements");
7709 #if ENABLE_HISTORIC_CHUNKS
7710 static void SaveLevel_CUS3(FILE *file, struct LevelInfo *level,
7711 int num_changed_custom_elements)
7713 int i, j, x, y, check = 0;
7715 putFile16BitBE(file, num_changed_custom_elements);
7717 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7719 int element = EL_CUSTOM_START + i;
7720 struct ElementInfo *ei = &element_info[element];
7722 if (ei->modified_settings)
7724 if (check < num_changed_custom_elements)
7726 putFile16BitBE(file, element);
7728 for (j = 0; j < MAX_ELEMENT_NAME_LEN; j++)
7729 putFile8Bit(file, ei->description[j]);
7731 putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
7733 // some free bytes for future properties and padding
7734 WriteUnusedBytesToFile(file, 7);
7736 putFile8Bit(file, ei->use_gfx_element);
7737 putFile16BitBE(file, ei->gfx_element_initial);
7739 putFile8Bit(file, ei->collect_score_initial);
7740 putFile8Bit(file, ei->collect_count_initial);
7742 putFile16BitBE(file, ei->push_delay_fixed);
7743 putFile16BitBE(file, ei->push_delay_random);
7744 putFile16BitBE(file, ei->move_delay_fixed);
7745 putFile16BitBE(file, ei->move_delay_random);
7747 putFile16BitBE(file, ei->move_pattern);
7748 putFile8Bit(file, ei->move_direction_initial);
7749 putFile8Bit(file, ei->move_stepsize);
7751 for (y = 0; y < 3; y++)
7752 for (x = 0; x < 3; x++)
7753 putFile16BitBE(file, ei->content.e[x][y]);
7755 putFile32BitBE(file, ei->change->events);
7757 putFile16BitBE(file, ei->change->target_element);
7759 putFile16BitBE(file, ei->change->delay_fixed);
7760 putFile16BitBE(file, ei->change->delay_random);
7761 putFile16BitBE(file, ei->change->delay_frames);
7763 putFile16BitBE(file, ei->change->initial_trigger_element);
7765 putFile8Bit(file, ei->change->explode);
7766 putFile8Bit(file, ei->change->use_target_content);
7767 putFile8Bit(file, ei->change->only_if_complete);
7768 putFile8Bit(file, ei->change->use_random_replace);
7770 putFile8Bit(file, ei->change->random_percentage);
7771 putFile8Bit(file, ei->change->replace_when);
7773 for (y = 0; y < 3; y++)
7774 for (x = 0; x < 3; x++)
7775 putFile16BitBE(file, ei->change->content.e[x][y]);
7777 putFile8Bit(file, ei->slippery_type);
7779 // some free bytes for future properties and padding
7780 WriteUnusedBytesToFile(file, LEVEL_CPART_CUS3_UNUSED);
7787 if (check != num_changed_custom_elements) // should not happen
7788 Warn("inconsistent number of custom element properties");
7792 #if ENABLE_HISTORIC_CHUNKS
7793 static void SaveLevel_CUS4(FILE *file, struct LevelInfo *level, int element)
7795 struct ElementInfo *ei = &element_info[element];
7798 // ---------- custom element base property values (96 bytes) ----------------
7800 putFile16BitBE(file, element);
7802 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
7803 putFile8Bit(file, ei->description[i]);
7805 putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
7807 WriteUnusedBytesToFile(file, 4); // reserved for more base properties
7809 putFile8Bit(file, ei->num_change_pages);
7811 putFile16BitBE(file, ei->ce_value_fixed_initial);
7812 putFile16BitBE(file, ei->ce_value_random_initial);
7813 putFile8Bit(file, ei->use_last_ce_value);
7815 putFile8Bit(file, ei->use_gfx_element);
7816 putFile16BitBE(file, ei->gfx_element_initial);
7818 putFile8Bit(file, ei->collect_score_initial);
7819 putFile8Bit(file, ei->collect_count_initial);
7821 putFile8Bit(file, ei->drop_delay_fixed);
7822 putFile8Bit(file, ei->push_delay_fixed);
7823 putFile8Bit(file, ei->drop_delay_random);
7824 putFile8Bit(file, ei->push_delay_random);
7825 putFile16BitBE(file, ei->move_delay_fixed);
7826 putFile16BitBE(file, ei->move_delay_random);
7828 // bits 0 - 15 of "move_pattern" ...
7829 putFile16BitBE(file, ei->move_pattern & 0xffff);
7830 putFile8Bit(file, ei->move_direction_initial);
7831 putFile8Bit(file, ei->move_stepsize);
7833 putFile8Bit(file, ei->slippery_type);
7835 for (y = 0; y < 3; y++)
7836 for (x = 0; x < 3; x++)
7837 putFile16BitBE(file, ei->content.e[x][y]);
7839 putFile16BitBE(file, ei->move_enter_element);
7840 putFile16BitBE(file, ei->move_leave_element);
7841 putFile8Bit(file, ei->move_leave_type);
7843 // ... bits 16 - 31 of "move_pattern" (not nice, but downward compatible)
7844 putFile16BitBE(file, (ei->move_pattern >> 16) & 0xffff);
7846 putFile8Bit(file, ei->access_direction);
7848 putFile8Bit(file, ei->explosion_delay);
7849 putFile8Bit(file, ei->ignition_delay);
7850 putFile8Bit(file, ei->explosion_type);
7852 // some free bytes for future custom property values and padding
7853 WriteUnusedBytesToFile(file, 1);
7855 // ---------- change page property values (48 bytes) ------------------------
7857 for (i = 0; i < ei->num_change_pages; i++)
7859 struct ElementChangeInfo *change = &ei->change_page[i];
7860 unsigned int event_bits;
7862 // bits 0 - 31 of "has_event[]" ...
7864 for (j = 0; j < MIN(NUM_CHANGE_EVENTS, 32); j++)
7865 if (change->has_event[j])
7866 event_bits |= (1u << j);
7867 putFile32BitBE(file, event_bits);
7869 putFile16BitBE(file, change->target_element);
7871 putFile16BitBE(file, change->delay_fixed);
7872 putFile16BitBE(file, change->delay_random);
7873 putFile16BitBE(file, change->delay_frames);
7875 putFile16BitBE(file, change->initial_trigger_element);
7877 putFile8Bit(file, change->explode);
7878 putFile8Bit(file, change->use_target_content);
7879 putFile8Bit(file, change->only_if_complete);
7880 putFile8Bit(file, change->use_random_replace);
7882 putFile8Bit(file, change->random_percentage);
7883 putFile8Bit(file, change->replace_when);
7885 for (y = 0; y < 3; y++)
7886 for (x = 0; x < 3; x++)
7887 putFile16BitBE(file, change->target_content.e[x][y]);
7889 putFile8Bit(file, change->can_change);
7891 putFile8Bit(file, change->trigger_side);
7893 putFile8Bit(file, change->trigger_player);
7894 putFile8Bit(file, (change->trigger_page == CH_PAGE_ANY ? CH_PAGE_ANY_FILE :
7895 log_2(change->trigger_page)));
7897 putFile8Bit(file, change->has_action);
7898 putFile8Bit(file, change->action_type);
7899 putFile8Bit(file, change->action_mode);
7900 putFile16BitBE(file, change->action_arg);
7902 // ... bits 32 - 39 of "has_event[]" (not nice, but downward compatible)
7904 for (j = 32; j < NUM_CHANGE_EVENTS; j++)
7905 if (change->has_event[j])
7906 event_bits |= (1u << (j - 32));
7907 putFile8Bit(file, event_bits);
7912 #if ENABLE_HISTORIC_CHUNKS
7913 static void SaveLevel_GRP1(FILE *file, struct LevelInfo *level, int element)
7915 struct ElementInfo *ei = &element_info[element];
7916 struct ElementGroupInfo *group = ei->group;
7919 putFile16BitBE(file, element);
7921 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
7922 putFile8Bit(file, ei->description[i]);
7924 putFile8Bit(file, group->num_elements);
7926 putFile8Bit(file, ei->use_gfx_element);
7927 putFile16BitBE(file, ei->gfx_element_initial);
7929 putFile8Bit(file, group->choice_mode);
7931 // some free bytes for future values and padding
7932 WriteUnusedBytesToFile(file, 3);
7934 for (i = 0; i < MAX_ELEMENTS_IN_GROUP; i++)
7935 putFile16BitBE(file, group->element[i]);
7939 static int SaveLevel_MicroChunk(FILE *file, struct LevelFileConfigInfo *entry,
7940 boolean write_element)
7942 int save_type = entry->save_type;
7943 int data_type = entry->data_type;
7944 int conf_type = entry->conf_type;
7945 int byte_mask = conf_type & CONF_MASK_BYTES;
7946 int element = entry->element;
7947 int default_value = entry->default_value;
7949 boolean modified = FALSE;
7951 if (byte_mask != CONF_MASK_MULTI_BYTES)
7953 void *value_ptr = entry->value;
7954 int value = (data_type == TYPE_BOOLEAN ? *(boolean *)value_ptr :
7957 // check if any settings have been modified before saving them
7958 if (value != default_value)
7961 // do not save if explicitly told or if unmodified default settings
7962 if ((save_type == SAVE_CONF_NEVER) ||
7963 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
7967 num_bytes += putFile16BitBE(file, element);
7969 num_bytes += putFile8Bit(file, conf_type);
7970 num_bytes += (byte_mask == CONF_MASK_1_BYTE ? putFile8Bit (file, value) :
7971 byte_mask == CONF_MASK_2_BYTE ? putFile16BitBE(file, value) :
7972 byte_mask == CONF_MASK_4_BYTE ? putFile32BitBE(file, value) :
7975 else if (data_type == TYPE_STRING)
7977 char *default_string = entry->default_string;
7978 char *string = (char *)(entry->value);
7979 int string_length = strlen(string);
7982 // check if any settings have been modified before saving them
7983 if (!strEqual(string, default_string))
7986 // do not save if explicitly told or if unmodified default settings
7987 if ((save_type == SAVE_CONF_NEVER) ||
7988 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
7992 num_bytes += putFile16BitBE(file, element);
7994 num_bytes += putFile8Bit(file, conf_type);
7995 num_bytes += putFile16BitBE(file, string_length);
7997 for (i = 0; i < string_length; i++)
7998 num_bytes += putFile8Bit(file, string[i]);
8000 else if (data_type == TYPE_ELEMENT_LIST)
8002 int *element_array = (int *)(entry->value);
8003 int num_elements = *(int *)(entry->num_entities);
8006 // check if any settings have been modified before saving them
8007 for (i = 0; i < num_elements; i++)
8008 if (element_array[i] != default_value)
8011 // do not save if explicitly told or if unmodified default settings
8012 if ((save_type == SAVE_CONF_NEVER) ||
8013 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
8017 num_bytes += putFile16BitBE(file, element);
8019 num_bytes += putFile8Bit(file, conf_type);
8020 num_bytes += putFile16BitBE(file, num_elements * CONF_ELEMENT_NUM_BYTES);
8022 for (i = 0; i < num_elements; i++)
8023 num_bytes += putFile16BitBE(file, element_array[i]);
8025 else if (data_type == TYPE_CONTENT_LIST)
8027 struct Content *content = (struct Content *)(entry->value);
8028 int num_contents = *(int *)(entry->num_entities);
8031 // check if any settings have been modified before saving them
8032 for (i = 0; i < num_contents; i++)
8033 for (y = 0; y < 3; y++)
8034 for (x = 0; x < 3; x++)
8035 if (content[i].e[x][y] != default_value)
8038 // do not save if explicitly told or if unmodified default settings
8039 if ((save_type == SAVE_CONF_NEVER) ||
8040 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
8044 num_bytes += putFile16BitBE(file, element);
8046 num_bytes += putFile8Bit(file, conf_type);
8047 num_bytes += putFile16BitBE(file, num_contents * CONF_CONTENT_NUM_BYTES);
8049 for (i = 0; i < num_contents; i++)
8050 for (y = 0; y < 3; y++)
8051 for (x = 0; x < 3; x++)
8052 num_bytes += putFile16BitBE(file, content[i].e[x][y]);
8058 static int SaveLevel_INFO(FILE *file, struct LevelInfo *level)
8063 li = *level; // copy level data into temporary buffer
8065 for (i = 0; chunk_config_INFO[i].data_type != -1; i++)
8066 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_INFO[i], FALSE);
8071 static int SaveLevel_ELEM(FILE *file, struct LevelInfo *level)
8076 li = *level; // copy level data into temporary buffer
8078 for (i = 0; chunk_config_ELEM[i].data_type != -1; i++)
8079 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_ELEM[i], TRUE);
8084 static int SaveLevel_NOTE(FILE *file, struct LevelInfo *level, int element)
8086 int envelope_nr = element - EL_ENVELOPE_1;
8090 chunk_size += putFile16BitBE(file, element);
8092 // copy envelope data into temporary buffer
8093 xx_envelope = level->envelope[envelope_nr];
8095 for (i = 0; chunk_config_NOTE[i].data_type != -1; i++)
8096 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_NOTE[i], FALSE);
8101 static int SaveLevel_CUSX(FILE *file, struct LevelInfo *level, int element)
8103 struct ElementInfo *ei = &element_info[element];
8107 chunk_size += putFile16BitBE(file, element);
8109 xx_ei = *ei; // copy element data into temporary buffer
8111 // set default description string for this specific element
8112 strcpy(xx_default_description, getDefaultElementDescription(ei));
8114 for (i = 0; chunk_config_CUSX_base[i].data_type != -1; i++)
8115 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_CUSX_base[i], FALSE);
8117 for (i = 0; i < ei->num_change_pages; i++)
8119 struct ElementChangeInfo *change = &ei->change_page[i];
8121 xx_current_change_page = i;
8123 xx_change = *change; // copy change data into temporary buffer
8126 setEventBitsFromEventFlags(change);
8128 for (j = 0; chunk_config_CUSX_change[j].data_type != -1; j++)
8129 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_CUSX_change[j],
8136 static int SaveLevel_GRPX(FILE *file, struct LevelInfo *level, int element)
8138 struct ElementInfo *ei = &element_info[element];
8139 struct ElementGroupInfo *group = ei->group;
8143 chunk_size += putFile16BitBE(file, element);
8145 xx_ei = *ei; // copy element data into temporary buffer
8146 xx_group = *group; // copy group data into temporary buffer
8148 // set default description string for this specific element
8149 strcpy(xx_default_description, getDefaultElementDescription(ei));
8151 for (i = 0; chunk_config_GRPX[i].data_type != -1; i++)
8152 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_GRPX[i], FALSE);
8157 static int SaveLevel_EMPX(FILE *file, struct LevelInfo *level, int element)
8159 struct ElementInfo *ei = &element_info[element];
8163 chunk_size += putFile16BitBE(file, element);
8165 xx_ei = *ei; // copy element data into temporary buffer
8167 for (i = 0; chunk_config_EMPX[i].data_type != -1; i++)
8168 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_EMPX[i], FALSE);
8173 static void SaveLevelFromFilename(struct LevelInfo *level, char *filename,
8174 boolean save_as_template)
8180 if (!(file = fopen(filename, MODE_WRITE)))
8182 Warn("cannot save level file '%s'", filename);
8187 level->file_version = FILE_VERSION_ACTUAL;
8188 level->game_version = GAME_VERSION_ACTUAL;
8190 level->creation_date = getCurrentDate();
8192 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
8193 putFileChunkBE(file, "CAVE", CHUNK_SIZE_NONE);
8195 chunk_size = SaveLevel_VERS(NULL, level);
8196 putFileChunkBE(file, "VERS", chunk_size);
8197 SaveLevel_VERS(file, level);
8199 chunk_size = SaveLevel_DATE(NULL, level);
8200 putFileChunkBE(file, "DATE", chunk_size);
8201 SaveLevel_DATE(file, level);
8203 chunk_size = SaveLevel_NAME(NULL, level);
8204 putFileChunkBE(file, "NAME", chunk_size);
8205 SaveLevel_NAME(file, level);
8207 chunk_size = SaveLevel_AUTH(NULL, level);
8208 putFileChunkBE(file, "AUTH", chunk_size);
8209 SaveLevel_AUTH(file, level);
8211 chunk_size = SaveLevel_INFO(NULL, level);
8212 putFileChunkBE(file, "INFO", chunk_size);
8213 SaveLevel_INFO(file, level);
8215 chunk_size = SaveLevel_BODY(NULL, level);
8216 putFileChunkBE(file, "BODY", chunk_size);
8217 SaveLevel_BODY(file, level);
8219 chunk_size = SaveLevel_ELEM(NULL, level);
8220 if (chunk_size > LEVEL_CHUNK_ELEM_UNCHANGED) // save if changed
8222 putFileChunkBE(file, "ELEM", chunk_size);
8223 SaveLevel_ELEM(file, level);
8226 for (i = 0; i < NUM_ENVELOPES; i++)
8228 int element = EL_ENVELOPE_1 + i;
8230 chunk_size = SaveLevel_NOTE(NULL, level, element);
8231 if (chunk_size > LEVEL_CHUNK_NOTE_UNCHANGED) // save if changed
8233 putFileChunkBE(file, "NOTE", chunk_size);
8234 SaveLevel_NOTE(file, level, element);
8238 // if not using template level, check for non-default custom/group elements
8239 if (!level->use_custom_template || save_as_template)
8241 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
8243 int element = EL_CUSTOM_START + i;
8245 chunk_size = SaveLevel_CUSX(NULL, level, element);
8246 if (chunk_size > LEVEL_CHUNK_CUSX_UNCHANGED) // save if changed
8248 putFileChunkBE(file, "CUSX", chunk_size);
8249 SaveLevel_CUSX(file, level, element);
8253 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
8255 int element = EL_GROUP_START + i;
8257 chunk_size = SaveLevel_GRPX(NULL, level, element);
8258 if (chunk_size > LEVEL_CHUNK_GRPX_UNCHANGED) // save if changed
8260 putFileChunkBE(file, "GRPX", chunk_size);
8261 SaveLevel_GRPX(file, level, element);
8265 for (i = 0; i < NUM_EMPTY_ELEMENTS_ALL; i++)
8267 int element = GET_EMPTY_ELEMENT(i);
8269 chunk_size = SaveLevel_EMPX(NULL, level, element);
8270 if (chunk_size > LEVEL_CHUNK_EMPX_UNCHANGED) // save if changed
8272 putFileChunkBE(file, "EMPX", chunk_size);
8273 SaveLevel_EMPX(file, level, element);
8280 SetFilePermissions(filename, PERMS_PRIVATE);
8283 void SaveLevel(int nr)
8285 char *filename = getDefaultLevelFilename(nr);
8287 SaveLevelFromFilename(&level, filename, FALSE);
8290 void SaveLevelTemplate(void)
8292 char *filename = getLocalLevelTemplateFilename();
8294 SaveLevelFromFilename(&level, filename, TRUE);
8297 boolean SaveLevelChecked(int nr)
8299 char *filename = getDefaultLevelFilename(nr);
8300 boolean new_level = !fileExists(filename);
8301 boolean level_saved = FALSE;
8303 if (new_level || Request("Save this level and kill the old?", REQ_ASK))
8308 Request("Level saved!", REQ_CONFIRM);
8316 void DumpLevel(struct LevelInfo *level)
8318 if (level->no_level_file || level->no_valid_file)
8320 Warn("cannot dump -- no valid level file found");
8326 Print("Level xxx (file version %08d, game version %08d)\n",
8327 level->file_version, level->game_version);
8330 Print("Level author: '%s'\n", level->author);
8331 Print("Level title: '%s'\n", level->name);
8333 Print("Playfield size: %d x %d\n", level->fieldx, level->fieldy);
8335 Print("Level time: %d seconds\n", level->time);
8336 Print("Gems needed: %d\n", level->gems_needed);
8338 Print("Time for magic wall: %d seconds\n", level->time_magic_wall);
8339 Print("Time for wheel: %d seconds\n", level->time_wheel);
8340 Print("Time for light: %d seconds\n", level->time_light);
8341 Print("Time for timegate: %d seconds\n", level->time_timegate);
8343 Print("Amoeba speed: %d\n", level->amoeba_speed);
8346 Print("EM style slippery gems: %s\n", (level->em_slippery_gems ? "yes" : "no"));
8347 Print("Player blocks last field: %s\n", (level->block_last_field ? "yes" : "no"));
8348 Print("SP player blocks last field: %s\n", (level->sp_block_last_field ? "yes" : "no"));
8349 Print("use spring bug: %s\n", (level->use_spring_bug ? "yes" : "no"));
8350 Print("use step counter: %s\n", (level->use_step_counter ? "yes" : "no"));
8351 Print("rate time over score: %s\n", (level->rate_time_over_score ? "yes" : "no"));
8357 for (i = 0; i < NUM_ENVELOPES; i++)
8359 char *text = level->envelope[i].text;
8360 int text_len = strlen(text);
8361 boolean has_text = FALSE;
8363 for (j = 0; j < text_len; j++)
8364 if (text[j] != ' ' && text[j] != '\n')
8370 Print("Envelope %d:\n'%s'\n", i + 1, text);
8378 void DumpLevels(void)
8380 static LevelDirTree *dumplevel_leveldir = NULL;
8382 dumplevel_leveldir = getTreeInfoFromIdentifier(leveldir_first,
8383 global.dumplevel_leveldir);
8385 if (dumplevel_leveldir == NULL)
8386 Fail("no such level identifier: '%s'", global.dumplevel_leveldir);
8388 if (global.dumplevel_level_nr < dumplevel_leveldir->first_level ||
8389 global.dumplevel_level_nr > dumplevel_leveldir->last_level)
8390 Fail("no such level number: %d", global.dumplevel_level_nr);
8392 leveldir_current = dumplevel_leveldir;
8394 LoadLevel(global.dumplevel_level_nr);
8401 // ============================================================================
8402 // tape file functions
8403 // ============================================================================
8405 static void setTapeInfoToDefaults(void)
8409 // always start with reliable default values (empty tape)
8412 // default values (also for pre-1.2 tapes) with only the first player
8413 tape.player_participates[0] = TRUE;
8414 for (i = 1; i < MAX_PLAYERS; i++)
8415 tape.player_participates[i] = FALSE;
8417 // at least one (default: the first) player participates in every tape
8418 tape.num_participating_players = 1;
8420 tape.property_bits = TAPE_PROPERTY_NONE;
8422 tape.level_nr = level_nr;
8424 tape.changed = FALSE;
8425 tape.solved = FALSE;
8427 tape.recording = FALSE;
8428 tape.playing = FALSE;
8429 tape.pausing = FALSE;
8431 tape.scr_fieldx = SCR_FIELDX_DEFAULT;
8432 tape.scr_fieldy = SCR_FIELDY_DEFAULT;
8434 tape.no_info_chunk = TRUE;
8435 tape.no_valid_file = FALSE;
8438 static int getTapePosSize(struct TapeInfo *tape)
8440 int tape_pos_size = 0;
8442 if (tape->use_key_actions)
8443 tape_pos_size += tape->num_participating_players;
8445 if (tape->use_mouse_actions)
8446 tape_pos_size += 3; // x and y position and mouse button mask
8448 tape_pos_size += 1; // tape action delay value
8450 return tape_pos_size;
8453 static void setTapeActionFlags(struct TapeInfo *tape, int value)
8455 tape->use_key_actions = FALSE;
8456 tape->use_mouse_actions = FALSE;
8458 if (value != TAPE_USE_MOUSE_ACTIONS_ONLY)
8459 tape->use_key_actions = TRUE;
8461 if (value != TAPE_USE_KEY_ACTIONS_ONLY)
8462 tape->use_mouse_actions = TRUE;
8465 static int getTapeActionValue(struct TapeInfo *tape)
8467 return (tape->use_key_actions &&
8468 tape->use_mouse_actions ? TAPE_USE_KEY_AND_MOUSE_ACTIONS :
8469 tape->use_key_actions ? TAPE_USE_KEY_ACTIONS_ONLY :
8470 tape->use_mouse_actions ? TAPE_USE_MOUSE_ACTIONS_ONLY :
8471 TAPE_ACTIONS_DEFAULT);
8474 static int LoadTape_VERS(File *file, int chunk_size, struct TapeInfo *tape)
8476 tape->file_version = getFileVersion(file);
8477 tape->game_version = getFileVersion(file);
8482 static int LoadTape_HEAD(File *file, int chunk_size, struct TapeInfo *tape)
8486 tape->random_seed = getFile32BitBE(file);
8487 tape->date = getFile32BitBE(file);
8488 tape->length = getFile32BitBE(file);
8490 // read header fields that are new since version 1.2
8491 if (tape->file_version >= FILE_VERSION_1_2)
8493 byte store_participating_players = getFile8Bit(file);
8496 // since version 1.2, tapes store which players participate in the tape
8497 tape->num_participating_players = 0;
8498 for (i = 0; i < MAX_PLAYERS; i++)
8500 tape->player_participates[i] = FALSE;
8502 if (store_participating_players & (1 << i))
8504 tape->player_participates[i] = TRUE;
8505 tape->num_participating_players++;
8509 setTapeActionFlags(tape, getFile8Bit(file));
8511 tape->property_bits = getFile8Bit(file);
8512 tape->solved = getFile8Bit(file);
8514 engine_version = getFileVersion(file);
8515 if (engine_version > 0)
8516 tape->engine_version = engine_version;
8518 tape->engine_version = tape->game_version;
8524 static int LoadTape_SCRN(File *file, int chunk_size, struct TapeInfo *tape)
8526 tape->scr_fieldx = getFile8Bit(file);
8527 tape->scr_fieldy = getFile8Bit(file);
8532 static int LoadTape_INFO(File *file, int chunk_size, struct TapeInfo *tape)
8534 char *level_identifier = NULL;
8535 int level_identifier_size;
8538 tape->no_info_chunk = FALSE;
8540 level_identifier_size = getFile16BitBE(file);
8542 level_identifier = checked_malloc(level_identifier_size);
8544 for (i = 0; i < level_identifier_size; i++)
8545 level_identifier[i] = getFile8Bit(file);
8547 strncpy(tape->level_identifier, level_identifier, MAX_FILENAME_LEN);
8548 tape->level_identifier[MAX_FILENAME_LEN] = '\0';
8550 checked_free(level_identifier);
8552 tape->level_nr = getFile16BitBE(file);
8554 chunk_size = 2 + level_identifier_size + 2;
8559 static int LoadTape_BODY(File *file, int chunk_size, struct TapeInfo *tape)
8562 int tape_pos_size = getTapePosSize(tape);
8563 int chunk_size_expected = tape_pos_size * tape->length;
8565 if (chunk_size_expected != chunk_size)
8567 ReadUnusedBytesFromFile(file, chunk_size);
8568 return chunk_size_expected;
8571 for (i = 0; i < tape->length; i++)
8573 if (i >= MAX_TAPE_LEN)
8575 Warn("tape truncated -- size exceeds maximum tape size %d",
8578 // tape too large; read and ignore remaining tape data from this chunk
8579 for (;i < tape->length; i++)
8580 ReadUnusedBytesFromFile(file, tape_pos_size);
8585 if (tape->use_key_actions)
8587 for (j = 0; j < MAX_PLAYERS; j++)
8589 tape->pos[i].action[j] = MV_NONE;
8591 if (tape->player_participates[j])
8592 tape->pos[i].action[j] = getFile8Bit(file);
8596 if (tape->use_mouse_actions)
8598 tape->pos[i].action[TAPE_ACTION_LX] = getFile8Bit(file);
8599 tape->pos[i].action[TAPE_ACTION_LY] = getFile8Bit(file);
8600 tape->pos[i].action[TAPE_ACTION_BUTTON] = getFile8Bit(file);
8603 tape->pos[i].delay = getFile8Bit(file);
8605 if (tape->file_version == FILE_VERSION_1_0)
8607 // eliminate possible diagonal moves in old tapes
8608 // this is only for backward compatibility
8610 byte joy_dir[4] = { JOY_LEFT, JOY_RIGHT, JOY_UP, JOY_DOWN };
8611 byte action = tape->pos[i].action[0];
8612 int k, num_moves = 0;
8614 for (k = 0; k < 4; k++)
8616 if (action & joy_dir[k])
8618 tape->pos[i + num_moves].action[0] = joy_dir[k];
8620 tape->pos[i + num_moves].delay = 0;
8629 tape->length += num_moves;
8632 else if (tape->file_version < FILE_VERSION_2_0)
8634 // convert pre-2.0 tapes to new tape format
8636 if (tape->pos[i].delay > 1)
8639 tape->pos[i + 1] = tape->pos[i];
8640 tape->pos[i + 1].delay = 1;
8643 for (j = 0; j < MAX_PLAYERS; j++)
8644 tape->pos[i].action[j] = MV_NONE;
8645 tape->pos[i].delay--;
8652 if (checkEndOfFile(file))
8656 if (i != tape->length)
8657 chunk_size = tape_pos_size * i;
8662 static void LoadTape_SokobanSolution(char *filename)
8665 int move_delay = TILESIZE / level.initial_player_stepsize[0];
8667 if (!(file = openFile(filename, MODE_READ)))
8669 tape.no_valid_file = TRUE;
8674 while (!checkEndOfFile(file))
8676 unsigned char c = getByteFromFile(file);
8678 if (checkEndOfFile(file))
8685 tape.pos[tape.length].action[0] = MV_UP;
8686 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
8692 tape.pos[tape.length].action[0] = MV_DOWN;
8693 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
8699 tape.pos[tape.length].action[0] = MV_LEFT;
8700 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
8706 tape.pos[tape.length].action[0] = MV_RIGHT;
8707 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
8715 // ignore white-space characters
8719 tape.no_valid_file = TRUE;
8721 Warn("unsupported Sokoban solution file '%s' ['%d']", filename, c);
8729 if (tape.no_valid_file)
8732 tape.length_frames = GetTapeLengthFrames();
8733 tape.length_seconds = GetTapeLengthSeconds();
8736 void LoadTapeFromFilename(char *filename)
8738 char cookie[MAX_LINE_LEN];
8739 char chunk_name[CHUNK_ID_LEN + 1];
8743 // always start with reliable default values
8744 setTapeInfoToDefaults();
8746 if (strSuffix(filename, ".sln"))
8748 LoadTape_SokobanSolution(filename);
8753 if (!(file = openFile(filename, MODE_READ)))
8755 tape.no_valid_file = TRUE;
8760 getFileChunkBE(file, chunk_name, NULL);
8761 if (strEqual(chunk_name, "RND1"))
8763 getFile32BitBE(file); // not used
8765 getFileChunkBE(file, chunk_name, NULL);
8766 if (!strEqual(chunk_name, "TAPE"))
8768 tape.no_valid_file = TRUE;
8770 Warn("unknown format of tape file '%s'", filename);
8777 else // check for pre-2.0 file format with cookie string
8779 strcpy(cookie, chunk_name);
8780 if (getStringFromFile(file, &cookie[4], MAX_LINE_LEN - 4) == NULL)
8782 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
8783 cookie[strlen(cookie) - 1] = '\0';
8785 if (!checkCookieString(cookie, TAPE_COOKIE_TMPL))
8787 tape.no_valid_file = TRUE;
8789 Warn("unknown format of tape file '%s'", filename);
8796 if ((tape.file_version = getFileVersionFromCookieString(cookie)) == -1)
8798 tape.no_valid_file = TRUE;
8800 Warn("unsupported version of tape file '%s'", filename);
8807 // pre-2.0 tape files have no game version, so use file version here
8808 tape.game_version = tape.file_version;
8811 if (tape.file_version < FILE_VERSION_1_2)
8813 // tape files from versions before 1.2.0 without chunk structure
8814 LoadTape_HEAD(file, TAPE_CHUNK_HEAD_SIZE, &tape);
8815 LoadTape_BODY(file, 2 * tape.length, &tape);
8823 int (*loader)(File *, int, struct TapeInfo *);
8827 { "VERS", TAPE_CHUNK_VERS_SIZE, LoadTape_VERS },
8828 { "HEAD", TAPE_CHUNK_HEAD_SIZE, LoadTape_HEAD },
8829 { "SCRN", TAPE_CHUNK_SCRN_SIZE, LoadTape_SCRN },
8830 { "INFO", -1, LoadTape_INFO },
8831 { "BODY", -1, LoadTape_BODY },
8835 while (getFileChunkBE(file, chunk_name, &chunk_size))
8839 while (chunk_info[i].name != NULL &&
8840 !strEqual(chunk_name, chunk_info[i].name))
8843 if (chunk_info[i].name == NULL)
8845 Warn("unknown chunk '%s' in tape file '%s'",
8846 chunk_name, filename);
8848 ReadUnusedBytesFromFile(file, chunk_size);
8850 else if (chunk_info[i].size != -1 &&
8851 chunk_info[i].size != chunk_size)
8853 Warn("wrong size (%d) of chunk '%s' in tape file '%s'",
8854 chunk_size, chunk_name, filename);
8856 ReadUnusedBytesFromFile(file, chunk_size);
8860 // call function to load this tape chunk
8861 int chunk_size_expected =
8862 (chunk_info[i].loader)(file, chunk_size, &tape);
8864 // the size of some chunks cannot be checked before reading other
8865 // chunks first (like "HEAD" and "BODY") that contain some header
8866 // information, so check them here
8867 if (chunk_size_expected != chunk_size)
8869 Warn("wrong size (%d) of chunk '%s' in tape file '%s'",
8870 chunk_size, chunk_name, filename);
8878 tape.length_frames = GetTapeLengthFrames();
8879 tape.length_seconds = GetTapeLengthSeconds();
8882 Debug("files:LoadTapeFromFilename", "tape file version: %d",
8884 Debug("files:LoadTapeFromFilename", "tape game version: %d",
8886 Debug("files:LoadTapeFromFilename", "tape engine version: %d",
8887 tape.engine_version);
8891 void LoadTape(int nr)
8893 char *filename = getTapeFilename(nr);
8895 LoadTapeFromFilename(filename);
8898 void LoadSolutionTape(int nr)
8900 char *filename = getSolutionTapeFilename(nr);
8902 LoadTapeFromFilename(filename);
8904 if (TAPE_IS_EMPTY(tape))
8906 if (level.game_engine_type == GAME_ENGINE_TYPE_BD &&
8907 level.native_bd_level->replay != NULL)
8908 CopyNativeTape_BD_to_RND(&level);
8909 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP &&
8910 level.native_sp_level->demo.is_available)
8911 CopyNativeTape_SP_to_RND(&level);
8915 void LoadScoreTape(char *score_tape_basename, int nr)
8917 char *filename = getScoreTapeFilename(score_tape_basename, nr);
8919 LoadTapeFromFilename(filename);
8922 void LoadScoreCacheTape(char *score_tape_basename, int nr)
8924 char *filename = getScoreCacheTapeFilename(score_tape_basename, nr);
8926 LoadTapeFromFilename(filename);
8929 static boolean checkSaveTape_SCRN(struct TapeInfo *tape)
8931 // chunk required for team mode tapes with non-default screen size
8932 return (tape->num_participating_players > 1 &&
8933 (tape->scr_fieldx != SCR_FIELDX_DEFAULT ||
8934 tape->scr_fieldy != SCR_FIELDY_DEFAULT));
8937 static void SaveTape_VERS(FILE *file, struct TapeInfo *tape)
8939 putFileVersion(file, tape->file_version);
8940 putFileVersion(file, tape->game_version);
8943 static void SaveTape_HEAD(FILE *file, struct TapeInfo *tape)
8946 byte store_participating_players = 0;
8948 // set bits for participating players for compact storage
8949 for (i = 0; i < MAX_PLAYERS; i++)
8950 if (tape->player_participates[i])
8951 store_participating_players |= (1 << i);
8953 putFile32BitBE(file, tape->random_seed);
8954 putFile32BitBE(file, tape->date);
8955 putFile32BitBE(file, tape->length);
8957 putFile8Bit(file, store_participating_players);
8959 putFile8Bit(file, getTapeActionValue(tape));
8961 putFile8Bit(file, tape->property_bits);
8962 putFile8Bit(file, tape->solved);
8964 putFileVersion(file, tape->engine_version);
8967 static void SaveTape_SCRN(FILE *file, struct TapeInfo *tape)
8969 putFile8Bit(file, tape->scr_fieldx);
8970 putFile8Bit(file, tape->scr_fieldy);
8973 static void SaveTape_INFO(FILE *file, struct TapeInfo *tape)
8975 int level_identifier_size = strlen(tape->level_identifier) + 1;
8978 putFile16BitBE(file, level_identifier_size);
8980 for (i = 0; i < level_identifier_size; i++)
8981 putFile8Bit(file, tape->level_identifier[i]);
8983 putFile16BitBE(file, tape->level_nr);
8986 static void SaveTape_BODY(FILE *file, struct TapeInfo *tape)
8990 for (i = 0; i < tape->length; i++)
8992 if (tape->use_key_actions)
8994 for (j = 0; j < MAX_PLAYERS; j++)
8995 if (tape->player_participates[j])
8996 putFile8Bit(file, tape->pos[i].action[j]);
8999 if (tape->use_mouse_actions)
9001 putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_LX]);
9002 putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_LY]);
9003 putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_BUTTON]);
9006 putFile8Bit(file, tape->pos[i].delay);
9010 void SaveTapeToFilename(char *filename)
9014 int info_chunk_size;
9015 int body_chunk_size;
9017 if (!(file = fopen(filename, MODE_WRITE)))
9019 Warn("cannot save level recording file '%s'", filename);
9024 tape_pos_size = getTapePosSize(&tape);
9026 info_chunk_size = 2 + (strlen(tape.level_identifier) + 1) + 2;
9027 body_chunk_size = tape_pos_size * tape.length;
9029 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
9030 putFileChunkBE(file, "TAPE", CHUNK_SIZE_NONE);
9032 putFileChunkBE(file, "VERS", TAPE_CHUNK_VERS_SIZE);
9033 SaveTape_VERS(file, &tape);
9035 putFileChunkBE(file, "HEAD", TAPE_CHUNK_HEAD_SIZE);
9036 SaveTape_HEAD(file, &tape);
9038 if (checkSaveTape_SCRN(&tape))
9040 putFileChunkBE(file, "SCRN", TAPE_CHUNK_SCRN_SIZE);
9041 SaveTape_SCRN(file, &tape);
9044 putFileChunkBE(file, "INFO", info_chunk_size);
9045 SaveTape_INFO(file, &tape);
9047 putFileChunkBE(file, "BODY", body_chunk_size);
9048 SaveTape_BODY(file, &tape);
9052 SetFilePermissions(filename, PERMS_PRIVATE);
9055 static void SaveTapeExt(char *filename)
9059 tape.file_version = FILE_VERSION_ACTUAL;
9060 tape.game_version = GAME_VERSION_ACTUAL;
9062 tape.num_participating_players = 0;
9064 // count number of participating players
9065 for (i = 0; i < MAX_PLAYERS; i++)
9066 if (tape.player_participates[i])
9067 tape.num_participating_players++;
9069 SaveTapeToFilename(filename);
9071 tape.changed = FALSE;
9074 void SaveTape(int nr)
9076 char *filename = getTapeFilename(nr);
9078 InitTapeDirectory(leveldir_current->subdir);
9080 SaveTapeExt(filename);
9083 void SaveScoreTape(int nr)
9085 char *filename = getScoreTapeFilename(tape.score_tape_basename, nr);
9087 // used instead of "leveldir_current->subdir" (for network games)
9088 InitScoreTapeDirectory(levelset.identifier, nr);
9090 SaveTapeExt(filename);
9093 static boolean SaveTapeCheckedExt(int nr, char *msg_replace, char *msg_saved,
9094 unsigned int req_state_added)
9096 char *filename = getTapeFilename(nr);
9097 boolean new_tape = !fileExists(filename);
9098 boolean tape_saved = FALSE;
9100 if (new_tape || Request(msg_replace, REQ_ASK | req_state_added))
9105 Request(msg_saved, REQ_CONFIRM | req_state_added);
9113 boolean SaveTapeChecked(int nr)
9115 return SaveTapeCheckedExt(nr, "Replace old tape?", "Tape saved!", 0);
9118 boolean SaveTapeChecked_LevelSolved(int nr)
9120 return SaveTapeCheckedExt(nr, "Level solved! Replace old tape?",
9121 "Level solved! Tape saved!", REQ_STAY_OPEN);
9124 void DumpTape(struct TapeInfo *tape)
9126 int tape_frame_counter;
9129 if (tape->no_valid_file)
9131 Warn("cannot dump -- no valid tape file found");
9138 Print("Tape of Level %03d (file version %08d, game version %08d)\n",
9139 tape->level_nr, tape->file_version, tape->game_version);
9140 Print(" (effective engine version %08d)\n",
9141 tape->engine_version);
9142 Print("Level series identifier: '%s'\n", tape->level_identifier);
9144 Print("Solution tape: %s\n",
9145 tape->solved ? "yes" :
9146 tape->game_version < VERSION_IDENT(4,3,2,3) ? "unknown" : "no");
9148 Print("Special tape properties: ");
9149 if (tape->property_bits == TAPE_PROPERTY_NONE)
9151 if (tape->property_bits & TAPE_PROPERTY_EM_RANDOM_BUG)
9152 Print("[em_random_bug]");
9153 if (tape->property_bits & TAPE_PROPERTY_GAME_SPEED)
9154 Print("[game_speed]");
9155 if (tape->property_bits & TAPE_PROPERTY_PAUSE_MODE)
9157 if (tape->property_bits & TAPE_PROPERTY_SINGLE_STEP)
9158 Print("[single_step]");
9159 if (tape->property_bits & TAPE_PROPERTY_SNAPSHOT)
9160 Print("[snapshot]");
9161 if (tape->property_bits & TAPE_PROPERTY_REPLAYED)
9162 Print("[replayed]");
9163 if (tape->property_bits & TAPE_PROPERTY_TAS_KEYS)
9164 Print("[tas_keys]");
9165 if (tape->property_bits & TAPE_PROPERTY_SMALL_GRAPHICS)
9166 Print("[small_graphics]");
9169 int year2 = tape->date / 10000;
9170 int year4 = (year2 < 70 ? 2000 + year2 : 1900 + year2);
9171 int month_index_raw = (tape->date / 100) % 100;
9172 int month_index = month_index_raw % 12; // prevent invalid index
9173 int month = month_index + 1;
9174 int day = tape->date % 100;
9176 Print("Tape date: %04d-%02d-%02d\n", year4, month, day);
9180 tape_frame_counter = 0;
9182 for (i = 0; i < tape->length; i++)
9184 if (i >= MAX_TAPE_LEN)
9189 for (j = 0; j < MAX_PLAYERS; j++)
9191 if (tape->player_participates[j])
9193 int action = tape->pos[i].action[j];
9195 Print("%d:%02x ", j, action);
9196 Print("[%c%c%c%c|%c%c] - ",
9197 (action & JOY_LEFT ? '<' : ' '),
9198 (action & JOY_RIGHT ? '>' : ' '),
9199 (action & JOY_UP ? '^' : ' '),
9200 (action & JOY_DOWN ? 'v' : ' '),
9201 (action & JOY_BUTTON_1 ? '1' : ' '),
9202 (action & JOY_BUTTON_2 ? '2' : ' '));
9206 Print("(%03d) ", tape->pos[i].delay);
9207 Print("[%05d]\n", tape_frame_counter);
9209 tape_frame_counter += tape->pos[i].delay;
9215 void DumpTapes(void)
9217 static LevelDirTree *dumptape_leveldir = NULL;
9219 dumptape_leveldir = getTreeInfoFromIdentifier(leveldir_first,
9220 global.dumptape_leveldir);
9222 if (dumptape_leveldir == NULL)
9223 Fail("no such level identifier: '%s'", global.dumptape_leveldir);
9225 if (global.dumptape_level_nr < dumptape_leveldir->first_level ||
9226 global.dumptape_level_nr > dumptape_leveldir->last_level)
9227 Fail("no such level number: %d", global.dumptape_level_nr);
9229 leveldir_current = dumptape_leveldir;
9231 if (options.mytapes)
9232 LoadTape(global.dumptape_level_nr);
9234 LoadSolutionTape(global.dumptape_level_nr);
9242 // ============================================================================
9243 // score file functions
9244 // ============================================================================
9246 static void setScoreInfoToDefaultsExt(struct ScoreInfo *scores)
9250 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9252 strcpy(scores->entry[i].tape_basename, UNDEFINED_FILENAME);
9253 strcpy(scores->entry[i].name, EMPTY_PLAYER_NAME);
9254 scores->entry[i].score = 0;
9255 scores->entry[i].time = 0;
9257 scores->entry[i].id = -1;
9258 strcpy(scores->entry[i].tape_date, UNKNOWN_NAME);
9259 strcpy(scores->entry[i].platform, UNKNOWN_NAME);
9260 strcpy(scores->entry[i].version, UNKNOWN_NAME);
9261 strcpy(scores->entry[i].country_name, UNKNOWN_NAME);
9262 strcpy(scores->entry[i].country_code, "??");
9265 scores->num_entries = 0;
9266 scores->last_added = -1;
9267 scores->last_added_local = -1;
9269 scores->updated = FALSE;
9270 scores->uploaded = FALSE;
9271 scores->tape_downloaded = FALSE;
9272 scores->force_last_added = FALSE;
9274 // The following values are intentionally not reset here:
9278 // - continue_playing
9279 // - continue_on_return
9282 static void setScoreInfoToDefaults(void)
9284 setScoreInfoToDefaultsExt(&scores);
9287 static void setServerScoreInfoToDefaults(void)
9289 setScoreInfoToDefaultsExt(&server_scores);
9292 static void LoadScore_OLD(int nr)
9295 char *filename = getScoreFilename(nr);
9296 char cookie[MAX_LINE_LEN];
9297 char line[MAX_LINE_LEN];
9301 if (!(file = fopen(filename, MODE_READ)))
9304 // check file identifier
9305 if (fgets(cookie, MAX_LINE_LEN, file) == NULL)
9307 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
9308 cookie[strlen(cookie) - 1] = '\0';
9310 if (!checkCookieString(cookie, SCORE_COOKIE_TMPL))
9312 Warn("unknown format of score file '%s'", filename);
9319 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9321 if (fscanf(file, "%d", &scores.entry[i].score) == EOF)
9322 Warn("fscanf() failed; %s", strerror(errno));
9324 if (fgets(line, MAX_LINE_LEN, file) == NULL)
9327 if (strlen(line) > 0 && line[strlen(line) - 1] == '\n')
9328 line[strlen(line) - 1] = '\0';
9330 for (line_ptr = line; *line_ptr; line_ptr++)
9332 if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != '\0')
9334 strncpy(scores.entry[i].name, line_ptr, MAX_PLAYER_NAME_LEN);
9335 scores.entry[i].name[MAX_PLAYER_NAME_LEN] = '\0';
9344 static void ConvertScore_OLD(void)
9346 // only convert score to time for levels that rate playing time over score
9347 if (!level.rate_time_over_score)
9350 // convert old score to playing time for score-less levels (like Supaplex)
9351 int time_final_max = 999;
9354 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9356 int score = scores.entry[i].score;
9358 if (score > 0 && score < time_final_max)
9359 scores.entry[i].time = (time_final_max - score - 1) * FRAMES_PER_SECOND;
9363 static int LoadScore_VERS(File *file, int chunk_size, struct ScoreInfo *scores)
9365 scores->file_version = getFileVersion(file);
9366 scores->game_version = getFileVersion(file);
9371 static int LoadScore_INFO(File *file, int chunk_size, struct ScoreInfo *scores)
9373 char *level_identifier = NULL;
9374 int level_identifier_size;
9377 level_identifier_size = getFile16BitBE(file);
9379 level_identifier = checked_malloc(level_identifier_size);
9381 for (i = 0; i < level_identifier_size; i++)
9382 level_identifier[i] = getFile8Bit(file);
9384 strncpy(scores->level_identifier, level_identifier, MAX_FILENAME_LEN);
9385 scores->level_identifier[MAX_FILENAME_LEN] = '\0';
9387 checked_free(level_identifier);
9389 scores->level_nr = getFile16BitBE(file);
9390 scores->num_entries = getFile16BitBE(file);
9392 chunk_size = 2 + level_identifier_size + 2 + 2;
9397 static int LoadScore_NAME(File *file, int chunk_size, struct ScoreInfo *scores)
9401 for (i = 0; i < scores->num_entries; i++)
9403 for (j = 0; j < MAX_PLAYER_NAME_LEN; j++)
9404 scores->entry[i].name[j] = getFile8Bit(file);
9406 scores->entry[i].name[MAX_PLAYER_NAME_LEN] = '\0';
9409 chunk_size = scores->num_entries * MAX_PLAYER_NAME_LEN;
9414 static int LoadScore_SCOR(File *file, int chunk_size, struct ScoreInfo *scores)
9418 for (i = 0; i < scores->num_entries; i++)
9419 scores->entry[i].score = getFile16BitBE(file);
9421 chunk_size = scores->num_entries * 2;
9426 static int LoadScore_SC4R(File *file, int chunk_size, struct ScoreInfo *scores)
9430 for (i = 0; i < scores->num_entries; i++)
9431 scores->entry[i].score = getFile32BitBE(file);
9433 chunk_size = scores->num_entries * 4;
9438 static int LoadScore_TIME(File *file, int chunk_size, struct ScoreInfo *scores)
9442 for (i = 0; i < scores->num_entries; i++)
9443 scores->entry[i].time = getFile32BitBE(file);
9445 chunk_size = scores->num_entries * 4;
9450 static int LoadScore_TAPE(File *file, int chunk_size, struct ScoreInfo *scores)
9454 for (i = 0; i < scores->num_entries; i++)
9456 for (j = 0; j < MAX_SCORE_TAPE_BASENAME_LEN; j++)
9457 scores->entry[i].tape_basename[j] = getFile8Bit(file);
9459 scores->entry[i].tape_basename[MAX_SCORE_TAPE_BASENAME_LEN] = '\0';
9462 chunk_size = scores->num_entries * MAX_SCORE_TAPE_BASENAME_LEN;
9467 void LoadScore(int nr)
9469 char *filename = getScoreFilename(nr);
9470 char cookie[MAX_LINE_LEN];
9471 char chunk_name[CHUNK_ID_LEN + 1];
9473 boolean old_score_file_format = FALSE;
9476 // always start with reliable default values
9477 setScoreInfoToDefaults();
9479 if (!(file = openFile(filename, MODE_READ)))
9482 getFileChunkBE(file, chunk_name, NULL);
9483 if (strEqual(chunk_name, "RND1"))
9485 getFile32BitBE(file); // not used
9487 getFileChunkBE(file, chunk_name, NULL);
9488 if (!strEqual(chunk_name, "SCOR"))
9490 Warn("unknown format of score file '%s'", filename);
9497 else // check for old file format with cookie string
9499 strcpy(cookie, chunk_name);
9500 if (getStringFromFile(file, &cookie[4], MAX_LINE_LEN - 4) == NULL)
9502 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
9503 cookie[strlen(cookie) - 1] = '\0';
9505 if (!checkCookieString(cookie, SCORE_COOKIE_TMPL))
9507 Warn("unknown format of score file '%s'", filename);
9514 old_score_file_format = TRUE;
9517 if (old_score_file_format)
9519 // score files from versions before 4.2.4.0 without chunk structure
9522 // convert score to time, if possible (mainly for Supaplex levels)
9531 int (*loader)(File *, int, struct ScoreInfo *);
9535 { "VERS", SCORE_CHUNK_VERS_SIZE, LoadScore_VERS },
9536 { "INFO", -1, LoadScore_INFO },
9537 { "NAME", -1, LoadScore_NAME },
9538 { "SCOR", -1, LoadScore_SCOR },
9539 { "SC4R", -1, LoadScore_SC4R },
9540 { "TIME", -1, LoadScore_TIME },
9541 { "TAPE", -1, LoadScore_TAPE },
9546 while (getFileChunkBE(file, chunk_name, &chunk_size))
9550 while (chunk_info[i].name != NULL &&
9551 !strEqual(chunk_name, chunk_info[i].name))
9554 if (chunk_info[i].name == NULL)
9556 Warn("unknown chunk '%s' in score file '%s'",
9557 chunk_name, filename);
9559 ReadUnusedBytesFromFile(file, chunk_size);
9561 else if (chunk_info[i].size != -1 &&
9562 chunk_info[i].size != chunk_size)
9564 Warn("wrong size (%d) of chunk '%s' in score file '%s'",
9565 chunk_size, chunk_name, filename);
9567 ReadUnusedBytesFromFile(file, chunk_size);
9571 // call function to load this score chunk
9572 int chunk_size_expected =
9573 (chunk_info[i].loader)(file, chunk_size, &scores);
9575 // the size of some chunks cannot be checked before reading other
9576 // chunks first (like "HEAD" and "BODY") that contain some header
9577 // information, so check them here
9578 if (chunk_size_expected != chunk_size)
9580 Warn("wrong size (%d) of chunk '%s' in score file '%s'",
9581 chunk_size, chunk_name, filename);
9590 #if ENABLE_HISTORIC_CHUNKS
9591 void SaveScore_OLD(int nr)
9594 char *filename = getScoreFilename(nr);
9597 // used instead of "leveldir_current->subdir" (for network games)
9598 InitScoreDirectory(levelset.identifier);
9600 if (!(file = fopen(filename, MODE_WRITE)))
9602 Warn("cannot save score for level %d", nr);
9607 fprintf(file, "%s\n\n", SCORE_COOKIE);
9609 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9610 fprintf(file, "%d %s\n", scores.entry[i].score, scores.entry[i].name);
9614 SetFilePermissions(filename, PERMS_PRIVATE);
9618 static void SaveScore_VERS(FILE *file, struct ScoreInfo *scores)
9620 putFileVersion(file, scores->file_version);
9621 putFileVersion(file, scores->game_version);
9624 static void SaveScore_INFO(FILE *file, struct ScoreInfo *scores)
9626 int level_identifier_size = strlen(scores->level_identifier) + 1;
9629 putFile16BitBE(file, level_identifier_size);
9631 for (i = 0; i < level_identifier_size; i++)
9632 putFile8Bit(file, scores->level_identifier[i]);
9634 putFile16BitBE(file, scores->level_nr);
9635 putFile16BitBE(file, scores->num_entries);
9638 static void SaveScore_NAME(FILE *file, struct ScoreInfo *scores)
9642 for (i = 0; i < scores->num_entries; i++)
9644 int name_size = strlen(scores->entry[i].name);
9646 for (j = 0; j < MAX_PLAYER_NAME_LEN; j++)
9647 putFile8Bit(file, (j < name_size ? scores->entry[i].name[j] : 0));
9651 static void SaveScore_SCOR(FILE *file, struct ScoreInfo *scores)
9655 for (i = 0; i < scores->num_entries; i++)
9656 putFile16BitBE(file, scores->entry[i].score);
9659 static void SaveScore_SC4R(FILE *file, struct ScoreInfo *scores)
9663 for (i = 0; i < scores->num_entries; i++)
9664 putFile32BitBE(file, scores->entry[i].score);
9667 static void SaveScore_TIME(FILE *file, struct ScoreInfo *scores)
9671 for (i = 0; i < scores->num_entries; i++)
9672 putFile32BitBE(file, scores->entry[i].time);
9675 static void SaveScore_TAPE(FILE *file, struct ScoreInfo *scores)
9679 for (i = 0; i < scores->num_entries; i++)
9681 int size = strlen(scores->entry[i].tape_basename);
9683 for (j = 0; j < MAX_SCORE_TAPE_BASENAME_LEN; j++)
9684 putFile8Bit(file, (j < size ? scores->entry[i].tape_basename[j] : 0));
9688 static void SaveScoreToFilename(char *filename)
9691 int info_chunk_size;
9692 int name_chunk_size;
9693 int scor_chunk_size;
9694 int sc4r_chunk_size;
9695 int time_chunk_size;
9696 int tape_chunk_size;
9697 boolean has_large_score_values;
9700 if (!(file = fopen(filename, MODE_WRITE)))
9702 Warn("cannot save score file '%s'", filename);
9707 info_chunk_size = 2 + (strlen(scores.level_identifier) + 1) + 2 + 2;
9708 name_chunk_size = scores.num_entries * MAX_PLAYER_NAME_LEN;
9709 scor_chunk_size = scores.num_entries * 2;
9710 sc4r_chunk_size = scores.num_entries * 4;
9711 time_chunk_size = scores.num_entries * 4;
9712 tape_chunk_size = scores.num_entries * MAX_SCORE_TAPE_BASENAME_LEN;
9714 has_large_score_values = FALSE;
9715 for (i = 0; i < scores.num_entries; i++)
9716 if (scores.entry[i].score > 0xffff)
9717 has_large_score_values = TRUE;
9719 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
9720 putFileChunkBE(file, "SCOR", CHUNK_SIZE_NONE);
9722 putFileChunkBE(file, "VERS", SCORE_CHUNK_VERS_SIZE);
9723 SaveScore_VERS(file, &scores);
9725 putFileChunkBE(file, "INFO", info_chunk_size);
9726 SaveScore_INFO(file, &scores);
9728 putFileChunkBE(file, "NAME", name_chunk_size);
9729 SaveScore_NAME(file, &scores);
9731 if (has_large_score_values)
9733 putFileChunkBE(file, "SC4R", sc4r_chunk_size);
9734 SaveScore_SC4R(file, &scores);
9738 putFileChunkBE(file, "SCOR", scor_chunk_size);
9739 SaveScore_SCOR(file, &scores);
9742 putFileChunkBE(file, "TIME", time_chunk_size);
9743 SaveScore_TIME(file, &scores);
9745 putFileChunkBE(file, "TAPE", tape_chunk_size);
9746 SaveScore_TAPE(file, &scores);
9750 SetFilePermissions(filename, PERMS_PRIVATE);
9753 void SaveScore(int nr)
9755 char *filename = getScoreFilename(nr);
9758 // used instead of "leveldir_current->subdir" (for network games)
9759 InitScoreDirectory(levelset.identifier);
9761 scores.file_version = FILE_VERSION_ACTUAL;
9762 scores.game_version = GAME_VERSION_ACTUAL;
9764 strncpy(scores.level_identifier, levelset.identifier, MAX_FILENAME_LEN);
9765 scores.level_identifier[MAX_FILENAME_LEN] = '\0';
9766 scores.level_nr = level_nr;
9768 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9769 if (scores.entry[i].score == 0 &&
9770 scores.entry[i].time == 0 &&
9771 strEqual(scores.entry[i].name, EMPTY_PLAYER_NAME))
9774 scores.num_entries = i;
9776 if (scores.num_entries == 0)
9779 SaveScoreToFilename(filename);
9782 static void LoadServerScoreFromCache(int nr)
9784 struct ScoreEntry score_entry;
9793 { &score_entry.score, FALSE, 0 },
9794 { &score_entry.time, FALSE, 0 },
9795 { score_entry.name, TRUE, MAX_PLAYER_NAME_LEN },
9796 { score_entry.tape_basename, TRUE, MAX_FILENAME_LEN },
9797 { score_entry.tape_date, TRUE, MAX_ISO_DATE_LEN },
9798 { &score_entry.id, FALSE, 0 },
9799 { score_entry.platform, TRUE, MAX_PLATFORM_TEXT_LEN },
9800 { score_entry.version, TRUE, MAX_VERSION_TEXT_LEN },
9801 { score_entry.country_code, TRUE, MAX_COUNTRY_CODE_LEN },
9802 { score_entry.country_name, TRUE, MAX_COUNTRY_NAME_LEN },
9806 char *filename = getScoreCacheFilename(nr);
9807 SetupFileHash *score_hash = loadSetupFileHash(filename);
9810 server_scores.num_entries = 0;
9812 if (score_hash == NULL)
9815 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9817 score_entry = server_scores.entry[i];
9819 for (j = 0; score_mapping[j].value != NULL; j++)
9823 sprintf(token, "%02d.%d", i, j);
9825 char *value = getHashEntry(score_hash, token);
9830 if (score_mapping[j].is_string)
9832 char *score_value = (char *)score_mapping[j].value;
9833 int value_size = score_mapping[j].string_size;
9835 strncpy(score_value, value, value_size);
9836 score_value[value_size] = '\0';
9840 int *score_value = (int *)score_mapping[j].value;
9842 *score_value = atoi(value);
9845 server_scores.num_entries = i + 1;
9848 server_scores.entry[i] = score_entry;
9851 freeSetupFileHash(score_hash);
9854 void LoadServerScore(int nr, boolean download_score)
9856 if (!setup.use_api_server)
9859 // always start with reliable default values
9860 setServerScoreInfoToDefaults();
9862 // 1st step: load server scores from cache file (which may not exist)
9863 // (this should prevent reading it while the thread is writing to it)
9864 LoadServerScoreFromCache(nr);
9866 if (download_score && runtime.use_api_server)
9868 // 2nd step: download server scores from score server to cache file
9869 // (as thread, as it might time out if the server is not reachable)
9870 ApiGetScoreAsThread(nr);
9874 void PrepareScoreTapesForUpload(char *leveldir_subdir)
9876 MarkTapeDirectoryUploadsAsIncomplete(leveldir_subdir);
9878 // if score tape not uploaded, ask for uploading missing tapes later
9879 if (!setup.has_remaining_tapes)
9880 setup.ask_for_remaining_tapes = TRUE;
9882 setup.provide_uploading_tapes = TRUE;
9883 setup.has_remaining_tapes = TRUE;
9885 SaveSetup_ServerSetup();
9888 void SaveServerScore(int nr, boolean tape_saved)
9890 if (!runtime.use_api_server)
9892 PrepareScoreTapesForUpload(leveldir_current->subdir);
9897 ApiAddScoreAsThread(nr, tape_saved, NULL);
9900 void SaveServerScoreFromFile(int nr, boolean tape_saved,
9901 char *score_tape_filename)
9903 if (!runtime.use_api_server)
9906 ApiAddScoreAsThread(nr, tape_saved, score_tape_filename);
9909 void LoadLocalAndServerScore(int nr, boolean download_score)
9911 int last_added_local = scores.last_added_local;
9912 boolean force_last_added = scores.force_last_added;
9914 // needed if only showing server scores
9915 setScoreInfoToDefaults();
9917 if (!strEqual(setup.scores_in_highscore_list, STR_SCORES_TYPE_SERVER_ONLY))
9920 // restore last added local score entry (before merging server scores)
9921 scores.last_added = scores.last_added_local = last_added_local;
9923 if (setup.use_api_server &&
9924 !strEqual(setup.scores_in_highscore_list, STR_SCORES_TYPE_LOCAL_ONLY))
9926 // load server scores from cache file and trigger update from server
9927 LoadServerScore(nr, download_score);
9929 // merge local scores with scores from server
9933 if (force_last_added)
9934 scores.force_last_added = force_last_added;
9938 // ============================================================================
9939 // setup file functions
9940 // ============================================================================
9942 #define TOKEN_STR_PLAYER_PREFIX "player_"
9945 static struct TokenInfo global_setup_tokens[] =
9949 &setup.player_name, "player_name"
9953 &setup.multiple_users, "multiple_users"
9957 &setup.sound, "sound"
9961 &setup.sound_loops, "repeating_sound_loops"
9965 &setup.sound_music, "background_music"
9969 &setup.sound_simple, "simple_sound_effects"
9973 &setup.toons, "toons"
9977 &setup.global_animations, "global_animations"
9981 &setup.scroll_delay, "scroll_delay"
9985 &setup.forced_scroll_delay, "forced_scroll_delay"
9989 &setup.scroll_delay_value, "scroll_delay_value"
9993 &setup.engine_snapshot_mode, "engine_snapshot_mode"
9997 &setup.engine_snapshot_memory, "engine_snapshot_memory"
10001 &setup.fade_screens, "fade_screens"
10005 &setup.autorecord, "automatic_tape_recording"
10009 &setup.autorecord_after_replay, "autorecord_after_replay"
10013 &setup.auto_pause_on_start, "auto_pause_on_start"
10017 &setup.show_titlescreen, "show_titlescreen"
10021 &setup.quick_doors, "quick_doors"
10025 &setup.team_mode, "team_mode"
10029 &setup.handicap, "handicap"
10033 &setup.skip_levels, "skip_levels"
10037 &setup.increment_levels, "increment_levels"
10041 &setup.auto_play_next_level, "auto_play_next_level"
10045 &setup.count_score_after_game, "count_score_after_game"
10049 &setup.show_scores_after_game, "show_scores_after_game"
10053 &setup.time_limit, "time_limit"
10057 &setup.fullscreen, "fullscreen"
10061 &setup.window_scaling_percent, "window_scaling_percent"
10065 &setup.window_scaling_quality, "window_scaling_quality"
10069 &setup.screen_rendering_mode, "screen_rendering_mode"
10073 &setup.vsync_mode, "vsync_mode"
10077 &setup.ask_on_escape, "ask_on_escape"
10081 &setup.ask_on_escape_editor, "ask_on_escape_editor"
10085 &setup.ask_on_game_over, "ask_on_game_over"
10089 &setup.ask_on_quit_game, "ask_on_quit_game"
10093 &setup.ask_on_quit_program, "ask_on_quit_program"
10097 &setup.quick_switch, "quick_player_switch"
10101 &setup.input_on_focus, "input_on_focus"
10105 &setup.prefer_aga_graphics, "prefer_aga_graphics"
10109 &setup.prefer_lowpass_sounds, "prefer_lowpass_sounds"
10113 &setup.prefer_extra_panel_items, "prefer_extra_panel_items"
10117 &setup.game_speed_extended, "game_speed_extended"
10121 &setup.game_frame_delay, "game_frame_delay"
10125 &setup.bd_skip_uncovering, "bd_skip_uncovering"
10129 &setup.bd_skip_hatching, "bd_skip_hatching"
10133 &setup.bd_scroll_delay, "bd_scroll_delay"
10137 &setup.bd_smooth_movements, "bd_smooth_movements"
10141 &setup.sp_show_border_elements, "sp_show_border_elements"
10145 &setup.small_game_graphics, "small_game_graphics"
10149 &setup.show_load_save_buttons, "show_load_save_buttons"
10153 &setup.show_undo_redo_buttons, "show_undo_redo_buttons"
10157 &setup.scores_in_highscore_list, "scores_in_highscore_list"
10161 &setup.graphics_set, "graphics_set"
10165 &setup.sounds_set, "sounds_set"
10169 &setup.music_set, "music_set"
10173 &setup.override_level_graphics, "override_level_graphics"
10177 &setup.override_level_sounds, "override_level_sounds"
10181 &setup.override_level_music, "override_level_music"
10185 &setup.volume_simple, "volume_simple"
10189 &setup.volume_loops, "volume_loops"
10193 &setup.volume_music, "volume_music"
10197 &setup.network_mode, "network_mode"
10201 &setup.network_player_nr, "network_player"
10205 &setup.network_server_hostname, "network_server_hostname"
10209 &setup.touch.control_type, "touch.control_type"
10213 &setup.touch.move_distance, "touch.move_distance"
10217 &setup.touch.drop_distance, "touch.drop_distance"
10221 &setup.touch.transparency, "touch.transparency"
10225 &setup.touch.draw_outlined, "touch.draw_outlined"
10229 &setup.touch.draw_pressed, "touch.draw_pressed"
10233 &setup.touch.grid_xsize[0], "touch.virtual_buttons.0.xsize"
10237 &setup.touch.grid_ysize[0], "touch.virtual_buttons.0.ysize"
10241 &setup.touch.grid_xsize[1], "touch.virtual_buttons.1.xsize"
10245 &setup.touch.grid_ysize[1], "touch.virtual_buttons.1.ysize"
10249 &setup.touch.overlay_buttons, "touch.overlay_buttons"
10253 static struct TokenInfo auto_setup_tokens[] =
10257 &setup.auto_setup.editor_zoom_tilesize, "editor.zoom_tilesize"
10261 static struct TokenInfo server_setup_tokens[] =
10265 &setup.player_uuid, "player_uuid"
10269 &setup.player_version, "player_version"
10273 &setup.use_api_server, TEST_PREFIX "use_api_server"
10277 &setup.api_server_hostname, TEST_PREFIX "api_server_hostname"
10281 &setup.api_server_password, TEST_PREFIX "api_server_password"
10285 &setup.ask_for_uploading_tapes, TEST_PREFIX "ask_for_uploading_tapes"
10289 &setup.ask_for_remaining_tapes, TEST_PREFIX "ask_for_remaining_tapes"
10293 &setup.provide_uploading_tapes, TEST_PREFIX "provide_uploading_tapes"
10297 &setup.ask_for_using_api_server,TEST_PREFIX "ask_for_using_api_server"
10301 &setup.has_remaining_tapes, TEST_PREFIX "has_remaining_tapes"
10305 static struct TokenInfo editor_setup_tokens[] =
10309 &setup.editor.el_classic, "editor.el_classic"
10313 &setup.editor.el_custom, "editor.el_custom"
10317 &setup.editor.el_user_defined, "editor.el_user_defined"
10321 &setup.editor.el_dynamic, "editor.el_dynamic"
10325 &setup.editor.el_headlines, "editor.el_headlines"
10329 &setup.editor.show_element_token, "editor.show_element_token"
10333 &setup.editor.show_read_only_warning, "editor.show_read_only_warning"
10337 static struct TokenInfo editor_cascade_setup_tokens[] =
10341 &setup.editor_cascade.el_bd, "editor.cascade.el_bd"
10345 &setup.editor_cascade.el_bd_native, "editor.cascade.el_bd_native"
10349 &setup.editor_cascade.el_em, "editor.cascade.el_em"
10353 &setup.editor_cascade.el_emc, "editor.cascade.el_emc"
10357 &setup.editor_cascade.el_rnd, "editor.cascade.el_rnd"
10361 &setup.editor_cascade.el_sb, "editor.cascade.el_sb"
10365 &setup.editor_cascade.el_sp, "editor.cascade.el_sp"
10369 &setup.editor_cascade.el_dc, "editor.cascade.el_dc"
10373 &setup.editor_cascade.el_dx, "editor.cascade.el_dx"
10377 &setup.editor_cascade.el_mm, "editor.cascade.el_mm"
10381 &setup.editor_cascade.el_df, "editor.cascade.el_df"
10385 &setup.editor_cascade.el_chars, "editor.cascade.el_chars"
10389 &setup.editor_cascade.el_steel_chars, "editor.cascade.el_steel_chars"
10393 &setup.editor_cascade.el_ce, "editor.cascade.el_ce"
10397 &setup.editor_cascade.el_ge, "editor.cascade.el_ge"
10401 &setup.editor_cascade.el_es, "editor.cascade.el_es"
10405 &setup.editor_cascade.el_ref, "editor.cascade.el_ref"
10409 &setup.editor_cascade.el_user, "editor.cascade.el_user"
10413 &setup.editor_cascade.el_dynamic, "editor.cascade.el_dynamic"
10417 static struct TokenInfo shortcut_setup_tokens[] =
10421 &setup.shortcut.save_game, "shortcut.save_game"
10425 &setup.shortcut.load_game, "shortcut.load_game"
10429 &setup.shortcut.restart_game, "shortcut.restart_game"
10433 &setup.shortcut.pause_before_end, "shortcut.pause_before_end"
10437 &setup.shortcut.toggle_pause, "shortcut.toggle_pause"
10441 &setup.shortcut.focus_player[0], "shortcut.focus_player_1"
10445 &setup.shortcut.focus_player[1], "shortcut.focus_player_2"
10449 &setup.shortcut.focus_player[2], "shortcut.focus_player_3"
10453 &setup.shortcut.focus_player[3], "shortcut.focus_player_4"
10457 &setup.shortcut.focus_player_all, "shortcut.focus_player_all"
10461 &setup.shortcut.tape_eject, "shortcut.tape_eject"
10465 &setup.shortcut.tape_extra, "shortcut.tape_extra"
10469 &setup.shortcut.tape_stop, "shortcut.tape_stop"
10473 &setup.shortcut.tape_pause, "shortcut.tape_pause"
10477 &setup.shortcut.tape_record, "shortcut.tape_record"
10481 &setup.shortcut.tape_play, "shortcut.tape_play"
10485 &setup.shortcut.sound_simple, "shortcut.sound_simple"
10489 &setup.shortcut.sound_loops, "shortcut.sound_loops"
10493 &setup.shortcut.sound_music, "shortcut.sound_music"
10497 &setup.shortcut.snap_left, "shortcut.snap_left"
10501 &setup.shortcut.snap_right, "shortcut.snap_right"
10505 &setup.shortcut.snap_up, "shortcut.snap_up"
10509 &setup.shortcut.snap_down, "shortcut.snap_down"
10513 static struct SetupInputInfo setup_input;
10514 static struct TokenInfo player_setup_tokens[] =
10518 &setup_input.use_joystick, ".use_joystick"
10522 &setup_input.joy.device_name, ".joy.device_name"
10526 &setup_input.joy.xleft, ".joy.xleft"
10530 &setup_input.joy.xmiddle, ".joy.xmiddle"
10534 &setup_input.joy.xright, ".joy.xright"
10538 &setup_input.joy.yupper, ".joy.yupper"
10542 &setup_input.joy.ymiddle, ".joy.ymiddle"
10546 &setup_input.joy.ylower, ".joy.ylower"
10550 &setup_input.joy.snap, ".joy.snap_field"
10554 &setup_input.joy.drop, ".joy.place_bomb"
10558 &setup_input.key.left, ".key.move_left"
10562 &setup_input.key.right, ".key.move_right"
10566 &setup_input.key.up, ".key.move_up"
10570 &setup_input.key.down, ".key.move_down"
10574 &setup_input.key.snap, ".key.snap_field"
10578 &setup_input.key.drop, ".key.place_bomb"
10582 static struct TokenInfo system_setup_tokens[] =
10586 &setup.system.sdl_renderdriver, "system.sdl_renderdriver"
10590 &setup.system.sdl_videodriver, "system.sdl_videodriver"
10594 &setup.system.sdl_audiodriver, "system.sdl_audiodriver"
10598 &setup.system.audio_fragment_size, "system.audio_fragment_size"
10602 static struct TokenInfo internal_setup_tokens[] =
10606 &setup.internal.program_title, "program_title"
10610 &setup.internal.program_version, "program_version"
10614 &setup.internal.program_author, "program_author"
10618 &setup.internal.program_email, "program_email"
10622 &setup.internal.program_website, "program_website"
10626 &setup.internal.program_copyright, "program_copyright"
10630 &setup.internal.program_company, "program_company"
10634 &setup.internal.program_icon_file, "program_icon_file"
10638 &setup.internal.default_graphics_set, "default_graphics_set"
10642 &setup.internal.default_sounds_set, "default_sounds_set"
10646 &setup.internal.default_music_set, "default_music_set"
10650 &setup.internal.fallback_graphics_file, "fallback_graphics_file"
10654 &setup.internal.fallback_sounds_file, "fallback_sounds_file"
10658 &setup.internal.fallback_music_file, "fallback_music_file"
10662 &setup.internal.default_level_series, "default_level_series"
10666 &setup.internal.default_window_width, "default_window_width"
10670 &setup.internal.default_window_height, "default_window_height"
10674 &setup.internal.choose_from_top_leveldir, "choose_from_top_leveldir"
10678 &setup.internal.show_scaling_in_title, "show_scaling_in_title"
10682 &setup.internal.create_user_levelset, "create_user_levelset"
10686 &setup.internal.info_screens_from_main, "info_screens_from_main"
10690 &setup.internal.menu_game, "menu_game"
10694 &setup.internal.menu_engines, "menu_engines"
10698 &setup.internal.menu_editor, "menu_editor"
10702 &setup.internal.menu_graphics, "menu_graphics"
10706 &setup.internal.menu_sound, "menu_sound"
10710 &setup.internal.menu_artwork, "menu_artwork"
10714 &setup.internal.menu_input, "menu_input"
10718 &setup.internal.menu_touch, "menu_touch"
10722 &setup.internal.menu_shortcuts, "menu_shortcuts"
10726 &setup.internal.menu_exit, "menu_exit"
10730 &setup.internal.menu_save_and_exit, "menu_save_and_exit"
10734 &setup.internal.menu_shortcuts_various, "menu_shortcuts_various"
10738 &setup.internal.menu_shortcuts_focus, "menu_shortcuts_focus"
10742 &setup.internal.menu_shortcuts_tape, "menu_shortcuts_tape"
10746 &setup.internal.menu_shortcuts_sound, "menu_shortcuts_sound"
10750 &setup.internal.menu_shortcuts_snap, "menu_shortcuts_snap"
10754 &setup.internal.info_title, "info_title"
10758 &setup.internal.info_elements, "info_elements"
10762 &setup.internal.info_music, "info_music"
10766 &setup.internal.info_credits, "info_credits"
10770 &setup.internal.info_program, "info_program"
10774 &setup.internal.info_version, "info_version"
10778 &setup.internal.info_levelset, "info_levelset"
10782 &setup.internal.info_exit, "info_exit"
10786 static struct TokenInfo debug_setup_tokens[] =
10790 &setup.debug.frame_delay[0], "debug.frame_delay_0"
10794 &setup.debug.frame_delay[1], "debug.frame_delay_1"
10798 &setup.debug.frame_delay[2], "debug.frame_delay_2"
10802 &setup.debug.frame_delay[3], "debug.frame_delay_3"
10806 &setup.debug.frame_delay[4], "debug.frame_delay_4"
10810 &setup.debug.frame_delay[5], "debug.frame_delay_5"
10814 &setup.debug.frame_delay[6], "debug.frame_delay_6"
10818 &setup.debug.frame_delay[7], "debug.frame_delay_7"
10822 &setup.debug.frame_delay[8], "debug.frame_delay_8"
10826 &setup.debug.frame_delay[9], "debug.frame_delay_9"
10830 &setup.debug.frame_delay_key[0], "debug.key.frame_delay_0"
10834 &setup.debug.frame_delay_key[1], "debug.key.frame_delay_1"
10838 &setup.debug.frame_delay_key[2], "debug.key.frame_delay_2"
10842 &setup.debug.frame_delay_key[3], "debug.key.frame_delay_3"
10846 &setup.debug.frame_delay_key[4], "debug.key.frame_delay_4"
10850 &setup.debug.frame_delay_key[5], "debug.key.frame_delay_5"
10854 &setup.debug.frame_delay_key[6], "debug.key.frame_delay_6"
10858 &setup.debug.frame_delay_key[7], "debug.key.frame_delay_7"
10862 &setup.debug.frame_delay_key[8], "debug.key.frame_delay_8"
10866 &setup.debug.frame_delay_key[9], "debug.key.frame_delay_9"
10870 &setup.debug.frame_delay_use_mod_key, "debug.frame_delay.use_mod_key"},
10873 &setup.debug.frame_delay_game_only, "debug.frame_delay.game_only"
10877 &setup.debug.show_frames_per_second, "debug.show_frames_per_second"
10881 &setup.debug.xsn_mode, "debug.xsn_mode"
10885 &setup.debug.xsn_percent, "debug.xsn_percent"
10889 static struct TokenInfo options_setup_tokens[] =
10893 &setup.options.verbose, "options.verbose"
10897 &setup.options.debug, "options.debug"
10901 &setup.options.debug_mode, "options.debug_mode"
10905 static void setSetupInfoToDefaults(struct SetupInfo *si)
10909 si->player_name = getStringCopy(getDefaultUserName(user.nr));
10911 si->multiple_users = TRUE;
10914 si->sound_loops = TRUE;
10915 si->sound_music = TRUE;
10916 si->sound_simple = TRUE;
10918 si->global_animations = TRUE;
10919 si->scroll_delay = TRUE;
10920 si->forced_scroll_delay = FALSE;
10921 si->scroll_delay_value = STD_SCROLL_DELAY;
10922 si->engine_snapshot_mode = getStringCopy(STR_SNAPSHOT_MODE_DEFAULT);
10923 si->engine_snapshot_memory = SNAPSHOT_MEMORY_DEFAULT;
10924 si->fade_screens = TRUE;
10925 si->autorecord = TRUE;
10926 si->autorecord_after_replay = TRUE;
10927 si->auto_pause_on_start = FALSE;
10928 si->show_titlescreen = TRUE;
10929 si->quick_doors = FALSE;
10930 si->team_mode = FALSE;
10931 si->handicap = TRUE;
10932 si->skip_levels = TRUE;
10933 si->increment_levels = TRUE;
10934 si->auto_play_next_level = TRUE;
10935 si->count_score_after_game = TRUE;
10936 si->show_scores_after_game = TRUE;
10937 si->time_limit = TRUE;
10938 si->fullscreen = FALSE;
10939 si->window_scaling_percent = STD_WINDOW_SCALING_PERCENT;
10940 si->window_scaling_quality = getStringCopy(SCALING_QUALITY_DEFAULT);
10941 si->screen_rendering_mode = getStringCopy(STR_SPECIAL_RENDERING_DEFAULT);
10942 si->vsync_mode = getStringCopy(STR_VSYNC_MODE_DEFAULT);
10943 si->ask_on_escape = TRUE;
10944 si->ask_on_escape_editor = TRUE;
10945 si->ask_on_game_over = TRUE;
10946 si->ask_on_quit_game = TRUE;
10947 si->ask_on_quit_program = TRUE;
10948 si->quick_switch = FALSE;
10949 si->input_on_focus = FALSE;
10950 si->prefer_aga_graphics = TRUE;
10951 si->prefer_lowpass_sounds = FALSE;
10952 si->prefer_extra_panel_items = TRUE;
10953 si->game_speed_extended = FALSE;
10954 si->game_frame_delay = GAME_FRAME_DELAY;
10955 si->bd_skip_uncovering = FALSE;
10956 si->bd_skip_hatching = FALSE;
10957 si->bd_scroll_delay = TRUE;
10958 si->bd_smooth_movements = AUTO;
10959 si->sp_show_border_elements = FALSE;
10960 si->small_game_graphics = FALSE;
10961 si->show_load_save_buttons = FALSE;
10962 si->show_undo_redo_buttons = FALSE;
10963 si->scores_in_highscore_list = getStringCopy(STR_SCORES_TYPE_DEFAULT);
10965 si->graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
10966 si->sounds_set = getStringCopy(SND_CLASSIC_SUBDIR);
10967 si->music_set = getStringCopy(MUS_CLASSIC_SUBDIR);
10969 si->override_level_graphics = FALSE;
10970 si->override_level_sounds = FALSE;
10971 si->override_level_music = FALSE;
10973 si->volume_simple = 100; // percent
10974 si->volume_loops = 100; // percent
10975 si->volume_music = 100; // percent
10977 si->network_mode = FALSE;
10978 si->network_player_nr = 0; // first player
10979 si->network_server_hostname = getStringCopy(STR_NETWORK_AUTO_DETECT);
10981 si->touch.control_type = getStringCopy(TOUCH_CONTROL_DEFAULT);
10982 si->touch.move_distance = TOUCH_MOVE_DISTANCE_DEFAULT; // percent
10983 si->touch.drop_distance = TOUCH_DROP_DISTANCE_DEFAULT; // percent
10984 si->touch.transparency = TOUCH_TRANSPARENCY_DEFAULT; // percent
10985 si->touch.draw_outlined = TRUE;
10986 si->touch.draw_pressed = TRUE;
10988 for (i = 0; i < 2; i++)
10990 char *default_grid_button[6][2] =
10996 { "111222", " vv " },
10997 { "111222", " vv " }
10999 int grid_xsize = DEFAULT_GRID_XSIZE(i);
11000 int grid_ysize = DEFAULT_GRID_YSIZE(i);
11001 int min_xsize = MIN(6, grid_xsize);
11002 int min_ysize = MIN(6, grid_ysize);
11003 int startx = grid_xsize - min_xsize;
11004 int starty = grid_ysize - min_ysize;
11007 // virtual buttons grid can only be set to defaults if video is initialized
11008 // (this will be repeated if virtual buttons are not loaded from setup file)
11009 if (video.initialized)
11011 si->touch.grid_xsize[i] = grid_xsize;
11012 si->touch.grid_ysize[i] = grid_ysize;
11016 si->touch.grid_xsize[i] = -1;
11017 si->touch.grid_ysize[i] = -1;
11020 for (x = 0; x < MAX_GRID_XSIZE; x++)
11021 for (y = 0; y < MAX_GRID_YSIZE; y++)
11022 si->touch.grid_button[i][x][y] = CHAR_GRID_BUTTON_NONE;
11024 for (x = 0; x < min_xsize; x++)
11025 for (y = 0; y < min_ysize; y++)
11026 si->touch.grid_button[i][x][starty + y] =
11027 default_grid_button[y][0][x];
11029 for (x = 0; x < min_xsize; x++)
11030 for (y = 0; y < min_ysize; y++)
11031 si->touch.grid_button[i][startx + x][starty + y] =
11032 default_grid_button[y][1][x];
11035 si->touch.grid_initialized = video.initialized;
11037 si->touch.overlay_buttons = FALSE;
11039 si->editor.el_boulderdash = TRUE;
11040 si->editor.el_boulderdash_native = TRUE;
11041 si->editor.el_emerald_mine = TRUE;
11042 si->editor.el_emerald_mine_club = TRUE;
11043 si->editor.el_more = TRUE;
11044 si->editor.el_sokoban = TRUE;
11045 si->editor.el_supaplex = TRUE;
11046 si->editor.el_diamond_caves = TRUE;
11047 si->editor.el_dx_boulderdash = TRUE;
11049 si->editor.el_mirror_magic = TRUE;
11050 si->editor.el_deflektor = TRUE;
11052 si->editor.el_chars = TRUE;
11053 si->editor.el_steel_chars = TRUE;
11055 si->editor.el_classic = TRUE;
11056 si->editor.el_custom = TRUE;
11058 si->editor.el_user_defined = FALSE;
11059 si->editor.el_dynamic = TRUE;
11061 si->editor.el_headlines = TRUE;
11063 si->editor.show_element_token = FALSE;
11065 si->editor.show_read_only_warning = TRUE;
11067 si->editor.use_template_for_new_levels = TRUE;
11069 si->shortcut.save_game = DEFAULT_KEY_SAVE_GAME;
11070 si->shortcut.load_game = DEFAULT_KEY_LOAD_GAME;
11071 si->shortcut.restart_game = DEFAULT_KEY_RESTART_GAME;
11072 si->shortcut.pause_before_end = DEFAULT_KEY_PAUSE_BEFORE_END;
11073 si->shortcut.toggle_pause = DEFAULT_KEY_TOGGLE_PAUSE;
11075 si->shortcut.focus_player[0] = DEFAULT_KEY_FOCUS_PLAYER_1;
11076 si->shortcut.focus_player[1] = DEFAULT_KEY_FOCUS_PLAYER_2;
11077 si->shortcut.focus_player[2] = DEFAULT_KEY_FOCUS_PLAYER_3;
11078 si->shortcut.focus_player[3] = DEFAULT_KEY_FOCUS_PLAYER_4;
11079 si->shortcut.focus_player_all = DEFAULT_KEY_FOCUS_PLAYER_ALL;
11081 si->shortcut.tape_eject = DEFAULT_KEY_TAPE_EJECT;
11082 si->shortcut.tape_extra = DEFAULT_KEY_TAPE_EXTRA;
11083 si->shortcut.tape_stop = DEFAULT_KEY_TAPE_STOP;
11084 si->shortcut.tape_pause = DEFAULT_KEY_TAPE_PAUSE;
11085 si->shortcut.tape_record = DEFAULT_KEY_TAPE_RECORD;
11086 si->shortcut.tape_play = DEFAULT_KEY_TAPE_PLAY;
11088 si->shortcut.sound_simple = DEFAULT_KEY_SOUND_SIMPLE;
11089 si->shortcut.sound_loops = DEFAULT_KEY_SOUND_LOOPS;
11090 si->shortcut.sound_music = DEFAULT_KEY_SOUND_MUSIC;
11092 si->shortcut.snap_left = DEFAULT_KEY_SNAP_LEFT;
11093 si->shortcut.snap_right = DEFAULT_KEY_SNAP_RIGHT;
11094 si->shortcut.snap_up = DEFAULT_KEY_SNAP_UP;
11095 si->shortcut.snap_down = DEFAULT_KEY_SNAP_DOWN;
11097 for (i = 0; i < MAX_PLAYERS; i++)
11099 si->input[i].use_joystick = FALSE;
11100 si->input[i].joy.device_name = getStringCopy(getDeviceNameFromJoystickNr(i));
11101 si->input[i].joy.xleft = JOYSTICK_XLEFT;
11102 si->input[i].joy.xmiddle = JOYSTICK_XMIDDLE;
11103 si->input[i].joy.xright = JOYSTICK_XRIGHT;
11104 si->input[i].joy.yupper = JOYSTICK_YUPPER;
11105 si->input[i].joy.ymiddle = JOYSTICK_YMIDDLE;
11106 si->input[i].joy.ylower = JOYSTICK_YLOWER;
11107 si->input[i].joy.snap = (i == 0 ? JOY_BUTTON_1 : 0);
11108 si->input[i].joy.drop = (i == 0 ? JOY_BUTTON_2 : 0);
11109 si->input[i].key.left = (i == 0 ? DEFAULT_KEY_LEFT : KSYM_UNDEFINED);
11110 si->input[i].key.right = (i == 0 ? DEFAULT_KEY_RIGHT : KSYM_UNDEFINED);
11111 si->input[i].key.up = (i == 0 ? DEFAULT_KEY_UP : KSYM_UNDEFINED);
11112 si->input[i].key.down = (i == 0 ? DEFAULT_KEY_DOWN : KSYM_UNDEFINED);
11113 si->input[i].key.snap = (i == 0 ? DEFAULT_KEY_SNAP : KSYM_UNDEFINED);
11114 si->input[i].key.drop = (i == 0 ? DEFAULT_KEY_DROP : KSYM_UNDEFINED);
11117 si->system.sdl_renderdriver = getStringCopy(ARG_DEFAULT);
11118 si->system.sdl_videodriver = getStringCopy(ARG_DEFAULT);
11119 si->system.sdl_audiodriver = getStringCopy(ARG_DEFAULT);
11120 si->system.audio_fragment_size = DEFAULT_AUDIO_FRAGMENT_SIZE;
11122 si->internal.program_title = getStringCopy(PROGRAM_TITLE_STRING);
11123 si->internal.program_version = getStringCopy(getProgramRealVersionString());
11124 si->internal.program_author = getStringCopy(PROGRAM_AUTHOR_STRING);
11125 si->internal.program_email = getStringCopy(PROGRAM_EMAIL_STRING);
11126 si->internal.program_website = getStringCopy(PROGRAM_WEBSITE_STRING);
11127 si->internal.program_copyright = getStringCopy(PROGRAM_COPYRIGHT_STRING);
11128 si->internal.program_company = getStringCopy(PROGRAM_COMPANY_STRING);
11130 si->internal.program_icon_file = getStringCopy(PROGRAM_ICON_FILENAME);
11132 si->internal.default_graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
11133 si->internal.default_sounds_set = getStringCopy(SND_CLASSIC_SUBDIR);
11134 si->internal.default_music_set = getStringCopy(MUS_CLASSIC_SUBDIR);
11136 si->internal.fallback_graphics_file = getStringCopy(UNDEFINED_FILENAME);
11137 si->internal.fallback_sounds_file = getStringCopy(UNDEFINED_FILENAME);
11138 si->internal.fallback_music_file = getStringCopy(UNDEFINED_FILENAME);
11140 si->internal.default_level_series = getStringCopy(UNDEFINED_LEVELSET);
11141 si->internal.choose_from_top_leveldir = FALSE;
11142 si->internal.show_scaling_in_title = TRUE;
11143 si->internal.create_user_levelset = TRUE;
11144 si->internal.info_screens_from_main = FALSE;
11146 si->internal.default_window_width = WIN_XSIZE_DEFAULT;
11147 si->internal.default_window_height = WIN_YSIZE_DEFAULT;
11149 si->debug.frame_delay[0] = DEFAULT_FRAME_DELAY_0;
11150 si->debug.frame_delay[1] = DEFAULT_FRAME_DELAY_1;
11151 si->debug.frame_delay[2] = DEFAULT_FRAME_DELAY_2;
11152 si->debug.frame_delay[3] = DEFAULT_FRAME_DELAY_3;
11153 si->debug.frame_delay[4] = DEFAULT_FRAME_DELAY_4;
11154 si->debug.frame_delay[5] = DEFAULT_FRAME_DELAY_5;
11155 si->debug.frame_delay[6] = DEFAULT_FRAME_DELAY_6;
11156 si->debug.frame_delay[7] = DEFAULT_FRAME_DELAY_7;
11157 si->debug.frame_delay[8] = DEFAULT_FRAME_DELAY_8;
11158 si->debug.frame_delay[9] = DEFAULT_FRAME_DELAY_9;
11160 si->debug.frame_delay_key[0] = DEFAULT_KEY_FRAME_DELAY_0;
11161 si->debug.frame_delay_key[1] = DEFAULT_KEY_FRAME_DELAY_1;
11162 si->debug.frame_delay_key[2] = DEFAULT_KEY_FRAME_DELAY_2;
11163 si->debug.frame_delay_key[3] = DEFAULT_KEY_FRAME_DELAY_3;
11164 si->debug.frame_delay_key[4] = DEFAULT_KEY_FRAME_DELAY_4;
11165 si->debug.frame_delay_key[5] = DEFAULT_KEY_FRAME_DELAY_5;
11166 si->debug.frame_delay_key[6] = DEFAULT_KEY_FRAME_DELAY_6;
11167 si->debug.frame_delay_key[7] = DEFAULT_KEY_FRAME_DELAY_7;
11168 si->debug.frame_delay_key[8] = DEFAULT_KEY_FRAME_DELAY_8;
11169 si->debug.frame_delay_key[9] = DEFAULT_KEY_FRAME_DELAY_9;
11171 si->debug.frame_delay_use_mod_key = DEFAULT_FRAME_DELAY_USE_MOD_KEY;
11172 si->debug.frame_delay_game_only = DEFAULT_FRAME_DELAY_GAME_ONLY;
11174 si->debug.show_frames_per_second = FALSE;
11176 si->debug.xsn_mode = AUTO;
11177 si->debug.xsn_percent = 0;
11179 si->options.verbose = FALSE;
11180 si->options.debug = FALSE;
11181 si->options.debug_mode = getStringCopy(ARG_UNDEFINED_STRING);
11183 #if defined(PLATFORM_ANDROID)
11184 si->fullscreen = TRUE;
11185 si->touch.overlay_buttons = TRUE;
11188 setHideSetupEntry(&setup.debug.xsn_mode);
11191 static void setSetupInfoToDefaults_AutoSetup(struct SetupInfo *si)
11193 si->auto_setup.editor_zoom_tilesize = MINI_TILESIZE;
11196 static void setSetupInfoToDefaults_ServerSetup(struct SetupInfo *si)
11198 si->player_uuid = NULL; // (will be set later)
11199 si->player_version = 1; // (will be set later)
11201 si->use_api_server = TRUE;
11202 si->api_server_hostname = getStringCopy(API_SERVER_HOSTNAME);
11203 si->api_server_password = getStringCopy(UNDEFINED_PASSWORD);
11204 si->ask_for_uploading_tapes = TRUE;
11205 si->ask_for_remaining_tapes = FALSE;
11206 si->provide_uploading_tapes = TRUE;
11207 si->ask_for_using_api_server = TRUE;
11208 si->has_remaining_tapes = FALSE;
11211 static void setSetupInfoToDefaults_EditorCascade(struct SetupInfo *si)
11213 si->editor_cascade.el_bd = TRUE;
11214 si->editor_cascade.el_bd_native = TRUE;
11215 si->editor_cascade.el_em = TRUE;
11216 si->editor_cascade.el_emc = TRUE;
11217 si->editor_cascade.el_rnd = TRUE;
11218 si->editor_cascade.el_sb = TRUE;
11219 si->editor_cascade.el_sp = TRUE;
11220 si->editor_cascade.el_dc = TRUE;
11221 si->editor_cascade.el_dx = TRUE;
11223 si->editor_cascade.el_mm = TRUE;
11224 si->editor_cascade.el_df = TRUE;
11226 si->editor_cascade.el_chars = FALSE;
11227 si->editor_cascade.el_steel_chars = FALSE;
11228 si->editor_cascade.el_ce = FALSE;
11229 si->editor_cascade.el_ge = FALSE;
11230 si->editor_cascade.el_es = FALSE;
11231 si->editor_cascade.el_ref = FALSE;
11232 si->editor_cascade.el_user = FALSE;
11233 si->editor_cascade.el_dynamic = FALSE;
11236 #define MAX_HIDE_SETUP_TOKEN_SIZE 20
11238 static char *getHideSetupToken(void *setup_value)
11240 static char hide_setup_token[MAX_HIDE_SETUP_TOKEN_SIZE];
11242 if (setup_value != NULL)
11243 snprintf(hide_setup_token, MAX_HIDE_SETUP_TOKEN_SIZE, "%p", setup_value);
11245 return hide_setup_token;
11248 void setHideSetupEntry(void *setup_value)
11250 char *hide_setup_token = getHideSetupToken(setup_value);
11252 if (hide_setup_hash == NULL)
11253 hide_setup_hash = newSetupFileHash();
11255 if (setup_value != NULL)
11256 setHashEntry(hide_setup_hash, hide_setup_token, "");
11259 void removeHideSetupEntry(void *setup_value)
11261 char *hide_setup_token = getHideSetupToken(setup_value);
11263 if (setup_value != NULL)
11264 removeHashEntry(hide_setup_hash, hide_setup_token);
11267 boolean hideSetupEntry(void *setup_value)
11269 char *hide_setup_token = getHideSetupToken(setup_value);
11271 return (setup_value != NULL &&
11272 getHashEntry(hide_setup_hash, hide_setup_token) != NULL);
11275 static void setSetupInfoFromTokenText(SetupFileHash *setup_file_hash,
11276 struct TokenInfo *token_info,
11277 int token_nr, char *token_text)
11279 char *token_hide_text = getStringCat2(token_text, ".hide");
11280 char *token_hide_value = getHashEntry(setup_file_hash, token_hide_text);
11282 // set the value of this setup option in the setup option structure
11283 setSetupInfo(token_info, token_nr, getHashEntry(setup_file_hash, token_text));
11285 // check if this setup option should be hidden in the setup menu
11286 if (token_hide_value != NULL && get_boolean_from_string(token_hide_value))
11287 setHideSetupEntry(token_info[token_nr].value);
11289 free(token_hide_text);
11292 static void setSetupInfoFromTokenInfo(SetupFileHash *setup_file_hash,
11293 struct TokenInfo *token_info,
11296 setSetupInfoFromTokenText(setup_file_hash, token_info, token_nr,
11297 token_info[token_nr].text);
11300 static void decodeSetupFileHash_Default(SetupFileHash *setup_file_hash)
11304 if (!setup_file_hash)
11307 for (i = 0; i < ARRAY_SIZE(global_setup_tokens); i++)
11308 setSetupInfoFromTokenInfo(setup_file_hash, global_setup_tokens, i);
11310 setup.touch.grid_initialized = TRUE;
11311 for (i = 0; i < 2; i++)
11313 int grid_xsize = setup.touch.grid_xsize[i];
11314 int grid_ysize = setup.touch.grid_ysize[i];
11317 // if virtual buttons are not loaded from setup file, repeat initializing
11318 // virtual buttons grid with default values later when video is initialized
11319 if (grid_xsize == -1 ||
11322 setup.touch.grid_initialized = FALSE;
11327 for (y = 0; y < grid_ysize; y++)
11329 char token_string[MAX_LINE_LEN];
11331 sprintf(token_string, "touch.virtual_buttons.%d.%02d", i, y);
11333 char *value_string = getHashEntry(setup_file_hash, token_string);
11335 if (value_string == NULL)
11338 for (x = 0; x < grid_xsize; x++)
11340 char c = value_string[x];
11342 setup.touch.grid_button[i][x][y] =
11343 (c == '.' ? CHAR_GRID_BUTTON_NONE : c);
11348 for (i = 0; i < ARRAY_SIZE(editor_setup_tokens); i++)
11349 setSetupInfoFromTokenInfo(setup_file_hash, editor_setup_tokens, i);
11351 for (i = 0; i < ARRAY_SIZE(shortcut_setup_tokens); i++)
11352 setSetupInfoFromTokenInfo(setup_file_hash, shortcut_setup_tokens, i);
11354 for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
11358 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
11360 setup_input = setup.input[pnr];
11361 for (i = 0; i < ARRAY_SIZE(player_setup_tokens); i++)
11363 char full_token[100];
11365 sprintf(full_token, "%s%s", prefix, player_setup_tokens[i].text);
11366 setSetupInfoFromTokenText(setup_file_hash, player_setup_tokens, i,
11369 setup.input[pnr] = setup_input;
11372 for (i = 0; i < ARRAY_SIZE(system_setup_tokens); i++)
11373 setSetupInfoFromTokenInfo(setup_file_hash, system_setup_tokens, i);
11375 for (i = 0; i < ARRAY_SIZE(internal_setup_tokens); i++)
11376 setSetupInfoFromTokenInfo(setup_file_hash, internal_setup_tokens, i);
11378 for (i = 0; i < ARRAY_SIZE(debug_setup_tokens); i++)
11379 setSetupInfoFromTokenInfo(setup_file_hash, debug_setup_tokens, i);
11381 for (i = 0; i < ARRAY_SIZE(options_setup_tokens); i++)
11382 setSetupInfoFromTokenInfo(setup_file_hash, options_setup_tokens, i);
11384 setHideRelatedSetupEntries();
11387 static void decodeSetupFileHash_AutoSetup(SetupFileHash *setup_file_hash)
11391 if (!setup_file_hash)
11394 for (i = 0; i < ARRAY_SIZE(auto_setup_tokens); i++)
11395 setSetupInfo(auto_setup_tokens, i,
11396 getHashEntry(setup_file_hash,
11397 auto_setup_tokens[i].text));
11400 static void decodeSetupFileHash_ServerSetup(SetupFileHash *setup_file_hash)
11404 if (!setup_file_hash)
11407 for (i = 0; i < ARRAY_SIZE(server_setup_tokens); i++)
11408 setSetupInfo(server_setup_tokens, i,
11409 getHashEntry(setup_file_hash,
11410 server_setup_tokens[i].text));
11413 static void decodeSetupFileHash_EditorCascade(SetupFileHash *setup_file_hash)
11417 if (!setup_file_hash)
11420 for (i = 0; i < ARRAY_SIZE(editor_cascade_setup_tokens); i++)
11421 setSetupInfo(editor_cascade_setup_tokens, i,
11422 getHashEntry(setup_file_hash,
11423 editor_cascade_setup_tokens[i].text));
11426 void LoadUserNames(void)
11428 int last_user_nr = user.nr;
11431 if (global.user_names != NULL)
11433 for (i = 0; i < MAX_PLAYER_NAMES; i++)
11434 checked_free(global.user_names[i]);
11436 checked_free(global.user_names);
11439 global.user_names = checked_calloc(MAX_PLAYER_NAMES * sizeof(char *));
11441 for (i = 0; i < MAX_PLAYER_NAMES; i++)
11445 SetupFileHash *setup_file_hash = loadSetupFileHash(getSetupFilename());
11447 if (setup_file_hash)
11449 char *player_name = getHashEntry(setup_file_hash, "player_name");
11451 global.user_names[i] = getFixedUserName(player_name);
11453 freeSetupFileHash(setup_file_hash);
11456 if (global.user_names[i] == NULL)
11457 global.user_names[i] = getStringCopy(getDefaultUserName(i));
11460 user.nr = last_user_nr;
11463 void LoadSetupFromFilename(char *filename)
11465 SetupFileHash *setup_file_hash = loadSetupFileHash(filename);
11467 if (setup_file_hash)
11469 decodeSetupFileHash_Default(setup_file_hash);
11471 freeSetupFileHash(setup_file_hash);
11475 Debug("setup", "using default setup values");
11479 static void LoadSetup_SpecialPostProcessing(void)
11481 char *player_name_new;
11483 // needed to work around problems with fixed length strings
11484 player_name_new = getFixedUserName(setup.player_name);
11485 free(setup.player_name);
11486 setup.player_name = player_name_new;
11488 // "scroll_delay: on(3) / off(0)" was replaced by scroll delay value
11489 if (setup.scroll_delay == FALSE)
11491 setup.scroll_delay_value = MIN_SCROLL_DELAY;
11492 setup.scroll_delay = TRUE; // now always "on"
11495 // make sure that scroll delay value stays inside valid range
11496 setup.scroll_delay_value =
11497 MIN(MAX(MIN_SCROLL_DELAY, setup.scroll_delay_value), MAX_SCROLL_DELAY);
11500 void LoadSetup_Default(void)
11504 // always start with reliable default values
11505 setSetupInfoToDefaults(&setup);
11507 // try to load setup values from default setup file
11508 filename = getDefaultSetupFilename();
11510 if (fileExists(filename))
11511 LoadSetupFromFilename(filename);
11513 // try to load setup values from platform setup file
11514 filename = getPlatformSetupFilename();
11516 if (fileExists(filename))
11517 LoadSetupFromFilename(filename);
11519 // try to load setup values from user setup file
11520 filename = getSetupFilename();
11522 LoadSetupFromFilename(filename);
11524 LoadSetup_SpecialPostProcessing();
11527 void LoadSetup_AutoSetup(void)
11529 char *filename = getPath2(getSetupDir(), AUTOSETUP_FILENAME);
11530 SetupFileHash *setup_file_hash = NULL;
11532 // always start with reliable default values
11533 setSetupInfoToDefaults_AutoSetup(&setup);
11535 setup_file_hash = loadSetupFileHash(filename);
11537 if (setup_file_hash)
11539 decodeSetupFileHash_AutoSetup(setup_file_hash);
11541 freeSetupFileHash(setup_file_hash);
11547 void LoadSetup_ServerSetup(void)
11549 char *filename = getPath2(getSetupDir(), SERVERSETUP_FILENAME);
11550 SetupFileHash *setup_file_hash = NULL;
11552 // always start with reliable default values
11553 setSetupInfoToDefaults_ServerSetup(&setup);
11555 setup_file_hash = loadSetupFileHash(filename);
11557 if (setup_file_hash)
11559 decodeSetupFileHash_ServerSetup(setup_file_hash);
11561 freeSetupFileHash(setup_file_hash);
11566 if (setup.player_uuid == NULL)
11568 // player UUID does not yet exist in setup file
11569 setup.player_uuid = getStringCopy(getUUID());
11570 setup.player_version = 2;
11572 SaveSetup_ServerSetup();
11576 void LoadSetup_EditorCascade(void)
11578 char *filename = getPath2(getSetupDir(), EDITORCASCADE_FILENAME);
11579 SetupFileHash *setup_file_hash = NULL;
11581 // always start with reliable default values
11582 setSetupInfoToDefaults_EditorCascade(&setup);
11584 setup_file_hash = loadSetupFileHash(filename);
11586 if (setup_file_hash)
11588 decodeSetupFileHash_EditorCascade(setup_file_hash);
11590 freeSetupFileHash(setup_file_hash);
11596 void LoadSetup(void)
11598 LoadSetup_Default();
11599 LoadSetup_AutoSetup();
11600 LoadSetup_ServerSetup();
11601 LoadSetup_EditorCascade();
11604 static void addGameControllerMappingToHash(SetupFileHash *mappings_hash,
11605 char *mapping_line)
11607 char mapping_guid[MAX_LINE_LEN];
11608 char *mapping_start, *mapping_end;
11610 // get GUID from game controller mapping line: copy complete line
11611 strncpy(mapping_guid, mapping_line, MAX_LINE_LEN - 1);
11612 mapping_guid[MAX_LINE_LEN - 1] = '\0';
11614 // get GUID from game controller mapping line: cut after GUID part
11615 mapping_start = strchr(mapping_guid, ',');
11616 if (mapping_start != NULL)
11617 *mapping_start = '\0';
11619 // cut newline from game controller mapping line
11620 mapping_end = strchr(mapping_line, '\n');
11621 if (mapping_end != NULL)
11622 *mapping_end = '\0';
11624 // add mapping entry to game controller mappings hash
11625 setHashEntry(mappings_hash, mapping_guid, mapping_line);
11628 static void LoadSetup_ReadGameControllerMappings(SetupFileHash *mappings_hash,
11633 if (!(file = fopen(filename, MODE_READ)))
11635 Warn("cannot read game controller mappings file '%s'", filename);
11640 while (!feof(file))
11642 char line[MAX_LINE_LEN];
11644 if (!fgets(line, MAX_LINE_LEN, file))
11647 addGameControllerMappingToHash(mappings_hash, line);
11653 void SaveSetup_Default(void)
11655 char *filename = getSetupFilename();
11659 InitUserDataDirectory();
11661 if (!(file = fopen(filename, MODE_WRITE)))
11663 Warn("cannot write setup file '%s'", filename);
11668 fprintFileHeader(file, SETUP_FILENAME);
11670 for (i = 0; i < ARRAY_SIZE(global_setup_tokens); i++)
11672 // just to make things nicer :)
11673 if (global_setup_tokens[i].value == &setup.multiple_users ||
11674 global_setup_tokens[i].value == &setup.sound ||
11675 global_setup_tokens[i].value == &setup.graphics_set ||
11676 global_setup_tokens[i].value == &setup.volume_simple ||
11677 global_setup_tokens[i].value == &setup.network_mode ||
11678 global_setup_tokens[i].value == &setup.touch.control_type ||
11679 global_setup_tokens[i].value == &setup.touch.grid_xsize[0] ||
11680 global_setup_tokens[i].value == &setup.touch.grid_xsize[1])
11681 fprintf(file, "\n");
11683 fprintf(file, "%s\n", getSetupLine(global_setup_tokens, "", i));
11686 for (i = 0; i < 2; i++)
11688 int grid_xsize = setup.touch.grid_xsize[i];
11689 int grid_ysize = setup.touch.grid_ysize[i];
11692 fprintf(file, "\n");
11694 for (y = 0; y < grid_ysize; y++)
11696 char token_string[MAX_LINE_LEN];
11697 char value_string[MAX_LINE_LEN];
11699 sprintf(token_string, "touch.virtual_buttons.%d.%02d", i, y);
11701 for (x = 0; x < grid_xsize; x++)
11703 char c = setup.touch.grid_button[i][x][y];
11705 value_string[x] = (c == CHAR_GRID_BUTTON_NONE ? '.' : c);
11708 value_string[grid_xsize] = '\0';
11710 fprintf(file, "%s\n", getFormattedSetupEntry(token_string, value_string));
11714 fprintf(file, "\n");
11715 for (i = 0; i < ARRAY_SIZE(editor_setup_tokens); i++)
11716 fprintf(file, "%s\n", getSetupLine(editor_setup_tokens, "", i));
11718 fprintf(file, "\n");
11719 for (i = 0; i < ARRAY_SIZE(shortcut_setup_tokens); i++)
11720 fprintf(file, "%s\n", getSetupLine(shortcut_setup_tokens, "", i));
11722 for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
11726 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
11727 fprintf(file, "\n");
11729 setup_input = setup.input[pnr];
11730 for (i = 0; i < ARRAY_SIZE(player_setup_tokens); i++)
11731 fprintf(file, "%s\n", getSetupLine(player_setup_tokens, prefix, i));
11734 fprintf(file, "\n");
11735 for (i = 0; i < ARRAY_SIZE(system_setup_tokens); i++)
11736 fprintf(file, "%s\n", getSetupLine(system_setup_tokens, "", i));
11738 // (internal setup values not saved to user setup file)
11740 fprintf(file, "\n");
11741 for (i = 0; i < ARRAY_SIZE(debug_setup_tokens); i++)
11742 if (!strPrefix(debug_setup_tokens[i].text, "debug.xsn_") ||
11743 setup.debug.xsn_mode != AUTO)
11744 fprintf(file, "%s\n", getSetupLine(debug_setup_tokens, "", i));
11746 fprintf(file, "\n");
11747 for (i = 0; i < ARRAY_SIZE(options_setup_tokens); i++)
11748 fprintf(file, "%s\n", getSetupLine(options_setup_tokens, "", i));
11752 SetFilePermissions(filename, PERMS_PRIVATE);
11755 void SaveSetup_AutoSetup(void)
11757 char *filename = getPath2(getSetupDir(), AUTOSETUP_FILENAME);
11761 InitUserDataDirectory();
11763 if (!(file = fopen(filename, MODE_WRITE)))
11765 Warn("cannot write auto setup file '%s'", filename);
11772 fprintFileHeader(file, AUTOSETUP_FILENAME);
11774 for (i = 0; i < ARRAY_SIZE(auto_setup_tokens); i++)
11775 fprintf(file, "%s\n", getSetupLine(auto_setup_tokens, "", i));
11779 SetFilePermissions(filename, PERMS_PRIVATE);
11784 void SaveSetup_ServerSetup(void)
11786 char *filename = getPath2(getSetupDir(), SERVERSETUP_FILENAME);
11790 InitUserDataDirectory();
11792 if (!(file = fopen(filename, MODE_WRITE)))
11794 Warn("cannot write server setup file '%s'", filename);
11801 fprintFileHeader(file, SERVERSETUP_FILENAME);
11803 for (i = 0; i < ARRAY_SIZE(server_setup_tokens); i++)
11805 // just to make things nicer :)
11806 if (server_setup_tokens[i].value == &setup.use_api_server)
11807 fprintf(file, "\n");
11809 fprintf(file, "%s\n", getSetupLine(server_setup_tokens, "", i));
11814 SetFilePermissions(filename, PERMS_PRIVATE);
11819 void SaveSetup_EditorCascade(void)
11821 char *filename = getPath2(getSetupDir(), EDITORCASCADE_FILENAME);
11825 InitUserDataDirectory();
11827 if (!(file = fopen(filename, MODE_WRITE)))
11829 Warn("cannot write editor cascade state file '%s'", filename);
11836 fprintFileHeader(file, EDITORCASCADE_FILENAME);
11838 for (i = 0; i < ARRAY_SIZE(editor_cascade_setup_tokens); i++)
11839 fprintf(file, "%s\n", getSetupLine(editor_cascade_setup_tokens, "", i));
11843 SetFilePermissions(filename, PERMS_PRIVATE);
11848 void SaveSetup(void)
11850 SaveSetup_Default();
11851 SaveSetup_AutoSetup();
11852 SaveSetup_ServerSetup();
11853 SaveSetup_EditorCascade();
11856 static void SaveSetup_WriteGameControllerMappings(SetupFileHash *mappings_hash,
11861 if (!(file = fopen(filename, MODE_WRITE)))
11863 Warn("cannot write game controller mappings file '%s'", filename);
11868 BEGIN_HASH_ITERATION(mappings_hash, itr)
11870 fprintf(file, "%s\n", HASH_ITERATION_VALUE(itr));
11872 END_HASH_ITERATION(mappings_hash, itr)
11877 void SaveSetup_AddGameControllerMapping(char *mapping)
11879 char *filename = getPath2(getSetupDir(), GAMECONTROLLER_BASENAME);
11880 SetupFileHash *mappings_hash = newSetupFileHash();
11882 InitUserDataDirectory();
11884 // load existing personal game controller mappings
11885 LoadSetup_ReadGameControllerMappings(mappings_hash, filename);
11887 // add new mapping to personal game controller mappings
11888 addGameControllerMappingToHash(mappings_hash, mapping);
11890 // save updated personal game controller mappings
11891 SaveSetup_WriteGameControllerMappings(mappings_hash, filename);
11893 freeSetupFileHash(mappings_hash);
11897 void LoadCustomElementDescriptions(void)
11899 char *filename = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
11900 SetupFileHash *setup_file_hash;
11903 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
11905 if (element_info[i].custom_description != NULL)
11907 free(element_info[i].custom_description);
11908 element_info[i].custom_description = NULL;
11912 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
11915 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
11917 char *token = getStringCat2(element_info[i].token_name, ".name");
11918 char *value = getHashEntry(setup_file_hash, token);
11921 element_info[i].custom_description = getStringCopy(value);
11926 freeSetupFileHash(setup_file_hash);
11929 static int getElementFromToken(char *token)
11931 char *value = getHashEntry(element_token_hash, token);
11934 return atoi(value);
11936 Warn("unknown element token '%s'", token);
11938 return EL_UNDEFINED;
11941 void FreeGlobalAnimEventInfo(void)
11943 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
11945 if (gaei->event_list == NULL)
11950 for (i = 0; i < gaei->num_event_lists; i++)
11952 checked_free(gaei->event_list[i]->event_value);
11953 checked_free(gaei->event_list[i]);
11956 checked_free(gaei->event_list);
11958 gaei->event_list = NULL;
11959 gaei->num_event_lists = 0;
11962 static int AddGlobalAnimEventList(void)
11964 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
11965 int list_pos = gaei->num_event_lists++;
11967 gaei->event_list = checked_realloc(gaei->event_list, gaei->num_event_lists *
11968 sizeof(struct GlobalAnimEventListInfo *));
11970 gaei->event_list[list_pos] =
11971 checked_calloc(sizeof(struct GlobalAnimEventListInfo));
11973 struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
11975 gaeli->event_value = NULL;
11976 gaeli->num_event_values = 0;
11981 static int AddGlobalAnimEventValue(int list_pos, int event_value)
11983 // do not add empty global animation events
11984 if (event_value == ANIM_EVENT_NONE)
11987 // if list position is undefined, create new list
11988 if (list_pos == ANIM_EVENT_UNDEFINED)
11989 list_pos = AddGlobalAnimEventList();
11991 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
11992 struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
11993 int value_pos = gaeli->num_event_values++;
11995 gaeli->event_value = checked_realloc(gaeli->event_value,
11996 gaeli->num_event_values * sizeof(int *));
11998 gaeli->event_value[value_pos] = event_value;
12003 int GetGlobalAnimEventValue(int list_pos, int value_pos)
12005 if (list_pos == ANIM_EVENT_UNDEFINED)
12006 return ANIM_EVENT_NONE;
12008 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
12009 struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
12011 return gaeli->event_value[value_pos];
12014 int GetGlobalAnimEventValueCount(int list_pos)
12016 if (list_pos == ANIM_EVENT_UNDEFINED)
12019 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
12020 struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
12022 return gaeli->num_event_values;
12025 // This function checks if a string <s> of the format "string1, string2, ..."
12026 // exactly contains a string <s_contained>.
12028 static boolean string_has_parameter(char *s, char *s_contained)
12032 if (s == NULL || s_contained == NULL)
12035 if (strlen(s_contained) > strlen(s))
12038 if (strncmp(s, s_contained, strlen(s_contained)) == 0)
12040 char next_char = s[strlen(s_contained)];
12042 // check if next character is delimiter or whitespace
12043 if (next_char == ',' || next_char == '\0' ||
12044 next_char == ' ' || next_char == '\t')
12048 // check if string contains another parameter string after a comma
12049 substring = strchr(s, ',');
12050 if (substring == NULL) // string does not contain a comma
12053 // advance string pointer to next character after the comma
12056 // skip potential whitespaces after the comma
12057 while (*substring == ' ' || *substring == '\t')
12060 return string_has_parameter(substring, s_contained);
12063 static int get_anim_parameter_value_ce(char *s)
12066 char *pattern_1 = "ce_change:custom_";
12067 char *pattern_2 = ".page_";
12068 int pattern_1_len = strlen(pattern_1);
12069 char *matching_char = strstr(s_ptr, pattern_1);
12070 int result = ANIM_EVENT_NONE;
12072 if (matching_char == NULL)
12073 return ANIM_EVENT_NONE;
12075 result = ANIM_EVENT_CE_CHANGE;
12077 s_ptr = matching_char + pattern_1_len;
12079 // check for custom element number ("custom_X", "custom_XX" or "custom_XXX")
12080 if (*s_ptr >= '0' && *s_ptr <= '9')
12082 int gic_ce_nr = (*s_ptr++ - '0');
12084 if (*s_ptr >= '0' && *s_ptr <= '9')
12086 gic_ce_nr = 10 * gic_ce_nr + (*s_ptr++ - '0');
12088 if (*s_ptr >= '0' && *s_ptr <= '9')
12089 gic_ce_nr = 10 * gic_ce_nr + (*s_ptr++ - '0');
12092 if (gic_ce_nr < 1 || gic_ce_nr > NUM_CUSTOM_ELEMENTS)
12093 return ANIM_EVENT_NONE;
12095 // custom element stored as 0 to 255
12098 result |= gic_ce_nr << ANIM_EVENT_CE_BIT;
12102 // invalid custom element number specified
12104 return ANIM_EVENT_NONE;
12107 // check for change page number ("page_X" or "page_XX") (optional)
12108 if (strPrefix(s_ptr, pattern_2))
12110 s_ptr += strlen(pattern_2);
12112 if (*s_ptr >= '0' && *s_ptr <= '9')
12114 int gic_page_nr = (*s_ptr++ - '0');
12116 if (*s_ptr >= '0' && *s_ptr <= '9')
12117 gic_page_nr = 10 * gic_page_nr + (*s_ptr++ - '0');
12119 if (gic_page_nr < 1 || gic_page_nr > MAX_CHANGE_PAGES)
12120 return ANIM_EVENT_NONE;
12122 // change page stored as 1 to 32 (0 means "all change pages")
12124 result |= gic_page_nr << ANIM_EVENT_PAGE_BIT;
12128 // invalid animation part number specified
12130 return ANIM_EVENT_NONE;
12134 // discard result if next character is neither delimiter nor whitespace
12135 if (!(*s_ptr == ',' || *s_ptr == '\0' ||
12136 *s_ptr == ' ' || *s_ptr == '\t'))
12137 return ANIM_EVENT_NONE;
12142 static int get_anim_parameter_value(char *s)
12144 int event_value[] =
12152 char *pattern_1[] =
12160 char *pattern_2 = ".part_";
12161 char *matching_char = NULL;
12163 int pattern_1_len = 0;
12164 int result = ANIM_EVENT_NONE;
12167 result = get_anim_parameter_value_ce(s);
12169 if (result != ANIM_EVENT_NONE)
12172 for (i = 0; i < ARRAY_SIZE(event_value); i++)
12174 matching_char = strstr(s_ptr, pattern_1[i]);
12175 pattern_1_len = strlen(pattern_1[i]);
12176 result = event_value[i];
12178 if (matching_char != NULL)
12182 if (matching_char == NULL)
12183 return ANIM_EVENT_NONE;
12185 s_ptr = matching_char + pattern_1_len;
12187 // check for main animation number ("anim_X" or "anim_XX")
12188 if (*s_ptr >= '0' && *s_ptr <= '9')
12190 int gic_anim_nr = (*s_ptr++ - '0');
12192 if (*s_ptr >= '0' && *s_ptr <= '9')
12193 gic_anim_nr = 10 * gic_anim_nr + (*s_ptr++ - '0');
12195 if (gic_anim_nr < 1 || gic_anim_nr > MAX_GLOBAL_ANIMS)
12196 return ANIM_EVENT_NONE;
12198 result |= gic_anim_nr << ANIM_EVENT_ANIM_BIT;
12202 // invalid main animation number specified
12204 return ANIM_EVENT_NONE;
12207 // check for animation part number ("part_X" or "part_XX") (optional)
12208 if (strPrefix(s_ptr, pattern_2))
12210 s_ptr += strlen(pattern_2);
12212 if (*s_ptr >= '0' && *s_ptr <= '9')
12214 int gic_part_nr = (*s_ptr++ - '0');
12216 if (*s_ptr >= '0' && *s_ptr <= '9')
12217 gic_part_nr = 10 * gic_part_nr + (*s_ptr++ - '0');
12219 if (gic_part_nr < 1 || gic_part_nr > MAX_GLOBAL_ANIM_PARTS)
12220 return ANIM_EVENT_NONE;
12222 result |= gic_part_nr << ANIM_EVENT_PART_BIT;
12226 // invalid animation part number specified
12228 return ANIM_EVENT_NONE;
12232 // discard result if next character is neither delimiter nor whitespace
12233 if (!(*s_ptr == ',' || *s_ptr == '\0' ||
12234 *s_ptr == ' ' || *s_ptr == '\t'))
12235 return ANIM_EVENT_NONE;
12240 static int get_anim_parameter_values(char *s)
12242 int list_pos = ANIM_EVENT_UNDEFINED;
12243 int event_value = ANIM_EVENT_DEFAULT;
12245 if (string_has_parameter(s, "any"))
12246 event_value |= ANIM_EVENT_ANY;
12248 if (string_has_parameter(s, "click:self") ||
12249 string_has_parameter(s, "click") ||
12250 string_has_parameter(s, "self"))
12251 event_value |= ANIM_EVENT_SELF;
12253 if (string_has_parameter(s, "unclick:any"))
12254 event_value |= ANIM_EVENT_UNCLICK_ANY;
12256 // if animation event found, add it to global animation event list
12257 if (event_value != ANIM_EVENT_NONE)
12258 list_pos = AddGlobalAnimEventValue(list_pos, event_value);
12262 // add optional "click:anim_X" or "click:anim_X.part_X" parameter
12263 event_value = get_anim_parameter_value(s);
12265 // if animation event found, add it to global animation event list
12266 if (event_value != ANIM_EVENT_NONE)
12267 list_pos = AddGlobalAnimEventValue(list_pos, event_value);
12269 // continue with next part of the string, starting with next comma
12270 s = strchr(s + 1, ',');
12276 static int get_anim_action_parameter_value(char *token)
12278 // check most common default case first to massively speed things up
12279 if (strEqual(token, ARG_UNDEFINED))
12280 return ANIM_EVENT_ACTION_NONE;
12282 int result = getImageIDFromToken(token);
12286 char *gfx_token = getStringCat2("gfx.", token);
12288 result = getImageIDFromToken(gfx_token);
12290 checked_free(gfx_token);
12295 Key key = getKeyFromX11KeyName(token);
12297 if (key != KSYM_UNDEFINED)
12298 result = -(int)key;
12305 result = get_hash_from_string(token); // unsigned int => int
12306 result = ABS(result); // may be negative now
12307 result += (result < MAX_IMAGE_FILES ? MAX_IMAGE_FILES : 0);
12309 setHashEntry(anim_url_hash, int2str(result, 0), token);
12314 result = ANIM_EVENT_ACTION_NONE;
12319 int get_parameter_value(char *value_raw, char *suffix, int type)
12321 char *value = getStringToLower(value_raw);
12322 int result = 0; // probably a save default value
12324 if (strEqual(suffix, ".direction"))
12326 result = (strEqual(value, "left") ? MV_LEFT :
12327 strEqual(value, "right") ? MV_RIGHT :
12328 strEqual(value, "up") ? MV_UP :
12329 strEqual(value, "down") ? MV_DOWN : MV_NONE);
12331 else if (strEqual(suffix, ".position"))
12333 result = (strEqual(value, "left") ? POS_LEFT :
12334 strEqual(value, "right") ? POS_RIGHT :
12335 strEqual(value, "top") ? POS_TOP :
12336 strEqual(value, "upper") ? POS_UPPER :
12337 strEqual(value, "middle") ? POS_MIDDLE :
12338 strEqual(value, "lower") ? POS_LOWER :
12339 strEqual(value, "bottom") ? POS_BOTTOM :
12340 strEqual(value, "any") ? POS_ANY :
12341 strEqual(value, "ce") ? POS_CE :
12342 strEqual(value, "ce_trigger") ? POS_CE_TRIGGER :
12343 strEqual(value, "last") ? POS_LAST : POS_UNDEFINED);
12345 else if (strEqual(suffix, ".align"))
12347 result = (strEqual(value, "left") ? ALIGN_LEFT :
12348 strEqual(value, "right") ? ALIGN_RIGHT :
12349 strEqual(value, "center") ? ALIGN_CENTER :
12350 strEqual(value, "middle") ? ALIGN_CENTER : ALIGN_DEFAULT);
12352 else if (strEqual(suffix, ".valign"))
12354 result = (strEqual(value, "top") ? VALIGN_TOP :
12355 strEqual(value, "bottom") ? VALIGN_BOTTOM :
12356 strEqual(value, "middle") ? VALIGN_MIDDLE :
12357 strEqual(value, "center") ? VALIGN_MIDDLE : VALIGN_DEFAULT);
12359 else if (strEqual(suffix, ".anim_mode"))
12361 result = (string_has_parameter(value, "none") ? ANIM_NONE :
12362 string_has_parameter(value, "loop") ? ANIM_LOOP :
12363 string_has_parameter(value, "linear") ? ANIM_LINEAR :
12364 string_has_parameter(value, "pingpong") ? ANIM_PINGPONG :
12365 string_has_parameter(value, "pingpong2") ? ANIM_PINGPONG2 :
12366 string_has_parameter(value, "random") ? ANIM_RANDOM :
12367 string_has_parameter(value, "random_static") ? ANIM_RANDOM_STATIC :
12368 string_has_parameter(value, "ce_value") ? ANIM_CE_VALUE :
12369 string_has_parameter(value, "ce_score") ? ANIM_CE_SCORE :
12370 string_has_parameter(value, "ce_delay") ? ANIM_CE_DELAY :
12371 string_has_parameter(value, "horizontal") ? ANIM_HORIZONTAL :
12372 string_has_parameter(value, "vertical") ? ANIM_VERTICAL :
12373 string_has_parameter(value, "centered") ? ANIM_CENTERED :
12374 string_has_parameter(value, "all") ? ANIM_ALL :
12375 string_has_parameter(value, "tiled") ? ANIM_TILED :
12376 string_has_parameter(value, "level_nr") ? ANIM_LEVEL_NR :
12379 if (string_has_parameter(value, "once"))
12380 result |= ANIM_ONCE;
12382 if (string_has_parameter(value, "reverse"))
12383 result |= ANIM_REVERSE;
12385 if (string_has_parameter(value, "opaque_player"))
12386 result |= ANIM_OPAQUE_PLAYER;
12388 if (string_has_parameter(value, "static_panel"))
12389 result |= ANIM_STATIC_PANEL;
12391 else if (strEqual(suffix, ".init_event") ||
12392 strEqual(suffix, ".anim_event"))
12394 result = get_anim_parameter_values(value);
12396 else if (strEqual(suffix, ".init_delay_action") ||
12397 strEqual(suffix, ".anim_delay_action") ||
12398 strEqual(suffix, ".post_delay_action") ||
12399 strEqual(suffix, ".init_event_action") ||
12400 strEqual(suffix, ".anim_event_action"))
12402 result = get_anim_action_parameter_value(value_raw);
12404 else if (strEqual(suffix, ".class"))
12406 result = (strEqual(value, ARG_UNDEFINED) ? ARG_UNDEFINED_VALUE :
12407 get_hash_from_string(value));
12409 else if (strEqual(suffix, ".style"))
12411 result = STYLE_DEFAULT;
12413 if (string_has_parameter(value, "accurate_borders"))
12414 result |= STYLE_ACCURATE_BORDERS;
12416 if (string_has_parameter(value, "inner_corners"))
12417 result |= STYLE_INNER_CORNERS;
12419 if (string_has_parameter(value, "reverse"))
12420 result |= STYLE_REVERSE;
12422 if (string_has_parameter(value, "leftmost_position"))
12423 result |= STYLE_LEFTMOST_POSITION;
12425 if (string_has_parameter(value, "block_clicks"))
12426 result |= STYLE_BLOCK;
12428 if (string_has_parameter(value, "passthrough_clicks"))
12429 result |= STYLE_PASSTHROUGH;
12431 if (string_has_parameter(value, "multiple_actions"))
12432 result |= STYLE_MULTIPLE_ACTIONS;
12434 if (string_has_parameter(value, "consume_ce_event"))
12435 result |= STYLE_CONSUME_CE_EVENT;
12437 else if (strEqual(suffix, ".fade_mode"))
12439 result = (string_has_parameter(value, "none") ? FADE_MODE_NONE :
12440 string_has_parameter(value, "fade") ? FADE_MODE_FADE :
12441 string_has_parameter(value, "fade_in") ? FADE_MODE_FADE_IN :
12442 string_has_parameter(value, "fade_out") ? FADE_MODE_FADE_OUT :
12443 string_has_parameter(value, "crossfade") ? FADE_MODE_CROSSFADE :
12444 string_has_parameter(value, "melt") ? FADE_MODE_MELT :
12445 string_has_parameter(value, "curtain") ? FADE_MODE_CURTAIN :
12446 FADE_MODE_DEFAULT);
12448 else if (strEqual(suffix, ".auto_delay_unit"))
12450 result = (string_has_parameter(value, "ms") ? AUTO_DELAY_UNIT_MS :
12451 string_has_parameter(value, "frames") ? AUTO_DELAY_UNIT_FRAMES :
12452 AUTO_DELAY_UNIT_DEFAULT);
12454 else if (strPrefix(suffix, ".font")) // (may also be ".font_xyz")
12456 result = gfx.get_font_from_token_function(value);
12458 else // generic parameter of type integer or boolean
12460 result = (strEqual(value, ARG_UNDEFINED) ? ARG_UNDEFINED_VALUE :
12461 type == TYPE_INTEGER ? get_integer_from_string(value) :
12462 type == TYPE_BOOLEAN ? get_boolean_from_string(value) :
12463 ARG_UNDEFINED_VALUE);
12471 static int get_token_parameter_value(char *token, char *value_raw)
12475 if (token == NULL || value_raw == NULL)
12476 return ARG_UNDEFINED_VALUE;
12478 suffix = strrchr(token, '.');
12479 if (suffix == NULL)
12482 if (strEqual(suffix, ".element"))
12483 return getElementFromToken(value_raw);
12485 // !!! USE CORRECT VALUE TYPE (currently works also for TYPE_BOOLEAN) !!!
12486 return get_parameter_value(value_raw, suffix, TYPE_INTEGER);
12489 void InitMenuDesignSettings_FromHash(SetupFileHash *setup_file_hash,
12490 boolean ignore_defaults)
12494 for (i = 0; image_config_vars[i].token != NULL; i++)
12496 char *value = getHashEntry(setup_file_hash, image_config_vars[i].token);
12498 // (ignore definitions set to "[DEFAULT]" which are already initialized)
12499 if (ignore_defaults && strEqual(value, ARG_DEFAULT))
12503 *image_config_vars[i].value =
12504 get_token_parameter_value(image_config_vars[i].token, value);
12508 void InitMenuDesignSettings_Static(void)
12510 // always start with reliable default values from static default config
12511 InitMenuDesignSettings_FromHash(image_config_hash, FALSE);
12514 static void InitMenuDesignSettings_SpecialPreProcessing(void)
12518 // the following initializes hierarchical values from static configuration
12520 // special case: initialize "ARG_DEFAULT" values in static default config
12521 // (e.g., initialize "[titlemessage].fade_mode" from "[title].fade_mode")
12522 titlescreen_initial_first_default.fade_mode =
12523 title_initial_first_default.fade_mode;
12524 titlescreen_initial_first_default.fade_delay =
12525 title_initial_first_default.fade_delay;
12526 titlescreen_initial_first_default.post_delay =
12527 title_initial_first_default.post_delay;
12528 titlescreen_initial_first_default.auto_delay =
12529 title_initial_first_default.auto_delay;
12530 titlescreen_initial_first_default.auto_delay_unit =
12531 title_initial_first_default.auto_delay_unit;
12532 titlescreen_first_default.fade_mode = title_first_default.fade_mode;
12533 titlescreen_first_default.fade_delay = title_first_default.fade_delay;
12534 titlescreen_first_default.post_delay = title_first_default.post_delay;
12535 titlescreen_first_default.auto_delay = title_first_default.auto_delay;
12536 titlescreen_first_default.auto_delay_unit =
12537 title_first_default.auto_delay_unit;
12538 titlemessage_initial_first_default.fade_mode =
12539 title_initial_first_default.fade_mode;
12540 titlemessage_initial_first_default.fade_delay =
12541 title_initial_first_default.fade_delay;
12542 titlemessage_initial_first_default.post_delay =
12543 title_initial_first_default.post_delay;
12544 titlemessage_initial_first_default.auto_delay =
12545 title_initial_first_default.auto_delay;
12546 titlemessage_initial_first_default.auto_delay_unit =
12547 title_initial_first_default.auto_delay_unit;
12548 titlemessage_first_default.fade_mode = title_first_default.fade_mode;
12549 titlemessage_first_default.fade_delay = title_first_default.fade_delay;
12550 titlemessage_first_default.post_delay = title_first_default.post_delay;
12551 titlemessage_first_default.auto_delay = title_first_default.auto_delay;
12552 titlemessage_first_default.auto_delay_unit =
12553 title_first_default.auto_delay_unit;
12555 titlescreen_initial_default.fade_mode = title_initial_default.fade_mode;
12556 titlescreen_initial_default.fade_delay = title_initial_default.fade_delay;
12557 titlescreen_initial_default.post_delay = title_initial_default.post_delay;
12558 titlescreen_initial_default.auto_delay = title_initial_default.auto_delay;
12559 titlescreen_initial_default.auto_delay_unit =
12560 title_initial_default.auto_delay_unit;
12561 titlescreen_default.fade_mode = title_default.fade_mode;
12562 titlescreen_default.fade_delay = title_default.fade_delay;
12563 titlescreen_default.post_delay = title_default.post_delay;
12564 titlescreen_default.auto_delay = title_default.auto_delay;
12565 titlescreen_default.auto_delay_unit = title_default.auto_delay_unit;
12566 titlemessage_initial_default.fade_mode = title_initial_default.fade_mode;
12567 titlemessage_initial_default.fade_delay = title_initial_default.fade_delay;
12568 titlemessage_initial_default.post_delay = title_initial_default.post_delay;
12569 titlemessage_initial_default.auto_delay_unit =
12570 title_initial_default.auto_delay_unit;
12571 titlemessage_default.fade_mode = title_default.fade_mode;
12572 titlemessage_default.fade_delay = title_default.fade_delay;
12573 titlemessage_default.post_delay = title_default.post_delay;
12574 titlemessage_default.auto_delay = title_default.auto_delay;
12575 titlemessage_default.auto_delay_unit = title_default.auto_delay_unit;
12577 // special case: initialize "ARG_DEFAULT" values in static default config
12578 // (e.g., init "titlemessage_1.fade_mode" from "[titlemessage].fade_mode")
12579 for (i = 0; i < MAX_NUM_TITLE_MESSAGES; i++)
12581 titlescreen_initial_first[i] = titlescreen_initial_first_default;
12582 titlescreen_first[i] = titlescreen_first_default;
12583 titlemessage_initial_first[i] = titlemessage_initial_first_default;
12584 titlemessage_first[i] = titlemessage_first_default;
12586 titlescreen_initial[i] = titlescreen_initial_default;
12587 titlescreen[i] = titlescreen_default;
12588 titlemessage_initial[i] = titlemessage_initial_default;
12589 titlemessage[i] = titlemessage_default;
12592 // special case: initialize "ARG_DEFAULT" values in static default config
12593 // (eg, init "menu.enter_screen.SCORES.xyz" from "menu.enter_screen.xyz")
12594 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
12596 if (i == GFX_SPECIAL_ARG_TITLE) // title values already initialized
12599 menu.enter_screen[i] = menu.enter_screen[GFX_SPECIAL_ARG_DEFAULT];
12600 menu.leave_screen[i] = menu.leave_screen[GFX_SPECIAL_ARG_DEFAULT];
12601 menu.next_screen[i] = menu.next_screen[GFX_SPECIAL_ARG_DEFAULT];
12604 // special case: initialize "ARG_DEFAULT" values in static default config
12605 // (eg, init "viewport.door_1.MAIN.xyz" from "viewport.door_1.xyz")
12606 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
12608 viewport.window[i] = viewport.window[GFX_SPECIAL_ARG_DEFAULT];
12609 viewport.playfield[i] = viewport.playfield[GFX_SPECIAL_ARG_DEFAULT];
12610 viewport.door_1[i] = viewport.door_1[GFX_SPECIAL_ARG_DEFAULT];
12612 if (i == GFX_SPECIAL_ARG_EDITOR) // editor values already initialized
12615 viewport.door_2[i] = viewport.door_2[GFX_SPECIAL_ARG_DEFAULT];
12619 static void InitMenuDesignSettings_SpecialPostProcessing(void)
12623 struct XY *dst, *src;
12625 game_buttons_xy[] =
12627 { &game.button.save, &game.button.stop },
12628 { &game.button.pause2, &game.button.pause },
12629 { &game.button.load, &game.button.play },
12630 { &game.button.undo, &game.button.stop },
12631 { &game.button.redo, &game.button.play },
12637 // special case: initialize later added SETUP list size from LEVELS value
12638 if (menu.list_size[GAME_MODE_SETUP] == -1)
12639 menu.list_size[GAME_MODE_SETUP] = menu.list_size[GAME_MODE_LEVELS];
12641 // set default position for snapshot buttons to stop/pause/play buttons
12642 for (i = 0; game_buttons_xy[i].dst != NULL; i++)
12643 if ((*game_buttons_xy[i].dst).x == -1 &&
12644 (*game_buttons_xy[i].dst).y == -1)
12645 *game_buttons_xy[i].dst = *game_buttons_xy[i].src;
12647 // --------------------------------------------------------------------------
12648 // dynamic viewports (including playfield margins, borders and alignments)
12649 // --------------------------------------------------------------------------
12651 // dynamic viewports currently only supported for landscape mode
12652 int display_width = MAX(video.display_width, video.display_height);
12653 int display_height = MIN(video.display_width, video.display_height);
12655 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
12657 struct RectWithBorder *vp_window = &viewport.window[i];
12658 struct RectWithBorder *vp_playfield = &viewport.playfield[i];
12659 struct RectWithBorder *vp_door_1 = &viewport.door_1[i];
12660 struct RectWithBorder *vp_door_2 = &viewport.door_2[i];
12661 boolean dynamic_window_width = (vp_window->min_width != -1);
12662 boolean dynamic_window_height = (vp_window->min_height != -1);
12663 boolean dynamic_playfield_width = (vp_playfield->min_width != -1);
12664 boolean dynamic_playfield_height = (vp_playfield->min_height != -1);
12666 // adjust window size if min/max width/height is specified
12668 if (vp_window->min_width != -1)
12670 int window_width = display_width;
12672 // when using static window height, use aspect ratio of display
12673 if (vp_window->min_height == -1)
12674 window_width = vp_window->height * display_width / display_height;
12676 vp_window->width = MAX(vp_window->min_width, window_width);
12679 if (vp_window->min_height != -1)
12681 int window_height = display_height;
12683 // when using static window width, use aspect ratio of display
12684 if (vp_window->min_width == -1)
12685 window_height = vp_window->width * display_height / display_width;
12687 vp_window->height = MAX(vp_window->min_height, window_height);
12690 if (vp_window->max_width != -1)
12691 vp_window->width = MIN(vp_window->width, vp_window->max_width);
12693 if (vp_window->max_height != -1)
12694 vp_window->height = MIN(vp_window->height, vp_window->max_height);
12696 int playfield_width = vp_window->width;
12697 int playfield_height = vp_window->height;
12699 // adjust playfield size and position according to specified margins
12701 playfield_width -= vp_playfield->margin_left;
12702 playfield_width -= vp_playfield->margin_right;
12704 playfield_height -= vp_playfield->margin_top;
12705 playfield_height -= vp_playfield->margin_bottom;
12707 // adjust playfield size if min/max width/height is specified
12709 if (vp_playfield->min_width != -1)
12710 vp_playfield->width = MAX(vp_playfield->min_width, playfield_width);
12712 if (vp_playfield->min_height != -1)
12713 vp_playfield->height = MAX(vp_playfield->min_height, playfield_height);
12715 if (vp_playfield->max_width != -1)
12716 vp_playfield->width = MIN(vp_playfield->width, vp_playfield->max_width);
12718 if (vp_playfield->max_height != -1)
12719 vp_playfield->height = MIN(vp_playfield->height, vp_playfield->max_height);
12721 // adjust playfield position according to specified alignment
12723 if (vp_playfield->align == ALIGN_LEFT || vp_playfield->x > 0)
12724 vp_playfield->x = ALIGNED_VP_XPOS(vp_playfield);
12725 else if (vp_playfield->align == ALIGN_CENTER)
12726 vp_playfield->x = playfield_width / 2 - vp_playfield->width / 2;
12727 else if (vp_playfield->align == ALIGN_RIGHT)
12728 vp_playfield->x += playfield_width - vp_playfield->width;
12730 if (vp_playfield->valign == VALIGN_TOP || vp_playfield->y > 0)
12731 vp_playfield->y = ALIGNED_VP_YPOS(vp_playfield);
12732 else if (vp_playfield->valign == VALIGN_MIDDLE)
12733 vp_playfield->y = playfield_height / 2 - vp_playfield->height / 2;
12734 else if (vp_playfield->valign == VALIGN_BOTTOM)
12735 vp_playfield->y += playfield_height - vp_playfield->height;
12737 vp_playfield->x += vp_playfield->margin_left;
12738 vp_playfield->y += vp_playfield->margin_top;
12740 // adjust individual playfield borders if only default border is specified
12742 if (vp_playfield->border_left == -1)
12743 vp_playfield->border_left = vp_playfield->border_size;
12744 if (vp_playfield->border_right == -1)
12745 vp_playfield->border_right = vp_playfield->border_size;
12746 if (vp_playfield->border_top == -1)
12747 vp_playfield->border_top = vp_playfield->border_size;
12748 if (vp_playfield->border_bottom == -1)
12749 vp_playfield->border_bottom = vp_playfield->border_size;
12751 // set dynamic playfield borders if borders are specified as undefined
12752 // (but only if window size was dynamic and playfield size was static)
12754 if (dynamic_window_width && !dynamic_playfield_width)
12756 if (vp_playfield->border_left == -1)
12758 vp_playfield->border_left = (vp_playfield->x -
12759 vp_playfield->margin_left);
12760 vp_playfield->x -= vp_playfield->border_left;
12761 vp_playfield->width += vp_playfield->border_left;
12764 if (vp_playfield->border_right == -1)
12766 vp_playfield->border_right = (vp_window->width -
12768 vp_playfield->width -
12769 vp_playfield->margin_right);
12770 vp_playfield->width += vp_playfield->border_right;
12774 if (dynamic_window_height && !dynamic_playfield_height)
12776 if (vp_playfield->border_top == -1)
12778 vp_playfield->border_top = (vp_playfield->y -
12779 vp_playfield->margin_top);
12780 vp_playfield->y -= vp_playfield->border_top;
12781 vp_playfield->height += vp_playfield->border_top;
12784 if (vp_playfield->border_bottom == -1)
12786 vp_playfield->border_bottom = (vp_window->height -
12788 vp_playfield->height -
12789 vp_playfield->margin_bottom);
12790 vp_playfield->height += vp_playfield->border_bottom;
12794 // adjust playfield size to be a multiple of a defined alignment tile size
12796 int align_size = vp_playfield->align_size;
12797 int playfield_xtiles = vp_playfield->width / align_size;
12798 int playfield_ytiles = vp_playfield->height / align_size;
12799 int playfield_width_corrected = playfield_xtiles * align_size;
12800 int playfield_height_corrected = playfield_ytiles * align_size;
12801 boolean is_playfield_mode = (i == GFX_SPECIAL_ARG_PLAYING ||
12802 i == GFX_SPECIAL_ARG_EDITOR);
12804 if (is_playfield_mode &&
12805 dynamic_playfield_width &&
12806 vp_playfield->width != playfield_width_corrected)
12808 int playfield_xdiff = vp_playfield->width - playfield_width_corrected;
12810 vp_playfield->width = playfield_width_corrected;
12812 if (vp_playfield->align == ALIGN_LEFT)
12814 vp_playfield->border_left += playfield_xdiff;
12816 else if (vp_playfield->align == ALIGN_RIGHT)
12818 vp_playfield->border_right += playfield_xdiff;
12820 else if (vp_playfield->align == ALIGN_CENTER)
12822 int border_left_diff = playfield_xdiff / 2;
12823 int border_right_diff = playfield_xdiff - border_left_diff;
12825 vp_playfield->border_left += border_left_diff;
12826 vp_playfield->border_right += border_right_diff;
12830 if (is_playfield_mode &&
12831 dynamic_playfield_height &&
12832 vp_playfield->height != playfield_height_corrected)
12834 int playfield_ydiff = vp_playfield->height - playfield_height_corrected;
12836 vp_playfield->height = playfield_height_corrected;
12838 if (vp_playfield->valign == VALIGN_TOP)
12840 vp_playfield->border_top += playfield_ydiff;
12842 else if (vp_playfield->align == VALIGN_BOTTOM)
12844 vp_playfield->border_right += playfield_ydiff;
12846 else if (vp_playfield->align == VALIGN_MIDDLE)
12848 int border_top_diff = playfield_ydiff / 2;
12849 int border_bottom_diff = playfield_ydiff - border_top_diff;
12851 vp_playfield->border_top += border_top_diff;
12852 vp_playfield->border_bottom += border_bottom_diff;
12856 // adjust door positions according to specified alignment
12858 for (j = 0; j < 2; j++)
12860 struct RectWithBorder *vp_door = (j == 0 ? vp_door_1 : vp_door_2);
12862 if (vp_door->align == ALIGN_LEFT || vp_door->x > 0)
12863 vp_door->x = ALIGNED_VP_XPOS(vp_door);
12864 else if (vp_door->align == ALIGN_CENTER)
12865 vp_door->x = vp_window->width / 2 - vp_door->width / 2;
12866 else if (vp_door->align == ALIGN_RIGHT)
12867 vp_door->x += vp_window->width - vp_door->width;
12869 if (vp_door->valign == VALIGN_TOP || vp_door->y > 0)
12870 vp_door->y = ALIGNED_VP_YPOS(vp_door);
12871 else if (vp_door->valign == VALIGN_MIDDLE)
12872 vp_door->y = vp_window->height / 2 - vp_door->height / 2;
12873 else if (vp_door->valign == VALIGN_BOTTOM)
12874 vp_door->y += vp_window->height - vp_door->height;
12879 static void InitMenuDesignSettings_SpecialPostProcessing_AfterGraphics(void)
12883 struct XYTileSize *dst, *src;
12886 editor_buttons_xy[] =
12889 &editor.button.element_left, &editor.palette.element_left,
12890 IMG_GFX_EDITOR_BUTTON_ELEMENT_LEFT
12893 &editor.button.element_middle, &editor.palette.element_middle,
12894 IMG_GFX_EDITOR_BUTTON_ELEMENT_MIDDLE
12897 &editor.button.element_right, &editor.palette.element_right,
12898 IMG_GFX_EDITOR_BUTTON_ELEMENT_RIGHT
12905 // set default position for element buttons to element graphics
12906 for (i = 0; editor_buttons_xy[i].dst != NULL; i++)
12908 if ((*editor_buttons_xy[i].dst).x == -1 &&
12909 (*editor_buttons_xy[i].dst).y == -1)
12911 struct GraphicInfo *gd = &graphic_info[editor_buttons_xy[i].graphic];
12913 gd->width = gd->height = editor_buttons_xy[i].src->tile_size;
12915 *editor_buttons_xy[i].dst = *editor_buttons_xy[i].src;
12919 // adjust editor palette rows and columns if specified to be dynamic
12921 if (editor.palette.cols == -1)
12923 int vp_width = viewport.playfield[GFX_SPECIAL_ARG_EDITOR].width;
12924 int bt_width = graphic_info[IMG_EDITOR_PALETTE_BUTTON].width;
12925 int sc_width = graphic_info[IMG_EDITOR_PALETTE_SCROLLBAR].width;
12927 editor.palette.cols = (vp_width - sc_width) / bt_width;
12929 if (editor.palette.x == -1)
12931 int palette_width = editor.palette.cols * bt_width + sc_width;
12933 editor.palette.x = (vp_width - palette_width) / 2;
12937 if (editor.palette.rows == -1)
12939 int vp_height = viewport.playfield[GFX_SPECIAL_ARG_EDITOR].height;
12940 int bt_height = graphic_info[IMG_EDITOR_PALETTE_BUTTON].height;
12941 int tx_height = getFontHeight(FONT_TEXT_2);
12943 editor.palette.rows = (vp_height - tx_height) / bt_height;
12945 if (editor.palette.y == -1)
12947 int palette_height = editor.palette.rows * bt_height + tx_height;
12949 editor.palette.y = (vp_height - palette_height) / 2;
12954 static void InitMenuDesignSettings_PreviewPlayers_Ext(SetupFileHash *hash,
12955 boolean initialize)
12957 // special case: check if network and preview player positions are redefined,
12958 // to compare this later against the main menu level preview being redefined
12959 struct TokenIntPtrInfo menu_config_players[] =
12961 { "main.network_players.x", &menu.main.network_players.redefined },
12962 { "main.network_players.y", &menu.main.network_players.redefined },
12963 { "main.preview_players.x", &menu.main.preview_players.redefined },
12964 { "main.preview_players.y", &menu.main.preview_players.redefined },
12965 { "preview.x", &preview.redefined },
12966 { "preview.y", &preview.redefined }
12972 for (i = 0; i < ARRAY_SIZE(menu_config_players); i++)
12973 *menu_config_players[i].value = FALSE;
12977 for (i = 0; i < ARRAY_SIZE(menu_config_players); i++)
12978 if (getHashEntry(hash, menu_config_players[i].token) != NULL)
12979 *menu_config_players[i].value = TRUE;
12983 static void InitMenuDesignSettings_PreviewPlayers(void)
12985 InitMenuDesignSettings_PreviewPlayers_Ext(NULL, TRUE);
12988 static void InitMenuDesignSettings_PreviewPlayers_FromHash(SetupFileHash *hash)
12990 InitMenuDesignSettings_PreviewPlayers_Ext(hash, FALSE);
12993 static void LoadMenuDesignSettingsFromFilename(char *filename)
12995 static struct TitleFadingInfo tfi;
12996 static struct TitleMessageInfo tmi;
12997 static struct TokenInfo title_tokens[] =
12999 { TYPE_INTEGER, &tfi.fade_mode, ".fade_mode" },
13000 { TYPE_INTEGER, &tfi.fade_delay, ".fade_delay" },
13001 { TYPE_INTEGER, &tfi.post_delay, ".post_delay" },
13002 { TYPE_INTEGER, &tfi.auto_delay, ".auto_delay" },
13003 { TYPE_INTEGER, &tfi.auto_delay_unit, ".auto_delay_unit" },
13007 static struct TokenInfo titlemessage_tokens[] =
13009 { TYPE_INTEGER, &tmi.x, ".x" },
13010 { TYPE_INTEGER, &tmi.y, ".y" },
13011 { TYPE_INTEGER, &tmi.width, ".width" },
13012 { TYPE_INTEGER, &tmi.height, ".height" },
13013 { TYPE_INTEGER, &tmi.chars, ".chars" },
13014 { TYPE_INTEGER, &tmi.lines, ".lines" },
13015 { TYPE_INTEGER, &tmi.align, ".align" },
13016 { TYPE_INTEGER, &tmi.valign, ".valign" },
13017 { TYPE_INTEGER, &tmi.font, ".font" },
13018 { TYPE_BOOLEAN, &tmi.autowrap, ".autowrap" },
13019 { TYPE_BOOLEAN, &tmi.centered, ".centered" },
13020 { TYPE_BOOLEAN, &tmi.parse_comments, ".parse_comments" },
13021 { TYPE_INTEGER, &tmi.sort_priority, ".sort_priority" },
13022 { TYPE_INTEGER, &tmi.fade_mode, ".fade_mode" },
13023 { TYPE_INTEGER, &tmi.fade_delay, ".fade_delay" },
13024 { TYPE_INTEGER, &tmi.post_delay, ".post_delay" },
13025 { TYPE_INTEGER, &tmi.auto_delay, ".auto_delay" },
13026 { TYPE_INTEGER, &tmi.auto_delay_unit, ".auto_delay_unit" },
13032 struct TitleFadingInfo *info;
13037 // initialize first titles from "enter screen" definitions, if defined
13038 { &title_initial_first_default, "menu.enter_screen.TITLE" },
13039 { &title_first_default, "menu.enter_screen.TITLE" },
13041 // initialize title screens from "next screen" definitions, if defined
13042 { &title_initial_default, "menu.next_screen.TITLE" },
13043 { &title_default, "menu.next_screen.TITLE" },
13049 struct TitleMessageInfo *array;
13052 titlemessage_arrays[] =
13054 // initialize first titles from "enter screen" definitions, if defined
13055 { titlescreen_initial_first, "menu.enter_screen.TITLE" },
13056 { titlescreen_first, "menu.enter_screen.TITLE" },
13057 { titlemessage_initial_first, "menu.enter_screen.TITLE" },
13058 { titlemessage_first, "menu.enter_screen.TITLE" },
13060 // initialize titles from "next screen" definitions, if defined
13061 { titlescreen_initial, "menu.next_screen.TITLE" },
13062 { titlescreen, "menu.next_screen.TITLE" },
13063 { titlemessage_initial, "menu.next_screen.TITLE" },
13064 { titlemessage, "menu.next_screen.TITLE" },
13066 // overwrite titles with title definitions, if defined
13067 { titlescreen_initial_first, "[title_initial]" },
13068 { titlescreen_first, "[title]" },
13069 { titlemessage_initial_first, "[title_initial]" },
13070 { titlemessage_first, "[title]" },
13072 { titlescreen_initial, "[title_initial]" },
13073 { titlescreen, "[title]" },
13074 { titlemessage_initial, "[title_initial]" },
13075 { titlemessage, "[title]" },
13077 // overwrite titles with title screen/message definitions, if defined
13078 { titlescreen_initial_first, "[titlescreen_initial]" },
13079 { titlescreen_first, "[titlescreen]" },
13080 { titlemessage_initial_first, "[titlemessage_initial]" },
13081 { titlemessage_first, "[titlemessage]" },
13083 { titlescreen_initial, "[titlescreen_initial]" },
13084 { titlescreen, "[titlescreen]" },
13085 { titlemessage_initial, "[titlemessage_initial]" },
13086 { titlemessage, "[titlemessage]" },
13090 SetupFileHash *setup_file_hash;
13093 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
13096 // the following initializes hierarchical values from dynamic configuration
13098 // special case: initialize with default values that may be overwritten
13099 // (e.g., init "menu.draw_xoffset.INFO" from "menu.draw_xoffset")
13100 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
13102 struct TokenIntPtrInfo menu_config[] =
13104 { "menu.draw_xoffset", &menu.draw_xoffset[i] },
13105 { "menu.draw_yoffset", &menu.draw_yoffset[i] },
13106 { "menu.list_size", &menu.list_size[i] }
13109 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
13111 char *token = menu_config[j].token;
13112 char *value = getHashEntry(setup_file_hash, token);
13115 *menu_config[j].value = get_integer_from_string(value);
13119 // special case: initialize with default values that may be overwritten
13120 // (eg, init "menu.draw_xoffset.INFO[XXX]" from "menu.draw_xoffset.INFO")
13121 for (i = 0; i < NUM_SPECIAL_GFX_INFO_ARGS; i++)
13123 struct TokenIntPtrInfo menu_config[] =
13125 { "menu.draw_xoffset.INFO", &menu.draw_xoffset_info[i] },
13126 { "menu.draw_yoffset.INFO", &menu.draw_yoffset_info[i] },
13127 { "menu.list_size.INFO", &menu.list_size_info[i] },
13128 { "menu.list_entry_size.INFO", &menu.list_entry_size_info[i] },
13129 { "menu.tile_size.INFO", &menu.tile_size_info[i] }
13132 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
13134 char *token = menu_config[j].token;
13135 char *value = getHashEntry(setup_file_hash, token);
13138 *menu_config[j].value = get_integer_from_string(value);
13142 // special case: initialize with default values that may be overwritten
13143 // (eg, init "menu.draw_xoffset.SETUP[XXX]" from "menu.draw_xoffset.SETUP")
13144 for (i = 0; i < NUM_SPECIAL_GFX_SETUP_ARGS; i++)
13146 struct TokenIntPtrInfo menu_config[] =
13148 { "menu.draw_xoffset.SETUP", &menu.draw_xoffset_setup[i] },
13149 { "menu.draw_yoffset.SETUP", &menu.draw_yoffset_setup[i] }
13152 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
13154 char *token = menu_config[j].token;
13155 char *value = getHashEntry(setup_file_hash, token);
13158 *menu_config[j].value = get_integer_from_string(value);
13162 // special case: initialize with default values that may be overwritten
13163 // (eg, init "menu.line_spacing.INFO[XXX]" from "menu.line_spacing.INFO")
13164 for (i = 0; i < NUM_SPECIAL_GFX_INFO_ARGS; i++)
13166 struct TokenIntPtrInfo menu_config[] =
13168 { "menu.left_spacing.INFO", &menu.left_spacing_info[i] },
13169 { "menu.middle_spacing.INFO", &menu.middle_spacing_info[i] },
13170 { "menu.right_spacing.INFO", &menu.right_spacing_info[i] },
13171 { "menu.top_spacing.INFO", &menu.top_spacing_info[i] },
13172 { "menu.bottom_spacing.INFO", &menu.bottom_spacing_info[i] },
13173 { "menu.paragraph_spacing.INFO", &menu.paragraph_spacing_info[i] },
13174 { "menu.headline1_spacing.INFO", &menu.headline1_spacing_info[i] },
13175 { "menu.headline2_spacing.INFO", &menu.headline2_spacing_info[i] },
13176 { "menu.line_spacing.INFO", &menu.line_spacing_info[i] },
13177 { "menu.extra_spacing.INFO", &menu.extra_spacing_info[i] },
13180 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
13182 char *token = menu_config[j].token;
13183 char *value = getHashEntry(setup_file_hash, token);
13186 *menu_config[j].value = get_integer_from_string(value);
13190 // special case: initialize with default values that may be overwritten
13191 // (eg, init "menu.enter_screen.SCORES.xyz" from "menu.enter_screen.xyz")
13192 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
13194 struct TokenIntPtrInfo menu_config[] =
13196 { "menu.enter_screen.fade_mode", &menu.enter_screen[i].fade_mode },
13197 { "menu.enter_screen.fade_delay", &menu.enter_screen[i].fade_delay },
13198 { "menu.enter_screen.post_delay", &menu.enter_screen[i].post_delay },
13199 { "menu.leave_screen.fade_mode", &menu.leave_screen[i].fade_mode },
13200 { "menu.leave_screen.fade_delay", &menu.leave_screen[i].fade_delay },
13201 { "menu.leave_screen.post_delay", &menu.leave_screen[i].post_delay },
13202 { "menu.next_screen.fade_mode", &menu.next_screen[i].fade_mode },
13203 { "menu.next_screen.fade_delay", &menu.next_screen[i].fade_delay },
13204 { "menu.next_screen.post_delay", &menu.next_screen[i].post_delay }
13207 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
13209 char *token = menu_config[j].token;
13210 char *value = getHashEntry(setup_file_hash, token);
13213 *menu_config[j].value = get_token_parameter_value(token, value);
13217 // special case: initialize with default values that may be overwritten
13218 // (eg, init "viewport.door_1.MAIN.xyz" from "viewport.door_1.xyz")
13219 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
13223 char *token_prefix;
13224 struct RectWithBorder *struct_ptr;
13228 { "viewport.window", &viewport.window[i] },
13229 { "viewport.playfield", &viewport.playfield[i] },
13230 { "viewport.door_1", &viewport.door_1[i] },
13231 { "viewport.door_2", &viewport.door_2[i] }
13234 for (j = 0; j < ARRAY_SIZE(vp_struct); j++)
13236 struct TokenIntPtrInfo vp_config[] =
13238 { ".x", &vp_struct[j].struct_ptr->x },
13239 { ".y", &vp_struct[j].struct_ptr->y },
13240 { ".width", &vp_struct[j].struct_ptr->width },
13241 { ".height", &vp_struct[j].struct_ptr->height },
13242 { ".min_width", &vp_struct[j].struct_ptr->min_width },
13243 { ".min_height", &vp_struct[j].struct_ptr->min_height },
13244 { ".max_width", &vp_struct[j].struct_ptr->max_width },
13245 { ".max_height", &vp_struct[j].struct_ptr->max_height },
13246 { ".margin_left", &vp_struct[j].struct_ptr->margin_left },
13247 { ".margin_right", &vp_struct[j].struct_ptr->margin_right },
13248 { ".margin_top", &vp_struct[j].struct_ptr->margin_top },
13249 { ".margin_bottom", &vp_struct[j].struct_ptr->margin_bottom },
13250 { ".border_left", &vp_struct[j].struct_ptr->border_left },
13251 { ".border_right", &vp_struct[j].struct_ptr->border_right },
13252 { ".border_top", &vp_struct[j].struct_ptr->border_top },
13253 { ".border_bottom", &vp_struct[j].struct_ptr->border_bottom },
13254 { ".border_size", &vp_struct[j].struct_ptr->border_size },
13255 { ".align_size", &vp_struct[j].struct_ptr->align_size },
13256 { ".align", &vp_struct[j].struct_ptr->align },
13257 { ".valign", &vp_struct[j].struct_ptr->valign }
13260 for (k = 0; k < ARRAY_SIZE(vp_config); k++)
13262 char *token = getStringCat2(vp_struct[j].token_prefix,
13263 vp_config[k].token);
13264 char *value = getHashEntry(setup_file_hash, token);
13267 *vp_config[k].value = get_token_parameter_value(token, value);
13274 // special case: initialize with default values that may be overwritten
13275 // (e.g., init "[title].fade_mode" from "menu.next_screen.TITLE.fade_mode")
13276 for (i = 0; title_info[i].info != NULL; i++)
13278 struct TitleFadingInfo *info = title_info[i].info;
13279 char *base_token = title_info[i].text;
13281 for (j = 0; title_tokens[j].type != -1; j++)
13283 char *token = getStringCat2(base_token, title_tokens[j].text);
13284 char *value = getHashEntry(setup_file_hash, token);
13288 int parameter_value = get_token_parameter_value(token, value);
13292 *(int *)title_tokens[j].value = (int)parameter_value;
13301 // special case: initialize with default values that may be overwritten
13302 // (e.g., init "titlemessage_1.fade_mode" from "[titlemessage].fade_mode")
13303 for (i = 0; titlemessage_arrays[i].array != NULL; i++)
13305 struct TitleMessageInfo *array = titlemessage_arrays[i].array;
13306 char *base_token = titlemessage_arrays[i].text;
13308 for (j = 0; titlemessage_tokens[j].type != -1; j++)
13310 char *token = getStringCat2(base_token, titlemessage_tokens[j].text);
13311 char *value = getHashEntry(setup_file_hash, token);
13315 int parameter_value = get_token_parameter_value(token, value);
13317 for (k = 0; k < MAX_NUM_TITLE_MESSAGES; k++)
13321 if (titlemessage_tokens[j].type == TYPE_INTEGER)
13322 *(int *)titlemessage_tokens[j].value = (int)parameter_value;
13324 *(boolean *)titlemessage_tokens[j].value = (boolean)parameter_value;
13334 // read (and overwrite with) values that may be specified in config file
13335 InitMenuDesignSettings_FromHash(setup_file_hash, TRUE);
13337 // special case: check if network and preview player positions are redefined
13338 InitMenuDesignSettings_PreviewPlayers_FromHash(setup_file_hash);
13340 freeSetupFileHash(setup_file_hash);
13343 void LoadMenuDesignSettings(void)
13345 char *filename_base = UNDEFINED_FILENAME, *filename_local;
13347 InitMenuDesignSettings_Static();
13348 InitMenuDesignSettings_SpecialPreProcessing();
13349 InitMenuDesignSettings_PreviewPlayers();
13351 if (!GFX_OVERRIDE_ARTWORK(ARTWORK_TYPE_GRAPHICS))
13353 // first look for special settings configured in level series config
13354 filename_base = getCustomArtworkLevelConfigFilename(ARTWORK_TYPE_GRAPHICS);
13356 if (fileExists(filename_base))
13357 LoadMenuDesignSettingsFromFilename(filename_base);
13360 filename_local = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
13362 if (filename_local != NULL && !strEqual(filename_base, filename_local))
13363 LoadMenuDesignSettingsFromFilename(filename_local);
13365 InitMenuDesignSettings_SpecialPostProcessing();
13368 void LoadMenuDesignSettings_AfterGraphics(void)
13370 InitMenuDesignSettings_SpecialPostProcessing_AfterGraphics();
13373 void InitSoundSettings_FromHash(SetupFileHash *setup_file_hash,
13374 boolean ignore_defaults)
13378 for (i = 0; sound_config_vars[i].token != NULL; i++)
13380 char *value = getHashEntry(setup_file_hash, sound_config_vars[i].token);
13382 // (ignore definitions set to "[DEFAULT]" which are already initialized)
13383 if (ignore_defaults && strEqual(value, ARG_DEFAULT))
13387 *sound_config_vars[i].value =
13388 get_token_parameter_value(sound_config_vars[i].token, value);
13392 void InitSoundSettings_Static(void)
13394 // always start with reliable default values from static default config
13395 InitSoundSettings_FromHash(sound_config_hash, FALSE);
13398 static void LoadSoundSettingsFromFilename(char *filename)
13400 SetupFileHash *setup_file_hash;
13402 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
13405 // read (and overwrite with) values that may be specified in config file
13406 InitSoundSettings_FromHash(setup_file_hash, TRUE);
13408 freeSetupFileHash(setup_file_hash);
13411 void LoadSoundSettings(void)
13413 char *filename_base = UNDEFINED_FILENAME, *filename_local;
13415 InitSoundSettings_Static();
13417 if (!GFX_OVERRIDE_ARTWORK(ARTWORK_TYPE_SOUNDS))
13419 // first look for special settings configured in level series config
13420 filename_base = getCustomArtworkLevelConfigFilename(ARTWORK_TYPE_SOUNDS);
13422 if (fileExists(filename_base))
13423 LoadSoundSettingsFromFilename(filename_base);
13426 filename_local = getCustomArtworkConfigFilename(ARTWORK_TYPE_SOUNDS);
13428 if (filename_local != NULL && !strEqual(filename_base, filename_local))
13429 LoadSoundSettingsFromFilename(filename_local);
13432 void LoadUserDefinedEditorElementList(int **elements, int *num_elements)
13434 char *filename = getEditorSetupFilename();
13435 SetupFileList *setup_file_list, *list;
13436 SetupFileHash *element_hash;
13437 int num_unknown_tokens = 0;
13440 if ((setup_file_list = loadSetupFileList(filename)) == NULL)
13443 element_hash = newSetupFileHash();
13445 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
13446 setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
13448 // determined size may be larger than needed (due to unknown elements)
13450 for (list = setup_file_list; list != NULL; list = list->next)
13453 // add space for up to 3 more elements for padding that may be needed
13454 *num_elements += 3;
13456 // free memory for old list of elements, if needed
13457 checked_free(*elements);
13459 // allocate memory for new list of elements
13460 *elements = checked_malloc(*num_elements * sizeof(int));
13463 for (list = setup_file_list; list != NULL; list = list->next)
13465 char *value = getHashEntry(element_hash, list->token);
13467 if (value == NULL) // try to find obsolete token mapping
13469 char *mapped_token = get_mapped_token(list->token);
13471 if (mapped_token != NULL)
13473 value = getHashEntry(element_hash, mapped_token);
13475 free(mapped_token);
13481 (*elements)[(*num_elements)++] = atoi(value);
13485 if (num_unknown_tokens == 0)
13488 Warn("unknown token(s) found in config file:");
13489 Warn("- config file: '%s'", filename);
13491 num_unknown_tokens++;
13494 Warn("- token: '%s'", list->token);
13498 if (num_unknown_tokens > 0)
13501 while (*num_elements % 4) // pad with empty elements, if needed
13502 (*elements)[(*num_elements)++] = EL_EMPTY;
13504 freeSetupFileList(setup_file_list);
13505 freeSetupFileHash(element_hash);
13508 for (i = 0; i < *num_elements; i++)
13509 Debug("editor", "element '%s' [%d]\n",
13510 element_info[(*elements)[i]].token_name, (*elements)[i]);
13514 static struct MusicFileInfo *get_music_file_info_ext(char *basename, int music,
13517 SetupFileHash *setup_file_hash = NULL;
13518 struct MusicFileInfo tmp_music_file_info, *new_music_file_info;
13519 char *filename_music, *filename_prefix, *filename_info;
13525 token_to_value_ptr[] =
13527 { "title_header", &tmp_music_file_info.title_header },
13528 { "artist_header", &tmp_music_file_info.artist_header },
13529 { "album_header", &tmp_music_file_info.album_header },
13530 { "year_header", &tmp_music_file_info.year_header },
13531 { "played_header", &tmp_music_file_info.played_header },
13533 { "title", &tmp_music_file_info.title },
13534 { "artist", &tmp_music_file_info.artist },
13535 { "album", &tmp_music_file_info.album },
13536 { "year", &tmp_music_file_info.year },
13537 { "played", &tmp_music_file_info.played },
13543 filename_music = (is_sound ? getCustomSoundFilename(basename) :
13544 getCustomMusicFilename(basename));
13546 if (filename_music == NULL)
13549 // ---------- try to replace file extension ----------
13551 filename_prefix = getStringCopy(filename_music);
13552 if (strrchr(filename_prefix, '.') != NULL)
13553 *strrchr(filename_prefix, '.') = '\0';
13554 filename_info = getStringCat2(filename_prefix, ".txt");
13556 if (fileExists(filename_info))
13557 setup_file_hash = loadSetupFileHash(filename_info);
13559 free(filename_prefix);
13560 free(filename_info);
13562 if (setup_file_hash == NULL)
13564 // ---------- try to add file extension ----------
13566 filename_prefix = getStringCopy(filename_music);
13567 filename_info = getStringCat2(filename_prefix, ".txt");
13569 if (fileExists(filename_info))
13570 setup_file_hash = loadSetupFileHash(filename_info);
13572 free(filename_prefix);
13573 free(filename_info);
13576 if (setup_file_hash == NULL)
13579 // ---------- music file info found ----------
13581 clear_mem(&tmp_music_file_info, sizeof(struct MusicFileInfo));
13583 for (i = 0; token_to_value_ptr[i].token != NULL; i++)
13585 char *value = getHashEntry(setup_file_hash, token_to_value_ptr[i].token);
13587 *token_to_value_ptr[i].value_ptr =
13588 getStringCopy(value != NULL && *value != '\0' ? value : UNKNOWN_NAME);
13591 tmp_music_file_info.basename = getStringCopy(basename);
13592 tmp_music_file_info.music = music;
13593 tmp_music_file_info.is_sound = is_sound;
13595 new_music_file_info = checked_malloc(sizeof(struct MusicFileInfo));
13596 *new_music_file_info = tmp_music_file_info;
13598 return new_music_file_info;
13601 static struct MusicFileInfo *get_music_file_info(char *basename, int music)
13603 return get_music_file_info_ext(basename, music, FALSE);
13606 static struct MusicFileInfo *get_sound_file_info(char *basename, int sound)
13608 return get_music_file_info_ext(basename, sound, TRUE);
13611 static boolean music_info_listed_ext(struct MusicFileInfo *list,
13612 char *basename, boolean is_sound)
13614 for (; list != NULL; list = list->next)
13615 if (list->is_sound == is_sound && strEqual(list->basename, basename))
13621 static boolean music_info_listed(struct MusicFileInfo *list, char *basename)
13623 return music_info_listed_ext(list, basename, FALSE);
13626 static boolean sound_info_listed(struct MusicFileInfo *list, char *basename)
13628 return music_info_listed_ext(list, basename, TRUE);
13631 void LoadMusicInfo(void)
13633 int num_music_noconf = getMusicListSize_NoConf();
13634 int num_music = getMusicListSize();
13635 int num_sounds = getSoundListSize();
13636 struct FileInfo *music, *sound;
13637 struct MusicFileInfo *next, **new;
13641 while (music_file_info != NULL)
13643 next = music_file_info->next;
13645 checked_free(music_file_info->basename);
13647 checked_free(music_file_info->title_header);
13648 checked_free(music_file_info->artist_header);
13649 checked_free(music_file_info->album_header);
13650 checked_free(music_file_info->year_header);
13651 checked_free(music_file_info->played_header);
13653 checked_free(music_file_info->title);
13654 checked_free(music_file_info->artist);
13655 checked_free(music_file_info->album);
13656 checked_free(music_file_info->year);
13657 checked_free(music_file_info->played);
13659 free(music_file_info);
13661 music_file_info = next;
13664 new = &music_file_info;
13666 // get (configured or unconfigured) music file info for all levels
13667 for (i = leveldir_current->first_level;
13668 i <= leveldir_current->last_level; i++)
13672 if (levelset.music[i] != MUS_UNDEFINED)
13674 // get music file info for configured level music
13675 music_nr = levelset.music[i];
13677 else if (num_music_noconf > 0)
13679 // get music file info for unconfigured level music
13680 int level_pos = i - leveldir_current->first_level;
13682 music_nr = MAP_NOCONF_MUSIC(level_pos % num_music_noconf);
13689 char *basename = getMusicInfoEntryFilename(music_nr);
13691 if (basename == NULL)
13694 if (!music_info_listed(music_file_info, basename))
13696 *new = get_music_file_info(basename, music_nr);
13699 new = &(*new)->next;
13703 // get music file info for all remaining configured music files
13704 for (i = 0; i < num_music; i++)
13706 music = getMusicListEntry(i);
13708 if (music->filename == NULL)
13711 if (strEqual(music->filename, UNDEFINED_FILENAME))
13714 // a configured file may be not recognized as music
13715 if (!FileIsMusic(music->filename))
13718 if (!music_info_listed(music_file_info, music->filename))
13720 *new = get_music_file_info(music->filename, i);
13723 new = &(*new)->next;
13727 // get sound file info for all configured sound files
13728 for (i = 0; i < num_sounds; i++)
13730 sound = getSoundListEntry(i);
13732 if (sound->filename == NULL)
13735 if (strEqual(sound->filename, UNDEFINED_FILENAME))
13738 // a configured file may be not recognized as sound
13739 if (!FileIsSound(sound->filename))
13742 if (!sound_info_listed(music_file_info, sound->filename))
13744 *new = get_sound_file_info(sound->filename, i);
13746 new = &(*new)->next;
13750 // add pointers to previous list nodes
13752 struct MusicFileInfo *node = music_file_info;
13754 while (node != NULL)
13757 node->next->prev = node;
13763 static void add_helpanim_entry(int element, int action, int direction,
13764 int delay, int *num_list_entries)
13766 struct HelpAnimInfo *new_list_entry;
13767 (*num_list_entries)++;
13770 checked_realloc(helpanim_info,
13771 *num_list_entries * sizeof(struct HelpAnimInfo));
13772 new_list_entry = &helpanim_info[*num_list_entries - 1];
13774 new_list_entry->element = element;
13775 new_list_entry->action = action;
13776 new_list_entry->direction = direction;
13777 new_list_entry->delay = delay;
13780 static void print_unknown_token(char *filename, char *token, int token_nr)
13785 Warn("unknown token(s) found in config file:");
13786 Warn("- config file: '%s'", filename);
13789 Warn("- token: '%s'", token);
13792 static void print_unknown_token_end(int token_nr)
13798 void LoadHelpAnimInfo(void)
13800 char *filename = getHelpAnimFilename();
13801 SetupFileList *setup_file_list = NULL, *list;
13802 SetupFileHash *element_hash, *action_hash, *direction_hash;
13803 int num_list_entries = 0;
13804 int num_unknown_tokens = 0;
13807 if (fileExists(filename))
13808 setup_file_list = loadSetupFileList(filename);
13810 if (setup_file_list == NULL)
13812 // use reliable default values from static configuration
13813 SetupFileList *insert_ptr;
13815 insert_ptr = setup_file_list =
13816 newSetupFileList(helpanim_config[0].token,
13817 helpanim_config[0].value);
13819 for (i = 1; helpanim_config[i].token; i++)
13820 insert_ptr = addListEntry(insert_ptr,
13821 helpanim_config[i].token,
13822 helpanim_config[i].value);
13825 element_hash = newSetupFileHash();
13826 action_hash = newSetupFileHash();
13827 direction_hash = newSetupFileHash();
13829 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
13830 setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
13832 for (i = 0; i < NUM_ACTIONS; i++)
13833 setHashEntry(action_hash, element_action_info[i].suffix,
13834 i_to_a(element_action_info[i].value));
13836 // do not store direction index (bit) here, but direction value!
13837 for (i = 0; i < NUM_DIRECTIONS_FULL; i++)
13838 setHashEntry(direction_hash, element_direction_info[i].suffix,
13839 i_to_a(1 << element_direction_info[i].value));
13841 for (list = setup_file_list; list != NULL; list = list->next)
13843 char *element_token, *action_token, *direction_token;
13844 char *element_value, *action_value, *direction_value;
13845 int delay = atoi(list->value);
13847 if (strEqual(list->token, "end"))
13849 add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
13854 /* first try to break element into element/action/direction parts;
13855 if this does not work, also accept combined "element[.act][.dir]"
13856 elements (like "dynamite.active"), which are unique elements */
13858 if (strchr(list->token, '.') == NULL) // token contains no '.'
13860 element_value = getHashEntry(element_hash, list->token);
13861 if (element_value != NULL) // element found
13862 add_helpanim_entry(atoi(element_value), -1, -1, delay,
13863 &num_list_entries);
13866 // no further suffixes found -- this is not an element
13867 print_unknown_token(filename, list->token, num_unknown_tokens++);
13873 // token has format "<prefix>.<something>"
13875 action_token = strchr(list->token, '.'); // suffix may be action ...
13876 direction_token = action_token; // ... or direction
13878 element_token = getStringCopy(list->token);
13879 *strchr(element_token, '.') = '\0';
13881 element_value = getHashEntry(element_hash, element_token);
13883 if (element_value == NULL) // this is no element
13885 element_value = getHashEntry(element_hash, list->token);
13886 if (element_value != NULL) // combined element found
13887 add_helpanim_entry(atoi(element_value), -1, -1, delay,
13888 &num_list_entries);
13890 print_unknown_token(filename, list->token, num_unknown_tokens++);
13892 free(element_token);
13897 action_value = getHashEntry(action_hash, action_token);
13899 if (action_value != NULL) // action found
13901 add_helpanim_entry(atoi(element_value), atoi(action_value), -1, delay,
13902 &num_list_entries);
13904 free(element_token);
13909 direction_value = getHashEntry(direction_hash, direction_token);
13911 if (direction_value != NULL) // direction found
13913 add_helpanim_entry(atoi(element_value), -1, atoi(direction_value), delay,
13914 &num_list_entries);
13916 free(element_token);
13921 if (strchr(action_token + 1, '.') == NULL)
13923 // no further suffixes found -- this is not an action nor direction
13925 element_value = getHashEntry(element_hash, list->token);
13926 if (element_value != NULL) // combined element found
13927 add_helpanim_entry(atoi(element_value), -1, -1, delay,
13928 &num_list_entries);
13930 print_unknown_token(filename, list->token, num_unknown_tokens++);
13932 free(element_token);
13937 // token has format "<prefix>.<suffix>.<something>"
13939 direction_token = strchr(action_token + 1, '.');
13941 action_token = getStringCopy(action_token);
13942 *strchr(action_token + 1, '.') = '\0';
13944 action_value = getHashEntry(action_hash, action_token);
13946 if (action_value == NULL) // this is no action
13948 element_value = getHashEntry(element_hash, list->token);
13949 if (element_value != NULL) // combined element found
13950 add_helpanim_entry(atoi(element_value), -1, -1, delay,
13951 &num_list_entries);
13953 print_unknown_token(filename, list->token, num_unknown_tokens++);
13955 free(element_token);
13956 free(action_token);
13961 direction_value = getHashEntry(direction_hash, direction_token);
13963 if (direction_value != NULL) // direction found
13965 add_helpanim_entry(atoi(element_value), atoi(action_value),
13966 atoi(direction_value), delay, &num_list_entries);
13968 free(element_token);
13969 free(action_token);
13974 // this is no direction
13976 element_value = getHashEntry(element_hash, list->token);
13977 if (element_value != NULL) // combined element found
13978 add_helpanim_entry(atoi(element_value), -1, -1, delay,
13979 &num_list_entries);
13981 print_unknown_token(filename, list->token, num_unknown_tokens++);
13983 free(element_token);
13984 free(action_token);
13987 print_unknown_token_end(num_unknown_tokens);
13989 add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
13990 add_helpanim_entry(HELPANIM_LIST_END, -1, -1, -1, &num_list_entries);
13992 freeSetupFileList(setup_file_list);
13993 freeSetupFileHash(element_hash);
13994 freeSetupFileHash(action_hash);
13995 freeSetupFileHash(direction_hash);
13998 for (i = 0; i < num_list_entries; i++)
13999 Debug("files:LoadHelpAnimInfo", "'%s': %d, %d, %d => %d",
14000 EL_NAME(helpanim_info[i].element),
14001 helpanim_info[i].element,
14002 helpanim_info[i].action,
14003 helpanim_info[i].direction,
14004 helpanim_info[i].delay);
14008 void LoadHelpTextInfo(void)
14010 char *filename = getHelpTextFilename();
14013 if (helptext_info != NULL)
14015 freeSetupFileHash(helptext_info);
14016 helptext_info = NULL;
14019 if (fileExists(filename))
14020 helptext_info = loadSetupFileHash(filename);
14022 if (helptext_info == NULL)
14024 // use reliable default values from static configuration
14025 helptext_info = newSetupFileHash();
14027 for (i = 0; helptext_config[i].token; i++)
14028 setHashEntry(helptext_info,
14029 helptext_config[i].token,
14030 helptext_config[i].value);
14034 BEGIN_HASH_ITERATION(helptext_info, itr)
14036 Debug("files:LoadHelpTextInfo", "'%s' => '%s'",
14037 HASH_ITERATION_TOKEN(itr), HASH_ITERATION_VALUE(itr));
14039 END_HASH_ITERATION(hash, itr)
14044 // ----------------------------------------------------------------------------
14046 // ----------------------------------------------------------------------------
14048 #define MAX_NUM_CONVERT_LEVELS 1000
14050 void ConvertLevels(void)
14052 static LevelDirTree *convert_leveldir = NULL;
14053 static int convert_level_nr = -1;
14054 static int num_levels_handled = 0;
14055 static int num_levels_converted = 0;
14056 static boolean levels_failed[MAX_NUM_CONVERT_LEVELS];
14059 convert_leveldir = getTreeInfoFromIdentifier(leveldir_first,
14060 global.convert_leveldir);
14062 if (convert_leveldir == NULL)
14063 Fail("no such level identifier: '%s'", global.convert_leveldir);
14065 leveldir_current = convert_leveldir;
14067 if (global.convert_level_nr != -1)
14069 convert_leveldir->first_level = global.convert_level_nr;
14070 convert_leveldir->last_level = global.convert_level_nr;
14073 convert_level_nr = convert_leveldir->first_level;
14075 PrintLine("=", 79);
14076 Print("Converting levels\n");
14077 PrintLine("-", 79);
14078 Print("Level series identifier: '%s'\n", convert_leveldir->identifier);
14079 Print("Level series name: '%s'\n", convert_leveldir->name);
14080 Print("Level series author: '%s'\n", convert_leveldir->author);
14081 Print("Number of levels: %d\n", convert_leveldir->levels);
14082 PrintLine("=", 79);
14085 for (i = 0; i < MAX_NUM_CONVERT_LEVELS; i++)
14086 levels_failed[i] = FALSE;
14088 while (convert_level_nr <= convert_leveldir->last_level)
14090 char *level_filename;
14093 level_nr = convert_level_nr++;
14095 Print("Level %03d: ", level_nr);
14097 LoadLevel(level_nr);
14098 if (level.no_level_file || level.no_valid_file)
14100 Print("(no level)\n");
14104 Print("converting level ... ");
14107 // special case: conversion of some EMC levels as requested by ACME
14108 level.game_engine_type = GAME_ENGINE_TYPE_RND;
14111 level_filename = getDefaultLevelFilename(level_nr);
14112 new_level = !fileExists(level_filename);
14116 SaveLevel(level_nr);
14118 num_levels_converted++;
14120 Print("converted.\n");
14124 if (level_nr >= 0 && level_nr < MAX_NUM_CONVERT_LEVELS)
14125 levels_failed[level_nr] = TRUE;
14127 Print("NOT CONVERTED -- LEVEL ALREADY EXISTS.\n");
14130 num_levels_handled++;
14134 PrintLine("=", 79);
14135 Print("Number of levels handled: %d\n", num_levels_handled);
14136 Print("Number of levels converted: %d (%d%%)\n", num_levels_converted,
14137 (num_levels_handled ?
14138 num_levels_converted * 100 / num_levels_handled : 0));
14139 PrintLine("-", 79);
14140 Print("Summary (for automatic parsing by scripts):\n");
14141 Print("LEVELDIR '%s', CONVERTED %d/%d (%d%%)",
14142 convert_leveldir->identifier, num_levels_converted,
14143 num_levels_handled,
14144 (num_levels_handled ?
14145 num_levels_converted * 100 / num_levels_handled : 0));
14147 if (num_levels_handled != num_levels_converted)
14149 Print(", FAILED:");
14150 for (i = 0; i < MAX_NUM_CONVERT_LEVELS; i++)
14151 if (levels_failed[i])
14156 PrintLine("=", 79);
14158 CloseAllAndExit(0);
14162 // ----------------------------------------------------------------------------
14163 // create and save images for use in level sketches (raw BMP format)
14164 // ----------------------------------------------------------------------------
14166 void CreateLevelSketchImages(void)
14172 InitElementPropertiesGfxElement();
14174 bitmap1 = CreateBitmap(TILEX, TILEY, DEFAULT_DEPTH);
14175 bitmap2 = CreateBitmap(MINI_TILEX, MINI_TILEY, DEFAULT_DEPTH);
14177 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
14179 int element = getMappedElement(i);
14180 char basename1[16];
14181 char basename2[16];
14185 sprintf(basename1, "%04d.bmp", i);
14186 sprintf(basename2, "%04ds.bmp", i);
14188 filename1 = getPath2(global.create_sketch_images_dir, basename1);
14189 filename2 = getPath2(global.create_sketch_images_dir, basename2);
14191 DrawSizedElement(0, 0, element, TILESIZE);
14192 BlitBitmap(drawto, bitmap1, SX, SY, TILEX, TILEY, 0, 0);
14194 if (SDL_SaveBMP(bitmap1->surface, filename1) != 0)
14195 Fail("cannot save level sketch image file '%s'", filename1);
14197 DrawSizedElement(0, 0, element, MINI_TILESIZE);
14198 BlitBitmap(drawto, bitmap2, SX, SY, MINI_TILEX, MINI_TILEY, 0, 0);
14200 if (SDL_SaveBMP(bitmap2->surface, filename2) != 0)
14201 Fail("cannot save level sketch image file '%s'", filename2);
14206 // create corresponding SQL statements (for normal and small images)
14209 printf("insert into phpbb_words values (NULL, '`%03d', '<IMG class=\"levelsketch\" src=\"/I/%04d.png\"/>');\n", i, i);
14210 printf("insert into phpbb_words values (NULL, '¸%03d', '<IMG class=\"levelsketch\" src=\"/I/%04ds.png\"/>');\n", i, i);
14213 printf("insert into phpbb_words values (NULL, '`%04d', '<IMG class=\"levelsketch\" src=\"/I/%04d.png\"/>');\n", i, i);
14214 printf("insert into phpbb_words values (NULL, '¸%04d', '<IMG class=\"levelsketch\" src=\"/I/%04ds.png\"/>');\n", i, i);
14216 // optional: create content for forum level sketch demonstration post
14218 fprintf(stderr, "%03d `%03d%c", i, i, (i % 10 < 9 ? ' ' : '\n'));
14221 FreeBitmap(bitmap1);
14222 FreeBitmap(bitmap2);
14225 fprintf(stderr, "\n");
14227 Info("%d normal and small images created", NUM_FILE_ELEMENTS);
14229 CloseAllAndExit(0);
14233 // ----------------------------------------------------------------------------
14234 // create and save images for element collecting animations (raw BMP format)
14235 // ----------------------------------------------------------------------------
14237 static boolean createCollectImage(int element)
14239 return (IS_COLLECTIBLE(element) && !IS_SP_ELEMENT(element));
14242 void CreateCollectElementImages(void)
14246 int anim_frames = num_steps - 1;
14247 int tile_size = TILESIZE;
14248 int anim_width = tile_size * anim_frames;
14249 int anim_height = tile_size;
14250 int num_collect_images = 0;
14251 int pos_collect_images = 0;
14253 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
14254 if (createCollectImage(i))
14255 num_collect_images++;
14257 Info("Creating %d element collecting animation images ...",
14258 num_collect_images);
14260 int dst_width = anim_width * 2;
14261 int dst_height = anim_height * num_collect_images / 2;
14262 Bitmap *dst_bitmap = CreateBitmap(dst_width, dst_height, DEFAULT_DEPTH);
14263 char *basename_bmp = "RocksCollect.bmp";
14264 char *basename_png = "RocksCollect.png";
14265 char *filename_bmp = getPath2(global.create_collect_images_dir, basename_bmp);
14266 char *filename_png = getPath2(global.create_collect_images_dir, basename_png);
14267 int len_filename_bmp = strlen(filename_bmp);
14268 int len_filename_png = strlen(filename_png);
14269 int max_command_len = MAX_FILENAME_LEN + len_filename_bmp + len_filename_png;
14270 char cmd_convert[max_command_len];
14272 snprintf(cmd_convert, max_command_len, "convert \"%s\" \"%s\"",
14276 // force using RGBA surface for destination bitmap
14277 SDL_SetColorKey(dst_bitmap->surface, SET_TRANSPARENT_PIXEL,
14278 SDL_MapRGB(dst_bitmap->surface->format, 0x00, 0x00, 0x00));
14280 dst_bitmap->surface =
14281 SDL_ConvertSurfaceFormat(dst_bitmap->surface, SDL_PIXELFORMAT_ARGB8888, 0);
14283 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
14285 if (!createCollectImage(i))
14288 int dst_x = (pos_collect_images / (num_collect_images / 2)) * anim_width;
14289 int dst_y = (pos_collect_images % (num_collect_images / 2)) * anim_height;
14290 int graphic = el2img(i);
14291 char *token_name = element_info[i].token_name;
14292 Bitmap *tmp_bitmap = CreateBitmap(tile_size, tile_size, DEFAULT_DEPTH);
14293 Bitmap *src_bitmap;
14296 Info("- creating collecting image for '%s' ...", token_name);
14298 getGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
14300 BlitBitmap(src_bitmap, tmp_bitmap, src_x, src_y,
14301 tile_size, tile_size, 0, 0);
14303 // force using RGBA surface for temporary bitmap (using transparent black)
14304 SDL_SetColorKey(tmp_bitmap->surface, SET_TRANSPARENT_PIXEL,
14305 SDL_MapRGB(tmp_bitmap->surface->format, 0x00, 0x00, 0x00));
14307 tmp_bitmap->surface =
14308 SDL_ConvertSurfaceFormat(tmp_bitmap->surface, SDL_PIXELFORMAT_ARGB8888, 0);
14310 tmp_bitmap->surface_masked = tmp_bitmap->surface;
14312 for (j = 0; j < anim_frames; j++)
14314 int frame_size_final = tile_size * (anim_frames - j) / num_steps;
14315 int frame_size = frame_size_final * num_steps;
14316 int offset = (tile_size - frame_size_final) / 2;
14317 Bitmap *frame_bitmap = ZoomBitmap(tmp_bitmap, frame_size, frame_size);
14319 while (frame_size > frame_size_final)
14323 Bitmap *half_bitmap = ZoomBitmap(frame_bitmap, frame_size, frame_size);
14325 FreeBitmap(frame_bitmap);
14327 frame_bitmap = half_bitmap;
14330 BlitBitmapMasked(frame_bitmap, dst_bitmap, 0, 0,
14331 frame_size_final, frame_size_final,
14332 dst_x + j * tile_size + offset, dst_y + offset);
14334 FreeBitmap(frame_bitmap);
14337 tmp_bitmap->surface_masked = NULL;
14339 FreeBitmap(tmp_bitmap);
14341 pos_collect_images++;
14344 if (SDL_SaveBMP(dst_bitmap->surface, filename_bmp) != 0)
14345 Fail("cannot save element collecting image file '%s'", filename_bmp);
14347 FreeBitmap(dst_bitmap);
14349 Info("Converting image file from BMP to PNG ...");
14351 if (system(cmd_convert) != 0)
14352 Fail("converting image file failed");
14354 unlink(filename_bmp);
14358 CloseAllAndExit(0);
14362 // ----------------------------------------------------------------------------
14363 // create and save images for custom and group elements (raw BMP format)
14364 // ----------------------------------------------------------------------------
14366 void CreateCustomElementImages(char *directory)
14368 char *src_basename = "RocksCE-template.ilbm";
14369 char *dst_basename = "RocksCE.bmp";
14370 char *src_filename = getPath2(directory, src_basename);
14371 char *dst_filename = getPath2(directory, dst_basename);
14372 Bitmap *src_bitmap;
14374 int yoffset_ce = 0;
14375 int yoffset_ge = (TILEY * NUM_CUSTOM_ELEMENTS / 16);
14378 InitVideoDefaults();
14380 ReCreateBitmap(&backbuffer, video.width, video.height);
14382 src_bitmap = LoadImage(src_filename);
14384 bitmap = CreateBitmap(TILEX * 16 * 2,
14385 TILEY * (NUM_CUSTOM_ELEMENTS + NUM_GROUP_ELEMENTS) / 16,
14388 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
14395 BlitBitmap(src_bitmap, bitmap, 0, 0, TILEX, TILEY,
14396 TILEX * x, TILEY * y + yoffset_ce);
14398 BlitBitmap(src_bitmap, bitmap, 0, TILEY,
14400 TILEX * x + TILEX * 16,
14401 TILEY * y + yoffset_ce);
14403 for (j = 2; j >= 0; j--)
14407 BlitBitmap(src_bitmap, bitmap,
14408 TILEX + c * 7, 0, 6, 10,
14409 TILEX * x + 6 + j * 7,
14410 TILEY * y + 11 + yoffset_ce);
14412 BlitBitmap(src_bitmap, bitmap,
14413 TILEX + c * 8, TILEY, 6, 10,
14414 TILEX * 16 + TILEX * x + 6 + j * 8,
14415 TILEY * y + 10 + yoffset_ce);
14421 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
14428 BlitBitmap(src_bitmap, bitmap, 0, 0, TILEX, TILEY,
14429 TILEX * x, TILEY * y + yoffset_ge);
14431 BlitBitmap(src_bitmap, bitmap, 0, TILEY,
14433 TILEX * x + TILEX * 16,
14434 TILEY * y + yoffset_ge);
14436 for (j = 1; j >= 0; j--)
14440 BlitBitmap(src_bitmap, bitmap, TILEX + c * 10, 11, 10, 10,
14441 TILEX * x + 6 + j * 10,
14442 TILEY * y + 11 + yoffset_ge);
14444 BlitBitmap(src_bitmap, bitmap,
14445 TILEX + c * 8, TILEY + 12, 6, 10,
14446 TILEX * 16 + TILEX * x + 10 + j * 8,
14447 TILEY * y + 10 + yoffset_ge);
14453 if (SDL_SaveBMP(bitmap->surface, dst_filename) != 0)
14454 Fail("cannot save CE graphics file '%s'", dst_filename);
14456 FreeBitmap(bitmap);
14458 CloseAllAndExit(0);