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 CAVE *cav = level_em->cav;
3521 cav->width = MIN(level->fieldx, MAX_PLAYFIELD_WIDTH);
3522 cav->height = MIN(level->fieldy, MAX_PLAYFIELD_HEIGHT);
3524 cav->time_seconds = level->time;
3525 cav->gems_needed = level->gems_needed;
3527 cav->emerald_score = level->score[SC_EMERALD];
3528 cav->diamond_score = level->score[SC_DIAMOND];
3529 cav->alien_score = level->score[SC_ROBOT];
3530 cav->tank_score = level->score[SC_SPACESHIP];
3531 cav->bug_score = level->score[SC_BUG];
3532 cav->eater_score = level->score[SC_YAMYAM];
3533 cav->nut_score = level->score[SC_NUT];
3534 cav->dynamite_score = level->score[SC_DYNAMITE];
3535 cav->key_score = level->score[SC_KEY];
3536 cav->exit_score = level->score[SC_TIME_BONUS];
3538 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
3539 for (y = 0; y < 3; y++)
3540 for (x = 0; x < 3; x++)
3541 cav->eater_array[i][y * 3 + x] =
3542 map_element_RND_to_EM_cave(level->yamyam_content[i].e[x][y]);
3544 cav->amoeba_time = level->amoeba_speed;
3545 cav->wonderwall_time = level->time_magic_wall;
3546 cav->wheel_time = level->time_wheel;
3548 cav->android_move_time = level->android_move_time;
3549 cav->android_clone_time = level->android_clone_time;
3550 cav->ball_random = level->ball_random;
3551 cav->ball_state = level->ball_state_initial;
3552 cav->ball_time = level->ball_time;
3553 cav->num_ball_arrays = level->num_ball_contents;
3555 cav->lenses_score = level->lenses_score;
3556 cav->magnify_score = level->magnify_score;
3557 cav->slurp_score = level->slurp_score;
3559 cav->lenses_time = level->lenses_time;
3560 cav->magnify_time = level->magnify_time;
3562 cav->wind_direction =
3563 map_direction_RND_to_EM(level->wind_direction_initial);
3564 cav->wind_cnt = (level->wind_direction_initial != MV_NONE ?
3565 cav->wind_time : 0);
3567 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
3568 for (j = 0; j < 8; j++)
3569 cav->ball_array[i][j] =
3570 map_element_RND_to_EM_cave(level->ball_content[i].
3571 e[ball_xy[j][0]][ball_xy[j][1]]);
3573 map_android_clone_elements_RND_to_EM(level);
3575 // first fill the complete playfield with the empty space element
3576 for (y = 0; y < EM_MAX_CAVE_HEIGHT; y++)
3577 for (x = 0; x < EM_MAX_CAVE_WIDTH; x++)
3578 cav->cave[x][y] = Cblank;
3580 // then copy the real level contents from level file into the playfield
3581 for (y = 0; y < cav->height; y++) for (x = 0; x < cav->width; x++)
3583 int new_element = map_element_RND_to_EM_cave(level->field[x][y]);
3585 if (level->field[x][y] == EL_AMOEBA_DEAD)
3586 new_element = map_element_RND_to_EM_cave(EL_AMOEBA_WET);
3588 cav->cave[x][y] = new_element;
3591 for (i = 0; i < MAX_PLAYERS; i++)
3593 cav->player_x[i] = -1;
3594 cav->player_y[i] = -1;
3597 // initialize player positions and delete players from the playfield
3598 for (y = 0; y < cav->height; y++) for (x = 0; x < cav->width; x++)
3600 if (ELEM_IS_PLAYER(level->field[x][y]))
3602 int player_nr = GET_PLAYER_NR(level->field[x][y]);
3604 cav->player_x[player_nr] = x;
3605 cav->player_y[player_nr] = y;
3607 cav->cave[x][y] = map_element_RND_to_EM_cave(EL_EMPTY);
3612 static void CopyNativeLevel_EM_to_RND(struct LevelInfo *level)
3614 static int ball_xy[8][2] =
3625 struct LevelInfo_EM *level_em = level->native_em_level;
3626 struct CAVE *cav = level_em->cav;
3629 level->fieldx = MIN(cav->width, MAX_LEV_FIELDX);
3630 level->fieldy = MIN(cav->height, MAX_LEV_FIELDY);
3632 level->time = cav->time_seconds;
3633 level->gems_needed = cav->gems_needed;
3635 sprintf(level->name, "Level %d", level->file_info.nr);
3637 level->score[SC_EMERALD] = cav->emerald_score;
3638 level->score[SC_DIAMOND] = cav->diamond_score;
3639 level->score[SC_ROBOT] = cav->alien_score;
3640 level->score[SC_SPACESHIP] = cav->tank_score;
3641 level->score[SC_BUG] = cav->bug_score;
3642 level->score[SC_YAMYAM] = cav->eater_score;
3643 level->score[SC_NUT] = cav->nut_score;
3644 level->score[SC_DYNAMITE] = cav->dynamite_score;
3645 level->score[SC_KEY] = cav->key_score;
3646 level->score[SC_TIME_BONUS] = cav->exit_score;
3648 level->num_yamyam_contents = MAX_ELEMENT_CONTENTS;
3650 for (i = 0; i < level->num_yamyam_contents; i++)
3651 for (y = 0; y < 3; y++)
3652 for (x = 0; x < 3; x++)
3653 level->yamyam_content[i].e[x][y] =
3654 map_element_EM_to_RND_cave(cav->eater_array[i][y * 3 + x]);
3656 level->amoeba_speed = cav->amoeba_time;
3657 level->time_magic_wall = cav->wonderwall_time;
3658 level->time_wheel = cav->wheel_time;
3660 level->android_move_time = cav->android_move_time;
3661 level->android_clone_time = cav->android_clone_time;
3662 level->ball_random = cav->ball_random;
3663 level->ball_state_initial = cav->ball_state;
3664 level->ball_time = cav->ball_time;
3665 level->num_ball_contents = cav->num_ball_arrays;
3667 level->lenses_score = cav->lenses_score;
3668 level->magnify_score = cav->magnify_score;
3669 level->slurp_score = cav->slurp_score;
3671 level->lenses_time = cav->lenses_time;
3672 level->magnify_time = cav->magnify_time;
3674 level->wind_direction_initial =
3675 map_direction_EM_to_RND(cav->wind_direction);
3677 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
3678 for (j = 0; j < 8; j++)
3679 level->ball_content[i].e[ball_xy[j][0]][ball_xy[j][1]] =
3680 map_element_EM_to_RND_cave(cav->ball_array[i][j]);
3682 map_android_clone_elements_EM_to_RND(level);
3684 // convert the playfield (some elements need special treatment)
3685 for (y = 0; y < level->fieldy; y++) for (x = 0; x < level->fieldx; x++)
3687 int new_element = map_element_EM_to_RND_cave(cav->cave[x][y]);
3689 if (new_element == EL_AMOEBA_WET && level->amoeba_speed == 0)
3690 new_element = EL_AMOEBA_DEAD;
3692 level->field[x][y] = new_element;
3695 for (i = 0; i < MAX_PLAYERS; i++)
3697 // in case of all players set to the same field, use the first player
3698 int nr = MAX_PLAYERS - i - 1;
3699 int jx = cav->player_x[nr];
3700 int jy = cav->player_y[nr];
3702 if (jx != -1 && jy != -1)
3703 level->field[jx][jy] = EL_PLAYER_1 + nr;
3708 // ----------------------------------------------------------------------------
3709 // functions for loading SP level
3710 // ----------------------------------------------------------------------------
3712 static void CopyNativeLevel_RND_to_SP(struct LevelInfo *level)
3714 struct LevelInfo_SP *level_sp = level->native_sp_level;
3715 LevelInfoType *header = &level_sp->header;
3718 level_sp->width = level->fieldx;
3719 level_sp->height = level->fieldy;
3721 for (x = 0; x < level->fieldx; x++)
3722 for (y = 0; y < level->fieldy; y++)
3723 level_sp->playfield[x][y] = map_element_RND_to_SP(level->field[x][y]);
3725 header->InitialGravity = (level->initial_player_gravity[0] ? 1 : 0);
3727 for (i = 0; i < SP_LEVEL_NAME_LEN; i++)
3728 header->LevelTitle[i] = level->name[i];
3729 // !!! NO STRING TERMINATION IN SUPAPLEX VB CODE YET -- FIX THIS !!!
3731 header->InfotronsNeeded = level->gems_needed;
3733 header->SpecialPortCount = 0;
3735 for (x = 0; x < level->fieldx; x++) for (y = 0; y < level->fieldy; y++)
3737 boolean gravity_port_found = FALSE;
3738 boolean gravity_port_valid = FALSE;
3739 int gravity_port_flag;
3740 int gravity_port_base_element;
3741 int element = level->field[x][y];
3743 if (element >= EL_SP_GRAVITY_ON_PORT_RIGHT &&
3744 element <= EL_SP_GRAVITY_ON_PORT_UP)
3746 gravity_port_found = TRUE;
3747 gravity_port_valid = TRUE;
3748 gravity_port_flag = 1;
3749 gravity_port_base_element = EL_SP_GRAVITY_ON_PORT_RIGHT;
3751 else if (element >= EL_SP_GRAVITY_OFF_PORT_RIGHT &&
3752 element <= EL_SP_GRAVITY_OFF_PORT_UP)
3754 gravity_port_found = TRUE;
3755 gravity_port_valid = TRUE;
3756 gravity_port_flag = 0;
3757 gravity_port_base_element = EL_SP_GRAVITY_OFF_PORT_RIGHT;
3759 else if (element >= EL_SP_GRAVITY_PORT_RIGHT &&
3760 element <= EL_SP_GRAVITY_PORT_UP)
3762 // change R'n'D style gravity inverting special port to normal port
3763 // (there are no gravity inverting ports in native Supaplex engine)
3765 gravity_port_found = TRUE;
3766 gravity_port_valid = FALSE;
3767 gravity_port_base_element = EL_SP_GRAVITY_PORT_RIGHT;
3770 if (gravity_port_found)
3772 if (gravity_port_valid &&
3773 header->SpecialPortCount < SP_MAX_SPECIAL_PORTS)
3775 SpecialPortType *port = &header->SpecialPort[header->SpecialPortCount];
3777 port->PortLocation = (y * level->fieldx + x) * 2;
3778 port->Gravity = gravity_port_flag;
3780 element += EL_SP_GRAVITY_PORT_RIGHT - gravity_port_base_element;
3782 header->SpecialPortCount++;
3786 // change special gravity port to normal port
3788 element += EL_SP_PORT_RIGHT - gravity_port_base_element;
3791 level_sp->playfield[x][y] = element - EL_SP_START;
3796 static void CopyNativeLevel_SP_to_RND(struct LevelInfo *level)
3798 struct LevelInfo_SP *level_sp = level->native_sp_level;
3799 LevelInfoType *header = &level_sp->header;
3800 boolean num_invalid_elements = 0;
3803 level->fieldx = level_sp->width;
3804 level->fieldy = level_sp->height;
3806 for (x = 0; x < level->fieldx; x++)
3808 for (y = 0; y < level->fieldy; y++)
3810 int element_old = level_sp->playfield[x][y];
3811 int element_new = getMappedElement(map_element_SP_to_RND(element_old));
3813 if (element_new == EL_UNKNOWN)
3815 num_invalid_elements++;
3817 Error(ERR_DEBUG, "invalid element %d at position %d, %d",
3821 level->field[x][y] = element_new;
3825 if (num_invalid_elements > 0)
3826 Error(ERR_WARN, "found %d invalid elements%s", num_invalid_elements,
3827 (!options.debug ? " (use '--debug' for more details)" : ""));
3829 for (i = 0; i < MAX_PLAYERS; i++)
3830 level->initial_player_gravity[i] =
3831 (header->InitialGravity == 1 ? TRUE : FALSE);
3833 // skip leading spaces
3834 for (i = 0; i < SP_LEVEL_NAME_LEN; i++)
3835 if (header->LevelTitle[i] != ' ')
3839 for (j = 0; i < SP_LEVEL_NAME_LEN; i++, j++)
3840 level->name[j] = header->LevelTitle[i];
3841 level->name[j] = '\0';
3843 // cut trailing spaces
3845 if (level->name[j - 1] == ' ' && level->name[j] == '\0')
3846 level->name[j - 1] = '\0';
3848 level->gems_needed = header->InfotronsNeeded;
3850 for (i = 0; i < header->SpecialPortCount; i++)
3852 SpecialPortType *port = &header->SpecialPort[i];
3853 int port_location = port->PortLocation;
3854 int gravity = port->Gravity;
3855 int port_x, port_y, port_element;
3857 port_x = (port_location / 2) % level->fieldx;
3858 port_y = (port_location / 2) / level->fieldx;
3860 if (port_x < 0 || port_x >= level->fieldx ||
3861 port_y < 0 || port_y >= level->fieldy)
3863 Error(ERR_WARN, "special port position (%d, %d) out of bounds",
3869 port_element = level->field[port_x][port_y];
3871 if (port_element < EL_SP_GRAVITY_PORT_RIGHT ||
3872 port_element > EL_SP_GRAVITY_PORT_UP)
3874 Error(ERR_WARN, "no special port at position (%d, %d)", port_x, port_y);
3879 // change previous (wrong) gravity inverting special port to either
3880 // gravity enabling special port or gravity disabling special port
3881 level->field[port_x][port_y] +=
3882 (gravity == 1 ? EL_SP_GRAVITY_ON_PORT_RIGHT :
3883 EL_SP_GRAVITY_OFF_PORT_RIGHT) - EL_SP_GRAVITY_PORT_RIGHT;
3886 // change special gravity ports without database entries to normal ports
3887 for (x = 0; x < level->fieldx; x++)
3888 for (y = 0; y < level->fieldy; y++)
3889 if (level->field[x][y] >= EL_SP_GRAVITY_PORT_RIGHT &&
3890 level->field[x][y] <= EL_SP_GRAVITY_PORT_UP)
3891 level->field[x][y] += EL_SP_PORT_RIGHT - EL_SP_GRAVITY_PORT_RIGHT;
3893 level->time = 0; // no time limit
3894 level->amoeba_speed = 0;
3895 level->time_magic_wall = 0;
3896 level->time_wheel = 0;
3897 level->amoeba_content = EL_EMPTY;
3900 // original Supaplex does not use score values -- use default values
3902 for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
3903 level->score[i] = 0;
3906 // there are no yamyams in supaplex levels
3907 for (i = 0; i < level->num_yamyam_contents; i++)
3908 for (x = 0; x < 3; x++)
3909 for (y = 0; y < 3; y++)
3910 level->yamyam_content[i].e[x][y] = EL_EMPTY;
3913 static void CopyNativeTape_RND_to_SP(struct LevelInfo *level)
3915 struct LevelInfo_SP *level_sp = level->native_sp_level;
3916 struct DemoInfo_SP *demo = &level_sp->demo;
3919 // always start with reliable default values
3920 demo->is_available = FALSE;
3923 if (TAPE_IS_EMPTY(tape))
3926 demo->level_nr = tape.level_nr; // (currently not used)
3928 level_sp->header.DemoRandomSeed = tape.random_seed;
3932 for (i = 0; i < tape.length; i++)
3934 int demo_action = map_key_RND_to_SP(tape.pos[i].action[0]);
3935 int demo_repeat = tape.pos[i].delay;
3936 int demo_entries = (demo_repeat + 15) / 16;
3938 if (demo->length + demo_entries >= SP_MAX_TAPE_LEN)
3940 Error(ERR_WARN, "tape truncated: size exceeds maximum SP demo size %d",
3946 for (j = 0; j < demo_repeat / 16; j++)
3947 demo->data[demo->length++] = 0xf0 | demo_action;
3949 if (demo_repeat % 16)
3950 demo->data[demo->length++] = ((demo_repeat % 16 - 1) << 4) | demo_action;
3953 demo->is_available = TRUE;
3956 static void setTapeInfoToDefaults(void);
3958 static void CopyNativeTape_SP_to_RND(struct LevelInfo *level)
3960 struct LevelInfo_SP *level_sp = level->native_sp_level;
3961 struct DemoInfo_SP *demo = &level_sp->demo;
3962 char *filename = level->file_info.filename;
3965 // always start with reliable default values
3966 setTapeInfoToDefaults();
3968 if (!demo->is_available)
3971 tape.level_nr = demo->level_nr; // (currently not used)
3972 tape.random_seed = level_sp->header.DemoRandomSeed;
3974 TapeSetDateFromEpochSeconds(getFileTimestampEpochSeconds(filename));
3977 tape.pos[tape.counter].delay = 0;
3979 for (i = 0; i < demo->length; i++)
3981 int demo_action = demo->data[i] & 0x0f;
3982 int demo_repeat = (demo->data[i] & 0xf0) >> 4;
3983 int tape_action = map_key_SP_to_RND(demo_action);
3984 int tape_repeat = demo_repeat + 1;
3985 byte action[MAX_PLAYERS] = { tape_action, 0, 0, 0 };
3986 boolean success = 0;
3989 for (j = 0; j < tape_repeat; j++)
3990 success = TapeAddAction(action);
3994 Error(ERR_WARN, "SP demo truncated: size exceeds maximum tape size %d",
4001 TapeHaltRecording();
4005 // ----------------------------------------------------------------------------
4006 // functions for loading MM level
4007 // ----------------------------------------------------------------------------
4009 static void CopyNativeLevel_RND_to_MM(struct LevelInfo *level)
4011 struct LevelInfo_MM *level_mm = level->native_mm_level;
4014 level_mm->fieldx = MIN(level->fieldx, MM_MAX_PLAYFIELD_WIDTH);
4015 level_mm->fieldy = MIN(level->fieldy, MM_MAX_PLAYFIELD_HEIGHT);
4017 level_mm->time = level->time;
4018 level_mm->kettles_needed = level->gems_needed;
4019 level_mm->auto_count_kettles = level->auto_count_gems;
4021 level_mm->laser_red = level->mm_laser_red;
4022 level_mm->laser_green = level->mm_laser_green;
4023 level_mm->laser_blue = level->mm_laser_blue;
4025 strcpy(level_mm->name, level->name);
4026 strcpy(level_mm->author, level->author);
4028 level_mm->score[SC_EMERALD] = level->score[SC_EMERALD];
4029 level_mm->score[SC_PACMAN] = level->score[SC_PACMAN];
4030 level_mm->score[SC_KEY] = level->score[SC_KEY];
4031 level_mm->score[SC_TIME_BONUS] = level->score[SC_TIME_BONUS];
4032 level_mm->score[SC_ELEM_BONUS] = level->score[SC_ELEM_BONUS];
4034 level_mm->amoeba_speed = level->amoeba_speed;
4035 level_mm->time_fuse = level->mm_time_fuse;
4036 level_mm->time_bomb = level->mm_time_bomb;
4037 level_mm->time_ball = level->mm_time_ball;
4038 level_mm->time_block = level->mm_time_block;
4040 for (x = 0; x < level->fieldx; x++)
4041 for (y = 0; y < level->fieldy; y++)
4043 level_mm->field[x][y] = map_element_RND_to_MM(level->field[x][y]);
4046 static void CopyNativeLevel_MM_to_RND(struct LevelInfo *level)
4048 struct LevelInfo_MM *level_mm = level->native_mm_level;
4051 level->fieldx = MIN(level_mm->fieldx, MAX_LEV_FIELDX);
4052 level->fieldy = MIN(level_mm->fieldy, MAX_LEV_FIELDY);
4054 level->time = level_mm->time;
4055 level->gems_needed = level_mm->kettles_needed;
4056 level->auto_count_gems = level_mm->auto_count_kettles;
4058 level->mm_laser_red = level_mm->laser_red;
4059 level->mm_laser_green = level_mm->laser_green;
4060 level->mm_laser_blue = level_mm->laser_blue;
4062 strcpy(level->name, level_mm->name);
4064 // only overwrite author from 'levelinfo.conf' if author defined in level
4065 if (!strEqual(level_mm->author, ANONYMOUS_NAME))
4066 strcpy(level->author, level_mm->author);
4068 level->score[SC_EMERALD] = level_mm->score[SC_EMERALD];
4069 level->score[SC_PACMAN] = level_mm->score[SC_PACMAN];
4070 level->score[SC_KEY] = level_mm->score[SC_KEY];
4071 level->score[SC_TIME_BONUS] = level_mm->score[SC_TIME_BONUS];
4072 level->score[SC_ELEM_BONUS] = level_mm->score[SC_ELEM_BONUS];
4074 level->amoeba_speed = level_mm->amoeba_speed;
4075 level->mm_time_fuse = level_mm->time_fuse;
4076 level->mm_time_bomb = level_mm->time_bomb;
4077 level->mm_time_ball = level_mm->time_ball;
4078 level->mm_time_block = level_mm->time_block;
4080 for (x = 0; x < level->fieldx; x++)
4081 for (y = 0; y < level->fieldy; y++)
4082 level->field[x][y] = map_element_MM_to_RND(level_mm->field[x][y]);
4086 // ----------------------------------------------------------------------------
4087 // functions for loading DC level
4088 // ----------------------------------------------------------------------------
4090 #define DC_LEVEL_HEADER_SIZE 344
4092 static unsigned short getDecodedWord_DC(unsigned short data_encoded,
4095 static int last_data_encoded;
4099 int diff_hi, diff_lo;
4100 int data_hi, data_lo;
4101 unsigned short data_decoded;
4105 last_data_encoded = 0;
4112 diff = data_encoded - last_data_encoded;
4113 diff_hi = diff & ~0xff;
4114 diff_lo = diff & 0xff;
4118 data_hi = diff_hi - (offset1 << 8) + (offset2 & 0xff00);
4119 data_lo = (diff_lo + (data_hi >> 16)) & 0x00ff;
4120 data_hi = data_hi & 0xff00;
4122 data_decoded = data_hi | data_lo;
4124 last_data_encoded = data_encoded;
4126 offset1 = (offset1 + 1) % 31;
4127 offset2 = offset2 & 0xff;
4129 return data_decoded;
4132 static int getMappedElement_DC(int element)
4140 // 0x0117 - 0x036e: (?)
4143 // 0x042d - 0x0684: (?)
4159 element = EL_CRYSTAL;
4162 case 0x0e77: // quicksand (boulder)
4163 element = EL_QUICKSAND_FAST_FULL;
4166 case 0x0e99: // slow quicksand (boulder)
4167 element = EL_QUICKSAND_FULL;
4171 element = EL_EM_EXIT_OPEN;
4175 element = EL_EM_EXIT_CLOSED;
4179 element = EL_EM_STEEL_EXIT_OPEN;
4183 element = EL_EM_STEEL_EXIT_CLOSED;
4186 case 0x0f4f: // dynamite (lit 1)
4187 element = EL_EM_DYNAMITE_ACTIVE;
4190 case 0x0f57: // dynamite (lit 2)
4191 element = EL_EM_DYNAMITE_ACTIVE;
4194 case 0x0f5f: // dynamite (lit 3)
4195 element = EL_EM_DYNAMITE_ACTIVE;
4198 case 0x0f67: // dynamite (lit 4)
4199 element = EL_EM_DYNAMITE_ACTIVE;
4206 element = EL_AMOEBA_WET;
4210 element = EL_AMOEBA_DROP;
4214 element = EL_DC_MAGIC_WALL;
4218 element = EL_SPACESHIP_UP;
4222 element = EL_SPACESHIP_DOWN;
4226 element = EL_SPACESHIP_LEFT;
4230 element = EL_SPACESHIP_RIGHT;
4234 element = EL_BUG_UP;
4238 element = EL_BUG_DOWN;
4242 element = EL_BUG_LEFT;
4246 element = EL_BUG_RIGHT;
4250 element = EL_MOLE_UP;
4254 element = EL_MOLE_DOWN;
4258 element = EL_MOLE_LEFT;
4262 element = EL_MOLE_RIGHT;
4270 element = EL_YAMYAM;
4274 element = EL_SWITCHGATE_OPEN;
4278 element = EL_SWITCHGATE_CLOSED;
4282 element = EL_DC_SWITCHGATE_SWITCH_UP;
4286 element = EL_TIMEGATE_CLOSED;
4289 case 0x144c: // conveyor belt switch (green)
4290 element = EL_CONVEYOR_BELT_3_SWITCH_MIDDLE;
4293 case 0x144f: // conveyor belt switch (red)
4294 element = EL_CONVEYOR_BELT_1_SWITCH_MIDDLE;
4297 case 0x1452: // conveyor belt switch (blue)
4298 element = EL_CONVEYOR_BELT_4_SWITCH_MIDDLE;
4302 element = EL_CONVEYOR_BELT_3_MIDDLE;
4306 element = EL_CONVEYOR_BELT_3_LEFT;
4310 element = EL_CONVEYOR_BELT_3_RIGHT;
4314 element = EL_CONVEYOR_BELT_1_MIDDLE;
4318 element = EL_CONVEYOR_BELT_1_LEFT;
4322 element = EL_CONVEYOR_BELT_1_RIGHT;
4326 element = EL_CONVEYOR_BELT_4_MIDDLE;
4330 element = EL_CONVEYOR_BELT_4_LEFT;
4334 element = EL_CONVEYOR_BELT_4_RIGHT;
4338 element = EL_EXPANDABLE_WALL_HORIZONTAL;
4342 element = EL_EXPANDABLE_WALL_VERTICAL;
4346 element = EL_EXPANDABLE_WALL_ANY;
4349 case 0x14ce: // growing steel wall (left/right)
4350 element = EL_EXPANDABLE_STEELWALL_HORIZONTAL;
4353 case 0x14df: // growing steel wall (up/down)
4354 element = EL_EXPANDABLE_STEELWALL_VERTICAL;
4357 case 0x14e8: // growing steel wall (up/down/left/right)
4358 element = EL_EXPANDABLE_STEELWALL_ANY;
4362 element = EL_SHIELD_DEADLY;
4366 element = EL_EXTRA_TIME;
4374 element = EL_EMPTY_SPACE;
4377 case 0x1578: // quicksand (empty)
4378 element = EL_QUICKSAND_FAST_EMPTY;
4381 case 0x1579: // slow quicksand (empty)
4382 element = EL_QUICKSAND_EMPTY;
4392 element = EL_EM_DYNAMITE;
4395 case 0x15a1: // key (red)
4396 element = EL_EM_KEY_1;
4399 case 0x15a2: // key (yellow)
4400 element = EL_EM_KEY_2;
4403 case 0x15a3: // key (blue)
4404 element = EL_EM_KEY_4;
4407 case 0x15a4: // key (green)
4408 element = EL_EM_KEY_3;
4411 case 0x15a5: // key (white)
4412 element = EL_DC_KEY_WHITE;
4416 element = EL_WALL_SLIPPERY;
4423 case 0x15a8: // wall (not round)
4427 case 0x15a9: // (blue)
4428 element = EL_CHAR_A;
4431 case 0x15aa: // (blue)
4432 element = EL_CHAR_B;
4435 case 0x15ab: // (blue)
4436 element = EL_CHAR_C;
4439 case 0x15ac: // (blue)
4440 element = EL_CHAR_D;
4443 case 0x15ad: // (blue)
4444 element = EL_CHAR_E;
4447 case 0x15ae: // (blue)
4448 element = EL_CHAR_F;
4451 case 0x15af: // (blue)
4452 element = EL_CHAR_G;
4455 case 0x15b0: // (blue)
4456 element = EL_CHAR_H;
4459 case 0x15b1: // (blue)
4460 element = EL_CHAR_I;
4463 case 0x15b2: // (blue)
4464 element = EL_CHAR_J;
4467 case 0x15b3: // (blue)
4468 element = EL_CHAR_K;
4471 case 0x15b4: // (blue)
4472 element = EL_CHAR_L;
4475 case 0x15b5: // (blue)
4476 element = EL_CHAR_M;
4479 case 0x15b6: // (blue)
4480 element = EL_CHAR_N;
4483 case 0x15b7: // (blue)
4484 element = EL_CHAR_O;
4487 case 0x15b8: // (blue)
4488 element = EL_CHAR_P;
4491 case 0x15b9: // (blue)
4492 element = EL_CHAR_Q;
4495 case 0x15ba: // (blue)
4496 element = EL_CHAR_R;
4499 case 0x15bb: // (blue)
4500 element = EL_CHAR_S;
4503 case 0x15bc: // (blue)
4504 element = EL_CHAR_T;
4507 case 0x15bd: // (blue)
4508 element = EL_CHAR_U;
4511 case 0x15be: // (blue)
4512 element = EL_CHAR_V;
4515 case 0x15bf: // (blue)
4516 element = EL_CHAR_W;
4519 case 0x15c0: // (blue)
4520 element = EL_CHAR_X;
4523 case 0x15c1: // (blue)
4524 element = EL_CHAR_Y;
4527 case 0x15c2: // (blue)
4528 element = EL_CHAR_Z;
4531 case 0x15c3: // (blue)
4532 element = EL_CHAR_AUMLAUT;
4535 case 0x15c4: // (blue)
4536 element = EL_CHAR_OUMLAUT;
4539 case 0x15c5: // (blue)
4540 element = EL_CHAR_UUMLAUT;
4543 case 0x15c6: // (blue)
4544 element = EL_CHAR_0;
4547 case 0x15c7: // (blue)
4548 element = EL_CHAR_1;
4551 case 0x15c8: // (blue)
4552 element = EL_CHAR_2;
4555 case 0x15c9: // (blue)
4556 element = EL_CHAR_3;
4559 case 0x15ca: // (blue)
4560 element = EL_CHAR_4;
4563 case 0x15cb: // (blue)
4564 element = EL_CHAR_5;
4567 case 0x15cc: // (blue)
4568 element = EL_CHAR_6;
4571 case 0x15cd: // (blue)
4572 element = EL_CHAR_7;
4575 case 0x15ce: // (blue)
4576 element = EL_CHAR_8;
4579 case 0x15cf: // (blue)
4580 element = EL_CHAR_9;
4583 case 0x15d0: // (blue)
4584 element = EL_CHAR_PERIOD;
4587 case 0x15d1: // (blue)
4588 element = EL_CHAR_EXCLAM;
4591 case 0x15d2: // (blue)
4592 element = EL_CHAR_COLON;
4595 case 0x15d3: // (blue)
4596 element = EL_CHAR_LESS;
4599 case 0x15d4: // (blue)
4600 element = EL_CHAR_GREATER;
4603 case 0x15d5: // (blue)
4604 element = EL_CHAR_QUESTION;
4607 case 0x15d6: // (blue)
4608 element = EL_CHAR_COPYRIGHT;
4611 case 0x15d7: // (blue)
4612 element = EL_CHAR_UP;
4615 case 0x15d8: // (blue)
4616 element = EL_CHAR_DOWN;
4619 case 0x15d9: // (blue)
4620 element = EL_CHAR_BUTTON;
4623 case 0x15da: // (blue)
4624 element = EL_CHAR_PLUS;
4627 case 0x15db: // (blue)
4628 element = EL_CHAR_MINUS;
4631 case 0x15dc: // (blue)
4632 element = EL_CHAR_APOSTROPHE;
4635 case 0x15dd: // (blue)
4636 element = EL_CHAR_PARENLEFT;
4639 case 0x15de: // (blue)
4640 element = EL_CHAR_PARENRIGHT;
4643 case 0x15df: // (green)
4644 element = EL_CHAR_A;
4647 case 0x15e0: // (green)
4648 element = EL_CHAR_B;
4651 case 0x15e1: // (green)
4652 element = EL_CHAR_C;
4655 case 0x15e2: // (green)
4656 element = EL_CHAR_D;
4659 case 0x15e3: // (green)
4660 element = EL_CHAR_E;
4663 case 0x15e4: // (green)
4664 element = EL_CHAR_F;
4667 case 0x15e5: // (green)
4668 element = EL_CHAR_G;
4671 case 0x15e6: // (green)
4672 element = EL_CHAR_H;
4675 case 0x15e7: // (green)
4676 element = EL_CHAR_I;
4679 case 0x15e8: // (green)
4680 element = EL_CHAR_J;
4683 case 0x15e9: // (green)
4684 element = EL_CHAR_K;
4687 case 0x15ea: // (green)
4688 element = EL_CHAR_L;
4691 case 0x15eb: // (green)
4692 element = EL_CHAR_M;
4695 case 0x15ec: // (green)
4696 element = EL_CHAR_N;
4699 case 0x15ed: // (green)
4700 element = EL_CHAR_O;
4703 case 0x15ee: // (green)
4704 element = EL_CHAR_P;
4707 case 0x15ef: // (green)
4708 element = EL_CHAR_Q;
4711 case 0x15f0: // (green)
4712 element = EL_CHAR_R;
4715 case 0x15f1: // (green)
4716 element = EL_CHAR_S;
4719 case 0x15f2: // (green)
4720 element = EL_CHAR_T;
4723 case 0x15f3: // (green)
4724 element = EL_CHAR_U;
4727 case 0x15f4: // (green)
4728 element = EL_CHAR_V;
4731 case 0x15f5: // (green)
4732 element = EL_CHAR_W;
4735 case 0x15f6: // (green)
4736 element = EL_CHAR_X;
4739 case 0x15f7: // (green)
4740 element = EL_CHAR_Y;
4743 case 0x15f8: // (green)
4744 element = EL_CHAR_Z;
4747 case 0x15f9: // (green)
4748 element = EL_CHAR_AUMLAUT;
4751 case 0x15fa: // (green)
4752 element = EL_CHAR_OUMLAUT;
4755 case 0x15fb: // (green)
4756 element = EL_CHAR_UUMLAUT;
4759 case 0x15fc: // (green)
4760 element = EL_CHAR_0;
4763 case 0x15fd: // (green)
4764 element = EL_CHAR_1;
4767 case 0x15fe: // (green)
4768 element = EL_CHAR_2;
4771 case 0x15ff: // (green)
4772 element = EL_CHAR_3;
4775 case 0x1600: // (green)
4776 element = EL_CHAR_4;
4779 case 0x1601: // (green)
4780 element = EL_CHAR_5;
4783 case 0x1602: // (green)
4784 element = EL_CHAR_6;
4787 case 0x1603: // (green)
4788 element = EL_CHAR_7;
4791 case 0x1604: // (green)
4792 element = EL_CHAR_8;
4795 case 0x1605: // (green)
4796 element = EL_CHAR_9;
4799 case 0x1606: // (green)
4800 element = EL_CHAR_PERIOD;
4803 case 0x1607: // (green)
4804 element = EL_CHAR_EXCLAM;
4807 case 0x1608: // (green)
4808 element = EL_CHAR_COLON;
4811 case 0x1609: // (green)
4812 element = EL_CHAR_LESS;
4815 case 0x160a: // (green)
4816 element = EL_CHAR_GREATER;
4819 case 0x160b: // (green)
4820 element = EL_CHAR_QUESTION;
4823 case 0x160c: // (green)
4824 element = EL_CHAR_COPYRIGHT;
4827 case 0x160d: // (green)
4828 element = EL_CHAR_UP;
4831 case 0x160e: // (green)
4832 element = EL_CHAR_DOWN;
4835 case 0x160f: // (green)
4836 element = EL_CHAR_BUTTON;
4839 case 0x1610: // (green)
4840 element = EL_CHAR_PLUS;
4843 case 0x1611: // (green)
4844 element = EL_CHAR_MINUS;
4847 case 0x1612: // (green)
4848 element = EL_CHAR_APOSTROPHE;
4851 case 0x1613: // (green)
4852 element = EL_CHAR_PARENLEFT;
4855 case 0x1614: // (green)
4856 element = EL_CHAR_PARENRIGHT;
4859 case 0x1615: // (blue steel)
4860 element = EL_STEEL_CHAR_A;
4863 case 0x1616: // (blue steel)
4864 element = EL_STEEL_CHAR_B;
4867 case 0x1617: // (blue steel)
4868 element = EL_STEEL_CHAR_C;
4871 case 0x1618: // (blue steel)
4872 element = EL_STEEL_CHAR_D;
4875 case 0x1619: // (blue steel)
4876 element = EL_STEEL_CHAR_E;
4879 case 0x161a: // (blue steel)
4880 element = EL_STEEL_CHAR_F;
4883 case 0x161b: // (blue steel)
4884 element = EL_STEEL_CHAR_G;
4887 case 0x161c: // (blue steel)
4888 element = EL_STEEL_CHAR_H;
4891 case 0x161d: // (blue steel)
4892 element = EL_STEEL_CHAR_I;
4895 case 0x161e: // (blue steel)
4896 element = EL_STEEL_CHAR_J;
4899 case 0x161f: // (blue steel)
4900 element = EL_STEEL_CHAR_K;
4903 case 0x1620: // (blue steel)
4904 element = EL_STEEL_CHAR_L;
4907 case 0x1621: // (blue steel)
4908 element = EL_STEEL_CHAR_M;
4911 case 0x1622: // (blue steel)
4912 element = EL_STEEL_CHAR_N;
4915 case 0x1623: // (blue steel)
4916 element = EL_STEEL_CHAR_O;
4919 case 0x1624: // (blue steel)
4920 element = EL_STEEL_CHAR_P;
4923 case 0x1625: // (blue steel)
4924 element = EL_STEEL_CHAR_Q;
4927 case 0x1626: // (blue steel)
4928 element = EL_STEEL_CHAR_R;
4931 case 0x1627: // (blue steel)
4932 element = EL_STEEL_CHAR_S;
4935 case 0x1628: // (blue steel)
4936 element = EL_STEEL_CHAR_T;
4939 case 0x1629: // (blue steel)
4940 element = EL_STEEL_CHAR_U;
4943 case 0x162a: // (blue steel)
4944 element = EL_STEEL_CHAR_V;
4947 case 0x162b: // (blue steel)
4948 element = EL_STEEL_CHAR_W;
4951 case 0x162c: // (blue steel)
4952 element = EL_STEEL_CHAR_X;
4955 case 0x162d: // (blue steel)
4956 element = EL_STEEL_CHAR_Y;
4959 case 0x162e: // (blue steel)
4960 element = EL_STEEL_CHAR_Z;
4963 case 0x162f: // (blue steel)
4964 element = EL_STEEL_CHAR_AUMLAUT;
4967 case 0x1630: // (blue steel)
4968 element = EL_STEEL_CHAR_OUMLAUT;
4971 case 0x1631: // (blue steel)
4972 element = EL_STEEL_CHAR_UUMLAUT;
4975 case 0x1632: // (blue steel)
4976 element = EL_STEEL_CHAR_0;
4979 case 0x1633: // (blue steel)
4980 element = EL_STEEL_CHAR_1;
4983 case 0x1634: // (blue steel)
4984 element = EL_STEEL_CHAR_2;
4987 case 0x1635: // (blue steel)
4988 element = EL_STEEL_CHAR_3;
4991 case 0x1636: // (blue steel)
4992 element = EL_STEEL_CHAR_4;
4995 case 0x1637: // (blue steel)
4996 element = EL_STEEL_CHAR_5;
4999 case 0x1638: // (blue steel)
5000 element = EL_STEEL_CHAR_6;
5003 case 0x1639: // (blue steel)
5004 element = EL_STEEL_CHAR_7;
5007 case 0x163a: // (blue steel)
5008 element = EL_STEEL_CHAR_8;
5011 case 0x163b: // (blue steel)
5012 element = EL_STEEL_CHAR_9;
5015 case 0x163c: // (blue steel)
5016 element = EL_STEEL_CHAR_PERIOD;
5019 case 0x163d: // (blue steel)
5020 element = EL_STEEL_CHAR_EXCLAM;
5023 case 0x163e: // (blue steel)
5024 element = EL_STEEL_CHAR_COLON;
5027 case 0x163f: // (blue steel)
5028 element = EL_STEEL_CHAR_LESS;
5031 case 0x1640: // (blue steel)
5032 element = EL_STEEL_CHAR_GREATER;
5035 case 0x1641: // (blue steel)
5036 element = EL_STEEL_CHAR_QUESTION;
5039 case 0x1642: // (blue steel)
5040 element = EL_STEEL_CHAR_COPYRIGHT;
5043 case 0x1643: // (blue steel)
5044 element = EL_STEEL_CHAR_UP;
5047 case 0x1644: // (blue steel)
5048 element = EL_STEEL_CHAR_DOWN;
5051 case 0x1645: // (blue steel)
5052 element = EL_STEEL_CHAR_BUTTON;
5055 case 0x1646: // (blue steel)
5056 element = EL_STEEL_CHAR_PLUS;
5059 case 0x1647: // (blue steel)
5060 element = EL_STEEL_CHAR_MINUS;
5063 case 0x1648: // (blue steel)
5064 element = EL_STEEL_CHAR_APOSTROPHE;
5067 case 0x1649: // (blue steel)
5068 element = EL_STEEL_CHAR_PARENLEFT;
5071 case 0x164a: // (blue steel)
5072 element = EL_STEEL_CHAR_PARENRIGHT;
5075 case 0x164b: // (green steel)
5076 element = EL_STEEL_CHAR_A;
5079 case 0x164c: // (green steel)
5080 element = EL_STEEL_CHAR_B;
5083 case 0x164d: // (green steel)
5084 element = EL_STEEL_CHAR_C;
5087 case 0x164e: // (green steel)
5088 element = EL_STEEL_CHAR_D;
5091 case 0x164f: // (green steel)
5092 element = EL_STEEL_CHAR_E;
5095 case 0x1650: // (green steel)
5096 element = EL_STEEL_CHAR_F;
5099 case 0x1651: // (green steel)
5100 element = EL_STEEL_CHAR_G;
5103 case 0x1652: // (green steel)
5104 element = EL_STEEL_CHAR_H;
5107 case 0x1653: // (green steel)
5108 element = EL_STEEL_CHAR_I;
5111 case 0x1654: // (green steel)
5112 element = EL_STEEL_CHAR_J;
5115 case 0x1655: // (green steel)
5116 element = EL_STEEL_CHAR_K;
5119 case 0x1656: // (green steel)
5120 element = EL_STEEL_CHAR_L;
5123 case 0x1657: // (green steel)
5124 element = EL_STEEL_CHAR_M;
5127 case 0x1658: // (green steel)
5128 element = EL_STEEL_CHAR_N;
5131 case 0x1659: // (green steel)
5132 element = EL_STEEL_CHAR_O;
5135 case 0x165a: // (green steel)
5136 element = EL_STEEL_CHAR_P;
5139 case 0x165b: // (green steel)
5140 element = EL_STEEL_CHAR_Q;
5143 case 0x165c: // (green steel)
5144 element = EL_STEEL_CHAR_R;
5147 case 0x165d: // (green steel)
5148 element = EL_STEEL_CHAR_S;
5151 case 0x165e: // (green steel)
5152 element = EL_STEEL_CHAR_T;
5155 case 0x165f: // (green steel)
5156 element = EL_STEEL_CHAR_U;
5159 case 0x1660: // (green steel)
5160 element = EL_STEEL_CHAR_V;
5163 case 0x1661: // (green steel)
5164 element = EL_STEEL_CHAR_W;
5167 case 0x1662: // (green steel)
5168 element = EL_STEEL_CHAR_X;
5171 case 0x1663: // (green steel)
5172 element = EL_STEEL_CHAR_Y;
5175 case 0x1664: // (green steel)
5176 element = EL_STEEL_CHAR_Z;
5179 case 0x1665: // (green steel)
5180 element = EL_STEEL_CHAR_AUMLAUT;
5183 case 0x1666: // (green steel)
5184 element = EL_STEEL_CHAR_OUMLAUT;
5187 case 0x1667: // (green steel)
5188 element = EL_STEEL_CHAR_UUMLAUT;
5191 case 0x1668: // (green steel)
5192 element = EL_STEEL_CHAR_0;
5195 case 0x1669: // (green steel)
5196 element = EL_STEEL_CHAR_1;
5199 case 0x166a: // (green steel)
5200 element = EL_STEEL_CHAR_2;
5203 case 0x166b: // (green steel)
5204 element = EL_STEEL_CHAR_3;
5207 case 0x166c: // (green steel)
5208 element = EL_STEEL_CHAR_4;
5211 case 0x166d: // (green steel)
5212 element = EL_STEEL_CHAR_5;
5215 case 0x166e: // (green steel)
5216 element = EL_STEEL_CHAR_6;
5219 case 0x166f: // (green steel)
5220 element = EL_STEEL_CHAR_7;
5223 case 0x1670: // (green steel)
5224 element = EL_STEEL_CHAR_8;
5227 case 0x1671: // (green steel)
5228 element = EL_STEEL_CHAR_9;
5231 case 0x1672: // (green steel)
5232 element = EL_STEEL_CHAR_PERIOD;
5235 case 0x1673: // (green steel)
5236 element = EL_STEEL_CHAR_EXCLAM;
5239 case 0x1674: // (green steel)
5240 element = EL_STEEL_CHAR_COLON;
5243 case 0x1675: // (green steel)
5244 element = EL_STEEL_CHAR_LESS;
5247 case 0x1676: // (green steel)
5248 element = EL_STEEL_CHAR_GREATER;
5251 case 0x1677: // (green steel)
5252 element = EL_STEEL_CHAR_QUESTION;
5255 case 0x1678: // (green steel)
5256 element = EL_STEEL_CHAR_COPYRIGHT;
5259 case 0x1679: // (green steel)
5260 element = EL_STEEL_CHAR_UP;
5263 case 0x167a: // (green steel)
5264 element = EL_STEEL_CHAR_DOWN;
5267 case 0x167b: // (green steel)
5268 element = EL_STEEL_CHAR_BUTTON;
5271 case 0x167c: // (green steel)
5272 element = EL_STEEL_CHAR_PLUS;
5275 case 0x167d: // (green steel)
5276 element = EL_STEEL_CHAR_MINUS;
5279 case 0x167e: // (green steel)
5280 element = EL_STEEL_CHAR_APOSTROPHE;
5283 case 0x167f: // (green steel)
5284 element = EL_STEEL_CHAR_PARENLEFT;
5287 case 0x1680: // (green steel)
5288 element = EL_STEEL_CHAR_PARENRIGHT;
5291 case 0x1681: // gate (red)
5292 element = EL_EM_GATE_1;
5295 case 0x1682: // secret gate (red)
5296 element = EL_GATE_1_GRAY;
5299 case 0x1683: // gate (yellow)
5300 element = EL_EM_GATE_2;
5303 case 0x1684: // secret gate (yellow)
5304 element = EL_GATE_2_GRAY;
5307 case 0x1685: // gate (blue)
5308 element = EL_EM_GATE_4;
5311 case 0x1686: // secret gate (blue)
5312 element = EL_GATE_4_GRAY;
5315 case 0x1687: // gate (green)
5316 element = EL_EM_GATE_3;
5319 case 0x1688: // secret gate (green)
5320 element = EL_GATE_3_GRAY;
5323 case 0x1689: // gate (white)
5324 element = EL_DC_GATE_WHITE;
5327 case 0x168a: // secret gate (white)
5328 element = EL_DC_GATE_WHITE_GRAY;
5331 case 0x168b: // secret gate (no key)
5332 element = EL_DC_GATE_FAKE_GRAY;
5336 element = EL_ROBOT_WHEEL;
5340 element = EL_DC_TIMEGATE_SWITCH;
5344 element = EL_ACID_POOL_BOTTOM;
5348 element = EL_ACID_POOL_TOPLEFT;
5352 element = EL_ACID_POOL_TOPRIGHT;
5356 element = EL_ACID_POOL_BOTTOMLEFT;
5360 element = EL_ACID_POOL_BOTTOMRIGHT;
5364 element = EL_STEELWALL;
5368 element = EL_STEELWALL_SLIPPERY;
5371 case 0x1695: // steel wall (not round)
5372 element = EL_STEELWALL;
5375 case 0x1696: // steel wall (left)
5376 element = EL_DC_STEELWALL_1_LEFT;
5379 case 0x1697: // steel wall (bottom)
5380 element = EL_DC_STEELWALL_1_BOTTOM;
5383 case 0x1698: // steel wall (right)
5384 element = EL_DC_STEELWALL_1_RIGHT;
5387 case 0x1699: // steel wall (top)
5388 element = EL_DC_STEELWALL_1_TOP;
5391 case 0x169a: // steel wall (left/bottom)
5392 element = EL_DC_STEELWALL_1_BOTTOMLEFT;
5395 case 0x169b: // steel wall (right/bottom)
5396 element = EL_DC_STEELWALL_1_BOTTOMRIGHT;
5399 case 0x169c: // steel wall (right/top)
5400 element = EL_DC_STEELWALL_1_TOPRIGHT;
5403 case 0x169d: // steel wall (left/top)
5404 element = EL_DC_STEELWALL_1_TOPLEFT;
5407 case 0x169e: // steel wall (right/bottom small)
5408 element = EL_DC_STEELWALL_1_BOTTOMRIGHT_2;
5411 case 0x169f: // steel wall (left/bottom small)
5412 element = EL_DC_STEELWALL_1_BOTTOMLEFT_2;
5415 case 0x16a0: // steel wall (right/top small)
5416 element = EL_DC_STEELWALL_1_TOPRIGHT_2;
5419 case 0x16a1: // steel wall (left/top small)
5420 element = EL_DC_STEELWALL_1_TOPLEFT_2;
5423 case 0x16a2: // steel wall (left/right)
5424 element = EL_DC_STEELWALL_1_VERTICAL;
5427 case 0x16a3: // steel wall (top/bottom)
5428 element = EL_DC_STEELWALL_1_HORIZONTAL;
5431 case 0x16a4: // steel wall 2 (left end)
5432 element = EL_DC_STEELWALL_2_LEFT;
5435 case 0x16a5: // steel wall 2 (right end)
5436 element = EL_DC_STEELWALL_2_RIGHT;
5439 case 0x16a6: // steel wall 2 (top end)
5440 element = EL_DC_STEELWALL_2_TOP;
5443 case 0x16a7: // steel wall 2 (bottom end)
5444 element = EL_DC_STEELWALL_2_BOTTOM;
5447 case 0x16a8: // steel wall 2 (left/right)
5448 element = EL_DC_STEELWALL_2_HORIZONTAL;
5451 case 0x16a9: // steel wall 2 (up/down)
5452 element = EL_DC_STEELWALL_2_VERTICAL;
5455 case 0x16aa: // steel wall 2 (mid)
5456 element = EL_DC_STEELWALL_2_MIDDLE;
5460 element = EL_SIGN_EXCLAMATION;
5464 element = EL_SIGN_RADIOACTIVITY;
5468 element = EL_SIGN_STOP;
5472 element = EL_SIGN_WHEELCHAIR;
5476 element = EL_SIGN_PARKING;
5480 element = EL_SIGN_NO_ENTRY;
5484 element = EL_SIGN_HEART;
5488 element = EL_SIGN_GIVE_WAY;
5492 element = EL_SIGN_ENTRY_FORBIDDEN;
5496 element = EL_SIGN_EMERGENCY_EXIT;
5500 element = EL_SIGN_YIN_YANG;
5504 element = EL_WALL_EMERALD;
5508 element = EL_WALL_DIAMOND;
5512 element = EL_WALL_PEARL;
5516 element = EL_WALL_CRYSTAL;
5520 element = EL_INVISIBLE_WALL;
5524 element = EL_INVISIBLE_STEELWALL;
5528 // EL_INVISIBLE_SAND
5531 element = EL_LIGHT_SWITCH;
5535 element = EL_ENVELOPE_1;
5539 if (element >= 0x0117 && element <= 0x036e) // (?)
5540 element = EL_DIAMOND;
5541 else if (element >= 0x042d && element <= 0x0684) // (?)
5542 element = EL_EMERALD;
5543 else if (element >= 0x157c && element <= 0x158b)
5545 else if (element >= 0x1590 && element <= 0x159f)
5546 element = EL_DC_LANDMINE;
5547 else if (element >= 0x16bc && element <= 0x16cb)
5548 element = EL_INVISIBLE_SAND;
5551 Error(ERR_WARN, "unknown Diamond Caves element 0x%04x", element);
5552 element = EL_UNKNOWN;
5557 return getMappedElement(element);
5560 static void LoadLevelFromFileStream_DC(File *file, struct LevelInfo *level,
5563 byte header[DC_LEVEL_HEADER_SIZE];
5565 int envelope_header_pos = 62;
5566 int envelope_content_pos = 94;
5567 int level_name_pos = 251;
5568 int level_author_pos = 292;
5569 int envelope_header_len;
5570 int envelope_content_len;
5572 int level_author_len;
5574 int num_yamyam_contents;
5577 getDecodedWord_DC(0, TRUE); // initialize DC2 decoding engine
5579 for (i = 0; i < DC_LEVEL_HEADER_SIZE / 2; i++)
5581 unsigned short header_word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
5583 header[i * 2 + 0] = header_word >> 8;
5584 header[i * 2 + 1] = header_word & 0xff;
5587 // read some values from level header to check level decoding integrity
5588 fieldx = header[6] | (header[7] << 8);
5589 fieldy = header[8] | (header[9] << 8);
5590 num_yamyam_contents = header[60] | (header[61] << 8);
5592 // do some simple sanity checks to ensure that level was correctly decoded
5593 if (fieldx < 1 || fieldx > 256 ||
5594 fieldy < 1 || fieldy > 256 ||
5595 num_yamyam_contents < 1 || num_yamyam_contents > 8)
5597 level->no_valid_file = TRUE;
5599 Error(ERR_WARN, "cannot decode level from stream -- using empty level");
5604 // maximum envelope header size is 31 bytes
5605 envelope_header_len = header[envelope_header_pos];
5606 // maximum envelope content size is 110 (156?) bytes
5607 envelope_content_len = header[envelope_content_pos];
5609 // maximum level title size is 40 bytes
5610 level_name_len = MIN(header[level_name_pos], MAX_LEVEL_NAME_LEN);
5611 // maximum level author size is 30 (51?) bytes
5612 level_author_len = MIN(header[level_author_pos], MAX_LEVEL_AUTHOR_LEN);
5616 for (i = 0; i < envelope_header_len; i++)
5617 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
5618 level->envelope[0].text[envelope_size++] =
5619 header[envelope_header_pos + 1 + i];
5621 if (envelope_header_len > 0 && envelope_content_len > 0)
5623 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
5624 level->envelope[0].text[envelope_size++] = '\n';
5625 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
5626 level->envelope[0].text[envelope_size++] = '\n';
5629 for (i = 0; i < envelope_content_len; i++)
5630 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
5631 level->envelope[0].text[envelope_size++] =
5632 header[envelope_content_pos + 1 + i];
5634 level->envelope[0].text[envelope_size] = '\0';
5636 level->envelope[0].xsize = MAX_ENVELOPE_XSIZE;
5637 level->envelope[0].ysize = 10;
5638 level->envelope[0].autowrap = TRUE;
5639 level->envelope[0].centered = TRUE;
5641 for (i = 0; i < level_name_len; i++)
5642 level->name[i] = header[level_name_pos + 1 + i];
5643 level->name[level_name_len] = '\0';
5645 for (i = 0; i < level_author_len; i++)
5646 level->author[i] = header[level_author_pos + 1 + i];
5647 level->author[level_author_len] = '\0';
5649 num_yamyam_contents = header[60] | (header[61] << 8);
5650 level->num_yamyam_contents =
5651 MIN(MAX(MIN_ELEMENT_CONTENTS, num_yamyam_contents), MAX_ELEMENT_CONTENTS);
5653 for (i = 0; i < num_yamyam_contents; i++)
5655 for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
5657 unsigned short word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
5658 int element_dc = ((word & 0xff) << 8) | ((word >> 8) & 0xff);
5660 if (i < MAX_ELEMENT_CONTENTS)
5661 level->yamyam_content[i].e[x][y] = getMappedElement_DC(element_dc);
5665 fieldx = header[6] | (header[7] << 8);
5666 fieldy = header[8] | (header[9] << 8);
5667 level->fieldx = MIN(MAX(MIN_LEV_FIELDX, fieldx), MAX_LEV_FIELDX);
5668 level->fieldy = MIN(MAX(MIN_LEV_FIELDY, fieldy), MAX_LEV_FIELDY);
5670 for (y = 0; y < fieldy; y++) for (x = 0; x < fieldx; x++)
5672 unsigned short word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
5673 int element_dc = ((word & 0xff) << 8) | ((word >> 8) & 0xff);
5675 if (x < MAX_LEV_FIELDX && y < MAX_LEV_FIELDY)
5676 level->field[x][y] = getMappedElement_DC(element_dc);
5679 x = MIN(MAX(0, (header[10] | (header[11] << 8)) - 1), MAX_LEV_FIELDX - 1);
5680 y = MIN(MAX(0, (header[12] | (header[13] << 8)) - 1), MAX_LEV_FIELDY - 1);
5681 level->field[x][y] = EL_PLAYER_1;
5683 x = MIN(MAX(0, (header[14] | (header[15] << 8)) - 1), MAX_LEV_FIELDX - 1);
5684 y = MIN(MAX(0, (header[16] | (header[17] << 8)) - 1), MAX_LEV_FIELDY - 1);
5685 level->field[x][y] = EL_PLAYER_2;
5687 level->gems_needed = header[18] | (header[19] << 8);
5689 level->score[SC_EMERALD] = header[20] | (header[21] << 8);
5690 level->score[SC_DIAMOND] = header[22] | (header[23] << 8);
5691 level->score[SC_PEARL] = header[24] | (header[25] << 8);
5692 level->score[SC_CRYSTAL] = header[26] | (header[27] << 8);
5693 level->score[SC_NUT] = header[28] | (header[29] << 8);
5694 level->score[SC_ROBOT] = header[30] | (header[31] << 8);
5695 level->score[SC_SPACESHIP] = header[32] | (header[33] << 8);
5696 level->score[SC_BUG] = header[34] | (header[35] << 8);
5697 level->score[SC_YAMYAM] = header[36] | (header[37] << 8);
5698 level->score[SC_DYNAMITE] = header[38] | (header[39] << 8);
5699 level->score[SC_KEY] = header[40] | (header[41] << 8);
5700 level->score[SC_TIME_BONUS] = header[42] | (header[43] << 8);
5702 level->time = header[44] | (header[45] << 8);
5704 level->amoeba_speed = header[46] | (header[47] << 8);
5705 level->time_light = header[48] | (header[49] << 8);
5706 level->time_timegate = header[50] | (header[51] << 8);
5707 level->time_wheel = header[52] | (header[53] << 8);
5708 level->time_magic_wall = header[54] | (header[55] << 8);
5709 level->extra_time = header[56] | (header[57] << 8);
5710 level->shield_normal_time = header[58] | (header[59] << 8);
5712 // Diamond Caves has the same (strange) behaviour as Emerald Mine that gems
5713 // can slip down from flat walls, like normal walls and steel walls
5714 level->em_slippery_gems = TRUE;
5717 static void LoadLevelFromFileInfo_DC(struct LevelInfo *level,
5718 struct LevelFileInfo *level_file_info,
5719 boolean level_info_only)
5721 char *filename = level_file_info->filename;
5723 int num_magic_bytes = 8;
5724 char magic_bytes[num_magic_bytes + 1];
5725 int num_levels_to_skip = level_file_info->nr - leveldir_current->first_level;
5727 if (!(file = openFile(filename, MODE_READ)))
5729 level->no_valid_file = TRUE;
5731 if (!level_info_only)
5732 Error(ERR_WARN, "cannot read level '%s' -- using empty level", filename);
5737 // fseek(file, 0x0000, SEEK_SET);
5739 if (level_file_info->packed)
5741 // read "magic bytes" from start of file
5742 if (getStringFromFile(file, magic_bytes, num_magic_bytes + 1) == NULL)
5743 magic_bytes[0] = '\0';
5745 // check "magic bytes" for correct file format
5746 if (!strPrefix(magic_bytes, "DC2"))
5748 level->no_valid_file = TRUE;
5750 Error(ERR_WARN, "unknown DC level file '%s' -- using empty level",
5756 if (strPrefix(magic_bytes, "DC2Win95") ||
5757 strPrefix(magic_bytes, "DC2Win98"))
5759 int position_first_level = 0x00fa;
5760 int extra_bytes = 4;
5763 // advance file stream to first level inside the level package
5764 skip_bytes = position_first_level - num_magic_bytes - extra_bytes;
5766 // each block of level data is followed by block of non-level data
5767 num_levels_to_skip *= 2;
5769 // at least skip header bytes, therefore use ">= 0" instead of "> 0"
5770 while (num_levels_to_skip >= 0)
5772 // advance file stream to next level inside the level package
5773 if (seekFile(file, skip_bytes, SEEK_CUR) != 0)
5775 level->no_valid_file = TRUE;
5777 Error(ERR_WARN, "cannot fseek in file '%s' -- using empty level",
5783 // skip apparently unused extra bytes following each level
5784 ReadUnusedBytesFromFile(file, extra_bytes);
5786 // read size of next level in level package
5787 skip_bytes = getFile32BitLE(file);
5789 num_levels_to_skip--;
5794 level->no_valid_file = TRUE;
5796 Error(ERR_WARN, "unknown DC2 level file '%s' -- using empty level",
5803 LoadLevelFromFileStream_DC(file, level, level_file_info->nr);
5809 // ----------------------------------------------------------------------------
5810 // functions for loading SB level
5811 // ----------------------------------------------------------------------------
5813 int getMappedElement_SB(int element_ascii, boolean use_ces)
5821 sb_element_mapping[] =
5823 { ' ', EL_EMPTY, EL_CUSTOM_1 }, // floor (space)
5824 { '#', EL_STEELWALL, EL_CUSTOM_2 }, // wall
5825 { '@', EL_PLAYER_1, EL_CUSTOM_3 }, // player
5826 { '$', EL_SOKOBAN_OBJECT, EL_CUSTOM_4 }, // box
5827 { '.', EL_SOKOBAN_FIELD_EMPTY, EL_CUSTOM_5 }, // goal square
5828 { '*', EL_SOKOBAN_FIELD_FULL, EL_CUSTOM_6 }, // box on goal square
5829 { '+', EL_SOKOBAN_FIELD_PLAYER, EL_CUSTOM_7 }, // player on goal square
5830 { '_', EL_INVISIBLE_STEELWALL, EL_FROM_LEVEL_TEMPLATE }, // floor beyond border
5837 for (i = 0; sb_element_mapping[i].ascii != 0; i++)
5838 if (element_ascii == sb_element_mapping[i].ascii)
5839 return (use_ces ? sb_element_mapping[i].ce : sb_element_mapping[i].sb);
5841 return EL_UNDEFINED;
5844 static void LoadLevelFromFileInfo_SB(struct LevelInfo *level,
5845 struct LevelFileInfo *level_file_info,
5846 boolean level_info_only)
5848 char *filename = level_file_info->filename;
5849 char line[MAX_LINE_LEN], line_raw[MAX_LINE_LEN], previous_line[MAX_LINE_LEN];
5850 char last_comment[MAX_LINE_LEN];
5851 char level_name[MAX_LINE_LEN];
5854 int num_levels_to_skip = level_file_info->nr - leveldir_current->first_level;
5855 boolean read_continued_line = FALSE;
5856 boolean reading_playfield = FALSE;
5857 boolean got_valid_playfield_line = FALSE;
5858 boolean invalid_playfield_char = FALSE;
5859 boolean load_xsb_to_ces = check_special_flags("load_xsb_to_ces");
5860 int file_level_nr = 0;
5862 int x = 0, y = 0; // initialized to make compilers happy
5864 last_comment[0] = '\0';
5865 level_name[0] = '\0';
5867 if (!(file = openFile(filename, MODE_READ)))
5869 level->no_valid_file = TRUE;
5871 if (!level_info_only)
5872 Error(ERR_WARN, "cannot read level '%s' -- using empty level", filename);
5877 while (!checkEndOfFile(file))
5879 // level successfully read, but next level may follow here
5880 if (!got_valid_playfield_line && reading_playfield)
5882 // read playfield from single level file -- skip remaining file
5883 if (!level_file_info->packed)
5886 if (file_level_nr >= num_levels_to_skip)
5891 last_comment[0] = '\0';
5892 level_name[0] = '\0';
5894 reading_playfield = FALSE;
5897 got_valid_playfield_line = FALSE;
5899 // read next line of input file
5900 if (!getStringFromFile(file, line, MAX_LINE_LEN))
5903 // check if line was completely read and is terminated by line break
5904 if (strlen(line) > 0 && line[strlen(line) - 1] == '\n')
5907 // cut trailing line break (this can be newline and/or carriage return)
5908 for (line_ptr = &line[strlen(line)]; line_ptr >= line; line_ptr--)
5909 if ((*line_ptr == '\n' || *line_ptr == '\r') && *(line_ptr + 1) == '\0')
5912 // copy raw input line for later use (mainly debugging output)
5913 strcpy(line_raw, line);
5915 if (read_continued_line)
5917 // append new line to existing line, if there is enough space
5918 if (strlen(previous_line) + strlen(line_ptr) < MAX_LINE_LEN)
5919 strcat(previous_line, line_ptr);
5921 strcpy(line, previous_line); // copy storage buffer to line
5923 read_continued_line = FALSE;
5926 // if the last character is '\', continue at next line
5927 if (strlen(line) > 0 && line[strlen(line) - 1] == '\\')
5929 line[strlen(line) - 1] = '\0'; // cut off trailing backslash
5930 strcpy(previous_line, line); // copy line to storage buffer
5932 read_continued_line = TRUE;
5938 if (line[0] == '\0')
5941 // extract comment text from comment line
5944 for (line_ptr = line; *line_ptr; line_ptr++)
5945 if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != ';')
5948 strcpy(last_comment, line_ptr);
5953 // extract level title text from line containing level title
5954 if (line[0] == '\'')
5956 strcpy(level_name, &line[1]);
5958 if (strlen(level_name) > 0 && level_name[strlen(level_name) - 1] == '\'')
5959 level_name[strlen(level_name) - 1] = '\0';
5964 // skip lines containing only spaces (or empty lines)
5965 for (line_ptr = line; *line_ptr; line_ptr++)
5966 if (*line_ptr != ' ')
5968 if (*line_ptr == '\0')
5971 // at this point, we have found a line containing part of a playfield
5973 got_valid_playfield_line = TRUE;
5975 if (!reading_playfield)
5977 reading_playfield = TRUE;
5978 invalid_playfield_char = FALSE;
5980 for (x = 0; x < MAX_LEV_FIELDX; x++)
5981 for (y = 0; y < MAX_LEV_FIELDY; y++)
5982 level->field[x][y] = getMappedElement_SB(' ', load_xsb_to_ces);
5987 // start with topmost tile row
5991 // skip playfield line if larger row than allowed
5992 if (y >= MAX_LEV_FIELDY)
5995 // start with leftmost tile column
5998 // read playfield elements from line
5999 for (line_ptr = line; *line_ptr; line_ptr++)
6001 int mapped_sb_element = getMappedElement_SB(*line_ptr, load_xsb_to_ces);
6003 // stop parsing playfield line if larger column than allowed
6004 if (x >= MAX_LEV_FIELDX)
6007 if (mapped_sb_element == EL_UNDEFINED)
6009 invalid_playfield_char = TRUE;
6014 level->field[x][y] = mapped_sb_element;
6016 // continue with next tile column
6019 level->fieldx = MAX(x, level->fieldx);
6022 if (invalid_playfield_char)
6024 // if first playfield line, treat invalid lines as comment lines
6026 reading_playfield = FALSE;
6031 // continue with next tile row
6039 level->fieldx = MIN(MAX(MIN_LEV_FIELDX, level->fieldx), MAX_LEV_FIELDX);
6040 level->fieldy = MIN(MAX(MIN_LEV_FIELDY, level->fieldy), MAX_LEV_FIELDY);
6042 if (!reading_playfield)
6044 level->no_valid_file = TRUE;
6046 Error(ERR_WARN, "cannot read level '%s' -- using empty level", filename);
6051 if (*level_name != '\0')
6053 strncpy(level->name, level_name, MAX_LEVEL_NAME_LEN);
6054 level->name[MAX_LEVEL_NAME_LEN] = '\0';
6056 else if (*last_comment != '\0')
6058 strncpy(level->name, last_comment, MAX_LEVEL_NAME_LEN);
6059 level->name[MAX_LEVEL_NAME_LEN] = '\0';
6063 sprintf(level->name, "--> Level %d <--", level_file_info->nr);
6066 // set all empty fields beyond the border walls to invisible steel wall
6067 for (y = 0; y < level->fieldy; y++) for (x = 0; x < level->fieldx; x++)
6069 if ((x == 0 || x == level->fieldx - 1 ||
6070 y == 0 || y == level->fieldy - 1) &&
6071 level->field[x][y] == getMappedElement_SB(' ', load_xsb_to_ces))
6072 FloodFillLevel(x, y, getMappedElement_SB('_', load_xsb_to_ces),
6073 level->field, level->fieldx, level->fieldy);
6076 // set special level settings for Sokoban levels
6079 level->use_step_counter = TRUE;
6081 if (load_xsb_to_ces)
6083 // special global settings can now be set in level template
6085 level->use_custom_template = TRUE;
6090 // -------------------------------------------------------------------------
6091 // functions for handling native levels
6092 // -------------------------------------------------------------------------
6094 static void LoadLevelFromFileInfo_EM(struct LevelInfo *level,
6095 struct LevelFileInfo *level_file_info,
6096 boolean level_info_only)
6098 if (!LoadNativeLevel_EM(level_file_info->filename, level_info_only))
6099 level->no_valid_file = TRUE;
6102 static void LoadLevelFromFileInfo_SP(struct LevelInfo *level,
6103 struct LevelFileInfo *level_file_info,
6104 boolean level_info_only)
6108 // determine position of requested level inside level package
6109 if (level_file_info->packed)
6110 pos = level_file_info->nr - leveldir_current->first_level;
6112 if (!LoadNativeLevel_SP(level_file_info->filename, pos, level_info_only))
6113 level->no_valid_file = TRUE;
6116 static void LoadLevelFromFileInfo_MM(struct LevelInfo *level,
6117 struct LevelFileInfo *level_file_info,
6118 boolean level_info_only)
6120 if (!LoadNativeLevel_MM(level_file_info->filename, level_info_only))
6121 level->no_valid_file = TRUE;
6124 void CopyNativeLevel_RND_to_Native(struct LevelInfo *level)
6126 if (level->game_engine_type == GAME_ENGINE_TYPE_EM)
6127 CopyNativeLevel_RND_to_EM(level);
6128 else if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
6129 CopyNativeLevel_RND_to_SP(level);
6130 else if (level->game_engine_type == GAME_ENGINE_TYPE_MM)
6131 CopyNativeLevel_RND_to_MM(level);
6134 void CopyNativeLevel_Native_to_RND(struct LevelInfo *level)
6136 if (level->game_engine_type == GAME_ENGINE_TYPE_EM)
6137 CopyNativeLevel_EM_to_RND(level);
6138 else if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
6139 CopyNativeLevel_SP_to_RND(level);
6140 else if (level->game_engine_type == GAME_ENGINE_TYPE_MM)
6141 CopyNativeLevel_MM_to_RND(level);
6144 void SaveNativeLevel(struct LevelInfo *level)
6146 if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
6148 char *basename = getSingleLevelBasenameExt(level->file_info.nr, "sp");
6149 char *filename = getLevelFilenameFromBasename(basename);
6151 CopyNativeLevel_RND_to_SP(level);
6152 CopyNativeTape_RND_to_SP(level);
6154 SaveNativeLevel_SP(filename);
6159 // ----------------------------------------------------------------------------
6160 // functions for loading generic level
6161 // ----------------------------------------------------------------------------
6163 static void LoadLevelFromFileInfo(struct LevelInfo *level,
6164 struct LevelFileInfo *level_file_info,
6165 boolean level_info_only)
6167 // always start with reliable default values
6168 setLevelInfoToDefaults(level, level_info_only, TRUE);
6170 switch (level_file_info->type)
6172 case LEVEL_FILE_TYPE_RND:
6173 LoadLevelFromFileInfo_RND(level, level_file_info, level_info_only);
6176 case LEVEL_FILE_TYPE_EM:
6177 LoadLevelFromFileInfo_EM(level, level_file_info, level_info_only);
6178 level->game_engine_type = GAME_ENGINE_TYPE_EM;
6181 case LEVEL_FILE_TYPE_SP:
6182 LoadLevelFromFileInfo_SP(level, level_file_info, level_info_only);
6183 level->game_engine_type = GAME_ENGINE_TYPE_SP;
6186 case LEVEL_FILE_TYPE_MM:
6187 LoadLevelFromFileInfo_MM(level, level_file_info, level_info_only);
6188 level->game_engine_type = GAME_ENGINE_TYPE_MM;
6191 case LEVEL_FILE_TYPE_DC:
6192 LoadLevelFromFileInfo_DC(level, level_file_info, level_info_only);
6195 case LEVEL_FILE_TYPE_SB:
6196 LoadLevelFromFileInfo_SB(level, level_file_info, level_info_only);
6200 LoadLevelFromFileInfo_RND(level, level_file_info, level_info_only);
6204 // if level file is invalid, restore level structure to default values
6205 if (level->no_valid_file)
6206 setLevelInfoToDefaults(level, level_info_only, FALSE);
6208 if (level->game_engine_type == GAME_ENGINE_TYPE_UNKNOWN)
6209 level->game_engine_type = GAME_ENGINE_TYPE_RND;
6211 if (level_file_info->type != LEVEL_FILE_TYPE_RND)
6212 CopyNativeLevel_Native_to_RND(level);
6215 void LoadLevelFromFilename(struct LevelInfo *level, char *filename)
6217 static struct LevelFileInfo level_file_info;
6219 // always start with reliable default values
6220 setFileInfoToDefaults(&level_file_info);
6222 level_file_info.nr = 0; // unknown level number
6223 level_file_info.type = LEVEL_FILE_TYPE_RND; // no others supported yet
6225 setString(&level_file_info.filename, filename);
6227 LoadLevelFromFileInfo(level, &level_file_info, FALSE);
6230 static void LoadLevel_InitVersion(struct LevelInfo *level)
6234 if (leveldir_current == NULL) // only when dumping level
6237 // all engine modifications also valid for levels which use latest engine
6238 if (level->game_version < VERSION_IDENT(3,2,0,5))
6240 // time bonus score was given for 10 s instead of 1 s before 3.2.0-5
6241 level->score[SC_TIME_BONUS] /= 10;
6244 if (leveldir_current->latest_engine)
6246 // ---------- use latest game engine --------------------------------------
6248 /* For all levels which are forced to use the latest game engine version
6249 (normally all but user contributed, private and undefined levels), set
6250 the game engine version to the actual version; this allows for actual
6251 corrections in the game engine to take effect for existing, converted
6252 levels (from "classic" or other existing games) to make the emulation
6253 of the corresponding game more accurate, while (hopefully) not breaking
6254 existing levels created from other players. */
6256 level->game_version = GAME_VERSION_ACTUAL;
6258 /* Set special EM style gems behaviour: EM style gems slip down from
6259 normal, steel and growing wall. As this is a more fundamental change,
6260 it seems better to set the default behaviour to "off" (as it is more
6261 natural) and make it configurable in the level editor (as a property
6262 of gem style elements). Already existing converted levels (neither
6263 private nor contributed levels) are changed to the new behaviour. */
6265 if (level->file_version < FILE_VERSION_2_0)
6266 level->em_slippery_gems = TRUE;
6271 // ---------- use game engine the level was created with --------------------
6273 /* For all levels which are not forced to use the latest game engine
6274 version (normally user contributed, private and undefined levels),
6275 use the version of the game engine the levels were created for.
6277 Since 2.0.1, the game engine version is now directly stored
6278 in the level file (chunk "VERS"), so there is no need anymore
6279 to set the game version from the file version (except for old,
6280 pre-2.0 levels, where the game version is still taken from the
6281 file format version used to store the level -- see above). */
6283 // player was faster than enemies in 1.0.0 and before
6284 if (level->file_version == FILE_VERSION_1_0)
6285 for (i = 0; i < MAX_PLAYERS; i++)
6286 level->initial_player_stepsize[i] = STEPSIZE_FAST;
6288 // default behaviour for EM style gems was "slippery" only in 2.0.1
6289 if (level->game_version == VERSION_IDENT(2,0,1,0))
6290 level->em_slippery_gems = TRUE;
6292 // springs could be pushed over pits before (pre-release version) 2.2.0
6293 if (level->game_version < VERSION_IDENT(2,2,0,0))
6294 level->use_spring_bug = TRUE;
6296 if (level->game_version < VERSION_IDENT(3,2,0,5))
6298 // time orb caused limited time in endless time levels before 3.2.0-5
6299 level->use_time_orb_bug = TRUE;
6301 // default behaviour for snapping was "no snap delay" before 3.2.0-5
6302 level->block_snap_field = FALSE;
6304 // extra time score was same value as time left score before 3.2.0-5
6305 level->extra_time_score = level->score[SC_TIME_BONUS];
6308 if (level->game_version < VERSION_IDENT(3,2,0,7))
6310 // default behaviour for snapping was "not continuous" before 3.2.0-7
6311 level->continuous_snapping = FALSE;
6314 // only few elements were able to actively move into acid before 3.1.0
6315 // trigger settings did not exist before 3.1.0; set to default "any"
6316 if (level->game_version < VERSION_IDENT(3,1,0,0))
6318 // correct "can move into acid" settings (all zero in old levels)
6320 level->can_move_into_acid_bits = 0; // nothing can move into acid
6321 level->dont_collide_with_bits = 0; // nothing is deadly when colliding
6323 setMoveIntoAcidProperty(level, EL_ROBOT, TRUE);
6324 setMoveIntoAcidProperty(level, EL_SATELLITE, TRUE);
6325 setMoveIntoAcidProperty(level, EL_PENGUIN, TRUE);
6326 setMoveIntoAcidProperty(level, EL_BALLOON, TRUE);
6328 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6329 SET_PROPERTY(EL_CUSTOM_START + i, EP_CAN_MOVE_INTO_ACID, TRUE);
6331 // correct trigger settings (stored as zero == "none" in old levels)
6333 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6335 int element = EL_CUSTOM_START + i;
6336 struct ElementInfo *ei = &element_info[element];
6338 for (j = 0; j < ei->num_change_pages; j++)
6340 struct ElementChangeInfo *change = &ei->change_page[j];
6342 change->trigger_player = CH_PLAYER_ANY;
6343 change->trigger_page = CH_PAGE_ANY;
6348 // try to detect and fix "Snake Bite" levels, which are broken with 3.2.0
6350 int element = EL_CUSTOM_256;
6351 struct ElementInfo *ei = &element_info[element];
6352 struct ElementChangeInfo *change = &ei->change_page[0];
6354 /* This is needed to fix a problem that was caused by a bugfix in function
6355 game.c/CreateFieldExt() introduced with 3.2.0 that corrects the behaviour
6356 when a custom element changes to EL_SOKOBAN_FIELD_PLAYER (before, it did
6357 not replace walkable elements, but instead just placed the player on it,
6358 without placing the Sokoban field under the player). Unfortunately, this
6359 breaks "Snake Bite" style levels when the snake is halfway through a door
6360 that just closes (the snake head is still alive and can be moved in this
6361 case). This can be fixed by replacing the EL_SOKOBAN_FIELD_PLAYER by the
6362 player (without Sokoban element) which then gets killed as designed). */
6364 if ((strncmp(leveldir_current->identifier, "snake_bite", 10) == 0 ||
6365 strncmp(ei->description, "pause b4 death", 14) == 0) &&
6366 change->target_element == EL_SOKOBAN_FIELD_PLAYER)
6367 change->target_element = EL_PLAYER_1;
6370 // try to detect and fix "Zelda" style levels, which are broken with 3.2.5
6371 if (level->game_version < VERSION_IDENT(3,2,5,0))
6373 /* This is needed to fix a problem that was caused by a bugfix in function
6374 game.c/CheckTriggeredElementChangeExt() introduced with 3.2.5 that
6375 corrects the behaviour when a custom element changes to another custom
6376 element with a higher element number that has change actions defined.
6377 Normally, only one change per frame is allowed for custom elements.
6378 Therefore, it is checked if a custom element already changed in the
6379 current frame; if it did, subsequent changes are suppressed.
6380 Unfortunately, this is only checked for element changes, but not for
6381 change actions, which are still executed. As the function above loops
6382 through all custom elements from lower to higher, an element change
6383 resulting in a lower CE number won't be checked again, while a target
6384 element with a higher number will also be checked, and potential change
6385 actions will get executed for this CE, too (which is wrong), while
6386 further changes are ignored (which is correct). As this bugfix breaks
6387 Zelda II (and introduces graphical bugs to Zelda I, and also breaks a
6388 few other levels like Alan Bond's "FMV"), allow the previous, incorrect
6389 behaviour for existing levels and tapes that make use of this bug */
6391 level->use_action_after_change_bug = TRUE;
6394 // not centering level after relocating player was default only in 3.2.3
6395 if (level->game_version == VERSION_IDENT(3,2,3,0)) // (no pre-releases)
6396 level->shifted_relocation = TRUE;
6398 // EM style elements always chain-exploded in R'n'D engine before 3.2.6
6399 if (level->game_version < VERSION_IDENT(3,2,6,0))
6400 level->em_explodes_by_fire = TRUE;
6402 // levels were solved by the first player entering an exit up to 4.1.0.0
6403 if (level->game_version <= VERSION_IDENT(4,1,0,0))
6404 level->solved_by_one_player = TRUE;
6406 // game logic of "game of life" and "biomaze" was buggy before 4.1.1.1
6407 if (level->game_version < VERSION_IDENT(4,1,1,1))
6408 level->use_life_bugs = TRUE;
6410 // only Sokoban fields (but not objects) had to be solved before 4.1.1.1
6411 if (level->game_version < VERSION_IDENT(4,1,1,1))
6412 level->sb_objects_needed = FALSE;
6415 static void LoadLevel_InitStandardElements(struct LevelInfo *level)
6419 // map elements that have changed in newer versions
6420 level->amoeba_content = getMappedElementByVersion(level->amoeba_content,
6421 level->game_version);
6422 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
6423 for (x = 0; x < 3; x++)
6424 for (y = 0; y < 3; y++)
6425 level->yamyam_content[i].e[x][y] =
6426 getMappedElementByVersion(level->yamyam_content[i].e[x][y],
6427 level->game_version);
6431 static void LoadLevel_InitCustomElements(struct LevelInfo *level)
6435 // map custom element change events that have changed in newer versions
6436 // (these following values were accidentally changed in version 3.0.1)
6437 // (this seems to be needed only for 'ab_levelset3' and 'ab_levelset4')
6438 if (level->game_version <= VERSION_IDENT(3,0,0,0))
6440 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6442 int element = EL_CUSTOM_START + i;
6444 // order of checking and copying events to be mapped is important
6445 // (do not change the start and end value -- they are constant)
6446 for (j = CE_BY_OTHER_ACTION; j >= CE_VALUE_GETS_ZERO; j--)
6448 if (HAS_CHANGE_EVENT(element, j - 2))
6450 SET_CHANGE_EVENT(element, j - 2, FALSE);
6451 SET_CHANGE_EVENT(element, j, TRUE);
6455 // order of checking and copying events to be mapped is important
6456 // (do not change the start and end value -- they are constant)
6457 for (j = CE_PLAYER_COLLECTS_X; j >= CE_HITTING_SOMETHING; j--)
6459 if (HAS_CHANGE_EVENT(element, j - 1))
6461 SET_CHANGE_EVENT(element, j - 1, FALSE);
6462 SET_CHANGE_EVENT(element, j, TRUE);
6468 // initialize "can_change" field for old levels with only one change page
6469 if (level->game_version <= VERSION_IDENT(3,0,2,0))
6471 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6473 int element = EL_CUSTOM_START + i;
6475 if (CAN_CHANGE(element))
6476 element_info[element].change->can_change = TRUE;
6480 // correct custom element values (for old levels without these options)
6481 if (level->game_version < VERSION_IDENT(3,1,1,0))
6483 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6485 int element = EL_CUSTOM_START + i;
6486 struct ElementInfo *ei = &element_info[element];
6488 if (ei->access_direction == MV_NO_DIRECTION)
6489 ei->access_direction = MV_ALL_DIRECTIONS;
6493 // correct custom element values (fix invalid values for all versions)
6496 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6498 int element = EL_CUSTOM_START + i;
6499 struct ElementInfo *ei = &element_info[element];
6501 for (j = 0; j < ei->num_change_pages; j++)
6503 struct ElementChangeInfo *change = &ei->change_page[j];
6505 if (change->trigger_player == CH_PLAYER_NONE)
6506 change->trigger_player = CH_PLAYER_ANY;
6508 if (change->trigger_side == CH_SIDE_NONE)
6509 change->trigger_side = CH_SIDE_ANY;
6514 // initialize "can_explode" field for old levels which did not store this
6515 // !!! CHECK THIS -- "<= 3,1,0,0" IS PROBABLY WRONG !!!
6516 if (level->game_version <= VERSION_IDENT(3,1,0,0))
6518 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6520 int element = EL_CUSTOM_START + i;
6522 if (EXPLODES_1X1_OLD(element))
6523 element_info[element].explosion_type = EXPLODES_1X1;
6525 SET_PROPERTY(element, EP_CAN_EXPLODE, (EXPLODES_BY_FIRE(element) ||
6526 EXPLODES_SMASHED(element) ||
6527 EXPLODES_IMPACT(element)));
6531 // correct previously hard-coded move delay values for maze runner style
6532 if (level->game_version < VERSION_IDENT(3,1,1,0))
6534 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6536 int element = EL_CUSTOM_START + i;
6538 if (element_info[element].move_pattern & MV_MAZE_RUNNER_STYLE)
6540 // previously hard-coded and therefore ignored
6541 element_info[element].move_delay_fixed = 9;
6542 element_info[element].move_delay_random = 0;
6547 // set some other uninitialized values of custom elements in older levels
6548 if (level->game_version < VERSION_IDENT(3,1,0,0))
6550 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6552 int element = EL_CUSTOM_START + i;
6554 element_info[element].access_direction = MV_ALL_DIRECTIONS;
6556 element_info[element].explosion_delay = 17;
6557 element_info[element].ignition_delay = 8;
6562 static void LoadLevel_InitElements(struct LevelInfo *level)
6564 LoadLevel_InitStandardElements(level);
6566 if (level->file_has_custom_elements)
6567 LoadLevel_InitCustomElements(level);
6569 // initialize element properties for level editor etc.
6570 InitElementPropertiesEngine(level->game_version);
6571 InitElementPropertiesGfxElement();
6574 static void LoadLevel_InitPlayfield(struct LevelInfo *level)
6578 // map elements that have changed in newer versions
6579 for (y = 0; y < level->fieldy; y++)
6580 for (x = 0; x < level->fieldx; x++)
6581 level->field[x][y] = getMappedElementByVersion(level->field[x][y],
6582 level->game_version);
6584 // clear unused playfield data (nicer if level gets resized in editor)
6585 for (x = 0; x < MAX_LEV_FIELDX; x++)
6586 for (y = 0; y < MAX_LEV_FIELDY; y++)
6587 if (x >= level->fieldx || y >= level->fieldy)
6588 level->field[x][y] = EL_EMPTY;
6590 // copy elements to runtime playfield array
6591 for (x = 0; x < MAX_LEV_FIELDX; x++)
6592 for (y = 0; y < MAX_LEV_FIELDY; y++)
6593 Feld[x][y] = level->field[x][y];
6595 // initialize level size variables for faster access
6596 lev_fieldx = level->fieldx;
6597 lev_fieldy = level->fieldy;
6599 // determine border element for this level
6600 if (level->file_info.type == LEVEL_FILE_TYPE_DC)
6601 BorderElement = EL_EMPTY; // (in editor, SetBorderElement() is used)
6606 static void LoadLevel_InitNativeEngines(struct LevelInfo *level)
6608 struct LevelFileInfo *level_file_info = &level->file_info;
6610 if (level_file_info->type == LEVEL_FILE_TYPE_RND)
6611 CopyNativeLevel_RND_to_Native(level);
6614 static void LoadLevelTemplate_LoadAndInit(void)
6616 LoadLevelFromFileInfo(&level_template, &level_template.file_info, FALSE);
6618 LoadLevel_InitVersion(&level_template);
6619 LoadLevel_InitElements(&level_template);
6621 ActivateLevelTemplate();
6624 void LoadLevelTemplate(int nr)
6626 if (!fileExists(getGlobalLevelTemplateFilename()))
6628 Error(ERR_WARN, "no level template found for this level");
6633 setLevelFileInfo(&level_template.file_info, nr);
6635 LoadLevelTemplate_LoadAndInit();
6638 static void LoadNetworkLevelTemplate(struct NetworkLevelInfo *network_level)
6640 copyLevelFileInfo(&network_level->tmpl_info, &level_template.file_info);
6642 LoadLevelTemplate_LoadAndInit();
6645 static void LoadLevel_LoadAndInit(struct NetworkLevelInfo *network_level)
6647 LoadLevelFromFileInfo(&level, &level.file_info, FALSE);
6649 if (level.use_custom_template)
6651 if (network_level != NULL)
6652 LoadNetworkLevelTemplate(network_level);
6654 LoadLevelTemplate(-1);
6657 LoadLevel_InitVersion(&level);
6658 LoadLevel_InitElements(&level);
6659 LoadLevel_InitPlayfield(&level);
6661 LoadLevel_InitNativeEngines(&level);
6664 void LoadLevel(int nr)
6666 SetLevelSetInfo(leveldir_current->identifier, nr);
6668 setLevelFileInfo(&level.file_info, nr);
6670 LoadLevel_LoadAndInit(NULL);
6673 void LoadLevelInfoOnly(int nr)
6675 setLevelFileInfo(&level.file_info, nr);
6677 LoadLevelFromFileInfo(&level, &level.file_info, TRUE);
6680 void LoadNetworkLevel(struct NetworkLevelInfo *network_level)
6682 SetLevelSetInfo(network_level->leveldir_identifier,
6683 network_level->file_info.nr);
6685 copyLevelFileInfo(&network_level->file_info, &level.file_info);
6687 LoadLevel_LoadAndInit(network_level);
6690 static int SaveLevel_VERS(FILE *file, struct LevelInfo *level)
6694 chunk_size += putFileVersion(file, level->file_version);
6695 chunk_size += putFileVersion(file, level->game_version);
6700 static int SaveLevel_DATE(FILE *file, struct LevelInfo *level)
6704 chunk_size += putFile16BitBE(file, level->creation_date.year);
6705 chunk_size += putFile8Bit(file, level->creation_date.month);
6706 chunk_size += putFile8Bit(file, level->creation_date.day);
6711 #if ENABLE_HISTORIC_CHUNKS
6712 static void SaveLevel_HEAD(FILE *file, struct LevelInfo *level)
6716 putFile8Bit(file, level->fieldx);
6717 putFile8Bit(file, level->fieldy);
6719 putFile16BitBE(file, level->time);
6720 putFile16BitBE(file, level->gems_needed);
6722 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
6723 putFile8Bit(file, level->name[i]);
6725 for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
6726 putFile8Bit(file, level->score[i]);
6728 for (i = 0; i < STD_ELEMENT_CONTENTS; i++)
6729 for (y = 0; y < 3; y++)
6730 for (x = 0; x < 3; x++)
6731 putFile8Bit(file, (level->encoding_16bit_yamyam ? EL_EMPTY :
6732 level->yamyam_content[i].e[x][y]));
6733 putFile8Bit(file, level->amoeba_speed);
6734 putFile8Bit(file, level->time_magic_wall);
6735 putFile8Bit(file, level->time_wheel);
6736 putFile8Bit(file, (level->encoding_16bit_amoeba ? EL_EMPTY :
6737 level->amoeba_content));
6738 putFile8Bit(file, (level->initial_player_stepsize == STEPSIZE_FAST ? 1 : 0));
6739 putFile8Bit(file, (level->initial_gravity ? 1 : 0));
6740 putFile8Bit(file, (level->encoding_16bit_field ? 1 : 0));
6741 putFile8Bit(file, (level->em_slippery_gems ? 1 : 0));
6743 putFile8Bit(file, (level->use_custom_template ? 1 : 0));
6745 putFile8Bit(file, (level->block_last_field ? 1 : 0));
6746 putFile8Bit(file, (level->sp_block_last_field ? 1 : 0));
6747 putFile32BitBE(file, level->can_move_into_acid_bits);
6748 putFile8Bit(file, level->dont_collide_with_bits);
6750 putFile8Bit(file, (level->use_spring_bug ? 1 : 0));
6751 putFile8Bit(file, (level->use_step_counter ? 1 : 0));
6753 putFile8Bit(file, (level->instant_relocation ? 1 : 0));
6754 putFile8Bit(file, (level->can_pass_to_walkable ? 1 : 0));
6755 putFile8Bit(file, (level->grow_into_diggable ? 1 : 0));
6757 putFile8Bit(file, level->game_engine_type);
6759 WriteUnusedBytesToFile(file, LEVEL_CHUNK_HEAD_UNUSED);
6763 static int SaveLevel_NAME(FILE *file, struct LevelInfo *level)
6768 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
6769 chunk_size += putFile8Bit(file, level->name[i]);
6774 static int SaveLevel_AUTH(FILE *file, struct LevelInfo *level)
6779 for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
6780 chunk_size += putFile8Bit(file, level->author[i]);
6785 #if ENABLE_HISTORIC_CHUNKS
6786 static int SaveLevel_BODY(FILE *file, struct LevelInfo *level)
6791 for (y = 0; y < level->fieldy; y++)
6792 for (x = 0; x < level->fieldx; x++)
6793 if (level->encoding_16bit_field)
6794 chunk_size += putFile16BitBE(file, level->field[x][y]);
6796 chunk_size += putFile8Bit(file, level->field[x][y]);
6802 static int SaveLevel_BODY(FILE *file, struct LevelInfo *level)
6807 for (y = 0; y < level->fieldy; y++)
6808 for (x = 0; x < level->fieldx; x++)
6809 chunk_size += putFile16BitBE(file, level->field[x][y]);
6814 #if ENABLE_HISTORIC_CHUNKS
6815 static void SaveLevel_CONT(FILE *file, struct LevelInfo *level)
6819 putFile8Bit(file, EL_YAMYAM);
6820 putFile8Bit(file, level->num_yamyam_contents);
6821 putFile8Bit(file, 0);
6822 putFile8Bit(file, 0);
6824 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
6825 for (y = 0; y < 3; y++)
6826 for (x = 0; x < 3; x++)
6827 if (level->encoding_16bit_field)
6828 putFile16BitBE(file, level->yamyam_content[i].e[x][y]);
6830 putFile8Bit(file, level->yamyam_content[i].e[x][y]);
6834 #if ENABLE_HISTORIC_CHUNKS
6835 static void SaveLevel_CNT2(FILE *file, struct LevelInfo *level, int element)
6838 int num_contents, content_xsize, content_ysize;
6839 int content_array[MAX_ELEMENT_CONTENTS][3][3];
6841 if (element == EL_YAMYAM)
6843 num_contents = level->num_yamyam_contents;
6847 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
6848 for (y = 0; y < 3; y++)
6849 for (x = 0; x < 3; x++)
6850 content_array[i][x][y] = level->yamyam_content[i].e[x][y];
6852 else if (element == EL_BD_AMOEBA)
6858 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
6859 for (y = 0; y < 3; y++)
6860 for (x = 0; x < 3; x++)
6861 content_array[i][x][y] = EL_EMPTY;
6862 content_array[0][0][0] = level->amoeba_content;
6866 // chunk header already written -- write empty chunk data
6867 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_SIZE);
6869 Error(ERR_WARN, "cannot save content for element '%d'", element);
6873 putFile16BitBE(file, element);
6874 putFile8Bit(file, num_contents);
6875 putFile8Bit(file, content_xsize);
6876 putFile8Bit(file, content_ysize);
6878 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_UNUSED);
6880 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
6881 for (y = 0; y < 3; y++)
6882 for (x = 0; x < 3; x++)
6883 putFile16BitBE(file, content_array[i][x][y]);
6887 #if ENABLE_HISTORIC_CHUNKS
6888 static int SaveLevel_CNT3(FILE *file, struct LevelInfo *level, int element)
6890 int envelope_nr = element - EL_ENVELOPE_1;
6891 int envelope_len = strlen(level->envelope_text[envelope_nr]) + 1;
6895 chunk_size += putFile16BitBE(file, element);
6896 chunk_size += putFile16BitBE(file, envelope_len);
6897 chunk_size += putFile8Bit(file, level->envelope_xsize[envelope_nr]);
6898 chunk_size += putFile8Bit(file, level->envelope_ysize[envelope_nr]);
6900 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT3_UNUSED);
6901 chunk_size += LEVEL_CHUNK_CNT3_UNUSED;
6903 for (i = 0; i < envelope_len; i++)
6904 chunk_size += putFile8Bit(file, level->envelope_text[envelope_nr][i]);
6910 #if ENABLE_HISTORIC_CHUNKS
6911 static void SaveLevel_CUS1(FILE *file, struct LevelInfo *level,
6912 int num_changed_custom_elements)
6916 putFile16BitBE(file, num_changed_custom_elements);
6918 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6920 int element = EL_CUSTOM_START + i;
6922 struct ElementInfo *ei = &element_info[element];
6924 if (ei->properties[EP_BITFIELD_BASE_NR] != EP_BITMASK_DEFAULT)
6926 if (check < num_changed_custom_elements)
6928 putFile16BitBE(file, element);
6929 putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
6936 if (check != num_changed_custom_elements) // should not happen
6937 Error(ERR_WARN, "inconsistent number of custom element properties");
6941 #if ENABLE_HISTORIC_CHUNKS
6942 static void SaveLevel_CUS2(FILE *file, struct LevelInfo *level,
6943 int num_changed_custom_elements)
6947 putFile16BitBE(file, num_changed_custom_elements);
6949 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6951 int element = EL_CUSTOM_START + i;
6953 if (element_info[element].change->target_element != EL_EMPTY_SPACE)
6955 if (check < num_changed_custom_elements)
6957 putFile16BitBE(file, element);
6958 putFile16BitBE(file, element_info[element].change->target_element);
6965 if (check != num_changed_custom_elements) // should not happen
6966 Error(ERR_WARN, "inconsistent number of custom target elements");
6970 #if ENABLE_HISTORIC_CHUNKS
6971 static void SaveLevel_CUS3(FILE *file, struct LevelInfo *level,
6972 int num_changed_custom_elements)
6974 int i, j, x, y, check = 0;
6976 putFile16BitBE(file, num_changed_custom_elements);
6978 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6980 int element = EL_CUSTOM_START + i;
6981 struct ElementInfo *ei = &element_info[element];
6983 if (ei->modified_settings)
6985 if (check < num_changed_custom_elements)
6987 putFile16BitBE(file, element);
6989 for (j = 0; j < MAX_ELEMENT_NAME_LEN; j++)
6990 putFile8Bit(file, ei->description[j]);
6992 putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
6994 // some free bytes for future properties and padding
6995 WriteUnusedBytesToFile(file, 7);
6997 putFile8Bit(file, ei->use_gfx_element);
6998 putFile16BitBE(file, ei->gfx_element_initial);
7000 putFile8Bit(file, ei->collect_score_initial);
7001 putFile8Bit(file, ei->collect_count_initial);
7003 putFile16BitBE(file, ei->push_delay_fixed);
7004 putFile16BitBE(file, ei->push_delay_random);
7005 putFile16BitBE(file, ei->move_delay_fixed);
7006 putFile16BitBE(file, ei->move_delay_random);
7008 putFile16BitBE(file, ei->move_pattern);
7009 putFile8Bit(file, ei->move_direction_initial);
7010 putFile8Bit(file, ei->move_stepsize);
7012 for (y = 0; y < 3; y++)
7013 for (x = 0; x < 3; x++)
7014 putFile16BitBE(file, ei->content.e[x][y]);
7016 putFile32BitBE(file, ei->change->events);
7018 putFile16BitBE(file, ei->change->target_element);
7020 putFile16BitBE(file, ei->change->delay_fixed);
7021 putFile16BitBE(file, ei->change->delay_random);
7022 putFile16BitBE(file, ei->change->delay_frames);
7024 putFile16BitBE(file, ei->change->initial_trigger_element);
7026 putFile8Bit(file, ei->change->explode);
7027 putFile8Bit(file, ei->change->use_target_content);
7028 putFile8Bit(file, ei->change->only_if_complete);
7029 putFile8Bit(file, ei->change->use_random_replace);
7031 putFile8Bit(file, ei->change->random_percentage);
7032 putFile8Bit(file, ei->change->replace_when);
7034 for (y = 0; y < 3; y++)
7035 for (x = 0; x < 3; x++)
7036 putFile16BitBE(file, ei->change->content.e[x][y]);
7038 putFile8Bit(file, ei->slippery_type);
7040 // some free bytes for future properties and padding
7041 WriteUnusedBytesToFile(file, LEVEL_CPART_CUS3_UNUSED);
7048 if (check != num_changed_custom_elements) // should not happen
7049 Error(ERR_WARN, "inconsistent number of custom element properties");
7053 #if ENABLE_HISTORIC_CHUNKS
7054 static void SaveLevel_CUS4(FILE *file, struct LevelInfo *level, int element)
7056 struct ElementInfo *ei = &element_info[element];
7059 // ---------- custom element base property values (96 bytes) ----------------
7061 putFile16BitBE(file, element);
7063 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
7064 putFile8Bit(file, ei->description[i]);
7066 putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
7068 WriteUnusedBytesToFile(file, 4); // reserved for more base properties
7070 putFile8Bit(file, ei->num_change_pages);
7072 putFile16BitBE(file, ei->ce_value_fixed_initial);
7073 putFile16BitBE(file, ei->ce_value_random_initial);
7074 putFile8Bit(file, ei->use_last_ce_value);
7076 putFile8Bit(file, ei->use_gfx_element);
7077 putFile16BitBE(file, ei->gfx_element_initial);
7079 putFile8Bit(file, ei->collect_score_initial);
7080 putFile8Bit(file, ei->collect_count_initial);
7082 putFile8Bit(file, ei->drop_delay_fixed);
7083 putFile8Bit(file, ei->push_delay_fixed);
7084 putFile8Bit(file, ei->drop_delay_random);
7085 putFile8Bit(file, ei->push_delay_random);
7086 putFile16BitBE(file, ei->move_delay_fixed);
7087 putFile16BitBE(file, ei->move_delay_random);
7089 // bits 0 - 15 of "move_pattern" ...
7090 putFile16BitBE(file, ei->move_pattern & 0xffff);
7091 putFile8Bit(file, ei->move_direction_initial);
7092 putFile8Bit(file, ei->move_stepsize);
7094 putFile8Bit(file, ei->slippery_type);
7096 for (y = 0; y < 3; y++)
7097 for (x = 0; x < 3; x++)
7098 putFile16BitBE(file, ei->content.e[x][y]);
7100 putFile16BitBE(file, ei->move_enter_element);
7101 putFile16BitBE(file, ei->move_leave_element);
7102 putFile8Bit(file, ei->move_leave_type);
7104 // ... bits 16 - 31 of "move_pattern" (not nice, but downward compatible)
7105 putFile16BitBE(file, (ei->move_pattern >> 16) & 0xffff);
7107 putFile8Bit(file, ei->access_direction);
7109 putFile8Bit(file, ei->explosion_delay);
7110 putFile8Bit(file, ei->ignition_delay);
7111 putFile8Bit(file, ei->explosion_type);
7113 // some free bytes for future custom property values and padding
7114 WriteUnusedBytesToFile(file, 1);
7116 // ---------- change page property values (48 bytes) ------------------------
7118 for (i = 0; i < ei->num_change_pages; i++)
7120 struct ElementChangeInfo *change = &ei->change_page[i];
7121 unsigned int event_bits;
7123 // bits 0 - 31 of "has_event[]" ...
7125 for (j = 0; j < MIN(NUM_CHANGE_EVENTS, 32); j++)
7126 if (change->has_event[j])
7127 event_bits |= (1 << j);
7128 putFile32BitBE(file, event_bits);
7130 putFile16BitBE(file, change->target_element);
7132 putFile16BitBE(file, change->delay_fixed);
7133 putFile16BitBE(file, change->delay_random);
7134 putFile16BitBE(file, change->delay_frames);
7136 putFile16BitBE(file, change->initial_trigger_element);
7138 putFile8Bit(file, change->explode);
7139 putFile8Bit(file, change->use_target_content);
7140 putFile8Bit(file, change->only_if_complete);
7141 putFile8Bit(file, change->use_random_replace);
7143 putFile8Bit(file, change->random_percentage);
7144 putFile8Bit(file, change->replace_when);
7146 for (y = 0; y < 3; y++)
7147 for (x = 0; x < 3; x++)
7148 putFile16BitBE(file, change->target_content.e[x][y]);
7150 putFile8Bit(file, change->can_change);
7152 putFile8Bit(file, change->trigger_side);
7154 putFile8Bit(file, change->trigger_player);
7155 putFile8Bit(file, (change->trigger_page == CH_PAGE_ANY ? CH_PAGE_ANY_FILE :
7156 log_2(change->trigger_page)));
7158 putFile8Bit(file, change->has_action);
7159 putFile8Bit(file, change->action_type);
7160 putFile8Bit(file, change->action_mode);
7161 putFile16BitBE(file, change->action_arg);
7163 // ... bits 32 - 39 of "has_event[]" (not nice, but downward compatible)
7165 for (j = 32; j < NUM_CHANGE_EVENTS; j++)
7166 if (change->has_event[j])
7167 event_bits |= (1 << (j - 32));
7168 putFile8Bit(file, event_bits);
7173 #if ENABLE_HISTORIC_CHUNKS
7174 static void SaveLevel_GRP1(FILE *file, struct LevelInfo *level, int element)
7176 struct ElementInfo *ei = &element_info[element];
7177 struct ElementGroupInfo *group = ei->group;
7180 putFile16BitBE(file, element);
7182 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
7183 putFile8Bit(file, ei->description[i]);
7185 putFile8Bit(file, group->num_elements);
7187 putFile8Bit(file, ei->use_gfx_element);
7188 putFile16BitBE(file, ei->gfx_element_initial);
7190 putFile8Bit(file, group->choice_mode);
7192 // some free bytes for future values and padding
7193 WriteUnusedBytesToFile(file, 3);
7195 for (i = 0; i < MAX_ELEMENTS_IN_GROUP; i++)
7196 putFile16BitBE(file, group->element[i]);
7200 static int SaveLevel_MicroChunk(FILE *file, struct LevelFileConfigInfo *entry,
7201 boolean write_element)
7203 int save_type = entry->save_type;
7204 int data_type = entry->data_type;
7205 int conf_type = entry->conf_type;
7206 int byte_mask = conf_type & CONF_MASK_BYTES;
7207 int element = entry->element;
7208 int default_value = entry->default_value;
7210 boolean modified = FALSE;
7212 if (byte_mask != CONF_MASK_MULTI_BYTES)
7214 void *value_ptr = entry->value;
7215 int value = (data_type == TYPE_BOOLEAN ? *(boolean *)value_ptr :
7218 // check if any settings have been modified before saving them
7219 if (value != default_value)
7222 // do not save if explicitly told or if unmodified default settings
7223 if ((save_type == SAVE_CONF_NEVER) ||
7224 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
7228 num_bytes += putFile16BitBE(file, element);
7230 num_bytes += putFile8Bit(file, conf_type);
7231 num_bytes += (byte_mask == CONF_MASK_1_BYTE ? putFile8Bit (file, value) :
7232 byte_mask == CONF_MASK_2_BYTE ? putFile16BitBE(file, value) :
7233 byte_mask == CONF_MASK_4_BYTE ? putFile32BitBE(file, value) :
7236 else if (data_type == TYPE_STRING)
7238 char *default_string = entry->default_string;
7239 char *string = (char *)(entry->value);
7240 int string_length = strlen(string);
7243 // check if any settings have been modified before saving them
7244 if (!strEqual(string, default_string))
7247 // do not save if explicitly told or if unmodified default settings
7248 if ((save_type == SAVE_CONF_NEVER) ||
7249 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
7253 num_bytes += putFile16BitBE(file, element);
7255 num_bytes += putFile8Bit(file, conf_type);
7256 num_bytes += putFile16BitBE(file, string_length);
7258 for (i = 0; i < string_length; i++)
7259 num_bytes += putFile8Bit(file, string[i]);
7261 else if (data_type == TYPE_ELEMENT_LIST)
7263 int *element_array = (int *)(entry->value);
7264 int num_elements = *(int *)(entry->num_entities);
7267 // check if any settings have been modified before saving them
7268 for (i = 0; i < num_elements; i++)
7269 if (element_array[i] != default_value)
7272 // do not save if explicitly told or if unmodified default settings
7273 if ((save_type == SAVE_CONF_NEVER) ||
7274 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
7278 num_bytes += putFile16BitBE(file, element);
7280 num_bytes += putFile8Bit(file, conf_type);
7281 num_bytes += putFile16BitBE(file, num_elements * CONF_ELEMENT_NUM_BYTES);
7283 for (i = 0; i < num_elements; i++)
7284 num_bytes += putFile16BitBE(file, element_array[i]);
7286 else if (data_type == TYPE_CONTENT_LIST)
7288 struct Content *content = (struct Content *)(entry->value);
7289 int num_contents = *(int *)(entry->num_entities);
7292 // check if any settings have been modified before saving them
7293 for (i = 0; i < num_contents; i++)
7294 for (y = 0; y < 3; y++)
7295 for (x = 0; x < 3; x++)
7296 if (content[i].e[x][y] != default_value)
7299 // do not save if explicitly told or if unmodified default settings
7300 if ((save_type == SAVE_CONF_NEVER) ||
7301 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
7305 num_bytes += putFile16BitBE(file, element);
7307 num_bytes += putFile8Bit(file, conf_type);
7308 num_bytes += putFile16BitBE(file, num_contents * CONF_CONTENT_NUM_BYTES);
7310 for (i = 0; i < num_contents; i++)
7311 for (y = 0; y < 3; y++)
7312 for (x = 0; x < 3; x++)
7313 num_bytes += putFile16BitBE(file, content[i].e[x][y]);
7319 static int SaveLevel_INFO(FILE *file, struct LevelInfo *level)
7324 li = *level; // copy level data into temporary buffer
7326 for (i = 0; chunk_config_INFO[i].data_type != -1; i++)
7327 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_INFO[i], FALSE);
7332 static int SaveLevel_ELEM(FILE *file, struct LevelInfo *level)
7337 li = *level; // copy level data into temporary buffer
7339 for (i = 0; chunk_config_ELEM[i].data_type != -1; i++)
7340 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_ELEM[i], TRUE);
7345 static int SaveLevel_NOTE(FILE *file, struct LevelInfo *level, int element)
7347 int envelope_nr = element - EL_ENVELOPE_1;
7351 chunk_size += putFile16BitBE(file, element);
7353 // copy envelope data into temporary buffer
7354 xx_envelope = level->envelope[envelope_nr];
7356 for (i = 0; chunk_config_NOTE[i].data_type != -1; i++)
7357 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_NOTE[i], FALSE);
7362 static int SaveLevel_CUSX(FILE *file, struct LevelInfo *level, int element)
7364 struct ElementInfo *ei = &element_info[element];
7368 chunk_size += putFile16BitBE(file, element);
7370 xx_ei = *ei; // copy element data into temporary buffer
7372 // set default description string for this specific element
7373 strcpy(xx_default_description, getDefaultElementDescription(ei));
7375 for (i = 0; chunk_config_CUSX_base[i].data_type != -1; i++)
7376 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_CUSX_base[i], FALSE);
7378 for (i = 0; i < ei->num_change_pages; i++)
7380 struct ElementChangeInfo *change = &ei->change_page[i];
7382 xx_current_change_page = i;
7384 xx_change = *change; // copy change data into temporary buffer
7387 setEventBitsFromEventFlags(change);
7389 for (j = 0; chunk_config_CUSX_change[j].data_type != -1; j++)
7390 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_CUSX_change[j],
7397 static int SaveLevel_GRPX(FILE *file, struct LevelInfo *level, int element)
7399 struct ElementInfo *ei = &element_info[element];
7400 struct ElementGroupInfo *group = ei->group;
7404 chunk_size += putFile16BitBE(file, element);
7406 xx_ei = *ei; // copy element data into temporary buffer
7407 xx_group = *group; // copy group data into temporary buffer
7409 // set default description string for this specific element
7410 strcpy(xx_default_description, getDefaultElementDescription(ei));
7412 for (i = 0; chunk_config_GRPX[i].data_type != -1; i++)
7413 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_GRPX[i], FALSE);
7418 static void SaveLevelFromFilename(struct LevelInfo *level, char *filename,
7419 boolean save_as_template)
7425 if (!(file = fopen(filename, MODE_WRITE)))
7427 Error(ERR_WARN, "cannot save level file '%s'", filename);
7431 level->file_version = FILE_VERSION_ACTUAL;
7432 level->game_version = GAME_VERSION_ACTUAL;
7434 level->creation_date = getCurrentDate();
7436 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
7437 putFileChunkBE(file, "CAVE", CHUNK_SIZE_NONE);
7439 chunk_size = SaveLevel_VERS(NULL, level);
7440 putFileChunkBE(file, "VERS", chunk_size);
7441 SaveLevel_VERS(file, level);
7443 chunk_size = SaveLevel_DATE(NULL, level);
7444 putFileChunkBE(file, "DATE", chunk_size);
7445 SaveLevel_DATE(file, level);
7447 chunk_size = SaveLevel_NAME(NULL, level);
7448 putFileChunkBE(file, "NAME", chunk_size);
7449 SaveLevel_NAME(file, level);
7451 chunk_size = SaveLevel_AUTH(NULL, level);
7452 putFileChunkBE(file, "AUTH", chunk_size);
7453 SaveLevel_AUTH(file, level);
7455 chunk_size = SaveLevel_INFO(NULL, level);
7456 putFileChunkBE(file, "INFO", chunk_size);
7457 SaveLevel_INFO(file, level);
7459 chunk_size = SaveLevel_BODY(NULL, level);
7460 putFileChunkBE(file, "BODY", chunk_size);
7461 SaveLevel_BODY(file, level);
7463 chunk_size = SaveLevel_ELEM(NULL, level);
7464 if (chunk_size > LEVEL_CHUNK_ELEM_UNCHANGED) // save if changed
7466 putFileChunkBE(file, "ELEM", chunk_size);
7467 SaveLevel_ELEM(file, level);
7470 for (i = 0; i < NUM_ENVELOPES; i++)
7472 int element = EL_ENVELOPE_1 + i;
7474 chunk_size = SaveLevel_NOTE(NULL, level, element);
7475 if (chunk_size > LEVEL_CHUNK_NOTE_UNCHANGED) // save if changed
7477 putFileChunkBE(file, "NOTE", chunk_size);
7478 SaveLevel_NOTE(file, level, element);
7482 // if not using template level, check for non-default custom/group elements
7483 if (!level->use_custom_template || save_as_template)
7485 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7487 int element = EL_CUSTOM_START + i;
7489 chunk_size = SaveLevel_CUSX(NULL, level, element);
7490 if (chunk_size > LEVEL_CHUNK_CUSX_UNCHANGED) // save if changed
7492 putFileChunkBE(file, "CUSX", chunk_size);
7493 SaveLevel_CUSX(file, level, element);
7497 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
7499 int element = EL_GROUP_START + i;
7501 chunk_size = SaveLevel_GRPX(NULL, level, element);
7502 if (chunk_size > LEVEL_CHUNK_GRPX_UNCHANGED) // save if changed
7504 putFileChunkBE(file, "GRPX", chunk_size);
7505 SaveLevel_GRPX(file, level, element);
7512 SetFilePermissions(filename, PERMS_PRIVATE);
7515 void SaveLevel(int nr)
7517 char *filename = getDefaultLevelFilename(nr);
7519 SaveLevelFromFilename(&level, filename, FALSE);
7522 void SaveLevelTemplate(void)
7524 char *filename = getLocalLevelTemplateFilename();
7526 SaveLevelFromFilename(&level, filename, TRUE);
7529 boolean SaveLevelChecked(int nr)
7531 char *filename = getDefaultLevelFilename(nr);
7532 boolean new_level = !fileExists(filename);
7533 boolean level_saved = FALSE;
7535 if (new_level || Request("Save this level and kill the old?", REQ_ASK))
7540 Request("Level saved!", REQ_CONFIRM);
7548 void DumpLevel(struct LevelInfo *level)
7550 if (level->no_level_file || level->no_valid_file)
7552 Error(ERR_WARN, "cannot dump -- no valid level file found");
7558 Print("Level xxx (file version %08d, game version %08d)\n",
7559 level->file_version, level->game_version);
7562 Print("Level author: '%s'\n", level->author);
7563 Print("Level title: '%s'\n", level->name);
7565 Print("Playfield size: %d x %d\n", level->fieldx, level->fieldy);
7567 Print("Level time: %d seconds\n", level->time);
7568 Print("Gems needed: %d\n", level->gems_needed);
7570 Print("Time for magic wall: %d seconds\n", level->time_magic_wall);
7571 Print("Time for wheel: %d seconds\n", level->time_wheel);
7572 Print("Time for light: %d seconds\n", level->time_light);
7573 Print("Time for timegate: %d seconds\n", level->time_timegate);
7575 Print("Amoeba speed: %d\n", level->amoeba_speed);
7578 Print("EM style slippery gems: %s\n", (level->em_slippery_gems ? "yes" : "no"));
7579 Print("Player blocks last field: %s\n", (level->block_last_field ? "yes" : "no"));
7580 Print("SP player blocks last field: %s\n", (level->sp_block_last_field ? "yes" : "no"));
7581 Print("use spring bug: %s\n", (level->use_spring_bug ? "yes" : "no"));
7582 Print("use step counter: %s\n", (level->use_step_counter ? "yes" : "no"));
7588 // ============================================================================
7589 // tape file functions
7590 // ============================================================================
7592 static void setTapeInfoToDefaults(void)
7596 // always start with reliable default values (empty tape)
7599 // default values (also for pre-1.2 tapes) with only the first player
7600 tape.player_participates[0] = TRUE;
7601 for (i = 1; i < MAX_PLAYERS; i++)
7602 tape.player_participates[i] = FALSE;
7604 // at least one (default: the first) player participates in every tape
7605 tape.num_participating_players = 1;
7607 tape.level_nr = level_nr;
7609 tape.changed = FALSE;
7611 tape.recording = FALSE;
7612 tape.playing = FALSE;
7613 tape.pausing = FALSE;
7615 tape.no_valid_file = FALSE;
7618 static int LoadTape_VERS(File *file, int chunk_size, struct TapeInfo *tape)
7620 tape->file_version = getFileVersion(file);
7621 tape->game_version = getFileVersion(file);
7626 static int LoadTape_HEAD(File *file, int chunk_size, struct TapeInfo *tape)
7630 tape->random_seed = getFile32BitBE(file);
7631 tape->date = getFile32BitBE(file);
7632 tape->length = getFile32BitBE(file);
7634 // read header fields that are new since version 1.2
7635 if (tape->file_version >= FILE_VERSION_1_2)
7637 byte store_participating_players = getFile8Bit(file);
7640 // since version 1.2, tapes store which players participate in the tape
7641 tape->num_participating_players = 0;
7642 for (i = 0; i < MAX_PLAYERS; i++)
7644 tape->player_participates[i] = FALSE;
7646 if (store_participating_players & (1 << i))
7648 tape->player_participates[i] = TRUE;
7649 tape->num_participating_players++;
7653 tape->use_mouse = (getFile8Bit(file) == 1 ? TRUE : FALSE);
7655 ReadUnusedBytesFromFile(file, TAPE_CHUNK_HEAD_UNUSED);
7657 engine_version = getFileVersion(file);
7658 if (engine_version > 0)
7659 tape->engine_version = engine_version;
7661 tape->engine_version = tape->game_version;
7667 static int LoadTape_INFO(File *file, int chunk_size, struct TapeInfo *tape)
7669 int level_identifier_size;
7672 level_identifier_size = getFile16BitBE(file);
7674 tape->level_identifier =
7675 checked_realloc(tape->level_identifier, level_identifier_size);
7677 for (i = 0; i < level_identifier_size; i++)
7678 tape->level_identifier[i] = getFile8Bit(file);
7680 tape->level_nr = getFile16BitBE(file);
7682 chunk_size = 2 + level_identifier_size + 2;
7687 static int LoadTape_BODY(File *file, int chunk_size, struct TapeInfo *tape)
7691 (tape->use_mouse ? 3 : tape->num_participating_players) + 1;
7692 int chunk_size_expected = tape_pos_size * tape->length;
7694 if (chunk_size_expected != chunk_size)
7696 ReadUnusedBytesFromFile(file, chunk_size);
7697 return chunk_size_expected;
7700 for (i = 0; i < tape->length; i++)
7702 if (i >= MAX_TAPE_LEN)
7704 Error(ERR_WARN, "tape truncated -- size exceeds maximum tape size %d",
7707 // tape too large; read and ignore remaining tape data from this chunk
7708 for (;i < tape->length; i++)
7709 ReadUnusedBytesFromFile(file, tape->num_participating_players + 1);
7714 if (tape->use_mouse)
7716 tape->pos[i].action[TAPE_ACTION_LX] = getFile8Bit(file);
7717 tape->pos[i].action[TAPE_ACTION_LY] = getFile8Bit(file);
7718 tape->pos[i].action[TAPE_ACTION_BUTTON] = getFile8Bit(file);
7720 tape->pos[i].action[TAPE_ACTION_UNUSED] = 0;
7724 for (j = 0; j < MAX_PLAYERS; j++)
7726 tape->pos[i].action[j] = MV_NONE;
7728 if (tape->player_participates[j])
7729 tape->pos[i].action[j] = getFile8Bit(file);
7733 tape->pos[i].delay = getFile8Bit(file);
7735 if (tape->file_version == FILE_VERSION_1_0)
7737 // eliminate possible diagonal moves in old tapes
7738 // this is only for backward compatibility
7740 byte joy_dir[4] = { JOY_LEFT, JOY_RIGHT, JOY_UP, JOY_DOWN };
7741 byte action = tape->pos[i].action[0];
7742 int k, num_moves = 0;
7744 for (k = 0; k<4; k++)
7746 if (action & joy_dir[k])
7748 tape->pos[i + num_moves].action[0] = joy_dir[k];
7750 tape->pos[i + num_moves].delay = 0;
7759 tape->length += num_moves;
7762 else if (tape->file_version < FILE_VERSION_2_0)
7764 // convert pre-2.0 tapes to new tape format
7766 if (tape->pos[i].delay > 1)
7769 tape->pos[i + 1] = tape->pos[i];
7770 tape->pos[i + 1].delay = 1;
7773 for (j = 0; j < MAX_PLAYERS; j++)
7774 tape->pos[i].action[j] = MV_NONE;
7775 tape->pos[i].delay--;
7782 if (checkEndOfFile(file))
7786 if (i != tape->length)
7787 chunk_size = tape_pos_size * i;
7792 static void LoadTape_SokobanSolution(char *filename)
7795 int move_delay = TILESIZE / level.initial_player_stepsize[0];
7797 if (!(file = openFile(filename, MODE_READ)))
7799 tape.no_valid_file = TRUE;
7804 while (!checkEndOfFile(file))
7806 unsigned char c = getByteFromFile(file);
7808 if (checkEndOfFile(file))
7815 tape.pos[tape.length].action[0] = MV_UP;
7816 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
7822 tape.pos[tape.length].action[0] = MV_DOWN;
7823 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
7829 tape.pos[tape.length].action[0] = MV_LEFT;
7830 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
7836 tape.pos[tape.length].action[0] = MV_RIGHT;
7837 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
7845 // ignore white-space characters
7849 tape.no_valid_file = TRUE;
7851 Error(ERR_WARN, "unsupported Sokoban solution file '%s' ['%d']", filename, c);
7859 if (tape.no_valid_file)
7862 tape.length_frames = GetTapeLengthFrames();
7863 tape.length_seconds = GetTapeLengthSeconds();
7866 void LoadTapeFromFilename(char *filename)
7868 char cookie[MAX_LINE_LEN];
7869 char chunk_name[CHUNK_ID_LEN + 1];
7873 // always start with reliable default values
7874 setTapeInfoToDefaults();
7876 if (strSuffix(filename, ".sln"))
7878 LoadTape_SokobanSolution(filename);
7883 if (!(file = openFile(filename, MODE_READ)))
7885 tape.no_valid_file = TRUE;
7890 getFileChunkBE(file, chunk_name, NULL);
7891 if (strEqual(chunk_name, "RND1"))
7893 getFile32BitBE(file); // not used
7895 getFileChunkBE(file, chunk_name, NULL);
7896 if (!strEqual(chunk_name, "TAPE"))
7898 tape.no_valid_file = TRUE;
7900 Error(ERR_WARN, "unknown format of tape file '%s'", filename);
7907 else // check for pre-2.0 file format with cookie string
7909 strcpy(cookie, chunk_name);
7910 if (getStringFromFile(file, &cookie[4], MAX_LINE_LEN - 4) == NULL)
7912 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
7913 cookie[strlen(cookie) - 1] = '\0';
7915 if (!checkCookieString(cookie, TAPE_COOKIE_TMPL))
7917 tape.no_valid_file = TRUE;
7919 Error(ERR_WARN, "unknown format of tape file '%s'", filename);
7926 if ((tape.file_version = getFileVersionFromCookieString(cookie)) == -1)
7928 tape.no_valid_file = TRUE;
7930 Error(ERR_WARN, "unsupported version of tape file '%s'", filename);
7937 // pre-2.0 tape files have no game version, so use file version here
7938 tape.game_version = tape.file_version;
7941 if (tape.file_version < FILE_VERSION_1_2)
7943 // tape files from versions before 1.2.0 without chunk structure
7944 LoadTape_HEAD(file, TAPE_CHUNK_HEAD_SIZE, &tape);
7945 LoadTape_BODY(file, 2 * tape.length, &tape);
7953 int (*loader)(File *, int, struct TapeInfo *);
7957 { "VERS", TAPE_CHUNK_VERS_SIZE, LoadTape_VERS },
7958 { "HEAD", TAPE_CHUNK_HEAD_SIZE, LoadTape_HEAD },
7959 { "INFO", -1, LoadTape_INFO },
7960 { "BODY", -1, LoadTape_BODY },
7964 while (getFileChunkBE(file, chunk_name, &chunk_size))
7968 while (chunk_info[i].name != NULL &&
7969 !strEqual(chunk_name, chunk_info[i].name))
7972 if (chunk_info[i].name == NULL)
7974 Error(ERR_WARN, "unknown chunk '%s' in tape file '%s'",
7975 chunk_name, filename);
7976 ReadUnusedBytesFromFile(file, chunk_size);
7978 else if (chunk_info[i].size != -1 &&
7979 chunk_info[i].size != chunk_size)
7981 Error(ERR_WARN, "wrong size (%d) of chunk '%s' in tape file '%s'",
7982 chunk_size, chunk_name, filename);
7983 ReadUnusedBytesFromFile(file, chunk_size);
7987 // call function to load this tape chunk
7988 int chunk_size_expected =
7989 (chunk_info[i].loader)(file, chunk_size, &tape);
7991 // the size of some chunks cannot be checked before reading other
7992 // chunks first (like "HEAD" and "BODY") that contain some header
7993 // information, so check them here
7994 if (chunk_size_expected != chunk_size)
7996 Error(ERR_WARN, "wrong size (%d) of chunk '%s' in tape file '%s'",
7997 chunk_size, chunk_name, filename);
8005 tape.length_frames = GetTapeLengthFrames();
8006 tape.length_seconds = GetTapeLengthSeconds();
8009 printf("::: tape file version: %d\n", tape.file_version);
8010 printf("::: tape game version: %d\n", tape.game_version);
8011 printf("::: tape engine version: %d\n", tape.engine_version);
8015 void LoadTape(int nr)
8017 char *filename = getTapeFilename(nr);
8019 LoadTapeFromFilename(filename);
8022 void LoadSolutionTape(int nr)
8024 char *filename = getSolutionTapeFilename(nr);
8026 LoadTapeFromFilename(filename);
8028 if (TAPE_IS_EMPTY(tape) &&
8029 level.game_engine_type == GAME_ENGINE_TYPE_SP &&
8030 level.native_sp_level->demo.is_available)
8031 CopyNativeTape_SP_to_RND(&level);
8034 static void SaveTape_VERS(FILE *file, struct TapeInfo *tape)
8036 putFileVersion(file, tape->file_version);
8037 putFileVersion(file, tape->game_version);
8040 static void SaveTape_HEAD(FILE *file, struct TapeInfo *tape)
8043 byte store_participating_players = 0;
8045 // set bits for participating players for compact storage
8046 for (i = 0; i < MAX_PLAYERS; i++)
8047 if (tape->player_participates[i])
8048 store_participating_players |= (1 << i);
8050 putFile32BitBE(file, tape->random_seed);
8051 putFile32BitBE(file, tape->date);
8052 putFile32BitBE(file, tape->length);
8054 putFile8Bit(file, store_participating_players);
8056 putFile8Bit(file, (tape->use_mouse ? 1 : 0));
8058 // unused bytes not at the end here for 4-byte alignment of engine_version
8059 WriteUnusedBytesToFile(file, TAPE_CHUNK_HEAD_UNUSED);
8061 putFileVersion(file, tape->engine_version);
8064 static void SaveTape_INFO(FILE *file, struct TapeInfo *tape)
8066 int level_identifier_size = strlen(tape->level_identifier) + 1;
8069 putFile16BitBE(file, level_identifier_size);
8071 for (i = 0; i < level_identifier_size; i++)
8072 putFile8Bit(file, tape->level_identifier[i]);
8074 putFile16BitBE(file, tape->level_nr);
8077 static void SaveTape_BODY(FILE *file, struct TapeInfo *tape)
8081 for (i = 0; i < tape->length; i++)
8083 if (tape->use_mouse)
8085 putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_LX]);
8086 putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_LY]);
8087 putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_BUTTON]);
8091 for (j = 0; j < MAX_PLAYERS; j++)
8092 if (tape->player_participates[j])
8093 putFile8Bit(file, tape->pos[i].action[j]);
8096 putFile8Bit(file, tape->pos[i].delay);
8100 void SaveTape(int nr)
8102 char *filename = getTapeFilename(nr);
8104 int num_participating_players = 0;
8106 int info_chunk_size;
8107 int body_chunk_size;
8110 InitTapeDirectory(leveldir_current->subdir);
8112 if (!(file = fopen(filename, MODE_WRITE)))
8114 Error(ERR_WARN, "cannot save level recording file '%s'", filename);
8118 tape.file_version = FILE_VERSION_ACTUAL;
8119 tape.game_version = GAME_VERSION_ACTUAL;
8121 // count number of participating players
8122 for (i = 0; i < MAX_PLAYERS; i++)
8123 if (tape.player_participates[i])
8124 num_participating_players++;
8126 tape_pos_size = (tape.use_mouse ? 3 : num_participating_players) + 1;
8128 info_chunk_size = 2 + (strlen(tape.level_identifier) + 1) + 2;
8129 body_chunk_size = tape_pos_size * tape.length;
8131 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
8132 putFileChunkBE(file, "TAPE", CHUNK_SIZE_NONE);
8134 putFileChunkBE(file, "VERS", TAPE_CHUNK_VERS_SIZE);
8135 SaveTape_VERS(file, &tape);
8137 putFileChunkBE(file, "HEAD", TAPE_CHUNK_HEAD_SIZE);
8138 SaveTape_HEAD(file, &tape);
8140 putFileChunkBE(file, "INFO", info_chunk_size);
8141 SaveTape_INFO(file, &tape);
8143 putFileChunkBE(file, "BODY", body_chunk_size);
8144 SaveTape_BODY(file, &tape);
8148 SetFilePermissions(filename, PERMS_PRIVATE);
8150 tape.changed = FALSE;
8153 static boolean SaveTapeCheckedExt(int nr, char *msg_replace, char *msg_saved,
8154 unsigned int req_state_added)
8156 char *filename = getTapeFilename(nr);
8157 boolean new_tape = !fileExists(filename);
8158 boolean tape_saved = FALSE;
8160 if (new_tape || Request(msg_replace, REQ_ASK | req_state_added))
8165 Request(msg_saved, REQ_CONFIRM | req_state_added);
8173 boolean SaveTapeChecked(int nr)
8175 return SaveTapeCheckedExt(nr, "Replace old tape?", "Tape saved!", 0);
8178 boolean SaveTapeChecked_LevelSolved(int nr)
8180 return SaveTapeCheckedExt(nr, "Level solved! Replace old tape?",
8181 "Level solved! Tape saved!", REQ_STAY_OPEN);
8184 void DumpTape(struct TapeInfo *tape)
8186 int tape_frame_counter;
8189 if (tape->no_valid_file)
8191 Error(ERR_WARN, "cannot dump -- no valid tape file found");
8197 Print("Tape of Level %03d (file version %08d, game version %08d)\n",
8198 tape->level_nr, tape->file_version, tape->game_version);
8199 Print(" (effective engine version %08d)\n",
8200 tape->engine_version);
8201 Print("Level series identifier: '%s'\n", tape->level_identifier);
8204 tape_frame_counter = 0;
8206 for (i = 0; i < tape->length; i++)
8208 if (i >= MAX_TAPE_LEN)
8213 for (j = 0; j < MAX_PLAYERS; j++)
8215 if (tape->player_participates[j])
8217 int action = tape->pos[i].action[j];
8219 Print("%d:%02x ", j, action);
8220 Print("[%c%c%c%c|%c%c] - ",
8221 (action & JOY_LEFT ? '<' : ' '),
8222 (action & JOY_RIGHT ? '>' : ' '),
8223 (action & JOY_UP ? '^' : ' '),
8224 (action & JOY_DOWN ? 'v' : ' '),
8225 (action & JOY_BUTTON_1 ? '1' : ' '),
8226 (action & JOY_BUTTON_2 ? '2' : ' '));
8230 Print("(%03d) ", tape->pos[i].delay);
8231 Print("[%05d]\n", tape_frame_counter);
8233 tape_frame_counter += tape->pos[i].delay;
8240 // ============================================================================
8241 // score file functions
8242 // ============================================================================
8244 void LoadScore(int nr)
8247 char *filename = getScoreFilename(nr);
8248 char cookie[MAX_LINE_LEN];
8249 char line[MAX_LINE_LEN];
8253 // always start with reliable default values
8254 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
8256 strcpy(highscore[i].Name, EMPTY_PLAYER_NAME);
8257 highscore[i].Score = 0;
8260 if (!(file = fopen(filename, MODE_READ)))
8263 // check file identifier
8264 if (fgets(cookie, MAX_LINE_LEN, file) == NULL)
8266 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
8267 cookie[strlen(cookie) - 1] = '\0';
8269 if (!checkCookieString(cookie, SCORE_COOKIE))
8271 Error(ERR_WARN, "unknown format of score file '%s'", filename);
8276 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
8278 if (fscanf(file, "%d", &highscore[i].Score) == EOF)
8279 Error(ERR_WARN, "fscanf() failed; %s", strerror(errno));
8280 if (fgets(line, MAX_LINE_LEN, file) == NULL)
8283 if (strlen(line) > 0 && line[strlen(line) - 1] == '\n')
8284 line[strlen(line) - 1] = '\0';
8286 for (line_ptr = line; *line_ptr; line_ptr++)
8288 if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != '\0')
8290 strncpy(highscore[i].Name, line_ptr, MAX_PLAYER_NAME_LEN);
8291 highscore[i].Name[MAX_PLAYER_NAME_LEN] = '\0';
8300 void SaveScore(int nr)
8303 int permissions = (program.global_scores ? PERMS_PUBLIC : PERMS_PRIVATE);
8304 char *filename = getScoreFilename(nr);
8307 // used instead of "leveldir_current->subdir" (for network games)
8308 InitScoreDirectory(levelset.identifier);
8310 if (!(file = fopen(filename, MODE_WRITE)))
8312 Error(ERR_WARN, "cannot save score for level %d", nr);
8316 fprintf(file, "%s\n\n", SCORE_COOKIE);
8318 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
8319 fprintf(file, "%d %s\n", highscore[i].Score, highscore[i].Name);
8323 SetFilePermissions(filename, permissions);
8327 // ============================================================================
8328 // setup file functions
8329 // ============================================================================
8331 #define TOKEN_STR_PLAYER_PREFIX "player_"
8334 static struct TokenInfo global_setup_tokens[] =
8338 &setup.player_name, "player_name"
8342 &setup.sound, "sound"
8346 &setup.sound_loops, "repeating_sound_loops"
8350 &setup.sound_music, "background_music"
8354 &setup.sound_simple, "simple_sound_effects"
8358 &setup.toons, "toons"
8362 &setup.scroll_delay, "scroll_delay"
8366 &setup.scroll_delay_value, "scroll_delay_value"
8370 &setup.engine_snapshot_mode, "engine_snapshot_mode"
8374 &setup.engine_snapshot_memory, "engine_snapshot_memory"
8378 &setup.fade_screens, "fade_screens"
8382 &setup.autorecord, "automatic_tape_recording"
8386 &setup.show_titlescreen, "show_titlescreen"
8390 &setup.quick_doors, "quick_doors"
8394 &setup.team_mode, "team_mode"
8398 &setup.handicap, "handicap"
8402 &setup.skip_levels, "skip_levels"
8406 &setup.increment_levels, "increment_levels"
8410 &setup.auto_play_next_level, "auto_play_next_level"
8414 &setup.skip_scores_after_game, "skip_scores_after_game"
8418 &setup.time_limit, "time_limit"
8422 &setup.fullscreen, "fullscreen"
8426 &setup.window_scaling_percent, "window_scaling_percent"
8430 &setup.window_scaling_quality, "window_scaling_quality"
8434 &setup.screen_rendering_mode, "screen_rendering_mode"
8438 &setup.vsync_mode, "vsync_mode"
8442 &setup.ask_on_escape, "ask_on_escape"
8446 &setup.ask_on_escape_editor, "ask_on_escape_editor"
8450 &setup.ask_on_game_over, "ask_on_game_over"
8454 &setup.quick_switch, "quick_player_switch"
8458 &setup.input_on_focus, "input_on_focus"
8462 &setup.prefer_aga_graphics, "prefer_aga_graphics"
8466 &setup.game_speed_extended, "game_speed_extended"
8470 &setup.game_frame_delay, "game_frame_delay"
8474 &setup.sp_show_border_elements, "sp_show_border_elements"
8478 &setup.small_game_graphics, "small_game_graphics"
8482 &setup.show_snapshot_buttons, "show_snapshot_buttons"
8486 &setup.graphics_set, "graphics_set"
8490 &setup.sounds_set, "sounds_set"
8494 &setup.music_set, "music_set"
8498 &setup.override_level_graphics, "override_level_graphics"
8502 &setup.override_level_sounds, "override_level_sounds"
8506 &setup.override_level_music, "override_level_music"
8510 &setup.volume_simple, "volume_simple"
8514 &setup.volume_loops, "volume_loops"
8518 &setup.volume_music, "volume_music"
8522 &setup.network_mode, "network_mode"
8526 &setup.network_player_nr, "network_player"
8530 &setup.network_server_hostname, "network_server_hostname"
8534 &setup.touch.control_type, "touch.control_type"
8538 &setup.touch.move_distance, "touch.move_distance"
8542 &setup.touch.drop_distance, "touch.drop_distance"
8546 &setup.touch.transparency, "touch.transparency"
8550 &setup.touch.draw_outlined, "touch.draw_outlined"
8554 &setup.touch.draw_pressed, "touch.draw_pressed"
8558 &setup.touch.grid_xsize[0], "touch.virtual_buttons.0.xsize"
8562 &setup.touch.grid_ysize[0], "touch.virtual_buttons.0.ysize"
8566 &setup.touch.grid_xsize[1], "touch.virtual_buttons.1.xsize"
8570 &setup.touch.grid_ysize[1], "touch.virtual_buttons.1.ysize"
8574 static struct TokenInfo auto_setup_tokens[] =
8578 &setup.auto_setup.editor_zoom_tilesize, "editor.zoom_tilesize"
8582 static struct TokenInfo editor_setup_tokens[] =
8586 &setup.editor.el_classic, "editor.el_classic"
8590 &setup.editor.el_custom, "editor.el_custom"
8594 &setup.editor.el_user_defined, "editor.el_user_defined"
8598 &setup.editor.el_dynamic, "editor.el_dynamic"
8602 &setup.editor.el_headlines, "editor.el_headlines"
8606 &setup.editor.show_element_token, "editor.show_element_token"
8610 static struct TokenInfo editor_cascade_setup_tokens[] =
8614 &setup.editor_cascade.el_bd, "editor.cascade.el_bd"
8618 &setup.editor_cascade.el_em, "editor.cascade.el_em"
8622 &setup.editor_cascade.el_emc, "editor.cascade.el_emc"
8626 &setup.editor_cascade.el_rnd, "editor.cascade.el_rnd"
8630 &setup.editor_cascade.el_sb, "editor.cascade.el_sb"
8634 &setup.editor_cascade.el_sp, "editor.cascade.el_sp"
8638 &setup.editor_cascade.el_dc, "editor.cascade.el_dc"
8642 &setup.editor_cascade.el_dx, "editor.cascade.el_dx"
8646 &setup.editor_cascade.el_mm, "editor.cascade.el_mm"
8650 &setup.editor_cascade.el_df, "editor.cascade.el_df"
8654 &setup.editor_cascade.el_chars, "editor.cascade.el_chars"
8658 &setup.editor_cascade.el_steel_chars, "editor.cascade.el_steel_chars"
8662 &setup.editor_cascade.el_ce, "editor.cascade.el_ce"
8666 &setup.editor_cascade.el_ge, "editor.cascade.el_ge"
8670 &setup.editor_cascade.el_ref, "editor.cascade.el_ref"
8674 &setup.editor_cascade.el_user, "editor.cascade.el_user"
8678 &setup.editor_cascade.el_dynamic, "editor.cascade.el_dynamic"
8682 static struct TokenInfo shortcut_setup_tokens[] =
8686 &setup.shortcut.save_game, "shortcut.save_game"
8690 &setup.shortcut.load_game, "shortcut.load_game"
8694 &setup.shortcut.toggle_pause, "shortcut.toggle_pause"
8698 &setup.shortcut.focus_player[0], "shortcut.focus_player_1"
8702 &setup.shortcut.focus_player[1], "shortcut.focus_player_2"
8706 &setup.shortcut.focus_player[2], "shortcut.focus_player_3"
8710 &setup.shortcut.focus_player[3], "shortcut.focus_player_4"
8714 &setup.shortcut.focus_player_all, "shortcut.focus_player_all"
8718 &setup.shortcut.tape_eject, "shortcut.tape_eject"
8722 &setup.shortcut.tape_extra, "shortcut.tape_extra"
8726 &setup.shortcut.tape_stop, "shortcut.tape_stop"
8730 &setup.shortcut.tape_pause, "shortcut.tape_pause"
8734 &setup.shortcut.tape_record, "shortcut.tape_record"
8738 &setup.shortcut.tape_play, "shortcut.tape_play"
8742 &setup.shortcut.sound_simple, "shortcut.sound_simple"
8746 &setup.shortcut.sound_loops, "shortcut.sound_loops"
8750 &setup.shortcut.sound_music, "shortcut.sound_music"
8754 &setup.shortcut.snap_left, "shortcut.snap_left"
8758 &setup.shortcut.snap_right, "shortcut.snap_right"
8762 &setup.shortcut.snap_up, "shortcut.snap_up"
8766 &setup.shortcut.snap_down, "shortcut.snap_down"
8770 static struct SetupInputInfo setup_input;
8771 static struct TokenInfo player_setup_tokens[] =
8775 &setup_input.use_joystick, ".use_joystick"
8779 &setup_input.joy.device_name, ".joy.device_name"
8783 &setup_input.joy.xleft, ".joy.xleft"
8787 &setup_input.joy.xmiddle, ".joy.xmiddle"
8791 &setup_input.joy.xright, ".joy.xright"
8795 &setup_input.joy.yupper, ".joy.yupper"
8799 &setup_input.joy.ymiddle, ".joy.ymiddle"
8803 &setup_input.joy.ylower, ".joy.ylower"
8807 &setup_input.joy.snap, ".joy.snap_field"
8811 &setup_input.joy.drop, ".joy.place_bomb"
8815 &setup_input.key.left, ".key.move_left"
8819 &setup_input.key.right, ".key.move_right"
8823 &setup_input.key.up, ".key.move_up"
8827 &setup_input.key.down, ".key.move_down"
8831 &setup_input.key.snap, ".key.snap_field"
8835 &setup_input.key.drop, ".key.place_bomb"
8839 static struct TokenInfo system_setup_tokens[] =
8843 &setup.system.sdl_videodriver, "system.sdl_videodriver"
8847 &setup.system.sdl_audiodriver, "system.sdl_audiodriver"
8851 &setup.system.audio_fragment_size, "system.audio_fragment_size"
8855 static struct TokenInfo internal_setup_tokens[] =
8859 &setup.internal.program_title, "program_title"
8863 &setup.internal.program_version, "program_version"
8867 &setup.internal.program_author, "program_author"
8871 &setup.internal.program_email, "program_email"
8875 &setup.internal.program_website, "program_website"
8879 &setup.internal.program_copyright, "program_copyright"
8883 &setup.internal.program_company, "program_company"
8887 &setup.internal.program_icon_file, "program_icon_file"
8891 &setup.internal.default_graphics_set, "default_graphics_set"
8895 &setup.internal.default_sounds_set, "default_sounds_set"
8899 &setup.internal.default_music_set, "default_music_set"
8903 &setup.internal.fallback_graphics_file, "fallback_graphics_file"
8907 &setup.internal.fallback_sounds_file, "fallback_sounds_file"
8911 &setup.internal.fallback_music_file, "fallback_music_file"
8915 &setup.internal.default_level_series, "default_level_series"
8919 &setup.internal.default_window_width, "default_window_width"
8923 &setup.internal.default_window_height, "default_window_height"
8927 &setup.internal.choose_from_top_leveldir, "choose_from_top_leveldir"
8931 &setup.internal.show_scaling_in_title, "show_scaling_in_title"
8935 &setup.internal.create_user_levelset, "create_user_levelset"
8939 &setup.internal.menu_game, "menu_game"
8943 &setup.internal.menu_editor, "menu_editor"
8947 &setup.internal.menu_graphics, "menu_graphics"
8951 &setup.internal.menu_sound, "menu_sound"
8955 &setup.internal.menu_artwork, "menu_artwork"
8959 &setup.internal.menu_input, "menu_input"
8963 &setup.internal.menu_touch, "menu_touch"
8967 &setup.internal.menu_shortcuts, "menu_shortcuts"
8971 &setup.internal.menu_exit, "menu_exit"
8975 &setup.internal.menu_save_and_exit, "menu_save_and_exit"
8979 static struct TokenInfo debug_setup_tokens[] =
8983 &setup.debug.frame_delay[0], "debug.frame_delay_0"
8987 &setup.debug.frame_delay[1], "debug.frame_delay_1"
8991 &setup.debug.frame_delay[2], "debug.frame_delay_2"
8995 &setup.debug.frame_delay[3], "debug.frame_delay_3"
8999 &setup.debug.frame_delay[4], "debug.frame_delay_4"
9003 &setup.debug.frame_delay[5], "debug.frame_delay_5"
9007 &setup.debug.frame_delay[6], "debug.frame_delay_6"
9011 &setup.debug.frame_delay[7], "debug.frame_delay_7"
9015 &setup.debug.frame_delay[8], "debug.frame_delay_8"
9019 &setup.debug.frame_delay[9], "debug.frame_delay_9"
9023 &setup.debug.frame_delay_key[0], "debug.key.frame_delay_0"
9027 &setup.debug.frame_delay_key[1], "debug.key.frame_delay_1"
9031 &setup.debug.frame_delay_key[2], "debug.key.frame_delay_2"
9035 &setup.debug.frame_delay_key[3], "debug.key.frame_delay_3"
9039 &setup.debug.frame_delay_key[4], "debug.key.frame_delay_4"
9043 &setup.debug.frame_delay_key[5], "debug.key.frame_delay_5"
9047 &setup.debug.frame_delay_key[6], "debug.key.frame_delay_6"
9051 &setup.debug.frame_delay_key[7], "debug.key.frame_delay_7"
9055 &setup.debug.frame_delay_key[8], "debug.key.frame_delay_8"
9059 &setup.debug.frame_delay_key[9], "debug.key.frame_delay_9"
9063 &setup.debug.frame_delay_use_mod_key, "debug.frame_delay.use_mod_key"},
9066 &setup.debug.frame_delay_game_only, "debug.frame_delay.game_only"
9070 &setup.debug.show_frames_per_second, "debug.show_frames_per_second"
9074 static struct TokenInfo options_setup_tokens[] =
9078 &setup.options.verbose, "options.verbose"
9082 static char *get_corrected_login_name(char *login_name)
9084 // needed because player name must be a fixed length string
9085 char *login_name_new = checked_malloc(MAX_PLAYER_NAME_LEN + 1);
9087 strncpy(login_name_new, login_name, MAX_PLAYER_NAME_LEN);
9088 login_name_new[MAX_PLAYER_NAME_LEN] = '\0';
9090 if (strlen(login_name) > MAX_PLAYER_NAME_LEN) // name has been cut
9091 if (strchr(login_name_new, ' '))
9092 *strchr(login_name_new, ' ') = '\0';
9094 return login_name_new;
9097 static void setSetupInfoToDefaults(struct SetupInfo *si)
9101 si->player_name = get_corrected_login_name(getLoginName());
9104 si->sound_loops = TRUE;
9105 si->sound_music = TRUE;
9106 si->sound_simple = TRUE;
9108 si->scroll_delay = TRUE;
9109 si->scroll_delay_value = STD_SCROLL_DELAY;
9110 si->engine_snapshot_mode = getStringCopy(STR_SNAPSHOT_MODE_DEFAULT);
9111 si->engine_snapshot_memory = SNAPSHOT_MEMORY_DEFAULT;
9112 si->fade_screens = TRUE;
9113 si->autorecord = TRUE;
9114 si->show_titlescreen = TRUE;
9115 si->quick_doors = FALSE;
9116 si->team_mode = FALSE;
9117 si->handicap = TRUE;
9118 si->skip_levels = TRUE;
9119 si->increment_levels = TRUE;
9120 si->auto_play_next_level = TRUE;
9121 si->skip_scores_after_game = FALSE;
9122 si->time_limit = TRUE;
9123 si->fullscreen = FALSE;
9124 si->window_scaling_percent = STD_WINDOW_SCALING_PERCENT;
9125 si->window_scaling_quality = getStringCopy(SCALING_QUALITY_DEFAULT);
9126 si->screen_rendering_mode = getStringCopy(STR_SPECIAL_RENDERING_DEFAULT);
9127 si->vsync_mode = getStringCopy(STR_VSYNC_MODE_DEFAULT);
9128 si->ask_on_escape = TRUE;
9129 si->ask_on_escape_editor = TRUE;
9130 si->ask_on_game_over = TRUE;
9131 si->quick_switch = FALSE;
9132 si->input_on_focus = FALSE;
9133 si->prefer_aga_graphics = TRUE;
9134 si->game_speed_extended = FALSE;
9135 si->game_frame_delay = GAME_FRAME_DELAY;
9136 si->sp_show_border_elements = FALSE;
9137 si->small_game_graphics = FALSE;
9138 si->show_snapshot_buttons = FALSE;
9140 si->graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
9141 si->sounds_set = getStringCopy(SND_CLASSIC_SUBDIR);
9142 si->music_set = getStringCopy(MUS_CLASSIC_SUBDIR);
9144 si->override_level_graphics = FALSE;
9145 si->override_level_sounds = FALSE;
9146 si->override_level_music = FALSE;
9148 si->volume_simple = 100; // percent
9149 si->volume_loops = 100; // percent
9150 si->volume_music = 100; // percent
9152 si->network_mode = FALSE;
9153 si->network_player_nr = 0; // first player
9154 si->network_server_hostname = getStringCopy(STR_NETWORK_AUTO_DETECT);
9156 si->touch.control_type = getStringCopy(TOUCH_CONTROL_DEFAULT);
9157 si->touch.move_distance = TOUCH_MOVE_DISTANCE_DEFAULT; // percent
9158 si->touch.drop_distance = TOUCH_DROP_DISTANCE_DEFAULT; // percent
9159 si->touch.transparency = TOUCH_TRANSPARENCY_DEFAULT; // percent
9160 si->touch.draw_outlined = TRUE;
9161 si->touch.draw_pressed = TRUE;
9163 for (i = 0; i < 2; i++)
9165 char *default_grid_button[6][2] =
9171 { "111222", " vv " },
9172 { "111222", " vv " }
9174 int grid_xsize = DEFAULT_GRID_XSIZE(i);
9175 int grid_ysize = DEFAULT_GRID_YSIZE(i);
9176 int min_xsize = MIN(6, grid_xsize);
9177 int min_ysize = MIN(6, grid_ysize);
9178 int startx = grid_xsize - min_xsize;
9179 int starty = grid_ysize - min_ysize;
9182 // virtual buttons grid can only be set to defaults if video is initialized
9183 // (this will be repeated if virtual buttons are not loaded from setup file)
9184 if (video.initialized)
9186 si->touch.grid_xsize[i] = grid_xsize;
9187 si->touch.grid_ysize[i] = grid_ysize;
9191 si->touch.grid_xsize[i] = -1;
9192 si->touch.grid_ysize[i] = -1;
9195 for (x = 0; x < MAX_GRID_XSIZE; x++)
9196 for (y = 0; y < MAX_GRID_YSIZE; y++)
9197 si->touch.grid_button[i][x][y] = CHAR_GRID_BUTTON_NONE;
9199 for (x = 0; x < min_xsize; x++)
9200 for (y = 0; y < min_ysize; y++)
9201 si->touch.grid_button[i][x][starty + y] =
9202 default_grid_button[y][0][x];
9204 for (x = 0; x < min_xsize; x++)
9205 for (y = 0; y < min_ysize; y++)
9206 si->touch.grid_button[i][startx + x][starty + y] =
9207 default_grid_button[y][1][x];
9210 si->touch.grid_initialized = video.initialized;
9212 si->editor.el_boulderdash = TRUE;
9213 si->editor.el_emerald_mine = TRUE;
9214 si->editor.el_emerald_mine_club = TRUE;
9215 si->editor.el_more = TRUE;
9216 si->editor.el_sokoban = TRUE;
9217 si->editor.el_supaplex = TRUE;
9218 si->editor.el_diamond_caves = TRUE;
9219 si->editor.el_dx_boulderdash = TRUE;
9221 si->editor.el_mirror_magic = TRUE;
9222 si->editor.el_deflektor = TRUE;
9224 si->editor.el_chars = TRUE;
9225 si->editor.el_steel_chars = TRUE;
9227 si->editor.el_classic = TRUE;
9228 si->editor.el_custom = TRUE;
9230 si->editor.el_user_defined = FALSE;
9231 si->editor.el_dynamic = TRUE;
9233 si->editor.el_headlines = TRUE;
9235 si->editor.show_element_token = FALSE;
9237 si->editor.use_template_for_new_levels = TRUE;
9239 si->shortcut.save_game = DEFAULT_KEY_SAVE_GAME;
9240 si->shortcut.load_game = DEFAULT_KEY_LOAD_GAME;
9241 si->shortcut.toggle_pause = DEFAULT_KEY_TOGGLE_PAUSE;
9243 si->shortcut.focus_player[0] = DEFAULT_KEY_FOCUS_PLAYER_1;
9244 si->shortcut.focus_player[1] = DEFAULT_KEY_FOCUS_PLAYER_2;
9245 si->shortcut.focus_player[2] = DEFAULT_KEY_FOCUS_PLAYER_3;
9246 si->shortcut.focus_player[3] = DEFAULT_KEY_FOCUS_PLAYER_4;
9247 si->shortcut.focus_player_all = DEFAULT_KEY_FOCUS_PLAYER_ALL;
9249 si->shortcut.tape_eject = DEFAULT_KEY_TAPE_EJECT;
9250 si->shortcut.tape_extra = DEFAULT_KEY_TAPE_EXTRA;
9251 si->shortcut.tape_stop = DEFAULT_KEY_TAPE_STOP;
9252 si->shortcut.tape_pause = DEFAULT_KEY_TAPE_PAUSE;
9253 si->shortcut.tape_record = DEFAULT_KEY_TAPE_RECORD;
9254 si->shortcut.tape_play = DEFAULT_KEY_TAPE_PLAY;
9256 si->shortcut.sound_simple = DEFAULT_KEY_SOUND_SIMPLE;
9257 si->shortcut.sound_loops = DEFAULT_KEY_SOUND_LOOPS;
9258 si->shortcut.sound_music = DEFAULT_KEY_SOUND_MUSIC;
9260 si->shortcut.snap_left = DEFAULT_KEY_SNAP_LEFT;
9261 si->shortcut.snap_right = DEFAULT_KEY_SNAP_RIGHT;
9262 si->shortcut.snap_up = DEFAULT_KEY_SNAP_UP;
9263 si->shortcut.snap_down = DEFAULT_KEY_SNAP_DOWN;
9265 for (i = 0; i < MAX_PLAYERS; i++)
9267 si->input[i].use_joystick = FALSE;
9268 si->input[i].joy.device_name=getStringCopy(getDeviceNameFromJoystickNr(i));
9269 si->input[i].joy.xleft = JOYSTICK_XLEFT;
9270 si->input[i].joy.xmiddle = JOYSTICK_XMIDDLE;
9271 si->input[i].joy.xright = JOYSTICK_XRIGHT;
9272 si->input[i].joy.yupper = JOYSTICK_YUPPER;
9273 si->input[i].joy.ymiddle = JOYSTICK_YMIDDLE;
9274 si->input[i].joy.ylower = JOYSTICK_YLOWER;
9275 si->input[i].joy.snap = (i == 0 ? JOY_BUTTON_1 : 0);
9276 si->input[i].joy.drop = (i == 0 ? JOY_BUTTON_2 : 0);
9277 si->input[i].key.left = (i == 0 ? DEFAULT_KEY_LEFT : KSYM_UNDEFINED);
9278 si->input[i].key.right = (i == 0 ? DEFAULT_KEY_RIGHT : KSYM_UNDEFINED);
9279 si->input[i].key.up = (i == 0 ? DEFAULT_KEY_UP : KSYM_UNDEFINED);
9280 si->input[i].key.down = (i == 0 ? DEFAULT_KEY_DOWN : KSYM_UNDEFINED);
9281 si->input[i].key.snap = (i == 0 ? DEFAULT_KEY_SNAP : KSYM_UNDEFINED);
9282 si->input[i].key.drop = (i == 0 ? DEFAULT_KEY_DROP : KSYM_UNDEFINED);
9285 si->system.sdl_videodriver = getStringCopy(ARG_DEFAULT);
9286 si->system.sdl_audiodriver = getStringCopy(ARG_DEFAULT);
9287 si->system.audio_fragment_size = DEFAULT_AUDIO_FRAGMENT_SIZE;
9289 si->internal.program_title = getStringCopy(PROGRAM_TITLE_STRING);
9290 si->internal.program_version = getStringCopy(getProgramRealVersionString());
9291 si->internal.program_author = getStringCopy(PROGRAM_AUTHOR_STRING);
9292 si->internal.program_email = getStringCopy(PROGRAM_EMAIL_STRING);
9293 si->internal.program_website = getStringCopy(PROGRAM_WEBSITE_STRING);
9294 si->internal.program_copyright = getStringCopy(PROGRAM_COPYRIGHT_STRING);
9295 si->internal.program_company = getStringCopy(PROGRAM_COMPANY_STRING);
9297 si->internal.program_icon_file = getStringCopy(PROGRAM_ICON_FILENAME);
9299 si->internal.default_graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
9300 si->internal.default_sounds_set = getStringCopy(SND_CLASSIC_SUBDIR);
9301 si->internal.default_music_set = getStringCopy(MUS_CLASSIC_SUBDIR);
9303 si->internal.fallback_graphics_file = getStringCopy(UNDEFINED_FILENAME);
9304 si->internal.fallback_sounds_file = getStringCopy(UNDEFINED_FILENAME);
9305 si->internal.fallback_music_file = getStringCopy(UNDEFINED_FILENAME);
9307 si->internal.default_level_series = getStringCopy(UNDEFINED_LEVELSET);
9308 si->internal.choose_from_top_leveldir = FALSE;
9309 si->internal.show_scaling_in_title = TRUE;
9310 si->internal.create_user_levelset = TRUE;
9312 si->internal.default_window_width = WIN_XSIZE_DEFAULT;
9313 si->internal.default_window_height = WIN_YSIZE_DEFAULT;
9315 si->debug.frame_delay[0] = DEFAULT_FRAME_DELAY_0;
9316 si->debug.frame_delay[1] = DEFAULT_FRAME_DELAY_1;
9317 si->debug.frame_delay[2] = DEFAULT_FRAME_DELAY_2;
9318 si->debug.frame_delay[3] = DEFAULT_FRAME_DELAY_3;
9319 si->debug.frame_delay[4] = DEFAULT_FRAME_DELAY_4;
9320 si->debug.frame_delay[5] = DEFAULT_FRAME_DELAY_5;
9321 si->debug.frame_delay[6] = DEFAULT_FRAME_DELAY_6;
9322 si->debug.frame_delay[7] = DEFAULT_FRAME_DELAY_7;
9323 si->debug.frame_delay[8] = DEFAULT_FRAME_DELAY_8;
9324 si->debug.frame_delay[9] = DEFAULT_FRAME_DELAY_9;
9326 si->debug.frame_delay_key[0] = DEFAULT_KEY_FRAME_DELAY_0;
9327 si->debug.frame_delay_key[1] = DEFAULT_KEY_FRAME_DELAY_1;
9328 si->debug.frame_delay_key[2] = DEFAULT_KEY_FRAME_DELAY_2;
9329 si->debug.frame_delay_key[3] = DEFAULT_KEY_FRAME_DELAY_3;
9330 si->debug.frame_delay_key[4] = DEFAULT_KEY_FRAME_DELAY_4;
9331 si->debug.frame_delay_key[5] = DEFAULT_KEY_FRAME_DELAY_5;
9332 si->debug.frame_delay_key[6] = DEFAULT_KEY_FRAME_DELAY_6;
9333 si->debug.frame_delay_key[7] = DEFAULT_KEY_FRAME_DELAY_7;
9334 si->debug.frame_delay_key[8] = DEFAULT_KEY_FRAME_DELAY_8;
9335 si->debug.frame_delay_key[9] = DEFAULT_KEY_FRAME_DELAY_9;
9337 si->debug.frame_delay_use_mod_key = DEFAULT_FRAME_DELAY_USE_MOD_KEY;
9338 si->debug.frame_delay_game_only = DEFAULT_FRAME_DELAY_GAME_ONLY;
9340 si->debug.show_frames_per_second = FALSE;
9342 si->options.verbose = FALSE;
9344 #if defined(PLATFORM_ANDROID)
9345 si->fullscreen = TRUE;
9349 static void setSetupInfoToDefaults_AutoSetup(struct SetupInfo *si)
9351 si->auto_setup.editor_zoom_tilesize = MINI_TILESIZE;
9354 static void setSetupInfoToDefaults_EditorCascade(struct SetupInfo *si)
9356 si->editor_cascade.el_bd = TRUE;
9357 si->editor_cascade.el_em = TRUE;
9358 si->editor_cascade.el_emc = TRUE;
9359 si->editor_cascade.el_rnd = TRUE;
9360 si->editor_cascade.el_sb = TRUE;
9361 si->editor_cascade.el_sp = TRUE;
9362 si->editor_cascade.el_dc = TRUE;
9363 si->editor_cascade.el_dx = TRUE;
9365 si->editor_cascade.el_mm = TRUE;
9366 si->editor_cascade.el_df = TRUE;
9368 si->editor_cascade.el_chars = FALSE;
9369 si->editor_cascade.el_steel_chars = FALSE;
9370 si->editor_cascade.el_ce = FALSE;
9371 si->editor_cascade.el_ge = FALSE;
9372 si->editor_cascade.el_ref = FALSE;
9373 si->editor_cascade.el_user = FALSE;
9374 si->editor_cascade.el_dynamic = FALSE;
9377 #define MAX_HIDE_SETUP_TOKEN_SIZE 20
9379 static char *getHideSetupToken(void *setup_value)
9381 static char hide_setup_token[MAX_HIDE_SETUP_TOKEN_SIZE];
9383 if (setup_value != NULL)
9384 snprintf(hide_setup_token, MAX_HIDE_SETUP_TOKEN_SIZE, "%p", setup_value);
9386 return hide_setup_token;
9389 void setHideSetupEntry(void *setup_value)
9391 char *hide_setup_token = getHideSetupToken(setup_value);
9393 if (setup_value != NULL)
9394 setHashEntry(hide_setup_hash, hide_setup_token, "");
9397 boolean hideSetupEntry(void *setup_value)
9399 char *hide_setup_token = getHideSetupToken(setup_value);
9401 return (setup_value != NULL &&
9402 getHashEntry(hide_setup_hash, hide_setup_token) != NULL);
9405 static void setSetupInfoFromTokenText(SetupFileHash *setup_file_hash,
9406 struct TokenInfo *token_info,
9407 int token_nr, char *token_text)
9409 char *token_hide_text = getStringCat2(token_text, ".hide");
9410 char *token_hide_value = getHashEntry(setup_file_hash, token_hide_text);
9412 // set the value of this setup option in the setup option structure
9413 setSetupInfo(token_info, token_nr, getHashEntry(setup_file_hash, token_text));
9415 // check if this setup option should be hidden in the setup menu
9416 if (token_hide_value != NULL && get_boolean_from_string(token_hide_value))
9417 setHideSetupEntry(token_info[token_nr].value);
9420 static void setSetupInfoFromTokenInfo(SetupFileHash *setup_file_hash,
9421 struct TokenInfo *token_info,
9424 setSetupInfoFromTokenText(setup_file_hash, token_info, token_nr,
9425 token_info[token_nr].text);
9428 static void decodeSetupFileHash(SetupFileHash *setup_file_hash)
9432 if (!setup_file_hash)
9435 if (hide_setup_hash == NULL)
9436 hide_setup_hash = newSetupFileHash();
9438 for (i = 0; i < ARRAY_SIZE(global_setup_tokens); i++)
9439 setSetupInfoFromTokenInfo(setup_file_hash, global_setup_tokens, i);
9441 setup.touch.grid_initialized = TRUE;
9442 for (i = 0; i < 2; i++)
9444 int grid_xsize = setup.touch.grid_xsize[i];
9445 int grid_ysize = setup.touch.grid_ysize[i];
9448 // if virtual buttons are not loaded from setup file, repeat initializing
9449 // virtual buttons grid with default values later when video is initialized
9450 if (grid_xsize == -1 ||
9453 setup.touch.grid_initialized = FALSE;
9458 for (y = 0; y < grid_ysize; y++)
9460 char token_string[MAX_LINE_LEN];
9462 sprintf(token_string, "touch.virtual_buttons.%d.%02d", i, y);
9464 char *value_string = getHashEntry(setup_file_hash, token_string);
9466 if (value_string == NULL)
9469 for (x = 0; x < grid_xsize; x++)
9471 char c = value_string[x];
9473 setup.touch.grid_button[i][x][y] =
9474 (c == '.' ? CHAR_GRID_BUTTON_NONE : c);
9479 for (i = 0; i < ARRAY_SIZE(editor_setup_tokens); i++)
9480 setSetupInfoFromTokenInfo(setup_file_hash, editor_setup_tokens, i);
9482 for (i = 0; i < ARRAY_SIZE(shortcut_setup_tokens); i++)
9483 setSetupInfoFromTokenInfo(setup_file_hash, shortcut_setup_tokens, i);
9485 for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
9489 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
9491 setup_input = setup.input[pnr];
9492 for (i = 0; i < ARRAY_SIZE(player_setup_tokens); i++)
9494 char full_token[100];
9496 sprintf(full_token, "%s%s", prefix, player_setup_tokens[i].text);
9497 setSetupInfoFromTokenText(setup_file_hash, player_setup_tokens, i,
9500 setup.input[pnr] = setup_input;
9503 for (i = 0; i < ARRAY_SIZE(system_setup_tokens); i++)
9504 setSetupInfoFromTokenInfo(setup_file_hash, system_setup_tokens, i);
9506 for (i = 0; i < ARRAY_SIZE(internal_setup_tokens); i++)
9507 setSetupInfoFromTokenInfo(setup_file_hash, internal_setup_tokens, i);
9509 for (i = 0; i < ARRAY_SIZE(debug_setup_tokens); i++)
9510 setSetupInfoFromTokenInfo(setup_file_hash, debug_setup_tokens, i);
9512 for (i = 0; i < ARRAY_SIZE(options_setup_tokens); i++)
9513 setSetupInfoFromTokenInfo(setup_file_hash, options_setup_tokens, i);
9515 setHideRelatedSetupEntries();
9518 static void decodeSetupFileHash_AutoSetup(SetupFileHash *setup_file_hash)
9522 if (!setup_file_hash)
9525 for (i = 0; i < ARRAY_SIZE(auto_setup_tokens); i++)
9526 setSetupInfo(auto_setup_tokens, i,
9527 getHashEntry(setup_file_hash,
9528 auto_setup_tokens[i].text));
9531 static void decodeSetupFileHash_EditorCascade(SetupFileHash *setup_file_hash)
9535 if (!setup_file_hash)
9538 for (i = 0; i < ARRAY_SIZE(editor_cascade_setup_tokens); i++)
9539 setSetupInfo(editor_cascade_setup_tokens, i,
9540 getHashEntry(setup_file_hash,
9541 editor_cascade_setup_tokens[i].text));
9544 void LoadSetupFromFilename(char *filename)
9546 SetupFileHash *setup_file_hash = loadSetupFileHash(filename);
9548 if (setup_file_hash)
9550 decodeSetupFileHash(setup_file_hash);
9552 freeSetupFileHash(setup_file_hash);
9556 Error(ERR_DEBUG, "using default setup values");
9560 static void LoadSetup_SpecialPostProcessing(void)
9562 char *player_name_new;
9564 // needed to work around problems with fixed length strings
9565 player_name_new = get_corrected_login_name(setup.player_name);
9566 free(setup.player_name);
9567 setup.player_name = player_name_new;
9569 // "scroll_delay: on(3) / off(0)" was replaced by scroll delay value
9570 if (setup.scroll_delay == FALSE)
9572 setup.scroll_delay_value = MIN_SCROLL_DELAY;
9573 setup.scroll_delay = TRUE; // now always "on"
9576 // make sure that scroll delay value stays inside valid range
9577 setup.scroll_delay_value =
9578 MIN(MAX(MIN_SCROLL_DELAY, setup.scroll_delay_value), MAX_SCROLL_DELAY);
9581 void LoadSetup(void)
9585 // always start with reliable default values
9586 setSetupInfoToDefaults(&setup);
9588 // try to load setup values from default setup file
9589 filename = getDefaultSetupFilename();
9591 if (fileExists(filename))
9592 LoadSetupFromFilename(filename);
9594 // try to load setup values from user setup file
9595 filename = getSetupFilename();
9597 LoadSetupFromFilename(filename);
9599 LoadSetup_SpecialPostProcessing();
9602 void LoadSetup_AutoSetup(void)
9604 char *filename = getPath2(getSetupDir(), AUTOSETUP_FILENAME);
9605 SetupFileHash *setup_file_hash = NULL;
9607 // always start with reliable default values
9608 setSetupInfoToDefaults_AutoSetup(&setup);
9610 setup_file_hash = loadSetupFileHash(filename);
9612 if (setup_file_hash)
9614 decodeSetupFileHash_AutoSetup(setup_file_hash);
9616 freeSetupFileHash(setup_file_hash);
9622 void LoadSetup_EditorCascade(void)
9624 char *filename = getPath2(getSetupDir(), EDITORCASCADE_FILENAME);
9625 SetupFileHash *setup_file_hash = NULL;
9627 // always start with reliable default values
9628 setSetupInfoToDefaults_EditorCascade(&setup);
9630 setup_file_hash = loadSetupFileHash(filename);
9632 if (setup_file_hash)
9634 decodeSetupFileHash_EditorCascade(setup_file_hash);
9636 freeSetupFileHash(setup_file_hash);
9642 static void addGameControllerMappingToHash(SetupFileHash *mappings_hash,
9645 char mapping_guid[MAX_LINE_LEN];
9646 char *mapping_start, *mapping_end;
9648 // get GUID from game controller mapping line: copy complete line
9649 strncpy(mapping_guid, mapping_line, MAX_LINE_LEN - 1);
9650 mapping_guid[MAX_LINE_LEN - 1] = '\0';
9652 // get GUID from game controller mapping line: cut after GUID part
9653 mapping_start = strchr(mapping_guid, ',');
9654 if (mapping_start != NULL)
9655 *mapping_start = '\0';
9657 // cut newline from game controller mapping line
9658 mapping_end = strchr(mapping_line, '\n');
9659 if (mapping_end != NULL)
9660 *mapping_end = '\0';
9662 // add mapping entry to game controller mappings hash
9663 setHashEntry(mappings_hash, mapping_guid, mapping_line);
9666 static void LoadSetup_ReadGameControllerMappings(SetupFileHash *mappings_hash,
9671 if (!(file = fopen(filename, MODE_READ)))
9673 Error(ERR_WARN, "cannot read game controller mappings file '%s'", filename);
9680 char line[MAX_LINE_LEN];
9682 if (!fgets(line, MAX_LINE_LEN, file))
9685 addGameControllerMappingToHash(mappings_hash, line);
9691 void SaveSetup(void)
9693 char *filename = getSetupFilename();
9697 InitUserDataDirectory();
9699 if (!(file = fopen(filename, MODE_WRITE)))
9701 Error(ERR_WARN, "cannot write setup file '%s'", filename);
9705 fprintFileHeader(file, SETUP_FILENAME);
9707 for (i = 0; i < ARRAY_SIZE(global_setup_tokens); i++)
9709 // just to make things nicer :)
9710 if (global_setup_tokens[i].value == &setup.sound ||
9711 global_setup_tokens[i].value == &setup.graphics_set ||
9712 global_setup_tokens[i].value == &setup.volume_simple ||
9713 global_setup_tokens[i].value == &setup.network_mode ||
9714 global_setup_tokens[i].value == &setup.touch.control_type ||
9715 global_setup_tokens[i].value == &setup.touch.grid_xsize[0] ||
9716 global_setup_tokens[i].value == &setup.touch.grid_xsize[1])
9717 fprintf(file, "\n");
9719 fprintf(file, "%s\n", getSetupLine(global_setup_tokens, "", i));
9722 for (i = 0; i < 2; i++)
9724 int grid_xsize = setup.touch.grid_xsize[i];
9725 int grid_ysize = setup.touch.grid_ysize[i];
9728 fprintf(file, "\n");
9730 for (y = 0; y < grid_ysize; y++)
9732 char token_string[MAX_LINE_LEN];
9733 char value_string[MAX_LINE_LEN];
9735 sprintf(token_string, "touch.virtual_buttons.%d.%02d", i, y);
9737 for (x = 0; x < grid_xsize; x++)
9739 char c = setup.touch.grid_button[i][x][y];
9741 value_string[x] = (c == CHAR_GRID_BUTTON_NONE ? '.' : c);
9744 value_string[grid_xsize] = '\0';
9746 fprintf(file, "%s\n", getFormattedSetupEntry(token_string, value_string));
9750 fprintf(file, "\n");
9751 for (i = 0; i < ARRAY_SIZE(editor_setup_tokens); i++)
9752 fprintf(file, "%s\n", getSetupLine(editor_setup_tokens, "", i));
9754 fprintf(file, "\n");
9755 for (i = 0; i < ARRAY_SIZE(shortcut_setup_tokens); i++)
9756 fprintf(file, "%s\n", getSetupLine(shortcut_setup_tokens, "", i));
9758 for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
9762 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
9763 fprintf(file, "\n");
9765 setup_input = setup.input[pnr];
9766 for (i = 0; i < ARRAY_SIZE(player_setup_tokens); i++)
9767 fprintf(file, "%s\n", getSetupLine(player_setup_tokens, prefix, i));
9770 fprintf(file, "\n");
9771 for (i = 0; i < ARRAY_SIZE(system_setup_tokens); i++)
9772 fprintf(file, "%s\n", getSetupLine(system_setup_tokens, "", i));
9774 // (internal setup values not saved to user setup file)
9776 fprintf(file, "\n");
9777 for (i = 0; i < ARRAY_SIZE(debug_setup_tokens); i++)
9778 fprintf(file, "%s\n", getSetupLine(debug_setup_tokens, "", i));
9780 fprintf(file, "\n");
9781 for (i = 0; i < ARRAY_SIZE(options_setup_tokens); i++)
9782 fprintf(file, "%s\n", getSetupLine(options_setup_tokens, "", i));
9786 SetFilePermissions(filename, PERMS_PRIVATE);
9789 void SaveSetup_AutoSetup(void)
9791 char *filename = getPath2(getSetupDir(), AUTOSETUP_FILENAME);
9795 InitUserDataDirectory();
9797 if (!(file = fopen(filename, MODE_WRITE)))
9799 Error(ERR_WARN, "cannot write auto setup file '%s'", filename);
9804 fprintFileHeader(file, AUTOSETUP_FILENAME);
9806 for (i = 0; i < ARRAY_SIZE(auto_setup_tokens); i++)
9807 fprintf(file, "%s\n", getSetupLine(auto_setup_tokens, "", i));
9811 SetFilePermissions(filename, PERMS_PRIVATE);
9816 void SaveSetup_EditorCascade(void)
9818 char *filename = getPath2(getSetupDir(), EDITORCASCADE_FILENAME);
9822 InitUserDataDirectory();
9824 if (!(file = fopen(filename, MODE_WRITE)))
9826 Error(ERR_WARN, "cannot write editor cascade state file '%s'", filename);
9831 fprintFileHeader(file, EDITORCASCADE_FILENAME);
9833 for (i = 0; i < ARRAY_SIZE(editor_cascade_setup_tokens); i++)
9834 fprintf(file, "%s\n", getSetupLine(editor_cascade_setup_tokens, "", i));
9838 SetFilePermissions(filename, PERMS_PRIVATE);
9843 static void SaveSetup_WriteGameControllerMappings(SetupFileHash *mappings_hash,
9848 if (!(file = fopen(filename, MODE_WRITE)))
9850 Error(ERR_WARN, "cannot write game controller mappings file '%s'",filename);
9855 BEGIN_HASH_ITERATION(mappings_hash, itr)
9857 fprintf(file, "%s\n", HASH_ITERATION_VALUE(itr));
9859 END_HASH_ITERATION(mappings_hash, itr)
9864 void SaveSetup_AddGameControllerMapping(char *mapping)
9866 char *filename = getPath2(getSetupDir(), GAMECONTROLLER_BASENAME);
9867 SetupFileHash *mappings_hash = newSetupFileHash();
9869 InitUserDataDirectory();
9871 // load existing personal game controller mappings
9872 LoadSetup_ReadGameControllerMappings(mappings_hash, filename);
9874 // add new mapping to personal game controller mappings
9875 addGameControllerMappingToHash(mappings_hash, mapping);
9877 // save updated personal game controller mappings
9878 SaveSetup_WriteGameControllerMappings(mappings_hash, filename);
9880 freeSetupFileHash(mappings_hash);
9884 void LoadCustomElementDescriptions(void)
9886 char *filename = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
9887 SetupFileHash *setup_file_hash;
9890 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
9892 if (element_info[i].custom_description != NULL)
9894 free(element_info[i].custom_description);
9895 element_info[i].custom_description = NULL;
9899 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
9902 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
9904 char *token = getStringCat2(element_info[i].token_name, ".name");
9905 char *value = getHashEntry(setup_file_hash, token);
9908 element_info[i].custom_description = getStringCopy(value);
9913 freeSetupFileHash(setup_file_hash);
9916 static int getElementFromToken(char *token)
9918 char *value = getHashEntry(element_token_hash, token);
9923 Error(ERR_WARN, "unknown element token '%s'", token);
9925 return EL_UNDEFINED;
9928 void FreeGlobalAnimEventInfo(void)
9930 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
9932 if (gaei->event_list == NULL)
9937 for (i = 0; i < gaei->num_event_lists; i++)
9939 checked_free(gaei->event_list[i]->event_value);
9940 checked_free(gaei->event_list[i]);
9943 checked_free(gaei->event_list);
9945 gaei->event_list = NULL;
9946 gaei->num_event_lists = 0;
9949 static int AddGlobalAnimEventList(void)
9951 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
9952 int list_pos = gaei->num_event_lists++;
9954 gaei->event_list = checked_realloc(gaei->event_list, gaei->num_event_lists *
9955 sizeof(struct GlobalAnimEventListInfo *));
9957 gaei->event_list[list_pos] =
9958 checked_calloc(sizeof(struct GlobalAnimEventListInfo));
9960 struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
9962 gaeli->event_value = NULL;
9963 gaeli->num_event_values = 0;
9968 static int AddGlobalAnimEventValue(int list_pos, int event_value)
9970 // do not add empty global animation events
9971 if (event_value == ANIM_EVENT_NONE)
9974 // if list position is undefined, create new list
9975 if (list_pos == ANIM_EVENT_UNDEFINED)
9976 list_pos = AddGlobalAnimEventList();
9978 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
9979 struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
9980 int value_pos = gaeli->num_event_values++;
9982 gaeli->event_value = checked_realloc(gaeli->event_value,
9983 gaeli->num_event_values * sizeof(int *));
9985 gaeli->event_value[value_pos] = event_value;
9990 int GetGlobalAnimEventValue(int list_pos, int value_pos)
9992 if (list_pos == ANIM_EVENT_UNDEFINED)
9993 return ANIM_EVENT_NONE;
9995 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
9996 struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
9998 return gaeli->event_value[value_pos];
10001 int GetGlobalAnimEventValueCount(int list_pos)
10003 if (list_pos == ANIM_EVENT_UNDEFINED)
10006 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
10007 struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
10009 return gaeli->num_event_values;
10012 // This function checks if a string <s> of the format "string1, string2, ..."
10013 // exactly contains a string <s_contained>.
10015 static boolean string_has_parameter(char *s, char *s_contained)
10019 if (s == NULL || s_contained == NULL)
10022 if (strlen(s_contained) > strlen(s))
10025 if (strncmp(s, s_contained, strlen(s_contained)) == 0)
10027 char next_char = s[strlen(s_contained)];
10029 // check if next character is delimiter or whitespace
10030 return (next_char == ',' || next_char == '\0' ||
10031 next_char == ' ' || next_char == '\t' ? TRUE : FALSE);
10034 // check if string contains another parameter string after a comma
10035 substring = strchr(s, ',');
10036 if (substring == NULL) // string does not contain a comma
10039 // advance string pointer to next character after the comma
10042 // skip potential whitespaces after the comma
10043 while (*substring == ' ' || *substring == '\t')
10046 return string_has_parameter(substring, s_contained);
10049 static int get_anim_parameter_value(char *s)
10051 int event_value[] =
10059 char *pattern_1[] =
10067 char *pattern_2 = ".part_";
10068 char *matching_char = NULL;
10070 int pattern_1_len = 0;
10071 int result = ANIM_EVENT_NONE;
10074 for (i = 0; i < ARRAY_SIZE(event_value); i++)
10076 matching_char = strstr(s_ptr, pattern_1[i]);
10077 pattern_1_len = strlen(pattern_1[i]);
10078 result = event_value[i];
10080 if (matching_char != NULL)
10084 if (matching_char == NULL)
10085 return ANIM_EVENT_NONE;
10087 s_ptr = matching_char + pattern_1_len;
10089 // check for main animation number ("anim_X" or "anim_XX")
10090 if (*s_ptr >= '0' && *s_ptr <= '9')
10092 int gic_anim_nr = (*s_ptr++ - '0');
10094 if (*s_ptr >= '0' && *s_ptr <= '9')
10095 gic_anim_nr = 10 * gic_anim_nr + (*s_ptr++ - '0');
10097 if (gic_anim_nr < 1 || gic_anim_nr > MAX_GLOBAL_ANIMS)
10098 return ANIM_EVENT_NONE;
10100 result |= gic_anim_nr << ANIM_EVENT_ANIM_BIT;
10104 // invalid main animation number specified
10106 return ANIM_EVENT_NONE;
10109 // check for animation part number ("part_X" or "part_XX") (optional)
10110 if (strPrefix(s_ptr, pattern_2))
10112 s_ptr += strlen(pattern_2);
10114 if (*s_ptr >= '0' && *s_ptr <= '9')
10116 int gic_part_nr = (*s_ptr++ - '0');
10118 if (*s_ptr >= '0' && *s_ptr <= '9')
10119 gic_part_nr = 10 * gic_part_nr + (*s_ptr++ - '0');
10121 if (gic_part_nr < 1 || gic_part_nr > MAX_GLOBAL_ANIM_PARTS)
10122 return ANIM_EVENT_NONE;
10124 result |= gic_part_nr << ANIM_EVENT_PART_BIT;
10128 // invalid animation part number specified
10130 return ANIM_EVENT_NONE;
10134 // discard result if next character is neither delimiter nor whitespace
10135 if (!(*s_ptr == ',' || *s_ptr == '\0' ||
10136 *s_ptr == ' ' || *s_ptr == '\t'))
10137 return ANIM_EVENT_NONE;
10142 static int get_anim_parameter_values(char *s)
10144 int list_pos = ANIM_EVENT_UNDEFINED;
10145 int event_value = ANIM_EVENT_DEFAULT;
10147 if (string_has_parameter(s, "any"))
10148 event_value |= ANIM_EVENT_ANY;
10150 if (string_has_parameter(s, "click:self") ||
10151 string_has_parameter(s, "click") ||
10152 string_has_parameter(s, "self"))
10153 event_value |= ANIM_EVENT_SELF;
10155 if (string_has_parameter(s, "unclick:any"))
10156 event_value |= ANIM_EVENT_UNCLICK_ANY;
10158 // if animation event found, add it to global animation event list
10159 if (event_value != ANIM_EVENT_NONE)
10160 list_pos = AddGlobalAnimEventValue(list_pos, event_value);
10164 // add optional "click:anim_X" or "click:anim_X.part_X" parameter
10165 event_value = get_anim_parameter_value(s);
10167 // if animation event found, add it to global animation event list
10168 if (event_value != ANIM_EVENT_NONE)
10169 list_pos = AddGlobalAnimEventValue(list_pos, event_value);
10171 // continue with next part of the string, starting with next comma
10172 s = strchr(s + 1, ',');
10178 static int get_anim_action_parameter_value(char *token)
10180 int result = getImageIDFromToken(token);
10184 char *gfx_token = getStringCat2("gfx.", token);
10186 result = getImageIDFromToken(gfx_token);
10188 checked_free(gfx_token);
10193 Key key = getKeyFromX11KeyName(token);
10195 if (key != KSYM_UNDEFINED)
10196 result = -(int)key;
10200 result = ANIM_EVENT_ACTION_NONE;
10205 int get_parameter_value(char *value_raw, char *suffix, int type)
10207 char *value = getStringToLower(value_raw);
10208 int result = 0; // probably a save default value
10210 if (strEqual(suffix, ".direction"))
10212 result = (strEqual(value, "left") ? MV_LEFT :
10213 strEqual(value, "right") ? MV_RIGHT :
10214 strEqual(value, "up") ? MV_UP :
10215 strEqual(value, "down") ? MV_DOWN : MV_NONE);
10217 else if (strEqual(suffix, ".position"))
10219 result = (strEqual(value, "left") ? POS_LEFT :
10220 strEqual(value, "right") ? POS_RIGHT :
10221 strEqual(value, "top") ? POS_TOP :
10222 strEqual(value, "upper") ? POS_UPPER :
10223 strEqual(value, "middle") ? POS_MIDDLE :
10224 strEqual(value, "lower") ? POS_LOWER :
10225 strEqual(value, "bottom") ? POS_BOTTOM :
10226 strEqual(value, "any") ? POS_ANY :
10227 strEqual(value, "last") ? POS_LAST : POS_UNDEFINED);
10229 else if (strEqual(suffix, ".align"))
10231 result = (strEqual(value, "left") ? ALIGN_LEFT :
10232 strEqual(value, "right") ? ALIGN_RIGHT :
10233 strEqual(value, "center") ? ALIGN_CENTER :
10234 strEqual(value, "middle") ? ALIGN_CENTER : ALIGN_DEFAULT);
10236 else if (strEqual(suffix, ".valign"))
10238 result = (strEqual(value, "top") ? VALIGN_TOP :
10239 strEqual(value, "bottom") ? VALIGN_BOTTOM :
10240 strEqual(value, "middle") ? VALIGN_MIDDLE :
10241 strEqual(value, "center") ? VALIGN_MIDDLE : VALIGN_DEFAULT);
10243 else if (strEqual(suffix, ".anim_mode"))
10245 result = (string_has_parameter(value, "none") ? ANIM_NONE :
10246 string_has_parameter(value, "loop") ? ANIM_LOOP :
10247 string_has_parameter(value, "linear") ? ANIM_LINEAR :
10248 string_has_parameter(value, "pingpong") ? ANIM_PINGPONG :
10249 string_has_parameter(value, "pingpong2") ? ANIM_PINGPONG2 :
10250 string_has_parameter(value, "random") ? ANIM_RANDOM :
10251 string_has_parameter(value, "ce_value") ? ANIM_CE_VALUE :
10252 string_has_parameter(value, "ce_score") ? ANIM_CE_SCORE :
10253 string_has_parameter(value, "ce_delay") ? ANIM_CE_DELAY :
10254 string_has_parameter(value, "horizontal") ? ANIM_HORIZONTAL :
10255 string_has_parameter(value, "vertical") ? ANIM_VERTICAL :
10256 string_has_parameter(value, "centered") ? ANIM_CENTERED :
10257 string_has_parameter(value, "all") ? ANIM_ALL :
10260 if (string_has_parameter(value, "once"))
10261 result |= ANIM_ONCE;
10263 if (string_has_parameter(value, "reverse"))
10264 result |= ANIM_REVERSE;
10266 if (string_has_parameter(value, "opaque_player"))
10267 result |= ANIM_OPAQUE_PLAYER;
10269 if (string_has_parameter(value, "static_panel"))
10270 result |= ANIM_STATIC_PANEL;
10272 else if (strEqual(suffix, ".init_event") ||
10273 strEqual(suffix, ".anim_event"))
10275 result = get_anim_parameter_values(value);
10277 else if (strEqual(suffix, ".init_delay_action") ||
10278 strEqual(suffix, ".anim_delay_action") ||
10279 strEqual(suffix, ".post_delay_action") ||
10280 strEqual(suffix, ".init_event_action") ||
10281 strEqual(suffix, ".anim_event_action"))
10283 result = get_anim_action_parameter_value(value_raw);
10285 else if (strEqual(suffix, ".class"))
10287 result = (strEqual(value, ARG_UNDEFINED) ? ARG_UNDEFINED_VALUE :
10288 get_hash_from_key(value));
10290 else if (strEqual(suffix, ".style"))
10292 result = STYLE_DEFAULT;
10294 if (string_has_parameter(value, "accurate_borders"))
10295 result |= STYLE_ACCURATE_BORDERS;
10297 if (string_has_parameter(value, "inner_corners"))
10298 result |= STYLE_INNER_CORNERS;
10300 if (string_has_parameter(value, "reverse"))
10301 result |= STYLE_REVERSE;
10303 if (string_has_parameter(value, "passthrough_clicks"))
10304 result |= STYLE_PASSTHROUGH;
10306 if (string_has_parameter(value, "multiple_actions"))
10307 result |= STYLE_MULTIPLE_ACTIONS;
10309 else if (strEqual(suffix, ".fade_mode"))
10311 result = (string_has_parameter(value, "none") ? FADE_MODE_NONE :
10312 string_has_parameter(value, "fade") ? FADE_MODE_FADE :
10313 string_has_parameter(value, "crossfade") ? FADE_MODE_CROSSFADE :
10314 string_has_parameter(value, "melt") ? FADE_MODE_MELT :
10315 string_has_parameter(value, "curtain") ? FADE_MODE_CURTAIN :
10316 FADE_MODE_DEFAULT);
10318 else if (strEqual(suffix, ".auto_delay_unit"))
10320 result = (string_has_parameter(value, "ms") ? AUTO_DELAY_UNIT_MS :
10321 string_has_parameter(value, "frames") ? AUTO_DELAY_UNIT_FRAMES :
10322 AUTO_DELAY_UNIT_DEFAULT);
10324 else if (strPrefix(suffix, ".font")) // (may also be ".font_xyz")
10326 result = gfx.get_font_from_token_function(value);
10328 else // generic parameter of type integer or boolean
10330 result = (strEqual(value, ARG_UNDEFINED) ? ARG_UNDEFINED_VALUE :
10331 type == TYPE_INTEGER ? get_integer_from_string(value) :
10332 type == TYPE_BOOLEAN ? get_boolean_from_string(value) :
10333 ARG_UNDEFINED_VALUE);
10341 static int get_token_parameter_value(char *token, char *value_raw)
10345 if (token == NULL || value_raw == NULL)
10346 return ARG_UNDEFINED_VALUE;
10348 suffix = strrchr(token, '.');
10349 if (suffix == NULL)
10352 if (strEqual(suffix, ".element"))
10353 return getElementFromToken(value_raw);
10355 // !!! USE CORRECT VALUE TYPE (currently works also for TYPE_BOOLEAN) !!!
10356 return get_parameter_value(value_raw, suffix, TYPE_INTEGER);
10359 void InitMenuDesignSettings_Static(void)
10363 // always start with reliable default values from static default config
10364 for (i = 0; image_config_vars[i].token != NULL; i++)
10366 char *value = getHashEntry(image_config_hash, image_config_vars[i].token);
10369 *image_config_vars[i].value =
10370 get_token_parameter_value(image_config_vars[i].token, value);
10374 static void InitMenuDesignSettings_SpecialPreProcessing(void)
10378 // the following initializes hierarchical values from static configuration
10380 // special case: initialize "ARG_DEFAULT" values in static default config
10381 // (e.g., initialize "[titlemessage].fade_mode" from "[title].fade_mode")
10382 titlescreen_initial_first_default.fade_mode =
10383 title_initial_first_default.fade_mode;
10384 titlescreen_initial_first_default.fade_delay =
10385 title_initial_first_default.fade_delay;
10386 titlescreen_initial_first_default.post_delay =
10387 title_initial_first_default.post_delay;
10388 titlescreen_initial_first_default.auto_delay =
10389 title_initial_first_default.auto_delay;
10390 titlescreen_initial_first_default.auto_delay_unit =
10391 title_initial_first_default.auto_delay_unit;
10392 titlescreen_first_default.fade_mode = title_first_default.fade_mode;
10393 titlescreen_first_default.fade_delay = title_first_default.fade_delay;
10394 titlescreen_first_default.post_delay = title_first_default.post_delay;
10395 titlescreen_first_default.auto_delay = title_first_default.auto_delay;
10396 titlescreen_first_default.auto_delay_unit =
10397 title_first_default.auto_delay_unit;
10398 titlemessage_initial_first_default.fade_mode =
10399 title_initial_first_default.fade_mode;
10400 titlemessage_initial_first_default.fade_delay =
10401 title_initial_first_default.fade_delay;
10402 titlemessage_initial_first_default.post_delay =
10403 title_initial_first_default.post_delay;
10404 titlemessage_initial_first_default.auto_delay =
10405 title_initial_first_default.auto_delay;
10406 titlemessage_initial_first_default.auto_delay_unit =
10407 title_initial_first_default.auto_delay_unit;
10408 titlemessage_first_default.fade_mode = title_first_default.fade_mode;
10409 titlemessage_first_default.fade_delay = title_first_default.fade_delay;
10410 titlemessage_first_default.post_delay = title_first_default.post_delay;
10411 titlemessage_first_default.auto_delay = title_first_default.auto_delay;
10412 titlemessage_first_default.auto_delay_unit =
10413 title_first_default.auto_delay_unit;
10415 titlescreen_initial_default.fade_mode = title_initial_default.fade_mode;
10416 titlescreen_initial_default.fade_delay = title_initial_default.fade_delay;
10417 titlescreen_initial_default.post_delay = title_initial_default.post_delay;
10418 titlescreen_initial_default.auto_delay = title_initial_default.auto_delay;
10419 titlescreen_initial_default.auto_delay_unit =
10420 title_initial_default.auto_delay_unit;
10421 titlescreen_default.fade_mode = title_default.fade_mode;
10422 titlescreen_default.fade_delay = title_default.fade_delay;
10423 titlescreen_default.post_delay = title_default.post_delay;
10424 titlescreen_default.auto_delay = title_default.auto_delay;
10425 titlescreen_default.auto_delay_unit = title_default.auto_delay_unit;
10426 titlemessage_initial_default.fade_mode = title_initial_default.fade_mode;
10427 titlemessage_initial_default.fade_delay = title_initial_default.fade_delay;
10428 titlemessage_initial_default.post_delay = title_initial_default.post_delay;
10429 titlemessage_initial_default.auto_delay_unit =
10430 title_initial_default.auto_delay_unit;
10431 titlemessage_default.fade_mode = title_default.fade_mode;
10432 titlemessage_default.fade_delay = title_default.fade_delay;
10433 titlemessage_default.post_delay = title_default.post_delay;
10434 titlemessage_default.auto_delay = title_default.auto_delay;
10435 titlemessage_default.auto_delay_unit = title_default.auto_delay_unit;
10437 // special case: initialize "ARG_DEFAULT" values in static default config
10438 // (e.g., init "titlemessage_1.fade_mode" from "[titlemessage].fade_mode")
10439 for (i = 0; i < MAX_NUM_TITLE_MESSAGES; i++)
10441 titlescreen_initial_first[i] = titlescreen_initial_first_default;
10442 titlescreen_first[i] = titlescreen_first_default;
10443 titlemessage_initial_first[i] = titlemessage_initial_first_default;
10444 titlemessage_first[i] = titlemessage_first_default;
10446 titlescreen_initial[i] = titlescreen_initial_default;
10447 titlescreen[i] = titlescreen_default;
10448 titlemessage_initial[i] = titlemessage_initial_default;
10449 titlemessage[i] = titlemessage_default;
10452 // special case: initialize "ARG_DEFAULT" values in static default config
10453 // (eg, init "menu.enter_screen.SCORES.xyz" from "menu.enter_screen.xyz")
10454 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
10456 if (i == GFX_SPECIAL_ARG_TITLE) // title values already initialized
10459 menu.enter_screen[i] = menu.enter_screen[GFX_SPECIAL_ARG_DEFAULT];
10460 menu.leave_screen[i] = menu.leave_screen[GFX_SPECIAL_ARG_DEFAULT];
10461 menu.next_screen[i] = menu.next_screen[GFX_SPECIAL_ARG_DEFAULT];
10464 // special case: initialize "ARG_DEFAULT" values in static default config
10465 // (eg, init "viewport.door_1.MAIN.xyz" from "viewport.door_1.xyz")
10466 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
10468 viewport.window[i] = viewport.window[GFX_SPECIAL_ARG_DEFAULT];
10469 viewport.playfield[i] = viewport.playfield[GFX_SPECIAL_ARG_DEFAULT];
10470 viewport.door_1[i] = viewport.door_1[GFX_SPECIAL_ARG_DEFAULT];
10472 if (i == GFX_SPECIAL_ARG_EDITOR) // editor values already initialized
10475 viewport.door_2[i] = viewport.door_2[GFX_SPECIAL_ARG_DEFAULT];
10479 static void InitMenuDesignSettings_SpecialPostProcessing(void)
10483 struct XY *dst, *src;
10485 game_buttons_xy[] =
10487 { &game.button.save, &game.button.stop },
10488 { &game.button.pause2, &game.button.pause },
10489 { &game.button.load, &game.button.play },
10490 { &game.button.undo, &game.button.stop },
10491 { &game.button.redo, &game.button.play },
10497 // special case: initialize later added SETUP list size from LEVELS value
10498 if (menu.list_size[GAME_MODE_SETUP] == -1)
10499 menu.list_size[GAME_MODE_SETUP] = menu.list_size[GAME_MODE_LEVELS];
10501 // set default position for snapshot buttons to stop/pause/play buttons
10502 for (i = 0; game_buttons_xy[i].dst != NULL; i++)
10503 if ((*game_buttons_xy[i].dst).x == -1 &&
10504 (*game_buttons_xy[i].dst).y == -1)
10505 *game_buttons_xy[i].dst = *game_buttons_xy[i].src;
10507 // --------------------------------------------------------------------------
10508 // dynamic viewports (including playfield margins, borders and alignments)
10509 // --------------------------------------------------------------------------
10511 // dynamic viewports currently only supported for landscape mode
10512 int display_width = MAX(video.display_width, video.display_height);
10513 int display_height = MIN(video.display_width, video.display_height);
10515 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
10517 struct RectWithBorder *vp_window = &viewport.window[i];
10518 struct RectWithBorder *vp_playfield = &viewport.playfield[i];
10519 struct RectWithBorder *vp_door_1 = &viewport.door_1[i];
10520 struct RectWithBorder *vp_door_2 = &viewport.door_2[i];
10521 boolean dynamic_window_width = (vp_window->min_width != -1);
10522 boolean dynamic_window_height = (vp_window->min_height != -1);
10523 boolean dynamic_playfield_width = (vp_playfield->min_width != -1);
10524 boolean dynamic_playfield_height = (vp_playfield->min_height != -1);
10526 // adjust window size if min/max width/height is specified
10528 if (vp_window->min_width != -1)
10530 int window_width = display_width;
10532 // when using static window height, use aspect ratio of display
10533 if (vp_window->min_height == -1)
10534 window_width = vp_window->height * display_width / display_height;
10536 vp_window->width = MAX(vp_window->min_width, window_width);
10539 if (vp_window->min_height != -1)
10541 int window_height = display_height;
10543 // when using static window width, use aspect ratio of display
10544 if (vp_window->min_width == -1)
10545 window_height = vp_window->width * display_height / display_width;
10547 vp_window->height = MAX(vp_window->min_height, window_height);
10550 if (vp_window->max_width != -1)
10551 vp_window->width = MIN(vp_window->width, vp_window->max_width);
10553 if (vp_window->max_height != -1)
10554 vp_window->height = MIN(vp_window->height, vp_window->max_height);
10556 int playfield_width = vp_window->width;
10557 int playfield_height = vp_window->height;
10559 // adjust playfield size and position according to specified margins
10561 playfield_width -= vp_playfield->margin_left;
10562 playfield_width -= vp_playfield->margin_right;
10564 playfield_height -= vp_playfield->margin_top;
10565 playfield_height -= vp_playfield->margin_bottom;
10567 // adjust playfield size if min/max width/height is specified
10569 if (vp_playfield->min_width != -1)
10570 vp_playfield->width = MAX(vp_playfield->min_width, playfield_width);
10572 if (vp_playfield->min_height != -1)
10573 vp_playfield->height = MAX(vp_playfield->min_height, playfield_height);
10575 if (vp_playfield->max_width != -1)
10576 vp_playfield->width = MIN(vp_playfield->width, vp_playfield->max_width);
10578 if (vp_playfield->max_height != -1)
10579 vp_playfield->height = MIN(vp_playfield->height,vp_playfield->max_height);
10581 // adjust playfield position according to specified alignment
10583 if (vp_playfield->align == ALIGN_LEFT || vp_playfield->x > 0)
10584 vp_playfield->x = ALIGNED_VP_XPOS(vp_playfield);
10585 else if (vp_playfield->align == ALIGN_CENTER)
10586 vp_playfield->x = playfield_width / 2 - vp_playfield->width / 2;
10587 else if (vp_playfield->align == ALIGN_RIGHT)
10588 vp_playfield->x += playfield_width - vp_playfield->width;
10590 if (vp_playfield->valign == VALIGN_TOP || vp_playfield->y > 0)
10591 vp_playfield->y = ALIGNED_VP_YPOS(vp_playfield);
10592 else if (vp_playfield->valign == VALIGN_MIDDLE)
10593 vp_playfield->y = playfield_height / 2 - vp_playfield->height / 2;
10594 else if (vp_playfield->valign == VALIGN_BOTTOM)
10595 vp_playfield->y += playfield_height - vp_playfield->height;
10597 vp_playfield->x += vp_playfield->margin_left;
10598 vp_playfield->y += vp_playfield->margin_top;
10600 // adjust individual playfield borders if only default border is specified
10602 if (vp_playfield->border_left == -1)
10603 vp_playfield->border_left = vp_playfield->border_size;
10604 if (vp_playfield->border_right == -1)
10605 vp_playfield->border_right = vp_playfield->border_size;
10606 if (vp_playfield->border_top == -1)
10607 vp_playfield->border_top = vp_playfield->border_size;
10608 if (vp_playfield->border_bottom == -1)
10609 vp_playfield->border_bottom = vp_playfield->border_size;
10611 // set dynamic playfield borders if borders are specified as undefined
10612 // (but only if window size was dynamic and playfield size was static)
10614 if (dynamic_window_width && !dynamic_playfield_width)
10616 if (vp_playfield->border_left == -1)
10618 vp_playfield->border_left = (vp_playfield->x -
10619 vp_playfield->margin_left);
10620 vp_playfield->x -= vp_playfield->border_left;
10621 vp_playfield->width += vp_playfield->border_left;
10624 if (vp_playfield->border_right == -1)
10626 vp_playfield->border_right = (vp_window->width -
10628 vp_playfield->width -
10629 vp_playfield->margin_right);
10630 vp_playfield->width += vp_playfield->border_right;
10634 if (dynamic_window_height && !dynamic_playfield_height)
10636 if (vp_playfield->border_top == -1)
10638 vp_playfield->border_top = (vp_playfield->y -
10639 vp_playfield->margin_top);
10640 vp_playfield->y -= vp_playfield->border_top;
10641 vp_playfield->height += vp_playfield->border_top;
10644 if (vp_playfield->border_bottom == -1)
10646 vp_playfield->border_bottom = (vp_window->height -
10648 vp_playfield->height -
10649 vp_playfield->margin_bottom);
10650 vp_playfield->height += vp_playfield->border_bottom;
10654 // adjust playfield size to be a multiple of a defined alignment tile size
10656 int align_size = vp_playfield->align_size;
10657 int playfield_xtiles = vp_playfield->width / align_size;
10658 int playfield_ytiles = vp_playfield->height / align_size;
10659 int playfield_width_corrected = playfield_xtiles * align_size;
10660 int playfield_height_corrected = playfield_ytiles * align_size;
10661 boolean is_playfield_mode = (i == GFX_SPECIAL_ARG_PLAYING ||
10662 i == GFX_SPECIAL_ARG_EDITOR);
10664 if (is_playfield_mode &&
10665 dynamic_playfield_width &&
10666 vp_playfield->width != playfield_width_corrected)
10668 int playfield_xdiff = vp_playfield->width - playfield_width_corrected;
10670 vp_playfield->width = playfield_width_corrected;
10672 if (vp_playfield->align == ALIGN_LEFT)
10674 vp_playfield->border_left += playfield_xdiff;
10676 else if (vp_playfield->align == ALIGN_RIGHT)
10678 vp_playfield->border_right += playfield_xdiff;
10680 else if (vp_playfield->align == ALIGN_CENTER)
10682 int border_left_diff = playfield_xdiff / 2;
10683 int border_right_diff = playfield_xdiff - border_left_diff;
10685 vp_playfield->border_left += border_left_diff;
10686 vp_playfield->border_right += border_right_diff;
10690 if (is_playfield_mode &&
10691 dynamic_playfield_height &&
10692 vp_playfield->height != playfield_height_corrected)
10694 int playfield_ydiff = vp_playfield->height - playfield_height_corrected;
10696 vp_playfield->height = playfield_height_corrected;
10698 if (vp_playfield->valign == VALIGN_TOP)
10700 vp_playfield->border_top += playfield_ydiff;
10702 else if (vp_playfield->align == VALIGN_BOTTOM)
10704 vp_playfield->border_right += playfield_ydiff;
10706 else if (vp_playfield->align == VALIGN_MIDDLE)
10708 int border_top_diff = playfield_ydiff / 2;
10709 int border_bottom_diff = playfield_ydiff - border_top_diff;
10711 vp_playfield->border_top += border_top_diff;
10712 vp_playfield->border_bottom += border_bottom_diff;
10716 // adjust door positions according to specified alignment
10718 for (j = 0; j < 2; j++)
10720 struct RectWithBorder *vp_door = (j == 0 ? vp_door_1 : vp_door_2);
10722 if (vp_door->align == ALIGN_LEFT || vp_door->x > 0)
10723 vp_door->x = ALIGNED_VP_XPOS(vp_door);
10724 else if (vp_door->align == ALIGN_CENTER)
10725 vp_door->x = vp_window->width / 2 - vp_door->width / 2;
10726 else if (vp_door->align == ALIGN_RIGHT)
10727 vp_door->x += vp_window->width - vp_door->width;
10729 if (vp_door->valign == VALIGN_TOP || vp_door->y > 0)
10730 vp_door->y = ALIGNED_VP_YPOS(vp_door);
10731 else if (vp_door->valign == VALIGN_MIDDLE)
10732 vp_door->y = vp_window->height / 2 - vp_door->height / 2;
10733 else if (vp_door->valign == VALIGN_BOTTOM)
10734 vp_door->y += vp_window->height - vp_door->height;
10739 static void InitMenuDesignSettings_SpecialPostProcessing_AfterGraphics(void)
10743 struct XYTileSize *dst, *src;
10746 editor_buttons_xy[] =
10749 &editor.button.element_left, &editor.palette.element_left,
10750 IMG_GFX_EDITOR_BUTTON_ELEMENT_LEFT
10753 &editor.button.element_middle, &editor.palette.element_middle,
10754 IMG_GFX_EDITOR_BUTTON_ELEMENT_MIDDLE
10757 &editor.button.element_right, &editor.palette.element_right,
10758 IMG_GFX_EDITOR_BUTTON_ELEMENT_RIGHT
10765 // set default position for element buttons to element graphics
10766 for (i = 0; editor_buttons_xy[i].dst != NULL; i++)
10768 if ((*editor_buttons_xy[i].dst).x == -1 &&
10769 (*editor_buttons_xy[i].dst).y == -1)
10771 struct GraphicInfo *gd = &graphic_info[editor_buttons_xy[i].graphic];
10773 gd->width = gd->height = editor_buttons_xy[i].src->tile_size;
10775 *editor_buttons_xy[i].dst = *editor_buttons_xy[i].src;
10779 // adjust editor palette rows and columns if specified to be dynamic
10781 if (editor.palette.cols == -1)
10783 int vp_width = viewport.playfield[GFX_SPECIAL_ARG_EDITOR].width;
10784 int bt_width = graphic_info[IMG_EDITOR_PALETTE_BUTTON].width;
10785 int sc_width = graphic_info[IMG_EDITOR_PALETTE_SCROLLBAR].width;
10787 editor.palette.cols = (vp_width - sc_width) / bt_width;
10789 if (editor.palette.x == -1)
10791 int palette_width = editor.palette.cols * bt_width + sc_width;
10793 editor.palette.x = (vp_width - palette_width) / 2;
10797 if (editor.palette.rows == -1)
10799 int vp_height = viewport.playfield[GFX_SPECIAL_ARG_EDITOR].height;
10800 int bt_height = graphic_info[IMG_EDITOR_PALETTE_BUTTON].height;
10801 int tx_height = getFontHeight(FONT_TEXT_2);
10803 editor.palette.rows = (vp_height - tx_height) / bt_height;
10805 if (editor.palette.y == -1)
10807 int palette_height = editor.palette.rows * bt_height + tx_height;
10809 editor.palette.y = (vp_height - palette_height) / 2;
10814 static void LoadMenuDesignSettingsFromFilename(char *filename)
10816 static struct TitleFadingInfo tfi;
10817 static struct TitleMessageInfo tmi;
10818 static struct TokenInfo title_tokens[] =
10820 { TYPE_INTEGER, &tfi.fade_mode, ".fade_mode" },
10821 { TYPE_INTEGER, &tfi.fade_delay, ".fade_delay" },
10822 { TYPE_INTEGER, &tfi.post_delay, ".post_delay" },
10823 { TYPE_INTEGER, &tfi.auto_delay, ".auto_delay" },
10824 { TYPE_INTEGER, &tfi.auto_delay_unit, ".auto_delay_unit" },
10828 static struct TokenInfo titlemessage_tokens[] =
10830 { TYPE_INTEGER, &tmi.x, ".x" },
10831 { TYPE_INTEGER, &tmi.y, ".y" },
10832 { TYPE_INTEGER, &tmi.width, ".width" },
10833 { TYPE_INTEGER, &tmi.height, ".height" },
10834 { TYPE_INTEGER, &tmi.chars, ".chars" },
10835 { TYPE_INTEGER, &tmi.lines, ".lines" },
10836 { TYPE_INTEGER, &tmi.align, ".align" },
10837 { TYPE_INTEGER, &tmi.valign, ".valign" },
10838 { TYPE_INTEGER, &tmi.font, ".font" },
10839 { TYPE_BOOLEAN, &tmi.autowrap, ".autowrap" },
10840 { TYPE_BOOLEAN, &tmi.centered, ".centered" },
10841 { TYPE_BOOLEAN, &tmi.parse_comments, ".parse_comments" },
10842 { TYPE_INTEGER, &tmi.sort_priority, ".sort_priority" },
10843 { TYPE_INTEGER, &tmi.fade_mode, ".fade_mode" },
10844 { TYPE_INTEGER, &tmi.fade_delay, ".fade_delay" },
10845 { TYPE_INTEGER, &tmi.post_delay, ".post_delay" },
10846 { TYPE_INTEGER, &tmi.auto_delay, ".auto_delay" },
10847 { TYPE_INTEGER, &tmi.auto_delay_unit, ".auto_delay_unit" },
10853 struct TitleFadingInfo *info;
10858 // initialize first titles from "enter screen" definitions, if defined
10859 { &title_initial_first_default, "menu.enter_screen.TITLE" },
10860 { &title_first_default, "menu.enter_screen.TITLE" },
10862 // initialize title screens from "next screen" definitions, if defined
10863 { &title_initial_default, "menu.next_screen.TITLE" },
10864 { &title_default, "menu.next_screen.TITLE" },
10870 struct TitleMessageInfo *array;
10873 titlemessage_arrays[] =
10875 // initialize first titles from "enter screen" definitions, if defined
10876 { titlescreen_initial_first, "menu.enter_screen.TITLE" },
10877 { titlescreen_first, "menu.enter_screen.TITLE" },
10878 { titlemessage_initial_first, "menu.enter_screen.TITLE" },
10879 { titlemessage_first, "menu.enter_screen.TITLE" },
10881 // initialize titles from "next screen" definitions, if defined
10882 { titlescreen_initial, "menu.next_screen.TITLE" },
10883 { titlescreen, "menu.next_screen.TITLE" },
10884 { titlemessage_initial, "menu.next_screen.TITLE" },
10885 { titlemessage, "menu.next_screen.TITLE" },
10887 // overwrite titles with title definitions, if defined
10888 { titlescreen_initial_first, "[title_initial]" },
10889 { titlescreen_first, "[title]" },
10890 { titlemessage_initial_first, "[title_initial]" },
10891 { titlemessage_first, "[title]" },
10893 { titlescreen_initial, "[title_initial]" },
10894 { titlescreen, "[title]" },
10895 { titlemessage_initial, "[title_initial]" },
10896 { titlemessage, "[title]" },
10898 // overwrite titles with title screen/message definitions, if defined
10899 { titlescreen_initial_first, "[titlescreen_initial]" },
10900 { titlescreen_first, "[titlescreen]" },
10901 { titlemessage_initial_first, "[titlemessage_initial]" },
10902 { titlemessage_first, "[titlemessage]" },
10904 { titlescreen_initial, "[titlescreen_initial]" },
10905 { titlescreen, "[titlescreen]" },
10906 { titlemessage_initial, "[titlemessage_initial]" },
10907 { titlemessage, "[titlemessage]" },
10911 SetupFileHash *setup_file_hash;
10914 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
10917 // the following initializes hierarchical values from dynamic configuration
10919 // special case: initialize with default values that may be overwritten
10920 // (e.g., init "menu.draw_xoffset.INFO" from "menu.draw_xoffset")
10921 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
10923 struct TokenIntPtrInfo menu_config[] =
10925 { "menu.draw_xoffset", &menu.draw_xoffset[i] },
10926 { "menu.draw_yoffset", &menu.draw_yoffset[i] },
10927 { "menu.list_size", &menu.list_size[i] }
10930 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
10932 char *token = menu_config[j].token;
10933 char *value = getHashEntry(setup_file_hash, token);
10936 *menu_config[j].value = get_integer_from_string(value);
10940 // special case: initialize with default values that may be overwritten
10941 // (eg, init "menu.draw_xoffset.INFO[XXX]" from "menu.draw_xoffset.INFO")
10942 for (i = 0; i < NUM_SPECIAL_GFX_INFO_ARGS; i++)
10944 struct TokenIntPtrInfo menu_config[] =
10946 { "menu.draw_xoffset.INFO", &menu.draw_xoffset_info[i] },
10947 { "menu.draw_yoffset.INFO", &menu.draw_yoffset_info[i] },
10948 { "menu.list_size.INFO", &menu.list_size_info[i] }
10951 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
10953 char *token = menu_config[j].token;
10954 char *value = getHashEntry(setup_file_hash, token);
10957 *menu_config[j].value = get_integer_from_string(value);
10961 // special case: initialize with default values that may be overwritten
10962 // (eg, init "menu.draw_xoffset.SETUP[XXX]" from "menu.draw_xoffset.SETUP")
10963 for (i = 0; i < NUM_SPECIAL_GFX_SETUP_ARGS; i++)
10965 struct TokenIntPtrInfo menu_config[] =
10967 { "menu.draw_xoffset.SETUP", &menu.draw_xoffset_setup[i] },
10968 { "menu.draw_yoffset.SETUP", &menu.draw_yoffset_setup[i] }
10971 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
10973 char *token = menu_config[j].token;
10974 char *value = getHashEntry(setup_file_hash, token);
10977 *menu_config[j].value = get_integer_from_string(value);
10981 // special case: initialize with default values that may be overwritten
10982 // (eg, init "menu.line_spacing.INFO[XXX]" from "menu.line_spacing.INFO")
10983 for (i = 0; i < NUM_SPECIAL_GFX_INFO_ARGS; i++)
10985 struct TokenIntPtrInfo menu_config[] =
10987 { "menu.left_spacing.INFO", &menu.left_spacing_info[i] },
10988 { "menu.right_spacing.INFO", &menu.right_spacing_info[i] },
10989 { "menu.top_spacing.INFO", &menu.top_spacing_info[i] },
10990 { "menu.bottom_spacing.INFO", &menu.bottom_spacing_info[i] },
10991 { "menu.paragraph_spacing.INFO", &menu.paragraph_spacing_info[i] },
10992 { "menu.headline1_spacing.INFO", &menu.headline1_spacing_info[i] },
10993 { "menu.headline2_spacing.INFO", &menu.headline2_spacing_info[i] },
10994 { "menu.line_spacing.INFO", &menu.line_spacing_info[i] },
10995 { "menu.extra_spacing.INFO", &menu.extra_spacing_info[i] },
10998 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
11000 char *token = menu_config[j].token;
11001 char *value = getHashEntry(setup_file_hash, token);
11004 *menu_config[j].value = get_integer_from_string(value);
11008 // special case: initialize with default values that may be overwritten
11009 // (eg, init "menu.enter_screen.SCORES.xyz" from "menu.enter_screen.xyz")
11010 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
11012 struct TokenIntPtrInfo menu_config[] =
11014 { "menu.enter_screen.fade_mode", &menu.enter_screen[i].fade_mode },
11015 { "menu.enter_screen.fade_delay", &menu.enter_screen[i].fade_delay },
11016 { "menu.enter_screen.post_delay", &menu.enter_screen[i].post_delay },
11017 { "menu.leave_screen.fade_mode", &menu.leave_screen[i].fade_mode },
11018 { "menu.leave_screen.fade_delay", &menu.leave_screen[i].fade_delay },
11019 { "menu.leave_screen.post_delay", &menu.leave_screen[i].post_delay },
11020 { "menu.next_screen.fade_mode", &menu.next_screen[i].fade_mode },
11021 { "menu.next_screen.fade_delay", &menu.next_screen[i].fade_delay },
11022 { "menu.next_screen.post_delay", &menu.next_screen[i].post_delay }
11025 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
11027 char *token = menu_config[j].token;
11028 char *value = getHashEntry(setup_file_hash, token);
11031 *menu_config[j].value = get_token_parameter_value(token, value);
11035 // special case: initialize with default values that may be overwritten
11036 // (eg, init "viewport.door_1.MAIN.xyz" from "viewport.door_1.xyz")
11037 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
11041 char *token_prefix;
11042 struct RectWithBorder *struct_ptr;
11046 { "viewport.window", &viewport.window[i] },
11047 { "viewport.playfield", &viewport.playfield[i] },
11048 { "viewport.door_1", &viewport.door_1[i] },
11049 { "viewport.door_2", &viewport.door_2[i] }
11052 for (j = 0; j < ARRAY_SIZE(vp_struct); j++)
11054 struct TokenIntPtrInfo vp_config[] =
11056 { ".x", &vp_struct[j].struct_ptr->x },
11057 { ".y", &vp_struct[j].struct_ptr->y },
11058 { ".width", &vp_struct[j].struct_ptr->width },
11059 { ".height", &vp_struct[j].struct_ptr->height },
11060 { ".min_width", &vp_struct[j].struct_ptr->min_width },
11061 { ".min_height", &vp_struct[j].struct_ptr->min_height },
11062 { ".max_width", &vp_struct[j].struct_ptr->max_width },
11063 { ".max_height", &vp_struct[j].struct_ptr->max_height },
11064 { ".margin_left", &vp_struct[j].struct_ptr->margin_left },
11065 { ".margin_right", &vp_struct[j].struct_ptr->margin_right },
11066 { ".margin_top", &vp_struct[j].struct_ptr->margin_top },
11067 { ".margin_bottom", &vp_struct[j].struct_ptr->margin_bottom },
11068 { ".border_left", &vp_struct[j].struct_ptr->border_left },
11069 { ".border_right", &vp_struct[j].struct_ptr->border_right },
11070 { ".border_top", &vp_struct[j].struct_ptr->border_top },
11071 { ".border_bottom", &vp_struct[j].struct_ptr->border_bottom },
11072 { ".border_size", &vp_struct[j].struct_ptr->border_size },
11073 { ".align_size", &vp_struct[j].struct_ptr->align_size },
11074 { ".align", &vp_struct[j].struct_ptr->align },
11075 { ".valign", &vp_struct[j].struct_ptr->valign }
11078 for (k = 0; k < ARRAY_SIZE(vp_config); k++)
11080 char *token = getStringCat2(vp_struct[j].token_prefix,
11081 vp_config[k].token);
11082 char *value = getHashEntry(setup_file_hash, token);
11085 *vp_config[k].value = get_token_parameter_value(token, value);
11092 // special case: initialize with default values that may be overwritten
11093 // (e.g., init "[title].fade_mode" from "menu.next_screen.TITLE.fade_mode")
11094 for (i = 0; title_info[i].info != NULL; i++)
11096 struct TitleFadingInfo *info = title_info[i].info;
11097 char *base_token = title_info[i].text;
11099 for (j = 0; title_tokens[j].type != -1; j++)
11101 char *token = getStringCat2(base_token, title_tokens[j].text);
11102 char *value = getHashEntry(setup_file_hash, token);
11106 int parameter_value = get_token_parameter_value(token, value);
11110 *(int *)title_tokens[j].value = (int)parameter_value;
11119 // special case: initialize with default values that may be overwritten
11120 // (e.g., init "titlemessage_1.fade_mode" from "[titlemessage].fade_mode")
11121 for (i = 0; titlemessage_arrays[i].array != NULL; i++)
11123 struct TitleMessageInfo *array = titlemessage_arrays[i].array;
11124 char *base_token = titlemessage_arrays[i].text;
11126 for (j = 0; titlemessage_tokens[j].type != -1; j++)
11128 char *token = getStringCat2(base_token, titlemessage_tokens[j].text);
11129 char *value = getHashEntry(setup_file_hash, token);
11133 int parameter_value = get_token_parameter_value(token, value);
11135 for (k = 0; k < MAX_NUM_TITLE_MESSAGES; k++)
11139 if (titlemessage_tokens[j].type == TYPE_INTEGER)
11140 *(int *)titlemessage_tokens[j].value = (int)parameter_value;
11142 *(boolean *)titlemessage_tokens[j].value = (boolean)parameter_value;
11152 // special case: check if network and preview player positions are redefined,
11153 // to compare this later against the main menu level preview being redefined
11154 struct TokenIntPtrInfo menu_config_players[] =
11156 { "main.network_players.x", &menu.main.network_players.redefined },
11157 { "main.network_players.y", &menu.main.network_players.redefined },
11158 { "main.preview_players.x", &menu.main.preview_players.redefined },
11159 { "main.preview_players.y", &menu.main.preview_players.redefined },
11160 { "preview.x", &preview.redefined },
11161 { "preview.y", &preview.redefined }
11164 for (i = 0; i < ARRAY_SIZE(menu_config_players); i++)
11165 *menu_config_players[i].value = FALSE;
11167 for (i = 0; i < ARRAY_SIZE(menu_config_players); i++)
11168 if (getHashEntry(setup_file_hash, menu_config_players[i].token) != NULL)
11169 *menu_config_players[i].value = TRUE;
11171 // read (and overwrite with) values that may be specified in config file
11172 for (i = 0; image_config_vars[i].token != NULL; i++)
11174 char *value = getHashEntry(setup_file_hash, image_config_vars[i].token);
11176 // (ignore definitions set to "[DEFAULT]" which are already initialized)
11177 if (value != NULL && !strEqual(value, ARG_DEFAULT))
11178 *image_config_vars[i].value =
11179 get_token_parameter_value(image_config_vars[i].token, value);
11182 freeSetupFileHash(setup_file_hash);
11185 void LoadMenuDesignSettings(void)
11187 char *filename_base = UNDEFINED_FILENAME, *filename_local;
11189 InitMenuDesignSettings_Static();
11190 InitMenuDesignSettings_SpecialPreProcessing();
11192 if (!GFX_OVERRIDE_ARTWORK(ARTWORK_TYPE_GRAPHICS))
11194 // first look for special settings configured in level series config
11195 filename_base = getCustomArtworkLevelConfigFilename(ARTWORK_TYPE_GRAPHICS);
11197 if (fileExists(filename_base))
11198 LoadMenuDesignSettingsFromFilename(filename_base);
11201 filename_local = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
11203 if (filename_local != NULL && !strEqual(filename_base, filename_local))
11204 LoadMenuDesignSettingsFromFilename(filename_local);
11206 InitMenuDesignSettings_SpecialPostProcessing();
11209 void LoadMenuDesignSettings_AfterGraphics(void)
11211 InitMenuDesignSettings_SpecialPostProcessing_AfterGraphics();
11214 void LoadUserDefinedEditorElementList(int **elements, int *num_elements)
11216 char *filename = getEditorSetupFilename();
11217 SetupFileList *setup_file_list, *list;
11218 SetupFileHash *element_hash;
11219 int num_unknown_tokens = 0;
11222 if ((setup_file_list = loadSetupFileList(filename)) == NULL)
11225 element_hash = newSetupFileHash();
11227 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
11228 setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
11230 // determined size may be larger than needed (due to unknown elements)
11232 for (list = setup_file_list; list != NULL; list = list->next)
11235 // add space for up to 3 more elements for padding that may be needed
11236 *num_elements += 3;
11238 // free memory for old list of elements, if needed
11239 checked_free(*elements);
11241 // allocate memory for new list of elements
11242 *elements = checked_malloc(*num_elements * sizeof(int));
11245 for (list = setup_file_list; list != NULL; list = list->next)
11247 char *value = getHashEntry(element_hash, list->token);
11249 if (value == NULL) // try to find obsolete token mapping
11251 char *mapped_token = get_mapped_token(list->token);
11253 if (mapped_token != NULL)
11255 value = getHashEntry(element_hash, mapped_token);
11257 free(mapped_token);
11263 (*elements)[(*num_elements)++] = atoi(value);
11267 if (num_unknown_tokens == 0)
11269 Error(ERR_INFO_LINE, "-");
11270 Error(ERR_INFO, "warning: unknown token(s) found in config file:");
11271 Error(ERR_INFO, "- config file: '%s'", filename);
11273 num_unknown_tokens++;
11276 Error(ERR_INFO, "- token: '%s'", list->token);
11280 if (num_unknown_tokens > 0)
11281 Error(ERR_INFO_LINE, "-");
11283 while (*num_elements % 4) // pad with empty elements, if needed
11284 (*elements)[(*num_elements)++] = EL_EMPTY;
11286 freeSetupFileList(setup_file_list);
11287 freeSetupFileHash(element_hash);
11290 for (i = 0; i < *num_elements; i++)
11291 printf("editor: element '%s' [%d]\n",
11292 element_info[(*elements)[i]].token_name, (*elements)[i]);
11296 static struct MusicFileInfo *get_music_file_info_ext(char *basename, int music,
11299 SetupFileHash *setup_file_hash = NULL;
11300 struct MusicFileInfo tmp_music_file_info, *new_music_file_info;
11301 char *filename_music, *filename_prefix, *filename_info;
11307 token_to_value_ptr[] =
11309 { "title_header", &tmp_music_file_info.title_header },
11310 { "artist_header", &tmp_music_file_info.artist_header },
11311 { "album_header", &tmp_music_file_info.album_header },
11312 { "year_header", &tmp_music_file_info.year_header },
11314 { "title", &tmp_music_file_info.title },
11315 { "artist", &tmp_music_file_info.artist },
11316 { "album", &tmp_music_file_info.album },
11317 { "year", &tmp_music_file_info.year },
11323 filename_music = (is_sound ? getCustomSoundFilename(basename) :
11324 getCustomMusicFilename(basename));
11326 if (filename_music == NULL)
11329 // ---------- try to replace file extension ----------
11331 filename_prefix = getStringCopy(filename_music);
11332 if (strrchr(filename_prefix, '.') != NULL)
11333 *strrchr(filename_prefix, '.') = '\0';
11334 filename_info = getStringCat2(filename_prefix, ".txt");
11336 if (fileExists(filename_info))
11337 setup_file_hash = loadSetupFileHash(filename_info);
11339 free(filename_prefix);
11340 free(filename_info);
11342 if (setup_file_hash == NULL)
11344 // ---------- try to add file extension ----------
11346 filename_prefix = getStringCopy(filename_music);
11347 filename_info = getStringCat2(filename_prefix, ".txt");
11349 if (fileExists(filename_info))
11350 setup_file_hash = loadSetupFileHash(filename_info);
11352 free(filename_prefix);
11353 free(filename_info);
11356 if (setup_file_hash == NULL)
11359 // ---------- music file info found ----------
11361 clear_mem(&tmp_music_file_info, sizeof(struct MusicFileInfo));
11363 for (i = 0; token_to_value_ptr[i].token != NULL; i++)
11365 char *value = getHashEntry(setup_file_hash, token_to_value_ptr[i].token);
11367 *token_to_value_ptr[i].value_ptr =
11368 getStringCopy(value != NULL && *value != '\0' ? value : UNKNOWN_NAME);
11371 tmp_music_file_info.basename = getStringCopy(basename);
11372 tmp_music_file_info.music = music;
11373 tmp_music_file_info.is_sound = is_sound;
11375 new_music_file_info = checked_malloc(sizeof(struct MusicFileInfo));
11376 *new_music_file_info = tmp_music_file_info;
11378 return new_music_file_info;
11381 static struct MusicFileInfo *get_music_file_info(char *basename, int music)
11383 return get_music_file_info_ext(basename, music, FALSE);
11386 static struct MusicFileInfo *get_sound_file_info(char *basename, int sound)
11388 return get_music_file_info_ext(basename, sound, TRUE);
11391 static boolean music_info_listed_ext(struct MusicFileInfo *list,
11392 char *basename, boolean is_sound)
11394 for (; list != NULL; list = list->next)
11395 if (list->is_sound == is_sound && strEqual(list->basename, basename))
11401 static boolean music_info_listed(struct MusicFileInfo *list, char *basename)
11403 return music_info_listed_ext(list, basename, FALSE);
11406 static boolean sound_info_listed(struct MusicFileInfo *list, char *basename)
11408 return music_info_listed_ext(list, basename, TRUE);
11411 void LoadMusicInfo(void)
11413 char *music_directory = getCustomMusicDirectory();
11414 int num_music = getMusicListSize();
11415 int num_music_noconf = 0;
11416 int num_sounds = getSoundListSize();
11418 DirectoryEntry *dir_entry;
11419 struct FileInfo *music, *sound;
11420 struct MusicFileInfo *next, **new;
11423 while (music_file_info != NULL)
11425 next = music_file_info->next;
11427 checked_free(music_file_info->basename);
11429 checked_free(music_file_info->title_header);
11430 checked_free(music_file_info->artist_header);
11431 checked_free(music_file_info->album_header);
11432 checked_free(music_file_info->year_header);
11434 checked_free(music_file_info->title);
11435 checked_free(music_file_info->artist);
11436 checked_free(music_file_info->album);
11437 checked_free(music_file_info->year);
11439 free(music_file_info);
11441 music_file_info = next;
11444 new = &music_file_info;
11446 for (i = 0; i < num_music; i++)
11448 music = getMusicListEntry(i);
11450 if (music->filename == NULL)
11453 if (strEqual(music->filename, UNDEFINED_FILENAME))
11456 // a configured file may be not recognized as music
11457 if (!FileIsMusic(music->filename))
11460 if (!music_info_listed(music_file_info, music->filename))
11462 *new = get_music_file_info(music->filename, i);
11465 new = &(*new)->next;
11469 if ((dir = openDirectory(music_directory)) == NULL)
11471 Error(ERR_WARN, "cannot read music directory '%s'", music_directory);
11475 while ((dir_entry = readDirectory(dir)) != NULL) // loop all entries
11477 char *basename = dir_entry->basename;
11478 boolean music_already_used = FALSE;
11481 // skip all music files that are configured in music config file
11482 for (i = 0; i < num_music; i++)
11484 music = getMusicListEntry(i);
11486 if (music->filename == NULL)
11489 if (strEqual(basename, music->filename))
11491 music_already_used = TRUE;
11496 if (music_already_used)
11499 if (!FileIsMusic(dir_entry->filename))
11502 if (!music_info_listed(music_file_info, basename))
11504 *new = get_music_file_info(basename, MAP_NOCONF_MUSIC(num_music_noconf));
11507 new = &(*new)->next;
11510 num_music_noconf++;
11513 closeDirectory(dir);
11515 for (i = 0; i < num_sounds; i++)
11517 sound = getSoundListEntry(i);
11519 if (sound->filename == NULL)
11522 if (strEqual(sound->filename, UNDEFINED_FILENAME))
11525 // a configured file may be not recognized as sound
11526 if (!FileIsSound(sound->filename))
11529 if (!sound_info_listed(music_file_info, sound->filename))
11531 *new = get_sound_file_info(sound->filename, i);
11533 new = &(*new)->next;
11538 static void add_helpanim_entry(int element, int action, int direction,
11539 int delay, int *num_list_entries)
11541 struct HelpAnimInfo *new_list_entry;
11542 (*num_list_entries)++;
11545 checked_realloc(helpanim_info,
11546 *num_list_entries * sizeof(struct HelpAnimInfo));
11547 new_list_entry = &helpanim_info[*num_list_entries - 1];
11549 new_list_entry->element = element;
11550 new_list_entry->action = action;
11551 new_list_entry->direction = direction;
11552 new_list_entry->delay = delay;
11555 static void print_unknown_token(char *filename, char *token, int token_nr)
11559 Error(ERR_INFO_LINE, "-");
11560 Error(ERR_INFO, "warning: unknown token(s) found in config file:");
11561 Error(ERR_INFO, "- config file: '%s'", filename);
11564 Error(ERR_INFO, "- token: '%s'", token);
11567 static void print_unknown_token_end(int token_nr)
11570 Error(ERR_INFO_LINE, "-");
11573 void LoadHelpAnimInfo(void)
11575 char *filename = getHelpAnimFilename();
11576 SetupFileList *setup_file_list = NULL, *list;
11577 SetupFileHash *element_hash, *action_hash, *direction_hash;
11578 int num_list_entries = 0;
11579 int num_unknown_tokens = 0;
11582 if (fileExists(filename))
11583 setup_file_list = loadSetupFileList(filename);
11585 if (setup_file_list == NULL)
11587 // use reliable default values from static configuration
11588 SetupFileList *insert_ptr;
11590 insert_ptr = setup_file_list =
11591 newSetupFileList(helpanim_config[0].token,
11592 helpanim_config[0].value);
11594 for (i = 1; helpanim_config[i].token; i++)
11595 insert_ptr = addListEntry(insert_ptr,
11596 helpanim_config[i].token,
11597 helpanim_config[i].value);
11600 element_hash = newSetupFileHash();
11601 action_hash = newSetupFileHash();
11602 direction_hash = newSetupFileHash();
11604 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
11605 setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
11607 for (i = 0; i < NUM_ACTIONS; i++)
11608 setHashEntry(action_hash, element_action_info[i].suffix,
11609 i_to_a(element_action_info[i].value));
11611 // do not store direction index (bit) here, but direction value!
11612 for (i = 0; i < NUM_DIRECTIONS_FULL; i++)
11613 setHashEntry(direction_hash, element_direction_info[i].suffix,
11614 i_to_a(1 << element_direction_info[i].value));
11616 for (list = setup_file_list; list != NULL; list = list->next)
11618 char *element_token, *action_token, *direction_token;
11619 char *element_value, *action_value, *direction_value;
11620 int delay = atoi(list->value);
11622 if (strEqual(list->token, "end"))
11624 add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
11629 /* first try to break element into element/action/direction parts;
11630 if this does not work, also accept combined "element[.act][.dir]"
11631 elements (like "dynamite.active"), which are unique elements */
11633 if (strchr(list->token, '.') == NULL) // token contains no '.'
11635 element_value = getHashEntry(element_hash, list->token);
11636 if (element_value != NULL) // element found
11637 add_helpanim_entry(atoi(element_value), -1, -1, delay,
11638 &num_list_entries);
11641 // no further suffixes found -- this is not an element
11642 print_unknown_token(filename, list->token, num_unknown_tokens++);
11648 // token has format "<prefix>.<something>"
11650 action_token = strchr(list->token, '.'); // suffix may be action ...
11651 direction_token = action_token; // ... or direction
11653 element_token = getStringCopy(list->token);
11654 *strchr(element_token, '.') = '\0';
11656 element_value = getHashEntry(element_hash, element_token);
11658 if (element_value == NULL) // this is no element
11660 element_value = getHashEntry(element_hash, list->token);
11661 if (element_value != NULL) // combined element found
11662 add_helpanim_entry(atoi(element_value), -1, -1, delay,
11663 &num_list_entries);
11665 print_unknown_token(filename, list->token, num_unknown_tokens++);
11667 free(element_token);
11672 action_value = getHashEntry(action_hash, action_token);
11674 if (action_value != NULL) // action found
11676 add_helpanim_entry(atoi(element_value), atoi(action_value), -1, delay,
11677 &num_list_entries);
11679 free(element_token);
11684 direction_value = getHashEntry(direction_hash, direction_token);
11686 if (direction_value != NULL) // direction found
11688 add_helpanim_entry(atoi(element_value), -1, atoi(direction_value), delay,
11689 &num_list_entries);
11691 free(element_token);
11696 if (strchr(action_token + 1, '.') == NULL)
11698 // no further suffixes found -- this is not an action nor direction
11700 element_value = getHashEntry(element_hash, list->token);
11701 if (element_value != NULL) // combined element found
11702 add_helpanim_entry(atoi(element_value), -1, -1, delay,
11703 &num_list_entries);
11705 print_unknown_token(filename, list->token, num_unknown_tokens++);
11707 free(element_token);
11712 // token has format "<prefix>.<suffix>.<something>"
11714 direction_token = strchr(action_token + 1, '.');
11716 action_token = getStringCopy(action_token);
11717 *strchr(action_token + 1, '.') = '\0';
11719 action_value = getHashEntry(action_hash, action_token);
11721 if (action_value == NULL) // this is no action
11723 element_value = getHashEntry(element_hash, list->token);
11724 if (element_value != NULL) // combined element found
11725 add_helpanim_entry(atoi(element_value), -1, -1, delay,
11726 &num_list_entries);
11728 print_unknown_token(filename, list->token, num_unknown_tokens++);
11730 free(element_token);
11731 free(action_token);
11736 direction_value = getHashEntry(direction_hash, direction_token);
11738 if (direction_value != NULL) // direction found
11740 add_helpanim_entry(atoi(element_value), atoi(action_value),
11741 atoi(direction_value), delay, &num_list_entries);
11743 free(element_token);
11744 free(action_token);
11749 // this is no direction
11751 element_value = getHashEntry(element_hash, list->token);
11752 if (element_value != NULL) // combined element found
11753 add_helpanim_entry(atoi(element_value), -1, -1, delay,
11754 &num_list_entries);
11756 print_unknown_token(filename, list->token, num_unknown_tokens++);
11758 free(element_token);
11759 free(action_token);
11762 print_unknown_token_end(num_unknown_tokens);
11764 add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
11765 add_helpanim_entry(HELPANIM_LIST_END, -1, -1, -1, &num_list_entries);
11767 freeSetupFileList(setup_file_list);
11768 freeSetupFileHash(element_hash);
11769 freeSetupFileHash(action_hash);
11770 freeSetupFileHash(direction_hash);
11773 for (i = 0; i < num_list_entries; i++)
11774 printf("::: '%s': %d, %d, %d => %d\n",
11775 EL_NAME(helpanim_info[i].element),
11776 helpanim_info[i].element,
11777 helpanim_info[i].action,
11778 helpanim_info[i].direction,
11779 helpanim_info[i].delay);
11783 void LoadHelpTextInfo(void)
11785 char *filename = getHelpTextFilename();
11788 if (helptext_info != NULL)
11790 freeSetupFileHash(helptext_info);
11791 helptext_info = NULL;
11794 if (fileExists(filename))
11795 helptext_info = loadSetupFileHash(filename);
11797 if (helptext_info == NULL)
11799 // use reliable default values from static configuration
11800 helptext_info = newSetupFileHash();
11802 for (i = 0; helptext_config[i].token; i++)
11803 setHashEntry(helptext_info,
11804 helptext_config[i].token,
11805 helptext_config[i].value);
11809 BEGIN_HASH_ITERATION(helptext_info, itr)
11811 printf("::: '%s' => '%s'\n",
11812 HASH_ITERATION_TOKEN(itr), HASH_ITERATION_VALUE(itr));
11814 END_HASH_ITERATION(hash, itr)
11819 // ----------------------------------------------------------------------------
11821 // ----------------------------------------------------------------------------
11823 #define MAX_NUM_CONVERT_LEVELS 1000
11825 void ConvertLevels(void)
11827 static LevelDirTree *convert_leveldir = NULL;
11828 static int convert_level_nr = -1;
11829 static int num_levels_handled = 0;
11830 static int num_levels_converted = 0;
11831 static boolean levels_failed[MAX_NUM_CONVERT_LEVELS];
11834 convert_leveldir = getTreeInfoFromIdentifier(leveldir_first,
11835 global.convert_leveldir);
11837 if (convert_leveldir == NULL)
11838 Error(ERR_EXIT, "no such level identifier: '%s'",
11839 global.convert_leveldir);
11841 leveldir_current = convert_leveldir;
11843 if (global.convert_level_nr != -1)
11845 convert_leveldir->first_level = global.convert_level_nr;
11846 convert_leveldir->last_level = global.convert_level_nr;
11849 convert_level_nr = convert_leveldir->first_level;
11851 PrintLine("=", 79);
11852 Print("Converting levels\n");
11853 PrintLine("-", 79);
11854 Print("Level series identifier: '%s'\n", convert_leveldir->identifier);
11855 Print("Level series name: '%s'\n", convert_leveldir->name);
11856 Print("Level series author: '%s'\n", convert_leveldir->author);
11857 Print("Number of levels: %d\n", convert_leveldir->levels);
11858 PrintLine("=", 79);
11861 for (i = 0; i < MAX_NUM_CONVERT_LEVELS; i++)
11862 levels_failed[i] = FALSE;
11864 while (convert_level_nr <= convert_leveldir->last_level)
11866 char *level_filename;
11869 level_nr = convert_level_nr++;
11871 Print("Level %03d: ", level_nr);
11873 LoadLevel(level_nr);
11874 if (level.no_level_file || level.no_valid_file)
11876 Print("(no level)\n");
11880 Print("converting level ... ");
11882 level_filename = getDefaultLevelFilename(level_nr);
11883 new_level = !fileExists(level_filename);
11887 SaveLevel(level_nr);
11889 num_levels_converted++;
11891 Print("converted.\n");
11895 if (level_nr >= 0 && level_nr < MAX_NUM_CONVERT_LEVELS)
11896 levels_failed[level_nr] = TRUE;
11898 Print("NOT CONVERTED -- LEVEL ALREADY EXISTS.\n");
11901 num_levels_handled++;
11905 PrintLine("=", 79);
11906 Print("Number of levels handled: %d\n", num_levels_handled);
11907 Print("Number of levels converted: %d (%d%%)\n", num_levels_converted,
11908 (num_levels_handled ?
11909 num_levels_converted * 100 / num_levels_handled : 0));
11910 PrintLine("-", 79);
11911 Print("Summary (for automatic parsing by scripts):\n");
11912 Print("LEVELDIR '%s', CONVERTED %d/%d (%d%%)",
11913 convert_leveldir->identifier, num_levels_converted,
11914 num_levels_handled,
11915 (num_levels_handled ?
11916 num_levels_converted * 100 / num_levels_handled : 0));
11918 if (num_levels_handled != num_levels_converted)
11920 Print(", FAILED:");
11921 for (i = 0; i < MAX_NUM_CONVERT_LEVELS; i++)
11922 if (levels_failed[i])
11927 PrintLine("=", 79);
11929 CloseAllAndExit(0);
11933 // ----------------------------------------------------------------------------
11934 // create and save images for use in level sketches (raw BMP format)
11935 // ----------------------------------------------------------------------------
11937 void CreateLevelSketchImages(void)
11943 InitElementPropertiesGfxElement();
11945 bitmap1 = CreateBitmap(TILEX, TILEY, DEFAULT_DEPTH);
11946 bitmap2 = CreateBitmap(MINI_TILEX, MINI_TILEY, DEFAULT_DEPTH);
11948 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
11950 int element = getMappedElement(i);
11951 char basename1[16];
11952 char basename2[16];
11956 sprintf(basename1, "%04d.bmp", i);
11957 sprintf(basename2, "%04ds.bmp", i);
11959 filename1 = getPath2(global.create_images_dir, basename1);
11960 filename2 = getPath2(global.create_images_dir, basename2);
11962 DrawSizedElement(0, 0, element, TILESIZE);
11963 BlitBitmap(drawto, bitmap1, SX, SY, TILEX, TILEY, 0, 0);
11965 if (SDL_SaveBMP(bitmap1->surface, filename1) != 0)
11966 Error(ERR_EXIT, "cannot save level sketch image file '%s'", filename1);
11968 DrawSizedElement(0, 0, element, MINI_TILESIZE);
11969 BlitBitmap(drawto, bitmap2, SX, SY, MINI_TILEX, MINI_TILEY, 0, 0);
11971 if (SDL_SaveBMP(bitmap2->surface, filename2) != 0)
11972 Error(ERR_EXIT, "cannot save level sketch image file '%s'", filename2);
11977 // create corresponding SQL statements (for normal and small images)
11980 printf("insert into phpbb_words values (NULL, '`%03d', '<IMG class=\"levelsketch\" src=\"/I/%04d.png\"/>');\n", i, i);
11981 printf("insert into phpbb_words values (NULL, '¸%03d', '<IMG class=\"levelsketch\" src=\"/I/%04ds.png\"/>');\n", i, i);
11984 printf("insert into phpbb_words values (NULL, '`%04d', '<IMG class=\"levelsketch\" src=\"/I/%04d.png\"/>');\n", i, i);
11985 printf("insert into phpbb_words values (NULL, '¸%04d', '<IMG class=\"levelsketch\" src=\"/I/%04ds.png\"/>');\n", i, i);
11987 // optional: create content for forum level sketch demonstration post
11989 fprintf(stderr, "%03d `%03d%c", i, i, (i % 10 < 9 ? ' ' : '\n'));
11992 FreeBitmap(bitmap1);
11993 FreeBitmap(bitmap2);
11996 fprintf(stderr, "\n");
11998 Error(ERR_INFO, "%d normal and small images created", NUM_FILE_ELEMENTS);
12000 CloseAllAndExit(0);
12004 // ----------------------------------------------------------------------------
12005 // create and save images for custom and group elements (raw BMP format)
12006 // ----------------------------------------------------------------------------
12008 void CreateCustomElementImages(char *directory)
12010 char *src_basename = "RocksCE-template.ilbm";
12011 char *dst_basename = "RocksCE.bmp";
12012 char *src_filename = getPath2(directory, src_basename);
12013 char *dst_filename = getPath2(directory, dst_basename);
12014 Bitmap *src_bitmap;
12016 int yoffset_ce = 0;
12017 int yoffset_ge = (TILEY * NUM_CUSTOM_ELEMENTS / 16);
12020 InitVideoDefaults();
12022 ReCreateBitmap(&backbuffer, video.width, video.height);
12024 src_bitmap = LoadImage(src_filename);
12026 bitmap = CreateBitmap(TILEX * 16 * 2,
12027 TILEY * (NUM_CUSTOM_ELEMENTS + NUM_GROUP_ELEMENTS) / 16,
12030 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
12037 BlitBitmap(src_bitmap, bitmap, 0, 0, TILEX, TILEY,
12038 TILEX * x, TILEY * y + yoffset_ce);
12040 BlitBitmap(src_bitmap, bitmap, 0, TILEY,
12042 TILEX * x + TILEX * 16,
12043 TILEY * y + yoffset_ce);
12045 for (j = 2; j >= 0; j--)
12049 BlitBitmap(src_bitmap, bitmap,
12050 TILEX + c * 7, 0, 6, 10,
12051 TILEX * x + 6 + j * 7,
12052 TILEY * y + 11 + yoffset_ce);
12054 BlitBitmap(src_bitmap, bitmap,
12055 TILEX + c * 8, TILEY, 6, 10,
12056 TILEX * 16 + TILEX * x + 6 + j * 8,
12057 TILEY * y + 10 + yoffset_ce);
12063 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
12070 BlitBitmap(src_bitmap, bitmap, 0, 0, TILEX, TILEY,
12071 TILEX * x, TILEY * y + yoffset_ge);
12073 BlitBitmap(src_bitmap, bitmap, 0, TILEY,
12075 TILEX * x + TILEX * 16,
12076 TILEY * y + yoffset_ge);
12078 for (j = 1; j >= 0; j--)
12082 BlitBitmap(src_bitmap, bitmap, TILEX + c * 10, 11, 10, 10,
12083 TILEX * x + 6 + j * 10,
12084 TILEY * y + 11 + yoffset_ge);
12086 BlitBitmap(src_bitmap, bitmap,
12087 TILEX + c * 8, TILEY + 12, 6, 10,
12088 TILEX * 16 + TILEX * x + 10 + j * 8,
12089 TILEY * y + 10 + yoffset_ge);
12095 if (SDL_SaveBMP(bitmap->surface, dst_filename) != 0)
12096 Error(ERR_EXIT, "cannot save CE graphics file '%s'", dst_filename);
12098 FreeBitmap(bitmap);
12100 CloseAllAndExit(0);