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"
26 #define ENABLE_UNUSED_CODE 0 // currently unused functions
27 #define ENABLE_HISTORIC_CHUNKS 0 // only for historic reference
28 #define ENABLE_RESERVED_CODE 0 // reserved for later use
30 #define CHUNK_ID_LEN 4 // IFF style chunk id length
31 #define CHUNK_SIZE_UNDEFINED 0 // undefined chunk size == 0
32 #define CHUNK_SIZE_NONE -1 // do not write chunk size
34 #define LEVEL_CHUNK_NAME_SIZE MAX_LEVEL_NAME_LEN
35 #define LEVEL_CHUNK_AUTH_SIZE MAX_LEVEL_AUTHOR_LEN
37 #define LEVEL_CHUNK_VERS_SIZE 8 // size of file version chunk
38 #define LEVEL_CHUNK_DATE_SIZE 4 // size of file date chunk
39 #define LEVEL_CHUNK_HEAD_SIZE 80 // size of level file header
40 #define LEVEL_CHUNK_HEAD_UNUSED 0 // unused level header bytes
41 #define LEVEL_CHUNK_CNT2_SIZE 160 // size of level CNT2 chunk
42 #define LEVEL_CHUNK_CNT2_UNUSED 11 // unused CNT2 chunk bytes
43 #define LEVEL_CHUNK_CNT3_HEADER 16 // size of level CNT3 header
44 #define LEVEL_CHUNK_CNT3_UNUSED 10 // unused CNT3 chunk bytes
45 #define LEVEL_CPART_CUS3_SIZE 134 // size of CUS3 chunk part
46 #define LEVEL_CPART_CUS3_UNUSED 15 // unused CUS3 bytes / part
47 #define LEVEL_CHUNK_GRP1_SIZE 74 // size of level GRP1 chunk
49 // (element number, number of change pages, change page number)
50 #define LEVEL_CHUNK_CUSX_UNCHANGED (2 + (1 + 1) + (1 + 1))
52 // (element number only)
53 #define LEVEL_CHUNK_GRPX_UNCHANGED 2
54 #define LEVEL_CHUNK_NOTE_UNCHANGED 2
56 // (nothing at all if unchanged)
57 #define LEVEL_CHUNK_ELEM_UNCHANGED 0
59 #define TAPE_CHUNK_VERS_SIZE 8 // size of file version chunk
60 #define TAPE_CHUNK_HEAD_SIZE 20 // size of tape file header
61 #define TAPE_CHUNK_HEAD_UNUSED 2 // unused tape header bytes
63 #define LEVEL_CHUNK_CNT3_SIZE(x) (LEVEL_CHUNK_CNT3_HEADER + (x))
64 #define LEVEL_CHUNK_CUS3_SIZE(x) (2 + (x) * LEVEL_CPART_CUS3_SIZE)
65 #define LEVEL_CHUNK_CUS4_SIZE(x) (96 + (x) * 48)
67 // file identifier strings
68 #define LEVEL_COOKIE_TMPL "ROCKSNDIAMONDS_LEVEL_FILE_VERSION_x.x"
69 #define TAPE_COOKIE_TMPL "ROCKSNDIAMONDS_TAPE_FILE_VERSION_x.x"
70 #define SCORE_COOKIE "ROCKSNDIAMONDS_SCORE_FILE_VERSION_1.2"
72 // values for deciding when (not) to save configuration data
73 #define SAVE_CONF_NEVER 0
74 #define SAVE_CONF_ALWAYS 1
75 #define SAVE_CONF_WHEN_CHANGED -1
77 // values for chunks using micro chunks
78 #define CONF_MASK_1_BYTE 0x00
79 #define CONF_MASK_2_BYTE 0x40
80 #define CONF_MASK_4_BYTE 0x80
81 #define CONF_MASK_MULTI_BYTES 0xc0
83 #define CONF_MASK_BYTES 0xc0
84 #define CONF_MASK_TOKEN 0x3f
86 #define CONF_VALUE_1_BYTE(x) (CONF_MASK_1_BYTE | (x))
87 #define CONF_VALUE_2_BYTE(x) (CONF_MASK_2_BYTE | (x))
88 #define CONF_VALUE_4_BYTE(x) (CONF_MASK_4_BYTE | (x))
89 #define CONF_VALUE_MULTI_BYTES(x) (CONF_MASK_MULTI_BYTES | (x))
91 // these definitions are just for convenience of use and readability
92 #define CONF_VALUE_8_BIT(x) CONF_VALUE_1_BYTE(x)
93 #define CONF_VALUE_16_BIT(x) CONF_VALUE_2_BYTE(x)
94 #define CONF_VALUE_32_BIT(x) CONF_VALUE_4_BYTE(x)
95 #define CONF_VALUE_BYTES(x) CONF_VALUE_MULTI_BYTES(x)
97 #define CONF_VALUE_NUM_BYTES(x) ((x) == CONF_MASK_1_BYTE ? 1 : \
98 (x) == CONF_MASK_2_BYTE ? 2 : \
99 (x) == CONF_MASK_4_BYTE ? 4 : 0)
101 #define CONF_CONTENT_NUM_ELEMENTS (3 * 3)
102 #define CONF_CONTENT_NUM_BYTES (CONF_CONTENT_NUM_ELEMENTS * 2)
103 #define CONF_ELEMENT_NUM_BYTES (2)
105 #define CONF_ENTITY_NUM_BYTES(t) ((t) == TYPE_ELEMENT || \
106 (t) == TYPE_ELEMENT_LIST ? \
107 CONF_ELEMENT_NUM_BYTES : \
108 (t) == TYPE_CONTENT || \
109 (t) == TYPE_CONTENT_LIST ? \
110 CONF_CONTENT_NUM_BYTES : 1)
112 #define CONF_ELEMENT_BYTE_POS(i) ((i) * CONF_ELEMENT_NUM_BYTES)
113 #define CONF_ELEMENTS_ELEMENT(b,i) ((b[CONF_ELEMENT_BYTE_POS(i)] << 8) | \
114 (b[CONF_ELEMENT_BYTE_POS(i) + 1]))
116 #define CONF_CONTENT_ELEMENT_POS(c,x,y) ((c) * CONF_CONTENT_NUM_ELEMENTS + \
118 #define CONF_CONTENT_BYTE_POS(c,x,y) (CONF_CONTENT_ELEMENT_POS(c,x,y) * \
119 CONF_ELEMENT_NUM_BYTES)
120 #define CONF_CONTENTS_ELEMENT(b,c,x,y) ((b[CONF_CONTENT_BYTE_POS(c,x,y)]<< 8)|\
121 (b[CONF_CONTENT_BYTE_POS(c,x,y) + 1]))
123 // temporary variables used to store pointers to structure members
124 static struct LevelInfo li;
125 static struct ElementInfo xx_ei, yy_ei;
126 static struct ElementChangeInfo xx_change;
127 static struct ElementGroupInfo xx_group;
128 static struct EnvelopeInfo xx_envelope;
129 static unsigned int xx_event_bits[NUM_CE_BITFIELDS];
130 static char xx_default_description[MAX_ELEMENT_NAME_LEN + 1];
131 static int xx_num_contents;
132 static int xx_current_change_page;
133 static char xx_default_string_empty[1] = "";
134 static int xx_string_length_unused;
136 struct LevelFileConfigInfo
138 int element; // element for which data is to be stored
139 int save_type; // save data always, never or when changed
140 int data_type; // data type (used internally, not stored)
141 int conf_type; // micro chunk identifier (stored in file)
144 void *value; // variable that holds the data to be stored
145 int default_value; // initial default value for this variable
148 void *value_copy; // variable that holds the data to be copied
149 void *num_entities; // number of entities for multi-byte data
150 int default_num_entities; // default number of entities for this data
151 int max_num_entities; // maximal number of entities for this data
152 char *default_string; // optional default string for string data
155 static struct LevelFileConfigInfo chunk_config_INFO[] =
157 // ---------- values not related to single elements -------------------------
160 -1, SAVE_CONF_ALWAYS,
161 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
162 &li.game_engine_type, GAME_ENGINE_TYPE_RND
166 -1, SAVE_CONF_ALWAYS,
167 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
168 &li.fieldx, STD_LEV_FIELDX
171 -1, SAVE_CONF_ALWAYS,
172 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
173 &li.fieldy, STD_LEV_FIELDY
177 -1, SAVE_CONF_ALWAYS,
178 TYPE_INTEGER, CONF_VALUE_16_BIT(3),
183 -1, SAVE_CONF_ALWAYS,
184 TYPE_INTEGER, CONF_VALUE_16_BIT(4),
190 TYPE_INTEGER, CONF_VALUE_32_BIT(2),
196 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
197 &li.use_step_counter, FALSE
202 TYPE_BITFIELD, CONF_VALUE_8_BIT(4),
203 &li.wind_direction_initial, MV_NONE
208 TYPE_BOOLEAN, CONF_VALUE_8_BIT(5),
209 &li.em_slippery_gems, FALSE
214 TYPE_BOOLEAN, CONF_VALUE_8_BIT(6),
215 &li.use_custom_template, FALSE
220 TYPE_BITFIELD, CONF_VALUE_32_BIT(1),
221 &li.can_move_into_acid_bits, ~0 // default: everything can
226 TYPE_BITFIELD, CONF_VALUE_8_BIT(7),
227 &li.dont_collide_with_bits, ~0 // default: always deadly
232 TYPE_BOOLEAN, CONF_VALUE_8_BIT(8),
233 &li.em_explodes_by_fire, FALSE
238 TYPE_INTEGER, CONF_VALUE_16_BIT(5),
239 &li.score[SC_TIME_BONUS], 1
244 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
245 &li.auto_exit_sokoban, FALSE
250 TYPE_BOOLEAN, CONF_VALUE_8_BIT(10),
251 &li.auto_count_gems, FALSE
256 TYPE_BOOLEAN, CONF_VALUE_8_BIT(11),
257 &li.solved_by_one_player, FALSE
267 static struct LevelFileConfigInfo chunk_config_ELEM[] =
269 // (these values are the same for each player)
272 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
273 &li.block_last_field, FALSE // default case for EM levels
277 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
278 &li.sp_block_last_field, TRUE // default case for SP levels
282 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
283 &li.instant_relocation, FALSE
287 TYPE_BOOLEAN, CONF_VALUE_8_BIT(4),
288 &li.can_pass_to_walkable, FALSE
292 TYPE_BOOLEAN, CONF_VALUE_8_BIT(5),
293 &li.block_snap_field, TRUE
297 TYPE_BOOLEAN, CONF_VALUE_8_BIT(6),
298 &li.continuous_snapping, TRUE
302 TYPE_BOOLEAN, CONF_VALUE_8_BIT(12),
303 &li.shifted_relocation, FALSE
307 TYPE_BOOLEAN, CONF_VALUE_8_BIT(15),
308 &li.lazy_relocation, FALSE
311 // (these values are different for each player)
314 TYPE_INTEGER, CONF_VALUE_8_BIT(7),
315 &li.initial_player_stepsize[0], STEPSIZE_NORMAL
319 TYPE_BOOLEAN, CONF_VALUE_8_BIT(8),
320 &li.initial_player_gravity[0], FALSE
324 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
325 &li.use_start_element[0], FALSE
329 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
330 &li.start_element[0], EL_PLAYER_1
334 TYPE_BOOLEAN, CONF_VALUE_8_BIT(10),
335 &li.use_artwork_element[0], FALSE
339 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
340 &li.artwork_element[0], EL_PLAYER_1
344 TYPE_BOOLEAN, CONF_VALUE_8_BIT(11),
345 &li.use_explosion_element[0], FALSE
349 TYPE_ELEMENT, CONF_VALUE_16_BIT(3),
350 &li.explosion_element[0], EL_PLAYER_1
354 TYPE_BOOLEAN, CONF_VALUE_8_BIT(13),
355 &li.use_initial_inventory[0], FALSE
359 TYPE_BOOLEAN, CONF_VALUE_8_BIT(14),
360 &li.initial_inventory_size[0], 1
364 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(1),
365 &li.initial_inventory_content[0][0],EL_EMPTY, NULL,
366 &li.initial_inventory_size[0], 1, MAX_INITIAL_INVENTORY_SIZE
371 TYPE_INTEGER, CONF_VALUE_8_BIT(7),
372 &li.initial_player_stepsize[1], STEPSIZE_NORMAL
376 TYPE_BOOLEAN, CONF_VALUE_8_BIT(8),
377 &li.initial_player_gravity[1], FALSE
381 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
382 &li.use_start_element[1], FALSE
386 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
387 &li.start_element[1], EL_PLAYER_2
391 TYPE_BOOLEAN, CONF_VALUE_8_BIT(10),
392 &li.use_artwork_element[1], FALSE
396 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
397 &li.artwork_element[1], EL_PLAYER_2
401 TYPE_BOOLEAN, CONF_VALUE_8_BIT(11),
402 &li.use_explosion_element[1], FALSE
406 TYPE_ELEMENT, CONF_VALUE_16_BIT(3),
407 &li.explosion_element[1], EL_PLAYER_2
411 TYPE_BOOLEAN, CONF_VALUE_8_BIT(13),
412 &li.use_initial_inventory[1], FALSE
416 TYPE_BOOLEAN, CONF_VALUE_8_BIT(14),
417 &li.initial_inventory_size[1], 1
421 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(1),
422 &li.initial_inventory_content[1][0],EL_EMPTY, NULL,
423 &li.initial_inventory_size[1], 1, MAX_INITIAL_INVENTORY_SIZE
428 TYPE_INTEGER, CONF_VALUE_8_BIT(7),
429 &li.initial_player_stepsize[2], STEPSIZE_NORMAL
433 TYPE_BOOLEAN, CONF_VALUE_8_BIT(8),
434 &li.initial_player_gravity[2], FALSE
438 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
439 &li.use_start_element[2], FALSE
443 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
444 &li.start_element[2], EL_PLAYER_3
448 TYPE_BOOLEAN, CONF_VALUE_8_BIT(10),
449 &li.use_artwork_element[2], FALSE
453 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
454 &li.artwork_element[2], EL_PLAYER_3
458 TYPE_BOOLEAN, CONF_VALUE_8_BIT(11),
459 &li.use_explosion_element[2], FALSE
463 TYPE_ELEMENT, CONF_VALUE_16_BIT(3),
464 &li.explosion_element[2], EL_PLAYER_3
468 TYPE_BOOLEAN, CONF_VALUE_8_BIT(13),
469 &li.use_initial_inventory[2], FALSE
473 TYPE_BOOLEAN, CONF_VALUE_8_BIT(14),
474 &li.initial_inventory_size[2], 1
478 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(1),
479 &li.initial_inventory_content[2][0],EL_EMPTY, NULL,
480 &li.initial_inventory_size[2], 1, MAX_INITIAL_INVENTORY_SIZE
485 TYPE_INTEGER, CONF_VALUE_8_BIT(7),
486 &li.initial_player_stepsize[3], STEPSIZE_NORMAL
490 TYPE_BOOLEAN, CONF_VALUE_8_BIT(8),
491 &li.initial_player_gravity[3], FALSE
495 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
496 &li.use_start_element[3], FALSE
500 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
501 &li.start_element[3], EL_PLAYER_4
505 TYPE_BOOLEAN, CONF_VALUE_8_BIT(10),
506 &li.use_artwork_element[3], FALSE
510 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
511 &li.artwork_element[3], EL_PLAYER_4
515 TYPE_BOOLEAN, CONF_VALUE_8_BIT(11),
516 &li.use_explosion_element[3], FALSE
520 TYPE_ELEMENT, CONF_VALUE_16_BIT(3),
521 &li.explosion_element[3], EL_PLAYER_4
525 TYPE_BOOLEAN, CONF_VALUE_8_BIT(13),
526 &li.use_initial_inventory[3], FALSE
530 TYPE_BOOLEAN, CONF_VALUE_8_BIT(14),
531 &li.initial_inventory_size[3], 1
535 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(1),
536 &li.initial_inventory_content[3][0],EL_EMPTY, NULL,
537 &li.initial_inventory_size[3], 1, MAX_INITIAL_INVENTORY_SIZE
542 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
543 &li.score[SC_EMERALD], 10
548 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
549 &li.score[SC_DIAMOND], 10
554 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
555 &li.score[SC_BUG], 10
560 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
561 &li.score[SC_SPACESHIP], 10
566 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
567 &li.score[SC_PACMAN], 10
572 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
573 &li.score[SC_NUT], 10
578 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
579 &li.score[SC_DYNAMITE], 10
584 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
585 &li.score[SC_KEY], 10
590 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
591 &li.score[SC_PEARL], 10
596 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
597 &li.score[SC_CRYSTAL], 10
602 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
603 &li.amoeba_content, EL_DIAMOND
607 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
612 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
613 &li.grow_into_diggable, TRUE
618 TYPE_CONTENT_LIST, CONF_VALUE_BYTES(1),
619 &li.yamyam_content, EL_ROCK, NULL,
620 &li.num_yamyam_contents, 4, MAX_ELEMENT_CONTENTS
624 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
625 &li.score[SC_YAMYAM], 10
630 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
631 &li.score[SC_ROBOT], 10
635 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
641 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
647 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
648 &li.time_magic_wall, 10
653 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
654 &li.game_of_life[0], 2
658 TYPE_INTEGER, CONF_VALUE_8_BIT(2),
659 &li.game_of_life[1], 3
663 TYPE_INTEGER, CONF_VALUE_8_BIT(3),
664 &li.game_of_life[2], 3
668 TYPE_INTEGER, CONF_VALUE_8_BIT(4),
669 &li.game_of_life[3], 3
673 TYPE_BOOLEAN, CONF_VALUE_8_BIT(5),
674 &li.use_life_bugs, FALSE
679 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
684 TYPE_INTEGER, CONF_VALUE_8_BIT(2),
689 TYPE_INTEGER, CONF_VALUE_8_BIT(3),
694 TYPE_INTEGER, CONF_VALUE_8_BIT(4),
699 EL_TIMEGATE_SWITCH, -1,
700 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
701 &li.time_timegate, 10
705 EL_LIGHT_SWITCH_ACTIVE, -1,
706 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
711 EL_SHIELD_NORMAL, -1,
712 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
713 &li.shield_normal_time, 10
716 EL_SHIELD_NORMAL, -1,
717 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
718 &li.score[SC_SHIELD], 10
722 EL_SHIELD_DEADLY, -1,
723 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
724 &li.shield_deadly_time, 10
727 EL_SHIELD_DEADLY, -1,
728 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
729 &li.score[SC_SHIELD], 10
734 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
739 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
740 &li.extra_time_score, 10
744 EL_TIME_ORB_FULL, -1,
745 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
746 &li.time_orb_time, 10
749 EL_TIME_ORB_FULL, -1,
750 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
751 &li.use_time_orb_bug, FALSE
756 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
757 &li.use_spring_bug, FALSE
762 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
763 &li.android_move_time, 10
767 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
768 &li.android_clone_time, 10
772 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(1),
773 &li.android_clone_element[0], EL_EMPTY, NULL,
774 &li.num_android_clone_elements, 1, MAX_ANDROID_ELEMENTS
779 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
784 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
789 EL_EMC_MAGNIFIER, -1,
790 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
791 &li.magnify_score, 10
794 EL_EMC_MAGNIFIER, -1,
795 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
800 EL_EMC_MAGIC_BALL, -1,
801 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
805 EL_EMC_MAGIC_BALL, -1,
806 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
807 &li.ball_random, FALSE
810 EL_EMC_MAGIC_BALL, -1,
811 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
812 &li.ball_state_initial, FALSE
815 EL_EMC_MAGIC_BALL, -1,
816 TYPE_CONTENT_LIST, CONF_VALUE_BYTES(1),
817 &li.ball_content, EL_EMPTY, NULL,
818 &li.num_ball_contents, 4, MAX_ELEMENT_CONTENTS
822 EL_SOKOBAN_FIELD_EMPTY, -1,
823 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
824 &li.sb_fields_needed, TRUE
828 EL_SOKOBAN_OBJECT, -1,
829 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
830 &li.sb_objects_needed, TRUE
835 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
836 &li.mm_laser_red, FALSE
840 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
841 &li.mm_laser_green, FALSE
845 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
846 &li.mm_laser_blue, TRUE
851 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
852 &li.df_laser_red, TRUE
856 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
857 &li.df_laser_green, TRUE
861 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
862 &li.df_laser_blue, FALSE
866 EL_MM_FUSE_ACTIVE, -1,
867 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
872 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
877 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
881 EL_MM_STEEL_BLOCK, -1,
882 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
883 &li.mm_time_block, 75
887 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
888 &li.score[SC_ELEM_BONUS], 10
891 // ---------- unused values -------------------------------------------------
894 EL_UNKNOWN, SAVE_CONF_NEVER,
895 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
896 &li.score[SC_UNKNOWN_15], 10
906 static struct LevelFileConfigInfo chunk_config_NOTE[] =
910 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
911 &xx_envelope.xsize, MAX_ENVELOPE_XSIZE,
915 TYPE_INTEGER, CONF_VALUE_8_BIT(2),
916 &xx_envelope.ysize, MAX_ENVELOPE_YSIZE,
921 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
922 &xx_envelope.autowrap, FALSE
926 TYPE_BOOLEAN, CONF_VALUE_8_BIT(4),
927 &xx_envelope.centered, FALSE
932 TYPE_STRING, CONF_VALUE_BYTES(1),
933 &xx_envelope.text, -1, NULL,
934 &xx_string_length_unused, -1, MAX_ENVELOPE_TEXT_LEN,
935 &xx_default_string_empty[0]
945 static struct LevelFileConfigInfo chunk_config_CUSX_base[] =
949 TYPE_STRING, CONF_VALUE_BYTES(1),
950 &xx_ei.description[0], -1,
951 &yy_ei.description[0],
952 &xx_string_length_unused, -1, MAX_ELEMENT_NAME_LEN,
953 &xx_default_description[0]
958 TYPE_BITFIELD, CONF_VALUE_32_BIT(1),
959 &xx_ei.properties[EP_BITFIELD_BASE_NR], EP_BITMASK_BASE_DEFAULT,
960 &yy_ei.properties[EP_BITFIELD_BASE_NR]
962 #if ENABLE_RESERVED_CODE
963 // (reserved for later use)
966 TYPE_BITFIELD, CONF_VALUE_32_BIT(2),
967 &xx_ei.properties[EP_BITFIELD_BASE_NR + 1], EP_BITMASK_DEFAULT,
968 &yy_ei.properties[EP_BITFIELD_BASE_NR + 1]
974 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
975 &xx_ei.use_gfx_element, FALSE,
976 &yy_ei.use_gfx_element
980 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
981 &xx_ei.gfx_element_initial, EL_EMPTY_SPACE,
982 &yy_ei.gfx_element_initial
987 TYPE_BITFIELD, CONF_VALUE_8_BIT(2),
988 &xx_ei.access_direction, MV_ALL_DIRECTIONS,
989 &yy_ei.access_direction
994 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
995 &xx_ei.collect_score_initial, 10,
996 &yy_ei.collect_score_initial
1000 TYPE_INTEGER, CONF_VALUE_16_BIT(3),
1001 &xx_ei.collect_count_initial, 1,
1002 &yy_ei.collect_count_initial
1007 TYPE_INTEGER, CONF_VALUE_16_BIT(4),
1008 &xx_ei.ce_value_fixed_initial, 0,
1009 &yy_ei.ce_value_fixed_initial
1013 TYPE_INTEGER, CONF_VALUE_16_BIT(5),
1014 &xx_ei.ce_value_random_initial, 0,
1015 &yy_ei.ce_value_random_initial
1019 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
1020 &xx_ei.use_last_ce_value, FALSE,
1021 &yy_ei.use_last_ce_value
1026 TYPE_INTEGER, CONF_VALUE_16_BIT(6),
1027 &xx_ei.push_delay_fixed, 8,
1028 &yy_ei.push_delay_fixed
1032 TYPE_INTEGER, CONF_VALUE_16_BIT(7),
1033 &xx_ei.push_delay_random, 8,
1034 &yy_ei.push_delay_random
1038 TYPE_INTEGER, CONF_VALUE_16_BIT(8),
1039 &xx_ei.drop_delay_fixed, 0,
1040 &yy_ei.drop_delay_fixed
1044 TYPE_INTEGER, CONF_VALUE_16_BIT(9),
1045 &xx_ei.drop_delay_random, 0,
1046 &yy_ei.drop_delay_random
1050 TYPE_INTEGER, CONF_VALUE_16_BIT(10),
1051 &xx_ei.move_delay_fixed, 0,
1052 &yy_ei.move_delay_fixed
1056 TYPE_INTEGER, CONF_VALUE_16_BIT(11),
1057 &xx_ei.move_delay_random, 0,
1058 &yy_ei.move_delay_random
1063 TYPE_BITFIELD, CONF_VALUE_32_BIT(3),
1064 &xx_ei.move_pattern, MV_ALL_DIRECTIONS,
1069 TYPE_BITFIELD, CONF_VALUE_8_BIT(4),
1070 &xx_ei.move_direction_initial, MV_START_AUTOMATIC,
1071 &yy_ei.move_direction_initial
1075 TYPE_INTEGER, CONF_VALUE_8_BIT(5),
1076 &xx_ei.move_stepsize, TILEX / 8,
1077 &yy_ei.move_stepsize
1082 TYPE_ELEMENT, CONF_VALUE_16_BIT(12),
1083 &xx_ei.move_enter_element, EL_EMPTY_SPACE,
1084 &yy_ei.move_enter_element
1088 TYPE_ELEMENT, CONF_VALUE_16_BIT(13),
1089 &xx_ei.move_leave_element, EL_EMPTY_SPACE,
1090 &yy_ei.move_leave_element
1094 TYPE_INTEGER, CONF_VALUE_8_BIT(6),
1095 &xx_ei.move_leave_type, LEAVE_TYPE_UNLIMITED,
1096 &yy_ei.move_leave_type
1101 TYPE_INTEGER, CONF_VALUE_8_BIT(7),
1102 &xx_ei.slippery_type, SLIPPERY_ANY_RANDOM,
1103 &yy_ei.slippery_type
1108 TYPE_INTEGER, CONF_VALUE_8_BIT(8),
1109 &xx_ei.explosion_type, EXPLODES_3X3,
1110 &yy_ei.explosion_type
1114 TYPE_INTEGER, CONF_VALUE_16_BIT(14),
1115 &xx_ei.explosion_delay, 16,
1116 &yy_ei.explosion_delay
1120 TYPE_INTEGER, CONF_VALUE_16_BIT(15),
1121 &xx_ei.ignition_delay, 8,
1122 &yy_ei.ignition_delay
1127 TYPE_CONTENT_LIST, CONF_VALUE_BYTES(2),
1128 &xx_ei.content, EL_EMPTY_SPACE,
1130 &xx_num_contents, 1, 1
1133 // ---------- "num_change_pages" must be the last entry ---------------------
1136 -1, SAVE_CONF_ALWAYS,
1137 TYPE_INTEGER, CONF_VALUE_8_BIT(9),
1138 &xx_ei.num_change_pages, 1,
1139 &yy_ei.num_change_pages
1150 static struct LevelFileConfigInfo chunk_config_CUSX_change[] =
1152 // ---------- "current_change_page" must be the first entry -----------------
1155 -1, SAVE_CONF_ALWAYS,
1156 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
1157 &xx_current_change_page, -1
1160 // ---------- (the remaining entries can be in any order) -------------------
1164 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
1165 &xx_change.can_change, FALSE
1170 TYPE_BITFIELD, CONF_VALUE_32_BIT(1),
1171 &xx_event_bits[0], 0
1175 TYPE_BITFIELD, CONF_VALUE_32_BIT(2),
1176 &xx_event_bits[1], 0
1181 TYPE_BITFIELD, CONF_VALUE_8_BIT(3),
1182 &xx_change.trigger_player, CH_PLAYER_ANY
1186 TYPE_BITFIELD, CONF_VALUE_8_BIT(4),
1187 &xx_change.trigger_side, CH_SIDE_ANY
1191 TYPE_BITFIELD, CONF_VALUE_32_BIT(3),
1192 &xx_change.trigger_page, CH_PAGE_ANY
1197 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
1198 &xx_change.target_element, EL_EMPTY_SPACE
1203 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
1204 &xx_change.delay_fixed, 0
1208 TYPE_INTEGER, CONF_VALUE_16_BIT(3),
1209 &xx_change.delay_random, 0
1213 TYPE_INTEGER, CONF_VALUE_16_BIT(4),
1214 &xx_change.delay_frames, FRAMES_PER_SECOND
1219 TYPE_ELEMENT, CONF_VALUE_16_BIT(5),
1220 &xx_change.initial_trigger_element, EL_EMPTY_SPACE
1225 TYPE_BOOLEAN, CONF_VALUE_8_BIT(6),
1226 &xx_change.explode, FALSE
1230 TYPE_BOOLEAN, CONF_VALUE_8_BIT(7),
1231 &xx_change.use_target_content, FALSE
1235 TYPE_BOOLEAN, CONF_VALUE_8_BIT(8),
1236 &xx_change.only_if_complete, FALSE
1240 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
1241 &xx_change.use_random_replace, FALSE
1245 TYPE_INTEGER, CONF_VALUE_8_BIT(10),
1246 &xx_change.random_percentage, 100
1250 TYPE_INTEGER, CONF_VALUE_8_BIT(11),
1251 &xx_change.replace_when, CP_WHEN_EMPTY
1256 TYPE_BOOLEAN, CONF_VALUE_8_BIT(12),
1257 &xx_change.has_action, FALSE
1261 TYPE_INTEGER, CONF_VALUE_8_BIT(13),
1262 &xx_change.action_type, CA_NO_ACTION
1266 TYPE_INTEGER, CONF_VALUE_8_BIT(14),
1267 &xx_change.action_mode, CA_MODE_UNDEFINED
1271 TYPE_INTEGER, CONF_VALUE_16_BIT(6),
1272 &xx_change.action_arg, CA_ARG_UNDEFINED
1277 TYPE_ELEMENT, CONF_VALUE_16_BIT(7),
1278 &xx_change.action_element, EL_EMPTY_SPACE
1283 TYPE_CONTENT_LIST, CONF_VALUE_BYTES(1),
1284 &xx_change.target_content, EL_EMPTY_SPACE, NULL,
1285 &xx_num_contents, 1, 1
1295 static struct LevelFileConfigInfo chunk_config_GRPX[] =
1299 TYPE_STRING, CONF_VALUE_BYTES(1),
1300 &xx_ei.description[0], -1, NULL,
1301 &xx_string_length_unused, -1, MAX_ELEMENT_NAME_LEN,
1302 &xx_default_description[0]
1307 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1308 &xx_ei.use_gfx_element, FALSE
1312 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
1313 &xx_ei.gfx_element_initial, EL_EMPTY_SPACE
1318 TYPE_INTEGER, CONF_VALUE_8_BIT(2),
1319 &xx_group.choice_mode, ANIM_RANDOM
1324 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(2),
1325 &xx_group.element[0], EL_EMPTY_SPACE, NULL,
1326 &xx_group.num_elements, 1, MAX_ELEMENTS_IN_GROUP
1336 static struct LevelFileConfigInfo chunk_config_CONF[] = // (OBSOLETE)
1340 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
1341 &li.block_snap_field, TRUE
1345 TYPE_BOOLEAN, CONF_VALUE_8_BIT(13),
1346 &li.continuous_snapping, TRUE
1350 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
1351 &li.initial_player_stepsize[0], STEPSIZE_NORMAL
1355 TYPE_BOOLEAN, CONF_VALUE_8_BIT(10),
1356 &li.use_start_element[0], FALSE
1360 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
1361 &li.start_element[0], EL_PLAYER_1
1365 TYPE_BOOLEAN, CONF_VALUE_8_BIT(11),
1366 &li.use_artwork_element[0], FALSE
1370 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
1371 &li.artwork_element[0], EL_PLAYER_1
1375 TYPE_BOOLEAN, CONF_VALUE_8_BIT(12),
1376 &li.use_explosion_element[0], FALSE
1380 TYPE_ELEMENT, CONF_VALUE_16_BIT(3),
1381 &li.explosion_element[0], EL_PLAYER_1
1396 filetype_id_list[] =
1398 { LEVEL_FILE_TYPE_RND, "RND" },
1399 { LEVEL_FILE_TYPE_BD, "BD" },
1400 { LEVEL_FILE_TYPE_EM, "EM" },
1401 { LEVEL_FILE_TYPE_SP, "SP" },
1402 { LEVEL_FILE_TYPE_DX, "DX" },
1403 { LEVEL_FILE_TYPE_SB, "SB" },
1404 { LEVEL_FILE_TYPE_DC, "DC" },
1405 { LEVEL_FILE_TYPE_MM, "MM" },
1406 { LEVEL_FILE_TYPE_MM, "DF" },
1411 // ============================================================================
1412 // level file functions
1413 // ============================================================================
1415 static boolean check_special_flags(char *flag)
1417 if (strEqual(options.special_flags, flag) ||
1418 strEqual(leveldir_current->special_flags, flag))
1424 static struct DateInfo getCurrentDate(void)
1426 time_t epoch_seconds = time(NULL);
1427 struct tm *now = localtime(&epoch_seconds);
1428 struct DateInfo date;
1430 date.year = now->tm_year + 1900;
1431 date.month = now->tm_mon + 1;
1432 date.day = now->tm_mday;
1434 date.src = DATE_SRC_CLOCK;
1439 static void resetEventFlags(struct ElementChangeInfo *change)
1443 for (i = 0; i < NUM_CHANGE_EVENTS; i++)
1444 change->has_event[i] = FALSE;
1447 static void resetEventBits(void)
1451 for (i = 0; i < NUM_CE_BITFIELDS; i++)
1452 xx_event_bits[i] = 0;
1455 static void setEventFlagsFromEventBits(struct ElementChangeInfo *change)
1459 /* important: only change event flag if corresponding event bit is set
1460 (this is because all xx_event_bits[] values are loaded separately,
1461 and all xx_event_bits[] values are set back to zero before loading
1462 another value xx_event_bits[x] (each value representing 32 flags)) */
1464 for (i = 0; i < NUM_CHANGE_EVENTS; i++)
1465 if (xx_event_bits[CH_EVENT_BITFIELD_NR(i)] & CH_EVENT_BIT(i))
1466 change->has_event[i] = TRUE;
1469 static void setEventBitsFromEventFlags(struct ElementChangeInfo *change)
1473 /* in contrast to the above function setEventFlagsFromEventBits(), it
1474 would also be possible to set all bits in xx_event_bits[] to 0 or 1
1475 depending on the corresponding change->has_event[i] values here, as
1476 all xx_event_bits[] values are reset in resetEventBits() before */
1478 for (i = 0; i < NUM_CHANGE_EVENTS; i++)
1479 if (change->has_event[i])
1480 xx_event_bits[CH_EVENT_BITFIELD_NR(i)] |= CH_EVENT_BIT(i);
1483 static char *getDefaultElementDescription(struct ElementInfo *ei)
1485 static char description[MAX_ELEMENT_NAME_LEN + 1];
1486 char *default_description = (ei->custom_description != NULL ?
1487 ei->custom_description :
1488 ei->editor_description);
1491 // always start with reliable default values
1492 for (i = 0; i < MAX_ELEMENT_NAME_LEN + 1; i++)
1493 description[i] = '\0';
1495 // truncate element description to MAX_ELEMENT_NAME_LEN bytes
1496 strncpy(description, default_description, MAX_ELEMENT_NAME_LEN);
1498 return &description[0];
1501 static void setElementDescriptionToDefault(struct ElementInfo *ei)
1503 char *default_description = getDefaultElementDescription(ei);
1506 for (i = 0; i < MAX_ELEMENT_NAME_LEN + 1; i++)
1507 ei->description[i] = default_description[i];
1510 static void setConfigToDefaultsFromConfigList(struct LevelFileConfigInfo *conf)
1514 for (i = 0; conf[i].data_type != -1; i++)
1516 int default_value = conf[i].default_value;
1517 int data_type = conf[i].data_type;
1518 int conf_type = conf[i].conf_type;
1519 int byte_mask = conf_type & CONF_MASK_BYTES;
1521 if (byte_mask == CONF_MASK_MULTI_BYTES)
1523 int default_num_entities = conf[i].default_num_entities;
1524 int max_num_entities = conf[i].max_num_entities;
1526 *(int *)(conf[i].num_entities) = default_num_entities;
1528 if (data_type == TYPE_STRING)
1530 char *default_string = conf[i].default_string;
1531 char *string = (char *)(conf[i].value);
1533 strncpy(string, default_string, max_num_entities);
1535 else if (data_type == TYPE_ELEMENT_LIST)
1537 int *element_array = (int *)(conf[i].value);
1540 for (j = 0; j < max_num_entities; j++)
1541 element_array[j] = default_value;
1543 else if (data_type == TYPE_CONTENT_LIST)
1545 struct Content *content = (struct Content *)(conf[i].value);
1548 for (c = 0; c < max_num_entities; c++)
1549 for (y = 0; y < 3; y++)
1550 for (x = 0; x < 3; x++)
1551 content[c].e[x][y] = default_value;
1554 else // constant size configuration data (1, 2 or 4 bytes)
1556 if (data_type == TYPE_BOOLEAN)
1557 *(boolean *)(conf[i].value) = default_value;
1559 *(int *) (conf[i].value) = default_value;
1564 static void copyConfigFromConfigList(struct LevelFileConfigInfo *conf)
1568 for (i = 0; conf[i].data_type != -1; i++)
1570 int data_type = conf[i].data_type;
1571 int conf_type = conf[i].conf_type;
1572 int byte_mask = conf_type & CONF_MASK_BYTES;
1574 if (byte_mask == CONF_MASK_MULTI_BYTES)
1576 int max_num_entities = conf[i].max_num_entities;
1578 if (data_type == TYPE_STRING)
1580 char *string = (char *)(conf[i].value);
1581 char *string_copy = (char *)(conf[i].value_copy);
1583 strncpy(string_copy, string, max_num_entities);
1585 else if (data_type == TYPE_ELEMENT_LIST)
1587 int *element_array = (int *)(conf[i].value);
1588 int *element_array_copy = (int *)(conf[i].value_copy);
1591 for (j = 0; j < max_num_entities; j++)
1592 element_array_copy[j] = element_array[j];
1594 else if (data_type == TYPE_CONTENT_LIST)
1596 struct Content *content = (struct Content *)(conf[i].value);
1597 struct Content *content_copy = (struct Content *)(conf[i].value_copy);
1600 for (c = 0; c < max_num_entities; c++)
1601 for (y = 0; y < 3; y++)
1602 for (x = 0; x < 3; x++)
1603 content_copy[c].e[x][y] = content[c].e[x][y];
1606 else // constant size configuration data (1, 2 or 4 bytes)
1608 if (data_type == TYPE_BOOLEAN)
1609 *(boolean *)(conf[i].value_copy) = *(boolean *)(conf[i].value);
1611 *(int *) (conf[i].value_copy) = *(int *) (conf[i].value);
1616 void copyElementInfo(struct ElementInfo *ei_from, struct ElementInfo *ei_to)
1620 xx_ei = *ei_from; // copy element data into temporary buffer
1621 yy_ei = *ei_to; // copy element data into temporary buffer
1623 copyConfigFromConfigList(chunk_config_CUSX_base);
1628 // ---------- reinitialize and copy change pages ----------
1630 ei_to->num_change_pages = ei_from->num_change_pages;
1631 ei_to->current_change_page = ei_from->current_change_page;
1633 setElementChangePages(ei_to, ei_to->num_change_pages);
1635 for (i = 0; i < ei_to->num_change_pages; i++)
1636 ei_to->change_page[i] = ei_from->change_page[i];
1638 // ---------- copy group element info ----------
1639 if (ei_from->group != NULL && ei_to->group != NULL) // group or internal
1640 *ei_to->group = *ei_from->group;
1642 // mark this custom element as modified
1643 ei_to->modified_settings = TRUE;
1646 void setElementChangePages(struct ElementInfo *ei, int change_pages)
1648 int change_page_size = sizeof(struct ElementChangeInfo);
1650 ei->num_change_pages = MAX(1, change_pages);
1653 checked_realloc(ei->change_page, ei->num_change_pages * change_page_size);
1655 if (ei->current_change_page >= ei->num_change_pages)
1656 ei->current_change_page = ei->num_change_pages - 1;
1658 ei->change = &ei->change_page[ei->current_change_page];
1661 void setElementChangeInfoToDefaults(struct ElementChangeInfo *change)
1663 xx_change = *change; // copy change data into temporary buffer
1665 setConfigToDefaultsFromConfigList(chunk_config_CUSX_change);
1667 *change = xx_change;
1669 resetEventFlags(change);
1671 change->direct_action = 0;
1672 change->other_action = 0;
1674 change->pre_change_function = NULL;
1675 change->change_function = NULL;
1676 change->post_change_function = NULL;
1679 static void setLevelInfoToDefaults_Level(struct LevelInfo *level)
1683 li = *level; // copy level data into temporary buffer
1684 setConfigToDefaultsFromConfigList(chunk_config_INFO);
1685 *level = li; // copy temporary buffer back to level data
1687 setLevelInfoToDefaults_EM();
1688 setLevelInfoToDefaults_SP();
1689 setLevelInfoToDefaults_MM();
1691 level->native_em_level = &native_em_level;
1692 level->native_sp_level = &native_sp_level;
1693 level->native_mm_level = &native_mm_level;
1695 level->file_version = FILE_VERSION_ACTUAL;
1696 level->game_version = GAME_VERSION_ACTUAL;
1698 level->creation_date = getCurrentDate();
1700 level->encoding_16bit_field = TRUE;
1701 level->encoding_16bit_yamyam = TRUE;
1702 level->encoding_16bit_amoeba = TRUE;
1704 // clear level name and level author string buffers
1705 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
1706 level->name[i] = '\0';
1707 for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
1708 level->author[i] = '\0';
1710 // set level name and level author to default values
1711 strcpy(level->name, NAMELESS_LEVEL_NAME);
1712 strcpy(level->author, ANONYMOUS_NAME);
1714 // set level playfield to playable default level with player and exit
1715 for (x = 0; x < MAX_LEV_FIELDX; x++)
1716 for (y = 0; y < MAX_LEV_FIELDY; y++)
1717 level->field[x][y] = EL_SAND;
1719 level->field[0][0] = EL_PLAYER_1;
1720 level->field[STD_LEV_FIELDX - 1][STD_LEV_FIELDY - 1] = EL_EXIT_CLOSED;
1722 BorderElement = EL_STEELWALL;
1724 // detect custom elements when loading them
1725 level->file_has_custom_elements = FALSE;
1727 // set all bug compatibility flags to "false" => do not emulate this bug
1728 level->use_action_after_change_bug = FALSE;
1730 if (leveldir_current)
1732 // try to determine better author name than 'anonymous'
1733 if (!strEqual(leveldir_current->author, ANONYMOUS_NAME))
1735 strncpy(level->author, leveldir_current->author, MAX_LEVEL_AUTHOR_LEN);
1736 level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
1740 switch (LEVELCLASS(leveldir_current))
1742 case LEVELCLASS_TUTORIAL:
1743 strcpy(level->author, PROGRAM_AUTHOR_STRING);
1746 case LEVELCLASS_CONTRIB:
1747 strncpy(level->author, leveldir_current->name, MAX_LEVEL_AUTHOR_LEN);
1748 level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
1751 case LEVELCLASS_PRIVATE:
1752 strncpy(level->author, getRealName(), MAX_LEVEL_AUTHOR_LEN);
1753 level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
1757 // keep default value
1764 static void setLevelInfoToDefaults_Elements(struct LevelInfo *level)
1766 static boolean clipboard_elements_initialized = FALSE;
1769 InitElementPropertiesStatic();
1771 li = *level; // copy level data into temporary buffer
1772 setConfigToDefaultsFromConfigList(chunk_config_ELEM);
1773 *level = li; // copy temporary buffer back to level data
1775 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1778 struct ElementInfo *ei = &element_info[element];
1780 // never initialize clipboard elements after the very first time
1781 // (to be able to use clipboard elements between several levels)
1782 if (IS_CLIPBOARD_ELEMENT(element) && clipboard_elements_initialized)
1785 if (IS_ENVELOPE(element))
1787 int envelope_nr = element - EL_ENVELOPE_1;
1789 setConfigToDefaultsFromConfigList(chunk_config_NOTE);
1791 level->envelope[envelope_nr] = xx_envelope;
1794 if (IS_CUSTOM_ELEMENT(element) ||
1795 IS_GROUP_ELEMENT(element) ||
1796 IS_INTERNAL_ELEMENT(element))
1798 xx_ei = *ei; // copy element data into temporary buffer
1800 setConfigToDefaultsFromConfigList(chunk_config_CUSX_base);
1805 setElementChangePages(ei, 1);
1806 setElementChangeInfoToDefaults(ei->change);
1808 if (IS_CUSTOM_ELEMENT(element) ||
1809 IS_GROUP_ELEMENT(element) ||
1810 IS_INTERNAL_ELEMENT(element))
1812 setElementDescriptionToDefault(ei);
1814 ei->modified_settings = FALSE;
1817 if (IS_CUSTOM_ELEMENT(element) ||
1818 IS_INTERNAL_ELEMENT(element))
1820 // internal values used in level editor
1822 ei->access_type = 0;
1823 ei->access_layer = 0;
1824 ei->access_protected = 0;
1825 ei->walk_to_action = 0;
1826 ei->smash_targets = 0;
1829 ei->can_explode_by_fire = FALSE;
1830 ei->can_explode_smashed = FALSE;
1831 ei->can_explode_impact = FALSE;
1833 ei->current_change_page = 0;
1836 if (IS_GROUP_ELEMENT(element) ||
1837 IS_INTERNAL_ELEMENT(element))
1839 struct ElementGroupInfo *group;
1841 // initialize memory for list of elements in group
1842 if (ei->group == NULL)
1843 ei->group = checked_malloc(sizeof(struct ElementGroupInfo));
1847 xx_group = *group; // copy group data into temporary buffer
1849 setConfigToDefaultsFromConfigList(chunk_config_GRPX);
1855 clipboard_elements_initialized = TRUE;
1858 static void setLevelInfoToDefaults(struct LevelInfo *level,
1859 boolean level_info_only,
1860 boolean reset_file_status)
1862 setLevelInfoToDefaults_Level(level);
1864 if (!level_info_only)
1865 setLevelInfoToDefaults_Elements(level);
1867 if (reset_file_status)
1869 level->no_valid_file = FALSE;
1870 level->no_level_file = FALSE;
1873 level->changed = FALSE;
1876 static void setFileInfoToDefaults(struct LevelFileInfo *level_file_info)
1878 level_file_info->nr = 0;
1879 level_file_info->type = LEVEL_FILE_TYPE_UNKNOWN;
1880 level_file_info->packed = FALSE;
1882 setString(&level_file_info->basename, NULL);
1883 setString(&level_file_info->filename, NULL);
1886 int getMappedElement_SB(int, boolean);
1888 static void ActivateLevelTemplate(void)
1892 if (check_special_flags("load_xsb_to_ces"))
1894 // fill smaller playfields with padding "beyond border wall" elements
1895 if (level.fieldx < level_template.fieldx ||
1896 level.fieldy < level_template.fieldy)
1898 short field[level.fieldx][level.fieldy];
1899 int new_fieldx = MAX(level.fieldx, level_template.fieldx);
1900 int new_fieldy = MAX(level.fieldy, level_template.fieldy);
1901 int pos_fieldx = (new_fieldx - level.fieldx) / 2;
1902 int pos_fieldy = (new_fieldy - level.fieldy) / 2;
1904 // copy old playfield (which is smaller than the visible area)
1905 for (y = 0; y < level.fieldy; y++) for (x = 0; x < level.fieldx; x++)
1906 field[x][y] = level.field[x][y];
1908 // fill new, larger playfield with "beyond border wall" elements
1909 for (y = 0; y < new_fieldy; y++) for (x = 0; x < new_fieldx; x++)
1910 level.field[x][y] = getMappedElement_SB('_', TRUE);
1912 // copy the old playfield to the middle of the new playfield
1913 for (y = 0; y < level.fieldy; y++) for (x = 0; x < level.fieldx; x++)
1914 level.field[pos_fieldx + x][pos_fieldy + y] = field[x][y];
1916 level.fieldx = new_fieldx;
1917 level.fieldy = new_fieldy;
1921 // Currently there is no special action needed to activate the template
1922 // data, because 'element_info' property settings overwrite the original
1923 // level data, while all other variables do not change.
1925 // Exception: 'from_level_template' elements in the original level playfield
1926 // are overwritten with the corresponding elements at the same position in
1927 // playfield from the level template.
1929 for (x = 0; x < level.fieldx; x++)
1930 for (y = 0; y < level.fieldy; y++)
1931 if (level.field[x][y] == EL_FROM_LEVEL_TEMPLATE)
1932 level.field[x][y] = level_template.field[x][y];
1934 if (check_special_flags("load_xsb_to_ces"))
1936 struct LevelInfo level_backup = level;
1938 // overwrite all individual level settings from template level settings
1939 level = level_template;
1941 // restore level file info
1942 level.file_info = level_backup.file_info;
1944 // restore playfield size
1945 level.fieldx = level_backup.fieldx;
1946 level.fieldy = level_backup.fieldy;
1948 // restore playfield content
1949 for (x = 0; x < level.fieldx; x++)
1950 for (y = 0; y < level.fieldy; y++)
1951 level.field[x][y] = level_backup.field[x][y];
1953 // restore name and author from individual level
1954 strcpy(level.name, level_backup.name);
1955 strcpy(level.author, level_backup.author);
1957 // restore flag "use_custom_template"
1958 level.use_custom_template = level_backup.use_custom_template;
1962 static char *getLevelFilenameFromBasename(char *basename)
1964 static char *filename = NULL;
1966 checked_free(filename);
1968 filename = getPath2(getCurrentLevelDir(), basename);
1973 static int getFileTypeFromBasename(char *basename)
1975 // !!! ALSO SEE COMMENT IN checkForPackageFromBasename() !!!
1977 static char *filename = NULL;
1978 struct stat file_status;
1980 // ---------- try to determine file type from filename ----------
1982 // check for typical filename of a Supaplex level package file
1983 if (strlen(basename) == 10 && strPrefixLower(basename, "levels.d"))
1984 return LEVEL_FILE_TYPE_SP;
1986 // check for typical filename of a Diamond Caves II level package file
1987 if (strSuffixLower(basename, ".dc") ||
1988 strSuffixLower(basename, ".dc2"))
1989 return LEVEL_FILE_TYPE_DC;
1991 // check for typical filename of a Sokoban level package file
1992 if (strSuffixLower(basename, ".xsb") &&
1993 strchr(basename, '%') == NULL)
1994 return LEVEL_FILE_TYPE_SB;
1996 // ---------- try to determine file type from filesize ----------
1998 checked_free(filename);
1999 filename = getPath2(getCurrentLevelDir(), basename);
2001 if (stat(filename, &file_status) == 0)
2003 // check for typical filesize of a Supaplex level package file
2004 if (file_status.st_size == 170496)
2005 return LEVEL_FILE_TYPE_SP;
2008 return LEVEL_FILE_TYPE_UNKNOWN;
2011 static int getFileTypeFromMagicBytes(char *filename, int type)
2015 if ((file = openFile(filename, MODE_READ)))
2017 char chunk_name[CHUNK_ID_LEN + 1];
2019 getFileChunkBE(file, chunk_name, NULL);
2021 if (strEqual(chunk_name, "MMII") ||
2022 strEqual(chunk_name, "MIRR"))
2023 type = LEVEL_FILE_TYPE_MM;
2031 static boolean checkForPackageFromBasename(char *basename)
2033 // !!! WON'T WORK ANYMORE IF getFileTypeFromBasename() ALSO DETECTS !!!
2034 // !!! SINGLE LEVELS (CURRENTLY ONLY DETECTS LEVEL PACKAGES !!!
2036 return (getFileTypeFromBasename(basename) != LEVEL_FILE_TYPE_UNKNOWN);
2039 static char *getSingleLevelBasenameExt(int nr, char *extension)
2041 static char basename[MAX_FILENAME_LEN];
2044 sprintf(basename, "%s", LEVELTEMPLATE_FILENAME);
2046 sprintf(basename, "%03d.%s", nr, extension);
2051 static char *getSingleLevelBasename(int nr)
2053 return getSingleLevelBasenameExt(nr, LEVELFILE_EXTENSION);
2056 static char *getPackedLevelBasename(int type)
2058 static char basename[MAX_FILENAME_LEN];
2059 char *directory = getCurrentLevelDir();
2061 DirectoryEntry *dir_entry;
2063 strcpy(basename, UNDEFINED_FILENAME); // default: undefined file
2065 if ((dir = openDirectory(directory)) == NULL)
2067 Error(ERR_WARN, "cannot read current level directory '%s'", directory);
2072 while ((dir_entry = readDirectory(dir)) != NULL) // loop all entries
2074 char *entry_basename = dir_entry->basename;
2075 int entry_type = getFileTypeFromBasename(entry_basename);
2077 if (entry_type != LEVEL_FILE_TYPE_UNKNOWN) // found valid level package
2079 if (type == LEVEL_FILE_TYPE_UNKNOWN ||
2082 strcpy(basename, entry_basename);
2089 closeDirectory(dir);
2094 static char *getSingleLevelFilename(int nr)
2096 return getLevelFilenameFromBasename(getSingleLevelBasename(nr));
2099 #if ENABLE_UNUSED_CODE
2100 static char *getPackedLevelFilename(int type)
2102 return getLevelFilenameFromBasename(getPackedLevelBasename(type));
2106 char *getDefaultLevelFilename(int nr)
2108 return getSingleLevelFilename(nr);
2111 #if ENABLE_UNUSED_CODE
2112 static void setLevelFileInfo_SingleLevelFilename(struct LevelFileInfo *lfi,
2116 lfi->packed = FALSE;
2118 setString(&lfi->basename, getSingleLevelBasename(lfi->nr, lfi->type));
2119 setString(&lfi->filename, getLevelFilenameFromBasename(lfi->basename));
2123 static void setLevelFileInfo_FormatLevelFilename(struct LevelFileInfo *lfi,
2124 int type, char *format, ...)
2126 static char basename[MAX_FILENAME_LEN];
2129 va_start(ap, format);
2130 vsprintf(basename, format, ap);
2134 lfi->packed = FALSE;
2136 setString(&lfi->basename, basename);
2137 setString(&lfi->filename, getLevelFilenameFromBasename(lfi->basename));
2140 static void setLevelFileInfo_PackedLevelFilename(struct LevelFileInfo *lfi,
2146 setString(&lfi->basename, getPackedLevelBasename(lfi->type));
2147 setString(&lfi->filename, getLevelFilenameFromBasename(lfi->basename));
2150 static int getFiletypeFromID(char *filetype_id)
2152 char *filetype_id_lower;
2153 int filetype = LEVEL_FILE_TYPE_UNKNOWN;
2156 if (filetype_id == NULL)
2157 return LEVEL_FILE_TYPE_UNKNOWN;
2159 filetype_id_lower = getStringToLower(filetype_id);
2161 for (i = 0; filetype_id_list[i].id != NULL; i++)
2163 char *id_lower = getStringToLower(filetype_id_list[i].id);
2165 if (strEqual(filetype_id_lower, id_lower))
2166 filetype = filetype_id_list[i].filetype;
2170 if (filetype != LEVEL_FILE_TYPE_UNKNOWN)
2174 free(filetype_id_lower);
2179 char *getLocalLevelTemplateFilename(void)
2181 return getDefaultLevelFilename(-1);
2184 char *getGlobalLevelTemplateFilename(void)
2186 // global variable "leveldir_current" must be modified in the loop below
2187 LevelDirTree *leveldir_current_last = leveldir_current;
2188 char *filename = NULL;
2190 // check for template level in path from current to topmost tree node
2192 while (leveldir_current != NULL)
2194 filename = getDefaultLevelFilename(-1);
2196 if (fileExists(filename))
2199 leveldir_current = leveldir_current->node_parent;
2202 // restore global variable "leveldir_current" modified in above loop
2203 leveldir_current = leveldir_current_last;
2208 static void determineLevelFileInfo_Filename(struct LevelFileInfo *lfi)
2212 // special case: level number is negative => check for level template file
2215 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
2216 getSingleLevelBasename(-1));
2218 // replace local level template filename with global template filename
2219 setString(&lfi->filename, getGlobalLevelTemplateFilename());
2221 // no fallback if template file not existing
2225 // special case: check for file name/pattern specified in "levelinfo.conf"
2226 if (leveldir_current->level_filename != NULL)
2228 int filetype = getFiletypeFromID(leveldir_current->level_filetype);
2230 setLevelFileInfo_FormatLevelFilename(lfi, filetype,
2231 leveldir_current->level_filename, nr);
2233 lfi->packed = checkForPackageFromBasename(leveldir_current->level_filename);
2235 if (fileExists(lfi->filename))
2238 else if (leveldir_current->level_filetype != NULL)
2240 int filetype = getFiletypeFromID(leveldir_current->level_filetype);
2242 // check for specified native level file with standard file name
2243 setLevelFileInfo_FormatLevelFilename(lfi, filetype,
2244 "%03d.%s", nr, LEVELFILE_EXTENSION);
2245 if (fileExists(lfi->filename))
2249 // check for native Rocks'n'Diamonds level file
2250 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
2251 "%03d.%s", nr, LEVELFILE_EXTENSION);
2252 if (fileExists(lfi->filename))
2255 // check for Emerald Mine level file (V1)
2256 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "a%c%c",
2257 'a' + (nr / 10) % 26, '0' + nr % 10);
2258 if (fileExists(lfi->filename))
2260 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "A%c%c",
2261 'A' + (nr / 10) % 26, '0' + nr % 10);
2262 if (fileExists(lfi->filename))
2265 // check for Emerald Mine level file (V2 to V5)
2266 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%d", nr);
2267 if (fileExists(lfi->filename))
2270 // check for Emerald Mine level file (V6 / single mode)
2271 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02ds", nr);
2272 if (fileExists(lfi->filename))
2274 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02dS", nr);
2275 if (fileExists(lfi->filename))
2278 // check for Emerald Mine level file (V6 / teamwork mode)
2279 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02dt", nr);
2280 if (fileExists(lfi->filename))
2282 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02dT", nr);
2283 if (fileExists(lfi->filename))
2286 // check for various packed level file formats
2287 setLevelFileInfo_PackedLevelFilename(lfi, LEVEL_FILE_TYPE_UNKNOWN);
2288 if (fileExists(lfi->filename))
2291 // no known level file found -- use default values (and fail later)
2292 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
2293 "%03d.%s", nr, LEVELFILE_EXTENSION);
2296 static void determineLevelFileInfo_Filetype(struct LevelFileInfo *lfi)
2298 if (lfi->type == LEVEL_FILE_TYPE_UNKNOWN)
2299 lfi->type = getFileTypeFromBasename(lfi->basename);
2301 if (lfi->type == LEVEL_FILE_TYPE_RND)
2302 lfi->type = getFileTypeFromMagicBytes(lfi->filename, lfi->type);
2305 static void setLevelFileInfo(struct LevelFileInfo *level_file_info, int nr)
2307 // always start with reliable default values
2308 setFileInfoToDefaults(level_file_info);
2310 level_file_info->nr = nr; // set requested level number
2312 determineLevelFileInfo_Filename(level_file_info);
2313 determineLevelFileInfo_Filetype(level_file_info);
2316 static void copyLevelFileInfo(struct LevelFileInfo *lfi_from,
2317 struct LevelFileInfo *lfi_to)
2319 lfi_to->nr = lfi_from->nr;
2320 lfi_to->type = lfi_from->type;
2321 lfi_to->packed = lfi_from->packed;
2323 setString(&lfi_to->basename, lfi_from->basename);
2324 setString(&lfi_to->filename, lfi_from->filename);
2327 // ----------------------------------------------------------------------------
2328 // functions for loading R'n'D level
2329 // ----------------------------------------------------------------------------
2331 static int getMappedElement(int element)
2333 // remap some (historic, now obsolete) elements
2337 case EL_PLAYER_OBSOLETE:
2338 element = EL_PLAYER_1;
2341 case EL_KEY_OBSOLETE:
2345 case EL_EM_KEY_1_FILE_OBSOLETE:
2346 element = EL_EM_KEY_1;
2349 case EL_EM_KEY_2_FILE_OBSOLETE:
2350 element = EL_EM_KEY_2;
2353 case EL_EM_KEY_3_FILE_OBSOLETE:
2354 element = EL_EM_KEY_3;
2357 case EL_EM_KEY_4_FILE_OBSOLETE:
2358 element = EL_EM_KEY_4;
2361 case EL_ENVELOPE_OBSOLETE:
2362 element = EL_ENVELOPE_1;
2370 if (element >= NUM_FILE_ELEMENTS)
2372 Error(ERR_WARN, "invalid level element %d", element);
2374 element = EL_UNKNOWN;
2382 static int getMappedElementByVersion(int element, int game_version)
2384 // remap some elements due to certain game version
2386 if (game_version <= VERSION_IDENT(2,2,0,0))
2388 // map game font elements
2389 element = (element == EL_CHAR('[') ? EL_CHAR_AUMLAUT :
2390 element == EL_CHAR('\\') ? EL_CHAR_OUMLAUT :
2391 element == EL_CHAR(']') ? EL_CHAR_UUMLAUT :
2392 element == EL_CHAR('^') ? EL_CHAR_COPYRIGHT : element);
2395 if (game_version < VERSION_IDENT(3,0,0,0))
2397 // map Supaplex gravity tube elements
2398 element = (element == EL_SP_GRAVITY_PORT_LEFT ? EL_SP_PORT_LEFT :
2399 element == EL_SP_GRAVITY_PORT_RIGHT ? EL_SP_PORT_RIGHT :
2400 element == EL_SP_GRAVITY_PORT_UP ? EL_SP_PORT_UP :
2401 element == EL_SP_GRAVITY_PORT_DOWN ? EL_SP_PORT_DOWN :
2408 static int LoadLevel_VERS(File *file, int chunk_size, struct LevelInfo *level)
2410 level->file_version = getFileVersion(file);
2411 level->game_version = getFileVersion(file);
2416 static int LoadLevel_DATE(File *file, int chunk_size, struct LevelInfo *level)
2418 level->creation_date.year = getFile16BitBE(file);
2419 level->creation_date.month = getFile8Bit(file);
2420 level->creation_date.day = getFile8Bit(file);
2422 level->creation_date.src = DATE_SRC_LEVELFILE;
2427 static int LoadLevel_HEAD(File *file, int chunk_size, struct LevelInfo *level)
2429 int initial_player_stepsize;
2430 int initial_player_gravity;
2433 level->fieldx = getFile8Bit(file);
2434 level->fieldy = getFile8Bit(file);
2436 level->time = getFile16BitBE(file);
2437 level->gems_needed = getFile16BitBE(file);
2439 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
2440 level->name[i] = getFile8Bit(file);
2441 level->name[MAX_LEVEL_NAME_LEN] = 0;
2443 for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
2444 level->score[i] = getFile8Bit(file);
2446 level->num_yamyam_contents = STD_ELEMENT_CONTENTS;
2447 for (i = 0; i < STD_ELEMENT_CONTENTS; i++)
2448 for (y = 0; y < 3; y++)
2449 for (x = 0; x < 3; x++)
2450 level->yamyam_content[i].e[x][y] = getMappedElement(getFile8Bit(file));
2452 level->amoeba_speed = getFile8Bit(file);
2453 level->time_magic_wall = getFile8Bit(file);
2454 level->time_wheel = getFile8Bit(file);
2455 level->amoeba_content = getMappedElement(getFile8Bit(file));
2457 initial_player_stepsize = (getFile8Bit(file) == 1 ? STEPSIZE_FAST :
2460 for (i = 0; i < MAX_PLAYERS; i++)
2461 level->initial_player_stepsize[i] = initial_player_stepsize;
2463 initial_player_gravity = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2465 for (i = 0; i < MAX_PLAYERS; i++)
2466 level->initial_player_gravity[i] = initial_player_gravity;
2468 level->encoding_16bit_field = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2469 level->em_slippery_gems = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2471 level->use_custom_template = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2473 level->block_last_field = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2474 level->sp_block_last_field = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2475 level->can_move_into_acid_bits = getFile32BitBE(file);
2476 level->dont_collide_with_bits = getFile8Bit(file);
2478 level->use_spring_bug = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2479 level->use_step_counter = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2481 level->instant_relocation = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2482 level->can_pass_to_walkable = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2483 level->grow_into_diggable = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2485 level->game_engine_type = getFile8Bit(file);
2487 ReadUnusedBytesFromFile(file, LEVEL_CHUNK_HEAD_UNUSED);
2492 static int LoadLevel_NAME(File *file, int chunk_size, struct LevelInfo *level)
2496 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
2497 level->name[i] = getFile8Bit(file);
2498 level->name[MAX_LEVEL_NAME_LEN] = 0;
2503 static int LoadLevel_AUTH(File *file, int chunk_size, struct LevelInfo *level)
2507 for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
2508 level->author[i] = getFile8Bit(file);
2509 level->author[MAX_LEVEL_AUTHOR_LEN] = 0;
2514 static int LoadLevel_BODY(File *file, int chunk_size, struct LevelInfo *level)
2517 int chunk_size_expected = level->fieldx * level->fieldy;
2519 /* Note: "chunk_size" was wrong before version 2.0 when elements are
2520 stored with 16-bit encoding (and should be twice as big then).
2521 Even worse, playfield data was stored 16-bit when only yamyam content
2522 contained 16-bit elements and vice versa. */
2524 if (level->encoding_16bit_field && level->file_version >= FILE_VERSION_2_0)
2525 chunk_size_expected *= 2;
2527 if (chunk_size_expected != chunk_size)
2529 ReadUnusedBytesFromFile(file, chunk_size);
2530 return chunk_size_expected;
2533 for (y = 0; y < level->fieldy; y++)
2534 for (x = 0; x < level->fieldx; x++)
2535 level->field[x][y] =
2536 getMappedElement(level->encoding_16bit_field ? getFile16BitBE(file) :
2541 static int LoadLevel_CONT(File *file, int chunk_size, struct LevelInfo *level)
2544 int header_size = 4;
2545 int content_size = MAX_ELEMENT_CONTENTS * 3 * 3;
2546 int chunk_size_expected = header_size + content_size;
2548 /* Note: "chunk_size" was wrong before version 2.0 when elements are
2549 stored with 16-bit encoding (and should be twice as big then).
2550 Even worse, playfield data was stored 16-bit when only yamyam content
2551 contained 16-bit elements and vice versa. */
2553 if (level->encoding_16bit_field && level->file_version >= FILE_VERSION_2_0)
2554 chunk_size_expected += content_size;
2556 if (chunk_size_expected != chunk_size)
2558 ReadUnusedBytesFromFile(file, chunk_size);
2559 return chunk_size_expected;
2563 level->num_yamyam_contents = getFile8Bit(file);
2567 // correct invalid number of content fields -- should never happen
2568 if (level->num_yamyam_contents < 1 ||
2569 level->num_yamyam_contents > MAX_ELEMENT_CONTENTS)
2570 level->num_yamyam_contents = STD_ELEMENT_CONTENTS;
2572 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
2573 for (y = 0; y < 3; y++)
2574 for (x = 0; x < 3; x++)
2575 level->yamyam_content[i].e[x][y] =
2576 getMappedElement(level->encoding_16bit_field ?
2577 getFile16BitBE(file) : getFile8Bit(file));
2581 static int LoadLevel_CNT2(File *file, int chunk_size, struct LevelInfo *level)
2586 int content_array[MAX_ELEMENT_CONTENTS][3][3];
2588 element = getMappedElement(getFile16BitBE(file));
2589 num_contents = getFile8Bit(file);
2591 getFile8Bit(file); // content x size (unused)
2592 getFile8Bit(file); // content y size (unused)
2594 ReadUnusedBytesFromFile(file, LEVEL_CHUNK_CNT2_UNUSED);
2596 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
2597 for (y = 0; y < 3; y++)
2598 for (x = 0; x < 3; x++)
2599 content_array[i][x][y] = getMappedElement(getFile16BitBE(file));
2601 // correct invalid number of content fields -- should never happen
2602 if (num_contents < 1 || num_contents > MAX_ELEMENT_CONTENTS)
2603 num_contents = STD_ELEMENT_CONTENTS;
2605 if (element == EL_YAMYAM)
2607 level->num_yamyam_contents = num_contents;
2609 for (i = 0; i < num_contents; i++)
2610 for (y = 0; y < 3; y++)
2611 for (x = 0; x < 3; x++)
2612 level->yamyam_content[i].e[x][y] = content_array[i][x][y];
2614 else if (element == EL_BD_AMOEBA)
2616 level->amoeba_content = content_array[0][0][0];
2620 Error(ERR_WARN, "cannot load content for element '%d'", element);
2626 static int LoadLevel_CNT3(File *file, int chunk_size, struct LevelInfo *level)
2632 int chunk_size_expected;
2634 element = getMappedElement(getFile16BitBE(file));
2635 if (!IS_ENVELOPE(element))
2636 element = EL_ENVELOPE_1;
2638 envelope_nr = element - EL_ENVELOPE_1;
2640 envelope_len = getFile16BitBE(file);
2642 level->envelope[envelope_nr].xsize = getFile8Bit(file);
2643 level->envelope[envelope_nr].ysize = getFile8Bit(file);
2645 ReadUnusedBytesFromFile(file, LEVEL_CHUNK_CNT3_UNUSED);
2647 chunk_size_expected = LEVEL_CHUNK_CNT3_SIZE(envelope_len);
2648 if (chunk_size_expected != chunk_size)
2650 ReadUnusedBytesFromFile(file, chunk_size - LEVEL_CHUNK_CNT3_HEADER);
2651 return chunk_size_expected;
2654 for (i = 0; i < envelope_len; i++)
2655 level->envelope[envelope_nr].text[i] = getFile8Bit(file);
2660 static int LoadLevel_CUS1(File *file, int chunk_size, struct LevelInfo *level)
2662 int num_changed_custom_elements = getFile16BitBE(file);
2663 int chunk_size_expected = 2 + num_changed_custom_elements * 6;
2666 if (chunk_size_expected != chunk_size)
2668 ReadUnusedBytesFromFile(file, chunk_size - 2);
2669 return chunk_size_expected;
2672 for (i = 0; i < num_changed_custom_elements; i++)
2674 int element = getMappedElement(getFile16BitBE(file));
2675 int properties = getFile32BitBE(file);
2677 if (IS_CUSTOM_ELEMENT(element))
2678 element_info[element].properties[EP_BITFIELD_BASE_NR] = properties;
2680 Error(ERR_WARN, "invalid custom element number %d", element);
2682 // older game versions that wrote level files with CUS1 chunks used
2683 // different default push delay values (not yet stored in level file)
2684 element_info[element].push_delay_fixed = 2;
2685 element_info[element].push_delay_random = 8;
2688 level->file_has_custom_elements = TRUE;
2693 static int LoadLevel_CUS2(File *file, int chunk_size, struct LevelInfo *level)
2695 int num_changed_custom_elements = getFile16BitBE(file);
2696 int chunk_size_expected = 2 + num_changed_custom_elements * 4;
2699 if (chunk_size_expected != chunk_size)
2701 ReadUnusedBytesFromFile(file, chunk_size - 2);
2702 return chunk_size_expected;
2705 for (i = 0; i < num_changed_custom_elements; i++)
2707 int element = getMappedElement(getFile16BitBE(file));
2708 int custom_target_element = getMappedElement(getFile16BitBE(file));
2710 if (IS_CUSTOM_ELEMENT(element))
2711 element_info[element].change->target_element = custom_target_element;
2713 Error(ERR_WARN, "invalid custom element number %d", element);
2716 level->file_has_custom_elements = TRUE;
2721 static int LoadLevel_CUS3(File *file, int chunk_size, struct LevelInfo *level)
2723 int num_changed_custom_elements = getFile16BitBE(file);
2724 int chunk_size_expected = LEVEL_CHUNK_CUS3_SIZE(num_changed_custom_elements);
2727 if (chunk_size_expected != chunk_size)
2729 ReadUnusedBytesFromFile(file, chunk_size - 2);
2730 return chunk_size_expected;
2733 for (i = 0; i < num_changed_custom_elements; i++)
2735 int element = getMappedElement(getFile16BitBE(file));
2736 struct ElementInfo *ei = &element_info[element];
2737 unsigned int event_bits;
2739 if (!IS_CUSTOM_ELEMENT(element))
2741 Error(ERR_WARN, "invalid custom element number %d", element);
2743 element = EL_INTERNAL_DUMMY;
2746 for (j = 0; j < MAX_ELEMENT_NAME_LEN; j++)
2747 ei->description[j] = getFile8Bit(file);
2748 ei->description[MAX_ELEMENT_NAME_LEN] = 0;
2750 ei->properties[EP_BITFIELD_BASE_NR] = getFile32BitBE(file);
2752 // some free bytes for future properties and padding
2753 ReadUnusedBytesFromFile(file, 7);
2755 ei->use_gfx_element = getFile8Bit(file);
2756 ei->gfx_element_initial = getMappedElement(getFile16BitBE(file));
2758 ei->collect_score_initial = getFile8Bit(file);
2759 ei->collect_count_initial = getFile8Bit(file);
2761 ei->push_delay_fixed = getFile16BitBE(file);
2762 ei->push_delay_random = getFile16BitBE(file);
2763 ei->move_delay_fixed = getFile16BitBE(file);
2764 ei->move_delay_random = getFile16BitBE(file);
2766 ei->move_pattern = getFile16BitBE(file);
2767 ei->move_direction_initial = getFile8Bit(file);
2768 ei->move_stepsize = getFile8Bit(file);
2770 for (y = 0; y < 3; y++)
2771 for (x = 0; x < 3; x++)
2772 ei->content.e[x][y] = getMappedElement(getFile16BitBE(file));
2774 event_bits = getFile32BitBE(file);
2775 for (j = 0; j < NUM_CHANGE_EVENTS; j++)
2776 if (event_bits & (1 << j))
2777 ei->change->has_event[j] = TRUE;
2779 ei->change->target_element = getMappedElement(getFile16BitBE(file));
2781 ei->change->delay_fixed = getFile16BitBE(file);
2782 ei->change->delay_random = getFile16BitBE(file);
2783 ei->change->delay_frames = getFile16BitBE(file);
2785 ei->change->initial_trigger_element= getMappedElement(getFile16BitBE(file));
2787 ei->change->explode = getFile8Bit(file);
2788 ei->change->use_target_content = getFile8Bit(file);
2789 ei->change->only_if_complete = getFile8Bit(file);
2790 ei->change->use_random_replace = getFile8Bit(file);
2792 ei->change->random_percentage = getFile8Bit(file);
2793 ei->change->replace_when = getFile8Bit(file);
2795 for (y = 0; y < 3; y++)
2796 for (x = 0; x < 3; x++)
2797 ei->change->target_content.e[x][y] =
2798 getMappedElement(getFile16BitBE(file));
2800 ei->slippery_type = getFile8Bit(file);
2802 // some free bytes for future properties and padding
2803 ReadUnusedBytesFromFile(file, LEVEL_CPART_CUS3_UNUSED);
2805 // mark that this custom element has been modified
2806 ei->modified_settings = TRUE;
2809 level->file_has_custom_elements = TRUE;
2814 static int LoadLevel_CUS4(File *file, int chunk_size, struct LevelInfo *level)
2816 struct ElementInfo *ei;
2817 int chunk_size_expected;
2821 // ---------- custom element base property values (96 bytes) ----------------
2823 element = getMappedElement(getFile16BitBE(file));
2825 if (!IS_CUSTOM_ELEMENT(element))
2827 Error(ERR_WARN, "invalid custom element number %d", element);
2829 ReadUnusedBytesFromFile(file, chunk_size - 2);
2833 ei = &element_info[element];
2835 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
2836 ei->description[i] = getFile8Bit(file);
2837 ei->description[MAX_ELEMENT_NAME_LEN] = 0;
2839 ei->properties[EP_BITFIELD_BASE_NR] = getFile32BitBE(file);
2841 ReadUnusedBytesFromFile(file, 4); // reserved for more base properties
2843 ei->num_change_pages = getFile8Bit(file);
2845 chunk_size_expected = LEVEL_CHUNK_CUS4_SIZE(ei->num_change_pages);
2846 if (chunk_size_expected != chunk_size)
2848 ReadUnusedBytesFromFile(file, chunk_size - 43);
2849 return chunk_size_expected;
2852 ei->ce_value_fixed_initial = getFile16BitBE(file);
2853 ei->ce_value_random_initial = getFile16BitBE(file);
2854 ei->use_last_ce_value = getFile8Bit(file);
2856 ei->use_gfx_element = getFile8Bit(file);
2857 ei->gfx_element_initial = getMappedElement(getFile16BitBE(file));
2859 ei->collect_score_initial = getFile8Bit(file);
2860 ei->collect_count_initial = getFile8Bit(file);
2862 ei->drop_delay_fixed = getFile8Bit(file);
2863 ei->push_delay_fixed = getFile8Bit(file);
2864 ei->drop_delay_random = getFile8Bit(file);
2865 ei->push_delay_random = getFile8Bit(file);
2866 ei->move_delay_fixed = getFile16BitBE(file);
2867 ei->move_delay_random = getFile16BitBE(file);
2869 // bits 0 - 15 of "move_pattern" ...
2870 ei->move_pattern = getFile16BitBE(file);
2871 ei->move_direction_initial = getFile8Bit(file);
2872 ei->move_stepsize = getFile8Bit(file);
2874 ei->slippery_type = getFile8Bit(file);
2876 for (y = 0; y < 3; y++)
2877 for (x = 0; x < 3; x++)
2878 ei->content.e[x][y] = getMappedElement(getFile16BitBE(file));
2880 ei->move_enter_element = getMappedElement(getFile16BitBE(file));
2881 ei->move_leave_element = getMappedElement(getFile16BitBE(file));
2882 ei->move_leave_type = getFile8Bit(file);
2884 // ... bits 16 - 31 of "move_pattern" (not nice, but downward compatible)
2885 ei->move_pattern |= (getFile16BitBE(file) << 16);
2887 ei->access_direction = getFile8Bit(file);
2889 ei->explosion_delay = getFile8Bit(file);
2890 ei->ignition_delay = getFile8Bit(file);
2891 ei->explosion_type = getFile8Bit(file);
2893 // some free bytes for future custom property values and padding
2894 ReadUnusedBytesFromFile(file, 1);
2896 // ---------- change page property values (48 bytes) ------------------------
2898 setElementChangePages(ei, ei->num_change_pages);
2900 for (i = 0; i < ei->num_change_pages; i++)
2902 struct ElementChangeInfo *change = &ei->change_page[i];
2903 unsigned int event_bits;
2905 // always start with reliable default values
2906 setElementChangeInfoToDefaults(change);
2908 // bits 0 - 31 of "has_event[]" ...
2909 event_bits = getFile32BitBE(file);
2910 for (j = 0; j < MIN(NUM_CHANGE_EVENTS, 32); j++)
2911 if (event_bits & (1 << j))
2912 change->has_event[j] = TRUE;
2914 change->target_element = getMappedElement(getFile16BitBE(file));
2916 change->delay_fixed = getFile16BitBE(file);
2917 change->delay_random = getFile16BitBE(file);
2918 change->delay_frames = getFile16BitBE(file);
2920 change->initial_trigger_element = getMappedElement(getFile16BitBE(file));
2922 change->explode = getFile8Bit(file);
2923 change->use_target_content = getFile8Bit(file);
2924 change->only_if_complete = getFile8Bit(file);
2925 change->use_random_replace = getFile8Bit(file);
2927 change->random_percentage = getFile8Bit(file);
2928 change->replace_when = getFile8Bit(file);
2930 for (y = 0; y < 3; y++)
2931 for (x = 0; x < 3; x++)
2932 change->target_content.e[x][y]= getMappedElement(getFile16BitBE(file));
2934 change->can_change = getFile8Bit(file);
2936 change->trigger_side = getFile8Bit(file);
2938 change->trigger_player = getFile8Bit(file);
2939 change->trigger_page = getFile8Bit(file);
2941 change->trigger_page = (change->trigger_page == CH_PAGE_ANY_FILE ?
2942 CH_PAGE_ANY : (1 << change->trigger_page));
2944 change->has_action = getFile8Bit(file);
2945 change->action_type = getFile8Bit(file);
2946 change->action_mode = getFile8Bit(file);
2947 change->action_arg = getFile16BitBE(file);
2949 // ... bits 32 - 39 of "has_event[]" (not nice, but downward compatible)
2950 event_bits = getFile8Bit(file);
2951 for (j = 32; j < NUM_CHANGE_EVENTS; j++)
2952 if (event_bits & (1 << (j - 32)))
2953 change->has_event[j] = TRUE;
2956 // mark this custom element as modified
2957 ei->modified_settings = TRUE;
2959 level->file_has_custom_elements = TRUE;
2964 static int LoadLevel_GRP1(File *file, int chunk_size, struct LevelInfo *level)
2966 struct ElementInfo *ei;
2967 struct ElementGroupInfo *group;
2971 element = getMappedElement(getFile16BitBE(file));
2973 if (!IS_GROUP_ELEMENT(element))
2975 Error(ERR_WARN, "invalid group element number %d", element);
2977 ReadUnusedBytesFromFile(file, chunk_size - 2);
2981 ei = &element_info[element];
2983 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
2984 ei->description[i] = getFile8Bit(file);
2985 ei->description[MAX_ELEMENT_NAME_LEN] = 0;
2987 group = element_info[element].group;
2989 group->num_elements = getFile8Bit(file);
2991 ei->use_gfx_element = getFile8Bit(file);
2992 ei->gfx_element_initial = getMappedElement(getFile16BitBE(file));
2994 group->choice_mode = getFile8Bit(file);
2996 // some free bytes for future values and padding
2997 ReadUnusedBytesFromFile(file, 3);
2999 for (i = 0; i < MAX_ELEMENTS_IN_GROUP; i++)
3000 group->element[i] = getMappedElement(getFile16BitBE(file));
3002 // mark this group element as modified
3003 element_info[element].modified_settings = TRUE;
3005 level->file_has_custom_elements = TRUE;
3010 static int LoadLevel_MicroChunk(File *file, struct LevelFileConfigInfo *conf,
3011 int element, int real_element)
3013 int micro_chunk_size = 0;
3014 int conf_type = getFile8Bit(file);
3015 int byte_mask = conf_type & CONF_MASK_BYTES;
3016 boolean element_found = FALSE;
3019 micro_chunk_size += 1;
3021 if (byte_mask == CONF_MASK_MULTI_BYTES)
3023 int num_bytes = getFile16BitBE(file);
3024 byte *buffer = checked_malloc(num_bytes);
3026 ReadBytesFromFile(file, buffer, num_bytes);
3028 for (i = 0; conf[i].data_type != -1; i++)
3030 if (conf[i].element == element &&
3031 conf[i].conf_type == conf_type)
3033 int data_type = conf[i].data_type;
3034 int num_entities = num_bytes / CONF_ENTITY_NUM_BYTES(data_type);
3035 int max_num_entities = conf[i].max_num_entities;
3037 if (num_entities > max_num_entities)
3040 "truncating number of entities for element %d from %d to %d",
3041 element, num_entities, max_num_entities);
3043 num_entities = max_num_entities;
3046 if (num_entities == 0 && (data_type == TYPE_ELEMENT_LIST ||
3047 data_type == TYPE_CONTENT_LIST))
3049 // for element and content lists, zero entities are not allowed
3050 Error(ERR_WARN, "found empty list of entities for element %d",
3053 // do not set "num_entities" here to prevent reading behind buffer
3055 *(int *)(conf[i].num_entities) = 1; // at least one is required
3059 *(int *)(conf[i].num_entities) = num_entities;
3062 element_found = TRUE;
3064 if (data_type == TYPE_STRING)
3066 char *string = (char *)(conf[i].value);
3069 for (j = 0; j < max_num_entities; j++)
3070 string[j] = (j < num_entities ? buffer[j] : '\0');
3072 else if (data_type == TYPE_ELEMENT_LIST)
3074 int *element_array = (int *)(conf[i].value);
3077 for (j = 0; j < num_entities; j++)
3079 getMappedElement(CONF_ELEMENTS_ELEMENT(buffer, j));
3081 else if (data_type == TYPE_CONTENT_LIST)
3083 struct Content *content= (struct Content *)(conf[i].value);
3086 for (c = 0; c < num_entities; c++)
3087 for (y = 0; y < 3; y++)
3088 for (x = 0; x < 3; x++)
3089 content[c].e[x][y] =
3090 getMappedElement(CONF_CONTENTS_ELEMENT(buffer, c, x, y));
3093 element_found = FALSE;
3099 checked_free(buffer);
3101 micro_chunk_size += 2 + num_bytes;
3103 else // constant size configuration data (1, 2 or 4 bytes)
3105 int value = (byte_mask == CONF_MASK_1_BYTE ? getFile8Bit (file) :
3106 byte_mask == CONF_MASK_2_BYTE ? getFile16BitBE(file) :
3107 byte_mask == CONF_MASK_4_BYTE ? getFile32BitBE(file) : 0);
3109 for (i = 0; conf[i].data_type != -1; i++)
3111 if (conf[i].element == element &&
3112 conf[i].conf_type == conf_type)
3114 int data_type = conf[i].data_type;
3116 if (data_type == TYPE_ELEMENT)
3117 value = getMappedElement(value);
3119 if (data_type == TYPE_BOOLEAN)
3120 *(boolean *)(conf[i].value) = value;
3122 *(int *) (conf[i].value) = value;
3124 element_found = TRUE;
3130 micro_chunk_size += CONF_VALUE_NUM_BYTES(byte_mask);
3135 char *error_conf_chunk_bytes =
3136 (byte_mask == CONF_MASK_1_BYTE ? "CONF_VALUE_8_BIT" :
3137 byte_mask == CONF_MASK_2_BYTE ? "CONF_VALUE_16_BIT" :
3138 byte_mask == CONF_MASK_4_BYTE ? "CONF_VALUE_32_BIT" :"CONF_VALUE_BYTES");
3139 int error_conf_chunk_token = conf_type & CONF_MASK_TOKEN;
3140 int error_element = real_element;
3142 Error(ERR_WARN, "cannot load micro chunk '%s(%d)' value for element %d ['%s']",
3143 error_conf_chunk_bytes, error_conf_chunk_token,
3144 error_element, EL_NAME(error_element));
3147 return micro_chunk_size;
3150 static int LoadLevel_INFO(File *file, int chunk_size, struct LevelInfo *level)
3152 int real_chunk_size = 0;
3154 li = *level; // copy level data into temporary buffer
3156 while (!checkEndOfFile(file))
3158 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_INFO, -1, -1);
3160 if (real_chunk_size >= chunk_size)
3164 *level = li; // copy temporary buffer back to level data
3166 return real_chunk_size;
3169 static int LoadLevel_CONF(File *file, int chunk_size, struct LevelInfo *level)
3171 int real_chunk_size = 0;
3173 li = *level; // copy level data into temporary buffer
3175 while (!checkEndOfFile(file))
3177 int element = getMappedElement(getFile16BitBE(file));
3179 real_chunk_size += 2;
3180 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_CONF,
3182 if (real_chunk_size >= chunk_size)
3186 *level = li; // copy temporary buffer back to level data
3188 return real_chunk_size;
3191 static int LoadLevel_ELEM(File *file, int chunk_size, struct LevelInfo *level)
3193 int real_chunk_size = 0;
3195 li = *level; // copy level data into temporary buffer
3197 while (!checkEndOfFile(file))
3199 int element = getMappedElement(getFile16BitBE(file));
3201 real_chunk_size += 2;
3202 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_ELEM,
3204 if (real_chunk_size >= chunk_size)
3208 *level = li; // copy temporary buffer back to level data
3210 return real_chunk_size;
3213 static int LoadLevel_NOTE(File *file, int chunk_size, struct LevelInfo *level)
3215 int element = getMappedElement(getFile16BitBE(file));
3216 int envelope_nr = element - EL_ENVELOPE_1;
3217 int real_chunk_size = 2;
3219 xx_envelope = level->envelope[envelope_nr]; // copy into temporary buffer
3221 while (!checkEndOfFile(file))
3223 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_NOTE,
3226 if (real_chunk_size >= chunk_size)
3230 level->envelope[envelope_nr] = xx_envelope; // copy from temporary buffer
3232 return real_chunk_size;
3235 static int LoadLevel_CUSX(File *file, int chunk_size, struct LevelInfo *level)
3237 int element = getMappedElement(getFile16BitBE(file));
3238 int real_chunk_size = 2;
3239 struct ElementInfo *ei = &element_info[element];
3242 xx_ei = *ei; // copy element data into temporary buffer
3244 xx_ei.num_change_pages = -1;
3246 while (!checkEndOfFile(file))
3248 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_CUSX_base,
3250 if (xx_ei.num_change_pages != -1)
3253 if (real_chunk_size >= chunk_size)
3259 if (ei->num_change_pages == -1)
3261 Error(ERR_WARN, "LoadLevel_CUSX(): missing 'num_change_pages' for '%s'",
3264 ei->num_change_pages = 1;
3266 setElementChangePages(ei, 1);
3267 setElementChangeInfoToDefaults(ei->change);
3269 return real_chunk_size;
3272 // initialize number of change pages stored for this custom element
3273 setElementChangePages(ei, ei->num_change_pages);
3274 for (i = 0; i < ei->num_change_pages; i++)
3275 setElementChangeInfoToDefaults(&ei->change_page[i]);
3277 // start with reading properties for the first change page
3278 xx_current_change_page = 0;
3280 while (!checkEndOfFile(file))
3282 struct ElementChangeInfo *change = &ei->change_page[xx_current_change_page];
3284 xx_change = *change; // copy change data into temporary buffer
3286 resetEventBits(); // reset bits; change page might have changed
3288 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_CUSX_change,
3291 *change = xx_change;
3293 setEventFlagsFromEventBits(change);
3295 if (real_chunk_size >= chunk_size)
3299 level->file_has_custom_elements = TRUE;
3301 return real_chunk_size;
3304 static int LoadLevel_GRPX(File *file, int chunk_size, struct LevelInfo *level)
3306 int element = getMappedElement(getFile16BitBE(file));
3307 int real_chunk_size = 2;
3308 struct ElementInfo *ei = &element_info[element];
3309 struct ElementGroupInfo *group = ei->group;
3311 xx_ei = *ei; // copy element data into temporary buffer
3312 xx_group = *group; // copy group data into temporary buffer
3314 while (!checkEndOfFile(file))
3316 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_GRPX,
3319 if (real_chunk_size >= chunk_size)
3326 level->file_has_custom_elements = TRUE;
3328 return real_chunk_size;
3331 static void LoadLevelFromFileInfo_RND(struct LevelInfo *level,
3332 struct LevelFileInfo *level_file_info,
3333 boolean level_info_only)
3335 char *filename = level_file_info->filename;
3336 char cookie[MAX_LINE_LEN];
3337 char chunk_name[CHUNK_ID_LEN + 1];
3341 if (!(file = openFile(filename, MODE_READ)))
3343 level->no_valid_file = TRUE;
3344 level->no_level_file = TRUE;
3346 if (level_info_only)
3349 Error(ERR_WARN, "cannot read level '%s' -- using empty level", filename);
3351 if (!setup.editor.use_template_for_new_levels)
3354 // if level file not found, try to initialize level data from template
3355 filename = getGlobalLevelTemplateFilename();
3357 if (!(file = openFile(filename, MODE_READ)))
3360 // default: for empty levels, use level template for custom elements
3361 level->use_custom_template = TRUE;
3363 level->no_valid_file = FALSE;
3366 getFileChunkBE(file, chunk_name, NULL);
3367 if (strEqual(chunk_name, "RND1"))
3369 getFile32BitBE(file); // not used
3371 getFileChunkBE(file, chunk_name, NULL);
3372 if (!strEqual(chunk_name, "CAVE"))
3374 level->no_valid_file = TRUE;
3376 Error(ERR_WARN, "unknown format of level file '%s'", filename);
3383 else // check for pre-2.0 file format with cookie string
3385 strcpy(cookie, chunk_name);
3386 if (getStringFromFile(file, &cookie[4], MAX_LINE_LEN - 4) == NULL)
3388 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
3389 cookie[strlen(cookie) - 1] = '\0';
3391 if (!checkCookieString(cookie, LEVEL_COOKIE_TMPL))
3393 level->no_valid_file = TRUE;
3395 Error(ERR_WARN, "unknown format of level file '%s'", filename);
3402 if ((level->file_version = getFileVersionFromCookieString(cookie)) == -1)
3404 level->no_valid_file = TRUE;
3406 Error(ERR_WARN, "unsupported version of level file '%s'", filename);
3413 // pre-2.0 level files have no game version, so use file version here
3414 level->game_version = level->file_version;
3417 if (level->file_version < FILE_VERSION_1_2)
3419 // level files from versions before 1.2.0 without chunk structure
3420 LoadLevel_HEAD(file, LEVEL_CHUNK_HEAD_SIZE, level);
3421 LoadLevel_BODY(file, level->fieldx * level->fieldy, level);
3429 int (*loader)(File *, int, struct LevelInfo *);
3433 { "VERS", LEVEL_CHUNK_VERS_SIZE, LoadLevel_VERS },
3434 { "DATE", LEVEL_CHUNK_DATE_SIZE, LoadLevel_DATE },
3435 { "HEAD", LEVEL_CHUNK_HEAD_SIZE, LoadLevel_HEAD },
3436 { "NAME", LEVEL_CHUNK_NAME_SIZE, LoadLevel_NAME },
3437 { "AUTH", LEVEL_CHUNK_AUTH_SIZE, LoadLevel_AUTH },
3438 { "INFO", -1, LoadLevel_INFO },
3439 { "BODY", -1, LoadLevel_BODY },
3440 { "CONT", -1, LoadLevel_CONT },
3441 { "CNT2", LEVEL_CHUNK_CNT2_SIZE, LoadLevel_CNT2 },
3442 { "CNT3", -1, LoadLevel_CNT3 },
3443 { "CUS1", -1, LoadLevel_CUS1 },
3444 { "CUS2", -1, LoadLevel_CUS2 },
3445 { "CUS3", -1, LoadLevel_CUS3 },
3446 { "CUS4", -1, LoadLevel_CUS4 },
3447 { "GRP1", -1, LoadLevel_GRP1 },
3448 { "CONF", -1, LoadLevel_CONF },
3449 { "ELEM", -1, LoadLevel_ELEM },
3450 { "NOTE", -1, LoadLevel_NOTE },
3451 { "CUSX", -1, LoadLevel_CUSX },
3452 { "GRPX", -1, LoadLevel_GRPX },
3457 while (getFileChunkBE(file, chunk_name, &chunk_size))
3461 while (chunk_info[i].name != NULL &&
3462 !strEqual(chunk_name, chunk_info[i].name))
3465 if (chunk_info[i].name == NULL)
3467 Error(ERR_WARN, "unknown chunk '%s' in level file '%s'",
3468 chunk_name, filename);
3469 ReadUnusedBytesFromFile(file, chunk_size);
3471 else if (chunk_info[i].size != -1 &&
3472 chunk_info[i].size != chunk_size)
3474 Error(ERR_WARN, "wrong size (%d) of chunk '%s' in level file '%s'",
3475 chunk_size, chunk_name, filename);
3476 ReadUnusedBytesFromFile(file, chunk_size);
3480 // call function to load this level chunk
3481 int chunk_size_expected =
3482 (chunk_info[i].loader)(file, chunk_size, level);
3484 // the size of some chunks cannot be checked before reading other
3485 // chunks first (like "HEAD" and "BODY") that contain some header
3486 // information, so check them here
3487 if (chunk_size_expected != chunk_size)
3489 Error(ERR_WARN, "wrong size (%d) of chunk '%s' in level file '%s'",
3490 chunk_size, chunk_name, filename);
3500 // ----------------------------------------------------------------------------
3501 // functions for loading EM level
3502 // ----------------------------------------------------------------------------
3504 static void CopyNativeLevel_RND_to_EM(struct LevelInfo *level)
3506 static int ball_xy[8][2] =
3517 struct LevelInfo_EM *level_em = level->native_em_level;
3518 struct LEVEL *lev = level_em->lev;
3519 struct PLAYER **ply = level_em->ply;
3522 lev->width = MIN(level->fieldx, MAX_PLAYFIELD_WIDTH);
3523 lev->height = MIN(level->fieldy, MAX_PLAYFIELD_HEIGHT);
3525 lev->time_seconds = level->time;
3526 lev->required_initial = level->gems_needed;
3528 lev->emerald_score = level->score[SC_EMERALD];
3529 lev->diamond_score = level->score[SC_DIAMOND];
3530 lev->alien_score = level->score[SC_ROBOT];
3531 lev->tank_score = level->score[SC_SPACESHIP];
3532 lev->bug_score = level->score[SC_BUG];
3533 lev->eater_score = level->score[SC_YAMYAM];
3534 lev->nut_score = level->score[SC_NUT];
3535 lev->dynamite_score = level->score[SC_DYNAMITE];
3536 lev->key_score = level->score[SC_KEY];
3537 lev->exit_score = level->score[SC_TIME_BONUS];
3539 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
3540 for (y = 0; y < 3; y++)
3541 for (x = 0; x < 3; x++)
3542 lev->eater_array[i][y * 3 + x] =
3543 map_element_RND_to_EM(level->yamyam_content[i].e[x][y]);
3545 lev->amoeba_time = level->amoeba_speed;
3546 lev->wonderwall_time_initial = level->time_magic_wall;
3547 lev->wheel_time = level->time_wheel;
3549 lev->android_move_time = level->android_move_time;
3550 lev->android_clone_time = level->android_clone_time;
3551 lev->ball_random = level->ball_random;
3552 lev->ball_state_initial = level->ball_state_initial;
3553 lev->ball_time = level->ball_time;
3554 lev->num_ball_arrays = level->num_ball_contents;
3556 lev->lenses_score = level->lenses_score;
3557 lev->magnify_score = level->magnify_score;
3558 lev->slurp_score = level->slurp_score;
3560 lev->lenses_time = level->lenses_time;
3561 lev->magnify_time = level->magnify_time;
3563 lev->wind_direction_initial =
3564 map_direction_RND_to_EM(level->wind_direction_initial);
3565 lev->wind_cnt_initial = (level->wind_direction_initial != MV_NONE ?
3566 lev->wind_time : 0);
3568 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
3569 for (j = 0; j < 8; j++)
3570 lev->ball_array[i][j] =
3571 map_element_RND_to_EM(level->
3572 ball_content[i].e[ball_xy[j][0]][ball_xy[j][1]]);
3574 map_android_clone_elements_RND_to_EM(level);
3576 // first fill the complete playfield with the default border element
3577 for (y = 0; y < EM_MAX_CAVE_HEIGHT; y++)
3578 for (x = 0; x < EM_MAX_CAVE_WIDTH; x++)
3579 level_em->cave[x][y] = Zborder;
3581 // then copy the real level contents from level file into the playfield
3582 for (y = 0; y < lev->height; y++) for (x = 0; x < lev->width; x++)
3584 int new_element = map_element_RND_to_EM(level->field[x][y]);
3586 if (level->field[x][y] == EL_AMOEBA_DEAD)
3587 new_element = map_element_RND_to_EM(EL_AMOEBA_WET);
3589 level_em->cave[x][y] = new_element;
3592 for (i = 0; i < MAX_PLAYERS; i++)
3594 ply[i]->x_initial = -1;
3595 ply[i]->y_initial = -1;
3598 // initialize player positions and delete players from the playfield
3599 for (y = 0; y < lev->height; y++) for (x = 0; x < lev->width; x++)
3601 if (ELEM_IS_PLAYER(level->field[x][y]))
3603 int player_nr = GET_PLAYER_NR(level->field[x][y]);
3605 ply[player_nr]->x_initial = x;
3606 ply[player_nr]->y_initial = y;
3608 level_em->cave[x][y] = map_element_RND_to_EM(EL_EMPTY);
3613 static void CopyNativeLevel_EM_to_RND(struct LevelInfo *level)
3615 static int ball_xy[8][2] =
3626 struct LevelInfo_EM *level_em = level->native_em_level;
3627 struct LEVEL *lev = level_em->lev;
3628 struct PLAYER **ply = level_em->ply;
3631 level->fieldx = MIN(lev->width, MAX_LEV_FIELDX);
3632 level->fieldy = MIN(lev->height, MAX_LEV_FIELDY);
3634 level->time = lev->time_seconds;
3635 level->gems_needed = lev->required_initial;
3637 sprintf(level->name, "Level %d", level->file_info.nr);
3639 level->score[SC_EMERALD] = lev->emerald_score;
3640 level->score[SC_DIAMOND] = lev->diamond_score;
3641 level->score[SC_ROBOT] = lev->alien_score;
3642 level->score[SC_SPACESHIP] = lev->tank_score;
3643 level->score[SC_BUG] = lev->bug_score;
3644 level->score[SC_YAMYAM] = lev->eater_score;
3645 level->score[SC_NUT] = lev->nut_score;
3646 level->score[SC_DYNAMITE] = lev->dynamite_score;
3647 level->score[SC_KEY] = lev->key_score;
3648 level->score[SC_TIME_BONUS] = lev->exit_score;
3650 level->num_yamyam_contents = MAX_ELEMENT_CONTENTS;
3652 for (i = 0; i < level->num_yamyam_contents; i++)
3653 for (y = 0; y < 3; y++)
3654 for (x = 0; x < 3; x++)
3655 level->yamyam_content[i].e[x][y] =
3656 map_element_EM_to_RND(lev->eater_array[i][y * 3 + x]);
3658 level->amoeba_speed = lev->amoeba_time;
3659 level->time_magic_wall = lev->wonderwall_time_initial;
3660 level->time_wheel = lev->wheel_time;
3662 level->android_move_time = lev->android_move_time;
3663 level->android_clone_time = lev->android_clone_time;
3664 level->ball_random = lev->ball_random;
3665 level->ball_state_initial = lev->ball_state_initial;
3666 level->ball_time = lev->ball_time;
3667 level->num_ball_contents = lev->num_ball_arrays;
3669 level->lenses_score = lev->lenses_score;
3670 level->magnify_score = lev->magnify_score;
3671 level->slurp_score = lev->slurp_score;
3673 level->lenses_time = lev->lenses_time;
3674 level->magnify_time = lev->magnify_time;
3676 level->wind_direction_initial =
3677 map_direction_EM_to_RND(lev->wind_direction_initial);
3679 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
3680 for (j = 0; j < 8; j++)
3681 level->ball_content[i].e[ball_xy[j][0]][ball_xy[j][1]] =
3682 map_element_EM_to_RND(lev->ball_array[i][j]);
3684 map_android_clone_elements_EM_to_RND(level);
3686 // convert the playfield (some elements need special treatment)
3687 for (y = 0; y < level->fieldy; y++) for (x = 0; x < level->fieldx; x++)
3689 int new_element = map_element_EM_to_RND(level_em->cave[x][y]);
3691 if (new_element == EL_AMOEBA_WET && level->amoeba_speed == 0)
3692 new_element = EL_AMOEBA_DEAD;
3694 level->field[x][y] = new_element;
3697 for (i = 0; i < MAX_PLAYERS; i++)
3699 // in case of all players set to the same field, use the first player
3700 int nr = MAX_PLAYERS - i - 1;
3701 int jx = ply[nr]->x_initial;
3702 int jy = ply[nr]->y_initial;
3704 if (jx != -1 && jy != -1)
3705 level->field[jx][jy] = EL_PLAYER_1 + nr;
3710 // ----------------------------------------------------------------------------
3711 // functions for loading SP level
3712 // ----------------------------------------------------------------------------
3714 static void CopyNativeLevel_RND_to_SP(struct LevelInfo *level)
3716 struct LevelInfo_SP *level_sp = level->native_sp_level;
3717 LevelInfoType *header = &level_sp->header;
3720 level_sp->width = level->fieldx;
3721 level_sp->height = level->fieldy;
3723 for (x = 0; x < level->fieldx; x++)
3724 for (y = 0; y < level->fieldy; y++)
3725 level_sp->playfield[x][y] = map_element_RND_to_SP(level->field[x][y]);
3727 header->InitialGravity = (level->initial_player_gravity[0] ? 1 : 0);
3729 for (i = 0; i < SP_LEVEL_NAME_LEN; i++)
3730 header->LevelTitle[i] = level->name[i];
3731 // !!! NO STRING TERMINATION IN SUPAPLEX VB CODE YET -- FIX THIS !!!
3733 header->InfotronsNeeded = level->gems_needed;
3735 header->SpecialPortCount = 0;
3737 for (x = 0; x < level->fieldx; x++) for (y = 0; y < level->fieldy; y++)
3739 boolean gravity_port_found = FALSE;
3740 boolean gravity_port_valid = FALSE;
3741 int gravity_port_flag;
3742 int gravity_port_base_element;
3743 int element = level->field[x][y];
3745 if (element >= EL_SP_GRAVITY_ON_PORT_RIGHT &&
3746 element <= EL_SP_GRAVITY_ON_PORT_UP)
3748 gravity_port_found = TRUE;
3749 gravity_port_valid = TRUE;
3750 gravity_port_flag = 1;
3751 gravity_port_base_element = EL_SP_GRAVITY_ON_PORT_RIGHT;
3753 else if (element >= EL_SP_GRAVITY_OFF_PORT_RIGHT &&
3754 element <= EL_SP_GRAVITY_OFF_PORT_UP)
3756 gravity_port_found = TRUE;
3757 gravity_port_valid = TRUE;
3758 gravity_port_flag = 0;
3759 gravity_port_base_element = EL_SP_GRAVITY_OFF_PORT_RIGHT;
3761 else if (element >= EL_SP_GRAVITY_PORT_RIGHT &&
3762 element <= EL_SP_GRAVITY_PORT_UP)
3764 // change R'n'D style gravity inverting special port to normal port
3765 // (there are no gravity inverting ports in native Supaplex engine)
3767 gravity_port_found = TRUE;
3768 gravity_port_valid = FALSE;
3769 gravity_port_base_element = EL_SP_GRAVITY_PORT_RIGHT;
3772 if (gravity_port_found)
3774 if (gravity_port_valid &&
3775 header->SpecialPortCount < SP_MAX_SPECIAL_PORTS)
3777 SpecialPortType *port = &header->SpecialPort[header->SpecialPortCount];
3779 port->PortLocation = (y * level->fieldx + x) * 2;
3780 port->Gravity = gravity_port_flag;
3782 element += EL_SP_GRAVITY_PORT_RIGHT - gravity_port_base_element;
3784 header->SpecialPortCount++;
3788 // change special gravity port to normal port
3790 element += EL_SP_PORT_RIGHT - gravity_port_base_element;
3793 level_sp->playfield[x][y] = element - EL_SP_START;
3798 static void CopyNativeLevel_SP_to_RND(struct LevelInfo *level)
3800 struct LevelInfo_SP *level_sp = level->native_sp_level;
3801 LevelInfoType *header = &level_sp->header;
3802 boolean num_invalid_elements = 0;
3805 level->fieldx = level_sp->width;
3806 level->fieldy = level_sp->height;
3808 for (x = 0; x < level->fieldx; x++)
3810 for (y = 0; y < level->fieldy; y++)
3812 int element_old = level_sp->playfield[x][y];
3813 int element_new = getMappedElement(map_element_SP_to_RND(element_old));
3815 if (element_new == EL_UNKNOWN)
3817 num_invalid_elements++;
3819 Error(ERR_DEBUG, "invalid element %d at position %d, %d",
3823 level->field[x][y] = element_new;
3827 if (num_invalid_elements > 0)
3828 Error(ERR_WARN, "found %d invalid elements%s", num_invalid_elements,
3829 (!options.debug ? " (use '--debug' for more details)" : ""));
3831 for (i = 0; i < MAX_PLAYERS; i++)
3832 level->initial_player_gravity[i] =
3833 (header->InitialGravity == 1 ? TRUE : FALSE);
3835 // skip leading spaces
3836 for (i = 0; i < SP_LEVEL_NAME_LEN; i++)
3837 if (header->LevelTitle[i] != ' ')
3841 for (j = 0; i < SP_LEVEL_NAME_LEN; i++, j++)
3842 level->name[j] = header->LevelTitle[i];
3843 level->name[j] = '\0';
3845 // cut trailing spaces
3847 if (level->name[j - 1] == ' ' && level->name[j] == '\0')
3848 level->name[j - 1] = '\0';
3850 level->gems_needed = header->InfotronsNeeded;
3852 for (i = 0; i < header->SpecialPortCount; i++)
3854 SpecialPortType *port = &header->SpecialPort[i];
3855 int port_location = port->PortLocation;
3856 int gravity = port->Gravity;
3857 int port_x, port_y, port_element;
3859 port_x = (port_location / 2) % level->fieldx;
3860 port_y = (port_location / 2) / level->fieldx;
3862 if (port_x < 0 || port_x >= level->fieldx ||
3863 port_y < 0 || port_y >= level->fieldy)
3865 Error(ERR_WARN, "special port position (%d, %d) out of bounds",
3871 port_element = level->field[port_x][port_y];
3873 if (port_element < EL_SP_GRAVITY_PORT_RIGHT ||
3874 port_element > EL_SP_GRAVITY_PORT_UP)
3876 Error(ERR_WARN, "no special port at position (%d, %d)", port_x, port_y);
3881 // change previous (wrong) gravity inverting special port to either
3882 // gravity enabling special port or gravity disabling special port
3883 level->field[port_x][port_y] +=
3884 (gravity == 1 ? EL_SP_GRAVITY_ON_PORT_RIGHT :
3885 EL_SP_GRAVITY_OFF_PORT_RIGHT) - EL_SP_GRAVITY_PORT_RIGHT;
3888 // change special gravity ports without database entries to normal ports
3889 for (x = 0; x < level->fieldx; x++)
3890 for (y = 0; y < level->fieldy; y++)
3891 if (level->field[x][y] >= EL_SP_GRAVITY_PORT_RIGHT &&
3892 level->field[x][y] <= EL_SP_GRAVITY_PORT_UP)
3893 level->field[x][y] += EL_SP_PORT_RIGHT - EL_SP_GRAVITY_PORT_RIGHT;
3895 level->time = 0; // no time limit
3896 level->amoeba_speed = 0;
3897 level->time_magic_wall = 0;
3898 level->time_wheel = 0;
3899 level->amoeba_content = EL_EMPTY;
3902 // original Supaplex does not use score values -- use default values
3904 for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
3905 level->score[i] = 0;
3908 // there are no yamyams in supaplex levels
3909 for (i = 0; i < level->num_yamyam_contents; i++)
3910 for (x = 0; x < 3; x++)
3911 for (y = 0; y < 3; y++)
3912 level->yamyam_content[i].e[x][y] = EL_EMPTY;
3915 static void CopyNativeTape_RND_to_SP(struct LevelInfo *level)
3917 struct LevelInfo_SP *level_sp = level->native_sp_level;
3918 struct DemoInfo_SP *demo = &level_sp->demo;
3921 // always start with reliable default values
3922 demo->is_available = FALSE;
3925 if (TAPE_IS_EMPTY(tape))
3928 demo->level_nr = tape.level_nr; // (currently not used)
3930 level_sp->header.DemoRandomSeed = tape.random_seed;
3934 for (i = 0; i < tape.length; i++)
3936 int demo_action = map_key_RND_to_SP(tape.pos[i].action[0]);
3937 int demo_repeat = tape.pos[i].delay;
3938 int demo_entries = (demo_repeat + 15) / 16;
3940 if (demo->length + demo_entries >= SP_MAX_TAPE_LEN)
3942 Error(ERR_WARN, "tape truncated: size exceeds maximum SP demo size %d",
3948 for (j = 0; j < demo_repeat / 16; j++)
3949 demo->data[demo->length++] = 0xf0 | demo_action;
3951 if (demo_repeat % 16)
3952 demo->data[demo->length++] = ((demo_repeat % 16 - 1) << 4) | demo_action;
3955 demo->is_available = TRUE;
3958 static void setTapeInfoToDefaults(void);
3960 static void CopyNativeTape_SP_to_RND(struct LevelInfo *level)
3962 struct LevelInfo_SP *level_sp = level->native_sp_level;
3963 struct DemoInfo_SP *demo = &level_sp->demo;
3964 char *filename = level->file_info.filename;
3967 // always start with reliable default values
3968 setTapeInfoToDefaults();
3970 if (!demo->is_available)
3973 tape.level_nr = demo->level_nr; // (currently not used)
3974 tape.random_seed = level_sp->header.DemoRandomSeed;
3976 TapeSetDateFromEpochSeconds(getFileTimestampEpochSeconds(filename));
3979 tape.pos[tape.counter].delay = 0;
3981 for (i = 0; i < demo->length; i++)
3983 int demo_action = demo->data[i] & 0x0f;
3984 int demo_repeat = (demo->data[i] & 0xf0) >> 4;
3985 int tape_action = map_key_SP_to_RND(demo_action);
3986 int tape_repeat = demo_repeat + 1;
3987 byte action[MAX_PLAYERS] = { tape_action, 0, 0, 0 };
3988 boolean success = 0;
3991 for (j = 0; j < tape_repeat; j++)
3992 success = TapeAddAction(action);
3996 Error(ERR_WARN, "SP demo truncated: size exceeds maximum tape size %d",
4003 TapeHaltRecording();
4007 // ----------------------------------------------------------------------------
4008 // functions for loading MM level
4009 // ----------------------------------------------------------------------------
4011 static void CopyNativeLevel_RND_to_MM(struct LevelInfo *level)
4013 struct LevelInfo_MM *level_mm = level->native_mm_level;
4016 level_mm->fieldx = MIN(level->fieldx, MM_MAX_PLAYFIELD_WIDTH);
4017 level_mm->fieldy = MIN(level->fieldy, MM_MAX_PLAYFIELD_HEIGHT);
4019 level_mm->time = level->time;
4020 level_mm->kettles_needed = level->gems_needed;
4021 level_mm->auto_count_kettles = level->auto_count_gems;
4023 level_mm->laser_red = level->mm_laser_red;
4024 level_mm->laser_green = level->mm_laser_green;
4025 level_mm->laser_blue = level->mm_laser_blue;
4027 strcpy(level_mm->name, level->name);
4028 strcpy(level_mm->author, level->author);
4030 level_mm->score[SC_EMERALD] = level->score[SC_EMERALD];
4031 level_mm->score[SC_PACMAN] = level->score[SC_PACMAN];
4032 level_mm->score[SC_KEY] = level->score[SC_KEY];
4033 level_mm->score[SC_TIME_BONUS] = level->score[SC_TIME_BONUS];
4034 level_mm->score[SC_ELEM_BONUS] = level->score[SC_ELEM_BONUS];
4036 level_mm->amoeba_speed = level->amoeba_speed;
4037 level_mm->time_fuse = level->mm_time_fuse;
4038 level_mm->time_bomb = level->mm_time_bomb;
4039 level_mm->time_ball = level->mm_time_ball;
4040 level_mm->time_block = level->mm_time_block;
4042 for (x = 0; x < level->fieldx; x++)
4043 for (y = 0; y < level->fieldy; y++)
4045 level_mm->field[x][y] = map_element_RND_to_MM(level->field[x][y]);
4048 static void CopyNativeLevel_MM_to_RND(struct LevelInfo *level)
4050 struct LevelInfo_MM *level_mm = level->native_mm_level;
4053 level->fieldx = MIN(level_mm->fieldx, MAX_LEV_FIELDX);
4054 level->fieldy = MIN(level_mm->fieldy, MAX_LEV_FIELDY);
4056 level->time = level_mm->time;
4057 level->gems_needed = level_mm->kettles_needed;
4058 level->auto_count_gems = level_mm->auto_count_kettles;
4060 level->mm_laser_red = level_mm->laser_red;
4061 level->mm_laser_green = level_mm->laser_green;
4062 level->mm_laser_blue = level_mm->laser_blue;
4064 strcpy(level->name, level_mm->name);
4066 // only overwrite author from 'levelinfo.conf' if author defined in level
4067 if (!strEqual(level_mm->author, ANONYMOUS_NAME))
4068 strcpy(level->author, level_mm->author);
4070 level->score[SC_EMERALD] = level_mm->score[SC_EMERALD];
4071 level->score[SC_PACMAN] = level_mm->score[SC_PACMAN];
4072 level->score[SC_KEY] = level_mm->score[SC_KEY];
4073 level->score[SC_TIME_BONUS] = level_mm->score[SC_TIME_BONUS];
4074 level->score[SC_ELEM_BONUS] = level_mm->score[SC_ELEM_BONUS];
4076 level->amoeba_speed = level_mm->amoeba_speed;
4077 level->mm_time_fuse = level_mm->time_fuse;
4078 level->mm_time_bomb = level_mm->time_bomb;
4079 level->mm_time_ball = level_mm->time_ball;
4080 level->mm_time_block = level_mm->time_block;
4082 for (x = 0; x < level->fieldx; x++)
4083 for (y = 0; y < level->fieldy; y++)
4084 level->field[x][y] = map_element_MM_to_RND(level_mm->field[x][y]);
4088 // ----------------------------------------------------------------------------
4089 // functions for loading DC level
4090 // ----------------------------------------------------------------------------
4092 #define DC_LEVEL_HEADER_SIZE 344
4094 static unsigned short getDecodedWord_DC(unsigned short data_encoded,
4097 static int last_data_encoded;
4101 int diff_hi, diff_lo;
4102 int data_hi, data_lo;
4103 unsigned short data_decoded;
4107 last_data_encoded = 0;
4114 diff = data_encoded - last_data_encoded;
4115 diff_hi = diff & ~0xff;
4116 diff_lo = diff & 0xff;
4120 data_hi = diff_hi - (offset1 << 8) + (offset2 & 0xff00);
4121 data_lo = (diff_lo + (data_hi >> 16)) & 0x00ff;
4122 data_hi = data_hi & 0xff00;
4124 data_decoded = data_hi | data_lo;
4126 last_data_encoded = data_encoded;
4128 offset1 = (offset1 + 1) % 31;
4129 offset2 = offset2 & 0xff;
4131 return data_decoded;
4134 static int getMappedElement_DC(int element)
4142 // 0x0117 - 0x036e: (?)
4145 // 0x042d - 0x0684: (?)
4161 element = EL_CRYSTAL;
4164 case 0x0e77: // quicksand (boulder)
4165 element = EL_QUICKSAND_FAST_FULL;
4168 case 0x0e99: // slow quicksand (boulder)
4169 element = EL_QUICKSAND_FULL;
4173 element = EL_EM_EXIT_OPEN;
4177 element = EL_EM_EXIT_CLOSED;
4181 element = EL_EM_STEEL_EXIT_OPEN;
4185 element = EL_EM_STEEL_EXIT_CLOSED;
4188 case 0x0f4f: // dynamite (lit 1)
4189 element = EL_EM_DYNAMITE_ACTIVE;
4192 case 0x0f57: // dynamite (lit 2)
4193 element = EL_EM_DYNAMITE_ACTIVE;
4196 case 0x0f5f: // dynamite (lit 3)
4197 element = EL_EM_DYNAMITE_ACTIVE;
4200 case 0x0f67: // dynamite (lit 4)
4201 element = EL_EM_DYNAMITE_ACTIVE;
4208 element = EL_AMOEBA_WET;
4212 element = EL_AMOEBA_DROP;
4216 element = EL_DC_MAGIC_WALL;
4220 element = EL_SPACESHIP_UP;
4224 element = EL_SPACESHIP_DOWN;
4228 element = EL_SPACESHIP_LEFT;
4232 element = EL_SPACESHIP_RIGHT;
4236 element = EL_BUG_UP;
4240 element = EL_BUG_DOWN;
4244 element = EL_BUG_LEFT;
4248 element = EL_BUG_RIGHT;
4252 element = EL_MOLE_UP;
4256 element = EL_MOLE_DOWN;
4260 element = EL_MOLE_LEFT;
4264 element = EL_MOLE_RIGHT;
4272 element = EL_YAMYAM;
4276 element = EL_SWITCHGATE_OPEN;
4280 element = EL_SWITCHGATE_CLOSED;
4284 element = EL_DC_SWITCHGATE_SWITCH_UP;
4288 element = EL_TIMEGATE_CLOSED;
4291 case 0x144c: // conveyor belt switch (green)
4292 element = EL_CONVEYOR_BELT_3_SWITCH_MIDDLE;
4295 case 0x144f: // conveyor belt switch (red)
4296 element = EL_CONVEYOR_BELT_1_SWITCH_MIDDLE;
4299 case 0x1452: // conveyor belt switch (blue)
4300 element = EL_CONVEYOR_BELT_4_SWITCH_MIDDLE;
4304 element = EL_CONVEYOR_BELT_3_MIDDLE;
4308 element = EL_CONVEYOR_BELT_3_LEFT;
4312 element = EL_CONVEYOR_BELT_3_RIGHT;
4316 element = EL_CONVEYOR_BELT_1_MIDDLE;
4320 element = EL_CONVEYOR_BELT_1_LEFT;
4324 element = EL_CONVEYOR_BELT_1_RIGHT;
4328 element = EL_CONVEYOR_BELT_4_MIDDLE;
4332 element = EL_CONVEYOR_BELT_4_LEFT;
4336 element = EL_CONVEYOR_BELT_4_RIGHT;
4340 element = EL_EXPANDABLE_WALL_HORIZONTAL;
4344 element = EL_EXPANDABLE_WALL_VERTICAL;
4348 element = EL_EXPANDABLE_WALL_ANY;
4351 case 0x14ce: // growing steel wall (left/right)
4352 element = EL_EXPANDABLE_STEELWALL_HORIZONTAL;
4355 case 0x14df: // growing steel wall (up/down)
4356 element = EL_EXPANDABLE_STEELWALL_VERTICAL;
4359 case 0x14e8: // growing steel wall (up/down/left/right)
4360 element = EL_EXPANDABLE_STEELWALL_ANY;
4364 element = EL_SHIELD_DEADLY;
4368 element = EL_EXTRA_TIME;
4376 element = EL_EMPTY_SPACE;
4379 case 0x1578: // quicksand (empty)
4380 element = EL_QUICKSAND_FAST_EMPTY;
4383 case 0x1579: // slow quicksand (empty)
4384 element = EL_QUICKSAND_EMPTY;
4394 element = EL_EM_DYNAMITE;
4397 case 0x15a1: // key (red)
4398 element = EL_EM_KEY_1;
4401 case 0x15a2: // key (yellow)
4402 element = EL_EM_KEY_2;
4405 case 0x15a3: // key (blue)
4406 element = EL_EM_KEY_4;
4409 case 0x15a4: // key (green)
4410 element = EL_EM_KEY_3;
4413 case 0x15a5: // key (white)
4414 element = EL_DC_KEY_WHITE;
4418 element = EL_WALL_SLIPPERY;
4425 case 0x15a8: // wall (not round)
4429 case 0x15a9: // (blue)
4430 element = EL_CHAR_A;
4433 case 0x15aa: // (blue)
4434 element = EL_CHAR_B;
4437 case 0x15ab: // (blue)
4438 element = EL_CHAR_C;
4441 case 0x15ac: // (blue)
4442 element = EL_CHAR_D;
4445 case 0x15ad: // (blue)
4446 element = EL_CHAR_E;
4449 case 0x15ae: // (blue)
4450 element = EL_CHAR_F;
4453 case 0x15af: // (blue)
4454 element = EL_CHAR_G;
4457 case 0x15b0: // (blue)
4458 element = EL_CHAR_H;
4461 case 0x15b1: // (blue)
4462 element = EL_CHAR_I;
4465 case 0x15b2: // (blue)
4466 element = EL_CHAR_J;
4469 case 0x15b3: // (blue)
4470 element = EL_CHAR_K;
4473 case 0x15b4: // (blue)
4474 element = EL_CHAR_L;
4477 case 0x15b5: // (blue)
4478 element = EL_CHAR_M;
4481 case 0x15b6: // (blue)
4482 element = EL_CHAR_N;
4485 case 0x15b7: // (blue)
4486 element = EL_CHAR_O;
4489 case 0x15b8: // (blue)
4490 element = EL_CHAR_P;
4493 case 0x15b9: // (blue)
4494 element = EL_CHAR_Q;
4497 case 0x15ba: // (blue)
4498 element = EL_CHAR_R;
4501 case 0x15bb: // (blue)
4502 element = EL_CHAR_S;
4505 case 0x15bc: // (blue)
4506 element = EL_CHAR_T;
4509 case 0x15bd: // (blue)
4510 element = EL_CHAR_U;
4513 case 0x15be: // (blue)
4514 element = EL_CHAR_V;
4517 case 0x15bf: // (blue)
4518 element = EL_CHAR_W;
4521 case 0x15c0: // (blue)
4522 element = EL_CHAR_X;
4525 case 0x15c1: // (blue)
4526 element = EL_CHAR_Y;
4529 case 0x15c2: // (blue)
4530 element = EL_CHAR_Z;
4533 case 0x15c3: // (blue)
4534 element = EL_CHAR_AUMLAUT;
4537 case 0x15c4: // (blue)
4538 element = EL_CHAR_OUMLAUT;
4541 case 0x15c5: // (blue)
4542 element = EL_CHAR_UUMLAUT;
4545 case 0x15c6: // (blue)
4546 element = EL_CHAR_0;
4549 case 0x15c7: // (blue)
4550 element = EL_CHAR_1;
4553 case 0x15c8: // (blue)
4554 element = EL_CHAR_2;
4557 case 0x15c9: // (blue)
4558 element = EL_CHAR_3;
4561 case 0x15ca: // (blue)
4562 element = EL_CHAR_4;
4565 case 0x15cb: // (blue)
4566 element = EL_CHAR_5;
4569 case 0x15cc: // (blue)
4570 element = EL_CHAR_6;
4573 case 0x15cd: // (blue)
4574 element = EL_CHAR_7;
4577 case 0x15ce: // (blue)
4578 element = EL_CHAR_8;
4581 case 0x15cf: // (blue)
4582 element = EL_CHAR_9;
4585 case 0x15d0: // (blue)
4586 element = EL_CHAR_PERIOD;
4589 case 0x15d1: // (blue)
4590 element = EL_CHAR_EXCLAM;
4593 case 0x15d2: // (blue)
4594 element = EL_CHAR_COLON;
4597 case 0x15d3: // (blue)
4598 element = EL_CHAR_LESS;
4601 case 0x15d4: // (blue)
4602 element = EL_CHAR_GREATER;
4605 case 0x15d5: // (blue)
4606 element = EL_CHAR_QUESTION;
4609 case 0x15d6: // (blue)
4610 element = EL_CHAR_COPYRIGHT;
4613 case 0x15d7: // (blue)
4614 element = EL_CHAR_UP;
4617 case 0x15d8: // (blue)
4618 element = EL_CHAR_DOWN;
4621 case 0x15d9: // (blue)
4622 element = EL_CHAR_BUTTON;
4625 case 0x15da: // (blue)
4626 element = EL_CHAR_PLUS;
4629 case 0x15db: // (blue)
4630 element = EL_CHAR_MINUS;
4633 case 0x15dc: // (blue)
4634 element = EL_CHAR_APOSTROPHE;
4637 case 0x15dd: // (blue)
4638 element = EL_CHAR_PARENLEFT;
4641 case 0x15de: // (blue)
4642 element = EL_CHAR_PARENRIGHT;
4645 case 0x15df: // (green)
4646 element = EL_CHAR_A;
4649 case 0x15e0: // (green)
4650 element = EL_CHAR_B;
4653 case 0x15e1: // (green)
4654 element = EL_CHAR_C;
4657 case 0x15e2: // (green)
4658 element = EL_CHAR_D;
4661 case 0x15e3: // (green)
4662 element = EL_CHAR_E;
4665 case 0x15e4: // (green)
4666 element = EL_CHAR_F;
4669 case 0x15e5: // (green)
4670 element = EL_CHAR_G;
4673 case 0x15e6: // (green)
4674 element = EL_CHAR_H;
4677 case 0x15e7: // (green)
4678 element = EL_CHAR_I;
4681 case 0x15e8: // (green)
4682 element = EL_CHAR_J;
4685 case 0x15e9: // (green)
4686 element = EL_CHAR_K;
4689 case 0x15ea: // (green)
4690 element = EL_CHAR_L;
4693 case 0x15eb: // (green)
4694 element = EL_CHAR_M;
4697 case 0x15ec: // (green)
4698 element = EL_CHAR_N;
4701 case 0x15ed: // (green)
4702 element = EL_CHAR_O;
4705 case 0x15ee: // (green)
4706 element = EL_CHAR_P;
4709 case 0x15ef: // (green)
4710 element = EL_CHAR_Q;
4713 case 0x15f0: // (green)
4714 element = EL_CHAR_R;
4717 case 0x15f1: // (green)
4718 element = EL_CHAR_S;
4721 case 0x15f2: // (green)
4722 element = EL_CHAR_T;
4725 case 0x15f3: // (green)
4726 element = EL_CHAR_U;
4729 case 0x15f4: // (green)
4730 element = EL_CHAR_V;
4733 case 0x15f5: // (green)
4734 element = EL_CHAR_W;
4737 case 0x15f6: // (green)
4738 element = EL_CHAR_X;
4741 case 0x15f7: // (green)
4742 element = EL_CHAR_Y;
4745 case 0x15f8: // (green)
4746 element = EL_CHAR_Z;
4749 case 0x15f9: // (green)
4750 element = EL_CHAR_AUMLAUT;
4753 case 0x15fa: // (green)
4754 element = EL_CHAR_OUMLAUT;
4757 case 0x15fb: // (green)
4758 element = EL_CHAR_UUMLAUT;
4761 case 0x15fc: // (green)
4762 element = EL_CHAR_0;
4765 case 0x15fd: // (green)
4766 element = EL_CHAR_1;
4769 case 0x15fe: // (green)
4770 element = EL_CHAR_2;
4773 case 0x15ff: // (green)
4774 element = EL_CHAR_3;
4777 case 0x1600: // (green)
4778 element = EL_CHAR_4;
4781 case 0x1601: // (green)
4782 element = EL_CHAR_5;
4785 case 0x1602: // (green)
4786 element = EL_CHAR_6;
4789 case 0x1603: // (green)
4790 element = EL_CHAR_7;
4793 case 0x1604: // (green)
4794 element = EL_CHAR_8;
4797 case 0x1605: // (green)
4798 element = EL_CHAR_9;
4801 case 0x1606: // (green)
4802 element = EL_CHAR_PERIOD;
4805 case 0x1607: // (green)
4806 element = EL_CHAR_EXCLAM;
4809 case 0x1608: // (green)
4810 element = EL_CHAR_COLON;
4813 case 0x1609: // (green)
4814 element = EL_CHAR_LESS;
4817 case 0x160a: // (green)
4818 element = EL_CHAR_GREATER;
4821 case 0x160b: // (green)
4822 element = EL_CHAR_QUESTION;
4825 case 0x160c: // (green)
4826 element = EL_CHAR_COPYRIGHT;
4829 case 0x160d: // (green)
4830 element = EL_CHAR_UP;
4833 case 0x160e: // (green)
4834 element = EL_CHAR_DOWN;
4837 case 0x160f: // (green)
4838 element = EL_CHAR_BUTTON;
4841 case 0x1610: // (green)
4842 element = EL_CHAR_PLUS;
4845 case 0x1611: // (green)
4846 element = EL_CHAR_MINUS;
4849 case 0x1612: // (green)
4850 element = EL_CHAR_APOSTROPHE;
4853 case 0x1613: // (green)
4854 element = EL_CHAR_PARENLEFT;
4857 case 0x1614: // (green)
4858 element = EL_CHAR_PARENRIGHT;
4861 case 0x1615: // (blue steel)
4862 element = EL_STEEL_CHAR_A;
4865 case 0x1616: // (blue steel)
4866 element = EL_STEEL_CHAR_B;
4869 case 0x1617: // (blue steel)
4870 element = EL_STEEL_CHAR_C;
4873 case 0x1618: // (blue steel)
4874 element = EL_STEEL_CHAR_D;
4877 case 0x1619: // (blue steel)
4878 element = EL_STEEL_CHAR_E;
4881 case 0x161a: // (blue steel)
4882 element = EL_STEEL_CHAR_F;
4885 case 0x161b: // (blue steel)
4886 element = EL_STEEL_CHAR_G;
4889 case 0x161c: // (blue steel)
4890 element = EL_STEEL_CHAR_H;
4893 case 0x161d: // (blue steel)
4894 element = EL_STEEL_CHAR_I;
4897 case 0x161e: // (blue steel)
4898 element = EL_STEEL_CHAR_J;
4901 case 0x161f: // (blue steel)
4902 element = EL_STEEL_CHAR_K;
4905 case 0x1620: // (blue steel)
4906 element = EL_STEEL_CHAR_L;
4909 case 0x1621: // (blue steel)
4910 element = EL_STEEL_CHAR_M;
4913 case 0x1622: // (blue steel)
4914 element = EL_STEEL_CHAR_N;
4917 case 0x1623: // (blue steel)
4918 element = EL_STEEL_CHAR_O;
4921 case 0x1624: // (blue steel)
4922 element = EL_STEEL_CHAR_P;
4925 case 0x1625: // (blue steel)
4926 element = EL_STEEL_CHAR_Q;
4929 case 0x1626: // (blue steel)
4930 element = EL_STEEL_CHAR_R;
4933 case 0x1627: // (blue steel)
4934 element = EL_STEEL_CHAR_S;
4937 case 0x1628: // (blue steel)
4938 element = EL_STEEL_CHAR_T;
4941 case 0x1629: // (blue steel)
4942 element = EL_STEEL_CHAR_U;
4945 case 0x162a: // (blue steel)
4946 element = EL_STEEL_CHAR_V;
4949 case 0x162b: // (blue steel)
4950 element = EL_STEEL_CHAR_W;
4953 case 0x162c: // (blue steel)
4954 element = EL_STEEL_CHAR_X;
4957 case 0x162d: // (blue steel)
4958 element = EL_STEEL_CHAR_Y;
4961 case 0x162e: // (blue steel)
4962 element = EL_STEEL_CHAR_Z;
4965 case 0x162f: // (blue steel)
4966 element = EL_STEEL_CHAR_AUMLAUT;
4969 case 0x1630: // (blue steel)
4970 element = EL_STEEL_CHAR_OUMLAUT;
4973 case 0x1631: // (blue steel)
4974 element = EL_STEEL_CHAR_UUMLAUT;
4977 case 0x1632: // (blue steel)
4978 element = EL_STEEL_CHAR_0;
4981 case 0x1633: // (blue steel)
4982 element = EL_STEEL_CHAR_1;
4985 case 0x1634: // (blue steel)
4986 element = EL_STEEL_CHAR_2;
4989 case 0x1635: // (blue steel)
4990 element = EL_STEEL_CHAR_3;
4993 case 0x1636: // (blue steel)
4994 element = EL_STEEL_CHAR_4;
4997 case 0x1637: // (blue steel)
4998 element = EL_STEEL_CHAR_5;
5001 case 0x1638: // (blue steel)
5002 element = EL_STEEL_CHAR_6;
5005 case 0x1639: // (blue steel)
5006 element = EL_STEEL_CHAR_7;
5009 case 0x163a: // (blue steel)
5010 element = EL_STEEL_CHAR_8;
5013 case 0x163b: // (blue steel)
5014 element = EL_STEEL_CHAR_9;
5017 case 0x163c: // (blue steel)
5018 element = EL_STEEL_CHAR_PERIOD;
5021 case 0x163d: // (blue steel)
5022 element = EL_STEEL_CHAR_EXCLAM;
5025 case 0x163e: // (blue steel)
5026 element = EL_STEEL_CHAR_COLON;
5029 case 0x163f: // (blue steel)
5030 element = EL_STEEL_CHAR_LESS;
5033 case 0x1640: // (blue steel)
5034 element = EL_STEEL_CHAR_GREATER;
5037 case 0x1641: // (blue steel)
5038 element = EL_STEEL_CHAR_QUESTION;
5041 case 0x1642: // (blue steel)
5042 element = EL_STEEL_CHAR_COPYRIGHT;
5045 case 0x1643: // (blue steel)
5046 element = EL_STEEL_CHAR_UP;
5049 case 0x1644: // (blue steel)
5050 element = EL_STEEL_CHAR_DOWN;
5053 case 0x1645: // (blue steel)
5054 element = EL_STEEL_CHAR_BUTTON;
5057 case 0x1646: // (blue steel)
5058 element = EL_STEEL_CHAR_PLUS;
5061 case 0x1647: // (blue steel)
5062 element = EL_STEEL_CHAR_MINUS;
5065 case 0x1648: // (blue steel)
5066 element = EL_STEEL_CHAR_APOSTROPHE;
5069 case 0x1649: // (blue steel)
5070 element = EL_STEEL_CHAR_PARENLEFT;
5073 case 0x164a: // (blue steel)
5074 element = EL_STEEL_CHAR_PARENRIGHT;
5077 case 0x164b: // (green steel)
5078 element = EL_STEEL_CHAR_A;
5081 case 0x164c: // (green steel)
5082 element = EL_STEEL_CHAR_B;
5085 case 0x164d: // (green steel)
5086 element = EL_STEEL_CHAR_C;
5089 case 0x164e: // (green steel)
5090 element = EL_STEEL_CHAR_D;
5093 case 0x164f: // (green steel)
5094 element = EL_STEEL_CHAR_E;
5097 case 0x1650: // (green steel)
5098 element = EL_STEEL_CHAR_F;
5101 case 0x1651: // (green steel)
5102 element = EL_STEEL_CHAR_G;
5105 case 0x1652: // (green steel)
5106 element = EL_STEEL_CHAR_H;
5109 case 0x1653: // (green steel)
5110 element = EL_STEEL_CHAR_I;
5113 case 0x1654: // (green steel)
5114 element = EL_STEEL_CHAR_J;
5117 case 0x1655: // (green steel)
5118 element = EL_STEEL_CHAR_K;
5121 case 0x1656: // (green steel)
5122 element = EL_STEEL_CHAR_L;
5125 case 0x1657: // (green steel)
5126 element = EL_STEEL_CHAR_M;
5129 case 0x1658: // (green steel)
5130 element = EL_STEEL_CHAR_N;
5133 case 0x1659: // (green steel)
5134 element = EL_STEEL_CHAR_O;
5137 case 0x165a: // (green steel)
5138 element = EL_STEEL_CHAR_P;
5141 case 0x165b: // (green steel)
5142 element = EL_STEEL_CHAR_Q;
5145 case 0x165c: // (green steel)
5146 element = EL_STEEL_CHAR_R;
5149 case 0x165d: // (green steel)
5150 element = EL_STEEL_CHAR_S;
5153 case 0x165e: // (green steel)
5154 element = EL_STEEL_CHAR_T;
5157 case 0x165f: // (green steel)
5158 element = EL_STEEL_CHAR_U;
5161 case 0x1660: // (green steel)
5162 element = EL_STEEL_CHAR_V;
5165 case 0x1661: // (green steel)
5166 element = EL_STEEL_CHAR_W;
5169 case 0x1662: // (green steel)
5170 element = EL_STEEL_CHAR_X;
5173 case 0x1663: // (green steel)
5174 element = EL_STEEL_CHAR_Y;
5177 case 0x1664: // (green steel)
5178 element = EL_STEEL_CHAR_Z;
5181 case 0x1665: // (green steel)
5182 element = EL_STEEL_CHAR_AUMLAUT;
5185 case 0x1666: // (green steel)
5186 element = EL_STEEL_CHAR_OUMLAUT;
5189 case 0x1667: // (green steel)
5190 element = EL_STEEL_CHAR_UUMLAUT;
5193 case 0x1668: // (green steel)
5194 element = EL_STEEL_CHAR_0;
5197 case 0x1669: // (green steel)
5198 element = EL_STEEL_CHAR_1;
5201 case 0x166a: // (green steel)
5202 element = EL_STEEL_CHAR_2;
5205 case 0x166b: // (green steel)
5206 element = EL_STEEL_CHAR_3;
5209 case 0x166c: // (green steel)
5210 element = EL_STEEL_CHAR_4;
5213 case 0x166d: // (green steel)
5214 element = EL_STEEL_CHAR_5;
5217 case 0x166e: // (green steel)
5218 element = EL_STEEL_CHAR_6;
5221 case 0x166f: // (green steel)
5222 element = EL_STEEL_CHAR_7;
5225 case 0x1670: // (green steel)
5226 element = EL_STEEL_CHAR_8;
5229 case 0x1671: // (green steel)
5230 element = EL_STEEL_CHAR_9;
5233 case 0x1672: // (green steel)
5234 element = EL_STEEL_CHAR_PERIOD;
5237 case 0x1673: // (green steel)
5238 element = EL_STEEL_CHAR_EXCLAM;
5241 case 0x1674: // (green steel)
5242 element = EL_STEEL_CHAR_COLON;
5245 case 0x1675: // (green steel)
5246 element = EL_STEEL_CHAR_LESS;
5249 case 0x1676: // (green steel)
5250 element = EL_STEEL_CHAR_GREATER;
5253 case 0x1677: // (green steel)
5254 element = EL_STEEL_CHAR_QUESTION;
5257 case 0x1678: // (green steel)
5258 element = EL_STEEL_CHAR_COPYRIGHT;
5261 case 0x1679: // (green steel)
5262 element = EL_STEEL_CHAR_UP;
5265 case 0x167a: // (green steel)
5266 element = EL_STEEL_CHAR_DOWN;
5269 case 0x167b: // (green steel)
5270 element = EL_STEEL_CHAR_BUTTON;
5273 case 0x167c: // (green steel)
5274 element = EL_STEEL_CHAR_PLUS;
5277 case 0x167d: // (green steel)
5278 element = EL_STEEL_CHAR_MINUS;
5281 case 0x167e: // (green steel)
5282 element = EL_STEEL_CHAR_APOSTROPHE;
5285 case 0x167f: // (green steel)
5286 element = EL_STEEL_CHAR_PARENLEFT;
5289 case 0x1680: // (green steel)
5290 element = EL_STEEL_CHAR_PARENRIGHT;
5293 case 0x1681: // gate (red)
5294 element = EL_EM_GATE_1;
5297 case 0x1682: // secret gate (red)
5298 element = EL_GATE_1_GRAY;
5301 case 0x1683: // gate (yellow)
5302 element = EL_EM_GATE_2;
5305 case 0x1684: // secret gate (yellow)
5306 element = EL_GATE_2_GRAY;
5309 case 0x1685: // gate (blue)
5310 element = EL_EM_GATE_4;
5313 case 0x1686: // secret gate (blue)
5314 element = EL_GATE_4_GRAY;
5317 case 0x1687: // gate (green)
5318 element = EL_EM_GATE_3;
5321 case 0x1688: // secret gate (green)
5322 element = EL_GATE_3_GRAY;
5325 case 0x1689: // gate (white)
5326 element = EL_DC_GATE_WHITE;
5329 case 0x168a: // secret gate (white)
5330 element = EL_DC_GATE_WHITE_GRAY;
5333 case 0x168b: // secret gate (no key)
5334 element = EL_DC_GATE_FAKE_GRAY;
5338 element = EL_ROBOT_WHEEL;
5342 element = EL_DC_TIMEGATE_SWITCH;
5346 element = EL_ACID_POOL_BOTTOM;
5350 element = EL_ACID_POOL_TOPLEFT;
5354 element = EL_ACID_POOL_TOPRIGHT;
5358 element = EL_ACID_POOL_BOTTOMLEFT;
5362 element = EL_ACID_POOL_BOTTOMRIGHT;
5366 element = EL_STEELWALL;
5370 element = EL_STEELWALL_SLIPPERY;
5373 case 0x1695: // steel wall (not round)
5374 element = EL_STEELWALL;
5377 case 0x1696: // steel wall (left)
5378 element = EL_DC_STEELWALL_1_LEFT;
5381 case 0x1697: // steel wall (bottom)
5382 element = EL_DC_STEELWALL_1_BOTTOM;
5385 case 0x1698: // steel wall (right)
5386 element = EL_DC_STEELWALL_1_RIGHT;
5389 case 0x1699: // steel wall (top)
5390 element = EL_DC_STEELWALL_1_TOP;
5393 case 0x169a: // steel wall (left/bottom)
5394 element = EL_DC_STEELWALL_1_BOTTOMLEFT;
5397 case 0x169b: // steel wall (right/bottom)
5398 element = EL_DC_STEELWALL_1_BOTTOMRIGHT;
5401 case 0x169c: // steel wall (right/top)
5402 element = EL_DC_STEELWALL_1_TOPRIGHT;
5405 case 0x169d: // steel wall (left/top)
5406 element = EL_DC_STEELWALL_1_TOPLEFT;
5409 case 0x169e: // steel wall (right/bottom small)
5410 element = EL_DC_STEELWALL_1_BOTTOMRIGHT_2;
5413 case 0x169f: // steel wall (left/bottom small)
5414 element = EL_DC_STEELWALL_1_BOTTOMLEFT_2;
5417 case 0x16a0: // steel wall (right/top small)
5418 element = EL_DC_STEELWALL_1_TOPRIGHT_2;
5421 case 0x16a1: // steel wall (left/top small)
5422 element = EL_DC_STEELWALL_1_TOPLEFT_2;
5425 case 0x16a2: // steel wall (left/right)
5426 element = EL_DC_STEELWALL_1_VERTICAL;
5429 case 0x16a3: // steel wall (top/bottom)
5430 element = EL_DC_STEELWALL_1_HORIZONTAL;
5433 case 0x16a4: // steel wall 2 (left end)
5434 element = EL_DC_STEELWALL_2_LEFT;
5437 case 0x16a5: // steel wall 2 (right end)
5438 element = EL_DC_STEELWALL_2_RIGHT;
5441 case 0x16a6: // steel wall 2 (top end)
5442 element = EL_DC_STEELWALL_2_TOP;
5445 case 0x16a7: // steel wall 2 (bottom end)
5446 element = EL_DC_STEELWALL_2_BOTTOM;
5449 case 0x16a8: // steel wall 2 (left/right)
5450 element = EL_DC_STEELWALL_2_HORIZONTAL;
5453 case 0x16a9: // steel wall 2 (up/down)
5454 element = EL_DC_STEELWALL_2_VERTICAL;
5457 case 0x16aa: // steel wall 2 (mid)
5458 element = EL_DC_STEELWALL_2_MIDDLE;
5462 element = EL_SIGN_EXCLAMATION;
5466 element = EL_SIGN_RADIOACTIVITY;
5470 element = EL_SIGN_STOP;
5474 element = EL_SIGN_WHEELCHAIR;
5478 element = EL_SIGN_PARKING;
5482 element = EL_SIGN_NO_ENTRY;
5486 element = EL_SIGN_HEART;
5490 element = EL_SIGN_GIVE_WAY;
5494 element = EL_SIGN_ENTRY_FORBIDDEN;
5498 element = EL_SIGN_EMERGENCY_EXIT;
5502 element = EL_SIGN_YIN_YANG;
5506 element = EL_WALL_EMERALD;
5510 element = EL_WALL_DIAMOND;
5514 element = EL_WALL_PEARL;
5518 element = EL_WALL_CRYSTAL;
5522 element = EL_INVISIBLE_WALL;
5526 element = EL_INVISIBLE_STEELWALL;
5530 // EL_INVISIBLE_SAND
5533 element = EL_LIGHT_SWITCH;
5537 element = EL_ENVELOPE_1;
5541 if (element >= 0x0117 && element <= 0x036e) // (?)
5542 element = EL_DIAMOND;
5543 else if (element >= 0x042d && element <= 0x0684) // (?)
5544 element = EL_EMERALD;
5545 else if (element >= 0x157c && element <= 0x158b)
5547 else if (element >= 0x1590 && element <= 0x159f)
5548 element = EL_DC_LANDMINE;
5549 else if (element >= 0x16bc && element <= 0x16cb)
5550 element = EL_INVISIBLE_SAND;
5553 Error(ERR_WARN, "unknown Diamond Caves element 0x%04x", element);
5554 element = EL_UNKNOWN;
5559 return getMappedElement(element);
5562 static void LoadLevelFromFileStream_DC(File *file, struct LevelInfo *level,
5565 byte header[DC_LEVEL_HEADER_SIZE];
5567 int envelope_header_pos = 62;
5568 int envelope_content_pos = 94;
5569 int level_name_pos = 251;
5570 int level_author_pos = 292;
5571 int envelope_header_len;
5572 int envelope_content_len;
5574 int level_author_len;
5576 int num_yamyam_contents;
5579 getDecodedWord_DC(0, TRUE); // initialize DC2 decoding engine
5581 for (i = 0; i < DC_LEVEL_HEADER_SIZE / 2; i++)
5583 unsigned short header_word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
5585 header[i * 2 + 0] = header_word >> 8;
5586 header[i * 2 + 1] = header_word & 0xff;
5589 // read some values from level header to check level decoding integrity
5590 fieldx = header[6] | (header[7] << 8);
5591 fieldy = header[8] | (header[9] << 8);
5592 num_yamyam_contents = header[60] | (header[61] << 8);
5594 // do some simple sanity checks to ensure that level was correctly decoded
5595 if (fieldx < 1 || fieldx > 256 ||
5596 fieldy < 1 || fieldy > 256 ||
5597 num_yamyam_contents < 1 || num_yamyam_contents > 8)
5599 level->no_valid_file = TRUE;
5601 Error(ERR_WARN, "cannot decode level from stream -- using empty level");
5606 // maximum envelope header size is 31 bytes
5607 envelope_header_len = header[envelope_header_pos];
5608 // maximum envelope content size is 110 (156?) bytes
5609 envelope_content_len = header[envelope_content_pos];
5611 // maximum level title size is 40 bytes
5612 level_name_len = MIN(header[level_name_pos], MAX_LEVEL_NAME_LEN);
5613 // maximum level author size is 30 (51?) bytes
5614 level_author_len = MIN(header[level_author_pos], MAX_LEVEL_AUTHOR_LEN);
5618 for (i = 0; i < envelope_header_len; i++)
5619 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
5620 level->envelope[0].text[envelope_size++] =
5621 header[envelope_header_pos + 1 + i];
5623 if (envelope_header_len > 0 && envelope_content_len > 0)
5625 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
5626 level->envelope[0].text[envelope_size++] = '\n';
5627 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
5628 level->envelope[0].text[envelope_size++] = '\n';
5631 for (i = 0; i < envelope_content_len; i++)
5632 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
5633 level->envelope[0].text[envelope_size++] =
5634 header[envelope_content_pos + 1 + i];
5636 level->envelope[0].text[envelope_size] = '\0';
5638 level->envelope[0].xsize = MAX_ENVELOPE_XSIZE;
5639 level->envelope[0].ysize = 10;
5640 level->envelope[0].autowrap = TRUE;
5641 level->envelope[0].centered = TRUE;
5643 for (i = 0; i < level_name_len; i++)
5644 level->name[i] = header[level_name_pos + 1 + i];
5645 level->name[level_name_len] = '\0';
5647 for (i = 0; i < level_author_len; i++)
5648 level->author[i] = header[level_author_pos + 1 + i];
5649 level->author[level_author_len] = '\0';
5651 num_yamyam_contents = header[60] | (header[61] << 8);
5652 level->num_yamyam_contents =
5653 MIN(MAX(MIN_ELEMENT_CONTENTS, num_yamyam_contents), MAX_ELEMENT_CONTENTS);
5655 for (i = 0; i < num_yamyam_contents; i++)
5657 for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
5659 unsigned short word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
5660 int element_dc = ((word & 0xff) << 8) | ((word >> 8) & 0xff);
5662 if (i < MAX_ELEMENT_CONTENTS)
5663 level->yamyam_content[i].e[x][y] = getMappedElement_DC(element_dc);
5667 fieldx = header[6] | (header[7] << 8);
5668 fieldy = header[8] | (header[9] << 8);
5669 level->fieldx = MIN(MAX(MIN_LEV_FIELDX, fieldx), MAX_LEV_FIELDX);
5670 level->fieldy = MIN(MAX(MIN_LEV_FIELDY, fieldy), MAX_LEV_FIELDY);
5672 for (y = 0; y < fieldy; y++) for (x = 0; x < fieldx; x++)
5674 unsigned short word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
5675 int element_dc = ((word & 0xff) << 8) | ((word >> 8) & 0xff);
5677 if (x < MAX_LEV_FIELDX && y < MAX_LEV_FIELDY)
5678 level->field[x][y] = getMappedElement_DC(element_dc);
5681 x = MIN(MAX(0, (header[10] | (header[11] << 8)) - 1), MAX_LEV_FIELDX - 1);
5682 y = MIN(MAX(0, (header[12] | (header[13] << 8)) - 1), MAX_LEV_FIELDY - 1);
5683 level->field[x][y] = EL_PLAYER_1;
5685 x = MIN(MAX(0, (header[14] | (header[15] << 8)) - 1), MAX_LEV_FIELDX - 1);
5686 y = MIN(MAX(0, (header[16] | (header[17] << 8)) - 1), MAX_LEV_FIELDY - 1);
5687 level->field[x][y] = EL_PLAYER_2;
5689 level->gems_needed = header[18] | (header[19] << 8);
5691 level->score[SC_EMERALD] = header[20] | (header[21] << 8);
5692 level->score[SC_DIAMOND] = header[22] | (header[23] << 8);
5693 level->score[SC_PEARL] = header[24] | (header[25] << 8);
5694 level->score[SC_CRYSTAL] = header[26] | (header[27] << 8);
5695 level->score[SC_NUT] = header[28] | (header[29] << 8);
5696 level->score[SC_ROBOT] = header[30] | (header[31] << 8);
5697 level->score[SC_SPACESHIP] = header[32] | (header[33] << 8);
5698 level->score[SC_BUG] = header[34] | (header[35] << 8);
5699 level->score[SC_YAMYAM] = header[36] | (header[37] << 8);
5700 level->score[SC_DYNAMITE] = header[38] | (header[39] << 8);
5701 level->score[SC_KEY] = header[40] | (header[41] << 8);
5702 level->score[SC_TIME_BONUS] = header[42] | (header[43] << 8);
5704 level->time = header[44] | (header[45] << 8);
5706 level->amoeba_speed = header[46] | (header[47] << 8);
5707 level->time_light = header[48] | (header[49] << 8);
5708 level->time_timegate = header[50] | (header[51] << 8);
5709 level->time_wheel = header[52] | (header[53] << 8);
5710 level->time_magic_wall = header[54] | (header[55] << 8);
5711 level->extra_time = header[56] | (header[57] << 8);
5712 level->shield_normal_time = header[58] | (header[59] << 8);
5714 // Diamond Caves has the same (strange) behaviour as Emerald Mine that gems
5715 // can slip down from flat walls, like normal walls and steel walls
5716 level->em_slippery_gems = TRUE;
5719 static void LoadLevelFromFileInfo_DC(struct LevelInfo *level,
5720 struct LevelFileInfo *level_file_info,
5721 boolean level_info_only)
5723 char *filename = level_file_info->filename;
5725 int num_magic_bytes = 8;
5726 char magic_bytes[num_magic_bytes + 1];
5727 int num_levels_to_skip = level_file_info->nr - leveldir_current->first_level;
5729 if (!(file = openFile(filename, MODE_READ)))
5731 level->no_valid_file = TRUE;
5733 if (!level_info_only)
5734 Error(ERR_WARN, "cannot read level '%s' -- using empty level", filename);
5739 // fseek(file, 0x0000, SEEK_SET);
5741 if (level_file_info->packed)
5743 // read "magic bytes" from start of file
5744 if (getStringFromFile(file, magic_bytes, num_magic_bytes + 1) == NULL)
5745 magic_bytes[0] = '\0';
5747 // check "magic bytes" for correct file format
5748 if (!strPrefix(magic_bytes, "DC2"))
5750 level->no_valid_file = TRUE;
5752 Error(ERR_WARN, "unknown DC level file '%s' -- using empty level",
5758 if (strPrefix(magic_bytes, "DC2Win95") ||
5759 strPrefix(magic_bytes, "DC2Win98"))
5761 int position_first_level = 0x00fa;
5762 int extra_bytes = 4;
5765 // advance file stream to first level inside the level package
5766 skip_bytes = position_first_level - num_magic_bytes - extra_bytes;
5768 // each block of level data is followed by block of non-level data
5769 num_levels_to_skip *= 2;
5771 // at least skip header bytes, therefore use ">= 0" instead of "> 0"
5772 while (num_levels_to_skip >= 0)
5774 // advance file stream to next level inside the level package
5775 if (seekFile(file, skip_bytes, SEEK_CUR) != 0)
5777 level->no_valid_file = TRUE;
5779 Error(ERR_WARN, "cannot fseek in file '%s' -- using empty level",
5785 // skip apparently unused extra bytes following each level
5786 ReadUnusedBytesFromFile(file, extra_bytes);
5788 // read size of next level in level package
5789 skip_bytes = getFile32BitLE(file);
5791 num_levels_to_skip--;
5796 level->no_valid_file = TRUE;
5798 Error(ERR_WARN, "unknown DC2 level file '%s' -- using empty level",
5805 LoadLevelFromFileStream_DC(file, level, level_file_info->nr);
5811 // ----------------------------------------------------------------------------
5812 // functions for loading SB level
5813 // ----------------------------------------------------------------------------
5815 int getMappedElement_SB(int element_ascii, boolean use_ces)
5823 sb_element_mapping[] =
5825 { ' ', EL_EMPTY, EL_CUSTOM_1 }, // floor (space)
5826 { '#', EL_STEELWALL, EL_CUSTOM_2 }, // wall
5827 { '@', EL_PLAYER_1, EL_CUSTOM_3 }, // player
5828 { '$', EL_SOKOBAN_OBJECT, EL_CUSTOM_4 }, // box
5829 { '.', EL_SOKOBAN_FIELD_EMPTY, EL_CUSTOM_5 }, // goal square
5830 { '*', EL_SOKOBAN_FIELD_FULL, EL_CUSTOM_6 }, // box on goal square
5831 { '+', EL_SOKOBAN_FIELD_PLAYER, EL_CUSTOM_7 }, // player on goal square
5832 { '_', EL_INVISIBLE_STEELWALL, EL_FROM_LEVEL_TEMPLATE }, // floor beyond border
5839 for (i = 0; sb_element_mapping[i].ascii != 0; i++)
5840 if (element_ascii == sb_element_mapping[i].ascii)
5841 return (use_ces ? sb_element_mapping[i].ce : sb_element_mapping[i].sb);
5843 return EL_UNDEFINED;
5846 static void LoadLevelFromFileInfo_SB(struct LevelInfo *level,
5847 struct LevelFileInfo *level_file_info,
5848 boolean level_info_only)
5850 char *filename = level_file_info->filename;
5851 char line[MAX_LINE_LEN], line_raw[MAX_LINE_LEN], previous_line[MAX_LINE_LEN];
5852 char last_comment[MAX_LINE_LEN];
5853 char level_name[MAX_LINE_LEN];
5856 int num_levels_to_skip = level_file_info->nr - leveldir_current->first_level;
5857 boolean read_continued_line = FALSE;
5858 boolean reading_playfield = FALSE;
5859 boolean got_valid_playfield_line = FALSE;
5860 boolean invalid_playfield_char = FALSE;
5861 boolean load_xsb_to_ces = check_special_flags("load_xsb_to_ces");
5862 int file_level_nr = 0;
5864 int x = 0, y = 0; // initialized to make compilers happy
5866 last_comment[0] = '\0';
5867 level_name[0] = '\0';
5869 if (!(file = openFile(filename, MODE_READ)))
5871 level->no_valid_file = TRUE;
5873 if (!level_info_only)
5874 Error(ERR_WARN, "cannot read level '%s' -- using empty level", filename);
5879 while (!checkEndOfFile(file))
5881 // level successfully read, but next level may follow here
5882 if (!got_valid_playfield_line && reading_playfield)
5884 // read playfield from single level file -- skip remaining file
5885 if (!level_file_info->packed)
5888 if (file_level_nr >= num_levels_to_skip)
5893 last_comment[0] = '\0';
5894 level_name[0] = '\0';
5896 reading_playfield = FALSE;
5899 got_valid_playfield_line = FALSE;
5901 // read next line of input file
5902 if (!getStringFromFile(file, line, MAX_LINE_LEN))
5905 // check if line was completely read and is terminated by line break
5906 if (strlen(line) > 0 && line[strlen(line) - 1] == '\n')
5909 // cut trailing line break (this can be newline and/or carriage return)
5910 for (line_ptr = &line[strlen(line)]; line_ptr >= line; line_ptr--)
5911 if ((*line_ptr == '\n' || *line_ptr == '\r') && *(line_ptr + 1) == '\0')
5914 // copy raw input line for later use (mainly debugging output)
5915 strcpy(line_raw, line);
5917 if (read_continued_line)
5919 // append new line to existing line, if there is enough space
5920 if (strlen(previous_line) + strlen(line_ptr) < MAX_LINE_LEN)
5921 strcat(previous_line, line_ptr);
5923 strcpy(line, previous_line); // copy storage buffer to line
5925 read_continued_line = FALSE;
5928 // if the last character is '\', continue at next line
5929 if (strlen(line) > 0 && line[strlen(line) - 1] == '\\')
5931 line[strlen(line) - 1] = '\0'; // cut off trailing backslash
5932 strcpy(previous_line, line); // copy line to storage buffer
5934 read_continued_line = TRUE;
5940 if (line[0] == '\0')
5943 // extract comment text from comment line
5946 for (line_ptr = line; *line_ptr; line_ptr++)
5947 if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != ';')
5950 strcpy(last_comment, line_ptr);
5955 // extract level title text from line containing level title
5956 if (line[0] == '\'')
5958 strcpy(level_name, &line[1]);
5960 if (strlen(level_name) > 0 && level_name[strlen(level_name) - 1] == '\'')
5961 level_name[strlen(level_name) - 1] = '\0';
5966 // skip lines containing only spaces (or empty lines)
5967 for (line_ptr = line; *line_ptr; line_ptr++)
5968 if (*line_ptr != ' ')
5970 if (*line_ptr == '\0')
5973 // at this point, we have found a line containing part of a playfield
5975 got_valid_playfield_line = TRUE;
5977 if (!reading_playfield)
5979 reading_playfield = TRUE;
5980 invalid_playfield_char = FALSE;
5982 for (x = 0; x < MAX_LEV_FIELDX; x++)
5983 for (y = 0; y < MAX_LEV_FIELDY; y++)
5984 level->field[x][y] = getMappedElement_SB(' ', load_xsb_to_ces);
5989 // start with topmost tile row
5993 // skip playfield line if larger row than allowed
5994 if (y >= MAX_LEV_FIELDY)
5997 // start with leftmost tile column
6000 // read playfield elements from line
6001 for (line_ptr = line; *line_ptr; line_ptr++)
6003 int mapped_sb_element = getMappedElement_SB(*line_ptr, load_xsb_to_ces);
6005 // stop parsing playfield line if larger column than allowed
6006 if (x >= MAX_LEV_FIELDX)
6009 if (mapped_sb_element == EL_UNDEFINED)
6011 invalid_playfield_char = TRUE;
6016 level->field[x][y] = mapped_sb_element;
6018 // continue with next tile column
6021 level->fieldx = MAX(x, level->fieldx);
6024 if (invalid_playfield_char)
6026 // if first playfield line, treat invalid lines as comment lines
6028 reading_playfield = FALSE;
6033 // continue with next tile row
6041 level->fieldx = MIN(MAX(MIN_LEV_FIELDX, level->fieldx), MAX_LEV_FIELDX);
6042 level->fieldy = MIN(MAX(MIN_LEV_FIELDY, level->fieldy), MAX_LEV_FIELDY);
6044 if (!reading_playfield)
6046 level->no_valid_file = TRUE;
6048 Error(ERR_WARN, "cannot read level '%s' -- using empty level", filename);
6053 if (*level_name != '\0')
6055 strncpy(level->name, level_name, MAX_LEVEL_NAME_LEN);
6056 level->name[MAX_LEVEL_NAME_LEN] = '\0';
6058 else if (*last_comment != '\0')
6060 strncpy(level->name, last_comment, MAX_LEVEL_NAME_LEN);
6061 level->name[MAX_LEVEL_NAME_LEN] = '\0';
6065 sprintf(level->name, "--> Level %d <--", level_file_info->nr);
6068 // set all empty fields beyond the border walls to invisible steel wall
6069 for (y = 0; y < level->fieldy; y++) for (x = 0; x < level->fieldx; x++)
6071 if ((x == 0 || x == level->fieldx - 1 ||
6072 y == 0 || y == level->fieldy - 1) &&
6073 level->field[x][y] == getMappedElement_SB(' ', load_xsb_to_ces))
6074 FloodFillLevel(x, y, getMappedElement_SB('_', load_xsb_to_ces),
6075 level->field, level->fieldx, level->fieldy);
6078 // set special level settings for Sokoban levels
6081 level->use_step_counter = TRUE;
6083 if (load_xsb_to_ces)
6085 // special global settings can now be set in level template
6087 level->use_custom_template = TRUE;
6092 // -------------------------------------------------------------------------
6093 // functions for handling native levels
6094 // -------------------------------------------------------------------------
6096 static void LoadLevelFromFileInfo_EM(struct LevelInfo *level,
6097 struct LevelFileInfo *level_file_info,
6098 boolean level_info_only)
6100 if (!LoadNativeLevel_EM(level_file_info->filename, level_info_only))
6101 level->no_valid_file = TRUE;
6104 static void LoadLevelFromFileInfo_SP(struct LevelInfo *level,
6105 struct LevelFileInfo *level_file_info,
6106 boolean level_info_only)
6110 // determine position of requested level inside level package
6111 if (level_file_info->packed)
6112 pos = level_file_info->nr - leveldir_current->first_level;
6114 if (!LoadNativeLevel_SP(level_file_info->filename, pos, level_info_only))
6115 level->no_valid_file = TRUE;
6118 static void LoadLevelFromFileInfo_MM(struct LevelInfo *level,
6119 struct LevelFileInfo *level_file_info,
6120 boolean level_info_only)
6122 if (!LoadNativeLevel_MM(level_file_info->filename, level_info_only))
6123 level->no_valid_file = TRUE;
6126 void CopyNativeLevel_RND_to_Native(struct LevelInfo *level)
6128 if (level->game_engine_type == GAME_ENGINE_TYPE_EM)
6129 CopyNativeLevel_RND_to_EM(level);
6130 else if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
6131 CopyNativeLevel_RND_to_SP(level);
6132 else if (level->game_engine_type == GAME_ENGINE_TYPE_MM)
6133 CopyNativeLevel_RND_to_MM(level);
6136 void CopyNativeLevel_Native_to_RND(struct LevelInfo *level)
6138 if (level->game_engine_type == GAME_ENGINE_TYPE_EM)
6139 CopyNativeLevel_EM_to_RND(level);
6140 else if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
6141 CopyNativeLevel_SP_to_RND(level);
6142 else if (level->game_engine_type == GAME_ENGINE_TYPE_MM)
6143 CopyNativeLevel_MM_to_RND(level);
6146 void SaveNativeLevel(struct LevelInfo *level)
6148 if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
6150 char *basename = getSingleLevelBasenameExt(level->file_info.nr, "sp");
6151 char *filename = getLevelFilenameFromBasename(basename);
6153 CopyNativeLevel_RND_to_SP(level);
6154 CopyNativeTape_RND_to_SP(level);
6156 SaveNativeLevel_SP(filename);
6161 // ----------------------------------------------------------------------------
6162 // functions for loading generic level
6163 // ----------------------------------------------------------------------------
6165 static void LoadLevelFromFileInfo(struct LevelInfo *level,
6166 struct LevelFileInfo *level_file_info,
6167 boolean level_info_only)
6169 // always start with reliable default values
6170 setLevelInfoToDefaults(level, level_info_only, TRUE);
6172 switch (level_file_info->type)
6174 case LEVEL_FILE_TYPE_RND:
6175 LoadLevelFromFileInfo_RND(level, level_file_info, level_info_only);
6178 case LEVEL_FILE_TYPE_EM:
6179 LoadLevelFromFileInfo_EM(level, level_file_info, level_info_only);
6180 level->game_engine_type = GAME_ENGINE_TYPE_EM;
6183 case LEVEL_FILE_TYPE_SP:
6184 LoadLevelFromFileInfo_SP(level, level_file_info, level_info_only);
6185 level->game_engine_type = GAME_ENGINE_TYPE_SP;
6188 case LEVEL_FILE_TYPE_MM:
6189 LoadLevelFromFileInfo_MM(level, level_file_info, level_info_only);
6190 level->game_engine_type = GAME_ENGINE_TYPE_MM;
6193 case LEVEL_FILE_TYPE_DC:
6194 LoadLevelFromFileInfo_DC(level, level_file_info, level_info_only);
6197 case LEVEL_FILE_TYPE_SB:
6198 LoadLevelFromFileInfo_SB(level, level_file_info, level_info_only);
6202 LoadLevelFromFileInfo_RND(level, level_file_info, level_info_only);
6206 // if level file is invalid, restore level structure to default values
6207 if (level->no_valid_file)
6208 setLevelInfoToDefaults(level, level_info_only, FALSE);
6210 if (level->game_engine_type == GAME_ENGINE_TYPE_UNKNOWN)
6211 level->game_engine_type = GAME_ENGINE_TYPE_RND;
6213 if (level_file_info->type != LEVEL_FILE_TYPE_RND)
6214 CopyNativeLevel_Native_to_RND(level);
6217 void LoadLevelFromFilename(struct LevelInfo *level, char *filename)
6219 static struct LevelFileInfo level_file_info;
6221 // always start with reliable default values
6222 setFileInfoToDefaults(&level_file_info);
6224 level_file_info.nr = 0; // unknown level number
6225 level_file_info.type = LEVEL_FILE_TYPE_RND; // no others supported yet
6227 setString(&level_file_info.filename, filename);
6229 LoadLevelFromFileInfo(level, &level_file_info, FALSE);
6232 static void LoadLevel_InitVersion(struct LevelInfo *level)
6236 if (leveldir_current == NULL) // only when dumping level
6239 // all engine modifications also valid for levels which use latest engine
6240 if (level->game_version < VERSION_IDENT(3,2,0,5))
6242 // time bonus score was given for 10 s instead of 1 s before 3.2.0-5
6243 level->score[SC_TIME_BONUS] /= 10;
6246 if (leveldir_current->latest_engine)
6248 // ---------- use latest game engine --------------------------------------
6250 /* For all levels which are forced to use the latest game engine version
6251 (normally all but user contributed, private and undefined levels), set
6252 the game engine version to the actual version; this allows for actual
6253 corrections in the game engine to take effect for existing, converted
6254 levels (from "classic" or other existing games) to make the emulation
6255 of the corresponding game more accurate, while (hopefully) not breaking
6256 existing levels created from other players. */
6258 level->game_version = GAME_VERSION_ACTUAL;
6260 /* Set special EM style gems behaviour: EM style gems slip down from
6261 normal, steel and growing wall. As this is a more fundamental change,
6262 it seems better to set the default behaviour to "off" (as it is more
6263 natural) and make it configurable in the level editor (as a property
6264 of gem style elements). Already existing converted levels (neither
6265 private nor contributed levels) are changed to the new behaviour. */
6267 if (level->file_version < FILE_VERSION_2_0)
6268 level->em_slippery_gems = TRUE;
6273 // ---------- use game engine the level was created with --------------------
6275 /* For all levels which are not forced to use the latest game engine
6276 version (normally user contributed, private and undefined levels),
6277 use the version of the game engine the levels were created for.
6279 Since 2.0.1, the game engine version is now directly stored
6280 in the level file (chunk "VERS"), so there is no need anymore
6281 to set the game version from the file version (except for old,
6282 pre-2.0 levels, where the game version is still taken from the
6283 file format version used to store the level -- see above). */
6285 // player was faster than enemies in 1.0.0 and before
6286 if (level->file_version == FILE_VERSION_1_0)
6287 for (i = 0; i < MAX_PLAYERS; i++)
6288 level->initial_player_stepsize[i] = STEPSIZE_FAST;
6290 // default behaviour for EM style gems was "slippery" only in 2.0.1
6291 if (level->game_version == VERSION_IDENT(2,0,1,0))
6292 level->em_slippery_gems = TRUE;
6294 // springs could be pushed over pits before (pre-release version) 2.2.0
6295 if (level->game_version < VERSION_IDENT(2,2,0,0))
6296 level->use_spring_bug = TRUE;
6298 if (level->game_version < VERSION_IDENT(3,2,0,5))
6300 // time orb caused limited time in endless time levels before 3.2.0-5
6301 level->use_time_orb_bug = TRUE;
6303 // default behaviour for snapping was "no snap delay" before 3.2.0-5
6304 level->block_snap_field = FALSE;
6306 // extra time score was same value as time left score before 3.2.0-5
6307 level->extra_time_score = level->score[SC_TIME_BONUS];
6310 if (level->game_version < VERSION_IDENT(3,2,0,7))
6312 // default behaviour for snapping was "not continuous" before 3.2.0-7
6313 level->continuous_snapping = FALSE;
6316 // only few elements were able to actively move into acid before 3.1.0
6317 // trigger settings did not exist before 3.1.0; set to default "any"
6318 if (level->game_version < VERSION_IDENT(3,1,0,0))
6320 // correct "can move into acid" settings (all zero in old levels)
6322 level->can_move_into_acid_bits = 0; // nothing can move into acid
6323 level->dont_collide_with_bits = 0; // nothing is deadly when colliding
6325 setMoveIntoAcidProperty(level, EL_ROBOT, TRUE);
6326 setMoveIntoAcidProperty(level, EL_SATELLITE, TRUE);
6327 setMoveIntoAcidProperty(level, EL_PENGUIN, TRUE);
6328 setMoveIntoAcidProperty(level, EL_BALLOON, TRUE);
6330 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6331 SET_PROPERTY(EL_CUSTOM_START + i, EP_CAN_MOVE_INTO_ACID, TRUE);
6333 // correct trigger settings (stored as zero == "none" in old levels)
6335 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6337 int element = EL_CUSTOM_START + i;
6338 struct ElementInfo *ei = &element_info[element];
6340 for (j = 0; j < ei->num_change_pages; j++)
6342 struct ElementChangeInfo *change = &ei->change_page[j];
6344 change->trigger_player = CH_PLAYER_ANY;
6345 change->trigger_page = CH_PAGE_ANY;
6350 // try to detect and fix "Snake Bite" levels, which are broken with 3.2.0
6352 int element = EL_CUSTOM_256;
6353 struct ElementInfo *ei = &element_info[element];
6354 struct ElementChangeInfo *change = &ei->change_page[0];
6356 /* This is needed to fix a problem that was caused by a bugfix in function
6357 game.c/CreateFieldExt() introduced with 3.2.0 that corrects the behaviour
6358 when a custom element changes to EL_SOKOBAN_FIELD_PLAYER (before, it did
6359 not replace walkable elements, but instead just placed the player on it,
6360 without placing the Sokoban field under the player). Unfortunately, this
6361 breaks "Snake Bite" style levels when the snake is halfway through a door
6362 that just closes (the snake head is still alive and can be moved in this
6363 case). This can be fixed by replacing the EL_SOKOBAN_FIELD_PLAYER by the
6364 player (without Sokoban element) which then gets killed as designed). */
6366 if ((strncmp(leveldir_current->identifier, "snake_bite", 10) == 0 ||
6367 strncmp(ei->description, "pause b4 death", 14) == 0) &&
6368 change->target_element == EL_SOKOBAN_FIELD_PLAYER)
6369 change->target_element = EL_PLAYER_1;
6372 // try to detect and fix "Zelda" style levels, which are broken with 3.2.5
6373 if (level->game_version < VERSION_IDENT(3,2,5,0))
6375 /* This is needed to fix a problem that was caused by a bugfix in function
6376 game.c/CheckTriggeredElementChangeExt() introduced with 3.2.5 that
6377 corrects the behaviour when a custom element changes to another custom
6378 element with a higher element number that has change actions defined.
6379 Normally, only one change per frame is allowed for custom elements.
6380 Therefore, it is checked if a custom element already changed in the
6381 current frame; if it did, subsequent changes are suppressed.
6382 Unfortunately, this is only checked for element changes, but not for
6383 change actions, which are still executed. As the function above loops
6384 through all custom elements from lower to higher, an element change
6385 resulting in a lower CE number won't be checked again, while a target
6386 element with a higher number will also be checked, and potential change
6387 actions will get executed for this CE, too (which is wrong), while
6388 further changes are ignored (which is correct). As this bugfix breaks
6389 Zelda II (and introduces graphical bugs to Zelda I, and also breaks a
6390 few other levels like Alan Bond's "FMV"), allow the previous, incorrect
6391 behaviour for existing levels and tapes that make use of this bug */
6393 level->use_action_after_change_bug = TRUE;
6396 // not centering level after relocating player was default only in 3.2.3
6397 if (level->game_version == VERSION_IDENT(3,2,3,0)) // (no pre-releases)
6398 level->shifted_relocation = TRUE;
6400 // EM style elements always chain-exploded in R'n'D engine before 3.2.6
6401 if (level->game_version < VERSION_IDENT(3,2,6,0))
6402 level->em_explodes_by_fire = TRUE;
6404 // levels were solved by the first player entering an exit up to 4.1.0.0
6405 if (level->game_version <= VERSION_IDENT(4,1,0,0))
6406 level->solved_by_one_player = TRUE;
6408 // game logic of "game of life" and "biomaze" was buggy before 4.1.1.1
6409 if (level->game_version < VERSION_IDENT(4,1,1,1))
6410 level->use_life_bugs = TRUE;
6412 // only Sokoban fields (but not objects) had to be solved before 4.1.1.1
6413 if (level->game_version < VERSION_IDENT(4,1,1,1))
6414 level->sb_objects_needed = FALSE;
6417 static void LoadLevel_InitStandardElements(struct LevelInfo *level)
6421 // map elements that have changed in newer versions
6422 level->amoeba_content = getMappedElementByVersion(level->amoeba_content,
6423 level->game_version);
6424 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
6425 for (x = 0; x < 3; x++)
6426 for (y = 0; y < 3; y++)
6427 level->yamyam_content[i].e[x][y] =
6428 getMappedElementByVersion(level->yamyam_content[i].e[x][y],
6429 level->game_version);
6433 static void LoadLevel_InitCustomElements(struct LevelInfo *level)
6437 // map custom element change events that have changed in newer versions
6438 // (these following values were accidentally changed in version 3.0.1)
6439 // (this seems to be needed only for 'ab_levelset3' and 'ab_levelset4')
6440 if (level->game_version <= VERSION_IDENT(3,0,0,0))
6442 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6444 int element = EL_CUSTOM_START + i;
6446 // order of checking and copying events to be mapped is important
6447 // (do not change the start and end value -- they are constant)
6448 for (j = CE_BY_OTHER_ACTION; j >= CE_VALUE_GETS_ZERO; j--)
6450 if (HAS_CHANGE_EVENT(element, j - 2))
6452 SET_CHANGE_EVENT(element, j - 2, FALSE);
6453 SET_CHANGE_EVENT(element, j, TRUE);
6457 // order of checking and copying events to be mapped is important
6458 // (do not change the start and end value -- they are constant)
6459 for (j = CE_PLAYER_COLLECTS_X; j >= CE_HITTING_SOMETHING; j--)
6461 if (HAS_CHANGE_EVENT(element, j - 1))
6463 SET_CHANGE_EVENT(element, j - 1, FALSE);
6464 SET_CHANGE_EVENT(element, j, TRUE);
6470 // initialize "can_change" field for old levels with only one change page
6471 if (level->game_version <= VERSION_IDENT(3,0,2,0))
6473 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6475 int element = EL_CUSTOM_START + i;
6477 if (CAN_CHANGE(element))
6478 element_info[element].change->can_change = TRUE;
6482 // correct custom element values (for old levels without these options)
6483 if (level->game_version < VERSION_IDENT(3,1,1,0))
6485 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6487 int element = EL_CUSTOM_START + i;
6488 struct ElementInfo *ei = &element_info[element];
6490 if (ei->access_direction == MV_NO_DIRECTION)
6491 ei->access_direction = MV_ALL_DIRECTIONS;
6495 // correct custom element values (fix invalid values for all versions)
6498 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6500 int element = EL_CUSTOM_START + i;
6501 struct ElementInfo *ei = &element_info[element];
6503 for (j = 0; j < ei->num_change_pages; j++)
6505 struct ElementChangeInfo *change = &ei->change_page[j];
6507 if (change->trigger_player == CH_PLAYER_NONE)
6508 change->trigger_player = CH_PLAYER_ANY;
6510 if (change->trigger_side == CH_SIDE_NONE)
6511 change->trigger_side = CH_SIDE_ANY;
6516 // initialize "can_explode" field for old levels which did not store this
6517 // !!! CHECK THIS -- "<= 3,1,0,0" IS PROBABLY WRONG !!!
6518 if (level->game_version <= VERSION_IDENT(3,1,0,0))
6520 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6522 int element = EL_CUSTOM_START + i;
6524 if (EXPLODES_1X1_OLD(element))
6525 element_info[element].explosion_type = EXPLODES_1X1;
6527 SET_PROPERTY(element, EP_CAN_EXPLODE, (EXPLODES_BY_FIRE(element) ||
6528 EXPLODES_SMASHED(element) ||
6529 EXPLODES_IMPACT(element)));
6533 // correct previously hard-coded move delay values for maze runner style
6534 if (level->game_version < VERSION_IDENT(3,1,1,0))
6536 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6538 int element = EL_CUSTOM_START + i;
6540 if (element_info[element].move_pattern & MV_MAZE_RUNNER_STYLE)
6542 // previously hard-coded and therefore ignored
6543 element_info[element].move_delay_fixed = 9;
6544 element_info[element].move_delay_random = 0;
6549 // set some other uninitialized values of custom elements in older levels
6550 if (level->game_version < VERSION_IDENT(3,1,0,0))
6552 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6554 int element = EL_CUSTOM_START + i;
6556 element_info[element].access_direction = MV_ALL_DIRECTIONS;
6558 element_info[element].explosion_delay = 17;
6559 element_info[element].ignition_delay = 8;
6564 static void LoadLevel_InitElements(struct LevelInfo *level)
6566 LoadLevel_InitStandardElements(level);
6568 if (level->file_has_custom_elements)
6569 LoadLevel_InitCustomElements(level);
6571 // initialize element properties for level editor etc.
6572 InitElementPropertiesEngine(level->game_version);
6573 InitElementPropertiesGfxElement();
6576 static void LoadLevel_InitPlayfield(struct LevelInfo *level)
6580 // map elements that have changed in newer versions
6581 for (y = 0; y < level->fieldy; y++)
6582 for (x = 0; x < level->fieldx; x++)
6583 level->field[x][y] = getMappedElementByVersion(level->field[x][y],
6584 level->game_version);
6586 // clear unused playfield data (nicer if level gets resized in editor)
6587 for (x = 0; x < MAX_LEV_FIELDX; x++)
6588 for (y = 0; y < MAX_LEV_FIELDY; y++)
6589 if (x >= level->fieldx || y >= level->fieldy)
6590 level->field[x][y] = EL_EMPTY;
6592 // copy elements to runtime playfield array
6593 for (x = 0; x < MAX_LEV_FIELDX; x++)
6594 for (y = 0; y < MAX_LEV_FIELDY; y++)
6595 Feld[x][y] = level->field[x][y];
6597 // initialize level size variables for faster access
6598 lev_fieldx = level->fieldx;
6599 lev_fieldy = level->fieldy;
6601 // determine border element for this level
6602 if (level->file_info.type == LEVEL_FILE_TYPE_DC)
6603 BorderElement = EL_EMPTY; // (in editor, SetBorderElement() is used)
6608 static void LoadLevel_InitNativeEngines(struct LevelInfo *level)
6610 struct LevelFileInfo *level_file_info = &level->file_info;
6612 if (level_file_info->type == LEVEL_FILE_TYPE_RND)
6613 CopyNativeLevel_RND_to_Native(level);
6616 static void LoadLevelTemplate_LoadAndInit(void)
6618 LoadLevelFromFileInfo(&level_template, &level_template.file_info, FALSE);
6620 LoadLevel_InitVersion(&level_template);
6621 LoadLevel_InitElements(&level_template);
6623 ActivateLevelTemplate();
6626 void LoadLevelTemplate(int nr)
6628 if (!fileExists(getGlobalLevelTemplateFilename()))
6630 Error(ERR_WARN, "no level template found for this level");
6635 setLevelFileInfo(&level_template.file_info, nr);
6637 LoadLevelTemplate_LoadAndInit();
6640 static void LoadNetworkLevelTemplate(struct NetworkLevelInfo *network_level)
6642 copyLevelFileInfo(&network_level->tmpl_info, &level_template.file_info);
6644 LoadLevelTemplate_LoadAndInit();
6647 static void LoadLevel_LoadAndInit(struct NetworkLevelInfo *network_level)
6649 LoadLevelFromFileInfo(&level, &level.file_info, FALSE);
6651 if (level.use_custom_template)
6653 if (network_level != NULL)
6654 LoadNetworkLevelTemplate(network_level);
6656 LoadLevelTemplate(-1);
6659 LoadLevel_InitVersion(&level);
6660 LoadLevel_InitElements(&level);
6661 LoadLevel_InitPlayfield(&level);
6663 LoadLevel_InitNativeEngines(&level);
6666 void LoadLevel(int nr)
6668 SetLevelSetInfo(leveldir_current->identifier, nr);
6670 setLevelFileInfo(&level.file_info, nr);
6672 LoadLevel_LoadAndInit(NULL);
6675 void LoadLevelInfoOnly(int nr)
6677 setLevelFileInfo(&level.file_info, nr);
6679 LoadLevelFromFileInfo(&level, &level.file_info, TRUE);
6682 void LoadNetworkLevel(struct NetworkLevelInfo *network_level)
6684 SetLevelSetInfo(network_level->leveldir_identifier,
6685 network_level->file_info.nr);
6687 copyLevelFileInfo(&network_level->file_info, &level.file_info);
6689 LoadLevel_LoadAndInit(network_level);
6692 static int SaveLevel_VERS(FILE *file, struct LevelInfo *level)
6696 chunk_size += putFileVersion(file, level->file_version);
6697 chunk_size += putFileVersion(file, level->game_version);
6702 static int SaveLevel_DATE(FILE *file, struct LevelInfo *level)
6706 chunk_size += putFile16BitBE(file, level->creation_date.year);
6707 chunk_size += putFile8Bit(file, level->creation_date.month);
6708 chunk_size += putFile8Bit(file, level->creation_date.day);
6713 #if ENABLE_HISTORIC_CHUNKS
6714 static void SaveLevel_HEAD(FILE *file, struct LevelInfo *level)
6718 putFile8Bit(file, level->fieldx);
6719 putFile8Bit(file, level->fieldy);
6721 putFile16BitBE(file, level->time);
6722 putFile16BitBE(file, level->gems_needed);
6724 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
6725 putFile8Bit(file, level->name[i]);
6727 for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
6728 putFile8Bit(file, level->score[i]);
6730 for (i = 0; i < STD_ELEMENT_CONTENTS; i++)
6731 for (y = 0; y < 3; y++)
6732 for (x = 0; x < 3; x++)
6733 putFile8Bit(file, (level->encoding_16bit_yamyam ? EL_EMPTY :
6734 level->yamyam_content[i].e[x][y]));
6735 putFile8Bit(file, level->amoeba_speed);
6736 putFile8Bit(file, level->time_magic_wall);
6737 putFile8Bit(file, level->time_wheel);
6738 putFile8Bit(file, (level->encoding_16bit_amoeba ? EL_EMPTY :
6739 level->amoeba_content));
6740 putFile8Bit(file, (level->initial_player_stepsize == STEPSIZE_FAST ? 1 : 0));
6741 putFile8Bit(file, (level->initial_gravity ? 1 : 0));
6742 putFile8Bit(file, (level->encoding_16bit_field ? 1 : 0));
6743 putFile8Bit(file, (level->em_slippery_gems ? 1 : 0));
6745 putFile8Bit(file, (level->use_custom_template ? 1 : 0));
6747 putFile8Bit(file, (level->block_last_field ? 1 : 0));
6748 putFile8Bit(file, (level->sp_block_last_field ? 1 : 0));
6749 putFile32BitBE(file, level->can_move_into_acid_bits);
6750 putFile8Bit(file, level->dont_collide_with_bits);
6752 putFile8Bit(file, (level->use_spring_bug ? 1 : 0));
6753 putFile8Bit(file, (level->use_step_counter ? 1 : 0));
6755 putFile8Bit(file, (level->instant_relocation ? 1 : 0));
6756 putFile8Bit(file, (level->can_pass_to_walkable ? 1 : 0));
6757 putFile8Bit(file, (level->grow_into_diggable ? 1 : 0));
6759 putFile8Bit(file, level->game_engine_type);
6761 WriteUnusedBytesToFile(file, LEVEL_CHUNK_HEAD_UNUSED);
6765 static int SaveLevel_NAME(FILE *file, struct LevelInfo *level)
6770 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
6771 chunk_size += putFile8Bit(file, level->name[i]);
6776 static int SaveLevel_AUTH(FILE *file, struct LevelInfo *level)
6781 for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
6782 chunk_size += putFile8Bit(file, level->author[i]);
6787 #if ENABLE_HISTORIC_CHUNKS
6788 static int SaveLevel_BODY(FILE *file, struct LevelInfo *level)
6793 for (y = 0; y < level->fieldy; y++)
6794 for (x = 0; x < level->fieldx; x++)
6795 if (level->encoding_16bit_field)
6796 chunk_size += putFile16BitBE(file, level->field[x][y]);
6798 chunk_size += putFile8Bit(file, level->field[x][y]);
6804 static int SaveLevel_BODY(FILE *file, struct LevelInfo *level)
6809 for (y = 0; y < level->fieldy; y++)
6810 for (x = 0; x < level->fieldx; x++)
6811 chunk_size += putFile16BitBE(file, level->field[x][y]);
6816 #if ENABLE_HISTORIC_CHUNKS
6817 static void SaveLevel_CONT(FILE *file, struct LevelInfo *level)
6821 putFile8Bit(file, EL_YAMYAM);
6822 putFile8Bit(file, level->num_yamyam_contents);
6823 putFile8Bit(file, 0);
6824 putFile8Bit(file, 0);
6826 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
6827 for (y = 0; y < 3; y++)
6828 for (x = 0; x < 3; x++)
6829 if (level->encoding_16bit_field)
6830 putFile16BitBE(file, level->yamyam_content[i].e[x][y]);
6832 putFile8Bit(file, level->yamyam_content[i].e[x][y]);
6836 #if ENABLE_HISTORIC_CHUNKS
6837 static void SaveLevel_CNT2(FILE *file, struct LevelInfo *level, int element)
6840 int num_contents, content_xsize, content_ysize;
6841 int content_array[MAX_ELEMENT_CONTENTS][3][3];
6843 if (element == EL_YAMYAM)
6845 num_contents = level->num_yamyam_contents;
6849 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
6850 for (y = 0; y < 3; y++)
6851 for (x = 0; x < 3; x++)
6852 content_array[i][x][y] = level->yamyam_content[i].e[x][y];
6854 else if (element == EL_BD_AMOEBA)
6860 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
6861 for (y = 0; y < 3; y++)
6862 for (x = 0; x < 3; x++)
6863 content_array[i][x][y] = EL_EMPTY;
6864 content_array[0][0][0] = level->amoeba_content;
6868 // chunk header already written -- write empty chunk data
6869 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_SIZE);
6871 Error(ERR_WARN, "cannot save content for element '%d'", element);
6875 putFile16BitBE(file, element);
6876 putFile8Bit(file, num_contents);
6877 putFile8Bit(file, content_xsize);
6878 putFile8Bit(file, content_ysize);
6880 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_UNUSED);
6882 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
6883 for (y = 0; y < 3; y++)
6884 for (x = 0; x < 3; x++)
6885 putFile16BitBE(file, content_array[i][x][y]);
6889 #if ENABLE_HISTORIC_CHUNKS
6890 static int SaveLevel_CNT3(FILE *file, struct LevelInfo *level, int element)
6892 int envelope_nr = element - EL_ENVELOPE_1;
6893 int envelope_len = strlen(level->envelope_text[envelope_nr]) + 1;
6897 chunk_size += putFile16BitBE(file, element);
6898 chunk_size += putFile16BitBE(file, envelope_len);
6899 chunk_size += putFile8Bit(file, level->envelope_xsize[envelope_nr]);
6900 chunk_size += putFile8Bit(file, level->envelope_ysize[envelope_nr]);
6902 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT3_UNUSED);
6903 chunk_size += LEVEL_CHUNK_CNT3_UNUSED;
6905 for (i = 0; i < envelope_len; i++)
6906 chunk_size += putFile8Bit(file, level->envelope_text[envelope_nr][i]);
6912 #if ENABLE_HISTORIC_CHUNKS
6913 static void SaveLevel_CUS1(FILE *file, struct LevelInfo *level,
6914 int num_changed_custom_elements)
6918 putFile16BitBE(file, num_changed_custom_elements);
6920 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6922 int element = EL_CUSTOM_START + i;
6924 struct ElementInfo *ei = &element_info[element];
6926 if (ei->properties[EP_BITFIELD_BASE_NR] != EP_BITMASK_DEFAULT)
6928 if (check < num_changed_custom_elements)
6930 putFile16BitBE(file, element);
6931 putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
6938 if (check != num_changed_custom_elements) // should not happen
6939 Error(ERR_WARN, "inconsistent number of custom element properties");
6943 #if ENABLE_HISTORIC_CHUNKS
6944 static void SaveLevel_CUS2(FILE *file, struct LevelInfo *level,
6945 int num_changed_custom_elements)
6949 putFile16BitBE(file, num_changed_custom_elements);
6951 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6953 int element = EL_CUSTOM_START + i;
6955 if (element_info[element].change->target_element != EL_EMPTY_SPACE)
6957 if (check < num_changed_custom_elements)
6959 putFile16BitBE(file, element);
6960 putFile16BitBE(file, element_info[element].change->target_element);
6967 if (check != num_changed_custom_elements) // should not happen
6968 Error(ERR_WARN, "inconsistent number of custom target elements");
6972 #if ENABLE_HISTORIC_CHUNKS
6973 static void SaveLevel_CUS3(FILE *file, struct LevelInfo *level,
6974 int num_changed_custom_elements)
6976 int i, j, x, y, check = 0;
6978 putFile16BitBE(file, num_changed_custom_elements);
6980 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6982 int element = EL_CUSTOM_START + i;
6983 struct ElementInfo *ei = &element_info[element];
6985 if (ei->modified_settings)
6987 if (check < num_changed_custom_elements)
6989 putFile16BitBE(file, element);
6991 for (j = 0; j < MAX_ELEMENT_NAME_LEN; j++)
6992 putFile8Bit(file, ei->description[j]);
6994 putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
6996 // some free bytes for future properties and padding
6997 WriteUnusedBytesToFile(file, 7);
6999 putFile8Bit(file, ei->use_gfx_element);
7000 putFile16BitBE(file, ei->gfx_element_initial);
7002 putFile8Bit(file, ei->collect_score_initial);
7003 putFile8Bit(file, ei->collect_count_initial);
7005 putFile16BitBE(file, ei->push_delay_fixed);
7006 putFile16BitBE(file, ei->push_delay_random);
7007 putFile16BitBE(file, ei->move_delay_fixed);
7008 putFile16BitBE(file, ei->move_delay_random);
7010 putFile16BitBE(file, ei->move_pattern);
7011 putFile8Bit(file, ei->move_direction_initial);
7012 putFile8Bit(file, ei->move_stepsize);
7014 for (y = 0; y < 3; y++)
7015 for (x = 0; x < 3; x++)
7016 putFile16BitBE(file, ei->content.e[x][y]);
7018 putFile32BitBE(file, ei->change->events);
7020 putFile16BitBE(file, ei->change->target_element);
7022 putFile16BitBE(file, ei->change->delay_fixed);
7023 putFile16BitBE(file, ei->change->delay_random);
7024 putFile16BitBE(file, ei->change->delay_frames);
7026 putFile16BitBE(file, ei->change->initial_trigger_element);
7028 putFile8Bit(file, ei->change->explode);
7029 putFile8Bit(file, ei->change->use_target_content);
7030 putFile8Bit(file, ei->change->only_if_complete);
7031 putFile8Bit(file, ei->change->use_random_replace);
7033 putFile8Bit(file, ei->change->random_percentage);
7034 putFile8Bit(file, ei->change->replace_when);
7036 for (y = 0; y < 3; y++)
7037 for (x = 0; x < 3; x++)
7038 putFile16BitBE(file, ei->change->content.e[x][y]);
7040 putFile8Bit(file, ei->slippery_type);
7042 // some free bytes for future properties and padding
7043 WriteUnusedBytesToFile(file, LEVEL_CPART_CUS3_UNUSED);
7050 if (check != num_changed_custom_elements) // should not happen
7051 Error(ERR_WARN, "inconsistent number of custom element properties");
7055 #if ENABLE_HISTORIC_CHUNKS
7056 static void SaveLevel_CUS4(FILE *file, struct LevelInfo *level, int element)
7058 struct ElementInfo *ei = &element_info[element];
7061 // ---------- custom element base property values (96 bytes) ----------------
7063 putFile16BitBE(file, element);
7065 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
7066 putFile8Bit(file, ei->description[i]);
7068 putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
7070 WriteUnusedBytesToFile(file, 4); // reserved for more base properties
7072 putFile8Bit(file, ei->num_change_pages);
7074 putFile16BitBE(file, ei->ce_value_fixed_initial);
7075 putFile16BitBE(file, ei->ce_value_random_initial);
7076 putFile8Bit(file, ei->use_last_ce_value);
7078 putFile8Bit(file, ei->use_gfx_element);
7079 putFile16BitBE(file, ei->gfx_element_initial);
7081 putFile8Bit(file, ei->collect_score_initial);
7082 putFile8Bit(file, ei->collect_count_initial);
7084 putFile8Bit(file, ei->drop_delay_fixed);
7085 putFile8Bit(file, ei->push_delay_fixed);
7086 putFile8Bit(file, ei->drop_delay_random);
7087 putFile8Bit(file, ei->push_delay_random);
7088 putFile16BitBE(file, ei->move_delay_fixed);
7089 putFile16BitBE(file, ei->move_delay_random);
7091 // bits 0 - 15 of "move_pattern" ...
7092 putFile16BitBE(file, ei->move_pattern & 0xffff);
7093 putFile8Bit(file, ei->move_direction_initial);
7094 putFile8Bit(file, ei->move_stepsize);
7096 putFile8Bit(file, ei->slippery_type);
7098 for (y = 0; y < 3; y++)
7099 for (x = 0; x < 3; x++)
7100 putFile16BitBE(file, ei->content.e[x][y]);
7102 putFile16BitBE(file, ei->move_enter_element);
7103 putFile16BitBE(file, ei->move_leave_element);
7104 putFile8Bit(file, ei->move_leave_type);
7106 // ... bits 16 - 31 of "move_pattern" (not nice, but downward compatible)
7107 putFile16BitBE(file, (ei->move_pattern >> 16) & 0xffff);
7109 putFile8Bit(file, ei->access_direction);
7111 putFile8Bit(file, ei->explosion_delay);
7112 putFile8Bit(file, ei->ignition_delay);
7113 putFile8Bit(file, ei->explosion_type);
7115 // some free bytes for future custom property values and padding
7116 WriteUnusedBytesToFile(file, 1);
7118 // ---------- change page property values (48 bytes) ------------------------
7120 for (i = 0; i < ei->num_change_pages; i++)
7122 struct ElementChangeInfo *change = &ei->change_page[i];
7123 unsigned int event_bits;
7125 // bits 0 - 31 of "has_event[]" ...
7127 for (j = 0; j < MIN(NUM_CHANGE_EVENTS, 32); j++)
7128 if (change->has_event[j])
7129 event_bits |= (1 << j);
7130 putFile32BitBE(file, event_bits);
7132 putFile16BitBE(file, change->target_element);
7134 putFile16BitBE(file, change->delay_fixed);
7135 putFile16BitBE(file, change->delay_random);
7136 putFile16BitBE(file, change->delay_frames);
7138 putFile16BitBE(file, change->initial_trigger_element);
7140 putFile8Bit(file, change->explode);
7141 putFile8Bit(file, change->use_target_content);
7142 putFile8Bit(file, change->only_if_complete);
7143 putFile8Bit(file, change->use_random_replace);
7145 putFile8Bit(file, change->random_percentage);
7146 putFile8Bit(file, change->replace_when);
7148 for (y = 0; y < 3; y++)
7149 for (x = 0; x < 3; x++)
7150 putFile16BitBE(file, change->target_content.e[x][y]);
7152 putFile8Bit(file, change->can_change);
7154 putFile8Bit(file, change->trigger_side);
7156 putFile8Bit(file, change->trigger_player);
7157 putFile8Bit(file, (change->trigger_page == CH_PAGE_ANY ? CH_PAGE_ANY_FILE :
7158 log_2(change->trigger_page)));
7160 putFile8Bit(file, change->has_action);
7161 putFile8Bit(file, change->action_type);
7162 putFile8Bit(file, change->action_mode);
7163 putFile16BitBE(file, change->action_arg);
7165 // ... bits 32 - 39 of "has_event[]" (not nice, but downward compatible)
7167 for (j = 32; j < NUM_CHANGE_EVENTS; j++)
7168 if (change->has_event[j])
7169 event_bits |= (1 << (j - 32));
7170 putFile8Bit(file, event_bits);
7175 #if ENABLE_HISTORIC_CHUNKS
7176 static void SaveLevel_GRP1(FILE *file, struct LevelInfo *level, int element)
7178 struct ElementInfo *ei = &element_info[element];
7179 struct ElementGroupInfo *group = ei->group;
7182 putFile16BitBE(file, element);
7184 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
7185 putFile8Bit(file, ei->description[i]);
7187 putFile8Bit(file, group->num_elements);
7189 putFile8Bit(file, ei->use_gfx_element);
7190 putFile16BitBE(file, ei->gfx_element_initial);
7192 putFile8Bit(file, group->choice_mode);
7194 // some free bytes for future values and padding
7195 WriteUnusedBytesToFile(file, 3);
7197 for (i = 0; i < MAX_ELEMENTS_IN_GROUP; i++)
7198 putFile16BitBE(file, group->element[i]);
7202 static int SaveLevel_MicroChunk(FILE *file, struct LevelFileConfigInfo *entry,
7203 boolean write_element)
7205 int save_type = entry->save_type;
7206 int data_type = entry->data_type;
7207 int conf_type = entry->conf_type;
7208 int byte_mask = conf_type & CONF_MASK_BYTES;
7209 int element = entry->element;
7210 int default_value = entry->default_value;
7212 boolean modified = FALSE;
7214 if (byte_mask != CONF_MASK_MULTI_BYTES)
7216 void *value_ptr = entry->value;
7217 int value = (data_type == TYPE_BOOLEAN ? *(boolean *)value_ptr :
7220 // check if any settings have been modified before saving them
7221 if (value != default_value)
7224 // do not save if explicitly told or if unmodified default settings
7225 if ((save_type == SAVE_CONF_NEVER) ||
7226 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
7230 num_bytes += putFile16BitBE(file, element);
7232 num_bytes += putFile8Bit(file, conf_type);
7233 num_bytes += (byte_mask == CONF_MASK_1_BYTE ? putFile8Bit (file, value) :
7234 byte_mask == CONF_MASK_2_BYTE ? putFile16BitBE(file, value) :
7235 byte_mask == CONF_MASK_4_BYTE ? putFile32BitBE(file, value) :
7238 else if (data_type == TYPE_STRING)
7240 char *default_string = entry->default_string;
7241 char *string = (char *)(entry->value);
7242 int string_length = strlen(string);
7245 // check if any settings have been modified before saving them
7246 if (!strEqual(string, default_string))
7249 // do not save if explicitly told or if unmodified default settings
7250 if ((save_type == SAVE_CONF_NEVER) ||
7251 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
7255 num_bytes += putFile16BitBE(file, element);
7257 num_bytes += putFile8Bit(file, conf_type);
7258 num_bytes += putFile16BitBE(file, string_length);
7260 for (i = 0; i < string_length; i++)
7261 num_bytes += putFile8Bit(file, string[i]);
7263 else if (data_type == TYPE_ELEMENT_LIST)
7265 int *element_array = (int *)(entry->value);
7266 int num_elements = *(int *)(entry->num_entities);
7269 // check if any settings have been modified before saving them
7270 for (i = 0; i < num_elements; i++)
7271 if (element_array[i] != default_value)
7274 // do not save if explicitly told or if unmodified default settings
7275 if ((save_type == SAVE_CONF_NEVER) ||
7276 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
7280 num_bytes += putFile16BitBE(file, element);
7282 num_bytes += putFile8Bit(file, conf_type);
7283 num_bytes += putFile16BitBE(file, num_elements * CONF_ELEMENT_NUM_BYTES);
7285 for (i = 0; i < num_elements; i++)
7286 num_bytes += putFile16BitBE(file, element_array[i]);
7288 else if (data_type == TYPE_CONTENT_LIST)
7290 struct Content *content = (struct Content *)(entry->value);
7291 int num_contents = *(int *)(entry->num_entities);
7294 // check if any settings have been modified before saving them
7295 for (i = 0; i < num_contents; i++)
7296 for (y = 0; y < 3; y++)
7297 for (x = 0; x < 3; x++)
7298 if (content[i].e[x][y] != default_value)
7301 // do not save if explicitly told or if unmodified default settings
7302 if ((save_type == SAVE_CONF_NEVER) ||
7303 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
7307 num_bytes += putFile16BitBE(file, element);
7309 num_bytes += putFile8Bit(file, conf_type);
7310 num_bytes += putFile16BitBE(file, num_contents * CONF_CONTENT_NUM_BYTES);
7312 for (i = 0; i < num_contents; i++)
7313 for (y = 0; y < 3; y++)
7314 for (x = 0; x < 3; x++)
7315 num_bytes += putFile16BitBE(file, content[i].e[x][y]);
7321 static int SaveLevel_INFO(FILE *file, struct LevelInfo *level)
7326 li = *level; // copy level data into temporary buffer
7328 for (i = 0; chunk_config_INFO[i].data_type != -1; i++)
7329 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_INFO[i], FALSE);
7334 static int SaveLevel_ELEM(FILE *file, struct LevelInfo *level)
7339 li = *level; // copy level data into temporary buffer
7341 for (i = 0; chunk_config_ELEM[i].data_type != -1; i++)
7342 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_ELEM[i], TRUE);
7347 static int SaveLevel_NOTE(FILE *file, struct LevelInfo *level, int element)
7349 int envelope_nr = element - EL_ENVELOPE_1;
7353 chunk_size += putFile16BitBE(file, element);
7355 // copy envelope data into temporary buffer
7356 xx_envelope = level->envelope[envelope_nr];
7358 for (i = 0; chunk_config_NOTE[i].data_type != -1; i++)
7359 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_NOTE[i], FALSE);
7364 static int SaveLevel_CUSX(FILE *file, struct LevelInfo *level, int element)
7366 struct ElementInfo *ei = &element_info[element];
7370 chunk_size += putFile16BitBE(file, element);
7372 xx_ei = *ei; // copy element data into temporary buffer
7374 // set default description string for this specific element
7375 strcpy(xx_default_description, getDefaultElementDescription(ei));
7377 for (i = 0; chunk_config_CUSX_base[i].data_type != -1; i++)
7378 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_CUSX_base[i], FALSE);
7380 for (i = 0; i < ei->num_change_pages; i++)
7382 struct ElementChangeInfo *change = &ei->change_page[i];
7384 xx_current_change_page = i;
7386 xx_change = *change; // copy change data into temporary buffer
7389 setEventBitsFromEventFlags(change);
7391 for (j = 0; chunk_config_CUSX_change[j].data_type != -1; j++)
7392 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_CUSX_change[j],
7399 static int SaveLevel_GRPX(FILE *file, struct LevelInfo *level, int element)
7401 struct ElementInfo *ei = &element_info[element];
7402 struct ElementGroupInfo *group = ei->group;
7406 chunk_size += putFile16BitBE(file, element);
7408 xx_ei = *ei; // copy element data into temporary buffer
7409 xx_group = *group; // copy group data into temporary buffer
7411 // set default description string for this specific element
7412 strcpy(xx_default_description, getDefaultElementDescription(ei));
7414 for (i = 0; chunk_config_GRPX[i].data_type != -1; i++)
7415 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_GRPX[i], FALSE);
7420 static void SaveLevelFromFilename(struct LevelInfo *level, char *filename,
7421 boolean save_as_template)
7427 if (!(file = fopen(filename, MODE_WRITE)))
7429 Error(ERR_WARN, "cannot save level file '%s'", filename);
7433 level->file_version = FILE_VERSION_ACTUAL;
7434 level->game_version = GAME_VERSION_ACTUAL;
7436 level->creation_date = getCurrentDate();
7438 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
7439 putFileChunkBE(file, "CAVE", CHUNK_SIZE_NONE);
7441 chunk_size = SaveLevel_VERS(NULL, level);
7442 putFileChunkBE(file, "VERS", chunk_size);
7443 SaveLevel_VERS(file, level);
7445 chunk_size = SaveLevel_DATE(NULL, level);
7446 putFileChunkBE(file, "DATE", chunk_size);
7447 SaveLevel_DATE(file, level);
7449 chunk_size = SaveLevel_NAME(NULL, level);
7450 putFileChunkBE(file, "NAME", chunk_size);
7451 SaveLevel_NAME(file, level);
7453 chunk_size = SaveLevel_AUTH(NULL, level);
7454 putFileChunkBE(file, "AUTH", chunk_size);
7455 SaveLevel_AUTH(file, level);
7457 chunk_size = SaveLevel_INFO(NULL, level);
7458 putFileChunkBE(file, "INFO", chunk_size);
7459 SaveLevel_INFO(file, level);
7461 chunk_size = SaveLevel_BODY(NULL, level);
7462 putFileChunkBE(file, "BODY", chunk_size);
7463 SaveLevel_BODY(file, level);
7465 chunk_size = SaveLevel_ELEM(NULL, level);
7466 if (chunk_size > LEVEL_CHUNK_ELEM_UNCHANGED) // save if changed
7468 putFileChunkBE(file, "ELEM", chunk_size);
7469 SaveLevel_ELEM(file, level);
7472 for (i = 0; i < NUM_ENVELOPES; i++)
7474 int element = EL_ENVELOPE_1 + i;
7476 chunk_size = SaveLevel_NOTE(NULL, level, element);
7477 if (chunk_size > LEVEL_CHUNK_NOTE_UNCHANGED) // save if changed
7479 putFileChunkBE(file, "NOTE", chunk_size);
7480 SaveLevel_NOTE(file, level, element);
7484 // if not using template level, check for non-default custom/group elements
7485 if (!level->use_custom_template || save_as_template)
7487 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7489 int element = EL_CUSTOM_START + i;
7491 chunk_size = SaveLevel_CUSX(NULL, level, element);
7492 if (chunk_size > LEVEL_CHUNK_CUSX_UNCHANGED) // save if changed
7494 putFileChunkBE(file, "CUSX", chunk_size);
7495 SaveLevel_CUSX(file, level, element);
7499 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
7501 int element = EL_GROUP_START + i;
7503 chunk_size = SaveLevel_GRPX(NULL, level, element);
7504 if (chunk_size > LEVEL_CHUNK_GRPX_UNCHANGED) // save if changed
7506 putFileChunkBE(file, "GRPX", chunk_size);
7507 SaveLevel_GRPX(file, level, element);
7514 SetFilePermissions(filename, PERMS_PRIVATE);
7517 void SaveLevel(int nr)
7519 char *filename = getDefaultLevelFilename(nr);
7521 SaveLevelFromFilename(&level, filename, FALSE);
7524 void SaveLevelTemplate(void)
7526 char *filename = getLocalLevelTemplateFilename();
7528 SaveLevelFromFilename(&level, filename, TRUE);
7531 boolean SaveLevelChecked(int nr)
7533 char *filename = getDefaultLevelFilename(nr);
7534 boolean new_level = !fileExists(filename);
7535 boolean level_saved = FALSE;
7537 if (new_level || Request("Save this level and kill the old?", REQ_ASK))
7542 Request("Level saved!", REQ_CONFIRM);
7550 void DumpLevel(struct LevelInfo *level)
7552 if (level->no_level_file || level->no_valid_file)
7554 Error(ERR_WARN, "cannot dump -- no valid level file found");
7560 Print("Level xxx (file version %08d, game version %08d)\n",
7561 level->file_version, level->game_version);
7564 Print("Level author: '%s'\n", level->author);
7565 Print("Level title: '%s'\n", level->name);
7567 Print("Playfield size: %d x %d\n", level->fieldx, level->fieldy);
7569 Print("Level time: %d seconds\n", level->time);
7570 Print("Gems needed: %d\n", level->gems_needed);
7572 Print("Time for magic wall: %d seconds\n", level->time_magic_wall);
7573 Print("Time for wheel: %d seconds\n", level->time_wheel);
7574 Print("Time for light: %d seconds\n", level->time_light);
7575 Print("Time for timegate: %d seconds\n", level->time_timegate);
7577 Print("Amoeba speed: %d\n", level->amoeba_speed);
7580 Print("EM style slippery gems: %s\n", (level->em_slippery_gems ? "yes" : "no"));
7581 Print("Player blocks last field: %s\n", (level->block_last_field ? "yes" : "no"));
7582 Print("SP player blocks last field: %s\n", (level->sp_block_last_field ? "yes" : "no"));
7583 Print("use spring bug: %s\n", (level->use_spring_bug ? "yes" : "no"));
7584 Print("use step counter: %s\n", (level->use_step_counter ? "yes" : "no"));
7590 // ============================================================================
7591 // tape file functions
7592 // ============================================================================
7594 static void setTapeInfoToDefaults(void)
7598 // always start with reliable default values (empty tape)
7601 // default values (also for pre-1.2 tapes) with only the first player
7602 tape.player_participates[0] = TRUE;
7603 for (i = 1; i < MAX_PLAYERS; i++)
7604 tape.player_participates[i] = FALSE;
7606 // at least one (default: the first) player participates in every tape
7607 tape.num_participating_players = 1;
7609 tape.level_nr = level_nr;
7611 tape.changed = FALSE;
7613 tape.recording = FALSE;
7614 tape.playing = FALSE;
7615 tape.pausing = FALSE;
7617 tape.no_valid_file = FALSE;
7620 static int LoadTape_VERS(File *file, int chunk_size, struct TapeInfo *tape)
7622 tape->file_version = getFileVersion(file);
7623 tape->game_version = getFileVersion(file);
7628 static int LoadTape_HEAD(File *file, int chunk_size, struct TapeInfo *tape)
7632 tape->random_seed = getFile32BitBE(file);
7633 tape->date = getFile32BitBE(file);
7634 tape->length = getFile32BitBE(file);
7636 // read header fields that are new since version 1.2
7637 if (tape->file_version >= FILE_VERSION_1_2)
7639 byte store_participating_players = getFile8Bit(file);
7642 // since version 1.2, tapes store which players participate in the tape
7643 tape->num_participating_players = 0;
7644 for (i = 0; i < MAX_PLAYERS; i++)
7646 tape->player_participates[i] = FALSE;
7648 if (store_participating_players & (1 << i))
7650 tape->player_participates[i] = TRUE;
7651 tape->num_participating_players++;
7655 tape->use_mouse = (getFile8Bit(file) == 1 ? TRUE : FALSE);
7657 ReadUnusedBytesFromFile(file, TAPE_CHUNK_HEAD_UNUSED);
7659 engine_version = getFileVersion(file);
7660 if (engine_version > 0)
7661 tape->engine_version = engine_version;
7663 tape->engine_version = tape->game_version;
7669 static int LoadTape_INFO(File *file, int chunk_size, struct TapeInfo *tape)
7671 int level_identifier_size;
7674 level_identifier_size = getFile16BitBE(file);
7676 tape->level_identifier =
7677 checked_realloc(tape->level_identifier, level_identifier_size);
7679 for (i = 0; i < level_identifier_size; i++)
7680 tape->level_identifier[i] = getFile8Bit(file);
7682 tape->level_nr = getFile16BitBE(file);
7684 chunk_size = 2 + level_identifier_size + 2;
7689 static int LoadTape_BODY(File *file, int chunk_size, struct TapeInfo *tape)
7693 (tape->use_mouse ? 3 : tape->num_participating_players) + 1;
7694 int chunk_size_expected = tape_pos_size * tape->length;
7696 if (chunk_size_expected != chunk_size)
7698 ReadUnusedBytesFromFile(file, chunk_size);
7699 return chunk_size_expected;
7702 for (i = 0; i < tape->length; i++)
7704 if (i >= MAX_TAPE_LEN)
7706 Error(ERR_WARN, "tape truncated -- size exceeds maximum tape size %d",
7709 // tape too large; read and ignore remaining tape data from this chunk
7710 for (;i < tape->length; i++)
7711 ReadUnusedBytesFromFile(file, tape->num_participating_players + 1);
7716 if (tape->use_mouse)
7718 tape->pos[i].action[TAPE_ACTION_LX] = getFile8Bit(file);
7719 tape->pos[i].action[TAPE_ACTION_LY] = getFile8Bit(file);
7720 tape->pos[i].action[TAPE_ACTION_BUTTON] = getFile8Bit(file);
7722 tape->pos[i].action[TAPE_ACTION_UNUSED] = 0;
7726 for (j = 0; j < MAX_PLAYERS; j++)
7728 tape->pos[i].action[j] = MV_NONE;
7730 if (tape->player_participates[j])
7731 tape->pos[i].action[j] = getFile8Bit(file);
7735 tape->pos[i].delay = getFile8Bit(file);
7737 if (tape->file_version == FILE_VERSION_1_0)
7739 // eliminate possible diagonal moves in old tapes
7740 // this is only for backward compatibility
7742 byte joy_dir[4] = { JOY_LEFT, JOY_RIGHT, JOY_UP, JOY_DOWN };
7743 byte action = tape->pos[i].action[0];
7744 int k, num_moves = 0;
7746 for (k = 0; k<4; k++)
7748 if (action & joy_dir[k])
7750 tape->pos[i + num_moves].action[0] = joy_dir[k];
7752 tape->pos[i + num_moves].delay = 0;
7761 tape->length += num_moves;
7764 else if (tape->file_version < FILE_VERSION_2_0)
7766 // convert pre-2.0 tapes to new tape format
7768 if (tape->pos[i].delay > 1)
7771 tape->pos[i + 1] = tape->pos[i];
7772 tape->pos[i + 1].delay = 1;
7775 for (j = 0; j < MAX_PLAYERS; j++)
7776 tape->pos[i].action[j] = MV_NONE;
7777 tape->pos[i].delay--;
7784 if (checkEndOfFile(file))
7788 if (i != tape->length)
7789 chunk_size = tape_pos_size * i;
7794 static void LoadTape_SokobanSolution(char *filename)
7797 int move_delay = TILESIZE / level.initial_player_stepsize[0];
7799 if (!(file = openFile(filename, MODE_READ)))
7801 tape.no_valid_file = TRUE;
7806 while (!checkEndOfFile(file))
7808 unsigned char c = getByteFromFile(file);
7810 if (checkEndOfFile(file))
7817 tape.pos[tape.length].action[0] = MV_UP;
7818 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
7824 tape.pos[tape.length].action[0] = MV_DOWN;
7825 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
7831 tape.pos[tape.length].action[0] = MV_LEFT;
7832 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
7838 tape.pos[tape.length].action[0] = MV_RIGHT;
7839 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
7847 // ignore white-space characters
7851 tape.no_valid_file = TRUE;
7853 Error(ERR_WARN, "unsupported Sokoban solution file '%s' ['%d']", filename, c);
7861 if (tape.no_valid_file)
7864 tape.length_frames = GetTapeLengthFrames();
7865 tape.length_seconds = GetTapeLengthSeconds();
7868 void LoadTapeFromFilename(char *filename)
7870 char cookie[MAX_LINE_LEN];
7871 char chunk_name[CHUNK_ID_LEN + 1];
7875 // always start with reliable default values
7876 setTapeInfoToDefaults();
7878 if (strSuffix(filename, ".sln"))
7880 LoadTape_SokobanSolution(filename);
7885 if (!(file = openFile(filename, MODE_READ)))
7887 tape.no_valid_file = TRUE;
7892 getFileChunkBE(file, chunk_name, NULL);
7893 if (strEqual(chunk_name, "RND1"))
7895 getFile32BitBE(file); // not used
7897 getFileChunkBE(file, chunk_name, NULL);
7898 if (!strEqual(chunk_name, "TAPE"))
7900 tape.no_valid_file = TRUE;
7902 Error(ERR_WARN, "unknown format of tape file '%s'", filename);
7909 else // check for pre-2.0 file format with cookie string
7911 strcpy(cookie, chunk_name);
7912 if (getStringFromFile(file, &cookie[4], MAX_LINE_LEN - 4) == NULL)
7914 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
7915 cookie[strlen(cookie) - 1] = '\0';
7917 if (!checkCookieString(cookie, TAPE_COOKIE_TMPL))
7919 tape.no_valid_file = TRUE;
7921 Error(ERR_WARN, "unknown format of tape file '%s'", filename);
7928 if ((tape.file_version = getFileVersionFromCookieString(cookie)) == -1)
7930 tape.no_valid_file = TRUE;
7932 Error(ERR_WARN, "unsupported version of tape file '%s'", filename);
7939 // pre-2.0 tape files have no game version, so use file version here
7940 tape.game_version = tape.file_version;
7943 if (tape.file_version < FILE_VERSION_1_2)
7945 // tape files from versions before 1.2.0 without chunk structure
7946 LoadTape_HEAD(file, TAPE_CHUNK_HEAD_SIZE, &tape);
7947 LoadTape_BODY(file, 2 * tape.length, &tape);
7955 int (*loader)(File *, int, struct TapeInfo *);
7959 { "VERS", TAPE_CHUNK_VERS_SIZE, LoadTape_VERS },
7960 { "HEAD", TAPE_CHUNK_HEAD_SIZE, LoadTape_HEAD },
7961 { "INFO", -1, LoadTape_INFO },
7962 { "BODY", -1, LoadTape_BODY },
7966 while (getFileChunkBE(file, chunk_name, &chunk_size))
7970 while (chunk_info[i].name != NULL &&
7971 !strEqual(chunk_name, chunk_info[i].name))
7974 if (chunk_info[i].name == NULL)
7976 Error(ERR_WARN, "unknown chunk '%s' in tape file '%s'",
7977 chunk_name, filename);
7978 ReadUnusedBytesFromFile(file, chunk_size);
7980 else if (chunk_info[i].size != -1 &&
7981 chunk_info[i].size != chunk_size)
7983 Error(ERR_WARN, "wrong size (%d) of chunk '%s' in tape file '%s'",
7984 chunk_size, chunk_name, filename);
7985 ReadUnusedBytesFromFile(file, chunk_size);
7989 // call function to load this tape chunk
7990 int chunk_size_expected =
7991 (chunk_info[i].loader)(file, chunk_size, &tape);
7993 // the size of some chunks cannot be checked before reading other
7994 // chunks first (like "HEAD" and "BODY") that contain some header
7995 // information, so check them here
7996 if (chunk_size_expected != chunk_size)
7998 Error(ERR_WARN, "wrong size (%d) of chunk '%s' in tape file '%s'",
7999 chunk_size, chunk_name, filename);
8007 tape.length_frames = GetTapeLengthFrames();
8008 tape.length_seconds = GetTapeLengthSeconds();
8011 printf("::: tape file version: %d\n", tape.file_version);
8012 printf("::: tape game version: %d\n", tape.game_version);
8013 printf("::: tape engine version: %d\n", tape.engine_version);
8017 void LoadTape(int nr)
8019 char *filename = getTapeFilename(nr);
8021 LoadTapeFromFilename(filename);
8024 void LoadSolutionTape(int nr)
8026 char *filename = getSolutionTapeFilename(nr);
8028 LoadTapeFromFilename(filename);
8030 if (TAPE_IS_EMPTY(tape) &&
8031 level.game_engine_type == GAME_ENGINE_TYPE_SP &&
8032 level.native_sp_level->demo.is_available)
8033 CopyNativeTape_SP_to_RND(&level);
8036 static void SaveTape_VERS(FILE *file, struct TapeInfo *tape)
8038 putFileVersion(file, tape->file_version);
8039 putFileVersion(file, tape->game_version);
8042 static void SaveTape_HEAD(FILE *file, struct TapeInfo *tape)
8045 byte store_participating_players = 0;
8047 // set bits for participating players for compact storage
8048 for (i = 0; i < MAX_PLAYERS; i++)
8049 if (tape->player_participates[i])
8050 store_participating_players |= (1 << i);
8052 putFile32BitBE(file, tape->random_seed);
8053 putFile32BitBE(file, tape->date);
8054 putFile32BitBE(file, tape->length);
8056 putFile8Bit(file, store_participating_players);
8058 putFile8Bit(file, (tape->use_mouse ? 1 : 0));
8060 // unused bytes not at the end here for 4-byte alignment of engine_version
8061 WriteUnusedBytesToFile(file, TAPE_CHUNK_HEAD_UNUSED);
8063 putFileVersion(file, tape->engine_version);
8066 static void SaveTape_INFO(FILE *file, struct TapeInfo *tape)
8068 int level_identifier_size = strlen(tape->level_identifier) + 1;
8071 putFile16BitBE(file, level_identifier_size);
8073 for (i = 0; i < level_identifier_size; i++)
8074 putFile8Bit(file, tape->level_identifier[i]);
8076 putFile16BitBE(file, tape->level_nr);
8079 static void SaveTape_BODY(FILE *file, struct TapeInfo *tape)
8083 for (i = 0; i < tape->length; i++)
8085 if (tape->use_mouse)
8087 putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_LX]);
8088 putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_LY]);
8089 putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_BUTTON]);
8093 for (j = 0; j < MAX_PLAYERS; j++)
8094 if (tape->player_participates[j])
8095 putFile8Bit(file, tape->pos[i].action[j]);
8098 putFile8Bit(file, tape->pos[i].delay);
8102 void SaveTape(int nr)
8104 char *filename = getTapeFilename(nr);
8106 int num_participating_players = 0;
8108 int info_chunk_size;
8109 int body_chunk_size;
8112 InitTapeDirectory(leveldir_current->subdir);
8114 if (!(file = fopen(filename, MODE_WRITE)))
8116 Error(ERR_WARN, "cannot save level recording file '%s'", filename);
8120 tape.file_version = FILE_VERSION_ACTUAL;
8121 tape.game_version = GAME_VERSION_ACTUAL;
8123 // count number of participating players
8124 for (i = 0; i < MAX_PLAYERS; i++)
8125 if (tape.player_participates[i])
8126 num_participating_players++;
8128 tape_pos_size = (tape.use_mouse ? 3 : num_participating_players) + 1;
8130 info_chunk_size = 2 + (strlen(tape.level_identifier) + 1) + 2;
8131 body_chunk_size = tape_pos_size * tape.length;
8133 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
8134 putFileChunkBE(file, "TAPE", CHUNK_SIZE_NONE);
8136 putFileChunkBE(file, "VERS", TAPE_CHUNK_VERS_SIZE);
8137 SaveTape_VERS(file, &tape);
8139 putFileChunkBE(file, "HEAD", TAPE_CHUNK_HEAD_SIZE);
8140 SaveTape_HEAD(file, &tape);
8142 putFileChunkBE(file, "INFO", info_chunk_size);
8143 SaveTape_INFO(file, &tape);
8145 putFileChunkBE(file, "BODY", body_chunk_size);
8146 SaveTape_BODY(file, &tape);
8150 SetFilePermissions(filename, PERMS_PRIVATE);
8152 tape.changed = FALSE;
8155 static boolean SaveTapeCheckedExt(int nr, char *msg_replace, char *msg_saved,
8156 unsigned int req_state_added)
8158 char *filename = getTapeFilename(nr);
8159 boolean new_tape = !fileExists(filename);
8160 boolean tape_saved = FALSE;
8162 if (new_tape || Request(msg_replace, REQ_ASK | req_state_added))
8167 Request(msg_saved, REQ_CONFIRM | req_state_added);
8175 boolean SaveTapeChecked(int nr)
8177 return SaveTapeCheckedExt(nr, "Replace old tape?", "Tape saved!", 0);
8180 boolean SaveTapeChecked_LevelSolved(int nr)
8182 return SaveTapeCheckedExt(nr, "Level solved! Replace old tape?",
8183 "Level solved! Tape saved!", REQ_STAY_OPEN);
8186 void DumpTape(struct TapeInfo *tape)
8188 int tape_frame_counter;
8191 if (tape->no_valid_file)
8193 Error(ERR_WARN, "cannot dump -- no valid tape file found");
8199 Print("Tape of Level %03d (file version %08d, game version %08d)\n",
8200 tape->level_nr, tape->file_version, tape->game_version);
8201 Print(" (effective engine version %08d)\n",
8202 tape->engine_version);
8203 Print("Level series identifier: '%s'\n", tape->level_identifier);
8206 tape_frame_counter = 0;
8208 for (i = 0; i < tape->length; i++)
8210 if (i >= MAX_TAPE_LEN)
8215 for (j = 0; j < MAX_PLAYERS; j++)
8217 if (tape->player_participates[j])
8219 int action = tape->pos[i].action[j];
8221 Print("%d:%02x ", j, action);
8222 Print("[%c%c%c%c|%c%c] - ",
8223 (action & JOY_LEFT ? '<' : ' '),
8224 (action & JOY_RIGHT ? '>' : ' '),
8225 (action & JOY_UP ? '^' : ' '),
8226 (action & JOY_DOWN ? 'v' : ' '),
8227 (action & JOY_BUTTON_1 ? '1' : ' '),
8228 (action & JOY_BUTTON_2 ? '2' : ' '));
8232 Print("(%03d) ", tape->pos[i].delay);
8233 Print("[%05d]\n", tape_frame_counter);
8235 tape_frame_counter += tape->pos[i].delay;
8242 // ============================================================================
8243 // score file functions
8244 // ============================================================================
8246 void LoadScore(int nr)
8249 char *filename = getScoreFilename(nr);
8250 char cookie[MAX_LINE_LEN];
8251 char line[MAX_LINE_LEN];
8255 // always start with reliable default values
8256 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
8258 strcpy(highscore[i].Name, EMPTY_PLAYER_NAME);
8259 highscore[i].Score = 0;
8262 if (!(file = fopen(filename, MODE_READ)))
8265 // check file identifier
8266 if (fgets(cookie, MAX_LINE_LEN, file) == NULL)
8268 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
8269 cookie[strlen(cookie) - 1] = '\0';
8271 if (!checkCookieString(cookie, SCORE_COOKIE))
8273 Error(ERR_WARN, "unknown format of score file '%s'", filename);
8278 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
8280 if (fscanf(file, "%d", &highscore[i].Score) == EOF)
8281 Error(ERR_WARN, "fscanf() failed; %s", strerror(errno));
8282 if (fgets(line, MAX_LINE_LEN, file) == NULL)
8285 if (strlen(line) > 0 && line[strlen(line) - 1] == '\n')
8286 line[strlen(line) - 1] = '\0';
8288 for (line_ptr = line; *line_ptr; line_ptr++)
8290 if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != '\0')
8292 strncpy(highscore[i].Name, line_ptr, MAX_PLAYER_NAME_LEN);
8293 highscore[i].Name[MAX_PLAYER_NAME_LEN] = '\0';
8302 void SaveScore(int nr)
8305 int permissions = (program.global_scores ? PERMS_PUBLIC : PERMS_PRIVATE);
8306 char *filename = getScoreFilename(nr);
8309 // used instead of "leveldir_current->subdir" (for network games)
8310 InitScoreDirectory(levelset.identifier);
8312 if (!(file = fopen(filename, MODE_WRITE)))
8314 Error(ERR_WARN, "cannot save score for level %d", nr);
8318 fprintf(file, "%s\n\n", SCORE_COOKIE);
8320 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
8321 fprintf(file, "%d %s\n", highscore[i].Score, highscore[i].Name);
8325 SetFilePermissions(filename, permissions);
8329 // ============================================================================
8330 // setup file functions
8331 // ============================================================================
8333 #define TOKEN_STR_PLAYER_PREFIX "player_"
8336 static struct TokenInfo global_setup_tokens[] =
8340 &setup.player_name, "player_name"
8344 &setup.sound, "sound"
8348 &setup.sound_loops, "repeating_sound_loops"
8352 &setup.sound_music, "background_music"
8356 &setup.sound_simple, "simple_sound_effects"
8360 &setup.toons, "toons"
8364 &setup.scroll_delay, "scroll_delay"
8368 &setup.scroll_delay_value, "scroll_delay_value"
8372 &setup.engine_snapshot_mode, "engine_snapshot_mode"
8376 &setup.engine_snapshot_memory, "engine_snapshot_memory"
8380 &setup.fade_screens, "fade_screens"
8384 &setup.autorecord, "automatic_tape_recording"
8388 &setup.show_titlescreen, "show_titlescreen"
8392 &setup.quick_doors, "quick_doors"
8396 &setup.team_mode, "team_mode"
8400 &setup.handicap, "handicap"
8404 &setup.skip_levels, "skip_levels"
8408 &setup.increment_levels, "increment_levels"
8412 &setup.auto_play_next_level, "auto_play_next_level"
8416 &setup.skip_scores_after_game, "skip_scores_after_game"
8420 &setup.time_limit, "time_limit"
8424 &setup.fullscreen, "fullscreen"
8428 &setup.window_scaling_percent, "window_scaling_percent"
8432 &setup.window_scaling_quality, "window_scaling_quality"
8436 &setup.screen_rendering_mode, "screen_rendering_mode"
8440 &setup.vsync_mode, "vsync_mode"
8444 &setup.ask_on_escape, "ask_on_escape"
8448 &setup.ask_on_escape_editor, "ask_on_escape_editor"
8452 &setup.ask_on_game_over, "ask_on_game_over"
8456 &setup.quick_switch, "quick_player_switch"
8460 &setup.input_on_focus, "input_on_focus"
8464 &setup.prefer_aga_graphics, "prefer_aga_graphics"
8468 &setup.game_speed_extended, "game_speed_extended"
8472 &setup.game_frame_delay, "game_frame_delay"
8476 &setup.sp_show_border_elements, "sp_show_border_elements"
8480 &setup.small_game_graphics, "small_game_graphics"
8484 &setup.show_snapshot_buttons, "show_snapshot_buttons"
8488 &setup.graphics_set, "graphics_set"
8492 &setup.sounds_set, "sounds_set"
8496 &setup.music_set, "music_set"
8500 &setup.override_level_graphics, "override_level_graphics"
8504 &setup.override_level_sounds, "override_level_sounds"
8508 &setup.override_level_music, "override_level_music"
8512 &setup.volume_simple, "volume_simple"
8516 &setup.volume_loops, "volume_loops"
8520 &setup.volume_music, "volume_music"
8524 &setup.network_mode, "network_mode"
8528 &setup.network_player_nr, "network_player"
8532 &setup.network_server_hostname, "network_server_hostname"
8536 &setup.touch.control_type, "touch.control_type"
8540 &setup.touch.move_distance, "touch.move_distance"
8544 &setup.touch.drop_distance, "touch.drop_distance"
8548 &setup.touch.transparency, "touch.transparency"
8552 &setup.touch.draw_outlined, "touch.draw_outlined"
8556 &setup.touch.draw_pressed, "touch.draw_pressed"
8560 &setup.touch.grid_xsize[0], "touch.virtual_buttons.0.xsize"
8564 &setup.touch.grid_ysize[0], "touch.virtual_buttons.0.ysize"
8568 &setup.touch.grid_xsize[1], "touch.virtual_buttons.1.xsize"
8572 &setup.touch.grid_ysize[1], "touch.virtual_buttons.1.ysize"
8576 static struct TokenInfo auto_setup_tokens[] =
8580 &setup.auto_setup.editor_zoom_tilesize, "editor.zoom_tilesize"
8584 static struct TokenInfo editor_setup_tokens[] =
8588 &setup.editor.el_classic, "editor.el_classic"
8592 &setup.editor.el_custom, "editor.el_custom"
8596 &setup.editor.el_user_defined, "editor.el_user_defined"
8600 &setup.editor.el_dynamic, "editor.el_dynamic"
8604 &setup.editor.el_headlines, "editor.el_headlines"
8608 &setup.editor.show_element_token, "editor.show_element_token"
8612 static struct TokenInfo editor_cascade_setup_tokens[] =
8616 &setup.editor_cascade.el_bd, "editor.cascade.el_bd"
8620 &setup.editor_cascade.el_em, "editor.cascade.el_em"
8624 &setup.editor_cascade.el_emc, "editor.cascade.el_emc"
8628 &setup.editor_cascade.el_rnd, "editor.cascade.el_rnd"
8632 &setup.editor_cascade.el_sb, "editor.cascade.el_sb"
8636 &setup.editor_cascade.el_sp, "editor.cascade.el_sp"
8640 &setup.editor_cascade.el_dc, "editor.cascade.el_dc"
8644 &setup.editor_cascade.el_dx, "editor.cascade.el_dx"
8648 &setup.editor_cascade.el_mm, "editor.cascade.el_mm"
8652 &setup.editor_cascade.el_df, "editor.cascade.el_df"
8656 &setup.editor_cascade.el_chars, "editor.cascade.el_chars"
8660 &setup.editor_cascade.el_steel_chars, "editor.cascade.el_steel_chars"
8664 &setup.editor_cascade.el_ce, "editor.cascade.el_ce"
8668 &setup.editor_cascade.el_ge, "editor.cascade.el_ge"
8672 &setup.editor_cascade.el_ref, "editor.cascade.el_ref"
8676 &setup.editor_cascade.el_user, "editor.cascade.el_user"
8680 &setup.editor_cascade.el_dynamic, "editor.cascade.el_dynamic"
8684 static struct TokenInfo shortcut_setup_tokens[] =
8688 &setup.shortcut.save_game, "shortcut.save_game"
8692 &setup.shortcut.load_game, "shortcut.load_game"
8696 &setup.shortcut.toggle_pause, "shortcut.toggle_pause"
8700 &setup.shortcut.focus_player[0], "shortcut.focus_player_1"
8704 &setup.shortcut.focus_player[1], "shortcut.focus_player_2"
8708 &setup.shortcut.focus_player[2], "shortcut.focus_player_3"
8712 &setup.shortcut.focus_player[3], "shortcut.focus_player_4"
8716 &setup.shortcut.focus_player_all, "shortcut.focus_player_all"
8720 &setup.shortcut.tape_eject, "shortcut.tape_eject"
8724 &setup.shortcut.tape_extra, "shortcut.tape_extra"
8728 &setup.shortcut.tape_stop, "shortcut.tape_stop"
8732 &setup.shortcut.tape_pause, "shortcut.tape_pause"
8736 &setup.shortcut.tape_record, "shortcut.tape_record"
8740 &setup.shortcut.tape_play, "shortcut.tape_play"
8744 &setup.shortcut.sound_simple, "shortcut.sound_simple"
8748 &setup.shortcut.sound_loops, "shortcut.sound_loops"
8752 &setup.shortcut.sound_music, "shortcut.sound_music"
8756 &setup.shortcut.snap_left, "shortcut.snap_left"
8760 &setup.shortcut.snap_right, "shortcut.snap_right"
8764 &setup.shortcut.snap_up, "shortcut.snap_up"
8768 &setup.shortcut.snap_down, "shortcut.snap_down"
8772 static struct SetupInputInfo setup_input;
8773 static struct TokenInfo player_setup_tokens[] =
8777 &setup_input.use_joystick, ".use_joystick"
8781 &setup_input.joy.device_name, ".joy.device_name"
8785 &setup_input.joy.xleft, ".joy.xleft"
8789 &setup_input.joy.xmiddle, ".joy.xmiddle"
8793 &setup_input.joy.xright, ".joy.xright"
8797 &setup_input.joy.yupper, ".joy.yupper"
8801 &setup_input.joy.ymiddle, ".joy.ymiddle"
8805 &setup_input.joy.ylower, ".joy.ylower"
8809 &setup_input.joy.snap, ".joy.snap_field"
8813 &setup_input.joy.drop, ".joy.place_bomb"
8817 &setup_input.key.left, ".key.move_left"
8821 &setup_input.key.right, ".key.move_right"
8825 &setup_input.key.up, ".key.move_up"
8829 &setup_input.key.down, ".key.move_down"
8833 &setup_input.key.snap, ".key.snap_field"
8837 &setup_input.key.drop, ".key.place_bomb"
8841 static struct TokenInfo system_setup_tokens[] =
8845 &setup.system.sdl_videodriver, "system.sdl_videodriver"
8849 &setup.system.sdl_audiodriver, "system.sdl_audiodriver"
8853 &setup.system.audio_fragment_size, "system.audio_fragment_size"
8857 static struct TokenInfo internal_setup_tokens[] =
8861 &setup.internal.program_title, "program_title"
8865 &setup.internal.program_version, "program_version"
8869 &setup.internal.program_author, "program_author"
8873 &setup.internal.program_email, "program_email"
8877 &setup.internal.program_website, "program_website"
8881 &setup.internal.program_copyright, "program_copyright"
8885 &setup.internal.program_company, "program_company"
8889 &setup.internal.program_icon_file, "program_icon_file"
8893 &setup.internal.default_graphics_set, "default_graphics_set"
8897 &setup.internal.default_sounds_set, "default_sounds_set"
8901 &setup.internal.default_music_set, "default_music_set"
8905 &setup.internal.fallback_graphics_file, "fallback_graphics_file"
8909 &setup.internal.fallback_sounds_file, "fallback_sounds_file"
8913 &setup.internal.fallback_music_file, "fallback_music_file"
8917 &setup.internal.default_level_series, "default_level_series"
8921 &setup.internal.default_window_width, "default_window_width"
8925 &setup.internal.default_window_height, "default_window_height"
8929 &setup.internal.choose_from_top_leveldir, "choose_from_top_leveldir"
8933 &setup.internal.show_scaling_in_title, "show_scaling_in_title"
8937 &setup.internal.create_user_levelset, "create_user_levelset"
8941 &setup.internal.menu_game, "menu_game"
8945 &setup.internal.menu_editor, "menu_editor"
8949 &setup.internal.menu_graphics, "menu_graphics"
8953 &setup.internal.menu_sound, "menu_sound"
8957 &setup.internal.menu_artwork, "menu_artwork"
8961 &setup.internal.menu_input, "menu_input"
8965 &setup.internal.menu_touch, "menu_touch"
8969 &setup.internal.menu_shortcuts, "menu_shortcuts"
8973 &setup.internal.menu_exit, "menu_exit"
8977 &setup.internal.menu_save_and_exit, "menu_save_and_exit"
8981 static struct TokenInfo debug_setup_tokens[] =
8985 &setup.debug.frame_delay[0], "debug.frame_delay_0"
8989 &setup.debug.frame_delay[1], "debug.frame_delay_1"
8993 &setup.debug.frame_delay[2], "debug.frame_delay_2"
8997 &setup.debug.frame_delay[3], "debug.frame_delay_3"
9001 &setup.debug.frame_delay[4], "debug.frame_delay_4"
9005 &setup.debug.frame_delay[5], "debug.frame_delay_5"
9009 &setup.debug.frame_delay[6], "debug.frame_delay_6"
9013 &setup.debug.frame_delay[7], "debug.frame_delay_7"
9017 &setup.debug.frame_delay[8], "debug.frame_delay_8"
9021 &setup.debug.frame_delay[9], "debug.frame_delay_9"
9025 &setup.debug.frame_delay_key[0], "debug.key.frame_delay_0"
9029 &setup.debug.frame_delay_key[1], "debug.key.frame_delay_1"
9033 &setup.debug.frame_delay_key[2], "debug.key.frame_delay_2"
9037 &setup.debug.frame_delay_key[3], "debug.key.frame_delay_3"
9041 &setup.debug.frame_delay_key[4], "debug.key.frame_delay_4"
9045 &setup.debug.frame_delay_key[5], "debug.key.frame_delay_5"
9049 &setup.debug.frame_delay_key[6], "debug.key.frame_delay_6"
9053 &setup.debug.frame_delay_key[7], "debug.key.frame_delay_7"
9057 &setup.debug.frame_delay_key[8], "debug.key.frame_delay_8"
9061 &setup.debug.frame_delay_key[9], "debug.key.frame_delay_9"
9065 &setup.debug.frame_delay_use_mod_key, "debug.frame_delay.use_mod_key"},
9068 &setup.debug.frame_delay_game_only, "debug.frame_delay.game_only"
9072 &setup.debug.show_frames_per_second, "debug.show_frames_per_second"
9076 static struct TokenInfo options_setup_tokens[] =
9080 &setup.options.verbose, "options.verbose"
9084 static char *get_corrected_login_name(char *login_name)
9086 // needed because player name must be a fixed length string
9087 char *login_name_new = checked_malloc(MAX_PLAYER_NAME_LEN + 1);
9089 strncpy(login_name_new, login_name, MAX_PLAYER_NAME_LEN);
9090 login_name_new[MAX_PLAYER_NAME_LEN] = '\0';
9092 if (strlen(login_name) > MAX_PLAYER_NAME_LEN) // name has been cut
9093 if (strchr(login_name_new, ' '))
9094 *strchr(login_name_new, ' ') = '\0';
9096 return login_name_new;
9099 static void setSetupInfoToDefaults(struct SetupInfo *si)
9103 si->player_name = get_corrected_login_name(getLoginName());
9106 si->sound_loops = TRUE;
9107 si->sound_music = TRUE;
9108 si->sound_simple = TRUE;
9110 si->scroll_delay = TRUE;
9111 si->scroll_delay_value = STD_SCROLL_DELAY;
9112 si->engine_snapshot_mode = getStringCopy(STR_SNAPSHOT_MODE_DEFAULT);
9113 si->engine_snapshot_memory = SNAPSHOT_MEMORY_DEFAULT;
9114 si->fade_screens = TRUE;
9115 si->autorecord = TRUE;
9116 si->show_titlescreen = TRUE;
9117 si->quick_doors = FALSE;
9118 si->team_mode = FALSE;
9119 si->handicap = TRUE;
9120 si->skip_levels = TRUE;
9121 si->increment_levels = TRUE;
9122 si->auto_play_next_level = TRUE;
9123 si->skip_scores_after_game = FALSE;
9124 si->time_limit = TRUE;
9125 si->fullscreen = FALSE;
9126 si->window_scaling_percent = STD_WINDOW_SCALING_PERCENT;
9127 si->window_scaling_quality = getStringCopy(SCALING_QUALITY_DEFAULT);
9128 si->screen_rendering_mode = getStringCopy(STR_SPECIAL_RENDERING_DEFAULT);
9129 si->vsync_mode = getStringCopy(STR_VSYNC_MODE_DEFAULT);
9130 si->ask_on_escape = TRUE;
9131 si->ask_on_escape_editor = TRUE;
9132 si->ask_on_game_over = TRUE;
9133 si->quick_switch = FALSE;
9134 si->input_on_focus = FALSE;
9135 si->prefer_aga_graphics = TRUE;
9136 si->game_speed_extended = FALSE;
9137 si->game_frame_delay = GAME_FRAME_DELAY;
9138 si->sp_show_border_elements = FALSE;
9139 si->small_game_graphics = FALSE;
9140 si->show_snapshot_buttons = FALSE;
9142 si->graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
9143 si->sounds_set = getStringCopy(SND_CLASSIC_SUBDIR);
9144 si->music_set = getStringCopy(MUS_CLASSIC_SUBDIR);
9146 si->override_level_graphics = FALSE;
9147 si->override_level_sounds = FALSE;
9148 si->override_level_music = FALSE;
9150 si->volume_simple = 100; // percent
9151 si->volume_loops = 100; // percent
9152 si->volume_music = 100; // percent
9154 si->network_mode = FALSE;
9155 si->network_player_nr = 0; // first player
9156 si->network_server_hostname = getStringCopy(STR_NETWORK_AUTO_DETECT);
9158 si->touch.control_type = getStringCopy(TOUCH_CONTROL_DEFAULT);
9159 si->touch.move_distance = TOUCH_MOVE_DISTANCE_DEFAULT; // percent
9160 si->touch.drop_distance = TOUCH_DROP_DISTANCE_DEFAULT; // percent
9161 si->touch.transparency = TOUCH_TRANSPARENCY_DEFAULT; // percent
9162 si->touch.draw_outlined = TRUE;
9163 si->touch.draw_pressed = TRUE;
9165 for (i = 0; i < 2; i++)
9167 char *default_grid_button[6][2] =
9173 { "111222", " vv " },
9174 { "111222", " vv " }
9176 int grid_xsize = DEFAULT_GRID_XSIZE(i);
9177 int grid_ysize = DEFAULT_GRID_YSIZE(i);
9178 int min_xsize = MIN(6, grid_xsize);
9179 int min_ysize = MIN(6, grid_ysize);
9180 int startx = grid_xsize - min_xsize;
9181 int starty = grid_ysize - min_ysize;
9184 // virtual buttons grid can only be set to defaults if video is initialized
9185 // (this will be repeated if virtual buttons are not loaded from setup file)
9186 if (video.initialized)
9188 si->touch.grid_xsize[i] = grid_xsize;
9189 si->touch.grid_ysize[i] = grid_ysize;
9193 si->touch.grid_xsize[i] = -1;
9194 si->touch.grid_ysize[i] = -1;
9197 for (x = 0; x < MAX_GRID_XSIZE; x++)
9198 for (y = 0; y < MAX_GRID_YSIZE; y++)
9199 si->touch.grid_button[i][x][y] = CHAR_GRID_BUTTON_NONE;
9201 for (x = 0; x < min_xsize; x++)
9202 for (y = 0; y < min_ysize; y++)
9203 si->touch.grid_button[i][x][starty + y] =
9204 default_grid_button[y][0][x];
9206 for (x = 0; x < min_xsize; x++)
9207 for (y = 0; y < min_ysize; y++)
9208 si->touch.grid_button[i][startx + x][starty + y] =
9209 default_grid_button[y][1][x];
9212 si->touch.grid_initialized = video.initialized;
9214 si->editor.el_boulderdash = TRUE;
9215 si->editor.el_emerald_mine = TRUE;
9216 si->editor.el_emerald_mine_club = TRUE;
9217 si->editor.el_more = TRUE;
9218 si->editor.el_sokoban = TRUE;
9219 si->editor.el_supaplex = TRUE;
9220 si->editor.el_diamond_caves = TRUE;
9221 si->editor.el_dx_boulderdash = TRUE;
9223 si->editor.el_mirror_magic = TRUE;
9224 si->editor.el_deflektor = TRUE;
9226 si->editor.el_chars = TRUE;
9227 si->editor.el_steel_chars = TRUE;
9229 si->editor.el_classic = TRUE;
9230 si->editor.el_custom = TRUE;
9232 si->editor.el_user_defined = FALSE;
9233 si->editor.el_dynamic = TRUE;
9235 si->editor.el_headlines = TRUE;
9237 si->editor.show_element_token = FALSE;
9239 si->editor.use_template_for_new_levels = TRUE;
9241 si->shortcut.save_game = DEFAULT_KEY_SAVE_GAME;
9242 si->shortcut.load_game = DEFAULT_KEY_LOAD_GAME;
9243 si->shortcut.toggle_pause = DEFAULT_KEY_TOGGLE_PAUSE;
9245 si->shortcut.focus_player[0] = DEFAULT_KEY_FOCUS_PLAYER_1;
9246 si->shortcut.focus_player[1] = DEFAULT_KEY_FOCUS_PLAYER_2;
9247 si->shortcut.focus_player[2] = DEFAULT_KEY_FOCUS_PLAYER_3;
9248 si->shortcut.focus_player[3] = DEFAULT_KEY_FOCUS_PLAYER_4;
9249 si->shortcut.focus_player_all = DEFAULT_KEY_FOCUS_PLAYER_ALL;
9251 si->shortcut.tape_eject = DEFAULT_KEY_TAPE_EJECT;
9252 si->shortcut.tape_extra = DEFAULT_KEY_TAPE_EXTRA;
9253 si->shortcut.tape_stop = DEFAULT_KEY_TAPE_STOP;
9254 si->shortcut.tape_pause = DEFAULT_KEY_TAPE_PAUSE;
9255 si->shortcut.tape_record = DEFAULT_KEY_TAPE_RECORD;
9256 si->shortcut.tape_play = DEFAULT_KEY_TAPE_PLAY;
9258 si->shortcut.sound_simple = DEFAULT_KEY_SOUND_SIMPLE;
9259 si->shortcut.sound_loops = DEFAULT_KEY_SOUND_LOOPS;
9260 si->shortcut.sound_music = DEFAULT_KEY_SOUND_MUSIC;
9262 si->shortcut.snap_left = DEFAULT_KEY_SNAP_LEFT;
9263 si->shortcut.snap_right = DEFAULT_KEY_SNAP_RIGHT;
9264 si->shortcut.snap_up = DEFAULT_KEY_SNAP_UP;
9265 si->shortcut.snap_down = DEFAULT_KEY_SNAP_DOWN;
9267 for (i = 0; i < MAX_PLAYERS; i++)
9269 si->input[i].use_joystick = FALSE;
9270 si->input[i].joy.device_name=getStringCopy(getDeviceNameFromJoystickNr(i));
9271 si->input[i].joy.xleft = JOYSTICK_XLEFT;
9272 si->input[i].joy.xmiddle = JOYSTICK_XMIDDLE;
9273 si->input[i].joy.xright = JOYSTICK_XRIGHT;
9274 si->input[i].joy.yupper = JOYSTICK_YUPPER;
9275 si->input[i].joy.ymiddle = JOYSTICK_YMIDDLE;
9276 si->input[i].joy.ylower = JOYSTICK_YLOWER;
9277 si->input[i].joy.snap = (i == 0 ? JOY_BUTTON_1 : 0);
9278 si->input[i].joy.drop = (i == 0 ? JOY_BUTTON_2 : 0);
9279 si->input[i].key.left = (i == 0 ? DEFAULT_KEY_LEFT : KSYM_UNDEFINED);
9280 si->input[i].key.right = (i == 0 ? DEFAULT_KEY_RIGHT : KSYM_UNDEFINED);
9281 si->input[i].key.up = (i == 0 ? DEFAULT_KEY_UP : KSYM_UNDEFINED);
9282 si->input[i].key.down = (i == 0 ? DEFAULT_KEY_DOWN : KSYM_UNDEFINED);
9283 si->input[i].key.snap = (i == 0 ? DEFAULT_KEY_SNAP : KSYM_UNDEFINED);
9284 si->input[i].key.drop = (i == 0 ? DEFAULT_KEY_DROP : KSYM_UNDEFINED);
9287 si->system.sdl_videodriver = getStringCopy(ARG_DEFAULT);
9288 si->system.sdl_audiodriver = getStringCopy(ARG_DEFAULT);
9289 si->system.audio_fragment_size = DEFAULT_AUDIO_FRAGMENT_SIZE;
9291 si->internal.program_title = getStringCopy(PROGRAM_TITLE_STRING);
9292 si->internal.program_version = getStringCopy(getProgramRealVersionString());
9293 si->internal.program_author = getStringCopy(PROGRAM_AUTHOR_STRING);
9294 si->internal.program_email = getStringCopy(PROGRAM_EMAIL_STRING);
9295 si->internal.program_website = getStringCopy(PROGRAM_WEBSITE_STRING);
9296 si->internal.program_copyright = getStringCopy(PROGRAM_COPYRIGHT_STRING);
9297 si->internal.program_company = getStringCopy(PROGRAM_COMPANY_STRING);
9299 si->internal.program_icon_file = getStringCopy(PROGRAM_ICON_FILENAME);
9301 si->internal.default_graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
9302 si->internal.default_sounds_set = getStringCopy(SND_CLASSIC_SUBDIR);
9303 si->internal.default_music_set = getStringCopy(MUS_CLASSIC_SUBDIR);
9305 si->internal.fallback_graphics_file = getStringCopy(UNDEFINED_FILENAME);
9306 si->internal.fallback_sounds_file = getStringCopy(UNDEFINED_FILENAME);
9307 si->internal.fallback_music_file = getStringCopy(UNDEFINED_FILENAME);
9309 si->internal.default_level_series = getStringCopy(UNDEFINED_LEVELSET);
9310 si->internal.choose_from_top_leveldir = FALSE;
9311 si->internal.show_scaling_in_title = TRUE;
9312 si->internal.create_user_levelset = TRUE;
9314 si->internal.default_window_width = WIN_XSIZE_DEFAULT;
9315 si->internal.default_window_height = WIN_YSIZE_DEFAULT;
9317 si->debug.frame_delay[0] = DEFAULT_FRAME_DELAY_0;
9318 si->debug.frame_delay[1] = DEFAULT_FRAME_DELAY_1;
9319 si->debug.frame_delay[2] = DEFAULT_FRAME_DELAY_2;
9320 si->debug.frame_delay[3] = DEFAULT_FRAME_DELAY_3;
9321 si->debug.frame_delay[4] = DEFAULT_FRAME_DELAY_4;
9322 si->debug.frame_delay[5] = DEFAULT_FRAME_DELAY_5;
9323 si->debug.frame_delay[6] = DEFAULT_FRAME_DELAY_6;
9324 si->debug.frame_delay[7] = DEFAULT_FRAME_DELAY_7;
9325 si->debug.frame_delay[8] = DEFAULT_FRAME_DELAY_8;
9326 si->debug.frame_delay[9] = DEFAULT_FRAME_DELAY_9;
9328 si->debug.frame_delay_key[0] = DEFAULT_KEY_FRAME_DELAY_0;
9329 si->debug.frame_delay_key[1] = DEFAULT_KEY_FRAME_DELAY_1;
9330 si->debug.frame_delay_key[2] = DEFAULT_KEY_FRAME_DELAY_2;
9331 si->debug.frame_delay_key[3] = DEFAULT_KEY_FRAME_DELAY_3;
9332 si->debug.frame_delay_key[4] = DEFAULT_KEY_FRAME_DELAY_4;
9333 si->debug.frame_delay_key[5] = DEFAULT_KEY_FRAME_DELAY_5;
9334 si->debug.frame_delay_key[6] = DEFAULT_KEY_FRAME_DELAY_6;
9335 si->debug.frame_delay_key[7] = DEFAULT_KEY_FRAME_DELAY_7;
9336 si->debug.frame_delay_key[8] = DEFAULT_KEY_FRAME_DELAY_8;
9337 si->debug.frame_delay_key[9] = DEFAULT_KEY_FRAME_DELAY_9;
9339 si->debug.frame_delay_use_mod_key = DEFAULT_FRAME_DELAY_USE_MOD_KEY;
9340 si->debug.frame_delay_game_only = DEFAULT_FRAME_DELAY_GAME_ONLY;
9342 si->debug.show_frames_per_second = FALSE;
9344 si->options.verbose = FALSE;
9346 #if defined(PLATFORM_ANDROID)
9347 si->fullscreen = TRUE;
9351 static void setSetupInfoToDefaults_AutoSetup(struct SetupInfo *si)
9353 si->auto_setup.editor_zoom_tilesize = MINI_TILESIZE;
9356 static void setSetupInfoToDefaults_EditorCascade(struct SetupInfo *si)
9358 si->editor_cascade.el_bd = TRUE;
9359 si->editor_cascade.el_em = TRUE;
9360 si->editor_cascade.el_emc = TRUE;
9361 si->editor_cascade.el_rnd = TRUE;
9362 si->editor_cascade.el_sb = TRUE;
9363 si->editor_cascade.el_sp = TRUE;
9364 si->editor_cascade.el_dc = TRUE;
9365 si->editor_cascade.el_dx = TRUE;
9367 si->editor_cascade.el_mm = TRUE;
9368 si->editor_cascade.el_df = TRUE;
9370 si->editor_cascade.el_chars = FALSE;
9371 si->editor_cascade.el_steel_chars = FALSE;
9372 si->editor_cascade.el_ce = FALSE;
9373 si->editor_cascade.el_ge = FALSE;
9374 si->editor_cascade.el_ref = FALSE;
9375 si->editor_cascade.el_user = FALSE;
9376 si->editor_cascade.el_dynamic = FALSE;
9379 #define MAX_HIDE_SETUP_TOKEN_SIZE 20
9381 static char *getHideSetupToken(void *setup_value)
9383 static char hide_setup_token[MAX_HIDE_SETUP_TOKEN_SIZE];
9385 if (setup_value != NULL)
9386 snprintf(hide_setup_token, MAX_HIDE_SETUP_TOKEN_SIZE, "%p", setup_value);
9388 return hide_setup_token;
9391 void setHideSetupEntry(void *setup_value)
9393 char *hide_setup_token = getHideSetupToken(setup_value);
9395 if (setup_value != NULL)
9396 setHashEntry(hide_setup_hash, hide_setup_token, "");
9399 boolean hideSetupEntry(void *setup_value)
9401 char *hide_setup_token = getHideSetupToken(setup_value);
9403 return (setup_value != NULL &&
9404 getHashEntry(hide_setup_hash, hide_setup_token) != NULL);
9407 static void setSetupInfoFromTokenText(SetupFileHash *setup_file_hash,
9408 struct TokenInfo *token_info,
9409 int token_nr, char *token_text)
9411 char *token_hide_text = getStringCat2(token_text, ".hide");
9412 char *token_hide_value = getHashEntry(setup_file_hash, token_hide_text);
9414 // set the value of this setup option in the setup option structure
9415 setSetupInfo(token_info, token_nr, getHashEntry(setup_file_hash, token_text));
9417 // check if this setup option should be hidden in the setup menu
9418 if (token_hide_value != NULL && get_boolean_from_string(token_hide_value))
9419 setHideSetupEntry(token_info[token_nr].value);
9422 static void setSetupInfoFromTokenInfo(SetupFileHash *setup_file_hash,
9423 struct TokenInfo *token_info,
9426 setSetupInfoFromTokenText(setup_file_hash, token_info, token_nr,
9427 token_info[token_nr].text);
9430 static void decodeSetupFileHash(SetupFileHash *setup_file_hash)
9434 if (!setup_file_hash)
9437 if (hide_setup_hash == NULL)
9438 hide_setup_hash = newSetupFileHash();
9440 for (i = 0; i < ARRAY_SIZE(global_setup_tokens); i++)
9441 setSetupInfoFromTokenInfo(setup_file_hash, global_setup_tokens, i);
9443 setup.touch.grid_initialized = TRUE;
9444 for (i = 0; i < 2; i++)
9446 int grid_xsize = setup.touch.grid_xsize[i];
9447 int grid_ysize = setup.touch.grid_ysize[i];
9450 // if virtual buttons are not loaded from setup file, repeat initializing
9451 // virtual buttons grid with default values later when video is initialized
9452 if (grid_xsize == -1 ||
9455 setup.touch.grid_initialized = FALSE;
9460 for (y = 0; y < grid_ysize; y++)
9462 char token_string[MAX_LINE_LEN];
9464 sprintf(token_string, "touch.virtual_buttons.%d.%02d", i, y);
9466 char *value_string = getHashEntry(setup_file_hash, token_string);
9468 if (value_string == NULL)
9471 for (x = 0; x < grid_xsize; x++)
9473 char c = value_string[x];
9475 setup.touch.grid_button[i][x][y] =
9476 (c == '.' ? CHAR_GRID_BUTTON_NONE : c);
9481 for (i = 0; i < ARRAY_SIZE(editor_setup_tokens); i++)
9482 setSetupInfoFromTokenInfo(setup_file_hash, editor_setup_tokens, i);
9484 for (i = 0; i < ARRAY_SIZE(shortcut_setup_tokens); i++)
9485 setSetupInfoFromTokenInfo(setup_file_hash, shortcut_setup_tokens, i);
9487 for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
9491 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
9493 setup_input = setup.input[pnr];
9494 for (i = 0; i < ARRAY_SIZE(player_setup_tokens); i++)
9496 char full_token[100];
9498 sprintf(full_token, "%s%s", prefix, player_setup_tokens[i].text);
9499 setSetupInfoFromTokenText(setup_file_hash, player_setup_tokens, i,
9502 setup.input[pnr] = setup_input;
9505 for (i = 0; i < ARRAY_SIZE(system_setup_tokens); i++)
9506 setSetupInfoFromTokenInfo(setup_file_hash, system_setup_tokens, i);
9508 for (i = 0; i < ARRAY_SIZE(internal_setup_tokens); i++)
9509 setSetupInfoFromTokenInfo(setup_file_hash, internal_setup_tokens, i);
9511 for (i = 0; i < ARRAY_SIZE(debug_setup_tokens); i++)
9512 setSetupInfoFromTokenInfo(setup_file_hash, debug_setup_tokens, i);
9514 for (i = 0; i < ARRAY_SIZE(options_setup_tokens); i++)
9515 setSetupInfoFromTokenInfo(setup_file_hash, options_setup_tokens, i);
9517 setHideRelatedSetupEntries();
9520 static void decodeSetupFileHash_AutoSetup(SetupFileHash *setup_file_hash)
9524 if (!setup_file_hash)
9527 for (i = 0; i < ARRAY_SIZE(auto_setup_tokens); i++)
9528 setSetupInfo(auto_setup_tokens, i,
9529 getHashEntry(setup_file_hash,
9530 auto_setup_tokens[i].text));
9533 static void decodeSetupFileHash_EditorCascade(SetupFileHash *setup_file_hash)
9537 if (!setup_file_hash)
9540 for (i = 0; i < ARRAY_SIZE(editor_cascade_setup_tokens); i++)
9541 setSetupInfo(editor_cascade_setup_tokens, i,
9542 getHashEntry(setup_file_hash,
9543 editor_cascade_setup_tokens[i].text));
9546 void LoadSetupFromFilename(char *filename)
9548 SetupFileHash *setup_file_hash = loadSetupFileHash(filename);
9550 if (setup_file_hash)
9552 decodeSetupFileHash(setup_file_hash);
9554 freeSetupFileHash(setup_file_hash);
9558 Error(ERR_DEBUG, "using default setup values");
9562 static void LoadSetup_SpecialPostProcessing(void)
9564 char *player_name_new;
9566 // needed to work around problems with fixed length strings
9567 player_name_new = get_corrected_login_name(setup.player_name);
9568 free(setup.player_name);
9569 setup.player_name = player_name_new;
9571 // "scroll_delay: on(3) / off(0)" was replaced by scroll delay value
9572 if (setup.scroll_delay == FALSE)
9574 setup.scroll_delay_value = MIN_SCROLL_DELAY;
9575 setup.scroll_delay = TRUE; // now always "on"
9578 // make sure that scroll delay value stays inside valid range
9579 setup.scroll_delay_value =
9580 MIN(MAX(MIN_SCROLL_DELAY, setup.scroll_delay_value), MAX_SCROLL_DELAY);
9583 void LoadSetup(void)
9587 // always start with reliable default values
9588 setSetupInfoToDefaults(&setup);
9590 // try to load setup values from default setup file
9591 filename = getDefaultSetupFilename();
9593 if (fileExists(filename))
9594 LoadSetupFromFilename(filename);
9596 // try to load setup values from user setup file
9597 filename = getSetupFilename();
9599 LoadSetupFromFilename(filename);
9601 LoadSetup_SpecialPostProcessing();
9604 void LoadSetup_AutoSetup(void)
9606 char *filename = getPath2(getSetupDir(), AUTOSETUP_FILENAME);
9607 SetupFileHash *setup_file_hash = NULL;
9609 // always start with reliable default values
9610 setSetupInfoToDefaults_AutoSetup(&setup);
9612 setup_file_hash = loadSetupFileHash(filename);
9614 if (setup_file_hash)
9616 decodeSetupFileHash_AutoSetup(setup_file_hash);
9618 freeSetupFileHash(setup_file_hash);
9624 void LoadSetup_EditorCascade(void)
9626 char *filename = getPath2(getSetupDir(), EDITORCASCADE_FILENAME);
9627 SetupFileHash *setup_file_hash = NULL;
9629 // always start with reliable default values
9630 setSetupInfoToDefaults_EditorCascade(&setup);
9632 setup_file_hash = loadSetupFileHash(filename);
9634 if (setup_file_hash)
9636 decodeSetupFileHash_EditorCascade(setup_file_hash);
9638 freeSetupFileHash(setup_file_hash);
9644 static void addGameControllerMappingToHash(SetupFileHash *mappings_hash,
9647 char mapping_guid[MAX_LINE_LEN];
9648 char *mapping_start, *mapping_end;
9650 // get GUID from game controller mapping line: copy complete line
9651 strncpy(mapping_guid, mapping_line, MAX_LINE_LEN - 1);
9652 mapping_guid[MAX_LINE_LEN - 1] = '\0';
9654 // get GUID from game controller mapping line: cut after GUID part
9655 mapping_start = strchr(mapping_guid, ',');
9656 if (mapping_start != NULL)
9657 *mapping_start = '\0';
9659 // cut newline from game controller mapping line
9660 mapping_end = strchr(mapping_line, '\n');
9661 if (mapping_end != NULL)
9662 *mapping_end = '\0';
9664 // add mapping entry to game controller mappings hash
9665 setHashEntry(mappings_hash, mapping_guid, mapping_line);
9668 static void LoadSetup_ReadGameControllerMappings(SetupFileHash *mappings_hash,
9673 if (!(file = fopen(filename, MODE_READ)))
9675 Error(ERR_WARN, "cannot read game controller mappings file '%s'", filename);
9682 char line[MAX_LINE_LEN];
9684 if (!fgets(line, MAX_LINE_LEN, file))
9687 addGameControllerMappingToHash(mappings_hash, line);
9693 void SaveSetup(void)
9695 char *filename = getSetupFilename();
9699 InitUserDataDirectory();
9701 if (!(file = fopen(filename, MODE_WRITE)))
9703 Error(ERR_WARN, "cannot write setup file '%s'", filename);
9707 fprintFileHeader(file, SETUP_FILENAME);
9709 for (i = 0; i < ARRAY_SIZE(global_setup_tokens); i++)
9711 // just to make things nicer :)
9712 if (global_setup_tokens[i].value == &setup.sound ||
9713 global_setup_tokens[i].value == &setup.graphics_set ||
9714 global_setup_tokens[i].value == &setup.volume_simple ||
9715 global_setup_tokens[i].value == &setup.network_mode ||
9716 global_setup_tokens[i].value == &setup.touch.control_type ||
9717 global_setup_tokens[i].value == &setup.touch.grid_xsize[0] ||
9718 global_setup_tokens[i].value == &setup.touch.grid_xsize[1])
9719 fprintf(file, "\n");
9721 fprintf(file, "%s\n", getSetupLine(global_setup_tokens, "", i));
9724 for (i = 0; i < 2; i++)
9726 int grid_xsize = setup.touch.grid_xsize[i];
9727 int grid_ysize = setup.touch.grid_ysize[i];
9730 fprintf(file, "\n");
9732 for (y = 0; y < grid_ysize; y++)
9734 char token_string[MAX_LINE_LEN];
9735 char value_string[MAX_LINE_LEN];
9737 sprintf(token_string, "touch.virtual_buttons.%d.%02d", i, y);
9739 for (x = 0; x < grid_xsize; x++)
9741 char c = setup.touch.grid_button[i][x][y];
9743 value_string[x] = (c == CHAR_GRID_BUTTON_NONE ? '.' : c);
9746 value_string[grid_xsize] = '\0';
9748 fprintf(file, "%s\n", getFormattedSetupEntry(token_string, value_string));
9752 fprintf(file, "\n");
9753 for (i = 0; i < ARRAY_SIZE(editor_setup_tokens); i++)
9754 fprintf(file, "%s\n", getSetupLine(editor_setup_tokens, "", i));
9756 fprintf(file, "\n");
9757 for (i = 0; i < ARRAY_SIZE(shortcut_setup_tokens); i++)
9758 fprintf(file, "%s\n", getSetupLine(shortcut_setup_tokens, "", i));
9760 for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
9764 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
9765 fprintf(file, "\n");
9767 setup_input = setup.input[pnr];
9768 for (i = 0; i < ARRAY_SIZE(player_setup_tokens); i++)
9769 fprintf(file, "%s\n", getSetupLine(player_setup_tokens, prefix, i));
9772 fprintf(file, "\n");
9773 for (i = 0; i < ARRAY_SIZE(system_setup_tokens); i++)
9774 fprintf(file, "%s\n", getSetupLine(system_setup_tokens, "", i));
9776 // (internal setup values not saved to user setup file)
9778 fprintf(file, "\n");
9779 for (i = 0; i < ARRAY_SIZE(debug_setup_tokens); i++)
9780 fprintf(file, "%s\n", getSetupLine(debug_setup_tokens, "", i));
9782 fprintf(file, "\n");
9783 for (i = 0; i < ARRAY_SIZE(options_setup_tokens); i++)
9784 fprintf(file, "%s\n", getSetupLine(options_setup_tokens, "", i));
9788 SetFilePermissions(filename, PERMS_PRIVATE);
9791 void SaveSetup_AutoSetup(void)
9793 char *filename = getPath2(getSetupDir(), AUTOSETUP_FILENAME);
9797 InitUserDataDirectory();
9799 if (!(file = fopen(filename, MODE_WRITE)))
9801 Error(ERR_WARN, "cannot write auto setup file '%s'", filename);
9806 fprintFileHeader(file, AUTOSETUP_FILENAME);
9808 for (i = 0; i < ARRAY_SIZE(auto_setup_tokens); i++)
9809 fprintf(file, "%s\n", getSetupLine(auto_setup_tokens, "", i));
9813 SetFilePermissions(filename, PERMS_PRIVATE);
9818 void SaveSetup_EditorCascade(void)
9820 char *filename = getPath2(getSetupDir(), EDITORCASCADE_FILENAME);
9824 InitUserDataDirectory();
9826 if (!(file = fopen(filename, MODE_WRITE)))
9828 Error(ERR_WARN, "cannot write editor cascade state file '%s'", filename);
9833 fprintFileHeader(file, EDITORCASCADE_FILENAME);
9835 for (i = 0; i < ARRAY_SIZE(editor_cascade_setup_tokens); i++)
9836 fprintf(file, "%s\n", getSetupLine(editor_cascade_setup_tokens, "", i));
9840 SetFilePermissions(filename, PERMS_PRIVATE);
9845 static void SaveSetup_WriteGameControllerMappings(SetupFileHash *mappings_hash,
9850 if (!(file = fopen(filename, MODE_WRITE)))
9852 Error(ERR_WARN, "cannot write game controller mappings file '%s'",filename);
9857 BEGIN_HASH_ITERATION(mappings_hash, itr)
9859 fprintf(file, "%s\n", HASH_ITERATION_VALUE(itr));
9861 END_HASH_ITERATION(mappings_hash, itr)
9866 void SaveSetup_AddGameControllerMapping(char *mapping)
9868 char *filename = getPath2(getSetupDir(), GAMECONTROLLER_BASENAME);
9869 SetupFileHash *mappings_hash = newSetupFileHash();
9871 InitUserDataDirectory();
9873 // load existing personal game controller mappings
9874 LoadSetup_ReadGameControllerMappings(mappings_hash, filename);
9876 // add new mapping to personal game controller mappings
9877 addGameControllerMappingToHash(mappings_hash, mapping);
9879 // save updated personal game controller mappings
9880 SaveSetup_WriteGameControllerMappings(mappings_hash, filename);
9882 freeSetupFileHash(mappings_hash);
9886 void LoadCustomElementDescriptions(void)
9888 char *filename = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
9889 SetupFileHash *setup_file_hash;
9892 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
9894 if (element_info[i].custom_description != NULL)
9896 free(element_info[i].custom_description);
9897 element_info[i].custom_description = NULL;
9901 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
9904 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
9906 char *token = getStringCat2(element_info[i].token_name, ".name");
9907 char *value = getHashEntry(setup_file_hash, token);
9910 element_info[i].custom_description = getStringCopy(value);
9915 freeSetupFileHash(setup_file_hash);
9918 static int getElementFromToken(char *token)
9920 char *value = getHashEntry(element_token_hash, token);
9925 Error(ERR_WARN, "unknown element token '%s'", token);
9927 return EL_UNDEFINED;
9930 void FreeGlobalAnimEventInfo(void)
9932 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
9934 if (gaei->event_list == NULL)
9939 for (i = 0; i < gaei->num_event_lists; i++)
9941 checked_free(gaei->event_list[i]->event_value);
9942 checked_free(gaei->event_list[i]);
9945 checked_free(gaei->event_list);
9947 gaei->event_list = NULL;
9948 gaei->num_event_lists = 0;
9951 static int AddGlobalAnimEventList(void)
9953 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
9954 int list_pos = gaei->num_event_lists++;
9956 gaei->event_list = checked_realloc(gaei->event_list, gaei->num_event_lists *
9957 sizeof(struct GlobalAnimEventListInfo *));
9959 gaei->event_list[list_pos] =
9960 checked_calloc(sizeof(struct GlobalAnimEventListInfo));
9962 struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
9964 gaeli->event_value = NULL;
9965 gaeli->num_event_values = 0;
9970 static int AddGlobalAnimEventValue(int list_pos, int event_value)
9972 // do not add empty global animation events
9973 if (event_value == ANIM_EVENT_NONE)
9976 // if list position is undefined, create new list
9977 if (list_pos == ANIM_EVENT_UNDEFINED)
9978 list_pos = AddGlobalAnimEventList();
9980 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
9981 struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
9982 int value_pos = gaeli->num_event_values++;
9984 gaeli->event_value = checked_realloc(gaeli->event_value,
9985 gaeli->num_event_values * sizeof(int *));
9987 gaeli->event_value[value_pos] = event_value;
9992 int GetGlobalAnimEventValue(int list_pos, int value_pos)
9994 if (list_pos == ANIM_EVENT_UNDEFINED)
9995 return ANIM_EVENT_NONE;
9997 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
9998 struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
10000 return gaeli->event_value[value_pos];
10003 int GetGlobalAnimEventValueCount(int list_pos)
10005 if (list_pos == ANIM_EVENT_UNDEFINED)
10008 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
10009 struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
10011 return gaeli->num_event_values;
10014 // This function checks if a string <s> of the format "string1, string2, ..."
10015 // exactly contains a string <s_contained>.
10017 static boolean string_has_parameter(char *s, char *s_contained)
10021 if (s == NULL || s_contained == NULL)
10024 if (strlen(s_contained) > strlen(s))
10027 if (strncmp(s, s_contained, strlen(s_contained)) == 0)
10029 char next_char = s[strlen(s_contained)];
10031 // check if next character is delimiter or whitespace
10032 return (next_char == ',' || next_char == '\0' ||
10033 next_char == ' ' || next_char == '\t' ? TRUE : FALSE);
10036 // check if string contains another parameter string after a comma
10037 substring = strchr(s, ',');
10038 if (substring == NULL) // string does not contain a comma
10041 // advance string pointer to next character after the comma
10044 // skip potential whitespaces after the comma
10045 while (*substring == ' ' || *substring == '\t')
10048 return string_has_parameter(substring, s_contained);
10051 static int get_anim_parameter_value(char *s)
10053 int event_value[] =
10061 char *pattern_1[] =
10069 char *pattern_2 = ".part_";
10070 char *matching_char = NULL;
10072 int pattern_1_len = 0;
10073 int result = ANIM_EVENT_NONE;
10076 for (i = 0; i < ARRAY_SIZE(event_value); i++)
10078 matching_char = strstr(s_ptr, pattern_1[i]);
10079 pattern_1_len = strlen(pattern_1[i]);
10080 result = event_value[i];
10082 if (matching_char != NULL)
10086 if (matching_char == NULL)
10087 return ANIM_EVENT_NONE;
10089 s_ptr = matching_char + pattern_1_len;
10091 // check for main animation number ("anim_X" or "anim_XX")
10092 if (*s_ptr >= '0' && *s_ptr <= '9')
10094 int gic_anim_nr = (*s_ptr++ - '0');
10096 if (*s_ptr >= '0' && *s_ptr <= '9')
10097 gic_anim_nr = 10 * gic_anim_nr + (*s_ptr++ - '0');
10099 if (gic_anim_nr < 1 || gic_anim_nr > MAX_GLOBAL_ANIMS)
10100 return ANIM_EVENT_NONE;
10102 result |= gic_anim_nr << ANIM_EVENT_ANIM_BIT;
10106 // invalid main animation number specified
10108 return ANIM_EVENT_NONE;
10111 // check for animation part number ("part_X" or "part_XX") (optional)
10112 if (strPrefix(s_ptr, pattern_2))
10114 s_ptr += strlen(pattern_2);
10116 if (*s_ptr >= '0' && *s_ptr <= '9')
10118 int gic_part_nr = (*s_ptr++ - '0');
10120 if (*s_ptr >= '0' && *s_ptr <= '9')
10121 gic_part_nr = 10 * gic_part_nr + (*s_ptr++ - '0');
10123 if (gic_part_nr < 1 || gic_part_nr > MAX_GLOBAL_ANIM_PARTS)
10124 return ANIM_EVENT_NONE;
10126 result |= gic_part_nr << ANIM_EVENT_PART_BIT;
10130 // invalid animation part number specified
10132 return ANIM_EVENT_NONE;
10136 // discard result if next character is neither delimiter nor whitespace
10137 if (!(*s_ptr == ',' || *s_ptr == '\0' ||
10138 *s_ptr == ' ' || *s_ptr == '\t'))
10139 return ANIM_EVENT_NONE;
10144 static int get_anim_parameter_values(char *s)
10146 int list_pos = ANIM_EVENT_UNDEFINED;
10147 int event_value = ANIM_EVENT_DEFAULT;
10149 if (string_has_parameter(s, "any"))
10150 event_value |= ANIM_EVENT_ANY;
10152 if (string_has_parameter(s, "click:self") ||
10153 string_has_parameter(s, "click") ||
10154 string_has_parameter(s, "self"))
10155 event_value |= ANIM_EVENT_SELF;
10157 if (string_has_parameter(s, "unclick:any"))
10158 event_value |= ANIM_EVENT_UNCLICK_ANY;
10160 // if animation event found, add it to global animation event list
10161 if (event_value != ANIM_EVENT_NONE)
10162 list_pos = AddGlobalAnimEventValue(list_pos, event_value);
10166 // add optional "click:anim_X" or "click:anim_X.part_X" parameter
10167 event_value = get_anim_parameter_value(s);
10169 // if animation event found, add it to global animation event list
10170 if (event_value != ANIM_EVENT_NONE)
10171 list_pos = AddGlobalAnimEventValue(list_pos, event_value);
10173 // continue with next part of the string, starting with next comma
10174 s = strchr(s + 1, ',');
10180 static int get_anim_action_parameter_value(char *token)
10182 int result = getImageIDFromToken(token);
10186 char *gfx_token = getStringCat2("gfx.", token);
10188 result = getImageIDFromToken(gfx_token);
10190 checked_free(gfx_token);
10195 Key key = getKeyFromX11KeyName(token);
10197 if (key != KSYM_UNDEFINED)
10198 result = -(int)key;
10202 result = ANIM_EVENT_ACTION_NONE;
10207 int get_parameter_value(char *value_raw, char *suffix, int type)
10209 char *value = getStringToLower(value_raw);
10210 int result = 0; // probably a save default value
10212 if (strEqual(suffix, ".direction"))
10214 result = (strEqual(value, "left") ? MV_LEFT :
10215 strEqual(value, "right") ? MV_RIGHT :
10216 strEqual(value, "up") ? MV_UP :
10217 strEqual(value, "down") ? MV_DOWN : MV_NONE);
10219 else if (strEqual(suffix, ".position"))
10221 result = (strEqual(value, "left") ? POS_LEFT :
10222 strEqual(value, "right") ? POS_RIGHT :
10223 strEqual(value, "top") ? POS_TOP :
10224 strEqual(value, "upper") ? POS_UPPER :
10225 strEqual(value, "middle") ? POS_MIDDLE :
10226 strEqual(value, "lower") ? POS_LOWER :
10227 strEqual(value, "bottom") ? POS_BOTTOM :
10228 strEqual(value, "any") ? POS_ANY :
10229 strEqual(value, "last") ? POS_LAST : POS_UNDEFINED);
10231 else if (strEqual(suffix, ".align"))
10233 result = (strEqual(value, "left") ? ALIGN_LEFT :
10234 strEqual(value, "right") ? ALIGN_RIGHT :
10235 strEqual(value, "center") ? ALIGN_CENTER :
10236 strEqual(value, "middle") ? ALIGN_CENTER : ALIGN_DEFAULT);
10238 else if (strEqual(suffix, ".valign"))
10240 result = (strEqual(value, "top") ? VALIGN_TOP :
10241 strEqual(value, "bottom") ? VALIGN_BOTTOM :
10242 strEqual(value, "middle") ? VALIGN_MIDDLE :
10243 strEqual(value, "center") ? VALIGN_MIDDLE : VALIGN_DEFAULT);
10245 else if (strEqual(suffix, ".anim_mode"))
10247 result = (string_has_parameter(value, "none") ? ANIM_NONE :
10248 string_has_parameter(value, "loop") ? ANIM_LOOP :
10249 string_has_parameter(value, "linear") ? ANIM_LINEAR :
10250 string_has_parameter(value, "pingpong") ? ANIM_PINGPONG :
10251 string_has_parameter(value, "pingpong2") ? ANIM_PINGPONG2 :
10252 string_has_parameter(value, "random") ? ANIM_RANDOM :
10253 string_has_parameter(value, "ce_value") ? ANIM_CE_VALUE :
10254 string_has_parameter(value, "ce_score") ? ANIM_CE_SCORE :
10255 string_has_parameter(value, "ce_delay") ? ANIM_CE_DELAY :
10256 string_has_parameter(value, "horizontal") ? ANIM_HORIZONTAL :
10257 string_has_parameter(value, "vertical") ? ANIM_VERTICAL :
10258 string_has_parameter(value, "centered") ? ANIM_CENTERED :
10259 string_has_parameter(value, "all") ? ANIM_ALL :
10262 if (string_has_parameter(value, "once"))
10263 result |= ANIM_ONCE;
10265 if (string_has_parameter(value, "reverse"))
10266 result |= ANIM_REVERSE;
10268 if (string_has_parameter(value, "opaque_player"))
10269 result |= ANIM_OPAQUE_PLAYER;
10271 if (string_has_parameter(value, "static_panel"))
10272 result |= ANIM_STATIC_PANEL;
10274 else if (strEqual(suffix, ".init_event") ||
10275 strEqual(suffix, ".anim_event"))
10277 result = get_anim_parameter_values(value);
10279 else if (strEqual(suffix, ".init_delay_action") ||
10280 strEqual(suffix, ".anim_delay_action") ||
10281 strEqual(suffix, ".post_delay_action") ||
10282 strEqual(suffix, ".init_event_action") ||
10283 strEqual(suffix, ".anim_event_action"))
10285 result = get_anim_action_parameter_value(value_raw);
10287 else if (strEqual(suffix, ".class"))
10289 result = (strEqual(value, ARG_UNDEFINED) ? ARG_UNDEFINED_VALUE :
10290 get_hash_from_key(value));
10292 else if (strEqual(suffix, ".style"))
10294 result = STYLE_DEFAULT;
10296 if (string_has_parameter(value, "accurate_borders"))
10297 result |= STYLE_ACCURATE_BORDERS;
10299 if (string_has_parameter(value, "inner_corners"))
10300 result |= STYLE_INNER_CORNERS;
10302 if (string_has_parameter(value, "reverse"))
10303 result |= STYLE_REVERSE;
10305 if (string_has_parameter(value, "passthrough_clicks"))
10306 result |= STYLE_PASSTHROUGH;
10308 if (string_has_parameter(value, "multiple_actions"))
10309 result |= STYLE_MULTIPLE_ACTIONS;
10311 else if (strEqual(suffix, ".fade_mode"))
10313 result = (string_has_parameter(value, "none") ? FADE_MODE_NONE :
10314 string_has_parameter(value, "fade") ? FADE_MODE_FADE :
10315 string_has_parameter(value, "crossfade") ? FADE_MODE_CROSSFADE :
10316 string_has_parameter(value, "melt") ? FADE_MODE_MELT :
10317 string_has_parameter(value, "curtain") ? FADE_MODE_CURTAIN :
10318 FADE_MODE_DEFAULT);
10320 else if (strEqual(suffix, ".auto_delay_unit"))
10322 result = (string_has_parameter(value, "ms") ? AUTO_DELAY_UNIT_MS :
10323 string_has_parameter(value, "frames") ? AUTO_DELAY_UNIT_FRAMES :
10324 AUTO_DELAY_UNIT_DEFAULT);
10326 else if (strPrefix(suffix, ".font")) // (may also be ".font_xyz")
10328 result = gfx.get_font_from_token_function(value);
10330 else // generic parameter of type integer or boolean
10332 result = (strEqual(value, ARG_UNDEFINED) ? ARG_UNDEFINED_VALUE :
10333 type == TYPE_INTEGER ? get_integer_from_string(value) :
10334 type == TYPE_BOOLEAN ? get_boolean_from_string(value) :
10335 ARG_UNDEFINED_VALUE);
10343 static int get_token_parameter_value(char *token, char *value_raw)
10347 if (token == NULL || value_raw == NULL)
10348 return ARG_UNDEFINED_VALUE;
10350 suffix = strrchr(token, '.');
10351 if (suffix == NULL)
10354 if (strEqual(suffix, ".element"))
10355 return getElementFromToken(value_raw);
10357 // !!! USE CORRECT VALUE TYPE (currently works also for TYPE_BOOLEAN) !!!
10358 return get_parameter_value(value_raw, suffix, TYPE_INTEGER);
10361 void InitMenuDesignSettings_Static(void)
10365 // always start with reliable default values from static default config
10366 for (i = 0; image_config_vars[i].token != NULL; i++)
10368 char *value = getHashEntry(image_config_hash, image_config_vars[i].token);
10371 *image_config_vars[i].value =
10372 get_token_parameter_value(image_config_vars[i].token, value);
10376 static void InitMenuDesignSettings_SpecialPreProcessing(void)
10380 // the following initializes hierarchical values from static configuration
10382 // special case: initialize "ARG_DEFAULT" values in static default config
10383 // (e.g., initialize "[titlemessage].fade_mode" from "[title].fade_mode")
10384 titlescreen_initial_first_default.fade_mode =
10385 title_initial_first_default.fade_mode;
10386 titlescreen_initial_first_default.fade_delay =
10387 title_initial_first_default.fade_delay;
10388 titlescreen_initial_first_default.post_delay =
10389 title_initial_first_default.post_delay;
10390 titlescreen_initial_first_default.auto_delay =
10391 title_initial_first_default.auto_delay;
10392 titlescreen_initial_first_default.auto_delay_unit =
10393 title_initial_first_default.auto_delay_unit;
10394 titlescreen_first_default.fade_mode = title_first_default.fade_mode;
10395 titlescreen_first_default.fade_delay = title_first_default.fade_delay;
10396 titlescreen_first_default.post_delay = title_first_default.post_delay;
10397 titlescreen_first_default.auto_delay = title_first_default.auto_delay;
10398 titlescreen_first_default.auto_delay_unit =
10399 title_first_default.auto_delay_unit;
10400 titlemessage_initial_first_default.fade_mode =
10401 title_initial_first_default.fade_mode;
10402 titlemessage_initial_first_default.fade_delay =
10403 title_initial_first_default.fade_delay;
10404 titlemessage_initial_first_default.post_delay =
10405 title_initial_first_default.post_delay;
10406 titlemessage_initial_first_default.auto_delay =
10407 title_initial_first_default.auto_delay;
10408 titlemessage_initial_first_default.auto_delay_unit =
10409 title_initial_first_default.auto_delay_unit;
10410 titlemessage_first_default.fade_mode = title_first_default.fade_mode;
10411 titlemessage_first_default.fade_delay = title_first_default.fade_delay;
10412 titlemessage_first_default.post_delay = title_first_default.post_delay;
10413 titlemessage_first_default.auto_delay = title_first_default.auto_delay;
10414 titlemessage_first_default.auto_delay_unit =
10415 title_first_default.auto_delay_unit;
10417 titlescreen_initial_default.fade_mode = title_initial_default.fade_mode;
10418 titlescreen_initial_default.fade_delay = title_initial_default.fade_delay;
10419 titlescreen_initial_default.post_delay = title_initial_default.post_delay;
10420 titlescreen_initial_default.auto_delay = title_initial_default.auto_delay;
10421 titlescreen_initial_default.auto_delay_unit =
10422 title_initial_default.auto_delay_unit;
10423 titlescreen_default.fade_mode = title_default.fade_mode;
10424 titlescreen_default.fade_delay = title_default.fade_delay;
10425 titlescreen_default.post_delay = title_default.post_delay;
10426 titlescreen_default.auto_delay = title_default.auto_delay;
10427 titlescreen_default.auto_delay_unit = title_default.auto_delay_unit;
10428 titlemessage_initial_default.fade_mode = title_initial_default.fade_mode;
10429 titlemessage_initial_default.fade_delay = title_initial_default.fade_delay;
10430 titlemessage_initial_default.post_delay = title_initial_default.post_delay;
10431 titlemessage_initial_default.auto_delay_unit =
10432 title_initial_default.auto_delay_unit;
10433 titlemessage_default.fade_mode = title_default.fade_mode;
10434 titlemessage_default.fade_delay = title_default.fade_delay;
10435 titlemessage_default.post_delay = title_default.post_delay;
10436 titlemessage_default.auto_delay = title_default.auto_delay;
10437 titlemessage_default.auto_delay_unit = title_default.auto_delay_unit;
10439 // special case: initialize "ARG_DEFAULT" values in static default config
10440 // (e.g., init "titlemessage_1.fade_mode" from "[titlemessage].fade_mode")
10441 for (i = 0; i < MAX_NUM_TITLE_MESSAGES; i++)
10443 titlescreen_initial_first[i] = titlescreen_initial_first_default;
10444 titlescreen_first[i] = titlescreen_first_default;
10445 titlemessage_initial_first[i] = titlemessage_initial_first_default;
10446 titlemessage_first[i] = titlemessage_first_default;
10448 titlescreen_initial[i] = titlescreen_initial_default;
10449 titlescreen[i] = titlescreen_default;
10450 titlemessage_initial[i] = titlemessage_initial_default;
10451 titlemessage[i] = titlemessage_default;
10454 // special case: initialize "ARG_DEFAULT" values in static default config
10455 // (eg, init "menu.enter_screen.SCORES.xyz" from "menu.enter_screen.xyz")
10456 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
10458 if (i == GFX_SPECIAL_ARG_TITLE) // title values already initialized
10461 menu.enter_screen[i] = menu.enter_screen[GFX_SPECIAL_ARG_DEFAULT];
10462 menu.leave_screen[i] = menu.leave_screen[GFX_SPECIAL_ARG_DEFAULT];
10463 menu.next_screen[i] = menu.next_screen[GFX_SPECIAL_ARG_DEFAULT];
10466 // special case: initialize "ARG_DEFAULT" values in static default config
10467 // (eg, init "viewport.door_1.MAIN.xyz" from "viewport.door_1.xyz")
10468 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
10470 viewport.window[i] = viewport.window[GFX_SPECIAL_ARG_DEFAULT];
10471 viewport.playfield[i] = viewport.playfield[GFX_SPECIAL_ARG_DEFAULT];
10472 viewport.door_1[i] = viewport.door_1[GFX_SPECIAL_ARG_DEFAULT];
10474 if (i == GFX_SPECIAL_ARG_EDITOR) // editor values already initialized
10477 viewport.door_2[i] = viewport.door_2[GFX_SPECIAL_ARG_DEFAULT];
10481 static void InitMenuDesignSettings_SpecialPostProcessing(void)
10485 struct XY *dst, *src;
10487 game_buttons_xy[] =
10489 { &game.button.save, &game.button.stop },
10490 { &game.button.pause2, &game.button.pause },
10491 { &game.button.load, &game.button.play },
10492 { &game.button.undo, &game.button.stop },
10493 { &game.button.redo, &game.button.play },
10499 // special case: initialize later added SETUP list size from LEVELS value
10500 if (menu.list_size[GAME_MODE_SETUP] == -1)
10501 menu.list_size[GAME_MODE_SETUP] = menu.list_size[GAME_MODE_LEVELS];
10503 // set default position for snapshot buttons to stop/pause/play buttons
10504 for (i = 0; game_buttons_xy[i].dst != NULL; i++)
10505 if ((*game_buttons_xy[i].dst).x == -1 &&
10506 (*game_buttons_xy[i].dst).y == -1)
10507 *game_buttons_xy[i].dst = *game_buttons_xy[i].src;
10509 // --------------------------------------------------------------------------
10510 // dynamic viewports (including playfield margins, borders and alignments)
10511 // --------------------------------------------------------------------------
10513 // dynamic viewports currently only supported for landscape mode
10514 int display_width = MAX(video.display_width, video.display_height);
10515 int display_height = MIN(video.display_width, video.display_height);
10517 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
10519 struct RectWithBorder *vp_window = &viewport.window[i];
10520 struct RectWithBorder *vp_playfield = &viewport.playfield[i];
10521 struct RectWithBorder *vp_door_1 = &viewport.door_1[i];
10522 struct RectWithBorder *vp_door_2 = &viewport.door_2[i];
10523 boolean dynamic_window_width = (vp_window->min_width != -1);
10524 boolean dynamic_window_height = (vp_window->min_height != -1);
10525 boolean dynamic_playfield_width = (vp_playfield->min_width != -1);
10526 boolean dynamic_playfield_height = (vp_playfield->min_height != -1);
10528 // adjust window size if min/max width/height is specified
10530 if (vp_window->min_width != -1)
10532 int window_width = display_width;
10534 // when using static window height, use aspect ratio of display
10535 if (vp_window->min_height == -1)
10536 window_width = vp_window->height * display_width / display_height;
10538 vp_window->width = MAX(vp_window->min_width, window_width);
10541 if (vp_window->min_height != -1)
10543 int window_height = display_height;
10545 // when using static window width, use aspect ratio of display
10546 if (vp_window->min_width == -1)
10547 window_height = vp_window->width * display_height / display_width;
10549 vp_window->height = MAX(vp_window->min_height, window_height);
10552 if (vp_window->max_width != -1)
10553 vp_window->width = MIN(vp_window->width, vp_window->max_width);
10555 if (vp_window->max_height != -1)
10556 vp_window->height = MIN(vp_window->height, vp_window->max_height);
10558 int playfield_width = vp_window->width;
10559 int playfield_height = vp_window->height;
10561 // adjust playfield size and position according to specified margins
10563 playfield_width -= vp_playfield->margin_left;
10564 playfield_width -= vp_playfield->margin_right;
10566 playfield_height -= vp_playfield->margin_top;
10567 playfield_height -= vp_playfield->margin_bottom;
10569 // adjust playfield size if min/max width/height is specified
10571 if (vp_playfield->min_width != -1)
10572 vp_playfield->width = MAX(vp_playfield->min_width, playfield_width);
10574 if (vp_playfield->min_height != -1)
10575 vp_playfield->height = MAX(vp_playfield->min_height, playfield_height);
10577 if (vp_playfield->max_width != -1)
10578 vp_playfield->width = MIN(vp_playfield->width, vp_playfield->max_width);
10580 if (vp_playfield->max_height != -1)
10581 vp_playfield->height = MIN(vp_playfield->height,vp_playfield->max_height);
10583 // adjust playfield position according to specified alignment
10585 if (vp_playfield->align == ALIGN_LEFT || vp_playfield->x > 0)
10586 vp_playfield->x = ALIGNED_VP_XPOS(vp_playfield);
10587 else if (vp_playfield->align == ALIGN_CENTER)
10588 vp_playfield->x = playfield_width / 2 - vp_playfield->width / 2;
10589 else if (vp_playfield->align == ALIGN_RIGHT)
10590 vp_playfield->x += playfield_width - vp_playfield->width;
10592 if (vp_playfield->valign == VALIGN_TOP || vp_playfield->y > 0)
10593 vp_playfield->y = ALIGNED_VP_YPOS(vp_playfield);
10594 else if (vp_playfield->valign == VALIGN_MIDDLE)
10595 vp_playfield->y = playfield_height / 2 - vp_playfield->height / 2;
10596 else if (vp_playfield->valign == VALIGN_BOTTOM)
10597 vp_playfield->y += playfield_height - vp_playfield->height;
10599 vp_playfield->x += vp_playfield->margin_left;
10600 vp_playfield->y += vp_playfield->margin_top;
10602 // adjust individual playfield borders if only default border is specified
10604 if (vp_playfield->border_left == -1)
10605 vp_playfield->border_left = vp_playfield->border_size;
10606 if (vp_playfield->border_right == -1)
10607 vp_playfield->border_right = vp_playfield->border_size;
10608 if (vp_playfield->border_top == -1)
10609 vp_playfield->border_top = vp_playfield->border_size;
10610 if (vp_playfield->border_bottom == -1)
10611 vp_playfield->border_bottom = vp_playfield->border_size;
10613 // set dynamic playfield borders if borders are specified as undefined
10614 // (but only if window size was dynamic and playfield size was static)
10616 if (dynamic_window_width && !dynamic_playfield_width)
10618 if (vp_playfield->border_left == -1)
10620 vp_playfield->border_left = (vp_playfield->x -
10621 vp_playfield->margin_left);
10622 vp_playfield->x -= vp_playfield->border_left;
10623 vp_playfield->width += vp_playfield->border_left;
10626 if (vp_playfield->border_right == -1)
10628 vp_playfield->border_right = (vp_window->width -
10630 vp_playfield->width -
10631 vp_playfield->margin_right);
10632 vp_playfield->width += vp_playfield->border_right;
10636 if (dynamic_window_height && !dynamic_playfield_height)
10638 if (vp_playfield->border_top == -1)
10640 vp_playfield->border_top = (vp_playfield->y -
10641 vp_playfield->margin_top);
10642 vp_playfield->y -= vp_playfield->border_top;
10643 vp_playfield->height += vp_playfield->border_top;
10646 if (vp_playfield->border_bottom == -1)
10648 vp_playfield->border_bottom = (vp_window->height -
10650 vp_playfield->height -
10651 vp_playfield->margin_bottom);
10652 vp_playfield->height += vp_playfield->border_bottom;
10656 // adjust playfield size to be a multiple of a defined alignment tile size
10658 int align_size = vp_playfield->align_size;
10659 int playfield_xtiles = vp_playfield->width / align_size;
10660 int playfield_ytiles = vp_playfield->height / align_size;
10661 int playfield_width_corrected = playfield_xtiles * align_size;
10662 int playfield_height_corrected = playfield_ytiles * align_size;
10663 boolean is_playfield_mode = (i == GFX_SPECIAL_ARG_PLAYING ||
10664 i == GFX_SPECIAL_ARG_EDITOR);
10666 if (is_playfield_mode &&
10667 dynamic_playfield_width &&
10668 vp_playfield->width != playfield_width_corrected)
10670 int playfield_xdiff = vp_playfield->width - playfield_width_corrected;
10672 vp_playfield->width = playfield_width_corrected;
10674 if (vp_playfield->align == ALIGN_LEFT)
10676 vp_playfield->border_left += playfield_xdiff;
10678 else if (vp_playfield->align == ALIGN_RIGHT)
10680 vp_playfield->border_right += playfield_xdiff;
10682 else if (vp_playfield->align == ALIGN_CENTER)
10684 int border_left_diff = playfield_xdiff / 2;
10685 int border_right_diff = playfield_xdiff - border_left_diff;
10687 vp_playfield->border_left += border_left_diff;
10688 vp_playfield->border_right += border_right_diff;
10692 if (is_playfield_mode &&
10693 dynamic_playfield_height &&
10694 vp_playfield->height != playfield_height_corrected)
10696 int playfield_ydiff = vp_playfield->height - playfield_height_corrected;
10698 vp_playfield->height = playfield_height_corrected;
10700 if (vp_playfield->valign == VALIGN_TOP)
10702 vp_playfield->border_top += playfield_ydiff;
10704 else if (vp_playfield->align == VALIGN_BOTTOM)
10706 vp_playfield->border_right += playfield_ydiff;
10708 else if (vp_playfield->align == VALIGN_MIDDLE)
10710 int border_top_diff = playfield_ydiff / 2;
10711 int border_bottom_diff = playfield_ydiff - border_top_diff;
10713 vp_playfield->border_top += border_top_diff;
10714 vp_playfield->border_bottom += border_bottom_diff;
10718 // adjust door positions according to specified alignment
10720 for (j = 0; j < 2; j++)
10722 struct RectWithBorder *vp_door = (j == 0 ? vp_door_1 : vp_door_2);
10724 if (vp_door->align == ALIGN_LEFT || vp_door->x > 0)
10725 vp_door->x = ALIGNED_VP_XPOS(vp_door);
10726 else if (vp_door->align == ALIGN_CENTER)
10727 vp_door->x = vp_window->width / 2 - vp_door->width / 2;
10728 else if (vp_door->align == ALIGN_RIGHT)
10729 vp_door->x += vp_window->width - vp_door->width;
10731 if (vp_door->valign == VALIGN_TOP || vp_door->y > 0)
10732 vp_door->y = ALIGNED_VP_YPOS(vp_door);
10733 else if (vp_door->valign == VALIGN_MIDDLE)
10734 vp_door->y = vp_window->height / 2 - vp_door->height / 2;
10735 else if (vp_door->valign == VALIGN_BOTTOM)
10736 vp_door->y += vp_window->height - vp_door->height;
10741 static void InitMenuDesignSettings_SpecialPostProcessing_AfterGraphics(void)
10745 struct XYTileSize *dst, *src;
10748 editor_buttons_xy[] =
10751 &editor.button.element_left, &editor.palette.element_left,
10752 IMG_GFX_EDITOR_BUTTON_ELEMENT_LEFT
10755 &editor.button.element_middle, &editor.palette.element_middle,
10756 IMG_GFX_EDITOR_BUTTON_ELEMENT_MIDDLE
10759 &editor.button.element_right, &editor.palette.element_right,
10760 IMG_GFX_EDITOR_BUTTON_ELEMENT_RIGHT
10767 // set default position for element buttons to element graphics
10768 for (i = 0; editor_buttons_xy[i].dst != NULL; i++)
10770 if ((*editor_buttons_xy[i].dst).x == -1 &&
10771 (*editor_buttons_xy[i].dst).y == -1)
10773 struct GraphicInfo *gd = &graphic_info[editor_buttons_xy[i].graphic];
10775 gd->width = gd->height = editor_buttons_xy[i].src->tile_size;
10777 *editor_buttons_xy[i].dst = *editor_buttons_xy[i].src;
10781 // adjust editor palette rows and columns if specified to be dynamic
10783 if (editor.palette.cols == -1)
10785 int vp_width = viewport.playfield[GFX_SPECIAL_ARG_EDITOR].width;
10786 int bt_width = graphic_info[IMG_EDITOR_PALETTE_BUTTON].width;
10787 int sc_width = graphic_info[IMG_EDITOR_PALETTE_SCROLLBAR].width;
10789 editor.palette.cols = (vp_width - sc_width) / bt_width;
10791 if (editor.palette.x == -1)
10793 int palette_width = editor.palette.cols * bt_width + sc_width;
10795 editor.palette.x = (vp_width - palette_width) / 2;
10799 if (editor.palette.rows == -1)
10801 int vp_height = viewport.playfield[GFX_SPECIAL_ARG_EDITOR].height;
10802 int bt_height = graphic_info[IMG_EDITOR_PALETTE_BUTTON].height;
10803 int tx_height = getFontHeight(FONT_TEXT_2);
10805 editor.palette.rows = (vp_height - tx_height) / bt_height;
10807 if (editor.palette.y == -1)
10809 int palette_height = editor.palette.rows * bt_height + tx_height;
10811 editor.palette.y = (vp_height - palette_height) / 2;
10816 static void LoadMenuDesignSettingsFromFilename(char *filename)
10818 static struct TitleFadingInfo tfi;
10819 static struct TitleMessageInfo tmi;
10820 static struct TokenInfo title_tokens[] =
10822 { TYPE_INTEGER, &tfi.fade_mode, ".fade_mode" },
10823 { TYPE_INTEGER, &tfi.fade_delay, ".fade_delay" },
10824 { TYPE_INTEGER, &tfi.post_delay, ".post_delay" },
10825 { TYPE_INTEGER, &tfi.auto_delay, ".auto_delay" },
10826 { TYPE_INTEGER, &tfi.auto_delay_unit, ".auto_delay_unit" },
10830 static struct TokenInfo titlemessage_tokens[] =
10832 { TYPE_INTEGER, &tmi.x, ".x" },
10833 { TYPE_INTEGER, &tmi.y, ".y" },
10834 { TYPE_INTEGER, &tmi.width, ".width" },
10835 { TYPE_INTEGER, &tmi.height, ".height" },
10836 { TYPE_INTEGER, &tmi.chars, ".chars" },
10837 { TYPE_INTEGER, &tmi.lines, ".lines" },
10838 { TYPE_INTEGER, &tmi.align, ".align" },
10839 { TYPE_INTEGER, &tmi.valign, ".valign" },
10840 { TYPE_INTEGER, &tmi.font, ".font" },
10841 { TYPE_BOOLEAN, &tmi.autowrap, ".autowrap" },
10842 { TYPE_BOOLEAN, &tmi.centered, ".centered" },
10843 { TYPE_BOOLEAN, &tmi.parse_comments, ".parse_comments" },
10844 { TYPE_INTEGER, &tmi.sort_priority, ".sort_priority" },
10845 { TYPE_INTEGER, &tmi.fade_mode, ".fade_mode" },
10846 { TYPE_INTEGER, &tmi.fade_delay, ".fade_delay" },
10847 { TYPE_INTEGER, &tmi.post_delay, ".post_delay" },
10848 { TYPE_INTEGER, &tmi.auto_delay, ".auto_delay" },
10849 { TYPE_INTEGER, &tmi.auto_delay_unit, ".auto_delay_unit" },
10855 struct TitleFadingInfo *info;
10860 // initialize first titles from "enter screen" definitions, if defined
10861 { &title_initial_first_default, "menu.enter_screen.TITLE" },
10862 { &title_first_default, "menu.enter_screen.TITLE" },
10864 // initialize title screens from "next screen" definitions, if defined
10865 { &title_initial_default, "menu.next_screen.TITLE" },
10866 { &title_default, "menu.next_screen.TITLE" },
10872 struct TitleMessageInfo *array;
10875 titlemessage_arrays[] =
10877 // initialize first titles from "enter screen" definitions, if defined
10878 { titlescreen_initial_first, "menu.enter_screen.TITLE" },
10879 { titlescreen_first, "menu.enter_screen.TITLE" },
10880 { titlemessage_initial_first, "menu.enter_screen.TITLE" },
10881 { titlemessage_first, "menu.enter_screen.TITLE" },
10883 // initialize titles from "next screen" definitions, if defined
10884 { titlescreen_initial, "menu.next_screen.TITLE" },
10885 { titlescreen, "menu.next_screen.TITLE" },
10886 { titlemessage_initial, "menu.next_screen.TITLE" },
10887 { titlemessage, "menu.next_screen.TITLE" },
10889 // overwrite titles with title definitions, if defined
10890 { titlescreen_initial_first, "[title_initial]" },
10891 { titlescreen_first, "[title]" },
10892 { titlemessage_initial_first, "[title_initial]" },
10893 { titlemessage_first, "[title]" },
10895 { titlescreen_initial, "[title_initial]" },
10896 { titlescreen, "[title]" },
10897 { titlemessage_initial, "[title_initial]" },
10898 { titlemessage, "[title]" },
10900 // overwrite titles with title screen/message definitions, if defined
10901 { titlescreen_initial_first, "[titlescreen_initial]" },
10902 { titlescreen_first, "[titlescreen]" },
10903 { titlemessage_initial_first, "[titlemessage_initial]" },
10904 { titlemessage_first, "[titlemessage]" },
10906 { titlescreen_initial, "[titlescreen_initial]" },
10907 { titlescreen, "[titlescreen]" },
10908 { titlemessage_initial, "[titlemessage_initial]" },
10909 { titlemessage, "[titlemessage]" },
10913 SetupFileHash *setup_file_hash;
10916 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
10919 // the following initializes hierarchical values from dynamic configuration
10921 // special case: initialize with default values that may be overwritten
10922 // (e.g., init "menu.draw_xoffset.INFO" from "menu.draw_xoffset")
10923 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
10925 struct TokenIntPtrInfo menu_config[] =
10927 { "menu.draw_xoffset", &menu.draw_xoffset[i] },
10928 { "menu.draw_yoffset", &menu.draw_yoffset[i] },
10929 { "menu.list_size", &menu.list_size[i] }
10932 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
10934 char *token = menu_config[j].token;
10935 char *value = getHashEntry(setup_file_hash, token);
10938 *menu_config[j].value = get_integer_from_string(value);
10942 // special case: initialize with default values that may be overwritten
10943 // (eg, init "menu.draw_xoffset.INFO[XXX]" from "menu.draw_xoffset.INFO")
10944 for (i = 0; i < NUM_SPECIAL_GFX_INFO_ARGS; i++)
10946 struct TokenIntPtrInfo menu_config[] =
10948 { "menu.draw_xoffset.INFO", &menu.draw_xoffset_info[i] },
10949 { "menu.draw_yoffset.INFO", &menu.draw_yoffset_info[i] },
10950 { "menu.list_size.INFO", &menu.list_size_info[i] }
10953 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
10955 char *token = menu_config[j].token;
10956 char *value = getHashEntry(setup_file_hash, token);
10959 *menu_config[j].value = get_integer_from_string(value);
10963 // special case: initialize with default values that may be overwritten
10964 // (eg, init "menu.draw_xoffset.SETUP[XXX]" from "menu.draw_xoffset.SETUP")
10965 for (i = 0; i < NUM_SPECIAL_GFX_SETUP_ARGS; i++)
10967 struct TokenIntPtrInfo menu_config[] =
10969 { "menu.draw_xoffset.SETUP", &menu.draw_xoffset_setup[i] },
10970 { "menu.draw_yoffset.SETUP", &menu.draw_yoffset_setup[i] }
10973 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
10975 char *token = menu_config[j].token;
10976 char *value = getHashEntry(setup_file_hash, token);
10979 *menu_config[j].value = get_integer_from_string(value);
10983 // special case: initialize with default values that may be overwritten
10984 // (eg, init "menu.line_spacing.INFO[XXX]" from "menu.line_spacing.INFO")
10985 for (i = 0; i < NUM_SPECIAL_GFX_INFO_ARGS; i++)
10987 struct TokenIntPtrInfo menu_config[] =
10989 { "menu.left_spacing.INFO", &menu.left_spacing_info[i] },
10990 { "menu.right_spacing.INFO", &menu.right_spacing_info[i] },
10991 { "menu.top_spacing.INFO", &menu.top_spacing_info[i] },
10992 { "menu.bottom_spacing.INFO", &menu.bottom_spacing_info[i] },
10993 { "menu.paragraph_spacing.INFO", &menu.paragraph_spacing_info[i] },
10994 { "menu.headline1_spacing.INFO", &menu.headline1_spacing_info[i] },
10995 { "menu.headline2_spacing.INFO", &menu.headline2_spacing_info[i] },
10996 { "menu.line_spacing.INFO", &menu.line_spacing_info[i] },
10997 { "menu.extra_spacing.INFO", &menu.extra_spacing_info[i] },
11000 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
11002 char *token = menu_config[j].token;
11003 char *value = getHashEntry(setup_file_hash, token);
11006 *menu_config[j].value = get_integer_from_string(value);
11010 // special case: initialize with default values that may be overwritten
11011 // (eg, init "menu.enter_screen.SCORES.xyz" from "menu.enter_screen.xyz")
11012 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
11014 struct TokenIntPtrInfo menu_config[] =
11016 { "menu.enter_screen.fade_mode", &menu.enter_screen[i].fade_mode },
11017 { "menu.enter_screen.fade_delay", &menu.enter_screen[i].fade_delay },
11018 { "menu.enter_screen.post_delay", &menu.enter_screen[i].post_delay },
11019 { "menu.leave_screen.fade_mode", &menu.leave_screen[i].fade_mode },
11020 { "menu.leave_screen.fade_delay", &menu.leave_screen[i].fade_delay },
11021 { "menu.leave_screen.post_delay", &menu.leave_screen[i].post_delay },
11022 { "menu.next_screen.fade_mode", &menu.next_screen[i].fade_mode },
11023 { "menu.next_screen.fade_delay", &menu.next_screen[i].fade_delay },
11024 { "menu.next_screen.post_delay", &menu.next_screen[i].post_delay }
11027 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
11029 char *token = menu_config[j].token;
11030 char *value = getHashEntry(setup_file_hash, token);
11033 *menu_config[j].value = get_token_parameter_value(token, value);
11037 // special case: initialize with default values that may be overwritten
11038 // (eg, init "viewport.door_1.MAIN.xyz" from "viewport.door_1.xyz")
11039 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
11043 char *token_prefix;
11044 struct RectWithBorder *struct_ptr;
11048 { "viewport.window", &viewport.window[i] },
11049 { "viewport.playfield", &viewport.playfield[i] },
11050 { "viewport.door_1", &viewport.door_1[i] },
11051 { "viewport.door_2", &viewport.door_2[i] }
11054 for (j = 0; j < ARRAY_SIZE(vp_struct); j++)
11056 struct TokenIntPtrInfo vp_config[] =
11058 { ".x", &vp_struct[j].struct_ptr->x },
11059 { ".y", &vp_struct[j].struct_ptr->y },
11060 { ".width", &vp_struct[j].struct_ptr->width },
11061 { ".height", &vp_struct[j].struct_ptr->height },
11062 { ".min_width", &vp_struct[j].struct_ptr->min_width },
11063 { ".min_height", &vp_struct[j].struct_ptr->min_height },
11064 { ".max_width", &vp_struct[j].struct_ptr->max_width },
11065 { ".max_height", &vp_struct[j].struct_ptr->max_height },
11066 { ".margin_left", &vp_struct[j].struct_ptr->margin_left },
11067 { ".margin_right", &vp_struct[j].struct_ptr->margin_right },
11068 { ".margin_top", &vp_struct[j].struct_ptr->margin_top },
11069 { ".margin_bottom", &vp_struct[j].struct_ptr->margin_bottom },
11070 { ".border_left", &vp_struct[j].struct_ptr->border_left },
11071 { ".border_right", &vp_struct[j].struct_ptr->border_right },
11072 { ".border_top", &vp_struct[j].struct_ptr->border_top },
11073 { ".border_bottom", &vp_struct[j].struct_ptr->border_bottom },
11074 { ".border_size", &vp_struct[j].struct_ptr->border_size },
11075 { ".align_size", &vp_struct[j].struct_ptr->align_size },
11076 { ".align", &vp_struct[j].struct_ptr->align },
11077 { ".valign", &vp_struct[j].struct_ptr->valign }
11080 for (k = 0; k < ARRAY_SIZE(vp_config); k++)
11082 char *token = getStringCat2(vp_struct[j].token_prefix,
11083 vp_config[k].token);
11084 char *value = getHashEntry(setup_file_hash, token);
11087 *vp_config[k].value = get_token_parameter_value(token, value);
11094 // special case: initialize with default values that may be overwritten
11095 // (e.g., init "[title].fade_mode" from "menu.next_screen.TITLE.fade_mode")
11096 for (i = 0; title_info[i].info != NULL; i++)
11098 struct TitleFadingInfo *info = title_info[i].info;
11099 char *base_token = title_info[i].text;
11101 for (j = 0; title_tokens[j].type != -1; j++)
11103 char *token = getStringCat2(base_token, title_tokens[j].text);
11104 char *value = getHashEntry(setup_file_hash, token);
11108 int parameter_value = get_token_parameter_value(token, value);
11112 *(int *)title_tokens[j].value = (int)parameter_value;
11121 // special case: initialize with default values that may be overwritten
11122 // (e.g., init "titlemessage_1.fade_mode" from "[titlemessage].fade_mode")
11123 for (i = 0; titlemessage_arrays[i].array != NULL; i++)
11125 struct TitleMessageInfo *array = titlemessage_arrays[i].array;
11126 char *base_token = titlemessage_arrays[i].text;
11128 for (j = 0; titlemessage_tokens[j].type != -1; j++)
11130 char *token = getStringCat2(base_token, titlemessage_tokens[j].text);
11131 char *value = getHashEntry(setup_file_hash, token);
11135 int parameter_value = get_token_parameter_value(token, value);
11137 for (k = 0; k < MAX_NUM_TITLE_MESSAGES; k++)
11141 if (titlemessage_tokens[j].type == TYPE_INTEGER)
11142 *(int *)titlemessage_tokens[j].value = (int)parameter_value;
11144 *(boolean *)titlemessage_tokens[j].value = (boolean)parameter_value;
11154 // special case: check if network and preview player positions are redefined,
11155 // to compare this later against the main menu level preview being redefined
11156 struct TokenIntPtrInfo menu_config_players[] =
11158 { "main.network_players.x", &menu.main.network_players.redefined },
11159 { "main.network_players.y", &menu.main.network_players.redefined },
11160 { "main.preview_players.x", &menu.main.preview_players.redefined },
11161 { "main.preview_players.y", &menu.main.preview_players.redefined },
11162 { "preview.x", &preview.redefined },
11163 { "preview.y", &preview.redefined }
11166 for (i = 0; i < ARRAY_SIZE(menu_config_players); i++)
11167 *menu_config_players[i].value = FALSE;
11169 for (i = 0; i < ARRAY_SIZE(menu_config_players); i++)
11170 if (getHashEntry(setup_file_hash, menu_config_players[i].token) != NULL)
11171 *menu_config_players[i].value = TRUE;
11173 // read (and overwrite with) values that may be specified in config file
11174 for (i = 0; image_config_vars[i].token != NULL; i++)
11176 char *value = getHashEntry(setup_file_hash, image_config_vars[i].token);
11178 // (ignore definitions set to "[DEFAULT]" which are already initialized)
11179 if (value != NULL && !strEqual(value, ARG_DEFAULT))
11180 *image_config_vars[i].value =
11181 get_token_parameter_value(image_config_vars[i].token, value);
11184 freeSetupFileHash(setup_file_hash);
11187 void LoadMenuDesignSettings(void)
11189 char *filename_base = UNDEFINED_FILENAME, *filename_local;
11191 InitMenuDesignSettings_Static();
11192 InitMenuDesignSettings_SpecialPreProcessing();
11194 if (!GFX_OVERRIDE_ARTWORK(ARTWORK_TYPE_GRAPHICS))
11196 // first look for special settings configured in level series config
11197 filename_base = getCustomArtworkLevelConfigFilename(ARTWORK_TYPE_GRAPHICS);
11199 if (fileExists(filename_base))
11200 LoadMenuDesignSettingsFromFilename(filename_base);
11203 filename_local = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
11205 if (filename_local != NULL && !strEqual(filename_base, filename_local))
11206 LoadMenuDesignSettingsFromFilename(filename_local);
11208 InitMenuDesignSettings_SpecialPostProcessing();
11211 void LoadMenuDesignSettings_AfterGraphics(void)
11213 InitMenuDesignSettings_SpecialPostProcessing_AfterGraphics();
11216 void LoadUserDefinedEditorElementList(int **elements, int *num_elements)
11218 char *filename = getEditorSetupFilename();
11219 SetupFileList *setup_file_list, *list;
11220 SetupFileHash *element_hash;
11221 int num_unknown_tokens = 0;
11224 if ((setup_file_list = loadSetupFileList(filename)) == NULL)
11227 element_hash = newSetupFileHash();
11229 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
11230 setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
11232 // determined size may be larger than needed (due to unknown elements)
11234 for (list = setup_file_list; list != NULL; list = list->next)
11237 // add space for up to 3 more elements for padding that may be needed
11238 *num_elements += 3;
11240 // free memory for old list of elements, if needed
11241 checked_free(*elements);
11243 // allocate memory for new list of elements
11244 *elements = checked_malloc(*num_elements * sizeof(int));
11247 for (list = setup_file_list; list != NULL; list = list->next)
11249 char *value = getHashEntry(element_hash, list->token);
11251 if (value == NULL) // try to find obsolete token mapping
11253 char *mapped_token = get_mapped_token(list->token);
11255 if (mapped_token != NULL)
11257 value = getHashEntry(element_hash, mapped_token);
11259 free(mapped_token);
11265 (*elements)[(*num_elements)++] = atoi(value);
11269 if (num_unknown_tokens == 0)
11271 Error(ERR_INFO_LINE, "-");
11272 Error(ERR_INFO, "warning: unknown token(s) found in config file:");
11273 Error(ERR_INFO, "- config file: '%s'", filename);
11275 num_unknown_tokens++;
11278 Error(ERR_INFO, "- token: '%s'", list->token);
11282 if (num_unknown_tokens > 0)
11283 Error(ERR_INFO_LINE, "-");
11285 while (*num_elements % 4) // pad with empty elements, if needed
11286 (*elements)[(*num_elements)++] = EL_EMPTY;
11288 freeSetupFileList(setup_file_list);
11289 freeSetupFileHash(element_hash);
11292 for (i = 0; i < *num_elements; i++)
11293 printf("editor: element '%s' [%d]\n",
11294 element_info[(*elements)[i]].token_name, (*elements)[i]);
11298 static struct MusicFileInfo *get_music_file_info_ext(char *basename, int music,
11301 SetupFileHash *setup_file_hash = NULL;
11302 struct MusicFileInfo tmp_music_file_info, *new_music_file_info;
11303 char *filename_music, *filename_prefix, *filename_info;
11309 token_to_value_ptr[] =
11311 { "title_header", &tmp_music_file_info.title_header },
11312 { "artist_header", &tmp_music_file_info.artist_header },
11313 { "album_header", &tmp_music_file_info.album_header },
11314 { "year_header", &tmp_music_file_info.year_header },
11316 { "title", &tmp_music_file_info.title },
11317 { "artist", &tmp_music_file_info.artist },
11318 { "album", &tmp_music_file_info.album },
11319 { "year", &tmp_music_file_info.year },
11325 filename_music = (is_sound ? getCustomSoundFilename(basename) :
11326 getCustomMusicFilename(basename));
11328 if (filename_music == NULL)
11331 // ---------- try to replace file extension ----------
11333 filename_prefix = getStringCopy(filename_music);
11334 if (strrchr(filename_prefix, '.') != NULL)
11335 *strrchr(filename_prefix, '.') = '\0';
11336 filename_info = getStringCat2(filename_prefix, ".txt");
11338 if (fileExists(filename_info))
11339 setup_file_hash = loadSetupFileHash(filename_info);
11341 free(filename_prefix);
11342 free(filename_info);
11344 if (setup_file_hash == NULL)
11346 // ---------- try to add file extension ----------
11348 filename_prefix = getStringCopy(filename_music);
11349 filename_info = getStringCat2(filename_prefix, ".txt");
11351 if (fileExists(filename_info))
11352 setup_file_hash = loadSetupFileHash(filename_info);
11354 free(filename_prefix);
11355 free(filename_info);
11358 if (setup_file_hash == NULL)
11361 // ---------- music file info found ----------
11363 clear_mem(&tmp_music_file_info, sizeof(struct MusicFileInfo));
11365 for (i = 0; token_to_value_ptr[i].token != NULL; i++)
11367 char *value = getHashEntry(setup_file_hash, token_to_value_ptr[i].token);
11369 *token_to_value_ptr[i].value_ptr =
11370 getStringCopy(value != NULL && *value != '\0' ? value : UNKNOWN_NAME);
11373 tmp_music_file_info.basename = getStringCopy(basename);
11374 tmp_music_file_info.music = music;
11375 tmp_music_file_info.is_sound = is_sound;
11377 new_music_file_info = checked_malloc(sizeof(struct MusicFileInfo));
11378 *new_music_file_info = tmp_music_file_info;
11380 return new_music_file_info;
11383 static struct MusicFileInfo *get_music_file_info(char *basename, int music)
11385 return get_music_file_info_ext(basename, music, FALSE);
11388 static struct MusicFileInfo *get_sound_file_info(char *basename, int sound)
11390 return get_music_file_info_ext(basename, sound, TRUE);
11393 static boolean music_info_listed_ext(struct MusicFileInfo *list,
11394 char *basename, boolean is_sound)
11396 for (; list != NULL; list = list->next)
11397 if (list->is_sound == is_sound && strEqual(list->basename, basename))
11403 static boolean music_info_listed(struct MusicFileInfo *list, char *basename)
11405 return music_info_listed_ext(list, basename, FALSE);
11408 static boolean sound_info_listed(struct MusicFileInfo *list, char *basename)
11410 return music_info_listed_ext(list, basename, TRUE);
11413 void LoadMusicInfo(void)
11415 char *music_directory = getCustomMusicDirectory();
11416 int num_music = getMusicListSize();
11417 int num_music_noconf = 0;
11418 int num_sounds = getSoundListSize();
11420 DirectoryEntry *dir_entry;
11421 struct FileInfo *music, *sound;
11422 struct MusicFileInfo *next, **new;
11425 while (music_file_info != NULL)
11427 next = music_file_info->next;
11429 checked_free(music_file_info->basename);
11431 checked_free(music_file_info->title_header);
11432 checked_free(music_file_info->artist_header);
11433 checked_free(music_file_info->album_header);
11434 checked_free(music_file_info->year_header);
11436 checked_free(music_file_info->title);
11437 checked_free(music_file_info->artist);
11438 checked_free(music_file_info->album);
11439 checked_free(music_file_info->year);
11441 free(music_file_info);
11443 music_file_info = next;
11446 new = &music_file_info;
11448 for (i = 0; i < num_music; i++)
11450 music = getMusicListEntry(i);
11452 if (music->filename == NULL)
11455 if (strEqual(music->filename, UNDEFINED_FILENAME))
11458 // a configured file may be not recognized as music
11459 if (!FileIsMusic(music->filename))
11462 if (!music_info_listed(music_file_info, music->filename))
11464 *new = get_music_file_info(music->filename, i);
11467 new = &(*new)->next;
11471 if ((dir = openDirectory(music_directory)) == NULL)
11473 Error(ERR_WARN, "cannot read music directory '%s'", music_directory);
11477 while ((dir_entry = readDirectory(dir)) != NULL) // loop all entries
11479 char *basename = dir_entry->basename;
11480 boolean music_already_used = FALSE;
11483 // skip all music files that are configured in music config file
11484 for (i = 0; i < num_music; i++)
11486 music = getMusicListEntry(i);
11488 if (music->filename == NULL)
11491 if (strEqual(basename, music->filename))
11493 music_already_used = TRUE;
11498 if (music_already_used)
11501 if (!FileIsMusic(dir_entry->filename))
11504 if (!music_info_listed(music_file_info, basename))
11506 *new = get_music_file_info(basename, MAP_NOCONF_MUSIC(num_music_noconf));
11509 new = &(*new)->next;
11512 num_music_noconf++;
11515 closeDirectory(dir);
11517 for (i = 0; i < num_sounds; i++)
11519 sound = getSoundListEntry(i);
11521 if (sound->filename == NULL)
11524 if (strEqual(sound->filename, UNDEFINED_FILENAME))
11527 // a configured file may be not recognized as sound
11528 if (!FileIsSound(sound->filename))
11531 if (!sound_info_listed(music_file_info, sound->filename))
11533 *new = get_sound_file_info(sound->filename, i);
11535 new = &(*new)->next;
11540 static void add_helpanim_entry(int element, int action, int direction,
11541 int delay, int *num_list_entries)
11543 struct HelpAnimInfo *new_list_entry;
11544 (*num_list_entries)++;
11547 checked_realloc(helpanim_info,
11548 *num_list_entries * sizeof(struct HelpAnimInfo));
11549 new_list_entry = &helpanim_info[*num_list_entries - 1];
11551 new_list_entry->element = element;
11552 new_list_entry->action = action;
11553 new_list_entry->direction = direction;
11554 new_list_entry->delay = delay;
11557 static void print_unknown_token(char *filename, char *token, int token_nr)
11561 Error(ERR_INFO_LINE, "-");
11562 Error(ERR_INFO, "warning: unknown token(s) found in config file:");
11563 Error(ERR_INFO, "- config file: '%s'", filename);
11566 Error(ERR_INFO, "- token: '%s'", token);
11569 static void print_unknown_token_end(int token_nr)
11572 Error(ERR_INFO_LINE, "-");
11575 void LoadHelpAnimInfo(void)
11577 char *filename = getHelpAnimFilename();
11578 SetupFileList *setup_file_list = NULL, *list;
11579 SetupFileHash *element_hash, *action_hash, *direction_hash;
11580 int num_list_entries = 0;
11581 int num_unknown_tokens = 0;
11584 if (fileExists(filename))
11585 setup_file_list = loadSetupFileList(filename);
11587 if (setup_file_list == NULL)
11589 // use reliable default values from static configuration
11590 SetupFileList *insert_ptr;
11592 insert_ptr = setup_file_list =
11593 newSetupFileList(helpanim_config[0].token,
11594 helpanim_config[0].value);
11596 for (i = 1; helpanim_config[i].token; i++)
11597 insert_ptr = addListEntry(insert_ptr,
11598 helpanim_config[i].token,
11599 helpanim_config[i].value);
11602 element_hash = newSetupFileHash();
11603 action_hash = newSetupFileHash();
11604 direction_hash = newSetupFileHash();
11606 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
11607 setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
11609 for (i = 0; i < NUM_ACTIONS; i++)
11610 setHashEntry(action_hash, element_action_info[i].suffix,
11611 i_to_a(element_action_info[i].value));
11613 // do not store direction index (bit) here, but direction value!
11614 for (i = 0; i < NUM_DIRECTIONS_FULL; i++)
11615 setHashEntry(direction_hash, element_direction_info[i].suffix,
11616 i_to_a(1 << element_direction_info[i].value));
11618 for (list = setup_file_list; list != NULL; list = list->next)
11620 char *element_token, *action_token, *direction_token;
11621 char *element_value, *action_value, *direction_value;
11622 int delay = atoi(list->value);
11624 if (strEqual(list->token, "end"))
11626 add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
11631 /* first try to break element into element/action/direction parts;
11632 if this does not work, also accept combined "element[.act][.dir]"
11633 elements (like "dynamite.active"), which are unique elements */
11635 if (strchr(list->token, '.') == NULL) // token contains no '.'
11637 element_value = getHashEntry(element_hash, list->token);
11638 if (element_value != NULL) // element found
11639 add_helpanim_entry(atoi(element_value), -1, -1, delay,
11640 &num_list_entries);
11643 // no further suffixes found -- this is not an element
11644 print_unknown_token(filename, list->token, num_unknown_tokens++);
11650 // token has format "<prefix>.<something>"
11652 action_token = strchr(list->token, '.'); // suffix may be action ...
11653 direction_token = action_token; // ... or direction
11655 element_token = getStringCopy(list->token);
11656 *strchr(element_token, '.') = '\0';
11658 element_value = getHashEntry(element_hash, element_token);
11660 if (element_value == NULL) // this is no element
11662 element_value = getHashEntry(element_hash, list->token);
11663 if (element_value != NULL) // combined element found
11664 add_helpanim_entry(atoi(element_value), -1, -1, delay,
11665 &num_list_entries);
11667 print_unknown_token(filename, list->token, num_unknown_tokens++);
11669 free(element_token);
11674 action_value = getHashEntry(action_hash, action_token);
11676 if (action_value != NULL) // action found
11678 add_helpanim_entry(atoi(element_value), atoi(action_value), -1, delay,
11679 &num_list_entries);
11681 free(element_token);
11686 direction_value = getHashEntry(direction_hash, direction_token);
11688 if (direction_value != NULL) // direction found
11690 add_helpanim_entry(atoi(element_value), -1, atoi(direction_value), delay,
11691 &num_list_entries);
11693 free(element_token);
11698 if (strchr(action_token + 1, '.') == NULL)
11700 // no further suffixes found -- this is not an action nor direction
11702 element_value = getHashEntry(element_hash, list->token);
11703 if (element_value != NULL) // combined element found
11704 add_helpanim_entry(atoi(element_value), -1, -1, delay,
11705 &num_list_entries);
11707 print_unknown_token(filename, list->token, num_unknown_tokens++);
11709 free(element_token);
11714 // token has format "<prefix>.<suffix>.<something>"
11716 direction_token = strchr(action_token + 1, '.');
11718 action_token = getStringCopy(action_token);
11719 *strchr(action_token + 1, '.') = '\0';
11721 action_value = getHashEntry(action_hash, action_token);
11723 if (action_value == NULL) // this is no action
11725 element_value = getHashEntry(element_hash, list->token);
11726 if (element_value != NULL) // combined element found
11727 add_helpanim_entry(atoi(element_value), -1, -1, delay,
11728 &num_list_entries);
11730 print_unknown_token(filename, list->token, num_unknown_tokens++);
11732 free(element_token);
11733 free(action_token);
11738 direction_value = getHashEntry(direction_hash, direction_token);
11740 if (direction_value != NULL) // direction found
11742 add_helpanim_entry(atoi(element_value), atoi(action_value),
11743 atoi(direction_value), delay, &num_list_entries);
11745 free(element_token);
11746 free(action_token);
11751 // this is no direction
11753 element_value = getHashEntry(element_hash, list->token);
11754 if (element_value != NULL) // combined element found
11755 add_helpanim_entry(atoi(element_value), -1, -1, delay,
11756 &num_list_entries);
11758 print_unknown_token(filename, list->token, num_unknown_tokens++);
11760 free(element_token);
11761 free(action_token);
11764 print_unknown_token_end(num_unknown_tokens);
11766 add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
11767 add_helpanim_entry(HELPANIM_LIST_END, -1, -1, -1, &num_list_entries);
11769 freeSetupFileList(setup_file_list);
11770 freeSetupFileHash(element_hash);
11771 freeSetupFileHash(action_hash);
11772 freeSetupFileHash(direction_hash);
11775 for (i = 0; i < num_list_entries; i++)
11776 printf("::: '%s': %d, %d, %d => %d\n",
11777 EL_NAME(helpanim_info[i].element),
11778 helpanim_info[i].element,
11779 helpanim_info[i].action,
11780 helpanim_info[i].direction,
11781 helpanim_info[i].delay);
11785 void LoadHelpTextInfo(void)
11787 char *filename = getHelpTextFilename();
11790 if (helptext_info != NULL)
11792 freeSetupFileHash(helptext_info);
11793 helptext_info = NULL;
11796 if (fileExists(filename))
11797 helptext_info = loadSetupFileHash(filename);
11799 if (helptext_info == NULL)
11801 // use reliable default values from static configuration
11802 helptext_info = newSetupFileHash();
11804 for (i = 0; helptext_config[i].token; i++)
11805 setHashEntry(helptext_info,
11806 helptext_config[i].token,
11807 helptext_config[i].value);
11811 BEGIN_HASH_ITERATION(helptext_info, itr)
11813 printf("::: '%s' => '%s'\n",
11814 HASH_ITERATION_TOKEN(itr), HASH_ITERATION_VALUE(itr));
11816 END_HASH_ITERATION(hash, itr)
11821 // ----------------------------------------------------------------------------
11823 // ----------------------------------------------------------------------------
11825 #define MAX_NUM_CONVERT_LEVELS 1000
11827 void ConvertLevels(void)
11829 static LevelDirTree *convert_leveldir = NULL;
11830 static int convert_level_nr = -1;
11831 static int num_levels_handled = 0;
11832 static int num_levels_converted = 0;
11833 static boolean levels_failed[MAX_NUM_CONVERT_LEVELS];
11836 convert_leveldir = getTreeInfoFromIdentifier(leveldir_first,
11837 global.convert_leveldir);
11839 if (convert_leveldir == NULL)
11840 Error(ERR_EXIT, "no such level identifier: '%s'",
11841 global.convert_leveldir);
11843 leveldir_current = convert_leveldir;
11845 if (global.convert_level_nr != -1)
11847 convert_leveldir->first_level = global.convert_level_nr;
11848 convert_leveldir->last_level = global.convert_level_nr;
11851 convert_level_nr = convert_leveldir->first_level;
11853 PrintLine("=", 79);
11854 Print("Converting levels\n");
11855 PrintLine("-", 79);
11856 Print("Level series identifier: '%s'\n", convert_leveldir->identifier);
11857 Print("Level series name: '%s'\n", convert_leveldir->name);
11858 Print("Level series author: '%s'\n", convert_leveldir->author);
11859 Print("Number of levels: %d\n", convert_leveldir->levels);
11860 PrintLine("=", 79);
11863 for (i = 0; i < MAX_NUM_CONVERT_LEVELS; i++)
11864 levels_failed[i] = FALSE;
11866 while (convert_level_nr <= convert_leveldir->last_level)
11868 char *level_filename;
11871 level_nr = convert_level_nr++;
11873 Print("Level %03d: ", level_nr);
11875 LoadLevel(level_nr);
11876 if (level.no_level_file || level.no_valid_file)
11878 Print("(no level)\n");
11882 Print("converting level ... ");
11884 level_filename = getDefaultLevelFilename(level_nr);
11885 new_level = !fileExists(level_filename);
11889 SaveLevel(level_nr);
11891 num_levels_converted++;
11893 Print("converted.\n");
11897 if (level_nr >= 0 && level_nr < MAX_NUM_CONVERT_LEVELS)
11898 levels_failed[level_nr] = TRUE;
11900 Print("NOT CONVERTED -- LEVEL ALREADY EXISTS.\n");
11903 num_levels_handled++;
11907 PrintLine("=", 79);
11908 Print("Number of levels handled: %d\n", num_levels_handled);
11909 Print("Number of levels converted: %d (%d%%)\n", num_levels_converted,
11910 (num_levels_handled ?
11911 num_levels_converted * 100 / num_levels_handled : 0));
11912 PrintLine("-", 79);
11913 Print("Summary (for automatic parsing by scripts):\n");
11914 Print("LEVELDIR '%s', CONVERTED %d/%d (%d%%)",
11915 convert_leveldir->identifier, num_levels_converted,
11916 num_levels_handled,
11917 (num_levels_handled ?
11918 num_levels_converted * 100 / num_levels_handled : 0));
11920 if (num_levels_handled != num_levels_converted)
11922 Print(", FAILED:");
11923 for (i = 0; i < MAX_NUM_CONVERT_LEVELS; i++)
11924 if (levels_failed[i])
11929 PrintLine("=", 79);
11931 CloseAllAndExit(0);
11935 // ----------------------------------------------------------------------------
11936 // create and save images for use in level sketches (raw BMP format)
11937 // ----------------------------------------------------------------------------
11939 void CreateLevelSketchImages(void)
11945 InitElementPropertiesGfxElement();
11947 bitmap1 = CreateBitmap(TILEX, TILEY, DEFAULT_DEPTH);
11948 bitmap2 = CreateBitmap(MINI_TILEX, MINI_TILEY, DEFAULT_DEPTH);
11950 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
11952 int element = getMappedElement(i);
11953 char basename1[16];
11954 char basename2[16];
11958 sprintf(basename1, "%04d.bmp", i);
11959 sprintf(basename2, "%04ds.bmp", i);
11961 filename1 = getPath2(global.create_images_dir, basename1);
11962 filename2 = getPath2(global.create_images_dir, basename2);
11964 DrawSizedElement(0, 0, element, TILESIZE);
11965 BlitBitmap(drawto, bitmap1, SX, SY, TILEX, TILEY, 0, 0);
11967 if (SDL_SaveBMP(bitmap1->surface, filename1) != 0)
11968 Error(ERR_EXIT, "cannot save level sketch image file '%s'", filename1);
11970 DrawSizedElement(0, 0, element, MINI_TILESIZE);
11971 BlitBitmap(drawto, bitmap2, SX, SY, MINI_TILEX, MINI_TILEY, 0, 0);
11973 if (SDL_SaveBMP(bitmap2->surface, filename2) != 0)
11974 Error(ERR_EXIT, "cannot save level sketch image file '%s'", filename2);
11979 // create corresponding SQL statements (for normal and small images)
11982 printf("insert into phpbb_words values (NULL, '`%03d', '<IMG class=\"levelsketch\" src=\"/I/%04d.png\"/>');\n", i, i);
11983 printf("insert into phpbb_words values (NULL, '¸%03d', '<IMG class=\"levelsketch\" src=\"/I/%04ds.png\"/>');\n", i, i);
11986 printf("insert into phpbb_words values (NULL, '`%04d', '<IMG class=\"levelsketch\" src=\"/I/%04d.png\"/>');\n", i, i);
11987 printf("insert into phpbb_words values (NULL, '¸%04d', '<IMG class=\"levelsketch\" src=\"/I/%04ds.png\"/>');\n", i, i);
11989 // optional: create content for forum level sketch demonstration post
11991 fprintf(stderr, "%03d `%03d%c", i, i, (i % 10 < 9 ? ' ' : '\n'));
11994 FreeBitmap(bitmap1);
11995 FreeBitmap(bitmap2);
11998 fprintf(stderr, "\n");
12000 Error(ERR_INFO, "%d normal and small images created", NUM_FILE_ELEMENTS);
12002 CloseAllAndExit(0);
12006 // ----------------------------------------------------------------------------
12007 // create and save images for custom and group elements (raw BMP format)
12008 // ----------------------------------------------------------------------------
12010 void CreateCustomElementImages(char *directory)
12012 char *src_basename = "RocksCE-template.ilbm";
12013 char *dst_basename = "RocksCE.bmp";
12014 char *src_filename = getPath2(directory, src_basename);
12015 char *dst_filename = getPath2(directory, dst_basename);
12016 Bitmap *src_bitmap;
12018 int yoffset_ce = 0;
12019 int yoffset_ge = (TILEY * NUM_CUSTOM_ELEMENTS / 16);
12022 InitVideoDefaults();
12024 ReCreateBitmap(&backbuffer, video.width, video.height);
12026 src_bitmap = LoadImage(src_filename);
12028 bitmap = CreateBitmap(TILEX * 16 * 2,
12029 TILEY * (NUM_CUSTOM_ELEMENTS + NUM_GROUP_ELEMENTS) / 16,
12032 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
12039 BlitBitmap(src_bitmap, bitmap, 0, 0, TILEX, TILEY,
12040 TILEX * x, TILEY * y + yoffset_ce);
12042 BlitBitmap(src_bitmap, bitmap, 0, TILEY,
12044 TILEX * x + TILEX * 16,
12045 TILEY * y + yoffset_ce);
12047 for (j = 2; j >= 0; j--)
12051 BlitBitmap(src_bitmap, bitmap,
12052 TILEX + c * 7, 0, 6, 10,
12053 TILEX * x + 6 + j * 7,
12054 TILEY * y + 11 + yoffset_ce);
12056 BlitBitmap(src_bitmap, bitmap,
12057 TILEX + c * 8, TILEY, 6, 10,
12058 TILEX * 16 + TILEX * x + 6 + j * 8,
12059 TILEY * y + 10 + yoffset_ce);
12065 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
12072 BlitBitmap(src_bitmap, bitmap, 0, 0, TILEX, TILEY,
12073 TILEX * x, TILEY * y + yoffset_ge);
12075 BlitBitmap(src_bitmap, bitmap, 0, TILEY,
12077 TILEX * x + TILEX * 16,
12078 TILEY * y + yoffset_ge);
12080 for (j = 1; j >= 0; j--)
12084 BlitBitmap(src_bitmap, bitmap, TILEX + c * 10, 11, 10, 10,
12085 TILEX * x + 6 + j * 10,
12086 TILEY * y + 11 + yoffset_ge);
12088 BlitBitmap(src_bitmap, bitmap,
12089 TILEX + c * 8, TILEY + 12, 6, 10,
12090 TILEX * 16 + TILEX * x + 10 + j * 8,
12091 TILEY * y + 10 + yoffset_ge);
12097 if (SDL_SaveBMP(bitmap->surface, dst_filename) != 0)
12098 Error(ERR_EXIT, "cannot save CE graphics file '%s'", dst_filename);
12100 FreeBitmap(bitmap);
12102 CloseAllAndExit(0);