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
674 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
679 TYPE_INTEGER, CONF_VALUE_8_BIT(2),
684 TYPE_INTEGER, CONF_VALUE_8_BIT(3),
689 TYPE_INTEGER, CONF_VALUE_8_BIT(4),
694 EL_TIMEGATE_SWITCH, -1,
695 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
696 &li.time_timegate, 10
700 EL_LIGHT_SWITCH_ACTIVE, -1,
701 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
706 EL_SHIELD_NORMAL, -1,
707 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
708 &li.shield_normal_time, 10
711 EL_SHIELD_NORMAL, -1,
712 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
713 &li.score[SC_SHIELD], 10
717 EL_SHIELD_DEADLY, -1,
718 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
719 &li.shield_deadly_time, 10
722 EL_SHIELD_DEADLY, -1,
723 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
724 &li.score[SC_SHIELD], 10
729 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
734 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
735 &li.extra_time_score, 10
739 EL_TIME_ORB_FULL, -1,
740 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
741 &li.time_orb_time, 10
744 EL_TIME_ORB_FULL, -1,
745 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
746 &li.use_time_orb_bug, FALSE
751 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
752 &li.use_spring_bug, FALSE
757 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
758 &li.android_move_time, 10
762 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
763 &li.android_clone_time, 10
767 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(1),
768 &li.android_clone_element[0], EL_EMPTY, NULL,
769 &li.num_android_clone_elements, 1, MAX_ANDROID_ELEMENTS
774 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
779 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
784 EL_EMC_MAGNIFIER, -1,
785 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
786 &li.magnify_score, 10
789 EL_EMC_MAGNIFIER, -1,
790 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
795 EL_EMC_MAGIC_BALL, -1,
796 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
800 EL_EMC_MAGIC_BALL, -1,
801 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
802 &li.ball_random, FALSE
805 EL_EMC_MAGIC_BALL, -1,
806 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
807 &li.ball_state_initial, FALSE
810 EL_EMC_MAGIC_BALL, -1,
811 TYPE_CONTENT_LIST, CONF_VALUE_BYTES(1),
812 &li.ball_content, EL_EMPTY, NULL,
813 &li.num_ball_contents, 4, MAX_ELEMENT_CONTENTS
818 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
819 &li.mm_laser_red, FALSE
823 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
824 &li.mm_laser_green, FALSE
828 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
829 &li.mm_laser_blue, TRUE
834 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
835 &li.df_laser_red, TRUE
839 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
840 &li.df_laser_green, TRUE
844 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
845 &li.df_laser_blue, FALSE
849 EL_MM_FUSE_ACTIVE, -1,
850 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
855 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
860 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
864 EL_MM_STEEL_BLOCK, -1,
865 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
866 &li.mm_time_block, 75
870 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
871 &li.score[SC_ELEM_BONUS], 10
874 /* ---------- unused values ----------------------------------------------- */
877 EL_UNKNOWN, SAVE_CONF_NEVER,
878 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
879 &li.score[SC_UNKNOWN_15], 10
889 static struct LevelFileConfigInfo chunk_config_NOTE[] =
893 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
894 &xx_envelope.xsize, MAX_ENVELOPE_XSIZE,
898 TYPE_INTEGER, CONF_VALUE_8_BIT(2),
899 &xx_envelope.ysize, MAX_ENVELOPE_YSIZE,
904 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
905 &xx_envelope.autowrap, FALSE
909 TYPE_BOOLEAN, CONF_VALUE_8_BIT(4),
910 &xx_envelope.centered, FALSE
915 TYPE_STRING, CONF_VALUE_BYTES(1),
916 &xx_envelope.text, -1, NULL,
917 &xx_string_length_unused, -1, MAX_ENVELOPE_TEXT_LEN,
918 &xx_default_string_empty[0]
928 static struct LevelFileConfigInfo chunk_config_CUSX_base[] =
932 TYPE_STRING, CONF_VALUE_BYTES(1),
933 &xx_ei.description[0], -1,
934 &yy_ei.description[0],
935 &xx_string_length_unused, -1, MAX_ELEMENT_NAME_LEN,
936 &xx_default_description[0]
941 TYPE_BITFIELD, CONF_VALUE_32_BIT(1),
942 &xx_ei.properties[EP_BITFIELD_BASE_NR], EP_BITMASK_BASE_DEFAULT,
943 &yy_ei.properties[EP_BITFIELD_BASE_NR]
945 #if ENABLE_RESERVED_CODE
946 /* (reserved for later use) */
949 TYPE_BITFIELD, CONF_VALUE_32_BIT(2),
950 &xx_ei.properties[EP_BITFIELD_BASE_NR + 1], EP_BITMASK_DEFAULT,
951 &yy_ei.properties[EP_BITFIELD_BASE_NR + 1]
957 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
958 &xx_ei.use_gfx_element, FALSE,
959 &yy_ei.use_gfx_element
963 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
964 &xx_ei.gfx_element_initial, EL_EMPTY_SPACE,
965 &yy_ei.gfx_element_initial
970 TYPE_BITFIELD, CONF_VALUE_8_BIT(2),
971 &xx_ei.access_direction, MV_ALL_DIRECTIONS,
972 &yy_ei.access_direction
977 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
978 &xx_ei.collect_score_initial, 10,
979 &yy_ei.collect_score_initial
983 TYPE_INTEGER, CONF_VALUE_16_BIT(3),
984 &xx_ei.collect_count_initial, 1,
985 &yy_ei.collect_count_initial
990 TYPE_INTEGER, CONF_VALUE_16_BIT(4),
991 &xx_ei.ce_value_fixed_initial, 0,
992 &yy_ei.ce_value_fixed_initial
996 TYPE_INTEGER, CONF_VALUE_16_BIT(5),
997 &xx_ei.ce_value_random_initial, 0,
998 &yy_ei.ce_value_random_initial
1002 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
1003 &xx_ei.use_last_ce_value, FALSE,
1004 &yy_ei.use_last_ce_value
1009 TYPE_INTEGER, CONF_VALUE_16_BIT(6),
1010 &xx_ei.push_delay_fixed, 8,
1011 &yy_ei.push_delay_fixed
1015 TYPE_INTEGER, CONF_VALUE_16_BIT(7),
1016 &xx_ei.push_delay_random, 8,
1017 &yy_ei.push_delay_random
1021 TYPE_INTEGER, CONF_VALUE_16_BIT(8),
1022 &xx_ei.drop_delay_fixed, 0,
1023 &yy_ei.drop_delay_fixed
1027 TYPE_INTEGER, CONF_VALUE_16_BIT(9),
1028 &xx_ei.drop_delay_random, 0,
1029 &yy_ei.drop_delay_random
1033 TYPE_INTEGER, CONF_VALUE_16_BIT(10),
1034 &xx_ei.move_delay_fixed, 0,
1035 &yy_ei.move_delay_fixed
1039 TYPE_INTEGER, CONF_VALUE_16_BIT(11),
1040 &xx_ei.move_delay_random, 0,
1041 &yy_ei.move_delay_random
1046 TYPE_BITFIELD, CONF_VALUE_32_BIT(3),
1047 &xx_ei.move_pattern, MV_ALL_DIRECTIONS,
1052 TYPE_BITFIELD, CONF_VALUE_8_BIT(4),
1053 &xx_ei.move_direction_initial, MV_START_AUTOMATIC,
1054 &yy_ei.move_direction_initial
1058 TYPE_INTEGER, CONF_VALUE_8_BIT(5),
1059 &xx_ei.move_stepsize, TILEX / 8,
1060 &yy_ei.move_stepsize
1065 TYPE_ELEMENT, CONF_VALUE_16_BIT(12),
1066 &xx_ei.move_enter_element, EL_EMPTY_SPACE,
1067 &yy_ei.move_enter_element
1071 TYPE_ELEMENT, CONF_VALUE_16_BIT(13),
1072 &xx_ei.move_leave_element, EL_EMPTY_SPACE,
1073 &yy_ei.move_leave_element
1077 TYPE_INTEGER, CONF_VALUE_8_BIT(6),
1078 &xx_ei.move_leave_type, LEAVE_TYPE_UNLIMITED,
1079 &yy_ei.move_leave_type
1084 TYPE_INTEGER, CONF_VALUE_8_BIT(7),
1085 &xx_ei.slippery_type, SLIPPERY_ANY_RANDOM,
1086 &yy_ei.slippery_type
1091 TYPE_INTEGER, CONF_VALUE_8_BIT(8),
1092 &xx_ei.explosion_type, EXPLODES_3X3,
1093 &yy_ei.explosion_type
1097 TYPE_INTEGER, CONF_VALUE_16_BIT(14),
1098 &xx_ei.explosion_delay, 16,
1099 &yy_ei.explosion_delay
1103 TYPE_INTEGER, CONF_VALUE_16_BIT(15),
1104 &xx_ei.ignition_delay, 8,
1105 &yy_ei.ignition_delay
1110 TYPE_CONTENT_LIST, CONF_VALUE_BYTES(2),
1111 &xx_ei.content, EL_EMPTY_SPACE,
1113 &xx_num_contents, 1, 1
1116 /* ---------- "num_change_pages" must be the last entry ------------------- */
1119 -1, SAVE_CONF_ALWAYS,
1120 TYPE_INTEGER, CONF_VALUE_8_BIT(9),
1121 &xx_ei.num_change_pages, 1,
1122 &yy_ei.num_change_pages
1133 static struct LevelFileConfigInfo chunk_config_CUSX_change[] =
1135 /* ---------- "current_change_page" must be the first entry --------------- */
1138 -1, SAVE_CONF_ALWAYS,
1139 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
1140 &xx_current_change_page, -1
1143 /* ---------- (the remaining entries can be in any order) ----------------- */
1147 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
1148 &xx_change.can_change, FALSE
1153 TYPE_BITFIELD, CONF_VALUE_32_BIT(1),
1154 &xx_event_bits[0], 0
1158 TYPE_BITFIELD, CONF_VALUE_32_BIT(2),
1159 &xx_event_bits[1], 0
1164 TYPE_BITFIELD, CONF_VALUE_8_BIT(3),
1165 &xx_change.trigger_player, CH_PLAYER_ANY
1169 TYPE_BITFIELD, CONF_VALUE_8_BIT(4),
1170 &xx_change.trigger_side, CH_SIDE_ANY
1174 TYPE_BITFIELD, CONF_VALUE_32_BIT(3),
1175 &xx_change.trigger_page, CH_PAGE_ANY
1180 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
1181 &xx_change.target_element, EL_EMPTY_SPACE
1186 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
1187 &xx_change.delay_fixed, 0
1191 TYPE_INTEGER, CONF_VALUE_16_BIT(3),
1192 &xx_change.delay_random, 0
1196 TYPE_INTEGER, CONF_VALUE_16_BIT(4),
1197 &xx_change.delay_frames, FRAMES_PER_SECOND
1202 TYPE_ELEMENT, CONF_VALUE_16_BIT(5),
1203 &xx_change.initial_trigger_element, EL_EMPTY_SPACE
1208 TYPE_BOOLEAN, CONF_VALUE_8_BIT(6),
1209 &xx_change.explode, FALSE
1213 TYPE_BOOLEAN, CONF_VALUE_8_BIT(7),
1214 &xx_change.use_target_content, FALSE
1218 TYPE_BOOLEAN, CONF_VALUE_8_BIT(8),
1219 &xx_change.only_if_complete, FALSE
1223 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
1224 &xx_change.use_random_replace, FALSE
1228 TYPE_INTEGER, CONF_VALUE_8_BIT(10),
1229 &xx_change.random_percentage, 100
1233 TYPE_INTEGER, CONF_VALUE_8_BIT(11),
1234 &xx_change.replace_when, CP_WHEN_EMPTY
1239 TYPE_BOOLEAN, CONF_VALUE_8_BIT(12),
1240 &xx_change.has_action, FALSE
1244 TYPE_INTEGER, CONF_VALUE_8_BIT(13),
1245 &xx_change.action_type, CA_NO_ACTION
1249 TYPE_INTEGER, CONF_VALUE_8_BIT(14),
1250 &xx_change.action_mode, CA_MODE_UNDEFINED
1254 TYPE_INTEGER, CONF_VALUE_16_BIT(6),
1255 &xx_change.action_arg, CA_ARG_UNDEFINED
1260 TYPE_ELEMENT, CONF_VALUE_16_BIT(7),
1261 &xx_change.action_element, EL_EMPTY_SPACE
1266 TYPE_CONTENT_LIST, CONF_VALUE_BYTES(1),
1267 &xx_change.target_content, EL_EMPTY_SPACE, NULL,
1268 &xx_num_contents, 1, 1
1278 static struct LevelFileConfigInfo chunk_config_GRPX[] =
1282 TYPE_STRING, CONF_VALUE_BYTES(1),
1283 &xx_ei.description[0], -1, NULL,
1284 &xx_string_length_unused, -1, MAX_ELEMENT_NAME_LEN,
1285 &xx_default_description[0]
1290 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1291 &xx_ei.use_gfx_element, FALSE
1295 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
1296 &xx_ei.gfx_element_initial, EL_EMPTY_SPACE
1301 TYPE_INTEGER, CONF_VALUE_8_BIT(2),
1302 &xx_group.choice_mode, ANIM_RANDOM
1307 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(2),
1308 &xx_group.element[0], EL_EMPTY_SPACE, NULL,
1309 &xx_group.num_elements, 1, MAX_ELEMENTS_IN_GROUP
1319 static struct LevelFileConfigInfo chunk_config_CONF[] = /* (OBSOLETE) */
1323 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
1324 &li.block_snap_field, TRUE
1328 TYPE_BOOLEAN, CONF_VALUE_8_BIT(13),
1329 &li.continuous_snapping, TRUE
1333 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
1334 &li.initial_player_stepsize[0], STEPSIZE_NORMAL
1338 TYPE_BOOLEAN, CONF_VALUE_8_BIT(10),
1339 &li.use_start_element[0], FALSE
1343 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
1344 &li.start_element[0], EL_PLAYER_1
1348 TYPE_BOOLEAN, CONF_VALUE_8_BIT(11),
1349 &li.use_artwork_element[0], FALSE
1353 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
1354 &li.artwork_element[0], EL_PLAYER_1
1358 TYPE_BOOLEAN, CONF_VALUE_8_BIT(12),
1359 &li.use_explosion_element[0], FALSE
1363 TYPE_ELEMENT, CONF_VALUE_16_BIT(3),
1364 &li.explosion_element[0], EL_PLAYER_1
1379 filetype_id_list[] =
1381 { LEVEL_FILE_TYPE_RND, "RND" },
1382 { LEVEL_FILE_TYPE_BD, "BD" },
1383 { LEVEL_FILE_TYPE_EM, "EM" },
1384 { LEVEL_FILE_TYPE_SP, "SP" },
1385 { LEVEL_FILE_TYPE_DX, "DX" },
1386 { LEVEL_FILE_TYPE_SB, "SB" },
1387 { LEVEL_FILE_TYPE_DC, "DC" },
1388 { LEVEL_FILE_TYPE_MM, "MM" },
1389 { LEVEL_FILE_TYPE_MM, "DF" },
1394 /* ========================================================================= */
1395 /* level file functions */
1396 /* ========================================================================= */
1398 static boolean check_special_flags(char *flag)
1400 if (strEqual(options.special_flags, flag) ||
1401 strEqual(leveldir_current->special_flags, flag))
1407 static struct DateInfo getCurrentDate()
1409 time_t epoch_seconds = time(NULL);
1410 struct tm *now = localtime(&epoch_seconds);
1411 struct DateInfo date;
1413 date.year = now->tm_year + 1900;
1414 date.month = now->tm_mon + 1;
1415 date.day = now->tm_mday;
1417 date.src = DATE_SRC_CLOCK;
1422 static void resetEventFlags(struct ElementChangeInfo *change)
1426 for (i = 0; i < NUM_CHANGE_EVENTS; i++)
1427 change->has_event[i] = FALSE;
1430 static void resetEventBits()
1434 for (i = 0; i < NUM_CE_BITFIELDS; i++)
1435 xx_event_bits[i] = 0;
1438 static void setEventFlagsFromEventBits(struct ElementChangeInfo *change)
1442 /* important: only change event flag if corresponding event bit is set
1443 (this is because all xx_event_bits[] values are loaded separately,
1444 and all xx_event_bits[] values are set back to zero before loading
1445 another value xx_event_bits[x] (each value representing 32 flags)) */
1447 for (i = 0; i < NUM_CHANGE_EVENTS; i++)
1448 if (xx_event_bits[CH_EVENT_BITFIELD_NR(i)] & CH_EVENT_BIT(i))
1449 change->has_event[i] = TRUE;
1452 static void setEventBitsFromEventFlags(struct ElementChangeInfo *change)
1456 /* in contrast to the above function setEventFlagsFromEventBits(), it
1457 would also be possible to set all bits in xx_event_bits[] to 0 or 1
1458 depending on the corresponding change->has_event[i] values here, as
1459 all xx_event_bits[] values are reset in resetEventBits() before */
1461 for (i = 0; i < NUM_CHANGE_EVENTS; i++)
1462 if (change->has_event[i])
1463 xx_event_bits[CH_EVENT_BITFIELD_NR(i)] |= CH_EVENT_BIT(i);
1466 static char *getDefaultElementDescription(struct ElementInfo *ei)
1468 static char description[MAX_ELEMENT_NAME_LEN + 1];
1469 char *default_description = (ei->custom_description != NULL ?
1470 ei->custom_description :
1471 ei->editor_description);
1474 /* always start with reliable default values */
1475 for (i = 0; i < MAX_ELEMENT_NAME_LEN + 1; i++)
1476 description[i] = '\0';
1478 /* truncate element description to MAX_ELEMENT_NAME_LEN bytes */
1479 strncpy(description, default_description, MAX_ELEMENT_NAME_LEN);
1481 return &description[0];
1484 static void setElementDescriptionToDefault(struct ElementInfo *ei)
1486 char *default_description = getDefaultElementDescription(ei);
1489 for (i = 0; i < MAX_ELEMENT_NAME_LEN + 1; i++)
1490 ei->description[i] = default_description[i];
1493 static void setConfigToDefaultsFromConfigList(struct LevelFileConfigInfo *conf)
1497 for (i = 0; conf[i].data_type != -1; i++)
1499 int default_value = conf[i].default_value;
1500 int data_type = conf[i].data_type;
1501 int conf_type = conf[i].conf_type;
1502 int byte_mask = conf_type & CONF_MASK_BYTES;
1504 if (byte_mask == CONF_MASK_MULTI_BYTES)
1506 int default_num_entities = conf[i].default_num_entities;
1507 int max_num_entities = conf[i].max_num_entities;
1509 *(int *)(conf[i].num_entities) = default_num_entities;
1511 if (data_type == TYPE_STRING)
1513 char *default_string = conf[i].default_string;
1514 char *string = (char *)(conf[i].value);
1516 strncpy(string, default_string, max_num_entities);
1518 else if (data_type == TYPE_ELEMENT_LIST)
1520 int *element_array = (int *)(conf[i].value);
1523 for (j = 0; j < max_num_entities; j++)
1524 element_array[j] = default_value;
1526 else if (data_type == TYPE_CONTENT_LIST)
1528 struct Content *content = (struct Content *)(conf[i].value);
1531 for (c = 0; c < max_num_entities; c++)
1532 for (y = 0; y < 3; y++)
1533 for (x = 0; x < 3; x++)
1534 content[c].e[x][y] = default_value;
1537 else /* constant size configuration data (1, 2 or 4 bytes) */
1539 if (data_type == TYPE_BOOLEAN)
1540 *(boolean *)(conf[i].value) = default_value;
1542 *(int *) (conf[i].value) = default_value;
1547 static void copyConfigFromConfigList(struct LevelFileConfigInfo *conf)
1551 for (i = 0; conf[i].data_type != -1; i++)
1553 int data_type = conf[i].data_type;
1554 int conf_type = conf[i].conf_type;
1555 int byte_mask = conf_type & CONF_MASK_BYTES;
1557 if (byte_mask == CONF_MASK_MULTI_BYTES)
1559 int max_num_entities = conf[i].max_num_entities;
1561 if (data_type == TYPE_STRING)
1563 char *string = (char *)(conf[i].value);
1564 char *string_copy = (char *)(conf[i].value_copy);
1566 strncpy(string_copy, string, max_num_entities);
1568 else if (data_type == TYPE_ELEMENT_LIST)
1570 int *element_array = (int *)(conf[i].value);
1571 int *element_array_copy = (int *)(conf[i].value_copy);
1574 for (j = 0; j < max_num_entities; j++)
1575 element_array_copy[j] = element_array[j];
1577 else if (data_type == TYPE_CONTENT_LIST)
1579 struct Content *content = (struct Content *)(conf[i].value);
1580 struct Content *content_copy = (struct Content *)(conf[i].value_copy);
1583 for (c = 0; c < max_num_entities; c++)
1584 for (y = 0; y < 3; y++)
1585 for (x = 0; x < 3; x++)
1586 content_copy[c].e[x][y] = content[c].e[x][y];
1589 else /* constant size configuration data (1, 2 or 4 bytes) */
1591 if (data_type == TYPE_BOOLEAN)
1592 *(boolean *)(conf[i].value_copy) = *(boolean *)(conf[i].value);
1594 *(int *) (conf[i].value_copy) = *(int *) (conf[i].value);
1599 void copyElementInfo(struct ElementInfo *ei_from, struct ElementInfo *ei_to)
1603 xx_ei = *ei_from; /* copy element data into temporary buffer */
1604 yy_ei = *ei_to; /* copy element data into temporary buffer */
1606 copyConfigFromConfigList(chunk_config_CUSX_base);
1611 /* ---------- reinitialize and copy change pages ---------- */
1613 ei_to->num_change_pages = ei_from->num_change_pages;
1614 ei_to->current_change_page = ei_from->current_change_page;
1616 setElementChangePages(ei_to, ei_to->num_change_pages);
1618 for (i = 0; i < ei_to->num_change_pages; i++)
1619 ei_to->change_page[i] = ei_from->change_page[i];
1621 /* ---------- copy group element info ---------- */
1622 if (ei_from->group != NULL && ei_to->group != NULL) /* group or internal */
1623 *ei_to->group = *ei_from->group;
1625 /* mark this custom element as modified */
1626 ei_to->modified_settings = TRUE;
1629 void setElementChangePages(struct ElementInfo *ei, int change_pages)
1631 int change_page_size = sizeof(struct ElementChangeInfo);
1633 ei->num_change_pages = MAX(1, change_pages);
1636 checked_realloc(ei->change_page, ei->num_change_pages * change_page_size);
1638 if (ei->current_change_page >= ei->num_change_pages)
1639 ei->current_change_page = ei->num_change_pages - 1;
1641 ei->change = &ei->change_page[ei->current_change_page];
1644 void setElementChangeInfoToDefaults(struct ElementChangeInfo *change)
1646 xx_change = *change; /* copy change data into temporary buffer */
1648 setConfigToDefaultsFromConfigList(chunk_config_CUSX_change);
1650 *change = xx_change;
1652 resetEventFlags(change);
1654 change->direct_action = 0;
1655 change->other_action = 0;
1657 change->pre_change_function = NULL;
1658 change->change_function = NULL;
1659 change->post_change_function = NULL;
1662 static void setLevelInfoToDefaults_Level(struct LevelInfo *level)
1666 li = *level; /* copy level data into temporary buffer */
1667 setConfigToDefaultsFromConfigList(chunk_config_INFO);
1668 *level = li; /* copy temporary buffer back to level data */
1670 setLevelInfoToDefaults_EM();
1671 setLevelInfoToDefaults_SP();
1672 setLevelInfoToDefaults_MM();
1674 level->native_em_level = &native_em_level;
1675 level->native_sp_level = &native_sp_level;
1676 level->native_mm_level = &native_mm_level;
1678 level->file_version = FILE_VERSION_ACTUAL;
1679 level->game_version = GAME_VERSION_ACTUAL;
1681 level->creation_date = getCurrentDate();
1683 level->encoding_16bit_field = TRUE;
1684 level->encoding_16bit_yamyam = TRUE;
1685 level->encoding_16bit_amoeba = TRUE;
1687 /* clear level name and level author string buffers */
1688 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
1689 level->name[i] = '\0';
1690 for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
1691 level->author[i] = '\0';
1693 /* set level name and level author to default values */
1694 strcpy(level->name, NAMELESS_LEVEL_NAME);
1695 strcpy(level->author, ANONYMOUS_NAME);
1697 /* set level playfield to playable default level with player and exit */
1698 for (x = 0; x < MAX_LEV_FIELDX; x++)
1699 for (y = 0; y < MAX_LEV_FIELDY; y++)
1700 level->field[x][y] = EL_SAND;
1702 level->field[0][0] = EL_PLAYER_1;
1703 level->field[STD_LEV_FIELDX - 1][STD_LEV_FIELDY - 1] = EL_EXIT_CLOSED;
1705 BorderElement = EL_STEELWALL;
1707 /* detect custom elements when loading them */
1708 level->file_has_custom_elements = FALSE;
1710 /* set all bug compatibility flags to "false" => do not emulate this bug */
1711 level->use_action_after_change_bug = FALSE;
1713 if (leveldir_current)
1715 /* try to determine better author name than 'anonymous' */
1716 if (!strEqual(leveldir_current->author, ANONYMOUS_NAME))
1718 strncpy(level->author, leveldir_current->author, MAX_LEVEL_AUTHOR_LEN);
1719 level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
1723 switch (LEVELCLASS(leveldir_current))
1725 case LEVELCLASS_TUTORIAL:
1726 strcpy(level->author, PROGRAM_AUTHOR_STRING);
1729 case LEVELCLASS_CONTRIB:
1730 strncpy(level->author, leveldir_current->name, MAX_LEVEL_AUTHOR_LEN);
1731 level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
1734 case LEVELCLASS_PRIVATE:
1735 strncpy(level->author, getRealName(), MAX_LEVEL_AUTHOR_LEN);
1736 level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
1740 /* keep default value */
1747 static void setLevelInfoToDefaults_Elements(struct LevelInfo *level)
1749 static boolean clipboard_elements_initialized = FALSE;
1752 InitElementPropertiesStatic();
1754 li = *level; /* copy level data into temporary buffer */
1755 setConfigToDefaultsFromConfigList(chunk_config_ELEM);
1756 *level = li; /* copy temporary buffer back to level data */
1758 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1761 struct ElementInfo *ei = &element_info[element];
1763 /* never initialize clipboard elements after the very first time */
1764 /* (to be able to use clipboard elements between several levels) */
1765 if (IS_CLIPBOARD_ELEMENT(element) && clipboard_elements_initialized)
1768 if (IS_ENVELOPE(element))
1770 int envelope_nr = element - EL_ENVELOPE_1;
1772 setConfigToDefaultsFromConfigList(chunk_config_NOTE);
1774 level->envelope[envelope_nr] = xx_envelope;
1777 if (IS_CUSTOM_ELEMENT(element) ||
1778 IS_GROUP_ELEMENT(element) ||
1779 IS_INTERNAL_ELEMENT(element))
1781 xx_ei = *ei; /* copy element data into temporary buffer */
1783 setConfigToDefaultsFromConfigList(chunk_config_CUSX_base);
1788 setElementChangePages(ei, 1);
1789 setElementChangeInfoToDefaults(ei->change);
1791 if (IS_CUSTOM_ELEMENT(element) ||
1792 IS_GROUP_ELEMENT(element) ||
1793 IS_INTERNAL_ELEMENT(element))
1795 setElementDescriptionToDefault(ei);
1797 ei->modified_settings = FALSE;
1800 if (IS_CUSTOM_ELEMENT(element) ||
1801 IS_INTERNAL_ELEMENT(element))
1803 /* internal values used in level editor */
1805 ei->access_type = 0;
1806 ei->access_layer = 0;
1807 ei->access_protected = 0;
1808 ei->walk_to_action = 0;
1809 ei->smash_targets = 0;
1812 ei->can_explode_by_fire = FALSE;
1813 ei->can_explode_smashed = FALSE;
1814 ei->can_explode_impact = FALSE;
1816 ei->current_change_page = 0;
1819 if (IS_GROUP_ELEMENT(element) ||
1820 IS_INTERNAL_ELEMENT(element))
1822 struct ElementGroupInfo *group;
1824 /* initialize memory for list of elements in group */
1825 if (ei->group == NULL)
1826 ei->group = checked_malloc(sizeof(struct ElementGroupInfo));
1830 xx_group = *group; /* copy group data into temporary buffer */
1832 setConfigToDefaultsFromConfigList(chunk_config_GRPX);
1838 clipboard_elements_initialized = TRUE;
1841 static void setLevelInfoToDefaults(struct LevelInfo *level,
1842 boolean level_info_only,
1843 boolean reset_file_status)
1845 setLevelInfoToDefaults_Level(level);
1847 if (!level_info_only)
1848 setLevelInfoToDefaults_Elements(level);
1850 if (reset_file_status)
1852 level->no_valid_file = FALSE;
1853 level->no_level_file = FALSE;
1856 level->changed = FALSE;
1859 static void setFileInfoToDefaults(struct LevelFileInfo *level_file_info)
1861 level_file_info->nr = 0;
1862 level_file_info->type = LEVEL_FILE_TYPE_UNKNOWN;
1863 level_file_info->packed = FALSE;
1865 setString(&level_file_info->basename, NULL);
1866 setString(&level_file_info->filename, NULL);
1869 int getMappedElement_SB(int, boolean);
1871 static void ActivateLevelTemplate()
1875 if (check_special_flags("load_xsb_to_ces"))
1877 /* fill smaller playfields with padding "beyond border wall" elements */
1878 if (level.fieldx < level_template.fieldx ||
1879 level.fieldy < level_template.fieldy)
1881 short field[level.fieldx][level.fieldy];
1882 int new_fieldx = MAX(level.fieldx, level_template.fieldx);
1883 int new_fieldy = MAX(level.fieldy, level_template.fieldy);
1884 int pos_fieldx = (new_fieldx - level.fieldx) / 2;
1885 int pos_fieldy = (new_fieldy - level.fieldy) / 2;
1887 /* copy old playfield (which is smaller than the visible area) */
1888 for (y = 0; y < level.fieldy; y++) for (x = 0; x < level.fieldx; x++)
1889 field[x][y] = level.field[x][y];
1891 /* fill new, larger playfield with "beyond border wall" elements */
1892 for (y = 0; y < new_fieldy; y++) for (x = 0; x < new_fieldx; x++)
1893 level.field[x][y] = getMappedElement_SB('_', TRUE);
1895 /* copy the old playfield to the middle of the new playfield */
1896 for (y = 0; y < level.fieldy; y++) for (x = 0; x < level.fieldx; x++)
1897 level.field[pos_fieldx + x][pos_fieldy + y] = field[x][y];
1899 level.fieldx = new_fieldx;
1900 level.fieldy = new_fieldy;
1904 /* Currently there is no special action needed to activate the template
1905 data, because 'element_info' property settings overwrite the original
1906 level data, while all other variables do not change. */
1908 /* Exception: 'from_level_template' elements in the original level playfield
1909 are overwritten with the corresponding elements at the same position in
1910 playfield from the level template. */
1912 for (x = 0; x < level.fieldx; x++)
1913 for (y = 0; y < level.fieldy; y++)
1914 if (level.field[x][y] == EL_FROM_LEVEL_TEMPLATE)
1915 level.field[x][y] = level_template.field[x][y];
1917 if (check_special_flags("load_xsb_to_ces"))
1919 struct LevelInfo level_backup = level;
1921 /* overwrite all individual level settings from template level settings */
1922 level = level_template;
1924 /* restore level file info */
1925 level.file_info = level_backup.file_info;
1927 /* restore playfield size */
1928 level.fieldx = level_backup.fieldx;
1929 level.fieldy = level_backup.fieldy;
1931 /* restore playfield content */
1932 for (x = 0; x < level.fieldx; x++)
1933 for (y = 0; y < level.fieldy; y++)
1934 level.field[x][y] = level_backup.field[x][y];
1936 /* restore name and author from individual level */
1937 strcpy(level.name, level_backup.name);
1938 strcpy(level.author, level_backup.author);
1940 /* restore flag "use_custom_template" */
1941 level.use_custom_template = level_backup.use_custom_template;
1945 static char *getLevelFilenameFromBasename(char *basename)
1947 static char *filename = NULL;
1949 checked_free(filename);
1951 filename = getPath2(getCurrentLevelDir(), basename);
1956 static int getFileTypeFromBasename(char *basename)
1958 /* !!! ALSO SEE COMMENT IN checkForPackageFromBasename() !!! */
1960 static char *filename = NULL;
1961 struct stat file_status;
1963 /* ---------- try to determine file type from filename ---------- */
1965 /* check for typical filename of a Supaplex level package file */
1966 if (strlen(basename) == 10 && strPrefixLower(basename, "levels.d"))
1967 return LEVEL_FILE_TYPE_SP;
1969 /* check for typical filename of a Diamond Caves II level package file */
1970 if (strSuffixLower(basename, ".dc") ||
1971 strSuffixLower(basename, ".dc2"))
1972 return LEVEL_FILE_TYPE_DC;
1974 /* check for typical filename of a Sokoban level package file */
1975 if (strSuffixLower(basename, ".xsb") &&
1976 strchr(basename, '%') == NULL)
1977 return LEVEL_FILE_TYPE_SB;
1979 /* ---------- try to determine file type from filesize ---------- */
1981 checked_free(filename);
1982 filename = getPath2(getCurrentLevelDir(), basename);
1984 if (stat(filename, &file_status) == 0)
1986 /* check for typical filesize of a Supaplex level package file */
1987 if (file_status.st_size == 170496)
1988 return LEVEL_FILE_TYPE_SP;
1991 return LEVEL_FILE_TYPE_UNKNOWN;
1994 static int getFileTypeFromMagicBytes(char *filename, int type)
1998 if ((file = openFile(filename, MODE_READ)))
2000 char chunk_name[CHUNK_ID_LEN + 1];
2002 getFileChunkBE(file, chunk_name, NULL);
2004 if (strEqual(chunk_name, "MMII") ||
2005 strEqual(chunk_name, "MIRR"))
2006 type = LEVEL_FILE_TYPE_MM;
2014 static boolean checkForPackageFromBasename(char *basename)
2016 /* !!! WON'T WORK ANYMORE IF getFileTypeFromBasename() ALSO DETECTS !!!
2017 !!! SINGLE LEVELS (CURRENTLY ONLY DETECTS LEVEL PACKAGES !!! */
2019 return (getFileTypeFromBasename(basename) != LEVEL_FILE_TYPE_UNKNOWN);
2022 static char *getSingleLevelBasenameExt(int nr, char *extension)
2024 static char basename[MAX_FILENAME_LEN];
2027 sprintf(basename, "%s", LEVELTEMPLATE_FILENAME);
2029 sprintf(basename, "%03d.%s", nr, extension);
2034 static char *getSingleLevelBasename(int nr)
2036 return getSingleLevelBasenameExt(nr, LEVELFILE_EXTENSION);
2039 static char *getPackedLevelBasename(int type)
2041 static char basename[MAX_FILENAME_LEN];
2042 char *directory = getCurrentLevelDir();
2044 DirectoryEntry *dir_entry;
2046 strcpy(basename, UNDEFINED_FILENAME); /* default: undefined file */
2048 if ((dir = openDirectory(directory)) == NULL)
2050 Error(ERR_WARN, "cannot read current level directory '%s'", directory);
2055 while ((dir_entry = readDirectory(dir)) != NULL) /* loop all entries */
2057 char *entry_basename = dir_entry->basename;
2058 int entry_type = getFileTypeFromBasename(entry_basename);
2060 if (entry_type != LEVEL_FILE_TYPE_UNKNOWN) /* found valid level package */
2062 if (type == LEVEL_FILE_TYPE_UNKNOWN ||
2065 strcpy(basename, entry_basename);
2072 closeDirectory(dir);
2077 static char *getSingleLevelFilename(int nr)
2079 return getLevelFilenameFromBasename(getSingleLevelBasename(nr));
2082 #if ENABLE_UNUSED_CODE
2083 static char *getPackedLevelFilename(int type)
2085 return getLevelFilenameFromBasename(getPackedLevelBasename(type));
2089 char *getDefaultLevelFilename(int nr)
2091 return getSingleLevelFilename(nr);
2094 #if ENABLE_UNUSED_CODE
2095 static void setLevelFileInfo_SingleLevelFilename(struct LevelFileInfo *lfi,
2099 lfi->packed = FALSE;
2101 setString(&lfi->basename, getSingleLevelBasename(lfi->nr, lfi->type));
2102 setString(&lfi->filename, getLevelFilenameFromBasename(lfi->basename));
2106 static void setLevelFileInfo_FormatLevelFilename(struct LevelFileInfo *lfi,
2107 int type, char *format, ...)
2109 static char basename[MAX_FILENAME_LEN];
2112 va_start(ap, format);
2113 vsprintf(basename, format, ap);
2117 lfi->packed = FALSE;
2119 setString(&lfi->basename, basename);
2120 setString(&lfi->filename, getLevelFilenameFromBasename(lfi->basename));
2123 static void setLevelFileInfo_PackedLevelFilename(struct LevelFileInfo *lfi,
2129 setString(&lfi->basename, getPackedLevelBasename(lfi->type));
2130 setString(&lfi->filename, getLevelFilenameFromBasename(lfi->basename));
2133 static int getFiletypeFromID(char *filetype_id)
2135 char *filetype_id_lower;
2136 int filetype = LEVEL_FILE_TYPE_UNKNOWN;
2139 if (filetype_id == NULL)
2140 return LEVEL_FILE_TYPE_UNKNOWN;
2142 filetype_id_lower = getStringToLower(filetype_id);
2144 for (i = 0; filetype_id_list[i].id != NULL; i++)
2146 char *id_lower = getStringToLower(filetype_id_list[i].id);
2148 if (strEqual(filetype_id_lower, id_lower))
2149 filetype = filetype_id_list[i].filetype;
2153 if (filetype != LEVEL_FILE_TYPE_UNKNOWN)
2157 free(filetype_id_lower);
2162 char *getLocalLevelTemplateFilename()
2164 return getDefaultLevelFilename(-1);
2167 char *getGlobalLevelTemplateFilename()
2169 /* global variable "leveldir_current" must be modified in the loop below */
2170 LevelDirTree *leveldir_current_last = leveldir_current;
2171 char *filename = NULL;
2173 /* check for template level in path from current to topmost tree node */
2175 while (leveldir_current != NULL)
2177 filename = getDefaultLevelFilename(-1);
2179 if (fileExists(filename))
2182 leveldir_current = leveldir_current->node_parent;
2185 /* restore global variable "leveldir_current" modified in above loop */
2186 leveldir_current = leveldir_current_last;
2191 static void determineLevelFileInfo_Filename(struct LevelFileInfo *lfi)
2195 /* special case: level number is negative => check for level template file */
2198 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
2199 getSingleLevelBasename(-1));
2201 /* replace local level template filename with global template filename */
2202 setString(&lfi->filename, getGlobalLevelTemplateFilename());
2204 /* no fallback if template file not existing */
2208 /* special case: check for file name/pattern specified in "levelinfo.conf" */
2209 if (leveldir_current->level_filename != NULL)
2211 int filetype = getFiletypeFromID(leveldir_current->level_filetype);
2213 setLevelFileInfo_FormatLevelFilename(lfi, filetype,
2214 leveldir_current->level_filename, nr);
2216 lfi->packed = checkForPackageFromBasename(leveldir_current->level_filename);
2218 if (fileExists(lfi->filename))
2221 else if (leveldir_current->level_filetype != NULL)
2223 int filetype = getFiletypeFromID(leveldir_current->level_filetype);
2225 /* check for specified native level file with standard file name */
2226 setLevelFileInfo_FormatLevelFilename(lfi, filetype,
2227 "%03d.%s", nr, LEVELFILE_EXTENSION);
2228 if (fileExists(lfi->filename))
2232 /* check for native Rocks'n'Diamonds level file */
2233 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
2234 "%03d.%s", nr, LEVELFILE_EXTENSION);
2235 if (fileExists(lfi->filename))
2238 /* check for Emerald Mine level file (V1) */
2239 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "a%c%c",
2240 'a' + (nr / 10) % 26, '0' + nr % 10);
2241 if (fileExists(lfi->filename))
2243 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "A%c%c",
2244 'A' + (nr / 10) % 26, '0' + nr % 10);
2245 if (fileExists(lfi->filename))
2248 /* check for Emerald Mine level file (V2 to V5) */
2249 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%d", nr);
2250 if (fileExists(lfi->filename))
2253 /* check for Emerald Mine level file (V6 / single mode) */
2254 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02ds", nr);
2255 if (fileExists(lfi->filename))
2257 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02dS", nr);
2258 if (fileExists(lfi->filename))
2261 /* check for Emerald Mine level file (V6 / teamwork mode) */
2262 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02dt", nr);
2263 if (fileExists(lfi->filename))
2265 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02dT", nr);
2266 if (fileExists(lfi->filename))
2269 /* check for various packed level file formats */
2270 setLevelFileInfo_PackedLevelFilename(lfi, LEVEL_FILE_TYPE_UNKNOWN);
2271 if (fileExists(lfi->filename))
2274 /* no known level file found -- use default values (and fail later) */
2275 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
2276 "%03d.%s", nr, LEVELFILE_EXTENSION);
2279 static void determineLevelFileInfo_Filetype(struct LevelFileInfo *lfi)
2281 if (lfi->type == LEVEL_FILE_TYPE_UNKNOWN)
2282 lfi->type = getFileTypeFromBasename(lfi->basename);
2284 if (lfi->type == LEVEL_FILE_TYPE_RND)
2285 lfi->type = getFileTypeFromMagicBytes(lfi->filename, lfi->type);
2288 static void setLevelFileInfo(struct LevelFileInfo *level_file_info, int nr)
2290 /* always start with reliable default values */
2291 setFileInfoToDefaults(level_file_info);
2293 level_file_info->nr = nr; /* set requested level number */
2295 determineLevelFileInfo_Filename(level_file_info);
2296 determineLevelFileInfo_Filetype(level_file_info);
2299 static void copyLevelFileInfo(struct LevelFileInfo *lfi_from,
2300 struct LevelFileInfo *lfi_to)
2302 lfi_to->nr = lfi_from->nr;
2303 lfi_to->type = lfi_from->type;
2304 lfi_to->packed = lfi_from->packed;
2306 setString(&lfi_to->basename, lfi_from->basename);
2307 setString(&lfi_to->filename, lfi_from->filename);
2310 /* ------------------------------------------------------------------------- */
2311 /* functions for loading R'n'D level */
2312 /* ------------------------------------------------------------------------- */
2314 int getMappedElement(int element)
2316 /* remap some (historic, now obsolete) elements */
2320 case EL_PLAYER_OBSOLETE:
2321 element = EL_PLAYER_1;
2324 case EL_KEY_OBSOLETE:
2328 case EL_EM_KEY_1_FILE_OBSOLETE:
2329 element = EL_EM_KEY_1;
2332 case EL_EM_KEY_2_FILE_OBSOLETE:
2333 element = EL_EM_KEY_2;
2336 case EL_EM_KEY_3_FILE_OBSOLETE:
2337 element = EL_EM_KEY_3;
2340 case EL_EM_KEY_4_FILE_OBSOLETE:
2341 element = EL_EM_KEY_4;
2344 case EL_ENVELOPE_OBSOLETE:
2345 element = EL_ENVELOPE_1;
2353 if (element >= NUM_FILE_ELEMENTS)
2355 Error(ERR_WARN, "invalid level element %d", element);
2357 element = EL_UNKNOWN;
2365 int getMappedElementByVersion(int element, int game_version)
2367 /* remap some elements due to certain game version */
2369 if (game_version <= VERSION_IDENT(2,2,0,0))
2371 /* map game font elements */
2372 element = (element == EL_CHAR('[') ? EL_CHAR_AUMLAUT :
2373 element == EL_CHAR('\\') ? EL_CHAR_OUMLAUT :
2374 element == EL_CHAR(']') ? EL_CHAR_UUMLAUT :
2375 element == EL_CHAR('^') ? EL_CHAR_COPYRIGHT : element);
2378 if (game_version < VERSION_IDENT(3,0,0,0))
2380 /* map Supaplex gravity tube elements */
2381 element = (element == EL_SP_GRAVITY_PORT_LEFT ? EL_SP_PORT_LEFT :
2382 element == EL_SP_GRAVITY_PORT_RIGHT ? EL_SP_PORT_RIGHT :
2383 element == EL_SP_GRAVITY_PORT_UP ? EL_SP_PORT_UP :
2384 element == EL_SP_GRAVITY_PORT_DOWN ? EL_SP_PORT_DOWN :
2391 static int LoadLevel_VERS(File *file, int chunk_size, struct LevelInfo *level)
2393 level->file_version = getFileVersion(file);
2394 level->game_version = getFileVersion(file);
2399 static int LoadLevel_DATE(File *file, int chunk_size, struct LevelInfo *level)
2401 level->creation_date.year = getFile16BitBE(file);
2402 level->creation_date.month = getFile8Bit(file);
2403 level->creation_date.day = getFile8Bit(file);
2405 level->creation_date.src = DATE_SRC_LEVELFILE;
2410 static int LoadLevel_HEAD(File *file, int chunk_size, struct LevelInfo *level)
2412 int initial_player_stepsize;
2413 int initial_player_gravity;
2416 level->fieldx = getFile8Bit(file);
2417 level->fieldy = getFile8Bit(file);
2419 level->time = getFile16BitBE(file);
2420 level->gems_needed = getFile16BitBE(file);
2422 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
2423 level->name[i] = getFile8Bit(file);
2424 level->name[MAX_LEVEL_NAME_LEN] = 0;
2426 for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
2427 level->score[i] = getFile8Bit(file);
2429 level->num_yamyam_contents = STD_ELEMENT_CONTENTS;
2430 for (i = 0; i < STD_ELEMENT_CONTENTS; i++)
2431 for (y = 0; y < 3; y++)
2432 for (x = 0; x < 3; x++)
2433 level->yamyam_content[i].e[x][y] = getMappedElement(getFile8Bit(file));
2435 level->amoeba_speed = getFile8Bit(file);
2436 level->time_magic_wall = getFile8Bit(file);
2437 level->time_wheel = getFile8Bit(file);
2438 level->amoeba_content = getMappedElement(getFile8Bit(file));
2440 initial_player_stepsize = (getFile8Bit(file) == 1 ? STEPSIZE_FAST :
2443 for (i = 0; i < MAX_PLAYERS; i++)
2444 level->initial_player_stepsize[i] = initial_player_stepsize;
2446 initial_player_gravity = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2448 for (i = 0; i < MAX_PLAYERS; i++)
2449 level->initial_player_gravity[i] = initial_player_gravity;
2451 level->encoding_16bit_field = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2452 level->em_slippery_gems = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2454 level->use_custom_template = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2456 level->block_last_field = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2457 level->sp_block_last_field = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2458 level->can_move_into_acid_bits = getFile32BitBE(file);
2459 level->dont_collide_with_bits = getFile8Bit(file);
2461 level->use_spring_bug = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2462 level->use_step_counter = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2464 level->instant_relocation = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2465 level->can_pass_to_walkable = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2466 level->grow_into_diggable = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2468 level->game_engine_type = getFile8Bit(file);
2470 ReadUnusedBytesFromFile(file, LEVEL_CHUNK_HEAD_UNUSED);
2475 static int LoadLevel_NAME(File *file, int chunk_size, struct LevelInfo *level)
2479 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
2480 level->name[i] = getFile8Bit(file);
2481 level->name[MAX_LEVEL_NAME_LEN] = 0;
2486 static int LoadLevel_AUTH(File *file, int chunk_size, struct LevelInfo *level)
2490 for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
2491 level->author[i] = getFile8Bit(file);
2492 level->author[MAX_LEVEL_AUTHOR_LEN] = 0;
2497 static int LoadLevel_BODY(File *file, int chunk_size, struct LevelInfo *level)
2500 int chunk_size_expected = level->fieldx * level->fieldy;
2502 /* Note: "chunk_size" was wrong before version 2.0 when elements are
2503 stored with 16-bit encoding (and should be twice as big then).
2504 Even worse, playfield data was stored 16-bit when only yamyam content
2505 contained 16-bit elements and vice versa. */
2507 if (level->encoding_16bit_field && level->file_version >= FILE_VERSION_2_0)
2508 chunk_size_expected *= 2;
2510 if (chunk_size_expected != chunk_size)
2512 ReadUnusedBytesFromFile(file, chunk_size);
2513 return chunk_size_expected;
2516 for (y = 0; y < level->fieldy; y++)
2517 for (x = 0; x < level->fieldx; x++)
2518 level->field[x][y] =
2519 getMappedElement(level->encoding_16bit_field ? getFile16BitBE(file) :
2524 static int LoadLevel_CONT(File *file, int chunk_size, struct LevelInfo *level)
2527 int header_size = 4;
2528 int content_size = MAX_ELEMENT_CONTENTS * 3 * 3;
2529 int chunk_size_expected = header_size + content_size;
2531 /* Note: "chunk_size" was wrong before version 2.0 when elements are
2532 stored with 16-bit encoding (and should be twice as big then).
2533 Even worse, playfield data was stored 16-bit when only yamyam content
2534 contained 16-bit elements and vice versa. */
2536 if (level->encoding_16bit_field && level->file_version >= FILE_VERSION_2_0)
2537 chunk_size_expected += content_size;
2539 if (chunk_size_expected != chunk_size)
2541 ReadUnusedBytesFromFile(file, chunk_size);
2542 return chunk_size_expected;
2546 level->num_yamyam_contents = getFile8Bit(file);
2550 /* correct invalid number of content fields -- should never happen */
2551 if (level->num_yamyam_contents < 1 ||
2552 level->num_yamyam_contents > MAX_ELEMENT_CONTENTS)
2553 level->num_yamyam_contents = STD_ELEMENT_CONTENTS;
2555 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
2556 for (y = 0; y < 3; y++)
2557 for (x = 0; x < 3; x++)
2558 level->yamyam_content[i].e[x][y] =
2559 getMappedElement(level->encoding_16bit_field ?
2560 getFile16BitBE(file) : getFile8Bit(file));
2564 static int LoadLevel_CNT2(File *file, int chunk_size, struct LevelInfo *level)
2569 int content_array[MAX_ELEMENT_CONTENTS][3][3];
2571 element = getMappedElement(getFile16BitBE(file));
2572 num_contents = getFile8Bit(file);
2574 getFile8Bit(file); /* content x size (unused) */
2575 getFile8Bit(file); /* content y size (unused) */
2577 ReadUnusedBytesFromFile(file, LEVEL_CHUNK_CNT2_UNUSED);
2579 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
2580 for (y = 0; y < 3; y++)
2581 for (x = 0; x < 3; x++)
2582 content_array[i][x][y] = getMappedElement(getFile16BitBE(file));
2584 /* correct invalid number of content fields -- should never happen */
2585 if (num_contents < 1 || num_contents > MAX_ELEMENT_CONTENTS)
2586 num_contents = STD_ELEMENT_CONTENTS;
2588 if (element == EL_YAMYAM)
2590 level->num_yamyam_contents = num_contents;
2592 for (i = 0; i < num_contents; i++)
2593 for (y = 0; y < 3; y++)
2594 for (x = 0; x < 3; x++)
2595 level->yamyam_content[i].e[x][y] = content_array[i][x][y];
2597 else if (element == EL_BD_AMOEBA)
2599 level->amoeba_content = content_array[0][0][0];
2603 Error(ERR_WARN, "cannot load content for element '%d'", element);
2609 static int LoadLevel_CNT3(File *file, int chunk_size, struct LevelInfo *level)
2615 int chunk_size_expected;
2617 element = getMappedElement(getFile16BitBE(file));
2618 if (!IS_ENVELOPE(element))
2619 element = EL_ENVELOPE_1;
2621 envelope_nr = element - EL_ENVELOPE_1;
2623 envelope_len = getFile16BitBE(file);
2625 level->envelope[envelope_nr].xsize = getFile8Bit(file);
2626 level->envelope[envelope_nr].ysize = getFile8Bit(file);
2628 ReadUnusedBytesFromFile(file, LEVEL_CHUNK_CNT3_UNUSED);
2630 chunk_size_expected = LEVEL_CHUNK_CNT3_SIZE(envelope_len);
2631 if (chunk_size_expected != chunk_size)
2633 ReadUnusedBytesFromFile(file, chunk_size - LEVEL_CHUNK_CNT3_HEADER);
2634 return chunk_size_expected;
2637 for (i = 0; i < envelope_len; i++)
2638 level->envelope[envelope_nr].text[i] = getFile8Bit(file);
2643 static int LoadLevel_CUS1(File *file, int chunk_size, struct LevelInfo *level)
2645 int num_changed_custom_elements = getFile16BitBE(file);
2646 int chunk_size_expected = 2 + num_changed_custom_elements * 6;
2649 if (chunk_size_expected != chunk_size)
2651 ReadUnusedBytesFromFile(file, chunk_size - 2);
2652 return chunk_size_expected;
2655 for (i = 0; i < num_changed_custom_elements; i++)
2657 int element = getMappedElement(getFile16BitBE(file));
2658 int properties = getFile32BitBE(file);
2660 if (IS_CUSTOM_ELEMENT(element))
2661 element_info[element].properties[EP_BITFIELD_BASE_NR] = properties;
2663 Error(ERR_WARN, "invalid custom element number %d", element);
2665 /* older game versions that wrote level files with CUS1 chunks used
2666 different default push delay values (not yet stored in level file) */
2667 element_info[element].push_delay_fixed = 2;
2668 element_info[element].push_delay_random = 8;
2671 level->file_has_custom_elements = TRUE;
2676 static int LoadLevel_CUS2(File *file, int chunk_size, struct LevelInfo *level)
2678 int num_changed_custom_elements = getFile16BitBE(file);
2679 int chunk_size_expected = 2 + num_changed_custom_elements * 4;
2682 if (chunk_size_expected != chunk_size)
2684 ReadUnusedBytesFromFile(file, chunk_size - 2);
2685 return chunk_size_expected;
2688 for (i = 0; i < num_changed_custom_elements; i++)
2690 int element = getMappedElement(getFile16BitBE(file));
2691 int custom_target_element = getMappedElement(getFile16BitBE(file));
2693 if (IS_CUSTOM_ELEMENT(element))
2694 element_info[element].change->target_element = custom_target_element;
2696 Error(ERR_WARN, "invalid custom element number %d", element);
2699 level->file_has_custom_elements = TRUE;
2704 static int LoadLevel_CUS3(File *file, int chunk_size, struct LevelInfo *level)
2706 int num_changed_custom_elements = getFile16BitBE(file);
2707 int chunk_size_expected = LEVEL_CHUNK_CUS3_SIZE(num_changed_custom_elements);
2710 if (chunk_size_expected != chunk_size)
2712 ReadUnusedBytesFromFile(file, chunk_size - 2);
2713 return chunk_size_expected;
2716 for (i = 0; i < num_changed_custom_elements; i++)
2718 int element = getMappedElement(getFile16BitBE(file));
2719 struct ElementInfo *ei = &element_info[element];
2720 unsigned int event_bits;
2722 if (!IS_CUSTOM_ELEMENT(element))
2724 Error(ERR_WARN, "invalid custom element number %d", element);
2726 element = EL_INTERNAL_DUMMY;
2729 for (j = 0; j < MAX_ELEMENT_NAME_LEN; j++)
2730 ei->description[j] = getFile8Bit(file);
2731 ei->description[MAX_ELEMENT_NAME_LEN] = 0;
2733 ei->properties[EP_BITFIELD_BASE_NR] = getFile32BitBE(file);
2735 /* some free bytes for future properties and padding */
2736 ReadUnusedBytesFromFile(file, 7);
2738 ei->use_gfx_element = getFile8Bit(file);
2739 ei->gfx_element_initial = getMappedElement(getFile16BitBE(file));
2741 ei->collect_score_initial = getFile8Bit(file);
2742 ei->collect_count_initial = getFile8Bit(file);
2744 ei->push_delay_fixed = getFile16BitBE(file);
2745 ei->push_delay_random = getFile16BitBE(file);
2746 ei->move_delay_fixed = getFile16BitBE(file);
2747 ei->move_delay_random = getFile16BitBE(file);
2749 ei->move_pattern = getFile16BitBE(file);
2750 ei->move_direction_initial = getFile8Bit(file);
2751 ei->move_stepsize = getFile8Bit(file);
2753 for (y = 0; y < 3; y++)
2754 for (x = 0; x < 3; x++)
2755 ei->content.e[x][y] = getMappedElement(getFile16BitBE(file));
2757 event_bits = getFile32BitBE(file);
2758 for (j = 0; j < NUM_CHANGE_EVENTS; j++)
2759 if (event_bits & (1 << j))
2760 ei->change->has_event[j] = TRUE;
2762 ei->change->target_element = getMappedElement(getFile16BitBE(file));
2764 ei->change->delay_fixed = getFile16BitBE(file);
2765 ei->change->delay_random = getFile16BitBE(file);
2766 ei->change->delay_frames = getFile16BitBE(file);
2768 ei->change->initial_trigger_element= getMappedElement(getFile16BitBE(file));
2770 ei->change->explode = getFile8Bit(file);
2771 ei->change->use_target_content = getFile8Bit(file);
2772 ei->change->only_if_complete = getFile8Bit(file);
2773 ei->change->use_random_replace = getFile8Bit(file);
2775 ei->change->random_percentage = getFile8Bit(file);
2776 ei->change->replace_when = getFile8Bit(file);
2778 for (y = 0; y < 3; y++)
2779 for (x = 0; x < 3; x++)
2780 ei->change->target_content.e[x][y] =
2781 getMappedElement(getFile16BitBE(file));
2783 ei->slippery_type = getFile8Bit(file);
2785 /* some free bytes for future properties and padding */
2786 ReadUnusedBytesFromFile(file, LEVEL_CPART_CUS3_UNUSED);
2788 /* mark that this custom element has been modified */
2789 ei->modified_settings = TRUE;
2792 level->file_has_custom_elements = TRUE;
2797 static int LoadLevel_CUS4(File *file, int chunk_size, struct LevelInfo *level)
2799 struct ElementInfo *ei;
2800 int chunk_size_expected;
2804 /* ---------- custom element base property values (96 bytes) ------------- */
2806 element = getMappedElement(getFile16BitBE(file));
2808 if (!IS_CUSTOM_ELEMENT(element))
2810 Error(ERR_WARN, "invalid custom element number %d", element);
2812 ReadUnusedBytesFromFile(file, chunk_size - 2);
2816 ei = &element_info[element];
2818 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
2819 ei->description[i] = getFile8Bit(file);
2820 ei->description[MAX_ELEMENT_NAME_LEN] = 0;
2822 ei->properties[EP_BITFIELD_BASE_NR] = getFile32BitBE(file);
2824 ReadUnusedBytesFromFile(file, 4); /* reserved for more base properties */
2826 ei->num_change_pages = getFile8Bit(file);
2828 chunk_size_expected = LEVEL_CHUNK_CUS4_SIZE(ei->num_change_pages);
2829 if (chunk_size_expected != chunk_size)
2831 ReadUnusedBytesFromFile(file, chunk_size - 43);
2832 return chunk_size_expected;
2835 ei->ce_value_fixed_initial = getFile16BitBE(file);
2836 ei->ce_value_random_initial = getFile16BitBE(file);
2837 ei->use_last_ce_value = getFile8Bit(file);
2839 ei->use_gfx_element = getFile8Bit(file);
2840 ei->gfx_element_initial = getMappedElement(getFile16BitBE(file));
2842 ei->collect_score_initial = getFile8Bit(file);
2843 ei->collect_count_initial = getFile8Bit(file);
2845 ei->drop_delay_fixed = getFile8Bit(file);
2846 ei->push_delay_fixed = getFile8Bit(file);
2847 ei->drop_delay_random = getFile8Bit(file);
2848 ei->push_delay_random = getFile8Bit(file);
2849 ei->move_delay_fixed = getFile16BitBE(file);
2850 ei->move_delay_random = getFile16BitBE(file);
2852 /* bits 0 - 15 of "move_pattern" ... */
2853 ei->move_pattern = getFile16BitBE(file);
2854 ei->move_direction_initial = getFile8Bit(file);
2855 ei->move_stepsize = getFile8Bit(file);
2857 ei->slippery_type = getFile8Bit(file);
2859 for (y = 0; y < 3; y++)
2860 for (x = 0; x < 3; x++)
2861 ei->content.e[x][y] = getMappedElement(getFile16BitBE(file));
2863 ei->move_enter_element = getMappedElement(getFile16BitBE(file));
2864 ei->move_leave_element = getMappedElement(getFile16BitBE(file));
2865 ei->move_leave_type = getFile8Bit(file);
2867 /* ... bits 16 - 31 of "move_pattern" (not nice, but downward compatible) */
2868 ei->move_pattern |= (getFile16BitBE(file) << 16);
2870 ei->access_direction = getFile8Bit(file);
2872 ei->explosion_delay = getFile8Bit(file);
2873 ei->ignition_delay = getFile8Bit(file);
2874 ei->explosion_type = getFile8Bit(file);
2876 /* some free bytes for future custom property values and padding */
2877 ReadUnusedBytesFromFile(file, 1);
2879 /* ---------- change page property values (48 bytes) --------------------- */
2881 setElementChangePages(ei, ei->num_change_pages);
2883 for (i = 0; i < ei->num_change_pages; i++)
2885 struct ElementChangeInfo *change = &ei->change_page[i];
2886 unsigned int event_bits;
2888 /* always start with reliable default values */
2889 setElementChangeInfoToDefaults(change);
2891 /* bits 0 - 31 of "has_event[]" ... */
2892 event_bits = getFile32BitBE(file);
2893 for (j = 0; j < MIN(NUM_CHANGE_EVENTS, 32); j++)
2894 if (event_bits & (1 << j))
2895 change->has_event[j] = TRUE;
2897 change->target_element = getMappedElement(getFile16BitBE(file));
2899 change->delay_fixed = getFile16BitBE(file);
2900 change->delay_random = getFile16BitBE(file);
2901 change->delay_frames = getFile16BitBE(file);
2903 change->initial_trigger_element = getMappedElement(getFile16BitBE(file));
2905 change->explode = getFile8Bit(file);
2906 change->use_target_content = getFile8Bit(file);
2907 change->only_if_complete = getFile8Bit(file);
2908 change->use_random_replace = getFile8Bit(file);
2910 change->random_percentage = getFile8Bit(file);
2911 change->replace_when = getFile8Bit(file);
2913 for (y = 0; y < 3; y++)
2914 for (x = 0; x < 3; x++)
2915 change->target_content.e[x][y]= getMappedElement(getFile16BitBE(file));
2917 change->can_change = getFile8Bit(file);
2919 change->trigger_side = getFile8Bit(file);
2921 change->trigger_player = getFile8Bit(file);
2922 change->trigger_page = getFile8Bit(file);
2924 change->trigger_page = (change->trigger_page == CH_PAGE_ANY_FILE ?
2925 CH_PAGE_ANY : (1 << change->trigger_page));
2927 change->has_action = getFile8Bit(file);
2928 change->action_type = getFile8Bit(file);
2929 change->action_mode = getFile8Bit(file);
2930 change->action_arg = getFile16BitBE(file);
2932 /* ... bits 32 - 39 of "has_event[]" (not nice, but downward compatible) */
2933 event_bits = getFile8Bit(file);
2934 for (j = 32; j < NUM_CHANGE_EVENTS; j++)
2935 if (event_bits & (1 << (j - 32)))
2936 change->has_event[j] = TRUE;
2939 /* mark this custom element as modified */
2940 ei->modified_settings = TRUE;
2942 level->file_has_custom_elements = TRUE;
2947 static int LoadLevel_GRP1(File *file, int chunk_size, struct LevelInfo *level)
2949 struct ElementInfo *ei;
2950 struct ElementGroupInfo *group;
2954 element = getMappedElement(getFile16BitBE(file));
2956 if (!IS_GROUP_ELEMENT(element))
2958 Error(ERR_WARN, "invalid group element number %d", element);
2960 ReadUnusedBytesFromFile(file, chunk_size - 2);
2964 ei = &element_info[element];
2966 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
2967 ei->description[i] = getFile8Bit(file);
2968 ei->description[MAX_ELEMENT_NAME_LEN] = 0;
2970 group = element_info[element].group;
2972 group->num_elements = getFile8Bit(file);
2974 ei->use_gfx_element = getFile8Bit(file);
2975 ei->gfx_element_initial = getMappedElement(getFile16BitBE(file));
2977 group->choice_mode = getFile8Bit(file);
2979 /* some free bytes for future values and padding */
2980 ReadUnusedBytesFromFile(file, 3);
2982 for (i = 0; i < MAX_ELEMENTS_IN_GROUP; i++)
2983 group->element[i] = getMappedElement(getFile16BitBE(file));
2985 /* mark this group element as modified */
2986 element_info[element].modified_settings = TRUE;
2988 level->file_has_custom_elements = TRUE;
2993 static int LoadLevel_MicroChunk(File *file, struct LevelFileConfigInfo *conf,
2994 int element, int real_element)
2996 int micro_chunk_size = 0;
2997 int conf_type = getFile8Bit(file);
2998 int byte_mask = conf_type & CONF_MASK_BYTES;
2999 boolean element_found = FALSE;
3002 micro_chunk_size += 1;
3004 if (byte_mask == CONF_MASK_MULTI_BYTES)
3006 int num_bytes = getFile16BitBE(file);
3007 byte *buffer = checked_malloc(num_bytes);
3009 ReadBytesFromFile(file, buffer, num_bytes);
3011 for (i = 0; conf[i].data_type != -1; i++)
3013 if (conf[i].element == element &&
3014 conf[i].conf_type == conf_type)
3016 int data_type = conf[i].data_type;
3017 int num_entities = num_bytes / CONF_ENTITY_NUM_BYTES(data_type);
3018 int max_num_entities = conf[i].max_num_entities;
3020 if (num_entities > max_num_entities)
3023 "truncating number of entities for element %d from %d to %d",
3024 element, num_entities, max_num_entities);
3026 num_entities = max_num_entities;
3029 if (num_entities == 0 && (data_type == TYPE_ELEMENT_LIST ||
3030 data_type == TYPE_CONTENT_LIST))
3032 /* for element and content lists, zero entities are not allowed */
3033 Error(ERR_WARN, "found empty list of entities for element %d",
3036 /* do not set "num_entities" here to prevent reading behind buffer */
3038 *(int *)(conf[i].num_entities) = 1; /* at least one is required */
3042 *(int *)(conf[i].num_entities) = num_entities;
3045 element_found = TRUE;
3047 if (data_type == TYPE_STRING)
3049 char *string = (char *)(conf[i].value);
3052 for (j = 0; j < max_num_entities; j++)
3053 string[j] = (j < num_entities ? buffer[j] : '\0');
3055 else if (data_type == TYPE_ELEMENT_LIST)
3057 int *element_array = (int *)(conf[i].value);
3060 for (j = 0; j < num_entities; j++)
3062 getMappedElement(CONF_ELEMENTS_ELEMENT(buffer, j));
3064 else if (data_type == TYPE_CONTENT_LIST)
3066 struct Content *content= (struct Content *)(conf[i].value);
3069 for (c = 0; c < num_entities; c++)
3070 for (y = 0; y < 3; y++)
3071 for (x = 0; x < 3; x++)
3072 content[c].e[x][y] =
3073 getMappedElement(CONF_CONTENTS_ELEMENT(buffer, c, x, y));
3076 element_found = FALSE;
3082 checked_free(buffer);
3084 micro_chunk_size += 2 + num_bytes;
3086 else /* constant size configuration data (1, 2 or 4 bytes) */
3088 int value = (byte_mask == CONF_MASK_1_BYTE ? getFile8Bit (file) :
3089 byte_mask == CONF_MASK_2_BYTE ? getFile16BitBE(file) :
3090 byte_mask == CONF_MASK_4_BYTE ? getFile32BitBE(file) : 0);
3092 for (i = 0; conf[i].data_type != -1; i++)
3094 if (conf[i].element == element &&
3095 conf[i].conf_type == conf_type)
3097 int data_type = conf[i].data_type;
3099 if (data_type == TYPE_ELEMENT)
3100 value = getMappedElement(value);
3102 if (data_type == TYPE_BOOLEAN)
3103 *(boolean *)(conf[i].value) = value;
3105 *(int *) (conf[i].value) = value;
3107 element_found = TRUE;
3113 micro_chunk_size += CONF_VALUE_NUM_BYTES(byte_mask);
3118 char *error_conf_chunk_bytes =
3119 (byte_mask == CONF_MASK_1_BYTE ? "CONF_VALUE_8_BIT" :
3120 byte_mask == CONF_MASK_2_BYTE ? "CONF_VALUE_16_BIT" :
3121 byte_mask == CONF_MASK_4_BYTE ? "CONF_VALUE_32_BIT" :"CONF_VALUE_BYTES");
3122 int error_conf_chunk_token = conf_type & CONF_MASK_TOKEN;
3123 int error_element = real_element;
3125 Error(ERR_WARN, "cannot load micro chunk '%s(%d)' value for element %d ['%s']",
3126 error_conf_chunk_bytes, error_conf_chunk_token,
3127 error_element, EL_NAME(error_element));
3130 return micro_chunk_size;
3133 static int LoadLevel_INFO(File *file, int chunk_size, struct LevelInfo *level)
3135 int real_chunk_size = 0;
3137 li = *level; /* copy level data into temporary buffer */
3139 while (!checkEndOfFile(file))
3141 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_INFO, -1, -1);
3143 if (real_chunk_size >= chunk_size)
3147 *level = li; /* copy temporary buffer back to level data */
3149 return real_chunk_size;
3152 static int LoadLevel_CONF(File *file, int chunk_size, struct LevelInfo *level)
3154 int real_chunk_size = 0;
3156 li = *level; /* copy level data into temporary buffer */
3158 while (!checkEndOfFile(file))
3160 int element = getMappedElement(getFile16BitBE(file));
3162 real_chunk_size += 2;
3163 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_CONF,
3165 if (real_chunk_size >= chunk_size)
3169 *level = li; /* copy temporary buffer back to level data */
3171 return real_chunk_size;
3174 static int LoadLevel_ELEM(File *file, int chunk_size, struct LevelInfo *level)
3176 int real_chunk_size = 0;
3178 li = *level; /* copy level data into temporary buffer */
3180 while (!checkEndOfFile(file))
3182 int element = getMappedElement(getFile16BitBE(file));
3184 real_chunk_size += 2;
3185 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_ELEM,
3187 if (real_chunk_size >= chunk_size)
3191 *level = li; /* copy temporary buffer back to level data */
3193 return real_chunk_size;
3196 static int LoadLevel_NOTE(File *file, int chunk_size, struct LevelInfo *level)
3198 int element = getMappedElement(getFile16BitBE(file));
3199 int envelope_nr = element - EL_ENVELOPE_1;
3200 int real_chunk_size = 2;
3202 xx_envelope = level->envelope[envelope_nr]; /* copy into temporary buffer */
3204 while (!checkEndOfFile(file))
3206 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_NOTE,
3209 if (real_chunk_size >= chunk_size)
3213 level->envelope[envelope_nr] = xx_envelope; /* copy from temporary buffer */
3215 return real_chunk_size;
3218 static int LoadLevel_CUSX(File *file, int chunk_size, struct LevelInfo *level)
3220 int element = getMappedElement(getFile16BitBE(file));
3221 int real_chunk_size = 2;
3222 struct ElementInfo *ei = &element_info[element];
3225 xx_ei = *ei; /* copy element data into temporary buffer */
3227 xx_ei.num_change_pages = -1;
3229 while (!checkEndOfFile(file))
3231 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_CUSX_base,
3233 if (xx_ei.num_change_pages != -1)
3236 if (real_chunk_size >= chunk_size)
3242 if (ei->num_change_pages == -1)
3244 Error(ERR_WARN, "LoadLevel_CUSX(): missing 'num_change_pages' for '%s'",
3247 ei->num_change_pages = 1;
3249 setElementChangePages(ei, 1);
3250 setElementChangeInfoToDefaults(ei->change);
3252 return real_chunk_size;
3255 /* initialize number of change pages stored for this custom element */
3256 setElementChangePages(ei, ei->num_change_pages);
3257 for (i = 0; i < ei->num_change_pages; i++)
3258 setElementChangeInfoToDefaults(&ei->change_page[i]);
3260 /* start with reading properties for the first change page */
3261 xx_current_change_page = 0;
3263 while (!checkEndOfFile(file))
3265 struct ElementChangeInfo *change = &ei->change_page[xx_current_change_page];
3267 xx_change = *change; /* copy change data into temporary buffer */
3269 resetEventBits(); /* reset bits; change page might have changed */
3271 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_CUSX_change,
3274 *change = xx_change;
3276 setEventFlagsFromEventBits(change);
3278 if (real_chunk_size >= chunk_size)
3282 level->file_has_custom_elements = TRUE;
3284 return real_chunk_size;
3287 static int LoadLevel_GRPX(File *file, int chunk_size, struct LevelInfo *level)
3289 int element = getMappedElement(getFile16BitBE(file));
3290 int real_chunk_size = 2;
3291 struct ElementInfo *ei = &element_info[element];
3292 struct ElementGroupInfo *group = ei->group;
3294 xx_ei = *ei; /* copy element data into temporary buffer */
3295 xx_group = *group; /* copy group data into temporary buffer */
3297 while (!checkEndOfFile(file))
3299 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_GRPX,
3302 if (real_chunk_size >= chunk_size)
3309 level->file_has_custom_elements = TRUE;
3311 return real_chunk_size;
3314 static void LoadLevelFromFileInfo_RND(struct LevelInfo *level,
3315 struct LevelFileInfo *level_file_info,
3316 boolean level_info_only)
3318 char *filename = level_file_info->filename;
3319 char cookie[MAX_LINE_LEN];
3320 char chunk_name[CHUNK_ID_LEN + 1];
3324 if (!(file = openFile(filename, MODE_READ)))
3326 level->no_valid_file = TRUE;
3327 level->no_level_file = TRUE;
3329 if (level_info_only)
3332 Error(ERR_WARN, "cannot read level '%s' -- using empty level", filename);
3334 if (!setup.editor.use_template_for_new_levels)
3337 /* if level file not found, try to initialize level data from template */
3338 filename = getGlobalLevelTemplateFilename();
3340 if (!(file = openFile(filename, MODE_READ)))
3343 /* default: for empty levels, use level template for custom elements */
3344 level->use_custom_template = TRUE;
3346 level->no_valid_file = FALSE;
3349 getFileChunkBE(file, chunk_name, NULL);
3350 if (strEqual(chunk_name, "RND1"))
3352 getFile32BitBE(file); /* not used */
3354 getFileChunkBE(file, chunk_name, NULL);
3355 if (!strEqual(chunk_name, "CAVE"))
3357 level->no_valid_file = TRUE;
3359 Error(ERR_WARN, "unknown format of level file '%s'", filename);
3366 else /* check for pre-2.0 file format with cookie string */
3368 strcpy(cookie, chunk_name);
3369 if (getStringFromFile(file, &cookie[4], MAX_LINE_LEN - 4) == NULL)
3371 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
3372 cookie[strlen(cookie) - 1] = '\0';
3374 if (!checkCookieString(cookie, LEVEL_COOKIE_TMPL))
3376 level->no_valid_file = TRUE;
3378 Error(ERR_WARN, "unknown format of level file '%s'", filename);
3385 if ((level->file_version = getFileVersionFromCookieString(cookie)) == -1)
3387 level->no_valid_file = TRUE;
3389 Error(ERR_WARN, "unsupported version of level file '%s'", filename);
3396 /* pre-2.0 level files have no game version, so use file version here */
3397 level->game_version = level->file_version;
3400 if (level->file_version < FILE_VERSION_1_2)
3402 /* level files from versions before 1.2.0 without chunk structure */
3403 LoadLevel_HEAD(file, LEVEL_CHUNK_HEAD_SIZE, level);
3404 LoadLevel_BODY(file, level->fieldx * level->fieldy, level);
3412 int (*loader)(File *, int, struct LevelInfo *);
3416 { "VERS", LEVEL_CHUNK_VERS_SIZE, LoadLevel_VERS },
3417 { "DATE", LEVEL_CHUNK_DATE_SIZE, LoadLevel_DATE },
3418 { "HEAD", LEVEL_CHUNK_HEAD_SIZE, LoadLevel_HEAD },
3419 { "NAME", LEVEL_CHUNK_NAME_SIZE, LoadLevel_NAME },
3420 { "AUTH", LEVEL_CHUNK_AUTH_SIZE, LoadLevel_AUTH },
3421 { "INFO", -1, LoadLevel_INFO },
3422 { "BODY", -1, LoadLevel_BODY },
3423 { "CONT", -1, LoadLevel_CONT },
3424 { "CNT2", LEVEL_CHUNK_CNT2_SIZE, LoadLevel_CNT2 },
3425 { "CNT3", -1, LoadLevel_CNT3 },
3426 { "CUS1", -1, LoadLevel_CUS1 },
3427 { "CUS2", -1, LoadLevel_CUS2 },
3428 { "CUS3", -1, LoadLevel_CUS3 },
3429 { "CUS4", -1, LoadLevel_CUS4 },
3430 { "GRP1", -1, LoadLevel_GRP1 },
3431 { "CONF", -1, LoadLevel_CONF },
3432 { "ELEM", -1, LoadLevel_ELEM },
3433 { "NOTE", -1, LoadLevel_NOTE },
3434 { "CUSX", -1, LoadLevel_CUSX },
3435 { "GRPX", -1, LoadLevel_GRPX },
3440 while (getFileChunkBE(file, chunk_name, &chunk_size))
3444 while (chunk_info[i].name != NULL &&
3445 !strEqual(chunk_name, chunk_info[i].name))
3448 if (chunk_info[i].name == NULL)
3450 Error(ERR_WARN, "unknown chunk '%s' in level file '%s'",
3451 chunk_name, filename);
3452 ReadUnusedBytesFromFile(file, chunk_size);
3454 else if (chunk_info[i].size != -1 &&
3455 chunk_info[i].size != chunk_size)
3457 Error(ERR_WARN, "wrong size (%d) of chunk '%s' in level file '%s'",
3458 chunk_size, chunk_name, filename);
3459 ReadUnusedBytesFromFile(file, chunk_size);
3463 /* call function to load this level chunk */
3464 int chunk_size_expected =
3465 (chunk_info[i].loader)(file, chunk_size, level);
3467 /* the size of some chunks cannot be checked before reading other
3468 chunks first (like "HEAD" and "BODY") that contain some header
3469 information, so check them here */
3470 if (chunk_size_expected != chunk_size)
3472 Error(ERR_WARN, "wrong size (%d) of chunk '%s' in level file '%s'",
3473 chunk_size, chunk_name, filename);
3483 /* ------------------------------------------------------------------------- */
3484 /* functions for loading EM level */
3485 /* ------------------------------------------------------------------------- */
3487 void CopyNativeLevel_RND_to_EM(struct LevelInfo *level)
3489 static int ball_xy[8][2] =
3500 struct LevelInfo_EM *level_em = level->native_em_level;
3501 struct LEVEL *lev = level_em->lev;
3502 struct PLAYER **ply = level_em->ply;
3505 lev->width = MIN(level->fieldx, EM_MAX_CAVE_WIDTH);
3506 lev->height = MIN(level->fieldy, EM_MAX_CAVE_HEIGHT);
3508 lev->time_seconds = level->time;
3509 lev->required_initial = level->gems_needed;
3511 lev->emerald_score = level->score[SC_EMERALD];
3512 lev->diamond_score = level->score[SC_DIAMOND];
3513 lev->alien_score = level->score[SC_ROBOT];
3514 lev->tank_score = level->score[SC_SPACESHIP];
3515 lev->bug_score = level->score[SC_BUG];
3516 lev->eater_score = level->score[SC_YAMYAM];
3517 lev->nut_score = level->score[SC_NUT];
3518 lev->dynamite_score = level->score[SC_DYNAMITE];
3519 lev->key_score = level->score[SC_KEY];
3520 lev->exit_score = level->score[SC_TIME_BONUS];
3522 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
3523 for (y = 0; y < 3; y++)
3524 for (x = 0; x < 3; x++)
3525 lev->eater_array[i][y * 3 + x] =
3526 map_element_RND_to_EM(level->yamyam_content[i].e[x][y]);
3528 lev->amoeba_time = level->amoeba_speed;
3529 lev->wonderwall_time_initial = level->time_magic_wall;
3530 lev->wheel_time = level->time_wheel;
3532 lev->android_move_time = level->android_move_time;
3533 lev->android_clone_time = level->android_clone_time;
3534 lev->ball_random = level->ball_random;
3535 lev->ball_state_initial = level->ball_state_initial;
3536 lev->ball_time = level->ball_time;
3537 lev->num_ball_arrays = level->num_ball_contents;
3539 lev->lenses_score = level->lenses_score;
3540 lev->magnify_score = level->magnify_score;
3541 lev->slurp_score = level->slurp_score;
3543 lev->lenses_time = level->lenses_time;
3544 lev->magnify_time = level->magnify_time;
3546 lev->wind_direction_initial =
3547 map_direction_RND_to_EM(level->wind_direction_initial);
3548 lev->wind_cnt_initial = (level->wind_direction_initial != MV_NONE ?
3549 lev->wind_time : 0);
3551 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
3552 for (j = 0; j < 8; j++)
3553 lev->ball_array[i][j] =
3554 map_element_RND_to_EM(level->
3555 ball_content[i].e[ball_xy[j][0]][ball_xy[j][1]]);
3557 map_android_clone_elements_RND_to_EM(level);
3559 /* first fill the complete playfield with the default border element */
3560 for (y = 0; y < EM_MAX_CAVE_HEIGHT; y++)
3561 for (x = 0; x < EM_MAX_CAVE_WIDTH; x++)
3562 level_em->cave[x][y] = ZBORDER;
3564 if (BorderElement == EL_STEELWALL)
3566 for (y = 0; y < lev->height + 2; y++)
3567 for (x = 0; x < lev->width + 2; x++)
3568 level_em->cave[x + 1][y + 1] = map_element_RND_to_EM(EL_STEELWALL);
3571 /* then copy the real level contents from level file into the playfield */
3572 for (y = 0; y < lev->height; y++) for (x = 0; x < lev->width; x++)
3574 int new_element = map_element_RND_to_EM(level->field[x][y]);
3575 int offset = (BorderElement == EL_STEELWALL ? 1 : 0);
3576 int xx = x + 1 + offset;
3577 int yy = y + 1 + offset;
3579 if (level->field[x][y] == EL_AMOEBA_DEAD)
3580 new_element = map_element_RND_to_EM(EL_AMOEBA_WET);
3582 level_em->cave[xx][yy] = new_element;
3585 for (i = 0; i < MAX_PLAYERS; i++)
3587 ply[i]->x_initial = 0;
3588 ply[i]->y_initial = 0;
3591 /* initialize player positions and delete players from the playfield */
3592 for (y = 0; y < lev->height; y++) for (x = 0; x < lev->width; x++)
3594 if (ELEM_IS_PLAYER(level->field[x][y]))
3596 int player_nr = GET_PLAYER_NR(level->field[x][y]);
3597 int offset = (BorderElement == EL_STEELWALL ? 1 : 0);
3598 int xx = x + 1 + offset;
3599 int yy = y + 1 + offset;
3601 ply[player_nr]->x_initial = xx;
3602 ply[player_nr]->y_initial = yy;
3604 level_em->cave[xx][yy] = map_element_RND_to_EM(EL_EMPTY);
3608 if (BorderElement == EL_STEELWALL)
3615 void CopyNativeLevel_EM_to_RND(struct LevelInfo *level)
3617 static int ball_xy[8][2] =
3628 struct LevelInfo_EM *level_em = level->native_em_level;
3629 struct LEVEL *lev = level_em->lev;
3630 struct PLAYER **ply = level_em->ply;
3633 level->fieldx = MIN(lev->width, MAX_LEV_FIELDX);
3634 level->fieldy = MIN(lev->height, MAX_LEV_FIELDY);
3636 level->time = lev->time_seconds;
3637 level->gems_needed = lev->required_initial;
3639 sprintf(level->name, "Level %d", level->file_info.nr);
3641 level->score[SC_EMERALD] = lev->emerald_score;
3642 level->score[SC_DIAMOND] = lev->diamond_score;
3643 level->score[SC_ROBOT] = lev->alien_score;
3644 level->score[SC_SPACESHIP] = lev->tank_score;
3645 level->score[SC_BUG] = lev->bug_score;
3646 level->score[SC_YAMYAM] = lev->eater_score;
3647 level->score[SC_NUT] = lev->nut_score;
3648 level->score[SC_DYNAMITE] = lev->dynamite_score;
3649 level->score[SC_KEY] = lev->key_score;
3650 level->score[SC_TIME_BONUS] = lev->exit_score;
3652 level->num_yamyam_contents = MAX_ELEMENT_CONTENTS;
3654 for (i = 0; i < level->num_yamyam_contents; i++)
3655 for (y = 0; y < 3; y++)
3656 for (x = 0; x < 3; x++)
3657 level->yamyam_content[i].e[x][y] =
3658 map_element_EM_to_RND(lev->eater_array[i][y * 3 + x]);
3660 level->amoeba_speed = lev->amoeba_time;
3661 level->time_magic_wall = lev->wonderwall_time_initial;
3662 level->time_wheel = lev->wheel_time;
3664 level->android_move_time = lev->android_move_time;
3665 level->android_clone_time = lev->android_clone_time;
3666 level->ball_random = lev->ball_random;
3667 level->ball_state_initial = lev->ball_state_initial;
3668 level->ball_time = lev->ball_time;
3669 level->num_ball_contents = lev->num_ball_arrays;
3671 level->lenses_score = lev->lenses_score;
3672 level->magnify_score = lev->magnify_score;
3673 level->slurp_score = lev->slurp_score;
3675 level->lenses_time = lev->lenses_time;
3676 level->magnify_time = lev->magnify_time;
3678 level->wind_direction_initial =
3679 map_direction_EM_to_RND(lev->wind_direction_initial);
3681 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
3682 for (j = 0; j < 8; j++)
3683 level->ball_content[i].e[ball_xy[j][0]][ball_xy[j][1]] =
3684 map_element_EM_to_RND(lev->ball_array[i][j]);
3686 map_android_clone_elements_EM_to_RND(level);
3688 /* convert the playfield (some elements need special treatment) */
3689 for (y = 0; y < level->fieldy; y++) for (x = 0; x < level->fieldx; x++)
3691 int new_element = map_element_EM_to_RND(level_em->cave[x + 1][y + 1]);
3693 if (new_element == EL_AMOEBA_WET && level->amoeba_speed == 0)
3694 new_element = EL_AMOEBA_DEAD;
3696 level->field[x][y] = new_element;
3699 for (i = 0; i < MAX_PLAYERS; i++)
3701 /* in case of all players set to the same field, use the first player */
3702 int nr = MAX_PLAYERS - i - 1;
3703 int jx = ply[nr]->x_initial - 1;
3704 int jy = ply[nr]->y_initial - 1;
3706 if (jx != -1 && jy != -1)
3707 level->field[jx][jy] = EL_PLAYER_1 + nr;
3712 /* ------------------------------------------------------------------------- */
3713 /* functions for loading SP level */
3714 /* ------------------------------------------------------------------------- */
3716 void CopyNativeLevel_RND_to_SP(struct LevelInfo *level)
3718 struct LevelInfo_SP *level_sp = level->native_sp_level;
3719 LevelInfoType *header = &level_sp->header;
3722 level_sp->width = level->fieldx;
3723 level_sp->height = level->fieldy;
3725 for (x = 0; x < level->fieldx; x++)
3726 for (y = 0; y < level->fieldy; y++)
3727 level_sp->playfield[x][y] = map_element_RND_to_SP(level->field[x][y]);
3729 header->InitialGravity = (level->initial_player_gravity[0] ? 1 : 0);
3731 for (i = 0; i < SP_LEVEL_NAME_LEN; i++)
3732 header->LevelTitle[i] = level->name[i];
3733 /* !!! NO STRING TERMINATION IN SUPAPLEX VB CODE YET -- FIX THIS !!! */
3735 header->InfotronsNeeded = level->gems_needed;
3737 header->SpecialPortCount = 0;
3739 for (x = 0; x < level->fieldx; x++) for (y = 0; y < level->fieldy; y++)
3741 boolean gravity_port_found = FALSE;
3742 boolean gravity_port_valid = FALSE;
3743 int gravity_port_flag;
3744 int gravity_port_base_element;
3745 int element = level->field[x][y];
3747 if (element >= EL_SP_GRAVITY_ON_PORT_RIGHT &&
3748 element <= EL_SP_GRAVITY_ON_PORT_UP)
3750 gravity_port_found = TRUE;
3751 gravity_port_valid = TRUE;
3752 gravity_port_flag = 1;
3753 gravity_port_base_element = EL_SP_GRAVITY_ON_PORT_RIGHT;
3755 else if (element >= EL_SP_GRAVITY_OFF_PORT_RIGHT &&
3756 element <= EL_SP_GRAVITY_OFF_PORT_UP)
3758 gravity_port_found = TRUE;
3759 gravity_port_valid = TRUE;
3760 gravity_port_flag = 0;
3761 gravity_port_base_element = EL_SP_GRAVITY_OFF_PORT_RIGHT;
3763 else if (element >= EL_SP_GRAVITY_PORT_RIGHT &&
3764 element <= EL_SP_GRAVITY_PORT_UP)
3766 /* change R'n'D style gravity inverting special port to normal port
3767 (there are no gravity inverting ports in native Supaplex engine) */
3769 gravity_port_found = TRUE;
3770 gravity_port_valid = FALSE;
3771 gravity_port_base_element = EL_SP_GRAVITY_PORT_RIGHT;
3774 if (gravity_port_found)
3776 if (gravity_port_valid &&
3777 header->SpecialPortCount < SP_MAX_SPECIAL_PORTS)
3779 SpecialPortType *port = &header->SpecialPort[header->SpecialPortCount];
3781 port->PortLocation = (y * level->fieldx + x) * 2;
3782 port->Gravity = gravity_port_flag;
3784 element += EL_SP_GRAVITY_PORT_RIGHT - gravity_port_base_element;
3786 header->SpecialPortCount++;
3790 /* change special gravity port to normal port */
3792 element += EL_SP_PORT_RIGHT - gravity_port_base_element;
3795 level_sp->playfield[x][y] = element - EL_SP_START;
3800 void CopyNativeLevel_SP_to_RND(struct LevelInfo *level)
3802 struct LevelInfo_SP *level_sp = level->native_sp_level;
3803 LevelInfoType *header = &level_sp->header;
3804 boolean num_invalid_elements = 0;
3807 level->fieldx = level_sp->width;
3808 level->fieldy = level_sp->height;
3810 for (x = 0; x < level->fieldx; x++)
3812 for (y = 0; y < level->fieldy; y++)
3814 int element_old = level_sp->playfield[x][y];
3815 int element_new = getMappedElement(map_element_SP_to_RND(element_old));
3817 if (element_new == EL_UNKNOWN)
3819 num_invalid_elements++;
3821 Error(ERR_DEBUG, "invalid element %d at position %d, %d",
3825 level->field[x][y] = element_new;
3829 if (num_invalid_elements > 0)
3830 Error(ERR_WARN, "found %d invalid elements%s", num_invalid_elements,
3831 (!options.debug ? " (use '--debug' for more details)" : ""));
3833 for (i = 0; i < MAX_PLAYERS; i++)
3834 level->initial_player_gravity[i] =
3835 (header->InitialGravity == 1 ? TRUE : FALSE);
3837 /* skip leading spaces */
3838 for (i = 0; i < SP_LEVEL_NAME_LEN; i++)
3839 if (header->LevelTitle[i] != ' ')
3842 /* copy level title */
3843 for (j = 0; i < SP_LEVEL_NAME_LEN; i++, j++)
3844 level->name[j] = header->LevelTitle[i];
3845 level->name[j] = '\0';
3847 /* cut trailing spaces */
3849 if (level->name[j - 1] == ' ' && level->name[j] == '\0')
3850 level->name[j - 1] = '\0';
3852 level->gems_needed = header->InfotronsNeeded;
3854 for (i = 0; i < header->SpecialPortCount; i++)
3856 SpecialPortType *port = &header->SpecialPort[i];
3857 int port_location = port->PortLocation;
3858 int gravity = port->Gravity;
3859 int port_x, port_y, port_element;
3861 port_x = (port_location / 2) % level->fieldx;
3862 port_y = (port_location / 2) / level->fieldx;
3864 if (port_x < 0 || port_x >= level->fieldx ||
3865 port_y < 0 || port_y >= level->fieldy)
3867 Error(ERR_WARN, "special port position (%d, %d) out of bounds",
3873 port_element = level->field[port_x][port_y];
3875 if (port_element < EL_SP_GRAVITY_PORT_RIGHT ||
3876 port_element > EL_SP_GRAVITY_PORT_UP)
3878 Error(ERR_WARN, "no special port at position (%d, %d)", port_x, port_y);
3883 /* change previous (wrong) gravity inverting special port to either
3884 gravity enabling special port or gravity disabling special port */
3885 level->field[port_x][port_y] +=
3886 (gravity == 1 ? EL_SP_GRAVITY_ON_PORT_RIGHT :
3887 EL_SP_GRAVITY_OFF_PORT_RIGHT) - EL_SP_GRAVITY_PORT_RIGHT;
3890 /* change special gravity ports without database entries to normal ports */
3891 for (x = 0; x < level->fieldx; x++)
3892 for (y = 0; y < level->fieldy; y++)
3893 if (level->field[x][y] >= EL_SP_GRAVITY_PORT_RIGHT &&
3894 level->field[x][y] <= EL_SP_GRAVITY_PORT_UP)
3895 level->field[x][y] += EL_SP_PORT_RIGHT - EL_SP_GRAVITY_PORT_RIGHT;
3897 level->time = 0; /* no time limit */
3898 level->amoeba_speed = 0;
3899 level->time_magic_wall = 0;
3900 level->time_wheel = 0;
3901 level->amoeba_content = EL_EMPTY;
3904 /* original Supaplex does not use score values -- use default values */
3906 for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
3907 level->score[i] = 0;
3910 /* there are no yamyams in supaplex levels */
3911 for (i = 0; i < level->num_yamyam_contents; i++)
3912 for (x = 0; x < 3; x++)
3913 for (y = 0; y < 3; y++)
3914 level->yamyam_content[i].e[x][y] = EL_EMPTY;
3917 static void CopyNativeTape_RND_to_SP(struct LevelInfo *level)
3919 struct LevelInfo_SP *level_sp = level->native_sp_level;
3920 struct DemoInfo_SP *demo = &level_sp->demo;
3923 /* always start with reliable default values */
3924 demo->is_available = FALSE;
3927 if (TAPE_IS_EMPTY(tape))
3930 demo->level_nr = tape.level_nr; /* (currently not used) */
3932 level_sp->header.DemoRandomSeed = tape.random_seed;
3936 for (i = 0; i < tape.length; i++)
3938 int demo_action = map_key_RND_to_SP(tape.pos[i].action[0]);
3939 int demo_repeat = tape.pos[i].delay;
3940 int demo_entries = (demo_repeat + 15) / 16;
3942 if (demo->length + demo_entries >= SP_MAX_TAPE_LEN)
3944 Error(ERR_WARN, "tape truncated: size exceeds maximum SP demo size %d",
3950 for (j = 0; j < demo_repeat / 16; j++)
3951 demo->data[demo->length++] = 0xf0 | demo_action;
3953 if (demo_repeat % 16)
3954 demo->data[demo->length++] = ((demo_repeat % 16 - 1) << 4) | demo_action;
3957 demo->is_available = TRUE;
3960 static void setTapeInfoToDefaults();
3962 static void CopyNativeTape_SP_to_RND(struct LevelInfo *level)
3964 struct LevelInfo_SP *level_sp = level->native_sp_level;
3965 struct DemoInfo_SP *demo = &level_sp->demo;
3966 char *filename = level->file_info.filename;
3969 /* always start with reliable default values */
3970 setTapeInfoToDefaults();
3972 if (!demo->is_available)
3975 tape.level_nr = demo->level_nr; /* (currently not used) */
3976 tape.random_seed = level_sp->header.DemoRandomSeed;
3978 TapeSetDateFromEpochSeconds(getFileTimestampEpochSeconds(filename));
3981 tape.pos[tape.counter].delay = 0;
3983 for (i = 0; i < demo->length; i++)
3985 int demo_action = demo->data[i] & 0x0f;
3986 int demo_repeat = (demo->data[i] & 0xf0) >> 4;
3987 int tape_action = map_key_SP_to_RND(demo_action);
3988 int tape_repeat = demo_repeat + 1;
3989 byte action[MAX_PLAYERS] = { tape_action, 0, 0, 0 };
3990 boolean success = 0;
3993 for (j = 0; j < tape_repeat; j++)
3994 success = TapeAddAction(action);
3998 Error(ERR_WARN, "SP demo truncated: size exceeds maximum tape size %d",
4005 TapeHaltRecording();
4009 /* ------------------------------------------------------------------------- */
4010 /* functions for loading MM level */
4011 /* ------------------------------------------------------------------------- */
4013 void CopyNativeLevel_RND_to_MM(struct LevelInfo *level)
4015 struct LevelInfo_MM *level_mm = level->native_mm_level;
4018 level_mm->fieldx = MIN(level->fieldx, MM_MAX_PLAYFIELD_WIDTH);
4019 level_mm->fieldy = MIN(level->fieldy, MM_MAX_PLAYFIELD_HEIGHT);
4021 level_mm->time = level->time;
4022 level_mm->kettles_needed = level->gems_needed;
4023 level_mm->auto_count_kettles = level->auto_count_gems;
4025 level_mm->laser_red = level->mm_laser_red;
4026 level_mm->laser_green = level->mm_laser_green;
4027 level_mm->laser_blue = level->mm_laser_blue;
4029 strcpy(level_mm->name, level->name);
4030 strcpy(level_mm->author, level->author);
4032 level_mm->score[SC_EMERALD] = level->score[SC_EMERALD];
4033 level_mm->score[SC_PACMAN] = level->score[SC_PACMAN];
4034 level_mm->score[SC_KEY] = level->score[SC_KEY];
4035 level_mm->score[SC_TIME_BONUS] = level->score[SC_TIME_BONUS];
4036 level_mm->score[SC_ELEM_BONUS] = level->score[SC_ELEM_BONUS];
4038 level_mm->amoeba_speed = level->amoeba_speed;
4039 level_mm->time_fuse = level->mm_time_fuse;
4040 level_mm->time_bomb = level->mm_time_bomb;
4041 level_mm->time_ball = level->mm_time_ball;
4042 level_mm->time_block = level->mm_time_block;
4044 for (x = 0; x < level->fieldx; x++)
4045 for (y = 0; y < level->fieldy; y++)
4047 level_mm->field[x][y] = map_element_RND_to_MM(level->field[x][y]);
4050 void CopyNativeLevel_MM_to_RND(struct LevelInfo *level)
4052 struct LevelInfo_MM *level_mm = level->native_mm_level;
4055 level->fieldx = MIN(level_mm->fieldx, MAX_LEV_FIELDX);
4056 level->fieldy = MIN(level_mm->fieldy, MAX_LEV_FIELDY);
4058 level->time = level_mm->time;
4059 level->gems_needed = level_mm->kettles_needed;
4060 level->auto_count_gems = level_mm->auto_count_kettles;
4062 level->mm_laser_red = level_mm->laser_red;
4063 level->mm_laser_green = level_mm->laser_green;
4064 level->mm_laser_blue = level_mm->laser_blue;
4066 strcpy(level->name, level_mm->name);
4068 /* only overwrite author from 'levelinfo.conf' if author defined in level */
4069 if (!strEqual(level_mm->author, ANONYMOUS_NAME))
4070 strcpy(level->author, level_mm->author);
4072 level->score[SC_EMERALD] = level_mm->score[SC_EMERALD];
4073 level->score[SC_PACMAN] = level_mm->score[SC_PACMAN];
4074 level->score[SC_KEY] = level_mm->score[SC_KEY];
4075 level->score[SC_TIME_BONUS] = level_mm->score[SC_TIME_BONUS];
4076 level->score[SC_ELEM_BONUS] = level_mm->score[SC_ELEM_BONUS];
4078 level->amoeba_speed = level_mm->amoeba_speed;
4079 level->mm_time_fuse = level_mm->time_fuse;
4080 level->mm_time_bomb = level_mm->time_bomb;
4081 level->mm_time_ball = level_mm->time_ball;
4082 level->mm_time_block = level_mm->time_block;
4084 for (x = 0; x < level->fieldx; x++)
4085 for (y = 0; y < level->fieldy; y++)
4086 level->field[x][y] = map_element_MM_to_RND(level_mm->field[x][y]);
4090 /* ------------------------------------------------------------------------- */
4091 /* functions for loading DC level */
4092 /* ------------------------------------------------------------------------- */
4094 #define DC_LEVEL_HEADER_SIZE 344
4096 unsigned short getDecodedWord_DC(unsigned short data_encoded, boolean init)
4098 static int last_data_encoded;
4102 int diff_hi, diff_lo;
4103 int data_hi, data_lo;
4104 unsigned short data_decoded;
4108 last_data_encoded = 0;
4115 diff = data_encoded - last_data_encoded;
4116 diff_hi = diff & ~0xff;
4117 diff_lo = diff & 0xff;
4121 data_hi = diff_hi - (offset1 << 8) + (offset2 & 0xff00);
4122 data_lo = (diff_lo + (data_hi >> 16)) & 0x00ff;
4123 data_hi = data_hi & 0xff00;
4125 data_decoded = data_hi | data_lo;
4127 last_data_encoded = data_encoded;
4129 offset1 = (offset1 + 1) % 31;
4130 offset2 = offset2 & 0xff;
4132 return data_decoded;
4135 int getMappedElement_DC(int element)
4143 /* 0x0117 - 0x036e: (?) */
4146 /* 0x042d - 0x0684: (?) */
4162 element = EL_CRYSTAL;
4165 case 0x0e77: /* quicksand (boulder) */
4166 element = EL_QUICKSAND_FAST_FULL;
4169 case 0x0e99: /* slow quicksand (boulder) */
4170 element = EL_QUICKSAND_FULL;
4174 element = EL_EM_EXIT_OPEN;
4178 element = EL_EM_EXIT_CLOSED;
4182 element = EL_EM_STEEL_EXIT_OPEN;
4186 element = EL_EM_STEEL_EXIT_CLOSED;
4189 case 0x0f4f: /* dynamite (lit 1) */
4190 element = EL_EM_DYNAMITE_ACTIVE;
4193 case 0x0f57: /* dynamite (lit 2) */
4194 element = EL_EM_DYNAMITE_ACTIVE;
4197 case 0x0f5f: /* dynamite (lit 3) */
4198 element = EL_EM_DYNAMITE_ACTIVE;
4201 case 0x0f67: /* dynamite (lit 4) */
4202 element = EL_EM_DYNAMITE_ACTIVE;
4209 element = EL_AMOEBA_WET;
4213 element = EL_AMOEBA_DROP;
4217 element = EL_DC_MAGIC_WALL;
4221 element = EL_SPACESHIP_UP;
4225 element = EL_SPACESHIP_DOWN;
4229 element = EL_SPACESHIP_LEFT;
4233 element = EL_SPACESHIP_RIGHT;
4237 element = EL_BUG_UP;
4241 element = EL_BUG_DOWN;
4245 element = EL_BUG_LEFT;
4249 element = EL_BUG_RIGHT;
4253 element = EL_MOLE_UP;
4257 element = EL_MOLE_DOWN;
4261 element = EL_MOLE_LEFT;
4265 element = EL_MOLE_RIGHT;
4273 element = EL_YAMYAM;
4277 element = EL_SWITCHGATE_OPEN;
4281 element = EL_SWITCHGATE_CLOSED;
4285 element = EL_DC_SWITCHGATE_SWITCH_UP;
4289 element = EL_TIMEGATE_CLOSED;
4292 case 0x144c: /* conveyor belt switch (green) */
4293 element = EL_CONVEYOR_BELT_3_SWITCH_MIDDLE;
4296 case 0x144f: /* conveyor belt switch (red) */
4297 element = EL_CONVEYOR_BELT_1_SWITCH_MIDDLE;
4300 case 0x1452: /* conveyor belt switch (blue) */
4301 element = EL_CONVEYOR_BELT_4_SWITCH_MIDDLE;
4305 element = EL_CONVEYOR_BELT_3_MIDDLE;
4309 element = EL_CONVEYOR_BELT_3_LEFT;
4313 element = EL_CONVEYOR_BELT_3_RIGHT;
4317 element = EL_CONVEYOR_BELT_1_MIDDLE;
4321 element = EL_CONVEYOR_BELT_1_LEFT;
4325 element = EL_CONVEYOR_BELT_1_RIGHT;
4329 element = EL_CONVEYOR_BELT_4_MIDDLE;
4333 element = EL_CONVEYOR_BELT_4_LEFT;
4337 element = EL_CONVEYOR_BELT_4_RIGHT;
4341 element = EL_EXPANDABLE_WALL_HORIZONTAL;
4345 element = EL_EXPANDABLE_WALL_VERTICAL;
4349 element = EL_EXPANDABLE_WALL_ANY;
4352 case 0x14ce: /* growing steel wall (left/right) */
4353 element = EL_EXPANDABLE_STEELWALL_HORIZONTAL;
4356 case 0x14df: /* growing steel wall (up/down) */
4357 element = EL_EXPANDABLE_STEELWALL_VERTICAL;
4360 case 0x14e8: /* growing steel wall (up/down/left/right) */
4361 element = EL_EXPANDABLE_STEELWALL_ANY;
4365 element = EL_SHIELD_DEADLY;
4369 element = EL_EXTRA_TIME;
4377 element = EL_EMPTY_SPACE;
4380 case 0x1578: /* quicksand (empty) */
4381 element = EL_QUICKSAND_FAST_EMPTY;
4384 case 0x1579: /* slow quicksand (empty) */
4385 element = EL_QUICKSAND_EMPTY;
4388 /* 0x157c - 0x158b: */
4391 /* 0x1590 - 0x159f: */
4392 /* EL_DC_LANDMINE */
4395 element = EL_EM_DYNAMITE;
4398 case 0x15a1: /* key (red) */
4399 element = EL_EM_KEY_1;
4402 case 0x15a2: /* key (yellow) */
4403 element = EL_EM_KEY_2;
4406 case 0x15a3: /* key (blue) */
4407 element = EL_EM_KEY_4;
4410 case 0x15a4: /* key (green) */
4411 element = EL_EM_KEY_3;
4414 case 0x15a5: /* key (white) */
4415 element = EL_DC_KEY_WHITE;
4419 element = EL_WALL_SLIPPERY;
4426 case 0x15a8: /* wall (not round) */
4430 case 0x15a9: /* (blue) */
4431 element = EL_CHAR_A;
4434 case 0x15aa: /* (blue) */
4435 element = EL_CHAR_B;
4438 case 0x15ab: /* (blue) */
4439 element = EL_CHAR_C;
4442 case 0x15ac: /* (blue) */
4443 element = EL_CHAR_D;
4446 case 0x15ad: /* (blue) */
4447 element = EL_CHAR_E;
4450 case 0x15ae: /* (blue) */
4451 element = EL_CHAR_F;
4454 case 0x15af: /* (blue) */
4455 element = EL_CHAR_G;
4458 case 0x15b0: /* (blue) */
4459 element = EL_CHAR_H;
4462 case 0x15b1: /* (blue) */
4463 element = EL_CHAR_I;
4466 case 0x15b2: /* (blue) */
4467 element = EL_CHAR_J;
4470 case 0x15b3: /* (blue) */
4471 element = EL_CHAR_K;
4474 case 0x15b4: /* (blue) */
4475 element = EL_CHAR_L;
4478 case 0x15b5: /* (blue) */
4479 element = EL_CHAR_M;
4482 case 0x15b6: /* (blue) */
4483 element = EL_CHAR_N;
4486 case 0x15b7: /* (blue) */
4487 element = EL_CHAR_O;
4490 case 0x15b8: /* (blue) */
4491 element = EL_CHAR_P;
4494 case 0x15b9: /* (blue) */
4495 element = EL_CHAR_Q;
4498 case 0x15ba: /* (blue) */
4499 element = EL_CHAR_R;
4502 case 0x15bb: /* (blue) */
4503 element = EL_CHAR_S;
4506 case 0x15bc: /* (blue) */
4507 element = EL_CHAR_T;
4510 case 0x15bd: /* (blue) */
4511 element = EL_CHAR_U;
4514 case 0x15be: /* (blue) */
4515 element = EL_CHAR_V;
4518 case 0x15bf: /* (blue) */
4519 element = EL_CHAR_W;
4522 case 0x15c0: /* (blue) */
4523 element = EL_CHAR_X;
4526 case 0x15c1: /* (blue) */
4527 element = EL_CHAR_Y;
4530 case 0x15c2: /* (blue) */
4531 element = EL_CHAR_Z;
4534 case 0x15c3: /* (blue) */
4535 element = EL_CHAR_AUMLAUT;
4538 case 0x15c4: /* (blue) */
4539 element = EL_CHAR_OUMLAUT;
4542 case 0x15c5: /* (blue) */
4543 element = EL_CHAR_UUMLAUT;
4546 case 0x15c6: /* (blue) */
4547 element = EL_CHAR_0;
4550 case 0x15c7: /* (blue) */
4551 element = EL_CHAR_1;
4554 case 0x15c8: /* (blue) */
4555 element = EL_CHAR_2;
4558 case 0x15c9: /* (blue) */
4559 element = EL_CHAR_3;
4562 case 0x15ca: /* (blue) */
4563 element = EL_CHAR_4;
4566 case 0x15cb: /* (blue) */
4567 element = EL_CHAR_5;
4570 case 0x15cc: /* (blue) */
4571 element = EL_CHAR_6;
4574 case 0x15cd: /* (blue) */
4575 element = EL_CHAR_7;
4578 case 0x15ce: /* (blue) */
4579 element = EL_CHAR_8;
4582 case 0x15cf: /* (blue) */
4583 element = EL_CHAR_9;
4586 case 0x15d0: /* (blue) */
4587 element = EL_CHAR_PERIOD;
4590 case 0x15d1: /* (blue) */
4591 element = EL_CHAR_EXCLAM;
4594 case 0x15d2: /* (blue) */
4595 element = EL_CHAR_COLON;
4598 case 0x15d3: /* (blue) */
4599 element = EL_CHAR_LESS;
4602 case 0x15d4: /* (blue) */
4603 element = EL_CHAR_GREATER;
4606 case 0x15d5: /* (blue) */
4607 element = EL_CHAR_QUESTION;
4610 case 0x15d6: /* (blue) */
4611 element = EL_CHAR_COPYRIGHT;
4614 case 0x15d7: /* (blue) */
4615 element = EL_CHAR_UP;
4618 case 0x15d8: /* (blue) */
4619 element = EL_CHAR_DOWN;
4622 case 0x15d9: /* (blue) */
4623 element = EL_CHAR_BUTTON;
4626 case 0x15da: /* (blue) */
4627 element = EL_CHAR_PLUS;
4630 case 0x15db: /* (blue) */
4631 element = EL_CHAR_MINUS;
4634 case 0x15dc: /* (blue) */
4635 element = EL_CHAR_APOSTROPHE;
4638 case 0x15dd: /* (blue) */
4639 element = EL_CHAR_PARENLEFT;
4642 case 0x15de: /* (blue) */
4643 element = EL_CHAR_PARENRIGHT;
4646 case 0x15df: /* (green) */
4647 element = EL_CHAR_A;
4650 case 0x15e0: /* (green) */
4651 element = EL_CHAR_B;
4654 case 0x15e1: /* (green) */
4655 element = EL_CHAR_C;
4658 case 0x15e2: /* (green) */
4659 element = EL_CHAR_D;
4662 case 0x15e3: /* (green) */
4663 element = EL_CHAR_E;
4666 case 0x15e4: /* (green) */
4667 element = EL_CHAR_F;
4670 case 0x15e5: /* (green) */
4671 element = EL_CHAR_G;
4674 case 0x15e6: /* (green) */
4675 element = EL_CHAR_H;
4678 case 0x15e7: /* (green) */
4679 element = EL_CHAR_I;
4682 case 0x15e8: /* (green) */
4683 element = EL_CHAR_J;
4686 case 0x15e9: /* (green) */
4687 element = EL_CHAR_K;
4690 case 0x15ea: /* (green) */
4691 element = EL_CHAR_L;
4694 case 0x15eb: /* (green) */
4695 element = EL_CHAR_M;
4698 case 0x15ec: /* (green) */
4699 element = EL_CHAR_N;
4702 case 0x15ed: /* (green) */
4703 element = EL_CHAR_O;
4706 case 0x15ee: /* (green) */
4707 element = EL_CHAR_P;
4710 case 0x15ef: /* (green) */
4711 element = EL_CHAR_Q;
4714 case 0x15f0: /* (green) */
4715 element = EL_CHAR_R;
4718 case 0x15f1: /* (green) */
4719 element = EL_CHAR_S;
4722 case 0x15f2: /* (green) */
4723 element = EL_CHAR_T;
4726 case 0x15f3: /* (green) */
4727 element = EL_CHAR_U;
4730 case 0x15f4: /* (green) */
4731 element = EL_CHAR_V;
4734 case 0x15f5: /* (green) */
4735 element = EL_CHAR_W;
4738 case 0x15f6: /* (green) */
4739 element = EL_CHAR_X;
4742 case 0x15f7: /* (green) */
4743 element = EL_CHAR_Y;
4746 case 0x15f8: /* (green) */
4747 element = EL_CHAR_Z;
4750 case 0x15f9: /* (green) */
4751 element = EL_CHAR_AUMLAUT;
4754 case 0x15fa: /* (green) */
4755 element = EL_CHAR_OUMLAUT;
4758 case 0x15fb: /* (green) */
4759 element = EL_CHAR_UUMLAUT;
4762 case 0x15fc: /* (green) */
4763 element = EL_CHAR_0;
4766 case 0x15fd: /* (green) */
4767 element = EL_CHAR_1;
4770 case 0x15fe: /* (green) */
4771 element = EL_CHAR_2;
4774 case 0x15ff: /* (green) */
4775 element = EL_CHAR_3;
4778 case 0x1600: /* (green) */
4779 element = EL_CHAR_4;
4782 case 0x1601: /* (green) */
4783 element = EL_CHAR_5;
4786 case 0x1602: /* (green) */
4787 element = EL_CHAR_6;
4790 case 0x1603: /* (green) */
4791 element = EL_CHAR_7;
4794 case 0x1604: /* (green) */
4795 element = EL_CHAR_8;
4798 case 0x1605: /* (green) */
4799 element = EL_CHAR_9;
4802 case 0x1606: /* (green) */
4803 element = EL_CHAR_PERIOD;
4806 case 0x1607: /* (green) */
4807 element = EL_CHAR_EXCLAM;
4810 case 0x1608: /* (green) */
4811 element = EL_CHAR_COLON;
4814 case 0x1609: /* (green) */
4815 element = EL_CHAR_LESS;
4818 case 0x160a: /* (green) */
4819 element = EL_CHAR_GREATER;
4822 case 0x160b: /* (green) */
4823 element = EL_CHAR_QUESTION;
4826 case 0x160c: /* (green) */
4827 element = EL_CHAR_COPYRIGHT;
4830 case 0x160d: /* (green) */
4831 element = EL_CHAR_UP;
4834 case 0x160e: /* (green) */
4835 element = EL_CHAR_DOWN;
4838 case 0x160f: /* (green) */
4839 element = EL_CHAR_BUTTON;
4842 case 0x1610: /* (green) */
4843 element = EL_CHAR_PLUS;
4846 case 0x1611: /* (green) */
4847 element = EL_CHAR_MINUS;
4850 case 0x1612: /* (green) */
4851 element = EL_CHAR_APOSTROPHE;
4854 case 0x1613: /* (green) */
4855 element = EL_CHAR_PARENLEFT;
4858 case 0x1614: /* (green) */
4859 element = EL_CHAR_PARENRIGHT;
4862 case 0x1615: /* (blue steel) */
4863 element = EL_STEEL_CHAR_A;
4866 case 0x1616: /* (blue steel) */
4867 element = EL_STEEL_CHAR_B;
4870 case 0x1617: /* (blue steel) */
4871 element = EL_STEEL_CHAR_C;
4874 case 0x1618: /* (blue steel) */
4875 element = EL_STEEL_CHAR_D;
4878 case 0x1619: /* (blue steel) */
4879 element = EL_STEEL_CHAR_E;
4882 case 0x161a: /* (blue steel) */
4883 element = EL_STEEL_CHAR_F;
4886 case 0x161b: /* (blue steel) */
4887 element = EL_STEEL_CHAR_G;
4890 case 0x161c: /* (blue steel) */
4891 element = EL_STEEL_CHAR_H;
4894 case 0x161d: /* (blue steel) */
4895 element = EL_STEEL_CHAR_I;
4898 case 0x161e: /* (blue steel) */
4899 element = EL_STEEL_CHAR_J;
4902 case 0x161f: /* (blue steel) */
4903 element = EL_STEEL_CHAR_K;
4906 case 0x1620: /* (blue steel) */
4907 element = EL_STEEL_CHAR_L;
4910 case 0x1621: /* (blue steel) */
4911 element = EL_STEEL_CHAR_M;
4914 case 0x1622: /* (blue steel) */
4915 element = EL_STEEL_CHAR_N;
4918 case 0x1623: /* (blue steel) */
4919 element = EL_STEEL_CHAR_O;
4922 case 0x1624: /* (blue steel) */
4923 element = EL_STEEL_CHAR_P;
4926 case 0x1625: /* (blue steel) */
4927 element = EL_STEEL_CHAR_Q;
4930 case 0x1626: /* (blue steel) */
4931 element = EL_STEEL_CHAR_R;
4934 case 0x1627: /* (blue steel) */
4935 element = EL_STEEL_CHAR_S;
4938 case 0x1628: /* (blue steel) */
4939 element = EL_STEEL_CHAR_T;
4942 case 0x1629: /* (blue steel) */
4943 element = EL_STEEL_CHAR_U;
4946 case 0x162a: /* (blue steel) */
4947 element = EL_STEEL_CHAR_V;
4950 case 0x162b: /* (blue steel) */
4951 element = EL_STEEL_CHAR_W;
4954 case 0x162c: /* (blue steel) */
4955 element = EL_STEEL_CHAR_X;
4958 case 0x162d: /* (blue steel) */
4959 element = EL_STEEL_CHAR_Y;
4962 case 0x162e: /* (blue steel) */
4963 element = EL_STEEL_CHAR_Z;
4966 case 0x162f: /* (blue steel) */
4967 element = EL_STEEL_CHAR_AUMLAUT;
4970 case 0x1630: /* (blue steel) */
4971 element = EL_STEEL_CHAR_OUMLAUT;
4974 case 0x1631: /* (blue steel) */
4975 element = EL_STEEL_CHAR_UUMLAUT;
4978 case 0x1632: /* (blue steel) */
4979 element = EL_STEEL_CHAR_0;
4982 case 0x1633: /* (blue steel) */
4983 element = EL_STEEL_CHAR_1;
4986 case 0x1634: /* (blue steel) */
4987 element = EL_STEEL_CHAR_2;
4990 case 0x1635: /* (blue steel) */
4991 element = EL_STEEL_CHAR_3;
4994 case 0x1636: /* (blue steel) */
4995 element = EL_STEEL_CHAR_4;
4998 case 0x1637: /* (blue steel) */
4999 element = EL_STEEL_CHAR_5;
5002 case 0x1638: /* (blue steel) */
5003 element = EL_STEEL_CHAR_6;
5006 case 0x1639: /* (blue steel) */
5007 element = EL_STEEL_CHAR_7;
5010 case 0x163a: /* (blue steel) */
5011 element = EL_STEEL_CHAR_8;
5014 case 0x163b: /* (blue steel) */
5015 element = EL_STEEL_CHAR_9;
5018 case 0x163c: /* (blue steel) */
5019 element = EL_STEEL_CHAR_PERIOD;
5022 case 0x163d: /* (blue steel) */
5023 element = EL_STEEL_CHAR_EXCLAM;
5026 case 0x163e: /* (blue steel) */
5027 element = EL_STEEL_CHAR_COLON;
5030 case 0x163f: /* (blue steel) */
5031 element = EL_STEEL_CHAR_LESS;
5034 case 0x1640: /* (blue steel) */
5035 element = EL_STEEL_CHAR_GREATER;
5038 case 0x1641: /* (blue steel) */
5039 element = EL_STEEL_CHAR_QUESTION;
5042 case 0x1642: /* (blue steel) */
5043 element = EL_STEEL_CHAR_COPYRIGHT;
5046 case 0x1643: /* (blue steel) */
5047 element = EL_STEEL_CHAR_UP;
5050 case 0x1644: /* (blue steel) */
5051 element = EL_STEEL_CHAR_DOWN;
5054 case 0x1645: /* (blue steel) */
5055 element = EL_STEEL_CHAR_BUTTON;
5058 case 0x1646: /* (blue steel) */
5059 element = EL_STEEL_CHAR_PLUS;
5062 case 0x1647: /* (blue steel) */
5063 element = EL_STEEL_CHAR_MINUS;
5066 case 0x1648: /* (blue steel) */
5067 element = EL_STEEL_CHAR_APOSTROPHE;
5070 case 0x1649: /* (blue steel) */
5071 element = EL_STEEL_CHAR_PARENLEFT;
5074 case 0x164a: /* (blue steel) */
5075 element = EL_STEEL_CHAR_PARENRIGHT;
5078 case 0x164b: /* (green steel) */
5079 element = EL_STEEL_CHAR_A;
5082 case 0x164c: /* (green steel) */
5083 element = EL_STEEL_CHAR_B;
5086 case 0x164d: /* (green steel) */
5087 element = EL_STEEL_CHAR_C;
5090 case 0x164e: /* (green steel) */
5091 element = EL_STEEL_CHAR_D;
5094 case 0x164f: /* (green steel) */
5095 element = EL_STEEL_CHAR_E;
5098 case 0x1650: /* (green steel) */
5099 element = EL_STEEL_CHAR_F;
5102 case 0x1651: /* (green steel) */
5103 element = EL_STEEL_CHAR_G;
5106 case 0x1652: /* (green steel) */
5107 element = EL_STEEL_CHAR_H;
5110 case 0x1653: /* (green steel) */
5111 element = EL_STEEL_CHAR_I;
5114 case 0x1654: /* (green steel) */
5115 element = EL_STEEL_CHAR_J;
5118 case 0x1655: /* (green steel) */
5119 element = EL_STEEL_CHAR_K;
5122 case 0x1656: /* (green steel) */
5123 element = EL_STEEL_CHAR_L;
5126 case 0x1657: /* (green steel) */
5127 element = EL_STEEL_CHAR_M;
5130 case 0x1658: /* (green steel) */
5131 element = EL_STEEL_CHAR_N;
5134 case 0x1659: /* (green steel) */
5135 element = EL_STEEL_CHAR_O;
5138 case 0x165a: /* (green steel) */
5139 element = EL_STEEL_CHAR_P;
5142 case 0x165b: /* (green steel) */
5143 element = EL_STEEL_CHAR_Q;
5146 case 0x165c: /* (green steel) */
5147 element = EL_STEEL_CHAR_R;
5150 case 0x165d: /* (green steel) */
5151 element = EL_STEEL_CHAR_S;
5154 case 0x165e: /* (green steel) */
5155 element = EL_STEEL_CHAR_T;
5158 case 0x165f: /* (green steel) */
5159 element = EL_STEEL_CHAR_U;
5162 case 0x1660: /* (green steel) */
5163 element = EL_STEEL_CHAR_V;
5166 case 0x1661: /* (green steel) */
5167 element = EL_STEEL_CHAR_W;
5170 case 0x1662: /* (green steel) */
5171 element = EL_STEEL_CHAR_X;
5174 case 0x1663: /* (green steel) */
5175 element = EL_STEEL_CHAR_Y;
5178 case 0x1664: /* (green steel) */
5179 element = EL_STEEL_CHAR_Z;
5182 case 0x1665: /* (green steel) */
5183 element = EL_STEEL_CHAR_AUMLAUT;
5186 case 0x1666: /* (green steel) */
5187 element = EL_STEEL_CHAR_OUMLAUT;
5190 case 0x1667: /* (green steel) */
5191 element = EL_STEEL_CHAR_UUMLAUT;
5194 case 0x1668: /* (green steel) */
5195 element = EL_STEEL_CHAR_0;
5198 case 0x1669: /* (green steel) */
5199 element = EL_STEEL_CHAR_1;
5202 case 0x166a: /* (green steel) */
5203 element = EL_STEEL_CHAR_2;
5206 case 0x166b: /* (green steel) */
5207 element = EL_STEEL_CHAR_3;
5210 case 0x166c: /* (green steel) */
5211 element = EL_STEEL_CHAR_4;
5214 case 0x166d: /* (green steel) */
5215 element = EL_STEEL_CHAR_5;
5218 case 0x166e: /* (green steel) */
5219 element = EL_STEEL_CHAR_6;
5222 case 0x166f: /* (green steel) */
5223 element = EL_STEEL_CHAR_7;
5226 case 0x1670: /* (green steel) */
5227 element = EL_STEEL_CHAR_8;
5230 case 0x1671: /* (green steel) */
5231 element = EL_STEEL_CHAR_9;
5234 case 0x1672: /* (green steel) */
5235 element = EL_STEEL_CHAR_PERIOD;
5238 case 0x1673: /* (green steel) */
5239 element = EL_STEEL_CHAR_EXCLAM;
5242 case 0x1674: /* (green steel) */
5243 element = EL_STEEL_CHAR_COLON;
5246 case 0x1675: /* (green steel) */
5247 element = EL_STEEL_CHAR_LESS;
5250 case 0x1676: /* (green steel) */
5251 element = EL_STEEL_CHAR_GREATER;
5254 case 0x1677: /* (green steel) */
5255 element = EL_STEEL_CHAR_QUESTION;
5258 case 0x1678: /* (green steel) */
5259 element = EL_STEEL_CHAR_COPYRIGHT;
5262 case 0x1679: /* (green steel) */
5263 element = EL_STEEL_CHAR_UP;
5266 case 0x167a: /* (green steel) */
5267 element = EL_STEEL_CHAR_DOWN;
5270 case 0x167b: /* (green steel) */
5271 element = EL_STEEL_CHAR_BUTTON;
5274 case 0x167c: /* (green steel) */
5275 element = EL_STEEL_CHAR_PLUS;
5278 case 0x167d: /* (green steel) */
5279 element = EL_STEEL_CHAR_MINUS;
5282 case 0x167e: /* (green steel) */
5283 element = EL_STEEL_CHAR_APOSTROPHE;
5286 case 0x167f: /* (green steel) */
5287 element = EL_STEEL_CHAR_PARENLEFT;
5290 case 0x1680: /* (green steel) */
5291 element = EL_STEEL_CHAR_PARENRIGHT;
5294 case 0x1681: /* gate (red) */
5295 element = EL_EM_GATE_1;
5298 case 0x1682: /* secret gate (red) */
5299 element = EL_GATE_1_GRAY;
5302 case 0x1683: /* gate (yellow) */
5303 element = EL_EM_GATE_2;
5306 case 0x1684: /* secret gate (yellow) */
5307 element = EL_GATE_2_GRAY;
5310 case 0x1685: /* gate (blue) */
5311 element = EL_EM_GATE_4;
5314 case 0x1686: /* secret gate (blue) */
5315 element = EL_GATE_4_GRAY;
5318 case 0x1687: /* gate (green) */
5319 element = EL_EM_GATE_3;
5322 case 0x1688: /* secret gate (green) */
5323 element = EL_GATE_3_GRAY;
5326 case 0x1689: /* gate (white) */
5327 element = EL_DC_GATE_WHITE;
5330 case 0x168a: /* secret gate (white) */
5331 element = EL_DC_GATE_WHITE_GRAY;
5334 case 0x168b: /* secret gate (no key) */
5335 element = EL_DC_GATE_FAKE_GRAY;
5339 element = EL_ROBOT_WHEEL;
5343 element = EL_DC_TIMEGATE_SWITCH;
5347 element = EL_ACID_POOL_BOTTOM;
5351 element = EL_ACID_POOL_TOPLEFT;
5355 element = EL_ACID_POOL_TOPRIGHT;
5359 element = EL_ACID_POOL_BOTTOMLEFT;
5363 element = EL_ACID_POOL_BOTTOMRIGHT;
5367 element = EL_STEELWALL;
5371 element = EL_STEELWALL_SLIPPERY;
5374 case 0x1695: /* steel wall (not round) */
5375 element = EL_STEELWALL;
5378 case 0x1696: /* steel wall (left) */
5379 element = EL_DC_STEELWALL_1_LEFT;
5382 case 0x1697: /* steel wall (bottom) */
5383 element = EL_DC_STEELWALL_1_BOTTOM;
5386 case 0x1698: /* steel wall (right) */
5387 element = EL_DC_STEELWALL_1_RIGHT;
5390 case 0x1699: /* steel wall (top) */
5391 element = EL_DC_STEELWALL_1_TOP;
5394 case 0x169a: /* steel wall (left/bottom) */
5395 element = EL_DC_STEELWALL_1_BOTTOMLEFT;
5398 case 0x169b: /* steel wall (right/bottom) */
5399 element = EL_DC_STEELWALL_1_BOTTOMRIGHT;
5402 case 0x169c: /* steel wall (right/top) */
5403 element = EL_DC_STEELWALL_1_TOPRIGHT;
5406 case 0x169d: /* steel wall (left/top) */
5407 element = EL_DC_STEELWALL_1_TOPLEFT;
5410 case 0x169e: /* steel wall (right/bottom small) */
5411 element = EL_DC_STEELWALL_1_BOTTOMRIGHT_2;
5414 case 0x169f: /* steel wall (left/bottom small) */
5415 element = EL_DC_STEELWALL_1_BOTTOMLEFT_2;
5418 case 0x16a0: /* steel wall (right/top small) */
5419 element = EL_DC_STEELWALL_1_TOPRIGHT_2;
5422 case 0x16a1: /* steel wall (left/top small) */
5423 element = EL_DC_STEELWALL_1_TOPLEFT_2;
5426 case 0x16a2: /* steel wall (left/right) */
5427 element = EL_DC_STEELWALL_1_VERTICAL;
5430 case 0x16a3: /* steel wall (top/bottom) */
5431 element = EL_DC_STEELWALL_1_HORIZONTAL;
5434 case 0x16a4: /* steel wall 2 (left end) */
5435 element = EL_DC_STEELWALL_2_LEFT;
5438 case 0x16a5: /* steel wall 2 (right end) */
5439 element = EL_DC_STEELWALL_2_RIGHT;
5442 case 0x16a6: /* steel wall 2 (top end) */
5443 element = EL_DC_STEELWALL_2_TOP;
5446 case 0x16a7: /* steel wall 2 (bottom end) */
5447 element = EL_DC_STEELWALL_2_BOTTOM;
5450 case 0x16a8: /* steel wall 2 (left/right) */
5451 element = EL_DC_STEELWALL_2_HORIZONTAL;
5454 case 0x16a9: /* steel wall 2 (up/down) */
5455 element = EL_DC_STEELWALL_2_VERTICAL;
5458 case 0x16aa: /* steel wall 2 (mid) */
5459 element = EL_DC_STEELWALL_2_MIDDLE;
5463 element = EL_SIGN_EXCLAMATION;
5467 element = EL_SIGN_RADIOACTIVITY;
5471 element = EL_SIGN_STOP;
5475 element = EL_SIGN_WHEELCHAIR;
5479 element = EL_SIGN_PARKING;
5483 element = EL_SIGN_NO_ENTRY;
5487 element = EL_SIGN_HEART;
5491 element = EL_SIGN_GIVE_WAY;
5495 element = EL_SIGN_ENTRY_FORBIDDEN;
5499 element = EL_SIGN_EMERGENCY_EXIT;
5503 element = EL_SIGN_YIN_YANG;
5507 element = EL_WALL_EMERALD;
5511 element = EL_WALL_DIAMOND;
5515 element = EL_WALL_PEARL;
5519 element = EL_WALL_CRYSTAL;
5523 element = EL_INVISIBLE_WALL;
5527 element = EL_INVISIBLE_STEELWALL;
5530 /* 0x16bc - 0x16cb: */
5531 /* EL_INVISIBLE_SAND */
5534 element = EL_LIGHT_SWITCH;
5538 element = EL_ENVELOPE_1;
5542 if (element >= 0x0117 && element <= 0x036e) /* (?) */
5543 element = EL_DIAMOND;
5544 else if (element >= 0x042d && element <= 0x0684) /* (?) */
5545 element = EL_EMERALD;
5546 else if (element >= 0x157c && element <= 0x158b)
5548 else if (element >= 0x1590 && element <= 0x159f)
5549 element = EL_DC_LANDMINE;
5550 else if (element >= 0x16bc && element <= 0x16cb)
5551 element = EL_INVISIBLE_SAND;
5554 Error(ERR_WARN, "unknown Diamond Caves element 0x%04x", element);
5555 element = EL_UNKNOWN;
5560 return getMappedElement(element);
5563 static void LoadLevelFromFileStream_DC(File *file, struct LevelInfo *level,
5566 byte header[DC_LEVEL_HEADER_SIZE];
5568 int envelope_header_pos = 62;
5569 int envelope_content_pos = 94;
5570 int level_name_pos = 251;
5571 int level_author_pos = 292;
5572 int envelope_header_len;
5573 int envelope_content_len;
5575 int level_author_len;
5577 int num_yamyam_contents;
5580 getDecodedWord_DC(0, TRUE); /* initialize DC2 decoding engine */
5582 for (i = 0; i < DC_LEVEL_HEADER_SIZE / 2; i++)
5584 unsigned short header_word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
5586 header[i * 2 + 0] = header_word >> 8;
5587 header[i * 2 + 1] = header_word & 0xff;
5590 /* read some values from level header to check level decoding integrity */
5591 fieldx = header[6] | (header[7] << 8);
5592 fieldy = header[8] | (header[9] << 8);
5593 num_yamyam_contents = header[60] | (header[61] << 8);
5595 /* do some simple sanity checks to ensure that level was correctly decoded */
5596 if (fieldx < 1 || fieldx > 256 ||
5597 fieldy < 1 || fieldy > 256 ||
5598 num_yamyam_contents < 1 || num_yamyam_contents > 8)
5600 level->no_valid_file = TRUE;
5602 Error(ERR_WARN, "cannot decode level from stream -- using empty level");
5607 /* maximum envelope header size is 31 bytes */
5608 envelope_header_len = header[envelope_header_pos];
5609 /* maximum envelope content size is 110 (156?) bytes */
5610 envelope_content_len = header[envelope_content_pos];
5612 /* maximum level title size is 40 bytes */
5613 level_name_len = MIN(header[level_name_pos], MAX_LEVEL_NAME_LEN);
5614 /* maximum level author size is 30 (51?) bytes */
5615 level_author_len = MIN(header[level_author_pos], MAX_LEVEL_AUTHOR_LEN);
5619 for (i = 0; i < envelope_header_len; i++)
5620 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
5621 level->envelope[0].text[envelope_size++] =
5622 header[envelope_header_pos + 1 + i];
5624 if (envelope_header_len > 0 && envelope_content_len > 0)
5626 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
5627 level->envelope[0].text[envelope_size++] = '\n';
5628 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
5629 level->envelope[0].text[envelope_size++] = '\n';
5632 for (i = 0; i < envelope_content_len; i++)
5633 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
5634 level->envelope[0].text[envelope_size++] =
5635 header[envelope_content_pos + 1 + i];
5637 level->envelope[0].text[envelope_size] = '\0';
5639 level->envelope[0].xsize = MAX_ENVELOPE_XSIZE;
5640 level->envelope[0].ysize = 10;
5641 level->envelope[0].autowrap = TRUE;
5642 level->envelope[0].centered = TRUE;
5644 for (i = 0; i < level_name_len; i++)
5645 level->name[i] = header[level_name_pos + 1 + i];
5646 level->name[level_name_len] = '\0';
5648 for (i = 0; i < level_author_len; i++)
5649 level->author[i] = header[level_author_pos + 1 + i];
5650 level->author[level_author_len] = '\0';
5652 num_yamyam_contents = header[60] | (header[61] << 8);
5653 level->num_yamyam_contents =
5654 MIN(MAX(MIN_ELEMENT_CONTENTS, num_yamyam_contents), MAX_ELEMENT_CONTENTS);
5656 for (i = 0; i < num_yamyam_contents; i++)
5658 for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
5660 unsigned short word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
5661 int element_dc = ((word & 0xff) << 8) | ((word >> 8) & 0xff);
5663 if (i < MAX_ELEMENT_CONTENTS)
5664 level->yamyam_content[i].e[x][y] = getMappedElement_DC(element_dc);
5668 fieldx = header[6] | (header[7] << 8);
5669 fieldy = header[8] | (header[9] << 8);
5670 level->fieldx = MIN(MAX(MIN_LEV_FIELDX, fieldx), MAX_LEV_FIELDX);
5671 level->fieldy = MIN(MAX(MIN_LEV_FIELDY, fieldy), MAX_LEV_FIELDY);
5673 for (y = 0; y < fieldy; y++) for (x = 0; x < fieldx; x++)
5675 unsigned short word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
5676 int element_dc = ((word & 0xff) << 8) | ((word >> 8) & 0xff);
5678 if (x < MAX_LEV_FIELDX && y < MAX_LEV_FIELDY)
5679 level->field[x][y] = getMappedElement_DC(element_dc);
5682 x = MIN(MAX(0, (header[10] | (header[11] << 8)) - 1), MAX_LEV_FIELDX - 1);
5683 y = MIN(MAX(0, (header[12] | (header[13] << 8)) - 1), MAX_LEV_FIELDY - 1);
5684 level->field[x][y] = EL_PLAYER_1;
5686 x = MIN(MAX(0, (header[14] | (header[15] << 8)) - 1), MAX_LEV_FIELDX - 1);
5687 y = MIN(MAX(0, (header[16] | (header[17] << 8)) - 1), MAX_LEV_FIELDY - 1);
5688 level->field[x][y] = EL_PLAYER_2;
5690 level->gems_needed = header[18] | (header[19] << 8);
5692 level->score[SC_EMERALD] = header[20] | (header[21] << 8);
5693 level->score[SC_DIAMOND] = header[22] | (header[23] << 8);
5694 level->score[SC_PEARL] = header[24] | (header[25] << 8);
5695 level->score[SC_CRYSTAL] = header[26] | (header[27] << 8);
5696 level->score[SC_NUT] = header[28] | (header[29] << 8);
5697 level->score[SC_ROBOT] = header[30] | (header[31] << 8);
5698 level->score[SC_SPACESHIP] = header[32] | (header[33] << 8);
5699 level->score[SC_BUG] = header[34] | (header[35] << 8);
5700 level->score[SC_YAMYAM] = header[36] | (header[37] << 8);
5701 level->score[SC_DYNAMITE] = header[38] | (header[39] << 8);
5702 level->score[SC_KEY] = header[40] | (header[41] << 8);
5703 level->score[SC_TIME_BONUS] = header[42] | (header[43] << 8);
5705 level->time = header[44] | (header[45] << 8);
5707 level->amoeba_speed = header[46] | (header[47] << 8);
5708 level->time_light = header[48] | (header[49] << 8);
5709 level->time_timegate = header[50] | (header[51] << 8);
5710 level->time_wheel = header[52] | (header[53] << 8);
5711 level->time_magic_wall = header[54] | (header[55] << 8);
5712 level->extra_time = header[56] | (header[57] << 8);
5713 level->shield_normal_time = header[58] | (header[59] << 8);
5715 /* Diamond Caves has the same (strange) behaviour as Emerald Mine that gems
5716 can slip down from flat walls, like normal walls and steel walls */
5717 level->em_slippery_gems = TRUE;
5720 static void LoadLevelFromFileInfo_DC(struct LevelInfo *level,
5721 struct LevelFileInfo *level_file_info,
5722 boolean level_info_only)
5724 char *filename = level_file_info->filename;
5726 int num_magic_bytes = 8;
5727 char magic_bytes[num_magic_bytes + 1];
5728 int num_levels_to_skip = level_file_info->nr - leveldir_current->first_level;
5730 if (!(file = openFile(filename, MODE_READ)))
5732 level->no_valid_file = TRUE;
5734 if (!level_info_only)
5735 Error(ERR_WARN, "cannot read level '%s' -- using empty level", filename);
5740 // fseek(file, 0x0000, SEEK_SET);
5742 if (level_file_info->packed)
5744 /* read "magic bytes" from start of file */
5745 if (getStringFromFile(file, magic_bytes, num_magic_bytes + 1) == NULL)
5746 magic_bytes[0] = '\0';
5748 /* check "magic bytes" for correct file format */
5749 if (!strPrefix(magic_bytes, "DC2"))
5751 level->no_valid_file = TRUE;
5753 Error(ERR_WARN, "unknown DC level file '%s' -- using empty level",
5759 if (strPrefix(magic_bytes, "DC2Win95") ||
5760 strPrefix(magic_bytes, "DC2Win98"))
5762 int position_first_level = 0x00fa;
5763 int extra_bytes = 4;
5766 /* advance file stream to first level inside the level package */
5767 skip_bytes = position_first_level - num_magic_bytes - extra_bytes;
5769 /* each block of level data is followed by block of non-level data */
5770 num_levels_to_skip *= 2;
5772 /* at least skip header bytes, therefore use ">= 0" instead of "> 0" */
5773 while (num_levels_to_skip >= 0)
5775 /* advance file stream to next level inside the level package */
5776 if (seekFile(file, skip_bytes, SEEK_CUR) != 0)
5778 level->no_valid_file = TRUE;
5780 Error(ERR_WARN, "cannot fseek in file '%s' -- using empty level",
5786 /* skip apparently unused extra bytes following each level */
5787 ReadUnusedBytesFromFile(file, extra_bytes);
5789 /* read size of next level in level package */
5790 skip_bytes = getFile32BitLE(file);
5792 num_levels_to_skip--;
5797 level->no_valid_file = TRUE;
5799 Error(ERR_WARN, "unknown DC2 level file '%s' -- using empty level",
5806 LoadLevelFromFileStream_DC(file, level, level_file_info->nr);
5812 /* ------------------------------------------------------------------------- */
5813 /* functions for loading SB level */
5814 /* ------------------------------------------------------------------------- */
5816 int getMappedElement_SB(int element_ascii, boolean use_ces)
5824 sb_element_mapping[] =
5826 { ' ', EL_EMPTY, EL_CUSTOM_1 }, /* floor (space) */
5827 { '#', EL_STEELWALL, EL_CUSTOM_2 }, /* wall */
5828 { '@', EL_PLAYER_1, EL_CUSTOM_3 }, /* player */
5829 { '$', EL_SOKOBAN_OBJECT, EL_CUSTOM_4 }, /* box */
5830 { '.', EL_SOKOBAN_FIELD_EMPTY, EL_CUSTOM_5 }, /* goal square */
5831 { '*', EL_SOKOBAN_FIELD_FULL, EL_CUSTOM_6 }, /* box on goal square */
5832 { '+', EL_SOKOBAN_FIELD_PLAYER, EL_CUSTOM_7 }, /* player on goal square */
5833 { '_', EL_INVISIBLE_STEELWALL, EL_FROM_LEVEL_TEMPLATE }, /* floor beyond border */
5840 for (i = 0; sb_element_mapping[i].ascii != 0; i++)
5841 if (element_ascii == sb_element_mapping[i].ascii)
5842 return (use_ces ? sb_element_mapping[i].ce : sb_element_mapping[i].sb);
5844 return EL_UNDEFINED;
5847 static void LoadLevelFromFileInfo_SB(struct LevelInfo *level,
5848 struct LevelFileInfo *level_file_info,
5849 boolean level_info_only)
5851 char *filename = level_file_info->filename;
5852 char line[MAX_LINE_LEN], line_raw[MAX_LINE_LEN], previous_line[MAX_LINE_LEN];
5853 char last_comment[MAX_LINE_LEN];
5854 char level_name[MAX_LINE_LEN];
5857 int num_levels_to_skip = level_file_info->nr - leveldir_current->first_level;
5858 boolean read_continued_line = FALSE;
5859 boolean reading_playfield = FALSE;
5860 boolean got_valid_playfield_line = FALSE;
5861 boolean invalid_playfield_char = FALSE;
5862 boolean load_xsb_to_ces = check_special_flags("load_xsb_to_ces");
5863 int file_level_nr = 0;
5865 int x = 0, y = 0; /* initialized to make compilers happy */
5867 last_comment[0] = '\0';
5868 level_name[0] = '\0';
5870 if (!(file = openFile(filename, MODE_READ)))
5872 level->no_valid_file = TRUE;
5874 if (!level_info_only)
5875 Error(ERR_WARN, "cannot read level '%s' -- using empty level", filename);
5880 while (!checkEndOfFile(file))
5882 /* level successfully read, but next level may follow here */
5883 if (!got_valid_playfield_line && reading_playfield)
5885 /* read playfield from single level file -- skip remaining file */
5886 if (!level_file_info->packed)
5889 if (file_level_nr >= num_levels_to_skip)
5894 last_comment[0] = '\0';
5895 level_name[0] = '\0';
5897 reading_playfield = FALSE;
5900 got_valid_playfield_line = FALSE;
5902 /* read next line of input file */
5903 if (!getStringFromFile(file, line, MAX_LINE_LEN))
5906 /* check if line was completely read and is terminated by line break */
5907 if (strlen(line) > 0 && line[strlen(line) - 1] == '\n')
5910 /* cut trailing line break (this can be newline and/or carriage return) */
5911 for (line_ptr = &line[strlen(line)]; line_ptr >= line; line_ptr--)
5912 if ((*line_ptr == '\n' || *line_ptr == '\r') && *(line_ptr + 1) == '\0')
5915 /* copy raw input line for later use (mainly debugging output) */
5916 strcpy(line_raw, line);
5918 if (read_continued_line)
5920 /* append new line to existing line, if there is enough space */
5921 if (strlen(previous_line) + strlen(line_ptr) < MAX_LINE_LEN)
5922 strcat(previous_line, line_ptr);
5924 strcpy(line, previous_line); /* copy storage buffer to line */
5926 read_continued_line = FALSE;
5929 /* if the last character is '\', continue at next line */
5930 if (strlen(line) > 0 && line[strlen(line) - 1] == '\\')
5932 line[strlen(line) - 1] = '\0'; /* cut off trailing backslash */
5933 strcpy(previous_line, line); /* copy line to storage buffer */
5935 read_continued_line = TRUE;
5940 /* skip empty lines */
5941 if (line[0] == '\0')
5944 /* extract comment text from comment line */
5947 for (line_ptr = line; *line_ptr; line_ptr++)
5948 if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != ';')
5951 strcpy(last_comment, line_ptr);
5956 /* extract level title text from line containing level title */
5957 if (line[0] == '\'')
5959 strcpy(level_name, &line[1]);
5961 if (strlen(level_name) > 0 && level_name[strlen(level_name) - 1] == '\'')
5962 level_name[strlen(level_name) - 1] = '\0';
5967 /* skip lines containing only spaces (or empty lines) */
5968 for (line_ptr = line; *line_ptr; line_ptr++)
5969 if (*line_ptr != ' ')
5971 if (*line_ptr == '\0')
5974 /* at this point, we have found a line containing part of a playfield */
5976 got_valid_playfield_line = TRUE;
5978 if (!reading_playfield)
5980 reading_playfield = TRUE;
5981 invalid_playfield_char = FALSE;
5983 for (x = 0; x < MAX_LEV_FIELDX; x++)
5984 for (y = 0; y < MAX_LEV_FIELDY; y++)
5985 level->field[x][y] = getMappedElement_SB(' ', load_xsb_to_ces);
5990 /* start with topmost tile row */
5994 /* skip playfield line if larger row than allowed */
5995 if (y >= MAX_LEV_FIELDY)
5998 /* start with leftmost tile column */
6001 /* read playfield elements from line */
6002 for (line_ptr = line; *line_ptr; line_ptr++)
6004 int mapped_sb_element = getMappedElement_SB(*line_ptr, load_xsb_to_ces);
6006 /* stop parsing playfield line if larger column than allowed */
6007 if (x >= MAX_LEV_FIELDX)
6010 if (mapped_sb_element == EL_UNDEFINED)
6012 invalid_playfield_char = TRUE;
6017 level->field[x][y] = mapped_sb_element;
6019 /* continue with next tile column */
6022 level->fieldx = MAX(x, level->fieldx);
6025 if (invalid_playfield_char)
6027 /* if first playfield line, treat invalid lines as comment lines */
6029 reading_playfield = FALSE;
6034 /* continue with next tile row */
6042 level->fieldx = MIN(MAX(MIN_LEV_FIELDX, level->fieldx), MAX_LEV_FIELDX);
6043 level->fieldy = MIN(MAX(MIN_LEV_FIELDY, level->fieldy), MAX_LEV_FIELDY);
6045 if (!reading_playfield)
6047 level->no_valid_file = TRUE;
6049 Error(ERR_WARN, "cannot read level '%s' -- using empty level", filename);
6054 if (*level_name != '\0')
6056 strncpy(level->name, level_name, MAX_LEVEL_NAME_LEN);
6057 level->name[MAX_LEVEL_NAME_LEN] = '\0';
6059 else if (*last_comment != '\0')
6061 strncpy(level->name, last_comment, MAX_LEVEL_NAME_LEN);
6062 level->name[MAX_LEVEL_NAME_LEN] = '\0';
6066 sprintf(level->name, "--> Level %d <--", level_file_info->nr);
6069 /* set all empty fields beyond the border walls to invisible steel wall */
6070 for (y = 0; y < level->fieldy; y++) for (x = 0; x < level->fieldx; x++)
6072 if ((x == 0 || x == level->fieldx - 1 ||
6073 y == 0 || y == level->fieldy - 1) &&
6074 level->field[x][y] == getMappedElement_SB(' ', load_xsb_to_ces))
6075 FloodFillLevel(x, y, getMappedElement_SB('_', load_xsb_to_ces),
6076 level->field, level->fieldx, level->fieldy);
6079 /* set special level settings for Sokoban levels */
6082 level->use_step_counter = TRUE;
6084 if (load_xsb_to_ces)
6086 /* special global settings can now be set in level template */
6088 level->use_custom_template = TRUE;
6093 /* ------------------------------------------------------------------------- */
6094 /* functions for handling native levels */
6095 /* ------------------------------------------------------------------------- */
6097 static void LoadLevelFromFileInfo_EM(struct LevelInfo *level,
6098 struct LevelFileInfo *level_file_info,
6099 boolean level_info_only)
6101 if (!LoadNativeLevel_EM(level_file_info->filename, level_info_only))
6102 level->no_valid_file = TRUE;
6105 static void LoadLevelFromFileInfo_SP(struct LevelInfo *level,
6106 struct LevelFileInfo *level_file_info,
6107 boolean level_info_only)
6111 /* determine position of requested level inside level package */
6112 if (level_file_info->packed)
6113 pos = level_file_info->nr - leveldir_current->first_level;
6115 if (!LoadNativeLevel_SP(level_file_info->filename, pos, level_info_only))
6116 level->no_valid_file = TRUE;
6119 static void LoadLevelFromFileInfo_MM(struct LevelInfo *level,
6120 struct LevelFileInfo *level_file_info,
6121 boolean level_info_only)
6123 if (!LoadNativeLevel_MM(level_file_info->filename, level_info_only))
6124 level->no_valid_file = TRUE;
6127 void CopyNativeLevel_RND_to_Native(struct LevelInfo *level)
6129 if (level->game_engine_type == GAME_ENGINE_TYPE_EM)
6130 CopyNativeLevel_RND_to_EM(level);
6131 else if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
6132 CopyNativeLevel_RND_to_SP(level);
6133 else if (level->game_engine_type == GAME_ENGINE_TYPE_MM)
6134 CopyNativeLevel_RND_to_MM(level);
6137 void CopyNativeLevel_Native_to_RND(struct LevelInfo *level)
6139 if (level->game_engine_type == GAME_ENGINE_TYPE_EM)
6140 CopyNativeLevel_EM_to_RND(level);
6141 else if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
6142 CopyNativeLevel_SP_to_RND(level);
6143 else if (level->game_engine_type == GAME_ENGINE_TYPE_MM)
6144 CopyNativeLevel_MM_to_RND(level);
6147 void SaveNativeLevel(struct LevelInfo *level)
6149 if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
6151 char *basename = getSingleLevelBasenameExt(level->file_info.nr, "sp");
6152 char *filename = getLevelFilenameFromBasename(basename);
6154 CopyNativeLevel_RND_to_SP(level);
6155 CopyNativeTape_RND_to_SP(level);
6157 SaveNativeLevel_SP(filename);
6162 /* ------------------------------------------------------------------------- */
6163 /* functions for loading generic level */
6164 /* ------------------------------------------------------------------------- */
6166 static void LoadLevelFromFileInfo(struct LevelInfo *level,
6167 struct LevelFileInfo *level_file_info,
6168 boolean level_info_only)
6170 /* always start with reliable default values */
6171 setLevelInfoToDefaults(level, level_info_only, TRUE);
6173 switch (level_file_info->type)
6175 case LEVEL_FILE_TYPE_RND:
6176 LoadLevelFromFileInfo_RND(level, level_file_info, level_info_only);
6179 case LEVEL_FILE_TYPE_EM:
6180 LoadLevelFromFileInfo_EM(level, level_file_info, level_info_only);
6181 level->game_engine_type = GAME_ENGINE_TYPE_EM;
6184 case LEVEL_FILE_TYPE_SP:
6185 LoadLevelFromFileInfo_SP(level, level_file_info, level_info_only);
6186 level->game_engine_type = GAME_ENGINE_TYPE_SP;
6189 case LEVEL_FILE_TYPE_MM:
6190 LoadLevelFromFileInfo_MM(level, level_file_info, level_info_only);
6191 level->game_engine_type = GAME_ENGINE_TYPE_MM;
6194 case LEVEL_FILE_TYPE_DC:
6195 LoadLevelFromFileInfo_DC(level, level_file_info, level_info_only);
6198 case LEVEL_FILE_TYPE_SB:
6199 LoadLevelFromFileInfo_SB(level, level_file_info, level_info_only);
6203 LoadLevelFromFileInfo_RND(level, level_file_info, level_info_only);
6207 /* if level file is invalid, restore level structure to default values */
6208 if (level->no_valid_file)
6209 setLevelInfoToDefaults(level, level_info_only, FALSE);
6211 if (level->game_engine_type == GAME_ENGINE_TYPE_UNKNOWN)
6212 level->game_engine_type = GAME_ENGINE_TYPE_RND;
6214 if (level_file_info->type != LEVEL_FILE_TYPE_RND)
6215 CopyNativeLevel_Native_to_RND(level);
6218 void LoadLevelFromFilename(struct LevelInfo *level, char *filename)
6220 static struct LevelFileInfo level_file_info;
6222 /* always start with reliable default values */
6223 setFileInfoToDefaults(&level_file_info);
6225 level_file_info.nr = 0; /* unknown level number */
6226 level_file_info.type = LEVEL_FILE_TYPE_RND; /* no others supported yet */
6228 setString(&level_file_info.filename, filename);
6230 LoadLevelFromFileInfo(level, &level_file_info, FALSE);
6233 static void LoadLevel_InitVersion(struct LevelInfo *level)
6237 if (leveldir_current == NULL) /* only when dumping level */
6240 /* all engine modifications also valid for levels which use latest engine */
6241 if (level->game_version < VERSION_IDENT(3,2,0,5))
6243 /* time bonus score was given for 10 s instead of 1 s before 3.2.0-5 */
6244 level->score[SC_TIME_BONUS] /= 10;
6247 if (leveldir_current->latest_engine)
6249 /* ---------- use latest game engine ----------------------------------- */
6251 /* For all levels which are forced to use the latest game engine version
6252 (normally all but user contributed, private and undefined levels), set
6253 the game engine version to the actual version; this allows for actual
6254 corrections in the game engine to take effect for existing, converted
6255 levels (from "classic" or other existing games) to make the emulation
6256 of the corresponding game more accurate, while (hopefully) not breaking
6257 existing levels created from other players. */
6259 level->game_version = GAME_VERSION_ACTUAL;
6261 /* Set special EM style gems behaviour: EM style gems slip down from
6262 normal, steel and growing wall. As this is a more fundamental change,
6263 it seems better to set the default behaviour to "off" (as it is more
6264 natural) and make it configurable in the level editor (as a property
6265 of gem style elements). Already existing converted levels (neither
6266 private nor contributed levels) are changed to the new behaviour. */
6268 if (level->file_version < FILE_VERSION_2_0)
6269 level->em_slippery_gems = TRUE;
6274 /* ---------- use game engine the level was created with ----------------- */
6276 /* For all levels which are not forced to use the latest game engine
6277 version (normally user contributed, private and undefined levels),
6278 use the version of the game engine the levels were created for.
6280 Since 2.0.1, the game engine version is now directly stored
6281 in the level file (chunk "VERS"), so there is no need anymore
6282 to set the game version from the file version (except for old,
6283 pre-2.0 levels, where the game version is still taken from the
6284 file format version used to store the level -- see above). */
6286 /* player was faster than enemies in 1.0.0 and before */
6287 if (level->file_version == FILE_VERSION_1_0)
6288 for (i = 0; i < MAX_PLAYERS; i++)
6289 level->initial_player_stepsize[i] = STEPSIZE_FAST;
6291 /* default behaviour for EM style gems was "slippery" only in 2.0.1 */
6292 if (level->game_version == VERSION_IDENT(2,0,1,0))
6293 level->em_slippery_gems = TRUE;
6295 /* springs could be pushed over pits before (pre-release version) 2.2.0 */
6296 if (level->game_version < VERSION_IDENT(2,2,0,0))
6297 level->use_spring_bug = TRUE;
6299 if (level->game_version < VERSION_IDENT(3,2,0,5))
6301 /* time orb caused limited time in endless time levels before 3.2.0-5 */
6302 level->use_time_orb_bug = TRUE;
6304 /* default behaviour for snapping was "no snap delay" before 3.2.0-5 */
6305 level->block_snap_field = FALSE;
6307 /* extra time score was same value as time left score before 3.2.0-5 */
6308 level->extra_time_score = level->score[SC_TIME_BONUS];
6311 if (level->game_version < VERSION_IDENT(3,2,0,7))
6313 /* default behaviour for snapping was "not continuous" before 3.2.0-7 */
6314 level->continuous_snapping = FALSE;
6317 /* only few elements were able to actively move into acid before 3.1.0 */
6318 /* trigger settings did not exist before 3.1.0; set to default "any" */
6319 if (level->game_version < VERSION_IDENT(3,1,0,0))
6321 /* correct "can move into acid" settings (all zero in old levels) */
6323 level->can_move_into_acid_bits = 0; /* nothing can move into acid */
6324 level->dont_collide_with_bits = 0; /* nothing is deadly when colliding */
6326 setMoveIntoAcidProperty(level, EL_ROBOT, TRUE);
6327 setMoveIntoAcidProperty(level, EL_SATELLITE, TRUE);
6328 setMoveIntoAcidProperty(level, EL_PENGUIN, TRUE);
6329 setMoveIntoAcidProperty(level, EL_BALLOON, TRUE);
6331 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6332 SET_PROPERTY(EL_CUSTOM_START + i, EP_CAN_MOVE_INTO_ACID, TRUE);
6334 /* correct trigger settings (stored as zero == "none" in old levels) */
6336 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6338 int element = EL_CUSTOM_START + i;
6339 struct ElementInfo *ei = &element_info[element];
6341 for (j = 0; j < ei->num_change_pages; j++)
6343 struct ElementChangeInfo *change = &ei->change_page[j];
6345 change->trigger_player = CH_PLAYER_ANY;
6346 change->trigger_page = CH_PAGE_ANY;
6351 /* try to detect and fix "Snake Bite" levels, which are broken with 3.2.0 */
6353 int element = EL_CUSTOM_256;
6354 struct ElementInfo *ei = &element_info[element];
6355 struct ElementChangeInfo *change = &ei->change_page[0];
6357 /* This is needed to fix a problem that was caused by a bugfix in function
6358 game.c/CreateFieldExt() introduced with 3.2.0 that corrects the behaviour
6359 when a custom element changes to EL_SOKOBAN_FIELD_PLAYER (before, it did
6360 not replace walkable elements, but instead just placed the player on it,
6361 without placing the Sokoban field under the player). Unfortunately, this
6362 breaks "Snake Bite" style levels when the snake is halfway through a door
6363 that just closes (the snake head is still alive and can be moved in this
6364 case). This can be fixed by replacing the EL_SOKOBAN_FIELD_PLAYER by the
6365 player (without Sokoban element) which then gets killed as designed). */
6367 if ((strncmp(leveldir_current->identifier, "snake_bite", 10) == 0 ||
6368 strncmp(ei->description, "pause b4 death", 14) == 0) &&
6369 change->target_element == EL_SOKOBAN_FIELD_PLAYER)
6370 change->target_element = EL_PLAYER_1;
6373 /* try to detect and fix "Zelda" style levels, which are broken with 3.2.5 */
6374 if (level->game_version < VERSION_IDENT(3,2,5,0))
6376 /* This is needed to fix a problem that was caused by a bugfix in function
6377 game.c/CheckTriggeredElementChangeExt() introduced with 3.2.5 that
6378 corrects the behaviour when a custom element changes to another custom
6379 element with a higher element number that has change actions defined.
6380 Normally, only one change per frame is allowed for custom elements.
6381 Therefore, it is checked if a custom element already changed in the
6382 current frame; if it did, subsequent changes are suppressed.
6383 Unfortunately, this is only checked for element changes, but not for
6384 change actions, which are still executed. As the function above loops
6385 through all custom elements from lower to higher, an element change
6386 resulting in a lower CE number won't be checked again, while a target
6387 element with a higher number will also be checked, and potential change
6388 actions will get executed for this CE, too (which is wrong), while
6389 further changes are ignored (which is correct). As this bugfix breaks
6390 Zelda II (and introduces graphical bugs to Zelda I, and also breaks a
6391 few other levels like Alan Bond's "FMV"), allow the previous, incorrect
6392 behaviour for existing levels and tapes that make use of this bug */
6394 level->use_action_after_change_bug = TRUE;
6397 /* not centering level after relocating player was default only in 3.2.3 */
6398 if (level->game_version == VERSION_IDENT(3,2,3,0)) /* (no pre-releases) */
6399 level->shifted_relocation = TRUE;
6401 /* EM style elements always chain-exploded in R'n'D engine before 3.2.6 */
6402 if (level->game_version < VERSION_IDENT(3,2,6,0))
6403 level->em_explodes_by_fire = TRUE;
6405 /* levels were solved by the first player entering an exit up to 4.1.0.0 */
6406 if (level->game_version <= VERSION_IDENT(4,1,0,0))
6407 level->solved_by_one_player = TRUE;
6410 static void LoadLevel_InitStandardElements(struct LevelInfo *level)
6414 /* map elements that have changed in newer versions */
6415 level->amoeba_content = getMappedElementByVersion(level->amoeba_content,
6416 level->game_version);
6417 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
6418 for (x = 0; x < 3; x++)
6419 for (y = 0; y < 3; y++)
6420 level->yamyam_content[i].e[x][y] =
6421 getMappedElementByVersion(level->yamyam_content[i].e[x][y],
6422 level->game_version);
6426 static void LoadLevel_InitCustomElements(struct LevelInfo *level)
6430 /* map custom element change events that have changed in newer versions
6431 (these following values were accidentally changed in version 3.0.1)
6432 (this seems to be needed only for 'ab_levelset3' and 'ab_levelset4') */
6433 if (level->game_version <= VERSION_IDENT(3,0,0,0))
6435 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6437 int element = EL_CUSTOM_START + i;
6439 /* order of checking and copying events to be mapped is important */
6440 /* (do not change the start and end value -- they are constant) */
6441 for (j = CE_BY_OTHER_ACTION; j >= CE_VALUE_GETS_ZERO; j--)
6443 if (HAS_CHANGE_EVENT(element, j - 2))
6445 SET_CHANGE_EVENT(element, j - 2, FALSE);
6446 SET_CHANGE_EVENT(element, j, TRUE);
6450 /* order of checking and copying events to be mapped is important */
6451 /* (do not change the start and end value -- they are constant) */
6452 for (j = CE_PLAYER_COLLECTS_X; j >= CE_HITTING_SOMETHING; j--)
6454 if (HAS_CHANGE_EVENT(element, j - 1))
6456 SET_CHANGE_EVENT(element, j - 1, FALSE);
6457 SET_CHANGE_EVENT(element, j, TRUE);
6463 /* initialize "can_change" field for old levels with only one change page */
6464 if (level->game_version <= VERSION_IDENT(3,0,2,0))
6466 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6468 int element = EL_CUSTOM_START + i;
6470 if (CAN_CHANGE(element))
6471 element_info[element].change->can_change = TRUE;
6475 /* correct custom element values (for old levels without these options) */
6476 if (level->game_version < VERSION_IDENT(3,1,1,0))
6478 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6480 int element = EL_CUSTOM_START + i;
6481 struct ElementInfo *ei = &element_info[element];
6483 if (ei->access_direction == MV_NO_DIRECTION)
6484 ei->access_direction = MV_ALL_DIRECTIONS;
6488 /* correct custom element values (fix invalid values for all versions) */
6491 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6493 int element = EL_CUSTOM_START + i;
6494 struct ElementInfo *ei = &element_info[element];
6496 for (j = 0; j < ei->num_change_pages; j++)
6498 struct ElementChangeInfo *change = &ei->change_page[j];
6500 if (change->trigger_player == CH_PLAYER_NONE)
6501 change->trigger_player = CH_PLAYER_ANY;
6503 if (change->trigger_side == CH_SIDE_NONE)
6504 change->trigger_side = CH_SIDE_ANY;
6509 /* initialize "can_explode" field for old levels which did not store this */
6510 /* !!! CHECK THIS -- "<= 3,1,0,0" IS PROBABLY WRONG !!! */
6511 if (level->game_version <= VERSION_IDENT(3,1,0,0))
6513 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6515 int element = EL_CUSTOM_START + i;
6517 if (EXPLODES_1X1_OLD(element))
6518 element_info[element].explosion_type = EXPLODES_1X1;
6520 SET_PROPERTY(element, EP_CAN_EXPLODE, (EXPLODES_BY_FIRE(element) ||
6521 EXPLODES_SMASHED(element) ||
6522 EXPLODES_IMPACT(element)));
6526 /* correct previously hard-coded move delay values for maze runner style */
6527 if (level->game_version < VERSION_IDENT(3,1,1,0))
6529 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6531 int element = EL_CUSTOM_START + i;
6533 if (element_info[element].move_pattern & MV_MAZE_RUNNER_STYLE)
6535 /* previously hard-coded and therefore ignored */
6536 element_info[element].move_delay_fixed = 9;
6537 element_info[element].move_delay_random = 0;
6542 /* set some other uninitialized values of custom elements in older levels */
6543 if (level->game_version < VERSION_IDENT(3,1,0,0))
6545 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6547 int element = EL_CUSTOM_START + i;
6549 element_info[element].access_direction = MV_ALL_DIRECTIONS;
6551 element_info[element].explosion_delay = 17;
6552 element_info[element].ignition_delay = 8;
6557 static void LoadLevel_InitElements(struct LevelInfo *level)
6559 LoadLevel_InitStandardElements(level);
6561 if (level->file_has_custom_elements)
6562 LoadLevel_InitCustomElements(level);
6564 /* initialize element properties for level editor etc. */
6565 InitElementPropertiesEngine(level->game_version);
6566 InitElementPropertiesGfxElement();
6569 static void LoadLevel_InitPlayfield(struct LevelInfo *level)
6573 /* map elements that have changed in newer versions */
6574 for (y = 0; y < level->fieldy; y++)
6575 for (x = 0; x < level->fieldx; x++)
6576 level->field[x][y] = getMappedElementByVersion(level->field[x][y],
6577 level->game_version);
6579 /* clear unused playfield data (nicer if level gets resized in editor) */
6580 for (x = 0; x < MAX_LEV_FIELDX; x++)
6581 for (y = 0; y < MAX_LEV_FIELDY; y++)
6582 if (x >= level->fieldx || y >= level->fieldy)
6583 level->field[x][y] = EL_EMPTY;
6585 /* copy elements to runtime playfield array */
6586 for (x = 0; x < MAX_LEV_FIELDX; x++)
6587 for (y = 0; y < MAX_LEV_FIELDY; y++)
6588 Feld[x][y] = level->field[x][y];
6590 /* initialize level size variables for faster access */
6591 lev_fieldx = level->fieldx;
6592 lev_fieldy = level->fieldy;
6594 /* determine border element for this level */
6595 if (level->file_info.type == LEVEL_FILE_TYPE_DC)
6596 BorderElement = EL_EMPTY; /* (in editor, SetBorderElement() is used) */
6601 static void LoadLevel_InitNativeEngines(struct LevelInfo *level)
6603 struct LevelFileInfo *level_file_info = &level->file_info;
6605 if (level_file_info->type == LEVEL_FILE_TYPE_RND)
6606 CopyNativeLevel_RND_to_Native(level);
6609 static void LoadLevelTemplate_LoadAndInit()
6611 LoadLevelFromFileInfo(&level_template, &level_template.file_info, FALSE);
6613 LoadLevel_InitVersion(&level_template);
6614 LoadLevel_InitElements(&level_template);
6616 ActivateLevelTemplate();
6619 void LoadLevelTemplate(int nr)
6621 if (!fileExists(getGlobalLevelTemplateFilename()))
6623 Error(ERR_WARN, "no level template found for this level");
6628 setLevelFileInfo(&level_template.file_info, nr);
6630 LoadLevelTemplate_LoadAndInit();
6633 static void LoadNetworkLevelTemplate(struct NetworkLevelInfo *network_level)
6635 copyLevelFileInfo(&network_level->tmpl_info, &level_template.file_info);
6637 LoadLevelTemplate_LoadAndInit();
6640 static void LoadLevel_LoadAndInit(struct NetworkLevelInfo *network_level)
6642 LoadLevelFromFileInfo(&level, &level.file_info, FALSE);
6644 if (level.use_custom_template)
6646 if (network_level != NULL)
6647 LoadNetworkLevelTemplate(network_level);
6649 LoadLevelTemplate(-1);
6652 LoadLevel_InitVersion(&level);
6653 LoadLevel_InitElements(&level);
6654 LoadLevel_InitPlayfield(&level);
6656 LoadLevel_InitNativeEngines(&level);
6659 void LoadLevel(int nr)
6661 SetLevelSetInfo(leveldir_current->identifier, nr);
6663 setLevelFileInfo(&level.file_info, nr);
6665 LoadLevel_LoadAndInit(NULL);
6668 void LoadLevelInfoOnly(int nr)
6670 setLevelFileInfo(&level.file_info, nr);
6672 LoadLevelFromFileInfo(&level, &level.file_info, TRUE);
6675 void LoadNetworkLevel(struct NetworkLevelInfo *network_level)
6677 SetLevelSetInfo(network_level->leveldir_identifier,
6678 network_level->file_info.nr);
6680 copyLevelFileInfo(&network_level->file_info, &level.file_info);
6682 LoadLevel_LoadAndInit(network_level);
6685 static int SaveLevel_VERS(FILE *file, struct LevelInfo *level)
6689 chunk_size += putFileVersion(file, level->file_version);
6690 chunk_size += putFileVersion(file, level->game_version);
6695 static int SaveLevel_DATE(FILE *file, struct LevelInfo *level)
6699 chunk_size += putFile16BitBE(file, level->creation_date.year);
6700 chunk_size += putFile8Bit(file, level->creation_date.month);
6701 chunk_size += putFile8Bit(file, level->creation_date.day);
6706 #if ENABLE_HISTORIC_CHUNKS
6707 static void SaveLevel_HEAD(FILE *file, struct LevelInfo *level)
6711 putFile8Bit(file, level->fieldx);
6712 putFile8Bit(file, level->fieldy);
6714 putFile16BitBE(file, level->time);
6715 putFile16BitBE(file, level->gems_needed);
6717 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
6718 putFile8Bit(file, level->name[i]);
6720 for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
6721 putFile8Bit(file, level->score[i]);
6723 for (i = 0; i < STD_ELEMENT_CONTENTS; i++)
6724 for (y = 0; y < 3; y++)
6725 for (x = 0; x < 3; x++)
6726 putFile8Bit(file, (level->encoding_16bit_yamyam ? EL_EMPTY :
6727 level->yamyam_content[i].e[x][y]));
6728 putFile8Bit(file, level->amoeba_speed);
6729 putFile8Bit(file, level->time_magic_wall);
6730 putFile8Bit(file, level->time_wheel);
6731 putFile8Bit(file, (level->encoding_16bit_amoeba ? EL_EMPTY :
6732 level->amoeba_content));
6733 putFile8Bit(file, (level->initial_player_stepsize == STEPSIZE_FAST ? 1 : 0));
6734 putFile8Bit(file, (level->initial_gravity ? 1 : 0));
6735 putFile8Bit(file, (level->encoding_16bit_field ? 1 : 0));
6736 putFile8Bit(file, (level->em_slippery_gems ? 1 : 0));
6738 putFile8Bit(file, (level->use_custom_template ? 1 : 0));
6740 putFile8Bit(file, (level->block_last_field ? 1 : 0));
6741 putFile8Bit(file, (level->sp_block_last_field ? 1 : 0));
6742 putFile32BitBE(file, level->can_move_into_acid_bits);
6743 putFile8Bit(file, level->dont_collide_with_bits);
6745 putFile8Bit(file, (level->use_spring_bug ? 1 : 0));
6746 putFile8Bit(file, (level->use_step_counter ? 1 : 0));
6748 putFile8Bit(file, (level->instant_relocation ? 1 : 0));
6749 putFile8Bit(file, (level->can_pass_to_walkable ? 1 : 0));
6750 putFile8Bit(file, (level->grow_into_diggable ? 1 : 0));
6752 putFile8Bit(file, level->game_engine_type);
6754 WriteUnusedBytesToFile(file, LEVEL_CHUNK_HEAD_UNUSED);
6758 static int SaveLevel_NAME(FILE *file, struct LevelInfo *level)
6763 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
6764 chunk_size += putFile8Bit(file, level->name[i]);
6769 static int SaveLevel_AUTH(FILE *file, struct LevelInfo *level)
6774 for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
6775 chunk_size += putFile8Bit(file, level->author[i]);
6780 #if ENABLE_HISTORIC_CHUNKS
6781 static int SaveLevel_BODY(FILE *file, struct LevelInfo *level)
6786 for (y = 0; y < level->fieldy; y++)
6787 for (x = 0; x < level->fieldx; x++)
6788 if (level->encoding_16bit_field)
6789 chunk_size += putFile16BitBE(file, level->field[x][y]);
6791 chunk_size += putFile8Bit(file, level->field[x][y]);
6797 static int SaveLevel_BODY(FILE *file, struct LevelInfo *level)
6802 for (y = 0; y < level->fieldy; y++)
6803 for (x = 0; x < level->fieldx; x++)
6804 chunk_size += putFile16BitBE(file, level->field[x][y]);
6809 #if ENABLE_HISTORIC_CHUNKS
6810 static void SaveLevel_CONT(FILE *file, struct LevelInfo *level)
6814 putFile8Bit(file, EL_YAMYAM);
6815 putFile8Bit(file, level->num_yamyam_contents);
6816 putFile8Bit(file, 0);
6817 putFile8Bit(file, 0);
6819 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
6820 for (y = 0; y < 3; y++)
6821 for (x = 0; x < 3; x++)
6822 if (level->encoding_16bit_field)
6823 putFile16BitBE(file, level->yamyam_content[i].e[x][y]);
6825 putFile8Bit(file, level->yamyam_content[i].e[x][y]);
6829 #if ENABLE_HISTORIC_CHUNKS
6830 static void SaveLevel_CNT2(FILE *file, struct LevelInfo *level, int element)
6833 int num_contents, content_xsize, content_ysize;
6834 int content_array[MAX_ELEMENT_CONTENTS][3][3];
6836 if (element == EL_YAMYAM)
6838 num_contents = level->num_yamyam_contents;
6842 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
6843 for (y = 0; y < 3; y++)
6844 for (x = 0; x < 3; x++)
6845 content_array[i][x][y] = level->yamyam_content[i].e[x][y];
6847 else if (element == EL_BD_AMOEBA)
6853 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
6854 for (y = 0; y < 3; y++)
6855 for (x = 0; x < 3; x++)
6856 content_array[i][x][y] = EL_EMPTY;
6857 content_array[0][0][0] = level->amoeba_content;
6861 /* chunk header already written -- write empty chunk data */
6862 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_SIZE);
6864 Error(ERR_WARN, "cannot save content for element '%d'", element);
6868 putFile16BitBE(file, element);
6869 putFile8Bit(file, num_contents);
6870 putFile8Bit(file, content_xsize);
6871 putFile8Bit(file, content_ysize);
6873 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_UNUSED);
6875 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
6876 for (y = 0; y < 3; y++)
6877 for (x = 0; x < 3; x++)
6878 putFile16BitBE(file, content_array[i][x][y]);
6882 #if ENABLE_HISTORIC_CHUNKS
6883 static int SaveLevel_CNT3(FILE *file, struct LevelInfo *level, int element)
6885 int envelope_nr = element - EL_ENVELOPE_1;
6886 int envelope_len = strlen(level->envelope_text[envelope_nr]) + 1;
6890 chunk_size += putFile16BitBE(file, element);
6891 chunk_size += putFile16BitBE(file, envelope_len);
6892 chunk_size += putFile8Bit(file, level->envelope_xsize[envelope_nr]);
6893 chunk_size += putFile8Bit(file, level->envelope_ysize[envelope_nr]);
6895 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT3_UNUSED);
6896 chunk_size += LEVEL_CHUNK_CNT3_UNUSED;
6898 for (i = 0; i < envelope_len; i++)
6899 chunk_size += putFile8Bit(file, level->envelope_text[envelope_nr][i]);
6905 #if ENABLE_HISTORIC_CHUNKS
6906 static void SaveLevel_CUS1(FILE *file, struct LevelInfo *level,
6907 int num_changed_custom_elements)
6911 putFile16BitBE(file, num_changed_custom_elements);
6913 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6915 int element = EL_CUSTOM_START + i;
6917 struct ElementInfo *ei = &element_info[element];
6919 if (ei->properties[EP_BITFIELD_BASE_NR] != EP_BITMASK_DEFAULT)
6921 if (check < num_changed_custom_elements)
6923 putFile16BitBE(file, element);
6924 putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
6931 if (check != num_changed_custom_elements) /* should not happen */
6932 Error(ERR_WARN, "inconsistent number of custom element properties");
6936 #if ENABLE_HISTORIC_CHUNKS
6937 static void SaveLevel_CUS2(FILE *file, struct LevelInfo *level,
6938 int num_changed_custom_elements)
6942 putFile16BitBE(file, num_changed_custom_elements);
6944 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6946 int element = EL_CUSTOM_START + i;
6948 if (element_info[element].change->target_element != EL_EMPTY_SPACE)
6950 if (check < num_changed_custom_elements)
6952 putFile16BitBE(file, element);
6953 putFile16BitBE(file, element_info[element].change->target_element);
6960 if (check != num_changed_custom_elements) /* should not happen */
6961 Error(ERR_WARN, "inconsistent number of custom target elements");
6965 #if ENABLE_HISTORIC_CHUNKS
6966 static void SaveLevel_CUS3(FILE *file, struct LevelInfo *level,
6967 int num_changed_custom_elements)
6969 int i, j, x, y, check = 0;
6971 putFile16BitBE(file, num_changed_custom_elements);
6973 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6975 int element = EL_CUSTOM_START + i;
6976 struct ElementInfo *ei = &element_info[element];
6978 if (ei->modified_settings)
6980 if (check < num_changed_custom_elements)
6982 putFile16BitBE(file, element);
6984 for (j = 0; j < MAX_ELEMENT_NAME_LEN; j++)
6985 putFile8Bit(file, ei->description[j]);
6987 putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
6989 /* some free bytes for future properties and padding */
6990 WriteUnusedBytesToFile(file, 7);
6992 putFile8Bit(file, ei->use_gfx_element);
6993 putFile16BitBE(file, ei->gfx_element_initial);
6995 putFile8Bit(file, ei->collect_score_initial);
6996 putFile8Bit(file, ei->collect_count_initial);
6998 putFile16BitBE(file, ei->push_delay_fixed);
6999 putFile16BitBE(file, ei->push_delay_random);
7000 putFile16BitBE(file, ei->move_delay_fixed);
7001 putFile16BitBE(file, ei->move_delay_random);
7003 putFile16BitBE(file, ei->move_pattern);
7004 putFile8Bit(file, ei->move_direction_initial);
7005 putFile8Bit(file, ei->move_stepsize);
7007 for (y = 0; y < 3; y++)
7008 for (x = 0; x < 3; x++)
7009 putFile16BitBE(file, ei->content.e[x][y]);
7011 putFile32BitBE(file, ei->change->events);
7013 putFile16BitBE(file, ei->change->target_element);
7015 putFile16BitBE(file, ei->change->delay_fixed);
7016 putFile16BitBE(file, ei->change->delay_random);
7017 putFile16BitBE(file, ei->change->delay_frames);
7019 putFile16BitBE(file, ei->change->initial_trigger_element);
7021 putFile8Bit(file, ei->change->explode);
7022 putFile8Bit(file, ei->change->use_target_content);
7023 putFile8Bit(file, ei->change->only_if_complete);
7024 putFile8Bit(file, ei->change->use_random_replace);
7026 putFile8Bit(file, ei->change->random_percentage);
7027 putFile8Bit(file, ei->change->replace_when);
7029 for (y = 0; y < 3; y++)
7030 for (x = 0; x < 3; x++)
7031 putFile16BitBE(file, ei->change->content.e[x][y]);
7033 putFile8Bit(file, ei->slippery_type);
7035 /* some free bytes for future properties and padding */
7036 WriteUnusedBytesToFile(file, LEVEL_CPART_CUS3_UNUSED);
7043 if (check != num_changed_custom_elements) /* should not happen */
7044 Error(ERR_WARN, "inconsistent number of custom element properties");
7048 #if ENABLE_HISTORIC_CHUNKS
7049 static void SaveLevel_CUS4(FILE *file, struct LevelInfo *level, int element)
7051 struct ElementInfo *ei = &element_info[element];
7054 /* ---------- custom element base property values (96 bytes) ------------- */
7056 putFile16BitBE(file, element);
7058 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
7059 putFile8Bit(file, ei->description[i]);
7061 putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
7063 WriteUnusedBytesToFile(file, 4); /* reserved for more base properties */
7065 putFile8Bit(file, ei->num_change_pages);
7067 putFile16BitBE(file, ei->ce_value_fixed_initial);
7068 putFile16BitBE(file, ei->ce_value_random_initial);
7069 putFile8Bit(file, ei->use_last_ce_value);
7071 putFile8Bit(file, ei->use_gfx_element);
7072 putFile16BitBE(file, ei->gfx_element_initial);
7074 putFile8Bit(file, ei->collect_score_initial);
7075 putFile8Bit(file, ei->collect_count_initial);
7077 putFile8Bit(file, ei->drop_delay_fixed);
7078 putFile8Bit(file, ei->push_delay_fixed);
7079 putFile8Bit(file, ei->drop_delay_random);
7080 putFile8Bit(file, ei->push_delay_random);
7081 putFile16BitBE(file, ei->move_delay_fixed);
7082 putFile16BitBE(file, ei->move_delay_random);
7084 /* bits 0 - 15 of "move_pattern" ... */
7085 putFile16BitBE(file, ei->move_pattern & 0xffff);
7086 putFile8Bit(file, ei->move_direction_initial);
7087 putFile8Bit(file, ei->move_stepsize);
7089 putFile8Bit(file, ei->slippery_type);
7091 for (y = 0; y < 3; y++)
7092 for (x = 0; x < 3; x++)
7093 putFile16BitBE(file, ei->content.e[x][y]);
7095 putFile16BitBE(file, ei->move_enter_element);
7096 putFile16BitBE(file, ei->move_leave_element);
7097 putFile8Bit(file, ei->move_leave_type);
7099 /* ... bits 16 - 31 of "move_pattern" (not nice, but downward compatible) */
7100 putFile16BitBE(file, (ei->move_pattern >> 16) & 0xffff);
7102 putFile8Bit(file, ei->access_direction);
7104 putFile8Bit(file, ei->explosion_delay);
7105 putFile8Bit(file, ei->ignition_delay);
7106 putFile8Bit(file, ei->explosion_type);
7108 /* some free bytes for future custom property values and padding */
7109 WriteUnusedBytesToFile(file, 1);
7111 /* ---------- change page property values (48 bytes) --------------------- */
7113 for (i = 0; i < ei->num_change_pages; i++)
7115 struct ElementChangeInfo *change = &ei->change_page[i];
7116 unsigned int event_bits;
7118 /* bits 0 - 31 of "has_event[]" ... */
7120 for (j = 0; j < MIN(NUM_CHANGE_EVENTS, 32); j++)
7121 if (change->has_event[j])
7122 event_bits |= (1 << j);
7123 putFile32BitBE(file, event_bits);
7125 putFile16BitBE(file, change->target_element);
7127 putFile16BitBE(file, change->delay_fixed);
7128 putFile16BitBE(file, change->delay_random);
7129 putFile16BitBE(file, change->delay_frames);
7131 putFile16BitBE(file, change->initial_trigger_element);
7133 putFile8Bit(file, change->explode);
7134 putFile8Bit(file, change->use_target_content);
7135 putFile8Bit(file, change->only_if_complete);
7136 putFile8Bit(file, change->use_random_replace);
7138 putFile8Bit(file, change->random_percentage);
7139 putFile8Bit(file, change->replace_when);
7141 for (y = 0; y < 3; y++)
7142 for (x = 0; x < 3; x++)
7143 putFile16BitBE(file, change->target_content.e[x][y]);
7145 putFile8Bit(file, change->can_change);
7147 putFile8Bit(file, change->trigger_side);
7149 putFile8Bit(file, change->trigger_player);
7150 putFile8Bit(file, (change->trigger_page == CH_PAGE_ANY ? CH_PAGE_ANY_FILE :
7151 log_2(change->trigger_page)));
7153 putFile8Bit(file, change->has_action);
7154 putFile8Bit(file, change->action_type);
7155 putFile8Bit(file, change->action_mode);
7156 putFile16BitBE(file, change->action_arg);
7158 /* ... bits 32 - 39 of "has_event[]" (not nice, but downward compatible) */
7160 for (j = 32; j < NUM_CHANGE_EVENTS; j++)
7161 if (change->has_event[j])
7162 event_bits |= (1 << (j - 32));
7163 putFile8Bit(file, event_bits);
7168 #if ENABLE_HISTORIC_CHUNKS
7169 static void SaveLevel_GRP1(FILE *file, struct LevelInfo *level, int element)
7171 struct ElementInfo *ei = &element_info[element];
7172 struct ElementGroupInfo *group = ei->group;
7175 putFile16BitBE(file, element);
7177 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
7178 putFile8Bit(file, ei->description[i]);
7180 putFile8Bit(file, group->num_elements);
7182 putFile8Bit(file, ei->use_gfx_element);
7183 putFile16BitBE(file, ei->gfx_element_initial);
7185 putFile8Bit(file, group->choice_mode);
7187 /* some free bytes for future values and padding */
7188 WriteUnusedBytesToFile(file, 3);
7190 for (i = 0; i < MAX_ELEMENTS_IN_GROUP; i++)
7191 putFile16BitBE(file, group->element[i]);
7195 static int SaveLevel_MicroChunk(FILE *file, struct LevelFileConfigInfo *entry,
7196 boolean write_element)
7198 int save_type = entry->save_type;
7199 int data_type = entry->data_type;
7200 int conf_type = entry->conf_type;
7201 int byte_mask = conf_type & CONF_MASK_BYTES;
7202 int element = entry->element;
7203 int default_value = entry->default_value;
7205 boolean modified = FALSE;
7207 if (byte_mask != CONF_MASK_MULTI_BYTES)
7209 void *value_ptr = entry->value;
7210 int value = (data_type == TYPE_BOOLEAN ? *(boolean *)value_ptr :
7213 /* check if any settings have been modified before saving them */
7214 if (value != default_value)
7217 /* do not save if explicitly told or if unmodified default settings */
7218 if ((save_type == SAVE_CONF_NEVER) ||
7219 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
7223 num_bytes += putFile16BitBE(file, element);
7225 num_bytes += putFile8Bit(file, conf_type);
7226 num_bytes += (byte_mask == CONF_MASK_1_BYTE ? putFile8Bit (file, value) :
7227 byte_mask == CONF_MASK_2_BYTE ? putFile16BitBE(file, value) :
7228 byte_mask == CONF_MASK_4_BYTE ? putFile32BitBE(file, value) :
7231 else if (data_type == TYPE_STRING)
7233 char *default_string = entry->default_string;
7234 char *string = (char *)(entry->value);
7235 int string_length = strlen(string);
7238 /* check if any settings have been modified before saving them */
7239 if (!strEqual(string, default_string))
7242 /* do not save if explicitly told or if unmodified default settings */
7243 if ((save_type == SAVE_CONF_NEVER) ||
7244 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
7248 num_bytes += putFile16BitBE(file, element);
7250 num_bytes += putFile8Bit(file, conf_type);
7251 num_bytes += putFile16BitBE(file, string_length);
7253 for (i = 0; i < string_length; i++)
7254 num_bytes += putFile8Bit(file, string[i]);
7256 else if (data_type == TYPE_ELEMENT_LIST)
7258 int *element_array = (int *)(entry->value);
7259 int num_elements = *(int *)(entry->num_entities);
7262 /* check if any settings have been modified before saving them */
7263 for (i = 0; i < num_elements; i++)
7264 if (element_array[i] != default_value)
7267 /* do not save if explicitly told or if unmodified default settings */
7268 if ((save_type == SAVE_CONF_NEVER) ||
7269 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
7273 num_bytes += putFile16BitBE(file, element);
7275 num_bytes += putFile8Bit(file, conf_type);
7276 num_bytes += putFile16BitBE(file, num_elements * CONF_ELEMENT_NUM_BYTES);
7278 for (i = 0; i < num_elements; i++)
7279 num_bytes += putFile16BitBE(file, element_array[i]);
7281 else if (data_type == TYPE_CONTENT_LIST)
7283 struct Content *content = (struct Content *)(entry->value);
7284 int num_contents = *(int *)(entry->num_entities);
7287 /* check if any settings have been modified before saving them */
7288 for (i = 0; i < num_contents; i++)
7289 for (y = 0; y < 3; y++)
7290 for (x = 0; x < 3; x++)
7291 if (content[i].e[x][y] != default_value)
7294 /* do not save if explicitly told or if unmodified default settings */
7295 if ((save_type == SAVE_CONF_NEVER) ||
7296 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
7300 num_bytes += putFile16BitBE(file, element);
7302 num_bytes += putFile8Bit(file, conf_type);
7303 num_bytes += putFile16BitBE(file, num_contents * CONF_CONTENT_NUM_BYTES);
7305 for (i = 0; i < num_contents; i++)
7306 for (y = 0; y < 3; y++)
7307 for (x = 0; x < 3; x++)
7308 num_bytes += putFile16BitBE(file, content[i].e[x][y]);
7314 static int SaveLevel_INFO(FILE *file, struct LevelInfo *level)
7319 li = *level; /* copy level data into temporary buffer */
7321 for (i = 0; chunk_config_INFO[i].data_type != -1; i++)
7322 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_INFO[i], FALSE);
7327 static int SaveLevel_ELEM(FILE *file, struct LevelInfo *level)
7332 li = *level; /* copy level data into temporary buffer */
7334 for (i = 0; chunk_config_ELEM[i].data_type != -1; i++)
7335 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_ELEM[i], TRUE);
7340 static int SaveLevel_NOTE(FILE *file, struct LevelInfo *level, int element)
7342 int envelope_nr = element - EL_ENVELOPE_1;
7346 chunk_size += putFile16BitBE(file, element);
7348 /* copy envelope data into temporary buffer */
7349 xx_envelope = level->envelope[envelope_nr];
7351 for (i = 0; chunk_config_NOTE[i].data_type != -1; i++)
7352 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_NOTE[i], FALSE);
7357 static int SaveLevel_CUSX(FILE *file, struct LevelInfo *level, int element)
7359 struct ElementInfo *ei = &element_info[element];
7363 chunk_size += putFile16BitBE(file, element);
7365 xx_ei = *ei; /* copy element data into temporary buffer */
7367 /* set default description string for this specific element */
7368 strcpy(xx_default_description, getDefaultElementDescription(ei));
7370 for (i = 0; chunk_config_CUSX_base[i].data_type != -1; i++)
7371 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_CUSX_base[i], FALSE);
7373 for (i = 0; i < ei->num_change_pages; i++)
7375 struct ElementChangeInfo *change = &ei->change_page[i];
7377 xx_current_change_page = i;
7379 xx_change = *change; /* copy change data into temporary buffer */
7382 setEventBitsFromEventFlags(change);
7384 for (j = 0; chunk_config_CUSX_change[j].data_type != -1; j++)
7385 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_CUSX_change[j],
7392 static int SaveLevel_GRPX(FILE *file, struct LevelInfo *level, int element)
7394 struct ElementInfo *ei = &element_info[element];
7395 struct ElementGroupInfo *group = ei->group;
7399 chunk_size += putFile16BitBE(file, element);
7401 xx_ei = *ei; /* copy element data into temporary buffer */
7402 xx_group = *group; /* copy group data into temporary buffer */
7404 /* set default description string for this specific element */
7405 strcpy(xx_default_description, getDefaultElementDescription(ei));
7407 for (i = 0; chunk_config_GRPX[i].data_type != -1; i++)
7408 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_GRPX[i], FALSE);
7413 static void SaveLevelFromFilename(struct LevelInfo *level, char *filename,
7414 boolean save_as_template)
7420 if (!(file = fopen(filename, MODE_WRITE)))
7422 Error(ERR_WARN, "cannot save level file '%s'", filename);
7426 level->file_version = FILE_VERSION_ACTUAL;
7427 level->game_version = GAME_VERSION_ACTUAL;
7429 level->creation_date = getCurrentDate();
7431 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
7432 putFileChunkBE(file, "CAVE", CHUNK_SIZE_NONE);
7434 chunk_size = SaveLevel_VERS(NULL, level);
7435 putFileChunkBE(file, "VERS", chunk_size);
7436 SaveLevel_VERS(file, level);
7438 chunk_size = SaveLevel_DATE(NULL, level);
7439 putFileChunkBE(file, "DATE", chunk_size);
7440 SaveLevel_DATE(file, level);
7442 chunk_size = SaveLevel_NAME(NULL, level);
7443 putFileChunkBE(file, "NAME", chunk_size);
7444 SaveLevel_NAME(file, level);
7446 chunk_size = SaveLevel_AUTH(NULL, level);
7447 putFileChunkBE(file, "AUTH", chunk_size);
7448 SaveLevel_AUTH(file, level);
7450 chunk_size = SaveLevel_INFO(NULL, level);
7451 putFileChunkBE(file, "INFO", chunk_size);
7452 SaveLevel_INFO(file, level);
7454 chunk_size = SaveLevel_BODY(NULL, level);
7455 putFileChunkBE(file, "BODY", chunk_size);
7456 SaveLevel_BODY(file, level);
7458 chunk_size = SaveLevel_ELEM(NULL, level);
7459 if (chunk_size > LEVEL_CHUNK_ELEM_UNCHANGED) /* save if changed */
7461 putFileChunkBE(file, "ELEM", chunk_size);
7462 SaveLevel_ELEM(file, level);
7465 for (i = 0; i < NUM_ENVELOPES; i++)
7467 int element = EL_ENVELOPE_1 + i;
7469 chunk_size = SaveLevel_NOTE(NULL, level, element);
7470 if (chunk_size > LEVEL_CHUNK_NOTE_UNCHANGED) /* save if changed */
7472 putFileChunkBE(file, "NOTE", chunk_size);
7473 SaveLevel_NOTE(file, level, element);
7477 /* if not using template level, check for non-default custom/group elements */
7478 if (!level->use_custom_template || save_as_template)
7480 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7482 int element = EL_CUSTOM_START + i;
7484 chunk_size = SaveLevel_CUSX(NULL, level, element);
7485 if (chunk_size > LEVEL_CHUNK_CUSX_UNCHANGED) /* save if changed */
7487 putFileChunkBE(file, "CUSX", chunk_size);
7488 SaveLevel_CUSX(file, level, element);
7492 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
7494 int element = EL_GROUP_START + i;
7496 chunk_size = SaveLevel_GRPX(NULL, level, element);
7497 if (chunk_size > LEVEL_CHUNK_GRPX_UNCHANGED) /* save if changed */
7499 putFileChunkBE(file, "GRPX", chunk_size);
7500 SaveLevel_GRPX(file, level, element);
7507 SetFilePermissions(filename, PERMS_PRIVATE);
7510 void SaveLevel(int nr)
7512 char *filename = getDefaultLevelFilename(nr);
7514 SaveLevelFromFilename(&level, filename, FALSE);
7517 void SaveLevelTemplate()
7519 char *filename = getLocalLevelTemplateFilename();
7521 SaveLevelFromFilename(&level, filename, TRUE);
7524 boolean SaveLevelChecked(int nr)
7526 char *filename = getDefaultLevelFilename(nr);
7527 boolean new_level = !fileExists(filename);
7528 boolean level_saved = FALSE;
7530 if (new_level || Request("Save this level and kill the old?", REQ_ASK))
7535 Request("Level saved!", REQ_CONFIRM);
7543 void DumpLevel(struct LevelInfo *level)
7545 if (level->no_level_file || level->no_valid_file)
7547 Error(ERR_WARN, "cannot dump -- no valid level file found");
7553 Print("Level xxx (file version %08d, game version %08d)\n",
7554 level->file_version, level->game_version);
7557 Print("Level author: '%s'\n", level->author);
7558 Print("Level title: '%s'\n", level->name);
7560 Print("Playfield size: %d x %d\n", level->fieldx, level->fieldy);
7562 Print("Level time: %d seconds\n", level->time);
7563 Print("Gems needed: %d\n", level->gems_needed);
7565 Print("Time for magic wall: %d seconds\n", level->time_magic_wall);
7566 Print("Time for wheel: %d seconds\n", level->time_wheel);
7567 Print("Time for light: %d seconds\n", level->time_light);
7568 Print("Time for timegate: %d seconds\n", level->time_timegate);
7570 Print("Amoeba speed: %d\n", level->amoeba_speed);
7573 Print("EM style slippery gems: %s\n", (level->em_slippery_gems ? "yes" : "no"));
7574 Print("Player blocks last field: %s\n", (level->block_last_field ? "yes" : "no"));
7575 Print("SP player blocks last field: %s\n", (level->sp_block_last_field ? "yes" : "no"));
7576 Print("use spring bug: %s\n", (level->use_spring_bug ? "yes" : "no"));
7577 Print("use step counter: %s\n", (level->use_step_counter ? "yes" : "no"));
7583 /* ========================================================================= */
7584 /* tape file functions */
7585 /* ========================================================================= */
7587 static void setTapeInfoToDefaults()
7591 /* always start with reliable default values (empty tape) */
7594 /* default values (also for pre-1.2 tapes) with only the first player */
7595 tape.player_participates[0] = TRUE;
7596 for (i = 1; i < MAX_PLAYERS; i++)
7597 tape.player_participates[i] = FALSE;
7599 /* at least one (default: the first) player participates in every tape */
7600 tape.num_participating_players = 1;
7602 tape.level_nr = level_nr;
7604 tape.changed = FALSE;
7606 tape.recording = FALSE;
7607 tape.playing = FALSE;
7608 tape.pausing = FALSE;
7610 tape.no_valid_file = FALSE;
7613 static int LoadTape_VERS(File *file, int chunk_size, struct TapeInfo *tape)
7615 tape->file_version = getFileVersion(file);
7616 tape->game_version = getFileVersion(file);
7621 static int LoadTape_HEAD(File *file, int chunk_size, struct TapeInfo *tape)
7625 tape->random_seed = getFile32BitBE(file);
7626 tape->date = getFile32BitBE(file);
7627 tape->length = getFile32BitBE(file);
7629 /* read header fields that are new since version 1.2 */
7630 if (tape->file_version >= FILE_VERSION_1_2)
7632 byte store_participating_players = getFile8Bit(file);
7635 /* since version 1.2, tapes store which players participate in the tape */
7636 tape->num_participating_players = 0;
7637 for (i = 0; i < MAX_PLAYERS; i++)
7639 tape->player_participates[i] = FALSE;
7641 if (store_participating_players & (1 << i))
7643 tape->player_participates[i] = TRUE;
7644 tape->num_participating_players++;
7648 tape->use_mouse = (getFile8Bit(file) == 1 ? TRUE : FALSE);
7650 ReadUnusedBytesFromFile(file, TAPE_CHUNK_HEAD_UNUSED);
7652 engine_version = getFileVersion(file);
7653 if (engine_version > 0)
7654 tape->engine_version = engine_version;
7656 tape->engine_version = tape->game_version;
7662 static int LoadTape_INFO(File *file, int chunk_size, struct TapeInfo *tape)
7664 int level_identifier_size;
7667 level_identifier_size = getFile16BitBE(file);
7669 tape->level_identifier =
7670 checked_realloc(tape->level_identifier, level_identifier_size);
7672 for (i = 0; i < level_identifier_size; i++)
7673 tape->level_identifier[i] = getFile8Bit(file);
7675 tape->level_nr = getFile16BitBE(file);
7677 chunk_size = 2 + level_identifier_size + 2;
7682 static int LoadTape_BODY(File *file, int chunk_size, struct TapeInfo *tape)
7686 (tape->use_mouse ? 3 : tape->num_participating_players) + 1;
7687 int chunk_size_expected = tape_pos_size * tape->length;
7689 if (chunk_size_expected != chunk_size)
7691 ReadUnusedBytesFromFile(file, chunk_size);
7692 return chunk_size_expected;
7695 for (i = 0; i < tape->length; i++)
7697 if (i >= MAX_TAPE_LEN)
7699 Error(ERR_WARN, "tape truncated -- size exceeds maximum tape size %d",
7702 // tape too large; read and ignore remaining tape data from this chunk
7703 for (;i < tape->length; i++)
7704 ReadUnusedBytesFromFile(file, tape->num_participating_players + 1);
7709 if (tape->use_mouse)
7711 tape->pos[i].action[TAPE_ACTION_LX] = getFile8Bit(file);
7712 tape->pos[i].action[TAPE_ACTION_LY] = getFile8Bit(file);
7713 tape->pos[i].action[TAPE_ACTION_BUTTON] = getFile8Bit(file);
7715 tape->pos[i].action[TAPE_ACTION_UNUSED] = 0;
7719 for (j = 0; j < MAX_PLAYERS; j++)
7721 tape->pos[i].action[j] = MV_NONE;
7723 if (tape->player_participates[j])
7724 tape->pos[i].action[j] = getFile8Bit(file);
7728 tape->pos[i].delay = getFile8Bit(file);
7730 if (tape->file_version == FILE_VERSION_1_0)
7732 /* eliminate possible diagonal moves in old tapes */
7733 /* this is only for backward compatibility */
7735 byte joy_dir[4] = { JOY_LEFT, JOY_RIGHT, JOY_UP, JOY_DOWN };
7736 byte action = tape->pos[i].action[0];
7737 int k, num_moves = 0;
7739 for (k = 0; k<4; k++)
7741 if (action & joy_dir[k])
7743 tape->pos[i + num_moves].action[0] = joy_dir[k];
7745 tape->pos[i + num_moves].delay = 0;
7754 tape->length += num_moves;
7757 else if (tape->file_version < FILE_VERSION_2_0)
7759 /* convert pre-2.0 tapes to new tape format */
7761 if (tape->pos[i].delay > 1)
7764 tape->pos[i + 1] = tape->pos[i];
7765 tape->pos[i + 1].delay = 1;
7768 for (j = 0; j < MAX_PLAYERS; j++)
7769 tape->pos[i].action[j] = MV_NONE;
7770 tape->pos[i].delay--;
7777 if (checkEndOfFile(file))
7781 if (i != tape->length)
7782 chunk_size = tape_pos_size * i;
7787 void LoadTape_SokobanSolution(char *filename)
7790 int move_delay = TILESIZE / level.initial_player_stepsize[0];
7792 if (!(file = openFile(filename, MODE_READ)))
7794 tape.no_valid_file = TRUE;
7799 while (!checkEndOfFile(file))
7801 unsigned char c = getByteFromFile(file);
7803 if (checkEndOfFile(file))
7810 tape.pos[tape.length].action[0] = MV_UP;
7811 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
7817 tape.pos[tape.length].action[0] = MV_DOWN;
7818 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
7824 tape.pos[tape.length].action[0] = MV_LEFT;
7825 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
7831 tape.pos[tape.length].action[0] = MV_RIGHT;
7832 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
7840 /* ignore white-space characters */
7844 tape.no_valid_file = TRUE;
7846 Error(ERR_WARN, "unsupported Sokoban solution file '%s' ['%d']", filename, c);
7854 if (tape.no_valid_file)
7857 tape.length_frames = GetTapeLengthFrames();
7858 tape.length_seconds = GetTapeLengthSeconds();
7861 void LoadTapeFromFilename(char *filename)
7863 char cookie[MAX_LINE_LEN];
7864 char chunk_name[CHUNK_ID_LEN + 1];
7868 /* always start with reliable default values */
7869 setTapeInfoToDefaults();
7871 if (strSuffix(filename, ".sln"))
7873 LoadTape_SokobanSolution(filename);
7878 if (!(file = openFile(filename, MODE_READ)))
7880 tape.no_valid_file = TRUE;
7885 getFileChunkBE(file, chunk_name, NULL);
7886 if (strEqual(chunk_name, "RND1"))
7888 getFile32BitBE(file); /* not used */
7890 getFileChunkBE(file, chunk_name, NULL);
7891 if (!strEqual(chunk_name, "TAPE"))
7893 tape.no_valid_file = TRUE;
7895 Error(ERR_WARN, "unknown format of tape file '%s'", filename);
7902 else /* check for pre-2.0 file format with cookie string */
7904 strcpy(cookie, chunk_name);
7905 if (getStringFromFile(file, &cookie[4], MAX_LINE_LEN - 4) == NULL)
7907 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
7908 cookie[strlen(cookie) - 1] = '\0';
7910 if (!checkCookieString(cookie, TAPE_COOKIE_TMPL))
7912 tape.no_valid_file = TRUE;
7914 Error(ERR_WARN, "unknown format of tape file '%s'", filename);
7921 if ((tape.file_version = getFileVersionFromCookieString(cookie)) == -1)
7923 tape.no_valid_file = TRUE;
7925 Error(ERR_WARN, "unsupported version of tape file '%s'", filename);
7932 /* pre-2.0 tape files have no game version, so use file version here */
7933 tape.game_version = tape.file_version;
7936 if (tape.file_version < FILE_VERSION_1_2)
7938 /* tape files from versions before 1.2.0 without chunk structure */
7939 LoadTape_HEAD(file, TAPE_CHUNK_HEAD_SIZE, &tape);
7940 LoadTape_BODY(file, 2 * tape.length, &tape);
7948 int (*loader)(File *, int, struct TapeInfo *);
7952 { "VERS", TAPE_CHUNK_VERS_SIZE, LoadTape_VERS },
7953 { "HEAD", TAPE_CHUNK_HEAD_SIZE, LoadTape_HEAD },
7954 { "INFO", -1, LoadTape_INFO },
7955 { "BODY", -1, LoadTape_BODY },
7959 while (getFileChunkBE(file, chunk_name, &chunk_size))
7963 while (chunk_info[i].name != NULL &&
7964 !strEqual(chunk_name, chunk_info[i].name))
7967 if (chunk_info[i].name == NULL)
7969 Error(ERR_WARN, "unknown chunk '%s' in tape file '%s'",
7970 chunk_name, filename);
7971 ReadUnusedBytesFromFile(file, chunk_size);
7973 else if (chunk_info[i].size != -1 &&
7974 chunk_info[i].size != chunk_size)
7976 Error(ERR_WARN, "wrong size (%d) of chunk '%s' in tape file '%s'",
7977 chunk_size, chunk_name, filename);
7978 ReadUnusedBytesFromFile(file, chunk_size);
7982 /* call function to load this tape chunk */
7983 int chunk_size_expected =
7984 (chunk_info[i].loader)(file, chunk_size, &tape);
7986 /* the size of some chunks cannot be checked before reading other
7987 chunks first (like "HEAD" and "BODY") that contain some header
7988 information, so check them here */
7989 if (chunk_size_expected != chunk_size)
7991 Error(ERR_WARN, "wrong size (%d) of chunk '%s' in tape file '%s'",
7992 chunk_size, chunk_name, filename);
8000 tape.length_frames = GetTapeLengthFrames();
8001 tape.length_seconds = GetTapeLengthSeconds();
8004 printf("::: tape file version: %d\n", tape.file_version);
8005 printf("::: tape game version: %d\n", tape.game_version);
8006 printf("::: tape engine version: %d\n", tape.engine_version);
8010 void LoadTape(int nr)
8012 char *filename = getTapeFilename(nr);
8014 LoadTapeFromFilename(filename);
8017 void LoadSolutionTape(int nr)
8019 char *filename = getSolutionTapeFilename(nr);
8021 LoadTapeFromFilename(filename);
8023 if (TAPE_IS_EMPTY(tape) &&
8024 level.game_engine_type == GAME_ENGINE_TYPE_SP &&
8025 level.native_sp_level->demo.is_available)
8026 CopyNativeTape_SP_to_RND(&level);
8029 static void SaveTape_VERS(FILE *file, struct TapeInfo *tape)
8031 putFileVersion(file, tape->file_version);
8032 putFileVersion(file, tape->game_version);
8035 static void SaveTape_HEAD(FILE *file, struct TapeInfo *tape)
8038 byte store_participating_players = 0;
8040 /* set bits for participating players for compact storage */
8041 for (i = 0; i < MAX_PLAYERS; i++)
8042 if (tape->player_participates[i])
8043 store_participating_players |= (1 << i);
8045 putFile32BitBE(file, tape->random_seed);
8046 putFile32BitBE(file, tape->date);
8047 putFile32BitBE(file, tape->length);
8049 putFile8Bit(file, store_participating_players);
8051 putFile8Bit(file, (tape->use_mouse ? 1 : 0));
8053 /* unused bytes not at the end here for 4-byte alignment of engine_version */
8054 WriteUnusedBytesToFile(file, TAPE_CHUNK_HEAD_UNUSED);
8056 putFileVersion(file, tape->engine_version);
8059 static void SaveTape_INFO(FILE *file, struct TapeInfo *tape)
8061 int level_identifier_size = strlen(tape->level_identifier) + 1;
8064 putFile16BitBE(file, level_identifier_size);
8066 for (i = 0; i < level_identifier_size; i++)
8067 putFile8Bit(file, tape->level_identifier[i]);
8069 putFile16BitBE(file, tape->level_nr);
8072 static void SaveTape_BODY(FILE *file, struct TapeInfo *tape)
8076 for (i = 0; i < tape->length; i++)
8078 if (tape->use_mouse)
8080 putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_LX]);
8081 putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_LY]);
8082 putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_BUTTON]);
8086 for (j = 0; j < MAX_PLAYERS; j++)
8087 if (tape->player_participates[j])
8088 putFile8Bit(file, tape->pos[i].action[j]);
8091 putFile8Bit(file, tape->pos[i].delay);
8095 void SaveTape(int nr)
8097 char *filename = getTapeFilename(nr);
8099 int num_participating_players = 0;
8101 int info_chunk_size;
8102 int body_chunk_size;
8105 InitTapeDirectory(leveldir_current->subdir);
8107 if (!(file = fopen(filename, MODE_WRITE)))
8109 Error(ERR_WARN, "cannot save level recording file '%s'", filename);
8113 tape.file_version = FILE_VERSION_ACTUAL;
8114 tape.game_version = GAME_VERSION_ACTUAL;
8116 /* count number of participating players */
8117 for (i = 0; i < MAX_PLAYERS; i++)
8118 if (tape.player_participates[i])
8119 num_participating_players++;
8121 tape_pos_size = (tape.use_mouse ? 3 : num_participating_players) + 1;
8123 info_chunk_size = 2 + (strlen(tape.level_identifier) + 1) + 2;
8124 body_chunk_size = tape_pos_size * tape.length;
8126 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
8127 putFileChunkBE(file, "TAPE", CHUNK_SIZE_NONE);
8129 putFileChunkBE(file, "VERS", TAPE_CHUNK_VERS_SIZE);
8130 SaveTape_VERS(file, &tape);
8132 putFileChunkBE(file, "HEAD", TAPE_CHUNK_HEAD_SIZE);
8133 SaveTape_HEAD(file, &tape);
8135 putFileChunkBE(file, "INFO", info_chunk_size);
8136 SaveTape_INFO(file, &tape);
8138 putFileChunkBE(file, "BODY", body_chunk_size);
8139 SaveTape_BODY(file, &tape);
8143 SetFilePermissions(filename, PERMS_PRIVATE);
8145 tape.changed = FALSE;
8148 static boolean SaveTapeCheckedExt(int nr, char *msg_replace, char *msg_saved)
8150 char *filename = getTapeFilename(nr);
8151 boolean new_tape = !fileExists(filename);
8152 boolean tape_saved = FALSE;
8154 if (new_tape || Request(msg_replace, REQ_ASK))
8159 Request(msg_saved, REQ_CONFIRM);
8167 boolean SaveTapeChecked(int nr)
8169 return SaveTapeCheckedExt(nr, "Replace old tape?", "Tape saved!");
8172 boolean SaveTapeChecked_LevelSolved(int nr)
8174 return SaveTapeCheckedExt(nr, "Level solved! Replace old tape?",
8175 "Level solved! Tape saved!");
8178 void DumpTape(struct TapeInfo *tape)
8180 int tape_frame_counter;
8183 if (tape->no_valid_file)
8185 Error(ERR_WARN, "cannot dump -- no valid tape file found");
8191 Print("Tape of Level %03d (file version %08d, game version %08d)\n",
8192 tape->level_nr, tape->file_version, tape->game_version);
8193 Print(" (effective engine version %08d)\n",
8194 tape->engine_version);
8195 Print("Level series identifier: '%s'\n", tape->level_identifier);
8198 tape_frame_counter = 0;
8200 for (i = 0; i < tape->length; i++)
8202 if (i >= MAX_TAPE_LEN)
8207 for (j = 0; j < MAX_PLAYERS; j++)
8209 if (tape->player_participates[j])
8211 int action = tape->pos[i].action[j];
8213 Print("%d:%02x ", j, action);
8214 Print("[%c%c%c%c|%c%c] - ",
8215 (action & JOY_LEFT ? '<' : ' '),
8216 (action & JOY_RIGHT ? '>' : ' '),
8217 (action & JOY_UP ? '^' : ' '),
8218 (action & JOY_DOWN ? 'v' : ' '),
8219 (action & JOY_BUTTON_1 ? '1' : ' '),
8220 (action & JOY_BUTTON_2 ? '2' : ' '));
8224 Print("(%03d) ", tape->pos[i].delay);
8225 Print("[%05d]\n", tape_frame_counter);
8227 tape_frame_counter += tape->pos[i].delay;
8234 /* ========================================================================= */
8235 /* score file functions */
8236 /* ========================================================================= */
8238 void LoadScore(int nr)
8241 char *filename = getScoreFilename(nr);
8242 char cookie[MAX_LINE_LEN];
8243 char line[MAX_LINE_LEN];
8247 /* always start with reliable default values */
8248 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
8250 strcpy(highscore[i].Name, EMPTY_PLAYER_NAME);
8251 highscore[i].Score = 0;
8254 if (!(file = fopen(filename, MODE_READ)))
8257 /* check file identifier */
8258 if (fgets(cookie, MAX_LINE_LEN, file) == NULL)
8260 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
8261 cookie[strlen(cookie) - 1] = '\0';
8263 if (!checkCookieString(cookie, SCORE_COOKIE))
8265 Error(ERR_WARN, "unknown format of score file '%s'", filename);
8270 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
8272 if (fscanf(file, "%d", &highscore[i].Score) == EOF)
8273 Error(ERR_WARN, "fscanf() failed; %s", strerror(errno));
8274 if (fgets(line, MAX_LINE_LEN, file) == NULL)
8277 if (strlen(line) > 0 && line[strlen(line) - 1] == '\n')
8278 line[strlen(line) - 1] = '\0';
8280 for (line_ptr = line; *line_ptr; line_ptr++)
8282 if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != '\0')
8284 strncpy(highscore[i].Name, line_ptr, MAX_PLAYER_NAME_LEN);
8285 highscore[i].Name[MAX_PLAYER_NAME_LEN] = '\0';
8294 void SaveScore(int nr)
8297 int permissions = (program.global_scores ? PERMS_PUBLIC : PERMS_PRIVATE);
8298 char *filename = getScoreFilename(nr);
8301 /* used instead of "leveldir_current->subdir" (for network games) */
8302 InitScoreDirectory(levelset.identifier);
8304 if (!(file = fopen(filename, MODE_WRITE)))
8306 Error(ERR_WARN, "cannot save score for level %d", nr);
8310 fprintf(file, "%s\n\n", SCORE_COOKIE);
8312 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
8313 fprintf(file, "%d %s\n", highscore[i].Score, highscore[i].Name);
8317 SetFilePermissions(filename, permissions);
8321 /* ========================================================================= */
8322 /* setup file functions */
8323 /* ========================================================================= */
8325 #define TOKEN_STR_PLAYER_PREFIX "player_"
8330 SETUP_TOKEN_PLAYER_NAME = 0,
8332 SETUP_TOKEN_SOUND_LOOPS,
8333 SETUP_TOKEN_SOUND_MUSIC,
8334 SETUP_TOKEN_SOUND_SIMPLE,
8336 SETUP_TOKEN_SCROLL_DELAY,
8337 SETUP_TOKEN_SCROLL_DELAY_VALUE,
8338 SETUP_TOKEN_ENGINE_SNAPSHOT_MODE,
8339 SETUP_TOKEN_ENGINE_SNAPSHOT_MEMORY,
8340 SETUP_TOKEN_FADE_SCREENS,
8341 SETUP_TOKEN_AUTORECORD,
8342 SETUP_TOKEN_SHOW_TITLESCREEN,
8343 SETUP_TOKEN_QUICK_DOORS,
8344 SETUP_TOKEN_TEAM_MODE,
8345 SETUP_TOKEN_HANDICAP,
8346 SETUP_TOKEN_SKIP_LEVELS,
8347 SETUP_TOKEN_INCREMENT_LEVELS,
8348 SETUP_TOKEN_AUTO_PLAY_NEXT_LEVEL,
8349 SETUP_TOKEN_SKIP_SCORES_AFTER_GAME,
8350 SETUP_TOKEN_TIME_LIMIT,
8351 SETUP_TOKEN_FULLSCREEN,
8352 SETUP_TOKEN_WINDOW_SCALING_PERCENT,
8353 SETUP_TOKEN_WINDOW_SCALING_QUALITY,
8354 SETUP_TOKEN_SCREEN_RENDERING_MODE,
8355 SETUP_TOKEN_ASK_ON_ESCAPE,
8356 SETUP_TOKEN_ASK_ON_ESCAPE_EDITOR,
8357 SETUP_TOKEN_QUICK_SWITCH,
8358 SETUP_TOKEN_INPUT_ON_FOCUS,
8359 SETUP_TOKEN_PREFER_AGA_GRAPHICS,
8360 SETUP_TOKEN_GAME_FRAME_DELAY,
8361 SETUP_TOKEN_SP_SHOW_BORDER_ELEMENTS,
8362 SETUP_TOKEN_SMALL_GAME_GRAPHICS,
8363 SETUP_TOKEN_SHOW_SNAPSHOT_BUTTONS,
8364 SETUP_TOKEN_GRAPHICS_SET,
8365 SETUP_TOKEN_SOUNDS_SET,
8366 SETUP_TOKEN_MUSIC_SET,
8367 SETUP_TOKEN_OVERRIDE_LEVEL_GRAPHICS,
8368 SETUP_TOKEN_OVERRIDE_LEVEL_SOUNDS,
8369 SETUP_TOKEN_OVERRIDE_LEVEL_MUSIC,
8370 SETUP_TOKEN_VOLUME_SIMPLE,
8371 SETUP_TOKEN_VOLUME_LOOPS,
8372 SETUP_TOKEN_VOLUME_MUSIC,
8373 SETUP_TOKEN_NETWORK_MODE,
8374 SETUP_TOKEN_NETWORK_PLAYER_NR,
8375 SETUP_TOKEN_TOUCH_CONTROL_TYPE,
8376 SETUP_TOKEN_TOUCH_MOVE_DISTANCE,
8377 SETUP_TOKEN_TOUCH_DROP_DISTANCE,
8378 SETUP_TOKEN_TOUCH_TRANSPARENCY,
8379 SETUP_TOKEN_TOUCH_DRAW_OUTLINED,
8380 SETUP_TOKEN_TOUCH_DRAW_PRESSED,
8381 SETUP_TOKEN_TOUCH_GRID_XSIZE_0,
8382 SETUP_TOKEN_TOUCH_GRID_YSIZE_0,
8383 SETUP_TOKEN_TOUCH_GRID_XSIZE_1,
8384 SETUP_TOKEN_TOUCH_GRID_YSIZE_1,
8386 NUM_GLOBAL_SETUP_TOKENS
8392 SETUP_TOKEN_AUTO_EDITOR_ZOOM_TILESIZE = 0,
8394 NUM_AUTO_SETUP_TOKENS
8400 SETUP_TOKEN_EDITOR_EL_CLASSIC = 0,
8401 SETUP_TOKEN_EDITOR_EL_CUSTOM,
8402 SETUP_TOKEN_EDITOR_EL_USER_DEFINED,
8403 SETUP_TOKEN_EDITOR_EL_DYNAMIC,
8404 SETUP_TOKEN_EDITOR_EL_HEADLINES,
8405 SETUP_TOKEN_EDITOR_SHOW_ELEMENT_TOKEN,
8407 NUM_EDITOR_SETUP_TOKENS
8410 /* editor cascade setup */
8413 SETUP_TOKEN_EDITOR_CASCADE_BD = 0,
8414 SETUP_TOKEN_EDITOR_CASCADE_EM,
8415 SETUP_TOKEN_EDITOR_CASCADE_EMC,
8416 SETUP_TOKEN_EDITOR_CASCADE_RND,
8417 SETUP_TOKEN_EDITOR_CASCADE_SB,
8418 SETUP_TOKEN_EDITOR_CASCADE_SP,
8419 SETUP_TOKEN_EDITOR_CASCADE_DC,
8420 SETUP_TOKEN_EDITOR_CASCADE_DX,
8421 SETUP_TOKEN_EDITOR_CASCADE_TEXT,
8422 SETUP_TOKEN_EDITOR_CASCADE_STEELTEXT,
8423 SETUP_TOKEN_EDITOR_CASCADE_CE,
8424 SETUP_TOKEN_EDITOR_CASCADE_GE,
8425 SETUP_TOKEN_EDITOR_CASCADE_REF,
8426 SETUP_TOKEN_EDITOR_CASCADE_USER,
8427 SETUP_TOKEN_EDITOR_CASCADE_DYNAMIC,
8429 NUM_EDITOR_CASCADE_SETUP_TOKENS
8432 /* shortcut setup */
8435 SETUP_TOKEN_SHORTCUT_SAVE_GAME = 0,
8436 SETUP_TOKEN_SHORTCUT_LOAD_GAME,
8437 SETUP_TOKEN_SHORTCUT_TOGGLE_PAUSE,
8438 SETUP_TOKEN_SHORTCUT_FOCUS_PLAYER_1,
8439 SETUP_TOKEN_SHORTCUT_FOCUS_PLAYER_2,
8440 SETUP_TOKEN_SHORTCUT_FOCUS_PLAYER_3,
8441 SETUP_TOKEN_SHORTCUT_FOCUS_PLAYER_4,
8442 SETUP_TOKEN_SHORTCUT_FOCUS_PLAYER_ALL,
8443 SETUP_TOKEN_SHORTCUT_TAPE_EJECT,
8444 SETUP_TOKEN_SHORTCUT_TAPE_EXTRA,
8445 SETUP_TOKEN_SHORTCUT_TAPE_STOP,
8446 SETUP_TOKEN_SHORTCUT_TAPE_PAUSE,
8447 SETUP_TOKEN_SHORTCUT_TAPE_RECORD,
8448 SETUP_TOKEN_SHORTCUT_TAPE_PLAY,
8449 SETUP_TOKEN_SHORTCUT_SOUND_SIMPLE,
8450 SETUP_TOKEN_SHORTCUT_SOUND_LOOPS,
8451 SETUP_TOKEN_SHORTCUT_SOUND_MUSIC,
8452 SETUP_TOKEN_SHORTCUT_SNAP_LEFT,
8453 SETUP_TOKEN_SHORTCUT_SNAP_RIGHT,
8454 SETUP_TOKEN_SHORTCUT_SNAP_UP,
8455 SETUP_TOKEN_SHORTCUT_SNAP_DOWN,
8457 NUM_SHORTCUT_SETUP_TOKENS
8463 SETUP_TOKEN_PLAYER_USE_JOYSTICK = 0,
8464 SETUP_TOKEN_PLAYER_JOY_DEVICE_NAME,
8465 SETUP_TOKEN_PLAYER_JOY_XLEFT,
8466 SETUP_TOKEN_PLAYER_JOY_XMIDDLE,
8467 SETUP_TOKEN_PLAYER_JOY_XRIGHT,
8468 SETUP_TOKEN_PLAYER_JOY_YUPPER,
8469 SETUP_TOKEN_PLAYER_JOY_YMIDDLE,
8470 SETUP_TOKEN_PLAYER_JOY_YLOWER,
8471 SETUP_TOKEN_PLAYER_JOY_SNAP,
8472 SETUP_TOKEN_PLAYER_JOY_DROP,
8473 SETUP_TOKEN_PLAYER_KEY_LEFT,
8474 SETUP_TOKEN_PLAYER_KEY_RIGHT,
8475 SETUP_TOKEN_PLAYER_KEY_UP,
8476 SETUP_TOKEN_PLAYER_KEY_DOWN,
8477 SETUP_TOKEN_PLAYER_KEY_SNAP,
8478 SETUP_TOKEN_PLAYER_KEY_DROP,
8480 NUM_PLAYER_SETUP_TOKENS
8486 SETUP_TOKEN_SYSTEM_SDL_VIDEODRIVER = 0,
8487 SETUP_TOKEN_SYSTEM_SDL_AUDIODRIVER,
8488 SETUP_TOKEN_SYSTEM_AUDIO_FRAGMENT_SIZE,
8490 NUM_SYSTEM_SETUP_TOKENS
8493 /* internal setup */
8496 SETUP_TOKEN_INT_PROGRAM_TITLE = 0,
8497 SETUP_TOKEN_INT_PROGRAM_VERSION,
8498 SETUP_TOKEN_INT_PROGRAM_AUTHOR,
8499 SETUP_TOKEN_INT_PROGRAM_EMAIL,
8500 SETUP_TOKEN_INT_PROGRAM_WEBSITE,
8501 SETUP_TOKEN_INT_PROGRAM_COPYRIGHT,
8502 SETUP_TOKEN_INT_PROGRAM_COMPANY,
8503 SETUP_TOKEN_INT_PROGRAM_ICON_FILE,
8504 SETUP_TOKEN_INT_DEFAULT_GRAPHICS_SET,
8505 SETUP_TOKEN_INT_DEFAULT_SOUNDS_SET,
8506 SETUP_TOKEN_INT_DEFAULT_MUSIC_SET,
8507 SETUP_TOKEN_INT_FALLBACK_GRAPHICS_FILE,
8508 SETUP_TOKEN_INT_FALLBACK_SOUNDS_FILE,
8509 SETUP_TOKEN_INT_FALLBACK_MUSIC_FILE,
8510 SETUP_TOKEN_INT_DEFAULT_LEVEL_SERIES,
8511 SETUP_TOKEN_INT_CHOOSE_FROM_TOP_LEVELDIR,
8512 SETUP_TOKEN_INT_SHOW_SCALING_IN_TITLE,
8513 SETUP_TOKEN_INT_DEFAULT_WINDOW_WIDTH,
8514 SETUP_TOKEN_INT_DEFAULT_WINDOW_HEIGHT,
8516 NUM_INTERNAL_SETUP_TOKENS
8522 SETUP_TOKEN_DEBUG_FRAME_DELAY_0 = 0,
8523 SETUP_TOKEN_DEBUG_FRAME_DELAY_1,
8524 SETUP_TOKEN_DEBUG_FRAME_DELAY_2,
8525 SETUP_TOKEN_DEBUG_FRAME_DELAY_3,
8526 SETUP_TOKEN_DEBUG_FRAME_DELAY_4,
8527 SETUP_TOKEN_DEBUG_FRAME_DELAY_5,
8528 SETUP_TOKEN_DEBUG_FRAME_DELAY_6,
8529 SETUP_TOKEN_DEBUG_FRAME_DELAY_7,
8530 SETUP_TOKEN_DEBUG_FRAME_DELAY_8,
8531 SETUP_TOKEN_DEBUG_FRAME_DELAY_9,
8532 SETUP_TOKEN_DEBUG_FRAME_DELAY_KEY_0,
8533 SETUP_TOKEN_DEBUG_FRAME_DELAY_KEY_1,
8534 SETUP_TOKEN_DEBUG_FRAME_DELAY_KEY_2,
8535 SETUP_TOKEN_DEBUG_FRAME_DELAY_KEY_3,
8536 SETUP_TOKEN_DEBUG_FRAME_DELAY_KEY_4,
8537 SETUP_TOKEN_DEBUG_FRAME_DELAY_KEY_5,
8538 SETUP_TOKEN_DEBUG_FRAME_DELAY_KEY_6,
8539 SETUP_TOKEN_DEBUG_FRAME_DELAY_KEY_7,
8540 SETUP_TOKEN_DEBUG_FRAME_DELAY_KEY_8,
8541 SETUP_TOKEN_DEBUG_FRAME_DELAY_KEY_9,
8542 SETUP_TOKEN_DEBUG_FRAME_DELAY_USE_MOD_KEY,
8543 SETUP_TOKEN_DEBUG_FRAME_DELAY_GAME_ONLY,
8544 SETUP_TOKEN_DEBUG_SHOW_FRAMES_PER_SECOND,
8546 NUM_DEBUG_SETUP_TOKENS
8552 SETUP_TOKEN_OPTIONS_VERBOSE = 0,
8554 NUM_OPTIONS_SETUP_TOKENS
8558 static struct SetupInfo si;
8559 static struct SetupAutoSetupInfo sasi;
8560 static struct SetupEditorInfo sei;
8561 static struct SetupEditorCascadeInfo seci;
8562 static struct SetupShortcutInfo ssi;
8563 static struct SetupInputInfo sii;
8564 static struct SetupSystemInfo syi;
8565 static struct SetupInternalInfo sxi;
8566 static struct SetupDebugInfo sdi;
8567 static struct OptionInfo soi;
8569 static struct TokenInfo global_setup_tokens[] =
8571 { TYPE_STRING, &si.player_name, "player_name" },
8572 { TYPE_SWITCH, &si.sound, "sound" },
8573 { TYPE_SWITCH, &si.sound_loops, "repeating_sound_loops" },
8574 { TYPE_SWITCH, &si.sound_music, "background_music" },
8575 { TYPE_SWITCH, &si.sound_simple, "simple_sound_effects" },
8576 { TYPE_SWITCH, &si.toons, "toons" },
8577 { TYPE_SWITCH, &si.scroll_delay, "scroll_delay" },
8578 { TYPE_INTEGER,&si.scroll_delay_value, "scroll_delay_value" },
8579 { TYPE_STRING, &si.engine_snapshot_mode, "engine_snapshot_mode" },
8580 { TYPE_INTEGER,&si.engine_snapshot_memory, "engine_snapshot_memory" },
8581 { TYPE_SWITCH, &si.fade_screens, "fade_screens" },
8582 { TYPE_SWITCH, &si.autorecord, "automatic_tape_recording"},
8583 { TYPE_SWITCH, &si.show_titlescreen, "show_titlescreen" },
8584 { TYPE_SWITCH, &si.quick_doors, "quick_doors" },
8585 { TYPE_SWITCH, &si.team_mode, "team_mode" },
8586 { TYPE_SWITCH, &si.handicap, "handicap" },
8587 { TYPE_SWITCH, &si.skip_levels, "skip_levels" },
8588 { TYPE_SWITCH, &si.increment_levels, "increment_levels" },
8589 { TYPE_SWITCH, &si.auto_play_next_level, "auto_play_next_level" },
8590 { TYPE_SWITCH, &si.skip_scores_after_game, "skip_scores_after_game" },
8591 { TYPE_SWITCH, &si.time_limit, "time_limit" },
8592 { TYPE_SWITCH, &si.fullscreen, "fullscreen" },
8593 { TYPE_INTEGER,&si.window_scaling_percent, "window_scaling_percent" },
8594 { TYPE_STRING, &si.window_scaling_quality, "window_scaling_quality" },
8595 { TYPE_STRING, &si.screen_rendering_mode, "screen_rendering_mode" },
8596 { TYPE_SWITCH, &si.ask_on_escape, "ask_on_escape" },
8597 { TYPE_SWITCH, &si.ask_on_escape_editor, "ask_on_escape_editor" },
8598 { TYPE_SWITCH, &si.quick_switch, "quick_player_switch" },
8599 { TYPE_SWITCH, &si.input_on_focus, "input_on_focus" },
8600 { TYPE_SWITCH, &si.prefer_aga_graphics, "prefer_aga_graphics" },
8601 { TYPE_INTEGER,&si.game_frame_delay, "game_frame_delay" },
8602 { TYPE_SWITCH, &si.sp_show_border_elements, "sp_show_border_elements" },
8603 { TYPE_SWITCH, &si.small_game_graphics, "small_game_graphics" },
8604 { TYPE_SWITCH, &si.show_snapshot_buttons, "show_snapshot_buttons" },
8605 { TYPE_STRING, &si.graphics_set, "graphics_set" },
8606 { TYPE_STRING, &si.sounds_set, "sounds_set" },
8607 { TYPE_STRING, &si.music_set, "music_set" },
8608 { TYPE_SWITCH3,&si.override_level_graphics, "override_level_graphics" },
8609 { TYPE_SWITCH3,&si.override_level_sounds, "override_level_sounds" },
8610 { TYPE_SWITCH3,&si.override_level_music, "override_level_music" },
8611 { TYPE_INTEGER,&si.volume_simple, "volume_simple" },
8612 { TYPE_INTEGER,&si.volume_loops, "volume_loops" },
8613 { TYPE_INTEGER,&si.volume_music, "volume_music" },
8614 { TYPE_SWITCH, &si.network_mode, "network_mode" },
8615 { TYPE_PLAYER, &si.network_player_nr, "network_player" },
8616 { TYPE_STRING, &si.touch.control_type, "touch.control_type" },
8617 { TYPE_INTEGER,&si.touch.move_distance, "touch.move_distance" },
8618 { TYPE_INTEGER,&si.touch.drop_distance, "touch.drop_distance" },
8619 { TYPE_INTEGER,&si.touch.transparency, "touch.transparency" },
8620 { TYPE_INTEGER,&si.touch.draw_outlined, "touch.draw_outlined" },
8621 { TYPE_INTEGER,&si.touch.draw_pressed, "touch.draw_pressed" },
8622 { TYPE_INTEGER,&si.touch.grid_xsize[0], "touch.virtual_buttons.0.xsize" },
8623 { TYPE_INTEGER,&si.touch.grid_ysize[0], "touch.virtual_buttons.0.ysize" },
8624 { TYPE_INTEGER,&si.touch.grid_xsize[1], "touch.virtual_buttons.1.xsize" },
8625 { TYPE_INTEGER,&si.touch.grid_ysize[1], "touch.virtual_buttons.1.ysize" },
8628 static struct TokenInfo auto_setup_tokens[] =
8630 { TYPE_INTEGER,&sasi.editor_zoom_tilesize, "editor.zoom_tilesize" },
8633 static struct TokenInfo editor_setup_tokens[] =
8635 { TYPE_SWITCH, &sei.el_classic, "editor.el_classic" },
8636 { TYPE_SWITCH, &sei.el_custom, "editor.el_custom" },
8637 { TYPE_SWITCH, &sei.el_user_defined, "editor.el_user_defined" },
8638 { TYPE_SWITCH, &sei.el_dynamic, "editor.el_dynamic" },
8639 { TYPE_SWITCH, &sei.el_headlines, "editor.el_headlines" },
8640 { TYPE_SWITCH, &sei.show_element_token,"editor.show_element_token" },
8643 static struct TokenInfo editor_cascade_setup_tokens[] =
8645 { TYPE_SWITCH, &seci.el_bd, "editor.cascade.el_bd" },
8646 { TYPE_SWITCH, &seci.el_em, "editor.cascade.el_em" },
8647 { TYPE_SWITCH, &seci.el_emc, "editor.cascade.el_emc" },
8648 { TYPE_SWITCH, &seci.el_rnd, "editor.cascade.el_rnd" },
8649 { TYPE_SWITCH, &seci.el_sb, "editor.cascade.el_sb" },
8650 { TYPE_SWITCH, &seci.el_sp, "editor.cascade.el_sp" },
8651 { TYPE_SWITCH, &seci.el_dc, "editor.cascade.el_dc" },
8652 { TYPE_SWITCH, &seci.el_dx, "editor.cascade.el_dx" },
8653 { TYPE_SWITCH, &seci.el_mm, "editor.cascade.el_mm" },
8654 { TYPE_SWITCH, &seci.el_df, "editor.cascade.el_df" },
8655 { TYPE_SWITCH, &seci.el_chars, "editor.cascade.el_chars" },
8656 { TYPE_SWITCH, &seci.el_steel_chars, "editor.cascade.el_steel_chars" },
8657 { TYPE_SWITCH, &seci.el_ce, "editor.cascade.el_ce" },
8658 { TYPE_SWITCH, &seci.el_ge, "editor.cascade.el_ge" },
8659 { TYPE_SWITCH, &seci.el_ref, "editor.cascade.el_ref" },
8660 { TYPE_SWITCH, &seci.el_user, "editor.cascade.el_user" },
8661 { TYPE_SWITCH, &seci.el_dynamic, "editor.cascade.el_dynamic" },
8664 static struct TokenInfo shortcut_setup_tokens[] =
8666 { TYPE_KEY_X11, &ssi.save_game, "shortcut.save_game" },
8667 { TYPE_KEY_X11, &ssi.load_game, "shortcut.load_game" },
8668 { TYPE_KEY_X11, &ssi.toggle_pause, "shortcut.toggle_pause" },
8669 { TYPE_KEY_X11, &ssi.focus_player[0], "shortcut.focus_player_1" },
8670 { TYPE_KEY_X11, &ssi.focus_player[1], "shortcut.focus_player_2" },
8671 { TYPE_KEY_X11, &ssi.focus_player[2], "shortcut.focus_player_3" },
8672 { TYPE_KEY_X11, &ssi.focus_player[3], "shortcut.focus_player_4" },
8673 { TYPE_KEY_X11, &ssi.focus_player_all,"shortcut.focus_player_all" },
8674 { TYPE_KEY_X11, &ssi.tape_eject, "shortcut.tape_eject" },
8675 { TYPE_KEY_X11, &ssi.tape_extra, "shortcut.tape_extra" },
8676 { TYPE_KEY_X11, &ssi.tape_stop, "shortcut.tape_stop" },
8677 { TYPE_KEY_X11, &ssi.tape_pause, "shortcut.tape_pause" },
8678 { TYPE_KEY_X11, &ssi.tape_record, "shortcut.tape_record" },
8679 { TYPE_KEY_X11, &ssi.tape_play, "shortcut.tape_play" },
8680 { TYPE_KEY_X11, &ssi.sound_simple, "shortcut.sound_simple" },
8681 { TYPE_KEY_X11, &ssi.sound_loops, "shortcut.sound_loops" },
8682 { TYPE_KEY_X11, &ssi.sound_music, "shortcut.sound_music" },
8683 { TYPE_KEY_X11, &ssi.snap_left, "shortcut.snap_left" },
8684 { TYPE_KEY_X11, &ssi.snap_right, "shortcut.snap_right" },
8685 { TYPE_KEY_X11, &ssi.snap_up, "shortcut.snap_up" },
8686 { TYPE_KEY_X11, &ssi.snap_down, "shortcut.snap_down" },
8689 static struct TokenInfo player_setup_tokens[] =
8691 { TYPE_BOOLEAN, &sii.use_joystick, ".use_joystick" },
8692 { TYPE_STRING, &sii.joy.device_name, ".joy.device_name" },
8693 { TYPE_INTEGER, &sii.joy.xleft, ".joy.xleft" },
8694 { TYPE_INTEGER, &sii.joy.xmiddle, ".joy.xmiddle" },
8695 { TYPE_INTEGER, &sii.joy.xright, ".joy.xright" },
8696 { TYPE_INTEGER, &sii.joy.yupper, ".joy.yupper" },
8697 { TYPE_INTEGER, &sii.joy.ymiddle, ".joy.ymiddle" },
8698 { TYPE_INTEGER, &sii.joy.ylower, ".joy.ylower" },
8699 { TYPE_INTEGER, &sii.joy.snap, ".joy.snap_field" },
8700 { TYPE_INTEGER, &sii.joy.drop, ".joy.place_bomb" },
8701 { TYPE_KEY_X11, &sii.key.left, ".key.move_left" },
8702 { TYPE_KEY_X11, &sii.key.right, ".key.move_right" },
8703 { TYPE_KEY_X11, &sii.key.up, ".key.move_up" },
8704 { TYPE_KEY_X11, &sii.key.down, ".key.move_down" },
8705 { TYPE_KEY_X11, &sii.key.snap, ".key.snap_field" },
8706 { TYPE_KEY_X11, &sii.key.drop, ".key.place_bomb" },
8709 static struct TokenInfo system_setup_tokens[] =
8711 { TYPE_STRING, &syi.sdl_videodriver, "system.sdl_videodriver" },
8712 { TYPE_STRING, &syi.sdl_audiodriver, "system.sdl_audiodriver" },
8713 { TYPE_INTEGER, &syi.audio_fragment_size,"system.audio_fragment_size" },
8716 static struct TokenInfo internal_setup_tokens[] =
8718 { TYPE_STRING, &sxi.program_title, "program_title" },
8719 { TYPE_STRING, &sxi.program_version, "program_version" },
8720 { TYPE_STRING, &sxi.program_author, "program_author" },
8721 { TYPE_STRING, &sxi.program_email, "program_email" },
8722 { TYPE_STRING, &sxi.program_website, "program_website" },
8723 { TYPE_STRING, &sxi.program_copyright, "program_copyright" },
8724 { TYPE_STRING, &sxi.program_company, "program_company" },
8725 { TYPE_STRING, &sxi.program_icon_file, "program_icon_file" },
8726 { TYPE_STRING, &sxi.default_graphics_set, "default_graphics_set" },
8727 { TYPE_STRING, &sxi.default_sounds_set, "default_sounds_set" },
8728 { TYPE_STRING, &sxi.default_music_set, "default_music_set" },
8729 { TYPE_STRING, &sxi.fallback_graphics_file, "fallback_graphics_file"},
8730 { TYPE_STRING, &sxi.fallback_sounds_file, "fallback_sounds_file" },
8731 { TYPE_STRING, &sxi.fallback_music_file, "fallback_music_file" },
8732 { TYPE_STRING, &sxi.default_level_series, "default_level_series" },
8733 { TYPE_BOOLEAN,&sxi.choose_from_top_leveldir, "choose_from_top_leveldir" },
8734 { TYPE_BOOLEAN,&sxi.show_scaling_in_title, "show_scaling_in_title" },
8735 { TYPE_INTEGER,&sxi.default_window_width, "default_window_width" },
8736 { TYPE_INTEGER,&sxi.default_window_height, "default_window_height" },
8739 static struct TokenInfo debug_setup_tokens[] =
8741 { TYPE_INTEGER, &sdi.frame_delay[0], "debug.frame_delay_0" },
8742 { TYPE_INTEGER, &sdi.frame_delay[1], "debug.frame_delay_1" },
8743 { TYPE_INTEGER, &sdi.frame_delay[2], "debug.frame_delay_2" },
8744 { TYPE_INTEGER, &sdi.frame_delay[3], "debug.frame_delay_3" },
8745 { TYPE_INTEGER, &sdi.frame_delay[4], "debug.frame_delay_4" },
8746 { TYPE_INTEGER, &sdi.frame_delay[5], "debug.frame_delay_5" },
8747 { TYPE_INTEGER, &sdi.frame_delay[6], "debug.frame_delay_6" },
8748 { TYPE_INTEGER, &sdi.frame_delay[7], "debug.frame_delay_7" },
8749 { TYPE_INTEGER, &sdi.frame_delay[8], "debug.frame_delay_8" },
8750 { TYPE_INTEGER, &sdi.frame_delay[9], "debug.frame_delay_9" },
8751 { TYPE_KEY_X11, &sdi.frame_delay_key[0], "debug.key.frame_delay_0" },
8752 { TYPE_KEY_X11, &sdi.frame_delay_key[1], "debug.key.frame_delay_1" },
8753 { TYPE_KEY_X11, &sdi.frame_delay_key[2], "debug.key.frame_delay_2" },
8754 { TYPE_KEY_X11, &sdi.frame_delay_key[3], "debug.key.frame_delay_3" },
8755 { TYPE_KEY_X11, &sdi.frame_delay_key[4], "debug.key.frame_delay_4" },
8756 { TYPE_KEY_X11, &sdi.frame_delay_key[5], "debug.key.frame_delay_5" },
8757 { TYPE_KEY_X11, &sdi.frame_delay_key[6], "debug.key.frame_delay_6" },
8758 { TYPE_KEY_X11, &sdi.frame_delay_key[7], "debug.key.frame_delay_7" },
8759 { TYPE_KEY_X11, &sdi.frame_delay_key[8], "debug.key.frame_delay_8" },
8760 { TYPE_KEY_X11, &sdi.frame_delay_key[9], "debug.key.frame_delay_9" },
8761 { TYPE_BOOLEAN, &sdi.frame_delay_use_mod_key,"debug.frame_delay.use_mod_key"},
8762 { TYPE_BOOLEAN, &sdi.frame_delay_game_only, "debug.frame_delay.game_only" },
8763 { TYPE_BOOLEAN, &sdi.show_frames_per_second, "debug.show_frames_per_second" },
8766 static struct TokenInfo options_setup_tokens[] =
8768 { TYPE_BOOLEAN, &soi.verbose, "options.verbose" },
8771 static char *get_corrected_login_name(char *login_name)
8773 /* needed because player name must be a fixed length string */
8774 char *login_name_new = checked_malloc(MAX_PLAYER_NAME_LEN + 1);
8776 strncpy(login_name_new, login_name, MAX_PLAYER_NAME_LEN);
8777 login_name_new[MAX_PLAYER_NAME_LEN] = '\0';
8779 if (strlen(login_name) > MAX_PLAYER_NAME_LEN) /* name has been cut */
8780 if (strchr(login_name_new, ' '))
8781 *strchr(login_name_new, ' ') = '\0';
8783 return login_name_new;
8786 static void setSetupInfoToDefaults(struct SetupInfo *si)
8790 si->player_name = get_corrected_login_name(getLoginName());
8793 si->sound_loops = TRUE;
8794 si->sound_music = TRUE;
8795 si->sound_simple = TRUE;
8797 si->scroll_delay = TRUE;
8798 si->scroll_delay_value = STD_SCROLL_DELAY;
8799 si->engine_snapshot_mode = getStringCopy(STR_SNAPSHOT_MODE_DEFAULT);
8800 si->engine_snapshot_memory = SNAPSHOT_MEMORY_DEFAULT;
8801 si->fade_screens = TRUE;
8802 si->autorecord = TRUE;
8803 si->show_titlescreen = TRUE;
8804 si->quick_doors = FALSE;
8805 si->team_mode = FALSE;
8806 si->handicap = TRUE;
8807 si->skip_levels = TRUE;
8808 si->increment_levels = TRUE;
8809 si->auto_play_next_level = TRUE;
8810 si->skip_scores_after_game = FALSE;
8811 si->time_limit = TRUE;
8812 si->fullscreen = FALSE;
8813 si->window_scaling_percent = STD_WINDOW_SCALING_PERCENT;
8814 si->window_scaling_quality = getStringCopy(SCALING_QUALITY_DEFAULT);
8815 si->screen_rendering_mode = getStringCopy(STR_SPECIAL_RENDERING_DEFAULT);
8816 si->ask_on_escape = TRUE;
8817 si->ask_on_escape_editor = TRUE;
8818 si->quick_switch = FALSE;
8819 si->input_on_focus = FALSE;
8820 si->prefer_aga_graphics = TRUE;
8821 si->game_frame_delay = GAME_FRAME_DELAY;
8822 si->sp_show_border_elements = FALSE;
8823 si->small_game_graphics = FALSE;
8824 si->show_snapshot_buttons = FALSE;
8826 si->graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
8827 si->sounds_set = getStringCopy(SND_CLASSIC_SUBDIR);
8828 si->music_set = getStringCopy(MUS_CLASSIC_SUBDIR);
8830 si->override_level_graphics = FALSE;
8831 si->override_level_sounds = FALSE;
8832 si->override_level_music = FALSE;
8834 si->volume_simple = 100; /* percent */
8835 si->volume_loops = 100; /* percent */
8836 si->volume_music = 100; /* percent */
8838 si->network_mode = FALSE;
8839 si->network_player_nr = 0; /* first player */
8841 si->touch.control_type = getStringCopy(TOUCH_CONTROL_DEFAULT);
8842 si->touch.move_distance = TOUCH_MOVE_DISTANCE_DEFAULT; /* percent */
8843 si->touch.drop_distance = TOUCH_DROP_DISTANCE_DEFAULT; /* percent */
8844 si->touch.transparency = TOUCH_TRANSPARENCY_DEFAULT; /* percent */
8845 si->touch.draw_outlined = TRUE;
8846 si->touch.draw_pressed = TRUE;
8848 for (i = 0; i < 2; i++)
8850 char *default_grid_button[6][2] =
8856 { "111222", " vv " },
8857 { "111222", " vv " }
8859 int grid_xsize = DEFAULT_GRID_XSIZE(i);
8860 int grid_ysize = DEFAULT_GRID_YSIZE(i);
8861 int min_xsize = MIN(6, grid_xsize);
8862 int min_ysize = MIN(6, grid_ysize);
8863 int startx = grid_xsize - min_xsize;
8864 int starty = grid_ysize - min_ysize;
8867 // virtual buttons grid can only be set to defaults if video is initialized
8868 // (this will be repeated if virtual buttons are not loaded from setup file)
8869 if (video.initialized)
8871 si->touch.grid_xsize[i] = grid_xsize;
8872 si->touch.grid_ysize[i] = grid_ysize;
8876 si->touch.grid_xsize[i] = -1;
8877 si->touch.grid_ysize[i] = -1;
8880 for (x = 0; x < MAX_GRID_XSIZE; x++)
8881 for (y = 0; y < MAX_GRID_YSIZE; y++)
8882 si->touch.grid_button[i][x][y] = CHAR_GRID_BUTTON_NONE;
8884 for (x = 0; x < min_xsize; x++)
8885 for (y = 0; y < min_ysize; y++)
8886 si->touch.grid_button[i][x][starty + y] =
8887 default_grid_button[y][0][x];
8889 for (x = 0; x < min_xsize; x++)
8890 for (y = 0; y < min_ysize; y++)
8891 si->touch.grid_button[i][startx + x][starty + y] =
8892 default_grid_button[y][1][x];
8895 si->touch.grid_initialized = video.initialized;
8897 si->editor.el_boulderdash = TRUE;
8898 si->editor.el_emerald_mine = TRUE;
8899 si->editor.el_emerald_mine_club = TRUE;
8900 si->editor.el_more = TRUE;
8901 si->editor.el_sokoban = TRUE;
8902 si->editor.el_supaplex = TRUE;
8903 si->editor.el_diamond_caves = TRUE;
8904 si->editor.el_dx_boulderdash = TRUE;
8906 si->editor.el_mirror_magic = TRUE;
8907 si->editor.el_deflektor = TRUE;
8909 si->editor.el_chars = TRUE;
8910 si->editor.el_steel_chars = TRUE;
8912 si->editor.el_classic = TRUE;
8913 si->editor.el_custom = TRUE;
8915 si->editor.el_user_defined = FALSE;
8916 si->editor.el_dynamic = TRUE;
8918 si->editor.el_headlines = TRUE;
8920 si->editor.show_element_token = FALSE;
8922 si->editor.use_template_for_new_levels = TRUE;
8924 si->shortcut.save_game = DEFAULT_KEY_SAVE_GAME;
8925 si->shortcut.load_game = DEFAULT_KEY_LOAD_GAME;
8926 si->shortcut.toggle_pause = DEFAULT_KEY_TOGGLE_PAUSE;
8928 si->shortcut.focus_player[0] = DEFAULT_KEY_FOCUS_PLAYER_1;
8929 si->shortcut.focus_player[1] = DEFAULT_KEY_FOCUS_PLAYER_2;
8930 si->shortcut.focus_player[2] = DEFAULT_KEY_FOCUS_PLAYER_3;
8931 si->shortcut.focus_player[3] = DEFAULT_KEY_FOCUS_PLAYER_4;
8932 si->shortcut.focus_player_all = DEFAULT_KEY_FOCUS_PLAYER_ALL;
8934 si->shortcut.tape_eject = DEFAULT_KEY_TAPE_EJECT;
8935 si->shortcut.tape_extra = DEFAULT_KEY_TAPE_EXTRA;
8936 si->shortcut.tape_stop = DEFAULT_KEY_TAPE_STOP;
8937 si->shortcut.tape_pause = DEFAULT_KEY_TAPE_PAUSE;
8938 si->shortcut.tape_record = DEFAULT_KEY_TAPE_RECORD;
8939 si->shortcut.tape_play = DEFAULT_KEY_TAPE_PLAY;
8941 si->shortcut.sound_simple = DEFAULT_KEY_SOUND_SIMPLE;
8942 si->shortcut.sound_loops = DEFAULT_KEY_SOUND_LOOPS;
8943 si->shortcut.sound_music = DEFAULT_KEY_SOUND_MUSIC;
8945 si->shortcut.snap_left = DEFAULT_KEY_SNAP_LEFT;
8946 si->shortcut.snap_right = DEFAULT_KEY_SNAP_RIGHT;
8947 si->shortcut.snap_up = DEFAULT_KEY_SNAP_UP;
8948 si->shortcut.snap_down = DEFAULT_KEY_SNAP_DOWN;
8950 for (i = 0; i < MAX_PLAYERS; i++)
8952 si->input[i].use_joystick = FALSE;
8953 si->input[i].joy.device_name=getStringCopy(getDeviceNameFromJoystickNr(i));
8954 si->input[i].joy.xleft = JOYSTICK_XLEFT;
8955 si->input[i].joy.xmiddle = JOYSTICK_XMIDDLE;
8956 si->input[i].joy.xright = JOYSTICK_XRIGHT;
8957 si->input[i].joy.yupper = JOYSTICK_YUPPER;
8958 si->input[i].joy.ymiddle = JOYSTICK_YMIDDLE;
8959 si->input[i].joy.ylower = JOYSTICK_YLOWER;
8960 si->input[i].joy.snap = (i == 0 ? JOY_BUTTON_1 : 0);
8961 si->input[i].joy.drop = (i == 0 ? JOY_BUTTON_2 : 0);
8962 si->input[i].key.left = (i == 0 ? DEFAULT_KEY_LEFT : KSYM_UNDEFINED);
8963 si->input[i].key.right = (i == 0 ? DEFAULT_KEY_RIGHT : KSYM_UNDEFINED);
8964 si->input[i].key.up = (i == 0 ? DEFAULT_KEY_UP : KSYM_UNDEFINED);
8965 si->input[i].key.down = (i == 0 ? DEFAULT_KEY_DOWN : KSYM_UNDEFINED);
8966 si->input[i].key.snap = (i == 0 ? DEFAULT_KEY_SNAP : KSYM_UNDEFINED);
8967 si->input[i].key.drop = (i == 0 ? DEFAULT_KEY_DROP : KSYM_UNDEFINED);
8970 si->system.sdl_videodriver = getStringCopy(ARG_DEFAULT);
8971 si->system.sdl_audiodriver = getStringCopy(ARG_DEFAULT);
8972 si->system.audio_fragment_size = DEFAULT_AUDIO_FRAGMENT_SIZE;
8974 si->internal.program_title = getStringCopy(PROGRAM_TITLE_STRING);
8975 si->internal.program_version = getStringCopy(getProgramRealVersionString());
8976 si->internal.program_author = getStringCopy(PROGRAM_AUTHOR_STRING);
8977 si->internal.program_email = getStringCopy(PROGRAM_EMAIL_STRING);
8978 si->internal.program_website = getStringCopy(PROGRAM_WEBSITE_STRING);
8979 si->internal.program_copyright = getStringCopy(PROGRAM_COPYRIGHT_STRING);
8980 si->internal.program_company = getStringCopy(PROGRAM_COMPANY_STRING);
8982 si->internal.program_icon_file = getStringCopy(PROGRAM_ICON_FILENAME);
8984 si->internal.default_graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
8985 si->internal.default_sounds_set = getStringCopy(SND_CLASSIC_SUBDIR);
8986 si->internal.default_music_set = getStringCopy(MUS_CLASSIC_SUBDIR);
8988 si->internal.fallback_graphics_file = getStringCopy(UNDEFINED_FILENAME);
8989 si->internal.fallback_sounds_file = getStringCopy(UNDEFINED_FILENAME);
8990 si->internal.fallback_music_file = getStringCopy(UNDEFINED_FILENAME);
8992 si->internal.default_level_series = getStringCopy(UNDEFINED_LEVELSET);
8993 si->internal.choose_from_top_leveldir = FALSE;
8994 si->internal.show_scaling_in_title = TRUE;
8996 si->internal.default_window_width = WIN_XSIZE_DEFAULT;
8997 si->internal.default_window_height = WIN_YSIZE_DEFAULT;
8999 si->debug.frame_delay[0] = DEFAULT_FRAME_DELAY_0;
9000 si->debug.frame_delay[1] = DEFAULT_FRAME_DELAY_1;
9001 si->debug.frame_delay[2] = DEFAULT_FRAME_DELAY_2;
9002 si->debug.frame_delay[3] = DEFAULT_FRAME_DELAY_3;
9003 si->debug.frame_delay[4] = DEFAULT_FRAME_DELAY_4;
9004 si->debug.frame_delay[5] = DEFAULT_FRAME_DELAY_5;
9005 si->debug.frame_delay[6] = DEFAULT_FRAME_DELAY_6;
9006 si->debug.frame_delay[7] = DEFAULT_FRAME_DELAY_7;
9007 si->debug.frame_delay[8] = DEFAULT_FRAME_DELAY_8;
9008 si->debug.frame_delay[9] = DEFAULT_FRAME_DELAY_9;
9010 si->debug.frame_delay_key[0] = DEFAULT_KEY_FRAME_DELAY_0;
9011 si->debug.frame_delay_key[1] = DEFAULT_KEY_FRAME_DELAY_1;
9012 si->debug.frame_delay_key[2] = DEFAULT_KEY_FRAME_DELAY_2;
9013 si->debug.frame_delay_key[3] = DEFAULT_KEY_FRAME_DELAY_3;
9014 si->debug.frame_delay_key[4] = DEFAULT_KEY_FRAME_DELAY_4;
9015 si->debug.frame_delay_key[5] = DEFAULT_KEY_FRAME_DELAY_5;
9016 si->debug.frame_delay_key[6] = DEFAULT_KEY_FRAME_DELAY_6;
9017 si->debug.frame_delay_key[7] = DEFAULT_KEY_FRAME_DELAY_7;
9018 si->debug.frame_delay_key[8] = DEFAULT_KEY_FRAME_DELAY_8;
9019 si->debug.frame_delay_key[9] = DEFAULT_KEY_FRAME_DELAY_9;
9021 si->debug.frame_delay_use_mod_key = DEFAULT_FRAME_DELAY_USE_MOD_KEY;
9022 si->debug.frame_delay_game_only = DEFAULT_FRAME_DELAY_GAME_ONLY;
9024 si->debug.show_frames_per_second = FALSE;
9026 si->options.verbose = FALSE;
9028 #if defined(PLATFORM_ANDROID)
9029 si->fullscreen = TRUE;
9033 static void setSetupInfoToDefaults_AutoSetup(struct SetupInfo *si)
9035 si->auto_setup.editor_zoom_tilesize = MINI_TILESIZE;
9038 static void setSetupInfoToDefaults_EditorCascade(struct SetupInfo *si)
9040 si->editor_cascade.el_bd = TRUE;
9041 si->editor_cascade.el_em = TRUE;
9042 si->editor_cascade.el_emc = TRUE;
9043 si->editor_cascade.el_rnd = TRUE;
9044 si->editor_cascade.el_sb = TRUE;
9045 si->editor_cascade.el_sp = TRUE;
9046 si->editor_cascade.el_dc = TRUE;
9047 si->editor_cascade.el_dx = TRUE;
9049 si->editor_cascade.el_mm = TRUE;
9050 si->editor_cascade.el_df = TRUE;
9052 si->editor_cascade.el_chars = FALSE;
9053 si->editor_cascade.el_steel_chars = FALSE;
9054 si->editor_cascade.el_ce = FALSE;
9055 si->editor_cascade.el_ge = FALSE;
9056 si->editor_cascade.el_ref = FALSE;
9057 si->editor_cascade.el_user = FALSE;
9058 si->editor_cascade.el_dynamic = FALSE;
9061 #define MAX_HIDE_SETUP_TOKEN_SIZE 20
9063 static char *getHideSetupToken(void *setup_value)
9065 static char hide_setup_token[MAX_HIDE_SETUP_TOKEN_SIZE];
9067 if (setup_value != NULL)
9068 snprintf(hide_setup_token, MAX_HIDE_SETUP_TOKEN_SIZE, "%p", setup_value);
9070 return hide_setup_token;
9073 void setHideSetupEntry(void *setup_value)
9075 char *hide_setup_token = getHideSetupToken(setup_value);
9077 if (setup_value != NULL)
9078 setHashEntry(hide_setup_hash, hide_setup_token, "");
9081 static void setHideSetupEntryRaw(char *token_text, void *setup_value_raw)
9083 /* !!! DIRTY WORKAROUND; TO BE FIXED AFTER THE MM ENGINE RELEASE !!! */
9084 void *setup_value = setup_value_raw - (void *)&si + (void *)&setup;
9086 setHideSetupEntry(setup_value);
9089 boolean hideSetupEntry(void *setup_value)
9091 char *hide_setup_token = getHideSetupToken(setup_value);
9093 return (setup_value != NULL &&
9094 getHashEntry(hide_setup_hash, hide_setup_token) != NULL);
9097 static void setSetupInfoFromTokenText(SetupFileHash *setup_file_hash,
9098 struct TokenInfo *token_info,
9099 int token_nr, char *token_text)
9101 char *token_hide_text = getStringCat2(token_text, ".hide");
9102 char *token_hide_value = getHashEntry(setup_file_hash, token_hide_text);
9104 /* set the value of this setup option in the setup option structure */
9105 setSetupInfo(token_info, token_nr, getHashEntry(setup_file_hash, token_text));
9107 /* check if this setup option should be hidden in the setup menu */
9108 if (token_hide_value != NULL && get_boolean_from_string(token_hide_value))
9109 setHideSetupEntryRaw(token_text, token_info[token_nr].value);
9112 static void setSetupInfoFromTokenInfo(SetupFileHash *setup_file_hash,
9113 struct TokenInfo *token_info,
9116 setSetupInfoFromTokenText(setup_file_hash, token_info, token_nr,
9117 token_info[token_nr].text);
9120 static void decodeSetupFileHash(SetupFileHash *setup_file_hash)
9124 if (!setup_file_hash)
9127 if (hide_setup_hash == NULL)
9128 hide_setup_hash = newSetupFileHash();
9132 for (i = 0; i < NUM_GLOBAL_SETUP_TOKENS; i++)
9133 setSetupInfoFromTokenInfo(setup_file_hash, global_setup_tokens, i);
9136 /* virtual buttons setup */
9137 setup.touch.grid_initialized = TRUE;
9138 for (i = 0; i < 2; i++)
9140 int grid_xsize = setup.touch.grid_xsize[i];
9141 int grid_ysize = setup.touch.grid_ysize[i];
9144 // if virtual buttons are not loaded from setup file, repeat initializing
9145 // virtual buttons grid with default values later when video is initialized
9146 if (grid_xsize == -1 ||
9149 setup.touch.grid_initialized = FALSE;
9154 for (y = 0; y < grid_ysize; y++)
9156 char token_string[MAX_LINE_LEN];
9158 sprintf(token_string, "touch.virtual_buttons.%d.%02d", i, y);
9160 char *value_string = getHashEntry(setup_file_hash, token_string);
9162 if (value_string == NULL)
9165 for (x = 0; x < grid_xsize; x++)
9167 char c = value_string[x];
9169 setup.touch.grid_button[i][x][y] =
9170 (c == '.' ? CHAR_GRID_BUTTON_NONE : c);
9177 for (i = 0; i < NUM_EDITOR_SETUP_TOKENS; i++)
9178 setSetupInfoFromTokenInfo(setup_file_hash, editor_setup_tokens, i);
9181 /* shortcut setup */
9182 ssi = setup.shortcut;
9183 for (i = 0; i < NUM_SHORTCUT_SETUP_TOKENS; i++)
9184 setSetupInfoFromTokenInfo(setup_file_hash, shortcut_setup_tokens, i);
9185 setup.shortcut = ssi;
9188 for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
9192 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
9194 sii = setup.input[pnr];
9195 for (i = 0; i < NUM_PLAYER_SETUP_TOKENS; i++)
9197 char full_token[100];
9199 sprintf(full_token, "%s%s", prefix, player_setup_tokens[i].text);
9200 setSetupInfoFromTokenText(setup_file_hash, player_setup_tokens, i,
9203 setup.input[pnr] = sii;
9208 for (i = 0; i < NUM_SYSTEM_SETUP_TOKENS; i++)
9209 setSetupInfoFromTokenInfo(setup_file_hash, system_setup_tokens, i);
9212 /* internal setup */
9213 sxi = setup.internal;
9214 for (i = 0; i < NUM_INTERNAL_SETUP_TOKENS; i++)
9215 setSetupInfoFromTokenInfo(setup_file_hash, internal_setup_tokens, i);
9216 setup.internal = sxi;
9220 for (i = 0; i < NUM_DEBUG_SETUP_TOKENS; i++)
9221 setSetupInfoFromTokenInfo(setup_file_hash, debug_setup_tokens, i);
9225 soi = setup.options;
9226 for (i = 0; i < NUM_OPTIONS_SETUP_TOKENS; i++)
9227 setSetupInfoFromTokenInfo(setup_file_hash, options_setup_tokens, i);
9228 setup.options = soi;
9230 setHideRelatedSetupEntries();
9233 static void decodeSetupFileHash_AutoSetup(SetupFileHash *setup_file_hash)
9237 if (!setup_file_hash)
9241 sasi = setup.auto_setup;
9242 for (i = 0; i < NUM_AUTO_SETUP_TOKENS; i++)
9243 setSetupInfo(auto_setup_tokens, i,
9244 getHashEntry(setup_file_hash,
9245 auto_setup_tokens[i].text));
9246 setup.auto_setup = sasi;
9249 static void decodeSetupFileHash_EditorCascade(SetupFileHash *setup_file_hash)
9253 if (!setup_file_hash)
9256 /* editor cascade setup */
9257 seci = setup.editor_cascade;
9258 for (i = 0; i < NUM_EDITOR_CASCADE_SETUP_TOKENS; i++)
9259 setSetupInfo(editor_cascade_setup_tokens, i,
9260 getHashEntry(setup_file_hash,
9261 editor_cascade_setup_tokens[i].text));
9262 setup.editor_cascade = seci;
9265 void LoadSetupFromFilename(char *filename)
9267 SetupFileHash *setup_file_hash = loadSetupFileHash(filename);
9269 if (setup_file_hash)
9271 decodeSetupFileHash(setup_file_hash);
9273 freeSetupFileHash(setup_file_hash);
9277 Error(ERR_DEBUG, "using default setup values");
9281 static void LoadSetup_SpecialPostProcessing()
9283 char *player_name_new;
9285 /* needed to work around problems with fixed length strings */
9286 player_name_new = get_corrected_login_name(setup.player_name);
9287 free(setup.player_name);
9288 setup.player_name = player_name_new;
9290 /* "scroll_delay: on(3) / off(0)" was replaced by scroll delay value */
9291 if (setup.scroll_delay == FALSE)
9293 setup.scroll_delay_value = MIN_SCROLL_DELAY;
9294 setup.scroll_delay = TRUE; /* now always "on" */
9297 /* make sure that scroll delay value stays inside valid range */
9298 setup.scroll_delay_value =
9299 MIN(MAX(MIN_SCROLL_DELAY, setup.scroll_delay_value), MAX_SCROLL_DELAY);
9306 /* always start with reliable default values */
9307 setSetupInfoToDefaults(&setup);
9309 /* try to load setup values from default setup file */
9310 filename = getDefaultSetupFilename();
9312 if (fileExists(filename))
9313 LoadSetupFromFilename(filename);
9315 /* try to load setup values from user setup file */
9316 filename = getSetupFilename();
9318 LoadSetupFromFilename(filename);
9320 LoadSetup_SpecialPostProcessing();
9323 void LoadSetup_AutoSetup()
9325 char *filename = getPath2(getSetupDir(), AUTOSETUP_FILENAME);
9326 SetupFileHash *setup_file_hash = NULL;
9328 /* always start with reliable default values */
9329 setSetupInfoToDefaults_AutoSetup(&setup);
9331 setup_file_hash = loadSetupFileHash(filename);
9333 if (setup_file_hash)
9335 decodeSetupFileHash_AutoSetup(setup_file_hash);
9337 freeSetupFileHash(setup_file_hash);
9343 void LoadSetup_EditorCascade()
9345 char *filename = getPath2(getSetupDir(), EDITORCASCADE_FILENAME);
9346 SetupFileHash *setup_file_hash = NULL;
9348 /* always start with reliable default values */
9349 setSetupInfoToDefaults_EditorCascade(&setup);
9351 setup_file_hash = loadSetupFileHash(filename);
9353 if (setup_file_hash)
9355 decodeSetupFileHash_EditorCascade(setup_file_hash);
9357 freeSetupFileHash(setup_file_hash);
9363 static void addGameControllerMappingToHash(SetupFileHash *mappings_hash,
9366 char mapping_guid[MAX_LINE_LEN];
9367 char *mapping_start, *mapping_end;
9369 // get GUID from game controller mapping line: copy complete line
9370 strncpy(mapping_guid, mapping_line, MAX_LINE_LEN - 1);
9371 mapping_guid[MAX_LINE_LEN - 1] = '\0';
9373 // get GUID from game controller mapping line: cut after GUID part
9374 mapping_start = strchr(mapping_guid, ',');
9375 if (mapping_start != NULL)
9376 *mapping_start = '\0';
9378 // cut newline from game controller mapping line
9379 mapping_end = strchr(mapping_line, '\n');
9380 if (mapping_end != NULL)
9381 *mapping_end = '\0';
9383 // add mapping entry to game controller mappings hash
9384 setHashEntry(mappings_hash, mapping_guid, mapping_line);
9387 static void LoadSetup_ReadGameControllerMappings(SetupFileHash *mappings_hash,
9392 if (!(file = fopen(filename, MODE_READ)))
9394 Error(ERR_WARN, "cannot read game controller mappings file '%s'", filename);
9401 char line[MAX_LINE_LEN];
9403 if (!fgets(line, MAX_LINE_LEN, file))
9406 addGameControllerMappingToHash(mappings_hash, line);
9414 char *filename = getSetupFilename();
9418 InitUserDataDirectory();
9420 if (!(file = fopen(filename, MODE_WRITE)))
9422 Error(ERR_WARN, "cannot write setup file '%s'", filename);
9426 fprintFileHeader(file, SETUP_FILENAME);
9430 for (i = 0; i < NUM_GLOBAL_SETUP_TOKENS; i++)
9432 /* just to make things nicer :) */
9433 if (i == SETUP_TOKEN_PLAYER_NAME + 1 ||
9434 i == SETUP_TOKEN_GRAPHICS_SET ||
9435 i == SETUP_TOKEN_VOLUME_SIMPLE ||
9436 i == SETUP_TOKEN_NETWORK_MODE ||
9437 i == SETUP_TOKEN_TOUCH_CONTROL_TYPE ||
9438 i == SETUP_TOKEN_TOUCH_GRID_XSIZE_0 ||
9439 i == SETUP_TOKEN_TOUCH_GRID_XSIZE_1)
9440 fprintf(file, "\n");
9442 fprintf(file, "%s\n", getSetupLine(global_setup_tokens, "", i));
9445 /* virtual buttons setup */
9446 for (i = 0; i < 2; i++)
9448 int grid_xsize = setup.touch.grid_xsize[i];
9449 int grid_ysize = setup.touch.grid_ysize[i];
9452 fprintf(file, "\n");
9454 for (y = 0; y < grid_ysize; y++)
9456 char token_string[MAX_LINE_LEN];
9457 char value_string[MAX_LINE_LEN];
9459 sprintf(token_string, "touch.virtual_buttons.%d.%02d", i, y);
9461 for (x = 0; x < grid_xsize; x++)
9463 char c = setup.touch.grid_button[i][x][y];
9465 value_string[x] = (c == CHAR_GRID_BUTTON_NONE ? '.' : c);
9468 value_string[grid_xsize] = '\0';
9470 fprintf(file, "%s\n", getFormattedSetupEntry(token_string, value_string));
9476 fprintf(file, "\n");
9477 for (i = 0; i < NUM_EDITOR_SETUP_TOKENS; i++)
9478 fprintf(file, "%s\n", getSetupLine(editor_setup_tokens, "", i));
9480 /* shortcut setup */
9481 ssi = setup.shortcut;
9482 fprintf(file, "\n");
9483 for (i = 0; i < NUM_SHORTCUT_SETUP_TOKENS; i++)
9484 fprintf(file, "%s\n", getSetupLine(shortcut_setup_tokens, "", i));
9487 for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
9491 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
9492 fprintf(file, "\n");
9494 sii = setup.input[pnr];
9495 for (i = 0; i < NUM_PLAYER_SETUP_TOKENS; i++)
9496 fprintf(file, "%s\n", getSetupLine(player_setup_tokens, prefix, i));
9501 fprintf(file, "\n");
9502 for (i = 0; i < NUM_SYSTEM_SETUP_TOKENS; i++)
9503 fprintf(file, "%s\n", getSetupLine(system_setup_tokens, "", i));
9505 /* internal setup */
9506 /* (internal setup values not saved to user setup file) */
9510 fprintf(file, "\n");
9511 for (i = 0; i < NUM_DEBUG_SETUP_TOKENS; i++)
9512 fprintf(file, "%s\n", getSetupLine(debug_setup_tokens, "", i));
9515 soi = setup.options;
9516 fprintf(file, "\n");
9517 for (i = 0; i < NUM_OPTIONS_SETUP_TOKENS; i++)
9518 fprintf(file, "%s\n", getSetupLine(options_setup_tokens, "", i));
9522 SetFilePermissions(filename, PERMS_PRIVATE);
9525 void SaveSetup_AutoSetup()
9527 char *filename = getPath2(getSetupDir(), AUTOSETUP_FILENAME);
9531 InitUserDataDirectory();
9533 if (!(file = fopen(filename, MODE_WRITE)))
9535 Error(ERR_WARN, "cannot write auto setup file '%s'", filename);
9540 fprintFileHeader(file, AUTOSETUP_FILENAME);
9542 sasi = setup.auto_setup;
9543 for (i = 0; i < NUM_AUTO_SETUP_TOKENS; i++)
9544 fprintf(file, "%s\n", getSetupLine(auto_setup_tokens, "", i));
9548 SetFilePermissions(filename, PERMS_PRIVATE);
9553 void SaveSetup_EditorCascade()
9555 char *filename = getPath2(getSetupDir(), EDITORCASCADE_FILENAME);
9559 InitUserDataDirectory();
9561 if (!(file = fopen(filename, MODE_WRITE)))
9563 Error(ERR_WARN, "cannot write editor cascade state file '%s'", filename);
9568 fprintFileHeader(file, EDITORCASCADE_FILENAME);
9570 seci = setup.editor_cascade;
9571 for (i = 0; i < NUM_EDITOR_CASCADE_SETUP_TOKENS; i++)
9572 fprintf(file, "%s\n", getSetupLine(editor_cascade_setup_tokens, "", i));
9576 SetFilePermissions(filename, PERMS_PRIVATE);
9581 static void SaveSetup_WriteGameControllerMappings(SetupFileHash *mappings_hash,
9586 if (!(file = fopen(filename, MODE_WRITE)))
9588 Error(ERR_WARN, "cannot write game controller mappings file '%s'",filename);
9593 BEGIN_HASH_ITERATION(mappings_hash, itr)
9595 fprintf(file, "%s\n", HASH_ITERATION_VALUE(itr));
9597 END_HASH_ITERATION(mappings_hash, itr)
9602 void SaveSetup_AddGameControllerMapping(char *mapping)
9604 char *filename = getPath2(getSetupDir(), GAMECONTROLLER_BASENAME);
9605 SetupFileHash *mappings_hash = newSetupFileHash();
9607 InitUserDataDirectory();
9609 // load existing personal game controller mappings
9610 LoadSetup_ReadGameControllerMappings(mappings_hash, filename);
9612 // add new mapping to personal game controller mappings
9613 addGameControllerMappingToHash(mappings_hash, mapping);
9615 // save updated personal game controller mappings
9616 SaveSetup_WriteGameControllerMappings(mappings_hash, filename);
9618 freeSetupFileHash(mappings_hash);
9622 void LoadCustomElementDescriptions()
9624 char *filename = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
9625 SetupFileHash *setup_file_hash;
9628 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
9630 if (element_info[i].custom_description != NULL)
9632 free(element_info[i].custom_description);
9633 element_info[i].custom_description = NULL;
9637 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
9640 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
9642 char *token = getStringCat2(element_info[i].token_name, ".name");
9643 char *value = getHashEntry(setup_file_hash, token);
9646 element_info[i].custom_description = getStringCopy(value);
9651 freeSetupFileHash(setup_file_hash);
9654 static int getElementFromToken(char *token)
9656 char *value = getHashEntry(element_token_hash, token);
9661 Error(ERR_WARN, "unknown element token '%s'", token);
9663 return EL_UNDEFINED;
9666 static int get_token_parameter_value(char *token, char *value_raw)
9670 if (token == NULL || value_raw == NULL)
9671 return ARG_UNDEFINED_VALUE;
9673 suffix = strrchr(token, '.');
9677 if (strEqual(suffix, ".element"))
9678 return getElementFromToken(value_raw);
9680 /* !!! USE CORRECT VALUE TYPE (currently works also for TYPE_BOOLEAN) !!! */
9681 return get_parameter_value(value_raw, suffix, TYPE_INTEGER);
9684 void InitMenuDesignSettings_Static()
9688 /* always start with reliable default values from static default config */
9689 for (i = 0; image_config_vars[i].token != NULL; i++)
9691 char *value = getHashEntry(image_config_hash, image_config_vars[i].token);
9694 *image_config_vars[i].value =
9695 get_token_parameter_value(image_config_vars[i].token, value);
9699 static void InitMenuDesignSettings_SpecialPreProcessing()
9703 /* the following initializes hierarchical values from static configuration */
9705 /* special case: initialize "ARG_DEFAULT" values in static default config */
9706 /* (e.g., initialize "[titlemessage].fade_mode" from "[title].fade_mode") */
9707 titlescreen_initial_first_default.fade_mode =
9708 title_initial_first_default.fade_mode;
9709 titlescreen_initial_first_default.fade_delay =
9710 title_initial_first_default.fade_delay;
9711 titlescreen_initial_first_default.post_delay =
9712 title_initial_first_default.post_delay;
9713 titlescreen_initial_first_default.auto_delay =
9714 title_initial_first_default.auto_delay;
9715 titlescreen_first_default.fade_mode = title_first_default.fade_mode;
9716 titlescreen_first_default.fade_delay = title_first_default.fade_delay;
9717 titlescreen_first_default.post_delay = title_first_default.post_delay;
9718 titlescreen_first_default.auto_delay = title_first_default.auto_delay;
9719 titlemessage_initial_first_default.fade_mode =
9720 title_initial_first_default.fade_mode;
9721 titlemessage_initial_first_default.fade_delay =
9722 title_initial_first_default.fade_delay;
9723 titlemessage_initial_first_default.post_delay =
9724 title_initial_first_default.post_delay;
9725 titlemessage_initial_first_default.auto_delay =
9726 title_initial_first_default.auto_delay;
9727 titlemessage_first_default.fade_mode = title_first_default.fade_mode;
9728 titlemessage_first_default.fade_delay = title_first_default.fade_delay;
9729 titlemessage_first_default.post_delay = title_first_default.post_delay;
9730 titlemessage_first_default.auto_delay = title_first_default.auto_delay;
9732 titlescreen_initial_default.fade_mode = title_initial_default.fade_mode;
9733 titlescreen_initial_default.fade_delay = title_initial_default.fade_delay;
9734 titlescreen_initial_default.post_delay = title_initial_default.post_delay;
9735 titlescreen_initial_default.auto_delay = title_initial_default.auto_delay;
9736 titlescreen_default.fade_mode = title_default.fade_mode;
9737 titlescreen_default.fade_delay = title_default.fade_delay;
9738 titlescreen_default.post_delay = title_default.post_delay;
9739 titlescreen_default.auto_delay = title_default.auto_delay;
9740 titlemessage_initial_default.fade_mode = title_initial_default.fade_mode;
9741 titlemessage_initial_default.fade_delay = title_initial_default.fade_delay;
9742 titlemessage_initial_default.post_delay = title_initial_default.post_delay;
9743 titlemessage_initial_default.auto_delay = title_initial_default.auto_delay;
9744 titlemessage_default.fade_mode = title_default.fade_mode;
9745 titlemessage_default.fade_delay = title_default.fade_delay;
9746 titlemessage_default.post_delay = title_default.post_delay;
9747 titlemessage_default.auto_delay = title_default.auto_delay;
9749 /* special case: initialize "ARG_DEFAULT" values in static default config */
9750 /* (e.g., init "titlemessage_1.fade_mode" from "[titlemessage].fade_mode") */
9751 for (i = 0; i < MAX_NUM_TITLE_MESSAGES; i++)
9753 titlescreen_initial_first[i] = titlescreen_initial_first_default;
9754 titlescreen_first[i] = titlescreen_first_default;
9755 titlemessage_initial_first[i] = titlemessage_initial_first_default;
9756 titlemessage_first[i] = titlemessage_first_default;
9758 titlescreen_initial[i] = titlescreen_initial_default;
9759 titlescreen[i] = titlescreen_default;
9760 titlemessage_initial[i] = titlemessage_initial_default;
9761 titlemessage[i] = titlemessage_default;
9764 /* special case: initialize "ARG_DEFAULT" values in static default config */
9765 /* (eg, init "menu.enter_screen.SCORES.xyz" from "menu.enter_screen.xyz") */
9766 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
9768 if (i == GFX_SPECIAL_ARG_TITLE) /* title values already initialized */
9771 menu.enter_screen[i] = menu.enter_screen[GFX_SPECIAL_ARG_DEFAULT];
9772 menu.leave_screen[i] = menu.leave_screen[GFX_SPECIAL_ARG_DEFAULT];
9773 menu.next_screen[i] = menu.next_screen[GFX_SPECIAL_ARG_DEFAULT];
9776 /* special case: initialize "ARG_DEFAULT" values in static default config */
9777 /* (eg, init "viewport.door_1.MAIN.xyz" from "viewport.door_1.xyz") */
9778 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
9780 viewport.window[i] = viewport.window[GFX_SPECIAL_ARG_DEFAULT];
9781 viewport.playfield[i] = viewport.playfield[GFX_SPECIAL_ARG_DEFAULT];
9782 viewport.door_1[i] = viewport.door_1[GFX_SPECIAL_ARG_DEFAULT];
9784 if (i == GFX_SPECIAL_ARG_EDITOR) /* editor values already initialized */
9787 viewport.door_2[i] = viewport.door_2[GFX_SPECIAL_ARG_DEFAULT];
9791 static void InitMenuDesignSettings_SpecialPostProcessing()
9795 struct XY *dst, *src;
9799 { &game.button.save, &game.button.stop },
9800 { &game.button.pause2, &game.button.pause },
9801 { &game.button.load, &game.button.play },
9802 { &game.button.undo, &game.button.stop },
9803 { &game.button.redo, &game.button.play },
9809 /* special case: initialize later added SETUP list size from LEVELS value */
9810 if (menu.list_size[GAME_MODE_SETUP] == -1)
9811 menu.list_size[GAME_MODE_SETUP] = menu.list_size[GAME_MODE_LEVELS];
9813 /* set default position for snapshot buttons to stop/pause/play buttons */
9814 for (i = 0; game_buttons_xy[i].dst != NULL; i++)
9815 if ((*game_buttons_xy[i].dst).x == -1 &&
9816 (*game_buttons_xy[i].dst).y == -1)
9817 *game_buttons_xy[i].dst = *game_buttons_xy[i].src;
9820 static void InitMenuDesignSettings_SpecialPostProcessing_AfterGraphics()
9824 struct XYTileSize *dst, *src;
9827 editor_buttons_xy[] =
9830 &editor.button.element_left, &editor.palette.element_left,
9831 IMG_GFX_EDITOR_BUTTON_ELEMENT_LEFT
9834 &editor.button.element_middle, &editor.palette.element_middle,
9835 IMG_GFX_EDITOR_BUTTON_ELEMENT_MIDDLE
9838 &editor.button.element_right, &editor.palette.element_right,
9839 IMG_GFX_EDITOR_BUTTON_ELEMENT_RIGHT
9846 /* set default position for element buttons to element graphics */
9847 for (i = 0; editor_buttons_xy[i].dst != NULL; i++)
9849 if ((*editor_buttons_xy[i].dst).x == -1 &&
9850 (*editor_buttons_xy[i].dst).y == -1)
9852 struct GraphicInfo *gd = &graphic_info[editor_buttons_xy[i].graphic];
9854 gd->width = gd->height = editor_buttons_xy[i].src->tile_size;
9856 *editor_buttons_xy[i].dst = *editor_buttons_xy[i].src;
9861 static void LoadMenuDesignSettingsFromFilename(char *filename)
9863 static struct TitleFadingInfo tfi;
9864 static struct TitleMessageInfo tmi;
9865 static struct TokenInfo title_tokens[] =
9867 { TYPE_INTEGER, &tfi.fade_mode, ".fade_mode" },
9868 { TYPE_INTEGER, &tfi.fade_delay, ".fade_delay" },
9869 { TYPE_INTEGER, &tfi.post_delay, ".post_delay" },
9870 { TYPE_INTEGER, &tfi.auto_delay, ".auto_delay" },
9874 static struct TokenInfo titlemessage_tokens[] =
9876 { TYPE_INTEGER, &tmi.x, ".x" },
9877 { TYPE_INTEGER, &tmi.y, ".y" },
9878 { TYPE_INTEGER, &tmi.width, ".width" },
9879 { TYPE_INTEGER, &tmi.height, ".height" },
9880 { TYPE_INTEGER, &tmi.chars, ".chars" },
9881 { TYPE_INTEGER, &tmi.lines, ".lines" },
9882 { TYPE_INTEGER, &tmi.align, ".align" },
9883 { TYPE_INTEGER, &tmi.valign, ".valign" },
9884 { TYPE_INTEGER, &tmi.font, ".font" },
9885 { TYPE_BOOLEAN, &tmi.autowrap, ".autowrap" },
9886 { TYPE_BOOLEAN, &tmi.centered, ".centered" },
9887 { TYPE_BOOLEAN, &tmi.parse_comments, ".parse_comments" },
9888 { TYPE_INTEGER, &tmi.sort_priority, ".sort_priority" },
9889 { TYPE_INTEGER, &tmi.fade_mode, ".fade_mode" },
9890 { TYPE_INTEGER, &tmi.fade_delay, ".fade_delay" },
9891 { TYPE_INTEGER, &tmi.post_delay, ".post_delay" },
9892 { TYPE_INTEGER, &tmi.auto_delay, ".auto_delay" },
9898 struct TitleFadingInfo *info;
9903 /* initialize first titles from "enter screen" definitions, if defined */
9904 { &title_initial_first_default, "menu.enter_screen.TITLE" },
9905 { &title_first_default, "menu.enter_screen.TITLE" },
9907 /* initialize title screens from "next screen" definitions, if defined */
9908 { &title_initial_default, "menu.next_screen.TITLE" },
9909 { &title_default, "menu.next_screen.TITLE" },
9915 struct TitleMessageInfo *array;
9918 titlemessage_arrays[] =
9920 /* initialize first titles from "enter screen" definitions, if defined */
9921 { titlescreen_initial_first, "menu.enter_screen.TITLE" },
9922 { titlescreen_first, "menu.enter_screen.TITLE" },
9923 { titlemessage_initial_first, "menu.enter_screen.TITLE" },
9924 { titlemessage_first, "menu.enter_screen.TITLE" },
9926 /* initialize titles from "next screen" definitions, if defined */
9927 { titlescreen_initial, "menu.next_screen.TITLE" },
9928 { titlescreen, "menu.next_screen.TITLE" },
9929 { titlemessage_initial, "menu.next_screen.TITLE" },
9930 { titlemessage, "menu.next_screen.TITLE" },
9932 /* overwrite titles with title definitions, if defined */
9933 { titlescreen_initial_first, "[title_initial]" },
9934 { titlescreen_first, "[title]" },
9935 { titlemessage_initial_first, "[title_initial]" },
9936 { titlemessage_first, "[title]" },
9938 { titlescreen_initial, "[title_initial]" },
9939 { titlescreen, "[title]" },
9940 { titlemessage_initial, "[title_initial]" },
9941 { titlemessage, "[title]" },
9943 /* overwrite titles with title screen/message definitions, if defined */
9944 { titlescreen_initial_first, "[titlescreen_initial]" },
9945 { titlescreen_first, "[titlescreen]" },
9946 { titlemessage_initial_first, "[titlemessage_initial]" },
9947 { titlemessage_first, "[titlemessage]" },
9949 { titlescreen_initial, "[titlescreen_initial]" },
9950 { titlescreen, "[titlescreen]" },
9951 { titlemessage_initial, "[titlemessage_initial]" },
9952 { titlemessage, "[titlemessage]" },
9956 SetupFileHash *setup_file_hash;
9959 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
9962 /* the following initializes hierarchical values from dynamic configuration */
9964 /* special case: initialize with default values that may be overwritten */
9965 /* (e.g., init "menu.draw_xoffset.INFO" from "menu.draw_xoffset") */
9966 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
9968 char *value_1 = getHashEntry(setup_file_hash, "menu.draw_xoffset");
9969 char *value_2 = getHashEntry(setup_file_hash, "menu.draw_yoffset");
9970 char *value_3 = getHashEntry(setup_file_hash, "menu.list_size");
9972 if (value_1 != NULL)
9973 menu.draw_xoffset[i] = get_integer_from_string(value_1);
9974 if (value_2 != NULL)
9975 menu.draw_yoffset[i] = get_integer_from_string(value_2);
9976 if (value_3 != NULL)
9977 menu.list_size[i] = get_integer_from_string(value_3);
9980 /* special case: initialize with default values that may be overwritten */
9981 /* (eg, init "menu.draw_xoffset.INFO[XXX]" from "menu.draw_xoffset.INFO") */
9982 for (i = 0; i < NUM_SPECIAL_GFX_INFO_ARGS; i++)
9984 char *value_1 = getHashEntry(setup_file_hash, "menu.draw_xoffset.INFO");
9985 char *value_2 = getHashEntry(setup_file_hash, "menu.draw_yoffset.INFO");
9987 if (value_1 != NULL)
9988 menu.draw_xoffset_info[i] = get_integer_from_string(value_1);
9989 if (value_2 != NULL)
9990 menu.draw_yoffset_info[i] = get_integer_from_string(value_2);
9992 if (i == GFX_SPECIAL_ARG_INFO_ELEMENTS)
9994 char *value_1 = getHashEntry(setup_file_hash, "menu.list_size.INFO");
9996 if (value_1 != NULL)
9997 menu.list_size_info[i] = get_integer_from_string(value_1);
10001 /* special case: initialize with default values that may be overwritten */
10002 /* (eg, init "menu.draw_xoffset.SETUP[XXX]" from "menu.draw_xoffset.SETUP") */
10003 for (i = 0; i < NUM_SPECIAL_GFX_SETUP_ARGS; i++)
10005 char *value_1 = getHashEntry(setup_file_hash, "menu.draw_xoffset.SETUP");
10006 char *value_2 = getHashEntry(setup_file_hash, "menu.draw_yoffset.SETUP");
10008 if (value_1 != NULL)
10009 menu.draw_xoffset_setup[i] = get_integer_from_string(value_1);
10010 if (value_2 != NULL)
10011 menu.draw_yoffset_setup[i] = get_integer_from_string(value_2);
10014 /* special case: initialize with default values that may be overwritten */
10015 /* (eg, init "menu.line_spacing.INFO[XXX]" from "menu.line_spacing.INFO") */
10016 for (i = 0; i < NUM_SPECIAL_GFX_INFO_ARGS; i++)
10018 char *value_1 = getHashEntry(setup_file_hash,"menu.left_spacing.INFO");
10019 char *value_2 = getHashEntry(setup_file_hash,"menu.right_spacing.INFO");
10020 char *value_3 = getHashEntry(setup_file_hash,"menu.top_spacing.INFO");
10021 char *value_4 = getHashEntry(setup_file_hash,"menu.bottom_spacing.INFO");
10022 char *value_5 = getHashEntry(setup_file_hash,"menu.paragraph_spacing.INFO");
10023 char *value_6 = getHashEntry(setup_file_hash,"menu.headline1_spacing.INFO");
10024 char *value_7 = getHashEntry(setup_file_hash,"menu.headline2_spacing.INFO");
10025 char *value_8 = getHashEntry(setup_file_hash,"menu.line_spacing.INFO");
10026 char *value_9 = getHashEntry(setup_file_hash,"menu.extra_spacing.INFO");
10028 if (value_1 != NULL)
10029 menu.left_spacing_info[i] = get_integer_from_string(value_1);
10030 if (value_2 != NULL)
10031 menu.right_spacing_info[i] = get_integer_from_string(value_2);
10032 if (value_3 != NULL)
10033 menu.top_spacing_info[i] = get_integer_from_string(value_3);
10034 if (value_4 != NULL)
10035 menu.bottom_spacing_info[i] = get_integer_from_string(value_4);
10036 if (value_5 != NULL)
10037 menu.paragraph_spacing_info[i] = get_integer_from_string(value_5);
10038 if (value_6 != NULL)
10039 menu.headline1_spacing_info[i] = get_integer_from_string(value_6);
10040 if (value_7 != NULL)
10041 menu.headline2_spacing_info[i] = get_integer_from_string(value_7);
10042 if (value_8 != NULL)
10043 menu.line_spacing_info[i] = get_integer_from_string(value_8);
10044 if (value_9 != NULL)
10045 menu.extra_spacing_info[i] = get_integer_from_string(value_9);
10048 /* special case: initialize with default values that may be overwritten */
10049 /* (eg, init "menu.enter_screen.SCORES.xyz" from "menu.enter_screen.xyz") */
10050 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
10052 char *token_1 = "menu.enter_screen.fade_mode";
10053 char *token_2 = "menu.enter_screen.fade_delay";
10054 char *token_3 = "menu.enter_screen.post_delay";
10055 char *token_4 = "menu.leave_screen.fade_mode";
10056 char *token_5 = "menu.leave_screen.fade_delay";
10057 char *token_6 = "menu.leave_screen.post_delay";
10058 char *token_7 = "menu.next_screen.fade_mode";
10059 char *token_8 = "menu.next_screen.fade_delay";
10060 char *token_9 = "menu.next_screen.post_delay";
10061 char *value_1 = getHashEntry(setup_file_hash, token_1);
10062 char *value_2 = getHashEntry(setup_file_hash, token_2);
10063 char *value_3 = getHashEntry(setup_file_hash, token_3);
10064 char *value_4 = getHashEntry(setup_file_hash, token_4);
10065 char *value_5 = getHashEntry(setup_file_hash, token_5);
10066 char *value_6 = getHashEntry(setup_file_hash, token_6);
10067 char *value_7 = getHashEntry(setup_file_hash, token_7);
10068 char *value_8 = getHashEntry(setup_file_hash, token_8);
10069 char *value_9 = getHashEntry(setup_file_hash, token_9);
10071 if (value_1 != NULL)
10072 menu.enter_screen[i].fade_mode = get_token_parameter_value(token_1,
10074 if (value_2 != NULL)
10075 menu.enter_screen[i].fade_delay = get_token_parameter_value(token_2,
10077 if (value_3 != NULL)
10078 menu.enter_screen[i].post_delay = get_token_parameter_value(token_3,
10080 if (value_4 != NULL)
10081 menu.leave_screen[i].fade_mode = get_token_parameter_value(token_4,
10083 if (value_5 != NULL)
10084 menu.leave_screen[i].fade_delay = get_token_parameter_value(token_5,
10086 if (value_6 != NULL)
10087 menu.leave_screen[i].post_delay = get_token_parameter_value(token_6,
10089 if (value_7 != NULL)
10090 menu.next_screen[i].fade_mode = get_token_parameter_value(token_7,
10092 if (value_8 != NULL)
10093 menu.next_screen[i].fade_delay = get_token_parameter_value(token_8,
10095 if (value_9 != NULL)
10096 menu.next_screen[i].post_delay = get_token_parameter_value(token_9,
10100 /* special case: initialize with default values that may be overwritten */
10101 /* (eg, init "viewport.door_1.MAIN.xyz" from "viewport.door_1.xyz") */
10102 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
10104 char *token_w1 = "viewport.window.width";
10105 char *token_w2 = "viewport.window.height";
10106 char *token_01 = "viewport.playfield.x";
10107 char *token_02 = "viewport.playfield.y";
10108 char *token_03 = "viewport.playfield.width";
10109 char *token_04 = "viewport.playfield.height";
10110 char *token_05 = "viewport.playfield.border_size";
10111 char *token_06 = "viewport.door_1.x";
10112 char *token_07 = "viewport.door_1.y";
10113 char *token_08 = "viewport.door_1.width";
10114 char *token_09 = "viewport.door_1.height";
10115 char *token_10 = "viewport.door_1.border_size";
10116 char *token_11 = "viewport.door_2.x";
10117 char *token_12 = "viewport.door_2.y";
10118 char *token_13 = "viewport.door_2.width";
10119 char *token_14 = "viewport.door_2.height";
10120 char *token_15 = "viewport.door_2.border_size";
10121 char *value_w1 = getHashEntry(setup_file_hash, token_w1);
10122 char *value_w2 = getHashEntry(setup_file_hash, token_w2);
10123 char *value_01 = getHashEntry(setup_file_hash, token_01);
10124 char *value_02 = getHashEntry(setup_file_hash, token_02);
10125 char *value_03 = getHashEntry(setup_file_hash, token_03);
10126 char *value_04 = getHashEntry(setup_file_hash, token_04);
10127 char *value_05 = getHashEntry(setup_file_hash, token_05);
10128 char *value_06 = getHashEntry(setup_file_hash, token_06);
10129 char *value_07 = getHashEntry(setup_file_hash, token_07);
10130 char *value_08 = getHashEntry(setup_file_hash, token_08);
10131 char *value_09 = getHashEntry(setup_file_hash, token_09);
10132 char *value_10 = getHashEntry(setup_file_hash, token_10);
10133 char *value_11 = getHashEntry(setup_file_hash, token_11);
10134 char *value_12 = getHashEntry(setup_file_hash, token_12);
10135 char *value_13 = getHashEntry(setup_file_hash, token_13);
10136 char *value_14 = getHashEntry(setup_file_hash, token_14);
10137 char *value_15 = getHashEntry(setup_file_hash, token_15);
10139 if (value_w1 != NULL)
10140 viewport.window[i].width = get_token_parameter_value(token_w1, value_w1);
10141 if (value_w2 != NULL)
10142 viewport.window[i].height = get_token_parameter_value(token_w2, value_w2);
10143 if (value_01 != NULL)
10144 viewport.playfield[i].x = get_token_parameter_value(token_01, value_01);
10145 if (value_02 != NULL)
10146 viewport.playfield[i].y = get_token_parameter_value(token_02, value_02);
10147 if (value_03 != NULL)
10148 viewport.playfield[i].width = get_token_parameter_value(token_03,
10150 if (value_04 != NULL)
10151 viewport.playfield[i].height = get_token_parameter_value(token_04,
10153 if (value_05 != NULL)
10154 viewport.playfield[i].border_size = get_token_parameter_value(token_05,
10156 if (value_06 != NULL)
10157 viewport.door_1[i].x = get_token_parameter_value(token_06, value_06);
10158 if (value_07 != NULL)
10159 viewport.door_1[i].y = get_token_parameter_value(token_07, value_07);
10160 if (value_08 != NULL)
10161 viewport.door_1[i].width = get_token_parameter_value(token_08, value_08);
10162 if (value_09 != NULL)
10163 viewport.door_1[i].height = get_token_parameter_value(token_09, value_09);
10164 if (value_10 != NULL)
10165 viewport.door_1[i].border_size = get_token_parameter_value(token_10,
10167 if (value_11 != NULL)
10168 viewport.door_2[i].x = get_token_parameter_value(token_11, value_11);
10169 if (value_12 != NULL)
10170 viewport.door_2[i].y = get_token_parameter_value(token_12, value_12);
10171 if (value_13 != NULL)
10172 viewport.door_2[i].width = get_token_parameter_value(token_13, value_13);
10173 if (value_14 != NULL)
10174 viewport.door_2[i].height = get_token_parameter_value(token_14, value_14);
10175 if (value_15 != NULL)
10176 viewport.door_1[i].border_size = get_token_parameter_value(token_15,
10180 /* special case: initialize with default values that may be overwritten */
10181 /* (e.g., init "[title].fade_mode" from "menu.next_screen.TITLE.fade_mode") */
10182 for (i = 0; title_info[i].info != NULL; i++)
10184 struct TitleFadingInfo *info = title_info[i].info;
10185 char *base_token = title_info[i].text;
10187 for (j = 0; title_tokens[j].type != -1; j++)
10189 char *token = getStringCat2(base_token, title_tokens[j].text);
10190 char *value = getHashEntry(setup_file_hash, token);
10194 int parameter_value = get_token_parameter_value(token, value);
10198 *(int *)title_tokens[j].value = (int)parameter_value;
10207 /* special case: initialize with default values that may be overwritten */
10208 /* (e.g., init "titlemessage_1.fade_mode" from "[titlemessage].fade_mode") */
10209 for (i = 0; titlemessage_arrays[i].array != NULL; i++)
10211 struct TitleMessageInfo *array = titlemessage_arrays[i].array;
10212 char *base_token = titlemessage_arrays[i].text;
10214 for (j = 0; titlemessage_tokens[j].type != -1; j++)
10216 char *token = getStringCat2(base_token, titlemessage_tokens[j].text);
10217 char *value = getHashEntry(setup_file_hash, token);
10221 int parameter_value = get_token_parameter_value(token, value);
10223 for (k = 0; k < MAX_NUM_TITLE_MESSAGES; k++)
10227 if (titlemessage_tokens[j].type == TYPE_INTEGER)
10228 *(int *)titlemessage_tokens[j].value = (int)parameter_value;
10230 *(boolean *)titlemessage_tokens[j].value = (boolean)parameter_value;
10240 /* read (and overwrite with) values that may be specified in config file */
10241 for (i = 0; image_config_vars[i].token != NULL; i++)
10243 char *value = getHashEntry(setup_file_hash, image_config_vars[i].token);
10245 /* (ignore definitions set to "[DEFAULT]" which are already initialized) */
10246 if (value != NULL && !strEqual(value, ARG_DEFAULT))
10247 *image_config_vars[i].value =
10248 get_token_parameter_value(image_config_vars[i].token, value);
10251 freeSetupFileHash(setup_file_hash);
10254 void LoadMenuDesignSettings()
10256 char *filename_base = UNDEFINED_FILENAME, *filename_local;
10258 InitMenuDesignSettings_Static();
10259 InitMenuDesignSettings_SpecialPreProcessing();
10261 if (!GFX_OVERRIDE_ARTWORK(ARTWORK_TYPE_GRAPHICS))
10263 /* first look for special settings configured in level series config */
10264 filename_base = getCustomArtworkLevelConfigFilename(ARTWORK_TYPE_GRAPHICS);
10266 if (fileExists(filename_base))
10267 LoadMenuDesignSettingsFromFilename(filename_base);
10270 filename_local = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
10272 if (filename_local != NULL && !strEqual(filename_base, filename_local))
10273 LoadMenuDesignSettingsFromFilename(filename_local);
10275 InitMenuDesignSettings_SpecialPostProcessing();
10278 void LoadMenuDesignSettings_AfterGraphics()
10280 InitMenuDesignSettings_SpecialPostProcessing_AfterGraphics();
10283 void LoadUserDefinedEditorElementList(int **elements, int *num_elements)
10285 char *filename = getEditorSetupFilename();
10286 SetupFileList *setup_file_list, *list;
10287 SetupFileHash *element_hash;
10288 int num_unknown_tokens = 0;
10291 if ((setup_file_list = loadSetupFileList(filename)) == NULL)
10294 element_hash = newSetupFileHash();
10296 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
10297 setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
10299 /* determined size may be larger than needed (due to unknown elements) */
10301 for (list = setup_file_list; list != NULL; list = list->next)
10304 /* add space for up to 3 more elements for padding that may be needed */
10305 *num_elements += 3;
10307 /* free memory for old list of elements, if needed */
10308 checked_free(*elements);
10310 /* allocate memory for new list of elements */
10311 *elements = checked_malloc(*num_elements * sizeof(int));
10314 for (list = setup_file_list; list != NULL; list = list->next)
10316 char *value = getHashEntry(element_hash, list->token);
10318 if (value == NULL) /* try to find obsolete token mapping */
10320 char *mapped_token = get_mapped_token(list->token);
10322 if (mapped_token != NULL)
10324 value = getHashEntry(element_hash, mapped_token);
10326 free(mapped_token);
10332 (*elements)[(*num_elements)++] = atoi(value);
10336 if (num_unknown_tokens == 0)
10338 Error(ERR_INFO_LINE, "-");
10339 Error(ERR_INFO, "warning: unknown token(s) found in config file:");
10340 Error(ERR_INFO, "- config file: '%s'", filename);
10342 num_unknown_tokens++;
10345 Error(ERR_INFO, "- token: '%s'", list->token);
10349 if (num_unknown_tokens > 0)
10350 Error(ERR_INFO_LINE, "-");
10352 while (*num_elements % 4) /* pad with empty elements, if needed */
10353 (*elements)[(*num_elements)++] = EL_EMPTY;
10355 freeSetupFileList(setup_file_list);
10356 freeSetupFileHash(element_hash);
10359 for (i = 0; i < *num_elements; i++)
10360 printf("editor: element '%s' [%d]\n",
10361 element_info[(*elements)[i]].token_name, (*elements)[i]);
10365 static struct MusicFileInfo *get_music_file_info_ext(char *basename, int music,
10368 SetupFileHash *setup_file_hash = NULL;
10369 struct MusicFileInfo tmp_music_file_info, *new_music_file_info;
10370 char *filename_music, *filename_prefix, *filename_info;
10376 token_to_value_ptr[] =
10378 { "title_header", &tmp_music_file_info.title_header },
10379 { "artist_header", &tmp_music_file_info.artist_header },
10380 { "album_header", &tmp_music_file_info.album_header },
10381 { "year_header", &tmp_music_file_info.year_header },
10383 { "title", &tmp_music_file_info.title },
10384 { "artist", &tmp_music_file_info.artist },
10385 { "album", &tmp_music_file_info.album },
10386 { "year", &tmp_music_file_info.year },
10392 filename_music = (is_sound ? getCustomSoundFilename(basename) :
10393 getCustomMusicFilename(basename));
10395 if (filename_music == NULL)
10398 /* ---------- try to replace file extension ---------- */
10400 filename_prefix = getStringCopy(filename_music);
10401 if (strrchr(filename_prefix, '.') != NULL)
10402 *strrchr(filename_prefix, '.') = '\0';
10403 filename_info = getStringCat2(filename_prefix, ".txt");
10405 if (fileExists(filename_info))
10406 setup_file_hash = loadSetupFileHash(filename_info);
10408 free(filename_prefix);
10409 free(filename_info);
10411 if (setup_file_hash == NULL)
10413 /* ---------- try to add file extension ---------- */
10415 filename_prefix = getStringCopy(filename_music);
10416 filename_info = getStringCat2(filename_prefix, ".txt");
10418 if (fileExists(filename_info))
10419 setup_file_hash = loadSetupFileHash(filename_info);
10421 free(filename_prefix);
10422 free(filename_info);
10425 if (setup_file_hash == NULL)
10428 /* ---------- music file info found ---------- */
10430 clear_mem(&tmp_music_file_info, sizeof(struct MusicFileInfo));
10432 for (i = 0; token_to_value_ptr[i].token != NULL; i++)
10434 char *value = getHashEntry(setup_file_hash, token_to_value_ptr[i].token);
10436 *token_to_value_ptr[i].value_ptr =
10437 getStringCopy(value != NULL && *value != '\0' ? value : UNKNOWN_NAME);
10440 tmp_music_file_info.basename = getStringCopy(basename);
10441 tmp_music_file_info.music = music;
10442 tmp_music_file_info.is_sound = is_sound;
10444 new_music_file_info = checked_malloc(sizeof(struct MusicFileInfo));
10445 *new_music_file_info = tmp_music_file_info;
10447 return new_music_file_info;
10450 static struct MusicFileInfo *get_music_file_info(char *basename, int music)
10452 return get_music_file_info_ext(basename, music, FALSE);
10455 static struct MusicFileInfo *get_sound_file_info(char *basename, int sound)
10457 return get_music_file_info_ext(basename, sound, TRUE);
10460 static boolean music_info_listed_ext(struct MusicFileInfo *list,
10461 char *basename, boolean is_sound)
10463 for (; list != NULL; list = list->next)
10464 if (list->is_sound == is_sound && strEqual(list->basename, basename))
10470 static boolean music_info_listed(struct MusicFileInfo *list, char *basename)
10472 return music_info_listed_ext(list, basename, FALSE);
10475 static boolean sound_info_listed(struct MusicFileInfo *list, char *basename)
10477 return music_info_listed_ext(list, basename, TRUE);
10480 void LoadMusicInfo()
10482 char *music_directory = getCustomMusicDirectory();
10483 int num_music = getMusicListSize();
10484 int num_music_noconf = 0;
10485 int num_sounds = getSoundListSize();
10487 DirectoryEntry *dir_entry;
10488 struct FileInfo *music, *sound;
10489 struct MusicFileInfo *next, **new;
10492 while (music_file_info != NULL)
10494 next = music_file_info->next;
10496 checked_free(music_file_info->basename);
10498 checked_free(music_file_info->title_header);
10499 checked_free(music_file_info->artist_header);
10500 checked_free(music_file_info->album_header);
10501 checked_free(music_file_info->year_header);
10503 checked_free(music_file_info->title);
10504 checked_free(music_file_info->artist);
10505 checked_free(music_file_info->album);
10506 checked_free(music_file_info->year);
10508 free(music_file_info);
10510 music_file_info = next;
10513 new = &music_file_info;
10515 for (i = 0; i < num_music; i++)
10517 music = getMusicListEntry(i);
10519 if (music->filename == NULL)
10522 if (strEqual(music->filename, UNDEFINED_FILENAME))
10525 /* a configured file may be not recognized as music */
10526 if (!FileIsMusic(music->filename))
10529 if (!music_info_listed(music_file_info, music->filename))
10531 *new = get_music_file_info(music->filename, i);
10534 new = &(*new)->next;
10538 if ((dir = openDirectory(music_directory)) == NULL)
10540 Error(ERR_WARN, "cannot read music directory '%s'", music_directory);
10544 while ((dir_entry = readDirectory(dir)) != NULL) /* loop all entries */
10546 char *basename = dir_entry->basename;
10547 boolean music_already_used = FALSE;
10550 /* skip all music files that are configured in music config file */
10551 for (i = 0; i < num_music; i++)
10553 music = getMusicListEntry(i);
10555 if (music->filename == NULL)
10558 if (strEqual(basename, music->filename))
10560 music_already_used = TRUE;
10565 if (music_already_used)
10568 if (!FileIsMusic(dir_entry->filename))
10571 if (!music_info_listed(music_file_info, basename))
10573 *new = get_music_file_info(basename, MAP_NOCONF_MUSIC(num_music_noconf));
10576 new = &(*new)->next;
10579 num_music_noconf++;
10582 closeDirectory(dir);
10584 for (i = 0; i < num_sounds; i++)
10586 sound = getSoundListEntry(i);
10588 if (sound->filename == NULL)
10591 if (strEqual(sound->filename, UNDEFINED_FILENAME))
10594 /* a configured file may be not recognized as sound */
10595 if (!FileIsSound(sound->filename))
10598 if (!sound_info_listed(music_file_info, sound->filename))
10600 *new = get_sound_file_info(sound->filename, i);
10602 new = &(*new)->next;
10607 void add_helpanim_entry(int element, int action, int direction, int delay,
10608 int *num_list_entries)
10610 struct HelpAnimInfo *new_list_entry;
10611 (*num_list_entries)++;
10614 checked_realloc(helpanim_info,
10615 *num_list_entries * sizeof(struct HelpAnimInfo));
10616 new_list_entry = &helpanim_info[*num_list_entries - 1];
10618 new_list_entry->element = element;
10619 new_list_entry->action = action;
10620 new_list_entry->direction = direction;
10621 new_list_entry->delay = delay;
10624 void print_unknown_token(char *filename, char *token, int token_nr)
10628 Error(ERR_INFO_LINE, "-");
10629 Error(ERR_INFO, "warning: unknown token(s) found in config file:");
10630 Error(ERR_INFO, "- config file: '%s'", filename);
10633 Error(ERR_INFO, "- token: '%s'", token);
10636 void print_unknown_token_end(int token_nr)
10639 Error(ERR_INFO_LINE, "-");
10642 void LoadHelpAnimInfo()
10644 char *filename = getHelpAnimFilename();
10645 SetupFileList *setup_file_list = NULL, *list;
10646 SetupFileHash *element_hash, *action_hash, *direction_hash;
10647 int num_list_entries = 0;
10648 int num_unknown_tokens = 0;
10651 if (fileExists(filename))
10652 setup_file_list = loadSetupFileList(filename);
10654 if (setup_file_list == NULL)
10656 /* use reliable default values from static configuration */
10657 SetupFileList *insert_ptr;
10659 insert_ptr = setup_file_list =
10660 newSetupFileList(helpanim_config[0].token,
10661 helpanim_config[0].value);
10663 for (i = 1; helpanim_config[i].token; i++)
10664 insert_ptr = addListEntry(insert_ptr,
10665 helpanim_config[i].token,
10666 helpanim_config[i].value);
10669 element_hash = newSetupFileHash();
10670 action_hash = newSetupFileHash();
10671 direction_hash = newSetupFileHash();
10673 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
10674 setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
10676 for (i = 0; i < NUM_ACTIONS; i++)
10677 setHashEntry(action_hash, element_action_info[i].suffix,
10678 i_to_a(element_action_info[i].value));
10680 /* do not store direction index (bit) here, but direction value! */
10681 for (i = 0; i < NUM_DIRECTIONS_FULL; i++)
10682 setHashEntry(direction_hash, element_direction_info[i].suffix,
10683 i_to_a(1 << element_direction_info[i].value));
10685 for (list = setup_file_list; list != NULL; list = list->next)
10687 char *element_token, *action_token, *direction_token;
10688 char *element_value, *action_value, *direction_value;
10689 int delay = atoi(list->value);
10691 if (strEqual(list->token, "end"))
10693 add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
10698 /* first try to break element into element/action/direction parts;
10699 if this does not work, also accept combined "element[.act][.dir]"
10700 elements (like "dynamite.active"), which are unique elements */
10702 if (strchr(list->token, '.') == NULL) /* token contains no '.' */
10704 element_value = getHashEntry(element_hash, list->token);
10705 if (element_value != NULL) /* element found */
10706 add_helpanim_entry(atoi(element_value), -1, -1, delay,
10707 &num_list_entries);
10710 /* no further suffixes found -- this is not an element */
10711 print_unknown_token(filename, list->token, num_unknown_tokens++);
10717 /* token has format "<prefix>.<something>" */
10719 action_token = strchr(list->token, '.'); /* suffix may be action ... */
10720 direction_token = action_token; /* ... or direction */
10722 element_token = getStringCopy(list->token);
10723 *strchr(element_token, '.') = '\0';
10725 element_value = getHashEntry(element_hash, element_token);
10727 if (element_value == NULL) /* this is no element */
10729 element_value = getHashEntry(element_hash, list->token);
10730 if (element_value != NULL) /* combined element found */
10731 add_helpanim_entry(atoi(element_value), -1, -1, delay,
10732 &num_list_entries);
10734 print_unknown_token(filename, list->token, num_unknown_tokens++);
10736 free(element_token);
10741 action_value = getHashEntry(action_hash, action_token);
10743 if (action_value != NULL) /* action found */
10745 add_helpanim_entry(atoi(element_value), atoi(action_value), -1, delay,
10746 &num_list_entries);
10748 free(element_token);
10753 direction_value = getHashEntry(direction_hash, direction_token);
10755 if (direction_value != NULL) /* direction found */
10757 add_helpanim_entry(atoi(element_value), -1, atoi(direction_value), delay,
10758 &num_list_entries);
10760 free(element_token);
10765 if (strchr(action_token + 1, '.') == NULL)
10767 /* no further suffixes found -- this is not an action nor direction */
10769 element_value = getHashEntry(element_hash, list->token);
10770 if (element_value != NULL) /* combined element found */
10771 add_helpanim_entry(atoi(element_value), -1, -1, delay,
10772 &num_list_entries);
10774 print_unknown_token(filename, list->token, num_unknown_tokens++);
10776 free(element_token);
10781 /* token has format "<prefix>.<suffix>.<something>" */
10783 direction_token = strchr(action_token + 1, '.');
10785 action_token = getStringCopy(action_token);
10786 *strchr(action_token + 1, '.') = '\0';
10788 action_value = getHashEntry(action_hash, action_token);
10790 if (action_value == NULL) /* this is no action */
10792 element_value = getHashEntry(element_hash, list->token);
10793 if (element_value != NULL) /* combined element found */
10794 add_helpanim_entry(atoi(element_value), -1, -1, delay,
10795 &num_list_entries);
10797 print_unknown_token(filename, list->token, num_unknown_tokens++);
10799 free(element_token);
10800 free(action_token);
10805 direction_value = getHashEntry(direction_hash, direction_token);
10807 if (direction_value != NULL) /* direction found */
10809 add_helpanim_entry(atoi(element_value), atoi(action_value),
10810 atoi(direction_value), delay, &num_list_entries);
10812 free(element_token);
10813 free(action_token);
10818 /* this is no direction */
10820 element_value = getHashEntry(element_hash, list->token);
10821 if (element_value != NULL) /* combined element found */
10822 add_helpanim_entry(atoi(element_value), -1, -1, delay,
10823 &num_list_entries);
10825 print_unknown_token(filename, list->token, num_unknown_tokens++);
10827 free(element_token);
10828 free(action_token);
10831 print_unknown_token_end(num_unknown_tokens);
10833 add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
10834 add_helpanim_entry(HELPANIM_LIST_END, -1, -1, -1, &num_list_entries);
10836 freeSetupFileList(setup_file_list);
10837 freeSetupFileHash(element_hash);
10838 freeSetupFileHash(action_hash);
10839 freeSetupFileHash(direction_hash);
10842 for (i = 0; i < num_list_entries; i++)
10843 printf("::: '%s': %d, %d, %d => %d\n",
10844 EL_NAME(helpanim_info[i].element),
10845 helpanim_info[i].element,
10846 helpanim_info[i].action,
10847 helpanim_info[i].direction,
10848 helpanim_info[i].delay);
10852 void LoadHelpTextInfo()
10854 char *filename = getHelpTextFilename();
10857 if (helptext_info != NULL)
10859 freeSetupFileHash(helptext_info);
10860 helptext_info = NULL;
10863 if (fileExists(filename))
10864 helptext_info = loadSetupFileHash(filename);
10866 if (helptext_info == NULL)
10868 /* use reliable default values from static configuration */
10869 helptext_info = newSetupFileHash();
10871 for (i = 0; helptext_config[i].token; i++)
10872 setHashEntry(helptext_info,
10873 helptext_config[i].token,
10874 helptext_config[i].value);
10878 BEGIN_HASH_ITERATION(helptext_info, itr)
10880 printf("::: '%s' => '%s'\n",
10881 HASH_ITERATION_TOKEN(itr), HASH_ITERATION_VALUE(itr));
10883 END_HASH_ITERATION(hash, itr)
10888 /* ------------------------------------------------------------------------- */
10889 /* convert levels */
10890 /* ------------------------------------------------------------------------- */
10892 #define MAX_NUM_CONVERT_LEVELS 1000
10894 void ConvertLevels()
10896 static LevelDirTree *convert_leveldir = NULL;
10897 static int convert_level_nr = -1;
10898 static int num_levels_handled = 0;
10899 static int num_levels_converted = 0;
10900 static boolean levels_failed[MAX_NUM_CONVERT_LEVELS];
10903 convert_leveldir = getTreeInfoFromIdentifier(leveldir_first,
10904 global.convert_leveldir);
10906 if (convert_leveldir == NULL)
10907 Error(ERR_EXIT, "no such level identifier: '%s'",
10908 global.convert_leveldir);
10910 leveldir_current = convert_leveldir;
10912 if (global.convert_level_nr != -1)
10914 convert_leveldir->first_level = global.convert_level_nr;
10915 convert_leveldir->last_level = global.convert_level_nr;
10918 convert_level_nr = convert_leveldir->first_level;
10920 PrintLine("=", 79);
10921 Print("Converting levels\n");
10922 PrintLine("-", 79);
10923 Print("Level series identifier: '%s'\n", convert_leveldir->identifier);
10924 Print("Level series name: '%s'\n", convert_leveldir->name);
10925 Print("Level series author: '%s'\n", convert_leveldir->author);
10926 Print("Number of levels: %d\n", convert_leveldir->levels);
10927 PrintLine("=", 79);
10930 for (i = 0; i < MAX_NUM_CONVERT_LEVELS; i++)
10931 levels_failed[i] = FALSE;
10933 while (convert_level_nr <= convert_leveldir->last_level)
10935 char *level_filename;
10938 level_nr = convert_level_nr++;
10940 Print("Level %03d: ", level_nr);
10942 LoadLevel(level_nr);
10943 if (level.no_level_file || level.no_valid_file)
10945 Print("(no level)\n");
10949 Print("converting level ... ");
10951 level_filename = getDefaultLevelFilename(level_nr);
10952 new_level = !fileExists(level_filename);
10956 SaveLevel(level_nr);
10958 num_levels_converted++;
10960 Print("converted.\n");
10964 if (level_nr >= 0 && level_nr < MAX_NUM_CONVERT_LEVELS)
10965 levels_failed[level_nr] = TRUE;
10967 Print("NOT CONVERTED -- LEVEL ALREADY EXISTS.\n");
10970 num_levels_handled++;
10974 PrintLine("=", 79);
10975 Print("Number of levels handled: %d\n", num_levels_handled);
10976 Print("Number of levels converted: %d (%d%%)\n", num_levels_converted,
10977 (num_levels_handled ?
10978 num_levels_converted * 100 / num_levels_handled : 0));
10979 PrintLine("-", 79);
10980 Print("Summary (for automatic parsing by scripts):\n");
10981 Print("LEVELDIR '%s', CONVERTED %d/%d (%d%%)",
10982 convert_leveldir->identifier, num_levels_converted,
10983 num_levels_handled,
10984 (num_levels_handled ?
10985 num_levels_converted * 100 / num_levels_handled : 0));
10987 if (num_levels_handled != num_levels_converted)
10989 Print(", FAILED:");
10990 for (i = 0; i < MAX_NUM_CONVERT_LEVELS; i++)
10991 if (levels_failed[i])
10996 PrintLine("=", 79);
10998 CloseAllAndExit(0);
11002 /* ------------------------------------------------------------------------- */
11003 /* create and save images for use in level sketches (raw BMP format) */
11004 /* ------------------------------------------------------------------------- */
11006 void CreateLevelSketchImages()
11008 #if defined(TARGET_SDL)
11013 InitElementPropertiesGfxElement();
11015 bitmap1 = CreateBitmap(TILEX, TILEY, DEFAULT_DEPTH);
11016 bitmap2 = CreateBitmap(MINI_TILEX, MINI_TILEY, DEFAULT_DEPTH);
11018 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
11020 Bitmap *src_bitmap;
11022 int element = getMappedElement(i);
11023 int graphic = el2edimg(element);
11024 char basename1[16];
11025 char basename2[16];
11029 sprintf(basename1, "%03d.bmp", i);
11030 sprintf(basename2, "%03ds.bmp", i);
11032 filename1 = getPath2(global.create_images_dir, basename1);
11033 filename2 = getPath2(global.create_images_dir, basename2);
11035 getFixedGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
11036 BlitBitmap(src_bitmap, bitmap1, src_x, src_y, TILEX, TILEY,
11039 if (SDL_SaveBMP(bitmap1->surface, filename1) != 0)
11040 Error(ERR_EXIT, "cannot save level sketch image file '%s'", filename1);
11042 getMiniGraphicSource(graphic, &src_bitmap, &src_x, &src_y);
11043 BlitBitmap(src_bitmap, bitmap2, src_x, src_y, MINI_TILEX, MINI_TILEY, 0, 0);
11045 if (SDL_SaveBMP(bitmap2->surface, filename2) != 0)
11046 Error(ERR_EXIT, "cannot save level sketch image file '%s'", filename2);
11052 printf("%03d `%03d%c", i, i, (i % 10 < 9 ? ' ' : '\n'));
11055 FreeBitmap(bitmap1);
11056 FreeBitmap(bitmap2);
11061 Error(ERR_INFO, "%d normal and small images created", NUM_FILE_ELEMENTS);
11063 CloseAllAndExit(0);
11068 /* ------------------------------------------------------------------------- */
11069 /* create and save images for custom and group elements (raw BMP format) */
11070 /* ------------------------------------------------------------------------- */
11072 void CreateCustomElementImages(char *directory)
11074 #if defined(TARGET_SDL)
11075 char *src_basename = "RocksCE-template.ilbm";
11076 char *dst_basename = "RocksCE.bmp";
11077 char *src_filename = getPath2(directory, src_basename);
11078 char *dst_filename = getPath2(directory, dst_basename);
11079 Bitmap *src_bitmap;
11081 int yoffset_ce = 0;
11082 int yoffset_ge = (TILEY * NUM_CUSTOM_ELEMENTS / 16);
11085 SDLInitVideoDisplay();
11087 ReCreateBitmap(&backbuffer, video.width, video.height);
11089 src_bitmap = LoadImage(src_filename);
11091 bitmap = CreateBitmap(TILEX * 16 * 2,
11092 TILEY * (NUM_CUSTOM_ELEMENTS + NUM_GROUP_ELEMENTS) / 16,
11095 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
11102 BlitBitmap(src_bitmap, bitmap, 0, 0, TILEX, TILEY,
11103 TILEX * x, TILEY * y + yoffset_ce);
11105 BlitBitmap(src_bitmap, bitmap, 0, TILEY,
11107 TILEX * x + TILEX * 16,
11108 TILEY * y + yoffset_ce);
11110 for (j = 2; j >= 0; j--)
11114 BlitBitmap(src_bitmap, bitmap,
11115 TILEX + c * 7, 0, 6, 10,
11116 TILEX * x + 6 + j * 7,
11117 TILEY * y + 11 + yoffset_ce);
11119 BlitBitmap(src_bitmap, bitmap,
11120 TILEX + c * 8, TILEY, 6, 10,
11121 TILEX * 16 + TILEX * x + 6 + j * 8,
11122 TILEY * y + 10 + yoffset_ce);
11128 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
11135 BlitBitmap(src_bitmap, bitmap, 0, 0, TILEX, TILEY,
11136 TILEX * x, TILEY * y + yoffset_ge);
11138 BlitBitmap(src_bitmap, bitmap, 0, TILEY,
11140 TILEX * x + TILEX * 16,
11141 TILEY * y + yoffset_ge);
11143 for (j = 1; j >= 0; j--)
11147 BlitBitmap(src_bitmap, bitmap, TILEX + c * 10, 11, 10, 10,
11148 TILEX * x + 6 + j * 10,
11149 TILEY * y + 11 + yoffset_ge);
11151 BlitBitmap(src_bitmap, bitmap,
11152 TILEX + c * 8, TILEY + 12, 6, 10,
11153 TILEX * 16 + TILEX * x + 10 + j * 8,
11154 TILEY * y + 10 + yoffset_ge);
11160 if (SDL_SaveBMP(bitmap->surface, dst_filename) != 0)
11161 Error(ERR_EXIT, "cannot save CE graphics file '%s'", dst_filename);
11163 FreeBitmap(bitmap);
11165 CloseAllAndExit(0);