1 // ============================================================================
2 // Rocks'n'Diamonds - McDuffin Strikes Back!
3 // ----------------------------------------------------------------------------
4 // (c) 1995-2014 by Artsoft Entertainment
7 // http://www.artsoft.org/
8 // ----------------------------------------------------------------------------
10 // ============================================================================
17 #include "libgame/libgame.h"
25 #define ENABLE_UNUSED_CODE 0 /* currently unused functions */
26 #define ENABLE_HISTORIC_CHUNKS 0 /* only for historic reference */
27 #define ENABLE_RESERVED_CODE 0 /* reserved for later use */
29 #define CHUNK_ID_LEN 4 /* IFF style chunk id length */
30 #define CHUNK_SIZE_UNDEFINED 0 /* undefined chunk size == 0 */
31 #define CHUNK_SIZE_NONE -1 /* do not write chunk size */
33 #define LEVEL_CHUNK_NAME_SIZE MAX_LEVEL_NAME_LEN
34 #define LEVEL_CHUNK_AUTH_SIZE MAX_LEVEL_AUTHOR_LEN
36 #define LEVEL_CHUNK_VERS_SIZE 8 /* size of file version chunk */
37 #define LEVEL_CHUNK_DATE_SIZE 4 /* size of file date chunk */
38 #define LEVEL_CHUNK_HEAD_SIZE 80 /* size of level file header */
39 #define LEVEL_CHUNK_HEAD_UNUSED 0 /* unused level header bytes */
40 #define LEVEL_CHUNK_CNT2_SIZE 160 /* size of level CNT2 chunk */
41 #define LEVEL_CHUNK_CNT2_UNUSED 11 /* unused CNT2 chunk bytes */
42 #define LEVEL_CHUNK_CNT3_HEADER 16 /* size of level CNT3 header */
43 #define LEVEL_CHUNK_CNT3_UNUSED 10 /* unused CNT3 chunk bytes */
44 #define LEVEL_CPART_CUS3_SIZE 134 /* size of CUS3 chunk part */
45 #define LEVEL_CPART_CUS3_UNUSED 15 /* unused CUS3 bytes / part */
46 #define LEVEL_CHUNK_GRP1_SIZE 74 /* size of level GRP1 chunk */
48 /* (element number, number of change pages, change page number) */
49 #define LEVEL_CHUNK_CUSX_UNCHANGED (2 + (1 + 1) + (1 + 1))
51 /* (element number only) */
52 #define LEVEL_CHUNK_GRPX_UNCHANGED 2
53 #define LEVEL_CHUNK_NOTE_UNCHANGED 2
55 /* (nothing at all if unchanged) */
56 #define LEVEL_CHUNK_ELEM_UNCHANGED 0
58 #define TAPE_CHUNK_VERS_SIZE 8 /* size of file version chunk */
59 #define TAPE_CHUNK_HEAD_SIZE 20 /* size of tape file header */
60 #define TAPE_CHUNK_HEAD_UNUSED 2 /* unused tape header bytes */
62 #define LEVEL_CHUNK_CNT3_SIZE(x) (LEVEL_CHUNK_CNT3_HEADER + (x))
63 #define LEVEL_CHUNK_CUS3_SIZE(x) (2 + (x) * LEVEL_CPART_CUS3_SIZE)
64 #define LEVEL_CHUNK_CUS4_SIZE(x) (96 + (x) * 48)
66 /* file identifier strings */
67 #define LEVEL_COOKIE_TMPL "ROCKSNDIAMONDS_LEVEL_FILE_VERSION_x.x"
68 #define TAPE_COOKIE_TMPL "ROCKSNDIAMONDS_TAPE_FILE_VERSION_x.x"
69 #define SCORE_COOKIE "ROCKSNDIAMONDS_SCORE_FILE_VERSION_1.2"
71 /* values for deciding when (not) to save configuration data */
72 #define SAVE_CONF_NEVER 0
73 #define SAVE_CONF_ALWAYS 1
74 #define SAVE_CONF_WHEN_CHANGED -1
76 /* values for chunks using micro chunks */
77 #define CONF_MASK_1_BYTE 0x00
78 #define CONF_MASK_2_BYTE 0x40
79 #define CONF_MASK_4_BYTE 0x80
80 #define CONF_MASK_MULTI_BYTES 0xc0
82 #define CONF_MASK_BYTES 0xc0
83 #define CONF_MASK_TOKEN 0x3f
85 #define CONF_VALUE_1_BYTE(x) (CONF_MASK_1_BYTE | (x))
86 #define CONF_VALUE_2_BYTE(x) (CONF_MASK_2_BYTE | (x))
87 #define CONF_VALUE_4_BYTE(x) (CONF_MASK_4_BYTE | (x))
88 #define CONF_VALUE_MULTI_BYTES(x) (CONF_MASK_MULTI_BYTES | (x))
90 /* these definitions are just for convenience of use and readability */
91 #define CONF_VALUE_8_BIT(x) CONF_VALUE_1_BYTE(x)
92 #define CONF_VALUE_16_BIT(x) CONF_VALUE_2_BYTE(x)
93 #define CONF_VALUE_32_BIT(x) CONF_VALUE_4_BYTE(x)
94 #define CONF_VALUE_BYTES(x) CONF_VALUE_MULTI_BYTES(x)
96 #define CONF_VALUE_NUM_BYTES(x) ((x) == CONF_MASK_1_BYTE ? 1 : \
97 (x) == CONF_MASK_2_BYTE ? 2 : \
98 (x) == CONF_MASK_4_BYTE ? 4 : 0)
100 #define CONF_CONTENT_NUM_ELEMENTS (3 * 3)
101 #define CONF_CONTENT_NUM_BYTES (CONF_CONTENT_NUM_ELEMENTS * 2)
102 #define CONF_ELEMENT_NUM_BYTES (2)
104 #define CONF_ENTITY_NUM_BYTES(t) ((t) == TYPE_ELEMENT || \
105 (t) == TYPE_ELEMENT_LIST ? \
106 CONF_ELEMENT_NUM_BYTES : \
107 (t) == TYPE_CONTENT || \
108 (t) == TYPE_CONTENT_LIST ? \
109 CONF_CONTENT_NUM_BYTES : 1)
111 #define CONF_ELEMENT_BYTE_POS(i) ((i) * CONF_ELEMENT_NUM_BYTES)
112 #define CONF_ELEMENTS_ELEMENT(b,i) ((b[CONF_ELEMENT_BYTE_POS(i)] << 8) | \
113 (b[CONF_ELEMENT_BYTE_POS(i) + 1]))
115 #define CONF_CONTENT_ELEMENT_POS(c,x,y) ((c) * CONF_CONTENT_NUM_ELEMENTS + \
117 #define CONF_CONTENT_BYTE_POS(c,x,y) (CONF_CONTENT_ELEMENT_POS(c,x,y) * \
118 CONF_ELEMENT_NUM_BYTES)
119 #define CONF_CONTENTS_ELEMENT(b,c,x,y) ((b[CONF_CONTENT_BYTE_POS(c,x,y)]<< 8)|\
120 (b[CONF_CONTENT_BYTE_POS(c,x,y) + 1]))
122 /* temporary variables used to store pointers to structure members */
123 static struct LevelInfo li;
124 static struct ElementInfo xx_ei, yy_ei;
125 static struct ElementChangeInfo xx_change;
126 static struct ElementGroupInfo xx_group;
127 static struct EnvelopeInfo xx_envelope;
128 static unsigned int xx_event_bits[NUM_CE_BITFIELDS];
129 static char xx_default_description[MAX_ELEMENT_NAME_LEN + 1];
130 static int xx_num_contents;
131 static int xx_current_change_page;
132 static char xx_default_string_empty[1] = "";
133 static int xx_string_length_unused;
135 struct LevelFileConfigInfo
137 int element; /* element for which data is to be stored */
138 int save_type; /* save data always, never or when changed */
139 int data_type; /* data type (used internally, not stored) */
140 int conf_type; /* micro chunk identifier (stored in file) */
143 void *value; /* variable that holds the data to be stored */
144 int default_value; /* initial default value for this variable */
147 void *value_copy; /* variable that holds the data to be copied */
148 void *num_entities; /* number of entities for multi-byte data */
149 int default_num_entities; /* default number of entities for this data */
150 int max_num_entities; /* maximal number of entities for this data */
151 char *default_string; /* optional default string for string data */
154 static struct LevelFileConfigInfo chunk_config_INFO[] =
156 /* ---------- values not related to single elements ----------------------- */
159 -1, SAVE_CONF_ALWAYS,
160 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
161 &li.game_engine_type, GAME_ENGINE_TYPE_RND
165 -1, SAVE_CONF_ALWAYS,
166 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
167 &li.fieldx, STD_LEV_FIELDX
170 -1, SAVE_CONF_ALWAYS,
171 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
172 &li.fieldy, STD_LEV_FIELDY
176 -1, SAVE_CONF_ALWAYS,
177 TYPE_INTEGER, CONF_VALUE_16_BIT(3),
182 -1, SAVE_CONF_ALWAYS,
183 TYPE_INTEGER, CONF_VALUE_16_BIT(4),
189 TYPE_INTEGER, CONF_VALUE_32_BIT(2),
195 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
196 &li.use_step_counter, FALSE
201 TYPE_BITFIELD, CONF_VALUE_8_BIT(4),
202 &li.wind_direction_initial, MV_NONE
207 TYPE_BOOLEAN, CONF_VALUE_8_BIT(5),
208 &li.em_slippery_gems, FALSE
213 TYPE_BOOLEAN, CONF_VALUE_8_BIT(6),
214 &li.use_custom_template, FALSE
219 TYPE_BITFIELD, CONF_VALUE_32_BIT(1),
220 &li.can_move_into_acid_bits, ~0 /* default: everything can */
225 TYPE_BITFIELD, CONF_VALUE_8_BIT(7),
226 &li.dont_collide_with_bits, ~0 /* default: always deadly */
231 TYPE_BOOLEAN, CONF_VALUE_8_BIT(8),
232 &li.em_explodes_by_fire, FALSE
237 TYPE_INTEGER, CONF_VALUE_16_BIT(5),
238 &li.score[SC_TIME_BONUS], 1
243 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
244 &li.auto_exit_sokoban, FALSE
249 TYPE_BOOLEAN, CONF_VALUE_8_BIT(10),
250 &li.auto_count_gems, FALSE
260 static struct LevelFileConfigInfo chunk_config_ELEM[] =
262 /* (these values are the same for each player) */
265 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
266 &li.block_last_field, FALSE /* default case for EM levels */
270 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
271 &li.sp_block_last_field, TRUE /* default case for SP levels */
275 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
276 &li.instant_relocation, FALSE
280 TYPE_BOOLEAN, CONF_VALUE_8_BIT(4),
281 &li.can_pass_to_walkable, FALSE
285 TYPE_BOOLEAN, CONF_VALUE_8_BIT(5),
286 &li.block_snap_field, TRUE
290 TYPE_BOOLEAN, CONF_VALUE_8_BIT(6),
291 &li.continuous_snapping, TRUE
295 TYPE_BOOLEAN, CONF_VALUE_8_BIT(12),
296 &li.shifted_relocation, FALSE
300 TYPE_BOOLEAN, CONF_VALUE_8_BIT(15),
301 &li.lazy_relocation, FALSE
304 /* (these values are different for each player) */
307 TYPE_INTEGER, CONF_VALUE_8_BIT(7),
308 &li.initial_player_stepsize[0], STEPSIZE_NORMAL
312 TYPE_BOOLEAN, CONF_VALUE_8_BIT(8),
313 &li.initial_player_gravity[0], FALSE
317 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
318 &li.use_start_element[0], FALSE
322 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
323 &li.start_element[0], EL_PLAYER_1
327 TYPE_BOOLEAN, CONF_VALUE_8_BIT(10),
328 &li.use_artwork_element[0], FALSE
332 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
333 &li.artwork_element[0], EL_PLAYER_1
337 TYPE_BOOLEAN, CONF_VALUE_8_BIT(11),
338 &li.use_explosion_element[0], FALSE
342 TYPE_ELEMENT, CONF_VALUE_16_BIT(3),
343 &li.explosion_element[0], EL_PLAYER_1
347 TYPE_BOOLEAN, CONF_VALUE_8_BIT(13),
348 &li.use_initial_inventory[0], FALSE
352 TYPE_BOOLEAN, CONF_VALUE_8_BIT(14),
353 &li.initial_inventory_size[0], 1
357 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(1),
358 &li.initial_inventory_content[0][0],EL_EMPTY, NULL,
359 &li.initial_inventory_size[0], 1, MAX_INITIAL_INVENTORY_SIZE
364 TYPE_INTEGER, CONF_VALUE_8_BIT(7),
365 &li.initial_player_stepsize[1], STEPSIZE_NORMAL
369 TYPE_BOOLEAN, CONF_VALUE_8_BIT(8),
370 &li.initial_player_gravity[1], FALSE
374 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
375 &li.use_start_element[1], FALSE
379 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
380 &li.start_element[1], EL_PLAYER_2
384 TYPE_BOOLEAN, CONF_VALUE_8_BIT(10),
385 &li.use_artwork_element[1], FALSE
389 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
390 &li.artwork_element[1], EL_PLAYER_2
394 TYPE_BOOLEAN, CONF_VALUE_8_BIT(11),
395 &li.use_explosion_element[1], FALSE
399 TYPE_ELEMENT, CONF_VALUE_16_BIT(3),
400 &li.explosion_element[1], EL_PLAYER_2
404 TYPE_BOOLEAN, CONF_VALUE_8_BIT(13),
405 &li.use_initial_inventory[1], FALSE
409 TYPE_BOOLEAN, CONF_VALUE_8_BIT(14),
410 &li.initial_inventory_size[1], 1
414 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(1),
415 &li.initial_inventory_content[1][0],EL_EMPTY, NULL,
416 &li.initial_inventory_size[1], 1, MAX_INITIAL_INVENTORY_SIZE
421 TYPE_INTEGER, CONF_VALUE_8_BIT(7),
422 &li.initial_player_stepsize[2], STEPSIZE_NORMAL
426 TYPE_BOOLEAN, CONF_VALUE_8_BIT(8),
427 &li.initial_player_gravity[2], FALSE
431 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
432 &li.use_start_element[2], FALSE
436 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
437 &li.start_element[2], EL_PLAYER_3
441 TYPE_BOOLEAN, CONF_VALUE_8_BIT(10),
442 &li.use_artwork_element[2], FALSE
446 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
447 &li.artwork_element[2], EL_PLAYER_3
451 TYPE_BOOLEAN, CONF_VALUE_8_BIT(11),
452 &li.use_explosion_element[2], FALSE
456 TYPE_ELEMENT, CONF_VALUE_16_BIT(3),
457 &li.explosion_element[2], EL_PLAYER_3
461 TYPE_BOOLEAN, CONF_VALUE_8_BIT(13),
462 &li.use_initial_inventory[2], FALSE
466 TYPE_BOOLEAN, CONF_VALUE_8_BIT(14),
467 &li.initial_inventory_size[2], 1
471 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(1),
472 &li.initial_inventory_content[2][0],EL_EMPTY, NULL,
473 &li.initial_inventory_size[2], 1, MAX_INITIAL_INVENTORY_SIZE
478 TYPE_INTEGER, CONF_VALUE_8_BIT(7),
479 &li.initial_player_stepsize[3], STEPSIZE_NORMAL
483 TYPE_BOOLEAN, CONF_VALUE_8_BIT(8),
484 &li.initial_player_gravity[3], FALSE
488 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
489 &li.use_start_element[3], FALSE
493 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
494 &li.start_element[3], EL_PLAYER_4
498 TYPE_BOOLEAN, CONF_VALUE_8_BIT(10),
499 &li.use_artwork_element[3], FALSE
503 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
504 &li.artwork_element[3], EL_PLAYER_4
508 TYPE_BOOLEAN, CONF_VALUE_8_BIT(11),
509 &li.use_explosion_element[3], FALSE
513 TYPE_ELEMENT, CONF_VALUE_16_BIT(3),
514 &li.explosion_element[3], EL_PLAYER_4
518 TYPE_BOOLEAN, CONF_VALUE_8_BIT(13),
519 &li.use_initial_inventory[3], FALSE
523 TYPE_BOOLEAN, CONF_VALUE_8_BIT(14),
524 &li.initial_inventory_size[3], 1
528 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(1),
529 &li.initial_inventory_content[3][0],EL_EMPTY, NULL,
530 &li.initial_inventory_size[3], 1, MAX_INITIAL_INVENTORY_SIZE
535 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
536 &li.score[SC_EMERALD], 10
541 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
542 &li.score[SC_DIAMOND], 10
547 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
548 &li.score[SC_BUG], 10
553 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
554 &li.score[SC_SPACESHIP], 10
559 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
560 &li.score[SC_PACMAN], 10
565 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
566 &li.score[SC_NUT], 10
571 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
572 &li.score[SC_DYNAMITE], 10
577 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
578 &li.score[SC_KEY], 10
583 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
584 &li.score[SC_PEARL], 10
589 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
590 &li.score[SC_CRYSTAL], 10
595 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
596 &li.amoeba_content, EL_DIAMOND
600 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
605 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
606 &li.grow_into_diggable, TRUE
611 TYPE_CONTENT_LIST, CONF_VALUE_BYTES(1),
612 &li.yamyam_content, EL_ROCK, NULL,
613 &li.num_yamyam_contents, 4, MAX_ELEMENT_CONTENTS
617 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
618 &li.score[SC_YAMYAM], 10
623 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
624 &li.score[SC_ROBOT], 10
628 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
634 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
640 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
641 &li.time_magic_wall, 10
646 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
647 &li.game_of_life[0], 2
651 TYPE_INTEGER, CONF_VALUE_8_BIT(2),
652 &li.game_of_life[1], 3
656 TYPE_INTEGER, CONF_VALUE_8_BIT(3),
657 &li.game_of_life[2], 3
661 TYPE_INTEGER, CONF_VALUE_8_BIT(4),
662 &li.game_of_life[3], 3
667 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
672 TYPE_INTEGER, CONF_VALUE_8_BIT(2),
677 TYPE_INTEGER, CONF_VALUE_8_BIT(3),
682 TYPE_INTEGER, CONF_VALUE_8_BIT(4),
687 EL_TIMEGATE_SWITCH, -1,
688 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
689 &li.time_timegate, 10
693 EL_LIGHT_SWITCH_ACTIVE, -1,
694 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
699 EL_SHIELD_NORMAL, -1,
700 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
701 &li.shield_normal_time, 10
704 EL_SHIELD_NORMAL, -1,
705 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
706 &li.score[SC_SHIELD], 10
710 EL_SHIELD_DEADLY, -1,
711 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
712 &li.shield_deadly_time, 10
715 EL_SHIELD_DEADLY, -1,
716 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
717 &li.score[SC_SHIELD], 10
722 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
727 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
728 &li.extra_time_score, 10
732 EL_TIME_ORB_FULL, -1,
733 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
734 &li.time_orb_time, 10
737 EL_TIME_ORB_FULL, -1,
738 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
739 &li.use_time_orb_bug, FALSE
744 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
745 &li.use_spring_bug, FALSE
750 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
751 &li.android_move_time, 10
755 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
756 &li.android_clone_time, 10
760 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(1),
761 &li.android_clone_element[0], EL_EMPTY, NULL,
762 &li.num_android_clone_elements, 1, MAX_ANDROID_ELEMENTS
767 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
772 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
777 EL_EMC_MAGNIFIER, -1,
778 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
779 &li.magnify_score, 10
782 EL_EMC_MAGNIFIER, -1,
783 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
788 EL_EMC_MAGIC_BALL, -1,
789 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
793 EL_EMC_MAGIC_BALL, -1,
794 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
795 &li.ball_random, FALSE
798 EL_EMC_MAGIC_BALL, -1,
799 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
800 &li.ball_state_initial, FALSE
803 EL_EMC_MAGIC_BALL, -1,
804 TYPE_CONTENT_LIST, CONF_VALUE_BYTES(1),
805 &li.ball_content, EL_EMPTY, NULL,
806 &li.num_ball_contents, 4, MAX_ELEMENT_CONTENTS
811 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
812 &li.mm_laser_red, FALSE
816 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
817 &li.mm_laser_green, FALSE
821 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
822 &li.mm_laser_blue, TRUE
827 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
828 &li.df_laser_red, TRUE
832 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
833 &li.df_laser_green, TRUE
837 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
838 &li.df_laser_blue, FALSE
843 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
847 /* ---------- unused values ----------------------------------------------- */
850 EL_UNKNOWN, SAVE_CONF_NEVER,
851 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
852 &li.score[SC_UNKNOWN_14], 10
855 EL_UNKNOWN, SAVE_CONF_NEVER,
856 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
857 &li.score[SC_UNKNOWN_15], 10
867 static struct LevelFileConfigInfo chunk_config_NOTE[] =
871 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
872 &xx_envelope.xsize, MAX_ENVELOPE_XSIZE,
876 TYPE_INTEGER, CONF_VALUE_8_BIT(2),
877 &xx_envelope.ysize, MAX_ENVELOPE_YSIZE,
882 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
883 &xx_envelope.autowrap, FALSE
887 TYPE_BOOLEAN, CONF_VALUE_8_BIT(4),
888 &xx_envelope.centered, FALSE
893 TYPE_STRING, CONF_VALUE_BYTES(1),
894 &xx_envelope.text, -1, NULL,
895 &xx_string_length_unused, -1, MAX_ENVELOPE_TEXT_LEN,
896 &xx_default_string_empty[0]
906 static struct LevelFileConfigInfo chunk_config_CUSX_base[] =
910 TYPE_STRING, CONF_VALUE_BYTES(1),
911 &xx_ei.description[0], -1,
912 &yy_ei.description[0],
913 &xx_string_length_unused, -1, MAX_ELEMENT_NAME_LEN,
914 &xx_default_description[0]
919 TYPE_BITFIELD, CONF_VALUE_32_BIT(1),
920 &xx_ei.properties[EP_BITFIELD_BASE_NR], EP_BITMASK_BASE_DEFAULT,
921 &yy_ei.properties[EP_BITFIELD_BASE_NR]
923 #if ENABLE_RESERVED_CODE
924 /* (reserved for later use) */
927 TYPE_BITFIELD, CONF_VALUE_32_BIT(2),
928 &xx_ei.properties[EP_BITFIELD_BASE_NR + 1], EP_BITMASK_DEFAULT,
929 &yy_ei.properties[EP_BITFIELD_BASE_NR + 1]
935 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
936 &xx_ei.use_gfx_element, FALSE,
937 &yy_ei.use_gfx_element
941 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
942 &xx_ei.gfx_element_initial, EL_EMPTY_SPACE,
943 &yy_ei.gfx_element_initial
948 TYPE_BITFIELD, CONF_VALUE_8_BIT(2),
949 &xx_ei.access_direction, MV_ALL_DIRECTIONS,
950 &yy_ei.access_direction
955 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
956 &xx_ei.collect_score_initial, 10,
957 &yy_ei.collect_score_initial
961 TYPE_INTEGER, CONF_VALUE_16_BIT(3),
962 &xx_ei.collect_count_initial, 1,
963 &yy_ei.collect_count_initial
968 TYPE_INTEGER, CONF_VALUE_16_BIT(4),
969 &xx_ei.ce_value_fixed_initial, 0,
970 &yy_ei.ce_value_fixed_initial
974 TYPE_INTEGER, CONF_VALUE_16_BIT(5),
975 &xx_ei.ce_value_random_initial, 0,
976 &yy_ei.ce_value_random_initial
980 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
981 &xx_ei.use_last_ce_value, FALSE,
982 &yy_ei.use_last_ce_value
987 TYPE_INTEGER, CONF_VALUE_16_BIT(6),
988 &xx_ei.push_delay_fixed, 8,
989 &yy_ei.push_delay_fixed
993 TYPE_INTEGER, CONF_VALUE_16_BIT(7),
994 &xx_ei.push_delay_random, 8,
995 &yy_ei.push_delay_random
999 TYPE_INTEGER, CONF_VALUE_16_BIT(8),
1000 &xx_ei.drop_delay_fixed, 0,
1001 &yy_ei.drop_delay_fixed
1005 TYPE_INTEGER, CONF_VALUE_16_BIT(9),
1006 &xx_ei.drop_delay_random, 0,
1007 &yy_ei.drop_delay_random
1011 TYPE_INTEGER, CONF_VALUE_16_BIT(10),
1012 &xx_ei.move_delay_fixed, 0,
1013 &yy_ei.move_delay_fixed
1017 TYPE_INTEGER, CONF_VALUE_16_BIT(11),
1018 &xx_ei.move_delay_random, 0,
1019 &yy_ei.move_delay_random
1024 TYPE_BITFIELD, CONF_VALUE_32_BIT(3),
1025 &xx_ei.move_pattern, MV_ALL_DIRECTIONS,
1030 TYPE_BITFIELD, CONF_VALUE_8_BIT(4),
1031 &xx_ei.move_direction_initial, MV_START_AUTOMATIC,
1032 &yy_ei.move_direction_initial
1036 TYPE_INTEGER, CONF_VALUE_8_BIT(5),
1037 &xx_ei.move_stepsize, TILEX / 8,
1038 &yy_ei.move_stepsize
1043 TYPE_ELEMENT, CONF_VALUE_16_BIT(12),
1044 &xx_ei.move_enter_element, EL_EMPTY_SPACE,
1045 &yy_ei.move_enter_element
1049 TYPE_ELEMENT, CONF_VALUE_16_BIT(13),
1050 &xx_ei.move_leave_element, EL_EMPTY_SPACE,
1051 &yy_ei.move_leave_element
1055 TYPE_INTEGER, CONF_VALUE_8_BIT(6),
1056 &xx_ei.move_leave_type, LEAVE_TYPE_UNLIMITED,
1057 &yy_ei.move_leave_type
1062 TYPE_INTEGER, CONF_VALUE_8_BIT(7),
1063 &xx_ei.slippery_type, SLIPPERY_ANY_RANDOM,
1064 &yy_ei.slippery_type
1069 TYPE_INTEGER, CONF_VALUE_8_BIT(8),
1070 &xx_ei.explosion_type, EXPLODES_3X3,
1071 &yy_ei.explosion_type
1075 TYPE_INTEGER, CONF_VALUE_16_BIT(14),
1076 &xx_ei.explosion_delay, 16,
1077 &yy_ei.explosion_delay
1081 TYPE_INTEGER, CONF_VALUE_16_BIT(15),
1082 &xx_ei.ignition_delay, 8,
1083 &yy_ei.ignition_delay
1088 TYPE_CONTENT_LIST, CONF_VALUE_BYTES(2),
1089 &xx_ei.content, EL_EMPTY_SPACE,
1091 &xx_num_contents, 1, 1
1094 /* ---------- "num_change_pages" must be the last entry ------------------- */
1097 -1, SAVE_CONF_ALWAYS,
1098 TYPE_INTEGER, CONF_VALUE_8_BIT(9),
1099 &xx_ei.num_change_pages, 1,
1100 &yy_ei.num_change_pages
1111 static struct LevelFileConfigInfo chunk_config_CUSX_change[] =
1113 /* ---------- "current_change_page" must be the first entry --------------- */
1116 -1, SAVE_CONF_ALWAYS,
1117 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
1118 &xx_current_change_page, -1
1121 /* ---------- (the remaining entries can be in any order) ----------------- */
1125 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
1126 &xx_change.can_change, FALSE
1131 TYPE_BITFIELD, CONF_VALUE_32_BIT(1),
1132 &xx_event_bits[0], 0
1136 TYPE_BITFIELD, CONF_VALUE_32_BIT(2),
1137 &xx_event_bits[1], 0
1142 TYPE_BITFIELD, CONF_VALUE_8_BIT(3),
1143 &xx_change.trigger_player, CH_PLAYER_ANY
1147 TYPE_BITFIELD, CONF_VALUE_8_BIT(4),
1148 &xx_change.trigger_side, CH_SIDE_ANY
1152 TYPE_BITFIELD, CONF_VALUE_32_BIT(3),
1153 &xx_change.trigger_page, CH_PAGE_ANY
1158 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
1159 &xx_change.target_element, EL_EMPTY_SPACE
1164 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
1165 &xx_change.delay_fixed, 0
1169 TYPE_INTEGER, CONF_VALUE_16_BIT(3),
1170 &xx_change.delay_random, 0
1174 TYPE_INTEGER, CONF_VALUE_16_BIT(4),
1175 &xx_change.delay_frames, FRAMES_PER_SECOND
1180 TYPE_ELEMENT, CONF_VALUE_16_BIT(5),
1181 &xx_change.initial_trigger_element, EL_EMPTY_SPACE
1186 TYPE_BOOLEAN, CONF_VALUE_8_BIT(6),
1187 &xx_change.explode, FALSE
1191 TYPE_BOOLEAN, CONF_VALUE_8_BIT(7),
1192 &xx_change.use_target_content, FALSE
1196 TYPE_BOOLEAN, CONF_VALUE_8_BIT(8),
1197 &xx_change.only_if_complete, FALSE
1201 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
1202 &xx_change.use_random_replace, FALSE
1206 TYPE_INTEGER, CONF_VALUE_8_BIT(10),
1207 &xx_change.random_percentage, 100
1211 TYPE_INTEGER, CONF_VALUE_8_BIT(11),
1212 &xx_change.replace_when, CP_WHEN_EMPTY
1217 TYPE_BOOLEAN, CONF_VALUE_8_BIT(12),
1218 &xx_change.has_action, FALSE
1222 TYPE_INTEGER, CONF_VALUE_8_BIT(13),
1223 &xx_change.action_type, CA_NO_ACTION
1227 TYPE_INTEGER, CONF_VALUE_8_BIT(14),
1228 &xx_change.action_mode, CA_MODE_UNDEFINED
1232 TYPE_INTEGER, CONF_VALUE_16_BIT(6),
1233 &xx_change.action_arg, CA_ARG_UNDEFINED
1238 TYPE_ELEMENT, CONF_VALUE_16_BIT(7),
1239 &xx_change.action_element, EL_EMPTY_SPACE
1244 TYPE_CONTENT_LIST, CONF_VALUE_BYTES(1),
1245 &xx_change.target_content, EL_EMPTY_SPACE, NULL,
1246 &xx_num_contents, 1, 1
1256 static struct LevelFileConfigInfo chunk_config_GRPX[] =
1260 TYPE_STRING, CONF_VALUE_BYTES(1),
1261 &xx_ei.description[0], -1, NULL,
1262 &xx_string_length_unused, -1, MAX_ELEMENT_NAME_LEN,
1263 &xx_default_description[0]
1268 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1269 &xx_ei.use_gfx_element, FALSE
1273 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
1274 &xx_ei.gfx_element_initial, EL_EMPTY_SPACE
1279 TYPE_INTEGER, CONF_VALUE_8_BIT(2),
1280 &xx_group.choice_mode, ANIM_RANDOM
1285 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(2),
1286 &xx_group.element[0], EL_EMPTY_SPACE, NULL,
1287 &xx_group.num_elements, 1, MAX_ELEMENTS_IN_GROUP
1297 static struct LevelFileConfigInfo chunk_config_CONF[] = /* (OBSOLETE) */
1301 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
1302 &li.block_snap_field, TRUE
1306 TYPE_BOOLEAN, CONF_VALUE_8_BIT(13),
1307 &li.continuous_snapping, TRUE
1311 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
1312 &li.initial_player_stepsize[0], STEPSIZE_NORMAL
1316 TYPE_BOOLEAN, CONF_VALUE_8_BIT(10),
1317 &li.use_start_element[0], FALSE
1321 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
1322 &li.start_element[0], EL_PLAYER_1
1326 TYPE_BOOLEAN, CONF_VALUE_8_BIT(11),
1327 &li.use_artwork_element[0], FALSE
1331 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
1332 &li.artwork_element[0], EL_PLAYER_1
1336 TYPE_BOOLEAN, CONF_VALUE_8_BIT(12),
1337 &li.use_explosion_element[0], FALSE
1341 TYPE_ELEMENT, CONF_VALUE_16_BIT(3),
1342 &li.explosion_element[0], EL_PLAYER_1
1357 filetype_id_list[] =
1359 { LEVEL_FILE_TYPE_RND, "RND" },
1360 { LEVEL_FILE_TYPE_BD, "BD" },
1361 { LEVEL_FILE_TYPE_EM, "EM" },
1362 { LEVEL_FILE_TYPE_SP, "SP" },
1363 { LEVEL_FILE_TYPE_DX, "DX" },
1364 { LEVEL_FILE_TYPE_SB, "SB" },
1365 { LEVEL_FILE_TYPE_DC, "DC" },
1366 { LEVEL_FILE_TYPE_MM, "MM" },
1367 { LEVEL_FILE_TYPE_MM, "DF" },
1372 /* ========================================================================= */
1373 /* level file functions */
1374 /* ========================================================================= */
1376 static boolean check_special_flags(char *flag)
1378 if (strEqual(options.special_flags, flag) ||
1379 strEqual(leveldir_current->special_flags, flag))
1385 static struct DateInfo getCurrentDate()
1387 time_t epoch_seconds = time(NULL);
1388 struct tm *now = localtime(&epoch_seconds);
1389 struct DateInfo date;
1391 date.year = now->tm_year + 1900;
1392 date.month = now->tm_mon + 1;
1393 date.day = now->tm_mday;
1395 date.src = DATE_SRC_CLOCK;
1400 static void resetEventFlags(struct ElementChangeInfo *change)
1404 for (i = 0; i < NUM_CHANGE_EVENTS; i++)
1405 change->has_event[i] = FALSE;
1408 static void resetEventBits()
1412 for (i = 0; i < NUM_CE_BITFIELDS; i++)
1413 xx_event_bits[i] = 0;
1416 static void setEventFlagsFromEventBits(struct ElementChangeInfo *change)
1420 /* important: only change event flag if corresponding event bit is set
1421 (this is because all xx_event_bits[] values are loaded separately,
1422 and all xx_event_bits[] values are set back to zero before loading
1423 another value xx_event_bits[x] (each value representing 32 flags)) */
1425 for (i = 0; i < NUM_CHANGE_EVENTS; i++)
1426 if (xx_event_bits[CH_EVENT_BITFIELD_NR(i)] & CH_EVENT_BIT(i))
1427 change->has_event[i] = TRUE;
1430 static void setEventBitsFromEventFlags(struct ElementChangeInfo *change)
1434 /* in contrast to the above function setEventFlagsFromEventBits(), it
1435 would also be possible to set all bits in xx_event_bits[] to 0 or 1
1436 depending on the corresponding change->has_event[i] values here, as
1437 all xx_event_bits[] values are reset in resetEventBits() before */
1439 for (i = 0; i < NUM_CHANGE_EVENTS; i++)
1440 if (change->has_event[i])
1441 xx_event_bits[CH_EVENT_BITFIELD_NR(i)] |= CH_EVENT_BIT(i);
1444 static char *getDefaultElementDescription(struct ElementInfo *ei)
1446 static char description[MAX_ELEMENT_NAME_LEN + 1];
1447 char *default_description = (ei->custom_description != NULL ?
1448 ei->custom_description :
1449 ei->editor_description);
1452 /* always start with reliable default values */
1453 for (i = 0; i < MAX_ELEMENT_NAME_LEN + 1; i++)
1454 description[i] = '\0';
1456 /* truncate element description to MAX_ELEMENT_NAME_LEN bytes */
1457 strncpy(description, default_description, MAX_ELEMENT_NAME_LEN);
1459 return &description[0];
1462 static void setElementDescriptionToDefault(struct ElementInfo *ei)
1464 char *default_description = getDefaultElementDescription(ei);
1467 for (i = 0; i < MAX_ELEMENT_NAME_LEN + 1; i++)
1468 ei->description[i] = default_description[i];
1471 static void setConfigToDefaultsFromConfigList(struct LevelFileConfigInfo *conf)
1475 for (i = 0; conf[i].data_type != -1; i++)
1477 int default_value = conf[i].default_value;
1478 int data_type = conf[i].data_type;
1479 int conf_type = conf[i].conf_type;
1480 int byte_mask = conf_type & CONF_MASK_BYTES;
1482 if (byte_mask == CONF_MASK_MULTI_BYTES)
1484 int default_num_entities = conf[i].default_num_entities;
1485 int max_num_entities = conf[i].max_num_entities;
1487 *(int *)(conf[i].num_entities) = default_num_entities;
1489 if (data_type == TYPE_STRING)
1491 char *default_string = conf[i].default_string;
1492 char *string = (char *)(conf[i].value);
1494 strncpy(string, default_string, max_num_entities);
1496 else if (data_type == TYPE_ELEMENT_LIST)
1498 int *element_array = (int *)(conf[i].value);
1501 for (j = 0; j < max_num_entities; j++)
1502 element_array[j] = default_value;
1504 else if (data_type == TYPE_CONTENT_LIST)
1506 struct Content *content = (struct Content *)(conf[i].value);
1509 for (c = 0; c < max_num_entities; c++)
1510 for (y = 0; y < 3; y++)
1511 for (x = 0; x < 3; x++)
1512 content[c].e[x][y] = default_value;
1515 else /* constant size configuration data (1, 2 or 4 bytes) */
1517 if (data_type == TYPE_BOOLEAN)
1518 *(boolean *)(conf[i].value) = default_value;
1520 *(int *) (conf[i].value) = default_value;
1525 static void copyConfigFromConfigList(struct LevelFileConfigInfo *conf)
1529 for (i = 0; conf[i].data_type != -1; i++)
1531 int data_type = conf[i].data_type;
1532 int conf_type = conf[i].conf_type;
1533 int byte_mask = conf_type & CONF_MASK_BYTES;
1535 if (byte_mask == CONF_MASK_MULTI_BYTES)
1537 int max_num_entities = conf[i].max_num_entities;
1539 if (data_type == TYPE_STRING)
1541 char *string = (char *)(conf[i].value);
1542 char *string_copy = (char *)(conf[i].value_copy);
1544 strncpy(string_copy, string, max_num_entities);
1546 else if (data_type == TYPE_ELEMENT_LIST)
1548 int *element_array = (int *)(conf[i].value);
1549 int *element_array_copy = (int *)(conf[i].value_copy);
1552 for (j = 0; j < max_num_entities; j++)
1553 element_array_copy[j] = element_array[j];
1555 else if (data_type == TYPE_CONTENT_LIST)
1557 struct Content *content = (struct Content *)(conf[i].value);
1558 struct Content *content_copy = (struct Content *)(conf[i].value_copy);
1561 for (c = 0; c < max_num_entities; c++)
1562 for (y = 0; y < 3; y++)
1563 for (x = 0; x < 3; x++)
1564 content_copy[c].e[x][y] = content[c].e[x][y];
1567 else /* constant size configuration data (1, 2 or 4 bytes) */
1569 if (data_type == TYPE_BOOLEAN)
1570 *(boolean *)(conf[i].value_copy) = *(boolean *)(conf[i].value);
1572 *(int *) (conf[i].value_copy) = *(int *) (conf[i].value);
1577 void copyElementInfo(struct ElementInfo *ei_from, struct ElementInfo *ei_to)
1581 xx_ei = *ei_from; /* copy element data into temporary buffer */
1582 yy_ei = *ei_to; /* copy element data into temporary buffer */
1584 copyConfigFromConfigList(chunk_config_CUSX_base);
1589 /* ---------- reinitialize and copy change pages ---------- */
1591 ei_to->num_change_pages = ei_from->num_change_pages;
1592 ei_to->current_change_page = ei_from->current_change_page;
1594 setElementChangePages(ei_to, ei_to->num_change_pages);
1596 for (i = 0; i < ei_to->num_change_pages; i++)
1597 ei_to->change_page[i] = ei_from->change_page[i];
1599 /* ---------- copy group element info ---------- */
1600 if (ei_from->group != NULL && ei_to->group != NULL) /* group or internal */
1601 *ei_to->group = *ei_from->group;
1603 /* mark this custom element as modified */
1604 ei_to->modified_settings = TRUE;
1607 void setElementChangePages(struct ElementInfo *ei, int change_pages)
1609 int change_page_size = sizeof(struct ElementChangeInfo);
1611 ei->num_change_pages = MAX(1, change_pages);
1614 checked_realloc(ei->change_page, ei->num_change_pages * change_page_size);
1616 if (ei->current_change_page >= ei->num_change_pages)
1617 ei->current_change_page = ei->num_change_pages - 1;
1619 ei->change = &ei->change_page[ei->current_change_page];
1622 void setElementChangeInfoToDefaults(struct ElementChangeInfo *change)
1624 xx_change = *change; /* copy change data into temporary buffer */
1626 setConfigToDefaultsFromConfigList(chunk_config_CUSX_change);
1628 *change = xx_change;
1630 resetEventFlags(change);
1632 change->direct_action = 0;
1633 change->other_action = 0;
1635 change->pre_change_function = NULL;
1636 change->change_function = NULL;
1637 change->post_change_function = NULL;
1640 static void setLevelInfoToDefaults_Level(struct LevelInfo *level)
1644 li = *level; /* copy level data into temporary buffer */
1645 setConfigToDefaultsFromConfigList(chunk_config_INFO);
1646 *level = li; /* copy temporary buffer back to level data */
1648 setLevelInfoToDefaults_EM();
1649 setLevelInfoToDefaults_SP();
1650 setLevelInfoToDefaults_MM();
1652 level->native_em_level = &native_em_level;
1653 level->native_sp_level = &native_sp_level;
1654 level->native_mm_level = &native_mm_level;
1656 level->file_version = FILE_VERSION_ACTUAL;
1657 level->game_version = GAME_VERSION_ACTUAL;
1659 level->creation_date = getCurrentDate();
1661 level->encoding_16bit_field = TRUE;
1662 level->encoding_16bit_yamyam = TRUE;
1663 level->encoding_16bit_amoeba = TRUE;
1665 /* clear level name and level author string buffers */
1666 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
1667 level->name[i] = '\0';
1668 for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
1669 level->author[i] = '\0';
1671 /* set level name and level author to default values */
1672 strcpy(level->name, NAMELESS_LEVEL_NAME);
1673 strcpy(level->author, ANONYMOUS_NAME);
1675 /* set level playfield to playable default level with player and exit */
1676 for (x = 0; x < MAX_LEV_FIELDX; x++)
1677 for (y = 0; y < MAX_LEV_FIELDY; y++)
1678 level->field[x][y] = EL_SAND;
1680 level->field[0][0] = EL_PLAYER_1;
1681 level->field[STD_LEV_FIELDX - 1][STD_LEV_FIELDY - 1] = EL_EXIT_CLOSED;
1683 BorderElement = EL_STEELWALL;
1685 /* detect custom elements when loading them */
1686 level->file_has_custom_elements = FALSE;
1688 /* set all bug compatibility flags to "false" => do not emulate this bug */
1689 level->use_action_after_change_bug = FALSE;
1691 if (leveldir_current)
1693 /* try to determine better author name than 'anonymous' */
1694 if (!strEqual(leveldir_current->author, ANONYMOUS_NAME))
1696 strncpy(level->author, leveldir_current->author, MAX_LEVEL_AUTHOR_LEN);
1697 level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
1701 switch (LEVELCLASS(leveldir_current))
1703 case LEVELCLASS_TUTORIAL:
1704 strcpy(level->author, PROGRAM_AUTHOR_STRING);
1707 case LEVELCLASS_CONTRIB:
1708 strncpy(level->author, leveldir_current->name, MAX_LEVEL_AUTHOR_LEN);
1709 level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
1712 case LEVELCLASS_PRIVATE:
1713 strncpy(level->author, getRealName(), MAX_LEVEL_AUTHOR_LEN);
1714 level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
1718 /* keep default value */
1725 static void setLevelInfoToDefaults_Elements(struct LevelInfo *level)
1727 static boolean clipboard_elements_initialized = FALSE;
1730 InitElementPropertiesStatic();
1732 li = *level; /* copy level data into temporary buffer */
1733 setConfigToDefaultsFromConfigList(chunk_config_ELEM);
1734 *level = li; /* copy temporary buffer back to level data */
1736 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1739 struct ElementInfo *ei = &element_info[element];
1741 /* never initialize clipboard elements after the very first time */
1742 /* (to be able to use clipboard elements between several levels) */
1743 if (IS_CLIPBOARD_ELEMENT(element) && clipboard_elements_initialized)
1746 if (IS_ENVELOPE(element))
1748 int envelope_nr = element - EL_ENVELOPE_1;
1750 setConfigToDefaultsFromConfigList(chunk_config_NOTE);
1752 level->envelope[envelope_nr] = xx_envelope;
1755 if (IS_CUSTOM_ELEMENT(element) ||
1756 IS_GROUP_ELEMENT(element) ||
1757 IS_INTERNAL_ELEMENT(element))
1759 xx_ei = *ei; /* copy element data into temporary buffer */
1761 setConfigToDefaultsFromConfigList(chunk_config_CUSX_base);
1766 setElementChangePages(ei, 1);
1767 setElementChangeInfoToDefaults(ei->change);
1769 if (IS_CUSTOM_ELEMENT(element) ||
1770 IS_GROUP_ELEMENT(element) ||
1771 IS_INTERNAL_ELEMENT(element))
1773 setElementDescriptionToDefault(ei);
1775 ei->modified_settings = FALSE;
1778 if (IS_CUSTOM_ELEMENT(element) ||
1779 IS_INTERNAL_ELEMENT(element))
1781 /* internal values used in level editor */
1783 ei->access_type = 0;
1784 ei->access_layer = 0;
1785 ei->access_protected = 0;
1786 ei->walk_to_action = 0;
1787 ei->smash_targets = 0;
1790 ei->can_explode_by_fire = FALSE;
1791 ei->can_explode_smashed = FALSE;
1792 ei->can_explode_impact = FALSE;
1794 ei->current_change_page = 0;
1797 if (IS_GROUP_ELEMENT(element) ||
1798 IS_INTERNAL_ELEMENT(element))
1800 struct ElementGroupInfo *group;
1802 /* initialize memory for list of elements in group */
1803 if (ei->group == NULL)
1804 ei->group = checked_malloc(sizeof(struct ElementGroupInfo));
1808 xx_group = *group; /* copy group data into temporary buffer */
1810 setConfigToDefaultsFromConfigList(chunk_config_GRPX);
1816 clipboard_elements_initialized = TRUE;
1819 static void setLevelInfoToDefaults(struct LevelInfo *level,
1820 boolean level_info_only,
1821 boolean reset_file_status)
1823 setLevelInfoToDefaults_Level(level);
1825 if (!level_info_only)
1826 setLevelInfoToDefaults_Elements(level);
1828 if (reset_file_status)
1830 level->no_valid_file = FALSE;
1831 level->no_level_file = FALSE;
1834 level->changed = FALSE;
1837 static void setFileInfoToDefaults(struct LevelFileInfo *level_file_info)
1839 level_file_info->nr = 0;
1840 level_file_info->type = LEVEL_FILE_TYPE_UNKNOWN;
1841 level_file_info->packed = FALSE;
1842 level_file_info->basename = NULL;
1843 level_file_info->filename = NULL;
1846 int getMappedElement_SB(int, boolean);
1848 static void ActivateLevelTemplate()
1852 if (check_special_flags("load_xsb_to_ces"))
1854 /* fill smaller playfields with padding "beyond border wall" elements */
1855 if (level.fieldx < level_template.fieldx ||
1856 level.fieldy < level_template.fieldy)
1858 short field[level.fieldx][level.fieldy];
1859 int new_fieldx = MAX(level.fieldx, level_template.fieldx);
1860 int new_fieldy = MAX(level.fieldy, level_template.fieldy);
1861 int pos_fieldx = (new_fieldx - level.fieldx) / 2;
1862 int pos_fieldy = (new_fieldy - level.fieldy) / 2;
1864 /* copy old playfield (which is smaller than the visible area) */
1865 for (y = 0; y < level.fieldy; y++) for (x = 0; x < level.fieldx; x++)
1866 field[x][y] = level.field[x][y];
1868 /* fill new, larger playfield with "beyond border wall" elements */
1869 for (y = 0; y < new_fieldy; y++) for (x = 0; x < new_fieldx; x++)
1870 level.field[x][y] = getMappedElement_SB('_', TRUE);
1872 /* copy the old playfield to the middle of the new playfield */
1873 for (y = 0; y < level.fieldy; y++) for (x = 0; x < level.fieldx; x++)
1874 level.field[pos_fieldx + x][pos_fieldy + y] = field[x][y];
1876 level.fieldx = new_fieldx;
1877 level.fieldy = new_fieldy;
1881 /* Currently there is no special action needed to activate the template
1882 data, because 'element_info' property settings overwrite the original
1883 level data, while all other variables do not change. */
1885 /* Exception: 'from_level_template' elements in the original level playfield
1886 are overwritten with the corresponding elements at the same position in
1887 playfield from the level template. */
1889 for (x = 0; x < level.fieldx; x++)
1890 for (y = 0; y < level.fieldy; y++)
1891 if (level.field[x][y] == EL_FROM_LEVEL_TEMPLATE)
1892 level.field[x][y] = level_template.field[x][y];
1894 if (check_special_flags("load_xsb_to_ces"))
1896 struct LevelInfo level_backup = level;
1898 /* overwrite all individual level settings from template level settings */
1899 level = level_template;
1901 /* restore playfield size */
1902 level.fieldx = level_backup.fieldx;
1903 level.fieldy = level_backup.fieldy;
1905 /* restore playfield content */
1906 for (x = 0; x < level.fieldx; x++)
1907 for (y = 0; y < level.fieldy; y++)
1908 level.field[x][y] = level_backup.field[x][y];
1910 /* restore name and author from individual level */
1911 strcpy(level.name, level_backup.name);
1912 strcpy(level.author, level_backup.author);
1914 /* restore flag "use_custom_template" */
1915 level.use_custom_template = level_backup.use_custom_template;
1919 static char *getLevelFilenameFromBasename(char *basename)
1921 static char *filename[2] = { NULL, NULL };
1922 int pos = (strEqual(basename, LEVELTEMPLATE_FILENAME) ? 0 : 1);
1924 checked_free(filename[pos]);
1926 filename[pos] = getPath2(getCurrentLevelDir(), basename);
1928 return filename[pos];
1931 static int getFileTypeFromBasename(char *basename)
1933 /* !!! ALSO SEE COMMENT IN checkForPackageFromBasename() !!! */
1935 static char *filename = NULL;
1936 struct stat file_status;
1938 /* ---------- try to determine file type from filename ---------- */
1940 /* check for typical filename of a Supaplex level package file */
1941 if (strlen(basename) == 10 && strPrefixLower(basename, "levels.d"))
1942 return LEVEL_FILE_TYPE_SP;
1944 /* check for typical filename of a Diamond Caves II level package file */
1945 if (strSuffixLower(basename, ".dc") ||
1946 strSuffixLower(basename, ".dc2"))
1947 return LEVEL_FILE_TYPE_DC;
1949 /* check for typical filename of a Sokoban level package file */
1950 if (strSuffixLower(basename, ".xsb") &&
1951 strchr(basename, '%') == NULL)
1952 return LEVEL_FILE_TYPE_SB;
1954 /* ---------- try to determine file type from filesize ---------- */
1956 checked_free(filename);
1957 filename = getPath2(getCurrentLevelDir(), basename);
1959 if (stat(filename, &file_status) == 0)
1961 /* check for typical filesize of a Supaplex level package file */
1962 if (file_status.st_size == 170496)
1963 return LEVEL_FILE_TYPE_SP;
1966 return LEVEL_FILE_TYPE_UNKNOWN;
1969 static int getFileTypeFromMagicBytes(char *filename, int type)
1973 if ((file = openFile(filename, MODE_READ)))
1975 char chunk_name[CHUNK_ID_LEN + 1];
1977 getFileChunkBE(file, chunk_name, NULL);
1979 if (strEqual(chunk_name, "MMII") ||
1980 strEqual(chunk_name, "MIRR"))
1981 type = LEVEL_FILE_TYPE_MM;
1989 static boolean checkForPackageFromBasename(char *basename)
1991 /* !!! WON'T WORK ANYMORE IF getFileTypeFromBasename() ALSO DETECTS !!!
1992 !!! SINGLE LEVELS (CURRENTLY ONLY DETECTS LEVEL PACKAGES !!! */
1994 return (getFileTypeFromBasename(basename) != LEVEL_FILE_TYPE_UNKNOWN);
1997 static char *getSingleLevelBasenameExt(int nr, char *extension)
1999 static char basename[MAX_FILENAME_LEN];
2002 sprintf(basename, "%s", LEVELTEMPLATE_FILENAME);
2004 sprintf(basename, "%03d.%s", nr, extension);
2009 static char *getSingleLevelBasename(int nr)
2011 return getSingleLevelBasenameExt(nr, LEVELFILE_EXTENSION);
2014 static char *getPackedLevelBasename(int type)
2016 static char basename[MAX_FILENAME_LEN];
2017 char *directory = getCurrentLevelDir();
2019 DirectoryEntry *dir_entry;
2021 strcpy(basename, UNDEFINED_FILENAME); /* default: undefined file */
2023 if ((dir = openDirectory(directory)) == NULL)
2025 Error(ERR_WARN, "cannot read current level directory '%s'", directory);
2030 while ((dir_entry = readDirectory(dir)) != NULL) /* loop all entries */
2032 char *entry_basename = dir_entry->basename;
2033 int entry_type = getFileTypeFromBasename(entry_basename);
2035 if (entry_type != LEVEL_FILE_TYPE_UNKNOWN) /* found valid level package */
2037 if (type == LEVEL_FILE_TYPE_UNKNOWN ||
2040 strcpy(basename, entry_basename);
2047 closeDirectory(dir);
2052 static char *getSingleLevelFilename(int nr)
2054 return getLevelFilenameFromBasename(getSingleLevelBasename(nr));
2057 #if ENABLE_UNUSED_CODE
2058 static char *getPackedLevelFilename(int type)
2060 return getLevelFilenameFromBasename(getPackedLevelBasename(type));
2064 char *getDefaultLevelFilename(int nr)
2066 return getSingleLevelFilename(nr);
2069 #if ENABLE_UNUSED_CODE
2070 static void setLevelFileInfo_SingleLevelFilename(struct LevelFileInfo *lfi,
2074 lfi->packed = FALSE;
2075 lfi->basename = getSingleLevelBasename(lfi->nr, lfi->type);
2076 lfi->filename = getLevelFilenameFromBasename(lfi->basename);
2080 static void setLevelFileInfo_FormatLevelFilename(struct LevelFileInfo *lfi,
2081 int type, char *format, ...)
2083 static char basename[MAX_FILENAME_LEN];
2086 va_start(ap, format);
2087 vsprintf(basename, format, ap);
2091 lfi->packed = FALSE;
2092 lfi->basename = basename;
2093 lfi->filename = getLevelFilenameFromBasename(lfi->basename);
2096 static void setLevelFileInfo_PackedLevelFilename(struct LevelFileInfo *lfi,
2101 lfi->basename = getPackedLevelBasename(lfi->type);
2102 lfi->filename = getLevelFilenameFromBasename(lfi->basename);
2105 static int getFiletypeFromID(char *filetype_id)
2107 char *filetype_id_lower;
2108 int filetype = LEVEL_FILE_TYPE_UNKNOWN;
2111 if (filetype_id == NULL)
2112 return LEVEL_FILE_TYPE_UNKNOWN;
2114 filetype_id_lower = getStringToLower(filetype_id);
2116 for (i = 0; filetype_id_list[i].id != NULL; i++)
2118 char *id_lower = getStringToLower(filetype_id_list[i].id);
2120 if (strEqual(filetype_id_lower, id_lower))
2121 filetype = filetype_id_list[i].filetype;
2125 if (filetype != LEVEL_FILE_TYPE_UNKNOWN)
2129 free(filetype_id_lower);
2134 char *getLocalLevelTemplateFilename()
2136 return getDefaultLevelFilename(-1);
2139 char *getGlobalLevelTemplateFilename()
2141 /* global variable "leveldir_current" must be modified in the loop below */
2142 LevelDirTree *leveldir_current_last = leveldir_current;
2143 char *filename = NULL;
2145 /* check for template level in path from current to topmost tree node */
2147 while (leveldir_current != NULL)
2149 filename = getDefaultLevelFilename(-1);
2151 if (fileExists(filename))
2154 leveldir_current = leveldir_current->node_parent;
2157 /* restore global variable "leveldir_current" modified in above loop */
2158 leveldir_current = leveldir_current_last;
2163 static void determineLevelFileInfo_Filename(struct LevelFileInfo *lfi)
2167 /* special case: level number is negative => check for level template file */
2170 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
2171 getSingleLevelBasename(-1));
2173 /* replace local level template filename with global template filename */
2174 lfi->filename = getGlobalLevelTemplateFilename();
2176 /* no fallback if template file not existing */
2180 /* special case: check for file name/pattern specified in "levelinfo.conf" */
2181 if (leveldir_current->level_filename != NULL)
2183 int filetype = getFiletypeFromID(leveldir_current->level_filetype);
2185 setLevelFileInfo_FormatLevelFilename(lfi, filetype,
2186 leveldir_current->level_filename, nr);
2188 lfi->packed = checkForPackageFromBasename(leveldir_current->level_filename);
2190 if (fileExists(lfi->filename))
2193 else if (leveldir_current->level_filetype != NULL)
2195 int filetype = getFiletypeFromID(leveldir_current->level_filetype);
2197 /* check for specified native level file with standard file name */
2198 setLevelFileInfo_FormatLevelFilename(lfi, filetype,
2199 "%03d.%s", nr, LEVELFILE_EXTENSION);
2200 if (fileExists(lfi->filename))
2204 /* check for native Rocks'n'Diamonds level file */
2205 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
2206 "%03d.%s", nr, LEVELFILE_EXTENSION);
2207 if (fileExists(lfi->filename))
2210 /* check for Emerald Mine level file (V1) */
2211 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "a%c%c",
2212 'a' + (nr / 10) % 26, '0' + nr % 10);
2213 if (fileExists(lfi->filename))
2215 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "A%c%c",
2216 'A' + (nr / 10) % 26, '0' + nr % 10);
2217 if (fileExists(lfi->filename))
2220 /* check for Emerald Mine level file (V2 to V5) */
2221 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%d", nr);
2222 if (fileExists(lfi->filename))
2225 /* check for Emerald Mine level file (V6 / single mode) */
2226 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02ds", nr);
2227 if (fileExists(lfi->filename))
2229 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02dS", nr);
2230 if (fileExists(lfi->filename))
2233 /* check for Emerald Mine level file (V6 / teamwork mode) */
2234 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02dt", nr);
2235 if (fileExists(lfi->filename))
2237 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02dT", nr);
2238 if (fileExists(lfi->filename))
2241 /* check for various packed level file formats */
2242 setLevelFileInfo_PackedLevelFilename(lfi, LEVEL_FILE_TYPE_UNKNOWN);
2243 if (fileExists(lfi->filename))
2246 /* no known level file found -- use default values (and fail later) */
2247 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
2248 "%03d.%s", nr, LEVELFILE_EXTENSION);
2251 static void determineLevelFileInfo_Filetype(struct LevelFileInfo *lfi)
2253 if (lfi->type == LEVEL_FILE_TYPE_UNKNOWN)
2254 lfi->type = getFileTypeFromBasename(lfi->basename);
2256 if (lfi->type == LEVEL_FILE_TYPE_RND)
2257 lfi->type = getFileTypeFromMagicBytes(lfi->filename, lfi->type);
2260 static void setLevelFileInfo(struct LevelFileInfo *level_file_info, int nr)
2262 /* always start with reliable default values */
2263 setFileInfoToDefaults(level_file_info);
2265 level_file_info->nr = nr; /* set requested level number */
2267 determineLevelFileInfo_Filename(level_file_info);
2268 determineLevelFileInfo_Filetype(level_file_info);
2271 /* ------------------------------------------------------------------------- */
2272 /* functions for loading R'n'D level */
2273 /* ------------------------------------------------------------------------- */
2275 int getMappedElement(int element)
2277 /* remap some (historic, now obsolete) elements */
2281 case EL_PLAYER_OBSOLETE:
2282 element = EL_PLAYER_1;
2285 case EL_KEY_OBSOLETE:
2289 case EL_EM_KEY_1_FILE_OBSOLETE:
2290 element = EL_EM_KEY_1;
2293 case EL_EM_KEY_2_FILE_OBSOLETE:
2294 element = EL_EM_KEY_2;
2297 case EL_EM_KEY_3_FILE_OBSOLETE:
2298 element = EL_EM_KEY_3;
2301 case EL_EM_KEY_4_FILE_OBSOLETE:
2302 element = EL_EM_KEY_4;
2305 case EL_ENVELOPE_OBSOLETE:
2306 element = EL_ENVELOPE_1;
2314 if (element >= NUM_FILE_ELEMENTS)
2316 Error(ERR_WARN, "invalid level element %d", element);
2318 element = EL_UNKNOWN;
2326 int getMappedElementByVersion(int element, int game_version)
2328 /* remap some elements due to certain game version */
2330 if (game_version <= VERSION_IDENT(2,2,0,0))
2332 /* map game font elements */
2333 element = (element == EL_CHAR('[') ? EL_CHAR_AUMLAUT :
2334 element == EL_CHAR('\\') ? EL_CHAR_OUMLAUT :
2335 element == EL_CHAR(']') ? EL_CHAR_UUMLAUT :
2336 element == EL_CHAR('^') ? EL_CHAR_COPYRIGHT : element);
2339 if (game_version < VERSION_IDENT(3,0,0,0))
2341 /* map Supaplex gravity tube elements */
2342 element = (element == EL_SP_GRAVITY_PORT_LEFT ? EL_SP_PORT_LEFT :
2343 element == EL_SP_GRAVITY_PORT_RIGHT ? EL_SP_PORT_RIGHT :
2344 element == EL_SP_GRAVITY_PORT_UP ? EL_SP_PORT_UP :
2345 element == EL_SP_GRAVITY_PORT_DOWN ? EL_SP_PORT_DOWN :
2352 static int LoadLevel_VERS(File *file, int chunk_size, struct LevelInfo *level)
2354 level->file_version = getFileVersion(file);
2355 level->game_version = getFileVersion(file);
2360 static int LoadLevel_DATE(File *file, int chunk_size, struct LevelInfo *level)
2362 level->creation_date.year = getFile16BitBE(file);
2363 level->creation_date.month = getFile8Bit(file);
2364 level->creation_date.day = getFile8Bit(file);
2366 level->creation_date.src = DATE_SRC_LEVELFILE;
2371 static int LoadLevel_HEAD(File *file, int chunk_size, struct LevelInfo *level)
2373 int initial_player_stepsize;
2374 int initial_player_gravity;
2377 level->fieldx = getFile8Bit(file);
2378 level->fieldy = getFile8Bit(file);
2380 level->time = getFile16BitBE(file);
2381 level->gems_needed = getFile16BitBE(file);
2383 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
2384 level->name[i] = getFile8Bit(file);
2385 level->name[MAX_LEVEL_NAME_LEN] = 0;
2387 for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
2388 level->score[i] = getFile8Bit(file);
2390 level->num_yamyam_contents = STD_ELEMENT_CONTENTS;
2391 for (i = 0; i < STD_ELEMENT_CONTENTS; i++)
2392 for (y = 0; y < 3; y++)
2393 for (x = 0; x < 3; x++)
2394 level->yamyam_content[i].e[x][y] = getMappedElement(getFile8Bit(file));
2396 level->amoeba_speed = getFile8Bit(file);
2397 level->time_magic_wall = getFile8Bit(file);
2398 level->time_wheel = getFile8Bit(file);
2399 level->amoeba_content = getMappedElement(getFile8Bit(file));
2401 initial_player_stepsize = (getFile8Bit(file) == 1 ? STEPSIZE_FAST :
2404 for (i = 0; i < MAX_PLAYERS; i++)
2405 level->initial_player_stepsize[i] = initial_player_stepsize;
2407 initial_player_gravity = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2409 for (i = 0; i < MAX_PLAYERS; i++)
2410 level->initial_player_gravity[i] = initial_player_gravity;
2412 level->encoding_16bit_field = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2413 level->em_slippery_gems = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2415 level->use_custom_template = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2417 level->block_last_field = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2418 level->sp_block_last_field = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2419 level->can_move_into_acid_bits = getFile32BitBE(file);
2420 level->dont_collide_with_bits = getFile8Bit(file);
2422 level->use_spring_bug = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2423 level->use_step_counter = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2425 level->instant_relocation = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2426 level->can_pass_to_walkable = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2427 level->grow_into_diggable = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2429 level->game_engine_type = getFile8Bit(file);
2431 ReadUnusedBytesFromFile(file, LEVEL_CHUNK_HEAD_UNUSED);
2436 static int LoadLevel_NAME(File *file, int chunk_size, struct LevelInfo *level)
2440 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
2441 level->name[i] = getFile8Bit(file);
2442 level->name[MAX_LEVEL_NAME_LEN] = 0;
2447 static int LoadLevel_AUTH(File *file, int chunk_size, struct LevelInfo *level)
2451 for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
2452 level->author[i] = getFile8Bit(file);
2453 level->author[MAX_LEVEL_AUTHOR_LEN] = 0;
2458 static int LoadLevel_BODY(File *file, int chunk_size, struct LevelInfo *level)
2461 int chunk_size_expected = level->fieldx * level->fieldy;
2463 /* Note: "chunk_size" was wrong before version 2.0 when elements are
2464 stored with 16-bit encoding (and should be twice as big then).
2465 Even worse, playfield data was stored 16-bit when only yamyam content
2466 contained 16-bit elements and vice versa. */
2468 if (level->encoding_16bit_field && level->file_version >= FILE_VERSION_2_0)
2469 chunk_size_expected *= 2;
2471 if (chunk_size_expected != chunk_size)
2473 ReadUnusedBytesFromFile(file, chunk_size);
2474 return chunk_size_expected;
2477 for (y = 0; y < level->fieldy; y++)
2478 for (x = 0; x < level->fieldx; x++)
2479 level->field[x][y] =
2480 getMappedElement(level->encoding_16bit_field ? getFile16BitBE(file) :
2485 static int LoadLevel_CONT(File *file, int chunk_size, struct LevelInfo *level)
2488 int header_size = 4;
2489 int content_size = MAX_ELEMENT_CONTENTS * 3 * 3;
2490 int chunk_size_expected = header_size + content_size;
2492 /* Note: "chunk_size" was wrong before version 2.0 when elements are
2493 stored with 16-bit encoding (and should be twice as big then).
2494 Even worse, playfield data was stored 16-bit when only yamyam content
2495 contained 16-bit elements and vice versa. */
2497 if (level->encoding_16bit_field && level->file_version >= FILE_VERSION_2_0)
2498 chunk_size_expected += content_size;
2500 if (chunk_size_expected != chunk_size)
2502 ReadUnusedBytesFromFile(file, chunk_size);
2503 return chunk_size_expected;
2507 level->num_yamyam_contents = getFile8Bit(file);
2511 /* correct invalid number of content fields -- should never happen */
2512 if (level->num_yamyam_contents < 1 ||
2513 level->num_yamyam_contents > MAX_ELEMENT_CONTENTS)
2514 level->num_yamyam_contents = STD_ELEMENT_CONTENTS;
2516 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
2517 for (y = 0; y < 3; y++)
2518 for (x = 0; x < 3; x++)
2519 level->yamyam_content[i].e[x][y] =
2520 getMappedElement(level->encoding_16bit_field ?
2521 getFile16BitBE(file) : getFile8Bit(file));
2525 static int LoadLevel_CNT2(File *file, int chunk_size, struct LevelInfo *level)
2530 int content_array[MAX_ELEMENT_CONTENTS][3][3];
2532 element = getMappedElement(getFile16BitBE(file));
2533 num_contents = getFile8Bit(file);
2535 getFile8Bit(file); /* content x size (unused) */
2536 getFile8Bit(file); /* content y size (unused) */
2538 ReadUnusedBytesFromFile(file, LEVEL_CHUNK_CNT2_UNUSED);
2540 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
2541 for (y = 0; y < 3; y++)
2542 for (x = 0; x < 3; x++)
2543 content_array[i][x][y] = getMappedElement(getFile16BitBE(file));
2545 /* correct invalid number of content fields -- should never happen */
2546 if (num_contents < 1 || num_contents > MAX_ELEMENT_CONTENTS)
2547 num_contents = STD_ELEMENT_CONTENTS;
2549 if (element == EL_YAMYAM)
2551 level->num_yamyam_contents = num_contents;
2553 for (i = 0; i < num_contents; i++)
2554 for (y = 0; y < 3; y++)
2555 for (x = 0; x < 3; x++)
2556 level->yamyam_content[i].e[x][y] = content_array[i][x][y];
2558 else if (element == EL_BD_AMOEBA)
2560 level->amoeba_content = content_array[0][0][0];
2564 Error(ERR_WARN, "cannot load content for element '%d'", element);
2570 static int LoadLevel_CNT3(File *file, int chunk_size, struct LevelInfo *level)
2576 int chunk_size_expected;
2578 element = getMappedElement(getFile16BitBE(file));
2579 if (!IS_ENVELOPE(element))
2580 element = EL_ENVELOPE_1;
2582 envelope_nr = element - EL_ENVELOPE_1;
2584 envelope_len = getFile16BitBE(file);
2586 level->envelope[envelope_nr].xsize = getFile8Bit(file);
2587 level->envelope[envelope_nr].ysize = getFile8Bit(file);
2589 ReadUnusedBytesFromFile(file, LEVEL_CHUNK_CNT3_UNUSED);
2591 chunk_size_expected = LEVEL_CHUNK_CNT3_SIZE(envelope_len);
2592 if (chunk_size_expected != chunk_size)
2594 ReadUnusedBytesFromFile(file, chunk_size - LEVEL_CHUNK_CNT3_HEADER);
2595 return chunk_size_expected;
2598 for (i = 0; i < envelope_len; i++)
2599 level->envelope[envelope_nr].text[i] = getFile8Bit(file);
2604 static int LoadLevel_CUS1(File *file, int chunk_size, struct LevelInfo *level)
2606 int num_changed_custom_elements = getFile16BitBE(file);
2607 int chunk_size_expected = 2 + num_changed_custom_elements * 6;
2610 if (chunk_size_expected != chunk_size)
2612 ReadUnusedBytesFromFile(file, chunk_size - 2);
2613 return chunk_size_expected;
2616 for (i = 0; i < num_changed_custom_elements; i++)
2618 int element = getMappedElement(getFile16BitBE(file));
2619 int properties = getFile32BitBE(file);
2621 if (IS_CUSTOM_ELEMENT(element))
2622 element_info[element].properties[EP_BITFIELD_BASE_NR] = properties;
2624 Error(ERR_WARN, "invalid custom element number %d", element);
2626 /* older game versions that wrote level files with CUS1 chunks used
2627 different default push delay values (not yet stored in level file) */
2628 element_info[element].push_delay_fixed = 2;
2629 element_info[element].push_delay_random = 8;
2632 level->file_has_custom_elements = TRUE;
2637 static int LoadLevel_CUS2(File *file, int chunk_size, struct LevelInfo *level)
2639 int num_changed_custom_elements = getFile16BitBE(file);
2640 int chunk_size_expected = 2 + num_changed_custom_elements * 4;
2643 if (chunk_size_expected != chunk_size)
2645 ReadUnusedBytesFromFile(file, chunk_size - 2);
2646 return chunk_size_expected;
2649 for (i = 0; i < num_changed_custom_elements; i++)
2651 int element = getMappedElement(getFile16BitBE(file));
2652 int custom_target_element = getMappedElement(getFile16BitBE(file));
2654 if (IS_CUSTOM_ELEMENT(element))
2655 element_info[element].change->target_element = custom_target_element;
2657 Error(ERR_WARN, "invalid custom element number %d", element);
2660 level->file_has_custom_elements = TRUE;
2665 static int LoadLevel_CUS3(File *file, int chunk_size, struct LevelInfo *level)
2667 int num_changed_custom_elements = getFile16BitBE(file);
2668 int chunk_size_expected = LEVEL_CHUNK_CUS3_SIZE(num_changed_custom_elements);
2671 if (chunk_size_expected != chunk_size)
2673 ReadUnusedBytesFromFile(file, chunk_size - 2);
2674 return chunk_size_expected;
2677 for (i = 0; i < num_changed_custom_elements; i++)
2679 int element = getMappedElement(getFile16BitBE(file));
2680 struct ElementInfo *ei = &element_info[element];
2681 unsigned int event_bits;
2683 if (!IS_CUSTOM_ELEMENT(element))
2685 Error(ERR_WARN, "invalid custom element number %d", element);
2687 element = EL_INTERNAL_DUMMY;
2690 for (j = 0; j < MAX_ELEMENT_NAME_LEN; j++)
2691 ei->description[j] = getFile8Bit(file);
2692 ei->description[MAX_ELEMENT_NAME_LEN] = 0;
2694 ei->properties[EP_BITFIELD_BASE_NR] = getFile32BitBE(file);
2696 /* some free bytes for future properties and padding */
2697 ReadUnusedBytesFromFile(file, 7);
2699 ei->use_gfx_element = getFile8Bit(file);
2700 ei->gfx_element_initial = getMappedElement(getFile16BitBE(file));
2702 ei->collect_score_initial = getFile8Bit(file);
2703 ei->collect_count_initial = getFile8Bit(file);
2705 ei->push_delay_fixed = getFile16BitBE(file);
2706 ei->push_delay_random = getFile16BitBE(file);
2707 ei->move_delay_fixed = getFile16BitBE(file);
2708 ei->move_delay_random = getFile16BitBE(file);
2710 ei->move_pattern = getFile16BitBE(file);
2711 ei->move_direction_initial = getFile8Bit(file);
2712 ei->move_stepsize = getFile8Bit(file);
2714 for (y = 0; y < 3; y++)
2715 for (x = 0; x < 3; x++)
2716 ei->content.e[x][y] = getMappedElement(getFile16BitBE(file));
2718 event_bits = getFile32BitBE(file);
2719 for (j = 0; j < NUM_CHANGE_EVENTS; j++)
2720 if (event_bits & (1 << j))
2721 ei->change->has_event[j] = TRUE;
2723 ei->change->target_element = getMappedElement(getFile16BitBE(file));
2725 ei->change->delay_fixed = getFile16BitBE(file);
2726 ei->change->delay_random = getFile16BitBE(file);
2727 ei->change->delay_frames = getFile16BitBE(file);
2729 ei->change->initial_trigger_element= getMappedElement(getFile16BitBE(file));
2731 ei->change->explode = getFile8Bit(file);
2732 ei->change->use_target_content = getFile8Bit(file);
2733 ei->change->only_if_complete = getFile8Bit(file);
2734 ei->change->use_random_replace = getFile8Bit(file);
2736 ei->change->random_percentage = getFile8Bit(file);
2737 ei->change->replace_when = getFile8Bit(file);
2739 for (y = 0; y < 3; y++)
2740 for (x = 0; x < 3; x++)
2741 ei->change->target_content.e[x][y] =
2742 getMappedElement(getFile16BitBE(file));
2744 ei->slippery_type = getFile8Bit(file);
2746 /* some free bytes for future properties and padding */
2747 ReadUnusedBytesFromFile(file, LEVEL_CPART_CUS3_UNUSED);
2749 /* mark that this custom element has been modified */
2750 ei->modified_settings = TRUE;
2753 level->file_has_custom_elements = TRUE;
2758 static int LoadLevel_CUS4(File *file, int chunk_size, struct LevelInfo *level)
2760 struct ElementInfo *ei;
2761 int chunk_size_expected;
2765 /* ---------- custom element base property values (96 bytes) ------------- */
2767 element = getMappedElement(getFile16BitBE(file));
2769 if (!IS_CUSTOM_ELEMENT(element))
2771 Error(ERR_WARN, "invalid custom element number %d", element);
2773 ReadUnusedBytesFromFile(file, chunk_size - 2);
2777 ei = &element_info[element];
2779 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
2780 ei->description[i] = getFile8Bit(file);
2781 ei->description[MAX_ELEMENT_NAME_LEN] = 0;
2783 ei->properties[EP_BITFIELD_BASE_NR] = getFile32BitBE(file);
2785 ReadUnusedBytesFromFile(file, 4); /* reserved for more base properties */
2787 ei->num_change_pages = getFile8Bit(file);
2789 chunk_size_expected = LEVEL_CHUNK_CUS4_SIZE(ei->num_change_pages);
2790 if (chunk_size_expected != chunk_size)
2792 ReadUnusedBytesFromFile(file, chunk_size - 43);
2793 return chunk_size_expected;
2796 ei->ce_value_fixed_initial = getFile16BitBE(file);
2797 ei->ce_value_random_initial = getFile16BitBE(file);
2798 ei->use_last_ce_value = getFile8Bit(file);
2800 ei->use_gfx_element = getFile8Bit(file);
2801 ei->gfx_element_initial = getMappedElement(getFile16BitBE(file));
2803 ei->collect_score_initial = getFile8Bit(file);
2804 ei->collect_count_initial = getFile8Bit(file);
2806 ei->drop_delay_fixed = getFile8Bit(file);
2807 ei->push_delay_fixed = getFile8Bit(file);
2808 ei->drop_delay_random = getFile8Bit(file);
2809 ei->push_delay_random = getFile8Bit(file);
2810 ei->move_delay_fixed = getFile16BitBE(file);
2811 ei->move_delay_random = getFile16BitBE(file);
2813 /* bits 0 - 15 of "move_pattern" ... */
2814 ei->move_pattern = getFile16BitBE(file);
2815 ei->move_direction_initial = getFile8Bit(file);
2816 ei->move_stepsize = getFile8Bit(file);
2818 ei->slippery_type = getFile8Bit(file);
2820 for (y = 0; y < 3; y++)
2821 for (x = 0; x < 3; x++)
2822 ei->content.e[x][y] = getMappedElement(getFile16BitBE(file));
2824 ei->move_enter_element = getMappedElement(getFile16BitBE(file));
2825 ei->move_leave_element = getMappedElement(getFile16BitBE(file));
2826 ei->move_leave_type = getFile8Bit(file);
2828 /* ... bits 16 - 31 of "move_pattern" (not nice, but downward compatible) */
2829 ei->move_pattern |= (getFile16BitBE(file) << 16);
2831 ei->access_direction = getFile8Bit(file);
2833 ei->explosion_delay = getFile8Bit(file);
2834 ei->ignition_delay = getFile8Bit(file);
2835 ei->explosion_type = getFile8Bit(file);
2837 /* some free bytes for future custom property values and padding */
2838 ReadUnusedBytesFromFile(file, 1);
2840 /* ---------- change page property values (48 bytes) --------------------- */
2842 setElementChangePages(ei, ei->num_change_pages);
2844 for (i = 0; i < ei->num_change_pages; i++)
2846 struct ElementChangeInfo *change = &ei->change_page[i];
2847 unsigned int event_bits;
2849 /* always start with reliable default values */
2850 setElementChangeInfoToDefaults(change);
2852 /* bits 0 - 31 of "has_event[]" ... */
2853 event_bits = getFile32BitBE(file);
2854 for (j = 0; j < MIN(NUM_CHANGE_EVENTS, 32); j++)
2855 if (event_bits & (1 << j))
2856 change->has_event[j] = TRUE;
2858 change->target_element = getMappedElement(getFile16BitBE(file));
2860 change->delay_fixed = getFile16BitBE(file);
2861 change->delay_random = getFile16BitBE(file);
2862 change->delay_frames = getFile16BitBE(file);
2864 change->initial_trigger_element = getMappedElement(getFile16BitBE(file));
2866 change->explode = getFile8Bit(file);
2867 change->use_target_content = getFile8Bit(file);
2868 change->only_if_complete = getFile8Bit(file);
2869 change->use_random_replace = getFile8Bit(file);
2871 change->random_percentage = getFile8Bit(file);
2872 change->replace_when = getFile8Bit(file);
2874 for (y = 0; y < 3; y++)
2875 for (x = 0; x < 3; x++)
2876 change->target_content.e[x][y]= getMappedElement(getFile16BitBE(file));
2878 change->can_change = getFile8Bit(file);
2880 change->trigger_side = getFile8Bit(file);
2882 change->trigger_player = getFile8Bit(file);
2883 change->trigger_page = getFile8Bit(file);
2885 change->trigger_page = (change->trigger_page == CH_PAGE_ANY_FILE ?
2886 CH_PAGE_ANY : (1 << change->trigger_page));
2888 change->has_action = getFile8Bit(file);
2889 change->action_type = getFile8Bit(file);
2890 change->action_mode = getFile8Bit(file);
2891 change->action_arg = getFile16BitBE(file);
2893 /* ... bits 32 - 39 of "has_event[]" (not nice, but downward compatible) */
2894 event_bits = getFile8Bit(file);
2895 for (j = 32; j < NUM_CHANGE_EVENTS; j++)
2896 if (event_bits & (1 << (j - 32)))
2897 change->has_event[j] = TRUE;
2900 /* mark this custom element as modified */
2901 ei->modified_settings = TRUE;
2903 level->file_has_custom_elements = TRUE;
2908 static int LoadLevel_GRP1(File *file, int chunk_size, struct LevelInfo *level)
2910 struct ElementInfo *ei;
2911 struct ElementGroupInfo *group;
2915 element = getMappedElement(getFile16BitBE(file));
2917 if (!IS_GROUP_ELEMENT(element))
2919 Error(ERR_WARN, "invalid group element number %d", element);
2921 ReadUnusedBytesFromFile(file, chunk_size - 2);
2925 ei = &element_info[element];
2927 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
2928 ei->description[i] = getFile8Bit(file);
2929 ei->description[MAX_ELEMENT_NAME_LEN] = 0;
2931 group = element_info[element].group;
2933 group->num_elements = getFile8Bit(file);
2935 ei->use_gfx_element = getFile8Bit(file);
2936 ei->gfx_element_initial = getMappedElement(getFile16BitBE(file));
2938 group->choice_mode = getFile8Bit(file);
2940 /* some free bytes for future values and padding */
2941 ReadUnusedBytesFromFile(file, 3);
2943 for (i = 0; i < MAX_ELEMENTS_IN_GROUP; i++)
2944 group->element[i] = getMappedElement(getFile16BitBE(file));
2946 /* mark this group element as modified */
2947 element_info[element].modified_settings = TRUE;
2949 level->file_has_custom_elements = TRUE;
2954 static int LoadLevel_MicroChunk(File *file, struct LevelFileConfigInfo *conf,
2955 int element, int real_element)
2957 int micro_chunk_size = 0;
2958 int conf_type = getFile8Bit(file);
2959 int byte_mask = conf_type & CONF_MASK_BYTES;
2960 boolean element_found = FALSE;
2963 micro_chunk_size += 1;
2965 if (byte_mask == CONF_MASK_MULTI_BYTES)
2967 int num_bytes = getFile16BitBE(file);
2968 byte *buffer = checked_malloc(num_bytes);
2970 ReadBytesFromFile(file, buffer, num_bytes);
2972 for (i = 0; conf[i].data_type != -1; i++)
2974 if (conf[i].element == element &&
2975 conf[i].conf_type == conf_type)
2977 int data_type = conf[i].data_type;
2978 int num_entities = num_bytes / CONF_ENTITY_NUM_BYTES(data_type);
2979 int max_num_entities = conf[i].max_num_entities;
2981 if (num_entities > max_num_entities)
2984 "truncating number of entities for element %d from %d to %d",
2985 element, num_entities, max_num_entities);
2987 num_entities = max_num_entities;
2990 if (num_entities == 0 && (data_type == TYPE_ELEMENT_LIST ||
2991 data_type == TYPE_CONTENT_LIST))
2993 /* for element and content lists, zero entities are not allowed */
2994 Error(ERR_WARN, "found empty list of entities for element %d",
2997 /* do not set "num_entities" here to prevent reading behind buffer */
2999 *(int *)(conf[i].num_entities) = 1; /* at least one is required */
3003 *(int *)(conf[i].num_entities) = num_entities;
3006 element_found = TRUE;
3008 if (data_type == TYPE_STRING)
3010 char *string = (char *)(conf[i].value);
3013 for (j = 0; j < max_num_entities; j++)
3014 string[j] = (j < num_entities ? buffer[j] : '\0');
3016 else if (data_type == TYPE_ELEMENT_LIST)
3018 int *element_array = (int *)(conf[i].value);
3021 for (j = 0; j < num_entities; j++)
3023 getMappedElement(CONF_ELEMENTS_ELEMENT(buffer, j));
3025 else if (data_type == TYPE_CONTENT_LIST)
3027 struct Content *content= (struct Content *)(conf[i].value);
3030 for (c = 0; c < num_entities; c++)
3031 for (y = 0; y < 3; y++)
3032 for (x = 0; x < 3; x++)
3033 content[c].e[x][y] =
3034 getMappedElement(CONF_CONTENTS_ELEMENT(buffer, c, x, y));
3037 element_found = FALSE;
3043 checked_free(buffer);
3045 micro_chunk_size += 2 + num_bytes;
3047 else /* constant size configuration data (1, 2 or 4 bytes) */
3049 int value = (byte_mask == CONF_MASK_1_BYTE ? getFile8Bit (file) :
3050 byte_mask == CONF_MASK_2_BYTE ? getFile16BitBE(file) :
3051 byte_mask == CONF_MASK_4_BYTE ? getFile32BitBE(file) : 0);
3053 for (i = 0; conf[i].data_type != -1; i++)
3055 if (conf[i].element == element &&
3056 conf[i].conf_type == conf_type)
3058 int data_type = conf[i].data_type;
3060 if (data_type == TYPE_ELEMENT)
3061 value = getMappedElement(value);
3063 if (data_type == TYPE_BOOLEAN)
3064 *(boolean *)(conf[i].value) = value;
3066 *(int *) (conf[i].value) = value;
3068 element_found = TRUE;
3074 micro_chunk_size += CONF_VALUE_NUM_BYTES(byte_mask);
3079 char *error_conf_chunk_bytes =
3080 (byte_mask == CONF_MASK_1_BYTE ? "CONF_VALUE_8_BIT" :
3081 byte_mask == CONF_MASK_2_BYTE ? "CONF_VALUE_16_BIT" :
3082 byte_mask == CONF_MASK_4_BYTE ? "CONF_VALUE_32_BIT" :"CONF_VALUE_BYTES");
3083 int error_conf_chunk_token = conf_type & CONF_MASK_TOKEN;
3084 int error_element = real_element;
3086 Error(ERR_WARN, "cannot load micro chunk '%s(%d)' value for element %d ['%s']",
3087 error_conf_chunk_bytes, error_conf_chunk_token,
3088 error_element, EL_NAME(error_element));
3091 return micro_chunk_size;
3094 static int LoadLevel_INFO(File *file, int chunk_size, struct LevelInfo *level)
3096 int real_chunk_size = 0;
3098 li = *level; /* copy level data into temporary buffer */
3100 while (!checkEndOfFile(file))
3102 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_INFO, -1, -1);
3104 if (real_chunk_size >= chunk_size)
3108 *level = li; /* copy temporary buffer back to level data */
3110 return real_chunk_size;
3113 static int LoadLevel_CONF(File *file, int chunk_size, struct LevelInfo *level)
3115 int real_chunk_size = 0;
3117 li = *level; /* copy level data into temporary buffer */
3119 while (!checkEndOfFile(file))
3121 int element = getMappedElement(getFile16BitBE(file));
3123 real_chunk_size += 2;
3124 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_CONF,
3126 if (real_chunk_size >= chunk_size)
3130 *level = li; /* copy temporary buffer back to level data */
3132 return real_chunk_size;
3135 static int LoadLevel_ELEM(File *file, int chunk_size, struct LevelInfo *level)
3137 int real_chunk_size = 0;
3139 li = *level; /* copy level data into temporary buffer */
3141 while (!checkEndOfFile(file))
3143 int element = getMappedElement(getFile16BitBE(file));
3145 real_chunk_size += 2;
3146 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_ELEM,
3148 if (real_chunk_size >= chunk_size)
3152 *level = li; /* copy temporary buffer back to level data */
3154 return real_chunk_size;
3157 static int LoadLevel_NOTE(File *file, int chunk_size, struct LevelInfo *level)
3159 int element = getMappedElement(getFile16BitBE(file));
3160 int envelope_nr = element - EL_ENVELOPE_1;
3161 int real_chunk_size = 2;
3163 xx_envelope = level->envelope[envelope_nr]; /* copy into temporary buffer */
3165 while (!checkEndOfFile(file))
3167 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_NOTE,
3170 if (real_chunk_size >= chunk_size)
3174 level->envelope[envelope_nr] = xx_envelope; /* copy from temporary buffer */
3176 return real_chunk_size;
3179 static int LoadLevel_CUSX(File *file, int chunk_size, struct LevelInfo *level)
3181 int element = getMappedElement(getFile16BitBE(file));
3182 int real_chunk_size = 2;
3183 struct ElementInfo *ei = &element_info[element];
3186 xx_ei = *ei; /* copy element data into temporary buffer */
3188 xx_ei.num_change_pages = -1;
3190 while (!checkEndOfFile(file))
3192 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_CUSX_base,
3194 if (xx_ei.num_change_pages != -1)
3197 if (real_chunk_size >= chunk_size)
3203 if (ei->num_change_pages == -1)
3205 Error(ERR_WARN, "LoadLevel_CUSX(): missing 'num_change_pages' for '%s'",
3208 ei->num_change_pages = 1;
3210 setElementChangePages(ei, 1);
3211 setElementChangeInfoToDefaults(ei->change);
3213 return real_chunk_size;
3216 /* initialize number of change pages stored for this custom element */
3217 setElementChangePages(ei, ei->num_change_pages);
3218 for (i = 0; i < ei->num_change_pages; i++)
3219 setElementChangeInfoToDefaults(&ei->change_page[i]);
3221 /* start with reading properties for the first change page */
3222 xx_current_change_page = 0;
3224 while (!checkEndOfFile(file))
3226 struct ElementChangeInfo *change = &ei->change_page[xx_current_change_page];
3228 xx_change = *change; /* copy change data into temporary buffer */
3230 resetEventBits(); /* reset bits; change page might have changed */
3232 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_CUSX_change,
3235 *change = xx_change;
3237 setEventFlagsFromEventBits(change);
3239 if (real_chunk_size >= chunk_size)
3243 level->file_has_custom_elements = TRUE;
3245 return real_chunk_size;
3248 static int LoadLevel_GRPX(File *file, int chunk_size, struct LevelInfo *level)
3250 int element = getMappedElement(getFile16BitBE(file));
3251 int real_chunk_size = 2;
3252 struct ElementInfo *ei = &element_info[element];
3253 struct ElementGroupInfo *group = ei->group;
3255 xx_ei = *ei; /* copy element data into temporary buffer */
3256 xx_group = *group; /* copy group data into temporary buffer */
3258 while (!checkEndOfFile(file))
3260 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_GRPX,
3263 if (real_chunk_size >= chunk_size)
3270 level->file_has_custom_elements = TRUE;
3272 return real_chunk_size;
3275 static void LoadLevelFromFileInfo_RND(struct LevelInfo *level,
3276 struct LevelFileInfo *level_file_info,
3277 boolean level_info_only)
3279 char *filename = level_file_info->filename;
3280 char cookie[MAX_LINE_LEN];
3281 char chunk_name[CHUNK_ID_LEN + 1];
3285 if (!(file = openFile(filename, MODE_READ)))
3287 level->no_valid_file = TRUE;
3288 level->no_level_file = TRUE;
3290 if (level_info_only)
3293 Error(ERR_WARN, "cannot read level '%s' -- using empty level", filename);
3295 if (!setup.editor.use_template_for_new_levels)
3298 /* if level file not found, try to initialize level data from template */
3299 filename = getGlobalLevelTemplateFilename();
3301 if (!(file = openFile(filename, MODE_READ)))
3304 /* default: for empty levels, use level template for custom elements */
3305 level->use_custom_template = TRUE;
3307 level->no_valid_file = FALSE;
3310 getFileChunkBE(file, chunk_name, NULL);
3311 if (strEqual(chunk_name, "RND1"))
3313 getFile32BitBE(file); /* not used */
3315 getFileChunkBE(file, chunk_name, NULL);
3316 if (!strEqual(chunk_name, "CAVE"))
3318 level->no_valid_file = TRUE;
3320 Error(ERR_WARN, "unknown format of level file '%s'", filename);
3327 else /* check for pre-2.0 file format with cookie string */
3329 strcpy(cookie, chunk_name);
3330 if (getStringFromFile(file, &cookie[4], MAX_LINE_LEN - 4) == NULL)
3332 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
3333 cookie[strlen(cookie) - 1] = '\0';
3335 if (!checkCookieString(cookie, LEVEL_COOKIE_TMPL))
3337 level->no_valid_file = TRUE;
3339 Error(ERR_WARN, "unknown format of level file '%s'", filename);
3346 if ((level->file_version = getFileVersionFromCookieString(cookie)) == -1)
3348 level->no_valid_file = TRUE;
3350 Error(ERR_WARN, "unsupported version of level file '%s'", filename);
3357 /* pre-2.0 level files have no game version, so use file version here */
3358 level->game_version = level->file_version;
3361 if (level->file_version < FILE_VERSION_1_2)
3363 /* level files from versions before 1.2.0 without chunk structure */
3364 LoadLevel_HEAD(file, LEVEL_CHUNK_HEAD_SIZE, level);
3365 LoadLevel_BODY(file, level->fieldx * level->fieldy, level);
3373 int (*loader)(File *, int, struct LevelInfo *);
3377 { "VERS", LEVEL_CHUNK_VERS_SIZE, LoadLevel_VERS },
3378 { "DATE", LEVEL_CHUNK_DATE_SIZE, LoadLevel_DATE },
3379 { "HEAD", LEVEL_CHUNK_HEAD_SIZE, LoadLevel_HEAD },
3380 { "NAME", LEVEL_CHUNK_NAME_SIZE, LoadLevel_NAME },
3381 { "AUTH", LEVEL_CHUNK_AUTH_SIZE, LoadLevel_AUTH },
3382 { "INFO", -1, LoadLevel_INFO },
3383 { "BODY", -1, LoadLevel_BODY },
3384 { "CONT", -1, LoadLevel_CONT },
3385 { "CNT2", LEVEL_CHUNK_CNT2_SIZE, LoadLevel_CNT2 },
3386 { "CNT3", -1, LoadLevel_CNT3 },
3387 { "CUS1", -1, LoadLevel_CUS1 },
3388 { "CUS2", -1, LoadLevel_CUS2 },
3389 { "CUS3", -1, LoadLevel_CUS3 },
3390 { "CUS4", -1, LoadLevel_CUS4 },
3391 { "GRP1", -1, LoadLevel_GRP1 },
3392 { "CONF", -1, LoadLevel_CONF },
3393 { "ELEM", -1, LoadLevel_ELEM },
3394 { "NOTE", -1, LoadLevel_NOTE },
3395 { "CUSX", -1, LoadLevel_CUSX },
3396 { "GRPX", -1, LoadLevel_GRPX },
3401 while (getFileChunkBE(file, chunk_name, &chunk_size))
3405 while (chunk_info[i].name != NULL &&
3406 !strEqual(chunk_name, chunk_info[i].name))
3409 if (chunk_info[i].name == NULL)
3411 Error(ERR_WARN, "unknown chunk '%s' in level file '%s'",
3412 chunk_name, filename);
3413 ReadUnusedBytesFromFile(file, chunk_size);
3415 else if (chunk_info[i].size != -1 &&
3416 chunk_info[i].size != chunk_size)
3418 Error(ERR_WARN, "wrong size (%d) of chunk '%s' in level file '%s'",
3419 chunk_size, chunk_name, filename);
3420 ReadUnusedBytesFromFile(file, chunk_size);
3424 /* call function to load this level chunk */
3425 int chunk_size_expected =
3426 (chunk_info[i].loader)(file, chunk_size, level);
3428 /* the size of some chunks cannot be checked before reading other
3429 chunks first (like "HEAD" and "BODY") that contain some header
3430 information, so check them here */
3431 if (chunk_size_expected != chunk_size)
3433 Error(ERR_WARN, "wrong size (%d) of chunk '%s' in level file '%s'",
3434 chunk_size, chunk_name, filename);
3444 /* ------------------------------------------------------------------------- */
3445 /* functions for loading EM level */
3446 /* ------------------------------------------------------------------------- */
3448 void CopyNativeLevel_RND_to_EM(struct LevelInfo *level)
3450 static int ball_xy[8][2] =
3461 struct LevelInfo_EM *level_em = level->native_em_level;
3462 struct LEVEL *lev = level_em->lev;
3463 struct PLAYER **ply = level_em->ply;
3466 lev->width = MIN(level->fieldx, EM_MAX_CAVE_WIDTH);
3467 lev->height = MIN(level->fieldy, EM_MAX_CAVE_HEIGHT);
3469 lev->time_seconds = level->time;
3470 lev->required_initial = level->gems_needed;
3472 lev->emerald_score = level->score[SC_EMERALD];
3473 lev->diamond_score = level->score[SC_DIAMOND];
3474 lev->alien_score = level->score[SC_ROBOT];
3475 lev->tank_score = level->score[SC_SPACESHIP];
3476 lev->bug_score = level->score[SC_BUG];
3477 lev->eater_score = level->score[SC_YAMYAM];
3478 lev->nut_score = level->score[SC_NUT];
3479 lev->dynamite_score = level->score[SC_DYNAMITE];
3480 lev->key_score = level->score[SC_KEY];
3481 lev->exit_score = level->score[SC_TIME_BONUS];
3483 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
3484 for (y = 0; y < 3; y++)
3485 for (x = 0; x < 3; x++)
3486 lev->eater_array[i][y * 3 + x] =
3487 map_element_RND_to_EM(level->yamyam_content[i].e[x][y]);
3489 lev->amoeba_time = level->amoeba_speed;
3490 lev->wonderwall_time_initial = level->time_magic_wall;
3491 lev->wheel_time = level->time_wheel;
3493 lev->android_move_time = level->android_move_time;
3494 lev->android_clone_time = level->android_clone_time;
3495 lev->ball_random = level->ball_random;
3496 lev->ball_state_initial = level->ball_state_initial;
3497 lev->ball_time = level->ball_time;
3498 lev->num_ball_arrays = level->num_ball_contents;
3500 lev->lenses_score = level->lenses_score;
3501 lev->magnify_score = level->magnify_score;
3502 lev->slurp_score = level->slurp_score;
3504 lev->lenses_time = level->lenses_time;
3505 lev->magnify_time = level->magnify_time;
3507 lev->wind_direction_initial =
3508 map_direction_RND_to_EM(level->wind_direction_initial);
3509 lev->wind_cnt_initial = (level->wind_direction_initial != MV_NONE ?
3510 lev->wind_time : 0);
3512 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
3513 for (j = 0; j < 8; j++)
3514 lev->ball_array[i][j] =
3515 map_element_RND_to_EM(level->
3516 ball_content[i].e[ball_xy[j][0]][ball_xy[j][1]]);
3518 map_android_clone_elements_RND_to_EM(level);
3520 /* first fill the complete playfield with the default border element */
3521 for (y = 0; y < EM_MAX_CAVE_HEIGHT; y++)
3522 for (x = 0; x < EM_MAX_CAVE_WIDTH; x++)
3523 level_em->cave[x][y] = ZBORDER;
3525 if (BorderElement == EL_STEELWALL)
3527 for (y = 0; y < lev->height + 2; y++)
3528 for (x = 0; x < lev->width + 2; x++)
3529 level_em->cave[x + 1][y + 1] = map_element_RND_to_EM(EL_STEELWALL);
3532 /* then copy the real level contents from level file into the playfield */
3533 for (y = 0; y < lev->height; y++) for (x = 0; x < lev->width; x++)
3535 int new_element = map_element_RND_to_EM(level->field[x][y]);
3536 int offset = (BorderElement == EL_STEELWALL ? 1 : 0);
3537 int xx = x + 1 + offset;
3538 int yy = y + 1 + offset;
3540 if (level->field[x][y] == EL_AMOEBA_DEAD)
3541 new_element = map_element_RND_to_EM(EL_AMOEBA_WET);
3543 level_em->cave[xx][yy] = new_element;
3546 for (i = 0; i < MAX_PLAYERS; i++)
3548 ply[i]->x_initial = 0;
3549 ply[i]->y_initial = 0;
3552 /* initialize player positions and delete players from the playfield */
3553 for (y = 0; y < lev->height; y++) for (x = 0; x < lev->width; x++)
3555 if (ELEM_IS_PLAYER(level->field[x][y]))
3557 int player_nr = GET_PLAYER_NR(level->field[x][y]);
3558 int offset = (BorderElement == EL_STEELWALL ? 1 : 0);
3559 int xx = x + 1 + offset;
3560 int yy = y + 1 + offset;
3562 ply[player_nr]->x_initial = xx;
3563 ply[player_nr]->y_initial = yy;
3565 level_em->cave[xx][yy] = map_element_RND_to_EM(EL_EMPTY);
3569 if (BorderElement == EL_STEELWALL)
3576 void CopyNativeLevel_EM_to_RND(struct LevelInfo *level)
3578 static int ball_xy[8][2] =
3589 struct LevelInfo_EM *level_em = level->native_em_level;
3590 struct LEVEL *lev = level_em->lev;
3591 struct PLAYER **ply = level_em->ply;
3594 level->fieldx = MIN(lev->width, MAX_LEV_FIELDX);
3595 level->fieldy = MIN(lev->height, MAX_LEV_FIELDY);
3597 level->time = lev->time_seconds;
3598 level->gems_needed = lev->required_initial;
3600 sprintf(level->name, "Level %d", level->file_info.nr);
3602 level->score[SC_EMERALD] = lev->emerald_score;
3603 level->score[SC_DIAMOND] = lev->diamond_score;
3604 level->score[SC_ROBOT] = lev->alien_score;
3605 level->score[SC_SPACESHIP] = lev->tank_score;
3606 level->score[SC_BUG] = lev->bug_score;
3607 level->score[SC_YAMYAM] = lev->eater_score;
3608 level->score[SC_NUT] = lev->nut_score;
3609 level->score[SC_DYNAMITE] = lev->dynamite_score;
3610 level->score[SC_KEY] = lev->key_score;
3611 level->score[SC_TIME_BONUS] = lev->exit_score;
3613 level->num_yamyam_contents = MAX_ELEMENT_CONTENTS;
3615 for (i = 0; i < level->num_yamyam_contents; i++)
3616 for (y = 0; y < 3; y++)
3617 for (x = 0; x < 3; x++)
3618 level->yamyam_content[i].e[x][y] =
3619 map_element_EM_to_RND(lev->eater_array[i][y * 3 + x]);
3621 level->amoeba_speed = lev->amoeba_time;
3622 level->time_magic_wall = lev->wonderwall_time_initial;
3623 level->time_wheel = lev->wheel_time;
3625 level->android_move_time = lev->android_move_time;
3626 level->android_clone_time = lev->android_clone_time;
3627 level->ball_random = lev->ball_random;
3628 level->ball_state_initial = lev->ball_state_initial;
3629 level->ball_time = lev->ball_time;
3630 level->num_ball_contents = lev->num_ball_arrays;
3632 level->lenses_score = lev->lenses_score;
3633 level->magnify_score = lev->magnify_score;
3634 level->slurp_score = lev->slurp_score;
3636 level->lenses_time = lev->lenses_time;
3637 level->magnify_time = lev->magnify_time;
3639 level->wind_direction_initial =
3640 map_direction_EM_to_RND(lev->wind_direction_initial);
3642 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
3643 for (j = 0; j < 8; j++)
3644 level->ball_content[i].e[ball_xy[j][0]][ball_xy[j][1]] =
3645 map_element_EM_to_RND(lev->ball_array[i][j]);
3647 map_android_clone_elements_EM_to_RND(level);
3649 /* convert the playfield (some elements need special treatment) */
3650 for (y = 0; y < level->fieldy; y++) for (x = 0; x < level->fieldx; x++)
3652 int new_element = map_element_EM_to_RND(level_em->cave[x + 1][y + 1]);
3654 if (new_element == EL_AMOEBA_WET && level->amoeba_speed == 0)
3655 new_element = EL_AMOEBA_DEAD;
3657 level->field[x][y] = new_element;
3660 for (i = 0; i < MAX_PLAYERS; i++)
3662 /* in case of all players set to the same field, use the first player */
3663 int nr = MAX_PLAYERS - i - 1;
3664 int jx = ply[nr]->x_initial - 1;
3665 int jy = ply[nr]->y_initial - 1;
3667 if (jx != -1 && jy != -1)
3668 level->field[jx][jy] = EL_PLAYER_1 + nr;
3673 /* ------------------------------------------------------------------------- */
3674 /* functions for loading SP level */
3675 /* ------------------------------------------------------------------------- */
3677 void CopyNativeLevel_RND_to_SP(struct LevelInfo *level)
3679 struct LevelInfo_SP *level_sp = level->native_sp_level;
3680 LevelInfoType *header = &level_sp->header;
3683 level_sp->width = level->fieldx;
3684 level_sp->height = level->fieldy;
3686 for (x = 0; x < level->fieldx; x++)
3687 for (y = 0; y < level->fieldy; y++)
3688 level_sp->playfield[x][y] = map_element_RND_to_SP(level->field[x][y]);
3690 header->InitialGravity = (level->initial_player_gravity[0] ? 1 : 0);
3692 for (i = 0; i < SP_LEVEL_NAME_LEN; i++)
3693 header->LevelTitle[i] = level->name[i];
3694 /* !!! NO STRING TERMINATION IN SUPAPLEX VB CODE YET -- FIX THIS !!! */
3696 header->InfotronsNeeded = level->gems_needed;
3698 header->SpecialPortCount = 0;
3700 for (x = 0; x < level->fieldx; x++) for (y = 0; y < level->fieldy; y++)
3702 boolean gravity_port_found = FALSE;
3703 boolean gravity_port_valid = FALSE;
3704 int gravity_port_flag;
3705 int gravity_port_base_element;
3706 int element = level->field[x][y];
3708 if (element >= EL_SP_GRAVITY_ON_PORT_RIGHT &&
3709 element <= EL_SP_GRAVITY_ON_PORT_UP)
3711 gravity_port_found = TRUE;
3712 gravity_port_valid = TRUE;
3713 gravity_port_flag = 1;
3714 gravity_port_base_element = EL_SP_GRAVITY_ON_PORT_RIGHT;
3716 else if (element >= EL_SP_GRAVITY_OFF_PORT_RIGHT &&
3717 element <= EL_SP_GRAVITY_OFF_PORT_UP)
3719 gravity_port_found = TRUE;
3720 gravity_port_valid = TRUE;
3721 gravity_port_flag = 0;
3722 gravity_port_base_element = EL_SP_GRAVITY_OFF_PORT_RIGHT;
3724 else if (element >= EL_SP_GRAVITY_PORT_RIGHT &&
3725 element <= EL_SP_GRAVITY_PORT_UP)
3727 /* change R'n'D style gravity inverting special port to normal port
3728 (there are no gravity inverting ports in native Supaplex engine) */
3730 gravity_port_found = TRUE;
3731 gravity_port_valid = FALSE;
3732 gravity_port_base_element = EL_SP_GRAVITY_PORT_RIGHT;
3735 if (gravity_port_found)
3737 if (gravity_port_valid &&
3738 header->SpecialPortCount < SP_MAX_SPECIAL_PORTS)
3740 SpecialPortType *port = &header->SpecialPort[header->SpecialPortCount];
3742 port->PortLocation = (y * level->fieldx + x) * 2;
3743 port->Gravity = gravity_port_flag;
3745 element += EL_SP_GRAVITY_PORT_RIGHT - gravity_port_base_element;
3747 header->SpecialPortCount++;
3751 /* change special gravity port to normal port */
3753 element += EL_SP_PORT_RIGHT - gravity_port_base_element;
3756 level_sp->playfield[x][y] = element - EL_SP_START;
3761 void CopyNativeLevel_SP_to_RND(struct LevelInfo *level)
3763 struct LevelInfo_SP *level_sp = level->native_sp_level;
3764 LevelInfoType *header = &level_sp->header;
3765 boolean num_invalid_elements = 0;
3768 level->fieldx = level_sp->width;
3769 level->fieldy = level_sp->height;
3771 for (x = 0; x < level->fieldx; x++)
3773 for (y = 0; y < level->fieldy; y++)
3775 int element_old = level_sp->playfield[x][y];
3776 int element_new = getMappedElement(map_element_SP_to_RND(element_old));
3778 if (element_new == EL_UNKNOWN)
3780 num_invalid_elements++;
3782 Error(ERR_DEBUG, "invalid element %d at position %d, %d",
3786 level->field[x][y] = element_new;
3790 if (num_invalid_elements > 0)
3791 Error(ERR_WARN, "found %d invalid elements%s", num_invalid_elements,
3792 (!options.debug ? " (use '--debug' for more details)" : ""));
3794 for (i = 0; i < MAX_PLAYERS; i++)
3795 level->initial_player_gravity[i] =
3796 (header->InitialGravity == 1 ? TRUE : FALSE);
3798 /* skip leading spaces */
3799 for (i = 0; i < SP_LEVEL_NAME_LEN; i++)
3800 if (header->LevelTitle[i] != ' ')
3803 /* copy level title */
3804 for (j = 0; i < SP_LEVEL_NAME_LEN; i++, j++)
3805 level->name[j] = header->LevelTitle[i];
3806 level->name[j] = '\0';
3808 /* cut trailing spaces */
3810 if (level->name[j - 1] == ' ' && level->name[j] == '\0')
3811 level->name[j - 1] = '\0';
3813 level->gems_needed = header->InfotronsNeeded;
3815 for (i = 0; i < header->SpecialPortCount; i++)
3817 SpecialPortType *port = &header->SpecialPort[i];
3818 int port_location = port->PortLocation;
3819 int gravity = port->Gravity;
3820 int port_x, port_y, port_element;
3822 port_x = (port_location / 2) % level->fieldx;
3823 port_y = (port_location / 2) / level->fieldx;
3825 if (port_x < 0 || port_x >= level->fieldx ||
3826 port_y < 0 || port_y >= level->fieldy)
3828 Error(ERR_WARN, "special port position (%d, %d) out of bounds",
3834 port_element = level->field[port_x][port_y];
3836 if (port_element < EL_SP_GRAVITY_PORT_RIGHT ||
3837 port_element > EL_SP_GRAVITY_PORT_UP)
3839 Error(ERR_WARN, "no special port at position (%d, %d)", port_x, port_y);
3844 /* change previous (wrong) gravity inverting special port to either
3845 gravity enabling special port or gravity disabling special port */
3846 level->field[port_x][port_y] +=
3847 (gravity == 1 ? EL_SP_GRAVITY_ON_PORT_RIGHT :
3848 EL_SP_GRAVITY_OFF_PORT_RIGHT) - EL_SP_GRAVITY_PORT_RIGHT;
3851 /* change special gravity ports without database entries to normal ports */
3852 for (x = 0; x < level->fieldx; x++)
3853 for (y = 0; y < level->fieldy; y++)
3854 if (level->field[x][y] >= EL_SP_GRAVITY_PORT_RIGHT &&
3855 level->field[x][y] <= EL_SP_GRAVITY_PORT_UP)
3856 level->field[x][y] += EL_SP_PORT_RIGHT - EL_SP_GRAVITY_PORT_RIGHT;
3858 level->time = 0; /* no time limit */
3859 level->amoeba_speed = 0;
3860 level->time_magic_wall = 0;
3861 level->time_wheel = 0;
3862 level->amoeba_content = EL_EMPTY;
3865 /* original Supaplex does not use score values -- use default values */
3867 for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
3868 level->score[i] = 0;
3871 /* there are no yamyams in supaplex levels */
3872 for (i = 0; i < level->num_yamyam_contents; i++)
3873 for (x = 0; x < 3; x++)
3874 for (y = 0; y < 3; y++)
3875 level->yamyam_content[i].e[x][y] = EL_EMPTY;
3878 static void CopyNativeTape_RND_to_SP(struct LevelInfo *level)
3880 struct LevelInfo_SP *level_sp = level->native_sp_level;
3881 struct DemoInfo_SP *demo = &level_sp->demo;
3884 /* always start with reliable default values */
3885 demo->is_available = FALSE;
3888 if (TAPE_IS_EMPTY(tape))
3891 demo->level_nr = tape.level_nr; /* (currently not used) */
3893 level_sp->header.DemoRandomSeed = tape.random_seed;
3897 for (i = 0; i < tape.length; i++)
3899 int demo_action = map_key_RND_to_SP(tape.pos[i].action[0]);
3900 int demo_repeat = tape.pos[i].delay;
3901 int demo_entries = (demo_repeat + 15) / 16;
3903 if (demo->length + demo_entries >= SP_MAX_TAPE_LEN)
3905 Error(ERR_WARN, "tape truncated: size exceeds maximum SP demo size %d",
3911 for (j = 0; j < demo_repeat / 16; j++)
3912 demo->data[demo->length++] = 0xf0 | demo_action;
3914 if (demo_repeat % 16)
3915 demo->data[demo->length++] = ((demo_repeat % 16 - 1) << 4) | demo_action;
3918 demo->is_available = TRUE;
3921 static void setTapeInfoToDefaults();
3923 static void CopyNativeTape_SP_to_RND(struct LevelInfo *level)
3925 struct LevelInfo_SP *level_sp = level->native_sp_level;
3926 struct DemoInfo_SP *demo = &level_sp->demo;
3927 char *filename = level->file_info.filename;
3930 /* always start with reliable default values */
3931 setTapeInfoToDefaults();
3933 if (!demo->is_available)
3936 tape.level_nr = demo->level_nr; /* (currently not used) */
3937 tape.random_seed = level_sp->header.DemoRandomSeed;
3939 TapeSetDateFromEpochSeconds(getFileTimestampEpochSeconds(filename));
3942 tape.pos[tape.counter].delay = 0;
3944 for (i = 0; i < demo->length; i++)
3946 int demo_action = demo->data[i] & 0x0f;
3947 int demo_repeat = (demo->data[i] & 0xf0) >> 4;
3948 int tape_action = map_key_SP_to_RND(demo_action);
3949 int tape_repeat = demo_repeat + 1;
3950 byte action[MAX_PLAYERS] = { tape_action, 0, 0, 0 };
3951 boolean success = 0;
3954 for (j = 0; j < tape_repeat; j++)
3955 success = TapeAddAction(action);
3959 Error(ERR_WARN, "SP demo truncated: size exceeds maximum tape size %d",
3966 TapeHaltRecording();
3970 /* ------------------------------------------------------------------------- */
3971 /* functions for loading MM level */
3972 /* ------------------------------------------------------------------------- */
3974 void CopyNativeLevel_RND_to_MM(struct LevelInfo *level)
3976 struct LevelInfo_MM *level_mm = level->native_mm_level;
3979 level_mm->fieldx = MIN(level->fieldx, MM_MAX_PLAYFIELD_WIDTH);
3980 level_mm->fieldy = MIN(level->fieldy, MM_MAX_PLAYFIELD_HEIGHT);
3982 level_mm->time = level->time;
3983 level_mm->kettles_needed = level->gems_needed;
3984 level_mm->auto_count_kettles = level->auto_count_gems;
3986 level_mm->laser_red = level->mm_laser_red;
3987 level_mm->laser_green = level->mm_laser_green;
3988 level_mm->laser_blue = level->mm_laser_blue;
3990 strcpy(level_mm->name, level->name);
3991 strcpy(level_mm->author, level->author);
3993 level_mm->score[SC_PACMAN] = level->score[SC_PACMAN];
3994 level_mm->score[SC_KEY] = level->score[SC_KEY];
3995 level_mm->score[SC_TIME_BONUS] = level->score[SC_TIME_BONUS];
3997 level_mm->amoeba_speed = level->amoeba_speed;
3998 level_mm->time_fuse = level->mm_time_fuse;
4000 for (x = 0; x < level->fieldx; x++)
4001 for (y = 0; y < level->fieldy; y++)
4003 level_mm->field[x][y] = map_element_RND_to_MM(level->field[x][y]);
4006 void CopyNativeLevel_MM_to_RND(struct LevelInfo *level)
4008 struct LevelInfo_MM *level_mm = level->native_mm_level;
4011 level->fieldx = MIN(level_mm->fieldx, MAX_LEV_FIELDX);
4012 level->fieldy = MIN(level_mm->fieldy, MAX_LEV_FIELDY);
4014 level->time = level_mm->time;
4015 level->gems_needed = level_mm->kettles_needed;
4016 level->auto_count_gems = level_mm->auto_count_kettles;
4018 level->mm_laser_red = level_mm->laser_red;
4019 level->mm_laser_green = level_mm->laser_green;
4020 level->mm_laser_blue = level_mm->laser_blue;
4022 strcpy(level->name, level_mm->name);
4024 /* only overwrite author from 'levelinfo.conf' if author defined in level */
4025 if (!strEqual(level_mm->author, ANONYMOUS_NAME))
4026 strcpy(level->author, level_mm->author);
4028 level->score[SC_PACMAN] = level_mm->score[SC_PACMAN];
4029 level->score[SC_KEY] = level_mm->score[SC_KEY];
4030 level->score[SC_TIME_BONUS] = level_mm->score[SC_TIME_BONUS];
4032 level->amoeba_speed = level_mm->amoeba_speed;
4033 level->mm_time_fuse = level_mm->time_fuse;
4035 for (x = 0; x < level->fieldx; x++)
4036 for (y = 0; y < level->fieldy; y++)
4037 level->field[x][y] = map_element_MM_to_RND(level_mm->field[x][y]);
4041 /* ------------------------------------------------------------------------- */
4042 /* functions for loading DC level */
4043 /* ------------------------------------------------------------------------- */
4045 #define DC_LEVEL_HEADER_SIZE 344
4047 unsigned short getDecodedWord_DC(unsigned short data_encoded, boolean init)
4049 static int last_data_encoded;
4053 int diff_hi, diff_lo;
4054 int data_hi, data_lo;
4055 unsigned short data_decoded;
4059 last_data_encoded = 0;
4066 diff = data_encoded - last_data_encoded;
4067 diff_hi = diff & ~0xff;
4068 diff_lo = diff & 0xff;
4072 data_hi = diff_hi - (offset1 << 8) + (offset2 & 0xff00);
4073 data_lo = (diff_lo + (data_hi >> 16)) & 0x00ff;
4074 data_hi = data_hi & 0xff00;
4076 data_decoded = data_hi | data_lo;
4078 last_data_encoded = data_encoded;
4080 offset1 = (offset1 + 1) % 31;
4081 offset2 = offset2 & 0xff;
4083 return data_decoded;
4086 int getMappedElement_DC(int element)
4094 /* 0x0117 - 0x036e: (?) */
4097 /* 0x042d - 0x0684: (?) */
4113 element = EL_CRYSTAL;
4116 case 0x0e77: /* quicksand (boulder) */
4117 element = EL_QUICKSAND_FAST_FULL;
4120 case 0x0e99: /* slow quicksand (boulder) */
4121 element = EL_QUICKSAND_FULL;
4125 element = EL_EM_EXIT_OPEN;
4129 element = EL_EM_EXIT_CLOSED;
4133 element = EL_EM_STEEL_EXIT_OPEN;
4137 element = EL_EM_STEEL_EXIT_CLOSED;
4140 case 0x0f4f: /* dynamite (lit 1) */
4141 element = EL_EM_DYNAMITE_ACTIVE;
4144 case 0x0f57: /* dynamite (lit 2) */
4145 element = EL_EM_DYNAMITE_ACTIVE;
4148 case 0x0f5f: /* dynamite (lit 3) */
4149 element = EL_EM_DYNAMITE_ACTIVE;
4152 case 0x0f67: /* dynamite (lit 4) */
4153 element = EL_EM_DYNAMITE_ACTIVE;
4160 element = EL_AMOEBA_WET;
4164 element = EL_AMOEBA_DROP;
4168 element = EL_DC_MAGIC_WALL;
4172 element = EL_SPACESHIP_UP;
4176 element = EL_SPACESHIP_DOWN;
4180 element = EL_SPACESHIP_LEFT;
4184 element = EL_SPACESHIP_RIGHT;
4188 element = EL_BUG_UP;
4192 element = EL_BUG_DOWN;
4196 element = EL_BUG_LEFT;
4200 element = EL_BUG_RIGHT;
4204 element = EL_MOLE_UP;
4208 element = EL_MOLE_DOWN;
4212 element = EL_MOLE_LEFT;
4216 element = EL_MOLE_RIGHT;
4224 element = EL_YAMYAM;
4228 element = EL_SWITCHGATE_OPEN;
4232 element = EL_SWITCHGATE_CLOSED;
4236 element = EL_DC_SWITCHGATE_SWITCH_UP;
4240 element = EL_TIMEGATE_CLOSED;
4243 case 0x144c: /* conveyor belt switch (green) */
4244 element = EL_CONVEYOR_BELT_3_SWITCH_MIDDLE;
4247 case 0x144f: /* conveyor belt switch (red) */
4248 element = EL_CONVEYOR_BELT_1_SWITCH_MIDDLE;
4251 case 0x1452: /* conveyor belt switch (blue) */
4252 element = EL_CONVEYOR_BELT_4_SWITCH_MIDDLE;
4256 element = EL_CONVEYOR_BELT_3_MIDDLE;
4260 element = EL_CONVEYOR_BELT_3_LEFT;
4264 element = EL_CONVEYOR_BELT_3_RIGHT;
4268 element = EL_CONVEYOR_BELT_1_MIDDLE;
4272 element = EL_CONVEYOR_BELT_1_LEFT;
4276 element = EL_CONVEYOR_BELT_1_RIGHT;
4280 element = EL_CONVEYOR_BELT_4_MIDDLE;
4284 element = EL_CONVEYOR_BELT_4_LEFT;
4288 element = EL_CONVEYOR_BELT_4_RIGHT;
4292 element = EL_EXPANDABLE_WALL_HORIZONTAL;
4296 element = EL_EXPANDABLE_WALL_VERTICAL;
4300 element = EL_EXPANDABLE_WALL_ANY;
4303 case 0x14ce: /* growing steel wall (left/right) */
4304 element = EL_EXPANDABLE_STEELWALL_HORIZONTAL;
4307 case 0x14df: /* growing steel wall (up/down) */
4308 element = EL_EXPANDABLE_STEELWALL_VERTICAL;
4311 case 0x14e8: /* growing steel wall (up/down/left/right) */
4312 element = EL_EXPANDABLE_STEELWALL_ANY;
4316 element = EL_SHIELD_DEADLY;
4320 element = EL_EXTRA_TIME;
4328 element = EL_EMPTY_SPACE;
4331 case 0x1578: /* quicksand (empty) */
4332 element = EL_QUICKSAND_FAST_EMPTY;
4335 case 0x1579: /* slow quicksand (empty) */
4336 element = EL_QUICKSAND_EMPTY;
4339 /* 0x157c - 0x158b: */
4342 /* 0x1590 - 0x159f: */
4343 /* EL_DC_LANDMINE */
4346 element = EL_EM_DYNAMITE;
4349 case 0x15a1: /* key (red) */
4350 element = EL_EM_KEY_1;
4353 case 0x15a2: /* key (yellow) */
4354 element = EL_EM_KEY_2;
4357 case 0x15a3: /* key (blue) */
4358 element = EL_EM_KEY_4;
4361 case 0x15a4: /* key (green) */
4362 element = EL_EM_KEY_3;
4365 case 0x15a5: /* key (white) */
4366 element = EL_DC_KEY_WHITE;
4370 element = EL_WALL_SLIPPERY;
4377 case 0x15a8: /* wall (not round) */
4381 case 0x15a9: /* (blue) */
4382 element = EL_CHAR_A;
4385 case 0x15aa: /* (blue) */
4386 element = EL_CHAR_B;
4389 case 0x15ab: /* (blue) */
4390 element = EL_CHAR_C;
4393 case 0x15ac: /* (blue) */
4394 element = EL_CHAR_D;
4397 case 0x15ad: /* (blue) */
4398 element = EL_CHAR_E;
4401 case 0x15ae: /* (blue) */
4402 element = EL_CHAR_F;
4405 case 0x15af: /* (blue) */
4406 element = EL_CHAR_G;
4409 case 0x15b0: /* (blue) */
4410 element = EL_CHAR_H;
4413 case 0x15b1: /* (blue) */
4414 element = EL_CHAR_I;
4417 case 0x15b2: /* (blue) */
4418 element = EL_CHAR_J;
4421 case 0x15b3: /* (blue) */
4422 element = EL_CHAR_K;
4425 case 0x15b4: /* (blue) */
4426 element = EL_CHAR_L;
4429 case 0x15b5: /* (blue) */
4430 element = EL_CHAR_M;
4433 case 0x15b6: /* (blue) */
4434 element = EL_CHAR_N;
4437 case 0x15b7: /* (blue) */
4438 element = EL_CHAR_O;
4441 case 0x15b8: /* (blue) */
4442 element = EL_CHAR_P;
4445 case 0x15b9: /* (blue) */
4446 element = EL_CHAR_Q;
4449 case 0x15ba: /* (blue) */
4450 element = EL_CHAR_R;
4453 case 0x15bb: /* (blue) */
4454 element = EL_CHAR_S;
4457 case 0x15bc: /* (blue) */
4458 element = EL_CHAR_T;
4461 case 0x15bd: /* (blue) */
4462 element = EL_CHAR_U;
4465 case 0x15be: /* (blue) */
4466 element = EL_CHAR_V;
4469 case 0x15bf: /* (blue) */
4470 element = EL_CHAR_W;
4473 case 0x15c0: /* (blue) */
4474 element = EL_CHAR_X;
4477 case 0x15c1: /* (blue) */
4478 element = EL_CHAR_Y;
4481 case 0x15c2: /* (blue) */
4482 element = EL_CHAR_Z;
4485 case 0x15c3: /* (blue) */
4486 element = EL_CHAR_AUMLAUT;
4489 case 0x15c4: /* (blue) */
4490 element = EL_CHAR_OUMLAUT;
4493 case 0x15c5: /* (blue) */
4494 element = EL_CHAR_UUMLAUT;
4497 case 0x15c6: /* (blue) */
4498 element = EL_CHAR_0;
4501 case 0x15c7: /* (blue) */
4502 element = EL_CHAR_1;
4505 case 0x15c8: /* (blue) */
4506 element = EL_CHAR_2;
4509 case 0x15c9: /* (blue) */
4510 element = EL_CHAR_3;
4513 case 0x15ca: /* (blue) */
4514 element = EL_CHAR_4;
4517 case 0x15cb: /* (blue) */
4518 element = EL_CHAR_5;
4521 case 0x15cc: /* (blue) */
4522 element = EL_CHAR_6;
4525 case 0x15cd: /* (blue) */
4526 element = EL_CHAR_7;
4529 case 0x15ce: /* (blue) */
4530 element = EL_CHAR_8;
4533 case 0x15cf: /* (blue) */
4534 element = EL_CHAR_9;
4537 case 0x15d0: /* (blue) */
4538 element = EL_CHAR_PERIOD;
4541 case 0x15d1: /* (blue) */
4542 element = EL_CHAR_EXCLAM;
4545 case 0x15d2: /* (blue) */
4546 element = EL_CHAR_COLON;
4549 case 0x15d3: /* (blue) */
4550 element = EL_CHAR_LESS;
4553 case 0x15d4: /* (blue) */
4554 element = EL_CHAR_GREATER;
4557 case 0x15d5: /* (blue) */
4558 element = EL_CHAR_QUESTION;
4561 case 0x15d6: /* (blue) */
4562 element = EL_CHAR_COPYRIGHT;
4565 case 0x15d7: /* (blue) */
4566 element = EL_CHAR_UP;
4569 case 0x15d8: /* (blue) */
4570 element = EL_CHAR_DOWN;
4573 case 0x15d9: /* (blue) */
4574 element = EL_CHAR_BUTTON;
4577 case 0x15da: /* (blue) */
4578 element = EL_CHAR_PLUS;
4581 case 0x15db: /* (blue) */
4582 element = EL_CHAR_MINUS;
4585 case 0x15dc: /* (blue) */
4586 element = EL_CHAR_APOSTROPHE;
4589 case 0x15dd: /* (blue) */
4590 element = EL_CHAR_PARENLEFT;
4593 case 0x15de: /* (blue) */
4594 element = EL_CHAR_PARENRIGHT;
4597 case 0x15df: /* (green) */
4598 element = EL_CHAR_A;
4601 case 0x15e0: /* (green) */
4602 element = EL_CHAR_B;
4605 case 0x15e1: /* (green) */
4606 element = EL_CHAR_C;
4609 case 0x15e2: /* (green) */
4610 element = EL_CHAR_D;
4613 case 0x15e3: /* (green) */
4614 element = EL_CHAR_E;
4617 case 0x15e4: /* (green) */
4618 element = EL_CHAR_F;
4621 case 0x15e5: /* (green) */
4622 element = EL_CHAR_G;
4625 case 0x15e6: /* (green) */
4626 element = EL_CHAR_H;
4629 case 0x15e7: /* (green) */
4630 element = EL_CHAR_I;
4633 case 0x15e8: /* (green) */
4634 element = EL_CHAR_J;
4637 case 0x15e9: /* (green) */
4638 element = EL_CHAR_K;
4641 case 0x15ea: /* (green) */
4642 element = EL_CHAR_L;
4645 case 0x15eb: /* (green) */
4646 element = EL_CHAR_M;
4649 case 0x15ec: /* (green) */
4650 element = EL_CHAR_N;
4653 case 0x15ed: /* (green) */
4654 element = EL_CHAR_O;
4657 case 0x15ee: /* (green) */
4658 element = EL_CHAR_P;
4661 case 0x15ef: /* (green) */
4662 element = EL_CHAR_Q;
4665 case 0x15f0: /* (green) */
4666 element = EL_CHAR_R;
4669 case 0x15f1: /* (green) */
4670 element = EL_CHAR_S;
4673 case 0x15f2: /* (green) */
4674 element = EL_CHAR_T;
4677 case 0x15f3: /* (green) */
4678 element = EL_CHAR_U;
4681 case 0x15f4: /* (green) */
4682 element = EL_CHAR_V;
4685 case 0x15f5: /* (green) */
4686 element = EL_CHAR_W;
4689 case 0x15f6: /* (green) */
4690 element = EL_CHAR_X;
4693 case 0x15f7: /* (green) */
4694 element = EL_CHAR_Y;
4697 case 0x15f8: /* (green) */
4698 element = EL_CHAR_Z;
4701 case 0x15f9: /* (green) */
4702 element = EL_CHAR_AUMLAUT;
4705 case 0x15fa: /* (green) */
4706 element = EL_CHAR_OUMLAUT;
4709 case 0x15fb: /* (green) */
4710 element = EL_CHAR_UUMLAUT;
4713 case 0x15fc: /* (green) */
4714 element = EL_CHAR_0;
4717 case 0x15fd: /* (green) */
4718 element = EL_CHAR_1;
4721 case 0x15fe: /* (green) */
4722 element = EL_CHAR_2;
4725 case 0x15ff: /* (green) */
4726 element = EL_CHAR_3;
4729 case 0x1600: /* (green) */
4730 element = EL_CHAR_4;
4733 case 0x1601: /* (green) */
4734 element = EL_CHAR_5;
4737 case 0x1602: /* (green) */
4738 element = EL_CHAR_6;
4741 case 0x1603: /* (green) */
4742 element = EL_CHAR_7;
4745 case 0x1604: /* (green) */
4746 element = EL_CHAR_8;
4749 case 0x1605: /* (green) */
4750 element = EL_CHAR_9;
4753 case 0x1606: /* (green) */
4754 element = EL_CHAR_PERIOD;
4757 case 0x1607: /* (green) */
4758 element = EL_CHAR_EXCLAM;
4761 case 0x1608: /* (green) */
4762 element = EL_CHAR_COLON;
4765 case 0x1609: /* (green) */
4766 element = EL_CHAR_LESS;
4769 case 0x160a: /* (green) */
4770 element = EL_CHAR_GREATER;
4773 case 0x160b: /* (green) */
4774 element = EL_CHAR_QUESTION;
4777 case 0x160c: /* (green) */
4778 element = EL_CHAR_COPYRIGHT;
4781 case 0x160d: /* (green) */
4782 element = EL_CHAR_UP;
4785 case 0x160e: /* (green) */
4786 element = EL_CHAR_DOWN;
4789 case 0x160f: /* (green) */
4790 element = EL_CHAR_BUTTON;
4793 case 0x1610: /* (green) */
4794 element = EL_CHAR_PLUS;
4797 case 0x1611: /* (green) */
4798 element = EL_CHAR_MINUS;
4801 case 0x1612: /* (green) */
4802 element = EL_CHAR_APOSTROPHE;
4805 case 0x1613: /* (green) */
4806 element = EL_CHAR_PARENLEFT;
4809 case 0x1614: /* (green) */
4810 element = EL_CHAR_PARENRIGHT;
4813 case 0x1615: /* (blue steel) */
4814 element = EL_STEEL_CHAR_A;
4817 case 0x1616: /* (blue steel) */
4818 element = EL_STEEL_CHAR_B;
4821 case 0x1617: /* (blue steel) */
4822 element = EL_STEEL_CHAR_C;
4825 case 0x1618: /* (blue steel) */
4826 element = EL_STEEL_CHAR_D;
4829 case 0x1619: /* (blue steel) */
4830 element = EL_STEEL_CHAR_E;
4833 case 0x161a: /* (blue steel) */
4834 element = EL_STEEL_CHAR_F;
4837 case 0x161b: /* (blue steel) */
4838 element = EL_STEEL_CHAR_G;
4841 case 0x161c: /* (blue steel) */
4842 element = EL_STEEL_CHAR_H;
4845 case 0x161d: /* (blue steel) */
4846 element = EL_STEEL_CHAR_I;
4849 case 0x161e: /* (blue steel) */
4850 element = EL_STEEL_CHAR_J;
4853 case 0x161f: /* (blue steel) */
4854 element = EL_STEEL_CHAR_K;
4857 case 0x1620: /* (blue steel) */
4858 element = EL_STEEL_CHAR_L;
4861 case 0x1621: /* (blue steel) */
4862 element = EL_STEEL_CHAR_M;
4865 case 0x1622: /* (blue steel) */
4866 element = EL_STEEL_CHAR_N;
4869 case 0x1623: /* (blue steel) */
4870 element = EL_STEEL_CHAR_O;
4873 case 0x1624: /* (blue steel) */
4874 element = EL_STEEL_CHAR_P;
4877 case 0x1625: /* (blue steel) */
4878 element = EL_STEEL_CHAR_Q;
4881 case 0x1626: /* (blue steel) */
4882 element = EL_STEEL_CHAR_R;
4885 case 0x1627: /* (blue steel) */
4886 element = EL_STEEL_CHAR_S;
4889 case 0x1628: /* (blue steel) */
4890 element = EL_STEEL_CHAR_T;
4893 case 0x1629: /* (blue steel) */
4894 element = EL_STEEL_CHAR_U;
4897 case 0x162a: /* (blue steel) */
4898 element = EL_STEEL_CHAR_V;
4901 case 0x162b: /* (blue steel) */
4902 element = EL_STEEL_CHAR_W;
4905 case 0x162c: /* (blue steel) */
4906 element = EL_STEEL_CHAR_X;
4909 case 0x162d: /* (blue steel) */
4910 element = EL_STEEL_CHAR_Y;
4913 case 0x162e: /* (blue steel) */
4914 element = EL_STEEL_CHAR_Z;
4917 case 0x162f: /* (blue steel) */
4918 element = EL_STEEL_CHAR_AUMLAUT;
4921 case 0x1630: /* (blue steel) */
4922 element = EL_STEEL_CHAR_OUMLAUT;
4925 case 0x1631: /* (blue steel) */
4926 element = EL_STEEL_CHAR_UUMLAUT;
4929 case 0x1632: /* (blue steel) */
4930 element = EL_STEEL_CHAR_0;
4933 case 0x1633: /* (blue steel) */
4934 element = EL_STEEL_CHAR_1;
4937 case 0x1634: /* (blue steel) */
4938 element = EL_STEEL_CHAR_2;
4941 case 0x1635: /* (blue steel) */
4942 element = EL_STEEL_CHAR_3;
4945 case 0x1636: /* (blue steel) */
4946 element = EL_STEEL_CHAR_4;
4949 case 0x1637: /* (blue steel) */
4950 element = EL_STEEL_CHAR_5;
4953 case 0x1638: /* (blue steel) */
4954 element = EL_STEEL_CHAR_6;
4957 case 0x1639: /* (blue steel) */
4958 element = EL_STEEL_CHAR_7;
4961 case 0x163a: /* (blue steel) */
4962 element = EL_STEEL_CHAR_8;
4965 case 0x163b: /* (blue steel) */
4966 element = EL_STEEL_CHAR_9;
4969 case 0x163c: /* (blue steel) */
4970 element = EL_STEEL_CHAR_PERIOD;
4973 case 0x163d: /* (blue steel) */
4974 element = EL_STEEL_CHAR_EXCLAM;
4977 case 0x163e: /* (blue steel) */
4978 element = EL_STEEL_CHAR_COLON;
4981 case 0x163f: /* (blue steel) */
4982 element = EL_STEEL_CHAR_LESS;
4985 case 0x1640: /* (blue steel) */
4986 element = EL_STEEL_CHAR_GREATER;
4989 case 0x1641: /* (blue steel) */
4990 element = EL_STEEL_CHAR_QUESTION;
4993 case 0x1642: /* (blue steel) */
4994 element = EL_STEEL_CHAR_COPYRIGHT;
4997 case 0x1643: /* (blue steel) */
4998 element = EL_STEEL_CHAR_UP;
5001 case 0x1644: /* (blue steel) */
5002 element = EL_STEEL_CHAR_DOWN;
5005 case 0x1645: /* (blue steel) */
5006 element = EL_STEEL_CHAR_BUTTON;
5009 case 0x1646: /* (blue steel) */
5010 element = EL_STEEL_CHAR_PLUS;
5013 case 0x1647: /* (blue steel) */
5014 element = EL_STEEL_CHAR_MINUS;
5017 case 0x1648: /* (blue steel) */
5018 element = EL_STEEL_CHAR_APOSTROPHE;
5021 case 0x1649: /* (blue steel) */
5022 element = EL_STEEL_CHAR_PARENLEFT;
5025 case 0x164a: /* (blue steel) */
5026 element = EL_STEEL_CHAR_PARENRIGHT;
5029 case 0x164b: /* (green steel) */
5030 element = EL_STEEL_CHAR_A;
5033 case 0x164c: /* (green steel) */
5034 element = EL_STEEL_CHAR_B;
5037 case 0x164d: /* (green steel) */
5038 element = EL_STEEL_CHAR_C;
5041 case 0x164e: /* (green steel) */
5042 element = EL_STEEL_CHAR_D;
5045 case 0x164f: /* (green steel) */
5046 element = EL_STEEL_CHAR_E;
5049 case 0x1650: /* (green steel) */
5050 element = EL_STEEL_CHAR_F;
5053 case 0x1651: /* (green steel) */
5054 element = EL_STEEL_CHAR_G;
5057 case 0x1652: /* (green steel) */
5058 element = EL_STEEL_CHAR_H;
5061 case 0x1653: /* (green steel) */
5062 element = EL_STEEL_CHAR_I;
5065 case 0x1654: /* (green steel) */
5066 element = EL_STEEL_CHAR_J;
5069 case 0x1655: /* (green steel) */
5070 element = EL_STEEL_CHAR_K;
5073 case 0x1656: /* (green steel) */
5074 element = EL_STEEL_CHAR_L;
5077 case 0x1657: /* (green steel) */
5078 element = EL_STEEL_CHAR_M;
5081 case 0x1658: /* (green steel) */
5082 element = EL_STEEL_CHAR_N;
5085 case 0x1659: /* (green steel) */
5086 element = EL_STEEL_CHAR_O;
5089 case 0x165a: /* (green steel) */
5090 element = EL_STEEL_CHAR_P;
5093 case 0x165b: /* (green steel) */
5094 element = EL_STEEL_CHAR_Q;
5097 case 0x165c: /* (green steel) */
5098 element = EL_STEEL_CHAR_R;
5101 case 0x165d: /* (green steel) */
5102 element = EL_STEEL_CHAR_S;
5105 case 0x165e: /* (green steel) */
5106 element = EL_STEEL_CHAR_T;
5109 case 0x165f: /* (green steel) */
5110 element = EL_STEEL_CHAR_U;
5113 case 0x1660: /* (green steel) */
5114 element = EL_STEEL_CHAR_V;
5117 case 0x1661: /* (green steel) */
5118 element = EL_STEEL_CHAR_W;
5121 case 0x1662: /* (green steel) */
5122 element = EL_STEEL_CHAR_X;
5125 case 0x1663: /* (green steel) */
5126 element = EL_STEEL_CHAR_Y;
5129 case 0x1664: /* (green steel) */
5130 element = EL_STEEL_CHAR_Z;
5133 case 0x1665: /* (green steel) */
5134 element = EL_STEEL_CHAR_AUMLAUT;
5137 case 0x1666: /* (green steel) */
5138 element = EL_STEEL_CHAR_OUMLAUT;
5141 case 0x1667: /* (green steel) */
5142 element = EL_STEEL_CHAR_UUMLAUT;
5145 case 0x1668: /* (green steel) */
5146 element = EL_STEEL_CHAR_0;
5149 case 0x1669: /* (green steel) */
5150 element = EL_STEEL_CHAR_1;
5153 case 0x166a: /* (green steel) */
5154 element = EL_STEEL_CHAR_2;
5157 case 0x166b: /* (green steel) */
5158 element = EL_STEEL_CHAR_3;
5161 case 0x166c: /* (green steel) */
5162 element = EL_STEEL_CHAR_4;
5165 case 0x166d: /* (green steel) */
5166 element = EL_STEEL_CHAR_5;
5169 case 0x166e: /* (green steel) */
5170 element = EL_STEEL_CHAR_6;
5173 case 0x166f: /* (green steel) */
5174 element = EL_STEEL_CHAR_7;
5177 case 0x1670: /* (green steel) */
5178 element = EL_STEEL_CHAR_8;
5181 case 0x1671: /* (green steel) */
5182 element = EL_STEEL_CHAR_9;
5185 case 0x1672: /* (green steel) */
5186 element = EL_STEEL_CHAR_PERIOD;
5189 case 0x1673: /* (green steel) */
5190 element = EL_STEEL_CHAR_EXCLAM;
5193 case 0x1674: /* (green steel) */
5194 element = EL_STEEL_CHAR_COLON;
5197 case 0x1675: /* (green steel) */
5198 element = EL_STEEL_CHAR_LESS;
5201 case 0x1676: /* (green steel) */
5202 element = EL_STEEL_CHAR_GREATER;
5205 case 0x1677: /* (green steel) */
5206 element = EL_STEEL_CHAR_QUESTION;
5209 case 0x1678: /* (green steel) */
5210 element = EL_STEEL_CHAR_COPYRIGHT;
5213 case 0x1679: /* (green steel) */
5214 element = EL_STEEL_CHAR_UP;
5217 case 0x167a: /* (green steel) */
5218 element = EL_STEEL_CHAR_DOWN;
5221 case 0x167b: /* (green steel) */
5222 element = EL_STEEL_CHAR_BUTTON;
5225 case 0x167c: /* (green steel) */
5226 element = EL_STEEL_CHAR_PLUS;
5229 case 0x167d: /* (green steel) */
5230 element = EL_STEEL_CHAR_MINUS;
5233 case 0x167e: /* (green steel) */
5234 element = EL_STEEL_CHAR_APOSTROPHE;
5237 case 0x167f: /* (green steel) */
5238 element = EL_STEEL_CHAR_PARENLEFT;
5241 case 0x1680: /* (green steel) */
5242 element = EL_STEEL_CHAR_PARENRIGHT;
5245 case 0x1681: /* gate (red) */
5246 element = EL_EM_GATE_1;
5249 case 0x1682: /* secret gate (red) */
5250 element = EL_GATE_1_GRAY;
5253 case 0x1683: /* gate (yellow) */
5254 element = EL_EM_GATE_2;
5257 case 0x1684: /* secret gate (yellow) */
5258 element = EL_GATE_2_GRAY;
5261 case 0x1685: /* gate (blue) */
5262 element = EL_EM_GATE_4;
5265 case 0x1686: /* secret gate (blue) */
5266 element = EL_GATE_4_GRAY;
5269 case 0x1687: /* gate (green) */
5270 element = EL_EM_GATE_3;
5273 case 0x1688: /* secret gate (green) */
5274 element = EL_GATE_3_GRAY;
5277 case 0x1689: /* gate (white) */
5278 element = EL_DC_GATE_WHITE;
5281 case 0x168a: /* secret gate (white) */
5282 element = EL_DC_GATE_WHITE_GRAY;
5285 case 0x168b: /* secret gate (no key) */
5286 element = EL_DC_GATE_FAKE_GRAY;
5290 element = EL_ROBOT_WHEEL;
5294 element = EL_DC_TIMEGATE_SWITCH;
5298 element = EL_ACID_POOL_BOTTOM;
5302 element = EL_ACID_POOL_TOPLEFT;
5306 element = EL_ACID_POOL_TOPRIGHT;
5310 element = EL_ACID_POOL_BOTTOMLEFT;
5314 element = EL_ACID_POOL_BOTTOMRIGHT;
5318 element = EL_STEELWALL;
5322 element = EL_STEELWALL_SLIPPERY;
5325 case 0x1695: /* steel wall (not round) */
5326 element = EL_STEELWALL;
5329 case 0x1696: /* steel wall (left) */
5330 element = EL_DC_STEELWALL_1_LEFT;
5333 case 0x1697: /* steel wall (bottom) */
5334 element = EL_DC_STEELWALL_1_BOTTOM;
5337 case 0x1698: /* steel wall (right) */
5338 element = EL_DC_STEELWALL_1_RIGHT;
5341 case 0x1699: /* steel wall (top) */
5342 element = EL_DC_STEELWALL_1_TOP;
5345 case 0x169a: /* steel wall (left/bottom) */
5346 element = EL_DC_STEELWALL_1_BOTTOMLEFT;
5349 case 0x169b: /* steel wall (right/bottom) */
5350 element = EL_DC_STEELWALL_1_BOTTOMRIGHT;
5353 case 0x169c: /* steel wall (right/top) */
5354 element = EL_DC_STEELWALL_1_TOPRIGHT;
5357 case 0x169d: /* steel wall (left/top) */
5358 element = EL_DC_STEELWALL_1_TOPLEFT;
5361 case 0x169e: /* steel wall (right/bottom small) */
5362 element = EL_DC_STEELWALL_1_BOTTOMRIGHT_2;
5365 case 0x169f: /* steel wall (left/bottom small) */
5366 element = EL_DC_STEELWALL_1_BOTTOMLEFT_2;
5369 case 0x16a0: /* steel wall (right/top small) */
5370 element = EL_DC_STEELWALL_1_TOPRIGHT_2;
5373 case 0x16a1: /* steel wall (left/top small) */
5374 element = EL_DC_STEELWALL_1_TOPLEFT_2;
5377 case 0x16a2: /* steel wall (left/right) */
5378 element = EL_DC_STEELWALL_1_VERTICAL;
5381 case 0x16a3: /* steel wall (top/bottom) */
5382 element = EL_DC_STEELWALL_1_HORIZONTAL;
5385 case 0x16a4: /* steel wall 2 (left end) */
5386 element = EL_DC_STEELWALL_2_LEFT;
5389 case 0x16a5: /* steel wall 2 (right end) */
5390 element = EL_DC_STEELWALL_2_RIGHT;
5393 case 0x16a6: /* steel wall 2 (top end) */
5394 element = EL_DC_STEELWALL_2_TOP;
5397 case 0x16a7: /* steel wall 2 (bottom end) */
5398 element = EL_DC_STEELWALL_2_BOTTOM;
5401 case 0x16a8: /* steel wall 2 (left/right) */
5402 element = EL_DC_STEELWALL_2_HORIZONTAL;
5405 case 0x16a9: /* steel wall 2 (up/down) */
5406 element = EL_DC_STEELWALL_2_VERTICAL;
5409 case 0x16aa: /* steel wall 2 (mid) */
5410 element = EL_DC_STEELWALL_2_MIDDLE;
5414 element = EL_SIGN_EXCLAMATION;
5418 element = EL_SIGN_RADIOACTIVITY;
5422 element = EL_SIGN_STOP;
5426 element = EL_SIGN_WHEELCHAIR;
5430 element = EL_SIGN_PARKING;
5434 element = EL_SIGN_NO_ENTRY;
5438 element = EL_SIGN_HEART;
5442 element = EL_SIGN_GIVE_WAY;
5446 element = EL_SIGN_ENTRY_FORBIDDEN;
5450 element = EL_SIGN_EMERGENCY_EXIT;
5454 element = EL_SIGN_YIN_YANG;
5458 element = EL_WALL_EMERALD;
5462 element = EL_WALL_DIAMOND;
5466 element = EL_WALL_PEARL;
5470 element = EL_WALL_CRYSTAL;
5474 element = EL_INVISIBLE_WALL;
5478 element = EL_INVISIBLE_STEELWALL;
5481 /* 0x16bc - 0x16cb: */
5482 /* EL_INVISIBLE_SAND */
5485 element = EL_LIGHT_SWITCH;
5489 element = EL_ENVELOPE_1;
5493 if (element >= 0x0117 && element <= 0x036e) /* (?) */
5494 element = EL_DIAMOND;
5495 else if (element >= 0x042d && element <= 0x0684) /* (?) */
5496 element = EL_EMERALD;
5497 else if (element >= 0x157c && element <= 0x158b)
5499 else if (element >= 0x1590 && element <= 0x159f)
5500 element = EL_DC_LANDMINE;
5501 else if (element >= 0x16bc && element <= 0x16cb)
5502 element = EL_INVISIBLE_SAND;
5505 Error(ERR_WARN, "unknown Diamond Caves element 0x%04x", element);
5506 element = EL_UNKNOWN;
5511 return getMappedElement(element);
5514 static void LoadLevelFromFileStream_DC(File *file, struct LevelInfo *level,
5517 byte header[DC_LEVEL_HEADER_SIZE];
5519 int envelope_header_pos = 62;
5520 int envelope_content_pos = 94;
5521 int level_name_pos = 251;
5522 int level_author_pos = 292;
5523 int envelope_header_len;
5524 int envelope_content_len;
5526 int level_author_len;
5528 int num_yamyam_contents;
5531 getDecodedWord_DC(0, TRUE); /* initialize DC2 decoding engine */
5533 for (i = 0; i < DC_LEVEL_HEADER_SIZE / 2; i++)
5535 unsigned short header_word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
5537 header[i * 2 + 0] = header_word >> 8;
5538 header[i * 2 + 1] = header_word & 0xff;
5541 /* read some values from level header to check level decoding integrity */
5542 fieldx = header[6] | (header[7] << 8);
5543 fieldy = header[8] | (header[9] << 8);
5544 num_yamyam_contents = header[60] | (header[61] << 8);
5546 /* do some simple sanity checks to ensure that level was correctly decoded */
5547 if (fieldx < 1 || fieldx > 256 ||
5548 fieldy < 1 || fieldy > 256 ||
5549 num_yamyam_contents < 1 || num_yamyam_contents > 8)
5551 level->no_valid_file = TRUE;
5553 Error(ERR_WARN, "cannot decode level from stream -- using empty level");
5558 /* maximum envelope header size is 31 bytes */
5559 envelope_header_len = header[envelope_header_pos];
5560 /* maximum envelope content size is 110 (156?) bytes */
5561 envelope_content_len = header[envelope_content_pos];
5563 /* maximum level title size is 40 bytes */
5564 level_name_len = MIN(header[level_name_pos], MAX_LEVEL_NAME_LEN);
5565 /* maximum level author size is 30 (51?) bytes */
5566 level_author_len = MIN(header[level_author_pos], MAX_LEVEL_AUTHOR_LEN);
5570 for (i = 0; i < envelope_header_len; i++)
5571 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
5572 level->envelope[0].text[envelope_size++] =
5573 header[envelope_header_pos + 1 + i];
5575 if (envelope_header_len > 0 && envelope_content_len > 0)
5577 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
5578 level->envelope[0].text[envelope_size++] = '\n';
5579 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
5580 level->envelope[0].text[envelope_size++] = '\n';
5583 for (i = 0; i < envelope_content_len; i++)
5584 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
5585 level->envelope[0].text[envelope_size++] =
5586 header[envelope_content_pos + 1 + i];
5588 level->envelope[0].text[envelope_size] = '\0';
5590 level->envelope[0].xsize = MAX_ENVELOPE_XSIZE;
5591 level->envelope[0].ysize = 10;
5592 level->envelope[0].autowrap = TRUE;
5593 level->envelope[0].centered = TRUE;
5595 for (i = 0; i < level_name_len; i++)
5596 level->name[i] = header[level_name_pos + 1 + i];
5597 level->name[level_name_len] = '\0';
5599 for (i = 0; i < level_author_len; i++)
5600 level->author[i] = header[level_author_pos + 1 + i];
5601 level->author[level_author_len] = '\0';
5603 num_yamyam_contents = header[60] | (header[61] << 8);
5604 level->num_yamyam_contents =
5605 MIN(MAX(MIN_ELEMENT_CONTENTS, num_yamyam_contents), MAX_ELEMENT_CONTENTS);
5607 for (i = 0; i < num_yamyam_contents; i++)
5609 for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
5611 unsigned short word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
5612 int element_dc = ((word & 0xff) << 8) | ((word >> 8) & 0xff);
5614 if (i < MAX_ELEMENT_CONTENTS)
5615 level->yamyam_content[i].e[x][y] = getMappedElement_DC(element_dc);
5619 fieldx = header[6] | (header[7] << 8);
5620 fieldy = header[8] | (header[9] << 8);
5621 level->fieldx = MIN(MAX(MIN_LEV_FIELDX, fieldx), MAX_LEV_FIELDX);
5622 level->fieldy = MIN(MAX(MIN_LEV_FIELDY, fieldy), MAX_LEV_FIELDY);
5624 for (y = 0; y < fieldy; y++) for (x = 0; x < fieldx; x++)
5626 unsigned short word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
5627 int element_dc = ((word & 0xff) << 8) | ((word >> 8) & 0xff);
5629 if (x < MAX_LEV_FIELDX && y < MAX_LEV_FIELDY)
5630 level->field[x][y] = getMappedElement_DC(element_dc);
5633 x = MIN(MAX(0, (header[10] | (header[11] << 8)) - 1), MAX_LEV_FIELDX - 1);
5634 y = MIN(MAX(0, (header[12] | (header[13] << 8)) - 1), MAX_LEV_FIELDY - 1);
5635 level->field[x][y] = EL_PLAYER_1;
5637 x = MIN(MAX(0, (header[14] | (header[15] << 8)) - 1), MAX_LEV_FIELDX - 1);
5638 y = MIN(MAX(0, (header[16] | (header[17] << 8)) - 1), MAX_LEV_FIELDY - 1);
5639 level->field[x][y] = EL_PLAYER_2;
5641 level->gems_needed = header[18] | (header[19] << 8);
5643 level->score[SC_EMERALD] = header[20] | (header[21] << 8);
5644 level->score[SC_DIAMOND] = header[22] | (header[23] << 8);
5645 level->score[SC_PEARL] = header[24] | (header[25] << 8);
5646 level->score[SC_CRYSTAL] = header[26] | (header[27] << 8);
5647 level->score[SC_NUT] = header[28] | (header[29] << 8);
5648 level->score[SC_ROBOT] = header[30] | (header[31] << 8);
5649 level->score[SC_SPACESHIP] = header[32] | (header[33] << 8);
5650 level->score[SC_BUG] = header[34] | (header[35] << 8);
5651 level->score[SC_YAMYAM] = header[36] | (header[37] << 8);
5652 level->score[SC_DYNAMITE] = header[38] | (header[39] << 8);
5653 level->score[SC_KEY] = header[40] | (header[41] << 8);
5654 level->score[SC_TIME_BONUS] = header[42] | (header[43] << 8);
5656 level->time = header[44] | (header[45] << 8);
5658 level->amoeba_speed = header[46] | (header[47] << 8);
5659 level->time_light = header[48] | (header[49] << 8);
5660 level->time_timegate = header[50] | (header[51] << 8);
5661 level->time_wheel = header[52] | (header[53] << 8);
5662 level->time_magic_wall = header[54] | (header[55] << 8);
5663 level->extra_time = header[56] | (header[57] << 8);
5664 level->shield_normal_time = header[58] | (header[59] << 8);
5666 /* Diamond Caves has the same (strange) behaviour as Emerald Mine that gems
5667 can slip down from flat walls, like normal walls and steel walls */
5668 level->em_slippery_gems = TRUE;
5671 static void LoadLevelFromFileInfo_DC(struct LevelInfo *level,
5672 struct LevelFileInfo *level_file_info,
5673 boolean level_info_only)
5675 char *filename = level_file_info->filename;
5677 int num_magic_bytes = 8;
5678 char magic_bytes[num_magic_bytes + 1];
5679 int num_levels_to_skip = level_file_info->nr - leveldir_current->first_level;
5681 if (!(file = openFile(filename, MODE_READ)))
5683 level->no_valid_file = TRUE;
5685 if (!level_info_only)
5686 Error(ERR_WARN, "cannot read level '%s' -- using empty level", filename);
5691 // fseek(file, 0x0000, SEEK_SET);
5693 if (level_file_info->packed)
5695 /* read "magic bytes" from start of file */
5696 if (getStringFromFile(file, magic_bytes, num_magic_bytes + 1) == NULL)
5697 magic_bytes[0] = '\0';
5699 /* check "magic bytes" for correct file format */
5700 if (!strPrefix(magic_bytes, "DC2"))
5702 level->no_valid_file = TRUE;
5704 Error(ERR_WARN, "unknown DC level file '%s' -- using empty level",
5710 if (strPrefix(magic_bytes, "DC2Win95") ||
5711 strPrefix(magic_bytes, "DC2Win98"))
5713 int position_first_level = 0x00fa;
5714 int extra_bytes = 4;
5717 /* advance file stream to first level inside the level package */
5718 skip_bytes = position_first_level - num_magic_bytes - extra_bytes;
5720 /* each block of level data is followed by block of non-level data */
5721 num_levels_to_skip *= 2;
5723 /* at least skip header bytes, therefore use ">= 0" instead of "> 0" */
5724 while (num_levels_to_skip >= 0)
5726 /* advance file stream to next level inside the level package */
5727 if (seekFile(file, skip_bytes, SEEK_CUR) != 0)
5729 level->no_valid_file = TRUE;
5731 Error(ERR_WARN, "cannot fseek in file '%s' -- using empty level",
5737 /* skip apparently unused extra bytes following each level */
5738 ReadUnusedBytesFromFile(file, extra_bytes);
5740 /* read size of next level in level package */
5741 skip_bytes = getFile32BitLE(file);
5743 num_levels_to_skip--;
5748 level->no_valid_file = TRUE;
5750 Error(ERR_WARN, "unknown DC2 level file '%s' -- using empty level",
5757 LoadLevelFromFileStream_DC(file, level, level_file_info->nr);
5763 /* ------------------------------------------------------------------------- */
5764 /* functions for loading SB level */
5765 /* ------------------------------------------------------------------------- */
5767 int getMappedElement_SB(int element_ascii, boolean use_ces)
5775 sb_element_mapping[] =
5777 { ' ', EL_EMPTY, EL_CUSTOM_1 }, /* floor (space) */
5778 { '#', EL_STEELWALL, EL_CUSTOM_2 }, /* wall */
5779 { '@', EL_PLAYER_1, EL_CUSTOM_3 }, /* player */
5780 { '$', EL_SOKOBAN_OBJECT, EL_CUSTOM_4 }, /* box */
5781 { '.', EL_SOKOBAN_FIELD_EMPTY, EL_CUSTOM_5 }, /* goal square */
5782 { '*', EL_SOKOBAN_FIELD_FULL, EL_CUSTOM_6 }, /* box on goal square */
5783 { '+', EL_SOKOBAN_FIELD_PLAYER, EL_CUSTOM_7 }, /* player on goal square */
5784 { '_', EL_INVISIBLE_STEELWALL, EL_FROM_LEVEL_TEMPLATE }, /* floor beyond border */
5791 for (i = 0; sb_element_mapping[i].ascii != 0; i++)
5792 if (element_ascii == sb_element_mapping[i].ascii)
5793 return (use_ces ? sb_element_mapping[i].ce : sb_element_mapping[i].sb);
5795 return EL_UNDEFINED;
5798 static void LoadLevelFromFileInfo_SB(struct LevelInfo *level,
5799 struct LevelFileInfo *level_file_info,
5800 boolean level_info_only)
5802 char *filename = level_file_info->filename;
5803 char line[MAX_LINE_LEN], line_raw[MAX_LINE_LEN], previous_line[MAX_LINE_LEN];
5804 char last_comment[MAX_LINE_LEN];
5805 char level_name[MAX_LINE_LEN];
5808 int num_levels_to_skip = level_file_info->nr - leveldir_current->first_level;
5809 boolean read_continued_line = FALSE;
5810 boolean reading_playfield = FALSE;
5811 boolean got_valid_playfield_line = FALSE;
5812 boolean invalid_playfield_char = FALSE;
5813 boolean load_xsb_to_ces = check_special_flags("load_xsb_to_ces");
5814 int file_level_nr = 0;
5816 int x = 0, y = 0; /* initialized to make compilers happy */
5818 last_comment[0] = '\0';
5819 level_name[0] = '\0';
5821 if (!(file = openFile(filename, MODE_READ)))
5823 level->no_valid_file = TRUE;
5825 if (!level_info_only)
5826 Error(ERR_WARN, "cannot read level '%s' -- using empty level", filename);
5831 while (!checkEndOfFile(file))
5833 /* level successfully read, but next level may follow here */
5834 if (!got_valid_playfield_line && reading_playfield)
5836 /* read playfield from single level file -- skip remaining file */
5837 if (!level_file_info->packed)
5840 if (file_level_nr >= num_levels_to_skip)
5845 last_comment[0] = '\0';
5846 level_name[0] = '\0';
5848 reading_playfield = FALSE;
5851 got_valid_playfield_line = FALSE;
5853 /* read next line of input file */
5854 if (!getStringFromFile(file, line, MAX_LINE_LEN))
5857 /* check if line was completely read and is terminated by line break */
5858 if (strlen(line) > 0 && line[strlen(line) - 1] == '\n')
5861 /* cut trailing line break (this can be newline and/or carriage return) */
5862 for (line_ptr = &line[strlen(line)]; line_ptr >= line; line_ptr--)
5863 if ((*line_ptr == '\n' || *line_ptr == '\r') && *(line_ptr + 1) == '\0')
5866 /* copy raw input line for later use (mainly debugging output) */
5867 strcpy(line_raw, line);
5869 if (read_continued_line)
5871 /* append new line to existing line, if there is enough space */
5872 if (strlen(previous_line) + strlen(line_ptr) < MAX_LINE_LEN)
5873 strcat(previous_line, line_ptr);
5875 strcpy(line, previous_line); /* copy storage buffer to line */
5877 read_continued_line = FALSE;
5880 /* if the last character is '\', continue at next line */
5881 if (strlen(line) > 0 && line[strlen(line) - 1] == '\\')
5883 line[strlen(line) - 1] = '\0'; /* cut off trailing backslash */
5884 strcpy(previous_line, line); /* copy line to storage buffer */
5886 read_continued_line = TRUE;
5891 /* skip empty lines */
5892 if (line[0] == '\0')
5895 /* extract comment text from comment line */
5898 for (line_ptr = line; *line_ptr; line_ptr++)
5899 if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != ';')
5902 strcpy(last_comment, line_ptr);
5907 /* extract level title text from line containing level title */
5908 if (line[0] == '\'')
5910 strcpy(level_name, &line[1]);
5912 if (strlen(level_name) > 0 && level_name[strlen(level_name) - 1] == '\'')
5913 level_name[strlen(level_name) - 1] = '\0';
5918 /* skip lines containing only spaces (or empty lines) */
5919 for (line_ptr = line; *line_ptr; line_ptr++)
5920 if (*line_ptr != ' ')
5922 if (*line_ptr == '\0')
5925 /* at this point, we have found a line containing part of a playfield */
5927 got_valid_playfield_line = TRUE;
5929 if (!reading_playfield)
5931 reading_playfield = TRUE;
5932 invalid_playfield_char = FALSE;
5934 for (x = 0; x < MAX_LEV_FIELDX; x++)
5935 for (y = 0; y < MAX_LEV_FIELDY; y++)
5936 level->field[x][y] = getMappedElement_SB(' ', load_xsb_to_ces);
5941 /* start with topmost tile row */
5945 /* skip playfield line if larger row than allowed */
5946 if (y >= MAX_LEV_FIELDY)
5949 /* start with leftmost tile column */
5952 /* read playfield elements from line */
5953 for (line_ptr = line; *line_ptr; line_ptr++)
5955 int mapped_sb_element = getMappedElement_SB(*line_ptr, load_xsb_to_ces);
5957 /* stop parsing playfield line if larger column than allowed */
5958 if (x >= MAX_LEV_FIELDX)
5961 if (mapped_sb_element == EL_UNDEFINED)
5963 invalid_playfield_char = TRUE;
5968 level->field[x][y] = mapped_sb_element;
5970 /* continue with next tile column */
5973 level->fieldx = MAX(x, level->fieldx);
5976 if (invalid_playfield_char)
5978 /* if first playfield line, treat invalid lines as comment lines */
5980 reading_playfield = FALSE;
5985 /* continue with next tile row */
5993 level->fieldx = MIN(MAX(MIN_LEV_FIELDX, level->fieldx), MAX_LEV_FIELDX);
5994 level->fieldy = MIN(MAX(MIN_LEV_FIELDY, level->fieldy), MAX_LEV_FIELDY);
5996 if (!reading_playfield)
5998 level->no_valid_file = TRUE;
6000 Error(ERR_WARN, "cannot read level '%s' -- using empty level", filename);
6005 if (*level_name != '\0')
6007 strncpy(level->name, level_name, MAX_LEVEL_NAME_LEN);
6008 level->name[MAX_LEVEL_NAME_LEN] = '\0';
6010 else if (*last_comment != '\0')
6012 strncpy(level->name, last_comment, MAX_LEVEL_NAME_LEN);
6013 level->name[MAX_LEVEL_NAME_LEN] = '\0';
6017 sprintf(level->name, "--> Level %d <--", level_file_info->nr);
6020 /* set all empty fields beyond the border walls to invisible steel wall */
6021 for (y = 0; y < level->fieldy; y++) for (x = 0; x < level->fieldx; x++)
6023 if ((x == 0 || x == level->fieldx - 1 ||
6024 y == 0 || y == level->fieldy - 1) &&
6025 level->field[x][y] == getMappedElement_SB(' ', load_xsb_to_ces))
6026 FloodFillLevel(x, y, getMappedElement_SB('_', load_xsb_to_ces),
6027 level->field, level->fieldx, level->fieldy);
6030 /* set special level settings for Sokoban levels */
6033 level->use_step_counter = TRUE;
6035 if (load_xsb_to_ces)
6037 /* special global settings can now be set in level template */
6039 level->use_custom_template = TRUE;
6044 /* ------------------------------------------------------------------------- */
6045 /* functions for handling native levels */
6046 /* ------------------------------------------------------------------------- */
6048 static void LoadLevelFromFileInfo_EM(struct LevelInfo *level,
6049 struct LevelFileInfo *level_file_info,
6050 boolean level_info_only)
6052 if (!LoadNativeLevel_EM(level_file_info->filename, level_info_only))
6053 level->no_valid_file = TRUE;
6056 static void LoadLevelFromFileInfo_SP(struct LevelInfo *level,
6057 struct LevelFileInfo *level_file_info,
6058 boolean level_info_only)
6062 /* determine position of requested level inside level package */
6063 if (level_file_info->packed)
6064 pos = level_file_info->nr - leveldir_current->first_level;
6066 if (!LoadNativeLevel_SP(level_file_info->filename, pos, level_info_only))
6067 level->no_valid_file = TRUE;
6070 static void LoadLevelFromFileInfo_MM(struct LevelInfo *level,
6071 struct LevelFileInfo *level_file_info,
6072 boolean level_info_only)
6074 if (!LoadNativeLevel_MM(level_file_info->filename, level_info_only))
6075 level->no_valid_file = TRUE;
6078 void CopyNativeLevel_RND_to_Native(struct LevelInfo *level)
6080 if (level->game_engine_type == GAME_ENGINE_TYPE_EM)
6081 CopyNativeLevel_RND_to_EM(level);
6082 else if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
6083 CopyNativeLevel_RND_to_SP(level);
6084 else if (level->game_engine_type == GAME_ENGINE_TYPE_MM)
6085 CopyNativeLevel_RND_to_MM(level);
6088 void CopyNativeLevel_Native_to_RND(struct LevelInfo *level)
6090 if (level->game_engine_type == GAME_ENGINE_TYPE_EM)
6091 CopyNativeLevel_EM_to_RND(level);
6092 else if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
6093 CopyNativeLevel_SP_to_RND(level);
6094 else if (level->game_engine_type == GAME_ENGINE_TYPE_MM)
6095 CopyNativeLevel_MM_to_RND(level);
6098 void SaveNativeLevel(struct LevelInfo *level)
6100 if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
6102 char *basename = getSingleLevelBasenameExt(level->file_info.nr, "sp");
6103 char *filename = getLevelFilenameFromBasename(basename);
6105 CopyNativeLevel_RND_to_SP(level);
6106 CopyNativeTape_RND_to_SP(level);
6108 SaveNativeLevel_SP(filename);
6113 /* ------------------------------------------------------------------------- */
6114 /* functions for loading generic level */
6115 /* ------------------------------------------------------------------------- */
6117 static void LoadLevelFromFileInfo(struct LevelInfo *level,
6118 struct LevelFileInfo *level_file_info,
6119 boolean level_info_only)
6121 /* always start with reliable default values */
6122 setLevelInfoToDefaults(level, level_info_only, TRUE);
6124 switch (level_file_info->type)
6126 case LEVEL_FILE_TYPE_RND:
6127 LoadLevelFromFileInfo_RND(level, level_file_info, level_info_only);
6130 case LEVEL_FILE_TYPE_EM:
6131 LoadLevelFromFileInfo_EM(level, level_file_info, level_info_only);
6132 level->game_engine_type = GAME_ENGINE_TYPE_EM;
6135 case LEVEL_FILE_TYPE_SP:
6136 LoadLevelFromFileInfo_SP(level, level_file_info, level_info_only);
6137 level->game_engine_type = GAME_ENGINE_TYPE_SP;
6140 case LEVEL_FILE_TYPE_MM:
6141 LoadLevelFromFileInfo_MM(level, level_file_info, level_info_only);
6142 level->game_engine_type = GAME_ENGINE_TYPE_MM;
6145 case LEVEL_FILE_TYPE_DC:
6146 LoadLevelFromFileInfo_DC(level, level_file_info, level_info_only);
6149 case LEVEL_FILE_TYPE_SB:
6150 LoadLevelFromFileInfo_SB(level, level_file_info, level_info_only);
6154 LoadLevelFromFileInfo_RND(level, level_file_info, level_info_only);
6158 /* if level file is invalid, restore level structure to default values */
6159 if (level->no_valid_file)
6160 setLevelInfoToDefaults(level, level_info_only, FALSE);
6162 if (level->game_engine_type == GAME_ENGINE_TYPE_UNKNOWN)
6163 level->game_engine_type = GAME_ENGINE_TYPE_RND;
6165 if (level_file_info->type != LEVEL_FILE_TYPE_RND)
6166 CopyNativeLevel_Native_to_RND(level);
6169 void LoadLevelFromFilename(struct LevelInfo *level, char *filename)
6171 static struct LevelFileInfo level_file_info;
6173 /* always start with reliable default values */
6174 setFileInfoToDefaults(&level_file_info);
6176 level_file_info.nr = 0; /* unknown level number */
6177 level_file_info.type = LEVEL_FILE_TYPE_RND; /* no others supported yet */
6178 level_file_info.filename = filename;
6180 LoadLevelFromFileInfo(level, &level_file_info, FALSE);
6183 static void LoadLevel_InitVersion(struct LevelInfo *level, char *filename)
6187 if (leveldir_current == NULL) /* only when dumping level */
6190 /* all engine modifications also valid for levels which use latest engine */
6191 if (level->game_version < VERSION_IDENT(3,2,0,5))
6193 /* time bonus score was given for 10 s instead of 1 s before 3.2.0-5 */
6194 level->score[SC_TIME_BONUS] /= 10;
6197 if (leveldir_current->latest_engine)
6199 /* ---------- use latest game engine ----------------------------------- */
6201 /* For all levels which are forced to use the latest game engine version
6202 (normally all but user contributed, private and undefined levels), set
6203 the game engine version to the actual version; this allows for actual
6204 corrections in the game engine to take effect for existing, converted
6205 levels (from "classic" or other existing games) to make the emulation
6206 of the corresponding game more accurate, while (hopefully) not breaking
6207 existing levels created from other players. */
6209 level->game_version = GAME_VERSION_ACTUAL;
6211 /* Set special EM style gems behaviour: EM style gems slip down from
6212 normal, steel and growing wall. As this is a more fundamental change,
6213 it seems better to set the default behaviour to "off" (as it is more
6214 natural) and make it configurable in the level editor (as a property
6215 of gem style elements). Already existing converted levels (neither
6216 private nor contributed levels) are changed to the new behaviour. */
6218 if (level->file_version < FILE_VERSION_2_0)
6219 level->em_slippery_gems = TRUE;
6224 /* ---------- use game engine the level was created with ----------------- */
6226 /* For all levels which are not forced to use the latest game engine
6227 version (normally user contributed, private and undefined levels),
6228 use the version of the game engine the levels were created for.
6230 Since 2.0.1, the game engine version is now directly stored
6231 in the level file (chunk "VERS"), so there is no need anymore
6232 to set the game version from the file version (except for old,
6233 pre-2.0 levels, where the game version is still taken from the
6234 file format version used to store the level -- see above). */
6236 /* player was faster than enemies in 1.0.0 and before */
6237 if (level->file_version == FILE_VERSION_1_0)
6238 for (i = 0; i < MAX_PLAYERS; i++)
6239 level->initial_player_stepsize[i] = STEPSIZE_FAST;
6241 /* default behaviour for EM style gems was "slippery" only in 2.0.1 */
6242 if (level->game_version == VERSION_IDENT(2,0,1,0))
6243 level->em_slippery_gems = TRUE;
6245 /* springs could be pushed over pits before (pre-release version) 2.2.0 */
6246 if (level->game_version < VERSION_IDENT(2,2,0,0))
6247 level->use_spring_bug = TRUE;
6249 if (level->game_version < VERSION_IDENT(3,2,0,5))
6251 /* time orb caused limited time in endless time levels before 3.2.0-5 */
6252 level->use_time_orb_bug = TRUE;
6254 /* default behaviour for snapping was "no snap delay" before 3.2.0-5 */
6255 level->block_snap_field = FALSE;
6257 /* extra time score was same value as time left score before 3.2.0-5 */
6258 level->extra_time_score = level->score[SC_TIME_BONUS];
6261 if (level->game_version < VERSION_IDENT(3,2,0,7))
6263 /* default behaviour for snapping was "not continuous" before 3.2.0-7 */
6264 level->continuous_snapping = FALSE;
6267 /* only few elements were able to actively move into acid before 3.1.0 */
6268 /* trigger settings did not exist before 3.1.0; set to default "any" */
6269 if (level->game_version < VERSION_IDENT(3,1,0,0))
6271 /* correct "can move into acid" settings (all zero in old levels) */
6273 level->can_move_into_acid_bits = 0; /* nothing can move into acid */
6274 level->dont_collide_with_bits = 0; /* nothing is deadly when colliding */
6276 setMoveIntoAcidProperty(level, EL_ROBOT, TRUE);
6277 setMoveIntoAcidProperty(level, EL_SATELLITE, TRUE);
6278 setMoveIntoAcidProperty(level, EL_PENGUIN, TRUE);
6279 setMoveIntoAcidProperty(level, EL_BALLOON, TRUE);
6281 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6282 SET_PROPERTY(EL_CUSTOM_START + i, EP_CAN_MOVE_INTO_ACID, TRUE);
6284 /* correct trigger settings (stored as zero == "none" in old levels) */
6286 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6288 int element = EL_CUSTOM_START + i;
6289 struct ElementInfo *ei = &element_info[element];
6291 for (j = 0; j < ei->num_change_pages; j++)
6293 struct ElementChangeInfo *change = &ei->change_page[j];
6295 change->trigger_player = CH_PLAYER_ANY;
6296 change->trigger_page = CH_PAGE_ANY;
6301 /* try to detect and fix "Snake Bite" levels, which are broken with 3.2.0 */
6303 int element = EL_CUSTOM_256;
6304 struct ElementInfo *ei = &element_info[element];
6305 struct ElementChangeInfo *change = &ei->change_page[0];
6307 /* This is needed to fix a problem that was caused by a bugfix in function
6308 game.c/CreateFieldExt() introduced with 3.2.0 that corrects the behaviour
6309 when a custom element changes to EL_SOKOBAN_FIELD_PLAYER (before, it did
6310 not replace walkable elements, but instead just placed the player on it,
6311 without placing the Sokoban field under the player). Unfortunately, this
6312 breaks "Snake Bite" style levels when the snake is halfway through a door
6313 that just closes (the snake head is still alive and can be moved in this
6314 case). This can be fixed by replacing the EL_SOKOBAN_FIELD_PLAYER by the
6315 player (without Sokoban element) which then gets killed as designed). */
6317 if ((strncmp(leveldir_current->identifier, "snake_bite", 10) == 0 ||
6318 strncmp(ei->description, "pause b4 death", 14) == 0) &&
6319 change->target_element == EL_SOKOBAN_FIELD_PLAYER)
6320 change->target_element = EL_PLAYER_1;
6323 /* try to detect and fix "Zelda" style levels, which are broken with 3.2.5 */
6324 if (level->game_version < VERSION_IDENT(3,2,5,0))
6326 /* This is needed to fix a problem that was caused by a bugfix in function
6327 game.c/CheckTriggeredElementChangeExt() introduced with 3.2.5 that
6328 corrects the behaviour when a custom element changes to another custom
6329 element with a higher element number that has change actions defined.
6330 Normally, only one change per frame is allowed for custom elements.
6331 Therefore, it is checked if a custom element already changed in the
6332 current frame; if it did, subsequent changes are suppressed.
6333 Unfortunately, this is only checked for element changes, but not for
6334 change actions, which are still executed. As the function above loops
6335 through all custom elements from lower to higher, an element change
6336 resulting in a lower CE number won't be checked again, while a target
6337 element with a higher number will also be checked, and potential change
6338 actions will get executed for this CE, too (which is wrong), while
6339 further changes are ignored (which is correct). As this bugfix breaks
6340 Zelda II (and introduces graphical bugs to Zelda I, and also breaks a
6341 few other levels like Alan Bond's "FMV"), allow the previous, incorrect
6342 behaviour for existing levels and tapes that make use of this bug */
6344 level->use_action_after_change_bug = TRUE;
6347 /* not centering level after relocating player was default only in 3.2.3 */
6348 if (level->game_version == VERSION_IDENT(3,2,3,0)) /* (no pre-releases) */
6349 level->shifted_relocation = TRUE;
6351 /* EM style elements always chain-exploded in R'n'D engine before 3.2.6 */
6352 if (level->game_version < VERSION_IDENT(3,2,6,0))
6353 level->em_explodes_by_fire = TRUE;
6356 static void LoadLevel_InitStandardElements(struct LevelInfo *level)
6360 /* map elements that have changed in newer versions */
6361 level->amoeba_content = getMappedElementByVersion(level->amoeba_content,
6362 level->game_version);
6363 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
6364 for (x = 0; x < 3; x++)
6365 for (y = 0; y < 3; y++)
6366 level->yamyam_content[i].e[x][y] =
6367 getMappedElementByVersion(level->yamyam_content[i].e[x][y],
6368 level->game_version);
6372 static void LoadLevel_InitCustomElements(struct LevelInfo *level)
6376 /* map custom element change events that have changed in newer versions
6377 (these following values were accidentally changed in version 3.0.1)
6378 (this seems to be needed only for 'ab_levelset3' and 'ab_levelset4') */
6379 if (level->game_version <= VERSION_IDENT(3,0,0,0))
6381 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6383 int element = EL_CUSTOM_START + i;
6385 /* order of checking and copying events to be mapped is important */
6386 /* (do not change the start and end value -- they are constant) */
6387 for (j = CE_BY_OTHER_ACTION; j >= CE_VALUE_GETS_ZERO; j--)
6389 if (HAS_CHANGE_EVENT(element, j - 2))
6391 SET_CHANGE_EVENT(element, j - 2, FALSE);
6392 SET_CHANGE_EVENT(element, j, TRUE);
6396 /* order of checking and copying events to be mapped is important */
6397 /* (do not change the start and end value -- they are constant) */
6398 for (j = CE_PLAYER_COLLECTS_X; j >= CE_HITTING_SOMETHING; j--)
6400 if (HAS_CHANGE_EVENT(element, j - 1))
6402 SET_CHANGE_EVENT(element, j - 1, FALSE);
6403 SET_CHANGE_EVENT(element, j, TRUE);
6409 /* initialize "can_change" field for old levels with only one change page */
6410 if (level->game_version <= VERSION_IDENT(3,0,2,0))
6412 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6414 int element = EL_CUSTOM_START + i;
6416 if (CAN_CHANGE(element))
6417 element_info[element].change->can_change = TRUE;
6421 /* correct custom element values (for old levels without these options) */
6422 if (level->game_version < VERSION_IDENT(3,1,1,0))
6424 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6426 int element = EL_CUSTOM_START + i;
6427 struct ElementInfo *ei = &element_info[element];
6429 if (ei->access_direction == MV_NO_DIRECTION)
6430 ei->access_direction = MV_ALL_DIRECTIONS;
6434 /* correct custom element values (fix invalid values for all versions) */
6437 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6439 int element = EL_CUSTOM_START + i;
6440 struct ElementInfo *ei = &element_info[element];
6442 for (j = 0; j < ei->num_change_pages; j++)
6444 struct ElementChangeInfo *change = &ei->change_page[j];
6446 if (change->trigger_player == CH_PLAYER_NONE)
6447 change->trigger_player = CH_PLAYER_ANY;
6449 if (change->trigger_side == CH_SIDE_NONE)
6450 change->trigger_side = CH_SIDE_ANY;
6455 /* initialize "can_explode" field for old levels which did not store this */
6456 /* !!! CHECK THIS -- "<= 3,1,0,0" IS PROBABLY WRONG !!! */
6457 if (level->game_version <= VERSION_IDENT(3,1,0,0))
6459 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6461 int element = EL_CUSTOM_START + i;
6463 if (EXPLODES_1X1_OLD(element))
6464 element_info[element].explosion_type = EXPLODES_1X1;
6466 SET_PROPERTY(element, EP_CAN_EXPLODE, (EXPLODES_BY_FIRE(element) ||
6467 EXPLODES_SMASHED(element) ||
6468 EXPLODES_IMPACT(element)));
6472 /* correct previously hard-coded move delay values for maze runner style */
6473 if (level->game_version < VERSION_IDENT(3,1,1,0))
6475 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6477 int element = EL_CUSTOM_START + i;
6479 if (element_info[element].move_pattern & MV_MAZE_RUNNER_STYLE)
6481 /* previously hard-coded and therefore ignored */
6482 element_info[element].move_delay_fixed = 9;
6483 element_info[element].move_delay_random = 0;
6488 /* set some other uninitialized values of custom elements in older levels */
6489 if (level->game_version < VERSION_IDENT(3,1,0,0))
6491 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6493 int element = EL_CUSTOM_START + i;
6495 element_info[element].access_direction = MV_ALL_DIRECTIONS;
6497 element_info[element].explosion_delay = 17;
6498 element_info[element].ignition_delay = 8;
6503 static void LoadLevel_InitElements(struct LevelInfo *level, char *filename)
6505 LoadLevel_InitStandardElements(level);
6507 if (level->file_has_custom_elements)
6508 LoadLevel_InitCustomElements(level);
6510 /* initialize element properties for level editor etc. */
6511 InitElementPropertiesEngine(level->game_version);
6512 InitElementPropertiesGfxElement();
6515 static void LoadLevel_InitPlayfield(struct LevelInfo *level, char *filename)
6519 /* map elements that have changed in newer versions */
6520 for (y = 0; y < level->fieldy; y++)
6521 for (x = 0; x < level->fieldx; x++)
6522 level->field[x][y] = getMappedElementByVersion(level->field[x][y],
6523 level->game_version);
6525 /* clear unused playfield data (nicer if level gets resized in editor) */
6526 for (x = 0; x < MAX_LEV_FIELDX; x++)
6527 for (y = 0; y < MAX_LEV_FIELDY; y++)
6528 if (x >= level->fieldx || y >= level->fieldy)
6529 level->field[x][y] = EL_EMPTY;
6531 /* copy elements to runtime playfield array */
6532 for (x = 0; x < MAX_LEV_FIELDX; x++)
6533 for (y = 0; y < MAX_LEV_FIELDY; y++)
6534 Feld[x][y] = level->field[x][y];
6536 /* initialize level size variables for faster access */
6537 lev_fieldx = level->fieldx;
6538 lev_fieldy = level->fieldy;
6540 /* determine border element for this level */
6541 if (level->file_info.type == LEVEL_FILE_TYPE_DC)
6542 BorderElement = EL_EMPTY; /* (in editor, SetBorderElement() is used) */
6547 static void LoadLevel_InitNativeEngines(struct LevelInfo *level,char *filename)
6549 struct LevelFileInfo *level_file_info = &level->file_info;
6551 if (level_file_info->type == LEVEL_FILE_TYPE_RND)
6552 CopyNativeLevel_RND_to_Native(level);
6555 void LoadLevelTemplate(int nr)
6559 setLevelFileInfo(&level_template.file_info, nr);
6560 filename = level_template.file_info.filename;
6562 LoadLevelFromFileInfo(&level_template, &level_template.file_info, FALSE);
6564 LoadLevel_InitVersion(&level_template, filename);
6565 LoadLevel_InitElements(&level_template, filename);
6567 ActivateLevelTemplate();
6570 void LoadLevel(int nr)
6574 setLevelFileInfo(&level.file_info, nr);
6575 filename = level.file_info.filename;
6577 LoadLevelFromFileInfo(&level, &level.file_info, FALSE);
6579 if (level.use_custom_template)
6580 LoadLevelTemplate(-1);
6582 LoadLevel_InitVersion(&level, filename);
6583 LoadLevel_InitElements(&level, filename);
6584 LoadLevel_InitPlayfield(&level, filename);
6586 LoadLevel_InitNativeEngines(&level, filename);
6589 void LoadLevelInfoOnly(int nr)
6591 setLevelFileInfo(&level.file_info, nr);
6593 LoadLevelFromFileInfo(&level, &level.file_info, TRUE);
6596 static int SaveLevel_VERS(FILE *file, struct LevelInfo *level)
6600 chunk_size += putFileVersion(file, level->file_version);
6601 chunk_size += putFileVersion(file, level->game_version);
6606 static int SaveLevel_DATE(FILE *file, struct LevelInfo *level)
6610 chunk_size += putFile16BitBE(file, level->creation_date.year);
6611 chunk_size += putFile8Bit(file, level->creation_date.month);
6612 chunk_size += putFile8Bit(file, level->creation_date.day);
6617 #if ENABLE_HISTORIC_CHUNKS
6618 static void SaveLevel_HEAD(FILE *file, struct LevelInfo *level)
6622 putFile8Bit(file, level->fieldx);
6623 putFile8Bit(file, level->fieldy);
6625 putFile16BitBE(file, level->time);
6626 putFile16BitBE(file, level->gems_needed);
6628 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
6629 putFile8Bit(file, level->name[i]);
6631 for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
6632 putFile8Bit(file, level->score[i]);
6634 for (i = 0; i < STD_ELEMENT_CONTENTS; i++)
6635 for (y = 0; y < 3; y++)
6636 for (x = 0; x < 3; x++)
6637 putFile8Bit(file, (level->encoding_16bit_yamyam ? EL_EMPTY :
6638 level->yamyam_content[i].e[x][y]));
6639 putFile8Bit(file, level->amoeba_speed);
6640 putFile8Bit(file, level->time_magic_wall);
6641 putFile8Bit(file, level->time_wheel);
6642 putFile8Bit(file, (level->encoding_16bit_amoeba ? EL_EMPTY :
6643 level->amoeba_content));
6644 putFile8Bit(file, (level->initial_player_stepsize == STEPSIZE_FAST ? 1 : 0));
6645 putFile8Bit(file, (level->initial_gravity ? 1 : 0));
6646 putFile8Bit(file, (level->encoding_16bit_field ? 1 : 0));
6647 putFile8Bit(file, (level->em_slippery_gems ? 1 : 0));
6649 putFile8Bit(file, (level->use_custom_template ? 1 : 0));
6651 putFile8Bit(file, (level->block_last_field ? 1 : 0));
6652 putFile8Bit(file, (level->sp_block_last_field ? 1 : 0));
6653 putFile32BitBE(file, level->can_move_into_acid_bits);
6654 putFile8Bit(file, level->dont_collide_with_bits);
6656 putFile8Bit(file, (level->use_spring_bug ? 1 : 0));
6657 putFile8Bit(file, (level->use_step_counter ? 1 : 0));
6659 putFile8Bit(file, (level->instant_relocation ? 1 : 0));
6660 putFile8Bit(file, (level->can_pass_to_walkable ? 1 : 0));
6661 putFile8Bit(file, (level->grow_into_diggable ? 1 : 0));
6663 putFile8Bit(file, level->game_engine_type);
6665 WriteUnusedBytesToFile(file, LEVEL_CHUNK_HEAD_UNUSED);
6669 static int SaveLevel_NAME(FILE *file, struct LevelInfo *level)
6674 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
6675 chunk_size += putFile8Bit(file, level->name[i]);
6680 static int SaveLevel_AUTH(FILE *file, struct LevelInfo *level)
6685 for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
6686 chunk_size += putFile8Bit(file, level->author[i]);
6691 #if ENABLE_HISTORIC_CHUNKS
6692 static int SaveLevel_BODY(FILE *file, struct LevelInfo *level)
6697 for (y = 0; y < level->fieldy; y++)
6698 for (x = 0; x < level->fieldx; x++)
6699 if (level->encoding_16bit_field)
6700 chunk_size += putFile16BitBE(file, level->field[x][y]);
6702 chunk_size += putFile8Bit(file, level->field[x][y]);
6708 static int SaveLevel_BODY(FILE *file, struct LevelInfo *level)
6713 for (y = 0; y < level->fieldy; y++)
6714 for (x = 0; x < level->fieldx; x++)
6715 chunk_size += putFile16BitBE(file, level->field[x][y]);
6720 #if ENABLE_HISTORIC_CHUNKS
6721 static void SaveLevel_CONT(FILE *file, struct LevelInfo *level)
6725 putFile8Bit(file, EL_YAMYAM);
6726 putFile8Bit(file, level->num_yamyam_contents);
6727 putFile8Bit(file, 0);
6728 putFile8Bit(file, 0);
6730 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
6731 for (y = 0; y < 3; y++)
6732 for (x = 0; x < 3; x++)
6733 if (level->encoding_16bit_field)
6734 putFile16BitBE(file, level->yamyam_content[i].e[x][y]);
6736 putFile8Bit(file, level->yamyam_content[i].e[x][y]);
6740 #if ENABLE_HISTORIC_CHUNKS
6741 static void SaveLevel_CNT2(FILE *file, struct LevelInfo *level, int element)
6744 int num_contents, content_xsize, content_ysize;
6745 int content_array[MAX_ELEMENT_CONTENTS][3][3];
6747 if (element == EL_YAMYAM)
6749 num_contents = level->num_yamyam_contents;
6753 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
6754 for (y = 0; y < 3; y++)
6755 for (x = 0; x < 3; x++)
6756 content_array[i][x][y] = level->yamyam_content[i].e[x][y];
6758 else if (element == EL_BD_AMOEBA)
6764 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
6765 for (y = 0; y < 3; y++)
6766 for (x = 0; x < 3; x++)
6767 content_array[i][x][y] = EL_EMPTY;
6768 content_array[0][0][0] = level->amoeba_content;
6772 /* chunk header already written -- write empty chunk data */
6773 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_SIZE);
6775 Error(ERR_WARN, "cannot save content for element '%d'", element);
6779 putFile16BitBE(file, element);
6780 putFile8Bit(file, num_contents);
6781 putFile8Bit(file, content_xsize);
6782 putFile8Bit(file, content_ysize);
6784 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_UNUSED);
6786 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
6787 for (y = 0; y < 3; y++)
6788 for (x = 0; x < 3; x++)
6789 putFile16BitBE(file, content_array[i][x][y]);
6793 #if ENABLE_HISTORIC_CHUNKS
6794 static int SaveLevel_CNT3(FILE *file, struct LevelInfo *level, int element)
6796 int envelope_nr = element - EL_ENVELOPE_1;
6797 int envelope_len = strlen(level->envelope_text[envelope_nr]) + 1;
6801 chunk_size += putFile16BitBE(file, element);
6802 chunk_size += putFile16BitBE(file, envelope_len);
6803 chunk_size += putFile8Bit(file, level->envelope_xsize[envelope_nr]);
6804 chunk_size += putFile8Bit(file, level->envelope_ysize[envelope_nr]);
6806 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT3_UNUSED);
6807 chunk_size += LEVEL_CHUNK_CNT3_UNUSED;
6809 for (i = 0; i < envelope_len; i++)
6810 chunk_size += putFile8Bit(file, level->envelope_text[envelope_nr][i]);
6816 #if ENABLE_HISTORIC_CHUNKS
6817 static void SaveLevel_CUS1(FILE *file, struct LevelInfo *level,
6818 int num_changed_custom_elements)
6822 putFile16BitBE(file, num_changed_custom_elements);
6824 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6826 int element = EL_CUSTOM_START + i;
6828 struct ElementInfo *ei = &element_info[element];
6830 if (ei->properties[EP_BITFIELD_BASE_NR] != EP_BITMASK_DEFAULT)
6832 if (check < num_changed_custom_elements)
6834 putFile16BitBE(file, element);
6835 putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
6842 if (check != num_changed_custom_elements) /* should not happen */
6843 Error(ERR_WARN, "inconsistent number of custom element properties");
6847 #if ENABLE_HISTORIC_CHUNKS
6848 static void SaveLevel_CUS2(FILE *file, struct LevelInfo *level,
6849 int num_changed_custom_elements)
6853 putFile16BitBE(file, num_changed_custom_elements);
6855 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6857 int element = EL_CUSTOM_START + i;
6859 if (element_info[element].change->target_element != EL_EMPTY_SPACE)
6861 if (check < num_changed_custom_elements)
6863 putFile16BitBE(file, element);
6864 putFile16BitBE(file, element_info[element].change->target_element);
6871 if (check != num_changed_custom_elements) /* should not happen */
6872 Error(ERR_WARN, "inconsistent number of custom target elements");
6876 #if ENABLE_HISTORIC_CHUNKS
6877 static void SaveLevel_CUS3(FILE *file, struct LevelInfo *level,
6878 int num_changed_custom_elements)
6880 int i, j, x, y, check = 0;
6882 putFile16BitBE(file, num_changed_custom_elements);
6884 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6886 int element = EL_CUSTOM_START + i;
6887 struct ElementInfo *ei = &element_info[element];
6889 if (ei->modified_settings)
6891 if (check < num_changed_custom_elements)
6893 putFile16BitBE(file, element);
6895 for (j = 0; j < MAX_ELEMENT_NAME_LEN; j++)
6896 putFile8Bit(file, ei->description[j]);
6898 putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
6900 /* some free bytes for future properties and padding */
6901 WriteUnusedBytesToFile(file, 7);
6903 putFile8Bit(file, ei->use_gfx_element);
6904 putFile16BitBE(file, ei->gfx_element_initial);
6906 putFile8Bit(file, ei->collect_score_initial);
6907 putFile8Bit(file, ei->collect_count_initial);
6909 putFile16BitBE(file, ei->push_delay_fixed);
6910 putFile16BitBE(file, ei->push_delay_random);
6911 putFile16BitBE(file, ei->move_delay_fixed);
6912 putFile16BitBE(file, ei->move_delay_random);
6914 putFile16BitBE(file, ei->move_pattern);
6915 putFile8Bit(file, ei->move_direction_initial);
6916 putFile8Bit(file, ei->move_stepsize);
6918 for (y = 0; y < 3; y++)
6919 for (x = 0; x < 3; x++)
6920 putFile16BitBE(file, ei->content.e[x][y]);
6922 putFile32BitBE(file, ei->change->events);
6924 putFile16BitBE(file, ei->change->target_element);
6926 putFile16BitBE(file, ei->change->delay_fixed);
6927 putFile16BitBE(file, ei->change->delay_random);
6928 putFile16BitBE(file, ei->change->delay_frames);
6930 putFile16BitBE(file, ei->change->initial_trigger_element);
6932 putFile8Bit(file, ei->change->explode);
6933 putFile8Bit(file, ei->change->use_target_content);
6934 putFile8Bit(file, ei->change->only_if_complete);
6935 putFile8Bit(file, ei->change->use_random_replace);
6937 putFile8Bit(file, ei->change->random_percentage);
6938 putFile8Bit(file, ei->change->replace_when);
6940 for (y = 0; y < 3; y++)
6941 for (x = 0; x < 3; x++)
6942 putFile16BitBE(file, ei->change->content.e[x][y]);
6944 putFile8Bit(file, ei->slippery_type);
6946 /* some free bytes for future properties and padding */
6947 WriteUnusedBytesToFile(file, LEVEL_CPART_CUS3_UNUSED);
6954 if (check != num_changed_custom_elements) /* should not happen */
6955 Error(ERR_WARN, "inconsistent number of custom element properties");
6959 #if ENABLE_HISTORIC_CHUNKS
6960 static void SaveLevel_CUS4(FILE *file, struct LevelInfo *level, int element)
6962 struct ElementInfo *ei = &element_info[element];
6965 /* ---------- custom element base property values (96 bytes) ------------- */
6967 putFile16BitBE(file, element);
6969 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
6970 putFile8Bit(file, ei->description[i]);
6972 putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
6974 WriteUnusedBytesToFile(file, 4); /* reserved for more base properties */
6976 putFile8Bit(file, ei->num_change_pages);
6978 putFile16BitBE(file, ei->ce_value_fixed_initial);
6979 putFile16BitBE(file, ei->ce_value_random_initial);
6980 putFile8Bit(file, ei->use_last_ce_value);
6982 putFile8Bit(file, ei->use_gfx_element);
6983 putFile16BitBE(file, ei->gfx_element_initial);
6985 putFile8Bit(file, ei->collect_score_initial);
6986 putFile8Bit(file, ei->collect_count_initial);
6988 putFile8Bit(file, ei->drop_delay_fixed);
6989 putFile8Bit(file, ei->push_delay_fixed);
6990 putFile8Bit(file, ei->drop_delay_random);
6991 putFile8Bit(file, ei->push_delay_random);
6992 putFile16BitBE(file, ei->move_delay_fixed);
6993 putFile16BitBE(file, ei->move_delay_random);
6995 /* bits 0 - 15 of "move_pattern" ... */
6996 putFile16BitBE(file, ei->move_pattern & 0xffff);
6997 putFile8Bit(file, ei->move_direction_initial);
6998 putFile8Bit(file, ei->move_stepsize);
7000 putFile8Bit(file, ei->slippery_type);
7002 for (y = 0; y < 3; y++)
7003 for (x = 0; x < 3; x++)
7004 putFile16BitBE(file, ei->content.e[x][y]);
7006 putFile16BitBE(file, ei->move_enter_element);
7007 putFile16BitBE(file, ei->move_leave_element);
7008 putFile8Bit(file, ei->move_leave_type);
7010 /* ... bits 16 - 31 of "move_pattern" (not nice, but downward compatible) */
7011 putFile16BitBE(file, (ei->move_pattern >> 16) & 0xffff);
7013 putFile8Bit(file, ei->access_direction);
7015 putFile8Bit(file, ei->explosion_delay);
7016 putFile8Bit(file, ei->ignition_delay);
7017 putFile8Bit(file, ei->explosion_type);
7019 /* some free bytes for future custom property values and padding */
7020 WriteUnusedBytesToFile(file, 1);
7022 /* ---------- change page property values (48 bytes) --------------------- */
7024 for (i = 0; i < ei->num_change_pages; i++)
7026 struct ElementChangeInfo *change = &ei->change_page[i];
7027 unsigned int event_bits;
7029 /* bits 0 - 31 of "has_event[]" ... */
7031 for (j = 0; j < MIN(NUM_CHANGE_EVENTS, 32); j++)
7032 if (change->has_event[j])
7033 event_bits |= (1 << j);
7034 putFile32BitBE(file, event_bits);
7036 putFile16BitBE(file, change->target_element);
7038 putFile16BitBE(file, change->delay_fixed);
7039 putFile16BitBE(file, change->delay_random);
7040 putFile16BitBE(file, change->delay_frames);
7042 putFile16BitBE(file, change->initial_trigger_element);
7044 putFile8Bit(file, change->explode);
7045 putFile8Bit(file, change->use_target_content);
7046 putFile8Bit(file, change->only_if_complete);
7047 putFile8Bit(file, change->use_random_replace);
7049 putFile8Bit(file, change->random_percentage);
7050 putFile8Bit(file, change->replace_when);
7052 for (y = 0; y < 3; y++)
7053 for (x = 0; x < 3; x++)
7054 putFile16BitBE(file, change->target_content.e[x][y]);
7056 putFile8Bit(file, change->can_change);
7058 putFile8Bit(file, change->trigger_side);
7060 putFile8Bit(file, change->trigger_player);
7061 putFile8Bit(file, (change->trigger_page == CH_PAGE_ANY ? CH_PAGE_ANY_FILE :
7062 log_2(change->trigger_page)));
7064 putFile8Bit(file, change->has_action);
7065 putFile8Bit(file, change->action_type);
7066 putFile8Bit(file, change->action_mode);
7067 putFile16BitBE(file, change->action_arg);
7069 /* ... bits 32 - 39 of "has_event[]" (not nice, but downward compatible) */
7071 for (j = 32; j < NUM_CHANGE_EVENTS; j++)
7072 if (change->has_event[j])
7073 event_bits |= (1 << (j - 32));
7074 putFile8Bit(file, event_bits);
7079 #if ENABLE_HISTORIC_CHUNKS
7080 static void SaveLevel_GRP1(FILE *file, struct LevelInfo *level, int element)
7082 struct ElementInfo *ei = &element_info[element];
7083 struct ElementGroupInfo *group = ei->group;
7086 putFile16BitBE(file, element);
7088 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
7089 putFile8Bit(file, ei->description[i]);
7091 putFile8Bit(file, group->num_elements);
7093 putFile8Bit(file, ei->use_gfx_element);
7094 putFile16BitBE(file, ei->gfx_element_initial);
7096 putFile8Bit(file, group->choice_mode);
7098 /* some free bytes for future values and padding */
7099 WriteUnusedBytesToFile(file, 3);
7101 for (i = 0; i < MAX_ELEMENTS_IN_GROUP; i++)
7102 putFile16BitBE(file, group->element[i]);
7106 static int SaveLevel_MicroChunk(FILE *file, struct LevelFileConfigInfo *entry,
7107 boolean write_element)
7109 int save_type = entry->save_type;
7110 int data_type = entry->data_type;
7111 int conf_type = entry->conf_type;
7112 int byte_mask = conf_type & CONF_MASK_BYTES;
7113 int element = entry->element;
7114 int default_value = entry->default_value;
7116 boolean modified = FALSE;
7118 if (byte_mask != CONF_MASK_MULTI_BYTES)
7120 void *value_ptr = entry->value;
7121 int value = (data_type == TYPE_BOOLEAN ? *(boolean *)value_ptr :
7124 /* check if any settings have been modified before saving them */
7125 if (value != default_value)
7128 /* do not save if explicitly told or if unmodified default settings */
7129 if ((save_type == SAVE_CONF_NEVER) ||
7130 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
7134 num_bytes += putFile16BitBE(file, element);
7136 num_bytes += putFile8Bit(file, conf_type);
7137 num_bytes += (byte_mask == CONF_MASK_1_BYTE ? putFile8Bit (file, value) :
7138 byte_mask == CONF_MASK_2_BYTE ? putFile16BitBE(file, value) :
7139 byte_mask == CONF_MASK_4_BYTE ? putFile32BitBE(file, value) :
7142 else if (data_type == TYPE_STRING)
7144 char *default_string = entry->default_string;
7145 char *string = (char *)(entry->value);
7146 int string_length = strlen(string);
7149 /* check if any settings have been modified before saving them */
7150 if (!strEqual(string, default_string))
7153 /* do not save if explicitly told or if unmodified default settings */
7154 if ((save_type == SAVE_CONF_NEVER) ||
7155 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
7159 num_bytes += putFile16BitBE(file, element);
7161 num_bytes += putFile8Bit(file, conf_type);
7162 num_bytes += putFile16BitBE(file, string_length);
7164 for (i = 0; i < string_length; i++)
7165 num_bytes += putFile8Bit(file, string[i]);
7167 else if (data_type == TYPE_ELEMENT_LIST)
7169 int *element_array = (int *)(entry->value);
7170 int num_elements = *(int *)(entry->num_entities);
7173 /* check if any settings have been modified before saving them */
7174 for (i = 0; i < num_elements; i++)
7175 if (element_array[i] != default_value)
7178 /* do not save if explicitly told or if unmodified default settings */
7179 if ((save_type == SAVE_CONF_NEVER) ||
7180 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
7184 num_bytes += putFile16BitBE(file, element);
7186 num_bytes += putFile8Bit(file, conf_type);
7187 num_bytes += putFile16BitBE(file, num_elements * CONF_ELEMENT_NUM_BYTES);
7189 for (i = 0; i < num_elements; i++)
7190 num_bytes += putFile16BitBE(file, element_array[i]);
7192 else if (data_type == TYPE_CONTENT_LIST)
7194 struct Content *content = (struct Content *)(entry->value);
7195 int num_contents = *(int *)(entry->num_entities);
7198 /* check if any settings have been modified before saving them */
7199 for (i = 0; i < num_contents; i++)
7200 for (y = 0; y < 3; y++)
7201 for (x = 0; x < 3; x++)
7202 if (content[i].e[x][y] != default_value)
7205 /* do not save if explicitly told or if unmodified default settings */
7206 if ((save_type == SAVE_CONF_NEVER) ||
7207 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
7211 num_bytes += putFile16BitBE(file, element);
7213 num_bytes += putFile8Bit(file, conf_type);
7214 num_bytes += putFile16BitBE(file, num_contents * CONF_CONTENT_NUM_BYTES);
7216 for (i = 0; i < num_contents; i++)
7217 for (y = 0; y < 3; y++)
7218 for (x = 0; x < 3; x++)
7219 num_bytes += putFile16BitBE(file, content[i].e[x][y]);
7225 static int SaveLevel_INFO(FILE *file, struct LevelInfo *level)
7230 li = *level; /* copy level data into temporary buffer */
7232 for (i = 0; chunk_config_INFO[i].data_type != -1; i++)
7233 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_INFO[i], FALSE);
7238 static int SaveLevel_ELEM(FILE *file, struct LevelInfo *level)
7243 li = *level; /* copy level data into temporary buffer */
7245 for (i = 0; chunk_config_ELEM[i].data_type != -1; i++)
7246 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_ELEM[i], TRUE);
7251 static int SaveLevel_NOTE(FILE *file, struct LevelInfo *level, int element)
7253 int envelope_nr = element - EL_ENVELOPE_1;
7257 chunk_size += putFile16BitBE(file, element);
7259 /* copy envelope data into temporary buffer */
7260 xx_envelope = level->envelope[envelope_nr];
7262 for (i = 0; chunk_config_NOTE[i].data_type != -1; i++)
7263 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_NOTE[i], FALSE);
7268 static int SaveLevel_CUSX(FILE *file, struct LevelInfo *level, int element)
7270 struct ElementInfo *ei = &element_info[element];
7274 chunk_size += putFile16BitBE(file, element);
7276 xx_ei = *ei; /* copy element data into temporary buffer */
7278 /* set default description string for this specific element */
7279 strcpy(xx_default_description, getDefaultElementDescription(ei));
7281 for (i = 0; chunk_config_CUSX_base[i].data_type != -1; i++)
7282 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_CUSX_base[i], FALSE);
7284 for (i = 0; i < ei->num_change_pages; i++)
7286 struct ElementChangeInfo *change = &ei->change_page[i];
7288 xx_current_change_page = i;
7290 xx_change = *change; /* copy change data into temporary buffer */
7293 setEventBitsFromEventFlags(change);
7295 for (j = 0; chunk_config_CUSX_change[j].data_type != -1; j++)
7296 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_CUSX_change[j],
7303 static int SaveLevel_GRPX(FILE *file, struct LevelInfo *level, int element)
7305 struct ElementInfo *ei = &element_info[element];
7306 struct ElementGroupInfo *group = ei->group;
7310 chunk_size += putFile16BitBE(file, element);
7312 xx_ei = *ei; /* copy element data into temporary buffer */
7313 xx_group = *group; /* copy group data into temporary buffer */
7315 /* set default description string for this specific element */
7316 strcpy(xx_default_description, getDefaultElementDescription(ei));
7318 for (i = 0; chunk_config_GRPX[i].data_type != -1; i++)
7319 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_GRPX[i], FALSE);
7324 static void SaveLevelFromFilename(struct LevelInfo *level, char *filename,
7325 boolean save_as_template)
7331 if (!(file = fopen(filename, MODE_WRITE)))
7333 Error(ERR_WARN, "cannot save level file '%s'", filename);
7337 level->file_version = FILE_VERSION_ACTUAL;
7338 level->game_version = GAME_VERSION_ACTUAL;
7340 level->creation_date = getCurrentDate();
7342 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
7343 putFileChunkBE(file, "CAVE", CHUNK_SIZE_NONE);
7345 chunk_size = SaveLevel_VERS(NULL, level);
7346 putFileChunkBE(file, "VERS", chunk_size);
7347 SaveLevel_VERS(file, level);
7349 chunk_size = SaveLevel_DATE(NULL, level);
7350 putFileChunkBE(file, "DATE", chunk_size);
7351 SaveLevel_DATE(file, level);
7353 chunk_size = SaveLevel_NAME(NULL, level);
7354 putFileChunkBE(file, "NAME", chunk_size);
7355 SaveLevel_NAME(file, level);
7357 chunk_size = SaveLevel_AUTH(NULL, level);
7358 putFileChunkBE(file, "AUTH", chunk_size);
7359 SaveLevel_AUTH(file, level);
7361 chunk_size = SaveLevel_INFO(NULL, level);
7362 putFileChunkBE(file, "INFO", chunk_size);
7363 SaveLevel_INFO(file, level);
7365 chunk_size = SaveLevel_BODY(NULL, level);
7366 putFileChunkBE(file, "BODY", chunk_size);
7367 SaveLevel_BODY(file, level);
7369 chunk_size = SaveLevel_ELEM(NULL, level);
7370 if (chunk_size > LEVEL_CHUNK_ELEM_UNCHANGED) /* save if changed */
7372 putFileChunkBE(file, "ELEM", chunk_size);
7373 SaveLevel_ELEM(file, level);
7376 for (i = 0; i < NUM_ENVELOPES; i++)
7378 int element = EL_ENVELOPE_1 + i;
7380 chunk_size = SaveLevel_NOTE(NULL, level, element);
7381 if (chunk_size > LEVEL_CHUNK_NOTE_UNCHANGED) /* save if changed */
7383 putFileChunkBE(file, "NOTE", chunk_size);
7384 SaveLevel_NOTE(file, level, element);
7388 /* if not using template level, check for non-default custom/group elements */
7389 if (!level->use_custom_template || save_as_template)
7391 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7393 int element = EL_CUSTOM_START + i;
7395 chunk_size = SaveLevel_CUSX(NULL, level, element);
7396 if (chunk_size > LEVEL_CHUNK_CUSX_UNCHANGED) /* save if changed */
7398 putFileChunkBE(file, "CUSX", chunk_size);
7399 SaveLevel_CUSX(file, level, element);
7403 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
7405 int element = EL_GROUP_START + i;
7407 chunk_size = SaveLevel_GRPX(NULL, level, element);
7408 if (chunk_size > LEVEL_CHUNK_GRPX_UNCHANGED) /* save if changed */
7410 putFileChunkBE(file, "GRPX", chunk_size);
7411 SaveLevel_GRPX(file, level, element);
7418 SetFilePermissions(filename, PERMS_PRIVATE);
7421 void SaveLevel(int nr)
7423 char *filename = getDefaultLevelFilename(nr);
7425 SaveLevelFromFilename(&level, filename, FALSE);
7428 void SaveLevelTemplate()
7430 char *filename = getLocalLevelTemplateFilename();
7432 SaveLevelFromFilename(&level, filename, TRUE);
7435 boolean SaveLevelChecked(int nr)
7437 char *filename = getDefaultLevelFilename(nr);
7438 boolean new_level = !fileExists(filename);
7439 boolean level_saved = FALSE;
7441 if (new_level || Request("Save this level and kill the old?", REQ_ASK))
7446 Request("Level saved!", REQ_CONFIRM);
7454 void DumpLevel(struct LevelInfo *level)
7456 if (level->no_level_file || level->no_valid_file)
7458 Error(ERR_WARN, "cannot dump -- no valid level file found");
7464 Print("Level xxx (file version %08d, game version %08d)\n",
7465 level->file_version, level->game_version);
7468 Print("Level author: '%s'\n", level->author);
7469 Print("Level title: '%s'\n", level->name);
7471 Print("Playfield size: %d x %d\n", level->fieldx, level->fieldy);
7473 Print("Level time: %d seconds\n", level->time);
7474 Print("Gems needed: %d\n", level->gems_needed);
7476 Print("Time for magic wall: %d seconds\n", level->time_magic_wall);
7477 Print("Time for wheel: %d seconds\n", level->time_wheel);
7478 Print("Time for light: %d seconds\n", level->time_light);
7479 Print("Time for timegate: %d seconds\n", level->time_timegate);
7481 Print("Amoeba speed: %d\n", level->amoeba_speed);
7484 Print("EM style slippery gems: %s\n", (level->em_slippery_gems ? "yes" : "no"));
7485 Print("Player blocks last field: %s\n", (level->block_last_field ? "yes" : "no"));
7486 Print("SP player blocks last field: %s\n", (level->sp_block_last_field ? "yes" : "no"));
7487 Print("use spring bug: %s\n", (level->use_spring_bug ? "yes" : "no"));
7488 Print("use step counter: %s\n", (level->use_step_counter ? "yes" : "no"));
7494 /* ========================================================================= */
7495 /* tape file functions */
7496 /* ========================================================================= */
7498 static void setTapeInfoToDefaults()
7502 /* always start with reliable default values (empty tape) */
7505 /* default values (also for pre-1.2 tapes) with only the first player */
7506 tape.player_participates[0] = TRUE;
7507 for (i = 1; i < MAX_PLAYERS; i++)
7508 tape.player_participates[i] = FALSE;
7510 /* at least one (default: the first) player participates in every tape */
7511 tape.num_participating_players = 1;
7513 tape.level_nr = level_nr;
7515 tape.changed = FALSE;
7517 tape.recording = FALSE;
7518 tape.playing = FALSE;
7519 tape.pausing = FALSE;
7521 tape.no_valid_file = FALSE;
7524 static int LoadTape_VERS(File *file, int chunk_size, struct TapeInfo *tape)
7526 tape->file_version = getFileVersion(file);
7527 tape->game_version = getFileVersion(file);
7532 static int LoadTape_HEAD(File *file, int chunk_size, struct TapeInfo *tape)
7536 tape->random_seed = getFile32BitBE(file);
7537 tape->date = getFile32BitBE(file);
7538 tape->length = getFile32BitBE(file);
7540 /* read header fields that are new since version 1.2 */
7541 if (tape->file_version >= FILE_VERSION_1_2)
7543 byte store_participating_players = getFile8Bit(file);
7546 /* since version 1.2, tapes store which players participate in the tape */
7547 tape->num_participating_players = 0;
7548 for (i = 0; i < MAX_PLAYERS; i++)
7550 tape->player_participates[i] = FALSE;
7552 if (store_participating_players & (1 << i))
7554 tape->player_participates[i] = TRUE;
7555 tape->num_participating_players++;
7559 tape->use_mouse = (getFile8Bit(file) == 1 ? TRUE : FALSE);
7561 ReadUnusedBytesFromFile(file, TAPE_CHUNK_HEAD_UNUSED);
7563 engine_version = getFileVersion(file);
7564 if (engine_version > 0)
7565 tape->engine_version = engine_version;
7567 tape->engine_version = tape->game_version;
7573 static int LoadTape_INFO(File *file, int chunk_size, struct TapeInfo *tape)
7575 int level_identifier_size;
7578 level_identifier_size = getFile16BitBE(file);
7580 tape->level_identifier =
7581 checked_realloc(tape->level_identifier, level_identifier_size);
7583 for (i = 0; i < level_identifier_size; i++)
7584 tape->level_identifier[i] = getFile8Bit(file);
7586 tape->level_nr = getFile16BitBE(file);
7588 chunk_size = 2 + level_identifier_size + 2;
7593 static int LoadTape_BODY(File *file, int chunk_size, struct TapeInfo *tape)
7597 (tape->use_mouse ? 3 : tape->num_participating_players) + 1;
7598 int chunk_size_expected = tape_pos_size * tape->length;
7600 if (chunk_size_expected != chunk_size)
7602 ReadUnusedBytesFromFile(file, chunk_size);
7603 return chunk_size_expected;
7606 for (i = 0; i < tape->length; i++)
7608 if (i >= MAX_TAPE_LEN)
7610 Error(ERR_WARN, "tape truncated -- size exceeds maximum tape size %d",
7613 // tape too large; read and ignore remaining tape data from this chunk
7614 for (;i < tape->length; i++)
7615 ReadUnusedBytesFromFile(file, tape->num_participating_players + 1);
7620 if (tape->use_mouse)
7622 tape->pos[i].action[TAPE_ACTION_LX] = getFile8Bit(file);
7623 tape->pos[i].action[TAPE_ACTION_LY] = getFile8Bit(file);
7624 tape->pos[i].action[TAPE_ACTION_BUTTON] = getFile8Bit(file);
7626 tape->pos[i].action[TAPE_ACTION_UNUSED] = 0;
7630 for (j = 0; j < MAX_PLAYERS; j++)
7632 tape->pos[i].action[j] = MV_NONE;
7634 if (tape->player_participates[j])
7635 tape->pos[i].action[j] = getFile8Bit(file);
7639 tape->pos[i].delay = getFile8Bit(file);
7641 if (tape->file_version == FILE_VERSION_1_0)
7643 /* eliminate possible diagonal moves in old tapes */
7644 /* this is only for backward compatibility */
7646 byte joy_dir[4] = { JOY_LEFT, JOY_RIGHT, JOY_UP, JOY_DOWN };
7647 byte action = tape->pos[i].action[0];
7648 int k, num_moves = 0;
7650 for (k = 0; k<4; k++)
7652 if (action & joy_dir[k])
7654 tape->pos[i + num_moves].action[0] = joy_dir[k];
7656 tape->pos[i + num_moves].delay = 0;
7665 tape->length += num_moves;
7668 else if (tape->file_version < FILE_VERSION_2_0)
7670 /* convert pre-2.0 tapes to new tape format */
7672 if (tape->pos[i].delay > 1)
7675 tape->pos[i + 1] = tape->pos[i];
7676 tape->pos[i + 1].delay = 1;
7679 for (j = 0; j < MAX_PLAYERS; j++)
7680 tape->pos[i].action[j] = MV_NONE;
7681 tape->pos[i].delay--;
7688 if (checkEndOfFile(file))
7692 if (i != tape->length)
7693 chunk_size = tape_pos_size * i;
7698 void LoadTape_SokobanSolution(char *filename)
7701 int move_delay = TILESIZE / level.initial_player_stepsize[0];
7703 if (!(file = openFile(filename, MODE_READ)))
7705 tape.no_valid_file = TRUE;
7710 while (!checkEndOfFile(file))
7712 unsigned char c = getByteFromFile(file);
7714 if (checkEndOfFile(file))
7721 tape.pos[tape.length].action[0] = MV_UP;
7722 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
7728 tape.pos[tape.length].action[0] = MV_DOWN;
7729 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
7735 tape.pos[tape.length].action[0] = MV_LEFT;
7736 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
7742 tape.pos[tape.length].action[0] = MV_RIGHT;
7743 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
7751 /* ignore white-space characters */
7755 tape.no_valid_file = TRUE;
7757 Error(ERR_WARN, "unsupported Sokoban solution file '%s' ['%d']", filename, c);
7765 if (tape.no_valid_file)
7768 tape.length_frames = GetTapeLengthFrames();
7769 tape.length_seconds = GetTapeLengthSeconds();
7772 void LoadTapeFromFilename(char *filename)
7774 char cookie[MAX_LINE_LEN];
7775 char chunk_name[CHUNK_ID_LEN + 1];
7779 /* always start with reliable default values */
7780 setTapeInfoToDefaults();
7782 if (strSuffix(filename, ".sln"))
7784 LoadTape_SokobanSolution(filename);
7789 if (!(file = openFile(filename, MODE_READ)))
7791 tape.no_valid_file = TRUE;
7796 getFileChunkBE(file, chunk_name, NULL);
7797 if (strEqual(chunk_name, "RND1"))
7799 getFile32BitBE(file); /* not used */
7801 getFileChunkBE(file, chunk_name, NULL);
7802 if (!strEqual(chunk_name, "TAPE"))
7804 tape.no_valid_file = TRUE;
7806 Error(ERR_WARN, "unknown format of tape file '%s'", filename);
7813 else /* check for pre-2.0 file format with cookie string */
7815 strcpy(cookie, chunk_name);
7816 if (getStringFromFile(file, &cookie[4], MAX_LINE_LEN - 4) == NULL)
7818 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
7819 cookie[strlen(cookie) - 1] = '\0';
7821 if (!checkCookieString(cookie, TAPE_COOKIE_TMPL))
7823 tape.no_valid_file = TRUE;
7825 Error(ERR_WARN, "unknown format of tape file '%s'", filename);
7832 if ((tape.file_version = getFileVersionFromCookieString(cookie)) == -1)
7834 tape.no_valid_file = TRUE;
7836 Error(ERR_WARN, "unsupported version of tape file '%s'", filename);
7843 /* pre-2.0 tape files have no game version, so use file version here */
7844 tape.game_version = tape.file_version;
7847 if (tape.file_version < FILE_VERSION_1_2)
7849 /* tape files from versions before 1.2.0 without chunk structure */
7850 LoadTape_HEAD(file, TAPE_CHUNK_HEAD_SIZE, &tape);
7851 LoadTape_BODY(file, 2 * tape.length, &tape);
7859 int (*loader)(File *, int, struct TapeInfo *);
7863 { "VERS", TAPE_CHUNK_VERS_SIZE, LoadTape_VERS },
7864 { "HEAD", TAPE_CHUNK_HEAD_SIZE, LoadTape_HEAD },
7865 { "INFO", -1, LoadTape_INFO },
7866 { "BODY", -1, LoadTape_BODY },
7870 while (getFileChunkBE(file, chunk_name, &chunk_size))
7874 while (chunk_info[i].name != NULL &&
7875 !strEqual(chunk_name, chunk_info[i].name))
7878 if (chunk_info[i].name == NULL)
7880 Error(ERR_WARN, "unknown chunk '%s' in tape file '%s'",
7881 chunk_name, filename);
7882 ReadUnusedBytesFromFile(file, chunk_size);
7884 else if (chunk_info[i].size != -1 &&
7885 chunk_info[i].size != chunk_size)
7887 Error(ERR_WARN, "wrong size (%d) of chunk '%s' in tape file '%s'",
7888 chunk_size, chunk_name, filename);
7889 ReadUnusedBytesFromFile(file, chunk_size);
7893 /* call function to load this tape chunk */
7894 int chunk_size_expected =
7895 (chunk_info[i].loader)(file, chunk_size, &tape);
7897 /* the size of some chunks cannot be checked before reading other
7898 chunks first (like "HEAD" and "BODY") that contain some header
7899 information, so check them here */
7900 if (chunk_size_expected != chunk_size)
7902 Error(ERR_WARN, "wrong size (%d) of chunk '%s' in tape file '%s'",
7903 chunk_size, chunk_name, filename);
7911 tape.length_frames = GetTapeLengthFrames();
7912 tape.length_seconds = GetTapeLengthSeconds();
7915 printf("::: tape file version: %d\n", tape.file_version);
7916 printf("::: tape game version: %d\n", tape.game_version);
7917 printf("::: tape engine version: %d\n", tape.engine_version);
7921 void LoadTape(int nr)
7923 char *filename = getTapeFilename(nr);
7925 LoadTapeFromFilename(filename);
7928 void LoadSolutionTape(int nr)
7930 char *filename = getSolutionTapeFilename(nr);
7932 LoadTapeFromFilename(filename);
7934 if (TAPE_IS_EMPTY(tape) &&
7935 level.game_engine_type == GAME_ENGINE_TYPE_SP &&
7936 level.native_sp_level->demo.is_available)
7937 CopyNativeTape_SP_to_RND(&level);
7940 static void SaveTape_VERS(FILE *file, struct TapeInfo *tape)
7942 putFileVersion(file, tape->file_version);
7943 putFileVersion(file, tape->game_version);
7946 static void SaveTape_HEAD(FILE *file, struct TapeInfo *tape)
7949 byte store_participating_players = 0;
7951 /* set bits for participating players for compact storage */
7952 for (i = 0; i < MAX_PLAYERS; i++)
7953 if (tape->player_participates[i])
7954 store_participating_players |= (1 << i);
7956 putFile32BitBE(file, tape->random_seed);
7957 putFile32BitBE(file, tape->date);
7958 putFile32BitBE(file, tape->length);
7960 putFile8Bit(file, store_participating_players);
7962 putFile8Bit(file, (tape->use_mouse ? 1 : 0));
7964 /* unused bytes not at the end here for 4-byte alignment of engine_version */
7965 WriteUnusedBytesToFile(file, TAPE_CHUNK_HEAD_UNUSED);
7967 putFileVersion(file, tape->engine_version);
7970 static void SaveTape_INFO(FILE *file, struct TapeInfo *tape)
7972 int level_identifier_size = strlen(tape->level_identifier) + 1;
7975 putFile16BitBE(file, level_identifier_size);
7977 for (i = 0; i < level_identifier_size; i++)
7978 putFile8Bit(file, tape->level_identifier[i]);
7980 putFile16BitBE(file, tape->level_nr);
7983 static void SaveTape_BODY(FILE *file, struct TapeInfo *tape)
7987 for (i = 0; i < tape->length; i++)
7989 if (tape->use_mouse)
7991 putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_LX]);
7992 putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_LY]);
7993 putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_BUTTON]);
7997 for (j = 0; j < MAX_PLAYERS; j++)
7998 if (tape->player_participates[j])
7999 putFile8Bit(file, tape->pos[i].action[j]);
8002 putFile8Bit(file, tape->pos[i].delay);
8006 void SaveTape(int nr)
8008 char *filename = getTapeFilename(nr);
8010 int num_participating_players = 0;
8012 int info_chunk_size;
8013 int body_chunk_size;
8016 InitTapeDirectory(leveldir_current->subdir);
8018 if (!(file = fopen(filename, MODE_WRITE)))
8020 Error(ERR_WARN, "cannot save level recording file '%s'", filename);
8024 tape.file_version = FILE_VERSION_ACTUAL;
8025 tape.game_version = GAME_VERSION_ACTUAL;
8027 /* count number of participating players */
8028 for (i = 0; i < MAX_PLAYERS; i++)
8029 if (tape.player_participates[i])
8030 num_participating_players++;
8032 tape_pos_size = (tape.use_mouse ? 3 : num_participating_players) + 1;
8034 info_chunk_size = 2 + (strlen(tape.level_identifier) + 1) + 2;
8035 body_chunk_size = tape_pos_size * tape.length;
8037 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
8038 putFileChunkBE(file, "TAPE", CHUNK_SIZE_NONE);
8040 putFileChunkBE(file, "VERS", TAPE_CHUNK_VERS_SIZE);
8041 SaveTape_VERS(file, &tape);
8043 putFileChunkBE(file, "HEAD", TAPE_CHUNK_HEAD_SIZE);
8044 SaveTape_HEAD(file, &tape);
8046 putFileChunkBE(file, "INFO", info_chunk_size);
8047 SaveTape_INFO(file, &tape);
8049 putFileChunkBE(file, "BODY", body_chunk_size);
8050 SaveTape_BODY(file, &tape);
8054 SetFilePermissions(filename, PERMS_PRIVATE);
8056 tape.changed = FALSE;
8059 boolean SaveTapeChecked(int nr)
8061 char *filename = getTapeFilename(nr);
8062 boolean new_tape = !fileExists(filename);
8063 boolean tape_saved = FALSE;
8065 if (new_tape || Request("Replace old tape?", REQ_ASK))
8070 Request("Tape saved!", REQ_CONFIRM);
8078 void DumpTape(struct TapeInfo *tape)
8080 int tape_frame_counter;
8083 if (tape->no_valid_file)
8085 Error(ERR_WARN, "cannot dump -- no valid tape file found");
8091 Print("Tape of Level %03d (file version %08d, game version %08d)\n",
8092 tape->level_nr, tape->file_version, tape->game_version);
8093 Print(" (effective engine version %08d)\n",
8094 tape->engine_version);
8095 Print("Level series identifier: '%s'\n", tape->level_identifier);
8098 tape_frame_counter = 0;
8100 for (i = 0; i < tape->length; i++)
8102 if (i >= MAX_TAPE_LEN)
8107 for (j = 0; j < MAX_PLAYERS; j++)
8109 if (tape->player_participates[j])
8111 int action = tape->pos[i].action[j];
8113 Print("%d:%02x ", j, action);
8114 Print("[%c%c%c%c|%c%c] - ",
8115 (action & JOY_LEFT ? '<' : ' '),
8116 (action & JOY_RIGHT ? '>' : ' '),
8117 (action & JOY_UP ? '^' : ' '),
8118 (action & JOY_DOWN ? 'v' : ' '),
8119 (action & JOY_BUTTON_1 ? '1' : ' '),
8120 (action & JOY_BUTTON_2 ? '2' : ' '));
8124 Print("(%03d) ", tape->pos[i].delay);
8125 Print("[%05d]\n", tape_frame_counter);
8127 tape_frame_counter += tape->pos[i].delay;
8134 /* ========================================================================= */
8135 /* score file functions */
8136 /* ========================================================================= */
8138 void LoadScore(int nr)
8141 char *filename = getScoreFilename(nr);
8142 char cookie[MAX_LINE_LEN];
8143 char line[MAX_LINE_LEN];
8147 /* always start with reliable default values */
8148 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
8150 strcpy(highscore[i].Name, EMPTY_PLAYER_NAME);
8151 highscore[i].Score = 0;
8154 if (!(file = fopen(filename, MODE_READ)))
8157 /* check file identifier */
8158 if (fgets(cookie, MAX_LINE_LEN, file) == NULL)
8160 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
8161 cookie[strlen(cookie) - 1] = '\0';
8163 if (!checkCookieString(cookie, SCORE_COOKIE))
8165 Error(ERR_WARN, "unknown format of score file '%s'", filename);
8170 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
8172 if (fscanf(file, "%d", &highscore[i].Score) == EOF)
8173 Error(ERR_WARN, "fscanf() failed; %s", strerror(errno));
8174 if (fgets(line, MAX_LINE_LEN, file) == NULL)
8177 if (strlen(line) > 0 && line[strlen(line) - 1] == '\n')
8178 line[strlen(line) - 1] = '\0';
8180 for (line_ptr = line; *line_ptr; line_ptr++)
8182 if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != '\0')
8184 strncpy(highscore[i].Name, line_ptr, MAX_PLAYER_NAME_LEN);
8185 highscore[i].Name[MAX_PLAYER_NAME_LEN] = '\0';
8194 void SaveScore(int nr)
8197 int permissions = (program.global_scores ? PERMS_PUBLIC : PERMS_PRIVATE);
8198 char *filename = getScoreFilename(nr);
8201 InitScoreDirectory(leveldir_current->subdir);
8203 if (!(file = fopen(filename, MODE_WRITE)))
8205 Error(ERR_WARN, "cannot save score for level %d", nr);
8209 fprintf(file, "%s\n\n", SCORE_COOKIE);
8211 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
8212 fprintf(file, "%d %s\n", highscore[i].Score, highscore[i].Name);
8216 SetFilePermissions(filename, permissions);
8220 /* ========================================================================= */
8221 /* setup file functions */
8222 /* ========================================================================= */
8224 #define TOKEN_STR_PLAYER_PREFIX "player_"
8227 #define SETUP_TOKEN_PLAYER_NAME 0
8228 #define SETUP_TOKEN_SOUND 1
8229 #define SETUP_TOKEN_SOUND_LOOPS 2
8230 #define SETUP_TOKEN_SOUND_MUSIC 3
8231 #define SETUP_TOKEN_SOUND_SIMPLE 4
8232 #define SETUP_TOKEN_TOONS 5
8233 #define SETUP_TOKEN_SCROLL_DELAY 6
8234 #define SETUP_TOKEN_SCROLL_DELAY_VALUE 7
8235 #define SETUP_TOKEN_ENGINE_SNAPSHOT_MODE 8
8236 #define SETUP_TOKEN_ENGINE_SNAPSHOT_MEMORY 9
8237 #define SETUP_TOKEN_FADE_SCREENS 10
8238 #define SETUP_TOKEN_AUTORECORD 11
8239 #define SETUP_TOKEN_SHOW_TITLESCREEN 12
8240 #define SETUP_TOKEN_QUICK_DOORS 13
8241 #define SETUP_TOKEN_TEAM_MODE 14
8242 #define SETUP_TOKEN_HANDICAP 15
8243 #define SETUP_TOKEN_SKIP_LEVELS 16
8244 #define SETUP_TOKEN_INCREMENT_LEVELS 17
8245 #define SETUP_TOKEN_TIME_LIMIT 18
8246 #define SETUP_TOKEN_FULLSCREEN 19
8247 #define SETUP_TOKEN_WINDOW_SCALING_PERCENT 20
8248 #define SETUP_TOKEN_WINDOW_SCALING_QUALITY 21
8249 #define SETUP_TOKEN_SCREEN_RENDERING_MODE 22
8250 #define SETUP_TOKEN_ASK_ON_ESCAPE 23
8251 #define SETUP_TOKEN_ASK_ON_ESCAPE_EDITOR 24
8252 #define SETUP_TOKEN_QUICK_SWITCH 25
8253 #define SETUP_TOKEN_INPUT_ON_FOCUS 26
8254 #define SETUP_TOKEN_PREFER_AGA_GRAPHICS 27
8255 #define SETUP_TOKEN_GAME_FRAME_DELAY 28
8256 #define SETUP_TOKEN_SP_SHOW_BORDER_ELEMENTS 29
8257 #define SETUP_TOKEN_SMALL_GAME_GRAPHICS 30
8258 #define SETUP_TOKEN_SHOW_SNAPSHOT_BUTTONS 31
8259 #define SETUP_TOKEN_GRAPHICS_SET 32
8260 #define SETUP_TOKEN_SOUNDS_SET 33
8261 #define SETUP_TOKEN_MUSIC_SET 34
8262 #define SETUP_TOKEN_OVERRIDE_LEVEL_GRAPHICS 35
8263 #define SETUP_TOKEN_OVERRIDE_LEVEL_SOUNDS 36
8264 #define SETUP_TOKEN_OVERRIDE_LEVEL_MUSIC 37
8265 #define SETUP_TOKEN_VOLUME_SIMPLE 38
8266 #define SETUP_TOKEN_VOLUME_LOOPS 39
8267 #define SETUP_TOKEN_VOLUME_MUSIC 40
8268 #define SETUP_TOKEN_TOUCH_CONTROL_TYPE 41
8269 #define SETUP_TOKEN_TOUCH_MOVE_DISTANCE 42
8270 #define SETUP_TOKEN_TOUCH_DROP_DISTANCE 43
8272 #define NUM_GLOBAL_SETUP_TOKENS 44
8275 #define SETUP_TOKEN_AUTO_EDITOR_ZOOM_TILESIZE 0
8277 #define NUM_AUTO_SETUP_TOKENS 1
8280 #define SETUP_TOKEN_EDITOR_EL_CLASSIC 0
8281 #define SETUP_TOKEN_EDITOR_EL_CUSTOM 1
8282 #define SETUP_TOKEN_EDITOR_EL_USER_DEFINED 2
8283 #define SETUP_TOKEN_EDITOR_EL_DYNAMIC 3
8284 #define SETUP_TOKEN_EDITOR_EL_HEADLINES 4
8285 #define SETUP_TOKEN_EDITOR_SHOW_ELEMENT_TOKEN 5
8287 #define NUM_EDITOR_SETUP_TOKENS 6
8289 /* editor cascade setup */
8290 #define SETUP_TOKEN_EDITOR_CASCADE_BD 0
8291 #define SETUP_TOKEN_EDITOR_CASCADE_EM 1
8292 #define SETUP_TOKEN_EDITOR_CASCADE_EMC 2
8293 #define SETUP_TOKEN_EDITOR_CASCADE_RND 3
8294 #define SETUP_TOKEN_EDITOR_CASCADE_SB 4
8295 #define SETUP_TOKEN_EDITOR_CASCADE_SP 5
8296 #define SETUP_TOKEN_EDITOR_CASCADE_DC 6
8297 #define SETUP_TOKEN_EDITOR_CASCADE_DX 7
8298 #define SETUP_TOKEN_EDITOR_CASCADE_TEXT 8
8299 #define SETUP_TOKEN_EDITOR_CASCADE_STEELTEXT 9
8300 #define SETUP_TOKEN_EDITOR_CASCADE_CE 10
8301 #define SETUP_TOKEN_EDITOR_CASCADE_GE 11
8302 #define SETUP_TOKEN_EDITOR_CASCADE_REF 12
8303 #define SETUP_TOKEN_EDITOR_CASCADE_USER 13
8304 #define SETUP_TOKEN_EDITOR_CASCADE_DYNAMIC 14
8306 #define NUM_EDITOR_CASCADE_SETUP_TOKENS 15
8308 /* shortcut setup */
8309 #define SETUP_TOKEN_SHORTCUT_SAVE_GAME 0
8310 #define SETUP_TOKEN_SHORTCUT_LOAD_GAME 1
8311 #define SETUP_TOKEN_SHORTCUT_TOGGLE_PAUSE 2
8312 #define SETUP_TOKEN_SHORTCUT_FOCUS_PLAYER_1 3
8313 #define SETUP_TOKEN_SHORTCUT_FOCUS_PLAYER_2 4
8314 #define SETUP_TOKEN_SHORTCUT_FOCUS_PLAYER_3 5
8315 #define SETUP_TOKEN_SHORTCUT_FOCUS_PLAYER_4 6
8316 #define SETUP_TOKEN_SHORTCUT_FOCUS_PLAYER_ALL 7
8317 #define SETUP_TOKEN_SHORTCUT_TAPE_EJECT 8
8318 #define SETUP_TOKEN_SHORTCUT_TAPE_EXTRA 9
8319 #define SETUP_TOKEN_SHORTCUT_TAPE_STOP 10
8320 #define SETUP_TOKEN_SHORTCUT_TAPE_PAUSE 11
8321 #define SETUP_TOKEN_SHORTCUT_TAPE_RECORD 12
8322 #define SETUP_TOKEN_SHORTCUT_TAPE_PLAY 13
8323 #define SETUP_TOKEN_SHORTCUT_SOUND_SIMPLE 14
8324 #define SETUP_TOKEN_SHORTCUT_SOUND_LOOPS 15
8325 #define SETUP_TOKEN_SHORTCUT_SOUND_MUSIC 16
8326 #define SETUP_TOKEN_SHORTCUT_SNAP_LEFT 17
8327 #define SETUP_TOKEN_SHORTCUT_SNAP_RIGHT 18
8328 #define SETUP_TOKEN_SHORTCUT_SNAP_UP 19
8329 #define SETUP_TOKEN_SHORTCUT_SNAP_DOWN 20
8331 #define NUM_SHORTCUT_SETUP_TOKENS 21
8334 #define SETUP_TOKEN_PLAYER_USE_JOYSTICK 0
8335 #define SETUP_TOKEN_PLAYER_JOY_DEVICE_NAME 1
8336 #define SETUP_TOKEN_PLAYER_JOY_XLEFT 2
8337 #define SETUP_TOKEN_PLAYER_JOY_XMIDDLE 3
8338 #define SETUP_TOKEN_PLAYER_JOY_XRIGHT 4
8339 #define SETUP_TOKEN_PLAYER_JOY_YUPPER 5
8340 #define SETUP_TOKEN_PLAYER_JOY_YMIDDLE 6
8341 #define SETUP_TOKEN_PLAYER_JOY_YLOWER 7
8342 #define SETUP_TOKEN_PLAYER_JOY_SNAP 8
8343 #define SETUP_TOKEN_PLAYER_JOY_DROP 9
8344 #define SETUP_TOKEN_PLAYER_KEY_LEFT 10
8345 #define SETUP_TOKEN_PLAYER_KEY_RIGHT 11
8346 #define SETUP_TOKEN_PLAYER_KEY_UP 12
8347 #define SETUP_TOKEN_PLAYER_KEY_DOWN 13
8348 #define SETUP_TOKEN_PLAYER_KEY_SNAP 14
8349 #define SETUP_TOKEN_PLAYER_KEY_DROP 15
8351 #define NUM_PLAYER_SETUP_TOKENS 16
8354 #define SETUP_TOKEN_SYSTEM_SDL_VIDEODRIVER 0
8355 #define SETUP_TOKEN_SYSTEM_SDL_AUDIODRIVER 1
8356 #define SETUP_TOKEN_SYSTEM_AUDIO_FRAGMENT_SIZE 2
8358 #define NUM_SYSTEM_SETUP_TOKENS 3
8360 /* internal setup */
8361 #define SETUP_TOKEN_INT_PROGRAM_TITLE 0
8362 #define SETUP_TOKEN_INT_PROGRAM_VERSION 1
8363 #define SETUP_TOKEN_INT_PROGRAM_AUTHOR 2
8364 #define SETUP_TOKEN_INT_PROGRAM_EMAIL 3
8365 #define SETUP_TOKEN_INT_PROGRAM_WEBSITE 4
8366 #define SETUP_TOKEN_INT_PROGRAM_COPYRIGHT 5
8367 #define SETUP_TOKEN_INT_PROGRAM_COMPANY 6
8368 #define SETUP_TOKEN_INT_PROGRAM_ICON_FILE 7
8369 #define SETUP_TOKEN_INT_DEFAULT_GRAPHICS_SET 8
8370 #define SETUP_TOKEN_INT_DEFAULT_SOUNDS_SET 9
8371 #define SETUP_TOKEN_INT_DEFAULT_MUSIC_SET 10
8372 #define SETUP_TOKEN_INT_FALLBACK_GRAPHICS_FILE 11
8373 #define SETUP_TOKEN_INT_FALLBACK_SOUNDS_FILE 12
8374 #define SETUP_TOKEN_INT_FALLBACK_MUSIC_FILE 13
8375 #define SETUP_TOKEN_INT_DEFAULT_LEVEL_SERIES 14
8376 #define SETUP_TOKEN_INT_CHOOSE_FROM_TOP_LEVELDIR 15
8377 #define SETUP_TOKEN_INT_SHOW_SCALING_IN_TITLE 16
8378 #define SETUP_TOKEN_INT_DEFAULT_WINDOW_WIDTH 17
8379 #define SETUP_TOKEN_INT_DEFAULT_WINDOW_HEIGHT 18
8381 #define NUM_INTERNAL_SETUP_TOKENS 19
8384 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_0 0
8385 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_1 1
8386 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_2 2
8387 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_3 3
8388 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_4 4
8389 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_5 5
8390 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_6 6
8391 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_7 7
8392 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_8 8
8393 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_9 9
8394 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_KEY_0 10
8395 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_KEY_1 11
8396 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_KEY_2 12
8397 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_KEY_3 13
8398 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_KEY_4 14
8399 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_KEY_5 15
8400 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_KEY_6 16
8401 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_KEY_7 17
8402 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_KEY_8 18
8403 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_KEY_9 19
8404 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_USE_MOD_KEY 20
8405 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_GAME_ONLY 21
8406 #define SETUP_TOKEN_DEBUG_SHOW_FRAMES_PER_SECOND 22
8408 #define NUM_DEBUG_SETUP_TOKENS 23
8411 #define SETUP_TOKEN_OPTIONS_VERBOSE 0
8413 #define NUM_OPTIONS_SETUP_TOKENS 1
8416 static struct SetupInfo si;
8417 static struct SetupAutoSetupInfo sasi;
8418 static struct SetupEditorInfo sei;
8419 static struct SetupEditorCascadeInfo seci;
8420 static struct SetupShortcutInfo ssi;
8421 static struct SetupInputInfo sii;
8422 static struct SetupSystemInfo syi;
8423 static struct SetupInternalInfo sxi;
8424 static struct SetupDebugInfo sdi;
8425 static struct OptionInfo soi;
8427 static struct TokenInfo global_setup_tokens[] =
8429 { TYPE_STRING, &si.player_name, "player_name" },
8430 { TYPE_SWITCH, &si.sound, "sound" },
8431 { TYPE_SWITCH, &si.sound_loops, "repeating_sound_loops" },
8432 { TYPE_SWITCH, &si.sound_music, "background_music" },
8433 { TYPE_SWITCH, &si.sound_simple, "simple_sound_effects" },
8434 { TYPE_SWITCH, &si.toons, "toons" },
8435 { TYPE_SWITCH, &si.scroll_delay, "scroll_delay" },
8436 { TYPE_INTEGER,&si.scroll_delay_value, "scroll_delay_value" },
8437 { TYPE_STRING, &si.engine_snapshot_mode, "engine_snapshot_mode" },
8438 { TYPE_INTEGER,&si.engine_snapshot_memory, "engine_snapshot_memory" },
8439 { TYPE_SWITCH, &si.fade_screens, "fade_screens" },
8440 { TYPE_SWITCH, &si.autorecord, "automatic_tape_recording"},
8441 { TYPE_SWITCH, &si.show_titlescreen, "show_titlescreen" },
8442 { TYPE_SWITCH, &si.quick_doors, "quick_doors" },
8443 { TYPE_SWITCH, &si.team_mode, "team_mode" },
8444 { TYPE_SWITCH, &si.handicap, "handicap" },
8445 { TYPE_SWITCH, &si.skip_levels, "skip_levels" },
8446 { TYPE_SWITCH, &si.increment_levels, "increment_levels" },
8447 { TYPE_SWITCH, &si.time_limit, "time_limit" },
8448 { TYPE_SWITCH, &si.fullscreen, "fullscreen" },
8449 { TYPE_INTEGER,&si.window_scaling_percent, "window_scaling_percent" },
8450 { TYPE_STRING, &si.window_scaling_quality, "window_scaling_quality" },
8451 { TYPE_STRING, &si.screen_rendering_mode, "screen_rendering_mode" },
8452 { TYPE_SWITCH, &si.ask_on_escape, "ask_on_escape" },
8453 { TYPE_SWITCH, &si.ask_on_escape_editor, "ask_on_escape_editor" },
8454 { TYPE_SWITCH, &si.quick_switch, "quick_player_switch" },
8455 { TYPE_SWITCH, &si.input_on_focus, "input_on_focus" },
8456 { TYPE_SWITCH, &si.prefer_aga_graphics, "prefer_aga_graphics" },
8457 { TYPE_INTEGER,&si.game_frame_delay, "game_frame_delay" },
8458 { TYPE_SWITCH, &si.sp_show_border_elements, "sp_show_border_elements" },
8459 { TYPE_SWITCH, &si.small_game_graphics, "small_game_graphics" },
8460 { TYPE_SWITCH, &si.show_snapshot_buttons, "show_snapshot_buttons" },
8461 { TYPE_STRING, &si.graphics_set, "graphics_set" },
8462 { TYPE_STRING, &si.sounds_set, "sounds_set" },
8463 { TYPE_STRING, &si.music_set, "music_set" },
8464 { TYPE_SWITCH3,&si.override_level_graphics, "override_level_graphics" },
8465 { TYPE_SWITCH3,&si.override_level_sounds, "override_level_sounds" },
8466 { TYPE_SWITCH3,&si.override_level_music, "override_level_music" },
8467 { TYPE_INTEGER,&si.volume_simple, "volume_simple" },
8468 { TYPE_INTEGER,&si.volume_loops, "volume_loops" },
8469 { TYPE_INTEGER,&si.volume_music, "volume_music" },
8470 { TYPE_STRING, &si.touch.control_type, "touch.control_type" },
8471 { TYPE_INTEGER,&si.touch.move_distance, "touch.move_distance" },
8472 { TYPE_INTEGER,&si.touch.drop_distance, "touch.drop_distance" },
8475 static struct TokenInfo auto_setup_tokens[] =
8477 { TYPE_INTEGER,&sasi.editor_zoom_tilesize, "editor.zoom_tilesize" },
8480 static struct TokenInfo editor_setup_tokens[] =
8482 { TYPE_SWITCH, &sei.el_classic, "editor.el_classic" },
8483 { TYPE_SWITCH, &sei.el_custom, "editor.el_custom" },
8484 { TYPE_SWITCH, &sei.el_user_defined, "editor.el_user_defined" },
8485 { TYPE_SWITCH, &sei.el_dynamic, "editor.el_dynamic" },
8486 { TYPE_SWITCH, &sei.el_headlines, "editor.el_headlines" },
8487 { TYPE_SWITCH, &sei.show_element_token,"editor.show_element_token" },
8490 static struct TokenInfo editor_cascade_setup_tokens[] =
8492 { TYPE_SWITCH, &seci.el_bd, "editor.cascade.el_bd" },
8493 { TYPE_SWITCH, &seci.el_em, "editor.cascade.el_em" },
8494 { TYPE_SWITCH, &seci.el_emc, "editor.cascade.el_emc" },
8495 { TYPE_SWITCH, &seci.el_rnd, "editor.cascade.el_rnd" },
8496 { TYPE_SWITCH, &seci.el_sb, "editor.cascade.el_sb" },
8497 { TYPE_SWITCH, &seci.el_sp, "editor.cascade.el_sp" },
8498 { TYPE_SWITCH, &seci.el_dc, "editor.cascade.el_dc" },
8499 { TYPE_SWITCH, &seci.el_dx, "editor.cascade.el_dx" },
8500 { TYPE_SWITCH, &seci.el_mm, "editor.cascade.el_mm" },
8501 { TYPE_SWITCH, &seci.el_df, "editor.cascade.el_df" },
8502 { TYPE_SWITCH, &seci.el_chars, "editor.cascade.el_chars" },
8503 { TYPE_SWITCH, &seci.el_steel_chars, "editor.cascade.el_steel_chars" },
8504 { TYPE_SWITCH, &seci.el_ce, "editor.cascade.el_ce" },
8505 { TYPE_SWITCH, &seci.el_ge, "editor.cascade.el_ge" },
8506 { TYPE_SWITCH, &seci.el_ref, "editor.cascade.el_ref" },
8507 { TYPE_SWITCH, &seci.el_user, "editor.cascade.el_user" },
8508 { TYPE_SWITCH, &seci.el_dynamic, "editor.cascade.el_dynamic" },
8511 static struct TokenInfo shortcut_setup_tokens[] =
8513 { TYPE_KEY_X11, &ssi.save_game, "shortcut.save_game" },
8514 { TYPE_KEY_X11, &ssi.load_game, "shortcut.load_game" },
8515 { TYPE_KEY_X11, &ssi.toggle_pause, "shortcut.toggle_pause" },
8516 { TYPE_KEY_X11, &ssi.focus_player[0], "shortcut.focus_player_1" },
8517 { TYPE_KEY_X11, &ssi.focus_player[1], "shortcut.focus_player_2" },
8518 { TYPE_KEY_X11, &ssi.focus_player[2], "shortcut.focus_player_3" },
8519 { TYPE_KEY_X11, &ssi.focus_player[3], "shortcut.focus_player_4" },
8520 { TYPE_KEY_X11, &ssi.focus_player_all,"shortcut.focus_player_all" },
8521 { TYPE_KEY_X11, &ssi.tape_eject, "shortcut.tape_eject" },
8522 { TYPE_KEY_X11, &ssi.tape_extra, "shortcut.tape_extra" },
8523 { TYPE_KEY_X11, &ssi.tape_stop, "shortcut.tape_stop" },
8524 { TYPE_KEY_X11, &ssi.tape_pause, "shortcut.tape_pause" },
8525 { TYPE_KEY_X11, &ssi.tape_record, "shortcut.tape_record" },
8526 { TYPE_KEY_X11, &ssi.tape_play, "shortcut.tape_play" },
8527 { TYPE_KEY_X11, &ssi.sound_simple, "shortcut.sound_simple" },
8528 { TYPE_KEY_X11, &ssi.sound_loops, "shortcut.sound_loops" },
8529 { TYPE_KEY_X11, &ssi.sound_music, "shortcut.sound_music" },
8530 { TYPE_KEY_X11, &ssi.snap_left, "shortcut.snap_left" },
8531 { TYPE_KEY_X11, &ssi.snap_right, "shortcut.snap_right" },
8532 { TYPE_KEY_X11, &ssi.snap_up, "shortcut.snap_up" },
8533 { TYPE_KEY_X11, &ssi.snap_down, "shortcut.snap_down" },
8536 static struct TokenInfo player_setup_tokens[] =
8538 { TYPE_BOOLEAN, &sii.use_joystick, ".use_joystick" },
8539 { TYPE_STRING, &sii.joy.device_name, ".joy.device_name" },
8540 { TYPE_INTEGER, &sii.joy.xleft, ".joy.xleft" },
8541 { TYPE_INTEGER, &sii.joy.xmiddle, ".joy.xmiddle" },
8542 { TYPE_INTEGER, &sii.joy.xright, ".joy.xright" },
8543 { TYPE_INTEGER, &sii.joy.yupper, ".joy.yupper" },
8544 { TYPE_INTEGER, &sii.joy.ymiddle, ".joy.ymiddle" },
8545 { TYPE_INTEGER, &sii.joy.ylower, ".joy.ylower" },
8546 { TYPE_INTEGER, &sii.joy.snap, ".joy.snap_field" },
8547 { TYPE_INTEGER, &sii.joy.drop, ".joy.place_bomb" },
8548 { TYPE_KEY_X11, &sii.key.left, ".key.move_left" },
8549 { TYPE_KEY_X11, &sii.key.right, ".key.move_right" },
8550 { TYPE_KEY_X11, &sii.key.up, ".key.move_up" },
8551 { TYPE_KEY_X11, &sii.key.down, ".key.move_down" },
8552 { TYPE_KEY_X11, &sii.key.snap, ".key.snap_field" },
8553 { TYPE_KEY_X11, &sii.key.drop, ".key.place_bomb" },
8556 static struct TokenInfo system_setup_tokens[] =
8558 { TYPE_STRING, &syi.sdl_videodriver, "system.sdl_videodriver" },
8559 { TYPE_STRING, &syi.sdl_audiodriver, "system.sdl_audiodriver" },
8560 { TYPE_INTEGER, &syi.audio_fragment_size,"system.audio_fragment_size" },
8563 static struct TokenInfo internal_setup_tokens[] =
8565 { TYPE_STRING, &sxi.program_title, "program_title" },
8566 { TYPE_STRING, &sxi.program_version, "program_version" },
8567 { TYPE_STRING, &sxi.program_author, "program_author" },
8568 { TYPE_STRING, &sxi.program_email, "program_email" },
8569 { TYPE_STRING, &sxi.program_website, "program_website" },
8570 { TYPE_STRING, &sxi.program_copyright, "program_copyright" },
8571 { TYPE_STRING, &sxi.program_company, "program_company" },
8572 { TYPE_STRING, &sxi.program_icon_file, "program_icon_file" },
8573 { TYPE_STRING, &sxi.default_graphics_set, "default_graphics_set" },
8574 { TYPE_STRING, &sxi.default_sounds_set, "default_sounds_set" },
8575 { TYPE_STRING, &sxi.default_music_set, "default_music_set" },
8576 { TYPE_STRING, &sxi.fallback_graphics_file, "fallback_graphics_file"},
8577 { TYPE_STRING, &sxi.fallback_sounds_file, "fallback_sounds_file" },
8578 { TYPE_STRING, &sxi.fallback_music_file, "fallback_music_file" },
8579 { TYPE_STRING, &sxi.default_level_series, "default_level_series" },
8580 { TYPE_BOOLEAN,&sxi.choose_from_top_leveldir, "choose_from_top_leveldir" },
8581 { TYPE_BOOLEAN,&sxi.show_scaling_in_title, "show_scaling_in_title" },
8582 { TYPE_INTEGER,&sxi.default_window_width, "default_window_width" },
8583 { TYPE_INTEGER,&sxi.default_window_height, "default_window_height" },
8586 static struct TokenInfo debug_setup_tokens[] =
8588 { TYPE_INTEGER, &sdi.frame_delay[0], "debug.frame_delay_0" },
8589 { TYPE_INTEGER, &sdi.frame_delay[1], "debug.frame_delay_1" },
8590 { TYPE_INTEGER, &sdi.frame_delay[2], "debug.frame_delay_2" },
8591 { TYPE_INTEGER, &sdi.frame_delay[3], "debug.frame_delay_3" },
8592 { TYPE_INTEGER, &sdi.frame_delay[4], "debug.frame_delay_4" },
8593 { TYPE_INTEGER, &sdi.frame_delay[5], "debug.frame_delay_5" },
8594 { TYPE_INTEGER, &sdi.frame_delay[6], "debug.frame_delay_6" },
8595 { TYPE_INTEGER, &sdi.frame_delay[7], "debug.frame_delay_7" },
8596 { TYPE_INTEGER, &sdi.frame_delay[8], "debug.frame_delay_8" },
8597 { TYPE_INTEGER, &sdi.frame_delay[9], "debug.frame_delay_9" },
8598 { TYPE_KEY_X11, &sdi.frame_delay_key[0], "debug.key.frame_delay_0" },
8599 { TYPE_KEY_X11, &sdi.frame_delay_key[1], "debug.key.frame_delay_1" },
8600 { TYPE_KEY_X11, &sdi.frame_delay_key[2], "debug.key.frame_delay_2" },
8601 { TYPE_KEY_X11, &sdi.frame_delay_key[3], "debug.key.frame_delay_3" },
8602 { TYPE_KEY_X11, &sdi.frame_delay_key[4], "debug.key.frame_delay_4" },
8603 { TYPE_KEY_X11, &sdi.frame_delay_key[5], "debug.key.frame_delay_5" },
8604 { TYPE_KEY_X11, &sdi.frame_delay_key[6], "debug.key.frame_delay_6" },
8605 { TYPE_KEY_X11, &sdi.frame_delay_key[7], "debug.key.frame_delay_7" },
8606 { TYPE_KEY_X11, &sdi.frame_delay_key[8], "debug.key.frame_delay_8" },
8607 { TYPE_KEY_X11, &sdi.frame_delay_key[9], "debug.key.frame_delay_9" },
8608 { TYPE_BOOLEAN, &sdi.frame_delay_use_mod_key,"debug.frame_delay.use_mod_key"},
8609 { TYPE_BOOLEAN, &sdi.frame_delay_game_only, "debug.frame_delay.game_only" },
8610 { TYPE_BOOLEAN, &sdi.show_frames_per_second, "debug.show_frames_per_second" },
8613 static struct TokenInfo options_setup_tokens[] =
8615 { TYPE_BOOLEAN, &soi.verbose, "options.verbose" },
8618 static char *get_corrected_login_name(char *login_name)
8620 /* needed because player name must be a fixed length string */
8621 char *login_name_new = checked_malloc(MAX_PLAYER_NAME_LEN + 1);
8623 strncpy(login_name_new, login_name, MAX_PLAYER_NAME_LEN);
8624 login_name_new[MAX_PLAYER_NAME_LEN] = '\0';
8626 if (strlen(login_name) > MAX_PLAYER_NAME_LEN) /* name has been cut */
8627 if (strchr(login_name_new, ' '))
8628 *strchr(login_name_new, ' ') = '\0';
8630 return login_name_new;
8633 static void setSetupInfoToDefaults(struct SetupInfo *si)
8637 si->player_name = get_corrected_login_name(getLoginName());
8640 si->sound_loops = TRUE;
8641 si->sound_music = TRUE;
8642 si->sound_simple = TRUE;
8644 si->scroll_delay = TRUE;
8645 si->scroll_delay_value = STD_SCROLL_DELAY;
8646 si->engine_snapshot_mode = getStringCopy(STR_SNAPSHOT_MODE_DEFAULT);
8647 si->engine_snapshot_memory = SNAPSHOT_MEMORY_DEFAULT;
8648 si->fade_screens = TRUE;
8649 si->autorecord = TRUE;
8650 si->show_titlescreen = TRUE;
8651 si->quick_doors = FALSE;
8652 si->team_mode = FALSE;
8653 si->handicap = TRUE;
8654 si->skip_levels = TRUE;
8655 si->increment_levels = TRUE;
8656 si->time_limit = TRUE;
8657 si->fullscreen = FALSE;
8658 si->window_scaling_percent = STD_WINDOW_SCALING_PERCENT;
8659 si->window_scaling_quality = getStringCopy(SCALING_QUALITY_DEFAULT);
8660 si->screen_rendering_mode = getStringCopy(STR_SPECIAL_RENDERING_DEFAULT);
8661 si->ask_on_escape = TRUE;
8662 si->ask_on_escape_editor = TRUE;
8663 si->quick_switch = FALSE;
8664 si->input_on_focus = FALSE;
8665 si->prefer_aga_graphics = TRUE;
8666 si->game_frame_delay = GAME_FRAME_DELAY;
8667 si->sp_show_border_elements = FALSE;
8668 si->small_game_graphics = FALSE;
8669 si->show_snapshot_buttons = FALSE;
8671 si->graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
8672 si->sounds_set = getStringCopy(SND_CLASSIC_SUBDIR);
8673 si->music_set = getStringCopy(MUS_CLASSIC_SUBDIR);
8675 si->override_level_graphics = FALSE;
8676 si->override_level_sounds = FALSE;
8677 si->override_level_music = FALSE;
8679 si->volume_simple = 100; /* percent */
8680 si->volume_loops = 100; /* percent */
8681 si->volume_music = 100; /* percent */
8683 si->touch.control_type = getStringCopy(TOUCH_CONTROL_DEFAULT);
8684 si->touch.move_distance = TOUCH_MOVE_DISTANCE_DEFAULT; /* percent */
8685 si->touch.drop_distance = TOUCH_DROP_DISTANCE_DEFAULT; /* percent */
8687 si->editor.el_boulderdash = TRUE;
8688 si->editor.el_emerald_mine = TRUE;
8689 si->editor.el_emerald_mine_club = TRUE;
8690 si->editor.el_more = TRUE;
8691 si->editor.el_sokoban = TRUE;
8692 si->editor.el_supaplex = TRUE;
8693 si->editor.el_diamond_caves = TRUE;
8694 si->editor.el_dx_boulderdash = TRUE;
8696 si->editor.el_mirror_magic = TRUE;
8697 si->editor.el_deflektor = TRUE;
8699 si->editor.el_chars = TRUE;
8700 si->editor.el_steel_chars = TRUE;
8702 si->editor.el_classic = TRUE;
8703 si->editor.el_custom = TRUE;
8705 si->editor.el_user_defined = FALSE;
8706 si->editor.el_dynamic = TRUE;
8708 si->editor.el_headlines = TRUE;
8710 si->editor.show_element_token = FALSE;
8712 si->editor.use_template_for_new_levels = TRUE;
8714 si->shortcut.save_game = DEFAULT_KEY_SAVE_GAME;
8715 si->shortcut.load_game = DEFAULT_KEY_LOAD_GAME;
8716 si->shortcut.toggle_pause = DEFAULT_KEY_TOGGLE_PAUSE;
8718 si->shortcut.focus_player[0] = DEFAULT_KEY_FOCUS_PLAYER_1;
8719 si->shortcut.focus_player[1] = DEFAULT_KEY_FOCUS_PLAYER_2;
8720 si->shortcut.focus_player[2] = DEFAULT_KEY_FOCUS_PLAYER_3;
8721 si->shortcut.focus_player[3] = DEFAULT_KEY_FOCUS_PLAYER_4;
8722 si->shortcut.focus_player_all = DEFAULT_KEY_FOCUS_PLAYER_ALL;
8724 si->shortcut.tape_eject = DEFAULT_KEY_TAPE_EJECT;
8725 si->shortcut.tape_extra = DEFAULT_KEY_TAPE_EXTRA;
8726 si->shortcut.tape_stop = DEFAULT_KEY_TAPE_STOP;
8727 si->shortcut.tape_pause = DEFAULT_KEY_TAPE_PAUSE;
8728 si->shortcut.tape_record = DEFAULT_KEY_TAPE_RECORD;
8729 si->shortcut.tape_play = DEFAULT_KEY_TAPE_PLAY;
8731 si->shortcut.sound_simple = DEFAULT_KEY_SOUND_SIMPLE;
8732 si->shortcut.sound_loops = DEFAULT_KEY_SOUND_LOOPS;
8733 si->shortcut.sound_music = DEFAULT_KEY_SOUND_MUSIC;
8735 si->shortcut.snap_left = DEFAULT_KEY_SNAP_LEFT;
8736 si->shortcut.snap_right = DEFAULT_KEY_SNAP_RIGHT;
8737 si->shortcut.snap_up = DEFAULT_KEY_SNAP_UP;
8738 si->shortcut.snap_down = DEFAULT_KEY_SNAP_DOWN;
8740 for (i = 0; i < MAX_PLAYERS; i++)
8742 si->input[i].use_joystick = FALSE;
8743 si->input[i].joy.device_name=getStringCopy(getDeviceNameFromJoystickNr(i));
8744 si->input[i].joy.xleft = JOYSTICK_XLEFT;
8745 si->input[i].joy.xmiddle = JOYSTICK_XMIDDLE;
8746 si->input[i].joy.xright = JOYSTICK_XRIGHT;
8747 si->input[i].joy.yupper = JOYSTICK_YUPPER;
8748 si->input[i].joy.ymiddle = JOYSTICK_YMIDDLE;
8749 si->input[i].joy.ylower = JOYSTICK_YLOWER;
8750 si->input[i].joy.snap = (i == 0 ? JOY_BUTTON_1 : 0);
8751 si->input[i].joy.drop = (i == 0 ? JOY_BUTTON_2 : 0);
8752 si->input[i].key.left = (i == 0 ? DEFAULT_KEY_LEFT : KSYM_UNDEFINED);
8753 si->input[i].key.right = (i == 0 ? DEFAULT_KEY_RIGHT : KSYM_UNDEFINED);
8754 si->input[i].key.up = (i == 0 ? DEFAULT_KEY_UP : KSYM_UNDEFINED);
8755 si->input[i].key.down = (i == 0 ? DEFAULT_KEY_DOWN : KSYM_UNDEFINED);
8756 si->input[i].key.snap = (i == 0 ? DEFAULT_KEY_SNAP : KSYM_UNDEFINED);
8757 si->input[i].key.drop = (i == 0 ? DEFAULT_KEY_DROP : KSYM_UNDEFINED);
8760 si->system.sdl_videodriver = getStringCopy(ARG_DEFAULT);
8761 si->system.sdl_audiodriver = getStringCopy(ARG_DEFAULT);
8762 si->system.audio_fragment_size = DEFAULT_AUDIO_FRAGMENT_SIZE;
8764 si->internal.program_title = getStringCopy(PROGRAM_TITLE_STRING);
8765 si->internal.program_version = getStringCopy(getProgramRealVersionString());
8766 si->internal.program_author = getStringCopy(PROGRAM_AUTHOR_STRING);
8767 si->internal.program_email = getStringCopy(PROGRAM_EMAIL_STRING);
8768 si->internal.program_website = getStringCopy(PROGRAM_WEBSITE_STRING);
8769 si->internal.program_copyright = getStringCopy(PROGRAM_COPYRIGHT_STRING);
8770 si->internal.program_company = getStringCopy(PROGRAM_COMPANY_STRING);
8772 si->internal.program_icon_file = getStringCopy(PROGRAM_ICON_FILENAME);
8774 si->internal.default_graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
8775 si->internal.default_sounds_set = getStringCopy(SND_CLASSIC_SUBDIR);
8776 si->internal.default_music_set = getStringCopy(MUS_CLASSIC_SUBDIR);
8778 si->internal.fallback_graphics_file = getStringCopy(UNDEFINED_FILENAME);
8779 si->internal.fallback_sounds_file = getStringCopy(UNDEFINED_FILENAME);
8780 si->internal.fallback_music_file = getStringCopy(UNDEFINED_FILENAME);
8782 si->internal.default_level_series = getStringCopy(UNDEFINED_LEVELSET);
8783 si->internal.choose_from_top_leveldir = FALSE;
8784 si->internal.show_scaling_in_title = TRUE;
8786 si->internal.default_window_width = WIN_XSIZE_DEFAULT;
8787 si->internal.default_window_height = WIN_YSIZE_DEFAULT;
8789 si->debug.frame_delay[0] = DEFAULT_FRAME_DELAY_0;
8790 si->debug.frame_delay[1] = DEFAULT_FRAME_DELAY_1;
8791 si->debug.frame_delay[2] = DEFAULT_FRAME_DELAY_2;
8792 si->debug.frame_delay[3] = DEFAULT_FRAME_DELAY_3;
8793 si->debug.frame_delay[4] = DEFAULT_FRAME_DELAY_4;
8794 si->debug.frame_delay[5] = DEFAULT_FRAME_DELAY_5;
8795 si->debug.frame_delay[6] = DEFAULT_FRAME_DELAY_6;
8796 si->debug.frame_delay[7] = DEFAULT_FRAME_DELAY_7;
8797 si->debug.frame_delay[8] = DEFAULT_FRAME_DELAY_8;
8798 si->debug.frame_delay[9] = DEFAULT_FRAME_DELAY_9;
8800 si->debug.frame_delay_key[0] = DEFAULT_KEY_FRAME_DELAY_0;
8801 si->debug.frame_delay_key[1] = DEFAULT_KEY_FRAME_DELAY_1;
8802 si->debug.frame_delay_key[2] = DEFAULT_KEY_FRAME_DELAY_2;
8803 si->debug.frame_delay_key[3] = DEFAULT_KEY_FRAME_DELAY_3;
8804 si->debug.frame_delay_key[4] = DEFAULT_KEY_FRAME_DELAY_4;
8805 si->debug.frame_delay_key[5] = DEFAULT_KEY_FRAME_DELAY_5;
8806 si->debug.frame_delay_key[6] = DEFAULT_KEY_FRAME_DELAY_6;
8807 si->debug.frame_delay_key[7] = DEFAULT_KEY_FRAME_DELAY_7;
8808 si->debug.frame_delay_key[8] = DEFAULT_KEY_FRAME_DELAY_8;
8809 si->debug.frame_delay_key[9] = DEFAULT_KEY_FRAME_DELAY_9;
8811 si->debug.frame_delay_use_mod_key = DEFAULT_FRAME_DELAY_USE_MOD_KEY;
8812 si->debug.frame_delay_game_only = DEFAULT_FRAME_DELAY_GAME_ONLY;
8814 si->debug.show_frames_per_second = FALSE;
8816 si->options.verbose = FALSE;
8818 #if defined(PLATFORM_ANDROID)
8819 si->fullscreen = TRUE;
8823 static void setSetupInfoToDefaults_AutoSetup(struct SetupInfo *si)
8825 si->auto_setup.editor_zoom_tilesize = MINI_TILESIZE;
8828 static void setSetupInfoToDefaults_EditorCascade(struct SetupInfo *si)
8830 si->editor_cascade.el_bd = TRUE;
8831 si->editor_cascade.el_em = TRUE;
8832 si->editor_cascade.el_emc = TRUE;
8833 si->editor_cascade.el_rnd = TRUE;
8834 si->editor_cascade.el_sb = TRUE;
8835 si->editor_cascade.el_sp = TRUE;
8836 si->editor_cascade.el_dc = TRUE;
8837 si->editor_cascade.el_dx = TRUE;
8839 si->editor_cascade.el_mm = TRUE;
8840 si->editor_cascade.el_df = TRUE;
8842 si->editor_cascade.el_chars = FALSE;
8843 si->editor_cascade.el_steel_chars = FALSE;
8844 si->editor_cascade.el_ce = FALSE;
8845 si->editor_cascade.el_ge = FALSE;
8846 si->editor_cascade.el_ref = FALSE;
8847 si->editor_cascade.el_user = FALSE;
8848 si->editor_cascade.el_dynamic = FALSE;
8851 #define MAX_HIDE_SETUP_TOKEN_SIZE 20
8853 static char *getHideSetupToken(void *setup_value)
8855 static char hide_setup_token[MAX_HIDE_SETUP_TOKEN_SIZE];
8857 if (setup_value != NULL)
8858 snprintf(hide_setup_token, MAX_HIDE_SETUP_TOKEN_SIZE, "%p", setup_value);
8860 return hide_setup_token;
8863 static void setHideSetupEntry(void *setup_value_raw)
8865 /* !!! DIRTY WORKAROUND; TO BE FIXED AFTER THE MM ENGINE RELEASE !!! */
8866 void *setup_value = setup_value_raw - (void *)&si + (void *)&setup;
8868 char *hide_setup_token = getHideSetupToken(setup_value);
8870 if (setup_value != NULL)
8871 setHashEntry(hide_setup_hash, hide_setup_token, "");
8874 boolean hideSetupEntry(void *setup_value)
8876 char *hide_setup_token = getHideSetupToken(setup_value);
8878 return (setup_value != NULL &&
8879 getHashEntry(hide_setup_hash, hide_setup_token) != NULL);
8882 static void setSetupInfoFromTokenText(SetupFileHash *setup_file_hash,
8883 struct TokenInfo *token_info,
8884 int token_nr, char *token_text)
8886 char *token_hide_text = getStringCat2(token_text, ".hide");
8887 char *token_hide_value = getHashEntry(setup_file_hash, token_hide_text);
8889 /* set the value of this setup option in the setup option structure */
8890 setSetupInfo(token_info, token_nr, getHashEntry(setup_file_hash, token_text));
8892 /* check if this setup option should be hidden in the setup menu */
8893 if (token_hide_value != NULL && get_boolean_from_string(token_hide_value))
8894 setHideSetupEntry(token_info[token_nr].value);
8897 static void setSetupInfoFromTokenInfo(SetupFileHash *setup_file_hash,
8898 struct TokenInfo *token_info,
8901 setSetupInfoFromTokenText(setup_file_hash, token_info, token_nr,
8902 token_info[token_nr].text);
8905 static void decodeSetupFileHash(SetupFileHash *setup_file_hash)
8909 if (!setup_file_hash)
8912 if (hide_setup_hash == NULL)
8913 hide_setup_hash = newSetupFileHash();
8917 for (i = 0; i < NUM_GLOBAL_SETUP_TOKENS; i++)
8918 setSetupInfoFromTokenInfo(setup_file_hash, global_setup_tokens, i);
8923 for (i = 0; i < NUM_EDITOR_SETUP_TOKENS; i++)
8924 setSetupInfoFromTokenInfo(setup_file_hash, editor_setup_tokens, i);
8927 /* shortcut setup */
8928 ssi = setup.shortcut;
8929 for (i = 0; i < NUM_SHORTCUT_SETUP_TOKENS; i++)
8930 setSetupInfoFromTokenInfo(setup_file_hash, shortcut_setup_tokens, i);
8931 setup.shortcut = ssi;
8934 for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
8938 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
8940 sii = setup.input[pnr];
8941 for (i = 0; i < NUM_PLAYER_SETUP_TOKENS; i++)
8943 char full_token[100];
8945 sprintf(full_token, "%s%s", prefix, player_setup_tokens[i].text);
8946 setSetupInfoFromTokenText(setup_file_hash, player_setup_tokens, i,
8949 setup.input[pnr] = sii;
8954 for (i = 0; i < NUM_SYSTEM_SETUP_TOKENS; i++)
8955 setSetupInfoFromTokenInfo(setup_file_hash, system_setup_tokens, i);
8958 /* internal setup */
8959 sxi = setup.internal;
8960 for (i = 0; i < NUM_INTERNAL_SETUP_TOKENS; i++)
8961 setSetupInfoFromTokenInfo(setup_file_hash, internal_setup_tokens, i);
8962 setup.internal = sxi;
8966 for (i = 0; i < NUM_DEBUG_SETUP_TOKENS; i++)
8967 setSetupInfoFromTokenInfo(setup_file_hash, debug_setup_tokens, i);
8971 soi = setup.options;
8972 for (i = 0; i < NUM_OPTIONS_SETUP_TOKENS; i++)
8973 setSetupInfoFromTokenInfo(setup_file_hash, options_setup_tokens, i);
8974 setup.options = soi;
8977 static void decodeSetupFileHash_AutoSetup(SetupFileHash *setup_file_hash)
8981 if (!setup_file_hash)
8985 sasi = setup.auto_setup;
8986 for (i = 0; i < NUM_AUTO_SETUP_TOKENS; i++)
8987 setSetupInfo(auto_setup_tokens, i,
8988 getHashEntry(setup_file_hash,
8989 auto_setup_tokens[i].text));
8990 setup.auto_setup = sasi;
8993 static void decodeSetupFileHash_EditorCascade(SetupFileHash *setup_file_hash)
8997 if (!setup_file_hash)
9000 /* editor cascade setup */
9001 seci = setup.editor_cascade;
9002 for (i = 0; i < NUM_EDITOR_CASCADE_SETUP_TOKENS; i++)
9003 setSetupInfo(editor_cascade_setup_tokens, i,
9004 getHashEntry(setup_file_hash,
9005 editor_cascade_setup_tokens[i].text));
9006 setup.editor_cascade = seci;
9009 void LoadSetupFromFilename(char *filename)
9011 SetupFileHash *setup_file_hash = loadSetupFileHash(filename);
9013 if (setup_file_hash)
9015 decodeSetupFileHash(setup_file_hash);
9017 freeSetupFileHash(setup_file_hash);
9021 Error(ERR_DEBUG, "using default setup values");
9025 static void LoadSetup_SpecialPostProcessing()
9027 char *player_name_new;
9029 /* needed to work around problems with fixed length strings */
9030 player_name_new = get_corrected_login_name(setup.player_name);
9031 free(setup.player_name);
9032 setup.player_name = player_name_new;
9034 /* "scroll_delay: on(3) / off(0)" was replaced by scroll delay value */
9035 if (setup.scroll_delay == FALSE)
9037 setup.scroll_delay_value = MIN_SCROLL_DELAY;
9038 setup.scroll_delay = TRUE; /* now always "on" */
9041 /* make sure that scroll delay value stays inside valid range */
9042 setup.scroll_delay_value =
9043 MIN(MAX(MIN_SCROLL_DELAY, setup.scroll_delay_value), MAX_SCROLL_DELAY);
9050 /* always start with reliable default values */
9051 setSetupInfoToDefaults(&setup);
9053 /* try to load setup values from default setup file */
9054 filename = getDefaultSetupFilename();
9056 if (fileExists(filename))
9057 LoadSetupFromFilename(filename);
9059 /* try to load setup values from user setup file */
9060 filename = getSetupFilename();
9062 LoadSetupFromFilename(filename);
9064 LoadSetup_SpecialPostProcessing();
9067 void LoadSetup_AutoSetup()
9069 char *filename = getPath2(getSetupDir(), AUTOSETUP_FILENAME);
9070 SetupFileHash *setup_file_hash = NULL;
9072 /* always start with reliable default values */
9073 setSetupInfoToDefaults_AutoSetup(&setup);
9075 setup_file_hash = loadSetupFileHash(filename);
9077 if (setup_file_hash)
9079 decodeSetupFileHash_AutoSetup(setup_file_hash);
9081 freeSetupFileHash(setup_file_hash);
9087 void LoadSetup_EditorCascade()
9089 char *filename = getPath2(getSetupDir(), EDITORCASCADE_FILENAME);
9090 SetupFileHash *setup_file_hash = NULL;
9092 /* always start with reliable default values */
9093 setSetupInfoToDefaults_EditorCascade(&setup);
9095 setup_file_hash = loadSetupFileHash(filename);
9097 if (setup_file_hash)
9099 decodeSetupFileHash_EditorCascade(setup_file_hash);
9101 freeSetupFileHash(setup_file_hash);
9107 static void addGameControllerMappingToHash(SetupFileHash *mappings_hash,
9110 char mapping_guid[MAX_LINE_LEN];
9111 char *mapping_start, *mapping_end;
9113 // get GUID from game controller mapping line: copy complete line
9114 strncpy(mapping_guid, mapping_line, MAX_LINE_LEN - 1);
9115 mapping_guid[MAX_LINE_LEN - 1] = '\0';
9117 // get GUID from game controller mapping line: cut after GUID part
9118 mapping_start = strchr(mapping_guid, ',');
9119 if (mapping_start != NULL)
9120 *mapping_start = '\0';
9122 // cut newline from game controller mapping line
9123 mapping_end = strchr(mapping_line, '\n');
9124 if (mapping_end != NULL)
9125 *mapping_end = '\0';
9127 // add mapping entry to game controller mappings hash
9128 setHashEntry(mappings_hash, mapping_guid, mapping_line);
9131 static void LoadSetup_ReadGameControllerMappings(SetupFileHash *mappings_hash,
9136 if (!(file = fopen(filename, MODE_READ)))
9138 Error(ERR_WARN, "cannot read game controller mappings file '%s'", filename);
9145 char line[MAX_LINE_LEN];
9147 if (!fgets(line, MAX_LINE_LEN, file))
9150 addGameControllerMappingToHash(mappings_hash, line);
9158 char *filename = getSetupFilename();
9162 InitUserDataDirectory();
9164 if (!(file = fopen(filename, MODE_WRITE)))
9166 Error(ERR_WARN, "cannot write setup file '%s'", filename);
9170 fprintFileHeader(file, SETUP_FILENAME);
9174 for (i = 0; i < NUM_GLOBAL_SETUP_TOKENS; i++)
9176 /* just to make things nicer :) */
9177 if (i == SETUP_TOKEN_PLAYER_NAME + 1 ||
9178 i == SETUP_TOKEN_GRAPHICS_SET ||
9179 i == SETUP_TOKEN_VOLUME_SIMPLE ||
9180 i == SETUP_TOKEN_TOUCH_CONTROL_TYPE)
9181 fprintf(file, "\n");
9183 fprintf(file, "%s\n", getSetupLine(global_setup_tokens, "", i));
9188 fprintf(file, "\n");
9189 for (i = 0; i < NUM_EDITOR_SETUP_TOKENS; i++)
9190 fprintf(file, "%s\n", getSetupLine(editor_setup_tokens, "", i));
9192 /* shortcut setup */
9193 ssi = setup.shortcut;
9194 fprintf(file, "\n");
9195 for (i = 0; i < NUM_SHORTCUT_SETUP_TOKENS; i++)
9196 fprintf(file, "%s\n", getSetupLine(shortcut_setup_tokens, "", i));
9199 for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
9203 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
9204 fprintf(file, "\n");
9206 sii = setup.input[pnr];
9207 for (i = 0; i < NUM_PLAYER_SETUP_TOKENS; i++)
9208 fprintf(file, "%s\n", getSetupLine(player_setup_tokens, prefix, i));
9213 fprintf(file, "\n");
9214 for (i = 0; i < NUM_SYSTEM_SETUP_TOKENS; i++)
9215 fprintf(file, "%s\n", getSetupLine(system_setup_tokens, "", i));
9217 /* internal setup */
9218 /* (internal setup values not saved to user setup file) */
9222 fprintf(file, "\n");
9223 for (i = 0; i < NUM_DEBUG_SETUP_TOKENS; i++)
9224 fprintf(file, "%s\n", getSetupLine(debug_setup_tokens, "", i));
9227 soi = setup.options;
9228 fprintf(file, "\n");
9229 for (i = 0; i < NUM_OPTIONS_SETUP_TOKENS; i++)
9230 fprintf(file, "%s\n", getSetupLine(options_setup_tokens, "", i));
9234 SetFilePermissions(filename, PERMS_PRIVATE);
9237 void SaveSetup_AutoSetup()
9239 char *filename = getPath2(getSetupDir(), AUTOSETUP_FILENAME);
9243 InitUserDataDirectory();
9245 if (!(file = fopen(filename, MODE_WRITE)))
9247 Error(ERR_WARN, "cannot write auto setup file '%s'", filename);
9252 fprintFileHeader(file, AUTOSETUP_FILENAME);
9254 sasi = setup.auto_setup;
9255 for (i = 0; i < NUM_AUTO_SETUP_TOKENS; i++)
9256 fprintf(file, "%s\n", getSetupLine(auto_setup_tokens, "", i));
9260 SetFilePermissions(filename, PERMS_PRIVATE);
9265 void SaveSetup_EditorCascade()
9267 char *filename = getPath2(getSetupDir(), EDITORCASCADE_FILENAME);
9271 InitUserDataDirectory();
9273 if (!(file = fopen(filename, MODE_WRITE)))
9275 Error(ERR_WARN, "cannot write editor cascade state file '%s'", filename);
9280 fprintFileHeader(file, EDITORCASCADE_FILENAME);
9282 seci = setup.editor_cascade;
9283 for (i = 0; i < NUM_EDITOR_CASCADE_SETUP_TOKENS; i++)
9284 fprintf(file, "%s\n", getSetupLine(editor_cascade_setup_tokens, "", i));
9288 SetFilePermissions(filename, PERMS_PRIVATE);
9293 static void SaveSetup_WriteGameControllerMappings(SetupFileHash *mappings_hash,
9298 if (!(file = fopen(filename, MODE_WRITE)))
9300 Error(ERR_WARN, "cannot write game controller mappings file '%s'",filename);
9305 BEGIN_HASH_ITERATION(mappings_hash, itr)
9307 fprintf(file, "%s\n", HASH_ITERATION_VALUE(itr));
9309 END_HASH_ITERATION(mappings_hash, itr)
9314 void SaveSetup_AddGameControllerMapping(char *mapping)
9316 char *filename = getPath2(getSetupDir(), GAMECONTROLLER_BASENAME);
9317 SetupFileHash *mappings_hash = newSetupFileHash();
9319 InitUserDataDirectory();
9321 // load existing personal game controller mappings
9322 LoadSetup_ReadGameControllerMappings(mappings_hash, filename);
9324 // add new mapping to personal game controller mappings
9325 addGameControllerMappingToHash(mappings_hash, mapping);
9327 // save updated personal game controller mappings
9328 SaveSetup_WriteGameControllerMappings(mappings_hash, filename);
9330 freeSetupFileHash(mappings_hash);
9334 void LoadCustomElementDescriptions()
9336 char *filename = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
9337 SetupFileHash *setup_file_hash;
9340 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
9342 if (element_info[i].custom_description != NULL)
9344 free(element_info[i].custom_description);
9345 element_info[i].custom_description = NULL;
9349 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
9352 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
9354 char *token = getStringCat2(element_info[i].token_name, ".name");
9355 char *value = getHashEntry(setup_file_hash, token);
9358 element_info[i].custom_description = getStringCopy(value);
9363 freeSetupFileHash(setup_file_hash);
9366 static int getElementFromToken(char *token)
9368 char *value = getHashEntry(element_token_hash, token);
9373 Error(ERR_WARN, "unknown element token '%s'", token);
9375 return EL_UNDEFINED;
9378 static int get_token_parameter_value(char *token, char *value_raw)
9382 if (token == NULL || value_raw == NULL)
9383 return ARG_UNDEFINED_VALUE;
9385 suffix = strrchr(token, '.');
9389 if (strEqual(suffix, ".element"))
9390 return getElementFromToken(value_raw);
9392 /* !!! USE CORRECT VALUE TYPE (currently works also for TYPE_BOOLEAN) !!! */
9393 return get_parameter_value(value_raw, suffix, TYPE_INTEGER);
9396 void InitMenuDesignSettings_Static()
9400 /* always start with reliable default values from static default config */
9401 for (i = 0; image_config_vars[i].token != NULL; i++)
9403 char *value = getHashEntry(image_config_hash, image_config_vars[i].token);
9406 *image_config_vars[i].value =
9407 get_token_parameter_value(image_config_vars[i].token, value);
9411 static void InitMenuDesignSettings_SpecialPreProcessing()
9415 /* the following initializes hierarchical values from static configuration */
9417 /* special case: initialize "ARG_DEFAULT" values in static default config */
9418 /* (e.g., initialize "[titlemessage].fade_mode" from "[title].fade_mode") */
9419 titlescreen_initial_first_default.fade_mode =
9420 title_initial_first_default.fade_mode;
9421 titlescreen_initial_first_default.fade_delay =
9422 title_initial_first_default.fade_delay;
9423 titlescreen_initial_first_default.post_delay =
9424 title_initial_first_default.post_delay;
9425 titlescreen_initial_first_default.auto_delay =
9426 title_initial_first_default.auto_delay;
9427 titlescreen_first_default.fade_mode = title_first_default.fade_mode;
9428 titlescreen_first_default.fade_delay = title_first_default.fade_delay;
9429 titlescreen_first_default.post_delay = title_first_default.post_delay;
9430 titlescreen_first_default.auto_delay = title_first_default.auto_delay;
9431 titlemessage_initial_first_default.fade_mode =
9432 title_initial_first_default.fade_mode;
9433 titlemessage_initial_first_default.fade_delay =
9434 title_initial_first_default.fade_delay;
9435 titlemessage_initial_first_default.post_delay =
9436 title_initial_first_default.post_delay;
9437 titlemessage_initial_first_default.auto_delay =
9438 title_initial_first_default.auto_delay;
9439 titlemessage_first_default.fade_mode = title_first_default.fade_mode;
9440 titlemessage_first_default.fade_delay = title_first_default.fade_delay;
9441 titlemessage_first_default.post_delay = title_first_default.post_delay;
9442 titlemessage_first_default.auto_delay = title_first_default.auto_delay;
9444 titlescreen_initial_default.fade_mode = title_initial_default.fade_mode;
9445 titlescreen_initial_default.fade_delay = title_initial_default.fade_delay;
9446 titlescreen_initial_default.post_delay = title_initial_default.post_delay;
9447 titlescreen_initial_default.auto_delay = title_initial_default.auto_delay;
9448 titlescreen_default.fade_mode = title_default.fade_mode;
9449 titlescreen_default.fade_delay = title_default.fade_delay;
9450 titlescreen_default.post_delay = title_default.post_delay;
9451 titlescreen_default.auto_delay = title_default.auto_delay;
9452 titlemessage_initial_default.fade_mode = title_initial_default.fade_mode;
9453 titlemessage_initial_default.fade_delay = title_initial_default.fade_delay;
9454 titlemessage_initial_default.post_delay = title_initial_default.post_delay;
9455 titlemessage_initial_default.auto_delay = title_initial_default.auto_delay;
9456 titlemessage_default.fade_mode = title_default.fade_mode;
9457 titlemessage_default.fade_delay = title_default.fade_delay;
9458 titlemessage_default.post_delay = title_default.post_delay;
9459 titlemessage_default.auto_delay = title_default.auto_delay;
9461 /* special case: initialize "ARG_DEFAULT" values in static default config */
9462 /* (e.g., init "titlemessage_1.fade_mode" from "[titlemessage].fade_mode") */
9463 for (i = 0; i < MAX_NUM_TITLE_MESSAGES; i++)
9465 titlescreen_initial_first[i] = titlescreen_initial_first_default;
9466 titlescreen_first[i] = titlescreen_first_default;
9467 titlemessage_initial_first[i] = titlemessage_initial_first_default;
9468 titlemessage_first[i] = titlemessage_first_default;
9470 titlescreen_initial[i] = titlescreen_initial_default;
9471 titlescreen[i] = titlescreen_default;
9472 titlemessage_initial[i] = titlemessage_initial_default;
9473 titlemessage[i] = titlemessage_default;
9476 /* special case: initialize "ARG_DEFAULT" values in static default config */
9477 /* (eg, init "menu.enter_screen.SCORES.xyz" from "menu.enter_screen.xyz") */
9478 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
9480 if (i == GFX_SPECIAL_ARG_TITLE) /* title values already initialized */
9483 menu.enter_screen[i] = menu.enter_screen[GFX_SPECIAL_ARG_DEFAULT];
9484 menu.leave_screen[i] = menu.leave_screen[GFX_SPECIAL_ARG_DEFAULT];
9485 menu.next_screen[i] = menu.next_screen[GFX_SPECIAL_ARG_DEFAULT];
9488 /* special case: initialize "ARG_DEFAULT" values in static default config */
9489 /* (eg, init "viewport.door_1.MAIN.xyz" from "viewport.door_1.xyz") */
9490 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
9492 viewport.window[i] = viewport.window[GFX_SPECIAL_ARG_DEFAULT];
9493 viewport.playfield[i] = viewport.playfield[GFX_SPECIAL_ARG_DEFAULT];
9494 viewport.door_1[i] = viewport.door_1[GFX_SPECIAL_ARG_DEFAULT];
9496 if (i == GFX_SPECIAL_ARG_EDITOR) /* editor values already initialized */
9499 viewport.door_2[i] = viewport.door_2[GFX_SPECIAL_ARG_DEFAULT];
9503 static void InitMenuDesignSettings_SpecialPostProcessing()
9507 struct XY *dst, *src;
9511 { &game.button.save, &game.button.stop },
9512 { &game.button.pause2, &game.button.pause },
9513 { &game.button.load, &game.button.play },
9514 { &game.button.undo, &game.button.stop },
9515 { &game.button.redo, &game.button.play },
9521 /* special case: initialize later added SETUP list size from LEVELS value */
9522 if (menu.list_size[GAME_MODE_SETUP] == -1)
9523 menu.list_size[GAME_MODE_SETUP] = menu.list_size[GAME_MODE_LEVELS];
9525 /* set default position for snapshot buttons to stop/pause/play buttons */
9526 for (i = 0; game_buttons_xy[i].dst != NULL; i++)
9527 if ((*game_buttons_xy[i].dst).x == -1 &&
9528 (*game_buttons_xy[i].dst).y == -1)
9529 *game_buttons_xy[i].dst = *game_buttons_xy[i].src;
9532 static void InitMenuDesignSettings_SpecialPostProcessing_AfterGraphics()
9536 struct XYTileSize *dst, *src;
9539 editor_buttons_xy[] =
9542 &editor.button.element_left, &editor.palette.element_left,
9543 IMG_GFX_EDITOR_BUTTON_ELEMENT_LEFT
9546 &editor.button.element_middle, &editor.palette.element_middle,
9547 IMG_GFX_EDITOR_BUTTON_ELEMENT_MIDDLE
9550 &editor.button.element_right, &editor.palette.element_right,
9551 IMG_GFX_EDITOR_BUTTON_ELEMENT_RIGHT
9558 /* set default position for element buttons to element graphics */
9559 for (i = 0; editor_buttons_xy[i].dst != NULL; i++)
9561 if ((*editor_buttons_xy[i].dst).x == -1 &&
9562 (*editor_buttons_xy[i].dst).y == -1)
9564 struct GraphicInfo *gd = &graphic_info[editor_buttons_xy[i].graphic];
9566 gd->width = gd->height = editor_buttons_xy[i].src->tile_size;
9568 *editor_buttons_xy[i].dst = *editor_buttons_xy[i].src;
9573 static void LoadMenuDesignSettingsFromFilename(char *filename)
9575 static struct TitleFadingInfo tfi;
9576 static struct TitleMessageInfo tmi;
9577 static struct TokenInfo title_tokens[] =
9579 { TYPE_INTEGER, &tfi.fade_mode, ".fade_mode" },
9580 { TYPE_INTEGER, &tfi.fade_delay, ".fade_delay" },
9581 { TYPE_INTEGER, &tfi.post_delay, ".post_delay" },
9582 { TYPE_INTEGER, &tfi.auto_delay, ".auto_delay" },
9586 static struct TokenInfo titlemessage_tokens[] =
9588 { TYPE_INTEGER, &tmi.x, ".x" },
9589 { TYPE_INTEGER, &tmi.y, ".y" },
9590 { TYPE_INTEGER, &tmi.width, ".width" },
9591 { TYPE_INTEGER, &tmi.height, ".height" },
9592 { TYPE_INTEGER, &tmi.chars, ".chars" },
9593 { TYPE_INTEGER, &tmi.lines, ".lines" },
9594 { TYPE_INTEGER, &tmi.align, ".align" },
9595 { TYPE_INTEGER, &tmi.valign, ".valign" },
9596 { TYPE_INTEGER, &tmi.font, ".font" },
9597 { TYPE_BOOLEAN, &tmi.autowrap, ".autowrap" },
9598 { TYPE_BOOLEAN, &tmi.centered, ".centered" },
9599 { TYPE_BOOLEAN, &tmi.parse_comments, ".parse_comments" },
9600 { TYPE_INTEGER, &tmi.sort_priority, ".sort_priority" },
9601 { TYPE_INTEGER, &tmi.fade_mode, ".fade_mode" },
9602 { TYPE_INTEGER, &tmi.fade_delay, ".fade_delay" },
9603 { TYPE_INTEGER, &tmi.post_delay, ".post_delay" },
9604 { TYPE_INTEGER, &tmi.auto_delay, ".auto_delay" },
9610 struct TitleFadingInfo *info;
9615 /* initialize first titles from "enter screen" definitions, if defined */
9616 { &title_initial_first_default, "menu.enter_screen.TITLE" },
9617 { &title_first_default, "menu.enter_screen.TITLE" },
9619 /* initialize title screens from "next screen" definitions, if defined */
9620 { &title_initial_default, "menu.next_screen.TITLE" },
9621 { &title_default, "menu.next_screen.TITLE" },
9627 struct TitleMessageInfo *array;
9630 titlemessage_arrays[] =
9632 /* initialize first titles from "enter screen" definitions, if defined */
9633 { titlescreen_initial_first, "menu.enter_screen.TITLE" },
9634 { titlescreen_first, "menu.enter_screen.TITLE" },
9635 { titlemessage_initial_first, "menu.enter_screen.TITLE" },
9636 { titlemessage_first, "menu.enter_screen.TITLE" },
9638 /* initialize titles from "next screen" definitions, if defined */
9639 { titlescreen_initial, "menu.next_screen.TITLE" },
9640 { titlescreen, "menu.next_screen.TITLE" },
9641 { titlemessage_initial, "menu.next_screen.TITLE" },
9642 { titlemessage, "menu.next_screen.TITLE" },
9644 /* overwrite titles with title definitions, if defined */
9645 { titlescreen_initial_first, "[title_initial]" },
9646 { titlescreen_first, "[title]" },
9647 { titlemessage_initial_first, "[title_initial]" },
9648 { titlemessage_first, "[title]" },
9650 { titlescreen_initial, "[title_initial]" },
9651 { titlescreen, "[title]" },
9652 { titlemessage_initial, "[title_initial]" },
9653 { titlemessage, "[title]" },
9655 /* overwrite titles with title screen/message definitions, if defined */
9656 { titlescreen_initial_first, "[titlescreen_initial]" },
9657 { titlescreen_first, "[titlescreen]" },
9658 { titlemessage_initial_first, "[titlemessage_initial]" },
9659 { titlemessage_first, "[titlemessage]" },
9661 { titlescreen_initial, "[titlescreen_initial]" },
9662 { titlescreen, "[titlescreen]" },
9663 { titlemessage_initial, "[titlemessage_initial]" },
9664 { titlemessage, "[titlemessage]" },
9668 SetupFileHash *setup_file_hash;
9671 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
9674 /* the following initializes hierarchical values from dynamic configuration */
9676 /* special case: initialize with default values that may be overwritten */
9677 /* (e.g., init "menu.draw_xoffset.INFO" from "menu.draw_xoffset") */
9678 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
9680 char *value_1 = getHashEntry(setup_file_hash, "menu.draw_xoffset");
9681 char *value_2 = getHashEntry(setup_file_hash, "menu.draw_yoffset");
9682 char *value_3 = getHashEntry(setup_file_hash, "menu.list_size");
9684 if (value_1 != NULL)
9685 menu.draw_xoffset[i] = get_integer_from_string(value_1);
9686 if (value_2 != NULL)
9687 menu.draw_yoffset[i] = get_integer_from_string(value_2);
9688 if (value_3 != NULL)
9689 menu.list_size[i] = get_integer_from_string(value_3);
9692 /* special case: initialize with default values that may be overwritten */
9693 /* (eg, init "menu.draw_xoffset.INFO[XXX]" from "menu.draw_xoffset.INFO") */
9694 for (i = 0; i < NUM_SPECIAL_GFX_INFO_ARGS; i++)
9696 char *value_1 = getHashEntry(setup_file_hash, "menu.draw_xoffset.INFO");
9697 char *value_2 = getHashEntry(setup_file_hash, "menu.draw_yoffset.INFO");
9699 if (value_1 != NULL)
9700 menu.draw_xoffset_info[i] = get_integer_from_string(value_1);
9701 if (value_2 != NULL)
9702 menu.draw_yoffset_info[i] = get_integer_from_string(value_2);
9704 if (i == GFX_SPECIAL_ARG_INFO_ELEMENTS)
9706 char *value_1 = getHashEntry(setup_file_hash, "menu.list_size.INFO");
9708 if (value_1 != NULL)
9709 menu.list_size_info[i] = get_integer_from_string(value_1);
9713 /* special case: initialize with default values that may be overwritten */
9714 /* (eg, init "menu.draw_xoffset.SETUP[XXX]" from "menu.draw_xoffset.SETUP") */
9715 for (i = 0; i < NUM_SPECIAL_GFX_SETUP_ARGS; i++)
9717 char *value_1 = getHashEntry(setup_file_hash, "menu.draw_xoffset.SETUP");
9718 char *value_2 = getHashEntry(setup_file_hash, "menu.draw_yoffset.SETUP");
9720 if (value_1 != NULL)
9721 menu.draw_xoffset_setup[i] = get_integer_from_string(value_1);
9722 if (value_2 != NULL)
9723 menu.draw_yoffset_setup[i] = get_integer_from_string(value_2);
9726 /* special case: initialize with default values that may be overwritten */
9727 /* (eg, init "menu.enter_screen.SCORES.xyz" from "menu.enter_screen.xyz") */
9728 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
9730 char *token_1 = "menu.enter_screen.fade_mode";
9731 char *token_2 = "menu.enter_screen.fade_delay";
9732 char *token_3 = "menu.enter_screen.post_delay";
9733 char *token_4 = "menu.leave_screen.fade_mode";
9734 char *token_5 = "menu.leave_screen.fade_delay";
9735 char *token_6 = "menu.leave_screen.post_delay";
9736 char *token_7 = "menu.next_screen.fade_mode";
9737 char *token_8 = "menu.next_screen.fade_delay";
9738 char *token_9 = "menu.next_screen.post_delay";
9739 char *value_1 = getHashEntry(setup_file_hash, token_1);
9740 char *value_2 = getHashEntry(setup_file_hash, token_2);
9741 char *value_3 = getHashEntry(setup_file_hash, token_3);
9742 char *value_4 = getHashEntry(setup_file_hash, token_4);
9743 char *value_5 = getHashEntry(setup_file_hash, token_5);
9744 char *value_6 = getHashEntry(setup_file_hash, token_6);
9745 char *value_7 = getHashEntry(setup_file_hash, token_7);
9746 char *value_8 = getHashEntry(setup_file_hash, token_8);
9747 char *value_9 = getHashEntry(setup_file_hash, token_9);
9749 if (value_1 != NULL)
9750 menu.enter_screen[i].fade_mode = get_token_parameter_value(token_1,
9752 if (value_2 != NULL)
9753 menu.enter_screen[i].fade_delay = get_token_parameter_value(token_2,
9755 if (value_3 != NULL)
9756 menu.enter_screen[i].post_delay = get_token_parameter_value(token_3,
9758 if (value_4 != NULL)
9759 menu.leave_screen[i].fade_mode = get_token_parameter_value(token_4,
9761 if (value_5 != NULL)
9762 menu.leave_screen[i].fade_delay = get_token_parameter_value(token_5,
9764 if (value_6 != NULL)
9765 menu.leave_screen[i].post_delay = get_token_parameter_value(token_6,
9767 if (value_7 != NULL)
9768 menu.next_screen[i].fade_mode = get_token_parameter_value(token_7,
9770 if (value_8 != NULL)
9771 menu.next_screen[i].fade_delay = get_token_parameter_value(token_8,
9773 if (value_9 != NULL)
9774 menu.next_screen[i].post_delay = get_token_parameter_value(token_9,
9778 /* special case: initialize with default values that may be overwritten */
9779 /* (eg, init "viewport.door_1.MAIN.xyz" from "viewport.door_1.xyz") */
9780 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
9782 char *token_w1 = "viewport.window.width";
9783 char *token_w2 = "viewport.window.height";
9784 char *token_01 = "viewport.playfield.x";
9785 char *token_02 = "viewport.playfield.y";
9786 char *token_03 = "viewport.playfield.width";
9787 char *token_04 = "viewport.playfield.height";
9788 char *token_05 = "viewport.playfield.border_size";
9789 char *token_06 = "viewport.door_1.x";
9790 char *token_07 = "viewport.door_1.y";
9791 char *token_08 = "viewport.door_1.width";
9792 char *token_09 = "viewport.door_1.height";
9793 char *token_10 = "viewport.door_1.border_size";
9794 char *token_11 = "viewport.door_2.x";
9795 char *token_12 = "viewport.door_2.y";
9796 char *token_13 = "viewport.door_2.width";
9797 char *token_14 = "viewport.door_2.height";
9798 char *token_15 = "viewport.door_2.border_size";
9799 char *value_w1 = getHashEntry(setup_file_hash, token_w1);
9800 char *value_w2 = getHashEntry(setup_file_hash, token_w2);
9801 char *value_01 = getHashEntry(setup_file_hash, token_01);
9802 char *value_02 = getHashEntry(setup_file_hash, token_02);
9803 char *value_03 = getHashEntry(setup_file_hash, token_03);
9804 char *value_04 = getHashEntry(setup_file_hash, token_04);
9805 char *value_05 = getHashEntry(setup_file_hash, token_05);
9806 char *value_06 = getHashEntry(setup_file_hash, token_06);
9807 char *value_07 = getHashEntry(setup_file_hash, token_07);
9808 char *value_08 = getHashEntry(setup_file_hash, token_08);
9809 char *value_09 = getHashEntry(setup_file_hash, token_09);
9810 char *value_10 = getHashEntry(setup_file_hash, token_10);
9811 char *value_11 = getHashEntry(setup_file_hash, token_11);
9812 char *value_12 = getHashEntry(setup_file_hash, token_12);
9813 char *value_13 = getHashEntry(setup_file_hash, token_13);
9814 char *value_14 = getHashEntry(setup_file_hash, token_14);
9815 char *value_15 = getHashEntry(setup_file_hash, token_15);
9817 if (value_w1 != NULL)
9818 viewport.window[i].width = get_token_parameter_value(token_w1, value_w1);
9819 if (value_w2 != NULL)
9820 viewport.window[i].height = get_token_parameter_value(token_w2, value_w2);
9821 if (value_01 != NULL)
9822 viewport.playfield[i].x = get_token_parameter_value(token_01, value_01);
9823 if (value_02 != NULL)
9824 viewport.playfield[i].y = get_token_parameter_value(token_02, value_02);
9825 if (value_03 != NULL)
9826 viewport.playfield[i].width = get_token_parameter_value(token_03,
9828 if (value_04 != NULL)
9829 viewport.playfield[i].height = get_token_parameter_value(token_04,
9831 if (value_05 != NULL)
9832 viewport.playfield[i].border_size = get_token_parameter_value(token_05,
9834 if (value_06 != NULL)
9835 viewport.door_1[i].x = get_token_parameter_value(token_06, value_06);
9836 if (value_07 != NULL)
9837 viewport.door_1[i].y = get_token_parameter_value(token_07, value_07);
9838 if (value_08 != NULL)
9839 viewport.door_1[i].width = get_token_parameter_value(token_08, value_08);
9840 if (value_09 != NULL)
9841 viewport.door_1[i].height = get_token_parameter_value(token_09, value_09);
9842 if (value_10 != NULL)
9843 viewport.door_1[i].border_size = get_token_parameter_value(token_10,
9845 if (value_11 != NULL)
9846 viewport.door_2[i].x = get_token_parameter_value(token_11, value_11);
9847 if (value_12 != NULL)
9848 viewport.door_2[i].y = get_token_parameter_value(token_12, value_12);
9849 if (value_13 != NULL)
9850 viewport.door_2[i].width = get_token_parameter_value(token_13, value_13);
9851 if (value_14 != NULL)
9852 viewport.door_2[i].height = get_token_parameter_value(token_14, value_14);
9853 if (value_15 != NULL)
9854 viewport.door_1[i].border_size = get_token_parameter_value(token_15,
9858 /* special case: initialize with default values that may be overwritten */
9859 /* (e.g., init "[title].fade_mode" from "menu.next_screen.TITLE.fade_mode") */
9860 for (i = 0; title_info[i].info != NULL; i++)
9862 struct TitleFadingInfo *info = title_info[i].info;
9863 char *base_token = title_info[i].text;
9865 for (j = 0; title_tokens[j].type != -1; j++)
9867 char *token = getStringCat2(base_token, title_tokens[j].text);
9868 char *value = getHashEntry(setup_file_hash, token);
9872 int parameter_value = get_token_parameter_value(token, value);
9876 *(int *)title_tokens[j].value = (int)parameter_value;
9885 /* special case: initialize with default values that may be overwritten */
9886 /* (e.g., init "titlemessage_1.fade_mode" from "[titlemessage].fade_mode") */
9887 for (i = 0; titlemessage_arrays[i].array != NULL; i++)
9889 struct TitleMessageInfo *array = titlemessage_arrays[i].array;
9890 char *base_token = titlemessage_arrays[i].text;
9892 for (j = 0; titlemessage_tokens[j].type != -1; j++)
9894 char *token = getStringCat2(base_token, titlemessage_tokens[j].text);
9895 char *value = getHashEntry(setup_file_hash, token);
9899 int parameter_value = get_token_parameter_value(token, value);
9901 for (k = 0; k < MAX_NUM_TITLE_MESSAGES; k++)
9905 if (titlemessage_tokens[j].type == TYPE_INTEGER)
9906 *(int *)titlemessage_tokens[j].value = (int)parameter_value;
9908 *(boolean *)titlemessage_tokens[j].value = (boolean)parameter_value;
9918 /* read (and overwrite with) values that may be specified in config file */
9919 for (i = 0; image_config_vars[i].token != NULL; i++)
9921 char *value = getHashEntry(setup_file_hash, image_config_vars[i].token);
9923 /* (ignore definitions set to "[DEFAULT]" which are already initialized) */
9924 if (value != NULL && !strEqual(value, ARG_DEFAULT))
9925 *image_config_vars[i].value =
9926 get_token_parameter_value(image_config_vars[i].token, value);
9929 freeSetupFileHash(setup_file_hash);
9932 void LoadMenuDesignSettings()
9934 char *filename_base = UNDEFINED_FILENAME, *filename_local;
9936 InitMenuDesignSettings_Static();
9937 InitMenuDesignSettings_SpecialPreProcessing();
9939 if (!GFX_OVERRIDE_ARTWORK(ARTWORK_TYPE_GRAPHICS))
9941 /* first look for special settings configured in level series config */
9942 filename_base = getCustomArtworkLevelConfigFilename(ARTWORK_TYPE_GRAPHICS);
9944 if (fileExists(filename_base))
9945 LoadMenuDesignSettingsFromFilename(filename_base);
9948 filename_local = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
9950 if (filename_local != NULL && !strEqual(filename_base, filename_local))
9951 LoadMenuDesignSettingsFromFilename(filename_local);
9953 InitMenuDesignSettings_SpecialPostProcessing();
9956 void LoadMenuDesignSettings_AfterGraphics()
9958 InitMenuDesignSettings_SpecialPostProcessing_AfterGraphics();
9961 void LoadUserDefinedEditorElementList(int **elements, int *num_elements)
9963 char *filename = getEditorSetupFilename();
9964 SetupFileList *setup_file_list, *list;
9965 SetupFileHash *element_hash;
9966 int num_unknown_tokens = 0;
9969 if ((setup_file_list = loadSetupFileList(filename)) == NULL)
9972 element_hash = newSetupFileHash();
9974 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
9975 setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
9977 /* determined size may be larger than needed (due to unknown elements) */
9979 for (list = setup_file_list; list != NULL; list = list->next)
9982 /* add space for up to 3 more elements for padding that may be needed */
9985 /* free memory for old list of elements, if needed */
9986 checked_free(*elements);
9988 /* allocate memory for new list of elements */
9989 *elements = checked_malloc(*num_elements * sizeof(int));
9992 for (list = setup_file_list; list != NULL; list = list->next)
9994 char *value = getHashEntry(element_hash, list->token);
9996 if (value == NULL) /* try to find obsolete token mapping */
9998 char *mapped_token = get_mapped_token(list->token);
10000 if (mapped_token != NULL)
10002 value = getHashEntry(element_hash, mapped_token);
10004 free(mapped_token);
10010 (*elements)[(*num_elements)++] = atoi(value);
10014 if (num_unknown_tokens == 0)
10016 Error(ERR_INFO_LINE, "-");
10017 Error(ERR_INFO, "warning: unknown token(s) found in config file:");
10018 Error(ERR_INFO, "- config file: '%s'", filename);
10020 num_unknown_tokens++;
10023 Error(ERR_INFO, "- token: '%s'", list->token);
10027 if (num_unknown_tokens > 0)
10028 Error(ERR_INFO_LINE, "-");
10030 while (*num_elements % 4) /* pad with empty elements, if needed */
10031 (*elements)[(*num_elements)++] = EL_EMPTY;
10033 freeSetupFileList(setup_file_list);
10034 freeSetupFileHash(element_hash);
10037 for (i = 0; i < *num_elements; i++)
10038 printf("editor: element '%s' [%d]\n",
10039 element_info[(*elements)[i]].token_name, (*elements)[i]);
10043 static struct MusicFileInfo *get_music_file_info_ext(char *basename, int music,
10046 SetupFileHash *setup_file_hash = NULL;
10047 struct MusicFileInfo tmp_music_file_info, *new_music_file_info;
10048 char *filename_music, *filename_prefix, *filename_info;
10054 token_to_value_ptr[] =
10056 { "title_header", &tmp_music_file_info.title_header },
10057 { "artist_header", &tmp_music_file_info.artist_header },
10058 { "album_header", &tmp_music_file_info.album_header },
10059 { "year_header", &tmp_music_file_info.year_header },
10061 { "title", &tmp_music_file_info.title },
10062 { "artist", &tmp_music_file_info.artist },
10063 { "album", &tmp_music_file_info.album },
10064 { "year", &tmp_music_file_info.year },
10070 filename_music = (is_sound ? getCustomSoundFilename(basename) :
10071 getCustomMusicFilename(basename));
10073 if (filename_music == NULL)
10076 /* ---------- try to replace file extension ---------- */
10078 filename_prefix = getStringCopy(filename_music);
10079 if (strrchr(filename_prefix, '.') != NULL)
10080 *strrchr(filename_prefix, '.') = '\0';
10081 filename_info = getStringCat2(filename_prefix, ".txt");
10083 if (fileExists(filename_info))
10084 setup_file_hash = loadSetupFileHash(filename_info);
10086 free(filename_prefix);
10087 free(filename_info);
10089 if (setup_file_hash == NULL)
10091 /* ---------- try to add file extension ---------- */
10093 filename_prefix = getStringCopy(filename_music);
10094 filename_info = getStringCat2(filename_prefix, ".txt");
10096 if (fileExists(filename_info))
10097 setup_file_hash = loadSetupFileHash(filename_info);
10099 free(filename_prefix);
10100 free(filename_info);
10103 if (setup_file_hash == NULL)
10106 /* ---------- music file info found ---------- */
10108 clear_mem(&tmp_music_file_info, sizeof(struct MusicFileInfo));
10110 for (i = 0; token_to_value_ptr[i].token != NULL; i++)
10112 char *value = getHashEntry(setup_file_hash, token_to_value_ptr[i].token);
10114 *token_to_value_ptr[i].value_ptr =
10115 getStringCopy(value != NULL && *value != '\0' ? value : UNKNOWN_NAME);
10118 tmp_music_file_info.basename = getStringCopy(basename);
10119 tmp_music_file_info.music = music;
10120 tmp_music_file_info.is_sound = is_sound;
10122 new_music_file_info = checked_malloc(sizeof(struct MusicFileInfo));
10123 *new_music_file_info = tmp_music_file_info;
10125 return new_music_file_info;
10128 static struct MusicFileInfo *get_music_file_info(char *basename, int music)
10130 return get_music_file_info_ext(basename, music, FALSE);
10133 static struct MusicFileInfo *get_sound_file_info(char *basename, int sound)
10135 return get_music_file_info_ext(basename, sound, TRUE);
10138 static boolean music_info_listed_ext(struct MusicFileInfo *list,
10139 char *basename, boolean is_sound)
10141 for (; list != NULL; list = list->next)
10142 if (list->is_sound == is_sound && strEqual(list->basename, basename))
10148 static boolean music_info_listed(struct MusicFileInfo *list, char *basename)
10150 return music_info_listed_ext(list, basename, FALSE);
10153 static boolean sound_info_listed(struct MusicFileInfo *list, char *basename)
10155 return music_info_listed_ext(list, basename, TRUE);
10158 void LoadMusicInfo()
10160 char *music_directory = getCustomMusicDirectory();
10161 int num_music = getMusicListSize();
10162 int num_music_noconf = 0;
10163 int num_sounds = getSoundListSize();
10165 DirectoryEntry *dir_entry;
10166 struct FileInfo *music, *sound;
10167 struct MusicFileInfo *next, **new;
10170 while (music_file_info != NULL)
10172 next = music_file_info->next;
10174 checked_free(music_file_info->basename);
10176 checked_free(music_file_info->title_header);
10177 checked_free(music_file_info->artist_header);
10178 checked_free(music_file_info->album_header);
10179 checked_free(music_file_info->year_header);
10181 checked_free(music_file_info->title);
10182 checked_free(music_file_info->artist);
10183 checked_free(music_file_info->album);
10184 checked_free(music_file_info->year);
10186 free(music_file_info);
10188 music_file_info = next;
10191 new = &music_file_info;
10193 for (i = 0; i < num_music; i++)
10195 music = getMusicListEntry(i);
10197 if (music->filename == NULL)
10200 if (strEqual(music->filename, UNDEFINED_FILENAME))
10203 /* a configured file may be not recognized as music */
10204 if (!FileIsMusic(music->filename))
10207 if (!music_info_listed(music_file_info, music->filename))
10209 *new = get_music_file_info(music->filename, i);
10212 new = &(*new)->next;
10216 if ((dir = openDirectory(music_directory)) == NULL)
10218 Error(ERR_WARN, "cannot read music directory '%s'", music_directory);
10222 while ((dir_entry = readDirectory(dir)) != NULL) /* loop all entries */
10224 char *basename = dir_entry->basename;
10225 boolean music_already_used = FALSE;
10228 /* skip all music files that are configured in music config file */
10229 for (i = 0; i < num_music; i++)
10231 music = getMusicListEntry(i);
10233 if (music->filename == NULL)
10236 if (strEqual(basename, music->filename))
10238 music_already_used = TRUE;
10243 if (music_already_used)
10246 if (!FileIsMusic(dir_entry->filename))
10249 if (!music_info_listed(music_file_info, basename))
10251 *new = get_music_file_info(basename, MAP_NOCONF_MUSIC(num_music_noconf));
10254 new = &(*new)->next;
10257 num_music_noconf++;
10260 closeDirectory(dir);
10262 for (i = 0; i < num_sounds; i++)
10264 sound = getSoundListEntry(i);
10266 if (sound->filename == NULL)
10269 if (strEqual(sound->filename, UNDEFINED_FILENAME))
10272 /* a configured file may be not recognized as sound */
10273 if (!FileIsSound(sound->filename))
10276 if (!sound_info_listed(music_file_info, sound->filename))
10278 *new = get_sound_file_info(sound->filename, i);
10280 new = &(*new)->next;
10285 void add_helpanim_entry(int element, int action, int direction, int delay,
10286 int *num_list_entries)
10288 struct HelpAnimInfo *new_list_entry;
10289 (*num_list_entries)++;
10292 checked_realloc(helpanim_info,
10293 *num_list_entries * sizeof(struct HelpAnimInfo));
10294 new_list_entry = &helpanim_info[*num_list_entries - 1];
10296 new_list_entry->element = element;
10297 new_list_entry->action = action;
10298 new_list_entry->direction = direction;
10299 new_list_entry->delay = delay;
10302 void print_unknown_token(char *filename, char *token, int token_nr)
10306 Error(ERR_INFO_LINE, "-");
10307 Error(ERR_INFO, "warning: unknown token(s) found in config file:");
10308 Error(ERR_INFO, "- config file: '%s'", filename);
10311 Error(ERR_INFO, "- token: '%s'", token);
10314 void print_unknown_token_end(int token_nr)
10317 Error(ERR_INFO_LINE, "-");
10320 void LoadHelpAnimInfo()
10322 char *filename = getHelpAnimFilename();
10323 SetupFileList *setup_file_list = NULL, *list;
10324 SetupFileHash *element_hash, *action_hash, *direction_hash;
10325 int num_list_entries = 0;
10326 int num_unknown_tokens = 0;
10329 if (fileExists(filename))
10330 setup_file_list = loadSetupFileList(filename);
10332 if (setup_file_list == NULL)
10334 /* use reliable default values from static configuration */
10335 SetupFileList *insert_ptr;
10337 insert_ptr = setup_file_list =
10338 newSetupFileList(helpanim_config[0].token,
10339 helpanim_config[0].value);
10341 for (i = 1; helpanim_config[i].token; i++)
10342 insert_ptr = addListEntry(insert_ptr,
10343 helpanim_config[i].token,
10344 helpanim_config[i].value);
10347 element_hash = newSetupFileHash();
10348 action_hash = newSetupFileHash();
10349 direction_hash = newSetupFileHash();
10351 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
10352 setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
10354 for (i = 0; i < NUM_ACTIONS; i++)
10355 setHashEntry(action_hash, element_action_info[i].suffix,
10356 i_to_a(element_action_info[i].value));
10358 /* do not store direction index (bit) here, but direction value! */
10359 for (i = 0; i < NUM_DIRECTIONS_FULL; i++)
10360 setHashEntry(direction_hash, element_direction_info[i].suffix,
10361 i_to_a(1 << element_direction_info[i].value));
10363 for (list = setup_file_list; list != NULL; list = list->next)
10365 char *element_token, *action_token, *direction_token;
10366 char *element_value, *action_value, *direction_value;
10367 int delay = atoi(list->value);
10369 if (strEqual(list->token, "end"))
10371 add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
10376 /* first try to break element into element/action/direction parts;
10377 if this does not work, also accept combined "element[.act][.dir]"
10378 elements (like "dynamite.active"), which are unique elements */
10380 if (strchr(list->token, '.') == NULL) /* token contains no '.' */
10382 element_value = getHashEntry(element_hash, list->token);
10383 if (element_value != NULL) /* element found */
10384 add_helpanim_entry(atoi(element_value), -1, -1, delay,
10385 &num_list_entries);
10388 /* no further suffixes found -- this is not an element */
10389 print_unknown_token(filename, list->token, num_unknown_tokens++);
10395 /* token has format "<prefix>.<something>" */
10397 action_token = strchr(list->token, '.'); /* suffix may be action ... */
10398 direction_token = action_token; /* ... or direction */
10400 element_token = getStringCopy(list->token);
10401 *strchr(element_token, '.') = '\0';
10403 element_value = getHashEntry(element_hash, element_token);
10405 if (element_value == NULL) /* this is no element */
10407 element_value = getHashEntry(element_hash, list->token);
10408 if (element_value != NULL) /* combined element found */
10409 add_helpanim_entry(atoi(element_value), -1, -1, delay,
10410 &num_list_entries);
10412 print_unknown_token(filename, list->token, num_unknown_tokens++);
10414 free(element_token);
10419 action_value = getHashEntry(action_hash, action_token);
10421 if (action_value != NULL) /* action found */
10423 add_helpanim_entry(atoi(element_value), atoi(action_value), -1, delay,
10424 &num_list_entries);
10426 free(element_token);
10431 direction_value = getHashEntry(direction_hash, direction_token);
10433 if (direction_value != NULL) /* direction found */
10435 add_helpanim_entry(atoi(element_value), -1, atoi(direction_value), delay,
10436 &num_list_entries);
10438 free(element_token);
10443 if (strchr(action_token + 1, '.') == NULL)
10445 /* no further suffixes found -- this is not an action nor direction */
10447 element_value = getHashEntry(element_hash, list->token);
10448 if (element_value != NULL) /* combined element found */
10449 add_helpanim_entry(atoi(element_value), -1, -1, delay,
10450 &num_list_entries);
10452 print_unknown_token(filename, list->token, num_unknown_tokens++);
10454 free(element_token);
10459 /* token has format "<prefix>.<suffix>.<something>" */
10461 direction_token = strchr(action_token + 1, '.');
10463 action_token = getStringCopy(action_token);
10464 *strchr(action_token + 1, '.') = '\0';
10466 action_value = getHashEntry(action_hash, action_token);
10468 if (action_value == NULL) /* this is no action */
10470 element_value = getHashEntry(element_hash, list->token);
10471 if (element_value != NULL) /* combined element found */
10472 add_helpanim_entry(atoi(element_value), -1, -1, delay,
10473 &num_list_entries);
10475 print_unknown_token(filename, list->token, num_unknown_tokens++);
10477 free(element_token);
10478 free(action_token);
10483 direction_value = getHashEntry(direction_hash, direction_token);
10485 if (direction_value != NULL) /* direction found */
10487 add_helpanim_entry(atoi(element_value), atoi(action_value),
10488 atoi(direction_value), delay, &num_list_entries);
10490 free(element_token);
10491 free(action_token);
10496 /* this is no direction */
10498 element_value = getHashEntry(element_hash, list->token);
10499 if (element_value != NULL) /* combined element found */
10500 add_helpanim_entry(atoi(element_value), -1, -1, delay,
10501 &num_list_entries);
10503 print_unknown_token(filename, list->token, num_unknown_tokens++);
10505 free(element_token);
10506 free(action_token);
10509 print_unknown_token_end(num_unknown_tokens);
10511 add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
10512 add_helpanim_entry(HELPANIM_LIST_END, -1, -1, -1, &num_list_entries);
10514 freeSetupFileList(setup_file_list);
10515 freeSetupFileHash(element_hash);
10516 freeSetupFileHash(action_hash);
10517 freeSetupFileHash(direction_hash);
10520 for (i = 0; i < num_list_entries; i++)
10521 printf("::: '%s': %d, %d, %d => %d\n",
10522 EL_NAME(helpanim_info[i].element),
10523 helpanim_info[i].element,
10524 helpanim_info[i].action,
10525 helpanim_info[i].direction,
10526 helpanim_info[i].delay);
10530 void LoadHelpTextInfo()
10532 char *filename = getHelpTextFilename();
10535 if (helptext_info != NULL)
10537 freeSetupFileHash(helptext_info);
10538 helptext_info = NULL;
10541 if (fileExists(filename))
10542 helptext_info = loadSetupFileHash(filename);
10544 if (helptext_info == NULL)
10546 /* use reliable default values from static configuration */
10547 helptext_info = newSetupFileHash();
10549 for (i = 0; helptext_config[i].token; i++)
10550 setHashEntry(helptext_info,
10551 helptext_config[i].token,
10552 helptext_config[i].value);
10556 BEGIN_HASH_ITERATION(helptext_info, itr)
10558 printf("::: '%s' => '%s'\n",
10559 HASH_ITERATION_TOKEN(itr), HASH_ITERATION_VALUE(itr));
10561 END_HASH_ITERATION(hash, itr)
10566 /* ------------------------------------------------------------------------- */
10567 /* convert levels */
10568 /* ------------------------------------------------------------------------- */
10570 #define MAX_NUM_CONVERT_LEVELS 1000
10572 void ConvertLevels()
10574 static LevelDirTree *convert_leveldir = NULL;
10575 static int convert_level_nr = -1;
10576 static int num_levels_handled = 0;
10577 static int num_levels_converted = 0;
10578 static boolean levels_failed[MAX_NUM_CONVERT_LEVELS];
10581 convert_leveldir = getTreeInfoFromIdentifier(leveldir_first,
10582 global.convert_leveldir);
10584 if (convert_leveldir == NULL)
10585 Error(ERR_EXIT, "no such level identifier: '%s'",
10586 global.convert_leveldir);
10588 leveldir_current = convert_leveldir;
10590 if (global.convert_level_nr != -1)
10592 convert_leveldir->first_level = global.convert_level_nr;
10593 convert_leveldir->last_level = global.convert_level_nr;
10596 convert_level_nr = convert_leveldir->first_level;
10598 PrintLine("=", 79);
10599 Print("Converting levels\n");
10600 PrintLine("-", 79);
10601 Print("Level series identifier: '%s'\n", convert_leveldir->identifier);
10602 Print("Level series name: '%s'\n", convert_leveldir->name);
10603 Print("Level series author: '%s'\n", convert_leveldir->author);
10604 Print("Number of levels: %d\n", convert_leveldir->levels);
10605 PrintLine("=", 79);
10608 for (i = 0; i < MAX_NUM_CONVERT_LEVELS; i++)
10609 levels_failed[i] = FALSE;
10611 while (convert_level_nr <= convert_leveldir->last_level)
10613 char *level_filename;
10616 level_nr = convert_level_nr++;
10618 Print("Level %03d: ", level_nr);
10620 LoadLevel(level_nr);
10621 if (level.no_level_file || level.no_valid_file)
10623 Print("(no level)\n");
10627 Print("converting level ... ");
10629 level_filename = getDefaultLevelFilename(level_nr);
10630 new_level = !fileExists(level_filename);
10634 SaveLevel(level_nr);
10636 num_levels_converted++;
10638 Print("converted.\n");
10642 if (level_nr >= 0 && level_nr < MAX_NUM_CONVERT_LEVELS)
10643 levels_failed[level_nr] = TRUE;
10645 Print("NOT CONVERTED -- LEVEL ALREADY EXISTS.\n");
10648 num_levels_handled++;
10652 PrintLine("=", 79);
10653 Print("Number of levels handled: %d\n", num_levels_handled);
10654 Print("Number of levels converted: %d (%d%%)\n", num_levels_converted,
10655 (num_levels_handled ?
10656 num_levels_converted * 100 / num_levels_handled : 0));
10657 PrintLine("-", 79);
10658 Print("Summary (for automatic parsing by scripts):\n");
10659 Print("LEVELDIR '%s', CONVERTED %d/%d (%d%%)",
10660 convert_leveldir->identifier, num_levels_converted,
10661 num_levels_handled,
10662 (num_levels_handled ?
10663 num_levels_converted * 100 / num_levels_handled : 0));
10665 if (num_levels_handled != num_levels_converted)
10667 Print(", FAILED:");
10668 for (i = 0; i < MAX_NUM_CONVERT_LEVELS; i++)
10669 if (levels_failed[i])
10674 PrintLine("=", 79);
10676 CloseAllAndExit(0);
10680 /* ------------------------------------------------------------------------- */
10681 /* create and save images for use in level sketches (raw BMP format) */
10682 /* ------------------------------------------------------------------------- */
10684 void CreateLevelSketchImages()
10686 #if defined(TARGET_SDL)
10691 InitElementPropertiesGfxElement();
10693 bitmap1 = CreateBitmap(TILEX, TILEY, DEFAULT_DEPTH);
10694 bitmap2 = CreateBitmap(MINI_TILEX, MINI_TILEY, DEFAULT_DEPTH);
10696 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
10698 Bitmap *src_bitmap;
10700 int element = getMappedElement(i);
10701 int graphic = el2edimg(element);
10702 char basename1[16];
10703 char basename2[16];
10707 sprintf(basename1, "%03d.bmp", i);
10708 sprintf(basename2, "%03ds.bmp", i);
10710 filename1 = getPath2(global.create_images_dir, basename1);
10711 filename2 = getPath2(global.create_images_dir, basename2);
10713 getFixedGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
10714 BlitBitmap(src_bitmap, bitmap1, src_x, src_y, TILEX, TILEY,
10717 if (SDL_SaveBMP(bitmap1->surface, filename1) != 0)
10718 Error(ERR_EXIT, "cannot save level sketch image file '%s'", filename1);
10720 getMiniGraphicSource(graphic, &src_bitmap, &src_x, &src_y);
10721 BlitBitmap(src_bitmap, bitmap2, src_x, src_y, MINI_TILEX, MINI_TILEY, 0, 0);
10723 if (SDL_SaveBMP(bitmap2->surface, filename2) != 0)
10724 Error(ERR_EXIT, "cannot save level sketch image file '%s'", filename2);
10730 printf("%03d `%03d%c", i, i, (i % 10 < 9 ? ' ' : '\n'));
10733 FreeBitmap(bitmap1);
10734 FreeBitmap(bitmap2);
10739 Error(ERR_INFO, "%d normal and small images created", NUM_FILE_ELEMENTS);
10741 CloseAllAndExit(0);
10746 /* ------------------------------------------------------------------------- */
10747 /* create and save images for custom and group elements (raw BMP format) */
10748 /* ------------------------------------------------------------------------- */
10750 void CreateCustomElementImages(char *directory)
10752 #if defined(TARGET_SDL)
10753 char *src_basename = "RocksCE-template.ilbm";
10754 char *dst_basename = "RocksCE.bmp";
10755 char *src_filename = getPath2(directory, src_basename);
10756 char *dst_filename = getPath2(directory, dst_basename);
10757 Bitmap *src_bitmap;
10759 int yoffset_ce = 0;
10760 int yoffset_ge = (TILEY * NUM_CUSTOM_ELEMENTS / 16);
10763 SDLInitVideoDisplay();
10765 ReCreateBitmap(&backbuffer, video.width, video.height);
10767 src_bitmap = LoadImage(src_filename);
10769 bitmap = CreateBitmap(TILEX * 16 * 2,
10770 TILEY * (NUM_CUSTOM_ELEMENTS + NUM_GROUP_ELEMENTS) / 16,
10773 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
10780 BlitBitmap(src_bitmap, bitmap, 0, 0, TILEX, TILEY,
10781 TILEX * x, TILEY * y + yoffset_ce);
10783 BlitBitmap(src_bitmap, bitmap, 0, TILEY,
10785 TILEX * x + TILEX * 16,
10786 TILEY * y + yoffset_ce);
10788 for (j = 2; j >= 0; j--)
10792 BlitBitmap(src_bitmap, bitmap,
10793 TILEX + c * 7, 0, 6, 10,
10794 TILEX * x + 6 + j * 7,
10795 TILEY * y + 11 + yoffset_ce);
10797 BlitBitmap(src_bitmap, bitmap,
10798 TILEX + c * 8, TILEY, 6, 10,
10799 TILEX * 16 + TILEX * x + 6 + j * 8,
10800 TILEY * y + 10 + yoffset_ce);
10806 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
10813 BlitBitmap(src_bitmap, bitmap, 0, 0, TILEX, TILEY,
10814 TILEX * x, TILEY * y + yoffset_ge);
10816 BlitBitmap(src_bitmap, bitmap, 0, TILEY,
10818 TILEX * x + TILEX * 16,
10819 TILEY * y + yoffset_ge);
10821 for (j = 1; j >= 0; j--)
10825 BlitBitmap(src_bitmap, bitmap, TILEX + c * 10, 11, 10, 10,
10826 TILEX * x + 6 + j * 10,
10827 TILEY * y + 11 + yoffset_ge);
10829 BlitBitmap(src_bitmap, bitmap,
10830 TILEX + c * 8, TILEY + 12, 6, 10,
10831 TILEX * 16 + TILEX * x + 10 + j * 8,
10832 TILEY * y + 10 + yoffset_ge);
10838 if (SDL_SaveBMP(bitmap->surface, dst_filename) != 0)
10839 Error(ERR_EXIT, "cannot save CE graphics file '%s'", dst_filename);
10841 FreeBitmap(bitmap);
10843 CloseAllAndExit(0);