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;
1858 level_file_info->basename = NULL;
1859 level_file_info->filename = NULL;
1862 int getMappedElement_SB(int, boolean);
1864 static void ActivateLevelTemplate()
1868 if (check_special_flags("load_xsb_to_ces"))
1870 /* fill smaller playfields with padding "beyond border wall" elements */
1871 if (level.fieldx < level_template.fieldx ||
1872 level.fieldy < level_template.fieldy)
1874 short field[level.fieldx][level.fieldy];
1875 int new_fieldx = MAX(level.fieldx, level_template.fieldx);
1876 int new_fieldy = MAX(level.fieldy, level_template.fieldy);
1877 int pos_fieldx = (new_fieldx - level.fieldx) / 2;
1878 int pos_fieldy = (new_fieldy - level.fieldy) / 2;
1880 /* copy old playfield (which is smaller than the visible area) */
1881 for (y = 0; y < level.fieldy; y++) for (x = 0; x < level.fieldx; x++)
1882 field[x][y] = level.field[x][y];
1884 /* fill new, larger playfield with "beyond border wall" elements */
1885 for (y = 0; y < new_fieldy; y++) for (x = 0; x < new_fieldx; x++)
1886 level.field[x][y] = getMappedElement_SB('_', TRUE);
1888 /* copy the old playfield to the middle of the new playfield */
1889 for (y = 0; y < level.fieldy; y++) for (x = 0; x < level.fieldx; x++)
1890 level.field[pos_fieldx + x][pos_fieldy + y] = field[x][y];
1892 level.fieldx = new_fieldx;
1893 level.fieldy = new_fieldy;
1897 /* Currently there is no special action needed to activate the template
1898 data, because 'element_info' property settings overwrite the original
1899 level data, while all other variables do not change. */
1901 /* Exception: 'from_level_template' elements in the original level playfield
1902 are overwritten with the corresponding elements at the same position in
1903 playfield from the level template. */
1905 for (x = 0; x < level.fieldx; x++)
1906 for (y = 0; y < level.fieldy; y++)
1907 if (level.field[x][y] == EL_FROM_LEVEL_TEMPLATE)
1908 level.field[x][y] = level_template.field[x][y];
1910 if (check_special_flags("load_xsb_to_ces"))
1912 struct LevelInfo level_backup = level;
1914 /* overwrite all individual level settings from template level settings */
1915 level = level_template;
1917 /* restore playfield size */
1918 level.fieldx = level_backup.fieldx;
1919 level.fieldy = level_backup.fieldy;
1921 /* restore playfield content */
1922 for (x = 0; x < level.fieldx; x++)
1923 for (y = 0; y < level.fieldy; y++)
1924 level.field[x][y] = level_backup.field[x][y];
1926 /* restore name and author from individual level */
1927 strcpy(level.name, level_backup.name);
1928 strcpy(level.author, level_backup.author);
1930 /* restore flag "use_custom_template" */
1931 level.use_custom_template = level_backup.use_custom_template;
1935 static char *getLevelFilenameFromBasename(char *basename)
1937 static char *filename[2] = { NULL, NULL };
1938 int pos = (strEqual(basename, LEVELTEMPLATE_FILENAME) ? 0 : 1);
1940 checked_free(filename[pos]);
1942 filename[pos] = getPath2(getCurrentLevelDir(), basename);
1944 return filename[pos];
1947 static int getFileTypeFromBasename(char *basename)
1949 /* !!! ALSO SEE COMMENT IN checkForPackageFromBasename() !!! */
1951 static char *filename = NULL;
1952 struct stat file_status;
1954 /* ---------- try to determine file type from filename ---------- */
1956 /* check for typical filename of a Supaplex level package file */
1957 if (strlen(basename) == 10 && strPrefixLower(basename, "levels.d"))
1958 return LEVEL_FILE_TYPE_SP;
1960 /* check for typical filename of a Diamond Caves II level package file */
1961 if (strSuffixLower(basename, ".dc") ||
1962 strSuffixLower(basename, ".dc2"))
1963 return LEVEL_FILE_TYPE_DC;
1965 /* check for typical filename of a Sokoban level package file */
1966 if (strSuffixLower(basename, ".xsb") &&
1967 strchr(basename, '%') == NULL)
1968 return LEVEL_FILE_TYPE_SB;
1970 /* ---------- try to determine file type from filesize ---------- */
1972 checked_free(filename);
1973 filename = getPath2(getCurrentLevelDir(), basename);
1975 if (stat(filename, &file_status) == 0)
1977 /* check for typical filesize of a Supaplex level package file */
1978 if (file_status.st_size == 170496)
1979 return LEVEL_FILE_TYPE_SP;
1982 return LEVEL_FILE_TYPE_UNKNOWN;
1985 static int getFileTypeFromMagicBytes(char *filename, int type)
1989 if ((file = openFile(filename, MODE_READ)))
1991 char chunk_name[CHUNK_ID_LEN + 1];
1993 getFileChunkBE(file, chunk_name, NULL);
1995 if (strEqual(chunk_name, "MMII") ||
1996 strEqual(chunk_name, "MIRR"))
1997 type = LEVEL_FILE_TYPE_MM;
2005 static boolean checkForPackageFromBasename(char *basename)
2007 /* !!! WON'T WORK ANYMORE IF getFileTypeFromBasename() ALSO DETECTS !!!
2008 !!! SINGLE LEVELS (CURRENTLY ONLY DETECTS LEVEL PACKAGES !!! */
2010 return (getFileTypeFromBasename(basename) != LEVEL_FILE_TYPE_UNKNOWN);
2013 static char *getSingleLevelBasenameExt(int nr, char *extension)
2015 static char basename[MAX_FILENAME_LEN];
2018 sprintf(basename, "%s", LEVELTEMPLATE_FILENAME);
2020 sprintf(basename, "%03d.%s", nr, extension);
2025 static char *getSingleLevelBasename(int nr)
2027 return getSingleLevelBasenameExt(nr, LEVELFILE_EXTENSION);
2030 static char *getPackedLevelBasename(int type)
2032 static char basename[MAX_FILENAME_LEN];
2033 char *directory = getCurrentLevelDir();
2035 DirectoryEntry *dir_entry;
2037 strcpy(basename, UNDEFINED_FILENAME); /* default: undefined file */
2039 if ((dir = openDirectory(directory)) == NULL)
2041 Error(ERR_WARN, "cannot read current level directory '%s'", directory);
2046 while ((dir_entry = readDirectory(dir)) != NULL) /* loop all entries */
2048 char *entry_basename = dir_entry->basename;
2049 int entry_type = getFileTypeFromBasename(entry_basename);
2051 if (entry_type != LEVEL_FILE_TYPE_UNKNOWN) /* found valid level package */
2053 if (type == LEVEL_FILE_TYPE_UNKNOWN ||
2056 strcpy(basename, entry_basename);
2063 closeDirectory(dir);
2068 static char *getSingleLevelFilename(int nr)
2070 return getLevelFilenameFromBasename(getSingleLevelBasename(nr));
2073 #if ENABLE_UNUSED_CODE
2074 static char *getPackedLevelFilename(int type)
2076 return getLevelFilenameFromBasename(getPackedLevelBasename(type));
2080 char *getDefaultLevelFilename(int nr)
2082 return getSingleLevelFilename(nr);
2085 #if ENABLE_UNUSED_CODE
2086 static void setLevelFileInfo_SingleLevelFilename(struct LevelFileInfo *lfi,
2090 lfi->packed = FALSE;
2091 lfi->basename = getSingleLevelBasename(lfi->nr, lfi->type);
2092 lfi->filename = getLevelFilenameFromBasename(lfi->basename);
2096 static void setLevelFileInfo_FormatLevelFilename(struct LevelFileInfo *lfi,
2097 int type, char *format, ...)
2099 static char basename[MAX_FILENAME_LEN];
2102 va_start(ap, format);
2103 vsprintf(basename, format, ap);
2107 lfi->packed = FALSE;
2108 lfi->basename = basename;
2109 lfi->filename = getLevelFilenameFromBasename(lfi->basename);
2112 static void setLevelFileInfo_PackedLevelFilename(struct LevelFileInfo *lfi,
2117 lfi->basename = getPackedLevelBasename(lfi->type);
2118 lfi->filename = getLevelFilenameFromBasename(lfi->basename);
2121 static int getFiletypeFromID(char *filetype_id)
2123 char *filetype_id_lower;
2124 int filetype = LEVEL_FILE_TYPE_UNKNOWN;
2127 if (filetype_id == NULL)
2128 return LEVEL_FILE_TYPE_UNKNOWN;
2130 filetype_id_lower = getStringToLower(filetype_id);
2132 for (i = 0; filetype_id_list[i].id != NULL; i++)
2134 char *id_lower = getStringToLower(filetype_id_list[i].id);
2136 if (strEqual(filetype_id_lower, id_lower))
2137 filetype = filetype_id_list[i].filetype;
2141 if (filetype != LEVEL_FILE_TYPE_UNKNOWN)
2145 free(filetype_id_lower);
2150 char *getLocalLevelTemplateFilename()
2152 return getDefaultLevelFilename(-1);
2155 char *getGlobalLevelTemplateFilename()
2157 /* global variable "leveldir_current" must be modified in the loop below */
2158 LevelDirTree *leveldir_current_last = leveldir_current;
2159 char *filename = NULL;
2161 /* check for template level in path from current to topmost tree node */
2163 while (leveldir_current != NULL)
2165 filename = getDefaultLevelFilename(-1);
2167 if (fileExists(filename))
2170 leveldir_current = leveldir_current->node_parent;
2173 /* restore global variable "leveldir_current" modified in above loop */
2174 leveldir_current = leveldir_current_last;
2179 static void determineLevelFileInfo_Filename(struct LevelFileInfo *lfi)
2183 /* special case: level number is negative => check for level template file */
2186 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
2187 getSingleLevelBasename(-1));
2189 /* replace local level template filename with global template filename */
2190 lfi->filename = getGlobalLevelTemplateFilename();
2192 /* no fallback if template file not existing */
2196 /* special case: check for file name/pattern specified in "levelinfo.conf" */
2197 if (leveldir_current->level_filename != NULL)
2199 int filetype = getFiletypeFromID(leveldir_current->level_filetype);
2201 setLevelFileInfo_FormatLevelFilename(lfi, filetype,
2202 leveldir_current->level_filename, nr);
2204 lfi->packed = checkForPackageFromBasename(leveldir_current->level_filename);
2206 if (fileExists(lfi->filename))
2209 else if (leveldir_current->level_filetype != NULL)
2211 int filetype = getFiletypeFromID(leveldir_current->level_filetype);
2213 /* check for specified native level file with standard file name */
2214 setLevelFileInfo_FormatLevelFilename(lfi, filetype,
2215 "%03d.%s", nr, LEVELFILE_EXTENSION);
2216 if (fileExists(lfi->filename))
2220 /* check for native Rocks'n'Diamonds level file */
2221 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
2222 "%03d.%s", nr, LEVELFILE_EXTENSION);
2223 if (fileExists(lfi->filename))
2226 /* check for Emerald Mine level file (V1) */
2227 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "a%c%c",
2228 'a' + (nr / 10) % 26, '0' + nr % 10);
2229 if (fileExists(lfi->filename))
2231 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "A%c%c",
2232 'A' + (nr / 10) % 26, '0' + nr % 10);
2233 if (fileExists(lfi->filename))
2236 /* check for Emerald Mine level file (V2 to V5) */
2237 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%d", nr);
2238 if (fileExists(lfi->filename))
2241 /* check for Emerald Mine level file (V6 / single mode) */
2242 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02ds", nr);
2243 if (fileExists(lfi->filename))
2245 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02dS", nr);
2246 if (fileExists(lfi->filename))
2249 /* check for Emerald Mine level file (V6 / teamwork mode) */
2250 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02dt", nr);
2251 if (fileExists(lfi->filename))
2253 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02dT", nr);
2254 if (fileExists(lfi->filename))
2257 /* check for various packed level file formats */
2258 setLevelFileInfo_PackedLevelFilename(lfi, LEVEL_FILE_TYPE_UNKNOWN);
2259 if (fileExists(lfi->filename))
2262 /* no known level file found -- use default values (and fail later) */
2263 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
2264 "%03d.%s", nr, LEVELFILE_EXTENSION);
2267 static void determineLevelFileInfo_Filetype(struct LevelFileInfo *lfi)
2269 if (lfi->type == LEVEL_FILE_TYPE_UNKNOWN)
2270 lfi->type = getFileTypeFromBasename(lfi->basename);
2272 if (lfi->type == LEVEL_FILE_TYPE_RND)
2273 lfi->type = getFileTypeFromMagicBytes(lfi->filename, lfi->type);
2276 static void setLevelFileInfo(struct LevelFileInfo *level_file_info, int nr)
2278 /* always start with reliable default values */
2279 setFileInfoToDefaults(level_file_info);
2281 level_file_info->nr = nr; /* set requested level number */
2283 determineLevelFileInfo_Filename(level_file_info);
2284 determineLevelFileInfo_Filetype(level_file_info);
2287 /* ------------------------------------------------------------------------- */
2288 /* functions for loading R'n'D level */
2289 /* ------------------------------------------------------------------------- */
2291 int getMappedElement(int element)
2293 /* remap some (historic, now obsolete) elements */
2297 case EL_PLAYER_OBSOLETE:
2298 element = EL_PLAYER_1;
2301 case EL_KEY_OBSOLETE:
2305 case EL_EM_KEY_1_FILE_OBSOLETE:
2306 element = EL_EM_KEY_1;
2309 case EL_EM_KEY_2_FILE_OBSOLETE:
2310 element = EL_EM_KEY_2;
2313 case EL_EM_KEY_3_FILE_OBSOLETE:
2314 element = EL_EM_KEY_3;
2317 case EL_EM_KEY_4_FILE_OBSOLETE:
2318 element = EL_EM_KEY_4;
2321 case EL_ENVELOPE_OBSOLETE:
2322 element = EL_ENVELOPE_1;
2330 if (element >= NUM_FILE_ELEMENTS)
2332 Error(ERR_WARN, "invalid level element %d", element);
2334 element = EL_UNKNOWN;
2342 int getMappedElementByVersion(int element, int game_version)
2344 /* remap some elements due to certain game version */
2346 if (game_version <= VERSION_IDENT(2,2,0,0))
2348 /* map game font elements */
2349 element = (element == EL_CHAR('[') ? EL_CHAR_AUMLAUT :
2350 element == EL_CHAR('\\') ? EL_CHAR_OUMLAUT :
2351 element == EL_CHAR(']') ? EL_CHAR_UUMLAUT :
2352 element == EL_CHAR('^') ? EL_CHAR_COPYRIGHT : element);
2355 if (game_version < VERSION_IDENT(3,0,0,0))
2357 /* map Supaplex gravity tube elements */
2358 element = (element == EL_SP_GRAVITY_PORT_LEFT ? EL_SP_PORT_LEFT :
2359 element == EL_SP_GRAVITY_PORT_RIGHT ? EL_SP_PORT_RIGHT :
2360 element == EL_SP_GRAVITY_PORT_UP ? EL_SP_PORT_UP :
2361 element == EL_SP_GRAVITY_PORT_DOWN ? EL_SP_PORT_DOWN :
2368 static int LoadLevel_VERS(File *file, int chunk_size, struct LevelInfo *level)
2370 level->file_version = getFileVersion(file);
2371 level->game_version = getFileVersion(file);
2376 static int LoadLevel_DATE(File *file, int chunk_size, struct LevelInfo *level)
2378 level->creation_date.year = getFile16BitBE(file);
2379 level->creation_date.month = getFile8Bit(file);
2380 level->creation_date.day = getFile8Bit(file);
2382 level->creation_date.src = DATE_SRC_LEVELFILE;
2387 static int LoadLevel_HEAD(File *file, int chunk_size, struct LevelInfo *level)
2389 int initial_player_stepsize;
2390 int initial_player_gravity;
2393 level->fieldx = getFile8Bit(file);
2394 level->fieldy = getFile8Bit(file);
2396 level->time = getFile16BitBE(file);
2397 level->gems_needed = getFile16BitBE(file);
2399 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
2400 level->name[i] = getFile8Bit(file);
2401 level->name[MAX_LEVEL_NAME_LEN] = 0;
2403 for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
2404 level->score[i] = getFile8Bit(file);
2406 level->num_yamyam_contents = STD_ELEMENT_CONTENTS;
2407 for (i = 0; i < STD_ELEMENT_CONTENTS; i++)
2408 for (y = 0; y < 3; y++)
2409 for (x = 0; x < 3; x++)
2410 level->yamyam_content[i].e[x][y] = getMappedElement(getFile8Bit(file));
2412 level->amoeba_speed = getFile8Bit(file);
2413 level->time_magic_wall = getFile8Bit(file);
2414 level->time_wheel = getFile8Bit(file);
2415 level->amoeba_content = getMappedElement(getFile8Bit(file));
2417 initial_player_stepsize = (getFile8Bit(file) == 1 ? STEPSIZE_FAST :
2420 for (i = 0; i < MAX_PLAYERS; i++)
2421 level->initial_player_stepsize[i] = initial_player_stepsize;
2423 initial_player_gravity = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2425 for (i = 0; i < MAX_PLAYERS; i++)
2426 level->initial_player_gravity[i] = initial_player_gravity;
2428 level->encoding_16bit_field = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2429 level->em_slippery_gems = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2431 level->use_custom_template = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2433 level->block_last_field = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2434 level->sp_block_last_field = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2435 level->can_move_into_acid_bits = getFile32BitBE(file);
2436 level->dont_collide_with_bits = getFile8Bit(file);
2438 level->use_spring_bug = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2439 level->use_step_counter = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2441 level->instant_relocation = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2442 level->can_pass_to_walkable = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2443 level->grow_into_diggable = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2445 level->game_engine_type = getFile8Bit(file);
2447 ReadUnusedBytesFromFile(file, LEVEL_CHUNK_HEAD_UNUSED);
2452 static int LoadLevel_NAME(File *file, int chunk_size, struct LevelInfo *level)
2456 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
2457 level->name[i] = getFile8Bit(file);
2458 level->name[MAX_LEVEL_NAME_LEN] = 0;
2463 static int LoadLevel_AUTH(File *file, int chunk_size, struct LevelInfo *level)
2467 for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
2468 level->author[i] = getFile8Bit(file);
2469 level->author[MAX_LEVEL_AUTHOR_LEN] = 0;
2474 static int LoadLevel_BODY(File *file, int chunk_size, struct LevelInfo *level)
2477 int chunk_size_expected = level->fieldx * level->fieldy;
2479 /* Note: "chunk_size" was wrong before version 2.0 when elements are
2480 stored with 16-bit encoding (and should be twice as big then).
2481 Even worse, playfield data was stored 16-bit when only yamyam content
2482 contained 16-bit elements and vice versa. */
2484 if (level->encoding_16bit_field && level->file_version >= FILE_VERSION_2_0)
2485 chunk_size_expected *= 2;
2487 if (chunk_size_expected != chunk_size)
2489 ReadUnusedBytesFromFile(file, chunk_size);
2490 return chunk_size_expected;
2493 for (y = 0; y < level->fieldy; y++)
2494 for (x = 0; x < level->fieldx; x++)
2495 level->field[x][y] =
2496 getMappedElement(level->encoding_16bit_field ? getFile16BitBE(file) :
2501 static int LoadLevel_CONT(File *file, int chunk_size, struct LevelInfo *level)
2504 int header_size = 4;
2505 int content_size = MAX_ELEMENT_CONTENTS * 3 * 3;
2506 int chunk_size_expected = header_size + content_size;
2508 /* Note: "chunk_size" was wrong before version 2.0 when elements are
2509 stored with 16-bit encoding (and should be twice as big then).
2510 Even worse, playfield data was stored 16-bit when only yamyam content
2511 contained 16-bit elements and vice versa. */
2513 if (level->encoding_16bit_field && level->file_version >= FILE_VERSION_2_0)
2514 chunk_size_expected += content_size;
2516 if (chunk_size_expected != chunk_size)
2518 ReadUnusedBytesFromFile(file, chunk_size);
2519 return chunk_size_expected;
2523 level->num_yamyam_contents = getFile8Bit(file);
2527 /* correct invalid number of content fields -- should never happen */
2528 if (level->num_yamyam_contents < 1 ||
2529 level->num_yamyam_contents > MAX_ELEMENT_CONTENTS)
2530 level->num_yamyam_contents = STD_ELEMENT_CONTENTS;
2532 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
2533 for (y = 0; y < 3; y++)
2534 for (x = 0; x < 3; x++)
2535 level->yamyam_content[i].e[x][y] =
2536 getMappedElement(level->encoding_16bit_field ?
2537 getFile16BitBE(file) : getFile8Bit(file));
2541 static int LoadLevel_CNT2(File *file, int chunk_size, struct LevelInfo *level)
2546 int content_array[MAX_ELEMENT_CONTENTS][3][3];
2548 element = getMappedElement(getFile16BitBE(file));
2549 num_contents = getFile8Bit(file);
2551 getFile8Bit(file); /* content x size (unused) */
2552 getFile8Bit(file); /* content y size (unused) */
2554 ReadUnusedBytesFromFile(file, LEVEL_CHUNK_CNT2_UNUSED);
2556 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
2557 for (y = 0; y < 3; y++)
2558 for (x = 0; x < 3; x++)
2559 content_array[i][x][y] = getMappedElement(getFile16BitBE(file));
2561 /* correct invalid number of content fields -- should never happen */
2562 if (num_contents < 1 || num_contents > MAX_ELEMENT_CONTENTS)
2563 num_contents = STD_ELEMENT_CONTENTS;
2565 if (element == EL_YAMYAM)
2567 level->num_yamyam_contents = num_contents;
2569 for (i = 0; i < num_contents; i++)
2570 for (y = 0; y < 3; y++)
2571 for (x = 0; x < 3; x++)
2572 level->yamyam_content[i].e[x][y] = content_array[i][x][y];
2574 else if (element == EL_BD_AMOEBA)
2576 level->amoeba_content = content_array[0][0][0];
2580 Error(ERR_WARN, "cannot load content for element '%d'", element);
2586 static int LoadLevel_CNT3(File *file, int chunk_size, struct LevelInfo *level)
2592 int chunk_size_expected;
2594 element = getMappedElement(getFile16BitBE(file));
2595 if (!IS_ENVELOPE(element))
2596 element = EL_ENVELOPE_1;
2598 envelope_nr = element - EL_ENVELOPE_1;
2600 envelope_len = getFile16BitBE(file);
2602 level->envelope[envelope_nr].xsize = getFile8Bit(file);
2603 level->envelope[envelope_nr].ysize = getFile8Bit(file);
2605 ReadUnusedBytesFromFile(file, LEVEL_CHUNK_CNT3_UNUSED);
2607 chunk_size_expected = LEVEL_CHUNK_CNT3_SIZE(envelope_len);
2608 if (chunk_size_expected != chunk_size)
2610 ReadUnusedBytesFromFile(file, chunk_size - LEVEL_CHUNK_CNT3_HEADER);
2611 return chunk_size_expected;
2614 for (i = 0; i < envelope_len; i++)
2615 level->envelope[envelope_nr].text[i] = getFile8Bit(file);
2620 static int LoadLevel_CUS1(File *file, int chunk_size, struct LevelInfo *level)
2622 int num_changed_custom_elements = getFile16BitBE(file);
2623 int chunk_size_expected = 2 + num_changed_custom_elements * 6;
2626 if (chunk_size_expected != chunk_size)
2628 ReadUnusedBytesFromFile(file, chunk_size - 2);
2629 return chunk_size_expected;
2632 for (i = 0; i < num_changed_custom_elements; i++)
2634 int element = getMappedElement(getFile16BitBE(file));
2635 int properties = getFile32BitBE(file);
2637 if (IS_CUSTOM_ELEMENT(element))
2638 element_info[element].properties[EP_BITFIELD_BASE_NR] = properties;
2640 Error(ERR_WARN, "invalid custom element number %d", element);
2642 /* older game versions that wrote level files with CUS1 chunks used
2643 different default push delay values (not yet stored in level file) */
2644 element_info[element].push_delay_fixed = 2;
2645 element_info[element].push_delay_random = 8;
2648 level->file_has_custom_elements = TRUE;
2653 static int LoadLevel_CUS2(File *file, int chunk_size, struct LevelInfo *level)
2655 int num_changed_custom_elements = getFile16BitBE(file);
2656 int chunk_size_expected = 2 + num_changed_custom_elements * 4;
2659 if (chunk_size_expected != chunk_size)
2661 ReadUnusedBytesFromFile(file, chunk_size - 2);
2662 return chunk_size_expected;
2665 for (i = 0; i < num_changed_custom_elements; i++)
2667 int element = getMappedElement(getFile16BitBE(file));
2668 int custom_target_element = getMappedElement(getFile16BitBE(file));
2670 if (IS_CUSTOM_ELEMENT(element))
2671 element_info[element].change->target_element = custom_target_element;
2673 Error(ERR_WARN, "invalid custom element number %d", element);
2676 level->file_has_custom_elements = TRUE;
2681 static int LoadLevel_CUS3(File *file, int chunk_size, struct LevelInfo *level)
2683 int num_changed_custom_elements = getFile16BitBE(file);
2684 int chunk_size_expected = LEVEL_CHUNK_CUS3_SIZE(num_changed_custom_elements);
2687 if (chunk_size_expected != chunk_size)
2689 ReadUnusedBytesFromFile(file, chunk_size - 2);
2690 return chunk_size_expected;
2693 for (i = 0; i < num_changed_custom_elements; i++)
2695 int element = getMappedElement(getFile16BitBE(file));
2696 struct ElementInfo *ei = &element_info[element];
2697 unsigned int event_bits;
2699 if (!IS_CUSTOM_ELEMENT(element))
2701 Error(ERR_WARN, "invalid custom element number %d", element);
2703 element = EL_INTERNAL_DUMMY;
2706 for (j = 0; j < MAX_ELEMENT_NAME_LEN; j++)
2707 ei->description[j] = getFile8Bit(file);
2708 ei->description[MAX_ELEMENT_NAME_LEN] = 0;
2710 ei->properties[EP_BITFIELD_BASE_NR] = getFile32BitBE(file);
2712 /* some free bytes for future properties and padding */
2713 ReadUnusedBytesFromFile(file, 7);
2715 ei->use_gfx_element = getFile8Bit(file);
2716 ei->gfx_element_initial = getMappedElement(getFile16BitBE(file));
2718 ei->collect_score_initial = getFile8Bit(file);
2719 ei->collect_count_initial = getFile8Bit(file);
2721 ei->push_delay_fixed = getFile16BitBE(file);
2722 ei->push_delay_random = getFile16BitBE(file);
2723 ei->move_delay_fixed = getFile16BitBE(file);
2724 ei->move_delay_random = getFile16BitBE(file);
2726 ei->move_pattern = getFile16BitBE(file);
2727 ei->move_direction_initial = getFile8Bit(file);
2728 ei->move_stepsize = getFile8Bit(file);
2730 for (y = 0; y < 3; y++)
2731 for (x = 0; x < 3; x++)
2732 ei->content.e[x][y] = getMappedElement(getFile16BitBE(file));
2734 event_bits = getFile32BitBE(file);
2735 for (j = 0; j < NUM_CHANGE_EVENTS; j++)
2736 if (event_bits & (1 << j))
2737 ei->change->has_event[j] = TRUE;
2739 ei->change->target_element = getMappedElement(getFile16BitBE(file));
2741 ei->change->delay_fixed = getFile16BitBE(file);
2742 ei->change->delay_random = getFile16BitBE(file);
2743 ei->change->delay_frames = getFile16BitBE(file);
2745 ei->change->initial_trigger_element= getMappedElement(getFile16BitBE(file));
2747 ei->change->explode = getFile8Bit(file);
2748 ei->change->use_target_content = getFile8Bit(file);
2749 ei->change->only_if_complete = getFile8Bit(file);
2750 ei->change->use_random_replace = getFile8Bit(file);
2752 ei->change->random_percentage = getFile8Bit(file);
2753 ei->change->replace_when = getFile8Bit(file);
2755 for (y = 0; y < 3; y++)
2756 for (x = 0; x < 3; x++)
2757 ei->change->target_content.e[x][y] =
2758 getMappedElement(getFile16BitBE(file));
2760 ei->slippery_type = getFile8Bit(file);
2762 /* some free bytes for future properties and padding */
2763 ReadUnusedBytesFromFile(file, LEVEL_CPART_CUS3_UNUSED);
2765 /* mark that this custom element has been modified */
2766 ei->modified_settings = TRUE;
2769 level->file_has_custom_elements = TRUE;
2774 static int LoadLevel_CUS4(File *file, int chunk_size, struct LevelInfo *level)
2776 struct ElementInfo *ei;
2777 int chunk_size_expected;
2781 /* ---------- custom element base property values (96 bytes) ------------- */
2783 element = getMappedElement(getFile16BitBE(file));
2785 if (!IS_CUSTOM_ELEMENT(element))
2787 Error(ERR_WARN, "invalid custom element number %d", element);
2789 ReadUnusedBytesFromFile(file, chunk_size - 2);
2793 ei = &element_info[element];
2795 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
2796 ei->description[i] = getFile8Bit(file);
2797 ei->description[MAX_ELEMENT_NAME_LEN] = 0;
2799 ei->properties[EP_BITFIELD_BASE_NR] = getFile32BitBE(file);
2801 ReadUnusedBytesFromFile(file, 4); /* reserved for more base properties */
2803 ei->num_change_pages = getFile8Bit(file);
2805 chunk_size_expected = LEVEL_CHUNK_CUS4_SIZE(ei->num_change_pages);
2806 if (chunk_size_expected != chunk_size)
2808 ReadUnusedBytesFromFile(file, chunk_size - 43);
2809 return chunk_size_expected;
2812 ei->ce_value_fixed_initial = getFile16BitBE(file);
2813 ei->ce_value_random_initial = getFile16BitBE(file);
2814 ei->use_last_ce_value = getFile8Bit(file);
2816 ei->use_gfx_element = getFile8Bit(file);
2817 ei->gfx_element_initial = getMappedElement(getFile16BitBE(file));
2819 ei->collect_score_initial = getFile8Bit(file);
2820 ei->collect_count_initial = getFile8Bit(file);
2822 ei->drop_delay_fixed = getFile8Bit(file);
2823 ei->push_delay_fixed = getFile8Bit(file);
2824 ei->drop_delay_random = getFile8Bit(file);
2825 ei->push_delay_random = getFile8Bit(file);
2826 ei->move_delay_fixed = getFile16BitBE(file);
2827 ei->move_delay_random = getFile16BitBE(file);
2829 /* bits 0 - 15 of "move_pattern" ... */
2830 ei->move_pattern = getFile16BitBE(file);
2831 ei->move_direction_initial = getFile8Bit(file);
2832 ei->move_stepsize = getFile8Bit(file);
2834 ei->slippery_type = getFile8Bit(file);
2836 for (y = 0; y < 3; y++)
2837 for (x = 0; x < 3; x++)
2838 ei->content.e[x][y] = getMappedElement(getFile16BitBE(file));
2840 ei->move_enter_element = getMappedElement(getFile16BitBE(file));
2841 ei->move_leave_element = getMappedElement(getFile16BitBE(file));
2842 ei->move_leave_type = getFile8Bit(file);
2844 /* ... bits 16 - 31 of "move_pattern" (not nice, but downward compatible) */
2845 ei->move_pattern |= (getFile16BitBE(file) << 16);
2847 ei->access_direction = getFile8Bit(file);
2849 ei->explosion_delay = getFile8Bit(file);
2850 ei->ignition_delay = getFile8Bit(file);
2851 ei->explosion_type = getFile8Bit(file);
2853 /* some free bytes for future custom property values and padding */
2854 ReadUnusedBytesFromFile(file, 1);
2856 /* ---------- change page property values (48 bytes) --------------------- */
2858 setElementChangePages(ei, ei->num_change_pages);
2860 for (i = 0; i < ei->num_change_pages; i++)
2862 struct ElementChangeInfo *change = &ei->change_page[i];
2863 unsigned int event_bits;
2865 /* always start with reliable default values */
2866 setElementChangeInfoToDefaults(change);
2868 /* bits 0 - 31 of "has_event[]" ... */
2869 event_bits = getFile32BitBE(file);
2870 for (j = 0; j < MIN(NUM_CHANGE_EVENTS, 32); j++)
2871 if (event_bits & (1 << j))
2872 change->has_event[j] = TRUE;
2874 change->target_element = getMappedElement(getFile16BitBE(file));
2876 change->delay_fixed = getFile16BitBE(file);
2877 change->delay_random = getFile16BitBE(file);
2878 change->delay_frames = getFile16BitBE(file);
2880 change->initial_trigger_element = getMappedElement(getFile16BitBE(file));
2882 change->explode = getFile8Bit(file);
2883 change->use_target_content = getFile8Bit(file);
2884 change->only_if_complete = getFile8Bit(file);
2885 change->use_random_replace = getFile8Bit(file);
2887 change->random_percentage = getFile8Bit(file);
2888 change->replace_when = getFile8Bit(file);
2890 for (y = 0; y < 3; y++)
2891 for (x = 0; x < 3; x++)
2892 change->target_content.e[x][y]= getMappedElement(getFile16BitBE(file));
2894 change->can_change = getFile8Bit(file);
2896 change->trigger_side = getFile8Bit(file);
2898 change->trigger_player = getFile8Bit(file);
2899 change->trigger_page = getFile8Bit(file);
2901 change->trigger_page = (change->trigger_page == CH_PAGE_ANY_FILE ?
2902 CH_PAGE_ANY : (1 << change->trigger_page));
2904 change->has_action = getFile8Bit(file);
2905 change->action_type = getFile8Bit(file);
2906 change->action_mode = getFile8Bit(file);
2907 change->action_arg = getFile16BitBE(file);
2909 /* ... bits 32 - 39 of "has_event[]" (not nice, but downward compatible) */
2910 event_bits = getFile8Bit(file);
2911 for (j = 32; j < NUM_CHANGE_EVENTS; j++)
2912 if (event_bits & (1 << (j - 32)))
2913 change->has_event[j] = TRUE;
2916 /* mark this custom element as modified */
2917 ei->modified_settings = TRUE;
2919 level->file_has_custom_elements = TRUE;
2924 static int LoadLevel_GRP1(File *file, int chunk_size, struct LevelInfo *level)
2926 struct ElementInfo *ei;
2927 struct ElementGroupInfo *group;
2931 element = getMappedElement(getFile16BitBE(file));
2933 if (!IS_GROUP_ELEMENT(element))
2935 Error(ERR_WARN, "invalid group element number %d", element);
2937 ReadUnusedBytesFromFile(file, chunk_size - 2);
2941 ei = &element_info[element];
2943 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
2944 ei->description[i] = getFile8Bit(file);
2945 ei->description[MAX_ELEMENT_NAME_LEN] = 0;
2947 group = element_info[element].group;
2949 group->num_elements = getFile8Bit(file);
2951 ei->use_gfx_element = getFile8Bit(file);
2952 ei->gfx_element_initial = getMappedElement(getFile16BitBE(file));
2954 group->choice_mode = getFile8Bit(file);
2956 /* some free bytes for future values and padding */
2957 ReadUnusedBytesFromFile(file, 3);
2959 for (i = 0; i < MAX_ELEMENTS_IN_GROUP; i++)
2960 group->element[i] = getMappedElement(getFile16BitBE(file));
2962 /* mark this group element as modified */
2963 element_info[element].modified_settings = TRUE;
2965 level->file_has_custom_elements = TRUE;
2970 static int LoadLevel_MicroChunk(File *file, struct LevelFileConfigInfo *conf,
2971 int element, int real_element)
2973 int micro_chunk_size = 0;
2974 int conf_type = getFile8Bit(file);
2975 int byte_mask = conf_type & CONF_MASK_BYTES;
2976 boolean element_found = FALSE;
2979 micro_chunk_size += 1;
2981 if (byte_mask == CONF_MASK_MULTI_BYTES)
2983 int num_bytes = getFile16BitBE(file);
2984 byte *buffer = checked_malloc(num_bytes);
2986 ReadBytesFromFile(file, buffer, num_bytes);
2988 for (i = 0; conf[i].data_type != -1; i++)
2990 if (conf[i].element == element &&
2991 conf[i].conf_type == conf_type)
2993 int data_type = conf[i].data_type;
2994 int num_entities = num_bytes / CONF_ENTITY_NUM_BYTES(data_type);
2995 int max_num_entities = conf[i].max_num_entities;
2997 if (num_entities > max_num_entities)
3000 "truncating number of entities for element %d from %d to %d",
3001 element, num_entities, max_num_entities);
3003 num_entities = max_num_entities;
3006 if (num_entities == 0 && (data_type == TYPE_ELEMENT_LIST ||
3007 data_type == TYPE_CONTENT_LIST))
3009 /* for element and content lists, zero entities are not allowed */
3010 Error(ERR_WARN, "found empty list of entities for element %d",
3013 /* do not set "num_entities" here to prevent reading behind buffer */
3015 *(int *)(conf[i].num_entities) = 1; /* at least one is required */
3019 *(int *)(conf[i].num_entities) = num_entities;
3022 element_found = TRUE;
3024 if (data_type == TYPE_STRING)
3026 char *string = (char *)(conf[i].value);
3029 for (j = 0; j < max_num_entities; j++)
3030 string[j] = (j < num_entities ? buffer[j] : '\0');
3032 else if (data_type == TYPE_ELEMENT_LIST)
3034 int *element_array = (int *)(conf[i].value);
3037 for (j = 0; j < num_entities; j++)
3039 getMappedElement(CONF_ELEMENTS_ELEMENT(buffer, j));
3041 else if (data_type == TYPE_CONTENT_LIST)
3043 struct Content *content= (struct Content *)(conf[i].value);
3046 for (c = 0; c < num_entities; c++)
3047 for (y = 0; y < 3; y++)
3048 for (x = 0; x < 3; x++)
3049 content[c].e[x][y] =
3050 getMappedElement(CONF_CONTENTS_ELEMENT(buffer, c, x, y));
3053 element_found = FALSE;
3059 checked_free(buffer);
3061 micro_chunk_size += 2 + num_bytes;
3063 else /* constant size configuration data (1, 2 or 4 bytes) */
3065 int value = (byte_mask == CONF_MASK_1_BYTE ? getFile8Bit (file) :
3066 byte_mask == CONF_MASK_2_BYTE ? getFile16BitBE(file) :
3067 byte_mask == CONF_MASK_4_BYTE ? getFile32BitBE(file) : 0);
3069 for (i = 0; conf[i].data_type != -1; i++)
3071 if (conf[i].element == element &&
3072 conf[i].conf_type == conf_type)
3074 int data_type = conf[i].data_type;
3076 if (data_type == TYPE_ELEMENT)
3077 value = getMappedElement(value);
3079 if (data_type == TYPE_BOOLEAN)
3080 *(boolean *)(conf[i].value) = value;
3082 *(int *) (conf[i].value) = value;
3084 element_found = TRUE;
3090 micro_chunk_size += CONF_VALUE_NUM_BYTES(byte_mask);
3095 char *error_conf_chunk_bytes =
3096 (byte_mask == CONF_MASK_1_BYTE ? "CONF_VALUE_8_BIT" :
3097 byte_mask == CONF_MASK_2_BYTE ? "CONF_VALUE_16_BIT" :
3098 byte_mask == CONF_MASK_4_BYTE ? "CONF_VALUE_32_BIT" :"CONF_VALUE_BYTES");
3099 int error_conf_chunk_token = conf_type & CONF_MASK_TOKEN;
3100 int error_element = real_element;
3102 Error(ERR_WARN, "cannot load micro chunk '%s(%d)' value for element %d ['%s']",
3103 error_conf_chunk_bytes, error_conf_chunk_token,
3104 error_element, EL_NAME(error_element));
3107 return micro_chunk_size;
3110 static int LoadLevel_INFO(File *file, int chunk_size, struct LevelInfo *level)
3112 int real_chunk_size = 0;
3114 li = *level; /* copy level data into temporary buffer */
3116 while (!checkEndOfFile(file))
3118 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_INFO, -1, -1);
3120 if (real_chunk_size >= chunk_size)
3124 *level = li; /* copy temporary buffer back to level data */
3126 return real_chunk_size;
3129 static int LoadLevel_CONF(File *file, int chunk_size, struct LevelInfo *level)
3131 int real_chunk_size = 0;
3133 li = *level; /* copy level data into temporary buffer */
3135 while (!checkEndOfFile(file))
3137 int element = getMappedElement(getFile16BitBE(file));
3139 real_chunk_size += 2;
3140 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_CONF,
3142 if (real_chunk_size >= chunk_size)
3146 *level = li; /* copy temporary buffer back to level data */
3148 return real_chunk_size;
3151 static int LoadLevel_ELEM(File *file, int chunk_size, struct LevelInfo *level)
3153 int real_chunk_size = 0;
3155 li = *level; /* copy level data into temporary buffer */
3157 while (!checkEndOfFile(file))
3159 int element = getMappedElement(getFile16BitBE(file));
3161 real_chunk_size += 2;
3162 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_ELEM,
3164 if (real_chunk_size >= chunk_size)
3168 *level = li; /* copy temporary buffer back to level data */
3170 return real_chunk_size;
3173 static int LoadLevel_NOTE(File *file, int chunk_size, struct LevelInfo *level)
3175 int element = getMappedElement(getFile16BitBE(file));
3176 int envelope_nr = element - EL_ENVELOPE_1;
3177 int real_chunk_size = 2;
3179 xx_envelope = level->envelope[envelope_nr]; /* copy into temporary buffer */
3181 while (!checkEndOfFile(file))
3183 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_NOTE,
3186 if (real_chunk_size >= chunk_size)
3190 level->envelope[envelope_nr] = xx_envelope; /* copy from temporary buffer */
3192 return real_chunk_size;
3195 static int LoadLevel_CUSX(File *file, int chunk_size, struct LevelInfo *level)
3197 int element = getMappedElement(getFile16BitBE(file));
3198 int real_chunk_size = 2;
3199 struct ElementInfo *ei = &element_info[element];
3202 xx_ei = *ei; /* copy element data into temporary buffer */
3204 xx_ei.num_change_pages = -1;
3206 while (!checkEndOfFile(file))
3208 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_CUSX_base,
3210 if (xx_ei.num_change_pages != -1)
3213 if (real_chunk_size >= chunk_size)
3219 if (ei->num_change_pages == -1)
3221 Error(ERR_WARN, "LoadLevel_CUSX(): missing 'num_change_pages' for '%s'",
3224 ei->num_change_pages = 1;
3226 setElementChangePages(ei, 1);
3227 setElementChangeInfoToDefaults(ei->change);
3229 return real_chunk_size;
3232 /* initialize number of change pages stored for this custom element */
3233 setElementChangePages(ei, ei->num_change_pages);
3234 for (i = 0; i < ei->num_change_pages; i++)
3235 setElementChangeInfoToDefaults(&ei->change_page[i]);
3237 /* start with reading properties for the first change page */
3238 xx_current_change_page = 0;
3240 while (!checkEndOfFile(file))
3242 struct ElementChangeInfo *change = &ei->change_page[xx_current_change_page];
3244 xx_change = *change; /* copy change data into temporary buffer */
3246 resetEventBits(); /* reset bits; change page might have changed */
3248 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_CUSX_change,
3251 *change = xx_change;
3253 setEventFlagsFromEventBits(change);
3255 if (real_chunk_size >= chunk_size)
3259 level->file_has_custom_elements = TRUE;
3261 return real_chunk_size;
3264 static int LoadLevel_GRPX(File *file, int chunk_size, struct LevelInfo *level)
3266 int element = getMappedElement(getFile16BitBE(file));
3267 int real_chunk_size = 2;
3268 struct ElementInfo *ei = &element_info[element];
3269 struct ElementGroupInfo *group = ei->group;
3271 xx_ei = *ei; /* copy element data into temporary buffer */
3272 xx_group = *group; /* copy group data into temporary buffer */
3274 while (!checkEndOfFile(file))
3276 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_GRPX,
3279 if (real_chunk_size >= chunk_size)
3286 level->file_has_custom_elements = TRUE;
3288 return real_chunk_size;
3291 static void LoadLevelFromFileInfo_RND(struct LevelInfo *level,
3292 struct LevelFileInfo *level_file_info,
3293 boolean level_info_only)
3295 char *filename = level_file_info->filename;
3296 char cookie[MAX_LINE_LEN];
3297 char chunk_name[CHUNK_ID_LEN + 1];
3301 if (!(file = openFile(filename, MODE_READ)))
3303 level->no_valid_file = TRUE;
3304 level->no_level_file = TRUE;
3306 if (level_info_only)
3309 Error(ERR_WARN, "cannot read level '%s' -- using empty level", filename);
3311 if (!setup.editor.use_template_for_new_levels)
3314 /* if level file not found, try to initialize level data from template */
3315 filename = getGlobalLevelTemplateFilename();
3317 if (!(file = openFile(filename, MODE_READ)))
3320 /* default: for empty levels, use level template for custom elements */
3321 level->use_custom_template = TRUE;
3323 level->no_valid_file = FALSE;
3326 getFileChunkBE(file, chunk_name, NULL);
3327 if (strEqual(chunk_name, "RND1"))
3329 getFile32BitBE(file); /* not used */
3331 getFileChunkBE(file, chunk_name, NULL);
3332 if (!strEqual(chunk_name, "CAVE"))
3334 level->no_valid_file = TRUE;
3336 Error(ERR_WARN, "unknown format of level file '%s'", filename);
3343 else /* check for pre-2.0 file format with cookie string */
3345 strcpy(cookie, chunk_name);
3346 if (getStringFromFile(file, &cookie[4], MAX_LINE_LEN - 4) == NULL)
3348 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
3349 cookie[strlen(cookie) - 1] = '\0';
3351 if (!checkCookieString(cookie, LEVEL_COOKIE_TMPL))
3353 level->no_valid_file = TRUE;
3355 Error(ERR_WARN, "unknown format of level file '%s'", filename);
3362 if ((level->file_version = getFileVersionFromCookieString(cookie)) == -1)
3364 level->no_valid_file = TRUE;
3366 Error(ERR_WARN, "unsupported version of level file '%s'", filename);
3373 /* pre-2.0 level files have no game version, so use file version here */
3374 level->game_version = level->file_version;
3377 if (level->file_version < FILE_VERSION_1_2)
3379 /* level files from versions before 1.2.0 without chunk structure */
3380 LoadLevel_HEAD(file, LEVEL_CHUNK_HEAD_SIZE, level);
3381 LoadLevel_BODY(file, level->fieldx * level->fieldy, level);
3389 int (*loader)(File *, int, struct LevelInfo *);
3393 { "VERS", LEVEL_CHUNK_VERS_SIZE, LoadLevel_VERS },
3394 { "DATE", LEVEL_CHUNK_DATE_SIZE, LoadLevel_DATE },
3395 { "HEAD", LEVEL_CHUNK_HEAD_SIZE, LoadLevel_HEAD },
3396 { "NAME", LEVEL_CHUNK_NAME_SIZE, LoadLevel_NAME },
3397 { "AUTH", LEVEL_CHUNK_AUTH_SIZE, LoadLevel_AUTH },
3398 { "INFO", -1, LoadLevel_INFO },
3399 { "BODY", -1, LoadLevel_BODY },
3400 { "CONT", -1, LoadLevel_CONT },
3401 { "CNT2", LEVEL_CHUNK_CNT2_SIZE, LoadLevel_CNT2 },
3402 { "CNT3", -1, LoadLevel_CNT3 },
3403 { "CUS1", -1, LoadLevel_CUS1 },
3404 { "CUS2", -1, LoadLevel_CUS2 },
3405 { "CUS3", -1, LoadLevel_CUS3 },
3406 { "CUS4", -1, LoadLevel_CUS4 },
3407 { "GRP1", -1, LoadLevel_GRP1 },
3408 { "CONF", -1, LoadLevel_CONF },
3409 { "ELEM", -1, LoadLevel_ELEM },
3410 { "NOTE", -1, LoadLevel_NOTE },
3411 { "CUSX", -1, LoadLevel_CUSX },
3412 { "GRPX", -1, LoadLevel_GRPX },
3417 while (getFileChunkBE(file, chunk_name, &chunk_size))
3421 while (chunk_info[i].name != NULL &&
3422 !strEqual(chunk_name, chunk_info[i].name))
3425 if (chunk_info[i].name == NULL)
3427 Error(ERR_WARN, "unknown chunk '%s' in level file '%s'",
3428 chunk_name, filename);
3429 ReadUnusedBytesFromFile(file, chunk_size);
3431 else if (chunk_info[i].size != -1 &&
3432 chunk_info[i].size != chunk_size)
3434 Error(ERR_WARN, "wrong size (%d) of chunk '%s' in level file '%s'",
3435 chunk_size, chunk_name, filename);
3436 ReadUnusedBytesFromFile(file, chunk_size);
3440 /* call function to load this level chunk */
3441 int chunk_size_expected =
3442 (chunk_info[i].loader)(file, chunk_size, level);
3444 /* the size of some chunks cannot be checked before reading other
3445 chunks first (like "HEAD" and "BODY") that contain some header
3446 information, so check them here */
3447 if (chunk_size_expected != chunk_size)
3449 Error(ERR_WARN, "wrong size (%d) of chunk '%s' in level file '%s'",
3450 chunk_size, chunk_name, filename);
3460 /* ------------------------------------------------------------------------- */
3461 /* functions for loading EM level */
3462 /* ------------------------------------------------------------------------- */
3464 void CopyNativeLevel_RND_to_EM(struct LevelInfo *level)
3466 static int ball_xy[8][2] =
3477 struct LevelInfo_EM *level_em = level->native_em_level;
3478 struct LEVEL *lev = level_em->lev;
3479 struct PLAYER **ply = level_em->ply;
3482 lev->width = MIN(level->fieldx, EM_MAX_CAVE_WIDTH);
3483 lev->height = MIN(level->fieldy, EM_MAX_CAVE_HEIGHT);
3485 lev->time_seconds = level->time;
3486 lev->required_initial = level->gems_needed;
3488 lev->emerald_score = level->score[SC_EMERALD];
3489 lev->diamond_score = level->score[SC_DIAMOND];
3490 lev->alien_score = level->score[SC_ROBOT];
3491 lev->tank_score = level->score[SC_SPACESHIP];
3492 lev->bug_score = level->score[SC_BUG];
3493 lev->eater_score = level->score[SC_YAMYAM];
3494 lev->nut_score = level->score[SC_NUT];
3495 lev->dynamite_score = level->score[SC_DYNAMITE];
3496 lev->key_score = level->score[SC_KEY];
3497 lev->exit_score = level->score[SC_TIME_BONUS];
3499 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
3500 for (y = 0; y < 3; y++)
3501 for (x = 0; x < 3; x++)
3502 lev->eater_array[i][y * 3 + x] =
3503 map_element_RND_to_EM(level->yamyam_content[i].e[x][y]);
3505 lev->amoeba_time = level->amoeba_speed;
3506 lev->wonderwall_time_initial = level->time_magic_wall;
3507 lev->wheel_time = level->time_wheel;
3509 lev->android_move_time = level->android_move_time;
3510 lev->android_clone_time = level->android_clone_time;
3511 lev->ball_random = level->ball_random;
3512 lev->ball_state_initial = level->ball_state_initial;
3513 lev->ball_time = level->ball_time;
3514 lev->num_ball_arrays = level->num_ball_contents;
3516 lev->lenses_score = level->lenses_score;
3517 lev->magnify_score = level->magnify_score;
3518 lev->slurp_score = level->slurp_score;
3520 lev->lenses_time = level->lenses_time;
3521 lev->magnify_time = level->magnify_time;
3523 lev->wind_direction_initial =
3524 map_direction_RND_to_EM(level->wind_direction_initial);
3525 lev->wind_cnt_initial = (level->wind_direction_initial != MV_NONE ?
3526 lev->wind_time : 0);
3528 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
3529 for (j = 0; j < 8; j++)
3530 lev->ball_array[i][j] =
3531 map_element_RND_to_EM(level->
3532 ball_content[i].e[ball_xy[j][0]][ball_xy[j][1]]);
3534 map_android_clone_elements_RND_to_EM(level);
3536 /* first fill the complete playfield with the default border element */
3537 for (y = 0; y < EM_MAX_CAVE_HEIGHT; y++)
3538 for (x = 0; x < EM_MAX_CAVE_WIDTH; x++)
3539 level_em->cave[x][y] = ZBORDER;
3541 if (BorderElement == EL_STEELWALL)
3543 for (y = 0; y < lev->height + 2; y++)
3544 for (x = 0; x < lev->width + 2; x++)
3545 level_em->cave[x + 1][y + 1] = map_element_RND_to_EM(EL_STEELWALL);
3548 /* then copy the real level contents from level file into the playfield */
3549 for (y = 0; y < lev->height; y++) for (x = 0; x < lev->width; x++)
3551 int new_element = map_element_RND_to_EM(level->field[x][y]);
3552 int offset = (BorderElement == EL_STEELWALL ? 1 : 0);
3553 int xx = x + 1 + offset;
3554 int yy = y + 1 + offset;
3556 if (level->field[x][y] == EL_AMOEBA_DEAD)
3557 new_element = map_element_RND_to_EM(EL_AMOEBA_WET);
3559 level_em->cave[xx][yy] = new_element;
3562 for (i = 0; i < MAX_PLAYERS; i++)
3564 ply[i]->x_initial = 0;
3565 ply[i]->y_initial = 0;
3568 /* initialize player positions and delete players from the playfield */
3569 for (y = 0; y < lev->height; y++) for (x = 0; x < lev->width; x++)
3571 if (ELEM_IS_PLAYER(level->field[x][y]))
3573 int player_nr = GET_PLAYER_NR(level->field[x][y]);
3574 int offset = (BorderElement == EL_STEELWALL ? 1 : 0);
3575 int xx = x + 1 + offset;
3576 int yy = y + 1 + offset;
3578 ply[player_nr]->x_initial = xx;
3579 ply[player_nr]->y_initial = yy;
3581 level_em->cave[xx][yy] = map_element_RND_to_EM(EL_EMPTY);
3585 if (BorderElement == EL_STEELWALL)
3592 void CopyNativeLevel_EM_to_RND(struct LevelInfo *level)
3594 static int ball_xy[8][2] =
3605 struct LevelInfo_EM *level_em = level->native_em_level;
3606 struct LEVEL *lev = level_em->lev;
3607 struct PLAYER **ply = level_em->ply;
3610 level->fieldx = MIN(lev->width, MAX_LEV_FIELDX);
3611 level->fieldy = MIN(lev->height, MAX_LEV_FIELDY);
3613 level->time = lev->time_seconds;
3614 level->gems_needed = lev->required_initial;
3616 sprintf(level->name, "Level %d", level->file_info.nr);
3618 level->score[SC_EMERALD] = lev->emerald_score;
3619 level->score[SC_DIAMOND] = lev->diamond_score;
3620 level->score[SC_ROBOT] = lev->alien_score;
3621 level->score[SC_SPACESHIP] = lev->tank_score;
3622 level->score[SC_BUG] = lev->bug_score;
3623 level->score[SC_YAMYAM] = lev->eater_score;
3624 level->score[SC_NUT] = lev->nut_score;
3625 level->score[SC_DYNAMITE] = lev->dynamite_score;
3626 level->score[SC_KEY] = lev->key_score;
3627 level->score[SC_TIME_BONUS] = lev->exit_score;
3629 level->num_yamyam_contents = MAX_ELEMENT_CONTENTS;
3631 for (i = 0; i < level->num_yamyam_contents; i++)
3632 for (y = 0; y < 3; y++)
3633 for (x = 0; x < 3; x++)
3634 level->yamyam_content[i].e[x][y] =
3635 map_element_EM_to_RND(lev->eater_array[i][y * 3 + x]);
3637 level->amoeba_speed = lev->amoeba_time;
3638 level->time_magic_wall = lev->wonderwall_time_initial;
3639 level->time_wheel = lev->wheel_time;
3641 level->android_move_time = lev->android_move_time;
3642 level->android_clone_time = lev->android_clone_time;
3643 level->ball_random = lev->ball_random;
3644 level->ball_state_initial = lev->ball_state_initial;
3645 level->ball_time = lev->ball_time;
3646 level->num_ball_contents = lev->num_ball_arrays;
3648 level->lenses_score = lev->lenses_score;
3649 level->magnify_score = lev->magnify_score;
3650 level->slurp_score = lev->slurp_score;
3652 level->lenses_time = lev->lenses_time;
3653 level->magnify_time = lev->magnify_time;
3655 level->wind_direction_initial =
3656 map_direction_EM_to_RND(lev->wind_direction_initial);
3658 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
3659 for (j = 0; j < 8; j++)
3660 level->ball_content[i].e[ball_xy[j][0]][ball_xy[j][1]] =
3661 map_element_EM_to_RND(lev->ball_array[i][j]);
3663 map_android_clone_elements_EM_to_RND(level);
3665 /* convert the playfield (some elements need special treatment) */
3666 for (y = 0; y < level->fieldy; y++) for (x = 0; x < level->fieldx; x++)
3668 int new_element = map_element_EM_to_RND(level_em->cave[x + 1][y + 1]);
3670 if (new_element == EL_AMOEBA_WET && level->amoeba_speed == 0)
3671 new_element = EL_AMOEBA_DEAD;
3673 level->field[x][y] = new_element;
3676 for (i = 0; i < MAX_PLAYERS; i++)
3678 /* in case of all players set to the same field, use the first player */
3679 int nr = MAX_PLAYERS - i - 1;
3680 int jx = ply[nr]->x_initial - 1;
3681 int jy = ply[nr]->y_initial - 1;
3683 if (jx != -1 && jy != -1)
3684 level->field[jx][jy] = EL_PLAYER_1 + nr;
3689 /* ------------------------------------------------------------------------- */
3690 /* functions for loading SP level */
3691 /* ------------------------------------------------------------------------- */
3693 void CopyNativeLevel_RND_to_SP(struct LevelInfo *level)
3695 struct LevelInfo_SP *level_sp = level->native_sp_level;
3696 LevelInfoType *header = &level_sp->header;
3699 level_sp->width = level->fieldx;
3700 level_sp->height = level->fieldy;
3702 for (x = 0; x < level->fieldx; x++)
3703 for (y = 0; y < level->fieldy; y++)
3704 level_sp->playfield[x][y] = map_element_RND_to_SP(level->field[x][y]);
3706 header->InitialGravity = (level->initial_player_gravity[0] ? 1 : 0);
3708 for (i = 0; i < SP_LEVEL_NAME_LEN; i++)
3709 header->LevelTitle[i] = level->name[i];
3710 /* !!! NO STRING TERMINATION IN SUPAPLEX VB CODE YET -- FIX THIS !!! */
3712 header->InfotronsNeeded = level->gems_needed;
3714 header->SpecialPortCount = 0;
3716 for (x = 0; x < level->fieldx; x++) for (y = 0; y < level->fieldy; y++)
3718 boolean gravity_port_found = FALSE;
3719 boolean gravity_port_valid = FALSE;
3720 int gravity_port_flag;
3721 int gravity_port_base_element;
3722 int element = level->field[x][y];
3724 if (element >= EL_SP_GRAVITY_ON_PORT_RIGHT &&
3725 element <= EL_SP_GRAVITY_ON_PORT_UP)
3727 gravity_port_found = TRUE;
3728 gravity_port_valid = TRUE;
3729 gravity_port_flag = 1;
3730 gravity_port_base_element = EL_SP_GRAVITY_ON_PORT_RIGHT;
3732 else if (element >= EL_SP_GRAVITY_OFF_PORT_RIGHT &&
3733 element <= EL_SP_GRAVITY_OFF_PORT_UP)
3735 gravity_port_found = TRUE;
3736 gravity_port_valid = TRUE;
3737 gravity_port_flag = 0;
3738 gravity_port_base_element = EL_SP_GRAVITY_OFF_PORT_RIGHT;
3740 else if (element >= EL_SP_GRAVITY_PORT_RIGHT &&
3741 element <= EL_SP_GRAVITY_PORT_UP)
3743 /* change R'n'D style gravity inverting special port to normal port
3744 (there are no gravity inverting ports in native Supaplex engine) */
3746 gravity_port_found = TRUE;
3747 gravity_port_valid = FALSE;
3748 gravity_port_base_element = EL_SP_GRAVITY_PORT_RIGHT;
3751 if (gravity_port_found)
3753 if (gravity_port_valid &&
3754 header->SpecialPortCount < SP_MAX_SPECIAL_PORTS)
3756 SpecialPortType *port = &header->SpecialPort[header->SpecialPortCount];
3758 port->PortLocation = (y * level->fieldx + x) * 2;
3759 port->Gravity = gravity_port_flag;
3761 element += EL_SP_GRAVITY_PORT_RIGHT - gravity_port_base_element;
3763 header->SpecialPortCount++;
3767 /* change special gravity port to normal port */
3769 element += EL_SP_PORT_RIGHT - gravity_port_base_element;
3772 level_sp->playfield[x][y] = element - EL_SP_START;
3777 void CopyNativeLevel_SP_to_RND(struct LevelInfo *level)
3779 struct LevelInfo_SP *level_sp = level->native_sp_level;
3780 LevelInfoType *header = &level_sp->header;
3781 boolean num_invalid_elements = 0;
3784 level->fieldx = level_sp->width;
3785 level->fieldy = level_sp->height;
3787 for (x = 0; x < level->fieldx; x++)
3789 for (y = 0; y < level->fieldy; y++)
3791 int element_old = level_sp->playfield[x][y];
3792 int element_new = getMappedElement(map_element_SP_to_RND(element_old));
3794 if (element_new == EL_UNKNOWN)
3796 num_invalid_elements++;
3798 Error(ERR_DEBUG, "invalid element %d at position %d, %d",
3802 level->field[x][y] = element_new;
3806 if (num_invalid_elements > 0)
3807 Error(ERR_WARN, "found %d invalid elements%s", num_invalid_elements,
3808 (!options.debug ? " (use '--debug' for more details)" : ""));
3810 for (i = 0; i < MAX_PLAYERS; i++)
3811 level->initial_player_gravity[i] =
3812 (header->InitialGravity == 1 ? TRUE : FALSE);
3814 /* skip leading spaces */
3815 for (i = 0; i < SP_LEVEL_NAME_LEN; i++)
3816 if (header->LevelTitle[i] != ' ')
3819 /* copy level title */
3820 for (j = 0; i < SP_LEVEL_NAME_LEN; i++, j++)
3821 level->name[j] = header->LevelTitle[i];
3822 level->name[j] = '\0';
3824 /* cut trailing spaces */
3826 if (level->name[j - 1] == ' ' && level->name[j] == '\0')
3827 level->name[j - 1] = '\0';
3829 level->gems_needed = header->InfotronsNeeded;
3831 for (i = 0; i < header->SpecialPortCount; i++)
3833 SpecialPortType *port = &header->SpecialPort[i];
3834 int port_location = port->PortLocation;
3835 int gravity = port->Gravity;
3836 int port_x, port_y, port_element;
3838 port_x = (port_location / 2) % level->fieldx;
3839 port_y = (port_location / 2) / level->fieldx;
3841 if (port_x < 0 || port_x >= level->fieldx ||
3842 port_y < 0 || port_y >= level->fieldy)
3844 Error(ERR_WARN, "special port position (%d, %d) out of bounds",
3850 port_element = level->field[port_x][port_y];
3852 if (port_element < EL_SP_GRAVITY_PORT_RIGHT ||
3853 port_element > EL_SP_GRAVITY_PORT_UP)
3855 Error(ERR_WARN, "no special port at position (%d, %d)", port_x, port_y);
3860 /* change previous (wrong) gravity inverting special port to either
3861 gravity enabling special port or gravity disabling special port */
3862 level->field[port_x][port_y] +=
3863 (gravity == 1 ? EL_SP_GRAVITY_ON_PORT_RIGHT :
3864 EL_SP_GRAVITY_OFF_PORT_RIGHT) - EL_SP_GRAVITY_PORT_RIGHT;
3867 /* change special gravity ports without database entries to normal ports */
3868 for (x = 0; x < level->fieldx; x++)
3869 for (y = 0; y < level->fieldy; y++)
3870 if (level->field[x][y] >= EL_SP_GRAVITY_PORT_RIGHT &&
3871 level->field[x][y] <= EL_SP_GRAVITY_PORT_UP)
3872 level->field[x][y] += EL_SP_PORT_RIGHT - EL_SP_GRAVITY_PORT_RIGHT;
3874 level->time = 0; /* no time limit */
3875 level->amoeba_speed = 0;
3876 level->time_magic_wall = 0;
3877 level->time_wheel = 0;
3878 level->amoeba_content = EL_EMPTY;
3881 /* original Supaplex does not use score values -- use default values */
3883 for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
3884 level->score[i] = 0;
3887 /* there are no yamyams in supaplex levels */
3888 for (i = 0; i < level->num_yamyam_contents; i++)
3889 for (x = 0; x < 3; x++)
3890 for (y = 0; y < 3; y++)
3891 level->yamyam_content[i].e[x][y] = EL_EMPTY;
3894 static void CopyNativeTape_RND_to_SP(struct LevelInfo *level)
3896 struct LevelInfo_SP *level_sp = level->native_sp_level;
3897 struct DemoInfo_SP *demo = &level_sp->demo;
3900 /* always start with reliable default values */
3901 demo->is_available = FALSE;
3904 if (TAPE_IS_EMPTY(tape))
3907 demo->level_nr = tape.level_nr; /* (currently not used) */
3909 level_sp->header.DemoRandomSeed = tape.random_seed;
3913 for (i = 0; i < tape.length; i++)
3915 int demo_action = map_key_RND_to_SP(tape.pos[i].action[0]);
3916 int demo_repeat = tape.pos[i].delay;
3917 int demo_entries = (demo_repeat + 15) / 16;
3919 if (demo->length + demo_entries >= SP_MAX_TAPE_LEN)
3921 Error(ERR_WARN, "tape truncated: size exceeds maximum SP demo size %d",
3927 for (j = 0; j < demo_repeat / 16; j++)
3928 demo->data[demo->length++] = 0xf0 | demo_action;
3930 if (demo_repeat % 16)
3931 demo->data[demo->length++] = ((demo_repeat % 16 - 1) << 4) | demo_action;
3934 demo->is_available = TRUE;
3937 static void setTapeInfoToDefaults();
3939 static void CopyNativeTape_SP_to_RND(struct LevelInfo *level)
3941 struct LevelInfo_SP *level_sp = level->native_sp_level;
3942 struct DemoInfo_SP *demo = &level_sp->demo;
3943 char *filename = level->file_info.filename;
3946 /* always start with reliable default values */
3947 setTapeInfoToDefaults();
3949 if (!demo->is_available)
3952 tape.level_nr = demo->level_nr; /* (currently not used) */
3953 tape.random_seed = level_sp->header.DemoRandomSeed;
3955 TapeSetDateFromEpochSeconds(getFileTimestampEpochSeconds(filename));
3958 tape.pos[tape.counter].delay = 0;
3960 for (i = 0; i < demo->length; i++)
3962 int demo_action = demo->data[i] & 0x0f;
3963 int demo_repeat = (demo->data[i] & 0xf0) >> 4;
3964 int tape_action = map_key_SP_to_RND(demo_action);
3965 int tape_repeat = demo_repeat + 1;
3966 byte action[MAX_PLAYERS] = { tape_action, 0, 0, 0 };
3967 boolean success = 0;
3970 for (j = 0; j < tape_repeat; j++)
3971 success = TapeAddAction(action);
3975 Error(ERR_WARN, "SP demo truncated: size exceeds maximum tape size %d",
3982 TapeHaltRecording();
3986 /* ------------------------------------------------------------------------- */
3987 /* functions for loading MM level */
3988 /* ------------------------------------------------------------------------- */
3990 void CopyNativeLevel_RND_to_MM(struct LevelInfo *level)
3992 struct LevelInfo_MM *level_mm = level->native_mm_level;
3995 level_mm->fieldx = MIN(level->fieldx, MM_MAX_PLAYFIELD_WIDTH);
3996 level_mm->fieldy = MIN(level->fieldy, MM_MAX_PLAYFIELD_HEIGHT);
3998 level_mm->time = level->time;
3999 level_mm->kettles_needed = level->gems_needed;
4000 level_mm->auto_count_kettles = level->auto_count_gems;
4002 level_mm->laser_red = level->mm_laser_red;
4003 level_mm->laser_green = level->mm_laser_green;
4004 level_mm->laser_blue = level->mm_laser_blue;
4006 strcpy(level_mm->name, level->name);
4007 strcpy(level_mm->author, level->author);
4009 level_mm->score[SC_EMERALD] = level->score[SC_EMERALD];
4010 level_mm->score[SC_PACMAN] = level->score[SC_PACMAN];
4011 level_mm->score[SC_KEY] = level->score[SC_KEY];
4012 level_mm->score[SC_TIME_BONUS] = level->score[SC_TIME_BONUS];
4013 level_mm->score[SC_ELEM_BONUS] = level->score[SC_ELEM_BONUS];
4015 level_mm->amoeba_speed = level->amoeba_speed;
4016 level_mm->time_fuse = level->mm_time_fuse;
4017 level_mm->time_bomb = level->mm_time_bomb;
4018 level_mm->time_ball = level->mm_time_ball;
4019 level_mm->time_block = level->mm_time_block;
4021 for (x = 0; x < level->fieldx; x++)
4022 for (y = 0; y < level->fieldy; y++)
4024 level_mm->field[x][y] = map_element_RND_to_MM(level->field[x][y]);
4027 void CopyNativeLevel_MM_to_RND(struct LevelInfo *level)
4029 struct LevelInfo_MM *level_mm = level->native_mm_level;
4032 level->fieldx = MIN(level_mm->fieldx, MAX_LEV_FIELDX);
4033 level->fieldy = MIN(level_mm->fieldy, MAX_LEV_FIELDY);
4035 level->time = level_mm->time;
4036 level->gems_needed = level_mm->kettles_needed;
4037 level->auto_count_gems = level_mm->auto_count_kettles;
4039 level->mm_laser_red = level_mm->laser_red;
4040 level->mm_laser_green = level_mm->laser_green;
4041 level->mm_laser_blue = level_mm->laser_blue;
4043 strcpy(level->name, level_mm->name);
4045 /* only overwrite author from 'levelinfo.conf' if author defined in level */
4046 if (!strEqual(level_mm->author, ANONYMOUS_NAME))
4047 strcpy(level->author, level_mm->author);
4049 level->score[SC_EMERALD] = level_mm->score[SC_EMERALD];
4050 level->score[SC_PACMAN] = level_mm->score[SC_PACMAN];
4051 level->score[SC_KEY] = level_mm->score[SC_KEY];
4052 level->score[SC_TIME_BONUS] = level_mm->score[SC_TIME_BONUS];
4053 level->score[SC_ELEM_BONUS] = level_mm->score[SC_ELEM_BONUS];
4055 level->amoeba_speed = level_mm->amoeba_speed;
4056 level->mm_time_fuse = level_mm->time_fuse;
4057 level->mm_time_bomb = level_mm->time_bomb;
4058 level->mm_time_ball = level_mm->time_ball;
4059 level->mm_time_block = level_mm->time_block;
4061 for (x = 0; x < level->fieldx; x++)
4062 for (y = 0; y < level->fieldy; y++)
4063 level->field[x][y] = map_element_MM_to_RND(level_mm->field[x][y]);
4067 /* ------------------------------------------------------------------------- */
4068 /* functions for loading DC level */
4069 /* ------------------------------------------------------------------------- */
4071 #define DC_LEVEL_HEADER_SIZE 344
4073 unsigned short getDecodedWord_DC(unsigned short data_encoded, boolean init)
4075 static int last_data_encoded;
4079 int diff_hi, diff_lo;
4080 int data_hi, data_lo;
4081 unsigned short data_decoded;
4085 last_data_encoded = 0;
4092 diff = data_encoded - last_data_encoded;
4093 diff_hi = diff & ~0xff;
4094 diff_lo = diff & 0xff;
4098 data_hi = diff_hi - (offset1 << 8) + (offset2 & 0xff00);
4099 data_lo = (diff_lo + (data_hi >> 16)) & 0x00ff;
4100 data_hi = data_hi & 0xff00;
4102 data_decoded = data_hi | data_lo;
4104 last_data_encoded = data_encoded;
4106 offset1 = (offset1 + 1) % 31;
4107 offset2 = offset2 & 0xff;
4109 return data_decoded;
4112 int getMappedElement_DC(int element)
4120 /* 0x0117 - 0x036e: (?) */
4123 /* 0x042d - 0x0684: (?) */
4139 element = EL_CRYSTAL;
4142 case 0x0e77: /* quicksand (boulder) */
4143 element = EL_QUICKSAND_FAST_FULL;
4146 case 0x0e99: /* slow quicksand (boulder) */
4147 element = EL_QUICKSAND_FULL;
4151 element = EL_EM_EXIT_OPEN;
4155 element = EL_EM_EXIT_CLOSED;
4159 element = EL_EM_STEEL_EXIT_OPEN;
4163 element = EL_EM_STEEL_EXIT_CLOSED;
4166 case 0x0f4f: /* dynamite (lit 1) */
4167 element = EL_EM_DYNAMITE_ACTIVE;
4170 case 0x0f57: /* dynamite (lit 2) */
4171 element = EL_EM_DYNAMITE_ACTIVE;
4174 case 0x0f5f: /* dynamite (lit 3) */
4175 element = EL_EM_DYNAMITE_ACTIVE;
4178 case 0x0f67: /* dynamite (lit 4) */
4179 element = EL_EM_DYNAMITE_ACTIVE;
4186 element = EL_AMOEBA_WET;
4190 element = EL_AMOEBA_DROP;
4194 element = EL_DC_MAGIC_WALL;
4198 element = EL_SPACESHIP_UP;
4202 element = EL_SPACESHIP_DOWN;
4206 element = EL_SPACESHIP_LEFT;
4210 element = EL_SPACESHIP_RIGHT;
4214 element = EL_BUG_UP;
4218 element = EL_BUG_DOWN;
4222 element = EL_BUG_LEFT;
4226 element = EL_BUG_RIGHT;
4230 element = EL_MOLE_UP;
4234 element = EL_MOLE_DOWN;
4238 element = EL_MOLE_LEFT;
4242 element = EL_MOLE_RIGHT;
4250 element = EL_YAMYAM;
4254 element = EL_SWITCHGATE_OPEN;
4258 element = EL_SWITCHGATE_CLOSED;
4262 element = EL_DC_SWITCHGATE_SWITCH_UP;
4266 element = EL_TIMEGATE_CLOSED;
4269 case 0x144c: /* conveyor belt switch (green) */
4270 element = EL_CONVEYOR_BELT_3_SWITCH_MIDDLE;
4273 case 0x144f: /* conveyor belt switch (red) */
4274 element = EL_CONVEYOR_BELT_1_SWITCH_MIDDLE;
4277 case 0x1452: /* conveyor belt switch (blue) */
4278 element = EL_CONVEYOR_BELT_4_SWITCH_MIDDLE;
4282 element = EL_CONVEYOR_BELT_3_MIDDLE;
4286 element = EL_CONVEYOR_BELT_3_LEFT;
4290 element = EL_CONVEYOR_BELT_3_RIGHT;
4294 element = EL_CONVEYOR_BELT_1_MIDDLE;
4298 element = EL_CONVEYOR_BELT_1_LEFT;
4302 element = EL_CONVEYOR_BELT_1_RIGHT;
4306 element = EL_CONVEYOR_BELT_4_MIDDLE;
4310 element = EL_CONVEYOR_BELT_4_LEFT;
4314 element = EL_CONVEYOR_BELT_4_RIGHT;
4318 element = EL_EXPANDABLE_WALL_HORIZONTAL;
4322 element = EL_EXPANDABLE_WALL_VERTICAL;
4326 element = EL_EXPANDABLE_WALL_ANY;
4329 case 0x14ce: /* growing steel wall (left/right) */
4330 element = EL_EXPANDABLE_STEELWALL_HORIZONTAL;
4333 case 0x14df: /* growing steel wall (up/down) */
4334 element = EL_EXPANDABLE_STEELWALL_VERTICAL;
4337 case 0x14e8: /* growing steel wall (up/down/left/right) */
4338 element = EL_EXPANDABLE_STEELWALL_ANY;
4342 element = EL_SHIELD_DEADLY;
4346 element = EL_EXTRA_TIME;
4354 element = EL_EMPTY_SPACE;
4357 case 0x1578: /* quicksand (empty) */
4358 element = EL_QUICKSAND_FAST_EMPTY;
4361 case 0x1579: /* slow quicksand (empty) */
4362 element = EL_QUICKSAND_EMPTY;
4365 /* 0x157c - 0x158b: */
4368 /* 0x1590 - 0x159f: */
4369 /* EL_DC_LANDMINE */
4372 element = EL_EM_DYNAMITE;
4375 case 0x15a1: /* key (red) */
4376 element = EL_EM_KEY_1;
4379 case 0x15a2: /* key (yellow) */
4380 element = EL_EM_KEY_2;
4383 case 0x15a3: /* key (blue) */
4384 element = EL_EM_KEY_4;
4387 case 0x15a4: /* key (green) */
4388 element = EL_EM_KEY_3;
4391 case 0x15a5: /* key (white) */
4392 element = EL_DC_KEY_WHITE;
4396 element = EL_WALL_SLIPPERY;
4403 case 0x15a8: /* wall (not round) */
4407 case 0x15a9: /* (blue) */
4408 element = EL_CHAR_A;
4411 case 0x15aa: /* (blue) */
4412 element = EL_CHAR_B;
4415 case 0x15ab: /* (blue) */
4416 element = EL_CHAR_C;
4419 case 0x15ac: /* (blue) */
4420 element = EL_CHAR_D;
4423 case 0x15ad: /* (blue) */
4424 element = EL_CHAR_E;
4427 case 0x15ae: /* (blue) */
4428 element = EL_CHAR_F;
4431 case 0x15af: /* (blue) */
4432 element = EL_CHAR_G;
4435 case 0x15b0: /* (blue) */
4436 element = EL_CHAR_H;
4439 case 0x15b1: /* (blue) */
4440 element = EL_CHAR_I;
4443 case 0x15b2: /* (blue) */
4444 element = EL_CHAR_J;
4447 case 0x15b3: /* (blue) */
4448 element = EL_CHAR_K;
4451 case 0x15b4: /* (blue) */
4452 element = EL_CHAR_L;
4455 case 0x15b5: /* (blue) */
4456 element = EL_CHAR_M;
4459 case 0x15b6: /* (blue) */
4460 element = EL_CHAR_N;
4463 case 0x15b7: /* (blue) */
4464 element = EL_CHAR_O;
4467 case 0x15b8: /* (blue) */
4468 element = EL_CHAR_P;
4471 case 0x15b9: /* (blue) */
4472 element = EL_CHAR_Q;
4475 case 0x15ba: /* (blue) */
4476 element = EL_CHAR_R;
4479 case 0x15bb: /* (blue) */
4480 element = EL_CHAR_S;
4483 case 0x15bc: /* (blue) */
4484 element = EL_CHAR_T;
4487 case 0x15bd: /* (blue) */
4488 element = EL_CHAR_U;
4491 case 0x15be: /* (blue) */
4492 element = EL_CHAR_V;
4495 case 0x15bf: /* (blue) */
4496 element = EL_CHAR_W;
4499 case 0x15c0: /* (blue) */
4500 element = EL_CHAR_X;
4503 case 0x15c1: /* (blue) */
4504 element = EL_CHAR_Y;
4507 case 0x15c2: /* (blue) */
4508 element = EL_CHAR_Z;
4511 case 0x15c3: /* (blue) */
4512 element = EL_CHAR_AUMLAUT;
4515 case 0x15c4: /* (blue) */
4516 element = EL_CHAR_OUMLAUT;
4519 case 0x15c5: /* (blue) */
4520 element = EL_CHAR_UUMLAUT;
4523 case 0x15c6: /* (blue) */
4524 element = EL_CHAR_0;
4527 case 0x15c7: /* (blue) */
4528 element = EL_CHAR_1;
4531 case 0x15c8: /* (blue) */
4532 element = EL_CHAR_2;
4535 case 0x15c9: /* (blue) */
4536 element = EL_CHAR_3;
4539 case 0x15ca: /* (blue) */
4540 element = EL_CHAR_4;
4543 case 0x15cb: /* (blue) */
4544 element = EL_CHAR_5;
4547 case 0x15cc: /* (blue) */
4548 element = EL_CHAR_6;
4551 case 0x15cd: /* (blue) */
4552 element = EL_CHAR_7;
4555 case 0x15ce: /* (blue) */
4556 element = EL_CHAR_8;
4559 case 0x15cf: /* (blue) */
4560 element = EL_CHAR_9;
4563 case 0x15d0: /* (blue) */
4564 element = EL_CHAR_PERIOD;
4567 case 0x15d1: /* (blue) */
4568 element = EL_CHAR_EXCLAM;
4571 case 0x15d2: /* (blue) */
4572 element = EL_CHAR_COLON;
4575 case 0x15d3: /* (blue) */
4576 element = EL_CHAR_LESS;
4579 case 0x15d4: /* (blue) */
4580 element = EL_CHAR_GREATER;
4583 case 0x15d5: /* (blue) */
4584 element = EL_CHAR_QUESTION;
4587 case 0x15d6: /* (blue) */
4588 element = EL_CHAR_COPYRIGHT;
4591 case 0x15d7: /* (blue) */
4592 element = EL_CHAR_UP;
4595 case 0x15d8: /* (blue) */
4596 element = EL_CHAR_DOWN;
4599 case 0x15d9: /* (blue) */
4600 element = EL_CHAR_BUTTON;
4603 case 0x15da: /* (blue) */
4604 element = EL_CHAR_PLUS;
4607 case 0x15db: /* (blue) */
4608 element = EL_CHAR_MINUS;
4611 case 0x15dc: /* (blue) */
4612 element = EL_CHAR_APOSTROPHE;
4615 case 0x15dd: /* (blue) */
4616 element = EL_CHAR_PARENLEFT;
4619 case 0x15de: /* (blue) */
4620 element = EL_CHAR_PARENRIGHT;
4623 case 0x15df: /* (green) */
4624 element = EL_CHAR_A;
4627 case 0x15e0: /* (green) */
4628 element = EL_CHAR_B;
4631 case 0x15e1: /* (green) */
4632 element = EL_CHAR_C;
4635 case 0x15e2: /* (green) */
4636 element = EL_CHAR_D;
4639 case 0x15e3: /* (green) */
4640 element = EL_CHAR_E;
4643 case 0x15e4: /* (green) */
4644 element = EL_CHAR_F;
4647 case 0x15e5: /* (green) */
4648 element = EL_CHAR_G;
4651 case 0x15e6: /* (green) */
4652 element = EL_CHAR_H;
4655 case 0x15e7: /* (green) */
4656 element = EL_CHAR_I;
4659 case 0x15e8: /* (green) */
4660 element = EL_CHAR_J;
4663 case 0x15e9: /* (green) */
4664 element = EL_CHAR_K;
4667 case 0x15ea: /* (green) */
4668 element = EL_CHAR_L;
4671 case 0x15eb: /* (green) */
4672 element = EL_CHAR_M;
4675 case 0x15ec: /* (green) */
4676 element = EL_CHAR_N;
4679 case 0x15ed: /* (green) */
4680 element = EL_CHAR_O;
4683 case 0x15ee: /* (green) */
4684 element = EL_CHAR_P;
4687 case 0x15ef: /* (green) */
4688 element = EL_CHAR_Q;
4691 case 0x15f0: /* (green) */
4692 element = EL_CHAR_R;
4695 case 0x15f1: /* (green) */
4696 element = EL_CHAR_S;
4699 case 0x15f2: /* (green) */
4700 element = EL_CHAR_T;
4703 case 0x15f3: /* (green) */
4704 element = EL_CHAR_U;
4707 case 0x15f4: /* (green) */
4708 element = EL_CHAR_V;
4711 case 0x15f5: /* (green) */
4712 element = EL_CHAR_W;
4715 case 0x15f6: /* (green) */
4716 element = EL_CHAR_X;
4719 case 0x15f7: /* (green) */
4720 element = EL_CHAR_Y;
4723 case 0x15f8: /* (green) */
4724 element = EL_CHAR_Z;
4727 case 0x15f9: /* (green) */
4728 element = EL_CHAR_AUMLAUT;
4731 case 0x15fa: /* (green) */
4732 element = EL_CHAR_OUMLAUT;
4735 case 0x15fb: /* (green) */
4736 element = EL_CHAR_UUMLAUT;
4739 case 0x15fc: /* (green) */
4740 element = EL_CHAR_0;
4743 case 0x15fd: /* (green) */
4744 element = EL_CHAR_1;
4747 case 0x15fe: /* (green) */
4748 element = EL_CHAR_2;
4751 case 0x15ff: /* (green) */
4752 element = EL_CHAR_3;
4755 case 0x1600: /* (green) */
4756 element = EL_CHAR_4;
4759 case 0x1601: /* (green) */
4760 element = EL_CHAR_5;
4763 case 0x1602: /* (green) */
4764 element = EL_CHAR_6;
4767 case 0x1603: /* (green) */
4768 element = EL_CHAR_7;
4771 case 0x1604: /* (green) */
4772 element = EL_CHAR_8;
4775 case 0x1605: /* (green) */
4776 element = EL_CHAR_9;
4779 case 0x1606: /* (green) */
4780 element = EL_CHAR_PERIOD;
4783 case 0x1607: /* (green) */
4784 element = EL_CHAR_EXCLAM;
4787 case 0x1608: /* (green) */
4788 element = EL_CHAR_COLON;
4791 case 0x1609: /* (green) */
4792 element = EL_CHAR_LESS;
4795 case 0x160a: /* (green) */
4796 element = EL_CHAR_GREATER;
4799 case 0x160b: /* (green) */
4800 element = EL_CHAR_QUESTION;
4803 case 0x160c: /* (green) */
4804 element = EL_CHAR_COPYRIGHT;
4807 case 0x160d: /* (green) */
4808 element = EL_CHAR_UP;
4811 case 0x160e: /* (green) */
4812 element = EL_CHAR_DOWN;
4815 case 0x160f: /* (green) */
4816 element = EL_CHAR_BUTTON;
4819 case 0x1610: /* (green) */
4820 element = EL_CHAR_PLUS;
4823 case 0x1611: /* (green) */
4824 element = EL_CHAR_MINUS;
4827 case 0x1612: /* (green) */
4828 element = EL_CHAR_APOSTROPHE;
4831 case 0x1613: /* (green) */
4832 element = EL_CHAR_PARENLEFT;
4835 case 0x1614: /* (green) */
4836 element = EL_CHAR_PARENRIGHT;
4839 case 0x1615: /* (blue steel) */
4840 element = EL_STEEL_CHAR_A;
4843 case 0x1616: /* (blue steel) */
4844 element = EL_STEEL_CHAR_B;
4847 case 0x1617: /* (blue steel) */
4848 element = EL_STEEL_CHAR_C;
4851 case 0x1618: /* (blue steel) */
4852 element = EL_STEEL_CHAR_D;
4855 case 0x1619: /* (blue steel) */
4856 element = EL_STEEL_CHAR_E;
4859 case 0x161a: /* (blue steel) */
4860 element = EL_STEEL_CHAR_F;
4863 case 0x161b: /* (blue steel) */
4864 element = EL_STEEL_CHAR_G;
4867 case 0x161c: /* (blue steel) */
4868 element = EL_STEEL_CHAR_H;
4871 case 0x161d: /* (blue steel) */
4872 element = EL_STEEL_CHAR_I;
4875 case 0x161e: /* (blue steel) */
4876 element = EL_STEEL_CHAR_J;
4879 case 0x161f: /* (blue steel) */
4880 element = EL_STEEL_CHAR_K;
4883 case 0x1620: /* (blue steel) */
4884 element = EL_STEEL_CHAR_L;
4887 case 0x1621: /* (blue steel) */
4888 element = EL_STEEL_CHAR_M;
4891 case 0x1622: /* (blue steel) */
4892 element = EL_STEEL_CHAR_N;
4895 case 0x1623: /* (blue steel) */
4896 element = EL_STEEL_CHAR_O;
4899 case 0x1624: /* (blue steel) */
4900 element = EL_STEEL_CHAR_P;
4903 case 0x1625: /* (blue steel) */
4904 element = EL_STEEL_CHAR_Q;
4907 case 0x1626: /* (blue steel) */
4908 element = EL_STEEL_CHAR_R;
4911 case 0x1627: /* (blue steel) */
4912 element = EL_STEEL_CHAR_S;
4915 case 0x1628: /* (blue steel) */
4916 element = EL_STEEL_CHAR_T;
4919 case 0x1629: /* (blue steel) */
4920 element = EL_STEEL_CHAR_U;
4923 case 0x162a: /* (blue steel) */
4924 element = EL_STEEL_CHAR_V;
4927 case 0x162b: /* (blue steel) */
4928 element = EL_STEEL_CHAR_W;
4931 case 0x162c: /* (blue steel) */
4932 element = EL_STEEL_CHAR_X;
4935 case 0x162d: /* (blue steel) */
4936 element = EL_STEEL_CHAR_Y;
4939 case 0x162e: /* (blue steel) */
4940 element = EL_STEEL_CHAR_Z;
4943 case 0x162f: /* (blue steel) */
4944 element = EL_STEEL_CHAR_AUMLAUT;
4947 case 0x1630: /* (blue steel) */
4948 element = EL_STEEL_CHAR_OUMLAUT;
4951 case 0x1631: /* (blue steel) */
4952 element = EL_STEEL_CHAR_UUMLAUT;
4955 case 0x1632: /* (blue steel) */
4956 element = EL_STEEL_CHAR_0;
4959 case 0x1633: /* (blue steel) */
4960 element = EL_STEEL_CHAR_1;
4963 case 0x1634: /* (blue steel) */
4964 element = EL_STEEL_CHAR_2;
4967 case 0x1635: /* (blue steel) */
4968 element = EL_STEEL_CHAR_3;
4971 case 0x1636: /* (blue steel) */
4972 element = EL_STEEL_CHAR_4;
4975 case 0x1637: /* (blue steel) */
4976 element = EL_STEEL_CHAR_5;
4979 case 0x1638: /* (blue steel) */
4980 element = EL_STEEL_CHAR_6;
4983 case 0x1639: /* (blue steel) */
4984 element = EL_STEEL_CHAR_7;
4987 case 0x163a: /* (blue steel) */
4988 element = EL_STEEL_CHAR_8;
4991 case 0x163b: /* (blue steel) */
4992 element = EL_STEEL_CHAR_9;
4995 case 0x163c: /* (blue steel) */
4996 element = EL_STEEL_CHAR_PERIOD;
4999 case 0x163d: /* (blue steel) */
5000 element = EL_STEEL_CHAR_EXCLAM;
5003 case 0x163e: /* (blue steel) */
5004 element = EL_STEEL_CHAR_COLON;
5007 case 0x163f: /* (blue steel) */
5008 element = EL_STEEL_CHAR_LESS;
5011 case 0x1640: /* (blue steel) */
5012 element = EL_STEEL_CHAR_GREATER;
5015 case 0x1641: /* (blue steel) */
5016 element = EL_STEEL_CHAR_QUESTION;
5019 case 0x1642: /* (blue steel) */
5020 element = EL_STEEL_CHAR_COPYRIGHT;
5023 case 0x1643: /* (blue steel) */
5024 element = EL_STEEL_CHAR_UP;
5027 case 0x1644: /* (blue steel) */
5028 element = EL_STEEL_CHAR_DOWN;
5031 case 0x1645: /* (blue steel) */
5032 element = EL_STEEL_CHAR_BUTTON;
5035 case 0x1646: /* (blue steel) */
5036 element = EL_STEEL_CHAR_PLUS;
5039 case 0x1647: /* (blue steel) */
5040 element = EL_STEEL_CHAR_MINUS;
5043 case 0x1648: /* (blue steel) */
5044 element = EL_STEEL_CHAR_APOSTROPHE;
5047 case 0x1649: /* (blue steel) */
5048 element = EL_STEEL_CHAR_PARENLEFT;
5051 case 0x164a: /* (blue steel) */
5052 element = EL_STEEL_CHAR_PARENRIGHT;
5055 case 0x164b: /* (green steel) */
5056 element = EL_STEEL_CHAR_A;
5059 case 0x164c: /* (green steel) */
5060 element = EL_STEEL_CHAR_B;
5063 case 0x164d: /* (green steel) */
5064 element = EL_STEEL_CHAR_C;
5067 case 0x164e: /* (green steel) */
5068 element = EL_STEEL_CHAR_D;
5071 case 0x164f: /* (green steel) */
5072 element = EL_STEEL_CHAR_E;
5075 case 0x1650: /* (green steel) */
5076 element = EL_STEEL_CHAR_F;
5079 case 0x1651: /* (green steel) */
5080 element = EL_STEEL_CHAR_G;
5083 case 0x1652: /* (green steel) */
5084 element = EL_STEEL_CHAR_H;
5087 case 0x1653: /* (green steel) */
5088 element = EL_STEEL_CHAR_I;
5091 case 0x1654: /* (green steel) */
5092 element = EL_STEEL_CHAR_J;
5095 case 0x1655: /* (green steel) */
5096 element = EL_STEEL_CHAR_K;
5099 case 0x1656: /* (green steel) */
5100 element = EL_STEEL_CHAR_L;
5103 case 0x1657: /* (green steel) */
5104 element = EL_STEEL_CHAR_M;
5107 case 0x1658: /* (green steel) */
5108 element = EL_STEEL_CHAR_N;
5111 case 0x1659: /* (green steel) */
5112 element = EL_STEEL_CHAR_O;
5115 case 0x165a: /* (green steel) */
5116 element = EL_STEEL_CHAR_P;
5119 case 0x165b: /* (green steel) */
5120 element = EL_STEEL_CHAR_Q;
5123 case 0x165c: /* (green steel) */
5124 element = EL_STEEL_CHAR_R;
5127 case 0x165d: /* (green steel) */
5128 element = EL_STEEL_CHAR_S;
5131 case 0x165e: /* (green steel) */
5132 element = EL_STEEL_CHAR_T;
5135 case 0x165f: /* (green steel) */
5136 element = EL_STEEL_CHAR_U;
5139 case 0x1660: /* (green steel) */
5140 element = EL_STEEL_CHAR_V;
5143 case 0x1661: /* (green steel) */
5144 element = EL_STEEL_CHAR_W;
5147 case 0x1662: /* (green steel) */
5148 element = EL_STEEL_CHAR_X;
5151 case 0x1663: /* (green steel) */
5152 element = EL_STEEL_CHAR_Y;
5155 case 0x1664: /* (green steel) */
5156 element = EL_STEEL_CHAR_Z;
5159 case 0x1665: /* (green steel) */
5160 element = EL_STEEL_CHAR_AUMLAUT;
5163 case 0x1666: /* (green steel) */
5164 element = EL_STEEL_CHAR_OUMLAUT;
5167 case 0x1667: /* (green steel) */
5168 element = EL_STEEL_CHAR_UUMLAUT;
5171 case 0x1668: /* (green steel) */
5172 element = EL_STEEL_CHAR_0;
5175 case 0x1669: /* (green steel) */
5176 element = EL_STEEL_CHAR_1;
5179 case 0x166a: /* (green steel) */
5180 element = EL_STEEL_CHAR_2;
5183 case 0x166b: /* (green steel) */
5184 element = EL_STEEL_CHAR_3;
5187 case 0x166c: /* (green steel) */
5188 element = EL_STEEL_CHAR_4;
5191 case 0x166d: /* (green steel) */
5192 element = EL_STEEL_CHAR_5;
5195 case 0x166e: /* (green steel) */
5196 element = EL_STEEL_CHAR_6;
5199 case 0x166f: /* (green steel) */
5200 element = EL_STEEL_CHAR_7;
5203 case 0x1670: /* (green steel) */
5204 element = EL_STEEL_CHAR_8;
5207 case 0x1671: /* (green steel) */
5208 element = EL_STEEL_CHAR_9;
5211 case 0x1672: /* (green steel) */
5212 element = EL_STEEL_CHAR_PERIOD;
5215 case 0x1673: /* (green steel) */
5216 element = EL_STEEL_CHAR_EXCLAM;
5219 case 0x1674: /* (green steel) */
5220 element = EL_STEEL_CHAR_COLON;
5223 case 0x1675: /* (green steel) */
5224 element = EL_STEEL_CHAR_LESS;
5227 case 0x1676: /* (green steel) */
5228 element = EL_STEEL_CHAR_GREATER;
5231 case 0x1677: /* (green steel) */
5232 element = EL_STEEL_CHAR_QUESTION;
5235 case 0x1678: /* (green steel) */
5236 element = EL_STEEL_CHAR_COPYRIGHT;
5239 case 0x1679: /* (green steel) */
5240 element = EL_STEEL_CHAR_UP;
5243 case 0x167a: /* (green steel) */
5244 element = EL_STEEL_CHAR_DOWN;
5247 case 0x167b: /* (green steel) */
5248 element = EL_STEEL_CHAR_BUTTON;
5251 case 0x167c: /* (green steel) */
5252 element = EL_STEEL_CHAR_PLUS;
5255 case 0x167d: /* (green steel) */
5256 element = EL_STEEL_CHAR_MINUS;
5259 case 0x167e: /* (green steel) */
5260 element = EL_STEEL_CHAR_APOSTROPHE;
5263 case 0x167f: /* (green steel) */
5264 element = EL_STEEL_CHAR_PARENLEFT;
5267 case 0x1680: /* (green steel) */
5268 element = EL_STEEL_CHAR_PARENRIGHT;
5271 case 0x1681: /* gate (red) */
5272 element = EL_EM_GATE_1;
5275 case 0x1682: /* secret gate (red) */
5276 element = EL_GATE_1_GRAY;
5279 case 0x1683: /* gate (yellow) */
5280 element = EL_EM_GATE_2;
5283 case 0x1684: /* secret gate (yellow) */
5284 element = EL_GATE_2_GRAY;
5287 case 0x1685: /* gate (blue) */
5288 element = EL_EM_GATE_4;
5291 case 0x1686: /* secret gate (blue) */
5292 element = EL_GATE_4_GRAY;
5295 case 0x1687: /* gate (green) */
5296 element = EL_EM_GATE_3;
5299 case 0x1688: /* secret gate (green) */
5300 element = EL_GATE_3_GRAY;
5303 case 0x1689: /* gate (white) */
5304 element = EL_DC_GATE_WHITE;
5307 case 0x168a: /* secret gate (white) */
5308 element = EL_DC_GATE_WHITE_GRAY;
5311 case 0x168b: /* secret gate (no key) */
5312 element = EL_DC_GATE_FAKE_GRAY;
5316 element = EL_ROBOT_WHEEL;
5320 element = EL_DC_TIMEGATE_SWITCH;
5324 element = EL_ACID_POOL_BOTTOM;
5328 element = EL_ACID_POOL_TOPLEFT;
5332 element = EL_ACID_POOL_TOPRIGHT;
5336 element = EL_ACID_POOL_BOTTOMLEFT;
5340 element = EL_ACID_POOL_BOTTOMRIGHT;
5344 element = EL_STEELWALL;
5348 element = EL_STEELWALL_SLIPPERY;
5351 case 0x1695: /* steel wall (not round) */
5352 element = EL_STEELWALL;
5355 case 0x1696: /* steel wall (left) */
5356 element = EL_DC_STEELWALL_1_LEFT;
5359 case 0x1697: /* steel wall (bottom) */
5360 element = EL_DC_STEELWALL_1_BOTTOM;
5363 case 0x1698: /* steel wall (right) */
5364 element = EL_DC_STEELWALL_1_RIGHT;
5367 case 0x1699: /* steel wall (top) */
5368 element = EL_DC_STEELWALL_1_TOP;
5371 case 0x169a: /* steel wall (left/bottom) */
5372 element = EL_DC_STEELWALL_1_BOTTOMLEFT;
5375 case 0x169b: /* steel wall (right/bottom) */
5376 element = EL_DC_STEELWALL_1_BOTTOMRIGHT;
5379 case 0x169c: /* steel wall (right/top) */
5380 element = EL_DC_STEELWALL_1_TOPRIGHT;
5383 case 0x169d: /* steel wall (left/top) */
5384 element = EL_DC_STEELWALL_1_TOPLEFT;
5387 case 0x169e: /* steel wall (right/bottom small) */
5388 element = EL_DC_STEELWALL_1_BOTTOMRIGHT_2;
5391 case 0x169f: /* steel wall (left/bottom small) */
5392 element = EL_DC_STEELWALL_1_BOTTOMLEFT_2;
5395 case 0x16a0: /* steel wall (right/top small) */
5396 element = EL_DC_STEELWALL_1_TOPRIGHT_2;
5399 case 0x16a1: /* steel wall (left/top small) */
5400 element = EL_DC_STEELWALL_1_TOPLEFT_2;
5403 case 0x16a2: /* steel wall (left/right) */
5404 element = EL_DC_STEELWALL_1_VERTICAL;
5407 case 0x16a3: /* steel wall (top/bottom) */
5408 element = EL_DC_STEELWALL_1_HORIZONTAL;
5411 case 0x16a4: /* steel wall 2 (left end) */
5412 element = EL_DC_STEELWALL_2_LEFT;
5415 case 0x16a5: /* steel wall 2 (right end) */
5416 element = EL_DC_STEELWALL_2_RIGHT;
5419 case 0x16a6: /* steel wall 2 (top end) */
5420 element = EL_DC_STEELWALL_2_TOP;
5423 case 0x16a7: /* steel wall 2 (bottom end) */
5424 element = EL_DC_STEELWALL_2_BOTTOM;
5427 case 0x16a8: /* steel wall 2 (left/right) */
5428 element = EL_DC_STEELWALL_2_HORIZONTAL;
5431 case 0x16a9: /* steel wall 2 (up/down) */
5432 element = EL_DC_STEELWALL_2_VERTICAL;
5435 case 0x16aa: /* steel wall 2 (mid) */
5436 element = EL_DC_STEELWALL_2_MIDDLE;
5440 element = EL_SIGN_EXCLAMATION;
5444 element = EL_SIGN_RADIOACTIVITY;
5448 element = EL_SIGN_STOP;
5452 element = EL_SIGN_WHEELCHAIR;
5456 element = EL_SIGN_PARKING;
5460 element = EL_SIGN_NO_ENTRY;
5464 element = EL_SIGN_HEART;
5468 element = EL_SIGN_GIVE_WAY;
5472 element = EL_SIGN_ENTRY_FORBIDDEN;
5476 element = EL_SIGN_EMERGENCY_EXIT;
5480 element = EL_SIGN_YIN_YANG;
5484 element = EL_WALL_EMERALD;
5488 element = EL_WALL_DIAMOND;
5492 element = EL_WALL_PEARL;
5496 element = EL_WALL_CRYSTAL;
5500 element = EL_INVISIBLE_WALL;
5504 element = EL_INVISIBLE_STEELWALL;
5507 /* 0x16bc - 0x16cb: */
5508 /* EL_INVISIBLE_SAND */
5511 element = EL_LIGHT_SWITCH;
5515 element = EL_ENVELOPE_1;
5519 if (element >= 0x0117 && element <= 0x036e) /* (?) */
5520 element = EL_DIAMOND;
5521 else if (element >= 0x042d && element <= 0x0684) /* (?) */
5522 element = EL_EMERALD;
5523 else if (element >= 0x157c && element <= 0x158b)
5525 else if (element >= 0x1590 && element <= 0x159f)
5526 element = EL_DC_LANDMINE;
5527 else if (element >= 0x16bc && element <= 0x16cb)
5528 element = EL_INVISIBLE_SAND;
5531 Error(ERR_WARN, "unknown Diamond Caves element 0x%04x", element);
5532 element = EL_UNKNOWN;
5537 return getMappedElement(element);
5540 static void LoadLevelFromFileStream_DC(File *file, struct LevelInfo *level,
5543 byte header[DC_LEVEL_HEADER_SIZE];
5545 int envelope_header_pos = 62;
5546 int envelope_content_pos = 94;
5547 int level_name_pos = 251;
5548 int level_author_pos = 292;
5549 int envelope_header_len;
5550 int envelope_content_len;
5552 int level_author_len;
5554 int num_yamyam_contents;
5557 getDecodedWord_DC(0, TRUE); /* initialize DC2 decoding engine */
5559 for (i = 0; i < DC_LEVEL_HEADER_SIZE / 2; i++)
5561 unsigned short header_word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
5563 header[i * 2 + 0] = header_word >> 8;
5564 header[i * 2 + 1] = header_word & 0xff;
5567 /* read some values from level header to check level decoding integrity */
5568 fieldx = header[6] | (header[7] << 8);
5569 fieldy = header[8] | (header[9] << 8);
5570 num_yamyam_contents = header[60] | (header[61] << 8);
5572 /* do some simple sanity checks to ensure that level was correctly decoded */
5573 if (fieldx < 1 || fieldx > 256 ||
5574 fieldy < 1 || fieldy > 256 ||
5575 num_yamyam_contents < 1 || num_yamyam_contents > 8)
5577 level->no_valid_file = TRUE;
5579 Error(ERR_WARN, "cannot decode level from stream -- using empty level");
5584 /* maximum envelope header size is 31 bytes */
5585 envelope_header_len = header[envelope_header_pos];
5586 /* maximum envelope content size is 110 (156?) bytes */
5587 envelope_content_len = header[envelope_content_pos];
5589 /* maximum level title size is 40 bytes */
5590 level_name_len = MIN(header[level_name_pos], MAX_LEVEL_NAME_LEN);
5591 /* maximum level author size is 30 (51?) bytes */
5592 level_author_len = MIN(header[level_author_pos], MAX_LEVEL_AUTHOR_LEN);
5596 for (i = 0; i < envelope_header_len; i++)
5597 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
5598 level->envelope[0].text[envelope_size++] =
5599 header[envelope_header_pos + 1 + i];
5601 if (envelope_header_len > 0 && envelope_content_len > 0)
5603 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
5604 level->envelope[0].text[envelope_size++] = '\n';
5605 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
5606 level->envelope[0].text[envelope_size++] = '\n';
5609 for (i = 0; i < envelope_content_len; i++)
5610 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
5611 level->envelope[0].text[envelope_size++] =
5612 header[envelope_content_pos + 1 + i];
5614 level->envelope[0].text[envelope_size] = '\0';
5616 level->envelope[0].xsize = MAX_ENVELOPE_XSIZE;
5617 level->envelope[0].ysize = 10;
5618 level->envelope[0].autowrap = TRUE;
5619 level->envelope[0].centered = TRUE;
5621 for (i = 0; i < level_name_len; i++)
5622 level->name[i] = header[level_name_pos + 1 + i];
5623 level->name[level_name_len] = '\0';
5625 for (i = 0; i < level_author_len; i++)
5626 level->author[i] = header[level_author_pos + 1 + i];
5627 level->author[level_author_len] = '\0';
5629 num_yamyam_contents = header[60] | (header[61] << 8);
5630 level->num_yamyam_contents =
5631 MIN(MAX(MIN_ELEMENT_CONTENTS, num_yamyam_contents), MAX_ELEMENT_CONTENTS);
5633 for (i = 0; i < num_yamyam_contents; i++)
5635 for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
5637 unsigned short word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
5638 int element_dc = ((word & 0xff) << 8) | ((word >> 8) & 0xff);
5640 if (i < MAX_ELEMENT_CONTENTS)
5641 level->yamyam_content[i].e[x][y] = getMappedElement_DC(element_dc);
5645 fieldx = header[6] | (header[7] << 8);
5646 fieldy = header[8] | (header[9] << 8);
5647 level->fieldx = MIN(MAX(MIN_LEV_FIELDX, fieldx), MAX_LEV_FIELDX);
5648 level->fieldy = MIN(MAX(MIN_LEV_FIELDY, fieldy), MAX_LEV_FIELDY);
5650 for (y = 0; y < fieldy; y++) for (x = 0; x < fieldx; x++)
5652 unsigned short word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
5653 int element_dc = ((word & 0xff) << 8) | ((word >> 8) & 0xff);
5655 if (x < MAX_LEV_FIELDX && y < MAX_LEV_FIELDY)
5656 level->field[x][y] = getMappedElement_DC(element_dc);
5659 x = MIN(MAX(0, (header[10] | (header[11] << 8)) - 1), MAX_LEV_FIELDX - 1);
5660 y = MIN(MAX(0, (header[12] | (header[13] << 8)) - 1), MAX_LEV_FIELDY - 1);
5661 level->field[x][y] = EL_PLAYER_1;
5663 x = MIN(MAX(0, (header[14] | (header[15] << 8)) - 1), MAX_LEV_FIELDX - 1);
5664 y = MIN(MAX(0, (header[16] | (header[17] << 8)) - 1), MAX_LEV_FIELDY - 1);
5665 level->field[x][y] = EL_PLAYER_2;
5667 level->gems_needed = header[18] | (header[19] << 8);
5669 level->score[SC_EMERALD] = header[20] | (header[21] << 8);
5670 level->score[SC_DIAMOND] = header[22] | (header[23] << 8);
5671 level->score[SC_PEARL] = header[24] | (header[25] << 8);
5672 level->score[SC_CRYSTAL] = header[26] | (header[27] << 8);
5673 level->score[SC_NUT] = header[28] | (header[29] << 8);
5674 level->score[SC_ROBOT] = header[30] | (header[31] << 8);
5675 level->score[SC_SPACESHIP] = header[32] | (header[33] << 8);
5676 level->score[SC_BUG] = header[34] | (header[35] << 8);
5677 level->score[SC_YAMYAM] = header[36] | (header[37] << 8);
5678 level->score[SC_DYNAMITE] = header[38] | (header[39] << 8);
5679 level->score[SC_KEY] = header[40] | (header[41] << 8);
5680 level->score[SC_TIME_BONUS] = header[42] | (header[43] << 8);
5682 level->time = header[44] | (header[45] << 8);
5684 level->amoeba_speed = header[46] | (header[47] << 8);
5685 level->time_light = header[48] | (header[49] << 8);
5686 level->time_timegate = header[50] | (header[51] << 8);
5687 level->time_wheel = header[52] | (header[53] << 8);
5688 level->time_magic_wall = header[54] | (header[55] << 8);
5689 level->extra_time = header[56] | (header[57] << 8);
5690 level->shield_normal_time = header[58] | (header[59] << 8);
5692 /* Diamond Caves has the same (strange) behaviour as Emerald Mine that gems
5693 can slip down from flat walls, like normal walls and steel walls */
5694 level->em_slippery_gems = TRUE;
5697 static void LoadLevelFromFileInfo_DC(struct LevelInfo *level,
5698 struct LevelFileInfo *level_file_info,
5699 boolean level_info_only)
5701 char *filename = level_file_info->filename;
5703 int num_magic_bytes = 8;
5704 char magic_bytes[num_magic_bytes + 1];
5705 int num_levels_to_skip = level_file_info->nr - leveldir_current->first_level;
5707 if (!(file = openFile(filename, MODE_READ)))
5709 level->no_valid_file = TRUE;
5711 if (!level_info_only)
5712 Error(ERR_WARN, "cannot read level '%s' -- using empty level", filename);
5717 // fseek(file, 0x0000, SEEK_SET);
5719 if (level_file_info->packed)
5721 /* read "magic bytes" from start of file */
5722 if (getStringFromFile(file, magic_bytes, num_magic_bytes + 1) == NULL)
5723 magic_bytes[0] = '\0';
5725 /* check "magic bytes" for correct file format */
5726 if (!strPrefix(magic_bytes, "DC2"))
5728 level->no_valid_file = TRUE;
5730 Error(ERR_WARN, "unknown DC level file '%s' -- using empty level",
5736 if (strPrefix(magic_bytes, "DC2Win95") ||
5737 strPrefix(magic_bytes, "DC2Win98"))
5739 int position_first_level = 0x00fa;
5740 int extra_bytes = 4;
5743 /* advance file stream to first level inside the level package */
5744 skip_bytes = position_first_level - num_magic_bytes - extra_bytes;
5746 /* each block of level data is followed by block of non-level data */
5747 num_levels_to_skip *= 2;
5749 /* at least skip header bytes, therefore use ">= 0" instead of "> 0" */
5750 while (num_levels_to_skip >= 0)
5752 /* advance file stream to next level inside the level package */
5753 if (seekFile(file, skip_bytes, SEEK_CUR) != 0)
5755 level->no_valid_file = TRUE;
5757 Error(ERR_WARN, "cannot fseek in file '%s' -- using empty level",
5763 /* skip apparently unused extra bytes following each level */
5764 ReadUnusedBytesFromFile(file, extra_bytes);
5766 /* read size of next level in level package */
5767 skip_bytes = getFile32BitLE(file);
5769 num_levels_to_skip--;
5774 level->no_valid_file = TRUE;
5776 Error(ERR_WARN, "unknown DC2 level file '%s' -- using empty level",
5783 LoadLevelFromFileStream_DC(file, level, level_file_info->nr);
5789 /* ------------------------------------------------------------------------- */
5790 /* functions for loading SB level */
5791 /* ------------------------------------------------------------------------- */
5793 int getMappedElement_SB(int element_ascii, boolean use_ces)
5801 sb_element_mapping[] =
5803 { ' ', EL_EMPTY, EL_CUSTOM_1 }, /* floor (space) */
5804 { '#', EL_STEELWALL, EL_CUSTOM_2 }, /* wall */
5805 { '@', EL_PLAYER_1, EL_CUSTOM_3 }, /* player */
5806 { '$', EL_SOKOBAN_OBJECT, EL_CUSTOM_4 }, /* box */
5807 { '.', EL_SOKOBAN_FIELD_EMPTY, EL_CUSTOM_5 }, /* goal square */
5808 { '*', EL_SOKOBAN_FIELD_FULL, EL_CUSTOM_6 }, /* box on goal square */
5809 { '+', EL_SOKOBAN_FIELD_PLAYER, EL_CUSTOM_7 }, /* player on goal square */
5810 { '_', EL_INVISIBLE_STEELWALL, EL_FROM_LEVEL_TEMPLATE }, /* floor beyond border */
5817 for (i = 0; sb_element_mapping[i].ascii != 0; i++)
5818 if (element_ascii == sb_element_mapping[i].ascii)
5819 return (use_ces ? sb_element_mapping[i].ce : sb_element_mapping[i].sb);
5821 return EL_UNDEFINED;
5824 static void LoadLevelFromFileInfo_SB(struct LevelInfo *level,
5825 struct LevelFileInfo *level_file_info,
5826 boolean level_info_only)
5828 char *filename = level_file_info->filename;
5829 char line[MAX_LINE_LEN], line_raw[MAX_LINE_LEN], previous_line[MAX_LINE_LEN];
5830 char last_comment[MAX_LINE_LEN];
5831 char level_name[MAX_LINE_LEN];
5834 int num_levels_to_skip = level_file_info->nr - leveldir_current->first_level;
5835 boolean read_continued_line = FALSE;
5836 boolean reading_playfield = FALSE;
5837 boolean got_valid_playfield_line = FALSE;
5838 boolean invalid_playfield_char = FALSE;
5839 boolean load_xsb_to_ces = check_special_flags("load_xsb_to_ces");
5840 int file_level_nr = 0;
5842 int x = 0, y = 0; /* initialized to make compilers happy */
5844 last_comment[0] = '\0';
5845 level_name[0] = '\0';
5847 if (!(file = openFile(filename, MODE_READ)))
5849 level->no_valid_file = TRUE;
5851 if (!level_info_only)
5852 Error(ERR_WARN, "cannot read level '%s' -- using empty level", filename);
5857 while (!checkEndOfFile(file))
5859 /* level successfully read, but next level may follow here */
5860 if (!got_valid_playfield_line && reading_playfield)
5862 /* read playfield from single level file -- skip remaining file */
5863 if (!level_file_info->packed)
5866 if (file_level_nr >= num_levels_to_skip)
5871 last_comment[0] = '\0';
5872 level_name[0] = '\0';
5874 reading_playfield = FALSE;
5877 got_valid_playfield_line = FALSE;
5879 /* read next line of input file */
5880 if (!getStringFromFile(file, line, MAX_LINE_LEN))
5883 /* check if line was completely read and is terminated by line break */
5884 if (strlen(line) > 0 && line[strlen(line) - 1] == '\n')
5887 /* cut trailing line break (this can be newline and/or carriage return) */
5888 for (line_ptr = &line[strlen(line)]; line_ptr >= line; line_ptr--)
5889 if ((*line_ptr == '\n' || *line_ptr == '\r') && *(line_ptr + 1) == '\0')
5892 /* copy raw input line for later use (mainly debugging output) */
5893 strcpy(line_raw, line);
5895 if (read_continued_line)
5897 /* append new line to existing line, if there is enough space */
5898 if (strlen(previous_line) + strlen(line_ptr) < MAX_LINE_LEN)
5899 strcat(previous_line, line_ptr);
5901 strcpy(line, previous_line); /* copy storage buffer to line */
5903 read_continued_line = FALSE;
5906 /* if the last character is '\', continue at next line */
5907 if (strlen(line) > 0 && line[strlen(line) - 1] == '\\')
5909 line[strlen(line) - 1] = '\0'; /* cut off trailing backslash */
5910 strcpy(previous_line, line); /* copy line to storage buffer */
5912 read_continued_line = TRUE;
5917 /* skip empty lines */
5918 if (line[0] == '\0')
5921 /* extract comment text from comment line */
5924 for (line_ptr = line; *line_ptr; line_ptr++)
5925 if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != ';')
5928 strcpy(last_comment, line_ptr);
5933 /* extract level title text from line containing level title */
5934 if (line[0] == '\'')
5936 strcpy(level_name, &line[1]);
5938 if (strlen(level_name) > 0 && level_name[strlen(level_name) - 1] == '\'')
5939 level_name[strlen(level_name) - 1] = '\0';
5944 /* skip lines containing only spaces (or empty lines) */
5945 for (line_ptr = line; *line_ptr; line_ptr++)
5946 if (*line_ptr != ' ')
5948 if (*line_ptr == '\0')
5951 /* at this point, we have found a line containing part of a playfield */
5953 got_valid_playfield_line = TRUE;
5955 if (!reading_playfield)
5957 reading_playfield = TRUE;
5958 invalid_playfield_char = FALSE;
5960 for (x = 0; x < MAX_LEV_FIELDX; x++)
5961 for (y = 0; y < MAX_LEV_FIELDY; y++)
5962 level->field[x][y] = getMappedElement_SB(' ', load_xsb_to_ces);
5967 /* start with topmost tile row */
5971 /* skip playfield line if larger row than allowed */
5972 if (y >= MAX_LEV_FIELDY)
5975 /* start with leftmost tile column */
5978 /* read playfield elements from line */
5979 for (line_ptr = line; *line_ptr; line_ptr++)
5981 int mapped_sb_element = getMappedElement_SB(*line_ptr, load_xsb_to_ces);
5983 /* stop parsing playfield line if larger column than allowed */
5984 if (x >= MAX_LEV_FIELDX)
5987 if (mapped_sb_element == EL_UNDEFINED)
5989 invalid_playfield_char = TRUE;
5994 level->field[x][y] = mapped_sb_element;
5996 /* continue with next tile column */
5999 level->fieldx = MAX(x, level->fieldx);
6002 if (invalid_playfield_char)
6004 /* if first playfield line, treat invalid lines as comment lines */
6006 reading_playfield = FALSE;
6011 /* continue with next tile row */
6019 level->fieldx = MIN(MAX(MIN_LEV_FIELDX, level->fieldx), MAX_LEV_FIELDX);
6020 level->fieldy = MIN(MAX(MIN_LEV_FIELDY, level->fieldy), MAX_LEV_FIELDY);
6022 if (!reading_playfield)
6024 level->no_valid_file = TRUE;
6026 Error(ERR_WARN, "cannot read level '%s' -- using empty level", filename);
6031 if (*level_name != '\0')
6033 strncpy(level->name, level_name, MAX_LEVEL_NAME_LEN);
6034 level->name[MAX_LEVEL_NAME_LEN] = '\0';
6036 else if (*last_comment != '\0')
6038 strncpy(level->name, last_comment, MAX_LEVEL_NAME_LEN);
6039 level->name[MAX_LEVEL_NAME_LEN] = '\0';
6043 sprintf(level->name, "--> Level %d <--", level_file_info->nr);
6046 /* set all empty fields beyond the border walls to invisible steel wall */
6047 for (y = 0; y < level->fieldy; y++) for (x = 0; x < level->fieldx; x++)
6049 if ((x == 0 || x == level->fieldx - 1 ||
6050 y == 0 || y == level->fieldy - 1) &&
6051 level->field[x][y] == getMappedElement_SB(' ', load_xsb_to_ces))
6052 FloodFillLevel(x, y, getMappedElement_SB('_', load_xsb_to_ces),
6053 level->field, level->fieldx, level->fieldy);
6056 /* set special level settings for Sokoban levels */
6059 level->use_step_counter = TRUE;
6061 if (load_xsb_to_ces)
6063 /* special global settings can now be set in level template */
6065 level->use_custom_template = TRUE;
6070 /* ------------------------------------------------------------------------- */
6071 /* functions for handling native levels */
6072 /* ------------------------------------------------------------------------- */
6074 static void LoadLevelFromFileInfo_EM(struct LevelInfo *level,
6075 struct LevelFileInfo *level_file_info,
6076 boolean level_info_only)
6078 if (!LoadNativeLevel_EM(level_file_info->filename, level_info_only))
6079 level->no_valid_file = TRUE;
6082 static void LoadLevelFromFileInfo_SP(struct LevelInfo *level,
6083 struct LevelFileInfo *level_file_info,
6084 boolean level_info_only)
6088 /* determine position of requested level inside level package */
6089 if (level_file_info->packed)
6090 pos = level_file_info->nr - leveldir_current->first_level;
6092 if (!LoadNativeLevel_SP(level_file_info->filename, pos, level_info_only))
6093 level->no_valid_file = TRUE;
6096 static void LoadLevelFromFileInfo_MM(struct LevelInfo *level,
6097 struct LevelFileInfo *level_file_info,
6098 boolean level_info_only)
6100 if (!LoadNativeLevel_MM(level_file_info->filename, level_info_only))
6101 level->no_valid_file = TRUE;
6104 void CopyNativeLevel_RND_to_Native(struct LevelInfo *level)
6106 if (level->game_engine_type == GAME_ENGINE_TYPE_EM)
6107 CopyNativeLevel_RND_to_EM(level);
6108 else if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
6109 CopyNativeLevel_RND_to_SP(level);
6110 else if (level->game_engine_type == GAME_ENGINE_TYPE_MM)
6111 CopyNativeLevel_RND_to_MM(level);
6114 void CopyNativeLevel_Native_to_RND(struct LevelInfo *level)
6116 if (level->game_engine_type == GAME_ENGINE_TYPE_EM)
6117 CopyNativeLevel_EM_to_RND(level);
6118 else if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
6119 CopyNativeLevel_SP_to_RND(level);
6120 else if (level->game_engine_type == GAME_ENGINE_TYPE_MM)
6121 CopyNativeLevel_MM_to_RND(level);
6124 void SaveNativeLevel(struct LevelInfo *level)
6126 if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
6128 char *basename = getSingleLevelBasenameExt(level->file_info.nr, "sp");
6129 char *filename = getLevelFilenameFromBasename(basename);
6131 CopyNativeLevel_RND_to_SP(level);
6132 CopyNativeTape_RND_to_SP(level);
6134 SaveNativeLevel_SP(filename);
6139 /* ------------------------------------------------------------------------- */
6140 /* functions for loading generic level */
6141 /* ------------------------------------------------------------------------- */
6143 static void LoadLevelFromFileInfo(struct LevelInfo *level,
6144 struct LevelFileInfo *level_file_info,
6145 boolean level_info_only)
6147 /* always start with reliable default values */
6148 setLevelInfoToDefaults(level, level_info_only, TRUE);
6150 switch (level_file_info->type)
6152 case LEVEL_FILE_TYPE_RND:
6153 LoadLevelFromFileInfo_RND(level, level_file_info, level_info_only);
6156 case LEVEL_FILE_TYPE_EM:
6157 LoadLevelFromFileInfo_EM(level, level_file_info, level_info_only);
6158 level->game_engine_type = GAME_ENGINE_TYPE_EM;
6161 case LEVEL_FILE_TYPE_SP:
6162 LoadLevelFromFileInfo_SP(level, level_file_info, level_info_only);
6163 level->game_engine_type = GAME_ENGINE_TYPE_SP;
6166 case LEVEL_FILE_TYPE_MM:
6167 LoadLevelFromFileInfo_MM(level, level_file_info, level_info_only);
6168 level->game_engine_type = GAME_ENGINE_TYPE_MM;
6171 case LEVEL_FILE_TYPE_DC:
6172 LoadLevelFromFileInfo_DC(level, level_file_info, level_info_only);
6175 case LEVEL_FILE_TYPE_SB:
6176 LoadLevelFromFileInfo_SB(level, level_file_info, level_info_only);
6180 LoadLevelFromFileInfo_RND(level, level_file_info, level_info_only);
6184 /* if level file is invalid, restore level structure to default values */
6185 if (level->no_valid_file)
6186 setLevelInfoToDefaults(level, level_info_only, FALSE);
6188 if (level->game_engine_type == GAME_ENGINE_TYPE_UNKNOWN)
6189 level->game_engine_type = GAME_ENGINE_TYPE_RND;
6191 if (level_file_info->type != LEVEL_FILE_TYPE_RND)
6192 CopyNativeLevel_Native_to_RND(level);
6195 void LoadLevelFromFilename(struct LevelInfo *level, char *filename)
6197 static struct LevelFileInfo level_file_info;
6199 /* always start with reliable default values */
6200 setFileInfoToDefaults(&level_file_info);
6202 level_file_info.nr = 0; /* unknown level number */
6203 level_file_info.type = LEVEL_FILE_TYPE_RND; /* no others supported yet */
6204 level_file_info.filename = filename;
6206 LoadLevelFromFileInfo(level, &level_file_info, FALSE);
6209 static void LoadLevel_InitVersion(struct LevelInfo *level, char *filename)
6213 if (leveldir_current == NULL) /* only when dumping level */
6216 /* all engine modifications also valid for levels which use latest engine */
6217 if (level->game_version < VERSION_IDENT(3,2,0,5))
6219 /* time bonus score was given for 10 s instead of 1 s before 3.2.0-5 */
6220 level->score[SC_TIME_BONUS] /= 10;
6223 if (leveldir_current->latest_engine)
6225 /* ---------- use latest game engine ----------------------------------- */
6227 /* For all levels which are forced to use the latest game engine version
6228 (normally all but user contributed, private and undefined levels), set
6229 the game engine version to the actual version; this allows for actual
6230 corrections in the game engine to take effect for existing, converted
6231 levels (from "classic" or other existing games) to make the emulation
6232 of the corresponding game more accurate, while (hopefully) not breaking
6233 existing levels created from other players. */
6235 level->game_version = GAME_VERSION_ACTUAL;
6237 /* Set special EM style gems behaviour: EM style gems slip down from
6238 normal, steel and growing wall. As this is a more fundamental change,
6239 it seems better to set the default behaviour to "off" (as it is more
6240 natural) and make it configurable in the level editor (as a property
6241 of gem style elements). Already existing converted levels (neither
6242 private nor contributed levels) are changed to the new behaviour. */
6244 if (level->file_version < FILE_VERSION_2_0)
6245 level->em_slippery_gems = TRUE;
6250 /* ---------- use game engine the level was created with ----------------- */
6252 /* For all levels which are not forced to use the latest game engine
6253 version (normally user contributed, private and undefined levels),
6254 use the version of the game engine the levels were created for.
6256 Since 2.0.1, the game engine version is now directly stored
6257 in the level file (chunk "VERS"), so there is no need anymore
6258 to set the game version from the file version (except for old,
6259 pre-2.0 levels, where the game version is still taken from the
6260 file format version used to store the level -- see above). */
6262 /* player was faster than enemies in 1.0.0 and before */
6263 if (level->file_version == FILE_VERSION_1_0)
6264 for (i = 0; i < MAX_PLAYERS; i++)
6265 level->initial_player_stepsize[i] = STEPSIZE_FAST;
6267 /* default behaviour for EM style gems was "slippery" only in 2.0.1 */
6268 if (level->game_version == VERSION_IDENT(2,0,1,0))
6269 level->em_slippery_gems = TRUE;
6271 /* springs could be pushed over pits before (pre-release version) 2.2.0 */
6272 if (level->game_version < VERSION_IDENT(2,2,0,0))
6273 level->use_spring_bug = TRUE;
6275 if (level->game_version < VERSION_IDENT(3,2,0,5))
6277 /* time orb caused limited time in endless time levels before 3.2.0-5 */
6278 level->use_time_orb_bug = TRUE;
6280 /* default behaviour for snapping was "no snap delay" before 3.2.0-5 */
6281 level->block_snap_field = FALSE;
6283 /* extra time score was same value as time left score before 3.2.0-5 */
6284 level->extra_time_score = level->score[SC_TIME_BONUS];
6287 if (level->game_version < VERSION_IDENT(3,2,0,7))
6289 /* default behaviour for snapping was "not continuous" before 3.2.0-7 */
6290 level->continuous_snapping = FALSE;
6293 /* only few elements were able to actively move into acid before 3.1.0 */
6294 /* trigger settings did not exist before 3.1.0; set to default "any" */
6295 if (level->game_version < VERSION_IDENT(3,1,0,0))
6297 /* correct "can move into acid" settings (all zero in old levels) */
6299 level->can_move_into_acid_bits = 0; /* nothing can move into acid */
6300 level->dont_collide_with_bits = 0; /* nothing is deadly when colliding */
6302 setMoveIntoAcidProperty(level, EL_ROBOT, TRUE);
6303 setMoveIntoAcidProperty(level, EL_SATELLITE, TRUE);
6304 setMoveIntoAcidProperty(level, EL_PENGUIN, TRUE);
6305 setMoveIntoAcidProperty(level, EL_BALLOON, TRUE);
6307 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6308 SET_PROPERTY(EL_CUSTOM_START + i, EP_CAN_MOVE_INTO_ACID, TRUE);
6310 /* correct trigger settings (stored as zero == "none" in old levels) */
6312 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6314 int element = EL_CUSTOM_START + i;
6315 struct ElementInfo *ei = &element_info[element];
6317 for (j = 0; j < ei->num_change_pages; j++)
6319 struct ElementChangeInfo *change = &ei->change_page[j];
6321 change->trigger_player = CH_PLAYER_ANY;
6322 change->trigger_page = CH_PAGE_ANY;
6327 /* try to detect and fix "Snake Bite" levels, which are broken with 3.2.0 */
6329 int element = EL_CUSTOM_256;
6330 struct ElementInfo *ei = &element_info[element];
6331 struct ElementChangeInfo *change = &ei->change_page[0];
6333 /* This is needed to fix a problem that was caused by a bugfix in function
6334 game.c/CreateFieldExt() introduced with 3.2.0 that corrects the behaviour
6335 when a custom element changes to EL_SOKOBAN_FIELD_PLAYER (before, it did
6336 not replace walkable elements, but instead just placed the player on it,
6337 without placing the Sokoban field under the player). Unfortunately, this
6338 breaks "Snake Bite" style levels when the snake is halfway through a door
6339 that just closes (the snake head is still alive and can be moved in this
6340 case). This can be fixed by replacing the EL_SOKOBAN_FIELD_PLAYER by the
6341 player (without Sokoban element) which then gets killed as designed). */
6343 if ((strncmp(leveldir_current->identifier, "snake_bite", 10) == 0 ||
6344 strncmp(ei->description, "pause b4 death", 14) == 0) &&
6345 change->target_element == EL_SOKOBAN_FIELD_PLAYER)
6346 change->target_element = EL_PLAYER_1;
6349 /* try to detect and fix "Zelda" style levels, which are broken with 3.2.5 */
6350 if (level->game_version < VERSION_IDENT(3,2,5,0))
6352 /* This is needed to fix a problem that was caused by a bugfix in function
6353 game.c/CheckTriggeredElementChangeExt() introduced with 3.2.5 that
6354 corrects the behaviour when a custom element changes to another custom
6355 element with a higher element number that has change actions defined.
6356 Normally, only one change per frame is allowed for custom elements.
6357 Therefore, it is checked if a custom element already changed in the
6358 current frame; if it did, subsequent changes are suppressed.
6359 Unfortunately, this is only checked for element changes, but not for
6360 change actions, which are still executed. As the function above loops
6361 through all custom elements from lower to higher, an element change
6362 resulting in a lower CE number won't be checked again, while a target
6363 element with a higher number will also be checked, and potential change
6364 actions will get executed for this CE, too (which is wrong), while
6365 further changes are ignored (which is correct). As this bugfix breaks
6366 Zelda II (and introduces graphical bugs to Zelda I, and also breaks a
6367 few other levels like Alan Bond's "FMV"), allow the previous, incorrect
6368 behaviour for existing levels and tapes that make use of this bug */
6370 level->use_action_after_change_bug = TRUE;
6373 /* not centering level after relocating player was default only in 3.2.3 */
6374 if (level->game_version == VERSION_IDENT(3,2,3,0)) /* (no pre-releases) */
6375 level->shifted_relocation = TRUE;
6377 /* EM style elements always chain-exploded in R'n'D engine before 3.2.6 */
6378 if (level->game_version < VERSION_IDENT(3,2,6,0))
6379 level->em_explodes_by_fire = TRUE;
6382 static void LoadLevel_InitStandardElements(struct LevelInfo *level)
6386 /* map elements that have changed in newer versions */
6387 level->amoeba_content = getMappedElementByVersion(level->amoeba_content,
6388 level->game_version);
6389 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
6390 for (x = 0; x < 3; x++)
6391 for (y = 0; y < 3; y++)
6392 level->yamyam_content[i].e[x][y] =
6393 getMappedElementByVersion(level->yamyam_content[i].e[x][y],
6394 level->game_version);
6398 static void LoadLevel_InitCustomElements(struct LevelInfo *level)
6402 /* map custom element change events that have changed in newer versions
6403 (these following values were accidentally changed in version 3.0.1)
6404 (this seems to be needed only for 'ab_levelset3' and 'ab_levelset4') */
6405 if (level->game_version <= VERSION_IDENT(3,0,0,0))
6407 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6409 int element = EL_CUSTOM_START + i;
6411 /* order of checking and copying events to be mapped is important */
6412 /* (do not change the start and end value -- they are constant) */
6413 for (j = CE_BY_OTHER_ACTION; j >= CE_VALUE_GETS_ZERO; j--)
6415 if (HAS_CHANGE_EVENT(element, j - 2))
6417 SET_CHANGE_EVENT(element, j - 2, FALSE);
6418 SET_CHANGE_EVENT(element, j, TRUE);
6422 /* order of checking and copying events to be mapped is important */
6423 /* (do not change the start and end value -- they are constant) */
6424 for (j = CE_PLAYER_COLLECTS_X; j >= CE_HITTING_SOMETHING; j--)
6426 if (HAS_CHANGE_EVENT(element, j - 1))
6428 SET_CHANGE_EVENT(element, j - 1, FALSE);
6429 SET_CHANGE_EVENT(element, j, TRUE);
6435 /* initialize "can_change" field for old levels with only one change page */
6436 if (level->game_version <= VERSION_IDENT(3,0,2,0))
6438 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6440 int element = EL_CUSTOM_START + i;
6442 if (CAN_CHANGE(element))
6443 element_info[element].change->can_change = TRUE;
6447 /* correct custom element values (for old levels without these options) */
6448 if (level->game_version < VERSION_IDENT(3,1,1,0))
6450 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6452 int element = EL_CUSTOM_START + i;
6453 struct ElementInfo *ei = &element_info[element];
6455 if (ei->access_direction == MV_NO_DIRECTION)
6456 ei->access_direction = MV_ALL_DIRECTIONS;
6460 /* correct custom element values (fix invalid values for all versions) */
6463 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6465 int element = EL_CUSTOM_START + i;
6466 struct ElementInfo *ei = &element_info[element];
6468 for (j = 0; j < ei->num_change_pages; j++)
6470 struct ElementChangeInfo *change = &ei->change_page[j];
6472 if (change->trigger_player == CH_PLAYER_NONE)
6473 change->trigger_player = CH_PLAYER_ANY;
6475 if (change->trigger_side == CH_SIDE_NONE)
6476 change->trigger_side = CH_SIDE_ANY;
6481 /* initialize "can_explode" field for old levels which did not store this */
6482 /* !!! CHECK THIS -- "<= 3,1,0,0" IS PROBABLY WRONG !!! */
6483 if (level->game_version <= VERSION_IDENT(3,1,0,0))
6485 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6487 int element = EL_CUSTOM_START + i;
6489 if (EXPLODES_1X1_OLD(element))
6490 element_info[element].explosion_type = EXPLODES_1X1;
6492 SET_PROPERTY(element, EP_CAN_EXPLODE, (EXPLODES_BY_FIRE(element) ||
6493 EXPLODES_SMASHED(element) ||
6494 EXPLODES_IMPACT(element)));
6498 /* correct previously hard-coded move delay values for maze runner style */
6499 if (level->game_version < VERSION_IDENT(3,1,1,0))
6501 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6503 int element = EL_CUSTOM_START + i;
6505 if (element_info[element].move_pattern & MV_MAZE_RUNNER_STYLE)
6507 /* previously hard-coded and therefore ignored */
6508 element_info[element].move_delay_fixed = 9;
6509 element_info[element].move_delay_random = 0;
6514 /* set some other uninitialized values of custom elements in older levels */
6515 if (level->game_version < VERSION_IDENT(3,1,0,0))
6517 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6519 int element = EL_CUSTOM_START + i;
6521 element_info[element].access_direction = MV_ALL_DIRECTIONS;
6523 element_info[element].explosion_delay = 17;
6524 element_info[element].ignition_delay = 8;
6529 static void LoadLevel_InitElements(struct LevelInfo *level, char *filename)
6531 LoadLevel_InitStandardElements(level);
6533 if (level->file_has_custom_elements)
6534 LoadLevel_InitCustomElements(level);
6536 /* initialize element properties for level editor etc. */
6537 InitElementPropertiesEngine(level->game_version);
6538 InitElementPropertiesGfxElement();
6541 static void LoadLevel_InitPlayfield(struct LevelInfo *level, char *filename)
6545 /* map elements that have changed in newer versions */
6546 for (y = 0; y < level->fieldy; y++)
6547 for (x = 0; x < level->fieldx; x++)
6548 level->field[x][y] = getMappedElementByVersion(level->field[x][y],
6549 level->game_version);
6551 /* clear unused playfield data (nicer if level gets resized in editor) */
6552 for (x = 0; x < MAX_LEV_FIELDX; x++)
6553 for (y = 0; y < MAX_LEV_FIELDY; y++)
6554 if (x >= level->fieldx || y >= level->fieldy)
6555 level->field[x][y] = EL_EMPTY;
6557 /* copy elements to runtime playfield array */
6558 for (x = 0; x < MAX_LEV_FIELDX; x++)
6559 for (y = 0; y < MAX_LEV_FIELDY; y++)
6560 Feld[x][y] = level->field[x][y];
6562 /* initialize level size variables for faster access */
6563 lev_fieldx = level->fieldx;
6564 lev_fieldy = level->fieldy;
6566 /* determine border element for this level */
6567 if (level->file_info.type == LEVEL_FILE_TYPE_DC)
6568 BorderElement = EL_EMPTY; /* (in editor, SetBorderElement() is used) */
6573 static void LoadLevel_InitNativeEngines(struct LevelInfo *level,char *filename)
6575 struct LevelFileInfo *level_file_info = &level->file_info;
6577 if (level_file_info->type == LEVEL_FILE_TYPE_RND)
6578 CopyNativeLevel_RND_to_Native(level);
6581 void LoadLevelTemplate(int nr)
6585 setLevelFileInfo(&level_template.file_info, nr);
6586 filename = level_template.file_info.filename;
6588 LoadLevelFromFileInfo(&level_template, &level_template.file_info, FALSE);
6590 LoadLevel_InitVersion(&level_template, filename);
6591 LoadLevel_InitElements(&level_template, filename);
6593 ActivateLevelTemplate();
6596 void LoadLevel(int nr)
6600 setLevelFileInfo(&level.file_info, nr);
6601 filename = level.file_info.filename;
6603 LoadLevelFromFileInfo(&level, &level.file_info, FALSE);
6605 if (level.use_custom_template)
6606 LoadLevelTemplate(-1);
6608 LoadLevel_InitVersion(&level, filename);
6609 LoadLevel_InitElements(&level, filename);
6610 LoadLevel_InitPlayfield(&level, filename);
6612 LoadLevel_InitNativeEngines(&level, filename);
6615 void LoadLevelInfoOnly(int nr)
6617 setLevelFileInfo(&level.file_info, nr);
6619 LoadLevelFromFileInfo(&level, &level.file_info, TRUE);
6622 static int SaveLevel_VERS(FILE *file, struct LevelInfo *level)
6626 chunk_size += putFileVersion(file, level->file_version);
6627 chunk_size += putFileVersion(file, level->game_version);
6632 static int SaveLevel_DATE(FILE *file, struct LevelInfo *level)
6636 chunk_size += putFile16BitBE(file, level->creation_date.year);
6637 chunk_size += putFile8Bit(file, level->creation_date.month);
6638 chunk_size += putFile8Bit(file, level->creation_date.day);
6643 #if ENABLE_HISTORIC_CHUNKS
6644 static void SaveLevel_HEAD(FILE *file, struct LevelInfo *level)
6648 putFile8Bit(file, level->fieldx);
6649 putFile8Bit(file, level->fieldy);
6651 putFile16BitBE(file, level->time);
6652 putFile16BitBE(file, level->gems_needed);
6654 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
6655 putFile8Bit(file, level->name[i]);
6657 for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
6658 putFile8Bit(file, level->score[i]);
6660 for (i = 0; i < STD_ELEMENT_CONTENTS; i++)
6661 for (y = 0; y < 3; y++)
6662 for (x = 0; x < 3; x++)
6663 putFile8Bit(file, (level->encoding_16bit_yamyam ? EL_EMPTY :
6664 level->yamyam_content[i].e[x][y]));
6665 putFile8Bit(file, level->amoeba_speed);
6666 putFile8Bit(file, level->time_magic_wall);
6667 putFile8Bit(file, level->time_wheel);
6668 putFile8Bit(file, (level->encoding_16bit_amoeba ? EL_EMPTY :
6669 level->amoeba_content));
6670 putFile8Bit(file, (level->initial_player_stepsize == STEPSIZE_FAST ? 1 : 0));
6671 putFile8Bit(file, (level->initial_gravity ? 1 : 0));
6672 putFile8Bit(file, (level->encoding_16bit_field ? 1 : 0));
6673 putFile8Bit(file, (level->em_slippery_gems ? 1 : 0));
6675 putFile8Bit(file, (level->use_custom_template ? 1 : 0));
6677 putFile8Bit(file, (level->block_last_field ? 1 : 0));
6678 putFile8Bit(file, (level->sp_block_last_field ? 1 : 0));
6679 putFile32BitBE(file, level->can_move_into_acid_bits);
6680 putFile8Bit(file, level->dont_collide_with_bits);
6682 putFile8Bit(file, (level->use_spring_bug ? 1 : 0));
6683 putFile8Bit(file, (level->use_step_counter ? 1 : 0));
6685 putFile8Bit(file, (level->instant_relocation ? 1 : 0));
6686 putFile8Bit(file, (level->can_pass_to_walkable ? 1 : 0));
6687 putFile8Bit(file, (level->grow_into_diggable ? 1 : 0));
6689 putFile8Bit(file, level->game_engine_type);
6691 WriteUnusedBytesToFile(file, LEVEL_CHUNK_HEAD_UNUSED);
6695 static int SaveLevel_NAME(FILE *file, struct LevelInfo *level)
6700 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
6701 chunk_size += putFile8Bit(file, level->name[i]);
6706 static int SaveLevel_AUTH(FILE *file, struct LevelInfo *level)
6711 for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
6712 chunk_size += putFile8Bit(file, level->author[i]);
6717 #if ENABLE_HISTORIC_CHUNKS
6718 static int SaveLevel_BODY(FILE *file, struct LevelInfo *level)
6723 for (y = 0; y < level->fieldy; y++)
6724 for (x = 0; x < level->fieldx; x++)
6725 if (level->encoding_16bit_field)
6726 chunk_size += putFile16BitBE(file, level->field[x][y]);
6728 chunk_size += putFile8Bit(file, level->field[x][y]);
6734 static int SaveLevel_BODY(FILE *file, struct LevelInfo *level)
6739 for (y = 0; y < level->fieldy; y++)
6740 for (x = 0; x < level->fieldx; x++)
6741 chunk_size += putFile16BitBE(file, level->field[x][y]);
6746 #if ENABLE_HISTORIC_CHUNKS
6747 static void SaveLevel_CONT(FILE *file, struct LevelInfo *level)
6751 putFile8Bit(file, EL_YAMYAM);
6752 putFile8Bit(file, level->num_yamyam_contents);
6753 putFile8Bit(file, 0);
6754 putFile8Bit(file, 0);
6756 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
6757 for (y = 0; y < 3; y++)
6758 for (x = 0; x < 3; x++)
6759 if (level->encoding_16bit_field)
6760 putFile16BitBE(file, level->yamyam_content[i].e[x][y]);
6762 putFile8Bit(file, level->yamyam_content[i].e[x][y]);
6766 #if ENABLE_HISTORIC_CHUNKS
6767 static void SaveLevel_CNT2(FILE *file, struct LevelInfo *level, int element)
6770 int num_contents, content_xsize, content_ysize;
6771 int content_array[MAX_ELEMENT_CONTENTS][3][3];
6773 if (element == EL_YAMYAM)
6775 num_contents = level->num_yamyam_contents;
6779 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
6780 for (y = 0; y < 3; y++)
6781 for (x = 0; x < 3; x++)
6782 content_array[i][x][y] = level->yamyam_content[i].e[x][y];
6784 else if (element == EL_BD_AMOEBA)
6790 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
6791 for (y = 0; y < 3; y++)
6792 for (x = 0; x < 3; x++)
6793 content_array[i][x][y] = EL_EMPTY;
6794 content_array[0][0][0] = level->amoeba_content;
6798 /* chunk header already written -- write empty chunk data */
6799 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_SIZE);
6801 Error(ERR_WARN, "cannot save content for element '%d'", element);
6805 putFile16BitBE(file, element);
6806 putFile8Bit(file, num_contents);
6807 putFile8Bit(file, content_xsize);
6808 putFile8Bit(file, content_ysize);
6810 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_UNUSED);
6812 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
6813 for (y = 0; y < 3; y++)
6814 for (x = 0; x < 3; x++)
6815 putFile16BitBE(file, content_array[i][x][y]);
6819 #if ENABLE_HISTORIC_CHUNKS
6820 static int SaveLevel_CNT3(FILE *file, struct LevelInfo *level, int element)
6822 int envelope_nr = element - EL_ENVELOPE_1;
6823 int envelope_len = strlen(level->envelope_text[envelope_nr]) + 1;
6827 chunk_size += putFile16BitBE(file, element);
6828 chunk_size += putFile16BitBE(file, envelope_len);
6829 chunk_size += putFile8Bit(file, level->envelope_xsize[envelope_nr]);
6830 chunk_size += putFile8Bit(file, level->envelope_ysize[envelope_nr]);
6832 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT3_UNUSED);
6833 chunk_size += LEVEL_CHUNK_CNT3_UNUSED;
6835 for (i = 0; i < envelope_len; i++)
6836 chunk_size += putFile8Bit(file, level->envelope_text[envelope_nr][i]);
6842 #if ENABLE_HISTORIC_CHUNKS
6843 static void SaveLevel_CUS1(FILE *file, struct LevelInfo *level,
6844 int num_changed_custom_elements)
6848 putFile16BitBE(file, num_changed_custom_elements);
6850 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6852 int element = EL_CUSTOM_START + i;
6854 struct ElementInfo *ei = &element_info[element];
6856 if (ei->properties[EP_BITFIELD_BASE_NR] != EP_BITMASK_DEFAULT)
6858 if (check < num_changed_custom_elements)
6860 putFile16BitBE(file, element);
6861 putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
6868 if (check != num_changed_custom_elements) /* should not happen */
6869 Error(ERR_WARN, "inconsistent number of custom element properties");
6873 #if ENABLE_HISTORIC_CHUNKS
6874 static void SaveLevel_CUS2(FILE *file, struct LevelInfo *level,
6875 int num_changed_custom_elements)
6879 putFile16BitBE(file, num_changed_custom_elements);
6881 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6883 int element = EL_CUSTOM_START + i;
6885 if (element_info[element].change->target_element != EL_EMPTY_SPACE)
6887 if (check < num_changed_custom_elements)
6889 putFile16BitBE(file, element);
6890 putFile16BitBE(file, element_info[element].change->target_element);
6897 if (check != num_changed_custom_elements) /* should not happen */
6898 Error(ERR_WARN, "inconsistent number of custom target elements");
6902 #if ENABLE_HISTORIC_CHUNKS
6903 static void SaveLevel_CUS3(FILE *file, struct LevelInfo *level,
6904 int num_changed_custom_elements)
6906 int i, j, x, y, check = 0;
6908 putFile16BitBE(file, num_changed_custom_elements);
6910 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6912 int element = EL_CUSTOM_START + i;
6913 struct ElementInfo *ei = &element_info[element];
6915 if (ei->modified_settings)
6917 if (check < num_changed_custom_elements)
6919 putFile16BitBE(file, element);
6921 for (j = 0; j < MAX_ELEMENT_NAME_LEN; j++)
6922 putFile8Bit(file, ei->description[j]);
6924 putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
6926 /* some free bytes for future properties and padding */
6927 WriteUnusedBytesToFile(file, 7);
6929 putFile8Bit(file, ei->use_gfx_element);
6930 putFile16BitBE(file, ei->gfx_element_initial);
6932 putFile8Bit(file, ei->collect_score_initial);
6933 putFile8Bit(file, ei->collect_count_initial);
6935 putFile16BitBE(file, ei->push_delay_fixed);
6936 putFile16BitBE(file, ei->push_delay_random);
6937 putFile16BitBE(file, ei->move_delay_fixed);
6938 putFile16BitBE(file, ei->move_delay_random);
6940 putFile16BitBE(file, ei->move_pattern);
6941 putFile8Bit(file, ei->move_direction_initial);
6942 putFile8Bit(file, ei->move_stepsize);
6944 for (y = 0; y < 3; y++)
6945 for (x = 0; x < 3; x++)
6946 putFile16BitBE(file, ei->content.e[x][y]);
6948 putFile32BitBE(file, ei->change->events);
6950 putFile16BitBE(file, ei->change->target_element);
6952 putFile16BitBE(file, ei->change->delay_fixed);
6953 putFile16BitBE(file, ei->change->delay_random);
6954 putFile16BitBE(file, ei->change->delay_frames);
6956 putFile16BitBE(file, ei->change->initial_trigger_element);
6958 putFile8Bit(file, ei->change->explode);
6959 putFile8Bit(file, ei->change->use_target_content);
6960 putFile8Bit(file, ei->change->only_if_complete);
6961 putFile8Bit(file, ei->change->use_random_replace);
6963 putFile8Bit(file, ei->change->random_percentage);
6964 putFile8Bit(file, ei->change->replace_when);
6966 for (y = 0; y < 3; y++)
6967 for (x = 0; x < 3; x++)
6968 putFile16BitBE(file, ei->change->content.e[x][y]);
6970 putFile8Bit(file, ei->slippery_type);
6972 /* some free bytes for future properties and padding */
6973 WriteUnusedBytesToFile(file, LEVEL_CPART_CUS3_UNUSED);
6980 if (check != num_changed_custom_elements) /* should not happen */
6981 Error(ERR_WARN, "inconsistent number of custom element properties");
6985 #if ENABLE_HISTORIC_CHUNKS
6986 static void SaveLevel_CUS4(FILE *file, struct LevelInfo *level, int element)
6988 struct ElementInfo *ei = &element_info[element];
6991 /* ---------- custom element base property values (96 bytes) ------------- */
6993 putFile16BitBE(file, element);
6995 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
6996 putFile8Bit(file, ei->description[i]);
6998 putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
7000 WriteUnusedBytesToFile(file, 4); /* reserved for more base properties */
7002 putFile8Bit(file, ei->num_change_pages);
7004 putFile16BitBE(file, ei->ce_value_fixed_initial);
7005 putFile16BitBE(file, ei->ce_value_random_initial);
7006 putFile8Bit(file, ei->use_last_ce_value);
7008 putFile8Bit(file, ei->use_gfx_element);
7009 putFile16BitBE(file, ei->gfx_element_initial);
7011 putFile8Bit(file, ei->collect_score_initial);
7012 putFile8Bit(file, ei->collect_count_initial);
7014 putFile8Bit(file, ei->drop_delay_fixed);
7015 putFile8Bit(file, ei->push_delay_fixed);
7016 putFile8Bit(file, ei->drop_delay_random);
7017 putFile8Bit(file, ei->push_delay_random);
7018 putFile16BitBE(file, ei->move_delay_fixed);
7019 putFile16BitBE(file, ei->move_delay_random);
7021 /* bits 0 - 15 of "move_pattern" ... */
7022 putFile16BitBE(file, ei->move_pattern & 0xffff);
7023 putFile8Bit(file, ei->move_direction_initial);
7024 putFile8Bit(file, ei->move_stepsize);
7026 putFile8Bit(file, ei->slippery_type);
7028 for (y = 0; y < 3; y++)
7029 for (x = 0; x < 3; x++)
7030 putFile16BitBE(file, ei->content.e[x][y]);
7032 putFile16BitBE(file, ei->move_enter_element);
7033 putFile16BitBE(file, ei->move_leave_element);
7034 putFile8Bit(file, ei->move_leave_type);
7036 /* ... bits 16 - 31 of "move_pattern" (not nice, but downward compatible) */
7037 putFile16BitBE(file, (ei->move_pattern >> 16) & 0xffff);
7039 putFile8Bit(file, ei->access_direction);
7041 putFile8Bit(file, ei->explosion_delay);
7042 putFile8Bit(file, ei->ignition_delay);
7043 putFile8Bit(file, ei->explosion_type);
7045 /* some free bytes for future custom property values and padding */
7046 WriteUnusedBytesToFile(file, 1);
7048 /* ---------- change page property values (48 bytes) --------------------- */
7050 for (i = 0; i < ei->num_change_pages; i++)
7052 struct ElementChangeInfo *change = &ei->change_page[i];
7053 unsigned int event_bits;
7055 /* bits 0 - 31 of "has_event[]" ... */
7057 for (j = 0; j < MIN(NUM_CHANGE_EVENTS, 32); j++)
7058 if (change->has_event[j])
7059 event_bits |= (1 << j);
7060 putFile32BitBE(file, event_bits);
7062 putFile16BitBE(file, change->target_element);
7064 putFile16BitBE(file, change->delay_fixed);
7065 putFile16BitBE(file, change->delay_random);
7066 putFile16BitBE(file, change->delay_frames);
7068 putFile16BitBE(file, change->initial_trigger_element);
7070 putFile8Bit(file, change->explode);
7071 putFile8Bit(file, change->use_target_content);
7072 putFile8Bit(file, change->only_if_complete);
7073 putFile8Bit(file, change->use_random_replace);
7075 putFile8Bit(file, change->random_percentage);
7076 putFile8Bit(file, change->replace_when);
7078 for (y = 0; y < 3; y++)
7079 for (x = 0; x < 3; x++)
7080 putFile16BitBE(file, change->target_content.e[x][y]);
7082 putFile8Bit(file, change->can_change);
7084 putFile8Bit(file, change->trigger_side);
7086 putFile8Bit(file, change->trigger_player);
7087 putFile8Bit(file, (change->trigger_page == CH_PAGE_ANY ? CH_PAGE_ANY_FILE :
7088 log_2(change->trigger_page)));
7090 putFile8Bit(file, change->has_action);
7091 putFile8Bit(file, change->action_type);
7092 putFile8Bit(file, change->action_mode);
7093 putFile16BitBE(file, change->action_arg);
7095 /* ... bits 32 - 39 of "has_event[]" (not nice, but downward compatible) */
7097 for (j = 32; j < NUM_CHANGE_EVENTS; j++)
7098 if (change->has_event[j])
7099 event_bits |= (1 << (j - 32));
7100 putFile8Bit(file, event_bits);
7105 #if ENABLE_HISTORIC_CHUNKS
7106 static void SaveLevel_GRP1(FILE *file, struct LevelInfo *level, int element)
7108 struct ElementInfo *ei = &element_info[element];
7109 struct ElementGroupInfo *group = ei->group;
7112 putFile16BitBE(file, element);
7114 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
7115 putFile8Bit(file, ei->description[i]);
7117 putFile8Bit(file, group->num_elements);
7119 putFile8Bit(file, ei->use_gfx_element);
7120 putFile16BitBE(file, ei->gfx_element_initial);
7122 putFile8Bit(file, group->choice_mode);
7124 /* some free bytes for future values and padding */
7125 WriteUnusedBytesToFile(file, 3);
7127 for (i = 0; i < MAX_ELEMENTS_IN_GROUP; i++)
7128 putFile16BitBE(file, group->element[i]);
7132 static int SaveLevel_MicroChunk(FILE *file, struct LevelFileConfigInfo *entry,
7133 boolean write_element)
7135 int save_type = entry->save_type;
7136 int data_type = entry->data_type;
7137 int conf_type = entry->conf_type;
7138 int byte_mask = conf_type & CONF_MASK_BYTES;
7139 int element = entry->element;
7140 int default_value = entry->default_value;
7142 boolean modified = FALSE;
7144 if (byte_mask != CONF_MASK_MULTI_BYTES)
7146 void *value_ptr = entry->value;
7147 int value = (data_type == TYPE_BOOLEAN ? *(boolean *)value_ptr :
7150 /* check if any settings have been modified before saving them */
7151 if (value != default_value)
7154 /* do not save if explicitly told or if unmodified default settings */
7155 if ((save_type == SAVE_CONF_NEVER) ||
7156 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
7160 num_bytes += putFile16BitBE(file, element);
7162 num_bytes += putFile8Bit(file, conf_type);
7163 num_bytes += (byte_mask == CONF_MASK_1_BYTE ? putFile8Bit (file, value) :
7164 byte_mask == CONF_MASK_2_BYTE ? putFile16BitBE(file, value) :
7165 byte_mask == CONF_MASK_4_BYTE ? putFile32BitBE(file, value) :
7168 else if (data_type == TYPE_STRING)
7170 char *default_string = entry->default_string;
7171 char *string = (char *)(entry->value);
7172 int string_length = strlen(string);
7175 /* check if any settings have been modified before saving them */
7176 if (!strEqual(string, default_string))
7179 /* do not save if explicitly told or if unmodified default settings */
7180 if ((save_type == SAVE_CONF_NEVER) ||
7181 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
7185 num_bytes += putFile16BitBE(file, element);
7187 num_bytes += putFile8Bit(file, conf_type);
7188 num_bytes += putFile16BitBE(file, string_length);
7190 for (i = 0; i < string_length; i++)
7191 num_bytes += putFile8Bit(file, string[i]);
7193 else if (data_type == TYPE_ELEMENT_LIST)
7195 int *element_array = (int *)(entry->value);
7196 int num_elements = *(int *)(entry->num_entities);
7199 /* check if any settings have been modified before saving them */
7200 for (i = 0; i < num_elements; i++)
7201 if (element_array[i] != default_value)
7204 /* do not save if explicitly told or if unmodified default settings */
7205 if ((save_type == SAVE_CONF_NEVER) ||
7206 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
7210 num_bytes += putFile16BitBE(file, element);
7212 num_bytes += putFile8Bit(file, conf_type);
7213 num_bytes += putFile16BitBE(file, num_elements * CONF_ELEMENT_NUM_BYTES);
7215 for (i = 0; i < num_elements; i++)
7216 num_bytes += putFile16BitBE(file, element_array[i]);
7218 else if (data_type == TYPE_CONTENT_LIST)
7220 struct Content *content = (struct Content *)(entry->value);
7221 int num_contents = *(int *)(entry->num_entities);
7224 /* check if any settings have been modified before saving them */
7225 for (i = 0; i < num_contents; i++)
7226 for (y = 0; y < 3; y++)
7227 for (x = 0; x < 3; x++)
7228 if (content[i].e[x][y] != default_value)
7231 /* do not save if explicitly told or if unmodified default settings */
7232 if ((save_type == SAVE_CONF_NEVER) ||
7233 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
7237 num_bytes += putFile16BitBE(file, element);
7239 num_bytes += putFile8Bit(file, conf_type);
7240 num_bytes += putFile16BitBE(file, num_contents * CONF_CONTENT_NUM_BYTES);
7242 for (i = 0; i < num_contents; i++)
7243 for (y = 0; y < 3; y++)
7244 for (x = 0; x < 3; x++)
7245 num_bytes += putFile16BitBE(file, content[i].e[x][y]);
7251 static int SaveLevel_INFO(FILE *file, struct LevelInfo *level)
7256 li = *level; /* copy level data into temporary buffer */
7258 for (i = 0; chunk_config_INFO[i].data_type != -1; i++)
7259 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_INFO[i], FALSE);
7264 static int SaveLevel_ELEM(FILE *file, struct LevelInfo *level)
7269 li = *level; /* copy level data into temporary buffer */
7271 for (i = 0; chunk_config_ELEM[i].data_type != -1; i++)
7272 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_ELEM[i], TRUE);
7277 static int SaveLevel_NOTE(FILE *file, struct LevelInfo *level, int element)
7279 int envelope_nr = element - EL_ENVELOPE_1;
7283 chunk_size += putFile16BitBE(file, element);
7285 /* copy envelope data into temporary buffer */
7286 xx_envelope = level->envelope[envelope_nr];
7288 for (i = 0; chunk_config_NOTE[i].data_type != -1; i++)
7289 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_NOTE[i], FALSE);
7294 static int SaveLevel_CUSX(FILE *file, struct LevelInfo *level, int element)
7296 struct ElementInfo *ei = &element_info[element];
7300 chunk_size += putFile16BitBE(file, element);
7302 xx_ei = *ei; /* copy element data into temporary buffer */
7304 /* set default description string for this specific element */
7305 strcpy(xx_default_description, getDefaultElementDescription(ei));
7307 for (i = 0; chunk_config_CUSX_base[i].data_type != -1; i++)
7308 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_CUSX_base[i], FALSE);
7310 for (i = 0; i < ei->num_change_pages; i++)
7312 struct ElementChangeInfo *change = &ei->change_page[i];
7314 xx_current_change_page = i;
7316 xx_change = *change; /* copy change data into temporary buffer */
7319 setEventBitsFromEventFlags(change);
7321 for (j = 0; chunk_config_CUSX_change[j].data_type != -1; j++)
7322 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_CUSX_change[j],
7329 static int SaveLevel_GRPX(FILE *file, struct LevelInfo *level, int element)
7331 struct ElementInfo *ei = &element_info[element];
7332 struct ElementGroupInfo *group = ei->group;
7336 chunk_size += putFile16BitBE(file, element);
7338 xx_ei = *ei; /* copy element data into temporary buffer */
7339 xx_group = *group; /* copy group data into temporary buffer */
7341 /* set default description string for this specific element */
7342 strcpy(xx_default_description, getDefaultElementDescription(ei));
7344 for (i = 0; chunk_config_GRPX[i].data_type != -1; i++)
7345 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_GRPX[i], FALSE);
7350 static void SaveLevelFromFilename(struct LevelInfo *level, char *filename,
7351 boolean save_as_template)
7357 if (!(file = fopen(filename, MODE_WRITE)))
7359 Error(ERR_WARN, "cannot save level file '%s'", filename);
7363 level->file_version = FILE_VERSION_ACTUAL;
7364 level->game_version = GAME_VERSION_ACTUAL;
7366 level->creation_date = getCurrentDate();
7368 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
7369 putFileChunkBE(file, "CAVE", CHUNK_SIZE_NONE);
7371 chunk_size = SaveLevel_VERS(NULL, level);
7372 putFileChunkBE(file, "VERS", chunk_size);
7373 SaveLevel_VERS(file, level);
7375 chunk_size = SaveLevel_DATE(NULL, level);
7376 putFileChunkBE(file, "DATE", chunk_size);
7377 SaveLevel_DATE(file, level);
7379 chunk_size = SaveLevel_NAME(NULL, level);
7380 putFileChunkBE(file, "NAME", chunk_size);
7381 SaveLevel_NAME(file, level);
7383 chunk_size = SaveLevel_AUTH(NULL, level);
7384 putFileChunkBE(file, "AUTH", chunk_size);
7385 SaveLevel_AUTH(file, level);
7387 chunk_size = SaveLevel_INFO(NULL, level);
7388 putFileChunkBE(file, "INFO", chunk_size);
7389 SaveLevel_INFO(file, level);
7391 chunk_size = SaveLevel_BODY(NULL, level);
7392 putFileChunkBE(file, "BODY", chunk_size);
7393 SaveLevel_BODY(file, level);
7395 chunk_size = SaveLevel_ELEM(NULL, level);
7396 if (chunk_size > LEVEL_CHUNK_ELEM_UNCHANGED) /* save if changed */
7398 putFileChunkBE(file, "ELEM", chunk_size);
7399 SaveLevel_ELEM(file, level);
7402 for (i = 0; i < NUM_ENVELOPES; i++)
7404 int element = EL_ENVELOPE_1 + i;
7406 chunk_size = SaveLevel_NOTE(NULL, level, element);
7407 if (chunk_size > LEVEL_CHUNK_NOTE_UNCHANGED) /* save if changed */
7409 putFileChunkBE(file, "NOTE", chunk_size);
7410 SaveLevel_NOTE(file, level, element);
7414 /* if not using template level, check for non-default custom/group elements */
7415 if (!level->use_custom_template || save_as_template)
7417 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7419 int element = EL_CUSTOM_START + i;
7421 chunk_size = SaveLevel_CUSX(NULL, level, element);
7422 if (chunk_size > LEVEL_CHUNK_CUSX_UNCHANGED) /* save if changed */
7424 putFileChunkBE(file, "CUSX", chunk_size);
7425 SaveLevel_CUSX(file, level, element);
7429 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
7431 int element = EL_GROUP_START + i;
7433 chunk_size = SaveLevel_GRPX(NULL, level, element);
7434 if (chunk_size > LEVEL_CHUNK_GRPX_UNCHANGED) /* save if changed */
7436 putFileChunkBE(file, "GRPX", chunk_size);
7437 SaveLevel_GRPX(file, level, element);
7444 SetFilePermissions(filename, PERMS_PRIVATE);
7447 void SaveLevel(int nr)
7449 char *filename = getDefaultLevelFilename(nr);
7451 SaveLevelFromFilename(&level, filename, FALSE);
7454 void SaveLevelTemplate()
7456 char *filename = getLocalLevelTemplateFilename();
7458 SaveLevelFromFilename(&level, filename, TRUE);
7461 boolean SaveLevelChecked(int nr)
7463 char *filename = getDefaultLevelFilename(nr);
7464 boolean new_level = !fileExists(filename);
7465 boolean level_saved = FALSE;
7467 if (new_level || Request("Save this level and kill the old?", REQ_ASK))
7472 Request("Level saved!", REQ_CONFIRM);
7480 void DumpLevel(struct LevelInfo *level)
7482 if (level->no_level_file || level->no_valid_file)
7484 Error(ERR_WARN, "cannot dump -- no valid level file found");
7490 Print("Level xxx (file version %08d, game version %08d)\n",
7491 level->file_version, level->game_version);
7494 Print("Level author: '%s'\n", level->author);
7495 Print("Level title: '%s'\n", level->name);
7497 Print("Playfield size: %d x %d\n", level->fieldx, level->fieldy);
7499 Print("Level time: %d seconds\n", level->time);
7500 Print("Gems needed: %d\n", level->gems_needed);
7502 Print("Time for magic wall: %d seconds\n", level->time_magic_wall);
7503 Print("Time for wheel: %d seconds\n", level->time_wheel);
7504 Print("Time for light: %d seconds\n", level->time_light);
7505 Print("Time for timegate: %d seconds\n", level->time_timegate);
7507 Print("Amoeba speed: %d\n", level->amoeba_speed);
7510 Print("EM style slippery gems: %s\n", (level->em_slippery_gems ? "yes" : "no"));
7511 Print("Player blocks last field: %s\n", (level->block_last_field ? "yes" : "no"));
7512 Print("SP player blocks last field: %s\n", (level->sp_block_last_field ? "yes" : "no"));
7513 Print("use spring bug: %s\n", (level->use_spring_bug ? "yes" : "no"));
7514 Print("use step counter: %s\n", (level->use_step_counter ? "yes" : "no"));
7520 /* ========================================================================= */
7521 /* tape file functions */
7522 /* ========================================================================= */
7524 static void setTapeInfoToDefaults()
7528 /* always start with reliable default values (empty tape) */
7531 /* default values (also for pre-1.2 tapes) with only the first player */
7532 tape.player_participates[0] = TRUE;
7533 for (i = 1; i < MAX_PLAYERS; i++)
7534 tape.player_participates[i] = FALSE;
7536 /* at least one (default: the first) player participates in every tape */
7537 tape.num_participating_players = 1;
7539 tape.level_nr = level_nr;
7541 tape.changed = FALSE;
7543 tape.recording = FALSE;
7544 tape.playing = FALSE;
7545 tape.pausing = FALSE;
7547 tape.no_valid_file = FALSE;
7550 static int LoadTape_VERS(File *file, int chunk_size, struct TapeInfo *tape)
7552 tape->file_version = getFileVersion(file);
7553 tape->game_version = getFileVersion(file);
7558 static int LoadTape_HEAD(File *file, int chunk_size, struct TapeInfo *tape)
7562 tape->random_seed = getFile32BitBE(file);
7563 tape->date = getFile32BitBE(file);
7564 tape->length = getFile32BitBE(file);
7566 /* read header fields that are new since version 1.2 */
7567 if (tape->file_version >= FILE_VERSION_1_2)
7569 byte store_participating_players = getFile8Bit(file);
7572 /* since version 1.2, tapes store which players participate in the tape */
7573 tape->num_participating_players = 0;
7574 for (i = 0; i < MAX_PLAYERS; i++)
7576 tape->player_participates[i] = FALSE;
7578 if (store_participating_players & (1 << i))
7580 tape->player_participates[i] = TRUE;
7581 tape->num_participating_players++;
7585 tape->use_mouse = (getFile8Bit(file) == 1 ? TRUE : FALSE);
7587 ReadUnusedBytesFromFile(file, TAPE_CHUNK_HEAD_UNUSED);
7589 engine_version = getFileVersion(file);
7590 if (engine_version > 0)
7591 tape->engine_version = engine_version;
7593 tape->engine_version = tape->game_version;
7599 static int LoadTape_INFO(File *file, int chunk_size, struct TapeInfo *tape)
7601 int level_identifier_size;
7604 level_identifier_size = getFile16BitBE(file);
7606 tape->level_identifier =
7607 checked_realloc(tape->level_identifier, level_identifier_size);
7609 for (i = 0; i < level_identifier_size; i++)
7610 tape->level_identifier[i] = getFile8Bit(file);
7612 tape->level_nr = getFile16BitBE(file);
7614 chunk_size = 2 + level_identifier_size + 2;
7619 static int LoadTape_BODY(File *file, int chunk_size, struct TapeInfo *tape)
7623 (tape->use_mouse ? 3 : tape->num_participating_players) + 1;
7624 int chunk_size_expected = tape_pos_size * tape->length;
7626 if (chunk_size_expected != chunk_size)
7628 ReadUnusedBytesFromFile(file, chunk_size);
7629 return chunk_size_expected;
7632 for (i = 0; i < tape->length; i++)
7634 if (i >= MAX_TAPE_LEN)
7636 Error(ERR_WARN, "tape truncated -- size exceeds maximum tape size %d",
7639 // tape too large; read and ignore remaining tape data from this chunk
7640 for (;i < tape->length; i++)
7641 ReadUnusedBytesFromFile(file, tape->num_participating_players + 1);
7646 if (tape->use_mouse)
7648 tape->pos[i].action[TAPE_ACTION_LX] = getFile8Bit(file);
7649 tape->pos[i].action[TAPE_ACTION_LY] = getFile8Bit(file);
7650 tape->pos[i].action[TAPE_ACTION_BUTTON] = getFile8Bit(file);
7652 tape->pos[i].action[TAPE_ACTION_UNUSED] = 0;
7656 for (j = 0; j < MAX_PLAYERS; j++)
7658 tape->pos[i].action[j] = MV_NONE;
7660 if (tape->player_participates[j])
7661 tape->pos[i].action[j] = getFile8Bit(file);
7665 tape->pos[i].delay = getFile8Bit(file);
7667 if (tape->file_version == FILE_VERSION_1_0)
7669 /* eliminate possible diagonal moves in old tapes */
7670 /* this is only for backward compatibility */
7672 byte joy_dir[4] = { JOY_LEFT, JOY_RIGHT, JOY_UP, JOY_DOWN };
7673 byte action = tape->pos[i].action[0];
7674 int k, num_moves = 0;
7676 for (k = 0; k<4; k++)
7678 if (action & joy_dir[k])
7680 tape->pos[i + num_moves].action[0] = joy_dir[k];
7682 tape->pos[i + num_moves].delay = 0;
7691 tape->length += num_moves;
7694 else if (tape->file_version < FILE_VERSION_2_0)
7696 /* convert pre-2.0 tapes to new tape format */
7698 if (tape->pos[i].delay > 1)
7701 tape->pos[i + 1] = tape->pos[i];
7702 tape->pos[i + 1].delay = 1;
7705 for (j = 0; j < MAX_PLAYERS; j++)
7706 tape->pos[i].action[j] = MV_NONE;
7707 tape->pos[i].delay--;
7714 if (checkEndOfFile(file))
7718 if (i != tape->length)
7719 chunk_size = tape_pos_size * i;
7724 void LoadTape_SokobanSolution(char *filename)
7727 int move_delay = TILESIZE / level.initial_player_stepsize[0];
7729 if (!(file = openFile(filename, MODE_READ)))
7731 tape.no_valid_file = TRUE;
7736 while (!checkEndOfFile(file))
7738 unsigned char c = getByteFromFile(file);
7740 if (checkEndOfFile(file))
7747 tape.pos[tape.length].action[0] = MV_UP;
7748 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
7754 tape.pos[tape.length].action[0] = MV_DOWN;
7755 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
7761 tape.pos[tape.length].action[0] = MV_LEFT;
7762 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
7768 tape.pos[tape.length].action[0] = MV_RIGHT;
7769 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
7777 /* ignore white-space characters */
7781 tape.no_valid_file = TRUE;
7783 Error(ERR_WARN, "unsupported Sokoban solution file '%s' ['%d']", filename, c);
7791 if (tape.no_valid_file)
7794 tape.length_frames = GetTapeLengthFrames();
7795 tape.length_seconds = GetTapeLengthSeconds();
7798 void LoadTapeFromFilename(char *filename)
7800 char cookie[MAX_LINE_LEN];
7801 char chunk_name[CHUNK_ID_LEN + 1];
7805 /* always start with reliable default values */
7806 setTapeInfoToDefaults();
7808 if (strSuffix(filename, ".sln"))
7810 LoadTape_SokobanSolution(filename);
7815 if (!(file = openFile(filename, MODE_READ)))
7817 tape.no_valid_file = TRUE;
7822 getFileChunkBE(file, chunk_name, NULL);
7823 if (strEqual(chunk_name, "RND1"))
7825 getFile32BitBE(file); /* not used */
7827 getFileChunkBE(file, chunk_name, NULL);
7828 if (!strEqual(chunk_name, "TAPE"))
7830 tape.no_valid_file = TRUE;
7832 Error(ERR_WARN, "unknown format of tape file '%s'", filename);
7839 else /* check for pre-2.0 file format with cookie string */
7841 strcpy(cookie, chunk_name);
7842 if (getStringFromFile(file, &cookie[4], MAX_LINE_LEN - 4) == NULL)
7844 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
7845 cookie[strlen(cookie) - 1] = '\0';
7847 if (!checkCookieString(cookie, TAPE_COOKIE_TMPL))
7849 tape.no_valid_file = TRUE;
7851 Error(ERR_WARN, "unknown format of tape file '%s'", filename);
7858 if ((tape.file_version = getFileVersionFromCookieString(cookie)) == -1)
7860 tape.no_valid_file = TRUE;
7862 Error(ERR_WARN, "unsupported version of tape file '%s'", filename);
7869 /* pre-2.0 tape files have no game version, so use file version here */
7870 tape.game_version = tape.file_version;
7873 if (tape.file_version < FILE_VERSION_1_2)
7875 /* tape files from versions before 1.2.0 without chunk structure */
7876 LoadTape_HEAD(file, TAPE_CHUNK_HEAD_SIZE, &tape);
7877 LoadTape_BODY(file, 2 * tape.length, &tape);
7885 int (*loader)(File *, int, struct TapeInfo *);
7889 { "VERS", TAPE_CHUNK_VERS_SIZE, LoadTape_VERS },
7890 { "HEAD", TAPE_CHUNK_HEAD_SIZE, LoadTape_HEAD },
7891 { "INFO", -1, LoadTape_INFO },
7892 { "BODY", -1, LoadTape_BODY },
7896 while (getFileChunkBE(file, chunk_name, &chunk_size))
7900 while (chunk_info[i].name != NULL &&
7901 !strEqual(chunk_name, chunk_info[i].name))
7904 if (chunk_info[i].name == NULL)
7906 Error(ERR_WARN, "unknown chunk '%s' in tape file '%s'",
7907 chunk_name, filename);
7908 ReadUnusedBytesFromFile(file, chunk_size);
7910 else if (chunk_info[i].size != -1 &&
7911 chunk_info[i].size != chunk_size)
7913 Error(ERR_WARN, "wrong size (%d) of chunk '%s' in tape file '%s'",
7914 chunk_size, chunk_name, filename);
7915 ReadUnusedBytesFromFile(file, chunk_size);
7919 /* call function to load this tape chunk */
7920 int chunk_size_expected =
7921 (chunk_info[i].loader)(file, chunk_size, &tape);
7923 /* the size of some chunks cannot be checked before reading other
7924 chunks first (like "HEAD" and "BODY") that contain some header
7925 information, so check them here */
7926 if (chunk_size_expected != chunk_size)
7928 Error(ERR_WARN, "wrong size (%d) of chunk '%s' in tape file '%s'",
7929 chunk_size, chunk_name, filename);
7937 tape.length_frames = GetTapeLengthFrames();
7938 tape.length_seconds = GetTapeLengthSeconds();
7941 printf("::: tape file version: %d\n", tape.file_version);
7942 printf("::: tape game version: %d\n", tape.game_version);
7943 printf("::: tape engine version: %d\n", tape.engine_version);
7947 void LoadTape(int nr)
7949 char *filename = getTapeFilename(nr);
7951 LoadTapeFromFilename(filename);
7954 void LoadSolutionTape(int nr)
7956 char *filename = getSolutionTapeFilename(nr);
7958 LoadTapeFromFilename(filename);
7960 if (TAPE_IS_EMPTY(tape) &&
7961 level.game_engine_type == GAME_ENGINE_TYPE_SP &&
7962 level.native_sp_level->demo.is_available)
7963 CopyNativeTape_SP_to_RND(&level);
7966 static void SaveTape_VERS(FILE *file, struct TapeInfo *tape)
7968 putFileVersion(file, tape->file_version);
7969 putFileVersion(file, tape->game_version);
7972 static void SaveTape_HEAD(FILE *file, struct TapeInfo *tape)
7975 byte store_participating_players = 0;
7977 /* set bits for participating players for compact storage */
7978 for (i = 0; i < MAX_PLAYERS; i++)
7979 if (tape->player_participates[i])
7980 store_participating_players |= (1 << i);
7982 putFile32BitBE(file, tape->random_seed);
7983 putFile32BitBE(file, tape->date);
7984 putFile32BitBE(file, tape->length);
7986 putFile8Bit(file, store_participating_players);
7988 putFile8Bit(file, (tape->use_mouse ? 1 : 0));
7990 /* unused bytes not at the end here for 4-byte alignment of engine_version */
7991 WriteUnusedBytesToFile(file, TAPE_CHUNK_HEAD_UNUSED);
7993 putFileVersion(file, tape->engine_version);
7996 static void SaveTape_INFO(FILE *file, struct TapeInfo *tape)
7998 int level_identifier_size = strlen(tape->level_identifier) + 1;
8001 putFile16BitBE(file, level_identifier_size);
8003 for (i = 0; i < level_identifier_size; i++)
8004 putFile8Bit(file, tape->level_identifier[i]);
8006 putFile16BitBE(file, tape->level_nr);
8009 static void SaveTape_BODY(FILE *file, struct TapeInfo *tape)
8013 for (i = 0; i < tape->length; i++)
8015 if (tape->use_mouse)
8017 putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_LX]);
8018 putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_LY]);
8019 putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_BUTTON]);
8023 for (j = 0; j < MAX_PLAYERS; j++)
8024 if (tape->player_participates[j])
8025 putFile8Bit(file, tape->pos[i].action[j]);
8028 putFile8Bit(file, tape->pos[i].delay);
8032 void SaveTape(int nr)
8034 char *filename = getTapeFilename(nr);
8036 int num_participating_players = 0;
8038 int info_chunk_size;
8039 int body_chunk_size;
8042 InitTapeDirectory(leveldir_current->subdir);
8044 if (!(file = fopen(filename, MODE_WRITE)))
8046 Error(ERR_WARN, "cannot save level recording file '%s'", filename);
8050 tape.file_version = FILE_VERSION_ACTUAL;
8051 tape.game_version = GAME_VERSION_ACTUAL;
8053 /* count number of participating players */
8054 for (i = 0; i < MAX_PLAYERS; i++)
8055 if (tape.player_participates[i])
8056 num_participating_players++;
8058 tape_pos_size = (tape.use_mouse ? 3 : num_participating_players) + 1;
8060 info_chunk_size = 2 + (strlen(tape.level_identifier) + 1) + 2;
8061 body_chunk_size = tape_pos_size * tape.length;
8063 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
8064 putFileChunkBE(file, "TAPE", CHUNK_SIZE_NONE);
8066 putFileChunkBE(file, "VERS", TAPE_CHUNK_VERS_SIZE);
8067 SaveTape_VERS(file, &tape);
8069 putFileChunkBE(file, "HEAD", TAPE_CHUNK_HEAD_SIZE);
8070 SaveTape_HEAD(file, &tape);
8072 putFileChunkBE(file, "INFO", info_chunk_size);
8073 SaveTape_INFO(file, &tape);
8075 putFileChunkBE(file, "BODY", body_chunk_size);
8076 SaveTape_BODY(file, &tape);
8080 SetFilePermissions(filename, PERMS_PRIVATE);
8082 tape.changed = FALSE;
8085 static boolean SaveTapeCheckedExt(int nr, char *msg_replace, char *msg_saved)
8087 char *filename = getTapeFilename(nr);
8088 boolean new_tape = !fileExists(filename);
8089 boolean tape_saved = FALSE;
8091 if (new_tape || Request(msg_replace, REQ_ASK))
8096 Request(msg_saved, REQ_CONFIRM);
8104 boolean SaveTapeChecked(int nr)
8106 return SaveTapeCheckedExt(nr, "Replace old tape?", "Tape saved!");
8109 boolean SaveTapeChecked_LevelSolved(int nr)
8111 return SaveTapeCheckedExt(nr, "Level solved! Replace old tape?",
8112 "Level solved! Tape saved!");
8115 void DumpTape(struct TapeInfo *tape)
8117 int tape_frame_counter;
8120 if (tape->no_valid_file)
8122 Error(ERR_WARN, "cannot dump -- no valid tape file found");
8128 Print("Tape of Level %03d (file version %08d, game version %08d)\n",
8129 tape->level_nr, tape->file_version, tape->game_version);
8130 Print(" (effective engine version %08d)\n",
8131 tape->engine_version);
8132 Print("Level series identifier: '%s'\n", tape->level_identifier);
8135 tape_frame_counter = 0;
8137 for (i = 0; i < tape->length; i++)
8139 if (i >= MAX_TAPE_LEN)
8144 for (j = 0; j < MAX_PLAYERS; j++)
8146 if (tape->player_participates[j])
8148 int action = tape->pos[i].action[j];
8150 Print("%d:%02x ", j, action);
8151 Print("[%c%c%c%c|%c%c] - ",
8152 (action & JOY_LEFT ? '<' : ' '),
8153 (action & JOY_RIGHT ? '>' : ' '),
8154 (action & JOY_UP ? '^' : ' '),
8155 (action & JOY_DOWN ? 'v' : ' '),
8156 (action & JOY_BUTTON_1 ? '1' : ' '),
8157 (action & JOY_BUTTON_2 ? '2' : ' '));
8161 Print("(%03d) ", tape->pos[i].delay);
8162 Print("[%05d]\n", tape_frame_counter);
8164 tape_frame_counter += tape->pos[i].delay;
8171 /* ========================================================================= */
8172 /* score file functions */
8173 /* ========================================================================= */
8175 void LoadScore(int nr)
8178 char *filename = getScoreFilename(nr);
8179 char cookie[MAX_LINE_LEN];
8180 char line[MAX_LINE_LEN];
8184 /* always start with reliable default values */
8185 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
8187 strcpy(highscore[i].Name, EMPTY_PLAYER_NAME);
8188 highscore[i].Score = 0;
8191 if (!(file = fopen(filename, MODE_READ)))
8194 /* check file identifier */
8195 if (fgets(cookie, MAX_LINE_LEN, file) == NULL)
8197 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
8198 cookie[strlen(cookie) - 1] = '\0';
8200 if (!checkCookieString(cookie, SCORE_COOKIE))
8202 Error(ERR_WARN, "unknown format of score file '%s'", filename);
8207 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
8209 if (fscanf(file, "%d", &highscore[i].Score) == EOF)
8210 Error(ERR_WARN, "fscanf() failed; %s", strerror(errno));
8211 if (fgets(line, MAX_LINE_LEN, file) == NULL)
8214 if (strlen(line) > 0 && line[strlen(line) - 1] == '\n')
8215 line[strlen(line) - 1] = '\0';
8217 for (line_ptr = line; *line_ptr; line_ptr++)
8219 if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != '\0')
8221 strncpy(highscore[i].Name, line_ptr, MAX_PLAYER_NAME_LEN);
8222 highscore[i].Name[MAX_PLAYER_NAME_LEN] = '\0';
8231 void SaveScore(int nr)
8234 int permissions = (program.global_scores ? PERMS_PUBLIC : PERMS_PRIVATE);
8235 char *filename = getScoreFilename(nr);
8238 InitScoreDirectory(leveldir_current->subdir);
8240 if (!(file = fopen(filename, MODE_WRITE)))
8242 Error(ERR_WARN, "cannot save score for level %d", nr);
8246 fprintf(file, "%s\n\n", SCORE_COOKIE);
8248 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
8249 fprintf(file, "%d %s\n", highscore[i].Score, highscore[i].Name);
8253 SetFilePermissions(filename, permissions);
8257 /* ========================================================================= */
8258 /* setup file functions */
8259 /* ========================================================================= */
8261 #define TOKEN_STR_PLAYER_PREFIX "player_"
8264 #define SETUP_TOKEN_PLAYER_NAME 0
8265 #define SETUP_TOKEN_SOUND 1
8266 #define SETUP_TOKEN_SOUND_LOOPS 2
8267 #define SETUP_TOKEN_SOUND_MUSIC 3
8268 #define SETUP_TOKEN_SOUND_SIMPLE 4
8269 #define SETUP_TOKEN_TOONS 5
8270 #define SETUP_TOKEN_SCROLL_DELAY 6
8271 #define SETUP_TOKEN_SCROLL_DELAY_VALUE 7
8272 #define SETUP_TOKEN_ENGINE_SNAPSHOT_MODE 8
8273 #define SETUP_TOKEN_ENGINE_SNAPSHOT_MEMORY 9
8274 #define SETUP_TOKEN_FADE_SCREENS 10
8275 #define SETUP_TOKEN_AUTORECORD 11
8276 #define SETUP_TOKEN_SHOW_TITLESCREEN 12
8277 #define SETUP_TOKEN_QUICK_DOORS 13
8278 #define SETUP_TOKEN_TEAM_MODE 14
8279 #define SETUP_TOKEN_HANDICAP 15
8280 #define SETUP_TOKEN_SKIP_LEVELS 16
8281 #define SETUP_TOKEN_INCREMENT_LEVELS 17
8282 #define SETUP_TOKEN_TIME_LIMIT 18
8283 #define SETUP_TOKEN_FULLSCREEN 19
8284 #define SETUP_TOKEN_WINDOW_SCALING_PERCENT 20
8285 #define SETUP_TOKEN_WINDOW_SCALING_QUALITY 21
8286 #define SETUP_TOKEN_SCREEN_RENDERING_MODE 22
8287 #define SETUP_TOKEN_ASK_ON_ESCAPE 23
8288 #define SETUP_TOKEN_ASK_ON_ESCAPE_EDITOR 24
8289 #define SETUP_TOKEN_QUICK_SWITCH 25
8290 #define SETUP_TOKEN_INPUT_ON_FOCUS 26
8291 #define SETUP_TOKEN_PREFER_AGA_GRAPHICS 27
8292 #define SETUP_TOKEN_GAME_FRAME_DELAY 28
8293 #define SETUP_TOKEN_SP_SHOW_BORDER_ELEMENTS 29
8294 #define SETUP_TOKEN_SMALL_GAME_GRAPHICS 30
8295 #define SETUP_TOKEN_SHOW_SNAPSHOT_BUTTONS 31
8296 #define SETUP_TOKEN_GRAPHICS_SET 32
8297 #define SETUP_TOKEN_SOUNDS_SET 33
8298 #define SETUP_TOKEN_MUSIC_SET 34
8299 #define SETUP_TOKEN_OVERRIDE_LEVEL_GRAPHICS 35
8300 #define SETUP_TOKEN_OVERRIDE_LEVEL_SOUNDS 36
8301 #define SETUP_TOKEN_OVERRIDE_LEVEL_MUSIC 37
8302 #define SETUP_TOKEN_VOLUME_SIMPLE 38
8303 #define SETUP_TOKEN_VOLUME_LOOPS 39
8304 #define SETUP_TOKEN_VOLUME_MUSIC 40
8305 #define SETUP_TOKEN_TOUCH_CONTROL_TYPE 41
8306 #define SETUP_TOKEN_TOUCH_MOVE_DISTANCE 42
8307 #define SETUP_TOKEN_TOUCH_DROP_DISTANCE 43
8308 #define SETUP_TOKEN_TOUCH_TRANSPARENCY 44
8309 #define SETUP_TOKEN_TOUCH_DRAW_OUTLINED 45
8310 #define SETUP_TOKEN_TOUCH_DRAW_PRESSED 46
8311 #define SETUP_TOKEN_TOUCH_GRID_XSIZE_0 47
8312 #define SETUP_TOKEN_TOUCH_GRID_YSIZE_0 48
8313 #define SETUP_TOKEN_TOUCH_GRID_XSIZE_1 49
8314 #define SETUP_TOKEN_TOUCH_GRID_YSIZE_1 50
8315 #define SETUP_TOKEN_NETWORK_MODE 51
8317 #define NUM_GLOBAL_SETUP_TOKENS 52
8320 #define SETUP_TOKEN_AUTO_EDITOR_ZOOM_TILESIZE 0
8322 #define NUM_AUTO_SETUP_TOKENS 1
8325 #define SETUP_TOKEN_EDITOR_EL_CLASSIC 0
8326 #define SETUP_TOKEN_EDITOR_EL_CUSTOM 1
8327 #define SETUP_TOKEN_EDITOR_EL_USER_DEFINED 2
8328 #define SETUP_TOKEN_EDITOR_EL_DYNAMIC 3
8329 #define SETUP_TOKEN_EDITOR_EL_HEADLINES 4
8330 #define SETUP_TOKEN_EDITOR_SHOW_ELEMENT_TOKEN 5
8332 #define NUM_EDITOR_SETUP_TOKENS 6
8334 /* editor cascade setup */
8335 #define SETUP_TOKEN_EDITOR_CASCADE_BD 0
8336 #define SETUP_TOKEN_EDITOR_CASCADE_EM 1
8337 #define SETUP_TOKEN_EDITOR_CASCADE_EMC 2
8338 #define SETUP_TOKEN_EDITOR_CASCADE_RND 3
8339 #define SETUP_TOKEN_EDITOR_CASCADE_SB 4
8340 #define SETUP_TOKEN_EDITOR_CASCADE_SP 5
8341 #define SETUP_TOKEN_EDITOR_CASCADE_DC 6
8342 #define SETUP_TOKEN_EDITOR_CASCADE_DX 7
8343 #define SETUP_TOKEN_EDITOR_CASCADE_TEXT 8
8344 #define SETUP_TOKEN_EDITOR_CASCADE_STEELTEXT 9
8345 #define SETUP_TOKEN_EDITOR_CASCADE_CE 10
8346 #define SETUP_TOKEN_EDITOR_CASCADE_GE 11
8347 #define SETUP_TOKEN_EDITOR_CASCADE_REF 12
8348 #define SETUP_TOKEN_EDITOR_CASCADE_USER 13
8349 #define SETUP_TOKEN_EDITOR_CASCADE_DYNAMIC 14
8351 #define NUM_EDITOR_CASCADE_SETUP_TOKENS 15
8353 /* shortcut setup */
8354 #define SETUP_TOKEN_SHORTCUT_SAVE_GAME 0
8355 #define SETUP_TOKEN_SHORTCUT_LOAD_GAME 1
8356 #define SETUP_TOKEN_SHORTCUT_TOGGLE_PAUSE 2
8357 #define SETUP_TOKEN_SHORTCUT_FOCUS_PLAYER_1 3
8358 #define SETUP_TOKEN_SHORTCUT_FOCUS_PLAYER_2 4
8359 #define SETUP_TOKEN_SHORTCUT_FOCUS_PLAYER_3 5
8360 #define SETUP_TOKEN_SHORTCUT_FOCUS_PLAYER_4 6
8361 #define SETUP_TOKEN_SHORTCUT_FOCUS_PLAYER_ALL 7
8362 #define SETUP_TOKEN_SHORTCUT_TAPE_EJECT 8
8363 #define SETUP_TOKEN_SHORTCUT_TAPE_EXTRA 9
8364 #define SETUP_TOKEN_SHORTCUT_TAPE_STOP 10
8365 #define SETUP_TOKEN_SHORTCUT_TAPE_PAUSE 11
8366 #define SETUP_TOKEN_SHORTCUT_TAPE_RECORD 12
8367 #define SETUP_TOKEN_SHORTCUT_TAPE_PLAY 13
8368 #define SETUP_TOKEN_SHORTCUT_SOUND_SIMPLE 14
8369 #define SETUP_TOKEN_SHORTCUT_SOUND_LOOPS 15
8370 #define SETUP_TOKEN_SHORTCUT_SOUND_MUSIC 16
8371 #define SETUP_TOKEN_SHORTCUT_SNAP_LEFT 17
8372 #define SETUP_TOKEN_SHORTCUT_SNAP_RIGHT 18
8373 #define SETUP_TOKEN_SHORTCUT_SNAP_UP 19
8374 #define SETUP_TOKEN_SHORTCUT_SNAP_DOWN 20
8376 #define NUM_SHORTCUT_SETUP_TOKENS 21
8379 #define SETUP_TOKEN_PLAYER_USE_JOYSTICK 0
8380 #define SETUP_TOKEN_PLAYER_JOY_DEVICE_NAME 1
8381 #define SETUP_TOKEN_PLAYER_JOY_XLEFT 2
8382 #define SETUP_TOKEN_PLAYER_JOY_XMIDDLE 3
8383 #define SETUP_TOKEN_PLAYER_JOY_XRIGHT 4
8384 #define SETUP_TOKEN_PLAYER_JOY_YUPPER 5
8385 #define SETUP_TOKEN_PLAYER_JOY_YMIDDLE 6
8386 #define SETUP_TOKEN_PLAYER_JOY_YLOWER 7
8387 #define SETUP_TOKEN_PLAYER_JOY_SNAP 8
8388 #define SETUP_TOKEN_PLAYER_JOY_DROP 9
8389 #define SETUP_TOKEN_PLAYER_KEY_LEFT 10
8390 #define SETUP_TOKEN_PLAYER_KEY_RIGHT 11
8391 #define SETUP_TOKEN_PLAYER_KEY_UP 12
8392 #define SETUP_TOKEN_PLAYER_KEY_DOWN 13
8393 #define SETUP_TOKEN_PLAYER_KEY_SNAP 14
8394 #define SETUP_TOKEN_PLAYER_KEY_DROP 15
8396 #define NUM_PLAYER_SETUP_TOKENS 16
8399 #define SETUP_TOKEN_SYSTEM_SDL_VIDEODRIVER 0
8400 #define SETUP_TOKEN_SYSTEM_SDL_AUDIODRIVER 1
8401 #define SETUP_TOKEN_SYSTEM_AUDIO_FRAGMENT_SIZE 2
8403 #define NUM_SYSTEM_SETUP_TOKENS 3
8405 /* internal setup */
8406 #define SETUP_TOKEN_INT_PROGRAM_TITLE 0
8407 #define SETUP_TOKEN_INT_PROGRAM_VERSION 1
8408 #define SETUP_TOKEN_INT_PROGRAM_AUTHOR 2
8409 #define SETUP_TOKEN_INT_PROGRAM_EMAIL 3
8410 #define SETUP_TOKEN_INT_PROGRAM_WEBSITE 4
8411 #define SETUP_TOKEN_INT_PROGRAM_COPYRIGHT 5
8412 #define SETUP_TOKEN_INT_PROGRAM_COMPANY 6
8413 #define SETUP_TOKEN_INT_PROGRAM_ICON_FILE 7
8414 #define SETUP_TOKEN_INT_DEFAULT_GRAPHICS_SET 8
8415 #define SETUP_TOKEN_INT_DEFAULT_SOUNDS_SET 9
8416 #define SETUP_TOKEN_INT_DEFAULT_MUSIC_SET 10
8417 #define SETUP_TOKEN_INT_FALLBACK_GRAPHICS_FILE 11
8418 #define SETUP_TOKEN_INT_FALLBACK_SOUNDS_FILE 12
8419 #define SETUP_TOKEN_INT_FALLBACK_MUSIC_FILE 13
8420 #define SETUP_TOKEN_INT_DEFAULT_LEVEL_SERIES 14
8421 #define SETUP_TOKEN_INT_CHOOSE_FROM_TOP_LEVELDIR 15
8422 #define SETUP_TOKEN_INT_SHOW_SCALING_IN_TITLE 16
8423 #define SETUP_TOKEN_INT_DEFAULT_WINDOW_WIDTH 17
8424 #define SETUP_TOKEN_INT_DEFAULT_WINDOW_HEIGHT 18
8426 #define NUM_INTERNAL_SETUP_TOKENS 19
8429 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_0 0
8430 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_1 1
8431 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_2 2
8432 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_3 3
8433 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_4 4
8434 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_5 5
8435 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_6 6
8436 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_7 7
8437 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_8 8
8438 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_9 9
8439 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_KEY_0 10
8440 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_KEY_1 11
8441 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_KEY_2 12
8442 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_KEY_3 13
8443 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_KEY_4 14
8444 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_KEY_5 15
8445 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_KEY_6 16
8446 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_KEY_7 17
8447 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_KEY_8 18
8448 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_KEY_9 19
8449 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_USE_MOD_KEY 20
8450 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_GAME_ONLY 21
8451 #define SETUP_TOKEN_DEBUG_SHOW_FRAMES_PER_SECOND 22
8453 #define NUM_DEBUG_SETUP_TOKENS 23
8456 #define SETUP_TOKEN_OPTIONS_VERBOSE 0
8458 #define NUM_OPTIONS_SETUP_TOKENS 1
8461 static struct SetupInfo si;
8462 static struct SetupAutoSetupInfo sasi;
8463 static struct SetupEditorInfo sei;
8464 static struct SetupEditorCascadeInfo seci;
8465 static struct SetupShortcutInfo ssi;
8466 static struct SetupInputInfo sii;
8467 static struct SetupSystemInfo syi;
8468 static struct SetupInternalInfo sxi;
8469 static struct SetupDebugInfo sdi;
8470 static struct OptionInfo soi;
8472 static struct TokenInfo global_setup_tokens[] =
8474 { TYPE_STRING, &si.player_name, "player_name" },
8475 { TYPE_SWITCH, &si.sound, "sound" },
8476 { TYPE_SWITCH, &si.sound_loops, "repeating_sound_loops" },
8477 { TYPE_SWITCH, &si.sound_music, "background_music" },
8478 { TYPE_SWITCH, &si.sound_simple, "simple_sound_effects" },
8479 { TYPE_SWITCH, &si.toons, "toons" },
8480 { TYPE_SWITCH, &si.scroll_delay, "scroll_delay" },
8481 { TYPE_INTEGER,&si.scroll_delay_value, "scroll_delay_value" },
8482 { TYPE_STRING, &si.engine_snapshot_mode, "engine_snapshot_mode" },
8483 { TYPE_INTEGER,&si.engine_snapshot_memory, "engine_snapshot_memory" },
8484 { TYPE_SWITCH, &si.fade_screens, "fade_screens" },
8485 { TYPE_SWITCH, &si.autorecord, "automatic_tape_recording"},
8486 { TYPE_SWITCH, &si.show_titlescreen, "show_titlescreen" },
8487 { TYPE_SWITCH, &si.quick_doors, "quick_doors" },
8488 { TYPE_SWITCH, &si.team_mode, "team_mode" },
8489 { TYPE_SWITCH, &si.handicap, "handicap" },
8490 { TYPE_SWITCH, &si.skip_levels, "skip_levels" },
8491 { TYPE_SWITCH, &si.increment_levels, "increment_levels" },
8492 { TYPE_SWITCH, &si.time_limit, "time_limit" },
8493 { TYPE_SWITCH, &si.fullscreen, "fullscreen" },
8494 { TYPE_INTEGER,&si.window_scaling_percent, "window_scaling_percent" },
8495 { TYPE_STRING, &si.window_scaling_quality, "window_scaling_quality" },
8496 { TYPE_STRING, &si.screen_rendering_mode, "screen_rendering_mode" },
8497 { TYPE_SWITCH, &si.ask_on_escape, "ask_on_escape" },
8498 { TYPE_SWITCH, &si.ask_on_escape_editor, "ask_on_escape_editor" },
8499 { TYPE_SWITCH, &si.quick_switch, "quick_player_switch" },
8500 { TYPE_SWITCH, &si.input_on_focus, "input_on_focus" },
8501 { TYPE_SWITCH, &si.prefer_aga_graphics, "prefer_aga_graphics" },
8502 { TYPE_INTEGER,&si.game_frame_delay, "game_frame_delay" },
8503 { TYPE_SWITCH, &si.sp_show_border_elements, "sp_show_border_elements" },
8504 { TYPE_SWITCH, &si.small_game_graphics, "small_game_graphics" },
8505 { TYPE_SWITCH, &si.show_snapshot_buttons, "show_snapshot_buttons" },
8506 { TYPE_STRING, &si.graphics_set, "graphics_set" },
8507 { TYPE_STRING, &si.sounds_set, "sounds_set" },
8508 { TYPE_STRING, &si.music_set, "music_set" },
8509 { TYPE_SWITCH3,&si.override_level_graphics, "override_level_graphics" },
8510 { TYPE_SWITCH3,&si.override_level_sounds, "override_level_sounds" },
8511 { TYPE_SWITCH3,&si.override_level_music, "override_level_music" },
8512 { TYPE_INTEGER,&si.volume_simple, "volume_simple" },
8513 { TYPE_INTEGER,&si.volume_loops, "volume_loops" },
8514 { TYPE_INTEGER,&si.volume_music, "volume_music" },
8515 { TYPE_STRING, &si.touch.control_type, "touch.control_type" },
8516 { TYPE_INTEGER,&si.touch.move_distance, "touch.move_distance" },
8517 { TYPE_INTEGER,&si.touch.drop_distance, "touch.drop_distance" },
8518 { TYPE_INTEGER,&si.touch.transparency, "touch.transparency" },
8519 { TYPE_INTEGER,&si.touch.draw_outlined, "touch.draw_outlined" },
8520 { TYPE_INTEGER,&si.touch.draw_pressed, "touch.draw_pressed" },
8521 { TYPE_INTEGER,&si.touch.grid_xsize[0], "touch.virtual_buttons.0.xsize" },
8522 { TYPE_INTEGER,&si.touch.grid_ysize[0], "touch.virtual_buttons.0.ysize" },
8523 { TYPE_INTEGER,&si.touch.grid_xsize[1], "touch.virtual_buttons.1.xsize" },
8524 { TYPE_INTEGER,&si.touch.grid_ysize[1], "touch.virtual_buttons.1.ysize" },
8525 { TYPE_SWITCH, &si.network_mode, "network_mode" },
8528 static struct TokenInfo auto_setup_tokens[] =
8530 { TYPE_INTEGER,&sasi.editor_zoom_tilesize, "editor.zoom_tilesize" },
8533 static struct TokenInfo editor_setup_tokens[] =
8535 { TYPE_SWITCH, &sei.el_classic, "editor.el_classic" },
8536 { TYPE_SWITCH, &sei.el_custom, "editor.el_custom" },
8537 { TYPE_SWITCH, &sei.el_user_defined, "editor.el_user_defined" },
8538 { TYPE_SWITCH, &sei.el_dynamic, "editor.el_dynamic" },
8539 { TYPE_SWITCH, &sei.el_headlines, "editor.el_headlines" },
8540 { TYPE_SWITCH, &sei.show_element_token,"editor.show_element_token" },
8543 static struct TokenInfo editor_cascade_setup_tokens[] =
8545 { TYPE_SWITCH, &seci.el_bd, "editor.cascade.el_bd" },
8546 { TYPE_SWITCH, &seci.el_em, "editor.cascade.el_em" },
8547 { TYPE_SWITCH, &seci.el_emc, "editor.cascade.el_emc" },
8548 { TYPE_SWITCH, &seci.el_rnd, "editor.cascade.el_rnd" },
8549 { TYPE_SWITCH, &seci.el_sb, "editor.cascade.el_sb" },
8550 { TYPE_SWITCH, &seci.el_sp, "editor.cascade.el_sp" },
8551 { TYPE_SWITCH, &seci.el_dc, "editor.cascade.el_dc" },
8552 { TYPE_SWITCH, &seci.el_dx, "editor.cascade.el_dx" },
8553 { TYPE_SWITCH, &seci.el_mm, "editor.cascade.el_mm" },
8554 { TYPE_SWITCH, &seci.el_df, "editor.cascade.el_df" },
8555 { TYPE_SWITCH, &seci.el_chars, "editor.cascade.el_chars" },
8556 { TYPE_SWITCH, &seci.el_steel_chars, "editor.cascade.el_steel_chars" },
8557 { TYPE_SWITCH, &seci.el_ce, "editor.cascade.el_ce" },
8558 { TYPE_SWITCH, &seci.el_ge, "editor.cascade.el_ge" },
8559 { TYPE_SWITCH, &seci.el_ref, "editor.cascade.el_ref" },
8560 { TYPE_SWITCH, &seci.el_user, "editor.cascade.el_user" },
8561 { TYPE_SWITCH, &seci.el_dynamic, "editor.cascade.el_dynamic" },
8564 static struct TokenInfo shortcut_setup_tokens[] =
8566 { TYPE_KEY_X11, &ssi.save_game, "shortcut.save_game" },
8567 { TYPE_KEY_X11, &ssi.load_game, "shortcut.load_game" },
8568 { TYPE_KEY_X11, &ssi.toggle_pause, "shortcut.toggle_pause" },
8569 { TYPE_KEY_X11, &ssi.focus_player[0], "shortcut.focus_player_1" },
8570 { TYPE_KEY_X11, &ssi.focus_player[1], "shortcut.focus_player_2" },
8571 { TYPE_KEY_X11, &ssi.focus_player[2], "shortcut.focus_player_3" },
8572 { TYPE_KEY_X11, &ssi.focus_player[3], "shortcut.focus_player_4" },
8573 { TYPE_KEY_X11, &ssi.focus_player_all,"shortcut.focus_player_all" },
8574 { TYPE_KEY_X11, &ssi.tape_eject, "shortcut.tape_eject" },
8575 { TYPE_KEY_X11, &ssi.tape_extra, "shortcut.tape_extra" },
8576 { TYPE_KEY_X11, &ssi.tape_stop, "shortcut.tape_stop" },
8577 { TYPE_KEY_X11, &ssi.tape_pause, "shortcut.tape_pause" },
8578 { TYPE_KEY_X11, &ssi.tape_record, "shortcut.tape_record" },
8579 { TYPE_KEY_X11, &ssi.tape_play, "shortcut.tape_play" },
8580 { TYPE_KEY_X11, &ssi.sound_simple, "shortcut.sound_simple" },
8581 { TYPE_KEY_X11, &ssi.sound_loops, "shortcut.sound_loops" },
8582 { TYPE_KEY_X11, &ssi.sound_music, "shortcut.sound_music" },
8583 { TYPE_KEY_X11, &ssi.snap_left, "shortcut.snap_left" },
8584 { TYPE_KEY_X11, &ssi.snap_right, "shortcut.snap_right" },
8585 { TYPE_KEY_X11, &ssi.snap_up, "shortcut.snap_up" },
8586 { TYPE_KEY_X11, &ssi.snap_down, "shortcut.snap_down" },
8589 static struct TokenInfo player_setup_tokens[] =
8591 { TYPE_BOOLEAN, &sii.use_joystick, ".use_joystick" },
8592 { TYPE_STRING, &sii.joy.device_name, ".joy.device_name" },
8593 { TYPE_INTEGER, &sii.joy.xleft, ".joy.xleft" },
8594 { TYPE_INTEGER, &sii.joy.xmiddle, ".joy.xmiddle" },
8595 { TYPE_INTEGER, &sii.joy.xright, ".joy.xright" },
8596 { TYPE_INTEGER, &sii.joy.yupper, ".joy.yupper" },
8597 { TYPE_INTEGER, &sii.joy.ymiddle, ".joy.ymiddle" },
8598 { TYPE_INTEGER, &sii.joy.ylower, ".joy.ylower" },
8599 { TYPE_INTEGER, &sii.joy.snap, ".joy.snap_field" },
8600 { TYPE_INTEGER, &sii.joy.drop, ".joy.place_bomb" },
8601 { TYPE_KEY_X11, &sii.key.left, ".key.move_left" },
8602 { TYPE_KEY_X11, &sii.key.right, ".key.move_right" },
8603 { TYPE_KEY_X11, &sii.key.up, ".key.move_up" },
8604 { TYPE_KEY_X11, &sii.key.down, ".key.move_down" },
8605 { TYPE_KEY_X11, &sii.key.snap, ".key.snap_field" },
8606 { TYPE_KEY_X11, &sii.key.drop, ".key.place_bomb" },
8609 static struct TokenInfo system_setup_tokens[] =
8611 { TYPE_STRING, &syi.sdl_videodriver, "system.sdl_videodriver" },
8612 { TYPE_STRING, &syi.sdl_audiodriver, "system.sdl_audiodriver" },
8613 { TYPE_INTEGER, &syi.audio_fragment_size,"system.audio_fragment_size" },
8616 static struct TokenInfo internal_setup_tokens[] =
8618 { TYPE_STRING, &sxi.program_title, "program_title" },
8619 { TYPE_STRING, &sxi.program_version, "program_version" },
8620 { TYPE_STRING, &sxi.program_author, "program_author" },
8621 { TYPE_STRING, &sxi.program_email, "program_email" },
8622 { TYPE_STRING, &sxi.program_website, "program_website" },
8623 { TYPE_STRING, &sxi.program_copyright, "program_copyright" },
8624 { TYPE_STRING, &sxi.program_company, "program_company" },
8625 { TYPE_STRING, &sxi.program_icon_file, "program_icon_file" },
8626 { TYPE_STRING, &sxi.default_graphics_set, "default_graphics_set" },
8627 { TYPE_STRING, &sxi.default_sounds_set, "default_sounds_set" },
8628 { TYPE_STRING, &sxi.default_music_set, "default_music_set" },
8629 { TYPE_STRING, &sxi.fallback_graphics_file, "fallback_graphics_file"},
8630 { TYPE_STRING, &sxi.fallback_sounds_file, "fallback_sounds_file" },
8631 { TYPE_STRING, &sxi.fallback_music_file, "fallback_music_file" },
8632 { TYPE_STRING, &sxi.default_level_series, "default_level_series" },
8633 { TYPE_BOOLEAN,&sxi.choose_from_top_leveldir, "choose_from_top_leveldir" },
8634 { TYPE_BOOLEAN,&sxi.show_scaling_in_title, "show_scaling_in_title" },
8635 { TYPE_INTEGER,&sxi.default_window_width, "default_window_width" },
8636 { TYPE_INTEGER,&sxi.default_window_height, "default_window_height" },
8639 static struct TokenInfo debug_setup_tokens[] =
8641 { TYPE_INTEGER, &sdi.frame_delay[0], "debug.frame_delay_0" },
8642 { TYPE_INTEGER, &sdi.frame_delay[1], "debug.frame_delay_1" },
8643 { TYPE_INTEGER, &sdi.frame_delay[2], "debug.frame_delay_2" },
8644 { TYPE_INTEGER, &sdi.frame_delay[3], "debug.frame_delay_3" },
8645 { TYPE_INTEGER, &sdi.frame_delay[4], "debug.frame_delay_4" },
8646 { TYPE_INTEGER, &sdi.frame_delay[5], "debug.frame_delay_5" },
8647 { TYPE_INTEGER, &sdi.frame_delay[6], "debug.frame_delay_6" },
8648 { TYPE_INTEGER, &sdi.frame_delay[7], "debug.frame_delay_7" },
8649 { TYPE_INTEGER, &sdi.frame_delay[8], "debug.frame_delay_8" },
8650 { TYPE_INTEGER, &sdi.frame_delay[9], "debug.frame_delay_9" },
8651 { TYPE_KEY_X11, &sdi.frame_delay_key[0], "debug.key.frame_delay_0" },
8652 { TYPE_KEY_X11, &sdi.frame_delay_key[1], "debug.key.frame_delay_1" },
8653 { TYPE_KEY_X11, &sdi.frame_delay_key[2], "debug.key.frame_delay_2" },
8654 { TYPE_KEY_X11, &sdi.frame_delay_key[3], "debug.key.frame_delay_3" },
8655 { TYPE_KEY_X11, &sdi.frame_delay_key[4], "debug.key.frame_delay_4" },
8656 { TYPE_KEY_X11, &sdi.frame_delay_key[5], "debug.key.frame_delay_5" },
8657 { TYPE_KEY_X11, &sdi.frame_delay_key[6], "debug.key.frame_delay_6" },
8658 { TYPE_KEY_X11, &sdi.frame_delay_key[7], "debug.key.frame_delay_7" },
8659 { TYPE_KEY_X11, &sdi.frame_delay_key[8], "debug.key.frame_delay_8" },
8660 { TYPE_KEY_X11, &sdi.frame_delay_key[9], "debug.key.frame_delay_9" },
8661 { TYPE_BOOLEAN, &sdi.frame_delay_use_mod_key,"debug.frame_delay.use_mod_key"},
8662 { TYPE_BOOLEAN, &sdi.frame_delay_game_only, "debug.frame_delay.game_only" },
8663 { TYPE_BOOLEAN, &sdi.show_frames_per_second, "debug.show_frames_per_second" },
8666 static struct TokenInfo options_setup_tokens[] =
8668 { TYPE_BOOLEAN, &soi.verbose, "options.verbose" },
8671 static char *get_corrected_login_name(char *login_name)
8673 /* needed because player name must be a fixed length string */
8674 char *login_name_new = checked_malloc(MAX_PLAYER_NAME_LEN + 1);
8676 strncpy(login_name_new, login_name, MAX_PLAYER_NAME_LEN);
8677 login_name_new[MAX_PLAYER_NAME_LEN] = '\0';
8679 if (strlen(login_name) > MAX_PLAYER_NAME_LEN) /* name has been cut */
8680 if (strchr(login_name_new, ' '))
8681 *strchr(login_name_new, ' ') = '\0';
8683 return login_name_new;
8686 static void setSetupInfoToDefaults(struct SetupInfo *si)
8690 si->player_name = get_corrected_login_name(getLoginName());
8693 si->sound_loops = TRUE;
8694 si->sound_music = TRUE;
8695 si->sound_simple = TRUE;
8697 si->scroll_delay = TRUE;
8698 si->scroll_delay_value = STD_SCROLL_DELAY;
8699 si->engine_snapshot_mode = getStringCopy(STR_SNAPSHOT_MODE_DEFAULT);
8700 si->engine_snapshot_memory = SNAPSHOT_MEMORY_DEFAULT;
8701 si->fade_screens = TRUE;
8702 si->autorecord = TRUE;
8703 si->show_titlescreen = TRUE;
8704 si->quick_doors = FALSE;
8705 si->team_mode = FALSE;
8706 si->handicap = TRUE;
8707 si->skip_levels = TRUE;
8708 si->increment_levels = TRUE;
8709 si->time_limit = TRUE;
8710 si->fullscreen = FALSE;
8711 si->window_scaling_percent = STD_WINDOW_SCALING_PERCENT;
8712 si->window_scaling_quality = getStringCopy(SCALING_QUALITY_DEFAULT);
8713 si->screen_rendering_mode = getStringCopy(STR_SPECIAL_RENDERING_DEFAULT);
8714 si->ask_on_escape = TRUE;
8715 si->ask_on_escape_editor = TRUE;
8716 si->quick_switch = FALSE;
8717 si->input_on_focus = FALSE;
8718 si->prefer_aga_graphics = TRUE;
8719 si->game_frame_delay = GAME_FRAME_DELAY;
8720 si->sp_show_border_elements = FALSE;
8721 si->small_game_graphics = FALSE;
8722 si->show_snapshot_buttons = FALSE;
8724 si->graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
8725 si->sounds_set = getStringCopy(SND_CLASSIC_SUBDIR);
8726 si->music_set = getStringCopy(MUS_CLASSIC_SUBDIR);
8728 si->override_level_graphics = FALSE;
8729 si->override_level_sounds = FALSE;
8730 si->override_level_music = FALSE;
8732 si->volume_simple = 100; /* percent */
8733 si->volume_loops = 100; /* percent */
8734 si->volume_music = 100; /* percent */
8736 si->touch.control_type = getStringCopy(TOUCH_CONTROL_DEFAULT);
8737 si->touch.move_distance = TOUCH_MOVE_DISTANCE_DEFAULT; /* percent */
8738 si->touch.drop_distance = TOUCH_DROP_DISTANCE_DEFAULT; /* percent */
8739 si->touch.transparency = TOUCH_TRANSPARENCY_DEFAULT; /* percent */
8740 si->touch.draw_outlined = TRUE;
8741 si->touch.draw_pressed = TRUE;
8743 for (i = 0; i < 2; i++)
8745 char *default_grid_button[6][2] =
8751 { "111222", " vv " },
8752 { "111222", " vv " }
8754 int grid_xsize = DEFAULT_GRID_XSIZE(i);
8755 int grid_ysize = DEFAULT_GRID_YSIZE(i);
8756 int min_xsize = MIN(6, grid_xsize);
8757 int min_ysize = MIN(6, grid_ysize);
8758 int startx = grid_xsize - min_xsize;
8759 int starty = grid_ysize - min_ysize;
8762 // virtual buttons grid can only be set to defaults if video is initialized
8763 // (this will be repeated if virtual buttons are not loaded from setup file)
8764 if (video.initialized)
8766 si->touch.grid_xsize[i] = grid_xsize;
8767 si->touch.grid_ysize[i] = grid_ysize;
8771 si->touch.grid_xsize[i] = -1;
8772 si->touch.grid_ysize[i] = -1;
8775 for (x = 0; x < MAX_GRID_XSIZE; x++)
8776 for (y = 0; y < MAX_GRID_YSIZE; y++)
8777 si->touch.grid_button[i][x][y] = CHAR_GRID_BUTTON_NONE;
8779 for (x = 0; x < min_xsize; x++)
8780 for (y = 0; y < min_ysize; y++)
8781 si->touch.grid_button[i][x][starty + y] =
8782 default_grid_button[y][0][x];
8784 for (x = 0; x < min_xsize; x++)
8785 for (y = 0; y < min_ysize; y++)
8786 si->touch.grid_button[i][startx + x][starty + y] =
8787 default_grid_button[y][1][x];
8790 si->touch.grid_initialized = video.initialized;
8792 si->network_mode = FALSE;
8794 si->editor.el_boulderdash = TRUE;
8795 si->editor.el_emerald_mine = TRUE;
8796 si->editor.el_emerald_mine_club = TRUE;
8797 si->editor.el_more = TRUE;
8798 si->editor.el_sokoban = TRUE;
8799 si->editor.el_supaplex = TRUE;
8800 si->editor.el_diamond_caves = TRUE;
8801 si->editor.el_dx_boulderdash = TRUE;
8803 si->editor.el_mirror_magic = TRUE;
8804 si->editor.el_deflektor = TRUE;
8806 si->editor.el_chars = TRUE;
8807 si->editor.el_steel_chars = TRUE;
8809 si->editor.el_classic = TRUE;
8810 si->editor.el_custom = TRUE;
8812 si->editor.el_user_defined = FALSE;
8813 si->editor.el_dynamic = TRUE;
8815 si->editor.el_headlines = TRUE;
8817 si->editor.show_element_token = FALSE;
8819 si->editor.use_template_for_new_levels = TRUE;
8821 si->shortcut.save_game = DEFAULT_KEY_SAVE_GAME;
8822 si->shortcut.load_game = DEFAULT_KEY_LOAD_GAME;
8823 si->shortcut.toggle_pause = DEFAULT_KEY_TOGGLE_PAUSE;
8825 si->shortcut.focus_player[0] = DEFAULT_KEY_FOCUS_PLAYER_1;
8826 si->shortcut.focus_player[1] = DEFAULT_KEY_FOCUS_PLAYER_2;
8827 si->shortcut.focus_player[2] = DEFAULT_KEY_FOCUS_PLAYER_3;
8828 si->shortcut.focus_player[3] = DEFAULT_KEY_FOCUS_PLAYER_4;
8829 si->shortcut.focus_player_all = DEFAULT_KEY_FOCUS_PLAYER_ALL;
8831 si->shortcut.tape_eject = DEFAULT_KEY_TAPE_EJECT;
8832 si->shortcut.tape_extra = DEFAULT_KEY_TAPE_EXTRA;
8833 si->shortcut.tape_stop = DEFAULT_KEY_TAPE_STOP;
8834 si->shortcut.tape_pause = DEFAULT_KEY_TAPE_PAUSE;
8835 si->shortcut.tape_record = DEFAULT_KEY_TAPE_RECORD;
8836 si->shortcut.tape_play = DEFAULT_KEY_TAPE_PLAY;
8838 si->shortcut.sound_simple = DEFAULT_KEY_SOUND_SIMPLE;
8839 si->shortcut.sound_loops = DEFAULT_KEY_SOUND_LOOPS;
8840 si->shortcut.sound_music = DEFAULT_KEY_SOUND_MUSIC;
8842 si->shortcut.snap_left = DEFAULT_KEY_SNAP_LEFT;
8843 si->shortcut.snap_right = DEFAULT_KEY_SNAP_RIGHT;
8844 si->shortcut.snap_up = DEFAULT_KEY_SNAP_UP;
8845 si->shortcut.snap_down = DEFAULT_KEY_SNAP_DOWN;
8847 for (i = 0; i < MAX_PLAYERS; i++)
8849 si->input[i].use_joystick = FALSE;
8850 si->input[i].joy.device_name=getStringCopy(getDeviceNameFromJoystickNr(i));
8851 si->input[i].joy.xleft = JOYSTICK_XLEFT;
8852 si->input[i].joy.xmiddle = JOYSTICK_XMIDDLE;
8853 si->input[i].joy.xright = JOYSTICK_XRIGHT;
8854 si->input[i].joy.yupper = JOYSTICK_YUPPER;
8855 si->input[i].joy.ymiddle = JOYSTICK_YMIDDLE;
8856 si->input[i].joy.ylower = JOYSTICK_YLOWER;
8857 si->input[i].joy.snap = (i == 0 ? JOY_BUTTON_1 : 0);
8858 si->input[i].joy.drop = (i == 0 ? JOY_BUTTON_2 : 0);
8859 si->input[i].key.left = (i == 0 ? DEFAULT_KEY_LEFT : KSYM_UNDEFINED);
8860 si->input[i].key.right = (i == 0 ? DEFAULT_KEY_RIGHT : KSYM_UNDEFINED);
8861 si->input[i].key.up = (i == 0 ? DEFAULT_KEY_UP : KSYM_UNDEFINED);
8862 si->input[i].key.down = (i == 0 ? DEFAULT_KEY_DOWN : KSYM_UNDEFINED);
8863 si->input[i].key.snap = (i == 0 ? DEFAULT_KEY_SNAP : KSYM_UNDEFINED);
8864 si->input[i].key.drop = (i == 0 ? DEFAULT_KEY_DROP : KSYM_UNDEFINED);
8867 si->system.sdl_videodriver = getStringCopy(ARG_DEFAULT);
8868 si->system.sdl_audiodriver = getStringCopy(ARG_DEFAULT);
8869 si->system.audio_fragment_size = DEFAULT_AUDIO_FRAGMENT_SIZE;
8871 si->internal.program_title = getStringCopy(PROGRAM_TITLE_STRING);
8872 si->internal.program_version = getStringCopy(getProgramRealVersionString());
8873 si->internal.program_author = getStringCopy(PROGRAM_AUTHOR_STRING);
8874 si->internal.program_email = getStringCopy(PROGRAM_EMAIL_STRING);
8875 si->internal.program_website = getStringCopy(PROGRAM_WEBSITE_STRING);
8876 si->internal.program_copyright = getStringCopy(PROGRAM_COPYRIGHT_STRING);
8877 si->internal.program_company = getStringCopy(PROGRAM_COMPANY_STRING);
8879 si->internal.program_icon_file = getStringCopy(PROGRAM_ICON_FILENAME);
8881 si->internal.default_graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
8882 si->internal.default_sounds_set = getStringCopy(SND_CLASSIC_SUBDIR);
8883 si->internal.default_music_set = getStringCopy(MUS_CLASSIC_SUBDIR);
8885 si->internal.fallback_graphics_file = getStringCopy(UNDEFINED_FILENAME);
8886 si->internal.fallback_sounds_file = getStringCopy(UNDEFINED_FILENAME);
8887 si->internal.fallback_music_file = getStringCopy(UNDEFINED_FILENAME);
8889 si->internal.default_level_series = getStringCopy(UNDEFINED_LEVELSET);
8890 si->internal.choose_from_top_leveldir = FALSE;
8891 si->internal.show_scaling_in_title = TRUE;
8893 si->internal.default_window_width = WIN_XSIZE_DEFAULT;
8894 si->internal.default_window_height = WIN_YSIZE_DEFAULT;
8896 si->debug.frame_delay[0] = DEFAULT_FRAME_DELAY_0;
8897 si->debug.frame_delay[1] = DEFAULT_FRAME_DELAY_1;
8898 si->debug.frame_delay[2] = DEFAULT_FRAME_DELAY_2;
8899 si->debug.frame_delay[3] = DEFAULT_FRAME_DELAY_3;
8900 si->debug.frame_delay[4] = DEFAULT_FRAME_DELAY_4;
8901 si->debug.frame_delay[5] = DEFAULT_FRAME_DELAY_5;
8902 si->debug.frame_delay[6] = DEFAULT_FRAME_DELAY_6;
8903 si->debug.frame_delay[7] = DEFAULT_FRAME_DELAY_7;
8904 si->debug.frame_delay[8] = DEFAULT_FRAME_DELAY_8;
8905 si->debug.frame_delay[9] = DEFAULT_FRAME_DELAY_9;
8907 si->debug.frame_delay_key[0] = DEFAULT_KEY_FRAME_DELAY_0;
8908 si->debug.frame_delay_key[1] = DEFAULT_KEY_FRAME_DELAY_1;
8909 si->debug.frame_delay_key[2] = DEFAULT_KEY_FRAME_DELAY_2;
8910 si->debug.frame_delay_key[3] = DEFAULT_KEY_FRAME_DELAY_3;
8911 si->debug.frame_delay_key[4] = DEFAULT_KEY_FRAME_DELAY_4;
8912 si->debug.frame_delay_key[5] = DEFAULT_KEY_FRAME_DELAY_5;
8913 si->debug.frame_delay_key[6] = DEFAULT_KEY_FRAME_DELAY_6;
8914 si->debug.frame_delay_key[7] = DEFAULT_KEY_FRAME_DELAY_7;
8915 si->debug.frame_delay_key[8] = DEFAULT_KEY_FRAME_DELAY_8;
8916 si->debug.frame_delay_key[9] = DEFAULT_KEY_FRAME_DELAY_9;
8918 si->debug.frame_delay_use_mod_key = DEFAULT_FRAME_DELAY_USE_MOD_KEY;
8919 si->debug.frame_delay_game_only = DEFAULT_FRAME_DELAY_GAME_ONLY;
8921 si->debug.show_frames_per_second = FALSE;
8923 si->options.verbose = FALSE;
8925 #if defined(PLATFORM_ANDROID)
8926 si->fullscreen = TRUE;
8930 static void setSetupInfoToDefaults_AutoSetup(struct SetupInfo *si)
8932 si->auto_setup.editor_zoom_tilesize = MINI_TILESIZE;
8935 static void setSetupInfoToDefaults_EditorCascade(struct SetupInfo *si)
8937 si->editor_cascade.el_bd = TRUE;
8938 si->editor_cascade.el_em = TRUE;
8939 si->editor_cascade.el_emc = TRUE;
8940 si->editor_cascade.el_rnd = TRUE;
8941 si->editor_cascade.el_sb = TRUE;
8942 si->editor_cascade.el_sp = TRUE;
8943 si->editor_cascade.el_dc = TRUE;
8944 si->editor_cascade.el_dx = TRUE;
8946 si->editor_cascade.el_mm = TRUE;
8947 si->editor_cascade.el_df = TRUE;
8949 si->editor_cascade.el_chars = FALSE;
8950 si->editor_cascade.el_steel_chars = FALSE;
8951 si->editor_cascade.el_ce = FALSE;
8952 si->editor_cascade.el_ge = FALSE;
8953 si->editor_cascade.el_ref = FALSE;
8954 si->editor_cascade.el_user = FALSE;
8955 si->editor_cascade.el_dynamic = FALSE;
8958 #define MAX_HIDE_SETUP_TOKEN_SIZE 20
8960 static char *getHideSetupToken(void *setup_value)
8962 static char hide_setup_token[MAX_HIDE_SETUP_TOKEN_SIZE];
8964 if (setup_value != NULL)
8965 snprintf(hide_setup_token, MAX_HIDE_SETUP_TOKEN_SIZE, "%p", setup_value);
8967 return hide_setup_token;
8970 void setHideSetupEntry(void *setup_value)
8972 char *hide_setup_token = getHideSetupToken(setup_value);
8974 if (setup_value != NULL)
8975 setHashEntry(hide_setup_hash, hide_setup_token, "");
8978 static void setHideSetupEntryRaw(char *token_text, void *setup_value_raw)
8980 /* !!! DIRTY WORKAROUND; TO BE FIXED AFTER THE MM ENGINE RELEASE !!! */
8981 void *setup_value = setup_value_raw - (void *)&si + (void *)&setup;
8983 setHideSetupEntry(setup_value);
8986 boolean hideSetupEntry(void *setup_value)
8988 char *hide_setup_token = getHideSetupToken(setup_value);
8990 return (setup_value != NULL &&
8991 getHashEntry(hide_setup_hash, hide_setup_token) != NULL);
8994 static void setSetupInfoFromTokenText(SetupFileHash *setup_file_hash,
8995 struct TokenInfo *token_info,
8996 int token_nr, char *token_text)
8998 char *token_hide_text = getStringCat2(token_text, ".hide");
8999 char *token_hide_value = getHashEntry(setup_file_hash, token_hide_text);
9001 /* set the value of this setup option in the setup option structure */
9002 setSetupInfo(token_info, token_nr, getHashEntry(setup_file_hash, token_text));
9004 /* check if this setup option should be hidden in the setup menu */
9005 if (token_hide_value != NULL && get_boolean_from_string(token_hide_value))
9006 setHideSetupEntryRaw(token_text, token_info[token_nr].value);
9009 static void setSetupInfoFromTokenInfo(SetupFileHash *setup_file_hash,
9010 struct TokenInfo *token_info,
9013 setSetupInfoFromTokenText(setup_file_hash, token_info, token_nr,
9014 token_info[token_nr].text);
9017 static void decodeSetupFileHash(SetupFileHash *setup_file_hash)
9021 if (!setup_file_hash)
9024 if (hide_setup_hash == NULL)
9025 hide_setup_hash = newSetupFileHash();
9029 for (i = 0; i < NUM_GLOBAL_SETUP_TOKENS; i++)
9030 setSetupInfoFromTokenInfo(setup_file_hash, global_setup_tokens, i);
9033 /* virtual buttons setup */
9034 setup.touch.grid_initialized = TRUE;
9035 for (i = 0; i < 2; i++)
9037 int grid_xsize = setup.touch.grid_xsize[i];
9038 int grid_ysize = setup.touch.grid_ysize[i];
9041 // if virtual buttons are not loaded from setup file, repeat initializing
9042 // virtual buttons grid with default values later when video is initialized
9043 if (grid_xsize == -1 ||
9046 setup.touch.grid_initialized = FALSE;
9051 for (y = 0; y < grid_ysize; y++)
9053 char token_string[MAX_LINE_LEN];
9055 sprintf(token_string, "touch.virtual_buttons.%d.%02d", i, y);
9057 char *value_string = getHashEntry(setup_file_hash, token_string);
9059 if (value_string == NULL)
9062 for (x = 0; x < grid_xsize; x++)
9064 char c = value_string[x];
9066 setup.touch.grid_button[i][x][y] =
9067 (c == '.' ? CHAR_GRID_BUTTON_NONE : c);
9074 for (i = 0; i < NUM_EDITOR_SETUP_TOKENS; i++)
9075 setSetupInfoFromTokenInfo(setup_file_hash, editor_setup_tokens, i);
9078 /* shortcut setup */
9079 ssi = setup.shortcut;
9080 for (i = 0; i < NUM_SHORTCUT_SETUP_TOKENS; i++)
9081 setSetupInfoFromTokenInfo(setup_file_hash, shortcut_setup_tokens, i);
9082 setup.shortcut = ssi;
9085 for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
9089 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
9091 sii = setup.input[pnr];
9092 for (i = 0; i < NUM_PLAYER_SETUP_TOKENS; i++)
9094 char full_token[100];
9096 sprintf(full_token, "%s%s", prefix, player_setup_tokens[i].text);
9097 setSetupInfoFromTokenText(setup_file_hash, player_setup_tokens, i,
9100 setup.input[pnr] = sii;
9105 for (i = 0; i < NUM_SYSTEM_SETUP_TOKENS; i++)
9106 setSetupInfoFromTokenInfo(setup_file_hash, system_setup_tokens, i);
9109 /* internal setup */
9110 sxi = setup.internal;
9111 for (i = 0; i < NUM_INTERNAL_SETUP_TOKENS; i++)
9112 setSetupInfoFromTokenInfo(setup_file_hash, internal_setup_tokens, i);
9113 setup.internal = sxi;
9117 for (i = 0; i < NUM_DEBUG_SETUP_TOKENS; i++)
9118 setSetupInfoFromTokenInfo(setup_file_hash, debug_setup_tokens, i);
9122 soi = setup.options;
9123 for (i = 0; i < NUM_OPTIONS_SETUP_TOKENS; i++)
9124 setSetupInfoFromTokenInfo(setup_file_hash, options_setup_tokens, i);
9125 setup.options = soi;
9127 setHideRelatedSetupEntries();
9130 static void decodeSetupFileHash_AutoSetup(SetupFileHash *setup_file_hash)
9134 if (!setup_file_hash)
9138 sasi = setup.auto_setup;
9139 for (i = 0; i < NUM_AUTO_SETUP_TOKENS; i++)
9140 setSetupInfo(auto_setup_tokens, i,
9141 getHashEntry(setup_file_hash,
9142 auto_setup_tokens[i].text));
9143 setup.auto_setup = sasi;
9146 static void decodeSetupFileHash_EditorCascade(SetupFileHash *setup_file_hash)
9150 if (!setup_file_hash)
9153 /* editor cascade setup */
9154 seci = setup.editor_cascade;
9155 for (i = 0; i < NUM_EDITOR_CASCADE_SETUP_TOKENS; i++)
9156 setSetupInfo(editor_cascade_setup_tokens, i,
9157 getHashEntry(setup_file_hash,
9158 editor_cascade_setup_tokens[i].text));
9159 setup.editor_cascade = seci;
9162 void LoadSetupFromFilename(char *filename)
9164 SetupFileHash *setup_file_hash = loadSetupFileHash(filename);
9166 if (setup_file_hash)
9168 decodeSetupFileHash(setup_file_hash);
9170 freeSetupFileHash(setup_file_hash);
9174 Error(ERR_DEBUG, "using default setup values");
9178 static void LoadSetup_SpecialPostProcessing()
9180 char *player_name_new;
9182 /* needed to work around problems with fixed length strings */
9183 player_name_new = get_corrected_login_name(setup.player_name);
9184 free(setup.player_name);
9185 setup.player_name = player_name_new;
9187 /* "scroll_delay: on(3) / off(0)" was replaced by scroll delay value */
9188 if (setup.scroll_delay == FALSE)
9190 setup.scroll_delay_value = MIN_SCROLL_DELAY;
9191 setup.scroll_delay = TRUE; /* now always "on" */
9194 /* make sure that scroll delay value stays inside valid range */
9195 setup.scroll_delay_value =
9196 MIN(MAX(MIN_SCROLL_DELAY, setup.scroll_delay_value), MAX_SCROLL_DELAY);
9203 /* always start with reliable default values */
9204 setSetupInfoToDefaults(&setup);
9206 /* try to load setup values from default setup file */
9207 filename = getDefaultSetupFilename();
9209 if (fileExists(filename))
9210 LoadSetupFromFilename(filename);
9212 /* try to load setup values from user setup file */
9213 filename = getSetupFilename();
9215 LoadSetupFromFilename(filename);
9217 LoadSetup_SpecialPostProcessing();
9220 void LoadSetup_AutoSetup()
9222 char *filename = getPath2(getSetupDir(), AUTOSETUP_FILENAME);
9223 SetupFileHash *setup_file_hash = NULL;
9225 /* always start with reliable default values */
9226 setSetupInfoToDefaults_AutoSetup(&setup);
9228 setup_file_hash = loadSetupFileHash(filename);
9230 if (setup_file_hash)
9232 decodeSetupFileHash_AutoSetup(setup_file_hash);
9234 freeSetupFileHash(setup_file_hash);
9240 void LoadSetup_EditorCascade()
9242 char *filename = getPath2(getSetupDir(), EDITORCASCADE_FILENAME);
9243 SetupFileHash *setup_file_hash = NULL;
9245 /* always start with reliable default values */
9246 setSetupInfoToDefaults_EditorCascade(&setup);
9248 setup_file_hash = loadSetupFileHash(filename);
9250 if (setup_file_hash)
9252 decodeSetupFileHash_EditorCascade(setup_file_hash);
9254 freeSetupFileHash(setup_file_hash);
9260 static void addGameControllerMappingToHash(SetupFileHash *mappings_hash,
9263 char mapping_guid[MAX_LINE_LEN];
9264 char *mapping_start, *mapping_end;
9266 // get GUID from game controller mapping line: copy complete line
9267 strncpy(mapping_guid, mapping_line, MAX_LINE_LEN - 1);
9268 mapping_guid[MAX_LINE_LEN - 1] = '\0';
9270 // get GUID from game controller mapping line: cut after GUID part
9271 mapping_start = strchr(mapping_guid, ',');
9272 if (mapping_start != NULL)
9273 *mapping_start = '\0';
9275 // cut newline from game controller mapping line
9276 mapping_end = strchr(mapping_line, '\n');
9277 if (mapping_end != NULL)
9278 *mapping_end = '\0';
9280 // add mapping entry to game controller mappings hash
9281 setHashEntry(mappings_hash, mapping_guid, mapping_line);
9284 static void LoadSetup_ReadGameControllerMappings(SetupFileHash *mappings_hash,
9289 if (!(file = fopen(filename, MODE_READ)))
9291 Error(ERR_WARN, "cannot read game controller mappings file '%s'", filename);
9298 char line[MAX_LINE_LEN];
9300 if (!fgets(line, MAX_LINE_LEN, file))
9303 addGameControllerMappingToHash(mappings_hash, line);
9311 char *filename = getSetupFilename();
9315 InitUserDataDirectory();
9317 if (!(file = fopen(filename, MODE_WRITE)))
9319 Error(ERR_WARN, "cannot write setup file '%s'", filename);
9323 fprintFileHeader(file, SETUP_FILENAME);
9327 for (i = 0; i < NUM_GLOBAL_SETUP_TOKENS; i++)
9329 /* just to make things nicer :) */
9330 if (i == SETUP_TOKEN_PLAYER_NAME + 1 ||
9331 i == SETUP_TOKEN_GRAPHICS_SET ||
9332 i == SETUP_TOKEN_VOLUME_SIMPLE ||
9333 i == SETUP_TOKEN_TOUCH_CONTROL_TYPE ||
9334 i == SETUP_TOKEN_TOUCH_GRID_XSIZE_0 ||
9335 i == SETUP_TOKEN_TOUCH_GRID_XSIZE_1)
9336 fprintf(file, "\n");
9338 fprintf(file, "%s\n", getSetupLine(global_setup_tokens, "", i));
9341 /* virtual buttons setup */
9342 for (i = 0; i < 2; i++)
9344 int grid_xsize = setup.touch.grid_xsize[i];
9345 int grid_ysize = setup.touch.grid_ysize[i];
9348 fprintf(file, "\n");
9350 for (y = 0; y < grid_ysize; y++)
9352 char token_string[MAX_LINE_LEN];
9353 char value_string[MAX_LINE_LEN];
9355 sprintf(token_string, "touch.virtual_buttons.%d.%02d", i, y);
9357 for (x = 0; x < grid_xsize; x++)
9359 char c = setup.touch.grid_button[i][x][y];
9361 value_string[x] = (c == CHAR_GRID_BUTTON_NONE ? '.' : c);
9364 value_string[grid_xsize] = '\0';
9366 fprintf(file, "%s\n", getFormattedSetupEntry(token_string, value_string));
9372 fprintf(file, "\n");
9373 for (i = 0; i < NUM_EDITOR_SETUP_TOKENS; i++)
9374 fprintf(file, "%s\n", getSetupLine(editor_setup_tokens, "", i));
9376 /* shortcut setup */
9377 ssi = setup.shortcut;
9378 fprintf(file, "\n");
9379 for (i = 0; i < NUM_SHORTCUT_SETUP_TOKENS; i++)
9380 fprintf(file, "%s\n", getSetupLine(shortcut_setup_tokens, "", i));
9383 for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
9387 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
9388 fprintf(file, "\n");
9390 sii = setup.input[pnr];
9391 for (i = 0; i < NUM_PLAYER_SETUP_TOKENS; i++)
9392 fprintf(file, "%s\n", getSetupLine(player_setup_tokens, prefix, i));
9397 fprintf(file, "\n");
9398 for (i = 0; i < NUM_SYSTEM_SETUP_TOKENS; i++)
9399 fprintf(file, "%s\n", getSetupLine(system_setup_tokens, "", i));
9401 /* internal setup */
9402 /* (internal setup values not saved to user setup file) */
9406 fprintf(file, "\n");
9407 for (i = 0; i < NUM_DEBUG_SETUP_TOKENS; i++)
9408 fprintf(file, "%s\n", getSetupLine(debug_setup_tokens, "", i));
9411 soi = setup.options;
9412 fprintf(file, "\n");
9413 for (i = 0; i < NUM_OPTIONS_SETUP_TOKENS; i++)
9414 fprintf(file, "%s\n", getSetupLine(options_setup_tokens, "", i));
9418 SetFilePermissions(filename, PERMS_PRIVATE);
9421 void SaveSetup_AutoSetup()
9423 char *filename = getPath2(getSetupDir(), AUTOSETUP_FILENAME);
9427 InitUserDataDirectory();
9429 if (!(file = fopen(filename, MODE_WRITE)))
9431 Error(ERR_WARN, "cannot write auto setup file '%s'", filename);
9436 fprintFileHeader(file, AUTOSETUP_FILENAME);
9438 sasi = setup.auto_setup;
9439 for (i = 0; i < NUM_AUTO_SETUP_TOKENS; i++)
9440 fprintf(file, "%s\n", getSetupLine(auto_setup_tokens, "", i));
9444 SetFilePermissions(filename, PERMS_PRIVATE);
9449 void SaveSetup_EditorCascade()
9451 char *filename = getPath2(getSetupDir(), EDITORCASCADE_FILENAME);
9455 InitUserDataDirectory();
9457 if (!(file = fopen(filename, MODE_WRITE)))
9459 Error(ERR_WARN, "cannot write editor cascade state file '%s'", filename);
9464 fprintFileHeader(file, EDITORCASCADE_FILENAME);
9466 seci = setup.editor_cascade;
9467 for (i = 0; i < NUM_EDITOR_CASCADE_SETUP_TOKENS; i++)
9468 fprintf(file, "%s\n", getSetupLine(editor_cascade_setup_tokens, "", i));
9472 SetFilePermissions(filename, PERMS_PRIVATE);
9477 static void SaveSetup_WriteGameControllerMappings(SetupFileHash *mappings_hash,
9482 if (!(file = fopen(filename, MODE_WRITE)))
9484 Error(ERR_WARN, "cannot write game controller mappings file '%s'",filename);
9489 BEGIN_HASH_ITERATION(mappings_hash, itr)
9491 fprintf(file, "%s\n", HASH_ITERATION_VALUE(itr));
9493 END_HASH_ITERATION(mappings_hash, itr)
9498 void SaveSetup_AddGameControllerMapping(char *mapping)
9500 char *filename = getPath2(getSetupDir(), GAMECONTROLLER_BASENAME);
9501 SetupFileHash *mappings_hash = newSetupFileHash();
9503 InitUserDataDirectory();
9505 // load existing personal game controller mappings
9506 LoadSetup_ReadGameControllerMappings(mappings_hash, filename);
9508 // add new mapping to personal game controller mappings
9509 addGameControllerMappingToHash(mappings_hash, mapping);
9511 // save updated personal game controller mappings
9512 SaveSetup_WriteGameControllerMappings(mappings_hash, filename);
9514 freeSetupFileHash(mappings_hash);
9518 void LoadCustomElementDescriptions()
9520 char *filename = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
9521 SetupFileHash *setup_file_hash;
9524 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
9526 if (element_info[i].custom_description != NULL)
9528 free(element_info[i].custom_description);
9529 element_info[i].custom_description = NULL;
9533 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
9536 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
9538 char *token = getStringCat2(element_info[i].token_name, ".name");
9539 char *value = getHashEntry(setup_file_hash, token);
9542 element_info[i].custom_description = getStringCopy(value);
9547 freeSetupFileHash(setup_file_hash);
9550 static int getElementFromToken(char *token)
9552 char *value = getHashEntry(element_token_hash, token);
9557 Error(ERR_WARN, "unknown element token '%s'", token);
9559 return EL_UNDEFINED;
9562 static int get_token_parameter_value(char *token, char *value_raw)
9566 if (token == NULL || value_raw == NULL)
9567 return ARG_UNDEFINED_VALUE;
9569 suffix = strrchr(token, '.');
9573 if (strEqual(suffix, ".element"))
9574 return getElementFromToken(value_raw);
9576 /* !!! USE CORRECT VALUE TYPE (currently works also for TYPE_BOOLEAN) !!! */
9577 return get_parameter_value(value_raw, suffix, TYPE_INTEGER);
9580 void InitMenuDesignSettings_Static()
9584 /* always start with reliable default values from static default config */
9585 for (i = 0; image_config_vars[i].token != NULL; i++)
9587 char *value = getHashEntry(image_config_hash, image_config_vars[i].token);
9590 *image_config_vars[i].value =
9591 get_token_parameter_value(image_config_vars[i].token, value);
9595 static void InitMenuDesignSettings_SpecialPreProcessing()
9599 /* the following initializes hierarchical values from static configuration */
9601 /* special case: initialize "ARG_DEFAULT" values in static default config */
9602 /* (e.g., initialize "[titlemessage].fade_mode" from "[title].fade_mode") */
9603 titlescreen_initial_first_default.fade_mode =
9604 title_initial_first_default.fade_mode;
9605 titlescreen_initial_first_default.fade_delay =
9606 title_initial_first_default.fade_delay;
9607 titlescreen_initial_first_default.post_delay =
9608 title_initial_first_default.post_delay;
9609 titlescreen_initial_first_default.auto_delay =
9610 title_initial_first_default.auto_delay;
9611 titlescreen_first_default.fade_mode = title_first_default.fade_mode;
9612 titlescreen_first_default.fade_delay = title_first_default.fade_delay;
9613 titlescreen_first_default.post_delay = title_first_default.post_delay;
9614 titlescreen_first_default.auto_delay = title_first_default.auto_delay;
9615 titlemessage_initial_first_default.fade_mode =
9616 title_initial_first_default.fade_mode;
9617 titlemessage_initial_first_default.fade_delay =
9618 title_initial_first_default.fade_delay;
9619 titlemessage_initial_first_default.post_delay =
9620 title_initial_first_default.post_delay;
9621 titlemessage_initial_first_default.auto_delay =
9622 title_initial_first_default.auto_delay;
9623 titlemessage_first_default.fade_mode = title_first_default.fade_mode;
9624 titlemessage_first_default.fade_delay = title_first_default.fade_delay;
9625 titlemessage_first_default.post_delay = title_first_default.post_delay;
9626 titlemessage_first_default.auto_delay = title_first_default.auto_delay;
9628 titlescreen_initial_default.fade_mode = title_initial_default.fade_mode;
9629 titlescreen_initial_default.fade_delay = title_initial_default.fade_delay;
9630 titlescreen_initial_default.post_delay = title_initial_default.post_delay;
9631 titlescreen_initial_default.auto_delay = title_initial_default.auto_delay;
9632 titlescreen_default.fade_mode = title_default.fade_mode;
9633 titlescreen_default.fade_delay = title_default.fade_delay;
9634 titlescreen_default.post_delay = title_default.post_delay;
9635 titlescreen_default.auto_delay = title_default.auto_delay;
9636 titlemessage_initial_default.fade_mode = title_initial_default.fade_mode;
9637 titlemessage_initial_default.fade_delay = title_initial_default.fade_delay;
9638 titlemessage_initial_default.post_delay = title_initial_default.post_delay;
9639 titlemessage_initial_default.auto_delay = title_initial_default.auto_delay;
9640 titlemessage_default.fade_mode = title_default.fade_mode;
9641 titlemessage_default.fade_delay = title_default.fade_delay;
9642 titlemessage_default.post_delay = title_default.post_delay;
9643 titlemessage_default.auto_delay = title_default.auto_delay;
9645 /* special case: initialize "ARG_DEFAULT" values in static default config */
9646 /* (e.g., init "titlemessage_1.fade_mode" from "[titlemessage].fade_mode") */
9647 for (i = 0; i < MAX_NUM_TITLE_MESSAGES; i++)
9649 titlescreen_initial_first[i] = titlescreen_initial_first_default;
9650 titlescreen_first[i] = titlescreen_first_default;
9651 titlemessage_initial_first[i] = titlemessage_initial_first_default;
9652 titlemessage_first[i] = titlemessage_first_default;
9654 titlescreen_initial[i] = titlescreen_initial_default;
9655 titlescreen[i] = titlescreen_default;
9656 titlemessage_initial[i] = titlemessage_initial_default;
9657 titlemessage[i] = titlemessage_default;
9660 /* special case: initialize "ARG_DEFAULT" values in static default config */
9661 /* (eg, init "menu.enter_screen.SCORES.xyz" from "menu.enter_screen.xyz") */
9662 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
9664 if (i == GFX_SPECIAL_ARG_TITLE) /* title values already initialized */
9667 menu.enter_screen[i] = menu.enter_screen[GFX_SPECIAL_ARG_DEFAULT];
9668 menu.leave_screen[i] = menu.leave_screen[GFX_SPECIAL_ARG_DEFAULT];
9669 menu.next_screen[i] = menu.next_screen[GFX_SPECIAL_ARG_DEFAULT];
9672 /* special case: initialize "ARG_DEFAULT" values in static default config */
9673 /* (eg, init "viewport.door_1.MAIN.xyz" from "viewport.door_1.xyz") */
9674 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
9676 viewport.window[i] = viewport.window[GFX_SPECIAL_ARG_DEFAULT];
9677 viewport.playfield[i] = viewport.playfield[GFX_SPECIAL_ARG_DEFAULT];
9678 viewport.door_1[i] = viewport.door_1[GFX_SPECIAL_ARG_DEFAULT];
9680 if (i == GFX_SPECIAL_ARG_EDITOR) /* editor values already initialized */
9683 viewport.door_2[i] = viewport.door_2[GFX_SPECIAL_ARG_DEFAULT];
9687 static void InitMenuDesignSettings_SpecialPostProcessing()
9691 struct XY *dst, *src;
9695 { &game.button.save, &game.button.stop },
9696 { &game.button.pause2, &game.button.pause },
9697 { &game.button.load, &game.button.play },
9698 { &game.button.undo, &game.button.stop },
9699 { &game.button.redo, &game.button.play },
9705 /* special case: initialize later added SETUP list size from LEVELS value */
9706 if (menu.list_size[GAME_MODE_SETUP] == -1)
9707 menu.list_size[GAME_MODE_SETUP] = menu.list_size[GAME_MODE_LEVELS];
9709 /* set default position for snapshot buttons to stop/pause/play buttons */
9710 for (i = 0; game_buttons_xy[i].dst != NULL; i++)
9711 if ((*game_buttons_xy[i].dst).x == -1 &&
9712 (*game_buttons_xy[i].dst).y == -1)
9713 *game_buttons_xy[i].dst = *game_buttons_xy[i].src;
9716 static void InitMenuDesignSettings_SpecialPostProcessing_AfterGraphics()
9720 struct XYTileSize *dst, *src;
9723 editor_buttons_xy[] =
9726 &editor.button.element_left, &editor.palette.element_left,
9727 IMG_GFX_EDITOR_BUTTON_ELEMENT_LEFT
9730 &editor.button.element_middle, &editor.palette.element_middle,
9731 IMG_GFX_EDITOR_BUTTON_ELEMENT_MIDDLE
9734 &editor.button.element_right, &editor.palette.element_right,
9735 IMG_GFX_EDITOR_BUTTON_ELEMENT_RIGHT
9742 /* set default position for element buttons to element graphics */
9743 for (i = 0; editor_buttons_xy[i].dst != NULL; i++)
9745 if ((*editor_buttons_xy[i].dst).x == -1 &&
9746 (*editor_buttons_xy[i].dst).y == -1)
9748 struct GraphicInfo *gd = &graphic_info[editor_buttons_xy[i].graphic];
9750 gd->width = gd->height = editor_buttons_xy[i].src->tile_size;
9752 *editor_buttons_xy[i].dst = *editor_buttons_xy[i].src;
9757 static void LoadMenuDesignSettingsFromFilename(char *filename)
9759 static struct TitleFadingInfo tfi;
9760 static struct TitleMessageInfo tmi;
9761 static struct TokenInfo title_tokens[] =
9763 { TYPE_INTEGER, &tfi.fade_mode, ".fade_mode" },
9764 { TYPE_INTEGER, &tfi.fade_delay, ".fade_delay" },
9765 { TYPE_INTEGER, &tfi.post_delay, ".post_delay" },
9766 { TYPE_INTEGER, &tfi.auto_delay, ".auto_delay" },
9770 static struct TokenInfo titlemessage_tokens[] =
9772 { TYPE_INTEGER, &tmi.x, ".x" },
9773 { TYPE_INTEGER, &tmi.y, ".y" },
9774 { TYPE_INTEGER, &tmi.width, ".width" },
9775 { TYPE_INTEGER, &tmi.height, ".height" },
9776 { TYPE_INTEGER, &tmi.chars, ".chars" },
9777 { TYPE_INTEGER, &tmi.lines, ".lines" },
9778 { TYPE_INTEGER, &tmi.align, ".align" },
9779 { TYPE_INTEGER, &tmi.valign, ".valign" },
9780 { TYPE_INTEGER, &tmi.font, ".font" },
9781 { TYPE_BOOLEAN, &tmi.autowrap, ".autowrap" },
9782 { TYPE_BOOLEAN, &tmi.centered, ".centered" },
9783 { TYPE_BOOLEAN, &tmi.parse_comments, ".parse_comments" },
9784 { TYPE_INTEGER, &tmi.sort_priority, ".sort_priority" },
9785 { TYPE_INTEGER, &tmi.fade_mode, ".fade_mode" },
9786 { TYPE_INTEGER, &tmi.fade_delay, ".fade_delay" },
9787 { TYPE_INTEGER, &tmi.post_delay, ".post_delay" },
9788 { TYPE_INTEGER, &tmi.auto_delay, ".auto_delay" },
9794 struct TitleFadingInfo *info;
9799 /* initialize first titles from "enter screen" definitions, if defined */
9800 { &title_initial_first_default, "menu.enter_screen.TITLE" },
9801 { &title_first_default, "menu.enter_screen.TITLE" },
9803 /* initialize title screens from "next screen" definitions, if defined */
9804 { &title_initial_default, "menu.next_screen.TITLE" },
9805 { &title_default, "menu.next_screen.TITLE" },
9811 struct TitleMessageInfo *array;
9814 titlemessage_arrays[] =
9816 /* initialize first titles from "enter screen" definitions, if defined */
9817 { titlescreen_initial_first, "menu.enter_screen.TITLE" },
9818 { titlescreen_first, "menu.enter_screen.TITLE" },
9819 { titlemessage_initial_first, "menu.enter_screen.TITLE" },
9820 { titlemessage_first, "menu.enter_screen.TITLE" },
9822 /* initialize titles from "next screen" definitions, if defined */
9823 { titlescreen_initial, "menu.next_screen.TITLE" },
9824 { titlescreen, "menu.next_screen.TITLE" },
9825 { titlemessage_initial, "menu.next_screen.TITLE" },
9826 { titlemessage, "menu.next_screen.TITLE" },
9828 /* overwrite titles with title definitions, if defined */
9829 { titlescreen_initial_first, "[title_initial]" },
9830 { titlescreen_first, "[title]" },
9831 { titlemessage_initial_first, "[title_initial]" },
9832 { titlemessage_first, "[title]" },
9834 { titlescreen_initial, "[title_initial]" },
9835 { titlescreen, "[title]" },
9836 { titlemessage_initial, "[title_initial]" },
9837 { titlemessage, "[title]" },
9839 /* overwrite titles with title screen/message definitions, if defined */
9840 { titlescreen_initial_first, "[titlescreen_initial]" },
9841 { titlescreen_first, "[titlescreen]" },
9842 { titlemessage_initial_first, "[titlemessage_initial]" },
9843 { titlemessage_first, "[titlemessage]" },
9845 { titlescreen_initial, "[titlescreen_initial]" },
9846 { titlescreen, "[titlescreen]" },
9847 { titlemessage_initial, "[titlemessage_initial]" },
9848 { titlemessage, "[titlemessage]" },
9852 SetupFileHash *setup_file_hash;
9855 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
9858 /* the following initializes hierarchical values from dynamic configuration */
9860 /* special case: initialize with default values that may be overwritten */
9861 /* (e.g., init "menu.draw_xoffset.INFO" from "menu.draw_xoffset") */
9862 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
9864 char *value_1 = getHashEntry(setup_file_hash, "menu.draw_xoffset");
9865 char *value_2 = getHashEntry(setup_file_hash, "menu.draw_yoffset");
9866 char *value_3 = getHashEntry(setup_file_hash, "menu.list_size");
9868 if (value_1 != NULL)
9869 menu.draw_xoffset[i] = get_integer_from_string(value_1);
9870 if (value_2 != NULL)
9871 menu.draw_yoffset[i] = get_integer_from_string(value_2);
9872 if (value_3 != NULL)
9873 menu.list_size[i] = get_integer_from_string(value_3);
9876 /* special case: initialize with default values that may be overwritten */
9877 /* (eg, init "menu.draw_xoffset.INFO[XXX]" from "menu.draw_xoffset.INFO") */
9878 for (i = 0; i < NUM_SPECIAL_GFX_INFO_ARGS; i++)
9880 char *value_1 = getHashEntry(setup_file_hash, "menu.draw_xoffset.INFO");
9881 char *value_2 = getHashEntry(setup_file_hash, "menu.draw_yoffset.INFO");
9883 if (value_1 != NULL)
9884 menu.draw_xoffset_info[i] = get_integer_from_string(value_1);
9885 if (value_2 != NULL)
9886 menu.draw_yoffset_info[i] = get_integer_from_string(value_2);
9888 if (i == GFX_SPECIAL_ARG_INFO_ELEMENTS)
9890 char *value_1 = getHashEntry(setup_file_hash, "menu.list_size.INFO");
9892 if (value_1 != NULL)
9893 menu.list_size_info[i] = get_integer_from_string(value_1);
9897 /* special case: initialize with default values that may be overwritten */
9898 /* (eg, init "menu.draw_xoffset.SETUP[XXX]" from "menu.draw_xoffset.SETUP") */
9899 for (i = 0; i < NUM_SPECIAL_GFX_SETUP_ARGS; i++)
9901 char *value_1 = getHashEntry(setup_file_hash, "menu.draw_xoffset.SETUP");
9902 char *value_2 = getHashEntry(setup_file_hash, "menu.draw_yoffset.SETUP");
9904 if (value_1 != NULL)
9905 menu.draw_xoffset_setup[i] = get_integer_from_string(value_1);
9906 if (value_2 != NULL)
9907 menu.draw_yoffset_setup[i] = get_integer_from_string(value_2);
9910 /* special case: initialize with default values that may be overwritten */
9911 /* (eg, init "menu.line_spacing.INFO[XXX]" from "menu.line_spacing.INFO") */
9912 for (i = 0; i < NUM_SPECIAL_GFX_INFO_ARGS; i++)
9914 char *value_1 = getHashEntry(setup_file_hash,"menu.left_spacing.INFO");
9915 char *value_2 = getHashEntry(setup_file_hash,"menu.right_spacing.INFO");
9916 char *value_3 = getHashEntry(setup_file_hash,"menu.top_spacing.INFO");
9917 char *value_4 = getHashEntry(setup_file_hash,"menu.bottom_spacing.INFO");
9918 char *value_5 = getHashEntry(setup_file_hash,"menu.paragraph_spacing.INFO");
9919 char *value_6 = getHashEntry(setup_file_hash,"menu.headline1_spacing.INFO");
9920 char *value_7 = getHashEntry(setup_file_hash,"menu.headline2_spacing.INFO");
9921 char *value_8 = getHashEntry(setup_file_hash,"menu.line_spacing.INFO");
9922 char *value_9 = getHashEntry(setup_file_hash,"menu.extra_spacing.INFO");
9924 if (value_1 != NULL)
9925 menu.left_spacing_info[i] = get_integer_from_string(value_1);
9926 if (value_2 != NULL)
9927 menu.right_spacing_info[i] = get_integer_from_string(value_2);
9928 if (value_3 != NULL)
9929 menu.top_spacing_info[i] = get_integer_from_string(value_3);
9930 if (value_4 != NULL)
9931 menu.bottom_spacing_info[i] = get_integer_from_string(value_4);
9932 if (value_5 != NULL)
9933 menu.paragraph_spacing_info[i] = get_integer_from_string(value_5);
9934 if (value_6 != NULL)
9935 menu.headline1_spacing_info[i] = get_integer_from_string(value_6);
9936 if (value_7 != NULL)
9937 menu.headline2_spacing_info[i] = get_integer_from_string(value_7);
9938 if (value_8 != NULL)
9939 menu.line_spacing_info[i] = get_integer_from_string(value_8);
9940 if (value_9 != NULL)
9941 menu.extra_spacing_info[i] = get_integer_from_string(value_9);
9944 /* special case: initialize with default values that may be overwritten */
9945 /* (eg, init "menu.enter_screen.SCORES.xyz" from "menu.enter_screen.xyz") */
9946 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
9948 char *token_1 = "menu.enter_screen.fade_mode";
9949 char *token_2 = "menu.enter_screen.fade_delay";
9950 char *token_3 = "menu.enter_screen.post_delay";
9951 char *token_4 = "menu.leave_screen.fade_mode";
9952 char *token_5 = "menu.leave_screen.fade_delay";
9953 char *token_6 = "menu.leave_screen.post_delay";
9954 char *token_7 = "menu.next_screen.fade_mode";
9955 char *token_8 = "menu.next_screen.fade_delay";
9956 char *token_9 = "menu.next_screen.post_delay";
9957 char *value_1 = getHashEntry(setup_file_hash, token_1);
9958 char *value_2 = getHashEntry(setup_file_hash, token_2);
9959 char *value_3 = getHashEntry(setup_file_hash, token_3);
9960 char *value_4 = getHashEntry(setup_file_hash, token_4);
9961 char *value_5 = getHashEntry(setup_file_hash, token_5);
9962 char *value_6 = getHashEntry(setup_file_hash, token_6);
9963 char *value_7 = getHashEntry(setup_file_hash, token_7);
9964 char *value_8 = getHashEntry(setup_file_hash, token_8);
9965 char *value_9 = getHashEntry(setup_file_hash, token_9);
9967 if (value_1 != NULL)
9968 menu.enter_screen[i].fade_mode = get_token_parameter_value(token_1,
9970 if (value_2 != NULL)
9971 menu.enter_screen[i].fade_delay = get_token_parameter_value(token_2,
9973 if (value_3 != NULL)
9974 menu.enter_screen[i].post_delay = get_token_parameter_value(token_3,
9976 if (value_4 != NULL)
9977 menu.leave_screen[i].fade_mode = get_token_parameter_value(token_4,
9979 if (value_5 != NULL)
9980 menu.leave_screen[i].fade_delay = get_token_parameter_value(token_5,
9982 if (value_6 != NULL)
9983 menu.leave_screen[i].post_delay = get_token_parameter_value(token_6,
9985 if (value_7 != NULL)
9986 menu.next_screen[i].fade_mode = get_token_parameter_value(token_7,
9988 if (value_8 != NULL)
9989 menu.next_screen[i].fade_delay = get_token_parameter_value(token_8,
9991 if (value_9 != NULL)
9992 menu.next_screen[i].post_delay = get_token_parameter_value(token_9,
9996 /* special case: initialize with default values that may be overwritten */
9997 /* (eg, init "viewport.door_1.MAIN.xyz" from "viewport.door_1.xyz") */
9998 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
10000 char *token_w1 = "viewport.window.width";
10001 char *token_w2 = "viewport.window.height";
10002 char *token_01 = "viewport.playfield.x";
10003 char *token_02 = "viewport.playfield.y";
10004 char *token_03 = "viewport.playfield.width";
10005 char *token_04 = "viewport.playfield.height";
10006 char *token_05 = "viewport.playfield.border_size";
10007 char *token_06 = "viewport.door_1.x";
10008 char *token_07 = "viewport.door_1.y";
10009 char *token_08 = "viewport.door_1.width";
10010 char *token_09 = "viewport.door_1.height";
10011 char *token_10 = "viewport.door_1.border_size";
10012 char *token_11 = "viewport.door_2.x";
10013 char *token_12 = "viewport.door_2.y";
10014 char *token_13 = "viewport.door_2.width";
10015 char *token_14 = "viewport.door_2.height";
10016 char *token_15 = "viewport.door_2.border_size";
10017 char *value_w1 = getHashEntry(setup_file_hash, token_w1);
10018 char *value_w2 = getHashEntry(setup_file_hash, token_w2);
10019 char *value_01 = getHashEntry(setup_file_hash, token_01);
10020 char *value_02 = getHashEntry(setup_file_hash, token_02);
10021 char *value_03 = getHashEntry(setup_file_hash, token_03);
10022 char *value_04 = getHashEntry(setup_file_hash, token_04);
10023 char *value_05 = getHashEntry(setup_file_hash, token_05);
10024 char *value_06 = getHashEntry(setup_file_hash, token_06);
10025 char *value_07 = getHashEntry(setup_file_hash, token_07);
10026 char *value_08 = getHashEntry(setup_file_hash, token_08);
10027 char *value_09 = getHashEntry(setup_file_hash, token_09);
10028 char *value_10 = getHashEntry(setup_file_hash, token_10);
10029 char *value_11 = getHashEntry(setup_file_hash, token_11);
10030 char *value_12 = getHashEntry(setup_file_hash, token_12);
10031 char *value_13 = getHashEntry(setup_file_hash, token_13);
10032 char *value_14 = getHashEntry(setup_file_hash, token_14);
10033 char *value_15 = getHashEntry(setup_file_hash, token_15);
10035 if (value_w1 != NULL)
10036 viewport.window[i].width = get_token_parameter_value(token_w1, value_w1);
10037 if (value_w2 != NULL)
10038 viewport.window[i].height = get_token_parameter_value(token_w2, value_w2);
10039 if (value_01 != NULL)
10040 viewport.playfield[i].x = get_token_parameter_value(token_01, value_01);
10041 if (value_02 != NULL)
10042 viewport.playfield[i].y = get_token_parameter_value(token_02, value_02);
10043 if (value_03 != NULL)
10044 viewport.playfield[i].width = get_token_parameter_value(token_03,
10046 if (value_04 != NULL)
10047 viewport.playfield[i].height = get_token_parameter_value(token_04,
10049 if (value_05 != NULL)
10050 viewport.playfield[i].border_size = get_token_parameter_value(token_05,
10052 if (value_06 != NULL)
10053 viewport.door_1[i].x = get_token_parameter_value(token_06, value_06);
10054 if (value_07 != NULL)
10055 viewport.door_1[i].y = get_token_parameter_value(token_07, value_07);
10056 if (value_08 != NULL)
10057 viewport.door_1[i].width = get_token_parameter_value(token_08, value_08);
10058 if (value_09 != NULL)
10059 viewport.door_1[i].height = get_token_parameter_value(token_09, value_09);
10060 if (value_10 != NULL)
10061 viewport.door_1[i].border_size = get_token_parameter_value(token_10,
10063 if (value_11 != NULL)
10064 viewport.door_2[i].x = get_token_parameter_value(token_11, value_11);
10065 if (value_12 != NULL)
10066 viewport.door_2[i].y = get_token_parameter_value(token_12, value_12);
10067 if (value_13 != NULL)
10068 viewport.door_2[i].width = get_token_parameter_value(token_13, value_13);
10069 if (value_14 != NULL)
10070 viewport.door_2[i].height = get_token_parameter_value(token_14, value_14);
10071 if (value_15 != NULL)
10072 viewport.door_1[i].border_size = get_token_parameter_value(token_15,
10076 /* special case: initialize with default values that may be overwritten */
10077 /* (e.g., init "[title].fade_mode" from "menu.next_screen.TITLE.fade_mode") */
10078 for (i = 0; title_info[i].info != NULL; i++)
10080 struct TitleFadingInfo *info = title_info[i].info;
10081 char *base_token = title_info[i].text;
10083 for (j = 0; title_tokens[j].type != -1; j++)
10085 char *token = getStringCat2(base_token, title_tokens[j].text);
10086 char *value = getHashEntry(setup_file_hash, token);
10090 int parameter_value = get_token_parameter_value(token, value);
10094 *(int *)title_tokens[j].value = (int)parameter_value;
10103 /* special case: initialize with default values that may be overwritten */
10104 /* (e.g., init "titlemessage_1.fade_mode" from "[titlemessage].fade_mode") */
10105 for (i = 0; titlemessage_arrays[i].array != NULL; i++)
10107 struct TitleMessageInfo *array = titlemessage_arrays[i].array;
10108 char *base_token = titlemessage_arrays[i].text;
10110 for (j = 0; titlemessage_tokens[j].type != -1; j++)
10112 char *token = getStringCat2(base_token, titlemessage_tokens[j].text);
10113 char *value = getHashEntry(setup_file_hash, token);
10117 int parameter_value = get_token_parameter_value(token, value);
10119 for (k = 0; k < MAX_NUM_TITLE_MESSAGES; k++)
10123 if (titlemessage_tokens[j].type == TYPE_INTEGER)
10124 *(int *)titlemessage_tokens[j].value = (int)parameter_value;
10126 *(boolean *)titlemessage_tokens[j].value = (boolean)parameter_value;
10136 /* read (and overwrite with) values that may be specified in config file */
10137 for (i = 0; image_config_vars[i].token != NULL; i++)
10139 char *value = getHashEntry(setup_file_hash, image_config_vars[i].token);
10141 /* (ignore definitions set to "[DEFAULT]" which are already initialized) */
10142 if (value != NULL && !strEqual(value, ARG_DEFAULT))
10143 *image_config_vars[i].value =
10144 get_token_parameter_value(image_config_vars[i].token, value);
10147 freeSetupFileHash(setup_file_hash);
10150 void LoadMenuDesignSettings()
10152 char *filename_base = UNDEFINED_FILENAME, *filename_local;
10154 InitMenuDesignSettings_Static();
10155 InitMenuDesignSettings_SpecialPreProcessing();
10157 if (!GFX_OVERRIDE_ARTWORK(ARTWORK_TYPE_GRAPHICS))
10159 /* first look for special settings configured in level series config */
10160 filename_base = getCustomArtworkLevelConfigFilename(ARTWORK_TYPE_GRAPHICS);
10162 if (fileExists(filename_base))
10163 LoadMenuDesignSettingsFromFilename(filename_base);
10166 filename_local = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
10168 if (filename_local != NULL && !strEqual(filename_base, filename_local))
10169 LoadMenuDesignSettingsFromFilename(filename_local);
10171 InitMenuDesignSettings_SpecialPostProcessing();
10174 void LoadMenuDesignSettings_AfterGraphics()
10176 InitMenuDesignSettings_SpecialPostProcessing_AfterGraphics();
10179 void LoadUserDefinedEditorElementList(int **elements, int *num_elements)
10181 char *filename = getEditorSetupFilename();
10182 SetupFileList *setup_file_list, *list;
10183 SetupFileHash *element_hash;
10184 int num_unknown_tokens = 0;
10187 if ((setup_file_list = loadSetupFileList(filename)) == NULL)
10190 element_hash = newSetupFileHash();
10192 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
10193 setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
10195 /* determined size may be larger than needed (due to unknown elements) */
10197 for (list = setup_file_list; list != NULL; list = list->next)
10200 /* add space for up to 3 more elements for padding that may be needed */
10201 *num_elements += 3;
10203 /* free memory for old list of elements, if needed */
10204 checked_free(*elements);
10206 /* allocate memory for new list of elements */
10207 *elements = checked_malloc(*num_elements * sizeof(int));
10210 for (list = setup_file_list; list != NULL; list = list->next)
10212 char *value = getHashEntry(element_hash, list->token);
10214 if (value == NULL) /* try to find obsolete token mapping */
10216 char *mapped_token = get_mapped_token(list->token);
10218 if (mapped_token != NULL)
10220 value = getHashEntry(element_hash, mapped_token);
10222 free(mapped_token);
10228 (*elements)[(*num_elements)++] = atoi(value);
10232 if (num_unknown_tokens == 0)
10234 Error(ERR_INFO_LINE, "-");
10235 Error(ERR_INFO, "warning: unknown token(s) found in config file:");
10236 Error(ERR_INFO, "- config file: '%s'", filename);
10238 num_unknown_tokens++;
10241 Error(ERR_INFO, "- token: '%s'", list->token);
10245 if (num_unknown_tokens > 0)
10246 Error(ERR_INFO_LINE, "-");
10248 while (*num_elements % 4) /* pad with empty elements, if needed */
10249 (*elements)[(*num_elements)++] = EL_EMPTY;
10251 freeSetupFileList(setup_file_list);
10252 freeSetupFileHash(element_hash);
10255 for (i = 0; i < *num_elements; i++)
10256 printf("editor: element '%s' [%d]\n",
10257 element_info[(*elements)[i]].token_name, (*elements)[i]);
10261 static struct MusicFileInfo *get_music_file_info_ext(char *basename, int music,
10264 SetupFileHash *setup_file_hash = NULL;
10265 struct MusicFileInfo tmp_music_file_info, *new_music_file_info;
10266 char *filename_music, *filename_prefix, *filename_info;
10272 token_to_value_ptr[] =
10274 { "title_header", &tmp_music_file_info.title_header },
10275 { "artist_header", &tmp_music_file_info.artist_header },
10276 { "album_header", &tmp_music_file_info.album_header },
10277 { "year_header", &tmp_music_file_info.year_header },
10279 { "title", &tmp_music_file_info.title },
10280 { "artist", &tmp_music_file_info.artist },
10281 { "album", &tmp_music_file_info.album },
10282 { "year", &tmp_music_file_info.year },
10288 filename_music = (is_sound ? getCustomSoundFilename(basename) :
10289 getCustomMusicFilename(basename));
10291 if (filename_music == NULL)
10294 /* ---------- try to replace file extension ---------- */
10296 filename_prefix = getStringCopy(filename_music);
10297 if (strrchr(filename_prefix, '.') != NULL)
10298 *strrchr(filename_prefix, '.') = '\0';
10299 filename_info = getStringCat2(filename_prefix, ".txt");
10301 if (fileExists(filename_info))
10302 setup_file_hash = loadSetupFileHash(filename_info);
10304 free(filename_prefix);
10305 free(filename_info);
10307 if (setup_file_hash == NULL)
10309 /* ---------- try to add file extension ---------- */
10311 filename_prefix = getStringCopy(filename_music);
10312 filename_info = getStringCat2(filename_prefix, ".txt");
10314 if (fileExists(filename_info))
10315 setup_file_hash = loadSetupFileHash(filename_info);
10317 free(filename_prefix);
10318 free(filename_info);
10321 if (setup_file_hash == NULL)
10324 /* ---------- music file info found ---------- */
10326 clear_mem(&tmp_music_file_info, sizeof(struct MusicFileInfo));
10328 for (i = 0; token_to_value_ptr[i].token != NULL; i++)
10330 char *value = getHashEntry(setup_file_hash, token_to_value_ptr[i].token);
10332 *token_to_value_ptr[i].value_ptr =
10333 getStringCopy(value != NULL && *value != '\0' ? value : UNKNOWN_NAME);
10336 tmp_music_file_info.basename = getStringCopy(basename);
10337 tmp_music_file_info.music = music;
10338 tmp_music_file_info.is_sound = is_sound;
10340 new_music_file_info = checked_malloc(sizeof(struct MusicFileInfo));
10341 *new_music_file_info = tmp_music_file_info;
10343 return new_music_file_info;
10346 static struct MusicFileInfo *get_music_file_info(char *basename, int music)
10348 return get_music_file_info_ext(basename, music, FALSE);
10351 static struct MusicFileInfo *get_sound_file_info(char *basename, int sound)
10353 return get_music_file_info_ext(basename, sound, TRUE);
10356 static boolean music_info_listed_ext(struct MusicFileInfo *list,
10357 char *basename, boolean is_sound)
10359 for (; list != NULL; list = list->next)
10360 if (list->is_sound == is_sound && strEqual(list->basename, basename))
10366 static boolean music_info_listed(struct MusicFileInfo *list, char *basename)
10368 return music_info_listed_ext(list, basename, FALSE);
10371 static boolean sound_info_listed(struct MusicFileInfo *list, char *basename)
10373 return music_info_listed_ext(list, basename, TRUE);
10376 void LoadMusicInfo()
10378 char *music_directory = getCustomMusicDirectory();
10379 int num_music = getMusicListSize();
10380 int num_music_noconf = 0;
10381 int num_sounds = getSoundListSize();
10383 DirectoryEntry *dir_entry;
10384 struct FileInfo *music, *sound;
10385 struct MusicFileInfo *next, **new;
10388 while (music_file_info != NULL)
10390 next = music_file_info->next;
10392 checked_free(music_file_info->basename);
10394 checked_free(music_file_info->title_header);
10395 checked_free(music_file_info->artist_header);
10396 checked_free(music_file_info->album_header);
10397 checked_free(music_file_info->year_header);
10399 checked_free(music_file_info->title);
10400 checked_free(music_file_info->artist);
10401 checked_free(music_file_info->album);
10402 checked_free(music_file_info->year);
10404 free(music_file_info);
10406 music_file_info = next;
10409 new = &music_file_info;
10411 for (i = 0; i < num_music; i++)
10413 music = getMusicListEntry(i);
10415 if (music->filename == NULL)
10418 if (strEqual(music->filename, UNDEFINED_FILENAME))
10421 /* a configured file may be not recognized as music */
10422 if (!FileIsMusic(music->filename))
10425 if (!music_info_listed(music_file_info, music->filename))
10427 *new = get_music_file_info(music->filename, i);
10430 new = &(*new)->next;
10434 if ((dir = openDirectory(music_directory)) == NULL)
10436 Error(ERR_WARN, "cannot read music directory '%s'", music_directory);
10440 while ((dir_entry = readDirectory(dir)) != NULL) /* loop all entries */
10442 char *basename = dir_entry->basename;
10443 boolean music_already_used = FALSE;
10446 /* skip all music files that are configured in music config file */
10447 for (i = 0; i < num_music; i++)
10449 music = getMusicListEntry(i);
10451 if (music->filename == NULL)
10454 if (strEqual(basename, music->filename))
10456 music_already_used = TRUE;
10461 if (music_already_used)
10464 if (!FileIsMusic(dir_entry->filename))
10467 if (!music_info_listed(music_file_info, basename))
10469 *new = get_music_file_info(basename, MAP_NOCONF_MUSIC(num_music_noconf));
10472 new = &(*new)->next;
10475 num_music_noconf++;
10478 closeDirectory(dir);
10480 for (i = 0; i < num_sounds; i++)
10482 sound = getSoundListEntry(i);
10484 if (sound->filename == NULL)
10487 if (strEqual(sound->filename, UNDEFINED_FILENAME))
10490 /* a configured file may be not recognized as sound */
10491 if (!FileIsSound(sound->filename))
10494 if (!sound_info_listed(music_file_info, sound->filename))
10496 *new = get_sound_file_info(sound->filename, i);
10498 new = &(*new)->next;
10503 void add_helpanim_entry(int element, int action, int direction, int delay,
10504 int *num_list_entries)
10506 struct HelpAnimInfo *new_list_entry;
10507 (*num_list_entries)++;
10510 checked_realloc(helpanim_info,
10511 *num_list_entries * sizeof(struct HelpAnimInfo));
10512 new_list_entry = &helpanim_info[*num_list_entries - 1];
10514 new_list_entry->element = element;
10515 new_list_entry->action = action;
10516 new_list_entry->direction = direction;
10517 new_list_entry->delay = delay;
10520 void print_unknown_token(char *filename, char *token, int token_nr)
10524 Error(ERR_INFO_LINE, "-");
10525 Error(ERR_INFO, "warning: unknown token(s) found in config file:");
10526 Error(ERR_INFO, "- config file: '%s'", filename);
10529 Error(ERR_INFO, "- token: '%s'", token);
10532 void print_unknown_token_end(int token_nr)
10535 Error(ERR_INFO_LINE, "-");
10538 void LoadHelpAnimInfo()
10540 char *filename = getHelpAnimFilename();
10541 SetupFileList *setup_file_list = NULL, *list;
10542 SetupFileHash *element_hash, *action_hash, *direction_hash;
10543 int num_list_entries = 0;
10544 int num_unknown_tokens = 0;
10547 if (fileExists(filename))
10548 setup_file_list = loadSetupFileList(filename);
10550 if (setup_file_list == NULL)
10552 /* use reliable default values from static configuration */
10553 SetupFileList *insert_ptr;
10555 insert_ptr = setup_file_list =
10556 newSetupFileList(helpanim_config[0].token,
10557 helpanim_config[0].value);
10559 for (i = 1; helpanim_config[i].token; i++)
10560 insert_ptr = addListEntry(insert_ptr,
10561 helpanim_config[i].token,
10562 helpanim_config[i].value);
10565 element_hash = newSetupFileHash();
10566 action_hash = newSetupFileHash();
10567 direction_hash = newSetupFileHash();
10569 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
10570 setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
10572 for (i = 0; i < NUM_ACTIONS; i++)
10573 setHashEntry(action_hash, element_action_info[i].suffix,
10574 i_to_a(element_action_info[i].value));
10576 /* do not store direction index (bit) here, but direction value! */
10577 for (i = 0; i < NUM_DIRECTIONS_FULL; i++)
10578 setHashEntry(direction_hash, element_direction_info[i].suffix,
10579 i_to_a(1 << element_direction_info[i].value));
10581 for (list = setup_file_list; list != NULL; list = list->next)
10583 char *element_token, *action_token, *direction_token;
10584 char *element_value, *action_value, *direction_value;
10585 int delay = atoi(list->value);
10587 if (strEqual(list->token, "end"))
10589 add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
10594 /* first try to break element into element/action/direction parts;
10595 if this does not work, also accept combined "element[.act][.dir]"
10596 elements (like "dynamite.active"), which are unique elements */
10598 if (strchr(list->token, '.') == NULL) /* token contains no '.' */
10600 element_value = getHashEntry(element_hash, list->token);
10601 if (element_value != NULL) /* element found */
10602 add_helpanim_entry(atoi(element_value), -1, -1, delay,
10603 &num_list_entries);
10606 /* no further suffixes found -- this is not an element */
10607 print_unknown_token(filename, list->token, num_unknown_tokens++);
10613 /* token has format "<prefix>.<something>" */
10615 action_token = strchr(list->token, '.'); /* suffix may be action ... */
10616 direction_token = action_token; /* ... or direction */
10618 element_token = getStringCopy(list->token);
10619 *strchr(element_token, '.') = '\0';
10621 element_value = getHashEntry(element_hash, element_token);
10623 if (element_value == NULL) /* this is no element */
10625 element_value = getHashEntry(element_hash, list->token);
10626 if (element_value != NULL) /* combined element found */
10627 add_helpanim_entry(atoi(element_value), -1, -1, delay,
10628 &num_list_entries);
10630 print_unknown_token(filename, list->token, num_unknown_tokens++);
10632 free(element_token);
10637 action_value = getHashEntry(action_hash, action_token);
10639 if (action_value != NULL) /* action found */
10641 add_helpanim_entry(atoi(element_value), atoi(action_value), -1, delay,
10642 &num_list_entries);
10644 free(element_token);
10649 direction_value = getHashEntry(direction_hash, direction_token);
10651 if (direction_value != NULL) /* direction found */
10653 add_helpanim_entry(atoi(element_value), -1, atoi(direction_value), delay,
10654 &num_list_entries);
10656 free(element_token);
10661 if (strchr(action_token + 1, '.') == NULL)
10663 /* no further suffixes found -- this is not an action nor direction */
10665 element_value = getHashEntry(element_hash, list->token);
10666 if (element_value != NULL) /* combined element found */
10667 add_helpanim_entry(atoi(element_value), -1, -1, delay,
10668 &num_list_entries);
10670 print_unknown_token(filename, list->token, num_unknown_tokens++);
10672 free(element_token);
10677 /* token has format "<prefix>.<suffix>.<something>" */
10679 direction_token = strchr(action_token + 1, '.');
10681 action_token = getStringCopy(action_token);
10682 *strchr(action_token + 1, '.') = '\0';
10684 action_value = getHashEntry(action_hash, action_token);
10686 if (action_value == NULL) /* this is no action */
10688 element_value = getHashEntry(element_hash, list->token);
10689 if (element_value != NULL) /* combined element found */
10690 add_helpanim_entry(atoi(element_value), -1, -1, delay,
10691 &num_list_entries);
10693 print_unknown_token(filename, list->token, num_unknown_tokens++);
10695 free(element_token);
10696 free(action_token);
10701 direction_value = getHashEntry(direction_hash, direction_token);
10703 if (direction_value != NULL) /* direction found */
10705 add_helpanim_entry(atoi(element_value), atoi(action_value),
10706 atoi(direction_value), delay, &num_list_entries);
10708 free(element_token);
10709 free(action_token);
10714 /* this is no direction */
10716 element_value = getHashEntry(element_hash, list->token);
10717 if (element_value != NULL) /* combined element found */
10718 add_helpanim_entry(atoi(element_value), -1, -1, delay,
10719 &num_list_entries);
10721 print_unknown_token(filename, list->token, num_unknown_tokens++);
10723 free(element_token);
10724 free(action_token);
10727 print_unknown_token_end(num_unknown_tokens);
10729 add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
10730 add_helpanim_entry(HELPANIM_LIST_END, -1, -1, -1, &num_list_entries);
10732 freeSetupFileList(setup_file_list);
10733 freeSetupFileHash(element_hash);
10734 freeSetupFileHash(action_hash);
10735 freeSetupFileHash(direction_hash);
10738 for (i = 0; i < num_list_entries; i++)
10739 printf("::: '%s': %d, %d, %d => %d\n",
10740 EL_NAME(helpanim_info[i].element),
10741 helpanim_info[i].element,
10742 helpanim_info[i].action,
10743 helpanim_info[i].direction,
10744 helpanim_info[i].delay);
10748 void LoadHelpTextInfo()
10750 char *filename = getHelpTextFilename();
10753 if (helptext_info != NULL)
10755 freeSetupFileHash(helptext_info);
10756 helptext_info = NULL;
10759 if (fileExists(filename))
10760 helptext_info = loadSetupFileHash(filename);
10762 if (helptext_info == NULL)
10764 /* use reliable default values from static configuration */
10765 helptext_info = newSetupFileHash();
10767 for (i = 0; helptext_config[i].token; i++)
10768 setHashEntry(helptext_info,
10769 helptext_config[i].token,
10770 helptext_config[i].value);
10774 BEGIN_HASH_ITERATION(helptext_info, itr)
10776 printf("::: '%s' => '%s'\n",
10777 HASH_ITERATION_TOKEN(itr), HASH_ITERATION_VALUE(itr));
10779 END_HASH_ITERATION(hash, itr)
10784 /* ------------------------------------------------------------------------- */
10785 /* convert levels */
10786 /* ------------------------------------------------------------------------- */
10788 #define MAX_NUM_CONVERT_LEVELS 1000
10790 void ConvertLevels()
10792 static LevelDirTree *convert_leveldir = NULL;
10793 static int convert_level_nr = -1;
10794 static int num_levels_handled = 0;
10795 static int num_levels_converted = 0;
10796 static boolean levels_failed[MAX_NUM_CONVERT_LEVELS];
10799 convert_leveldir = getTreeInfoFromIdentifier(leveldir_first,
10800 global.convert_leveldir);
10802 if (convert_leveldir == NULL)
10803 Error(ERR_EXIT, "no such level identifier: '%s'",
10804 global.convert_leveldir);
10806 leveldir_current = convert_leveldir;
10808 if (global.convert_level_nr != -1)
10810 convert_leveldir->first_level = global.convert_level_nr;
10811 convert_leveldir->last_level = global.convert_level_nr;
10814 convert_level_nr = convert_leveldir->first_level;
10816 PrintLine("=", 79);
10817 Print("Converting levels\n");
10818 PrintLine("-", 79);
10819 Print("Level series identifier: '%s'\n", convert_leveldir->identifier);
10820 Print("Level series name: '%s'\n", convert_leveldir->name);
10821 Print("Level series author: '%s'\n", convert_leveldir->author);
10822 Print("Number of levels: %d\n", convert_leveldir->levels);
10823 PrintLine("=", 79);
10826 for (i = 0; i < MAX_NUM_CONVERT_LEVELS; i++)
10827 levels_failed[i] = FALSE;
10829 while (convert_level_nr <= convert_leveldir->last_level)
10831 char *level_filename;
10834 level_nr = convert_level_nr++;
10836 Print("Level %03d: ", level_nr);
10838 LoadLevel(level_nr);
10839 if (level.no_level_file || level.no_valid_file)
10841 Print("(no level)\n");
10845 Print("converting level ... ");
10847 level_filename = getDefaultLevelFilename(level_nr);
10848 new_level = !fileExists(level_filename);
10852 SaveLevel(level_nr);
10854 num_levels_converted++;
10856 Print("converted.\n");
10860 if (level_nr >= 0 && level_nr < MAX_NUM_CONVERT_LEVELS)
10861 levels_failed[level_nr] = TRUE;
10863 Print("NOT CONVERTED -- LEVEL ALREADY EXISTS.\n");
10866 num_levels_handled++;
10870 PrintLine("=", 79);
10871 Print("Number of levels handled: %d\n", num_levels_handled);
10872 Print("Number of levels converted: %d (%d%%)\n", num_levels_converted,
10873 (num_levels_handled ?
10874 num_levels_converted * 100 / num_levels_handled : 0));
10875 PrintLine("-", 79);
10876 Print("Summary (for automatic parsing by scripts):\n");
10877 Print("LEVELDIR '%s', CONVERTED %d/%d (%d%%)",
10878 convert_leveldir->identifier, num_levels_converted,
10879 num_levels_handled,
10880 (num_levels_handled ?
10881 num_levels_converted * 100 / num_levels_handled : 0));
10883 if (num_levels_handled != num_levels_converted)
10885 Print(", FAILED:");
10886 for (i = 0; i < MAX_NUM_CONVERT_LEVELS; i++)
10887 if (levels_failed[i])
10892 PrintLine("=", 79);
10894 CloseAllAndExit(0);
10898 /* ------------------------------------------------------------------------- */
10899 /* create and save images for use in level sketches (raw BMP format) */
10900 /* ------------------------------------------------------------------------- */
10902 void CreateLevelSketchImages()
10904 #if defined(TARGET_SDL)
10909 InitElementPropertiesGfxElement();
10911 bitmap1 = CreateBitmap(TILEX, TILEY, DEFAULT_DEPTH);
10912 bitmap2 = CreateBitmap(MINI_TILEX, MINI_TILEY, DEFAULT_DEPTH);
10914 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
10916 Bitmap *src_bitmap;
10918 int element = getMappedElement(i);
10919 int graphic = el2edimg(element);
10920 char basename1[16];
10921 char basename2[16];
10925 sprintf(basename1, "%03d.bmp", i);
10926 sprintf(basename2, "%03ds.bmp", i);
10928 filename1 = getPath2(global.create_images_dir, basename1);
10929 filename2 = getPath2(global.create_images_dir, basename2);
10931 getFixedGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
10932 BlitBitmap(src_bitmap, bitmap1, src_x, src_y, TILEX, TILEY,
10935 if (SDL_SaveBMP(bitmap1->surface, filename1) != 0)
10936 Error(ERR_EXIT, "cannot save level sketch image file '%s'", filename1);
10938 getMiniGraphicSource(graphic, &src_bitmap, &src_x, &src_y);
10939 BlitBitmap(src_bitmap, bitmap2, src_x, src_y, MINI_TILEX, MINI_TILEY, 0, 0);
10941 if (SDL_SaveBMP(bitmap2->surface, filename2) != 0)
10942 Error(ERR_EXIT, "cannot save level sketch image file '%s'", filename2);
10948 printf("%03d `%03d%c", i, i, (i % 10 < 9 ? ' ' : '\n'));
10951 FreeBitmap(bitmap1);
10952 FreeBitmap(bitmap2);
10957 Error(ERR_INFO, "%d normal and small images created", NUM_FILE_ELEMENTS);
10959 CloseAllAndExit(0);
10964 /* ------------------------------------------------------------------------- */
10965 /* create and save images for custom and group elements (raw BMP format) */
10966 /* ------------------------------------------------------------------------- */
10968 void CreateCustomElementImages(char *directory)
10970 #if defined(TARGET_SDL)
10971 char *src_basename = "RocksCE-template.ilbm";
10972 char *dst_basename = "RocksCE.bmp";
10973 char *src_filename = getPath2(directory, src_basename);
10974 char *dst_filename = getPath2(directory, dst_basename);
10975 Bitmap *src_bitmap;
10977 int yoffset_ce = 0;
10978 int yoffset_ge = (TILEY * NUM_CUSTOM_ELEMENTS / 16);
10981 SDLInitVideoDisplay();
10983 ReCreateBitmap(&backbuffer, video.width, video.height);
10985 src_bitmap = LoadImage(src_filename);
10987 bitmap = CreateBitmap(TILEX * 16 * 2,
10988 TILEY * (NUM_CUSTOM_ELEMENTS + NUM_GROUP_ELEMENTS) / 16,
10991 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
10998 BlitBitmap(src_bitmap, bitmap, 0, 0, TILEX, TILEY,
10999 TILEX * x, TILEY * y + yoffset_ce);
11001 BlitBitmap(src_bitmap, bitmap, 0, TILEY,
11003 TILEX * x + TILEX * 16,
11004 TILEY * y + yoffset_ce);
11006 for (j = 2; j >= 0; j--)
11010 BlitBitmap(src_bitmap, bitmap,
11011 TILEX + c * 7, 0, 6, 10,
11012 TILEX * x + 6 + j * 7,
11013 TILEY * y + 11 + yoffset_ce);
11015 BlitBitmap(src_bitmap, bitmap,
11016 TILEX + c * 8, TILEY, 6, 10,
11017 TILEX * 16 + TILEX * x + 6 + j * 8,
11018 TILEY * y + 10 + yoffset_ce);
11024 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
11031 BlitBitmap(src_bitmap, bitmap, 0, 0, TILEX, TILEY,
11032 TILEX * x, TILEY * y + yoffset_ge);
11034 BlitBitmap(src_bitmap, bitmap, 0, TILEY,
11036 TILEX * x + TILEX * 16,
11037 TILEY * y + yoffset_ge);
11039 for (j = 1; j >= 0; j--)
11043 BlitBitmap(src_bitmap, bitmap, TILEX + c * 10, 11, 10, 10,
11044 TILEX * x + 6 + j * 10,
11045 TILEY * y + 11 + yoffset_ge);
11047 BlitBitmap(src_bitmap, bitmap,
11048 TILEX + c * 8, TILEY + 12, 6, 10,
11049 TILEX * 16 + TILEX * x + 10 + j * 8,
11050 TILEY * y + 10 + yoffset_ge);
11056 if (SDL_SaveBMP(bitmap->surface, dst_filename) != 0)
11057 Error(ERR_EXIT, "cannot save CE graphics file '%s'", dst_filename);
11059 FreeBitmap(bitmap);
11061 CloseAllAndExit(0);