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 3 /* 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 ReadUnusedBytesFromFile(file, TAPE_CHUNK_HEAD_UNUSED);
7569 engine_version = getFileVersion(file);
7570 if (engine_version > 0)
7571 tape->engine_version = engine_version;
7573 tape->engine_version = tape->game_version;
7579 static int LoadTape_INFO(File *file, int chunk_size, struct TapeInfo *tape)
7581 int level_identifier_size;
7584 level_identifier_size = getFile16BitBE(file);
7586 tape->level_identifier =
7587 checked_realloc(tape->level_identifier, level_identifier_size);
7589 for (i = 0; i < level_identifier_size; i++)
7590 tape->level_identifier[i] = getFile8Bit(file);
7592 tape->level_nr = getFile16BitBE(file);
7594 chunk_size = 2 + level_identifier_size + 2;
7599 static int LoadTape_BODY(File *file, int chunk_size, struct TapeInfo *tape)
7602 int chunk_size_expected =
7603 (tape->num_participating_players + 1) * tape->length;
7605 if (chunk_size_expected != chunk_size)
7607 ReadUnusedBytesFromFile(file, chunk_size);
7608 return chunk_size_expected;
7611 for (i = 0; i < tape->length; i++)
7613 if (i >= MAX_TAPE_LEN)
7615 Error(ERR_WARN, "tape truncated -- size exceeds maximum tape size %d",
7618 // tape too large; read and ignore remaining tape data from this chunk
7619 for (;i < tape->length; i++)
7620 ReadUnusedBytesFromFile(file, tape->num_participating_players + 1);
7625 for (j = 0; j < MAX_PLAYERS; j++)
7627 tape->pos[i].action[j] = MV_NONE;
7629 if (tape->player_participates[j])
7630 tape->pos[i].action[j] = getFile8Bit(file);
7633 tape->pos[i].delay = getFile8Bit(file);
7635 if (tape->file_version == FILE_VERSION_1_0)
7637 /* eliminate possible diagonal moves in old tapes */
7638 /* this is only for backward compatibility */
7640 byte joy_dir[4] = { JOY_LEFT, JOY_RIGHT, JOY_UP, JOY_DOWN };
7641 byte action = tape->pos[i].action[0];
7642 int k, num_moves = 0;
7644 for (k = 0; k<4; k++)
7646 if (action & joy_dir[k])
7648 tape->pos[i + num_moves].action[0] = joy_dir[k];
7650 tape->pos[i + num_moves].delay = 0;
7659 tape->length += num_moves;
7662 else if (tape->file_version < FILE_VERSION_2_0)
7664 /* convert pre-2.0 tapes to new tape format */
7666 if (tape->pos[i].delay > 1)
7669 tape->pos[i + 1] = tape->pos[i];
7670 tape->pos[i + 1].delay = 1;
7673 for (j = 0; j < MAX_PLAYERS; j++)
7674 tape->pos[i].action[j] = MV_NONE;
7675 tape->pos[i].delay--;
7682 if (checkEndOfFile(file))
7686 if (i != tape->length)
7687 chunk_size = (tape->num_participating_players + 1) * i;
7692 void LoadTape_SokobanSolution(char *filename)
7695 int move_delay = TILESIZE / level.initial_player_stepsize[0];
7697 if (!(file = openFile(filename, MODE_READ)))
7699 tape.no_valid_file = TRUE;
7704 while (!checkEndOfFile(file))
7706 unsigned char c = getByteFromFile(file);
7708 if (checkEndOfFile(file))
7715 tape.pos[tape.length].action[0] = MV_UP;
7716 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
7722 tape.pos[tape.length].action[0] = MV_DOWN;
7723 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
7729 tape.pos[tape.length].action[0] = MV_LEFT;
7730 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
7736 tape.pos[tape.length].action[0] = MV_RIGHT;
7737 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
7745 /* ignore white-space characters */
7749 tape.no_valid_file = TRUE;
7751 Error(ERR_WARN, "unsupported Sokoban solution file '%s' ['%d']", filename, c);
7759 if (tape.no_valid_file)
7762 tape.length_frames = GetTapeLengthFrames();
7763 tape.length_seconds = GetTapeLengthSeconds();
7766 void LoadTapeFromFilename(char *filename)
7768 char cookie[MAX_LINE_LEN];
7769 char chunk_name[CHUNK_ID_LEN + 1];
7773 /* always start with reliable default values */
7774 setTapeInfoToDefaults();
7776 if (strSuffix(filename, ".sln"))
7778 LoadTape_SokobanSolution(filename);
7783 if (!(file = openFile(filename, MODE_READ)))
7785 tape.no_valid_file = TRUE;
7790 getFileChunkBE(file, chunk_name, NULL);
7791 if (strEqual(chunk_name, "RND1"))
7793 getFile32BitBE(file); /* not used */
7795 getFileChunkBE(file, chunk_name, NULL);
7796 if (!strEqual(chunk_name, "TAPE"))
7798 tape.no_valid_file = TRUE;
7800 Error(ERR_WARN, "unknown format of tape file '%s'", filename);
7807 else /* check for pre-2.0 file format with cookie string */
7809 strcpy(cookie, chunk_name);
7810 if (getStringFromFile(file, &cookie[4], MAX_LINE_LEN - 4) == NULL)
7812 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
7813 cookie[strlen(cookie) - 1] = '\0';
7815 if (!checkCookieString(cookie, TAPE_COOKIE_TMPL))
7817 tape.no_valid_file = TRUE;
7819 Error(ERR_WARN, "unknown format of tape file '%s'", filename);
7826 if ((tape.file_version = getFileVersionFromCookieString(cookie)) == -1)
7828 tape.no_valid_file = TRUE;
7830 Error(ERR_WARN, "unsupported version of tape file '%s'", filename);
7837 /* pre-2.0 tape files have no game version, so use file version here */
7838 tape.game_version = tape.file_version;
7841 if (tape.file_version < FILE_VERSION_1_2)
7843 /* tape files from versions before 1.2.0 without chunk structure */
7844 LoadTape_HEAD(file, TAPE_CHUNK_HEAD_SIZE, &tape);
7845 LoadTape_BODY(file, 2 * tape.length, &tape);
7853 int (*loader)(File *, int, struct TapeInfo *);
7857 { "VERS", TAPE_CHUNK_VERS_SIZE, LoadTape_VERS },
7858 { "HEAD", TAPE_CHUNK_HEAD_SIZE, LoadTape_HEAD },
7859 { "INFO", -1, LoadTape_INFO },
7860 { "BODY", -1, LoadTape_BODY },
7864 while (getFileChunkBE(file, chunk_name, &chunk_size))
7868 while (chunk_info[i].name != NULL &&
7869 !strEqual(chunk_name, chunk_info[i].name))
7872 if (chunk_info[i].name == NULL)
7874 Error(ERR_WARN, "unknown chunk '%s' in tape file '%s'",
7875 chunk_name, filename);
7876 ReadUnusedBytesFromFile(file, chunk_size);
7878 else if (chunk_info[i].size != -1 &&
7879 chunk_info[i].size != chunk_size)
7881 Error(ERR_WARN, "wrong size (%d) of chunk '%s' in tape file '%s'",
7882 chunk_size, chunk_name, filename);
7883 ReadUnusedBytesFromFile(file, chunk_size);
7887 /* call function to load this tape chunk */
7888 int chunk_size_expected =
7889 (chunk_info[i].loader)(file, chunk_size, &tape);
7891 /* the size of some chunks cannot be checked before reading other
7892 chunks first (like "HEAD" and "BODY") that contain some header
7893 information, so check them here */
7894 if (chunk_size_expected != chunk_size)
7896 Error(ERR_WARN, "wrong size (%d) of chunk '%s' in tape file '%s'",
7897 chunk_size, chunk_name, filename);
7905 tape.length_frames = GetTapeLengthFrames();
7906 tape.length_seconds = GetTapeLengthSeconds();
7909 printf("::: tape file version: %d\n", tape.file_version);
7910 printf("::: tape game version: %d\n", tape.game_version);
7911 printf("::: tape engine version: %d\n", tape.engine_version);
7915 void LoadTape(int nr)
7917 char *filename = getTapeFilename(nr);
7919 LoadTapeFromFilename(filename);
7922 void LoadSolutionTape(int nr)
7924 char *filename = getSolutionTapeFilename(nr);
7926 LoadTapeFromFilename(filename);
7928 if (TAPE_IS_EMPTY(tape) &&
7929 level.game_engine_type == GAME_ENGINE_TYPE_SP &&
7930 level.native_sp_level->demo.is_available)
7931 CopyNativeTape_SP_to_RND(&level);
7934 static void SaveTape_VERS(FILE *file, struct TapeInfo *tape)
7936 putFileVersion(file, tape->file_version);
7937 putFileVersion(file, tape->game_version);
7940 static void SaveTape_HEAD(FILE *file, struct TapeInfo *tape)
7943 byte store_participating_players = 0;
7945 /* set bits for participating players for compact storage */
7946 for (i = 0; i < MAX_PLAYERS; i++)
7947 if (tape->player_participates[i])
7948 store_participating_players |= (1 << i);
7950 putFile32BitBE(file, tape->random_seed);
7951 putFile32BitBE(file, tape->date);
7952 putFile32BitBE(file, tape->length);
7954 putFile8Bit(file, store_participating_players);
7956 /* unused bytes not at the end here for 4-byte alignment of engine_version */
7957 WriteUnusedBytesToFile(file, TAPE_CHUNK_HEAD_UNUSED);
7959 putFileVersion(file, tape->engine_version);
7962 static void SaveTape_INFO(FILE *file, struct TapeInfo *tape)
7964 int level_identifier_size = strlen(tape->level_identifier) + 1;
7967 putFile16BitBE(file, level_identifier_size);
7969 for (i = 0; i < level_identifier_size; i++)
7970 putFile8Bit(file, tape->level_identifier[i]);
7972 putFile16BitBE(file, tape->level_nr);
7975 static void SaveTape_BODY(FILE *file, struct TapeInfo *tape)
7979 for (i = 0; i < tape->length; i++)
7981 for (j = 0; j < MAX_PLAYERS; j++)
7982 if (tape->player_participates[j])
7983 putFile8Bit(file, tape->pos[i].action[j]);
7985 putFile8Bit(file, tape->pos[i].delay);
7989 void SaveTape(int nr)
7991 char *filename = getTapeFilename(nr);
7993 int num_participating_players = 0;
7994 int info_chunk_size;
7995 int body_chunk_size;
7998 InitTapeDirectory(leveldir_current->subdir);
8000 if (!(file = fopen(filename, MODE_WRITE)))
8002 Error(ERR_WARN, "cannot save level recording file '%s'", filename);
8006 tape.file_version = FILE_VERSION_ACTUAL;
8007 tape.game_version = GAME_VERSION_ACTUAL;
8009 /* count number of participating players */
8010 for (i = 0; i < MAX_PLAYERS; i++)
8011 if (tape.player_participates[i])
8012 num_participating_players++;
8014 info_chunk_size = 2 + (strlen(tape.level_identifier) + 1) + 2;
8015 body_chunk_size = (num_participating_players + 1) * tape.length;
8017 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
8018 putFileChunkBE(file, "TAPE", CHUNK_SIZE_NONE);
8020 putFileChunkBE(file, "VERS", TAPE_CHUNK_VERS_SIZE);
8021 SaveTape_VERS(file, &tape);
8023 putFileChunkBE(file, "HEAD", TAPE_CHUNK_HEAD_SIZE);
8024 SaveTape_HEAD(file, &tape);
8026 putFileChunkBE(file, "INFO", info_chunk_size);
8027 SaveTape_INFO(file, &tape);
8029 putFileChunkBE(file, "BODY", body_chunk_size);
8030 SaveTape_BODY(file, &tape);
8034 SetFilePermissions(filename, PERMS_PRIVATE);
8036 tape.changed = FALSE;
8039 boolean SaveTapeChecked(int nr)
8041 char *filename = getTapeFilename(nr);
8042 boolean new_tape = !fileExists(filename);
8043 boolean tape_saved = FALSE;
8045 if (new_tape || Request("Replace old tape?", REQ_ASK))
8050 Request("Tape saved!", REQ_CONFIRM);
8058 void DumpTape(struct TapeInfo *tape)
8060 int tape_frame_counter;
8063 if (tape->no_valid_file)
8065 Error(ERR_WARN, "cannot dump -- no valid tape file found");
8071 Print("Tape of Level %03d (file version %08d, game version %08d)\n",
8072 tape->level_nr, tape->file_version, tape->game_version);
8073 Print(" (effective engine version %08d)\n",
8074 tape->engine_version);
8075 Print("Level series identifier: '%s'\n", tape->level_identifier);
8078 tape_frame_counter = 0;
8080 for (i = 0; i < tape->length; i++)
8082 if (i >= MAX_TAPE_LEN)
8087 for (j = 0; j < MAX_PLAYERS; j++)
8089 if (tape->player_participates[j])
8091 int action = tape->pos[i].action[j];
8093 Print("%d:%02x ", j, action);
8094 Print("[%c%c%c%c|%c%c] - ",
8095 (action & JOY_LEFT ? '<' : ' '),
8096 (action & JOY_RIGHT ? '>' : ' '),
8097 (action & JOY_UP ? '^' : ' '),
8098 (action & JOY_DOWN ? 'v' : ' '),
8099 (action & JOY_BUTTON_1 ? '1' : ' '),
8100 (action & JOY_BUTTON_2 ? '2' : ' '));
8104 Print("(%03d) ", tape->pos[i].delay);
8105 Print("[%05d]\n", tape_frame_counter);
8107 tape_frame_counter += tape->pos[i].delay;
8114 /* ========================================================================= */
8115 /* score file functions */
8116 /* ========================================================================= */
8118 void LoadScore(int nr)
8121 char *filename = getScoreFilename(nr);
8122 char cookie[MAX_LINE_LEN];
8123 char line[MAX_LINE_LEN];
8127 /* always start with reliable default values */
8128 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
8130 strcpy(highscore[i].Name, EMPTY_PLAYER_NAME);
8131 highscore[i].Score = 0;
8134 if (!(file = fopen(filename, MODE_READ)))
8137 /* check file identifier */
8138 if (fgets(cookie, MAX_LINE_LEN, file) == NULL)
8140 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
8141 cookie[strlen(cookie) - 1] = '\0';
8143 if (!checkCookieString(cookie, SCORE_COOKIE))
8145 Error(ERR_WARN, "unknown format of score file '%s'", filename);
8150 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
8152 if (fscanf(file, "%d", &highscore[i].Score) == EOF)
8153 Error(ERR_WARN, "fscanf() failed; %s", strerror(errno));
8154 if (fgets(line, MAX_LINE_LEN, file) == NULL)
8157 if (strlen(line) > 0 && line[strlen(line) - 1] == '\n')
8158 line[strlen(line) - 1] = '\0';
8160 for (line_ptr = line; *line_ptr; line_ptr++)
8162 if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != '\0')
8164 strncpy(highscore[i].Name, line_ptr, MAX_PLAYER_NAME_LEN);
8165 highscore[i].Name[MAX_PLAYER_NAME_LEN] = '\0';
8174 void SaveScore(int nr)
8177 int permissions = (program.global_scores ? PERMS_PUBLIC : PERMS_PRIVATE);
8178 char *filename = getScoreFilename(nr);
8181 InitScoreDirectory(leveldir_current->subdir);
8183 if (!(file = fopen(filename, MODE_WRITE)))
8185 Error(ERR_WARN, "cannot save score for level %d", nr);
8189 fprintf(file, "%s\n\n", SCORE_COOKIE);
8191 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
8192 fprintf(file, "%d %s\n", highscore[i].Score, highscore[i].Name);
8196 SetFilePermissions(filename, permissions);
8200 /* ========================================================================= */
8201 /* setup file functions */
8202 /* ========================================================================= */
8204 #define TOKEN_STR_PLAYER_PREFIX "player_"
8207 #define SETUP_TOKEN_PLAYER_NAME 0
8208 #define SETUP_TOKEN_SOUND 1
8209 #define SETUP_TOKEN_SOUND_LOOPS 2
8210 #define SETUP_TOKEN_SOUND_MUSIC 3
8211 #define SETUP_TOKEN_SOUND_SIMPLE 4
8212 #define SETUP_TOKEN_TOONS 5
8213 #define SETUP_TOKEN_SCROLL_DELAY 6
8214 #define SETUP_TOKEN_SCROLL_DELAY_VALUE 7
8215 #define SETUP_TOKEN_ENGINE_SNAPSHOT_MODE 8
8216 #define SETUP_TOKEN_ENGINE_SNAPSHOT_MEMORY 9
8217 #define SETUP_TOKEN_FADE_SCREENS 10
8218 #define SETUP_TOKEN_AUTORECORD 11
8219 #define SETUP_TOKEN_SHOW_TITLESCREEN 12
8220 #define SETUP_TOKEN_QUICK_DOORS 13
8221 #define SETUP_TOKEN_TEAM_MODE 14
8222 #define SETUP_TOKEN_HANDICAP 15
8223 #define SETUP_TOKEN_SKIP_LEVELS 16
8224 #define SETUP_TOKEN_INCREMENT_LEVELS 17
8225 #define SETUP_TOKEN_TIME_LIMIT 18
8226 #define SETUP_TOKEN_FULLSCREEN 19
8227 #define SETUP_TOKEN_WINDOW_SCALING_PERCENT 20
8228 #define SETUP_TOKEN_WINDOW_SCALING_QUALITY 21
8229 #define SETUP_TOKEN_SCREEN_RENDERING_MODE 22
8230 #define SETUP_TOKEN_ASK_ON_ESCAPE 23
8231 #define SETUP_TOKEN_ASK_ON_ESCAPE_EDITOR 24
8232 #define SETUP_TOKEN_QUICK_SWITCH 25
8233 #define SETUP_TOKEN_INPUT_ON_FOCUS 26
8234 #define SETUP_TOKEN_PREFER_AGA_GRAPHICS 27
8235 #define SETUP_TOKEN_GAME_FRAME_DELAY 28
8236 #define SETUP_TOKEN_SP_SHOW_BORDER_ELEMENTS 29
8237 #define SETUP_TOKEN_SMALL_GAME_GRAPHICS 30
8238 #define SETUP_TOKEN_SHOW_SNAPSHOT_BUTTONS 31
8239 #define SETUP_TOKEN_GRAPHICS_SET 32
8240 #define SETUP_TOKEN_SOUNDS_SET 33
8241 #define SETUP_TOKEN_MUSIC_SET 34
8242 #define SETUP_TOKEN_OVERRIDE_LEVEL_GRAPHICS 35
8243 #define SETUP_TOKEN_OVERRIDE_LEVEL_SOUNDS 36
8244 #define SETUP_TOKEN_OVERRIDE_LEVEL_MUSIC 37
8245 #define SETUP_TOKEN_VOLUME_SIMPLE 38
8246 #define SETUP_TOKEN_VOLUME_LOOPS 39
8247 #define SETUP_TOKEN_VOLUME_MUSIC 40
8248 #define SETUP_TOKEN_TOUCH_CONTROL_TYPE 41
8249 #define SETUP_TOKEN_TOUCH_MOVE_DISTANCE 42
8250 #define SETUP_TOKEN_TOUCH_DROP_DISTANCE 43
8252 #define NUM_GLOBAL_SETUP_TOKENS 44
8255 #define SETUP_TOKEN_AUTO_EDITOR_ZOOM_TILESIZE 0
8257 #define NUM_AUTO_SETUP_TOKENS 1
8260 #define SETUP_TOKEN_EDITOR_EL_CLASSIC 0
8261 #define SETUP_TOKEN_EDITOR_EL_CUSTOM 1
8262 #define SETUP_TOKEN_EDITOR_EL_USER_DEFINED 2
8263 #define SETUP_TOKEN_EDITOR_EL_DYNAMIC 3
8264 #define SETUP_TOKEN_EDITOR_EL_HEADLINES 4
8265 #define SETUP_TOKEN_EDITOR_SHOW_ELEMENT_TOKEN 5
8267 #define NUM_EDITOR_SETUP_TOKENS 6
8269 /* editor cascade setup */
8270 #define SETUP_TOKEN_EDITOR_CASCADE_BD 0
8271 #define SETUP_TOKEN_EDITOR_CASCADE_EM 1
8272 #define SETUP_TOKEN_EDITOR_CASCADE_EMC 2
8273 #define SETUP_TOKEN_EDITOR_CASCADE_RND 3
8274 #define SETUP_TOKEN_EDITOR_CASCADE_SB 4
8275 #define SETUP_TOKEN_EDITOR_CASCADE_SP 5
8276 #define SETUP_TOKEN_EDITOR_CASCADE_DC 6
8277 #define SETUP_TOKEN_EDITOR_CASCADE_DX 7
8278 #define SETUP_TOKEN_EDITOR_CASCADE_TEXT 8
8279 #define SETUP_TOKEN_EDITOR_CASCADE_STEELTEXT 9
8280 #define SETUP_TOKEN_EDITOR_CASCADE_CE 10
8281 #define SETUP_TOKEN_EDITOR_CASCADE_GE 11
8282 #define SETUP_TOKEN_EDITOR_CASCADE_REF 12
8283 #define SETUP_TOKEN_EDITOR_CASCADE_USER 13
8284 #define SETUP_TOKEN_EDITOR_CASCADE_DYNAMIC 14
8286 #define NUM_EDITOR_CASCADE_SETUP_TOKENS 15
8288 /* shortcut setup */
8289 #define SETUP_TOKEN_SHORTCUT_SAVE_GAME 0
8290 #define SETUP_TOKEN_SHORTCUT_LOAD_GAME 1
8291 #define SETUP_TOKEN_SHORTCUT_TOGGLE_PAUSE 2
8292 #define SETUP_TOKEN_SHORTCUT_FOCUS_PLAYER_1 3
8293 #define SETUP_TOKEN_SHORTCUT_FOCUS_PLAYER_2 4
8294 #define SETUP_TOKEN_SHORTCUT_FOCUS_PLAYER_3 5
8295 #define SETUP_TOKEN_SHORTCUT_FOCUS_PLAYER_4 6
8296 #define SETUP_TOKEN_SHORTCUT_FOCUS_PLAYER_ALL 7
8297 #define SETUP_TOKEN_SHORTCUT_TAPE_EJECT 8
8298 #define SETUP_TOKEN_SHORTCUT_TAPE_EXTRA 9
8299 #define SETUP_TOKEN_SHORTCUT_TAPE_STOP 10
8300 #define SETUP_TOKEN_SHORTCUT_TAPE_PAUSE 11
8301 #define SETUP_TOKEN_SHORTCUT_TAPE_RECORD 12
8302 #define SETUP_TOKEN_SHORTCUT_TAPE_PLAY 13
8303 #define SETUP_TOKEN_SHORTCUT_SOUND_SIMPLE 14
8304 #define SETUP_TOKEN_SHORTCUT_SOUND_LOOPS 15
8305 #define SETUP_TOKEN_SHORTCUT_SOUND_MUSIC 16
8306 #define SETUP_TOKEN_SHORTCUT_SNAP_LEFT 17
8307 #define SETUP_TOKEN_SHORTCUT_SNAP_RIGHT 18
8308 #define SETUP_TOKEN_SHORTCUT_SNAP_UP 19
8309 #define SETUP_TOKEN_SHORTCUT_SNAP_DOWN 20
8311 #define NUM_SHORTCUT_SETUP_TOKENS 21
8314 #define SETUP_TOKEN_PLAYER_USE_JOYSTICK 0
8315 #define SETUP_TOKEN_PLAYER_JOY_DEVICE_NAME 1
8316 #define SETUP_TOKEN_PLAYER_JOY_XLEFT 2
8317 #define SETUP_TOKEN_PLAYER_JOY_XMIDDLE 3
8318 #define SETUP_TOKEN_PLAYER_JOY_XRIGHT 4
8319 #define SETUP_TOKEN_PLAYER_JOY_YUPPER 5
8320 #define SETUP_TOKEN_PLAYER_JOY_YMIDDLE 6
8321 #define SETUP_TOKEN_PLAYER_JOY_YLOWER 7
8322 #define SETUP_TOKEN_PLAYER_JOY_SNAP 8
8323 #define SETUP_TOKEN_PLAYER_JOY_DROP 9
8324 #define SETUP_TOKEN_PLAYER_KEY_LEFT 10
8325 #define SETUP_TOKEN_PLAYER_KEY_RIGHT 11
8326 #define SETUP_TOKEN_PLAYER_KEY_UP 12
8327 #define SETUP_TOKEN_PLAYER_KEY_DOWN 13
8328 #define SETUP_TOKEN_PLAYER_KEY_SNAP 14
8329 #define SETUP_TOKEN_PLAYER_KEY_DROP 15
8331 #define NUM_PLAYER_SETUP_TOKENS 16
8334 #define SETUP_TOKEN_SYSTEM_SDL_VIDEODRIVER 0
8335 #define SETUP_TOKEN_SYSTEM_SDL_AUDIODRIVER 1
8336 #define SETUP_TOKEN_SYSTEM_AUDIO_FRAGMENT_SIZE 2
8338 #define NUM_SYSTEM_SETUP_TOKENS 3
8340 /* internal setup */
8341 #define SETUP_TOKEN_INT_PROGRAM_TITLE 0
8342 #define SETUP_TOKEN_INT_PROGRAM_VERSION 1
8343 #define SETUP_TOKEN_INT_PROGRAM_AUTHOR 2
8344 #define SETUP_TOKEN_INT_PROGRAM_EMAIL 3
8345 #define SETUP_TOKEN_INT_PROGRAM_WEBSITE 4
8346 #define SETUP_TOKEN_INT_PROGRAM_COPYRIGHT 5
8347 #define SETUP_TOKEN_INT_PROGRAM_COMPANY 6
8348 #define SETUP_TOKEN_INT_PROGRAM_ICON_FILE 7
8349 #define SETUP_TOKEN_INT_DEFAULT_GRAPHICS_SET 8
8350 #define SETUP_TOKEN_INT_DEFAULT_SOUNDS_SET 9
8351 #define SETUP_TOKEN_INT_DEFAULT_MUSIC_SET 10
8352 #define SETUP_TOKEN_INT_FALLBACK_GRAPHICS_FILE 11
8353 #define SETUP_TOKEN_INT_FALLBACK_SOUNDS_FILE 12
8354 #define SETUP_TOKEN_INT_FALLBACK_MUSIC_FILE 13
8355 #define SETUP_TOKEN_INT_DEFAULT_LEVEL_SERIES 14
8356 #define SETUP_TOKEN_INT_CHOOSE_FROM_TOP_LEVELDIR 15
8357 #define SETUP_TOKEN_INT_SHOW_SCALING_IN_TITLE 16
8358 #define SETUP_TOKEN_INT_DEFAULT_WINDOW_WIDTH 17
8359 #define SETUP_TOKEN_INT_DEFAULT_WINDOW_HEIGHT 18
8361 #define NUM_INTERNAL_SETUP_TOKENS 19
8364 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_0 0
8365 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_1 1
8366 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_2 2
8367 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_3 3
8368 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_4 4
8369 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_5 5
8370 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_6 6
8371 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_7 7
8372 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_8 8
8373 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_9 9
8374 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_KEY_0 10
8375 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_KEY_1 11
8376 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_KEY_2 12
8377 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_KEY_3 13
8378 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_KEY_4 14
8379 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_KEY_5 15
8380 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_KEY_6 16
8381 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_KEY_7 17
8382 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_KEY_8 18
8383 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_KEY_9 19
8384 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_USE_MOD_KEY 20
8385 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_GAME_ONLY 21
8386 #define SETUP_TOKEN_DEBUG_SHOW_FRAMES_PER_SECOND 22
8388 #define NUM_DEBUG_SETUP_TOKENS 23
8391 #define SETUP_TOKEN_OPTIONS_VERBOSE 0
8393 #define NUM_OPTIONS_SETUP_TOKENS 1
8396 static struct SetupInfo si;
8397 static struct SetupAutoSetupInfo sasi;
8398 static struct SetupEditorInfo sei;
8399 static struct SetupEditorCascadeInfo seci;
8400 static struct SetupShortcutInfo ssi;
8401 static struct SetupInputInfo sii;
8402 static struct SetupSystemInfo syi;
8403 static struct SetupInternalInfo sxi;
8404 static struct SetupDebugInfo sdi;
8405 static struct OptionInfo soi;
8407 static struct TokenInfo global_setup_tokens[] =
8409 { TYPE_STRING, &si.player_name, "player_name" },
8410 { TYPE_SWITCH, &si.sound, "sound" },
8411 { TYPE_SWITCH, &si.sound_loops, "repeating_sound_loops" },
8412 { TYPE_SWITCH, &si.sound_music, "background_music" },
8413 { TYPE_SWITCH, &si.sound_simple, "simple_sound_effects" },
8414 { TYPE_SWITCH, &si.toons, "toons" },
8415 { TYPE_SWITCH, &si.scroll_delay, "scroll_delay" },
8416 { TYPE_INTEGER,&si.scroll_delay_value, "scroll_delay_value" },
8417 { TYPE_STRING, &si.engine_snapshot_mode, "engine_snapshot_mode" },
8418 { TYPE_INTEGER,&si.engine_snapshot_memory, "engine_snapshot_memory" },
8419 { TYPE_SWITCH, &si.fade_screens, "fade_screens" },
8420 { TYPE_SWITCH, &si.autorecord, "automatic_tape_recording"},
8421 { TYPE_SWITCH, &si.show_titlescreen, "show_titlescreen" },
8422 { TYPE_SWITCH, &si.quick_doors, "quick_doors" },
8423 { TYPE_SWITCH, &si.team_mode, "team_mode" },
8424 { TYPE_SWITCH, &si.handicap, "handicap" },
8425 { TYPE_SWITCH, &si.skip_levels, "skip_levels" },
8426 { TYPE_SWITCH, &si.increment_levels, "increment_levels" },
8427 { TYPE_SWITCH, &si.time_limit, "time_limit" },
8428 { TYPE_SWITCH, &si.fullscreen, "fullscreen" },
8429 { TYPE_INTEGER,&si.window_scaling_percent, "window_scaling_percent" },
8430 { TYPE_STRING, &si.window_scaling_quality, "window_scaling_quality" },
8431 { TYPE_STRING, &si.screen_rendering_mode, "screen_rendering_mode" },
8432 { TYPE_SWITCH, &si.ask_on_escape, "ask_on_escape" },
8433 { TYPE_SWITCH, &si.ask_on_escape_editor, "ask_on_escape_editor" },
8434 { TYPE_SWITCH, &si.quick_switch, "quick_player_switch" },
8435 { TYPE_SWITCH, &si.input_on_focus, "input_on_focus" },
8436 { TYPE_SWITCH, &si.prefer_aga_graphics, "prefer_aga_graphics" },
8437 { TYPE_INTEGER,&si.game_frame_delay, "game_frame_delay" },
8438 { TYPE_SWITCH, &si.sp_show_border_elements, "sp_show_border_elements" },
8439 { TYPE_SWITCH, &si.small_game_graphics, "small_game_graphics" },
8440 { TYPE_SWITCH, &si.show_snapshot_buttons, "show_snapshot_buttons" },
8441 { TYPE_STRING, &si.graphics_set, "graphics_set" },
8442 { TYPE_STRING, &si.sounds_set, "sounds_set" },
8443 { TYPE_STRING, &si.music_set, "music_set" },
8444 { TYPE_SWITCH3,&si.override_level_graphics, "override_level_graphics" },
8445 { TYPE_SWITCH3,&si.override_level_sounds, "override_level_sounds" },
8446 { TYPE_SWITCH3,&si.override_level_music, "override_level_music" },
8447 { TYPE_INTEGER,&si.volume_simple, "volume_simple" },
8448 { TYPE_INTEGER,&si.volume_loops, "volume_loops" },
8449 { TYPE_INTEGER,&si.volume_music, "volume_music" },
8450 { TYPE_STRING, &si.touch.control_type, "touch.control_type" },
8451 { TYPE_INTEGER,&si.touch.move_distance, "touch.move_distance" },
8452 { TYPE_INTEGER,&si.touch.drop_distance, "touch.drop_distance" },
8455 static struct TokenInfo auto_setup_tokens[] =
8457 { TYPE_INTEGER,&sasi.editor_zoom_tilesize, "editor.zoom_tilesize" },
8460 static struct TokenInfo editor_setup_tokens[] =
8462 { TYPE_SWITCH, &sei.el_classic, "editor.el_classic" },
8463 { TYPE_SWITCH, &sei.el_custom, "editor.el_custom" },
8464 { TYPE_SWITCH, &sei.el_user_defined, "editor.el_user_defined" },
8465 { TYPE_SWITCH, &sei.el_dynamic, "editor.el_dynamic" },
8466 { TYPE_SWITCH, &sei.el_headlines, "editor.el_headlines" },
8467 { TYPE_SWITCH, &sei.show_element_token,"editor.show_element_token" },
8470 static struct TokenInfo editor_cascade_setup_tokens[] =
8472 { TYPE_SWITCH, &seci.el_bd, "editor.cascade.el_bd" },
8473 { TYPE_SWITCH, &seci.el_em, "editor.cascade.el_em" },
8474 { TYPE_SWITCH, &seci.el_emc, "editor.cascade.el_emc" },
8475 { TYPE_SWITCH, &seci.el_rnd, "editor.cascade.el_rnd" },
8476 { TYPE_SWITCH, &seci.el_sb, "editor.cascade.el_sb" },
8477 { TYPE_SWITCH, &seci.el_sp, "editor.cascade.el_sp" },
8478 { TYPE_SWITCH, &seci.el_dc, "editor.cascade.el_dc" },
8479 { TYPE_SWITCH, &seci.el_dx, "editor.cascade.el_dx" },
8480 { TYPE_SWITCH, &seci.el_mm, "editor.cascade.el_mm" },
8481 { TYPE_SWITCH, &seci.el_df, "editor.cascade.el_df" },
8482 { TYPE_SWITCH, &seci.el_chars, "editor.cascade.el_chars" },
8483 { TYPE_SWITCH, &seci.el_steel_chars, "editor.cascade.el_steel_chars" },
8484 { TYPE_SWITCH, &seci.el_ce, "editor.cascade.el_ce" },
8485 { TYPE_SWITCH, &seci.el_ge, "editor.cascade.el_ge" },
8486 { TYPE_SWITCH, &seci.el_ref, "editor.cascade.el_ref" },
8487 { TYPE_SWITCH, &seci.el_user, "editor.cascade.el_user" },
8488 { TYPE_SWITCH, &seci.el_dynamic, "editor.cascade.el_dynamic" },
8491 static struct TokenInfo shortcut_setup_tokens[] =
8493 { TYPE_KEY_X11, &ssi.save_game, "shortcut.save_game" },
8494 { TYPE_KEY_X11, &ssi.load_game, "shortcut.load_game" },
8495 { TYPE_KEY_X11, &ssi.toggle_pause, "shortcut.toggle_pause" },
8496 { TYPE_KEY_X11, &ssi.focus_player[0], "shortcut.focus_player_1" },
8497 { TYPE_KEY_X11, &ssi.focus_player[1], "shortcut.focus_player_2" },
8498 { TYPE_KEY_X11, &ssi.focus_player[2], "shortcut.focus_player_3" },
8499 { TYPE_KEY_X11, &ssi.focus_player[3], "shortcut.focus_player_4" },
8500 { TYPE_KEY_X11, &ssi.focus_player_all,"shortcut.focus_player_all" },
8501 { TYPE_KEY_X11, &ssi.tape_eject, "shortcut.tape_eject" },
8502 { TYPE_KEY_X11, &ssi.tape_extra, "shortcut.tape_extra" },
8503 { TYPE_KEY_X11, &ssi.tape_stop, "shortcut.tape_stop" },
8504 { TYPE_KEY_X11, &ssi.tape_pause, "shortcut.tape_pause" },
8505 { TYPE_KEY_X11, &ssi.tape_record, "shortcut.tape_record" },
8506 { TYPE_KEY_X11, &ssi.tape_play, "shortcut.tape_play" },
8507 { TYPE_KEY_X11, &ssi.sound_simple, "shortcut.sound_simple" },
8508 { TYPE_KEY_X11, &ssi.sound_loops, "shortcut.sound_loops" },
8509 { TYPE_KEY_X11, &ssi.sound_music, "shortcut.sound_music" },
8510 { TYPE_KEY_X11, &ssi.snap_left, "shortcut.snap_left" },
8511 { TYPE_KEY_X11, &ssi.snap_right, "shortcut.snap_right" },
8512 { TYPE_KEY_X11, &ssi.snap_up, "shortcut.snap_up" },
8513 { TYPE_KEY_X11, &ssi.snap_down, "shortcut.snap_down" },
8516 static struct TokenInfo player_setup_tokens[] =
8518 { TYPE_BOOLEAN, &sii.use_joystick, ".use_joystick" },
8519 { TYPE_STRING, &sii.joy.device_name, ".joy.device_name" },
8520 { TYPE_INTEGER, &sii.joy.xleft, ".joy.xleft" },
8521 { TYPE_INTEGER, &sii.joy.xmiddle, ".joy.xmiddle" },
8522 { TYPE_INTEGER, &sii.joy.xright, ".joy.xright" },
8523 { TYPE_INTEGER, &sii.joy.yupper, ".joy.yupper" },
8524 { TYPE_INTEGER, &sii.joy.ymiddle, ".joy.ymiddle" },
8525 { TYPE_INTEGER, &sii.joy.ylower, ".joy.ylower" },
8526 { TYPE_INTEGER, &sii.joy.snap, ".joy.snap_field" },
8527 { TYPE_INTEGER, &sii.joy.drop, ".joy.place_bomb" },
8528 { TYPE_KEY_X11, &sii.key.left, ".key.move_left" },
8529 { TYPE_KEY_X11, &sii.key.right, ".key.move_right" },
8530 { TYPE_KEY_X11, &sii.key.up, ".key.move_up" },
8531 { TYPE_KEY_X11, &sii.key.down, ".key.move_down" },
8532 { TYPE_KEY_X11, &sii.key.snap, ".key.snap_field" },
8533 { TYPE_KEY_X11, &sii.key.drop, ".key.place_bomb" },
8536 static struct TokenInfo system_setup_tokens[] =
8538 { TYPE_STRING, &syi.sdl_videodriver, "system.sdl_videodriver" },
8539 { TYPE_STRING, &syi.sdl_audiodriver, "system.sdl_audiodriver" },
8540 { TYPE_INTEGER, &syi.audio_fragment_size,"system.audio_fragment_size" },
8543 static struct TokenInfo internal_setup_tokens[] =
8545 { TYPE_STRING, &sxi.program_title, "program_title" },
8546 { TYPE_STRING, &sxi.program_version, "program_version" },
8547 { TYPE_STRING, &sxi.program_author, "program_author" },
8548 { TYPE_STRING, &sxi.program_email, "program_email" },
8549 { TYPE_STRING, &sxi.program_website, "program_website" },
8550 { TYPE_STRING, &sxi.program_copyright, "program_copyright" },
8551 { TYPE_STRING, &sxi.program_company, "program_company" },
8552 { TYPE_STRING, &sxi.program_icon_file, "program_icon_file" },
8553 { TYPE_STRING, &sxi.default_graphics_set, "default_graphics_set" },
8554 { TYPE_STRING, &sxi.default_sounds_set, "default_sounds_set" },
8555 { TYPE_STRING, &sxi.default_music_set, "default_music_set" },
8556 { TYPE_STRING, &sxi.fallback_graphics_file, "fallback_graphics_file"},
8557 { TYPE_STRING, &sxi.fallback_sounds_file, "fallback_sounds_file" },
8558 { TYPE_STRING, &sxi.fallback_music_file, "fallback_music_file" },
8559 { TYPE_STRING, &sxi.default_level_series, "default_level_series" },
8560 { TYPE_BOOLEAN,&sxi.choose_from_top_leveldir, "choose_from_top_leveldir" },
8561 { TYPE_BOOLEAN,&sxi.show_scaling_in_title, "show_scaling_in_title" },
8562 { TYPE_INTEGER,&sxi.default_window_width, "default_window_width" },
8563 { TYPE_INTEGER,&sxi.default_window_height, "default_window_height" },
8566 static struct TokenInfo debug_setup_tokens[] =
8568 { TYPE_INTEGER, &sdi.frame_delay[0], "debug.frame_delay_0" },
8569 { TYPE_INTEGER, &sdi.frame_delay[1], "debug.frame_delay_1" },
8570 { TYPE_INTEGER, &sdi.frame_delay[2], "debug.frame_delay_2" },
8571 { TYPE_INTEGER, &sdi.frame_delay[3], "debug.frame_delay_3" },
8572 { TYPE_INTEGER, &sdi.frame_delay[4], "debug.frame_delay_4" },
8573 { TYPE_INTEGER, &sdi.frame_delay[5], "debug.frame_delay_5" },
8574 { TYPE_INTEGER, &sdi.frame_delay[6], "debug.frame_delay_6" },
8575 { TYPE_INTEGER, &sdi.frame_delay[7], "debug.frame_delay_7" },
8576 { TYPE_INTEGER, &sdi.frame_delay[8], "debug.frame_delay_8" },
8577 { TYPE_INTEGER, &sdi.frame_delay[9], "debug.frame_delay_9" },
8578 { TYPE_KEY_X11, &sdi.frame_delay_key[0], "debug.key.frame_delay_0" },
8579 { TYPE_KEY_X11, &sdi.frame_delay_key[1], "debug.key.frame_delay_1" },
8580 { TYPE_KEY_X11, &sdi.frame_delay_key[2], "debug.key.frame_delay_2" },
8581 { TYPE_KEY_X11, &sdi.frame_delay_key[3], "debug.key.frame_delay_3" },
8582 { TYPE_KEY_X11, &sdi.frame_delay_key[4], "debug.key.frame_delay_4" },
8583 { TYPE_KEY_X11, &sdi.frame_delay_key[5], "debug.key.frame_delay_5" },
8584 { TYPE_KEY_X11, &sdi.frame_delay_key[6], "debug.key.frame_delay_6" },
8585 { TYPE_KEY_X11, &sdi.frame_delay_key[7], "debug.key.frame_delay_7" },
8586 { TYPE_KEY_X11, &sdi.frame_delay_key[8], "debug.key.frame_delay_8" },
8587 { TYPE_KEY_X11, &sdi.frame_delay_key[9], "debug.key.frame_delay_9" },
8588 { TYPE_BOOLEAN, &sdi.frame_delay_use_mod_key,"debug.frame_delay.use_mod_key"},
8589 { TYPE_BOOLEAN, &sdi.frame_delay_game_only, "debug.frame_delay.game_only" },
8590 { TYPE_BOOLEAN, &sdi.show_frames_per_second, "debug.show_frames_per_second" },
8593 static struct TokenInfo options_setup_tokens[] =
8595 { TYPE_BOOLEAN, &soi.verbose, "options.verbose" },
8598 static char *get_corrected_login_name(char *login_name)
8600 /* needed because player name must be a fixed length string */
8601 char *login_name_new = checked_malloc(MAX_PLAYER_NAME_LEN + 1);
8603 strncpy(login_name_new, login_name, MAX_PLAYER_NAME_LEN);
8604 login_name_new[MAX_PLAYER_NAME_LEN] = '\0';
8606 if (strlen(login_name) > MAX_PLAYER_NAME_LEN) /* name has been cut */
8607 if (strchr(login_name_new, ' '))
8608 *strchr(login_name_new, ' ') = '\0';
8610 return login_name_new;
8613 static void setSetupInfoToDefaults(struct SetupInfo *si)
8617 si->player_name = get_corrected_login_name(getLoginName());
8620 si->sound_loops = TRUE;
8621 si->sound_music = TRUE;
8622 si->sound_simple = TRUE;
8624 si->scroll_delay = TRUE;
8625 si->scroll_delay_value = STD_SCROLL_DELAY;
8626 si->engine_snapshot_mode = getStringCopy(STR_SNAPSHOT_MODE_DEFAULT);
8627 si->engine_snapshot_memory = SNAPSHOT_MEMORY_DEFAULT;
8628 si->fade_screens = TRUE;
8629 si->autorecord = TRUE;
8630 si->show_titlescreen = TRUE;
8631 si->quick_doors = FALSE;
8632 si->team_mode = FALSE;
8633 si->handicap = TRUE;
8634 si->skip_levels = TRUE;
8635 si->increment_levels = TRUE;
8636 si->time_limit = TRUE;
8637 si->fullscreen = FALSE;
8638 si->window_scaling_percent = STD_WINDOW_SCALING_PERCENT;
8639 si->window_scaling_quality = getStringCopy(SCALING_QUALITY_DEFAULT);
8640 si->screen_rendering_mode = getStringCopy(STR_SPECIAL_RENDERING_DEFAULT);
8641 si->ask_on_escape = TRUE;
8642 si->ask_on_escape_editor = TRUE;
8643 si->quick_switch = FALSE;
8644 si->input_on_focus = FALSE;
8645 si->prefer_aga_graphics = TRUE;
8646 si->game_frame_delay = GAME_FRAME_DELAY;
8647 si->sp_show_border_elements = FALSE;
8648 si->small_game_graphics = FALSE;
8649 si->show_snapshot_buttons = FALSE;
8651 si->graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
8652 si->sounds_set = getStringCopy(SND_CLASSIC_SUBDIR);
8653 si->music_set = getStringCopy(MUS_CLASSIC_SUBDIR);
8655 si->override_level_graphics = FALSE;
8656 si->override_level_sounds = FALSE;
8657 si->override_level_music = FALSE;
8659 si->volume_simple = 100; /* percent */
8660 si->volume_loops = 100; /* percent */
8661 si->volume_music = 100; /* percent */
8663 si->touch.control_type = getStringCopy(TOUCH_CONTROL_DEFAULT);
8664 si->touch.move_distance = TOUCH_MOVE_DISTANCE_DEFAULT; /* percent */
8665 si->touch.drop_distance = TOUCH_DROP_DISTANCE_DEFAULT; /* percent */
8667 si->editor.el_boulderdash = TRUE;
8668 si->editor.el_emerald_mine = TRUE;
8669 si->editor.el_emerald_mine_club = TRUE;
8670 si->editor.el_more = TRUE;
8671 si->editor.el_sokoban = TRUE;
8672 si->editor.el_supaplex = TRUE;
8673 si->editor.el_diamond_caves = TRUE;
8674 si->editor.el_dx_boulderdash = TRUE;
8676 si->editor.el_mirror_magic = TRUE;
8677 si->editor.el_deflektor = TRUE;
8679 si->editor.el_chars = TRUE;
8680 si->editor.el_steel_chars = TRUE;
8682 si->editor.el_classic = TRUE;
8683 si->editor.el_custom = TRUE;
8685 si->editor.el_user_defined = FALSE;
8686 si->editor.el_dynamic = TRUE;
8688 si->editor.el_headlines = TRUE;
8690 si->editor.show_element_token = FALSE;
8692 si->editor.use_template_for_new_levels = TRUE;
8694 si->shortcut.save_game = DEFAULT_KEY_SAVE_GAME;
8695 si->shortcut.load_game = DEFAULT_KEY_LOAD_GAME;
8696 si->shortcut.toggle_pause = DEFAULT_KEY_TOGGLE_PAUSE;
8698 si->shortcut.focus_player[0] = DEFAULT_KEY_FOCUS_PLAYER_1;
8699 si->shortcut.focus_player[1] = DEFAULT_KEY_FOCUS_PLAYER_2;
8700 si->shortcut.focus_player[2] = DEFAULT_KEY_FOCUS_PLAYER_3;
8701 si->shortcut.focus_player[3] = DEFAULT_KEY_FOCUS_PLAYER_4;
8702 si->shortcut.focus_player_all = DEFAULT_KEY_FOCUS_PLAYER_ALL;
8704 si->shortcut.tape_eject = DEFAULT_KEY_TAPE_EJECT;
8705 si->shortcut.tape_extra = DEFAULT_KEY_TAPE_EXTRA;
8706 si->shortcut.tape_stop = DEFAULT_KEY_TAPE_STOP;
8707 si->shortcut.tape_pause = DEFAULT_KEY_TAPE_PAUSE;
8708 si->shortcut.tape_record = DEFAULT_KEY_TAPE_RECORD;
8709 si->shortcut.tape_play = DEFAULT_KEY_TAPE_PLAY;
8711 si->shortcut.sound_simple = DEFAULT_KEY_SOUND_SIMPLE;
8712 si->shortcut.sound_loops = DEFAULT_KEY_SOUND_LOOPS;
8713 si->shortcut.sound_music = DEFAULT_KEY_SOUND_MUSIC;
8715 si->shortcut.snap_left = DEFAULT_KEY_SNAP_LEFT;
8716 si->shortcut.snap_right = DEFAULT_KEY_SNAP_RIGHT;
8717 si->shortcut.snap_up = DEFAULT_KEY_SNAP_UP;
8718 si->shortcut.snap_down = DEFAULT_KEY_SNAP_DOWN;
8720 for (i = 0; i < MAX_PLAYERS; i++)
8722 si->input[i].use_joystick = FALSE;
8723 si->input[i].joy.device_name=getStringCopy(getDeviceNameFromJoystickNr(i));
8724 si->input[i].joy.xleft = JOYSTICK_XLEFT;
8725 si->input[i].joy.xmiddle = JOYSTICK_XMIDDLE;
8726 si->input[i].joy.xright = JOYSTICK_XRIGHT;
8727 si->input[i].joy.yupper = JOYSTICK_YUPPER;
8728 si->input[i].joy.ymiddle = JOYSTICK_YMIDDLE;
8729 si->input[i].joy.ylower = JOYSTICK_YLOWER;
8730 si->input[i].joy.snap = (i == 0 ? JOY_BUTTON_1 : 0);
8731 si->input[i].joy.drop = (i == 0 ? JOY_BUTTON_2 : 0);
8732 si->input[i].key.left = (i == 0 ? DEFAULT_KEY_LEFT : KSYM_UNDEFINED);
8733 si->input[i].key.right = (i == 0 ? DEFAULT_KEY_RIGHT : KSYM_UNDEFINED);
8734 si->input[i].key.up = (i == 0 ? DEFAULT_KEY_UP : KSYM_UNDEFINED);
8735 si->input[i].key.down = (i == 0 ? DEFAULT_KEY_DOWN : KSYM_UNDEFINED);
8736 si->input[i].key.snap = (i == 0 ? DEFAULT_KEY_SNAP : KSYM_UNDEFINED);
8737 si->input[i].key.drop = (i == 0 ? DEFAULT_KEY_DROP : KSYM_UNDEFINED);
8740 si->system.sdl_videodriver = getStringCopy(ARG_DEFAULT);
8741 si->system.sdl_audiodriver = getStringCopy(ARG_DEFAULT);
8742 si->system.audio_fragment_size = DEFAULT_AUDIO_FRAGMENT_SIZE;
8744 si->internal.program_title = getStringCopy(PROGRAM_TITLE_STRING);
8745 si->internal.program_version = getStringCopy(getProgramRealVersionString());
8746 si->internal.program_author = getStringCopy(PROGRAM_AUTHOR_STRING);
8747 si->internal.program_email = getStringCopy(PROGRAM_EMAIL_STRING);
8748 si->internal.program_website = getStringCopy(PROGRAM_WEBSITE_STRING);
8749 si->internal.program_copyright = getStringCopy(PROGRAM_COPYRIGHT_STRING);
8750 si->internal.program_company = getStringCopy(PROGRAM_COMPANY_STRING);
8752 si->internal.program_icon_file = getStringCopy(PROGRAM_ICON_FILENAME);
8754 si->internal.default_graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
8755 si->internal.default_sounds_set = getStringCopy(SND_CLASSIC_SUBDIR);
8756 si->internal.default_music_set = getStringCopy(MUS_CLASSIC_SUBDIR);
8758 si->internal.fallback_graphics_file = getStringCopy(UNDEFINED_FILENAME);
8759 si->internal.fallback_sounds_file = getStringCopy(UNDEFINED_FILENAME);
8760 si->internal.fallback_music_file = getStringCopy(UNDEFINED_FILENAME);
8762 si->internal.default_level_series = getStringCopy(UNDEFINED_LEVELSET);
8763 si->internal.choose_from_top_leveldir = FALSE;
8764 si->internal.show_scaling_in_title = TRUE;
8766 si->internal.default_window_width = WIN_XSIZE_DEFAULT;
8767 si->internal.default_window_height = WIN_YSIZE_DEFAULT;
8769 si->debug.frame_delay[0] = DEFAULT_FRAME_DELAY_0;
8770 si->debug.frame_delay[1] = DEFAULT_FRAME_DELAY_1;
8771 si->debug.frame_delay[2] = DEFAULT_FRAME_DELAY_2;
8772 si->debug.frame_delay[3] = DEFAULT_FRAME_DELAY_3;
8773 si->debug.frame_delay[4] = DEFAULT_FRAME_DELAY_4;
8774 si->debug.frame_delay[5] = DEFAULT_FRAME_DELAY_5;
8775 si->debug.frame_delay[6] = DEFAULT_FRAME_DELAY_6;
8776 si->debug.frame_delay[7] = DEFAULT_FRAME_DELAY_7;
8777 si->debug.frame_delay[8] = DEFAULT_FRAME_DELAY_8;
8778 si->debug.frame_delay[9] = DEFAULT_FRAME_DELAY_9;
8780 si->debug.frame_delay_key[0] = DEFAULT_KEY_FRAME_DELAY_0;
8781 si->debug.frame_delay_key[1] = DEFAULT_KEY_FRAME_DELAY_1;
8782 si->debug.frame_delay_key[2] = DEFAULT_KEY_FRAME_DELAY_2;
8783 si->debug.frame_delay_key[3] = DEFAULT_KEY_FRAME_DELAY_3;
8784 si->debug.frame_delay_key[4] = DEFAULT_KEY_FRAME_DELAY_4;
8785 si->debug.frame_delay_key[5] = DEFAULT_KEY_FRAME_DELAY_5;
8786 si->debug.frame_delay_key[6] = DEFAULT_KEY_FRAME_DELAY_6;
8787 si->debug.frame_delay_key[7] = DEFAULT_KEY_FRAME_DELAY_7;
8788 si->debug.frame_delay_key[8] = DEFAULT_KEY_FRAME_DELAY_8;
8789 si->debug.frame_delay_key[9] = DEFAULT_KEY_FRAME_DELAY_9;
8791 si->debug.frame_delay_use_mod_key = DEFAULT_FRAME_DELAY_USE_MOD_KEY;
8792 si->debug.frame_delay_game_only = DEFAULT_FRAME_DELAY_GAME_ONLY;
8794 si->debug.show_frames_per_second = FALSE;
8796 si->options.verbose = FALSE;
8798 #if defined(PLATFORM_ANDROID)
8799 si->fullscreen = TRUE;
8803 static void setSetupInfoToDefaults_AutoSetup(struct SetupInfo *si)
8805 si->auto_setup.editor_zoom_tilesize = MINI_TILESIZE;
8808 static void setSetupInfoToDefaults_EditorCascade(struct SetupInfo *si)
8810 si->editor_cascade.el_bd = TRUE;
8811 si->editor_cascade.el_em = TRUE;
8812 si->editor_cascade.el_emc = TRUE;
8813 si->editor_cascade.el_rnd = TRUE;
8814 si->editor_cascade.el_sb = TRUE;
8815 si->editor_cascade.el_sp = TRUE;
8816 si->editor_cascade.el_dc = TRUE;
8817 si->editor_cascade.el_dx = TRUE;
8819 si->editor_cascade.el_mm = TRUE;
8820 si->editor_cascade.el_df = TRUE;
8822 si->editor_cascade.el_chars = FALSE;
8823 si->editor_cascade.el_steel_chars = FALSE;
8824 si->editor_cascade.el_ce = FALSE;
8825 si->editor_cascade.el_ge = FALSE;
8826 si->editor_cascade.el_ref = FALSE;
8827 si->editor_cascade.el_user = FALSE;
8828 si->editor_cascade.el_dynamic = FALSE;
8831 #define MAX_HIDE_SETUP_TOKEN_SIZE 20
8833 static char *getHideSetupToken(void *setup_value)
8835 static char hide_setup_token[MAX_HIDE_SETUP_TOKEN_SIZE];
8837 if (setup_value != NULL)
8838 snprintf(hide_setup_token, MAX_HIDE_SETUP_TOKEN_SIZE, "%p", setup_value);
8840 return hide_setup_token;
8843 static void setHideSetupEntry(void *setup_value_raw)
8845 /* !!! DIRTY WORKAROUND; TO BE FIXED AFTER THE MM ENGINE RELEASE !!! */
8846 void *setup_value = setup_value_raw - (void *)&si + (void *)&setup;
8848 char *hide_setup_token = getHideSetupToken(setup_value);
8850 if (setup_value != NULL)
8851 setHashEntry(hide_setup_hash, hide_setup_token, "");
8854 boolean hideSetupEntry(void *setup_value)
8856 char *hide_setup_token = getHideSetupToken(setup_value);
8858 return (setup_value != NULL &&
8859 getHashEntry(hide_setup_hash, hide_setup_token) != NULL);
8862 static void setSetupInfoFromTokenText(SetupFileHash *setup_file_hash,
8863 struct TokenInfo *token_info,
8864 int token_nr, char *token_text)
8866 char *token_hide_text = getStringCat2(token_text, ".hide");
8867 char *token_hide_value = getHashEntry(setup_file_hash, token_hide_text);
8869 /* set the value of this setup option in the setup option structure */
8870 setSetupInfo(token_info, token_nr, getHashEntry(setup_file_hash, token_text));
8872 /* check if this setup option should be hidden in the setup menu */
8873 if (token_hide_value != NULL && get_boolean_from_string(token_hide_value))
8874 setHideSetupEntry(token_info[token_nr].value);
8877 static void setSetupInfoFromTokenInfo(SetupFileHash *setup_file_hash,
8878 struct TokenInfo *token_info,
8881 setSetupInfoFromTokenText(setup_file_hash, token_info, token_nr,
8882 token_info[token_nr].text);
8885 static void decodeSetupFileHash(SetupFileHash *setup_file_hash)
8889 if (!setup_file_hash)
8892 if (hide_setup_hash == NULL)
8893 hide_setup_hash = newSetupFileHash();
8897 for (i = 0; i < NUM_GLOBAL_SETUP_TOKENS; i++)
8898 setSetupInfoFromTokenInfo(setup_file_hash, global_setup_tokens, i);
8903 for (i = 0; i < NUM_EDITOR_SETUP_TOKENS; i++)
8904 setSetupInfoFromTokenInfo(setup_file_hash, editor_setup_tokens, i);
8907 /* shortcut setup */
8908 ssi = setup.shortcut;
8909 for (i = 0; i < NUM_SHORTCUT_SETUP_TOKENS; i++)
8910 setSetupInfoFromTokenInfo(setup_file_hash, shortcut_setup_tokens, i);
8911 setup.shortcut = ssi;
8914 for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
8918 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
8920 sii = setup.input[pnr];
8921 for (i = 0; i < NUM_PLAYER_SETUP_TOKENS; i++)
8923 char full_token[100];
8925 sprintf(full_token, "%s%s", prefix, player_setup_tokens[i].text);
8926 setSetupInfoFromTokenText(setup_file_hash, player_setup_tokens, i,
8929 setup.input[pnr] = sii;
8934 for (i = 0; i < NUM_SYSTEM_SETUP_TOKENS; i++)
8935 setSetupInfoFromTokenInfo(setup_file_hash, system_setup_tokens, i);
8938 /* internal setup */
8939 sxi = setup.internal;
8940 for (i = 0; i < NUM_INTERNAL_SETUP_TOKENS; i++)
8941 setSetupInfoFromTokenInfo(setup_file_hash, internal_setup_tokens, i);
8942 setup.internal = sxi;
8946 for (i = 0; i < NUM_DEBUG_SETUP_TOKENS; i++)
8947 setSetupInfoFromTokenInfo(setup_file_hash, debug_setup_tokens, i);
8951 soi = setup.options;
8952 for (i = 0; i < NUM_OPTIONS_SETUP_TOKENS; i++)
8953 setSetupInfoFromTokenInfo(setup_file_hash, options_setup_tokens, i);
8954 setup.options = soi;
8957 static void decodeSetupFileHash_AutoSetup(SetupFileHash *setup_file_hash)
8961 if (!setup_file_hash)
8965 sasi = setup.auto_setup;
8966 for (i = 0; i < NUM_AUTO_SETUP_TOKENS; i++)
8967 setSetupInfo(auto_setup_tokens, i,
8968 getHashEntry(setup_file_hash,
8969 auto_setup_tokens[i].text));
8970 setup.auto_setup = sasi;
8973 static void decodeSetupFileHash_EditorCascade(SetupFileHash *setup_file_hash)
8977 if (!setup_file_hash)
8980 /* editor cascade setup */
8981 seci = setup.editor_cascade;
8982 for (i = 0; i < NUM_EDITOR_CASCADE_SETUP_TOKENS; i++)
8983 setSetupInfo(editor_cascade_setup_tokens, i,
8984 getHashEntry(setup_file_hash,
8985 editor_cascade_setup_tokens[i].text));
8986 setup.editor_cascade = seci;
8989 void LoadSetupFromFilename(char *filename)
8991 SetupFileHash *setup_file_hash = loadSetupFileHash(filename);
8993 if (setup_file_hash)
8995 decodeSetupFileHash(setup_file_hash);
8997 freeSetupFileHash(setup_file_hash);
9001 Error(ERR_DEBUG, "using default setup values");
9005 static void LoadSetup_SpecialPostProcessing()
9007 char *player_name_new;
9009 /* needed to work around problems with fixed length strings */
9010 player_name_new = get_corrected_login_name(setup.player_name);
9011 free(setup.player_name);
9012 setup.player_name = player_name_new;
9014 /* "scroll_delay: on(3) / off(0)" was replaced by scroll delay value */
9015 if (setup.scroll_delay == FALSE)
9017 setup.scroll_delay_value = MIN_SCROLL_DELAY;
9018 setup.scroll_delay = TRUE; /* now always "on" */
9021 /* make sure that scroll delay value stays inside valid range */
9022 setup.scroll_delay_value =
9023 MIN(MAX(MIN_SCROLL_DELAY, setup.scroll_delay_value), MAX_SCROLL_DELAY);
9030 /* always start with reliable default values */
9031 setSetupInfoToDefaults(&setup);
9033 /* try to load setup values from default setup file */
9034 filename = getDefaultSetupFilename();
9036 if (fileExists(filename))
9037 LoadSetupFromFilename(filename);
9039 /* try to load setup values from user setup file */
9040 filename = getSetupFilename();
9042 LoadSetupFromFilename(filename);
9044 LoadSetup_SpecialPostProcessing();
9047 void LoadSetup_AutoSetup()
9049 char *filename = getPath2(getSetupDir(), AUTOSETUP_FILENAME);
9050 SetupFileHash *setup_file_hash = NULL;
9052 /* always start with reliable default values */
9053 setSetupInfoToDefaults_AutoSetup(&setup);
9055 setup_file_hash = loadSetupFileHash(filename);
9057 if (setup_file_hash)
9059 decodeSetupFileHash_AutoSetup(setup_file_hash);
9061 freeSetupFileHash(setup_file_hash);
9067 void LoadSetup_EditorCascade()
9069 char *filename = getPath2(getSetupDir(), EDITORCASCADE_FILENAME);
9070 SetupFileHash *setup_file_hash = NULL;
9072 /* always start with reliable default values */
9073 setSetupInfoToDefaults_EditorCascade(&setup);
9075 setup_file_hash = loadSetupFileHash(filename);
9077 if (setup_file_hash)
9079 decodeSetupFileHash_EditorCascade(setup_file_hash);
9081 freeSetupFileHash(setup_file_hash);
9087 static void addGameControllerMappingToHash(SetupFileHash *mappings_hash,
9090 char mapping_guid[MAX_LINE_LEN];
9091 char *mapping_start, *mapping_end;
9093 // get GUID from game controller mapping line: copy complete line
9094 strncpy(mapping_guid, mapping_line, MAX_LINE_LEN - 1);
9095 mapping_guid[MAX_LINE_LEN - 1] = '\0';
9097 // get GUID from game controller mapping line: cut after GUID part
9098 mapping_start = strchr(mapping_guid, ',');
9099 if (mapping_start != NULL)
9100 *mapping_start = '\0';
9102 // cut newline from game controller mapping line
9103 mapping_end = strchr(mapping_line, '\n');
9104 if (mapping_end != NULL)
9105 *mapping_end = '\0';
9107 // add mapping entry to game controller mappings hash
9108 setHashEntry(mappings_hash, mapping_guid, mapping_line);
9111 static void LoadSetup_ReadGameControllerMappings(SetupFileHash *mappings_hash,
9116 if (!(file = fopen(filename, MODE_READ)))
9118 Error(ERR_WARN, "cannot read game controller mappings file '%s'", filename);
9125 char line[MAX_LINE_LEN];
9127 if (!fgets(line, MAX_LINE_LEN, file))
9130 addGameControllerMappingToHash(mappings_hash, line);
9138 char *filename = getSetupFilename();
9142 InitUserDataDirectory();
9144 if (!(file = fopen(filename, MODE_WRITE)))
9146 Error(ERR_WARN, "cannot write setup file '%s'", filename);
9150 fprintFileHeader(file, SETUP_FILENAME);
9154 for (i = 0; i < NUM_GLOBAL_SETUP_TOKENS; i++)
9156 /* just to make things nicer :) */
9157 if (i == SETUP_TOKEN_PLAYER_NAME + 1 ||
9158 i == SETUP_TOKEN_GRAPHICS_SET ||
9159 i == SETUP_TOKEN_VOLUME_SIMPLE ||
9160 i == SETUP_TOKEN_TOUCH_CONTROL_TYPE)
9161 fprintf(file, "\n");
9163 fprintf(file, "%s\n", getSetupLine(global_setup_tokens, "", i));
9168 fprintf(file, "\n");
9169 for (i = 0; i < NUM_EDITOR_SETUP_TOKENS; i++)
9170 fprintf(file, "%s\n", getSetupLine(editor_setup_tokens, "", i));
9172 /* shortcut setup */
9173 ssi = setup.shortcut;
9174 fprintf(file, "\n");
9175 for (i = 0; i < NUM_SHORTCUT_SETUP_TOKENS; i++)
9176 fprintf(file, "%s\n", getSetupLine(shortcut_setup_tokens, "", i));
9179 for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
9183 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
9184 fprintf(file, "\n");
9186 sii = setup.input[pnr];
9187 for (i = 0; i < NUM_PLAYER_SETUP_TOKENS; i++)
9188 fprintf(file, "%s\n", getSetupLine(player_setup_tokens, prefix, i));
9193 fprintf(file, "\n");
9194 for (i = 0; i < NUM_SYSTEM_SETUP_TOKENS; i++)
9195 fprintf(file, "%s\n", getSetupLine(system_setup_tokens, "", i));
9197 /* internal setup */
9198 /* (internal setup values not saved to user setup file) */
9202 fprintf(file, "\n");
9203 for (i = 0; i < NUM_DEBUG_SETUP_TOKENS; i++)
9204 fprintf(file, "%s\n", getSetupLine(debug_setup_tokens, "", i));
9207 soi = setup.options;
9208 fprintf(file, "\n");
9209 for (i = 0; i < NUM_OPTIONS_SETUP_TOKENS; i++)
9210 fprintf(file, "%s\n", getSetupLine(options_setup_tokens, "", i));
9214 SetFilePermissions(filename, PERMS_PRIVATE);
9217 void SaveSetup_AutoSetup()
9219 char *filename = getPath2(getSetupDir(), AUTOSETUP_FILENAME);
9223 InitUserDataDirectory();
9225 if (!(file = fopen(filename, MODE_WRITE)))
9227 Error(ERR_WARN, "cannot write auto setup file '%s'", filename);
9232 fprintFileHeader(file, AUTOSETUP_FILENAME);
9234 sasi = setup.auto_setup;
9235 for (i = 0; i < NUM_AUTO_SETUP_TOKENS; i++)
9236 fprintf(file, "%s\n", getSetupLine(auto_setup_tokens, "", i));
9240 SetFilePermissions(filename, PERMS_PRIVATE);
9245 void SaveSetup_EditorCascade()
9247 char *filename = getPath2(getSetupDir(), EDITORCASCADE_FILENAME);
9251 InitUserDataDirectory();
9253 if (!(file = fopen(filename, MODE_WRITE)))
9255 Error(ERR_WARN, "cannot write editor cascade state file '%s'", filename);
9260 fprintFileHeader(file, EDITORCASCADE_FILENAME);
9262 seci = setup.editor_cascade;
9263 for (i = 0; i < NUM_EDITOR_CASCADE_SETUP_TOKENS; i++)
9264 fprintf(file, "%s\n", getSetupLine(editor_cascade_setup_tokens, "", i));
9268 SetFilePermissions(filename, PERMS_PRIVATE);
9273 static void SaveSetup_WriteGameControllerMappings(SetupFileHash *mappings_hash,
9278 if (!(file = fopen(filename, MODE_WRITE)))
9280 Error(ERR_WARN, "cannot write game controller mappings file '%s'",filename);
9285 BEGIN_HASH_ITERATION(mappings_hash, itr)
9287 fprintf(file, "%s\n", HASH_ITERATION_VALUE(itr));
9289 END_HASH_ITERATION(mappings_hash, itr)
9294 void SaveSetup_AddGameControllerMapping(char *mapping)
9296 char *filename = getPath2(getSetupDir(), GAMECONTROLLER_BASENAME);
9297 SetupFileHash *mappings_hash = newSetupFileHash();
9299 InitUserDataDirectory();
9301 // load existing personal game controller mappings
9302 LoadSetup_ReadGameControllerMappings(mappings_hash, filename);
9304 // add new mapping to personal game controller mappings
9305 addGameControllerMappingToHash(mappings_hash, mapping);
9307 // save updated personal game controller mappings
9308 SaveSetup_WriteGameControllerMappings(mappings_hash, filename);
9310 freeSetupFileHash(mappings_hash);
9314 void LoadCustomElementDescriptions()
9316 char *filename = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
9317 SetupFileHash *setup_file_hash;
9320 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
9322 if (element_info[i].custom_description != NULL)
9324 free(element_info[i].custom_description);
9325 element_info[i].custom_description = NULL;
9329 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
9332 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
9334 char *token = getStringCat2(element_info[i].token_name, ".name");
9335 char *value = getHashEntry(setup_file_hash, token);
9338 element_info[i].custom_description = getStringCopy(value);
9343 freeSetupFileHash(setup_file_hash);
9346 static int getElementFromToken(char *token)
9348 char *value = getHashEntry(element_token_hash, token);
9353 Error(ERR_WARN, "unknown element token '%s'", token);
9355 return EL_UNDEFINED;
9358 static int get_token_parameter_value(char *token, char *value_raw)
9362 if (token == NULL || value_raw == NULL)
9363 return ARG_UNDEFINED_VALUE;
9365 suffix = strrchr(token, '.');
9369 if (strEqual(suffix, ".element"))
9370 return getElementFromToken(value_raw);
9372 /* !!! USE CORRECT VALUE TYPE (currently works also for TYPE_BOOLEAN) !!! */
9373 return get_parameter_value(value_raw, suffix, TYPE_INTEGER);
9376 void InitMenuDesignSettings_Static()
9380 /* always start with reliable default values from static default config */
9381 for (i = 0; image_config_vars[i].token != NULL; i++)
9383 char *value = getHashEntry(image_config_hash, image_config_vars[i].token);
9386 *image_config_vars[i].value =
9387 get_token_parameter_value(image_config_vars[i].token, value);
9391 static void InitMenuDesignSettings_SpecialPreProcessing()
9395 /* the following initializes hierarchical values from static configuration */
9397 /* special case: initialize "ARG_DEFAULT" values in static default config */
9398 /* (e.g., initialize "[titlemessage].fade_mode" from "[title].fade_mode") */
9399 titlescreen_initial_first_default.fade_mode =
9400 title_initial_first_default.fade_mode;
9401 titlescreen_initial_first_default.fade_delay =
9402 title_initial_first_default.fade_delay;
9403 titlescreen_initial_first_default.post_delay =
9404 title_initial_first_default.post_delay;
9405 titlescreen_initial_first_default.auto_delay =
9406 title_initial_first_default.auto_delay;
9407 titlescreen_first_default.fade_mode = title_first_default.fade_mode;
9408 titlescreen_first_default.fade_delay = title_first_default.fade_delay;
9409 titlescreen_first_default.post_delay = title_first_default.post_delay;
9410 titlescreen_first_default.auto_delay = title_first_default.auto_delay;
9411 titlemessage_initial_first_default.fade_mode =
9412 title_initial_first_default.fade_mode;
9413 titlemessage_initial_first_default.fade_delay =
9414 title_initial_first_default.fade_delay;
9415 titlemessage_initial_first_default.post_delay =
9416 title_initial_first_default.post_delay;
9417 titlemessage_initial_first_default.auto_delay =
9418 title_initial_first_default.auto_delay;
9419 titlemessage_first_default.fade_mode = title_first_default.fade_mode;
9420 titlemessage_first_default.fade_delay = title_first_default.fade_delay;
9421 titlemessage_first_default.post_delay = title_first_default.post_delay;
9422 titlemessage_first_default.auto_delay = title_first_default.auto_delay;
9424 titlescreen_initial_default.fade_mode = title_initial_default.fade_mode;
9425 titlescreen_initial_default.fade_delay = title_initial_default.fade_delay;
9426 titlescreen_initial_default.post_delay = title_initial_default.post_delay;
9427 titlescreen_initial_default.auto_delay = title_initial_default.auto_delay;
9428 titlescreen_default.fade_mode = title_default.fade_mode;
9429 titlescreen_default.fade_delay = title_default.fade_delay;
9430 titlescreen_default.post_delay = title_default.post_delay;
9431 titlescreen_default.auto_delay = title_default.auto_delay;
9432 titlemessage_initial_default.fade_mode = title_initial_default.fade_mode;
9433 titlemessage_initial_default.fade_delay = title_initial_default.fade_delay;
9434 titlemessage_initial_default.post_delay = title_initial_default.post_delay;
9435 titlemessage_initial_default.auto_delay = title_initial_default.auto_delay;
9436 titlemessage_default.fade_mode = title_default.fade_mode;
9437 titlemessage_default.fade_delay = title_default.fade_delay;
9438 titlemessage_default.post_delay = title_default.post_delay;
9439 titlemessage_default.auto_delay = title_default.auto_delay;
9441 /* special case: initialize "ARG_DEFAULT" values in static default config */
9442 /* (e.g., init "titlemessage_1.fade_mode" from "[titlemessage].fade_mode") */
9443 for (i = 0; i < MAX_NUM_TITLE_MESSAGES; i++)
9445 titlescreen_initial_first[i] = titlescreen_initial_first_default;
9446 titlescreen_first[i] = titlescreen_first_default;
9447 titlemessage_initial_first[i] = titlemessage_initial_first_default;
9448 titlemessage_first[i] = titlemessage_first_default;
9450 titlescreen_initial[i] = titlescreen_initial_default;
9451 titlescreen[i] = titlescreen_default;
9452 titlemessage_initial[i] = titlemessage_initial_default;
9453 titlemessage[i] = titlemessage_default;
9456 /* special case: initialize "ARG_DEFAULT" values in static default config */
9457 /* (eg, init "menu.enter_screen.SCORES.xyz" from "menu.enter_screen.xyz") */
9458 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
9460 if (i == GFX_SPECIAL_ARG_TITLE) /* title values already initialized */
9463 menu.enter_screen[i] = menu.enter_screen[GFX_SPECIAL_ARG_DEFAULT];
9464 menu.leave_screen[i] = menu.leave_screen[GFX_SPECIAL_ARG_DEFAULT];
9465 menu.next_screen[i] = menu.next_screen[GFX_SPECIAL_ARG_DEFAULT];
9468 /* special case: initialize "ARG_DEFAULT" values in static default config */
9469 /* (eg, init "viewport.door_1.MAIN.xyz" from "viewport.door_1.xyz") */
9470 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
9472 viewport.window[i] = viewport.window[GFX_SPECIAL_ARG_DEFAULT];
9473 viewport.playfield[i] = viewport.playfield[GFX_SPECIAL_ARG_DEFAULT];
9474 viewport.door_1[i] = viewport.door_1[GFX_SPECIAL_ARG_DEFAULT];
9476 if (i == GFX_SPECIAL_ARG_EDITOR) /* editor values already initialized */
9479 viewport.door_2[i] = viewport.door_2[GFX_SPECIAL_ARG_DEFAULT];
9483 static void InitMenuDesignSettings_SpecialPostProcessing()
9487 struct XY *dst, *src;
9491 { &game.button.save, &game.button.stop },
9492 { &game.button.pause2, &game.button.pause },
9493 { &game.button.load, &game.button.play },
9494 { &game.button.undo, &game.button.stop },
9495 { &game.button.redo, &game.button.play },
9501 /* special case: initialize later added SETUP list size from LEVELS value */
9502 if (menu.list_size[GAME_MODE_SETUP] == -1)
9503 menu.list_size[GAME_MODE_SETUP] = menu.list_size[GAME_MODE_LEVELS];
9505 /* set default position for snapshot buttons to stop/pause/play buttons */
9506 for (i = 0; game_buttons_xy[i].dst != NULL; i++)
9507 if ((*game_buttons_xy[i].dst).x == -1 &&
9508 (*game_buttons_xy[i].dst).y == -1)
9509 *game_buttons_xy[i].dst = *game_buttons_xy[i].src;
9512 static void InitMenuDesignSettings_SpecialPostProcessing_AfterGraphics()
9516 struct XYTileSize *dst, *src;
9519 editor_buttons_xy[] =
9522 &editor.button.element_left, &editor.palette.element_left,
9523 IMG_GFX_EDITOR_BUTTON_ELEMENT_LEFT
9526 &editor.button.element_middle, &editor.palette.element_middle,
9527 IMG_GFX_EDITOR_BUTTON_ELEMENT_MIDDLE
9530 &editor.button.element_right, &editor.palette.element_right,
9531 IMG_GFX_EDITOR_BUTTON_ELEMENT_RIGHT
9538 /* set default position for element buttons to element graphics */
9539 for (i = 0; editor_buttons_xy[i].dst != NULL; i++)
9541 if ((*editor_buttons_xy[i].dst).x == -1 &&
9542 (*editor_buttons_xy[i].dst).y == -1)
9544 struct GraphicInfo *gd = &graphic_info[editor_buttons_xy[i].graphic];
9546 gd->width = gd->height = editor_buttons_xy[i].src->tile_size;
9548 *editor_buttons_xy[i].dst = *editor_buttons_xy[i].src;
9553 static void LoadMenuDesignSettingsFromFilename(char *filename)
9555 static struct TitleFadingInfo tfi;
9556 static struct TitleMessageInfo tmi;
9557 static struct TokenInfo title_tokens[] =
9559 { TYPE_INTEGER, &tfi.fade_mode, ".fade_mode" },
9560 { TYPE_INTEGER, &tfi.fade_delay, ".fade_delay" },
9561 { TYPE_INTEGER, &tfi.post_delay, ".post_delay" },
9562 { TYPE_INTEGER, &tfi.auto_delay, ".auto_delay" },
9566 static struct TokenInfo titlemessage_tokens[] =
9568 { TYPE_INTEGER, &tmi.x, ".x" },
9569 { TYPE_INTEGER, &tmi.y, ".y" },
9570 { TYPE_INTEGER, &tmi.width, ".width" },
9571 { TYPE_INTEGER, &tmi.height, ".height" },
9572 { TYPE_INTEGER, &tmi.chars, ".chars" },
9573 { TYPE_INTEGER, &tmi.lines, ".lines" },
9574 { TYPE_INTEGER, &tmi.align, ".align" },
9575 { TYPE_INTEGER, &tmi.valign, ".valign" },
9576 { TYPE_INTEGER, &tmi.font, ".font" },
9577 { TYPE_BOOLEAN, &tmi.autowrap, ".autowrap" },
9578 { TYPE_BOOLEAN, &tmi.centered, ".centered" },
9579 { TYPE_BOOLEAN, &tmi.parse_comments, ".parse_comments" },
9580 { TYPE_INTEGER, &tmi.sort_priority, ".sort_priority" },
9581 { TYPE_INTEGER, &tmi.fade_mode, ".fade_mode" },
9582 { TYPE_INTEGER, &tmi.fade_delay, ".fade_delay" },
9583 { TYPE_INTEGER, &tmi.post_delay, ".post_delay" },
9584 { TYPE_INTEGER, &tmi.auto_delay, ".auto_delay" },
9590 struct TitleFadingInfo *info;
9595 /* initialize first titles from "enter screen" definitions, if defined */
9596 { &title_initial_first_default, "menu.enter_screen.TITLE" },
9597 { &title_first_default, "menu.enter_screen.TITLE" },
9599 /* initialize title screens from "next screen" definitions, if defined */
9600 { &title_initial_default, "menu.next_screen.TITLE" },
9601 { &title_default, "menu.next_screen.TITLE" },
9607 struct TitleMessageInfo *array;
9610 titlemessage_arrays[] =
9612 /* initialize first titles from "enter screen" definitions, if defined */
9613 { titlescreen_initial_first, "menu.enter_screen.TITLE" },
9614 { titlescreen_first, "menu.enter_screen.TITLE" },
9615 { titlemessage_initial_first, "menu.enter_screen.TITLE" },
9616 { titlemessage_first, "menu.enter_screen.TITLE" },
9618 /* initialize titles from "next screen" definitions, if defined */
9619 { titlescreen_initial, "menu.next_screen.TITLE" },
9620 { titlescreen, "menu.next_screen.TITLE" },
9621 { titlemessage_initial, "menu.next_screen.TITLE" },
9622 { titlemessage, "menu.next_screen.TITLE" },
9624 /* overwrite titles with title definitions, if defined */
9625 { titlescreen_initial_first, "[title_initial]" },
9626 { titlescreen_first, "[title]" },
9627 { titlemessage_initial_first, "[title_initial]" },
9628 { titlemessage_first, "[title]" },
9630 { titlescreen_initial, "[title_initial]" },
9631 { titlescreen, "[title]" },
9632 { titlemessage_initial, "[title_initial]" },
9633 { titlemessage, "[title]" },
9635 /* overwrite titles with title screen/message definitions, if defined */
9636 { titlescreen_initial_first, "[titlescreen_initial]" },
9637 { titlescreen_first, "[titlescreen]" },
9638 { titlemessage_initial_first, "[titlemessage_initial]" },
9639 { titlemessage_first, "[titlemessage]" },
9641 { titlescreen_initial, "[titlescreen_initial]" },
9642 { titlescreen, "[titlescreen]" },
9643 { titlemessage_initial, "[titlemessage_initial]" },
9644 { titlemessage, "[titlemessage]" },
9648 SetupFileHash *setup_file_hash;
9651 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
9654 /* the following initializes hierarchical values from dynamic configuration */
9656 /* special case: initialize with default values that may be overwritten */
9657 /* (e.g., init "menu.draw_xoffset.INFO" from "menu.draw_xoffset") */
9658 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
9660 char *value_1 = getHashEntry(setup_file_hash, "menu.draw_xoffset");
9661 char *value_2 = getHashEntry(setup_file_hash, "menu.draw_yoffset");
9662 char *value_3 = getHashEntry(setup_file_hash, "menu.list_size");
9664 if (value_1 != NULL)
9665 menu.draw_xoffset[i] = get_integer_from_string(value_1);
9666 if (value_2 != NULL)
9667 menu.draw_yoffset[i] = get_integer_from_string(value_2);
9668 if (value_3 != NULL)
9669 menu.list_size[i] = get_integer_from_string(value_3);
9672 /* special case: initialize with default values that may be overwritten */
9673 /* (eg, init "menu.draw_xoffset.INFO[XXX]" from "menu.draw_xoffset.INFO") */
9674 for (i = 0; i < NUM_SPECIAL_GFX_INFO_ARGS; i++)
9676 char *value_1 = getHashEntry(setup_file_hash, "menu.draw_xoffset.INFO");
9677 char *value_2 = getHashEntry(setup_file_hash, "menu.draw_yoffset.INFO");
9679 if (value_1 != NULL)
9680 menu.draw_xoffset_info[i] = get_integer_from_string(value_1);
9681 if (value_2 != NULL)
9682 menu.draw_yoffset_info[i] = get_integer_from_string(value_2);
9684 if (i == GFX_SPECIAL_ARG_INFO_ELEMENTS)
9686 char *value_1 = getHashEntry(setup_file_hash, "menu.list_size.INFO");
9688 if (value_1 != NULL)
9689 menu.list_size_info[i] = get_integer_from_string(value_1);
9693 /* special case: initialize with default values that may be overwritten */
9694 /* (eg, init "menu.draw_xoffset.SETUP[XXX]" from "menu.draw_xoffset.SETUP") */
9695 for (i = 0; i < NUM_SPECIAL_GFX_SETUP_ARGS; i++)
9697 char *value_1 = getHashEntry(setup_file_hash, "menu.draw_xoffset.SETUP");
9698 char *value_2 = getHashEntry(setup_file_hash, "menu.draw_yoffset.SETUP");
9700 if (value_1 != NULL)
9701 menu.draw_xoffset_setup[i] = get_integer_from_string(value_1);
9702 if (value_2 != NULL)
9703 menu.draw_yoffset_setup[i] = get_integer_from_string(value_2);
9706 /* special case: initialize with default values that may be overwritten */
9707 /* (eg, init "menu.enter_screen.SCORES.xyz" from "menu.enter_screen.xyz") */
9708 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
9710 char *token_1 = "menu.enter_screen.fade_mode";
9711 char *token_2 = "menu.enter_screen.fade_delay";
9712 char *token_3 = "menu.enter_screen.post_delay";
9713 char *token_4 = "menu.leave_screen.fade_mode";
9714 char *token_5 = "menu.leave_screen.fade_delay";
9715 char *token_6 = "menu.leave_screen.post_delay";
9716 char *token_7 = "menu.next_screen.fade_mode";
9717 char *token_8 = "menu.next_screen.fade_delay";
9718 char *token_9 = "menu.next_screen.post_delay";
9719 char *value_1 = getHashEntry(setup_file_hash, token_1);
9720 char *value_2 = getHashEntry(setup_file_hash, token_2);
9721 char *value_3 = getHashEntry(setup_file_hash, token_3);
9722 char *value_4 = getHashEntry(setup_file_hash, token_4);
9723 char *value_5 = getHashEntry(setup_file_hash, token_5);
9724 char *value_6 = getHashEntry(setup_file_hash, token_6);
9725 char *value_7 = getHashEntry(setup_file_hash, token_7);
9726 char *value_8 = getHashEntry(setup_file_hash, token_8);
9727 char *value_9 = getHashEntry(setup_file_hash, token_9);
9729 if (value_1 != NULL)
9730 menu.enter_screen[i].fade_mode = get_token_parameter_value(token_1,
9732 if (value_2 != NULL)
9733 menu.enter_screen[i].fade_delay = get_token_parameter_value(token_2,
9735 if (value_3 != NULL)
9736 menu.enter_screen[i].post_delay = get_token_parameter_value(token_3,
9738 if (value_4 != NULL)
9739 menu.leave_screen[i].fade_mode = get_token_parameter_value(token_4,
9741 if (value_5 != NULL)
9742 menu.leave_screen[i].fade_delay = get_token_parameter_value(token_5,
9744 if (value_6 != NULL)
9745 menu.leave_screen[i].post_delay = get_token_parameter_value(token_6,
9747 if (value_7 != NULL)
9748 menu.next_screen[i].fade_mode = get_token_parameter_value(token_7,
9750 if (value_8 != NULL)
9751 menu.next_screen[i].fade_delay = get_token_parameter_value(token_8,
9753 if (value_9 != NULL)
9754 menu.next_screen[i].post_delay = get_token_parameter_value(token_9,
9758 /* special case: initialize with default values that may be overwritten */
9759 /* (eg, init "viewport.door_1.MAIN.xyz" from "viewport.door_1.xyz") */
9760 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
9762 char *token_w1 = "viewport.window.width";
9763 char *token_w2 = "viewport.window.height";
9764 char *token_01 = "viewport.playfield.x";
9765 char *token_02 = "viewport.playfield.y";
9766 char *token_03 = "viewport.playfield.width";
9767 char *token_04 = "viewport.playfield.height";
9768 char *token_05 = "viewport.playfield.border_size";
9769 char *token_06 = "viewport.door_1.x";
9770 char *token_07 = "viewport.door_1.y";
9771 char *token_08 = "viewport.door_1.width";
9772 char *token_09 = "viewport.door_1.height";
9773 char *token_10 = "viewport.door_1.border_size";
9774 char *token_11 = "viewport.door_2.x";
9775 char *token_12 = "viewport.door_2.y";
9776 char *token_13 = "viewport.door_2.width";
9777 char *token_14 = "viewport.door_2.height";
9778 char *token_15 = "viewport.door_2.border_size";
9779 char *value_w1 = getHashEntry(setup_file_hash, token_w1);
9780 char *value_w2 = getHashEntry(setup_file_hash, token_w2);
9781 char *value_01 = getHashEntry(setup_file_hash, token_01);
9782 char *value_02 = getHashEntry(setup_file_hash, token_02);
9783 char *value_03 = getHashEntry(setup_file_hash, token_03);
9784 char *value_04 = getHashEntry(setup_file_hash, token_04);
9785 char *value_05 = getHashEntry(setup_file_hash, token_05);
9786 char *value_06 = getHashEntry(setup_file_hash, token_06);
9787 char *value_07 = getHashEntry(setup_file_hash, token_07);
9788 char *value_08 = getHashEntry(setup_file_hash, token_08);
9789 char *value_09 = getHashEntry(setup_file_hash, token_09);
9790 char *value_10 = getHashEntry(setup_file_hash, token_10);
9791 char *value_11 = getHashEntry(setup_file_hash, token_11);
9792 char *value_12 = getHashEntry(setup_file_hash, token_12);
9793 char *value_13 = getHashEntry(setup_file_hash, token_13);
9794 char *value_14 = getHashEntry(setup_file_hash, token_14);
9795 char *value_15 = getHashEntry(setup_file_hash, token_15);
9797 if (value_w1 != NULL)
9798 viewport.window[i].width = get_token_parameter_value(token_w1, value_w1);
9799 if (value_w2 != NULL)
9800 viewport.window[i].height = get_token_parameter_value(token_w2, value_w2);
9801 if (value_01 != NULL)
9802 viewport.playfield[i].x = get_token_parameter_value(token_01, value_01);
9803 if (value_02 != NULL)
9804 viewport.playfield[i].y = get_token_parameter_value(token_02, value_02);
9805 if (value_03 != NULL)
9806 viewport.playfield[i].width = get_token_parameter_value(token_03,
9808 if (value_04 != NULL)
9809 viewport.playfield[i].height = get_token_parameter_value(token_04,
9811 if (value_05 != NULL)
9812 viewport.playfield[i].border_size = get_token_parameter_value(token_05,
9814 if (value_06 != NULL)
9815 viewport.door_1[i].x = get_token_parameter_value(token_06, value_06);
9816 if (value_07 != NULL)
9817 viewport.door_1[i].y = get_token_parameter_value(token_07, value_07);
9818 if (value_08 != NULL)
9819 viewport.door_1[i].width = get_token_parameter_value(token_08, value_08);
9820 if (value_09 != NULL)
9821 viewport.door_1[i].height = get_token_parameter_value(token_09, value_09);
9822 if (value_10 != NULL)
9823 viewport.door_1[i].border_size = get_token_parameter_value(token_10,
9825 if (value_11 != NULL)
9826 viewport.door_2[i].x = get_token_parameter_value(token_11, value_11);
9827 if (value_12 != NULL)
9828 viewport.door_2[i].y = get_token_parameter_value(token_12, value_12);
9829 if (value_13 != NULL)
9830 viewport.door_2[i].width = get_token_parameter_value(token_13, value_13);
9831 if (value_14 != NULL)
9832 viewport.door_2[i].height = get_token_parameter_value(token_14, value_14);
9833 if (value_15 != NULL)
9834 viewport.door_1[i].border_size = get_token_parameter_value(token_15,
9838 /* special case: initialize with default values that may be overwritten */
9839 /* (e.g., init "[title].fade_mode" from "menu.next_screen.TITLE.fade_mode") */
9840 for (i = 0; title_info[i].info != NULL; i++)
9842 struct TitleFadingInfo *info = title_info[i].info;
9843 char *base_token = title_info[i].text;
9845 for (j = 0; title_tokens[j].type != -1; j++)
9847 char *token = getStringCat2(base_token, title_tokens[j].text);
9848 char *value = getHashEntry(setup_file_hash, token);
9852 int parameter_value = get_token_parameter_value(token, value);
9856 *(int *)title_tokens[j].value = (int)parameter_value;
9865 /* special case: initialize with default values that may be overwritten */
9866 /* (e.g., init "titlemessage_1.fade_mode" from "[titlemessage].fade_mode") */
9867 for (i = 0; titlemessage_arrays[i].array != NULL; i++)
9869 struct TitleMessageInfo *array = titlemessage_arrays[i].array;
9870 char *base_token = titlemessage_arrays[i].text;
9872 for (j = 0; titlemessage_tokens[j].type != -1; j++)
9874 char *token = getStringCat2(base_token, titlemessage_tokens[j].text);
9875 char *value = getHashEntry(setup_file_hash, token);
9879 int parameter_value = get_token_parameter_value(token, value);
9881 for (k = 0; k < MAX_NUM_TITLE_MESSAGES; k++)
9885 if (titlemessage_tokens[j].type == TYPE_INTEGER)
9886 *(int *)titlemessage_tokens[j].value = (int)parameter_value;
9888 *(boolean *)titlemessage_tokens[j].value = (boolean)parameter_value;
9898 /* read (and overwrite with) values that may be specified in config file */
9899 for (i = 0; image_config_vars[i].token != NULL; i++)
9901 char *value = getHashEntry(setup_file_hash, image_config_vars[i].token);
9903 /* (ignore definitions set to "[DEFAULT]" which are already initialized) */
9904 if (value != NULL && !strEqual(value, ARG_DEFAULT))
9905 *image_config_vars[i].value =
9906 get_token_parameter_value(image_config_vars[i].token, value);
9909 freeSetupFileHash(setup_file_hash);
9912 void LoadMenuDesignSettings()
9914 char *filename_base = UNDEFINED_FILENAME, *filename_local;
9916 InitMenuDesignSettings_Static();
9917 InitMenuDesignSettings_SpecialPreProcessing();
9919 if (!GFX_OVERRIDE_ARTWORK(ARTWORK_TYPE_GRAPHICS))
9921 /* first look for special settings configured in level series config */
9922 filename_base = getCustomArtworkLevelConfigFilename(ARTWORK_TYPE_GRAPHICS);
9924 if (fileExists(filename_base))
9925 LoadMenuDesignSettingsFromFilename(filename_base);
9928 filename_local = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
9930 if (filename_local != NULL && !strEqual(filename_base, filename_local))
9931 LoadMenuDesignSettingsFromFilename(filename_local);
9933 InitMenuDesignSettings_SpecialPostProcessing();
9936 void LoadMenuDesignSettings_AfterGraphics()
9938 InitMenuDesignSettings_SpecialPostProcessing_AfterGraphics();
9941 void LoadUserDefinedEditorElementList(int **elements, int *num_elements)
9943 char *filename = getEditorSetupFilename();
9944 SetupFileList *setup_file_list, *list;
9945 SetupFileHash *element_hash;
9946 int num_unknown_tokens = 0;
9949 if ((setup_file_list = loadSetupFileList(filename)) == NULL)
9952 element_hash = newSetupFileHash();
9954 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
9955 setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
9957 /* determined size may be larger than needed (due to unknown elements) */
9959 for (list = setup_file_list; list != NULL; list = list->next)
9962 /* add space for up to 3 more elements for padding that may be needed */
9965 /* free memory for old list of elements, if needed */
9966 checked_free(*elements);
9968 /* allocate memory for new list of elements */
9969 *elements = checked_malloc(*num_elements * sizeof(int));
9972 for (list = setup_file_list; list != NULL; list = list->next)
9974 char *value = getHashEntry(element_hash, list->token);
9976 if (value == NULL) /* try to find obsolete token mapping */
9978 char *mapped_token = get_mapped_token(list->token);
9980 if (mapped_token != NULL)
9982 value = getHashEntry(element_hash, mapped_token);
9990 (*elements)[(*num_elements)++] = atoi(value);
9994 if (num_unknown_tokens == 0)
9996 Error(ERR_INFO_LINE, "-");
9997 Error(ERR_INFO, "warning: unknown token(s) found in config file:");
9998 Error(ERR_INFO, "- config file: '%s'", filename);
10000 num_unknown_tokens++;
10003 Error(ERR_INFO, "- token: '%s'", list->token);
10007 if (num_unknown_tokens > 0)
10008 Error(ERR_INFO_LINE, "-");
10010 while (*num_elements % 4) /* pad with empty elements, if needed */
10011 (*elements)[(*num_elements)++] = EL_EMPTY;
10013 freeSetupFileList(setup_file_list);
10014 freeSetupFileHash(element_hash);
10017 for (i = 0; i < *num_elements; i++)
10018 printf("editor: element '%s' [%d]\n",
10019 element_info[(*elements)[i]].token_name, (*elements)[i]);
10023 static struct MusicFileInfo *get_music_file_info_ext(char *basename, int music,
10026 SetupFileHash *setup_file_hash = NULL;
10027 struct MusicFileInfo tmp_music_file_info, *new_music_file_info;
10028 char *filename_music, *filename_prefix, *filename_info;
10034 token_to_value_ptr[] =
10036 { "title_header", &tmp_music_file_info.title_header },
10037 { "artist_header", &tmp_music_file_info.artist_header },
10038 { "album_header", &tmp_music_file_info.album_header },
10039 { "year_header", &tmp_music_file_info.year_header },
10041 { "title", &tmp_music_file_info.title },
10042 { "artist", &tmp_music_file_info.artist },
10043 { "album", &tmp_music_file_info.album },
10044 { "year", &tmp_music_file_info.year },
10050 filename_music = (is_sound ? getCustomSoundFilename(basename) :
10051 getCustomMusicFilename(basename));
10053 if (filename_music == NULL)
10056 /* ---------- try to replace file extension ---------- */
10058 filename_prefix = getStringCopy(filename_music);
10059 if (strrchr(filename_prefix, '.') != NULL)
10060 *strrchr(filename_prefix, '.') = '\0';
10061 filename_info = getStringCat2(filename_prefix, ".txt");
10063 if (fileExists(filename_info))
10064 setup_file_hash = loadSetupFileHash(filename_info);
10066 free(filename_prefix);
10067 free(filename_info);
10069 if (setup_file_hash == NULL)
10071 /* ---------- try to add file extension ---------- */
10073 filename_prefix = getStringCopy(filename_music);
10074 filename_info = getStringCat2(filename_prefix, ".txt");
10076 if (fileExists(filename_info))
10077 setup_file_hash = loadSetupFileHash(filename_info);
10079 free(filename_prefix);
10080 free(filename_info);
10083 if (setup_file_hash == NULL)
10086 /* ---------- music file info found ---------- */
10088 clear_mem(&tmp_music_file_info, sizeof(struct MusicFileInfo));
10090 for (i = 0; token_to_value_ptr[i].token != NULL; i++)
10092 char *value = getHashEntry(setup_file_hash, token_to_value_ptr[i].token);
10094 *token_to_value_ptr[i].value_ptr =
10095 getStringCopy(value != NULL && *value != '\0' ? value : UNKNOWN_NAME);
10098 tmp_music_file_info.basename = getStringCopy(basename);
10099 tmp_music_file_info.music = music;
10100 tmp_music_file_info.is_sound = is_sound;
10102 new_music_file_info = checked_malloc(sizeof(struct MusicFileInfo));
10103 *new_music_file_info = tmp_music_file_info;
10105 return new_music_file_info;
10108 static struct MusicFileInfo *get_music_file_info(char *basename, int music)
10110 return get_music_file_info_ext(basename, music, FALSE);
10113 static struct MusicFileInfo *get_sound_file_info(char *basename, int sound)
10115 return get_music_file_info_ext(basename, sound, TRUE);
10118 static boolean music_info_listed_ext(struct MusicFileInfo *list,
10119 char *basename, boolean is_sound)
10121 for (; list != NULL; list = list->next)
10122 if (list->is_sound == is_sound && strEqual(list->basename, basename))
10128 static boolean music_info_listed(struct MusicFileInfo *list, char *basename)
10130 return music_info_listed_ext(list, basename, FALSE);
10133 static boolean sound_info_listed(struct MusicFileInfo *list, char *basename)
10135 return music_info_listed_ext(list, basename, TRUE);
10138 void LoadMusicInfo()
10140 char *music_directory = getCustomMusicDirectory();
10141 int num_music = getMusicListSize();
10142 int num_music_noconf = 0;
10143 int num_sounds = getSoundListSize();
10145 DirectoryEntry *dir_entry;
10146 struct FileInfo *music, *sound;
10147 struct MusicFileInfo *next, **new;
10150 while (music_file_info != NULL)
10152 next = music_file_info->next;
10154 checked_free(music_file_info->basename);
10156 checked_free(music_file_info->title_header);
10157 checked_free(music_file_info->artist_header);
10158 checked_free(music_file_info->album_header);
10159 checked_free(music_file_info->year_header);
10161 checked_free(music_file_info->title);
10162 checked_free(music_file_info->artist);
10163 checked_free(music_file_info->album);
10164 checked_free(music_file_info->year);
10166 free(music_file_info);
10168 music_file_info = next;
10171 new = &music_file_info;
10173 for (i = 0; i < num_music; i++)
10175 music = getMusicListEntry(i);
10177 if (music->filename == NULL)
10180 if (strEqual(music->filename, UNDEFINED_FILENAME))
10183 /* a configured file may be not recognized as music */
10184 if (!FileIsMusic(music->filename))
10187 if (!music_info_listed(music_file_info, music->filename))
10189 *new = get_music_file_info(music->filename, i);
10192 new = &(*new)->next;
10196 if ((dir = openDirectory(music_directory)) == NULL)
10198 Error(ERR_WARN, "cannot read music directory '%s'", music_directory);
10202 while ((dir_entry = readDirectory(dir)) != NULL) /* loop all entries */
10204 char *basename = dir_entry->basename;
10205 boolean music_already_used = FALSE;
10208 /* skip all music files that are configured in music config file */
10209 for (i = 0; i < num_music; i++)
10211 music = getMusicListEntry(i);
10213 if (music->filename == NULL)
10216 if (strEqual(basename, music->filename))
10218 music_already_used = TRUE;
10223 if (music_already_used)
10226 if (!FileIsMusic(dir_entry->filename))
10229 if (!music_info_listed(music_file_info, basename))
10231 *new = get_music_file_info(basename, MAP_NOCONF_MUSIC(num_music_noconf));
10234 new = &(*new)->next;
10237 num_music_noconf++;
10240 closeDirectory(dir);
10242 for (i = 0; i < num_sounds; i++)
10244 sound = getSoundListEntry(i);
10246 if (sound->filename == NULL)
10249 if (strEqual(sound->filename, UNDEFINED_FILENAME))
10252 /* a configured file may be not recognized as sound */
10253 if (!FileIsSound(sound->filename))
10256 if (!sound_info_listed(music_file_info, sound->filename))
10258 *new = get_sound_file_info(sound->filename, i);
10260 new = &(*new)->next;
10265 void add_helpanim_entry(int element, int action, int direction, int delay,
10266 int *num_list_entries)
10268 struct HelpAnimInfo *new_list_entry;
10269 (*num_list_entries)++;
10272 checked_realloc(helpanim_info,
10273 *num_list_entries * sizeof(struct HelpAnimInfo));
10274 new_list_entry = &helpanim_info[*num_list_entries - 1];
10276 new_list_entry->element = element;
10277 new_list_entry->action = action;
10278 new_list_entry->direction = direction;
10279 new_list_entry->delay = delay;
10282 void print_unknown_token(char *filename, char *token, int token_nr)
10286 Error(ERR_INFO_LINE, "-");
10287 Error(ERR_INFO, "warning: unknown token(s) found in config file:");
10288 Error(ERR_INFO, "- config file: '%s'", filename);
10291 Error(ERR_INFO, "- token: '%s'", token);
10294 void print_unknown_token_end(int token_nr)
10297 Error(ERR_INFO_LINE, "-");
10300 void LoadHelpAnimInfo()
10302 char *filename = getHelpAnimFilename();
10303 SetupFileList *setup_file_list = NULL, *list;
10304 SetupFileHash *element_hash, *action_hash, *direction_hash;
10305 int num_list_entries = 0;
10306 int num_unknown_tokens = 0;
10309 if (fileExists(filename))
10310 setup_file_list = loadSetupFileList(filename);
10312 if (setup_file_list == NULL)
10314 /* use reliable default values from static configuration */
10315 SetupFileList *insert_ptr;
10317 insert_ptr = setup_file_list =
10318 newSetupFileList(helpanim_config[0].token,
10319 helpanim_config[0].value);
10321 for (i = 1; helpanim_config[i].token; i++)
10322 insert_ptr = addListEntry(insert_ptr,
10323 helpanim_config[i].token,
10324 helpanim_config[i].value);
10327 element_hash = newSetupFileHash();
10328 action_hash = newSetupFileHash();
10329 direction_hash = newSetupFileHash();
10331 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
10332 setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
10334 for (i = 0; i < NUM_ACTIONS; i++)
10335 setHashEntry(action_hash, element_action_info[i].suffix,
10336 i_to_a(element_action_info[i].value));
10338 /* do not store direction index (bit) here, but direction value! */
10339 for (i = 0; i < NUM_DIRECTIONS_FULL; i++)
10340 setHashEntry(direction_hash, element_direction_info[i].suffix,
10341 i_to_a(1 << element_direction_info[i].value));
10343 for (list = setup_file_list; list != NULL; list = list->next)
10345 char *element_token, *action_token, *direction_token;
10346 char *element_value, *action_value, *direction_value;
10347 int delay = atoi(list->value);
10349 if (strEqual(list->token, "end"))
10351 add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
10356 /* first try to break element into element/action/direction parts;
10357 if this does not work, also accept combined "element[.act][.dir]"
10358 elements (like "dynamite.active"), which are unique elements */
10360 if (strchr(list->token, '.') == NULL) /* token contains no '.' */
10362 element_value = getHashEntry(element_hash, list->token);
10363 if (element_value != NULL) /* element found */
10364 add_helpanim_entry(atoi(element_value), -1, -1, delay,
10365 &num_list_entries);
10368 /* no further suffixes found -- this is not an element */
10369 print_unknown_token(filename, list->token, num_unknown_tokens++);
10375 /* token has format "<prefix>.<something>" */
10377 action_token = strchr(list->token, '.'); /* suffix may be action ... */
10378 direction_token = action_token; /* ... or direction */
10380 element_token = getStringCopy(list->token);
10381 *strchr(element_token, '.') = '\0';
10383 element_value = getHashEntry(element_hash, element_token);
10385 if (element_value == NULL) /* this is no element */
10387 element_value = getHashEntry(element_hash, list->token);
10388 if (element_value != NULL) /* combined element found */
10389 add_helpanim_entry(atoi(element_value), -1, -1, delay,
10390 &num_list_entries);
10392 print_unknown_token(filename, list->token, num_unknown_tokens++);
10394 free(element_token);
10399 action_value = getHashEntry(action_hash, action_token);
10401 if (action_value != NULL) /* action found */
10403 add_helpanim_entry(atoi(element_value), atoi(action_value), -1, delay,
10404 &num_list_entries);
10406 free(element_token);
10411 direction_value = getHashEntry(direction_hash, direction_token);
10413 if (direction_value != NULL) /* direction found */
10415 add_helpanim_entry(atoi(element_value), -1, atoi(direction_value), delay,
10416 &num_list_entries);
10418 free(element_token);
10423 if (strchr(action_token + 1, '.') == NULL)
10425 /* no further suffixes found -- this is not an action nor direction */
10427 element_value = getHashEntry(element_hash, list->token);
10428 if (element_value != NULL) /* combined element found */
10429 add_helpanim_entry(atoi(element_value), -1, -1, delay,
10430 &num_list_entries);
10432 print_unknown_token(filename, list->token, num_unknown_tokens++);
10434 free(element_token);
10439 /* token has format "<prefix>.<suffix>.<something>" */
10441 direction_token = strchr(action_token + 1, '.');
10443 action_token = getStringCopy(action_token);
10444 *strchr(action_token + 1, '.') = '\0';
10446 action_value = getHashEntry(action_hash, action_token);
10448 if (action_value == NULL) /* this is no action */
10450 element_value = getHashEntry(element_hash, list->token);
10451 if (element_value != NULL) /* combined element found */
10452 add_helpanim_entry(atoi(element_value), -1, -1, delay,
10453 &num_list_entries);
10455 print_unknown_token(filename, list->token, num_unknown_tokens++);
10457 free(element_token);
10458 free(action_token);
10463 direction_value = getHashEntry(direction_hash, direction_token);
10465 if (direction_value != NULL) /* direction found */
10467 add_helpanim_entry(atoi(element_value), atoi(action_value),
10468 atoi(direction_value), delay, &num_list_entries);
10470 free(element_token);
10471 free(action_token);
10476 /* this is no direction */
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);
10489 print_unknown_token_end(num_unknown_tokens);
10491 add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
10492 add_helpanim_entry(HELPANIM_LIST_END, -1, -1, -1, &num_list_entries);
10494 freeSetupFileList(setup_file_list);
10495 freeSetupFileHash(element_hash);
10496 freeSetupFileHash(action_hash);
10497 freeSetupFileHash(direction_hash);
10500 for (i = 0; i < num_list_entries; i++)
10501 printf("::: '%s': %d, %d, %d => %d\n",
10502 EL_NAME(helpanim_info[i].element),
10503 helpanim_info[i].element,
10504 helpanim_info[i].action,
10505 helpanim_info[i].direction,
10506 helpanim_info[i].delay);
10510 void LoadHelpTextInfo()
10512 char *filename = getHelpTextFilename();
10515 if (helptext_info != NULL)
10517 freeSetupFileHash(helptext_info);
10518 helptext_info = NULL;
10521 if (fileExists(filename))
10522 helptext_info = loadSetupFileHash(filename);
10524 if (helptext_info == NULL)
10526 /* use reliable default values from static configuration */
10527 helptext_info = newSetupFileHash();
10529 for (i = 0; helptext_config[i].token; i++)
10530 setHashEntry(helptext_info,
10531 helptext_config[i].token,
10532 helptext_config[i].value);
10536 BEGIN_HASH_ITERATION(helptext_info, itr)
10538 printf("::: '%s' => '%s'\n",
10539 HASH_ITERATION_TOKEN(itr), HASH_ITERATION_VALUE(itr));
10541 END_HASH_ITERATION(hash, itr)
10546 /* ------------------------------------------------------------------------- */
10547 /* convert levels */
10548 /* ------------------------------------------------------------------------- */
10550 #define MAX_NUM_CONVERT_LEVELS 1000
10552 void ConvertLevels()
10554 static LevelDirTree *convert_leveldir = NULL;
10555 static int convert_level_nr = -1;
10556 static int num_levels_handled = 0;
10557 static int num_levels_converted = 0;
10558 static boolean levels_failed[MAX_NUM_CONVERT_LEVELS];
10561 convert_leveldir = getTreeInfoFromIdentifier(leveldir_first,
10562 global.convert_leveldir);
10564 if (convert_leveldir == NULL)
10565 Error(ERR_EXIT, "no such level identifier: '%s'",
10566 global.convert_leveldir);
10568 leveldir_current = convert_leveldir;
10570 if (global.convert_level_nr != -1)
10572 convert_leveldir->first_level = global.convert_level_nr;
10573 convert_leveldir->last_level = global.convert_level_nr;
10576 convert_level_nr = convert_leveldir->first_level;
10578 PrintLine("=", 79);
10579 Print("Converting levels\n");
10580 PrintLine("-", 79);
10581 Print("Level series identifier: '%s'\n", convert_leveldir->identifier);
10582 Print("Level series name: '%s'\n", convert_leveldir->name);
10583 Print("Level series author: '%s'\n", convert_leveldir->author);
10584 Print("Number of levels: %d\n", convert_leveldir->levels);
10585 PrintLine("=", 79);
10588 for (i = 0; i < MAX_NUM_CONVERT_LEVELS; i++)
10589 levels_failed[i] = FALSE;
10591 while (convert_level_nr <= convert_leveldir->last_level)
10593 char *level_filename;
10596 level_nr = convert_level_nr++;
10598 Print("Level %03d: ", level_nr);
10600 LoadLevel(level_nr);
10601 if (level.no_level_file || level.no_valid_file)
10603 Print("(no level)\n");
10607 Print("converting level ... ");
10609 level_filename = getDefaultLevelFilename(level_nr);
10610 new_level = !fileExists(level_filename);
10614 SaveLevel(level_nr);
10616 num_levels_converted++;
10618 Print("converted.\n");
10622 if (level_nr >= 0 && level_nr < MAX_NUM_CONVERT_LEVELS)
10623 levels_failed[level_nr] = TRUE;
10625 Print("NOT CONVERTED -- LEVEL ALREADY EXISTS.\n");
10628 num_levels_handled++;
10632 PrintLine("=", 79);
10633 Print("Number of levels handled: %d\n", num_levels_handled);
10634 Print("Number of levels converted: %d (%d%%)\n", num_levels_converted,
10635 (num_levels_handled ?
10636 num_levels_converted * 100 / num_levels_handled : 0));
10637 PrintLine("-", 79);
10638 Print("Summary (for automatic parsing by scripts):\n");
10639 Print("LEVELDIR '%s', CONVERTED %d/%d (%d%%)",
10640 convert_leveldir->identifier, num_levels_converted,
10641 num_levels_handled,
10642 (num_levels_handled ?
10643 num_levels_converted * 100 / num_levels_handled : 0));
10645 if (num_levels_handled != num_levels_converted)
10647 Print(", FAILED:");
10648 for (i = 0; i < MAX_NUM_CONVERT_LEVELS; i++)
10649 if (levels_failed[i])
10654 PrintLine("=", 79);
10656 CloseAllAndExit(0);
10660 /* ------------------------------------------------------------------------- */
10661 /* create and save images for use in level sketches (raw BMP format) */
10662 /* ------------------------------------------------------------------------- */
10664 void CreateLevelSketchImages()
10666 #if defined(TARGET_SDL)
10671 InitElementPropertiesGfxElement();
10673 bitmap1 = CreateBitmap(TILEX, TILEY, DEFAULT_DEPTH);
10674 bitmap2 = CreateBitmap(MINI_TILEX, MINI_TILEY, DEFAULT_DEPTH);
10676 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
10678 Bitmap *src_bitmap;
10680 int element = getMappedElement(i);
10681 int graphic = el2edimg(element);
10682 char basename1[16];
10683 char basename2[16];
10687 sprintf(basename1, "%03d.bmp", i);
10688 sprintf(basename2, "%03ds.bmp", i);
10690 filename1 = getPath2(global.create_images_dir, basename1);
10691 filename2 = getPath2(global.create_images_dir, basename2);
10693 getFixedGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
10694 BlitBitmap(src_bitmap, bitmap1, src_x, src_y, TILEX, TILEY,
10697 if (SDL_SaveBMP(bitmap1->surface, filename1) != 0)
10698 Error(ERR_EXIT, "cannot save level sketch image file '%s'", filename1);
10700 getMiniGraphicSource(graphic, &src_bitmap, &src_x, &src_y);
10701 BlitBitmap(src_bitmap, bitmap2, src_x, src_y, MINI_TILEX, MINI_TILEY, 0, 0);
10703 if (SDL_SaveBMP(bitmap2->surface, filename2) != 0)
10704 Error(ERR_EXIT, "cannot save level sketch image file '%s'", filename2);
10710 printf("%03d `%03d%c", i, i, (i % 10 < 9 ? ' ' : '\n'));
10713 FreeBitmap(bitmap1);
10714 FreeBitmap(bitmap2);
10719 Error(ERR_INFO, "%d normal and small images created", NUM_FILE_ELEMENTS);
10721 CloseAllAndExit(0);
10726 /* ------------------------------------------------------------------------- */
10727 /* create and save images for custom and group elements (raw BMP format) */
10728 /* ------------------------------------------------------------------------- */
10730 void CreateCustomElementImages(char *directory)
10732 #if defined(TARGET_SDL)
10733 char *src_basename = "RocksCE-template.ilbm";
10734 char *dst_basename = "RocksCE.bmp";
10735 char *src_filename = getPath2(directory, src_basename);
10736 char *dst_filename = getPath2(directory, dst_basename);
10737 Bitmap *src_bitmap;
10739 int yoffset_ce = 0;
10740 int yoffset_ge = (TILEY * NUM_CUSTOM_ELEMENTS / 16);
10743 SDLInitVideoDisplay();
10745 ReCreateBitmap(&backbuffer, video.width, video.height);
10747 src_bitmap = LoadImage(src_filename);
10749 bitmap = CreateBitmap(TILEX * 16 * 2,
10750 TILEY * (NUM_CUSTOM_ELEMENTS + NUM_GROUP_ELEMENTS) / 16,
10753 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
10760 BlitBitmap(src_bitmap, bitmap, 0, 0, TILEX, TILEY,
10761 TILEX * x, TILEY * y + yoffset_ce);
10763 BlitBitmap(src_bitmap, bitmap, 0, TILEY,
10765 TILEX * x + TILEX * 16,
10766 TILEY * y + yoffset_ce);
10768 for (j = 2; j >= 0; j--)
10772 BlitBitmap(src_bitmap, bitmap,
10773 TILEX + c * 7, 0, 6, 10,
10774 TILEX * x + 6 + j * 7,
10775 TILEY * y + 11 + yoffset_ce);
10777 BlitBitmap(src_bitmap, bitmap,
10778 TILEX + c * 8, TILEY, 6, 10,
10779 TILEX * 16 + TILEX * x + 6 + j * 8,
10780 TILEY * y + 10 + yoffset_ce);
10786 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
10793 BlitBitmap(src_bitmap, bitmap, 0, 0, TILEX, TILEY,
10794 TILEX * x, TILEY * y + yoffset_ge);
10796 BlitBitmap(src_bitmap, bitmap, 0, TILEY,
10798 TILEX * x + TILEX * 16,
10799 TILEY * y + yoffset_ge);
10801 for (j = 1; j >= 0; j--)
10805 BlitBitmap(src_bitmap, bitmap, TILEX + c * 10, 11, 10, 10,
10806 TILEX * x + 6 + j * 10,
10807 TILEY * y + 11 + yoffset_ge);
10809 BlitBitmap(src_bitmap, bitmap,
10810 TILEX + c * 8, TILEY + 12, 6, 10,
10811 TILEX * 16 + TILEX * x + 10 + j * 8,
10812 TILEY * y + 10 + yoffset_ge);
10818 if (SDL_SaveBMP(bitmap->surface, dst_filename) != 0)
10819 Error(ERR_EXIT, "cannot save CE graphics file '%s'", dst_filename);
10821 FreeBitmap(bitmap);
10823 CloseAllAndExit(0);