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),
847 /* ---------- unused values ----------------------------------------------- */
850 EL_UNKNOWN, SAVE_CONF_NEVER,
851 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
852 &li.score[SC_UNKNOWN_14], 10
855 EL_UNKNOWN, SAVE_CONF_NEVER,
856 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
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->file_version = level->file_version;
3980 level_mm->game_version = level->game_version;
3981 level_mm->encoding_16bit_field = level->encoding_16bit_field;
3983 level_mm->fieldx = MIN(level->fieldx, MM_MAX_PLAYFIELD_WIDTH);
3984 level_mm->fieldy = MIN(level->fieldy, MM_MAX_PLAYFIELD_HEIGHT);
3986 level_mm->time = level->time;
3987 level_mm->kettles_needed = level->gems_needed;
3988 level_mm->auto_count_kettles = level->auto_count_gems;
3990 level_mm->laser_red = level->mm_laser_red;
3991 level_mm->laser_green = level->mm_laser_green;
3992 level_mm->laser_blue = level->mm_laser_blue;
3994 strcpy(level_mm->name, level->name);
3995 strcpy(level_mm->author, level->author);
3997 level_mm->score[SC_PACMAN] = level->score[SC_PACMAN];
3998 level_mm->score[SC_KEY] = level->score[SC_PACMAN];
3999 level_mm->score[SC_TIME_BONUS] = level->score[SC_TIME_BONUS];
4001 level_mm->amoeba_speed = level->amoeba_speed;
4002 level_mm->time_fuse = level->mm_time_fuse;
4004 for (x = 0; x < level->fieldx; x++)
4005 for (y = 0; y < level->fieldy; y++)
4007 level_mm->field[x][y] = map_element_RND_to_MM(level->field[x][y]);
4010 void CopyNativeLevel_MM_to_RND(struct LevelInfo *level)
4012 struct LevelInfo_MM *level_mm = level->native_mm_level;
4015 level->file_version = level_mm->file_version;
4016 level->game_version = level_mm->game_version;
4017 level->encoding_16bit_field = level_mm->encoding_16bit_field;
4019 level->fieldx = MIN(level_mm->fieldx, MAX_LEV_FIELDX);
4020 level->fieldy = MIN(level_mm->fieldy, MAX_LEV_FIELDY);
4022 level->time = level_mm->time;
4023 level->gems_needed = level_mm->kettles_needed;
4024 level->auto_count_gems = level_mm->auto_count_kettles;
4026 level->mm_laser_red = level_mm->laser_red;
4027 level->mm_laser_green = level_mm->laser_green;
4028 level->mm_laser_blue = level_mm->laser_blue;
4030 strcpy(level->name, level_mm->name);
4032 /* only overwrite author from 'levelinfo.conf' if author defined in level */
4033 if (!strEqual(level_mm->author, ANONYMOUS_NAME))
4034 strcpy(level->author, level_mm->author);
4036 level->score[SC_PACMAN] = level_mm->score[SC_PACMAN];
4037 level->score[SC_KEY] = level_mm->score[SC_PACMAN];
4038 level->score[SC_TIME_BONUS] = level_mm->score[SC_TIME_BONUS];
4040 level->amoeba_speed = level_mm->amoeba_speed;
4041 level->mm_time_fuse = level_mm->time_fuse;
4043 for (x = 0; x < level->fieldx; x++)
4044 for (y = 0; y < level->fieldy; y++)
4045 level->field[x][y] = map_element_MM_to_RND(level_mm->field[x][y]);
4049 /* ------------------------------------------------------------------------- */
4050 /* functions for loading DC level */
4051 /* ------------------------------------------------------------------------- */
4053 #define DC_LEVEL_HEADER_SIZE 344
4055 unsigned short getDecodedWord_DC(unsigned short data_encoded, boolean init)
4057 static int last_data_encoded;
4061 int diff_hi, diff_lo;
4062 int data_hi, data_lo;
4063 unsigned short data_decoded;
4067 last_data_encoded = 0;
4074 diff = data_encoded - last_data_encoded;
4075 diff_hi = diff & ~0xff;
4076 diff_lo = diff & 0xff;
4080 data_hi = diff_hi - (offset1 << 8) + (offset2 & 0xff00);
4081 data_lo = (diff_lo + (data_hi >> 16)) & 0x00ff;
4082 data_hi = data_hi & 0xff00;
4084 data_decoded = data_hi | data_lo;
4086 last_data_encoded = data_encoded;
4088 offset1 = (offset1 + 1) % 31;
4089 offset2 = offset2 & 0xff;
4091 return data_decoded;
4094 int getMappedElement_DC(int element)
4102 /* 0x0117 - 0x036e: (?) */
4105 /* 0x042d - 0x0684: (?) */
4121 element = EL_CRYSTAL;
4124 case 0x0e77: /* quicksand (boulder) */
4125 element = EL_QUICKSAND_FAST_FULL;
4128 case 0x0e99: /* slow quicksand (boulder) */
4129 element = EL_QUICKSAND_FULL;
4133 element = EL_EM_EXIT_OPEN;
4137 element = EL_EM_EXIT_CLOSED;
4141 element = EL_EM_STEEL_EXIT_OPEN;
4145 element = EL_EM_STEEL_EXIT_CLOSED;
4148 case 0x0f4f: /* dynamite (lit 1) */
4149 element = EL_EM_DYNAMITE_ACTIVE;
4152 case 0x0f57: /* dynamite (lit 2) */
4153 element = EL_EM_DYNAMITE_ACTIVE;
4156 case 0x0f5f: /* dynamite (lit 3) */
4157 element = EL_EM_DYNAMITE_ACTIVE;
4160 case 0x0f67: /* dynamite (lit 4) */
4161 element = EL_EM_DYNAMITE_ACTIVE;
4168 element = EL_AMOEBA_WET;
4172 element = EL_AMOEBA_DROP;
4176 element = EL_DC_MAGIC_WALL;
4180 element = EL_SPACESHIP_UP;
4184 element = EL_SPACESHIP_DOWN;
4188 element = EL_SPACESHIP_LEFT;
4192 element = EL_SPACESHIP_RIGHT;
4196 element = EL_BUG_UP;
4200 element = EL_BUG_DOWN;
4204 element = EL_BUG_LEFT;
4208 element = EL_BUG_RIGHT;
4212 element = EL_MOLE_UP;
4216 element = EL_MOLE_DOWN;
4220 element = EL_MOLE_LEFT;
4224 element = EL_MOLE_RIGHT;
4232 element = EL_YAMYAM;
4236 element = EL_SWITCHGATE_OPEN;
4240 element = EL_SWITCHGATE_CLOSED;
4244 element = EL_DC_SWITCHGATE_SWITCH_UP;
4248 element = EL_TIMEGATE_CLOSED;
4251 case 0x144c: /* conveyor belt switch (green) */
4252 element = EL_CONVEYOR_BELT_3_SWITCH_MIDDLE;
4255 case 0x144f: /* conveyor belt switch (red) */
4256 element = EL_CONVEYOR_BELT_1_SWITCH_MIDDLE;
4259 case 0x1452: /* conveyor belt switch (blue) */
4260 element = EL_CONVEYOR_BELT_4_SWITCH_MIDDLE;
4264 element = EL_CONVEYOR_BELT_3_MIDDLE;
4268 element = EL_CONVEYOR_BELT_3_LEFT;
4272 element = EL_CONVEYOR_BELT_3_RIGHT;
4276 element = EL_CONVEYOR_BELT_1_MIDDLE;
4280 element = EL_CONVEYOR_BELT_1_LEFT;
4284 element = EL_CONVEYOR_BELT_1_RIGHT;
4288 element = EL_CONVEYOR_BELT_4_MIDDLE;
4292 element = EL_CONVEYOR_BELT_4_LEFT;
4296 element = EL_CONVEYOR_BELT_4_RIGHT;
4300 element = EL_EXPANDABLE_WALL_HORIZONTAL;
4304 element = EL_EXPANDABLE_WALL_VERTICAL;
4308 element = EL_EXPANDABLE_WALL_ANY;
4311 case 0x14ce: /* growing steel wall (left/right) */
4312 element = EL_EXPANDABLE_STEELWALL_HORIZONTAL;
4315 case 0x14df: /* growing steel wall (up/down) */
4316 element = EL_EXPANDABLE_STEELWALL_VERTICAL;
4319 case 0x14e8: /* growing steel wall (up/down/left/right) */
4320 element = EL_EXPANDABLE_STEELWALL_ANY;
4324 element = EL_SHIELD_DEADLY;
4328 element = EL_EXTRA_TIME;
4336 element = EL_EMPTY_SPACE;
4339 case 0x1578: /* quicksand (empty) */
4340 element = EL_QUICKSAND_FAST_EMPTY;
4343 case 0x1579: /* slow quicksand (empty) */
4344 element = EL_QUICKSAND_EMPTY;
4347 /* 0x157c - 0x158b: */
4350 /* 0x1590 - 0x159f: */
4351 /* EL_DC_LANDMINE */
4354 element = EL_EM_DYNAMITE;
4357 case 0x15a1: /* key (red) */
4358 element = EL_EM_KEY_1;
4361 case 0x15a2: /* key (yellow) */
4362 element = EL_EM_KEY_2;
4365 case 0x15a3: /* key (blue) */
4366 element = EL_EM_KEY_4;
4369 case 0x15a4: /* key (green) */
4370 element = EL_EM_KEY_3;
4373 case 0x15a5: /* key (white) */
4374 element = EL_DC_KEY_WHITE;
4378 element = EL_WALL_SLIPPERY;
4385 case 0x15a8: /* wall (not round) */
4389 case 0x15a9: /* (blue) */
4390 element = EL_CHAR_A;
4393 case 0x15aa: /* (blue) */
4394 element = EL_CHAR_B;
4397 case 0x15ab: /* (blue) */
4398 element = EL_CHAR_C;
4401 case 0x15ac: /* (blue) */
4402 element = EL_CHAR_D;
4405 case 0x15ad: /* (blue) */
4406 element = EL_CHAR_E;
4409 case 0x15ae: /* (blue) */
4410 element = EL_CHAR_F;
4413 case 0x15af: /* (blue) */
4414 element = EL_CHAR_G;
4417 case 0x15b0: /* (blue) */
4418 element = EL_CHAR_H;
4421 case 0x15b1: /* (blue) */
4422 element = EL_CHAR_I;
4425 case 0x15b2: /* (blue) */
4426 element = EL_CHAR_J;
4429 case 0x15b3: /* (blue) */
4430 element = EL_CHAR_K;
4433 case 0x15b4: /* (blue) */
4434 element = EL_CHAR_L;
4437 case 0x15b5: /* (blue) */
4438 element = EL_CHAR_M;
4441 case 0x15b6: /* (blue) */
4442 element = EL_CHAR_N;
4445 case 0x15b7: /* (blue) */
4446 element = EL_CHAR_O;
4449 case 0x15b8: /* (blue) */
4450 element = EL_CHAR_P;
4453 case 0x15b9: /* (blue) */
4454 element = EL_CHAR_Q;
4457 case 0x15ba: /* (blue) */
4458 element = EL_CHAR_R;
4461 case 0x15bb: /* (blue) */
4462 element = EL_CHAR_S;
4465 case 0x15bc: /* (blue) */
4466 element = EL_CHAR_T;
4469 case 0x15bd: /* (blue) */
4470 element = EL_CHAR_U;
4473 case 0x15be: /* (blue) */
4474 element = EL_CHAR_V;
4477 case 0x15bf: /* (blue) */
4478 element = EL_CHAR_W;
4481 case 0x15c0: /* (blue) */
4482 element = EL_CHAR_X;
4485 case 0x15c1: /* (blue) */
4486 element = EL_CHAR_Y;
4489 case 0x15c2: /* (blue) */
4490 element = EL_CHAR_Z;
4493 case 0x15c3: /* (blue) */
4494 element = EL_CHAR_AUMLAUT;
4497 case 0x15c4: /* (blue) */
4498 element = EL_CHAR_OUMLAUT;
4501 case 0x15c5: /* (blue) */
4502 element = EL_CHAR_UUMLAUT;
4505 case 0x15c6: /* (blue) */
4506 element = EL_CHAR_0;
4509 case 0x15c7: /* (blue) */
4510 element = EL_CHAR_1;
4513 case 0x15c8: /* (blue) */
4514 element = EL_CHAR_2;
4517 case 0x15c9: /* (blue) */
4518 element = EL_CHAR_3;
4521 case 0x15ca: /* (blue) */
4522 element = EL_CHAR_4;
4525 case 0x15cb: /* (blue) */
4526 element = EL_CHAR_5;
4529 case 0x15cc: /* (blue) */
4530 element = EL_CHAR_6;
4533 case 0x15cd: /* (blue) */
4534 element = EL_CHAR_7;
4537 case 0x15ce: /* (blue) */
4538 element = EL_CHAR_8;
4541 case 0x15cf: /* (blue) */
4542 element = EL_CHAR_9;
4545 case 0x15d0: /* (blue) */
4546 element = EL_CHAR_PERIOD;
4549 case 0x15d1: /* (blue) */
4550 element = EL_CHAR_EXCLAM;
4553 case 0x15d2: /* (blue) */
4554 element = EL_CHAR_COLON;
4557 case 0x15d3: /* (blue) */
4558 element = EL_CHAR_LESS;
4561 case 0x15d4: /* (blue) */
4562 element = EL_CHAR_GREATER;
4565 case 0x15d5: /* (blue) */
4566 element = EL_CHAR_QUESTION;
4569 case 0x15d6: /* (blue) */
4570 element = EL_CHAR_COPYRIGHT;
4573 case 0x15d7: /* (blue) */
4574 element = EL_CHAR_UP;
4577 case 0x15d8: /* (blue) */
4578 element = EL_CHAR_DOWN;
4581 case 0x15d9: /* (blue) */
4582 element = EL_CHAR_BUTTON;
4585 case 0x15da: /* (blue) */
4586 element = EL_CHAR_PLUS;
4589 case 0x15db: /* (blue) */
4590 element = EL_CHAR_MINUS;
4593 case 0x15dc: /* (blue) */
4594 element = EL_CHAR_APOSTROPHE;
4597 case 0x15dd: /* (blue) */
4598 element = EL_CHAR_PARENLEFT;
4601 case 0x15de: /* (blue) */
4602 element = EL_CHAR_PARENRIGHT;
4605 case 0x15df: /* (green) */
4606 element = EL_CHAR_A;
4609 case 0x15e0: /* (green) */
4610 element = EL_CHAR_B;
4613 case 0x15e1: /* (green) */
4614 element = EL_CHAR_C;
4617 case 0x15e2: /* (green) */
4618 element = EL_CHAR_D;
4621 case 0x15e3: /* (green) */
4622 element = EL_CHAR_E;
4625 case 0x15e4: /* (green) */
4626 element = EL_CHAR_F;
4629 case 0x15e5: /* (green) */
4630 element = EL_CHAR_G;
4633 case 0x15e6: /* (green) */
4634 element = EL_CHAR_H;
4637 case 0x15e7: /* (green) */
4638 element = EL_CHAR_I;
4641 case 0x15e8: /* (green) */
4642 element = EL_CHAR_J;
4645 case 0x15e9: /* (green) */
4646 element = EL_CHAR_K;
4649 case 0x15ea: /* (green) */
4650 element = EL_CHAR_L;
4653 case 0x15eb: /* (green) */
4654 element = EL_CHAR_M;
4657 case 0x15ec: /* (green) */
4658 element = EL_CHAR_N;
4661 case 0x15ed: /* (green) */
4662 element = EL_CHAR_O;
4665 case 0x15ee: /* (green) */
4666 element = EL_CHAR_P;
4669 case 0x15ef: /* (green) */
4670 element = EL_CHAR_Q;
4673 case 0x15f0: /* (green) */
4674 element = EL_CHAR_R;
4677 case 0x15f1: /* (green) */
4678 element = EL_CHAR_S;
4681 case 0x15f2: /* (green) */
4682 element = EL_CHAR_T;
4685 case 0x15f3: /* (green) */
4686 element = EL_CHAR_U;
4689 case 0x15f4: /* (green) */
4690 element = EL_CHAR_V;
4693 case 0x15f5: /* (green) */
4694 element = EL_CHAR_W;
4697 case 0x15f6: /* (green) */
4698 element = EL_CHAR_X;
4701 case 0x15f7: /* (green) */
4702 element = EL_CHAR_Y;
4705 case 0x15f8: /* (green) */
4706 element = EL_CHAR_Z;
4709 case 0x15f9: /* (green) */
4710 element = EL_CHAR_AUMLAUT;
4713 case 0x15fa: /* (green) */
4714 element = EL_CHAR_OUMLAUT;
4717 case 0x15fb: /* (green) */
4718 element = EL_CHAR_UUMLAUT;
4721 case 0x15fc: /* (green) */
4722 element = EL_CHAR_0;
4725 case 0x15fd: /* (green) */
4726 element = EL_CHAR_1;
4729 case 0x15fe: /* (green) */
4730 element = EL_CHAR_2;
4733 case 0x15ff: /* (green) */
4734 element = EL_CHAR_3;
4737 case 0x1600: /* (green) */
4738 element = EL_CHAR_4;
4741 case 0x1601: /* (green) */
4742 element = EL_CHAR_5;
4745 case 0x1602: /* (green) */
4746 element = EL_CHAR_6;
4749 case 0x1603: /* (green) */
4750 element = EL_CHAR_7;
4753 case 0x1604: /* (green) */
4754 element = EL_CHAR_8;
4757 case 0x1605: /* (green) */
4758 element = EL_CHAR_9;
4761 case 0x1606: /* (green) */
4762 element = EL_CHAR_PERIOD;
4765 case 0x1607: /* (green) */
4766 element = EL_CHAR_EXCLAM;
4769 case 0x1608: /* (green) */
4770 element = EL_CHAR_COLON;
4773 case 0x1609: /* (green) */
4774 element = EL_CHAR_LESS;
4777 case 0x160a: /* (green) */
4778 element = EL_CHAR_GREATER;
4781 case 0x160b: /* (green) */
4782 element = EL_CHAR_QUESTION;
4785 case 0x160c: /* (green) */
4786 element = EL_CHAR_COPYRIGHT;
4789 case 0x160d: /* (green) */
4790 element = EL_CHAR_UP;
4793 case 0x160e: /* (green) */
4794 element = EL_CHAR_DOWN;
4797 case 0x160f: /* (green) */
4798 element = EL_CHAR_BUTTON;
4801 case 0x1610: /* (green) */
4802 element = EL_CHAR_PLUS;
4805 case 0x1611: /* (green) */
4806 element = EL_CHAR_MINUS;
4809 case 0x1612: /* (green) */
4810 element = EL_CHAR_APOSTROPHE;
4813 case 0x1613: /* (green) */
4814 element = EL_CHAR_PARENLEFT;
4817 case 0x1614: /* (green) */
4818 element = EL_CHAR_PARENRIGHT;
4821 case 0x1615: /* (blue steel) */
4822 element = EL_STEEL_CHAR_A;
4825 case 0x1616: /* (blue steel) */
4826 element = EL_STEEL_CHAR_B;
4829 case 0x1617: /* (blue steel) */
4830 element = EL_STEEL_CHAR_C;
4833 case 0x1618: /* (blue steel) */
4834 element = EL_STEEL_CHAR_D;
4837 case 0x1619: /* (blue steel) */
4838 element = EL_STEEL_CHAR_E;
4841 case 0x161a: /* (blue steel) */
4842 element = EL_STEEL_CHAR_F;
4845 case 0x161b: /* (blue steel) */
4846 element = EL_STEEL_CHAR_G;
4849 case 0x161c: /* (blue steel) */
4850 element = EL_STEEL_CHAR_H;
4853 case 0x161d: /* (blue steel) */
4854 element = EL_STEEL_CHAR_I;
4857 case 0x161e: /* (blue steel) */
4858 element = EL_STEEL_CHAR_J;
4861 case 0x161f: /* (blue steel) */
4862 element = EL_STEEL_CHAR_K;
4865 case 0x1620: /* (blue steel) */
4866 element = EL_STEEL_CHAR_L;
4869 case 0x1621: /* (blue steel) */
4870 element = EL_STEEL_CHAR_M;
4873 case 0x1622: /* (blue steel) */
4874 element = EL_STEEL_CHAR_N;
4877 case 0x1623: /* (blue steel) */
4878 element = EL_STEEL_CHAR_O;
4881 case 0x1624: /* (blue steel) */
4882 element = EL_STEEL_CHAR_P;
4885 case 0x1625: /* (blue steel) */
4886 element = EL_STEEL_CHAR_Q;
4889 case 0x1626: /* (blue steel) */
4890 element = EL_STEEL_CHAR_R;
4893 case 0x1627: /* (blue steel) */
4894 element = EL_STEEL_CHAR_S;
4897 case 0x1628: /* (blue steel) */
4898 element = EL_STEEL_CHAR_T;
4901 case 0x1629: /* (blue steel) */
4902 element = EL_STEEL_CHAR_U;
4905 case 0x162a: /* (blue steel) */
4906 element = EL_STEEL_CHAR_V;
4909 case 0x162b: /* (blue steel) */
4910 element = EL_STEEL_CHAR_W;
4913 case 0x162c: /* (blue steel) */
4914 element = EL_STEEL_CHAR_X;
4917 case 0x162d: /* (blue steel) */
4918 element = EL_STEEL_CHAR_Y;
4921 case 0x162e: /* (blue steel) */
4922 element = EL_STEEL_CHAR_Z;
4925 case 0x162f: /* (blue steel) */
4926 element = EL_STEEL_CHAR_AUMLAUT;
4929 case 0x1630: /* (blue steel) */
4930 element = EL_STEEL_CHAR_OUMLAUT;
4933 case 0x1631: /* (blue steel) */
4934 element = EL_STEEL_CHAR_UUMLAUT;
4937 case 0x1632: /* (blue steel) */
4938 element = EL_STEEL_CHAR_0;
4941 case 0x1633: /* (blue steel) */
4942 element = EL_STEEL_CHAR_1;
4945 case 0x1634: /* (blue steel) */
4946 element = EL_STEEL_CHAR_2;
4949 case 0x1635: /* (blue steel) */
4950 element = EL_STEEL_CHAR_3;
4953 case 0x1636: /* (blue steel) */
4954 element = EL_STEEL_CHAR_4;
4957 case 0x1637: /* (blue steel) */
4958 element = EL_STEEL_CHAR_5;
4961 case 0x1638: /* (blue steel) */
4962 element = EL_STEEL_CHAR_6;
4965 case 0x1639: /* (blue steel) */
4966 element = EL_STEEL_CHAR_7;
4969 case 0x163a: /* (blue steel) */
4970 element = EL_STEEL_CHAR_8;
4973 case 0x163b: /* (blue steel) */
4974 element = EL_STEEL_CHAR_9;
4977 case 0x163c: /* (blue steel) */
4978 element = EL_STEEL_CHAR_PERIOD;
4981 case 0x163d: /* (blue steel) */
4982 element = EL_STEEL_CHAR_EXCLAM;
4985 case 0x163e: /* (blue steel) */
4986 element = EL_STEEL_CHAR_COLON;
4989 case 0x163f: /* (blue steel) */
4990 element = EL_STEEL_CHAR_LESS;
4993 case 0x1640: /* (blue steel) */
4994 element = EL_STEEL_CHAR_GREATER;
4997 case 0x1641: /* (blue steel) */
4998 element = EL_STEEL_CHAR_QUESTION;
5001 case 0x1642: /* (blue steel) */
5002 element = EL_STEEL_CHAR_COPYRIGHT;
5005 case 0x1643: /* (blue steel) */
5006 element = EL_STEEL_CHAR_UP;
5009 case 0x1644: /* (blue steel) */
5010 element = EL_STEEL_CHAR_DOWN;
5013 case 0x1645: /* (blue steel) */
5014 element = EL_STEEL_CHAR_BUTTON;
5017 case 0x1646: /* (blue steel) */
5018 element = EL_STEEL_CHAR_PLUS;
5021 case 0x1647: /* (blue steel) */
5022 element = EL_STEEL_CHAR_MINUS;
5025 case 0x1648: /* (blue steel) */
5026 element = EL_STEEL_CHAR_APOSTROPHE;
5029 case 0x1649: /* (blue steel) */
5030 element = EL_STEEL_CHAR_PARENLEFT;
5033 case 0x164a: /* (blue steel) */
5034 element = EL_STEEL_CHAR_PARENRIGHT;
5037 case 0x164b: /* (green steel) */
5038 element = EL_STEEL_CHAR_A;
5041 case 0x164c: /* (green steel) */
5042 element = EL_STEEL_CHAR_B;
5045 case 0x164d: /* (green steel) */
5046 element = EL_STEEL_CHAR_C;
5049 case 0x164e: /* (green steel) */
5050 element = EL_STEEL_CHAR_D;
5053 case 0x164f: /* (green steel) */
5054 element = EL_STEEL_CHAR_E;
5057 case 0x1650: /* (green steel) */
5058 element = EL_STEEL_CHAR_F;
5061 case 0x1651: /* (green steel) */
5062 element = EL_STEEL_CHAR_G;
5065 case 0x1652: /* (green steel) */
5066 element = EL_STEEL_CHAR_H;
5069 case 0x1653: /* (green steel) */
5070 element = EL_STEEL_CHAR_I;
5073 case 0x1654: /* (green steel) */
5074 element = EL_STEEL_CHAR_J;
5077 case 0x1655: /* (green steel) */
5078 element = EL_STEEL_CHAR_K;
5081 case 0x1656: /* (green steel) */
5082 element = EL_STEEL_CHAR_L;
5085 case 0x1657: /* (green steel) */
5086 element = EL_STEEL_CHAR_M;
5089 case 0x1658: /* (green steel) */
5090 element = EL_STEEL_CHAR_N;
5093 case 0x1659: /* (green steel) */
5094 element = EL_STEEL_CHAR_O;
5097 case 0x165a: /* (green steel) */
5098 element = EL_STEEL_CHAR_P;
5101 case 0x165b: /* (green steel) */
5102 element = EL_STEEL_CHAR_Q;
5105 case 0x165c: /* (green steel) */
5106 element = EL_STEEL_CHAR_R;
5109 case 0x165d: /* (green steel) */
5110 element = EL_STEEL_CHAR_S;
5113 case 0x165e: /* (green steel) */
5114 element = EL_STEEL_CHAR_T;
5117 case 0x165f: /* (green steel) */
5118 element = EL_STEEL_CHAR_U;
5121 case 0x1660: /* (green steel) */
5122 element = EL_STEEL_CHAR_V;
5125 case 0x1661: /* (green steel) */
5126 element = EL_STEEL_CHAR_W;
5129 case 0x1662: /* (green steel) */
5130 element = EL_STEEL_CHAR_X;
5133 case 0x1663: /* (green steel) */
5134 element = EL_STEEL_CHAR_Y;
5137 case 0x1664: /* (green steel) */
5138 element = EL_STEEL_CHAR_Z;
5141 case 0x1665: /* (green steel) */
5142 element = EL_STEEL_CHAR_AUMLAUT;
5145 case 0x1666: /* (green steel) */
5146 element = EL_STEEL_CHAR_OUMLAUT;
5149 case 0x1667: /* (green steel) */
5150 element = EL_STEEL_CHAR_UUMLAUT;
5153 case 0x1668: /* (green steel) */
5154 element = EL_STEEL_CHAR_0;
5157 case 0x1669: /* (green steel) */
5158 element = EL_STEEL_CHAR_1;
5161 case 0x166a: /* (green steel) */
5162 element = EL_STEEL_CHAR_2;
5165 case 0x166b: /* (green steel) */
5166 element = EL_STEEL_CHAR_3;
5169 case 0x166c: /* (green steel) */
5170 element = EL_STEEL_CHAR_4;
5173 case 0x166d: /* (green steel) */
5174 element = EL_STEEL_CHAR_5;
5177 case 0x166e: /* (green steel) */
5178 element = EL_STEEL_CHAR_6;
5181 case 0x166f: /* (green steel) */
5182 element = EL_STEEL_CHAR_7;
5185 case 0x1670: /* (green steel) */
5186 element = EL_STEEL_CHAR_8;
5189 case 0x1671: /* (green steel) */
5190 element = EL_STEEL_CHAR_9;
5193 case 0x1672: /* (green steel) */
5194 element = EL_STEEL_CHAR_PERIOD;
5197 case 0x1673: /* (green steel) */
5198 element = EL_STEEL_CHAR_EXCLAM;
5201 case 0x1674: /* (green steel) */
5202 element = EL_STEEL_CHAR_COLON;
5205 case 0x1675: /* (green steel) */
5206 element = EL_STEEL_CHAR_LESS;
5209 case 0x1676: /* (green steel) */
5210 element = EL_STEEL_CHAR_GREATER;
5213 case 0x1677: /* (green steel) */
5214 element = EL_STEEL_CHAR_QUESTION;
5217 case 0x1678: /* (green steel) */
5218 element = EL_STEEL_CHAR_COPYRIGHT;
5221 case 0x1679: /* (green steel) */
5222 element = EL_STEEL_CHAR_UP;
5225 case 0x167a: /* (green steel) */
5226 element = EL_STEEL_CHAR_DOWN;
5229 case 0x167b: /* (green steel) */
5230 element = EL_STEEL_CHAR_BUTTON;
5233 case 0x167c: /* (green steel) */
5234 element = EL_STEEL_CHAR_PLUS;
5237 case 0x167d: /* (green steel) */
5238 element = EL_STEEL_CHAR_MINUS;
5241 case 0x167e: /* (green steel) */
5242 element = EL_STEEL_CHAR_APOSTROPHE;
5245 case 0x167f: /* (green steel) */
5246 element = EL_STEEL_CHAR_PARENLEFT;
5249 case 0x1680: /* (green steel) */
5250 element = EL_STEEL_CHAR_PARENRIGHT;
5253 case 0x1681: /* gate (red) */
5254 element = EL_EM_GATE_1;
5257 case 0x1682: /* secret gate (red) */
5258 element = EL_GATE_1_GRAY;
5261 case 0x1683: /* gate (yellow) */
5262 element = EL_EM_GATE_2;
5265 case 0x1684: /* secret gate (yellow) */
5266 element = EL_GATE_2_GRAY;
5269 case 0x1685: /* gate (blue) */
5270 element = EL_EM_GATE_4;
5273 case 0x1686: /* secret gate (blue) */
5274 element = EL_GATE_4_GRAY;
5277 case 0x1687: /* gate (green) */
5278 element = EL_EM_GATE_3;
5281 case 0x1688: /* secret gate (green) */
5282 element = EL_GATE_3_GRAY;
5285 case 0x1689: /* gate (white) */
5286 element = EL_DC_GATE_WHITE;
5289 case 0x168a: /* secret gate (white) */
5290 element = EL_DC_GATE_WHITE_GRAY;
5293 case 0x168b: /* secret gate (no key) */
5294 element = EL_DC_GATE_FAKE_GRAY;
5298 element = EL_ROBOT_WHEEL;
5302 element = EL_DC_TIMEGATE_SWITCH;
5306 element = EL_ACID_POOL_BOTTOM;
5310 element = EL_ACID_POOL_TOPLEFT;
5314 element = EL_ACID_POOL_TOPRIGHT;
5318 element = EL_ACID_POOL_BOTTOMLEFT;
5322 element = EL_ACID_POOL_BOTTOMRIGHT;
5326 element = EL_STEELWALL;
5330 element = EL_STEELWALL_SLIPPERY;
5333 case 0x1695: /* steel wall (not round) */
5334 element = EL_STEELWALL;
5337 case 0x1696: /* steel wall (left) */
5338 element = EL_DC_STEELWALL_1_LEFT;
5341 case 0x1697: /* steel wall (bottom) */
5342 element = EL_DC_STEELWALL_1_BOTTOM;
5345 case 0x1698: /* steel wall (right) */
5346 element = EL_DC_STEELWALL_1_RIGHT;
5349 case 0x1699: /* steel wall (top) */
5350 element = EL_DC_STEELWALL_1_TOP;
5353 case 0x169a: /* steel wall (left/bottom) */
5354 element = EL_DC_STEELWALL_1_BOTTOMLEFT;
5357 case 0x169b: /* steel wall (right/bottom) */
5358 element = EL_DC_STEELWALL_1_BOTTOMRIGHT;
5361 case 0x169c: /* steel wall (right/top) */
5362 element = EL_DC_STEELWALL_1_TOPRIGHT;
5365 case 0x169d: /* steel wall (left/top) */
5366 element = EL_DC_STEELWALL_1_TOPLEFT;
5369 case 0x169e: /* steel wall (right/bottom small) */
5370 element = EL_DC_STEELWALL_1_BOTTOMRIGHT_2;
5373 case 0x169f: /* steel wall (left/bottom small) */
5374 element = EL_DC_STEELWALL_1_BOTTOMLEFT_2;
5377 case 0x16a0: /* steel wall (right/top small) */
5378 element = EL_DC_STEELWALL_1_TOPRIGHT_2;
5381 case 0x16a1: /* steel wall (left/top small) */
5382 element = EL_DC_STEELWALL_1_TOPLEFT_2;
5385 case 0x16a2: /* steel wall (left/right) */
5386 element = EL_DC_STEELWALL_1_VERTICAL;
5389 case 0x16a3: /* steel wall (top/bottom) */
5390 element = EL_DC_STEELWALL_1_HORIZONTAL;
5393 case 0x16a4: /* steel wall 2 (left end) */
5394 element = EL_DC_STEELWALL_2_LEFT;
5397 case 0x16a5: /* steel wall 2 (right end) */
5398 element = EL_DC_STEELWALL_2_RIGHT;
5401 case 0x16a6: /* steel wall 2 (top end) */
5402 element = EL_DC_STEELWALL_2_TOP;
5405 case 0x16a7: /* steel wall 2 (bottom end) */
5406 element = EL_DC_STEELWALL_2_BOTTOM;
5409 case 0x16a8: /* steel wall 2 (left/right) */
5410 element = EL_DC_STEELWALL_2_HORIZONTAL;
5413 case 0x16a9: /* steel wall 2 (up/down) */
5414 element = EL_DC_STEELWALL_2_VERTICAL;
5417 case 0x16aa: /* steel wall 2 (mid) */
5418 element = EL_DC_STEELWALL_2_MIDDLE;
5422 element = EL_SIGN_EXCLAMATION;
5426 element = EL_SIGN_RADIOACTIVITY;
5430 element = EL_SIGN_STOP;
5434 element = EL_SIGN_WHEELCHAIR;
5438 element = EL_SIGN_PARKING;
5442 element = EL_SIGN_NO_ENTRY;
5446 element = EL_SIGN_HEART;
5450 element = EL_SIGN_GIVE_WAY;
5454 element = EL_SIGN_ENTRY_FORBIDDEN;
5458 element = EL_SIGN_EMERGENCY_EXIT;
5462 element = EL_SIGN_YIN_YANG;
5466 element = EL_WALL_EMERALD;
5470 element = EL_WALL_DIAMOND;
5474 element = EL_WALL_PEARL;
5478 element = EL_WALL_CRYSTAL;
5482 element = EL_INVISIBLE_WALL;
5486 element = EL_INVISIBLE_STEELWALL;
5489 /* 0x16bc - 0x16cb: */
5490 /* EL_INVISIBLE_SAND */
5493 element = EL_LIGHT_SWITCH;
5497 element = EL_ENVELOPE_1;
5501 if (element >= 0x0117 && element <= 0x036e) /* (?) */
5502 element = EL_DIAMOND;
5503 else if (element >= 0x042d && element <= 0x0684) /* (?) */
5504 element = EL_EMERALD;
5505 else if (element >= 0x157c && element <= 0x158b)
5507 else if (element >= 0x1590 && element <= 0x159f)
5508 element = EL_DC_LANDMINE;
5509 else if (element >= 0x16bc && element <= 0x16cb)
5510 element = EL_INVISIBLE_SAND;
5513 Error(ERR_WARN, "unknown Diamond Caves element 0x%04x", element);
5514 element = EL_UNKNOWN;
5519 return getMappedElement(element);
5522 static void LoadLevelFromFileStream_DC(File *file, struct LevelInfo *level,
5525 byte header[DC_LEVEL_HEADER_SIZE];
5527 int envelope_header_pos = 62;
5528 int envelope_content_pos = 94;
5529 int level_name_pos = 251;
5530 int level_author_pos = 292;
5531 int envelope_header_len;
5532 int envelope_content_len;
5534 int level_author_len;
5536 int num_yamyam_contents;
5539 getDecodedWord_DC(0, TRUE); /* initialize DC2 decoding engine */
5541 for (i = 0; i < DC_LEVEL_HEADER_SIZE / 2; i++)
5543 unsigned short header_word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
5545 header[i * 2 + 0] = header_word >> 8;
5546 header[i * 2 + 1] = header_word & 0xff;
5549 /* read some values from level header to check level decoding integrity */
5550 fieldx = header[6] | (header[7] << 8);
5551 fieldy = header[8] | (header[9] << 8);
5552 num_yamyam_contents = header[60] | (header[61] << 8);
5554 /* do some simple sanity checks to ensure that level was correctly decoded */
5555 if (fieldx < 1 || fieldx > 256 ||
5556 fieldy < 1 || fieldy > 256 ||
5557 num_yamyam_contents < 1 || num_yamyam_contents > 8)
5559 level->no_valid_file = TRUE;
5561 Error(ERR_WARN, "cannot decode level from stream -- using empty level");
5566 /* maximum envelope header size is 31 bytes */
5567 envelope_header_len = header[envelope_header_pos];
5568 /* maximum envelope content size is 110 (156?) bytes */
5569 envelope_content_len = header[envelope_content_pos];
5571 /* maximum level title size is 40 bytes */
5572 level_name_len = MIN(header[level_name_pos], MAX_LEVEL_NAME_LEN);
5573 /* maximum level author size is 30 (51?) bytes */
5574 level_author_len = MIN(header[level_author_pos], MAX_LEVEL_AUTHOR_LEN);
5578 for (i = 0; i < envelope_header_len; i++)
5579 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
5580 level->envelope[0].text[envelope_size++] =
5581 header[envelope_header_pos + 1 + i];
5583 if (envelope_header_len > 0 && envelope_content_len > 0)
5585 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
5586 level->envelope[0].text[envelope_size++] = '\n';
5587 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
5588 level->envelope[0].text[envelope_size++] = '\n';
5591 for (i = 0; i < envelope_content_len; i++)
5592 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
5593 level->envelope[0].text[envelope_size++] =
5594 header[envelope_content_pos + 1 + i];
5596 level->envelope[0].text[envelope_size] = '\0';
5598 level->envelope[0].xsize = MAX_ENVELOPE_XSIZE;
5599 level->envelope[0].ysize = 10;
5600 level->envelope[0].autowrap = TRUE;
5601 level->envelope[0].centered = TRUE;
5603 for (i = 0; i < level_name_len; i++)
5604 level->name[i] = header[level_name_pos + 1 + i];
5605 level->name[level_name_len] = '\0';
5607 for (i = 0; i < level_author_len; i++)
5608 level->author[i] = header[level_author_pos + 1 + i];
5609 level->author[level_author_len] = '\0';
5611 num_yamyam_contents = header[60] | (header[61] << 8);
5612 level->num_yamyam_contents =
5613 MIN(MAX(MIN_ELEMENT_CONTENTS, num_yamyam_contents), MAX_ELEMENT_CONTENTS);
5615 for (i = 0; i < num_yamyam_contents; i++)
5617 for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
5619 unsigned short word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
5620 int element_dc = ((word & 0xff) << 8) | ((word >> 8) & 0xff);
5622 if (i < MAX_ELEMENT_CONTENTS)
5623 level->yamyam_content[i].e[x][y] = getMappedElement_DC(element_dc);
5627 fieldx = header[6] | (header[7] << 8);
5628 fieldy = header[8] | (header[9] << 8);
5629 level->fieldx = MIN(MAX(MIN_LEV_FIELDX, fieldx), MAX_LEV_FIELDX);
5630 level->fieldy = MIN(MAX(MIN_LEV_FIELDY, fieldy), MAX_LEV_FIELDY);
5632 for (y = 0; y < fieldy; y++) for (x = 0; x < fieldx; x++)
5634 unsigned short word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
5635 int element_dc = ((word & 0xff) << 8) | ((word >> 8) & 0xff);
5637 if (x < MAX_LEV_FIELDX && y < MAX_LEV_FIELDY)
5638 level->field[x][y] = getMappedElement_DC(element_dc);
5641 x = MIN(MAX(0, (header[10] | (header[11] << 8)) - 1), MAX_LEV_FIELDX - 1);
5642 y = MIN(MAX(0, (header[12] | (header[13] << 8)) - 1), MAX_LEV_FIELDY - 1);
5643 level->field[x][y] = EL_PLAYER_1;
5645 x = MIN(MAX(0, (header[14] | (header[15] << 8)) - 1), MAX_LEV_FIELDX - 1);
5646 y = MIN(MAX(0, (header[16] | (header[17] << 8)) - 1), MAX_LEV_FIELDY - 1);
5647 level->field[x][y] = EL_PLAYER_2;
5649 level->gems_needed = header[18] | (header[19] << 8);
5651 level->score[SC_EMERALD] = header[20] | (header[21] << 8);
5652 level->score[SC_DIAMOND] = header[22] | (header[23] << 8);
5653 level->score[SC_PEARL] = header[24] | (header[25] << 8);
5654 level->score[SC_CRYSTAL] = header[26] | (header[27] << 8);
5655 level->score[SC_NUT] = header[28] | (header[29] << 8);
5656 level->score[SC_ROBOT] = header[30] | (header[31] << 8);
5657 level->score[SC_SPACESHIP] = header[32] | (header[33] << 8);
5658 level->score[SC_BUG] = header[34] | (header[35] << 8);
5659 level->score[SC_YAMYAM] = header[36] | (header[37] << 8);
5660 level->score[SC_DYNAMITE] = header[38] | (header[39] << 8);
5661 level->score[SC_KEY] = header[40] | (header[41] << 8);
5662 level->score[SC_TIME_BONUS] = header[42] | (header[43] << 8);
5664 level->time = header[44] | (header[45] << 8);
5666 level->amoeba_speed = header[46] | (header[47] << 8);
5667 level->time_light = header[48] | (header[49] << 8);
5668 level->time_timegate = header[50] | (header[51] << 8);
5669 level->time_wheel = header[52] | (header[53] << 8);
5670 level->time_magic_wall = header[54] | (header[55] << 8);
5671 level->extra_time = header[56] | (header[57] << 8);
5672 level->shield_normal_time = header[58] | (header[59] << 8);
5674 /* Diamond Caves has the same (strange) behaviour as Emerald Mine that gems
5675 can slip down from flat walls, like normal walls and steel walls */
5676 level->em_slippery_gems = TRUE;
5679 static void LoadLevelFromFileInfo_DC(struct LevelInfo *level,
5680 struct LevelFileInfo *level_file_info,
5681 boolean level_info_only)
5683 char *filename = level_file_info->filename;
5685 int num_magic_bytes = 8;
5686 char magic_bytes[num_magic_bytes + 1];
5687 int num_levels_to_skip = level_file_info->nr - leveldir_current->first_level;
5689 if (!(file = openFile(filename, MODE_READ)))
5691 level->no_valid_file = TRUE;
5693 if (!level_info_only)
5694 Error(ERR_WARN, "cannot read level '%s' -- using empty level", filename);
5699 // fseek(file, 0x0000, SEEK_SET);
5701 if (level_file_info->packed)
5703 /* read "magic bytes" from start of file */
5704 if (getStringFromFile(file, magic_bytes, num_magic_bytes + 1) == NULL)
5705 magic_bytes[0] = '\0';
5707 /* check "magic bytes" for correct file format */
5708 if (!strPrefix(magic_bytes, "DC2"))
5710 level->no_valid_file = TRUE;
5712 Error(ERR_WARN, "unknown DC level file '%s' -- using empty level",
5718 if (strPrefix(magic_bytes, "DC2Win95") ||
5719 strPrefix(magic_bytes, "DC2Win98"))
5721 int position_first_level = 0x00fa;
5722 int extra_bytes = 4;
5725 /* advance file stream to first level inside the level package */
5726 skip_bytes = position_first_level - num_magic_bytes - extra_bytes;
5728 /* each block of level data is followed by block of non-level data */
5729 num_levels_to_skip *= 2;
5731 /* at least skip header bytes, therefore use ">= 0" instead of "> 0" */
5732 while (num_levels_to_skip >= 0)
5734 /* advance file stream to next level inside the level package */
5735 if (seekFile(file, skip_bytes, SEEK_CUR) != 0)
5737 level->no_valid_file = TRUE;
5739 Error(ERR_WARN, "cannot fseek in file '%s' -- using empty level",
5745 /* skip apparently unused extra bytes following each level */
5746 ReadUnusedBytesFromFile(file, extra_bytes);
5748 /* read size of next level in level package */
5749 skip_bytes = getFile32BitLE(file);
5751 num_levels_to_skip--;
5756 level->no_valid_file = TRUE;
5758 Error(ERR_WARN, "unknown DC2 level file '%s' -- using empty level",
5765 LoadLevelFromFileStream_DC(file, level, level_file_info->nr);
5771 /* ------------------------------------------------------------------------- */
5772 /* functions for loading SB level */
5773 /* ------------------------------------------------------------------------- */
5775 int getMappedElement_SB(int element_ascii, boolean use_ces)
5783 sb_element_mapping[] =
5785 { ' ', EL_EMPTY, EL_CUSTOM_1 }, /* floor (space) */
5786 { '#', EL_STEELWALL, EL_CUSTOM_2 }, /* wall */
5787 { '@', EL_PLAYER_1, EL_CUSTOM_3 }, /* player */
5788 { '$', EL_SOKOBAN_OBJECT, EL_CUSTOM_4 }, /* box */
5789 { '.', EL_SOKOBAN_FIELD_EMPTY, EL_CUSTOM_5 }, /* goal square */
5790 { '*', EL_SOKOBAN_FIELD_FULL, EL_CUSTOM_6 }, /* box on goal square */
5791 { '+', EL_SOKOBAN_FIELD_PLAYER, EL_CUSTOM_7 }, /* player on goal square */
5792 { '_', EL_INVISIBLE_STEELWALL, EL_FROM_LEVEL_TEMPLATE }, /* floor beyond border */
5799 for (i = 0; sb_element_mapping[i].ascii != 0; i++)
5800 if (element_ascii == sb_element_mapping[i].ascii)
5801 return (use_ces ? sb_element_mapping[i].ce : sb_element_mapping[i].sb);
5803 return EL_UNDEFINED;
5806 static void LoadLevelFromFileInfo_SB(struct LevelInfo *level,
5807 struct LevelFileInfo *level_file_info,
5808 boolean level_info_only)
5810 char *filename = level_file_info->filename;
5811 char line[MAX_LINE_LEN], line_raw[MAX_LINE_LEN], previous_line[MAX_LINE_LEN];
5812 char last_comment[MAX_LINE_LEN];
5813 char level_name[MAX_LINE_LEN];
5816 int num_levels_to_skip = level_file_info->nr - leveldir_current->first_level;
5817 boolean read_continued_line = FALSE;
5818 boolean reading_playfield = FALSE;
5819 boolean got_valid_playfield_line = FALSE;
5820 boolean invalid_playfield_char = FALSE;
5821 boolean load_xsb_to_ces = check_special_flags("load_xsb_to_ces");
5822 int file_level_nr = 0;
5824 int x = 0, y = 0; /* initialized to make compilers happy */
5826 last_comment[0] = '\0';
5827 level_name[0] = '\0';
5829 if (!(file = openFile(filename, MODE_READ)))
5831 level->no_valid_file = TRUE;
5833 if (!level_info_only)
5834 Error(ERR_WARN, "cannot read level '%s' -- using empty level", filename);
5839 while (!checkEndOfFile(file))
5841 /* level successfully read, but next level may follow here */
5842 if (!got_valid_playfield_line && reading_playfield)
5844 /* read playfield from single level file -- skip remaining file */
5845 if (!level_file_info->packed)
5848 if (file_level_nr >= num_levels_to_skip)
5853 last_comment[0] = '\0';
5854 level_name[0] = '\0';
5856 reading_playfield = FALSE;
5859 got_valid_playfield_line = FALSE;
5861 /* read next line of input file */
5862 if (!getStringFromFile(file, line, MAX_LINE_LEN))
5865 /* check if line was completely read and is terminated by line break */
5866 if (strlen(line) > 0 && line[strlen(line) - 1] == '\n')
5869 /* cut trailing line break (this can be newline and/or carriage return) */
5870 for (line_ptr = &line[strlen(line)]; line_ptr >= line; line_ptr--)
5871 if ((*line_ptr == '\n' || *line_ptr == '\r') && *(line_ptr + 1) == '\0')
5874 /* copy raw input line for later use (mainly debugging output) */
5875 strcpy(line_raw, line);
5877 if (read_continued_line)
5879 /* append new line to existing line, if there is enough space */
5880 if (strlen(previous_line) + strlen(line_ptr) < MAX_LINE_LEN)
5881 strcat(previous_line, line_ptr);
5883 strcpy(line, previous_line); /* copy storage buffer to line */
5885 read_continued_line = FALSE;
5888 /* if the last character is '\', continue at next line */
5889 if (strlen(line) > 0 && line[strlen(line) - 1] == '\\')
5891 line[strlen(line) - 1] = '\0'; /* cut off trailing backslash */
5892 strcpy(previous_line, line); /* copy line to storage buffer */
5894 read_continued_line = TRUE;
5899 /* skip empty lines */
5900 if (line[0] == '\0')
5903 /* extract comment text from comment line */
5906 for (line_ptr = line; *line_ptr; line_ptr++)
5907 if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != ';')
5910 strcpy(last_comment, line_ptr);
5915 /* extract level title text from line containing level title */
5916 if (line[0] == '\'')
5918 strcpy(level_name, &line[1]);
5920 if (strlen(level_name) > 0 && level_name[strlen(level_name) - 1] == '\'')
5921 level_name[strlen(level_name) - 1] = '\0';
5926 /* skip lines containing only spaces (or empty lines) */
5927 for (line_ptr = line; *line_ptr; line_ptr++)
5928 if (*line_ptr != ' ')
5930 if (*line_ptr == '\0')
5933 /* at this point, we have found a line containing part of a playfield */
5935 got_valid_playfield_line = TRUE;
5937 if (!reading_playfield)
5939 reading_playfield = TRUE;
5940 invalid_playfield_char = FALSE;
5942 for (x = 0; x < MAX_LEV_FIELDX; x++)
5943 for (y = 0; y < MAX_LEV_FIELDY; y++)
5944 level->field[x][y] = getMappedElement_SB(' ', load_xsb_to_ces);
5949 /* start with topmost tile row */
5953 /* skip playfield line if larger row than allowed */
5954 if (y >= MAX_LEV_FIELDY)
5957 /* start with leftmost tile column */
5960 /* read playfield elements from line */
5961 for (line_ptr = line; *line_ptr; line_ptr++)
5963 int mapped_sb_element = getMappedElement_SB(*line_ptr, load_xsb_to_ces);
5965 /* stop parsing playfield line if larger column than allowed */
5966 if (x >= MAX_LEV_FIELDX)
5969 if (mapped_sb_element == EL_UNDEFINED)
5971 invalid_playfield_char = TRUE;
5976 level->field[x][y] = mapped_sb_element;
5978 /* continue with next tile column */
5981 level->fieldx = MAX(x, level->fieldx);
5984 if (invalid_playfield_char)
5986 /* if first playfield line, treat invalid lines as comment lines */
5988 reading_playfield = FALSE;
5993 /* continue with next tile row */
6001 level->fieldx = MIN(MAX(MIN_LEV_FIELDX, level->fieldx), MAX_LEV_FIELDX);
6002 level->fieldy = MIN(MAX(MIN_LEV_FIELDY, level->fieldy), MAX_LEV_FIELDY);
6004 if (!reading_playfield)
6006 level->no_valid_file = TRUE;
6008 Error(ERR_WARN, "cannot read level '%s' -- using empty level", filename);
6013 if (*level_name != '\0')
6015 strncpy(level->name, level_name, MAX_LEVEL_NAME_LEN);
6016 level->name[MAX_LEVEL_NAME_LEN] = '\0';
6018 else if (*last_comment != '\0')
6020 strncpy(level->name, last_comment, MAX_LEVEL_NAME_LEN);
6021 level->name[MAX_LEVEL_NAME_LEN] = '\0';
6025 sprintf(level->name, "--> Level %d <--", level_file_info->nr);
6028 /* set all empty fields beyond the border walls to invisible steel wall */
6029 for (y = 0; y < level->fieldy; y++) for (x = 0; x < level->fieldx; x++)
6031 if ((x == 0 || x == level->fieldx - 1 ||
6032 y == 0 || y == level->fieldy - 1) &&
6033 level->field[x][y] == getMappedElement_SB(' ', load_xsb_to_ces))
6034 FloodFillLevel(x, y, getMappedElement_SB('_', load_xsb_to_ces),
6035 level->field, level->fieldx, level->fieldy);
6038 /* set special level settings for Sokoban levels */
6041 level->use_step_counter = TRUE;
6043 if (load_xsb_to_ces)
6045 /* special global settings can now be set in level template */
6047 level->use_custom_template = TRUE;
6052 /* ------------------------------------------------------------------------- */
6053 /* functions for handling native levels */
6054 /* ------------------------------------------------------------------------- */
6056 static void LoadLevelFromFileInfo_EM(struct LevelInfo *level,
6057 struct LevelFileInfo *level_file_info,
6058 boolean level_info_only)
6060 if (!LoadNativeLevel_EM(level_file_info->filename, level_info_only))
6061 level->no_valid_file = TRUE;
6064 static void LoadLevelFromFileInfo_SP(struct LevelInfo *level,
6065 struct LevelFileInfo *level_file_info,
6066 boolean level_info_only)
6070 /* determine position of requested level inside level package */
6071 if (level_file_info->packed)
6072 pos = level_file_info->nr - leveldir_current->first_level;
6074 if (!LoadNativeLevel_SP(level_file_info->filename, pos, level_info_only))
6075 level->no_valid_file = TRUE;
6078 static void LoadLevelFromFileInfo_MM(struct LevelInfo *level,
6079 struct LevelFileInfo *level_file_info,
6080 boolean level_info_only)
6082 if (!LoadNativeLevel_MM(level_file_info->filename, level_info_only))
6083 level->no_valid_file = TRUE;
6086 void CopyNativeLevel_RND_to_Native(struct LevelInfo *level)
6088 if (level->game_engine_type == GAME_ENGINE_TYPE_EM)
6089 CopyNativeLevel_RND_to_EM(level);
6090 else if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
6091 CopyNativeLevel_RND_to_SP(level);
6092 else if (level->game_engine_type == GAME_ENGINE_TYPE_MM)
6093 CopyNativeLevel_RND_to_MM(level);
6096 void CopyNativeLevel_Native_to_RND(struct LevelInfo *level)
6098 if (level->game_engine_type == GAME_ENGINE_TYPE_EM)
6099 CopyNativeLevel_EM_to_RND(level);
6100 else if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
6101 CopyNativeLevel_SP_to_RND(level);
6102 else if (level->game_engine_type == GAME_ENGINE_TYPE_MM)
6103 CopyNativeLevel_MM_to_RND(level);
6106 void SaveNativeLevel(struct LevelInfo *level)
6108 if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
6110 char *basename = getSingleLevelBasenameExt(level->file_info.nr, "sp");
6111 char *filename = getLevelFilenameFromBasename(basename);
6113 CopyNativeLevel_RND_to_SP(level);
6114 CopyNativeTape_RND_to_SP(level);
6116 SaveNativeLevel_SP(filename);
6121 /* ------------------------------------------------------------------------- */
6122 /* functions for loading generic level */
6123 /* ------------------------------------------------------------------------- */
6125 static void LoadLevelFromFileInfo(struct LevelInfo *level,
6126 struct LevelFileInfo *level_file_info,
6127 boolean level_info_only)
6129 /* always start with reliable default values */
6130 setLevelInfoToDefaults(level, level_info_only, TRUE);
6132 switch (level_file_info->type)
6134 case LEVEL_FILE_TYPE_RND:
6135 LoadLevelFromFileInfo_RND(level, level_file_info, level_info_only);
6138 case LEVEL_FILE_TYPE_EM:
6139 LoadLevelFromFileInfo_EM(level, level_file_info, level_info_only);
6140 level->game_engine_type = GAME_ENGINE_TYPE_EM;
6143 case LEVEL_FILE_TYPE_SP:
6144 LoadLevelFromFileInfo_SP(level, level_file_info, level_info_only);
6145 level->game_engine_type = GAME_ENGINE_TYPE_SP;
6148 case LEVEL_FILE_TYPE_MM:
6149 LoadLevelFromFileInfo_MM(level, level_file_info, level_info_only);
6150 level->game_engine_type = GAME_ENGINE_TYPE_MM;
6153 case LEVEL_FILE_TYPE_DC:
6154 LoadLevelFromFileInfo_DC(level, level_file_info, level_info_only);
6157 case LEVEL_FILE_TYPE_SB:
6158 LoadLevelFromFileInfo_SB(level, level_file_info, level_info_only);
6162 LoadLevelFromFileInfo_RND(level, level_file_info, level_info_only);
6166 /* if level file is invalid, restore level structure to default values */
6167 if (level->no_valid_file)
6168 setLevelInfoToDefaults(level, level_info_only, FALSE);
6170 if (level->game_engine_type == GAME_ENGINE_TYPE_UNKNOWN)
6171 level->game_engine_type = GAME_ENGINE_TYPE_RND;
6173 if (level_file_info->type != LEVEL_FILE_TYPE_RND)
6174 CopyNativeLevel_Native_to_RND(level);
6177 void LoadLevelFromFilename(struct LevelInfo *level, char *filename)
6179 static struct LevelFileInfo level_file_info;
6181 /* always start with reliable default values */
6182 setFileInfoToDefaults(&level_file_info);
6184 level_file_info.nr = 0; /* unknown level number */
6185 level_file_info.type = LEVEL_FILE_TYPE_RND; /* no others supported yet */
6186 level_file_info.filename = filename;
6188 LoadLevelFromFileInfo(level, &level_file_info, FALSE);
6191 static void LoadLevel_InitVersion(struct LevelInfo *level, char *filename)
6195 if (leveldir_current == NULL) /* only when dumping level */
6198 /* all engine modifications also valid for levels which use latest engine */
6199 if (level->game_version < VERSION_IDENT(3,2,0,5))
6201 /* time bonus score was given for 10 s instead of 1 s before 3.2.0-5 */
6202 level->score[SC_TIME_BONUS] /= 10;
6205 if (leveldir_current->latest_engine)
6207 /* ---------- use latest game engine ----------------------------------- */
6209 /* For all levels which are forced to use the latest game engine version
6210 (normally all but user contributed, private and undefined levels), set
6211 the game engine version to the actual version; this allows for actual
6212 corrections in the game engine to take effect for existing, converted
6213 levels (from "classic" or other existing games) to make the emulation
6214 of the corresponding game more accurate, while (hopefully) not breaking
6215 existing levels created from other players. */
6217 level->game_version = GAME_VERSION_ACTUAL;
6219 /* Set special EM style gems behaviour: EM style gems slip down from
6220 normal, steel and growing wall. As this is a more fundamental change,
6221 it seems better to set the default behaviour to "off" (as it is more
6222 natural) and make it configurable in the level editor (as a property
6223 of gem style elements). Already existing converted levels (neither
6224 private nor contributed levels) are changed to the new behaviour. */
6226 if (level->file_version < FILE_VERSION_2_0)
6227 level->em_slippery_gems = TRUE;
6232 /* ---------- use game engine the level was created with ----------------- */
6234 /* For all levels which are not forced to use the latest game engine
6235 version (normally user contributed, private and undefined levels),
6236 use the version of the game engine the levels were created for.
6238 Since 2.0.1, the game engine version is now directly stored
6239 in the level file (chunk "VERS"), so there is no need anymore
6240 to set the game version from the file version (except for old,
6241 pre-2.0 levels, where the game version is still taken from the
6242 file format version used to store the level -- see above). */
6244 /* player was faster than enemies in 1.0.0 and before */
6245 if (level->file_version == FILE_VERSION_1_0)
6246 for (i = 0; i < MAX_PLAYERS; i++)
6247 level->initial_player_stepsize[i] = STEPSIZE_FAST;
6249 /* default behaviour for EM style gems was "slippery" only in 2.0.1 */
6250 if (level->game_version == VERSION_IDENT(2,0,1,0))
6251 level->em_slippery_gems = TRUE;
6253 /* springs could be pushed over pits before (pre-release version) 2.2.0 */
6254 if (level->game_version < VERSION_IDENT(2,2,0,0))
6255 level->use_spring_bug = TRUE;
6257 if (level->game_version < VERSION_IDENT(3,2,0,5))
6259 /* time orb caused limited time in endless time levels before 3.2.0-5 */
6260 level->use_time_orb_bug = TRUE;
6262 /* default behaviour for snapping was "no snap delay" before 3.2.0-5 */
6263 level->block_snap_field = FALSE;
6265 /* extra time score was same value as time left score before 3.2.0-5 */
6266 level->extra_time_score = level->score[SC_TIME_BONUS];
6269 if (level->game_version < VERSION_IDENT(3,2,0,7))
6271 /* default behaviour for snapping was "not continuous" before 3.2.0-7 */
6272 level->continuous_snapping = FALSE;
6275 /* only few elements were able to actively move into acid before 3.1.0 */
6276 /* trigger settings did not exist before 3.1.0; set to default "any" */
6277 if (level->game_version < VERSION_IDENT(3,1,0,0))
6279 /* correct "can move into acid" settings (all zero in old levels) */
6281 level->can_move_into_acid_bits = 0; /* nothing can move into acid */
6282 level->dont_collide_with_bits = 0; /* nothing is deadly when colliding */
6284 setMoveIntoAcidProperty(level, EL_ROBOT, TRUE);
6285 setMoveIntoAcidProperty(level, EL_SATELLITE, TRUE);
6286 setMoveIntoAcidProperty(level, EL_PENGUIN, TRUE);
6287 setMoveIntoAcidProperty(level, EL_BALLOON, TRUE);
6289 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6290 SET_PROPERTY(EL_CUSTOM_START + i, EP_CAN_MOVE_INTO_ACID, TRUE);
6292 /* correct trigger settings (stored as zero == "none" in old levels) */
6294 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6296 int element = EL_CUSTOM_START + i;
6297 struct ElementInfo *ei = &element_info[element];
6299 for (j = 0; j < ei->num_change_pages; j++)
6301 struct ElementChangeInfo *change = &ei->change_page[j];
6303 change->trigger_player = CH_PLAYER_ANY;
6304 change->trigger_page = CH_PAGE_ANY;
6309 /* try to detect and fix "Snake Bite" levels, which are broken with 3.2.0 */
6311 int element = EL_CUSTOM_256;
6312 struct ElementInfo *ei = &element_info[element];
6313 struct ElementChangeInfo *change = &ei->change_page[0];
6315 /* This is needed to fix a problem that was caused by a bugfix in function
6316 game.c/CreateFieldExt() introduced with 3.2.0 that corrects the behaviour
6317 when a custom element changes to EL_SOKOBAN_FIELD_PLAYER (before, it did
6318 not replace walkable elements, but instead just placed the player on it,
6319 without placing the Sokoban field under the player). Unfortunately, this
6320 breaks "Snake Bite" style levels when the snake is halfway through a door
6321 that just closes (the snake head is still alive and can be moved in this
6322 case). This can be fixed by replacing the EL_SOKOBAN_FIELD_PLAYER by the
6323 player (without Sokoban element) which then gets killed as designed). */
6325 if ((strncmp(leveldir_current->identifier, "snake_bite", 10) == 0 ||
6326 strncmp(ei->description, "pause b4 death", 14) == 0) &&
6327 change->target_element == EL_SOKOBAN_FIELD_PLAYER)
6328 change->target_element = EL_PLAYER_1;
6331 /* try to detect and fix "Zelda" style levels, which are broken with 3.2.5 */
6332 if (level->game_version < VERSION_IDENT(3,2,5,0))
6334 /* This is needed to fix a problem that was caused by a bugfix in function
6335 game.c/CheckTriggeredElementChangeExt() introduced with 3.2.5 that
6336 corrects the behaviour when a custom element changes to another custom
6337 element with a higher element number that has change actions defined.
6338 Normally, only one change per frame is allowed for custom elements.
6339 Therefore, it is checked if a custom element already changed in the
6340 current frame; if it did, subsequent changes are suppressed.
6341 Unfortunately, this is only checked for element changes, but not for
6342 change actions, which are still executed. As the function above loops
6343 through all custom elements from lower to higher, an element change
6344 resulting in a lower CE number won't be checked again, while a target
6345 element with a higher number will also be checked, and potential change
6346 actions will get executed for this CE, too (which is wrong), while
6347 further changes are ignored (which is correct). As this bugfix breaks
6348 Zelda II (and introduces graphical bugs to Zelda I, and also breaks a
6349 few other levels like Alan Bond's "FMV"), allow the previous, incorrect
6350 behaviour for existing levels and tapes that make use of this bug */
6352 level->use_action_after_change_bug = TRUE;
6355 /* not centering level after relocating player was default only in 3.2.3 */
6356 if (level->game_version == VERSION_IDENT(3,2,3,0)) /* (no pre-releases) */
6357 level->shifted_relocation = TRUE;
6359 /* EM style elements always chain-exploded in R'n'D engine before 3.2.6 */
6360 if (level->game_version < VERSION_IDENT(3,2,6,0))
6361 level->em_explodes_by_fire = TRUE;
6364 static void LoadLevel_InitStandardElements(struct LevelInfo *level)
6368 /* map elements that have changed in newer versions */
6369 level->amoeba_content = getMappedElementByVersion(level->amoeba_content,
6370 level->game_version);
6371 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
6372 for (x = 0; x < 3; x++)
6373 for (y = 0; y < 3; y++)
6374 level->yamyam_content[i].e[x][y] =
6375 getMappedElementByVersion(level->yamyam_content[i].e[x][y],
6376 level->game_version);
6380 static void LoadLevel_InitCustomElements(struct LevelInfo *level)
6384 /* map custom element change events that have changed in newer versions
6385 (these following values were accidentally changed in version 3.0.1)
6386 (this seems to be needed only for 'ab_levelset3' and 'ab_levelset4') */
6387 if (level->game_version <= VERSION_IDENT(3,0,0,0))
6389 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6391 int element = EL_CUSTOM_START + i;
6393 /* order of checking and copying events to be mapped is important */
6394 /* (do not change the start and end value -- they are constant) */
6395 for (j = CE_BY_OTHER_ACTION; j >= CE_VALUE_GETS_ZERO; j--)
6397 if (HAS_CHANGE_EVENT(element, j - 2))
6399 SET_CHANGE_EVENT(element, j - 2, FALSE);
6400 SET_CHANGE_EVENT(element, j, TRUE);
6404 /* order of checking and copying events to be mapped is important */
6405 /* (do not change the start and end value -- they are constant) */
6406 for (j = CE_PLAYER_COLLECTS_X; j >= CE_HITTING_SOMETHING; j--)
6408 if (HAS_CHANGE_EVENT(element, j - 1))
6410 SET_CHANGE_EVENT(element, j - 1, FALSE);
6411 SET_CHANGE_EVENT(element, j, TRUE);
6417 /* initialize "can_change" field for old levels with only one change page */
6418 if (level->game_version <= VERSION_IDENT(3,0,2,0))
6420 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6422 int element = EL_CUSTOM_START + i;
6424 if (CAN_CHANGE(element))
6425 element_info[element].change->can_change = TRUE;
6429 /* correct custom element values (for old levels without these options) */
6430 if (level->game_version < VERSION_IDENT(3,1,1,0))
6432 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6434 int element = EL_CUSTOM_START + i;
6435 struct ElementInfo *ei = &element_info[element];
6437 if (ei->access_direction == MV_NO_DIRECTION)
6438 ei->access_direction = MV_ALL_DIRECTIONS;
6442 /* correct custom element values (fix invalid values for all versions) */
6445 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6447 int element = EL_CUSTOM_START + i;
6448 struct ElementInfo *ei = &element_info[element];
6450 for (j = 0; j < ei->num_change_pages; j++)
6452 struct ElementChangeInfo *change = &ei->change_page[j];
6454 if (change->trigger_player == CH_PLAYER_NONE)
6455 change->trigger_player = CH_PLAYER_ANY;
6457 if (change->trigger_side == CH_SIDE_NONE)
6458 change->trigger_side = CH_SIDE_ANY;
6463 /* initialize "can_explode" field for old levels which did not store this */
6464 /* !!! CHECK THIS -- "<= 3,1,0,0" IS PROBABLY WRONG !!! */
6465 if (level->game_version <= VERSION_IDENT(3,1,0,0))
6467 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6469 int element = EL_CUSTOM_START + i;
6471 if (EXPLODES_1X1_OLD(element))
6472 element_info[element].explosion_type = EXPLODES_1X1;
6474 SET_PROPERTY(element, EP_CAN_EXPLODE, (EXPLODES_BY_FIRE(element) ||
6475 EXPLODES_SMASHED(element) ||
6476 EXPLODES_IMPACT(element)));
6480 /* correct previously hard-coded move delay values for maze runner style */
6481 if (level->game_version < VERSION_IDENT(3,1,1,0))
6483 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6485 int element = EL_CUSTOM_START + i;
6487 if (element_info[element].move_pattern & MV_MAZE_RUNNER_STYLE)
6489 /* previously hard-coded and therefore ignored */
6490 element_info[element].move_delay_fixed = 9;
6491 element_info[element].move_delay_random = 0;
6496 /* set some other uninitialized values of custom elements in older levels */
6497 if (level->game_version < VERSION_IDENT(3,1,0,0))
6499 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6501 int element = EL_CUSTOM_START + i;
6503 element_info[element].access_direction = MV_ALL_DIRECTIONS;
6505 element_info[element].explosion_delay = 17;
6506 element_info[element].ignition_delay = 8;
6511 static void LoadLevel_InitElements(struct LevelInfo *level, char *filename)
6513 LoadLevel_InitStandardElements(level);
6515 if (level->file_has_custom_elements)
6516 LoadLevel_InitCustomElements(level);
6518 /* initialize element properties for level editor etc. */
6519 InitElementPropertiesEngine(level->game_version);
6520 InitElementPropertiesGfxElement();
6523 static void LoadLevel_InitPlayfield(struct LevelInfo *level, char *filename)
6527 /* map elements that have changed in newer versions */
6528 for (y = 0; y < level->fieldy; y++)
6529 for (x = 0; x < level->fieldx; x++)
6530 level->field[x][y] = getMappedElementByVersion(level->field[x][y],
6531 level->game_version);
6533 /* clear unused playfield data (nicer if level gets resized in editor) */
6534 for (x = 0; x < MAX_LEV_FIELDX; x++)
6535 for (y = 0; y < MAX_LEV_FIELDY; y++)
6536 if (x >= level->fieldx || y >= level->fieldy)
6537 level->field[x][y] = EL_EMPTY;
6539 /* copy elements to runtime playfield array */
6540 for (x = 0; x < MAX_LEV_FIELDX; x++)
6541 for (y = 0; y < MAX_LEV_FIELDY; y++)
6542 Feld[x][y] = level->field[x][y];
6544 /* initialize level size variables for faster access */
6545 lev_fieldx = level->fieldx;
6546 lev_fieldy = level->fieldy;
6548 /* determine border element for this level */
6549 if (level->file_info.type == LEVEL_FILE_TYPE_DC)
6550 BorderElement = EL_EMPTY; /* (in editor, SetBorderElement() is used) */
6555 static void LoadLevel_InitNativeEngines(struct LevelInfo *level,char *filename)
6557 struct LevelFileInfo *level_file_info = &level->file_info;
6559 if (level_file_info->type == LEVEL_FILE_TYPE_RND)
6560 CopyNativeLevel_RND_to_Native(level);
6563 void LoadLevelTemplate(int nr)
6567 setLevelFileInfo(&level_template.file_info, nr);
6568 filename = level_template.file_info.filename;
6570 LoadLevelFromFileInfo(&level_template, &level_template.file_info, FALSE);
6572 LoadLevel_InitVersion(&level_template, filename);
6573 LoadLevel_InitElements(&level_template, filename);
6575 ActivateLevelTemplate();
6578 void LoadLevel(int nr)
6582 setLevelFileInfo(&level.file_info, nr);
6583 filename = level.file_info.filename;
6585 LoadLevelFromFileInfo(&level, &level.file_info, FALSE);
6587 if (level.use_custom_template)
6588 LoadLevelTemplate(-1);
6590 LoadLevel_InitVersion(&level, filename);
6591 LoadLevel_InitElements(&level, filename);
6592 LoadLevel_InitPlayfield(&level, filename);
6594 LoadLevel_InitNativeEngines(&level, filename);
6597 void LoadLevelInfoOnly(int nr)
6599 setLevelFileInfo(&level.file_info, nr);
6601 LoadLevelFromFileInfo(&level, &level.file_info, TRUE);
6604 static int SaveLevel_VERS(FILE *file, struct LevelInfo *level)
6608 chunk_size += putFileVersion(file, level->file_version);
6609 chunk_size += putFileVersion(file, level->game_version);
6614 static int SaveLevel_DATE(FILE *file, struct LevelInfo *level)
6618 chunk_size += putFile16BitBE(file, level->creation_date.year);
6619 chunk_size += putFile8Bit(file, level->creation_date.month);
6620 chunk_size += putFile8Bit(file, level->creation_date.day);
6625 #if ENABLE_HISTORIC_CHUNKS
6626 static void SaveLevel_HEAD(FILE *file, struct LevelInfo *level)
6630 putFile8Bit(file, level->fieldx);
6631 putFile8Bit(file, level->fieldy);
6633 putFile16BitBE(file, level->time);
6634 putFile16BitBE(file, level->gems_needed);
6636 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
6637 putFile8Bit(file, level->name[i]);
6639 for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
6640 putFile8Bit(file, level->score[i]);
6642 for (i = 0; i < STD_ELEMENT_CONTENTS; i++)
6643 for (y = 0; y < 3; y++)
6644 for (x = 0; x < 3; x++)
6645 putFile8Bit(file, (level->encoding_16bit_yamyam ? EL_EMPTY :
6646 level->yamyam_content[i].e[x][y]));
6647 putFile8Bit(file, level->amoeba_speed);
6648 putFile8Bit(file, level->time_magic_wall);
6649 putFile8Bit(file, level->time_wheel);
6650 putFile8Bit(file, (level->encoding_16bit_amoeba ? EL_EMPTY :
6651 level->amoeba_content));
6652 putFile8Bit(file, (level->initial_player_stepsize == STEPSIZE_FAST ? 1 : 0));
6653 putFile8Bit(file, (level->initial_gravity ? 1 : 0));
6654 putFile8Bit(file, (level->encoding_16bit_field ? 1 : 0));
6655 putFile8Bit(file, (level->em_slippery_gems ? 1 : 0));
6657 putFile8Bit(file, (level->use_custom_template ? 1 : 0));
6659 putFile8Bit(file, (level->block_last_field ? 1 : 0));
6660 putFile8Bit(file, (level->sp_block_last_field ? 1 : 0));
6661 putFile32BitBE(file, level->can_move_into_acid_bits);
6662 putFile8Bit(file, level->dont_collide_with_bits);
6664 putFile8Bit(file, (level->use_spring_bug ? 1 : 0));
6665 putFile8Bit(file, (level->use_step_counter ? 1 : 0));
6667 putFile8Bit(file, (level->instant_relocation ? 1 : 0));
6668 putFile8Bit(file, (level->can_pass_to_walkable ? 1 : 0));
6669 putFile8Bit(file, (level->grow_into_diggable ? 1 : 0));
6671 putFile8Bit(file, level->game_engine_type);
6673 WriteUnusedBytesToFile(file, LEVEL_CHUNK_HEAD_UNUSED);
6677 static int SaveLevel_NAME(FILE *file, struct LevelInfo *level)
6682 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
6683 chunk_size += putFile8Bit(file, level->name[i]);
6688 static int SaveLevel_AUTH(FILE *file, struct LevelInfo *level)
6693 for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
6694 chunk_size += putFile8Bit(file, level->author[i]);
6699 #if ENABLE_HISTORIC_CHUNKS
6700 static int SaveLevel_BODY(FILE *file, struct LevelInfo *level)
6705 for (y = 0; y < level->fieldy; y++)
6706 for (x = 0; x < level->fieldx; x++)
6707 if (level->encoding_16bit_field)
6708 chunk_size += putFile16BitBE(file, level->field[x][y]);
6710 chunk_size += putFile8Bit(file, level->field[x][y]);
6716 static int SaveLevel_BODY(FILE *file, struct LevelInfo *level)
6721 for (y = 0; y < level->fieldy; y++)
6722 for (x = 0; x < level->fieldx; x++)
6723 chunk_size += putFile16BitBE(file, level->field[x][y]);
6728 #if ENABLE_HISTORIC_CHUNKS
6729 static void SaveLevel_CONT(FILE *file, struct LevelInfo *level)
6733 putFile8Bit(file, EL_YAMYAM);
6734 putFile8Bit(file, level->num_yamyam_contents);
6735 putFile8Bit(file, 0);
6736 putFile8Bit(file, 0);
6738 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
6739 for (y = 0; y < 3; y++)
6740 for (x = 0; x < 3; x++)
6741 if (level->encoding_16bit_field)
6742 putFile16BitBE(file, level->yamyam_content[i].e[x][y]);
6744 putFile8Bit(file, level->yamyam_content[i].e[x][y]);
6748 #if ENABLE_HISTORIC_CHUNKS
6749 static void SaveLevel_CNT2(FILE *file, struct LevelInfo *level, int element)
6752 int num_contents, content_xsize, content_ysize;
6753 int content_array[MAX_ELEMENT_CONTENTS][3][3];
6755 if (element == EL_YAMYAM)
6757 num_contents = level->num_yamyam_contents;
6761 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
6762 for (y = 0; y < 3; y++)
6763 for (x = 0; x < 3; x++)
6764 content_array[i][x][y] = level->yamyam_content[i].e[x][y];
6766 else if (element == EL_BD_AMOEBA)
6772 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
6773 for (y = 0; y < 3; y++)
6774 for (x = 0; x < 3; x++)
6775 content_array[i][x][y] = EL_EMPTY;
6776 content_array[0][0][0] = level->amoeba_content;
6780 /* chunk header already written -- write empty chunk data */
6781 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_SIZE);
6783 Error(ERR_WARN, "cannot save content for element '%d'", element);
6787 putFile16BitBE(file, element);
6788 putFile8Bit(file, num_contents);
6789 putFile8Bit(file, content_xsize);
6790 putFile8Bit(file, content_ysize);
6792 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_UNUSED);
6794 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
6795 for (y = 0; y < 3; y++)
6796 for (x = 0; x < 3; x++)
6797 putFile16BitBE(file, content_array[i][x][y]);
6801 #if ENABLE_HISTORIC_CHUNKS
6802 static int SaveLevel_CNT3(FILE *file, struct LevelInfo *level, int element)
6804 int envelope_nr = element - EL_ENVELOPE_1;
6805 int envelope_len = strlen(level->envelope_text[envelope_nr]) + 1;
6809 chunk_size += putFile16BitBE(file, element);
6810 chunk_size += putFile16BitBE(file, envelope_len);
6811 chunk_size += putFile8Bit(file, level->envelope_xsize[envelope_nr]);
6812 chunk_size += putFile8Bit(file, level->envelope_ysize[envelope_nr]);
6814 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT3_UNUSED);
6815 chunk_size += LEVEL_CHUNK_CNT3_UNUSED;
6817 for (i = 0; i < envelope_len; i++)
6818 chunk_size += putFile8Bit(file, level->envelope_text[envelope_nr][i]);
6824 #if ENABLE_HISTORIC_CHUNKS
6825 static void SaveLevel_CUS1(FILE *file, struct LevelInfo *level,
6826 int num_changed_custom_elements)
6830 putFile16BitBE(file, num_changed_custom_elements);
6832 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6834 int element = EL_CUSTOM_START + i;
6836 struct ElementInfo *ei = &element_info[element];
6838 if (ei->properties[EP_BITFIELD_BASE_NR] != EP_BITMASK_DEFAULT)
6840 if (check < num_changed_custom_elements)
6842 putFile16BitBE(file, element);
6843 putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
6850 if (check != num_changed_custom_elements) /* should not happen */
6851 Error(ERR_WARN, "inconsistent number of custom element properties");
6855 #if ENABLE_HISTORIC_CHUNKS
6856 static void SaveLevel_CUS2(FILE *file, struct LevelInfo *level,
6857 int num_changed_custom_elements)
6861 putFile16BitBE(file, num_changed_custom_elements);
6863 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6865 int element = EL_CUSTOM_START + i;
6867 if (element_info[element].change->target_element != EL_EMPTY_SPACE)
6869 if (check < num_changed_custom_elements)
6871 putFile16BitBE(file, element);
6872 putFile16BitBE(file, element_info[element].change->target_element);
6879 if (check != num_changed_custom_elements) /* should not happen */
6880 Error(ERR_WARN, "inconsistent number of custom target elements");
6884 #if ENABLE_HISTORIC_CHUNKS
6885 static void SaveLevel_CUS3(FILE *file, struct LevelInfo *level,
6886 int num_changed_custom_elements)
6888 int i, j, x, y, check = 0;
6890 putFile16BitBE(file, num_changed_custom_elements);
6892 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6894 int element = EL_CUSTOM_START + i;
6895 struct ElementInfo *ei = &element_info[element];
6897 if (ei->modified_settings)
6899 if (check < num_changed_custom_elements)
6901 putFile16BitBE(file, element);
6903 for (j = 0; j < MAX_ELEMENT_NAME_LEN; j++)
6904 putFile8Bit(file, ei->description[j]);
6906 putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
6908 /* some free bytes for future properties and padding */
6909 WriteUnusedBytesToFile(file, 7);
6911 putFile8Bit(file, ei->use_gfx_element);
6912 putFile16BitBE(file, ei->gfx_element_initial);
6914 putFile8Bit(file, ei->collect_score_initial);
6915 putFile8Bit(file, ei->collect_count_initial);
6917 putFile16BitBE(file, ei->push_delay_fixed);
6918 putFile16BitBE(file, ei->push_delay_random);
6919 putFile16BitBE(file, ei->move_delay_fixed);
6920 putFile16BitBE(file, ei->move_delay_random);
6922 putFile16BitBE(file, ei->move_pattern);
6923 putFile8Bit(file, ei->move_direction_initial);
6924 putFile8Bit(file, ei->move_stepsize);
6926 for (y = 0; y < 3; y++)
6927 for (x = 0; x < 3; x++)
6928 putFile16BitBE(file, ei->content.e[x][y]);
6930 putFile32BitBE(file, ei->change->events);
6932 putFile16BitBE(file, ei->change->target_element);
6934 putFile16BitBE(file, ei->change->delay_fixed);
6935 putFile16BitBE(file, ei->change->delay_random);
6936 putFile16BitBE(file, ei->change->delay_frames);
6938 putFile16BitBE(file, ei->change->initial_trigger_element);
6940 putFile8Bit(file, ei->change->explode);
6941 putFile8Bit(file, ei->change->use_target_content);
6942 putFile8Bit(file, ei->change->only_if_complete);
6943 putFile8Bit(file, ei->change->use_random_replace);
6945 putFile8Bit(file, ei->change->random_percentage);
6946 putFile8Bit(file, ei->change->replace_when);
6948 for (y = 0; y < 3; y++)
6949 for (x = 0; x < 3; x++)
6950 putFile16BitBE(file, ei->change->content.e[x][y]);
6952 putFile8Bit(file, ei->slippery_type);
6954 /* some free bytes for future properties and padding */
6955 WriteUnusedBytesToFile(file, LEVEL_CPART_CUS3_UNUSED);
6962 if (check != num_changed_custom_elements) /* should not happen */
6963 Error(ERR_WARN, "inconsistent number of custom element properties");
6967 #if ENABLE_HISTORIC_CHUNKS
6968 static void SaveLevel_CUS4(FILE *file, struct LevelInfo *level, int element)
6970 struct ElementInfo *ei = &element_info[element];
6973 /* ---------- custom element base property values (96 bytes) ------------- */
6975 putFile16BitBE(file, element);
6977 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
6978 putFile8Bit(file, ei->description[i]);
6980 putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
6982 WriteUnusedBytesToFile(file, 4); /* reserved for more base properties */
6984 putFile8Bit(file, ei->num_change_pages);
6986 putFile16BitBE(file, ei->ce_value_fixed_initial);
6987 putFile16BitBE(file, ei->ce_value_random_initial);
6988 putFile8Bit(file, ei->use_last_ce_value);
6990 putFile8Bit(file, ei->use_gfx_element);
6991 putFile16BitBE(file, ei->gfx_element_initial);
6993 putFile8Bit(file, ei->collect_score_initial);
6994 putFile8Bit(file, ei->collect_count_initial);
6996 putFile8Bit(file, ei->drop_delay_fixed);
6997 putFile8Bit(file, ei->push_delay_fixed);
6998 putFile8Bit(file, ei->drop_delay_random);
6999 putFile8Bit(file, ei->push_delay_random);
7000 putFile16BitBE(file, ei->move_delay_fixed);
7001 putFile16BitBE(file, ei->move_delay_random);
7003 /* bits 0 - 15 of "move_pattern" ... */
7004 putFile16BitBE(file, ei->move_pattern & 0xffff);
7005 putFile8Bit(file, ei->move_direction_initial);
7006 putFile8Bit(file, ei->move_stepsize);
7008 putFile8Bit(file, ei->slippery_type);
7010 for (y = 0; y < 3; y++)
7011 for (x = 0; x < 3; x++)
7012 putFile16BitBE(file, ei->content.e[x][y]);
7014 putFile16BitBE(file, ei->move_enter_element);
7015 putFile16BitBE(file, ei->move_leave_element);
7016 putFile8Bit(file, ei->move_leave_type);
7018 /* ... bits 16 - 31 of "move_pattern" (not nice, but downward compatible) */
7019 putFile16BitBE(file, (ei->move_pattern >> 16) & 0xffff);
7021 putFile8Bit(file, ei->access_direction);
7023 putFile8Bit(file, ei->explosion_delay);
7024 putFile8Bit(file, ei->ignition_delay);
7025 putFile8Bit(file, ei->explosion_type);
7027 /* some free bytes for future custom property values and padding */
7028 WriteUnusedBytesToFile(file, 1);
7030 /* ---------- change page property values (48 bytes) --------------------- */
7032 for (i = 0; i < ei->num_change_pages; i++)
7034 struct ElementChangeInfo *change = &ei->change_page[i];
7035 unsigned int event_bits;
7037 /* bits 0 - 31 of "has_event[]" ... */
7039 for (j = 0; j < MIN(NUM_CHANGE_EVENTS, 32); j++)
7040 if (change->has_event[j])
7041 event_bits |= (1 << j);
7042 putFile32BitBE(file, event_bits);
7044 putFile16BitBE(file, change->target_element);
7046 putFile16BitBE(file, change->delay_fixed);
7047 putFile16BitBE(file, change->delay_random);
7048 putFile16BitBE(file, change->delay_frames);
7050 putFile16BitBE(file, change->initial_trigger_element);
7052 putFile8Bit(file, change->explode);
7053 putFile8Bit(file, change->use_target_content);
7054 putFile8Bit(file, change->only_if_complete);
7055 putFile8Bit(file, change->use_random_replace);
7057 putFile8Bit(file, change->random_percentage);
7058 putFile8Bit(file, change->replace_when);
7060 for (y = 0; y < 3; y++)
7061 for (x = 0; x < 3; x++)
7062 putFile16BitBE(file, change->target_content.e[x][y]);
7064 putFile8Bit(file, change->can_change);
7066 putFile8Bit(file, change->trigger_side);
7068 putFile8Bit(file, change->trigger_player);
7069 putFile8Bit(file, (change->trigger_page == CH_PAGE_ANY ? CH_PAGE_ANY_FILE :
7070 log_2(change->trigger_page)));
7072 putFile8Bit(file, change->has_action);
7073 putFile8Bit(file, change->action_type);
7074 putFile8Bit(file, change->action_mode);
7075 putFile16BitBE(file, change->action_arg);
7077 /* ... bits 32 - 39 of "has_event[]" (not nice, but downward compatible) */
7079 for (j = 32; j < NUM_CHANGE_EVENTS; j++)
7080 if (change->has_event[j])
7081 event_bits |= (1 << (j - 32));
7082 putFile8Bit(file, event_bits);
7087 #if ENABLE_HISTORIC_CHUNKS
7088 static void SaveLevel_GRP1(FILE *file, struct LevelInfo *level, int element)
7090 struct ElementInfo *ei = &element_info[element];
7091 struct ElementGroupInfo *group = ei->group;
7094 putFile16BitBE(file, element);
7096 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
7097 putFile8Bit(file, ei->description[i]);
7099 putFile8Bit(file, group->num_elements);
7101 putFile8Bit(file, ei->use_gfx_element);
7102 putFile16BitBE(file, ei->gfx_element_initial);
7104 putFile8Bit(file, group->choice_mode);
7106 /* some free bytes for future values and padding */
7107 WriteUnusedBytesToFile(file, 3);
7109 for (i = 0; i < MAX_ELEMENTS_IN_GROUP; i++)
7110 putFile16BitBE(file, group->element[i]);
7114 static int SaveLevel_MicroChunk(FILE *file, struct LevelFileConfigInfo *entry,
7115 boolean write_element)
7117 int save_type = entry->save_type;
7118 int data_type = entry->data_type;
7119 int conf_type = entry->conf_type;
7120 int byte_mask = conf_type & CONF_MASK_BYTES;
7121 int element = entry->element;
7122 int default_value = entry->default_value;
7124 boolean modified = FALSE;
7126 if (byte_mask != CONF_MASK_MULTI_BYTES)
7128 void *value_ptr = entry->value;
7129 int value = (data_type == TYPE_BOOLEAN ? *(boolean *)value_ptr :
7132 /* check if any settings have been modified before saving them */
7133 if (value != default_value)
7136 /* do not save if explicitly told or if unmodified default settings */
7137 if ((save_type == SAVE_CONF_NEVER) ||
7138 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
7142 num_bytes += putFile16BitBE(file, element);
7144 num_bytes += putFile8Bit(file, conf_type);
7145 num_bytes += (byte_mask == CONF_MASK_1_BYTE ? putFile8Bit (file, value) :
7146 byte_mask == CONF_MASK_2_BYTE ? putFile16BitBE(file, value) :
7147 byte_mask == CONF_MASK_4_BYTE ? putFile32BitBE(file, value) :
7150 else if (data_type == TYPE_STRING)
7152 char *default_string = entry->default_string;
7153 char *string = (char *)(entry->value);
7154 int string_length = strlen(string);
7157 /* check if any settings have been modified before saving them */
7158 if (!strEqual(string, default_string))
7161 /* do not save if explicitly told or if unmodified default settings */
7162 if ((save_type == SAVE_CONF_NEVER) ||
7163 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
7167 num_bytes += putFile16BitBE(file, element);
7169 num_bytes += putFile8Bit(file, conf_type);
7170 num_bytes += putFile16BitBE(file, string_length);
7172 for (i = 0; i < string_length; i++)
7173 num_bytes += putFile8Bit(file, string[i]);
7175 else if (data_type == TYPE_ELEMENT_LIST)
7177 int *element_array = (int *)(entry->value);
7178 int num_elements = *(int *)(entry->num_entities);
7181 /* check if any settings have been modified before saving them */
7182 for (i = 0; i < num_elements; i++)
7183 if (element_array[i] != default_value)
7186 /* do not save if explicitly told or if unmodified default settings */
7187 if ((save_type == SAVE_CONF_NEVER) ||
7188 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
7192 num_bytes += putFile16BitBE(file, element);
7194 num_bytes += putFile8Bit(file, conf_type);
7195 num_bytes += putFile16BitBE(file, num_elements * CONF_ELEMENT_NUM_BYTES);
7197 for (i = 0; i < num_elements; i++)
7198 num_bytes += putFile16BitBE(file, element_array[i]);
7200 else if (data_type == TYPE_CONTENT_LIST)
7202 struct Content *content = (struct Content *)(entry->value);
7203 int num_contents = *(int *)(entry->num_entities);
7206 /* check if any settings have been modified before saving them */
7207 for (i = 0; i < num_contents; i++)
7208 for (y = 0; y < 3; y++)
7209 for (x = 0; x < 3; x++)
7210 if (content[i].e[x][y] != default_value)
7213 /* do not save if explicitly told or if unmodified default settings */
7214 if ((save_type == SAVE_CONF_NEVER) ||
7215 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
7219 num_bytes += putFile16BitBE(file, element);
7221 num_bytes += putFile8Bit(file, conf_type);
7222 num_bytes += putFile16BitBE(file, num_contents * CONF_CONTENT_NUM_BYTES);
7224 for (i = 0; i < num_contents; i++)
7225 for (y = 0; y < 3; y++)
7226 for (x = 0; x < 3; x++)
7227 num_bytes += putFile16BitBE(file, content[i].e[x][y]);
7233 static int SaveLevel_INFO(FILE *file, struct LevelInfo *level)
7238 li = *level; /* copy level data into temporary buffer */
7240 for (i = 0; chunk_config_INFO[i].data_type != -1; i++)
7241 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_INFO[i], FALSE);
7246 static int SaveLevel_ELEM(FILE *file, struct LevelInfo *level)
7251 li = *level; /* copy level data into temporary buffer */
7253 for (i = 0; chunk_config_ELEM[i].data_type != -1; i++)
7254 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_ELEM[i], TRUE);
7259 static int SaveLevel_NOTE(FILE *file, struct LevelInfo *level, int element)
7261 int envelope_nr = element - EL_ENVELOPE_1;
7265 chunk_size += putFile16BitBE(file, element);
7267 /* copy envelope data into temporary buffer */
7268 xx_envelope = level->envelope[envelope_nr];
7270 for (i = 0; chunk_config_NOTE[i].data_type != -1; i++)
7271 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_NOTE[i], FALSE);
7276 static int SaveLevel_CUSX(FILE *file, struct LevelInfo *level, int element)
7278 struct ElementInfo *ei = &element_info[element];
7282 chunk_size += putFile16BitBE(file, element);
7284 xx_ei = *ei; /* copy element data into temporary buffer */
7286 /* set default description string for this specific element */
7287 strcpy(xx_default_description, getDefaultElementDescription(ei));
7289 for (i = 0; chunk_config_CUSX_base[i].data_type != -1; i++)
7290 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_CUSX_base[i], FALSE);
7292 for (i = 0; i < ei->num_change_pages; i++)
7294 struct ElementChangeInfo *change = &ei->change_page[i];
7296 xx_current_change_page = i;
7298 xx_change = *change; /* copy change data into temporary buffer */
7301 setEventBitsFromEventFlags(change);
7303 for (j = 0; chunk_config_CUSX_change[j].data_type != -1; j++)
7304 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_CUSX_change[j],
7311 static int SaveLevel_GRPX(FILE *file, struct LevelInfo *level, int element)
7313 struct ElementInfo *ei = &element_info[element];
7314 struct ElementGroupInfo *group = ei->group;
7318 chunk_size += putFile16BitBE(file, element);
7320 xx_ei = *ei; /* copy element data into temporary buffer */
7321 xx_group = *group; /* copy group data into temporary buffer */
7323 /* set default description string for this specific element */
7324 strcpy(xx_default_description, getDefaultElementDescription(ei));
7326 for (i = 0; chunk_config_GRPX[i].data_type != -1; i++)
7327 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_GRPX[i], FALSE);
7332 static void SaveLevelFromFilename(struct LevelInfo *level, char *filename,
7333 boolean save_as_template)
7339 if (!(file = fopen(filename, MODE_WRITE)))
7341 Error(ERR_WARN, "cannot save level file '%s'", filename);
7345 level->file_version = FILE_VERSION_ACTUAL;
7346 level->game_version = GAME_VERSION_ACTUAL;
7348 level->creation_date = getCurrentDate();
7350 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
7351 putFileChunkBE(file, "CAVE", CHUNK_SIZE_NONE);
7353 chunk_size = SaveLevel_VERS(NULL, level);
7354 putFileChunkBE(file, "VERS", chunk_size);
7355 SaveLevel_VERS(file, level);
7357 chunk_size = SaveLevel_DATE(NULL, level);
7358 putFileChunkBE(file, "DATE", chunk_size);
7359 SaveLevel_DATE(file, level);
7361 chunk_size = SaveLevel_NAME(NULL, level);
7362 putFileChunkBE(file, "NAME", chunk_size);
7363 SaveLevel_NAME(file, level);
7365 chunk_size = SaveLevel_AUTH(NULL, level);
7366 putFileChunkBE(file, "AUTH", chunk_size);
7367 SaveLevel_AUTH(file, level);
7369 chunk_size = SaveLevel_INFO(NULL, level);
7370 putFileChunkBE(file, "INFO", chunk_size);
7371 SaveLevel_INFO(file, level);
7373 chunk_size = SaveLevel_BODY(NULL, level);
7374 putFileChunkBE(file, "BODY", chunk_size);
7375 SaveLevel_BODY(file, level);
7377 chunk_size = SaveLevel_ELEM(NULL, level);
7378 if (chunk_size > LEVEL_CHUNK_ELEM_UNCHANGED) /* save if changed */
7380 putFileChunkBE(file, "ELEM", chunk_size);
7381 SaveLevel_ELEM(file, level);
7384 for (i = 0; i < NUM_ENVELOPES; i++)
7386 int element = EL_ENVELOPE_1 + i;
7388 chunk_size = SaveLevel_NOTE(NULL, level, element);
7389 if (chunk_size > LEVEL_CHUNK_NOTE_UNCHANGED) /* save if changed */
7391 putFileChunkBE(file, "NOTE", chunk_size);
7392 SaveLevel_NOTE(file, level, element);
7396 /* if not using template level, check for non-default custom/group elements */
7397 if (!level->use_custom_template || save_as_template)
7399 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7401 int element = EL_CUSTOM_START + i;
7403 chunk_size = SaveLevel_CUSX(NULL, level, element);
7404 if (chunk_size > LEVEL_CHUNK_CUSX_UNCHANGED) /* save if changed */
7406 putFileChunkBE(file, "CUSX", chunk_size);
7407 SaveLevel_CUSX(file, level, element);
7411 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
7413 int element = EL_GROUP_START + i;
7415 chunk_size = SaveLevel_GRPX(NULL, level, element);
7416 if (chunk_size > LEVEL_CHUNK_GRPX_UNCHANGED) /* save if changed */
7418 putFileChunkBE(file, "GRPX", chunk_size);
7419 SaveLevel_GRPX(file, level, element);
7426 SetFilePermissions(filename, PERMS_PRIVATE);
7429 void SaveLevel(int nr)
7431 char *filename = getDefaultLevelFilename(nr);
7433 SaveLevelFromFilename(&level, filename, FALSE);
7436 void SaveLevelTemplate()
7438 char *filename = getLocalLevelTemplateFilename();
7440 SaveLevelFromFilename(&level, filename, TRUE);
7443 boolean SaveLevelChecked(int nr)
7445 char *filename = getDefaultLevelFilename(nr);
7446 boolean new_level = !fileExists(filename);
7447 boolean level_saved = FALSE;
7449 if (new_level || Request("Save this level and kill the old?", REQ_ASK))
7454 Request("Level saved!", REQ_CONFIRM);
7462 void DumpLevel(struct LevelInfo *level)
7464 if (level->no_level_file || level->no_valid_file)
7466 Error(ERR_WARN, "cannot dump -- no valid level file found");
7472 Print("Level xxx (file version %08d, game version %08d)\n",
7473 level->file_version, level->game_version);
7476 Print("Level author: '%s'\n", level->author);
7477 Print("Level title: '%s'\n", level->name);
7479 Print("Playfield size: %d x %d\n", level->fieldx, level->fieldy);
7481 Print("Level time: %d seconds\n", level->time);
7482 Print("Gems needed: %d\n", level->gems_needed);
7484 Print("Time for magic wall: %d seconds\n", level->time_magic_wall);
7485 Print("Time for wheel: %d seconds\n", level->time_wheel);
7486 Print("Time for light: %d seconds\n", level->time_light);
7487 Print("Time for timegate: %d seconds\n", level->time_timegate);
7489 Print("Amoeba speed: %d\n", level->amoeba_speed);
7492 Print("EM style slippery gems: %s\n", (level->em_slippery_gems ? "yes" : "no"));
7493 Print("Player blocks last field: %s\n", (level->block_last_field ? "yes" : "no"));
7494 Print("SP player blocks last field: %s\n", (level->sp_block_last_field ? "yes" : "no"));
7495 Print("use spring bug: %s\n", (level->use_spring_bug ? "yes" : "no"));
7496 Print("use step counter: %s\n", (level->use_step_counter ? "yes" : "no"));
7502 /* ========================================================================= */
7503 /* tape file functions */
7504 /* ========================================================================= */
7506 static void setTapeInfoToDefaults()
7510 /* always start with reliable default values (empty tape) */
7513 /* default values (also for pre-1.2 tapes) with only the first player */
7514 tape.player_participates[0] = TRUE;
7515 for (i = 1; i < MAX_PLAYERS; i++)
7516 tape.player_participates[i] = FALSE;
7518 /* at least one (default: the first) player participates in every tape */
7519 tape.num_participating_players = 1;
7521 tape.level_nr = level_nr;
7523 tape.changed = FALSE;
7525 tape.recording = FALSE;
7526 tape.playing = FALSE;
7527 tape.pausing = FALSE;
7529 tape.no_valid_file = FALSE;
7532 static int LoadTape_VERS(File *file, int chunk_size, struct TapeInfo *tape)
7534 tape->file_version = getFileVersion(file);
7535 tape->game_version = getFileVersion(file);
7540 static int LoadTape_HEAD(File *file, int chunk_size, struct TapeInfo *tape)
7544 tape->random_seed = getFile32BitBE(file);
7545 tape->date = getFile32BitBE(file);
7546 tape->length = getFile32BitBE(file);
7548 /* read header fields that are new since version 1.2 */
7549 if (tape->file_version >= FILE_VERSION_1_2)
7551 byte store_participating_players = getFile8Bit(file);
7554 /* since version 1.2, tapes store which players participate in the tape */
7555 tape->num_participating_players = 0;
7556 for (i = 0; i < MAX_PLAYERS; i++)
7558 tape->player_participates[i] = FALSE;
7560 if (store_participating_players & (1 << i))
7562 tape->player_participates[i] = TRUE;
7563 tape->num_participating_players++;
7567 tape->use_mouse = (getFile8Bit(file) == 1 ? TRUE : FALSE);
7569 ReadUnusedBytesFromFile(file, TAPE_CHUNK_HEAD_UNUSED);
7571 engine_version = getFileVersion(file);
7572 if (engine_version > 0)
7573 tape->engine_version = engine_version;
7575 tape->engine_version = tape->game_version;
7581 static int LoadTape_INFO(File *file, int chunk_size, struct TapeInfo *tape)
7583 int level_identifier_size;
7586 level_identifier_size = getFile16BitBE(file);
7588 tape->level_identifier =
7589 checked_realloc(tape->level_identifier, level_identifier_size);
7591 for (i = 0; i < level_identifier_size; i++)
7592 tape->level_identifier[i] = getFile8Bit(file);
7594 tape->level_nr = getFile16BitBE(file);
7596 chunk_size = 2 + level_identifier_size + 2;
7601 static int LoadTape_BODY(File *file, int chunk_size, struct TapeInfo *tape)
7605 (tape->use_mouse ? 3 : tape->num_participating_players) + 1;
7606 int chunk_size_expected = tape_pos_size * tape->length;
7608 if (chunk_size_expected != chunk_size)
7610 ReadUnusedBytesFromFile(file, chunk_size);
7611 return chunk_size_expected;
7614 for (i = 0; i < tape->length; i++)
7616 if (i >= MAX_TAPE_LEN)
7618 Error(ERR_WARN, "tape truncated -- size exceeds maximum tape size %d",
7621 // tape too large; read and ignore remaining tape data from this chunk
7622 for (;i < tape->length; i++)
7623 ReadUnusedBytesFromFile(file, tape->num_participating_players + 1);
7628 if (tape->use_mouse)
7630 tape->pos[i].action[TAPE_ACTION_LX] = getFile8Bit(file);
7631 tape->pos[i].action[TAPE_ACTION_LY] = getFile8Bit(file);
7632 tape->pos[i].action[TAPE_ACTION_BUTTON] = getFile8Bit(file);
7634 tape->pos[i].action[TAPE_ACTION_UNUSED] = 0;
7638 for (j = 0; j < MAX_PLAYERS; j++)
7640 tape->pos[i].action[j] = MV_NONE;
7642 if (tape->player_participates[j])
7643 tape->pos[i].action[j] = getFile8Bit(file);
7647 tape->pos[i].delay = getFile8Bit(file);
7649 if (tape->file_version == FILE_VERSION_1_0)
7651 /* eliminate possible diagonal moves in old tapes */
7652 /* this is only for backward compatibility */
7654 byte joy_dir[4] = { JOY_LEFT, JOY_RIGHT, JOY_UP, JOY_DOWN };
7655 byte action = tape->pos[i].action[0];
7656 int k, num_moves = 0;
7658 for (k = 0; k<4; k++)
7660 if (action & joy_dir[k])
7662 tape->pos[i + num_moves].action[0] = joy_dir[k];
7664 tape->pos[i + num_moves].delay = 0;
7673 tape->length += num_moves;
7676 else if (tape->file_version < FILE_VERSION_2_0)
7678 /* convert pre-2.0 tapes to new tape format */
7680 if (tape->pos[i].delay > 1)
7683 tape->pos[i + 1] = tape->pos[i];
7684 tape->pos[i + 1].delay = 1;
7687 for (j = 0; j < MAX_PLAYERS; j++)
7688 tape->pos[i].action[j] = MV_NONE;
7689 tape->pos[i].delay--;
7696 if (checkEndOfFile(file))
7700 if (i != tape->length)
7701 chunk_size = tape_pos_size * i;
7706 void LoadTape_SokobanSolution(char *filename)
7709 int move_delay = TILESIZE / level.initial_player_stepsize[0];
7711 if (!(file = openFile(filename, MODE_READ)))
7713 tape.no_valid_file = TRUE;
7718 while (!checkEndOfFile(file))
7720 unsigned char c = getByteFromFile(file);
7722 if (checkEndOfFile(file))
7729 tape.pos[tape.length].action[0] = MV_UP;
7730 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
7736 tape.pos[tape.length].action[0] = MV_DOWN;
7737 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
7743 tape.pos[tape.length].action[0] = MV_LEFT;
7744 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
7750 tape.pos[tape.length].action[0] = MV_RIGHT;
7751 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
7759 /* ignore white-space characters */
7763 tape.no_valid_file = TRUE;
7765 Error(ERR_WARN, "unsupported Sokoban solution file '%s' ['%d']", filename, c);
7773 if (tape.no_valid_file)
7776 tape.length_frames = GetTapeLengthFrames();
7777 tape.length_seconds = GetTapeLengthSeconds();
7780 void LoadTapeFromFilename(char *filename)
7782 char cookie[MAX_LINE_LEN];
7783 char chunk_name[CHUNK_ID_LEN + 1];
7787 /* always start with reliable default values */
7788 setTapeInfoToDefaults();
7790 if (strSuffix(filename, ".sln"))
7792 LoadTape_SokobanSolution(filename);
7797 if (!(file = openFile(filename, MODE_READ)))
7799 tape.no_valid_file = TRUE;
7804 getFileChunkBE(file, chunk_name, NULL);
7805 if (strEqual(chunk_name, "RND1"))
7807 getFile32BitBE(file); /* not used */
7809 getFileChunkBE(file, chunk_name, NULL);
7810 if (!strEqual(chunk_name, "TAPE"))
7812 tape.no_valid_file = TRUE;
7814 Error(ERR_WARN, "unknown format of tape file '%s'", filename);
7821 else /* check for pre-2.0 file format with cookie string */
7823 strcpy(cookie, chunk_name);
7824 if (getStringFromFile(file, &cookie[4], MAX_LINE_LEN - 4) == NULL)
7826 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
7827 cookie[strlen(cookie) - 1] = '\0';
7829 if (!checkCookieString(cookie, TAPE_COOKIE_TMPL))
7831 tape.no_valid_file = TRUE;
7833 Error(ERR_WARN, "unknown format of tape file '%s'", filename);
7840 if ((tape.file_version = getFileVersionFromCookieString(cookie)) == -1)
7842 tape.no_valid_file = TRUE;
7844 Error(ERR_WARN, "unsupported version of tape file '%s'", filename);
7851 /* pre-2.0 tape files have no game version, so use file version here */
7852 tape.game_version = tape.file_version;
7855 if (tape.file_version < FILE_VERSION_1_2)
7857 /* tape files from versions before 1.2.0 without chunk structure */
7858 LoadTape_HEAD(file, TAPE_CHUNK_HEAD_SIZE, &tape);
7859 LoadTape_BODY(file, 2 * tape.length, &tape);
7867 int (*loader)(File *, int, struct TapeInfo *);
7871 { "VERS", TAPE_CHUNK_VERS_SIZE, LoadTape_VERS },
7872 { "HEAD", TAPE_CHUNK_HEAD_SIZE, LoadTape_HEAD },
7873 { "INFO", -1, LoadTape_INFO },
7874 { "BODY", -1, LoadTape_BODY },
7878 while (getFileChunkBE(file, chunk_name, &chunk_size))
7882 while (chunk_info[i].name != NULL &&
7883 !strEqual(chunk_name, chunk_info[i].name))
7886 if (chunk_info[i].name == NULL)
7888 Error(ERR_WARN, "unknown chunk '%s' in tape file '%s'",
7889 chunk_name, filename);
7890 ReadUnusedBytesFromFile(file, chunk_size);
7892 else if (chunk_info[i].size != -1 &&
7893 chunk_info[i].size != chunk_size)
7895 Error(ERR_WARN, "wrong size (%d) of chunk '%s' in tape file '%s'",
7896 chunk_size, chunk_name, filename);
7897 ReadUnusedBytesFromFile(file, chunk_size);
7901 /* call function to load this tape chunk */
7902 int chunk_size_expected =
7903 (chunk_info[i].loader)(file, chunk_size, &tape);
7905 /* the size of some chunks cannot be checked before reading other
7906 chunks first (like "HEAD" and "BODY") that contain some header
7907 information, so check them here */
7908 if (chunk_size_expected != chunk_size)
7910 Error(ERR_WARN, "wrong size (%d) of chunk '%s' in tape file '%s'",
7911 chunk_size, chunk_name, filename);
7919 tape.length_frames = GetTapeLengthFrames();
7920 tape.length_seconds = GetTapeLengthSeconds();
7923 printf("::: tape file version: %d\n", tape.file_version);
7924 printf("::: tape game version: %d\n", tape.game_version);
7925 printf("::: tape engine version: %d\n", tape.engine_version);
7929 void LoadTape(int nr)
7931 char *filename = getTapeFilename(nr);
7933 LoadTapeFromFilename(filename);
7936 void LoadSolutionTape(int nr)
7938 char *filename = getSolutionTapeFilename(nr);
7940 LoadTapeFromFilename(filename);
7942 if (TAPE_IS_EMPTY(tape) &&
7943 level.game_engine_type == GAME_ENGINE_TYPE_SP &&
7944 level.native_sp_level->demo.is_available)
7945 CopyNativeTape_SP_to_RND(&level);
7948 static void SaveTape_VERS(FILE *file, struct TapeInfo *tape)
7950 putFileVersion(file, tape->file_version);
7951 putFileVersion(file, tape->game_version);
7954 static void SaveTape_HEAD(FILE *file, struct TapeInfo *tape)
7957 byte store_participating_players = 0;
7959 /* set bits for participating players for compact storage */
7960 for (i = 0; i < MAX_PLAYERS; i++)
7961 if (tape->player_participates[i])
7962 store_participating_players |= (1 << i);
7964 putFile32BitBE(file, tape->random_seed);
7965 putFile32BitBE(file, tape->date);
7966 putFile32BitBE(file, tape->length);
7968 putFile8Bit(file, store_participating_players);
7970 putFile8Bit(file, (tape->use_mouse ? 1 : 0));
7972 /* unused bytes not at the end here for 4-byte alignment of engine_version */
7973 WriteUnusedBytesToFile(file, TAPE_CHUNK_HEAD_UNUSED);
7975 putFileVersion(file, tape->engine_version);
7978 static void SaveTape_INFO(FILE *file, struct TapeInfo *tape)
7980 int level_identifier_size = strlen(tape->level_identifier) + 1;
7983 putFile16BitBE(file, level_identifier_size);
7985 for (i = 0; i < level_identifier_size; i++)
7986 putFile8Bit(file, tape->level_identifier[i]);
7988 putFile16BitBE(file, tape->level_nr);
7991 static void SaveTape_BODY(FILE *file, struct TapeInfo *tape)
7995 for (i = 0; i < tape->length; i++)
7997 if (tape->use_mouse)
7999 putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_LX]);
8000 putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_LY]);
8001 putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_BUTTON]);
8005 for (j = 0; j < MAX_PLAYERS; j++)
8006 if (tape->player_participates[j])
8007 putFile8Bit(file, tape->pos[i].action[j]);
8010 putFile8Bit(file, tape->pos[i].delay);
8014 void SaveTape(int nr)
8016 char *filename = getTapeFilename(nr);
8018 int num_participating_players = 0;
8020 int info_chunk_size;
8021 int body_chunk_size;
8024 InitTapeDirectory(leveldir_current->subdir);
8026 if (!(file = fopen(filename, MODE_WRITE)))
8028 Error(ERR_WARN, "cannot save level recording file '%s'", filename);
8032 tape.file_version = FILE_VERSION_ACTUAL;
8033 tape.game_version = GAME_VERSION_ACTUAL;
8035 /* count number of participating players */
8036 for (i = 0; i < MAX_PLAYERS; i++)
8037 if (tape.player_participates[i])
8038 num_participating_players++;
8040 tape_pos_size = (tape.use_mouse ? 3 : num_participating_players) + 1;
8042 info_chunk_size = 2 + (strlen(tape.level_identifier) + 1) + 2;
8043 body_chunk_size = tape_pos_size * tape.length;
8045 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
8046 putFileChunkBE(file, "TAPE", CHUNK_SIZE_NONE);
8048 putFileChunkBE(file, "VERS", TAPE_CHUNK_VERS_SIZE);
8049 SaveTape_VERS(file, &tape);
8051 putFileChunkBE(file, "HEAD", TAPE_CHUNK_HEAD_SIZE);
8052 SaveTape_HEAD(file, &tape);
8054 putFileChunkBE(file, "INFO", info_chunk_size);
8055 SaveTape_INFO(file, &tape);
8057 putFileChunkBE(file, "BODY", body_chunk_size);
8058 SaveTape_BODY(file, &tape);
8062 SetFilePermissions(filename, PERMS_PRIVATE);
8064 tape.changed = FALSE;
8067 boolean SaveTapeChecked(int nr)
8069 char *filename = getTapeFilename(nr);
8070 boolean new_tape = !fileExists(filename);
8071 boolean tape_saved = FALSE;
8073 if (new_tape || Request("Replace old tape?", REQ_ASK))
8078 Request("Tape saved!", REQ_CONFIRM);
8086 void DumpTape(struct TapeInfo *tape)
8088 int tape_frame_counter;
8091 if (tape->no_valid_file)
8093 Error(ERR_WARN, "cannot dump -- no valid tape file found");
8099 Print("Tape of Level %03d (file version %08d, game version %08d)\n",
8100 tape->level_nr, tape->file_version, tape->game_version);
8101 Print(" (effective engine version %08d)\n",
8102 tape->engine_version);
8103 Print("Level series identifier: '%s'\n", tape->level_identifier);
8106 tape_frame_counter = 0;
8108 for (i = 0; i < tape->length; i++)
8110 if (i >= MAX_TAPE_LEN)
8115 for (j = 0; j < MAX_PLAYERS; j++)
8117 if (tape->player_participates[j])
8119 int action = tape->pos[i].action[j];
8121 Print("%d:%02x ", j, action);
8122 Print("[%c%c%c%c|%c%c] - ",
8123 (action & JOY_LEFT ? '<' : ' '),
8124 (action & JOY_RIGHT ? '>' : ' '),
8125 (action & JOY_UP ? '^' : ' '),
8126 (action & JOY_DOWN ? 'v' : ' '),
8127 (action & JOY_BUTTON_1 ? '1' : ' '),
8128 (action & JOY_BUTTON_2 ? '2' : ' '));
8132 Print("(%03d) ", tape->pos[i].delay);
8133 Print("[%05d]\n", tape_frame_counter);
8135 tape_frame_counter += tape->pos[i].delay;
8142 /* ========================================================================= */
8143 /* score file functions */
8144 /* ========================================================================= */
8146 void LoadScore(int nr)
8149 char *filename = getScoreFilename(nr);
8150 char cookie[MAX_LINE_LEN];
8151 char line[MAX_LINE_LEN];
8155 /* always start with reliable default values */
8156 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
8158 strcpy(highscore[i].Name, EMPTY_PLAYER_NAME);
8159 highscore[i].Score = 0;
8162 if (!(file = fopen(filename, MODE_READ)))
8165 /* check file identifier */
8166 if (fgets(cookie, MAX_LINE_LEN, file) == NULL)
8168 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
8169 cookie[strlen(cookie) - 1] = '\0';
8171 if (!checkCookieString(cookie, SCORE_COOKIE))
8173 Error(ERR_WARN, "unknown format of score file '%s'", filename);
8178 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
8180 if (fscanf(file, "%d", &highscore[i].Score) == EOF)
8181 Error(ERR_WARN, "fscanf() failed; %s", strerror(errno));
8182 if (fgets(line, MAX_LINE_LEN, file) == NULL)
8185 if (strlen(line) > 0 && line[strlen(line) - 1] == '\n')
8186 line[strlen(line) - 1] = '\0';
8188 for (line_ptr = line; *line_ptr; line_ptr++)
8190 if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != '\0')
8192 strncpy(highscore[i].Name, line_ptr, MAX_PLAYER_NAME_LEN);
8193 highscore[i].Name[MAX_PLAYER_NAME_LEN] = '\0';
8202 void SaveScore(int nr)
8205 int permissions = (program.global_scores ? PERMS_PUBLIC : PERMS_PRIVATE);
8206 char *filename = getScoreFilename(nr);
8209 InitScoreDirectory(leveldir_current->subdir);
8211 if (!(file = fopen(filename, MODE_WRITE)))
8213 Error(ERR_WARN, "cannot save score for level %d", nr);
8217 fprintf(file, "%s\n\n", SCORE_COOKIE);
8219 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
8220 fprintf(file, "%d %s\n", highscore[i].Score, highscore[i].Name);
8224 SetFilePermissions(filename, permissions);
8228 /* ========================================================================= */
8229 /* setup file functions */
8230 /* ========================================================================= */
8232 #define TOKEN_STR_PLAYER_PREFIX "player_"
8235 #define SETUP_TOKEN_PLAYER_NAME 0
8236 #define SETUP_TOKEN_SOUND 1
8237 #define SETUP_TOKEN_SOUND_LOOPS 2
8238 #define SETUP_TOKEN_SOUND_MUSIC 3
8239 #define SETUP_TOKEN_SOUND_SIMPLE 4
8240 #define SETUP_TOKEN_TOONS 5
8241 #define SETUP_TOKEN_SCROLL_DELAY 6
8242 #define SETUP_TOKEN_SCROLL_DELAY_VALUE 7
8243 #define SETUP_TOKEN_ENGINE_SNAPSHOT_MODE 8
8244 #define SETUP_TOKEN_ENGINE_SNAPSHOT_MEMORY 9
8245 #define SETUP_TOKEN_FADE_SCREENS 10
8246 #define SETUP_TOKEN_AUTORECORD 11
8247 #define SETUP_TOKEN_SHOW_TITLESCREEN 12
8248 #define SETUP_TOKEN_QUICK_DOORS 13
8249 #define SETUP_TOKEN_TEAM_MODE 14
8250 #define SETUP_TOKEN_HANDICAP 15
8251 #define SETUP_TOKEN_SKIP_LEVELS 16
8252 #define SETUP_TOKEN_INCREMENT_LEVELS 17
8253 #define SETUP_TOKEN_TIME_LIMIT 18
8254 #define SETUP_TOKEN_FULLSCREEN 19
8255 #define SETUP_TOKEN_WINDOW_SCALING_PERCENT 20
8256 #define SETUP_TOKEN_WINDOW_SCALING_QUALITY 21
8257 #define SETUP_TOKEN_SCREEN_RENDERING_MODE 22
8258 #define SETUP_TOKEN_ASK_ON_ESCAPE 23
8259 #define SETUP_TOKEN_ASK_ON_ESCAPE_EDITOR 24
8260 #define SETUP_TOKEN_QUICK_SWITCH 25
8261 #define SETUP_TOKEN_INPUT_ON_FOCUS 26
8262 #define SETUP_TOKEN_PREFER_AGA_GRAPHICS 27
8263 #define SETUP_TOKEN_GAME_FRAME_DELAY 28
8264 #define SETUP_TOKEN_SP_SHOW_BORDER_ELEMENTS 29
8265 #define SETUP_TOKEN_SMALL_GAME_GRAPHICS 30
8266 #define SETUP_TOKEN_SHOW_SNAPSHOT_BUTTONS 31
8267 #define SETUP_TOKEN_GRAPHICS_SET 32
8268 #define SETUP_TOKEN_SOUNDS_SET 33
8269 #define SETUP_TOKEN_MUSIC_SET 34
8270 #define SETUP_TOKEN_OVERRIDE_LEVEL_GRAPHICS 35
8271 #define SETUP_TOKEN_OVERRIDE_LEVEL_SOUNDS 36
8272 #define SETUP_TOKEN_OVERRIDE_LEVEL_MUSIC 37
8273 #define SETUP_TOKEN_VOLUME_SIMPLE 38
8274 #define SETUP_TOKEN_VOLUME_LOOPS 39
8275 #define SETUP_TOKEN_VOLUME_MUSIC 40
8276 #define SETUP_TOKEN_TOUCH_CONTROL_TYPE 41
8277 #define SETUP_TOKEN_TOUCH_MOVE_DISTANCE 42
8278 #define SETUP_TOKEN_TOUCH_DROP_DISTANCE 43
8280 #define NUM_GLOBAL_SETUP_TOKENS 44
8283 #define SETUP_TOKEN_AUTO_EDITOR_ZOOM_TILESIZE 0
8285 #define NUM_AUTO_SETUP_TOKENS 1
8288 #define SETUP_TOKEN_EDITOR_EL_CLASSIC 0
8289 #define SETUP_TOKEN_EDITOR_EL_CUSTOM 1
8290 #define SETUP_TOKEN_EDITOR_EL_USER_DEFINED 2
8291 #define SETUP_TOKEN_EDITOR_EL_DYNAMIC 3
8292 #define SETUP_TOKEN_EDITOR_EL_HEADLINES 4
8293 #define SETUP_TOKEN_EDITOR_SHOW_ELEMENT_TOKEN 5
8295 #define NUM_EDITOR_SETUP_TOKENS 6
8297 /* editor cascade setup */
8298 #define SETUP_TOKEN_EDITOR_CASCADE_BD 0
8299 #define SETUP_TOKEN_EDITOR_CASCADE_EM 1
8300 #define SETUP_TOKEN_EDITOR_CASCADE_EMC 2
8301 #define SETUP_TOKEN_EDITOR_CASCADE_RND 3
8302 #define SETUP_TOKEN_EDITOR_CASCADE_SB 4
8303 #define SETUP_TOKEN_EDITOR_CASCADE_SP 5
8304 #define SETUP_TOKEN_EDITOR_CASCADE_DC 6
8305 #define SETUP_TOKEN_EDITOR_CASCADE_DX 7
8306 #define SETUP_TOKEN_EDITOR_CASCADE_TEXT 8
8307 #define SETUP_TOKEN_EDITOR_CASCADE_STEELTEXT 9
8308 #define SETUP_TOKEN_EDITOR_CASCADE_CE 10
8309 #define SETUP_TOKEN_EDITOR_CASCADE_GE 11
8310 #define SETUP_TOKEN_EDITOR_CASCADE_REF 12
8311 #define SETUP_TOKEN_EDITOR_CASCADE_USER 13
8312 #define SETUP_TOKEN_EDITOR_CASCADE_DYNAMIC 14
8314 #define NUM_EDITOR_CASCADE_SETUP_TOKENS 15
8316 /* shortcut setup */
8317 #define SETUP_TOKEN_SHORTCUT_SAVE_GAME 0
8318 #define SETUP_TOKEN_SHORTCUT_LOAD_GAME 1
8319 #define SETUP_TOKEN_SHORTCUT_TOGGLE_PAUSE 2
8320 #define SETUP_TOKEN_SHORTCUT_FOCUS_PLAYER_1 3
8321 #define SETUP_TOKEN_SHORTCUT_FOCUS_PLAYER_2 4
8322 #define SETUP_TOKEN_SHORTCUT_FOCUS_PLAYER_3 5
8323 #define SETUP_TOKEN_SHORTCUT_FOCUS_PLAYER_4 6
8324 #define SETUP_TOKEN_SHORTCUT_FOCUS_PLAYER_ALL 7
8325 #define SETUP_TOKEN_SHORTCUT_TAPE_EJECT 8
8326 #define SETUP_TOKEN_SHORTCUT_TAPE_EXTRA 9
8327 #define SETUP_TOKEN_SHORTCUT_TAPE_STOP 10
8328 #define SETUP_TOKEN_SHORTCUT_TAPE_PAUSE 11
8329 #define SETUP_TOKEN_SHORTCUT_TAPE_RECORD 12
8330 #define SETUP_TOKEN_SHORTCUT_TAPE_PLAY 13
8331 #define SETUP_TOKEN_SHORTCUT_SOUND_SIMPLE 14
8332 #define SETUP_TOKEN_SHORTCUT_SOUND_LOOPS 15
8333 #define SETUP_TOKEN_SHORTCUT_SOUND_MUSIC 16
8334 #define SETUP_TOKEN_SHORTCUT_SNAP_LEFT 17
8335 #define SETUP_TOKEN_SHORTCUT_SNAP_RIGHT 18
8336 #define SETUP_TOKEN_SHORTCUT_SNAP_UP 19
8337 #define SETUP_TOKEN_SHORTCUT_SNAP_DOWN 20
8339 #define NUM_SHORTCUT_SETUP_TOKENS 21
8342 #define SETUP_TOKEN_PLAYER_USE_JOYSTICK 0
8343 #define SETUP_TOKEN_PLAYER_JOY_DEVICE_NAME 1
8344 #define SETUP_TOKEN_PLAYER_JOY_XLEFT 2
8345 #define SETUP_TOKEN_PLAYER_JOY_XMIDDLE 3
8346 #define SETUP_TOKEN_PLAYER_JOY_XRIGHT 4
8347 #define SETUP_TOKEN_PLAYER_JOY_YUPPER 5
8348 #define SETUP_TOKEN_PLAYER_JOY_YMIDDLE 6
8349 #define SETUP_TOKEN_PLAYER_JOY_YLOWER 7
8350 #define SETUP_TOKEN_PLAYER_JOY_SNAP 8
8351 #define SETUP_TOKEN_PLAYER_JOY_DROP 9
8352 #define SETUP_TOKEN_PLAYER_KEY_LEFT 10
8353 #define SETUP_TOKEN_PLAYER_KEY_RIGHT 11
8354 #define SETUP_TOKEN_PLAYER_KEY_UP 12
8355 #define SETUP_TOKEN_PLAYER_KEY_DOWN 13
8356 #define SETUP_TOKEN_PLAYER_KEY_SNAP 14
8357 #define SETUP_TOKEN_PLAYER_KEY_DROP 15
8359 #define NUM_PLAYER_SETUP_TOKENS 16
8362 #define SETUP_TOKEN_SYSTEM_SDL_VIDEODRIVER 0
8363 #define SETUP_TOKEN_SYSTEM_SDL_AUDIODRIVER 1
8364 #define SETUP_TOKEN_SYSTEM_AUDIO_FRAGMENT_SIZE 2
8366 #define NUM_SYSTEM_SETUP_TOKENS 3
8368 /* internal setup */
8369 #define SETUP_TOKEN_INT_PROGRAM_TITLE 0
8370 #define SETUP_TOKEN_INT_PROGRAM_VERSION 1
8371 #define SETUP_TOKEN_INT_PROGRAM_AUTHOR 2
8372 #define SETUP_TOKEN_INT_PROGRAM_EMAIL 3
8373 #define SETUP_TOKEN_INT_PROGRAM_WEBSITE 4
8374 #define SETUP_TOKEN_INT_PROGRAM_COPYRIGHT 5
8375 #define SETUP_TOKEN_INT_PROGRAM_COMPANY 6
8376 #define SETUP_TOKEN_INT_PROGRAM_ICON_FILE 7
8377 #define SETUP_TOKEN_INT_DEFAULT_GRAPHICS_SET 8
8378 #define SETUP_TOKEN_INT_DEFAULT_SOUNDS_SET 9
8379 #define SETUP_TOKEN_INT_DEFAULT_MUSIC_SET 10
8380 #define SETUP_TOKEN_INT_FALLBACK_GRAPHICS_FILE 11
8381 #define SETUP_TOKEN_INT_FALLBACK_SOUNDS_FILE 12
8382 #define SETUP_TOKEN_INT_FALLBACK_MUSIC_FILE 13
8383 #define SETUP_TOKEN_INT_DEFAULT_LEVEL_SERIES 14
8384 #define SETUP_TOKEN_INT_CHOOSE_FROM_TOP_LEVELDIR 15
8385 #define SETUP_TOKEN_INT_SHOW_SCALING_IN_TITLE 16
8386 #define SETUP_TOKEN_INT_DEFAULT_WINDOW_WIDTH 17
8387 #define SETUP_TOKEN_INT_DEFAULT_WINDOW_HEIGHT 18
8389 #define NUM_INTERNAL_SETUP_TOKENS 19
8392 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_0 0
8393 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_1 1
8394 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_2 2
8395 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_3 3
8396 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_4 4
8397 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_5 5
8398 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_6 6
8399 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_7 7
8400 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_8 8
8401 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_9 9
8402 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_KEY_0 10
8403 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_KEY_1 11
8404 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_KEY_2 12
8405 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_KEY_3 13
8406 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_KEY_4 14
8407 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_KEY_5 15
8408 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_KEY_6 16
8409 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_KEY_7 17
8410 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_KEY_8 18
8411 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_KEY_9 19
8412 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_USE_MOD_KEY 20
8413 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_GAME_ONLY 21
8414 #define SETUP_TOKEN_DEBUG_SHOW_FRAMES_PER_SECOND 22
8416 #define NUM_DEBUG_SETUP_TOKENS 23
8419 #define SETUP_TOKEN_OPTIONS_VERBOSE 0
8421 #define NUM_OPTIONS_SETUP_TOKENS 1
8424 static struct SetupInfo si;
8425 static struct SetupAutoSetupInfo sasi;
8426 static struct SetupEditorInfo sei;
8427 static struct SetupEditorCascadeInfo seci;
8428 static struct SetupShortcutInfo ssi;
8429 static struct SetupInputInfo sii;
8430 static struct SetupSystemInfo syi;
8431 static struct SetupInternalInfo sxi;
8432 static struct SetupDebugInfo sdi;
8433 static struct OptionInfo soi;
8435 static struct TokenInfo global_setup_tokens[] =
8437 { TYPE_STRING, &si.player_name, "player_name" },
8438 { TYPE_SWITCH, &si.sound, "sound" },
8439 { TYPE_SWITCH, &si.sound_loops, "repeating_sound_loops" },
8440 { TYPE_SWITCH, &si.sound_music, "background_music" },
8441 { TYPE_SWITCH, &si.sound_simple, "simple_sound_effects" },
8442 { TYPE_SWITCH, &si.toons, "toons" },
8443 { TYPE_SWITCH, &si.scroll_delay, "scroll_delay" },
8444 { TYPE_INTEGER,&si.scroll_delay_value, "scroll_delay_value" },
8445 { TYPE_STRING, &si.engine_snapshot_mode, "engine_snapshot_mode" },
8446 { TYPE_INTEGER,&si.engine_snapshot_memory, "engine_snapshot_memory" },
8447 { TYPE_SWITCH, &si.fade_screens, "fade_screens" },
8448 { TYPE_SWITCH, &si.autorecord, "automatic_tape_recording"},
8449 { TYPE_SWITCH, &si.show_titlescreen, "show_titlescreen" },
8450 { TYPE_SWITCH, &si.quick_doors, "quick_doors" },
8451 { TYPE_SWITCH, &si.team_mode, "team_mode" },
8452 { TYPE_SWITCH, &si.handicap, "handicap" },
8453 { TYPE_SWITCH, &si.skip_levels, "skip_levels" },
8454 { TYPE_SWITCH, &si.increment_levels, "increment_levels" },
8455 { TYPE_SWITCH, &si.time_limit, "time_limit" },
8456 { TYPE_SWITCH, &si.fullscreen, "fullscreen" },
8457 { TYPE_INTEGER,&si.window_scaling_percent, "window_scaling_percent" },
8458 { TYPE_STRING, &si.window_scaling_quality, "window_scaling_quality" },
8459 { TYPE_STRING, &si.screen_rendering_mode, "screen_rendering_mode" },
8460 { TYPE_SWITCH, &si.ask_on_escape, "ask_on_escape" },
8461 { TYPE_SWITCH, &si.ask_on_escape_editor, "ask_on_escape_editor" },
8462 { TYPE_SWITCH, &si.quick_switch, "quick_player_switch" },
8463 { TYPE_SWITCH, &si.input_on_focus, "input_on_focus" },
8464 { TYPE_SWITCH, &si.prefer_aga_graphics, "prefer_aga_graphics" },
8465 { TYPE_INTEGER,&si.game_frame_delay, "game_frame_delay" },
8466 { TYPE_SWITCH, &si.sp_show_border_elements, "sp_show_border_elements" },
8467 { TYPE_SWITCH, &si.small_game_graphics, "small_game_graphics" },
8468 { TYPE_SWITCH, &si.show_snapshot_buttons, "show_snapshot_buttons" },
8469 { TYPE_STRING, &si.graphics_set, "graphics_set" },
8470 { TYPE_STRING, &si.sounds_set, "sounds_set" },
8471 { TYPE_STRING, &si.music_set, "music_set" },
8472 { TYPE_SWITCH3,&si.override_level_graphics, "override_level_graphics" },
8473 { TYPE_SWITCH3,&si.override_level_sounds, "override_level_sounds" },
8474 { TYPE_SWITCH3,&si.override_level_music, "override_level_music" },
8475 { TYPE_INTEGER,&si.volume_simple, "volume_simple" },
8476 { TYPE_INTEGER,&si.volume_loops, "volume_loops" },
8477 { TYPE_INTEGER,&si.volume_music, "volume_music" },
8478 { TYPE_STRING, &si.touch.control_type, "touch.control_type" },
8479 { TYPE_INTEGER,&si.touch.move_distance, "touch.move_distance" },
8480 { TYPE_INTEGER,&si.touch.drop_distance, "touch.drop_distance" },
8483 static struct TokenInfo auto_setup_tokens[] =
8485 { TYPE_INTEGER,&sasi.editor_zoom_tilesize, "editor.zoom_tilesize" },
8488 static struct TokenInfo editor_setup_tokens[] =
8490 { TYPE_SWITCH, &sei.el_classic, "editor.el_classic" },
8491 { TYPE_SWITCH, &sei.el_custom, "editor.el_custom" },
8492 { TYPE_SWITCH, &sei.el_user_defined, "editor.el_user_defined" },
8493 { TYPE_SWITCH, &sei.el_dynamic, "editor.el_dynamic" },
8494 { TYPE_SWITCH, &sei.el_headlines, "editor.el_headlines" },
8495 { TYPE_SWITCH, &sei.show_element_token,"editor.show_element_token" },
8498 static struct TokenInfo editor_cascade_setup_tokens[] =
8500 { TYPE_SWITCH, &seci.el_bd, "editor.cascade.el_bd" },
8501 { TYPE_SWITCH, &seci.el_em, "editor.cascade.el_em" },
8502 { TYPE_SWITCH, &seci.el_emc, "editor.cascade.el_emc" },
8503 { TYPE_SWITCH, &seci.el_rnd, "editor.cascade.el_rnd" },
8504 { TYPE_SWITCH, &seci.el_sb, "editor.cascade.el_sb" },
8505 { TYPE_SWITCH, &seci.el_sp, "editor.cascade.el_sp" },
8506 { TYPE_SWITCH, &seci.el_dc, "editor.cascade.el_dc" },
8507 { TYPE_SWITCH, &seci.el_dx, "editor.cascade.el_dx" },
8508 { TYPE_SWITCH, &seci.el_mm, "editor.cascade.el_mm" },
8509 { TYPE_SWITCH, &seci.el_df, "editor.cascade.el_df" },
8510 { TYPE_SWITCH, &seci.el_chars, "editor.cascade.el_chars" },
8511 { TYPE_SWITCH, &seci.el_steel_chars, "editor.cascade.el_steel_chars" },
8512 { TYPE_SWITCH, &seci.el_ce, "editor.cascade.el_ce" },
8513 { TYPE_SWITCH, &seci.el_ge, "editor.cascade.el_ge" },
8514 { TYPE_SWITCH, &seci.el_ref, "editor.cascade.el_ref" },
8515 { TYPE_SWITCH, &seci.el_user, "editor.cascade.el_user" },
8516 { TYPE_SWITCH, &seci.el_dynamic, "editor.cascade.el_dynamic" },
8519 static struct TokenInfo shortcut_setup_tokens[] =
8521 { TYPE_KEY_X11, &ssi.save_game, "shortcut.save_game" },
8522 { TYPE_KEY_X11, &ssi.load_game, "shortcut.load_game" },
8523 { TYPE_KEY_X11, &ssi.toggle_pause, "shortcut.toggle_pause" },
8524 { TYPE_KEY_X11, &ssi.focus_player[0], "shortcut.focus_player_1" },
8525 { TYPE_KEY_X11, &ssi.focus_player[1], "shortcut.focus_player_2" },
8526 { TYPE_KEY_X11, &ssi.focus_player[2], "shortcut.focus_player_3" },
8527 { TYPE_KEY_X11, &ssi.focus_player[3], "shortcut.focus_player_4" },
8528 { TYPE_KEY_X11, &ssi.focus_player_all,"shortcut.focus_player_all" },
8529 { TYPE_KEY_X11, &ssi.tape_eject, "shortcut.tape_eject" },
8530 { TYPE_KEY_X11, &ssi.tape_extra, "shortcut.tape_extra" },
8531 { TYPE_KEY_X11, &ssi.tape_stop, "shortcut.tape_stop" },
8532 { TYPE_KEY_X11, &ssi.tape_pause, "shortcut.tape_pause" },
8533 { TYPE_KEY_X11, &ssi.tape_record, "shortcut.tape_record" },
8534 { TYPE_KEY_X11, &ssi.tape_play, "shortcut.tape_play" },
8535 { TYPE_KEY_X11, &ssi.sound_simple, "shortcut.sound_simple" },
8536 { TYPE_KEY_X11, &ssi.sound_loops, "shortcut.sound_loops" },
8537 { TYPE_KEY_X11, &ssi.sound_music, "shortcut.sound_music" },
8538 { TYPE_KEY_X11, &ssi.snap_left, "shortcut.snap_left" },
8539 { TYPE_KEY_X11, &ssi.snap_right, "shortcut.snap_right" },
8540 { TYPE_KEY_X11, &ssi.snap_up, "shortcut.snap_up" },
8541 { TYPE_KEY_X11, &ssi.snap_down, "shortcut.snap_down" },
8544 static struct TokenInfo player_setup_tokens[] =
8546 { TYPE_BOOLEAN, &sii.use_joystick, ".use_joystick" },
8547 { TYPE_STRING, &sii.joy.device_name, ".joy.device_name" },
8548 { TYPE_INTEGER, &sii.joy.xleft, ".joy.xleft" },
8549 { TYPE_INTEGER, &sii.joy.xmiddle, ".joy.xmiddle" },
8550 { TYPE_INTEGER, &sii.joy.xright, ".joy.xright" },
8551 { TYPE_INTEGER, &sii.joy.yupper, ".joy.yupper" },
8552 { TYPE_INTEGER, &sii.joy.ymiddle, ".joy.ymiddle" },
8553 { TYPE_INTEGER, &sii.joy.ylower, ".joy.ylower" },
8554 { TYPE_INTEGER, &sii.joy.snap, ".joy.snap_field" },
8555 { TYPE_INTEGER, &sii.joy.drop, ".joy.place_bomb" },
8556 { TYPE_KEY_X11, &sii.key.left, ".key.move_left" },
8557 { TYPE_KEY_X11, &sii.key.right, ".key.move_right" },
8558 { TYPE_KEY_X11, &sii.key.up, ".key.move_up" },
8559 { TYPE_KEY_X11, &sii.key.down, ".key.move_down" },
8560 { TYPE_KEY_X11, &sii.key.snap, ".key.snap_field" },
8561 { TYPE_KEY_X11, &sii.key.drop, ".key.place_bomb" },
8564 static struct TokenInfo system_setup_tokens[] =
8566 { TYPE_STRING, &syi.sdl_videodriver, "system.sdl_videodriver" },
8567 { TYPE_STRING, &syi.sdl_audiodriver, "system.sdl_audiodriver" },
8568 { TYPE_INTEGER, &syi.audio_fragment_size,"system.audio_fragment_size" },
8571 static struct TokenInfo internal_setup_tokens[] =
8573 { TYPE_STRING, &sxi.program_title, "program_title" },
8574 { TYPE_STRING, &sxi.program_version, "program_version" },
8575 { TYPE_STRING, &sxi.program_author, "program_author" },
8576 { TYPE_STRING, &sxi.program_email, "program_email" },
8577 { TYPE_STRING, &sxi.program_website, "program_website" },
8578 { TYPE_STRING, &sxi.program_copyright, "program_copyright" },
8579 { TYPE_STRING, &sxi.program_company, "program_company" },
8580 { TYPE_STRING, &sxi.program_icon_file, "program_icon_file" },
8581 { TYPE_STRING, &sxi.default_graphics_set, "default_graphics_set" },
8582 { TYPE_STRING, &sxi.default_sounds_set, "default_sounds_set" },
8583 { TYPE_STRING, &sxi.default_music_set, "default_music_set" },
8584 { TYPE_STRING, &sxi.fallback_graphics_file, "fallback_graphics_file"},
8585 { TYPE_STRING, &sxi.fallback_sounds_file, "fallback_sounds_file" },
8586 { TYPE_STRING, &sxi.fallback_music_file, "fallback_music_file" },
8587 { TYPE_STRING, &sxi.default_level_series, "default_level_series" },
8588 { TYPE_BOOLEAN,&sxi.choose_from_top_leveldir, "choose_from_top_leveldir" },
8589 { TYPE_BOOLEAN,&sxi.show_scaling_in_title, "show_scaling_in_title" },
8590 { TYPE_INTEGER,&sxi.default_window_width, "default_window_width" },
8591 { TYPE_INTEGER,&sxi.default_window_height, "default_window_height" },
8594 static struct TokenInfo debug_setup_tokens[] =
8596 { TYPE_INTEGER, &sdi.frame_delay[0], "debug.frame_delay_0" },
8597 { TYPE_INTEGER, &sdi.frame_delay[1], "debug.frame_delay_1" },
8598 { TYPE_INTEGER, &sdi.frame_delay[2], "debug.frame_delay_2" },
8599 { TYPE_INTEGER, &sdi.frame_delay[3], "debug.frame_delay_3" },
8600 { TYPE_INTEGER, &sdi.frame_delay[4], "debug.frame_delay_4" },
8601 { TYPE_INTEGER, &sdi.frame_delay[5], "debug.frame_delay_5" },
8602 { TYPE_INTEGER, &sdi.frame_delay[6], "debug.frame_delay_6" },
8603 { TYPE_INTEGER, &sdi.frame_delay[7], "debug.frame_delay_7" },
8604 { TYPE_INTEGER, &sdi.frame_delay[8], "debug.frame_delay_8" },
8605 { TYPE_INTEGER, &sdi.frame_delay[9], "debug.frame_delay_9" },
8606 { TYPE_KEY_X11, &sdi.frame_delay_key[0], "debug.key.frame_delay_0" },
8607 { TYPE_KEY_X11, &sdi.frame_delay_key[1], "debug.key.frame_delay_1" },
8608 { TYPE_KEY_X11, &sdi.frame_delay_key[2], "debug.key.frame_delay_2" },
8609 { TYPE_KEY_X11, &sdi.frame_delay_key[3], "debug.key.frame_delay_3" },
8610 { TYPE_KEY_X11, &sdi.frame_delay_key[4], "debug.key.frame_delay_4" },
8611 { TYPE_KEY_X11, &sdi.frame_delay_key[5], "debug.key.frame_delay_5" },
8612 { TYPE_KEY_X11, &sdi.frame_delay_key[6], "debug.key.frame_delay_6" },
8613 { TYPE_KEY_X11, &sdi.frame_delay_key[7], "debug.key.frame_delay_7" },
8614 { TYPE_KEY_X11, &sdi.frame_delay_key[8], "debug.key.frame_delay_8" },
8615 { TYPE_KEY_X11, &sdi.frame_delay_key[9], "debug.key.frame_delay_9" },
8616 { TYPE_BOOLEAN, &sdi.frame_delay_use_mod_key,"debug.frame_delay.use_mod_key"},
8617 { TYPE_BOOLEAN, &sdi.frame_delay_game_only, "debug.frame_delay.game_only" },
8618 { TYPE_BOOLEAN, &sdi.show_frames_per_second, "debug.show_frames_per_second" },
8621 static struct TokenInfo options_setup_tokens[] =
8623 { TYPE_BOOLEAN, &soi.verbose, "options.verbose" },
8626 static char *get_corrected_login_name(char *login_name)
8628 /* needed because player name must be a fixed length string */
8629 char *login_name_new = checked_malloc(MAX_PLAYER_NAME_LEN + 1);
8631 strncpy(login_name_new, login_name, MAX_PLAYER_NAME_LEN);
8632 login_name_new[MAX_PLAYER_NAME_LEN] = '\0';
8634 if (strlen(login_name) > MAX_PLAYER_NAME_LEN) /* name has been cut */
8635 if (strchr(login_name_new, ' '))
8636 *strchr(login_name_new, ' ') = '\0';
8638 return login_name_new;
8641 static void setSetupInfoToDefaults(struct SetupInfo *si)
8645 si->player_name = get_corrected_login_name(getLoginName());
8648 si->sound_loops = TRUE;
8649 si->sound_music = TRUE;
8650 si->sound_simple = TRUE;
8652 si->scroll_delay = TRUE;
8653 si->scroll_delay_value = STD_SCROLL_DELAY;
8654 si->engine_snapshot_mode = getStringCopy(STR_SNAPSHOT_MODE_DEFAULT);
8655 si->engine_snapshot_memory = SNAPSHOT_MEMORY_DEFAULT;
8656 si->fade_screens = TRUE;
8657 si->autorecord = TRUE;
8658 si->show_titlescreen = TRUE;
8659 si->quick_doors = FALSE;
8660 si->team_mode = FALSE;
8661 si->handicap = TRUE;
8662 si->skip_levels = TRUE;
8663 si->increment_levels = TRUE;
8664 si->time_limit = TRUE;
8665 si->fullscreen = FALSE;
8666 si->window_scaling_percent = STD_WINDOW_SCALING_PERCENT;
8667 si->window_scaling_quality = getStringCopy(SCALING_QUALITY_DEFAULT);
8668 si->screen_rendering_mode = getStringCopy(STR_SPECIAL_RENDERING_DEFAULT);
8669 si->ask_on_escape = TRUE;
8670 si->ask_on_escape_editor = TRUE;
8671 si->quick_switch = FALSE;
8672 si->input_on_focus = FALSE;
8673 si->prefer_aga_graphics = TRUE;
8674 si->game_frame_delay = GAME_FRAME_DELAY;
8675 si->sp_show_border_elements = FALSE;
8676 si->small_game_graphics = FALSE;
8677 si->show_snapshot_buttons = FALSE;
8679 si->graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
8680 si->sounds_set = getStringCopy(SND_CLASSIC_SUBDIR);
8681 si->music_set = getStringCopy(MUS_CLASSIC_SUBDIR);
8683 si->override_level_graphics = FALSE;
8684 si->override_level_sounds = FALSE;
8685 si->override_level_music = FALSE;
8687 si->volume_simple = 100; /* percent */
8688 si->volume_loops = 100; /* percent */
8689 si->volume_music = 100; /* percent */
8691 si->touch.control_type = getStringCopy(TOUCH_CONTROL_DEFAULT);
8692 si->touch.move_distance = TOUCH_MOVE_DISTANCE_DEFAULT; /* percent */
8693 si->touch.drop_distance = TOUCH_DROP_DISTANCE_DEFAULT; /* percent */
8695 si->editor.el_boulderdash = TRUE;
8696 si->editor.el_emerald_mine = TRUE;
8697 si->editor.el_emerald_mine_club = TRUE;
8698 si->editor.el_more = TRUE;
8699 si->editor.el_sokoban = TRUE;
8700 si->editor.el_supaplex = TRUE;
8701 si->editor.el_diamond_caves = TRUE;
8702 si->editor.el_dx_boulderdash = TRUE;
8704 si->editor.el_mirror_magic = TRUE;
8705 si->editor.el_deflektor = TRUE;
8707 si->editor.el_chars = TRUE;
8708 si->editor.el_steel_chars = TRUE;
8710 si->editor.el_classic = TRUE;
8711 si->editor.el_custom = TRUE;
8713 si->editor.el_user_defined = FALSE;
8714 si->editor.el_dynamic = TRUE;
8716 si->editor.el_headlines = TRUE;
8718 si->editor.show_element_token = FALSE;
8720 si->editor.use_template_for_new_levels = TRUE;
8722 si->shortcut.save_game = DEFAULT_KEY_SAVE_GAME;
8723 si->shortcut.load_game = DEFAULT_KEY_LOAD_GAME;
8724 si->shortcut.toggle_pause = DEFAULT_KEY_TOGGLE_PAUSE;
8726 si->shortcut.focus_player[0] = DEFAULT_KEY_FOCUS_PLAYER_1;
8727 si->shortcut.focus_player[1] = DEFAULT_KEY_FOCUS_PLAYER_2;
8728 si->shortcut.focus_player[2] = DEFAULT_KEY_FOCUS_PLAYER_3;
8729 si->shortcut.focus_player[3] = DEFAULT_KEY_FOCUS_PLAYER_4;
8730 si->shortcut.focus_player_all = DEFAULT_KEY_FOCUS_PLAYER_ALL;
8732 si->shortcut.tape_eject = DEFAULT_KEY_TAPE_EJECT;
8733 si->shortcut.tape_extra = DEFAULT_KEY_TAPE_EXTRA;
8734 si->shortcut.tape_stop = DEFAULT_KEY_TAPE_STOP;
8735 si->shortcut.tape_pause = DEFAULT_KEY_TAPE_PAUSE;
8736 si->shortcut.tape_record = DEFAULT_KEY_TAPE_RECORD;
8737 si->shortcut.tape_play = DEFAULT_KEY_TAPE_PLAY;
8739 si->shortcut.sound_simple = DEFAULT_KEY_SOUND_SIMPLE;
8740 si->shortcut.sound_loops = DEFAULT_KEY_SOUND_LOOPS;
8741 si->shortcut.sound_music = DEFAULT_KEY_SOUND_MUSIC;
8743 si->shortcut.snap_left = DEFAULT_KEY_SNAP_LEFT;
8744 si->shortcut.snap_right = DEFAULT_KEY_SNAP_RIGHT;
8745 si->shortcut.snap_up = DEFAULT_KEY_SNAP_UP;
8746 si->shortcut.snap_down = DEFAULT_KEY_SNAP_DOWN;
8748 for (i = 0; i < MAX_PLAYERS; i++)
8750 si->input[i].use_joystick = FALSE;
8751 si->input[i].joy.device_name=getStringCopy(getDeviceNameFromJoystickNr(i));
8752 si->input[i].joy.xleft = JOYSTICK_XLEFT;
8753 si->input[i].joy.xmiddle = JOYSTICK_XMIDDLE;
8754 si->input[i].joy.xright = JOYSTICK_XRIGHT;
8755 si->input[i].joy.yupper = JOYSTICK_YUPPER;
8756 si->input[i].joy.ymiddle = JOYSTICK_YMIDDLE;
8757 si->input[i].joy.ylower = JOYSTICK_YLOWER;
8758 si->input[i].joy.snap = (i == 0 ? JOY_BUTTON_1 : 0);
8759 si->input[i].joy.drop = (i == 0 ? JOY_BUTTON_2 : 0);
8760 si->input[i].key.left = (i == 0 ? DEFAULT_KEY_LEFT : KSYM_UNDEFINED);
8761 si->input[i].key.right = (i == 0 ? DEFAULT_KEY_RIGHT : KSYM_UNDEFINED);
8762 si->input[i].key.up = (i == 0 ? DEFAULT_KEY_UP : KSYM_UNDEFINED);
8763 si->input[i].key.down = (i == 0 ? DEFAULT_KEY_DOWN : KSYM_UNDEFINED);
8764 si->input[i].key.snap = (i == 0 ? DEFAULT_KEY_SNAP : KSYM_UNDEFINED);
8765 si->input[i].key.drop = (i == 0 ? DEFAULT_KEY_DROP : KSYM_UNDEFINED);
8768 si->system.sdl_videodriver = getStringCopy(ARG_DEFAULT);
8769 si->system.sdl_audiodriver = getStringCopy(ARG_DEFAULT);
8770 si->system.audio_fragment_size = DEFAULT_AUDIO_FRAGMENT_SIZE;
8772 si->internal.program_title = getStringCopy(PROGRAM_TITLE_STRING);
8773 si->internal.program_version = getStringCopy(getProgramRealVersionString());
8774 si->internal.program_author = getStringCopy(PROGRAM_AUTHOR_STRING);
8775 si->internal.program_email = getStringCopy(PROGRAM_EMAIL_STRING);
8776 si->internal.program_website = getStringCopy(PROGRAM_WEBSITE_STRING);
8777 si->internal.program_copyright = getStringCopy(PROGRAM_COPYRIGHT_STRING);
8778 si->internal.program_company = getStringCopy(PROGRAM_COMPANY_STRING);
8780 si->internal.program_icon_file = getStringCopy(PROGRAM_ICON_FILENAME);
8782 si->internal.default_graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
8783 si->internal.default_sounds_set = getStringCopy(SND_CLASSIC_SUBDIR);
8784 si->internal.default_music_set = getStringCopy(MUS_CLASSIC_SUBDIR);
8786 si->internal.fallback_graphics_file = getStringCopy(UNDEFINED_FILENAME);
8787 si->internal.fallback_sounds_file = getStringCopy(UNDEFINED_FILENAME);
8788 si->internal.fallback_music_file = getStringCopy(UNDEFINED_FILENAME);
8790 si->internal.default_level_series = getStringCopy(UNDEFINED_LEVELSET);
8791 si->internal.choose_from_top_leveldir = FALSE;
8792 si->internal.show_scaling_in_title = TRUE;
8794 si->internal.default_window_width = WIN_XSIZE_DEFAULT;
8795 si->internal.default_window_height = WIN_YSIZE_DEFAULT;
8797 si->debug.frame_delay[0] = DEFAULT_FRAME_DELAY_0;
8798 si->debug.frame_delay[1] = DEFAULT_FRAME_DELAY_1;
8799 si->debug.frame_delay[2] = DEFAULT_FRAME_DELAY_2;
8800 si->debug.frame_delay[3] = DEFAULT_FRAME_DELAY_3;
8801 si->debug.frame_delay[4] = DEFAULT_FRAME_DELAY_4;
8802 si->debug.frame_delay[5] = DEFAULT_FRAME_DELAY_5;
8803 si->debug.frame_delay[6] = DEFAULT_FRAME_DELAY_6;
8804 si->debug.frame_delay[7] = DEFAULT_FRAME_DELAY_7;
8805 si->debug.frame_delay[8] = DEFAULT_FRAME_DELAY_8;
8806 si->debug.frame_delay[9] = DEFAULT_FRAME_DELAY_9;
8808 si->debug.frame_delay_key[0] = DEFAULT_KEY_FRAME_DELAY_0;
8809 si->debug.frame_delay_key[1] = DEFAULT_KEY_FRAME_DELAY_1;
8810 si->debug.frame_delay_key[2] = DEFAULT_KEY_FRAME_DELAY_2;
8811 si->debug.frame_delay_key[3] = DEFAULT_KEY_FRAME_DELAY_3;
8812 si->debug.frame_delay_key[4] = DEFAULT_KEY_FRAME_DELAY_4;
8813 si->debug.frame_delay_key[5] = DEFAULT_KEY_FRAME_DELAY_5;
8814 si->debug.frame_delay_key[6] = DEFAULT_KEY_FRAME_DELAY_6;
8815 si->debug.frame_delay_key[7] = DEFAULT_KEY_FRAME_DELAY_7;
8816 si->debug.frame_delay_key[8] = DEFAULT_KEY_FRAME_DELAY_8;
8817 si->debug.frame_delay_key[9] = DEFAULT_KEY_FRAME_DELAY_9;
8819 si->debug.frame_delay_use_mod_key = DEFAULT_FRAME_DELAY_USE_MOD_KEY;
8820 si->debug.frame_delay_game_only = DEFAULT_FRAME_DELAY_GAME_ONLY;
8822 si->debug.show_frames_per_second = FALSE;
8824 si->options.verbose = FALSE;
8826 #if defined(PLATFORM_ANDROID)
8827 si->fullscreen = TRUE;
8831 static void setSetupInfoToDefaults_AutoSetup(struct SetupInfo *si)
8833 si->auto_setup.editor_zoom_tilesize = MINI_TILESIZE;
8836 static void setSetupInfoToDefaults_EditorCascade(struct SetupInfo *si)
8838 si->editor_cascade.el_bd = TRUE;
8839 si->editor_cascade.el_em = TRUE;
8840 si->editor_cascade.el_emc = TRUE;
8841 si->editor_cascade.el_rnd = TRUE;
8842 si->editor_cascade.el_sb = TRUE;
8843 si->editor_cascade.el_sp = TRUE;
8844 si->editor_cascade.el_dc = TRUE;
8845 si->editor_cascade.el_dx = TRUE;
8847 si->editor_cascade.el_mm = TRUE;
8848 si->editor_cascade.el_df = TRUE;
8850 si->editor_cascade.el_chars = FALSE;
8851 si->editor_cascade.el_steel_chars = FALSE;
8852 si->editor_cascade.el_ce = FALSE;
8853 si->editor_cascade.el_ge = FALSE;
8854 si->editor_cascade.el_ref = FALSE;
8855 si->editor_cascade.el_user = FALSE;
8856 si->editor_cascade.el_dynamic = FALSE;
8859 #define MAX_HIDE_SETUP_TOKEN_SIZE 20
8861 static char *getHideSetupToken(void *setup_value)
8863 static char hide_setup_token[MAX_HIDE_SETUP_TOKEN_SIZE];
8865 if (setup_value != NULL)
8866 snprintf(hide_setup_token, MAX_HIDE_SETUP_TOKEN_SIZE, "%p", setup_value);
8868 return hide_setup_token;
8871 static void setHideSetupEntry(void *setup_value_raw)
8873 /* !!! DIRTY WORKAROUND; TO BE FIXED AFTER THE MM ENGINE RELEASE !!! */
8874 void *setup_value = setup_value_raw - (void *)&si + (void *)&setup;
8876 char *hide_setup_token = getHideSetupToken(setup_value);
8878 if (setup_value != NULL)
8879 setHashEntry(hide_setup_hash, hide_setup_token, "");
8882 boolean hideSetupEntry(void *setup_value)
8884 char *hide_setup_token = getHideSetupToken(setup_value);
8886 return (setup_value != NULL &&
8887 getHashEntry(hide_setup_hash, hide_setup_token) != NULL);
8890 static void setSetupInfoFromTokenText(SetupFileHash *setup_file_hash,
8891 struct TokenInfo *token_info,
8892 int token_nr, char *token_text)
8894 char *token_hide_text = getStringCat2(token_text, ".hide");
8895 char *token_hide_value = getHashEntry(setup_file_hash, token_hide_text);
8897 /* set the value of this setup option in the setup option structure */
8898 setSetupInfo(token_info, token_nr, getHashEntry(setup_file_hash, token_text));
8900 /* check if this setup option should be hidden in the setup menu */
8901 if (token_hide_value != NULL && get_boolean_from_string(token_hide_value))
8902 setHideSetupEntry(token_info[token_nr].value);
8905 static void setSetupInfoFromTokenInfo(SetupFileHash *setup_file_hash,
8906 struct TokenInfo *token_info,
8909 setSetupInfoFromTokenText(setup_file_hash, token_info, token_nr,
8910 token_info[token_nr].text);
8913 static void decodeSetupFileHash(SetupFileHash *setup_file_hash)
8917 if (!setup_file_hash)
8920 if (hide_setup_hash == NULL)
8921 hide_setup_hash = newSetupFileHash();
8925 for (i = 0; i < NUM_GLOBAL_SETUP_TOKENS; i++)
8926 setSetupInfoFromTokenInfo(setup_file_hash, global_setup_tokens, i);
8931 for (i = 0; i < NUM_EDITOR_SETUP_TOKENS; i++)
8932 setSetupInfoFromTokenInfo(setup_file_hash, editor_setup_tokens, i);
8935 /* shortcut setup */
8936 ssi = setup.shortcut;
8937 for (i = 0; i < NUM_SHORTCUT_SETUP_TOKENS; i++)
8938 setSetupInfoFromTokenInfo(setup_file_hash, shortcut_setup_tokens, i);
8939 setup.shortcut = ssi;
8942 for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
8946 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
8948 sii = setup.input[pnr];
8949 for (i = 0; i < NUM_PLAYER_SETUP_TOKENS; i++)
8951 char full_token[100];
8953 sprintf(full_token, "%s%s", prefix, player_setup_tokens[i].text);
8954 setSetupInfoFromTokenText(setup_file_hash, player_setup_tokens, i,
8957 setup.input[pnr] = sii;
8962 for (i = 0; i < NUM_SYSTEM_SETUP_TOKENS; i++)
8963 setSetupInfoFromTokenInfo(setup_file_hash, system_setup_tokens, i);
8966 /* internal setup */
8967 sxi = setup.internal;
8968 for (i = 0; i < NUM_INTERNAL_SETUP_TOKENS; i++)
8969 setSetupInfoFromTokenInfo(setup_file_hash, internal_setup_tokens, i);
8970 setup.internal = sxi;
8974 for (i = 0; i < NUM_DEBUG_SETUP_TOKENS; i++)
8975 setSetupInfoFromTokenInfo(setup_file_hash, debug_setup_tokens, i);
8979 soi = setup.options;
8980 for (i = 0; i < NUM_OPTIONS_SETUP_TOKENS; i++)
8981 setSetupInfoFromTokenInfo(setup_file_hash, options_setup_tokens, i);
8982 setup.options = soi;
8985 static void decodeSetupFileHash_AutoSetup(SetupFileHash *setup_file_hash)
8989 if (!setup_file_hash)
8993 sasi = setup.auto_setup;
8994 for (i = 0; i < NUM_AUTO_SETUP_TOKENS; i++)
8995 setSetupInfo(auto_setup_tokens, i,
8996 getHashEntry(setup_file_hash,
8997 auto_setup_tokens[i].text));
8998 setup.auto_setup = sasi;
9001 static void decodeSetupFileHash_EditorCascade(SetupFileHash *setup_file_hash)
9005 if (!setup_file_hash)
9008 /* editor cascade setup */
9009 seci = setup.editor_cascade;
9010 for (i = 0; i < NUM_EDITOR_CASCADE_SETUP_TOKENS; i++)
9011 setSetupInfo(editor_cascade_setup_tokens, i,
9012 getHashEntry(setup_file_hash,
9013 editor_cascade_setup_tokens[i].text));
9014 setup.editor_cascade = seci;
9017 void LoadSetupFromFilename(char *filename)
9019 SetupFileHash *setup_file_hash = loadSetupFileHash(filename);
9021 if (setup_file_hash)
9023 decodeSetupFileHash(setup_file_hash);
9025 freeSetupFileHash(setup_file_hash);
9029 Error(ERR_DEBUG, "using default setup values");
9033 static void LoadSetup_SpecialPostProcessing()
9035 char *player_name_new;
9037 /* needed to work around problems with fixed length strings */
9038 player_name_new = get_corrected_login_name(setup.player_name);
9039 free(setup.player_name);
9040 setup.player_name = player_name_new;
9042 /* "scroll_delay: on(3) / off(0)" was replaced by scroll delay value */
9043 if (setup.scroll_delay == FALSE)
9045 setup.scroll_delay_value = MIN_SCROLL_DELAY;
9046 setup.scroll_delay = TRUE; /* now always "on" */
9049 /* make sure that scroll delay value stays inside valid range */
9050 setup.scroll_delay_value =
9051 MIN(MAX(MIN_SCROLL_DELAY, setup.scroll_delay_value), MAX_SCROLL_DELAY);
9058 /* always start with reliable default values */
9059 setSetupInfoToDefaults(&setup);
9061 /* try to load setup values from default setup file */
9062 filename = getDefaultSetupFilename();
9064 if (fileExists(filename))
9065 LoadSetupFromFilename(filename);
9067 /* try to load setup values from user setup file */
9068 filename = getSetupFilename();
9070 LoadSetupFromFilename(filename);
9072 LoadSetup_SpecialPostProcessing();
9075 void LoadSetup_AutoSetup()
9077 char *filename = getPath2(getSetupDir(), AUTOSETUP_FILENAME);
9078 SetupFileHash *setup_file_hash = NULL;
9080 /* always start with reliable default values */
9081 setSetupInfoToDefaults_AutoSetup(&setup);
9083 setup_file_hash = loadSetupFileHash(filename);
9085 if (setup_file_hash)
9087 decodeSetupFileHash_AutoSetup(setup_file_hash);
9089 freeSetupFileHash(setup_file_hash);
9095 void LoadSetup_EditorCascade()
9097 char *filename = getPath2(getSetupDir(), EDITORCASCADE_FILENAME);
9098 SetupFileHash *setup_file_hash = NULL;
9100 /* always start with reliable default values */
9101 setSetupInfoToDefaults_EditorCascade(&setup);
9103 setup_file_hash = loadSetupFileHash(filename);
9105 if (setup_file_hash)
9107 decodeSetupFileHash_EditorCascade(setup_file_hash);
9109 freeSetupFileHash(setup_file_hash);
9115 static void addGameControllerMappingToHash(SetupFileHash *mappings_hash,
9118 char mapping_guid[MAX_LINE_LEN];
9119 char *mapping_start, *mapping_end;
9121 // get GUID from game controller mapping line: copy complete line
9122 strncpy(mapping_guid, mapping_line, MAX_LINE_LEN - 1);
9123 mapping_guid[MAX_LINE_LEN - 1] = '\0';
9125 // get GUID from game controller mapping line: cut after GUID part
9126 mapping_start = strchr(mapping_guid, ',');
9127 if (mapping_start != NULL)
9128 *mapping_start = '\0';
9130 // cut newline from game controller mapping line
9131 mapping_end = strchr(mapping_line, '\n');
9132 if (mapping_end != NULL)
9133 *mapping_end = '\0';
9135 // add mapping entry to game controller mappings hash
9136 setHashEntry(mappings_hash, mapping_guid, mapping_line);
9139 static void LoadSetup_ReadGameControllerMappings(SetupFileHash *mappings_hash,
9144 if (!(file = fopen(filename, MODE_READ)))
9146 Error(ERR_WARN, "cannot read game controller mappings file '%s'", filename);
9153 char line[MAX_LINE_LEN];
9155 if (!fgets(line, MAX_LINE_LEN, file))
9158 addGameControllerMappingToHash(mappings_hash, line);
9166 char *filename = getSetupFilename();
9170 InitUserDataDirectory();
9172 if (!(file = fopen(filename, MODE_WRITE)))
9174 Error(ERR_WARN, "cannot write setup file '%s'", filename);
9178 fprintFileHeader(file, SETUP_FILENAME);
9182 for (i = 0; i < NUM_GLOBAL_SETUP_TOKENS; i++)
9184 /* just to make things nicer :) */
9185 if (i == SETUP_TOKEN_PLAYER_NAME + 1 ||
9186 i == SETUP_TOKEN_GRAPHICS_SET ||
9187 i == SETUP_TOKEN_VOLUME_SIMPLE ||
9188 i == SETUP_TOKEN_TOUCH_CONTROL_TYPE)
9189 fprintf(file, "\n");
9191 fprintf(file, "%s\n", getSetupLine(global_setup_tokens, "", i));
9196 fprintf(file, "\n");
9197 for (i = 0; i < NUM_EDITOR_SETUP_TOKENS; i++)
9198 fprintf(file, "%s\n", getSetupLine(editor_setup_tokens, "", i));
9200 /* shortcut setup */
9201 ssi = setup.shortcut;
9202 fprintf(file, "\n");
9203 for (i = 0; i < NUM_SHORTCUT_SETUP_TOKENS; i++)
9204 fprintf(file, "%s\n", getSetupLine(shortcut_setup_tokens, "", i));
9207 for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
9211 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
9212 fprintf(file, "\n");
9214 sii = setup.input[pnr];
9215 for (i = 0; i < NUM_PLAYER_SETUP_TOKENS; i++)
9216 fprintf(file, "%s\n", getSetupLine(player_setup_tokens, prefix, i));
9221 fprintf(file, "\n");
9222 for (i = 0; i < NUM_SYSTEM_SETUP_TOKENS; i++)
9223 fprintf(file, "%s\n", getSetupLine(system_setup_tokens, "", i));
9225 /* internal setup */
9226 /* (internal setup values not saved to user setup file) */
9230 fprintf(file, "\n");
9231 for (i = 0; i < NUM_DEBUG_SETUP_TOKENS; i++)
9232 fprintf(file, "%s\n", getSetupLine(debug_setup_tokens, "", i));
9235 soi = setup.options;
9236 fprintf(file, "\n");
9237 for (i = 0; i < NUM_OPTIONS_SETUP_TOKENS; i++)
9238 fprintf(file, "%s\n", getSetupLine(options_setup_tokens, "", i));
9242 SetFilePermissions(filename, PERMS_PRIVATE);
9245 void SaveSetup_AutoSetup()
9247 char *filename = getPath2(getSetupDir(), AUTOSETUP_FILENAME);
9251 InitUserDataDirectory();
9253 if (!(file = fopen(filename, MODE_WRITE)))
9255 Error(ERR_WARN, "cannot write auto setup file '%s'", filename);
9260 fprintFileHeader(file, AUTOSETUP_FILENAME);
9262 sasi = setup.auto_setup;
9263 for (i = 0; i < NUM_AUTO_SETUP_TOKENS; i++)
9264 fprintf(file, "%s\n", getSetupLine(auto_setup_tokens, "", i));
9268 SetFilePermissions(filename, PERMS_PRIVATE);
9273 void SaveSetup_EditorCascade()
9275 char *filename = getPath2(getSetupDir(), EDITORCASCADE_FILENAME);
9279 InitUserDataDirectory();
9281 if (!(file = fopen(filename, MODE_WRITE)))
9283 Error(ERR_WARN, "cannot write editor cascade state file '%s'", filename);
9288 fprintFileHeader(file, EDITORCASCADE_FILENAME);
9290 seci = setup.editor_cascade;
9291 for (i = 0; i < NUM_EDITOR_CASCADE_SETUP_TOKENS; i++)
9292 fprintf(file, "%s\n", getSetupLine(editor_cascade_setup_tokens, "", i));
9296 SetFilePermissions(filename, PERMS_PRIVATE);
9301 static void SaveSetup_WriteGameControllerMappings(SetupFileHash *mappings_hash,
9306 if (!(file = fopen(filename, MODE_WRITE)))
9308 Error(ERR_WARN, "cannot write game controller mappings file '%s'",filename);
9313 BEGIN_HASH_ITERATION(mappings_hash, itr)
9315 fprintf(file, "%s\n", HASH_ITERATION_VALUE(itr));
9317 END_HASH_ITERATION(mappings_hash, itr)
9322 void SaveSetup_AddGameControllerMapping(char *mapping)
9324 char *filename = getPath2(getSetupDir(), GAMECONTROLLER_BASENAME);
9325 SetupFileHash *mappings_hash = newSetupFileHash();
9327 InitUserDataDirectory();
9329 // load existing personal game controller mappings
9330 LoadSetup_ReadGameControllerMappings(mappings_hash, filename);
9332 // add new mapping to personal game controller mappings
9333 addGameControllerMappingToHash(mappings_hash, mapping);
9335 // save updated personal game controller mappings
9336 SaveSetup_WriteGameControllerMappings(mappings_hash, filename);
9338 freeSetupFileHash(mappings_hash);
9342 void LoadCustomElementDescriptions()
9344 char *filename = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
9345 SetupFileHash *setup_file_hash;
9348 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
9350 if (element_info[i].custom_description != NULL)
9352 free(element_info[i].custom_description);
9353 element_info[i].custom_description = NULL;
9357 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
9360 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
9362 char *token = getStringCat2(element_info[i].token_name, ".name");
9363 char *value = getHashEntry(setup_file_hash, token);
9366 element_info[i].custom_description = getStringCopy(value);
9371 freeSetupFileHash(setup_file_hash);
9374 static int getElementFromToken(char *token)
9376 char *value = getHashEntry(element_token_hash, token);
9381 Error(ERR_WARN, "unknown element token '%s'", token);
9383 return EL_UNDEFINED;
9386 static int get_token_parameter_value(char *token, char *value_raw)
9390 if (token == NULL || value_raw == NULL)
9391 return ARG_UNDEFINED_VALUE;
9393 suffix = strrchr(token, '.');
9397 if (strEqual(suffix, ".element"))
9398 return getElementFromToken(value_raw);
9400 /* !!! USE CORRECT VALUE TYPE (currently works also for TYPE_BOOLEAN) !!! */
9401 return get_parameter_value(value_raw, suffix, TYPE_INTEGER);
9404 void InitMenuDesignSettings_Static()
9408 /* always start with reliable default values from static default config */
9409 for (i = 0; image_config_vars[i].token != NULL; i++)
9411 char *value = getHashEntry(image_config_hash, image_config_vars[i].token);
9414 *image_config_vars[i].value =
9415 get_token_parameter_value(image_config_vars[i].token, value);
9419 static void InitMenuDesignSettings_SpecialPreProcessing()
9423 /* the following initializes hierarchical values from static configuration */
9425 /* special case: initialize "ARG_DEFAULT" values in static default config */
9426 /* (e.g., initialize "[titlemessage].fade_mode" from "[title].fade_mode") */
9427 titlescreen_initial_first_default.fade_mode =
9428 title_initial_first_default.fade_mode;
9429 titlescreen_initial_first_default.fade_delay =
9430 title_initial_first_default.fade_delay;
9431 titlescreen_initial_first_default.post_delay =
9432 title_initial_first_default.post_delay;
9433 titlescreen_initial_first_default.auto_delay =
9434 title_initial_first_default.auto_delay;
9435 titlescreen_first_default.fade_mode = title_first_default.fade_mode;
9436 titlescreen_first_default.fade_delay = title_first_default.fade_delay;
9437 titlescreen_first_default.post_delay = title_first_default.post_delay;
9438 titlescreen_first_default.auto_delay = title_first_default.auto_delay;
9439 titlemessage_initial_first_default.fade_mode =
9440 title_initial_first_default.fade_mode;
9441 titlemessage_initial_first_default.fade_delay =
9442 title_initial_first_default.fade_delay;
9443 titlemessage_initial_first_default.post_delay =
9444 title_initial_first_default.post_delay;
9445 titlemessage_initial_first_default.auto_delay =
9446 title_initial_first_default.auto_delay;
9447 titlemessage_first_default.fade_mode = title_first_default.fade_mode;
9448 titlemessage_first_default.fade_delay = title_first_default.fade_delay;
9449 titlemessage_first_default.post_delay = title_first_default.post_delay;
9450 titlemessage_first_default.auto_delay = title_first_default.auto_delay;
9452 titlescreen_initial_default.fade_mode = title_initial_default.fade_mode;
9453 titlescreen_initial_default.fade_delay = title_initial_default.fade_delay;
9454 titlescreen_initial_default.post_delay = title_initial_default.post_delay;
9455 titlescreen_initial_default.auto_delay = title_initial_default.auto_delay;
9456 titlescreen_default.fade_mode = title_default.fade_mode;
9457 titlescreen_default.fade_delay = title_default.fade_delay;
9458 titlescreen_default.post_delay = title_default.post_delay;
9459 titlescreen_default.auto_delay = title_default.auto_delay;
9460 titlemessage_initial_default.fade_mode = title_initial_default.fade_mode;
9461 titlemessage_initial_default.fade_delay = title_initial_default.fade_delay;
9462 titlemessage_initial_default.post_delay = title_initial_default.post_delay;
9463 titlemessage_initial_default.auto_delay = title_initial_default.auto_delay;
9464 titlemessage_default.fade_mode = title_default.fade_mode;
9465 titlemessage_default.fade_delay = title_default.fade_delay;
9466 titlemessage_default.post_delay = title_default.post_delay;
9467 titlemessage_default.auto_delay = title_default.auto_delay;
9469 /* special case: initialize "ARG_DEFAULT" values in static default config */
9470 /* (e.g., init "titlemessage_1.fade_mode" from "[titlemessage].fade_mode") */
9471 for (i = 0; i < MAX_NUM_TITLE_MESSAGES; i++)
9473 titlescreen_initial_first[i] = titlescreen_initial_first_default;
9474 titlescreen_first[i] = titlescreen_first_default;
9475 titlemessage_initial_first[i] = titlemessage_initial_first_default;
9476 titlemessage_first[i] = titlemessage_first_default;
9478 titlescreen_initial[i] = titlescreen_initial_default;
9479 titlescreen[i] = titlescreen_default;
9480 titlemessage_initial[i] = titlemessage_initial_default;
9481 titlemessage[i] = titlemessage_default;
9484 /* special case: initialize "ARG_DEFAULT" values in static default config */
9485 /* (eg, init "menu.enter_screen.SCORES.xyz" from "menu.enter_screen.xyz") */
9486 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
9488 if (i == GFX_SPECIAL_ARG_TITLE) /* title values already initialized */
9491 menu.enter_screen[i] = menu.enter_screen[GFX_SPECIAL_ARG_DEFAULT];
9492 menu.leave_screen[i] = menu.leave_screen[GFX_SPECIAL_ARG_DEFAULT];
9493 menu.next_screen[i] = menu.next_screen[GFX_SPECIAL_ARG_DEFAULT];
9496 /* special case: initialize "ARG_DEFAULT" values in static default config */
9497 /* (eg, init "viewport.door_1.MAIN.xyz" from "viewport.door_1.xyz") */
9498 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
9500 viewport.window[i] = viewport.window[GFX_SPECIAL_ARG_DEFAULT];
9501 viewport.playfield[i] = viewport.playfield[GFX_SPECIAL_ARG_DEFAULT];
9502 viewport.door_1[i] = viewport.door_1[GFX_SPECIAL_ARG_DEFAULT];
9504 if (i == GFX_SPECIAL_ARG_EDITOR) /* editor values already initialized */
9507 viewport.door_2[i] = viewport.door_2[GFX_SPECIAL_ARG_DEFAULT];
9511 static void InitMenuDesignSettings_SpecialPostProcessing()
9515 struct XY *dst, *src;
9519 { &game.button.save, &game.button.stop },
9520 { &game.button.pause2, &game.button.pause },
9521 { &game.button.load, &game.button.play },
9522 { &game.button.undo, &game.button.stop },
9523 { &game.button.redo, &game.button.play },
9529 /* special case: initialize later added SETUP list size from LEVELS value */
9530 if (menu.list_size[GAME_MODE_SETUP] == -1)
9531 menu.list_size[GAME_MODE_SETUP] = menu.list_size[GAME_MODE_LEVELS];
9533 /* set default position for snapshot buttons to stop/pause/play buttons */
9534 for (i = 0; game_buttons_xy[i].dst != NULL; i++)
9535 if ((*game_buttons_xy[i].dst).x == -1 &&
9536 (*game_buttons_xy[i].dst).y == -1)
9537 *game_buttons_xy[i].dst = *game_buttons_xy[i].src;
9540 static void InitMenuDesignSettings_SpecialPostProcessing_AfterGraphics()
9544 struct XYTileSize *dst, *src;
9547 editor_buttons_xy[] =
9550 &editor.button.element_left, &editor.palette.element_left,
9551 IMG_GFX_EDITOR_BUTTON_ELEMENT_LEFT
9554 &editor.button.element_middle, &editor.palette.element_middle,
9555 IMG_GFX_EDITOR_BUTTON_ELEMENT_MIDDLE
9558 &editor.button.element_right, &editor.palette.element_right,
9559 IMG_GFX_EDITOR_BUTTON_ELEMENT_RIGHT
9566 /* set default position for element buttons to element graphics */
9567 for (i = 0; editor_buttons_xy[i].dst != NULL; i++)
9569 if ((*editor_buttons_xy[i].dst).x == -1 &&
9570 (*editor_buttons_xy[i].dst).y == -1)
9572 struct GraphicInfo *gd = &graphic_info[editor_buttons_xy[i].graphic];
9574 gd->width = gd->height = editor_buttons_xy[i].src->tile_size;
9576 *editor_buttons_xy[i].dst = *editor_buttons_xy[i].src;
9581 static void LoadMenuDesignSettingsFromFilename(char *filename)
9583 static struct TitleFadingInfo tfi;
9584 static struct TitleMessageInfo tmi;
9585 static struct TokenInfo title_tokens[] =
9587 { TYPE_INTEGER, &tfi.fade_mode, ".fade_mode" },
9588 { TYPE_INTEGER, &tfi.fade_delay, ".fade_delay" },
9589 { TYPE_INTEGER, &tfi.post_delay, ".post_delay" },
9590 { TYPE_INTEGER, &tfi.auto_delay, ".auto_delay" },
9594 static struct TokenInfo titlemessage_tokens[] =
9596 { TYPE_INTEGER, &tmi.x, ".x" },
9597 { TYPE_INTEGER, &tmi.y, ".y" },
9598 { TYPE_INTEGER, &tmi.width, ".width" },
9599 { TYPE_INTEGER, &tmi.height, ".height" },
9600 { TYPE_INTEGER, &tmi.chars, ".chars" },
9601 { TYPE_INTEGER, &tmi.lines, ".lines" },
9602 { TYPE_INTEGER, &tmi.align, ".align" },
9603 { TYPE_INTEGER, &tmi.valign, ".valign" },
9604 { TYPE_INTEGER, &tmi.font, ".font" },
9605 { TYPE_BOOLEAN, &tmi.autowrap, ".autowrap" },
9606 { TYPE_BOOLEAN, &tmi.centered, ".centered" },
9607 { TYPE_BOOLEAN, &tmi.parse_comments, ".parse_comments" },
9608 { TYPE_INTEGER, &tmi.sort_priority, ".sort_priority" },
9609 { TYPE_INTEGER, &tmi.fade_mode, ".fade_mode" },
9610 { TYPE_INTEGER, &tmi.fade_delay, ".fade_delay" },
9611 { TYPE_INTEGER, &tmi.post_delay, ".post_delay" },
9612 { TYPE_INTEGER, &tmi.auto_delay, ".auto_delay" },
9618 struct TitleFadingInfo *info;
9623 /* initialize first titles from "enter screen" definitions, if defined */
9624 { &title_initial_first_default, "menu.enter_screen.TITLE" },
9625 { &title_first_default, "menu.enter_screen.TITLE" },
9627 /* initialize title screens from "next screen" definitions, if defined */
9628 { &title_initial_default, "menu.next_screen.TITLE" },
9629 { &title_default, "menu.next_screen.TITLE" },
9635 struct TitleMessageInfo *array;
9638 titlemessage_arrays[] =
9640 /* initialize first titles from "enter screen" definitions, if defined */
9641 { titlescreen_initial_first, "menu.enter_screen.TITLE" },
9642 { titlescreen_first, "menu.enter_screen.TITLE" },
9643 { titlemessage_initial_first, "menu.enter_screen.TITLE" },
9644 { titlemessage_first, "menu.enter_screen.TITLE" },
9646 /* initialize titles from "next screen" definitions, if defined */
9647 { titlescreen_initial, "menu.next_screen.TITLE" },
9648 { titlescreen, "menu.next_screen.TITLE" },
9649 { titlemessage_initial, "menu.next_screen.TITLE" },
9650 { titlemessage, "menu.next_screen.TITLE" },
9652 /* overwrite titles with title definitions, if defined */
9653 { titlescreen_initial_first, "[title_initial]" },
9654 { titlescreen_first, "[title]" },
9655 { titlemessage_initial_first, "[title_initial]" },
9656 { titlemessage_first, "[title]" },
9658 { titlescreen_initial, "[title_initial]" },
9659 { titlescreen, "[title]" },
9660 { titlemessage_initial, "[title_initial]" },
9661 { titlemessage, "[title]" },
9663 /* overwrite titles with title screen/message definitions, if defined */
9664 { titlescreen_initial_first, "[titlescreen_initial]" },
9665 { titlescreen_first, "[titlescreen]" },
9666 { titlemessage_initial_first, "[titlemessage_initial]" },
9667 { titlemessage_first, "[titlemessage]" },
9669 { titlescreen_initial, "[titlescreen_initial]" },
9670 { titlescreen, "[titlescreen]" },
9671 { titlemessage_initial, "[titlemessage_initial]" },
9672 { titlemessage, "[titlemessage]" },
9676 SetupFileHash *setup_file_hash;
9679 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
9682 /* the following initializes hierarchical values from dynamic configuration */
9684 /* special case: initialize with default values that may be overwritten */
9685 /* (e.g., init "menu.draw_xoffset.INFO" from "menu.draw_xoffset") */
9686 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
9688 char *value_1 = getHashEntry(setup_file_hash, "menu.draw_xoffset");
9689 char *value_2 = getHashEntry(setup_file_hash, "menu.draw_yoffset");
9690 char *value_3 = getHashEntry(setup_file_hash, "menu.list_size");
9692 if (value_1 != NULL)
9693 menu.draw_xoffset[i] = get_integer_from_string(value_1);
9694 if (value_2 != NULL)
9695 menu.draw_yoffset[i] = get_integer_from_string(value_2);
9696 if (value_3 != NULL)
9697 menu.list_size[i] = get_integer_from_string(value_3);
9700 /* special case: initialize with default values that may be overwritten */
9701 /* (eg, init "menu.draw_xoffset.INFO[XXX]" from "menu.draw_xoffset.INFO") */
9702 for (i = 0; i < NUM_SPECIAL_GFX_INFO_ARGS; i++)
9704 char *value_1 = getHashEntry(setup_file_hash, "menu.draw_xoffset.INFO");
9705 char *value_2 = getHashEntry(setup_file_hash, "menu.draw_yoffset.INFO");
9707 if (value_1 != NULL)
9708 menu.draw_xoffset_info[i] = get_integer_from_string(value_1);
9709 if (value_2 != NULL)
9710 menu.draw_yoffset_info[i] = get_integer_from_string(value_2);
9712 if (i == GFX_SPECIAL_ARG_INFO_ELEMENTS)
9714 char *value_1 = getHashEntry(setup_file_hash, "menu.list_size.INFO");
9716 if (value_1 != NULL)
9717 menu.list_size_info[i] = get_integer_from_string(value_1);
9721 /* special case: initialize with default values that may be overwritten */
9722 /* (eg, init "menu.draw_xoffset.SETUP[XXX]" from "menu.draw_xoffset.SETUP") */
9723 for (i = 0; i < NUM_SPECIAL_GFX_SETUP_ARGS; i++)
9725 char *value_1 = getHashEntry(setup_file_hash, "menu.draw_xoffset.SETUP");
9726 char *value_2 = getHashEntry(setup_file_hash, "menu.draw_yoffset.SETUP");
9728 if (value_1 != NULL)
9729 menu.draw_xoffset_setup[i] = get_integer_from_string(value_1);
9730 if (value_2 != NULL)
9731 menu.draw_yoffset_setup[i] = get_integer_from_string(value_2);
9734 /* special case: initialize with default values that may be overwritten */
9735 /* (eg, init "menu.enter_screen.SCORES.xyz" from "menu.enter_screen.xyz") */
9736 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
9738 char *token_1 = "menu.enter_screen.fade_mode";
9739 char *token_2 = "menu.enter_screen.fade_delay";
9740 char *token_3 = "menu.enter_screen.post_delay";
9741 char *token_4 = "menu.leave_screen.fade_mode";
9742 char *token_5 = "menu.leave_screen.fade_delay";
9743 char *token_6 = "menu.leave_screen.post_delay";
9744 char *token_7 = "menu.next_screen.fade_mode";
9745 char *token_8 = "menu.next_screen.fade_delay";
9746 char *token_9 = "menu.next_screen.post_delay";
9747 char *value_1 = getHashEntry(setup_file_hash, token_1);
9748 char *value_2 = getHashEntry(setup_file_hash, token_2);
9749 char *value_3 = getHashEntry(setup_file_hash, token_3);
9750 char *value_4 = getHashEntry(setup_file_hash, token_4);
9751 char *value_5 = getHashEntry(setup_file_hash, token_5);
9752 char *value_6 = getHashEntry(setup_file_hash, token_6);
9753 char *value_7 = getHashEntry(setup_file_hash, token_7);
9754 char *value_8 = getHashEntry(setup_file_hash, token_8);
9755 char *value_9 = getHashEntry(setup_file_hash, token_9);
9757 if (value_1 != NULL)
9758 menu.enter_screen[i].fade_mode = get_token_parameter_value(token_1,
9760 if (value_2 != NULL)
9761 menu.enter_screen[i].fade_delay = get_token_parameter_value(token_2,
9763 if (value_3 != NULL)
9764 menu.enter_screen[i].post_delay = get_token_parameter_value(token_3,
9766 if (value_4 != NULL)
9767 menu.leave_screen[i].fade_mode = get_token_parameter_value(token_4,
9769 if (value_5 != NULL)
9770 menu.leave_screen[i].fade_delay = get_token_parameter_value(token_5,
9772 if (value_6 != NULL)
9773 menu.leave_screen[i].post_delay = get_token_parameter_value(token_6,
9775 if (value_7 != NULL)
9776 menu.next_screen[i].fade_mode = get_token_parameter_value(token_7,
9778 if (value_8 != NULL)
9779 menu.next_screen[i].fade_delay = get_token_parameter_value(token_8,
9781 if (value_9 != NULL)
9782 menu.next_screen[i].post_delay = get_token_parameter_value(token_9,
9786 /* special case: initialize with default values that may be overwritten */
9787 /* (eg, init "viewport.door_1.MAIN.xyz" from "viewport.door_1.xyz") */
9788 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
9790 char *token_w1 = "viewport.window.width";
9791 char *token_w2 = "viewport.window.height";
9792 char *token_01 = "viewport.playfield.x";
9793 char *token_02 = "viewport.playfield.y";
9794 char *token_03 = "viewport.playfield.width";
9795 char *token_04 = "viewport.playfield.height";
9796 char *token_05 = "viewport.playfield.border_size";
9797 char *token_06 = "viewport.door_1.x";
9798 char *token_07 = "viewport.door_1.y";
9799 char *token_08 = "viewport.door_1.width";
9800 char *token_09 = "viewport.door_1.height";
9801 char *token_10 = "viewport.door_1.border_size";
9802 char *token_11 = "viewport.door_2.x";
9803 char *token_12 = "viewport.door_2.y";
9804 char *token_13 = "viewport.door_2.width";
9805 char *token_14 = "viewport.door_2.height";
9806 char *token_15 = "viewport.door_2.border_size";
9807 char *value_w1 = getHashEntry(setup_file_hash, token_w1);
9808 char *value_w2 = getHashEntry(setup_file_hash, token_w2);
9809 char *value_01 = getHashEntry(setup_file_hash, token_01);
9810 char *value_02 = getHashEntry(setup_file_hash, token_02);
9811 char *value_03 = getHashEntry(setup_file_hash, token_03);
9812 char *value_04 = getHashEntry(setup_file_hash, token_04);
9813 char *value_05 = getHashEntry(setup_file_hash, token_05);
9814 char *value_06 = getHashEntry(setup_file_hash, token_06);
9815 char *value_07 = getHashEntry(setup_file_hash, token_07);
9816 char *value_08 = getHashEntry(setup_file_hash, token_08);
9817 char *value_09 = getHashEntry(setup_file_hash, token_09);
9818 char *value_10 = getHashEntry(setup_file_hash, token_10);
9819 char *value_11 = getHashEntry(setup_file_hash, token_11);
9820 char *value_12 = getHashEntry(setup_file_hash, token_12);
9821 char *value_13 = getHashEntry(setup_file_hash, token_13);
9822 char *value_14 = getHashEntry(setup_file_hash, token_14);
9823 char *value_15 = getHashEntry(setup_file_hash, token_15);
9825 if (value_w1 != NULL)
9826 viewport.window[i].width = get_token_parameter_value(token_w1, value_w1);
9827 if (value_w2 != NULL)
9828 viewport.window[i].height = get_token_parameter_value(token_w2, value_w2);
9829 if (value_01 != NULL)
9830 viewport.playfield[i].x = get_token_parameter_value(token_01, value_01);
9831 if (value_02 != NULL)
9832 viewport.playfield[i].y = get_token_parameter_value(token_02, value_02);
9833 if (value_03 != NULL)
9834 viewport.playfield[i].width = get_token_parameter_value(token_03,
9836 if (value_04 != NULL)
9837 viewport.playfield[i].height = get_token_parameter_value(token_04,
9839 if (value_05 != NULL)
9840 viewport.playfield[i].border_size = get_token_parameter_value(token_05,
9842 if (value_06 != NULL)
9843 viewport.door_1[i].x = get_token_parameter_value(token_06, value_06);
9844 if (value_07 != NULL)
9845 viewport.door_1[i].y = get_token_parameter_value(token_07, value_07);
9846 if (value_08 != NULL)
9847 viewport.door_1[i].width = get_token_parameter_value(token_08, value_08);
9848 if (value_09 != NULL)
9849 viewport.door_1[i].height = get_token_parameter_value(token_09, value_09);
9850 if (value_10 != NULL)
9851 viewport.door_1[i].border_size = get_token_parameter_value(token_10,
9853 if (value_11 != NULL)
9854 viewport.door_2[i].x = get_token_parameter_value(token_11, value_11);
9855 if (value_12 != NULL)
9856 viewport.door_2[i].y = get_token_parameter_value(token_12, value_12);
9857 if (value_13 != NULL)
9858 viewport.door_2[i].width = get_token_parameter_value(token_13, value_13);
9859 if (value_14 != NULL)
9860 viewport.door_2[i].height = get_token_parameter_value(token_14, value_14);
9861 if (value_15 != NULL)
9862 viewport.door_1[i].border_size = get_token_parameter_value(token_15,
9866 /* special case: initialize with default values that may be overwritten */
9867 /* (e.g., init "[title].fade_mode" from "menu.next_screen.TITLE.fade_mode") */
9868 for (i = 0; title_info[i].info != NULL; i++)
9870 struct TitleFadingInfo *info = title_info[i].info;
9871 char *base_token = title_info[i].text;
9873 for (j = 0; title_tokens[j].type != -1; j++)
9875 char *token = getStringCat2(base_token, title_tokens[j].text);
9876 char *value = getHashEntry(setup_file_hash, token);
9880 int parameter_value = get_token_parameter_value(token, value);
9884 *(int *)title_tokens[j].value = (int)parameter_value;
9893 /* special case: initialize with default values that may be overwritten */
9894 /* (e.g., init "titlemessage_1.fade_mode" from "[titlemessage].fade_mode") */
9895 for (i = 0; titlemessage_arrays[i].array != NULL; i++)
9897 struct TitleMessageInfo *array = titlemessage_arrays[i].array;
9898 char *base_token = titlemessage_arrays[i].text;
9900 for (j = 0; titlemessage_tokens[j].type != -1; j++)
9902 char *token = getStringCat2(base_token, titlemessage_tokens[j].text);
9903 char *value = getHashEntry(setup_file_hash, token);
9907 int parameter_value = get_token_parameter_value(token, value);
9909 for (k = 0; k < MAX_NUM_TITLE_MESSAGES; k++)
9913 if (titlemessage_tokens[j].type == TYPE_INTEGER)
9914 *(int *)titlemessage_tokens[j].value = (int)parameter_value;
9916 *(boolean *)titlemessage_tokens[j].value = (boolean)parameter_value;
9926 /* read (and overwrite with) values that may be specified in config file */
9927 for (i = 0; image_config_vars[i].token != NULL; i++)
9929 char *value = getHashEntry(setup_file_hash, image_config_vars[i].token);
9931 /* (ignore definitions set to "[DEFAULT]" which are already initialized) */
9932 if (value != NULL && !strEqual(value, ARG_DEFAULT))
9933 *image_config_vars[i].value =
9934 get_token_parameter_value(image_config_vars[i].token, value);
9937 freeSetupFileHash(setup_file_hash);
9940 void LoadMenuDesignSettings()
9942 char *filename_base = UNDEFINED_FILENAME, *filename_local;
9944 InitMenuDesignSettings_Static();
9945 InitMenuDesignSettings_SpecialPreProcessing();
9947 if (!GFX_OVERRIDE_ARTWORK(ARTWORK_TYPE_GRAPHICS))
9949 /* first look for special settings configured in level series config */
9950 filename_base = getCustomArtworkLevelConfigFilename(ARTWORK_TYPE_GRAPHICS);
9952 if (fileExists(filename_base))
9953 LoadMenuDesignSettingsFromFilename(filename_base);
9956 filename_local = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
9958 if (filename_local != NULL && !strEqual(filename_base, filename_local))
9959 LoadMenuDesignSettingsFromFilename(filename_local);
9961 InitMenuDesignSettings_SpecialPostProcessing();
9964 void LoadMenuDesignSettings_AfterGraphics()
9966 InitMenuDesignSettings_SpecialPostProcessing_AfterGraphics();
9969 void LoadUserDefinedEditorElementList(int **elements, int *num_elements)
9971 char *filename = getEditorSetupFilename();
9972 SetupFileList *setup_file_list, *list;
9973 SetupFileHash *element_hash;
9974 int num_unknown_tokens = 0;
9977 if ((setup_file_list = loadSetupFileList(filename)) == NULL)
9980 element_hash = newSetupFileHash();
9982 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
9983 setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
9985 /* determined size may be larger than needed (due to unknown elements) */
9987 for (list = setup_file_list; list != NULL; list = list->next)
9990 /* add space for up to 3 more elements for padding that may be needed */
9993 /* free memory for old list of elements, if needed */
9994 checked_free(*elements);
9996 /* allocate memory for new list of elements */
9997 *elements = checked_malloc(*num_elements * sizeof(int));
10000 for (list = setup_file_list; list != NULL; list = list->next)
10002 char *value = getHashEntry(element_hash, list->token);
10004 if (value == NULL) /* try to find obsolete token mapping */
10006 char *mapped_token = get_mapped_token(list->token);
10008 if (mapped_token != NULL)
10010 value = getHashEntry(element_hash, mapped_token);
10012 free(mapped_token);
10018 (*elements)[(*num_elements)++] = atoi(value);
10022 if (num_unknown_tokens == 0)
10024 Error(ERR_INFO_LINE, "-");
10025 Error(ERR_INFO, "warning: unknown token(s) found in config file:");
10026 Error(ERR_INFO, "- config file: '%s'", filename);
10028 num_unknown_tokens++;
10031 Error(ERR_INFO, "- token: '%s'", list->token);
10035 if (num_unknown_tokens > 0)
10036 Error(ERR_INFO_LINE, "-");
10038 while (*num_elements % 4) /* pad with empty elements, if needed */
10039 (*elements)[(*num_elements)++] = EL_EMPTY;
10041 freeSetupFileList(setup_file_list);
10042 freeSetupFileHash(element_hash);
10045 for (i = 0; i < *num_elements; i++)
10046 printf("editor: element '%s' [%d]\n",
10047 element_info[(*elements)[i]].token_name, (*elements)[i]);
10051 static struct MusicFileInfo *get_music_file_info_ext(char *basename, int music,
10054 SetupFileHash *setup_file_hash = NULL;
10055 struct MusicFileInfo tmp_music_file_info, *new_music_file_info;
10056 char *filename_music, *filename_prefix, *filename_info;
10062 token_to_value_ptr[] =
10064 { "title_header", &tmp_music_file_info.title_header },
10065 { "artist_header", &tmp_music_file_info.artist_header },
10066 { "album_header", &tmp_music_file_info.album_header },
10067 { "year_header", &tmp_music_file_info.year_header },
10069 { "title", &tmp_music_file_info.title },
10070 { "artist", &tmp_music_file_info.artist },
10071 { "album", &tmp_music_file_info.album },
10072 { "year", &tmp_music_file_info.year },
10078 filename_music = (is_sound ? getCustomSoundFilename(basename) :
10079 getCustomMusicFilename(basename));
10081 if (filename_music == NULL)
10084 /* ---------- try to replace file extension ---------- */
10086 filename_prefix = getStringCopy(filename_music);
10087 if (strrchr(filename_prefix, '.') != NULL)
10088 *strrchr(filename_prefix, '.') = '\0';
10089 filename_info = getStringCat2(filename_prefix, ".txt");
10091 if (fileExists(filename_info))
10092 setup_file_hash = loadSetupFileHash(filename_info);
10094 free(filename_prefix);
10095 free(filename_info);
10097 if (setup_file_hash == NULL)
10099 /* ---------- try to add file extension ---------- */
10101 filename_prefix = getStringCopy(filename_music);
10102 filename_info = getStringCat2(filename_prefix, ".txt");
10104 if (fileExists(filename_info))
10105 setup_file_hash = loadSetupFileHash(filename_info);
10107 free(filename_prefix);
10108 free(filename_info);
10111 if (setup_file_hash == NULL)
10114 /* ---------- music file info found ---------- */
10116 clear_mem(&tmp_music_file_info, sizeof(struct MusicFileInfo));
10118 for (i = 0; token_to_value_ptr[i].token != NULL; i++)
10120 char *value = getHashEntry(setup_file_hash, token_to_value_ptr[i].token);
10122 *token_to_value_ptr[i].value_ptr =
10123 getStringCopy(value != NULL && *value != '\0' ? value : UNKNOWN_NAME);
10126 tmp_music_file_info.basename = getStringCopy(basename);
10127 tmp_music_file_info.music = music;
10128 tmp_music_file_info.is_sound = is_sound;
10130 new_music_file_info = checked_malloc(sizeof(struct MusicFileInfo));
10131 *new_music_file_info = tmp_music_file_info;
10133 return new_music_file_info;
10136 static struct MusicFileInfo *get_music_file_info(char *basename, int music)
10138 return get_music_file_info_ext(basename, music, FALSE);
10141 static struct MusicFileInfo *get_sound_file_info(char *basename, int sound)
10143 return get_music_file_info_ext(basename, sound, TRUE);
10146 static boolean music_info_listed_ext(struct MusicFileInfo *list,
10147 char *basename, boolean is_sound)
10149 for (; list != NULL; list = list->next)
10150 if (list->is_sound == is_sound && strEqual(list->basename, basename))
10156 static boolean music_info_listed(struct MusicFileInfo *list, char *basename)
10158 return music_info_listed_ext(list, basename, FALSE);
10161 static boolean sound_info_listed(struct MusicFileInfo *list, char *basename)
10163 return music_info_listed_ext(list, basename, TRUE);
10166 void LoadMusicInfo()
10168 char *music_directory = getCustomMusicDirectory();
10169 int num_music = getMusicListSize();
10170 int num_music_noconf = 0;
10171 int num_sounds = getSoundListSize();
10173 DirectoryEntry *dir_entry;
10174 struct FileInfo *music, *sound;
10175 struct MusicFileInfo *next, **new;
10178 while (music_file_info != NULL)
10180 next = music_file_info->next;
10182 checked_free(music_file_info->basename);
10184 checked_free(music_file_info->title_header);
10185 checked_free(music_file_info->artist_header);
10186 checked_free(music_file_info->album_header);
10187 checked_free(music_file_info->year_header);
10189 checked_free(music_file_info->title);
10190 checked_free(music_file_info->artist);
10191 checked_free(music_file_info->album);
10192 checked_free(music_file_info->year);
10194 free(music_file_info);
10196 music_file_info = next;
10199 new = &music_file_info;
10201 for (i = 0; i < num_music; i++)
10203 music = getMusicListEntry(i);
10205 if (music->filename == NULL)
10208 if (strEqual(music->filename, UNDEFINED_FILENAME))
10211 /* a configured file may be not recognized as music */
10212 if (!FileIsMusic(music->filename))
10215 if (!music_info_listed(music_file_info, music->filename))
10217 *new = get_music_file_info(music->filename, i);
10220 new = &(*new)->next;
10224 if ((dir = openDirectory(music_directory)) == NULL)
10226 Error(ERR_WARN, "cannot read music directory '%s'", music_directory);
10230 while ((dir_entry = readDirectory(dir)) != NULL) /* loop all entries */
10232 char *basename = dir_entry->basename;
10233 boolean music_already_used = FALSE;
10236 /* skip all music files that are configured in music config file */
10237 for (i = 0; i < num_music; i++)
10239 music = getMusicListEntry(i);
10241 if (music->filename == NULL)
10244 if (strEqual(basename, music->filename))
10246 music_already_used = TRUE;
10251 if (music_already_used)
10254 if (!FileIsMusic(dir_entry->filename))
10257 if (!music_info_listed(music_file_info, basename))
10259 *new = get_music_file_info(basename, MAP_NOCONF_MUSIC(num_music_noconf));
10262 new = &(*new)->next;
10265 num_music_noconf++;
10268 closeDirectory(dir);
10270 for (i = 0; i < num_sounds; i++)
10272 sound = getSoundListEntry(i);
10274 if (sound->filename == NULL)
10277 if (strEqual(sound->filename, UNDEFINED_FILENAME))
10280 /* a configured file may be not recognized as sound */
10281 if (!FileIsSound(sound->filename))
10284 if (!sound_info_listed(music_file_info, sound->filename))
10286 *new = get_sound_file_info(sound->filename, i);
10288 new = &(*new)->next;
10293 void add_helpanim_entry(int element, int action, int direction, int delay,
10294 int *num_list_entries)
10296 struct HelpAnimInfo *new_list_entry;
10297 (*num_list_entries)++;
10300 checked_realloc(helpanim_info,
10301 *num_list_entries * sizeof(struct HelpAnimInfo));
10302 new_list_entry = &helpanim_info[*num_list_entries - 1];
10304 new_list_entry->element = element;
10305 new_list_entry->action = action;
10306 new_list_entry->direction = direction;
10307 new_list_entry->delay = delay;
10310 void print_unknown_token(char *filename, char *token, int token_nr)
10314 Error(ERR_INFO_LINE, "-");
10315 Error(ERR_INFO, "warning: unknown token(s) found in config file:");
10316 Error(ERR_INFO, "- config file: '%s'", filename);
10319 Error(ERR_INFO, "- token: '%s'", token);
10322 void print_unknown_token_end(int token_nr)
10325 Error(ERR_INFO_LINE, "-");
10328 void LoadHelpAnimInfo()
10330 char *filename = getHelpAnimFilename();
10331 SetupFileList *setup_file_list = NULL, *list;
10332 SetupFileHash *element_hash, *action_hash, *direction_hash;
10333 int num_list_entries = 0;
10334 int num_unknown_tokens = 0;
10337 if (fileExists(filename))
10338 setup_file_list = loadSetupFileList(filename);
10340 if (setup_file_list == NULL)
10342 /* use reliable default values from static configuration */
10343 SetupFileList *insert_ptr;
10345 insert_ptr = setup_file_list =
10346 newSetupFileList(helpanim_config[0].token,
10347 helpanim_config[0].value);
10349 for (i = 1; helpanim_config[i].token; i++)
10350 insert_ptr = addListEntry(insert_ptr,
10351 helpanim_config[i].token,
10352 helpanim_config[i].value);
10355 element_hash = newSetupFileHash();
10356 action_hash = newSetupFileHash();
10357 direction_hash = newSetupFileHash();
10359 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
10360 setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
10362 for (i = 0; i < NUM_ACTIONS; i++)
10363 setHashEntry(action_hash, element_action_info[i].suffix,
10364 i_to_a(element_action_info[i].value));
10366 /* do not store direction index (bit) here, but direction value! */
10367 for (i = 0; i < NUM_DIRECTIONS_FULL; i++)
10368 setHashEntry(direction_hash, element_direction_info[i].suffix,
10369 i_to_a(1 << element_direction_info[i].value));
10371 for (list = setup_file_list; list != NULL; list = list->next)
10373 char *element_token, *action_token, *direction_token;
10374 char *element_value, *action_value, *direction_value;
10375 int delay = atoi(list->value);
10377 if (strEqual(list->token, "end"))
10379 add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
10384 /* first try to break element into element/action/direction parts;
10385 if this does not work, also accept combined "element[.act][.dir]"
10386 elements (like "dynamite.active"), which are unique elements */
10388 if (strchr(list->token, '.') == NULL) /* token contains no '.' */
10390 element_value = getHashEntry(element_hash, list->token);
10391 if (element_value != NULL) /* element found */
10392 add_helpanim_entry(atoi(element_value), -1, -1, delay,
10393 &num_list_entries);
10396 /* no further suffixes found -- this is not an element */
10397 print_unknown_token(filename, list->token, num_unknown_tokens++);
10403 /* token has format "<prefix>.<something>" */
10405 action_token = strchr(list->token, '.'); /* suffix may be action ... */
10406 direction_token = action_token; /* ... or direction */
10408 element_token = getStringCopy(list->token);
10409 *strchr(element_token, '.') = '\0';
10411 element_value = getHashEntry(element_hash, element_token);
10413 if (element_value == NULL) /* this is no element */
10415 element_value = getHashEntry(element_hash, list->token);
10416 if (element_value != NULL) /* combined element found */
10417 add_helpanim_entry(atoi(element_value), -1, -1, delay,
10418 &num_list_entries);
10420 print_unknown_token(filename, list->token, num_unknown_tokens++);
10422 free(element_token);
10427 action_value = getHashEntry(action_hash, action_token);
10429 if (action_value != NULL) /* action found */
10431 add_helpanim_entry(atoi(element_value), atoi(action_value), -1, delay,
10432 &num_list_entries);
10434 free(element_token);
10439 direction_value = getHashEntry(direction_hash, direction_token);
10441 if (direction_value != NULL) /* direction found */
10443 add_helpanim_entry(atoi(element_value), -1, atoi(direction_value), delay,
10444 &num_list_entries);
10446 free(element_token);
10451 if (strchr(action_token + 1, '.') == NULL)
10453 /* no further suffixes found -- this is not an action nor direction */
10455 element_value = getHashEntry(element_hash, list->token);
10456 if (element_value != NULL) /* combined element found */
10457 add_helpanim_entry(atoi(element_value), -1, -1, delay,
10458 &num_list_entries);
10460 print_unknown_token(filename, list->token, num_unknown_tokens++);
10462 free(element_token);
10467 /* token has format "<prefix>.<suffix>.<something>" */
10469 direction_token = strchr(action_token + 1, '.');
10471 action_token = getStringCopy(action_token);
10472 *strchr(action_token + 1, '.') = '\0';
10474 action_value = getHashEntry(action_hash, action_token);
10476 if (action_value == NULL) /* this is no action */
10478 element_value = getHashEntry(element_hash, list->token);
10479 if (element_value != NULL) /* combined element found */
10480 add_helpanim_entry(atoi(element_value), -1, -1, delay,
10481 &num_list_entries);
10483 print_unknown_token(filename, list->token, num_unknown_tokens++);
10485 free(element_token);
10486 free(action_token);
10491 direction_value = getHashEntry(direction_hash, direction_token);
10493 if (direction_value != NULL) /* direction found */
10495 add_helpanim_entry(atoi(element_value), atoi(action_value),
10496 atoi(direction_value), delay, &num_list_entries);
10498 free(element_token);
10499 free(action_token);
10504 /* this is no direction */
10506 element_value = getHashEntry(element_hash, list->token);
10507 if (element_value != NULL) /* combined element found */
10508 add_helpanim_entry(atoi(element_value), -1, -1, delay,
10509 &num_list_entries);
10511 print_unknown_token(filename, list->token, num_unknown_tokens++);
10513 free(element_token);
10514 free(action_token);
10517 print_unknown_token_end(num_unknown_tokens);
10519 add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
10520 add_helpanim_entry(HELPANIM_LIST_END, -1, -1, -1, &num_list_entries);
10522 freeSetupFileList(setup_file_list);
10523 freeSetupFileHash(element_hash);
10524 freeSetupFileHash(action_hash);
10525 freeSetupFileHash(direction_hash);
10528 for (i = 0; i < num_list_entries; i++)
10529 printf("::: '%s': %d, %d, %d => %d\n",
10530 EL_NAME(helpanim_info[i].element),
10531 helpanim_info[i].element,
10532 helpanim_info[i].action,
10533 helpanim_info[i].direction,
10534 helpanim_info[i].delay);
10538 void LoadHelpTextInfo()
10540 char *filename = getHelpTextFilename();
10543 if (helptext_info != NULL)
10545 freeSetupFileHash(helptext_info);
10546 helptext_info = NULL;
10549 if (fileExists(filename))
10550 helptext_info = loadSetupFileHash(filename);
10552 if (helptext_info == NULL)
10554 /* use reliable default values from static configuration */
10555 helptext_info = newSetupFileHash();
10557 for (i = 0; helptext_config[i].token; i++)
10558 setHashEntry(helptext_info,
10559 helptext_config[i].token,
10560 helptext_config[i].value);
10564 BEGIN_HASH_ITERATION(helptext_info, itr)
10566 printf("::: '%s' => '%s'\n",
10567 HASH_ITERATION_TOKEN(itr), HASH_ITERATION_VALUE(itr));
10569 END_HASH_ITERATION(hash, itr)
10574 /* ------------------------------------------------------------------------- */
10575 /* convert levels */
10576 /* ------------------------------------------------------------------------- */
10578 #define MAX_NUM_CONVERT_LEVELS 1000
10580 void ConvertLevels()
10582 static LevelDirTree *convert_leveldir = NULL;
10583 static int convert_level_nr = -1;
10584 static int num_levels_handled = 0;
10585 static int num_levels_converted = 0;
10586 static boolean levels_failed[MAX_NUM_CONVERT_LEVELS];
10589 convert_leveldir = getTreeInfoFromIdentifier(leveldir_first,
10590 global.convert_leveldir);
10592 if (convert_leveldir == NULL)
10593 Error(ERR_EXIT, "no such level identifier: '%s'",
10594 global.convert_leveldir);
10596 leveldir_current = convert_leveldir;
10598 if (global.convert_level_nr != -1)
10600 convert_leveldir->first_level = global.convert_level_nr;
10601 convert_leveldir->last_level = global.convert_level_nr;
10604 convert_level_nr = convert_leveldir->first_level;
10606 PrintLine("=", 79);
10607 Print("Converting levels\n");
10608 PrintLine("-", 79);
10609 Print("Level series identifier: '%s'\n", convert_leveldir->identifier);
10610 Print("Level series name: '%s'\n", convert_leveldir->name);
10611 Print("Level series author: '%s'\n", convert_leveldir->author);
10612 Print("Number of levels: %d\n", convert_leveldir->levels);
10613 PrintLine("=", 79);
10616 for (i = 0; i < MAX_NUM_CONVERT_LEVELS; i++)
10617 levels_failed[i] = FALSE;
10619 while (convert_level_nr <= convert_leveldir->last_level)
10621 char *level_filename;
10624 level_nr = convert_level_nr++;
10626 Print("Level %03d: ", level_nr);
10628 LoadLevel(level_nr);
10629 if (level.no_level_file || level.no_valid_file)
10631 Print("(no level)\n");
10635 Print("converting level ... ");
10637 level_filename = getDefaultLevelFilename(level_nr);
10638 new_level = !fileExists(level_filename);
10642 SaveLevel(level_nr);
10644 num_levels_converted++;
10646 Print("converted.\n");
10650 if (level_nr >= 0 && level_nr < MAX_NUM_CONVERT_LEVELS)
10651 levels_failed[level_nr] = TRUE;
10653 Print("NOT CONVERTED -- LEVEL ALREADY EXISTS.\n");
10656 num_levels_handled++;
10660 PrintLine("=", 79);
10661 Print("Number of levels handled: %d\n", num_levels_handled);
10662 Print("Number of levels converted: %d (%d%%)\n", num_levels_converted,
10663 (num_levels_handled ?
10664 num_levels_converted * 100 / num_levels_handled : 0));
10665 PrintLine("-", 79);
10666 Print("Summary (for automatic parsing by scripts):\n");
10667 Print("LEVELDIR '%s', CONVERTED %d/%d (%d%%)",
10668 convert_leveldir->identifier, num_levels_converted,
10669 num_levels_handled,
10670 (num_levels_handled ?
10671 num_levels_converted * 100 / num_levels_handled : 0));
10673 if (num_levels_handled != num_levels_converted)
10675 Print(", FAILED:");
10676 for (i = 0; i < MAX_NUM_CONVERT_LEVELS; i++)
10677 if (levels_failed[i])
10682 PrintLine("=", 79);
10684 CloseAllAndExit(0);
10688 /* ------------------------------------------------------------------------- */
10689 /* create and save images for use in level sketches (raw BMP format) */
10690 /* ------------------------------------------------------------------------- */
10692 void CreateLevelSketchImages()
10694 #if defined(TARGET_SDL)
10699 InitElementPropertiesGfxElement();
10701 bitmap1 = CreateBitmap(TILEX, TILEY, DEFAULT_DEPTH);
10702 bitmap2 = CreateBitmap(MINI_TILEX, MINI_TILEY, DEFAULT_DEPTH);
10704 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
10706 Bitmap *src_bitmap;
10708 int element = getMappedElement(i);
10709 int graphic = el2edimg(element);
10710 char basename1[16];
10711 char basename2[16];
10715 sprintf(basename1, "%03d.bmp", i);
10716 sprintf(basename2, "%03ds.bmp", i);
10718 filename1 = getPath2(global.create_images_dir, basename1);
10719 filename2 = getPath2(global.create_images_dir, basename2);
10721 getFixedGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
10722 BlitBitmap(src_bitmap, bitmap1, src_x, src_y, TILEX, TILEY,
10725 if (SDL_SaveBMP(bitmap1->surface, filename1) != 0)
10726 Error(ERR_EXIT, "cannot save level sketch image file '%s'", filename1);
10728 getMiniGraphicSource(graphic, &src_bitmap, &src_x, &src_y);
10729 BlitBitmap(src_bitmap, bitmap2, src_x, src_y, MINI_TILEX, MINI_TILEY, 0, 0);
10731 if (SDL_SaveBMP(bitmap2->surface, filename2) != 0)
10732 Error(ERR_EXIT, "cannot save level sketch image file '%s'", filename2);
10738 printf("%03d `%03d%c", i, i, (i % 10 < 9 ? ' ' : '\n'));
10741 FreeBitmap(bitmap1);
10742 FreeBitmap(bitmap2);
10747 Error(ERR_INFO, "%d normal and small images created", NUM_FILE_ELEMENTS);
10749 CloseAllAndExit(0);
10754 /* ------------------------------------------------------------------------- */
10755 /* create and save images for custom and group elements (raw BMP format) */
10756 /* ------------------------------------------------------------------------- */
10758 void CreateCustomElementImages(char *directory)
10760 #if defined(TARGET_SDL)
10761 char *src_basename = "RocksCE-template.ilbm";
10762 char *dst_basename = "RocksCE.bmp";
10763 char *src_filename = getPath2(directory, src_basename);
10764 char *dst_filename = getPath2(directory, dst_basename);
10765 Bitmap *src_bitmap;
10767 int yoffset_ce = 0;
10768 int yoffset_ge = (TILEY * NUM_CUSTOM_ELEMENTS / 16);
10771 SDLInitVideoDisplay();
10773 ReCreateBitmap(&backbuffer, video.width, video.height);
10775 src_bitmap = LoadImage(src_filename);
10777 bitmap = CreateBitmap(TILEX * 16 * 2,
10778 TILEY * (NUM_CUSTOM_ELEMENTS + NUM_GROUP_ELEMENTS) / 16,
10781 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
10788 BlitBitmap(src_bitmap, bitmap, 0, 0, TILEX, TILEY,
10789 TILEX * x, TILEY * y + yoffset_ce);
10791 BlitBitmap(src_bitmap, bitmap, 0, TILEY,
10793 TILEX * x + TILEX * 16,
10794 TILEY * y + yoffset_ce);
10796 for (j = 2; j >= 0; j--)
10800 BlitBitmap(src_bitmap, bitmap,
10801 TILEX + c * 7, 0, 6, 10,
10802 TILEX * x + 6 + j * 7,
10803 TILEY * y + 11 + yoffset_ce);
10805 BlitBitmap(src_bitmap, bitmap,
10806 TILEX + c * 8, TILEY, 6, 10,
10807 TILEX * 16 + TILEX * x + 6 + j * 8,
10808 TILEY * y + 10 + yoffset_ce);
10814 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
10821 BlitBitmap(src_bitmap, bitmap, 0, 0, TILEX, TILEY,
10822 TILEX * x, TILEY * y + yoffset_ge);
10824 BlitBitmap(src_bitmap, bitmap, 0, TILEY,
10826 TILEX * x + TILEX * 16,
10827 TILEY * y + yoffset_ge);
10829 for (j = 1; j >= 0; j--)
10833 BlitBitmap(src_bitmap, bitmap, TILEX + c * 10, 11, 10, 10,
10834 TILEX * x + 6 + j * 10,
10835 TILEY * y + 11 + yoffset_ge);
10837 BlitBitmap(src_bitmap, bitmap,
10838 TILEX + c * 8, TILEY + 12, 6, 10,
10839 TILEX * 16 + TILEX * x + 10 + j * 8,
10840 TILEY * y + 10 + yoffset_ge);
10846 if (SDL_SaveBMP(bitmap->surface, dst_filename) != 0)
10847 Error(ERR_EXIT, "cannot save CE graphics file '%s'", dst_filename);
10849 FreeBitmap(bitmap);
10851 CloseAllAndExit(0);