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_EDITOR_EL_CLASSIC 0
8256 #define SETUP_TOKEN_EDITOR_EL_CUSTOM 1
8257 #define SETUP_TOKEN_EDITOR_EL_USER_DEFINED 2
8258 #define SETUP_TOKEN_EDITOR_EL_DYNAMIC 3
8259 #define SETUP_TOKEN_EDITOR_EL_HEADLINES 4
8260 #define SETUP_TOKEN_EDITOR_SHOW_ELEMENT_TOKEN 5
8262 #define NUM_EDITOR_SETUP_TOKENS 6
8264 /* editor cascade setup */
8265 #define SETUP_TOKEN_EDITOR_CASCADE_BD 0
8266 #define SETUP_TOKEN_EDITOR_CASCADE_EM 1
8267 #define SETUP_TOKEN_EDITOR_CASCADE_EMC 2
8268 #define SETUP_TOKEN_EDITOR_CASCADE_RND 3
8269 #define SETUP_TOKEN_EDITOR_CASCADE_SB 4
8270 #define SETUP_TOKEN_EDITOR_CASCADE_SP 5
8271 #define SETUP_TOKEN_EDITOR_CASCADE_DC 6
8272 #define SETUP_TOKEN_EDITOR_CASCADE_DX 7
8273 #define SETUP_TOKEN_EDITOR_CASCADE_TEXT 8
8274 #define SETUP_TOKEN_EDITOR_CASCADE_STEELTEXT 9
8275 #define SETUP_TOKEN_EDITOR_CASCADE_CE 10
8276 #define SETUP_TOKEN_EDITOR_CASCADE_GE 11
8277 #define SETUP_TOKEN_EDITOR_CASCADE_REF 12
8278 #define SETUP_TOKEN_EDITOR_CASCADE_USER 13
8279 #define SETUP_TOKEN_EDITOR_CASCADE_DYNAMIC 14
8281 #define NUM_EDITOR_CASCADE_SETUP_TOKENS 15
8283 /* shortcut setup */
8284 #define SETUP_TOKEN_SHORTCUT_SAVE_GAME 0
8285 #define SETUP_TOKEN_SHORTCUT_LOAD_GAME 1
8286 #define SETUP_TOKEN_SHORTCUT_TOGGLE_PAUSE 2
8287 #define SETUP_TOKEN_SHORTCUT_FOCUS_PLAYER_1 3
8288 #define SETUP_TOKEN_SHORTCUT_FOCUS_PLAYER_2 4
8289 #define SETUP_TOKEN_SHORTCUT_FOCUS_PLAYER_3 5
8290 #define SETUP_TOKEN_SHORTCUT_FOCUS_PLAYER_4 6
8291 #define SETUP_TOKEN_SHORTCUT_FOCUS_PLAYER_ALL 7
8292 #define SETUP_TOKEN_SHORTCUT_TAPE_EJECT 8
8293 #define SETUP_TOKEN_SHORTCUT_TAPE_EXTRA 9
8294 #define SETUP_TOKEN_SHORTCUT_TAPE_STOP 10
8295 #define SETUP_TOKEN_SHORTCUT_TAPE_PAUSE 11
8296 #define SETUP_TOKEN_SHORTCUT_TAPE_RECORD 12
8297 #define SETUP_TOKEN_SHORTCUT_TAPE_PLAY 13
8298 #define SETUP_TOKEN_SHORTCUT_SOUND_SIMPLE 14
8299 #define SETUP_TOKEN_SHORTCUT_SOUND_LOOPS 15
8300 #define SETUP_TOKEN_SHORTCUT_SOUND_MUSIC 16
8301 #define SETUP_TOKEN_SHORTCUT_SNAP_LEFT 17
8302 #define SETUP_TOKEN_SHORTCUT_SNAP_RIGHT 18
8303 #define SETUP_TOKEN_SHORTCUT_SNAP_UP 19
8304 #define SETUP_TOKEN_SHORTCUT_SNAP_DOWN 20
8306 #define NUM_SHORTCUT_SETUP_TOKENS 21
8309 #define SETUP_TOKEN_PLAYER_USE_JOYSTICK 0
8310 #define SETUP_TOKEN_PLAYER_JOY_DEVICE_NAME 1
8311 #define SETUP_TOKEN_PLAYER_JOY_XLEFT 2
8312 #define SETUP_TOKEN_PLAYER_JOY_XMIDDLE 3
8313 #define SETUP_TOKEN_PLAYER_JOY_XRIGHT 4
8314 #define SETUP_TOKEN_PLAYER_JOY_YUPPER 5
8315 #define SETUP_TOKEN_PLAYER_JOY_YMIDDLE 6
8316 #define SETUP_TOKEN_PLAYER_JOY_YLOWER 7
8317 #define SETUP_TOKEN_PLAYER_JOY_SNAP 8
8318 #define SETUP_TOKEN_PLAYER_JOY_DROP 9
8319 #define SETUP_TOKEN_PLAYER_KEY_LEFT 10
8320 #define SETUP_TOKEN_PLAYER_KEY_RIGHT 11
8321 #define SETUP_TOKEN_PLAYER_KEY_UP 12
8322 #define SETUP_TOKEN_PLAYER_KEY_DOWN 13
8323 #define SETUP_TOKEN_PLAYER_KEY_SNAP 14
8324 #define SETUP_TOKEN_PLAYER_KEY_DROP 15
8326 #define NUM_PLAYER_SETUP_TOKENS 16
8329 #define SETUP_TOKEN_SYSTEM_SDL_VIDEODRIVER 0
8330 #define SETUP_TOKEN_SYSTEM_SDL_AUDIODRIVER 1
8331 #define SETUP_TOKEN_SYSTEM_AUDIO_FRAGMENT_SIZE 2
8333 #define NUM_SYSTEM_SETUP_TOKENS 3
8335 /* internal setup */
8336 #define SETUP_TOKEN_INT_PROGRAM_TITLE 0
8337 #define SETUP_TOKEN_INT_PROGRAM_VERSION 1
8338 #define SETUP_TOKEN_INT_PROGRAM_AUTHOR 2
8339 #define SETUP_TOKEN_INT_PROGRAM_EMAIL 3
8340 #define SETUP_TOKEN_INT_PROGRAM_WEBSITE 4
8341 #define SETUP_TOKEN_INT_PROGRAM_COPYRIGHT 5
8342 #define SETUP_TOKEN_INT_PROGRAM_COMPANY 6
8343 #define SETUP_TOKEN_INT_PROGRAM_ICON_FILE 7
8344 #define SETUP_TOKEN_INT_DEFAULT_GRAPHICS_SET 8
8345 #define SETUP_TOKEN_INT_DEFAULT_SOUNDS_SET 9
8346 #define SETUP_TOKEN_INT_DEFAULT_MUSIC_SET 10
8347 #define SETUP_TOKEN_INT_FALLBACK_GRAPHICS_FILE 11
8348 #define SETUP_TOKEN_INT_FALLBACK_SOUNDS_FILE 12
8349 #define SETUP_TOKEN_INT_FALLBACK_MUSIC_FILE 13
8350 #define SETUP_TOKEN_INT_DEFAULT_LEVEL_SERIES 14
8351 #define SETUP_TOKEN_INT_CHOOSE_FROM_TOP_LEVELDIR 15
8352 #define SETUP_TOKEN_INT_SHOW_SCALING_IN_TITLE 16
8353 #define SETUP_TOKEN_INT_DEFAULT_WINDOW_WIDTH 17
8354 #define SETUP_TOKEN_INT_DEFAULT_WINDOW_HEIGHT 18
8356 #define NUM_INTERNAL_SETUP_TOKENS 19
8359 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_0 0
8360 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_1 1
8361 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_2 2
8362 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_3 3
8363 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_4 4
8364 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_5 5
8365 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_6 6
8366 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_7 7
8367 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_8 8
8368 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_9 9
8369 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_KEY_0 10
8370 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_KEY_1 11
8371 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_KEY_2 12
8372 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_KEY_3 13
8373 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_KEY_4 14
8374 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_KEY_5 15
8375 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_KEY_6 16
8376 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_KEY_7 17
8377 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_KEY_8 18
8378 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_KEY_9 19
8379 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_USE_MOD_KEY 20
8380 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_GAME_ONLY 21
8381 #define SETUP_TOKEN_DEBUG_SHOW_FRAMES_PER_SECOND 22
8383 #define NUM_DEBUG_SETUP_TOKENS 23
8386 #define SETUP_TOKEN_OPTIONS_VERBOSE 0
8388 #define NUM_OPTIONS_SETUP_TOKENS 1
8391 static struct SetupInfo si;
8392 static struct SetupEditorInfo sei;
8393 static struct SetupEditorCascadeInfo seci;
8394 static struct SetupShortcutInfo ssi;
8395 static struct SetupInputInfo sii;
8396 static struct SetupSystemInfo syi;
8397 static struct SetupInternalInfo sxi;
8398 static struct SetupDebugInfo sdi;
8399 static struct OptionInfo soi;
8401 static struct TokenInfo global_setup_tokens[] =
8403 { TYPE_STRING, &si.player_name, "player_name" },
8404 { TYPE_SWITCH, &si.sound, "sound" },
8405 { TYPE_SWITCH, &si.sound_loops, "repeating_sound_loops" },
8406 { TYPE_SWITCH, &si.sound_music, "background_music" },
8407 { TYPE_SWITCH, &si.sound_simple, "simple_sound_effects" },
8408 { TYPE_SWITCH, &si.toons, "toons" },
8409 { TYPE_SWITCH, &si.scroll_delay, "scroll_delay" },
8410 { TYPE_INTEGER,&si.scroll_delay_value, "scroll_delay_value" },
8411 { TYPE_STRING, &si.engine_snapshot_mode, "engine_snapshot_mode" },
8412 { TYPE_INTEGER,&si.engine_snapshot_memory, "engine_snapshot_memory" },
8413 { TYPE_SWITCH, &si.fade_screens, "fade_screens" },
8414 { TYPE_SWITCH, &si.autorecord, "automatic_tape_recording"},
8415 { TYPE_SWITCH, &si.show_titlescreen, "show_titlescreen" },
8416 { TYPE_SWITCH, &si.quick_doors, "quick_doors" },
8417 { TYPE_SWITCH, &si.team_mode, "team_mode" },
8418 { TYPE_SWITCH, &si.handicap, "handicap" },
8419 { TYPE_SWITCH, &si.skip_levels, "skip_levels" },
8420 { TYPE_SWITCH, &si.increment_levels, "increment_levels" },
8421 { TYPE_SWITCH, &si.time_limit, "time_limit" },
8422 { TYPE_SWITCH, &si.fullscreen, "fullscreen" },
8423 { TYPE_INTEGER,&si.window_scaling_percent, "window_scaling_percent" },
8424 { TYPE_STRING, &si.window_scaling_quality, "window_scaling_quality" },
8425 { TYPE_STRING, &si.screen_rendering_mode, "screen_rendering_mode" },
8426 { TYPE_SWITCH, &si.ask_on_escape, "ask_on_escape" },
8427 { TYPE_SWITCH, &si.ask_on_escape_editor, "ask_on_escape_editor" },
8428 { TYPE_SWITCH, &si.quick_switch, "quick_player_switch" },
8429 { TYPE_SWITCH, &si.input_on_focus, "input_on_focus" },
8430 { TYPE_SWITCH, &si.prefer_aga_graphics, "prefer_aga_graphics" },
8431 { TYPE_INTEGER,&si.game_frame_delay, "game_frame_delay" },
8432 { TYPE_SWITCH, &si.sp_show_border_elements, "sp_show_border_elements" },
8433 { TYPE_SWITCH, &si.small_game_graphics, "small_game_graphics" },
8434 { TYPE_SWITCH, &si.show_snapshot_buttons, "show_snapshot_buttons" },
8435 { TYPE_STRING, &si.graphics_set, "graphics_set" },
8436 { TYPE_STRING, &si.sounds_set, "sounds_set" },
8437 { TYPE_STRING, &si.music_set, "music_set" },
8438 { TYPE_SWITCH3,&si.override_level_graphics, "override_level_graphics" },
8439 { TYPE_SWITCH3,&si.override_level_sounds, "override_level_sounds" },
8440 { TYPE_SWITCH3,&si.override_level_music, "override_level_music" },
8441 { TYPE_INTEGER,&si.volume_simple, "volume_simple" },
8442 { TYPE_INTEGER,&si.volume_loops, "volume_loops" },
8443 { TYPE_INTEGER,&si.volume_music, "volume_music" },
8444 { TYPE_STRING, &si.touch.control_type, "touch.control_type" },
8445 { TYPE_INTEGER,&si.touch.move_distance, "touch.move_distance" },
8446 { TYPE_INTEGER,&si.touch.drop_distance, "touch.drop_distance" },
8449 static struct TokenInfo editor_setup_tokens[] =
8451 { TYPE_SWITCH, &sei.el_classic, "editor.el_classic" },
8452 { TYPE_SWITCH, &sei.el_custom, "editor.el_custom" },
8453 { TYPE_SWITCH, &sei.el_user_defined, "editor.el_user_defined" },
8454 { TYPE_SWITCH, &sei.el_dynamic, "editor.el_dynamic" },
8455 { TYPE_SWITCH, &sei.el_headlines, "editor.el_headlines" },
8456 { TYPE_SWITCH, &sei.show_element_token,"editor.show_element_token" },
8459 static struct TokenInfo editor_cascade_setup_tokens[] =
8461 { TYPE_SWITCH, &seci.el_bd, "editor.cascade.el_bd" },
8462 { TYPE_SWITCH, &seci.el_em, "editor.cascade.el_em" },
8463 { TYPE_SWITCH, &seci.el_emc, "editor.cascade.el_emc" },
8464 { TYPE_SWITCH, &seci.el_rnd, "editor.cascade.el_rnd" },
8465 { TYPE_SWITCH, &seci.el_sb, "editor.cascade.el_sb" },
8466 { TYPE_SWITCH, &seci.el_sp, "editor.cascade.el_sp" },
8467 { TYPE_SWITCH, &seci.el_dc, "editor.cascade.el_dc" },
8468 { TYPE_SWITCH, &seci.el_dx, "editor.cascade.el_dx" },
8469 { TYPE_SWITCH, &seci.el_mm, "editor.cascade.el_mm" },
8470 { TYPE_SWITCH, &seci.el_df, "editor.cascade.el_df" },
8471 { TYPE_SWITCH, &seci.el_chars, "editor.cascade.el_chars" },
8472 { TYPE_SWITCH, &seci.el_steel_chars, "editor.cascade.el_steel_chars" },
8473 { TYPE_SWITCH, &seci.el_ce, "editor.cascade.el_ce" },
8474 { TYPE_SWITCH, &seci.el_ge, "editor.cascade.el_ge" },
8475 { TYPE_SWITCH, &seci.el_ref, "editor.cascade.el_ref" },
8476 { TYPE_SWITCH, &seci.el_user, "editor.cascade.el_user" },
8477 { TYPE_SWITCH, &seci.el_dynamic, "editor.cascade.el_dynamic" },
8480 static struct TokenInfo shortcut_setup_tokens[] =
8482 { TYPE_KEY_X11, &ssi.save_game, "shortcut.save_game" },
8483 { TYPE_KEY_X11, &ssi.load_game, "shortcut.load_game" },
8484 { TYPE_KEY_X11, &ssi.toggle_pause, "shortcut.toggle_pause" },
8485 { TYPE_KEY_X11, &ssi.focus_player[0], "shortcut.focus_player_1" },
8486 { TYPE_KEY_X11, &ssi.focus_player[1], "shortcut.focus_player_2" },
8487 { TYPE_KEY_X11, &ssi.focus_player[2], "shortcut.focus_player_3" },
8488 { TYPE_KEY_X11, &ssi.focus_player[3], "shortcut.focus_player_4" },
8489 { TYPE_KEY_X11, &ssi.focus_player_all,"shortcut.focus_player_all" },
8490 { TYPE_KEY_X11, &ssi.tape_eject, "shortcut.tape_eject" },
8491 { TYPE_KEY_X11, &ssi.tape_extra, "shortcut.tape_extra" },
8492 { TYPE_KEY_X11, &ssi.tape_stop, "shortcut.tape_stop" },
8493 { TYPE_KEY_X11, &ssi.tape_pause, "shortcut.tape_pause" },
8494 { TYPE_KEY_X11, &ssi.tape_record, "shortcut.tape_record" },
8495 { TYPE_KEY_X11, &ssi.tape_play, "shortcut.tape_play" },
8496 { TYPE_KEY_X11, &ssi.sound_simple, "shortcut.sound_simple" },
8497 { TYPE_KEY_X11, &ssi.sound_loops, "shortcut.sound_loops" },
8498 { TYPE_KEY_X11, &ssi.sound_music, "shortcut.sound_music" },
8499 { TYPE_KEY_X11, &ssi.snap_left, "shortcut.snap_left" },
8500 { TYPE_KEY_X11, &ssi.snap_right, "shortcut.snap_right" },
8501 { TYPE_KEY_X11, &ssi.snap_up, "shortcut.snap_up" },
8502 { TYPE_KEY_X11, &ssi.snap_down, "shortcut.snap_down" },
8505 static struct TokenInfo player_setup_tokens[] =
8507 { TYPE_BOOLEAN, &sii.use_joystick, ".use_joystick" },
8508 { TYPE_STRING, &sii.joy.device_name, ".joy.device_name" },
8509 { TYPE_INTEGER, &sii.joy.xleft, ".joy.xleft" },
8510 { TYPE_INTEGER, &sii.joy.xmiddle, ".joy.xmiddle" },
8511 { TYPE_INTEGER, &sii.joy.xright, ".joy.xright" },
8512 { TYPE_INTEGER, &sii.joy.yupper, ".joy.yupper" },
8513 { TYPE_INTEGER, &sii.joy.ymiddle, ".joy.ymiddle" },
8514 { TYPE_INTEGER, &sii.joy.ylower, ".joy.ylower" },
8515 { TYPE_INTEGER, &sii.joy.snap, ".joy.snap_field" },
8516 { TYPE_INTEGER, &sii.joy.drop, ".joy.place_bomb" },
8517 { TYPE_KEY_X11, &sii.key.left, ".key.move_left" },
8518 { TYPE_KEY_X11, &sii.key.right, ".key.move_right" },
8519 { TYPE_KEY_X11, &sii.key.up, ".key.move_up" },
8520 { TYPE_KEY_X11, &sii.key.down, ".key.move_down" },
8521 { TYPE_KEY_X11, &sii.key.snap, ".key.snap_field" },
8522 { TYPE_KEY_X11, &sii.key.drop, ".key.place_bomb" },
8525 static struct TokenInfo system_setup_tokens[] =
8527 { TYPE_STRING, &syi.sdl_videodriver, "system.sdl_videodriver" },
8528 { TYPE_STRING, &syi.sdl_audiodriver, "system.sdl_audiodriver" },
8529 { TYPE_INTEGER, &syi.audio_fragment_size,"system.audio_fragment_size" },
8532 static struct TokenInfo internal_setup_tokens[] =
8534 { TYPE_STRING, &sxi.program_title, "program_title" },
8535 { TYPE_STRING, &sxi.program_version, "program_version" },
8536 { TYPE_STRING, &sxi.program_author, "program_author" },
8537 { TYPE_STRING, &sxi.program_email, "program_email" },
8538 { TYPE_STRING, &sxi.program_website, "program_website" },
8539 { TYPE_STRING, &sxi.program_copyright, "program_copyright" },
8540 { TYPE_STRING, &sxi.program_company, "program_company" },
8541 { TYPE_STRING, &sxi.program_icon_file, "program_icon_file" },
8542 { TYPE_STRING, &sxi.default_graphics_set, "default_graphics_set" },
8543 { TYPE_STRING, &sxi.default_sounds_set, "default_sounds_set" },
8544 { TYPE_STRING, &sxi.default_music_set, "default_music_set" },
8545 { TYPE_STRING, &sxi.fallback_graphics_file, "fallback_graphics_file"},
8546 { TYPE_STRING, &sxi.fallback_sounds_file, "fallback_sounds_file" },
8547 { TYPE_STRING, &sxi.fallback_music_file, "fallback_music_file" },
8548 { TYPE_STRING, &sxi.default_level_series, "default_level_series" },
8549 { TYPE_BOOLEAN,&sxi.choose_from_top_leveldir, "choose_from_top_leveldir" },
8550 { TYPE_BOOLEAN,&sxi.show_scaling_in_title, "show_scaling_in_title" },
8551 { TYPE_INTEGER,&sxi.default_window_width, "default_window_width" },
8552 { TYPE_INTEGER,&sxi.default_window_height, "default_window_height" },
8555 static struct TokenInfo debug_setup_tokens[] =
8557 { TYPE_INTEGER, &sdi.frame_delay[0], "debug.frame_delay_0" },
8558 { TYPE_INTEGER, &sdi.frame_delay[1], "debug.frame_delay_1" },
8559 { TYPE_INTEGER, &sdi.frame_delay[2], "debug.frame_delay_2" },
8560 { TYPE_INTEGER, &sdi.frame_delay[3], "debug.frame_delay_3" },
8561 { TYPE_INTEGER, &sdi.frame_delay[4], "debug.frame_delay_4" },
8562 { TYPE_INTEGER, &sdi.frame_delay[5], "debug.frame_delay_5" },
8563 { TYPE_INTEGER, &sdi.frame_delay[6], "debug.frame_delay_6" },
8564 { TYPE_INTEGER, &sdi.frame_delay[7], "debug.frame_delay_7" },
8565 { TYPE_INTEGER, &sdi.frame_delay[8], "debug.frame_delay_8" },
8566 { TYPE_INTEGER, &sdi.frame_delay[9], "debug.frame_delay_9" },
8567 { TYPE_KEY_X11, &sdi.frame_delay_key[0], "debug.key.frame_delay_0" },
8568 { TYPE_KEY_X11, &sdi.frame_delay_key[1], "debug.key.frame_delay_1" },
8569 { TYPE_KEY_X11, &sdi.frame_delay_key[2], "debug.key.frame_delay_2" },
8570 { TYPE_KEY_X11, &sdi.frame_delay_key[3], "debug.key.frame_delay_3" },
8571 { TYPE_KEY_X11, &sdi.frame_delay_key[4], "debug.key.frame_delay_4" },
8572 { TYPE_KEY_X11, &sdi.frame_delay_key[5], "debug.key.frame_delay_5" },
8573 { TYPE_KEY_X11, &sdi.frame_delay_key[6], "debug.key.frame_delay_6" },
8574 { TYPE_KEY_X11, &sdi.frame_delay_key[7], "debug.key.frame_delay_7" },
8575 { TYPE_KEY_X11, &sdi.frame_delay_key[8], "debug.key.frame_delay_8" },
8576 { TYPE_KEY_X11, &sdi.frame_delay_key[9], "debug.key.frame_delay_9" },
8577 { TYPE_BOOLEAN, &sdi.frame_delay_use_mod_key,"debug.frame_delay.use_mod_key"},
8578 { TYPE_BOOLEAN, &sdi.frame_delay_game_only, "debug.frame_delay.game_only" },
8579 { TYPE_BOOLEAN, &sdi.show_frames_per_second, "debug.show_frames_per_second" },
8582 static struct TokenInfo options_setup_tokens[] =
8584 { TYPE_BOOLEAN, &soi.verbose, "options.verbose" },
8587 static char *get_corrected_login_name(char *login_name)
8589 /* needed because player name must be a fixed length string */
8590 char *login_name_new = checked_malloc(MAX_PLAYER_NAME_LEN + 1);
8592 strncpy(login_name_new, login_name, MAX_PLAYER_NAME_LEN);
8593 login_name_new[MAX_PLAYER_NAME_LEN] = '\0';
8595 if (strlen(login_name) > MAX_PLAYER_NAME_LEN) /* name has been cut */
8596 if (strchr(login_name_new, ' '))
8597 *strchr(login_name_new, ' ') = '\0';
8599 return login_name_new;
8602 static void setSetupInfoToDefaults(struct SetupInfo *si)
8606 si->player_name = get_corrected_login_name(getLoginName());
8609 si->sound_loops = TRUE;
8610 si->sound_music = TRUE;
8611 si->sound_simple = TRUE;
8613 si->scroll_delay = TRUE;
8614 si->scroll_delay_value = STD_SCROLL_DELAY;
8615 si->engine_snapshot_mode = getStringCopy(STR_SNAPSHOT_MODE_DEFAULT);
8616 si->engine_snapshot_memory = SNAPSHOT_MEMORY_DEFAULT;
8617 si->fade_screens = TRUE;
8618 si->autorecord = TRUE;
8619 si->show_titlescreen = TRUE;
8620 si->quick_doors = FALSE;
8621 si->team_mode = FALSE;
8622 si->handicap = TRUE;
8623 si->skip_levels = TRUE;
8624 si->increment_levels = TRUE;
8625 si->time_limit = TRUE;
8626 si->fullscreen = FALSE;
8627 si->window_scaling_percent = STD_WINDOW_SCALING_PERCENT;
8628 si->window_scaling_quality = getStringCopy(SCALING_QUALITY_DEFAULT);
8629 si->screen_rendering_mode = getStringCopy(STR_SPECIAL_RENDERING_DEFAULT);
8630 si->ask_on_escape = TRUE;
8631 si->ask_on_escape_editor = TRUE;
8632 si->quick_switch = FALSE;
8633 si->input_on_focus = FALSE;
8634 si->prefer_aga_graphics = TRUE;
8635 si->game_frame_delay = GAME_FRAME_DELAY;
8636 si->sp_show_border_elements = FALSE;
8637 si->small_game_graphics = FALSE;
8638 si->show_snapshot_buttons = FALSE;
8640 si->graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
8641 si->sounds_set = getStringCopy(SND_CLASSIC_SUBDIR);
8642 si->music_set = getStringCopy(MUS_CLASSIC_SUBDIR);
8644 si->override_level_graphics = FALSE;
8645 si->override_level_sounds = FALSE;
8646 si->override_level_music = FALSE;
8648 si->volume_simple = 100; /* percent */
8649 si->volume_loops = 100; /* percent */
8650 si->volume_music = 100; /* percent */
8652 si->touch.control_type = getStringCopy(TOUCH_CONTROL_DEFAULT);
8653 si->touch.move_distance = TOUCH_MOVE_DISTANCE_DEFAULT; /* percent */
8654 si->touch.drop_distance = TOUCH_DROP_DISTANCE_DEFAULT; /* percent */
8656 si->editor.el_boulderdash = TRUE;
8657 si->editor.el_emerald_mine = TRUE;
8658 si->editor.el_emerald_mine_club = TRUE;
8659 si->editor.el_more = TRUE;
8660 si->editor.el_sokoban = TRUE;
8661 si->editor.el_supaplex = TRUE;
8662 si->editor.el_diamond_caves = TRUE;
8663 si->editor.el_dx_boulderdash = TRUE;
8665 si->editor.el_mirror_magic = TRUE;
8666 si->editor.el_deflektor = TRUE;
8668 si->editor.el_chars = TRUE;
8669 si->editor.el_steel_chars = TRUE;
8671 si->editor.el_classic = TRUE;
8672 si->editor.el_custom = TRUE;
8674 si->editor.el_user_defined = FALSE;
8675 si->editor.el_dynamic = TRUE;
8677 si->editor.el_headlines = TRUE;
8679 si->editor.show_element_token = FALSE;
8681 si->editor.use_template_for_new_levels = TRUE;
8683 si->shortcut.save_game = DEFAULT_KEY_SAVE_GAME;
8684 si->shortcut.load_game = DEFAULT_KEY_LOAD_GAME;
8685 si->shortcut.toggle_pause = DEFAULT_KEY_TOGGLE_PAUSE;
8687 si->shortcut.focus_player[0] = DEFAULT_KEY_FOCUS_PLAYER_1;
8688 si->shortcut.focus_player[1] = DEFAULT_KEY_FOCUS_PLAYER_2;
8689 si->shortcut.focus_player[2] = DEFAULT_KEY_FOCUS_PLAYER_3;
8690 si->shortcut.focus_player[3] = DEFAULT_KEY_FOCUS_PLAYER_4;
8691 si->shortcut.focus_player_all = DEFAULT_KEY_FOCUS_PLAYER_ALL;
8693 si->shortcut.tape_eject = DEFAULT_KEY_TAPE_EJECT;
8694 si->shortcut.tape_extra = DEFAULT_KEY_TAPE_EXTRA;
8695 si->shortcut.tape_stop = DEFAULT_KEY_TAPE_STOP;
8696 si->shortcut.tape_pause = DEFAULT_KEY_TAPE_PAUSE;
8697 si->shortcut.tape_record = DEFAULT_KEY_TAPE_RECORD;
8698 si->shortcut.tape_play = DEFAULT_KEY_TAPE_PLAY;
8700 si->shortcut.sound_simple = DEFAULT_KEY_SOUND_SIMPLE;
8701 si->shortcut.sound_loops = DEFAULT_KEY_SOUND_LOOPS;
8702 si->shortcut.sound_music = DEFAULT_KEY_SOUND_MUSIC;
8704 si->shortcut.snap_left = DEFAULT_KEY_SNAP_LEFT;
8705 si->shortcut.snap_right = DEFAULT_KEY_SNAP_RIGHT;
8706 si->shortcut.snap_up = DEFAULT_KEY_SNAP_UP;
8707 si->shortcut.snap_down = DEFAULT_KEY_SNAP_DOWN;
8709 for (i = 0; i < MAX_PLAYERS; i++)
8711 si->input[i].use_joystick = FALSE;
8712 si->input[i].joy.device_name=getStringCopy(getDeviceNameFromJoystickNr(i));
8713 si->input[i].joy.xleft = JOYSTICK_XLEFT;
8714 si->input[i].joy.xmiddle = JOYSTICK_XMIDDLE;
8715 si->input[i].joy.xright = JOYSTICK_XRIGHT;
8716 si->input[i].joy.yupper = JOYSTICK_YUPPER;
8717 si->input[i].joy.ymiddle = JOYSTICK_YMIDDLE;
8718 si->input[i].joy.ylower = JOYSTICK_YLOWER;
8719 si->input[i].joy.snap = (i == 0 ? JOY_BUTTON_1 : 0);
8720 si->input[i].joy.drop = (i == 0 ? JOY_BUTTON_2 : 0);
8721 si->input[i].key.left = (i == 0 ? DEFAULT_KEY_LEFT : KSYM_UNDEFINED);
8722 si->input[i].key.right = (i == 0 ? DEFAULT_KEY_RIGHT : KSYM_UNDEFINED);
8723 si->input[i].key.up = (i == 0 ? DEFAULT_KEY_UP : KSYM_UNDEFINED);
8724 si->input[i].key.down = (i == 0 ? DEFAULT_KEY_DOWN : KSYM_UNDEFINED);
8725 si->input[i].key.snap = (i == 0 ? DEFAULT_KEY_SNAP : KSYM_UNDEFINED);
8726 si->input[i].key.drop = (i == 0 ? DEFAULT_KEY_DROP : KSYM_UNDEFINED);
8729 si->system.sdl_videodriver = getStringCopy(ARG_DEFAULT);
8730 si->system.sdl_audiodriver = getStringCopy(ARG_DEFAULT);
8731 si->system.audio_fragment_size = DEFAULT_AUDIO_FRAGMENT_SIZE;
8733 si->internal.program_title = getStringCopy(PROGRAM_TITLE_STRING);
8734 si->internal.program_version = getStringCopy(getProgramRealVersionString());
8735 si->internal.program_author = getStringCopy(PROGRAM_AUTHOR_STRING);
8736 si->internal.program_email = getStringCopy(PROGRAM_EMAIL_STRING);
8737 si->internal.program_website = getStringCopy(PROGRAM_WEBSITE_STRING);
8738 si->internal.program_copyright = getStringCopy(PROGRAM_COPYRIGHT_STRING);
8739 si->internal.program_company = getStringCopy(PROGRAM_COMPANY_STRING);
8741 si->internal.program_icon_file = getStringCopy(PROGRAM_ICON_FILENAME);
8743 si->internal.default_graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
8744 si->internal.default_sounds_set = getStringCopy(SND_CLASSIC_SUBDIR);
8745 si->internal.default_music_set = getStringCopy(MUS_CLASSIC_SUBDIR);
8747 si->internal.fallback_graphics_file = getStringCopy(UNDEFINED_FILENAME);
8748 si->internal.fallback_sounds_file = getStringCopy(UNDEFINED_FILENAME);
8749 si->internal.fallback_music_file = getStringCopy(UNDEFINED_FILENAME);
8751 si->internal.default_level_series = getStringCopy(UNDEFINED_LEVELSET);
8752 si->internal.choose_from_top_leveldir = FALSE;
8753 si->internal.show_scaling_in_title = TRUE;
8755 si->internal.default_window_width = WIN_XSIZE_DEFAULT;
8756 si->internal.default_window_height = WIN_YSIZE_DEFAULT;
8758 si->debug.frame_delay[0] = DEFAULT_FRAME_DELAY_0;
8759 si->debug.frame_delay[1] = DEFAULT_FRAME_DELAY_1;
8760 si->debug.frame_delay[2] = DEFAULT_FRAME_DELAY_2;
8761 si->debug.frame_delay[3] = DEFAULT_FRAME_DELAY_3;
8762 si->debug.frame_delay[4] = DEFAULT_FRAME_DELAY_4;
8763 si->debug.frame_delay[5] = DEFAULT_FRAME_DELAY_5;
8764 si->debug.frame_delay[6] = DEFAULT_FRAME_DELAY_6;
8765 si->debug.frame_delay[7] = DEFAULT_FRAME_DELAY_7;
8766 si->debug.frame_delay[8] = DEFAULT_FRAME_DELAY_8;
8767 si->debug.frame_delay[9] = DEFAULT_FRAME_DELAY_9;
8769 si->debug.frame_delay_key[0] = DEFAULT_KEY_FRAME_DELAY_0;
8770 si->debug.frame_delay_key[1] = DEFAULT_KEY_FRAME_DELAY_1;
8771 si->debug.frame_delay_key[2] = DEFAULT_KEY_FRAME_DELAY_2;
8772 si->debug.frame_delay_key[3] = DEFAULT_KEY_FRAME_DELAY_3;
8773 si->debug.frame_delay_key[4] = DEFAULT_KEY_FRAME_DELAY_4;
8774 si->debug.frame_delay_key[5] = DEFAULT_KEY_FRAME_DELAY_5;
8775 si->debug.frame_delay_key[6] = DEFAULT_KEY_FRAME_DELAY_6;
8776 si->debug.frame_delay_key[7] = DEFAULT_KEY_FRAME_DELAY_7;
8777 si->debug.frame_delay_key[8] = DEFAULT_KEY_FRAME_DELAY_8;
8778 si->debug.frame_delay_key[9] = DEFAULT_KEY_FRAME_DELAY_9;
8780 si->debug.frame_delay_use_mod_key = DEFAULT_FRAME_DELAY_USE_MOD_KEY;
8781 si->debug.frame_delay_game_only = DEFAULT_FRAME_DELAY_GAME_ONLY;
8783 si->debug.show_frames_per_second = FALSE;
8785 si->options.verbose = FALSE;
8787 #if defined(PLATFORM_ANDROID)
8788 si->fullscreen = TRUE;
8792 static void setSetupInfoToDefaults_EditorCascade(struct SetupInfo *si)
8794 si->editor_cascade.el_bd = TRUE;
8795 si->editor_cascade.el_em = TRUE;
8796 si->editor_cascade.el_emc = TRUE;
8797 si->editor_cascade.el_rnd = TRUE;
8798 si->editor_cascade.el_sb = TRUE;
8799 si->editor_cascade.el_sp = TRUE;
8800 si->editor_cascade.el_dc = TRUE;
8801 si->editor_cascade.el_dx = TRUE;
8803 si->editor_cascade.el_mm = TRUE;
8804 si->editor_cascade.el_df = TRUE;
8806 si->editor_cascade.el_chars = FALSE;
8807 si->editor_cascade.el_steel_chars = FALSE;
8808 si->editor_cascade.el_ce = FALSE;
8809 si->editor_cascade.el_ge = FALSE;
8810 si->editor_cascade.el_ref = FALSE;
8811 si->editor_cascade.el_user = FALSE;
8812 si->editor_cascade.el_dynamic = FALSE;
8815 #define MAX_HIDE_SETUP_TOKEN_SIZE 20
8817 static char *getHideSetupToken(void *setup_value)
8819 static char hide_setup_token[MAX_HIDE_SETUP_TOKEN_SIZE];
8821 if (setup_value != NULL)
8822 snprintf(hide_setup_token, MAX_HIDE_SETUP_TOKEN_SIZE, "%p", setup_value);
8824 return hide_setup_token;
8827 static void setHideSetupEntry(void *setup_value_raw)
8829 /* !!! DIRTY WORKAROUND; TO BE FIXED AFTER THE MM ENGINE RELEASE !!! */
8830 void *setup_value = setup_value_raw - (void *)&si + (void *)&setup;
8832 char *hide_setup_token = getHideSetupToken(setup_value);
8834 if (setup_value != NULL)
8835 setHashEntry(hide_setup_hash, hide_setup_token, "");
8838 boolean hideSetupEntry(void *setup_value)
8840 char *hide_setup_token = getHideSetupToken(setup_value);
8842 return (setup_value != NULL &&
8843 getHashEntry(hide_setup_hash, hide_setup_token) != NULL);
8846 static void setSetupInfoFromTokenText(SetupFileHash *setup_file_hash,
8847 struct TokenInfo *token_info,
8848 int token_nr, char *token_text)
8850 char *token_hide_text = getStringCat2(token_text, ".hide");
8851 char *token_hide_value = getHashEntry(setup_file_hash, token_hide_text);
8853 /* set the value of this setup option in the setup option structure */
8854 setSetupInfo(token_info, token_nr, getHashEntry(setup_file_hash, token_text));
8856 /* check if this setup option should be hidden in the setup menu */
8857 if (token_hide_value != NULL && get_boolean_from_string(token_hide_value))
8858 setHideSetupEntry(token_info[token_nr].value);
8861 static void setSetupInfoFromTokenInfo(SetupFileHash *setup_file_hash,
8862 struct TokenInfo *token_info,
8865 setSetupInfoFromTokenText(setup_file_hash, token_info, token_nr,
8866 token_info[token_nr].text);
8869 static void decodeSetupFileHash(SetupFileHash *setup_file_hash)
8873 if (!setup_file_hash)
8876 if (hide_setup_hash == NULL)
8877 hide_setup_hash = newSetupFileHash();
8881 for (i = 0; i < NUM_GLOBAL_SETUP_TOKENS; i++)
8882 setSetupInfoFromTokenInfo(setup_file_hash, global_setup_tokens, i);
8887 for (i = 0; i < NUM_EDITOR_SETUP_TOKENS; i++)
8888 setSetupInfoFromTokenInfo(setup_file_hash, editor_setup_tokens, i);
8891 /* shortcut setup */
8892 ssi = setup.shortcut;
8893 for (i = 0; i < NUM_SHORTCUT_SETUP_TOKENS; i++)
8894 setSetupInfoFromTokenInfo(setup_file_hash, shortcut_setup_tokens, i);
8895 setup.shortcut = ssi;
8898 for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
8902 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
8904 sii = setup.input[pnr];
8905 for (i = 0; i < NUM_PLAYER_SETUP_TOKENS; i++)
8907 char full_token[100];
8909 sprintf(full_token, "%s%s", prefix, player_setup_tokens[i].text);
8910 setSetupInfoFromTokenText(setup_file_hash, player_setup_tokens, i,
8913 setup.input[pnr] = sii;
8918 for (i = 0; i < NUM_SYSTEM_SETUP_TOKENS; i++)
8919 setSetupInfoFromTokenInfo(setup_file_hash, system_setup_tokens, i);
8922 /* internal setup */
8923 sxi = setup.internal;
8924 for (i = 0; i < NUM_INTERNAL_SETUP_TOKENS; i++)
8925 setSetupInfoFromTokenInfo(setup_file_hash, internal_setup_tokens, i);
8926 setup.internal = sxi;
8930 for (i = 0; i < NUM_DEBUG_SETUP_TOKENS; i++)
8931 setSetupInfoFromTokenInfo(setup_file_hash, debug_setup_tokens, i);
8935 soi = setup.options;
8936 for (i = 0; i < NUM_OPTIONS_SETUP_TOKENS; i++)
8937 setSetupInfoFromTokenInfo(setup_file_hash, options_setup_tokens, i);
8938 setup.options = soi;
8941 static void decodeSetupFileHash_EditorCascade(SetupFileHash *setup_file_hash)
8945 if (!setup_file_hash)
8948 /* editor cascade setup */
8949 seci = setup.editor_cascade;
8950 for (i = 0; i < NUM_EDITOR_CASCADE_SETUP_TOKENS; i++)
8951 setSetupInfo(editor_cascade_setup_tokens, i,
8952 getHashEntry(setup_file_hash,
8953 editor_cascade_setup_tokens[i].text));
8954 setup.editor_cascade = seci;
8957 void LoadSetupFromFilename(char *filename)
8959 SetupFileHash *setup_file_hash = loadSetupFileHash(filename);
8961 if (setup_file_hash)
8963 decodeSetupFileHash(setup_file_hash);
8965 freeSetupFileHash(setup_file_hash);
8969 Error(ERR_DEBUG, "using default setup values");
8973 static void LoadSetup_SpecialPostProcessing()
8975 char *player_name_new;
8977 /* needed to work around problems with fixed length strings */
8978 player_name_new = get_corrected_login_name(setup.player_name);
8979 free(setup.player_name);
8980 setup.player_name = player_name_new;
8982 /* "scroll_delay: on(3) / off(0)" was replaced by scroll delay value */
8983 if (setup.scroll_delay == FALSE)
8985 setup.scroll_delay_value = MIN_SCROLL_DELAY;
8986 setup.scroll_delay = TRUE; /* now always "on" */
8989 /* make sure that scroll delay value stays inside valid range */
8990 setup.scroll_delay_value =
8991 MIN(MAX(MIN_SCROLL_DELAY, setup.scroll_delay_value), MAX_SCROLL_DELAY);
8998 /* always start with reliable default values */
8999 setSetupInfoToDefaults(&setup);
9001 /* try to load setup values from default setup file */
9002 filename = getDefaultSetupFilename();
9004 if (fileExists(filename))
9005 LoadSetupFromFilename(filename);
9007 /* try to load setup values from user setup file */
9008 filename = getSetupFilename();
9010 LoadSetupFromFilename(filename);
9012 LoadSetup_SpecialPostProcessing();
9015 void LoadSetup_EditorCascade()
9017 char *filename = getPath2(getSetupDir(), EDITORCASCADE_FILENAME);
9018 SetupFileHash *setup_file_hash = NULL;
9020 /* always start with reliable default values */
9021 setSetupInfoToDefaults_EditorCascade(&setup);
9023 setup_file_hash = loadSetupFileHash(filename);
9025 if (setup_file_hash)
9027 decodeSetupFileHash_EditorCascade(setup_file_hash);
9029 freeSetupFileHash(setup_file_hash);
9035 static void addGameControllerMappingToHash(SetupFileHash *mappings_hash,
9038 char mapping_guid[MAX_LINE_LEN];
9039 char *mapping_start, *mapping_end;
9041 // get GUID from game controller mapping line: copy complete line
9042 strncpy(mapping_guid, mapping_line, MAX_LINE_LEN - 1);
9043 mapping_guid[MAX_LINE_LEN - 1] = '\0';
9045 // get GUID from game controller mapping line: cut after GUID part
9046 mapping_start = strchr(mapping_guid, ',');
9047 if (mapping_start != NULL)
9048 *mapping_start = '\0';
9050 // cut newline from game controller mapping line
9051 mapping_end = strchr(mapping_line, '\n');
9052 if (mapping_end != NULL)
9053 *mapping_end = '\0';
9055 // add mapping entry to game controller mappings hash
9056 setHashEntry(mappings_hash, mapping_guid, mapping_line);
9059 static void LoadSetup_ReadGameControllerMappings(SetupFileHash *mappings_hash,
9064 if (!(file = fopen(filename, MODE_READ)))
9066 Error(ERR_WARN, "cannot read game controller mappings file '%s'", filename);
9073 char line[MAX_LINE_LEN];
9075 if (!fgets(line, MAX_LINE_LEN, file))
9078 addGameControllerMappingToHash(mappings_hash, line);
9086 char *filename = getSetupFilename();
9090 InitUserDataDirectory();
9092 if (!(file = fopen(filename, MODE_WRITE)))
9094 Error(ERR_WARN, "cannot write setup file '%s'", filename);
9098 fprintFileHeader(file, SETUP_FILENAME);
9102 for (i = 0; i < NUM_GLOBAL_SETUP_TOKENS; i++)
9104 /* just to make things nicer :) */
9105 if (i == SETUP_TOKEN_PLAYER_NAME + 1 ||
9106 i == SETUP_TOKEN_GRAPHICS_SET ||
9107 i == SETUP_TOKEN_VOLUME_SIMPLE ||
9108 i == SETUP_TOKEN_TOUCH_CONTROL_TYPE)
9109 fprintf(file, "\n");
9111 fprintf(file, "%s\n", getSetupLine(global_setup_tokens, "", i));
9116 fprintf(file, "\n");
9117 for (i = 0; i < NUM_EDITOR_SETUP_TOKENS; i++)
9118 fprintf(file, "%s\n", getSetupLine(editor_setup_tokens, "", i));
9120 /* shortcut setup */
9121 ssi = setup.shortcut;
9122 fprintf(file, "\n");
9123 for (i = 0; i < NUM_SHORTCUT_SETUP_TOKENS; i++)
9124 fprintf(file, "%s\n", getSetupLine(shortcut_setup_tokens, "", i));
9127 for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
9131 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
9132 fprintf(file, "\n");
9134 sii = setup.input[pnr];
9135 for (i = 0; i < NUM_PLAYER_SETUP_TOKENS; i++)
9136 fprintf(file, "%s\n", getSetupLine(player_setup_tokens, prefix, i));
9141 fprintf(file, "\n");
9142 for (i = 0; i < NUM_SYSTEM_SETUP_TOKENS; i++)
9143 fprintf(file, "%s\n", getSetupLine(system_setup_tokens, "", i));
9145 /* internal setup */
9146 /* (internal setup values not saved to user setup file) */
9150 fprintf(file, "\n");
9151 for (i = 0; i < NUM_DEBUG_SETUP_TOKENS; i++)
9152 fprintf(file, "%s\n", getSetupLine(debug_setup_tokens, "", i));
9155 soi = setup.options;
9156 fprintf(file, "\n");
9157 for (i = 0; i < NUM_OPTIONS_SETUP_TOKENS; i++)
9158 fprintf(file, "%s\n", getSetupLine(options_setup_tokens, "", i));
9162 SetFilePermissions(filename, PERMS_PRIVATE);
9165 void SaveSetup_EditorCascade()
9167 char *filename = getPath2(getSetupDir(), EDITORCASCADE_FILENAME);
9171 InitUserDataDirectory();
9173 if (!(file = fopen(filename, MODE_WRITE)))
9175 Error(ERR_WARN, "cannot write editor cascade state file '%s'", filename);
9180 fprintFileHeader(file, EDITORCASCADE_FILENAME);
9182 seci = setup.editor_cascade;
9183 for (i = 0; i < NUM_EDITOR_CASCADE_SETUP_TOKENS; i++)
9184 fprintf(file, "%s\n", getSetupLine(editor_cascade_setup_tokens, "", i));
9188 SetFilePermissions(filename, PERMS_PRIVATE);
9193 static void SaveSetup_WriteGameControllerMappings(SetupFileHash *mappings_hash,
9198 if (!(file = fopen(filename, MODE_WRITE)))
9200 Error(ERR_WARN, "cannot write game controller mappings file '%s'",filename);
9205 BEGIN_HASH_ITERATION(mappings_hash, itr)
9207 fprintf(file, "%s\n", HASH_ITERATION_VALUE(itr));
9209 END_HASH_ITERATION(mappings_hash, itr)
9214 void SaveSetup_AddGameControllerMapping(char *mapping)
9216 char *filename = getPath2(getSetupDir(), GAMECONTROLLER_BASENAME);
9217 SetupFileHash *mappings_hash = newSetupFileHash();
9219 InitUserDataDirectory();
9221 // load existing personal game controller mappings
9222 LoadSetup_ReadGameControllerMappings(mappings_hash, filename);
9224 // add new mapping to personal game controller mappings
9225 addGameControllerMappingToHash(mappings_hash, mapping);
9227 // save updated personal game controller mappings
9228 SaveSetup_WriteGameControllerMappings(mappings_hash, filename);
9230 freeSetupFileHash(mappings_hash);
9234 void LoadCustomElementDescriptions()
9236 char *filename = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
9237 SetupFileHash *setup_file_hash;
9240 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
9242 if (element_info[i].custom_description != NULL)
9244 free(element_info[i].custom_description);
9245 element_info[i].custom_description = NULL;
9249 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
9252 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
9254 char *token = getStringCat2(element_info[i].token_name, ".name");
9255 char *value = getHashEntry(setup_file_hash, token);
9258 element_info[i].custom_description = getStringCopy(value);
9263 freeSetupFileHash(setup_file_hash);
9266 static int getElementFromToken(char *token)
9268 char *value = getHashEntry(element_token_hash, token);
9273 Error(ERR_WARN, "unknown element token '%s'", token);
9275 return EL_UNDEFINED;
9278 static int get_token_parameter_value(char *token, char *value_raw)
9282 if (token == NULL || value_raw == NULL)
9283 return ARG_UNDEFINED_VALUE;
9285 suffix = strrchr(token, '.');
9289 if (strEqual(suffix, ".element"))
9290 return getElementFromToken(value_raw);
9292 /* !!! USE CORRECT VALUE TYPE (currently works also for TYPE_BOOLEAN) !!! */
9293 return get_parameter_value(value_raw, suffix, TYPE_INTEGER);
9296 void InitMenuDesignSettings_Static()
9300 /* always start with reliable default values from static default config */
9301 for (i = 0; image_config_vars[i].token != NULL; i++)
9303 char *value = getHashEntry(image_config_hash, image_config_vars[i].token);
9306 *image_config_vars[i].value =
9307 get_token_parameter_value(image_config_vars[i].token, value);
9311 static void InitMenuDesignSettings_SpecialPreProcessing()
9315 /* the following initializes hierarchical values from static configuration */
9317 /* special case: initialize "ARG_DEFAULT" values in static default config */
9318 /* (e.g., initialize "[titlemessage].fade_mode" from "[title].fade_mode") */
9319 titlescreen_initial_first_default.fade_mode =
9320 title_initial_first_default.fade_mode;
9321 titlescreen_initial_first_default.fade_delay =
9322 title_initial_first_default.fade_delay;
9323 titlescreen_initial_first_default.post_delay =
9324 title_initial_first_default.post_delay;
9325 titlescreen_initial_first_default.auto_delay =
9326 title_initial_first_default.auto_delay;
9327 titlescreen_first_default.fade_mode = title_first_default.fade_mode;
9328 titlescreen_first_default.fade_delay = title_first_default.fade_delay;
9329 titlescreen_first_default.post_delay = title_first_default.post_delay;
9330 titlescreen_first_default.auto_delay = title_first_default.auto_delay;
9331 titlemessage_initial_first_default.fade_mode =
9332 title_initial_first_default.fade_mode;
9333 titlemessage_initial_first_default.fade_delay =
9334 title_initial_first_default.fade_delay;
9335 titlemessage_initial_first_default.post_delay =
9336 title_initial_first_default.post_delay;
9337 titlemessage_initial_first_default.auto_delay =
9338 title_initial_first_default.auto_delay;
9339 titlemessage_first_default.fade_mode = title_first_default.fade_mode;
9340 titlemessage_first_default.fade_delay = title_first_default.fade_delay;
9341 titlemessage_first_default.post_delay = title_first_default.post_delay;
9342 titlemessage_first_default.auto_delay = title_first_default.auto_delay;
9344 titlescreen_initial_default.fade_mode = title_initial_default.fade_mode;
9345 titlescreen_initial_default.fade_delay = title_initial_default.fade_delay;
9346 titlescreen_initial_default.post_delay = title_initial_default.post_delay;
9347 titlescreen_initial_default.auto_delay = title_initial_default.auto_delay;
9348 titlescreen_default.fade_mode = title_default.fade_mode;
9349 titlescreen_default.fade_delay = title_default.fade_delay;
9350 titlescreen_default.post_delay = title_default.post_delay;
9351 titlescreen_default.auto_delay = title_default.auto_delay;
9352 titlemessage_initial_default.fade_mode = title_initial_default.fade_mode;
9353 titlemessage_initial_default.fade_delay = title_initial_default.fade_delay;
9354 titlemessage_initial_default.post_delay = title_initial_default.post_delay;
9355 titlemessage_initial_default.auto_delay = title_initial_default.auto_delay;
9356 titlemessage_default.fade_mode = title_default.fade_mode;
9357 titlemessage_default.fade_delay = title_default.fade_delay;
9358 titlemessage_default.post_delay = title_default.post_delay;
9359 titlemessage_default.auto_delay = title_default.auto_delay;
9361 /* special case: initialize "ARG_DEFAULT" values in static default config */
9362 /* (e.g., init "titlemessage_1.fade_mode" from "[titlemessage].fade_mode") */
9363 for (i = 0; i < MAX_NUM_TITLE_MESSAGES; i++)
9365 titlescreen_initial_first[i] = titlescreen_initial_first_default;
9366 titlescreen_first[i] = titlescreen_first_default;
9367 titlemessage_initial_first[i] = titlemessage_initial_first_default;
9368 titlemessage_first[i] = titlemessage_first_default;
9370 titlescreen_initial[i] = titlescreen_initial_default;
9371 titlescreen[i] = titlescreen_default;
9372 titlemessage_initial[i] = titlemessage_initial_default;
9373 titlemessage[i] = titlemessage_default;
9376 /* special case: initialize "ARG_DEFAULT" values in static default config */
9377 /* (eg, init "menu.enter_screen.SCORES.xyz" from "menu.enter_screen.xyz") */
9378 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
9380 if (i == GFX_SPECIAL_ARG_TITLE) /* title values already initialized */
9383 menu.enter_screen[i] = menu.enter_screen[GFX_SPECIAL_ARG_DEFAULT];
9384 menu.leave_screen[i] = menu.leave_screen[GFX_SPECIAL_ARG_DEFAULT];
9385 menu.next_screen[i] = menu.next_screen[GFX_SPECIAL_ARG_DEFAULT];
9388 /* special case: initialize "ARG_DEFAULT" values in static default config */
9389 /* (eg, init "viewport.door_1.MAIN.xyz" from "viewport.door_1.xyz") */
9390 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
9392 viewport.window[i] = viewport.window[GFX_SPECIAL_ARG_DEFAULT];
9393 viewport.playfield[i] = viewport.playfield[GFX_SPECIAL_ARG_DEFAULT];
9394 viewport.door_1[i] = viewport.door_1[GFX_SPECIAL_ARG_DEFAULT];
9396 if (i == GFX_SPECIAL_ARG_EDITOR) /* editor values already initialized */
9399 viewport.door_2[i] = viewport.door_2[GFX_SPECIAL_ARG_DEFAULT];
9403 static void InitMenuDesignSettings_SpecialPostProcessing()
9407 struct XY *dst, *src;
9411 { &game.button.save, &game.button.stop },
9412 { &game.button.pause2, &game.button.pause },
9413 { &game.button.load, &game.button.play },
9414 { &game.button.undo, &game.button.stop },
9415 { &game.button.redo, &game.button.play },
9421 /* special case: initialize later added SETUP list size from LEVELS value */
9422 if (menu.list_size[GAME_MODE_SETUP] == -1)
9423 menu.list_size[GAME_MODE_SETUP] = menu.list_size[GAME_MODE_LEVELS];
9425 /* set default position for snapshot buttons to stop/pause/play buttons */
9426 for (i = 0; game_buttons_xy[i].dst != NULL; i++)
9427 if ((*game_buttons_xy[i].dst).x == -1 &&
9428 (*game_buttons_xy[i].dst).y == -1)
9429 *game_buttons_xy[i].dst = *game_buttons_xy[i].src;
9432 static void InitMenuDesignSettings_SpecialPostProcessing_AfterGraphics()
9436 struct XYTileSize *dst, *src;
9439 editor_buttons_xy[] =
9442 &editor.button.element_left, &editor.palette.element_left,
9443 IMG_GFX_EDITOR_BUTTON_ELEMENT_LEFT
9446 &editor.button.element_middle, &editor.palette.element_middle,
9447 IMG_GFX_EDITOR_BUTTON_ELEMENT_MIDDLE
9450 &editor.button.element_right, &editor.palette.element_right,
9451 IMG_GFX_EDITOR_BUTTON_ELEMENT_RIGHT
9458 /* set default position for element buttons to element graphics */
9459 for (i = 0; editor_buttons_xy[i].dst != NULL; i++)
9461 if ((*editor_buttons_xy[i].dst).x == -1 &&
9462 (*editor_buttons_xy[i].dst).y == -1)
9464 struct GraphicInfo *gd = &graphic_info[editor_buttons_xy[i].graphic];
9466 gd->width = gd->height = editor_buttons_xy[i].src->tile_size;
9468 *editor_buttons_xy[i].dst = *editor_buttons_xy[i].src;
9473 static void LoadMenuDesignSettingsFromFilename(char *filename)
9475 static struct TitleFadingInfo tfi;
9476 static struct TitleMessageInfo tmi;
9477 static struct TokenInfo title_tokens[] =
9479 { TYPE_INTEGER, &tfi.fade_mode, ".fade_mode" },
9480 { TYPE_INTEGER, &tfi.fade_delay, ".fade_delay" },
9481 { TYPE_INTEGER, &tfi.post_delay, ".post_delay" },
9482 { TYPE_INTEGER, &tfi.auto_delay, ".auto_delay" },
9486 static struct TokenInfo titlemessage_tokens[] =
9488 { TYPE_INTEGER, &tmi.x, ".x" },
9489 { TYPE_INTEGER, &tmi.y, ".y" },
9490 { TYPE_INTEGER, &tmi.width, ".width" },
9491 { TYPE_INTEGER, &tmi.height, ".height" },
9492 { TYPE_INTEGER, &tmi.chars, ".chars" },
9493 { TYPE_INTEGER, &tmi.lines, ".lines" },
9494 { TYPE_INTEGER, &tmi.align, ".align" },
9495 { TYPE_INTEGER, &tmi.valign, ".valign" },
9496 { TYPE_INTEGER, &tmi.font, ".font" },
9497 { TYPE_BOOLEAN, &tmi.autowrap, ".autowrap" },
9498 { TYPE_BOOLEAN, &tmi.centered, ".centered" },
9499 { TYPE_BOOLEAN, &tmi.parse_comments, ".parse_comments" },
9500 { TYPE_INTEGER, &tmi.sort_priority, ".sort_priority" },
9501 { TYPE_INTEGER, &tmi.fade_mode, ".fade_mode" },
9502 { TYPE_INTEGER, &tmi.fade_delay, ".fade_delay" },
9503 { TYPE_INTEGER, &tmi.post_delay, ".post_delay" },
9504 { TYPE_INTEGER, &tmi.auto_delay, ".auto_delay" },
9510 struct TitleFadingInfo *info;
9515 /* initialize first titles from "enter screen" definitions, if defined */
9516 { &title_initial_first_default, "menu.enter_screen.TITLE" },
9517 { &title_first_default, "menu.enter_screen.TITLE" },
9519 /* initialize title screens from "next screen" definitions, if defined */
9520 { &title_initial_default, "menu.next_screen.TITLE" },
9521 { &title_default, "menu.next_screen.TITLE" },
9527 struct TitleMessageInfo *array;
9530 titlemessage_arrays[] =
9532 /* initialize first titles from "enter screen" definitions, if defined */
9533 { titlescreen_initial_first, "menu.enter_screen.TITLE" },
9534 { titlescreen_first, "menu.enter_screen.TITLE" },
9535 { titlemessage_initial_first, "menu.enter_screen.TITLE" },
9536 { titlemessage_first, "menu.enter_screen.TITLE" },
9538 /* initialize titles from "next screen" definitions, if defined */
9539 { titlescreen_initial, "menu.next_screen.TITLE" },
9540 { titlescreen, "menu.next_screen.TITLE" },
9541 { titlemessage_initial, "menu.next_screen.TITLE" },
9542 { titlemessage, "menu.next_screen.TITLE" },
9544 /* overwrite titles with title definitions, if defined */
9545 { titlescreen_initial_first, "[title_initial]" },
9546 { titlescreen_first, "[title]" },
9547 { titlemessage_initial_first, "[title_initial]" },
9548 { titlemessage_first, "[title]" },
9550 { titlescreen_initial, "[title_initial]" },
9551 { titlescreen, "[title]" },
9552 { titlemessage_initial, "[title_initial]" },
9553 { titlemessage, "[title]" },
9555 /* overwrite titles with title screen/message definitions, if defined */
9556 { titlescreen_initial_first, "[titlescreen_initial]" },
9557 { titlescreen_first, "[titlescreen]" },
9558 { titlemessage_initial_first, "[titlemessage_initial]" },
9559 { titlemessage_first, "[titlemessage]" },
9561 { titlescreen_initial, "[titlescreen_initial]" },
9562 { titlescreen, "[titlescreen]" },
9563 { titlemessage_initial, "[titlemessage_initial]" },
9564 { titlemessage, "[titlemessage]" },
9568 SetupFileHash *setup_file_hash;
9571 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
9574 /* the following initializes hierarchical values from dynamic configuration */
9576 /* special case: initialize with default values that may be overwritten */
9577 /* (e.g., init "menu.draw_xoffset.INFO" from "menu.draw_xoffset") */
9578 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
9580 char *value_1 = getHashEntry(setup_file_hash, "menu.draw_xoffset");
9581 char *value_2 = getHashEntry(setup_file_hash, "menu.draw_yoffset");
9582 char *value_3 = getHashEntry(setup_file_hash, "menu.list_size");
9584 if (value_1 != NULL)
9585 menu.draw_xoffset[i] = get_integer_from_string(value_1);
9586 if (value_2 != NULL)
9587 menu.draw_yoffset[i] = get_integer_from_string(value_2);
9588 if (value_3 != NULL)
9589 menu.list_size[i] = get_integer_from_string(value_3);
9592 /* special case: initialize with default values that may be overwritten */
9593 /* (eg, init "menu.draw_xoffset.INFO[XXX]" from "menu.draw_xoffset.INFO") */
9594 for (i = 0; i < NUM_SPECIAL_GFX_INFO_ARGS; i++)
9596 char *value_1 = getHashEntry(setup_file_hash, "menu.draw_xoffset.INFO");
9597 char *value_2 = getHashEntry(setup_file_hash, "menu.draw_yoffset.INFO");
9599 if (value_1 != NULL)
9600 menu.draw_xoffset_info[i] = get_integer_from_string(value_1);
9601 if (value_2 != NULL)
9602 menu.draw_yoffset_info[i] = get_integer_from_string(value_2);
9604 if (i == GFX_SPECIAL_ARG_INFO_ELEMENTS)
9606 char *value_1 = getHashEntry(setup_file_hash, "menu.list_size.INFO");
9608 if (value_1 != NULL)
9609 menu.list_size_info[i] = get_integer_from_string(value_1);
9613 /* special case: initialize with default values that may be overwritten */
9614 /* (eg, init "menu.draw_xoffset.SETUP[XXX]" from "menu.draw_xoffset.SETUP") */
9615 for (i = 0; i < NUM_SPECIAL_GFX_SETUP_ARGS; i++)
9617 char *value_1 = getHashEntry(setup_file_hash, "menu.draw_xoffset.SETUP");
9618 char *value_2 = getHashEntry(setup_file_hash, "menu.draw_yoffset.SETUP");
9620 if (value_1 != NULL)
9621 menu.draw_xoffset_setup[i] = get_integer_from_string(value_1);
9622 if (value_2 != NULL)
9623 menu.draw_yoffset_setup[i] = get_integer_from_string(value_2);
9626 /* special case: initialize with default values that may be overwritten */
9627 /* (eg, init "menu.enter_screen.SCORES.xyz" from "menu.enter_screen.xyz") */
9628 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
9630 char *token_1 = "menu.enter_screen.fade_mode";
9631 char *token_2 = "menu.enter_screen.fade_delay";
9632 char *token_3 = "menu.enter_screen.post_delay";
9633 char *token_4 = "menu.leave_screen.fade_mode";
9634 char *token_5 = "menu.leave_screen.fade_delay";
9635 char *token_6 = "menu.leave_screen.post_delay";
9636 char *token_7 = "menu.next_screen.fade_mode";
9637 char *token_8 = "menu.next_screen.fade_delay";
9638 char *token_9 = "menu.next_screen.post_delay";
9639 char *value_1 = getHashEntry(setup_file_hash, token_1);
9640 char *value_2 = getHashEntry(setup_file_hash, token_2);
9641 char *value_3 = getHashEntry(setup_file_hash, token_3);
9642 char *value_4 = getHashEntry(setup_file_hash, token_4);
9643 char *value_5 = getHashEntry(setup_file_hash, token_5);
9644 char *value_6 = getHashEntry(setup_file_hash, token_6);
9645 char *value_7 = getHashEntry(setup_file_hash, token_7);
9646 char *value_8 = getHashEntry(setup_file_hash, token_8);
9647 char *value_9 = getHashEntry(setup_file_hash, token_9);
9649 if (value_1 != NULL)
9650 menu.enter_screen[i].fade_mode = get_token_parameter_value(token_1,
9652 if (value_2 != NULL)
9653 menu.enter_screen[i].fade_delay = get_token_parameter_value(token_2,
9655 if (value_3 != NULL)
9656 menu.enter_screen[i].post_delay = get_token_parameter_value(token_3,
9658 if (value_4 != NULL)
9659 menu.leave_screen[i].fade_mode = get_token_parameter_value(token_4,
9661 if (value_5 != NULL)
9662 menu.leave_screen[i].fade_delay = get_token_parameter_value(token_5,
9664 if (value_6 != NULL)
9665 menu.leave_screen[i].post_delay = get_token_parameter_value(token_6,
9667 if (value_7 != NULL)
9668 menu.next_screen[i].fade_mode = get_token_parameter_value(token_7,
9670 if (value_8 != NULL)
9671 menu.next_screen[i].fade_delay = get_token_parameter_value(token_8,
9673 if (value_9 != NULL)
9674 menu.next_screen[i].post_delay = get_token_parameter_value(token_9,
9678 /* special case: initialize with default values that may be overwritten */
9679 /* (eg, init "viewport.door_1.MAIN.xyz" from "viewport.door_1.xyz") */
9680 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
9682 char *token_w1 = "viewport.window.width";
9683 char *token_w2 = "viewport.window.height";
9684 char *token_01 = "viewport.playfield.x";
9685 char *token_02 = "viewport.playfield.y";
9686 char *token_03 = "viewport.playfield.width";
9687 char *token_04 = "viewport.playfield.height";
9688 char *token_05 = "viewport.playfield.border_size";
9689 char *token_06 = "viewport.door_1.x";
9690 char *token_07 = "viewport.door_1.y";
9691 char *token_08 = "viewport.door_1.width";
9692 char *token_09 = "viewport.door_1.height";
9693 char *token_10 = "viewport.door_1.border_size";
9694 char *token_11 = "viewport.door_2.x";
9695 char *token_12 = "viewport.door_2.y";
9696 char *token_13 = "viewport.door_2.width";
9697 char *token_14 = "viewport.door_2.height";
9698 char *token_15 = "viewport.door_2.border_size";
9699 char *value_w1 = getHashEntry(setup_file_hash, token_w1);
9700 char *value_w2 = getHashEntry(setup_file_hash, token_w2);
9701 char *value_01 = getHashEntry(setup_file_hash, token_01);
9702 char *value_02 = getHashEntry(setup_file_hash, token_02);
9703 char *value_03 = getHashEntry(setup_file_hash, token_03);
9704 char *value_04 = getHashEntry(setup_file_hash, token_04);
9705 char *value_05 = getHashEntry(setup_file_hash, token_05);
9706 char *value_06 = getHashEntry(setup_file_hash, token_06);
9707 char *value_07 = getHashEntry(setup_file_hash, token_07);
9708 char *value_08 = getHashEntry(setup_file_hash, token_08);
9709 char *value_09 = getHashEntry(setup_file_hash, token_09);
9710 char *value_10 = getHashEntry(setup_file_hash, token_10);
9711 char *value_11 = getHashEntry(setup_file_hash, token_11);
9712 char *value_12 = getHashEntry(setup_file_hash, token_12);
9713 char *value_13 = getHashEntry(setup_file_hash, token_13);
9714 char *value_14 = getHashEntry(setup_file_hash, token_14);
9715 char *value_15 = getHashEntry(setup_file_hash, token_15);
9717 if (value_w1 != NULL)
9718 viewport.window[i].width = get_token_parameter_value(token_w1, value_w1);
9719 if (value_w2 != NULL)
9720 viewport.window[i].height = get_token_parameter_value(token_w2, value_w2);
9721 if (value_01 != NULL)
9722 viewport.playfield[i].x = get_token_parameter_value(token_01, value_01);
9723 if (value_02 != NULL)
9724 viewport.playfield[i].y = get_token_parameter_value(token_02, value_02);
9725 if (value_03 != NULL)
9726 viewport.playfield[i].width = get_token_parameter_value(token_03,
9728 if (value_04 != NULL)
9729 viewport.playfield[i].height = get_token_parameter_value(token_04,
9731 if (value_05 != NULL)
9732 viewport.playfield[i].border_size = get_token_parameter_value(token_05,
9734 if (value_06 != NULL)
9735 viewport.door_1[i].x = get_token_parameter_value(token_06, value_06);
9736 if (value_07 != NULL)
9737 viewport.door_1[i].y = get_token_parameter_value(token_07, value_07);
9738 if (value_08 != NULL)
9739 viewport.door_1[i].width = get_token_parameter_value(token_08, value_08);
9740 if (value_09 != NULL)
9741 viewport.door_1[i].height = get_token_parameter_value(token_09, value_09);
9742 if (value_10 != NULL)
9743 viewport.door_1[i].border_size = get_token_parameter_value(token_10,
9745 if (value_11 != NULL)
9746 viewport.door_2[i].x = get_token_parameter_value(token_11, value_11);
9747 if (value_12 != NULL)
9748 viewport.door_2[i].y = get_token_parameter_value(token_12, value_12);
9749 if (value_13 != NULL)
9750 viewport.door_2[i].width = get_token_parameter_value(token_13, value_13);
9751 if (value_14 != NULL)
9752 viewport.door_2[i].height = get_token_parameter_value(token_14, value_14);
9753 if (value_15 != NULL)
9754 viewport.door_1[i].border_size = get_token_parameter_value(token_15,
9758 /* special case: initialize with default values that may be overwritten */
9759 /* (e.g., init "[title].fade_mode" from "menu.next_screen.TITLE.fade_mode") */
9760 for (i = 0; title_info[i].info != NULL; i++)
9762 struct TitleFadingInfo *info = title_info[i].info;
9763 char *base_token = title_info[i].text;
9765 for (j = 0; title_tokens[j].type != -1; j++)
9767 char *token = getStringCat2(base_token, title_tokens[j].text);
9768 char *value = getHashEntry(setup_file_hash, token);
9772 int parameter_value = get_token_parameter_value(token, value);
9776 *(int *)title_tokens[j].value = (int)parameter_value;
9785 /* special case: initialize with default values that may be overwritten */
9786 /* (e.g., init "titlemessage_1.fade_mode" from "[titlemessage].fade_mode") */
9787 for (i = 0; titlemessage_arrays[i].array != NULL; i++)
9789 struct TitleMessageInfo *array = titlemessage_arrays[i].array;
9790 char *base_token = titlemessage_arrays[i].text;
9792 for (j = 0; titlemessage_tokens[j].type != -1; j++)
9794 char *token = getStringCat2(base_token, titlemessage_tokens[j].text);
9795 char *value = getHashEntry(setup_file_hash, token);
9799 int parameter_value = get_token_parameter_value(token, value);
9801 for (k = 0; k < MAX_NUM_TITLE_MESSAGES; k++)
9805 if (titlemessage_tokens[j].type == TYPE_INTEGER)
9806 *(int *)titlemessage_tokens[j].value = (int)parameter_value;
9808 *(boolean *)titlemessage_tokens[j].value = (boolean)parameter_value;
9818 /* read (and overwrite with) values that may be specified in config file */
9819 for (i = 0; image_config_vars[i].token != NULL; i++)
9821 char *value = getHashEntry(setup_file_hash, image_config_vars[i].token);
9823 /* (ignore definitions set to "[DEFAULT]" which are already initialized) */
9824 if (value != NULL && !strEqual(value, ARG_DEFAULT))
9825 *image_config_vars[i].value =
9826 get_token_parameter_value(image_config_vars[i].token, value);
9829 freeSetupFileHash(setup_file_hash);
9832 void LoadMenuDesignSettings()
9834 char *filename_base = UNDEFINED_FILENAME, *filename_local;
9836 InitMenuDesignSettings_Static();
9837 InitMenuDesignSettings_SpecialPreProcessing();
9839 if (!GFX_OVERRIDE_ARTWORK(ARTWORK_TYPE_GRAPHICS))
9841 /* first look for special settings configured in level series config */
9842 filename_base = getCustomArtworkLevelConfigFilename(ARTWORK_TYPE_GRAPHICS);
9844 if (fileExists(filename_base))
9845 LoadMenuDesignSettingsFromFilename(filename_base);
9848 filename_local = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
9850 if (filename_local != NULL && !strEqual(filename_base, filename_local))
9851 LoadMenuDesignSettingsFromFilename(filename_local);
9853 InitMenuDesignSettings_SpecialPostProcessing();
9856 void LoadMenuDesignSettings_AfterGraphics()
9858 InitMenuDesignSettings_SpecialPostProcessing_AfterGraphics();
9861 void LoadUserDefinedEditorElementList(int **elements, int *num_elements)
9863 char *filename = getEditorSetupFilename();
9864 SetupFileList *setup_file_list, *list;
9865 SetupFileHash *element_hash;
9866 int num_unknown_tokens = 0;
9869 if ((setup_file_list = loadSetupFileList(filename)) == NULL)
9872 element_hash = newSetupFileHash();
9874 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
9875 setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
9877 /* determined size may be larger than needed (due to unknown elements) */
9879 for (list = setup_file_list; list != NULL; list = list->next)
9882 /* add space for up to 3 more elements for padding that may be needed */
9885 /* free memory for old list of elements, if needed */
9886 checked_free(*elements);
9888 /* allocate memory for new list of elements */
9889 *elements = checked_malloc(*num_elements * sizeof(int));
9892 for (list = setup_file_list; list != NULL; list = list->next)
9894 char *value = getHashEntry(element_hash, list->token);
9896 if (value == NULL) /* try to find obsolete token mapping */
9898 char *mapped_token = get_mapped_token(list->token);
9900 if (mapped_token != NULL)
9902 value = getHashEntry(element_hash, mapped_token);
9910 (*elements)[(*num_elements)++] = atoi(value);
9914 if (num_unknown_tokens == 0)
9916 Error(ERR_INFO_LINE, "-");
9917 Error(ERR_INFO, "warning: unknown token(s) found in config file:");
9918 Error(ERR_INFO, "- config file: '%s'", filename);
9920 num_unknown_tokens++;
9923 Error(ERR_INFO, "- token: '%s'", list->token);
9927 if (num_unknown_tokens > 0)
9928 Error(ERR_INFO_LINE, "-");
9930 while (*num_elements % 4) /* pad with empty elements, if needed */
9931 (*elements)[(*num_elements)++] = EL_EMPTY;
9933 freeSetupFileList(setup_file_list);
9934 freeSetupFileHash(element_hash);
9937 for (i = 0; i < *num_elements; i++)
9938 printf("editor: element '%s' [%d]\n",
9939 element_info[(*elements)[i]].token_name, (*elements)[i]);
9943 static struct MusicFileInfo *get_music_file_info_ext(char *basename, int music,
9946 SetupFileHash *setup_file_hash = NULL;
9947 struct MusicFileInfo tmp_music_file_info, *new_music_file_info;
9948 char *filename_music, *filename_prefix, *filename_info;
9954 token_to_value_ptr[] =
9956 { "title_header", &tmp_music_file_info.title_header },
9957 { "artist_header", &tmp_music_file_info.artist_header },
9958 { "album_header", &tmp_music_file_info.album_header },
9959 { "year_header", &tmp_music_file_info.year_header },
9961 { "title", &tmp_music_file_info.title },
9962 { "artist", &tmp_music_file_info.artist },
9963 { "album", &tmp_music_file_info.album },
9964 { "year", &tmp_music_file_info.year },
9970 filename_music = (is_sound ? getCustomSoundFilename(basename) :
9971 getCustomMusicFilename(basename));
9973 if (filename_music == NULL)
9976 /* ---------- try to replace file extension ---------- */
9978 filename_prefix = getStringCopy(filename_music);
9979 if (strrchr(filename_prefix, '.') != NULL)
9980 *strrchr(filename_prefix, '.') = '\0';
9981 filename_info = getStringCat2(filename_prefix, ".txt");
9983 if (fileExists(filename_info))
9984 setup_file_hash = loadSetupFileHash(filename_info);
9986 free(filename_prefix);
9987 free(filename_info);
9989 if (setup_file_hash == NULL)
9991 /* ---------- try to add file extension ---------- */
9993 filename_prefix = getStringCopy(filename_music);
9994 filename_info = getStringCat2(filename_prefix, ".txt");
9996 if (fileExists(filename_info))
9997 setup_file_hash = loadSetupFileHash(filename_info);
9999 free(filename_prefix);
10000 free(filename_info);
10003 if (setup_file_hash == NULL)
10006 /* ---------- music file info found ---------- */
10008 clear_mem(&tmp_music_file_info, sizeof(struct MusicFileInfo));
10010 for (i = 0; token_to_value_ptr[i].token != NULL; i++)
10012 char *value = getHashEntry(setup_file_hash, token_to_value_ptr[i].token);
10014 *token_to_value_ptr[i].value_ptr =
10015 getStringCopy(value != NULL && *value != '\0' ? value : UNKNOWN_NAME);
10018 tmp_music_file_info.basename = getStringCopy(basename);
10019 tmp_music_file_info.music = music;
10020 tmp_music_file_info.is_sound = is_sound;
10022 new_music_file_info = checked_malloc(sizeof(struct MusicFileInfo));
10023 *new_music_file_info = tmp_music_file_info;
10025 return new_music_file_info;
10028 static struct MusicFileInfo *get_music_file_info(char *basename, int music)
10030 return get_music_file_info_ext(basename, music, FALSE);
10033 static struct MusicFileInfo *get_sound_file_info(char *basename, int sound)
10035 return get_music_file_info_ext(basename, sound, TRUE);
10038 static boolean music_info_listed_ext(struct MusicFileInfo *list,
10039 char *basename, boolean is_sound)
10041 for (; list != NULL; list = list->next)
10042 if (list->is_sound == is_sound && strEqual(list->basename, basename))
10048 static boolean music_info_listed(struct MusicFileInfo *list, char *basename)
10050 return music_info_listed_ext(list, basename, FALSE);
10053 static boolean sound_info_listed(struct MusicFileInfo *list, char *basename)
10055 return music_info_listed_ext(list, basename, TRUE);
10058 void LoadMusicInfo()
10060 char *music_directory = getCustomMusicDirectory();
10061 int num_music = getMusicListSize();
10062 int num_music_noconf = 0;
10063 int num_sounds = getSoundListSize();
10065 DirectoryEntry *dir_entry;
10066 struct FileInfo *music, *sound;
10067 struct MusicFileInfo *next, **new;
10070 while (music_file_info != NULL)
10072 next = music_file_info->next;
10074 checked_free(music_file_info->basename);
10076 checked_free(music_file_info->title_header);
10077 checked_free(music_file_info->artist_header);
10078 checked_free(music_file_info->album_header);
10079 checked_free(music_file_info->year_header);
10081 checked_free(music_file_info->title);
10082 checked_free(music_file_info->artist);
10083 checked_free(music_file_info->album);
10084 checked_free(music_file_info->year);
10086 free(music_file_info);
10088 music_file_info = next;
10091 new = &music_file_info;
10093 for (i = 0; i < num_music; i++)
10095 music = getMusicListEntry(i);
10097 if (music->filename == NULL)
10100 if (strEqual(music->filename, UNDEFINED_FILENAME))
10103 /* a configured file may be not recognized as music */
10104 if (!FileIsMusic(music->filename))
10107 if (!music_info_listed(music_file_info, music->filename))
10109 *new = get_music_file_info(music->filename, i);
10112 new = &(*new)->next;
10116 if ((dir = openDirectory(music_directory)) == NULL)
10118 Error(ERR_WARN, "cannot read music directory '%s'", music_directory);
10122 while ((dir_entry = readDirectory(dir)) != NULL) /* loop all entries */
10124 char *basename = dir_entry->basename;
10125 boolean music_already_used = FALSE;
10128 /* skip all music files that are configured in music config file */
10129 for (i = 0; i < num_music; i++)
10131 music = getMusicListEntry(i);
10133 if (music->filename == NULL)
10136 if (strEqual(basename, music->filename))
10138 music_already_used = TRUE;
10143 if (music_already_used)
10146 if (!FileIsMusic(dir_entry->filename))
10149 if (!music_info_listed(music_file_info, basename))
10151 *new = get_music_file_info(basename, MAP_NOCONF_MUSIC(num_music_noconf));
10154 new = &(*new)->next;
10157 num_music_noconf++;
10160 closeDirectory(dir);
10162 for (i = 0; i < num_sounds; i++)
10164 sound = getSoundListEntry(i);
10166 if (sound->filename == NULL)
10169 if (strEqual(sound->filename, UNDEFINED_FILENAME))
10172 /* a configured file may be not recognized as sound */
10173 if (!FileIsSound(sound->filename))
10176 if (!sound_info_listed(music_file_info, sound->filename))
10178 *new = get_sound_file_info(sound->filename, i);
10180 new = &(*new)->next;
10185 void add_helpanim_entry(int element, int action, int direction, int delay,
10186 int *num_list_entries)
10188 struct HelpAnimInfo *new_list_entry;
10189 (*num_list_entries)++;
10192 checked_realloc(helpanim_info,
10193 *num_list_entries * sizeof(struct HelpAnimInfo));
10194 new_list_entry = &helpanim_info[*num_list_entries - 1];
10196 new_list_entry->element = element;
10197 new_list_entry->action = action;
10198 new_list_entry->direction = direction;
10199 new_list_entry->delay = delay;
10202 void print_unknown_token(char *filename, char *token, int token_nr)
10206 Error(ERR_INFO_LINE, "-");
10207 Error(ERR_INFO, "warning: unknown token(s) found in config file:");
10208 Error(ERR_INFO, "- config file: '%s'", filename);
10211 Error(ERR_INFO, "- token: '%s'", token);
10214 void print_unknown_token_end(int token_nr)
10217 Error(ERR_INFO_LINE, "-");
10220 void LoadHelpAnimInfo()
10222 char *filename = getHelpAnimFilename();
10223 SetupFileList *setup_file_list = NULL, *list;
10224 SetupFileHash *element_hash, *action_hash, *direction_hash;
10225 int num_list_entries = 0;
10226 int num_unknown_tokens = 0;
10229 if (fileExists(filename))
10230 setup_file_list = loadSetupFileList(filename);
10232 if (setup_file_list == NULL)
10234 /* use reliable default values from static configuration */
10235 SetupFileList *insert_ptr;
10237 insert_ptr = setup_file_list =
10238 newSetupFileList(helpanim_config[0].token,
10239 helpanim_config[0].value);
10241 for (i = 1; helpanim_config[i].token; i++)
10242 insert_ptr = addListEntry(insert_ptr,
10243 helpanim_config[i].token,
10244 helpanim_config[i].value);
10247 element_hash = newSetupFileHash();
10248 action_hash = newSetupFileHash();
10249 direction_hash = newSetupFileHash();
10251 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
10252 setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
10254 for (i = 0; i < NUM_ACTIONS; i++)
10255 setHashEntry(action_hash, element_action_info[i].suffix,
10256 i_to_a(element_action_info[i].value));
10258 /* do not store direction index (bit) here, but direction value! */
10259 for (i = 0; i < NUM_DIRECTIONS_FULL; i++)
10260 setHashEntry(direction_hash, element_direction_info[i].suffix,
10261 i_to_a(1 << element_direction_info[i].value));
10263 for (list = setup_file_list; list != NULL; list = list->next)
10265 char *element_token, *action_token, *direction_token;
10266 char *element_value, *action_value, *direction_value;
10267 int delay = atoi(list->value);
10269 if (strEqual(list->token, "end"))
10271 add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
10276 /* first try to break element into element/action/direction parts;
10277 if this does not work, also accept combined "element[.act][.dir]"
10278 elements (like "dynamite.active"), which are unique elements */
10280 if (strchr(list->token, '.') == NULL) /* token contains no '.' */
10282 element_value = getHashEntry(element_hash, list->token);
10283 if (element_value != NULL) /* element found */
10284 add_helpanim_entry(atoi(element_value), -1, -1, delay,
10285 &num_list_entries);
10288 /* no further suffixes found -- this is not an element */
10289 print_unknown_token(filename, list->token, num_unknown_tokens++);
10295 /* token has format "<prefix>.<something>" */
10297 action_token = strchr(list->token, '.'); /* suffix may be action ... */
10298 direction_token = action_token; /* ... or direction */
10300 element_token = getStringCopy(list->token);
10301 *strchr(element_token, '.') = '\0';
10303 element_value = getHashEntry(element_hash, element_token);
10305 if (element_value == NULL) /* this is no element */
10307 element_value = getHashEntry(element_hash, list->token);
10308 if (element_value != NULL) /* combined element found */
10309 add_helpanim_entry(atoi(element_value), -1, -1, delay,
10310 &num_list_entries);
10312 print_unknown_token(filename, list->token, num_unknown_tokens++);
10314 free(element_token);
10319 action_value = getHashEntry(action_hash, action_token);
10321 if (action_value != NULL) /* action found */
10323 add_helpanim_entry(atoi(element_value), atoi(action_value), -1, delay,
10324 &num_list_entries);
10326 free(element_token);
10331 direction_value = getHashEntry(direction_hash, direction_token);
10333 if (direction_value != NULL) /* direction found */
10335 add_helpanim_entry(atoi(element_value), -1, atoi(direction_value), delay,
10336 &num_list_entries);
10338 free(element_token);
10343 if (strchr(action_token + 1, '.') == NULL)
10345 /* no further suffixes found -- this is not an action nor direction */
10347 element_value = getHashEntry(element_hash, list->token);
10348 if (element_value != NULL) /* combined element found */
10349 add_helpanim_entry(atoi(element_value), -1, -1, delay,
10350 &num_list_entries);
10352 print_unknown_token(filename, list->token, num_unknown_tokens++);
10354 free(element_token);
10359 /* token has format "<prefix>.<suffix>.<something>" */
10361 direction_token = strchr(action_token + 1, '.');
10363 action_token = getStringCopy(action_token);
10364 *strchr(action_token + 1, '.') = '\0';
10366 action_value = getHashEntry(action_hash, action_token);
10368 if (action_value == NULL) /* this is no action */
10370 element_value = getHashEntry(element_hash, list->token);
10371 if (element_value != NULL) /* combined element found */
10372 add_helpanim_entry(atoi(element_value), -1, -1, delay,
10373 &num_list_entries);
10375 print_unknown_token(filename, list->token, num_unknown_tokens++);
10377 free(element_token);
10378 free(action_token);
10383 direction_value = getHashEntry(direction_hash, direction_token);
10385 if (direction_value != NULL) /* direction found */
10387 add_helpanim_entry(atoi(element_value), atoi(action_value),
10388 atoi(direction_value), delay, &num_list_entries);
10390 free(element_token);
10391 free(action_token);
10396 /* this is no direction */
10398 element_value = getHashEntry(element_hash, list->token);
10399 if (element_value != NULL) /* combined element found */
10400 add_helpanim_entry(atoi(element_value), -1, -1, delay,
10401 &num_list_entries);
10403 print_unknown_token(filename, list->token, num_unknown_tokens++);
10405 free(element_token);
10406 free(action_token);
10409 print_unknown_token_end(num_unknown_tokens);
10411 add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
10412 add_helpanim_entry(HELPANIM_LIST_END, -1, -1, -1, &num_list_entries);
10414 freeSetupFileList(setup_file_list);
10415 freeSetupFileHash(element_hash);
10416 freeSetupFileHash(action_hash);
10417 freeSetupFileHash(direction_hash);
10420 for (i = 0; i < num_list_entries; i++)
10421 printf("::: '%s': %d, %d, %d => %d\n",
10422 EL_NAME(helpanim_info[i].element),
10423 helpanim_info[i].element,
10424 helpanim_info[i].action,
10425 helpanim_info[i].direction,
10426 helpanim_info[i].delay);
10430 void LoadHelpTextInfo()
10432 char *filename = getHelpTextFilename();
10435 if (helptext_info != NULL)
10437 freeSetupFileHash(helptext_info);
10438 helptext_info = NULL;
10441 if (fileExists(filename))
10442 helptext_info = loadSetupFileHash(filename);
10444 if (helptext_info == NULL)
10446 /* use reliable default values from static configuration */
10447 helptext_info = newSetupFileHash();
10449 for (i = 0; helptext_config[i].token; i++)
10450 setHashEntry(helptext_info,
10451 helptext_config[i].token,
10452 helptext_config[i].value);
10456 BEGIN_HASH_ITERATION(helptext_info, itr)
10458 printf("::: '%s' => '%s'\n",
10459 HASH_ITERATION_TOKEN(itr), HASH_ITERATION_VALUE(itr));
10461 END_HASH_ITERATION(hash, itr)
10466 /* ------------------------------------------------------------------------- */
10467 /* convert levels */
10468 /* ------------------------------------------------------------------------- */
10470 #define MAX_NUM_CONVERT_LEVELS 1000
10472 void ConvertLevels()
10474 static LevelDirTree *convert_leveldir = NULL;
10475 static int convert_level_nr = -1;
10476 static int num_levels_handled = 0;
10477 static int num_levels_converted = 0;
10478 static boolean levels_failed[MAX_NUM_CONVERT_LEVELS];
10481 convert_leveldir = getTreeInfoFromIdentifier(leveldir_first,
10482 global.convert_leveldir);
10484 if (convert_leveldir == NULL)
10485 Error(ERR_EXIT, "no such level identifier: '%s'",
10486 global.convert_leveldir);
10488 leveldir_current = convert_leveldir;
10490 if (global.convert_level_nr != -1)
10492 convert_leveldir->first_level = global.convert_level_nr;
10493 convert_leveldir->last_level = global.convert_level_nr;
10496 convert_level_nr = convert_leveldir->first_level;
10498 PrintLine("=", 79);
10499 Print("Converting levels\n");
10500 PrintLine("-", 79);
10501 Print("Level series identifier: '%s'\n", convert_leveldir->identifier);
10502 Print("Level series name: '%s'\n", convert_leveldir->name);
10503 Print("Level series author: '%s'\n", convert_leveldir->author);
10504 Print("Number of levels: %d\n", convert_leveldir->levels);
10505 PrintLine("=", 79);
10508 for (i = 0; i < MAX_NUM_CONVERT_LEVELS; i++)
10509 levels_failed[i] = FALSE;
10511 while (convert_level_nr <= convert_leveldir->last_level)
10513 char *level_filename;
10516 level_nr = convert_level_nr++;
10518 Print("Level %03d: ", level_nr);
10520 LoadLevel(level_nr);
10521 if (level.no_level_file || level.no_valid_file)
10523 Print("(no level)\n");
10527 Print("converting level ... ");
10529 level_filename = getDefaultLevelFilename(level_nr);
10530 new_level = !fileExists(level_filename);
10534 SaveLevel(level_nr);
10536 num_levels_converted++;
10538 Print("converted.\n");
10542 if (level_nr >= 0 && level_nr < MAX_NUM_CONVERT_LEVELS)
10543 levels_failed[level_nr] = TRUE;
10545 Print("NOT CONVERTED -- LEVEL ALREADY EXISTS.\n");
10548 num_levels_handled++;
10552 PrintLine("=", 79);
10553 Print("Number of levels handled: %d\n", num_levels_handled);
10554 Print("Number of levels converted: %d (%d%%)\n", num_levels_converted,
10555 (num_levels_handled ?
10556 num_levels_converted * 100 / num_levels_handled : 0));
10557 PrintLine("-", 79);
10558 Print("Summary (for automatic parsing by scripts):\n");
10559 Print("LEVELDIR '%s', CONVERTED %d/%d (%d%%)",
10560 convert_leveldir->identifier, num_levels_converted,
10561 num_levels_handled,
10562 (num_levels_handled ?
10563 num_levels_converted * 100 / num_levels_handled : 0));
10565 if (num_levels_handled != num_levels_converted)
10567 Print(", FAILED:");
10568 for (i = 0; i < MAX_NUM_CONVERT_LEVELS; i++)
10569 if (levels_failed[i])
10574 PrintLine("=", 79);
10576 CloseAllAndExit(0);
10580 /* ------------------------------------------------------------------------- */
10581 /* create and save images for use in level sketches (raw BMP format) */
10582 /* ------------------------------------------------------------------------- */
10584 void CreateLevelSketchImages()
10586 #if defined(TARGET_SDL)
10591 InitElementPropertiesGfxElement();
10593 bitmap1 = CreateBitmap(TILEX, TILEY, DEFAULT_DEPTH);
10594 bitmap2 = CreateBitmap(MINI_TILEX, MINI_TILEY, DEFAULT_DEPTH);
10596 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
10598 Bitmap *src_bitmap;
10600 int element = getMappedElement(i);
10601 int graphic = el2edimg(element);
10602 char basename1[16];
10603 char basename2[16];
10607 sprintf(basename1, "%03d.bmp", i);
10608 sprintf(basename2, "%03ds.bmp", i);
10610 filename1 = getPath2(global.create_images_dir, basename1);
10611 filename2 = getPath2(global.create_images_dir, basename2);
10613 getFixedGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
10614 BlitBitmap(src_bitmap, bitmap1, src_x, src_y, TILEX, TILEY,
10617 if (SDL_SaveBMP(bitmap1->surface, filename1) != 0)
10618 Error(ERR_EXIT, "cannot save level sketch image file '%s'", filename1);
10620 getMiniGraphicSource(graphic, &src_bitmap, &src_x, &src_y);
10621 BlitBitmap(src_bitmap, bitmap2, src_x, src_y, MINI_TILEX, MINI_TILEY, 0, 0);
10623 if (SDL_SaveBMP(bitmap2->surface, filename2) != 0)
10624 Error(ERR_EXIT, "cannot save level sketch image file '%s'", filename2);
10630 printf("%03d `%03d%c", i, i, (i % 10 < 9 ? ' ' : '\n'));
10633 FreeBitmap(bitmap1);
10634 FreeBitmap(bitmap2);
10639 Error(ERR_INFO, "%d normal and small images created", NUM_FILE_ELEMENTS);
10641 CloseAllAndExit(0);
10646 /* ------------------------------------------------------------------------- */
10647 /* create and save images for custom and group elements (raw BMP format) */
10648 /* ------------------------------------------------------------------------- */
10650 void CreateCustomElementImages(char *directory)
10652 #if defined(TARGET_SDL)
10653 char *src_basename = "RocksCE-template.ilbm";
10654 char *dst_basename = "RocksCE.bmp";
10655 char *src_filename = getPath2(directory, src_basename);
10656 char *dst_filename = getPath2(directory, dst_basename);
10657 Bitmap *src_bitmap;
10659 int yoffset_ce = 0;
10660 int yoffset_ge = (TILEY * NUM_CUSTOM_ELEMENTS / 16);
10663 SDLInitVideoDisplay();
10665 ReCreateBitmap(&backbuffer, video.width, video.height);
10667 src_bitmap = LoadImage(src_filename);
10669 bitmap = CreateBitmap(TILEX * 16 * 2,
10670 TILEY * (NUM_CUSTOM_ELEMENTS + NUM_GROUP_ELEMENTS) / 16,
10673 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
10680 BlitBitmap(src_bitmap, bitmap, 0, 0, TILEX, TILEY,
10681 TILEX * x, TILEY * y + yoffset_ce);
10683 BlitBitmap(src_bitmap, bitmap, 0, TILEY,
10685 TILEX * x + TILEX * 16,
10686 TILEY * y + yoffset_ce);
10688 for (j = 2; j >= 0; j--)
10692 BlitBitmap(src_bitmap, bitmap,
10693 TILEX + c * 7, 0, 6, 10,
10694 TILEX * x + 6 + j * 7,
10695 TILEY * y + 11 + yoffset_ce);
10697 BlitBitmap(src_bitmap, bitmap,
10698 TILEX + c * 8, TILEY, 6, 10,
10699 TILEX * 16 + TILEX * x + 6 + j * 8,
10700 TILEY * y + 10 + yoffset_ce);
10706 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
10713 BlitBitmap(src_bitmap, bitmap, 0, 0, TILEX, TILEY,
10714 TILEX * x, TILEY * y + yoffset_ge);
10716 BlitBitmap(src_bitmap, bitmap, 0, TILEY,
10718 TILEX * x + TILEX * 16,
10719 TILEY * y + yoffset_ge);
10721 for (j = 1; j >= 0; j--)
10725 BlitBitmap(src_bitmap, bitmap, TILEX + c * 10, 11, 10, 10,
10726 TILEX * x + 6 + j * 10,
10727 TILEY * y + 11 + yoffset_ge);
10729 BlitBitmap(src_bitmap, bitmap,
10730 TILEX + c * 8, TILEY + 12, 6, 10,
10731 TILEX * 16 + TILEX * x + 10 + j * 8,
10732 TILEY * y + 10 + yoffset_ge);
10738 if (SDL_SaveBMP(bitmap->surface, dst_filename) != 0)
10739 Error(ERR_EXIT, "cannot save CE graphics file '%s'", dst_filename);
10741 FreeBitmap(bitmap);
10743 CloseAllAndExit(0);