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"
25 #define ENABLE_UNUSED_CODE 0 /* currently unused functions */
26 #define ENABLE_HISTORIC_CHUNKS 0 /* only for historic reference */
27 #define ENABLE_RESERVED_CODE 0 /* reserved for later use */
29 #define CHUNK_ID_LEN 4 /* IFF style chunk id length */
30 #define CHUNK_SIZE_UNDEFINED 0 /* undefined chunk size == 0 */
31 #define CHUNK_SIZE_NONE -1 /* do not write chunk size */
33 #define LEVEL_CHUNK_NAME_SIZE MAX_LEVEL_NAME_LEN
34 #define LEVEL_CHUNK_AUTH_SIZE MAX_LEVEL_AUTHOR_LEN
36 #define LEVEL_CHUNK_VERS_SIZE 8 /* size of file version chunk */
37 #define LEVEL_CHUNK_DATE_SIZE 4 /* size of file date chunk */
38 #define LEVEL_CHUNK_HEAD_SIZE 80 /* size of level file header */
39 #define LEVEL_CHUNK_HEAD_UNUSED 0 /* unused level header bytes */
40 #define LEVEL_CHUNK_CNT2_SIZE 160 /* size of level CNT2 chunk */
41 #define LEVEL_CHUNK_CNT2_UNUSED 11 /* unused CNT2 chunk bytes */
42 #define LEVEL_CHUNK_CNT3_HEADER 16 /* size of level CNT3 header */
43 #define LEVEL_CHUNK_CNT3_UNUSED 10 /* unused CNT3 chunk bytes */
44 #define LEVEL_CPART_CUS3_SIZE 134 /* size of CUS3 chunk part */
45 #define LEVEL_CPART_CUS3_UNUSED 15 /* unused CUS3 bytes / part */
46 #define LEVEL_CHUNK_GRP1_SIZE 74 /* size of level GRP1 chunk */
48 /* (element number, number of change pages, change page number) */
49 #define LEVEL_CHUNK_CUSX_UNCHANGED (2 + (1 + 1) + (1 + 1))
51 /* (element number only) */
52 #define LEVEL_CHUNK_GRPX_UNCHANGED 2
53 #define LEVEL_CHUNK_NOTE_UNCHANGED 2
55 /* (nothing at all if unchanged) */
56 #define LEVEL_CHUNK_ELEM_UNCHANGED 0
58 #define TAPE_CHUNK_VERS_SIZE 8 /* size of file version chunk */
59 #define TAPE_CHUNK_HEAD_SIZE 20 /* size of tape file header */
60 #define TAPE_CHUNK_HEAD_UNUSED 2 /* unused tape header bytes */
62 #define LEVEL_CHUNK_CNT3_SIZE(x) (LEVEL_CHUNK_CNT3_HEADER + (x))
63 #define LEVEL_CHUNK_CUS3_SIZE(x) (2 + (x) * LEVEL_CPART_CUS3_SIZE)
64 #define LEVEL_CHUNK_CUS4_SIZE(x) (96 + (x) * 48)
66 /* file identifier strings */
67 #define LEVEL_COOKIE_TMPL "ROCKSNDIAMONDS_LEVEL_FILE_VERSION_x.x"
68 #define TAPE_COOKIE_TMPL "ROCKSNDIAMONDS_TAPE_FILE_VERSION_x.x"
69 #define SCORE_COOKIE "ROCKSNDIAMONDS_SCORE_FILE_VERSION_1.2"
71 /* values for deciding when (not) to save configuration data */
72 #define SAVE_CONF_NEVER 0
73 #define SAVE_CONF_ALWAYS 1
74 #define SAVE_CONF_WHEN_CHANGED -1
76 /* values for chunks using micro chunks */
77 #define CONF_MASK_1_BYTE 0x00
78 #define CONF_MASK_2_BYTE 0x40
79 #define CONF_MASK_4_BYTE 0x80
80 #define CONF_MASK_MULTI_BYTES 0xc0
82 #define CONF_MASK_BYTES 0xc0
83 #define CONF_MASK_TOKEN 0x3f
85 #define CONF_VALUE_1_BYTE(x) (CONF_MASK_1_BYTE | (x))
86 #define CONF_VALUE_2_BYTE(x) (CONF_MASK_2_BYTE | (x))
87 #define CONF_VALUE_4_BYTE(x) (CONF_MASK_4_BYTE | (x))
88 #define CONF_VALUE_MULTI_BYTES(x) (CONF_MASK_MULTI_BYTES | (x))
90 /* these definitions are just for convenience of use and readability */
91 #define CONF_VALUE_8_BIT(x) CONF_VALUE_1_BYTE(x)
92 #define CONF_VALUE_16_BIT(x) CONF_VALUE_2_BYTE(x)
93 #define CONF_VALUE_32_BIT(x) CONF_VALUE_4_BYTE(x)
94 #define CONF_VALUE_BYTES(x) CONF_VALUE_MULTI_BYTES(x)
96 #define CONF_VALUE_NUM_BYTES(x) ((x) == CONF_MASK_1_BYTE ? 1 : \
97 (x) == CONF_MASK_2_BYTE ? 2 : \
98 (x) == CONF_MASK_4_BYTE ? 4 : 0)
100 #define CONF_CONTENT_NUM_ELEMENTS (3 * 3)
101 #define CONF_CONTENT_NUM_BYTES (CONF_CONTENT_NUM_ELEMENTS * 2)
102 #define CONF_ELEMENT_NUM_BYTES (2)
104 #define CONF_ENTITY_NUM_BYTES(t) ((t) == TYPE_ELEMENT || \
105 (t) == TYPE_ELEMENT_LIST ? \
106 CONF_ELEMENT_NUM_BYTES : \
107 (t) == TYPE_CONTENT || \
108 (t) == TYPE_CONTENT_LIST ? \
109 CONF_CONTENT_NUM_BYTES : 1)
111 #define CONF_ELEMENT_BYTE_POS(i) ((i) * CONF_ELEMENT_NUM_BYTES)
112 #define CONF_ELEMENTS_ELEMENT(b,i) ((b[CONF_ELEMENT_BYTE_POS(i)] << 8) | \
113 (b[CONF_ELEMENT_BYTE_POS(i) + 1]))
115 #define CONF_CONTENT_ELEMENT_POS(c,x,y) ((c) * CONF_CONTENT_NUM_ELEMENTS + \
117 #define CONF_CONTENT_BYTE_POS(c,x,y) (CONF_CONTENT_ELEMENT_POS(c,x,y) * \
118 CONF_ELEMENT_NUM_BYTES)
119 #define CONF_CONTENTS_ELEMENT(b,c,x,y) ((b[CONF_CONTENT_BYTE_POS(c,x,y)]<< 8)|\
120 (b[CONF_CONTENT_BYTE_POS(c,x,y) + 1]))
122 /* temporary variables used to store pointers to structure members */
123 static struct LevelInfo li;
124 static struct ElementInfo xx_ei, yy_ei;
125 static struct ElementChangeInfo xx_change;
126 static struct ElementGroupInfo xx_group;
127 static struct EnvelopeInfo xx_envelope;
128 static unsigned int xx_event_bits[NUM_CE_BITFIELDS];
129 static char xx_default_description[MAX_ELEMENT_NAME_LEN + 1];
130 static int xx_num_contents;
131 static int xx_current_change_page;
132 static char xx_default_string_empty[1] = "";
133 static int xx_string_length_unused;
135 struct LevelFileConfigInfo
137 int element; /* element for which data is to be stored */
138 int save_type; /* save data always, never or when changed */
139 int data_type; /* data type (used internally, not stored) */
140 int conf_type; /* micro chunk identifier (stored in file) */
143 void *value; /* variable that holds the data to be stored */
144 int default_value; /* initial default value for this variable */
147 void *value_copy; /* variable that holds the data to be copied */
148 void *num_entities; /* number of entities for multi-byte data */
149 int default_num_entities; /* default number of entities for this data */
150 int max_num_entities; /* maximal number of entities for this data */
151 char *default_string; /* optional default string for string data */
154 static struct LevelFileConfigInfo chunk_config_INFO[] =
156 /* ---------- values not related to single elements ----------------------- */
159 -1, SAVE_CONF_ALWAYS,
160 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
161 &li.game_engine_type, GAME_ENGINE_TYPE_RND
165 -1, SAVE_CONF_ALWAYS,
166 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
167 &li.fieldx, STD_LEV_FIELDX
170 -1, SAVE_CONF_ALWAYS,
171 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
172 &li.fieldy, STD_LEV_FIELDY
176 -1, SAVE_CONF_ALWAYS,
177 TYPE_INTEGER, CONF_VALUE_16_BIT(3),
182 -1, SAVE_CONF_ALWAYS,
183 TYPE_INTEGER, CONF_VALUE_16_BIT(4),
189 TYPE_INTEGER, CONF_VALUE_32_BIT(2),
195 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
196 &li.use_step_counter, FALSE
201 TYPE_BITFIELD, CONF_VALUE_8_BIT(4),
202 &li.wind_direction_initial, MV_NONE
207 TYPE_BOOLEAN, CONF_VALUE_8_BIT(5),
208 &li.em_slippery_gems, FALSE
213 TYPE_BOOLEAN, CONF_VALUE_8_BIT(6),
214 &li.use_custom_template, FALSE
219 TYPE_BITFIELD, CONF_VALUE_32_BIT(1),
220 &li.can_move_into_acid_bits, ~0 /* default: everything can */
225 TYPE_BITFIELD, CONF_VALUE_8_BIT(7),
226 &li.dont_collide_with_bits, ~0 /* default: always deadly */
231 TYPE_BOOLEAN, CONF_VALUE_8_BIT(8),
232 &li.em_explodes_by_fire, FALSE
237 TYPE_INTEGER, CONF_VALUE_16_BIT(5),
238 &li.score[SC_TIME_BONUS], 1
243 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
244 &li.auto_exit_sokoban, FALSE
249 TYPE_BOOLEAN, CONF_VALUE_8_BIT(10),
250 &li.auto_count_gems, FALSE
260 static struct LevelFileConfigInfo chunk_config_ELEM[] =
262 /* (these values are the same for each player) */
265 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
266 &li.block_last_field, FALSE /* default case for EM levels */
270 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
271 &li.sp_block_last_field, TRUE /* default case for SP levels */
275 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
276 &li.instant_relocation, FALSE
280 TYPE_BOOLEAN, CONF_VALUE_8_BIT(4),
281 &li.can_pass_to_walkable, FALSE
285 TYPE_BOOLEAN, CONF_VALUE_8_BIT(5),
286 &li.block_snap_field, TRUE
290 TYPE_BOOLEAN, CONF_VALUE_8_BIT(6),
291 &li.continuous_snapping, TRUE
295 TYPE_BOOLEAN, CONF_VALUE_8_BIT(12),
296 &li.shifted_relocation, FALSE
300 TYPE_BOOLEAN, CONF_VALUE_8_BIT(15),
301 &li.lazy_relocation, FALSE
304 /* (these values are different for each player) */
307 TYPE_INTEGER, CONF_VALUE_8_BIT(7),
308 &li.initial_player_stepsize[0], STEPSIZE_NORMAL
312 TYPE_BOOLEAN, CONF_VALUE_8_BIT(8),
313 &li.initial_player_gravity[0], FALSE
317 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
318 &li.use_start_element[0], FALSE
322 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
323 &li.start_element[0], EL_PLAYER_1
327 TYPE_BOOLEAN, CONF_VALUE_8_BIT(10),
328 &li.use_artwork_element[0], FALSE
332 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
333 &li.artwork_element[0], EL_PLAYER_1
337 TYPE_BOOLEAN, CONF_VALUE_8_BIT(11),
338 &li.use_explosion_element[0], FALSE
342 TYPE_ELEMENT, CONF_VALUE_16_BIT(3),
343 &li.explosion_element[0], EL_PLAYER_1
347 TYPE_BOOLEAN, CONF_VALUE_8_BIT(13),
348 &li.use_initial_inventory[0], FALSE
352 TYPE_BOOLEAN, CONF_VALUE_8_BIT(14),
353 &li.initial_inventory_size[0], 1
357 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(1),
358 &li.initial_inventory_content[0][0],EL_EMPTY, NULL,
359 &li.initial_inventory_size[0], 1, MAX_INITIAL_INVENTORY_SIZE
364 TYPE_INTEGER, CONF_VALUE_8_BIT(7),
365 &li.initial_player_stepsize[1], STEPSIZE_NORMAL
369 TYPE_BOOLEAN, CONF_VALUE_8_BIT(8),
370 &li.initial_player_gravity[1], FALSE
374 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
375 &li.use_start_element[1], FALSE
379 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
380 &li.start_element[1], EL_PLAYER_2
384 TYPE_BOOLEAN, CONF_VALUE_8_BIT(10),
385 &li.use_artwork_element[1], FALSE
389 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
390 &li.artwork_element[1], EL_PLAYER_2
394 TYPE_BOOLEAN, CONF_VALUE_8_BIT(11),
395 &li.use_explosion_element[1], FALSE
399 TYPE_ELEMENT, CONF_VALUE_16_BIT(3),
400 &li.explosion_element[1], EL_PLAYER_2
404 TYPE_BOOLEAN, CONF_VALUE_8_BIT(13),
405 &li.use_initial_inventory[1], FALSE
409 TYPE_BOOLEAN, CONF_VALUE_8_BIT(14),
410 &li.initial_inventory_size[1], 1
414 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(1),
415 &li.initial_inventory_content[1][0],EL_EMPTY, NULL,
416 &li.initial_inventory_size[1], 1, MAX_INITIAL_INVENTORY_SIZE
421 TYPE_INTEGER, CONF_VALUE_8_BIT(7),
422 &li.initial_player_stepsize[2], STEPSIZE_NORMAL
426 TYPE_BOOLEAN, CONF_VALUE_8_BIT(8),
427 &li.initial_player_gravity[2], FALSE
431 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
432 &li.use_start_element[2], FALSE
436 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
437 &li.start_element[2], EL_PLAYER_3
441 TYPE_BOOLEAN, CONF_VALUE_8_BIT(10),
442 &li.use_artwork_element[2], FALSE
446 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
447 &li.artwork_element[2], EL_PLAYER_3
451 TYPE_BOOLEAN, CONF_VALUE_8_BIT(11),
452 &li.use_explosion_element[2], FALSE
456 TYPE_ELEMENT, CONF_VALUE_16_BIT(3),
457 &li.explosion_element[2], EL_PLAYER_3
461 TYPE_BOOLEAN, CONF_VALUE_8_BIT(13),
462 &li.use_initial_inventory[2], FALSE
466 TYPE_BOOLEAN, CONF_VALUE_8_BIT(14),
467 &li.initial_inventory_size[2], 1
471 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(1),
472 &li.initial_inventory_content[2][0],EL_EMPTY, NULL,
473 &li.initial_inventory_size[2], 1, MAX_INITIAL_INVENTORY_SIZE
478 TYPE_INTEGER, CONF_VALUE_8_BIT(7),
479 &li.initial_player_stepsize[3], STEPSIZE_NORMAL
483 TYPE_BOOLEAN, CONF_VALUE_8_BIT(8),
484 &li.initial_player_gravity[3], FALSE
488 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
489 &li.use_start_element[3], FALSE
493 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
494 &li.start_element[3], EL_PLAYER_4
498 TYPE_BOOLEAN, CONF_VALUE_8_BIT(10),
499 &li.use_artwork_element[3], FALSE
503 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
504 &li.artwork_element[3], EL_PLAYER_4
508 TYPE_BOOLEAN, CONF_VALUE_8_BIT(11),
509 &li.use_explosion_element[3], FALSE
513 TYPE_ELEMENT, CONF_VALUE_16_BIT(3),
514 &li.explosion_element[3], EL_PLAYER_4
518 TYPE_BOOLEAN, CONF_VALUE_8_BIT(13),
519 &li.use_initial_inventory[3], FALSE
523 TYPE_BOOLEAN, CONF_VALUE_8_BIT(14),
524 &li.initial_inventory_size[3], 1
528 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(1),
529 &li.initial_inventory_content[3][0],EL_EMPTY, NULL,
530 &li.initial_inventory_size[3], 1, MAX_INITIAL_INVENTORY_SIZE
535 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
536 &li.score[SC_EMERALD], 10
541 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
542 &li.score[SC_DIAMOND], 10
547 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
548 &li.score[SC_BUG], 10
553 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
554 &li.score[SC_SPACESHIP], 10
559 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
560 &li.score[SC_PACMAN], 10
565 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
566 &li.score[SC_NUT], 10
571 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
572 &li.score[SC_DYNAMITE], 10
577 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
578 &li.score[SC_KEY], 10
583 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
584 &li.score[SC_PEARL], 10
589 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
590 &li.score[SC_CRYSTAL], 10
595 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
596 &li.amoeba_content, EL_DIAMOND
600 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
605 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
606 &li.grow_into_diggable, TRUE
611 TYPE_CONTENT_LIST, CONF_VALUE_BYTES(1),
612 &li.yamyam_content, EL_ROCK, NULL,
613 &li.num_yamyam_contents, 4, MAX_ELEMENT_CONTENTS
617 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
618 &li.score[SC_YAMYAM], 10
623 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
624 &li.score[SC_ROBOT], 10
628 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
634 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
640 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
641 &li.time_magic_wall, 10
646 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
647 &li.game_of_life[0], 2
651 TYPE_INTEGER, CONF_VALUE_8_BIT(2),
652 &li.game_of_life[1], 3
656 TYPE_INTEGER, CONF_VALUE_8_BIT(3),
657 &li.game_of_life[2], 3
661 TYPE_INTEGER, CONF_VALUE_8_BIT(4),
662 &li.game_of_life[3], 3
667 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
672 TYPE_INTEGER, CONF_VALUE_8_BIT(2),
677 TYPE_INTEGER, CONF_VALUE_8_BIT(3),
682 TYPE_INTEGER, CONF_VALUE_8_BIT(4),
687 EL_TIMEGATE_SWITCH, -1,
688 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
689 &li.time_timegate, 10
693 EL_LIGHT_SWITCH_ACTIVE, -1,
694 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
699 EL_SHIELD_NORMAL, -1,
700 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
701 &li.shield_normal_time, 10
704 EL_SHIELD_NORMAL, -1,
705 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
706 &li.score[SC_SHIELD], 10
710 EL_SHIELD_DEADLY, -1,
711 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
712 &li.shield_deadly_time, 10
715 EL_SHIELD_DEADLY, -1,
716 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
717 &li.score[SC_SHIELD], 10
722 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
727 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
728 &li.extra_time_score, 10
732 EL_TIME_ORB_FULL, -1,
733 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
734 &li.time_orb_time, 10
737 EL_TIME_ORB_FULL, -1,
738 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
739 &li.use_time_orb_bug, FALSE
744 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
745 &li.use_spring_bug, FALSE
750 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
751 &li.android_move_time, 10
755 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
756 &li.android_clone_time, 10
760 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(1),
761 &li.android_clone_element[0], EL_EMPTY, NULL,
762 &li.num_android_clone_elements, 1, MAX_ANDROID_ELEMENTS
767 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
772 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
777 EL_EMC_MAGNIFIER, -1,
778 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
779 &li.magnify_score, 10
782 EL_EMC_MAGNIFIER, -1,
783 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
788 EL_EMC_MAGIC_BALL, -1,
789 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
793 EL_EMC_MAGIC_BALL, -1,
794 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
795 &li.ball_random, FALSE
798 EL_EMC_MAGIC_BALL, -1,
799 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
800 &li.ball_state_initial, FALSE
803 EL_EMC_MAGIC_BALL, -1,
804 TYPE_CONTENT_LIST, CONF_VALUE_BYTES(1),
805 &li.ball_content, EL_EMPTY, NULL,
806 &li.num_ball_contents, 4, MAX_ELEMENT_CONTENTS
811 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
812 &li.mm_laser_red, FALSE
816 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
817 &li.mm_laser_green, FALSE
821 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
822 &li.mm_laser_blue, TRUE
827 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
828 &li.df_laser_red, TRUE
832 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
833 &li.df_laser_green, TRUE
837 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
838 &li.df_laser_blue, FALSE
843 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
848 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
849 &li.score[SC_ELEM_BONUS], 10
852 /* ---------- unused values ----------------------------------------------- */
855 EL_UNKNOWN, SAVE_CONF_NEVER,
856 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
857 &li.score[SC_UNKNOWN_15], 10
867 static struct LevelFileConfigInfo chunk_config_NOTE[] =
871 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
872 &xx_envelope.xsize, MAX_ENVELOPE_XSIZE,
876 TYPE_INTEGER, CONF_VALUE_8_BIT(2),
877 &xx_envelope.ysize, MAX_ENVELOPE_YSIZE,
882 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
883 &xx_envelope.autowrap, FALSE
887 TYPE_BOOLEAN, CONF_VALUE_8_BIT(4),
888 &xx_envelope.centered, FALSE
893 TYPE_STRING, CONF_VALUE_BYTES(1),
894 &xx_envelope.text, -1, NULL,
895 &xx_string_length_unused, -1, MAX_ENVELOPE_TEXT_LEN,
896 &xx_default_string_empty[0]
906 static struct LevelFileConfigInfo chunk_config_CUSX_base[] =
910 TYPE_STRING, CONF_VALUE_BYTES(1),
911 &xx_ei.description[0], -1,
912 &yy_ei.description[0],
913 &xx_string_length_unused, -1, MAX_ELEMENT_NAME_LEN,
914 &xx_default_description[0]
919 TYPE_BITFIELD, CONF_VALUE_32_BIT(1),
920 &xx_ei.properties[EP_BITFIELD_BASE_NR], EP_BITMASK_BASE_DEFAULT,
921 &yy_ei.properties[EP_BITFIELD_BASE_NR]
923 #if ENABLE_RESERVED_CODE
924 /* (reserved for later use) */
927 TYPE_BITFIELD, CONF_VALUE_32_BIT(2),
928 &xx_ei.properties[EP_BITFIELD_BASE_NR + 1], EP_BITMASK_DEFAULT,
929 &yy_ei.properties[EP_BITFIELD_BASE_NR + 1]
935 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
936 &xx_ei.use_gfx_element, FALSE,
937 &yy_ei.use_gfx_element
941 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
942 &xx_ei.gfx_element_initial, EL_EMPTY_SPACE,
943 &yy_ei.gfx_element_initial
948 TYPE_BITFIELD, CONF_VALUE_8_BIT(2),
949 &xx_ei.access_direction, MV_ALL_DIRECTIONS,
950 &yy_ei.access_direction
955 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
956 &xx_ei.collect_score_initial, 10,
957 &yy_ei.collect_score_initial
961 TYPE_INTEGER, CONF_VALUE_16_BIT(3),
962 &xx_ei.collect_count_initial, 1,
963 &yy_ei.collect_count_initial
968 TYPE_INTEGER, CONF_VALUE_16_BIT(4),
969 &xx_ei.ce_value_fixed_initial, 0,
970 &yy_ei.ce_value_fixed_initial
974 TYPE_INTEGER, CONF_VALUE_16_BIT(5),
975 &xx_ei.ce_value_random_initial, 0,
976 &yy_ei.ce_value_random_initial
980 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
981 &xx_ei.use_last_ce_value, FALSE,
982 &yy_ei.use_last_ce_value
987 TYPE_INTEGER, CONF_VALUE_16_BIT(6),
988 &xx_ei.push_delay_fixed, 8,
989 &yy_ei.push_delay_fixed
993 TYPE_INTEGER, CONF_VALUE_16_BIT(7),
994 &xx_ei.push_delay_random, 8,
995 &yy_ei.push_delay_random
999 TYPE_INTEGER, CONF_VALUE_16_BIT(8),
1000 &xx_ei.drop_delay_fixed, 0,
1001 &yy_ei.drop_delay_fixed
1005 TYPE_INTEGER, CONF_VALUE_16_BIT(9),
1006 &xx_ei.drop_delay_random, 0,
1007 &yy_ei.drop_delay_random
1011 TYPE_INTEGER, CONF_VALUE_16_BIT(10),
1012 &xx_ei.move_delay_fixed, 0,
1013 &yy_ei.move_delay_fixed
1017 TYPE_INTEGER, CONF_VALUE_16_BIT(11),
1018 &xx_ei.move_delay_random, 0,
1019 &yy_ei.move_delay_random
1024 TYPE_BITFIELD, CONF_VALUE_32_BIT(3),
1025 &xx_ei.move_pattern, MV_ALL_DIRECTIONS,
1030 TYPE_BITFIELD, CONF_VALUE_8_BIT(4),
1031 &xx_ei.move_direction_initial, MV_START_AUTOMATIC,
1032 &yy_ei.move_direction_initial
1036 TYPE_INTEGER, CONF_VALUE_8_BIT(5),
1037 &xx_ei.move_stepsize, TILEX / 8,
1038 &yy_ei.move_stepsize
1043 TYPE_ELEMENT, CONF_VALUE_16_BIT(12),
1044 &xx_ei.move_enter_element, EL_EMPTY_SPACE,
1045 &yy_ei.move_enter_element
1049 TYPE_ELEMENT, CONF_VALUE_16_BIT(13),
1050 &xx_ei.move_leave_element, EL_EMPTY_SPACE,
1051 &yy_ei.move_leave_element
1055 TYPE_INTEGER, CONF_VALUE_8_BIT(6),
1056 &xx_ei.move_leave_type, LEAVE_TYPE_UNLIMITED,
1057 &yy_ei.move_leave_type
1062 TYPE_INTEGER, CONF_VALUE_8_BIT(7),
1063 &xx_ei.slippery_type, SLIPPERY_ANY_RANDOM,
1064 &yy_ei.slippery_type
1069 TYPE_INTEGER, CONF_VALUE_8_BIT(8),
1070 &xx_ei.explosion_type, EXPLODES_3X3,
1071 &yy_ei.explosion_type
1075 TYPE_INTEGER, CONF_VALUE_16_BIT(14),
1076 &xx_ei.explosion_delay, 16,
1077 &yy_ei.explosion_delay
1081 TYPE_INTEGER, CONF_VALUE_16_BIT(15),
1082 &xx_ei.ignition_delay, 8,
1083 &yy_ei.ignition_delay
1088 TYPE_CONTENT_LIST, CONF_VALUE_BYTES(2),
1089 &xx_ei.content, EL_EMPTY_SPACE,
1091 &xx_num_contents, 1, 1
1094 /* ---------- "num_change_pages" must be the last entry ------------------- */
1097 -1, SAVE_CONF_ALWAYS,
1098 TYPE_INTEGER, CONF_VALUE_8_BIT(9),
1099 &xx_ei.num_change_pages, 1,
1100 &yy_ei.num_change_pages
1111 static struct LevelFileConfigInfo chunk_config_CUSX_change[] =
1113 /* ---------- "current_change_page" must be the first entry --------------- */
1116 -1, SAVE_CONF_ALWAYS,
1117 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
1118 &xx_current_change_page, -1
1121 /* ---------- (the remaining entries can be in any order) ----------------- */
1125 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
1126 &xx_change.can_change, FALSE
1131 TYPE_BITFIELD, CONF_VALUE_32_BIT(1),
1132 &xx_event_bits[0], 0
1136 TYPE_BITFIELD, CONF_VALUE_32_BIT(2),
1137 &xx_event_bits[1], 0
1142 TYPE_BITFIELD, CONF_VALUE_8_BIT(3),
1143 &xx_change.trigger_player, CH_PLAYER_ANY
1147 TYPE_BITFIELD, CONF_VALUE_8_BIT(4),
1148 &xx_change.trigger_side, CH_SIDE_ANY
1152 TYPE_BITFIELD, CONF_VALUE_32_BIT(3),
1153 &xx_change.trigger_page, CH_PAGE_ANY
1158 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
1159 &xx_change.target_element, EL_EMPTY_SPACE
1164 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
1165 &xx_change.delay_fixed, 0
1169 TYPE_INTEGER, CONF_VALUE_16_BIT(3),
1170 &xx_change.delay_random, 0
1174 TYPE_INTEGER, CONF_VALUE_16_BIT(4),
1175 &xx_change.delay_frames, FRAMES_PER_SECOND
1180 TYPE_ELEMENT, CONF_VALUE_16_BIT(5),
1181 &xx_change.initial_trigger_element, EL_EMPTY_SPACE
1186 TYPE_BOOLEAN, CONF_VALUE_8_BIT(6),
1187 &xx_change.explode, FALSE
1191 TYPE_BOOLEAN, CONF_VALUE_8_BIT(7),
1192 &xx_change.use_target_content, FALSE
1196 TYPE_BOOLEAN, CONF_VALUE_8_BIT(8),
1197 &xx_change.only_if_complete, FALSE
1201 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
1202 &xx_change.use_random_replace, FALSE
1206 TYPE_INTEGER, CONF_VALUE_8_BIT(10),
1207 &xx_change.random_percentage, 100
1211 TYPE_INTEGER, CONF_VALUE_8_BIT(11),
1212 &xx_change.replace_when, CP_WHEN_EMPTY
1217 TYPE_BOOLEAN, CONF_VALUE_8_BIT(12),
1218 &xx_change.has_action, FALSE
1222 TYPE_INTEGER, CONF_VALUE_8_BIT(13),
1223 &xx_change.action_type, CA_NO_ACTION
1227 TYPE_INTEGER, CONF_VALUE_8_BIT(14),
1228 &xx_change.action_mode, CA_MODE_UNDEFINED
1232 TYPE_INTEGER, CONF_VALUE_16_BIT(6),
1233 &xx_change.action_arg, CA_ARG_UNDEFINED
1238 TYPE_ELEMENT, CONF_VALUE_16_BIT(7),
1239 &xx_change.action_element, EL_EMPTY_SPACE
1244 TYPE_CONTENT_LIST, CONF_VALUE_BYTES(1),
1245 &xx_change.target_content, EL_EMPTY_SPACE, NULL,
1246 &xx_num_contents, 1, 1
1256 static struct LevelFileConfigInfo chunk_config_GRPX[] =
1260 TYPE_STRING, CONF_VALUE_BYTES(1),
1261 &xx_ei.description[0], -1, NULL,
1262 &xx_string_length_unused, -1, MAX_ELEMENT_NAME_LEN,
1263 &xx_default_description[0]
1268 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1269 &xx_ei.use_gfx_element, FALSE
1273 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
1274 &xx_ei.gfx_element_initial, EL_EMPTY_SPACE
1279 TYPE_INTEGER, CONF_VALUE_8_BIT(2),
1280 &xx_group.choice_mode, ANIM_RANDOM
1285 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(2),
1286 &xx_group.element[0], EL_EMPTY_SPACE, NULL,
1287 &xx_group.num_elements, 1, MAX_ELEMENTS_IN_GROUP
1297 static struct LevelFileConfigInfo chunk_config_CONF[] = /* (OBSOLETE) */
1301 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
1302 &li.block_snap_field, TRUE
1306 TYPE_BOOLEAN, CONF_VALUE_8_BIT(13),
1307 &li.continuous_snapping, TRUE
1311 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
1312 &li.initial_player_stepsize[0], STEPSIZE_NORMAL
1316 TYPE_BOOLEAN, CONF_VALUE_8_BIT(10),
1317 &li.use_start_element[0], FALSE
1321 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
1322 &li.start_element[0], EL_PLAYER_1
1326 TYPE_BOOLEAN, CONF_VALUE_8_BIT(11),
1327 &li.use_artwork_element[0], FALSE
1331 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
1332 &li.artwork_element[0], EL_PLAYER_1
1336 TYPE_BOOLEAN, CONF_VALUE_8_BIT(12),
1337 &li.use_explosion_element[0], FALSE
1341 TYPE_ELEMENT, CONF_VALUE_16_BIT(3),
1342 &li.explosion_element[0], EL_PLAYER_1
1357 filetype_id_list[] =
1359 { LEVEL_FILE_TYPE_RND, "RND" },
1360 { LEVEL_FILE_TYPE_BD, "BD" },
1361 { LEVEL_FILE_TYPE_EM, "EM" },
1362 { LEVEL_FILE_TYPE_SP, "SP" },
1363 { LEVEL_FILE_TYPE_DX, "DX" },
1364 { LEVEL_FILE_TYPE_SB, "SB" },
1365 { LEVEL_FILE_TYPE_DC, "DC" },
1366 { LEVEL_FILE_TYPE_MM, "MM" },
1367 { LEVEL_FILE_TYPE_MM, "DF" },
1372 /* ========================================================================= */
1373 /* level file functions */
1374 /* ========================================================================= */
1376 static boolean check_special_flags(char *flag)
1378 if (strEqual(options.special_flags, flag) ||
1379 strEqual(leveldir_current->special_flags, flag))
1385 static struct DateInfo getCurrentDate()
1387 time_t epoch_seconds = time(NULL);
1388 struct tm *now = localtime(&epoch_seconds);
1389 struct DateInfo date;
1391 date.year = now->tm_year + 1900;
1392 date.month = now->tm_mon + 1;
1393 date.day = now->tm_mday;
1395 date.src = DATE_SRC_CLOCK;
1400 static void resetEventFlags(struct ElementChangeInfo *change)
1404 for (i = 0; i < NUM_CHANGE_EVENTS; i++)
1405 change->has_event[i] = FALSE;
1408 static void resetEventBits()
1412 for (i = 0; i < NUM_CE_BITFIELDS; i++)
1413 xx_event_bits[i] = 0;
1416 static void setEventFlagsFromEventBits(struct ElementChangeInfo *change)
1420 /* important: only change event flag if corresponding event bit is set
1421 (this is because all xx_event_bits[] values are loaded separately,
1422 and all xx_event_bits[] values are set back to zero before loading
1423 another value xx_event_bits[x] (each value representing 32 flags)) */
1425 for (i = 0; i < NUM_CHANGE_EVENTS; i++)
1426 if (xx_event_bits[CH_EVENT_BITFIELD_NR(i)] & CH_EVENT_BIT(i))
1427 change->has_event[i] = TRUE;
1430 static void setEventBitsFromEventFlags(struct ElementChangeInfo *change)
1434 /* in contrast to the above function setEventFlagsFromEventBits(), it
1435 would also be possible to set all bits in xx_event_bits[] to 0 or 1
1436 depending on the corresponding change->has_event[i] values here, as
1437 all xx_event_bits[] values are reset in resetEventBits() before */
1439 for (i = 0; i < NUM_CHANGE_EVENTS; i++)
1440 if (change->has_event[i])
1441 xx_event_bits[CH_EVENT_BITFIELD_NR(i)] |= CH_EVENT_BIT(i);
1444 static char *getDefaultElementDescription(struct ElementInfo *ei)
1446 static char description[MAX_ELEMENT_NAME_LEN + 1];
1447 char *default_description = (ei->custom_description != NULL ?
1448 ei->custom_description :
1449 ei->editor_description);
1452 /* always start with reliable default values */
1453 for (i = 0; i < MAX_ELEMENT_NAME_LEN + 1; i++)
1454 description[i] = '\0';
1456 /* truncate element description to MAX_ELEMENT_NAME_LEN bytes */
1457 strncpy(description, default_description, MAX_ELEMENT_NAME_LEN);
1459 return &description[0];
1462 static void setElementDescriptionToDefault(struct ElementInfo *ei)
1464 char *default_description = getDefaultElementDescription(ei);
1467 for (i = 0; i < MAX_ELEMENT_NAME_LEN + 1; i++)
1468 ei->description[i] = default_description[i];
1471 static void setConfigToDefaultsFromConfigList(struct LevelFileConfigInfo *conf)
1475 for (i = 0; conf[i].data_type != -1; i++)
1477 int default_value = conf[i].default_value;
1478 int data_type = conf[i].data_type;
1479 int conf_type = conf[i].conf_type;
1480 int byte_mask = conf_type & CONF_MASK_BYTES;
1482 if (byte_mask == CONF_MASK_MULTI_BYTES)
1484 int default_num_entities = conf[i].default_num_entities;
1485 int max_num_entities = conf[i].max_num_entities;
1487 *(int *)(conf[i].num_entities) = default_num_entities;
1489 if (data_type == TYPE_STRING)
1491 char *default_string = conf[i].default_string;
1492 char *string = (char *)(conf[i].value);
1494 strncpy(string, default_string, max_num_entities);
1496 else if (data_type == TYPE_ELEMENT_LIST)
1498 int *element_array = (int *)(conf[i].value);
1501 for (j = 0; j < max_num_entities; j++)
1502 element_array[j] = default_value;
1504 else if (data_type == TYPE_CONTENT_LIST)
1506 struct Content *content = (struct Content *)(conf[i].value);
1509 for (c = 0; c < max_num_entities; c++)
1510 for (y = 0; y < 3; y++)
1511 for (x = 0; x < 3; x++)
1512 content[c].e[x][y] = default_value;
1515 else /* constant size configuration data (1, 2 or 4 bytes) */
1517 if (data_type == TYPE_BOOLEAN)
1518 *(boolean *)(conf[i].value) = default_value;
1520 *(int *) (conf[i].value) = default_value;
1525 static void copyConfigFromConfigList(struct LevelFileConfigInfo *conf)
1529 for (i = 0; conf[i].data_type != -1; i++)
1531 int data_type = conf[i].data_type;
1532 int conf_type = conf[i].conf_type;
1533 int byte_mask = conf_type & CONF_MASK_BYTES;
1535 if (byte_mask == CONF_MASK_MULTI_BYTES)
1537 int max_num_entities = conf[i].max_num_entities;
1539 if (data_type == TYPE_STRING)
1541 char *string = (char *)(conf[i].value);
1542 char *string_copy = (char *)(conf[i].value_copy);
1544 strncpy(string_copy, string, max_num_entities);
1546 else if (data_type == TYPE_ELEMENT_LIST)
1548 int *element_array = (int *)(conf[i].value);
1549 int *element_array_copy = (int *)(conf[i].value_copy);
1552 for (j = 0; j < max_num_entities; j++)
1553 element_array_copy[j] = element_array[j];
1555 else if (data_type == TYPE_CONTENT_LIST)
1557 struct Content *content = (struct Content *)(conf[i].value);
1558 struct Content *content_copy = (struct Content *)(conf[i].value_copy);
1561 for (c = 0; c < max_num_entities; c++)
1562 for (y = 0; y < 3; y++)
1563 for (x = 0; x < 3; x++)
1564 content_copy[c].e[x][y] = content[c].e[x][y];
1567 else /* constant size configuration data (1, 2 or 4 bytes) */
1569 if (data_type == TYPE_BOOLEAN)
1570 *(boolean *)(conf[i].value_copy) = *(boolean *)(conf[i].value);
1572 *(int *) (conf[i].value_copy) = *(int *) (conf[i].value);
1577 void copyElementInfo(struct ElementInfo *ei_from, struct ElementInfo *ei_to)
1581 xx_ei = *ei_from; /* copy element data into temporary buffer */
1582 yy_ei = *ei_to; /* copy element data into temporary buffer */
1584 copyConfigFromConfigList(chunk_config_CUSX_base);
1589 /* ---------- reinitialize and copy change pages ---------- */
1591 ei_to->num_change_pages = ei_from->num_change_pages;
1592 ei_to->current_change_page = ei_from->current_change_page;
1594 setElementChangePages(ei_to, ei_to->num_change_pages);
1596 for (i = 0; i < ei_to->num_change_pages; i++)
1597 ei_to->change_page[i] = ei_from->change_page[i];
1599 /* ---------- copy group element info ---------- */
1600 if (ei_from->group != NULL && ei_to->group != NULL) /* group or internal */
1601 *ei_to->group = *ei_from->group;
1603 /* mark this custom element as modified */
1604 ei_to->modified_settings = TRUE;
1607 void setElementChangePages(struct ElementInfo *ei, int change_pages)
1609 int change_page_size = sizeof(struct ElementChangeInfo);
1611 ei->num_change_pages = MAX(1, change_pages);
1614 checked_realloc(ei->change_page, ei->num_change_pages * change_page_size);
1616 if (ei->current_change_page >= ei->num_change_pages)
1617 ei->current_change_page = ei->num_change_pages - 1;
1619 ei->change = &ei->change_page[ei->current_change_page];
1622 void setElementChangeInfoToDefaults(struct ElementChangeInfo *change)
1624 xx_change = *change; /* copy change data into temporary buffer */
1626 setConfigToDefaultsFromConfigList(chunk_config_CUSX_change);
1628 *change = xx_change;
1630 resetEventFlags(change);
1632 change->direct_action = 0;
1633 change->other_action = 0;
1635 change->pre_change_function = NULL;
1636 change->change_function = NULL;
1637 change->post_change_function = NULL;
1640 static void setLevelInfoToDefaults_Level(struct LevelInfo *level)
1644 li = *level; /* copy level data into temporary buffer */
1645 setConfigToDefaultsFromConfigList(chunk_config_INFO);
1646 *level = li; /* copy temporary buffer back to level data */
1648 setLevelInfoToDefaults_EM();
1649 setLevelInfoToDefaults_SP();
1650 setLevelInfoToDefaults_MM();
1652 level->native_em_level = &native_em_level;
1653 level->native_sp_level = &native_sp_level;
1654 level->native_mm_level = &native_mm_level;
1656 level->file_version = FILE_VERSION_ACTUAL;
1657 level->game_version = GAME_VERSION_ACTUAL;
1659 level->creation_date = getCurrentDate();
1661 level->encoding_16bit_field = TRUE;
1662 level->encoding_16bit_yamyam = TRUE;
1663 level->encoding_16bit_amoeba = TRUE;
1665 /* clear level name and level author string buffers */
1666 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
1667 level->name[i] = '\0';
1668 for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
1669 level->author[i] = '\0';
1671 /* set level name and level author to default values */
1672 strcpy(level->name, NAMELESS_LEVEL_NAME);
1673 strcpy(level->author, ANONYMOUS_NAME);
1675 /* set level playfield to playable default level with player and exit */
1676 for (x = 0; x < MAX_LEV_FIELDX; x++)
1677 for (y = 0; y < MAX_LEV_FIELDY; y++)
1678 level->field[x][y] = EL_SAND;
1680 level->field[0][0] = EL_PLAYER_1;
1681 level->field[STD_LEV_FIELDX - 1][STD_LEV_FIELDY - 1] = EL_EXIT_CLOSED;
1683 BorderElement = EL_STEELWALL;
1685 /* detect custom elements when loading them */
1686 level->file_has_custom_elements = FALSE;
1688 /* set all bug compatibility flags to "false" => do not emulate this bug */
1689 level->use_action_after_change_bug = FALSE;
1691 if (leveldir_current)
1693 /* try to determine better author name than 'anonymous' */
1694 if (!strEqual(leveldir_current->author, ANONYMOUS_NAME))
1696 strncpy(level->author, leveldir_current->author, MAX_LEVEL_AUTHOR_LEN);
1697 level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
1701 switch (LEVELCLASS(leveldir_current))
1703 case LEVELCLASS_TUTORIAL:
1704 strcpy(level->author, PROGRAM_AUTHOR_STRING);
1707 case LEVELCLASS_CONTRIB:
1708 strncpy(level->author, leveldir_current->name, MAX_LEVEL_AUTHOR_LEN);
1709 level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
1712 case LEVELCLASS_PRIVATE:
1713 strncpy(level->author, getRealName(), MAX_LEVEL_AUTHOR_LEN);
1714 level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
1718 /* keep default value */
1725 static void setLevelInfoToDefaults_Elements(struct LevelInfo *level)
1727 static boolean clipboard_elements_initialized = FALSE;
1730 InitElementPropertiesStatic();
1732 li = *level; /* copy level data into temporary buffer */
1733 setConfigToDefaultsFromConfigList(chunk_config_ELEM);
1734 *level = li; /* copy temporary buffer back to level data */
1736 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1739 struct ElementInfo *ei = &element_info[element];
1741 /* never initialize clipboard elements after the very first time */
1742 /* (to be able to use clipboard elements between several levels) */
1743 if (IS_CLIPBOARD_ELEMENT(element) && clipboard_elements_initialized)
1746 if (IS_ENVELOPE(element))
1748 int envelope_nr = element - EL_ENVELOPE_1;
1750 setConfigToDefaultsFromConfigList(chunk_config_NOTE);
1752 level->envelope[envelope_nr] = xx_envelope;
1755 if (IS_CUSTOM_ELEMENT(element) ||
1756 IS_GROUP_ELEMENT(element) ||
1757 IS_INTERNAL_ELEMENT(element))
1759 xx_ei = *ei; /* copy element data into temporary buffer */
1761 setConfigToDefaultsFromConfigList(chunk_config_CUSX_base);
1766 setElementChangePages(ei, 1);
1767 setElementChangeInfoToDefaults(ei->change);
1769 if (IS_CUSTOM_ELEMENT(element) ||
1770 IS_GROUP_ELEMENT(element) ||
1771 IS_INTERNAL_ELEMENT(element))
1773 setElementDescriptionToDefault(ei);
1775 ei->modified_settings = FALSE;
1778 if (IS_CUSTOM_ELEMENT(element) ||
1779 IS_INTERNAL_ELEMENT(element))
1781 /* internal values used in level editor */
1783 ei->access_type = 0;
1784 ei->access_layer = 0;
1785 ei->access_protected = 0;
1786 ei->walk_to_action = 0;
1787 ei->smash_targets = 0;
1790 ei->can_explode_by_fire = FALSE;
1791 ei->can_explode_smashed = FALSE;
1792 ei->can_explode_impact = FALSE;
1794 ei->current_change_page = 0;
1797 if (IS_GROUP_ELEMENT(element) ||
1798 IS_INTERNAL_ELEMENT(element))
1800 struct ElementGroupInfo *group;
1802 /* initialize memory for list of elements in group */
1803 if (ei->group == NULL)
1804 ei->group = checked_malloc(sizeof(struct ElementGroupInfo));
1808 xx_group = *group; /* copy group data into temporary buffer */
1810 setConfigToDefaultsFromConfigList(chunk_config_GRPX);
1816 clipboard_elements_initialized = TRUE;
1819 static void setLevelInfoToDefaults(struct LevelInfo *level,
1820 boolean level_info_only,
1821 boolean reset_file_status)
1823 setLevelInfoToDefaults_Level(level);
1825 if (!level_info_only)
1826 setLevelInfoToDefaults_Elements(level);
1828 if (reset_file_status)
1830 level->no_valid_file = FALSE;
1831 level->no_level_file = FALSE;
1834 level->changed = FALSE;
1837 static void setFileInfoToDefaults(struct LevelFileInfo *level_file_info)
1839 level_file_info->nr = 0;
1840 level_file_info->type = LEVEL_FILE_TYPE_UNKNOWN;
1841 level_file_info->packed = FALSE;
1842 level_file_info->basename = NULL;
1843 level_file_info->filename = NULL;
1846 int getMappedElement_SB(int, boolean);
1848 static void ActivateLevelTemplate()
1852 if (check_special_flags("load_xsb_to_ces"))
1854 /* fill smaller playfields with padding "beyond border wall" elements */
1855 if (level.fieldx < level_template.fieldx ||
1856 level.fieldy < level_template.fieldy)
1858 short field[level.fieldx][level.fieldy];
1859 int new_fieldx = MAX(level.fieldx, level_template.fieldx);
1860 int new_fieldy = MAX(level.fieldy, level_template.fieldy);
1861 int pos_fieldx = (new_fieldx - level.fieldx) / 2;
1862 int pos_fieldy = (new_fieldy - level.fieldy) / 2;
1864 /* copy old playfield (which is smaller than the visible area) */
1865 for (y = 0; y < level.fieldy; y++) for (x = 0; x < level.fieldx; x++)
1866 field[x][y] = level.field[x][y];
1868 /* fill new, larger playfield with "beyond border wall" elements */
1869 for (y = 0; y < new_fieldy; y++) for (x = 0; x < new_fieldx; x++)
1870 level.field[x][y] = getMappedElement_SB('_', TRUE);
1872 /* copy the old playfield to the middle of the new playfield */
1873 for (y = 0; y < level.fieldy; y++) for (x = 0; x < level.fieldx; x++)
1874 level.field[pos_fieldx + x][pos_fieldy + y] = field[x][y];
1876 level.fieldx = new_fieldx;
1877 level.fieldy = new_fieldy;
1881 /* Currently there is no special action needed to activate the template
1882 data, because 'element_info' property settings overwrite the original
1883 level data, while all other variables do not change. */
1885 /* Exception: 'from_level_template' elements in the original level playfield
1886 are overwritten with the corresponding elements at the same position in
1887 playfield from the level template. */
1889 for (x = 0; x < level.fieldx; x++)
1890 for (y = 0; y < level.fieldy; y++)
1891 if (level.field[x][y] == EL_FROM_LEVEL_TEMPLATE)
1892 level.field[x][y] = level_template.field[x][y];
1894 if (check_special_flags("load_xsb_to_ces"))
1896 struct LevelInfo level_backup = level;
1898 /* overwrite all individual level settings from template level settings */
1899 level = level_template;
1901 /* restore playfield size */
1902 level.fieldx = level_backup.fieldx;
1903 level.fieldy = level_backup.fieldy;
1905 /* restore playfield content */
1906 for (x = 0; x < level.fieldx; x++)
1907 for (y = 0; y < level.fieldy; y++)
1908 level.field[x][y] = level_backup.field[x][y];
1910 /* restore name and author from individual level */
1911 strcpy(level.name, level_backup.name);
1912 strcpy(level.author, level_backup.author);
1914 /* restore flag "use_custom_template" */
1915 level.use_custom_template = level_backup.use_custom_template;
1919 static char *getLevelFilenameFromBasename(char *basename)
1921 static char *filename[2] = { NULL, NULL };
1922 int pos = (strEqual(basename, LEVELTEMPLATE_FILENAME) ? 0 : 1);
1924 checked_free(filename[pos]);
1926 filename[pos] = getPath2(getCurrentLevelDir(), basename);
1928 return filename[pos];
1931 static int getFileTypeFromBasename(char *basename)
1933 /* !!! ALSO SEE COMMENT IN checkForPackageFromBasename() !!! */
1935 static char *filename = NULL;
1936 struct stat file_status;
1938 /* ---------- try to determine file type from filename ---------- */
1940 /* check for typical filename of a Supaplex level package file */
1941 if (strlen(basename) == 10 && strPrefixLower(basename, "levels.d"))
1942 return LEVEL_FILE_TYPE_SP;
1944 /* check for typical filename of a Diamond Caves II level package file */
1945 if (strSuffixLower(basename, ".dc") ||
1946 strSuffixLower(basename, ".dc2"))
1947 return LEVEL_FILE_TYPE_DC;
1949 /* check for typical filename of a Sokoban level package file */
1950 if (strSuffixLower(basename, ".xsb") &&
1951 strchr(basename, '%') == NULL)
1952 return LEVEL_FILE_TYPE_SB;
1954 /* ---------- try to determine file type from filesize ---------- */
1956 checked_free(filename);
1957 filename = getPath2(getCurrentLevelDir(), basename);
1959 if (stat(filename, &file_status) == 0)
1961 /* check for typical filesize of a Supaplex level package file */
1962 if (file_status.st_size == 170496)
1963 return LEVEL_FILE_TYPE_SP;
1966 return LEVEL_FILE_TYPE_UNKNOWN;
1969 static int getFileTypeFromMagicBytes(char *filename, int type)
1973 if ((file = openFile(filename, MODE_READ)))
1975 char chunk_name[CHUNK_ID_LEN + 1];
1977 getFileChunkBE(file, chunk_name, NULL);
1979 if (strEqual(chunk_name, "MMII") ||
1980 strEqual(chunk_name, "MIRR"))
1981 type = LEVEL_FILE_TYPE_MM;
1989 static boolean checkForPackageFromBasename(char *basename)
1991 /* !!! WON'T WORK ANYMORE IF getFileTypeFromBasename() ALSO DETECTS !!!
1992 !!! SINGLE LEVELS (CURRENTLY ONLY DETECTS LEVEL PACKAGES !!! */
1994 return (getFileTypeFromBasename(basename) != LEVEL_FILE_TYPE_UNKNOWN);
1997 static char *getSingleLevelBasenameExt(int nr, char *extension)
1999 static char basename[MAX_FILENAME_LEN];
2002 sprintf(basename, "%s", LEVELTEMPLATE_FILENAME);
2004 sprintf(basename, "%03d.%s", nr, extension);
2009 static char *getSingleLevelBasename(int nr)
2011 return getSingleLevelBasenameExt(nr, LEVELFILE_EXTENSION);
2014 static char *getPackedLevelBasename(int type)
2016 static char basename[MAX_FILENAME_LEN];
2017 char *directory = getCurrentLevelDir();
2019 DirectoryEntry *dir_entry;
2021 strcpy(basename, UNDEFINED_FILENAME); /* default: undefined file */
2023 if ((dir = openDirectory(directory)) == NULL)
2025 Error(ERR_WARN, "cannot read current level directory '%s'", directory);
2030 while ((dir_entry = readDirectory(dir)) != NULL) /* loop all entries */
2032 char *entry_basename = dir_entry->basename;
2033 int entry_type = getFileTypeFromBasename(entry_basename);
2035 if (entry_type != LEVEL_FILE_TYPE_UNKNOWN) /* found valid level package */
2037 if (type == LEVEL_FILE_TYPE_UNKNOWN ||
2040 strcpy(basename, entry_basename);
2047 closeDirectory(dir);
2052 static char *getSingleLevelFilename(int nr)
2054 return getLevelFilenameFromBasename(getSingleLevelBasename(nr));
2057 #if ENABLE_UNUSED_CODE
2058 static char *getPackedLevelFilename(int type)
2060 return getLevelFilenameFromBasename(getPackedLevelBasename(type));
2064 char *getDefaultLevelFilename(int nr)
2066 return getSingleLevelFilename(nr);
2069 #if ENABLE_UNUSED_CODE
2070 static void setLevelFileInfo_SingleLevelFilename(struct LevelFileInfo *lfi,
2074 lfi->packed = FALSE;
2075 lfi->basename = getSingleLevelBasename(lfi->nr, lfi->type);
2076 lfi->filename = getLevelFilenameFromBasename(lfi->basename);
2080 static void setLevelFileInfo_FormatLevelFilename(struct LevelFileInfo *lfi,
2081 int type, char *format, ...)
2083 static char basename[MAX_FILENAME_LEN];
2086 va_start(ap, format);
2087 vsprintf(basename, format, ap);
2091 lfi->packed = FALSE;
2092 lfi->basename = basename;
2093 lfi->filename = getLevelFilenameFromBasename(lfi->basename);
2096 static void setLevelFileInfo_PackedLevelFilename(struct LevelFileInfo *lfi,
2101 lfi->basename = getPackedLevelBasename(lfi->type);
2102 lfi->filename = getLevelFilenameFromBasename(lfi->basename);
2105 static int getFiletypeFromID(char *filetype_id)
2107 char *filetype_id_lower;
2108 int filetype = LEVEL_FILE_TYPE_UNKNOWN;
2111 if (filetype_id == NULL)
2112 return LEVEL_FILE_TYPE_UNKNOWN;
2114 filetype_id_lower = getStringToLower(filetype_id);
2116 for (i = 0; filetype_id_list[i].id != NULL; i++)
2118 char *id_lower = getStringToLower(filetype_id_list[i].id);
2120 if (strEqual(filetype_id_lower, id_lower))
2121 filetype = filetype_id_list[i].filetype;
2125 if (filetype != LEVEL_FILE_TYPE_UNKNOWN)
2129 free(filetype_id_lower);
2134 char *getLocalLevelTemplateFilename()
2136 return getDefaultLevelFilename(-1);
2139 char *getGlobalLevelTemplateFilename()
2141 /* global variable "leveldir_current" must be modified in the loop below */
2142 LevelDirTree *leveldir_current_last = leveldir_current;
2143 char *filename = NULL;
2145 /* check for template level in path from current to topmost tree node */
2147 while (leveldir_current != NULL)
2149 filename = getDefaultLevelFilename(-1);
2151 if (fileExists(filename))
2154 leveldir_current = leveldir_current->node_parent;
2157 /* restore global variable "leveldir_current" modified in above loop */
2158 leveldir_current = leveldir_current_last;
2163 static void determineLevelFileInfo_Filename(struct LevelFileInfo *lfi)
2167 /* special case: level number is negative => check for level template file */
2170 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
2171 getSingleLevelBasename(-1));
2173 /* replace local level template filename with global template filename */
2174 lfi->filename = getGlobalLevelTemplateFilename();
2176 /* no fallback if template file not existing */
2180 /* special case: check for file name/pattern specified in "levelinfo.conf" */
2181 if (leveldir_current->level_filename != NULL)
2183 int filetype = getFiletypeFromID(leveldir_current->level_filetype);
2185 setLevelFileInfo_FormatLevelFilename(lfi, filetype,
2186 leveldir_current->level_filename, nr);
2188 lfi->packed = checkForPackageFromBasename(leveldir_current->level_filename);
2190 if (fileExists(lfi->filename))
2193 else if (leveldir_current->level_filetype != NULL)
2195 int filetype = getFiletypeFromID(leveldir_current->level_filetype);
2197 /* check for specified native level file with standard file name */
2198 setLevelFileInfo_FormatLevelFilename(lfi, filetype,
2199 "%03d.%s", nr, LEVELFILE_EXTENSION);
2200 if (fileExists(lfi->filename))
2204 /* check for native Rocks'n'Diamonds level file */
2205 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
2206 "%03d.%s", nr, LEVELFILE_EXTENSION);
2207 if (fileExists(lfi->filename))
2210 /* check for Emerald Mine level file (V1) */
2211 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "a%c%c",
2212 'a' + (nr / 10) % 26, '0' + nr % 10);
2213 if (fileExists(lfi->filename))
2215 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "A%c%c",
2216 'A' + (nr / 10) % 26, '0' + nr % 10);
2217 if (fileExists(lfi->filename))
2220 /* check for Emerald Mine level file (V2 to V5) */
2221 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%d", nr);
2222 if (fileExists(lfi->filename))
2225 /* check for Emerald Mine level file (V6 / single mode) */
2226 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02ds", nr);
2227 if (fileExists(lfi->filename))
2229 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02dS", nr);
2230 if (fileExists(lfi->filename))
2233 /* check for Emerald Mine level file (V6 / teamwork mode) */
2234 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02dt", nr);
2235 if (fileExists(lfi->filename))
2237 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02dT", nr);
2238 if (fileExists(lfi->filename))
2241 /* check for various packed level file formats */
2242 setLevelFileInfo_PackedLevelFilename(lfi, LEVEL_FILE_TYPE_UNKNOWN);
2243 if (fileExists(lfi->filename))
2246 /* no known level file found -- use default values (and fail later) */
2247 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
2248 "%03d.%s", nr, LEVELFILE_EXTENSION);
2251 static void determineLevelFileInfo_Filetype(struct LevelFileInfo *lfi)
2253 if (lfi->type == LEVEL_FILE_TYPE_UNKNOWN)
2254 lfi->type = getFileTypeFromBasename(lfi->basename);
2256 if (lfi->type == LEVEL_FILE_TYPE_RND)
2257 lfi->type = getFileTypeFromMagicBytes(lfi->filename, lfi->type);
2260 static void setLevelFileInfo(struct LevelFileInfo *level_file_info, int nr)
2262 /* always start with reliable default values */
2263 setFileInfoToDefaults(level_file_info);
2265 level_file_info->nr = nr; /* set requested level number */
2267 determineLevelFileInfo_Filename(level_file_info);
2268 determineLevelFileInfo_Filetype(level_file_info);
2271 /* ------------------------------------------------------------------------- */
2272 /* functions for loading R'n'D level */
2273 /* ------------------------------------------------------------------------- */
2275 int getMappedElement(int element)
2277 /* remap some (historic, now obsolete) elements */
2281 case EL_PLAYER_OBSOLETE:
2282 element = EL_PLAYER_1;
2285 case EL_KEY_OBSOLETE:
2289 case EL_EM_KEY_1_FILE_OBSOLETE:
2290 element = EL_EM_KEY_1;
2293 case EL_EM_KEY_2_FILE_OBSOLETE:
2294 element = EL_EM_KEY_2;
2297 case EL_EM_KEY_3_FILE_OBSOLETE:
2298 element = EL_EM_KEY_3;
2301 case EL_EM_KEY_4_FILE_OBSOLETE:
2302 element = EL_EM_KEY_4;
2305 case EL_ENVELOPE_OBSOLETE:
2306 element = EL_ENVELOPE_1;
2314 if (element >= NUM_FILE_ELEMENTS)
2316 Error(ERR_WARN, "invalid level element %d", element);
2318 element = EL_UNKNOWN;
2326 int getMappedElementByVersion(int element, int game_version)
2328 /* remap some elements due to certain game version */
2330 if (game_version <= VERSION_IDENT(2,2,0,0))
2332 /* map game font elements */
2333 element = (element == EL_CHAR('[') ? EL_CHAR_AUMLAUT :
2334 element == EL_CHAR('\\') ? EL_CHAR_OUMLAUT :
2335 element == EL_CHAR(']') ? EL_CHAR_UUMLAUT :
2336 element == EL_CHAR('^') ? EL_CHAR_COPYRIGHT : element);
2339 if (game_version < VERSION_IDENT(3,0,0,0))
2341 /* map Supaplex gravity tube elements */
2342 element = (element == EL_SP_GRAVITY_PORT_LEFT ? EL_SP_PORT_LEFT :
2343 element == EL_SP_GRAVITY_PORT_RIGHT ? EL_SP_PORT_RIGHT :
2344 element == EL_SP_GRAVITY_PORT_UP ? EL_SP_PORT_UP :
2345 element == EL_SP_GRAVITY_PORT_DOWN ? EL_SP_PORT_DOWN :
2352 static int LoadLevel_VERS(File *file, int chunk_size, struct LevelInfo *level)
2354 level->file_version = getFileVersion(file);
2355 level->game_version = getFileVersion(file);
2360 static int LoadLevel_DATE(File *file, int chunk_size, struct LevelInfo *level)
2362 level->creation_date.year = getFile16BitBE(file);
2363 level->creation_date.month = getFile8Bit(file);
2364 level->creation_date.day = getFile8Bit(file);
2366 level->creation_date.src = DATE_SRC_LEVELFILE;
2371 static int LoadLevel_HEAD(File *file, int chunk_size, struct LevelInfo *level)
2373 int initial_player_stepsize;
2374 int initial_player_gravity;
2377 level->fieldx = getFile8Bit(file);
2378 level->fieldy = getFile8Bit(file);
2380 level->time = getFile16BitBE(file);
2381 level->gems_needed = getFile16BitBE(file);
2383 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
2384 level->name[i] = getFile8Bit(file);
2385 level->name[MAX_LEVEL_NAME_LEN] = 0;
2387 for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
2388 level->score[i] = getFile8Bit(file);
2390 level->num_yamyam_contents = STD_ELEMENT_CONTENTS;
2391 for (i = 0; i < STD_ELEMENT_CONTENTS; i++)
2392 for (y = 0; y < 3; y++)
2393 for (x = 0; x < 3; x++)
2394 level->yamyam_content[i].e[x][y] = getMappedElement(getFile8Bit(file));
2396 level->amoeba_speed = getFile8Bit(file);
2397 level->time_magic_wall = getFile8Bit(file);
2398 level->time_wheel = getFile8Bit(file);
2399 level->amoeba_content = getMappedElement(getFile8Bit(file));
2401 initial_player_stepsize = (getFile8Bit(file) == 1 ? STEPSIZE_FAST :
2404 for (i = 0; i < MAX_PLAYERS; i++)
2405 level->initial_player_stepsize[i] = initial_player_stepsize;
2407 initial_player_gravity = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2409 for (i = 0; i < MAX_PLAYERS; i++)
2410 level->initial_player_gravity[i] = initial_player_gravity;
2412 level->encoding_16bit_field = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2413 level->em_slippery_gems = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2415 level->use_custom_template = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2417 level->block_last_field = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2418 level->sp_block_last_field = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2419 level->can_move_into_acid_bits = getFile32BitBE(file);
2420 level->dont_collide_with_bits = getFile8Bit(file);
2422 level->use_spring_bug = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2423 level->use_step_counter = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2425 level->instant_relocation = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2426 level->can_pass_to_walkable = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2427 level->grow_into_diggable = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2429 level->game_engine_type = getFile8Bit(file);
2431 ReadUnusedBytesFromFile(file, LEVEL_CHUNK_HEAD_UNUSED);
2436 static int LoadLevel_NAME(File *file, int chunk_size, struct LevelInfo *level)
2440 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
2441 level->name[i] = getFile8Bit(file);
2442 level->name[MAX_LEVEL_NAME_LEN] = 0;
2447 static int LoadLevel_AUTH(File *file, int chunk_size, struct LevelInfo *level)
2451 for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
2452 level->author[i] = getFile8Bit(file);
2453 level->author[MAX_LEVEL_AUTHOR_LEN] = 0;
2458 static int LoadLevel_BODY(File *file, int chunk_size, struct LevelInfo *level)
2461 int chunk_size_expected = level->fieldx * level->fieldy;
2463 /* Note: "chunk_size" was wrong before version 2.0 when elements are
2464 stored with 16-bit encoding (and should be twice as big then).
2465 Even worse, playfield data was stored 16-bit when only yamyam content
2466 contained 16-bit elements and vice versa. */
2468 if (level->encoding_16bit_field && level->file_version >= FILE_VERSION_2_0)
2469 chunk_size_expected *= 2;
2471 if (chunk_size_expected != chunk_size)
2473 ReadUnusedBytesFromFile(file, chunk_size);
2474 return chunk_size_expected;
2477 for (y = 0; y < level->fieldy; y++)
2478 for (x = 0; x < level->fieldx; x++)
2479 level->field[x][y] =
2480 getMappedElement(level->encoding_16bit_field ? getFile16BitBE(file) :
2485 static int LoadLevel_CONT(File *file, int chunk_size, struct LevelInfo *level)
2488 int header_size = 4;
2489 int content_size = MAX_ELEMENT_CONTENTS * 3 * 3;
2490 int chunk_size_expected = header_size + content_size;
2492 /* Note: "chunk_size" was wrong before version 2.0 when elements are
2493 stored with 16-bit encoding (and should be twice as big then).
2494 Even worse, playfield data was stored 16-bit when only yamyam content
2495 contained 16-bit elements and vice versa. */
2497 if (level->encoding_16bit_field && level->file_version >= FILE_VERSION_2_0)
2498 chunk_size_expected += content_size;
2500 if (chunk_size_expected != chunk_size)
2502 ReadUnusedBytesFromFile(file, chunk_size);
2503 return chunk_size_expected;
2507 level->num_yamyam_contents = getFile8Bit(file);
2511 /* correct invalid number of content fields -- should never happen */
2512 if (level->num_yamyam_contents < 1 ||
2513 level->num_yamyam_contents > MAX_ELEMENT_CONTENTS)
2514 level->num_yamyam_contents = STD_ELEMENT_CONTENTS;
2516 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
2517 for (y = 0; y < 3; y++)
2518 for (x = 0; x < 3; x++)
2519 level->yamyam_content[i].e[x][y] =
2520 getMappedElement(level->encoding_16bit_field ?
2521 getFile16BitBE(file) : getFile8Bit(file));
2525 static int LoadLevel_CNT2(File *file, int chunk_size, struct LevelInfo *level)
2530 int content_array[MAX_ELEMENT_CONTENTS][3][3];
2532 element = getMappedElement(getFile16BitBE(file));
2533 num_contents = getFile8Bit(file);
2535 getFile8Bit(file); /* content x size (unused) */
2536 getFile8Bit(file); /* content y size (unused) */
2538 ReadUnusedBytesFromFile(file, LEVEL_CHUNK_CNT2_UNUSED);
2540 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
2541 for (y = 0; y < 3; y++)
2542 for (x = 0; x < 3; x++)
2543 content_array[i][x][y] = getMappedElement(getFile16BitBE(file));
2545 /* correct invalid number of content fields -- should never happen */
2546 if (num_contents < 1 || num_contents > MAX_ELEMENT_CONTENTS)
2547 num_contents = STD_ELEMENT_CONTENTS;
2549 if (element == EL_YAMYAM)
2551 level->num_yamyam_contents = num_contents;
2553 for (i = 0; i < num_contents; i++)
2554 for (y = 0; y < 3; y++)
2555 for (x = 0; x < 3; x++)
2556 level->yamyam_content[i].e[x][y] = content_array[i][x][y];
2558 else if (element == EL_BD_AMOEBA)
2560 level->amoeba_content = content_array[0][0][0];
2564 Error(ERR_WARN, "cannot load content for element '%d'", element);
2570 static int LoadLevel_CNT3(File *file, int chunk_size, struct LevelInfo *level)
2576 int chunk_size_expected;
2578 element = getMappedElement(getFile16BitBE(file));
2579 if (!IS_ENVELOPE(element))
2580 element = EL_ENVELOPE_1;
2582 envelope_nr = element - EL_ENVELOPE_1;
2584 envelope_len = getFile16BitBE(file);
2586 level->envelope[envelope_nr].xsize = getFile8Bit(file);
2587 level->envelope[envelope_nr].ysize = getFile8Bit(file);
2589 ReadUnusedBytesFromFile(file, LEVEL_CHUNK_CNT3_UNUSED);
2591 chunk_size_expected = LEVEL_CHUNK_CNT3_SIZE(envelope_len);
2592 if (chunk_size_expected != chunk_size)
2594 ReadUnusedBytesFromFile(file, chunk_size - LEVEL_CHUNK_CNT3_HEADER);
2595 return chunk_size_expected;
2598 for (i = 0; i < envelope_len; i++)
2599 level->envelope[envelope_nr].text[i] = getFile8Bit(file);
2604 static int LoadLevel_CUS1(File *file, int chunk_size, struct LevelInfo *level)
2606 int num_changed_custom_elements = getFile16BitBE(file);
2607 int chunk_size_expected = 2 + num_changed_custom_elements * 6;
2610 if (chunk_size_expected != chunk_size)
2612 ReadUnusedBytesFromFile(file, chunk_size - 2);
2613 return chunk_size_expected;
2616 for (i = 0; i < num_changed_custom_elements; i++)
2618 int element = getMappedElement(getFile16BitBE(file));
2619 int properties = getFile32BitBE(file);
2621 if (IS_CUSTOM_ELEMENT(element))
2622 element_info[element].properties[EP_BITFIELD_BASE_NR] = properties;
2624 Error(ERR_WARN, "invalid custom element number %d", element);
2626 /* older game versions that wrote level files with CUS1 chunks used
2627 different default push delay values (not yet stored in level file) */
2628 element_info[element].push_delay_fixed = 2;
2629 element_info[element].push_delay_random = 8;
2632 level->file_has_custom_elements = TRUE;
2637 static int LoadLevel_CUS2(File *file, int chunk_size, struct LevelInfo *level)
2639 int num_changed_custom_elements = getFile16BitBE(file);
2640 int chunk_size_expected = 2 + num_changed_custom_elements * 4;
2643 if (chunk_size_expected != chunk_size)
2645 ReadUnusedBytesFromFile(file, chunk_size - 2);
2646 return chunk_size_expected;
2649 for (i = 0; i < num_changed_custom_elements; i++)
2651 int element = getMappedElement(getFile16BitBE(file));
2652 int custom_target_element = getMappedElement(getFile16BitBE(file));
2654 if (IS_CUSTOM_ELEMENT(element))
2655 element_info[element].change->target_element = custom_target_element;
2657 Error(ERR_WARN, "invalid custom element number %d", element);
2660 level->file_has_custom_elements = TRUE;
2665 static int LoadLevel_CUS3(File *file, int chunk_size, struct LevelInfo *level)
2667 int num_changed_custom_elements = getFile16BitBE(file);
2668 int chunk_size_expected = LEVEL_CHUNK_CUS3_SIZE(num_changed_custom_elements);
2671 if (chunk_size_expected != chunk_size)
2673 ReadUnusedBytesFromFile(file, chunk_size - 2);
2674 return chunk_size_expected;
2677 for (i = 0; i < num_changed_custom_elements; i++)
2679 int element = getMappedElement(getFile16BitBE(file));
2680 struct ElementInfo *ei = &element_info[element];
2681 unsigned int event_bits;
2683 if (!IS_CUSTOM_ELEMENT(element))
2685 Error(ERR_WARN, "invalid custom element number %d", element);
2687 element = EL_INTERNAL_DUMMY;
2690 for (j = 0; j < MAX_ELEMENT_NAME_LEN; j++)
2691 ei->description[j] = getFile8Bit(file);
2692 ei->description[MAX_ELEMENT_NAME_LEN] = 0;
2694 ei->properties[EP_BITFIELD_BASE_NR] = getFile32BitBE(file);
2696 /* some free bytes for future properties and padding */
2697 ReadUnusedBytesFromFile(file, 7);
2699 ei->use_gfx_element = getFile8Bit(file);
2700 ei->gfx_element_initial = getMappedElement(getFile16BitBE(file));
2702 ei->collect_score_initial = getFile8Bit(file);
2703 ei->collect_count_initial = getFile8Bit(file);
2705 ei->push_delay_fixed = getFile16BitBE(file);
2706 ei->push_delay_random = getFile16BitBE(file);
2707 ei->move_delay_fixed = getFile16BitBE(file);
2708 ei->move_delay_random = getFile16BitBE(file);
2710 ei->move_pattern = getFile16BitBE(file);
2711 ei->move_direction_initial = getFile8Bit(file);
2712 ei->move_stepsize = getFile8Bit(file);
2714 for (y = 0; y < 3; y++)
2715 for (x = 0; x < 3; x++)
2716 ei->content.e[x][y] = getMappedElement(getFile16BitBE(file));
2718 event_bits = getFile32BitBE(file);
2719 for (j = 0; j < NUM_CHANGE_EVENTS; j++)
2720 if (event_bits & (1 << j))
2721 ei->change->has_event[j] = TRUE;
2723 ei->change->target_element = getMappedElement(getFile16BitBE(file));
2725 ei->change->delay_fixed = getFile16BitBE(file);
2726 ei->change->delay_random = getFile16BitBE(file);
2727 ei->change->delay_frames = getFile16BitBE(file);
2729 ei->change->initial_trigger_element= getMappedElement(getFile16BitBE(file));
2731 ei->change->explode = getFile8Bit(file);
2732 ei->change->use_target_content = getFile8Bit(file);
2733 ei->change->only_if_complete = getFile8Bit(file);
2734 ei->change->use_random_replace = getFile8Bit(file);
2736 ei->change->random_percentage = getFile8Bit(file);
2737 ei->change->replace_when = getFile8Bit(file);
2739 for (y = 0; y < 3; y++)
2740 for (x = 0; x < 3; x++)
2741 ei->change->target_content.e[x][y] =
2742 getMappedElement(getFile16BitBE(file));
2744 ei->slippery_type = getFile8Bit(file);
2746 /* some free bytes for future properties and padding */
2747 ReadUnusedBytesFromFile(file, LEVEL_CPART_CUS3_UNUSED);
2749 /* mark that this custom element has been modified */
2750 ei->modified_settings = TRUE;
2753 level->file_has_custom_elements = TRUE;
2758 static int LoadLevel_CUS4(File *file, int chunk_size, struct LevelInfo *level)
2760 struct ElementInfo *ei;
2761 int chunk_size_expected;
2765 /* ---------- custom element base property values (96 bytes) ------------- */
2767 element = getMappedElement(getFile16BitBE(file));
2769 if (!IS_CUSTOM_ELEMENT(element))
2771 Error(ERR_WARN, "invalid custom element number %d", element);
2773 ReadUnusedBytesFromFile(file, chunk_size - 2);
2777 ei = &element_info[element];
2779 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
2780 ei->description[i] = getFile8Bit(file);
2781 ei->description[MAX_ELEMENT_NAME_LEN] = 0;
2783 ei->properties[EP_BITFIELD_BASE_NR] = getFile32BitBE(file);
2785 ReadUnusedBytesFromFile(file, 4); /* reserved for more base properties */
2787 ei->num_change_pages = getFile8Bit(file);
2789 chunk_size_expected = LEVEL_CHUNK_CUS4_SIZE(ei->num_change_pages);
2790 if (chunk_size_expected != chunk_size)
2792 ReadUnusedBytesFromFile(file, chunk_size - 43);
2793 return chunk_size_expected;
2796 ei->ce_value_fixed_initial = getFile16BitBE(file);
2797 ei->ce_value_random_initial = getFile16BitBE(file);
2798 ei->use_last_ce_value = getFile8Bit(file);
2800 ei->use_gfx_element = getFile8Bit(file);
2801 ei->gfx_element_initial = getMappedElement(getFile16BitBE(file));
2803 ei->collect_score_initial = getFile8Bit(file);
2804 ei->collect_count_initial = getFile8Bit(file);
2806 ei->drop_delay_fixed = getFile8Bit(file);
2807 ei->push_delay_fixed = getFile8Bit(file);
2808 ei->drop_delay_random = getFile8Bit(file);
2809 ei->push_delay_random = getFile8Bit(file);
2810 ei->move_delay_fixed = getFile16BitBE(file);
2811 ei->move_delay_random = getFile16BitBE(file);
2813 /* bits 0 - 15 of "move_pattern" ... */
2814 ei->move_pattern = getFile16BitBE(file);
2815 ei->move_direction_initial = getFile8Bit(file);
2816 ei->move_stepsize = getFile8Bit(file);
2818 ei->slippery_type = getFile8Bit(file);
2820 for (y = 0; y < 3; y++)
2821 for (x = 0; x < 3; x++)
2822 ei->content.e[x][y] = getMappedElement(getFile16BitBE(file));
2824 ei->move_enter_element = getMappedElement(getFile16BitBE(file));
2825 ei->move_leave_element = getMappedElement(getFile16BitBE(file));
2826 ei->move_leave_type = getFile8Bit(file);
2828 /* ... bits 16 - 31 of "move_pattern" (not nice, but downward compatible) */
2829 ei->move_pattern |= (getFile16BitBE(file) << 16);
2831 ei->access_direction = getFile8Bit(file);
2833 ei->explosion_delay = getFile8Bit(file);
2834 ei->ignition_delay = getFile8Bit(file);
2835 ei->explosion_type = getFile8Bit(file);
2837 /* some free bytes for future custom property values and padding */
2838 ReadUnusedBytesFromFile(file, 1);
2840 /* ---------- change page property values (48 bytes) --------------------- */
2842 setElementChangePages(ei, ei->num_change_pages);
2844 for (i = 0; i < ei->num_change_pages; i++)
2846 struct ElementChangeInfo *change = &ei->change_page[i];
2847 unsigned int event_bits;
2849 /* always start with reliable default values */
2850 setElementChangeInfoToDefaults(change);
2852 /* bits 0 - 31 of "has_event[]" ... */
2853 event_bits = getFile32BitBE(file);
2854 for (j = 0; j < MIN(NUM_CHANGE_EVENTS, 32); j++)
2855 if (event_bits & (1 << j))
2856 change->has_event[j] = TRUE;
2858 change->target_element = getMappedElement(getFile16BitBE(file));
2860 change->delay_fixed = getFile16BitBE(file);
2861 change->delay_random = getFile16BitBE(file);
2862 change->delay_frames = getFile16BitBE(file);
2864 change->initial_trigger_element = getMappedElement(getFile16BitBE(file));
2866 change->explode = getFile8Bit(file);
2867 change->use_target_content = getFile8Bit(file);
2868 change->only_if_complete = getFile8Bit(file);
2869 change->use_random_replace = getFile8Bit(file);
2871 change->random_percentage = getFile8Bit(file);
2872 change->replace_when = getFile8Bit(file);
2874 for (y = 0; y < 3; y++)
2875 for (x = 0; x < 3; x++)
2876 change->target_content.e[x][y]= getMappedElement(getFile16BitBE(file));
2878 change->can_change = getFile8Bit(file);
2880 change->trigger_side = getFile8Bit(file);
2882 change->trigger_player = getFile8Bit(file);
2883 change->trigger_page = getFile8Bit(file);
2885 change->trigger_page = (change->trigger_page == CH_PAGE_ANY_FILE ?
2886 CH_PAGE_ANY : (1 << change->trigger_page));
2888 change->has_action = getFile8Bit(file);
2889 change->action_type = getFile8Bit(file);
2890 change->action_mode = getFile8Bit(file);
2891 change->action_arg = getFile16BitBE(file);
2893 /* ... bits 32 - 39 of "has_event[]" (not nice, but downward compatible) */
2894 event_bits = getFile8Bit(file);
2895 for (j = 32; j < NUM_CHANGE_EVENTS; j++)
2896 if (event_bits & (1 << (j - 32)))
2897 change->has_event[j] = TRUE;
2900 /* mark this custom element as modified */
2901 ei->modified_settings = TRUE;
2903 level->file_has_custom_elements = TRUE;
2908 static int LoadLevel_GRP1(File *file, int chunk_size, struct LevelInfo *level)
2910 struct ElementInfo *ei;
2911 struct ElementGroupInfo *group;
2915 element = getMappedElement(getFile16BitBE(file));
2917 if (!IS_GROUP_ELEMENT(element))
2919 Error(ERR_WARN, "invalid group element number %d", element);
2921 ReadUnusedBytesFromFile(file, chunk_size - 2);
2925 ei = &element_info[element];
2927 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
2928 ei->description[i] = getFile8Bit(file);
2929 ei->description[MAX_ELEMENT_NAME_LEN] = 0;
2931 group = element_info[element].group;
2933 group->num_elements = getFile8Bit(file);
2935 ei->use_gfx_element = getFile8Bit(file);
2936 ei->gfx_element_initial = getMappedElement(getFile16BitBE(file));
2938 group->choice_mode = getFile8Bit(file);
2940 /* some free bytes for future values and padding */
2941 ReadUnusedBytesFromFile(file, 3);
2943 for (i = 0; i < MAX_ELEMENTS_IN_GROUP; i++)
2944 group->element[i] = getMappedElement(getFile16BitBE(file));
2946 /* mark this group element as modified */
2947 element_info[element].modified_settings = TRUE;
2949 level->file_has_custom_elements = TRUE;
2954 static int LoadLevel_MicroChunk(File *file, struct LevelFileConfigInfo *conf,
2955 int element, int real_element)
2957 int micro_chunk_size = 0;
2958 int conf_type = getFile8Bit(file);
2959 int byte_mask = conf_type & CONF_MASK_BYTES;
2960 boolean element_found = FALSE;
2963 micro_chunk_size += 1;
2965 if (byte_mask == CONF_MASK_MULTI_BYTES)
2967 int num_bytes = getFile16BitBE(file);
2968 byte *buffer = checked_malloc(num_bytes);
2970 ReadBytesFromFile(file, buffer, num_bytes);
2972 for (i = 0; conf[i].data_type != -1; i++)
2974 if (conf[i].element == element &&
2975 conf[i].conf_type == conf_type)
2977 int data_type = conf[i].data_type;
2978 int num_entities = num_bytes / CONF_ENTITY_NUM_BYTES(data_type);
2979 int max_num_entities = conf[i].max_num_entities;
2981 if (num_entities > max_num_entities)
2984 "truncating number of entities for element %d from %d to %d",
2985 element, num_entities, max_num_entities);
2987 num_entities = max_num_entities;
2990 if (num_entities == 0 && (data_type == TYPE_ELEMENT_LIST ||
2991 data_type == TYPE_CONTENT_LIST))
2993 /* for element and content lists, zero entities are not allowed */
2994 Error(ERR_WARN, "found empty list of entities for element %d",
2997 /* do not set "num_entities" here to prevent reading behind buffer */
2999 *(int *)(conf[i].num_entities) = 1; /* at least one is required */
3003 *(int *)(conf[i].num_entities) = num_entities;
3006 element_found = TRUE;
3008 if (data_type == TYPE_STRING)
3010 char *string = (char *)(conf[i].value);
3013 for (j = 0; j < max_num_entities; j++)
3014 string[j] = (j < num_entities ? buffer[j] : '\0');
3016 else if (data_type == TYPE_ELEMENT_LIST)
3018 int *element_array = (int *)(conf[i].value);
3021 for (j = 0; j < num_entities; j++)
3023 getMappedElement(CONF_ELEMENTS_ELEMENT(buffer, j));
3025 else if (data_type == TYPE_CONTENT_LIST)
3027 struct Content *content= (struct Content *)(conf[i].value);
3030 for (c = 0; c < num_entities; c++)
3031 for (y = 0; y < 3; y++)
3032 for (x = 0; x < 3; x++)
3033 content[c].e[x][y] =
3034 getMappedElement(CONF_CONTENTS_ELEMENT(buffer, c, x, y));
3037 element_found = FALSE;
3043 checked_free(buffer);
3045 micro_chunk_size += 2 + num_bytes;
3047 else /* constant size configuration data (1, 2 or 4 bytes) */
3049 int value = (byte_mask == CONF_MASK_1_BYTE ? getFile8Bit (file) :
3050 byte_mask == CONF_MASK_2_BYTE ? getFile16BitBE(file) :
3051 byte_mask == CONF_MASK_4_BYTE ? getFile32BitBE(file) : 0);
3053 for (i = 0; conf[i].data_type != -1; i++)
3055 if (conf[i].element == element &&
3056 conf[i].conf_type == conf_type)
3058 int data_type = conf[i].data_type;
3060 if (data_type == TYPE_ELEMENT)
3061 value = getMappedElement(value);
3063 if (data_type == TYPE_BOOLEAN)
3064 *(boolean *)(conf[i].value) = value;
3066 *(int *) (conf[i].value) = value;
3068 element_found = TRUE;
3074 micro_chunk_size += CONF_VALUE_NUM_BYTES(byte_mask);
3079 char *error_conf_chunk_bytes =
3080 (byte_mask == CONF_MASK_1_BYTE ? "CONF_VALUE_8_BIT" :
3081 byte_mask == CONF_MASK_2_BYTE ? "CONF_VALUE_16_BIT" :
3082 byte_mask == CONF_MASK_4_BYTE ? "CONF_VALUE_32_BIT" :"CONF_VALUE_BYTES");
3083 int error_conf_chunk_token = conf_type & CONF_MASK_TOKEN;
3084 int error_element = real_element;
3086 Error(ERR_WARN, "cannot load micro chunk '%s(%d)' value for element %d ['%s']",
3087 error_conf_chunk_bytes, error_conf_chunk_token,
3088 error_element, EL_NAME(error_element));
3091 return micro_chunk_size;
3094 static int LoadLevel_INFO(File *file, int chunk_size, struct LevelInfo *level)
3096 int real_chunk_size = 0;
3098 li = *level; /* copy level data into temporary buffer */
3100 while (!checkEndOfFile(file))
3102 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_INFO, -1, -1);
3104 if (real_chunk_size >= chunk_size)
3108 *level = li; /* copy temporary buffer back to level data */
3110 return real_chunk_size;
3113 static int LoadLevel_CONF(File *file, int chunk_size, struct LevelInfo *level)
3115 int real_chunk_size = 0;
3117 li = *level; /* copy level data into temporary buffer */
3119 while (!checkEndOfFile(file))
3121 int element = getMappedElement(getFile16BitBE(file));
3123 real_chunk_size += 2;
3124 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_CONF,
3126 if (real_chunk_size >= chunk_size)
3130 *level = li; /* copy temporary buffer back to level data */
3132 return real_chunk_size;
3135 static int LoadLevel_ELEM(File *file, int chunk_size, struct LevelInfo *level)
3137 int real_chunk_size = 0;
3139 li = *level; /* copy level data into temporary buffer */
3141 while (!checkEndOfFile(file))
3143 int element = getMappedElement(getFile16BitBE(file));
3145 real_chunk_size += 2;
3146 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_ELEM,
3148 if (real_chunk_size >= chunk_size)
3152 *level = li; /* copy temporary buffer back to level data */
3154 return real_chunk_size;
3157 static int LoadLevel_NOTE(File *file, int chunk_size, struct LevelInfo *level)
3159 int element = getMappedElement(getFile16BitBE(file));
3160 int envelope_nr = element - EL_ENVELOPE_1;
3161 int real_chunk_size = 2;
3163 xx_envelope = level->envelope[envelope_nr]; /* copy into temporary buffer */
3165 while (!checkEndOfFile(file))
3167 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_NOTE,
3170 if (real_chunk_size >= chunk_size)
3174 level->envelope[envelope_nr] = xx_envelope; /* copy from temporary buffer */
3176 return real_chunk_size;
3179 static int LoadLevel_CUSX(File *file, int chunk_size, struct LevelInfo *level)
3181 int element = getMappedElement(getFile16BitBE(file));
3182 int real_chunk_size = 2;
3183 struct ElementInfo *ei = &element_info[element];
3186 xx_ei = *ei; /* copy element data into temporary buffer */
3188 xx_ei.num_change_pages = -1;
3190 while (!checkEndOfFile(file))
3192 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_CUSX_base,
3194 if (xx_ei.num_change_pages != -1)
3197 if (real_chunk_size >= chunk_size)
3203 if (ei->num_change_pages == -1)
3205 Error(ERR_WARN, "LoadLevel_CUSX(): missing 'num_change_pages' for '%s'",
3208 ei->num_change_pages = 1;
3210 setElementChangePages(ei, 1);
3211 setElementChangeInfoToDefaults(ei->change);
3213 return real_chunk_size;
3216 /* initialize number of change pages stored for this custom element */
3217 setElementChangePages(ei, ei->num_change_pages);
3218 for (i = 0; i < ei->num_change_pages; i++)
3219 setElementChangeInfoToDefaults(&ei->change_page[i]);
3221 /* start with reading properties for the first change page */
3222 xx_current_change_page = 0;
3224 while (!checkEndOfFile(file))
3226 struct ElementChangeInfo *change = &ei->change_page[xx_current_change_page];
3228 xx_change = *change; /* copy change data into temporary buffer */
3230 resetEventBits(); /* reset bits; change page might have changed */
3232 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_CUSX_change,
3235 *change = xx_change;
3237 setEventFlagsFromEventBits(change);
3239 if (real_chunk_size >= chunk_size)
3243 level->file_has_custom_elements = TRUE;
3245 return real_chunk_size;
3248 static int LoadLevel_GRPX(File *file, int chunk_size, struct LevelInfo *level)
3250 int element = getMappedElement(getFile16BitBE(file));
3251 int real_chunk_size = 2;
3252 struct ElementInfo *ei = &element_info[element];
3253 struct ElementGroupInfo *group = ei->group;
3255 xx_ei = *ei; /* copy element data into temporary buffer */
3256 xx_group = *group; /* copy group data into temporary buffer */
3258 while (!checkEndOfFile(file))
3260 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_GRPX,
3263 if (real_chunk_size >= chunk_size)
3270 level->file_has_custom_elements = TRUE;
3272 return real_chunk_size;
3275 static void LoadLevelFromFileInfo_RND(struct LevelInfo *level,
3276 struct LevelFileInfo *level_file_info,
3277 boolean level_info_only)
3279 char *filename = level_file_info->filename;
3280 char cookie[MAX_LINE_LEN];
3281 char chunk_name[CHUNK_ID_LEN + 1];
3285 if (!(file = openFile(filename, MODE_READ)))
3287 level->no_valid_file = TRUE;
3288 level->no_level_file = TRUE;
3290 if (level_info_only)
3293 Error(ERR_WARN, "cannot read level '%s' -- using empty level", filename);
3295 if (!setup.editor.use_template_for_new_levels)
3298 /* if level file not found, try to initialize level data from template */
3299 filename = getGlobalLevelTemplateFilename();
3301 if (!(file = openFile(filename, MODE_READ)))
3304 /* default: for empty levels, use level template for custom elements */
3305 level->use_custom_template = TRUE;
3307 level->no_valid_file = FALSE;
3310 getFileChunkBE(file, chunk_name, NULL);
3311 if (strEqual(chunk_name, "RND1"))
3313 getFile32BitBE(file); /* not used */
3315 getFileChunkBE(file, chunk_name, NULL);
3316 if (!strEqual(chunk_name, "CAVE"))
3318 level->no_valid_file = TRUE;
3320 Error(ERR_WARN, "unknown format of level file '%s'", filename);
3327 else /* check for pre-2.0 file format with cookie string */
3329 strcpy(cookie, chunk_name);
3330 if (getStringFromFile(file, &cookie[4], MAX_LINE_LEN - 4) == NULL)
3332 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
3333 cookie[strlen(cookie) - 1] = '\0';
3335 if (!checkCookieString(cookie, LEVEL_COOKIE_TMPL))
3337 level->no_valid_file = TRUE;
3339 Error(ERR_WARN, "unknown format of level file '%s'", filename);
3346 if ((level->file_version = getFileVersionFromCookieString(cookie)) == -1)
3348 level->no_valid_file = TRUE;
3350 Error(ERR_WARN, "unsupported version of level file '%s'", filename);
3357 /* pre-2.0 level files have no game version, so use file version here */
3358 level->game_version = level->file_version;
3361 if (level->file_version < FILE_VERSION_1_2)
3363 /* level files from versions before 1.2.0 without chunk structure */
3364 LoadLevel_HEAD(file, LEVEL_CHUNK_HEAD_SIZE, level);
3365 LoadLevel_BODY(file, level->fieldx * level->fieldy, level);
3373 int (*loader)(File *, int, struct LevelInfo *);
3377 { "VERS", LEVEL_CHUNK_VERS_SIZE, LoadLevel_VERS },
3378 { "DATE", LEVEL_CHUNK_DATE_SIZE, LoadLevel_DATE },
3379 { "HEAD", LEVEL_CHUNK_HEAD_SIZE, LoadLevel_HEAD },
3380 { "NAME", LEVEL_CHUNK_NAME_SIZE, LoadLevel_NAME },
3381 { "AUTH", LEVEL_CHUNK_AUTH_SIZE, LoadLevel_AUTH },
3382 { "INFO", -1, LoadLevel_INFO },
3383 { "BODY", -1, LoadLevel_BODY },
3384 { "CONT", -1, LoadLevel_CONT },
3385 { "CNT2", LEVEL_CHUNK_CNT2_SIZE, LoadLevel_CNT2 },
3386 { "CNT3", -1, LoadLevel_CNT3 },
3387 { "CUS1", -1, LoadLevel_CUS1 },
3388 { "CUS2", -1, LoadLevel_CUS2 },
3389 { "CUS3", -1, LoadLevel_CUS3 },
3390 { "CUS4", -1, LoadLevel_CUS4 },
3391 { "GRP1", -1, LoadLevel_GRP1 },
3392 { "CONF", -1, LoadLevel_CONF },
3393 { "ELEM", -1, LoadLevel_ELEM },
3394 { "NOTE", -1, LoadLevel_NOTE },
3395 { "CUSX", -1, LoadLevel_CUSX },
3396 { "GRPX", -1, LoadLevel_GRPX },
3401 while (getFileChunkBE(file, chunk_name, &chunk_size))
3405 while (chunk_info[i].name != NULL &&
3406 !strEqual(chunk_name, chunk_info[i].name))
3409 if (chunk_info[i].name == NULL)
3411 Error(ERR_WARN, "unknown chunk '%s' in level file '%s'",
3412 chunk_name, filename);
3413 ReadUnusedBytesFromFile(file, chunk_size);
3415 else if (chunk_info[i].size != -1 &&
3416 chunk_info[i].size != chunk_size)
3418 Error(ERR_WARN, "wrong size (%d) of chunk '%s' in level file '%s'",
3419 chunk_size, chunk_name, filename);
3420 ReadUnusedBytesFromFile(file, chunk_size);
3424 /* call function to load this level chunk */
3425 int chunk_size_expected =
3426 (chunk_info[i].loader)(file, chunk_size, level);
3428 /* the size of some chunks cannot be checked before reading other
3429 chunks first (like "HEAD" and "BODY") that contain some header
3430 information, so check them here */
3431 if (chunk_size_expected != chunk_size)
3433 Error(ERR_WARN, "wrong size (%d) of chunk '%s' in level file '%s'",
3434 chunk_size, chunk_name, filename);
3444 /* ------------------------------------------------------------------------- */
3445 /* functions for loading EM level */
3446 /* ------------------------------------------------------------------------- */
3448 void CopyNativeLevel_RND_to_EM(struct LevelInfo *level)
3450 static int ball_xy[8][2] =
3461 struct LevelInfo_EM *level_em = level->native_em_level;
3462 struct LEVEL *lev = level_em->lev;
3463 struct PLAYER **ply = level_em->ply;
3466 lev->width = MIN(level->fieldx, EM_MAX_CAVE_WIDTH);
3467 lev->height = MIN(level->fieldy, EM_MAX_CAVE_HEIGHT);
3469 lev->time_seconds = level->time;
3470 lev->required_initial = level->gems_needed;
3472 lev->emerald_score = level->score[SC_EMERALD];
3473 lev->diamond_score = level->score[SC_DIAMOND];
3474 lev->alien_score = level->score[SC_ROBOT];
3475 lev->tank_score = level->score[SC_SPACESHIP];
3476 lev->bug_score = level->score[SC_BUG];
3477 lev->eater_score = level->score[SC_YAMYAM];
3478 lev->nut_score = level->score[SC_NUT];
3479 lev->dynamite_score = level->score[SC_DYNAMITE];
3480 lev->key_score = level->score[SC_KEY];
3481 lev->exit_score = level->score[SC_TIME_BONUS];
3483 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
3484 for (y = 0; y < 3; y++)
3485 for (x = 0; x < 3; x++)
3486 lev->eater_array[i][y * 3 + x] =
3487 map_element_RND_to_EM(level->yamyam_content[i].e[x][y]);
3489 lev->amoeba_time = level->amoeba_speed;
3490 lev->wonderwall_time_initial = level->time_magic_wall;
3491 lev->wheel_time = level->time_wheel;
3493 lev->android_move_time = level->android_move_time;
3494 lev->android_clone_time = level->android_clone_time;
3495 lev->ball_random = level->ball_random;
3496 lev->ball_state_initial = level->ball_state_initial;
3497 lev->ball_time = level->ball_time;
3498 lev->num_ball_arrays = level->num_ball_contents;
3500 lev->lenses_score = level->lenses_score;
3501 lev->magnify_score = level->magnify_score;
3502 lev->slurp_score = level->slurp_score;
3504 lev->lenses_time = level->lenses_time;
3505 lev->magnify_time = level->magnify_time;
3507 lev->wind_direction_initial =
3508 map_direction_RND_to_EM(level->wind_direction_initial);
3509 lev->wind_cnt_initial = (level->wind_direction_initial != MV_NONE ?
3510 lev->wind_time : 0);
3512 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
3513 for (j = 0; j < 8; j++)
3514 lev->ball_array[i][j] =
3515 map_element_RND_to_EM(level->
3516 ball_content[i].e[ball_xy[j][0]][ball_xy[j][1]]);
3518 map_android_clone_elements_RND_to_EM(level);
3520 /* first fill the complete playfield with the default border element */
3521 for (y = 0; y < EM_MAX_CAVE_HEIGHT; y++)
3522 for (x = 0; x < EM_MAX_CAVE_WIDTH; x++)
3523 level_em->cave[x][y] = ZBORDER;
3525 if (BorderElement == EL_STEELWALL)
3527 for (y = 0; y < lev->height + 2; y++)
3528 for (x = 0; x < lev->width + 2; x++)
3529 level_em->cave[x + 1][y + 1] = map_element_RND_to_EM(EL_STEELWALL);
3532 /* then copy the real level contents from level file into the playfield */
3533 for (y = 0; y < lev->height; y++) for (x = 0; x < lev->width; x++)
3535 int new_element = map_element_RND_to_EM(level->field[x][y]);
3536 int offset = (BorderElement == EL_STEELWALL ? 1 : 0);
3537 int xx = x + 1 + offset;
3538 int yy = y + 1 + offset;
3540 if (level->field[x][y] == EL_AMOEBA_DEAD)
3541 new_element = map_element_RND_to_EM(EL_AMOEBA_WET);
3543 level_em->cave[xx][yy] = new_element;
3546 for (i = 0; i < MAX_PLAYERS; i++)
3548 ply[i]->x_initial = 0;
3549 ply[i]->y_initial = 0;
3552 /* initialize player positions and delete players from the playfield */
3553 for (y = 0; y < lev->height; y++) for (x = 0; x < lev->width; x++)
3555 if (ELEM_IS_PLAYER(level->field[x][y]))
3557 int player_nr = GET_PLAYER_NR(level->field[x][y]);
3558 int offset = (BorderElement == EL_STEELWALL ? 1 : 0);
3559 int xx = x + 1 + offset;
3560 int yy = y + 1 + offset;
3562 ply[player_nr]->x_initial = xx;
3563 ply[player_nr]->y_initial = yy;
3565 level_em->cave[xx][yy] = map_element_RND_to_EM(EL_EMPTY);
3569 if (BorderElement == EL_STEELWALL)
3576 void CopyNativeLevel_EM_to_RND(struct LevelInfo *level)
3578 static int ball_xy[8][2] =
3589 struct LevelInfo_EM *level_em = level->native_em_level;
3590 struct LEVEL *lev = level_em->lev;
3591 struct PLAYER **ply = level_em->ply;
3594 level->fieldx = MIN(lev->width, MAX_LEV_FIELDX);
3595 level->fieldy = MIN(lev->height, MAX_LEV_FIELDY);
3597 level->time = lev->time_seconds;
3598 level->gems_needed = lev->required_initial;
3600 sprintf(level->name, "Level %d", level->file_info.nr);
3602 level->score[SC_EMERALD] = lev->emerald_score;
3603 level->score[SC_DIAMOND] = lev->diamond_score;
3604 level->score[SC_ROBOT] = lev->alien_score;
3605 level->score[SC_SPACESHIP] = lev->tank_score;
3606 level->score[SC_BUG] = lev->bug_score;
3607 level->score[SC_YAMYAM] = lev->eater_score;
3608 level->score[SC_NUT] = lev->nut_score;
3609 level->score[SC_DYNAMITE] = lev->dynamite_score;
3610 level->score[SC_KEY] = lev->key_score;
3611 level->score[SC_TIME_BONUS] = lev->exit_score;
3613 level->num_yamyam_contents = MAX_ELEMENT_CONTENTS;
3615 for (i = 0; i < level->num_yamyam_contents; i++)
3616 for (y = 0; y < 3; y++)
3617 for (x = 0; x < 3; x++)
3618 level->yamyam_content[i].e[x][y] =
3619 map_element_EM_to_RND(lev->eater_array[i][y * 3 + x]);
3621 level->amoeba_speed = lev->amoeba_time;
3622 level->time_magic_wall = lev->wonderwall_time_initial;
3623 level->time_wheel = lev->wheel_time;
3625 level->android_move_time = lev->android_move_time;
3626 level->android_clone_time = lev->android_clone_time;
3627 level->ball_random = lev->ball_random;
3628 level->ball_state_initial = lev->ball_state_initial;
3629 level->ball_time = lev->ball_time;
3630 level->num_ball_contents = lev->num_ball_arrays;
3632 level->lenses_score = lev->lenses_score;
3633 level->magnify_score = lev->magnify_score;
3634 level->slurp_score = lev->slurp_score;
3636 level->lenses_time = lev->lenses_time;
3637 level->magnify_time = lev->magnify_time;
3639 level->wind_direction_initial =
3640 map_direction_EM_to_RND(lev->wind_direction_initial);
3642 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
3643 for (j = 0; j < 8; j++)
3644 level->ball_content[i].e[ball_xy[j][0]][ball_xy[j][1]] =
3645 map_element_EM_to_RND(lev->ball_array[i][j]);
3647 map_android_clone_elements_EM_to_RND(level);
3649 /* convert the playfield (some elements need special treatment) */
3650 for (y = 0; y < level->fieldy; y++) for (x = 0; x < level->fieldx; x++)
3652 int new_element = map_element_EM_to_RND(level_em->cave[x + 1][y + 1]);
3654 if (new_element == EL_AMOEBA_WET && level->amoeba_speed == 0)
3655 new_element = EL_AMOEBA_DEAD;
3657 level->field[x][y] = new_element;
3660 for (i = 0; i < MAX_PLAYERS; i++)
3662 /* in case of all players set to the same field, use the first player */
3663 int nr = MAX_PLAYERS - i - 1;
3664 int jx = ply[nr]->x_initial - 1;
3665 int jy = ply[nr]->y_initial - 1;
3667 if (jx != -1 && jy != -1)
3668 level->field[jx][jy] = EL_PLAYER_1 + nr;
3673 /* ------------------------------------------------------------------------- */
3674 /* functions for loading SP level */
3675 /* ------------------------------------------------------------------------- */
3677 void CopyNativeLevel_RND_to_SP(struct LevelInfo *level)
3679 struct LevelInfo_SP *level_sp = level->native_sp_level;
3680 LevelInfoType *header = &level_sp->header;
3683 level_sp->width = level->fieldx;
3684 level_sp->height = level->fieldy;
3686 for (x = 0; x < level->fieldx; x++)
3687 for (y = 0; y < level->fieldy; y++)
3688 level_sp->playfield[x][y] = map_element_RND_to_SP(level->field[x][y]);
3690 header->InitialGravity = (level->initial_player_gravity[0] ? 1 : 0);
3692 for (i = 0; i < SP_LEVEL_NAME_LEN; i++)
3693 header->LevelTitle[i] = level->name[i];
3694 /* !!! NO STRING TERMINATION IN SUPAPLEX VB CODE YET -- FIX THIS !!! */
3696 header->InfotronsNeeded = level->gems_needed;
3698 header->SpecialPortCount = 0;
3700 for (x = 0; x < level->fieldx; x++) for (y = 0; y < level->fieldy; y++)
3702 boolean gravity_port_found = FALSE;
3703 boolean gravity_port_valid = FALSE;
3704 int gravity_port_flag;
3705 int gravity_port_base_element;
3706 int element = level->field[x][y];
3708 if (element >= EL_SP_GRAVITY_ON_PORT_RIGHT &&
3709 element <= EL_SP_GRAVITY_ON_PORT_UP)
3711 gravity_port_found = TRUE;
3712 gravity_port_valid = TRUE;
3713 gravity_port_flag = 1;
3714 gravity_port_base_element = EL_SP_GRAVITY_ON_PORT_RIGHT;
3716 else if (element >= EL_SP_GRAVITY_OFF_PORT_RIGHT &&
3717 element <= EL_SP_GRAVITY_OFF_PORT_UP)
3719 gravity_port_found = TRUE;
3720 gravity_port_valid = TRUE;
3721 gravity_port_flag = 0;
3722 gravity_port_base_element = EL_SP_GRAVITY_OFF_PORT_RIGHT;
3724 else if (element >= EL_SP_GRAVITY_PORT_RIGHT &&
3725 element <= EL_SP_GRAVITY_PORT_UP)
3727 /* change R'n'D style gravity inverting special port to normal port
3728 (there are no gravity inverting ports in native Supaplex engine) */
3730 gravity_port_found = TRUE;
3731 gravity_port_valid = FALSE;
3732 gravity_port_base_element = EL_SP_GRAVITY_PORT_RIGHT;
3735 if (gravity_port_found)
3737 if (gravity_port_valid &&
3738 header->SpecialPortCount < SP_MAX_SPECIAL_PORTS)
3740 SpecialPortType *port = &header->SpecialPort[header->SpecialPortCount];
3742 port->PortLocation = (y * level->fieldx + x) * 2;
3743 port->Gravity = gravity_port_flag;
3745 element += EL_SP_GRAVITY_PORT_RIGHT - gravity_port_base_element;
3747 header->SpecialPortCount++;
3751 /* change special gravity port to normal port */
3753 element += EL_SP_PORT_RIGHT - gravity_port_base_element;
3756 level_sp->playfield[x][y] = element - EL_SP_START;
3761 void CopyNativeLevel_SP_to_RND(struct LevelInfo *level)
3763 struct LevelInfo_SP *level_sp = level->native_sp_level;
3764 LevelInfoType *header = &level_sp->header;
3765 boolean num_invalid_elements = 0;
3768 level->fieldx = level_sp->width;
3769 level->fieldy = level_sp->height;
3771 for (x = 0; x < level->fieldx; x++)
3773 for (y = 0; y < level->fieldy; y++)
3775 int element_old = level_sp->playfield[x][y];
3776 int element_new = getMappedElement(map_element_SP_to_RND(element_old));
3778 if (element_new == EL_UNKNOWN)
3780 num_invalid_elements++;
3782 Error(ERR_DEBUG, "invalid element %d at position %d, %d",
3786 level->field[x][y] = element_new;
3790 if (num_invalid_elements > 0)
3791 Error(ERR_WARN, "found %d invalid elements%s", num_invalid_elements,
3792 (!options.debug ? " (use '--debug' for more details)" : ""));
3794 for (i = 0; i < MAX_PLAYERS; i++)
3795 level->initial_player_gravity[i] =
3796 (header->InitialGravity == 1 ? TRUE : FALSE);
3798 /* skip leading spaces */
3799 for (i = 0; i < SP_LEVEL_NAME_LEN; i++)
3800 if (header->LevelTitle[i] != ' ')
3803 /* copy level title */
3804 for (j = 0; i < SP_LEVEL_NAME_LEN; i++, j++)
3805 level->name[j] = header->LevelTitle[i];
3806 level->name[j] = '\0';
3808 /* cut trailing spaces */
3810 if (level->name[j - 1] == ' ' && level->name[j] == '\0')
3811 level->name[j - 1] = '\0';
3813 level->gems_needed = header->InfotronsNeeded;
3815 for (i = 0; i < header->SpecialPortCount; i++)
3817 SpecialPortType *port = &header->SpecialPort[i];
3818 int port_location = port->PortLocation;
3819 int gravity = port->Gravity;
3820 int port_x, port_y, port_element;
3822 port_x = (port_location / 2) % level->fieldx;
3823 port_y = (port_location / 2) / level->fieldx;
3825 if (port_x < 0 || port_x >= level->fieldx ||
3826 port_y < 0 || port_y >= level->fieldy)
3828 Error(ERR_WARN, "special port position (%d, %d) out of bounds",
3834 port_element = level->field[port_x][port_y];
3836 if (port_element < EL_SP_GRAVITY_PORT_RIGHT ||
3837 port_element > EL_SP_GRAVITY_PORT_UP)
3839 Error(ERR_WARN, "no special port at position (%d, %d)", port_x, port_y);
3844 /* change previous (wrong) gravity inverting special port to either
3845 gravity enabling special port or gravity disabling special port */
3846 level->field[port_x][port_y] +=
3847 (gravity == 1 ? EL_SP_GRAVITY_ON_PORT_RIGHT :
3848 EL_SP_GRAVITY_OFF_PORT_RIGHT) - EL_SP_GRAVITY_PORT_RIGHT;
3851 /* change special gravity ports without database entries to normal ports */
3852 for (x = 0; x < level->fieldx; x++)
3853 for (y = 0; y < level->fieldy; y++)
3854 if (level->field[x][y] >= EL_SP_GRAVITY_PORT_RIGHT &&
3855 level->field[x][y] <= EL_SP_GRAVITY_PORT_UP)
3856 level->field[x][y] += EL_SP_PORT_RIGHT - EL_SP_GRAVITY_PORT_RIGHT;
3858 level->time = 0; /* no time limit */
3859 level->amoeba_speed = 0;
3860 level->time_magic_wall = 0;
3861 level->time_wheel = 0;
3862 level->amoeba_content = EL_EMPTY;
3865 /* original Supaplex does not use score values -- use default values */
3867 for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
3868 level->score[i] = 0;
3871 /* there are no yamyams in supaplex levels */
3872 for (i = 0; i < level->num_yamyam_contents; i++)
3873 for (x = 0; x < 3; x++)
3874 for (y = 0; y < 3; y++)
3875 level->yamyam_content[i].e[x][y] = EL_EMPTY;
3878 static void CopyNativeTape_RND_to_SP(struct LevelInfo *level)
3880 struct LevelInfo_SP *level_sp = level->native_sp_level;
3881 struct DemoInfo_SP *demo = &level_sp->demo;
3884 /* always start with reliable default values */
3885 demo->is_available = FALSE;
3888 if (TAPE_IS_EMPTY(tape))
3891 demo->level_nr = tape.level_nr; /* (currently not used) */
3893 level_sp->header.DemoRandomSeed = tape.random_seed;
3897 for (i = 0; i < tape.length; i++)
3899 int demo_action = map_key_RND_to_SP(tape.pos[i].action[0]);
3900 int demo_repeat = tape.pos[i].delay;
3901 int demo_entries = (demo_repeat + 15) / 16;
3903 if (demo->length + demo_entries >= SP_MAX_TAPE_LEN)
3905 Error(ERR_WARN, "tape truncated: size exceeds maximum SP demo size %d",
3911 for (j = 0; j < demo_repeat / 16; j++)
3912 demo->data[demo->length++] = 0xf0 | demo_action;
3914 if (demo_repeat % 16)
3915 demo->data[demo->length++] = ((demo_repeat % 16 - 1) << 4) | demo_action;
3918 demo->is_available = TRUE;
3921 static void setTapeInfoToDefaults();
3923 static void CopyNativeTape_SP_to_RND(struct LevelInfo *level)
3925 struct LevelInfo_SP *level_sp = level->native_sp_level;
3926 struct DemoInfo_SP *demo = &level_sp->demo;
3927 char *filename = level->file_info.filename;
3930 /* always start with reliable default values */
3931 setTapeInfoToDefaults();
3933 if (!demo->is_available)
3936 tape.level_nr = demo->level_nr; /* (currently not used) */
3937 tape.random_seed = level_sp->header.DemoRandomSeed;
3939 TapeSetDateFromEpochSeconds(getFileTimestampEpochSeconds(filename));
3942 tape.pos[tape.counter].delay = 0;
3944 for (i = 0; i < demo->length; i++)
3946 int demo_action = demo->data[i] & 0x0f;
3947 int demo_repeat = (demo->data[i] & 0xf0) >> 4;
3948 int tape_action = map_key_SP_to_RND(demo_action);
3949 int tape_repeat = demo_repeat + 1;
3950 byte action[MAX_PLAYERS] = { tape_action, 0, 0, 0 };
3951 boolean success = 0;
3954 for (j = 0; j < tape_repeat; j++)
3955 success = TapeAddAction(action);
3959 Error(ERR_WARN, "SP demo truncated: size exceeds maximum tape size %d",
3966 TapeHaltRecording();
3970 /* ------------------------------------------------------------------------- */
3971 /* functions for loading MM level */
3972 /* ------------------------------------------------------------------------- */
3974 void CopyNativeLevel_RND_to_MM(struct LevelInfo *level)
3976 struct LevelInfo_MM *level_mm = level->native_mm_level;
3979 level_mm->fieldx = MIN(level->fieldx, MM_MAX_PLAYFIELD_WIDTH);
3980 level_mm->fieldy = MIN(level->fieldy, MM_MAX_PLAYFIELD_HEIGHT);
3982 level_mm->time = level->time;
3983 level_mm->kettles_needed = level->gems_needed;
3984 level_mm->auto_count_kettles = level->auto_count_gems;
3986 level_mm->laser_red = level->mm_laser_red;
3987 level_mm->laser_green = level->mm_laser_green;
3988 level_mm->laser_blue = level->mm_laser_blue;
3990 strcpy(level_mm->name, level->name);
3991 strcpy(level_mm->author, level->author);
3993 level_mm->score[SC_EMERALD] = level->score[SC_EMERALD];
3994 level_mm->score[SC_PACMAN] = level->score[SC_PACMAN];
3995 level_mm->score[SC_KEY] = level->score[SC_KEY];
3996 level_mm->score[SC_TIME_BONUS] = level->score[SC_TIME_BONUS];
3997 level_mm->score[SC_ELEM_BONUS] = level->score[SC_ELEM_BONUS];
3999 level_mm->amoeba_speed = level->amoeba_speed;
4000 level_mm->time_fuse = level->mm_time_fuse;
4002 for (x = 0; x < level->fieldx; x++)
4003 for (y = 0; y < level->fieldy; y++)
4005 level_mm->field[x][y] = map_element_RND_to_MM(level->field[x][y]);
4008 void CopyNativeLevel_MM_to_RND(struct LevelInfo *level)
4010 struct LevelInfo_MM *level_mm = level->native_mm_level;
4013 level->fieldx = MIN(level_mm->fieldx, MAX_LEV_FIELDX);
4014 level->fieldy = MIN(level_mm->fieldy, MAX_LEV_FIELDY);
4016 level->time = level_mm->time;
4017 level->gems_needed = level_mm->kettles_needed;
4018 level->auto_count_gems = level_mm->auto_count_kettles;
4020 level->mm_laser_red = level_mm->laser_red;
4021 level->mm_laser_green = level_mm->laser_green;
4022 level->mm_laser_blue = level_mm->laser_blue;
4024 strcpy(level->name, level_mm->name);
4026 /* only overwrite author from 'levelinfo.conf' if author defined in level */
4027 if (!strEqual(level_mm->author, ANONYMOUS_NAME))
4028 strcpy(level->author, level_mm->author);
4030 level->score[SC_EMERALD] = level_mm->score[SC_EMERALD];
4031 level->score[SC_PACMAN] = level_mm->score[SC_PACMAN];
4032 level->score[SC_KEY] = level_mm->score[SC_KEY];
4033 level->score[SC_TIME_BONUS] = level_mm->score[SC_TIME_BONUS];
4034 level->score[SC_ELEM_BONUS] = level_mm->score[SC_ELEM_BONUS];
4036 level->amoeba_speed = level_mm->amoeba_speed;
4037 level->mm_time_fuse = level_mm->time_fuse;
4039 for (x = 0; x < level->fieldx; x++)
4040 for (y = 0; y < level->fieldy; y++)
4041 level->field[x][y] = map_element_MM_to_RND(level_mm->field[x][y]);
4045 /* ------------------------------------------------------------------------- */
4046 /* functions for loading DC level */
4047 /* ------------------------------------------------------------------------- */
4049 #define DC_LEVEL_HEADER_SIZE 344
4051 unsigned short getDecodedWord_DC(unsigned short data_encoded, boolean init)
4053 static int last_data_encoded;
4057 int diff_hi, diff_lo;
4058 int data_hi, data_lo;
4059 unsigned short data_decoded;
4063 last_data_encoded = 0;
4070 diff = data_encoded - last_data_encoded;
4071 diff_hi = diff & ~0xff;
4072 diff_lo = diff & 0xff;
4076 data_hi = diff_hi - (offset1 << 8) + (offset2 & 0xff00);
4077 data_lo = (diff_lo + (data_hi >> 16)) & 0x00ff;
4078 data_hi = data_hi & 0xff00;
4080 data_decoded = data_hi | data_lo;
4082 last_data_encoded = data_encoded;
4084 offset1 = (offset1 + 1) % 31;
4085 offset2 = offset2 & 0xff;
4087 return data_decoded;
4090 int getMappedElement_DC(int element)
4098 /* 0x0117 - 0x036e: (?) */
4101 /* 0x042d - 0x0684: (?) */
4117 element = EL_CRYSTAL;
4120 case 0x0e77: /* quicksand (boulder) */
4121 element = EL_QUICKSAND_FAST_FULL;
4124 case 0x0e99: /* slow quicksand (boulder) */
4125 element = EL_QUICKSAND_FULL;
4129 element = EL_EM_EXIT_OPEN;
4133 element = EL_EM_EXIT_CLOSED;
4137 element = EL_EM_STEEL_EXIT_OPEN;
4141 element = EL_EM_STEEL_EXIT_CLOSED;
4144 case 0x0f4f: /* dynamite (lit 1) */
4145 element = EL_EM_DYNAMITE_ACTIVE;
4148 case 0x0f57: /* dynamite (lit 2) */
4149 element = EL_EM_DYNAMITE_ACTIVE;
4152 case 0x0f5f: /* dynamite (lit 3) */
4153 element = EL_EM_DYNAMITE_ACTIVE;
4156 case 0x0f67: /* dynamite (lit 4) */
4157 element = EL_EM_DYNAMITE_ACTIVE;
4164 element = EL_AMOEBA_WET;
4168 element = EL_AMOEBA_DROP;
4172 element = EL_DC_MAGIC_WALL;
4176 element = EL_SPACESHIP_UP;
4180 element = EL_SPACESHIP_DOWN;
4184 element = EL_SPACESHIP_LEFT;
4188 element = EL_SPACESHIP_RIGHT;
4192 element = EL_BUG_UP;
4196 element = EL_BUG_DOWN;
4200 element = EL_BUG_LEFT;
4204 element = EL_BUG_RIGHT;
4208 element = EL_MOLE_UP;
4212 element = EL_MOLE_DOWN;
4216 element = EL_MOLE_LEFT;
4220 element = EL_MOLE_RIGHT;
4228 element = EL_YAMYAM;
4232 element = EL_SWITCHGATE_OPEN;
4236 element = EL_SWITCHGATE_CLOSED;
4240 element = EL_DC_SWITCHGATE_SWITCH_UP;
4244 element = EL_TIMEGATE_CLOSED;
4247 case 0x144c: /* conveyor belt switch (green) */
4248 element = EL_CONVEYOR_BELT_3_SWITCH_MIDDLE;
4251 case 0x144f: /* conveyor belt switch (red) */
4252 element = EL_CONVEYOR_BELT_1_SWITCH_MIDDLE;
4255 case 0x1452: /* conveyor belt switch (blue) */
4256 element = EL_CONVEYOR_BELT_4_SWITCH_MIDDLE;
4260 element = EL_CONVEYOR_BELT_3_MIDDLE;
4264 element = EL_CONVEYOR_BELT_3_LEFT;
4268 element = EL_CONVEYOR_BELT_3_RIGHT;
4272 element = EL_CONVEYOR_BELT_1_MIDDLE;
4276 element = EL_CONVEYOR_BELT_1_LEFT;
4280 element = EL_CONVEYOR_BELT_1_RIGHT;
4284 element = EL_CONVEYOR_BELT_4_MIDDLE;
4288 element = EL_CONVEYOR_BELT_4_LEFT;
4292 element = EL_CONVEYOR_BELT_4_RIGHT;
4296 element = EL_EXPANDABLE_WALL_HORIZONTAL;
4300 element = EL_EXPANDABLE_WALL_VERTICAL;
4304 element = EL_EXPANDABLE_WALL_ANY;
4307 case 0x14ce: /* growing steel wall (left/right) */
4308 element = EL_EXPANDABLE_STEELWALL_HORIZONTAL;
4311 case 0x14df: /* growing steel wall (up/down) */
4312 element = EL_EXPANDABLE_STEELWALL_VERTICAL;
4315 case 0x14e8: /* growing steel wall (up/down/left/right) */
4316 element = EL_EXPANDABLE_STEELWALL_ANY;
4320 element = EL_SHIELD_DEADLY;
4324 element = EL_EXTRA_TIME;
4332 element = EL_EMPTY_SPACE;
4335 case 0x1578: /* quicksand (empty) */
4336 element = EL_QUICKSAND_FAST_EMPTY;
4339 case 0x1579: /* slow quicksand (empty) */
4340 element = EL_QUICKSAND_EMPTY;
4343 /* 0x157c - 0x158b: */
4346 /* 0x1590 - 0x159f: */
4347 /* EL_DC_LANDMINE */
4350 element = EL_EM_DYNAMITE;
4353 case 0x15a1: /* key (red) */
4354 element = EL_EM_KEY_1;
4357 case 0x15a2: /* key (yellow) */
4358 element = EL_EM_KEY_2;
4361 case 0x15a3: /* key (blue) */
4362 element = EL_EM_KEY_4;
4365 case 0x15a4: /* key (green) */
4366 element = EL_EM_KEY_3;
4369 case 0x15a5: /* key (white) */
4370 element = EL_DC_KEY_WHITE;
4374 element = EL_WALL_SLIPPERY;
4381 case 0x15a8: /* wall (not round) */
4385 case 0x15a9: /* (blue) */
4386 element = EL_CHAR_A;
4389 case 0x15aa: /* (blue) */
4390 element = EL_CHAR_B;
4393 case 0x15ab: /* (blue) */
4394 element = EL_CHAR_C;
4397 case 0x15ac: /* (blue) */
4398 element = EL_CHAR_D;
4401 case 0x15ad: /* (blue) */
4402 element = EL_CHAR_E;
4405 case 0x15ae: /* (blue) */
4406 element = EL_CHAR_F;
4409 case 0x15af: /* (blue) */
4410 element = EL_CHAR_G;
4413 case 0x15b0: /* (blue) */
4414 element = EL_CHAR_H;
4417 case 0x15b1: /* (blue) */
4418 element = EL_CHAR_I;
4421 case 0x15b2: /* (blue) */
4422 element = EL_CHAR_J;
4425 case 0x15b3: /* (blue) */
4426 element = EL_CHAR_K;
4429 case 0x15b4: /* (blue) */
4430 element = EL_CHAR_L;
4433 case 0x15b5: /* (blue) */
4434 element = EL_CHAR_M;
4437 case 0x15b6: /* (blue) */
4438 element = EL_CHAR_N;
4441 case 0x15b7: /* (blue) */
4442 element = EL_CHAR_O;
4445 case 0x15b8: /* (blue) */
4446 element = EL_CHAR_P;
4449 case 0x15b9: /* (blue) */
4450 element = EL_CHAR_Q;
4453 case 0x15ba: /* (blue) */
4454 element = EL_CHAR_R;
4457 case 0x15bb: /* (blue) */
4458 element = EL_CHAR_S;
4461 case 0x15bc: /* (blue) */
4462 element = EL_CHAR_T;
4465 case 0x15bd: /* (blue) */
4466 element = EL_CHAR_U;
4469 case 0x15be: /* (blue) */
4470 element = EL_CHAR_V;
4473 case 0x15bf: /* (blue) */
4474 element = EL_CHAR_W;
4477 case 0x15c0: /* (blue) */
4478 element = EL_CHAR_X;
4481 case 0x15c1: /* (blue) */
4482 element = EL_CHAR_Y;
4485 case 0x15c2: /* (blue) */
4486 element = EL_CHAR_Z;
4489 case 0x15c3: /* (blue) */
4490 element = EL_CHAR_AUMLAUT;
4493 case 0x15c4: /* (blue) */
4494 element = EL_CHAR_OUMLAUT;
4497 case 0x15c5: /* (blue) */
4498 element = EL_CHAR_UUMLAUT;
4501 case 0x15c6: /* (blue) */
4502 element = EL_CHAR_0;
4505 case 0x15c7: /* (blue) */
4506 element = EL_CHAR_1;
4509 case 0x15c8: /* (blue) */
4510 element = EL_CHAR_2;
4513 case 0x15c9: /* (blue) */
4514 element = EL_CHAR_3;
4517 case 0x15ca: /* (blue) */
4518 element = EL_CHAR_4;
4521 case 0x15cb: /* (blue) */
4522 element = EL_CHAR_5;
4525 case 0x15cc: /* (blue) */
4526 element = EL_CHAR_6;
4529 case 0x15cd: /* (blue) */
4530 element = EL_CHAR_7;
4533 case 0x15ce: /* (blue) */
4534 element = EL_CHAR_8;
4537 case 0x15cf: /* (blue) */
4538 element = EL_CHAR_9;
4541 case 0x15d0: /* (blue) */
4542 element = EL_CHAR_PERIOD;
4545 case 0x15d1: /* (blue) */
4546 element = EL_CHAR_EXCLAM;
4549 case 0x15d2: /* (blue) */
4550 element = EL_CHAR_COLON;
4553 case 0x15d3: /* (blue) */
4554 element = EL_CHAR_LESS;
4557 case 0x15d4: /* (blue) */
4558 element = EL_CHAR_GREATER;
4561 case 0x15d5: /* (blue) */
4562 element = EL_CHAR_QUESTION;
4565 case 0x15d6: /* (blue) */
4566 element = EL_CHAR_COPYRIGHT;
4569 case 0x15d7: /* (blue) */
4570 element = EL_CHAR_UP;
4573 case 0x15d8: /* (blue) */
4574 element = EL_CHAR_DOWN;
4577 case 0x15d9: /* (blue) */
4578 element = EL_CHAR_BUTTON;
4581 case 0x15da: /* (blue) */
4582 element = EL_CHAR_PLUS;
4585 case 0x15db: /* (blue) */
4586 element = EL_CHAR_MINUS;
4589 case 0x15dc: /* (blue) */
4590 element = EL_CHAR_APOSTROPHE;
4593 case 0x15dd: /* (blue) */
4594 element = EL_CHAR_PARENLEFT;
4597 case 0x15de: /* (blue) */
4598 element = EL_CHAR_PARENRIGHT;
4601 case 0x15df: /* (green) */
4602 element = EL_CHAR_A;
4605 case 0x15e0: /* (green) */
4606 element = EL_CHAR_B;
4609 case 0x15e1: /* (green) */
4610 element = EL_CHAR_C;
4613 case 0x15e2: /* (green) */
4614 element = EL_CHAR_D;
4617 case 0x15e3: /* (green) */
4618 element = EL_CHAR_E;
4621 case 0x15e4: /* (green) */
4622 element = EL_CHAR_F;
4625 case 0x15e5: /* (green) */
4626 element = EL_CHAR_G;
4629 case 0x15e6: /* (green) */
4630 element = EL_CHAR_H;
4633 case 0x15e7: /* (green) */
4634 element = EL_CHAR_I;
4637 case 0x15e8: /* (green) */
4638 element = EL_CHAR_J;
4641 case 0x15e9: /* (green) */
4642 element = EL_CHAR_K;
4645 case 0x15ea: /* (green) */
4646 element = EL_CHAR_L;
4649 case 0x15eb: /* (green) */
4650 element = EL_CHAR_M;
4653 case 0x15ec: /* (green) */
4654 element = EL_CHAR_N;
4657 case 0x15ed: /* (green) */
4658 element = EL_CHAR_O;
4661 case 0x15ee: /* (green) */
4662 element = EL_CHAR_P;
4665 case 0x15ef: /* (green) */
4666 element = EL_CHAR_Q;
4669 case 0x15f0: /* (green) */
4670 element = EL_CHAR_R;
4673 case 0x15f1: /* (green) */
4674 element = EL_CHAR_S;
4677 case 0x15f2: /* (green) */
4678 element = EL_CHAR_T;
4681 case 0x15f3: /* (green) */
4682 element = EL_CHAR_U;
4685 case 0x15f4: /* (green) */
4686 element = EL_CHAR_V;
4689 case 0x15f5: /* (green) */
4690 element = EL_CHAR_W;
4693 case 0x15f6: /* (green) */
4694 element = EL_CHAR_X;
4697 case 0x15f7: /* (green) */
4698 element = EL_CHAR_Y;
4701 case 0x15f8: /* (green) */
4702 element = EL_CHAR_Z;
4705 case 0x15f9: /* (green) */
4706 element = EL_CHAR_AUMLAUT;
4709 case 0x15fa: /* (green) */
4710 element = EL_CHAR_OUMLAUT;
4713 case 0x15fb: /* (green) */
4714 element = EL_CHAR_UUMLAUT;
4717 case 0x15fc: /* (green) */
4718 element = EL_CHAR_0;
4721 case 0x15fd: /* (green) */
4722 element = EL_CHAR_1;
4725 case 0x15fe: /* (green) */
4726 element = EL_CHAR_2;
4729 case 0x15ff: /* (green) */
4730 element = EL_CHAR_3;
4733 case 0x1600: /* (green) */
4734 element = EL_CHAR_4;
4737 case 0x1601: /* (green) */
4738 element = EL_CHAR_5;
4741 case 0x1602: /* (green) */
4742 element = EL_CHAR_6;
4745 case 0x1603: /* (green) */
4746 element = EL_CHAR_7;
4749 case 0x1604: /* (green) */
4750 element = EL_CHAR_8;
4753 case 0x1605: /* (green) */
4754 element = EL_CHAR_9;
4757 case 0x1606: /* (green) */
4758 element = EL_CHAR_PERIOD;
4761 case 0x1607: /* (green) */
4762 element = EL_CHAR_EXCLAM;
4765 case 0x1608: /* (green) */
4766 element = EL_CHAR_COLON;
4769 case 0x1609: /* (green) */
4770 element = EL_CHAR_LESS;
4773 case 0x160a: /* (green) */
4774 element = EL_CHAR_GREATER;
4777 case 0x160b: /* (green) */
4778 element = EL_CHAR_QUESTION;
4781 case 0x160c: /* (green) */
4782 element = EL_CHAR_COPYRIGHT;
4785 case 0x160d: /* (green) */
4786 element = EL_CHAR_UP;
4789 case 0x160e: /* (green) */
4790 element = EL_CHAR_DOWN;
4793 case 0x160f: /* (green) */
4794 element = EL_CHAR_BUTTON;
4797 case 0x1610: /* (green) */
4798 element = EL_CHAR_PLUS;
4801 case 0x1611: /* (green) */
4802 element = EL_CHAR_MINUS;
4805 case 0x1612: /* (green) */
4806 element = EL_CHAR_APOSTROPHE;
4809 case 0x1613: /* (green) */
4810 element = EL_CHAR_PARENLEFT;
4813 case 0x1614: /* (green) */
4814 element = EL_CHAR_PARENRIGHT;
4817 case 0x1615: /* (blue steel) */
4818 element = EL_STEEL_CHAR_A;
4821 case 0x1616: /* (blue steel) */
4822 element = EL_STEEL_CHAR_B;
4825 case 0x1617: /* (blue steel) */
4826 element = EL_STEEL_CHAR_C;
4829 case 0x1618: /* (blue steel) */
4830 element = EL_STEEL_CHAR_D;
4833 case 0x1619: /* (blue steel) */
4834 element = EL_STEEL_CHAR_E;
4837 case 0x161a: /* (blue steel) */
4838 element = EL_STEEL_CHAR_F;
4841 case 0x161b: /* (blue steel) */
4842 element = EL_STEEL_CHAR_G;
4845 case 0x161c: /* (blue steel) */
4846 element = EL_STEEL_CHAR_H;
4849 case 0x161d: /* (blue steel) */
4850 element = EL_STEEL_CHAR_I;
4853 case 0x161e: /* (blue steel) */
4854 element = EL_STEEL_CHAR_J;
4857 case 0x161f: /* (blue steel) */
4858 element = EL_STEEL_CHAR_K;
4861 case 0x1620: /* (blue steel) */
4862 element = EL_STEEL_CHAR_L;
4865 case 0x1621: /* (blue steel) */
4866 element = EL_STEEL_CHAR_M;
4869 case 0x1622: /* (blue steel) */
4870 element = EL_STEEL_CHAR_N;
4873 case 0x1623: /* (blue steel) */
4874 element = EL_STEEL_CHAR_O;
4877 case 0x1624: /* (blue steel) */
4878 element = EL_STEEL_CHAR_P;
4881 case 0x1625: /* (blue steel) */
4882 element = EL_STEEL_CHAR_Q;
4885 case 0x1626: /* (blue steel) */
4886 element = EL_STEEL_CHAR_R;
4889 case 0x1627: /* (blue steel) */
4890 element = EL_STEEL_CHAR_S;
4893 case 0x1628: /* (blue steel) */
4894 element = EL_STEEL_CHAR_T;
4897 case 0x1629: /* (blue steel) */
4898 element = EL_STEEL_CHAR_U;
4901 case 0x162a: /* (blue steel) */
4902 element = EL_STEEL_CHAR_V;
4905 case 0x162b: /* (blue steel) */
4906 element = EL_STEEL_CHAR_W;
4909 case 0x162c: /* (blue steel) */
4910 element = EL_STEEL_CHAR_X;
4913 case 0x162d: /* (blue steel) */
4914 element = EL_STEEL_CHAR_Y;
4917 case 0x162e: /* (blue steel) */
4918 element = EL_STEEL_CHAR_Z;
4921 case 0x162f: /* (blue steel) */
4922 element = EL_STEEL_CHAR_AUMLAUT;
4925 case 0x1630: /* (blue steel) */
4926 element = EL_STEEL_CHAR_OUMLAUT;
4929 case 0x1631: /* (blue steel) */
4930 element = EL_STEEL_CHAR_UUMLAUT;
4933 case 0x1632: /* (blue steel) */
4934 element = EL_STEEL_CHAR_0;
4937 case 0x1633: /* (blue steel) */
4938 element = EL_STEEL_CHAR_1;
4941 case 0x1634: /* (blue steel) */
4942 element = EL_STEEL_CHAR_2;
4945 case 0x1635: /* (blue steel) */
4946 element = EL_STEEL_CHAR_3;
4949 case 0x1636: /* (blue steel) */
4950 element = EL_STEEL_CHAR_4;
4953 case 0x1637: /* (blue steel) */
4954 element = EL_STEEL_CHAR_5;
4957 case 0x1638: /* (blue steel) */
4958 element = EL_STEEL_CHAR_6;
4961 case 0x1639: /* (blue steel) */
4962 element = EL_STEEL_CHAR_7;
4965 case 0x163a: /* (blue steel) */
4966 element = EL_STEEL_CHAR_8;
4969 case 0x163b: /* (blue steel) */
4970 element = EL_STEEL_CHAR_9;
4973 case 0x163c: /* (blue steel) */
4974 element = EL_STEEL_CHAR_PERIOD;
4977 case 0x163d: /* (blue steel) */
4978 element = EL_STEEL_CHAR_EXCLAM;
4981 case 0x163e: /* (blue steel) */
4982 element = EL_STEEL_CHAR_COLON;
4985 case 0x163f: /* (blue steel) */
4986 element = EL_STEEL_CHAR_LESS;
4989 case 0x1640: /* (blue steel) */
4990 element = EL_STEEL_CHAR_GREATER;
4993 case 0x1641: /* (blue steel) */
4994 element = EL_STEEL_CHAR_QUESTION;
4997 case 0x1642: /* (blue steel) */
4998 element = EL_STEEL_CHAR_COPYRIGHT;
5001 case 0x1643: /* (blue steel) */
5002 element = EL_STEEL_CHAR_UP;
5005 case 0x1644: /* (blue steel) */
5006 element = EL_STEEL_CHAR_DOWN;
5009 case 0x1645: /* (blue steel) */
5010 element = EL_STEEL_CHAR_BUTTON;
5013 case 0x1646: /* (blue steel) */
5014 element = EL_STEEL_CHAR_PLUS;
5017 case 0x1647: /* (blue steel) */
5018 element = EL_STEEL_CHAR_MINUS;
5021 case 0x1648: /* (blue steel) */
5022 element = EL_STEEL_CHAR_APOSTROPHE;
5025 case 0x1649: /* (blue steel) */
5026 element = EL_STEEL_CHAR_PARENLEFT;
5029 case 0x164a: /* (blue steel) */
5030 element = EL_STEEL_CHAR_PARENRIGHT;
5033 case 0x164b: /* (green steel) */
5034 element = EL_STEEL_CHAR_A;
5037 case 0x164c: /* (green steel) */
5038 element = EL_STEEL_CHAR_B;
5041 case 0x164d: /* (green steel) */
5042 element = EL_STEEL_CHAR_C;
5045 case 0x164e: /* (green steel) */
5046 element = EL_STEEL_CHAR_D;
5049 case 0x164f: /* (green steel) */
5050 element = EL_STEEL_CHAR_E;
5053 case 0x1650: /* (green steel) */
5054 element = EL_STEEL_CHAR_F;
5057 case 0x1651: /* (green steel) */
5058 element = EL_STEEL_CHAR_G;
5061 case 0x1652: /* (green steel) */
5062 element = EL_STEEL_CHAR_H;
5065 case 0x1653: /* (green steel) */
5066 element = EL_STEEL_CHAR_I;
5069 case 0x1654: /* (green steel) */
5070 element = EL_STEEL_CHAR_J;
5073 case 0x1655: /* (green steel) */
5074 element = EL_STEEL_CHAR_K;
5077 case 0x1656: /* (green steel) */
5078 element = EL_STEEL_CHAR_L;
5081 case 0x1657: /* (green steel) */
5082 element = EL_STEEL_CHAR_M;
5085 case 0x1658: /* (green steel) */
5086 element = EL_STEEL_CHAR_N;
5089 case 0x1659: /* (green steel) */
5090 element = EL_STEEL_CHAR_O;
5093 case 0x165a: /* (green steel) */
5094 element = EL_STEEL_CHAR_P;
5097 case 0x165b: /* (green steel) */
5098 element = EL_STEEL_CHAR_Q;
5101 case 0x165c: /* (green steel) */
5102 element = EL_STEEL_CHAR_R;
5105 case 0x165d: /* (green steel) */
5106 element = EL_STEEL_CHAR_S;
5109 case 0x165e: /* (green steel) */
5110 element = EL_STEEL_CHAR_T;
5113 case 0x165f: /* (green steel) */
5114 element = EL_STEEL_CHAR_U;
5117 case 0x1660: /* (green steel) */
5118 element = EL_STEEL_CHAR_V;
5121 case 0x1661: /* (green steel) */
5122 element = EL_STEEL_CHAR_W;
5125 case 0x1662: /* (green steel) */
5126 element = EL_STEEL_CHAR_X;
5129 case 0x1663: /* (green steel) */
5130 element = EL_STEEL_CHAR_Y;
5133 case 0x1664: /* (green steel) */
5134 element = EL_STEEL_CHAR_Z;
5137 case 0x1665: /* (green steel) */
5138 element = EL_STEEL_CHAR_AUMLAUT;
5141 case 0x1666: /* (green steel) */
5142 element = EL_STEEL_CHAR_OUMLAUT;
5145 case 0x1667: /* (green steel) */
5146 element = EL_STEEL_CHAR_UUMLAUT;
5149 case 0x1668: /* (green steel) */
5150 element = EL_STEEL_CHAR_0;
5153 case 0x1669: /* (green steel) */
5154 element = EL_STEEL_CHAR_1;
5157 case 0x166a: /* (green steel) */
5158 element = EL_STEEL_CHAR_2;
5161 case 0x166b: /* (green steel) */
5162 element = EL_STEEL_CHAR_3;
5165 case 0x166c: /* (green steel) */
5166 element = EL_STEEL_CHAR_4;
5169 case 0x166d: /* (green steel) */
5170 element = EL_STEEL_CHAR_5;
5173 case 0x166e: /* (green steel) */
5174 element = EL_STEEL_CHAR_6;
5177 case 0x166f: /* (green steel) */
5178 element = EL_STEEL_CHAR_7;
5181 case 0x1670: /* (green steel) */
5182 element = EL_STEEL_CHAR_8;
5185 case 0x1671: /* (green steel) */
5186 element = EL_STEEL_CHAR_9;
5189 case 0x1672: /* (green steel) */
5190 element = EL_STEEL_CHAR_PERIOD;
5193 case 0x1673: /* (green steel) */
5194 element = EL_STEEL_CHAR_EXCLAM;
5197 case 0x1674: /* (green steel) */
5198 element = EL_STEEL_CHAR_COLON;
5201 case 0x1675: /* (green steel) */
5202 element = EL_STEEL_CHAR_LESS;
5205 case 0x1676: /* (green steel) */
5206 element = EL_STEEL_CHAR_GREATER;
5209 case 0x1677: /* (green steel) */
5210 element = EL_STEEL_CHAR_QUESTION;
5213 case 0x1678: /* (green steel) */
5214 element = EL_STEEL_CHAR_COPYRIGHT;
5217 case 0x1679: /* (green steel) */
5218 element = EL_STEEL_CHAR_UP;
5221 case 0x167a: /* (green steel) */
5222 element = EL_STEEL_CHAR_DOWN;
5225 case 0x167b: /* (green steel) */
5226 element = EL_STEEL_CHAR_BUTTON;
5229 case 0x167c: /* (green steel) */
5230 element = EL_STEEL_CHAR_PLUS;
5233 case 0x167d: /* (green steel) */
5234 element = EL_STEEL_CHAR_MINUS;
5237 case 0x167e: /* (green steel) */
5238 element = EL_STEEL_CHAR_APOSTROPHE;
5241 case 0x167f: /* (green steel) */
5242 element = EL_STEEL_CHAR_PARENLEFT;
5245 case 0x1680: /* (green steel) */
5246 element = EL_STEEL_CHAR_PARENRIGHT;
5249 case 0x1681: /* gate (red) */
5250 element = EL_EM_GATE_1;
5253 case 0x1682: /* secret gate (red) */
5254 element = EL_GATE_1_GRAY;
5257 case 0x1683: /* gate (yellow) */
5258 element = EL_EM_GATE_2;
5261 case 0x1684: /* secret gate (yellow) */
5262 element = EL_GATE_2_GRAY;
5265 case 0x1685: /* gate (blue) */
5266 element = EL_EM_GATE_4;
5269 case 0x1686: /* secret gate (blue) */
5270 element = EL_GATE_4_GRAY;
5273 case 0x1687: /* gate (green) */
5274 element = EL_EM_GATE_3;
5277 case 0x1688: /* secret gate (green) */
5278 element = EL_GATE_3_GRAY;
5281 case 0x1689: /* gate (white) */
5282 element = EL_DC_GATE_WHITE;
5285 case 0x168a: /* secret gate (white) */
5286 element = EL_DC_GATE_WHITE_GRAY;
5289 case 0x168b: /* secret gate (no key) */
5290 element = EL_DC_GATE_FAKE_GRAY;
5294 element = EL_ROBOT_WHEEL;
5298 element = EL_DC_TIMEGATE_SWITCH;
5302 element = EL_ACID_POOL_BOTTOM;
5306 element = EL_ACID_POOL_TOPLEFT;
5310 element = EL_ACID_POOL_TOPRIGHT;
5314 element = EL_ACID_POOL_BOTTOMLEFT;
5318 element = EL_ACID_POOL_BOTTOMRIGHT;
5322 element = EL_STEELWALL;
5326 element = EL_STEELWALL_SLIPPERY;
5329 case 0x1695: /* steel wall (not round) */
5330 element = EL_STEELWALL;
5333 case 0x1696: /* steel wall (left) */
5334 element = EL_DC_STEELWALL_1_LEFT;
5337 case 0x1697: /* steel wall (bottom) */
5338 element = EL_DC_STEELWALL_1_BOTTOM;
5341 case 0x1698: /* steel wall (right) */
5342 element = EL_DC_STEELWALL_1_RIGHT;
5345 case 0x1699: /* steel wall (top) */
5346 element = EL_DC_STEELWALL_1_TOP;
5349 case 0x169a: /* steel wall (left/bottom) */
5350 element = EL_DC_STEELWALL_1_BOTTOMLEFT;
5353 case 0x169b: /* steel wall (right/bottom) */
5354 element = EL_DC_STEELWALL_1_BOTTOMRIGHT;
5357 case 0x169c: /* steel wall (right/top) */
5358 element = EL_DC_STEELWALL_1_TOPRIGHT;
5361 case 0x169d: /* steel wall (left/top) */
5362 element = EL_DC_STEELWALL_1_TOPLEFT;
5365 case 0x169e: /* steel wall (right/bottom small) */
5366 element = EL_DC_STEELWALL_1_BOTTOMRIGHT_2;
5369 case 0x169f: /* steel wall (left/bottom small) */
5370 element = EL_DC_STEELWALL_1_BOTTOMLEFT_2;
5373 case 0x16a0: /* steel wall (right/top small) */
5374 element = EL_DC_STEELWALL_1_TOPRIGHT_2;
5377 case 0x16a1: /* steel wall (left/top small) */
5378 element = EL_DC_STEELWALL_1_TOPLEFT_2;
5381 case 0x16a2: /* steel wall (left/right) */
5382 element = EL_DC_STEELWALL_1_VERTICAL;
5385 case 0x16a3: /* steel wall (top/bottom) */
5386 element = EL_DC_STEELWALL_1_HORIZONTAL;
5389 case 0x16a4: /* steel wall 2 (left end) */
5390 element = EL_DC_STEELWALL_2_LEFT;
5393 case 0x16a5: /* steel wall 2 (right end) */
5394 element = EL_DC_STEELWALL_2_RIGHT;
5397 case 0x16a6: /* steel wall 2 (top end) */
5398 element = EL_DC_STEELWALL_2_TOP;
5401 case 0x16a7: /* steel wall 2 (bottom end) */
5402 element = EL_DC_STEELWALL_2_BOTTOM;
5405 case 0x16a8: /* steel wall 2 (left/right) */
5406 element = EL_DC_STEELWALL_2_HORIZONTAL;
5409 case 0x16a9: /* steel wall 2 (up/down) */
5410 element = EL_DC_STEELWALL_2_VERTICAL;
5413 case 0x16aa: /* steel wall 2 (mid) */
5414 element = EL_DC_STEELWALL_2_MIDDLE;
5418 element = EL_SIGN_EXCLAMATION;
5422 element = EL_SIGN_RADIOACTIVITY;
5426 element = EL_SIGN_STOP;
5430 element = EL_SIGN_WHEELCHAIR;
5434 element = EL_SIGN_PARKING;
5438 element = EL_SIGN_NO_ENTRY;
5442 element = EL_SIGN_HEART;
5446 element = EL_SIGN_GIVE_WAY;
5450 element = EL_SIGN_ENTRY_FORBIDDEN;
5454 element = EL_SIGN_EMERGENCY_EXIT;
5458 element = EL_SIGN_YIN_YANG;
5462 element = EL_WALL_EMERALD;
5466 element = EL_WALL_DIAMOND;
5470 element = EL_WALL_PEARL;
5474 element = EL_WALL_CRYSTAL;
5478 element = EL_INVISIBLE_WALL;
5482 element = EL_INVISIBLE_STEELWALL;
5485 /* 0x16bc - 0x16cb: */
5486 /* EL_INVISIBLE_SAND */
5489 element = EL_LIGHT_SWITCH;
5493 element = EL_ENVELOPE_1;
5497 if (element >= 0x0117 && element <= 0x036e) /* (?) */
5498 element = EL_DIAMOND;
5499 else if (element >= 0x042d && element <= 0x0684) /* (?) */
5500 element = EL_EMERALD;
5501 else if (element >= 0x157c && element <= 0x158b)
5503 else if (element >= 0x1590 && element <= 0x159f)
5504 element = EL_DC_LANDMINE;
5505 else if (element >= 0x16bc && element <= 0x16cb)
5506 element = EL_INVISIBLE_SAND;
5509 Error(ERR_WARN, "unknown Diamond Caves element 0x%04x", element);
5510 element = EL_UNKNOWN;
5515 return getMappedElement(element);
5518 static void LoadLevelFromFileStream_DC(File *file, struct LevelInfo *level,
5521 byte header[DC_LEVEL_HEADER_SIZE];
5523 int envelope_header_pos = 62;
5524 int envelope_content_pos = 94;
5525 int level_name_pos = 251;
5526 int level_author_pos = 292;
5527 int envelope_header_len;
5528 int envelope_content_len;
5530 int level_author_len;
5532 int num_yamyam_contents;
5535 getDecodedWord_DC(0, TRUE); /* initialize DC2 decoding engine */
5537 for (i = 0; i < DC_LEVEL_HEADER_SIZE / 2; i++)
5539 unsigned short header_word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
5541 header[i * 2 + 0] = header_word >> 8;
5542 header[i * 2 + 1] = header_word & 0xff;
5545 /* read some values from level header to check level decoding integrity */
5546 fieldx = header[6] | (header[7] << 8);
5547 fieldy = header[8] | (header[9] << 8);
5548 num_yamyam_contents = header[60] | (header[61] << 8);
5550 /* do some simple sanity checks to ensure that level was correctly decoded */
5551 if (fieldx < 1 || fieldx > 256 ||
5552 fieldy < 1 || fieldy > 256 ||
5553 num_yamyam_contents < 1 || num_yamyam_contents > 8)
5555 level->no_valid_file = TRUE;
5557 Error(ERR_WARN, "cannot decode level from stream -- using empty level");
5562 /* maximum envelope header size is 31 bytes */
5563 envelope_header_len = header[envelope_header_pos];
5564 /* maximum envelope content size is 110 (156?) bytes */
5565 envelope_content_len = header[envelope_content_pos];
5567 /* maximum level title size is 40 bytes */
5568 level_name_len = MIN(header[level_name_pos], MAX_LEVEL_NAME_LEN);
5569 /* maximum level author size is 30 (51?) bytes */
5570 level_author_len = MIN(header[level_author_pos], MAX_LEVEL_AUTHOR_LEN);
5574 for (i = 0; i < envelope_header_len; i++)
5575 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
5576 level->envelope[0].text[envelope_size++] =
5577 header[envelope_header_pos + 1 + i];
5579 if (envelope_header_len > 0 && envelope_content_len > 0)
5581 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
5582 level->envelope[0].text[envelope_size++] = '\n';
5583 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
5584 level->envelope[0].text[envelope_size++] = '\n';
5587 for (i = 0; i < envelope_content_len; i++)
5588 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
5589 level->envelope[0].text[envelope_size++] =
5590 header[envelope_content_pos + 1 + i];
5592 level->envelope[0].text[envelope_size] = '\0';
5594 level->envelope[0].xsize = MAX_ENVELOPE_XSIZE;
5595 level->envelope[0].ysize = 10;
5596 level->envelope[0].autowrap = TRUE;
5597 level->envelope[0].centered = TRUE;
5599 for (i = 0; i < level_name_len; i++)
5600 level->name[i] = header[level_name_pos + 1 + i];
5601 level->name[level_name_len] = '\0';
5603 for (i = 0; i < level_author_len; i++)
5604 level->author[i] = header[level_author_pos + 1 + i];
5605 level->author[level_author_len] = '\0';
5607 num_yamyam_contents = header[60] | (header[61] << 8);
5608 level->num_yamyam_contents =
5609 MIN(MAX(MIN_ELEMENT_CONTENTS, num_yamyam_contents), MAX_ELEMENT_CONTENTS);
5611 for (i = 0; i < num_yamyam_contents; i++)
5613 for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
5615 unsigned short word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
5616 int element_dc = ((word & 0xff) << 8) | ((word >> 8) & 0xff);
5618 if (i < MAX_ELEMENT_CONTENTS)
5619 level->yamyam_content[i].e[x][y] = getMappedElement_DC(element_dc);
5623 fieldx = header[6] | (header[7] << 8);
5624 fieldy = header[8] | (header[9] << 8);
5625 level->fieldx = MIN(MAX(MIN_LEV_FIELDX, fieldx), MAX_LEV_FIELDX);
5626 level->fieldy = MIN(MAX(MIN_LEV_FIELDY, fieldy), MAX_LEV_FIELDY);
5628 for (y = 0; y < fieldy; y++) for (x = 0; x < fieldx; x++)
5630 unsigned short word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
5631 int element_dc = ((word & 0xff) << 8) | ((word >> 8) & 0xff);
5633 if (x < MAX_LEV_FIELDX && y < MAX_LEV_FIELDY)
5634 level->field[x][y] = getMappedElement_DC(element_dc);
5637 x = MIN(MAX(0, (header[10] | (header[11] << 8)) - 1), MAX_LEV_FIELDX - 1);
5638 y = MIN(MAX(0, (header[12] | (header[13] << 8)) - 1), MAX_LEV_FIELDY - 1);
5639 level->field[x][y] = EL_PLAYER_1;
5641 x = MIN(MAX(0, (header[14] | (header[15] << 8)) - 1), MAX_LEV_FIELDX - 1);
5642 y = MIN(MAX(0, (header[16] | (header[17] << 8)) - 1), MAX_LEV_FIELDY - 1);
5643 level->field[x][y] = EL_PLAYER_2;
5645 level->gems_needed = header[18] | (header[19] << 8);
5647 level->score[SC_EMERALD] = header[20] | (header[21] << 8);
5648 level->score[SC_DIAMOND] = header[22] | (header[23] << 8);
5649 level->score[SC_PEARL] = header[24] | (header[25] << 8);
5650 level->score[SC_CRYSTAL] = header[26] | (header[27] << 8);
5651 level->score[SC_NUT] = header[28] | (header[29] << 8);
5652 level->score[SC_ROBOT] = header[30] | (header[31] << 8);
5653 level->score[SC_SPACESHIP] = header[32] | (header[33] << 8);
5654 level->score[SC_BUG] = header[34] | (header[35] << 8);
5655 level->score[SC_YAMYAM] = header[36] | (header[37] << 8);
5656 level->score[SC_DYNAMITE] = header[38] | (header[39] << 8);
5657 level->score[SC_KEY] = header[40] | (header[41] << 8);
5658 level->score[SC_TIME_BONUS] = header[42] | (header[43] << 8);
5660 level->time = header[44] | (header[45] << 8);
5662 level->amoeba_speed = header[46] | (header[47] << 8);
5663 level->time_light = header[48] | (header[49] << 8);
5664 level->time_timegate = header[50] | (header[51] << 8);
5665 level->time_wheel = header[52] | (header[53] << 8);
5666 level->time_magic_wall = header[54] | (header[55] << 8);
5667 level->extra_time = header[56] | (header[57] << 8);
5668 level->shield_normal_time = header[58] | (header[59] << 8);
5670 /* Diamond Caves has the same (strange) behaviour as Emerald Mine that gems
5671 can slip down from flat walls, like normal walls and steel walls */
5672 level->em_slippery_gems = TRUE;
5675 static void LoadLevelFromFileInfo_DC(struct LevelInfo *level,
5676 struct LevelFileInfo *level_file_info,
5677 boolean level_info_only)
5679 char *filename = level_file_info->filename;
5681 int num_magic_bytes = 8;
5682 char magic_bytes[num_magic_bytes + 1];
5683 int num_levels_to_skip = level_file_info->nr - leveldir_current->first_level;
5685 if (!(file = openFile(filename, MODE_READ)))
5687 level->no_valid_file = TRUE;
5689 if (!level_info_only)
5690 Error(ERR_WARN, "cannot read level '%s' -- using empty level", filename);
5695 // fseek(file, 0x0000, SEEK_SET);
5697 if (level_file_info->packed)
5699 /* read "magic bytes" from start of file */
5700 if (getStringFromFile(file, magic_bytes, num_magic_bytes + 1) == NULL)
5701 magic_bytes[0] = '\0';
5703 /* check "magic bytes" for correct file format */
5704 if (!strPrefix(magic_bytes, "DC2"))
5706 level->no_valid_file = TRUE;
5708 Error(ERR_WARN, "unknown DC level file '%s' -- using empty level",
5714 if (strPrefix(magic_bytes, "DC2Win95") ||
5715 strPrefix(magic_bytes, "DC2Win98"))
5717 int position_first_level = 0x00fa;
5718 int extra_bytes = 4;
5721 /* advance file stream to first level inside the level package */
5722 skip_bytes = position_first_level - num_magic_bytes - extra_bytes;
5724 /* each block of level data is followed by block of non-level data */
5725 num_levels_to_skip *= 2;
5727 /* at least skip header bytes, therefore use ">= 0" instead of "> 0" */
5728 while (num_levels_to_skip >= 0)
5730 /* advance file stream to next level inside the level package */
5731 if (seekFile(file, skip_bytes, SEEK_CUR) != 0)
5733 level->no_valid_file = TRUE;
5735 Error(ERR_WARN, "cannot fseek in file '%s' -- using empty level",
5741 /* skip apparently unused extra bytes following each level */
5742 ReadUnusedBytesFromFile(file, extra_bytes);
5744 /* read size of next level in level package */
5745 skip_bytes = getFile32BitLE(file);
5747 num_levels_to_skip--;
5752 level->no_valid_file = TRUE;
5754 Error(ERR_WARN, "unknown DC2 level file '%s' -- using empty level",
5761 LoadLevelFromFileStream_DC(file, level, level_file_info->nr);
5767 /* ------------------------------------------------------------------------- */
5768 /* functions for loading SB level */
5769 /* ------------------------------------------------------------------------- */
5771 int getMappedElement_SB(int element_ascii, boolean use_ces)
5779 sb_element_mapping[] =
5781 { ' ', EL_EMPTY, EL_CUSTOM_1 }, /* floor (space) */
5782 { '#', EL_STEELWALL, EL_CUSTOM_2 }, /* wall */
5783 { '@', EL_PLAYER_1, EL_CUSTOM_3 }, /* player */
5784 { '$', EL_SOKOBAN_OBJECT, EL_CUSTOM_4 }, /* box */
5785 { '.', EL_SOKOBAN_FIELD_EMPTY, EL_CUSTOM_5 }, /* goal square */
5786 { '*', EL_SOKOBAN_FIELD_FULL, EL_CUSTOM_6 }, /* box on goal square */
5787 { '+', EL_SOKOBAN_FIELD_PLAYER, EL_CUSTOM_7 }, /* player on goal square */
5788 { '_', EL_INVISIBLE_STEELWALL, EL_FROM_LEVEL_TEMPLATE }, /* floor beyond border */
5795 for (i = 0; sb_element_mapping[i].ascii != 0; i++)
5796 if (element_ascii == sb_element_mapping[i].ascii)
5797 return (use_ces ? sb_element_mapping[i].ce : sb_element_mapping[i].sb);
5799 return EL_UNDEFINED;
5802 static void LoadLevelFromFileInfo_SB(struct LevelInfo *level,
5803 struct LevelFileInfo *level_file_info,
5804 boolean level_info_only)
5806 char *filename = level_file_info->filename;
5807 char line[MAX_LINE_LEN], line_raw[MAX_LINE_LEN], previous_line[MAX_LINE_LEN];
5808 char last_comment[MAX_LINE_LEN];
5809 char level_name[MAX_LINE_LEN];
5812 int num_levels_to_skip = level_file_info->nr - leveldir_current->first_level;
5813 boolean read_continued_line = FALSE;
5814 boolean reading_playfield = FALSE;
5815 boolean got_valid_playfield_line = FALSE;
5816 boolean invalid_playfield_char = FALSE;
5817 boolean load_xsb_to_ces = check_special_flags("load_xsb_to_ces");
5818 int file_level_nr = 0;
5820 int x = 0, y = 0; /* initialized to make compilers happy */
5822 last_comment[0] = '\0';
5823 level_name[0] = '\0';
5825 if (!(file = openFile(filename, MODE_READ)))
5827 level->no_valid_file = TRUE;
5829 if (!level_info_only)
5830 Error(ERR_WARN, "cannot read level '%s' -- using empty level", filename);
5835 while (!checkEndOfFile(file))
5837 /* level successfully read, but next level may follow here */
5838 if (!got_valid_playfield_line && reading_playfield)
5840 /* read playfield from single level file -- skip remaining file */
5841 if (!level_file_info->packed)
5844 if (file_level_nr >= num_levels_to_skip)
5849 last_comment[0] = '\0';
5850 level_name[0] = '\0';
5852 reading_playfield = FALSE;
5855 got_valid_playfield_line = FALSE;
5857 /* read next line of input file */
5858 if (!getStringFromFile(file, line, MAX_LINE_LEN))
5861 /* check if line was completely read and is terminated by line break */
5862 if (strlen(line) > 0 && line[strlen(line) - 1] == '\n')
5865 /* cut trailing line break (this can be newline and/or carriage return) */
5866 for (line_ptr = &line[strlen(line)]; line_ptr >= line; line_ptr--)
5867 if ((*line_ptr == '\n' || *line_ptr == '\r') && *(line_ptr + 1) == '\0')
5870 /* copy raw input line for later use (mainly debugging output) */
5871 strcpy(line_raw, line);
5873 if (read_continued_line)
5875 /* append new line to existing line, if there is enough space */
5876 if (strlen(previous_line) + strlen(line_ptr) < MAX_LINE_LEN)
5877 strcat(previous_line, line_ptr);
5879 strcpy(line, previous_line); /* copy storage buffer to line */
5881 read_continued_line = FALSE;
5884 /* if the last character is '\', continue at next line */
5885 if (strlen(line) > 0 && line[strlen(line) - 1] == '\\')
5887 line[strlen(line) - 1] = '\0'; /* cut off trailing backslash */
5888 strcpy(previous_line, line); /* copy line to storage buffer */
5890 read_continued_line = TRUE;
5895 /* skip empty lines */
5896 if (line[0] == '\0')
5899 /* extract comment text from comment line */
5902 for (line_ptr = line; *line_ptr; line_ptr++)
5903 if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != ';')
5906 strcpy(last_comment, line_ptr);
5911 /* extract level title text from line containing level title */
5912 if (line[0] == '\'')
5914 strcpy(level_name, &line[1]);
5916 if (strlen(level_name) > 0 && level_name[strlen(level_name) - 1] == '\'')
5917 level_name[strlen(level_name) - 1] = '\0';
5922 /* skip lines containing only spaces (or empty lines) */
5923 for (line_ptr = line; *line_ptr; line_ptr++)
5924 if (*line_ptr != ' ')
5926 if (*line_ptr == '\0')
5929 /* at this point, we have found a line containing part of a playfield */
5931 got_valid_playfield_line = TRUE;
5933 if (!reading_playfield)
5935 reading_playfield = TRUE;
5936 invalid_playfield_char = FALSE;
5938 for (x = 0; x < MAX_LEV_FIELDX; x++)
5939 for (y = 0; y < MAX_LEV_FIELDY; y++)
5940 level->field[x][y] = getMappedElement_SB(' ', load_xsb_to_ces);
5945 /* start with topmost tile row */
5949 /* skip playfield line if larger row than allowed */
5950 if (y >= MAX_LEV_FIELDY)
5953 /* start with leftmost tile column */
5956 /* read playfield elements from line */
5957 for (line_ptr = line; *line_ptr; line_ptr++)
5959 int mapped_sb_element = getMappedElement_SB(*line_ptr, load_xsb_to_ces);
5961 /* stop parsing playfield line if larger column than allowed */
5962 if (x >= MAX_LEV_FIELDX)
5965 if (mapped_sb_element == EL_UNDEFINED)
5967 invalid_playfield_char = TRUE;
5972 level->field[x][y] = mapped_sb_element;
5974 /* continue with next tile column */
5977 level->fieldx = MAX(x, level->fieldx);
5980 if (invalid_playfield_char)
5982 /* if first playfield line, treat invalid lines as comment lines */
5984 reading_playfield = FALSE;
5989 /* continue with next tile row */
5997 level->fieldx = MIN(MAX(MIN_LEV_FIELDX, level->fieldx), MAX_LEV_FIELDX);
5998 level->fieldy = MIN(MAX(MIN_LEV_FIELDY, level->fieldy), MAX_LEV_FIELDY);
6000 if (!reading_playfield)
6002 level->no_valid_file = TRUE;
6004 Error(ERR_WARN, "cannot read level '%s' -- using empty level", filename);
6009 if (*level_name != '\0')
6011 strncpy(level->name, level_name, MAX_LEVEL_NAME_LEN);
6012 level->name[MAX_LEVEL_NAME_LEN] = '\0';
6014 else if (*last_comment != '\0')
6016 strncpy(level->name, last_comment, MAX_LEVEL_NAME_LEN);
6017 level->name[MAX_LEVEL_NAME_LEN] = '\0';
6021 sprintf(level->name, "--> Level %d <--", level_file_info->nr);
6024 /* set all empty fields beyond the border walls to invisible steel wall */
6025 for (y = 0; y < level->fieldy; y++) for (x = 0; x < level->fieldx; x++)
6027 if ((x == 0 || x == level->fieldx - 1 ||
6028 y == 0 || y == level->fieldy - 1) &&
6029 level->field[x][y] == getMappedElement_SB(' ', load_xsb_to_ces))
6030 FloodFillLevel(x, y, getMappedElement_SB('_', load_xsb_to_ces),
6031 level->field, level->fieldx, level->fieldy);
6034 /* set special level settings for Sokoban levels */
6037 level->use_step_counter = TRUE;
6039 if (load_xsb_to_ces)
6041 /* special global settings can now be set in level template */
6043 level->use_custom_template = TRUE;
6048 /* ------------------------------------------------------------------------- */
6049 /* functions for handling native levels */
6050 /* ------------------------------------------------------------------------- */
6052 static void LoadLevelFromFileInfo_EM(struct LevelInfo *level,
6053 struct LevelFileInfo *level_file_info,
6054 boolean level_info_only)
6056 if (!LoadNativeLevel_EM(level_file_info->filename, level_info_only))
6057 level->no_valid_file = TRUE;
6060 static void LoadLevelFromFileInfo_SP(struct LevelInfo *level,
6061 struct LevelFileInfo *level_file_info,
6062 boolean level_info_only)
6066 /* determine position of requested level inside level package */
6067 if (level_file_info->packed)
6068 pos = level_file_info->nr - leveldir_current->first_level;
6070 if (!LoadNativeLevel_SP(level_file_info->filename, pos, level_info_only))
6071 level->no_valid_file = TRUE;
6074 static void LoadLevelFromFileInfo_MM(struct LevelInfo *level,
6075 struct LevelFileInfo *level_file_info,
6076 boolean level_info_only)
6078 if (!LoadNativeLevel_MM(level_file_info->filename, level_info_only))
6079 level->no_valid_file = TRUE;
6082 void CopyNativeLevel_RND_to_Native(struct LevelInfo *level)
6084 if (level->game_engine_type == GAME_ENGINE_TYPE_EM)
6085 CopyNativeLevel_RND_to_EM(level);
6086 else if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
6087 CopyNativeLevel_RND_to_SP(level);
6088 else if (level->game_engine_type == GAME_ENGINE_TYPE_MM)
6089 CopyNativeLevel_RND_to_MM(level);
6092 void CopyNativeLevel_Native_to_RND(struct LevelInfo *level)
6094 if (level->game_engine_type == GAME_ENGINE_TYPE_EM)
6095 CopyNativeLevel_EM_to_RND(level);
6096 else if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
6097 CopyNativeLevel_SP_to_RND(level);
6098 else if (level->game_engine_type == GAME_ENGINE_TYPE_MM)
6099 CopyNativeLevel_MM_to_RND(level);
6102 void SaveNativeLevel(struct LevelInfo *level)
6104 if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
6106 char *basename = getSingleLevelBasenameExt(level->file_info.nr, "sp");
6107 char *filename = getLevelFilenameFromBasename(basename);
6109 CopyNativeLevel_RND_to_SP(level);
6110 CopyNativeTape_RND_to_SP(level);
6112 SaveNativeLevel_SP(filename);
6117 /* ------------------------------------------------------------------------- */
6118 /* functions for loading generic level */
6119 /* ------------------------------------------------------------------------- */
6121 static void LoadLevelFromFileInfo(struct LevelInfo *level,
6122 struct LevelFileInfo *level_file_info,
6123 boolean level_info_only)
6125 /* always start with reliable default values */
6126 setLevelInfoToDefaults(level, level_info_only, TRUE);
6128 switch (level_file_info->type)
6130 case LEVEL_FILE_TYPE_RND:
6131 LoadLevelFromFileInfo_RND(level, level_file_info, level_info_only);
6134 case LEVEL_FILE_TYPE_EM:
6135 LoadLevelFromFileInfo_EM(level, level_file_info, level_info_only);
6136 level->game_engine_type = GAME_ENGINE_TYPE_EM;
6139 case LEVEL_FILE_TYPE_SP:
6140 LoadLevelFromFileInfo_SP(level, level_file_info, level_info_only);
6141 level->game_engine_type = GAME_ENGINE_TYPE_SP;
6144 case LEVEL_FILE_TYPE_MM:
6145 LoadLevelFromFileInfo_MM(level, level_file_info, level_info_only);
6146 level->game_engine_type = GAME_ENGINE_TYPE_MM;
6149 case LEVEL_FILE_TYPE_DC:
6150 LoadLevelFromFileInfo_DC(level, level_file_info, level_info_only);
6153 case LEVEL_FILE_TYPE_SB:
6154 LoadLevelFromFileInfo_SB(level, level_file_info, level_info_only);
6158 LoadLevelFromFileInfo_RND(level, level_file_info, level_info_only);
6162 /* if level file is invalid, restore level structure to default values */
6163 if (level->no_valid_file)
6164 setLevelInfoToDefaults(level, level_info_only, FALSE);
6166 if (level->game_engine_type == GAME_ENGINE_TYPE_UNKNOWN)
6167 level->game_engine_type = GAME_ENGINE_TYPE_RND;
6169 if (level_file_info->type != LEVEL_FILE_TYPE_RND)
6170 CopyNativeLevel_Native_to_RND(level);
6173 void LoadLevelFromFilename(struct LevelInfo *level, char *filename)
6175 static struct LevelFileInfo level_file_info;
6177 /* always start with reliable default values */
6178 setFileInfoToDefaults(&level_file_info);
6180 level_file_info.nr = 0; /* unknown level number */
6181 level_file_info.type = LEVEL_FILE_TYPE_RND; /* no others supported yet */
6182 level_file_info.filename = filename;
6184 LoadLevelFromFileInfo(level, &level_file_info, FALSE);
6187 static void LoadLevel_InitVersion(struct LevelInfo *level, char *filename)
6191 if (leveldir_current == NULL) /* only when dumping level */
6194 /* all engine modifications also valid for levels which use latest engine */
6195 if (level->game_version < VERSION_IDENT(3,2,0,5))
6197 /* time bonus score was given for 10 s instead of 1 s before 3.2.0-5 */
6198 level->score[SC_TIME_BONUS] /= 10;
6201 if (leveldir_current->latest_engine)
6203 /* ---------- use latest game engine ----------------------------------- */
6205 /* For all levels which are forced to use the latest game engine version
6206 (normally all but user contributed, private and undefined levels), set
6207 the game engine version to the actual version; this allows for actual
6208 corrections in the game engine to take effect for existing, converted
6209 levels (from "classic" or other existing games) to make the emulation
6210 of the corresponding game more accurate, while (hopefully) not breaking
6211 existing levels created from other players. */
6213 level->game_version = GAME_VERSION_ACTUAL;
6215 /* Set special EM style gems behaviour: EM style gems slip down from
6216 normal, steel and growing wall. As this is a more fundamental change,
6217 it seems better to set the default behaviour to "off" (as it is more
6218 natural) and make it configurable in the level editor (as a property
6219 of gem style elements). Already existing converted levels (neither
6220 private nor contributed levels) are changed to the new behaviour. */
6222 if (level->file_version < FILE_VERSION_2_0)
6223 level->em_slippery_gems = TRUE;
6228 /* ---------- use game engine the level was created with ----------------- */
6230 /* For all levels which are not forced to use the latest game engine
6231 version (normally user contributed, private and undefined levels),
6232 use the version of the game engine the levels were created for.
6234 Since 2.0.1, the game engine version is now directly stored
6235 in the level file (chunk "VERS"), so there is no need anymore
6236 to set the game version from the file version (except for old,
6237 pre-2.0 levels, where the game version is still taken from the
6238 file format version used to store the level -- see above). */
6240 /* player was faster than enemies in 1.0.0 and before */
6241 if (level->file_version == FILE_VERSION_1_0)
6242 for (i = 0; i < MAX_PLAYERS; i++)
6243 level->initial_player_stepsize[i] = STEPSIZE_FAST;
6245 /* default behaviour for EM style gems was "slippery" only in 2.0.1 */
6246 if (level->game_version == VERSION_IDENT(2,0,1,0))
6247 level->em_slippery_gems = TRUE;
6249 /* springs could be pushed over pits before (pre-release version) 2.2.0 */
6250 if (level->game_version < VERSION_IDENT(2,2,0,0))
6251 level->use_spring_bug = TRUE;
6253 if (level->game_version < VERSION_IDENT(3,2,0,5))
6255 /* time orb caused limited time in endless time levels before 3.2.0-5 */
6256 level->use_time_orb_bug = TRUE;
6258 /* default behaviour for snapping was "no snap delay" before 3.2.0-5 */
6259 level->block_snap_field = FALSE;
6261 /* extra time score was same value as time left score before 3.2.0-5 */
6262 level->extra_time_score = level->score[SC_TIME_BONUS];
6265 if (level->game_version < VERSION_IDENT(3,2,0,7))
6267 /* default behaviour for snapping was "not continuous" before 3.2.0-7 */
6268 level->continuous_snapping = FALSE;
6271 /* only few elements were able to actively move into acid before 3.1.0 */
6272 /* trigger settings did not exist before 3.1.0; set to default "any" */
6273 if (level->game_version < VERSION_IDENT(3,1,0,0))
6275 /* correct "can move into acid" settings (all zero in old levels) */
6277 level->can_move_into_acid_bits = 0; /* nothing can move into acid */
6278 level->dont_collide_with_bits = 0; /* nothing is deadly when colliding */
6280 setMoveIntoAcidProperty(level, EL_ROBOT, TRUE);
6281 setMoveIntoAcidProperty(level, EL_SATELLITE, TRUE);
6282 setMoveIntoAcidProperty(level, EL_PENGUIN, TRUE);
6283 setMoveIntoAcidProperty(level, EL_BALLOON, TRUE);
6285 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6286 SET_PROPERTY(EL_CUSTOM_START + i, EP_CAN_MOVE_INTO_ACID, TRUE);
6288 /* correct trigger settings (stored as zero == "none" in old levels) */
6290 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6292 int element = EL_CUSTOM_START + i;
6293 struct ElementInfo *ei = &element_info[element];
6295 for (j = 0; j < ei->num_change_pages; j++)
6297 struct ElementChangeInfo *change = &ei->change_page[j];
6299 change->trigger_player = CH_PLAYER_ANY;
6300 change->trigger_page = CH_PAGE_ANY;
6305 /* try to detect and fix "Snake Bite" levels, which are broken with 3.2.0 */
6307 int element = EL_CUSTOM_256;
6308 struct ElementInfo *ei = &element_info[element];
6309 struct ElementChangeInfo *change = &ei->change_page[0];
6311 /* This is needed to fix a problem that was caused by a bugfix in function
6312 game.c/CreateFieldExt() introduced with 3.2.0 that corrects the behaviour
6313 when a custom element changes to EL_SOKOBAN_FIELD_PLAYER (before, it did
6314 not replace walkable elements, but instead just placed the player on it,
6315 without placing the Sokoban field under the player). Unfortunately, this
6316 breaks "Snake Bite" style levels when the snake is halfway through a door
6317 that just closes (the snake head is still alive and can be moved in this
6318 case). This can be fixed by replacing the EL_SOKOBAN_FIELD_PLAYER by the
6319 player (without Sokoban element) which then gets killed as designed). */
6321 if ((strncmp(leveldir_current->identifier, "snake_bite", 10) == 0 ||
6322 strncmp(ei->description, "pause b4 death", 14) == 0) &&
6323 change->target_element == EL_SOKOBAN_FIELD_PLAYER)
6324 change->target_element = EL_PLAYER_1;
6327 /* try to detect and fix "Zelda" style levels, which are broken with 3.2.5 */
6328 if (level->game_version < VERSION_IDENT(3,2,5,0))
6330 /* This is needed to fix a problem that was caused by a bugfix in function
6331 game.c/CheckTriggeredElementChangeExt() introduced with 3.2.5 that
6332 corrects the behaviour when a custom element changes to another custom
6333 element with a higher element number that has change actions defined.
6334 Normally, only one change per frame is allowed for custom elements.
6335 Therefore, it is checked if a custom element already changed in the
6336 current frame; if it did, subsequent changes are suppressed.
6337 Unfortunately, this is only checked for element changes, but not for
6338 change actions, which are still executed. As the function above loops
6339 through all custom elements from lower to higher, an element change
6340 resulting in a lower CE number won't be checked again, while a target
6341 element with a higher number will also be checked, and potential change
6342 actions will get executed for this CE, too (which is wrong), while
6343 further changes are ignored (which is correct). As this bugfix breaks
6344 Zelda II (and introduces graphical bugs to Zelda I, and also breaks a
6345 few other levels like Alan Bond's "FMV"), allow the previous, incorrect
6346 behaviour for existing levels and tapes that make use of this bug */
6348 level->use_action_after_change_bug = TRUE;
6351 /* not centering level after relocating player was default only in 3.2.3 */
6352 if (level->game_version == VERSION_IDENT(3,2,3,0)) /* (no pre-releases) */
6353 level->shifted_relocation = TRUE;
6355 /* EM style elements always chain-exploded in R'n'D engine before 3.2.6 */
6356 if (level->game_version < VERSION_IDENT(3,2,6,0))
6357 level->em_explodes_by_fire = TRUE;
6360 static void LoadLevel_InitStandardElements(struct LevelInfo *level)
6364 /* map elements that have changed in newer versions */
6365 level->amoeba_content = getMappedElementByVersion(level->amoeba_content,
6366 level->game_version);
6367 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
6368 for (x = 0; x < 3; x++)
6369 for (y = 0; y < 3; y++)
6370 level->yamyam_content[i].e[x][y] =
6371 getMappedElementByVersion(level->yamyam_content[i].e[x][y],
6372 level->game_version);
6376 static void LoadLevel_InitCustomElements(struct LevelInfo *level)
6380 /* map custom element change events that have changed in newer versions
6381 (these following values were accidentally changed in version 3.0.1)
6382 (this seems to be needed only for 'ab_levelset3' and 'ab_levelset4') */
6383 if (level->game_version <= VERSION_IDENT(3,0,0,0))
6385 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6387 int element = EL_CUSTOM_START + i;
6389 /* order of checking and copying events to be mapped is important */
6390 /* (do not change the start and end value -- they are constant) */
6391 for (j = CE_BY_OTHER_ACTION; j >= CE_VALUE_GETS_ZERO; j--)
6393 if (HAS_CHANGE_EVENT(element, j - 2))
6395 SET_CHANGE_EVENT(element, j - 2, FALSE);
6396 SET_CHANGE_EVENT(element, j, TRUE);
6400 /* order of checking and copying events to be mapped is important */
6401 /* (do not change the start and end value -- they are constant) */
6402 for (j = CE_PLAYER_COLLECTS_X; j >= CE_HITTING_SOMETHING; j--)
6404 if (HAS_CHANGE_EVENT(element, j - 1))
6406 SET_CHANGE_EVENT(element, j - 1, FALSE);
6407 SET_CHANGE_EVENT(element, j, TRUE);
6413 /* initialize "can_change" field for old levels with only one change page */
6414 if (level->game_version <= VERSION_IDENT(3,0,2,0))
6416 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6418 int element = EL_CUSTOM_START + i;
6420 if (CAN_CHANGE(element))
6421 element_info[element].change->can_change = TRUE;
6425 /* correct custom element values (for old levels without these options) */
6426 if (level->game_version < VERSION_IDENT(3,1,1,0))
6428 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6430 int element = EL_CUSTOM_START + i;
6431 struct ElementInfo *ei = &element_info[element];
6433 if (ei->access_direction == MV_NO_DIRECTION)
6434 ei->access_direction = MV_ALL_DIRECTIONS;
6438 /* correct custom element values (fix invalid values for all versions) */
6441 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6443 int element = EL_CUSTOM_START + i;
6444 struct ElementInfo *ei = &element_info[element];
6446 for (j = 0; j < ei->num_change_pages; j++)
6448 struct ElementChangeInfo *change = &ei->change_page[j];
6450 if (change->trigger_player == CH_PLAYER_NONE)
6451 change->trigger_player = CH_PLAYER_ANY;
6453 if (change->trigger_side == CH_SIDE_NONE)
6454 change->trigger_side = CH_SIDE_ANY;
6459 /* initialize "can_explode" field for old levels which did not store this */
6460 /* !!! CHECK THIS -- "<= 3,1,0,0" IS PROBABLY WRONG !!! */
6461 if (level->game_version <= VERSION_IDENT(3,1,0,0))
6463 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6465 int element = EL_CUSTOM_START + i;
6467 if (EXPLODES_1X1_OLD(element))
6468 element_info[element].explosion_type = EXPLODES_1X1;
6470 SET_PROPERTY(element, EP_CAN_EXPLODE, (EXPLODES_BY_FIRE(element) ||
6471 EXPLODES_SMASHED(element) ||
6472 EXPLODES_IMPACT(element)));
6476 /* correct previously hard-coded move delay values for maze runner style */
6477 if (level->game_version < VERSION_IDENT(3,1,1,0))
6479 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6481 int element = EL_CUSTOM_START + i;
6483 if (element_info[element].move_pattern & MV_MAZE_RUNNER_STYLE)
6485 /* previously hard-coded and therefore ignored */
6486 element_info[element].move_delay_fixed = 9;
6487 element_info[element].move_delay_random = 0;
6492 /* set some other uninitialized values of custom elements in older levels */
6493 if (level->game_version < VERSION_IDENT(3,1,0,0))
6495 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6497 int element = EL_CUSTOM_START + i;
6499 element_info[element].access_direction = MV_ALL_DIRECTIONS;
6501 element_info[element].explosion_delay = 17;
6502 element_info[element].ignition_delay = 8;
6507 static void LoadLevel_InitElements(struct LevelInfo *level, char *filename)
6509 LoadLevel_InitStandardElements(level);
6511 if (level->file_has_custom_elements)
6512 LoadLevel_InitCustomElements(level);
6514 /* initialize element properties for level editor etc. */
6515 InitElementPropertiesEngine(level->game_version);
6516 InitElementPropertiesGfxElement();
6519 static void LoadLevel_InitPlayfield(struct LevelInfo *level, char *filename)
6523 /* map elements that have changed in newer versions */
6524 for (y = 0; y < level->fieldy; y++)
6525 for (x = 0; x < level->fieldx; x++)
6526 level->field[x][y] = getMappedElementByVersion(level->field[x][y],
6527 level->game_version);
6529 /* clear unused playfield data (nicer if level gets resized in editor) */
6530 for (x = 0; x < MAX_LEV_FIELDX; x++)
6531 for (y = 0; y < MAX_LEV_FIELDY; y++)
6532 if (x >= level->fieldx || y >= level->fieldy)
6533 level->field[x][y] = EL_EMPTY;
6535 /* copy elements to runtime playfield array */
6536 for (x = 0; x < MAX_LEV_FIELDX; x++)
6537 for (y = 0; y < MAX_LEV_FIELDY; y++)
6538 Feld[x][y] = level->field[x][y];
6540 /* initialize level size variables for faster access */
6541 lev_fieldx = level->fieldx;
6542 lev_fieldy = level->fieldy;
6544 /* determine border element for this level */
6545 if (level->file_info.type == LEVEL_FILE_TYPE_DC)
6546 BorderElement = EL_EMPTY; /* (in editor, SetBorderElement() is used) */
6551 static void LoadLevel_InitNativeEngines(struct LevelInfo *level,char *filename)
6553 struct LevelFileInfo *level_file_info = &level->file_info;
6555 if (level_file_info->type == LEVEL_FILE_TYPE_RND)
6556 CopyNativeLevel_RND_to_Native(level);
6559 void LoadLevelTemplate(int nr)
6563 setLevelFileInfo(&level_template.file_info, nr);
6564 filename = level_template.file_info.filename;
6566 LoadLevelFromFileInfo(&level_template, &level_template.file_info, FALSE);
6568 LoadLevel_InitVersion(&level_template, filename);
6569 LoadLevel_InitElements(&level_template, filename);
6571 ActivateLevelTemplate();
6574 void LoadLevel(int nr)
6578 setLevelFileInfo(&level.file_info, nr);
6579 filename = level.file_info.filename;
6581 LoadLevelFromFileInfo(&level, &level.file_info, FALSE);
6583 if (level.use_custom_template)
6584 LoadLevelTemplate(-1);
6586 LoadLevel_InitVersion(&level, filename);
6587 LoadLevel_InitElements(&level, filename);
6588 LoadLevel_InitPlayfield(&level, filename);
6590 LoadLevel_InitNativeEngines(&level, filename);
6593 void LoadLevelInfoOnly(int nr)
6595 setLevelFileInfo(&level.file_info, nr);
6597 LoadLevelFromFileInfo(&level, &level.file_info, TRUE);
6600 static int SaveLevel_VERS(FILE *file, struct LevelInfo *level)
6604 chunk_size += putFileVersion(file, level->file_version);
6605 chunk_size += putFileVersion(file, level->game_version);
6610 static int SaveLevel_DATE(FILE *file, struct LevelInfo *level)
6614 chunk_size += putFile16BitBE(file, level->creation_date.year);
6615 chunk_size += putFile8Bit(file, level->creation_date.month);
6616 chunk_size += putFile8Bit(file, level->creation_date.day);
6621 #if ENABLE_HISTORIC_CHUNKS
6622 static void SaveLevel_HEAD(FILE *file, struct LevelInfo *level)
6626 putFile8Bit(file, level->fieldx);
6627 putFile8Bit(file, level->fieldy);
6629 putFile16BitBE(file, level->time);
6630 putFile16BitBE(file, level->gems_needed);
6632 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
6633 putFile8Bit(file, level->name[i]);
6635 for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
6636 putFile8Bit(file, level->score[i]);
6638 for (i = 0; i < STD_ELEMENT_CONTENTS; i++)
6639 for (y = 0; y < 3; y++)
6640 for (x = 0; x < 3; x++)
6641 putFile8Bit(file, (level->encoding_16bit_yamyam ? EL_EMPTY :
6642 level->yamyam_content[i].e[x][y]));
6643 putFile8Bit(file, level->amoeba_speed);
6644 putFile8Bit(file, level->time_magic_wall);
6645 putFile8Bit(file, level->time_wheel);
6646 putFile8Bit(file, (level->encoding_16bit_amoeba ? EL_EMPTY :
6647 level->amoeba_content));
6648 putFile8Bit(file, (level->initial_player_stepsize == STEPSIZE_FAST ? 1 : 0));
6649 putFile8Bit(file, (level->initial_gravity ? 1 : 0));
6650 putFile8Bit(file, (level->encoding_16bit_field ? 1 : 0));
6651 putFile8Bit(file, (level->em_slippery_gems ? 1 : 0));
6653 putFile8Bit(file, (level->use_custom_template ? 1 : 0));
6655 putFile8Bit(file, (level->block_last_field ? 1 : 0));
6656 putFile8Bit(file, (level->sp_block_last_field ? 1 : 0));
6657 putFile32BitBE(file, level->can_move_into_acid_bits);
6658 putFile8Bit(file, level->dont_collide_with_bits);
6660 putFile8Bit(file, (level->use_spring_bug ? 1 : 0));
6661 putFile8Bit(file, (level->use_step_counter ? 1 : 0));
6663 putFile8Bit(file, (level->instant_relocation ? 1 : 0));
6664 putFile8Bit(file, (level->can_pass_to_walkable ? 1 : 0));
6665 putFile8Bit(file, (level->grow_into_diggable ? 1 : 0));
6667 putFile8Bit(file, level->game_engine_type);
6669 WriteUnusedBytesToFile(file, LEVEL_CHUNK_HEAD_UNUSED);
6673 static int SaveLevel_NAME(FILE *file, struct LevelInfo *level)
6678 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
6679 chunk_size += putFile8Bit(file, level->name[i]);
6684 static int SaveLevel_AUTH(FILE *file, struct LevelInfo *level)
6689 for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
6690 chunk_size += putFile8Bit(file, level->author[i]);
6695 #if ENABLE_HISTORIC_CHUNKS
6696 static int SaveLevel_BODY(FILE *file, struct LevelInfo *level)
6701 for (y = 0; y < level->fieldy; y++)
6702 for (x = 0; x < level->fieldx; x++)
6703 if (level->encoding_16bit_field)
6704 chunk_size += putFile16BitBE(file, level->field[x][y]);
6706 chunk_size += putFile8Bit(file, level->field[x][y]);
6712 static int SaveLevel_BODY(FILE *file, struct LevelInfo *level)
6717 for (y = 0; y < level->fieldy; y++)
6718 for (x = 0; x < level->fieldx; x++)
6719 chunk_size += putFile16BitBE(file, level->field[x][y]);
6724 #if ENABLE_HISTORIC_CHUNKS
6725 static void SaveLevel_CONT(FILE *file, struct LevelInfo *level)
6729 putFile8Bit(file, EL_YAMYAM);
6730 putFile8Bit(file, level->num_yamyam_contents);
6731 putFile8Bit(file, 0);
6732 putFile8Bit(file, 0);
6734 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
6735 for (y = 0; y < 3; y++)
6736 for (x = 0; x < 3; x++)
6737 if (level->encoding_16bit_field)
6738 putFile16BitBE(file, level->yamyam_content[i].e[x][y]);
6740 putFile8Bit(file, level->yamyam_content[i].e[x][y]);
6744 #if ENABLE_HISTORIC_CHUNKS
6745 static void SaveLevel_CNT2(FILE *file, struct LevelInfo *level, int element)
6748 int num_contents, content_xsize, content_ysize;
6749 int content_array[MAX_ELEMENT_CONTENTS][3][3];
6751 if (element == EL_YAMYAM)
6753 num_contents = level->num_yamyam_contents;
6757 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
6758 for (y = 0; y < 3; y++)
6759 for (x = 0; x < 3; x++)
6760 content_array[i][x][y] = level->yamyam_content[i].e[x][y];
6762 else if (element == EL_BD_AMOEBA)
6768 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
6769 for (y = 0; y < 3; y++)
6770 for (x = 0; x < 3; x++)
6771 content_array[i][x][y] = EL_EMPTY;
6772 content_array[0][0][0] = level->amoeba_content;
6776 /* chunk header already written -- write empty chunk data */
6777 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_SIZE);
6779 Error(ERR_WARN, "cannot save content for element '%d'", element);
6783 putFile16BitBE(file, element);
6784 putFile8Bit(file, num_contents);
6785 putFile8Bit(file, content_xsize);
6786 putFile8Bit(file, content_ysize);
6788 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_UNUSED);
6790 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
6791 for (y = 0; y < 3; y++)
6792 for (x = 0; x < 3; x++)
6793 putFile16BitBE(file, content_array[i][x][y]);
6797 #if ENABLE_HISTORIC_CHUNKS
6798 static int SaveLevel_CNT3(FILE *file, struct LevelInfo *level, int element)
6800 int envelope_nr = element - EL_ENVELOPE_1;
6801 int envelope_len = strlen(level->envelope_text[envelope_nr]) + 1;
6805 chunk_size += putFile16BitBE(file, element);
6806 chunk_size += putFile16BitBE(file, envelope_len);
6807 chunk_size += putFile8Bit(file, level->envelope_xsize[envelope_nr]);
6808 chunk_size += putFile8Bit(file, level->envelope_ysize[envelope_nr]);
6810 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT3_UNUSED);
6811 chunk_size += LEVEL_CHUNK_CNT3_UNUSED;
6813 for (i = 0; i < envelope_len; i++)
6814 chunk_size += putFile8Bit(file, level->envelope_text[envelope_nr][i]);
6820 #if ENABLE_HISTORIC_CHUNKS
6821 static void SaveLevel_CUS1(FILE *file, struct LevelInfo *level,
6822 int num_changed_custom_elements)
6826 putFile16BitBE(file, num_changed_custom_elements);
6828 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6830 int element = EL_CUSTOM_START + i;
6832 struct ElementInfo *ei = &element_info[element];
6834 if (ei->properties[EP_BITFIELD_BASE_NR] != EP_BITMASK_DEFAULT)
6836 if (check < num_changed_custom_elements)
6838 putFile16BitBE(file, element);
6839 putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
6846 if (check != num_changed_custom_elements) /* should not happen */
6847 Error(ERR_WARN, "inconsistent number of custom element properties");
6851 #if ENABLE_HISTORIC_CHUNKS
6852 static void SaveLevel_CUS2(FILE *file, struct LevelInfo *level,
6853 int num_changed_custom_elements)
6857 putFile16BitBE(file, num_changed_custom_elements);
6859 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6861 int element = EL_CUSTOM_START + i;
6863 if (element_info[element].change->target_element != EL_EMPTY_SPACE)
6865 if (check < num_changed_custom_elements)
6867 putFile16BitBE(file, element);
6868 putFile16BitBE(file, element_info[element].change->target_element);
6875 if (check != num_changed_custom_elements) /* should not happen */
6876 Error(ERR_WARN, "inconsistent number of custom target elements");
6880 #if ENABLE_HISTORIC_CHUNKS
6881 static void SaveLevel_CUS3(FILE *file, struct LevelInfo *level,
6882 int num_changed_custom_elements)
6884 int i, j, x, y, check = 0;
6886 putFile16BitBE(file, num_changed_custom_elements);
6888 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6890 int element = EL_CUSTOM_START + i;
6891 struct ElementInfo *ei = &element_info[element];
6893 if (ei->modified_settings)
6895 if (check < num_changed_custom_elements)
6897 putFile16BitBE(file, element);
6899 for (j = 0; j < MAX_ELEMENT_NAME_LEN; j++)
6900 putFile8Bit(file, ei->description[j]);
6902 putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
6904 /* some free bytes for future properties and padding */
6905 WriteUnusedBytesToFile(file, 7);
6907 putFile8Bit(file, ei->use_gfx_element);
6908 putFile16BitBE(file, ei->gfx_element_initial);
6910 putFile8Bit(file, ei->collect_score_initial);
6911 putFile8Bit(file, ei->collect_count_initial);
6913 putFile16BitBE(file, ei->push_delay_fixed);
6914 putFile16BitBE(file, ei->push_delay_random);
6915 putFile16BitBE(file, ei->move_delay_fixed);
6916 putFile16BitBE(file, ei->move_delay_random);
6918 putFile16BitBE(file, ei->move_pattern);
6919 putFile8Bit(file, ei->move_direction_initial);
6920 putFile8Bit(file, ei->move_stepsize);
6922 for (y = 0; y < 3; y++)
6923 for (x = 0; x < 3; x++)
6924 putFile16BitBE(file, ei->content.e[x][y]);
6926 putFile32BitBE(file, ei->change->events);
6928 putFile16BitBE(file, ei->change->target_element);
6930 putFile16BitBE(file, ei->change->delay_fixed);
6931 putFile16BitBE(file, ei->change->delay_random);
6932 putFile16BitBE(file, ei->change->delay_frames);
6934 putFile16BitBE(file, ei->change->initial_trigger_element);
6936 putFile8Bit(file, ei->change->explode);
6937 putFile8Bit(file, ei->change->use_target_content);
6938 putFile8Bit(file, ei->change->only_if_complete);
6939 putFile8Bit(file, ei->change->use_random_replace);
6941 putFile8Bit(file, ei->change->random_percentage);
6942 putFile8Bit(file, ei->change->replace_when);
6944 for (y = 0; y < 3; y++)
6945 for (x = 0; x < 3; x++)
6946 putFile16BitBE(file, ei->change->content.e[x][y]);
6948 putFile8Bit(file, ei->slippery_type);
6950 /* some free bytes for future properties and padding */
6951 WriteUnusedBytesToFile(file, LEVEL_CPART_CUS3_UNUSED);
6958 if (check != num_changed_custom_elements) /* should not happen */
6959 Error(ERR_WARN, "inconsistent number of custom element properties");
6963 #if ENABLE_HISTORIC_CHUNKS
6964 static void SaveLevel_CUS4(FILE *file, struct LevelInfo *level, int element)
6966 struct ElementInfo *ei = &element_info[element];
6969 /* ---------- custom element base property values (96 bytes) ------------- */
6971 putFile16BitBE(file, element);
6973 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
6974 putFile8Bit(file, ei->description[i]);
6976 putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
6978 WriteUnusedBytesToFile(file, 4); /* reserved for more base properties */
6980 putFile8Bit(file, ei->num_change_pages);
6982 putFile16BitBE(file, ei->ce_value_fixed_initial);
6983 putFile16BitBE(file, ei->ce_value_random_initial);
6984 putFile8Bit(file, ei->use_last_ce_value);
6986 putFile8Bit(file, ei->use_gfx_element);
6987 putFile16BitBE(file, ei->gfx_element_initial);
6989 putFile8Bit(file, ei->collect_score_initial);
6990 putFile8Bit(file, ei->collect_count_initial);
6992 putFile8Bit(file, ei->drop_delay_fixed);
6993 putFile8Bit(file, ei->push_delay_fixed);
6994 putFile8Bit(file, ei->drop_delay_random);
6995 putFile8Bit(file, ei->push_delay_random);
6996 putFile16BitBE(file, ei->move_delay_fixed);
6997 putFile16BitBE(file, ei->move_delay_random);
6999 /* bits 0 - 15 of "move_pattern" ... */
7000 putFile16BitBE(file, ei->move_pattern & 0xffff);
7001 putFile8Bit(file, ei->move_direction_initial);
7002 putFile8Bit(file, ei->move_stepsize);
7004 putFile8Bit(file, ei->slippery_type);
7006 for (y = 0; y < 3; y++)
7007 for (x = 0; x < 3; x++)
7008 putFile16BitBE(file, ei->content.e[x][y]);
7010 putFile16BitBE(file, ei->move_enter_element);
7011 putFile16BitBE(file, ei->move_leave_element);
7012 putFile8Bit(file, ei->move_leave_type);
7014 /* ... bits 16 - 31 of "move_pattern" (not nice, but downward compatible) */
7015 putFile16BitBE(file, (ei->move_pattern >> 16) & 0xffff);
7017 putFile8Bit(file, ei->access_direction);
7019 putFile8Bit(file, ei->explosion_delay);
7020 putFile8Bit(file, ei->ignition_delay);
7021 putFile8Bit(file, ei->explosion_type);
7023 /* some free bytes for future custom property values and padding */
7024 WriteUnusedBytesToFile(file, 1);
7026 /* ---------- change page property values (48 bytes) --------------------- */
7028 for (i = 0; i < ei->num_change_pages; i++)
7030 struct ElementChangeInfo *change = &ei->change_page[i];
7031 unsigned int event_bits;
7033 /* bits 0 - 31 of "has_event[]" ... */
7035 for (j = 0; j < MIN(NUM_CHANGE_EVENTS, 32); j++)
7036 if (change->has_event[j])
7037 event_bits |= (1 << j);
7038 putFile32BitBE(file, event_bits);
7040 putFile16BitBE(file, change->target_element);
7042 putFile16BitBE(file, change->delay_fixed);
7043 putFile16BitBE(file, change->delay_random);
7044 putFile16BitBE(file, change->delay_frames);
7046 putFile16BitBE(file, change->initial_trigger_element);
7048 putFile8Bit(file, change->explode);
7049 putFile8Bit(file, change->use_target_content);
7050 putFile8Bit(file, change->only_if_complete);
7051 putFile8Bit(file, change->use_random_replace);
7053 putFile8Bit(file, change->random_percentage);
7054 putFile8Bit(file, change->replace_when);
7056 for (y = 0; y < 3; y++)
7057 for (x = 0; x < 3; x++)
7058 putFile16BitBE(file, change->target_content.e[x][y]);
7060 putFile8Bit(file, change->can_change);
7062 putFile8Bit(file, change->trigger_side);
7064 putFile8Bit(file, change->trigger_player);
7065 putFile8Bit(file, (change->trigger_page == CH_PAGE_ANY ? CH_PAGE_ANY_FILE :
7066 log_2(change->trigger_page)));
7068 putFile8Bit(file, change->has_action);
7069 putFile8Bit(file, change->action_type);
7070 putFile8Bit(file, change->action_mode);
7071 putFile16BitBE(file, change->action_arg);
7073 /* ... bits 32 - 39 of "has_event[]" (not nice, but downward compatible) */
7075 for (j = 32; j < NUM_CHANGE_EVENTS; j++)
7076 if (change->has_event[j])
7077 event_bits |= (1 << (j - 32));
7078 putFile8Bit(file, event_bits);
7083 #if ENABLE_HISTORIC_CHUNKS
7084 static void SaveLevel_GRP1(FILE *file, struct LevelInfo *level, int element)
7086 struct ElementInfo *ei = &element_info[element];
7087 struct ElementGroupInfo *group = ei->group;
7090 putFile16BitBE(file, element);
7092 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
7093 putFile8Bit(file, ei->description[i]);
7095 putFile8Bit(file, group->num_elements);
7097 putFile8Bit(file, ei->use_gfx_element);
7098 putFile16BitBE(file, ei->gfx_element_initial);
7100 putFile8Bit(file, group->choice_mode);
7102 /* some free bytes for future values and padding */
7103 WriteUnusedBytesToFile(file, 3);
7105 for (i = 0; i < MAX_ELEMENTS_IN_GROUP; i++)
7106 putFile16BitBE(file, group->element[i]);
7110 static int SaveLevel_MicroChunk(FILE *file, struct LevelFileConfigInfo *entry,
7111 boolean write_element)
7113 int save_type = entry->save_type;
7114 int data_type = entry->data_type;
7115 int conf_type = entry->conf_type;
7116 int byte_mask = conf_type & CONF_MASK_BYTES;
7117 int element = entry->element;
7118 int default_value = entry->default_value;
7120 boolean modified = FALSE;
7122 if (byte_mask != CONF_MASK_MULTI_BYTES)
7124 void *value_ptr = entry->value;
7125 int value = (data_type == TYPE_BOOLEAN ? *(boolean *)value_ptr :
7128 /* check if any settings have been modified before saving them */
7129 if (value != default_value)
7132 /* do not save if explicitly told or if unmodified default settings */
7133 if ((save_type == SAVE_CONF_NEVER) ||
7134 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
7138 num_bytes += putFile16BitBE(file, element);
7140 num_bytes += putFile8Bit(file, conf_type);
7141 num_bytes += (byte_mask == CONF_MASK_1_BYTE ? putFile8Bit (file, value) :
7142 byte_mask == CONF_MASK_2_BYTE ? putFile16BitBE(file, value) :
7143 byte_mask == CONF_MASK_4_BYTE ? putFile32BitBE(file, value) :
7146 else if (data_type == TYPE_STRING)
7148 char *default_string = entry->default_string;
7149 char *string = (char *)(entry->value);
7150 int string_length = strlen(string);
7153 /* check if any settings have been modified before saving them */
7154 if (!strEqual(string, default_string))
7157 /* do not save if explicitly told or if unmodified default settings */
7158 if ((save_type == SAVE_CONF_NEVER) ||
7159 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
7163 num_bytes += putFile16BitBE(file, element);
7165 num_bytes += putFile8Bit(file, conf_type);
7166 num_bytes += putFile16BitBE(file, string_length);
7168 for (i = 0; i < string_length; i++)
7169 num_bytes += putFile8Bit(file, string[i]);
7171 else if (data_type == TYPE_ELEMENT_LIST)
7173 int *element_array = (int *)(entry->value);
7174 int num_elements = *(int *)(entry->num_entities);
7177 /* check if any settings have been modified before saving them */
7178 for (i = 0; i < num_elements; i++)
7179 if (element_array[i] != default_value)
7182 /* do not save if explicitly told or if unmodified default settings */
7183 if ((save_type == SAVE_CONF_NEVER) ||
7184 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
7188 num_bytes += putFile16BitBE(file, element);
7190 num_bytes += putFile8Bit(file, conf_type);
7191 num_bytes += putFile16BitBE(file, num_elements * CONF_ELEMENT_NUM_BYTES);
7193 for (i = 0; i < num_elements; i++)
7194 num_bytes += putFile16BitBE(file, element_array[i]);
7196 else if (data_type == TYPE_CONTENT_LIST)
7198 struct Content *content = (struct Content *)(entry->value);
7199 int num_contents = *(int *)(entry->num_entities);
7202 /* check if any settings have been modified before saving them */
7203 for (i = 0; i < num_contents; i++)
7204 for (y = 0; y < 3; y++)
7205 for (x = 0; x < 3; x++)
7206 if (content[i].e[x][y] != default_value)
7209 /* do not save if explicitly told or if unmodified default settings */
7210 if ((save_type == SAVE_CONF_NEVER) ||
7211 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
7215 num_bytes += putFile16BitBE(file, element);
7217 num_bytes += putFile8Bit(file, conf_type);
7218 num_bytes += putFile16BitBE(file, num_contents * CONF_CONTENT_NUM_BYTES);
7220 for (i = 0; i < num_contents; i++)
7221 for (y = 0; y < 3; y++)
7222 for (x = 0; x < 3; x++)
7223 num_bytes += putFile16BitBE(file, content[i].e[x][y]);
7229 static int SaveLevel_INFO(FILE *file, struct LevelInfo *level)
7234 li = *level; /* copy level data into temporary buffer */
7236 for (i = 0; chunk_config_INFO[i].data_type != -1; i++)
7237 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_INFO[i], FALSE);
7242 static int SaveLevel_ELEM(FILE *file, struct LevelInfo *level)
7247 li = *level; /* copy level data into temporary buffer */
7249 for (i = 0; chunk_config_ELEM[i].data_type != -1; i++)
7250 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_ELEM[i], TRUE);
7255 static int SaveLevel_NOTE(FILE *file, struct LevelInfo *level, int element)
7257 int envelope_nr = element - EL_ENVELOPE_1;
7261 chunk_size += putFile16BitBE(file, element);
7263 /* copy envelope data into temporary buffer */
7264 xx_envelope = level->envelope[envelope_nr];
7266 for (i = 0; chunk_config_NOTE[i].data_type != -1; i++)
7267 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_NOTE[i], FALSE);
7272 static int SaveLevel_CUSX(FILE *file, struct LevelInfo *level, int element)
7274 struct ElementInfo *ei = &element_info[element];
7278 chunk_size += putFile16BitBE(file, element);
7280 xx_ei = *ei; /* copy element data into temporary buffer */
7282 /* set default description string for this specific element */
7283 strcpy(xx_default_description, getDefaultElementDescription(ei));
7285 for (i = 0; chunk_config_CUSX_base[i].data_type != -1; i++)
7286 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_CUSX_base[i], FALSE);
7288 for (i = 0; i < ei->num_change_pages; i++)
7290 struct ElementChangeInfo *change = &ei->change_page[i];
7292 xx_current_change_page = i;
7294 xx_change = *change; /* copy change data into temporary buffer */
7297 setEventBitsFromEventFlags(change);
7299 for (j = 0; chunk_config_CUSX_change[j].data_type != -1; j++)
7300 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_CUSX_change[j],
7307 static int SaveLevel_GRPX(FILE *file, struct LevelInfo *level, int element)
7309 struct ElementInfo *ei = &element_info[element];
7310 struct ElementGroupInfo *group = ei->group;
7314 chunk_size += putFile16BitBE(file, element);
7316 xx_ei = *ei; /* copy element data into temporary buffer */
7317 xx_group = *group; /* copy group data into temporary buffer */
7319 /* set default description string for this specific element */
7320 strcpy(xx_default_description, getDefaultElementDescription(ei));
7322 for (i = 0; chunk_config_GRPX[i].data_type != -1; i++)
7323 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_GRPX[i], FALSE);
7328 static void SaveLevelFromFilename(struct LevelInfo *level, char *filename,
7329 boolean save_as_template)
7335 if (!(file = fopen(filename, MODE_WRITE)))
7337 Error(ERR_WARN, "cannot save level file '%s'", filename);
7341 level->file_version = FILE_VERSION_ACTUAL;
7342 level->game_version = GAME_VERSION_ACTUAL;
7344 level->creation_date = getCurrentDate();
7346 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
7347 putFileChunkBE(file, "CAVE", CHUNK_SIZE_NONE);
7349 chunk_size = SaveLevel_VERS(NULL, level);
7350 putFileChunkBE(file, "VERS", chunk_size);
7351 SaveLevel_VERS(file, level);
7353 chunk_size = SaveLevel_DATE(NULL, level);
7354 putFileChunkBE(file, "DATE", chunk_size);
7355 SaveLevel_DATE(file, level);
7357 chunk_size = SaveLevel_NAME(NULL, level);
7358 putFileChunkBE(file, "NAME", chunk_size);
7359 SaveLevel_NAME(file, level);
7361 chunk_size = SaveLevel_AUTH(NULL, level);
7362 putFileChunkBE(file, "AUTH", chunk_size);
7363 SaveLevel_AUTH(file, level);
7365 chunk_size = SaveLevel_INFO(NULL, level);
7366 putFileChunkBE(file, "INFO", chunk_size);
7367 SaveLevel_INFO(file, level);
7369 chunk_size = SaveLevel_BODY(NULL, level);
7370 putFileChunkBE(file, "BODY", chunk_size);
7371 SaveLevel_BODY(file, level);
7373 chunk_size = SaveLevel_ELEM(NULL, level);
7374 if (chunk_size > LEVEL_CHUNK_ELEM_UNCHANGED) /* save if changed */
7376 putFileChunkBE(file, "ELEM", chunk_size);
7377 SaveLevel_ELEM(file, level);
7380 for (i = 0; i < NUM_ENVELOPES; i++)
7382 int element = EL_ENVELOPE_1 + i;
7384 chunk_size = SaveLevel_NOTE(NULL, level, element);
7385 if (chunk_size > LEVEL_CHUNK_NOTE_UNCHANGED) /* save if changed */
7387 putFileChunkBE(file, "NOTE", chunk_size);
7388 SaveLevel_NOTE(file, level, element);
7392 /* if not using template level, check for non-default custom/group elements */
7393 if (!level->use_custom_template || save_as_template)
7395 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7397 int element = EL_CUSTOM_START + i;
7399 chunk_size = SaveLevel_CUSX(NULL, level, element);
7400 if (chunk_size > LEVEL_CHUNK_CUSX_UNCHANGED) /* save if changed */
7402 putFileChunkBE(file, "CUSX", chunk_size);
7403 SaveLevel_CUSX(file, level, element);
7407 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
7409 int element = EL_GROUP_START + i;
7411 chunk_size = SaveLevel_GRPX(NULL, level, element);
7412 if (chunk_size > LEVEL_CHUNK_GRPX_UNCHANGED) /* save if changed */
7414 putFileChunkBE(file, "GRPX", chunk_size);
7415 SaveLevel_GRPX(file, level, element);
7422 SetFilePermissions(filename, PERMS_PRIVATE);
7425 void SaveLevel(int nr)
7427 char *filename = getDefaultLevelFilename(nr);
7429 SaveLevelFromFilename(&level, filename, FALSE);
7432 void SaveLevelTemplate()
7434 char *filename = getLocalLevelTemplateFilename();
7436 SaveLevelFromFilename(&level, filename, TRUE);
7439 boolean SaveLevelChecked(int nr)
7441 char *filename = getDefaultLevelFilename(nr);
7442 boolean new_level = !fileExists(filename);
7443 boolean level_saved = FALSE;
7445 if (new_level || Request("Save this level and kill the old?", REQ_ASK))
7450 Request("Level saved!", REQ_CONFIRM);
7458 void DumpLevel(struct LevelInfo *level)
7460 if (level->no_level_file || level->no_valid_file)
7462 Error(ERR_WARN, "cannot dump -- no valid level file found");
7468 Print("Level xxx (file version %08d, game version %08d)\n",
7469 level->file_version, level->game_version);
7472 Print("Level author: '%s'\n", level->author);
7473 Print("Level title: '%s'\n", level->name);
7475 Print("Playfield size: %d x %d\n", level->fieldx, level->fieldy);
7477 Print("Level time: %d seconds\n", level->time);
7478 Print("Gems needed: %d\n", level->gems_needed);
7480 Print("Time for magic wall: %d seconds\n", level->time_magic_wall);
7481 Print("Time for wheel: %d seconds\n", level->time_wheel);
7482 Print("Time for light: %d seconds\n", level->time_light);
7483 Print("Time for timegate: %d seconds\n", level->time_timegate);
7485 Print("Amoeba speed: %d\n", level->amoeba_speed);
7488 Print("EM style slippery gems: %s\n", (level->em_slippery_gems ? "yes" : "no"));
7489 Print("Player blocks last field: %s\n", (level->block_last_field ? "yes" : "no"));
7490 Print("SP player blocks last field: %s\n", (level->sp_block_last_field ? "yes" : "no"));
7491 Print("use spring bug: %s\n", (level->use_spring_bug ? "yes" : "no"));
7492 Print("use step counter: %s\n", (level->use_step_counter ? "yes" : "no"));
7498 /* ========================================================================= */
7499 /* tape file functions */
7500 /* ========================================================================= */
7502 static void setTapeInfoToDefaults()
7506 /* always start with reliable default values (empty tape) */
7509 /* default values (also for pre-1.2 tapes) with only the first player */
7510 tape.player_participates[0] = TRUE;
7511 for (i = 1; i < MAX_PLAYERS; i++)
7512 tape.player_participates[i] = FALSE;
7514 /* at least one (default: the first) player participates in every tape */
7515 tape.num_participating_players = 1;
7517 tape.level_nr = level_nr;
7519 tape.changed = FALSE;
7521 tape.recording = FALSE;
7522 tape.playing = FALSE;
7523 tape.pausing = FALSE;
7525 tape.no_valid_file = FALSE;
7528 static int LoadTape_VERS(File *file, int chunk_size, struct TapeInfo *tape)
7530 tape->file_version = getFileVersion(file);
7531 tape->game_version = getFileVersion(file);
7536 static int LoadTape_HEAD(File *file, int chunk_size, struct TapeInfo *tape)
7540 tape->random_seed = getFile32BitBE(file);
7541 tape->date = getFile32BitBE(file);
7542 tape->length = getFile32BitBE(file);
7544 /* read header fields that are new since version 1.2 */
7545 if (tape->file_version >= FILE_VERSION_1_2)
7547 byte store_participating_players = getFile8Bit(file);
7550 /* since version 1.2, tapes store which players participate in the tape */
7551 tape->num_participating_players = 0;
7552 for (i = 0; i < MAX_PLAYERS; i++)
7554 tape->player_participates[i] = FALSE;
7556 if (store_participating_players & (1 << i))
7558 tape->player_participates[i] = TRUE;
7559 tape->num_participating_players++;
7563 tape->use_mouse = (getFile8Bit(file) == 1 ? TRUE : FALSE);
7565 ReadUnusedBytesFromFile(file, TAPE_CHUNK_HEAD_UNUSED);
7567 engine_version = getFileVersion(file);
7568 if (engine_version > 0)
7569 tape->engine_version = engine_version;
7571 tape->engine_version = tape->game_version;
7577 static int LoadTape_INFO(File *file, int chunk_size, struct TapeInfo *tape)
7579 int level_identifier_size;
7582 level_identifier_size = getFile16BitBE(file);
7584 tape->level_identifier =
7585 checked_realloc(tape->level_identifier, level_identifier_size);
7587 for (i = 0; i < level_identifier_size; i++)
7588 tape->level_identifier[i] = getFile8Bit(file);
7590 tape->level_nr = getFile16BitBE(file);
7592 chunk_size = 2 + level_identifier_size + 2;
7597 static int LoadTape_BODY(File *file, int chunk_size, struct TapeInfo *tape)
7601 (tape->use_mouse ? 3 : tape->num_participating_players) + 1;
7602 int chunk_size_expected = tape_pos_size * tape->length;
7604 if (chunk_size_expected != chunk_size)
7606 ReadUnusedBytesFromFile(file, chunk_size);
7607 return chunk_size_expected;
7610 for (i = 0; i < tape->length; i++)
7612 if (i >= MAX_TAPE_LEN)
7614 Error(ERR_WARN, "tape truncated -- size exceeds maximum tape size %d",
7617 // tape too large; read and ignore remaining tape data from this chunk
7618 for (;i < tape->length; i++)
7619 ReadUnusedBytesFromFile(file, tape->num_participating_players + 1);
7624 if (tape->use_mouse)
7626 tape->pos[i].action[TAPE_ACTION_LX] = getFile8Bit(file);
7627 tape->pos[i].action[TAPE_ACTION_LY] = getFile8Bit(file);
7628 tape->pos[i].action[TAPE_ACTION_BUTTON] = getFile8Bit(file);
7630 tape->pos[i].action[TAPE_ACTION_UNUSED] = 0;
7634 for (j = 0; j < MAX_PLAYERS; j++)
7636 tape->pos[i].action[j] = MV_NONE;
7638 if (tape->player_participates[j])
7639 tape->pos[i].action[j] = getFile8Bit(file);
7643 tape->pos[i].delay = getFile8Bit(file);
7645 if (tape->file_version == FILE_VERSION_1_0)
7647 /* eliminate possible diagonal moves in old tapes */
7648 /* this is only for backward compatibility */
7650 byte joy_dir[4] = { JOY_LEFT, JOY_RIGHT, JOY_UP, JOY_DOWN };
7651 byte action = tape->pos[i].action[0];
7652 int k, num_moves = 0;
7654 for (k = 0; k<4; k++)
7656 if (action & joy_dir[k])
7658 tape->pos[i + num_moves].action[0] = joy_dir[k];
7660 tape->pos[i + num_moves].delay = 0;
7669 tape->length += num_moves;
7672 else if (tape->file_version < FILE_VERSION_2_0)
7674 /* convert pre-2.0 tapes to new tape format */
7676 if (tape->pos[i].delay > 1)
7679 tape->pos[i + 1] = tape->pos[i];
7680 tape->pos[i + 1].delay = 1;
7683 for (j = 0; j < MAX_PLAYERS; j++)
7684 tape->pos[i].action[j] = MV_NONE;
7685 tape->pos[i].delay--;
7692 if (checkEndOfFile(file))
7696 if (i != tape->length)
7697 chunk_size = tape_pos_size * i;
7702 void LoadTape_SokobanSolution(char *filename)
7705 int move_delay = TILESIZE / level.initial_player_stepsize[0];
7707 if (!(file = openFile(filename, MODE_READ)))
7709 tape.no_valid_file = TRUE;
7714 while (!checkEndOfFile(file))
7716 unsigned char c = getByteFromFile(file);
7718 if (checkEndOfFile(file))
7725 tape.pos[tape.length].action[0] = MV_UP;
7726 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
7732 tape.pos[tape.length].action[0] = MV_DOWN;
7733 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
7739 tape.pos[tape.length].action[0] = MV_LEFT;
7740 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
7746 tape.pos[tape.length].action[0] = MV_RIGHT;
7747 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
7755 /* ignore white-space characters */
7759 tape.no_valid_file = TRUE;
7761 Error(ERR_WARN, "unsupported Sokoban solution file '%s' ['%d']", filename, c);
7769 if (tape.no_valid_file)
7772 tape.length_frames = GetTapeLengthFrames();
7773 tape.length_seconds = GetTapeLengthSeconds();
7776 void LoadTapeFromFilename(char *filename)
7778 char cookie[MAX_LINE_LEN];
7779 char chunk_name[CHUNK_ID_LEN + 1];
7783 /* always start with reliable default values */
7784 setTapeInfoToDefaults();
7786 if (strSuffix(filename, ".sln"))
7788 LoadTape_SokobanSolution(filename);
7793 if (!(file = openFile(filename, MODE_READ)))
7795 tape.no_valid_file = TRUE;
7800 getFileChunkBE(file, chunk_name, NULL);
7801 if (strEqual(chunk_name, "RND1"))
7803 getFile32BitBE(file); /* not used */
7805 getFileChunkBE(file, chunk_name, NULL);
7806 if (!strEqual(chunk_name, "TAPE"))
7808 tape.no_valid_file = TRUE;
7810 Error(ERR_WARN, "unknown format of tape file '%s'", filename);
7817 else /* check for pre-2.0 file format with cookie string */
7819 strcpy(cookie, chunk_name);
7820 if (getStringFromFile(file, &cookie[4], MAX_LINE_LEN - 4) == NULL)
7822 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
7823 cookie[strlen(cookie) - 1] = '\0';
7825 if (!checkCookieString(cookie, TAPE_COOKIE_TMPL))
7827 tape.no_valid_file = TRUE;
7829 Error(ERR_WARN, "unknown format of tape file '%s'", filename);
7836 if ((tape.file_version = getFileVersionFromCookieString(cookie)) == -1)
7838 tape.no_valid_file = TRUE;
7840 Error(ERR_WARN, "unsupported version of tape file '%s'", filename);
7847 /* pre-2.0 tape files have no game version, so use file version here */
7848 tape.game_version = tape.file_version;
7851 if (tape.file_version < FILE_VERSION_1_2)
7853 /* tape files from versions before 1.2.0 without chunk structure */
7854 LoadTape_HEAD(file, TAPE_CHUNK_HEAD_SIZE, &tape);
7855 LoadTape_BODY(file, 2 * tape.length, &tape);
7863 int (*loader)(File *, int, struct TapeInfo *);
7867 { "VERS", TAPE_CHUNK_VERS_SIZE, LoadTape_VERS },
7868 { "HEAD", TAPE_CHUNK_HEAD_SIZE, LoadTape_HEAD },
7869 { "INFO", -1, LoadTape_INFO },
7870 { "BODY", -1, LoadTape_BODY },
7874 while (getFileChunkBE(file, chunk_name, &chunk_size))
7878 while (chunk_info[i].name != NULL &&
7879 !strEqual(chunk_name, chunk_info[i].name))
7882 if (chunk_info[i].name == NULL)
7884 Error(ERR_WARN, "unknown chunk '%s' in tape file '%s'",
7885 chunk_name, filename);
7886 ReadUnusedBytesFromFile(file, chunk_size);
7888 else if (chunk_info[i].size != -1 &&
7889 chunk_info[i].size != chunk_size)
7891 Error(ERR_WARN, "wrong size (%d) of chunk '%s' in tape file '%s'",
7892 chunk_size, chunk_name, filename);
7893 ReadUnusedBytesFromFile(file, chunk_size);
7897 /* call function to load this tape chunk */
7898 int chunk_size_expected =
7899 (chunk_info[i].loader)(file, chunk_size, &tape);
7901 /* the size of some chunks cannot be checked before reading other
7902 chunks first (like "HEAD" and "BODY") that contain some header
7903 information, so check them here */
7904 if (chunk_size_expected != chunk_size)
7906 Error(ERR_WARN, "wrong size (%d) of chunk '%s' in tape file '%s'",
7907 chunk_size, chunk_name, filename);
7915 tape.length_frames = GetTapeLengthFrames();
7916 tape.length_seconds = GetTapeLengthSeconds();
7919 printf("::: tape file version: %d\n", tape.file_version);
7920 printf("::: tape game version: %d\n", tape.game_version);
7921 printf("::: tape engine version: %d\n", tape.engine_version);
7925 void LoadTape(int nr)
7927 char *filename = getTapeFilename(nr);
7929 LoadTapeFromFilename(filename);
7932 void LoadSolutionTape(int nr)
7934 char *filename = getSolutionTapeFilename(nr);
7936 LoadTapeFromFilename(filename);
7938 if (TAPE_IS_EMPTY(tape) &&
7939 level.game_engine_type == GAME_ENGINE_TYPE_SP &&
7940 level.native_sp_level->demo.is_available)
7941 CopyNativeTape_SP_to_RND(&level);
7944 static void SaveTape_VERS(FILE *file, struct TapeInfo *tape)
7946 putFileVersion(file, tape->file_version);
7947 putFileVersion(file, tape->game_version);
7950 static void SaveTape_HEAD(FILE *file, struct TapeInfo *tape)
7953 byte store_participating_players = 0;
7955 /* set bits for participating players for compact storage */
7956 for (i = 0; i < MAX_PLAYERS; i++)
7957 if (tape->player_participates[i])
7958 store_participating_players |= (1 << i);
7960 putFile32BitBE(file, tape->random_seed);
7961 putFile32BitBE(file, tape->date);
7962 putFile32BitBE(file, tape->length);
7964 putFile8Bit(file, store_participating_players);
7966 putFile8Bit(file, (tape->use_mouse ? 1 : 0));
7968 /* unused bytes not at the end here for 4-byte alignment of engine_version */
7969 WriteUnusedBytesToFile(file, TAPE_CHUNK_HEAD_UNUSED);
7971 putFileVersion(file, tape->engine_version);
7974 static void SaveTape_INFO(FILE *file, struct TapeInfo *tape)
7976 int level_identifier_size = strlen(tape->level_identifier) + 1;
7979 putFile16BitBE(file, level_identifier_size);
7981 for (i = 0; i < level_identifier_size; i++)
7982 putFile8Bit(file, tape->level_identifier[i]);
7984 putFile16BitBE(file, tape->level_nr);
7987 static void SaveTape_BODY(FILE *file, struct TapeInfo *tape)
7991 for (i = 0; i < tape->length; i++)
7993 if (tape->use_mouse)
7995 putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_LX]);
7996 putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_LY]);
7997 putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_BUTTON]);
8001 for (j = 0; j < MAX_PLAYERS; j++)
8002 if (tape->player_participates[j])
8003 putFile8Bit(file, tape->pos[i].action[j]);
8006 putFile8Bit(file, tape->pos[i].delay);
8010 void SaveTape(int nr)
8012 char *filename = getTapeFilename(nr);
8014 int num_participating_players = 0;
8016 int info_chunk_size;
8017 int body_chunk_size;
8020 InitTapeDirectory(leveldir_current->subdir);
8022 if (!(file = fopen(filename, MODE_WRITE)))
8024 Error(ERR_WARN, "cannot save level recording file '%s'", filename);
8028 tape.file_version = FILE_VERSION_ACTUAL;
8029 tape.game_version = GAME_VERSION_ACTUAL;
8031 /* count number of participating players */
8032 for (i = 0; i < MAX_PLAYERS; i++)
8033 if (tape.player_participates[i])
8034 num_participating_players++;
8036 tape_pos_size = (tape.use_mouse ? 3 : num_participating_players) + 1;
8038 info_chunk_size = 2 + (strlen(tape.level_identifier) + 1) + 2;
8039 body_chunk_size = tape_pos_size * tape.length;
8041 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
8042 putFileChunkBE(file, "TAPE", CHUNK_SIZE_NONE);
8044 putFileChunkBE(file, "VERS", TAPE_CHUNK_VERS_SIZE);
8045 SaveTape_VERS(file, &tape);
8047 putFileChunkBE(file, "HEAD", TAPE_CHUNK_HEAD_SIZE);
8048 SaveTape_HEAD(file, &tape);
8050 putFileChunkBE(file, "INFO", info_chunk_size);
8051 SaveTape_INFO(file, &tape);
8053 putFileChunkBE(file, "BODY", body_chunk_size);
8054 SaveTape_BODY(file, &tape);
8058 SetFilePermissions(filename, PERMS_PRIVATE);
8060 tape.changed = FALSE;
8063 boolean SaveTapeChecked(int nr)
8065 char *filename = getTapeFilename(nr);
8066 boolean new_tape = !fileExists(filename);
8067 boolean tape_saved = FALSE;
8069 if (new_tape || Request("Replace old tape?", REQ_ASK))
8074 Request("Tape saved!", REQ_CONFIRM);
8082 void DumpTape(struct TapeInfo *tape)
8084 int tape_frame_counter;
8087 if (tape->no_valid_file)
8089 Error(ERR_WARN, "cannot dump -- no valid tape file found");
8095 Print("Tape of Level %03d (file version %08d, game version %08d)\n",
8096 tape->level_nr, tape->file_version, tape->game_version);
8097 Print(" (effective engine version %08d)\n",
8098 tape->engine_version);
8099 Print("Level series identifier: '%s'\n", tape->level_identifier);
8102 tape_frame_counter = 0;
8104 for (i = 0; i < tape->length; i++)
8106 if (i >= MAX_TAPE_LEN)
8111 for (j = 0; j < MAX_PLAYERS; j++)
8113 if (tape->player_participates[j])
8115 int action = tape->pos[i].action[j];
8117 Print("%d:%02x ", j, action);
8118 Print("[%c%c%c%c|%c%c] - ",
8119 (action & JOY_LEFT ? '<' : ' '),
8120 (action & JOY_RIGHT ? '>' : ' '),
8121 (action & JOY_UP ? '^' : ' '),
8122 (action & JOY_DOWN ? 'v' : ' '),
8123 (action & JOY_BUTTON_1 ? '1' : ' '),
8124 (action & JOY_BUTTON_2 ? '2' : ' '));
8128 Print("(%03d) ", tape->pos[i].delay);
8129 Print("[%05d]\n", tape_frame_counter);
8131 tape_frame_counter += tape->pos[i].delay;
8138 /* ========================================================================= */
8139 /* score file functions */
8140 /* ========================================================================= */
8142 void LoadScore(int nr)
8145 char *filename = getScoreFilename(nr);
8146 char cookie[MAX_LINE_LEN];
8147 char line[MAX_LINE_LEN];
8151 /* always start with reliable default values */
8152 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
8154 strcpy(highscore[i].Name, EMPTY_PLAYER_NAME);
8155 highscore[i].Score = 0;
8158 if (!(file = fopen(filename, MODE_READ)))
8161 /* check file identifier */
8162 if (fgets(cookie, MAX_LINE_LEN, file) == NULL)
8164 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
8165 cookie[strlen(cookie) - 1] = '\0';
8167 if (!checkCookieString(cookie, SCORE_COOKIE))
8169 Error(ERR_WARN, "unknown format of score file '%s'", filename);
8174 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
8176 if (fscanf(file, "%d", &highscore[i].Score) == EOF)
8177 Error(ERR_WARN, "fscanf() failed; %s", strerror(errno));
8178 if (fgets(line, MAX_LINE_LEN, file) == NULL)
8181 if (strlen(line) > 0 && line[strlen(line) - 1] == '\n')
8182 line[strlen(line) - 1] = '\0';
8184 for (line_ptr = line; *line_ptr; line_ptr++)
8186 if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != '\0')
8188 strncpy(highscore[i].Name, line_ptr, MAX_PLAYER_NAME_LEN);
8189 highscore[i].Name[MAX_PLAYER_NAME_LEN] = '\0';
8198 void SaveScore(int nr)
8201 int permissions = (program.global_scores ? PERMS_PUBLIC : PERMS_PRIVATE);
8202 char *filename = getScoreFilename(nr);
8205 InitScoreDirectory(leveldir_current->subdir);
8207 if (!(file = fopen(filename, MODE_WRITE)))
8209 Error(ERR_WARN, "cannot save score for level %d", nr);
8213 fprintf(file, "%s\n\n", SCORE_COOKIE);
8215 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
8216 fprintf(file, "%d %s\n", highscore[i].Score, highscore[i].Name);
8220 SetFilePermissions(filename, permissions);
8224 /* ========================================================================= */
8225 /* setup file functions */
8226 /* ========================================================================= */
8228 #define TOKEN_STR_PLAYER_PREFIX "player_"
8231 #define SETUP_TOKEN_PLAYER_NAME 0
8232 #define SETUP_TOKEN_SOUND 1
8233 #define SETUP_TOKEN_SOUND_LOOPS 2
8234 #define SETUP_TOKEN_SOUND_MUSIC 3
8235 #define SETUP_TOKEN_SOUND_SIMPLE 4
8236 #define SETUP_TOKEN_TOONS 5
8237 #define SETUP_TOKEN_SCROLL_DELAY 6
8238 #define SETUP_TOKEN_SCROLL_DELAY_VALUE 7
8239 #define SETUP_TOKEN_ENGINE_SNAPSHOT_MODE 8
8240 #define SETUP_TOKEN_ENGINE_SNAPSHOT_MEMORY 9
8241 #define SETUP_TOKEN_FADE_SCREENS 10
8242 #define SETUP_TOKEN_AUTORECORD 11
8243 #define SETUP_TOKEN_SHOW_TITLESCREEN 12
8244 #define SETUP_TOKEN_QUICK_DOORS 13
8245 #define SETUP_TOKEN_TEAM_MODE 14
8246 #define SETUP_TOKEN_HANDICAP 15
8247 #define SETUP_TOKEN_SKIP_LEVELS 16
8248 #define SETUP_TOKEN_INCREMENT_LEVELS 17
8249 #define SETUP_TOKEN_TIME_LIMIT 18
8250 #define SETUP_TOKEN_FULLSCREEN 19
8251 #define SETUP_TOKEN_WINDOW_SCALING_PERCENT 20
8252 #define SETUP_TOKEN_WINDOW_SCALING_QUALITY 21
8253 #define SETUP_TOKEN_SCREEN_RENDERING_MODE 22
8254 #define SETUP_TOKEN_ASK_ON_ESCAPE 23
8255 #define SETUP_TOKEN_ASK_ON_ESCAPE_EDITOR 24
8256 #define SETUP_TOKEN_QUICK_SWITCH 25
8257 #define SETUP_TOKEN_INPUT_ON_FOCUS 26
8258 #define SETUP_TOKEN_PREFER_AGA_GRAPHICS 27
8259 #define SETUP_TOKEN_GAME_FRAME_DELAY 28
8260 #define SETUP_TOKEN_SP_SHOW_BORDER_ELEMENTS 29
8261 #define SETUP_TOKEN_SMALL_GAME_GRAPHICS 30
8262 #define SETUP_TOKEN_SHOW_SNAPSHOT_BUTTONS 31
8263 #define SETUP_TOKEN_GRAPHICS_SET 32
8264 #define SETUP_TOKEN_SOUNDS_SET 33
8265 #define SETUP_TOKEN_MUSIC_SET 34
8266 #define SETUP_TOKEN_OVERRIDE_LEVEL_GRAPHICS 35
8267 #define SETUP_TOKEN_OVERRIDE_LEVEL_SOUNDS 36
8268 #define SETUP_TOKEN_OVERRIDE_LEVEL_MUSIC 37
8269 #define SETUP_TOKEN_VOLUME_SIMPLE 38
8270 #define SETUP_TOKEN_VOLUME_LOOPS 39
8271 #define SETUP_TOKEN_VOLUME_MUSIC 40
8272 #define SETUP_TOKEN_TOUCH_CONTROL_TYPE 41
8273 #define SETUP_TOKEN_TOUCH_MOVE_DISTANCE 42
8274 #define SETUP_TOKEN_TOUCH_DROP_DISTANCE 43
8276 #define NUM_GLOBAL_SETUP_TOKENS 44
8279 #define SETUP_TOKEN_AUTO_EDITOR_ZOOM_TILESIZE 0
8281 #define NUM_AUTO_SETUP_TOKENS 1
8284 #define SETUP_TOKEN_EDITOR_EL_CLASSIC 0
8285 #define SETUP_TOKEN_EDITOR_EL_CUSTOM 1
8286 #define SETUP_TOKEN_EDITOR_EL_USER_DEFINED 2
8287 #define SETUP_TOKEN_EDITOR_EL_DYNAMIC 3
8288 #define SETUP_TOKEN_EDITOR_EL_HEADLINES 4
8289 #define SETUP_TOKEN_EDITOR_SHOW_ELEMENT_TOKEN 5
8291 #define NUM_EDITOR_SETUP_TOKENS 6
8293 /* editor cascade setup */
8294 #define SETUP_TOKEN_EDITOR_CASCADE_BD 0
8295 #define SETUP_TOKEN_EDITOR_CASCADE_EM 1
8296 #define SETUP_TOKEN_EDITOR_CASCADE_EMC 2
8297 #define SETUP_TOKEN_EDITOR_CASCADE_RND 3
8298 #define SETUP_TOKEN_EDITOR_CASCADE_SB 4
8299 #define SETUP_TOKEN_EDITOR_CASCADE_SP 5
8300 #define SETUP_TOKEN_EDITOR_CASCADE_DC 6
8301 #define SETUP_TOKEN_EDITOR_CASCADE_DX 7
8302 #define SETUP_TOKEN_EDITOR_CASCADE_TEXT 8
8303 #define SETUP_TOKEN_EDITOR_CASCADE_STEELTEXT 9
8304 #define SETUP_TOKEN_EDITOR_CASCADE_CE 10
8305 #define SETUP_TOKEN_EDITOR_CASCADE_GE 11
8306 #define SETUP_TOKEN_EDITOR_CASCADE_REF 12
8307 #define SETUP_TOKEN_EDITOR_CASCADE_USER 13
8308 #define SETUP_TOKEN_EDITOR_CASCADE_DYNAMIC 14
8310 #define NUM_EDITOR_CASCADE_SETUP_TOKENS 15
8312 /* shortcut setup */
8313 #define SETUP_TOKEN_SHORTCUT_SAVE_GAME 0
8314 #define SETUP_TOKEN_SHORTCUT_LOAD_GAME 1
8315 #define SETUP_TOKEN_SHORTCUT_TOGGLE_PAUSE 2
8316 #define SETUP_TOKEN_SHORTCUT_FOCUS_PLAYER_1 3
8317 #define SETUP_TOKEN_SHORTCUT_FOCUS_PLAYER_2 4
8318 #define SETUP_TOKEN_SHORTCUT_FOCUS_PLAYER_3 5
8319 #define SETUP_TOKEN_SHORTCUT_FOCUS_PLAYER_4 6
8320 #define SETUP_TOKEN_SHORTCUT_FOCUS_PLAYER_ALL 7
8321 #define SETUP_TOKEN_SHORTCUT_TAPE_EJECT 8
8322 #define SETUP_TOKEN_SHORTCUT_TAPE_EXTRA 9
8323 #define SETUP_TOKEN_SHORTCUT_TAPE_STOP 10
8324 #define SETUP_TOKEN_SHORTCUT_TAPE_PAUSE 11
8325 #define SETUP_TOKEN_SHORTCUT_TAPE_RECORD 12
8326 #define SETUP_TOKEN_SHORTCUT_TAPE_PLAY 13
8327 #define SETUP_TOKEN_SHORTCUT_SOUND_SIMPLE 14
8328 #define SETUP_TOKEN_SHORTCUT_SOUND_LOOPS 15
8329 #define SETUP_TOKEN_SHORTCUT_SOUND_MUSIC 16
8330 #define SETUP_TOKEN_SHORTCUT_SNAP_LEFT 17
8331 #define SETUP_TOKEN_SHORTCUT_SNAP_RIGHT 18
8332 #define SETUP_TOKEN_SHORTCUT_SNAP_UP 19
8333 #define SETUP_TOKEN_SHORTCUT_SNAP_DOWN 20
8335 #define NUM_SHORTCUT_SETUP_TOKENS 21
8338 #define SETUP_TOKEN_PLAYER_USE_JOYSTICK 0
8339 #define SETUP_TOKEN_PLAYER_JOY_DEVICE_NAME 1
8340 #define SETUP_TOKEN_PLAYER_JOY_XLEFT 2
8341 #define SETUP_TOKEN_PLAYER_JOY_XMIDDLE 3
8342 #define SETUP_TOKEN_PLAYER_JOY_XRIGHT 4
8343 #define SETUP_TOKEN_PLAYER_JOY_YUPPER 5
8344 #define SETUP_TOKEN_PLAYER_JOY_YMIDDLE 6
8345 #define SETUP_TOKEN_PLAYER_JOY_YLOWER 7
8346 #define SETUP_TOKEN_PLAYER_JOY_SNAP 8
8347 #define SETUP_TOKEN_PLAYER_JOY_DROP 9
8348 #define SETUP_TOKEN_PLAYER_KEY_LEFT 10
8349 #define SETUP_TOKEN_PLAYER_KEY_RIGHT 11
8350 #define SETUP_TOKEN_PLAYER_KEY_UP 12
8351 #define SETUP_TOKEN_PLAYER_KEY_DOWN 13
8352 #define SETUP_TOKEN_PLAYER_KEY_SNAP 14
8353 #define SETUP_TOKEN_PLAYER_KEY_DROP 15
8355 #define NUM_PLAYER_SETUP_TOKENS 16
8358 #define SETUP_TOKEN_SYSTEM_SDL_VIDEODRIVER 0
8359 #define SETUP_TOKEN_SYSTEM_SDL_AUDIODRIVER 1
8360 #define SETUP_TOKEN_SYSTEM_AUDIO_FRAGMENT_SIZE 2
8362 #define NUM_SYSTEM_SETUP_TOKENS 3
8364 /* internal setup */
8365 #define SETUP_TOKEN_INT_PROGRAM_TITLE 0
8366 #define SETUP_TOKEN_INT_PROGRAM_VERSION 1
8367 #define SETUP_TOKEN_INT_PROGRAM_AUTHOR 2
8368 #define SETUP_TOKEN_INT_PROGRAM_EMAIL 3
8369 #define SETUP_TOKEN_INT_PROGRAM_WEBSITE 4
8370 #define SETUP_TOKEN_INT_PROGRAM_COPYRIGHT 5
8371 #define SETUP_TOKEN_INT_PROGRAM_COMPANY 6
8372 #define SETUP_TOKEN_INT_PROGRAM_ICON_FILE 7
8373 #define SETUP_TOKEN_INT_DEFAULT_GRAPHICS_SET 8
8374 #define SETUP_TOKEN_INT_DEFAULT_SOUNDS_SET 9
8375 #define SETUP_TOKEN_INT_DEFAULT_MUSIC_SET 10
8376 #define SETUP_TOKEN_INT_FALLBACK_GRAPHICS_FILE 11
8377 #define SETUP_TOKEN_INT_FALLBACK_SOUNDS_FILE 12
8378 #define SETUP_TOKEN_INT_FALLBACK_MUSIC_FILE 13
8379 #define SETUP_TOKEN_INT_DEFAULT_LEVEL_SERIES 14
8380 #define SETUP_TOKEN_INT_CHOOSE_FROM_TOP_LEVELDIR 15
8381 #define SETUP_TOKEN_INT_SHOW_SCALING_IN_TITLE 16
8382 #define SETUP_TOKEN_INT_DEFAULT_WINDOW_WIDTH 17
8383 #define SETUP_TOKEN_INT_DEFAULT_WINDOW_HEIGHT 18
8385 #define NUM_INTERNAL_SETUP_TOKENS 19
8388 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_0 0
8389 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_1 1
8390 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_2 2
8391 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_3 3
8392 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_4 4
8393 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_5 5
8394 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_6 6
8395 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_7 7
8396 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_8 8
8397 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_9 9
8398 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_KEY_0 10
8399 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_KEY_1 11
8400 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_KEY_2 12
8401 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_KEY_3 13
8402 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_KEY_4 14
8403 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_KEY_5 15
8404 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_KEY_6 16
8405 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_KEY_7 17
8406 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_KEY_8 18
8407 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_KEY_9 19
8408 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_USE_MOD_KEY 20
8409 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_GAME_ONLY 21
8410 #define SETUP_TOKEN_DEBUG_SHOW_FRAMES_PER_SECOND 22
8412 #define NUM_DEBUG_SETUP_TOKENS 23
8415 #define SETUP_TOKEN_OPTIONS_VERBOSE 0
8417 #define NUM_OPTIONS_SETUP_TOKENS 1
8420 static struct SetupInfo si;
8421 static struct SetupAutoSetupInfo sasi;
8422 static struct SetupEditorInfo sei;
8423 static struct SetupEditorCascadeInfo seci;
8424 static struct SetupShortcutInfo ssi;
8425 static struct SetupInputInfo sii;
8426 static struct SetupSystemInfo syi;
8427 static struct SetupInternalInfo sxi;
8428 static struct SetupDebugInfo sdi;
8429 static struct OptionInfo soi;
8431 static struct TokenInfo global_setup_tokens[] =
8433 { TYPE_STRING, &si.player_name, "player_name" },
8434 { TYPE_SWITCH, &si.sound, "sound" },
8435 { TYPE_SWITCH, &si.sound_loops, "repeating_sound_loops" },
8436 { TYPE_SWITCH, &si.sound_music, "background_music" },
8437 { TYPE_SWITCH, &si.sound_simple, "simple_sound_effects" },
8438 { TYPE_SWITCH, &si.toons, "toons" },
8439 { TYPE_SWITCH, &si.scroll_delay, "scroll_delay" },
8440 { TYPE_INTEGER,&si.scroll_delay_value, "scroll_delay_value" },
8441 { TYPE_STRING, &si.engine_snapshot_mode, "engine_snapshot_mode" },
8442 { TYPE_INTEGER,&si.engine_snapshot_memory, "engine_snapshot_memory" },
8443 { TYPE_SWITCH, &si.fade_screens, "fade_screens" },
8444 { TYPE_SWITCH, &si.autorecord, "automatic_tape_recording"},
8445 { TYPE_SWITCH, &si.show_titlescreen, "show_titlescreen" },
8446 { TYPE_SWITCH, &si.quick_doors, "quick_doors" },
8447 { TYPE_SWITCH, &si.team_mode, "team_mode" },
8448 { TYPE_SWITCH, &si.handicap, "handicap" },
8449 { TYPE_SWITCH, &si.skip_levels, "skip_levels" },
8450 { TYPE_SWITCH, &si.increment_levels, "increment_levels" },
8451 { TYPE_SWITCH, &si.time_limit, "time_limit" },
8452 { TYPE_SWITCH, &si.fullscreen, "fullscreen" },
8453 { TYPE_INTEGER,&si.window_scaling_percent, "window_scaling_percent" },
8454 { TYPE_STRING, &si.window_scaling_quality, "window_scaling_quality" },
8455 { TYPE_STRING, &si.screen_rendering_mode, "screen_rendering_mode" },
8456 { TYPE_SWITCH, &si.ask_on_escape, "ask_on_escape" },
8457 { TYPE_SWITCH, &si.ask_on_escape_editor, "ask_on_escape_editor" },
8458 { TYPE_SWITCH, &si.quick_switch, "quick_player_switch" },
8459 { TYPE_SWITCH, &si.input_on_focus, "input_on_focus" },
8460 { TYPE_SWITCH, &si.prefer_aga_graphics, "prefer_aga_graphics" },
8461 { TYPE_INTEGER,&si.game_frame_delay, "game_frame_delay" },
8462 { TYPE_SWITCH, &si.sp_show_border_elements, "sp_show_border_elements" },
8463 { TYPE_SWITCH, &si.small_game_graphics, "small_game_graphics" },
8464 { TYPE_SWITCH, &si.show_snapshot_buttons, "show_snapshot_buttons" },
8465 { TYPE_STRING, &si.graphics_set, "graphics_set" },
8466 { TYPE_STRING, &si.sounds_set, "sounds_set" },
8467 { TYPE_STRING, &si.music_set, "music_set" },
8468 { TYPE_SWITCH3,&si.override_level_graphics, "override_level_graphics" },
8469 { TYPE_SWITCH3,&si.override_level_sounds, "override_level_sounds" },
8470 { TYPE_SWITCH3,&si.override_level_music, "override_level_music" },
8471 { TYPE_INTEGER,&si.volume_simple, "volume_simple" },
8472 { TYPE_INTEGER,&si.volume_loops, "volume_loops" },
8473 { TYPE_INTEGER,&si.volume_music, "volume_music" },
8474 { TYPE_STRING, &si.touch.control_type, "touch.control_type" },
8475 { TYPE_INTEGER,&si.touch.move_distance, "touch.move_distance" },
8476 { TYPE_INTEGER,&si.touch.drop_distance, "touch.drop_distance" },
8479 static struct TokenInfo auto_setup_tokens[] =
8481 { TYPE_INTEGER,&sasi.editor_zoom_tilesize, "editor.zoom_tilesize" },
8484 static struct TokenInfo editor_setup_tokens[] =
8486 { TYPE_SWITCH, &sei.el_classic, "editor.el_classic" },
8487 { TYPE_SWITCH, &sei.el_custom, "editor.el_custom" },
8488 { TYPE_SWITCH, &sei.el_user_defined, "editor.el_user_defined" },
8489 { TYPE_SWITCH, &sei.el_dynamic, "editor.el_dynamic" },
8490 { TYPE_SWITCH, &sei.el_headlines, "editor.el_headlines" },
8491 { TYPE_SWITCH, &sei.show_element_token,"editor.show_element_token" },
8494 static struct TokenInfo editor_cascade_setup_tokens[] =
8496 { TYPE_SWITCH, &seci.el_bd, "editor.cascade.el_bd" },
8497 { TYPE_SWITCH, &seci.el_em, "editor.cascade.el_em" },
8498 { TYPE_SWITCH, &seci.el_emc, "editor.cascade.el_emc" },
8499 { TYPE_SWITCH, &seci.el_rnd, "editor.cascade.el_rnd" },
8500 { TYPE_SWITCH, &seci.el_sb, "editor.cascade.el_sb" },
8501 { TYPE_SWITCH, &seci.el_sp, "editor.cascade.el_sp" },
8502 { TYPE_SWITCH, &seci.el_dc, "editor.cascade.el_dc" },
8503 { TYPE_SWITCH, &seci.el_dx, "editor.cascade.el_dx" },
8504 { TYPE_SWITCH, &seci.el_mm, "editor.cascade.el_mm" },
8505 { TYPE_SWITCH, &seci.el_df, "editor.cascade.el_df" },
8506 { TYPE_SWITCH, &seci.el_chars, "editor.cascade.el_chars" },
8507 { TYPE_SWITCH, &seci.el_steel_chars, "editor.cascade.el_steel_chars" },
8508 { TYPE_SWITCH, &seci.el_ce, "editor.cascade.el_ce" },
8509 { TYPE_SWITCH, &seci.el_ge, "editor.cascade.el_ge" },
8510 { TYPE_SWITCH, &seci.el_ref, "editor.cascade.el_ref" },
8511 { TYPE_SWITCH, &seci.el_user, "editor.cascade.el_user" },
8512 { TYPE_SWITCH, &seci.el_dynamic, "editor.cascade.el_dynamic" },
8515 static struct TokenInfo shortcut_setup_tokens[] =
8517 { TYPE_KEY_X11, &ssi.save_game, "shortcut.save_game" },
8518 { TYPE_KEY_X11, &ssi.load_game, "shortcut.load_game" },
8519 { TYPE_KEY_X11, &ssi.toggle_pause, "shortcut.toggle_pause" },
8520 { TYPE_KEY_X11, &ssi.focus_player[0], "shortcut.focus_player_1" },
8521 { TYPE_KEY_X11, &ssi.focus_player[1], "shortcut.focus_player_2" },
8522 { TYPE_KEY_X11, &ssi.focus_player[2], "shortcut.focus_player_3" },
8523 { TYPE_KEY_X11, &ssi.focus_player[3], "shortcut.focus_player_4" },
8524 { TYPE_KEY_X11, &ssi.focus_player_all,"shortcut.focus_player_all" },
8525 { TYPE_KEY_X11, &ssi.tape_eject, "shortcut.tape_eject" },
8526 { TYPE_KEY_X11, &ssi.tape_extra, "shortcut.tape_extra" },
8527 { TYPE_KEY_X11, &ssi.tape_stop, "shortcut.tape_stop" },
8528 { TYPE_KEY_X11, &ssi.tape_pause, "shortcut.tape_pause" },
8529 { TYPE_KEY_X11, &ssi.tape_record, "shortcut.tape_record" },
8530 { TYPE_KEY_X11, &ssi.tape_play, "shortcut.tape_play" },
8531 { TYPE_KEY_X11, &ssi.sound_simple, "shortcut.sound_simple" },
8532 { TYPE_KEY_X11, &ssi.sound_loops, "shortcut.sound_loops" },
8533 { TYPE_KEY_X11, &ssi.sound_music, "shortcut.sound_music" },
8534 { TYPE_KEY_X11, &ssi.snap_left, "shortcut.snap_left" },
8535 { TYPE_KEY_X11, &ssi.snap_right, "shortcut.snap_right" },
8536 { TYPE_KEY_X11, &ssi.snap_up, "shortcut.snap_up" },
8537 { TYPE_KEY_X11, &ssi.snap_down, "shortcut.snap_down" },
8540 static struct TokenInfo player_setup_tokens[] =
8542 { TYPE_BOOLEAN, &sii.use_joystick, ".use_joystick" },
8543 { TYPE_STRING, &sii.joy.device_name, ".joy.device_name" },
8544 { TYPE_INTEGER, &sii.joy.xleft, ".joy.xleft" },
8545 { TYPE_INTEGER, &sii.joy.xmiddle, ".joy.xmiddle" },
8546 { TYPE_INTEGER, &sii.joy.xright, ".joy.xright" },
8547 { TYPE_INTEGER, &sii.joy.yupper, ".joy.yupper" },
8548 { TYPE_INTEGER, &sii.joy.ymiddle, ".joy.ymiddle" },
8549 { TYPE_INTEGER, &sii.joy.ylower, ".joy.ylower" },
8550 { TYPE_INTEGER, &sii.joy.snap, ".joy.snap_field" },
8551 { TYPE_INTEGER, &sii.joy.drop, ".joy.place_bomb" },
8552 { TYPE_KEY_X11, &sii.key.left, ".key.move_left" },
8553 { TYPE_KEY_X11, &sii.key.right, ".key.move_right" },
8554 { TYPE_KEY_X11, &sii.key.up, ".key.move_up" },
8555 { TYPE_KEY_X11, &sii.key.down, ".key.move_down" },
8556 { TYPE_KEY_X11, &sii.key.snap, ".key.snap_field" },
8557 { TYPE_KEY_X11, &sii.key.drop, ".key.place_bomb" },
8560 static struct TokenInfo system_setup_tokens[] =
8562 { TYPE_STRING, &syi.sdl_videodriver, "system.sdl_videodriver" },
8563 { TYPE_STRING, &syi.sdl_audiodriver, "system.sdl_audiodriver" },
8564 { TYPE_INTEGER, &syi.audio_fragment_size,"system.audio_fragment_size" },
8567 static struct TokenInfo internal_setup_tokens[] =
8569 { TYPE_STRING, &sxi.program_title, "program_title" },
8570 { TYPE_STRING, &sxi.program_version, "program_version" },
8571 { TYPE_STRING, &sxi.program_author, "program_author" },
8572 { TYPE_STRING, &sxi.program_email, "program_email" },
8573 { TYPE_STRING, &sxi.program_website, "program_website" },
8574 { TYPE_STRING, &sxi.program_copyright, "program_copyright" },
8575 { TYPE_STRING, &sxi.program_company, "program_company" },
8576 { TYPE_STRING, &sxi.program_icon_file, "program_icon_file" },
8577 { TYPE_STRING, &sxi.default_graphics_set, "default_graphics_set" },
8578 { TYPE_STRING, &sxi.default_sounds_set, "default_sounds_set" },
8579 { TYPE_STRING, &sxi.default_music_set, "default_music_set" },
8580 { TYPE_STRING, &sxi.fallback_graphics_file, "fallback_graphics_file"},
8581 { TYPE_STRING, &sxi.fallback_sounds_file, "fallback_sounds_file" },
8582 { TYPE_STRING, &sxi.fallback_music_file, "fallback_music_file" },
8583 { TYPE_STRING, &sxi.default_level_series, "default_level_series" },
8584 { TYPE_BOOLEAN,&sxi.choose_from_top_leveldir, "choose_from_top_leveldir" },
8585 { TYPE_BOOLEAN,&sxi.show_scaling_in_title, "show_scaling_in_title" },
8586 { TYPE_INTEGER,&sxi.default_window_width, "default_window_width" },
8587 { TYPE_INTEGER,&sxi.default_window_height, "default_window_height" },
8590 static struct TokenInfo debug_setup_tokens[] =
8592 { TYPE_INTEGER, &sdi.frame_delay[0], "debug.frame_delay_0" },
8593 { TYPE_INTEGER, &sdi.frame_delay[1], "debug.frame_delay_1" },
8594 { TYPE_INTEGER, &sdi.frame_delay[2], "debug.frame_delay_2" },
8595 { TYPE_INTEGER, &sdi.frame_delay[3], "debug.frame_delay_3" },
8596 { TYPE_INTEGER, &sdi.frame_delay[4], "debug.frame_delay_4" },
8597 { TYPE_INTEGER, &sdi.frame_delay[5], "debug.frame_delay_5" },
8598 { TYPE_INTEGER, &sdi.frame_delay[6], "debug.frame_delay_6" },
8599 { TYPE_INTEGER, &sdi.frame_delay[7], "debug.frame_delay_7" },
8600 { TYPE_INTEGER, &sdi.frame_delay[8], "debug.frame_delay_8" },
8601 { TYPE_INTEGER, &sdi.frame_delay[9], "debug.frame_delay_9" },
8602 { TYPE_KEY_X11, &sdi.frame_delay_key[0], "debug.key.frame_delay_0" },
8603 { TYPE_KEY_X11, &sdi.frame_delay_key[1], "debug.key.frame_delay_1" },
8604 { TYPE_KEY_X11, &sdi.frame_delay_key[2], "debug.key.frame_delay_2" },
8605 { TYPE_KEY_X11, &sdi.frame_delay_key[3], "debug.key.frame_delay_3" },
8606 { TYPE_KEY_X11, &sdi.frame_delay_key[4], "debug.key.frame_delay_4" },
8607 { TYPE_KEY_X11, &sdi.frame_delay_key[5], "debug.key.frame_delay_5" },
8608 { TYPE_KEY_X11, &sdi.frame_delay_key[6], "debug.key.frame_delay_6" },
8609 { TYPE_KEY_X11, &sdi.frame_delay_key[7], "debug.key.frame_delay_7" },
8610 { TYPE_KEY_X11, &sdi.frame_delay_key[8], "debug.key.frame_delay_8" },
8611 { TYPE_KEY_X11, &sdi.frame_delay_key[9], "debug.key.frame_delay_9" },
8612 { TYPE_BOOLEAN, &sdi.frame_delay_use_mod_key,"debug.frame_delay.use_mod_key"},
8613 { TYPE_BOOLEAN, &sdi.frame_delay_game_only, "debug.frame_delay.game_only" },
8614 { TYPE_BOOLEAN, &sdi.show_frames_per_second, "debug.show_frames_per_second" },
8617 static struct TokenInfo options_setup_tokens[] =
8619 { TYPE_BOOLEAN, &soi.verbose, "options.verbose" },
8622 static char *get_corrected_login_name(char *login_name)
8624 /* needed because player name must be a fixed length string */
8625 char *login_name_new = checked_malloc(MAX_PLAYER_NAME_LEN + 1);
8627 strncpy(login_name_new, login_name, MAX_PLAYER_NAME_LEN);
8628 login_name_new[MAX_PLAYER_NAME_LEN] = '\0';
8630 if (strlen(login_name) > MAX_PLAYER_NAME_LEN) /* name has been cut */
8631 if (strchr(login_name_new, ' '))
8632 *strchr(login_name_new, ' ') = '\0';
8634 return login_name_new;
8637 static void setSetupInfoToDefaults(struct SetupInfo *si)
8641 si->player_name = get_corrected_login_name(getLoginName());
8644 si->sound_loops = TRUE;
8645 si->sound_music = TRUE;
8646 si->sound_simple = TRUE;
8648 si->scroll_delay = TRUE;
8649 si->scroll_delay_value = STD_SCROLL_DELAY;
8650 si->engine_snapshot_mode = getStringCopy(STR_SNAPSHOT_MODE_DEFAULT);
8651 si->engine_snapshot_memory = SNAPSHOT_MEMORY_DEFAULT;
8652 si->fade_screens = TRUE;
8653 si->autorecord = TRUE;
8654 si->show_titlescreen = TRUE;
8655 si->quick_doors = FALSE;
8656 si->team_mode = FALSE;
8657 si->handicap = TRUE;
8658 si->skip_levels = TRUE;
8659 si->increment_levels = TRUE;
8660 si->time_limit = TRUE;
8661 si->fullscreen = FALSE;
8662 si->window_scaling_percent = STD_WINDOW_SCALING_PERCENT;
8663 si->window_scaling_quality = getStringCopy(SCALING_QUALITY_DEFAULT);
8664 si->screen_rendering_mode = getStringCopy(STR_SPECIAL_RENDERING_DEFAULT);
8665 si->ask_on_escape = TRUE;
8666 si->ask_on_escape_editor = TRUE;
8667 si->quick_switch = FALSE;
8668 si->input_on_focus = FALSE;
8669 si->prefer_aga_graphics = TRUE;
8670 si->game_frame_delay = GAME_FRAME_DELAY;
8671 si->sp_show_border_elements = FALSE;
8672 si->small_game_graphics = FALSE;
8673 si->show_snapshot_buttons = FALSE;
8675 si->graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
8676 si->sounds_set = getStringCopy(SND_CLASSIC_SUBDIR);
8677 si->music_set = getStringCopy(MUS_CLASSIC_SUBDIR);
8679 si->override_level_graphics = FALSE;
8680 si->override_level_sounds = FALSE;
8681 si->override_level_music = FALSE;
8683 si->volume_simple = 100; /* percent */
8684 si->volume_loops = 100; /* percent */
8685 si->volume_music = 100; /* percent */
8687 si->touch.control_type = getStringCopy(TOUCH_CONTROL_DEFAULT);
8688 si->touch.move_distance = TOUCH_MOVE_DISTANCE_DEFAULT; /* percent */
8689 si->touch.drop_distance = TOUCH_DROP_DISTANCE_DEFAULT; /* percent */
8691 si->editor.el_boulderdash = TRUE;
8692 si->editor.el_emerald_mine = TRUE;
8693 si->editor.el_emerald_mine_club = TRUE;
8694 si->editor.el_more = TRUE;
8695 si->editor.el_sokoban = TRUE;
8696 si->editor.el_supaplex = TRUE;
8697 si->editor.el_diamond_caves = TRUE;
8698 si->editor.el_dx_boulderdash = TRUE;
8700 si->editor.el_mirror_magic = TRUE;
8701 si->editor.el_deflektor = TRUE;
8703 si->editor.el_chars = TRUE;
8704 si->editor.el_steel_chars = TRUE;
8706 si->editor.el_classic = TRUE;
8707 si->editor.el_custom = TRUE;
8709 si->editor.el_user_defined = FALSE;
8710 si->editor.el_dynamic = TRUE;
8712 si->editor.el_headlines = TRUE;
8714 si->editor.show_element_token = FALSE;
8716 si->editor.use_template_for_new_levels = TRUE;
8718 si->shortcut.save_game = DEFAULT_KEY_SAVE_GAME;
8719 si->shortcut.load_game = DEFAULT_KEY_LOAD_GAME;
8720 si->shortcut.toggle_pause = DEFAULT_KEY_TOGGLE_PAUSE;
8722 si->shortcut.focus_player[0] = DEFAULT_KEY_FOCUS_PLAYER_1;
8723 si->shortcut.focus_player[1] = DEFAULT_KEY_FOCUS_PLAYER_2;
8724 si->shortcut.focus_player[2] = DEFAULT_KEY_FOCUS_PLAYER_3;
8725 si->shortcut.focus_player[3] = DEFAULT_KEY_FOCUS_PLAYER_4;
8726 si->shortcut.focus_player_all = DEFAULT_KEY_FOCUS_PLAYER_ALL;
8728 si->shortcut.tape_eject = DEFAULT_KEY_TAPE_EJECT;
8729 si->shortcut.tape_extra = DEFAULT_KEY_TAPE_EXTRA;
8730 si->shortcut.tape_stop = DEFAULT_KEY_TAPE_STOP;
8731 si->shortcut.tape_pause = DEFAULT_KEY_TAPE_PAUSE;
8732 si->shortcut.tape_record = DEFAULT_KEY_TAPE_RECORD;
8733 si->shortcut.tape_play = DEFAULT_KEY_TAPE_PLAY;
8735 si->shortcut.sound_simple = DEFAULT_KEY_SOUND_SIMPLE;
8736 si->shortcut.sound_loops = DEFAULT_KEY_SOUND_LOOPS;
8737 si->shortcut.sound_music = DEFAULT_KEY_SOUND_MUSIC;
8739 si->shortcut.snap_left = DEFAULT_KEY_SNAP_LEFT;
8740 si->shortcut.snap_right = DEFAULT_KEY_SNAP_RIGHT;
8741 si->shortcut.snap_up = DEFAULT_KEY_SNAP_UP;
8742 si->shortcut.snap_down = DEFAULT_KEY_SNAP_DOWN;
8744 for (i = 0; i < MAX_PLAYERS; i++)
8746 si->input[i].use_joystick = FALSE;
8747 si->input[i].joy.device_name=getStringCopy(getDeviceNameFromJoystickNr(i));
8748 si->input[i].joy.xleft = JOYSTICK_XLEFT;
8749 si->input[i].joy.xmiddle = JOYSTICK_XMIDDLE;
8750 si->input[i].joy.xright = JOYSTICK_XRIGHT;
8751 si->input[i].joy.yupper = JOYSTICK_YUPPER;
8752 si->input[i].joy.ymiddle = JOYSTICK_YMIDDLE;
8753 si->input[i].joy.ylower = JOYSTICK_YLOWER;
8754 si->input[i].joy.snap = (i == 0 ? JOY_BUTTON_1 : 0);
8755 si->input[i].joy.drop = (i == 0 ? JOY_BUTTON_2 : 0);
8756 si->input[i].key.left = (i == 0 ? DEFAULT_KEY_LEFT : KSYM_UNDEFINED);
8757 si->input[i].key.right = (i == 0 ? DEFAULT_KEY_RIGHT : KSYM_UNDEFINED);
8758 si->input[i].key.up = (i == 0 ? DEFAULT_KEY_UP : KSYM_UNDEFINED);
8759 si->input[i].key.down = (i == 0 ? DEFAULT_KEY_DOWN : KSYM_UNDEFINED);
8760 si->input[i].key.snap = (i == 0 ? DEFAULT_KEY_SNAP : KSYM_UNDEFINED);
8761 si->input[i].key.drop = (i == 0 ? DEFAULT_KEY_DROP : KSYM_UNDEFINED);
8764 si->system.sdl_videodriver = getStringCopy(ARG_DEFAULT);
8765 si->system.sdl_audiodriver = getStringCopy(ARG_DEFAULT);
8766 si->system.audio_fragment_size = DEFAULT_AUDIO_FRAGMENT_SIZE;
8768 si->internal.program_title = getStringCopy(PROGRAM_TITLE_STRING);
8769 si->internal.program_version = getStringCopy(getProgramRealVersionString());
8770 si->internal.program_author = getStringCopy(PROGRAM_AUTHOR_STRING);
8771 si->internal.program_email = getStringCopy(PROGRAM_EMAIL_STRING);
8772 si->internal.program_website = getStringCopy(PROGRAM_WEBSITE_STRING);
8773 si->internal.program_copyright = getStringCopy(PROGRAM_COPYRIGHT_STRING);
8774 si->internal.program_company = getStringCopy(PROGRAM_COMPANY_STRING);
8776 si->internal.program_icon_file = getStringCopy(PROGRAM_ICON_FILENAME);
8778 si->internal.default_graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
8779 si->internal.default_sounds_set = getStringCopy(SND_CLASSIC_SUBDIR);
8780 si->internal.default_music_set = getStringCopy(MUS_CLASSIC_SUBDIR);
8782 si->internal.fallback_graphics_file = getStringCopy(UNDEFINED_FILENAME);
8783 si->internal.fallback_sounds_file = getStringCopy(UNDEFINED_FILENAME);
8784 si->internal.fallback_music_file = getStringCopy(UNDEFINED_FILENAME);
8786 si->internal.default_level_series = getStringCopy(UNDEFINED_LEVELSET);
8787 si->internal.choose_from_top_leveldir = FALSE;
8788 si->internal.show_scaling_in_title = TRUE;
8790 si->internal.default_window_width = WIN_XSIZE_DEFAULT;
8791 si->internal.default_window_height = WIN_YSIZE_DEFAULT;
8793 si->debug.frame_delay[0] = DEFAULT_FRAME_DELAY_0;
8794 si->debug.frame_delay[1] = DEFAULT_FRAME_DELAY_1;
8795 si->debug.frame_delay[2] = DEFAULT_FRAME_DELAY_2;
8796 si->debug.frame_delay[3] = DEFAULT_FRAME_DELAY_3;
8797 si->debug.frame_delay[4] = DEFAULT_FRAME_DELAY_4;
8798 si->debug.frame_delay[5] = DEFAULT_FRAME_DELAY_5;
8799 si->debug.frame_delay[6] = DEFAULT_FRAME_DELAY_6;
8800 si->debug.frame_delay[7] = DEFAULT_FRAME_DELAY_7;
8801 si->debug.frame_delay[8] = DEFAULT_FRAME_DELAY_8;
8802 si->debug.frame_delay[9] = DEFAULT_FRAME_DELAY_9;
8804 si->debug.frame_delay_key[0] = DEFAULT_KEY_FRAME_DELAY_0;
8805 si->debug.frame_delay_key[1] = DEFAULT_KEY_FRAME_DELAY_1;
8806 si->debug.frame_delay_key[2] = DEFAULT_KEY_FRAME_DELAY_2;
8807 si->debug.frame_delay_key[3] = DEFAULT_KEY_FRAME_DELAY_3;
8808 si->debug.frame_delay_key[4] = DEFAULT_KEY_FRAME_DELAY_4;
8809 si->debug.frame_delay_key[5] = DEFAULT_KEY_FRAME_DELAY_5;
8810 si->debug.frame_delay_key[6] = DEFAULT_KEY_FRAME_DELAY_6;
8811 si->debug.frame_delay_key[7] = DEFAULT_KEY_FRAME_DELAY_7;
8812 si->debug.frame_delay_key[8] = DEFAULT_KEY_FRAME_DELAY_8;
8813 si->debug.frame_delay_key[9] = DEFAULT_KEY_FRAME_DELAY_9;
8815 si->debug.frame_delay_use_mod_key = DEFAULT_FRAME_DELAY_USE_MOD_KEY;
8816 si->debug.frame_delay_game_only = DEFAULT_FRAME_DELAY_GAME_ONLY;
8818 si->debug.show_frames_per_second = FALSE;
8820 si->options.verbose = FALSE;
8822 #if defined(PLATFORM_ANDROID)
8823 si->fullscreen = TRUE;
8827 static void setSetupInfoToDefaults_AutoSetup(struct SetupInfo *si)
8829 si->auto_setup.editor_zoom_tilesize = MINI_TILESIZE;
8832 static void setSetupInfoToDefaults_EditorCascade(struct SetupInfo *si)
8834 si->editor_cascade.el_bd = TRUE;
8835 si->editor_cascade.el_em = TRUE;
8836 si->editor_cascade.el_emc = TRUE;
8837 si->editor_cascade.el_rnd = TRUE;
8838 si->editor_cascade.el_sb = TRUE;
8839 si->editor_cascade.el_sp = TRUE;
8840 si->editor_cascade.el_dc = TRUE;
8841 si->editor_cascade.el_dx = TRUE;
8843 si->editor_cascade.el_mm = TRUE;
8844 si->editor_cascade.el_df = TRUE;
8846 si->editor_cascade.el_chars = FALSE;
8847 si->editor_cascade.el_steel_chars = FALSE;
8848 si->editor_cascade.el_ce = FALSE;
8849 si->editor_cascade.el_ge = FALSE;
8850 si->editor_cascade.el_ref = FALSE;
8851 si->editor_cascade.el_user = FALSE;
8852 si->editor_cascade.el_dynamic = FALSE;
8855 #define MAX_HIDE_SETUP_TOKEN_SIZE 20
8857 static char *getHideSetupToken(void *setup_value)
8859 static char hide_setup_token[MAX_HIDE_SETUP_TOKEN_SIZE];
8861 if (setup_value != NULL)
8862 snprintf(hide_setup_token, MAX_HIDE_SETUP_TOKEN_SIZE, "%p", setup_value);
8864 return hide_setup_token;
8867 static void setHideSetupEntry(void *setup_value_raw)
8869 /* !!! DIRTY WORKAROUND; TO BE FIXED AFTER THE MM ENGINE RELEASE !!! */
8870 void *setup_value = setup_value_raw - (void *)&si + (void *)&setup;
8872 char *hide_setup_token = getHideSetupToken(setup_value);
8874 if (setup_value != NULL)
8875 setHashEntry(hide_setup_hash, hide_setup_token, "");
8878 boolean hideSetupEntry(void *setup_value)
8880 char *hide_setup_token = getHideSetupToken(setup_value);
8882 return (setup_value != NULL &&
8883 getHashEntry(hide_setup_hash, hide_setup_token) != NULL);
8886 static void setSetupInfoFromTokenText(SetupFileHash *setup_file_hash,
8887 struct TokenInfo *token_info,
8888 int token_nr, char *token_text)
8890 char *token_hide_text = getStringCat2(token_text, ".hide");
8891 char *token_hide_value = getHashEntry(setup_file_hash, token_hide_text);
8893 /* set the value of this setup option in the setup option structure */
8894 setSetupInfo(token_info, token_nr, getHashEntry(setup_file_hash, token_text));
8896 /* check if this setup option should be hidden in the setup menu */
8897 if (token_hide_value != NULL && get_boolean_from_string(token_hide_value))
8898 setHideSetupEntry(token_info[token_nr].value);
8901 static void setSetupInfoFromTokenInfo(SetupFileHash *setup_file_hash,
8902 struct TokenInfo *token_info,
8905 setSetupInfoFromTokenText(setup_file_hash, token_info, token_nr,
8906 token_info[token_nr].text);
8909 static void decodeSetupFileHash(SetupFileHash *setup_file_hash)
8913 if (!setup_file_hash)
8916 if (hide_setup_hash == NULL)
8917 hide_setup_hash = newSetupFileHash();
8921 for (i = 0; i < NUM_GLOBAL_SETUP_TOKENS; i++)
8922 setSetupInfoFromTokenInfo(setup_file_hash, global_setup_tokens, i);
8927 for (i = 0; i < NUM_EDITOR_SETUP_TOKENS; i++)
8928 setSetupInfoFromTokenInfo(setup_file_hash, editor_setup_tokens, i);
8931 /* shortcut setup */
8932 ssi = setup.shortcut;
8933 for (i = 0; i < NUM_SHORTCUT_SETUP_TOKENS; i++)
8934 setSetupInfoFromTokenInfo(setup_file_hash, shortcut_setup_tokens, i);
8935 setup.shortcut = ssi;
8938 for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
8942 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
8944 sii = setup.input[pnr];
8945 for (i = 0; i < NUM_PLAYER_SETUP_TOKENS; i++)
8947 char full_token[100];
8949 sprintf(full_token, "%s%s", prefix, player_setup_tokens[i].text);
8950 setSetupInfoFromTokenText(setup_file_hash, player_setup_tokens, i,
8953 setup.input[pnr] = sii;
8958 for (i = 0; i < NUM_SYSTEM_SETUP_TOKENS; i++)
8959 setSetupInfoFromTokenInfo(setup_file_hash, system_setup_tokens, i);
8962 /* internal setup */
8963 sxi = setup.internal;
8964 for (i = 0; i < NUM_INTERNAL_SETUP_TOKENS; i++)
8965 setSetupInfoFromTokenInfo(setup_file_hash, internal_setup_tokens, i);
8966 setup.internal = sxi;
8970 for (i = 0; i < NUM_DEBUG_SETUP_TOKENS; i++)
8971 setSetupInfoFromTokenInfo(setup_file_hash, debug_setup_tokens, i);
8975 soi = setup.options;
8976 for (i = 0; i < NUM_OPTIONS_SETUP_TOKENS; i++)
8977 setSetupInfoFromTokenInfo(setup_file_hash, options_setup_tokens, i);
8978 setup.options = soi;
8981 static void decodeSetupFileHash_AutoSetup(SetupFileHash *setup_file_hash)
8985 if (!setup_file_hash)
8989 sasi = setup.auto_setup;
8990 for (i = 0; i < NUM_AUTO_SETUP_TOKENS; i++)
8991 setSetupInfo(auto_setup_tokens, i,
8992 getHashEntry(setup_file_hash,
8993 auto_setup_tokens[i].text));
8994 setup.auto_setup = sasi;
8997 static void decodeSetupFileHash_EditorCascade(SetupFileHash *setup_file_hash)
9001 if (!setup_file_hash)
9004 /* editor cascade setup */
9005 seci = setup.editor_cascade;
9006 for (i = 0; i < NUM_EDITOR_CASCADE_SETUP_TOKENS; i++)
9007 setSetupInfo(editor_cascade_setup_tokens, i,
9008 getHashEntry(setup_file_hash,
9009 editor_cascade_setup_tokens[i].text));
9010 setup.editor_cascade = seci;
9013 void LoadSetupFromFilename(char *filename)
9015 SetupFileHash *setup_file_hash = loadSetupFileHash(filename);
9017 if (setup_file_hash)
9019 decodeSetupFileHash(setup_file_hash);
9021 freeSetupFileHash(setup_file_hash);
9025 Error(ERR_DEBUG, "using default setup values");
9029 static void LoadSetup_SpecialPostProcessing()
9031 char *player_name_new;
9033 /* needed to work around problems with fixed length strings */
9034 player_name_new = get_corrected_login_name(setup.player_name);
9035 free(setup.player_name);
9036 setup.player_name = player_name_new;
9038 /* "scroll_delay: on(3) / off(0)" was replaced by scroll delay value */
9039 if (setup.scroll_delay == FALSE)
9041 setup.scroll_delay_value = MIN_SCROLL_DELAY;
9042 setup.scroll_delay = TRUE; /* now always "on" */
9045 /* make sure that scroll delay value stays inside valid range */
9046 setup.scroll_delay_value =
9047 MIN(MAX(MIN_SCROLL_DELAY, setup.scroll_delay_value), MAX_SCROLL_DELAY);
9054 /* always start with reliable default values */
9055 setSetupInfoToDefaults(&setup);
9057 /* try to load setup values from default setup file */
9058 filename = getDefaultSetupFilename();
9060 if (fileExists(filename))
9061 LoadSetupFromFilename(filename);
9063 /* try to load setup values from user setup file */
9064 filename = getSetupFilename();
9066 LoadSetupFromFilename(filename);
9068 LoadSetup_SpecialPostProcessing();
9071 void LoadSetup_AutoSetup()
9073 char *filename = getPath2(getSetupDir(), AUTOSETUP_FILENAME);
9074 SetupFileHash *setup_file_hash = NULL;
9076 /* always start with reliable default values */
9077 setSetupInfoToDefaults_AutoSetup(&setup);
9079 setup_file_hash = loadSetupFileHash(filename);
9081 if (setup_file_hash)
9083 decodeSetupFileHash_AutoSetup(setup_file_hash);
9085 freeSetupFileHash(setup_file_hash);
9091 void LoadSetup_EditorCascade()
9093 char *filename = getPath2(getSetupDir(), EDITORCASCADE_FILENAME);
9094 SetupFileHash *setup_file_hash = NULL;
9096 /* always start with reliable default values */
9097 setSetupInfoToDefaults_EditorCascade(&setup);
9099 setup_file_hash = loadSetupFileHash(filename);
9101 if (setup_file_hash)
9103 decodeSetupFileHash_EditorCascade(setup_file_hash);
9105 freeSetupFileHash(setup_file_hash);
9111 static void addGameControllerMappingToHash(SetupFileHash *mappings_hash,
9114 char mapping_guid[MAX_LINE_LEN];
9115 char *mapping_start, *mapping_end;
9117 // get GUID from game controller mapping line: copy complete line
9118 strncpy(mapping_guid, mapping_line, MAX_LINE_LEN - 1);
9119 mapping_guid[MAX_LINE_LEN - 1] = '\0';
9121 // get GUID from game controller mapping line: cut after GUID part
9122 mapping_start = strchr(mapping_guid, ',');
9123 if (mapping_start != NULL)
9124 *mapping_start = '\0';
9126 // cut newline from game controller mapping line
9127 mapping_end = strchr(mapping_line, '\n');
9128 if (mapping_end != NULL)
9129 *mapping_end = '\0';
9131 // add mapping entry to game controller mappings hash
9132 setHashEntry(mappings_hash, mapping_guid, mapping_line);
9135 static void LoadSetup_ReadGameControllerMappings(SetupFileHash *mappings_hash,
9140 if (!(file = fopen(filename, MODE_READ)))
9142 Error(ERR_WARN, "cannot read game controller mappings file '%s'", filename);
9149 char line[MAX_LINE_LEN];
9151 if (!fgets(line, MAX_LINE_LEN, file))
9154 addGameControllerMappingToHash(mappings_hash, line);
9162 char *filename = getSetupFilename();
9166 InitUserDataDirectory();
9168 if (!(file = fopen(filename, MODE_WRITE)))
9170 Error(ERR_WARN, "cannot write setup file '%s'", filename);
9174 fprintFileHeader(file, SETUP_FILENAME);
9178 for (i = 0; i < NUM_GLOBAL_SETUP_TOKENS; i++)
9180 /* just to make things nicer :) */
9181 if (i == SETUP_TOKEN_PLAYER_NAME + 1 ||
9182 i == SETUP_TOKEN_GRAPHICS_SET ||
9183 i == SETUP_TOKEN_VOLUME_SIMPLE ||
9184 i == SETUP_TOKEN_TOUCH_CONTROL_TYPE)
9185 fprintf(file, "\n");
9187 fprintf(file, "%s\n", getSetupLine(global_setup_tokens, "", i));
9192 fprintf(file, "\n");
9193 for (i = 0; i < NUM_EDITOR_SETUP_TOKENS; i++)
9194 fprintf(file, "%s\n", getSetupLine(editor_setup_tokens, "", i));
9196 /* shortcut setup */
9197 ssi = setup.shortcut;
9198 fprintf(file, "\n");
9199 for (i = 0; i < NUM_SHORTCUT_SETUP_TOKENS; i++)
9200 fprintf(file, "%s\n", getSetupLine(shortcut_setup_tokens, "", i));
9203 for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
9207 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
9208 fprintf(file, "\n");
9210 sii = setup.input[pnr];
9211 for (i = 0; i < NUM_PLAYER_SETUP_TOKENS; i++)
9212 fprintf(file, "%s\n", getSetupLine(player_setup_tokens, prefix, i));
9217 fprintf(file, "\n");
9218 for (i = 0; i < NUM_SYSTEM_SETUP_TOKENS; i++)
9219 fprintf(file, "%s\n", getSetupLine(system_setup_tokens, "", i));
9221 /* internal setup */
9222 /* (internal setup values not saved to user setup file) */
9226 fprintf(file, "\n");
9227 for (i = 0; i < NUM_DEBUG_SETUP_TOKENS; i++)
9228 fprintf(file, "%s\n", getSetupLine(debug_setup_tokens, "", i));
9231 soi = setup.options;
9232 fprintf(file, "\n");
9233 for (i = 0; i < NUM_OPTIONS_SETUP_TOKENS; i++)
9234 fprintf(file, "%s\n", getSetupLine(options_setup_tokens, "", i));
9238 SetFilePermissions(filename, PERMS_PRIVATE);
9241 void SaveSetup_AutoSetup()
9243 char *filename = getPath2(getSetupDir(), AUTOSETUP_FILENAME);
9247 InitUserDataDirectory();
9249 if (!(file = fopen(filename, MODE_WRITE)))
9251 Error(ERR_WARN, "cannot write auto setup file '%s'", filename);
9256 fprintFileHeader(file, AUTOSETUP_FILENAME);
9258 sasi = setup.auto_setup;
9259 for (i = 0; i < NUM_AUTO_SETUP_TOKENS; i++)
9260 fprintf(file, "%s\n", getSetupLine(auto_setup_tokens, "", i));
9264 SetFilePermissions(filename, PERMS_PRIVATE);
9269 void SaveSetup_EditorCascade()
9271 char *filename = getPath2(getSetupDir(), EDITORCASCADE_FILENAME);
9275 InitUserDataDirectory();
9277 if (!(file = fopen(filename, MODE_WRITE)))
9279 Error(ERR_WARN, "cannot write editor cascade state file '%s'", filename);
9284 fprintFileHeader(file, EDITORCASCADE_FILENAME);
9286 seci = setup.editor_cascade;
9287 for (i = 0; i < NUM_EDITOR_CASCADE_SETUP_TOKENS; i++)
9288 fprintf(file, "%s\n", getSetupLine(editor_cascade_setup_tokens, "", i));
9292 SetFilePermissions(filename, PERMS_PRIVATE);
9297 static void SaveSetup_WriteGameControllerMappings(SetupFileHash *mappings_hash,
9302 if (!(file = fopen(filename, MODE_WRITE)))
9304 Error(ERR_WARN, "cannot write game controller mappings file '%s'",filename);
9309 BEGIN_HASH_ITERATION(mappings_hash, itr)
9311 fprintf(file, "%s\n", HASH_ITERATION_VALUE(itr));
9313 END_HASH_ITERATION(mappings_hash, itr)
9318 void SaveSetup_AddGameControllerMapping(char *mapping)
9320 char *filename = getPath2(getSetupDir(), GAMECONTROLLER_BASENAME);
9321 SetupFileHash *mappings_hash = newSetupFileHash();
9323 InitUserDataDirectory();
9325 // load existing personal game controller mappings
9326 LoadSetup_ReadGameControllerMappings(mappings_hash, filename);
9328 // add new mapping to personal game controller mappings
9329 addGameControllerMappingToHash(mappings_hash, mapping);
9331 // save updated personal game controller mappings
9332 SaveSetup_WriteGameControllerMappings(mappings_hash, filename);
9334 freeSetupFileHash(mappings_hash);
9338 void LoadCustomElementDescriptions()
9340 char *filename = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
9341 SetupFileHash *setup_file_hash;
9344 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
9346 if (element_info[i].custom_description != NULL)
9348 free(element_info[i].custom_description);
9349 element_info[i].custom_description = NULL;
9353 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
9356 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
9358 char *token = getStringCat2(element_info[i].token_name, ".name");
9359 char *value = getHashEntry(setup_file_hash, token);
9362 element_info[i].custom_description = getStringCopy(value);
9367 freeSetupFileHash(setup_file_hash);
9370 static int getElementFromToken(char *token)
9372 char *value = getHashEntry(element_token_hash, token);
9377 Error(ERR_WARN, "unknown element token '%s'", token);
9379 return EL_UNDEFINED;
9382 static int get_token_parameter_value(char *token, char *value_raw)
9386 if (token == NULL || value_raw == NULL)
9387 return ARG_UNDEFINED_VALUE;
9389 suffix = strrchr(token, '.');
9393 if (strEqual(suffix, ".element"))
9394 return getElementFromToken(value_raw);
9396 /* !!! USE CORRECT VALUE TYPE (currently works also for TYPE_BOOLEAN) !!! */
9397 return get_parameter_value(value_raw, suffix, TYPE_INTEGER);
9400 void InitMenuDesignSettings_Static()
9404 /* always start with reliable default values from static default config */
9405 for (i = 0; image_config_vars[i].token != NULL; i++)
9407 char *value = getHashEntry(image_config_hash, image_config_vars[i].token);
9410 *image_config_vars[i].value =
9411 get_token_parameter_value(image_config_vars[i].token, value);
9415 static void InitMenuDesignSettings_SpecialPreProcessing()
9419 /* the following initializes hierarchical values from static configuration */
9421 /* special case: initialize "ARG_DEFAULT" values in static default config */
9422 /* (e.g., initialize "[titlemessage].fade_mode" from "[title].fade_mode") */
9423 titlescreen_initial_first_default.fade_mode =
9424 title_initial_first_default.fade_mode;
9425 titlescreen_initial_first_default.fade_delay =
9426 title_initial_first_default.fade_delay;
9427 titlescreen_initial_first_default.post_delay =
9428 title_initial_first_default.post_delay;
9429 titlescreen_initial_first_default.auto_delay =
9430 title_initial_first_default.auto_delay;
9431 titlescreen_first_default.fade_mode = title_first_default.fade_mode;
9432 titlescreen_first_default.fade_delay = title_first_default.fade_delay;
9433 titlescreen_first_default.post_delay = title_first_default.post_delay;
9434 titlescreen_first_default.auto_delay = title_first_default.auto_delay;
9435 titlemessage_initial_first_default.fade_mode =
9436 title_initial_first_default.fade_mode;
9437 titlemessage_initial_first_default.fade_delay =
9438 title_initial_first_default.fade_delay;
9439 titlemessage_initial_first_default.post_delay =
9440 title_initial_first_default.post_delay;
9441 titlemessage_initial_first_default.auto_delay =
9442 title_initial_first_default.auto_delay;
9443 titlemessage_first_default.fade_mode = title_first_default.fade_mode;
9444 titlemessage_first_default.fade_delay = title_first_default.fade_delay;
9445 titlemessage_first_default.post_delay = title_first_default.post_delay;
9446 titlemessage_first_default.auto_delay = title_first_default.auto_delay;
9448 titlescreen_initial_default.fade_mode = title_initial_default.fade_mode;
9449 titlescreen_initial_default.fade_delay = title_initial_default.fade_delay;
9450 titlescreen_initial_default.post_delay = title_initial_default.post_delay;
9451 titlescreen_initial_default.auto_delay = title_initial_default.auto_delay;
9452 titlescreen_default.fade_mode = title_default.fade_mode;
9453 titlescreen_default.fade_delay = title_default.fade_delay;
9454 titlescreen_default.post_delay = title_default.post_delay;
9455 titlescreen_default.auto_delay = title_default.auto_delay;
9456 titlemessage_initial_default.fade_mode = title_initial_default.fade_mode;
9457 titlemessage_initial_default.fade_delay = title_initial_default.fade_delay;
9458 titlemessage_initial_default.post_delay = title_initial_default.post_delay;
9459 titlemessage_initial_default.auto_delay = title_initial_default.auto_delay;
9460 titlemessage_default.fade_mode = title_default.fade_mode;
9461 titlemessage_default.fade_delay = title_default.fade_delay;
9462 titlemessage_default.post_delay = title_default.post_delay;
9463 titlemessage_default.auto_delay = title_default.auto_delay;
9465 /* special case: initialize "ARG_DEFAULT" values in static default config */
9466 /* (e.g., init "titlemessage_1.fade_mode" from "[titlemessage].fade_mode") */
9467 for (i = 0; i < MAX_NUM_TITLE_MESSAGES; i++)
9469 titlescreen_initial_first[i] = titlescreen_initial_first_default;
9470 titlescreen_first[i] = titlescreen_first_default;
9471 titlemessage_initial_first[i] = titlemessage_initial_first_default;
9472 titlemessage_first[i] = titlemessage_first_default;
9474 titlescreen_initial[i] = titlescreen_initial_default;
9475 titlescreen[i] = titlescreen_default;
9476 titlemessage_initial[i] = titlemessage_initial_default;
9477 titlemessage[i] = titlemessage_default;
9480 /* special case: initialize "ARG_DEFAULT" values in static default config */
9481 /* (eg, init "menu.enter_screen.SCORES.xyz" from "menu.enter_screen.xyz") */
9482 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
9484 if (i == GFX_SPECIAL_ARG_TITLE) /* title values already initialized */
9487 menu.enter_screen[i] = menu.enter_screen[GFX_SPECIAL_ARG_DEFAULT];
9488 menu.leave_screen[i] = menu.leave_screen[GFX_SPECIAL_ARG_DEFAULT];
9489 menu.next_screen[i] = menu.next_screen[GFX_SPECIAL_ARG_DEFAULT];
9492 /* special case: initialize "ARG_DEFAULT" values in static default config */
9493 /* (eg, init "viewport.door_1.MAIN.xyz" from "viewport.door_1.xyz") */
9494 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
9496 viewport.window[i] = viewport.window[GFX_SPECIAL_ARG_DEFAULT];
9497 viewport.playfield[i] = viewport.playfield[GFX_SPECIAL_ARG_DEFAULT];
9498 viewport.door_1[i] = viewport.door_1[GFX_SPECIAL_ARG_DEFAULT];
9500 if (i == GFX_SPECIAL_ARG_EDITOR) /* editor values already initialized */
9503 viewport.door_2[i] = viewport.door_2[GFX_SPECIAL_ARG_DEFAULT];
9507 static void InitMenuDesignSettings_SpecialPostProcessing()
9511 struct XY *dst, *src;
9515 { &game.button.save, &game.button.stop },
9516 { &game.button.pause2, &game.button.pause },
9517 { &game.button.load, &game.button.play },
9518 { &game.button.undo, &game.button.stop },
9519 { &game.button.redo, &game.button.play },
9525 /* special case: initialize later added SETUP list size from LEVELS value */
9526 if (menu.list_size[GAME_MODE_SETUP] == -1)
9527 menu.list_size[GAME_MODE_SETUP] = menu.list_size[GAME_MODE_LEVELS];
9529 /* set default position for snapshot buttons to stop/pause/play buttons */
9530 for (i = 0; game_buttons_xy[i].dst != NULL; i++)
9531 if ((*game_buttons_xy[i].dst).x == -1 &&
9532 (*game_buttons_xy[i].dst).y == -1)
9533 *game_buttons_xy[i].dst = *game_buttons_xy[i].src;
9536 static void InitMenuDesignSettings_SpecialPostProcessing_AfterGraphics()
9540 struct XYTileSize *dst, *src;
9543 editor_buttons_xy[] =
9546 &editor.button.element_left, &editor.palette.element_left,
9547 IMG_GFX_EDITOR_BUTTON_ELEMENT_LEFT
9550 &editor.button.element_middle, &editor.palette.element_middle,
9551 IMG_GFX_EDITOR_BUTTON_ELEMENT_MIDDLE
9554 &editor.button.element_right, &editor.palette.element_right,
9555 IMG_GFX_EDITOR_BUTTON_ELEMENT_RIGHT
9562 /* set default position for element buttons to element graphics */
9563 for (i = 0; editor_buttons_xy[i].dst != NULL; i++)
9565 if ((*editor_buttons_xy[i].dst).x == -1 &&
9566 (*editor_buttons_xy[i].dst).y == -1)
9568 struct GraphicInfo *gd = &graphic_info[editor_buttons_xy[i].graphic];
9570 gd->width = gd->height = editor_buttons_xy[i].src->tile_size;
9572 *editor_buttons_xy[i].dst = *editor_buttons_xy[i].src;
9577 static void LoadMenuDesignSettingsFromFilename(char *filename)
9579 static struct TitleFadingInfo tfi;
9580 static struct TitleMessageInfo tmi;
9581 static struct TokenInfo title_tokens[] =
9583 { TYPE_INTEGER, &tfi.fade_mode, ".fade_mode" },
9584 { TYPE_INTEGER, &tfi.fade_delay, ".fade_delay" },
9585 { TYPE_INTEGER, &tfi.post_delay, ".post_delay" },
9586 { TYPE_INTEGER, &tfi.auto_delay, ".auto_delay" },
9590 static struct TokenInfo titlemessage_tokens[] =
9592 { TYPE_INTEGER, &tmi.x, ".x" },
9593 { TYPE_INTEGER, &tmi.y, ".y" },
9594 { TYPE_INTEGER, &tmi.width, ".width" },
9595 { TYPE_INTEGER, &tmi.height, ".height" },
9596 { TYPE_INTEGER, &tmi.chars, ".chars" },
9597 { TYPE_INTEGER, &tmi.lines, ".lines" },
9598 { TYPE_INTEGER, &tmi.align, ".align" },
9599 { TYPE_INTEGER, &tmi.valign, ".valign" },
9600 { TYPE_INTEGER, &tmi.font, ".font" },
9601 { TYPE_BOOLEAN, &tmi.autowrap, ".autowrap" },
9602 { TYPE_BOOLEAN, &tmi.centered, ".centered" },
9603 { TYPE_BOOLEAN, &tmi.parse_comments, ".parse_comments" },
9604 { TYPE_INTEGER, &tmi.sort_priority, ".sort_priority" },
9605 { TYPE_INTEGER, &tmi.fade_mode, ".fade_mode" },
9606 { TYPE_INTEGER, &tmi.fade_delay, ".fade_delay" },
9607 { TYPE_INTEGER, &tmi.post_delay, ".post_delay" },
9608 { TYPE_INTEGER, &tmi.auto_delay, ".auto_delay" },
9614 struct TitleFadingInfo *info;
9619 /* initialize first titles from "enter screen" definitions, if defined */
9620 { &title_initial_first_default, "menu.enter_screen.TITLE" },
9621 { &title_first_default, "menu.enter_screen.TITLE" },
9623 /* initialize title screens from "next screen" definitions, if defined */
9624 { &title_initial_default, "menu.next_screen.TITLE" },
9625 { &title_default, "menu.next_screen.TITLE" },
9631 struct TitleMessageInfo *array;
9634 titlemessage_arrays[] =
9636 /* initialize first titles from "enter screen" definitions, if defined */
9637 { titlescreen_initial_first, "menu.enter_screen.TITLE" },
9638 { titlescreen_first, "menu.enter_screen.TITLE" },
9639 { titlemessage_initial_first, "menu.enter_screen.TITLE" },
9640 { titlemessage_first, "menu.enter_screen.TITLE" },
9642 /* initialize titles from "next screen" definitions, if defined */
9643 { titlescreen_initial, "menu.next_screen.TITLE" },
9644 { titlescreen, "menu.next_screen.TITLE" },
9645 { titlemessage_initial, "menu.next_screen.TITLE" },
9646 { titlemessage, "menu.next_screen.TITLE" },
9648 /* overwrite titles with title definitions, if defined */
9649 { titlescreen_initial_first, "[title_initial]" },
9650 { titlescreen_first, "[title]" },
9651 { titlemessage_initial_first, "[title_initial]" },
9652 { titlemessage_first, "[title]" },
9654 { titlescreen_initial, "[title_initial]" },
9655 { titlescreen, "[title]" },
9656 { titlemessage_initial, "[title_initial]" },
9657 { titlemessage, "[title]" },
9659 /* overwrite titles with title screen/message definitions, if defined */
9660 { titlescreen_initial_first, "[titlescreen_initial]" },
9661 { titlescreen_first, "[titlescreen]" },
9662 { titlemessage_initial_first, "[titlemessage_initial]" },
9663 { titlemessage_first, "[titlemessage]" },
9665 { titlescreen_initial, "[titlescreen_initial]" },
9666 { titlescreen, "[titlescreen]" },
9667 { titlemessage_initial, "[titlemessage_initial]" },
9668 { titlemessage, "[titlemessage]" },
9672 SetupFileHash *setup_file_hash;
9675 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
9678 /* the following initializes hierarchical values from dynamic configuration */
9680 /* special case: initialize with default values that may be overwritten */
9681 /* (e.g., init "menu.draw_xoffset.INFO" from "menu.draw_xoffset") */
9682 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
9684 char *value_1 = getHashEntry(setup_file_hash, "menu.draw_xoffset");
9685 char *value_2 = getHashEntry(setup_file_hash, "menu.draw_yoffset");
9686 char *value_3 = getHashEntry(setup_file_hash, "menu.list_size");
9688 if (value_1 != NULL)
9689 menu.draw_xoffset[i] = get_integer_from_string(value_1);
9690 if (value_2 != NULL)
9691 menu.draw_yoffset[i] = get_integer_from_string(value_2);
9692 if (value_3 != NULL)
9693 menu.list_size[i] = get_integer_from_string(value_3);
9696 /* special case: initialize with default values that may be overwritten */
9697 /* (eg, init "menu.draw_xoffset.INFO[XXX]" from "menu.draw_xoffset.INFO") */
9698 for (i = 0; i < NUM_SPECIAL_GFX_INFO_ARGS; i++)
9700 char *value_1 = getHashEntry(setup_file_hash, "menu.draw_xoffset.INFO");
9701 char *value_2 = getHashEntry(setup_file_hash, "menu.draw_yoffset.INFO");
9703 if (value_1 != NULL)
9704 menu.draw_xoffset_info[i] = get_integer_from_string(value_1);
9705 if (value_2 != NULL)
9706 menu.draw_yoffset_info[i] = get_integer_from_string(value_2);
9708 if (i == GFX_SPECIAL_ARG_INFO_ELEMENTS)
9710 char *value_1 = getHashEntry(setup_file_hash, "menu.list_size.INFO");
9712 if (value_1 != NULL)
9713 menu.list_size_info[i] = get_integer_from_string(value_1);
9717 /* special case: initialize with default values that may be overwritten */
9718 /* (eg, init "menu.draw_xoffset.SETUP[XXX]" from "menu.draw_xoffset.SETUP") */
9719 for (i = 0; i < NUM_SPECIAL_GFX_SETUP_ARGS; i++)
9721 char *value_1 = getHashEntry(setup_file_hash, "menu.draw_xoffset.SETUP");
9722 char *value_2 = getHashEntry(setup_file_hash, "menu.draw_yoffset.SETUP");
9724 if (value_1 != NULL)
9725 menu.draw_xoffset_setup[i] = get_integer_from_string(value_1);
9726 if (value_2 != NULL)
9727 menu.draw_yoffset_setup[i] = get_integer_from_string(value_2);
9730 /* special case: initialize with default values that may be overwritten */
9731 /* (eg, init "menu.enter_screen.SCORES.xyz" from "menu.enter_screen.xyz") */
9732 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
9734 char *token_1 = "menu.enter_screen.fade_mode";
9735 char *token_2 = "menu.enter_screen.fade_delay";
9736 char *token_3 = "menu.enter_screen.post_delay";
9737 char *token_4 = "menu.leave_screen.fade_mode";
9738 char *token_5 = "menu.leave_screen.fade_delay";
9739 char *token_6 = "menu.leave_screen.post_delay";
9740 char *token_7 = "menu.next_screen.fade_mode";
9741 char *token_8 = "menu.next_screen.fade_delay";
9742 char *token_9 = "menu.next_screen.post_delay";
9743 char *value_1 = getHashEntry(setup_file_hash, token_1);
9744 char *value_2 = getHashEntry(setup_file_hash, token_2);
9745 char *value_3 = getHashEntry(setup_file_hash, token_3);
9746 char *value_4 = getHashEntry(setup_file_hash, token_4);
9747 char *value_5 = getHashEntry(setup_file_hash, token_5);
9748 char *value_6 = getHashEntry(setup_file_hash, token_6);
9749 char *value_7 = getHashEntry(setup_file_hash, token_7);
9750 char *value_8 = getHashEntry(setup_file_hash, token_8);
9751 char *value_9 = getHashEntry(setup_file_hash, token_9);
9753 if (value_1 != NULL)
9754 menu.enter_screen[i].fade_mode = get_token_parameter_value(token_1,
9756 if (value_2 != NULL)
9757 menu.enter_screen[i].fade_delay = get_token_parameter_value(token_2,
9759 if (value_3 != NULL)
9760 menu.enter_screen[i].post_delay = get_token_parameter_value(token_3,
9762 if (value_4 != NULL)
9763 menu.leave_screen[i].fade_mode = get_token_parameter_value(token_4,
9765 if (value_5 != NULL)
9766 menu.leave_screen[i].fade_delay = get_token_parameter_value(token_5,
9768 if (value_6 != NULL)
9769 menu.leave_screen[i].post_delay = get_token_parameter_value(token_6,
9771 if (value_7 != NULL)
9772 menu.next_screen[i].fade_mode = get_token_parameter_value(token_7,
9774 if (value_8 != NULL)
9775 menu.next_screen[i].fade_delay = get_token_parameter_value(token_8,
9777 if (value_9 != NULL)
9778 menu.next_screen[i].post_delay = get_token_parameter_value(token_9,
9782 /* special case: initialize with default values that may be overwritten */
9783 /* (eg, init "viewport.door_1.MAIN.xyz" from "viewport.door_1.xyz") */
9784 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
9786 char *token_w1 = "viewport.window.width";
9787 char *token_w2 = "viewport.window.height";
9788 char *token_01 = "viewport.playfield.x";
9789 char *token_02 = "viewport.playfield.y";
9790 char *token_03 = "viewport.playfield.width";
9791 char *token_04 = "viewport.playfield.height";
9792 char *token_05 = "viewport.playfield.border_size";
9793 char *token_06 = "viewport.door_1.x";
9794 char *token_07 = "viewport.door_1.y";
9795 char *token_08 = "viewport.door_1.width";
9796 char *token_09 = "viewport.door_1.height";
9797 char *token_10 = "viewport.door_1.border_size";
9798 char *token_11 = "viewport.door_2.x";
9799 char *token_12 = "viewport.door_2.y";
9800 char *token_13 = "viewport.door_2.width";
9801 char *token_14 = "viewport.door_2.height";
9802 char *token_15 = "viewport.door_2.border_size";
9803 char *value_w1 = getHashEntry(setup_file_hash, token_w1);
9804 char *value_w2 = getHashEntry(setup_file_hash, token_w2);
9805 char *value_01 = getHashEntry(setup_file_hash, token_01);
9806 char *value_02 = getHashEntry(setup_file_hash, token_02);
9807 char *value_03 = getHashEntry(setup_file_hash, token_03);
9808 char *value_04 = getHashEntry(setup_file_hash, token_04);
9809 char *value_05 = getHashEntry(setup_file_hash, token_05);
9810 char *value_06 = getHashEntry(setup_file_hash, token_06);
9811 char *value_07 = getHashEntry(setup_file_hash, token_07);
9812 char *value_08 = getHashEntry(setup_file_hash, token_08);
9813 char *value_09 = getHashEntry(setup_file_hash, token_09);
9814 char *value_10 = getHashEntry(setup_file_hash, token_10);
9815 char *value_11 = getHashEntry(setup_file_hash, token_11);
9816 char *value_12 = getHashEntry(setup_file_hash, token_12);
9817 char *value_13 = getHashEntry(setup_file_hash, token_13);
9818 char *value_14 = getHashEntry(setup_file_hash, token_14);
9819 char *value_15 = getHashEntry(setup_file_hash, token_15);
9821 if (value_w1 != NULL)
9822 viewport.window[i].width = get_token_parameter_value(token_w1, value_w1);
9823 if (value_w2 != NULL)
9824 viewport.window[i].height = get_token_parameter_value(token_w2, value_w2);
9825 if (value_01 != NULL)
9826 viewport.playfield[i].x = get_token_parameter_value(token_01, value_01);
9827 if (value_02 != NULL)
9828 viewport.playfield[i].y = get_token_parameter_value(token_02, value_02);
9829 if (value_03 != NULL)
9830 viewport.playfield[i].width = get_token_parameter_value(token_03,
9832 if (value_04 != NULL)
9833 viewport.playfield[i].height = get_token_parameter_value(token_04,
9835 if (value_05 != NULL)
9836 viewport.playfield[i].border_size = get_token_parameter_value(token_05,
9838 if (value_06 != NULL)
9839 viewport.door_1[i].x = get_token_parameter_value(token_06, value_06);
9840 if (value_07 != NULL)
9841 viewport.door_1[i].y = get_token_parameter_value(token_07, value_07);
9842 if (value_08 != NULL)
9843 viewport.door_1[i].width = get_token_parameter_value(token_08, value_08);
9844 if (value_09 != NULL)
9845 viewport.door_1[i].height = get_token_parameter_value(token_09, value_09);
9846 if (value_10 != NULL)
9847 viewport.door_1[i].border_size = get_token_parameter_value(token_10,
9849 if (value_11 != NULL)
9850 viewport.door_2[i].x = get_token_parameter_value(token_11, value_11);
9851 if (value_12 != NULL)
9852 viewport.door_2[i].y = get_token_parameter_value(token_12, value_12);
9853 if (value_13 != NULL)
9854 viewport.door_2[i].width = get_token_parameter_value(token_13, value_13);
9855 if (value_14 != NULL)
9856 viewport.door_2[i].height = get_token_parameter_value(token_14, value_14);
9857 if (value_15 != NULL)
9858 viewport.door_1[i].border_size = get_token_parameter_value(token_15,
9862 /* special case: initialize with default values that may be overwritten */
9863 /* (e.g., init "[title].fade_mode" from "menu.next_screen.TITLE.fade_mode") */
9864 for (i = 0; title_info[i].info != NULL; i++)
9866 struct TitleFadingInfo *info = title_info[i].info;
9867 char *base_token = title_info[i].text;
9869 for (j = 0; title_tokens[j].type != -1; j++)
9871 char *token = getStringCat2(base_token, title_tokens[j].text);
9872 char *value = getHashEntry(setup_file_hash, token);
9876 int parameter_value = get_token_parameter_value(token, value);
9880 *(int *)title_tokens[j].value = (int)parameter_value;
9889 /* special case: initialize with default values that may be overwritten */
9890 /* (e.g., init "titlemessage_1.fade_mode" from "[titlemessage].fade_mode") */
9891 for (i = 0; titlemessage_arrays[i].array != NULL; i++)
9893 struct TitleMessageInfo *array = titlemessage_arrays[i].array;
9894 char *base_token = titlemessage_arrays[i].text;
9896 for (j = 0; titlemessage_tokens[j].type != -1; j++)
9898 char *token = getStringCat2(base_token, titlemessage_tokens[j].text);
9899 char *value = getHashEntry(setup_file_hash, token);
9903 int parameter_value = get_token_parameter_value(token, value);
9905 for (k = 0; k < MAX_NUM_TITLE_MESSAGES; k++)
9909 if (titlemessage_tokens[j].type == TYPE_INTEGER)
9910 *(int *)titlemessage_tokens[j].value = (int)parameter_value;
9912 *(boolean *)titlemessage_tokens[j].value = (boolean)parameter_value;
9922 /* read (and overwrite with) values that may be specified in config file */
9923 for (i = 0; image_config_vars[i].token != NULL; i++)
9925 char *value = getHashEntry(setup_file_hash, image_config_vars[i].token);
9927 /* (ignore definitions set to "[DEFAULT]" which are already initialized) */
9928 if (value != NULL && !strEqual(value, ARG_DEFAULT))
9929 *image_config_vars[i].value =
9930 get_token_parameter_value(image_config_vars[i].token, value);
9933 freeSetupFileHash(setup_file_hash);
9936 void LoadMenuDesignSettings()
9938 char *filename_base = UNDEFINED_FILENAME, *filename_local;
9940 InitMenuDesignSettings_Static();
9941 InitMenuDesignSettings_SpecialPreProcessing();
9943 if (!GFX_OVERRIDE_ARTWORK(ARTWORK_TYPE_GRAPHICS))
9945 /* first look for special settings configured in level series config */
9946 filename_base = getCustomArtworkLevelConfigFilename(ARTWORK_TYPE_GRAPHICS);
9948 if (fileExists(filename_base))
9949 LoadMenuDesignSettingsFromFilename(filename_base);
9952 filename_local = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
9954 if (filename_local != NULL && !strEqual(filename_base, filename_local))
9955 LoadMenuDesignSettingsFromFilename(filename_local);
9957 InitMenuDesignSettings_SpecialPostProcessing();
9960 void LoadMenuDesignSettings_AfterGraphics()
9962 InitMenuDesignSettings_SpecialPostProcessing_AfterGraphics();
9965 void LoadUserDefinedEditorElementList(int **elements, int *num_elements)
9967 char *filename = getEditorSetupFilename();
9968 SetupFileList *setup_file_list, *list;
9969 SetupFileHash *element_hash;
9970 int num_unknown_tokens = 0;
9973 if ((setup_file_list = loadSetupFileList(filename)) == NULL)
9976 element_hash = newSetupFileHash();
9978 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
9979 setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
9981 /* determined size may be larger than needed (due to unknown elements) */
9983 for (list = setup_file_list; list != NULL; list = list->next)
9986 /* add space for up to 3 more elements for padding that may be needed */
9989 /* free memory for old list of elements, if needed */
9990 checked_free(*elements);
9992 /* allocate memory for new list of elements */
9993 *elements = checked_malloc(*num_elements * sizeof(int));
9996 for (list = setup_file_list; list != NULL; list = list->next)
9998 char *value = getHashEntry(element_hash, list->token);
10000 if (value == NULL) /* try to find obsolete token mapping */
10002 char *mapped_token = get_mapped_token(list->token);
10004 if (mapped_token != NULL)
10006 value = getHashEntry(element_hash, mapped_token);
10008 free(mapped_token);
10014 (*elements)[(*num_elements)++] = atoi(value);
10018 if (num_unknown_tokens == 0)
10020 Error(ERR_INFO_LINE, "-");
10021 Error(ERR_INFO, "warning: unknown token(s) found in config file:");
10022 Error(ERR_INFO, "- config file: '%s'", filename);
10024 num_unknown_tokens++;
10027 Error(ERR_INFO, "- token: '%s'", list->token);
10031 if (num_unknown_tokens > 0)
10032 Error(ERR_INFO_LINE, "-");
10034 while (*num_elements % 4) /* pad with empty elements, if needed */
10035 (*elements)[(*num_elements)++] = EL_EMPTY;
10037 freeSetupFileList(setup_file_list);
10038 freeSetupFileHash(element_hash);
10041 for (i = 0; i < *num_elements; i++)
10042 printf("editor: element '%s' [%d]\n",
10043 element_info[(*elements)[i]].token_name, (*elements)[i]);
10047 static struct MusicFileInfo *get_music_file_info_ext(char *basename, int music,
10050 SetupFileHash *setup_file_hash = NULL;
10051 struct MusicFileInfo tmp_music_file_info, *new_music_file_info;
10052 char *filename_music, *filename_prefix, *filename_info;
10058 token_to_value_ptr[] =
10060 { "title_header", &tmp_music_file_info.title_header },
10061 { "artist_header", &tmp_music_file_info.artist_header },
10062 { "album_header", &tmp_music_file_info.album_header },
10063 { "year_header", &tmp_music_file_info.year_header },
10065 { "title", &tmp_music_file_info.title },
10066 { "artist", &tmp_music_file_info.artist },
10067 { "album", &tmp_music_file_info.album },
10068 { "year", &tmp_music_file_info.year },
10074 filename_music = (is_sound ? getCustomSoundFilename(basename) :
10075 getCustomMusicFilename(basename));
10077 if (filename_music == NULL)
10080 /* ---------- try to replace file extension ---------- */
10082 filename_prefix = getStringCopy(filename_music);
10083 if (strrchr(filename_prefix, '.') != NULL)
10084 *strrchr(filename_prefix, '.') = '\0';
10085 filename_info = getStringCat2(filename_prefix, ".txt");
10087 if (fileExists(filename_info))
10088 setup_file_hash = loadSetupFileHash(filename_info);
10090 free(filename_prefix);
10091 free(filename_info);
10093 if (setup_file_hash == NULL)
10095 /* ---------- try to add file extension ---------- */
10097 filename_prefix = getStringCopy(filename_music);
10098 filename_info = getStringCat2(filename_prefix, ".txt");
10100 if (fileExists(filename_info))
10101 setup_file_hash = loadSetupFileHash(filename_info);
10103 free(filename_prefix);
10104 free(filename_info);
10107 if (setup_file_hash == NULL)
10110 /* ---------- music file info found ---------- */
10112 clear_mem(&tmp_music_file_info, sizeof(struct MusicFileInfo));
10114 for (i = 0; token_to_value_ptr[i].token != NULL; i++)
10116 char *value = getHashEntry(setup_file_hash, token_to_value_ptr[i].token);
10118 *token_to_value_ptr[i].value_ptr =
10119 getStringCopy(value != NULL && *value != '\0' ? value : UNKNOWN_NAME);
10122 tmp_music_file_info.basename = getStringCopy(basename);
10123 tmp_music_file_info.music = music;
10124 tmp_music_file_info.is_sound = is_sound;
10126 new_music_file_info = checked_malloc(sizeof(struct MusicFileInfo));
10127 *new_music_file_info = tmp_music_file_info;
10129 return new_music_file_info;
10132 static struct MusicFileInfo *get_music_file_info(char *basename, int music)
10134 return get_music_file_info_ext(basename, music, FALSE);
10137 static struct MusicFileInfo *get_sound_file_info(char *basename, int sound)
10139 return get_music_file_info_ext(basename, sound, TRUE);
10142 static boolean music_info_listed_ext(struct MusicFileInfo *list,
10143 char *basename, boolean is_sound)
10145 for (; list != NULL; list = list->next)
10146 if (list->is_sound == is_sound && strEqual(list->basename, basename))
10152 static boolean music_info_listed(struct MusicFileInfo *list, char *basename)
10154 return music_info_listed_ext(list, basename, FALSE);
10157 static boolean sound_info_listed(struct MusicFileInfo *list, char *basename)
10159 return music_info_listed_ext(list, basename, TRUE);
10162 void LoadMusicInfo()
10164 char *music_directory = getCustomMusicDirectory();
10165 int num_music = getMusicListSize();
10166 int num_music_noconf = 0;
10167 int num_sounds = getSoundListSize();
10169 DirectoryEntry *dir_entry;
10170 struct FileInfo *music, *sound;
10171 struct MusicFileInfo *next, **new;
10174 while (music_file_info != NULL)
10176 next = music_file_info->next;
10178 checked_free(music_file_info->basename);
10180 checked_free(music_file_info->title_header);
10181 checked_free(music_file_info->artist_header);
10182 checked_free(music_file_info->album_header);
10183 checked_free(music_file_info->year_header);
10185 checked_free(music_file_info->title);
10186 checked_free(music_file_info->artist);
10187 checked_free(music_file_info->album);
10188 checked_free(music_file_info->year);
10190 free(music_file_info);
10192 music_file_info = next;
10195 new = &music_file_info;
10197 for (i = 0; i < num_music; i++)
10199 music = getMusicListEntry(i);
10201 if (music->filename == NULL)
10204 if (strEqual(music->filename, UNDEFINED_FILENAME))
10207 /* a configured file may be not recognized as music */
10208 if (!FileIsMusic(music->filename))
10211 if (!music_info_listed(music_file_info, music->filename))
10213 *new = get_music_file_info(music->filename, i);
10216 new = &(*new)->next;
10220 if ((dir = openDirectory(music_directory)) == NULL)
10222 Error(ERR_WARN, "cannot read music directory '%s'", music_directory);
10226 while ((dir_entry = readDirectory(dir)) != NULL) /* loop all entries */
10228 char *basename = dir_entry->basename;
10229 boolean music_already_used = FALSE;
10232 /* skip all music files that are configured in music config file */
10233 for (i = 0; i < num_music; i++)
10235 music = getMusicListEntry(i);
10237 if (music->filename == NULL)
10240 if (strEqual(basename, music->filename))
10242 music_already_used = TRUE;
10247 if (music_already_used)
10250 if (!FileIsMusic(dir_entry->filename))
10253 if (!music_info_listed(music_file_info, basename))
10255 *new = get_music_file_info(basename, MAP_NOCONF_MUSIC(num_music_noconf));
10258 new = &(*new)->next;
10261 num_music_noconf++;
10264 closeDirectory(dir);
10266 for (i = 0; i < num_sounds; i++)
10268 sound = getSoundListEntry(i);
10270 if (sound->filename == NULL)
10273 if (strEqual(sound->filename, UNDEFINED_FILENAME))
10276 /* a configured file may be not recognized as sound */
10277 if (!FileIsSound(sound->filename))
10280 if (!sound_info_listed(music_file_info, sound->filename))
10282 *new = get_sound_file_info(sound->filename, i);
10284 new = &(*new)->next;
10289 void add_helpanim_entry(int element, int action, int direction, int delay,
10290 int *num_list_entries)
10292 struct HelpAnimInfo *new_list_entry;
10293 (*num_list_entries)++;
10296 checked_realloc(helpanim_info,
10297 *num_list_entries * sizeof(struct HelpAnimInfo));
10298 new_list_entry = &helpanim_info[*num_list_entries - 1];
10300 new_list_entry->element = element;
10301 new_list_entry->action = action;
10302 new_list_entry->direction = direction;
10303 new_list_entry->delay = delay;
10306 void print_unknown_token(char *filename, char *token, int token_nr)
10310 Error(ERR_INFO_LINE, "-");
10311 Error(ERR_INFO, "warning: unknown token(s) found in config file:");
10312 Error(ERR_INFO, "- config file: '%s'", filename);
10315 Error(ERR_INFO, "- token: '%s'", token);
10318 void print_unknown_token_end(int token_nr)
10321 Error(ERR_INFO_LINE, "-");
10324 void LoadHelpAnimInfo()
10326 char *filename = getHelpAnimFilename();
10327 SetupFileList *setup_file_list = NULL, *list;
10328 SetupFileHash *element_hash, *action_hash, *direction_hash;
10329 int num_list_entries = 0;
10330 int num_unknown_tokens = 0;
10333 if (fileExists(filename))
10334 setup_file_list = loadSetupFileList(filename);
10336 if (setup_file_list == NULL)
10338 /* use reliable default values from static configuration */
10339 SetupFileList *insert_ptr;
10341 insert_ptr = setup_file_list =
10342 newSetupFileList(helpanim_config[0].token,
10343 helpanim_config[0].value);
10345 for (i = 1; helpanim_config[i].token; i++)
10346 insert_ptr = addListEntry(insert_ptr,
10347 helpanim_config[i].token,
10348 helpanim_config[i].value);
10351 element_hash = newSetupFileHash();
10352 action_hash = newSetupFileHash();
10353 direction_hash = newSetupFileHash();
10355 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
10356 setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
10358 for (i = 0; i < NUM_ACTIONS; i++)
10359 setHashEntry(action_hash, element_action_info[i].suffix,
10360 i_to_a(element_action_info[i].value));
10362 /* do not store direction index (bit) here, but direction value! */
10363 for (i = 0; i < NUM_DIRECTIONS_FULL; i++)
10364 setHashEntry(direction_hash, element_direction_info[i].suffix,
10365 i_to_a(1 << element_direction_info[i].value));
10367 for (list = setup_file_list; list != NULL; list = list->next)
10369 char *element_token, *action_token, *direction_token;
10370 char *element_value, *action_value, *direction_value;
10371 int delay = atoi(list->value);
10373 if (strEqual(list->token, "end"))
10375 add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
10380 /* first try to break element into element/action/direction parts;
10381 if this does not work, also accept combined "element[.act][.dir]"
10382 elements (like "dynamite.active"), which are unique elements */
10384 if (strchr(list->token, '.') == NULL) /* token contains no '.' */
10386 element_value = getHashEntry(element_hash, list->token);
10387 if (element_value != NULL) /* element found */
10388 add_helpanim_entry(atoi(element_value), -1, -1, delay,
10389 &num_list_entries);
10392 /* no further suffixes found -- this is not an element */
10393 print_unknown_token(filename, list->token, num_unknown_tokens++);
10399 /* token has format "<prefix>.<something>" */
10401 action_token = strchr(list->token, '.'); /* suffix may be action ... */
10402 direction_token = action_token; /* ... or direction */
10404 element_token = getStringCopy(list->token);
10405 *strchr(element_token, '.') = '\0';
10407 element_value = getHashEntry(element_hash, element_token);
10409 if (element_value == NULL) /* this is no element */
10411 element_value = getHashEntry(element_hash, list->token);
10412 if (element_value != NULL) /* combined element found */
10413 add_helpanim_entry(atoi(element_value), -1, -1, delay,
10414 &num_list_entries);
10416 print_unknown_token(filename, list->token, num_unknown_tokens++);
10418 free(element_token);
10423 action_value = getHashEntry(action_hash, action_token);
10425 if (action_value != NULL) /* action found */
10427 add_helpanim_entry(atoi(element_value), atoi(action_value), -1, delay,
10428 &num_list_entries);
10430 free(element_token);
10435 direction_value = getHashEntry(direction_hash, direction_token);
10437 if (direction_value != NULL) /* direction found */
10439 add_helpanim_entry(atoi(element_value), -1, atoi(direction_value), delay,
10440 &num_list_entries);
10442 free(element_token);
10447 if (strchr(action_token + 1, '.') == NULL)
10449 /* no further suffixes found -- this is not an action nor direction */
10451 element_value = getHashEntry(element_hash, list->token);
10452 if (element_value != NULL) /* combined element found */
10453 add_helpanim_entry(atoi(element_value), -1, -1, delay,
10454 &num_list_entries);
10456 print_unknown_token(filename, list->token, num_unknown_tokens++);
10458 free(element_token);
10463 /* token has format "<prefix>.<suffix>.<something>" */
10465 direction_token = strchr(action_token + 1, '.');
10467 action_token = getStringCopy(action_token);
10468 *strchr(action_token + 1, '.') = '\0';
10470 action_value = getHashEntry(action_hash, action_token);
10472 if (action_value == NULL) /* this is no action */
10474 element_value = getHashEntry(element_hash, list->token);
10475 if (element_value != NULL) /* combined element found */
10476 add_helpanim_entry(atoi(element_value), -1, -1, delay,
10477 &num_list_entries);
10479 print_unknown_token(filename, list->token, num_unknown_tokens++);
10481 free(element_token);
10482 free(action_token);
10487 direction_value = getHashEntry(direction_hash, direction_token);
10489 if (direction_value != NULL) /* direction found */
10491 add_helpanim_entry(atoi(element_value), atoi(action_value),
10492 atoi(direction_value), delay, &num_list_entries);
10494 free(element_token);
10495 free(action_token);
10500 /* this is no direction */
10502 element_value = getHashEntry(element_hash, list->token);
10503 if (element_value != NULL) /* combined element found */
10504 add_helpanim_entry(atoi(element_value), -1, -1, delay,
10505 &num_list_entries);
10507 print_unknown_token(filename, list->token, num_unknown_tokens++);
10509 free(element_token);
10510 free(action_token);
10513 print_unknown_token_end(num_unknown_tokens);
10515 add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
10516 add_helpanim_entry(HELPANIM_LIST_END, -1, -1, -1, &num_list_entries);
10518 freeSetupFileList(setup_file_list);
10519 freeSetupFileHash(element_hash);
10520 freeSetupFileHash(action_hash);
10521 freeSetupFileHash(direction_hash);
10524 for (i = 0; i < num_list_entries; i++)
10525 printf("::: '%s': %d, %d, %d => %d\n",
10526 EL_NAME(helpanim_info[i].element),
10527 helpanim_info[i].element,
10528 helpanim_info[i].action,
10529 helpanim_info[i].direction,
10530 helpanim_info[i].delay);
10534 void LoadHelpTextInfo()
10536 char *filename = getHelpTextFilename();
10539 if (helptext_info != NULL)
10541 freeSetupFileHash(helptext_info);
10542 helptext_info = NULL;
10545 if (fileExists(filename))
10546 helptext_info = loadSetupFileHash(filename);
10548 if (helptext_info == NULL)
10550 /* use reliable default values from static configuration */
10551 helptext_info = newSetupFileHash();
10553 for (i = 0; helptext_config[i].token; i++)
10554 setHashEntry(helptext_info,
10555 helptext_config[i].token,
10556 helptext_config[i].value);
10560 BEGIN_HASH_ITERATION(helptext_info, itr)
10562 printf("::: '%s' => '%s'\n",
10563 HASH_ITERATION_TOKEN(itr), HASH_ITERATION_VALUE(itr));
10565 END_HASH_ITERATION(hash, itr)
10570 /* ------------------------------------------------------------------------- */
10571 /* convert levels */
10572 /* ------------------------------------------------------------------------- */
10574 #define MAX_NUM_CONVERT_LEVELS 1000
10576 void ConvertLevels()
10578 static LevelDirTree *convert_leveldir = NULL;
10579 static int convert_level_nr = -1;
10580 static int num_levels_handled = 0;
10581 static int num_levels_converted = 0;
10582 static boolean levels_failed[MAX_NUM_CONVERT_LEVELS];
10585 convert_leveldir = getTreeInfoFromIdentifier(leveldir_first,
10586 global.convert_leveldir);
10588 if (convert_leveldir == NULL)
10589 Error(ERR_EXIT, "no such level identifier: '%s'",
10590 global.convert_leveldir);
10592 leveldir_current = convert_leveldir;
10594 if (global.convert_level_nr != -1)
10596 convert_leveldir->first_level = global.convert_level_nr;
10597 convert_leveldir->last_level = global.convert_level_nr;
10600 convert_level_nr = convert_leveldir->first_level;
10602 PrintLine("=", 79);
10603 Print("Converting levels\n");
10604 PrintLine("-", 79);
10605 Print("Level series identifier: '%s'\n", convert_leveldir->identifier);
10606 Print("Level series name: '%s'\n", convert_leveldir->name);
10607 Print("Level series author: '%s'\n", convert_leveldir->author);
10608 Print("Number of levels: %d\n", convert_leveldir->levels);
10609 PrintLine("=", 79);
10612 for (i = 0; i < MAX_NUM_CONVERT_LEVELS; i++)
10613 levels_failed[i] = FALSE;
10615 while (convert_level_nr <= convert_leveldir->last_level)
10617 char *level_filename;
10620 level_nr = convert_level_nr++;
10622 Print("Level %03d: ", level_nr);
10624 LoadLevel(level_nr);
10625 if (level.no_level_file || level.no_valid_file)
10627 Print("(no level)\n");
10631 Print("converting level ... ");
10633 level_filename = getDefaultLevelFilename(level_nr);
10634 new_level = !fileExists(level_filename);
10638 SaveLevel(level_nr);
10640 num_levels_converted++;
10642 Print("converted.\n");
10646 if (level_nr >= 0 && level_nr < MAX_NUM_CONVERT_LEVELS)
10647 levels_failed[level_nr] = TRUE;
10649 Print("NOT CONVERTED -- LEVEL ALREADY EXISTS.\n");
10652 num_levels_handled++;
10656 PrintLine("=", 79);
10657 Print("Number of levels handled: %d\n", num_levels_handled);
10658 Print("Number of levels converted: %d (%d%%)\n", num_levels_converted,
10659 (num_levels_handled ?
10660 num_levels_converted * 100 / num_levels_handled : 0));
10661 PrintLine("-", 79);
10662 Print("Summary (for automatic parsing by scripts):\n");
10663 Print("LEVELDIR '%s', CONVERTED %d/%d (%d%%)",
10664 convert_leveldir->identifier, num_levels_converted,
10665 num_levels_handled,
10666 (num_levels_handled ?
10667 num_levels_converted * 100 / num_levels_handled : 0));
10669 if (num_levels_handled != num_levels_converted)
10671 Print(", FAILED:");
10672 for (i = 0; i < MAX_NUM_CONVERT_LEVELS; i++)
10673 if (levels_failed[i])
10678 PrintLine("=", 79);
10680 CloseAllAndExit(0);
10684 /* ------------------------------------------------------------------------- */
10685 /* create and save images for use in level sketches (raw BMP format) */
10686 /* ------------------------------------------------------------------------- */
10688 void CreateLevelSketchImages()
10690 #if defined(TARGET_SDL)
10695 InitElementPropertiesGfxElement();
10697 bitmap1 = CreateBitmap(TILEX, TILEY, DEFAULT_DEPTH);
10698 bitmap2 = CreateBitmap(MINI_TILEX, MINI_TILEY, DEFAULT_DEPTH);
10700 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
10702 Bitmap *src_bitmap;
10704 int element = getMappedElement(i);
10705 int graphic = el2edimg(element);
10706 char basename1[16];
10707 char basename2[16];
10711 sprintf(basename1, "%03d.bmp", i);
10712 sprintf(basename2, "%03ds.bmp", i);
10714 filename1 = getPath2(global.create_images_dir, basename1);
10715 filename2 = getPath2(global.create_images_dir, basename2);
10717 getFixedGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
10718 BlitBitmap(src_bitmap, bitmap1, src_x, src_y, TILEX, TILEY,
10721 if (SDL_SaveBMP(bitmap1->surface, filename1) != 0)
10722 Error(ERR_EXIT, "cannot save level sketch image file '%s'", filename1);
10724 getMiniGraphicSource(graphic, &src_bitmap, &src_x, &src_y);
10725 BlitBitmap(src_bitmap, bitmap2, src_x, src_y, MINI_TILEX, MINI_TILEY, 0, 0);
10727 if (SDL_SaveBMP(bitmap2->surface, filename2) != 0)
10728 Error(ERR_EXIT, "cannot save level sketch image file '%s'", filename2);
10734 printf("%03d `%03d%c", i, i, (i % 10 < 9 ? ' ' : '\n'));
10737 FreeBitmap(bitmap1);
10738 FreeBitmap(bitmap2);
10743 Error(ERR_INFO, "%d normal and small images created", NUM_FILE_ELEMENTS);
10745 CloseAllAndExit(0);
10750 /* ------------------------------------------------------------------------- */
10751 /* create and save images for custom and group elements (raw BMP format) */
10752 /* ------------------------------------------------------------------------- */
10754 void CreateCustomElementImages(char *directory)
10756 #if defined(TARGET_SDL)
10757 char *src_basename = "RocksCE-template.ilbm";
10758 char *dst_basename = "RocksCE.bmp";
10759 char *src_filename = getPath2(directory, src_basename);
10760 char *dst_filename = getPath2(directory, dst_basename);
10761 Bitmap *src_bitmap;
10763 int yoffset_ce = 0;
10764 int yoffset_ge = (TILEY * NUM_CUSTOM_ELEMENTS / 16);
10767 SDLInitVideoDisplay();
10769 ReCreateBitmap(&backbuffer, video.width, video.height);
10771 src_bitmap = LoadImage(src_filename);
10773 bitmap = CreateBitmap(TILEX * 16 * 2,
10774 TILEY * (NUM_CUSTOM_ELEMENTS + NUM_GROUP_ELEMENTS) / 16,
10777 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
10784 BlitBitmap(src_bitmap, bitmap, 0, 0, TILEX, TILEY,
10785 TILEX * x, TILEY * y + yoffset_ce);
10787 BlitBitmap(src_bitmap, bitmap, 0, TILEY,
10789 TILEX * x + TILEX * 16,
10790 TILEY * y + yoffset_ce);
10792 for (j = 2; j >= 0; j--)
10796 BlitBitmap(src_bitmap, bitmap,
10797 TILEX + c * 7, 0, 6, 10,
10798 TILEX * x + 6 + j * 7,
10799 TILEY * y + 11 + yoffset_ce);
10801 BlitBitmap(src_bitmap, bitmap,
10802 TILEX + c * 8, TILEY, 6, 10,
10803 TILEX * 16 + TILEX * x + 6 + j * 8,
10804 TILEY * y + 10 + yoffset_ce);
10810 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
10817 BlitBitmap(src_bitmap, bitmap, 0, 0, TILEX, TILEY,
10818 TILEX * x, TILEY * y + yoffset_ge);
10820 BlitBitmap(src_bitmap, bitmap, 0, TILEY,
10822 TILEX * x + TILEX * 16,
10823 TILEY * y + yoffset_ge);
10825 for (j = 1; j >= 0; j--)
10829 BlitBitmap(src_bitmap, bitmap, TILEX + c * 10, 11, 10, 10,
10830 TILEX * x + 6 + j * 10,
10831 TILEY * y + 11 + yoffset_ge);
10833 BlitBitmap(src_bitmap, bitmap,
10834 TILEX + c * 8, TILEY + 12, 6, 10,
10835 TILEX * 16 + TILEX * x + 10 + j * 8,
10836 TILEY * y + 10 + yoffset_ge);
10842 if (SDL_SaveBMP(bitmap->surface, dst_filename) != 0)
10843 Error(ERR_EXIT, "cannot save CE graphics file '%s'", dst_filename);
10845 FreeBitmap(bitmap);
10847 CloseAllAndExit(0);