1 // ============================================================================
2 // Rocks'n'Diamonds - McDuffin Strikes Back!
3 // ----------------------------------------------------------------------------
4 // (c) 1995-2014 by Artsoft Entertainment
7 // http://www.artsoft.org/
8 // ----------------------------------------------------------------------------
10 // ============================================================================
17 #include "libgame/libgame.h"
26 #define ENABLE_UNUSED_CODE 0 /* currently unused functions */
27 #define ENABLE_HISTORIC_CHUNKS 0 /* only for historic reference */
28 #define ENABLE_RESERVED_CODE 0 /* reserved for later use */
30 #define CHUNK_ID_LEN 4 /* IFF style chunk id length */
31 #define CHUNK_SIZE_UNDEFINED 0 /* undefined chunk size == 0 */
32 #define CHUNK_SIZE_NONE -1 /* do not write chunk size */
34 #define LEVEL_CHUNK_NAME_SIZE MAX_LEVEL_NAME_LEN
35 #define LEVEL_CHUNK_AUTH_SIZE MAX_LEVEL_AUTHOR_LEN
37 #define LEVEL_CHUNK_VERS_SIZE 8 /* size of file version chunk */
38 #define LEVEL_CHUNK_DATE_SIZE 4 /* size of file date chunk */
39 #define LEVEL_CHUNK_HEAD_SIZE 80 /* size of level file header */
40 #define LEVEL_CHUNK_HEAD_UNUSED 0 /* unused level header bytes */
41 #define LEVEL_CHUNK_CNT2_SIZE 160 /* size of level CNT2 chunk */
42 #define LEVEL_CHUNK_CNT2_UNUSED 11 /* unused CNT2 chunk bytes */
43 #define LEVEL_CHUNK_CNT3_HEADER 16 /* size of level CNT3 header */
44 #define LEVEL_CHUNK_CNT3_UNUSED 10 /* unused CNT3 chunk bytes */
45 #define LEVEL_CPART_CUS3_SIZE 134 /* size of CUS3 chunk part */
46 #define LEVEL_CPART_CUS3_UNUSED 15 /* unused CUS3 bytes / part */
47 #define LEVEL_CHUNK_GRP1_SIZE 74 /* size of level GRP1 chunk */
49 /* (element number, number of change pages, change page number) */
50 #define LEVEL_CHUNK_CUSX_UNCHANGED (2 + (1 + 1) + (1 + 1))
52 /* (element number only) */
53 #define LEVEL_CHUNK_GRPX_UNCHANGED 2
54 #define LEVEL_CHUNK_NOTE_UNCHANGED 2
56 /* (nothing at all if unchanged) */
57 #define LEVEL_CHUNK_ELEM_UNCHANGED 0
59 #define TAPE_CHUNK_VERS_SIZE 8 /* size of file version chunk */
60 #define TAPE_CHUNK_HEAD_SIZE 20 /* size of tape file header */
61 #define TAPE_CHUNK_HEAD_UNUSED 2 /* unused tape header bytes */
63 #define LEVEL_CHUNK_CNT3_SIZE(x) (LEVEL_CHUNK_CNT3_HEADER + (x))
64 #define LEVEL_CHUNK_CUS3_SIZE(x) (2 + (x) * LEVEL_CPART_CUS3_SIZE)
65 #define LEVEL_CHUNK_CUS4_SIZE(x) (96 + (x) * 48)
67 /* file identifier strings */
68 #define LEVEL_COOKIE_TMPL "ROCKSNDIAMONDS_LEVEL_FILE_VERSION_x.x"
69 #define TAPE_COOKIE_TMPL "ROCKSNDIAMONDS_TAPE_FILE_VERSION_x.x"
70 #define SCORE_COOKIE "ROCKSNDIAMONDS_SCORE_FILE_VERSION_1.2"
72 /* values for deciding when (not) to save configuration data */
73 #define SAVE_CONF_NEVER 0
74 #define SAVE_CONF_ALWAYS 1
75 #define SAVE_CONF_WHEN_CHANGED -1
77 /* values for chunks using micro chunks */
78 #define CONF_MASK_1_BYTE 0x00
79 #define CONF_MASK_2_BYTE 0x40
80 #define CONF_MASK_4_BYTE 0x80
81 #define CONF_MASK_MULTI_BYTES 0xc0
83 #define CONF_MASK_BYTES 0xc0
84 #define CONF_MASK_TOKEN 0x3f
86 #define CONF_VALUE_1_BYTE(x) (CONF_MASK_1_BYTE | (x))
87 #define CONF_VALUE_2_BYTE(x) (CONF_MASK_2_BYTE | (x))
88 #define CONF_VALUE_4_BYTE(x) (CONF_MASK_4_BYTE | (x))
89 #define CONF_VALUE_MULTI_BYTES(x) (CONF_MASK_MULTI_BYTES | (x))
91 /* these definitions are just for convenience of use and readability */
92 #define CONF_VALUE_8_BIT(x) CONF_VALUE_1_BYTE(x)
93 #define CONF_VALUE_16_BIT(x) CONF_VALUE_2_BYTE(x)
94 #define CONF_VALUE_32_BIT(x) CONF_VALUE_4_BYTE(x)
95 #define CONF_VALUE_BYTES(x) CONF_VALUE_MULTI_BYTES(x)
97 #define CONF_VALUE_NUM_BYTES(x) ((x) == CONF_MASK_1_BYTE ? 1 : \
98 (x) == CONF_MASK_2_BYTE ? 2 : \
99 (x) == CONF_MASK_4_BYTE ? 4 : 0)
101 #define CONF_CONTENT_NUM_ELEMENTS (3 * 3)
102 #define CONF_CONTENT_NUM_BYTES (CONF_CONTENT_NUM_ELEMENTS * 2)
103 #define CONF_ELEMENT_NUM_BYTES (2)
105 #define CONF_ENTITY_NUM_BYTES(t) ((t) == TYPE_ELEMENT || \
106 (t) == TYPE_ELEMENT_LIST ? \
107 CONF_ELEMENT_NUM_BYTES : \
108 (t) == TYPE_CONTENT || \
109 (t) == TYPE_CONTENT_LIST ? \
110 CONF_CONTENT_NUM_BYTES : 1)
112 #define CONF_ELEMENT_BYTE_POS(i) ((i) * CONF_ELEMENT_NUM_BYTES)
113 #define CONF_ELEMENTS_ELEMENT(b,i) ((b[CONF_ELEMENT_BYTE_POS(i)] << 8) | \
114 (b[CONF_ELEMENT_BYTE_POS(i) + 1]))
116 #define CONF_CONTENT_ELEMENT_POS(c,x,y) ((c) * CONF_CONTENT_NUM_ELEMENTS + \
118 #define CONF_CONTENT_BYTE_POS(c,x,y) (CONF_CONTENT_ELEMENT_POS(c,x,y) * \
119 CONF_ELEMENT_NUM_BYTES)
120 #define CONF_CONTENTS_ELEMENT(b,c,x,y) ((b[CONF_CONTENT_BYTE_POS(c,x,y)]<< 8)|\
121 (b[CONF_CONTENT_BYTE_POS(c,x,y) + 1]))
123 /* temporary variables used to store pointers to structure members */
124 static struct LevelInfo li;
125 static struct ElementInfo xx_ei, yy_ei;
126 static struct ElementChangeInfo xx_change;
127 static struct ElementGroupInfo xx_group;
128 static struct EnvelopeInfo xx_envelope;
129 static unsigned int xx_event_bits[NUM_CE_BITFIELDS];
130 static char xx_default_description[MAX_ELEMENT_NAME_LEN + 1];
131 static int xx_num_contents;
132 static int xx_current_change_page;
133 static char xx_default_string_empty[1] = "";
134 static int xx_string_length_unused;
136 struct LevelFileConfigInfo
138 int element; /* element for which data is to be stored */
139 int save_type; /* save data always, never or when changed */
140 int data_type; /* data type (used internally, not stored) */
141 int conf_type; /* micro chunk identifier (stored in file) */
144 void *value; /* variable that holds the data to be stored */
145 int default_value; /* initial default value for this variable */
148 void *value_copy; /* variable that holds the data to be copied */
149 void *num_entities; /* number of entities for multi-byte data */
150 int default_num_entities; /* default number of entities for this data */
151 int max_num_entities; /* maximal number of entities for this data */
152 char *default_string; /* optional default string for string data */
155 static struct LevelFileConfigInfo chunk_config_INFO[] =
157 /* ---------- values not related to single elements ----------------------- */
160 -1, SAVE_CONF_ALWAYS,
161 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
162 &li.game_engine_type, GAME_ENGINE_TYPE_RND
166 -1, SAVE_CONF_ALWAYS,
167 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
168 &li.fieldx, STD_LEV_FIELDX
171 -1, SAVE_CONF_ALWAYS,
172 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
173 &li.fieldy, STD_LEV_FIELDY
177 -1, SAVE_CONF_ALWAYS,
178 TYPE_INTEGER, CONF_VALUE_16_BIT(3),
183 -1, SAVE_CONF_ALWAYS,
184 TYPE_INTEGER, CONF_VALUE_16_BIT(4),
190 TYPE_INTEGER, CONF_VALUE_32_BIT(2),
196 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
197 &li.use_step_counter, FALSE
202 TYPE_BITFIELD, CONF_VALUE_8_BIT(4),
203 &li.wind_direction_initial, MV_NONE
208 TYPE_BOOLEAN, CONF_VALUE_8_BIT(5),
209 &li.em_slippery_gems, FALSE
214 TYPE_BOOLEAN, CONF_VALUE_8_BIT(6),
215 &li.use_custom_template, FALSE
220 TYPE_BITFIELD, CONF_VALUE_32_BIT(1),
221 &li.can_move_into_acid_bits, ~0 /* default: everything can */
226 TYPE_BITFIELD, CONF_VALUE_8_BIT(7),
227 &li.dont_collide_with_bits, ~0 /* default: always deadly */
232 TYPE_BOOLEAN, CONF_VALUE_8_BIT(8),
233 &li.em_explodes_by_fire, FALSE
238 TYPE_INTEGER, CONF_VALUE_16_BIT(5),
239 &li.score[SC_TIME_BONUS], 1
244 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
245 &li.auto_exit_sokoban, FALSE
250 TYPE_BOOLEAN, CONF_VALUE_8_BIT(10),
251 &li.auto_count_gems, FALSE
256 TYPE_BOOLEAN, CONF_VALUE_8_BIT(11),
257 &li.solved_by_one_player, FALSE
267 static struct LevelFileConfigInfo chunk_config_ELEM[] =
269 /* (these values are the same for each player) */
272 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
273 &li.block_last_field, FALSE /* default case for EM levels */
277 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
278 &li.sp_block_last_field, TRUE /* default case for SP levels */
282 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
283 &li.instant_relocation, FALSE
287 TYPE_BOOLEAN, CONF_VALUE_8_BIT(4),
288 &li.can_pass_to_walkable, FALSE
292 TYPE_BOOLEAN, CONF_VALUE_8_BIT(5),
293 &li.block_snap_field, TRUE
297 TYPE_BOOLEAN, CONF_VALUE_8_BIT(6),
298 &li.continuous_snapping, TRUE
302 TYPE_BOOLEAN, CONF_VALUE_8_BIT(12),
303 &li.shifted_relocation, FALSE
307 TYPE_BOOLEAN, CONF_VALUE_8_BIT(15),
308 &li.lazy_relocation, FALSE
311 /* (these values are different for each player) */
314 TYPE_INTEGER, CONF_VALUE_8_BIT(7),
315 &li.initial_player_stepsize[0], STEPSIZE_NORMAL
319 TYPE_BOOLEAN, CONF_VALUE_8_BIT(8),
320 &li.initial_player_gravity[0], FALSE
324 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
325 &li.use_start_element[0], FALSE
329 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
330 &li.start_element[0], EL_PLAYER_1
334 TYPE_BOOLEAN, CONF_VALUE_8_BIT(10),
335 &li.use_artwork_element[0], FALSE
339 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
340 &li.artwork_element[0], EL_PLAYER_1
344 TYPE_BOOLEAN, CONF_VALUE_8_BIT(11),
345 &li.use_explosion_element[0], FALSE
349 TYPE_ELEMENT, CONF_VALUE_16_BIT(3),
350 &li.explosion_element[0], EL_PLAYER_1
354 TYPE_BOOLEAN, CONF_VALUE_8_BIT(13),
355 &li.use_initial_inventory[0], FALSE
359 TYPE_BOOLEAN, CONF_VALUE_8_BIT(14),
360 &li.initial_inventory_size[0], 1
364 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(1),
365 &li.initial_inventory_content[0][0],EL_EMPTY, NULL,
366 &li.initial_inventory_size[0], 1, MAX_INITIAL_INVENTORY_SIZE
371 TYPE_INTEGER, CONF_VALUE_8_BIT(7),
372 &li.initial_player_stepsize[1], STEPSIZE_NORMAL
376 TYPE_BOOLEAN, CONF_VALUE_8_BIT(8),
377 &li.initial_player_gravity[1], FALSE
381 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
382 &li.use_start_element[1], FALSE
386 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
387 &li.start_element[1], EL_PLAYER_2
391 TYPE_BOOLEAN, CONF_VALUE_8_BIT(10),
392 &li.use_artwork_element[1], FALSE
396 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
397 &li.artwork_element[1], EL_PLAYER_2
401 TYPE_BOOLEAN, CONF_VALUE_8_BIT(11),
402 &li.use_explosion_element[1], FALSE
406 TYPE_ELEMENT, CONF_VALUE_16_BIT(3),
407 &li.explosion_element[1], EL_PLAYER_2
411 TYPE_BOOLEAN, CONF_VALUE_8_BIT(13),
412 &li.use_initial_inventory[1], FALSE
416 TYPE_BOOLEAN, CONF_VALUE_8_BIT(14),
417 &li.initial_inventory_size[1], 1
421 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(1),
422 &li.initial_inventory_content[1][0],EL_EMPTY, NULL,
423 &li.initial_inventory_size[1], 1, MAX_INITIAL_INVENTORY_SIZE
428 TYPE_INTEGER, CONF_VALUE_8_BIT(7),
429 &li.initial_player_stepsize[2], STEPSIZE_NORMAL
433 TYPE_BOOLEAN, CONF_VALUE_8_BIT(8),
434 &li.initial_player_gravity[2], FALSE
438 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
439 &li.use_start_element[2], FALSE
443 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
444 &li.start_element[2], EL_PLAYER_3
448 TYPE_BOOLEAN, CONF_VALUE_8_BIT(10),
449 &li.use_artwork_element[2], FALSE
453 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
454 &li.artwork_element[2], EL_PLAYER_3
458 TYPE_BOOLEAN, CONF_VALUE_8_BIT(11),
459 &li.use_explosion_element[2], FALSE
463 TYPE_ELEMENT, CONF_VALUE_16_BIT(3),
464 &li.explosion_element[2], EL_PLAYER_3
468 TYPE_BOOLEAN, CONF_VALUE_8_BIT(13),
469 &li.use_initial_inventory[2], FALSE
473 TYPE_BOOLEAN, CONF_VALUE_8_BIT(14),
474 &li.initial_inventory_size[2], 1
478 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(1),
479 &li.initial_inventory_content[2][0],EL_EMPTY, NULL,
480 &li.initial_inventory_size[2], 1, MAX_INITIAL_INVENTORY_SIZE
485 TYPE_INTEGER, CONF_VALUE_8_BIT(7),
486 &li.initial_player_stepsize[3], STEPSIZE_NORMAL
490 TYPE_BOOLEAN, CONF_VALUE_8_BIT(8),
491 &li.initial_player_gravity[3], FALSE
495 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
496 &li.use_start_element[3], FALSE
500 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
501 &li.start_element[3], EL_PLAYER_4
505 TYPE_BOOLEAN, CONF_VALUE_8_BIT(10),
506 &li.use_artwork_element[3], FALSE
510 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
511 &li.artwork_element[3], EL_PLAYER_4
515 TYPE_BOOLEAN, CONF_VALUE_8_BIT(11),
516 &li.use_explosion_element[3], FALSE
520 TYPE_ELEMENT, CONF_VALUE_16_BIT(3),
521 &li.explosion_element[3], EL_PLAYER_4
525 TYPE_BOOLEAN, CONF_VALUE_8_BIT(13),
526 &li.use_initial_inventory[3], FALSE
530 TYPE_BOOLEAN, CONF_VALUE_8_BIT(14),
531 &li.initial_inventory_size[3], 1
535 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(1),
536 &li.initial_inventory_content[3][0],EL_EMPTY, NULL,
537 &li.initial_inventory_size[3], 1, MAX_INITIAL_INVENTORY_SIZE
542 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
543 &li.score[SC_EMERALD], 10
548 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
549 &li.score[SC_DIAMOND], 10
554 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
555 &li.score[SC_BUG], 10
560 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
561 &li.score[SC_SPACESHIP], 10
566 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
567 &li.score[SC_PACMAN], 10
572 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
573 &li.score[SC_NUT], 10
578 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
579 &li.score[SC_DYNAMITE], 10
584 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
585 &li.score[SC_KEY], 10
590 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
591 &li.score[SC_PEARL], 10
596 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
597 &li.score[SC_CRYSTAL], 10
602 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
603 &li.amoeba_content, EL_DIAMOND
607 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
612 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
613 &li.grow_into_diggable, TRUE
618 TYPE_CONTENT_LIST, CONF_VALUE_BYTES(1),
619 &li.yamyam_content, EL_ROCK, NULL,
620 &li.num_yamyam_contents, 4, MAX_ELEMENT_CONTENTS
624 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
625 &li.score[SC_YAMYAM], 10
630 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
631 &li.score[SC_ROBOT], 10
635 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
641 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
647 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
648 &li.time_magic_wall, 10
653 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
654 &li.game_of_life[0], 2
658 TYPE_INTEGER, CONF_VALUE_8_BIT(2),
659 &li.game_of_life[1], 3
663 TYPE_INTEGER, CONF_VALUE_8_BIT(3),
664 &li.game_of_life[2], 3
668 TYPE_INTEGER, CONF_VALUE_8_BIT(4),
669 &li.game_of_life[3], 3
673 TYPE_BOOLEAN, CONF_VALUE_8_BIT(5),
674 &li.use_life_bugs, FALSE
679 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
684 TYPE_INTEGER, CONF_VALUE_8_BIT(2),
689 TYPE_INTEGER, CONF_VALUE_8_BIT(3),
694 TYPE_INTEGER, CONF_VALUE_8_BIT(4),
699 EL_TIMEGATE_SWITCH, -1,
700 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
701 &li.time_timegate, 10
705 EL_LIGHT_SWITCH_ACTIVE, -1,
706 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
711 EL_SHIELD_NORMAL, -1,
712 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
713 &li.shield_normal_time, 10
716 EL_SHIELD_NORMAL, -1,
717 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
718 &li.score[SC_SHIELD], 10
722 EL_SHIELD_DEADLY, -1,
723 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
724 &li.shield_deadly_time, 10
727 EL_SHIELD_DEADLY, -1,
728 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
729 &li.score[SC_SHIELD], 10
734 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
739 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
740 &li.extra_time_score, 10
744 EL_TIME_ORB_FULL, -1,
745 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
746 &li.time_orb_time, 10
749 EL_TIME_ORB_FULL, -1,
750 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
751 &li.use_time_orb_bug, FALSE
756 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
757 &li.use_spring_bug, FALSE
762 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
763 &li.android_move_time, 10
767 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
768 &li.android_clone_time, 10
772 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(1),
773 &li.android_clone_element[0], EL_EMPTY, NULL,
774 &li.num_android_clone_elements, 1, MAX_ANDROID_ELEMENTS
779 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
784 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
789 EL_EMC_MAGNIFIER, -1,
790 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
791 &li.magnify_score, 10
794 EL_EMC_MAGNIFIER, -1,
795 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
800 EL_EMC_MAGIC_BALL, -1,
801 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
805 EL_EMC_MAGIC_BALL, -1,
806 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
807 &li.ball_random, FALSE
810 EL_EMC_MAGIC_BALL, -1,
811 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
812 &li.ball_state_initial, FALSE
815 EL_EMC_MAGIC_BALL, -1,
816 TYPE_CONTENT_LIST, CONF_VALUE_BYTES(1),
817 &li.ball_content, EL_EMPTY, NULL,
818 &li.num_ball_contents, 4, MAX_ELEMENT_CONTENTS
823 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
824 &li.mm_laser_red, FALSE
828 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
829 &li.mm_laser_green, FALSE
833 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
834 &li.mm_laser_blue, TRUE
839 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
840 &li.df_laser_red, TRUE
844 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
845 &li.df_laser_green, TRUE
849 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
850 &li.df_laser_blue, FALSE
854 EL_MM_FUSE_ACTIVE, -1,
855 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
860 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
865 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
869 EL_MM_STEEL_BLOCK, -1,
870 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
871 &li.mm_time_block, 75
875 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
876 &li.score[SC_ELEM_BONUS], 10
879 /* ---------- unused values ----------------------------------------------- */
882 EL_UNKNOWN, SAVE_CONF_NEVER,
883 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
884 &li.score[SC_UNKNOWN_15], 10
894 static struct LevelFileConfigInfo chunk_config_NOTE[] =
898 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
899 &xx_envelope.xsize, MAX_ENVELOPE_XSIZE,
903 TYPE_INTEGER, CONF_VALUE_8_BIT(2),
904 &xx_envelope.ysize, MAX_ENVELOPE_YSIZE,
909 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
910 &xx_envelope.autowrap, FALSE
914 TYPE_BOOLEAN, CONF_VALUE_8_BIT(4),
915 &xx_envelope.centered, FALSE
920 TYPE_STRING, CONF_VALUE_BYTES(1),
921 &xx_envelope.text, -1, NULL,
922 &xx_string_length_unused, -1, MAX_ENVELOPE_TEXT_LEN,
923 &xx_default_string_empty[0]
933 static struct LevelFileConfigInfo chunk_config_CUSX_base[] =
937 TYPE_STRING, CONF_VALUE_BYTES(1),
938 &xx_ei.description[0], -1,
939 &yy_ei.description[0],
940 &xx_string_length_unused, -1, MAX_ELEMENT_NAME_LEN,
941 &xx_default_description[0]
946 TYPE_BITFIELD, CONF_VALUE_32_BIT(1),
947 &xx_ei.properties[EP_BITFIELD_BASE_NR], EP_BITMASK_BASE_DEFAULT,
948 &yy_ei.properties[EP_BITFIELD_BASE_NR]
950 #if ENABLE_RESERVED_CODE
951 /* (reserved for later use) */
954 TYPE_BITFIELD, CONF_VALUE_32_BIT(2),
955 &xx_ei.properties[EP_BITFIELD_BASE_NR + 1], EP_BITMASK_DEFAULT,
956 &yy_ei.properties[EP_BITFIELD_BASE_NR + 1]
962 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
963 &xx_ei.use_gfx_element, FALSE,
964 &yy_ei.use_gfx_element
968 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
969 &xx_ei.gfx_element_initial, EL_EMPTY_SPACE,
970 &yy_ei.gfx_element_initial
975 TYPE_BITFIELD, CONF_VALUE_8_BIT(2),
976 &xx_ei.access_direction, MV_ALL_DIRECTIONS,
977 &yy_ei.access_direction
982 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
983 &xx_ei.collect_score_initial, 10,
984 &yy_ei.collect_score_initial
988 TYPE_INTEGER, CONF_VALUE_16_BIT(3),
989 &xx_ei.collect_count_initial, 1,
990 &yy_ei.collect_count_initial
995 TYPE_INTEGER, CONF_VALUE_16_BIT(4),
996 &xx_ei.ce_value_fixed_initial, 0,
997 &yy_ei.ce_value_fixed_initial
1001 TYPE_INTEGER, CONF_VALUE_16_BIT(5),
1002 &xx_ei.ce_value_random_initial, 0,
1003 &yy_ei.ce_value_random_initial
1007 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
1008 &xx_ei.use_last_ce_value, FALSE,
1009 &yy_ei.use_last_ce_value
1014 TYPE_INTEGER, CONF_VALUE_16_BIT(6),
1015 &xx_ei.push_delay_fixed, 8,
1016 &yy_ei.push_delay_fixed
1020 TYPE_INTEGER, CONF_VALUE_16_BIT(7),
1021 &xx_ei.push_delay_random, 8,
1022 &yy_ei.push_delay_random
1026 TYPE_INTEGER, CONF_VALUE_16_BIT(8),
1027 &xx_ei.drop_delay_fixed, 0,
1028 &yy_ei.drop_delay_fixed
1032 TYPE_INTEGER, CONF_VALUE_16_BIT(9),
1033 &xx_ei.drop_delay_random, 0,
1034 &yy_ei.drop_delay_random
1038 TYPE_INTEGER, CONF_VALUE_16_BIT(10),
1039 &xx_ei.move_delay_fixed, 0,
1040 &yy_ei.move_delay_fixed
1044 TYPE_INTEGER, CONF_VALUE_16_BIT(11),
1045 &xx_ei.move_delay_random, 0,
1046 &yy_ei.move_delay_random
1051 TYPE_BITFIELD, CONF_VALUE_32_BIT(3),
1052 &xx_ei.move_pattern, MV_ALL_DIRECTIONS,
1057 TYPE_BITFIELD, CONF_VALUE_8_BIT(4),
1058 &xx_ei.move_direction_initial, MV_START_AUTOMATIC,
1059 &yy_ei.move_direction_initial
1063 TYPE_INTEGER, CONF_VALUE_8_BIT(5),
1064 &xx_ei.move_stepsize, TILEX / 8,
1065 &yy_ei.move_stepsize
1070 TYPE_ELEMENT, CONF_VALUE_16_BIT(12),
1071 &xx_ei.move_enter_element, EL_EMPTY_SPACE,
1072 &yy_ei.move_enter_element
1076 TYPE_ELEMENT, CONF_VALUE_16_BIT(13),
1077 &xx_ei.move_leave_element, EL_EMPTY_SPACE,
1078 &yy_ei.move_leave_element
1082 TYPE_INTEGER, CONF_VALUE_8_BIT(6),
1083 &xx_ei.move_leave_type, LEAVE_TYPE_UNLIMITED,
1084 &yy_ei.move_leave_type
1089 TYPE_INTEGER, CONF_VALUE_8_BIT(7),
1090 &xx_ei.slippery_type, SLIPPERY_ANY_RANDOM,
1091 &yy_ei.slippery_type
1096 TYPE_INTEGER, CONF_VALUE_8_BIT(8),
1097 &xx_ei.explosion_type, EXPLODES_3X3,
1098 &yy_ei.explosion_type
1102 TYPE_INTEGER, CONF_VALUE_16_BIT(14),
1103 &xx_ei.explosion_delay, 16,
1104 &yy_ei.explosion_delay
1108 TYPE_INTEGER, CONF_VALUE_16_BIT(15),
1109 &xx_ei.ignition_delay, 8,
1110 &yy_ei.ignition_delay
1115 TYPE_CONTENT_LIST, CONF_VALUE_BYTES(2),
1116 &xx_ei.content, EL_EMPTY_SPACE,
1118 &xx_num_contents, 1, 1
1121 /* ---------- "num_change_pages" must be the last entry ------------------- */
1124 -1, SAVE_CONF_ALWAYS,
1125 TYPE_INTEGER, CONF_VALUE_8_BIT(9),
1126 &xx_ei.num_change_pages, 1,
1127 &yy_ei.num_change_pages
1138 static struct LevelFileConfigInfo chunk_config_CUSX_change[] =
1140 /* ---------- "current_change_page" must be the first entry --------------- */
1143 -1, SAVE_CONF_ALWAYS,
1144 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
1145 &xx_current_change_page, -1
1148 /* ---------- (the remaining entries can be in any order) ----------------- */
1152 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
1153 &xx_change.can_change, FALSE
1158 TYPE_BITFIELD, CONF_VALUE_32_BIT(1),
1159 &xx_event_bits[0], 0
1163 TYPE_BITFIELD, CONF_VALUE_32_BIT(2),
1164 &xx_event_bits[1], 0
1169 TYPE_BITFIELD, CONF_VALUE_8_BIT(3),
1170 &xx_change.trigger_player, CH_PLAYER_ANY
1174 TYPE_BITFIELD, CONF_VALUE_8_BIT(4),
1175 &xx_change.trigger_side, CH_SIDE_ANY
1179 TYPE_BITFIELD, CONF_VALUE_32_BIT(3),
1180 &xx_change.trigger_page, CH_PAGE_ANY
1185 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
1186 &xx_change.target_element, EL_EMPTY_SPACE
1191 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
1192 &xx_change.delay_fixed, 0
1196 TYPE_INTEGER, CONF_VALUE_16_BIT(3),
1197 &xx_change.delay_random, 0
1201 TYPE_INTEGER, CONF_VALUE_16_BIT(4),
1202 &xx_change.delay_frames, FRAMES_PER_SECOND
1207 TYPE_ELEMENT, CONF_VALUE_16_BIT(5),
1208 &xx_change.initial_trigger_element, EL_EMPTY_SPACE
1213 TYPE_BOOLEAN, CONF_VALUE_8_BIT(6),
1214 &xx_change.explode, FALSE
1218 TYPE_BOOLEAN, CONF_VALUE_8_BIT(7),
1219 &xx_change.use_target_content, FALSE
1223 TYPE_BOOLEAN, CONF_VALUE_8_BIT(8),
1224 &xx_change.only_if_complete, FALSE
1228 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
1229 &xx_change.use_random_replace, FALSE
1233 TYPE_INTEGER, CONF_VALUE_8_BIT(10),
1234 &xx_change.random_percentage, 100
1238 TYPE_INTEGER, CONF_VALUE_8_BIT(11),
1239 &xx_change.replace_when, CP_WHEN_EMPTY
1244 TYPE_BOOLEAN, CONF_VALUE_8_BIT(12),
1245 &xx_change.has_action, FALSE
1249 TYPE_INTEGER, CONF_VALUE_8_BIT(13),
1250 &xx_change.action_type, CA_NO_ACTION
1254 TYPE_INTEGER, CONF_VALUE_8_BIT(14),
1255 &xx_change.action_mode, CA_MODE_UNDEFINED
1259 TYPE_INTEGER, CONF_VALUE_16_BIT(6),
1260 &xx_change.action_arg, CA_ARG_UNDEFINED
1265 TYPE_ELEMENT, CONF_VALUE_16_BIT(7),
1266 &xx_change.action_element, EL_EMPTY_SPACE
1271 TYPE_CONTENT_LIST, CONF_VALUE_BYTES(1),
1272 &xx_change.target_content, EL_EMPTY_SPACE, NULL,
1273 &xx_num_contents, 1, 1
1283 static struct LevelFileConfigInfo chunk_config_GRPX[] =
1287 TYPE_STRING, CONF_VALUE_BYTES(1),
1288 &xx_ei.description[0], -1, NULL,
1289 &xx_string_length_unused, -1, MAX_ELEMENT_NAME_LEN,
1290 &xx_default_description[0]
1295 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1296 &xx_ei.use_gfx_element, FALSE
1300 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
1301 &xx_ei.gfx_element_initial, EL_EMPTY_SPACE
1306 TYPE_INTEGER, CONF_VALUE_8_BIT(2),
1307 &xx_group.choice_mode, ANIM_RANDOM
1312 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(2),
1313 &xx_group.element[0], EL_EMPTY_SPACE, NULL,
1314 &xx_group.num_elements, 1, MAX_ELEMENTS_IN_GROUP
1324 static struct LevelFileConfigInfo chunk_config_CONF[] = /* (OBSOLETE) */
1328 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
1329 &li.block_snap_field, TRUE
1333 TYPE_BOOLEAN, CONF_VALUE_8_BIT(13),
1334 &li.continuous_snapping, TRUE
1338 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
1339 &li.initial_player_stepsize[0], STEPSIZE_NORMAL
1343 TYPE_BOOLEAN, CONF_VALUE_8_BIT(10),
1344 &li.use_start_element[0], FALSE
1348 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
1349 &li.start_element[0], EL_PLAYER_1
1353 TYPE_BOOLEAN, CONF_VALUE_8_BIT(11),
1354 &li.use_artwork_element[0], FALSE
1358 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
1359 &li.artwork_element[0], EL_PLAYER_1
1363 TYPE_BOOLEAN, CONF_VALUE_8_BIT(12),
1364 &li.use_explosion_element[0], FALSE
1368 TYPE_ELEMENT, CONF_VALUE_16_BIT(3),
1369 &li.explosion_element[0], EL_PLAYER_1
1384 filetype_id_list[] =
1386 { LEVEL_FILE_TYPE_RND, "RND" },
1387 { LEVEL_FILE_TYPE_BD, "BD" },
1388 { LEVEL_FILE_TYPE_EM, "EM" },
1389 { LEVEL_FILE_TYPE_SP, "SP" },
1390 { LEVEL_FILE_TYPE_DX, "DX" },
1391 { LEVEL_FILE_TYPE_SB, "SB" },
1392 { LEVEL_FILE_TYPE_DC, "DC" },
1393 { LEVEL_FILE_TYPE_MM, "MM" },
1394 { LEVEL_FILE_TYPE_MM, "DF" },
1399 /* ========================================================================= */
1400 /* level file functions */
1401 /* ========================================================================= */
1403 static boolean check_special_flags(char *flag)
1405 if (strEqual(options.special_flags, flag) ||
1406 strEqual(leveldir_current->special_flags, flag))
1412 static struct DateInfo getCurrentDate(void)
1414 time_t epoch_seconds = time(NULL);
1415 struct tm *now = localtime(&epoch_seconds);
1416 struct DateInfo date;
1418 date.year = now->tm_year + 1900;
1419 date.month = now->tm_mon + 1;
1420 date.day = now->tm_mday;
1422 date.src = DATE_SRC_CLOCK;
1427 static void resetEventFlags(struct ElementChangeInfo *change)
1431 for (i = 0; i < NUM_CHANGE_EVENTS; i++)
1432 change->has_event[i] = FALSE;
1435 static void resetEventBits(void)
1439 for (i = 0; i < NUM_CE_BITFIELDS; i++)
1440 xx_event_bits[i] = 0;
1443 static void setEventFlagsFromEventBits(struct ElementChangeInfo *change)
1447 /* important: only change event flag if corresponding event bit is set
1448 (this is because all xx_event_bits[] values are loaded separately,
1449 and all xx_event_bits[] values are set back to zero before loading
1450 another value xx_event_bits[x] (each value representing 32 flags)) */
1452 for (i = 0; i < NUM_CHANGE_EVENTS; i++)
1453 if (xx_event_bits[CH_EVENT_BITFIELD_NR(i)] & CH_EVENT_BIT(i))
1454 change->has_event[i] = TRUE;
1457 static void setEventBitsFromEventFlags(struct ElementChangeInfo *change)
1461 /* in contrast to the above function setEventFlagsFromEventBits(), it
1462 would also be possible to set all bits in xx_event_bits[] to 0 or 1
1463 depending on the corresponding change->has_event[i] values here, as
1464 all xx_event_bits[] values are reset in resetEventBits() before */
1466 for (i = 0; i < NUM_CHANGE_EVENTS; i++)
1467 if (change->has_event[i])
1468 xx_event_bits[CH_EVENT_BITFIELD_NR(i)] |= CH_EVENT_BIT(i);
1471 static char *getDefaultElementDescription(struct ElementInfo *ei)
1473 static char description[MAX_ELEMENT_NAME_LEN + 1];
1474 char *default_description = (ei->custom_description != NULL ?
1475 ei->custom_description :
1476 ei->editor_description);
1479 /* always start with reliable default values */
1480 for (i = 0; i < MAX_ELEMENT_NAME_LEN + 1; i++)
1481 description[i] = '\0';
1483 /* truncate element description to MAX_ELEMENT_NAME_LEN bytes */
1484 strncpy(description, default_description, MAX_ELEMENT_NAME_LEN);
1486 return &description[0];
1489 static void setElementDescriptionToDefault(struct ElementInfo *ei)
1491 char *default_description = getDefaultElementDescription(ei);
1494 for (i = 0; i < MAX_ELEMENT_NAME_LEN + 1; i++)
1495 ei->description[i] = default_description[i];
1498 static void setConfigToDefaultsFromConfigList(struct LevelFileConfigInfo *conf)
1502 for (i = 0; conf[i].data_type != -1; i++)
1504 int default_value = conf[i].default_value;
1505 int data_type = conf[i].data_type;
1506 int conf_type = conf[i].conf_type;
1507 int byte_mask = conf_type & CONF_MASK_BYTES;
1509 if (byte_mask == CONF_MASK_MULTI_BYTES)
1511 int default_num_entities = conf[i].default_num_entities;
1512 int max_num_entities = conf[i].max_num_entities;
1514 *(int *)(conf[i].num_entities) = default_num_entities;
1516 if (data_type == TYPE_STRING)
1518 char *default_string = conf[i].default_string;
1519 char *string = (char *)(conf[i].value);
1521 strncpy(string, default_string, max_num_entities);
1523 else if (data_type == TYPE_ELEMENT_LIST)
1525 int *element_array = (int *)(conf[i].value);
1528 for (j = 0; j < max_num_entities; j++)
1529 element_array[j] = default_value;
1531 else if (data_type == TYPE_CONTENT_LIST)
1533 struct Content *content = (struct Content *)(conf[i].value);
1536 for (c = 0; c < max_num_entities; c++)
1537 for (y = 0; y < 3; y++)
1538 for (x = 0; x < 3; x++)
1539 content[c].e[x][y] = default_value;
1542 else /* constant size configuration data (1, 2 or 4 bytes) */
1544 if (data_type == TYPE_BOOLEAN)
1545 *(boolean *)(conf[i].value) = default_value;
1547 *(int *) (conf[i].value) = default_value;
1552 static void copyConfigFromConfigList(struct LevelFileConfigInfo *conf)
1556 for (i = 0; conf[i].data_type != -1; i++)
1558 int data_type = conf[i].data_type;
1559 int conf_type = conf[i].conf_type;
1560 int byte_mask = conf_type & CONF_MASK_BYTES;
1562 if (byte_mask == CONF_MASK_MULTI_BYTES)
1564 int max_num_entities = conf[i].max_num_entities;
1566 if (data_type == TYPE_STRING)
1568 char *string = (char *)(conf[i].value);
1569 char *string_copy = (char *)(conf[i].value_copy);
1571 strncpy(string_copy, string, max_num_entities);
1573 else if (data_type == TYPE_ELEMENT_LIST)
1575 int *element_array = (int *)(conf[i].value);
1576 int *element_array_copy = (int *)(conf[i].value_copy);
1579 for (j = 0; j < max_num_entities; j++)
1580 element_array_copy[j] = element_array[j];
1582 else if (data_type == TYPE_CONTENT_LIST)
1584 struct Content *content = (struct Content *)(conf[i].value);
1585 struct Content *content_copy = (struct Content *)(conf[i].value_copy);
1588 for (c = 0; c < max_num_entities; c++)
1589 for (y = 0; y < 3; y++)
1590 for (x = 0; x < 3; x++)
1591 content_copy[c].e[x][y] = content[c].e[x][y];
1594 else /* constant size configuration data (1, 2 or 4 bytes) */
1596 if (data_type == TYPE_BOOLEAN)
1597 *(boolean *)(conf[i].value_copy) = *(boolean *)(conf[i].value);
1599 *(int *) (conf[i].value_copy) = *(int *) (conf[i].value);
1604 void copyElementInfo(struct ElementInfo *ei_from, struct ElementInfo *ei_to)
1608 xx_ei = *ei_from; /* copy element data into temporary buffer */
1609 yy_ei = *ei_to; /* copy element data into temporary buffer */
1611 copyConfigFromConfigList(chunk_config_CUSX_base);
1616 /* ---------- reinitialize and copy change pages ---------- */
1618 ei_to->num_change_pages = ei_from->num_change_pages;
1619 ei_to->current_change_page = ei_from->current_change_page;
1621 setElementChangePages(ei_to, ei_to->num_change_pages);
1623 for (i = 0; i < ei_to->num_change_pages; i++)
1624 ei_to->change_page[i] = ei_from->change_page[i];
1626 /* ---------- copy group element info ---------- */
1627 if (ei_from->group != NULL && ei_to->group != NULL) /* group or internal */
1628 *ei_to->group = *ei_from->group;
1630 /* mark this custom element as modified */
1631 ei_to->modified_settings = TRUE;
1634 void setElementChangePages(struct ElementInfo *ei, int change_pages)
1636 int change_page_size = sizeof(struct ElementChangeInfo);
1638 ei->num_change_pages = MAX(1, change_pages);
1641 checked_realloc(ei->change_page, ei->num_change_pages * change_page_size);
1643 if (ei->current_change_page >= ei->num_change_pages)
1644 ei->current_change_page = ei->num_change_pages - 1;
1646 ei->change = &ei->change_page[ei->current_change_page];
1649 void setElementChangeInfoToDefaults(struct ElementChangeInfo *change)
1651 xx_change = *change; /* copy change data into temporary buffer */
1653 setConfigToDefaultsFromConfigList(chunk_config_CUSX_change);
1655 *change = xx_change;
1657 resetEventFlags(change);
1659 change->direct_action = 0;
1660 change->other_action = 0;
1662 change->pre_change_function = NULL;
1663 change->change_function = NULL;
1664 change->post_change_function = NULL;
1667 static void setLevelInfoToDefaults_Level(struct LevelInfo *level)
1671 li = *level; /* copy level data into temporary buffer */
1672 setConfigToDefaultsFromConfigList(chunk_config_INFO);
1673 *level = li; /* copy temporary buffer back to level data */
1675 setLevelInfoToDefaults_EM();
1676 setLevelInfoToDefaults_SP();
1677 setLevelInfoToDefaults_MM();
1679 level->native_em_level = &native_em_level;
1680 level->native_sp_level = &native_sp_level;
1681 level->native_mm_level = &native_mm_level;
1683 level->file_version = FILE_VERSION_ACTUAL;
1684 level->game_version = GAME_VERSION_ACTUAL;
1686 level->creation_date = getCurrentDate();
1688 level->encoding_16bit_field = TRUE;
1689 level->encoding_16bit_yamyam = TRUE;
1690 level->encoding_16bit_amoeba = TRUE;
1692 /* clear level name and level author string buffers */
1693 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
1694 level->name[i] = '\0';
1695 for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
1696 level->author[i] = '\0';
1698 /* set level name and level author to default values */
1699 strcpy(level->name, NAMELESS_LEVEL_NAME);
1700 strcpy(level->author, ANONYMOUS_NAME);
1702 /* set level playfield to playable default level with player and exit */
1703 for (x = 0; x < MAX_LEV_FIELDX; x++)
1704 for (y = 0; y < MAX_LEV_FIELDY; y++)
1705 level->field[x][y] = EL_SAND;
1707 level->field[0][0] = EL_PLAYER_1;
1708 level->field[STD_LEV_FIELDX - 1][STD_LEV_FIELDY - 1] = EL_EXIT_CLOSED;
1710 BorderElement = EL_STEELWALL;
1712 /* detect custom elements when loading them */
1713 level->file_has_custom_elements = FALSE;
1715 /* set all bug compatibility flags to "false" => do not emulate this bug */
1716 level->use_action_after_change_bug = FALSE;
1718 if (leveldir_current)
1720 /* try to determine better author name than 'anonymous' */
1721 if (!strEqual(leveldir_current->author, ANONYMOUS_NAME))
1723 strncpy(level->author, leveldir_current->author, MAX_LEVEL_AUTHOR_LEN);
1724 level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
1728 switch (LEVELCLASS(leveldir_current))
1730 case LEVELCLASS_TUTORIAL:
1731 strcpy(level->author, PROGRAM_AUTHOR_STRING);
1734 case LEVELCLASS_CONTRIB:
1735 strncpy(level->author, leveldir_current->name, MAX_LEVEL_AUTHOR_LEN);
1736 level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
1739 case LEVELCLASS_PRIVATE:
1740 strncpy(level->author, getRealName(), MAX_LEVEL_AUTHOR_LEN);
1741 level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
1745 /* keep default value */
1752 static void setLevelInfoToDefaults_Elements(struct LevelInfo *level)
1754 static boolean clipboard_elements_initialized = FALSE;
1757 InitElementPropertiesStatic();
1759 li = *level; /* copy level data into temporary buffer */
1760 setConfigToDefaultsFromConfigList(chunk_config_ELEM);
1761 *level = li; /* copy temporary buffer back to level data */
1763 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1766 struct ElementInfo *ei = &element_info[element];
1768 /* never initialize clipboard elements after the very first time */
1769 /* (to be able to use clipboard elements between several levels) */
1770 if (IS_CLIPBOARD_ELEMENT(element) && clipboard_elements_initialized)
1773 if (IS_ENVELOPE(element))
1775 int envelope_nr = element - EL_ENVELOPE_1;
1777 setConfigToDefaultsFromConfigList(chunk_config_NOTE);
1779 level->envelope[envelope_nr] = xx_envelope;
1782 if (IS_CUSTOM_ELEMENT(element) ||
1783 IS_GROUP_ELEMENT(element) ||
1784 IS_INTERNAL_ELEMENT(element))
1786 xx_ei = *ei; /* copy element data into temporary buffer */
1788 setConfigToDefaultsFromConfigList(chunk_config_CUSX_base);
1793 setElementChangePages(ei, 1);
1794 setElementChangeInfoToDefaults(ei->change);
1796 if (IS_CUSTOM_ELEMENT(element) ||
1797 IS_GROUP_ELEMENT(element) ||
1798 IS_INTERNAL_ELEMENT(element))
1800 setElementDescriptionToDefault(ei);
1802 ei->modified_settings = FALSE;
1805 if (IS_CUSTOM_ELEMENT(element) ||
1806 IS_INTERNAL_ELEMENT(element))
1808 /* internal values used in level editor */
1810 ei->access_type = 0;
1811 ei->access_layer = 0;
1812 ei->access_protected = 0;
1813 ei->walk_to_action = 0;
1814 ei->smash_targets = 0;
1817 ei->can_explode_by_fire = FALSE;
1818 ei->can_explode_smashed = FALSE;
1819 ei->can_explode_impact = FALSE;
1821 ei->current_change_page = 0;
1824 if (IS_GROUP_ELEMENT(element) ||
1825 IS_INTERNAL_ELEMENT(element))
1827 struct ElementGroupInfo *group;
1829 /* initialize memory for list of elements in group */
1830 if (ei->group == NULL)
1831 ei->group = checked_malloc(sizeof(struct ElementGroupInfo));
1835 xx_group = *group; /* copy group data into temporary buffer */
1837 setConfigToDefaultsFromConfigList(chunk_config_GRPX);
1843 clipboard_elements_initialized = TRUE;
1846 static void setLevelInfoToDefaults(struct LevelInfo *level,
1847 boolean level_info_only,
1848 boolean reset_file_status)
1850 setLevelInfoToDefaults_Level(level);
1852 if (!level_info_only)
1853 setLevelInfoToDefaults_Elements(level);
1855 if (reset_file_status)
1857 level->no_valid_file = FALSE;
1858 level->no_level_file = FALSE;
1861 level->changed = FALSE;
1864 static void setFileInfoToDefaults(struct LevelFileInfo *level_file_info)
1866 level_file_info->nr = 0;
1867 level_file_info->type = LEVEL_FILE_TYPE_UNKNOWN;
1868 level_file_info->packed = FALSE;
1870 setString(&level_file_info->basename, NULL);
1871 setString(&level_file_info->filename, NULL);
1874 int getMappedElement_SB(int, boolean);
1876 static void ActivateLevelTemplate(void)
1880 if (check_special_flags("load_xsb_to_ces"))
1882 /* fill smaller playfields with padding "beyond border wall" elements */
1883 if (level.fieldx < level_template.fieldx ||
1884 level.fieldy < level_template.fieldy)
1886 short field[level.fieldx][level.fieldy];
1887 int new_fieldx = MAX(level.fieldx, level_template.fieldx);
1888 int new_fieldy = MAX(level.fieldy, level_template.fieldy);
1889 int pos_fieldx = (new_fieldx - level.fieldx) / 2;
1890 int pos_fieldy = (new_fieldy - level.fieldy) / 2;
1892 /* copy old playfield (which is smaller than the visible area) */
1893 for (y = 0; y < level.fieldy; y++) for (x = 0; x < level.fieldx; x++)
1894 field[x][y] = level.field[x][y];
1896 /* fill new, larger playfield with "beyond border wall" elements */
1897 for (y = 0; y < new_fieldy; y++) for (x = 0; x < new_fieldx; x++)
1898 level.field[x][y] = getMappedElement_SB('_', TRUE);
1900 /* copy the old playfield to the middle of the new playfield */
1901 for (y = 0; y < level.fieldy; y++) for (x = 0; x < level.fieldx; x++)
1902 level.field[pos_fieldx + x][pos_fieldy + y] = field[x][y];
1904 level.fieldx = new_fieldx;
1905 level.fieldy = new_fieldy;
1909 /* Currently there is no special action needed to activate the template
1910 data, because 'element_info' property settings overwrite the original
1911 level data, while all other variables do not change. */
1913 /* Exception: 'from_level_template' elements in the original level playfield
1914 are overwritten with the corresponding elements at the same position in
1915 playfield from the level template. */
1917 for (x = 0; x < level.fieldx; x++)
1918 for (y = 0; y < level.fieldy; y++)
1919 if (level.field[x][y] == EL_FROM_LEVEL_TEMPLATE)
1920 level.field[x][y] = level_template.field[x][y];
1922 if (check_special_flags("load_xsb_to_ces"))
1924 struct LevelInfo level_backup = level;
1926 /* overwrite all individual level settings from template level settings */
1927 level = level_template;
1929 /* restore level file info */
1930 level.file_info = level_backup.file_info;
1932 /* restore playfield size */
1933 level.fieldx = level_backup.fieldx;
1934 level.fieldy = level_backup.fieldy;
1936 /* restore playfield content */
1937 for (x = 0; x < level.fieldx; x++)
1938 for (y = 0; y < level.fieldy; y++)
1939 level.field[x][y] = level_backup.field[x][y];
1941 /* restore name and author from individual level */
1942 strcpy(level.name, level_backup.name);
1943 strcpy(level.author, level_backup.author);
1945 /* restore flag "use_custom_template" */
1946 level.use_custom_template = level_backup.use_custom_template;
1950 static char *getLevelFilenameFromBasename(char *basename)
1952 static char *filename = NULL;
1954 checked_free(filename);
1956 filename = getPath2(getCurrentLevelDir(), basename);
1961 static int getFileTypeFromBasename(char *basename)
1963 /* !!! ALSO SEE COMMENT IN checkForPackageFromBasename() !!! */
1965 static char *filename = NULL;
1966 struct stat file_status;
1968 /* ---------- try to determine file type from filename ---------- */
1970 /* check for typical filename of a Supaplex level package file */
1971 if (strlen(basename) == 10 && strPrefixLower(basename, "levels.d"))
1972 return LEVEL_FILE_TYPE_SP;
1974 /* check for typical filename of a Diamond Caves II level package file */
1975 if (strSuffixLower(basename, ".dc") ||
1976 strSuffixLower(basename, ".dc2"))
1977 return LEVEL_FILE_TYPE_DC;
1979 /* check for typical filename of a Sokoban level package file */
1980 if (strSuffixLower(basename, ".xsb") &&
1981 strchr(basename, '%') == NULL)
1982 return LEVEL_FILE_TYPE_SB;
1984 /* ---------- try to determine file type from filesize ---------- */
1986 checked_free(filename);
1987 filename = getPath2(getCurrentLevelDir(), basename);
1989 if (stat(filename, &file_status) == 0)
1991 /* check for typical filesize of a Supaplex level package file */
1992 if (file_status.st_size == 170496)
1993 return LEVEL_FILE_TYPE_SP;
1996 return LEVEL_FILE_TYPE_UNKNOWN;
1999 static int getFileTypeFromMagicBytes(char *filename, int type)
2003 if ((file = openFile(filename, MODE_READ)))
2005 char chunk_name[CHUNK_ID_LEN + 1];
2007 getFileChunkBE(file, chunk_name, NULL);
2009 if (strEqual(chunk_name, "MMII") ||
2010 strEqual(chunk_name, "MIRR"))
2011 type = LEVEL_FILE_TYPE_MM;
2019 static boolean checkForPackageFromBasename(char *basename)
2021 /* !!! WON'T WORK ANYMORE IF getFileTypeFromBasename() ALSO DETECTS !!!
2022 !!! SINGLE LEVELS (CURRENTLY ONLY DETECTS LEVEL PACKAGES !!! */
2024 return (getFileTypeFromBasename(basename) != LEVEL_FILE_TYPE_UNKNOWN);
2027 static char *getSingleLevelBasenameExt(int nr, char *extension)
2029 static char basename[MAX_FILENAME_LEN];
2032 sprintf(basename, "%s", LEVELTEMPLATE_FILENAME);
2034 sprintf(basename, "%03d.%s", nr, extension);
2039 static char *getSingleLevelBasename(int nr)
2041 return getSingleLevelBasenameExt(nr, LEVELFILE_EXTENSION);
2044 static char *getPackedLevelBasename(int type)
2046 static char basename[MAX_FILENAME_LEN];
2047 char *directory = getCurrentLevelDir();
2049 DirectoryEntry *dir_entry;
2051 strcpy(basename, UNDEFINED_FILENAME); /* default: undefined file */
2053 if ((dir = openDirectory(directory)) == NULL)
2055 Error(ERR_WARN, "cannot read current level directory '%s'", directory);
2060 while ((dir_entry = readDirectory(dir)) != NULL) /* loop all entries */
2062 char *entry_basename = dir_entry->basename;
2063 int entry_type = getFileTypeFromBasename(entry_basename);
2065 if (entry_type != LEVEL_FILE_TYPE_UNKNOWN) /* found valid level package */
2067 if (type == LEVEL_FILE_TYPE_UNKNOWN ||
2070 strcpy(basename, entry_basename);
2077 closeDirectory(dir);
2082 static char *getSingleLevelFilename(int nr)
2084 return getLevelFilenameFromBasename(getSingleLevelBasename(nr));
2087 #if ENABLE_UNUSED_CODE
2088 static char *getPackedLevelFilename(int type)
2090 return getLevelFilenameFromBasename(getPackedLevelBasename(type));
2094 char *getDefaultLevelFilename(int nr)
2096 return getSingleLevelFilename(nr);
2099 #if ENABLE_UNUSED_CODE
2100 static void setLevelFileInfo_SingleLevelFilename(struct LevelFileInfo *lfi,
2104 lfi->packed = FALSE;
2106 setString(&lfi->basename, getSingleLevelBasename(lfi->nr, lfi->type));
2107 setString(&lfi->filename, getLevelFilenameFromBasename(lfi->basename));
2111 static void setLevelFileInfo_FormatLevelFilename(struct LevelFileInfo *lfi,
2112 int type, char *format, ...)
2114 static char basename[MAX_FILENAME_LEN];
2117 va_start(ap, format);
2118 vsprintf(basename, format, ap);
2122 lfi->packed = FALSE;
2124 setString(&lfi->basename, basename);
2125 setString(&lfi->filename, getLevelFilenameFromBasename(lfi->basename));
2128 static void setLevelFileInfo_PackedLevelFilename(struct LevelFileInfo *lfi,
2134 setString(&lfi->basename, getPackedLevelBasename(lfi->type));
2135 setString(&lfi->filename, getLevelFilenameFromBasename(lfi->basename));
2138 static int getFiletypeFromID(char *filetype_id)
2140 char *filetype_id_lower;
2141 int filetype = LEVEL_FILE_TYPE_UNKNOWN;
2144 if (filetype_id == NULL)
2145 return LEVEL_FILE_TYPE_UNKNOWN;
2147 filetype_id_lower = getStringToLower(filetype_id);
2149 for (i = 0; filetype_id_list[i].id != NULL; i++)
2151 char *id_lower = getStringToLower(filetype_id_list[i].id);
2153 if (strEqual(filetype_id_lower, id_lower))
2154 filetype = filetype_id_list[i].filetype;
2158 if (filetype != LEVEL_FILE_TYPE_UNKNOWN)
2162 free(filetype_id_lower);
2167 char *getLocalLevelTemplateFilename(void)
2169 return getDefaultLevelFilename(-1);
2172 char *getGlobalLevelTemplateFilename(void)
2174 /* global variable "leveldir_current" must be modified in the loop below */
2175 LevelDirTree *leveldir_current_last = leveldir_current;
2176 char *filename = NULL;
2178 /* check for template level in path from current to topmost tree node */
2180 while (leveldir_current != NULL)
2182 filename = getDefaultLevelFilename(-1);
2184 if (fileExists(filename))
2187 leveldir_current = leveldir_current->node_parent;
2190 /* restore global variable "leveldir_current" modified in above loop */
2191 leveldir_current = leveldir_current_last;
2196 static void determineLevelFileInfo_Filename(struct LevelFileInfo *lfi)
2200 /* special case: level number is negative => check for level template file */
2203 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
2204 getSingleLevelBasename(-1));
2206 /* replace local level template filename with global template filename */
2207 setString(&lfi->filename, getGlobalLevelTemplateFilename());
2209 /* no fallback if template file not existing */
2213 /* special case: check for file name/pattern specified in "levelinfo.conf" */
2214 if (leveldir_current->level_filename != NULL)
2216 int filetype = getFiletypeFromID(leveldir_current->level_filetype);
2218 setLevelFileInfo_FormatLevelFilename(lfi, filetype,
2219 leveldir_current->level_filename, nr);
2221 lfi->packed = checkForPackageFromBasename(leveldir_current->level_filename);
2223 if (fileExists(lfi->filename))
2226 else if (leveldir_current->level_filetype != NULL)
2228 int filetype = getFiletypeFromID(leveldir_current->level_filetype);
2230 /* check for specified native level file with standard file name */
2231 setLevelFileInfo_FormatLevelFilename(lfi, filetype,
2232 "%03d.%s", nr, LEVELFILE_EXTENSION);
2233 if (fileExists(lfi->filename))
2237 /* check for native Rocks'n'Diamonds level file */
2238 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
2239 "%03d.%s", nr, LEVELFILE_EXTENSION);
2240 if (fileExists(lfi->filename))
2243 /* check for Emerald Mine level file (V1) */
2244 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "a%c%c",
2245 'a' + (nr / 10) % 26, '0' + nr % 10);
2246 if (fileExists(lfi->filename))
2248 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "A%c%c",
2249 'A' + (nr / 10) % 26, '0' + nr % 10);
2250 if (fileExists(lfi->filename))
2253 /* check for Emerald Mine level file (V2 to V5) */
2254 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%d", nr);
2255 if (fileExists(lfi->filename))
2258 /* check for Emerald Mine level file (V6 / single mode) */
2259 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02ds", nr);
2260 if (fileExists(lfi->filename))
2262 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02dS", nr);
2263 if (fileExists(lfi->filename))
2266 /* check for Emerald Mine level file (V6 / teamwork mode) */
2267 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02dt", nr);
2268 if (fileExists(lfi->filename))
2270 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02dT", nr);
2271 if (fileExists(lfi->filename))
2274 /* check for various packed level file formats */
2275 setLevelFileInfo_PackedLevelFilename(lfi, LEVEL_FILE_TYPE_UNKNOWN);
2276 if (fileExists(lfi->filename))
2279 /* no known level file found -- use default values (and fail later) */
2280 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
2281 "%03d.%s", nr, LEVELFILE_EXTENSION);
2284 static void determineLevelFileInfo_Filetype(struct LevelFileInfo *lfi)
2286 if (lfi->type == LEVEL_FILE_TYPE_UNKNOWN)
2287 lfi->type = getFileTypeFromBasename(lfi->basename);
2289 if (lfi->type == LEVEL_FILE_TYPE_RND)
2290 lfi->type = getFileTypeFromMagicBytes(lfi->filename, lfi->type);
2293 static void setLevelFileInfo(struct LevelFileInfo *level_file_info, int nr)
2295 /* always start with reliable default values */
2296 setFileInfoToDefaults(level_file_info);
2298 level_file_info->nr = nr; /* set requested level number */
2300 determineLevelFileInfo_Filename(level_file_info);
2301 determineLevelFileInfo_Filetype(level_file_info);
2304 static void copyLevelFileInfo(struct LevelFileInfo *lfi_from,
2305 struct LevelFileInfo *lfi_to)
2307 lfi_to->nr = lfi_from->nr;
2308 lfi_to->type = lfi_from->type;
2309 lfi_to->packed = lfi_from->packed;
2311 setString(&lfi_to->basename, lfi_from->basename);
2312 setString(&lfi_to->filename, lfi_from->filename);
2315 /* ------------------------------------------------------------------------- */
2316 /* functions for loading R'n'D level */
2317 /* ------------------------------------------------------------------------- */
2319 static int getMappedElement(int element)
2321 /* remap some (historic, now obsolete) elements */
2325 case EL_PLAYER_OBSOLETE:
2326 element = EL_PLAYER_1;
2329 case EL_KEY_OBSOLETE:
2333 case EL_EM_KEY_1_FILE_OBSOLETE:
2334 element = EL_EM_KEY_1;
2337 case EL_EM_KEY_2_FILE_OBSOLETE:
2338 element = EL_EM_KEY_2;
2341 case EL_EM_KEY_3_FILE_OBSOLETE:
2342 element = EL_EM_KEY_3;
2345 case EL_EM_KEY_4_FILE_OBSOLETE:
2346 element = EL_EM_KEY_4;
2349 case EL_ENVELOPE_OBSOLETE:
2350 element = EL_ENVELOPE_1;
2358 if (element >= NUM_FILE_ELEMENTS)
2360 Error(ERR_WARN, "invalid level element %d", element);
2362 element = EL_UNKNOWN;
2370 static int getMappedElementByVersion(int element, int game_version)
2372 /* remap some elements due to certain game version */
2374 if (game_version <= VERSION_IDENT(2,2,0,0))
2376 /* map game font elements */
2377 element = (element == EL_CHAR('[') ? EL_CHAR_AUMLAUT :
2378 element == EL_CHAR('\\') ? EL_CHAR_OUMLAUT :
2379 element == EL_CHAR(']') ? EL_CHAR_UUMLAUT :
2380 element == EL_CHAR('^') ? EL_CHAR_COPYRIGHT : element);
2383 if (game_version < VERSION_IDENT(3,0,0,0))
2385 /* map Supaplex gravity tube elements */
2386 element = (element == EL_SP_GRAVITY_PORT_LEFT ? EL_SP_PORT_LEFT :
2387 element == EL_SP_GRAVITY_PORT_RIGHT ? EL_SP_PORT_RIGHT :
2388 element == EL_SP_GRAVITY_PORT_UP ? EL_SP_PORT_UP :
2389 element == EL_SP_GRAVITY_PORT_DOWN ? EL_SP_PORT_DOWN :
2396 static int LoadLevel_VERS(File *file, int chunk_size, struct LevelInfo *level)
2398 level->file_version = getFileVersion(file);
2399 level->game_version = getFileVersion(file);
2404 static int LoadLevel_DATE(File *file, int chunk_size, struct LevelInfo *level)
2406 level->creation_date.year = getFile16BitBE(file);
2407 level->creation_date.month = getFile8Bit(file);
2408 level->creation_date.day = getFile8Bit(file);
2410 level->creation_date.src = DATE_SRC_LEVELFILE;
2415 static int LoadLevel_HEAD(File *file, int chunk_size, struct LevelInfo *level)
2417 int initial_player_stepsize;
2418 int initial_player_gravity;
2421 level->fieldx = getFile8Bit(file);
2422 level->fieldy = getFile8Bit(file);
2424 level->time = getFile16BitBE(file);
2425 level->gems_needed = getFile16BitBE(file);
2427 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
2428 level->name[i] = getFile8Bit(file);
2429 level->name[MAX_LEVEL_NAME_LEN] = 0;
2431 for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
2432 level->score[i] = getFile8Bit(file);
2434 level->num_yamyam_contents = STD_ELEMENT_CONTENTS;
2435 for (i = 0; i < STD_ELEMENT_CONTENTS; i++)
2436 for (y = 0; y < 3; y++)
2437 for (x = 0; x < 3; x++)
2438 level->yamyam_content[i].e[x][y] = getMappedElement(getFile8Bit(file));
2440 level->amoeba_speed = getFile8Bit(file);
2441 level->time_magic_wall = getFile8Bit(file);
2442 level->time_wheel = getFile8Bit(file);
2443 level->amoeba_content = getMappedElement(getFile8Bit(file));
2445 initial_player_stepsize = (getFile8Bit(file) == 1 ? STEPSIZE_FAST :
2448 for (i = 0; i < MAX_PLAYERS; i++)
2449 level->initial_player_stepsize[i] = initial_player_stepsize;
2451 initial_player_gravity = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2453 for (i = 0; i < MAX_PLAYERS; i++)
2454 level->initial_player_gravity[i] = initial_player_gravity;
2456 level->encoding_16bit_field = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2457 level->em_slippery_gems = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2459 level->use_custom_template = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2461 level->block_last_field = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2462 level->sp_block_last_field = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2463 level->can_move_into_acid_bits = getFile32BitBE(file);
2464 level->dont_collide_with_bits = getFile8Bit(file);
2466 level->use_spring_bug = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2467 level->use_step_counter = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2469 level->instant_relocation = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2470 level->can_pass_to_walkable = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2471 level->grow_into_diggable = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2473 level->game_engine_type = getFile8Bit(file);
2475 ReadUnusedBytesFromFile(file, LEVEL_CHUNK_HEAD_UNUSED);
2480 static int LoadLevel_NAME(File *file, int chunk_size, struct LevelInfo *level)
2484 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
2485 level->name[i] = getFile8Bit(file);
2486 level->name[MAX_LEVEL_NAME_LEN] = 0;
2491 static int LoadLevel_AUTH(File *file, int chunk_size, struct LevelInfo *level)
2495 for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
2496 level->author[i] = getFile8Bit(file);
2497 level->author[MAX_LEVEL_AUTHOR_LEN] = 0;
2502 static int LoadLevel_BODY(File *file, int chunk_size, struct LevelInfo *level)
2505 int chunk_size_expected = level->fieldx * level->fieldy;
2507 /* Note: "chunk_size" was wrong before version 2.0 when elements are
2508 stored with 16-bit encoding (and should be twice as big then).
2509 Even worse, playfield data was stored 16-bit when only yamyam content
2510 contained 16-bit elements and vice versa. */
2512 if (level->encoding_16bit_field && level->file_version >= FILE_VERSION_2_0)
2513 chunk_size_expected *= 2;
2515 if (chunk_size_expected != chunk_size)
2517 ReadUnusedBytesFromFile(file, chunk_size);
2518 return chunk_size_expected;
2521 for (y = 0; y < level->fieldy; y++)
2522 for (x = 0; x < level->fieldx; x++)
2523 level->field[x][y] =
2524 getMappedElement(level->encoding_16bit_field ? getFile16BitBE(file) :
2529 static int LoadLevel_CONT(File *file, int chunk_size, struct LevelInfo *level)
2532 int header_size = 4;
2533 int content_size = MAX_ELEMENT_CONTENTS * 3 * 3;
2534 int chunk_size_expected = header_size + content_size;
2536 /* Note: "chunk_size" was wrong before version 2.0 when elements are
2537 stored with 16-bit encoding (and should be twice as big then).
2538 Even worse, playfield data was stored 16-bit when only yamyam content
2539 contained 16-bit elements and vice versa. */
2541 if (level->encoding_16bit_field && level->file_version >= FILE_VERSION_2_0)
2542 chunk_size_expected += content_size;
2544 if (chunk_size_expected != chunk_size)
2546 ReadUnusedBytesFromFile(file, chunk_size);
2547 return chunk_size_expected;
2551 level->num_yamyam_contents = getFile8Bit(file);
2555 /* correct invalid number of content fields -- should never happen */
2556 if (level->num_yamyam_contents < 1 ||
2557 level->num_yamyam_contents > MAX_ELEMENT_CONTENTS)
2558 level->num_yamyam_contents = STD_ELEMENT_CONTENTS;
2560 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
2561 for (y = 0; y < 3; y++)
2562 for (x = 0; x < 3; x++)
2563 level->yamyam_content[i].e[x][y] =
2564 getMappedElement(level->encoding_16bit_field ?
2565 getFile16BitBE(file) : getFile8Bit(file));
2569 static int LoadLevel_CNT2(File *file, int chunk_size, struct LevelInfo *level)
2574 int content_array[MAX_ELEMENT_CONTENTS][3][3];
2576 element = getMappedElement(getFile16BitBE(file));
2577 num_contents = getFile8Bit(file);
2579 getFile8Bit(file); /* content x size (unused) */
2580 getFile8Bit(file); /* content y size (unused) */
2582 ReadUnusedBytesFromFile(file, LEVEL_CHUNK_CNT2_UNUSED);
2584 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
2585 for (y = 0; y < 3; y++)
2586 for (x = 0; x < 3; x++)
2587 content_array[i][x][y] = getMappedElement(getFile16BitBE(file));
2589 /* correct invalid number of content fields -- should never happen */
2590 if (num_contents < 1 || num_contents > MAX_ELEMENT_CONTENTS)
2591 num_contents = STD_ELEMENT_CONTENTS;
2593 if (element == EL_YAMYAM)
2595 level->num_yamyam_contents = num_contents;
2597 for (i = 0; i < num_contents; i++)
2598 for (y = 0; y < 3; y++)
2599 for (x = 0; x < 3; x++)
2600 level->yamyam_content[i].e[x][y] = content_array[i][x][y];
2602 else if (element == EL_BD_AMOEBA)
2604 level->amoeba_content = content_array[0][0][0];
2608 Error(ERR_WARN, "cannot load content for element '%d'", element);
2614 static int LoadLevel_CNT3(File *file, int chunk_size, struct LevelInfo *level)
2620 int chunk_size_expected;
2622 element = getMappedElement(getFile16BitBE(file));
2623 if (!IS_ENVELOPE(element))
2624 element = EL_ENVELOPE_1;
2626 envelope_nr = element - EL_ENVELOPE_1;
2628 envelope_len = getFile16BitBE(file);
2630 level->envelope[envelope_nr].xsize = getFile8Bit(file);
2631 level->envelope[envelope_nr].ysize = getFile8Bit(file);
2633 ReadUnusedBytesFromFile(file, LEVEL_CHUNK_CNT3_UNUSED);
2635 chunk_size_expected = LEVEL_CHUNK_CNT3_SIZE(envelope_len);
2636 if (chunk_size_expected != chunk_size)
2638 ReadUnusedBytesFromFile(file, chunk_size - LEVEL_CHUNK_CNT3_HEADER);
2639 return chunk_size_expected;
2642 for (i = 0; i < envelope_len; i++)
2643 level->envelope[envelope_nr].text[i] = getFile8Bit(file);
2648 static int LoadLevel_CUS1(File *file, int chunk_size, struct LevelInfo *level)
2650 int num_changed_custom_elements = getFile16BitBE(file);
2651 int chunk_size_expected = 2 + num_changed_custom_elements * 6;
2654 if (chunk_size_expected != chunk_size)
2656 ReadUnusedBytesFromFile(file, chunk_size - 2);
2657 return chunk_size_expected;
2660 for (i = 0; i < num_changed_custom_elements; i++)
2662 int element = getMappedElement(getFile16BitBE(file));
2663 int properties = getFile32BitBE(file);
2665 if (IS_CUSTOM_ELEMENT(element))
2666 element_info[element].properties[EP_BITFIELD_BASE_NR] = properties;
2668 Error(ERR_WARN, "invalid custom element number %d", element);
2670 /* older game versions that wrote level files with CUS1 chunks used
2671 different default push delay values (not yet stored in level file) */
2672 element_info[element].push_delay_fixed = 2;
2673 element_info[element].push_delay_random = 8;
2676 level->file_has_custom_elements = TRUE;
2681 static int LoadLevel_CUS2(File *file, int chunk_size, struct LevelInfo *level)
2683 int num_changed_custom_elements = getFile16BitBE(file);
2684 int chunk_size_expected = 2 + num_changed_custom_elements * 4;
2687 if (chunk_size_expected != chunk_size)
2689 ReadUnusedBytesFromFile(file, chunk_size - 2);
2690 return chunk_size_expected;
2693 for (i = 0; i < num_changed_custom_elements; i++)
2695 int element = getMappedElement(getFile16BitBE(file));
2696 int custom_target_element = getMappedElement(getFile16BitBE(file));
2698 if (IS_CUSTOM_ELEMENT(element))
2699 element_info[element].change->target_element = custom_target_element;
2701 Error(ERR_WARN, "invalid custom element number %d", element);
2704 level->file_has_custom_elements = TRUE;
2709 static int LoadLevel_CUS3(File *file, int chunk_size, struct LevelInfo *level)
2711 int num_changed_custom_elements = getFile16BitBE(file);
2712 int chunk_size_expected = LEVEL_CHUNK_CUS3_SIZE(num_changed_custom_elements);
2715 if (chunk_size_expected != chunk_size)
2717 ReadUnusedBytesFromFile(file, chunk_size - 2);
2718 return chunk_size_expected;
2721 for (i = 0; i < num_changed_custom_elements; i++)
2723 int element = getMappedElement(getFile16BitBE(file));
2724 struct ElementInfo *ei = &element_info[element];
2725 unsigned int event_bits;
2727 if (!IS_CUSTOM_ELEMENT(element))
2729 Error(ERR_WARN, "invalid custom element number %d", element);
2731 element = EL_INTERNAL_DUMMY;
2734 for (j = 0; j < MAX_ELEMENT_NAME_LEN; j++)
2735 ei->description[j] = getFile8Bit(file);
2736 ei->description[MAX_ELEMENT_NAME_LEN] = 0;
2738 ei->properties[EP_BITFIELD_BASE_NR] = getFile32BitBE(file);
2740 /* some free bytes for future properties and padding */
2741 ReadUnusedBytesFromFile(file, 7);
2743 ei->use_gfx_element = getFile8Bit(file);
2744 ei->gfx_element_initial = getMappedElement(getFile16BitBE(file));
2746 ei->collect_score_initial = getFile8Bit(file);
2747 ei->collect_count_initial = getFile8Bit(file);
2749 ei->push_delay_fixed = getFile16BitBE(file);
2750 ei->push_delay_random = getFile16BitBE(file);
2751 ei->move_delay_fixed = getFile16BitBE(file);
2752 ei->move_delay_random = getFile16BitBE(file);
2754 ei->move_pattern = getFile16BitBE(file);
2755 ei->move_direction_initial = getFile8Bit(file);
2756 ei->move_stepsize = getFile8Bit(file);
2758 for (y = 0; y < 3; y++)
2759 for (x = 0; x < 3; x++)
2760 ei->content.e[x][y] = getMappedElement(getFile16BitBE(file));
2762 event_bits = getFile32BitBE(file);
2763 for (j = 0; j < NUM_CHANGE_EVENTS; j++)
2764 if (event_bits & (1 << j))
2765 ei->change->has_event[j] = TRUE;
2767 ei->change->target_element = getMappedElement(getFile16BitBE(file));
2769 ei->change->delay_fixed = getFile16BitBE(file);
2770 ei->change->delay_random = getFile16BitBE(file);
2771 ei->change->delay_frames = getFile16BitBE(file);
2773 ei->change->initial_trigger_element= getMappedElement(getFile16BitBE(file));
2775 ei->change->explode = getFile8Bit(file);
2776 ei->change->use_target_content = getFile8Bit(file);
2777 ei->change->only_if_complete = getFile8Bit(file);
2778 ei->change->use_random_replace = getFile8Bit(file);
2780 ei->change->random_percentage = getFile8Bit(file);
2781 ei->change->replace_when = getFile8Bit(file);
2783 for (y = 0; y < 3; y++)
2784 for (x = 0; x < 3; x++)
2785 ei->change->target_content.e[x][y] =
2786 getMappedElement(getFile16BitBE(file));
2788 ei->slippery_type = getFile8Bit(file);
2790 /* some free bytes for future properties and padding */
2791 ReadUnusedBytesFromFile(file, LEVEL_CPART_CUS3_UNUSED);
2793 /* mark that this custom element has been modified */
2794 ei->modified_settings = TRUE;
2797 level->file_has_custom_elements = TRUE;
2802 static int LoadLevel_CUS4(File *file, int chunk_size, struct LevelInfo *level)
2804 struct ElementInfo *ei;
2805 int chunk_size_expected;
2809 /* ---------- custom element base property values (96 bytes) ------------- */
2811 element = getMappedElement(getFile16BitBE(file));
2813 if (!IS_CUSTOM_ELEMENT(element))
2815 Error(ERR_WARN, "invalid custom element number %d", element);
2817 ReadUnusedBytesFromFile(file, chunk_size - 2);
2821 ei = &element_info[element];
2823 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
2824 ei->description[i] = getFile8Bit(file);
2825 ei->description[MAX_ELEMENT_NAME_LEN] = 0;
2827 ei->properties[EP_BITFIELD_BASE_NR] = getFile32BitBE(file);
2829 ReadUnusedBytesFromFile(file, 4); /* reserved for more base properties */
2831 ei->num_change_pages = getFile8Bit(file);
2833 chunk_size_expected = LEVEL_CHUNK_CUS4_SIZE(ei->num_change_pages);
2834 if (chunk_size_expected != chunk_size)
2836 ReadUnusedBytesFromFile(file, chunk_size - 43);
2837 return chunk_size_expected;
2840 ei->ce_value_fixed_initial = getFile16BitBE(file);
2841 ei->ce_value_random_initial = getFile16BitBE(file);
2842 ei->use_last_ce_value = getFile8Bit(file);
2844 ei->use_gfx_element = getFile8Bit(file);
2845 ei->gfx_element_initial = getMappedElement(getFile16BitBE(file));
2847 ei->collect_score_initial = getFile8Bit(file);
2848 ei->collect_count_initial = getFile8Bit(file);
2850 ei->drop_delay_fixed = getFile8Bit(file);
2851 ei->push_delay_fixed = getFile8Bit(file);
2852 ei->drop_delay_random = getFile8Bit(file);
2853 ei->push_delay_random = getFile8Bit(file);
2854 ei->move_delay_fixed = getFile16BitBE(file);
2855 ei->move_delay_random = getFile16BitBE(file);
2857 /* bits 0 - 15 of "move_pattern" ... */
2858 ei->move_pattern = getFile16BitBE(file);
2859 ei->move_direction_initial = getFile8Bit(file);
2860 ei->move_stepsize = getFile8Bit(file);
2862 ei->slippery_type = getFile8Bit(file);
2864 for (y = 0; y < 3; y++)
2865 for (x = 0; x < 3; x++)
2866 ei->content.e[x][y] = getMappedElement(getFile16BitBE(file));
2868 ei->move_enter_element = getMappedElement(getFile16BitBE(file));
2869 ei->move_leave_element = getMappedElement(getFile16BitBE(file));
2870 ei->move_leave_type = getFile8Bit(file);
2872 /* ... bits 16 - 31 of "move_pattern" (not nice, but downward compatible) */
2873 ei->move_pattern |= (getFile16BitBE(file) << 16);
2875 ei->access_direction = getFile8Bit(file);
2877 ei->explosion_delay = getFile8Bit(file);
2878 ei->ignition_delay = getFile8Bit(file);
2879 ei->explosion_type = getFile8Bit(file);
2881 /* some free bytes for future custom property values and padding */
2882 ReadUnusedBytesFromFile(file, 1);
2884 /* ---------- change page property values (48 bytes) --------------------- */
2886 setElementChangePages(ei, ei->num_change_pages);
2888 for (i = 0; i < ei->num_change_pages; i++)
2890 struct ElementChangeInfo *change = &ei->change_page[i];
2891 unsigned int event_bits;
2893 /* always start with reliable default values */
2894 setElementChangeInfoToDefaults(change);
2896 /* bits 0 - 31 of "has_event[]" ... */
2897 event_bits = getFile32BitBE(file);
2898 for (j = 0; j < MIN(NUM_CHANGE_EVENTS, 32); j++)
2899 if (event_bits & (1 << j))
2900 change->has_event[j] = TRUE;
2902 change->target_element = getMappedElement(getFile16BitBE(file));
2904 change->delay_fixed = getFile16BitBE(file);
2905 change->delay_random = getFile16BitBE(file);
2906 change->delay_frames = getFile16BitBE(file);
2908 change->initial_trigger_element = getMappedElement(getFile16BitBE(file));
2910 change->explode = getFile8Bit(file);
2911 change->use_target_content = getFile8Bit(file);
2912 change->only_if_complete = getFile8Bit(file);
2913 change->use_random_replace = getFile8Bit(file);
2915 change->random_percentage = getFile8Bit(file);
2916 change->replace_when = getFile8Bit(file);
2918 for (y = 0; y < 3; y++)
2919 for (x = 0; x < 3; x++)
2920 change->target_content.e[x][y]= getMappedElement(getFile16BitBE(file));
2922 change->can_change = getFile8Bit(file);
2924 change->trigger_side = getFile8Bit(file);
2926 change->trigger_player = getFile8Bit(file);
2927 change->trigger_page = getFile8Bit(file);
2929 change->trigger_page = (change->trigger_page == CH_PAGE_ANY_FILE ?
2930 CH_PAGE_ANY : (1 << change->trigger_page));
2932 change->has_action = getFile8Bit(file);
2933 change->action_type = getFile8Bit(file);
2934 change->action_mode = getFile8Bit(file);
2935 change->action_arg = getFile16BitBE(file);
2937 /* ... bits 32 - 39 of "has_event[]" (not nice, but downward compatible) */
2938 event_bits = getFile8Bit(file);
2939 for (j = 32; j < NUM_CHANGE_EVENTS; j++)
2940 if (event_bits & (1 << (j - 32)))
2941 change->has_event[j] = TRUE;
2944 /* mark this custom element as modified */
2945 ei->modified_settings = TRUE;
2947 level->file_has_custom_elements = TRUE;
2952 static int LoadLevel_GRP1(File *file, int chunk_size, struct LevelInfo *level)
2954 struct ElementInfo *ei;
2955 struct ElementGroupInfo *group;
2959 element = getMappedElement(getFile16BitBE(file));
2961 if (!IS_GROUP_ELEMENT(element))
2963 Error(ERR_WARN, "invalid group element number %d", element);
2965 ReadUnusedBytesFromFile(file, chunk_size - 2);
2969 ei = &element_info[element];
2971 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
2972 ei->description[i] = getFile8Bit(file);
2973 ei->description[MAX_ELEMENT_NAME_LEN] = 0;
2975 group = element_info[element].group;
2977 group->num_elements = getFile8Bit(file);
2979 ei->use_gfx_element = getFile8Bit(file);
2980 ei->gfx_element_initial = getMappedElement(getFile16BitBE(file));
2982 group->choice_mode = getFile8Bit(file);
2984 /* some free bytes for future values and padding */
2985 ReadUnusedBytesFromFile(file, 3);
2987 for (i = 0; i < MAX_ELEMENTS_IN_GROUP; i++)
2988 group->element[i] = getMappedElement(getFile16BitBE(file));
2990 /* mark this group element as modified */
2991 element_info[element].modified_settings = TRUE;
2993 level->file_has_custom_elements = TRUE;
2998 static int LoadLevel_MicroChunk(File *file, struct LevelFileConfigInfo *conf,
2999 int element, int real_element)
3001 int micro_chunk_size = 0;
3002 int conf_type = getFile8Bit(file);
3003 int byte_mask = conf_type & CONF_MASK_BYTES;
3004 boolean element_found = FALSE;
3007 micro_chunk_size += 1;
3009 if (byte_mask == CONF_MASK_MULTI_BYTES)
3011 int num_bytes = getFile16BitBE(file);
3012 byte *buffer = checked_malloc(num_bytes);
3014 ReadBytesFromFile(file, buffer, num_bytes);
3016 for (i = 0; conf[i].data_type != -1; i++)
3018 if (conf[i].element == element &&
3019 conf[i].conf_type == conf_type)
3021 int data_type = conf[i].data_type;
3022 int num_entities = num_bytes / CONF_ENTITY_NUM_BYTES(data_type);
3023 int max_num_entities = conf[i].max_num_entities;
3025 if (num_entities > max_num_entities)
3028 "truncating number of entities for element %d from %d to %d",
3029 element, num_entities, max_num_entities);
3031 num_entities = max_num_entities;
3034 if (num_entities == 0 && (data_type == TYPE_ELEMENT_LIST ||
3035 data_type == TYPE_CONTENT_LIST))
3037 /* for element and content lists, zero entities are not allowed */
3038 Error(ERR_WARN, "found empty list of entities for element %d",
3041 /* do not set "num_entities" here to prevent reading behind buffer */
3043 *(int *)(conf[i].num_entities) = 1; /* at least one is required */
3047 *(int *)(conf[i].num_entities) = num_entities;
3050 element_found = TRUE;
3052 if (data_type == TYPE_STRING)
3054 char *string = (char *)(conf[i].value);
3057 for (j = 0; j < max_num_entities; j++)
3058 string[j] = (j < num_entities ? buffer[j] : '\0');
3060 else if (data_type == TYPE_ELEMENT_LIST)
3062 int *element_array = (int *)(conf[i].value);
3065 for (j = 0; j < num_entities; j++)
3067 getMappedElement(CONF_ELEMENTS_ELEMENT(buffer, j));
3069 else if (data_type == TYPE_CONTENT_LIST)
3071 struct Content *content= (struct Content *)(conf[i].value);
3074 for (c = 0; c < num_entities; c++)
3075 for (y = 0; y < 3; y++)
3076 for (x = 0; x < 3; x++)
3077 content[c].e[x][y] =
3078 getMappedElement(CONF_CONTENTS_ELEMENT(buffer, c, x, y));
3081 element_found = FALSE;
3087 checked_free(buffer);
3089 micro_chunk_size += 2 + num_bytes;
3091 else /* constant size configuration data (1, 2 or 4 bytes) */
3093 int value = (byte_mask == CONF_MASK_1_BYTE ? getFile8Bit (file) :
3094 byte_mask == CONF_MASK_2_BYTE ? getFile16BitBE(file) :
3095 byte_mask == CONF_MASK_4_BYTE ? getFile32BitBE(file) : 0);
3097 for (i = 0; conf[i].data_type != -1; i++)
3099 if (conf[i].element == element &&
3100 conf[i].conf_type == conf_type)
3102 int data_type = conf[i].data_type;
3104 if (data_type == TYPE_ELEMENT)
3105 value = getMappedElement(value);
3107 if (data_type == TYPE_BOOLEAN)
3108 *(boolean *)(conf[i].value) = value;
3110 *(int *) (conf[i].value) = value;
3112 element_found = TRUE;
3118 micro_chunk_size += CONF_VALUE_NUM_BYTES(byte_mask);
3123 char *error_conf_chunk_bytes =
3124 (byte_mask == CONF_MASK_1_BYTE ? "CONF_VALUE_8_BIT" :
3125 byte_mask == CONF_MASK_2_BYTE ? "CONF_VALUE_16_BIT" :
3126 byte_mask == CONF_MASK_4_BYTE ? "CONF_VALUE_32_BIT" :"CONF_VALUE_BYTES");
3127 int error_conf_chunk_token = conf_type & CONF_MASK_TOKEN;
3128 int error_element = real_element;
3130 Error(ERR_WARN, "cannot load micro chunk '%s(%d)' value for element %d ['%s']",
3131 error_conf_chunk_bytes, error_conf_chunk_token,
3132 error_element, EL_NAME(error_element));
3135 return micro_chunk_size;
3138 static int LoadLevel_INFO(File *file, int chunk_size, struct LevelInfo *level)
3140 int real_chunk_size = 0;
3142 li = *level; /* copy level data into temporary buffer */
3144 while (!checkEndOfFile(file))
3146 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_INFO, -1, -1);
3148 if (real_chunk_size >= chunk_size)
3152 *level = li; /* copy temporary buffer back to level data */
3154 return real_chunk_size;
3157 static int LoadLevel_CONF(File *file, int chunk_size, struct LevelInfo *level)
3159 int real_chunk_size = 0;
3161 li = *level; /* copy level data into temporary buffer */
3163 while (!checkEndOfFile(file))
3165 int element = getMappedElement(getFile16BitBE(file));
3167 real_chunk_size += 2;
3168 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_CONF,
3170 if (real_chunk_size >= chunk_size)
3174 *level = li; /* copy temporary buffer back to level data */
3176 return real_chunk_size;
3179 static int LoadLevel_ELEM(File *file, int chunk_size, struct LevelInfo *level)
3181 int real_chunk_size = 0;
3183 li = *level; /* copy level data into temporary buffer */
3185 while (!checkEndOfFile(file))
3187 int element = getMappedElement(getFile16BitBE(file));
3189 real_chunk_size += 2;
3190 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_ELEM,
3192 if (real_chunk_size >= chunk_size)
3196 *level = li; /* copy temporary buffer back to level data */
3198 return real_chunk_size;
3201 static int LoadLevel_NOTE(File *file, int chunk_size, struct LevelInfo *level)
3203 int element = getMappedElement(getFile16BitBE(file));
3204 int envelope_nr = element - EL_ENVELOPE_1;
3205 int real_chunk_size = 2;
3207 xx_envelope = level->envelope[envelope_nr]; /* copy into temporary buffer */
3209 while (!checkEndOfFile(file))
3211 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_NOTE,
3214 if (real_chunk_size >= chunk_size)
3218 level->envelope[envelope_nr] = xx_envelope; /* copy from temporary buffer */
3220 return real_chunk_size;
3223 static int LoadLevel_CUSX(File *file, int chunk_size, struct LevelInfo *level)
3225 int element = getMappedElement(getFile16BitBE(file));
3226 int real_chunk_size = 2;
3227 struct ElementInfo *ei = &element_info[element];
3230 xx_ei = *ei; /* copy element data into temporary buffer */
3232 xx_ei.num_change_pages = -1;
3234 while (!checkEndOfFile(file))
3236 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_CUSX_base,
3238 if (xx_ei.num_change_pages != -1)
3241 if (real_chunk_size >= chunk_size)
3247 if (ei->num_change_pages == -1)
3249 Error(ERR_WARN, "LoadLevel_CUSX(): missing 'num_change_pages' for '%s'",
3252 ei->num_change_pages = 1;
3254 setElementChangePages(ei, 1);
3255 setElementChangeInfoToDefaults(ei->change);
3257 return real_chunk_size;
3260 /* initialize number of change pages stored for this custom element */
3261 setElementChangePages(ei, ei->num_change_pages);
3262 for (i = 0; i < ei->num_change_pages; i++)
3263 setElementChangeInfoToDefaults(&ei->change_page[i]);
3265 /* start with reading properties for the first change page */
3266 xx_current_change_page = 0;
3268 while (!checkEndOfFile(file))
3270 struct ElementChangeInfo *change = &ei->change_page[xx_current_change_page];
3272 xx_change = *change; /* copy change data into temporary buffer */
3274 resetEventBits(); /* reset bits; change page might have changed */
3276 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_CUSX_change,
3279 *change = xx_change;
3281 setEventFlagsFromEventBits(change);
3283 if (real_chunk_size >= chunk_size)
3287 level->file_has_custom_elements = TRUE;
3289 return real_chunk_size;
3292 static int LoadLevel_GRPX(File *file, int chunk_size, struct LevelInfo *level)
3294 int element = getMappedElement(getFile16BitBE(file));
3295 int real_chunk_size = 2;
3296 struct ElementInfo *ei = &element_info[element];
3297 struct ElementGroupInfo *group = ei->group;
3299 xx_ei = *ei; /* copy element data into temporary buffer */
3300 xx_group = *group; /* copy group data into temporary buffer */
3302 while (!checkEndOfFile(file))
3304 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_GRPX,
3307 if (real_chunk_size >= chunk_size)
3314 level->file_has_custom_elements = TRUE;
3316 return real_chunk_size;
3319 static void LoadLevelFromFileInfo_RND(struct LevelInfo *level,
3320 struct LevelFileInfo *level_file_info,
3321 boolean level_info_only)
3323 char *filename = level_file_info->filename;
3324 char cookie[MAX_LINE_LEN];
3325 char chunk_name[CHUNK_ID_LEN + 1];
3329 if (!(file = openFile(filename, MODE_READ)))
3331 level->no_valid_file = TRUE;
3332 level->no_level_file = TRUE;
3334 if (level_info_only)
3337 Error(ERR_WARN, "cannot read level '%s' -- using empty level", filename);
3339 if (!setup.editor.use_template_for_new_levels)
3342 /* if level file not found, try to initialize level data from template */
3343 filename = getGlobalLevelTemplateFilename();
3345 if (!(file = openFile(filename, MODE_READ)))
3348 /* default: for empty levels, use level template for custom elements */
3349 level->use_custom_template = TRUE;
3351 level->no_valid_file = FALSE;
3354 getFileChunkBE(file, chunk_name, NULL);
3355 if (strEqual(chunk_name, "RND1"))
3357 getFile32BitBE(file); /* not used */
3359 getFileChunkBE(file, chunk_name, NULL);
3360 if (!strEqual(chunk_name, "CAVE"))
3362 level->no_valid_file = TRUE;
3364 Error(ERR_WARN, "unknown format of level file '%s'", filename);
3371 else /* check for pre-2.0 file format with cookie string */
3373 strcpy(cookie, chunk_name);
3374 if (getStringFromFile(file, &cookie[4], MAX_LINE_LEN - 4) == NULL)
3376 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
3377 cookie[strlen(cookie) - 1] = '\0';
3379 if (!checkCookieString(cookie, LEVEL_COOKIE_TMPL))
3381 level->no_valid_file = TRUE;
3383 Error(ERR_WARN, "unknown format of level file '%s'", filename);
3390 if ((level->file_version = getFileVersionFromCookieString(cookie)) == -1)
3392 level->no_valid_file = TRUE;
3394 Error(ERR_WARN, "unsupported version of level file '%s'", filename);
3401 /* pre-2.0 level files have no game version, so use file version here */
3402 level->game_version = level->file_version;
3405 if (level->file_version < FILE_VERSION_1_2)
3407 /* level files from versions before 1.2.0 without chunk structure */
3408 LoadLevel_HEAD(file, LEVEL_CHUNK_HEAD_SIZE, level);
3409 LoadLevel_BODY(file, level->fieldx * level->fieldy, level);
3417 int (*loader)(File *, int, struct LevelInfo *);
3421 { "VERS", LEVEL_CHUNK_VERS_SIZE, LoadLevel_VERS },
3422 { "DATE", LEVEL_CHUNK_DATE_SIZE, LoadLevel_DATE },
3423 { "HEAD", LEVEL_CHUNK_HEAD_SIZE, LoadLevel_HEAD },
3424 { "NAME", LEVEL_CHUNK_NAME_SIZE, LoadLevel_NAME },
3425 { "AUTH", LEVEL_CHUNK_AUTH_SIZE, LoadLevel_AUTH },
3426 { "INFO", -1, LoadLevel_INFO },
3427 { "BODY", -1, LoadLevel_BODY },
3428 { "CONT", -1, LoadLevel_CONT },
3429 { "CNT2", LEVEL_CHUNK_CNT2_SIZE, LoadLevel_CNT2 },
3430 { "CNT3", -1, LoadLevel_CNT3 },
3431 { "CUS1", -1, LoadLevel_CUS1 },
3432 { "CUS2", -1, LoadLevel_CUS2 },
3433 { "CUS3", -1, LoadLevel_CUS3 },
3434 { "CUS4", -1, LoadLevel_CUS4 },
3435 { "GRP1", -1, LoadLevel_GRP1 },
3436 { "CONF", -1, LoadLevel_CONF },
3437 { "ELEM", -1, LoadLevel_ELEM },
3438 { "NOTE", -1, LoadLevel_NOTE },
3439 { "CUSX", -1, LoadLevel_CUSX },
3440 { "GRPX", -1, LoadLevel_GRPX },
3445 while (getFileChunkBE(file, chunk_name, &chunk_size))
3449 while (chunk_info[i].name != NULL &&
3450 !strEqual(chunk_name, chunk_info[i].name))
3453 if (chunk_info[i].name == NULL)
3455 Error(ERR_WARN, "unknown chunk '%s' in level file '%s'",
3456 chunk_name, filename);
3457 ReadUnusedBytesFromFile(file, chunk_size);
3459 else if (chunk_info[i].size != -1 &&
3460 chunk_info[i].size != chunk_size)
3462 Error(ERR_WARN, "wrong size (%d) of chunk '%s' in level file '%s'",
3463 chunk_size, chunk_name, filename);
3464 ReadUnusedBytesFromFile(file, chunk_size);
3468 /* call function to load this level chunk */
3469 int chunk_size_expected =
3470 (chunk_info[i].loader)(file, chunk_size, level);
3472 /* the size of some chunks cannot be checked before reading other
3473 chunks first (like "HEAD" and "BODY") that contain some header
3474 information, so check them here */
3475 if (chunk_size_expected != chunk_size)
3477 Error(ERR_WARN, "wrong size (%d) of chunk '%s' in level file '%s'",
3478 chunk_size, chunk_name, filename);
3488 /* ------------------------------------------------------------------------- */
3489 /* functions for loading EM level */
3490 /* ------------------------------------------------------------------------- */
3492 static void CopyNativeLevel_RND_to_EM(struct LevelInfo *level)
3494 static int ball_xy[8][2] =
3505 struct LevelInfo_EM *level_em = level->native_em_level;
3506 struct LEVEL *lev = level_em->lev;
3507 struct PLAYER **ply = level_em->ply;
3510 lev->width = MIN(level->fieldx, EM_MAX_CAVE_WIDTH);
3511 lev->height = MIN(level->fieldy, EM_MAX_CAVE_HEIGHT);
3513 lev->time_seconds = level->time;
3514 lev->required_initial = level->gems_needed;
3516 lev->emerald_score = level->score[SC_EMERALD];
3517 lev->diamond_score = level->score[SC_DIAMOND];
3518 lev->alien_score = level->score[SC_ROBOT];
3519 lev->tank_score = level->score[SC_SPACESHIP];
3520 lev->bug_score = level->score[SC_BUG];
3521 lev->eater_score = level->score[SC_YAMYAM];
3522 lev->nut_score = level->score[SC_NUT];
3523 lev->dynamite_score = level->score[SC_DYNAMITE];
3524 lev->key_score = level->score[SC_KEY];
3525 lev->exit_score = level->score[SC_TIME_BONUS];
3527 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
3528 for (y = 0; y < 3; y++)
3529 for (x = 0; x < 3; x++)
3530 lev->eater_array[i][y * 3 + x] =
3531 map_element_RND_to_EM(level->yamyam_content[i].e[x][y]);
3533 lev->amoeba_time = level->amoeba_speed;
3534 lev->wonderwall_time_initial = level->time_magic_wall;
3535 lev->wheel_time = level->time_wheel;
3537 lev->android_move_time = level->android_move_time;
3538 lev->android_clone_time = level->android_clone_time;
3539 lev->ball_random = level->ball_random;
3540 lev->ball_state_initial = level->ball_state_initial;
3541 lev->ball_time = level->ball_time;
3542 lev->num_ball_arrays = level->num_ball_contents;
3544 lev->lenses_score = level->lenses_score;
3545 lev->magnify_score = level->magnify_score;
3546 lev->slurp_score = level->slurp_score;
3548 lev->lenses_time = level->lenses_time;
3549 lev->magnify_time = level->magnify_time;
3551 lev->wind_direction_initial =
3552 map_direction_RND_to_EM(level->wind_direction_initial);
3553 lev->wind_cnt_initial = (level->wind_direction_initial != MV_NONE ?
3554 lev->wind_time : 0);
3556 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
3557 for (j = 0; j < 8; j++)
3558 lev->ball_array[i][j] =
3559 map_element_RND_to_EM(level->
3560 ball_content[i].e[ball_xy[j][0]][ball_xy[j][1]]);
3562 map_android_clone_elements_RND_to_EM(level);
3564 /* first fill the complete playfield with the default border element */
3565 for (y = 0; y < EM_MAX_CAVE_HEIGHT; y++)
3566 for (x = 0; x < EM_MAX_CAVE_WIDTH; x++)
3567 level_em->cave[x][y] = ZBORDER;
3569 if (BorderElement == EL_STEELWALL)
3571 for (y = 0; y < lev->height + 2; y++)
3572 for (x = 0; x < lev->width + 2; x++)
3573 level_em->cave[x + 1][y + 1] = map_element_RND_to_EM(EL_STEELWALL);
3576 /* then copy the real level contents from level file into the playfield */
3577 for (y = 0; y < lev->height; y++) for (x = 0; x < lev->width; x++)
3579 int new_element = map_element_RND_to_EM(level->field[x][y]);
3580 int offset = (BorderElement == EL_STEELWALL ? 1 : 0);
3581 int xx = x + 1 + offset;
3582 int yy = y + 1 + offset;
3584 if (level->field[x][y] == EL_AMOEBA_DEAD)
3585 new_element = map_element_RND_to_EM(EL_AMOEBA_WET);
3587 level_em->cave[xx][yy] = new_element;
3590 for (i = 0; i < MAX_PLAYERS; i++)
3592 ply[i]->x_initial = 0;
3593 ply[i]->y_initial = 0;
3596 /* initialize player positions and delete players from the playfield */
3597 for (y = 0; y < lev->height; y++) for (x = 0; x < lev->width; x++)
3599 if (ELEM_IS_PLAYER(level->field[x][y]))
3601 int player_nr = GET_PLAYER_NR(level->field[x][y]);
3602 int offset = (BorderElement == EL_STEELWALL ? 1 : 0);
3603 int xx = x + 1 + offset;
3604 int yy = y + 1 + offset;
3606 ply[player_nr]->x_initial = xx;
3607 ply[player_nr]->y_initial = yy;
3609 level_em->cave[xx][yy] = map_element_RND_to_EM(EL_EMPTY);
3613 if (BorderElement == EL_STEELWALL)
3620 static void CopyNativeLevel_EM_to_RND(struct LevelInfo *level)
3622 static int ball_xy[8][2] =
3633 struct LevelInfo_EM *level_em = level->native_em_level;
3634 struct LEVEL *lev = level_em->lev;
3635 struct PLAYER **ply = level_em->ply;
3638 level->fieldx = MIN(lev->width, MAX_LEV_FIELDX);
3639 level->fieldy = MIN(lev->height, MAX_LEV_FIELDY);
3641 level->time = lev->time_seconds;
3642 level->gems_needed = lev->required_initial;
3644 sprintf(level->name, "Level %d", level->file_info.nr);
3646 level->score[SC_EMERALD] = lev->emerald_score;
3647 level->score[SC_DIAMOND] = lev->diamond_score;
3648 level->score[SC_ROBOT] = lev->alien_score;
3649 level->score[SC_SPACESHIP] = lev->tank_score;
3650 level->score[SC_BUG] = lev->bug_score;
3651 level->score[SC_YAMYAM] = lev->eater_score;
3652 level->score[SC_NUT] = lev->nut_score;
3653 level->score[SC_DYNAMITE] = lev->dynamite_score;
3654 level->score[SC_KEY] = lev->key_score;
3655 level->score[SC_TIME_BONUS] = lev->exit_score;
3657 level->num_yamyam_contents = MAX_ELEMENT_CONTENTS;
3659 for (i = 0; i < level->num_yamyam_contents; i++)
3660 for (y = 0; y < 3; y++)
3661 for (x = 0; x < 3; x++)
3662 level->yamyam_content[i].e[x][y] =
3663 map_element_EM_to_RND(lev->eater_array[i][y * 3 + x]);
3665 level->amoeba_speed = lev->amoeba_time;
3666 level->time_magic_wall = lev->wonderwall_time_initial;
3667 level->time_wheel = lev->wheel_time;
3669 level->android_move_time = lev->android_move_time;
3670 level->android_clone_time = lev->android_clone_time;
3671 level->ball_random = lev->ball_random;
3672 level->ball_state_initial = lev->ball_state_initial;
3673 level->ball_time = lev->ball_time;
3674 level->num_ball_contents = lev->num_ball_arrays;
3676 level->lenses_score = lev->lenses_score;
3677 level->magnify_score = lev->magnify_score;
3678 level->slurp_score = lev->slurp_score;
3680 level->lenses_time = lev->lenses_time;
3681 level->magnify_time = lev->magnify_time;
3683 level->wind_direction_initial =
3684 map_direction_EM_to_RND(lev->wind_direction_initial);
3686 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
3687 for (j = 0; j < 8; j++)
3688 level->ball_content[i].e[ball_xy[j][0]][ball_xy[j][1]] =
3689 map_element_EM_to_RND(lev->ball_array[i][j]);
3691 map_android_clone_elements_EM_to_RND(level);
3693 /* convert the playfield (some elements need special treatment) */
3694 for (y = 0; y < level->fieldy; y++) for (x = 0; x < level->fieldx; x++)
3696 int new_element = map_element_EM_to_RND(level_em->cave[x + 1][y + 1]);
3698 if (new_element == EL_AMOEBA_WET && level->amoeba_speed == 0)
3699 new_element = EL_AMOEBA_DEAD;
3701 level->field[x][y] = new_element;
3704 for (i = 0; i < MAX_PLAYERS; i++)
3706 /* in case of all players set to the same field, use the first player */
3707 int nr = MAX_PLAYERS - i - 1;
3708 int jx = ply[nr]->x_initial - 1;
3709 int jy = ply[nr]->y_initial - 1;
3711 if (jx != -1 && jy != -1)
3712 level->field[jx][jy] = EL_PLAYER_1 + nr;
3717 /* ------------------------------------------------------------------------- */
3718 /* functions for loading SP level */
3719 /* ------------------------------------------------------------------------- */
3721 static void CopyNativeLevel_RND_to_SP(struct LevelInfo *level)
3723 struct LevelInfo_SP *level_sp = level->native_sp_level;
3724 LevelInfoType *header = &level_sp->header;
3727 level_sp->width = level->fieldx;
3728 level_sp->height = level->fieldy;
3730 for (x = 0; x < level->fieldx; x++)
3731 for (y = 0; y < level->fieldy; y++)
3732 level_sp->playfield[x][y] = map_element_RND_to_SP(level->field[x][y]);
3734 header->InitialGravity = (level->initial_player_gravity[0] ? 1 : 0);
3736 for (i = 0; i < SP_LEVEL_NAME_LEN; i++)
3737 header->LevelTitle[i] = level->name[i];
3738 /* !!! NO STRING TERMINATION IN SUPAPLEX VB CODE YET -- FIX THIS !!! */
3740 header->InfotronsNeeded = level->gems_needed;
3742 header->SpecialPortCount = 0;
3744 for (x = 0; x < level->fieldx; x++) for (y = 0; y < level->fieldy; y++)
3746 boolean gravity_port_found = FALSE;
3747 boolean gravity_port_valid = FALSE;
3748 int gravity_port_flag;
3749 int gravity_port_base_element;
3750 int element = level->field[x][y];
3752 if (element >= EL_SP_GRAVITY_ON_PORT_RIGHT &&
3753 element <= EL_SP_GRAVITY_ON_PORT_UP)
3755 gravity_port_found = TRUE;
3756 gravity_port_valid = TRUE;
3757 gravity_port_flag = 1;
3758 gravity_port_base_element = EL_SP_GRAVITY_ON_PORT_RIGHT;
3760 else if (element >= EL_SP_GRAVITY_OFF_PORT_RIGHT &&
3761 element <= EL_SP_GRAVITY_OFF_PORT_UP)
3763 gravity_port_found = TRUE;
3764 gravity_port_valid = TRUE;
3765 gravity_port_flag = 0;
3766 gravity_port_base_element = EL_SP_GRAVITY_OFF_PORT_RIGHT;
3768 else if (element >= EL_SP_GRAVITY_PORT_RIGHT &&
3769 element <= EL_SP_GRAVITY_PORT_UP)
3771 /* change R'n'D style gravity inverting special port to normal port
3772 (there are no gravity inverting ports in native Supaplex engine) */
3774 gravity_port_found = TRUE;
3775 gravity_port_valid = FALSE;
3776 gravity_port_base_element = EL_SP_GRAVITY_PORT_RIGHT;
3779 if (gravity_port_found)
3781 if (gravity_port_valid &&
3782 header->SpecialPortCount < SP_MAX_SPECIAL_PORTS)
3784 SpecialPortType *port = &header->SpecialPort[header->SpecialPortCount];
3786 port->PortLocation = (y * level->fieldx + x) * 2;
3787 port->Gravity = gravity_port_flag;
3789 element += EL_SP_GRAVITY_PORT_RIGHT - gravity_port_base_element;
3791 header->SpecialPortCount++;
3795 /* change special gravity port to normal port */
3797 element += EL_SP_PORT_RIGHT - gravity_port_base_element;
3800 level_sp->playfield[x][y] = element - EL_SP_START;
3805 static void CopyNativeLevel_SP_to_RND(struct LevelInfo *level)
3807 struct LevelInfo_SP *level_sp = level->native_sp_level;
3808 LevelInfoType *header = &level_sp->header;
3809 boolean num_invalid_elements = 0;
3812 level->fieldx = level_sp->width;
3813 level->fieldy = level_sp->height;
3815 for (x = 0; x < level->fieldx; x++)
3817 for (y = 0; y < level->fieldy; y++)
3819 int element_old = level_sp->playfield[x][y];
3820 int element_new = getMappedElement(map_element_SP_to_RND(element_old));
3822 if (element_new == EL_UNKNOWN)
3824 num_invalid_elements++;
3826 Error(ERR_DEBUG, "invalid element %d at position %d, %d",
3830 level->field[x][y] = element_new;
3834 if (num_invalid_elements > 0)
3835 Error(ERR_WARN, "found %d invalid elements%s", num_invalid_elements,
3836 (!options.debug ? " (use '--debug' for more details)" : ""));
3838 for (i = 0; i < MAX_PLAYERS; i++)
3839 level->initial_player_gravity[i] =
3840 (header->InitialGravity == 1 ? TRUE : FALSE);
3842 /* skip leading spaces */
3843 for (i = 0; i < SP_LEVEL_NAME_LEN; i++)
3844 if (header->LevelTitle[i] != ' ')
3847 /* copy level title */
3848 for (j = 0; i < SP_LEVEL_NAME_LEN; i++, j++)
3849 level->name[j] = header->LevelTitle[i];
3850 level->name[j] = '\0';
3852 /* cut trailing spaces */
3854 if (level->name[j - 1] == ' ' && level->name[j] == '\0')
3855 level->name[j - 1] = '\0';
3857 level->gems_needed = header->InfotronsNeeded;
3859 for (i = 0; i < header->SpecialPortCount; i++)
3861 SpecialPortType *port = &header->SpecialPort[i];
3862 int port_location = port->PortLocation;
3863 int gravity = port->Gravity;
3864 int port_x, port_y, port_element;
3866 port_x = (port_location / 2) % level->fieldx;
3867 port_y = (port_location / 2) / level->fieldx;
3869 if (port_x < 0 || port_x >= level->fieldx ||
3870 port_y < 0 || port_y >= level->fieldy)
3872 Error(ERR_WARN, "special port position (%d, %d) out of bounds",
3878 port_element = level->field[port_x][port_y];
3880 if (port_element < EL_SP_GRAVITY_PORT_RIGHT ||
3881 port_element > EL_SP_GRAVITY_PORT_UP)
3883 Error(ERR_WARN, "no special port at position (%d, %d)", port_x, port_y);
3888 /* change previous (wrong) gravity inverting special port to either
3889 gravity enabling special port or gravity disabling special port */
3890 level->field[port_x][port_y] +=
3891 (gravity == 1 ? EL_SP_GRAVITY_ON_PORT_RIGHT :
3892 EL_SP_GRAVITY_OFF_PORT_RIGHT) - EL_SP_GRAVITY_PORT_RIGHT;
3895 /* change special gravity ports without database entries to normal ports */
3896 for (x = 0; x < level->fieldx; x++)
3897 for (y = 0; y < level->fieldy; y++)
3898 if (level->field[x][y] >= EL_SP_GRAVITY_PORT_RIGHT &&
3899 level->field[x][y] <= EL_SP_GRAVITY_PORT_UP)
3900 level->field[x][y] += EL_SP_PORT_RIGHT - EL_SP_GRAVITY_PORT_RIGHT;
3902 level->time = 0; /* no time limit */
3903 level->amoeba_speed = 0;
3904 level->time_magic_wall = 0;
3905 level->time_wheel = 0;
3906 level->amoeba_content = EL_EMPTY;
3909 /* original Supaplex does not use score values -- use default values */
3911 for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
3912 level->score[i] = 0;
3915 /* there are no yamyams in supaplex levels */
3916 for (i = 0; i < level->num_yamyam_contents; i++)
3917 for (x = 0; x < 3; x++)
3918 for (y = 0; y < 3; y++)
3919 level->yamyam_content[i].e[x][y] = EL_EMPTY;
3922 static void CopyNativeTape_RND_to_SP(struct LevelInfo *level)
3924 struct LevelInfo_SP *level_sp = level->native_sp_level;
3925 struct DemoInfo_SP *demo = &level_sp->demo;
3928 /* always start with reliable default values */
3929 demo->is_available = FALSE;
3932 if (TAPE_IS_EMPTY(tape))
3935 demo->level_nr = tape.level_nr; /* (currently not used) */
3937 level_sp->header.DemoRandomSeed = tape.random_seed;
3941 for (i = 0; i < tape.length; i++)
3943 int demo_action = map_key_RND_to_SP(tape.pos[i].action[0]);
3944 int demo_repeat = tape.pos[i].delay;
3945 int demo_entries = (demo_repeat + 15) / 16;
3947 if (demo->length + demo_entries >= SP_MAX_TAPE_LEN)
3949 Error(ERR_WARN, "tape truncated: size exceeds maximum SP demo size %d",
3955 for (j = 0; j < demo_repeat / 16; j++)
3956 demo->data[demo->length++] = 0xf0 | demo_action;
3958 if (demo_repeat % 16)
3959 demo->data[demo->length++] = ((demo_repeat % 16 - 1) << 4) | demo_action;
3962 demo->is_available = TRUE;
3965 static void setTapeInfoToDefaults(void);
3967 static void CopyNativeTape_SP_to_RND(struct LevelInfo *level)
3969 struct LevelInfo_SP *level_sp = level->native_sp_level;
3970 struct DemoInfo_SP *demo = &level_sp->demo;
3971 char *filename = level->file_info.filename;
3974 /* always start with reliable default values */
3975 setTapeInfoToDefaults();
3977 if (!demo->is_available)
3980 tape.level_nr = demo->level_nr; /* (currently not used) */
3981 tape.random_seed = level_sp->header.DemoRandomSeed;
3983 TapeSetDateFromEpochSeconds(getFileTimestampEpochSeconds(filename));
3986 tape.pos[tape.counter].delay = 0;
3988 for (i = 0; i < demo->length; i++)
3990 int demo_action = demo->data[i] & 0x0f;
3991 int demo_repeat = (demo->data[i] & 0xf0) >> 4;
3992 int tape_action = map_key_SP_to_RND(demo_action);
3993 int tape_repeat = demo_repeat + 1;
3994 byte action[MAX_PLAYERS] = { tape_action, 0, 0, 0 };
3995 boolean success = 0;
3998 for (j = 0; j < tape_repeat; j++)
3999 success = TapeAddAction(action);
4003 Error(ERR_WARN, "SP demo truncated: size exceeds maximum tape size %d",
4010 TapeHaltRecording();
4014 /* ------------------------------------------------------------------------- */
4015 /* functions for loading MM level */
4016 /* ------------------------------------------------------------------------- */
4018 static void CopyNativeLevel_RND_to_MM(struct LevelInfo *level)
4020 struct LevelInfo_MM *level_mm = level->native_mm_level;
4023 level_mm->fieldx = MIN(level->fieldx, MM_MAX_PLAYFIELD_WIDTH);
4024 level_mm->fieldy = MIN(level->fieldy, MM_MAX_PLAYFIELD_HEIGHT);
4026 level_mm->time = level->time;
4027 level_mm->kettles_needed = level->gems_needed;
4028 level_mm->auto_count_kettles = level->auto_count_gems;
4030 level_mm->laser_red = level->mm_laser_red;
4031 level_mm->laser_green = level->mm_laser_green;
4032 level_mm->laser_blue = level->mm_laser_blue;
4034 strcpy(level_mm->name, level->name);
4035 strcpy(level_mm->author, level->author);
4037 level_mm->score[SC_EMERALD] = level->score[SC_EMERALD];
4038 level_mm->score[SC_PACMAN] = level->score[SC_PACMAN];
4039 level_mm->score[SC_KEY] = level->score[SC_KEY];
4040 level_mm->score[SC_TIME_BONUS] = level->score[SC_TIME_BONUS];
4041 level_mm->score[SC_ELEM_BONUS] = level->score[SC_ELEM_BONUS];
4043 level_mm->amoeba_speed = level->amoeba_speed;
4044 level_mm->time_fuse = level->mm_time_fuse;
4045 level_mm->time_bomb = level->mm_time_bomb;
4046 level_mm->time_ball = level->mm_time_ball;
4047 level_mm->time_block = level->mm_time_block;
4049 for (x = 0; x < level->fieldx; x++)
4050 for (y = 0; y < level->fieldy; y++)
4052 level_mm->field[x][y] = map_element_RND_to_MM(level->field[x][y]);
4055 static void CopyNativeLevel_MM_to_RND(struct LevelInfo *level)
4057 struct LevelInfo_MM *level_mm = level->native_mm_level;
4060 level->fieldx = MIN(level_mm->fieldx, MAX_LEV_FIELDX);
4061 level->fieldy = MIN(level_mm->fieldy, MAX_LEV_FIELDY);
4063 level->time = level_mm->time;
4064 level->gems_needed = level_mm->kettles_needed;
4065 level->auto_count_gems = level_mm->auto_count_kettles;
4067 level->mm_laser_red = level_mm->laser_red;
4068 level->mm_laser_green = level_mm->laser_green;
4069 level->mm_laser_blue = level_mm->laser_blue;
4071 strcpy(level->name, level_mm->name);
4073 /* only overwrite author from 'levelinfo.conf' if author defined in level */
4074 if (!strEqual(level_mm->author, ANONYMOUS_NAME))
4075 strcpy(level->author, level_mm->author);
4077 level->score[SC_EMERALD] = level_mm->score[SC_EMERALD];
4078 level->score[SC_PACMAN] = level_mm->score[SC_PACMAN];
4079 level->score[SC_KEY] = level_mm->score[SC_KEY];
4080 level->score[SC_TIME_BONUS] = level_mm->score[SC_TIME_BONUS];
4081 level->score[SC_ELEM_BONUS] = level_mm->score[SC_ELEM_BONUS];
4083 level->amoeba_speed = level_mm->amoeba_speed;
4084 level->mm_time_fuse = level_mm->time_fuse;
4085 level->mm_time_bomb = level_mm->time_bomb;
4086 level->mm_time_ball = level_mm->time_ball;
4087 level->mm_time_block = level_mm->time_block;
4089 for (x = 0; x < level->fieldx; x++)
4090 for (y = 0; y < level->fieldy; y++)
4091 level->field[x][y] = map_element_MM_to_RND(level_mm->field[x][y]);
4095 /* ------------------------------------------------------------------------- */
4096 /* functions for loading DC level */
4097 /* ------------------------------------------------------------------------- */
4099 #define DC_LEVEL_HEADER_SIZE 344
4101 static unsigned short getDecodedWord_DC(unsigned short data_encoded,
4104 static int last_data_encoded;
4108 int diff_hi, diff_lo;
4109 int data_hi, data_lo;
4110 unsigned short data_decoded;
4114 last_data_encoded = 0;
4121 diff = data_encoded - last_data_encoded;
4122 diff_hi = diff & ~0xff;
4123 diff_lo = diff & 0xff;
4127 data_hi = diff_hi - (offset1 << 8) + (offset2 & 0xff00);
4128 data_lo = (diff_lo + (data_hi >> 16)) & 0x00ff;
4129 data_hi = data_hi & 0xff00;
4131 data_decoded = data_hi | data_lo;
4133 last_data_encoded = data_encoded;
4135 offset1 = (offset1 + 1) % 31;
4136 offset2 = offset2 & 0xff;
4138 return data_decoded;
4141 static int getMappedElement_DC(int element)
4149 /* 0x0117 - 0x036e: (?) */
4152 /* 0x042d - 0x0684: (?) */
4168 element = EL_CRYSTAL;
4171 case 0x0e77: /* quicksand (boulder) */
4172 element = EL_QUICKSAND_FAST_FULL;
4175 case 0x0e99: /* slow quicksand (boulder) */
4176 element = EL_QUICKSAND_FULL;
4180 element = EL_EM_EXIT_OPEN;
4184 element = EL_EM_EXIT_CLOSED;
4188 element = EL_EM_STEEL_EXIT_OPEN;
4192 element = EL_EM_STEEL_EXIT_CLOSED;
4195 case 0x0f4f: /* dynamite (lit 1) */
4196 element = EL_EM_DYNAMITE_ACTIVE;
4199 case 0x0f57: /* dynamite (lit 2) */
4200 element = EL_EM_DYNAMITE_ACTIVE;
4203 case 0x0f5f: /* dynamite (lit 3) */
4204 element = EL_EM_DYNAMITE_ACTIVE;
4207 case 0x0f67: /* dynamite (lit 4) */
4208 element = EL_EM_DYNAMITE_ACTIVE;
4215 element = EL_AMOEBA_WET;
4219 element = EL_AMOEBA_DROP;
4223 element = EL_DC_MAGIC_WALL;
4227 element = EL_SPACESHIP_UP;
4231 element = EL_SPACESHIP_DOWN;
4235 element = EL_SPACESHIP_LEFT;
4239 element = EL_SPACESHIP_RIGHT;
4243 element = EL_BUG_UP;
4247 element = EL_BUG_DOWN;
4251 element = EL_BUG_LEFT;
4255 element = EL_BUG_RIGHT;
4259 element = EL_MOLE_UP;
4263 element = EL_MOLE_DOWN;
4267 element = EL_MOLE_LEFT;
4271 element = EL_MOLE_RIGHT;
4279 element = EL_YAMYAM;
4283 element = EL_SWITCHGATE_OPEN;
4287 element = EL_SWITCHGATE_CLOSED;
4291 element = EL_DC_SWITCHGATE_SWITCH_UP;
4295 element = EL_TIMEGATE_CLOSED;
4298 case 0x144c: /* conveyor belt switch (green) */
4299 element = EL_CONVEYOR_BELT_3_SWITCH_MIDDLE;
4302 case 0x144f: /* conveyor belt switch (red) */
4303 element = EL_CONVEYOR_BELT_1_SWITCH_MIDDLE;
4306 case 0x1452: /* conveyor belt switch (blue) */
4307 element = EL_CONVEYOR_BELT_4_SWITCH_MIDDLE;
4311 element = EL_CONVEYOR_BELT_3_MIDDLE;
4315 element = EL_CONVEYOR_BELT_3_LEFT;
4319 element = EL_CONVEYOR_BELT_3_RIGHT;
4323 element = EL_CONVEYOR_BELT_1_MIDDLE;
4327 element = EL_CONVEYOR_BELT_1_LEFT;
4331 element = EL_CONVEYOR_BELT_1_RIGHT;
4335 element = EL_CONVEYOR_BELT_4_MIDDLE;
4339 element = EL_CONVEYOR_BELT_4_LEFT;
4343 element = EL_CONVEYOR_BELT_4_RIGHT;
4347 element = EL_EXPANDABLE_WALL_HORIZONTAL;
4351 element = EL_EXPANDABLE_WALL_VERTICAL;
4355 element = EL_EXPANDABLE_WALL_ANY;
4358 case 0x14ce: /* growing steel wall (left/right) */
4359 element = EL_EXPANDABLE_STEELWALL_HORIZONTAL;
4362 case 0x14df: /* growing steel wall (up/down) */
4363 element = EL_EXPANDABLE_STEELWALL_VERTICAL;
4366 case 0x14e8: /* growing steel wall (up/down/left/right) */
4367 element = EL_EXPANDABLE_STEELWALL_ANY;
4371 element = EL_SHIELD_DEADLY;
4375 element = EL_EXTRA_TIME;
4383 element = EL_EMPTY_SPACE;
4386 case 0x1578: /* quicksand (empty) */
4387 element = EL_QUICKSAND_FAST_EMPTY;
4390 case 0x1579: /* slow quicksand (empty) */
4391 element = EL_QUICKSAND_EMPTY;
4394 /* 0x157c - 0x158b: */
4397 /* 0x1590 - 0x159f: */
4398 /* EL_DC_LANDMINE */
4401 element = EL_EM_DYNAMITE;
4404 case 0x15a1: /* key (red) */
4405 element = EL_EM_KEY_1;
4408 case 0x15a2: /* key (yellow) */
4409 element = EL_EM_KEY_2;
4412 case 0x15a3: /* key (blue) */
4413 element = EL_EM_KEY_4;
4416 case 0x15a4: /* key (green) */
4417 element = EL_EM_KEY_3;
4420 case 0x15a5: /* key (white) */
4421 element = EL_DC_KEY_WHITE;
4425 element = EL_WALL_SLIPPERY;
4432 case 0x15a8: /* wall (not round) */
4436 case 0x15a9: /* (blue) */
4437 element = EL_CHAR_A;
4440 case 0x15aa: /* (blue) */
4441 element = EL_CHAR_B;
4444 case 0x15ab: /* (blue) */
4445 element = EL_CHAR_C;
4448 case 0x15ac: /* (blue) */
4449 element = EL_CHAR_D;
4452 case 0x15ad: /* (blue) */
4453 element = EL_CHAR_E;
4456 case 0x15ae: /* (blue) */
4457 element = EL_CHAR_F;
4460 case 0x15af: /* (blue) */
4461 element = EL_CHAR_G;
4464 case 0x15b0: /* (blue) */
4465 element = EL_CHAR_H;
4468 case 0x15b1: /* (blue) */
4469 element = EL_CHAR_I;
4472 case 0x15b2: /* (blue) */
4473 element = EL_CHAR_J;
4476 case 0x15b3: /* (blue) */
4477 element = EL_CHAR_K;
4480 case 0x15b4: /* (blue) */
4481 element = EL_CHAR_L;
4484 case 0x15b5: /* (blue) */
4485 element = EL_CHAR_M;
4488 case 0x15b6: /* (blue) */
4489 element = EL_CHAR_N;
4492 case 0x15b7: /* (blue) */
4493 element = EL_CHAR_O;
4496 case 0x15b8: /* (blue) */
4497 element = EL_CHAR_P;
4500 case 0x15b9: /* (blue) */
4501 element = EL_CHAR_Q;
4504 case 0x15ba: /* (blue) */
4505 element = EL_CHAR_R;
4508 case 0x15bb: /* (blue) */
4509 element = EL_CHAR_S;
4512 case 0x15bc: /* (blue) */
4513 element = EL_CHAR_T;
4516 case 0x15bd: /* (blue) */
4517 element = EL_CHAR_U;
4520 case 0x15be: /* (blue) */
4521 element = EL_CHAR_V;
4524 case 0x15bf: /* (blue) */
4525 element = EL_CHAR_W;
4528 case 0x15c0: /* (blue) */
4529 element = EL_CHAR_X;
4532 case 0x15c1: /* (blue) */
4533 element = EL_CHAR_Y;
4536 case 0x15c2: /* (blue) */
4537 element = EL_CHAR_Z;
4540 case 0x15c3: /* (blue) */
4541 element = EL_CHAR_AUMLAUT;
4544 case 0x15c4: /* (blue) */
4545 element = EL_CHAR_OUMLAUT;
4548 case 0x15c5: /* (blue) */
4549 element = EL_CHAR_UUMLAUT;
4552 case 0x15c6: /* (blue) */
4553 element = EL_CHAR_0;
4556 case 0x15c7: /* (blue) */
4557 element = EL_CHAR_1;
4560 case 0x15c8: /* (blue) */
4561 element = EL_CHAR_2;
4564 case 0x15c9: /* (blue) */
4565 element = EL_CHAR_3;
4568 case 0x15ca: /* (blue) */
4569 element = EL_CHAR_4;
4572 case 0x15cb: /* (blue) */
4573 element = EL_CHAR_5;
4576 case 0x15cc: /* (blue) */
4577 element = EL_CHAR_6;
4580 case 0x15cd: /* (blue) */
4581 element = EL_CHAR_7;
4584 case 0x15ce: /* (blue) */
4585 element = EL_CHAR_8;
4588 case 0x15cf: /* (blue) */
4589 element = EL_CHAR_9;
4592 case 0x15d0: /* (blue) */
4593 element = EL_CHAR_PERIOD;
4596 case 0x15d1: /* (blue) */
4597 element = EL_CHAR_EXCLAM;
4600 case 0x15d2: /* (blue) */
4601 element = EL_CHAR_COLON;
4604 case 0x15d3: /* (blue) */
4605 element = EL_CHAR_LESS;
4608 case 0x15d4: /* (blue) */
4609 element = EL_CHAR_GREATER;
4612 case 0x15d5: /* (blue) */
4613 element = EL_CHAR_QUESTION;
4616 case 0x15d6: /* (blue) */
4617 element = EL_CHAR_COPYRIGHT;
4620 case 0x15d7: /* (blue) */
4621 element = EL_CHAR_UP;
4624 case 0x15d8: /* (blue) */
4625 element = EL_CHAR_DOWN;
4628 case 0x15d9: /* (blue) */
4629 element = EL_CHAR_BUTTON;
4632 case 0x15da: /* (blue) */
4633 element = EL_CHAR_PLUS;
4636 case 0x15db: /* (blue) */
4637 element = EL_CHAR_MINUS;
4640 case 0x15dc: /* (blue) */
4641 element = EL_CHAR_APOSTROPHE;
4644 case 0x15dd: /* (blue) */
4645 element = EL_CHAR_PARENLEFT;
4648 case 0x15de: /* (blue) */
4649 element = EL_CHAR_PARENRIGHT;
4652 case 0x15df: /* (green) */
4653 element = EL_CHAR_A;
4656 case 0x15e0: /* (green) */
4657 element = EL_CHAR_B;
4660 case 0x15e1: /* (green) */
4661 element = EL_CHAR_C;
4664 case 0x15e2: /* (green) */
4665 element = EL_CHAR_D;
4668 case 0x15e3: /* (green) */
4669 element = EL_CHAR_E;
4672 case 0x15e4: /* (green) */
4673 element = EL_CHAR_F;
4676 case 0x15e5: /* (green) */
4677 element = EL_CHAR_G;
4680 case 0x15e6: /* (green) */
4681 element = EL_CHAR_H;
4684 case 0x15e7: /* (green) */
4685 element = EL_CHAR_I;
4688 case 0x15e8: /* (green) */
4689 element = EL_CHAR_J;
4692 case 0x15e9: /* (green) */
4693 element = EL_CHAR_K;
4696 case 0x15ea: /* (green) */
4697 element = EL_CHAR_L;
4700 case 0x15eb: /* (green) */
4701 element = EL_CHAR_M;
4704 case 0x15ec: /* (green) */
4705 element = EL_CHAR_N;
4708 case 0x15ed: /* (green) */
4709 element = EL_CHAR_O;
4712 case 0x15ee: /* (green) */
4713 element = EL_CHAR_P;
4716 case 0x15ef: /* (green) */
4717 element = EL_CHAR_Q;
4720 case 0x15f0: /* (green) */
4721 element = EL_CHAR_R;
4724 case 0x15f1: /* (green) */
4725 element = EL_CHAR_S;
4728 case 0x15f2: /* (green) */
4729 element = EL_CHAR_T;
4732 case 0x15f3: /* (green) */
4733 element = EL_CHAR_U;
4736 case 0x15f4: /* (green) */
4737 element = EL_CHAR_V;
4740 case 0x15f5: /* (green) */
4741 element = EL_CHAR_W;
4744 case 0x15f6: /* (green) */
4745 element = EL_CHAR_X;
4748 case 0x15f7: /* (green) */
4749 element = EL_CHAR_Y;
4752 case 0x15f8: /* (green) */
4753 element = EL_CHAR_Z;
4756 case 0x15f9: /* (green) */
4757 element = EL_CHAR_AUMLAUT;
4760 case 0x15fa: /* (green) */
4761 element = EL_CHAR_OUMLAUT;
4764 case 0x15fb: /* (green) */
4765 element = EL_CHAR_UUMLAUT;
4768 case 0x15fc: /* (green) */
4769 element = EL_CHAR_0;
4772 case 0x15fd: /* (green) */
4773 element = EL_CHAR_1;
4776 case 0x15fe: /* (green) */
4777 element = EL_CHAR_2;
4780 case 0x15ff: /* (green) */
4781 element = EL_CHAR_3;
4784 case 0x1600: /* (green) */
4785 element = EL_CHAR_4;
4788 case 0x1601: /* (green) */
4789 element = EL_CHAR_5;
4792 case 0x1602: /* (green) */
4793 element = EL_CHAR_6;
4796 case 0x1603: /* (green) */
4797 element = EL_CHAR_7;
4800 case 0x1604: /* (green) */
4801 element = EL_CHAR_8;
4804 case 0x1605: /* (green) */
4805 element = EL_CHAR_9;
4808 case 0x1606: /* (green) */
4809 element = EL_CHAR_PERIOD;
4812 case 0x1607: /* (green) */
4813 element = EL_CHAR_EXCLAM;
4816 case 0x1608: /* (green) */
4817 element = EL_CHAR_COLON;
4820 case 0x1609: /* (green) */
4821 element = EL_CHAR_LESS;
4824 case 0x160a: /* (green) */
4825 element = EL_CHAR_GREATER;
4828 case 0x160b: /* (green) */
4829 element = EL_CHAR_QUESTION;
4832 case 0x160c: /* (green) */
4833 element = EL_CHAR_COPYRIGHT;
4836 case 0x160d: /* (green) */
4837 element = EL_CHAR_UP;
4840 case 0x160e: /* (green) */
4841 element = EL_CHAR_DOWN;
4844 case 0x160f: /* (green) */
4845 element = EL_CHAR_BUTTON;
4848 case 0x1610: /* (green) */
4849 element = EL_CHAR_PLUS;
4852 case 0x1611: /* (green) */
4853 element = EL_CHAR_MINUS;
4856 case 0x1612: /* (green) */
4857 element = EL_CHAR_APOSTROPHE;
4860 case 0x1613: /* (green) */
4861 element = EL_CHAR_PARENLEFT;
4864 case 0x1614: /* (green) */
4865 element = EL_CHAR_PARENRIGHT;
4868 case 0x1615: /* (blue steel) */
4869 element = EL_STEEL_CHAR_A;
4872 case 0x1616: /* (blue steel) */
4873 element = EL_STEEL_CHAR_B;
4876 case 0x1617: /* (blue steel) */
4877 element = EL_STEEL_CHAR_C;
4880 case 0x1618: /* (blue steel) */
4881 element = EL_STEEL_CHAR_D;
4884 case 0x1619: /* (blue steel) */
4885 element = EL_STEEL_CHAR_E;
4888 case 0x161a: /* (blue steel) */
4889 element = EL_STEEL_CHAR_F;
4892 case 0x161b: /* (blue steel) */
4893 element = EL_STEEL_CHAR_G;
4896 case 0x161c: /* (blue steel) */
4897 element = EL_STEEL_CHAR_H;
4900 case 0x161d: /* (blue steel) */
4901 element = EL_STEEL_CHAR_I;
4904 case 0x161e: /* (blue steel) */
4905 element = EL_STEEL_CHAR_J;
4908 case 0x161f: /* (blue steel) */
4909 element = EL_STEEL_CHAR_K;
4912 case 0x1620: /* (blue steel) */
4913 element = EL_STEEL_CHAR_L;
4916 case 0x1621: /* (blue steel) */
4917 element = EL_STEEL_CHAR_M;
4920 case 0x1622: /* (blue steel) */
4921 element = EL_STEEL_CHAR_N;
4924 case 0x1623: /* (blue steel) */
4925 element = EL_STEEL_CHAR_O;
4928 case 0x1624: /* (blue steel) */
4929 element = EL_STEEL_CHAR_P;
4932 case 0x1625: /* (blue steel) */
4933 element = EL_STEEL_CHAR_Q;
4936 case 0x1626: /* (blue steel) */
4937 element = EL_STEEL_CHAR_R;
4940 case 0x1627: /* (blue steel) */
4941 element = EL_STEEL_CHAR_S;
4944 case 0x1628: /* (blue steel) */
4945 element = EL_STEEL_CHAR_T;
4948 case 0x1629: /* (blue steel) */
4949 element = EL_STEEL_CHAR_U;
4952 case 0x162a: /* (blue steel) */
4953 element = EL_STEEL_CHAR_V;
4956 case 0x162b: /* (blue steel) */
4957 element = EL_STEEL_CHAR_W;
4960 case 0x162c: /* (blue steel) */
4961 element = EL_STEEL_CHAR_X;
4964 case 0x162d: /* (blue steel) */
4965 element = EL_STEEL_CHAR_Y;
4968 case 0x162e: /* (blue steel) */
4969 element = EL_STEEL_CHAR_Z;
4972 case 0x162f: /* (blue steel) */
4973 element = EL_STEEL_CHAR_AUMLAUT;
4976 case 0x1630: /* (blue steel) */
4977 element = EL_STEEL_CHAR_OUMLAUT;
4980 case 0x1631: /* (blue steel) */
4981 element = EL_STEEL_CHAR_UUMLAUT;
4984 case 0x1632: /* (blue steel) */
4985 element = EL_STEEL_CHAR_0;
4988 case 0x1633: /* (blue steel) */
4989 element = EL_STEEL_CHAR_1;
4992 case 0x1634: /* (blue steel) */
4993 element = EL_STEEL_CHAR_2;
4996 case 0x1635: /* (blue steel) */
4997 element = EL_STEEL_CHAR_3;
5000 case 0x1636: /* (blue steel) */
5001 element = EL_STEEL_CHAR_4;
5004 case 0x1637: /* (blue steel) */
5005 element = EL_STEEL_CHAR_5;
5008 case 0x1638: /* (blue steel) */
5009 element = EL_STEEL_CHAR_6;
5012 case 0x1639: /* (blue steel) */
5013 element = EL_STEEL_CHAR_7;
5016 case 0x163a: /* (blue steel) */
5017 element = EL_STEEL_CHAR_8;
5020 case 0x163b: /* (blue steel) */
5021 element = EL_STEEL_CHAR_9;
5024 case 0x163c: /* (blue steel) */
5025 element = EL_STEEL_CHAR_PERIOD;
5028 case 0x163d: /* (blue steel) */
5029 element = EL_STEEL_CHAR_EXCLAM;
5032 case 0x163e: /* (blue steel) */
5033 element = EL_STEEL_CHAR_COLON;
5036 case 0x163f: /* (blue steel) */
5037 element = EL_STEEL_CHAR_LESS;
5040 case 0x1640: /* (blue steel) */
5041 element = EL_STEEL_CHAR_GREATER;
5044 case 0x1641: /* (blue steel) */
5045 element = EL_STEEL_CHAR_QUESTION;
5048 case 0x1642: /* (blue steel) */
5049 element = EL_STEEL_CHAR_COPYRIGHT;
5052 case 0x1643: /* (blue steel) */
5053 element = EL_STEEL_CHAR_UP;
5056 case 0x1644: /* (blue steel) */
5057 element = EL_STEEL_CHAR_DOWN;
5060 case 0x1645: /* (blue steel) */
5061 element = EL_STEEL_CHAR_BUTTON;
5064 case 0x1646: /* (blue steel) */
5065 element = EL_STEEL_CHAR_PLUS;
5068 case 0x1647: /* (blue steel) */
5069 element = EL_STEEL_CHAR_MINUS;
5072 case 0x1648: /* (blue steel) */
5073 element = EL_STEEL_CHAR_APOSTROPHE;
5076 case 0x1649: /* (blue steel) */
5077 element = EL_STEEL_CHAR_PARENLEFT;
5080 case 0x164a: /* (blue steel) */
5081 element = EL_STEEL_CHAR_PARENRIGHT;
5084 case 0x164b: /* (green steel) */
5085 element = EL_STEEL_CHAR_A;
5088 case 0x164c: /* (green steel) */
5089 element = EL_STEEL_CHAR_B;
5092 case 0x164d: /* (green steel) */
5093 element = EL_STEEL_CHAR_C;
5096 case 0x164e: /* (green steel) */
5097 element = EL_STEEL_CHAR_D;
5100 case 0x164f: /* (green steel) */
5101 element = EL_STEEL_CHAR_E;
5104 case 0x1650: /* (green steel) */
5105 element = EL_STEEL_CHAR_F;
5108 case 0x1651: /* (green steel) */
5109 element = EL_STEEL_CHAR_G;
5112 case 0x1652: /* (green steel) */
5113 element = EL_STEEL_CHAR_H;
5116 case 0x1653: /* (green steel) */
5117 element = EL_STEEL_CHAR_I;
5120 case 0x1654: /* (green steel) */
5121 element = EL_STEEL_CHAR_J;
5124 case 0x1655: /* (green steel) */
5125 element = EL_STEEL_CHAR_K;
5128 case 0x1656: /* (green steel) */
5129 element = EL_STEEL_CHAR_L;
5132 case 0x1657: /* (green steel) */
5133 element = EL_STEEL_CHAR_M;
5136 case 0x1658: /* (green steel) */
5137 element = EL_STEEL_CHAR_N;
5140 case 0x1659: /* (green steel) */
5141 element = EL_STEEL_CHAR_O;
5144 case 0x165a: /* (green steel) */
5145 element = EL_STEEL_CHAR_P;
5148 case 0x165b: /* (green steel) */
5149 element = EL_STEEL_CHAR_Q;
5152 case 0x165c: /* (green steel) */
5153 element = EL_STEEL_CHAR_R;
5156 case 0x165d: /* (green steel) */
5157 element = EL_STEEL_CHAR_S;
5160 case 0x165e: /* (green steel) */
5161 element = EL_STEEL_CHAR_T;
5164 case 0x165f: /* (green steel) */
5165 element = EL_STEEL_CHAR_U;
5168 case 0x1660: /* (green steel) */
5169 element = EL_STEEL_CHAR_V;
5172 case 0x1661: /* (green steel) */
5173 element = EL_STEEL_CHAR_W;
5176 case 0x1662: /* (green steel) */
5177 element = EL_STEEL_CHAR_X;
5180 case 0x1663: /* (green steel) */
5181 element = EL_STEEL_CHAR_Y;
5184 case 0x1664: /* (green steel) */
5185 element = EL_STEEL_CHAR_Z;
5188 case 0x1665: /* (green steel) */
5189 element = EL_STEEL_CHAR_AUMLAUT;
5192 case 0x1666: /* (green steel) */
5193 element = EL_STEEL_CHAR_OUMLAUT;
5196 case 0x1667: /* (green steel) */
5197 element = EL_STEEL_CHAR_UUMLAUT;
5200 case 0x1668: /* (green steel) */
5201 element = EL_STEEL_CHAR_0;
5204 case 0x1669: /* (green steel) */
5205 element = EL_STEEL_CHAR_1;
5208 case 0x166a: /* (green steel) */
5209 element = EL_STEEL_CHAR_2;
5212 case 0x166b: /* (green steel) */
5213 element = EL_STEEL_CHAR_3;
5216 case 0x166c: /* (green steel) */
5217 element = EL_STEEL_CHAR_4;
5220 case 0x166d: /* (green steel) */
5221 element = EL_STEEL_CHAR_5;
5224 case 0x166e: /* (green steel) */
5225 element = EL_STEEL_CHAR_6;
5228 case 0x166f: /* (green steel) */
5229 element = EL_STEEL_CHAR_7;
5232 case 0x1670: /* (green steel) */
5233 element = EL_STEEL_CHAR_8;
5236 case 0x1671: /* (green steel) */
5237 element = EL_STEEL_CHAR_9;
5240 case 0x1672: /* (green steel) */
5241 element = EL_STEEL_CHAR_PERIOD;
5244 case 0x1673: /* (green steel) */
5245 element = EL_STEEL_CHAR_EXCLAM;
5248 case 0x1674: /* (green steel) */
5249 element = EL_STEEL_CHAR_COLON;
5252 case 0x1675: /* (green steel) */
5253 element = EL_STEEL_CHAR_LESS;
5256 case 0x1676: /* (green steel) */
5257 element = EL_STEEL_CHAR_GREATER;
5260 case 0x1677: /* (green steel) */
5261 element = EL_STEEL_CHAR_QUESTION;
5264 case 0x1678: /* (green steel) */
5265 element = EL_STEEL_CHAR_COPYRIGHT;
5268 case 0x1679: /* (green steel) */
5269 element = EL_STEEL_CHAR_UP;
5272 case 0x167a: /* (green steel) */
5273 element = EL_STEEL_CHAR_DOWN;
5276 case 0x167b: /* (green steel) */
5277 element = EL_STEEL_CHAR_BUTTON;
5280 case 0x167c: /* (green steel) */
5281 element = EL_STEEL_CHAR_PLUS;
5284 case 0x167d: /* (green steel) */
5285 element = EL_STEEL_CHAR_MINUS;
5288 case 0x167e: /* (green steel) */
5289 element = EL_STEEL_CHAR_APOSTROPHE;
5292 case 0x167f: /* (green steel) */
5293 element = EL_STEEL_CHAR_PARENLEFT;
5296 case 0x1680: /* (green steel) */
5297 element = EL_STEEL_CHAR_PARENRIGHT;
5300 case 0x1681: /* gate (red) */
5301 element = EL_EM_GATE_1;
5304 case 0x1682: /* secret gate (red) */
5305 element = EL_GATE_1_GRAY;
5308 case 0x1683: /* gate (yellow) */
5309 element = EL_EM_GATE_2;
5312 case 0x1684: /* secret gate (yellow) */
5313 element = EL_GATE_2_GRAY;
5316 case 0x1685: /* gate (blue) */
5317 element = EL_EM_GATE_4;
5320 case 0x1686: /* secret gate (blue) */
5321 element = EL_GATE_4_GRAY;
5324 case 0x1687: /* gate (green) */
5325 element = EL_EM_GATE_3;
5328 case 0x1688: /* secret gate (green) */
5329 element = EL_GATE_3_GRAY;
5332 case 0x1689: /* gate (white) */
5333 element = EL_DC_GATE_WHITE;
5336 case 0x168a: /* secret gate (white) */
5337 element = EL_DC_GATE_WHITE_GRAY;
5340 case 0x168b: /* secret gate (no key) */
5341 element = EL_DC_GATE_FAKE_GRAY;
5345 element = EL_ROBOT_WHEEL;
5349 element = EL_DC_TIMEGATE_SWITCH;
5353 element = EL_ACID_POOL_BOTTOM;
5357 element = EL_ACID_POOL_TOPLEFT;
5361 element = EL_ACID_POOL_TOPRIGHT;
5365 element = EL_ACID_POOL_BOTTOMLEFT;
5369 element = EL_ACID_POOL_BOTTOMRIGHT;
5373 element = EL_STEELWALL;
5377 element = EL_STEELWALL_SLIPPERY;
5380 case 0x1695: /* steel wall (not round) */
5381 element = EL_STEELWALL;
5384 case 0x1696: /* steel wall (left) */
5385 element = EL_DC_STEELWALL_1_LEFT;
5388 case 0x1697: /* steel wall (bottom) */
5389 element = EL_DC_STEELWALL_1_BOTTOM;
5392 case 0x1698: /* steel wall (right) */
5393 element = EL_DC_STEELWALL_1_RIGHT;
5396 case 0x1699: /* steel wall (top) */
5397 element = EL_DC_STEELWALL_1_TOP;
5400 case 0x169a: /* steel wall (left/bottom) */
5401 element = EL_DC_STEELWALL_1_BOTTOMLEFT;
5404 case 0x169b: /* steel wall (right/bottom) */
5405 element = EL_DC_STEELWALL_1_BOTTOMRIGHT;
5408 case 0x169c: /* steel wall (right/top) */
5409 element = EL_DC_STEELWALL_1_TOPRIGHT;
5412 case 0x169d: /* steel wall (left/top) */
5413 element = EL_DC_STEELWALL_1_TOPLEFT;
5416 case 0x169e: /* steel wall (right/bottom small) */
5417 element = EL_DC_STEELWALL_1_BOTTOMRIGHT_2;
5420 case 0x169f: /* steel wall (left/bottom small) */
5421 element = EL_DC_STEELWALL_1_BOTTOMLEFT_2;
5424 case 0x16a0: /* steel wall (right/top small) */
5425 element = EL_DC_STEELWALL_1_TOPRIGHT_2;
5428 case 0x16a1: /* steel wall (left/top small) */
5429 element = EL_DC_STEELWALL_1_TOPLEFT_2;
5432 case 0x16a2: /* steel wall (left/right) */
5433 element = EL_DC_STEELWALL_1_VERTICAL;
5436 case 0x16a3: /* steel wall (top/bottom) */
5437 element = EL_DC_STEELWALL_1_HORIZONTAL;
5440 case 0x16a4: /* steel wall 2 (left end) */
5441 element = EL_DC_STEELWALL_2_LEFT;
5444 case 0x16a5: /* steel wall 2 (right end) */
5445 element = EL_DC_STEELWALL_2_RIGHT;
5448 case 0x16a6: /* steel wall 2 (top end) */
5449 element = EL_DC_STEELWALL_2_TOP;
5452 case 0x16a7: /* steel wall 2 (bottom end) */
5453 element = EL_DC_STEELWALL_2_BOTTOM;
5456 case 0x16a8: /* steel wall 2 (left/right) */
5457 element = EL_DC_STEELWALL_2_HORIZONTAL;
5460 case 0x16a9: /* steel wall 2 (up/down) */
5461 element = EL_DC_STEELWALL_2_VERTICAL;
5464 case 0x16aa: /* steel wall 2 (mid) */
5465 element = EL_DC_STEELWALL_2_MIDDLE;
5469 element = EL_SIGN_EXCLAMATION;
5473 element = EL_SIGN_RADIOACTIVITY;
5477 element = EL_SIGN_STOP;
5481 element = EL_SIGN_WHEELCHAIR;
5485 element = EL_SIGN_PARKING;
5489 element = EL_SIGN_NO_ENTRY;
5493 element = EL_SIGN_HEART;
5497 element = EL_SIGN_GIVE_WAY;
5501 element = EL_SIGN_ENTRY_FORBIDDEN;
5505 element = EL_SIGN_EMERGENCY_EXIT;
5509 element = EL_SIGN_YIN_YANG;
5513 element = EL_WALL_EMERALD;
5517 element = EL_WALL_DIAMOND;
5521 element = EL_WALL_PEARL;
5525 element = EL_WALL_CRYSTAL;
5529 element = EL_INVISIBLE_WALL;
5533 element = EL_INVISIBLE_STEELWALL;
5536 /* 0x16bc - 0x16cb: */
5537 /* EL_INVISIBLE_SAND */
5540 element = EL_LIGHT_SWITCH;
5544 element = EL_ENVELOPE_1;
5548 if (element >= 0x0117 && element <= 0x036e) /* (?) */
5549 element = EL_DIAMOND;
5550 else if (element >= 0x042d && element <= 0x0684) /* (?) */
5551 element = EL_EMERALD;
5552 else if (element >= 0x157c && element <= 0x158b)
5554 else if (element >= 0x1590 && element <= 0x159f)
5555 element = EL_DC_LANDMINE;
5556 else if (element >= 0x16bc && element <= 0x16cb)
5557 element = EL_INVISIBLE_SAND;
5560 Error(ERR_WARN, "unknown Diamond Caves element 0x%04x", element);
5561 element = EL_UNKNOWN;
5566 return getMappedElement(element);
5569 static void LoadLevelFromFileStream_DC(File *file, struct LevelInfo *level,
5572 byte header[DC_LEVEL_HEADER_SIZE];
5574 int envelope_header_pos = 62;
5575 int envelope_content_pos = 94;
5576 int level_name_pos = 251;
5577 int level_author_pos = 292;
5578 int envelope_header_len;
5579 int envelope_content_len;
5581 int level_author_len;
5583 int num_yamyam_contents;
5586 getDecodedWord_DC(0, TRUE); /* initialize DC2 decoding engine */
5588 for (i = 0; i < DC_LEVEL_HEADER_SIZE / 2; i++)
5590 unsigned short header_word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
5592 header[i * 2 + 0] = header_word >> 8;
5593 header[i * 2 + 1] = header_word & 0xff;
5596 /* read some values from level header to check level decoding integrity */
5597 fieldx = header[6] | (header[7] << 8);
5598 fieldy = header[8] | (header[9] << 8);
5599 num_yamyam_contents = header[60] | (header[61] << 8);
5601 /* do some simple sanity checks to ensure that level was correctly decoded */
5602 if (fieldx < 1 || fieldx > 256 ||
5603 fieldy < 1 || fieldy > 256 ||
5604 num_yamyam_contents < 1 || num_yamyam_contents > 8)
5606 level->no_valid_file = TRUE;
5608 Error(ERR_WARN, "cannot decode level from stream -- using empty level");
5613 /* maximum envelope header size is 31 bytes */
5614 envelope_header_len = header[envelope_header_pos];
5615 /* maximum envelope content size is 110 (156?) bytes */
5616 envelope_content_len = header[envelope_content_pos];
5618 /* maximum level title size is 40 bytes */
5619 level_name_len = MIN(header[level_name_pos], MAX_LEVEL_NAME_LEN);
5620 /* maximum level author size is 30 (51?) bytes */
5621 level_author_len = MIN(header[level_author_pos], MAX_LEVEL_AUTHOR_LEN);
5625 for (i = 0; i < envelope_header_len; i++)
5626 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
5627 level->envelope[0].text[envelope_size++] =
5628 header[envelope_header_pos + 1 + i];
5630 if (envelope_header_len > 0 && envelope_content_len > 0)
5632 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
5633 level->envelope[0].text[envelope_size++] = '\n';
5634 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
5635 level->envelope[0].text[envelope_size++] = '\n';
5638 for (i = 0; i < envelope_content_len; i++)
5639 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
5640 level->envelope[0].text[envelope_size++] =
5641 header[envelope_content_pos + 1 + i];
5643 level->envelope[0].text[envelope_size] = '\0';
5645 level->envelope[0].xsize = MAX_ENVELOPE_XSIZE;
5646 level->envelope[0].ysize = 10;
5647 level->envelope[0].autowrap = TRUE;
5648 level->envelope[0].centered = TRUE;
5650 for (i = 0; i < level_name_len; i++)
5651 level->name[i] = header[level_name_pos + 1 + i];
5652 level->name[level_name_len] = '\0';
5654 for (i = 0; i < level_author_len; i++)
5655 level->author[i] = header[level_author_pos + 1 + i];
5656 level->author[level_author_len] = '\0';
5658 num_yamyam_contents = header[60] | (header[61] << 8);
5659 level->num_yamyam_contents =
5660 MIN(MAX(MIN_ELEMENT_CONTENTS, num_yamyam_contents), MAX_ELEMENT_CONTENTS);
5662 for (i = 0; i < num_yamyam_contents; i++)
5664 for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
5666 unsigned short word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
5667 int element_dc = ((word & 0xff) << 8) | ((word >> 8) & 0xff);
5669 if (i < MAX_ELEMENT_CONTENTS)
5670 level->yamyam_content[i].e[x][y] = getMappedElement_DC(element_dc);
5674 fieldx = header[6] | (header[7] << 8);
5675 fieldy = header[8] | (header[9] << 8);
5676 level->fieldx = MIN(MAX(MIN_LEV_FIELDX, fieldx), MAX_LEV_FIELDX);
5677 level->fieldy = MIN(MAX(MIN_LEV_FIELDY, fieldy), MAX_LEV_FIELDY);
5679 for (y = 0; y < fieldy; y++) for (x = 0; x < fieldx; x++)
5681 unsigned short word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
5682 int element_dc = ((word & 0xff) << 8) | ((word >> 8) & 0xff);
5684 if (x < MAX_LEV_FIELDX && y < MAX_LEV_FIELDY)
5685 level->field[x][y] = getMappedElement_DC(element_dc);
5688 x = MIN(MAX(0, (header[10] | (header[11] << 8)) - 1), MAX_LEV_FIELDX - 1);
5689 y = MIN(MAX(0, (header[12] | (header[13] << 8)) - 1), MAX_LEV_FIELDY - 1);
5690 level->field[x][y] = EL_PLAYER_1;
5692 x = MIN(MAX(0, (header[14] | (header[15] << 8)) - 1), MAX_LEV_FIELDX - 1);
5693 y = MIN(MAX(0, (header[16] | (header[17] << 8)) - 1), MAX_LEV_FIELDY - 1);
5694 level->field[x][y] = EL_PLAYER_2;
5696 level->gems_needed = header[18] | (header[19] << 8);
5698 level->score[SC_EMERALD] = header[20] | (header[21] << 8);
5699 level->score[SC_DIAMOND] = header[22] | (header[23] << 8);
5700 level->score[SC_PEARL] = header[24] | (header[25] << 8);
5701 level->score[SC_CRYSTAL] = header[26] | (header[27] << 8);
5702 level->score[SC_NUT] = header[28] | (header[29] << 8);
5703 level->score[SC_ROBOT] = header[30] | (header[31] << 8);
5704 level->score[SC_SPACESHIP] = header[32] | (header[33] << 8);
5705 level->score[SC_BUG] = header[34] | (header[35] << 8);
5706 level->score[SC_YAMYAM] = header[36] | (header[37] << 8);
5707 level->score[SC_DYNAMITE] = header[38] | (header[39] << 8);
5708 level->score[SC_KEY] = header[40] | (header[41] << 8);
5709 level->score[SC_TIME_BONUS] = header[42] | (header[43] << 8);
5711 level->time = header[44] | (header[45] << 8);
5713 level->amoeba_speed = header[46] | (header[47] << 8);
5714 level->time_light = header[48] | (header[49] << 8);
5715 level->time_timegate = header[50] | (header[51] << 8);
5716 level->time_wheel = header[52] | (header[53] << 8);
5717 level->time_magic_wall = header[54] | (header[55] << 8);
5718 level->extra_time = header[56] | (header[57] << 8);
5719 level->shield_normal_time = header[58] | (header[59] << 8);
5721 /* Diamond Caves has the same (strange) behaviour as Emerald Mine that gems
5722 can slip down from flat walls, like normal walls and steel walls */
5723 level->em_slippery_gems = TRUE;
5726 static void LoadLevelFromFileInfo_DC(struct LevelInfo *level,
5727 struct LevelFileInfo *level_file_info,
5728 boolean level_info_only)
5730 char *filename = level_file_info->filename;
5732 int num_magic_bytes = 8;
5733 char magic_bytes[num_magic_bytes + 1];
5734 int num_levels_to_skip = level_file_info->nr - leveldir_current->first_level;
5736 if (!(file = openFile(filename, MODE_READ)))
5738 level->no_valid_file = TRUE;
5740 if (!level_info_only)
5741 Error(ERR_WARN, "cannot read level '%s' -- using empty level", filename);
5746 // fseek(file, 0x0000, SEEK_SET);
5748 if (level_file_info->packed)
5750 /* read "magic bytes" from start of file */
5751 if (getStringFromFile(file, magic_bytes, num_magic_bytes + 1) == NULL)
5752 magic_bytes[0] = '\0';
5754 /* check "magic bytes" for correct file format */
5755 if (!strPrefix(magic_bytes, "DC2"))
5757 level->no_valid_file = TRUE;
5759 Error(ERR_WARN, "unknown DC level file '%s' -- using empty level",
5765 if (strPrefix(magic_bytes, "DC2Win95") ||
5766 strPrefix(magic_bytes, "DC2Win98"))
5768 int position_first_level = 0x00fa;
5769 int extra_bytes = 4;
5772 /* advance file stream to first level inside the level package */
5773 skip_bytes = position_first_level - num_magic_bytes - extra_bytes;
5775 /* each block of level data is followed by block of non-level data */
5776 num_levels_to_skip *= 2;
5778 /* at least skip header bytes, therefore use ">= 0" instead of "> 0" */
5779 while (num_levels_to_skip >= 0)
5781 /* advance file stream to next level inside the level package */
5782 if (seekFile(file, skip_bytes, SEEK_CUR) != 0)
5784 level->no_valid_file = TRUE;
5786 Error(ERR_WARN, "cannot fseek in file '%s' -- using empty level",
5792 /* skip apparently unused extra bytes following each level */
5793 ReadUnusedBytesFromFile(file, extra_bytes);
5795 /* read size of next level in level package */
5796 skip_bytes = getFile32BitLE(file);
5798 num_levels_to_skip--;
5803 level->no_valid_file = TRUE;
5805 Error(ERR_WARN, "unknown DC2 level file '%s' -- using empty level",
5812 LoadLevelFromFileStream_DC(file, level, level_file_info->nr);
5818 /* ------------------------------------------------------------------------- */
5819 /* functions for loading SB level */
5820 /* ------------------------------------------------------------------------- */
5822 int getMappedElement_SB(int element_ascii, boolean use_ces)
5830 sb_element_mapping[] =
5832 { ' ', EL_EMPTY, EL_CUSTOM_1 }, /* floor (space) */
5833 { '#', EL_STEELWALL, EL_CUSTOM_2 }, /* wall */
5834 { '@', EL_PLAYER_1, EL_CUSTOM_3 }, /* player */
5835 { '$', EL_SOKOBAN_OBJECT, EL_CUSTOM_4 }, /* box */
5836 { '.', EL_SOKOBAN_FIELD_EMPTY, EL_CUSTOM_5 }, /* goal square */
5837 { '*', EL_SOKOBAN_FIELD_FULL, EL_CUSTOM_6 }, /* box on goal square */
5838 { '+', EL_SOKOBAN_FIELD_PLAYER, EL_CUSTOM_7 }, /* player on goal square */
5839 { '_', EL_INVISIBLE_STEELWALL, EL_FROM_LEVEL_TEMPLATE }, /* floor beyond border */
5846 for (i = 0; sb_element_mapping[i].ascii != 0; i++)
5847 if (element_ascii == sb_element_mapping[i].ascii)
5848 return (use_ces ? sb_element_mapping[i].ce : sb_element_mapping[i].sb);
5850 return EL_UNDEFINED;
5853 static void LoadLevelFromFileInfo_SB(struct LevelInfo *level,
5854 struct LevelFileInfo *level_file_info,
5855 boolean level_info_only)
5857 char *filename = level_file_info->filename;
5858 char line[MAX_LINE_LEN], line_raw[MAX_LINE_LEN], previous_line[MAX_LINE_LEN];
5859 char last_comment[MAX_LINE_LEN];
5860 char level_name[MAX_LINE_LEN];
5863 int num_levels_to_skip = level_file_info->nr - leveldir_current->first_level;
5864 boolean read_continued_line = FALSE;
5865 boolean reading_playfield = FALSE;
5866 boolean got_valid_playfield_line = FALSE;
5867 boolean invalid_playfield_char = FALSE;
5868 boolean load_xsb_to_ces = check_special_flags("load_xsb_to_ces");
5869 int file_level_nr = 0;
5871 int x = 0, y = 0; /* initialized to make compilers happy */
5873 last_comment[0] = '\0';
5874 level_name[0] = '\0';
5876 if (!(file = openFile(filename, MODE_READ)))
5878 level->no_valid_file = TRUE;
5880 if (!level_info_only)
5881 Error(ERR_WARN, "cannot read level '%s' -- using empty level", filename);
5886 while (!checkEndOfFile(file))
5888 /* level successfully read, but next level may follow here */
5889 if (!got_valid_playfield_line && reading_playfield)
5891 /* read playfield from single level file -- skip remaining file */
5892 if (!level_file_info->packed)
5895 if (file_level_nr >= num_levels_to_skip)
5900 last_comment[0] = '\0';
5901 level_name[0] = '\0';
5903 reading_playfield = FALSE;
5906 got_valid_playfield_line = FALSE;
5908 /* read next line of input file */
5909 if (!getStringFromFile(file, line, MAX_LINE_LEN))
5912 /* check if line was completely read and is terminated by line break */
5913 if (strlen(line) > 0 && line[strlen(line) - 1] == '\n')
5916 /* cut trailing line break (this can be newline and/or carriage return) */
5917 for (line_ptr = &line[strlen(line)]; line_ptr >= line; line_ptr--)
5918 if ((*line_ptr == '\n' || *line_ptr == '\r') && *(line_ptr + 1) == '\0')
5921 /* copy raw input line for later use (mainly debugging output) */
5922 strcpy(line_raw, line);
5924 if (read_continued_line)
5926 /* append new line to existing line, if there is enough space */
5927 if (strlen(previous_line) + strlen(line_ptr) < MAX_LINE_LEN)
5928 strcat(previous_line, line_ptr);
5930 strcpy(line, previous_line); /* copy storage buffer to line */
5932 read_continued_line = FALSE;
5935 /* if the last character is '\', continue at next line */
5936 if (strlen(line) > 0 && line[strlen(line) - 1] == '\\')
5938 line[strlen(line) - 1] = '\0'; /* cut off trailing backslash */
5939 strcpy(previous_line, line); /* copy line to storage buffer */
5941 read_continued_line = TRUE;
5946 /* skip empty lines */
5947 if (line[0] == '\0')
5950 /* extract comment text from comment line */
5953 for (line_ptr = line; *line_ptr; line_ptr++)
5954 if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != ';')
5957 strcpy(last_comment, line_ptr);
5962 /* extract level title text from line containing level title */
5963 if (line[0] == '\'')
5965 strcpy(level_name, &line[1]);
5967 if (strlen(level_name) > 0 && level_name[strlen(level_name) - 1] == '\'')
5968 level_name[strlen(level_name) - 1] = '\0';
5973 /* skip lines containing only spaces (or empty lines) */
5974 for (line_ptr = line; *line_ptr; line_ptr++)
5975 if (*line_ptr != ' ')
5977 if (*line_ptr == '\0')
5980 /* at this point, we have found a line containing part of a playfield */
5982 got_valid_playfield_line = TRUE;
5984 if (!reading_playfield)
5986 reading_playfield = TRUE;
5987 invalid_playfield_char = FALSE;
5989 for (x = 0; x < MAX_LEV_FIELDX; x++)
5990 for (y = 0; y < MAX_LEV_FIELDY; y++)
5991 level->field[x][y] = getMappedElement_SB(' ', load_xsb_to_ces);
5996 /* start with topmost tile row */
6000 /* skip playfield line if larger row than allowed */
6001 if (y >= MAX_LEV_FIELDY)
6004 /* start with leftmost tile column */
6007 /* read playfield elements from line */
6008 for (line_ptr = line; *line_ptr; line_ptr++)
6010 int mapped_sb_element = getMappedElement_SB(*line_ptr, load_xsb_to_ces);
6012 /* stop parsing playfield line if larger column than allowed */
6013 if (x >= MAX_LEV_FIELDX)
6016 if (mapped_sb_element == EL_UNDEFINED)
6018 invalid_playfield_char = TRUE;
6023 level->field[x][y] = mapped_sb_element;
6025 /* continue with next tile column */
6028 level->fieldx = MAX(x, level->fieldx);
6031 if (invalid_playfield_char)
6033 /* if first playfield line, treat invalid lines as comment lines */
6035 reading_playfield = FALSE;
6040 /* continue with next tile row */
6048 level->fieldx = MIN(MAX(MIN_LEV_FIELDX, level->fieldx), MAX_LEV_FIELDX);
6049 level->fieldy = MIN(MAX(MIN_LEV_FIELDY, level->fieldy), MAX_LEV_FIELDY);
6051 if (!reading_playfield)
6053 level->no_valid_file = TRUE;
6055 Error(ERR_WARN, "cannot read level '%s' -- using empty level", filename);
6060 if (*level_name != '\0')
6062 strncpy(level->name, level_name, MAX_LEVEL_NAME_LEN);
6063 level->name[MAX_LEVEL_NAME_LEN] = '\0';
6065 else if (*last_comment != '\0')
6067 strncpy(level->name, last_comment, MAX_LEVEL_NAME_LEN);
6068 level->name[MAX_LEVEL_NAME_LEN] = '\0';
6072 sprintf(level->name, "--> Level %d <--", level_file_info->nr);
6075 /* set all empty fields beyond the border walls to invisible steel wall */
6076 for (y = 0; y < level->fieldy; y++) for (x = 0; x < level->fieldx; x++)
6078 if ((x == 0 || x == level->fieldx - 1 ||
6079 y == 0 || y == level->fieldy - 1) &&
6080 level->field[x][y] == getMappedElement_SB(' ', load_xsb_to_ces))
6081 FloodFillLevel(x, y, getMappedElement_SB('_', load_xsb_to_ces),
6082 level->field, level->fieldx, level->fieldy);
6085 /* set special level settings for Sokoban levels */
6088 level->use_step_counter = TRUE;
6090 if (load_xsb_to_ces)
6092 /* special global settings can now be set in level template */
6094 level->use_custom_template = TRUE;
6099 /* ------------------------------------------------------------------------- */
6100 /* functions for handling native levels */
6101 /* ------------------------------------------------------------------------- */
6103 static void LoadLevelFromFileInfo_EM(struct LevelInfo *level,
6104 struct LevelFileInfo *level_file_info,
6105 boolean level_info_only)
6107 if (!LoadNativeLevel_EM(level_file_info->filename, level_info_only))
6108 level->no_valid_file = TRUE;
6111 static void LoadLevelFromFileInfo_SP(struct LevelInfo *level,
6112 struct LevelFileInfo *level_file_info,
6113 boolean level_info_only)
6117 /* determine position of requested level inside level package */
6118 if (level_file_info->packed)
6119 pos = level_file_info->nr - leveldir_current->first_level;
6121 if (!LoadNativeLevel_SP(level_file_info->filename, pos, level_info_only))
6122 level->no_valid_file = TRUE;
6125 static void LoadLevelFromFileInfo_MM(struct LevelInfo *level,
6126 struct LevelFileInfo *level_file_info,
6127 boolean level_info_only)
6129 if (!LoadNativeLevel_MM(level_file_info->filename, level_info_only))
6130 level->no_valid_file = TRUE;
6133 void CopyNativeLevel_RND_to_Native(struct LevelInfo *level)
6135 if (level->game_engine_type == GAME_ENGINE_TYPE_EM)
6136 CopyNativeLevel_RND_to_EM(level);
6137 else if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
6138 CopyNativeLevel_RND_to_SP(level);
6139 else if (level->game_engine_type == GAME_ENGINE_TYPE_MM)
6140 CopyNativeLevel_RND_to_MM(level);
6143 void CopyNativeLevel_Native_to_RND(struct LevelInfo *level)
6145 if (level->game_engine_type == GAME_ENGINE_TYPE_EM)
6146 CopyNativeLevel_EM_to_RND(level);
6147 else if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
6148 CopyNativeLevel_SP_to_RND(level);
6149 else if (level->game_engine_type == GAME_ENGINE_TYPE_MM)
6150 CopyNativeLevel_MM_to_RND(level);
6153 void SaveNativeLevel(struct LevelInfo *level)
6155 if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
6157 char *basename = getSingleLevelBasenameExt(level->file_info.nr, "sp");
6158 char *filename = getLevelFilenameFromBasename(basename);
6160 CopyNativeLevel_RND_to_SP(level);
6161 CopyNativeTape_RND_to_SP(level);
6163 SaveNativeLevel_SP(filename);
6168 /* ------------------------------------------------------------------------- */
6169 /* functions for loading generic level */
6170 /* ------------------------------------------------------------------------- */
6172 static void LoadLevelFromFileInfo(struct LevelInfo *level,
6173 struct LevelFileInfo *level_file_info,
6174 boolean level_info_only)
6176 /* always start with reliable default values */
6177 setLevelInfoToDefaults(level, level_info_only, TRUE);
6179 switch (level_file_info->type)
6181 case LEVEL_FILE_TYPE_RND:
6182 LoadLevelFromFileInfo_RND(level, level_file_info, level_info_only);
6185 case LEVEL_FILE_TYPE_EM:
6186 LoadLevelFromFileInfo_EM(level, level_file_info, level_info_only);
6187 level->game_engine_type = GAME_ENGINE_TYPE_EM;
6190 case LEVEL_FILE_TYPE_SP:
6191 LoadLevelFromFileInfo_SP(level, level_file_info, level_info_only);
6192 level->game_engine_type = GAME_ENGINE_TYPE_SP;
6195 case LEVEL_FILE_TYPE_MM:
6196 LoadLevelFromFileInfo_MM(level, level_file_info, level_info_only);
6197 level->game_engine_type = GAME_ENGINE_TYPE_MM;
6200 case LEVEL_FILE_TYPE_DC:
6201 LoadLevelFromFileInfo_DC(level, level_file_info, level_info_only);
6204 case LEVEL_FILE_TYPE_SB:
6205 LoadLevelFromFileInfo_SB(level, level_file_info, level_info_only);
6209 LoadLevelFromFileInfo_RND(level, level_file_info, level_info_only);
6213 /* if level file is invalid, restore level structure to default values */
6214 if (level->no_valid_file)
6215 setLevelInfoToDefaults(level, level_info_only, FALSE);
6217 if (level->game_engine_type == GAME_ENGINE_TYPE_UNKNOWN)
6218 level->game_engine_type = GAME_ENGINE_TYPE_RND;
6220 if (level_file_info->type != LEVEL_FILE_TYPE_RND)
6221 CopyNativeLevel_Native_to_RND(level);
6224 void LoadLevelFromFilename(struct LevelInfo *level, char *filename)
6226 static struct LevelFileInfo level_file_info;
6228 /* always start with reliable default values */
6229 setFileInfoToDefaults(&level_file_info);
6231 level_file_info.nr = 0; /* unknown level number */
6232 level_file_info.type = LEVEL_FILE_TYPE_RND; /* no others supported yet */
6234 setString(&level_file_info.filename, filename);
6236 LoadLevelFromFileInfo(level, &level_file_info, FALSE);
6239 static void LoadLevel_InitVersion(struct LevelInfo *level)
6243 if (leveldir_current == NULL) /* only when dumping level */
6246 /* all engine modifications also valid for levels which use latest engine */
6247 if (level->game_version < VERSION_IDENT(3,2,0,5))
6249 /* time bonus score was given for 10 s instead of 1 s before 3.2.0-5 */
6250 level->score[SC_TIME_BONUS] /= 10;
6253 if (leveldir_current->latest_engine)
6255 /* ---------- use latest game engine ----------------------------------- */
6257 /* For all levels which are forced to use the latest game engine version
6258 (normally all but user contributed, private and undefined levels), set
6259 the game engine version to the actual version; this allows for actual
6260 corrections in the game engine to take effect for existing, converted
6261 levels (from "classic" or other existing games) to make the emulation
6262 of the corresponding game more accurate, while (hopefully) not breaking
6263 existing levels created from other players. */
6265 level->game_version = GAME_VERSION_ACTUAL;
6267 /* Set special EM style gems behaviour: EM style gems slip down from
6268 normal, steel and growing wall. As this is a more fundamental change,
6269 it seems better to set the default behaviour to "off" (as it is more
6270 natural) and make it configurable in the level editor (as a property
6271 of gem style elements). Already existing converted levels (neither
6272 private nor contributed levels) are changed to the new behaviour. */
6274 if (level->file_version < FILE_VERSION_2_0)
6275 level->em_slippery_gems = TRUE;
6280 /* ---------- use game engine the level was created with ----------------- */
6282 /* For all levels which are not forced to use the latest game engine
6283 version (normally user contributed, private and undefined levels),
6284 use the version of the game engine the levels were created for.
6286 Since 2.0.1, the game engine version is now directly stored
6287 in the level file (chunk "VERS"), so there is no need anymore
6288 to set the game version from the file version (except for old,
6289 pre-2.0 levels, where the game version is still taken from the
6290 file format version used to store the level -- see above). */
6292 /* player was faster than enemies in 1.0.0 and before */
6293 if (level->file_version == FILE_VERSION_1_0)
6294 for (i = 0; i < MAX_PLAYERS; i++)
6295 level->initial_player_stepsize[i] = STEPSIZE_FAST;
6297 /* default behaviour for EM style gems was "slippery" only in 2.0.1 */
6298 if (level->game_version == VERSION_IDENT(2,0,1,0))
6299 level->em_slippery_gems = TRUE;
6301 /* springs could be pushed over pits before (pre-release version) 2.2.0 */
6302 if (level->game_version < VERSION_IDENT(2,2,0,0))
6303 level->use_spring_bug = TRUE;
6305 if (level->game_version < VERSION_IDENT(3,2,0,5))
6307 /* time orb caused limited time in endless time levels before 3.2.0-5 */
6308 level->use_time_orb_bug = TRUE;
6310 /* default behaviour for snapping was "no snap delay" before 3.2.0-5 */
6311 level->block_snap_field = FALSE;
6313 /* extra time score was same value as time left score before 3.2.0-5 */
6314 level->extra_time_score = level->score[SC_TIME_BONUS];
6317 /* game logic of "game of life" and "biomaze" was buggy before 4.1.1.1 */
6318 if (level->game_version < VERSION_IDENT(4,1,1,1))
6319 level->use_life_bugs = TRUE;
6321 if (level->game_version < VERSION_IDENT(3,2,0,7))
6323 /* default behaviour for snapping was "not continuous" before 3.2.0-7 */
6324 level->continuous_snapping = FALSE;
6327 /* only few elements were able to actively move into acid before 3.1.0 */
6328 /* trigger settings did not exist before 3.1.0; set to default "any" */
6329 if (level->game_version < VERSION_IDENT(3,1,0,0))
6331 /* correct "can move into acid" settings (all zero in old levels) */
6333 level->can_move_into_acid_bits = 0; /* nothing can move into acid */
6334 level->dont_collide_with_bits = 0; /* nothing is deadly when colliding */
6336 setMoveIntoAcidProperty(level, EL_ROBOT, TRUE);
6337 setMoveIntoAcidProperty(level, EL_SATELLITE, TRUE);
6338 setMoveIntoAcidProperty(level, EL_PENGUIN, TRUE);
6339 setMoveIntoAcidProperty(level, EL_BALLOON, TRUE);
6341 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6342 SET_PROPERTY(EL_CUSTOM_START + i, EP_CAN_MOVE_INTO_ACID, TRUE);
6344 /* correct trigger settings (stored as zero == "none" in old levels) */
6346 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6348 int element = EL_CUSTOM_START + i;
6349 struct ElementInfo *ei = &element_info[element];
6351 for (j = 0; j < ei->num_change_pages; j++)
6353 struct ElementChangeInfo *change = &ei->change_page[j];
6355 change->trigger_player = CH_PLAYER_ANY;
6356 change->trigger_page = CH_PAGE_ANY;
6361 /* try to detect and fix "Snake Bite" levels, which are broken with 3.2.0 */
6363 int element = EL_CUSTOM_256;
6364 struct ElementInfo *ei = &element_info[element];
6365 struct ElementChangeInfo *change = &ei->change_page[0];
6367 /* This is needed to fix a problem that was caused by a bugfix in function
6368 game.c/CreateFieldExt() introduced with 3.2.0 that corrects the behaviour
6369 when a custom element changes to EL_SOKOBAN_FIELD_PLAYER (before, it did
6370 not replace walkable elements, but instead just placed the player on it,
6371 without placing the Sokoban field under the player). Unfortunately, this
6372 breaks "Snake Bite" style levels when the snake is halfway through a door
6373 that just closes (the snake head is still alive and can be moved in this
6374 case). This can be fixed by replacing the EL_SOKOBAN_FIELD_PLAYER by the
6375 player (without Sokoban element) which then gets killed as designed). */
6377 if ((strncmp(leveldir_current->identifier, "snake_bite", 10) == 0 ||
6378 strncmp(ei->description, "pause b4 death", 14) == 0) &&
6379 change->target_element == EL_SOKOBAN_FIELD_PLAYER)
6380 change->target_element = EL_PLAYER_1;
6383 /* try to detect and fix "Zelda" style levels, which are broken with 3.2.5 */
6384 if (level->game_version < VERSION_IDENT(3,2,5,0))
6386 /* This is needed to fix a problem that was caused by a bugfix in function
6387 game.c/CheckTriggeredElementChangeExt() introduced with 3.2.5 that
6388 corrects the behaviour when a custom element changes to another custom
6389 element with a higher element number that has change actions defined.
6390 Normally, only one change per frame is allowed for custom elements.
6391 Therefore, it is checked if a custom element already changed in the
6392 current frame; if it did, subsequent changes are suppressed.
6393 Unfortunately, this is only checked for element changes, but not for
6394 change actions, which are still executed. As the function above loops
6395 through all custom elements from lower to higher, an element change
6396 resulting in a lower CE number won't be checked again, while a target
6397 element with a higher number will also be checked, and potential change
6398 actions will get executed for this CE, too (which is wrong), while
6399 further changes are ignored (which is correct). As this bugfix breaks
6400 Zelda II (and introduces graphical bugs to Zelda I, and also breaks a
6401 few other levels like Alan Bond's "FMV"), allow the previous, incorrect
6402 behaviour for existing levels and tapes that make use of this bug */
6404 level->use_action_after_change_bug = TRUE;
6407 /* not centering level after relocating player was default only in 3.2.3 */
6408 if (level->game_version == VERSION_IDENT(3,2,3,0)) /* (no pre-releases) */
6409 level->shifted_relocation = TRUE;
6411 /* EM style elements always chain-exploded in R'n'D engine before 3.2.6 */
6412 if (level->game_version < VERSION_IDENT(3,2,6,0))
6413 level->em_explodes_by_fire = TRUE;
6415 /* levels were solved by the first player entering an exit up to 4.1.0.0 */
6416 if (level->game_version <= VERSION_IDENT(4,1,0,0))
6417 level->solved_by_one_player = TRUE;
6420 static void LoadLevel_InitStandardElements(struct LevelInfo *level)
6424 /* map elements that have changed in newer versions */
6425 level->amoeba_content = getMappedElementByVersion(level->amoeba_content,
6426 level->game_version);
6427 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
6428 for (x = 0; x < 3; x++)
6429 for (y = 0; y < 3; y++)
6430 level->yamyam_content[i].e[x][y] =
6431 getMappedElementByVersion(level->yamyam_content[i].e[x][y],
6432 level->game_version);
6436 static void LoadLevel_InitCustomElements(struct LevelInfo *level)
6440 /* map custom element change events that have changed in newer versions
6441 (these following values were accidentally changed in version 3.0.1)
6442 (this seems to be needed only for 'ab_levelset3' and 'ab_levelset4') */
6443 if (level->game_version <= VERSION_IDENT(3,0,0,0))
6445 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6447 int element = EL_CUSTOM_START + i;
6449 /* order of checking and copying events to be mapped is important */
6450 /* (do not change the start and end value -- they are constant) */
6451 for (j = CE_BY_OTHER_ACTION; j >= CE_VALUE_GETS_ZERO; j--)
6453 if (HAS_CHANGE_EVENT(element, j - 2))
6455 SET_CHANGE_EVENT(element, j - 2, FALSE);
6456 SET_CHANGE_EVENT(element, j, TRUE);
6460 /* order of checking and copying events to be mapped is important */
6461 /* (do not change the start and end value -- they are constant) */
6462 for (j = CE_PLAYER_COLLECTS_X; j >= CE_HITTING_SOMETHING; j--)
6464 if (HAS_CHANGE_EVENT(element, j - 1))
6466 SET_CHANGE_EVENT(element, j - 1, FALSE);
6467 SET_CHANGE_EVENT(element, j, TRUE);
6473 /* initialize "can_change" field for old levels with only one change page */
6474 if (level->game_version <= VERSION_IDENT(3,0,2,0))
6476 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6478 int element = EL_CUSTOM_START + i;
6480 if (CAN_CHANGE(element))
6481 element_info[element].change->can_change = TRUE;
6485 /* correct custom element values (for old levels without these options) */
6486 if (level->game_version < VERSION_IDENT(3,1,1,0))
6488 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6490 int element = EL_CUSTOM_START + i;
6491 struct ElementInfo *ei = &element_info[element];
6493 if (ei->access_direction == MV_NO_DIRECTION)
6494 ei->access_direction = MV_ALL_DIRECTIONS;
6498 /* correct custom element values (fix invalid values for all versions) */
6501 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6503 int element = EL_CUSTOM_START + i;
6504 struct ElementInfo *ei = &element_info[element];
6506 for (j = 0; j < ei->num_change_pages; j++)
6508 struct ElementChangeInfo *change = &ei->change_page[j];
6510 if (change->trigger_player == CH_PLAYER_NONE)
6511 change->trigger_player = CH_PLAYER_ANY;
6513 if (change->trigger_side == CH_SIDE_NONE)
6514 change->trigger_side = CH_SIDE_ANY;
6519 /* initialize "can_explode" field for old levels which did not store this */
6520 /* !!! CHECK THIS -- "<= 3,1,0,0" IS PROBABLY WRONG !!! */
6521 if (level->game_version <= VERSION_IDENT(3,1,0,0))
6523 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6525 int element = EL_CUSTOM_START + i;
6527 if (EXPLODES_1X1_OLD(element))
6528 element_info[element].explosion_type = EXPLODES_1X1;
6530 SET_PROPERTY(element, EP_CAN_EXPLODE, (EXPLODES_BY_FIRE(element) ||
6531 EXPLODES_SMASHED(element) ||
6532 EXPLODES_IMPACT(element)));
6536 /* correct previously hard-coded move delay values for maze runner style */
6537 if (level->game_version < VERSION_IDENT(3,1,1,0))
6539 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6541 int element = EL_CUSTOM_START + i;
6543 if (element_info[element].move_pattern & MV_MAZE_RUNNER_STYLE)
6545 /* previously hard-coded and therefore ignored */
6546 element_info[element].move_delay_fixed = 9;
6547 element_info[element].move_delay_random = 0;
6552 /* set some other uninitialized values of custom elements in older levels */
6553 if (level->game_version < VERSION_IDENT(3,1,0,0))
6555 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6557 int element = EL_CUSTOM_START + i;
6559 element_info[element].access_direction = MV_ALL_DIRECTIONS;
6561 element_info[element].explosion_delay = 17;
6562 element_info[element].ignition_delay = 8;
6567 static void LoadLevel_InitElements(struct LevelInfo *level)
6569 LoadLevel_InitStandardElements(level);
6571 if (level->file_has_custom_elements)
6572 LoadLevel_InitCustomElements(level);
6574 /* initialize element properties for level editor etc. */
6575 InitElementPropertiesEngine(level->game_version);
6576 InitElementPropertiesGfxElement();
6579 static void LoadLevel_InitPlayfield(struct LevelInfo *level)
6583 /* map elements that have changed in newer versions */
6584 for (y = 0; y < level->fieldy; y++)
6585 for (x = 0; x < level->fieldx; x++)
6586 level->field[x][y] = getMappedElementByVersion(level->field[x][y],
6587 level->game_version);
6589 /* clear unused playfield data (nicer if level gets resized in editor) */
6590 for (x = 0; x < MAX_LEV_FIELDX; x++)
6591 for (y = 0; y < MAX_LEV_FIELDY; y++)
6592 if (x >= level->fieldx || y >= level->fieldy)
6593 level->field[x][y] = EL_EMPTY;
6595 /* copy elements to runtime playfield array */
6596 for (x = 0; x < MAX_LEV_FIELDX; x++)
6597 for (y = 0; y < MAX_LEV_FIELDY; y++)
6598 Feld[x][y] = level->field[x][y];
6600 /* initialize level size variables for faster access */
6601 lev_fieldx = level->fieldx;
6602 lev_fieldy = level->fieldy;
6604 /* determine border element for this level */
6605 if (level->file_info.type == LEVEL_FILE_TYPE_DC)
6606 BorderElement = EL_EMPTY; /* (in editor, SetBorderElement() is used) */
6611 static void LoadLevel_InitNativeEngines(struct LevelInfo *level)
6613 struct LevelFileInfo *level_file_info = &level->file_info;
6615 if (level_file_info->type == LEVEL_FILE_TYPE_RND)
6616 CopyNativeLevel_RND_to_Native(level);
6619 static void LoadLevelTemplate_LoadAndInit(void)
6621 LoadLevelFromFileInfo(&level_template, &level_template.file_info, FALSE);
6623 LoadLevel_InitVersion(&level_template);
6624 LoadLevel_InitElements(&level_template);
6626 ActivateLevelTemplate();
6629 void LoadLevelTemplate(int nr)
6631 if (!fileExists(getGlobalLevelTemplateFilename()))
6633 Error(ERR_WARN, "no level template found for this level");
6638 setLevelFileInfo(&level_template.file_info, nr);
6640 LoadLevelTemplate_LoadAndInit();
6643 static void LoadNetworkLevelTemplate(struct NetworkLevelInfo *network_level)
6645 copyLevelFileInfo(&network_level->tmpl_info, &level_template.file_info);
6647 LoadLevelTemplate_LoadAndInit();
6650 static void LoadLevel_LoadAndInit(struct NetworkLevelInfo *network_level)
6652 LoadLevelFromFileInfo(&level, &level.file_info, FALSE);
6654 if (level.use_custom_template)
6656 if (network_level != NULL)
6657 LoadNetworkLevelTemplate(network_level);
6659 LoadLevelTemplate(-1);
6662 LoadLevel_InitVersion(&level);
6663 LoadLevel_InitElements(&level);
6664 LoadLevel_InitPlayfield(&level);
6666 LoadLevel_InitNativeEngines(&level);
6669 void LoadLevel(int nr)
6671 SetLevelSetInfo(leveldir_current->identifier, nr);
6673 setLevelFileInfo(&level.file_info, nr);
6675 LoadLevel_LoadAndInit(NULL);
6678 void LoadLevelInfoOnly(int nr)
6680 setLevelFileInfo(&level.file_info, nr);
6682 LoadLevelFromFileInfo(&level, &level.file_info, TRUE);
6685 void LoadNetworkLevel(struct NetworkLevelInfo *network_level)
6687 SetLevelSetInfo(network_level->leveldir_identifier,
6688 network_level->file_info.nr);
6690 copyLevelFileInfo(&network_level->file_info, &level.file_info);
6692 LoadLevel_LoadAndInit(network_level);
6695 static int SaveLevel_VERS(FILE *file, struct LevelInfo *level)
6699 chunk_size += putFileVersion(file, level->file_version);
6700 chunk_size += putFileVersion(file, level->game_version);
6705 static int SaveLevel_DATE(FILE *file, struct LevelInfo *level)
6709 chunk_size += putFile16BitBE(file, level->creation_date.year);
6710 chunk_size += putFile8Bit(file, level->creation_date.month);
6711 chunk_size += putFile8Bit(file, level->creation_date.day);
6716 #if ENABLE_HISTORIC_CHUNKS
6717 static void SaveLevel_HEAD(FILE *file, struct LevelInfo *level)
6721 putFile8Bit(file, level->fieldx);
6722 putFile8Bit(file, level->fieldy);
6724 putFile16BitBE(file, level->time);
6725 putFile16BitBE(file, level->gems_needed);
6727 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
6728 putFile8Bit(file, level->name[i]);
6730 for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
6731 putFile8Bit(file, level->score[i]);
6733 for (i = 0; i < STD_ELEMENT_CONTENTS; i++)
6734 for (y = 0; y < 3; y++)
6735 for (x = 0; x < 3; x++)
6736 putFile8Bit(file, (level->encoding_16bit_yamyam ? EL_EMPTY :
6737 level->yamyam_content[i].e[x][y]));
6738 putFile8Bit(file, level->amoeba_speed);
6739 putFile8Bit(file, level->time_magic_wall);
6740 putFile8Bit(file, level->time_wheel);
6741 putFile8Bit(file, (level->encoding_16bit_amoeba ? EL_EMPTY :
6742 level->amoeba_content));
6743 putFile8Bit(file, (level->initial_player_stepsize == STEPSIZE_FAST ? 1 : 0));
6744 putFile8Bit(file, (level->initial_gravity ? 1 : 0));
6745 putFile8Bit(file, (level->encoding_16bit_field ? 1 : 0));
6746 putFile8Bit(file, (level->em_slippery_gems ? 1 : 0));
6748 putFile8Bit(file, (level->use_custom_template ? 1 : 0));
6750 putFile8Bit(file, (level->block_last_field ? 1 : 0));
6751 putFile8Bit(file, (level->sp_block_last_field ? 1 : 0));
6752 putFile32BitBE(file, level->can_move_into_acid_bits);
6753 putFile8Bit(file, level->dont_collide_with_bits);
6755 putFile8Bit(file, (level->use_spring_bug ? 1 : 0));
6756 putFile8Bit(file, (level->use_step_counter ? 1 : 0));
6758 putFile8Bit(file, (level->instant_relocation ? 1 : 0));
6759 putFile8Bit(file, (level->can_pass_to_walkable ? 1 : 0));
6760 putFile8Bit(file, (level->grow_into_diggable ? 1 : 0));
6762 putFile8Bit(file, level->game_engine_type);
6764 WriteUnusedBytesToFile(file, LEVEL_CHUNK_HEAD_UNUSED);
6768 static int SaveLevel_NAME(FILE *file, struct LevelInfo *level)
6773 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
6774 chunk_size += putFile8Bit(file, level->name[i]);
6779 static int SaveLevel_AUTH(FILE *file, struct LevelInfo *level)
6784 for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
6785 chunk_size += putFile8Bit(file, level->author[i]);
6790 #if ENABLE_HISTORIC_CHUNKS
6791 static int SaveLevel_BODY(FILE *file, struct LevelInfo *level)
6796 for (y = 0; y < level->fieldy; y++)
6797 for (x = 0; x < level->fieldx; x++)
6798 if (level->encoding_16bit_field)
6799 chunk_size += putFile16BitBE(file, level->field[x][y]);
6801 chunk_size += putFile8Bit(file, level->field[x][y]);
6807 static int SaveLevel_BODY(FILE *file, struct LevelInfo *level)
6812 for (y = 0; y < level->fieldy; y++)
6813 for (x = 0; x < level->fieldx; x++)
6814 chunk_size += putFile16BitBE(file, level->field[x][y]);
6819 #if ENABLE_HISTORIC_CHUNKS
6820 static void SaveLevel_CONT(FILE *file, struct LevelInfo *level)
6824 putFile8Bit(file, EL_YAMYAM);
6825 putFile8Bit(file, level->num_yamyam_contents);
6826 putFile8Bit(file, 0);
6827 putFile8Bit(file, 0);
6829 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
6830 for (y = 0; y < 3; y++)
6831 for (x = 0; x < 3; x++)
6832 if (level->encoding_16bit_field)
6833 putFile16BitBE(file, level->yamyam_content[i].e[x][y]);
6835 putFile8Bit(file, level->yamyam_content[i].e[x][y]);
6839 #if ENABLE_HISTORIC_CHUNKS
6840 static void SaveLevel_CNT2(FILE *file, struct LevelInfo *level, int element)
6843 int num_contents, content_xsize, content_ysize;
6844 int content_array[MAX_ELEMENT_CONTENTS][3][3];
6846 if (element == EL_YAMYAM)
6848 num_contents = level->num_yamyam_contents;
6852 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
6853 for (y = 0; y < 3; y++)
6854 for (x = 0; x < 3; x++)
6855 content_array[i][x][y] = level->yamyam_content[i].e[x][y];
6857 else if (element == EL_BD_AMOEBA)
6863 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
6864 for (y = 0; y < 3; y++)
6865 for (x = 0; x < 3; x++)
6866 content_array[i][x][y] = EL_EMPTY;
6867 content_array[0][0][0] = level->amoeba_content;
6871 /* chunk header already written -- write empty chunk data */
6872 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_SIZE);
6874 Error(ERR_WARN, "cannot save content for element '%d'", element);
6878 putFile16BitBE(file, element);
6879 putFile8Bit(file, num_contents);
6880 putFile8Bit(file, content_xsize);
6881 putFile8Bit(file, content_ysize);
6883 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_UNUSED);
6885 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
6886 for (y = 0; y < 3; y++)
6887 for (x = 0; x < 3; x++)
6888 putFile16BitBE(file, content_array[i][x][y]);
6892 #if ENABLE_HISTORIC_CHUNKS
6893 static int SaveLevel_CNT3(FILE *file, struct LevelInfo *level, int element)
6895 int envelope_nr = element - EL_ENVELOPE_1;
6896 int envelope_len = strlen(level->envelope_text[envelope_nr]) + 1;
6900 chunk_size += putFile16BitBE(file, element);
6901 chunk_size += putFile16BitBE(file, envelope_len);
6902 chunk_size += putFile8Bit(file, level->envelope_xsize[envelope_nr]);
6903 chunk_size += putFile8Bit(file, level->envelope_ysize[envelope_nr]);
6905 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT3_UNUSED);
6906 chunk_size += LEVEL_CHUNK_CNT3_UNUSED;
6908 for (i = 0; i < envelope_len; i++)
6909 chunk_size += putFile8Bit(file, level->envelope_text[envelope_nr][i]);
6915 #if ENABLE_HISTORIC_CHUNKS
6916 static void SaveLevel_CUS1(FILE *file, struct LevelInfo *level,
6917 int num_changed_custom_elements)
6921 putFile16BitBE(file, num_changed_custom_elements);
6923 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6925 int element = EL_CUSTOM_START + i;
6927 struct ElementInfo *ei = &element_info[element];
6929 if (ei->properties[EP_BITFIELD_BASE_NR] != EP_BITMASK_DEFAULT)
6931 if (check < num_changed_custom_elements)
6933 putFile16BitBE(file, element);
6934 putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
6941 if (check != num_changed_custom_elements) /* should not happen */
6942 Error(ERR_WARN, "inconsistent number of custom element properties");
6946 #if ENABLE_HISTORIC_CHUNKS
6947 static void SaveLevel_CUS2(FILE *file, struct LevelInfo *level,
6948 int num_changed_custom_elements)
6952 putFile16BitBE(file, num_changed_custom_elements);
6954 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6956 int element = EL_CUSTOM_START + i;
6958 if (element_info[element].change->target_element != EL_EMPTY_SPACE)
6960 if (check < num_changed_custom_elements)
6962 putFile16BitBE(file, element);
6963 putFile16BitBE(file, element_info[element].change->target_element);
6970 if (check != num_changed_custom_elements) /* should not happen */
6971 Error(ERR_WARN, "inconsistent number of custom target elements");
6975 #if ENABLE_HISTORIC_CHUNKS
6976 static void SaveLevel_CUS3(FILE *file, struct LevelInfo *level,
6977 int num_changed_custom_elements)
6979 int i, j, x, y, check = 0;
6981 putFile16BitBE(file, num_changed_custom_elements);
6983 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6985 int element = EL_CUSTOM_START + i;
6986 struct ElementInfo *ei = &element_info[element];
6988 if (ei->modified_settings)
6990 if (check < num_changed_custom_elements)
6992 putFile16BitBE(file, element);
6994 for (j = 0; j < MAX_ELEMENT_NAME_LEN; j++)
6995 putFile8Bit(file, ei->description[j]);
6997 putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
6999 /* some free bytes for future properties and padding */
7000 WriteUnusedBytesToFile(file, 7);
7002 putFile8Bit(file, ei->use_gfx_element);
7003 putFile16BitBE(file, ei->gfx_element_initial);
7005 putFile8Bit(file, ei->collect_score_initial);
7006 putFile8Bit(file, ei->collect_count_initial);
7008 putFile16BitBE(file, ei->push_delay_fixed);
7009 putFile16BitBE(file, ei->push_delay_random);
7010 putFile16BitBE(file, ei->move_delay_fixed);
7011 putFile16BitBE(file, ei->move_delay_random);
7013 putFile16BitBE(file, ei->move_pattern);
7014 putFile8Bit(file, ei->move_direction_initial);
7015 putFile8Bit(file, ei->move_stepsize);
7017 for (y = 0; y < 3; y++)
7018 for (x = 0; x < 3; x++)
7019 putFile16BitBE(file, ei->content.e[x][y]);
7021 putFile32BitBE(file, ei->change->events);
7023 putFile16BitBE(file, ei->change->target_element);
7025 putFile16BitBE(file, ei->change->delay_fixed);
7026 putFile16BitBE(file, ei->change->delay_random);
7027 putFile16BitBE(file, ei->change->delay_frames);
7029 putFile16BitBE(file, ei->change->initial_trigger_element);
7031 putFile8Bit(file, ei->change->explode);
7032 putFile8Bit(file, ei->change->use_target_content);
7033 putFile8Bit(file, ei->change->only_if_complete);
7034 putFile8Bit(file, ei->change->use_random_replace);
7036 putFile8Bit(file, ei->change->random_percentage);
7037 putFile8Bit(file, ei->change->replace_when);
7039 for (y = 0; y < 3; y++)
7040 for (x = 0; x < 3; x++)
7041 putFile16BitBE(file, ei->change->content.e[x][y]);
7043 putFile8Bit(file, ei->slippery_type);
7045 /* some free bytes for future properties and padding */
7046 WriteUnusedBytesToFile(file, LEVEL_CPART_CUS3_UNUSED);
7053 if (check != num_changed_custom_elements) /* should not happen */
7054 Error(ERR_WARN, "inconsistent number of custom element properties");
7058 #if ENABLE_HISTORIC_CHUNKS
7059 static void SaveLevel_CUS4(FILE *file, struct LevelInfo *level, int element)
7061 struct ElementInfo *ei = &element_info[element];
7064 /* ---------- custom element base property values (96 bytes) ------------- */
7066 putFile16BitBE(file, element);
7068 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
7069 putFile8Bit(file, ei->description[i]);
7071 putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
7073 WriteUnusedBytesToFile(file, 4); /* reserved for more base properties */
7075 putFile8Bit(file, ei->num_change_pages);
7077 putFile16BitBE(file, ei->ce_value_fixed_initial);
7078 putFile16BitBE(file, ei->ce_value_random_initial);
7079 putFile8Bit(file, ei->use_last_ce_value);
7081 putFile8Bit(file, ei->use_gfx_element);
7082 putFile16BitBE(file, ei->gfx_element_initial);
7084 putFile8Bit(file, ei->collect_score_initial);
7085 putFile8Bit(file, ei->collect_count_initial);
7087 putFile8Bit(file, ei->drop_delay_fixed);
7088 putFile8Bit(file, ei->push_delay_fixed);
7089 putFile8Bit(file, ei->drop_delay_random);
7090 putFile8Bit(file, ei->push_delay_random);
7091 putFile16BitBE(file, ei->move_delay_fixed);
7092 putFile16BitBE(file, ei->move_delay_random);
7094 /* bits 0 - 15 of "move_pattern" ... */
7095 putFile16BitBE(file, ei->move_pattern & 0xffff);
7096 putFile8Bit(file, ei->move_direction_initial);
7097 putFile8Bit(file, ei->move_stepsize);
7099 putFile8Bit(file, ei->slippery_type);
7101 for (y = 0; y < 3; y++)
7102 for (x = 0; x < 3; x++)
7103 putFile16BitBE(file, ei->content.e[x][y]);
7105 putFile16BitBE(file, ei->move_enter_element);
7106 putFile16BitBE(file, ei->move_leave_element);
7107 putFile8Bit(file, ei->move_leave_type);
7109 /* ... bits 16 - 31 of "move_pattern" (not nice, but downward compatible) */
7110 putFile16BitBE(file, (ei->move_pattern >> 16) & 0xffff);
7112 putFile8Bit(file, ei->access_direction);
7114 putFile8Bit(file, ei->explosion_delay);
7115 putFile8Bit(file, ei->ignition_delay);
7116 putFile8Bit(file, ei->explosion_type);
7118 /* some free bytes for future custom property values and padding */
7119 WriteUnusedBytesToFile(file, 1);
7121 /* ---------- change page property values (48 bytes) --------------------- */
7123 for (i = 0; i < ei->num_change_pages; i++)
7125 struct ElementChangeInfo *change = &ei->change_page[i];
7126 unsigned int event_bits;
7128 /* bits 0 - 31 of "has_event[]" ... */
7130 for (j = 0; j < MIN(NUM_CHANGE_EVENTS, 32); j++)
7131 if (change->has_event[j])
7132 event_bits |= (1 << j);
7133 putFile32BitBE(file, event_bits);
7135 putFile16BitBE(file, change->target_element);
7137 putFile16BitBE(file, change->delay_fixed);
7138 putFile16BitBE(file, change->delay_random);
7139 putFile16BitBE(file, change->delay_frames);
7141 putFile16BitBE(file, change->initial_trigger_element);
7143 putFile8Bit(file, change->explode);
7144 putFile8Bit(file, change->use_target_content);
7145 putFile8Bit(file, change->only_if_complete);
7146 putFile8Bit(file, change->use_random_replace);
7148 putFile8Bit(file, change->random_percentage);
7149 putFile8Bit(file, change->replace_when);
7151 for (y = 0; y < 3; y++)
7152 for (x = 0; x < 3; x++)
7153 putFile16BitBE(file, change->target_content.e[x][y]);
7155 putFile8Bit(file, change->can_change);
7157 putFile8Bit(file, change->trigger_side);
7159 putFile8Bit(file, change->trigger_player);
7160 putFile8Bit(file, (change->trigger_page == CH_PAGE_ANY ? CH_PAGE_ANY_FILE :
7161 log_2(change->trigger_page)));
7163 putFile8Bit(file, change->has_action);
7164 putFile8Bit(file, change->action_type);
7165 putFile8Bit(file, change->action_mode);
7166 putFile16BitBE(file, change->action_arg);
7168 /* ... bits 32 - 39 of "has_event[]" (not nice, but downward compatible) */
7170 for (j = 32; j < NUM_CHANGE_EVENTS; j++)
7171 if (change->has_event[j])
7172 event_bits |= (1 << (j - 32));
7173 putFile8Bit(file, event_bits);
7178 #if ENABLE_HISTORIC_CHUNKS
7179 static void SaveLevel_GRP1(FILE *file, struct LevelInfo *level, int element)
7181 struct ElementInfo *ei = &element_info[element];
7182 struct ElementGroupInfo *group = ei->group;
7185 putFile16BitBE(file, element);
7187 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
7188 putFile8Bit(file, ei->description[i]);
7190 putFile8Bit(file, group->num_elements);
7192 putFile8Bit(file, ei->use_gfx_element);
7193 putFile16BitBE(file, ei->gfx_element_initial);
7195 putFile8Bit(file, group->choice_mode);
7197 /* some free bytes for future values and padding */
7198 WriteUnusedBytesToFile(file, 3);
7200 for (i = 0; i < MAX_ELEMENTS_IN_GROUP; i++)
7201 putFile16BitBE(file, group->element[i]);
7205 static int SaveLevel_MicroChunk(FILE *file, struct LevelFileConfigInfo *entry,
7206 boolean write_element)
7208 int save_type = entry->save_type;
7209 int data_type = entry->data_type;
7210 int conf_type = entry->conf_type;
7211 int byte_mask = conf_type & CONF_MASK_BYTES;
7212 int element = entry->element;
7213 int default_value = entry->default_value;
7215 boolean modified = FALSE;
7217 if (byte_mask != CONF_MASK_MULTI_BYTES)
7219 void *value_ptr = entry->value;
7220 int value = (data_type == TYPE_BOOLEAN ? *(boolean *)value_ptr :
7223 /* check if any settings have been modified before saving them */
7224 if (value != default_value)
7227 /* do not save if explicitly told or if unmodified default settings */
7228 if ((save_type == SAVE_CONF_NEVER) ||
7229 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
7233 num_bytes += putFile16BitBE(file, element);
7235 num_bytes += putFile8Bit(file, conf_type);
7236 num_bytes += (byte_mask == CONF_MASK_1_BYTE ? putFile8Bit (file, value) :
7237 byte_mask == CONF_MASK_2_BYTE ? putFile16BitBE(file, value) :
7238 byte_mask == CONF_MASK_4_BYTE ? putFile32BitBE(file, value) :
7241 else if (data_type == TYPE_STRING)
7243 char *default_string = entry->default_string;
7244 char *string = (char *)(entry->value);
7245 int string_length = strlen(string);
7248 /* check if any settings have been modified before saving them */
7249 if (!strEqual(string, default_string))
7252 /* do not save if explicitly told or if unmodified default settings */
7253 if ((save_type == SAVE_CONF_NEVER) ||
7254 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
7258 num_bytes += putFile16BitBE(file, element);
7260 num_bytes += putFile8Bit(file, conf_type);
7261 num_bytes += putFile16BitBE(file, string_length);
7263 for (i = 0; i < string_length; i++)
7264 num_bytes += putFile8Bit(file, string[i]);
7266 else if (data_type == TYPE_ELEMENT_LIST)
7268 int *element_array = (int *)(entry->value);
7269 int num_elements = *(int *)(entry->num_entities);
7272 /* check if any settings have been modified before saving them */
7273 for (i = 0; i < num_elements; i++)
7274 if (element_array[i] != default_value)
7277 /* do not save if explicitly told or if unmodified default settings */
7278 if ((save_type == SAVE_CONF_NEVER) ||
7279 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
7283 num_bytes += putFile16BitBE(file, element);
7285 num_bytes += putFile8Bit(file, conf_type);
7286 num_bytes += putFile16BitBE(file, num_elements * CONF_ELEMENT_NUM_BYTES);
7288 for (i = 0; i < num_elements; i++)
7289 num_bytes += putFile16BitBE(file, element_array[i]);
7291 else if (data_type == TYPE_CONTENT_LIST)
7293 struct Content *content = (struct Content *)(entry->value);
7294 int num_contents = *(int *)(entry->num_entities);
7297 /* check if any settings have been modified before saving them */
7298 for (i = 0; i < num_contents; i++)
7299 for (y = 0; y < 3; y++)
7300 for (x = 0; x < 3; x++)
7301 if (content[i].e[x][y] != default_value)
7304 /* do not save if explicitly told or if unmodified default settings */
7305 if ((save_type == SAVE_CONF_NEVER) ||
7306 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
7310 num_bytes += putFile16BitBE(file, element);
7312 num_bytes += putFile8Bit(file, conf_type);
7313 num_bytes += putFile16BitBE(file, num_contents * CONF_CONTENT_NUM_BYTES);
7315 for (i = 0; i < num_contents; i++)
7316 for (y = 0; y < 3; y++)
7317 for (x = 0; x < 3; x++)
7318 num_bytes += putFile16BitBE(file, content[i].e[x][y]);
7324 static int SaveLevel_INFO(FILE *file, struct LevelInfo *level)
7329 li = *level; /* copy level data into temporary buffer */
7331 for (i = 0; chunk_config_INFO[i].data_type != -1; i++)
7332 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_INFO[i], FALSE);
7337 static int SaveLevel_ELEM(FILE *file, struct LevelInfo *level)
7342 li = *level; /* copy level data into temporary buffer */
7344 for (i = 0; chunk_config_ELEM[i].data_type != -1; i++)
7345 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_ELEM[i], TRUE);
7350 static int SaveLevel_NOTE(FILE *file, struct LevelInfo *level, int element)
7352 int envelope_nr = element - EL_ENVELOPE_1;
7356 chunk_size += putFile16BitBE(file, element);
7358 /* copy envelope data into temporary buffer */
7359 xx_envelope = level->envelope[envelope_nr];
7361 for (i = 0; chunk_config_NOTE[i].data_type != -1; i++)
7362 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_NOTE[i], FALSE);
7367 static int SaveLevel_CUSX(FILE *file, struct LevelInfo *level, int element)
7369 struct ElementInfo *ei = &element_info[element];
7373 chunk_size += putFile16BitBE(file, element);
7375 xx_ei = *ei; /* copy element data into temporary buffer */
7377 /* set default description string for this specific element */
7378 strcpy(xx_default_description, getDefaultElementDescription(ei));
7380 for (i = 0; chunk_config_CUSX_base[i].data_type != -1; i++)
7381 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_CUSX_base[i], FALSE);
7383 for (i = 0; i < ei->num_change_pages; i++)
7385 struct ElementChangeInfo *change = &ei->change_page[i];
7387 xx_current_change_page = i;
7389 xx_change = *change; /* copy change data into temporary buffer */
7392 setEventBitsFromEventFlags(change);
7394 for (j = 0; chunk_config_CUSX_change[j].data_type != -1; j++)
7395 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_CUSX_change[j],
7402 static int SaveLevel_GRPX(FILE *file, struct LevelInfo *level, int element)
7404 struct ElementInfo *ei = &element_info[element];
7405 struct ElementGroupInfo *group = ei->group;
7409 chunk_size += putFile16BitBE(file, element);
7411 xx_ei = *ei; /* copy element data into temporary buffer */
7412 xx_group = *group; /* copy group data into temporary buffer */
7414 /* set default description string for this specific element */
7415 strcpy(xx_default_description, getDefaultElementDescription(ei));
7417 for (i = 0; chunk_config_GRPX[i].data_type != -1; i++)
7418 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_GRPX[i], FALSE);
7423 static void SaveLevelFromFilename(struct LevelInfo *level, char *filename,
7424 boolean save_as_template)
7430 if (!(file = fopen(filename, MODE_WRITE)))
7432 Error(ERR_WARN, "cannot save level file '%s'", filename);
7436 level->file_version = FILE_VERSION_ACTUAL;
7437 level->game_version = GAME_VERSION_ACTUAL;
7439 level->creation_date = getCurrentDate();
7441 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
7442 putFileChunkBE(file, "CAVE", CHUNK_SIZE_NONE);
7444 chunk_size = SaveLevel_VERS(NULL, level);
7445 putFileChunkBE(file, "VERS", chunk_size);
7446 SaveLevel_VERS(file, level);
7448 chunk_size = SaveLevel_DATE(NULL, level);
7449 putFileChunkBE(file, "DATE", chunk_size);
7450 SaveLevel_DATE(file, level);
7452 chunk_size = SaveLevel_NAME(NULL, level);
7453 putFileChunkBE(file, "NAME", chunk_size);
7454 SaveLevel_NAME(file, level);
7456 chunk_size = SaveLevel_AUTH(NULL, level);
7457 putFileChunkBE(file, "AUTH", chunk_size);
7458 SaveLevel_AUTH(file, level);
7460 chunk_size = SaveLevel_INFO(NULL, level);
7461 putFileChunkBE(file, "INFO", chunk_size);
7462 SaveLevel_INFO(file, level);
7464 chunk_size = SaveLevel_BODY(NULL, level);
7465 putFileChunkBE(file, "BODY", chunk_size);
7466 SaveLevel_BODY(file, level);
7468 chunk_size = SaveLevel_ELEM(NULL, level);
7469 if (chunk_size > LEVEL_CHUNK_ELEM_UNCHANGED) /* save if changed */
7471 putFileChunkBE(file, "ELEM", chunk_size);
7472 SaveLevel_ELEM(file, level);
7475 for (i = 0; i < NUM_ENVELOPES; i++)
7477 int element = EL_ENVELOPE_1 + i;
7479 chunk_size = SaveLevel_NOTE(NULL, level, element);
7480 if (chunk_size > LEVEL_CHUNK_NOTE_UNCHANGED) /* save if changed */
7482 putFileChunkBE(file, "NOTE", chunk_size);
7483 SaveLevel_NOTE(file, level, element);
7487 /* if not using template level, check for non-default custom/group elements */
7488 if (!level->use_custom_template || save_as_template)
7490 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7492 int element = EL_CUSTOM_START + i;
7494 chunk_size = SaveLevel_CUSX(NULL, level, element);
7495 if (chunk_size > LEVEL_CHUNK_CUSX_UNCHANGED) /* save if changed */
7497 putFileChunkBE(file, "CUSX", chunk_size);
7498 SaveLevel_CUSX(file, level, element);
7502 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
7504 int element = EL_GROUP_START + i;
7506 chunk_size = SaveLevel_GRPX(NULL, level, element);
7507 if (chunk_size > LEVEL_CHUNK_GRPX_UNCHANGED) /* save if changed */
7509 putFileChunkBE(file, "GRPX", chunk_size);
7510 SaveLevel_GRPX(file, level, element);
7517 SetFilePermissions(filename, PERMS_PRIVATE);
7520 void SaveLevel(int nr)
7522 char *filename = getDefaultLevelFilename(nr);
7524 SaveLevelFromFilename(&level, filename, FALSE);
7527 void SaveLevelTemplate(void)
7529 char *filename = getLocalLevelTemplateFilename();
7531 SaveLevelFromFilename(&level, filename, TRUE);
7534 boolean SaveLevelChecked(int nr)
7536 char *filename = getDefaultLevelFilename(nr);
7537 boolean new_level = !fileExists(filename);
7538 boolean level_saved = FALSE;
7540 if (new_level || Request("Save this level and kill the old?", REQ_ASK))
7545 Request("Level saved!", REQ_CONFIRM);
7553 void DumpLevel(struct LevelInfo *level)
7555 if (level->no_level_file || level->no_valid_file)
7557 Error(ERR_WARN, "cannot dump -- no valid level file found");
7563 Print("Level xxx (file version %08d, game version %08d)\n",
7564 level->file_version, level->game_version);
7567 Print("Level author: '%s'\n", level->author);
7568 Print("Level title: '%s'\n", level->name);
7570 Print("Playfield size: %d x %d\n", level->fieldx, level->fieldy);
7572 Print("Level time: %d seconds\n", level->time);
7573 Print("Gems needed: %d\n", level->gems_needed);
7575 Print("Time for magic wall: %d seconds\n", level->time_magic_wall);
7576 Print("Time for wheel: %d seconds\n", level->time_wheel);
7577 Print("Time for light: %d seconds\n", level->time_light);
7578 Print("Time for timegate: %d seconds\n", level->time_timegate);
7580 Print("Amoeba speed: %d\n", level->amoeba_speed);
7583 Print("EM style slippery gems: %s\n", (level->em_slippery_gems ? "yes" : "no"));
7584 Print("Player blocks last field: %s\n", (level->block_last_field ? "yes" : "no"));
7585 Print("SP player blocks last field: %s\n", (level->sp_block_last_field ? "yes" : "no"));
7586 Print("use spring bug: %s\n", (level->use_spring_bug ? "yes" : "no"));
7587 Print("use step counter: %s\n", (level->use_step_counter ? "yes" : "no"));
7593 /* ========================================================================= */
7594 /* tape file functions */
7595 /* ========================================================================= */
7597 static void setTapeInfoToDefaults(void)
7601 /* always start with reliable default values (empty tape) */
7604 /* default values (also for pre-1.2 tapes) with only the first player */
7605 tape.player_participates[0] = TRUE;
7606 for (i = 1; i < MAX_PLAYERS; i++)
7607 tape.player_participates[i] = FALSE;
7609 /* at least one (default: the first) player participates in every tape */
7610 tape.num_participating_players = 1;
7612 tape.level_nr = level_nr;
7614 tape.changed = FALSE;
7616 tape.recording = FALSE;
7617 tape.playing = FALSE;
7618 tape.pausing = FALSE;
7620 tape.no_valid_file = FALSE;
7623 static int LoadTape_VERS(File *file, int chunk_size, struct TapeInfo *tape)
7625 tape->file_version = getFileVersion(file);
7626 tape->game_version = getFileVersion(file);
7631 static int LoadTape_HEAD(File *file, int chunk_size, struct TapeInfo *tape)
7635 tape->random_seed = getFile32BitBE(file);
7636 tape->date = getFile32BitBE(file);
7637 tape->length = getFile32BitBE(file);
7639 /* read header fields that are new since version 1.2 */
7640 if (tape->file_version >= FILE_VERSION_1_2)
7642 byte store_participating_players = getFile8Bit(file);
7645 /* since version 1.2, tapes store which players participate in the tape */
7646 tape->num_participating_players = 0;
7647 for (i = 0; i < MAX_PLAYERS; i++)
7649 tape->player_participates[i] = FALSE;
7651 if (store_participating_players & (1 << i))
7653 tape->player_participates[i] = TRUE;
7654 tape->num_participating_players++;
7658 tape->use_mouse = (getFile8Bit(file) == 1 ? TRUE : FALSE);
7660 ReadUnusedBytesFromFile(file, TAPE_CHUNK_HEAD_UNUSED);
7662 engine_version = getFileVersion(file);
7663 if (engine_version > 0)
7664 tape->engine_version = engine_version;
7666 tape->engine_version = tape->game_version;
7672 static int LoadTape_INFO(File *file, int chunk_size, struct TapeInfo *tape)
7674 int level_identifier_size;
7677 level_identifier_size = getFile16BitBE(file);
7679 tape->level_identifier =
7680 checked_realloc(tape->level_identifier, level_identifier_size);
7682 for (i = 0; i < level_identifier_size; i++)
7683 tape->level_identifier[i] = getFile8Bit(file);
7685 tape->level_nr = getFile16BitBE(file);
7687 chunk_size = 2 + level_identifier_size + 2;
7692 static int LoadTape_BODY(File *file, int chunk_size, struct TapeInfo *tape)
7696 (tape->use_mouse ? 3 : tape->num_participating_players) + 1;
7697 int chunk_size_expected = tape_pos_size * tape->length;
7699 if (chunk_size_expected != chunk_size)
7701 ReadUnusedBytesFromFile(file, chunk_size);
7702 return chunk_size_expected;
7705 for (i = 0; i < tape->length; i++)
7707 if (i >= MAX_TAPE_LEN)
7709 Error(ERR_WARN, "tape truncated -- size exceeds maximum tape size %d",
7712 // tape too large; read and ignore remaining tape data from this chunk
7713 for (;i < tape->length; i++)
7714 ReadUnusedBytesFromFile(file, tape->num_participating_players + 1);
7719 if (tape->use_mouse)
7721 tape->pos[i].action[TAPE_ACTION_LX] = getFile8Bit(file);
7722 tape->pos[i].action[TAPE_ACTION_LY] = getFile8Bit(file);
7723 tape->pos[i].action[TAPE_ACTION_BUTTON] = getFile8Bit(file);
7725 tape->pos[i].action[TAPE_ACTION_UNUSED] = 0;
7729 for (j = 0; j < MAX_PLAYERS; j++)
7731 tape->pos[i].action[j] = MV_NONE;
7733 if (tape->player_participates[j])
7734 tape->pos[i].action[j] = getFile8Bit(file);
7738 tape->pos[i].delay = getFile8Bit(file);
7740 if (tape->file_version == FILE_VERSION_1_0)
7742 /* eliminate possible diagonal moves in old tapes */
7743 /* this is only for backward compatibility */
7745 byte joy_dir[4] = { JOY_LEFT, JOY_RIGHT, JOY_UP, JOY_DOWN };
7746 byte action = tape->pos[i].action[0];
7747 int k, num_moves = 0;
7749 for (k = 0; k<4; k++)
7751 if (action & joy_dir[k])
7753 tape->pos[i + num_moves].action[0] = joy_dir[k];
7755 tape->pos[i + num_moves].delay = 0;
7764 tape->length += num_moves;
7767 else if (tape->file_version < FILE_VERSION_2_0)
7769 /* convert pre-2.0 tapes to new tape format */
7771 if (tape->pos[i].delay > 1)
7774 tape->pos[i + 1] = tape->pos[i];
7775 tape->pos[i + 1].delay = 1;
7778 for (j = 0; j < MAX_PLAYERS; j++)
7779 tape->pos[i].action[j] = MV_NONE;
7780 tape->pos[i].delay--;
7787 if (checkEndOfFile(file))
7791 if (i != tape->length)
7792 chunk_size = tape_pos_size * i;
7797 static void LoadTape_SokobanSolution(char *filename)
7800 int move_delay = TILESIZE / level.initial_player_stepsize[0];
7802 if (!(file = openFile(filename, MODE_READ)))
7804 tape.no_valid_file = TRUE;
7809 while (!checkEndOfFile(file))
7811 unsigned char c = getByteFromFile(file);
7813 if (checkEndOfFile(file))
7820 tape.pos[tape.length].action[0] = MV_UP;
7821 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
7827 tape.pos[tape.length].action[0] = MV_DOWN;
7828 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
7834 tape.pos[tape.length].action[0] = MV_LEFT;
7835 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
7841 tape.pos[tape.length].action[0] = MV_RIGHT;
7842 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
7850 /* ignore white-space characters */
7854 tape.no_valid_file = TRUE;
7856 Error(ERR_WARN, "unsupported Sokoban solution file '%s' ['%d']", filename, c);
7864 if (tape.no_valid_file)
7867 tape.length_frames = GetTapeLengthFrames();
7868 tape.length_seconds = GetTapeLengthSeconds();
7871 void LoadTapeFromFilename(char *filename)
7873 char cookie[MAX_LINE_LEN];
7874 char chunk_name[CHUNK_ID_LEN + 1];
7878 /* always start with reliable default values */
7879 setTapeInfoToDefaults();
7881 if (strSuffix(filename, ".sln"))
7883 LoadTape_SokobanSolution(filename);
7888 if (!(file = openFile(filename, MODE_READ)))
7890 tape.no_valid_file = TRUE;
7895 getFileChunkBE(file, chunk_name, NULL);
7896 if (strEqual(chunk_name, "RND1"))
7898 getFile32BitBE(file); /* not used */
7900 getFileChunkBE(file, chunk_name, NULL);
7901 if (!strEqual(chunk_name, "TAPE"))
7903 tape.no_valid_file = TRUE;
7905 Error(ERR_WARN, "unknown format of tape file '%s'", filename);
7912 else /* check for pre-2.0 file format with cookie string */
7914 strcpy(cookie, chunk_name);
7915 if (getStringFromFile(file, &cookie[4], MAX_LINE_LEN - 4) == NULL)
7917 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
7918 cookie[strlen(cookie) - 1] = '\0';
7920 if (!checkCookieString(cookie, TAPE_COOKIE_TMPL))
7922 tape.no_valid_file = TRUE;
7924 Error(ERR_WARN, "unknown format of tape file '%s'", filename);
7931 if ((tape.file_version = getFileVersionFromCookieString(cookie)) == -1)
7933 tape.no_valid_file = TRUE;
7935 Error(ERR_WARN, "unsupported version of tape file '%s'", filename);
7942 /* pre-2.0 tape files have no game version, so use file version here */
7943 tape.game_version = tape.file_version;
7946 if (tape.file_version < FILE_VERSION_1_2)
7948 /* tape files from versions before 1.2.0 without chunk structure */
7949 LoadTape_HEAD(file, TAPE_CHUNK_HEAD_SIZE, &tape);
7950 LoadTape_BODY(file, 2 * tape.length, &tape);
7958 int (*loader)(File *, int, struct TapeInfo *);
7962 { "VERS", TAPE_CHUNK_VERS_SIZE, LoadTape_VERS },
7963 { "HEAD", TAPE_CHUNK_HEAD_SIZE, LoadTape_HEAD },
7964 { "INFO", -1, LoadTape_INFO },
7965 { "BODY", -1, LoadTape_BODY },
7969 while (getFileChunkBE(file, chunk_name, &chunk_size))
7973 while (chunk_info[i].name != NULL &&
7974 !strEqual(chunk_name, chunk_info[i].name))
7977 if (chunk_info[i].name == NULL)
7979 Error(ERR_WARN, "unknown chunk '%s' in tape file '%s'",
7980 chunk_name, filename);
7981 ReadUnusedBytesFromFile(file, chunk_size);
7983 else if (chunk_info[i].size != -1 &&
7984 chunk_info[i].size != chunk_size)
7986 Error(ERR_WARN, "wrong size (%d) of chunk '%s' in tape file '%s'",
7987 chunk_size, chunk_name, filename);
7988 ReadUnusedBytesFromFile(file, chunk_size);
7992 /* call function to load this tape chunk */
7993 int chunk_size_expected =
7994 (chunk_info[i].loader)(file, chunk_size, &tape);
7996 /* the size of some chunks cannot be checked before reading other
7997 chunks first (like "HEAD" and "BODY") that contain some header
7998 information, so check them here */
7999 if (chunk_size_expected != chunk_size)
8001 Error(ERR_WARN, "wrong size (%d) of chunk '%s' in tape file '%s'",
8002 chunk_size, chunk_name, filename);
8010 tape.length_frames = GetTapeLengthFrames();
8011 tape.length_seconds = GetTapeLengthSeconds();
8014 printf("::: tape file version: %d\n", tape.file_version);
8015 printf("::: tape game version: %d\n", tape.game_version);
8016 printf("::: tape engine version: %d\n", tape.engine_version);
8020 void LoadTape(int nr)
8022 char *filename = getTapeFilename(nr);
8024 LoadTapeFromFilename(filename);
8027 void LoadSolutionTape(int nr)
8029 char *filename = getSolutionTapeFilename(nr);
8031 LoadTapeFromFilename(filename);
8033 if (TAPE_IS_EMPTY(tape) &&
8034 level.game_engine_type == GAME_ENGINE_TYPE_SP &&
8035 level.native_sp_level->demo.is_available)
8036 CopyNativeTape_SP_to_RND(&level);
8039 static void SaveTape_VERS(FILE *file, struct TapeInfo *tape)
8041 putFileVersion(file, tape->file_version);
8042 putFileVersion(file, tape->game_version);
8045 static void SaveTape_HEAD(FILE *file, struct TapeInfo *tape)
8048 byte store_participating_players = 0;
8050 /* set bits for participating players for compact storage */
8051 for (i = 0; i < MAX_PLAYERS; i++)
8052 if (tape->player_participates[i])
8053 store_participating_players |= (1 << i);
8055 putFile32BitBE(file, tape->random_seed);
8056 putFile32BitBE(file, tape->date);
8057 putFile32BitBE(file, tape->length);
8059 putFile8Bit(file, store_participating_players);
8061 putFile8Bit(file, (tape->use_mouse ? 1 : 0));
8063 /* unused bytes not at the end here for 4-byte alignment of engine_version */
8064 WriteUnusedBytesToFile(file, TAPE_CHUNK_HEAD_UNUSED);
8066 putFileVersion(file, tape->engine_version);
8069 static void SaveTape_INFO(FILE *file, struct TapeInfo *tape)
8071 int level_identifier_size = strlen(tape->level_identifier) + 1;
8074 putFile16BitBE(file, level_identifier_size);
8076 for (i = 0; i < level_identifier_size; i++)
8077 putFile8Bit(file, tape->level_identifier[i]);
8079 putFile16BitBE(file, tape->level_nr);
8082 static void SaveTape_BODY(FILE *file, struct TapeInfo *tape)
8086 for (i = 0; i < tape->length; i++)
8088 if (tape->use_mouse)
8090 putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_LX]);
8091 putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_LY]);
8092 putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_BUTTON]);
8096 for (j = 0; j < MAX_PLAYERS; j++)
8097 if (tape->player_participates[j])
8098 putFile8Bit(file, tape->pos[i].action[j]);
8101 putFile8Bit(file, tape->pos[i].delay);
8105 void SaveTape(int nr)
8107 char *filename = getTapeFilename(nr);
8109 int num_participating_players = 0;
8111 int info_chunk_size;
8112 int body_chunk_size;
8115 InitTapeDirectory(leveldir_current->subdir);
8117 if (!(file = fopen(filename, MODE_WRITE)))
8119 Error(ERR_WARN, "cannot save level recording file '%s'", filename);
8123 tape.file_version = FILE_VERSION_ACTUAL;
8124 tape.game_version = GAME_VERSION_ACTUAL;
8126 /* count number of participating players */
8127 for (i = 0; i < MAX_PLAYERS; i++)
8128 if (tape.player_participates[i])
8129 num_participating_players++;
8131 tape_pos_size = (tape.use_mouse ? 3 : num_participating_players) + 1;
8133 info_chunk_size = 2 + (strlen(tape.level_identifier) + 1) + 2;
8134 body_chunk_size = tape_pos_size * tape.length;
8136 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
8137 putFileChunkBE(file, "TAPE", CHUNK_SIZE_NONE);
8139 putFileChunkBE(file, "VERS", TAPE_CHUNK_VERS_SIZE);
8140 SaveTape_VERS(file, &tape);
8142 putFileChunkBE(file, "HEAD", TAPE_CHUNK_HEAD_SIZE);
8143 SaveTape_HEAD(file, &tape);
8145 putFileChunkBE(file, "INFO", info_chunk_size);
8146 SaveTape_INFO(file, &tape);
8148 putFileChunkBE(file, "BODY", body_chunk_size);
8149 SaveTape_BODY(file, &tape);
8153 SetFilePermissions(filename, PERMS_PRIVATE);
8155 tape.changed = FALSE;
8158 static boolean SaveTapeCheckedExt(int nr, char *msg_replace, char *msg_saved)
8160 char *filename = getTapeFilename(nr);
8161 boolean new_tape = !fileExists(filename);
8162 boolean tape_saved = FALSE;
8164 if (new_tape || Request(msg_replace, REQ_ASK))
8169 Request(msg_saved, REQ_CONFIRM);
8177 boolean SaveTapeChecked(int nr)
8179 return SaveTapeCheckedExt(nr, "Replace old tape?", "Tape saved!");
8182 boolean SaveTapeChecked_LevelSolved(int nr)
8184 return SaveTapeCheckedExt(nr, "Level solved! Replace old tape?",
8185 "Level solved! Tape saved!");
8188 void DumpTape(struct TapeInfo *tape)
8190 int tape_frame_counter;
8193 if (tape->no_valid_file)
8195 Error(ERR_WARN, "cannot dump -- no valid tape file found");
8201 Print("Tape of Level %03d (file version %08d, game version %08d)\n",
8202 tape->level_nr, tape->file_version, tape->game_version);
8203 Print(" (effective engine version %08d)\n",
8204 tape->engine_version);
8205 Print("Level series identifier: '%s'\n", tape->level_identifier);
8208 tape_frame_counter = 0;
8210 for (i = 0; i < tape->length; i++)
8212 if (i >= MAX_TAPE_LEN)
8217 for (j = 0; j < MAX_PLAYERS; j++)
8219 if (tape->player_participates[j])
8221 int action = tape->pos[i].action[j];
8223 Print("%d:%02x ", j, action);
8224 Print("[%c%c%c%c|%c%c] - ",
8225 (action & JOY_LEFT ? '<' : ' '),
8226 (action & JOY_RIGHT ? '>' : ' '),
8227 (action & JOY_UP ? '^' : ' '),
8228 (action & JOY_DOWN ? 'v' : ' '),
8229 (action & JOY_BUTTON_1 ? '1' : ' '),
8230 (action & JOY_BUTTON_2 ? '2' : ' '));
8234 Print("(%03d) ", tape->pos[i].delay);
8235 Print("[%05d]\n", tape_frame_counter);
8237 tape_frame_counter += tape->pos[i].delay;
8244 /* ========================================================================= */
8245 /* score file functions */
8246 /* ========================================================================= */
8248 void LoadScore(int nr)
8251 char *filename = getScoreFilename(nr);
8252 char cookie[MAX_LINE_LEN];
8253 char line[MAX_LINE_LEN];
8257 /* always start with reliable default values */
8258 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
8260 strcpy(highscore[i].Name, EMPTY_PLAYER_NAME);
8261 highscore[i].Score = 0;
8264 if (!(file = fopen(filename, MODE_READ)))
8267 /* check file identifier */
8268 if (fgets(cookie, MAX_LINE_LEN, file) == NULL)
8270 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
8271 cookie[strlen(cookie) - 1] = '\0';
8273 if (!checkCookieString(cookie, SCORE_COOKIE))
8275 Error(ERR_WARN, "unknown format of score file '%s'", filename);
8280 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
8282 if (fscanf(file, "%d", &highscore[i].Score) == EOF)
8283 Error(ERR_WARN, "fscanf() failed; %s", strerror(errno));
8284 if (fgets(line, MAX_LINE_LEN, file) == NULL)
8287 if (strlen(line) > 0 && line[strlen(line) - 1] == '\n')
8288 line[strlen(line) - 1] = '\0';
8290 for (line_ptr = line; *line_ptr; line_ptr++)
8292 if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != '\0')
8294 strncpy(highscore[i].Name, line_ptr, MAX_PLAYER_NAME_LEN);
8295 highscore[i].Name[MAX_PLAYER_NAME_LEN] = '\0';
8304 void SaveScore(int nr)
8307 int permissions = (program.global_scores ? PERMS_PUBLIC : PERMS_PRIVATE);
8308 char *filename = getScoreFilename(nr);
8311 /* used instead of "leveldir_current->subdir" (for network games) */
8312 InitScoreDirectory(levelset.identifier);
8314 if (!(file = fopen(filename, MODE_WRITE)))
8316 Error(ERR_WARN, "cannot save score for level %d", nr);
8320 fprintf(file, "%s\n\n", SCORE_COOKIE);
8322 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
8323 fprintf(file, "%d %s\n", highscore[i].Score, highscore[i].Name);
8327 SetFilePermissions(filename, permissions);
8331 /* ========================================================================= */
8332 /* setup file functions */
8333 /* ========================================================================= */
8335 #define TOKEN_STR_PLAYER_PREFIX "player_"
8340 SETUP_TOKEN_PLAYER_NAME = 0,
8342 SETUP_TOKEN_SOUND_LOOPS,
8343 SETUP_TOKEN_SOUND_MUSIC,
8344 SETUP_TOKEN_SOUND_SIMPLE,
8346 SETUP_TOKEN_SCROLL_DELAY,
8347 SETUP_TOKEN_SCROLL_DELAY_VALUE,
8348 SETUP_TOKEN_ENGINE_SNAPSHOT_MODE,
8349 SETUP_TOKEN_ENGINE_SNAPSHOT_MEMORY,
8350 SETUP_TOKEN_FADE_SCREENS,
8351 SETUP_TOKEN_AUTORECORD,
8352 SETUP_TOKEN_SHOW_TITLESCREEN,
8353 SETUP_TOKEN_QUICK_DOORS,
8354 SETUP_TOKEN_TEAM_MODE,
8355 SETUP_TOKEN_HANDICAP,
8356 SETUP_TOKEN_SKIP_LEVELS,
8357 SETUP_TOKEN_INCREMENT_LEVELS,
8358 SETUP_TOKEN_AUTO_PLAY_NEXT_LEVEL,
8359 SETUP_TOKEN_SKIP_SCORES_AFTER_GAME,
8360 SETUP_TOKEN_TIME_LIMIT,
8361 SETUP_TOKEN_FULLSCREEN,
8362 SETUP_TOKEN_WINDOW_SCALING_PERCENT,
8363 SETUP_TOKEN_WINDOW_SCALING_QUALITY,
8364 SETUP_TOKEN_SCREEN_RENDERING_MODE,
8365 SETUP_TOKEN_VSYNC_MODE,
8366 SETUP_TOKEN_ASK_ON_ESCAPE,
8367 SETUP_TOKEN_ASK_ON_ESCAPE_EDITOR,
8368 SETUP_TOKEN_QUICK_SWITCH,
8369 SETUP_TOKEN_INPUT_ON_FOCUS,
8370 SETUP_TOKEN_PREFER_AGA_GRAPHICS,
8371 SETUP_TOKEN_GAME_SPEED_EXTENDED,
8372 SETUP_TOKEN_GAME_FRAME_DELAY,
8373 SETUP_TOKEN_SP_SHOW_BORDER_ELEMENTS,
8374 SETUP_TOKEN_SMALL_GAME_GRAPHICS,
8375 SETUP_TOKEN_SHOW_SNAPSHOT_BUTTONS,
8376 SETUP_TOKEN_GRAPHICS_SET,
8377 SETUP_TOKEN_SOUNDS_SET,
8378 SETUP_TOKEN_MUSIC_SET,
8379 SETUP_TOKEN_OVERRIDE_LEVEL_GRAPHICS,
8380 SETUP_TOKEN_OVERRIDE_LEVEL_SOUNDS,
8381 SETUP_TOKEN_OVERRIDE_LEVEL_MUSIC,
8382 SETUP_TOKEN_VOLUME_SIMPLE,
8383 SETUP_TOKEN_VOLUME_LOOPS,
8384 SETUP_TOKEN_VOLUME_MUSIC,
8385 SETUP_TOKEN_NETWORK_MODE,
8386 SETUP_TOKEN_NETWORK_PLAYER_NR,
8387 SETUP_TOKEN_NETWORK_SERVER_HOSTNAME,
8388 SETUP_TOKEN_TOUCH_CONTROL_TYPE,
8389 SETUP_TOKEN_TOUCH_MOVE_DISTANCE,
8390 SETUP_TOKEN_TOUCH_DROP_DISTANCE,
8391 SETUP_TOKEN_TOUCH_TRANSPARENCY,
8392 SETUP_TOKEN_TOUCH_DRAW_OUTLINED,
8393 SETUP_TOKEN_TOUCH_DRAW_PRESSED,
8394 SETUP_TOKEN_TOUCH_GRID_XSIZE_0,
8395 SETUP_TOKEN_TOUCH_GRID_YSIZE_0,
8396 SETUP_TOKEN_TOUCH_GRID_XSIZE_1,
8397 SETUP_TOKEN_TOUCH_GRID_YSIZE_1,
8399 NUM_GLOBAL_SETUP_TOKENS
8405 SETUP_TOKEN_AUTO_EDITOR_ZOOM_TILESIZE = 0,
8407 NUM_AUTO_SETUP_TOKENS
8413 SETUP_TOKEN_EDITOR_EL_CLASSIC = 0,
8414 SETUP_TOKEN_EDITOR_EL_CUSTOM,
8415 SETUP_TOKEN_EDITOR_EL_USER_DEFINED,
8416 SETUP_TOKEN_EDITOR_EL_DYNAMIC,
8417 SETUP_TOKEN_EDITOR_EL_HEADLINES,
8418 SETUP_TOKEN_EDITOR_SHOW_ELEMENT_TOKEN,
8420 NUM_EDITOR_SETUP_TOKENS
8423 /* editor cascade setup */
8426 SETUP_TOKEN_EDITOR_CASCADE_BD = 0,
8427 SETUP_TOKEN_EDITOR_CASCADE_EM,
8428 SETUP_TOKEN_EDITOR_CASCADE_EMC,
8429 SETUP_TOKEN_EDITOR_CASCADE_RND,
8430 SETUP_TOKEN_EDITOR_CASCADE_SB,
8431 SETUP_TOKEN_EDITOR_CASCADE_SP,
8432 SETUP_TOKEN_EDITOR_CASCADE_DC,
8433 SETUP_TOKEN_EDITOR_CASCADE_DX,
8434 SETUP_TOKEN_EDITOR_CASCADE_TEXT,
8435 SETUP_TOKEN_EDITOR_CASCADE_STEELTEXT,
8436 SETUP_TOKEN_EDITOR_CASCADE_CE,
8437 SETUP_TOKEN_EDITOR_CASCADE_GE,
8438 SETUP_TOKEN_EDITOR_CASCADE_REF,
8439 SETUP_TOKEN_EDITOR_CASCADE_USER,
8440 SETUP_TOKEN_EDITOR_CASCADE_DYNAMIC,
8442 NUM_EDITOR_CASCADE_SETUP_TOKENS
8445 /* shortcut setup */
8448 SETUP_TOKEN_SHORTCUT_SAVE_GAME = 0,
8449 SETUP_TOKEN_SHORTCUT_LOAD_GAME,
8450 SETUP_TOKEN_SHORTCUT_TOGGLE_PAUSE,
8451 SETUP_TOKEN_SHORTCUT_FOCUS_PLAYER_1,
8452 SETUP_TOKEN_SHORTCUT_FOCUS_PLAYER_2,
8453 SETUP_TOKEN_SHORTCUT_FOCUS_PLAYER_3,
8454 SETUP_TOKEN_SHORTCUT_FOCUS_PLAYER_4,
8455 SETUP_TOKEN_SHORTCUT_FOCUS_PLAYER_ALL,
8456 SETUP_TOKEN_SHORTCUT_TAPE_EJECT,
8457 SETUP_TOKEN_SHORTCUT_TAPE_EXTRA,
8458 SETUP_TOKEN_SHORTCUT_TAPE_STOP,
8459 SETUP_TOKEN_SHORTCUT_TAPE_PAUSE,
8460 SETUP_TOKEN_SHORTCUT_TAPE_RECORD,
8461 SETUP_TOKEN_SHORTCUT_TAPE_PLAY,
8462 SETUP_TOKEN_SHORTCUT_SOUND_SIMPLE,
8463 SETUP_TOKEN_SHORTCUT_SOUND_LOOPS,
8464 SETUP_TOKEN_SHORTCUT_SOUND_MUSIC,
8465 SETUP_TOKEN_SHORTCUT_SNAP_LEFT,
8466 SETUP_TOKEN_SHORTCUT_SNAP_RIGHT,
8467 SETUP_TOKEN_SHORTCUT_SNAP_UP,
8468 SETUP_TOKEN_SHORTCUT_SNAP_DOWN,
8470 NUM_SHORTCUT_SETUP_TOKENS
8476 SETUP_TOKEN_PLAYER_USE_JOYSTICK = 0,
8477 SETUP_TOKEN_PLAYER_JOY_DEVICE_NAME,
8478 SETUP_TOKEN_PLAYER_JOY_XLEFT,
8479 SETUP_TOKEN_PLAYER_JOY_XMIDDLE,
8480 SETUP_TOKEN_PLAYER_JOY_XRIGHT,
8481 SETUP_TOKEN_PLAYER_JOY_YUPPER,
8482 SETUP_TOKEN_PLAYER_JOY_YMIDDLE,
8483 SETUP_TOKEN_PLAYER_JOY_YLOWER,
8484 SETUP_TOKEN_PLAYER_JOY_SNAP,
8485 SETUP_TOKEN_PLAYER_JOY_DROP,
8486 SETUP_TOKEN_PLAYER_KEY_LEFT,
8487 SETUP_TOKEN_PLAYER_KEY_RIGHT,
8488 SETUP_TOKEN_PLAYER_KEY_UP,
8489 SETUP_TOKEN_PLAYER_KEY_DOWN,
8490 SETUP_TOKEN_PLAYER_KEY_SNAP,
8491 SETUP_TOKEN_PLAYER_KEY_DROP,
8493 NUM_PLAYER_SETUP_TOKENS
8499 SETUP_TOKEN_SYSTEM_SDL_VIDEODRIVER = 0,
8500 SETUP_TOKEN_SYSTEM_SDL_AUDIODRIVER,
8501 SETUP_TOKEN_SYSTEM_AUDIO_FRAGMENT_SIZE,
8503 NUM_SYSTEM_SETUP_TOKENS
8506 /* internal setup */
8509 SETUP_TOKEN_INT_PROGRAM_TITLE = 0,
8510 SETUP_TOKEN_INT_PROGRAM_VERSION,
8511 SETUP_TOKEN_INT_PROGRAM_AUTHOR,
8512 SETUP_TOKEN_INT_PROGRAM_EMAIL,
8513 SETUP_TOKEN_INT_PROGRAM_WEBSITE,
8514 SETUP_TOKEN_INT_PROGRAM_COPYRIGHT,
8515 SETUP_TOKEN_INT_PROGRAM_COMPANY,
8516 SETUP_TOKEN_INT_PROGRAM_ICON_FILE,
8517 SETUP_TOKEN_INT_DEFAULT_GRAPHICS_SET,
8518 SETUP_TOKEN_INT_DEFAULT_SOUNDS_SET,
8519 SETUP_TOKEN_INT_DEFAULT_MUSIC_SET,
8520 SETUP_TOKEN_INT_FALLBACK_GRAPHICS_FILE,
8521 SETUP_TOKEN_INT_FALLBACK_SOUNDS_FILE,
8522 SETUP_TOKEN_INT_FALLBACK_MUSIC_FILE,
8523 SETUP_TOKEN_INT_DEFAULT_LEVEL_SERIES,
8524 SETUP_TOKEN_INT_CHOOSE_FROM_TOP_LEVELDIR,
8525 SETUP_TOKEN_INT_SHOW_SCALING_IN_TITLE,
8526 SETUP_TOKEN_INT_DEFAULT_WINDOW_WIDTH,
8527 SETUP_TOKEN_INT_DEFAULT_WINDOW_HEIGHT,
8529 NUM_INTERNAL_SETUP_TOKENS
8535 SETUP_TOKEN_DEBUG_FRAME_DELAY_0 = 0,
8536 SETUP_TOKEN_DEBUG_FRAME_DELAY_1,
8537 SETUP_TOKEN_DEBUG_FRAME_DELAY_2,
8538 SETUP_TOKEN_DEBUG_FRAME_DELAY_3,
8539 SETUP_TOKEN_DEBUG_FRAME_DELAY_4,
8540 SETUP_TOKEN_DEBUG_FRAME_DELAY_5,
8541 SETUP_TOKEN_DEBUG_FRAME_DELAY_6,
8542 SETUP_TOKEN_DEBUG_FRAME_DELAY_7,
8543 SETUP_TOKEN_DEBUG_FRAME_DELAY_8,
8544 SETUP_TOKEN_DEBUG_FRAME_DELAY_9,
8545 SETUP_TOKEN_DEBUG_FRAME_DELAY_KEY_0,
8546 SETUP_TOKEN_DEBUG_FRAME_DELAY_KEY_1,
8547 SETUP_TOKEN_DEBUG_FRAME_DELAY_KEY_2,
8548 SETUP_TOKEN_DEBUG_FRAME_DELAY_KEY_3,
8549 SETUP_TOKEN_DEBUG_FRAME_DELAY_KEY_4,
8550 SETUP_TOKEN_DEBUG_FRAME_DELAY_KEY_5,
8551 SETUP_TOKEN_DEBUG_FRAME_DELAY_KEY_6,
8552 SETUP_TOKEN_DEBUG_FRAME_DELAY_KEY_7,
8553 SETUP_TOKEN_DEBUG_FRAME_DELAY_KEY_8,
8554 SETUP_TOKEN_DEBUG_FRAME_DELAY_KEY_9,
8555 SETUP_TOKEN_DEBUG_FRAME_DELAY_USE_MOD_KEY,
8556 SETUP_TOKEN_DEBUG_FRAME_DELAY_GAME_ONLY,
8557 SETUP_TOKEN_DEBUG_SHOW_FRAMES_PER_SECOND,
8559 NUM_DEBUG_SETUP_TOKENS
8565 SETUP_TOKEN_OPTIONS_VERBOSE = 0,
8567 NUM_OPTIONS_SETUP_TOKENS
8571 static struct SetupInfo si;
8572 static struct SetupAutoSetupInfo sasi;
8573 static struct SetupEditorInfo sei;
8574 static struct SetupEditorCascadeInfo seci;
8575 static struct SetupShortcutInfo ssi;
8576 static struct SetupInputInfo sii;
8577 static struct SetupSystemInfo syi;
8578 static struct SetupInternalInfo sxi;
8579 static struct SetupDebugInfo sdi;
8580 static struct OptionInfo soi;
8582 static struct TokenInfo global_setup_tokens[] =
8584 { TYPE_STRING, &si.player_name, "player_name" },
8585 { TYPE_SWITCH, &si.sound, "sound" },
8586 { TYPE_SWITCH, &si.sound_loops, "repeating_sound_loops" },
8587 { TYPE_SWITCH, &si.sound_music, "background_music" },
8588 { TYPE_SWITCH, &si.sound_simple, "simple_sound_effects" },
8589 { TYPE_SWITCH, &si.toons, "toons" },
8590 { TYPE_SWITCH, &si.scroll_delay, "scroll_delay" },
8591 { TYPE_INTEGER,&si.scroll_delay_value, "scroll_delay_value" },
8592 { TYPE_STRING, &si.engine_snapshot_mode, "engine_snapshot_mode" },
8593 { TYPE_INTEGER,&si.engine_snapshot_memory, "engine_snapshot_memory" },
8594 { TYPE_SWITCH, &si.fade_screens, "fade_screens" },
8595 { TYPE_SWITCH, &si.autorecord, "automatic_tape_recording"},
8596 { TYPE_SWITCH, &si.show_titlescreen, "show_titlescreen" },
8597 { TYPE_SWITCH, &si.quick_doors, "quick_doors" },
8598 { TYPE_SWITCH, &si.team_mode, "team_mode" },
8599 { TYPE_SWITCH, &si.handicap, "handicap" },
8600 { TYPE_SWITCH, &si.skip_levels, "skip_levels" },
8601 { TYPE_SWITCH, &si.increment_levels, "increment_levels" },
8602 { TYPE_SWITCH, &si.auto_play_next_level, "auto_play_next_level" },
8603 { TYPE_SWITCH, &si.skip_scores_after_game, "skip_scores_after_game" },
8604 { TYPE_SWITCH, &si.time_limit, "time_limit" },
8605 { TYPE_SWITCH, &si.fullscreen, "fullscreen" },
8606 { TYPE_INTEGER,&si.window_scaling_percent, "window_scaling_percent" },
8607 { TYPE_STRING, &si.window_scaling_quality, "window_scaling_quality" },
8608 { TYPE_STRING, &si.screen_rendering_mode, "screen_rendering_mode" },
8609 { TYPE_STRING, &si.vsync_mode, "vsync_mode" },
8610 { TYPE_SWITCH, &si.ask_on_escape, "ask_on_escape" },
8611 { TYPE_SWITCH, &si.ask_on_escape_editor, "ask_on_escape_editor" },
8612 { TYPE_SWITCH, &si.quick_switch, "quick_player_switch" },
8613 { TYPE_SWITCH, &si.input_on_focus, "input_on_focus" },
8614 { TYPE_SWITCH, &si.prefer_aga_graphics, "prefer_aga_graphics" },
8615 { TYPE_SWITCH, &si.game_speed_extended, "game_speed_extended" },
8616 { TYPE_INTEGER,&si.game_frame_delay, "game_frame_delay" },
8617 { TYPE_SWITCH, &si.sp_show_border_elements, "sp_show_border_elements" },
8618 { TYPE_SWITCH, &si.small_game_graphics, "small_game_graphics" },
8619 { TYPE_SWITCH, &si.show_snapshot_buttons, "show_snapshot_buttons" },
8620 { TYPE_STRING, &si.graphics_set, "graphics_set" },
8621 { TYPE_STRING, &si.sounds_set, "sounds_set" },
8622 { TYPE_STRING, &si.music_set, "music_set" },
8623 { TYPE_SWITCH3,&si.override_level_graphics, "override_level_graphics" },
8624 { TYPE_SWITCH3,&si.override_level_sounds, "override_level_sounds" },
8625 { TYPE_SWITCH3,&si.override_level_music, "override_level_music" },
8626 { TYPE_INTEGER,&si.volume_simple, "volume_simple" },
8627 { TYPE_INTEGER,&si.volume_loops, "volume_loops" },
8628 { TYPE_INTEGER,&si.volume_music, "volume_music" },
8629 { TYPE_SWITCH, &si.network_mode, "network_mode" },
8630 { TYPE_PLAYER, &si.network_player_nr, "network_player" },
8631 { TYPE_STRING, &si.network_server_hostname, "network_server_hostname" },
8632 { TYPE_STRING, &si.touch.control_type, "touch.control_type" },
8633 { TYPE_INTEGER,&si.touch.move_distance, "touch.move_distance" },
8634 { TYPE_INTEGER,&si.touch.drop_distance, "touch.drop_distance" },
8635 { TYPE_INTEGER,&si.touch.transparency, "touch.transparency" },
8636 { TYPE_INTEGER,&si.touch.draw_outlined, "touch.draw_outlined" },
8637 { TYPE_INTEGER,&si.touch.draw_pressed, "touch.draw_pressed" },
8638 { TYPE_INTEGER,&si.touch.grid_xsize[0], "touch.virtual_buttons.0.xsize" },
8639 { TYPE_INTEGER,&si.touch.grid_ysize[0], "touch.virtual_buttons.0.ysize" },
8640 { TYPE_INTEGER,&si.touch.grid_xsize[1], "touch.virtual_buttons.1.xsize" },
8641 { TYPE_INTEGER,&si.touch.grid_ysize[1], "touch.virtual_buttons.1.ysize" },
8644 static struct TokenInfo auto_setup_tokens[] =
8646 { TYPE_INTEGER,&sasi.editor_zoom_tilesize, "editor.zoom_tilesize" },
8649 static struct TokenInfo editor_setup_tokens[] =
8651 { TYPE_SWITCH, &sei.el_classic, "editor.el_classic" },
8652 { TYPE_SWITCH, &sei.el_custom, "editor.el_custom" },
8653 { TYPE_SWITCH, &sei.el_user_defined, "editor.el_user_defined" },
8654 { TYPE_SWITCH, &sei.el_dynamic, "editor.el_dynamic" },
8655 { TYPE_SWITCH, &sei.el_headlines, "editor.el_headlines" },
8656 { TYPE_SWITCH, &sei.show_element_token,"editor.show_element_token" },
8659 static struct TokenInfo editor_cascade_setup_tokens[] =
8661 { TYPE_SWITCH, &seci.el_bd, "editor.cascade.el_bd" },
8662 { TYPE_SWITCH, &seci.el_em, "editor.cascade.el_em" },
8663 { TYPE_SWITCH, &seci.el_emc, "editor.cascade.el_emc" },
8664 { TYPE_SWITCH, &seci.el_rnd, "editor.cascade.el_rnd" },
8665 { TYPE_SWITCH, &seci.el_sb, "editor.cascade.el_sb" },
8666 { TYPE_SWITCH, &seci.el_sp, "editor.cascade.el_sp" },
8667 { TYPE_SWITCH, &seci.el_dc, "editor.cascade.el_dc" },
8668 { TYPE_SWITCH, &seci.el_dx, "editor.cascade.el_dx" },
8669 { TYPE_SWITCH, &seci.el_mm, "editor.cascade.el_mm" },
8670 { TYPE_SWITCH, &seci.el_df, "editor.cascade.el_df" },
8671 { TYPE_SWITCH, &seci.el_chars, "editor.cascade.el_chars" },
8672 { TYPE_SWITCH, &seci.el_steel_chars, "editor.cascade.el_steel_chars" },
8673 { TYPE_SWITCH, &seci.el_ce, "editor.cascade.el_ce" },
8674 { TYPE_SWITCH, &seci.el_ge, "editor.cascade.el_ge" },
8675 { TYPE_SWITCH, &seci.el_ref, "editor.cascade.el_ref" },
8676 { TYPE_SWITCH, &seci.el_user, "editor.cascade.el_user" },
8677 { TYPE_SWITCH, &seci.el_dynamic, "editor.cascade.el_dynamic" },
8680 static struct TokenInfo shortcut_setup_tokens[] =
8682 { TYPE_KEY_X11, &ssi.save_game, "shortcut.save_game" },
8683 { TYPE_KEY_X11, &ssi.load_game, "shortcut.load_game" },
8684 { TYPE_KEY_X11, &ssi.toggle_pause, "shortcut.toggle_pause" },
8685 { TYPE_KEY_X11, &ssi.focus_player[0], "shortcut.focus_player_1" },
8686 { TYPE_KEY_X11, &ssi.focus_player[1], "shortcut.focus_player_2" },
8687 { TYPE_KEY_X11, &ssi.focus_player[2], "shortcut.focus_player_3" },
8688 { TYPE_KEY_X11, &ssi.focus_player[3], "shortcut.focus_player_4" },
8689 { TYPE_KEY_X11, &ssi.focus_player_all,"shortcut.focus_player_all" },
8690 { TYPE_KEY_X11, &ssi.tape_eject, "shortcut.tape_eject" },
8691 { TYPE_KEY_X11, &ssi.tape_extra, "shortcut.tape_extra" },
8692 { TYPE_KEY_X11, &ssi.tape_stop, "shortcut.tape_stop" },
8693 { TYPE_KEY_X11, &ssi.tape_pause, "shortcut.tape_pause" },
8694 { TYPE_KEY_X11, &ssi.tape_record, "shortcut.tape_record" },
8695 { TYPE_KEY_X11, &ssi.tape_play, "shortcut.tape_play" },
8696 { TYPE_KEY_X11, &ssi.sound_simple, "shortcut.sound_simple" },
8697 { TYPE_KEY_X11, &ssi.sound_loops, "shortcut.sound_loops" },
8698 { TYPE_KEY_X11, &ssi.sound_music, "shortcut.sound_music" },
8699 { TYPE_KEY_X11, &ssi.snap_left, "shortcut.snap_left" },
8700 { TYPE_KEY_X11, &ssi.snap_right, "shortcut.snap_right" },
8701 { TYPE_KEY_X11, &ssi.snap_up, "shortcut.snap_up" },
8702 { TYPE_KEY_X11, &ssi.snap_down, "shortcut.snap_down" },
8705 static struct TokenInfo player_setup_tokens[] =
8707 { TYPE_BOOLEAN, &sii.use_joystick, ".use_joystick" },
8708 { TYPE_STRING, &sii.joy.device_name, ".joy.device_name" },
8709 { TYPE_INTEGER, &sii.joy.xleft, ".joy.xleft" },
8710 { TYPE_INTEGER, &sii.joy.xmiddle, ".joy.xmiddle" },
8711 { TYPE_INTEGER, &sii.joy.xright, ".joy.xright" },
8712 { TYPE_INTEGER, &sii.joy.yupper, ".joy.yupper" },
8713 { TYPE_INTEGER, &sii.joy.ymiddle, ".joy.ymiddle" },
8714 { TYPE_INTEGER, &sii.joy.ylower, ".joy.ylower" },
8715 { TYPE_INTEGER, &sii.joy.snap, ".joy.snap_field" },
8716 { TYPE_INTEGER, &sii.joy.drop, ".joy.place_bomb" },
8717 { TYPE_KEY_X11, &sii.key.left, ".key.move_left" },
8718 { TYPE_KEY_X11, &sii.key.right, ".key.move_right" },
8719 { TYPE_KEY_X11, &sii.key.up, ".key.move_up" },
8720 { TYPE_KEY_X11, &sii.key.down, ".key.move_down" },
8721 { TYPE_KEY_X11, &sii.key.snap, ".key.snap_field" },
8722 { TYPE_KEY_X11, &sii.key.drop, ".key.place_bomb" },
8725 static struct TokenInfo system_setup_tokens[] =
8727 { TYPE_STRING, &syi.sdl_videodriver, "system.sdl_videodriver" },
8728 { TYPE_STRING, &syi.sdl_audiodriver, "system.sdl_audiodriver" },
8729 { TYPE_INTEGER, &syi.audio_fragment_size,"system.audio_fragment_size" },
8732 static struct TokenInfo internal_setup_tokens[] =
8734 { TYPE_STRING, &sxi.program_title, "program_title" },
8735 { TYPE_STRING, &sxi.program_version, "program_version" },
8736 { TYPE_STRING, &sxi.program_author, "program_author" },
8737 { TYPE_STRING, &sxi.program_email, "program_email" },
8738 { TYPE_STRING, &sxi.program_website, "program_website" },
8739 { TYPE_STRING, &sxi.program_copyright, "program_copyright" },
8740 { TYPE_STRING, &sxi.program_company, "program_company" },
8741 { TYPE_STRING, &sxi.program_icon_file, "program_icon_file" },
8742 { TYPE_STRING, &sxi.default_graphics_set, "default_graphics_set" },
8743 { TYPE_STRING, &sxi.default_sounds_set, "default_sounds_set" },
8744 { TYPE_STRING, &sxi.default_music_set, "default_music_set" },
8745 { TYPE_STRING, &sxi.fallback_graphics_file, "fallback_graphics_file"},
8746 { TYPE_STRING, &sxi.fallback_sounds_file, "fallback_sounds_file" },
8747 { TYPE_STRING, &sxi.fallback_music_file, "fallback_music_file" },
8748 { TYPE_STRING, &sxi.default_level_series, "default_level_series" },
8749 { TYPE_BOOLEAN,&sxi.choose_from_top_leveldir, "choose_from_top_leveldir" },
8750 { TYPE_BOOLEAN,&sxi.show_scaling_in_title, "show_scaling_in_title" },
8751 { TYPE_INTEGER,&sxi.default_window_width, "default_window_width" },
8752 { TYPE_INTEGER,&sxi.default_window_height, "default_window_height" },
8755 static struct TokenInfo debug_setup_tokens[] =
8757 { TYPE_INTEGER, &sdi.frame_delay[0], "debug.frame_delay_0" },
8758 { TYPE_INTEGER, &sdi.frame_delay[1], "debug.frame_delay_1" },
8759 { TYPE_INTEGER, &sdi.frame_delay[2], "debug.frame_delay_2" },
8760 { TYPE_INTEGER, &sdi.frame_delay[3], "debug.frame_delay_3" },
8761 { TYPE_INTEGER, &sdi.frame_delay[4], "debug.frame_delay_4" },
8762 { TYPE_INTEGER, &sdi.frame_delay[5], "debug.frame_delay_5" },
8763 { TYPE_INTEGER, &sdi.frame_delay[6], "debug.frame_delay_6" },
8764 { TYPE_INTEGER, &sdi.frame_delay[7], "debug.frame_delay_7" },
8765 { TYPE_INTEGER, &sdi.frame_delay[8], "debug.frame_delay_8" },
8766 { TYPE_INTEGER, &sdi.frame_delay[9], "debug.frame_delay_9" },
8767 { TYPE_KEY_X11, &sdi.frame_delay_key[0], "debug.key.frame_delay_0" },
8768 { TYPE_KEY_X11, &sdi.frame_delay_key[1], "debug.key.frame_delay_1" },
8769 { TYPE_KEY_X11, &sdi.frame_delay_key[2], "debug.key.frame_delay_2" },
8770 { TYPE_KEY_X11, &sdi.frame_delay_key[3], "debug.key.frame_delay_3" },
8771 { TYPE_KEY_X11, &sdi.frame_delay_key[4], "debug.key.frame_delay_4" },
8772 { TYPE_KEY_X11, &sdi.frame_delay_key[5], "debug.key.frame_delay_5" },
8773 { TYPE_KEY_X11, &sdi.frame_delay_key[6], "debug.key.frame_delay_6" },
8774 { TYPE_KEY_X11, &sdi.frame_delay_key[7], "debug.key.frame_delay_7" },
8775 { TYPE_KEY_X11, &sdi.frame_delay_key[8], "debug.key.frame_delay_8" },
8776 { TYPE_KEY_X11, &sdi.frame_delay_key[9], "debug.key.frame_delay_9" },
8777 { TYPE_BOOLEAN, &sdi.frame_delay_use_mod_key,"debug.frame_delay.use_mod_key"},
8778 { TYPE_BOOLEAN, &sdi.frame_delay_game_only, "debug.frame_delay.game_only" },
8779 { TYPE_BOOLEAN, &sdi.show_frames_per_second, "debug.show_frames_per_second" },
8782 static struct TokenInfo options_setup_tokens[] =
8784 { TYPE_BOOLEAN, &soi.verbose, "options.verbose" },
8787 static char *get_corrected_login_name(char *login_name)
8789 /* needed because player name must be a fixed length string */
8790 char *login_name_new = checked_malloc(MAX_PLAYER_NAME_LEN + 1);
8792 strncpy(login_name_new, login_name, MAX_PLAYER_NAME_LEN);
8793 login_name_new[MAX_PLAYER_NAME_LEN] = '\0';
8795 if (strlen(login_name) > MAX_PLAYER_NAME_LEN) /* name has been cut */
8796 if (strchr(login_name_new, ' '))
8797 *strchr(login_name_new, ' ') = '\0';
8799 return login_name_new;
8802 static void setSetupInfoToDefaults(struct SetupInfo *si)
8806 si->player_name = get_corrected_login_name(getLoginName());
8809 si->sound_loops = TRUE;
8810 si->sound_music = TRUE;
8811 si->sound_simple = TRUE;
8813 si->scroll_delay = TRUE;
8814 si->scroll_delay_value = STD_SCROLL_DELAY;
8815 si->engine_snapshot_mode = getStringCopy(STR_SNAPSHOT_MODE_DEFAULT);
8816 si->engine_snapshot_memory = SNAPSHOT_MEMORY_DEFAULT;
8817 si->fade_screens = TRUE;
8818 si->autorecord = TRUE;
8819 si->show_titlescreen = TRUE;
8820 si->quick_doors = FALSE;
8821 si->team_mode = FALSE;
8822 si->handicap = TRUE;
8823 si->skip_levels = TRUE;
8824 si->increment_levels = TRUE;
8825 si->auto_play_next_level = TRUE;
8826 si->skip_scores_after_game = FALSE;
8827 si->time_limit = TRUE;
8828 si->fullscreen = FALSE;
8829 si->window_scaling_percent = STD_WINDOW_SCALING_PERCENT;
8830 si->window_scaling_quality = getStringCopy(SCALING_QUALITY_DEFAULT);
8831 si->screen_rendering_mode = getStringCopy(STR_SPECIAL_RENDERING_DEFAULT);
8832 si->vsync_mode = getStringCopy(STR_VSYNC_MODE_DEFAULT);
8833 si->ask_on_escape = TRUE;
8834 si->ask_on_escape_editor = TRUE;
8835 si->quick_switch = FALSE;
8836 si->input_on_focus = FALSE;
8837 si->prefer_aga_graphics = TRUE;
8838 si->game_speed_extended = FALSE;
8839 si->game_frame_delay = GAME_FRAME_DELAY;
8840 si->sp_show_border_elements = FALSE;
8841 si->small_game_graphics = FALSE;
8842 si->show_snapshot_buttons = FALSE;
8844 si->graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
8845 si->sounds_set = getStringCopy(SND_CLASSIC_SUBDIR);
8846 si->music_set = getStringCopy(MUS_CLASSIC_SUBDIR);
8848 si->override_level_graphics = FALSE;
8849 si->override_level_sounds = FALSE;
8850 si->override_level_music = FALSE;
8852 si->volume_simple = 100; /* percent */
8853 si->volume_loops = 100; /* percent */
8854 si->volume_music = 100; /* percent */
8856 si->network_mode = FALSE;
8857 si->network_player_nr = 0; /* first player */
8858 si->network_server_hostname = getStringCopy(STR_NETWORK_AUTO_DETECT);
8860 si->touch.control_type = getStringCopy(TOUCH_CONTROL_DEFAULT);
8861 si->touch.move_distance = TOUCH_MOVE_DISTANCE_DEFAULT; /* percent */
8862 si->touch.drop_distance = TOUCH_DROP_DISTANCE_DEFAULT; /* percent */
8863 si->touch.transparency = TOUCH_TRANSPARENCY_DEFAULT; /* percent */
8864 si->touch.draw_outlined = TRUE;
8865 si->touch.draw_pressed = TRUE;
8867 for (i = 0; i < 2; i++)
8869 char *default_grid_button[6][2] =
8875 { "111222", " vv " },
8876 { "111222", " vv " }
8878 int grid_xsize = DEFAULT_GRID_XSIZE(i);
8879 int grid_ysize = DEFAULT_GRID_YSIZE(i);
8880 int min_xsize = MIN(6, grid_xsize);
8881 int min_ysize = MIN(6, grid_ysize);
8882 int startx = grid_xsize - min_xsize;
8883 int starty = grid_ysize - min_ysize;
8886 // virtual buttons grid can only be set to defaults if video is initialized
8887 // (this will be repeated if virtual buttons are not loaded from setup file)
8888 if (video.initialized)
8890 si->touch.grid_xsize[i] = grid_xsize;
8891 si->touch.grid_ysize[i] = grid_ysize;
8895 si->touch.grid_xsize[i] = -1;
8896 si->touch.grid_ysize[i] = -1;
8899 for (x = 0; x < MAX_GRID_XSIZE; x++)
8900 for (y = 0; y < MAX_GRID_YSIZE; y++)
8901 si->touch.grid_button[i][x][y] = CHAR_GRID_BUTTON_NONE;
8903 for (x = 0; x < min_xsize; x++)
8904 for (y = 0; y < min_ysize; y++)
8905 si->touch.grid_button[i][x][starty + y] =
8906 default_grid_button[y][0][x];
8908 for (x = 0; x < min_xsize; x++)
8909 for (y = 0; y < min_ysize; y++)
8910 si->touch.grid_button[i][startx + x][starty + y] =
8911 default_grid_button[y][1][x];
8914 si->touch.grid_initialized = video.initialized;
8916 si->editor.el_boulderdash = TRUE;
8917 si->editor.el_emerald_mine = TRUE;
8918 si->editor.el_emerald_mine_club = TRUE;
8919 si->editor.el_more = TRUE;
8920 si->editor.el_sokoban = TRUE;
8921 si->editor.el_supaplex = TRUE;
8922 si->editor.el_diamond_caves = TRUE;
8923 si->editor.el_dx_boulderdash = TRUE;
8925 si->editor.el_mirror_magic = TRUE;
8926 si->editor.el_deflektor = TRUE;
8928 si->editor.el_chars = TRUE;
8929 si->editor.el_steel_chars = TRUE;
8931 si->editor.el_classic = TRUE;
8932 si->editor.el_custom = TRUE;
8934 si->editor.el_user_defined = FALSE;
8935 si->editor.el_dynamic = TRUE;
8937 si->editor.el_headlines = TRUE;
8939 si->editor.show_element_token = FALSE;
8941 si->editor.use_template_for_new_levels = TRUE;
8943 si->shortcut.save_game = DEFAULT_KEY_SAVE_GAME;
8944 si->shortcut.load_game = DEFAULT_KEY_LOAD_GAME;
8945 si->shortcut.toggle_pause = DEFAULT_KEY_TOGGLE_PAUSE;
8947 si->shortcut.focus_player[0] = DEFAULT_KEY_FOCUS_PLAYER_1;
8948 si->shortcut.focus_player[1] = DEFAULT_KEY_FOCUS_PLAYER_2;
8949 si->shortcut.focus_player[2] = DEFAULT_KEY_FOCUS_PLAYER_3;
8950 si->shortcut.focus_player[3] = DEFAULT_KEY_FOCUS_PLAYER_4;
8951 si->shortcut.focus_player_all = DEFAULT_KEY_FOCUS_PLAYER_ALL;
8953 si->shortcut.tape_eject = DEFAULT_KEY_TAPE_EJECT;
8954 si->shortcut.tape_extra = DEFAULT_KEY_TAPE_EXTRA;
8955 si->shortcut.tape_stop = DEFAULT_KEY_TAPE_STOP;
8956 si->shortcut.tape_pause = DEFAULT_KEY_TAPE_PAUSE;
8957 si->shortcut.tape_record = DEFAULT_KEY_TAPE_RECORD;
8958 si->shortcut.tape_play = DEFAULT_KEY_TAPE_PLAY;
8960 si->shortcut.sound_simple = DEFAULT_KEY_SOUND_SIMPLE;
8961 si->shortcut.sound_loops = DEFAULT_KEY_SOUND_LOOPS;
8962 si->shortcut.sound_music = DEFAULT_KEY_SOUND_MUSIC;
8964 si->shortcut.snap_left = DEFAULT_KEY_SNAP_LEFT;
8965 si->shortcut.snap_right = DEFAULT_KEY_SNAP_RIGHT;
8966 si->shortcut.snap_up = DEFAULT_KEY_SNAP_UP;
8967 si->shortcut.snap_down = DEFAULT_KEY_SNAP_DOWN;
8969 for (i = 0; i < MAX_PLAYERS; i++)
8971 si->input[i].use_joystick = FALSE;
8972 si->input[i].joy.device_name=getStringCopy(getDeviceNameFromJoystickNr(i));
8973 si->input[i].joy.xleft = JOYSTICK_XLEFT;
8974 si->input[i].joy.xmiddle = JOYSTICK_XMIDDLE;
8975 si->input[i].joy.xright = JOYSTICK_XRIGHT;
8976 si->input[i].joy.yupper = JOYSTICK_YUPPER;
8977 si->input[i].joy.ymiddle = JOYSTICK_YMIDDLE;
8978 si->input[i].joy.ylower = JOYSTICK_YLOWER;
8979 si->input[i].joy.snap = (i == 0 ? JOY_BUTTON_1 : 0);
8980 si->input[i].joy.drop = (i == 0 ? JOY_BUTTON_2 : 0);
8981 si->input[i].key.left = (i == 0 ? DEFAULT_KEY_LEFT : KSYM_UNDEFINED);
8982 si->input[i].key.right = (i == 0 ? DEFAULT_KEY_RIGHT : KSYM_UNDEFINED);
8983 si->input[i].key.up = (i == 0 ? DEFAULT_KEY_UP : KSYM_UNDEFINED);
8984 si->input[i].key.down = (i == 0 ? DEFAULT_KEY_DOWN : KSYM_UNDEFINED);
8985 si->input[i].key.snap = (i == 0 ? DEFAULT_KEY_SNAP : KSYM_UNDEFINED);
8986 si->input[i].key.drop = (i == 0 ? DEFAULT_KEY_DROP : KSYM_UNDEFINED);
8989 si->system.sdl_videodriver = getStringCopy(ARG_DEFAULT);
8990 si->system.sdl_audiodriver = getStringCopy(ARG_DEFAULT);
8991 si->system.audio_fragment_size = DEFAULT_AUDIO_FRAGMENT_SIZE;
8993 si->internal.program_title = getStringCopy(PROGRAM_TITLE_STRING);
8994 si->internal.program_version = getStringCopy(getProgramRealVersionString());
8995 si->internal.program_author = getStringCopy(PROGRAM_AUTHOR_STRING);
8996 si->internal.program_email = getStringCopy(PROGRAM_EMAIL_STRING);
8997 si->internal.program_website = getStringCopy(PROGRAM_WEBSITE_STRING);
8998 si->internal.program_copyright = getStringCopy(PROGRAM_COPYRIGHT_STRING);
8999 si->internal.program_company = getStringCopy(PROGRAM_COMPANY_STRING);
9001 si->internal.program_icon_file = getStringCopy(PROGRAM_ICON_FILENAME);
9003 si->internal.default_graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
9004 si->internal.default_sounds_set = getStringCopy(SND_CLASSIC_SUBDIR);
9005 si->internal.default_music_set = getStringCopy(MUS_CLASSIC_SUBDIR);
9007 si->internal.fallback_graphics_file = getStringCopy(UNDEFINED_FILENAME);
9008 si->internal.fallback_sounds_file = getStringCopy(UNDEFINED_FILENAME);
9009 si->internal.fallback_music_file = getStringCopy(UNDEFINED_FILENAME);
9011 si->internal.default_level_series = getStringCopy(UNDEFINED_LEVELSET);
9012 si->internal.choose_from_top_leveldir = FALSE;
9013 si->internal.show_scaling_in_title = TRUE;
9015 si->internal.default_window_width = WIN_XSIZE_DEFAULT;
9016 si->internal.default_window_height = WIN_YSIZE_DEFAULT;
9018 si->debug.frame_delay[0] = DEFAULT_FRAME_DELAY_0;
9019 si->debug.frame_delay[1] = DEFAULT_FRAME_DELAY_1;
9020 si->debug.frame_delay[2] = DEFAULT_FRAME_DELAY_2;
9021 si->debug.frame_delay[3] = DEFAULT_FRAME_DELAY_3;
9022 si->debug.frame_delay[4] = DEFAULT_FRAME_DELAY_4;
9023 si->debug.frame_delay[5] = DEFAULT_FRAME_DELAY_5;
9024 si->debug.frame_delay[6] = DEFAULT_FRAME_DELAY_6;
9025 si->debug.frame_delay[7] = DEFAULT_FRAME_DELAY_7;
9026 si->debug.frame_delay[8] = DEFAULT_FRAME_DELAY_8;
9027 si->debug.frame_delay[9] = DEFAULT_FRAME_DELAY_9;
9029 si->debug.frame_delay_key[0] = DEFAULT_KEY_FRAME_DELAY_0;
9030 si->debug.frame_delay_key[1] = DEFAULT_KEY_FRAME_DELAY_1;
9031 si->debug.frame_delay_key[2] = DEFAULT_KEY_FRAME_DELAY_2;
9032 si->debug.frame_delay_key[3] = DEFAULT_KEY_FRAME_DELAY_3;
9033 si->debug.frame_delay_key[4] = DEFAULT_KEY_FRAME_DELAY_4;
9034 si->debug.frame_delay_key[5] = DEFAULT_KEY_FRAME_DELAY_5;
9035 si->debug.frame_delay_key[6] = DEFAULT_KEY_FRAME_DELAY_6;
9036 si->debug.frame_delay_key[7] = DEFAULT_KEY_FRAME_DELAY_7;
9037 si->debug.frame_delay_key[8] = DEFAULT_KEY_FRAME_DELAY_8;
9038 si->debug.frame_delay_key[9] = DEFAULT_KEY_FRAME_DELAY_9;
9040 si->debug.frame_delay_use_mod_key = DEFAULT_FRAME_DELAY_USE_MOD_KEY;
9041 si->debug.frame_delay_game_only = DEFAULT_FRAME_DELAY_GAME_ONLY;
9043 si->debug.show_frames_per_second = FALSE;
9045 si->options.verbose = FALSE;
9047 #if defined(PLATFORM_ANDROID)
9048 si->fullscreen = TRUE;
9052 static void setSetupInfoToDefaults_AutoSetup(struct SetupInfo *si)
9054 si->auto_setup.editor_zoom_tilesize = MINI_TILESIZE;
9057 static void setSetupInfoToDefaults_EditorCascade(struct SetupInfo *si)
9059 si->editor_cascade.el_bd = TRUE;
9060 si->editor_cascade.el_em = TRUE;
9061 si->editor_cascade.el_emc = TRUE;
9062 si->editor_cascade.el_rnd = TRUE;
9063 si->editor_cascade.el_sb = TRUE;
9064 si->editor_cascade.el_sp = TRUE;
9065 si->editor_cascade.el_dc = TRUE;
9066 si->editor_cascade.el_dx = TRUE;
9068 si->editor_cascade.el_mm = TRUE;
9069 si->editor_cascade.el_df = TRUE;
9071 si->editor_cascade.el_chars = FALSE;
9072 si->editor_cascade.el_steel_chars = FALSE;
9073 si->editor_cascade.el_ce = FALSE;
9074 si->editor_cascade.el_ge = FALSE;
9075 si->editor_cascade.el_ref = FALSE;
9076 si->editor_cascade.el_user = FALSE;
9077 si->editor_cascade.el_dynamic = FALSE;
9080 #define MAX_HIDE_SETUP_TOKEN_SIZE 20
9082 static char *getHideSetupToken(void *setup_value)
9084 static char hide_setup_token[MAX_HIDE_SETUP_TOKEN_SIZE];
9086 if (setup_value != NULL)
9087 snprintf(hide_setup_token, MAX_HIDE_SETUP_TOKEN_SIZE, "%p", setup_value);
9089 return hide_setup_token;
9092 void setHideSetupEntry(void *setup_value)
9094 char *hide_setup_token = getHideSetupToken(setup_value);
9096 if (setup_value != NULL)
9097 setHashEntry(hide_setup_hash, hide_setup_token, "");
9100 static void setHideSetupEntryRaw(char *token_text, void *setup_value_raw)
9102 /* !!! DIRTY WORKAROUND; TO BE FIXED AFTER THE MM ENGINE RELEASE !!! */
9103 void *setup_value = setup_value_raw - (void *)&si + (void *)&setup;
9105 setHideSetupEntry(setup_value);
9108 boolean hideSetupEntry(void *setup_value)
9110 char *hide_setup_token = getHideSetupToken(setup_value);
9112 return (setup_value != NULL &&
9113 getHashEntry(hide_setup_hash, hide_setup_token) != NULL);
9116 static void setSetupInfoFromTokenText(SetupFileHash *setup_file_hash,
9117 struct TokenInfo *token_info,
9118 int token_nr, char *token_text)
9120 char *token_hide_text = getStringCat2(token_text, ".hide");
9121 char *token_hide_value = getHashEntry(setup_file_hash, token_hide_text);
9123 /* set the value of this setup option in the setup option structure */
9124 setSetupInfo(token_info, token_nr, getHashEntry(setup_file_hash, token_text));
9126 /* check if this setup option should be hidden in the setup menu */
9127 if (token_hide_value != NULL && get_boolean_from_string(token_hide_value))
9128 setHideSetupEntryRaw(token_text, token_info[token_nr].value);
9131 static void setSetupInfoFromTokenInfo(SetupFileHash *setup_file_hash,
9132 struct TokenInfo *token_info,
9135 setSetupInfoFromTokenText(setup_file_hash, token_info, token_nr,
9136 token_info[token_nr].text);
9139 static void decodeSetupFileHash(SetupFileHash *setup_file_hash)
9143 if (!setup_file_hash)
9146 if (hide_setup_hash == NULL)
9147 hide_setup_hash = newSetupFileHash();
9151 for (i = 0; i < NUM_GLOBAL_SETUP_TOKENS; i++)
9152 setSetupInfoFromTokenInfo(setup_file_hash, global_setup_tokens, i);
9155 /* virtual buttons setup */
9156 setup.touch.grid_initialized = TRUE;
9157 for (i = 0; i < 2; i++)
9159 int grid_xsize = setup.touch.grid_xsize[i];
9160 int grid_ysize = setup.touch.grid_ysize[i];
9163 // if virtual buttons are not loaded from setup file, repeat initializing
9164 // virtual buttons grid with default values later when video is initialized
9165 if (grid_xsize == -1 ||
9168 setup.touch.grid_initialized = FALSE;
9173 for (y = 0; y < grid_ysize; y++)
9175 char token_string[MAX_LINE_LEN];
9177 sprintf(token_string, "touch.virtual_buttons.%d.%02d", i, y);
9179 char *value_string = getHashEntry(setup_file_hash, token_string);
9181 if (value_string == NULL)
9184 for (x = 0; x < grid_xsize; x++)
9186 char c = value_string[x];
9188 setup.touch.grid_button[i][x][y] =
9189 (c == '.' ? CHAR_GRID_BUTTON_NONE : c);
9196 for (i = 0; i < NUM_EDITOR_SETUP_TOKENS; i++)
9197 setSetupInfoFromTokenInfo(setup_file_hash, editor_setup_tokens, i);
9200 /* shortcut setup */
9201 ssi = setup.shortcut;
9202 for (i = 0; i < NUM_SHORTCUT_SETUP_TOKENS; i++)
9203 setSetupInfoFromTokenInfo(setup_file_hash, shortcut_setup_tokens, i);
9204 setup.shortcut = ssi;
9207 for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
9211 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
9213 sii = setup.input[pnr];
9214 for (i = 0; i < NUM_PLAYER_SETUP_TOKENS; i++)
9216 char full_token[100];
9218 sprintf(full_token, "%s%s", prefix, player_setup_tokens[i].text);
9219 setSetupInfoFromTokenText(setup_file_hash, player_setup_tokens, i,
9222 setup.input[pnr] = sii;
9227 for (i = 0; i < NUM_SYSTEM_SETUP_TOKENS; i++)
9228 setSetupInfoFromTokenInfo(setup_file_hash, system_setup_tokens, i);
9231 /* internal setup */
9232 sxi = setup.internal;
9233 for (i = 0; i < NUM_INTERNAL_SETUP_TOKENS; i++)
9234 setSetupInfoFromTokenInfo(setup_file_hash, internal_setup_tokens, i);
9235 setup.internal = sxi;
9239 for (i = 0; i < NUM_DEBUG_SETUP_TOKENS; i++)
9240 setSetupInfoFromTokenInfo(setup_file_hash, debug_setup_tokens, i);
9244 soi = setup.options;
9245 for (i = 0; i < NUM_OPTIONS_SETUP_TOKENS; i++)
9246 setSetupInfoFromTokenInfo(setup_file_hash, options_setup_tokens, i);
9247 setup.options = soi;
9249 setHideRelatedSetupEntries();
9252 static void decodeSetupFileHash_AutoSetup(SetupFileHash *setup_file_hash)
9256 if (!setup_file_hash)
9260 sasi = setup.auto_setup;
9261 for (i = 0; i < NUM_AUTO_SETUP_TOKENS; i++)
9262 setSetupInfo(auto_setup_tokens, i,
9263 getHashEntry(setup_file_hash,
9264 auto_setup_tokens[i].text));
9265 setup.auto_setup = sasi;
9268 static void decodeSetupFileHash_EditorCascade(SetupFileHash *setup_file_hash)
9272 if (!setup_file_hash)
9275 /* editor cascade setup */
9276 seci = setup.editor_cascade;
9277 for (i = 0; i < NUM_EDITOR_CASCADE_SETUP_TOKENS; i++)
9278 setSetupInfo(editor_cascade_setup_tokens, i,
9279 getHashEntry(setup_file_hash,
9280 editor_cascade_setup_tokens[i].text));
9281 setup.editor_cascade = seci;
9284 void LoadSetupFromFilename(char *filename)
9286 SetupFileHash *setup_file_hash = loadSetupFileHash(filename);
9288 if (setup_file_hash)
9290 decodeSetupFileHash(setup_file_hash);
9292 freeSetupFileHash(setup_file_hash);
9296 Error(ERR_DEBUG, "using default setup values");
9300 static void LoadSetup_SpecialPostProcessing(void)
9302 char *player_name_new;
9304 /* needed to work around problems with fixed length strings */
9305 player_name_new = get_corrected_login_name(setup.player_name);
9306 free(setup.player_name);
9307 setup.player_name = player_name_new;
9309 /* "scroll_delay: on(3) / off(0)" was replaced by scroll delay value */
9310 if (setup.scroll_delay == FALSE)
9312 setup.scroll_delay_value = MIN_SCROLL_DELAY;
9313 setup.scroll_delay = TRUE; /* now always "on" */
9316 /* make sure that scroll delay value stays inside valid range */
9317 setup.scroll_delay_value =
9318 MIN(MAX(MIN_SCROLL_DELAY, setup.scroll_delay_value), MAX_SCROLL_DELAY);
9321 void LoadSetup(void)
9325 /* always start with reliable default values */
9326 setSetupInfoToDefaults(&setup);
9328 /* try to load setup values from default setup file */
9329 filename = getDefaultSetupFilename();
9331 if (fileExists(filename))
9332 LoadSetupFromFilename(filename);
9334 /* try to load setup values from user setup file */
9335 filename = getSetupFilename();
9337 LoadSetupFromFilename(filename);
9339 LoadSetup_SpecialPostProcessing();
9342 void LoadSetup_AutoSetup(void)
9344 char *filename = getPath2(getSetupDir(), AUTOSETUP_FILENAME);
9345 SetupFileHash *setup_file_hash = NULL;
9347 /* always start with reliable default values */
9348 setSetupInfoToDefaults_AutoSetup(&setup);
9350 setup_file_hash = loadSetupFileHash(filename);
9352 if (setup_file_hash)
9354 decodeSetupFileHash_AutoSetup(setup_file_hash);
9356 freeSetupFileHash(setup_file_hash);
9362 void LoadSetup_EditorCascade(void)
9364 char *filename = getPath2(getSetupDir(), EDITORCASCADE_FILENAME);
9365 SetupFileHash *setup_file_hash = NULL;
9367 /* always start with reliable default values */
9368 setSetupInfoToDefaults_EditorCascade(&setup);
9370 setup_file_hash = loadSetupFileHash(filename);
9372 if (setup_file_hash)
9374 decodeSetupFileHash_EditorCascade(setup_file_hash);
9376 freeSetupFileHash(setup_file_hash);
9382 static void addGameControllerMappingToHash(SetupFileHash *mappings_hash,
9385 char mapping_guid[MAX_LINE_LEN];
9386 char *mapping_start, *mapping_end;
9388 // get GUID from game controller mapping line: copy complete line
9389 strncpy(mapping_guid, mapping_line, MAX_LINE_LEN - 1);
9390 mapping_guid[MAX_LINE_LEN - 1] = '\0';
9392 // get GUID from game controller mapping line: cut after GUID part
9393 mapping_start = strchr(mapping_guid, ',');
9394 if (mapping_start != NULL)
9395 *mapping_start = '\0';
9397 // cut newline from game controller mapping line
9398 mapping_end = strchr(mapping_line, '\n');
9399 if (mapping_end != NULL)
9400 *mapping_end = '\0';
9402 // add mapping entry to game controller mappings hash
9403 setHashEntry(mappings_hash, mapping_guid, mapping_line);
9406 static void LoadSetup_ReadGameControllerMappings(SetupFileHash *mappings_hash,
9411 if (!(file = fopen(filename, MODE_READ)))
9413 Error(ERR_WARN, "cannot read game controller mappings file '%s'", filename);
9420 char line[MAX_LINE_LEN];
9422 if (!fgets(line, MAX_LINE_LEN, file))
9425 addGameControllerMappingToHash(mappings_hash, line);
9431 void SaveSetup(void)
9433 char *filename = getSetupFilename();
9437 InitUserDataDirectory();
9439 if (!(file = fopen(filename, MODE_WRITE)))
9441 Error(ERR_WARN, "cannot write setup file '%s'", filename);
9445 fprintFileHeader(file, SETUP_FILENAME);
9449 for (i = 0; i < NUM_GLOBAL_SETUP_TOKENS; i++)
9451 /* just to make things nicer :) */
9452 if (i == SETUP_TOKEN_PLAYER_NAME + 1 ||
9453 i == SETUP_TOKEN_GRAPHICS_SET ||
9454 i == SETUP_TOKEN_VOLUME_SIMPLE ||
9455 i == SETUP_TOKEN_NETWORK_MODE ||
9456 i == SETUP_TOKEN_TOUCH_CONTROL_TYPE ||
9457 i == SETUP_TOKEN_TOUCH_GRID_XSIZE_0 ||
9458 i == SETUP_TOKEN_TOUCH_GRID_XSIZE_1)
9459 fprintf(file, "\n");
9461 fprintf(file, "%s\n", getSetupLine(global_setup_tokens, "", i));
9464 /* virtual buttons setup */
9465 for (i = 0; i < 2; i++)
9467 int grid_xsize = setup.touch.grid_xsize[i];
9468 int grid_ysize = setup.touch.grid_ysize[i];
9471 fprintf(file, "\n");
9473 for (y = 0; y < grid_ysize; y++)
9475 char token_string[MAX_LINE_LEN];
9476 char value_string[MAX_LINE_LEN];
9478 sprintf(token_string, "touch.virtual_buttons.%d.%02d", i, y);
9480 for (x = 0; x < grid_xsize; x++)
9482 char c = setup.touch.grid_button[i][x][y];
9484 value_string[x] = (c == CHAR_GRID_BUTTON_NONE ? '.' : c);
9487 value_string[grid_xsize] = '\0';
9489 fprintf(file, "%s\n", getFormattedSetupEntry(token_string, value_string));
9495 fprintf(file, "\n");
9496 for (i = 0; i < NUM_EDITOR_SETUP_TOKENS; i++)
9497 fprintf(file, "%s\n", getSetupLine(editor_setup_tokens, "", i));
9499 /* shortcut setup */
9500 ssi = setup.shortcut;
9501 fprintf(file, "\n");
9502 for (i = 0; i < NUM_SHORTCUT_SETUP_TOKENS; i++)
9503 fprintf(file, "%s\n", getSetupLine(shortcut_setup_tokens, "", i));
9506 for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
9510 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
9511 fprintf(file, "\n");
9513 sii = setup.input[pnr];
9514 for (i = 0; i < NUM_PLAYER_SETUP_TOKENS; i++)
9515 fprintf(file, "%s\n", getSetupLine(player_setup_tokens, prefix, i));
9520 fprintf(file, "\n");
9521 for (i = 0; i < NUM_SYSTEM_SETUP_TOKENS; i++)
9522 fprintf(file, "%s\n", getSetupLine(system_setup_tokens, "", i));
9524 /* internal setup */
9525 /* (internal setup values not saved to user setup file) */
9529 fprintf(file, "\n");
9530 for (i = 0; i < NUM_DEBUG_SETUP_TOKENS; i++)
9531 fprintf(file, "%s\n", getSetupLine(debug_setup_tokens, "", i));
9534 soi = setup.options;
9535 fprintf(file, "\n");
9536 for (i = 0; i < NUM_OPTIONS_SETUP_TOKENS; i++)
9537 fprintf(file, "%s\n", getSetupLine(options_setup_tokens, "", i));
9541 SetFilePermissions(filename, PERMS_PRIVATE);
9544 void SaveSetup_AutoSetup(void)
9546 char *filename = getPath2(getSetupDir(), AUTOSETUP_FILENAME);
9550 InitUserDataDirectory();
9552 if (!(file = fopen(filename, MODE_WRITE)))
9554 Error(ERR_WARN, "cannot write auto setup file '%s'", filename);
9559 fprintFileHeader(file, AUTOSETUP_FILENAME);
9561 sasi = setup.auto_setup;
9562 for (i = 0; i < NUM_AUTO_SETUP_TOKENS; i++)
9563 fprintf(file, "%s\n", getSetupLine(auto_setup_tokens, "", i));
9567 SetFilePermissions(filename, PERMS_PRIVATE);
9572 void SaveSetup_EditorCascade(void)
9574 char *filename = getPath2(getSetupDir(), EDITORCASCADE_FILENAME);
9578 InitUserDataDirectory();
9580 if (!(file = fopen(filename, MODE_WRITE)))
9582 Error(ERR_WARN, "cannot write editor cascade state file '%s'", filename);
9587 fprintFileHeader(file, EDITORCASCADE_FILENAME);
9589 seci = setup.editor_cascade;
9590 for (i = 0; i < NUM_EDITOR_CASCADE_SETUP_TOKENS; i++)
9591 fprintf(file, "%s\n", getSetupLine(editor_cascade_setup_tokens, "", i));
9595 SetFilePermissions(filename, PERMS_PRIVATE);
9600 static void SaveSetup_WriteGameControllerMappings(SetupFileHash *mappings_hash,
9605 if (!(file = fopen(filename, MODE_WRITE)))
9607 Error(ERR_WARN, "cannot write game controller mappings file '%s'",filename);
9612 BEGIN_HASH_ITERATION(mappings_hash, itr)
9614 fprintf(file, "%s\n", HASH_ITERATION_VALUE(itr));
9616 END_HASH_ITERATION(mappings_hash, itr)
9621 void SaveSetup_AddGameControllerMapping(char *mapping)
9623 char *filename = getPath2(getSetupDir(), GAMECONTROLLER_BASENAME);
9624 SetupFileHash *mappings_hash = newSetupFileHash();
9626 InitUserDataDirectory();
9628 // load existing personal game controller mappings
9629 LoadSetup_ReadGameControllerMappings(mappings_hash, filename);
9631 // add new mapping to personal game controller mappings
9632 addGameControllerMappingToHash(mappings_hash, mapping);
9634 // save updated personal game controller mappings
9635 SaveSetup_WriteGameControllerMappings(mappings_hash, filename);
9637 freeSetupFileHash(mappings_hash);
9641 void LoadCustomElementDescriptions(void)
9643 char *filename = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
9644 SetupFileHash *setup_file_hash;
9647 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
9649 if (element_info[i].custom_description != NULL)
9651 free(element_info[i].custom_description);
9652 element_info[i].custom_description = NULL;
9656 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
9659 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
9661 char *token = getStringCat2(element_info[i].token_name, ".name");
9662 char *value = getHashEntry(setup_file_hash, token);
9665 element_info[i].custom_description = getStringCopy(value);
9670 freeSetupFileHash(setup_file_hash);
9673 static int getElementFromToken(char *token)
9675 char *value = getHashEntry(element_token_hash, token);
9680 Error(ERR_WARN, "unknown element token '%s'", token);
9682 return EL_UNDEFINED;
9685 static int get_token_parameter_value(char *token, char *value_raw)
9689 if (token == NULL || value_raw == NULL)
9690 return ARG_UNDEFINED_VALUE;
9692 suffix = strrchr(token, '.');
9696 if (strEqual(suffix, ".element"))
9697 return getElementFromToken(value_raw);
9699 /* !!! USE CORRECT VALUE TYPE (currently works also for TYPE_BOOLEAN) !!! */
9700 return get_parameter_value(value_raw, suffix, TYPE_INTEGER);
9703 void InitMenuDesignSettings_Static(void)
9707 /* always start with reliable default values from static default config */
9708 for (i = 0; image_config_vars[i].token != NULL; i++)
9710 char *value = getHashEntry(image_config_hash, image_config_vars[i].token);
9713 *image_config_vars[i].value =
9714 get_token_parameter_value(image_config_vars[i].token, value);
9718 static void InitMenuDesignSettings_SpecialPreProcessing(void)
9722 /* the following initializes hierarchical values from static configuration */
9724 /* special case: initialize "ARG_DEFAULT" values in static default config */
9725 /* (e.g., initialize "[titlemessage].fade_mode" from "[title].fade_mode") */
9726 titlescreen_initial_first_default.fade_mode =
9727 title_initial_first_default.fade_mode;
9728 titlescreen_initial_first_default.fade_delay =
9729 title_initial_first_default.fade_delay;
9730 titlescreen_initial_first_default.post_delay =
9731 title_initial_first_default.post_delay;
9732 titlescreen_initial_first_default.auto_delay =
9733 title_initial_first_default.auto_delay;
9734 titlescreen_first_default.fade_mode = title_first_default.fade_mode;
9735 titlescreen_first_default.fade_delay = title_first_default.fade_delay;
9736 titlescreen_first_default.post_delay = title_first_default.post_delay;
9737 titlescreen_first_default.auto_delay = title_first_default.auto_delay;
9738 titlemessage_initial_first_default.fade_mode =
9739 title_initial_first_default.fade_mode;
9740 titlemessage_initial_first_default.fade_delay =
9741 title_initial_first_default.fade_delay;
9742 titlemessage_initial_first_default.post_delay =
9743 title_initial_first_default.post_delay;
9744 titlemessage_initial_first_default.auto_delay =
9745 title_initial_first_default.auto_delay;
9746 titlemessage_first_default.fade_mode = title_first_default.fade_mode;
9747 titlemessage_first_default.fade_delay = title_first_default.fade_delay;
9748 titlemessage_first_default.post_delay = title_first_default.post_delay;
9749 titlemessage_first_default.auto_delay = title_first_default.auto_delay;
9751 titlescreen_initial_default.fade_mode = title_initial_default.fade_mode;
9752 titlescreen_initial_default.fade_delay = title_initial_default.fade_delay;
9753 titlescreen_initial_default.post_delay = title_initial_default.post_delay;
9754 titlescreen_initial_default.auto_delay = title_initial_default.auto_delay;
9755 titlescreen_default.fade_mode = title_default.fade_mode;
9756 titlescreen_default.fade_delay = title_default.fade_delay;
9757 titlescreen_default.post_delay = title_default.post_delay;
9758 titlescreen_default.auto_delay = title_default.auto_delay;
9759 titlemessage_initial_default.fade_mode = title_initial_default.fade_mode;
9760 titlemessage_initial_default.fade_delay = title_initial_default.fade_delay;
9761 titlemessage_initial_default.post_delay = title_initial_default.post_delay;
9762 titlemessage_initial_default.auto_delay = title_initial_default.auto_delay;
9763 titlemessage_default.fade_mode = title_default.fade_mode;
9764 titlemessage_default.fade_delay = title_default.fade_delay;
9765 titlemessage_default.post_delay = title_default.post_delay;
9766 titlemessage_default.auto_delay = title_default.auto_delay;
9768 /* special case: initialize "ARG_DEFAULT" values in static default config */
9769 /* (e.g., init "titlemessage_1.fade_mode" from "[titlemessage].fade_mode") */
9770 for (i = 0; i < MAX_NUM_TITLE_MESSAGES; i++)
9772 titlescreen_initial_first[i] = titlescreen_initial_first_default;
9773 titlescreen_first[i] = titlescreen_first_default;
9774 titlemessage_initial_first[i] = titlemessage_initial_first_default;
9775 titlemessage_first[i] = titlemessage_first_default;
9777 titlescreen_initial[i] = titlescreen_initial_default;
9778 titlescreen[i] = titlescreen_default;
9779 titlemessage_initial[i] = titlemessage_initial_default;
9780 titlemessage[i] = titlemessage_default;
9783 /* special case: initialize "ARG_DEFAULT" values in static default config */
9784 /* (eg, init "menu.enter_screen.SCORES.xyz" from "menu.enter_screen.xyz") */
9785 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
9787 if (i == GFX_SPECIAL_ARG_TITLE) /* title values already initialized */
9790 menu.enter_screen[i] = menu.enter_screen[GFX_SPECIAL_ARG_DEFAULT];
9791 menu.leave_screen[i] = menu.leave_screen[GFX_SPECIAL_ARG_DEFAULT];
9792 menu.next_screen[i] = menu.next_screen[GFX_SPECIAL_ARG_DEFAULT];
9795 /* special case: initialize "ARG_DEFAULT" values in static default config */
9796 /* (eg, init "viewport.door_1.MAIN.xyz" from "viewport.door_1.xyz") */
9797 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
9799 viewport.window[i] = viewport.window[GFX_SPECIAL_ARG_DEFAULT];
9800 viewport.playfield[i] = viewport.playfield[GFX_SPECIAL_ARG_DEFAULT];
9801 viewport.door_1[i] = viewport.door_1[GFX_SPECIAL_ARG_DEFAULT];
9803 if (i == GFX_SPECIAL_ARG_EDITOR) /* editor values already initialized */
9806 viewport.door_2[i] = viewport.door_2[GFX_SPECIAL_ARG_DEFAULT];
9810 static void InitMenuDesignSettings_SpecialPostProcessing(void)
9814 struct XY *dst, *src;
9818 { &game.button.save, &game.button.stop },
9819 { &game.button.pause2, &game.button.pause },
9820 { &game.button.load, &game.button.play },
9821 { &game.button.undo, &game.button.stop },
9822 { &game.button.redo, &game.button.play },
9828 /* special case: initialize later added SETUP list size from LEVELS value */
9829 if (menu.list_size[GAME_MODE_SETUP] == -1)
9830 menu.list_size[GAME_MODE_SETUP] = menu.list_size[GAME_MODE_LEVELS];
9832 /* set default position for snapshot buttons to stop/pause/play buttons */
9833 for (i = 0; game_buttons_xy[i].dst != NULL; i++)
9834 if ((*game_buttons_xy[i].dst).x == -1 &&
9835 (*game_buttons_xy[i].dst).y == -1)
9836 *game_buttons_xy[i].dst = *game_buttons_xy[i].src;
9839 static void InitMenuDesignSettings_SpecialPostProcessing_AfterGraphics(void)
9843 struct XYTileSize *dst, *src;
9846 editor_buttons_xy[] =
9849 &editor.button.element_left, &editor.palette.element_left,
9850 IMG_GFX_EDITOR_BUTTON_ELEMENT_LEFT
9853 &editor.button.element_middle, &editor.palette.element_middle,
9854 IMG_GFX_EDITOR_BUTTON_ELEMENT_MIDDLE
9857 &editor.button.element_right, &editor.palette.element_right,
9858 IMG_GFX_EDITOR_BUTTON_ELEMENT_RIGHT
9865 /* set default position for element buttons to element graphics */
9866 for (i = 0; editor_buttons_xy[i].dst != NULL; i++)
9868 if ((*editor_buttons_xy[i].dst).x == -1 &&
9869 (*editor_buttons_xy[i].dst).y == -1)
9871 struct GraphicInfo *gd = &graphic_info[editor_buttons_xy[i].graphic];
9873 gd->width = gd->height = editor_buttons_xy[i].src->tile_size;
9875 *editor_buttons_xy[i].dst = *editor_buttons_xy[i].src;
9880 static void LoadMenuDesignSettingsFromFilename(char *filename)
9882 static struct TitleFadingInfo tfi;
9883 static struct TitleMessageInfo tmi;
9884 static struct TokenInfo title_tokens[] =
9886 { TYPE_INTEGER, &tfi.fade_mode, ".fade_mode" },
9887 { TYPE_INTEGER, &tfi.fade_delay, ".fade_delay" },
9888 { TYPE_INTEGER, &tfi.post_delay, ".post_delay" },
9889 { TYPE_INTEGER, &tfi.auto_delay, ".auto_delay" },
9893 static struct TokenInfo titlemessage_tokens[] =
9895 { TYPE_INTEGER, &tmi.x, ".x" },
9896 { TYPE_INTEGER, &tmi.y, ".y" },
9897 { TYPE_INTEGER, &tmi.width, ".width" },
9898 { TYPE_INTEGER, &tmi.height, ".height" },
9899 { TYPE_INTEGER, &tmi.chars, ".chars" },
9900 { TYPE_INTEGER, &tmi.lines, ".lines" },
9901 { TYPE_INTEGER, &tmi.align, ".align" },
9902 { TYPE_INTEGER, &tmi.valign, ".valign" },
9903 { TYPE_INTEGER, &tmi.font, ".font" },
9904 { TYPE_BOOLEAN, &tmi.autowrap, ".autowrap" },
9905 { TYPE_BOOLEAN, &tmi.centered, ".centered" },
9906 { TYPE_BOOLEAN, &tmi.parse_comments, ".parse_comments" },
9907 { TYPE_INTEGER, &tmi.sort_priority, ".sort_priority" },
9908 { TYPE_INTEGER, &tmi.fade_mode, ".fade_mode" },
9909 { TYPE_INTEGER, &tmi.fade_delay, ".fade_delay" },
9910 { TYPE_INTEGER, &tmi.post_delay, ".post_delay" },
9911 { TYPE_INTEGER, &tmi.auto_delay, ".auto_delay" },
9917 struct TitleFadingInfo *info;
9922 /* initialize first titles from "enter screen" definitions, if defined */
9923 { &title_initial_first_default, "menu.enter_screen.TITLE" },
9924 { &title_first_default, "menu.enter_screen.TITLE" },
9926 /* initialize title screens from "next screen" definitions, if defined */
9927 { &title_initial_default, "menu.next_screen.TITLE" },
9928 { &title_default, "menu.next_screen.TITLE" },
9934 struct TitleMessageInfo *array;
9937 titlemessage_arrays[] =
9939 /* initialize first titles from "enter screen" definitions, if defined */
9940 { titlescreen_initial_first, "menu.enter_screen.TITLE" },
9941 { titlescreen_first, "menu.enter_screen.TITLE" },
9942 { titlemessage_initial_first, "menu.enter_screen.TITLE" },
9943 { titlemessage_first, "menu.enter_screen.TITLE" },
9945 /* initialize titles from "next screen" definitions, if defined */
9946 { titlescreen_initial, "menu.next_screen.TITLE" },
9947 { titlescreen, "menu.next_screen.TITLE" },
9948 { titlemessage_initial, "menu.next_screen.TITLE" },
9949 { titlemessage, "menu.next_screen.TITLE" },
9951 /* overwrite titles with title definitions, if defined */
9952 { titlescreen_initial_first, "[title_initial]" },
9953 { titlescreen_first, "[title]" },
9954 { titlemessage_initial_first, "[title_initial]" },
9955 { titlemessage_first, "[title]" },
9957 { titlescreen_initial, "[title_initial]" },
9958 { titlescreen, "[title]" },
9959 { titlemessage_initial, "[title_initial]" },
9960 { titlemessage, "[title]" },
9962 /* overwrite titles with title screen/message definitions, if defined */
9963 { titlescreen_initial_first, "[titlescreen_initial]" },
9964 { titlescreen_first, "[titlescreen]" },
9965 { titlemessage_initial_first, "[titlemessage_initial]" },
9966 { titlemessage_first, "[titlemessage]" },
9968 { titlescreen_initial, "[titlescreen_initial]" },
9969 { titlescreen, "[titlescreen]" },
9970 { titlemessage_initial, "[titlemessage_initial]" },
9971 { titlemessage, "[titlemessage]" },
9975 SetupFileHash *setup_file_hash;
9978 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
9981 /* the following initializes hierarchical values from dynamic configuration */
9983 /* special case: initialize with default values that may be overwritten */
9984 /* (e.g., init "menu.draw_xoffset.INFO" from "menu.draw_xoffset") */
9985 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
9987 char *value_1 = getHashEntry(setup_file_hash, "menu.draw_xoffset");
9988 char *value_2 = getHashEntry(setup_file_hash, "menu.draw_yoffset");
9989 char *value_3 = getHashEntry(setup_file_hash, "menu.list_size");
9991 if (value_1 != NULL)
9992 menu.draw_xoffset[i] = get_integer_from_string(value_1);
9993 if (value_2 != NULL)
9994 menu.draw_yoffset[i] = get_integer_from_string(value_2);
9995 if (value_3 != NULL)
9996 menu.list_size[i] = get_integer_from_string(value_3);
9999 /* special case: initialize with default values that may be overwritten */
10000 /* (eg, init "menu.draw_xoffset.INFO[XXX]" from "menu.draw_xoffset.INFO") */
10001 for (i = 0; i < NUM_SPECIAL_GFX_INFO_ARGS; i++)
10003 char *value_1 = getHashEntry(setup_file_hash, "menu.draw_xoffset.INFO");
10004 char *value_2 = getHashEntry(setup_file_hash, "menu.draw_yoffset.INFO");
10006 if (value_1 != NULL)
10007 menu.draw_xoffset_info[i] = get_integer_from_string(value_1);
10008 if (value_2 != NULL)
10009 menu.draw_yoffset_info[i] = get_integer_from_string(value_2);
10011 if (i == GFX_SPECIAL_ARG_INFO_ELEMENTS)
10013 char *value_1 = getHashEntry(setup_file_hash, "menu.list_size.INFO");
10015 if (value_1 != NULL)
10016 menu.list_size_info[i] = get_integer_from_string(value_1);
10020 /* special case: initialize with default values that may be overwritten */
10021 /* (eg, init "menu.draw_xoffset.SETUP[XXX]" from "menu.draw_xoffset.SETUP") */
10022 for (i = 0; i < NUM_SPECIAL_GFX_SETUP_ARGS; i++)
10024 char *value_1 = getHashEntry(setup_file_hash, "menu.draw_xoffset.SETUP");
10025 char *value_2 = getHashEntry(setup_file_hash, "menu.draw_yoffset.SETUP");
10027 if (value_1 != NULL)
10028 menu.draw_xoffset_setup[i] = get_integer_from_string(value_1);
10029 if (value_2 != NULL)
10030 menu.draw_yoffset_setup[i] = get_integer_from_string(value_2);
10033 /* special case: initialize with default values that may be overwritten */
10034 /* (eg, init "menu.line_spacing.INFO[XXX]" from "menu.line_spacing.INFO") */
10035 for (i = 0; i < NUM_SPECIAL_GFX_INFO_ARGS; i++)
10037 char *value_1 = getHashEntry(setup_file_hash,"menu.left_spacing.INFO");
10038 char *value_2 = getHashEntry(setup_file_hash,"menu.right_spacing.INFO");
10039 char *value_3 = getHashEntry(setup_file_hash,"menu.top_spacing.INFO");
10040 char *value_4 = getHashEntry(setup_file_hash,"menu.bottom_spacing.INFO");
10041 char *value_5 = getHashEntry(setup_file_hash,"menu.paragraph_spacing.INFO");
10042 char *value_6 = getHashEntry(setup_file_hash,"menu.headline1_spacing.INFO");
10043 char *value_7 = getHashEntry(setup_file_hash,"menu.headline2_spacing.INFO");
10044 char *value_8 = getHashEntry(setup_file_hash,"menu.line_spacing.INFO");
10045 char *value_9 = getHashEntry(setup_file_hash,"menu.extra_spacing.INFO");
10047 if (value_1 != NULL)
10048 menu.left_spacing_info[i] = get_integer_from_string(value_1);
10049 if (value_2 != NULL)
10050 menu.right_spacing_info[i] = get_integer_from_string(value_2);
10051 if (value_3 != NULL)
10052 menu.top_spacing_info[i] = get_integer_from_string(value_3);
10053 if (value_4 != NULL)
10054 menu.bottom_spacing_info[i] = get_integer_from_string(value_4);
10055 if (value_5 != NULL)
10056 menu.paragraph_spacing_info[i] = get_integer_from_string(value_5);
10057 if (value_6 != NULL)
10058 menu.headline1_spacing_info[i] = get_integer_from_string(value_6);
10059 if (value_7 != NULL)
10060 menu.headline2_spacing_info[i] = get_integer_from_string(value_7);
10061 if (value_8 != NULL)
10062 menu.line_spacing_info[i] = get_integer_from_string(value_8);
10063 if (value_9 != NULL)
10064 menu.extra_spacing_info[i] = get_integer_from_string(value_9);
10067 /* special case: initialize with default values that may be overwritten */
10068 /* (eg, init "menu.enter_screen.SCORES.xyz" from "menu.enter_screen.xyz") */
10069 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
10071 char *token_1 = "menu.enter_screen.fade_mode";
10072 char *token_2 = "menu.enter_screen.fade_delay";
10073 char *token_3 = "menu.enter_screen.post_delay";
10074 char *token_4 = "menu.leave_screen.fade_mode";
10075 char *token_5 = "menu.leave_screen.fade_delay";
10076 char *token_6 = "menu.leave_screen.post_delay";
10077 char *token_7 = "menu.next_screen.fade_mode";
10078 char *token_8 = "menu.next_screen.fade_delay";
10079 char *token_9 = "menu.next_screen.post_delay";
10080 char *value_1 = getHashEntry(setup_file_hash, token_1);
10081 char *value_2 = getHashEntry(setup_file_hash, token_2);
10082 char *value_3 = getHashEntry(setup_file_hash, token_3);
10083 char *value_4 = getHashEntry(setup_file_hash, token_4);
10084 char *value_5 = getHashEntry(setup_file_hash, token_5);
10085 char *value_6 = getHashEntry(setup_file_hash, token_6);
10086 char *value_7 = getHashEntry(setup_file_hash, token_7);
10087 char *value_8 = getHashEntry(setup_file_hash, token_8);
10088 char *value_9 = getHashEntry(setup_file_hash, token_9);
10090 if (value_1 != NULL)
10091 menu.enter_screen[i].fade_mode = get_token_parameter_value(token_1,
10093 if (value_2 != NULL)
10094 menu.enter_screen[i].fade_delay = get_token_parameter_value(token_2,
10096 if (value_3 != NULL)
10097 menu.enter_screen[i].post_delay = get_token_parameter_value(token_3,
10099 if (value_4 != NULL)
10100 menu.leave_screen[i].fade_mode = get_token_parameter_value(token_4,
10102 if (value_5 != NULL)
10103 menu.leave_screen[i].fade_delay = get_token_parameter_value(token_5,
10105 if (value_6 != NULL)
10106 menu.leave_screen[i].post_delay = get_token_parameter_value(token_6,
10108 if (value_7 != NULL)
10109 menu.next_screen[i].fade_mode = get_token_parameter_value(token_7,
10111 if (value_8 != NULL)
10112 menu.next_screen[i].fade_delay = get_token_parameter_value(token_8,
10114 if (value_9 != NULL)
10115 menu.next_screen[i].post_delay = get_token_parameter_value(token_9,
10119 /* special case: initialize with default values that may be overwritten */
10120 /* (eg, init "viewport.door_1.MAIN.xyz" from "viewport.door_1.xyz") */
10121 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
10123 char *token_w1 = "viewport.window.width";
10124 char *token_w2 = "viewport.window.height";
10125 char *token_01 = "viewport.playfield.x";
10126 char *token_02 = "viewport.playfield.y";
10127 char *token_03 = "viewport.playfield.width";
10128 char *token_04 = "viewport.playfield.height";
10129 char *token_05 = "viewport.playfield.border_size";
10130 char *token_06 = "viewport.door_1.x";
10131 char *token_07 = "viewport.door_1.y";
10132 char *token_08 = "viewport.door_1.width";
10133 char *token_09 = "viewport.door_1.height";
10134 char *token_10 = "viewport.door_1.border_size";
10135 char *token_11 = "viewport.door_2.x";
10136 char *token_12 = "viewport.door_2.y";
10137 char *token_13 = "viewport.door_2.width";
10138 char *token_14 = "viewport.door_2.height";
10139 char *token_15 = "viewport.door_2.border_size";
10140 char *value_w1 = getHashEntry(setup_file_hash, token_w1);
10141 char *value_w2 = getHashEntry(setup_file_hash, token_w2);
10142 char *value_01 = getHashEntry(setup_file_hash, token_01);
10143 char *value_02 = getHashEntry(setup_file_hash, token_02);
10144 char *value_03 = getHashEntry(setup_file_hash, token_03);
10145 char *value_04 = getHashEntry(setup_file_hash, token_04);
10146 char *value_05 = getHashEntry(setup_file_hash, token_05);
10147 char *value_06 = getHashEntry(setup_file_hash, token_06);
10148 char *value_07 = getHashEntry(setup_file_hash, token_07);
10149 char *value_08 = getHashEntry(setup_file_hash, token_08);
10150 char *value_09 = getHashEntry(setup_file_hash, token_09);
10151 char *value_10 = getHashEntry(setup_file_hash, token_10);
10152 char *value_11 = getHashEntry(setup_file_hash, token_11);
10153 char *value_12 = getHashEntry(setup_file_hash, token_12);
10154 char *value_13 = getHashEntry(setup_file_hash, token_13);
10155 char *value_14 = getHashEntry(setup_file_hash, token_14);
10156 char *value_15 = getHashEntry(setup_file_hash, token_15);
10158 if (value_w1 != NULL)
10159 viewport.window[i].width = get_token_parameter_value(token_w1, value_w1);
10160 if (value_w2 != NULL)
10161 viewport.window[i].height = get_token_parameter_value(token_w2, value_w2);
10162 if (value_01 != NULL)
10163 viewport.playfield[i].x = get_token_parameter_value(token_01, value_01);
10164 if (value_02 != NULL)
10165 viewport.playfield[i].y = get_token_parameter_value(token_02, value_02);
10166 if (value_03 != NULL)
10167 viewport.playfield[i].width = get_token_parameter_value(token_03,
10169 if (value_04 != NULL)
10170 viewport.playfield[i].height = get_token_parameter_value(token_04,
10172 if (value_05 != NULL)
10173 viewport.playfield[i].border_size = get_token_parameter_value(token_05,
10175 if (value_06 != NULL)
10176 viewport.door_1[i].x = get_token_parameter_value(token_06, value_06);
10177 if (value_07 != NULL)
10178 viewport.door_1[i].y = get_token_parameter_value(token_07, value_07);
10179 if (value_08 != NULL)
10180 viewport.door_1[i].width = get_token_parameter_value(token_08, value_08);
10181 if (value_09 != NULL)
10182 viewport.door_1[i].height = get_token_parameter_value(token_09, value_09);
10183 if (value_10 != NULL)
10184 viewport.door_1[i].border_size = get_token_parameter_value(token_10,
10186 if (value_11 != NULL)
10187 viewport.door_2[i].x = get_token_parameter_value(token_11, value_11);
10188 if (value_12 != NULL)
10189 viewport.door_2[i].y = get_token_parameter_value(token_12, value_12);
10190 if (value_13 != NULL)
10191 viewport.door_2[i].width = get_token_parameter_value(token_13, value_13);
10192 if (value_14 != NULL)
10193 viewport.door_2[i].height = get_token_parameter_value(token_14, value_14);
10194 if (value_15 != NULL)
10195 viewport.door_1[i].border_size = get_token_parameter_value(token_15,
10199 /* special case: initialize with default values that may be overwritten */
10200 /* (e.g., init "[title].fade_mode" from "menu.next_screen.TITLE.fade_mode") */
10201 for (i = 0; title_info[i].info != NULL; i++)
10203 struct TitleFadingInfo *info = title_info[i].info;
10204 char *base_token = title_info[i].text;
10206 for (j = 0; title_tokens[j].type != -1; j++)
10208 char *token = getStringCat2(base_token, title_tokens[j].text);
10209 char *value = getHashEntry(setup_file_hash, token);
10213 int parameter_value = get_token_parameter_value(token, value);
10217 *(int *)title_tokens[j].value = (int)parameter_value;
10226 /* special case: initialize with default values that may be overwritten */
10227 /* (e.g., init "titlemessage_1.fade_mode" from "[titlemessage].fade_mode") */
10228 for (i = 0; titlemessage_arrays[i].array != NULL; i++)
10230 struct TitleMessageInfo *array = titlemessage_arrays[i].array;
10231 char *base_token = titlemessage_arrays[i].text;
10233 for (j = 0; titlemessage_tokens[j].type != -1; j++)
10235 char *token = getStringCat2(base_token, titlemessage_tokens[j].text);
10236 char *value = getHashEntry(setup_file_hash, token);
10240 int parameter_value = get_token_parameter_value(token, value);
10242 for (k = 0; k < MAX_NUM_TITLE_MESSAGES; k++)
10246 if (titlemessage_tokens[j].type == TYPE_INTEGER)
10247 *(int *)titlemessage_tokens[j].value = (int)parameter_value;
10249 *(boolean *)titlemessage_tokens[j].value = (boolean)parameter_value;
10259 /* read (and overwrite with) values that may be specified in config file */
10260 for (i = 0; image_config_vars[i].token != NULL; i++)
10262 char *value = getHashEntry(setup_file_hash, image_config_vars[i].token);
10264 /* (ignore definitions set to "[DEFAULT]" which are already initialized) */
10265 if (value != NULL && !strEqual(value, ARG_DEFAULT))
10266 *image_config_vars[i].value =
10267 get_token_parameter_value(image_config_vars[i].token, value);
10270 freeSetupFileHash(setup_file_hash);
10273 void LoadMenuDesignSettings(void)
10275 char *filename_base = UNDEFINED_FILENAME, *filename_local;
10277 InitMenuDesignSettings_Static();
10278 InitMenuDesignSettings_SpecialPreProcessing();
10280 if (!GFX_OVERRIDE_ARTWORK(ARTWORK_TYPE_GRAPHICS))
10282 /* first look for special settings configured in level series config */
10283 filename_base = getCustomArtworkLevelConfigFilename(ARTWORK_TYPE_GRAPHICS);
10285 if (fileExists(filename_base))
10286 LoadMenuDesignSettingsFromFilename(filename_base);
10289 filename_local = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
10291 if (filename_local != NULL && !strEqual(filename_base, filename_local))
10292 LoadMenuDesignSettingsFromFilename(filename_local);
10294 InitMenuDesignSettings_SpecialPostProcessing();
10297 void LoadMenuDesignSettings_AfterGraphics(void)
10299 InitMenuDesignSettings_SpecialPostProcessing_AfterGraphics();
10302 void LoadUserDefinedEditorElementList(int **elements, int *num_elements)
10304 char *filename = getEditorSetupFilename();
10305 SetupFileList *setup_file_list, *list;
10306 SetupFileHash *element_hash;
10307 int num_unknown_tokens = 0;
10310 if ((setup_file_list = loadSetupFileList(filename)) == NULL)
10313 element_hash = newSetupFileHash();
10315 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
10316 setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
10318 /* determined size may be larger than needed (due to unknown elements) */
10320 for (list = setup_file_list; list != NULL; list = list->next)
10323 /* add space for up to 3 more elements for padding that may be needed */
10324 *num_elements += 3;
10326 /* free memory for old list of elements, if needed */
10327 checked_free(*elements);
10329 /* allocate memory for new list of elements */
10330 *elements = checked_malloc(*num_elements * sizeof(int));
10333 for (list = setup_file_list; list != NULL; list = list->next)
10335 char *value = getHashEntry(element_hash, list->token);
10337 if (value == NULL) /* try to find obsolete token mapping */
10339 char *mapped_token = get_mapped_token(list->token);
10341 if (mapped_token != NULL)
10343 value = getHashEntry(element_hash, mapped_token);
10345 free(mapped_token);
10351 (*elements)[(*num_elements)++] = atoi(value);
10355 if (num_unknown_tokens == 0)
10357 Error(ERR_INFO_LINE, "-");
10358 Error(ERR_INFO, "warning: unknown token(s) found in config file:");
10359 Error(ERR_INFO, "- config file: '%s'", filename);
10361 num_unknown_tokens++;
10364 Error(ERR_INFO, "- token: '%s'", list->token);
10368 if (num_unknown_tokens > 0)
10369 Error(ERR_INFO_LINE, "-");
10371 while (*num_elements % 4) /* pad with empty elements, if needed */
10372 (*elements)[(*num_elements)++] = EL_EMPTY;
10374 freeSetupFileList(setup_file_list);
10375 freeSetupFileHash(element_hash);
10378 for (i = 0; i < *num_elements; i++)
10379 printf("editor: element '%s' [%d]\n",
10380 element_info[(*elements)[i]].token_name, (*elements)[i]);
10384 static struct MusicFileInfo *get_music_file_info_ext(char *basename, int music,
10387 SetupFileHash *setup_file_hash = NULL;
10388 struct MusicFileInfo tmp_music_file_info, *new_music_file_info;
10389 char *filename_music, *filename_prefix, *filename_info;
10395 token_to_value_ptr[] =
10397 { "title_header", &tmp_music_file_info.title_header },
10398 { "artist_header", &tmp_music_file_info.artist_header },
10399 { "album_header", &tmp_music_file_info.album_header },
10400 { "year_header", &tmp_music_file_info.year_header },
10402 { "title", &tmp_music_file_info.title },
10403 { "artist", &tmp_music_file_info.artist },
10404 { "album", &tmp_music_file_info.album },
10405 { "year", &tmp_music_file_info.year },
10411 filename_music = (is_sound ? getCustomSoundFilename(basename) :
10412 getCustomMusicFilename(basename));
10414 if (filename_music == NULL)
10417 /* ---------- try to replace file extension ---------- */
10419 filename_prefix = getStringCopy(filename_music);
10420 if (strrchr(filename_prefix, '.') != NULL)
10421 *strrchr(filename_prefix, '.') = '\0';
10422 filename_info = getStringCat2(filename_prefix, ".txt");
10424 if (fileExists(filename_info))
10425 setup_file_hash = loadSetupFileHash(filename_info);
10427 free(filename_prefix);
10428 free(filename_info);
10430 if (setup_file_hash == NULL)
10432 /* ---------- try to add file extension ---------- */
10434 filename_prefix = getStringCopy(filename_music);
10435 filename_info = getStringCat2(filename_prefix, ".txt");
10437 if (fileExists(filename_info))
10438 setup_file_hash = loadSetupFileHash(filename_info);
10440 free(filename_prefix);
10441 free(filename_info);
10444 if (setup_file_hash == NULL)
10447 /* ---------- music file info found ---------- */
10449 clear_mem(&tmp_music_file_info, sizeof(struct MusicFileInfo));
10451 for (i = 0; token_to_value_ptr[i].token != NULL; i++)
10453 char *value = getHashEntry(setup_file_hash, token_to_value_ptr[i].token);
10455 *token_to_value_ptr[i].value_ptr =
10456 getStringCopy(value != NULL && *value != '\0' ? value : UNKNOWN_NAME);
10459 tmp_music_file_info.basename = getStringCopy(basename);
10460 tmp_music_file_info.music = music;
10461 tmp_music_file_info.is_sound = is_sound;
10463 new_music_file_info = checked_malloc(sizeof(struct MusicFileInfo));
10464 *new_music_file_info = tmp_music_file_info;
10466 return new_music_file_info;
10469 static struct MusicFileInfo *get_music_file_info(char *basename, int music)
10471 return get_music_file_info_ext(basename, music, FALSE);
10474 static struct MusicFileInfo *get_sound_file_info(char *basename, int sound)
10476 return get_music_file_info_ext(basename, sound, TRUE);
10479 static boolean music_info_listed_ext(struct MusicFileInfo *list,
10480 char *basename, boolean is_sound)
10482 for (; list != NULL; list = list->next)
10483 if (list->is_sound == is_sound && strEqual(list->basename, basename))
10489 static boolean music_info_listed(struct MusicFileInfo *list, char *basename)
10491 return music_info_listed_ext(list, basename, FALSE);
10494 static boolean sound_info_listed(struct MusicFileInfo *list, char *basename)
10496 return music_info_listed_ext(list, basename, TRUE);
10499 void LoadMusicInfo(void)
10501 char *music_directory = getCustomMusicDirectory();
10502 int num_music = getMusicListSize();
10503 int num_music_noconf = 0;
10504 int num_sounds = getSoundListSize();
10506 DirectoryEntry *dir_entry;
10507 struct FileInfo *music, *sound;
10508 struct MusicFileInfo *next, **new;
10511 while (music_file_info != NULL)
10513 next = music_file_info->next;
10515 checked_free(music_file_info->basename);
10517 checked_free(music_file_info->title_header);
10518 checked_free(music_file_info->artist_header);
10519 checked_free(music_file_info->album_header);
10520 checked_free(music_file_info->year_header);
10522 checked_free(music_file_info->title);
10523 checked_free(music_file_info->artist);
10524 checked_free(music_file_info->album);
10525 checked_free(music_file_info->year);
10527 free(music_file_info);
10529 music_file_info = next;
10532 new = &music_file_info;
10534 for (i = 0; i < num_music; i++)
10536 music = getMusicListEntry(i);
10538 if (music->filename == NULL)
10541 if (strEqual(music->filename, UNDEFINED_FILENAME))
10544 /* a configured file may be not recognized as music */
10545 if (!FileIsMusic(music->filename))
10548 if (!music_info_listed(music_file_info, music->filename))
10550 *new = get_music_file_info(music->filename, i);
10553 new = &(*new)->next;
10557 if ((dir = openDirectory(music_directory)) == NULL)
10559 Error(ERR_WARN, "cannot read music directory '%s'", music_directory);
10563 while ((dir_entry = readDirectory(dir)) != NULL) /* loop all entries */
10565 char *basename = dir_entry->basename;
10566 boolean music_already_used = FALSE;
10569 /* skip all music files that are configured in music config file */
10570 for (i = 0; i < num_music; i++)
10572 music = getMusicListEntry(i);
10574 if (music->filename == NULL)
10577 if (strEqual(basename, music->filename))
10579 music_already_used = TRUE;
10584 if (music_already_used)
10587 if (!FileIsMusic(dir_entry->filename))
10590 if (!music_info_listed(music_file_info, basename))
10592 *new = get_music_file_info(basename, MAP_NOCONF_MUSIC(num_music_noconf));
10595 new = &(*new)->next;
10598 num_music_noconf++;
10601 closeDirectory(dir);
10603 for (i = 0; i < num_sounds; i++)
10605 sound = getSoundListEntry(i);
10607 if (sound->filename == NULL)
10610 if (strEqual(sound->filename, UNDEFINED_FILENAME))
10613 /* a configured file may be not recognized as sound */
10614 if (!FileIsSound(sound->filename))
10617 if (!sound_info_listed(music_file_info, sound->filename))
10619 *new = get_sound_file_info(sound->filename, i);
10621 new = &(*new)->next;
10626 static void add_helpanim_entry(int element, int action, int direction,
10627 int delay, int *num_list_entries)
10629 struct HelpAnimInfo *new_list_entry;
10630 (*num_list_entries)++;
10633 checked_realloc(helpanim_info,
10634 *num_list_entries * sizeof(struct HelpAnimInfo));
10635 new_list_entry = &helpanim_info[*num_list_entries - 1];
10637 new_list_entry->element = element;
10638 new_list_entry->action = action;
10639 new_list_entry->direction = direction;
10640 new_list_entry->delay = delay;
10643 static void print_unknown_token(char *filename, char *token, int token_nr)
10647 Error(ERR_INFO_LINE, "-");
10648 Error(ERR_INFO, "warning: unknown token(s) found in config file:");
10649 Error(ERR_INFO, "- config file: '%s'", filename);
10652 Error(ERR_INFO, "- token: '%s'", token);
10655 static void print_unknown_token_end(int token_nr)
10658 Error(ERR_INFO_LINE, "-");
10661 void LoadHelpAnimInfo(void)
10663 char *filename = getHelpAnimFilename();
10664 SetupFileList *setup_file_list = NULL, *list;
10665 SetupFileHash *element_hash, *action_hash, *direction_hash;
10666 int num_list_entries = 0;
10667 int num_unknown_tokens = 0;
10670 if (fileExists(filename))
10671 setup_file_list = loadSetupFileList(filename);
10673 if (setup_file_list == NULL)
10675 /* use reliable default values from static configuration */
10676 SetupFileList *insert_ptr;
10678 insert_ptr = setup_file_list =
10679 newSetupFileList(helpanim_config[0].token,
10680 helpanim_config[0].value);
10682 for (i = 1; helpanim_config[i].token; i++)
10683 insert_ptr = addListEntry(insert_ptr,
10684 helpanim_config[i].token,
10685 helpanim_config[i].value);
10688 element_hash = newSetupFileHash();
10689 action_hash = newSetupFileHash();
10690 direction_hash = newSetupFileHash();
10692 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
10693 setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
10695 for (i = 0; i < NUM_ACTIONS; i++)
10696 setHashEntry(action_hash, element_action_info[i].suffix,
10697 i_to_a(element_action_info[i].value));
10699 /* do not store direction index (bit) here, but direction value! */
10700 for (i = 0; i < NUM_DIRECTIONS_FULL; i++)
10701 setHashEntry(direction_hash, element_direction_info[i].suffix,
10702 i_to_a(1 << element_direction_info[i].value));
10704 for (list = setup_file_list; list != NULL; list = list->next)
10706 char *element_token, *action_token, *direction_token;
10707 char *element_value, *action_value, *direction_value;
10708 int delay = atoi(list->value);
10710 if (strEqual(list->token, "end"))
10712 add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
10717 /* first try to break element into element/action/direction parts;
10718 if this does not work, also accept combined "element[.act][.dir]"
10719 elements (like "dynamite.active"), which are unique elements */
10721 if (strchr(list->token, '.') == NULL) /* token contains no '.' */
10723 element_value = getHashEntry(element_hash, list->token);
10724 if (element_value != NULL) /* element found */
10725 add_helpanim_entry(atoi(element_value), -1, -1, delay,
10726 &num_list_entries);
10729 /* no further suffixes found -- this is not an element */
10730 print_unknown_token(filename, list->token, num_unknown_tokens++);
10736 /* token has format "<prefix>.<something>" */
10738 action_token = strchr(list->token, '.'); /* suffix may be action ... */
10739 direction_token = action_token; /* ... or direction */
10741 element_token = getStringCopy(list->token);
10742 *strchr(element_token, '.') = '\0';
10744 element_value = getHashEntry(element_hash, element_token);
10746 if (element_value == NULL) /* this is no element */
10748 element_value = getHashEntry(element_hash, list->token);
10749 if (element_value != NULL) /* combined element found */
10750 add_helpanim_entry(atoi(element_value), -1, -1, delay,
10751 &num_list_entries);
10753 print_unknown_token(filename, list->token, num_unknown_tokens++);
10755 free(element_token);
10760 action_value = getHashEntry(action_hash, action_token);
10762 if (action_value != NULL) /* action found */
10764 add_helpanim_entry(atoi(element_value), atoi(action_value), -1, delay,
10765 &num_list_entries);
10767 free(element_token);
10772 direction_value = getHashEntry(direction_hash, direction_token);
10774 if (direction_value != NULL) /* direction found */
10776 add_helpanim_entry(atoi(element_value), -1, atoi(direction_value), delay,
10777 &num_list_entries);
10779 free(element_token);
10784 if (strchr(action_token + 1, '.') == NULL)
10786 /* no further suffixes found -- this is not an action nor direction */
10788 element_value = getHashEntry(element_hash, list->token);
10789 if (element_value != NULL) /* combined element found */
10790 add_helpanim_entry(atoi(element_value), -1, -1, delay,
10791 &num_list_entries);
10793 print_unknown_token(filename, list->token, num_unknown_tokens++);
10795 free(element_token);
10800 /* token has format "<prefix>.<suffix>.<something>" */
10802 direction_token = strchr(action_token + 1, '.');
10804 action_token = getStringCopy(action_token);
10805 *strchr(action_token + 1, '.') = '\0';
10807 action_value = getHashEntry(action_hash, action_token);
10809 if (action_value == NULL) /* this is no action */
10811 element_value = getHashEntry(element_hash, list->token);
10812 if (element_value != NULL) /* combined element found */
10813 add_helpanim_entry(atoi(element_value), -1, -1, delay,
10814 &num_list_entries);
10816 print_unknown_token(filename, list->token, num_unknown_tokens++);
10818 free(element_token);
10819 free(action_token);
10824 direction_value = getHashEntry(direction_hash, direction_token);
10826 if (direction_value != NULL) /* direction found */
10828 add_helpanim_entry(atoi(element_value), atoi(action_value),
10829 atoi(direction_value), delay, &num_list_entries);
10831 free(element_token);
10832 free(action_token);
10837 /* this is no direction */
10839 element_value = getHashEntry(element_hash, list->token);
10840 if (element_value != NULL) /* combined element found */
10841 add_helpanim_entry(atoi(element_value), -1, -1, delay,
10842 &num_list_entries);
10844 print_unknown_token(filename, list->token, num_unknown_tokens++);
10846 free(element_token);
10847 free(action_token);
10850 print_unknown_token_end(num_unknown_tokens);
10852 add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
10853 add_helpanim_entry(HELPANIM_LIST_END, -1, -1, -1, &num_list_entries);
10855 freeSetupFileList(setup_file_list);
10856 freeSetupFileHash(element_hash);
10857 freeSetupFileHash(action_hash);
10858 freeSetupFileHash(direction_hash);
10861 for (i = 0; i < num_list_entries; i++)
10862 printf("::: '%s': %d, %d, %d => %d\n",
10863 EL_NAME(helpanim_info[i].element),
10864 helpanim_info[i].element,
10865 helpanim_info[i].action,
10866 helpanim_info[i].direction,
10867 helpanim_info[i].delay);
10871 void LoadHelpTextInfo(void)
10873 char *filename = getHelpTextFilename();
10876 if (helptext_info != NULL)
10878 freeSetupFileHash(helptext_info);
10879 helptext_info = NULL;
10882 if (fileExists(filename))
10883 helptext_info = loadSetupFileHash(filename);
10885 if (helptext_info == NULL)
10887 /* use reliable default values from static configuration */
10888 helptext_info = newSetupFileHash();
10890 for (i = 0; helptext_config[i].token; i++)
10891 setHashEntry(helptext_info,
10892 helptext_config[i].token,
10893 helptext_config[i].value);
10897 BEGIN_HASH_ITERATION(helptext_info, itr)
10899 printf("::: '%s' => '%s'\n",
10900 HASH_ITERATION_TOKEN(itr), HASH_ITERATION_VALUE(itr));
10902 END_HASH_ITERATION(hash, itr)
10907 /* ------------------------------------------------------------------------- */
10908 /* convert levels */
10909 /* ------------------------------------------------------------------------- */
10911 #define MAX_NUM_CONVERT_LEVELS 1000
10913 void ConvertLevels(void)
10915 static LevelDirTree *convert_leveldir = NULL;
10916 static int convert_level_nr = -1;
10917 static int num_levels_handled = 0;
10918 static int num_levels_converted = 0;
10919 static boolean levels_failed[MAX_NUM_CONVERT_LEVELS];
10922 convert_leveldir = getTreeInfoFromIdentifier(leveldir_first,
10923 global.convert_leveldir);
10925 if (convert_leveldir == NULL)
10926 Error(ERR_EXIT, "no such level identifier: '%s'",
10927 global.convert_leveldir);
10929 leveldir_current = convert_leveldir;
10931 if (global.convert_level_nr != -1)
10933 convert_leveldir->first_level = global.convert_level_nr;
10934 convert_leveldir->last_level = global.convert_level_nr;
10937 convert_level_nr = convert_leveldir->first_level;
10939 PrintLine("=", 79);
10940 Print("Converting levels\n");
10941 PrintLine("-", 79);
10942 Print("Level series identifier: '%s'\n", convert_leveldir->identifier);
10943 Print("Level series name: '%s'\n", convert_leveldir->name);
10944 Print("Level series author: '%s'\n", convert_leveldir->author);
10945 Print("Number of levels: %d\n", convert_leveldir->levels);
10946 PrintLine("=", 79);
10949 for (i = 0; i < MAX_NUM_CONVERT_LEVELS; i++)
10950 levels_failed[i] = FALSE;
10952 while (convert_level_nr <= convert_leveldir->last_level)
10954 char *level_filename;
10957 level_nr = convert_level_nr++;
10959 Print("Level %03d: ", level_nr);
10961 LoadLevel(level_nr);
10962 if (level.no_level_file || level.no_valid_file)
10964 Print("(no level)\n");
10968 Print("converting level ... ");
10970 level_filename = getDefaultLevelFilename(level_nr);
10971 new_level = !fileExists(level_filename);
10975 SaveLevel(level_nr);
10977 num_levels_converted++;
10979 Print("converted.\n");
10983 if (level_nr >= 0 && level_nr < MAX_NUM_CONVERT_LEVELS)
10984 levels_failed[level_nr] = TRUE;
10986 Print("NOT CONVERTED -- LEVEL ALREADY EXISTS.\n");
10989 num_levels_handled++;
10993 PrintLine("=", 79);
10994 Print("Number of levels handled: %d\n", num_levels_handled);
10995 Print("Number of levels converted: %d (%d%%)\n", num_levels_converted,
10996 (num_levels_handled ?
10997 num_levels_converted * 100 / num_levels_handled : 0));
10998 PrintLine("-", 79);
10999 Print("Summary (for automatic parsing by scripts):\n");
11000 Print("LEVELDIR '%s', CONVERTED %d/%d (%d%%)",
11001 convert_leveldir->identifier, num_levels_converted,
11002 num_levels_handled,
11003 (num_levels_handled ?
11004 num_levels_converted * 100 / num_levels_handled : 0));
11006 if (num_levels_handled != num_levels_converted)
11008 Print(", FAILED:");
11009 for (i = 0; i < MAX_NUM_CONVERT_LEVELS; i++)
11010 if (levels_failed[i])
11015 PrintLine("=", 79);
11017 CloseAllAndExit(0);
11021 /* ------------------------------------------------------------------------- */
11022 /* create and save images for use in level sketches (raw BMP format) */
11023 /* ------------------------------------------------------------------------- */
11025 void CreateLevelSketchImages(void)
11027 #if defined(TARGET_SDL)
11032 InitElementPropertiesGfxElement();
11034 bitmap1 = CreateBitmap(TILEX, TILEY, DEFAULT_DEPTH);
11035 bitmap2 = CreateBitmap(MINI_TILEX, MINI_TILEY, DEFAULT_DEPTH);
11037 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
11039 Bitmap *src_bitmap;
11041 int element = getMappedElement(i);
11042 int graphic = el2edimg(element);
11043 char basename1[16];
11044 char basename2[16];
11048 sprintf(basename1, "%03d.bmp", i);
11049 sprintf(basename2, "%03ds.bmp", i);
11051 filename1 = getPath2(global.create_images_dir, basename1);
11052 filename2 = getPath2(global.create_images_dir, basename2);
11054 getFixedGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
11055 BlitBitmap(src_bitmap, bitmap1, src_x, src_y, TILEX, TILEY,
11058 if (SDL_SaveBMP(bitmap1->surface, filename1) != 0)
11059 Error(ERR_EXIT, "cannot save level sketch image file '%s'", filename1);
11061 getMiniGraphicSource(graphic, &src_bitmap, &src_x, &src_y);
11062 BlitBitmap(src_bitmap, bitmap2, src_x, src_y, MINI_TILEX, MINI_TILEY, 0, 0);
11064 if (SDL_SaveBMP(bitmap2->surface, filename2) != 0)
11065 Error(ERR_EXIT, "cannot save level sketch image file '%s'", filename2);
11071 printf("%03d `%03d%c", i, i, (i % 10 < 9 ? ' ' : '\n'));
11074 FreeBitmap(bitmap1);
11075 FreeBitmap(bitmap2);
11080 Error(ERR_INFO, "%d normal and small images created", NUM_FILE_ELEMENTS);
11082 CloseAllAndExit(0);
11087 /* ------------------------------------------------------------------------- */
11088 /* create and save images for custom and group elements (raw BMP format) */
11089 /* ------------------------------------------------------------------------- */
11091 void CreateCustomElementImages(char *directory)
11093 #if defined(TARGET_SDL)
11094 char *src_basename = "RocksCE-template.ilbm";
11095 char *dst_basename = "RocksCE.bmp";
11096 char *src_filename = getPath2(directory, src_basename);
11097 char *dst_filename = getPath2(directory, dst_basename);
11098 Bitmap *src_bitmap;
11100 int yoffset_ce = 0;
11101 int yoffset_ge = (TILEY * NUM_CUSTOM_ELEMENTS / 16);
11104 SDLInitVideoDisplay();
11106 ReCreateBitmap(&backbuffer, video.width, video.height);
11108 src_bitmap = LoadImage(src_filename);
11110 bitmap = CreateBitmap(TILEX * 16 * 2,
11111 TILEY * (NUM_CUSTOM_ELEMENTS + NUM_GROUP_ELEMENTS) / 16,
11114 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
11121 BlitBitmap(src_bitmap, bitmap, 0, 0, TILEX, TILEY,
11122 TILEX * x, TILEY * y + yoffset_ce);
11124 BlitBitmap(src_bitmap, bitmap, 0, TILEY,
11126 TILEX * x + TILEX * 16,
11127 TILEY * y + yoffset_ce);
11129 for (j = 2; j >= 0; j--)
11133 BlitBitmap(src_bitmap, bitmap,
11134 TILEX + c * 7, 0, 6, 10,
11135 TILEX * x + 6 + j * 7,
11136 TILEY * y + 11 + yoffset_ce);
11138 BlitBitmap(src_bitmap, bitmap,
11139 TILEX + c * 8, TILEY, 6, 10,
11140 TILEX * 16 + TILEX * x + 6 + j * 8,
11141 TILEY * y + 10 + yoffset_ce);
11147 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
11154 BlitBitmap(src_bitmap, bitmap, 0, 0, TILEX, TILEY,
11155 TILEX * x, TILEY * y + yoffset_ge);
11157 BlitBitmap(src_bitmap, bitmap, 0, TILEY,
11159 TILEX * x + TILEX * 16,
11160 TILEY * y + yoffset_ge);
11162 for (j = 1; j >= 0; j--)
11166 BlitBitmap(src_bitmap, bitmap, TILEX + c * 10, 11, 10, 10,
11167 TILEX * x + 6 + j * 10,
11168 TILEY * y + 11 + yoffset_ge);
11170 BlitBitmap(src_bitmap, bitmap,
11171 TILEX + c * 8, TILEY + 12, 6, 10,
11172 TILEX * 16 + TILEX * x + 10 + j * 8,
11173 TILEY * y + 10 + yoffset_ge);
11179 if (SDL_SaveBMP(bitmap->surface, dst_filename) != 0)
11180 Error(ERR_EXIT, "cannot save CE graphics file '%s'", dst_filename);
11182 FreeBitmap(bitmap);
11184 CloseAllAndExit(0);