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
261 static struct LevelFileConfigInfo chunk_config_ELEM[] =
263 /* (these values are the same for each player) */
266 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
267 &li.block_last_field, FALSE /* default case for EM levels */
271 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
272 &li.sp_block_last_field, TRUE /* default case for SP levels */
276 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
277 &li.instant_relocation, FALSE
281 TYPE_BOOLEAN, CONF_VALUE_8_BIT(4),
282 &li.can_pass_to_walkable, FALSE
286 TYPE_BOOLEAN, CONF_VALUE_8_BIT(5),
287 &li.block_snap_field, TRUE
291 TYPE_BOOLEAN, CONF_VALUE_8_BIT(6),
292 &li.continuous_snapping, TRUE
296 TYPE_BOOLEAN, CONF_VALUE_8_BIT(12),
297 &li.shifted_relocation, FALSE
301 TYPE_BOOLEAN, CONF_VALUE_8_BIT(15),
302 &li.lazy_relocation, FALSE
305 /* (these values are different for each player) */
308 TYPE_INTEGER, CONF_VALUE_8_BIT(7),
309 &li.initial_player_stepsize[0], STEPSIZE_NORMAL
313 TYPE_BOOLEAN, CONF_VALUE_8_BIT(8),
314 &li.initial_player_gravity[0], FALSE
318 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
319 &li.use_start_element[0], FALSE
323 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
324 &li.start_element[0], EL_PLAYER_1
328 TYPE_BOOLEAN, CONF_VALUE_8_BIT(10),
329 &li.use_artwork_element[0], FALSE
333 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
334 &li.artwork_element[0], EL_PLAYER_1
338 TYPE_BOOLEAN, CONF_VALUE_8_BIT(11),
339 &li.use_explosion_element[0], FALSE
343 TYPE_ELEMENT, CONF_VALUE_16_BIT(3),
344 &li.explosion_element[0], EL_PLAYER_1
348 TYPE_BOOLEAN, CONF_VALUE_8_BIT(13),
349 &li.use_initial_inventory[0], FALSE
353 TYPE_BOOLEAN, CONF_VALUE_8_BIT(14),
354 &li.initial_inventory_size[0], 1
358 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(1),
359 &li.initial_inventory_content[0][0],EL_EMPTY, NULL,
360 &li.initial_inventory_size[0], 1, MAX_INITIAL_INVENTORY_SIZE
365 TYPE_INTEGER, CONF_VALUE_8_BIT(7),
366 &li.initial_player_stepsize[1], STEPSIZE_NORMAL
370 TYPE_BOOLEAN, CONF_VALUE_8_BIT(8),
371 &li.initial_player_gravity[1], FALSE
375 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
376 &li.use_start_element[1], FALSE
380 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
381 &li.start_element[1], EL_PLAYER_2
385 TYPE_BOOLEAN, CONF_VALUE_8_BIT(10),
386 &li.use_artwork_element[1], FALSE
390 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
391 &li.artwork_element[1], EL_PLAYER_2
395 TYPE_BOOLEAN, CONF_VALUE_8_BIT(11),
396 &li.use_explosion_element[1], FALSE
400 TYPE_ELEMENT, CONF_VALUE_16_BIT(3),
401 &li.explosion_element[1], EL_PLAYER_2
405 TYPE_BOOLEAN, CONF_VALUE_8_BIT(13),
406 &li.use_initial_inventory[1], FALSE
410 TYPE_BOOLEAN, CONF_VALUE_8_BIT(14),
411 &li.initial_inventory_size[1], 1
415 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(1),
416 &li.initial_inventory_content[1][0],EL_EMPTY, NULL,
417 &li.initial_inventory_size[1], 1, MAX_INITIAL_INVENTORY_SIZE
422 TYPE_INTEGER, CONF_VALUE_8_BIT(7),
423 &li.initial_player_stepsize[2], STEPSIZE_NORMAL
427 TYPE_BOOLEAN, CONF_VALUE_8_BIT(8),
428 &li.initial_player_gravity[2], FALSE
432 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
433 &li.use_start_element[2], FALSE
437 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
438 &li.start_element[2], EL_PLAYER_3
442 TYPE_BOOLEAN, CONF_VALUE_8_BIT(10),
443 &li.use_artwork_element[2], FALSE
447 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
448 &li.artwork_element[2], EL_PLAYER_3
452 TYPE_BOOLEAN, CONF_VALUE_8_BIT(11),
453 &li.use_explosion_element[2], FALSE
457 TYPE_ELEMENT, CONF_VALUE_16_BIT(3),
458 &li.explosion_element[2], EL_PLAYER_3
462 TYPE_BOOLEAN, CONF_VALUE_8_BIT(13),
463 &li.use_initial_inventory[2], FALSE
467 TYPE_BOOLEAN, CONF_VALUE_8_BIT(14),
468 &li.initial_inventory_size[2], 1
472 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(1),
473 &li.initial_inventory_content[2][0],EL_EMPTY, NULL,
474 &li.initial_inventory_size[2], 1, MAX_INITIAL_INVENTORY_SIZE
479 TYPE_INTEGER, CONF_VALUE_8_BIT(7),
480 &li.initial_player_stepsize[3], STEPSIZE_NORMAL
484 TYPE_BOOLEAN, CONF_VALUE_8_BIT(8),
485 &li.initial_player_gravity[3], FALSE
489 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
490 &li.use_start_element[3], FALSE
494 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
495 &li.start_element[3], EL_PLAYER_4
499 TYPE_BOOLEAN, CONF_VALUE_8_BIT(10),
500 &li.use_artwork_element[3], FALSE
504 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
505 &li.artwork_element[3], EL_PLAYER_4
509 TYPE_BOOLEAN, CONF_VALUE_8_BIT(11),
510 &li.use_explosion_element[3], FALSE
514 TYPE_ELEMENT, CONF_VALUE_16_BIT(3),
515 &li.explosion_element[3], EL_PLAYER_4
519 TYPE_BOOLEAN, CONF_VALUE_8_BIT(13),
520 &li.use_initial_inventory[3], FALSE
524 TYPE_BOOLEAN, CONF_VALUE_8_BIT(14),
525 &li.initial_inventory_size[3], 1
529 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(1),
530 &li.initial_inventory_content[3][0],EL_EMPTY, NULL,
531 &li.initial_inventory_size[3], 1, MAX_INITIAL_INVENTORY_SIZE
536 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
537 &li.score[SC_EMERALD], 10
542 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
543 &li.score[SC_DIAMOND], 10
548 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
549 &li.score[SC_BUG], 10
554 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
555 &li.score[SC_SPACESHIP], 10
560 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
561 &li.score[SC_PACMAN], 10
566 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
567 &li.score[SC_NUT], 10
572 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
573 &li.score[SC_DYNAMITE], 10
578 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
579 &li.score[SC_KEY], 10
584 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
585 &li.score[SC_PEARL], 10
590 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
591 &li.score[SC_CRYSTAL], 10
596 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
597 &li.amoeba_content, EL_DIAMOND
601 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
606 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
607 &li.grow_into_diggable, TRUE
612 TYPE_CONTENT_LIST, CONF_VALUE_BYTES(1),
613 &li.yamyam_content, EL_ROCK, NULL,
614 &li.num_yamyam_contents, 4, MAX_ELEMENT_CONTENTS
618 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
619 &li.score[SC_YAMYAM], 10
624 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
625 &li.score[SC_ROBOT], 10
629 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
635 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
641 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
642 &li.time_magic_wall, 10
647 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
648 &li.game_of_life[0], 2
652 TYPE_INTEGER, CONF_VALUE_8_BIT(2),
653 &li.game_of_life[1], 3
657 TYPE_INTEGER, CONF_VALUE_8_BIT(3),
658 &li.game_of_life[2], 3
662 TYPE_INTEGER, CONF_VALUE_8_BIT(4),
663 &li.game_of_life[3], 3
668 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
673 TYPE_INTEGER, CONF_VALUE_8_BIT(2),
678 TYPE_INTEGER, CONF_VALUE_8_BIT(3),
683 TYPE_INTEGER, CONF_VALUE_8_BIT(4),
688 EL_TIMEGATE_SWITCH, -1,
689 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
690 &li.time_timegate, 10
694 EL_LIGHT_SWITCH_ACTIVE, -1,
695 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
700 EL_SHIELD_NORMAL, -1,
701 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
702 &li.shield_normal_time, 10
705 EL_SHIELD_NORMAL, -1,
706 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
707 &li.score[SC_SHIELD], 10
711 EL_SHIELD_DEADLY, -1,
712 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
713 &li.shield_deadly_time, 10
716 EL_SHIELD_DEADLY, -1,
717 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
718 &li.score[SC_SHIELD], 10
723 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
728 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
729 &li.extra_time_score, 10
733 EL_TIME_ORB_FULL, -1,
734 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
735 &li.time_orb_time, 10
738 EL_TIME_ORB_FULL, -1,
739 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
740 &li.use_time_orb_bug, FALSE
745 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
746 &li.use_spring_bug, FALSE
751 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
752 &li.android_move_time, 10
756 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
757 &li.android_clone_time, 10
761 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(1),
762 &li.android_clone_element[0], EL_EMPTY, NULL,
763 &li.num_android_clone_elements, 1, MAX_ANDROID_ELEMENTS
768 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
773 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
778 EL_EMC_MAGNIFIER, -1,
779 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
780 &li.magnify_score, 10
783 EL_EMC_MAGNIFIER, -1,
784 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
789 EL_EMC_MAGIC_BALL, -1,
790 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
794 EL_EMC_MAGIC_BALL, -1,
795 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
796 &li.ball_random, FALSE
799 EL_EMC_MAGIC_BALL, -1,
800 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
801 &li.ball_state_initial, FALSE
804 EL_EMC_MAGIC_BALL, -1,
805 TYPE_CONTENT_LIST, CONF_VALUE_BYTES(1),
806 &li.ball_content, EL_EMPTY, NULL,
807 &li.num_ball_contents, 4, MAX_ELEMENT_CONTENTS
812 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
813 &li.mm_laser_red, FALSE
817 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
818 &li.mm_laser_green, FALSE
822 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
823 &li.mm_laser_blue, TRUE
828 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
829 &li.df_laser_red, TRUE
833 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
834 &li.df_laser_green, TRUE
838 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
839 &li.df_laser_blue, FALSE
843 EL_MM_FUSE_ACTIVE, -1,
844 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
849 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
854 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
858 EL_MM_STEEL_BLOCK, -1,
859 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
860 &li.mm_time_block, 75
864 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
865 &li.score[SC_ELEM_BONUS], 10
868 /* ---------- unused values ----------------------------------------------- */
871 EL_UNKNOWN, SAVE_CONF_NEVER,
872 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
873 &li.score[SC_UNKNOWN_15], 10
883 static struct LevelFileConfigInfo chunk_config_NOTE[] =
887 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
888 &xx_envelope.xsize, MAX_ENVELOPE_XSIZE,
892 TYPE_INTEGER, CONF_VALUE_8_BIT(2),
893 &xx_envelope.ysize, MAX_ENVELOPE_YSIZE,
898 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
899 &xx_envelope.autowrap, FALSE
903 TYPE_BOOLEAN, CONF_VALUE_8_BIT(4),
904 &xx_envelope.centered, FALSE
909 TYPE_STRING, CONF_VALUE_BYTES(1),
910 &xx_envelope.text, -1, NULL,
911 &xx_string_length_unused, -1, MAX_ENVELOPE_TEXT_LEN,
912 &xx_default_string_empty[0]
922 static struct LevelFileConfigInfo chunk_config_CUSX_base[] =
926 TYPE_STRING, CONF_VALUE_BYTES(1),
927 &xx_ei.description[0], -1,
928 &yy_ei.description[0],
929 &xx_string_length_unused, -1, MAX_ELEMENT_NAME_LEN,
930 &xx_default_description[0]
935 TYPE_BITFIELD, CONF_VALUE_32_BIT(1),
936 &xx_ei.properties[EP_BITFIELD_BASE_NR], EP_BITMASK_BASE_DEFAULT,
937 &yy_ei.properties[EP_BITFIELD_BASE_NR]
939 #if ENABLE_RESERVED_CODE
940 /* (reserved for later use) */
943 TYPE_BITFIELD, CONF_VALUE_32_BIT(2),
944 &xx_ei.properties[EP_BITFIELD_BASE_NR + 1], EP_BITMASK_DEFAULT,
945 &yy_ei.properties[EP_BITFIELD_BASE_NR + 1]
951 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
952 &xx_ei.use_gfx_element, FALSE,
953 &yy_ei.use_gfx_element
957 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
958 &xx_ei.gfx_element_initial, EL_EMPTY_SPACE,
959 &yy_ei.gfx_element_initial
964 TYPE_BITFIELD, CONF_VALUE_8_BIT(2),
965 &xx_ei.access_direction, MV_ALL_DIRECTIONS,
966 &yy_ei.access_direction
971 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
972 &xx_ei.collect_score_initial, 10,
973 &yy_ei.collect_score_initial
977 TYPE_INTEGER, CONF_VALUE_16_BIT(3),
978 &xx_ei.collect_count_initial, 1,
979 &yy_ei.collect_count_initial
984 TYPE_INTEGER, CONF_VALUE_16_BIT(4),
985 &xx_ei.ce_value_fixed_initial, 0,
986 &yy_ei.ce_value_fixed_initial
990 TYPE_INTEGER, CONF_VALUE_16_BIT(5),
991 &xx_ei.ce_value_random_initial, 0,
992 &yy_ei.ce_value_random_initial
996 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
997 &xx_ei.use_last_ce_value, FALSE,
998 &yy_ei.use_last_ce_value
1003 TYPE_INTEGER, CONF_VALUE_16_BIT(6),
1004 &xx_ei.push_delay_fixed, 8,
1005 &yy_ei.push_delay_fixed
1009 TYPE_INTEGER, CONF_VALUE_16_BIT(7),
1010 &xx_ei.push_delay_random, 8,
1011 &yy_ei.push_delay_random
1015 TYPE_INTEGER, CONF_VALUE_16_BIT(8),
1016 &xx_ei.drop_delay_fixed, 0,
1017 &yy_ei.drop_delay_fixed
1021 TYPE_INTEGER, CONF_VALUE_16_BIT(9),
1022 &xx_ei.drop_delay_random, 0,
1023 &yy_ei.drop_delay_random
1027 TYPE_INTEGER, CONF_VALUE_16_BIT(10),
1028 &xx_ei.move_delay_fixed, 0,
1029 &yy_ei.move_delay_fixed
1033 TYPE_INTEGER, CONF_VALUE_16_BIT(11),
1034 &xx_ei.move_delay_random, 0,
1035 &yy_ei.move_delay_random
1040 TYPE_BITFIELD, CONF_VALUE_32_BIT(3),
1041 &xx_ei.move_pattern, MV_ALL_DIRECTIONS,
1046 TYPE_BITFIELD, CONF_VALUE_8_BIT(4),
1047 &xx_ei.move_direction_initial, MV_START_AUTOMATIC,
1048 &yy_ei.move_direction_initial
1052 TYPE_INTEGER, CONF_VALUE_8_BIT(5),
1053 &xx_ei.move_stepsize, TILEX / 8,
1054 &yy_ei.move_stepsize
1059 TYPE_ELEMENT, CONF_VALUE_16_BIT(12),
1060 &xx_ei.move_enter_element, EL_EMPTY_SPACE,
1061 &yy_ei.move_enter_element
1065 TYPE_ELEMENT, CONF_VALUE_16_BIT(13),
1066 &xx_ei.move_leave_element, EL_EMPTY_SPACE,
1067 &yy_ei.move_leave_element
1071 TYPE_INTEGER, CONF_VALUE_8_BIT(6),
1072 &xx_ei.move_leave_type, LEAVE_TYPE_UNLIMITED,
1073 &yy_ei.move_leave_type
1078 TYPE_INTEGER, CONF_VALUE_8_BIT(7),
1079 &xx_ei.slippery_type, SLIPPERY_ANY_RANDOM,
1080 &yy_ei.slippery_type
1085 TYPE_INTEGER, CONF_VALUE_8_BIT(8),
1086 &xx_ei.explosion_type, EXPLODES_3X3,
1087 &yy_ei.explosion_type
1091 TYPE_INTEGER, CONF_VALUE_16_BIT(14),
1092 &xx_ei.explosion_delay, 16,
1093 &yy_ei.explosion_delay
1097 TYPE_INTEGER, CONF_VALUE_16_BIT(15),
1098 &xx_ei.ignition_delay, 8,
1099 &yy_ei.ignition_delay
1104 TYPE_CONTENT_LIST, CONF_VALUE_BYTES(2),
1105 &xx_ei.content, EL_EMPTY_SPACE,
1107 &xx_num_contents, 1, 1
1110 /* ---------- "num_change_pages" must be the last entry ------------------- */
1113 -1, SAVE_CONF_ALWAYS,
1114 TYPE_INTEGER, CONF_VALUE_8_BIT(9),
1115 &xx_ei.num_change_pages, 1,
1116 &yy_ei.num_change_pages
1127 static struct LevelFileConfigInfo chunk_config_CUSX_change[] =
1129 /* ---------- "current_change_page" must be the first entry --------------- */
1132 -1, SAVE_CONF_ALWAYS,
1133 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
1134 &xx_current_change_page, -1
1137 /* ---------- (the remaining entries can be in any order) ----------------- */
1141 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
1142 &xx_change.can_change, FALSE
1147 TYPE_BITFIELD, CONF_VALUE_32_BIT(1),
1148 &xx_event_bits[0], 0
1152 TYPE_BITFIELD, CONF_VALUE_32_BIT(2),
1153 &xx_event_bits[1], 0
1158 TYPE_BITFIELD, CONF_VALUE_8_BIT(3),
1159 &xx_change.trigger_player, CH_PLAYER_ANY
1163 TYPE_BITFIELD, CONF_VALUE_8_BIT(4),
1164 &xx_change.trigger_side, CH_SIDE_ANY
1168 TYPE_BITFIELD, CONF_VALUE_32_BIT(3),
1169 &xx_change.trigger_page, CH_PAGE_ANY
1174 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
1175 &xx_change.target_element, EL_EMPTY_SPACE
1180 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
1181 &xx_change.delay_fixed, 0
1185 TYPE_INTEGER, CONF_VALUE_16_BIT(3),
1186 &xx_change.delay_random, 0
1190 TYPE_INTEGER, CONF_VALUE_16_BIT(4),
1191 &xx_change.delay_frames, FRAMES_PER_SECOND
1196 TYPE_ELEMENT, CONF_VALUE_16_BIT(5),
1197 &xx_change.initial_trigger_element, EL_EMPTY_SPACE
1202 TYPE_BOOLEAN, CONF_VALUE_8_BIT(6),
1203 &xx_change.explode, FALSE
1207 TYPE_BOOLEAN, CONF_VALUE_8_BIT(7),
1208 &xx_change.use_target_content, FALSE
1212 TYPE_BOOLEAN, CONF_VALUE_8_BIT(8),
1213 &xx_change.only_if_complete, FALSE
1217 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
1218 &xx_change.use_random_replace, FALSE
1222 TYPE_INTEGER, CONF_VALUE_8_BIT(10),
1223 &xx_change.random_percentage, 100
1227 TYPE_INTEGER, CONF_VALUE_8_BIT(11),
1228 &xx_change.replace_when, CP_WHEN_EMPTY
1233 TYPE_BOOLEAN, CONF_VALUE_8_BIT(12),
1234 &xx_change.has_action, FALSE
1238 TYPE_INTEGER, CONF_VALUE_8_BIT(13),
1239 &xx_change.action_type, CA_NO_ACTION
1243 TYPE_INTEGER, CONF_VALUE_8_BIT(14),
1244 &xx_change.action_mode, CA_MODE_UNDEFINED
1248 TYPE_INTEGER, CONF_VALUE_16_BIT(6),
1249 &xx_change.action_arg, CA_ARG_UNDEFINED
1254 TYPE_ELEMENT, CONF_VALUE_16_BIT(7),
1255 &xx_change.action_element, EL_EMPTY_SPACE
1260 TYPE_CONTENT_LIST, CONF_VALUE_BYTES(1),
1261 &xx_change.target_content, EL_EMPTY_SPACE, NULL,
1262 &xx_num_contents, 1, 1
1272 static struct LevelFileConfigInfo chunk_config_GRPX[] =
1276 TYPE_STRING, CONF_VALUE_BYTES(1),
1277 &xx_ei.description[0], -1, NULL,
1278 &xx_string_length_unused, -1, MAX_ELEMENT_NAME_LEN,
1279 &xx_default_description[0]
1284 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1285 &xx_ei.use_gfx_element, FALSE
1289 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
1290 &xx_ei.gfx_element_initial, EL_EMPTY_SPACE
1295 TYPE_INTEGER, CONF_VALUE_8_BIT(2),
1296 &xx_group.choice_mode, ANIM_RANDOM
1301 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(2),
1302 &xx_group.element[0], EL_EMPTY_SPACE, NULL,
1303 &xx_group.num_elements, 1, MAX_ELEMENTS_IN_GROUP
1313 static struct LevelFileConfigInfo chunk_config_CONF[] = /* (OBSOLETE) */
1317 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
1318 &li.block_snap_field, TRUE
1322 TYPE_BOOLEAN, CONF_VALUE_8_BIT(13),
1323 &li.continuous_snapping, TRUE
1327 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
1328 &li.initial_player_stepsize[0], STEPSIZE_NORMAL
1332 TYPE_BOOLEAN, CONF_VALUE_8_BIT(10),
1333 &li.use_start_element[0], FALSE
1337 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
1338 &li.start_element[0], EL_PLAYER_1
1342 TYPE_BOOLEAN, CONF_VALUE_8_BIT(11),
1343 &li.use_artwork_element[0], FALSE
1347 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
1348 &li.artwork_element[0], EL_PLAYER_1
1352 TYPE_BOOLEAN, CONF_VALUE_8_BIT(12),
1353 &li.use_explosion_element[0], FALSE
1357 TYPE_ELEMENT, CONF_VALUE_16_BIT(3),
1358 &li.explosion_element[0], EL_PLAYER_1
1373 filetype_id_list[] =
1375 { LEVEL_FILE_TYPE_RND, "RND" },
1376 { LEVEL_FILE_TYPE_BD, "BD" },
1377 { LEVEL_FILE_TYPE_EM, "EM" },
1378 { LEVEL_FILE_TYPE_SP, "SP" },
1379 { LEVEL_FILE_TYPE_DX, "DX" },
1380 { LEVEL_FILE_TYPE_SB, "SB" },
1381 { LEVEL_FILE_TYPE_DC, "DC" },
1382 { LEVEL_FILE_TYPE_MM, "MM" },
1383 { LEVEL_FILE_TYPE_MM, "DF" },
1388 /* ========================================================================= */
1389 /* level file functions */
1390 /* ========================================================================= */
1392 static boolean check_special_flags(char *flag)
1394 if (strEqual(options.special_flags, flag) ||
1395 strEqual(leveldir_current->special_flags, flag))
1401 static struct DateInfo getCurrentDate()
1403 time_t epoch_seconds = time(NULL);
1404 struct tm *now = localtime(&epoch_seconds);
1405 struct DateInfo date;
1407 date.year = now->tm_year + 1900;
1408 date.month = now->tm_mon + 1;
1409 date.day = now->tm_mday;
1411 date.src = DATE_SRC_CLOCK;
1416 static void resetEventFlags(struct ElementChangeInfo *change)
1420 for (i = 0; i < NUM_CHANGE_EVENTS; i++)
1421 change->has_event[i] = FALSE;
1424 static void resetEventBits()
1428 for (i = 0; i < NUM_CE_BITFIELDS; i++)
1429 xx_event_bits[i] = 0;
1432 static void setEventFlagsFromEventBits(struct ElementChangeInfo *change)
1436 /* important: only change event flag if corresponding event bit is set
1437 (this is because all xx_event_bits[] values are loaded separately,
1438 and all xx_event_bits[] values are set back to zero before loading
1439 another value xx_event_bits[x] (each value representing 32 flags)) */
1441 for (i = 0; i < NUM_CHANGE_EVENTS; i++)
1442 if (xx_event_bits[CH_EVENT_BITFIELD_NR(i)] & CH_EVENT_BIT(i))
1443 change->has_event[i] = TRUE;
1446 static void setEventBitsFromEventFlags(struct ElementChangeInfo *change)
1450 /* in contrast to the above function setEventFlagsFromEventBits(), it
1451 would also be possible to set all bits in xx_event_bits[] to 0 or 1
1452 depending on the corresponding change->has_event[i] values here, as
1453 all xx_event_bits[] values are reset in resetEventBits() before */
1455 for (i = 0; i < NUM_CHANGE_EVENTS; i++)
1456 if (change->has_event[i])
1457 xx_event_bits[CH_EVENT_BITFIELD_NR(i)] |= CH_EVENT_BIT(i);
1460 static char *getDefaultElementDescription(struct ElementInfo *ei)
1462 static char description[MAX_ELEMENT_NAME_LEN + 1];
1463 char *default_description = (ei->custom_description != NULL ?
1464 ei->custom_description :
1465 ei->editor_description);
1468 /* always start with reliable default values */
1469 for (i = 0; i < MAX_ELEMENT_NAME_LEN + 1; i++)
1470 description[i] = '\0';
1472 /* truncate element description to MAX_ELEMENT_NAME_LEN bytes */
1473 strncpy(description, default_description, MAX_ELEMENT_NAME_LEN);
1475 return &description[0];
1478 static void setElementDescriptionToDefault(struct ElementInfo *ei)
1480 char *default_description = getDefaultElementDescription(ei);
1483 for (i = 0; i < MAX_ELEMENT_NAME_LEN + 1; i++)
1484 ei->description[i] = default_description[i];
1487 static void setConfigToDefaultsFromConfigList(struct LevelFileConfigInfo *conf)
1491 for (i = 0; conf[i].data_type != -1; i++)
1493 int default_value = conf[i].default_value;
1494 int data_type = conf[i].data_type;
1495 int conf_type = conf[i].conf_type;
1496 int byte_mask = conf_type & CONF_MASK_BYTES;
1498 if (byte_mask == CONF_MASK_MULTI_BYTES)
1500 int default_num_entities = conf[i].default_num_entities;
1501 int max_num_entities = conf[i].max_num_entities;
1503 *(int *)(conf[i].num_entities) = default_num_entities;
1505 if (data_type == TYPE_STRING)
1507 char *default_string = conf[i].default_string;
1508 char *string = (char *)(conf[i].value);
1510 strncpy(string, default_string, max_num_entities);
1512 else if (data_type == TYPE_ELEMENT_LIST)
1514 int *element_array = (int *)(conf[i].value);
1517 for (j = 0; j < max_num_entities; j++)
1518 element_array[j] = default_value;
1520 else if (data_type == TYPE_CONTENT_LIST)
1522 struct Content *content = (struct Content *)(conf[i].value);
1525 for (c = 0; c < max_num_entities; c++)
1526 for (y = 0; y < 3; y++)
1527 for (x = 0; x < 3; x++)
1528 content[c].e[x][y] = default_value;
1531 else /* constant size configuration data (1, 2 or 4 bytes) */
1533 if (data_type == TYPE_BOOLEAN)
1534 *(boolean *)(conf[i].value) = default_value;
1536 *(int *) (conf[i].value) = default_value;
1541 static void copyConfigFromConfigList(struct LevelFileConfigInfo *conf)
1545 for (i = 0; conf[i].data_type != -1; i++)
1547 int data_type = conf[i].data_type;
1548 int conf_type = conf[i].conf_type;
1549 int byte_mask = conf_type & CONF_MASK_BYTES;
1551 if (byte_mask == CONF_MASK_MULTI_BYTES)
1553 int max_num_entities = conf[i].max_num_entities;
1555 if (data_type == TYPE_STRING)
1557 char *string = (char *)(conf[i].value);
1558 char *string_copy = (char *)(conf[i].value_copy);
1560 strncpy(string_copy, string, max_num_entities);
1562 else if (data_type == TYPE_ELEMENT_LIST)
1564 int *element_array = (int *)(conf[i].value);
1565 int *element_array_copy = (int *)(conf[i].value_copy);
1568 for (j = 0; j < max_num_entities; j++)
1569 element_array_copy[j] = element_array[j];
1571 else if (data_type == TYPE_CONTENT_LIST)
1573 struct Content *content = (struct Content *)(conf[i].value);
1574 struct Content *content_copy = (struct Content *)(conf[i].value_copy);
1577 for (c = 0; c < max_num_entities; c++)
1578 for (y = 0; y < 3; y++)
1579 for (x = 0; x < 3; x++)
1580 content_copy[c].e[x][y] = content[c].e[x][y];
1583 else /* constant size configuration data (1, 2 or 4 bytes) */
1585 if (data_type == TYPE_BOOLEAN)
1586 *(boolean *)(conf[i].value_copy) = *(boolean *)(conf[i].value);
1588 *(int *) (conf[i].value_copy) = *(int *) (conf[i].value);
1593 void copyElementInfo(struct ElementInfo *ei_from, struct ElementInfo *ei_to)
1597 xx_ei = *ei_from; /* copy element data into temporary buffer */
1598 yy_ei = *ei_to; /* copy element data into temporary buffer */
1600 copyConfigFromConfigList(chunk_config_CUSX_base);
1605 /* ---------- reinitialize and copy change pages ---------- */
1607 ei_to->num_change_pages = ei_from->num_change_pages;
1608 ei_to->current_change_page = ei_from->current_change_page;
1610 setElementChangePages(ei_to, ei_to->num_change_pages);
1612 for (i = 0; i < ei_to->num_change_pages; i++)
1613 ei_to->change_page[i] = ei_from->change_page[i];
1615 /* ---------- copy group element info ---------- */
1616 if (ei_from->group != NULL && ei_to->group != NULL) /* group or internal */
1617 *ei_to->group = *ei_from->group;
1619 /* mark this custom element as modified */
1620 ei_to->modified_settings = TRUE;
1623 void setElementChangePages(struct ElementInfo *ei, int change_pages)
1625 int change_page_size = sizeof(struct ElementChangeInfo);
1627 ei->num_change_pages = MAX(1, change_pages);
1630 checked_realloc(ei->change_page, ei->num_change_pages * change_page_size);
1632 if (ei->current_change_page >= ei->num_change_pages)
1633 ei->current_change_page = ei->num_change_pages - 1;
1635 ei->change = &ei->change_page[ei->current_change_page];
1638 void setElementChangeInfoToDefaults(struct ElementChangeInfo *change)
1640 xx_change = *change; /* copy change data into temporary buffer */
1642 setConfigToDefaultsFromConfigList(chunk_config_CUSX_change);
1644 *change = xx_change;
1646 resetEventFlags(change);
1648 change->direct_action = 0;
1649 change->other_action = 0;
1651 change->pre_change_function = NULL;
1652 change->change_function = NULL;
1653 change->post_change_function = NULL;
1656 static void setLevelInfoToDefaults_Level(struct LevelInfo *level)
1660 li = *level; /* copy level data into temporary buffer */
1661 setConfigToDefaultsFromConfigList(chunk_config_INFO);
1662 *level = li; /* copy temporary buffer back to level data */
1664 setLevelInfoToDefaults_EM();
1665 setLevelInfoToDefaults_SP();
1666 setLevelInfoToDefaults_MM();
1668 level->native_em_level = &native_em_level;
1669 level->native_sp_level = &native_sp_level;
1670 level->native_mm_level = &native_mm_level;
1672 level->file_version = FILE_VERSION_ACTUAL;
1673 level->game_version = GAME_VERSION_ACTUAL;
1675 level->creation_date = getCurrentDate();
1677 level->encoding_16bit_field = TRUE;
1678 level->encoding_16bit_yamyam = TRUE;
1679 level->encoding_16bit_amoeba = TRUE;
1681 /* clear level name and level author string buffers */
1682 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
1683 level->name[i] = '\0';
1684 for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
1685 level->author[i] = '\0';
1687 /* set level name and level author to default values */
1688 strcpy(level->name, NAMELESS_LEVEL_NAME);
1689 strcpy(level->author, ANONYMOUS_NAME);
1691 /* set level playfield to playable default level with player and exit */
1692 for (x = 0; x < MAX_LEV_FIELDX; x++)
1693 for (y = 0; y < MAX_LEV_FIELDY; y++)
1694 level->field[x][y] = EL_SAND;
1696 level->field[0][0] = EL_PLAYER_1;
1697 level->field[STD_LEV_FIELDX - 1][STD_LEV_FIELDY - 1] = EL_EXIT_CLOSED;
1699 BorderElement = EL_STEELWALL;
1701 /* detect custom elements when loading them */
1702 level->file_has_custom_elements = FALSE;
1704 /* set all bug compatibility flags to "false" => do not emulate this bug */
1705 level->use_action_after_change_bug = FALSE;
1707 if (leveldir_current)
1709 /* try to determine better author name than 'anonymous' */
1710 if (!strEqual(leveldir_current->author, ANONYMOUS_NAME))
1712 strncpy(level->author, leveldir_current->author, MAX_LEVEL_AUTHOR_LEN);
1713 level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
1717 switch (LEVELCLASS(leveldir_current))
1719 case LEVELCLASS_TUTORIAL:
1720 strcpy(level->author, PROGRAM_AUTHOR_STRING);
1723 case LEVELCLASS_CONTRIB:
1724 strncpy(level->author, leveldir_current->name, MAX_LEVEL_AUTHOR_LEN);
1725 level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
1728 case LEVELCLASS_PRIVATE:
1729 strncpy(level->author, getRealName(), MAX_LEVEL_AUTHOR_LEN);
1730 level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
1734 /* keep default value */
1741 static void setLevelInfoToDefaults_Elements(struct LevelInfo *level)
1743 static boolean clipboard_elements_initialized = FALSE;
1746 InitElementPropertiesStatic();
1748 li = *level; /* copy level data into temporary buffer */
1749 setConfigToDefaultsFromConfigList(chunk_config_ELEM);
1750 *level = li; /* copy temporary buffer back to level data */
1752 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1755 struct ElementInfo *ei = &element_info[element];
1757 /* never initialize clipboard elements after the very first time */
1758 /* (to be able to use clipboard elements between several levels) */
1759 if (IS_CLIPBOARD_ELEMENT(element) && clipboard_elements_initialized)
1762 if (IS_ENVELOPE(element))
1764 int envelope_nr = element - EL_ENVELOPE_1;
1766 setConfigToDefaultsFromConfigList(chunk_config_NOTE);
1768 level->envelope[envelope_nr] = xx_envelope;
1771 if (IS_CUSTOM_ELEMENT(element) ||
1772 IS_GROUP_ELEMENT(element) ||
1773 IS_INTERNAL_ELEMENT(element))
1775 xx_ei = *ei; /* copy element data into temporary buffer */
1777 setConfigToDefaultsFromConfigList(chunk_config_CUSX_base);
1782 setElementChangePages(ei, 1);
1783 setElementChangeInfoToDefaults(ei->change);
1785 if (IS_CUSTOM_ELEMENT(element) ||
1786 IS_GROUP_ELEMENT(element) ||
1787 IS_INTERNAL_ELEMENT(element))
1789 setElementDescriptionToDefault(ei);
1791 ei->modified_settings = FALSE;
1794 if (IS_CUSTOM_ELEMENT(element) ||
1795 IS_INTERNAL_ELEMENT(element))
1797 /* internal values used in level editor */
1799 ei->access_type = 0;
1800 ei->access_layer = 0;
1801 ei->access_protected = 0;
1802 ei->walk_to_action = 0;
1803 ei->smash_targets = 0;
1806 ei->can_explode_by_fire = FALSE;
1807 ei->can_explode_smashed = FALSE;
1808 ei->can_explode_impact = FALSE;
1810 ei->current_change_page = 0;
1813 if (IS_GROUP_ELEMENT(element) ||
1814 IS_INTERNAL_ELEMENT(element))
1816 struct ElementGroupInfo *group;
1818 /* initialize memory for list of elements in group */
1819 if (ei->group == NULL)
1820 ei->group = checked_malloc(sizeof(struct ElementGroupInfo));
1824 xx_group = *group; /* copy group data into temporary buffer */
1826 setConfigToDefaultsFromConfigList(chunk_config_GRPX);
1832 clipboard_elements_initialized = TRUE;
1835 static void setLevelInfoToDefaults(struct LevelInfo *level,
1836 boolean level_info_only,
1837 boolean reset_file_status)
1839 setLevelInfoToDefaults_Level(level);
1841 if (!level_info_only)
1842 setLevelInfoToDefaults_Elements(level);
1844 if (reset_file_status)
1846 level->no_valid_file = FALSE;
1847 level->no_level_file = FALSE;
1850 level->changed = FALSE;
1853 static void setFileInfoToDefaults(struct LevelFileInfo *level_file_info)
1855 level_file_info->nr = 0;
1856 level_file_info->type = LEVEL_FILE_TYPE_UNKNOWN;
1857 level_file_info->packed = FALSE;
1859 setString(&level_file_info->basename, NULL);
1860 setString(&level_file_info->filename, NULL);
1863 int getMappedElement_SB(int, boolean);
1865 static void ActivateLevelTemplate()
1869 if (check_special_flags("load_xsb_to_ces"))
1871 /* fill smaller playfields with padding "beyond border wall" elements */
1872 if (level.fieldx < level_template.fieldx ||
1873 level.fieldy < level_template.fieldy)
1875 short field[level.fieldx][level.fieldy];
1876 int new_fieldx = MAX(level.fieldx, level_template.fieldx);
1877 int new_fieldy = MAX(level.fieldy, level_template.fieldy);
1878 int pos_fieldx = (new_fieldx - level.fieldx) / 2;
1879 int pos_fieldy = (new_fieldy - level.fieldy) / 2;
1881 /* copy old playfield (which is smaller than the visible area) */
1882 for (y = 0; y < level.fieldy; y++) for (x = 0; x < level.fieldx; x++)
1883 field[x][y] = level.field[x][y];
1885 /* fill new, larger playfield with "beyond border wall" elements */
1886 for (y = 0; y < new_fieldy; y++) for (x = 0; x < new_fieldx; x++)
1887 level.field[x][y] = getMappedElement_SB('_', TRUE);
1889 /* copy the old playfield to the middle of the new playfield */
1890 for (y = 0; y < level.fieldy; y++) for (x = 0; x < level.fieldx; x++)
1891 level.field[pos_fieldx + x][pos_fieldy + y] = field[x][y];
1893 level.fieldx = new_fieldx;
1894 level.fieldy = new_fieldy;
1898 /* Currently there is no special action needed to activate the template
1899 data, because 'element_info' property settings overwrite the original
1900 level data, while all other variables do not change. */
1902 /* Exception: 'from_level_template' elements in the original level playfield
1903 are overwritten with the corresponding elements at the same position in
1904 playfield from the level template. */
1906 for (x = 0; x < level.fieldx; x++)
1907 for (y = 0; y < level.fieldy; y++)
1908 if (level.field[x][y] == EL_FROM_LEVEL_TEMPLATE)
1909 level.field[x][y] = level_template.field[x][y];
1911 if (check_special_flags("load_xsb_to_ces"))
1913 struct LevelInfo level_backup = level;
1915 /* overwrite all individual level settings from template level settings */
1916 level = level_template;
1918 /* restore playfield size */
1919 level.fieldx = level_backup.fieldx;
1920 level.fieldy = level_backup.fieldy;
1922 /* restore playfield content */
1923 for (x = 0; x < level.fieldx; x++)
1924 for (y = 0; y < level.fieldy; y++)
1925 level.field[x][y] = level_backup.field[x][y];
1927 /* restore name and author from individual level */
1928 strcpy(level.name, level_backup.name);
1929 strcpy(level.author, level_backup.author);
1931 /* restore flag "use_custom_template" */
1932 level.use_custom_template = level_backup.use_custom_template;
1936 static char *getLevelFilenameFromBasename(char *basename)
1938 static char *filename[2] = { NULL, NULL };
1939 int pos = (strEqual(basename, LEVELTEMPLATE_FILENAME) ? 0 : 1);
1941 checked_free(filename[pos]);
1943 filename[pos] = getPath2(getCurrentLevelDir(), basename);
1945 return filename[pos];
1948 static int getFileTypeFromBasename(char *basename)
1950 /* !!! ALSO SEE COMMENT IN checkForPackageFromBasename() !!! */
1952 static char *filename = NULL;
1953 struct stat file_status;
1955 /* ---------- try to determine file type from filename ---------- */
1957 /* check for typical filename of a Supaplex level package file */
1958 if (strlen(basename) == 10 && strPrefixLower(basename, "levels.d"))
1959 return LEVEL_FILE_TYPE_SP;
1961 /* check for typical filename of a Diamond Caves II level package file */
1962 if (strSuffixLower(basename, ".dc") ||
1963 strSuffixLower(basename, ".dc2"))
1964 return LEVEL_FILE_TYPE_DC;
1966 /* check for typical filename of a Sokoban level package file */
1967 if (strSuffixLower(basename, ".xsb") &&
1968 strchr(basename, '%') == NULL)
1969 return LEVEL_FILE_TYPE_SB;
1971 /* ---------- try to determine file type from filesize ---------- */
1973 checked_free(filename);
1974 filename = getPath2(getCurrentLevelDir(), basename);
1976 if (stat(filename, &file_status) == 0)
1978 /* check for typical filesize of a Supaplex level package file */
1979 if (file_status.st_size == 170496)
1980 return LEVEL_FILE_TYPE_SP;
1983 return LEVEL_FILE_TYPE_UNKNOWN;
1986 static int getFileTypeFromMagicBytes(char *filename, int type)
1990 if ((file = openFile(filename, MODE_READ)))
1992 char chunk_name[CHUNK_ID_LEN + 1];
1994 getFileChunkBE(file, chunk_name, NULL);
1996 if (strEqual(chunk_name, "MMII") ||
1997 strEqual(chunk_name, "MIRR"))
1998 type = LEVEL_FILE_TYPE_MM;
2006 static boolean checkForPackageFromBasename(char *basename)
2008 /* !!! WON'T WORK ANYMORE IF getFileTypeFromBasename() ALSO DETECTS !!!
2009 !!! SINGLE LEVELS (CURRENTLY ONLY DETECTS LEVEL PACKAGES !!! */
2011 return (getFileTypeFromBasename(basename) != LEVEL_FILE_TYPE_UNKNOWN);
2014 static char *getSingleLevelBasenameExt(int nr, char *extension)
2016 static char basename[MAX_FILENAME_LEN];
2019 sprintf(basename, "%s", LEVELTEMPLATE_FILENAME);
2021 sprintf(basename, "%03d.%s", nr, extension);
2026 static char *getSingleLevelBasename(int nr)
2028 return getSingleLevelBasenameExt(nr, LEVELFILE_EXTENSION);
2031 static char *getPackedLevelBasename(int type)
2033 static char basename[MAX_FILENAME_LEN];
2034 char *directory = getCurrentLevelDir();
2036 DirectoryEntry *dir_entry;
2038 strcpy(basename, UNDEFINED_FILENAME); /* default: undefined file */
2040 if ((dir = openDirectory(directory)) == NULL)
2042 Error(ERR_WARN, "cannot read current level directory '%s'", directory);
2047 while ((dir_entry = readDirectory(dir)) != NULL) /* loop all entries */
2049 char *entry_basename = dir_entry->basename;
2050 int entry_type = getFileTypeFromBasename(entry_basename);
2052 if (entry_type != LEVEL_FILE_TYPE_UNKNOWN) /* found valid level package */
2054 if (type == LEVEL_FILE_TYPE_UNKNOWN ||
2057 strcpy(basename, entry_basename);
2064 closeDirectory(dir);
2069 static char *getSingleLevelFilename(int nr)
2071 return getLevelFilenameFromBasename(getSingleLevelBasename(nr));
2074 #if ENABLE_UNUSED_CODE
2075 static char *getPackedLevelFilename(int type)
2077 return getLevelFilenameFromBasename(getPackedLevelBasename(type));
2081 char *getDefaultLevelFilename(int nr)
2083 return getSingleLevelFilename(nr);
2086 #if ENABLE_UNUSED_CODE
2087 static void setLevelFileInfo_SingleLevelFilename(struct LevelFileInfo *lfi,
2091 lfi->packed = FALSE;
2093 setString(&lfi->basename, getSingleLevelBasename(lfi->nr, lfi->type));
2094 setString(&lfi->filename, getLevelFilenameFromBasename(lfi->basename));
2098 static void setLevelFileInfo_FormatLevelFilename(struct LevelFileInfo *lfi,
2099 int type, char *format, ...)
2101 static char basename[MAX_FILENAME_LEN];
2104 va_start(ap, format);
2105 vsprintf(basename, format, ap);
2109 lfi->packed = FALSE;
2111 setString(&lfi->basename, basename);
2112 setString(&lfi->filename, getLevelFilenameFromBasename(lfi->basename));
2115 static void setLevelFileInfo_PackedLevelFilename(struct LevelFileInfo *lfi,
2121 setString(&lfi->basename, getPackedLevelBasename(lfi->type));
2122 setString(&lfi->filename, getLevelFilenameFromBasename(lfi->basename));
2125 static int getFiletypeFromID(char *filetype_id)
2127 char *filetype_id_lower;
2128 int filetype = LEVEL_FILE_TYPE_UNKNOWN;
2131 if (filetype_id == NULL)
2132 return LEVEL_FILE_TYPE_UNKNOWN;
2134 filetype_id_lower = getStringToLower(filetype_id);
2136 for (i = 0; filetype_id_list[i].id != NULL; i++)
2138 char *id_lower = getStringToLower(filetype_id_list[i].id);
2140 if (strEqual(filetype_id_lower, id_lower))
2141 filetype = filetype_id_list[i].filetype;
2145 if (filetype != LEVEL_FILE_TYPE_UNKNOWN)
2149 free(filetype_id_lower);
2154 char *getLocalLevelTemplateFilename()
2156 return getDefaultLevelFilename(-1);
2159 char *getGlobalLevelTemplateFilename()
2161 /* global variable "leveldir_current" must be modified in the loop below */
2162 LevelDirTree *leveldir_current_last = leveldir_current;
2163 char *filename = NULL;
2165 /* check for template level in path from current to topmost tree node */
2167 while (leveldir_current != NULL)
2169 filename = getDefaultLevelFilename(-1);
2171 if (fileExists(filename))
2174 leveldir_current = leveldir_current->node_parent;
2177 /* restore global variable "leveldir_current" modified in above loop */
2178 leveldir_current = leveldir_current_last;
2183 static void determineLevelFileInfo_Filename(struct LevelFileInfo *lfi)
2187 /* special case: level number is negative => check for level template file */
2190 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
2191 getSingleLevelBasename(-1));
2193 /* replace local level template filename with global template filename */
2194 setString(&lfi->filename, getGlobalLevelTemplateFilename());
2196 /* no fallback if template file not existing */
2200 /* special case: check for file name/pattern specified in "levelinfo.conf" */
2201 if (leveldir_current->level_filename != NULL)
2203 int filetype = getFiletypeFromID(leveldir_current->level_filetype);
2205 setLevelFileInfo_FormatLevelFilename(lfi, filetype,
2206 leveldir_current->level_filename, nr);
2208 lfi->packed = checkForPackageFromBasename(leveldir_current->level_filename);
2210 if (fileExists(lfi->filename))
2213 else if (leveldir_current->level_filetype != NULL)
2215 int filetype = getFiletypeFromID(leveldir_current->level_filetype);
2217 /* check for specified native level file with standard file name */
2218 setLevelFileInfo_FormatLevelFilename(lfi, filetype,
2219 "%03d.%s", nr, LEVELFILE_EXTENSION);
2220 if (fileExists(lfi->filename))
2224 /* check for native Rocks'n'Diamonds level file */
2225 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
2226 "%03d.%s", nr, LEVELFILE_EXTENSION);
2227 if (fileExists(lfi->filename))
2230 /* check for Emerald Mine level file (V1) */
2231 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "a%c%c",
2232 'a' + (nr / 10) % 26, '0' + nr % 10);
2233 if (fileExists(lfi->filename))
2235 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "A%c%c",
2236 'A' + (nr / 10) % 26, '0' + nr % 10);
2237 if (fileExists(lfi->filename))
2240 /* check for Emerald Mine level file (V2 to V5) */
2241 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%d", nr);
2242 if (fileExists(lfi->filename))
2245 /* check for Emerald Mine level file (V6 / single mode) */
2246 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02ds", nr);
2247 if (fileExists(lfi->filename))
2249 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02dS", nr);
2250 if (fileExists(lfi->filename))
2253 /* check for Emerald Mine level file (V6 / teamwork mode) */
2254 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02dt", nr);
2255 if (fileExists(lfi->filename))
2257 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02dT", nr);
2258 if (fileExists(lfi->filename))
2261 /* check for various packed level file formats */
2262 setLevelFileInfo_PackedLevelFilename(lfi, LEVEL_FILE_TYPE_UNKNOWN);
2263 if (fileExists(lfi->filename))
2266 /* no known level file found -- use default values (and fail later) */
2267 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
2268 "%03d.%s", nr, LEVELFILE_EXTENSION);
2271 static void determineLevelFileInfo_Filetype(struct LevelFileInfo *lfi)
2273 if (lfi->type == LEVEL_FILE_TYPE_UNKNOWN)
2274 lfi->type = getFileTypeFromBasename(lfi->basename);
2276 if (lfi->type == LEVEL_FILE_TYPE_RND)
2277 lfi->type = getFileTypeFromMagicBytes(lfi->filename, lfi->type);
2280 static void setLevelFileInfo(struct LevelFileInfo *level_file_info, int nr)
2282 /* always start with reliable default values */
2283 setFileInfoToDefaults(level_file_info);
2285 level_file_info->nr = nr; /* set requested level number */
2287 determineLevelFileInfo_Filename(level_file_info);
2288 determineLevelFileInfo_Filetype(level_file_info);
2291 /* ------------------------------------------------------------------------- */
2292 /* functions for loading R'n'D level */
2293 /* ------------------------------------------------------------------------- */
2295 int getMappedElement(int element)
2297 /* remap some (historic, now obsolete) elements */
2301 case EL_PLAYER_OBSOLETE:
2302 element = EL_PLAYER_1;
2305 case EL_KEY_OBSOLETE:
2309 case EL_EM_KEY_1_FILE_OBSOLETE:
2310 element = EL_EM_KEY_1;
2313 case EL_EM_KEY_2_FILE_OBSOLETE:
2314 element = EL_EM_KEY_2;
2317 case EL_EM_KEY_3_FILE_OBSOLETE:
2318 element = EL_EM_KEY_3;
2321 case EL_EM_KEY_4_FILE_OBSOLETE:
2322 element = EL_EM_KEY_4;
2325 case EL_ENVELOPE_OBSOLETE:
2326 element = EL_ENVELOPE_1;
2334 if (element >= NUM_FILE_ELEMENTS)
2336 Error(ERR_WARN, "invalid level element %d", element);
2338 element = EL_UNKNOWN;
2346 int getMappedElementByVersion(int element, int game_version)
2348 /* remap some elements due to certain game version */
2350 if (game_version <= VERSION_IDENT(2,2,0,0))
2352 /* map game font elements */
2353 element = (element == EL_CHAR('[') ? EL_CHAR_AUMLAUT :
2354 element == EL_CHAR('\\') ? EL_CHAR_OUMLAUT :
2355 element == EL_CHAR(']') ? EL_CHAR_UUMLAUT :
2356 element == EL_CHAR('^') ? EL_CHAR_COPYRIGHT : element);
2359 if (game_version < VERSION_IDENT(3,0,0,0))
2361 /* map Supaplex gravity tube elements */
2362 element = (element == EL_SP_GRAVITY_PORT_LEFT ? EL_SP_PORT_LEFT :
2363 element == EL_SP_GRAVITY_PORT_RIGHT ? EL_SP_PORT_RIGHT :
2364 element == EL_SP_GRAVITY_PORT_UP ? EL_SP_PORT_UP :
2365 element == EL_SP_GRAVITY_PORT_DOWN ? EL_SP_PORT_DOWN :
2372 static int LoadLevel_VERS(File *file, int chunk_size, struct LevelInfo *level)
2374 level->file_version = getFileVersion(file);
2375 level->game_version = getFileVersion(file);
2380 static int LoadLevel_DATE(File *file, int chunk_size, struct LevelInfo *level)
2382 level->creation_date.year = getFile16BitBE(file);
2383 level->creation_date.month = getFile8Bit(file);
2384 level->creation_date.day = getFile8Bit(file);
2386 level->creation_date.src = DATE_SRC_LEVELFILE;
2391 static int LoadLevel_HEAD(File *file, int chunk_size, struct LevelInfo *level)
2393 int initial_player_stepsize;
2394 int initial_player_gravity;
2397 level->fieldx = getFile8Bit(file);
2398 level->fieldy = getFile8Bit(file);
2400 level->time = getFile16BitBE(file);
2401 level->gems_needed = getFile16BitBE(file);
2403 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
2404 level->name[i] = getFile8Bit(file);
2405 level->name[MAX_LEVEL_NAME_LEN] = 0;
2407 for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
2408 level->score[i] = getFile8Bit(file);
2410 level->num_yamyam_contents = STD_ELEMENT_CONTENTS;
2411 for (i = 0; i < STD_ELEMENT_CONTENTS; i++)
2412 for (y = 0; y < 3; y++)
2413 for (x = 0; x < 3; x++)
2414 level->yamyam_content[i].e[x][y] = getMappedElement(getFile8Bit(file));
2416 level->amoeba_speed = getFile8Bit(file);
2417 level->time_magic_wall = getFile8Bit(file);
2418 level->time_wheel = getFile8Bit(file);
2419 level->amoeba_content = getMappedElement(getFile8Bit(file));
2421 initial_player_stepsize = (getFile8Bit(file) == 1 ? STEPSIZE_FAST :
2424 for (i = 0; i < MAX_PLAYERS; i++)
2425 level->initial_player_stepsize[i] = initial_player_stepsize;
2427 initial_player_gravity = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2429 for (i = 0; i < MAX_PLAYERS; i++)
2430 level->initial_player_gravity[i] = initial_player_gravity;
2432 level->encoding_16bit_field = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2433 level->em_slippery_gems = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2435 level->use_custom_template = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2437 level->block_last_field = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2438 level->sp_block_last_field = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2439 level->can_move_into_acid_bits = getFile32BitBE(file);
2440 level->dont_collide_with_bits = getFile8Bit(file);
2442 level->use_spring_bug = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2443 level->use_step_counter = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2445 level->instant_relocation = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2446 level->can_pass_to_walkable = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2447 level->grow_into_diggable = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2449 level->game_engine_type = getFile8Bit(file);
2451 ReadUnusedBytesFromFile(file, LEVEL_CHUNK_HEAD_UNUSED);
2456 static int LoadLevel_NAME(File *file, int chunk_size, struct LevelInfo *level)
2460 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
2461 level->name[i] = getFile8Bit(file);
2462 level->name[MAX_LEVEL_NAME_LEN] = 0;
2467 static int LoadLevel_AUTH(File *file, int chunk_size, struct LevelInfo *level)
2471 for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
2472 level->author[i] = getFile8Bit(file);
2473 level->author[MAX_LEVEL_AUTHOR_LEN] = 0;
2478 static int LoadLevel_BODY(File *file, int chunk_size, struct LevelInfo *level)
2481 int chunk_size_expected = level->fieldx * level->fieldy;
2483 /* Note: "chunk_size" was wrong before version 2.0 when elements are
2484 stored with 16-bit encoding (and should be twice as big then).
2485 Even worse, playfield data was stored 16-bit when only yamyam content
2486 contained 16-bit elements and vice versa. */
2488 if (level->encoding_16bit_field && level->file_version >= FILE_VERSION_2_0)
2489 chunk_size_expected *= 2;
2491 if (chunk_size_expected != chunk_size)
2493 ReadUnusedBytesFromFile(file, chunk_size);
2494 return chunk_size_expected;
2497 for (y = 0; y < level->fieldy; y++)
2498 for (x = 0; x < level->fieldx; x++)
2499 level->field[x][y] =
2500 getMappedElement(level->encoding_16bit_field ? getFile16BitBE(file) :
2505 static int LoadLevel_CONT(File *file, int chunk_size, struct LevelInfo *level)
2508 int header_size = 4;
2509 int content_size = MAX_ELEMENT_CONTENTS * 3 * 3;
2510 int chunk_size_expected = header_size + content_size;
2512 /* Note: "chunk_size" was wrong before version 2.0 when elements are
2513 stored with 16-bit encoding (and should be twice as big then).
2514 Even worse, playfield data was stored 16-bit when only yamyam content
2515 contained 16-bit elements and vice versa. */
2517 if (level->encoding_16bit_field && level->file_version >= FILE_VERSION_2_0)
2518 chunk_size_expected += content_size;
2520 if (chunk_size_expected != chunk_size)
2522 ReadUnusedBytesFromFile(file, chunk_size);
2523 return chunk_size_expected;
2527 level->num_yamyam_contents = getFile8Bit(file);
2531 /* correct invalid number of content fields -- should never happen */
2532 if (level->num_yamyam_contents < 1 ||
2533 level->num_yamyam_contents > MAX_ELEMENT_CONTENTS)
2534 level->num_yamyam_contents = STD_ELEMENT_CONTENTS;
2536 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
2537 for (y = 0; y < 3; y++)
2538 for (x = 0; x < 3; x++)
2539 level->yamyam_content[i].e[x][y] =
2540 getMappedElement(level->encoding_16bit_field ?
2541 getFile16BitBE(file) : getFile8Bit(file));
2545 static int LoadLevel_CNT2(File *file, int chunk_size, struct LevelInfo *level)
2550 int content_array[MAX_ELEMENT_CONTENTS][3][3];
2552 element = getMappedElement(getFile16BitBE(file));
2553 num_contents = getFile8Bit(file);
2555 getFile8Bit(file); /* content x size (unused) */
2556 getFile8Bit(file); /* content y size (unused) */
2558 ReadUnusedBytesFromFile(file, LEVEL_CHUNK_CNT2_UNUSED);
2560 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
2561 for (y = 0; y < 3; y++)
2562 for (x = 0; x < 3; x++)
2563 content_array[i][x][y] = getMappedElement(getFile16BitBE(file));
2565 /* correct invalid number of content fields -- should never happen */
2566 if (num_contents < 1 || num_contents > MAX_ELEMENT_CONTENTS)
2567 num_contents = STD_ELEMENT_CONTENTS;
2569 if (element == EL_YAMYAM)
2571 level->num_yamyam_contents = num_contents;
2573 for (i = 0; i < num_contents; i++)
2574 for (y = 0; y < 3; y++)
2575 for (x = 0; x < 3; x++)
2576 level->yamyam_content[i].e[x][y] = content_array[i][x][y];
2578 else if (element == EL_BD_AMOEBA)
2580 level->amoeba_content = content_array[0][0][0];
2584 Error(ERR_WARN, "cannot load content for element '%d'", element);
2590 static int LoadLevel_CNT3(File *file, int chunk_size, struct LevelInfo *level)
2596 int chunk_size_expected;
2598 element = getMappedElement(getFile16BitBE(file));
2599 if (!IS_ENVELOPE(element))
2600 element = EL_ENVELOPE_1;
2602 envelope_nr = element - EL_ENVELOPE_1;
2604 envelope_len = getFile16BitBE(file);
2606 level->envelope[envelope_nr].xsize = getFile8Bit(file);
2607 level->envelope[envelope_nr].ysize = getFile8Bit(file);
2609 ReadUnusedBytesFromFile(file, LEVEL_CHUNK_CNT3_UNUSED);
2611 chunk_size_expected = LEVEL_CHUNK_CNT3_SIZE(envelope_len);
2612 if (chunk_size_expected != chunk_size)
2614 ReadUnusedBytesFromFile(file, chunk_size - LEVEL_CHUNK_CNT3_HEADER);
2615 return chunk_size_expected;
2618 for (i = 0; i < envelope_len; i++)
2619 level->envelope[envelope_nr].text[i] = getFile8Bit(file);
2624 static int LoadLevel_CUS1(File *file, int chunk_size, struct LevelInfo *level)
2626 int num_changed_custom_elements = getFile16BitBE(file);
2627 int chunk_size_expected = 2 + num_changed_custom_elements * 6;
2630 if (chunk_size_expected != chunk_size)
2632 ReadUnusedBytesFromFile(file, chunk_size - 2);
2633 return chunk_size_expected;
2636 for (i = 0; i < num_changed_custom_elements; i++)
2638 int element = getMappedElement(getFile16BitBE(file));
2639 int properties = getFile32BitBE(file);
2641 if (IS_CUSTOM_ELEMENT(element))
2642 element_info[element].properties[EP_BITFIELD_BASE_NR] = properties;
2644 Error(ERR_WARN, "invalid custom element number %d", element);
2646 /* older game versions that wrote level files with CUS1 chunks used
2647 different default push delay values (not yet stored in level file) */
2648 element_info[element].push_delay_fixed = 2;
2649 element_info[element].push_delay_random = 8;
2652 level->file_has_custom_elements = TRUE;
2657 static int LoadLevel_CUS2(File *file, int chunk_size, struct LevelInfo *level)
2659 int num_changed_custom_elements = getFile16BitBE(file);
2660 int chunk_size_expected = 2 + num_changed_custom_elements * 4;
2663 if (chunk_size_expected != chunk_size)
2665 ReadUnusedBytesFromFile(file, chunk_size - 2);
2666 return chunk_size_expected;
2669 for (i = 0; i < num_changed_custom_elements; i++)
2671 int element = getMappedElement(getFile16BitBE(file));
2672 int custom_target_element = getMappedElement(getFile16BitBE(file));
2674 if (IS_CUSTOM_ELEMENT(element))
2675 element_info[element].change->target_element = custom_target_element;
2677 Error(ERR_WARN, "invalid custom element number %d", element);
2680 level->file_has_custom_elements = TRUE;
2685 static int LoadLevel_CUS3(File *file, int chunk_size, struct LevelInfo *level)
2687 int num_changed_custom_elements = getFile16BitBE(file);
2688 int chunk_size_expected = LEVEL_CHUNK_CUS3_SIZE(num_changed_custom_elements);
2691 if (chunk_size_expected != chunk_size)
2693 ReadUnusedBytesFromFile(file, chunk_size - 2);
2694 return chunk_size_expected;
2697 for (i = 0; i < num_changed_custom_elements; i++)
2699 int element = getMappedElement(getFile16BitBE(file));
2700 struct ElementInfo *ei = &element_info[element];
2701 unsigned int event_bits;
2703 if (!IS_CUSTOM_ELEMENT(element))
2705 Error(ERR_WARN, "invalid custom element number %d", element);
2707 element = EL_INTERNAL_DUMMY;
2710 for (j = 0; j < MAX_ELEMENT_NAME_LEN; j++)
2711 ei->description[j] = getFile8Bit(file);
2712 ei->description[MAX_ELEMENT_NAME_LEN] = 0;
2714 ei->properties[EP_BITFIELD_BASE_NR] = getFile32BitBE(file);
2716 /* some free bytes for future properties and padding */
2717 ReadUnusedBytesFromFile(file, 7);
2719 ei->use_gfx_element = getFile8Bit(file);
2720 ei->gfx_element_initial = getMappedElement(getFile16BitBE(file));
2722 ei->collect_score_initial = getFile8Bit(file);
2723 ei->collect_count_initial = getFile8Bit(file);
2725 ei->push_delay_fixed = getFile16BitBE(file);
2726 ei->push_delay_random = getFile16BitBE(file);
2727 ei->move_delay_fixed = getFile16BitBE(file);
2728 ei->move_delay_random = getFile16BitBE(file);
2730 ei->move_pattern = getFile16BitBE(file);
2731 ei->move_direction_initial = getFile8Bit(file);
2732 ei->move_stepsize = getFile8Bit(file);
2734 for (y = 0; y < 3; y++)
2735 for (x = 0; x < 3; x++)
2736 ei->content.e[x][y] = getMappedElement(getFile16BitBE(file));
2738 event_bits = getFile32BitBE(file);
2739 for (j = 0; j < NUM_CHANGE_EVENTS; j++)
2740 if (event_bits & (1 << j))
2741 ei->change->has_event[j] = TRUE;
2743 ei->change->target_element = getMappedElement(getFile16BitBE(file));
2745 ei->change->delay_fixed = getFile16BitBE(file);
2746 ei->change->delay_random = getFile16BitBE(file);
2747 ei->change->delay_frames = getFile16BitBE(file);
2749 ei->change->initial_trigger_element= getMappedElement(getFile16BitBE(file));
2751 ei->change->explode = getFile8Bit(file);
2752 ei->change->use_target_content = getFile8Bit(file);
2753 ei->change->only_if_complete = getFile8Bit(file);
2754 ei->change->use_random_replace = getFile8Bit(file);
2756 ei->change->random_percentage = getFile8Bit(file);
2757 ei->change->replace_when = getFile8Bit(file);
2759 for (y = 0; y < 3; y++)
2760 for (x = 0; x < 3; x++)
2761 ei->change->target_content.e[x][y] =
2762 getMappedElement(getFile16BitBE(file));
2764 ei->slippery_type = getFile8Bit(file);
2766 /* some free bytes for future properties and padding */
2767 ReadUnusedBytesFromFile(file, LEVEL_CPART_CUS3_UNUSED);
2769 /* mark that this custom element has been modified */
2770 ei->modified_settings = TRUE;
2773 level->file_has_custom_elements = TRUE;
2778 static int LoadLevel_CUS4(File *file, int chunk_size, struct LevelInfo *level)
2780 struct ElementInfo *ei;
2781 int chunk_size_expected;
2785 /* ---------- custom element base property values (96 bytes) ------------- */
2787 element = getMappedElement(getFile16BitBE(file));
2789 if (!IS_CUSTOM_ELEMENT(element))
2791 Error(ERR_WARN, "invalid custom element number %d", element);
2793 ReadUnusedBytesFromFile(file, chunk_size - 2);
2797 ei = &element_info[element];
2799 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
2800 ei->description[i] = getFile8Bit(file);
2801 ei->description[MAX_ELEMENT_NAME_LEN] = 0;
2803 ei->properties[EP_BITFIELD_BASE_NR] = getFile32BitBE(file);
2805 ReadUnusedBytesFromFile(file, 4); /* reserved for more base properties */
2807 ei->num_change_pages = getFile8Bit(file);
2809 chunk_size_expected = LEVEL_CHUNK_CUS4_SIZE(ei->num_change_pages);
2810 if (chunk_size_expected != chunk_size)
2812 ReadUnusedBytesFromFile(file, chunk_size - 43);
2813 return chunk_size_expected;
2816 ei->ce_value_fixed_initial = getFile16BitBE(file);
2817 ei->ce_value_random_initial = getFile16BitBE(file);
2818 ei->use_last_ce_value = getFile8Bit(file);
2820 ei->use_gfx_element = getFile8Bit(file);
2821 ei->gfx_element_initial = getMappedElement(getFile16BitBE(file));
2823 ei->collect_score_initial = getFile8Bit(file);
2824 ei->collect_count_initial = getFile8Bit(file);
2826 ei->drop_delay_fixed = getFile8Bit(file);
2827 ei->push_delay_fixed = getFile8Bit(file);
2828 ei->drop_delay_random = getFile8Bit(file);
2829 ei->push_delay_random = getFile8Bit(file);
2830 ei->move_delay_fixed = getFile16BitBE(file);
2831 ei->move_delay_random = getFile16BitBE(file);
2833 /* bits 0 - 15 of "move_pattern" ... */
2834 ei->move_pattern = getFile16BitBE(file);
2835 ei->move_direction_initial = getFile8Bit(file);
2836 ei->move_stepsize = getFile8Bit(file);
2838 ei->slippery_type = getFile8Bit(file);
2840 for (y = 0; y < 3; y++)
2841 for (x = 0; x < 3; x++)
2842 ei->content.e[x][y] = getMappedElement(getFile16BitBE(file));
2844 ei->move_enter_element = getMappedElement(getFile16BitBE(file));
2845 ei->move_leave_element = getMappedElement(getFile16BitBE(file));
2846 ei->move_leave_type = getFile8Bit(file);
2848 /* ... bits 16 - 31 of "move_pattern" (not nice, but downward compatible) */
2849 ei->move_pattern |= (getFile16BitBE(file) << 16);
2851 ei->access_direction = getFile8Bit(file);
2853 ei->explosion_delay = getFile8Bit(file);
2854 ei->ignition_delay = getFile8Bit(file);
2855 ei->explosion_type = getFile8Bit(file);
2857 /* some free bytes for future custom property values and padding */
2858 ReadUnusedBytesFromFile(file, 1);
2860 /* ---------- change page property values (48 bytes) --------------------- */
2862 setElementChangePages(ei, ei->num_change_pages);
2864 for (i = 0; i < ei->num_change_pages; i++)
2866 struct ElementChangeInfo *change = &ei->change_page[i];
2867 unsigned int event_bits;
2869 /* always start with reliable default values */
2870 setElementChangeInfoToDefaults(change);
2872 /* bits 0 - 31 of "has_event[]" ... */
2873 event_bits = getFile32BitBE(file);
2874 for (j = 0; j < MIN(NUM_CHANGE_EVENTS, 32); j++)
2875 if (event_bits & (1 << j))
2876 change->has_event[j] = TRUE;
2878 change->target_element = getMappedElement(getFile16BitBE(file));
2880 change->delay_fixed = getFile16BitBE(file);
2881 change->delay_random = getFile16BitBE(file);
2882 change->delay_frames = getFile16BitBE(file);
2884 change->initial_trigger_element = getMappedElement(getFile16BitBE(file));
2886 change->explode = getFile8Bit(file);
2887 change->use_target_content = getFile8Bit(file);
2888 change->only_if_complete = getFile8Bit(file);
2889 change->use_random_replace = getFile8Bit(file);
2891 change->random_percentage = getFile8Bit(file);
2892 change->replace_when = getFile8Bit(file);
2894 for (y = 0; y < 3; y++)
2895 for (x = 0; x < 3; x++)
2896 change->target_content.e[x][y]= getMappedElement(getFile16BitBE(file));
2898 change->can_change = getFile8Bit(file);
2900 change->trigger_side = getFile8Bit(file);
2902 change->trigger_player = getFile8Bit(file);
2903 change->trigger_page = getFile8Bit(file);
2905 change->trigger_page = (change->trigger_page == CH_PAGE_ANY_FILE ?
2906 CH_PAGE_ANY : (1 << change->trigger_page));
2908 change->has_action = getFile8Bit(file);
2909 change->action_type = getFile8Bit(file);
2910 change->action_mode = getFile8Bit(file);
2911 change->action_arg = getFile16BitBE(file);
2913 /* ... bits 32 - 39 of "has_event[]" (not nice, but downward compatible) */
2914 event_bits = getFile8Bit(file);
2915 for (j = 32; j < NUM_CHANGE_EVENTS; j++)
2916 if (event_bits & (1 << (j - 32)))
2917 change->has_event[j] = TRUE;
2920 /* mark this custom element as modified */
2921 ei->modified_settings = TRUE;
2923 level->file_has_custom_elements = TRUE;
2928 static int LoadLevel_GRP1(File *file, int chunk_size, struct LevelInfo *level)
2930 struct ElementInfo *ei;
2931 struct ElementGroupInfo *group;
2935 element = getMappedElement(getFile16BitBE(file));
2937 if (!IS_GROUP_ELEMENT(element))
2939 Error(ERR_WARN, "invalid group element number %d", element);
2941 ReadUnusedBytesFromFile(file, chunk_size - 2);
2945 ei = &element_info[element];
2947 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
2948 ei->description[i] = getFile8Bit(file);
2949 ei->description[MAX_ELEMENT_NAME_LEN] = 0;
2951 group = element_info[element].group;
2953 group->num_elements = getFile8Bit(file);
2955 ei->use_gfx_element = getFile8Bit(file);
2956 ei->gfx_element_initial = getMappedElement(getFile16BitBE(file));
2958 group->choice_mode = getFile8Bit(file);
2960 /* some free bytes for future values and padding */
2961 ReadUnusedBytesFromFile(file, 3);
2963 for (i = 0; i < MAX_ELEMENTS_IN_GROUP; i++)
2964 group->element[i] = getMappedElement(getFile16BitBE(file));
2966 /* mark this group element as modified */
2967 element_info[element].modified_settings = TRUE;
2969 level->file_has_custom_elements = TRUE;
2974 static int LoadLevel_MicroChunk(File *file, struct LevelFileConfigInfo *conf,
2975 int element, int real_element)
2977 int micro_chunk_size = 0;
2978 int conf_type = getFile8Bit(file);
2979 int byte_mask = conf_type & CONF_MASK_BYTES;
2980 boolean element_found = FALSE;
2983 micro_chunk_size += 1;
2985 if (byte_mask == CONF_MASK_MULTI_BYTES)
2987 int num_bytes = getFile16BitBE(file);
2988 byte *buffer = checked_malloc(num_bytes);
2990 ReadBytesFromFile(file, buffer, num_bytes);
2992 for (i = 0; conf[i].data_type != -1; i++)
2994 if (conf[i].element == element &&
2995 conf[i].conf_type == conf_type)
2997 int data_type = conf[i].data_type;
2998 int num_entities = num_bytes / CONF_ENTITY_NUM_BYTES(data_type);
2999 int max_num_entities = conf[i].max_num_entities;
3001 if (num_entities > max_num_entities)
3004 "truncating number of entities for element %d from %d to %d",
3005 element, num_entities, max_num_entities);
3007 num_entities = max_num_entities;
3010 if (num_entities == 0 && (data_type == TYPE_ELEMENT_LIST ||
3011 data_type == TYPE_CONTENT_LIST))
3013 /* for element and content lists, zero entities are not allowed */
3014 Error(ERR_WARN, "found empty list of entities for element %d",
3017 /* do not set "num_entities" here to prevent reading behind buffer */
3019 *(int *)(conf[i].num_entities) = 1; /* at least one is required */
3023 *(int *)(conf[i].num_entities) = num_entities;
3026 element_found = TRUE;
3028 if (data_type == TYPE_STRING)
3030 char *string = (char *)(conf[i].value);
3033 for (j = 0; j < max_num_entities; j++)
3034 string[j] = (j < num_entities ? buffer[j] : '\0');
3036 else if (data_type == TYPE_ELEMENT_LIST)
3038 int *element_array = (int *)(conf[i].value);
3041 for (j = 0; j < num_entities; j++)
3043 getMappedElement(CONF_ELEMENTS_ELEMENT(buffer, j));
3045 else if (data_type == TYPE_CONTENT_LIST)
3047 struct Content *content= (struct Content *)(conf[i].value);
3050 for (c = 0; c < num_entities; c++)
3051 for (y = 0; y < 3; y++)
3052 for (x = 0; x < 3; x++)
3053 content[c].e[x][y] =
3054 getMappedElement(CONF_CONTENTS_ELEMENT(buffer, c, x, y));
3057 element_found = FALSE;
3063 checked_free(buffer);
3065 micro_chunk_size += 2 + num_bytes;
3067 else /* constant size configuration data (1, 2 or 4 bytes) */
3069 int value = (byte_mask == CONF_MASK_1_BYTE ? getFile8Bit (file) :
3070 byte_mask == CONF_MASK_2_BYTE ? getFile16BitBE(file) :
3071 byte_mask == CONF_MASK_4_BYTE ? getFile32BitBE(file) : 0);
3073 for (i = 0; conf[i].data_type != -1; i++)
3075 if (conf[i].element == element &&
3076 conf[i].conf_type == conf_type)
3078 int data_type = conf[i].data_type;
3080 if (data_type == TYPE_ELEMENT)
3081 value = getMappedElement(value);
3083 if (data_type == TYPE_BOOLEAN)
3084 *(boolean *)(conf[i].value) = value;
3086 *(int *) (conf[i].value) = value;
3088 element_found = TRUE;
3094 micro_chunk_size += CONF_VALUE_NUM_BYTES(byte_mask);
3099 char *error_conf_chunk_bytes =
3100 (byte_mask == CONF_MASK_1_BYTE ? "CONF_VALUE_8_BIT" :
3101 byte_mask == CONF_MASK_2_BYTE ? "CONF_VALUE_16_BIT" :
3102 byte_mask == CONF_MASK_4_BYTE ? "CONF_VALUE_32_BIT" :"CONF_VALUE_BYTES");
3103 int error_conf_chunk_token = conf_type & CONF_MASK_TOKEN;
3104 int error_element = real_element;
3106 Error(ERR_WARN, "cannot load micro chunk '%s(%d)' value for element %d ['%s']",
3107 error_conf_chunk_bytes, error_conf_chunk_token,
3108 error_element, EL_NAME(error_element));
3111 return micro_chunk_size;
3114 static int LoadLevel_INFO(File *file, int chunk_size, struct LevelInfo *level)
3116 int real_chunk_size = 0;
3118 li = *level; /* copy level data into temporary buffer */
3120 while (!checkEndOfFile(file))
3122 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_INFO, -1, -1);
3124 if (real_chunk_size >= chunk_size)
3128 *level = li; /* copy temporary buffer back to level data */
3130 return real_chunk_size;
3133 static int LoadLevel_CONF(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 int element = getMappedElement(getFile16BitBE(file));
3143 real_chunk_size += 2;
3144 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_CONF,
3146 if (real_chunk_size >= chunk_size)
3150 *level = li; /* copy temporary buffer back to level data */
3152 return real_chunk_size;
3155 static int LoadLevel_ELEM(File *file, int chunk_size, struct LevelInfo *level)
3157 int real_chunk_size = 0;
3159 li = *level; /* copy level data into temporary buffer */
3161 while (!checkEndOfFile(file))
3163 int element = getMappedElement(getFile16BitBE(file));
3165 real_chunk_size += 2;
3166 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_ELEM,
3168 if (real_chunk_size >= chunk_size)
3172 *level = li; /* copy temporary buffer back to level data */
3174 return real_chunk_size;
3177 static int LoadLevel_NOTE(File *file, int chunk_size, struct LevelInfo *level)
3179 int element = getMappedElement(getFile16BitBE(file));
3180 int envelope_nr = element - EL_ENVELOPE_1;
3181 int real_chunk_size = 2;
3183 xx_envelope = level->envelope[envelope_nr]; /* copy into temporary buffer */
3185 while (!checkEndOfFile(file))
3187 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_NOTE,
3190 if (real_chunk_size >= chunk_size)
3194 level->envelope[envelope_nr] = xx_envelope; /* copy from temporary buffer */
3196 return real_chunk_size;
3199 static int LoadLevel_CUSX(File *file, int chunk_size, struct LevelInfo *level)
3201 int element = getMappedElement(getFile16BitBE(file));
3202 int real_chunk_size = 2;
3203 struct ElementInfo *ei = &element_info[element];
3206 xx_ei = *ei; /* copy element data into temporary buffer */
3208 xx_ei.num_change_pages = -1;
3210 while (!checkEndOfFile(file))
3212 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_CUSX_base,
3214 if (xx_ei.num_change_pages != -1)
3217 if (real_chunk_size >= chunk_size)
3223 if (ei->num_change_pages == -1)
3225 Error(ERR_WARN, "LoadLevel_CUSX(): missing 'num_change_pages' for '%s'",
3228 ei->num_change_pages = 1;
3230 setElementChangePages(ei, 1);
3231 setElementChangeInfoToDefaults(ei->change);
3233 return real_chunk_size;
3236 /* initialize number of change pages stored for this custom element */
3237 setElementChangePages(ei, ei->num_change_pages);
3238 for (i = 0; i < ei->num_change_pages; i++)
3239 setElementChangeInfoToDefaults(&ei->change_page[i]);
3241 /* start with reading properties for the first change page */
3242 xx_current_change_page = 0;
3244 while (!checkEndOfFile(file))
3246 struct ElementChangeInfo *change = &ei->change_page[xx_current_change_page];
3248 xx_change = *change; /* copy change data into temporary buffer */
3250 resetEventBits(); /* reset bits; change page might have changed */
3252 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_CUSX_change,
3255 *change = xx_change;
3257 setEventFlagsFromEventBits(change);
3259 if (real_chunk_size >= chunk_size)
3263 level->file_has_custom_elements = TRUE;
3265 return real_chunk_size;
3268 static int LoadLevel_GRPX(File *file, int chunk_size, struct LevelInfo *level)
3270 int element = getMappedElement(getFile16BitBE(file));
3271 int real_chunk_size = 2;
3272 struct ElementInfo *ei = &element_info[element];
3273 struct ElementGroupInfo *group = ei->group;
3275 xx_ei = *ei; /* copy element data into temporary buffer */
3276 xx_group = *group; /* copy group data into temporary buffer */
3278 while (!checkEndOfFile(file))
3280 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_GRPX,
3283 if (real_chunk_size >= chunk_size)
3290 level->file_has_custom_elements = TRUE;
3292 return real_chunk_size;
3295 static void LoadLevelFromFileInfo_RND(struct LevelInfo *level,
3296 struct LevelFileInfo *level_file_info,
3297 boolean level_info_only)
3299 char *filename = level_file_info->filename;
3300 char cookie[MAX_LINE_LEN];
3301 char chunk_name[CHUNK_ID_LEN + 1];
3305 if (!(file = openFile(filename, MODE_READ)))
3307 level->no_valid_file = TRUE;
3308 level->no_level_file = TRUE;
3310 if (level_info_only)
3313 Error(ERR_WARN, "cannot read level '%s' -- using empty level", filename);
3315 if (!setup.editor.use_template_for_new_levels)
3318 /* if level file not found, try to initialize level data from template */
3319 filename = getGlobalLevelTemplateFilename();
3321 if (!(file = openFile(filename, MODE_READ)))
3324 /* default: for empty levels, use level template for custom elements */
3325 level->use_custom_template = TRUE;
3327 level->no_valid_file = FALSE;
3330 getFileChunkBE(file, chunk_name, NULL);
3331 if (strEqual(chunk_name, "RND1"))
3333 getFile32BitBE(file); /* not used */
3335 getFileChunkBE(file, chunk_name, NULL);
3336 if (!strEqual(chunk_name, "CAVE"))
3338 level->no_valid_file = TRUE;
3340 Error(ERR_WARN, "unknown format of level file '%s'", filename);
3347 else /* check for pre-2.0 file format with cookie string */
3349 strcpy(cookie, chunk_name);
3350 if (getStringFromFile(file, &cookie[4], MAX_LINE_LEN - 4) == NULL)
3352 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
3353 cookie[strlen(cookie) - 1] = '\0';
3355 if (!checkCookieString(cookie, LEVEL_COOKIE_TMPL))
3357 level->no_valid_file = TRUE;
3359 Error(ERR_WARN, "unknown format of level file '%s'", filename);
3366 if ((level->file_version = getFileVersionFromCookieString(cookie)) == -1)
3368 level->no_valid_file = TRUE;
3370 Error(ERR_WARN, "unsupported version of level file '%s'", filename);
3377 /* pre-2.0 level files have no game version, so use file version here */
3378 level->game_version = level->file_version;
3381 if (level->file_version < FILE_VERSION_1_2)
3383 /* level files from versions before 1.2.0 without chunk structure */
3384 LoadLevel_HEAD(file, LEVEL_CHUNK_HEAD_SIZE, level);
3385 LoadLevel_BODY(file, level->fieldx * level->fieldy, level);
3393 int (*loader)(File *, int, struct LevelInfo *);
3397 { "VERS", LEVEL_CHUNK_VERS_SIZE, LoadLevel_VERS },
3398 { "DATE", LEVEL_CHUNK_DATE_SIZE, LoadLevel_DATE },
3399 { "HEAD", LEVEL_CHUNK_HEAD_SIZE, LoadLevel_HEAD },
3400 { "NAME", LEVEL_CHUNK_NAME_SIZE, LoadLevel_NAME },
3401 { "AUTH", LEVEL_CHUNK_AUTH_SIZE, LoadLevel_AUTH },
3402 { "INFO", -1, LoadLevel_INFO },
3403 { "BODY", -1, LoadLevel_BODY },
3404 { "CONT", -1, LoadLevel_CONT },
3405 { "CNT2", LEVEL_CHUNK_CNT2_SIZE, LoadLevel_CNT2 },
3406 { "CNT3", -1, LoadLevel_CNT3 },
3407 { "CUS1", -1, LoadLevel_CUS1 },
3408 { "CUS2", -1, LoadLevel_CUS2 },
3409 { "CUS3", -1, LoadLevel_CUS3 },
3410 { "CUS4", -1, LoadLevel_CUS4 },
3411 { "GRP1", -1, LoadLevel_GRP1 },
3412 { "CONF", -1, LoadLevel_CONF },
3413 { "ELEM", -1, LoadLevel_ELEM },
3414 { "NOTE", -1, LoadLevel_NOTE },
3415 { "CUSX", -1, LoadLevel_CUSX },
3416 { "GRPX", -1, LoadLevel_GRPX },
3421 while (getFileChunkBE(file, chunk_name, &chunk_size))
3425 while (chunk_info[i].name != NULL &&
3426 !strEqual(chunk_name, chunk_info[i].name))
3429 if (chunk_info[i].name == NULL)
3431 Error(ERR_WARN, "unknown chunk '%s' in level file '%s'",
3432 chunk_name, filename);
3433 ReadUnusedBytesFromFile(file, chunk_size);
3435 else if (chunk_info[i].size != -1 &&
3436 chunk_info[i].size != chunk_size)
3438 Error(ERR_WARN, "wrong size (%d) of chunk '%s' in level file '%s'",
3439 chunk_size, chunk_name, filename);
3440 ReadUnusedBytesFromFile(file, chunk_size);
3444 /* call function to load this level chunk */
3445 int chunk_size_expected =
3446 (chunk_info[i].loader)(file, chunk_size, level);
3448 /* the size of some chunks cannot be checked before reading other
3449 chunks first (like "HEAD" and "BODY") that contain some header
3450 information, so check them here */
3451 if (chunk_size_expected != chunk_size)
3453 Error(ERR_WARN, "wrong size (%d) of chunk '%s' in level file '%s'",
3454 chunk_size, chunk_name, filename);
3464 /* ------------------------------------------------------------------------- */
3465 /* functions for loading EM level */
3466 /* ------------------------------------------------------------------------- */
3468 void CopyNativeLevel_RND_to_EM(struct LevelInfo *level)
3470 static int ball_xy[8][2] =
3481 struct LevelInfo_EM *level_em = level->native_em_level;
3482 struct LEVEL *lev = level_em->lev;
3483 struct PLAYER **ply = level_em->ply;
3486 lev->width = MIN(level->fieldx, EM_MAX_CAVE_WIDTH);
3487 lev->height = MIN(level->fieldy, EM_MAX_CAVE_HEIGHT);
3489 lev->time_seconds = level->time;
3490 lev->required_initial = level->gems_needed;
3492 lev->emerald_score = level->score[SC_EMERALD];
3493 lev->diamond_score = level->score[SC_DIAMOND];
3494 lev->alien_score = level->score[SC_ROBOT];
3495 lev->tank_score = level->score[SC_SPACESHIP];
3496 lev->bug_score = level->score[SC_BUG];
3497 lev->eater_score = level->score[SC_YAMYAM];
3498 lev->nut_score = level->score[SC_NUT];
3499 lev->dynamite_score = level->score[SC_DYNAMITE];
3500 lev->key_score = level->score[SC_KEY];
3501 lev->exit_score = level->score[SC_TIME_BONUS];
3503 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
3504 for (y = 0; y < 3; y++)
3505 for (x = 0; x < 3; x++)
3506 lev->eater_array[i][y * 3 + x] =
3507 map_element_RND_to_EM(level->yamyam_content[i].e[x][y]);
3509 lev->amoeba_time = level->amoeba_speed;
3510 lev->wonderwall_time_initial = level->time_magic_wall;
3511 lev->wheel_time = level->time_wheel;
3513 lev->android_move_time = level->android_move_time;
3514 lev->android_clone_time = level->android_clone_time;
3515 lev->ball_random = level->ball_random;
3516 lev->ball_state_initial = level->ball_state_initial;
3517 lev->ball_time = level->ball_time;
3518 lev->num_ball_arrays = level->num_ball_contents;
3520 lev->lenses_score = level->lenses_score;
3521 lev->magnify_score = level->magnify_score;
3522 lev->slurp_score = level->slurp_score;
3524 lev->lenses_time = level->lenses_time;
3525 lev->magnify_time = level->magnify_time;
3527 lev->wind_direction_initial =
3528 map_direction_RND_to_EM(level->wind_direction_initial);
3529 lev->wind_cnt_initial = (level->wind_direction_initial != MV_NONE ?
3530 lev->wind_time : 0);
3532 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
3533 for (j = 0; j < 8; j++)
3534 lev->ball_array[i][j] =
3535 map_element_RND_to_EM(level->
3536 ball_content[i].e[ball_xy[j][0]][ball_xy[j][1]]);
3538 map_android_clone_elements_RND_to_EM(level);
3540 /* first fill the complete playfield with the default border element */
3541 for (y = 0; y < EM_MAX_CAVE_HEIGHT; y++)
3542 for (x = 0; x < EM_MAX_CAVE_WIDTH; x++)
3543 level_em->cave[x][y] = ZBORDER;
3545 if (BorderElement == EL_STEELWALL)
3547 for (y = 0; y < lev->height + 2; y++)
3548 for (x = 0; x < lev->width + 2; x++)
3549 level_em->cave[x + 1][y + 1] = map_element_RND_to_EM(EL_STEELWALL);
3552 /* then copy the real level contents from level file into the playfield */
3553 for (y = 0; y < lev->height; y++) for (x = 0; x < lev->width; x++)
3555 int new_element = map_element_RND_to_EM(level->field[x][y]);
3556 int offset = (BorderElement == EL_STEELWALL ? 1 : 0);
3557 int xx = x + 1 + offset;
3558 int yy = y + 1 + offset;
3560 if (level->field[x][y] == EL_AMOEBA_DEAD)
3561 new_element = map_element_RND_to_EM(EL_AMOEBA_WET);
3563 level_em->cave[xx][yy] = new_element;
3566 for (i = 0; i < MAX_PLAYERS; i++)
3568 ply[i]->x_initial = 0;
3569 ply[i]->y_initial = 0;
3572 /* initialize player positions and delete players from the playfield */
3573 for (y = 0; y < lev->height; y++) for (x = 0; x < lev->width; x++)
3575 if (ELEM_IS_PLAYER(level->field[x][y]))
3577 int player_nr = GET_PLAYER_NR(level->field[x][y]);
3578 int offset = (BorderElement == EL_STEELWALL ? 1 : 0);
3579 int xx = x + 1 + offset;
3580 int yy = y + 1 + offset;
3582 ply[player_nr]->x_initial = xx;
3583 ply[player_nr]->y_initial = yy;
3585 level_em->cave[xx][yy] = map_element_RND_to_EM(EL_EMPTY);
3589 if (BorderElement == EL_STEELWALL)
3596 void CopyNativeLevel_EM_to_RND(struct LevelInfo *level)
3598 static int ball_xy[8][2] =
3609 struct LevelInfo_EM *level_em = level->native_em_level;
3610 struct LEVEL *lev = level_em->lev;
3611 struct PLAYER **ply = level_em->ply;
3614 level->fieldx = MIN(lev->width, MAX_LEV_FIELDX);
3615 level->fieldy = MIN(lev->height, MAX_LEV_FIELDY);
3617 level->time = lev->time_seconds;
3618 level->gems_needed = lev->required_initial;
3620 sprintf(level->name, "Level %d", level->file_info.nr);
3622 level->score[SC_EMERALD] = lev->emerald_score;
3623 level->score[SC_DIAMOND] = lev->diamond_score;
3624 level->score[SC_ROBOT] = lev->alien_score;
3625 level->score[SC_SPACESHIP] = lev->tank_score;
3626 level->score[SC_BUG] = lev->bug_score;
3627 level->score[SC_YAMYAM] = lev->eater_score;
3628 level->score[SC_NUT] = lev->nut_score;
3629 level->score[SC_DYNAMITE] = lev->dynamite_score;
3630 level->score[SC_KEY] = lev->key_score;
3631 level->score[SC_TIME_BONUS] = lev->exit_score;
3633 level->num_yamyam_contents = MAX_ELEMENT_CONTENTS;
3635 for (i = 0; i < level->num_yamyam_contents; i++)
3636 for (y = 0; y < 3; y++)
3637 for (x = 0; x < 3; x++)
3638 level->yamyam_content[i].e[x][y] =
3639 map_element_EM_to_RND(lev->eater_array[i][y * 3 + x]);
3641 level->amoeba_speed = lev->amoeba_time;
3642 level->time_magic_wall = lev->wonderwall_time_initial;
3643 level->time_wheel = lev->wheel_time;
3645 level->android_move_time = lev->android_move_time;
3646 level->android_clone_time = lev->android_clone_time;
3647 level->ball_random = lev->ball_random;
3648 level->ball_state_initial = lev->ball_state_initial;
3649 level->ball_time = lev->ball_time;
3650 level->num_ball_contents = lev->num_ball_arrays;
3652 level->lenses_score = lev->lenses_score;
3653 level->magnify_score = lev->magnify_score;
3654 level->slurp_score = lev->slurp_score;
3656 level->lenses_time = lev->lenses_time;
3657 level->magnify_time = lev->magnify_time;
3659 level->wind_direction_initial =
3660 map_direction_EM_to_RND(lev->wind_direction_initial);
3662 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
3663 for (j = 0; j < 8; j++)
3664 level->ball_content[i].e[ball_xy[j][0]][ball_xy[j][1]] =
3665 map_element_EM_to_RND(lev->ball_array[i][j]);
3667 map_android_clone_elements_EM_to_RND(level);
3669 /* convert the playfield (some elements need special treatment) */
3670 for (y = 0; y < level->fieldy; y++) for (x = 0; x < level->fieldx; x++)
3672 int new_element = map_element_EM_to_RND(level_em->cave[x + 1][y + 1]);
3674 if (new_element == EL_AMOEBA_WET && level->amoeba_speed == 0)
3675 new_element = EL_AMOEBA_DEAD;
3677 level->field[x][y] = new_element;
3680 for (i = 0; i < MAX_PLAYERS; i++)
3682 /* in case of all players set to the same field, use the first player */
3683 int nr = MAX_PLAYERS - i - 1;
3684 int jx = ply[nr]->x_initial - 1;
3685 int jy = ply[nr]->y_initial - 1;
3687 if (jx != -1 && jy != -1)
3688 level->field[jx][jy] = EL_PLAYER_1 + nr;
3693 /* ------------------------------------------------------------------------- */
3694 /* functions for loading SP level */
3695 /* ------------------------------------------------------------------------- */
3697 void CopyNativeLevel_RND_to_SP(struct LevelInfo *level)
3699 struct LevelInfo_SP *level_sp = level->native_sp_level;
3700 LevelInfoType *header = &level_sp->header;
3703 level_sp->width = level->fieldx;
3704 level_sp->height = level->fieldy;
3706 for (x = 0; x < level->fieldx; x++)
3707 for (y = 0; y < level->fieldy; y++)
3708 level_sp->playfield[x][y] = map_element_RND_to_SP(level->field[x][y]);
3710 header->InitialGravity = (level->initial_player_gravity[0] ? 1 : 0);
3712 for (i = 0; i < SP_LEVEL_NAME_LEN; i++)
3713 header->LevelTitle[i] = level->name[i];
3714 /* !!! NO STRING TERMINATION IN SUPAPLEX VB CODE YET -- FIX THIS !!! */
3716 header->InfotronsNeeded = level->gems_needed;
3718 header->SpecialPortCount = 0;
3720 for (x = 0; x < level->fieldx; x++) for (y = 0; y < level->fieldy; y++)
3722 boolean gravity_port_found = FALSE;
3723 boolean gravity_port_valid = FALSE;
3724 int gravity_port_flag;
3725 int gravity_port_base_element;
3726 int element = level->field[x][y];
3728 if (element >= EL_SP_GRAVITY_ON_PORT_RIGHT &&
3729 element <= EL_SP_GRAVITY_ON_PORT_UP)
3731 gravity_port_found = TRUE;
3732 gravity_port_valid = TRUE;
3733 gravity_port_flag = 1;
3734 gravity_port_base_element = EL_SP_GRAVITY_ON_PORT_RIGHT;
3736 else if (element >= EL_SP_GRAVITY_OFF_PORT_RIGHT &&
3737 element <= EL_SP_GRAVITY_OFF_PORT_UP)
3739 gravity_port_found = TRUE;
3740 gravity_port_valid = TRUE;
3741 gravity_port_flag = 0;
3742 gravity_port_base_element = EL_SP_GRAVITY_OFF_PORT_RIGHT;
3744 else if (element >= EL_SP_GRAVITY_PORT_RIGHT &&
3745 element <= EL_SP_GRAVITY_PORT_UP)
3747 /* change R'n'D style gravity inverting special port to normal port
3748 (there are no gravity inverting ports in native Supaplex engine) */
3750 gravity_port_found = TRUE;
3751 gravity_port_valid = FALSE;
3752 gravity_port_base_element = EL_SP_GRAVITY_PORT_RIGHT;
3755 if (gravity_port_found)
3757 if (gravity_port_valid &&
3758 header->SpecialPortCount < SP_MAX_SPECIAL_PORTS)
3760 SpecialPortType *port = &header->SpecialPort[header->SpecialPortCount];
3762 port->PortLocation = (y * level->fieldx + x) * 2;
3763 port->Gravity = gravity_port_flag;
3765 element += EL_SP_GRAVITY_PORT_RIGHT - gravity_port_base_element;
3767 header->SpecialPortCount++;
3771 /* change special gravity port to normal port */
3773 element += EL_SP_PORT_RIGHT - gravity_port_base_element;
3776 level_sp->playfield[x][y] = element - EL_SP_START;
3781 void CopyNativeLevel_SP_to_RND(struct LevelInfo *level)
3783 struct LevelInfo_SP *level_sp = level->native_sp_level;
3784 LevelInfoType *header = &level_sp->header;
3785 boolean num_invalid_elements = 0;
3788 level->fieldx = level_sp->width;
3789 level->fieldy = level_sp->height;
3791 for (x = 0; x < level->fieldx; x++)
3793 for (y = 0; y < level->fieldy; y++)
3795 int element_old = level_sp->playfield[x][y];
3796 int element_new = getMappedElement(map_element_SP_to_RND(element_old));
3798 if (element_new == EL_UNKNOWN)
3800 num_invalid_elements++;
3802 Error(ERR_DEBUG, "invalid element %d at position %d, %d",
3806 level->field[x][y] = element_new;
3810 if (num_invalid_elements > 0)
3811 Error(ERR_WARN, "found %d invalid elements%s", num_invalid_elements,
3812 (!options.debug ? " (use '--debug' for more details)" : ""));
3814 for (i = 0; i < MAX_PLAYERS; i++)
3815 level->initial_player_gravity[i] =
3816 (header->InitialGravity == 1 ? TRUE : FALSE);
3818 /* skip leading spaces */
3819 for (i = 0; i < SP_LEVEL_NAME_LEN; i++)
3820 if (header->LevelTitle[i] != ' ')
3823 /* copy level title */
3824 for (j = 0; i < SP_LEVEL_NAME_LEN; i++, j++)
3825 level->name[j] = header->LevelTitle[i];
3826 level->name[j] = '\0';
3828 /* cut trailing spaces */
3830 if (level->name[j - 1] == ' ' && level->name[j] == '\0')
3831 level->name[j - 1] = '\0';
3833 level->gems_needed = header->InfotronsNeeded;
3835 for (i = 0; i < header->SpecialPortCount; i++)
3837 SpecialPortType *port = &header->SpecialPort[i];
3838 int port_location = port->PortLocation;
3839 int gravity = port->Gravity;
3840 int port_x, port_y, port_element;
3842 port_x = (port_location / 2) % level->fieldx;
3843 port_y = (port_location / 2) / level->fieldx;
3845 if (port_x < 0 || port_x >= level->fieldx ||
3846 port_y < 0 || port_y >= level->fieldy)
3848 Error(ERR_WARN, "special port position (%d, %d) out of bounds",
3854 port_element = level->field[port_x][port_y];
3856 if (port_element < EL_SP_GRAVITY_PORT_RIGHT ||
3857 port_element > EL_SP_GRAVITY_PORT_UP)
3859 Error(ERR_WARN, "no special port at position (%d, %d)", port_x, port_y);
3864 /* change previous (wrong) gravity inverting special port to either
3865 gravity enabling special port or gravity disabling special port */
3866 level->field[port_x][port_y] +=
3867 (gravity == 1 ? EL_SP_GRAVITY_ON_PORT_RIGHT :
3868 EL_SP_GRAVITY_OFF_PORT_RIGHT) - EL_SP_GRAVITY_PORT_RIGHT;
3871 /* change special gravity ports without database entries to normal ports */
3872 for (x = 0; x < level->fieldx; x++)
3873 for (y = 0; y < level->fieldy; y++)
3874 if (level->field[x][y] >= EL_SP_GRAVITY_PORT_RIGHT &&
3875 level->field[x][y] <= EL_SP_GRAVITY_PORT_UP)
3876 level->field[x][y] += EL_SP_PORT_RIGHT - EL_SP_GRAVITY_PORT_RIGHT;
3878 level->time = 0; /* no time limit */
3879 level->amoeba_speed = 0;
3880 level->time_magic_wall = 0;
3881 level->time_wheel = 0;
3882 level->amoeba_content = EL_EMPTY;
3885 /* original Supaplex does not use score values -- use default values */
3887 for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
3888 level->score[i] = 0;
3891 /* there are no yamyams in supaplex levels */
3892 for (i = 0; i < level->num_yamyam_contents; i++)
3893 for (x = 0; x < 3; x++)
3894 for (y = 0; y < 3; y++)
3895 level->yamyam_content[i].e[x][y] = EL_EMPTY;
3898 static void CopyNativeTape_RND_to_SP(struct LevelInfo *level)
3900 struct LevelInfo_SP *level_sp = level->native_sp_level;
3901 struct DemoInfo_SP *demo = &level_sp->demo;
3904 /* always start with reliable default values */
3905 demo->is_available = FALSE;
3908 if (TAPE_IS_EMPTY(tape))
3911 demo->level_nr = tape.level_nr; /* (currently not used) */
3913 level_sp->header.DemoRandomSeed = tape.random_seed;
3917 for (i = 0; i < tape.length; i++)
3919 int demo_action = map_key_RND_to_SP(tape.pos[i].action[0]);
3920 int demo_repeat = tape.pos[i].delay;
3921 int demo_entries = (demo_repeat + 15) / 16;
3923 if (demo->length + demo_entries >= SP_MAX_TAPE_LEN)
3925 Error(ERR_WARN, "tape truncated: size exceeds maximum SP demo size %d",
3931 for (j = 0; j < demo_repeat / 16; j++)
3932 demo->data[demo->length++] = 0xf0 | demo_action;
3934 if (demo_repeat % 16)
3935 demo->data[demo->length++] = ((demo_repeat % 16 - 1) << 4) | demo_action;
3938 demo->is_available = TRUE;
3941 static void setTapeInfoToDefaults();
3943 static void CopyNativeTape_SP_to_RND(struct LevelInfo *level)
3945 struct LevelInfo_SP *level_sp = level->native_sp_level;
3946 struct DemoInfo_SP *demo = &level_sp->demo;
3947 char *filename = level->file_info.filename;
3950 /* always start with reliable default values */
3951 setTapeInfoToDefaults();
3953 if (!demo->is_available)
3956 tape.level_nr = demo->level_nr; /* (currently not used) */
3957 tape.random_seed = level_sp->header.DemoRandomSeed;
3959 TapeSetDateFromEpochSeconds(getFileTimestampEpochSeconds(filename));
3962 tape.pos[tape.counter].delay = 0;
3964 for (i = 0; i < demo->length; i++)
3966 int demo_action = demo->data[i] & 0x0f;
3967 int demo_repeat = (demo->data[i] & 0xf0) >> 4;
3968 int tape_action = map_key_SP_to_RND(demo_action);
3969 int tape_repeat = demo_repeat + 1;
3970 byte action[MAX_PLAYERS] = { tape_action, 0, 0, 0 };
3971 boolean success = 0;
3974 for (j = 0; j < tape_repeat; j++)
3975 success = TapeAddAction(action);
3979 Error(ERR_WARN, "SP demo truncated: size exceeds maximum tape size %d",
3986 TapeHaltRecording();
3990 /* ------------------------------------------------------------------------- */
3991 /* functions for loading MM level */
3992 /* ------------------------------------------------------------------------- */
3994 void CopyNativeLevel_RND_to_MM(struct LevelInfo *level)
3996 struct LevelInfo_MM *level_mm = level->native_mm_level;
3999 level_mm->fieldx = MIN(level->fieldx, MM_MAX_PLAYFIELD_WIDTH);
4000 level_mm->fieldy = MIN(level->fieldy, MM_MAX_PLAYFIELD_HEIGHT);
4002 level_mm->time = level->time;
4003 level_mm->kettles_needed = level->gems_needed;
4004 level_mm->auto_count_kettles = level->auto_count_gems;
4006 level_mm->laser_red = level->mm_laser_red;
4007 level_mm->laser_green = level->mm_laser_green;
4008 level_mm->laser_blue = level->mm_laser_blue;
4010 strcpy(level_mm->name, level->name);
4011 strcpy(level_mm->author, level->author);
4013 level_mm->score[SC_EMERALD] = level->score[SC_EMERALD];
4014 level_mm->score[SC_PACMAN] = level->score[SC_PACMAN];
4015 level_mm->score[SC_KEY] = level->score[SC_KEY];
4016 level_mm->score[SC_TIME_BONUS] = level->score[SC_TIME_BONUS];
4017 level_mm->score[SC_ELEM_BONUS] = level->score[SC_ELEM_BONUS];
4019 level_mm->amoeba_speed = level->amoeba_speed;
4020 level_mm->time_fuse = level->mm_time_fuse;
4021 level_mm->time_bomb = level->mm_time_bomb;
4022 level_mm->time_ball = level->mm_time_ball;
4023 level_mm->time_block = level->mm_time_block;
4025 for (x = 0; x < level->fieldx; x++)
4026 for (y = 0; y < level->fieldy; y++)
4028 level_mm->field[x][y] = map_element_RND_to_MM(level->field[x][y]);
4031 void CopyNativeLevel_MM_to_RND(struct LevelInfo *level)
4033 struct LevelInfo_MM *level_mm = level->native_mm_level;
4036 level->fieldx = MIN(level_mm->fieldx, MAX_LEV_FIELDX);
4037 level->fieldy = MIN(level_mm->fieldy, MAX_LEV_FIELDY);
4039 level->time = level_mm->time;
4040 level->gems_needed = level_mm->kettles_needed;
4041 level->auto_count_gems = level_mm->auto_count_kettles;
4043 level->mm_laser_red = level_mm->laser_red;
4044 level->mm_laser_green = level_mm->laser_green;
4045 level->mm_laser_blue = level_mm->laser_blue;
4047 strcpy(level->name, level_mm->name);
4049 /* only overwrite author from 'levelinfo.conf' if author defined in level */
4050 if (!strEqual(level_mm->author, ANONYMOUS_NAME))
4051 strcpy(level->author, level_mm->author);
4053 level->score[SC_EMERALD] = level_mm->score[SC_EMERALD];
4054 level->score[SC_PACMAN] = level_mm->score[SC_PACMAN];
4055 level->score[SC_KEY] = level_mm->score[SC_KEY];
4056 level->score[SC_TIME_BONUS] = level_mm->score[SC_TIME_BONUS];
4057 level->score[SC_ELEM_BONUS] = level_mm->score[SC_ELEM_BONUS];
4059 level->amoeba_speed = level_mm->amoeba_speed;
4060 level->mm_time_fuse = level_mm->time_fuse;
4061 level->mm_time_bomb = level_mm->time_bomb;
4062 level->mm_time_ball = level_mm->time_ball;
4063 level->mm_time_block = level_mm->time_block;
4065 for (x = 0; x < level->fieldx; x++)
4066 for (y = 0; y < level->fieldy; y++)
4067 level->field[x][y] = map_element_MM_to_RND(level_mm->field[x][y]);
4071 /* ------------------------------------------------------------------------- */
4072 /* functions for loading DC level */
4073 /* ------------------------------------------------------------------------- */
4075 #define DC_LEVEL_HEADER_SIZE 344
4077 unsigned short getDecodedWord_DC(unsigned short data_encoded, boolean init)
4079 static int last_data_encoded;
4083 int diff_hi, diff_lo;
4084 int data_hi, data_lo;
4085 unsigned short data_decoded;
4089 last_data_encoded = 0;
4096 diff = data_encoded - last_data_encoded;
4097 diff_hi = diff & ~0xff;
4098 diff_lo = diff & 0xff;
4102 data_hi = diff_hi - (offset1 << 8) + (offset2 & 0xff00);
4103 data_lo = (diff_lo + (data_hi >> 16)) & 0x00ff;
4104 data_hi = data_hi & 0xff00;
4106 data_decoded = data_hi | data_lo;
4108 last_data_encoded = data_encoded;
4110 offset1 = (offset1 + 1) % 31;
4111 offset2 = offset2 & 0xff;
4113 return data_decoded;
4116 int getMappedElement_DC(int element)
4124 /* 0x0117 - 0x036e: (?) */
4127 /* 0x042d - 0x0684: (?) */
4143 element = EL_CRYSTAL;
4146 case 0x0e77: /* quicksand (boulder) */
4147 element = EL_QUICKSAND_FAST_FULL;
4150 case 0x0e99: /* slow quicksand (boulder) */
4151 element = EL_QUICKSAND_FULL;
4155 element = EL_EM_EXIT_OPEN;
4159 element = EL_EM_EXIT_CLOSED;
4163 element = EL_EM_STEEL_EXIT_OPEN;
4167 element = EL_EM_STEEL_EXIT_CLOSED;
4170 case 0x0f4f: /* dynamite (lit 1) */
4171 element = EL_EM_DYNAMITE_ACTIVE;
4174 case 0x0f57: /* dynamite (lit 2) */
4175 element = EL_EM_DYNAMITE_ACTIVE;
4178 case 0x0f5f: /* dynamite (lit 3) */
4179 element = EL_EM_DYNAMITE_ACTIVE;
4182 case 0x0f67: /* dynamite (lit 4) */
4183 element = EL_EM_DYNAMITE_ACTIVE;
4190 element = EL_AMOEBA_WET;
4194 element = EL_AMOEBA_DROP;
4198 element = EL_DC_MAGIC_WALL;
4202 element = EL_SPACESHIP_UP;
4206 element = EL_SPACESHIP_DOWN;
4210 element = EL_SPACESHIP_LEFT;
4214 element = EL_SPACESHIP_RIGHT;
4218 element = EL_BUG_UP;
4222 element = EL_BUG_DOWN;
4226 element = EL_BUG_LEFT;
4230 element = EL_BUG_RIGHT;
4234 element = EL_MOLE_UP;
4238 element = EL_MOLE_DOWN;
4242 element = EL_MOLE_LEFT;
4246 element = EL_MOLE_RIGHT;
4254 element = EL_YAMYAM;
4258 element = EL_SWITCHGATE_OPEN;
4262 element = EL_SWITCHGATE_CLOSED;
4266 element = EL_DC_SWITCHGATE_SWITCH_UP;
4270 element = EL_TIMEGATE_CLOSED;
4273 case 0x144c: /* conveyor belt switch (green) */
4274 element = EL_CONVEYOR_BELT_3_SWITCH_MIDDLE;
4277 case 0x144f: /* conveyor belt switch (red) */
4278 element = EL_CONVEYOR_BELT_1_SWITCH_MIDDLE;
4281 case 0x1452: /* conveyor belt switch (blue) */
4282 element = EL_CONVEYOR_BELT_4_SWITCH_MIDDLE;
4286 element = EL_CONVEYOR_BELT_3_MIDDLE;
4290 element = EL_CONVEYOR_BELT_3_LEFT;
4294 element = EL_CONVEYOR_BELT_3_RIGHT;
4298 element = EL_CONVEYOR_BELT_1_MIDDLE;
4302 element = EL_CONVEYOR_BELT_1_LEFT;
4306 element = EL_CONVEYOR_BELT_1_RIGHT;
4310 element = EL_CONVEYOR_BELT_4_MIDDLE;
4314 element = EL_CONVEYOR_BELT_4_LEFT;
4318 element = EL_CONVEYOR_BELT_4_RIGHT;
4322 element = EL_EXPANDABLE_WALL_HORIZONTAL;
4326 element = EL_EXPANDABLE_WALL_VERTICAL;
4330 element = EL_EXPANDABLE_WALL_ANY;
4333 case 0x14ce: /* growing steel wall (left/right) */
4334 element = EL_EXPANDABLE_STEELWALL_HORIZONTAL;
4337 case 0x14df: /* growing steel wall (up/down) */
4338 element = EL_EXPANDABLE_STEELWALL_VERTICAL;
4341 case 0x14e8: /* growing steel wall (up/down/left/right) */
4342 element = EL_EXPANDABLE_STEELWALL_ANY;
4346 element = EL_SHIELD_DEADLY;
4350 element = EL_EXTRA_TIME;
4358 element = EL_EMPTY_SPACE;
4361 case 0x1578: /* quicksand (empty) */
4362 element = EL_QUICKSAND_FAST_EMPTY;
4365 case 0x1579: /* slow quicksand (empty) */
4366 element = EL_QUICKSAND_EMPTY;
4369 /* 0x157c - 0x158b: */
4372 /* 0x1590 - 0x159f: */
4373 /* EL_DC_LANDMINE */
4376 element = EL_EM_DYNAMITE;
4379 case 0x15a1: /* key (red) */
4380 element = EL_EM_KEY_1;
4383 case 0x15a2: /* key (yellow) */
4384 element = EL_EM_KEY_2;
4387 case 0x15a3: /* key (blue) */
4388 element = EL_EM_KEY_4;
4391 case 0x15a4: /* key (green) */
4392 element = EL_EM_KEY_3;
4395 case 0x15a5: /* key (white) */
4396 element = EL_DC_KEY_WHITE;
4400 element = EL_WALL_SLIPPERY;
4407 case 0x15a8: /* wall (not round) */
4411 case 0x15a9: /* (blue) */
4412 element = EL_CHAR_A;
4415 case 0x15aa: /* (blue) */
4416 element = EL_CHAR_B;
4419 case 0x15ab: /* (blue) */
4420 element = EL_CHAR_C;
4423 case 0x15ac: /* (blue) */
4424 element = EL_CHAR_D;
4427 case 0x15ad: /* (blue) */
4428 element = EL_CHAR_E;
4431 case 0x15ae: /* (blue) */
4432 element = EL_CHAR_F;
4435 case 0x15af: /* (blue) */
4436 element = EL_CHAR_G;
4439 case 0x15b0: /* (blue) */
4440 element = EL_CHAR_H;
4443 case 0x15b1: /* (blue) */
4444 element = EL_CHAR_I;
4447 case 0x15b2: /* (blue) */
4448 element = EL_CHAR_J;
4451 case 0x15b3: /* (blue) */
4452 element = EL_CHAR_K;
4455 case 0x15b4: /* (blue) */
4456 element = EL_CHAR_L;
4459 case 0x15b5: /* (blue) */
4460 element = EL_CHAR_M;
4463 case 0x15b6: /* (blue) */
4464 element = EL_CHAR_N;
4467 case 0x15b7: /* (blue) */
4468 element = EL_CHAR_O;
4471 case 0x15b8: /* (blue) */
4472 element = EL_CHAR_P;
4475 case 0x15b9: /* (blue) */
4476 element = EL_CHAR_Q;
4479 case 0x15ba: /* (blue) */
4480 element = EL_CHAR_R;
4483 case 0x15bb: /* (blue) */
4484 element = EL_CHAR_S;
4487 case 0x15bc: /* (blue) */
4488 element = EL_CHAR_T;
4491 case 0x15bd: /* (blue) */
4492 element = EL_CHAR_U;
4495 case 0x15be: /* (blue) */
4496 element = EL_CHAR_V;
4499 case 0x15bf: /* (blue) */
4500 element = EL_CHAR_W;
4503 case 0x15c0: /* (blue) */
4504 element = EL_CHAR_X;
4507 case 0x15c1: /* (blue) */
4508 element = EL_CHAR_Y;
4511 case 0x15c2: /* (blue) */
4512 element = EL_CHAR_Z;
4515 case 0x15c3: /* (blue) */
4516 element = EL_CHAR_AUMLAUT;
4519 case 0x15c4: /* (blue) */
4520 element = EL_CHAR_OUMLAUT;
4523 case 0x15c5: /* (blue) */
4524 element = EL_CHAR_UUMLAUT;
4527 case 0x15c6: /* (blue) */
4528 element = EL_CHAR_0;
4531 case 0x15c7: /* (blue) */
4532 element = EL_CHAR_1;
4535 case 0x15c8: /* (blue) */
4536 element = EL_CHAR_2;
4539 case 0x15c9: /* (blue) */
4540 element = EL_CHAR_3;
4543 case 0x15ca: /* (blue) */
4544 element = EL_CHAR_4;
4547 case 0x15cb: /* (blue) */
4548 element = EL_CHAR_5;
4551 case 0x15cc: /* (blue) */
4552 element = EL_CHAR_6;
4555 case 0x15cd: /* (blue) */
4556 element = EL_CHAR_7;
4559 case 0x15ce: /* (blue) */
4560 element = EL_CHAR_8;
4563 case 0x15cf: /* (blue) */
4564 element = EL_CHAR_9;
4567 case 0x15d0: /* (blue) */
4568 element = EL_CHAR_PERIOD;
4571 case 0x15d1: /* (blue) */
4572 element = EL_CHAR_EXCLAM;
4575 case 0x15d2: /* (blue) */
4576 element = EL_CHAR_COLON;
4579 case 0x15d3: /* (blue) */
4580 element = EL_CHAR_LESS;
4583 case 0x15d4: /* (blue) */
4584 element = EL_CHAR_GREATER;
4587 case 0x15d5: /* (blue) */
4588 element = EL_CHAR_QUESTION;
4591 case 0x15d6: /* (blue) */
4592 element = EL_CHAR_COPYRIGHT;
4595 case 0x15d7: /* (blue) */
4596 element = EL_CHAR_UP;
4599 case 0x15d8: /* (blue) */
4600 element = EL_CHAR_DOWN;
4603 case 0x15d9: /* (blue) */
4604 element = EL_CHAR_BUTTON;
4607 case 0x15da: /* (blue) */
4608 element = EL_CHAR_PLUS;
4611 case 0x15db: /* (blue) */
4612 element = EL_CHAR_MINUS;
4615 case 0x15dc: /* (blue) */
4616 element = EL_CHAR_APOSTROPHE;
4619 case 0x15dd: /* (blue) */
4620 element = EL_CHAR_PARENLEFT;
4623 case 0x15de: /* (blue) */
4624 element = EL_CHAR_PARENRIGHT;
4627 case 0x15df: /* (green) */
4628 element = EL_CHAR_A;
4631 case 0x15e0: /* (green) */
4632 element = EL_CHAR_B;
4635 case 0x15e1: /* (green) */
4636 element = EL_CHAR_C;
4639 case 0x15e2: /* (green) */
4640 element = EL_CHAR_D;
4643 case 0x15e3: /* (green) */
4644 element = EL_CHAR_E;
4647 case 0x15e4: /* (green) */
4648 element = EL_CHAR_F;
4651 case 0x15e5: /* (green) */
4652 element = EL_CHAR_G;
4655 case 0x15e6: /* (green) */
4656 element = EL_CHAR_H;
4659 case 0x15e7: /* (green) */
4660 element = EL_CHAR_I;
4663 case 0x15e8: /* (green) */
4664 element = EL_CHAR_J;
4667 case 0x15e9: /* (green) */
4668 element = EL_CHAR_K;
4671 case 0x15ea: /* (green) */
4672 element = EL_CHAR_L;
4675 case 0x15eb: /* (green) */
4676 element = EL_CHAR_M;
4679 case 0x15ec: /* (green) */
4680 element = EL_CHAR_N;
4683 case 0x15ed: /* (green) */
4684 element = EL_CHAR_O;
4687 case 0x15ee: /* (green) */
4688 element = EL_CHAR_P;
4691 case 0x15ef: /* (green) */
4692 element = EL_CHAR_Q;
4695 case 0x15f0: /* (green) */
4696 element = EL_CHAR_R;
4699 case 0x15f1: /* (green) */
4700 element = EL_CHAR_S;
4703 case 0x15f2: /* (green) */
4704 element = EL_CHAR_T;
4707 case 0x15f3: /* (green) */
4708 element = EL_CHAR_U;
4711 case 0x15f4: /* (green) */
4712 element = EL_CHAR_V;
4715 case 0x15f5: /* (green) */
4716 element = EL_CHAR_W;
4719 case 0x15f6: /* (green) */
4720 element = EL_CHAR_X;
4723 case 0x15f7: /* (green) */
4724 element = EL_CHAR_Y;
4727 case 0x15f8: /* (green) */
4728 element = EL_CHAR_Z;
4731 case 0x15f9: /* (green) */
4732 element = EL_CHAR_AUMLAUT;
4735 case 0x15fa: /* (green) */
4736 element = EL_CHAR_OUMLAUT;
4739 case 0x15fb: /* (green) */
4740 element = EL_CHAR_UUMLAUT;
4743 case 0x15fc: /* (green) */
4744 element = EL_CHAR_0;
4747 case 0x15fd: /* (green) */
4748 element = EL_CHAR_1;
4751 case 0x15fe: /* (green) */
4752 element = EL_CHAR_2;
4755 case 0x15ff: /* (green) */
4756 element = EL_CHAR_3;
4759 case 0x1600: /* (green) */
4760 element = EL_CHAR_4;
4763 case 0x1601: /* (green) */
4764 element = EL_CHAR_5;
4767 case 0x1602: /* (green) */
4768 element = EL_CHAR_6;
4771 case 0x1603: /* (green) */
4772 element = EL_CHAR_7;
4775 case 0x1604: /* (green) */
4776 element = EL_CHAR_8;
4779 case 0x1605: /* (green) */
4780 element = EL_CHAR_9;
4783 case 0x1606: /* (green) */
4784 element = EL_CHAR_PERIOD;
4787 case 0x1607: /* (green) */
4788 element = EL_CHAR_EXCLAM;
4791 case 0x1608: /* (green) */
4792 element = EL_CHAR_COLON;
4795 case 0x1609: /* (green) */
4796 element = EL_CHAR_LESS;
4799 case 0x160a: /* (green) */
4800 element = EL_CHAR_GREATER;
4803 case 0x160b: /* (green) */
4804 element = EL_CHAR_QUESTION;
4807 case 0x160c: /* (green) */
4808 element = EL_CHAR_COPYRIGHT;
4811 case 0x160d: /* (green) */
4812 element = EL_CHAR_UP;
4815 case 0x160e: /* (green) */
4816 element = EL_CHAR_DOWN;
4819 case 0x160f: /* (green) */
4820 element = EL_CHAR_BUTTON;
4823 case 0x1610: /* (green) */
4824 element = EL_CHAR_PLUS;
4827 case 0x1611: /* (green) */
4828 element = EL_CHAR_MINUS;
4831 case 0x1612: /* (green) */
4832 element = EL_CHAR_APOSTROPHE;
4835 case 0x1613: /* (green) */
4836 element = EL_CHAR_PARENLEFT;
4839 case 0x1614: /* (green) */
4840 element = EL_CHAR_PARENRIGHT;
4843 case 0x1615: /* (blue steel) */
4844 element = EL_STEEL_CHAR_A;
4847 case 0x1616: /* (blue steel) */
4848 element = EL_STEEL_CHAR_B;
4851 case 0x1617: /* (blue steel) */
4852 element = EL_STEEL_CHAR_C;
4855 case 0x1618: /* (blue steel) */
4856 element = EL_STEEL_CHAR_D;
4859 case 0x1619: /* (blue steel) */
4860 element = EL_STEEL_CHAR_E;
4863 case 0x161a: /* (blue steel) */
4864 element = EL_STEEL_CHAR_F;
4867 case 0x161b: /* (blue steel) */
4868 element = EL_STEEL_CHAR_G;
4871 case 0x161c: /* (blue steel) */
4872 element = EL_STEEL_CHAR_H;
4875 case 0x161d: /* (blue steel) */
4876 element = EL_STEEL_CHAR_I;
4879 case 0x161e: /* (blue steel) */
4880 element = EL_STEEL_CHAR_J;
4883 case 0x161f: /* (blue steel) */
4884 element = EL_STEEL_CHAR_K;
4887 case 0x1620: /* (blue steel) */
4888 element = EL_STEEL_CHAR_L;
4891 case 0x1621: /* (blue steel) */
4892 element = EL_STEEL_CHAR_M;
4895 case 0x1622: /* (blue steel) */
4896 element = EL_STEEL_CHAR_N;
4899 case 0x1623: /* (blue steel) */
4900 element = EL_STEEL_CHAR_O;
4903 case 0x1624: /* (blue steel) */
4904 element = EL_STEEL_CHAR_P;
4907 case 0x1625: /* (blue steel) */
4908 element = EL_STEEL_CHAR_Q;
4911 case 0x1626: /* (blue steel) */
4912 element = EL_STEEL_CHAR_R;
4915 case 0x1627: /* (blue steel) */
4916 element = EL_STEEL_CHAR_S;
4919 case 0x1628: /* (blue steel) */
4920 element = EL_STEEL_CHAR_T;
4923 case 0x1629: /* (blue steel) */
4924 element = EL_STEEL_CHAR_U;
4927 case 0x162a: /* (blue steel) */
4928 element = EL_STEEL_CHAR_V;
4931 case 0x162b: /* (blue steel) */
4932 element = EL_STEEL_CHAR_W;
4935 case 0x162c: /* (blue steel) */
4936 element = EL_STEEL_CHAR_X;
4939 case 0x162d: /* (blue steel) */
4940 element = EL_STEEL_CHAR_Y;
4943 case 0x162e: /* (blue steel) */
4944 element = EL_STEEL_CHAR_Z;
4947 case 0x162f: /* (blue steel) */
4948 element = EL_STEEL_CHAR_AUMLAUT;
4951 case 0x1630: /* (blue steel) */
4952 element = EL_STEEL_CHAR_OUMLAUT;
4955 case 0x1631: /* (blue steel) */
4956 element = EL_STEEL_CHAR_UUMLAUT;
4959 case 0x1632: /* (blue steel) */
4960 element = EL_STEEL_CHAR_0;
4963 case 0x1633: /* (blue steel) */
4964 element = EL_STEEL_CHAR_1;
4967 case 0x1634: /* (blue steel) */
4968 element = EL_STEEL_CHAR_2;
4971 case 0x1635: /* (blue steel) */
4972 element = EL_STEEL_CHAR_3;
4975 case 0x1636: /* (blue steel) */
4976 element = EL_STEEL_CHAR_4;
4979 case 0x1637: /* (blue steel) */
4980 element = EL_STEEL_CHAR_5;
4983 case 0x1638: /* (blue steel) */
4984 element = EL_STEEL_CHAR_6;
4987 case 0x1639: /* (blue steel) */
4988 element = EL_STEEL_CHAR_7;
4991 case 0x163a: /* (blue steel) */
4992 element = EL_STEEL_CHAR_8;
4995 case 0x163b: /* (blue steel) */
4996 element = EL_STEEL_CHAR_9;
4999 case 0x163c: /* (blue steel) */
5000 element = EL_STEEL_CHAR_PERIOD;
5003 case 0x163d: /* (blue steel) */
5004 element = EL_STEEL_CHAR_EXCLAM;
5007 case 0x163e: /* (blue steel) */
5008 element = EL_STEEL_CHAR_COLON;
5011 case 0x163f: /* (blue steel) */
5012 element = EL_STEEL_CHAR_LESS;
5015 case 0x1640: /* (blue steel) */
5016 element = EL_STEEL_CHAR_GREATER;
5019 case 0x1641: /* (blue steel) */
5020 element = EL_STEEL_CHAR_QUESTION;
5023 case 0x1642: /* (blue steel) */
5024 element = EL_STEEL_CHAR_COPYRIGHT;
5027 case 0x1643: /* (blue steel) */
5028 element = EL_STEEL_CHAR_UP;
5031 case 0x1644: /* (blue steel) */
5032 element = EL_STEEL_CHAR_DOWN;
5035 case 0x1645: /* (blue steel) */
5036 element = EL_STEEL_CHAR_BUTTON;
5039 case 0x1646: /* (blue steel) */
5040 element = EL_STEEL_CHAR_PLUS;
5043 case 0x1647: /* (blue steel) */
5044 element = EL_STEEL_CHAR_MINUS;
5047 case 0x1648: /* (blue steel) */
5048 element = EL_STEEL_CHAR_APOSTROPHE;
5051 case 0x1649: /* (blue steel) */
5052 element = EL_STEEL_CHAR_PARENLEFT;
5055 case 0x164a: /* (blue steel) */
5056 element = EL_STEEL_CHAR_PARENRIGHT;
5059 case 0x164b: /* (green steel) */
5060 element = EL_STEEL_CHAR_A;
5063 case 0x164c: /* (green steel) */
5064 element = EL_STEEL_CHAR_B;
5067 case 0x164d: /* (green steel) */
5068 element = EL_STEEL_CHAR_C;
5071 case 0x164e: /* (green steel) */
5072 element = EL_STEEL_CHAR_D;
5075 case 0x164f: /* (green steel) */
5076 element = EL_STEEL_CHAR_E;
5079 case 0x1650: /* (green steel) */
5080 element = EL_STEEL_CHAR_F;
5083 case 0x1651: /* (green steel) */
5084 element = EL_STEEL_CHAR_G;
5087 case 0x1652: /* (green steel) */
5088 element = EL_STEEL_CHAR_H;
5091 case 0x1653: /* (green steel) */
5092 element = EL_STEEL_CHAR_I;
5095 case 0x1654: /* (green steel) */
5096 element = EL_STEEL_CHAR_J;
5099 case 0x1655: /* (green steel) */
5100 element = EL_STEEL_CHAR_K;
5103 case 0x1656: /* (green steel) */
5104 element = EL_STEEL_CHAR_L;
5107 case 0x1657: /* (green steel) */
5108 element = EL_STEEL_CHAR_M;
5111 case 0x1658: /* (green steel) */
5112 element = EL_STEEL_CHAR_N;
5115 case 0x1659: /* (green steel) */
5116 element = EL_STEEL_CHAR_O;
5119 case 0x165a: /* (green steel) */
5120 element = EL_STEEL_CHAR_P;
5123 case 0x165b: /* (green steel) */
5124 element = EL_STEEL_CHAR_Q;
5127 case 0x165c: /* (green steel) */
5128 element = EL_STEEL_CHAR_R;
5131 case 0x165d: /* (green steel) */
5132 element = EL_STEEL_CHAR_S;
5135 case 0x165e: /* (green steel) */
5136 element = EL_STEEL_CHAR_T;
5139 case 0x165f: /* (green steel) */
5140 element = EL_STEEL_CHAR_U;
5143 case 0x1660: /* (green steel) */
5144 element = EL_STEEL_CHAR_V;
5147 case 0x1661: /* (green steel) */
5148 element = EL_STEEL_CHAR_W;
5151 case 0x1662: /* (green steel) */
5152 element = EL_STEEL_CHAR_X;
5155 case 0x1663: /* (green steel) */
5156 element = EL_STEEL_CHAR_Y;
5159 case 0x1664: /* (green steel) */
5160 element = EL_STEEL_CHAR_Z;
5163 case 0x1665: /* (green steel) */
5164 element = EL_STEEL_CHAR_AUMLAUT;
5167 case 0x1666: /* (green steel) */
5168 element = EL_STEEL_CHAR_OUMLAUT;
5171 case 0x1667: /* (green steel) */
5172 element = EL_STEEL_CHAR_UUMLAUT;
5175 case 0x1668: /* (green steel) */
5176 element = EL_STEEL_CHAR_0;
5179 case 0x1669: /* (green steel) */
5180 element = EL_STEEL_CHAR_1;
5183 case 0x166a: /* (green steel) */
5184 element = EL_STEEL_CHAR_2;
5187 case 0x166b: /* (green steel) */
5188 element = EL_STEEL_CHAR_3;
5191 case 0x166c: /* (green steel) */
5192 element = EL_STEEL_CHAR_4;
5195 case 0x166d: /* (green steel) */
5196 element = EL_STEEL_CHAR_5;
5199 case 0x166e: /* (green steel) */
5200 element = EL_STEEL_CHAR_6;
5203 case 0x166f: /* (green steel) */
5204 element = EL_STEEL_CHAR_7;
5207 case 0x1670: /* (green steel) */
5208 element = EL_STEEL_CHAR_8;
5211 case 0x1671: /* (green steel) */
5212 element = EL_STEEL_CHAR_9;
5215 case 0x1672: /* (green steel) */
5216 element = EL_STEEL_CHAR_PERIOD;
5219 case 0x1673: /* (green steel) */
5220 element = EL_STEEL_CHAR_EXCLAM;
5223 case 0x1674: /* (green steel) */
5224 element = EL_STEEL_CHAR_COLON;
5227 case 0x1675: /* (green steel) */
5228 element = EL_STEEL_CHAR_LESS;
5231 case 0x1676: /* (green steel) */
5232 element = EL_STEEL_CHAR_GREATER;
5235 case 0x1677: /* (green steel) */
5236 element = EL_STEEL_CHAR_QUESTION;
5239 case 0x1678: /* (green steel) */
5240 element = EL_STEEL_CHAR_COPYRIGHT;
5243 case 0x1679: /* (green steel) */
5244 element = EL_STEEL_CHAR_UP;
5247 case 0x167a: /* (green steel) */
5248 element = EL_STEEL_CHAR_DOWN;
5251 case 0x167b: /* (green steel) */
5252 element = EL_STEEL_CHAR_BUTTON;
5255 case 0x167c: /* (green steel) */
5256 element = EL_STEEL_CHAR_PLUS;
5259 case 0x167d: /* (green steel) */
5260 element = EL_STEEL_CHAR_MINUS;
5263 case 0x167e: /* (green steel) */
5264 element = EL_STEEL_CHAR_APOSTROPHE;
5267 case 0x167f: /* (green steel) */
5268 element = EL_STEEL_CHAR_PARENLEFT;
5271 case 0x1680: /* (green steel) */
5272 element = EL_STEEL_CHAR_PARENRIGHT;
5275 case 0x1681: /* gate (red) */
5276 element = EL_EM_GATE_1;
5279 case 0x1682: /* secret gate (red) */
5280 element = EL_GATE_1_GRAY;
5283 case 0x1683: /* gate (yellow) */
5284 element = EL_EM_GATE_2;
5287 case 0x1684: /* secret gate (yellow) */
5288 element = EL_GATE_2_GRAY;
5291 case 0x1685: /* gate (blue) */
5292 element = EL_EM_GATE_4;
5295 case 0x1686: /* secret gate (blue) */
5296 element = EL_GATE_4_GRAY;
5299 case 0x1687: /* gate (green) */
5300 element = EL_EM_GATE_3;
5303 case 0x1688: /* secret gate (green) */
5304 element = EL_GATE_3_GRAY;
5307 case 0x1689: /* gate (white) */
5308 element = EL_DC_GATE_WHITE;
5311 case 0x168a: /* secret gate (white) */
5312 element = EL_DC_GATE_WHITE_GRAY;
5315 case 0x168b: /* secret gate (no key) */
5316 element = EL_DC_GATE_FAKE_GRAY;
5320 element = EL_ROBOT_WHEEL;
5324 element = EL_DC_TIMEGATE_SWITCH;
5328 element = EL_ACID_POOL_BOTTOM;
5332 element = EL_ACID_POOL_TOPLEFT;
5336 element = EL_ACID_POOL_TOPRIGHT;
5340 element = EL_ACID_POOL_BOTTOMLEFT;
5344 element = EL_ACID_POOL_BOTTOMRIGHT;
5348 element = EL_STEELWALL;
5352 element = EL_STEELWALL_SLIPPERY;
5355 case 0x1695: /* steel wall (not round) */
5356 element = EL_STEELWALL;
5359 case 0x1696: /* steel wall (left) */
5360 element = EL_DC_STEELWALL_1_LEFT;
5363 case 0x1697: /* steel wall (bottom) */
5364 element = EL_DC_STEELWALL_1_BOTTOM;
5367 case 0x1698: /* steel wall (right) */
5368 element = EL_DC_STEELWALL_1_RIGHT;
5371 case 0x1699: /* steel wall (top) */
5372 element = EL_DC_STEELWALL_1_TOP;
5375 case 0x169a: /* steel wall (left/bottom) */
5376 element = EL_DC_STEELWALL_1_BOTTOMLEFT;
5379 case 0x169b: /* steel wall (right/bottom) */
5380 element = EL_DC_STEELWALL_1_BOTTOMRIGHT;
5383 case 0x169c: /* steel wall (right/top) */
5384 element = EL_DC_STEELWALL_1_TOPRIGHT;
5387 case 0x169d: /* steel wall (left/top) */
5388 element = EL_DC_STEELWALL_1_TOPLEFT;
5391 case 0x169e: /* steel wall (right/bottom small) */
5392 element = EL_DC_STEELWALL_1_BOTTOMRIGHT_2;
5395 case 0x169f: /* steel wall (left/bottom small) */
5396 element = EL_DC_STEELWALL_1_BOTTOMLEFT_2;
5399 case 0x16a0: /* steel wall (right/top small) */
5400 element = EL_DC_STEELWALL_1_TOPRIGHT_2;
5403 case 0x16a1: /* steel wall (left/top small) */
5404 element = EL_DC_STEELWALL_1_TOPLEFT_2;
5407 case 0x16a2: /* steel wall (left/right) */
5408 element = EL_DC_STEELWALL_1_VERTICAL;
5411 case 0x16a3: /* steel wall (top/bottom) */
5412 element = EL_DC_STEELWALL_1_HORIZONTAL;
5415 case 0x16a4: /* steel wall 2 (left end) */
5416 element = EL_DC_STEELWALL_2_LEFT;
5419 case 0x16a5: /* steel wall 2 (right end) */
5420 element = EL_DC_STEELWALL_2_RIGHT;
5423 case 0x16a6: /* steel wall 2 (top end) */
5424 element = EL_DC_STEELWALL_2_TOP;
5427 case 0x16a7: /* steel wall 2 (bottom end) */
5428 element = EL_DC_STEELWALL_2_BOTTOM;
5431 case 0x16a8: /* steel wall 2 (left/right) */
5432 element = EL_DC_STEELWALL_2_HORIZONTAL;
5435 case 0x16a9: /* steel wall 2 (up/down) */
5436 element = EL_DC_STEELWALL_2_VERTICAL;
5439 case 0x16aa: /* steel wall 2 (mid) */
5440 element = EL_DC_STEELWALL_2_MIDDLE;
5444 element = EL_SIGN_EXCLAMATION;
5448 element = EL_SIGN_RADIOACTIVITY;
5452 element = EL_SIGN_STOP;
5456 element = EL_SIGN_WHEELCHAIR;
5460 element = EL_SIGN_PARKING;
5464 element = EL_SIGN_NO_ENTRY;
5468 element = EL_SIGN_HEART;
5472 element = EL_SIGN_GIVE_WAY;
5476 element = EL_SIGN_ENTRY_FORBIDDEN;
5480 element = EL_SIGN_EMERGENCY_EXIT;
5484 element = EL_SIGN_YIN_YANG;
5488 element = EL_WALL_EMERALD;
5492 element = EL_WALL_DIAMOND;
5496 element = EL_WALL_PEARL;
5500 element = EL_WALL_CRYSTAL;
5504 element = EL_INVISIBLE_WALL;
5508 element = EL_INVISIBLE_STEELWALL;
5511 /* 0x16bc - 0x16cb: */
5512 /* EL_INVISIBLE_SAND */
5515 element = EL_LIGHT_SWITCH;
5519 element = EL_ENVELOPE_1;
5523 if (element >= 0x0117 && element <= 0x036e) /* (?) */
5524 element = EL_DIAMOND;
5525 else if (element >= 0x042d && element <= 0x0684) /* (?) */
5526 element = EL_EMERALD;
5527 else if (element >= 0x157c && element <= 0x158b)
5529 else if (element >= 0x1590 && element <= 0x159f)
5530 element = EL_DC_LANDMINE;
5531 else if (element >= 0x16bc && element <= 0x16cb)
5532 element = EL_INVISIBLE_SAND;
5535 Error(ERR_WARN, "unknown Diamond Caves element 0x%04x", element);
5536 element = EL_UNKNOWN;
5541 return getMappedElement(element);
5544 static void LoadLevelFromFileStream_DC(File *file, struct LevelInfo *level,
5547 byte header[DC_LEVEL_HEADER_SIZE];
5549 int envelope_header_pos = 62;
5550 int envelope_content_pos = 94;
5551 int level_name_pos = 251;
5552 int level_author_pos = 292;
5553 int envelope_header_len;
5554 int envelope_content_len;
5556 int level_author_len;
5558 int num_yamyam_contents;
5561 getDecodedWord_DC(0, TRUE); /* initialize DC2 decoding engine */
5563 for (i = 0; i < DC_LEVEL_HEADER_SIZE / 2; i++)
5565 unsigned short header_word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
5567 header[i * 2 + 0] = header_word >> 8;
5568 header[i * 2 + 1] = header_word & 0xff;
5571 /* read some values from level header to check level decoding integrity */
5572 fieldx = header[6] | (header[7] << 8);
5573 fieldy = header[8] | (header[9] << 8);
5574 num_yamyam_contents = header[60] | (header[61] << 8);
5576 /* do some simple sanity checks to ensure that level was correctly decoded */
5577 if (fieldx < 1 || fieldx > 256 ||
5578 fieldy < 1 || fieldy > 256 ||
5579 num_yamyam_contents < 1 || num_yamyam_contents > 8)
5581 level->no_valid_file = TRUE;
5583 Error(ERR_WARN, "cannot decode level from stream -- using empty level");
5588 /* maximum envelope header size is 31 bytes */
5589 envelope_header_len = header[envelope_header_pos];
5590 /* maximum envelope content size is 110 (156?) bytes */
5591 envelope_content_len = header[envelope_content_pos];
5593 /* maximum level title size is 40 bytes */
5594 level_name_len = MIN(header[level_name_pos], MAX_LEVEL_NAME_LEN);
5595 /* maximum level author size is 30 (51?) bytes */
5596 level_author_len = MIN(header[level_author_pos], MAX_LEVEL_AUTHOR_LEN);
5600 for (i = 0; i < envelope_header_len; i++)
5601 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
5602 level->envelope[0].text[envelope_size++] =
5603 header[envelope_header_pos + 1 + i];
5605 if (envelope_header_len > 0 && envelope_content_len > 0)
5607 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
5608 level->envelope[0].text[envelope_size++] = '\n';
5609 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
5610 level->envelope[0].text[envelope_size++] = '\n';
5613 for (i = 0; i < envelope_content_len; i++)
5614 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
5615 level->envelope[0].text[envelope_size++] =
5616 header[envelope_content_pos + 1 + i];
5618 level->envelope[0].text[envelope_size] = '\0';
5620 level->envelope[0].xsize = MAX_ENVELOPE_XSIZE;
5621 level->envelope[0].ysize = 10;
5622 level->envelope[0].autowrap = TRUE;
5623 level->envelope[0].centered = TRUE;
5625 for (i = 0; i < level_name_len; i++)
5626 level->name[i] = header[level_name_pos + 1 + i];
5627 level->name[level_name_len] = '\0';
5629 for (i = 0; i < level_author_len; i++)
5630 level->author[i] = header[level_author_pos + 1 + i];
5631 level->author[level_author_len] = '\0';
5633 num_yamyam_contents = header[60] | (header[61] << 8);
5634 level->num_yamyam_contents =
5635 MIN(MAX(MIN_ELEMENT_CONTENTS, num_yamyam_contents), MAX_ELEMENT_CONTENTS);
5637 for (i = 0; i < num_yamyam_contents; i++)
5639 for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
5641 unsigned short word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
5642 int element_dc = ((word & 0xff) << 8) | ((word >> 8) & 0xff);
5644 if (i < MAX_ELEMENT_CONTENTS)
5645 level->yamyam_content[i].e[x][y] = getMappedElement_DC(element_dc);
5649 fieldx = header[6] | (header[7] << 8);
5650 fieldy = header[8] | (header[9] << 8);
5651 level->fieldx = MIN(MAX(MIN_LEV_FIELDX, fieldx), MAX_LEV_FIELDX);
5652 level->fieldy = MIN(MAX(MIN_LEV_FIELDY, fieldy), MAX_LEV_FIELDY);
5654 for (y = 0; y < fieldy; y++) for (x = 0; x < fieldx; x++)
5656 unsigned short word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
5657 int element_dc = ((word & 0xff) << 8) | ((word >> 8) & 0xff);
5659 if (x < MAX_LEV_FIELDX && y < MAX_LEV_FIELDY)
5660 level->field[x][y] = getMappedElement_DC(element_dc);
5663 x = MIN(MAX(0, (header[10] | (header[11] << 8)) - 1), MAX_LEV_FIELDX - 1);
5664 y = MIN(MAX(0, (header[12] | (header[13] << 8)) - 1), MAX_LEV_FIELDY - 1);
5665 level->field[x][y] = EL_PLAYER_1;
5667 x = MIN(MAX(0, (header[14] | (header[15] << 8)) - 1), MAX_LEV_FIELDX - 1);
5668 y = MIN(MAX(0, (header[16] | (header[17] << 8)) - 1), MAX_LEV_FIELDY - 1);
5669 level->field[x][y] = EL_PLAYER_2;
5671 level->gems_needed = header[18] | (header[19] << 8);
5673 level->score[SC_EMERALD] = header[20] | (header[21] << 8);
5674 level->score[SC_DIAMOND] = header[22] | (header[23] << 8);
5675 level->score[SC_PEARL] = header[24] | (header[25] << 8);
5676 level->score[SC_CRYSTAL] = header[26] | (header[27] << 8);
5677 level->score[SC_NUT] = header[28] | (header[29] << 8);
5678 level->score[SC_ROBOT] = header[30] | (header[31] << 8);
5679 level->score[SC_SPACESHIP] = header[32] | (header[33] << 8);
5680 level->score[SC_BUG] = header[34] | (header[35] << 8);
5681 level->score[SC_YAMYAM] = header[36] | (header[37] << 8);
5682 level->score[SC_DYNAMITE] = header[38] | (header[39] << 8);
5683 level->score[SC_KEY] = header[40] | (header[41] << 8);
5684 level->score[SC_TIME_BONUS] = header[42] | (header[43] << 8);
5686 level->time = header[44] | (header[45] << 8);
5688 level->amoeba_speed = header[46] | (header[47] << 8);
5689 level->time_light = header[48] | (header[49] << 8);
5690 level->time_timegate = header[50] | (header[51] << 8);
5691 level->time_wheel = header[52] | (header[53] << 8);
5692 level->time_magic_wall = header[54] | (header[55] << 8);
5693 level->extra_time = header[56] | (header[57] << 8);
5694 level->shield_normal_time = header[58] | (header[59] << 8);
5696 /* Diamond Caves has the same (strange) behaviour as Emerald Mine that gems
5697 can slip down from flat walls, like normal walls and steel walls */
5698 level->em_slippery_gems = TRUE;
5701 static void LoadLevelFromFileInfo_DC(struct LevelInfo *level,
5702 struct LevelFileInfo *level_file_info,
5703 boolean level_info_only)
5705 char *filename = level_file_info->filename;
5707 int num_magic_bytes = 8;
5708 char magic_bytes[num_magic_bytes + 1];
5709 int num_levels_to_skip = level_file_info->nr - leveldir_current->first_level;
5711 if (!(file = openFile(filename, MODE_READ)))
5713 level->no_valid_file = TRUE;
5715 if (!level_info_only)
5716 Error(ERR_WARN, "cannot read level '%s' -- using empty level", filename);
5721 // fseek(file, 0x0000, SEEK_SET);
5723 if (level_file_info->packed)
5725 /* read "magic bytes" from start of file */
5726 if (getStringFromFile(file, magic_bytes, num_magic_bytes + 1) == NULL)
5727 magic_bytes[0] = '\0';
5729 /* check "magic bytes" for correct file format */
5730 if (!strPrefix(magic_bytes, "DC2"))
5732 level->no_valid_file = TRUE;
5734 Error(ERR_WARN, "unknown DC level file '%s' -- using empty level",
5740 if (strPrefix(magic_bytes, "DC2Win95") ||
5741 strPrefix(magic_bytes, "DC2Win98"))
5743 int position_first_level = 0x00fa;
5744 int extra_bytes = 4;
5747 /* advance file stream to first level inside the level package */
5748 skip_bytes = position_first_level - num_magic_bytes - extra_bytes;
5750 /* each block of level data is followed by block of non-level data */
5751 num_levels_to_skip *= 2;
5753 /* at least skip header bytes, therefore use ">= 0" instead of "> 0" */
5754 while (num_levels_to_skip >= 0)
5756 /* advance file stream to next level inside the level package */
5757 if (seekFile(file, skip_bytes, SEEK_CUR) != 0)
5759 level->no_valid_file = TRUE;
5761 Error(ERR_WARN, "cannot fseek in file '%s' -- using empty level",
5767 /* skip apparently unused extra bytes following each level */
5768 ReadUnusedBytesFromFile(file, extra_bytes);
5770 /* read size of next level in level package */
5771 skip_bytes = getFile32BitLE(file);
5773 num_levels_to_skip--;
5778 level->no_valid_file = TRUE;
5780 Error(ERR_WARN, "unknown DC2 level file '%s' -- using empty level",
5787 LoadLevelFromFileStream_DC(file, level, level_file_info->nr);
5793 /* ------------------------------------------------------------------------- */
5794 /* functions for loading SB level */
5795 /* ------------------------------------------------------------------------- */
5797 int getMappedElement_SB(int element_ascii, boolean use_ces)
5805 sb_element_mapping[] =
5807 { ' ', EL_EMPTY, EL_CUSTOM_1 }, /* floor (space) */
5808 { '#', EL_STEELWALL, EL_CUSTOM_2 }, /* wall */
5809 { '@', EL_PLAYER_1, EL_CUSTOM_3 }, /* player */
5810 { '$', EL_SOKOBAN_OBJECT, EL_CUSTOM_4 }, /* box */
5811 { '.', EL_SOKOBAN_FIELD_EMPTY, EL_CUSTOM_5 }, /* goal square */
5812 { '*', EL_SOKOBAN_FIELD_FULL, EL_CUSTOM_6 }, /* box on goal square */
5813 { '+', EL_SOKOBAN_FIELD_PLAYER, EL_CUSTOM_7 }, /* player on goal square */
5814 { '_', EL_INVISIBLE_STEELWALL, EL_FROM_LEVEL_TEMPLATE }, /* floor beyond border */
5821 for (i = 0; sb_element_mapping[i].ascii != 0; i++)
5822 if (element_ascii == sb_element_mapping[i].ascii)
5823 return (use_ces ? sb_element_mapping[i].ce : sb_element_mapping[i].sb);
5825 return EL_UNDEFINED;
5828 static void LoadLevelFromFileInfo_SB(struct LevelInfo *level,
5829 struct LevelFileInfo *level_file_info,
5830 boolean level_info_only)
5832 char *filename = level_file_info->filename;
5833 char line[MAX_LINE_LEN], line_raw[MAX_LINE_LEN], previous_line[MAX_LINE_LEN];
5834 char last_comment[MAX_LINE_LEN];
5835 char level_name[MAX_LINE_LEN];
5838 int num_levels_to_skip = level_file_info->nr - leveldir_current->first_level;
5839 boolean read_continued_line = FALSE;
5840 boolean reading_playfield = FALSE;
5841 boolean got_valid_playfield_line = FALSE;
5842 boolean invalid_playfield_char = FALSE;
5843 boolean load_xsb_to_ces = check_special_flags("load_xsb_to_ces");
5844 int file_level_nr = 0;
5846 int x = 0, y = 0; /* initialized to make compilers happy */
5848 last_comment[0] = '\0';
5849 level_name[0] = '\0';
5851 if (!(file = openFile(filename, MODE_READ)))
5853 level->no_valid_file = TRUE;
5855 if (!level_info_only)
5856 Error(ERR_WARN, "cannot read level '%s' -- using empty level", filename);
5861 while (!checkEndOfFile(file))
5863 /* level successfully read, but next level may follow here */
5864 if (!got_valid_playfield_line && reading_playfield)
5866 /* read playfield from single level file -- skip remaining file */
5867 if (!level_file_info->packed)
5870 if (file_level_nr >= num_levels_to_skip)
5875 last_comment[0] = '\0';
5876 level_name[0] = '\0';
5878 reading_playfield = FALSE;
5881 got_valid_playfield_line = FALSE;
5883 /* read next line of input file */
5884 if (!getStringFromFile(file, line, MAX_LINE_LEN))
5887 /* check if line was completely read and is terminated by line break */
5888 if (strlen(line) > 0 && line[strlen(line) - 1] == '\n')
5891 /* cut trailing line break (this can be newline and/or carriage return) */
5892 for (line_ptr = &line[strlen(line)]; line_ptr >= line; line_ptr--)
5893 if ((*line_ptr == '\n' || *line_ptr == '\r') && *(line_ptr + 1) == '\0')
5896 /* copy raw input line for later use (mainly debugging output) */
5897 strcpy(line_raw, line);
5899 if (read_continued_line)
5901 /* append new line to existing line, if there is enough space */
5902 if (strlen(previous_line) + strlen(line_ptr) < MAX_LINE_LEN)
5903 strcat(previous_line, line_ptr);
5905 strcpy(line, previous_line); /* copy storage buffer to line */
5907 read_continued_line = FALSE;
5910 /* if the last character is '\', continue at next line */
5911 if (strlen(line) > 0 && line[strlen(line) - 1] == '\\')
5913 line[strlen(line) - 1] = '\0'; /* cut off trailing backslash */
5914 strcpy(previous_line, line); /* copy line to storage buffer */
5916 read_continued_line = TRUE;
5921 /* skip empty lines */
5922 if (line[0] == '\0')
5925 /* extract comment text from comment line */
5928 for (line_ptr = line; *line_ptr; line_ptr++)
5929 if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != ';')
5932 strcpy(last_comment, line_ptr);
5937 /* extract level title text from line containing level title */
5938 if (line[0] == '\'')
5940 strcpy(level_name, &line[1]);
5942 if (strlen(level_name) > 0 && level_name[strlen(level_name) - 1] == '\'')
5943 level_name[strlen(level_name) - 1] = '\0';
5948 /* skip lines containing only spaces (or empty lines) */
5949 for (line_ptr = line; *line_ptr; line_ptr++)
5950 if (*line_ptr != ' ')
5952 if (*line_ptr == '\0')
5955 /* at this point, we have found a line containing part of a playfield */
5957 got_valid_playfield_line = TRUE;
5959 if (!reading_playfield)
5961 reading_playfield = TRUE;
5962 invalid_playfield_char = FALSE;
5964 for (x = 0; x < MAX_LEV_FIELDX; x++)
5965 for (y = 0; y < MAX_LEV_FIELDY; y++)
5966 level->field[x][y] = getMappedElement_SB(' ', load_xsb_to_ces);
5971 /* start with topmost tile row */
5975 /* skip playfield line if larger row than allowed */
5976 if (y >= MAX_LEV_FIELDY)
5979 /* start with leftmost tile column */
5982 /* read playfield elements from line */
5983 for (line_ptr = line; *line_ptr; line_ptr++)
5985 int mapped_sb_element = getMappedElement_SB(*line_ptr, load_xsb_to_ces);
5987 /* stop parsing playfield line if larger column than allowed */
5988 if (x >= MAX_LEV_FIELDX)
5991 if (mapped_sb_element == EL_UNDEFINED)
5993 invalid_playfield_char = TRUE;
5998 level->field[x][y] = mapped_sb_element;
6000 /* continue with next tile column */
6003 level->fieldx = MAX(x, level->fieldx);
6006 if (invalid_playfield_char)
6008 /* if first playfield line, treat invalid lines as comment lines */
6010 reading_playfield = FALSE;
6015 /* continue with next tile row */
6023 level->fieldx = MIN(MAX(MIN_LEV_FIELDX, level->fieldx), MAX_LEV_FIELDX);
6024 level->fieldy = MIN(MAX(MIN_LEV_FIELDY, level->fieldy), MAX_LEV_FIELDY);
6026 if (!reading_playfield)
6028 level->no_valid_file = TRUE;
6030 Error(ERR_WARN, "cannot read level '%s' -- using empty level", filename);
6035 if (*level_name != '\0')
6037 strncpy(level->name, level_name, MAX_LEVEL_NAME_LEN);
6038 level->name[MAX_LEVEL_NAME_LEN] = '\0';
6040 else if (*last_comment != '\0')
6042 strncpy(level->name, last_comment, MAX_LEVEL_NAME_LEN);
6043 level->name[MAX_LEVEL_NAME_LEN] = '\0';
6047 sprintf(level->name, "--> Level %d <--", level_file_info->nr);
6050 /* set all empty fields beyond the border walls to invisible steel wall */
6051 for (y = 0; y < level->fieldy; y++) for (x = 0; x < level->fieldx; x++)
6053 if ((x == 0 || x == level->fieldx - 1 ||
6054 y == 0 || y == level->fieldy - 1) &&
6055 level->field[x][y] == getMappedElement_SB(' ', load_xsb_to_ces))
6056 FloodFillLevel(x, y, getMappedElement_SB('_', load_xsb_to_ces),
6057 level->field, level->fieldx, level->fieldy);
6060 /* set special level settings for Sokoban levels */
6063 level->use_step_counter = TRUE;
6065 if (load_xsb_to_ces)
6067 /* special global settings can now be set in level template */
6069 level->use_custom_template = TRUE;
6074 /* ------------------------------------------------------------------------- */
6075 /* functions for handling native levels */
6076 /* ------------------------------------------------------------------------- */
6078 static void LoadLevelFromFileInfo_EM(struct LevelInfo *level,
6079 struct LevelFileInfo *level_file_info,
6080 boolean level_info_only)
6082 if (!LoadNativeLevel_EM(level_file_info->filename, level_info_only))
6083 level->no_valid_file = TRUE;
6086 static void LoadLevelFromFileInfo_SP(struct LevelInfo *level,
6087 struct LevelFileInfo *level_file_info,
6088 boolean level_info_only)
6092 /* determine position of requested level inside level package */
6093 if (level_file_info->packed)
6094 pos = level_file_info->nr - leveldir_current->first_level;
6096 if (!LoadNativeLevel_SP(level_file_info->filename, pos, level_info_only))
6097 level->no_valid_file = TRUE;
6100 static void LoadLevelFromFileInfo_MM(struct LevelInfo *level,
6101 struct LevelFileInfo *level_file_info,
6102 boolean level_info_only)
6104 if (!LoadNativeLevel_MM(level_file_info->filename, level_info_only))
6105 level->no_valid_file = TRUE;
6108 void CopyNativeLevel_RND_to_Native(struct LevelInfo *level)
6110 if (level->game_engine_type == GAME_ENGINE_TYPE_EM)
6111 CopyNativeLevel_RND_to_EM(level);
6112 else if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
6113 CopyNativeLevel_RND_to_SP(level);
6114 else if (level->game_engine_type == GAME_ENGINE_TYPE_MM)
6115 CopyNativeLevel_RND_to_MM(level);
6118 void CopyNativeLevel_Native_to_RND(struct LevelInfo *level)
6120 if (level->game_engine_type == GAME_ENGINE_TYPE_EM)
6121 CopyNativeLevel_EM_to_RND(level);
6122 else if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
6123 CopyNativeLevel_SP_to_RND(level);
6124 else if (level->game_engine_type == GAME_ENGINE_TYPE_MM)
6125 CopyNativeLevel_MM_to_RND(level);
6128 void SaveNativeLevel(struct LevelInfo *level)
6130 if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
6132 char *basename = getSingleLevelBasenameExt(level->file_info.nr, "sp");
6133 char *filename = getLevelFilenameFromBasename(basename);
6135 CopyNativeLevel_RND_to_SP(level);
6136 CopyNativeTape_RND_to_SP(level);
6138 SaveNativeLevel_SP(filename);
6143 /* ------------------------------------------------------------------------- */
6144 /* functions for loading generic level */
6145 /* ------------------------------------------------------------------------- */
6147 static void LoadLevelFromFileInfo(struct LevelInfo *level,
6148 struct LevelFileInfo *level_file_info,
6149 boolean level_info_only)
6151 /* always start with reliable default values */
6152 setLevelInfoToDefaults(level, level_info_only, TRUE);
6154 switch (level_file_info->type)
6156 case LEVEL_FILE_TYPE_RND:
6157 LoadLevelFromFileInfo_RND(level, level_file_info, level_info_only);
6160 case LEVEL_FILE_TYPE_EM:
6161 LoadLevelFromFileInfo_EM(level, level_file_info, level_info_only);
6162 level->game_engine_type = GAME_ENGINE_TYPE_EM;
6165 case LEVEL_FILE_TYPE_SP:
6166 LoadLevelFromFileInfo_SP(level, level_file_info, level_info_only);
6167 level->game_engine_type = GAME_ENGINE_TYPE_SP;
6170 case LEVEL_FILE_TYPE_MM:
6171 LoadLevelFromFileInfo_MM(level, level_file_info, level_info_only);
6172 level->game_engine_type = GAME_ENGINE_TYPE_MM;
6175 case LEVEL_FILE_TYPE_DC:
6176 LoadLevelFromFileInfo_DC(level, level_file_info, level_info_only);
6179 case LEVEL_FILE_TYPE_SB:
6180 LoadLevelFromFileInfo_SB(level, level_file_info, level_info_only);
6184 LoadLevelFromFileInfo_RND(level, level_file_info, level_info_only);
6188 /* if level file is invalid, restore level structure to default values */
6189 if (level->no_valid_file)
6190 setLevelInfoToDefaults(level, level_info_only, FALSE);
6192 if (level->game_engine_type == GAME_ENGINE_TYPE_UNKNOWN)
6193 level->game_engine_type = GAME_ENGINE_TYPE_RND;
6195 if (level_file_info->type != LEVEL_FILE_TYPE_RND)
6196 CopyNativeLevel_Native_to_RND(level);
6199 void LoadLevelFromFilename(struct LevelInfo *level, char *filename)
6201 static struct LevelFileInfo level_file_info;
6203 /* always start with reliable default values */
6204 setFileInfoToDefaults(&level_file_info);
6206 level_file_info.nr = 0; /* unknown level number */
6207 level_file_info.type = LEVEL_FILE_TYPE_RND; /* no others supported yet */
6209 setString(&level_file_info.filename, filename);
6211 LoadLevelFromFileInfo(level, &level_file_info, FALSE);
6214 static void LoadLevel_InitVersion(struct LevelInfo *level)
6218 if (leveldir_current == NULL) /* only when dumping level */
6221 /* all engine modifications also valid for levels which use latest engine */
6222 if (level->game_version < VERSION_IDENT(3,2,0,5))
6224 /* time bonus score was given for 10 s instead of 1 s before 3.2.0-5 */
6225 level->score[SC_TIME_BONUS] /= 10;
6228 if (leveldir_current->latest_engine)
6230 /* ---------- use latest game engine ----------------------------------- */
6232 /* For all levels which are forced to use the latest game engine version
6233 (normally all but user contributed, private and undefined levels), set
6234 the game engine version to the actual version; this allows for actual
6235 corrections in the game engine to take effect for existing, converted
6236 levels (from "classic" or other existing games) to make the emulation
6237 of the corresponding game more accurate, while (hopefully) not breaking
6238 existing levels created from other players. */
6240 level->game_version = GAME_VERSION_ACTUAL;
6242 /* Set special EM style gems behaviour: EM style gems slip down from
6243 normal, steel and growing wall. As this is a more fundamental change,
6244 it seems better to set the default behaviour to "off" (as it is more
6245 natural) and make it configurable in the level editor (as a property
6246 of gem style elements). Already existing converted levels (neither
6247 private nor contributed levels) are changed to the new behaviour. */
6249 if (level->file_version < FILE_VERSION_2_0)
6250 level->em_slippery_gems = TRUE;
6255 /* ---------- use game engine the level was created with ----------------- */
6257 /* For all levels which are not forced to use the latest game engine
6258 version (normally user contributed, private and undefined levels),
6259 use the version of the game engine the levels were created for.
6261 Since 2.0.1, the game engine version is now directly stored
6262 in the level file (chunk "VERS"), so there is no need anymore
6263 to set the game version from the file version (except for old,
6264 pre-2.0 levels, where the game version is still taken from the
6265 file format version used to store the level -- see above). */
6267 /* player was faster than enemies in 1.0.0 and before */
6268 if (level->file_version == FILE_VERSION_1_0)
6269 for (i = 0; i < MAX_PLAYERS; i++)
6270 level->initial_player_stepsize[i] = STEPSIZE_FAST;
6272 /* default behaviour for EM style gems was "slippery" only in 2.0.1 */
6273 if (level->game_version == VERSION_IDENT(2,0,1,0))
6274 level->em_slippery_gems = TRUE;
6276 /* springs could be pushed over pits before (pre-release version) 2.2.0 */
6277 if (level->game_version < VERSION_IDENT(2,2,0,0))
6278 level->use_spring_bug = TRUE;
6280 if (level->game_version < VERSION_IDENT(3,2,0,5))
6282 /* time orb caused limited time in endless time levels before 3.2.0-5 */
6283 level->use_time_orb_bug = TRUE;
6285 /* default behaviour for snapping was "no snap delay" before 3.2.0-5 */
6286 level->block_snap_field = FALSE;
6288 /* extra time score was same value as time left score before 3.2.0-5 */
6289 level->extra_time_score = level->score[SC_TIME_BONUS];
6292 if (level->game_version < VERSION_IDENT(3,2,0,7))
6294 /* default behaviour for snapping was "not continuous" before 3.2.0-7 */
6295 level->continuous_snapping = FALSE;
6298 /* only few elements were able to actively move into acid before 3.1.0 */
6299 /* trigger settings did not exist before 3.1.0; set to default "any" */
6300 if (level->game_version < VERSION_IDENT(3,1,0,0))
6302 /* correct "can move into acid" settings (all zero in old levels) */
6304 level->can_move_into_acid_bits = 0; /* nothing can move into acid */
6305 level->dont_collide_with_bits = 0; /* nothing is deadly when colliding */
6307 setMoveIntoAcidProperty(level, EL_ROBOT, TRUE);
6308 setMoveIntoAcidProperty(level, EL_SATELLITE, TRUE);
6309 setMoveIntoAcidProperty(level, EL_PENGUIN, TRUE);
6310 setMoveIntoAcidProperty(level, EL_BALLOON, TRUE);
6312 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6313 SET_PROPERTY(EL_CUSTOM_START + i, EP_CAN_MOVE_INTO_ACID, TRUE);
6315 /* correct trigger settings (stored as zero == "none" in old levels) */
6317 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6319 int element = EL_CUSTOM_START + i;
6320 struct ElementInfo *ei = &element_info[element];
6322 for (j = 0; j < ei->num_change_pages; j++)
6324 struct ElementChangeInfo *change = &ei->change_page[j];
6326 change->trigger_player = CH_PLAYER_ANY;
6327 change->trigger_page = CH_PAGE_ANY;
6332 /* try to detect and fix "Snake Bite" levels, which are broken with 3.2.0 */
6334 int element = EL_CUSTOM_256;
6335 struct ElementInfo *ei = &element_info[element];
6336 struct ElementChangeInfo *change = &ei->change_page[0];
6338 /* This is needed to fix a problem that was caused by a bugfix in function
6339 game.c/CreateFieldExt() introduced with 3.2.0 that corrects the behaviour
6340 when a custom element changes to EL_SOKOBAN_FIELD_PLAYER (before, it did
6341 not replace walkable elements, but instead just placed the player on it,
6342 without placing the Sokoban field under the player). Unfortunately, this
6343 breaks "Snake Bite" style levels when the snake is halfway through a door
6344 that just closes (the snake head is still alive and can be moved in this
6345 case). This can be fixed by replacing the EL_SOKOBAN_FIELD_PLAYER by the
6346 player (without Sokoban element) which then gets killed as designed). */
6348 if ((strncmp(leveldir_current->identifier, "snake_bite", 10) == 0 ||
6349 strncmp(ei->description, "pause b4 death", 14) == 0) &&
6350 change->target_element == EL_SOKOBAN_FIELD_PLAYER)
6351 change->target_element = EL_PLAYER_1;
6354 /* try to detect and fix "Zelda" style levels, which are broken with 3.2.5 */
6355 if (level->game_version < VERSION_IDENT(3,2,5,0))
6357 /* This is needed to fix a problem that was caused by a bugfix in function
6358 game.c/CheckTriggeredElementChangeExt() introduced with 3.2.5 that
6359 corrects the behaviour when a custom element changes to another custom
6360 element with a higher element number that has change actions defined.
6361 Normally, only one change per frame is allowed for custom elements.
6362 Therefore, it is checked if a custom element already changed in the
6363 current frame; if it did, subsequent changes are suppressed.
6364 Unfortunately, this is only checked for element changes, but not for
6365 change actions, which are still executed. As the function above loops
6366 through all custom elements from lower to higher, an element change
6367 resulting in a lower CE number won't be checked again, while a target
6368 element with a higher number will also be checked, and potential change
6369 actions will get executed for this CE, too (which is wrong), while
6370 further changes are ignored (which is correct). As this bugfix breaks
6371 Zelda II (and introduces graphical bugs to Zelda I, and also breaks a
6372 few other levels like Alan Bond's "FMV"), allow the previous, incorrect
6373 behaviour for existing levels and tapes that make use of this bug */
6375 level->use_action_after_change_bug = TRUE;
6378 /* not centering level after relocating player was default only in 3.2.3 */
6379 if (level->game_version == VERSION_IDENT(3,2,3,0)) /* (no pre-releases) */
6380 level->shifted_relocation = TRUE;
6382 /* EM style elements always chain-exploded in R'n'D engine before 3.2.6 */
6383 if (level->game_version < VERSION_IDENT(3,2,6,0))
6384 level->em_explodes_by_fire = TRUE;
6387 static void LoadLevel_InitStandardElements(struct LevelInfo *level)
6391 /* map elements that have changed in newer versions */
6392 level->amoeba_content = getMappedElementByVersion(level->amoeba_content,
6393 level->game_version);
6394 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
6395 for (x = 0; x < 3; x++)
6396 for (y = 0; y < 3; y++)
6397 level->yamyam_content[i].e[x][y] =
6398 getMappedElementByVersion(level->yamyam_content[i].e[x][y],
6399 level->game_version);
6403 static void LoadLevel_InitCustomElements(struct LevelInfo *level)
6407 /* map custom element change events that have changed in newer versions
6408 (these following values were accidentally changed in version 3.0.1)
6409 (this seems to be needed only for 'ab_levelset3' and 'ab_levelset4') */
6410 if (level->game_version <= VERSION_IDENT(3,0,0,0))
6412 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6414 int element = EL_CUSTOM_START + i;
6416 /* order of checking and copying events to be mapped is important */
6417 /* (do not change the start and end value -- they are constant) */
6418 for (j = CE_BY_OTHER_ACTION; j >= CE_VALUE_GETS_ZERO; j--)
6420 if (HAS_CHANGE_EVENT(element, j - 2))
6422 SET_CHANGE_EVENT(element, j - 2, FALSE);
6423 SET_CHANGE_EVENT(element, j, TRUE);
6427 /* order of checking and copying events to be mapped is important */
6428 /* (do not change the start and end value -- they are constant) */
6429 for (j = CE_PLAYER_COLLECTS_X; j >= CE_HITTING_SOMETHING; j--)
6431 if (HAS_CHANGE_EVENT(element, j - 1))
6433 SET_CHANGE_EVENT(element, j - 1, FALSE);
6434 SET_CHANGE_EVENT(element, j, TRUE);
6440 /* initialize "can_change" field for old levels with only one change page */
6441 if (level->game_version <= VERSION_IDENT(3,0,2,0))
6443 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6445 int element = EL_CUSTOM_START + i;
6447 if (CAN_CHANGE(element))
6448 element_info[element].change->can_change = TRUE;
6452 /* correct custom element values (for old levels without these options) */
6453 if (level->game_version < VERSION_IDENT(3,1,1,0))
6455 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6457 int element = EL_CUSTOM_START + i;
6458 struct ElementInfo *ei = &element_info[element];
6460 if (ei->access_direction == MV_NO_DIRECTION)
6461 ei->access_direction = MV_ALL_DIRECTIONS;
6465 /* correct custom element values (fix invalid values for all versions) */
6468 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6470 int element = EL_CUSTOM_START + i;
6471 struct ElementInfo *ei = &element_info[element];
6473 for (j = 0; j < ei->num_change_pages; j++)
6475 struct ElementChangeInfo *change = &ei->change_page[j];
6477 if (change->trigger_player == CH_PLAYER_NONE)
6478 change->trigger_player = CH_PLAYER_ANY;
6480 if (change->trigger_side == CH_SIDE_NONE)
6481 change->trigger_side = CH_SIDE_ANY;
6486 /* initialize "can_explode" field for old levels which did not store this */
6487 /* !!! CHECK THIS -- "<= 3,1,0,0" IS PROBABLY WRONG !!! */
6488 if (level->game_version <= VERSION_IDENT(3,1,0,0))
6490 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6492 int element = EL_CUSTOM_START + i;
6494 if (EXPLODES_1X1_OLD(element))
6495 element_info[element].explosion_type = EXPLODES_1X1;
6497 SET_PROPERTY(element, EP_CAN_EXPLODE, (EXPLODES_BY_FIRE(element) ||
6498 EXPLODES_SMASHED(element) ||
6499 EXPLODES_IMPACT(element)));
6503 /* correct previously hard-coded move delay values for maze runner style */
6504 if (level->game_version < VERSION_IDENT(3,1,1,0))
6506 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6508 int element = EL_CUSTOM_START + i;
6510 if (element_info[element].move_pattern & MV_MAZE_RUNNER_STYLE)
6512 /* previously hard-coded and therefore ignored */
6513 element_info[element].move_delay_fixed = 9;
6514 element_info[element].move_delay_random = 0;
6519 /* set some other uninitialized values of custom elements in older levels */
6520 if (level->game_version < VERSION_IDENT(3,1,0,0))
6522 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6524 int element = EL_CUSTOM_START + i;
6526 element_info[element].access_direction = MV_ALL_DIRECTIONS;
6528 element_info[element].explosion_delay = 17;
6529 element_info[element].ignition_delay = 8;
6534 static void LoadLevel_InitElements(struct LevelInfo *level)
6536 LoadLevel_InitStandardElements(level);
6538 if (level->file_has_custom_elements)
6539 LoadLevel_InitCustomElements(level);
6541 /* initialize element properties for level editor etc. */
6542 InitElementPropertiesEngine(level->game_version);
6543 InitElementPropertiesGfxElement();
6546 static void LoadLevel_InitPlayfield(struct LevelInfo *level)
6550 /* map elements that have changed in newer versions */
6551 for (y = 0; y < level->fieldy; y++)
6552 for (x = 0; x < level->fieldx; x++)
6553 level->field[x][y] = getMappedElementByVersion(level->field[x][y],
6554 level->game_version);
6556 /* clear unused playfield data (nicer if level gets resized in editor) */
6557 for (x = 0; x < MAX_LEV_FIELDX; x++)
6558 for (y = 0; y < MAX_LEV_FIELDY; y++)
6559 if (x >= level->fieldx || y >= level->fieldy)
6560 level->field[x][y] = EL_EMPTY;
6562 /* copy elements to runtime playfield array */
6563 for (x = 0; x < MAX_LEV_FIELDX; x++)
6564 for (y = 0; y < MAX_LEV_FIELDY; y++)
6565 Feld[x][y] = level->field[x][y];
6567 /* initialize level size variables for faster access */
6568 lev_fieldx = level->fieldx;
6569 lev_fieldy = level->fieldy;
6571 /* determine border element for this level */
6572 if (level->file_info.type == LEVEL_FILE_TYPE_DC)
6573 BorderElement = EL_EMPTY; /* (in editor, SetBorderElement() is used) */
6578 static void LoadLevel_InitNativeEngines(struct LevelInfo *level)
6580 struct LevelFileInfo *level_file_info = &level->file_info;
6582 if (level_file_info->type == LEVEL_FILE_TYPE_RND)
6583 CopyNativeLevel_RND_to_Native(level);
6586 void LoadLevelTemplate(int nr)
6588 if (!fileExists(getGlobalLevelTemplateFilename()))
6590 Error(ERR_WARN, "no level template found for this level");
6595 setLevelFileInfo(&level_template.file_info, nr);
6597 LoadLevelFromFileInfo(&level_template, &level_template.file_info, FALSE);
6599 LoadLevel_InitVersion(&level_template);
6600 LoadLevel_InitElements(&level_template);
6602 ActivateLevelTemplate();
6605 void LoadLevel(int nr)
6607 setLevelFileInfo(&level.file_info, nr);
6609 LoadLevelFromFileInfo(&level, &level.file_info, FALSE);
6611 if (level.use_custom_template)
6612 LoadLevelTemplate(-1);
6614 LoadLevel_InitVersion(&level);
6615 LoadLevel_InitElements(&level);
6616 LoadLevel_InitPlayfield(&level);
6618 LoadLevel_InitNativeEngines(&level);
6621 void LoadLevelInfoOnly(int nr)
6623 setLevelFileInfo(&level.file_info, nr);
6625 LoadLevelFromFileInfo(&level, &level.file_info, TRUE);
6628 static int SaveLevel_VERS(FILE *file, struct LevelInfo *level)
6632 chunk_size += putFileVersion(file, level->file_version);
6633 chunk_size += putFileVersion(file, level->game_version);
6638 static int SaveLevel_DATE(FILE *file, struct LevelInfo *level)
6642 chunk_size += putFile16BitBE(file, level->creation_date.year);
6643 chunk_size += putFile8Bit(file, level->creation_date.month);
6644 chunk_size += putFile8Bit(file, level->creation_date.day);
6649 #if ENABLE_HISTORIC_CHUNKS
6650 static void SaveLevel_HEAD(FILE *file, struct LevelInfo *level)
6654 putFile8Bit(file, level->fieldx);
6655 putFile8Bit(file, level->fieldy);
6657 putFile16BitBE(file, level->time);
6658 putFile16BitBE(file, level->gems_needed);
6660 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
6661 putFile8Bit(file, level->name[i]);
6663 for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
6664 putFile8Bit(file, level->score[i]);
6666 for (i = 0; i < STD_ELEMENT_CONTENTS; i++)
6667 for (y = 0; y < 3; y++)
6668 for (x = 0; x < 3; x++)
6669 putFile8Bit(file, (level->encoding_16bit_yamyam ? EL_EMPTY :
6670 level->yamyam_content[i].e[x][y]));
6671 putFile8Bit(file, level->amoeba_speed);
6672 putFile8Bit(file, level->time_magic_wall);
6673 putFile8Bit(file, level->time_wheel);
6674 putFile8Bit(file, (level->encoding_16bit_amoeba ? EL_EMPTY :
6675 level->amoeba_content));
6676 putFile8Bit(file, (level->initial_player_stepsize == STEPSIZE_FAST ? 1 : 0));
6677 putFile8Bit(file, (level->initial_gravity ? 1 : 0));
6678 putFile8Bit(file, (level->encoding_16bit_field ? 1 : 0));
6679 putFile8Bit(file, (level->em_slippery_gems ? 1 : 0));
6681 putFile8Bit(file, (level->use_custom_template ? 1 : 0));
6683 putFile8Bit(file, (level->block_last_field ? 1 : 0));
6684 putFile8Bit(file, (level->sp_block_last_field ? 1 : 0));
6685 putFile32BitBE(file, level->can_move_into_acid_bits);
6686 putFile8Bit(file, level->dont_collide_with_bits);
6688 putFile8Bit(file, (level->use_spring_bug ? 1 : 0));
6689 putFile8Bit(file, (level->use_step_counter ? 1 : 0));
6691 putFile8Bit(file, (level->instant_relocation ? 1 : 0));
6692 putFile8Bit(file, (level->can_pass_to_walkable ? 1 : 0));
6693 putFile8Bit(file, (level->grow_into_diggable ? 1 : 0));
6695 putFile8Bit(file, level->game_engine_type);
6697 WriteUnusedBytesToFile(file, LEVEL_CHUNK_HEAD_UNUSED);
6701 static int SaveLevel_NAME(FILE *file, struct LevelInfo *level)
6706 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
6707 chunk_size += putFile8Bit(file, level->name[i]);
6712 static int SaveLevel_AUTH(FILE *file, struct LevelInfo *level)
6717 for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
6718 chunk_size += putFile8Bit(file, level->author[i]);
6723 #if ENABLE_HISTORIC_CHUNKS
6724 static int SaveLevel_BODY(FILE *file, struct LevelInfo *level)
6729 for (y = 0; y < level->fieldy; y++)
6730 for (x = 0; x < level->fieldx; x++)
6731 if (level->encoding_16bit_field)
6732 chunk_size += putFile16BitBE(file, level->field[x][y]);
6734 chunk_size += putFile8Bit(file, level->field[x][y]);
6740 static int SaveLevel_BODY(FILE *file, struct LevelInfo *level)
6745 for (y = 0; y < level->fieldy; y++)
6746 for (x = 0; x < level->fieldx; x++)
6747 chunk_size += putFile16BitBE(file, level->field[x][y]);
6752 #if ENABLE_HISTORIC_CHUNKS
6753 static void SaveLevel_CONT(FILE *file, struct LevelInfo *level)
6757 putFile8Bit(file, EL_YAMYAM);
6758 putFile8Bit(file, level->num_yamyam_contents);
6759 putFile8Bit(file, 0);
6760 putFile8Bit(file, 0);
6762 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
6763 for (y = 0; y < 3; y++)
6764 for (x = 0; x < 3; x++)
6765 if (level->encoding_16bit_field)
6766 putFile16BitBE(file, level->yamyam_content[i].e[x][y]);
6768 putFile8Bit(file, level->yamyam_content[i].e[x][y]);
6772 #if ENABLE_HISTORIC_CHUNKS
6773 static void SaveLevel_CNT2(FILE *file, struct LevelInfo *level, int element)
6776 int num_contents, content_xsize, content_ysize;
6777 int content_array[MAX_ELEMENT_CONTENTS][3][3];
6779 if (element == EL_YAMYAM)
6781 num_contents = level->num_yamyam_contents;
6785 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
6786 for (y = 0; y < 3; y++)
6787 for (x = 0; x < 3; x++)
6788 content_array[i][x][y] = level->yamyam_content[i].e[x][y];
6790 else if (element == EL_BD_AMOEBA)
6796 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
6797 for (y = 0; y < 3; y++)
6798 for (x = 0; x < 3; x++)
6799 content_array[i][x][y] = EL_EMPTY;
6800 content_array[0][0][0] = level->amoeba_content;
6804 /* chunk header already written -- write empty chunk data */
6805 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_SIZE);
6807 Error(ERR_WARN, "cannot save content for element '%d'", element);
6811 putFile16BitBE(file, element);
6812 putFile8Bit(file, num_contents);
6813 putFile8Bit(file, content_xsize);
6814 putFile8Bit(file, content_ysize);
6816 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_UNUSED);
6818 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
6819 for (y = 0; y < 3; y++)
6820 for (x = 0; x < 3; x++)
6821 putFile16BitBE(file, content_array[i][x][y]);
6825 #if ENABLE_HISTORIC_CHUNKS
6826 static int SaveLevel_CNT3(FILE *file, struct LevelInfo *level, int element)
6828 int envelope_nr = element - EL_ENVELOPE_1;
6829 int envelope_len = strlen(level->envelope_text[envelope_nr]) + 1;
6833 chunk_size += putFile16BitBE(file, element);
6834 chunk_size += putFile16BitBE(file, envelope_len);
6835 chunk_size += putFile8Bit(file, level->envelope_xsize[envelope_nr]);
6836 chunk_size += putFile8Bit(file, level->envelope_ysize[envelope_nr]);
6838 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT3_UNUSED);
6839 chunk_size += LEVEL_CHUNK_CNT3_UNUSED;
6841 for (i = 0; i < envelope_len; i++)
6842 chunk_size += putFile8Bit(file, level->envelope_text[envelope_nr][i]);
6848 #if ENABLE_HISTORIC_CHUNKS
6849 static void SaveLevel_CUS1(FILE *file, struct LevelInfo *level,
6850 int num_changed_custom_elements)
6854 putFile16BitBE(file, num_changed_custom_elements);
6856 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6858 int element = EL_CUSTOM_START + i;
6860 struct ElementInfo *ei = &element_info[element];
6862 if (ei->properties[EP_BITFIELD_BASE_NR] != EP_BITMASK_DEFAULT)
6864 if (check < num_changed_custom_elements)
6866 putFile16BitBE(file, element);
6867 putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
6874 if (check != num_changed_custom_elements) /* should not happen */
6875 Error(ERR_WARN, "inconsistent number of custom element properties");
6879 #if ENABLE_HISTORIC_CHUNKS
6880 static void SaveLevel_CUS2(FILE *file, struct LevelInfo *level,
6881 int num_changed_custom_elements)
6885 putFile16BitBE(file, num_changed_custom_elements);
6887 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6889 int element = EL_CUSTOM_START + i;
6891 if (element_info[element].change->target_element != EL_EMPTY_SPACE)
6893 if (check < num_changed_custom_elements)
6895 putFile16BitBE(file, element);
6896 putFile16BitBE(file, element_info[element].change->target_element);
6903 if (check != num_changed_custom_elements) /* should not happen */
6904 Error(ERR_WARN, "inconsistent number of custom target elements");
6908 #if ENABLE_HISTORIC_CHUNKS
6909 static void SaveLevel_CUS3(FILE *file, struct LevelInfo *level,
6910 int num_changed_custom_elements)
6912 int i, j, x, y, check = 0;
6914 putFile16BitBE(file, num_changed_custom_elements);
6916 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6918 int element = EL_CUSTOM_START + i;
6919 struct ElementInfo *ei = &element_info[element];
6921 if (ei->modified_settings)
6923 if (check < num_changed_custom_elements)
6925 putFile16BitBE(file, element);
6927 for (j = 0; j < MAX_ELEMENT_NAME_LEN; j++)
6928 putFile8Bit(file, ei->description[j]);
6930 putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
6932 /* some free bytes for future properties and padding */
6933 WriteUnusedBytesToFile(file, 7);
6935 putFile8Bit(file, ei->use_gfx_element);
6936 putFile16BitBE(file, ei->gfx_element_initial);
6938 putFile8Bit(file, ei->collect_score_initial);
6939 putFile8Bit(file, ei->collect_count_initial);
6941 putFile16BitBE(file, ei->push_delay_fixed);
6942 putFile16BitBE(file, ei->push_delay_random);
6943 putFile16BitBE(file, ei->move_delay_fixed);
6944 putFile16BitBE(file, ei->move_delay_random);
6946 putFile16BitBE(file, ei->move_pattern);
6947 putFile8Bit(file, ei->move_direction_initial);
6948 putFile8Bit(file, ei->move_stepsize);
6950 for (y = 0; y < 3; y++)
6951 for (x = 0; x < 3; x++)
6952 putFile16BitBE(file, ei->content.e[x][y]);
6954 putFile32BitBE(file, ei->change->events);
6956 putFile16BitBE(file, ei->change->target_element);
6958 putFile16BitBE(file, ei->change->delay_fixed);
6959 putFile16BitBE(file, ei->change->delay_random);
6960 putFile16BitBE(file, ei->change->delay_frames);
6962 putFile16BitBE(file, ei->change->initial_trigger_element);
6964 putFile8Bit(file, ei->change->explode);
6965 putFile8Bit(file, ei->change->use_target_content);
6966 putFile8Bit(file, ei->change->only_if_complete);
6967 putFile8Bit(file, ei->change->use_random_replace);
6969 putFile8Bit(file, ei->change->random_percentage);
6970 putFile8Bit(file, ei->change->replace_when);
6972 for (y = 0; y < 3; y++)
6973 for (x = 0; x < 3; x++)
6974 putFile16BitBE(file, ei->change->content.e[x][y]);
6976 putFile8Bit(file, ei->slippery_type);
6978 /* some free bytes for future properties and padding */
6979 WriteUnusedBytesToFile(file, LEVEL_CPART_CUS3_UNUSED);
6986 if (check != num_changed_custom_elements) /* should not happen */
6987 Error(ERR_WARN, "inconsistent number of custom element properties");
6991 #if ENABLE_HISTORIC_CHUNKS
6992 static void SaveLevel_CUS4(FILE *file, struct LevelInfo *level, int element)
6994 struct ElementInfo *ei = &element_info[element];
6997 /* ---------- custom element base property values (96 bytes) ------------- */
6999 putFile16BitBE(file, element);
7001 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
7002 putFile8Bit(file, ei->description[i]);
7004 putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
7006 WriteUnusedBytesToFile(file, 4); /* reserved for more base properties */
7008 putFile8Bit(file, ei->num_change_pages);
7010 putFile16BitBE(file, ei->ce_value_fixed_initial);
7011 putFile16BitBE(file, ei->ce_value_random_initial);
7012 putFile8Bit(file, ei->use_last_ce_value);
7014 putFile8Bit(file, ei->use_gfx_element);
7015 putFile16BitBE(file, ei->gfx_element_initial);
7017 putFile8Bit(file, ei->collect_score_initial);
7018 putFile8Bit(file, ei->collect_count_initial);
7020 putFile8Bit(file, ei->drop_delay_fixed);
7021 putFile8Bit(file, ei->push_delay_fixed);
7022 putFile8Bit(file, ei->drop_delay_random);
7023 putFile8Bit(file, ei->push_delay_random);
7024 putFile16BitBE(file, ei->move_delay_fixed);
7025 putFile16BitBE(file, ei->move_delay_random);
7027 /* bits 0 - 15 of "move_pattern" ... */
7028 putFile16BitBE(file, ei->move_pattern & 0xffff);
7029 putFile8Bit(file, ei->move_direction_initial);
7030 putFile8Bit(file, ei->move_stepsize);
7032 putFile8Bit(file, ei->slippery_type);
7034 for (y = 0; y < 3; y++)
7035 for (x = 0; x < 3; x++)
7036 putFile16BitBE(file, ei->content.e[x][y]);
7038 putFile16BitBE(file, ei->move_enter_element);
7039 putFile16BitBE(file, ei->move_leave_element);
7040 putFile8Bit(file, ei->move_leave_type);
7042 /* ... bits 16 - 31 of "move_pattern" (not nice, but downward compatible) */
7043 putFile16BitBE(file, (ei->move_pattern >> 16) & 0xffff);
7045 putFile8Bit(file, ei->access_direction);
7047 putFile8Bit(file, ei->explosion_delay);
7048 putFile8Bit(file, ei->ignition_delay);
7049 putFile8Bit(file, ei->explosion_type);
7051 /* some free bytes for future custom property values and padding */
7052 WriteUnusedBytesToFile(file, 1);
7054 /* ---------- change page property values (48 bytes) --------------------- */
7056 for (i = 0; i < ei->num_change_pages; i++)
7058 struct ElementChangeInfo *change = &ei->change_page[i];
7059 unsigned int event_bits;
7061 /* bits 0 - 31 of "has_event[]" ... */
7063 for (j = 0; j < MIN(NUM_CHANGE_EVENTS, 32); j++)
7064 if (change->has_event[j])
7065 event_bits |= (1 << j);
7066 putFile32BitBE(file, event_bits);
7068 putFile16BitBE(file, change->target_element);
7070 putFile16BitBE(file, change->delay_fixed);
7071 putFile16BitBE(file, change->delay_random);
7072 putFile16BitBE(file, change->delay_frames);
7074 putFile16BitBE(file, change->initial_trigger_element);
7076 putFile8Bit(file, change->explode);
7077 putFile8Bit(file, change->use_target_content);
7078 putFile8Bit(file, change->only_if_complete);
7079 putFile8Bit(file, change->use_random_replace);
7081 putFile8Bit(file, change->random_percentage);
7082 putFile8Bit(file, change->replace_when);
7084 for (y = 0; y < 3; y++)
7085 for (x = 0; x < 3; x++)
7086 putFile16BitBE(file, change->target_content.e[x][y]);
7088 putFile8Bit(file, change->can_change);
7090 putFile8Bit(file, change->trigger_side);
7092 putFile8Bit(file, change->trigger_player);
7093 putFile8Bit(file, (change->trigger_page == CH_PAGE_ANY ? CH_PAGE_ANY_FILE :
7094 log_2(change->trigger_page)));
7096 putFile8Bit(file, change->has_action);
7097 putFile8Bit(file, change->action_type);
7098 putFile8Bit(file, change->action_mode);
7099 putFile16BitBE(file, change->action_arg);
7101 /* ... bits 32 - 39 of "has_event[]" (not nice, but downward compatible) */
7103 for (j = 32; j < NUM_CHANGE_EVENTS; j++)
7104 if (change->has_event[j])
7105 event_bits |= (1 << (j - 32));
7106 putFile8Bit(file, event_bits);
7111 #if ENABLE_HISTORIC_CHUNKS
7112 static void SaveLevel_GRP1(FILE *file, struct LevelInfo *level, int element)
7114 struct ElementInfo *ei = &element_info[element];
7115 struct ElementGroupInfo *group = ei->group;
7118 putFile16BitBE(file, element);
7120 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
7121 putFile8Bit(file, ei->description[i]);
7123 putFile8Bit(file, group->num_elements);
7125 putFile8Bit(file, ei->use_gfx_element);
7126 putFile16BitBE(file, ei->gfx_element_initial);
7128 putFile8Bit(file, group->choice_mode);
7130 /* some free bytes for future values and padding */
7131 WriteUnusedBytesToFile(file, 3);
7133 for (i = 0; i < MAX_ELEMENTS_IN_GROUP; i++)
7134 putFile16BitBE(file, group->element[i]);
7138 static int SaveLevel_MicroChunk(FILE *file, struct LevelFileConfigInfo *entry,
7139 boolean write_element)
7141 int save_type = entry->save_type;
7142 int data_type = entry->data_type;
7143 int conf_type = entry->conf_type;
7144 int byte_mask = conf_type & CONF_MASK_BYTES;
7145 int element = entry->element;
7146 int default_value = entry->default_value;
7148 boolean modified = FALSE;
7150 if (byte_mask != CONF_MASK_MULTI_BYTES)
7152 void *value_ptr = entry->value;
7153 int value = (data_type == TYPE_BOOLEAN ? *(boolean *)value_ptr :
7156 /* check if any settings have been modified before saving them */
7157 if (value != default_value)
7160 /* do not save if explicitly told or if unmodified default settings */
7161 if ((save_type == SAVE_CONF_NEVER) ||
7162 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
7166 num_bytes += putFile16BitBE(file, element);
7168 num_bytes += putFile8Bit(file, conf_type);
7169 num_bytes += (byte_mask == CONF_MASK_1_BYTE ? putFile8Bit (file, value) :
7170 byte_mask == CONF_MASK_2_BYTE ? putFile16BitBE(file, value) :
7171 byte_mask == CONF_MASK_4_BYTE ? putFile32BitBE(file, value) :
7174 else if (data_type == TYPE_STRING)
7176 char *default_string = entry->default_string;
7177 char *string = (char *)(entry->value);
7178 int string_length = strlen(string);
7181 /* check if any settings have been modified before saving them */
7182 if (!strEqual(string, default_string))
7185 /* do not save if explicitly told or if unmodified default settings */
7186 if ((save_type == SAVE_CONF_NEVER) ||
7187 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
7191 num_bytes += putFile16BitBE(file, element);
7193 num_bytes += putFile8Bit(file, conf_type);
7194 num_bytes += putFile16BitBE(file, string_length);
7196 for (i = 0; i < string_length; i++)
7197 num_bytes += putFile8Bit(file, string[i]);
7199 else if (data_type == TYPE_ELEMENT_LIST)
7201 int *element_array = (int *)(entry->value);
7202 int num_elements = *(int *)(entry->num_entities);
7205 /* check if any settings have been modified before saving them */
7206 for (i = 0; i < num_elements; i++)
7207 if (element_array[i] != default_value)
7210 /* do not save if explicitly told or if unmodified default settings */
7211 if ((save_type == SAVE_CONF_NEVER) ||
7212 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
7216 num_bytes += putFile16BitBE(file, element);
7218 num_bytes += putFile8Bit(file, conf_type);
7219 num_bytes += putFile16BitBE(file, num_elements * CONF_ELEMENT_NUM_BYTES);
7221 for (i = 0; i < num_elements; i++)
7222 num_bytes += putFile16BitBE(file, element_array[i]);
7224 else if (data_type == TYPE_CONTENT_LIST)
7226 struct Content *content = (struct Content *)(entry->value);
7227 int num_contents = *(int *)(entry->num_entities);
7230 /* check if any settings have been modified before saving them */
7231 for (i = 0; i < num_contents; i++)
7232 for (y = 0; y < 3; y++)
7233 for (x = 0; x < 3; x++)
7234 if (content[i].e[x][y] != default_value)
7237 /* do not save if explicitly told or if unmodified default settings */
7238 if ((save_type == SAVE_CONF_NEVER) ||
7239 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
7243 num_bytes += putFile16BitBE(file, element);
7245 num_bytes += putFile8Bit(file, conf_type);
7246 num_bytes += putFile16BitBE(file, num_contents * CONF_CONTENT_NUM_BYTES);
7248 for (i = 0; i < num_contents; i++)
7249 for (y = 0; y < 3; y++)
7250 for (x = 0; x < 3; x++)
7251 num_bytes += putFile16BitBE(file, content[i].e[x][y]);
7257 static int SaveLevel_INFO(FILE *file, struct LevelInfo *level)
7262 li = *level; /* copy level data into temporary buffer */
7264 for (i = 0; chunk_config_INFO[i].data_type != -1; i++)
7265 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_INFO[i], FALSE);
7270 static int SaveLevel_ELEM(FILE *file, struct LevelInfo *level)
7275 li = *level; /* copy level data into temporary buffer */
7277 for (i = 0; chunk_config_ELEM[i].data_type != -1; i++)
7278 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_ELEM[i], TRUE);
7283 static int SaveLevel_NOTE(FILE *file, struct LevelInfo *level, int element)
7285 int envelope_nr = element - EL_ENVELOPE_1;
7289 chunk_size += putFile16BitBE(file, element);
7291 /* copy envelope data into temporary buffer */
7292 xx_envelope = level->envelope[envelope_nr];
7294 for (i = 0; chunk_config_NOTE[i].data_type != -1; i++)
7295 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_NOTE[i], FALSE);
7300 static int SaveLevel_CUSX(FILE *file, struct LevelInfo *level, int element)
7302 struct ElementInfo *ei = &element_info[element];
7306 chunk_size += putFile16BitBE(file, element);
7308 xx_ei = *ei; /* copy element data into temporary buffer */
7310 /* set default description string for this specific element */
7311 strcpy(xx_default_description, getDefaultElementDescription(ei));
7313 for (i = 0; chunk_config_CUSX_base[i].data_type != -1; i++)
7314 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_CUSX_base[i], FALSE);
7316 for (i = 0; i < ei->num_change_pages; i++)
7318 struct ElementChangeInfo *change = &ei->change_page[i];
7320 xx_current_change_page = i;
7322 xx_change = *change; /* copy change data into temporary buffer */
7325 setEventBitsFromEventFlags(change);
7327 for (j = 0; chunk_config_CUSX_change[j].data_type != -1; j++)
7328 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_CUSX_change[j],
7335 static int SaveLevel_GRPX(FILE *file, struct LevelInfo *level, int element)
7337 struct ElementInfo *ei = &element_info[element];
7338 struct ElementGroupInfo *group = ei->group;
7342 chunk_size += putFile16BitBE(file, element);
7344 xx_ei = *ei; /* copy element data into temporary buffer */
7345 xx_group = *group; /* copy group data into temporary buffer */
7347 /* set default description string for this specific element */
7348 strcpy(xx_default_description, getDefaultElementDescription(ei));
7350 for (i = 0; chunk_config_GRPX[i].data_type != -1; i++)
7351 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_GRPX[i], FALSE);
7356 static void SaveLevelFromFilename(struct LevelInfo *level, char *filename,
7357 boolean save_as_template)
7363 if (!(file = fopen(filename, MODE_WRITE)))
7365 Error(ERR_WARN, "cannot save level file '%s'", filename);
7369 level->file_version = FILE_VERSION_ACTUAL;
7370 level->game_version = GAME_VERSION_ACTUAL;
7372 level->creation_date = getCurrentDate();
7374 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
7375 putFileChunkBE(file, "CAVE", CHUNK_SIZE_NONE);
7377 chunk_size = SaveLevel_VERS(NULL, level);
7378 putFileChunkBE(file, "VERS", chunk_size);
7379 SaveLevel_VERS(file, level);
7381 chunk_size = SaveLevel_DATE(NULL, level);
7382 putFileChunkBE(file, "DATE", chunk_size);
7383 SaveLevel_DATE(file, level);
7385 chunk_size = SaveLevel_NAME(NULL, level);
7386 putFileChunkBE(file, "NAME", chunk_size);
7387 SaveLevel_NAME(file, level);
7389 chunk_size = SaveLevel_AUTH(NULL, level);
7390 putFileChunkBE(file, "AUTH", chunk_size);
7391 SaveLevel_AUTH(file, level);
7393 chunk_size = SaveLevel_INFO(NULL, level);
7394 putFileChunkBE(file, "INFO", chunk_size);
7395 SaveLevel_INFO(file, level);
7397 chunk_size = SaveLevel_BODY(NULL, level);
7398 putFileChunkBE(file, "BODY", chunk_size);
7399 SaveLevel_BODY(file, level);
7401 chunk_size = SaveLevel_ELEM(NULL, level);
7402 if (chunk_size > LEVEL_CHUNK_ELEM_UNCHANGED) /* save if changed */
7404 putFileChunkBE(file, "ELEM", chunk_size);
7405 SaveLevel_ELEM(file, level);
7408 for (i = 0; i < NUM_ENVELOPES; i++)
7410 int element = EL_ENVELOPE_1 + i;
7412 chunk_size = SaveLevel_NOTE(NULL, level, element);
7413 if (chunk_size > LEVEL_CHUNK_NOTE_UNCHANGED) /* save if changed */
7415 putFileChunkBE(file, "NOTE", chunk_size);
7416 SaveLevel_NOTE(file, level, element);
7420 /* if not using template level, check for non-default custom/group elements */
7421 if (!level->use_custom_template || save_as_template)
7423 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7425 int element = EL_CUSTOM_START + i;
7427 chunk_size = SaveLevel_CUSX(NULL, level, element);
7428 if (chunk_size > LEVEL_CHUNK_CUSX_UNCHANGED) /* save if changed */
7430 putFileChunkBE(file, "CUSX", chunk_size);
7431 SaveLevel_CUSX(file, level, element);
7435 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
7437 int element = EL_GROUP_START + i;
7439 chunk_size = SaveLevel_GRPX(NULL, level, element);
7440 if (chunk_size > LEVEL_CHUNK_GRPX_UNCHANGED) /* save if changed */
7442 putFileChunkBE(file, "GRPX", chunk_size);
7443 SaveLevel_GRPX(file, level, element);
7450 SetFilePermissions(filename, PERMS_PRIVATE);
7453 void SaveLevel(int nr)
7455 char *filename = getDefaultLevelFilename(nr);
7457 SaveLevelFromFilename(&level, filename, FALSE);
7460 void SaveLevelTemplate()
7462 char *filename = getLocalLevelTemplateFilename();
7464 SaveLevelFromFilename(&level, filename, TRUE);
7467 boolean SaveLevelChecked(int nr)
7469 char *filename = getDefaultLevelFilename(nr);
7470 boolean new_level = !fileExists(filename);
7471 boolean level_saved = FALSE;
7473 if (new_level || Request("Save this level and kill the old?", REQ_ASK))
7478 Request("Level saved!", REQ_CONFIRM);
7486 void DumpLevel(struct LevelInfo *level)
7488 if (level->no_level_file || level->no_valid_file)
7490 Error(ERR_WARN, "cannot dump -- no valid level file found");
7496 Print("Level xxx (file version %08d, game version %08d)\n",
7497 level->file_version, level->game_version);
7500 Print("Level author: '%s'\n", level->author);
7501 Print("Level title: '%s'\n", level->name);
7503 Print("Playfield size: %d x %d\n", level->fieldx, level->fieldy);
7505 Print("Level time: %d seconds\n", level->time);
7506 Print("Gems needed: %d\n", level->gems_needed);
7508 Print("Time for magic wall: %d seconds\n", level->time_magic_wall);
7509 Print("Time for wheel: %d seconds\n", level->time_wheel);
7510 Print("Time for light: %d seconds\n", level->time_light);
7511 Print("Time for timegate: %d seconds\n", level->time_timegate);
7513 Print("Amoeba speed: %d\n", level->amoeba_speed);
7516 Print("EM style slippery gems: %s\n", (level->em_slippery_gems ? "yes" : "no"));
7517 Print("Player blocks last field: %s\n", (level->block_last_field ? "yes" : "no"));
7518 Print("SP player blocks last field: %s\n", (level->sp_block_last_field ? "yes" : "no"));
7519 Print("use spring bug: %s\n", (level->use_spring_bug ? "yes" : "no"));
7520 Print("use step counter: %s\n", (level->use_step_counter ? "yes" : "no"));
7526 /* ========================================================================= */
7527 /* tape file functions */
7528 /* ========================================================================= */
7530 static void setTapeInfoToDefaults()
7534 /* always start with reliable default values (empty tape) */
7537 /* default values (also for pre-1.2 tapes) with only the first player */
7538 tape.player_participates[0] = TRUE;
7539 for (i = 1; i < MAX_PLAYERS; i++)
7540 tape.player_participates[i] = FALSE;
7542 /* at least one (default: the first) player participates in every tape */
7543 tape.num_participating_players = 1;
7545 tape.level_nr = level_nr;
7547 tape.changed = FALSE;
7549 tape.recording = FALSE;
7550 tape.playing = FALSE;
7551 tape.pausing = FALSE;
7553 tape.no_valid_file = FALSE;
7556 static int LoadTape_VERS(File *file, int chunk_size, struct TapeInfo *tape)
7558 tape->file_version = getFileVersion(file);
7559 tape->game_version = getFileVersion(file);
7564 static int LoadTape_HEAD(File *file, int chunk_size, struct TapeInfo *tape)
7568 tape->random_seed = getFile32BitBE(file);
7569 tape->date = getFile32BitBE(file);
7570 tape->length = getFile32BitBE(file);
7572 /* read header fields that are new since version 1.2 */
7573 if (tape->file_version >= FILE_VERSION_1_2)
7575 byte store_participating_players = getFile8Bit(file);
7578 /* since version 1.2, tapes store which players participate in the tape */
7579 tape->num_participating_players = 0;
7580 for (i = 0; i < MAX_PLAYERS; i++)
7582 tape->player_participates[i] = FALSE;
7584 if (store_participating_players & (1 << i))
7586 tape->player_participates[i] = TRUE;
7587 tape->num_participating_players++;
7591 tape->use_mouse = (getFile8Bit(file) == 1 ? TRUE : FALSE);
7593 ReadUnusedBytesFromFile(file, TAPE_CHUNK_HEAD_UNUSED);
7595 engine_version = getFileVersion(file);
7596 if (engine_version > 0)
7597 tape->engine_version = engine_version;
7599 tape->engine_version = tape->game_version;
7605 static int LoadTape_INFO(File *file, int chunk_size, struct TapeInfo *tape)
7607 int level_identifier_size;
7610 level_identifier_size = getFile16BitBE(file);
7612 tape->level_identifier =
7613 checked_realloc(tape->level_identifier, level_identifier_size);
7615 for (i = 0; i < level_identifier_size; i++)
7616 tape->level_identifier[i] = getFile8Bit(file);
7618 tape->level_nr = getFile16BitBE(file);
7620 chunk_size = 2 + level_identifier_size + 2;
7625 static int LoadTape_BODY(File *file, int chunk_size, struct TapeInfo *tape)
7629 (tape->use_mouse ? 3 : tape->num_participating_players) + 1;
7630 int chunk_size_expected = tape_pos_size * tape->length;
7632 if (chunk_size_expected != chunk_size)
7634 ReadUnusedBytesFromFile(file, chunk_size);
7635 return chunk_size_expected;
7638 for (i = 0; i < tape->length; i++)
7640 if (i >= MAX_TAPE_LEN)
7642 Error(ERR_WARN, "tape truncated -- size exceeds maximum tape size %d",
7645 // tape too large; read and ignore remaining tape data from this chunk
7646 for (;i < tape->length; i++)
7647 ReadUnusedBytesFromFile(file, tape->num_participating_players + 1);
7652 if (tape->use_mouse)
7654 tape->pos[i].action[TAPE_ACTION_LX] = getFile8Bit(file);
7655 tape->pos[i].action[TAPE_ACTION_LY] = getFile8Bit(file);
7656 tape->pos[i].action[TAPE_ACTION_BUTTON] = getFile8Bit(file);
7658 tape->pos[i].action[TAPE_ACTION_UNUSED] = 0;
7662 for (j = 0; j < MAX_PLAYERS; j++)
7664 tape->pos[i].action[j] = MV_NONE;
7666 if (tape->player_participates[j])
7667 tape->pos[i].action[j] = getFile8Bit(file);
7671 tape->pos[i].delay = getFile8Bit(file);
7673 if (tape->file_version == FILE_VERSION_1_0)
7675 /* eliminate possible diagonal moves in old tapes */
7676 /* this is only for backward compatibility */
7678 byte joy_dir[4] = { JOY_LEFT, JOY_RIGHT, JOY_UP, JOY_DOWN };
7679 byte action = tape->pos[i].action[0];
7680 int k, num_moves = 0;
7682 for (k = 0; k<4; k++)
7684 if (action & joy_dir[k])
7686 tape->pos[i + num_moves].action[0] = joy_dir[k];
7688 tape->pos[i + num_moves].delay = 0;
7697 tape->length += num_moves;
7700 else if (tape->file_version < FILE_VERSION_2_0)
7702 /* convert pre-2.0 tapes to new tape format */
7704 if (tape->pos[i].delay > 1)
7707 tape->pos[i + 1] = tape->pos[i];
7708 tape->pos[i + 1].delay = 1;
7711 for (j = 0; j < MAX_PLAYERS; j++)
7712 tape->pos[i].action[j] = MV_NONE;
7713 tape->pos[i].delay--;
7720 if (checkEndOfFile(file))
7724 if (i != tape->length)
7725 chunk_size = tape_pos_size * i;
7730 void LoadTape_SokobanSolution(char *filename)
7733 int move_delay = TILESIZE / level.initial_player_stepsize[0];
7735 if (!(file = openFile(filename, MODE_READ)))
7737 tape.no_valid_file = TRUE;
7742 while (!checkEndOfFile(file))
7744 unsigned char c = getByteFromFile(file);
7746 if (checkEndOfFile(file))
7753 tape.pos[tape.length].action[0] = MV_UP;
7754 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
7760 tape.pos[tape.length].action[0] = MV_DOWN;
7761 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
7767 tape.pos[tape.length].action[0] = MV_LEFT;
7768 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
7774 tape.pos[tape.length].action[0] = MV_RIGHT;
7775 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
7783 /* ignore white-space characters */
7787 tape.no_valid_file = TRUE;
7789 Error(ERR_WARN, "unsupported Sokoban solution file '%s' ['%d']", filename, c);
7797 if (tape.no_valid_file)
7800 tape.length_frames = GetTapeLengthFrames();
7801 tape.length_seconds = GetTapeLengthSeconds();
7804 void LoadTapeFromFilename(char *filename)
7806 char cookie[MAX_LINE_LEN];
7807 char chunk_name[CHUNK_ID_LEN + 1];
7811 /* always start with reliable default values */
7812 setTapeInfoToDefaults();
7814 if (strSuffix(filename, ".sln"))
7816 LoadTape_SokobanSolution(filename);
7821 if (!(file = openFile(filename, MODE_READ)))
7823 tape.no_valid_file = TRUE;
7828 getFileChunkBE(file, chunk_name, NULL);
7829 if (strEqual(chunk_name, "RND1"))
7831 getFile32BitBE(file); /* not used */
7833 getFileChunkBE(file, chunk_name, NULL);
7834 if (!strEqual(chunk_name, "TAPE"))
7836 tape.no_valid_file = TRUE;
7838 Error(ERR_WARN, "unknown format of tape file '%s'", filename);
7845 else /* check for pre-2.0 file format with cookie string */
7847 strcpy(cookie, chunk_name);
7848 if (getStringFromFile(file, &cookie[4], MAX_LINE_LEN - 4) == NULL)
7850 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
7851 cookie[strlen(cookie) - 1] = '\0';
7853 if (!checkCookieString(cookie, TAPE_COOKIE_TMPL))
7855 tape.no_valid_file = TRUE;
7857 Error(ERR_WARN, "unknown format of tape file '%s'", filename);
7864 if ((tape.file_version = getFileVersionFromCookieString(cookie)) == -1)
7866 tape.no_valid_file = TRUE;
7868 Error(ERR_WARN, "unsupported version of tape file '%s'", filename);
7875 /* pre-2.0 tape files have no game version, so use file version here */
7876 tape.game_version = tape.file_version;
7879 if (tape.file_version < FILE_VERSION_1_2)
7881 /* tape files from versions before 1.2.0 without chunk structure */
7882 LoadTape_HEAD(file, TAPE_CHUNK_HEAD_SIZE, &tape);
7883 LoadTape_BODY(file, 2 * tape.length, &tape);
7891 int (*loader)(File *, int, struct TapeInfo *);
7895 { "VERS", TAPE_CHUNK_VERS_SIZE, LoadTape_VERS },
7896 { "HEAD", TAPE_CHUNK_HEAD_SIZE, LoadTape_HEAD },
7897 { "INFO", -1, LoadTape_INFO },
7898 { "BODY", -1, LoadTape_BODY },
7902 while (getFileChunkBE(file, chunk_name, &chunk_size))
7906 while (chunk_info[i].name != NULL &&
7907 !strEqual(chunk_name, chunk_info[i].name))
7910 if (chunk_info[i].name == NULL)
7912 Error(ERR_WARN, "unknown chunk '%s' in tape file '%s'",
7913 chunk_name, filename);
7914 ReadUnusedBytesFromFile(file, chunk_size);
7916 else if (chunk_info[i].size != -1 &&
7917 chunk_info[i].size != chunk_size)
7919 Error(ERR_WARN, "wrong size (%d) of chunk '%s' in tape file '%s'",
7920 chunk_size, chunk_name, filename);
7921 ReadUnusedBytesFromFile(file, chunk_size);
7925 /* call function to load this tape chunk */
7926 int chunk_size_expected =
7927 (chunk_info[i].loader)(file, chunk_size, &tape);
7929 /* the size of some chunks cannot be checked before reading other
7930 chunks first (like "HEAD" and "BODY") that contain some header
7931 information, so check them here */
7932 if (chunk_size_expected != chunk_size)
7934 Error(ERR_WARN, "wrong size (%d) of chunk '%s' in tape file '%s'",
7935 chunk_size, chunk_name, filename);
7943 tape.length_frames = GetTapeLengthFrames();
7944 tape.length_seconds = GetTapeLengthSeconds();
7947 printf("::: tape file version: %d\n", tape.file_version);
7948 printf("::: tape game version: %d\n", tape.game_version);
7949 printf("::: tape engine version: %d\n", tape.engine_version);
7953 void LoadTape(int nr)
7955 char *filename = getTapeFilename(nr);
7957 LoadTapeFromFilename(filename);
7960 void LoadSolutionTape(int nr)
7962 char *filename = getSolutionTapeFilename(nr);
7964 LoadTapeFromFilename(filename);
7966 if (TAPE_IS_EMPTY(tape) &&
7967 level.game_engine_type == GAME_ENGINE_TYPE_SP &&
7968 level.native_sp_level->demo.is_available)
7969 CopyNativeTape_SP_to_RND(&level);
7972 static void SaveTape_VERS(FILE *file, struct TapeInfo *tape)
7974 putFileVersion(file, tape->file_version);
7975 putFileVersion(file, tape->game_version);
7978 static void SaveTape_HEAD(FILE *file, struct TapeInfo *tape)
7981 byte store_participating_players = 0;
7983 /* set bits for participating players for compact storage */
7984 for (i = 0; i < MAX_PLAYERS; i++)
7985 if (tape->player_participates[i])
7986 store_participating_players |= (1 << i);
7988 putFile32BitBE(file, tape->random_seed);
7989 putFile32BitBE(file, tape->date);
7990 putFile32BitBE(file, tape->length);
7992 putFile8Bit(file, store_participating_players);
7994 putFile8Bit(file, (tape->use_mouse ? 1 : 0));
7996 /* unused bytes not at the end here for 4-byte alignment of engine_version */
7997 WriteUnusedBytesToFile(file, TAPE_CHUNK_HEAD_UNUSED);
7999 putFileVersion(file, tape->engine_version);
8002 static void SaveTape_INFO(FILE *file, struct TapeInfo *tape)
8004 int level_identifier_size = strlen(tape->level_identifier) + 1;
8007 putFile16BitBE(file, level_identifier_size);
8009 for (i = 0; i < level_identifier_size; i++)
8010 putFile8Bit(file, tape->level_identifier[i]);
8012 putFile16BitBE(file, tape->level_nr);
8015 static void SaveTape_BODY(FILE *file, struct TapeInfo *tape)
8019 for (i = 0; i < tape->length; i++)
8021 if (tape->use_mouse)
8023 putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_LX]);
8024 putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_LY]);
8025 putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_BUTTON]);
8029 for (j = 0; j < MAX_PLAYERS; j++)
8030 if (tape->player_participates[j])
8031 putFile8Bit(file, tape->pos[i].action[j]);
8034 putFile8Bit(file, tape->pos[i].delay);
8038 void SaveTape(int nr)
8040 char *filename = getTapeFilename(nr);
8042 int num_participating_players = 0;
8044 int info_chunk_size;
8045 int body_chunk_size;
8048 InitTapeDirectory(leveldir_current->subdir);
8050 if (!(file = fopen(filename, MODE_WRITE)))
8052 Error(ERR_WARN, "cannot save level recording file '%s'", filename);
8056 tape.file_version = FILE_VERSION_ACTUAL;
8057 tape.game_version = GAME_VERSION_ACTUAL;
8059 /* count number of participating players */
8060 for (i = 0; i < MAX_PLAYERS; i++)
8061 if (tape.player_participates[i])
8062 num_participating_players++;
8064 tape_pos_size = (tape.use_mouse ? 3 : num_participating_players) + 1;
8066 info_chunk_size = 2 + (strlen(tape.level_identifier) + 1) + 2;
8067 body_chunk_size = tape_pos_size * tape.length;
8069 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
8070 putFileChunkBE(file, "TAPE", CHUNK_SIZE_NONE);
8072 putFileChunkBE(file, "VERS", TAPE_CHUNK_VERS_SIZE);
8073 SaveTape_VERS(file, &tape);
8075 putFileChunkBE(file, "HEAD", TAPE_CHUNK_HEAD_SIZE);
8076 SaveTape_HEAD(file, &tape);
8078 putFileChunkBE(file, "INFO", info_chunk_size);
8079 SaveTape_INFO(file, &tape);
8081 putFileChunkBE(file, "BODY", body_chunk_size);
8082 SaveTape_BODY(file, &tape);
8086 SetFilePermissions(filename, PERMS_PRIVATE);
8088 tape.changed = FALSE;
8091 static boolean SaveTapeCheckedExt(int nr, char *msg_replace, char *msg_saved)
8093 char *filename = getTapeFilename(nr);
8094 boolean new_tape = !fileExists(filename);
8095 boolean tape_saved = FALSE;
8097 if (new_tape || Request(msg_replace, REQ_ASK))
8102 Request(msg_saved, REQ_CONFIRM);
8110 boolean SaveTapeChecked(int nr)
8112 return SaveTapeCheckedExt(nr, "Replace old tape?", "Tape saved!");
8115 boolean SaveTapeChecked_LevelSolved(int nr)
8117 return SaveTapeCheckedExt(nr, "Level solved! Replace old tape?",
8118 "Level solved! Tape saved!");
8121 void DumpTape(struct TapeInfo *tape)
8123 int tape_frame_counter;
8126 if (tape->no_valid_file)
8128 Error(ERR_WARN, "cannot dump -- no valid tape file found");
8134 Print("Tape of Level %03d (file version %08d, game version %08d)\n",
8135 tape->level_nr, tape->file_version, tape->game_version);
8136 Print(" (effective engine version %08d)\n",
8137 tape->engine_version);
8138 Print("Level series identifier: '%s'\n", tape->level_identifier);
8141 tape_frame_counter = 0;
8143 for (i = 0; i < tape->length; i++)
8145 if (i >= MAX_TAPE_LEN)
8150 for (j = 0; j < MAX_PLAYERS; j++)
8152 if (tape->player_participates[j])
8154 int action = tape->pos[i].action[j];
8156 Print("%d:%02x ", j, action);
8157 Print("[%c%c%c%c|%c%c] - ",
8158 (action & JOY_LEFT ? '<' : ' '),
8159 (action & JOY_RIGHT ? '>' : ' '),
8160 (action & JOY_UP ? '^' : ' '),
8161 (action & JOY_DOWN ? 'v' : ' '),
8162 (action & JOY_BUTTON_1 ? '1' : ' '),
8163 (action & JOY_BUTTON_2 ? '2' : ' '));
8167 Print("(%03d) ", tape->pos[i].delay);
8168 Print("[%05d]\n", tape_frame_counter);
8170 tape_frame_counter += tape->pos[i].delay;
8177 /* ========================================================================= */
8178 /* score file functions */
8179 /* ========================================================================= */
8181 void LoadScore(int nr)
8184 char *filename = getScoreFilename(nr);
8185 char cookie[MAX_LINE_LEN];
8186 char line[MAX_LINE_LEN];
8190 /* always start with reliable default values */
8191 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
8193 strcpy(highscore[i].Name, EMPTY_PLAYER_NAME);
8194 highscore[i].Score = 0;
8197 if (!(file = fopen(filename, MODE_READ)))
8200 /* check file identifier */
8201 if (fgets(cookie, MAX_LINE_LEN, file) == NULL)
8203 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
8204 cookie[strlen(cookie) - 1] = '\0';
8206 if (!checkCookieString(cookie, SCORE_COOKIE))
8208 Error(ERR_WARN, "unknown format of score file '%s'", filename);
8213 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
8215 if (fscanf(file, "%d", &highscore[i].Score) == EOF)
8216 Error(ERR_WARN, "fscanf() failed; %s", strerror(errno));
8217 if (fgets(line, MAX_LINE_LEN, file) == NULL)
8220 if (strlen(line) > 0 && line[strlen(line) - 1] == '\n')
8221 line[strlen(line) - 1] = '\0';
8223 for (line_ptr = line; *line_ptr; line_ptr++)
8225 if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != '\0')
8227 strncpy(highscore[i].Name, line_ptr, MAX_PLAYER_NAME_LEN);
8228 highscore[i].Name[MAX_PLAYER_NAME_LEN] = '\0';
8237 void SaveScore(int nr)
8240 int permissions = (program.global_scores ? PERMS_PUBLIC : PERMS_PRIVATE);
8241 char *filename = getScoreFilename(nr);
8244 InitScoreDirectory(leveldir_current->subdir);
8246 if (!(file = fopen(filename, MODE_WRITE)))
8248 Error(ERR_WARN, "cannot save score for level %d", nr);
8252 fprintf(file, "%s\n\n", SCORE_COOKIE);
8254 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
8255 fprintf(file, "%d %s\n", highscore[i].Score, highscore[i].Name);
8259 SetFilePermissions(filename, permissions);
8263 /* ========================================================================= */
8264 /* setup file functions */
8265 /* ========================================================================= */
8267 #define TOKEN_STR_PLAYER_PREFIX "player_"
8272 SETUP_TOKEN_PLAYER_NAME = 0,
8274 SETUP_TOKEN_SOUND_LOOPS,
8275 SETUP_TOKEN_SOUND_MUSIC,
8276 SETUP_TOKEN_SOUND_SIMPLE,
8278 SETUP_TOKEN_SCROLL_DELAY,
8279 SETUP_TOKEN_SCROLL_DELAY_VALUE,
8280 SETUP_TOKEN_ENGINE_SNAPSHOT_MODE,
8281 SETUP_TOKEN_ENGINE_SNAPSHOT_MEMORY,
8282 SETUP_TOKEN_FADE_SCREENS,
8283 SETUP_TOKEN_AUTORECORD,
8284 SETUP_TOKEN_SHOW_TITLESCREEN,
8285 SETUP_TOKEN_QUICK_DOORS,
8286 SETUP_TOKEN_TEAM_MODE,
8287 SETUP_TOKEN_HANDICAP,
8288 SETUP_TOKEN_SKIP_LEVELS,
8289 SETUP_TOKEN_INCREMENT_LEVELS,
8290 SETUP_TOKEN_AUTO_PLAY_NEXT_LEVEL,
8291 SETUP_TOKEN_SKIP_SCORES_AFTER_GAME,
8292 SETUP_TOKEN_TIME_LIMIT,
8293 SETUP_TOKEN_FULLSCREEN,
8294 SETUP_TOKEN_WINDOW_SCALING_PERCENT,
8295 SETUP_TOKEN_WINDOW_SCALING_QUALITY,
8296 SETUP_TOKEN_SCREEN_RENDERING_MODE,
8297 SETUP_TOKEN_ASK_ON_ESCAPE,
8298 SETUP_TOKEN_ASK_ON_ESCAPE_EDITOR,
8299 SETUP_TOKEN_QUICK_SWITCH,
8300 SETUP_TOKEN_INPUT_ON_FOCUS,
8301 SETUP_TOKEN_PREFER_AGA_GRAPHICS,
8302 SETUP_TOKEN_GAME_FRAME_DELAY,
8303 SETUP_TOKEN_SP_SHOW_BORDER_ELEMENTS,
8304 SETUP_TOKEN_SMALL_GAME_GRAPHICS,
8305 SETUP_TOKEN_SHOW_SNAPSHOT_BUTTONS,
8306 SETUP_TOKEN_GRAPHICS_SET,
8307 SETUP_TOKEN_SOUNDS_SET,
8308 SETUP_TOKEN_MUSIC_SET,
8309 SETUP_TOKEN_OVERRIDE_LEVEL_GRAPHICS,
8310 SETUP_TOKEN_OVERRIDE_LEVEL_SOUNDS,
8311 SETUP_TOKEN_OVERRIDE_LEVEL_MUSIC,
8312 SETUP_TOKEN_VOLUME_SIMPLE,
8313 SETUP_TOKEN_VOLUME_LOOPS,
8314 SETUP_TOKEN_VOLUME_MUSIC,
8315 SETUP_TOKEN_NETWORK_MODE,
8316 SETUP_TOKEN_NETWORK_PLAYER_NR,
8317 SETUP_TOKEN_TOUCH_CONTROL_TYPE,
8318 SETUP_TOKEN_TOUCH_MOVE_DISTANCE,
8319 SETUP_TOKEN_TOUCH_DROP_DISTANCE,
8320 SETUP_TOKEN_TOUCH_TRANSPARENCY,
8321 SETUP_TOKEN_TOUCH_DRAW_OUTLINED,
8322 SETUP_TOKEN_TOUCH_DRAW_PRESSED,
8323 SETUP_TOKEN_TOUCH_GRID_XSIZE_0,
8324 SETUP_TOKEN_TOUCH_GRID_YSIZE_0,
8325 SETUP_TOKEN_TOUCH_GRID_XSIZE_1,
8326 SETUP_TOKEN_TOUCH_GRID_YSIZE_1,
8328 NUM_GLOBAL_SETUP_TOKENS
8334 SETUP_TOKEN_AUTO_EDITOR_ZOOM_TILESIZE = 0,
8336 NUM_AUTO_SETUP_TOKENS
8342 SETUP_TOKEN_EDITOR_EL_CLASSIC = 0,
8343 SETUP_TOKEN_EDITOR_EL_CUSTOM,
8344 SETUP_TOKEN_EDITOR_EL_USER_DEFINED,
8345 SETUP_TOKEN_EDITOR_EL_DYNAMIC,
8346 SETUP_TOKEN_EDITOR_EL_HEADLINES,
8347 SETUP_TOKEN_EDITOR_SHOW_ELEMENT_TOKEN,
8349 NUM_EDITOR_SETUP_TOKENS
8352 /* editor cascade setup */
8355 SETUP_TOKEN_EDITOR_CASCADE_BD = 0,
8356 SETUP_TOKEN_EDITOR_CASCADE_EM,
8357 SETUP_TOKEN_EDITOR_CASCADE_EMC,
8358 SETUP_TOKEN_EDITOR_CASCADE_RND,
8359 SETUP_TOKEN_EDITOR_CASCADE_SB,
8360 SETUP_TOKEN_EDITOR_CASCADE_SP,
8361 SETUP_TOKEN_EDITOR_CASCADE_DC,
8362 SETUP_TOKEN_EDITOR_CASCADE_DX,
8363 SETUP_TOKEN_EDITOR_CASCADE_TEXT,
8364 SETUP_TOKEN_EDITOR_CASCADE_STEELTEXT,
8365 SETUP_TOKEN_EDITOR_CASCADE_CE,
8366 SETUP_TOKEN_EDITOR_CASCADE_GE,
8367 SETUP_TOKEN_EDITOR_CASCADE_REF,
8368 SETUP_TOKEN_EDITOR_CASCADE_USER,
8369 SETUP_TOKEN_EDITOR_CASCADE_DYNAMIC,
8371 NUM_EDITOR_CASCADE_SETUP_TOKENS
8374 /* shortcut setup */
8377 SETUP_TOKEN_SHORTCUT_SAVE_GAME = 0,
8378 SETUP_TOKEN_SHORTCUT_LOAD_GAME,
8379 SETUP_TOKEN_SHORTCUT_TOGGLE_PAUSE,
8380 SETUP_TOKEN_SHORTCUT_FOCUS_PLAYER_1,
8381 SETUP_TOKEN_SHORTCUT_FOCUS_PLAYER_2,
8382 SETUP_TOKEN_SHORTCUT_FOCUS_PLAYER_3,
8383 SETUP_TOKEN_SHORTCUT_FOCUS_PLAYER_4,
8384 SETUP_TOKEN_SHORTCUT_FOCUS_PLAYER_ALL,
8385 SETUP_TOKEN_SHORTCUT_TAPE_EJECT,
8386 SETUP_TOKEN_SHORTCUT_TAPE_EXTRA,
8387 SETUP_TOKEN_SHORTCUT_TAPE_STOP,
8388 SETUP_TOKEN_SHORTCUT_TAPE_PAUSE,
8389 SETUP_TOKEN_SHORTCUT_TAPE_RECORD,
8390 SETUP_TOKEN_SHORTCUT_TAPE_PLAY,
8391 SETUP_TOKEN_SHORTCUT_SOUND_SIMPLE,
8392 SETUP_TOKEN_SHORTCUT_SOUND_LOOPS,
8393 SETUP_TOKEN_SHORTCUT_SOUND_MUSIC,
8394 SETUP_TOKEN_SHORTCUT_SNAP_LEFT,
8395 SETUP_TOKEN_SHORTCUT_SNAP_RIGHT,
8396 SETUP_TOKEN_SHORTCUT_SNAP_UP,
8397 SETUP_TOKEN_SHORTCUT_SNAP_DOWN,
8399 NUM_SHORTCUT_SETUP_TOKENS
8405 SETUP_TOKEN_PLAYER_USE_JOYSTICK = 0,
8406 SETUP_TOKEN_PLAYER_JOY_DEVICE_NAME,
8407 SETUP_TOKEN_PLAYER_JOY_XLEFT,
8408 SETUP_TOKEN_PLAYER_JOY_XMIDDLE,
8409 SETUP_TOKEN_PLAYER_JOY_XRIGHT,
8410 SETUP_TOKEN_PLAYER_JOY_YUPPER,
8411 SETUP_TOKEN_PLAYER_JOY_YMIDDLE,
8412 SETUP_TOKEN_PLAYER_JOY_YLOWER,
8413 SETUP_TOKEN_PLAYER_JOY_SNAP,
8414 SETUP_TOKEN_PLAYER_JOY_DROP,
8415 SETUP_TOKEN_PLAYER_KEY_LEFT,
8416 SETUP_TOKEN_PLAYER_KEY_RIGHT,
8417 SETUP_TOKEN_PLAYER_KEY_UP,
8418 SETUP_TOKEN_PLAYER_KEY_DOWN,
8419 SETUP_TOKEN_PLAYER_KEY_SNAP,
8420 SETUP_TOKEN_PLAYER_KEY_DROP,
8422 NUM_PLAYER_SETUP_TOKENS
8428 SETUP_TOKEN_SYSTEM_SDL_VIDEODRIVER = 0,
8429 SETUP_TOKEN_SYSTEM_SDL_AUDIODRIVER,
8430 SETUP_TOKEN_SYSTEM_AUDIO_FRAGMENT_SIZE,
8432 NUM_SYSTEM_SETUP_TOKENS
8435 /* internal setup */
8438 SETUP_TOKEN_INT_PROGRAM_TITLE = 0,
8439 SETUP_TOKEN_INT_PROGRAM_VERSION,
8440 SETUP_TOKEN_INT_PROGRAM_AUTHOR,
8441 SETUP_TOKEN_INT_PROGRAM_EMAIL,
8442 SETUP_TOKEN_INT_PROGRAM_WEBSITE,
8443 SETUP_TOKEN_INT_PROGRAM_COPYRIGHT,
8444 SETUP_TOKEN_INT_PROGRAM_COMPANY,
8445 SETUP_TOKEN_INT_PROGRAM_ICON_FILE,
8446 SETUP_TOKEN_INT_DEFAULT_GRAPHICS_SET,
8447 SETUP_TOKEN_INT_DEFAULT_SOUNDS_SET,
8448 SETUP_TOKEN_INT_DEFAULT_MUSIC_SET,
8449 SETUP_TOKEN_INT_FALLBACK_GRAPHICS_FILE,
8450 SETUP_TOKEN_INT_FALLBACK_SOUNDS_FILE,
8451 SETUP_TOKEN_INT_FALLBACK_MUSIC_FILE,
8452 SETUP_TOKEN_INT_DEFAULT_LEVEL_SERIES,
8453 SETUP_TOKEN_INT_CHOOSE_FROM_TOP_LEVELDIR,
8454 SETUP_TOKEN_INT_SHOW_SCALING_IN_TITLE,
8455 SETUP_TOKEN_INT_DEFAULT_WINDOW_WIDTH,
8456 SETUP_TOKEN_INT_DEFAULT_WINDOW_HEIGHT,
8458 NUM_INTERNAL_SETUP_TOKENS
8464 SETUP_TOKEN_DEBUG_FRAME_DELAY_0 = 0,
8465 SETUP_TOKEN_DEBUG_FRAME_DELAY_1,
8466 SETUP_TOKEN_DEBUG_FRAME_DELAY_2,
8467 SETUP_TOKEN_DEBUG_FRAME_DELAY_3,
8468 SETUP_TOKEN_DEBUG_FRAME_DELAY_4,
8469 SETUP_TOKEN_DEBUG_FRAME_DELAY_5,
8470 SETUP_TOKEN_DEBUG_FRAME_DELAY_6,
8471 SETUP_TOKEN_DEBUG_FRAME_DELAY_7,
8472 SETUP_TOKEN_DEBUG_FRAME_DELAY_8,
8473 SETUP_TOKEN_DEBUG_FRAME_DELAY_9,
8474 SETUP_TOKEN_DEBUG_FRAME_DELAY_KEY_0,
8475 SETUP_TOKEN_DEBUG_FRAME_DELAY_KEY_1,
8476 SETUP_TOKEN_DEBUG_FRAME_DELAY_KEY_2,
8477 SETUP_TOKEN_DEBUG_FRAME_DELAY_KEY_3,
8478 SETUP_TOKEN_DEBUG_FRAME_DELAY_KEY_4,
8479 SETUP_TOKEN_DEBUG_FRAME_DELAY_KEY_5,
8480 SETUP_TOKEN_DEBUG_FRAME_DELAY_KEY_6,
8481 SETUP_TOKEN_DEBUG_FRAME_DELAY_KEY_7,
8482 SETUP_TOKEN_DEBUG_FRAME_DELAY_KEY_8,
8483 SETUP_TOKEN_DEBUG_FRAME_DELAY_KEY_9,
8484 SETUP_TOKEN_DEBUG_FRAME_DELAY_USE_MOD_KEY,
8485 SETUP_TOKEN_DEBUG_FRAME_DELAY_GAME_ONLY,
8486 SETUP_TOKEN_DEBUG_SHOW_FRAMES_PER_SECOND,
8488 NUM_DEBUG_SETUP_TOKENS
8494 SETUP_TOKEN_OPTIONS_VERBOSE = 0,
8496 NUM_OPTIONS_SETUP_TOKENS
8500 static struct SetupInfo si;
8501 static struct SetupAutoSetupInfo sasi;
8502 static struct SetupEditorInfo sei;
8503 static struct SetupEditorCascadeInfo seci;
8504 static struct SetupShortcutInfo ssi;
8505 static struct SetupInputInfo sii;
8506 static struct SetupSystemInfo syi;
8507 static struct SetupInternalInfo sxi;
8508 static struct SetupDebugInfo sdi;
8509 static struct OptionInfo soi;
8511 static struct TokenInfo global_setup_tokens[] =
8513 { TYPE_STRING, &si.player_name, "player_name" },
8514 { TYPE_SWITCH, &si.sound, "sound" },
8515 { TYPE_SWITCH, &si.sound_loops, "repeating_sound_loops" },
8516 { TYPE_SWITCH, &si.sound_music, "background_music" },
8517 { TYPE_SWITCH, &si.sound_simple, "simple_sound_effects" },
8518 { TYPE_SWITCH, &si.toons, "toons" },
8519 { TYPE_SWITCH, &si.scroll_delay, "scroll_delay" },
8520 { TYPE_INTEGER,&si.scroll_delay_value, "scroll_delay_value" },
8521 { TYPE_STRING, &si.engine_snapshot_mode, "engine_snapshot_mode" },
8522 { TYPE_INTEGER,&si.engine_snapshot_memory, "engine_snapshot_memory" },
8523 { TYPE_SWITCH, &si.fade_screens, "fade_screens" },
8524 { TYPE_SWITCH, &si.autorecord, "automatic_tape_recording"},
8525 { TYPE_SWITCH, &si.show_titlescreen, "show_titlescreen" },
8526 { TYPE_SWITCH, &si.quick_doors, "quick_doors" },
8527 { TYPE_SWITCH, &si.team_mode, "team_mode" },
8528 { TYPE_SWITCH, &si.handicap, "handicap" },
8529 { TYPE_SWITCH, &si.skip_levels, "skip_levels" },
8530 { TYPE_SWITCH, &si.increment_levels, "increment_levels" },
8531 { TYPE_SWITCH, &si.auto_play_next_level, "auto_play_next_level" },
8532 { TYPE_SWITCH, &si.skip_scores_after_game, "skip_scores_after_game" },
8533 { TYPE_SWITCH, &si.time_limit, "time_limit" },
8534 { TYPE_SWITCH, &si.fullscreen, "fullscreen" },
8535 { TYPE_INTEGER,&si.window_scaling_percent, "window_scaling_percent" },
8536 { TYPE_STRING, &si.window_scaling_quality, "window_scaling_quality" },
8537 { TYPE_STRING, &si.screen_rendering_mode, "screen_rendering_mode" },
8538 { TYPE_SWITCH, &si.ask_on_escape, "ask_on_escape" },
8539 { TYPE_SWITCH, &si.ask_on_escape_editor, "ask_on_escape_editor" },
8540 { TYPE_SWITCH, &si.quick_switch, "quick_player_switch" },
8541 { TYPE_SWITCH, &si.input_on_focus, "input_on_focus" },
8542 { TYPE_SWITCH, &si.prefer_aga_graphics, "prefer_aga_graphics" },
8543 { TYPE_INTEGER,&si.game_frame_delay, "game_frame_delay" },
8544 { TYPE_SWITCH, &si.sp_show_border_elements, "sp_show_border_elements" },
8545 { TYPE_SWITCH, &si.small_game_graphics, "small_game_graphics" },
8546 { TYPE_SWITCH, &si.show_snapshot_buttons, "show_snapshot_buttons" },
8547 { TYPE_STRING, &si.graphics_set, "graphics_set" },
8548 { TYPE_STRING, &si.sounds_set, "sounds_set" },
8549 { TYPE_STRING, &si.music_set, "music_set" },
8550 { TYPE_SWITCH3,&si.override_level_graphics, "override_level_graphics" },
8551 { TYPE_SWITCH3,&si.override_level_sounds, "override_level_sounds" },
8552 { TYPE_SWITCH3,&si.override_level_music, "override_level_music" },
8553 { TYPE_INTEGER,&si.volume_simple, "volume_simple" },
8554 { TYPE_INTEGER,&si.volume_loops, "volume_loops" },
8555 { TYPE_INTEGER,&si.volume_music, "volume_music" },
8556 { TYPE_SWITCH, &si.network_mode, "network_mode" },
8557 { TYPE_PLAYER, &si.network_player_nr, "network_player" },
8558 { TYPE_STRING, &si.touch.control_type, "touch.control_type" },
8559 { TYPE_INTEGER,&si.touch.move_distance, "touch.move_distance" },
8560 { TYPE_INTEGER,&si.touch.drop_distance, "touch.drop_distance" },
8561 { TYPE_INTEGER,&si.touch.transparency, "touch.transparency" },
8562 { TYPE_INTEGER,&si.touch.draw_outlined, "touch.draw_outlined" },
8563 { TYPE_INTEGER,&si.touch.draw_pressed, "touch.draw_pressed" },
8564 { TYPE_INTEGER,&si.touch.grid_xsize[0], "touch.virtual_buttons.0.xsize" },
8565 { TYPE_INTEGER,&si.touch.grid_ysize[0], "touch.virtual_buttons.0.ysize" },
8566 { TYPE_INTEGER,&si.touch.grid_xsize[1], "touch.virtual_buttons.1.xsize" },
8567 { TYPE_INTEGER,&si.touch.grid_ysize[1], "touch.virtual_buttons.1.ysize" },
8570 static struct TokenInfo auto_setup_tokens[] =
8572 { TYPE_INTEGER,&sasi.editor_zoom_tilesize, "editor.zoom_tilesize" },
8575 static struct TokenInfo editor_setup_tokens[] =
8577 { TYPE_SWITCH, &sei.el_classic, "editor.el_classic" },
8578 { TYPE_SWITCH, &sei.el_custom, "editor.el_custom" },
8579 { TYPE_SWITCH, &sei.el_user_defined, "editor.el_user_defined" },
8580 { TYPE_SWITCH, &sei.el_dynamic, "editor.el_dynamic" },
8581 { TYPE_SWITCH, &sei.el_headlines, "editor.el_headlines" },
8582 { TYPE_SWITCH, &sei.show_element_token,"editor.show_element_token" },
8585 static struct TokenInfo editor_cascade_setup_tokens[] =
8587 { TYPE_SWITCH, &seci.el_bd, "editor.cascade.el_bd" },
8588 { TYPE_SWITCH, &seci.el_em, "editor.cascade.el_em" },
8589 { TYPE_SWITCH, &seci.el_emc, "editor.cascade.el_emc" },
8590 { TYPE_SWITCH, &seci.el_rnd, "editor.cascade.el_rnd" },
8591 { TYPE_SWITCH, &seci.el_sb, "editor.cascade.el_sb" },
8592 { TYPE_SWITCH, &seci.el_sp, "editor.cascade.el_sp" },
8593 { TYPE_SWITCH, &seci.el_dc, "editor.cascade.el_dc" },
8594 { TYPE_SWITCH, &seci.el_dx, "editor.cascade.el_dx" },
8595 { TYPE_SWITCH, &seci.el_mm, "editor.cascade.el_mm" },
8596 { TYPE_SWITCH, &seci.el_df, "editor.cascade.el_df" },
8597 { TYPE_SWITCH, &seci.el_chars, "editor.cascade.el_chars" },
8598 { TYPE_SWITCH, &seci.el_steel_chars, "editor.cascade.el_steel_chars" },
8599 { TYPE_SWITCH, &seci.el_ce, "editor.cascade.el_ce" },
8600 { TYPE_SWITCH, &seci.el_ge, "editor.cascade.el_ge" },
8601 { TYPE_SWITCH, &seci.el_ref, "editor.cascade.el_ref" },
8602 { TYPE_SWITCH, &seci.el_user, "editor.cascade.el_user" },
8603 { TYPE_SWITCH, &seci.el_dynamic, "editor.cascade.el_dynamic" },
8606 static struct TokenInfo shortcut_setup_tokens[] =
8608 { TYPE_KEY_X11, &ssi.save_game, "shortcut.save_game" },
8609 { TYPE_KEY_X11, &ssi.load_game, "shortcut.load_game" },
8610 { TYPE_KEY_X11, &ssi.toggle_pause, "shortcut.toggle_pause" },
8611 { TYPE_KEY_X11, &ssi.focus_player[0], "shortcut.focus_player_1" },
8612 { TYPE_KEY_X11, &ssi.focus_player[1], "shortcut.focus_player_2" },
8613 { TYPE_KEY_X11, &ssi.focus_player[2], "shortcut.focus_player_3" },
8614 { TYPE_KEY_X11, &ssi.focus_player[3], "shortcut.focus_player_4" },
8615 { TYPE_KEY_X11, &ssi.focus_player_all,"shortcut.focus_player_all" },
8616 { TYPE_KEY_X11, &ssi.tape_eject, "shortcut.tape_eject" },
8617 { TYPE_KEY_X11, &ssi.tape_extra, "shortcut.tape_extra" },
8618 { TYPE_KEY_X11, &ssi.tape_stop, "shortcut.tape_stop" },
8619 { TYPE_KEY_X11, &ssi.tape_pause, "shortcut.tape_pause" },
8620 { TYPE_KEY_X11, &ssi.tape_record, "shortcut.tape_record" },
8621 { TYPE_KEY_X11, &ssi.tape_play, "shortcut.tape_play" },
8622 { TYPE_KEY_X11, &ssi.sound_simple, "shortcut.sound_simple" },
8623 { TYPE_KEY_X11, &ssi.sound_loops, "shortcut.sound_loops" },
8624 { TYPE_KEY_X11, &ssi.sound_music, "shortcut.sound_music" },
8625 { TYPE_KEY_X11, &ssi.snap_left, "shortcut.snap_left" },
8626 { TYPE_KEY_X11, &ssi.snap_right, "shortcut.snap_right" },
8627 { TYPE_KEY_X11, &ssi.snap_up, "shortcut.snap_up" },
8628 { TYPE_KEY_X11, &ssi.snap_down, "shortcut.snap_down" },
8631 static struct TokenInfo player_setup_tokens[] =
8633 { TYPE_BOOLEAN, &sii.use_joystick, ".use_joystick" },
8634 { TYPE_STRING, &sii.joy.device_name, ".joy.device_name" },
8635 { TYPE_INTEGER, &sii.joy.xleft, ".joy.xleft" },
8636 { TYPE_INTEGER, &sii.joy.xmiddle, ".joy.xmiddle" },
8637 { TYPE_INTEGER, &sii.joy.xright, ".joy.xright" },
8638 { TYPE_INTEGER, &sii.joy.yupper, ".joy.yupper" },
8639 { TYPE_INTEGER, &sii.joy.ymiddle, ".joy.ymiddle" },
8640 { TYPE_INTEGER, &sii.joy.ylower, ".joy.ylower" },
8641 { TYPE_INTEGER, &sii.joy.snap, ".joy.snap_field" },
8642 { TYPE_INTEGER, &sii.joy.drop, ".joy.place_bomb" },
8643 { TYPE_KEY_X11, &sii.key.left, ".key.move_left" },
8644 { TYPE_KEY_X11, &sii.key.right, ".key.move_right" },
8645 { TYPE_KEY_X11, &sii.key.up, ".key.move_up" },
8646 { TYPE_KEY_X11, &sii.key.down, ".key.move_down" },
8647 { TYPE_KEY_X11, &sii.key.snap, ".key.snap_field" },
8648 { TYPE_KEY_X11, &sii.key.drop, ".key.place_bomb" },
8651 static struct TokenInfo system_setup_tokens[] =
8653 { TYPE_STRING, &syi.sdl_videodriver, "system.sdl_videodriver" },
8654 { TYPE_STRING, &syi.sdl_audiodriver, "system.sdl_audiodriver" },
8655 { TYPE_INTEGER, &syi.audio_fragment_size,"system.audio_fragment_size" },
8658 static struct TokenInfo internal_setup_tokens[] =
8660 { TYPE_STRING, &sxi.program_title, "program_title" },
8661 { TYPE_STRING, &sxi.program_version, "program_version" },
8662 { TYPE_STRING, &sxi.program_author, "program_author" },
8663 { TYPE_STRING, &sxi.program_email, "program_email" },
8664 { TYPE_STRING, &sxi.program_website, "program_website" },
8665 { TYPE_STRING, &sxi.program_copyright, "program_copyright" },
8666 { TYPE_STRING, &sxi.program_company, "program_company" },
8667 { TYPE_STRING, &sxi.program_icon_file, "program_icon_file" },
8668 { TYPE_STRING, &sxi.default_graphics_set, "default_graphics_set" },
8669 { TYPE_STRING, &sxi.default_sounds_set, "default_sounds_set" },
8670 { TYPE_STRING, &sxi.default_music_set, "default_music_set" },
8671 { TYPE_STRING, &sxi.fallback_graphics_file, "fallback_graphics_file"},
8672 { TYPE_STRING, &sxi.fallback_sounds_file, "fallback_sounds_file" },
8673 { TYPE_STRING, &sxi.fallback_music_file, "fallback_music_file" },
8674 { TYPE_STRING, &sxi.default_level_series, "default_level_series" },
8675 { TYPE_BOOLEAN,&sxi.choose_from_top_leveldir, "choose_from_top_leveldir" },
8676 { TYPE_BOOLEAN,&sxi.show_scaling_in_title, "show_scaling_in_title" },
8677 { TYPE_INTEGER,&sxi.default_window_width, "default_window_width" },
8678 { TYPE_INTEGER,&sxi.default_window_height, "default_window_height" },
8681 static struct TokenInfo debug_setup_tokens[] =
8683 { TYPE_INTEGER, &sdi.frame_delay[0], "debug.frame_delay_0" },
8684 { TYPE_INTEGER, &sdi.frame_delay[1], "debug.frame_delay_1" },
8685 { TYPE_INTEGER, &sdi.frame_delay[2], "debug.frame_delay_2" },
8686 { TYPE_INTEGER, &sdi.frame_delay[3], "debug.frame_delay_3" },
8687 { TYPE_INTEGER, &sdi.frame_delay[4], "debug.frame_delay_4" },
8688 { TYPE_INTEGER, &sdi.frame_delay[5], "debug.frame_delay_5" },
8689 { TYPE_INTEGER, &sdi.frame_delay[6], "debug.frame_delay_6" },
8690 { TYPE_INTEGER, &sdi.frame_delay[7], "debug.frame_delay_7" },
8691 { TYPE_INTEGER, &sdi.frame_delay[8], "debug.frame_delay_8" },
8692 { TYPE_INTEGER, &sdi.frame_delay[9], "debug.frame_delay_9" },
8693 { TYPE_KEY_X11, &sdi.frame_delay_key[0], "debug.key.frame_delay_0" },
8694 { TYPE_KEY_X11, &sdi.frame_delay_key[1], "debug.key.frame_delay_1" },
8695 { TYPE_KEY_X11, &sdi.frame_delay_key[2], "debug.key.frame_delay_2" },
8696 { TYPE_KEY_X11, &sdi.frame_delay_key[3], "debug.key.frame_delay_3" },
8697 { TYPE_KEY_X11, &sdi.frame_delay_key[4], "debug.key.frame_delay_4" },
8698 { TYPE_KEY_X11, &sdi.frame_delay_key[5], "debug.key.frame_delay_5" },
8699 { TYPE_KEY_X11, &sdi.frame_delay_key[6], "debug.key.frame_delay_6" },
8700 { TYPE_KEY_X11, &sdi.frame_delay_key[7], "debug.key.frame_delay_7" },
8701 { TYPE_KEY_X11, &sdi.frame_delay_key[8], "debug.key.frame_delay_8" },
8702 { TYPE_KEY_X11, &sdi.frame_delay_key[9], "debug.key.frame_delay_9" },
8703 { TYPE_BOOLEAN, &sdi.frame_delay_use_mod_key,"debug.frame_delay.use_mod_key"},
8704 { TYPE_BOOLEAN, &sdi.frame_delay_game_only, "debug.frame_delay.game_only" },
8705 { TYPE_BOOLEAN, &sdi.show_frames_per_second, "debug.show_frames_per_second" },
8708 static struct TokenInfo options_setup_tokens[] =
8710 { TYPE_BOOLEAN, &soi.verbose, "options.verbose" },
8713 static char *get_corrected_login_name(char *login_name)
8715 /* needed because player name must be a fixed length string */
8716 char *login_name_new = checked_malloc(MAX_PLAYER_NAME_LEN + 1);
8718 strncpy(login_name_new, login_name, MAX_PLAYER_NAME_LEN);
8719 login_name_new[MAX_PLAYER_NAME_LEN] = '\0';
8721 if (strlen(login_name) > MAX_PLAYER_NAME_LEN) /* name has been cut */
8722 if (strchr(login_name_new, ' '))
8723 *strchr(login_name_new, ' ') = '\0';
8725 return login_name_new;
8728 static void setSetupInfoToDefaults(struct SetupInfo *si)
8732 si->player_name = get_corrected_login_name(getLoginName());
8735 si->sound_loops = TRUE;
8736 si->sound_music = TRUE;
8737 si->sound_simple = TRUE;
8739 si->scroll_delay = TRUE;
8740 si->scroll_delay_value = STD_SCROLL_DELAY;
8741 si->engine_snapshot_mode = getStringCopy(STR_SNAPSHOT_MODE_DEFAULT);
8742 si->engine_snapshot_memory = SNAPSHOT_MEMORY_DEFAULT;
8743 si->fade_screens = TRUE;
8744 si->autorecord = TRUE;
8745 si->show_titlescreen = TRUE;
8746 si->quick_doors = FALSE;
8747 si->team_mode = FALSE;
8748 si->handicap = TRUE;
8749 si->skip_levels = TRUE;
8750 si->increment_levels = TRUE;
8751 si->auto_play_next_level = TRUE;
8752 si->skip_scores_after_game = FALSE;
8753 si->time_limit = TRUE;
8754 si->fullscreen = FALSE;
8755 si->window_scaling_percent = STD_WINDOW_SCALING_PERCENT;
8756 si->window_scaling_quality = getStringCopy(SCALING_QUALITY_DEFAULT);
8757 si->screen_rendering_mode = getStringCopy(STR_SPECIAL_RENDERING_DEFAULT);
8758 si->ask_on_escape = TRUE;
8759 si->ask_on_escape_editor = TRUE;
8760 si->quick_switch = FALSE;
8761 si->input_on_focus = FALSE;
8762 si->prefer_aga_graphics = TRUE;
8763 si->game_frame_delay = GAME_FRAME_DELAY;
8764 si->sp_show_border_elements = FALSE;
8765 si->small_game_graphics = FALSE;
8766 si->show_snapshot_buttons = FALSE;
8768 si->graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
8769 si->sounds_set = getStringCopy(SND_CLASSIC_SUBDIR);
8770 si->music_set = getStringCopy(MUS_CLASSIC_SUBDIR);
8772 si->override_level_graphics = FALSE;
8773 si->override_level_sounds = FALSE;
8774 si->override_level_music = FALSE;
8776 si->volume_simple = 100; /* percent */
8777 si->volume_loops = 100; /* percent */
8778 si->volume_music = 100; /* percent */
8780 si->network_mode = FALSE;
8781 si->network_player_nr = 0; /* first player */
8783 si->touch.control_type = getStringCopy(TOUCH_CONTROL_DEFAULT);
8784 si->touch.move_distance = TOUCH_MOVE_DISTANCE_DEFAULT; /* percent */
8785 si->touch.drop_distance = TOUCH_DROP_DISTANCE_DEFAULT; /* percent */
8786 si->touch.transparency = TOUCH_TRANSPARENCY_DEFAULT; /* percent */
8787 si->touch.draw_outlined = TRUE;
8788 si->touch.draw_pressed = TRUE;
8790 for (i = 0; i < 2; i++)
8792 char *default_grid_button[6][2] =
8798 { "111222", " vv " },
8799 { "111222", " vv " }
8801 int grid_xsize = DEFAULT_GRID_XSIZE(i);
8802 int grid_ysize = DEFAULT_GRID_YSIZE(i);
8803 int min_xsize = MIN(6, grid_xsize);
8804 int min_ysize = MIN(6, grid_ysize);
8805 int startx = grid_xsize - min_xsize;
8806 int starty = grid_ysize - min_ysize;
8809 // virtual buttons grid can only be set to defaults if video is initialized
8810 // (this will be repeated if virtual buttons are not loaded from setup file)
8811 if (video.initialized)
8813 si->touch.grid_xsize[i] = grid_xsize;
8814 si->touch.grid_ysize[i] = grid_ysize;
8818 si->touch.grid_xsize[i] = -1;
8819 si->touch.grid_ysize[i] = -1;
8822 for (x = 0; x < MAX_GRID_XSIZE; x++)
8823 for (y = 0; y < MAX_GRID_YSIZE; y++)
8824 si->touch.grid_button[i][x][y] = CHAR_GRID_BUTTON_NONE;
8826 for (x = 0; x < min_xsize; x++)
8827 for (y = 0; y < min_ysize; y++)
8828 si->touch.grid_button[i][x][starty + y] =
8829 default_grid_button[y][0][x];
8831 for (x = 0; x < min_xsize; x++)
8832 for (y = 0; y < min_ysize; y++)
8833 si->touch.grid_button[i][startx + x][starty + y] =
8834 default_grid_button[y][1][x];
8837 si->touch.grid_initialized = video.initialized;
8839 si->editor.el_boulderdash = TRUE;
8840 si->editor.el_emerald_mine = TRUE;
8841 si->editor.el_emerald_mine_club = TRUE;
8842 si->editor.el_more = TRUE;
8843 si->editor.el_sokoban = TRUE;
8844 si->editor.el_supaplex = TRUE;
8845 si->editor.el_diamond_caves = TRUE;
8846 si->editor.el_dx_boulderdash = TRUE;
8848 si->editor.el_mirror_magic = TRUE;
8849 si->editor.el_deflektor = TRUE;
8851 si->editor.el_chars = TRUE;
8852 si->editor.el_steel_chars = TRUE;
8854 si->editor.el_classic = TRUE;
8855 si->editor.el_custom = TRUE;
8857 si->editor.el_user_defined = FALSE;
8858 si->editor.el_dynamic = TRUE;
8860 si->editor.el_headlines = TRUE;
8862 si->editor.show_element_token = FALSE;
8864 si->editor.use_template_for_new_levels = TRUE;
8866 si->shortcut.save_game = DEFAULT_KEY_SAVE_GAME;
8867 si->shortcut.load_game = DEFAULT_KEY_LOAD_GAME;
8868 si->shortcut.toggle_pause = DEFAULT_KEY_TOGGLE_PAUSE;
8870 si->shortcut.focus_player[0] = DEFAULT_KEY_FOCUS_PLAYER_1;
8871 si->shortcut.focus_player[1] = DEFAULT_KEY_FOCUS_PLAYER_2;
8872 si->shortcut.focus_player[2] = DEFAULT_KEY_FOCUS_PLAYER_3;
8873 si->shortcut.focus_player[3] = DEFAULT_KEY_FOCUS_PLAYER_4;
8874 si->shortcut.focus_player_all = DEFAULT_KEY_FOCUS_PLAYER_ALL;
8876 si->shortcut.tape_eject = DEFAULT_KEY_TAPE_EJECT;
8877 si->shortcut.tape_extra = DEFAULT_KEY_TAPE_EXTRA;
8878 si->shortcut.tape_stop = DEFAULT_KEY_TAPE_STOP;
8879 si->shortcut.tape_pause = DEFAULT_KEY_TAPE_PAUSE;
8880 si->shortcut.tape_record = DEFAULT_KEY_TAPE_RECORD;
8881 si->shortcut.tape_play = DEFAULT_KEY_TAPE_PLAY;
8883 si->shortcut.sound_simple = DEFAULT_KEY_SOUND_SIMPLE;
8884 si->shortcut.sound_loops = DEFAULT_KEY_SOUND_LOOPS;
8885 si->shortcut.sound_music = DEFAULT_KEY_SOUND_MUSIC;
8887 si->shortcut.snap_left = DEFAULT_KEY_SNAP_LEFT;
8888 si->shortcut.snap_right = DEFAULT_KEY_SNAP_RIGHT;
8889 si->shortcut.snap_up = DEFAULT_KEY_SNAP_UP;
8890 si->shortcut.snap_down = DEFAULT_KEY_SNAP_DOWN;
8892 for (i = 0; i < MAX_PLAYERS; i++)
8894 si->input[i].use_joystick = FALSE;
8895 si->input[i].joy.device_name=getStringCopy(getDeviceNameFromJoystickNr(i));
8896 si->input[i].joy.xleft = JOYSTICK_XLEFT;
8897 si->input[i].joy.xmiddle = JOYSTICK_XMIDDLE;
8898 si->input[i].joy.xright = JOYSTICK_XRIGHT;
8899 si->input[i].joy.yupper = JOYSTICK_YUPPER;
8900 si->input[i].joy.ymiddle = JOYSTICK_YMIDDLE;
8901 si->input[i].joy.ylower = JOYSTICK_YLOWER;
8902 si->input[i].joy.snap = (i == 0 ? JOY_BUTTON_1 : 0);
8903 si->input[i].joy.drop = (i == 0 ? JOY_BUTTON_2 : 0);
8904 si->input[i].key.left = (i == 0 ? DEFAULT_KEY_LEFT : KSYM_UNDEFINED);
8905 si->input[i].key.right = (i == 0 ? DEFAULT_KEY_RIGHT : KSYM_UNDEFINED);
8906 si->input[i].key.up = (i == 0 ? DEFAULT_KEY_UP : KSYM_UNDEFINED);
8907 si->input[i].key.down = (i == 0 ? DEFAULT_KEY_DOWN : KSYM_UNDEFINED);
8908 si->input[i].key.snap = (i == 0 ? DEFAULT_KEY_SNAP : KSYM_UNDEFINED);
8909 si->input[i].key.drop = (i == 0 ? DEFAULT_KEY_DROP : KSYM_UNDEFINED);
8912 si->system.sdl_videodriver = getStringCopy(ARG_DEFAULT);
8913 si->system.sdl_audiodriver = getStringCopy(ARG_DEFAULT);
8914 si->system.audio_fragment_size = DEFAULT_AUDIO_FRAGMENT_SIZE;
8916 si->internal.program_title = getStringCopy(PROGRAM_TITLE_STRING);
8917 si->internal.program_version = getStringCopy(getProgramRealVersionString());
8918 si->internal.program_author = getStringCopy(PROGRAM_AUTHOR_STRING);
8919 si->internal.program_email = getStringCopy(PROGRAM_EMAIL_STRING);
8920 si->internal.program_website = getStringCopy(PROGRAM_WEBSITE_STRING);
8921 si->internal.program_copyright = getStringCopy(PROGRAM_COPYRIGHT_STRING);
8922 si->internal.program_company = getStringCopy(PROGRAM_COMPANY_STRING);
8924 si->internal.program_icon_file = getStringCopy(PROGRAM_ICON_FILENAME);
8926 si->internal.default_graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
8927 si->internal.default_sounds_set = getStringCopy(SND_CLASSIC_SUBDIR);
8928 si->internal.default_music_set = getStringCopy(MUS_CLASSIC_SUBDIR);
8930 si->internal.fallback_graphics_file = getStringCopy(UNDEFINED_FILENAME);
8931 si->internal.fallback_sounds_file = getStringCopy(UNDEFINED_FILENAME);
8932 si->internal.fallback_music_file = getStringCopy(UNDEFINED_FILENAME);
8934 si->internal.default_level_series = getStringCopy(UNDEFINED_LEVELSET);
8935 si->internal.choose_from_top_leveldir = FALSE;
8936 si->internal.show_scaling_in_title = TRUE;
8938 si->internal.default_window_width = WIN_XSIZE_DEFAULT;
8939 si->internal.default_window_height = WIN_YSIZE_DEFAULT;
8941 si->debug.frame_delay[0] = DEFAULT_FRAME_DELAY_0;
8942 si->debug.frame_delay[1] = DEFAULT_FRAME_DELAY_1;
8943 si->debug.frame_delay[2] = DEFAULT_FRAME_DELAY_2;
8944 si->debug.frame_delay[3] = DEFAULT_FRAME_DELAY_3;
8945 si->debug.frame_delay[4] = DEFAULT_FRAME_DELAY_4;
8946 si->debug.frame_delay[5] = DEFAULT_FRAME_DELAY_5;
8947 si->debug.frame_delay[6] = DEFAULT_FRAME_DELAY_6;
8948 si->debug.frame_delay[7] = DEFAULT_FRAME_DELAY_7;
8949 si->debug.frame_delay[8] = DEFAULT_FRAME_DELAY_8;
8950 si->debug.frame_delay[9] = DEFAULT_FRAME_DELAY_9;
8952 si->debug.frame_delay_key[0] = DEFAULT_KEY_FRAME_DELAY_0;
8953 si->debug.frame_delay_key[1] = DEFAULT_KEY_FRAME_DELAY_1;
8954 si->debug.frame_delay_key[2] = DEFAULT_KEY_FRAME_DELAY_2;
8955 si->debug.frame_delay_key[3] = DEFAULT_KEY_FRAME_DELAY_3;
8956 si->debug.frame_delay_key[4] = DEFAULT_KEY_FRAME_DELAY_4;
8957 si->debug.frame_delay_key[5] = DEFAULT_KEY_FRAME_DELAY_5;
8958 si->debug.frame_delay_key[6] = DEFAULT_KEY_FRAME_DELAY_6;
8959 si->debug.frame_delay_key[7] = DEFAULT_KEY_FRAME_DELAY_7;
8960 si->debug.frame_delay_key[8] = DEFAULT_KEY_FRAME_DELAY_8;
8961 si->debug.frame_delay_key[9] = DEFAULT_KEY_FRAME_DELAY_9;
8963 si->debug.frame_delay_use_mod_key = DEFAULT_FRAME_DELAY_USE_MOD_KEY;
8964 si->debug.frame_delay_game_only = DEFAULT_FRAME_DELAY_GAME_ONLY;
8966 si->debug.show_frames_per_second = FALSE;
8968 si->options.verbose = FALSE;
8970 #if defined(PLATFORM_ANDROID)
8971 si->fullscreen = TRUE;
8975 static void setSetupInfoToDefaults_AutoSetup(struct SetupInfo *si)
8977 si->auto_setup.editor_zoom_tilesize = MINI_TILESIZE;
8980 static void setSetupInfoToDefaults_EditorCascade(struct SetupInfo *si)
8982 si->editor_cascade.el_bd = TRUE;
8983 si->editor_cascade.el_em = TRUE;
8984 si->editor_cascade.el_emc = TRUE;
8985 si->editor_cascade.el_rnd = TRUE;
8986 si->editor_cascade.el_sb = TRUE;
8987 si->editor_cascade.el_sp = TRUE;
8988 si->editor_cascade.el_dc = TRUE;
8989 si->editor_cascade.el_dx = TRUE;
8991 si->editor_cascade.el_mm = TRUE;
8992 si->editor_cascade.el_df = TRUE;
8994 si->editor_cascade.el_chars = FALSE;
8995 si->editor_cascade.el_steel_chars = FALSE;
8996 si->editor_cascade.el_ce = FALSE;
8997 si->editor_cascade.el_ge = FALSE;
8998 si->editor_cascade.el_ref = FALSE;
8999 si->editor_cascade.el_user = FALSE;
9000 si->editor_cascade.el_dynamic = FALSE;
9003 #define MAX_HIDE_SETUP_TOKEN_SIZE 20
9005 static char *getHideSetupToken(void *setup_value)
9007 static char hide_setup_token[MAX_HIDE_SETUP_TOKEN_SIZE];
9009 if (setup_value != NULL)
9010 snprintf(hide_setup_token, MAX_HIDE_SETUP_TOKEN_SIZE, "%p", setup_value);
9012 return hide_setup_token;
9015 void setHideSetupEntry(void *setup_value)
9017 char *hide_setup_token = getHideSetupToken(setup_value);
9019 if (setup_value != NULL)
9020 setHashEntry(hide_setup_hash, hide_setup_token, "");
9023 static void setHideSetupEntryRaw(char *token_text, void *setup_value_raw)
9025 /* !!! DIRTY WORKAROUND; TO BE FIXED AFTER THE MM ENGINE RELEASE !!! */
9026 void *setup_value = setup_value_raw - (void *)&si + (void *)&setup;
9028 setHideSetupEntry(setup_value);
9031 boolean hideSetupEntry(void *setup_value)
9033 char *hide_setup_token = getHideSetupToken(setup_value);
9035 return (setup_value != NULL &&
9036 getHashEntry(hide_setup_hash, hide_setup_token) != NULL);
9039 static void setSetupInfoFromTokenText(SetupFileHash *setup_file_hash,
9040 struct TokenInfo *token_info,
9041 int token_nr, char *token_text)
9043 char *token_hide_text = getStringCat2(token_text, ".hide");
9044 char *token_hide_value = getHashEntry(setup_file_hash, token_hide_text);
9046 /* set the value of this setup option in the setup option structure */
9047 setSetupInfo(token_info, token_nr, getHashEntry(setup_file_hash, token_text));
9049 /* check if this setup option should be hidden in the setup menu */
9050 if (token_hide_value != NULL && get_boolean_from_string(token_hide_value))
9051 setHideSetupEntryRaw(token_text, token_info[token_nr].value);
9054 static void setSetupInfoFromTokenInfo(SetupFileHash *setup_file_hash,
9055 struct TokenInfo *token_info,
9058 setSetupInfoFromTokenText(setup_file_hash, token_info, token_nr,
9059 token_info[token_nr].text);
9062 static void decodeSetupFileHash(SetupFileHash *setup_file_hash)
9066 if (!setup_file_hash)
9069 if (hide_setup_hash == NULL)
9070 hide_setup_hash = newSetupFileHash();
9074 for (i = 0; i < NUM_GLOBAL_SETUP_TOKENS; i++)
9075 setSetupInfoFromTokenInfo(setup_file_hash, global_setup_tokens, i);
9078 /* virtual buttons setup */
9079 setup.touch.grid_initialized = TRUE;
9080 for (i = 0; i < 2; i++)
9082 int grid_xsize = setup.touch.grid_xsize[i];
9083 int grid_ysize = setup.touch.grid_ysize[i];
9086 // if virtual buttons are not loaded from setup file, repeat initializing
9087 // virtual buttons grid with default values later when video is initialized
9088 if (grid_xsize == -1 ||
9091 setup.touch.grid_initialized = FALSE;
9096 for (y = 0; y < grid_ysize; y++)
9098 char token_string[MAX_LINE_LEN];
9100 sprintf(token_string, "touch.virtual_buttons.%d.%02d", i, y);
9102 char *value_string = getHashEntry(setup_file_hash, token_string);
9104 if (value_string == NULL)
9107 for (x = 0; x < grid_xsize; x++)
9109 char c = value_string[x];
9111 setup.touch.grid_button[i][x][y] =
9112 (c == '.' ? CHAR_GRID_BUTTON_NONE : c);
9119 for (i = 0; i < NUM_EDITOR_SETUP_TOKENS; i++)
9120 setSetupInfoFromTokenInfo(setup_file_hash, editor_setup_tokens, i);
9123 /* shortcut setup */
9124 ssi = setup.shortcut;
9125 for (i = 0; i < NUM_SHORTCUT_SETUP_TOKENS; i++)
9126 setSetupInfoFromTokenInfo(setup_file_hash, shortcut_setup_tokens, i);
9127 setup.shortcut = ssi;
9130 for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
9134 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
9136 sii = setup.input[pnr];
9137 for (i = 0; i < NUM_PLAYER_SETUP_TOKENS; i++)
9139 char full_token[100];
9141 sprintf(full_token, "%s%s", prefix, player_setup_tokens[i].text);
9142 setSetupInfoFromTokenText(setup_file_hash, player_setup_tokens, i,
9145 setup.input[pnr] = sii;
9150 for (i = 0; i < NUM_SYSTEM_SETUP_TOKENS; i++)
9151 setSetupInfoFromTokenInfo(setup_file_hash, system_setup_tokens, i);
9154 /* internal setup */
9155 sxi = setup.internal;
9156 for (i = 0; i < NUM_INTERNAL_SETUP_TOKENS; i++)
9157 setSetupInfoFromTokenInfo(setup_file_hash, internal_setup_tokens, i);
9158 setup.internal = sxi;
9162 for (i = 0; i < NUM_DEBUG_SETUP_TOKENS; i++)
9163 setSetupInfoFromTokenInfo(setup_file_hash, debug_setup_tokens, i);
9167 soi = setup.options;
9168 for (i = 0; i < NUM_OPTIONS_SETUP_TOKENS; i++)
9169 setSetupInfoFromTokenInfo(setup_file_hash, options_setup_tokens, i);
9170 setup.options = soi;
9172 setHideRelatedSetupEntries();
9175 static void decodeSetupFileHash_AutoSetup(SetupFileHash *setup_file_hash)
9179 if (!setup_file_hash)
9183 sasi = setup.auto_setup;
9184 for (i = 0; i < NUM_AUTO_SETUP_TOKENS; i++)
9185 setSetupInfo(auto_setup_tokens, i,
9186 getHashEntry(setup_file_hash,
9187 auto_setup_tokens[i].text));
9188 setup.auto_setup = sasi;
9191 static void decodeSetupFileHash_EditorCascade(SetupFileHash *setup_file_hash)
9195 if (!setup_file_hash)
9198 /* editor cascade setup */
9199 seci = setup.editor_cascade;
9200 for (i = 0; i < NUM_EDITOR_CASCADE_SETUP_TOKENS; i++)
9201 setSetupInfo(editor_cascade_setup_tokens, i,
9202 getHashEntry(setup_file_hash,
9203 editor_cascade_setup_tokens[i].text));
9204 setup.editor_cascade = seci;
9207 void LoadSetupFromFilename(char *filename)
9209 SetupFileHash *setup_file_hash = loadSetupFileHash(filename);
9211 if (setup_file_hash)
9213 decodeSetupFileHash(setup_file_hash);
9215 freeSetupFileHash(setup_file_hash);
9219 Error(ERR_DEBUG, "using default setup values");
9223 static void LoadSetup_SpecialPostProcessing()
9225 char *player_name_new;
9227 /* needed to work around problems with fixed length strings */
9228 player_name_new = get_corrected_login_name(setup.player_name);
9229 free(setup.player_name);
9230 setup.player_name = player_name_new;
9232 /* "scroll_delay: on(3) / off(0)" was replaced by scroll delay value */
9233 if (setup.scroll_delay == FALSE)
9235 setup.scroll_delay_value = MIN_SCROLL_DELAY;
9236 setup.scroll_delay = TRUE; /* now always "on" */
9239 /* make sure that scroll delay value stays inside valid range */
9240 setup.scroll_delay_value =
9241 MIN(MAX(MIN_SCROLL_DELAY, setup.scroll_delay_value), MAX_SCROLL_DELAY);
9248 /* always start with reliable default values */
9249 setSetupInfoToDefaults(&setup);
9251 /* try to load setup values from default setup file */
9252 filename = getDefaultSetupFilename();
9254 if (fileExists(filename))
9255 LoadSetupFromFilename(filename);
9257 /* try to load setup values from user setup file */
9258 filename = getSetupFilename();
9260 LoadSetupFromFilename(filename);
9262 LoadSetup_SpecialPostProcessing();
9265 void LoadSetup_AutoSetup()
9267 char *filename = getPath2(getSetupDir(), AUTOSETUP_FILENAME);
9268 SetupFileHash *setup_file_hash = NULL;
9270 /* always start with reliable default values */
9271 setSetupInfoToDefaults_AutoSetup(&setup);
9273 setup_file_hash = loadSetupFileHash(filename);
9275 if (setup_file_hash)
9277 decodeSetupFileHash_AutoSetup(setup_file_hash);
9279 freeSetupFileHash(setup_file_hash);
9285 void LoadSetup_EditorCascade()
9287 char *filename = getPath2(getSetupDir(), EDITORCASCADE_FILENAME);
9288 SetupFileHash *setup_file_hash = NULL;
9290 /* always start with reliable default values */
9291 setSetupInfoToDefaults_EditorCascade(&setup);
9293 setup_file_hash = loadSetupFileHash(filename);
9295 if (setup_file_hash)
9297 decodeSetupFileHash_EditorCascade(setup_file_hash);
9299 freeSetupFileHash(setup_file_hash);
9305 static void addGameControllerMappingToHash(SetupFileHash *mappings_hash,
9308 char mapping_guid[MAX_LINE_LEN];
9309 char *mapping_start, *mapping_end;
9311 // get GUID from game controller mapping line: copy complete line
9312 strncpy(mapping_guid, mapping_line, MAX_LINE_LEN - 1);
9313 mapping_guid[MAX_LINE_LEN - 1] = '\0';
9315 // get GUID from game controller mapping line: cut after GUID part
9316 mapping_start = strchr(mapping_guid, ',');
9317 if (mapping_start != NULL)
9318 *mapping_start = '\0';
9320 // cut newline from game controller mapping line
9321 mapping_end = strchr(mapping_line, '\n');
9322 if (mapping_end != NULL)
9323 *mapping_end = '\0';
9325 // add mapping entry to game controller mappings hash
9326 setHashEntry(mappings_hash, mapping_guid, mapping_line);
9329 static void LoadSetup_ReadGameControllerMappings(SetupFileHash *mappings_hash,
9334 if (!(file = fopen(filename, MODE_READ)))
9336 Error(ERR_WARN, "cannot read game controller mappings file '%s'", filename);
9343 char line[MAX_LINE_LEN];
9345 if (!fgets(line, MAX_LINE_LEN, file))
9348 addGameControllerMappingToHash(mappings_hash, line);
9356 char *filename = getSetupFilename();
9360 InitUserDataDirectory();
9362 if (!(file = fopen(filename, MODE_WRITE)))
9364 Error(ERR_WARN, "cannot write setup file '%s'", filename);
9368 fprintFileHeader(file, SETUP_FILENAME);
9372 for (i = 0; i < NUM_GLOBAL_SETUP_TOKENS; i++)
9374 /* just to make things nicer :) */
9375 if (i == SETUP_TOKEN_PLAYER_NAME + 1 ||
9376 i == SETUP_TOKEN_GRAPHICS_SET ||
9377 i == SETUP_TOKEN_VOLUME_SIMPLE ||
9378 i == SETUP_TOKEN_NETWORK_MODE ||
9379 i == SETUP_TOKEN_TOUCH_CONTROL_TYPE ||
9380 i == SETUP_TOKEN_TOUCH_GRID_XSIZE_0 ||
9381 i == SETUP_TOKEN_TOUCH_GRID_XSIZE_1)
9382 fprintf(file, "\n");
9384 fprintf(file, "%s\n", getSetupLine(global_setup_tokens, "", i));
9387 /* virtual buttons setup */
9388 for (i = 0; i < 2; i++)
9390 int grid_xsize = setup.touch.grid_xsize[i];
9391 int grid_ysize = setup.touch.grid_ysize[i];
9394 fprintf(file, "\n");
9396 for (y = 0; y < grid_ysize; y++)
9398 char token_string[MAX_LINE_LEN];
9399 char value_string[MAX_LINE_LEN];
9401 sprintf(token_string, "touch.virtual_buttons.%d.%02d", i, y);
9403 for (x = 0; x < grid_xsize; x++)
9405 char c = setup.touch.grid_button[i][x][y];
9407 value_string[x] = (c == CHAR_GRID_BUTTON_NONE ? '.' : c);
9410 value_string[grid_xsize] = '\0';
9412 fprintf(file, "%s\n", getFormattedSetupEntry(token_string, value_string));
9418 fprintf(file, "\n");
9419 for (i = 0; i < NUM_EDITOR_SETUP_TOKENS; i++)
9420 fprintf(file, "%s\n", getSetupLine(editor_setup_tokens, "", i));
9422 /* shortcut setup */
9423 ssi = setup.shortcut;
9424 fprintf(file, "\n");
9425 for (i = 0; i < NUM_SHORTCUT_SETUP_TOKENS; i++)
9426 fprintf(file, "%s\n", getSetupLine(shortcut_setup_tokens, "", i));
9429 for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
9433 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
9434 fprintf(file, "\n");
9436 sii = setup.input[pnr];
9437 for (i = 0; i < NUM_PLAYER_SETUP_TOKENS; i++)
9438 fprintf(file, "%s\n", getSetupLine(player_setup_tokens, prefix, i));
9443 fprintf(file, "\n");
9444 for (i = 0; i < NUM_SYSTEM_SETUP_TOKENS; i++)
9445 fprintf(file, "%s\n", getSetupLine(system_setup_tokens, "", i));
9447 /* internal setup */
9448 /* (internal setup values not saved to user setup file) */
9452 fprintf(file, "\n");
9453 for (i = 0; i < NUM_DEBUG_SETUP_TOKENS; i++)
9454 fprintf(file, "%s\n", getSetupLine(debug_setup_tokens, "", i));
9457 soi = setup.options;
9458 fprintf(file, "\n");
9459 for (i = 0; i < NUM_OPTIONS_SETUP_TOKENS; i++)
9460 fprintf(file, "%s\n", getSetupLine(options_setup_tokens, "", i));
9464 SetFilePermissions(filename, PERMS_PRIVATE);
9467 void SaveSetup_AutoSetup()
9469 char *filename = getPath2(getSetupDir(), AUTOSETUP_FILENAME);
9473 InitUserDataDirectory();
9475 if (!(file = fopen(filename, MODE_WRITE)))
9477 Error(ERR_WARN, "cannot write auto setup file '%s'", filename);
9482 fprintFileHeader(file, AUTOSETUP_FILENAME);
9484 sasi = setup.auto_setup;
9485 for (i = 0; i < NUM_AUTO_SETUP_TOKENS; i++)
9486 fprintf(file, "%s\n", getSetupLine(auto_setup_tokens, "", i));
9490 SetFilePermissions(filename, PERMS_PRIVATE);
9495 void SaveSetup_EditorCascade()
9497 char *filename = getPath2(getSetupDir(), EDITORCASCADE_FILENAME);
9501 InitUserDataDirectory();
9503 if (!(file = fopen(filename, MODE_WRITE)))
9505 Error(ERR_WARN, "cannot write editor cascade state file '%s'", filename);
9510 fprintFileHeader(file, EDITORCASCADE_FILENAME);
9512 seci = setup.editor_cascade;
9513 for (i = 0; i < NUM_EDITOR_CASCADE_SETUP_TOKENS; i++)
9514 fprintf(file, "%s\n", getSetupLine(editor_cascade_setup_tokens, "", i));
9518 SetFilePermissions(filename, PERMS_PRIVATE);
9523 static void SaveSetup_WriteGameControllerMappings(SetupFileHash *mappings_hash,
9528 if (!(file = fopen(filename, MODE_WRITE)))
9530 Error(ERR_WARN, "cannot write game controller mappings file '%s'",filename);
9535 BEGIN_HASH_ITERATION(mappings_hash, itr)
9537 fprintf(file, "%s\n", HASH_ITERATION_VALUE(itr));
9539 END_HASH_ITERATION(mappings_hash, itr)
9544 void SaveSetup_AddGameControllerMapping(char *mapping)
9546 char *filename = getPath2(getSetupDir(), GAMECONTROLLER_BASENAME);
9547 SetupFileHash *mappings_hash = newSetupFileHash();
9549 InitUserDataDirectory();
9551 // load existing personal game controller mappings
9552 LoadSetup_ReadGameControllerMappings(mappings_hash, filename);
9554 // add new mapping to personal game controller mappings
9555 addGameControllerMappingToHash(mappings_hash, mapping);
9557 // save updated personal game controller mappings
9558 SaveSetup_WriteGameControllerMappings(mappings_hash, filename);
9560 freeSetupFileHash(mappings_hash);
9564 void LoadCustomElementDescriptions()
9566 char *filename = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
9567 SetupFileHash *setup_file_hash;
9570 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
9572 if (element_info[i].custom_description != NULL)
9574 free(element_info[i].custom_description);
9575 element_info[i].custom_description = NULL;
9579 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
9582 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
9584 char *token = getStringCat2(element_info[i].token_name, ".name");
9585 char *value = getHashEntry(setup_file_hash, token);
9588 element_info[i].custom_description = getStringCopy(value);
9593 freeSetupFileHash(setup_file_hash);
9596 static int getElementFromToken(char *token)
9598 char *value = getHashEntry(element_token_hash, token);
9603 Error(ERR_WARN, "unknown element token '%s'", token);
9605 return EL_UNDEFINED;
9608 static int get_token_parameter_value(char *token, char *value_raw)
9612 if (token == NULL || value_raw == NULL)
9613 return ARG_UNDEFINED_VALUE;
9615 suffix = strrchr(token, '.');
9619 if (strEqual(suffix, ".element"))
9620 return getElementFromToken(value_raw);
9622 /* !!! USE CORRECT VALUE TYPE (currently works also for TYPE_BOOLEAN) !!! */
9623 return get_parameter_value(value_raw, suffix, TYPE_INTEGER);
9626 void InitMenuDesignSettings_Static()
9630 /* always start with reliable default values from static default config */
9631 for (i = 0; image_config_vars[i].token != NULL; i++)
9633 char *value = getHashEntry(image_config_hash, image_config_vars[i].token);
9636 *image_config_vars[i].value =
9637 get_token_parameter_value(image_config_vars[i].token, value);
9641 static void InitMenuDesignSettings_SpecialPreProcessing()
9645 /* the following initializes hierarchical values from static configuration */
9647 /* special case: initialize "ARG_DEFAULT" values in static default config */
9648 /* (e.g., initialize "[titlemessage].fade_mode" from "[title].fade_mode") */
9649 titlescreen_initial_first_default.fade_mode =
9650 title_initial_first_default.fade_mode;
9651 titlescreen_initial_first_default.fade_delay =
9652 title_initial_first_default.fade_delay;
9653 titlescreen_initial_first_default.post_delay =
9654 title_initial_first_default.post_delay;
9655 titlescreen_initial_first_default.auto_delay =
9656 title_initial_first_default.auto_delay;
9657 titlescreen_first_default.fade_mode = title_first_default.fade_mode;
9658 titlescreen_first_default.fade_delay = title_first_default.fade_delay;
9659 titlescreen_first_default.post_delay = title_first_default.post_delay;
9660 titlescreen_first_default.auto_delay = title_first_default.auto_delay;
9661 titlemessage_initial_first_default.fade_mode =
9662 title_initial_first_default.fade_mode;
9663 titlemessage_initial_first_default.fade_delay =
9664 title_initial_first_default.fade_delay;
9665 titlemessage_initial_first_default.post_delay =
9666 title_initial_first_default.post_delay;
9667 titlemessage_initial_first_default.auto_delay =
9668 title_initial_first_default.auto_delay;
9669 titlemessage_first_default.fade_mode = title_first_default.fade_mode;
9670 titlemessage_first_default.fade_delay = title_first_default.fade_delay;
9671 titlemessage_first_default.post_delay = title_first_default.post_delay;
9672 titlemessage_first_default.auto_delay = title_first_default.auto_delay;
9674 titlescreen_initial_default.fade_mode = title_initial_default.fade_mode;
9675 titlescreen_initial_default.fade_delay = title_initial_default.fade_delay;
9676 titlescreen_initial_default.post_delay = title_initial_default.post_delay;
9677 titlescreen_initial_default.auto_delay = title_initial_default.auto_delay;
9678 titlescreen_default.fade_mode = title_default.fade_mode;
9679 titlescreen_default.fade_delay = title_default.fade_delay;
9680 titlescreen_default.post_delay = title_default.post_delay;
9681 titlescreen_default.auto_delay = title_default.auto_delay;
9682 titlemessage_initial_default.fade_mode = title_initial_default.fade_mode;
9683 titlemessage_initial_default.fade_delay = title_initial_default.fade_delay;
9684 titlemessage_initial_default.post_delay = title_initial_default.post_delay;
9685 titlemessage_initial_default.auto_delay = title_initial_default.auto_delay;
9686 titlemessage_default.fade_mode = title_default.fade_mode;
9687 titlemessage_default.fade_delay = title_default.fade_delay;
9688 titlemessage_default.post_delay = title_default.post_delay;
9689 titlemessage_default.auto_delay = title_default.auto_delay;
9691 /* special case: initialize "ARG_DEFAULT" values in static default config */
9692 /* (e.g., init "titlemessage_1.fade_mode" from "[titlemessage].fade_mode") */
9693 for (i = 0; i < MAX_NUM_TITLE_MESSAGES; i++)
9695 titlescreen_initial_first[i] = titlescreen_initial_first_default;
9696 titlescreen_first[i] = titlescreen_first_default;
9697 titlemessage_initial_first[i] = titlemessage_initial_first_default;
9698 titlemessage_first[i] = titlemessage_first_default;
9700 titlescreen_initial[i] = titlescreen_initial_default;
9701 titlescreen[i] = titlescreen_default;
9702 titlemessage_initial[i] = titlemessage_initial_default;
9703 titlemessage[i] = titlemessage_default;
9706 /* special case: initialize "ARG_DEFAULT" values in static default config */
9707 /* (eg, init "menu.enter_screen.SCORES.xyz" from "menu.enter_screen.xyz") */
9708 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
9710 if (i == GFX_SPECIAL_ARG_TITLE) /* title values already initialized */
9713 menu.enter_screen[i] = menu.enter_screen[GFX_SPECIAL_ARG_DEFAULT];
9714 menu.leave_screen[i] = menu.leave_screen[GFX_SPECIAL_ARG_DEFAULT];
9715 menu.next_screen[i] = menu.next_screen[GFX_SPECIAL_ARG_DEFAULT];
9718 /* special case: initialize "ARG_DEFAULT" values in static default config */
9719 /* (eg, init "viewport.door_1.MAIN.xyz" from "viewport.door_1.xyz") */
9720 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
9722 viewport.window[i] = viewport.window[GFX_SPECIAL_ARG_DEFAULT];
9723 viewport.playfield[i] = viewport.playfield[GFX_SPECIAL_ARG_DEFAULT];
9724 viewport.door_1[i] = viewport.door_1[GFX_SPECIAL_ARG_DEFAULT];
9726 if (i == GFX_SPECIAL_ARG_EDITOR) /* editor values already initialized */
9729 viewport.door_2[i] = viewport.door_2[GFX_SPECIAL_ARG_DEFAULT];
9733 static void InitMenuDesignSettings_SpecialPostProcessing()
9737 struct XY *dst, *src;
9741 { &game.button.save, &game.button.stop },
9742 { &game.button.pause2, &game.button.pause },
9743 { &game.button.load, &game.button.play },
9744 { &game.button.undo, &game.button.stop },
9745 { &game.button.redo, &game.button.play },
9751 /* special case: initialize later added SETUP list size from LEVELS value */
9752 if (menu.list_size[GAME_MODE_SETUP] == -1)
9753 menu.list_size[GAME_MODE_SETUP] = menu.list_size[GAME_MODE_LEVELS];
9755 /* set default position for snapshot buttons to stop/pause/play buttons */
9756 for (i = 0; game_buttons_xy[i].dst != NULL; i++)
9757 if ((*game_buttons_xy[i].dst).x == -1 &&
9758 (*game_buttons_xy[i].dst).y == -1)
9759 *game_buttons_xy[i].dst = *game_buttons_xy[i].src;
9762 static void InitMenuDesignSettings_SpecialPostProcessing_AfterGraphics()
9766 struct XYTileSize *dst, *src;
9769 editor_buttons_xy[] =
9772 &editor.button.element_left, &editor.palette.element_left,
9773 IMG_GFX_EDITOR_BUTTON_ELEMENT_LEFT
9776 &editor.button.element_middle, &editor.palette.element_middle,
9777 IMG_GFX_EDITOR_BUTTON_ELEMENT_MIDDLE
9780 &editor.button.element_right, &editor.palette.element_right,
9781 IMG_GFX_EDITOR_BUTTON_ELEMENT_RIGHT
9788 /* set default position for element buttons to element graphics */
9789 for (i = 0; editor_buttons_xy[i].dst != NULL; i++)
9791 if ((*editor_buttons_xy[i].dst).x == -1 &&
9792 (*editor_buttons_xy[i].dst).y == -1)
9794 struct GraphicInfo *gd = &graphic_info[editor_buttons_xy[i].graphic];
9796 gd->width = gd->height = editor_buttons_xy[i].src->tile_size;
9798 *editor_buttons_xy[i].dst = *editor_buttons_xy[i].src;
9803 static void LoadMenuDesignSettingsFromFilename(char *filename)
9805 static struct TitleFadingInfo tfi;
9806 static struct TitleMessageInfo tmi;
9807 static struct TokenInfo title_tokens[] =
9809 { TYPE_INTEGER, &tfi.fade_mode, ".fade_mode" },
9810 { TYPE_INTEGER, &tfi.fade_delay, ".fade_delay" },
9811 { TYPE_INTEGER, &tfi.post_delay, ".post_delay" },
9812 { TYPE_INTEGER, &tfi.auto_delay, ".auto_delay" },
9816 static struct TokenInfo titlemessage_tokens[] =
9818 { TYPE_INTEGER, &tmi.x, ".x" },
9819 { TYPE_INTEGER, &tmi.y, ".y" },
9820 { TYPE_INTEGER, &tmi.width, ".width" },
9821 { TYPE_INTEGER, &tmi.height, ".height" },
9822 { TYPE_INTEGER, &tmi.chars, ".chars" },
9823 { TYPE_INTEGER, &tmi.lines, ".lines" },
9824 { TYPE_INTEGER, &tmi.align, ".align" },
9825 { TYPE_INTEGER, &tmi.valign, ".valign" },
9826 { TYPE_INTEGER, &tmi.font, ".font" },
9827 { TYPE_BOOLEAN, &tmi.autowrap, ".autowrap" },
9828 { TYPE_BOOLEAN, &tmi.centered, ".centered" },
9829 { TYPE_BOOLEAN, &tmi.parse_comments, ".parse_comments" },
9830 { TYPE_INTEGER, &tmi.sort_priority, ".sort_priority" },
9831 { TYPE_INTEGER, &tmi.fade_mode, ".fade_mode" },
9832 { TYPE_INTEGER, &tmi.fade_delay, ".fade_delay" },
9833 { TYPE_INTEGER, &tmi.post_delay, ".post_delay" },
9834 { TYPE_INTEGER, &tmi.auto_delay, ".auto_delay" },
9840 struct TitleFadingInfo *info;
9845 /* initialize first titles from "enter screen" definitions, if defined */
9846 { &title_initial_first_default, "menu.enter_screen.TITLE" },
9847 { &title_first_default, "menu.enter_screen.TITLE" },
9849 /* initialize title screens from "next screen" definitions, if defined */
9850 { &title_initial_default, "menu.next_screen.TITLE" },
9851 { &title_default, "menu.next_screen.TITLE" },
9857 struct TitleMessageInfo *array;
9860 titlemessage_arrays[] =
9862 /* initialize first titles from "enter screen" definitions, if defined */
9863 { titlescreen_initial_first, "menu.enter_screen.TITLE" },
9864 { titlescreen_first, "menu.enter_screen.TITLE" },
9865 { titlemessage_initial_first, "menu.enter_screen.TITLE" },
9866 { titlemessage_first, "menu.enter_screen.TITLE" },
9868 /* initialize titles from "next screen" definitions, if defined */
9869 { titlescreen_initial, "menu.next_screen.TITLE" },
9870 { titlescreen, "menu.next_screen.TITLE" },
9871 { titlemessage_initial, "menu.next_screen.TITLE" },
9872 { titlemessage, "menu.next_screen.TITLE" },
9874 /* overwrite titles with title definitions, if defined */
9875 { titlescreen_initial_first, "[title_initial]" },
9876 { titlescreen_first, "[title]" },
9877 { titlemessage_initial_first, "[title_initial]" },
9878 { titlemessage_first, "[title]" },
9880 { titlescreen_initial, "[title_initial]" },
9881 { titlescreen, "[title]" },
9882 { titlemessage_initial, "[title_initial]" },
9883 { titlemessage, "[title]" },
9885 /* overwrite titles with title screen/message definitions, if defined */
9886 { titlescreen_initial_first, "[titlescreen_initial]" },
9887 { titlescreen_first, "[titlescreen]" },
9888 { titlemessage_initial_first, "[titlemessage_initial]" },
9889 { titlemessage_first, "[titlemessage]" },
9891 { titlescreen_initial, "[titlescreen_initial]" },
9892 { titlescreen, "[titlescreen]" },
9893 { titlemessage_initial, "[titlemessage_initial]" },
9894 { titlemessage, "[titlemessage]" },
9898 SetupFileHash *setup_file_hash;
9901 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
9904 /* the following initializes hierarchical values from dynamic configuration */
9906 /* special case: initialize with default values that may be overwritten */
9907 /* (e.g., init "menu.draw_xoffset.INFO" from "menu.draw_xoffset") */
9908 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
9910 char *value_1 = getHashEntry(setup_file_hash, "menu.draw_xoffset");
9911 char *value_2 = getHashEntry(setup_file_hash, "menu.draw_yoffset");
9912 char *value_3 = getHashEntry(setup_file_hash, "menu.list_size");
9914 if (value_1 != NULL)
9915 menu.draw_xoffset[i] = get_integer_from_string(value_1);
9916 if (value_2 != NULL)
9917 menu.draw_yoffset[i] = get_integer_from_string(value_2);
9918 if (value_3 != NULL)
9919 menu.list_size[i] = get_integer_from_string(value_3);
9922 /* special case: initialize with default values that may be overwritten */
9923 /* (eg, init "menu.draw_xoffset.INFO[XXX]" from "menu.draw_xoffset.INFO") */
9924 for (i = 0; i < NUM_SPECIAL_GFX_INFO_ARGS; i++)
9926 char *value_1 = getHashEntry(setup_file_hash, "menu.draw_xoffset.INFO");
9927 char *value_2 = getHashEntry(setup_file_hash, "menu.draw_yoffset.INFO");
9929 if (value_1 != NULL)
9930 menu.draw_xoffset_info[i] = get_integer_from_string(value_1);
9931 if (value_2 != NULL)
9932 menu.draw_yoffset_info[i] = get_integer_from_string(value_2);
9934 if (i == GFX_SPECIAL_ARG_INFO_ELEMENTS)
9936 char *value_1 = getHashEntry(setup_file_hash, "menu.list_size.INFO");
9938 if (value_1 != NULL)
9939 menu.list_size_info[i] = get_integer_from_string(value_1);
9943 /* special case: initialize with default values that may be overwritten */
9944 /* (eg, init "menu.draw_xoffset.SETUP[XXX]" from "menu.draw_xoffset.SETUP") */
9945 for (i = 0; i < NUM_SPECIAL_GFX_SETUP_ARGS; i++)
9947 char *value_1 = getHashEntry(setup_file_hash, "menu.draw_xoffset.SETUP");
9948 char *value_2 = getHashEntry(setup_file_hash, "menu.draw_yoffset.SETUP");
9950 if (value_1 != NULL)
9951 menu.draw_xoffset_setup[i] = get_integer_from_string(value_1);
9952 if (value_2 != NULL)
9953 menu.draw_yoffset_setup[i] = get_integer_from_string(value_2);
9956 /* special case: initialize with default values that may be overwritten */
9957 /* (eg, init "menu.line_spacing.INFO[XXX]" from "menu.line_spacing.INFO") */
9958 for (i = 0; i < NUM_SPECIAL_GFX_INFO_ARGS; i++)
9960 char *value_1 = getHashEntry(setup_file_hash,"menu.left_spacing.INFO");
9961 char *value_2 = getHashEntry(setup_file_hash,"menu.right_spacing.INFO");
9962 char *value_3 = getHashEntry(setup_file_hash,"menu.top_spacing.INFO");
9963 char *value_4 = getHashEntry(setup_file_hash,"menu.bottom_spacing.INFO");
9964 char *value_5 = getHashEntry(setup_file_hash,"menu.paragraph_spacing.INFO");
9965 char *value_6 = getHashEntry(setup_file_hash,"menu.headline1_spacing.INFO");
9966 char *value_7 = getHashEntry(setup_file_hash,"menu.headline2_spacing.INFO");
9967 char *value_8 = getHashEntry(setup_file_hash,"menu.line_spacing.INFO");
9968 char *value_9 = getHashEntry(setup_file_hash,"menu.extra_spacing.INFO");
9970 if (value_1 != NULL)
9971 menu.left_spacing_info[i] = get_integer_from_string(value_1);
9972 if (value_2 != NULL)
9973 menu.right_spacing_info[i] = get_integer_from_string(value_2);
9974 if (value_3 != NULL)
9975 menu.top_spacing_info[i] = get_integer_from_string(value_3);
9976 if (value_4 != NULL)
9977 menu.bottom_spacing_info[i] = get_integer_from_string(value_4);
9978 if (value_5 != NULL)
9979 menu.paragraph_spacing_info[i] = get_integer_from_string(value_5);
9980 if (value_6 != NULL)
9981 menu.headline1_spacing_info[i] = get_integer_from_string(value_6);
9982 if (value_7 != NULL)
9983 menu.headline2_spacing_info[i] = get_integer_from_string(value_7);
9984 if (value_8 != NULL)
9985 menu.line_spacing_info[i] = get_integer_from_string(value_8);
9986 if (value_9 != NULL)
9987 menu.extra_spacing_info[i] = get_integer_from_string(value_9);
9990 /* special case: initialize with default values that may be overwritten */
9991 /* (eg, init "menu.enter_screen.SCORES.xyz" from "menu.enter_screen.xyz") */
9992 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
9994 char *token_1 = "menu.enter_screen.fade_mode";
9995 char *token_2 = "menu.enter_screen.fade_delay";
9996 char *token_3 = "menu.enter_screen.post_delay";
9997 char *token_4 = "menu.leave_screen.fade_mode";
9998 char *token_5 = "menu.leave_screen.fade_delay";
9999 char *token_6 = "menu.leave_screen.post_delay";
10000 char *token_7 = "menu.next_screen.fade_mode";
10001 char *token_8 = "menu.next_screen.fade_delay";
10002 char *token_9 = "menu.next_screen.post_delay";
10003 char *value_1 = getHashEntry(setup_file_hash, token_1);
10004 char *value_2 = getHashEntry(setup_file_hash, token_2);
10005 char *value_3 = getHashEntry(setup_file_hash, token_3);
10006 char *value_4 = getHashEntry(setup_file_hash, token_4);
10007 char *value_5 = getHashEntry(setup_file_hash, token_5);
10008 char *value_6 = getHashEntry(setup_file_hash, token_6);
10009 char *value_7 = getHashEntry(setup_file_hash, token_7);
10010 char *value_8 = getHashEntry(setup_file_hash, token_8);
10011 char *value_9 = getHashEntry(setup_file_hash, token_9);
10013 if (value_1 != NULL)
10014 menu.enter_screen[i].fade_mode = get_token_parameter_value(token_1,
10016 if (value_2 != NULL)
10017 menu.enter_screen[i].fade_delay = get_token_parameter_value(token_2,
10019 if (value_3 != NULL)
10020 menu.enter_screen[i].post_delay = get_token_parameter_value(token_3,
10022 if (value_4 != NULL)
10023 menu.leave_screen[i].fade_mode = get_token_parameter_value(token_4,
10025 if (value_5 != NULL)
10026 menu.leave_screen[i].fade_delay = get_token_parameter_value(token_5,
10028 if (value_6 != NULL)
10029 menu.leave_screen[i].post_delay = get_token_parameter_value(token_6,
10031 if (value_7 != NULL)
10032 menu.next_screen[i].fade_mode = get_token_parameter_value(token_7,
10034 if (value_8 != NULL)
10035 menu.next_screen[i].fade_delay = get_token_parameter_value(token_8,
10037 if (value_9 != NULL)
10038 menu.next_screen[i].post_delay = get_token_parameter_value(token_9,
10042 /* special case: initialize with default values that may be overwritten */
10043 /* (eg, init "viewport.door_1.MAIN.xyz" from "viewport.door_1.xyz") */
10044 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
10046 char *token_w1 = "viewport.window.width";
10047 char *token_w2 = "viewport.window.height";
10048 char *token_01 = "viewport.playfield.x";
10049 char *token_02 = "viewport.playfield.y";
10050 char *token_03 = "viewport.playfield.width";
10051 char *token_04 = "viewport.playfield.height";
10052 char *token_05 = "viewport.playfield.border_size";
10053 char *token_06 = "viewport.door_1.x";
10054 char *token_07 = "viewport.door_1.y";
10055 char *token_08 = "viewport.door_1.width";
10056 char *token_09 = "viewport.door_1.height";
10057 char *token_10 = "viewport.door_1.border_size";
10058 char *token_11 = "viewport.door_2.x";
10059 char *token_12 = "viewport.door_2.y";
10060 char *token_13 = "viewport.door_2.width";
10061 char *token_14 = "viewport.door_2.height";
10062 char *token_15 = "viewport.door_2.border_size";
10063 char *value_w1 = getHashEntry(setup_file_hash, token_w1);
10064 char *value_w2 = getHashEntry(setup_file_hash, token_w2);
10065 char *value_01 = getHashEntry(setup_file_hash, token_01);
10066 char *value_02 = getHashEntry(setup_file_hash, token_02);
10067 char *value_03 = getHashEntry(setup_file_hash, token_03);
10068 char *value_04 = getHashEntry(setup_file_hash, token_04);
10069 char *value_05 = getHashEntry(setup_file_hash, token_05);
10070 char *value_06 = getHashEntry(setup_file_hash, token_06);
10071 char *value_07 = getHashEntry(setup_file_hash, token_07);
10072 char *value_08 = getHashEntry(setup_file_hash, token_08);
10073 char *value_09 = getHashEntry(setup_file_hash, token_09);
10074 char *value_10 = getHashEntry(setup_file_hash, token_10);
10075 char *value_11 = getHashEntry(setup_file_hash, token_11);
10076 char *value_12 = getHashEntry(setup_file_hash, token_12);
10077 char *value_13 = getHashEntry(setup_file_hash, token_13);
10078 char *value_14 = getHashEntry(setup_file_hash, token_14);
10079 char *value_15 = getHashEntry(setup_file_hash, token_15);
10081 if (value_w1 != NULL)
10082 viewport.window[i].width = get_token_parameter_value(token_w1, value_w1);
10083 if (value_w2 != NULL)
10084 viewport.window[i].height = get_token_parameter_value(token_w2, value_w2);
10085 if (value_01 != NULL)
10086 viewport.playfield[i].x = get_token_parameter_value(token_01, value_01);
10087 if (value_02 != NULL)
10088 viewport.playfield[i].y = get_token_parameter_value(token_02, value_02);
10089 if (value_03 != NULL)
10090 viewport.playfield[i].width = get_token_parameter_value(token_03,
10092 if (value_04 != NULL)
10093 viewport.playfield[i].height = get_token_parameter_value(token_04,
10095 if (value_05 != NULL)
10096 viewport.playfield[i].border_size = get_token_parameter_value(token_05,
10098 if (value_06 != NULL)
10099 viewport.door_1[i].x = get_token_parameter_value(token_06, value_06);
10100 if (value_07 != NULL)
10101 viewport.door_1[i].y = get_token_parameter_value(token_07, value_07);
10102 if (value_08 != NULL)
10103 viewport.door_1[i].width = get_token_parameter_value(token_08, value_08);
10104 if (value_09 != NULL)
10105 viewport.door_1[i].height = get_token_parameter_value(token_09, value_09);
10106 if (value_10 != NULL)
10107 viewport.door_1[i].border_size = get_token_parameter_value(token_10,
10109 if (value_11 != NULL)
10110 viewport.door_2[i].x = get_token_parameter_value(token_11, value_11);
10111 if (value_12 != NULL)
10112 viewport.door_2[i].y = get_token_parameter_value(token_12, value_12);
10113 if (value_13 != NULL)
10114 viewport.door_2[i].width = get_token_parameter_value(token_13, value_13);
10115 if (value_14 != NULL)
10116 viewport.door_2[i].height = get_token_parameter_value(token_14, value_14);
10117 if (value_15 != NULL)
10118 viewport.door_1[i].border_size = get_token_parameter_value(token_15,
10122 /* special case: initialize with default values that may be overwritten */
10123 /* (e.g., init "[title].fade_mode" from "menu.next_screen.TITLE.fade_mode") */
10124 for (i = 0; title_info[i].info != NULL; i++)
10126 struct TitleFadingInfo *info = title_info[i].info;
10127 char *base_token = title_info[i].text;
10129 for (j = 0; title_tokens[j].type != -1; j++)
10131 char *token = getStringCat2(base_token, title_tokens[j].text);
10132 char *value = getHashEntry(setup_file_hash, token);
10136 int parameter_value = get_token_parameter_value(token, value);
10140 *(int *)title_tokens[j].value = (int)parameter_value;
10149 /* special case: initialize with default values that may be overwritten */
10150 /* (e.g., init "titlemessage_1.fade_mode" from "[titlemessage].fade_mode") */
10151 for (i = 0; titlemessage_arrays[i].array != NULL; i++)
10153 struct TitleMessageInfo *array = titlemessage_arrays[i].array;
10154 char *base_token = titlemessage_arrays[i].text;
10156 for (j = 0; titlemessage_tokens[j].type != -1; j++)
10158 char *token = getStringCat2(base_token, titlemessage_tokens[j].text);
10159 char *value = getHashEntry(setup_file_hash, token);
10163 int parameter_value = get_token_parameter_value(token, value);
10165 for (k = 0; k < MAX_NUM_TITLE_MESSAGES; k++)
10169 if (titlemessage_tokens[j].type == TYPE_INTEGER)
10170 *(int *)titlemessage_tokens[j].value = (int)parameter_value;
10172 *(boolean *)titlemessage_tokens[j].value = (boolean)parameter_value;
10182 /* read (and overwrite with) values that may be specified in config file */
10183 for (i = 0; image_config_vars[i].token != NULL; i++)
10185 char *value = getHashEntry(setup_file_hash, image_config_vars[i].token);
10187 /* (ignore definitions set to "[DEFAULT]" which are already initialized) */
10188 if (value != NULL && !strEqual(value, ARG_DEFAULT))
10189 *image_config_vars[i].value =
10190 get_token_parameter_value(image_config_vars[i].token, value);
10193 freeSetupFileHash(setup_file_hash);
10196 void LoadMenuDesignSettings()
10198 char *filename_base = UNDEFINED_FILENAME, *filename_local;
10200 InitMenuDesignSettings_Static();
10201 InitMenuDesignSettings_SpecialPreProcessing();
10203 if (!GFX_OVERRIDE_ARTWORK(ARTWORK_TYPE_GRAPHICS))
10205 /* first look for special settings configured in level series config */
10206 filename_base = getCustomArtworkLevelConfigFilename(ARTWORK_TYPE_GRAPHICS);
10208 if (fileExists(filename_base))
10209 LoadMenuDesignSettingsFromFilename(filename_base);
10212 filename_local = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
10214 if (filename_local != NULL && !strEqual(filename_base, filename_local))
10215 LoadMenuDesignSettingsFromFilename(filename_local);
10217 InitMenuDesignSettings_SpecialPostProcessing();
10220 void LoadMenuDesignSettings_AfterGraphics()
10222 InitMenuDesignSettings_SpecialPostProcessing_AfterGraphics();
10225 void LoadUserDefinedEditorElementList(int **elements, int *num_elements)
10227 char *filename = getEditorSetupFilename();
10228 SetupFileList *setup_file_list, *list;
10229 SetupFileHash *element_hash;
10230 int num_unknown_tokens = 0;
10233 if ((setup_file_list = loadSetupFileList(filename)) == NULL)
10236 element_hash = newSetupFileHash();
10238 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
10239 setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
10241 /* determined size may be larger than needed (due to unknown elements) */
10243 for (list = setup_file_list; list != NULL; list = list->next)
10246 /* add space for up to 3 more elements for padding that may be needed */
10247 *num_elements += 3;
10249 /* free memory for old list of elements, if needed */
10250 checked_free(*elements);
10252 /* allocate memory for new list of elements */
10253 *elements = checked_malloc(*num_elements * sizeof(int));
10256 for (list = setup_file_list; list != NULL; list = list->next)
10258 char *value = getHashEntry(element_hash, list->token);
10260 if (value == NULL) /* try to find obsolete token mapping */
10262 char *mapped_token = get_mapped_token(list->token);
10264 if (mapped_token != NULL)
10266 value = getHashEntry(element_hash, mapped_token);
10268 free(mapped_token);
10274 (*elements)[(*num_elements)++] = atoi(value);
10278 if (num_unknown_tokens == 0)
10280 Error(ERR_INFO_LINE, "-");
10281 Error(ERR_INFO, "warning: unknown token(s) found in config file:");
10282 Error(ERR_INFO, "- config file: '%s'", filename);
10284 num_unknown_tokens++;
10287 Error(ERR_INFO, "- token: '%s'", list->token);
10291 if (num_unknown_tokens > 0)
10292 Error(ERR_INFO_LINE, "-");
10294 while (*num_elements % 4) /* pad with empty elements, if needed */
10295 (*elements)[(*num_elements)++] = EL_EMPTY;
10297 freeSetupFileList(setup_file_list);
10298 freeSetupFileHash(element_hash);
10301 for (i = 0; i < *num_elements; i++)
10302 printf("editor: element '%s' [%d]\n",
10303 element_info[(*elements)[i]].token_name, (*elements)[i]);
10307 static struct MusicFileInfo *get_music_file_info_ext(char *basename, int music,
10310 SetupFileHash *setup_file_hash = NULL;
10311 struct MusicFileInfo tmp_music_file_info, *new_music_file_info;
10312 char *filename_music, *filename_prefix, *filename_info;
10318 token_to_value_ptr[] =
10320 { "title_header", &tmp_music_file_info.title_header },
10321 { "artist_header", &tmp_music_file_info.artist_header },
10322 { "album_header", &tmp_music_file_info.album_header },
10323 { "year_header", &tmp_music_file_info.year_header },
10325 { "title", &tmp_music_file_info.title },
10326 { "artist", &tmp_music_file_info.artist },
10327 { "album", &tmp_music_file_info.album },
10328 { "year", &tmp_music_file_info.year },
10334 filename_music = (is_sound ? getCustomSoundFilename(basename) :
10335 getCustomMusicFilename(basename));
10337 if (filename_music == NULL)
10340 /* ---------- try to replace file extension ---------- */
10342 filename_prefix = getStringCopy(filename_music);
10343 if (strrchr(filename_prefix, '.') != NULL)
10344 *strrchr(filename_prefix, '.') = '\0';
10345 filename_info = getStringCat2(filename_prefix, ".txt");
10347 if (fileExists(filename_info))
10348 setup_file_hash = loadSetupFileHash(filename_info);
10350 free(filename_prefix);
10351 free(filename_info);
10353 if (setup_file_hash == NULL)
10355 /* ---------- try to add file extension ---------- */
10357 filename_prefix = getStringCopy(filename_music);
10358 filename_info = getStringCat2(filename_prefix, ".txt");
10360 if (fileExists(filename_info))
10361 setup_file_hash = loadSetupFileHash(filename_info);
10363 free(filename_prefix);
10364 free(filename_info);
10367 if (setup_file_hash == NULL)
10370 /* ---------- music file info found ---------- */
10372 clear_mem(&tmp_music_file_info, sizeof(struct MusicFileInfo));
10374 for (i = 0; token_to_value_ptr[i].token != NULL; i++)
10376 char *value = getHashEntry(setup_file_hash, token_to_value_ptr[i].token);
10378 *token_to_value_ptr[i].value_ptr =
10379 getStringCopy(value != NULL && *value != '\0' ? value : UNKNOWN_NAME);
10382 tmp_music_file_info.basename = getStringCopy(basename);
10383 tmp_music_file_info.music = music;
10384 tmp_music_file_info.is_sound = is_sound;
10386 new_music_file_info = checked_malloc(sizeof(struct MusicFileInfo));
10387 *new_music_file_info = tmp_music_file_info;
10389 return new_music_file_info;
10392 static struct MusicFileInfo *get_music_file_info(char *basename, int music)
10394 return get_music_file_info_ext(basename, music, FALSE);
10397 static struct MusicFileInfo *get_sound_file_info(char *basename, int sound)
10399 return get_music_file_info_ext(basename, sound, TRUE);
10402 static boolean music_info_listed_ext(struct MusicFileInfo *list,
10403 char *basename, boolean is_sound)
10405 for (; list != NULL; list = list->next)
10406 if (list->is_sound == is_sound && strEqual(list->basename, basename))
10412 static boolean music_info_listed(struct MusicFileInfo *list, char *basename)
10414 return music_info_listed_ext(list, basename, FALSE);
10417 static boolean sound_info_listed(struct MusicFileInfo *list, char *basename)
10419 return music_info_listed_ext(list, basename, TRUE);
10422 void LoadMusicInfo()
10424 char *music_directory = getCustomMusicDirectory();
10425 int num_music = getMusicListSize();
10426 int num_music_noconf = 0;
10427 int num_sounds = getSoundListSize();
10429 DirectoryEntry *dir_entry;
10430 struct FileInfo *music, *sound;
10431 struct MusicFileInfo *next, **new;
10434 while (music_file_info != NULL)
10436 next = music_file_info->next;
10438 checked_free(music_file_info->basename);
10440 checked_free(music_file_info->title_header);
10441 checked_free(music_file_info->artist_header);
10442 checked_free(music_file_info->album_header);
10443 checked_free(music_file_info->year_header);
10445 checked_free(music_file_info->title);
10446 checked_free(music_file_info->artist);
10447 checked_free(music_file_info->album);
10448 checked_free(music_file_info->year);
10450 free(music_file_info);
10452 music_file_info = next;
10455 new = &music_file_info;
10457 for (i = 0; i < num_music; i++)
10459 music = getMusicListEntry(i);
10461 if (music->filename == NULL)
10464 if (strEqual(music->filename, UNDEFINED_FILENAME))
10467 /* a configured file may be not recognized as music */
10468 if (!FileIsMusic(music->filename))
10471 if (!music_info_listed(music_file_info, music->filename))
10473 *new = get_music_file_info(music->filename, i);
10476 new = &(*new)->next;
10480 if ((dir = openDirectory(music_directory)) == NULL)
10482 Error(ERR_WARN, "cannot read music directory '%s'", music_directory);
10486 while ((dir_entry = readDirectory(dir)) != NULL) /* loop all entries */
10488 char *basename = dir_entry->basename;
10489 boolean music_already_used = FALSE;
10492 /* skip all music files that are configured in music config file */
10493 for (i = 0; i < num_music; i++)
10495 music = getMusicListEntry(i);
10497 if (music->filename == NULL)
10500 if (strEqual(basename, music->filename))
10502 music_already_used = TRUE;
10507 if (music_already_used)
10510 if (!FileIsMusic(dir_entry->filename))
10513 if (!music_info_listed(music_file_info, basename))
10515 *new = get_music_file_info(basename, MAP_NOCONF_MUSIC(num_music_noconf));
10518 new = &(*new)->next;
10521 num_music_noconf++;
10524 closeDirectory(dir);
10526 for (i = 0; i < num_sounds; i++)
10528 sound = getSoundListEntry(i);
10530 if (sound->filename == NULL)
10533 if (strEqual(sound->filename, UNDEFINED_FILENAME))
10536 /* a configured file may be not recognized as sound */
10537 if (!FileIsSound(sound->filename))
10540 if (!sound_info_listed(music_file_info, sound->filename))
10542 *new = get_sound_file_info(sound->filename, i);
10544 new = &(*new)->next;
10549 void add_helpanim_entry(int element, int action, int direction, int delay,
10550 int *num_list_entries)
10552 struct HelpAnimInfo *new_list_entry;
10553 (*num_list_entries)++;
10556 checked_realloc(helpanim_info,
10557 *num_list_entries * sizeof(struct HelpAnimInfo));
10558 new_list_entry = &helpanim_info[*num_list_entries - 1];
10560 new_list_entry->element = element;
10561 new_list_entry->action = action;
10562 new_list_entry->direction = direction;
10563 new_list_entry->delay = delay;
10566 void print_unknown_token(char *filename, char *token, int token_nr)
10570 Error(ERR_INFO_LINE, "-");
10571 Error(ERR_INFO, "warning: unknown token(s) found in config file:");
10572 Error(ERR_INFO, "- config file: '%s'", filename);
10575 Error(ERR_INFO, "- token: '%s'", token);
10578 void print_unknown_token_end(int token_nr)
10581 Error(ERR_INFO_LINE, "-");
10584 void LoadHelpAnimInfo()
10586 char *filename = getHelpAnimFilename();
10587 SetupFileList *setup_file_list = NULL, *list;
10588 SetupFileHash *element_hash, *action_hash, *direction_hash;
10589 int num_list_entries = 0;
10590 int num_unknown_tokens = 0;
10593 if (fileExists(filename))
10594 setup_file_list = loadSetupFileList(filename);
10596 if (setup_file_list == NULL)
10598 /* use reliable default values from static configuration */
10599 SetupFileList *insert_ptr;
10601 insert_ptr = setup_file_list =
10602 newSetupFileList(helpanim_config[0].token,
10603 helpanim_config[0].value);
10605 for (i = 1; helpanim_config[i].token; i++)
10606 insert_ptr = addListEntry(insert_ptr,
10607 helpanim_config[i].token,
10608 helpanim_config[i].value);
10611 element_hash = newSetupFileHash();
10612 action_hash = newSetupFileHash();
10613 direction_hash = newSetupFileHash();
10615 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
10616 setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
10618 for (i = 0; i < NUM_ACTIONS; i++)
10619 setHashEntry(action_hash, element_action_info[i].suffix,
10620 i_to_a(element_action_info[i].value));
10622 /* do not store direction index (bit) here, but direction value! */
10623 for (i = 0; i < NUM_DIRECTIONS_FULL; i++)
10624 setHashEntry(direction_hash, element_direction_info[i].suffix,
10625 i_to_a(1 << element_direction_info[i].value));
10627 for (list = setup_file_list; list != NULL; list = list->next)
10629 char *element_token, *action_token, *direction_token;
10630 char *element_value, *action_value, *direction_value;
10631 int delay = atoi(list->value);
10633 if (strEqual(list->token, "end"))
10635 add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
10640 /* first try to break element into element/action/direction parts;
10641 if this does not work, also accept combined "element[.act][.dir]"
10642 elements (like "dynamite.active"), which are unique elements */
10644 if (strchr(list->token, '.') == NULL) /* token contains no '.' */
10646 element_value = getHashEntry(element_hash, list->token);
10647 if (element_value != NULL) /* element found */
10648 add_helpanim_entry(atoi(element_value), -1, -1, delay,
10649 &num_list_entries);
10652 /* no further suffixes found -- this is not an element */
10653 print_unknown_token(filename, list->token, num_unknown_tokens++);
10659 /* token has format "<prefix>.<something>" */
10661 action_token = strchr(list->token, '.'); /* suffix may be action ... */
10662 direction_token = action_token; /* ... or direction */
10664 element_token = getStringCopy(list->token);
10665 *strchr(element_token, '.') = '\0';
10667 element_value = getHashEntry(element_hash, element_token);
10669 if (element_value == NULL) /* this is no element */
10671 element_value = getHashEntry(element_hash, list->token);
10672 if (element_value != NULL) /* combined element found */
10673 add_helpanim_entry(atoi(element_value), -1, -1, delay,
10674 &num_list_entries);
10676 print_unknown_token(filename, list->token, num_unknown_tokens++);
10678 free(element_token);
10683 action_value = getHashEntry(action_hash, action_token);
10685 if (action_value != NULL) /* action found */
10687 add_helpanim_entry(atoi(element_value), atoi(action_value), -1, delay,
10688 &num_list_entries);
10690 free(element_token);
10695 direction_value = getHashEntry(direction_hash, direction_token);
10697 if (direction_value != NULL) /* direction found */
10699 add_helpanim_entry(atoi(element_value), -1, atoi(direction_value), delay,
10700 &num_list_entries);
10702 free(element_token);
10707 if (strchr(action_token + 1, '.') == NULL)
10709 /* no further suffixes found -- this is not an action nor direction */
10711 element_value = getHashEntry(element_hash, list->token);
10712 if (element_value != NULL) /* combined element found */
10713 add_helpanim_entry(atoi(element_value), -1, -1, delay,
10714 &num_list_entries);
10716 print_unknown_token(filename, list->token, num_unknown_tokens++);
10718 free(element_token);
10723 /* token has format "<prefix>.<suffix>.<something>" */
10725 direction_token = strchr(action_token + 1, '.');
10727 action_token = getStringCopy(action_token);
10728 *strchr(action_token + 1, '.') = '\0';
10730 action_value = getHashEntry(action_hash, action_token);
10732 if (action_value == NULL) /* this is no action */
10734 element_value = getHashEntry(element_hash, list->token);
10735 if (element_value != NULL) /* combined element found */
10736 add_helpanim_entry(atoi(element_value), -1, -1, delay,
10737 &num_list_entries);
10739 print_unknown_token(filename, list->token, num_unknown_tokens++);
10741 free(element_token);
10742 free(action_token);
10747 direction_value = getHashEntry(direction_hash, direction_token);
10749 if (direction_value != NULL) /* direction found */
10751 add_helpanim_entry(atoi(element_value), atoi(action_value),
10752 atoi(direction_value), delay, &num_list_entries);
10754 free(element_token);
10755 free(action_token);
10760 /* this is no direction */
10762 element_value = getHashEntry(element_hash, list->token);
10763 if (element_value != NULL) /* combined element found */
10764 add_helpanim_entry(atoi(element_value), -1, -1, delay,
10765 &num_list_entries);
10767 print_unknown_token(filename, list->token, num_unknown_tokens++);
10769 free(element_token);
10770 free(action_token);
10773 print_unknown_token_end(num_unknown_tokens);
10775 add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
10776 add_helpanim_entry(HELPANIM_LIST_END, -1, -1, -1, &num_list_entries);
10778 freeSetupFileList(setup_file_list);
10779 freeSetupFileHash(element_hash);
10780 freeSetupFileHash(action_hash);
10781 freeSetupFileHash(direction_hash);
10784 for (i = 0; i < num_list_entries; i++)
10785 printf("::: '%s': %d, %d, %d => %d\n",
10786 EL_NAME(helpanim_info[i].element),
10787 helpanim_info[i].element,
10788 helpanim_info[i].action,
10789 helpanim_info[i].direction,
10790 helpanim_info[i].delay);
10794 void LoadHelpTextInfo()
10796 char *filename = getHelpTextFilename();
10799 if (helptext_info != NULL)
10801 freeSetupFileHash(helptext_info);
10802 helptext_info = NULL;
10805 if (fileExists(filename))
10806 helptext_info = loadSetupFileHash(filename);
10808 if (helptext_info == NULL)
10810 /* use reliable default values from static configuration */
10811 helptext_info = newSetupFileHash();
10813 for (i = 0; helptext_config[i].token; i++)
10814 setHashEntry(helptext_info,
10815 helptext_config[i].token,
10816 helptext_config[i].value);
10820 BEGIN_HASH_ITERATION(helptext_info, itr)
10822 printf("::: '%s' => '%s'\n",
10823 HASH_ITERATION_TOKEN(itr), HASH_ITERATION_VALUE(itr));
10825 END_HASH_ITERATION(hash, itr)
10830 /* ------------------------------------------------------------------------- */
10831 /* convert levels */
10832 /* ------------------------------------------------------------------------- */
10834 #define MAX_NUM_CONVERT_LEVELS 1000
10836 void ConvertLevels()
10838 static LevelDirTree *convert_leveldir = NULL;
10839 static int convert_level_nr = -1;
10840 static int num_levels_handled = 0;
10841 static int num_levels_converted = 0;
10842 static boolean levels_failed[MAX_NUM_CONVERT_LEVELS];
10845 convert_leveldir = getTreeInfoFromIdentifier(leveldir_first,
10846 global.convert_leveldir);
10848 if (convert_leveldir == NULL)
10849 Error(ERR_EXIT, "no such level identifier: '%s'",
10850 global.convert_leveldir);
10852 leveldir_current = convert_leveldir;
10854 if (global.convert_level_nr != -1)
10856 convert_leveldir->first_level = global.convert_level_nr;
10857 convert_leveldir->last_level = global.convert_level_nr;
10860 convert_level_nr = convert_leveldir->first_level;
10862 PrintLine("=", 79);
10863 Print("Converting levels\n");
10864 PrintLine("-", 79);
10865 Print("Level series identifier: '%s'\n", convert_leveldir->identifier);
10866 Print("Level series name: '%s'\n", convert_leveldir->name);
10867 Print("Level series author: '%s'\n", convert_leveldir->author);
10868 Print("Number of levels: %d\n", convert_leveldir->levels);
10869 PrintLine("=", 79);
10872 for (i = 0; i < MAX_NUM_CONVERT_LEVELS; i++)
10873 levels_failed[i] = FALSE;
10875 while (convert_level_nr <= convert_leveldir->last_level)
10877 char *level_filename;
10880 level_nr = convert_level_nr++;
10882 Print("Level %03d: ", level_nr);
10884 LoadLevel(level_nr);
10885 if (level.no_level_file || level.no_valid_file)
10887 Print("(no level)\n");
10891 Print("converting level ... ");
10893 level_filename = getDefaultLevelFilename(level_nr);
10894 new_level = !fileExists(level_filename);
10898 SaveLevel(level_nr);
10900 num_levels_converted++;
10902 Print("converted.\n");
10906 if (level_nr >= 0 && level_nr < MAX_NUM_CONVERT_LEVELS)
10907 levels_failed[level_nr] = TRUE;
10909 Print("NOT CONVERTED -- LEVEL ALREADY EXISTS.\n");
10912 num_levels_handled++;
10916 PrintLine("=", 79);
10917 Print("Number of levels handled: %d\n", num_levels_handled);
10918 Print("Number of levels converted: %d (%d%%)\n", num_levels_converted,
10919 (num_levels_handled ?
10920 num_levels_converted * 100 / num_levels_handled : 0));
10921 PrintLine("-", 79);
10922 Print("Summary (for automatic parsing by scripts):\n");
10923 Print("LEVELDIR '%s', CONVERTED %d/%d (%d%%)",
10924 convert_leveldir->identifier, num_levels_converted,
10925 num_levels_handled,
10926 (num_levels_handled ?
10927 num_levels_converted * 100 / num_levels_handled : 0));
10929 if (num_levels_handled != num_levels_converted)
10931 Print(", FAILED:");
10932 for (i = 0; i < MAX_NUM_CONVERT_LEVELS; i++)
10933 if (levels_failed[i])
10938 PrintLine("=", 79);
10940 CloseAllAndExit(0);
10944 /* ------------------------------------------------------------------------- */
10945 /* create and save images for use in level sketches (raw BMP format) */
10946 /* ------------------------------------------------------------------------- */
10948 void CreateLevelSketchImages()
10950 #if defined(TARGET_SDL)
10955 InitElementPropertiesGfxElement();
10957 bitmap1 = CreateBitmap(TILEX, TILEY, DEFAULT_DEPTH);
10958 bitmap2 = CreateBitmap(MINI_TILEX, MINI_TILEY, DEFAULT_DEPTH);
10960 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
10962 Bitmap *src_bitmap;
10964 int element = getMappedElement(i);
10965 int graphic = el2edimg(element);
10966 char basename1[16];
10967 char basename2[16];
10971 sprintf(basename1, "%03d.bmp", i);
10972 sprintf(basename2, "%03ds.bmp", i);
10974 filename1 = getPath2(global.create_images_dir, basename1);
10975 filename2 = getPath2(global.create_images_dir, basename2);
10977 getFixedGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
10978 BlitBitmap(src_bitmap, bitmap1, src_x, src_y, TILEX, TILEY,
10981 if (SDL_SaveBMP(bitmap1->surface, filename1) != 0)
10982 Error(ERR_EXIT, "cannot save level sketch image file '%s'", filename1);
10984 getMiniGraphicSource(graphic, &src_bitmap, &src_x, &src_y);
10985 BlitBitmap(src_bitmap, bitmap2, src_x, src_y, MINI_TILEX, MINI_TILEY, 0, 0);
10987 if (SDL_SaveBMP(bitmap2->surface, filename2) != 0)
10988 Error(ERR_EXIT, "cannot save level sketch image file '%s'", filename2);
10994 printf("%03d `%03d%c", i, i, (i % 10 < 9 ? ' ' : '\n'));
10997 FreeBitmap(bitmap1);
10998 FreeBitmap(bitmap2);
11003 Error(ERR_INFO, "%d normal and small images created", NUM_FILE_ELEMENTS);
11005 CloseAllAndExit(0);
11010 /* ------------------------------------------------------------------------- */
11011 /* create and save images for custom and group elements (raw BMP format) */
11012 /* ------------------------------------------------------------------------- */
11014 void CreateCustomElementImages(char *directory)
11016 #if defined(TARGET_SDL)
11017 char *src_basename = "RocksCE-template.ilbm";
11018 char *dst_basename = "RocksCE.bmp";
11019 char *src_filename = getPath2(directory, src_basename);
11020 char *dst_filename = getPath2(directory, dst_basename);
11021 Bitmap *src_bitmap;
11023 int yoffset_ce = 0;
11024 int yoffset_ge = (TILEY * NUM_CUSTOM_ELEMENTS / 16);
11027 SDLInitVideoDisplay();
11029 ReCreateBitmap(&backbuffer, video.width, video.height);
11031 src_bitmap = LoadImage(src_filename);
11033 bitmap = CreateBitmap(TILEX * 16 * 2,
11034 TILEY * (NUM_CUSTOM_ELEMENTS + NUM_GROUP_ELEMENTS) / 16,
11037 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
11044 BlitBitmap(src_bitmap, bitmap, 0, 0, TILEX, TILEY,
11045 TILEX * x, TILEY * y + yoffset_ce);
11047 BlitBitmap(src_bitmap, bitmap, 0, TILEY,
11049 TILEX * x + TILEX * 16,
11050 TILEY * y + yoffset_ce);
11052 for (j = 2; j >= 0; j--)
11056 BlitBitmap(src_bitmap, bitmap,
11057 TILEX + c * 7, 0, 6, 10,
11058 TILEX * x + 6 + j * 7,
11059 TILEY * y + 11 + yoffset_ce);
11061 BlitBitmap(src_bitmap, bitmap,
11062 TILEX + c * 8, TILEY, 6, 10,
11063 TILEX * 16 + TILEX * x + 6 + j * 8,
11064 TILEY * y + 10 + yoffset_ce);
11070 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
11077 BlitBitmap(src_bitmap, bitmap, 0, 0, TILEX, TILEY,
11078 TILEX * x, TILEY * y + yoffset_ge);
11080 BlitBitmap(src_bitmap, bitmap, 0, TILEY,
11082 TILEX * x + TILEX * 16,
11083 TILEY * y + yoffset_ge);
11085 for (j = 1; j >= 0; j--)
11089 BlitBitmap(src_bitmap, bitmap, TILEX + c * 10, 11, 10, 10,
11090 TILEX * x + 6 + j * 10,
11091 TILEY * y + 11 + yoffset_ge);
11093 BlitBitmap(src_bitmap, bitmap,
11094 TILEX + c * 8, TILEY + 12, 6, 10,
11095 TILEX * 16 + TILEX * x + 10 + j * 8,
11096 TILEY * y + 10 + yoffset_ge);
11102 if (SDL_SaveBMP(bitmap->surface, dst_filename) != 0)
11103 Error(ERR_EXIT, "cannot save CE graphics file '%s'", dst_filename);
11105 FreeBitmap(bitmap);
11107 CloseAllAndExit(0);