1 // ============================================================================
2 // Rocks'n'Diamonds - McDuffin Strikes Back!
3 // ----------------------------------------------------------------------------
4 // (c) 1995-2014 by Artsoft Entertainment
7 // https://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 1 // 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
771 EL_EMC_ANDROID, SAVE_CONF_NEVER,
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_OLD
778 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(2),
779 &li.android_clone_element[0], EL_EMPTY, NULL,
780 &li.num_android_clone_elements, 1, MAX_ANDROID_ELEMENTS
785 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
790 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
795 EL_EMC_MAGNIFIER, -1,
796 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
797 &li.magnify_score, 10
800 EL_EMC_MAGNIFIER, -1,
801 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
806 EL_EMC_MAGIC_BALL, -1,
807 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
811 EL_EMC_MAGIC_BALL, -1,
812 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
813 &li.ball_random, FALSE
816 EL_EMC_MAGIC_BALL, -1,
817 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
818 &li.ball_active_initial, FALSE
821 EL_EMC_MAGIC_BALL, -1,
822 TYPE_CONTENT_LIST, CONF_VALUE_BYTES(1),
823 &li.ball_content, EL_EMPTY, NULL,
824 &li.num_ball_contents, 4, MAX_ELEMENT_CONTENTS
828 EL_SOKOBAN_FIELD_EMPTY, -1,
829 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
830 &li.sb_fields_needed, TRUE
834 EL_SOKOBAN_OBJECT, -1,
835 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
836 &li.sb_objects_needed, TRUE
841 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
842 &li.mm_laser_red, FALSE
846 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
847 &li.mm_laser_green, FALSE
851 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
852 &li.mm_laser_blue, TRUE
857 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
858 &li.df_laser_red, TRUE
862 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
863 &li.df_laser_green, TRUE
867 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
868 &li.df_laser_blue, FALSE
872 EL_MM_FUSE_ACTIVE, -1,
873 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
878 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
883 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
887 EL_MM_STEEL_BLOCK, -1,
888 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
889 &li.mm_time_block, 75
893 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
894 &li.score[SC_ELEM_BONUS], 10
897 // ---------- unused values -------------------------------------------------
900 EL_UNKNOWN, SAVE_CONF_NEVER,
901 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
902 &li.score[SC_UNKNOWN_15], 10
912 static struct LevelFileConfigInfo chunk_config_NOTE[] =
916 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
917 &xx_envelope.xsize, MAX_ENVELOPE_XSIZE,
921 TYPE_INTEGER, CONF_VALUE_8_BIT(2),
922 &xx_envelope.ysize, MAX_ENVELOPE_YSIZE,
927 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
928 &xx_envelope.autowrap, FALSE
932 TYPE_BOOLEAN, CONF_VALUE_8_BIT(4),
933 &xx_envelope.centered, FALSE
938 TYPE_STRING, CONF_VALUE_BYTES(1),
939 &xx_envelope.text, -1, NULL,
940 &xx_string_length_unused, -1, MAX_ENVELOPE_TEXT_LEN,
941 &xx_default_string_empty[0]
951 static struct LevelFileConfigInfo chunk_config_CUSX_base[] =
955 TYPE_STRING, CONF_VALUE_BYTES(1),
956 &xx_ei.description[0], -1,
957 &yy_ei.description[0],
958 &xx_string_length_unused, -1, MAX_ELEMENT_NAME_LEN,
959 &xx_default_description[0]
964 TYPE_BITFIELD, CONF_VALUE_32_BIT(1),
965 &xx_ei.properties[EP_BITFIELD_BASE_NR], EP_BITMASK_BASE_DEFAULT,
966 &yy_ei.properties[EP_BITFIELD_BASE_NR]
968 #if ENABLE_RESERVED_CODE
969 // (reserved for later use)
972 TYPE_BITFIELD, CONF_VALUE_32_BIT(2),
973 &xx_ei.properties[EP_BITFIELD_BASE_NR + 1], EP_BITMASK_DEFAULT,
974 &yy_ei.properties[EP_BITFIELD_BASE_NR + 1]
980 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
981 &xx_ei.use_gfx_element, FALSE,
982 &yy_ei.use_gfx_element
986 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
987 &xx_ei.gfx_element_initial, EL_EMPTY_SPACE,
988 &yy_ei.gfx_element_initial
993 TYPE_BITFIELD, CONF_VALUE_8_BIT(2),
994 &xx_ei.access_direction, MV_ALL_DIRECTIONS,
995 &yy_ei.access_direction
1000 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
1001 &xx_ei.collect_score_initial, 10,
1002 &yy_ei.collect_score_initial
1006 TYPE_INTEGER, CONF_VALUE_16_BIT(3),
1007 &xx_ei.collect_count_initial, 1,
1008 &yy_ei.collect_count_initial
1013 TYPE_INTEGER, CONF_VALUE_16_BIT(4),
1014 &xx_ei.ce_value_fixed_initial, 0,
1015 &yy_ei.ce_value_fixed_initial
1019 TYPE_INTEGER, CONF_VALUE_16_BIT(5),
1020 &xx_ei.ce_value_random_initial, 0,
1021 &yy_ei.ce_value_random_initial
1025 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
1026 &xx_ei.use_last_ce_value, FALSE,
1027 &yy_ei.use_last_ce_value
1032 TYPE_INTEGER, CONF_VALUE_16_BIT(6),
1033 &xx_ei.push_delay_fixed, 8,
1034 &yy_ei.push_delay_fixed
1038 TYPE_INTEGER, CONF_VALUE_16_BIT(7),
1039 &xx_ei.push_delay_random, 8,
1040 &yy_ei.push_delay_random
1044 TYPE_INTEGER, CONF_VALUE_16_BIT(8),
1045 &xx_ei.drop_delay_fixed, 0,
1046 &yy_ei.drop_delay_fixed
1050 TYPE_INTEGER, CONF_VALUE_16_BIT(9),
1051 &xx_ei.drop_delay_random, 0,
1052 &yy_ei.drop_delay_random
1056 TYPE_INTEGER, CONF_VALUE_16_BIT(10),
1057 &xx_ei.move_delay_fixed, 0,
1058 &yy_ei.move_delay_fixed
1062 TYPE_INTEGER, CONF_VALUE_16_BIT(11),
1063 &xx_ei.move_delay_random, 0,
1064 &yy_ei.move_delay_random
1069 TYPE_BITFIELD, CONF_VALUE_32_BIT(3),
1070 &xx_ei.move_pattern, MV_ALL_DIRECTIONS,
1075 TYPE_BITFIELD, CONF_VALUE_8_BIT(4),
1076 &xx_ei.move_direction_initial, MV_START_AUTOMATIC,
1077 &yy_ei.move_direction_initial
1081 TYPE_INTEGER, CONF_VALUE_8_BIT(5),
1082 &xx_ei.move_stepsize, TILEX / 8,
1083 &yy_ei.move_stepsize
1088 TYPE_ELEMENT, CONF_VALUE_16_BIT(12),
1089 &xx_ei.move_enter_element, EL_EMPTY_SPACE,
1090 &yy_ei.move_enter_element
1094 TYPE_ELEMENT, CONF_VALUE_16_BIT(13),
1095 &xx_ei.move_leave_element, EL_EMPTY_SPACE,
1096 &yy_ei.move_leave_element
1100 TYPE_INTEGER, CONF_VALUE_8_BIT(6),
1101 &xx_ei.move_leave_type, LEAVE_TYPE_UNLIMITED,
1102 &yy_ei.move_leave_type
1107 TYPE_INTEGER, CONF_VALUE_8_BIT(7),
1108 &xx_ei.slippery_type, SLIPPERY_ANY_RANDOM,
1109 &yy_ei.slippery_type
1114 TYPE_INTEGER, CONF_VALUE_8_BIT(8),
1115 &xx_ei.explosion_type, EXPLODES_3X3,
1116 &yy_ei.explosion_type
1120 TYPE_INTEGER, CONF_VALUE_16_BIT(14),
1121 &xx_ei.explosion_delay, 16,
1122 &yy_ei.explosion_delay
1126 TYPE_INTEGER, CONF_VALUE_16_BIT(15),
1127 &xx_ei.ignition_delay, 8,
1128 &yy_ei.ignition_delay
1133 TYPE_CONTENT_LIST, CONF_VALUE_BYTES(2),
1134 &xx_ei.content, EL_EMPTY_SPACE,
1136 &xx_num_contents, 1, 1
1139 // ---------- "num_change_pages" must be the last entry ---------------------
1142 -1, SAVE_CONF_ALWAYS,
1143 TYPE_INTEGER, CONF_VALUE_8_BIT(9),
1144 &xx_ei.num_change_pages, 1,
1145 &yy_ei.num_change_pages
1156 static struct LevelFileConfigInfo chunk_config_CUSX_change[] =
1158 // ---------- "current_change_page" must be the first entry -----------------
1161 -1, SAVE_CONF_ALWAYS,
1162 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
1163 &xx_current_change_page, -1
1166 // ---------- (the remaining entries can be in any order) -------------------
1170 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
1171 &xx_change.can_change, FALSE
1176 TYPE_BITFIELD, CONF_VALUE_32_BIT(1),
1177 &xx_event_bits[0], 0
1181 TYPE_BITFIELD, CONF_VALUE_32_BIT(2),
1182 &xx_event_bits[1], 0
1187 TYPE_BITFIELD, CONF_VALUE_8_BIT(3),
1188 &xx_change.trigger_player, CH_PLAYER_ANY
1192 TYPE_BITFIELD, CONF_VALUE_8_BIT(4),
1193 &xx_change.trigger_side, CH_SIDE_ANY
1197 TYPE_BITFIELD, CONF_VALUE_32_BIT(3),
1198 &xx_change.trigger_page, CH_PAGE_ANY
1203 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
1204 &xx_change.target_element, EL_EMPTY_SPACE
1209 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
1210 &xx_change.delay_fixed, 0
1214 TYPE_INTEGER, CONF_VALUE_16_BIT(3),
1215 &xx_change.delay_random, 0
1219 TYPE_INTEGER, CONF_VALUE_16_BIT(4),
1220 &xx_change.delay_frames, FRAMES_PER_SECOND
1225 TYPE_ELEMENT, CONF_VALUE_16_BIT(5),
1226 &xx_change.initial_trigger_element, EL_EMPTY_SPACE
1231 TYPE_BOOLEAN, CONF_VALUE_8_BIT(6),
1232 &xx_change.explode, FALSE
1236 TYPE_BOOLEAN, CONF_VALUE_8_BIT(7),
1237 &xx_change.use_target_content, FALSE
1241 TYPE_BOOLEAN, CONF_VALUE_8_BIT(8),
1242 &xx_change.only_if_complete, FALSE
1246 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
1247 &xx_change.use_random_replace, FALSE
1251 TYPE_INTEGER, CONF_VALUE_8_BIT(10),
1252 &xx_change.random_percentage, 100
1256 TYPE_INTEGER, CONF_VALUE_8_BIT(11),
1257 &xx_change.replace_when, CP_WHEN_EMPTY
1262 TYPE_BOOLEAN, CONF_VALUE_8_BIT(12),
1263 &xx_change.has_action, FALSE
1267 TYPE_INTEGER, CONF_VALUE_8_BIT(13),
1268 &xx_change.action_type, CA_NO_ACTION
1272 TYPE_INTEGER, CONF_VALUE_8_BIT(14),
1273 &xx_change.action_mode, CA_MODE_UNDEFINED
1277 TYPE_INTEGER, CONF_VALUE_16_BIT(6),
1278 &xx_change.action_arg, CA_ARG_UNDEFINED
1283 TYPE_ELEMENT, CONF_VALUE_16_BIT(7),
1284 &xx_change.action_element, EL_EMPTY_SPACE
1289 TYPE_CONTENT_LIST, CONF_VALUE_BYTES(1),
1290 &xx_change.target_content, EL_EMPTY_SPACE, NULL,
1291 &xx_num_contents, 1, 1
1301 static struct LevelFileConfigInfo chunk_config_GRPX[] =
1305 TYPE_STRING, CONF_VALUE_BYTES(1),
1306 &xx_ei.description[0], -1, NULL,
1307 &xx_string_length_unused, -1, MAX_ELEMENT_NAME_LEN,
1308 &xx_default_description[0]
1313 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1314 &xx_ei.use_gfx_element, FALSE
1318 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
1319 &xx_ei.gfx_element_initial, EL_EMPTY_SPACE
1324 TYPE_INTEGER, CONF_VALUE_8_BIT(2),
1325 &xx_group.choice_mode, ANIM_RANDOM
1330 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(2),
1331 &xx_group.element[0], EL_EMPTY_SPACE, NULL,
1332 &xx_group.num_elements, 1, MAX_ELEMENTS_IN_GROUP
1342 static struct LevelFileConfigInfo chunk_config_CONF[] = // (OBSOLETE)
1346 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
1347 &li.block_snap_field, TRUE
1351 TYPE_BOOLEAN, CONF_VALUE_8_BIT(13),
1352 &li.continuous_snapping, TRUE
1356 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
1357 &li.initial_player_stepsize[0], STEPSIZE_NORMAL
1361 TYPE_BOOLEAN, CONF_VALUE_8_BIT(10),
1362 &li.use_start_element[0], FALSE
1366 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
1367 &li.start_element[0], EL_PLAYER_1
1371 TYPE_BOOLEAN, CONF_VALUE_8_BIT(11),
1372 &li.use_artwork_element[0], FALSE
1376 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
1377 &li.artwork_element[0], EL_PLAYER_1
1381 TYPE_BOOLEAN, CONF_VALUE_8_BIT(12),
1382 &li.use_explosion_element[0], FALSE
1386 TYPE_ELEMENT, CONF_VALUE_16_BIT(3),
1387 &li.explosion_element[0], EL_PLAYER_1
1402 filetype_id_list[] =
1404 { LEVEL_FILE_TYPE_RND, "RND" },
1405 { LEVEL_FILE_TYPE_BD, "BD" },
1406 { LEVEL_FILE_TYPE_EM, "EM" },
1407 { LEVEL_FILE_TYPE_SP, "SP" },
1408 { LEVEL_FILE_TYPE_DX, "DX" },
1409 { LEVEL_FILE_TYPE_SB, "SB" },
1410 { LEVEL_FILE_TYPE_DC, "DC" },
1411 { LEVEL_FILE_TYPE_MM, "MM" },
1412 { LEVEL_FILE_TYPE_MM, "DF" },
1417 // ============================================================================
1418 // level file functions
1419 // ============================================================================
1421 static boolean check_special_flags(char *flag)
1423 if (strEqual(options.special_flags, flag) ||
1424 strEqual(leveldir_current->special_flags, flag))
1430 static struct DateInfo getCurrentDate(void)
1432 time_t epoch_seconds = time(NULL);
1433 struct tm *now = localtime(&epoch_seconds);
1434 struct DateInfo date;
1436 date.year = now->tm_year + 1900;
1437 date.month = now->tm_mon + 1;
1438 date.day = now->tm_mday;
1440 date.src = DATE_SRC_CLOCK;
1445 static void resetEventFlags(struct ElementChangeInfo *change)
1449 for (i = 0; i < NUM_CHANGE_EVENTS; i++)
1450 change->has_event[i] = FALSE;
1453 static void resetEventBits(void)
1457 for (i = 0; i < NUM_CE_BITFIELDS; i++)
1458 xx_event_bits[i] = 0;
1461 static void setEventFlagsFromEventBits(struct ElementChangeInfo *change)
1465 /* important: only change event flag if corresponding event bit is set
1466 (this is because all xx_event_bits[] values are loaded separately,
1467 and all xx_event_bits[] values are set back to zero before loading
1468 another value xx_event_bits[x] (each value representing 32 flags)) */
1470 for (i = 0; i < NUM_CHANGE_EVENTS; i++)
1471 if (xx_event_bits[CH_EVENT_BITFIELD_NR(i)] & CH_EVENT_BIT(i))
1472 change->has_event[i] = TRUE;
1475 static void setEventBitsFromEventFlags(struct ElementChangeInfo *change)
1479 /* in contrast to the above function setEventFlagsFromEventBits(), it
1480 would also be possible to set all bits in xx_event_bits[] to 0 or 1
1481 depending on the corresponding change->has_event[i] values here, as
1482 all xx_event_bits[] values are reset in resetEventBits() before */
1484 for (i = 0; i < NUM_CHANGE_EVENTS; i++)
1485 if (change->has_event[i])
1486 xx_event_bits[CH_EVENT_BITFIELD_NR(i)] |= CH_EVENT_BIT(i);
1489 static char *getDefaultElementDescription(struct ElementInfo *ei)
1491 static char description[MAX_ELEMENT_NAME_LEN + 1];
1492 char *default_description = (ei->custom_description != NULL ?
1493 ei->custom_description :
1494 ei->editor_description);
1497 // always start with reliable default values
1498 for (i = 0; i < MAX_ELEMENT_NAME_LEN + 1; i++)
1499 description[i] = '\0';
1501 // truncate element description to MAX_ELEMENT_NAME_LEN bytes
1502 strncpy(description, default_description, MAX_ELEMENT_NAME_LEN);
1504 return &description[0];
1507 static void setElementDescriptionToDefault(struct ElementInfo *ei)
1509 char *default_description = getDefaultElementDescription(ei);
1512 for (i = 0; i < MAX_ELEMENT_NAME_LEN + 1; i++)
1513 ei->description[i] = default_description[i];
1516 static void setConfigToDefaultsFromConfigList(struct LevelFileConfigInfo *conf)
1520 for (i = 0; conf[i].data_type != -1; i++)
1522 int default_value = conf[i].default_value;
1523 int data_type = conf[i].data_type;
1524 int conf_type = conf[i].conf_type;
1525 int byte_mask = conf_type & CONF_MASK_BYTES;
1527 if (byte_mask == CONF_MASK_MULTI_BYTES)
1529 int default_num_entities = conf[i].default_num_entities;
1530 int max_num_entities = conf[i].max_num_entities;
1532 *(int *)(conf[i].num_entities) = default_num_entities;
1534 if (data_type == TYPE_STRING)
1536 char *default_string = conf[i].default_string;
1537 char *string = (char *)(conf[i].value);
1539 strncpy(string, default_string, max_num_entities);
1541 else if (data_type == TYPE_ELEMENT_LIST)
1543 int *element_array = (int *)(conf[i].value);
1546 for (j = 0; j < max_num_entities; j++)
1547 element_array[j] = default_value;
1549 else if (data_type == TYPE_CONTENT_LIST)
1551 struct Content *content = (struct Content *)(conf[i].value);
1554 for (c = 0; c < max_num_entities; c++)
1555 for (y = 0; y < 3; y++)
1556 for (x = 0; x < 3; x++)
1557 content[c].e[x][y] = default_value;
1560 else // constant size configuration data (1, 2 or 4 bytes)
1562 if (data_type == TYPE_BOOLEAN)
1563 *(boolean *)(conf[i].value) = default_value;
1565 *(int *) (conf[i].value) = default_value;
1570 static void copyConfigFromConfigList(struct LevelFileConfigInfo *conf)
1574 for (i = 0; conf[i].data_type != -1; i++)
1576 int data_type = conf[i].data_type;
1577 int conf_type = conf[i].conf_type;
1578 int byte_mask = conf_type & CONF_MASK_BYTES;
1580 if (byte_mask == CONF_MASK_MULTI_BYTES)
1582 int max_num_entities = conf[i].max_num_entities;
1584 if (data_type == TYPE_STRING)
1586 char *string = (char *)(conf[i].value);
1587 char *string_copy = (char *)(conf[i].value_copy);
1589 strncpy(string_copy, string, max_num_entities);
1591 else if (data_type == TYPE_ELEMENT_LIST)
1593 int *element_array = (int *)(conf[i].value);
1594 int *element_array_copy = (int *)(conf[i].value_copy);
1597 for (j = 0; j < max_num_entities; j++)
1598 element_array_copy[j] = element_array[j];
1600 else if (data_type == TYPE_CONTENT_LIST)
1602 struct Content *content = (struct Content *)(conf[i].value);
1603 struct Content *content_copy = (struct Content *)(conf[i].value_copy);
1606 for (c = 0; c < max_num_entities; c++)
1607 for (y = 0; y < 3; y++)
1608 for (x = 0; x < 3; x++)
1609 content_copy[c].e[x][y] = content[c].e[x][y];
1612 else // constant size configuration data (1, 2 or 4 bytes)
1614 if (data_type == TYPE_BOOLEAN)
1615 *(boolean *)(conf[i].value_copy) = *(boolean *)(conf[i].value);
1617 *(int *) (conf[i].value_copy) = *(int *) (conf[i].value);
1622 void copyElementInfo(struct ElementInfo *ei_from, struct ElementInfo *ei_to)
1626 xx_ei = *ei_from; // copy element data into temporary buffer
1627 yy_ei = *ei_to; // copy element data into temporary buffer
1629 copyConfigFromConfigList(chunk_config_CUSX_base);
1634 // ---------- reinitialize and copy change pages ----------
1636 ei_to->num_change_pages = ei_from->num_change_pages;
1637 ei_to->current_change_page = ei_from->current_change_page;
1639 setElementChangePages(ei_to, ei_to->num_change_pages);
1641 for (i = 0; i < ei_to->num_change_pages; i++)
1642 ei_to->change_page[i] = ei_from->change_page[i];
1644 // ---------- copy group element info ----------
1645 if (ei_from->group != NULL && ei_to->group != NULL) // group or internal
1646 *ei_to->group = *ei_from->group;
1648 // mark this custom element as modified
1649 ei_to->modified_settings = TRUE;
1652 void setElementChangePages(struct ElementInfo *ei, int change_pages)
1654 int change_page_size = sizeof(struct ElementChangeInfo);
1656 ei->num_change_pages = MAX(1, change_pages);
1659 checked_realloc(ei->change_page, ei->num_change_pages * change_page_size);
1661 if (ei->current_change_page >= ei->num_change_pages)
1662 ei->current_change_page = ei->num_change_pages - 1;
1664 ei->change = &ei->change_page[ei->current_change_page];
1667 void setElementChangeInfoToDefaults(struct ElementChangeInfo *change)
1669 xx_change = *change; // copy change data into temporary buffer
1671 setConfigToDefaultsFromConfigList(chunk_config_CUSX_change);
1673 *change = xx_change;
1675 resetEventFlags(change);
1677 change->direct_action = 0;
1678 change->other_action = 0;
1680 change->pre_change_function = NULL;
1681 change->change_function = NULL;
1682 change->post_change_function = NULL;
1685 static void setLevelInfoToDefaults_Level(struct LevelInfo *level)
1689 li = *level; // copy level data into temporary buffer
1690 setConfigToDefaultsFromConfigList(chunk_config_INFO);
1691 *level = li; // copy temporary buffer back to level data
1693 setLevelInfoToDefaults_EM();
1694 setLevelInfoToDefaults_SP();
1695 setLevelInfoToDefaults_MM();
1697 level->native_em_level = &native_em_level;
1698 level->native_sp_level = &native_sp_level;
1699 level->native_mm_level = &native_mm_level;
1701 level->file_version = FILE_VERSION_ACTUAL;
1702 level->game_version = GAME_VERSION_ACTUAL;
1704 level->creation_date = getCurrentDate();
1706 level->encoding_16bit_field = TRUE;
1707 level->encoding_16bit_yamyam = TRUE;
1708 level->encoding_16bit_amoeba = TRUE;
1710 // clear level name and level author string buffers
1711 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
1712 level->name[i] = '\0';
1713 for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
1714 level->author[i] = '\0';
1716 // set level name and level author to default values
1717 strcpy(level->name, NAMELESS_LEVEL_NAME);
1718 strcpy(level->author, ANONYMOUS_NAME);
1720 // set level playfield to playable default level with player and exit
1721 for (x = 0; x < MAX_LEV_FIELDX; x++)
1722 for (y = 0; y < MAX_LEV_FIELDY; y++)
1723 level->field[x][y] = EL_SAND;
1725 level->field[0][0] = EL_PLAYER_1;
1726 level->field[STD_LEV_FIELDX - 1][STD_LEV_FIELDY - 1] = EL_EXIT_CLOSED;
1728 BorderElement = EL_STEELWALL;
1730 // detect custom elements when loading them
1731 level->file_has_custom_elements = FALSE;
1733 // set all bug compatibility flags to "false" => do not emulate this bug
1734 level->use_action_after_change_bug = FALSE;
1736 if (leveldir_current)
1738 // try to determine better author name than 'anonymous'
1739 if (!strEqual(leveldir_current->author, ANONYMOUS_NAME))
1741 strncpy(level->author, leveldir_current->author, MAX_LEVEL_AUTHOR_LEN);
1742 level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
1746 switch (LEVELCLASS(leveldir_current))
1748 case LEVELCLASS_TUTORIAL:
1749 strcpy(level->author, PROGRAM_AUTHOR_STRING);
1752 case LEVELCLASS_CONTRIB:
1753 strncpy(level->author, leveldir_current->name, MAX_LEVEL_AUTHOR_LEN);
1754 level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
1757 case LEVELCLASS_PRIVATE:
1758 strncpy(level->author, getRealName(), MAX_LEVEL_AUTHOR_LEN);
1759 level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
1763 // keep default value
1770 static void setLevelInfoToDefaults_Elements(struct LevelInfo *level)
1772 static boolean clipboard_elements_initialized = FALSE;
1775 InitElementPropertiesStatic();
1777 li = *level; // copy level data into temporary buffer
1778 setConfigToDefaultsFromConfigList(chunk_config_ELEM);
1779 *level = li; // copy temporary buffer back to level data
1781 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1784 struct ElementInfo *ei = &element_info[element];
1786 // never initialize clipboard elements after the very first time
1787 // (to be able to use clipboard elements between several levels)
1788 if (IS_CLIPBOARD_ELEMENT(element) && clipboard_elements_initialized)
1791 if (IS_ENVELOPE(element))
1793 int envelope_nr = element - EL_ENVELOPE_1;
1795 setConfigToDefaultsFromConfigList(chunk_config_NOTE);
1797 level->envelope[envelope_nr] = xx_envelope;
1800 if (IS_CUSTOM_ELEMENT(element) ||
1801 IS_GROUP_ELEMENT(element) ||
1802 IS_INTERNAL_ELEMENT(element))
1804 xx_ei = *ei; // copy element data into temporary buffer
1806 setConfigToDefaultsFromConfigList(chunk_config_CUSX_base);
1811 setElementChangePages(ei, 1);
1812 setElementChangeInfoToDefaults(ei->change);
1814 if (IS_CUSTOM_ELEMENT(element) ||
1815 IS_GROUP_ELEMENT(element) ||
1816 IS_INTERNAL_ELEMENT(element))
1818 setElementDescriptionToDefault(ei);
1820 ei->modified_settings = FALSE;
1823 if (IS_CUSTOM_ELEMENT(element) ||
1824 IS_INTERNAL_ELEMENT(element))
1826 // internal values used in level editor
1828 ei->access_type = 0;
1829 ei->access_layer = 0;
1830 ei->access_protected = 0;
1831 ei->walk_to_action = 0;
1832 ei->smash_targets = 0;
1835 ei->can_explode_by_fire = FALSE;
1836 ei->can_explode_smashed = FALSE;
1837 ei->can_explode_impact = FALSE;
1839 ei->current_change_page = 0;
1842 if (IS_GROUP_ELEMENT(element) ||
1843 IS_INTERNAL_ELEMENT(element))
1845 struct ElementGroupInfo *group;
1847 // initialize memory for list of elements in group
1848 if (ei->group == NULL)
1849 ei->group = checked_malloc(sizeof(struct ElementGroupInfo));
1853 xx_group = *group; // copy group data into temporary buffer
1855 setConfigToDefaultsFromConfigList(chunk_config_GRPX);
1861 clipboard_elements_initialized = TRUE;
1864 static void setLevelInfoToDefaults(struct LevelInfo *level,
1865 boolean level_info_only,
1866 boolean reset_file_status)
1868 setLevelInfoToDefaults_Level(level);
1870 if (!level_info_only)
1871 setLevelInfoToDefaults_Elements(level);
1873 if (reset_file_status)
1875 level->no_valid_file = FALSE;
1876 level->no_level_file = FALSE;
1879 level->changed = FALSE;
1882 static void setFileInfoToDefaults(struct LevelFileInfo *level_file_info)
1884 level_file_info->nr = 0;
1885 level_file_info->type = LEVEL_FILE_TYPE_UNKNOWN;
1886 level_file_info->packed = FALSE;
1888 setString(&level_file_info->basename, NULL);
1889 setString(&level_file_info->filename, NULL);
1892 int getMappedElement_SB(int, boolean);
1894 static void ActivateLevelTemplate(void)
1898 if (check_special_flags("load_xsb_to_ces"))
1900 // fill smaller playfields with padding "beyond border wall" elements
1901 if (level.fieldx < level_template.fieldx ||
1902 level.fieldy < level_template.fieldy)
1904 short field[level.fieldx][level.fieldy];
1905 int new_fieldx = MAX(level.fieldx, level_template.fieldx);
1906 int new_fieldy = MAX(level.fieldy, level_template.fieldy);
1907 int pos_fieldx = (new_fieldx - level.fieldx) / 2;
1908 int pos_fieldy = (new_fieldy - level.fieldy) / 2;
1910 // copy old playfield (which is smaller than the visible area)
1911 for (y = 0; y < level.fieldy; y++) for (x = 0; x < level.fieldx; x++)
1912 field[x][y] = level.field[x][y];
1914 // fill new, larger playfield with "beyond border wall" elements
1915 for (y = 0; y < new_fieldy; y++) for (x = 0; x < new_fieldx; x++)
1916 level.field[x][y] = getMappedElement_SB('_', TRUE);
1918 // copy the old playfield to the middle of the new playfield
1919 for (y = 0; y < level.fieldy; y++) for (x = 0; x < level.fieldx; x++)
1920 level.field[pos_fieldx + x][pos_fieldy + y] = field[x][y];
1922 level.fieldx = new_fieldx;
1923 level.fieldy = new_fieldy;
1927 // Currently there is no special action needed to activate the template
1928 // data, because 'element_info' property settings overwrite the original
1929 // level data, while all other variables do not change.
1931 // Exception: 'from_level_template' elements in the original level playfield
1932 // are overwritten with the corresponding elements at the same position in
1933 // playfield from the level template.
1935 for (x = 0; x < level.fieldx; x++)
1936 for (y = 0; y < level.fieldy; y++)
1937 if (level.field[x][y] == EL_FROM_LEVEL_TEMPLATE)
1938 level.field[x][y] = level_template.field[x][y];
1940 if (check_special_flags("load_xsb_to_ces"))
1942 struct LevelInfo level_backup = level;
1944 // overwrite all individual level settings from template level settings
1945 level = level_template;
1947 // restore level file info
1948 level.file_info = level_backup.file_info;
1950 // restore playfield size
1951 level.fieldx = level_backup.fieldx;
1952 level.fieldy = level_backup.fieldy;
1954 // restore playfield content
1955 for (x = 0; x < level.fieldx; x++)
1956 for (y = 0; y < level.fieldy; y++)
1957 level.field[x][y] = level_backup.field[x][y];
1959 // restore name and author from individual level
1960 strcpy(level.name, level_backup.name);
1961 strcpy(level.author, level_backup.author);
1963 // restore flag "use_custom_template"
1964 level.use_custom_template = level_backup.use_custom_template;
1968 static char *getLevelFilenameFromBasename(char *basename)
1970 static char *filename = NULL;
1972 checked_free(filename);
1974 filename = getPath2(getCurrentLevelDir(), basename);
1979 static int getFileTypeFromBasename(char *basename)
1981 // !!! ALSO SEE COMMENT IN checkForPackageFromBasename() !!!
1983 static char *filename = NULL;
1984 struct stat file_status;
1986 // ---------- try to determine file type from filename ----------
1988 // check for typical filename of a Supaplex level package file
1989 if (strlen(basename) == 10 && strPrefixLower(basename, "levels.d"))
1990 return LEVEL_FILE_TYPE_SP;
1992 // check for typical filename of a Diamond Caves II level package file
1993 if (strSuffixLower(basename, ".dc") ||
1994 strSuffixLower(basename, ".dc2"))
1995 return LEVEL_FILE_TYPE_DC;
1997 // check for typical filename of a Sokoban level package file
1998 if (strSuffixLower(basename, ".xsb") &&
1999 strchr(basename, '%') == NULL)
2000 return LEVEL_FILE_TYPE_SB;
2002 // ---------- try to determine file type from filesize ----------
2004 checked_free(filename);
2005 filename = getPath2(getCurrentLevelDir(), basename);
2007 if (stat(filename, &file_status) == 0)
2009 // check for typical filesize of a Supaplex level package file
2010 if (file_status.st_size == 170496)
2011 return LEVEL_FILE_TYPE_SP;
2014 return LEVEL_FILE_TYPE_UNKNOWN;
2017 static int getFileTypeFromMagicBytes(char *filename, int type)
2021 if ((file = openFile(filename, MODE_READ)))
2023 char chunk_name[CHUNK_ID_LEN + 1];
2025 getFileChunkBE(file, chunk_name, NULL);
2027 if (strEqual(chunk_name, "MMII") ||
2028 strEqual(chunk_name, "MIRR"))
2029 type = LEVEL_FILE_TYPE_MM;
2037 static boolean checkForPackageFromBasename(char *basename)
2039 // !!! WON'T WORK ANYMORE IF getFileTypeFromBasename() ALSO DETECTS !!!
2040 // !!! SINGLE LEVELS (CURRENTLY ONLY DETECTS LEVEL PACKAGES !!!
2042 return (getFileTypeFromBasename(basename) != LEVEL_FILE_TYPE_UNKNOWN);
2045 static char *getSingleLevelBasenameExt(int nr, char *extension)
2047 static char basename[MAX_FILENAME_LEN];
2050 sprintf(basename, "%s", LEVELTEMPLATE_FILENAME);
2052 sprintf(basename, "%03d.%s", nr, extension);
2057 static char *getSingleLevelBasename(int nr)
2059 return getSingleLevelBasenameExt(nr, LEVELFILE_EXTENSION);
2062 static char *getPackedLevelBasename(int type)
2064 static char basename[MAX_FILENAME_LEN];
2065 char *directory = getCurrentLevelDir();
2067 DirectoryEntry *dir_entry;
2069 strcpy(basename, UNDEFINED_FILENAME); // default: undefined file
2071 if ((dir = openDirectory(directory)) == NULL)
2073 Warn("cannot read current level directory '%s'", directory);
2078 while ((dir_entry = readDirectory(dir)) != NULL) // loop all entries
2080 char *entry_basename = dir_entry->basename;
2081 int entry_type = getFileTypeFromBasename(entry_basename);
2083 if (entry_type != LEVEL_FILE_TYPE_UNKNOWN) // found valid level package
2085 if (type == LEVEL_FILE_TYPE_UNKNOWN ||
2088 strcpy(basename, entry_basename);
2095 closeDirectory(dir);
2100 static char *getSingleLevelFilename(int nr)
2102 return getLevelFilenameFromBasename(getSingleLevelBasename(nr));
2105 #if ENABLE_UNUSED_CODE
2106 static char *getPackedLevelFilename(int type)
2108 return getLevelFilenameFromBasename(getPackedLevelBasename(type));
2112 char *getDefaultLevelFilename(int nr)
2114 return getSingleLevelFilename(nr);
2117 #if ENABLE_UNUSED_CODE
2118 static void setLevelFileInfo_SingleLevelFilename(struct LevelFileInfo *lfi,
2122 lfi->packed = FALSE;
2124 setString(&lfi->basename, getSingleLevelBasename(lfi->nr, lfi->type));
2125 setString(&lfi->filename, getLevelFilenameFromBasename(lfi->basename));
2129 static void setLevelFileInfo_FormatLevelFilename(struct LevelFileInfo *lfi,
2130 int type, char *format, ...)
2132 static char basename[MAX_FILENAME_LEN];
2135 va_start(ap, format);
2136 vsprintf(basename, format, ap);
2140 lfi->packed = FALSE;
2142 setString(&lfi->basename, basename);
2143 setString(&lfi->filename, getLevelFilenameFromBasename(lfi->basename));
2146 static void setLevelFileInfo_PackedLevelFilename(struct LevelFileInfo *lfi,
2152 setString(&lfi->basename, getPackedLevelBasename(lfi->type));
2153 setString(&lfi->filename, getLevelFilenameFromBasename(lfi->basename));
2156 static int getFiletypeFromID(char *filetype_id)
2158 char *filetype_id_lower;
2159 int filetype = LEVEL_FILE_TYPE_UNKNOWN;
2162 if (filetype_id == NULL)
2163 return LEVEL_FILE_TYPE_UNKNOWN;
2165 filetype_id_lower = getStringToLower(filetype_id);
2167 for (i = 0; filetype_id_list[i].id != NULL; i++)
2169 char *id_lower = getStringToLower(filetype_id_list[i].id);
2171 if (strEqual(filetype_id_lower, id_lower))
2172 filetype = filetype_id_list[i].filetype;
2176 if (filetype != LEVEL_FILE_TYPE_UNKNOWN)
2180 free(filetype_id_lower);
2185 char *getLocalLevelTemplateFilename(void)
2187 return getDefaultLevelFilename(-1);
2190 char *getGlobalLevelTemplateFilename(void)
2192 // global variable "leveldir_current" must be modified in the loop below
2193 LevelDirTree *leveldir_current_last = leveldir_current;
2194 char *filename = NULL;
2196 // check for template level in path from current to topmost tree node
2198 while (leveldir_current != NULL)
2200 filename = getDefaultLevelFilename(-1);
2202 if (fileExists(filename))
2205 leveldir_current = leveldir_current->node_parent;
2208 // restore global variable "leveldir_current" modified in above loop
2209 leveldir_current = leveldir_current_last;
2214 static void determineLevelFileInfo_Filename(struct LevelFileInfo *lfi)
2218 // special case: level number is negative => check for level template file
2221 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
2222 getSingleLevelBasename(-1));
2224 // replace local level template filename with global template filename
2225 setString(&lfi->filename, getGlobalLevelTemplateFilename());
2227 // no fallback if template file not existing
2231 // special case: check for file name/pattern specified in "levelinfo.conf"
2232 if (leveldir_current->level_filename != NULL)
2234 int filetype = getFiletypeFromID(leveldir_current->level_filetype);
2236 setLevelFileInfo_FormatLevelFilename(lfi, filetype,
2237 leveldir_current->level_filename, nr);
2239 lfi->packed = checkForPackageFromBasename(leveldir_current->level_filename);
2241 if (fileExists(lfi->filename))
2244 else if (leveldir_current->level_filetype != NULL)
2246 int filetype = getFiletypeFromID(leveldir_current->level_filetype);
2248 // check for specified native level file with standard file name
2249 setLevelFileInfo_FormatLevelFilename(lfi, filetype,
2250 "%03d.%s", nr, LEVELFILE_EXTENSION);
2251 if (fileExists(lfi->filename))
2255 // check for native Rocks'n'Diamonds level file
2256 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
2257 "%03d.%s", nr, LEVELFILE_EXTENSION);
2258 if (fileExists(lfi->filename))
2261 // check for Emerald Mine level file (V1)
2262 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "a%c%c",
2263 'a' + (nr / 10) % 26, '0' + nr % 10);
2264 if (fileExists(lfi->filename))
2266 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "A%c%c",
2267 'A' + (nr / 10) % 26, '0' + nr % 10);
2268 if (fileExists(lfi->filename))
2271 // check for Emerald Mine level file (V2 to V5)
2272 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%d", nr);
2273 if (fileExists(lfi->filename))
2276 // check for Emerald Mine level file (V6 / single mode)
2277 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02ds", nr);
2278 if (fileExists(lfi->filename))
2280 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02dS", nr);
2281 if (fileExists(lfi->filename))
2284 // check for Emerald Mine level file (V6 / teamwork mode)
2285 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02dt", nr);
2286 if (fileExists(lfi->filename))
2288 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02dT", nr);
2289 if (fileExists(lfi->filename))
2292 // check for various packed level file formats
2293 setLevelFileInfo_PackedLevelFilename(lfi, LEVEL_FILE_TYPE_UNKNOWN);
2294 if (fileExists(lfi->filename))
2297 // no known level file found -- use default values (and fail later)
2298 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
2299 "%03d.%s", nr, LEVELFILE_EXTENSION);
2302 static void determineLevelFileInfo_Filetype(struct LevelFileInfo *lfi)
2304 if (lfi->type == LEVEL_FILE_TYPE_UNKNOWN)
2305 lfi->type = getFileTypeFromBasename(lfi->basename);
2307 if (lfi->type == LEVEL_FILE_TYPE_RND)
2308 lfi->type = getFileTypeFromMagicBytes(lfi->filename, lfi->type);
2311 static void setLevelFileInfo(struct LevelFileInfo *level_file_info, int nr)
2313 // always start with reliable default values
2314 setFileInfoToDefaults(level_file_info);
2316 level_file_info->nr = nr; // set requested level number
2318 determineLevelFileInfo_Filename(level_file_info);
2319 determineLevelFileInfo_Filetype(level_file_info);
2322 static void copyLevelFileInfo(struct LevelFileInfo *lfi_from,
2323 struct LevelFileInfo *lfi_to)
2325 lfi_to->nr = lfi_from->nr;
2326 lfi_to->type = lfi_from->type;
2327 lfi_to->packed = lfi_from->packed;
2329 setString(&lfi_to->basename, lfi_from->basename);
2330 setString(&lfi_to->filename, lfi_from->filename);
2333 // ----------------------------------------------------------------------------
2334 // functions for loading R'n'D level
2335 // ----------------------------------------------------------------------------
2337 int getMappedElement(int element)
2339 // remap some (historic, now obsolete) elements
2343 case EL_PLAYER_OBSOLETE:
2344 element = EL_PLAYER_1;
2347 case EL_KEY_OBSOLETE:
2351 case EL_EM_KEY_1_FILE_OBSOLETE:
2352 element = EL_EM_KEY_1;
2355 case EL_EM_KEY_2_FILE_OBSOLETE:
2356 element = EL_EM_KEY_2;
2359 case EL_EM_KEY_3_FILE_OBSOLETE:
2360 element = EL_EM_KEY_3;
2363 case EL_EM_KEY_4_FILE_OBSOLETE:
2364 element = EL_EM_KEY_4;
2367 case EL_ENVELOPE_OBSOLETE:
2368 element = EL_ENVELOPE_1;
2376 if (element >= NUM_FILE_ELEMENTS)
2378 Warn("invalid level element %d", element);
2380 element = EL_UNKNOWN;
2388 static int getMappedElementByVersion(int element, int game_version)
2390 // remap some elements due to certain game version
2392 if (game_version <= VERSION_IDENT(2,2,0,0))
2394 // map game font elements
2395 element = (element == EL_CHAR('[') ? EL_CHAR_AUMLAUT :
2396 element == EL_CHAR('\\') ? EL_CHAR_OUMLAUT :
2397 element == EL_CHAR(']') ? EL_CHAR_UUMLAUT :
2398 element == EL_CHAR('^') ? EL_CHAR_COPYRIGHT : element);
2401 if (game_version < VERSION_IDENT(3,0,0,0))
2403 // map Supaplex gravity tube elements
2404 element = (element == EL_SP_GRAVITY_PORT_LEFT ? EL_SP_PORT_LEFT :
2405 element == EL_SP_GRAVITY_PORT_RIGHT ? EL_SP_PORT_RIGHT :
2406 element == EL_SP_GRAVITY_PORT_UP ? EL_SP_PORT_UP :
2407 element == EL_SP_GRAVITY_PORT_DOWN ? EL_SP_PORT_DOWN :
2414 static int LoadLevel_VERS(File *file, int chunk_size, struct LevelInfo *level)
2416 level->file_version = getFileVersion(file);
2417 level->game_version = getFileVersion(file);
2422 static int LoadLevel_DATE(File *file, int chunk_size, struct LevelInfo *level)
2424 level->creation_date.year = getFile16BitBE(file);
2425 level->creation_date.month = getFile8Bit(file);
2426 level->creation_date.day = getFile8Bit(file);
2428 level->creation_date.src = DATE_SRC_LEVELFILE;
2433 static int LoadLevel_HEAD(File *file, int chunk_size, struct LevelInfo *level)
2435 int initial_player_stepsize;
2436 int initial_player_gravity;
2439 level->fieldx = getFile8Bit(file);
2440 level->fieldy = getFile8Bit(file);
2442 level->time = getFile16BitBE(file);
2443 level->gems_needed = getFile16BitBE(file);
2445 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
2446 level->name[i] = getFile8Bit(file);
2447 level->name[MAX_LEVEL_NAME_LEN] = 0;
2449 for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
2450 level->score[i] = getFile8Bit(file);
2452 level->num_yamyam_contents = STD_ELEMENT_CONTENTS;
2453 for (i = 0; i < STD_ELEMENT_CONTENTS; i++)
2454 for (y = 0; y < 3; y++)
2455 for (x = 0; x < 3; x++)
2456 level->yamyam_content[i].e[x][y] = getMappedElement(getFile8Bit(file));
2458 level->amoeba_speed = getFile8Bit(file);
2459 level->time_magic_wall = getFile8Bit(file);
2460 level->time_wheel = getFile8Bit(file);
2461 level->amoeba_content = getMappedElement(getFile8Bit(file));
2463 initial_player_stepsize = (getFile8Bit(file) == 1 ? STEPSIZE_FAST :
2466 for (i = 0; i < MAX_PLAYERS; i++)
2467 level->initial_player_stepsize[i] = initial_player_stepsize;
2469 initial_player_gravity = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2471 for (i = 0; i < MAX_PLAYERS; i++)
2472 level->initial_player_gravity[i] = initial_player_gravity;
2474 level->encoding_16bit_field = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2475 level->em_slippery_gems = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2477 level->use_custom_template = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2479 level->block_last_field = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2480 level->sp_block_last_field = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2481 level->can_move_into_acid_bits = getFile32BitBE(file);
2482 level->dont_collide_with_bits = getFile8Bit(file);
2484 level->use_spring_bug = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2485 level->use_step_counter = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2487 level->instant_relocation = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2488 level->can_pass_to_walkable = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2489 level->grow_into_diggable = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2491 level->game_engine_type = getFile8Bit(file);
2493 ReadUnusedBytesFromFile(file, LEVEL_CHUNK_HEAD_UNUSED);
2498 static int LoadLevel_NAME(File *file, int chunk_size, struct LevelInfo *level)
2502 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
2503 level->name[i] = getFile8Bit(file);
2504 level->name[MAX_LEVEL_NAME_LEN] = 0;
2509 static int LoadLevel_AUTH(File *file, int chunk_size, struct LevelInfo *level)
2513 for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
2514 level->author[i] = getFile8Bit(file);
2515 level->author[MAX_LEVEL_AUTHOR_LEN] = 0;
2520 static int LoadLevel_BODY(File *file, int chunk_size, struct LevelInfo *level)
2523 int chunk_size_expected = level->fieldx * level->fieldy;
2525 /* Note: "chunk_size" was wrong before version 2.0 when elements are
2526 stored with 16-bit encoding (and should be twice as big then).
2527 Even worse, playfield data was stored 16-bit when only yamyam content
2528 contained 16-bit elements and vice versa. */
2530 if (level->encoding_16bit_field && level->file_version >= FILE_VERSION_2_0)
2531 chunk_size_expected *= 2;
2533 if (chunk_size_expected != chunk_size)
2535 ReadUnusedBytesFromFile(file, chunk_size);
2536 return chunk_size_expected;
2539 for (y = 0; y < level->fieldy; y++)
2540 for (x = 0; x < level->fieldx; x++)
2541 level->field[x][y] =
2542 getMappedElement(level->encoding_16bit_field ? getFile16BitBE(file) :
2547 static int LoadLevel_CONT(File *file, int chunk_size, struct LevelInfo *level)
2550 int header_size = 4;
2551 int content_size = MAX_ELEMENT_CONTENTS * 3 * 3;
2552 int chunk_size_expected = header_size + content_size;
2554 /* Note: "chunk_size" was wrong before version 2.0 when elements are
2555 stored with 16-bit encoding (and should be twice as big then).
2556 Even worse, playfield data was stored 16-bit when only yamyam content
2557 contained 16-bit elements and vice versa. */
2559 if (level->encoding_16bit_field && level->file_version >= FILE_VERSION_2_0)
2560 chunk_size_expected += content_size;
2562 if (chunk_size_expected != chunk_size)
2564 ReadUnusedBytesFromFile(file, chunk_size);
2565 return chunk_size_expected;
2569 level->num_yamyam_contents = getFile8Bit(file);
2573 // correct invalid number of content fields -- should never happen
2574 if (level->num_yamyam_contents < 1 ||
2575 level->num_yamyam_contents > MAX_ELEMENT_CONTENTS)
2576 level->num_yamyam_contents = STD_ELEMENT_CONTENTS;
2578 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
2579 for (y = 0; y < 3; y++)
2580 for (x = 0; x < 3; x++)
2581 level->yamyam_content[i].e[x][y] =
2582 getMappedElement(level->encoding_16bit_field ?
2583 getFile16BitBE(file) : getFile8Bit(file));
2587 static int LoadLevel_CNT2(File *file, int chunk_size, struct LevelInfo *level)
2592 int content_array[MAX_ELEMENT_CONTENTS][3][3];
2594 element = getMappedElement(getFile16BitBE(file));
2595 num_contents = getFile8Bit(file);
2597 getFile8Bit(file); // content x size (unused)
2598 getFile8Bit(file); // content y size (unused)
2600 ReadUnusedBytesFromFile(file, LEVEL_CHUNK_CNT2_UNUSED);
2602 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
2603 for (y = 0; y < 3; y++)
2604 for (x = 0; x < 3; x++)
2605 content_array[i][x][y] = getMappedElement(getFile16BitBE(file));
2607 // correct invalid number of content fields -- should never happen
2608 if (num_contents < 1 || num_contents > MAX_ELEMENT_CONTENTS)
2609 num_contents = STD_ELEMENT_CONTENTS;
2611 if (element == EL_YAMYAM)
2613 level->num_yamyam_contents = num_contents;
2615 for (i = 0; i < num_contents; i++)
2616 for (y = 0; y < 3; y++)
2617 for (x = 0; x < 3; x++)
2618 level->yamyam_content[i].e[x][y] = content_array[i][x][y];
2620 else if (element == EL_BD_AMOEBA)
2622 level->amoeba_content = content_array[0][0][0];
2626 Warn("cannot load content for element '%d'", element);
2632 static int LoadLevel_CNT3(File *file, int chunk_size, struct LevelInfo *level)
2638 int chunk_size_expected;
2640 element = getMappedElement(getFile16BitBE(file));
2641 if (!IS_ENVELOPE(element))
2642 element = EL_ENVELOPE_1;
2644 envelope_nr = element - EL_ENVELOPE_1;
2646 envelope_len = getFile16BitBE(file);
2648 level->envelope[envelope_nr].xsize = getFile8Bit(file);
2649 level->envelope[envelope_nr].ysize = getFile8Bit(file);
2651 ReadUnusedBytesFromFile(file, LEVEL_CHUNK_CNT3_UNUSED);
2653 chunk_size_expected = LEVEL_CHUNK_CNT3_SIZE(envelope_len);
2654 if (chunk_size_expected != chunk_size)
2656 ReadUnusedBytesFromFile(file, chunk_size - LEVEL_CHUNK_CNT3_HEADER);
2657 return chunk_size_expected;
2660 for (i = 0; i < envelope_len; i++)
2661 level->envelope[envelope_nr].text[i] = getFile8Bit(file);
2666 static int LoadLevel_CUS1(File *file, int chunk_size, struct LevelInfo *level)
2668 int num_changed_custom_elements = getFile16BitBE(file);
2669 int chunk_size_expected = 2 + num_changed_custom_elements * 6;
2672 if (chunk_size_expected != chunk_size)
2674 ReadUnusedBytesFromFile(file, chunk_size - 2);
2675 return chunk_size_expected;
2678 for (i = 0; i < num_changed_custom_elements; i++)
2680 int element = getMappedElement(getFile16BitBE(file));
2681 int properties = getFile32BitBE(file);
2683 if (IS_CUSTOM_ELEMENT(element))
2684 element_info[element].properties[EP_BITFIELD_BASE_NR] = properties;
2686 Warn("invalid custom element number %d", element);
2688 // older game versions that wrote level files with CUS1 chunks used
2689 // different default push delay values (not yet stored in level file)
2690 element_info[element].push_delay_fixed = 2;
2691 element_info[element].push_delay_random = 8;
2694 level->file_has_custom_elements = TRUE;
2699 static int LoadLevel_CUS2(File *file, int chunk_size, struct LevelInfo *level)
2701 int num_changed_custom_elements = getFile16BitBE(file);
2702 int chunk_size_expected = 2 + num_changed_custom_elements * 4;
2705 if (chunk_size_expected != chunk_size)
2707 ReadUnusedBytesFromFile(file, chunk_size - 2);
2708 return chunk_size_expected;
2711 for (i = 0; i < num_changed_custom_elements; i++)
2713 int element = getMappedElement(getFile16BitBE(file));
2714 int custom_target_element = getMappedElement(getFile16BitBE(file));
2716 if (IS_CUSTOM_ELEMENT(element))
2717 element_info[element].change->target_element = custom_target_element;
2719 Warn("invalid custom element number %d", element);
2722 level->file_has_custom_elements = TRUE;
2727 static int LoadLevel_CUS3(File *file, int chunk_size, struct LevelInfo *level)
2729 int num_changed_custom_elements = getFile16BitBE(file);
2730 int chunk_size_expected = LEVEL_CHUNK_CUS3_SIZE(num_changed_custom_elements);
2733 if (chunk_size_expected != chunk_size)
2735 ReadUnusedBytesFromFile(file, chunk_size - 2);
2736 return chunk_size_expected;
2739 for (i = 0; i < num_changed_custom_elements; i++)
2741 int element = getMappedElement(getFile16BitBE(file));
2742 struct ElementInfo *ei = &element_info[element];
2743 unsigned int event_bits;
2745 if (!IS_CUSTOM_ELEMENT(element))
2747 Warn("invalid custom element number %d", element);
2749 element = EL_INTERNAL_DUMMY;
2752 for (j = 0; j < MAX_ELEMENT_NAME_LEN; j++)
2753 ei->description[j] = getFile8Bit(file);
2754 ei->description[MAX_ELEMENT_NAME_LEN] = 0;
2756 ei->properties[EP_BITFIELD_BASE_NR] = getFile32BitBE(file);
2758 // some free bytes for future properties and padding
2759 ReadUnusedBytesFromFile(file, 7);
2761 ei->use_gfx_element = getFile8Bit(file);
2762 ei->gfx_element_initial = getMappedElement(getFile16BitBE(file));
2764 ei->collect_score_initial = getFile8Bit(file);
2765 ei->collect_count_initial = getFile8Bit(file);
2767 ei->push_delay_fixed = getFile16BitBE(file);
2768 ei->push_delay_random = getFile16BitBE(file);
2769 ei->move_delay_fixed = getFile16BitBE(file);
2770 ei->move_delay_random = getFile16BitBE(file);
2772 ei->move_pattern = getFile16BitBE(file);
2773 ei->move_direction_initial = getFile8Bit(file);
2774 ei->move_stepsize = getFile8Bit(file);
2776 for (y = 0; y < 3; y++)
2777 for (x = 0; x < 3; x++)
2778 ei->content.e[x][y] = getMappedElement(getFile16BitBE(file));
2780 event_bits = getFile32BitBE(file);
2781 for (j = 0; j < NUM_CHANGE_EVENTS; j++)
2782 if (event_bits & (1 << j))
2783 ei->change->has_event[j] = TRUE;
2785 ei->change->target_element = getMappedElement(getFile16BitBE(file));
2787 ei->change->delay_fixed = getFile16BitBE(file);
2788 ei->change->delay_random = getFile16BitBE(file);
2789 ei->change->delay_frames = getFile16BitBE(file);
2791 ei->change->initial_trigger_element= getMappedElement(getFile16BitBE(file));
2793 ei->change->explode = getFile8Bit(file);
2794 ei->change->use_target_content = getFile8Bit(file);
2795 ei->change->only_if_complete = getFile8Bit(file);
2796 ei->change->use_random_replace = getFile8Bit(file);
2798 ei->change->random_percentage = getFile8Bit(file);
2799 ei->change->replace_when = getFile8Bit(file);
2801 for (y = 0; y < 3; y++)
2802 for (x = 0; x < 3; x++)
2803 ei->change->target_content.e[x][y] =
2804 getMappedElement(getFile16BitBE(file));
2806 ei->slippery_type = getFile8Bit(file);
2808 // some free bytes for future properties and padding
2809 ReadUnusedBytesFromFile(file, LEVEL_CPART_CUS3_UNUSED);
2811 // mark that this custom element has been modified
2812 ei->modified_settings = TRUE;
2815 level->file_has_custom_elements = TRUE;
2820 static int LoadLevel_CUS4(File *file, int chunk_size, struct LevelInfo *level)
2822 struct ElementInfo *ei;
2823 int chunk_size_expected;
2827 // ---------- custom element base property values (96 bytes) ----------------
2829 element = getMappedElement(getFile16BitBE(file));
2831 if (!IS_CUSTOM_ELEMENT(element))
2833 Warn("invalid custom element number %d", element);
2835 ReadUnusedBytesFromFile(file, chunk_size - 2);
2840 ei = &element_info[element];
2842 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
2843 ei->description[i] = getFile8Bit(file);
2844 ei->description[MAX_ELEMENT_NAME_LEN] = 0;
2846 ei->properties[EP_BITFIELD_BASE_NR] = getFile32BitBE(file);
2848 ReadUnusedBytesFromFile(file, 4); // reserved for more base properties
2850 ei->num_change_pages = getFile8Bit(file);
2852 chunk_size_expected = LEVEL_CHUNK_CUS4_SIZE(ei->num_change_pages);
2853 if (chunk_size_expected != chunk_size)
2855 ReadUnusedBytesFromFile(file, chunk_size - 43);
2856 return chunk_size_expected;
2859 ei->ce_value_fixed_initial = getFile16BitBE(file);
2860 ei->ce_value_random_initial = getFile16BitBE(file);
2861 ei->use_last_ce_value = getFile8Bit(file);
2863 ei->use_gfx_element = getFile8Bit(file);
2864 ei->gfx_element_initial = getMappedElement(getFile16BitBE(file));
2866 ei->collect_score_initial = getFile8Bit(file);
2867 ei->collect_count_initial = getFile8Bit(file);
2869 ei->drop_delay_fixed = getFile8Bit(file);
2870 ei->push_delay_fixed = getFile8Bit(file);
2871 ei->drop_delay_random = getFile8Bit(file);
2872 ei->push_delay_random = getFile8Bit(file);
2873 ei->move_delay_fixed = getFile16BitBE(file);
2874 ei->move_delay_random = getFile16BitBE(file);
2876 // bits 0 - 15 of "move_pattern" ...
2877 ei->move_pattern = getFile16BitBE(file);
2878 ei->move_direction_initial = getFile8Bit(file);
2879 ei->move_stepsize = getFile8Bit(file);
2881 ei->slippery_type = getFile8Bit(file);
2883 for (y = 0; y < 3; y++)
2884 for (x = 0; x < 3; x++)
2885 ei->content.e[x][y] = getMappedElement(getFile16BitBE(file));
2887 ei->move_enter_element = getMappedElement(getFile16BitBE(file));
2888 ei->move_leave_element = getMappedElement(getFile16BitBE(file));
2889 ei->move_leave_type = getFile8Bit(file);
2891 // ... bits 16 - 31 of "move_pattern" (not nice, but downward compatible)
2892 ei->move_pattern |= (getFile16BitBE(file) << 16);
2894 ei->access_direction = getFile8Bit(file);
2896 ei->explosion_delay = getFile8Bit(file);
2897 ei->ignition_delay = getFile8Bit(file);
2898 ei->explosion_type = getFile8Bit(file);
2900 // some free bytes for future custom property values and padding
2901 ReadUnusedBytesFromFile(file, 1);
2903 // ---------- change page property values (48 bytes) ------------------------
2905 setElementChangePages(ei, ei->num_change_pages);
2907 for (i = 0; i < ei->num_change_pages; i++)
2909 struct ElementChangeInfo *change = &ei->change_page[i];
2910 unsigned int event_bits;
2912 // always start with reliable default values
2913 setElementChangeInfoToDefaults(change);
2915 // bits 0 - 31 of "has_event[]" ...
2916 event_bits = getFile32BitBE(file);
2917 for (j = 0; j < MIN(NUM_CHANGE_EVENTS, 32); j++)
2918 if (event_bits & (1 << j))
2919 change->has_event[j] = TRUE;
2921 change->target_element = getMappedElement(getFile16BitBE(file));
2923 change->delay_fixed = getFile16BitBE(file);
2924 change->delay_random = getFile16BitBE(file);
2925 change->delay_frames = getFile16BitBE(file);
2927 change->initial_trigger_element = getMappedElement(getFile16BitBE(file));
2929 change->explode = getFile8Bit(file);
2930 change->use_target_content = getFile8Bit(file);
2931 change->only_if_complete = getFile8Bit(file);
2932 change->use_random_replace = getFile8Bit(file);
2934 change->random_percentage = getFile8Bit(file);
2935 change->replace_when = getFile8Bit(file);
2937 for (y = 0; y < 3; y++)
2938 for (x = 0; x < 3; x++)
2939 change->target_content.e[x][y]= getMappedElement(getFile16BitBE(file));
2941 change->can_change = getFile8Bit(file);
2943 change->trigger_side = getFile8Bit(file);
2945 change->trigger_player = getFile8Bit(file);
2946 change->trigger_page = getFile8Bit(file);
2948 change->trigger_page = (change->trigger_page == CH_PAGE_ANY_FILE ?
2949 CH_PAGE_ANY : (1 << change->trigger_page));
2951 change->has_action = getFile8Bit(file);
2952 change->action_type = getFile8Bit(file);
2953 change->action_mode = getFile8Bit(file);
2954 change->action_arg = getFile16BitBE(file);
2956 // ... bits 32 - 39 of "has_event[]" (not nice, but downward compatible)
2957 event_bits = getFile8Bit(file);
2958 for (j = 32; j < NUM_CHANGE_EVENTS; j++)
2959 if (event_bits & (1 << (j - 32)))
2960 change->has_event[j] = TRUE;
2963 // mark this custom element as modified
2964 ei->modified_settings = TRUE;
2966 level->file_has_custom_elements = TRUE;
2971 static int LoadLevel_GRP1(File *file, int chunk_size, struct LevelInfo *level)
2973 struct ElementInfo *ei;
2974 struct ElementGroupInfo *group;
2978 element = getMappedElement(getFile16BitBE(file));
2980 if (!IS_GROUP_ELEMENT(element))
2982 Warn("invalid group element number %d", element);
2984 ReadUnusedBytesFromFile(file, chunk_size - 2);
2989 ei = &element_info[element];
2991 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
2992 ei->description[i] = getFile8Bit(file);
2993 ei->description[MAX_ELEMENT_NAME_LEN] = 0;
2995 group = element_info[element].group;
2997 group->num_elements = getFile8Bit(file);
2999 ei->use_gfx_element = getFile8Bit(file);
3000 ei->gfx_element_initial = getMappedElement(getFile16BitBE(file));
3002 group->choice_mode = getFile8Bit(file);
3004 // some free bytes for future values and padding
3005 ReadUnusedBytesFromFile(file, 3);
3007 for (i = 0; i < MAX_ELEMENTS_IN_GROUP; i++)
3008 group->element[i] = getMappedElement(getFile16BitBE(file));
3010 // mark this group element as modified
3011 element_info[element].modified_settings = TRUE;
3013 level->file_has_custom_elements = TRUE;
3018 static int LoadLevel_MicroChunk(File *file, struct LevelFileConfigInfo *conf,
3019 int element, int real_element)
3021 int micro_chunk_size = 0;
3022 int conf_type = getFile8Bit(file);
3023 int byte_mask = conf_type & CONF_MASK_BYTES;
3024 boolean element_found = FALSE;
3027 micro_chunk_size += 1;
3029 if (byte_mask == CONF_MASK_MULTI_BYTES)
3031 int num_bytes = getFile16BitBE(file);
3032 byte *buffer = checked_malloc(num_bytes);
3034 ReadBytesFromFile(file, buffer, num_bytes);
3036 for (i = 0; conf[i].data_type != -1; i++)
3038 if (conf[i].element == element &&
3039 conf[i].conf_type == conf_type)
3041 int data_type = conf[i].data_type;
3042 int num_entities = num_bytes / CONF_ENTITY_NUM_BYTES(data_type);
3043 int max_num_entities = conf[i].max_num_entities;
3045 if (num_entities > max_num_entities)
3047 Warn("truncating number of entities for element %d from %d to %d",
3048 element, num_entities, max_num_entities);
3050 num_entities = max_num_entities;
3053 if (num_entities == 0 && (data_type == TYPE_ELEMENT_LIST ||
3054 data_type == TYPE_CONTENT_LIST))
3056 // for element and content lists, zero entities are not allowed
3057 Warn("found empty list of entities for element %d", element);
3059 // do not set "num_entities" here to prevent reading behind buffer
3061 *(int *)(conf[i].num_entities) = 1; // at least one is required
3065 *(int *)(conf[i].num_entities) = num_entities;
3068 element_found = TRUE;
3070 if (data_type == TYPE_STRING)
3072 char *string = (char *)(conf[i].value);
3075 for (j = 0; j < max_num_entities; j++)
3076 string[j] = (j < num_entities ? buffer[j] : '\0');
3078 else if (data_type == TYPE_ELEMENT_LIST)
3080 int *element_array = (int *)(conf[i].value);
3083 for (j = 0; j < num_entities; j++)
3085 getMappedElement(CONF_ELEMENTS_ELEMENT(buffer, j));
3087 else if (data_type == TYPE_CONTENT_LIST)
3089 struct Content *content= (struct Content *)(conf[i].value);
3092 for (c = 0; c < num_entities; c++)
3093 for (y = 0; y < 3; y++)
3094 for (x = 0; x < 3; x++)
3095 content[c].e[x][y] =
3096 getMappedElement(CONF_CONTENTS_ELEMENT(buffer, c, x, y));
3099 element_found = FALSE;
3105 checked_free(buffer);
3107 micro_chunk_size += 2 + num_bytes;
3109 else // constant size configuration data (1, 2 or 4 bytes)
3111 int value = (byte_mask == CONF_MASK_1_BYTE ? getFile8Bit (file) :
3112 byte_mask == CONF_MASK_2_BYTE ? getFile16BitBE(file) :
3113 byte_mask == CONF_MASK_4_BYTE ? getFile32BitBE(file) : 0);
3115 for (i = 0; conf[i].data_type != -1; i++)
3117 if (conf[i].element == element &&
3118 conf[i].conf_type == conf_type)
3120 int data_type = conf[i].data_type;
3122 if (data_type == TYPE_ELEMENT)
3123 value = getMappedElement(value);
3125 if (data_type == TYPE_BOOLEAN)
3126 *(boolean *)(conf[i].value) = value;
3128 *(int *) (conf[i].value) = value;
3130 element_found = TRUE;
3136 micro_chunk_size += CONF_VALUE_NUM_BYTES(byte_mask);
3141 char *error_conf_chunk_bytes =
3142 (byte_mask == CONF_MASK_1_BYTE ? "CONF_VALUE_8_BIT" :
3143 byte_mask == CONF_MASK_2_BYTE ? "CONF_VALUE_16_BIT" :
3144 byte_mask == CONF_MASK_4_BYTE ? "CONF_VALUE_32_BIT" :"CONF_VALUE_BYTES");
3145 int error_conf_chunk_token = conf_type & CONF_MASK_TOKEN;
3146 int error_element = real_element;
3148 Warn("cannot load micro chunk '%s(%d)' value for element %d ['%s']",
3149 error_conf_chunk_bytes, error_conf_chunk_token,
3150 error_element, EL_NAME(error_element));
3153 return micro_chunk_size;
3156 static int LoadLevel_INFO(File *file, int chunk_size, struct LevelInfo *level)
3158 int real_chunk_size = 0;
3160 li = *level; // copy level data into temporary buffer
3162 while (!checkEndOfFile(file))
3164 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_INFO, -1, -1);
3166 if (real_chunk_size >= chunk_size)
3170 *level = li; // copy temporary buffer back to level data
3172 return real_chunk_size;
3175 static int LoadLevel_CONF(File *file, int chunk_size, struct LevelInfo *level)
3177 int real_chunk_size = 0;
3179 li = *level; // copy level data into temporary buffer
3181 while (!checkEndOfFile(file))
3183 int element = getMappedElement(getFile16BitBE(file));
3185 real_chunk_size += 2;
3186 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_CONF,
3188 if (real_chunk_size >= chunk_size)
3192 *level = li; // copy temporary buffer back to level data
3194 return real_chunk_size;
3197 static int LoadLevel_ELEM(File *file, int chunk_size, struct LevelInfo *level)
3199 int real_chunk_size = 0;
3201 li = *level; // copy level data into temporary buffer
3203 while (!checkEndOfFile(file))
3205 int element = getMappedElement(getFile16BitBE(file));
3207 real_chunk_size += 2;
3208 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_ELEM,
3210 if (real_chunk_size >= chunk_size)
3214 *level = li; // copy temporary buffer back to level data
3216 return real_chunk_size;
3219 static int LoadLevel_NOTE(File *file, int chunk_size, struct LevelInfo *level)
3221 int element = getMappedElement(getFile16BitBE(file));
3222 int envelope_nr = element - EL_ENVELOPE_1;
3223 int real_chunk_size = 2;
3225 xx_envelope = level->envelope[envelope_nr]; // copy into temporary buffer
3227 while (!checkEndOfFile(file))
3229 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_NOTE,
3232 if (real_chunk_size >= chunk_size)
3236 level->envelope[envelope_nr] = xx_envelope; // copy from temporary buffer
3238 return real_chunk_size;
3241 static int LoadLevel_CUSX(File *file, int chunk_size, struct LevelInfo *level)
3243 int element = getMappedElement(getFile16BitBE(file));
3244 int real_chunk_size = 2;
3245 struct ElementInfo *ei = &element_info[element];
3248 xx_ei = *ei; // copy element data into temporary buffer
3250 xx_ei.num_change_pages = -1;
3252 while (!checkEndOfFile(file))
3254 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_CUSX_base,
3256 if (xx_ei.num_change_pages != -1)
3259 if (real_chunk_size >= chunk_size)
3265 if (ei->num_change_pages == -1)
3267 Warn("LoadLevel_CUSX(): missing 'num_change_pages' for '%s'",
3270 ei->num_change_pages = 1;
3272 setElementChangePages(ei, 1);
3273 setElementChangeInfoToDefaults(ei->change);
3275 return real_chunk_size;
3278 // initialize number of change pages stored for this custom element
3279 setElementChangePages(ei, ei->num_change_pages);
3280 for (i = 0; i < ei->num_change_pages; i++)
3281 setElementChangeInfoToDefaults(&ei->change_page[i]);
3283 // start with reading properties for the first change page
3284 xx_current_change_page = 0;
3286 while (!checkEndOfFile(file))
3288 struct ElementChangeInfo *change = &ei->change_page[xx_current_change_page];
3290 xx_change = *change; // copy change data into temporary buffer
3292 resetEventBits(); // reset bits; change page might have changed
3294 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_CUSX_change,
3297 *change = xx_change;
3299 setEventFlagsFromEventBits(change);
3301 if (real_chunk_size >= chunk_size)
3305 level->file_has_custom_elements = TRUE;
3307 return real_chunk_size;
3310 static int LoadLevel_GRPX(File *file, int chunk_size, struct LevelInfo *level)
3312 int element = getMappedElement(getFile16BitBE(file));
3313 int real_chunk_size = 2;
3314 struct ElementInfo *ei = &element_info[element];
3315 struct ElementGroupInfo *group = ei->group;
3317 xx_ei = *ei; // copy element data into temporary buffer
3318 xx_group = *group; // copy group data into temporary buffer
3320 while (!checkEndOfFile(file))
3322 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_GRPX,
3325 if (real_chunk_size >= chunk_size)
3332 level->file_has_custom_elements = TRUE;
3334 return real_chunk_size;
3337 static void LoadLevelFromFileInfo_RND(struct LevelInfo *level,
3338 struct LevelFileInfo *level_file_info,
3339 boolean level_info_only)
3341 char *filename = level_file_info->filename;
3342 char cookie[MAX_LINE_LEN];
3343 char chunk_name[CHUNK_ID_LEN + 1];
3347 if (!(file = openFile(filename, MODE_READ)))
3349 level->no_valid_file = TRUE;
3350 level->no_level_file = TRUE;
3352 if (level_info_only)
3355 Warn("cannot read level '%s' -- using empty level", filename);
3357 if (!setup.editor.use_template_for_new_levels)
3360 // if level file not found, try to initialize level data from template
3361 filename = getGlobalLevelTemplateFilename();
3363 if (!(file = openFile(filename, MODE_READ)))
3366 // default: for empty levels, use level template for custom elements
3367 level->use_custom_template = TRUE;
3369 level->no_valid_file = FALSE;
3372 getFileChunkBE(file, chunk_name, NULL);
3373 if (strEqual(chunk_name, "RND1"))
3375 getFile32BitBE(file); // not used
3377 getFileChunkBE(file, chunk_name, NULL);
3378 if (!strEqual(chunk_name, "CAVE"))
3380 level->no_valid_file = TRUE;
3382 Warn("unknown format of level file '%s'", filename);
3389 else // check for pre-2.0 file format with cookie string
3391 strcpy(cookie, chunk_name);
3392 if (getStringFromFile(file, &cookie[4], MAX_LINE_LEN - 4) == NULL)
3394 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
3395 cookie[strlen(cookie) - 1] = '\0';
3397 if (!checkCookieString(cookie, LEVEL_COOKIE_TMPL))
3399 level->no_valid_file = TRUE;
3401 Warn("unknown format of level file '%s'", filename);
3408 if ((level->file_version = getFileVersionFromCookieString(cookie)) == -1)
3410 level->no_valid_file = TRUE;
3412 Warn("unsupported version of level file '%s'", filename);
3419 // pre-2.0 level files have no game version, so use file version here
3420 level->game_version = level->file_version;
3423 if (level->file_version < FILE_VERSION_1_2)
3425 // level files from versions before 1.2.0 without chunk structure
3426 LoadLevel_HEAD(file, LEVEL_CHUNK_HEAD_SIZE, level);
3427 LoadLevel_BODY(file, level->fieldx * level->fieldy, level);
3435 int (*loader)(File *, int, struct LevelInfo *);
3439 { "VERS", LEVEL_CHUNK_VERS_SIZE, LoadLevel_VERS },
3440 { "DATE", LEVEL_CHUNK_DATE_SIZE, LoadLevel_DATE },
3441 { "HEAD", LEVEL_CHUNK_HEAD_SIZE, LoadLevel_HEAD },
3442 { "NAME", LEVEL_CHUNK_NAME_SIZE, LoadLevel_NAME },
3443 { "AUTH", LEVEL_CHUNK_AUTH_SIZE, LoadLevel_AUTH },
3444 { "INFO", -1, LoadLevel_INFO },
3445 { "BODY", -1, LoadLevel_BODY },
3446 { "CONT", -1, LoadLevel_CONT },
3447 { "CNT2", LEVEL_CHUNK_CNT2_SIZE, LoadLevel_CNT2 },
3448 { "CNT3", -1, LoadLevel_CNT3 },
3449 { "CUS1", -1, LoadLevel_CUS1 },
3450 { "CUS2", -1, LoadLevel_CUS2 },
3451 { "CUS3", -1, LoadLevel_CUS3 },
3452 { "CUS4", -1, LoadLevel_CUS4 },
3453 { "GRP1", -1, LoadLevel_GRP1 },
3454 { "CONF", -1, LoadLevel_CONF },
3455 { "ELEM", -1, LoadLevel_ELEM },
3456 { "NOTE", -1, LoadLevel_NOTE },
3457 { "CUSX", -1, LoadLevel_CUSX },
3458 { "GRPX", -1, LoadLevel_GRPX },
3463 while (getFileChunkBE(file, chunk_name, &chunk_size))
3467 while (chunk_info[i].name != NULL &&
3468 !strEqual(chunk_name, chunk_info[i].name))
3471 if (chunk_info[i].name == NULL)
3473 Warn("unknown chunk '%s' in level file '%s'",
3474 chunk_name, filename);
3476 ReadUnusedBytesFromFile(file, chunk_size);
3478 else if (chunk_info[i].size != -1 &&
3479 chunk_info[i].size != chunk_size)
3481 Warn("wrong size (%d) of chunk '%s' in level file '%s'",
3482 chunk_size, chunk_name, filename);
3484 ReadUnusedBytesFromFile(file, chunk_size);
3488 // call function to load this level chunk
3489 int chunk_size_expected =
3490 (chunk_info[i].loader)(file, chunk_size, level);
3492 // the size of some chunks cannot be checked before reading other
3493 // chunks first (like "HEAD" and "BODY") that contain some header
3494 // information, so check them here
3495 if (chunk_size_expected != chunk_size)
3497 Warn("wrong size (%d) of chunk '%s' in level file '%s'",
3498 chunk_size, chunk_name, filename);
3508 // ----------------------------------------------------------------------------
3509 // functions for loading EM level
3510 // ----------------------------------------------------------------------------
3512 static void CopyNativeLevel_RND_to_EM(struct LevelInfo *level)
3514 static int ball_xy[8][2] =
3525 struct LevelInfo_EM *level_em = level->native_em_level;
3526 struct CAVE *cav = level_em->cav;
3529 cav->width = MIN(level->fieldx, MAX_PLAYFIELD_WIDTH);
3530 cav->height = MIN(level->fieldy, MAX_PLAYFIELD_HEIGHT);
3532 cav->time_seconds = level->time;
3533 cav->gems_needed = level->gems_needed;
3535 cav->emerald_score = level->score[SC_EMERALD];
3536 cav->diamond_score = level->score[SC_DIAMOND];
3537 cav->alien_score = level->score[SC_ROBOT];
3538 cav->tank_score = level->score[SC_SPACESHIP];
3539 cav->bug_score = level->score[SC_BUG];
3540 cav->eater_score = level->score[SC_YAMYAM];
3541 cav->nut_score = level->score[SC_NUT];
3542 cav->dynamite_score = level->score[SC_DYNAMITE];
3543 cav->key_score = level->score[SC_KEY];
3544 cav->exit_score = level->score[SC_TIME_BONUS];
3546 cav->num_eater_arrays = level->num_yamyam_contents;
3548 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
3549 for (y = 0; y < 3; y++)
3550 for (x = 0; x < 3; x++)
3551 cav->eater_array[i][y * 3 + x] =
3552 map_element_RND_to_EM_cave(level->yamyam_content[i].e[x][y]);
3554 cav->amoeba_time = level->amoeba_speed;
3555 cav->wonderwall_time = level->time_magic_wall;
3556 cav->wheel_time = level->time_wheel;
3558 cav->android_move_time = level->android_move_time;
3559 cav->android_clone_time = level->android_clone_time;
3560 cav->ball_random = level->ball_random;
3561 cav->ball_active = level->ball_active_initial;
3562 cav->ball_time = level->ball_time;
3563 cav->num_ball_arrays = level->num_ball_contents;
3565 cav->lenses_score = level->lenses_score;
3566 cav->magnify_score = level->magnify_score;
3567 cav->slurp_score = level->slurp_score;
3569 cav->lenses_time = level->lenses_time;
3570 cav->magnify_time = level->magnify_time;
3572 cav->wind_direction =
3573 map_direction_RND_to_EM(level->wind_direction_initial);
3575 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
3576 for (j = 0; j < 8; j++)
3577 cav->ball_array[i][j] =
3578 map_element_RND_to_EM_cave(level->ball_content[i].
3579 e[ball_xy[j][0]][ball_xy[j][1]]);
3581 map_android_clone_elements_RND_to_EM(level);
3583 // first fill the complete playfield with the empty space element
3584 for (y = 0; y < EM_MAX_CAVE_HEIGHT; y++)
3585 for (x = 0; x < EM_MAX_CAVE_WIDTH; x++)
3586 cav->cave[x][y] = Cblank;
3588 // then copy the real level contents from level file into the playfield
3589 for (y = 0; y < cav->height; y++) for (x = 0; x < cav->width; x++)
3591 int new_element = map_element_RND_to_EM_cave(level->field[x][y]);
3593 if (level->field[x][y] == EL_AMOEBA_DEAD)
3594 new_element = map_element_RND_to_EM_cave(EL_AMOEBA_WET);
3596 cav->cave[x][y] = new_element;
3599 for (i = 0; i < MAX_PLAYERS; i++)
3601 cav->player_x[i] = -1;
3602 cav->player_y[i] = -1;
3605 // initialize player positions and delete players from the playfield
3606 for (y = 0; y < cav->height; y++) for (x = 0; x < cav->width; x++)
3608 if (ELEM_IS_PLAYER(level->field[x][y]))
3610 int player_nr = GET_PLAYER_NR(level->field[x][y]);
3612 cav->player_x[player_nr] = x;
3613 cav->player_y[player_nr] = y;
3615 cav->cave[x][y] = map_element_RND_to_EM_cave(EL_EMPTY);
3620 static void CopyNativeLevel_EM_to_RND(struct LevelInfo *level)
3622 static int ball_xy[8][2] =
3633 struct LevelInfo_EM *level_em = level->native_em_level;
3634 struct CAVE *cav = level_em->cav;
3637 level->fieldx = MIN(cav->width, MAX_LEV_FIELDX);
3638 level->fieldy = MIN(cav->height, MAX_LEV_FIELDY);
3640 level->time = cav->time_seconds;
3641 level->gems_needed = cav->gems_needed;
3643 sprintf(level->name, "Level %d", level->file_info.nr);
3645 level->score[SC_EMERALD] = cav->emerald_score;
3646 level->score[SC_DIAMOND] = cav->diamond_score;
3647 level->score[SC_ROBOT] = cav->alien_score;
3648 level->score[SC_SPACESHIP] = cav->tank_score;
3649 level->score[SC_BUG] = cav->bug_score;
3650 level->score[SC_YAMYAM] = cav->eater_score;
3651 level->score[SC_NUT] = cav->nut_score;
3652 level->score[SC_DYNAMITE] = cav->dynamite_score;
3653 level->score[SC_KEY] = cav->key_score;
3654 level->score[SC_TIME_BONUS] = cav->exit_score;
3656 level->num_yamyam_contents = cav->num_eater_arrays;
3658 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
3659 for (y = 0; y < 3; y++)
3660 for (x = 0; x < 3; x++)
3661 level->yamyam_content[i].e[x][y] =
3662 map_element_EM_to_RND_cave(cav->eater_array[i][y * 3 + x]);
3664 level->amoeba_speed = cav->amoeba_time;
3665 level->time_magic_wall = cav->wonderwall_time;
3666 level->time_wheel = cav->wheel_time;
3668 level->android_move_time = cav->android_move_time;
3669 level->android_clone_time = cav->android_clone_time;
3670 level->ball_random = cav->ball_random;
3671 level->ball_active_initial = cav->ball_active;
3672 level->ball_time = cav->ball_time;
3673 level->num_ball_contents = cav->num_ball_arrays;
3675 level->lenses_score = cav->lenses_score;
3676 level->magnify_score = cav->magnify_score;
3677 level->slurp_score = cav->slurp_score;
3679 level->lenses_time = cav->lenses_time;
3680 level->magnify_time = cav->magnify_time;
3682 level->wind_direction_initial =
3683 map_direction_EM_to_RND(cav->wind_direction);
3685 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
3686 for (j = 0; j < 8; j++)
3687 level->ball_content[i].e[ball_xy[j][0]][ball_xy[j][1]] =
3688 map_element_EM_to_RND_cave(cav->ball_array[i][j]);
3690 map_android_clone_elements_EM_to_RND(level);
3692 // convert the playfield (some elements need special treatment)
3693 for (y = 0; y < level->fieldy; y++) for (x = 0; x < level->fieldx; x++)
3695 int new_element = map_element_EM_to_RND_cave(cav->cave[x][y]);
3697 if (new_element == EL_AMOEBA_WET && level->amoeba_speed == 0)
3698 new_element = EL_AMOEBA_DEAD;
3700 level->field[x][y] = new_element;
3703 for (i = 0; i < MAX_PLAYERS; i++)
3705 // in case of all players set to the same field, use the first player
3706 int nr = MAX_PLAYERS - i - 1;
3707 int jx = cav->player_x[nr];
3708 int jy = cav->player_y[nr];
3710 if (jx != -1 && jy != -1)
3711 level->field[jx][jy] = EL_PLAYER_1 + nr;
3716 // ----------------------------------------------------------------------------
3717 // functions for loading SP level
3718 // ----------------------------------------------------------------------------
3720 static void CopyNativeLevel_RND_to_SP(struct LevelInfo *level)
3722 struct LevelInfo_SP *level_sp = level->native_sp_level;
3723 LevelInfoType *header = &level_sp->header;
3726 level_sp->width = level->fieldx;
3727 level_sp->height = level->fieldy;
3729 for (x = 0; x < level->fieldx; x++)
3730 for (y = 0; y < level->fieldy; y++)
3731 level_sp->playfield[x][y] = map_element_RND_to_SP(level->field[x][y]);
3733 header->InitialGravity = (level->initial_player_gravity[0] ? 1 : 0);
3735 for (i = 0; i < SP_LEVEL_NAME_LEN; i++)
3736 header->LevelTitle[i] = level->name[i];
3737 // !!! NO STRING TERMINATION IN SUPAPLEX VB CODE YET -- FIX THIS !!!
3739 header->InfotronsNeeded = level->gems_needed;
3741 header->SpecialPortCount = 0;
3743 for (x = 0; x < level->fieldx; x++) for (y = 0; y < level->fieldy; y++)
3745 boolean gravity_port_found = FALSE;
3746 boolean gravity_port_valid = FALSE;
3747 int gravity_port_flag;
3748 int gravity_port_base_element;
3749 int element = level->field[x][y];
3751 if (element >= EL_SP_GRAVITY_ON_PORT_RIGHT &&
3752 element <= EL_SP_GRAVITY_ON_PORT_UP)
3754 gravity_port_found = TRUE;
3755 gravity_port_valid = TRUE;
3756 gravity_port_flag = 1;
3757 gravity_port_base_element = EL_SP_GRAVITY_ON_PORT_RIGHT;
3759 else if (element >= EL_SP_GRAVITY_OFF_PORT_RIGHT &&
3760 element <= EL_SP_GRAVITY_OFF_PORT_UP)
3762 gravity_port_found = TRUE;
3763 gravity_port_valid = TRUE;
3764 gravity_port_flag = 0;
3765 gravity_port_base_element = EL_SP_GRAVITY_OFF_PORT_RIGHT;
3767 else if (element >= EL_SP_GRAVITY_PORT_RIGHT &&
3768 element <= EL_SP_GRAVITY_PORT_UP)
3770 // change R'n'D style gravity inverting special port to normal port
3771 // (there are no gravity inverting ports in native Supaplex engine)
3773 gravity_port_found = TRUE;
3774 gravity_port_valid = FALSE;
3775 gravity_port_base_element = EL_SP_GRAVITY_PORT_RIGHT;
3778 if (gravity_port_found)
3780 if (gravity_port_valid &&
3781 header->SpecialPortCount < SP_MAX_SPECIAL_PORTS)
3783 SpecialPortType *port = &header->SpecialPort[header->SpecialPortCount];
3785 port->PortLocation = (y * level->fieldx + x) * 2;
3786 port->Gravity = gravity_port_flag;
3788 element += EL_SP_GRAVITY_PORT_RIGHT - gravity_port_base_element;
3790 header->SpecialPortCount++;
3794 // change special gravity port to normal port
3796 element += EL_SP_PORT_RIGHT - gravity_port_base_element;
3799 level_sp->playfield[x][y] = element - EL_SP_START;
3804 static void CopyNativeLevel_SP_to_RND(struct LevelInfo *level)
3806 struct LevelInfo_SP *level_sp = level->native_sp_level;
3807 LevelInfoType *header = &level_sp->header;
3808 boolean num_invalid_elements = 0;
3811 level->fieldx = level_sp->width;
3812 level->fieldy = level_sp->height;
3814 for (x = 0; x < level->fieldx; x++)
3816 for (y = 0; y < level->fieldy; y++)
3818 int element_old = level_sp->playfield[x][y];
3819 int element_new = getMappedElement(map_element_SP_to_RND(element_old));
3821 if (element_new == EL_UNKNOWN)
3823 num_invalid_elements++;
3825 Debug("level:native:SP", "invalid element %d at position %d, %d",
3829 level->field[x][y] = element_new;
3833 if (num_invalid_elements > 0)
3834 Warn("found %d invalid elements%s", num_invalid_elements,
3835 (!options.debug ? " (use '--debug' for more details)" : ""));
3837 for (i = 0; i < MAX_PLAYERS; i++)
3838 level->initial_player_gravity[i] =
3839 (header->InitialGravity == 1 ? TRUE : FALSE);
3841 // skip leading spaces
3842 for (i = 0; i < SP_LEVEL_NAME_LEN; i++)
3843 if (header->LevelTitle[i] != ' ')
3847 for (j = 0; i < SP_LEVEL_NAME_LEN; i++, j++)
3848 level->name[j] = header->LevelTitle[i];
3849 level->name[j] = '\0';
3851 // cut trailing spaces
3853 if (level->name[j - 1] == ' ' && level->name[j] == '\0')
3854 level->name[j - 1] = '\0';
3856 level->gems_needed = header->InfotronsNeeded;
3858 for (i = 0; i < header->SpecialPortCount; i++)
3860 SpecialPortType *port = &header->SpecialPort[i];
3861 int port_location = port->PortLocation;
3862 int gravity = port->Gravity;
3863 int port_x, port_y, port_element;
3865 port_x = (port_location / 2) % level->fieldx;
3866 port_y = (port_location / 2) / level->fieldx;
3868 if (port_x < 0 || port_x >= level->fieldx ||
3869 port_y < 0 || port_y >= level->fieldy)
3871 Warn("special port position (%d, %d) out of bounds", port_x, port_y);
3876 port_element = level->field[port_x][port_y];
3878 if (port_element < EL_SP_GRAVITY_PORT_RIGHT ||
3879 port_element > EL_SP_GRAVITY_PORT_UP)
3881 Warn("no special port at position (%d, %d)", port_x, port_y);
3886 // change previous (wrong) gravity inverting special port to either
3887 // gravity enabling special port or gravity disabling special port
3888 level->field[port_x][port_y] +=
3889 (gravity == 1 ? EL_SP_GRAVITY_ON_PORT_RIGHT :
3890 EL_SP_GRAVITY_OFF_PORT_RIGHT) - EL_SP_GRAVITY_PORT_RIGHT;
3893 // change special gravity ports without database entries to normal ports
3894 for (x = 0; x < level->fieldx; x++)
3895 for (y = 0; y < level->fieldy; y++)
3896 if (level->field[x][y] >= EL_SP_GRAVITY_PORT_RIGHT &&
3897 level->field[x][y] <= EL_SP_GRAVITY_PORT_UP)
3898 level->field[x][y] += EL_SP_PORT_RIGHT - EL_SP_GRAVITY_PORT_RIGHT;
3900 level->time = 0; // no time limit
3901 level->amoeba_speed = 0;
3902 level->time_magic_wall = 0;
3903 level->time_wheel = 0;
3904 level->amoeba_content = EL_EMPTY;
3907 // original Supaplex does not use score values -- use default values
3909 for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
3910 level->score[i] = 0;
3913 // there are no yamyams in supaplex levels
3914 for (i = 0; i < level->num_yamyam_contents; i++)
3915 for (x = 0; x < 3; x++)
3916 for (y = 0; y < 3; y++)
3917 level->yamyam_content[i].e[x][y] = EL_EMPTY;
3920 static void CopyNativeTape_RND_to_SP(struct LevelInfo *level)
3922 struct LevelInfo_SP *level_sp = level->native_sp_level;
3923 struct DemoInfo_SP *demo = &level_sp->demo;
3926 // always start with reliable default values
3927 demo->is_available = FALSE;
3930 if (TAPE_IS_EMPTY(tape))
3933 demo->level_nr = tape.level_nr; // (currently not used)
3935 level_sp->header.DemoRandomSeed = tape.random_seed;
3939 for (i = 0; i < tape.length; i++)
3941 int demo_action = map_key_RND_to_SP(tape.pos[i].action[0]);
3942 int demo_repeat = tape.pos[i].delay;
3943 int demo_entries = (demo_repeat + 15) / 16;
3945 if (demo->length + demo_entries >= SP_MAX_TAPE_LEN)
3947 Warn("tape truncated: size exceeds maximum SP demo size %d",
3953 for (j = 0; j < demo_repeat / 16; j++)
3954 demo->data[demo->length++] = 0xf0 | demo_action;
3956 if (demo_repeat % 16)
3957 demo->data[demo->length++] = ((demo_repeat % 16 - 1) << 4) | demo_action;
3960 demo->is_available = TRUE;
3963 static void setTapeInfoToDefaults(void);
3965 static void CopyNativeTape_SP_to_RND(struct LevelInfo *level)
3967 struct LevelInfo_SP *level_sp = level->native_sp_level;
3968 struct DemoInfo_SP *demo = &level_sp->demo;
3969 char *filename = level->file_info.filename;
3972 // always start with reliable default values
3973 setTapeInfoToDefaults();
3975 if (!demo->is_available)
3978 tape.level_nr = demo->level_nr; // (currently not used)
3979 tape.random_seed = level_sp->header.DemoRandomSeed;
3981 TapeSetDateFromEpochSeconds(getFileTimestampEpochSeconds(filename));
3984 tape.pos[tape.counter].delay = 0;
3986 for (i = 0; i < demo->length; i++)
3988 int demo_action = demo->data[i] & 0x0f;
3989 int demo_repeat = (demo->data[i] & 0xf0) >> 4;
3990 int tape_action = map_key_SP_to_RND(demo_action);
3991 int tape_repeat = demo_repeat + 1;
3992 byte action[MAX_TAPE_ACTIONS] = { tape_action };
3993 boolean success = 0;
3996 for (j = 0; j < tape_repeat; j++)
3997 success = TapeAddAction(action);
4001 Warn("SP demo truncated: size exceeds maximum tape size %d",
4008 TapeHaltRecording();
4012 // ----------------------------------------------------------------------------
4013 // functions for loading MM level
4014 // ----------------------------------------------------------------------------
4016 static void CopyNativeLevel_RND_to_MM(struct LevelInfo *level)
4018 struct LevelInfo_MM *level_mm = level->native_mm_level;
4021 level_mm->fieldx = MIN(level->fieldx, MM_MAX_PLAYFIELD_WIDTH);
4022 level_mm->fieldy = MIN(level->fieldy, MM_MAX_PLAYFIELD_HEIGHT);
4024 level_mm->time = level->time;
4025 level_mm->kettles_needed = level->gems_needed;
4026 level_mm->auto_count_kettles = level->auto_count_gems;
4028 level_mm->laser_red = level->mm_laser_red;
4029 level_mm->laser_green = level->mm_laser_green;
4030 level_mm->laser_blue = level->mm_laser_blue;
4032 strcpy(level_mm->name, level->name);
4033 strcpy(level_mm->author, level->author);
4035 level_mm->score[SC_EMERALD] = level->score[SC_EMERALD];
4036 level_mm->score[SC_PACMAN] = level->score[SC_PACMAN];
4037 level_mm->score[SC_KEY] = level->score[SC_KEY];
4038 level_mm->score[SC_TIME_BONUS] = level->score[SC_TIME_BONUS];
4039 level_mm->score[SC_ELEM_BONUS] = level->score[SC_ELEM_BONUS];
4041 level_mm->amoeba_speed = level->amoeba_speed;
4042 level_mm->time_fuse = level->mm_time_fuse;
4043 level_mm->time_bomb = level->mm_time_bomb;
4044 level_mm->time_ball = level->mm_time_ball;
4045 level_mm->time_block = level->mm_time_block;
4047 for (x = 0; x < level->fieldx; x++)
4048 for (y = 0; y < level->fieldy; y++)
4050 level_mm->field[x][y] = map_element_RND_to_MM(level->field[x][y]);
4053 static void CopyNativeLevel_MM_to_RND(struct LevelInfo *level)
4055 struct LevelInfo_MM *level_mm = level->native_mm_level;
4058 level->fieldx = MIN(level_mm->fieldx, MAX_LEV_FIELDX);
4059 level->fieldy = MIN(level_mm->fieldy, MAX_LEV_FIELDY);
4061 level->time = level_mm->time;
4062 level->gems_needed = level_mm->kettles_needed;
4063 level->auto_count_gems = level_mm->auto_count_kettles;
4065 level->mm_laser_red = level_mm->laser_red;
4066 level->mm_laser_green = level_mm->laser_green;
4067 level->mm_laser_blue = level_mm->laser_blue;
4069 strcpy(level->name, level_mm->name);
4071 // only overwrite author from 'levelinfo.conf' if author defined in level
4072 if (!strEqual(level_mm->author, ANONYMOUS_NAME))
4073 strcpy(level->author, level_mm->author);
4075 level->score[SC_EMERALD] = level_mm->score[SC_EMERALD];
4076 level->score[SC_PACMAN] = level_mm->score[SC_PACMAN];
4077 level->score[SC_KEY] = level_mm->score[SC_KEY];
4078 level->score[SC_TIME_BONUS] = level_mm->score[SC_TIME_BONUS];
4079 level->score[SC_ELEM_BONUS] = level_mm->score[SC_ELEM_BONUS];
4081 level->amoeba_speed = level_mm->amoeba_speed;
4082 level->mm_time_fuse = level_mm->time_fuse;
4083 level->mm_time_bomb = level_mm->time_bomb;
4084 level->mm_time_ball = level_mm->time_ball;
4085 level->mm_time_block = level_mm->time_block;
4087 for (x = 0; x < level->fieldx; x++)
4088 for (y = 0; y < level->fieldy; y++)
4089 level->field[x][y] = map_element_MM_to_RND(level_mm->field[x][y]);
4093 // ----------------------------------------------------------------------------
4094 // functions for loading DC level
4095 // ----------------------------------------------------------------------------
4097 #define DC_LEVEL_HEADER_SIZE 344
4099 static unsigned short getDecodedWord_DC(unsigned short data_encoded,
4102 static int last_data_encoded;
4106 int diff_hi, diff_lo;
4107 int data_hi, data_lo;
4108 unsigned short data_decoded;
4112 last_data_encoded = 0;
4119 diff = data_encoded - last_data_encoded;
4120 diff_hi = diff & ~0xff;
4121 diff_lo = diff & 0xff;
4125 data_hi = diff_hi - (offset1 << 8) + (offset2 & 0xff00);
4126 data_lo = (diff_lo + (data_hi >> 16)) & 0x00ff;
4127 data_hi = data_hi & 0xff00;
4129 data_decoded = data_hi | data_lo;
4131 last_data_encoded = data_encoded;
4133 offset1 = (offset1 + 1) % 31;
4134 offset2 = offset2 & 0xff;
4136 return data_decoded;
4139 static int getMappedElement_DC(int element)
4147 // 0x0117 - 0x036e: (?)
4150 // 0x042d - 0x0684: (?)
4166 element = EL_CRYSTAL;
4169 case 0x0e77: // quicksand (boulder)
4170 element = EL_QUICKSAND_FAST_FULL;
4173 case 0x0e99: // slow quicksand (boulder)
4174 element = EL_QUICKSAND_FULL;
4178 element = EL_EM_EXIT_OPEN;
4182 element = EL_EM_EXIT_CLOSED;
4186 element = EL_EM_STEEL_EXIT_OPEN;
4190 element = EL_EM_STEEL_EXIT_CLOSED;
4193 case 0x0f4f: // dynamite (lit 1)
4194 element = EL_EM_DYNAMITE_ACTIVE;
4197 case 0x0f57: // dynamite (lit 2)
4198 element = EL_EM_DYNAMITE_ACTIVE;
4201 case 0x0f5f: // dynamite (lit 3)
4202 element = EL_EM_DYNAMITE_ACTIVE;
4205 case 0x0f67: // dynamite (lit 4)
4206 element = EL_EM_DYNAMITE_ACTIVE;
4213 element = EL_AMOEBA_WET;
4217 element = EL_AMOEBA_DROP;
4221 element = EL_DC_MAGIC_WALL;
4225 element = EL_SPACESHIP_UP;
4229 element = EL_SPACESHIP_DOWN;
4233 element = EL_SPACESHIP_LEFT;
4237 element = EL_SPACESHIP_RIGHT;
4241 element = EL_BUG_UP;
4245 element = EL_BUG_DOWN;
4249 element = EL_BUG_LEFT;
4253 element = EL_BUG_RIGHT;
4257 element = EL_MOLE_UP;
4261 element = EL_MOLE_DOWN;
4265 element = EL_MOLE_LEFT;
4269 element = EL_MOLE_RIGHT;
4277 element = EL_YAMYAM_UP;
4281 element = EL_SWITCHGATE_OPEN;
4285 element = EL_SWITCHGATE_CLOSED;
4289 element = EL_DC_SWITCHGATE_SWITCH_UP;
4293 element = EL_TIMEGATE_CLOSED;
4296 case 0x144c: // conveyor belt switch (green)
4297 element = EL_CONVEYOR_BELT_3_SWITCH_MIDDLE;
4300 case 0x144f: // conveyor belt switch (red)
4301 element = EL_CONVEYOR_BELT_1_SWITCH_MIDDLE;
4304 case 0x1452: // conveyor belt switch (blue)
4305 element = EL_CONVEYOR_BELT_4_SWITCH_MIDDLE;
4309 element = EL_CONVEYOR_BELT_3_MIDDLE;
4313 element = EL_CONVEYOR_BELT_3_LEFT;
4317 element = EL_CONVEYOR_BELT_3_RIGHT;
4321 element = EL_CONVEYOR_BELT_1_MIDDLE;
4325 element = EL_CONVEYOR_BELT_1_LEFT;
4329 element = EL_CONVEYOR_BELT_1_RIGHT;
4333 element = EL_CONVEYOR_BELT_4_MIDDLE;
4337 element = EL_CONVEYOR_BELT_4_LEFT;
4341 element = EL_CONVEYOR_BELT_4_RIGHT;
4345 element = EL_EXPANDABLE_WALL_HORIZONTAL;
4349 element = EL_EXPANDABLE_WALL_VERTICAL;
4353 element = EL_EXPANDABLE_WALL_ANY;
4356 case 0x14ce: // growing steel wall (left/right)
4357 element = EL_EXPANDABLE_STEELWALL_HORIZONTAL;
4360 case 0x14df: // growing steel wall (up/down)
4361 element = EL_EXPANDABLE_STEELWALL_VERTICAL;
4364 case 0x14e8: // growing steel wall (up/down/left/right)
4365 element = EL_EXPANDABLE_STEELWALL_ANY;
4369 element = EL_SHIELD_DEADLY;
4373 element = EL_EXTRA_TIME;
4381 element = EL_EMPTY_SPACE;
4384 case 0x1578: // quicksand (empty)
4385 element = EL_QUICKSAND_FAST_EMPTY;
4388 case 0x1579: // slow quicksand (empty)
4389 element = EL_QUICKSAND_EMPTY;
4399 element = EL_EM_DYNAMITE;
4402 case 0x15a1: // key (red)
4403 element = EL_EM_KEY_1;
4406 case 0x15a2: // key (yellow)
4407 element = EL_EM_KEY_2;
4410 case 0x15a3: // key (blue)
4411 element = EL_EM_KEY_4;
4414 case 0x15a4: // key (green)
4415 element = EL_EM_KEY_3;
4418 case 0x15a5: // key (white)
4419 element = EL_DC_KEY_WHITE;
4423 element = EL_WALL_SLIPPERY;
4430 case 0x15a8: // wall (not round)
4434 case 0x15a9: // (blue)
4435 element = EL_CHAR_A;
4438 case 0x15aa: // (blue)
4439 element = EL_CHAR_B;
4442 case 0x15ab: // (blue)
4443 element = EL_CHAR_C;
4446 case 0x15ac: // (blue)
4447 element = EL_CHAR_D;
4450 case 0x15ad: // (blue)
4451 element = EL_CHAR_E;
4454 case 0x15ae: // (blue)
4455 element = EL_CHAR_F;
4458 case 0x15af: // (blue)
4459 element = EL_CHAR_G;
4462 case 0x15b0: // (blue)
4463 element = EL_CHAR_H;
4466 case 0x15b1: // (blue)
4467 element = EL_CHAR_I;
4470 case 0x15b2: // (blue)
4471 element = EL_CHAR_J;
4474 case 0x15b3: // (blue)
4475 element = EL_CHAR_K;
4478 case 0x15b4: // (blue)
4479 element = EL_CHAR_L;
4482 case 0x15b5: // (blue)
4483 element = EL_CHAR_M;
4486 case 0x15b6: // (blue)
4487 element = EL_CHAR_N;
4490 case 0x15b7: // (blue)
4491 element = EL_CHAR_O;
4494 case 0x15b8: // (blue)
4495 element = EL_CHAR_P;
4498 case 0x15b9: // (blue)
4499 element = EL_CHAR_Q;
4502 case 0x15ba: // (blue)
4503 element = EL_CHAR_R;
4506 case 0x15bb: // (blue)
4507 element = EL_CHAR_S;
4510 case 0x15bc: // (blue)
4511 element = EL_CHAR_T;
4514 case 0x15bd: // (blue)
4515 element = EL_CHAR_U;
4518 case 0x15be: // (blue)
4519 element = EL_CHAR_V;
4522 case 0x15bf: // (blue)
4523 element = EL_CHAR_W;
4526 case 0x15c0: // (blue)
4527 element = EL_CHAR_X;
4530 case 0x15c1: // (blue)
4531 element = EL_CHAR_Y;
4534 case 0x15c2: // (blue)
4535 element = EL_CHAR_Z;
4538 case 0x15c3: // (blue)
4539 element = EL_CHAR_AUMLAUT;
4542 case 0x15c4: // (blue)
4543 element = EL_CHAR_OUMLAUT;
4546 case 0x15c5: // (blue)
4547 element = EL_CHAR_UUMLAUT;
4550 case 0x15c6: // (blue)
4551 element = EL_CHAR_0;
4554 case 0x15c7: // (blue)
4555 element = EL_CHAR_1;
4558 case 0x15c8: // (blue)
4559 element = EL_CHAR_2;
4562 case 0x15c9: // (blue)
4563 element = EL_CHAR_3;
4566 case 0x15ca: // (blue)
4567 element = EL_CHAR_4;
4570 case 0x15cb: // (blue)
4571 element = EL_CHAR_5;
4574 case 0x15cc: // (blue)
4575 element = EL_CHAR_6;
4578 case 0x15cd: // (blue)
4579 element = EL_CHAR_7;
4582 case 0x15ce: // (blue)
4583 element = EL_CHAR_8;
4586 case 0x15cf: // (blue)
4587 element = EL_CHAR_9;
4590 case 0x15d0: // (blue)
4591 element = EL_CHAR_PERIOD;
4594 case 0x15d1: // (blue)
4595 element = EL_CHAR_EXCLAM;
4598 case 0x15d2: // (blue)
4599 element = EL_CHAR_COLON;
4602 case 0x15d3: // (blue)
4603 element = EL_CHAR_LESS;
4606 case 0x15d4: // (blue)
4607 element = EL_CHAR_GREATER;
4610 case 0x15d5: // (blue)
4611 element = EL_CHAR_QUESTION;
4614 case 0x15d6: // (blue)
4615 element = EL_CHAR_COPYRIGHT;
4618 case 0x15d7: // (blue)
4619 element = EL_CHAR_UP;
4622 case 0x15d8: // (blue)
4623 element = EL_CHAR_DOWN;
4626 case 0x15d9: // (blue)
4627 element = EL_CHAR_BUTTON;
4630 case 0x15da: // (blue)
4631 element = EL_CHAR_PLUS;
4634 case 0x15db: // (blue)
4635 element = EL_CHAR_MINUS;
4638 case 0x15dc: // (blue)
4639 element = EL_CHAR_APOSTROPHE;
4642 case 0x15dd: // (blue)
4643 element = EL_CHAR_PARENLEFT;
4646 case 0x15de: // (blue)
4647 element = EL_CHAR_PARENRIGHT;
4650 case 0x15df: // (green)
4651 element = EL_CHAR_A;
4654 case 0x15e0: // (green)
4655 element = EL_CHAR_B;
4658 case 0x15e1: // (green)
4659 element = EL_CHAR_C;
4662 case 0x15e2: // (green)
4663 element = EL_CHAR_D;
4666 case 0x15e3: // (green)
4667 element = EL_CHAR_E;
4670 case 0x15e4: // (green)
4671 element = EL_CHAR_F;
4674 case 0x15e5: // (green)
4675 element = EL_CHAR_G;
4678 case 0x15e6: // (green)
4679 element = EL_CHAR_H;
4682 case 0x15e7: // (green)
4683 element = EL_CHAR_I;
4686 case 0x15e8: // (green)
4687 element = EL_CHAR_J;
4690 case 0x15e9: // (green)
4691 element = EL_CHAR_K;
4694 case 0x15ea: // (green)
4695 element = EL_CHAR_L;
4698 case 0x15eb: // (green)
4699 element = EL_CHAR_M;
4702 case 0x15ec: // (green)
4703 element = EL_CHAR_N;
4706 case 0x15ed: // (green)
4707 element = EL_CHAR_O;
4710 case 0x15ee: // (green)
4711 element = EL_CHAR_P;
4714 case 0x15ef: // (green)
4715 element = EL_CHAR_Q;
4718 case 0x15f0: // (green)
4719 element = EL_CHAR_R;
4722 case 0x15f1: // (green)
4723 element = EL_CHAR_S;
4726 case 0x15f2: // (green)
4727 element = EL_CHAR_T;
4730 case 0x15f3: // (green)
4731 element = EL_CHAR_U;
4734 case 0x15f4: // (green)
4735 element = EL_CHAR_V;
4738 case 0x15f5: // (green)
4739 element = EL_CHAR_W;
4742 case 0x15f6: // (green)
4743 element = EL_CHAR_X;
4746 case 0x15f7: // (green)
4747 element = EL_CHAR_Y;
4750 case 0x15f8: // (green)
4751 element = EL_CHAR_Z;
4754 case 0x15f9: // (green)
4755 element = EL_CHAR_AUMLAUT;
4758 case 0x15fa: // (green)
4759 element = EL_CHAR_OUMLAUT;
4762 case 0x15fb: // (green)
4763 element = EL_CHAR_UUMLAUT;
4766 case 0x15fc: // (green)
4767 element = EL_CHAR_0;
4770 case 0x15fd: // (green)
4771 element = EL_CHAR_1;
4774 case 0x15fe: // (green)
4775 element = EL_CHAR_2;
4778 case 0x15ff: // (green)
4779 element = EL_CHAR_3;
4782 case 0x1600: // (green)
4783 element = EL_CHAR_4;
4786 case 0x1601: // (green)
4787 element = EL_CHAR_5;
4790 case 0x1602: // (green)
4791 element = EL_CHAR_6;
4794 case 0x1603: // (green)
4795 element = EL_CHAR_7;
4798 case 0x1604: // (green)
4799 element = EL_CHAR_8;
4802 case 0x1605: // (green)
4803 element = EL_CHAR_9;
4806 case 0x1606: // (green)
4807 element = EL_CHAR_PERIOD;
4810 case 0x1607: // (green)
4811 element = EL_CHAR_EXCLAM;
4814 case 0x1608: // (green)
4815 element = EL_CHAR_COLON;
4818 case 0x1609: // (green)
4819 element = EL_CHAR_LESS;
4822 case 0x160a: // (green)
4823 element = EL_CHAR_GREATER;
4826 case 0x160b: // (green)
4827 element = EL_CHAR_QUESTION;
4830 case 0x160c: // (green)
4831 element = EL_CHAR_COPYRIGHT;
4834 case 0x160d: // (green)
4835 element = EL_CHAR_UP;
4838 case 0x160e: // (green)
4839 element = EL_CHAR_DOWN;
4842 case 0x160f: // (green)
4843 element = EL_CHAR_BUTTON;
4846 case 0x1610: // (green)
4847 element = EL_CHAR_PLUS;
4850 case 0x1611: // (green)
4851 element = EL_CHAR_MINUS;
4854 case 0x1612: // (green)
4855 element = EL_CHAR_APOSTROPHE;
4858 case 0x1613: // (green)
4859 element = EL_CHAR_PARENLEFT;
4862 case 0x1614: // (green)
4863 element = EL_CHAR_PARENRIGHT;
4866 case 0x1615: // (blue steel)
4867 element = EL_STEEL_CHAR_A;
4870 case 0x1616: // (blue steel)
4871 element = EL_STEEL_CHAR_B;
4874 case 0x1617: // (blue steel)
4875 element = EL_STEEL_CHAR_C;
4878 case 0x1618: // (blue steel)
4879 element = EL_STEEL_CHAR_D;
4882 case 0x1619: // (blue steel)
4883 element = EL_STEEL_CHAR_E;
4886 case 0x161a: // (blue steel)
4887 element = EL_STEEL_CHAR_F;
4890 case 0x161b: // (blue steel)
4891 element = EL_STEEL_CHAR_G;
4894 case 0x161c: // (blue steel)
4895 element = EL_STEEL_CHAR_H;
4898 case 0x161d: // (blue steel)
4899 element = EL_STEEL_CHAR_I;
4902 case 0x161e: // (blue steel)
4903 element = EL_STEEL_CHAR_J;
4906 case 0x161f: // (blue steel)
4907 element = EL_STEEL_CHAR_K;
4910 case 0x1620: // (blue steel)
4911 element = EL_STEEL_CHAR_L;
4914 case 0x1621: // (blue steel)
4915 element = EL_STEEL_CHAR_M;
4918 case 0x1622: // (blue steel)
4919 element = EL_STEEL_CHAR_N;
4922 case 0x1623: // (blue steel)
4923 element = EL_STEEL_CHAR_O;
4926 case 0x1624: // (blue steel)
4927 element = EL_STEEL_CHAR_P;
4930 case 0x1625: // (blue steel)
4931 element = EL_STEEL_CHAR_Q;
4934 case 0x1626: // (blue steel)
4935 element = EL_STEEL_CHAR_R;
4938 case 0x1627: // (blue steel)
4939 element = EL_STEEL_CHAR_S;
4942 case 0x1628: // (blue steel)
4943 element = EL_STEEL_CHAR_T;
4946 case 0x1629: // (blue steel)
4947 element = EL_STEEL_CHAR_U;
4950 case 0x162a: // (blue steel)
4951 element = EL_STEEL_CHAR_V;
4954 case 0x162b: // (blue steel)
4955 element = EL_STEEL_CHAR_W;
4958 case 0x162c: // (blue steel)
4959 element = EL_STEEL_CHAR_X;
4962 case 0x162d: // (blue steel)
4963 element = EL_STEEL_CHAR_Y;
4966 case 0x162e: // (blue steel)
4967 element = EL_STEEL_CHAR_Z;
4970 case 0x162f: // (blue steel)
4971 element = EL_STEEL_CHAR_AUMLAUT;
4974 case 0x1630: // (blue steel)
4975 element = EL_STEEL_CHAR_OUMLAUT;
4978 case 0x1631: // (blue steel)
4979 element = EL_STEEL_CHAR_UUMLAUT;
4982 case 0x1632: // (blue steel)
4983 element = EL_STEEL_CHAR_0;
4986 case 0x1633: // (blue steel)
4987 element = EL_STEEL_CHAR_1;
4990 case 0x1634: // (blue steel)
4991 element = EL_STEEL_CHAR_2;
4994 case 0x1635: // (blue steel)
4995 element = EL_STEEL_CHAR_3;
4998 case 0x1636: // (blue steel)
4999 element = EL_STEEL_CHAR_4;
5002 case 0x1637: // (blue steel)
5003 element = EL_STEEL_CHAR_5;
5006 case 0x1638: // (blue steel)
5007 element = EL_STEEL_CHAR_6;
5010 case 0x1639: // (blue steel)
5011 element = EL_STEEL_CHAR_7;
5014 case 0x163a: // (blue steel)
5015 element = EL_STEEL_CHAR_8;
5018 case 0x163b: // (blue steel)
5019 element = EL_STEEL_CHAR_9;
5022 case 0x163c: // (blue steel)
5023 element = EL_STEEL_CHAR_PERIOD;
5026 case 0x163d: // (blue steel)
5027 element = EL_STEEL_CHAR_EXCLAM;
5030 case 0x163e: // (blue steel)
5031 element = EL_STEEL_CHAR_COLON;
5034 case 0x163f: // (blue steel)
5035 element = EL_STEEL_CHAR_LESS;
5038 case 0x1640: // (blue steel)
5039 element = EL_STEEL_CHAR_GREATER;
5042 case 0x1641: // (blue steel)
5043 element = EL_STEEL_CHAR_QUESTION;
5046 case 0x1642: // (blue steel)
5047 element = EL_STEEL_CHAR_COPYRIGHT;
5050 case 0x1643: // (blue steel)
5051 element = EL_STEEL_CHAR_UP;
5054 case 0x1644: // (blue steel)
5055 element = EL_STEEL_CHAR_DOWN;
5058 case 0x1645: // (blue steel)
5059 element = EL_STEEL_CHAR_BUTTON;
5062 case 0x1646: // (blue steel)
5063 element = EL_STEEL_CHAR_PLUS;
5066 case 0x1647: // (blue steel)
5067 element = EL_STEEL_CHAR_MINUS;
5070 case 0x1648: // (blue steel)
5071 element = EL_STEEL_CHAR_APOSTROPHE;
5074 case 0x1649: // (blue steel)
5075 element = EL_STEEL_CHAR_PARENLEFT;
5078 case 0x164a: // (blue steel)
5079 element = EL_STEEL_CHAR_PARENRIGHT;
5082 case 0x164b: // (green steel)
5083 element = EL_STEEL_CHAR_A;
5086 case 0x164c: // (green steel)
5087 element = EL_STEEL_CHAR_B;
5090 case 0x164d: // (green steel)
5091 element = EL_STEEL_CHAR_C;
5094 case 0x164e: // (green steel)
5095 element = EL_STEEL_CHAR_D;
5098 case 0x164f: // (green steel)
5099 element = EL_STEEL_CHAR_E;
5102 case 0x1650: // (green steel)
5103 element = EL_STEEL_CHAR_F;
5106 case 0x1651: // (green steel)
5107 element = EL_STEEL_CHAR_G;
5110 case 0x1652: // (green steel)
5111 element = EL_STEEL_CHAR_H;
5114 case 0x1653: // (green steel)
5115 element = EL_STEEL_CHAR_I;
5118 case 0x1654: // (green steel)
5119 element = EL_STEEL_CHAR_J;
5122 case 0x1655: // (green steel)
5123 element = EL_STEEL_CHAR_K;
5126 case 0x1656: // (green steel)
5127 element = EL_STEEL_CHAR_L;
5130 case 0x1657: // (green steel)
5131 element = EL_STEEL_CHAR_M;
5134 case 0x1658: // (green steel)
5135 element = EL_STEEL_CHAR_N;
5138 case 0x1659: // (green steel)
5139 element = EL_STEEL_CHAR_O;
5142 case 0x165a: // (green steel)
5143 element = EL_STEEL_CHAR_P;
5146 case 0x165b: // (green steel)
5147 element = EL_STEEL_CHAR_Q;
5150 case 0x165c: // (green steel)
5151 element = EL_STEEL_CHAR_R;
5154 case 0x165d: // (green steel)
5155 element = EL_STEEL_CHAR_S;
5158 case 0x165e: // (green steel)
5159 element = EL_STEEL_CHAR_T;
5162 case 0x165f: // (green steel)
5163 element = EL_STEEL_CHAR_U;
5166 case 0x1660: // (green steel)
5167 element = EL_STEEL_CHAR_V;
5170 case 0x1661: // (green steel)
5171 element = EL_STEEL_CHAR_W;
5174 case 0x1662: // (green steel)
5175 element = EL_STEEL_CHAR_X;
5178 case 0x1663: // (green steel)
5179 element = EL_STEEL_CHAR_Y;
5182 case 0x1664: // (green steel)
5183 element = EL_STEEL_CHAR_Z;
5186 case 0x1665: // (green steel)
5187 element = EL_STEEL_CHAR_AUMLAUT;
5190 case 0x1666: // (green steel)
5191 element = EL_STEEL_CHAR_OUMLAUT;
5194 case 0x1667: // (green steel)
5195 element = EL_STEEL_CHAR_UUMLAUT;
5198 case 0x1668: // (green steel)
5199 element = EL_STEEL_CHAR_0;
5202 case 0x1669: // (green steel)
5203 element = EL_STEEL_CHAR_1;
5206 case 0x166a: // (green steel)
5207 element = EL_STEEL_CHAR_2;
5210 case 0x166b: // (green steel)
5211 element = EL_STEEL_CHAR_3;
5214 case 0x166c: // (green steel)
5215 element = EL_STEEL_CHAR_4;
5218 case 0x166d: // (green steel)
5219 element = EL_STEEL_CHAR_5;
5222 case 0x166e: // (green steel)
5223 element = EL_STEEL_CHAR_6;
5226 case 0x166f: // (green steel)
5227 element = EL_STEEL_CHAR_7;
5230 case 0x1670: // (green steel)
5231 element = EL_STEEL_CHAR_8;
5234 case 0x1671: // (green steel)
5235 element = EL_STEEL_CHAR_9;
5238 case 0x1672: // (green steel)
5239 element = EL_STEEL_CHAR_PERIOD;
5242 case 0x1673: // (green steel)
5243 element = EL_STEEL_CHAR_EXCLAM;
5246 case 0x1674: // (green steel)
5247 element = EL_STEEL_CHAR_COLON;
5250 case 0x1675: // (green steel)
5251 element = EL_STEEL_CHAR_LESS;
5254 case 0x1676: // (green steel)
5255 element = EL_STEEL_CHAR_GREATER;
5258 case 0x1677: // (green steel)
5259 element = EL_STEEL_CHAR_QUESTION;
5262 case 0x1678: // (green steel)
5263 element = EL_STEEL_CHAR_COPYRIGHT;
5266 case 0x1679: // (green steel)
5267 element = EL_STEEL_CHAR_UP;
5270 case 0x167a: // (green steel)
5271 element = EL_STEEL_CHAR_DOWN;
5274 case 0x167b: // (green steel)
5275 element = EL_STEEL_CHAR_BUTTON;
5278 case 0x167c: // (green steel)
5279 element = EL_STEEL_CHAR_PLUS;
5282 case 0x167d: // (green steel)
5283 element = EL_STEEL_CHAR_MINUS;
5286 case 0x167e: // (green steel)
5287 element = EL_STEEL_CHAR_APOSTROPHE;
5290 case 0x167f: // (green steel)
5291 element = EL_STEEL_CHAR_PARENLEFT;
5294 case 0x1680: // (green steel)
5295 element = EL_STEEL_CHAR_PARENRIGHT;
5298 case 0x1681: // gate (red)
5299 element = EL_EM_GATE_1;
5302 case 0x1682: // secret gate (red)
5303 element = EL_EM_GATE_1_GRAY;
5306 case 0x1683: // gate (yellow)
5307 element = EL_EM_GATE_2;
5310 case 0x1684: // secret gate (yellow)
5311 element = EL_EM_GATE_2_GRAY;
5314 case 0x1685: // gate (blue)
5315 element = EL_EM_GATE_4;
5318 case 0x1686: // secret gate (blue)
5319 element = EL_EM_GATE_4_GRAY;
5322 case 0x1687: // gate (green)
5323 element = EL_EM_GATE_3;
5326 case 0x1688: // secret gate (green)
5327 element = EL_EM_GATE_3_GRAY;
5330 case 0x1689: // gate (white)
5331 element = EL_DC_GATE_WHITE;
5334 case 0x168a: // secret gate (white)
5335 element = EL_DC_GATE_WHITE_GRAY;
5338 case 0x168b: // secret gate (no key)
5339 element = EL_DC_GATE_FAKE_GRAY;
5343 element = EL_ROBOT_WHEEL;
5347 element = EL_DC_TIMEGATE_SWITCH;
5351 element = EL_ACID_POOL_BOTTOM;
5355 element = EL_ACID_POOL_TOPLEFT;
5359 element = EL_ACID_POOL_TOPRIGHT;
5363 element = EL_ACID_POOL_BOTTOMLEFT;
5367 element = EL_ACID_POOL_BOTTOMRIGHT;
5371 element = EL_STEELWALL;
5375 element = EL_STEELWALL_SLIPPERY;
5378 case 0x1695: // steel wall (not round)
5379 element = EL_STEELWALL;
5382 case 0x1696: // steel wall (left)
5383 element = EL_DC_STEELWALL_1_LEFT;
5386 case 0x1697: // steel wall (bottom)
5387 element = EL_DC_STEELWALL_1_BOTTOM;
5390 case 0x1698: // steel wall (right)
5391 element = EL_DC_STEELWALL_1_RIGHT;
5394 case 0x1699: // steel wall (top)
5395 element = EL_DC_STEELWALL_1_TOP;
5398 case 0x169a: // steel wall (left/bottom)
5399 element = EL_DC_STEELWALL_1_BOTTOMLEFT;
5402 case 0x169b: // steel wall (right/bottom)
5403 element = EL_DC_STEELWALL_1_BOTTOMRIGHT;
5406 case 0x169c: // steel wall (right/top)
5407 element = EL_DC_STEELWALL_1_TOPRIGHT;
5410 case 0x169d: // steel wall (left/top)
5411 element = EL_DC_STEELWALL_1_TOPLEFT;
5414 case 0x169e: // steel wall (right/bottom small)
5415 element = EL_DC_STEELWALL_1_BOTTOMRIGHT_2;
5418 case 0x169f: // steel wall (left/bottom small)
5419 element = EL_DC_STEELWALL_1_BOTTOMLEFT_2;
5422 case 0x16a0: // steel wall (right/top small)
5423 element = EL_DC_STEELWALL_1_TOPRIGHT_2;
5426 case 0x16a1: // steel wall (left/top small)
5427 element = EL_DC_STEELWALL_1_TOPLEFT_2;
5430 case 0x16a2: // steel wall (left/right)
5431 element = EL_DC_STEELWALL_1_VERTICAL;
5434 case 0x16a3: // steel wall (top/bottom)
5435 element = EL_DC_STEELWALL_1_HORIZONTAL;
5438 case 0x16a4: // steel wall 2 (left end)
5439 element = EL_DC_STEELWALL_2_LEFT;
5442 case 0x16a5: // steel wall 2 (right end)
5443 element = EL_DC_STEELWALL_2_RIGHT;
5446 case 0x16a6: // steel wall 2 (top end)
5447 element = EL_DC_STEELWALL_2_TOP;
5450 case 0x16a7: // steel wall 2 (bottom end)
5451 element = EL_DC_STEELWALL_2_BOTTOM;
5454 case 0x16a8: // steel wall 2 (left/right)
5455 element = EL_DC_STEELWALL_2_HORIZONTAL;
5458 case 0x16a9: // steel wall 2 (up/down)
5459 element = EL_DC_STEELWALL_2_VERTICAL;
5462 case 0x16aa: // steel wall 2 (mid)
5463 element = EL_DC_STEELWALL_2_MIDDLE;
5467 element = EL_SIGN_EXCLAMATION;
5471 element = EL_SIGN_RADIOACTIVITY;
5475 element = EL_SIGN_STOP;
5479 element = EL_SIGN_WHEELCHAIR;
5483 element = EL_SIGN_PARKING;
5487 element = EL_SIGN_NO_ENTRY;
5491 element = EL_SIGN_HEART;
5495 element = EL_SIGN_GIVE_WAY;
5499 element = EL_SIGN_ENTRY_FORBIDDEN;
5503 element = EL_SIGN_EMERGENCY_EXIT;
5507 element = EL_SIGN_YIN_YANG;
5511 element = EL_WALL_EMERALD;
5515 element = EL_WALL_DIAMOND;
5519 element = EL_WALL_PEARL;
5523 element = EL_WALL_CRYSTAL;
5527 element = EL_INVISIBLE_WALL;
5531 element = EL_INVISIBLE_STEELWALL;
5535 // EL_INVISIBLE_SAND
5538 element = EL_LIGHT_SWITCH;
5542 element = EL_ENVELOPE_1;
5546 if (element >= 0x0117 && element <= 0x036e) // (?)
5547 element = EL_DIAMOND;
5548 else if (element >= 0x042d && element <= 0x0684) // (?)
5549 element = EL_EMERALD;
5550 else if (element >= 0x157c && element <= 0x158b)
5552 else if (element >= 0x1590 && element <= 0x159f)
5553 element = EL_DC_LANDMINE;
5554 else if (element >= 0x16bc && element <= 0x16cb)
5555 element = EL_INVISIBLE_SAND;
5558 Warn("unknown Diamond Caves element 0x%04x", element);
5560 element = EL_UNKNOWN;
5565 return getMappedElement(element);
5568 static void LoadLevelFromFileStream_DC(File *file, struct LevelInfo *level,
5571 byte header[DC_LEVEL_HEADER_SIZE];
5573 int envelope_header_pos = 62;
5574 int envelope_content_pos = 94;
5575 int level_name_pos = 251;
5576 int level_author_pos = 292;
5577 int envelope_header_len;
5578 int envelope_content_len;
5580 int level_author_len;
5582 int num_yamyam_contents;
5585 getDecodedWord_DC(0, TRUE); // initialize DC2 decoding engine
5587 for (i = 0; i < DC_LEVEL_HEADER_SIZE / 2; i++)
5589 unsigned short header_word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
5591 header[i * 2 + 0] = header_word >> 8;
5592 header[i * 2 + 1] = header_word & 0xff;
5595 // read some values from level header to check level decoding integrity
5596 fieldx = header[6] | (header[7] << 8);
5597 fieldy = header[8] | (header[9] << 8);
5598 num_yamyam_contents = header[60] | (header[61] << 8);
5600 // do some simple sanity checks to ensure that level was correctly decoded
5601 if (fieldx < 1 || fieldx > 256 ||
5602 fieldy < 1 || fieldy > 256 ||
5603 num_yamyam_contents < 1 || num_yamyam_contents > 8)
5605 level->no_valid_file = TRUE;
5607 Warn("cannot decode level from stream -- using empty level");
5612 // maximum envelope header size is 31 bytes
5613 envelope_header_len = header[envelope_header_pos];
5614 // maximum envelope content size is 110 (156?) bytes
5615 envelope_content_len = header[envelope_content_pos];
5617 // maximum level title size is 40 bytes
5618 level_name_len = MIN(header[level_name_pos], MAX_LEVEL_NAME_LEN);
5619 // maximum level author size is 30 (51?) bytes
5620 level_author_len = MIN(header[level_author_pos], MAX_LEVEL_AUTHOR_LEN);
5624 for (i = 0; i < envelope_header_len; i++)
5625 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
5626 level->envelope[0].text[envelope_size++] =
5627 header[envelope_header_pos + 1 + i];
5629 if (envelope_header_len > 0 && envelope_content_len > 0)
5631 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
5632 level->envelope[0].text[envelope_size++] = '\n';
5633 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
5634 level->envelope[0].text[envelope_size++] = '\n';
5637 for (i = 0; i < envelope_content_len; i++)
5638 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
5639 level->envelope[0].text[envelope_size++] =
5640 header[envelope_content_pos + 1 + i];
5642 level->envelope[0].text[envelope_size] = '\0';
5644 level->envelope[0].xsize = MAX_ENVELOPE_XSIZE;
5645 level->envelope[0].ysize = 10;
5646 level->envelope[0].autowrap = TRUE;
5647 level->envelope[0].centered = TRUE;
5649 for (i = 0; i < level_name_len; i++)
5650 level->name[i] = header[level_name_pos + 1 + i];
5651 level->name[level_name_len] = '\0';
5653 for (i = 0; i < level_author_len; i++)
5654 level->author[i] = header[level_author_pos + 1 + i];
5655 level->author[level_author_len] = '\0';
5657 num_yamyam_contents = header[60] | (header[61] << 8);
5658 level->num_yamyam_contents =
5659 MIN(MAX(MIN_ELEMENT_CONTENTS, num_yamyam_contents), MAX_ELEMENT_CONTENTS);
5661 for (i = 0; i < num_yamyam_contents; i++)
5663 for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
5665 unsigned short word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
5666 int element_dc = ((word & 0xff) << 8) | ((word >> 8) & 0xff);
5668 if (i < MAX_ELEMENT_CONTENTS)
5669 level->yamyam_content[i].e[x][y] = getMappedElement_DC(element_dc);
5673 fieldx = header[6] | (header[7] << 8);
5674 fieldy = header[8] | (header[9] << 8);
5675 level->fieldx = MIN(MAX(MIN_LEV_FIELDX, fieldx), MAX_LEV_FIELDX);
5676 level->fieldy = MIN(MAX(MIN_LEV_FIELDY, fieldy), MAX_LEV_FIELDY);
5678 for (y = 0; y < fieldy; y++) for (x = 0; x < fieldx; x++)
5680 unsigned short word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
5681 int element_dc = ((word & 0xff) << 8) | ((word >> 8) & 0xff);
5683 if (x < MAX_LEV_FIELDX && y < MAX_LEV_FIELDY)
5684 level->field[x][y] = getMappedElement_DC(element_dc);
5687 x = MIN(MAX(0, (header[10] | (header[11] << 8)) - 1), MAX_LEV_FIELDX - 1);
5688 y = MIN(MAX(0, (header[12] | (header[13] << 8)) - 1), MAX_LEV_FIELDY - 1);
5689 level->field[x][y] = EL_PLAYER_1;
5691 x = MIN(MAX(0, (header[14] | (header[15] << 8)) - 1), MAX_LEV_FIELDX - 1);
5692 y = MIN(MAX(0, (header[16] | (header[17] << 8)) - 1), MAX_LEV_FIELDY - 1);
5693 level->field[x][y] = EL_PLAYER_2;
5695 level->gems_needed = header[18] | (header[19] << 8);
5697 level->score[SC_EMERALD] = header[20] | (header[21] << 8);
5698 level->score[SC_DIAMOND] = header[22] | (header[23] << 8);
5699 level->score[SC_PEARL] = header[24] | (header[25] << 8);
5700 level->score[SC_CRYSTAL] = header[26] | (header[27] << 8);
5701 level->score[SC_NUT] = header[28] | (header[29] << 8);
5702 level->score[SC_ROBOT] = header[30] | (header[31] << 8);
5703 level->score[SC_SPACESHIP] = header[32] | (header[33] << 8);
5704 level->score[SC_BUG] = header[34] | (header[35] << 8);
5705 level->score[SC_YAMYAM] = header[36] | (header[37] << 8);
5706 level->score[SC_DYNAMITE] = header[38] | (header[39] << 8);
5707 level->score[SC_KEY] = header[40] | (header[41] << 8);
5708 level->score[SC_TIME_BONUS] = header[42] | (header[43] << 8);
5710 level->time = header[44] | (header[45] << 8);
5712 level->amoeba_speed = header[46] | (header[47] << 8);
5713 level->time_light = header[48] | (header[49] << 8);
5714 level->time_timegate = header[50] | (header[51] << 8);
5715 level->time_wheel = header[52] | (header[53] << 8);
5716 level->time_magic_wall = header[54] | (header[55] << 8);
5717 level->extra_time = header[56] | (header[57] << 8);
5718 level->shield_normal_time = header[58] | (header[59] << 8);
5720 // shield and extra time elements do not have a score
5721 level->score[SC_SHIELD] = 0;
5722 level->extra_time_score = 0;
5724 // set time for normal and deadly shields to the same value
5725 level->shield_deadly_time = level->shield_normal_time;
5727 // Diamond Caves has the same (strange) behaviour as Emerald Mine that gems
5728 // can slip down from flat walls, like normal walls and steel walls
5729 level->em_slippery_gems = TRUE;
5732 static void LoadLevelFromFileInfo_DC(struct LevelInfo *level,
5733 struct LevelFileInfo *level_file_info,
5734 boolean level_info_only)
5736 char *filename = level_file_info->filename;
5738 int num_magic_bytes = 8;
5739 char magic_bytes[num_magic_bytes + 1];
5740 int num_levels_to_skip = level_file_info->nr - leveldir_current->first_level;
5742 if (!(file = openFile(filename, MODE_READ)))
5744 level->no_valid_file = TRUE;
5746 if (!level_info_only)
5747 Warn("cannot read level '%s' -- using empty level", filename);
5752 // fseek(file, 0x0000, SEEK_SET);
5754 if (level_file_info->packed)
5756 // read "magic bytes" from start of file
5757 if (getStringFromFile(file, magic_bytes, num_magic_bytes + 1) == NULL)
5758 magic_bytes[0] = '\0';
5760 // check "magic bytes" for correct file format
5761 if (!strPrefix(magic_bytes, "DC2"))
5763 level->no_valid_file = TRUE;
5765 Warn("unknown DC level file '%s' -- using empty level", filename);
5770 if (strPrefix(magic_bytes, "DC2Win95") ||
5771 strPrefix(magic_bytes, "DC2Win98"))
5773 int position_first_level = 0x00fa;
5774 int extra_bytes = 4;
5777 // advance file stream to first level inside the level package
5778 skip_bytes = position_first_level - num_magic_bytes - extra_bytes;
5780 // each block of level data is followed by block of non-level data
5781 num_levels_to_skip *= 2;
5783 // at least skip header bytes, therefore use ">= 0" instead of "> 0"
5784 while (num_levels_to_skip >= 0)
5786 // advance file stream to next level inside the level package
5787 if (seekFile(file, skip_bytes, SEEK_CUR) != 0)
5789 level->no_valid_file = TRUE;
5791 Warn("cannot fseek in file '%s' -- using empty level", filename);
5796 // skip apparently unused extra bytes following each level
5797 ReadUnusedBytesFromFile(file, extra_bytes);
5799 // read size of next level in level package
5800 skip_bytes = getFile32BitLE(file);
5802 num_levels_to_skip--;
5807 level->no_valid_file = TRUE;
5809 Warn("unknown DC2 level file '%s' -- using empty level", filename);
5815 LoadLevelFromFileStream_DC(file, level, level_file_info->nr);
5821 // ----------------------------------------------------------------------------
5822 // functions for loading SB level
5823 // ----------------------------------------------------------------------------
5825 int getMappedElement_SB(int element_ascii, boolean use_ces)
5833 sb_element_mapping[] =
5835 { ' ', EL_EMPTY, EL_CUSTOM_1 }, // floor (space)
5836 { '#', EL_STEELWALL, EL_CUSTOM_2 }, // wall
5837 { '@', EL_PLAYER_1, EL_CUSTOM_3 }, // player
5838 { '$', EL_SOKOBAN_OBJECT, EL_CUSTOM_4 }, // box
5839 { '.', EL_SOKOBAN_FIELD_EMPTY, EL_CUSTOM_5 }, // goal square
5840 { '*', EL_SOKOBAN_FIELD_FULL, EL_CUSTOM_6 }, // box on goal square
5841 { '+', EL_SOKOBAN_FIELD_PLAYER, EL_CUSTOM_7 }, // player on goal square
5842 { '_', EL_INVISIBLE_STEELWALL, EL_FROM_LEVEL_TEMPLATE }, // floor beyond border
5849 for (i = 0; sb_element_mapping[i].ascii != 0; i++)
5850 if (element_ascii == sb_element_mapping[i].ascii)
5851 return (use_ces ? sb_element_mapping[i].ce : sb_element_mapping[i].sb);
5853 return EL_UNDEFINED;
5856 static void LoadLevelFromFileInfo_SB(struct LevelInfo *level,
5857 struct LevelFileInfo *level_file_info,
5858 boolean level_info_only)
5860 char *filename = level_file_info->filename;
5861 char line[MAX_LINE_LEN], line_raw[MAX_LINE_LEN], previous_line[MAX_LINE_LEN];
5862 char last_comment[MAX_LINE_LEN];
5863 char level_name[MAX_LINE_LEN];
5866 int num_levels_to_skip = level_file_info->nr - leveldir_current->first_level;
5867 boolean read_continued_line = FALSE;
5868 boolean reading_playfield = FALSE;
5869 boolean got_valid_playfield_line = FALSE;
5870 boolean invalid_playfield_char = FALSE;
5871 boolean load_xsb_to_ces = check_special_flags("load_xsb_to_ces");
5872 int file_level_nr = 0;
5874 int x = 0, y = 0; // initialized to make compilers happy
5876 last_comment[0] = '\0';
5877 level_name[0] = '\0';
5879 if (!(file = openFile(filename, MODE_READ)))
5881 level->no_valid_file = TRUE;
5883 if (!level_info_only)
5884 Warn("cannot read level '%s' -- using empty level", filename);
5889 while (!checkEndOfFile(file))
5891 // level successfully read, but next level may follow here
5892 if (!got_valid_playfield_line && reading_playfield)
5894 // read playfield from single level file -- skip remaining file
5895 if (!level_file_info->packed)
5898 if (file_level_nr >= num_levels_to_skip)
5903 last_comment[0] = '\0';
5904 level_name[0] = '\0';
5906 reading_playfield = FALSE;
5909 got_valid_playfield_line = FALSE;
5911 // read next line of input file
5912 if (!getStringFromFile(file, line, MAX_LINE_LEN))
5915 // check if line was completely read and is terminated by line break
5916 if (strlen(line) > 0 && line[strlen(line) - 1] == '\n')
5919 // cut trailing line break (this can be newline and/or carriage return)
5920 for (line_ptr = &line[strlen(line)]; line_ptr >= line; line_ptr--)
5921 if ((*line_ptr == '\n' || *line_ptr == '\r') && *(line_ptr + 1) == '\0')
5924 // copy raw input line for later use (mainly debugging output)
5925 strcpy(line_raw, line);
5927 if (read_continued_line)
5929 // append new line to existing line, if there is enough space
5930 if (strlen(previous_line) + strlen(line_ptr) < MAX_LINE_LEN)
5931 strcat(previous_line, line_ptr);
5933 strcpy(line, previous_line); // copy storage buffer to line
5935 read_continued_line = FALSE;
5938 // if the last character is '\', continue at next line
5939 if (strlen(line) > 0 && line[strlen(line) - 1] == '\\')
5941 line[strlen(line) - 1] = '\0'; // cut off trailing backslash
5942 strcpy(previous_line, line); // copy line to storage buffer
5944 read_continued_line = TRUE;
5950 if (line[0] == '\0')
5953 // extract comment text from comment line
5956 for (line_ptr = line; *line_ptr; line_ptr++)
5957 if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != ';')
5960 strcpy(last_comment, line_ptr);
5965 // extract level title text from line containing level title
5966 if (line[0] == '\'')
5968 strcpy(level_name, &line[1]);
5970 if (strlen(level_name) > 0 && level_name[strlen(level_name) - 1] == '\'')
5971 level_name[strlen(level_name) - 1] = '\0';
5976 // skip lines containing only spaces (or empty lines)
5977 for (line_ptr = line; *line_ptr; line_ptr++)
5978 if (*line_ptr != ' ')
5980 if (*line_ptr == '\0')
5983 // at this point, we have found a line containing part of a playfield
5985 got_valid_playfield_line = TRUE;
5987 if (!reading_playfield)
5989 reading_playfield = TRUE;
5990 invalid_playfield_char = FALSE;
5992 for (x = 0; x < MAX_LEV_FIELDX; x++)
5993 for (y = 0; y < MAX_LEV_FIELDY; y++)
5994 level->field[x][y] = getMappedElement_SB(' ', load_xsb_to_ces);
5999 // start with topmost tile row
6003 // skip playfield line if larger row than allowed
6004 if (y >= MAX_LEV_FIELDY)
6007 // start with leftmost tile column
6010 // read playfield elements from line
6011 for (line_ptr = line; *line_ptr; line_ptr++)
6013 int mapped_sb_element = getMappedElement_SB(*line_ptr, load_xsb_to_ces);
6015 // stop parsing playfield line if larger column than allowed
6016 if (x >= MAX_LEV_FIELDX)
6019 if (mapped_sb_element == EL_UNDEFINED)
6021 invalid_playfield_char = TRUE;
6026 level->field[x][y] = mapped_sb_element;
6028 // continue with next tile column
6031 level->fieldx = MAX(x, level->fieldx);
6034 if (invalid_playfield_char)
6036 // if first playfield line, treat invalid lines as comment lines
6038 reading_playfield = FALSE;
6043 // continue with next tile row
6051 level->fieldx = MIN(MAX(MIN_LEV_FIELDX, level->fieldx), MAX_LEV_FIELDX);
6052 level->fieldy = MIN(MAX(MIN_LEV_FIELDY, level->fieldy), MAX_LEV_FIELDY);
6054 if (!reading_playfield)
6056 level->no_valid_file = TRUE;
6058 Warn("cannot read level '%s' -- using empty level", filename);
6063 if (*level_name != '\0')
6065 strncpy(level->name, level_name, MAX_LEVEL_NAME_LEN);
6066 level->name[MAX_LEVEL_NAME_LEN] = '\0';
6068 else if (*last_comment != '\0')
6070 strncpy(level->name, last_comment, MAX_LEVEL_NAME_LEN);
6071 level->name[MAX_LEVEL_NAME_LEN] = '\0';
6075 sprintf(level->name, "--> Level %d <--", level_file_info->nr);
6078 // set all empty fields beyond the border walls to invisible steel wall
6079 for (y = 0; y < level->fieldy; y++) for (x = 0; x < level->fieldx; x++)
6081 if ((x == 0 || x == level->fieldx - 1 ||
6082 y == 0 || y == level->fieldy - 1) &&
6083 level->field[x][y] == getMappedElement_SB(' ', load_xsb_to_ces))
6084 FloodFillLevel(x, y, getMappedElement_SB('_', load_xsb_to_ces),
6085 level->field, level->fieldx, level->fieldy);
6088 // set special level settings for Sokoban levels
6091 level->use_step_counter = TRUE;
6093 if (load_xsb_to_ces)
6095 // special global settings can now be set in level template
6097 level->use_custom_template = TRUE;
6102 // -------------------------------------------------------------------------
6103 // functions for handling native levels
6104 // -------------------------------------------------------------------------
6106 static void LoadLevelFromFileInfo_EM(struct LevelInfo *level,
6107 struct LevelFileInfo *level_file_info,
6108 boolean level_info_only)
6110 if (!LoadNativeLevel_EM(level_file_info->filename, level_info_only))
6111 level->no_valid_file = TRUE;
6114 static void LoadLevelFromFileInfo_SP(struct LevelInfo *level,
6115 struct LevelFileInfo *level_file_info,
6116 boolean level_info_only)
6120 // determine position of requested level inside level package
6121 if (level_file_info->packed)
6122 pos = level_file_info->nr - leveldir_current->first_level;
6124 if (!LoadNativeLevel_SP(level_file_info->filename, pos, level_info_only))
6125 level->no_valid_file = TRUE;
6128 static void LoadLevelFromFileInfo_MM(struct LevelInfo *level,
6129 struct LevelFileInfo *level_file_info,
6130 boolean level_info_only)
6132 if (!LoadNativeLevel_MM(level_file_info->filename, level_info_only))
6133 level->no_valid_file = TRUE;
6136 void CopyNativeLevel_RND_to_Native(struct LevelInfo *level)
6138 if (level->game_engine_type == GAME_ENGINE_TYPE_EM)
6139 CopyNativeLevel_RND_to_EM(level);
6140 else if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
6141 CopyNativeLevel_RND_to_SP(level);
6142 else if (level->game_engine_type == GAME_ENGINE_TYPE_MM)
6143 CopyNativeLevel_RND_to_MM(level);
6146 void CopyNativeLevel_Native_to_RND(struct LevelInfo *level)
6148 if (level->game_engine_type == GAME_ENGINE_TYPE_EM)
6149 CopyNativeLevel_EM_to_RND(level);
6150 else if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
6151 CopyNativeLevel_SP_to_RND(level);
6152 else if (level->game_engine_type == GAME_ENGINE_TYPE_MM)
6153 CopyNativeLevel_MM_to_RND(level);
6156 void SaveNativeLevel(struct LevelInfo *level)
6158 if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
6160 char *basename = getSingleLevelBasenameExt(level->file_info.nr, "sp");
6161 char *filename = getLevelFilenameFromBasename(basename);
6163 CopyNativeLevel_RND_to_SP(level);
6164 CopyNativeTape_RND_to_SP(level);
6166 SaveNativeLevel_SP(filename);
6171 // ----------------------------------------------------------------------------
6172 // functions for loading generic level
6173 // ----------------------------------------------------------------------------
6175 static void LoadLevelFromFileInfo(struct LevelInfo *level,
6176 struct LevelFileInfo *level_file_info,
6177 boolean level_info_only)
6179 // always start with reliable default values
6180 setLevelInfoToDefaults(level, level_info_only, TRUE);
6182 switch (level_file_info->type)
6184 case LEVEL_FILE_TYPE_RND:
6185 LoadLevelFromFileInfo_RND(level, level_file_info, level_info_only);
6188 case LEVEL_FILE_TYPE_EM:
6189 LoadLevelFromFileInfo_EM(level, level_file_info, level_info_only);
6190 level->game_engine_type = GAME_ENGINE_TYPE_EM;
6193 case LEVEL_FILE_TYPE_SP:
6194 LoadLevelFromFileInfo_SP(level, level_file_info, level_info_only);
6195 level->game_engine_type = GAME_ENGINE_TYPE_SP;
6198 case LEVEL_FILE_TYPE_MM:
6199 LoadLevelFromFileInfo_MM(level, level_file_info, level_info_only);
6200 level->game_engine_type = GAME_ENGINE_TYPE_MM;
6203 case LEVEL_FILE_TYPE_DC:
6204 LoadLevelFromFileInfo_DC(level, level_file_info, level_info_only);
6207 case LEVEL_FILE_TYPE_SB:
6208 LoadLevelFromFileInfo_SB(level, level_file_info, level_info_only);
6212 LoadLevelFromFileInfo_RND(level, level_file_info, level_info_only);
6216 // if level file is invalid, restore level structure to default values
6217 if (level->no_valid_file)
6218 setLevelInfoToDefaults(level, level_info_only, FALSE);
6220 if (level->game_engine_type == GAME_ENGINE_TYPE_UNKNOWN)
6221 level->game_engine_type = GAME_ENGINE_TYPE_RND;
6223 if (level_file_info->type != LEVEL_FILE_TYPE_RND)
6224 CopyNativeLevel_Native_to_RND(level);
6227 void LoadLevelFromFilename(struct LevelInfo *level, char *filename)
6229 static struct LevelFileInfo level_file_info;
6231 // always start with reliable default values
6232 setFileInfoToDefaults(&level_file_info);
6234 level_file_info.nr = 0; // unknown level number
6235 level_file_info.type = LEVEL_FILE_TYPE_RND; // no others supported yet
6237 setString(&level_file_info.filename, filename);
6239 LoadLevelFromFileInfo(level, &level_file_info, FALSE);
6242 static void LoadLevel_InitVersion(struct LevelInfo *level)
6246 if (leveldir_current == NULL) // only when dumping level
6249 // all engine modifications also valid for levels which use latest engine
6250 if (level->game_version < VERSION_IDENT(3,2,0,5))
6252 // time bonus score was given for 10 s instead of 1 s before 3.2.0-5
6253 level->score[SC_TIME_BONUS] /= 10;
6256 if (leveldir_current->latest_engine)
6258 // ---------- use latest game engine --------------------------------------
6260 /* For all levels which are forced to use the latest game engine version
6261 (normally all but user contributed, private and undefined levels), set
6262 the game engine version to the actual version; this allows for actual
6263 corrections in the game engine to take effect for existing, converted
6264 levels (from "classic" or other existing games) to make the emulation
6265 of the corresponding game more accurate, while (hopefully) not breaking
6266 existing levels created from other players. */
6268 level->game_version = GAME_VERSION_ACTUAL;
6270 /* Set special EM style gems behaviour: EM style gems slip down from
6271 normal, steel and growing wall. As this is a more fundamental change,
6272 it seems better to set the default behaviour to "off" (as it is more
6273 natural) and make it configurable in the level editor (as a property
6274 of gem style elements). Already existing converted levels (neither
6275 private nor contributed levels) are changed to the new behaviour. */
6277 if (level->file_version < FILE_VERSION_2_0)
6278 level->em_slippery_gems = TRUE;
6283 // ---------- use game engine the level was created with --------------------
6285 /* For all levels which are not forced to use the latest game engine
6286 version (normally user contributed, private and undefined levels),
6287 use the version of the game engine the levels were created for.
6289 Since 2.0.1, the game engine version is now directly stored
6290 in the level file (chunk "VERS"), so there is no need anymore
6291 to set the game version from the file version (except for old,
6292 pre-2.0 levels, where the game version is still taken from the
6293 file format version used to store the level -- see above). */
6295 // player was faster than enemies in 1.0.0 and before
6296 if (level->file_version == FILE_VERSION_1_0)
6297 for (i = 0; i < MAX_PLAYERS; i++)
6298 level->initial_player_stepsize[i] = STEPSIZE_FAST;
6300 // default behaviour for EM style gems was "slippery" only in 2.0.1
6301 if (level->game_version == VERSION_IDENT(2,0,1,0))
6302 level->em_slippery_gems = TRUE;
6304 // springs could be pushed over pits before (pre-release version) 2.2.0
6305 if (level->game_version < VERSION_IDENT(2,2,0,0))
6306 level->use_spring_bug = TRUE;
6308 if (level->game_version < VERSION_IDENT(3,2,0,5))
6310 // time orb caused limited time in endless time levels before 3.2.0-5
6311 level->use_time_orb_bug = TRUE;
6313 // default behaviour for snapping was "no snap delay" before 3.2.0-5
6314 level->block_snap_field = FALSE;
6316 // extra time score was same value as time left score before 3.2.0-5
6317 level->extra_time_score = level->score[SC_TIME_BONUS];
6320 if (level->game_version < VERSION_IDENT(3,2,0,7))
6322 // default behaviour for snapping was "not continuous" before 3.2.0-7
6323 level->continuous_snapping = FALSE;
6326 // only few elements were able to actively move into acid before 3.1.0
6327 // trigger settings did not exist before 3.1.0; set to default "any"
6328 if (level->game_version < VERSION_IDENT(3,1,0,0))
6330 // correct "can move into acid" settings (all zero in old levels)
6332 level->can_move_into_acid_bits = 0; // nothing can move into acid
6333 level->dont_collide_with_bits = 0; // nothing is deadly when colliding
6335 setMoveIntoAcidProperty(level, EL_ROBOT, TRUE);
6336 setMoveIntoAcidProperty(level, EL_SATELLITE, TRUE);
6337 setMoveIntoAcidProperty(level, EL_PENGUIN, TRUE);
6338 setMoveIntoAcidProperty(level, EL_BALLOON, TRUE);
6340 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6341 SET_PROPERTY(EL_CUSTOM_START + i, EP_CAN_MOVE_INTO_ACID, TRUE);
6343 // correct trigger settings (stored as zero == "none" in old levels)
6345 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6347 int element = EL_CUSTOM_START + i;
6348 struct ElementInfo *ei = &element_info[element];
6350 for (j = 0; j < ei->num_change_pages; j++)
6352 struct ElementChangeInfo *change = &ei->change_page[j];
6354 change->trigger_player = CH_PLAYER_ANY;
6355 change->trigger_page = CH_PAGE_ANY;
6360 // try to detect and fix "Snake Bite" levels, which are broken with 3.2.0
6362 int element = EL_CUSTOM_256;
6363 struct ElementInfo *ei = &element_info[element];
6364 struct ElementChangeInfo *change = &ei->change_page[0];
6366 /* This is needed to fix a problem that was caused by a bugfix in function
6367 game.c/CreateFieldExt() introduced with 3.2.0 that corrects the behaviour
6368 when a custom element changes to EL_SOKOBAN_FIELD_PLAYER (before, it did
6369 not replace walkable elements, but instead just placed the player on it,
6370 without placing the Sokoban field under the player). Unfortunately, this
6371 breaks "Snake Bite" style levels when the snake is halfway through a door
6372 that just closes (the snake head is still alive and can be moved in this
6373 case). This can be fixed by replacing the EL_SOKOBAN_FIELD_PLAYER by the
6374 player (without Sokoban element) which then gets killed as designed). */
6376 if ((strncmp(leveldir_current->identifier, "snake_bite", 10) == 0 ||
6377 strncmp(ei->description, "pause b4 death", 14) == 0) &&
6378 change->target_element == EL_SOKOBAN_FIELD_PLAYER)
6379 change->target_element = EL_PLAYER_1;
6382 // try to detect and fix "Zelda" style levels, which are broken with 3.2.5
6383 if (level->game_version < VERSION_IDENT(3,2,5,0))
6385 /* This is needed to fix a problem that was caused by a bugfix in function
6386 game.c/CheckTriggeredElementChangeExt() introduced with 3.2.5 that
6387 corrects the behaviour when a custom element changes to another custom
6388 element with a higher element number that has change actions defined.
6389 Normally, only one change per frame is allowed for custom elements.
6390 Therefore, it is checked if a custom element already changed in the
6391 current frame; if it did, subsequent changes are suppressed.
6392 Unfortunately, this is only checked for element changes, but not for
6393 change actions, which are still executed. As the function above loops
6394 through all custom elements from lower to higher, an element change
6395 resulting in a lower CE number won't be checked again, while a target
6396 element with a higher number will also be checked, and potential change
6397 actions will get executed for this CE, too (which is wrong), while
6398 further changes are ignored (which is correct). As this bugfix breaks
6399 Zelda II (and introduces graphical bugs to Zelda I, and also breaks a
6400 few other levels like Alan Bond's "FMV"), allow the previous, incorrect
6401 behaviour for existing levels and tapes that make use of this bug */
6403 level->use_action_after_change_bug = TRUE;
6406 // not centering level after relocating player was default only in 3.2.3
6407 if (level->game_version == VERSION_IDENT(3,2,3,0)) // (no pre-releases)
6408 level->shifted_relocation = TRUE;
6410 // EM style elements always chain-exploded in R'n'D engine before 3.2.6
6411 if (level->game_version < VERSION_IDENT(3,2,6,0))
6412 level->em_explodes_by_fire = TRUE;
6414 // levels were solved by the first player entering an exit up to 4.1.0.0
6415 if (level->game_version <= VERSION_IDENT(4,1,0,0))
6416 level->solved_by_one_player = TRUE;
6418 // game logic of "game of life" and "biomaze" was buggy before 4.1.1.1
6419 if (level->game_version < VERSION_IDENT(4,1,1,1))
6420 level->use_life_bugs = TRUE;
6422 // only Sokoban fields (but not objects) had to be solved before 4.1.1.1
6423 if (level->game_version < VERSION_IDENT(4,1,1,1))
6424 level->sb_objects_needed = FALSE;
6427 static void LoadLevel_InitStandardElements(struct LevelInfo *level)
6431 // map elements that have changed in newer versions
6432 level->amoeba_content = getMappedElementByVersion(level->amoeba_content,
6433 level->game_version);
6434 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
6435 for (x = 0; x < 3; x++)
6436 for (y = 0; y < 3; y++)
6437 level->yamyam_content[i].e[x][y] =
6438 getMappedElementByVersion(level->yamyam_content[i].e[x][y],
6439 level->game_version);
6443 static void LoadLevel_InitCustomElements(struct LevelInfo *level)
6447 // map custom element change events that have changed in newer versions
6448 // (these following values were accidentally changed in version 3.0.1)
6449 // (this seems to be needed only for 'ab_levelset3' and 'ab_levelset4')
6450 if (level->game_version <= VERSION_IDENT(3,0,0,0))
6452 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6454 int element = EL_CUSTOM_START + i;
6456 // order of checking and copying events to be mapped is important
6457 // (do not change the start and end value -- they are constant)
6458 for (j = CE_BY_OTHER_ACTION; j >= CE_VALUE_GETS_ZERO; j--)
6460 if (HAS_CHANGE_EVENT(element, j - 2))
6462 SET_CHANGE_EVENT(element, j - 2, FALSE);
6463 SET_CHANGE_EVENT(element, j, TRUE);
6467 // order of checking and copying events to be mapped is important
6468 // (do not change the start and end value -- they are constant)
6469 for (j = CE_PLAYER_COLLECTS_X; j >= CE_HITTING_SOMETHING; j--)
6471 if (HAS_CHANGE_EVENT(element, j - 1))
6473 SET_CHANGE_EVENT(element, j - 1, FALSE);
6474 SET_CHANGE_EVENT(element, j, TRUE);
6480 // initialize "can_change" field for old levels with only one change page
6481 if (level->game_version <= VERSION_IDENT(3,0,2,0))
6483 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6485 int element = EL_CUSTOM_START + i;
6487 if (CAN_CHANGE(element))
6488 element_info[element].change->can_change = TRUE;
6492 // correct custom element values (for old levels without these options)
6493 if (level->game_version < VERSION_IDENT(3,1,1,0))
6495 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6497 int element = EL_CUSTOM_START + i;
6498 struct ElementInfo *ei = &element_info[element];
6500 if (ei->access_direction == MV_NO_DIRECTION)
6501 ei->access_direction = MV_ALL_DIRECTIONS;
6505 // correct custom element values (fix invalid values for all versions)
6508 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6510 int element = EL_CUSTOM_START + i;
6511 struct ElementInfo *ei = &element_info[element];
6513 for (j = 0; j < ei->num_change_pages; j++)
6515 struct ElementChangeInfo *change = &ei->change_page[j];
6517 if (change->trigger_player == CH_PLAYER_NONE)
6518 change->trigger_player = CH_PLAYER_ANY;
6520 if (change->trigger_side == CH_SIDE_NONE)
6521 change->trigger_side = CH_SIDE_ANY;
6526 // initialize "can_explode" field for old levels which did not store this
6527 // !!! CHECK THIS -- "<= 3,1,0,0" IS PROBABLY WRONG !!!
6528 if (level->game_version <= VERSION_IDENT(3,1,0,0))
6530 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6532 int element = EL_CUSTOM_START + i;
6534 if (EXPLODES_1X1_OLD(element))
6535 element_info[element].explosion_type = EXPLODES_1X1;
6537 SET_PROPERTY(element, EP_CAN_EXPLODE, (EXPLODES_BY_FIRE(element) ||
6538 EXPLODES_SMASHED(element) ||
6539 EXPLODES_IMPACT(element)));
6543 // correct previously hard-coded move delay values for maze runner style
6544 if (level->game_version < VERSION_IDENT(3,1,1,0))
6546 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6548 int element = EL_CUSTOM_START + i;
6550 if (element_info[element].move_pattern & MV_MAZE_RUNNER_STYLE)
6552 // previously hard-coded and therefore ignored
6553 element_info[element].move_delay_fixed = 9;
6554 element_info[element].move_delay_random = 0;
6559 // set some other uninitialized values of custom elements in older levels
6560 if (level->game_version < VERSION_IDENT(3,1,0,0))
6562 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6564 int element = EL_CUSTOM_START + i;
6566 element_info[element].access_direction = MV_ALL_DIRECTIONS;
6568 element_info[element].explosion_delay = 17;
6569 element_info[element].ignition_delay = 8;
6574 static void LoadLevel_InitElements(struct LevelInfo *level)
6576 LoadLevel_InitStandardElements(level);
6578 if (level->file_has_custom_elements)
6579 LoadLevel_InitCustomElements(level);
6581 // initialize element properties for level editor etc.
6582 InitElementPropertiesEngine(level->game_version);
6583 InitElementPropertiesGfxElement();
6586 static void LoadLevel_InitPlayfield(struct LevelInfo *level)
6590 // map elements that have changed in newer versions
6591 for (y = 0; y < level->fieldy; y++)
6592 for (x = 0; x < level->fieldx; x++)
6593 level->field[x][y] = getMappedElementByVersion(level->field[x][y],
6594 level->game_version);
6596 // clear unused playfield data (nicer if level gets resized in editor)
6597 for (x = 0; x < MAX_LEV_FIELDX; x++)
6598 for (y = 0; y < MAX_LEV_FIELDY; y++)
6599 if (x >= level->fieldx || y >= level->fieldy)
6600 level->field[x][y] = EL_EMPTY;
6602 // copy elements to runtime playfield array
6603 for (x = 0; x < MAX_LEV_FIELDX; x++)
6604 for (y = 0; y < MAX_LEV_FIELDY; y++)
6605 Tile[x][y] = level->field[x][y];
6607 // initialize level size variables for faster access
6608 lev_fieldx = level->fieldx;
6609 lev_fieldy = level->fieldy;
6611 // determine border element for this level
6612 if (level->file_info.type == LEVEL_FILE_TYPE_DC)
6613 BorderElement = EL_EMPTY; // (in editor, SetBorderElement() is used)
6618 static void LoadLevel_InitNativeEngines(struct LevelInfo *level)
6620 struct LevelFileInfo *level_file_info = &level->file_info;
6622 if (level_file_info->type == LEVEL_FILE_TYPE_RND)
6623 CopyNativeLevel_RND_to_Native(level);
6626 static void LoadLevelTemplate_LoadAndInit(void)
6628 LoadLevelFromFileInfo(&level_template, &level_template.file_info, FALSE);
6630 LoadLevel_InitVersion(&level_template);
6631 LoadLevel_InitElements(&level_template);
6633 ActivateLevelTemplate();
6636 void LoadLevelTemplate(int nr)
6638 if (!fileExists(getGlobalLevelTemplateFilename()))
6640 Warn("no level template found for this level");
6645 setLevelFileInfo(&level_template.file_info, nr);
6647 LoadLevelTemplate_LoadAndInit();
6650 static void LoadNetworkLevelTemplate(struct NetworkLevelInfo *network_level)
6652 copyLevelFileInfo(&network_level->tmpl_info, &level_template.file_info);
6654 LoadLevelTemplate_LoadAndInit();
6657 static void LoadLevel_LoadAndInit(struct NetworkLevelInfo *network_level)
6659 LoadLevelFromFileInfo(&level, &level.file_info, FALSE);
6661 if (level.use_custom_template)
6663 if (network_level != NULL)
6664 LoadNetworkLevelTemplate(network_level);
6666 LoadLevelTemplate(-1);
6669 LoadLevel_InitVersion(&level);
6670 LoadLevel_InitElements(&level);
6671 LoadLevel_InitPlayfield(&level);
6673 LoadLevel_InitNativeEngines(&level);
6676 void LoadLevel(int nr)
6678 SetLevelSetInfo(leveldir_current->identifier, nr);
6680 setLevelFileInfo(&level.file_info, nr);
6682 LoadLevel_LoadAndInit(NULL);
6685 void LoadLevelInfoOnly(int nr)
6687 setLevelFileInfo(&level.file_info, nr);
6689 LoadLevelFromFileInfo(&level, &level.file_info, TRUE);
6692 void LoadNetworkLevel(struct NetworkLevelInfo *network_level)
6694 SetLevelSetInfo(network_level->leveldir_identifier,
6695 network_level->file_info.nr);
6697 copyLevelFileInfo(&network_level->file_info, &level.file_info);
6699 LoadLevel_LoadAndInit(network_level);
6702 static int SaveLevel_VERS(FILE *file, struct LevelInfo *level)
6706 chunk_size += putFileVersion(file, level->file_version);
6707 chunk_size += putFileVersion(file, level->game_version);
6712 static int SaveLevel_DATE(FILE *file, struct LevelInfo *level)
6716 chunk_size += putFile16BitBE(file, level->creation_date.year);
6717 chunk_size += putFile8Bit(file, level->creation_date.month);
6718 chunk_size += putFile8Bit(file, level->creation_date.day);
6723 #if ENABLE_HISTORIC_CHUNKS
6724 static void SaveLevel_HEAD(FILE *file, struct LevelInfo *level)
6728 putFile8Bit(file, level->fieldx);
6729 putFile8Bit(file, level->fieldy);
6731 putFile16BitBE(file, level->time);
6732 putFile16BitBE(file, level->gems_needed);
6734 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
6735 putFile8Bit(file, level->name[i]);
6737 for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
6738 putFile8Bit(file, level->score[i]);
6740 for (i = 0; i < STD_ELEMENT_CONTENTS; i++)
6741 for (y = 0; y < 3; y++)
6742 for (x = 0; x < 3; x++)
6743 putFile8Bit(file, (level->encoding_16bit_yamyam ? EL_EMPTY :
6744 level->yamyam_content[i].e[x][y]));
6745 putFile8Bit(file, level->amoeba_speed);
6746 putFile8Bit(file, level->time_magic_wall);
6747 putFile8Bit(file, level->time_wheel);
6748 putFile8Bit(file, (level->encoding_16bit_amoeba ? EL_EMPTY :
6749 level->amoeba_content));
6750 putFile8Bit(file, (level->initial_player_stepsize == STEPSIZE_FAST ? 1 : 0));
6751 putFile8Bit(file, (level->initial_gravity ? 1 : 0));
6752 putFile8Bit(file, (level->encoding_16bit_field ? 1 : 0));
6753 putFile8Bit(file, (level->em_slippery_gems ? 1 : 0));
6755 putFile8Bit(file, (level->use_custom_template ? 1 : 0));
6757 putFile8Bit(file, (level->block_last_field ? 1 : 0));
6758 putFile8Bit(file, (level->sp_block_last_field ? 1 : 0));
6759 putFile32BitBE(file, level->can_move_into_acid_bits);
6760 putFile8Bit(file, level->dont_collide_with_bits);
6762 putFile8Bit(file, (level->use_spring_bug ? 1 : 0));
6763 putFile8Bit(file, (level->use_step_counter ? 1 : 0));
6765 putFile8Bit(file, (level->instant_relocation ? 1 : 0));
6766 putFile8Bit(file, (level->can_pass_to_walkable ? 1 : 0));
6767 putFile8Bit(file, (level->grow_into_diggable ? 1 : 0));
6769 putFile8Bit(file, level->game_engine_type);
6771 WriteUnusedBytesToFile(file, LEVEL_CHUNK_HEAD_UNUSED);
6775 static int SaveLevel_NAME(FILE *file, struct LevelInfo *level)
6780 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
6781 chunk_size += putFile8Bit(file, level->name[i]);
6786 static int SaveLevel_AUTH(FILE *file, struct LevelInfo *level)
6791 for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
6792 chunk_size += putFile8Bit(file, level->author[i]);
6797 #if ENABLE_HISTORIC_CHUNKS
6798 static int SaveLevel_BODY(FILE *file, struct LevelInfo *level)
6803 for (y = 0; y < level->fieldy; y++)
6804 for (x = 0; x < level->fieldx; x++)
6805 if (level->encoding_16bit_field)
6806 chunk_size += putFile16BitBE(file, level->field[x][y]);
6808 chunk_size += putFile8Bit(file, level->field[x][y]);
6814 static int SaveLevel_BODY(FILE *file, struct LevelInfo *level)
6819 for (y = 0; y < level->fieldy; y++)
6820 for (x = 0; x < level->fieldx; x++)
6821 chunk_size += putFile16BitBE(file, level->field[x][y]);
6826 #if ENABLE_HISTORIC_CHUNKS
6827 static void SaveLevel_CONT(FILE *file, struct LevelInfo *level)
6831 putFile8Bit(file, EL_YAMYAM);
6832 putFile8Bit(file, level->num_yamyam_contents);
6833 putFile8Bit(file, 0);
6834 putFile8Bit(file, 0);
6836 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
6837 for (y = 0; y < 3; y++)
6838 for (x = 0; x < 3; x++)
6839 if (level->encoding_16bit_field)
6840 putFile16BitBE(file, level->yamyam_content[i].e[x][y]);
6842 putFile8Bit(file, level->yamyam_content[i].e[x][y]);
6846 #if ENABLE_HISTORIC_CHUNKS
6847 static void SaveLevel_CNT2(FILE *file, struct LevelInfo *level, int element)
6850 int num_contents, content_xsize, content_ysize;
6851 int content_array[MAX_ELEMENT_CONTENTS][3][3];
6853 if (element == EL_YAMYAM)
6855 num_contents = level->num_yamyam_contents;
6859 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
6860 for (y = 0; y < 3; y++)
6861 for (x = 0; x < 3; x++)
6862 content_array[i][x][y] = level->yamyam_content[i].e[x][y];
6864 else if (element == EL_BD_AMOEBA)
6870 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
6871 for (y = 0; y < 3; y++)
6872 for (x = 0; x < 3; x++)
6873 content_array[i][x][y] = EL_EMPTY;
6874 content_array[0][0][0] = level->amoeba_content;
6878 // chunk header already written -- write empty chunk data
6879 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_SIZE);
6881 Warn("cannot save content for element '%d'", element);
6886 putFile16BitBE(file, element);
6887 putFile8Bit(file, num_contents);
6888 putFile8Bit(file, content_xsize);
6889 putFile8Bit(file, content_ysize);
6891 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_UNUSED);
6893 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
6894 for (y = 0; y < 3; y++)
6895 for (x = 0; x < 3; x++)
6896 putFile16BitBE(file, content_array[i][x][y]);
6900 #if ENABLE_HISTORIC_CHUNKS
6901 static int SaveLevel_CNT3(FILE *file, struct LevelInfo *level, int element)
6903 int envelope_nr = element - EL_ENVELOPE_1;
6904 int envelope_len = strlen(level->envelope_text[envelope_nr]) + 1;
6908 chunk_size += putFile16BitBE(file, element);
6909 chunk_size += putFile16BitBE(file, envelope_len);
6910 chunk_size += putFile8Bit(file, level->envelope_xsize[envelope_nr]);
6911 chunk_size += putFile8Bit(file, level->envelope_ysize[envelope_nr]);
6913 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT3_UNUSED);
6914 chunk_size += LEVEL_CHUNK_CNT3_UNUSED;
6916 for (i = 0; i < envelope_len; i++)
6917 chunk_size += putFile8Bit(file, level->envelope_text[envelope_nr][i]);
6923 #if ENABLE_HISTORIC_CHUNKS
6924 static void SaveLevel_CUS1(FILE *file, struct LevelInfo *level,
6925 int num_changed_custom_elements)
6929 putFile16BitBE(file, num_changed_custom_elements);
6931 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6933 int element = EL_CUSTOM_START + i;
6935 struct ElementInfo *ei = &element_info[element];
6937 if (ei->properties[EP_BITFIELD_BASE_NR] != EP_BITMASK_DEFAULT)
6939 if (check < num_changed_custom_elements)
6941 putFile16BitBE(file, element);
6942 putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
6949 if (check != num_changed_custom_elements) // should not happen
6950 Warn("inconsistent number of custom element properties");
6954 #if ENABLE_HISTORIC_CHUNKS
6955 static void SaveLevel_CUS2(FILE *file, struct LevelInfo *level,
6956 int num_changed_custom_elements)
6960 putFile16BitBE(file, num_changed_custom_elements);
6962 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6964 int element = EL_CUSTOM_START + i;
6966 if (element_info[element].change->target_element != EL_EMPTY_SPACE)
6968 if (check < num_changed_custom_elements)
6970 putFile16BitBE(file, element);
6971 putFile16BitBE(file, element_info[element].change->target_element);
6978 if (check != num_changed_custom_elements) // should not happen
6979 Warn("inconsistent number of custom target elements");
6983 #if ENABLE_HISTORIC_CHUNKS
6984 static void SaveLevel_CUS3(FILE *file, struct LevelInfo *level,
6985 int num_changed_custom_elements)
6987 int i, j, x, y, check = 0;
6989 putFile16BitBE(file, num_changed_custom_elements);
6991 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6993 int element = EL_CUSTOM_START + i;
6994 struct ElementInfo *ei = &element_info[element];
6996 if (ei->modified_settings)
6998 if (check < num_changed_custom_elements)
7000 putFile16BitBE(file, element);
7002 for (j = 0; j < MAX_ELEMENT_NAME_LEN; j++)
7003 putFile8Bit(file, ei->description[j]);
7005 putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
7007 // some free bytes for future properties and padding
7008 WriteUnusedBytesToFile(file, 7);
7010 putFile8Bit(file, ei->use_gfx_element);
7011 putFile16BitBE(file, ei->gfx_element_initial);
7013 putFile8Bit(file, ei->collect_score_initial);
7014 putFile8Bit(file, ei->collect_count_initial);
7016 putFile16BitBE(file, ei->push_delay_fixed);
7017 putFile16BitBE(file, ei->push_delay_random);
7018 putFile16BitBE(file, ei->move_delay_fixed);
7019 putFile16BitBE(file, ei->move_delay_random);
7021 putFile16BitBE(file, ei->move_pattern);
7022 putFile8Bit(file, ei->move_direction_initial);
7023 putFile8Bit(file, ei->move_stepsize);
7025 for (y = 0; y < 3; y++)
7026 for (x = 0; x < 3; x++)
7027 putFile16BitBE(file, ei->content.e[x][y]);
7029 putFile32BitBE(file, ei->change->events);
7031 putFile16BitBE(file, ei->change->target_element);
7033 putFile16BitBE(file, ei->change->delay_fixed);
7034 putFile16BitBE(file, ei->change->delay_random);
7035 putFile16BitBE(file, ei->change->delay_frames);
7037 putFile16BitBE(file, ei->change->initial_trigger_element);
7039 putFile8Bit(file, ei->change->explode);
7040 putFile8Bit(file, ei->change->use_target_content);
7041 putFile8Bit(file, ei->change->only_if_complete);
7042 putFile8Bit(file, ei->change->use_random_replace);
7044 putFile8Bit(file, ei->change->random_percentage);
7045 putFile8Bit(file, ei->change->replace_when);
7047 for (y = 0; y < 3; y++)
7048 for (x = 0; x < 3; x++)
7049 putFile16BitBE(file, ei->change->content.e[x][y]);
7051 putFile8Bit(file, ei->slippery_type);
7053 // some free bytes for future properties and padding
7054 WriteUnusedBytesToFile(file, LEVEL_CPART_CUS3_UNUSED);
7061 if (check != num_changed_custom_elements) // should not happen
7062 Warn("inconsistent number of custom element properties");
7066 #if ENABLE_HISTORIC_CHUNKS
7067 static void SaveLevel_CUS4(FILE *file, struct LevelInfo *level, int element)
7069 struct ElementInfo *ei = &element_info[element];
7072 // ---------- custom element base property values (96 bytes) ----------------
7074 putFile16BitBE(file, element);
7076 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
7077 putFile8Bit(file, ei->description[i]);
7079 putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
7081 WriteUnusedBytesToFile(file, 4); // reserved for more base properties
7083 putFile8Bit(file, ei->num_change_pages);
7085 putFile16BitBE(file, ei->ce_value_fixed_initial);
7086 putFile16BitBE(file, ei->ce_value_random_initial);
7087 putFile8Bit(file, ei->use_last_ce_value);
7089 putFile8Bit(file, ei->use_gfx_element);
7090 putFile16BitBE(file, ei->gfx_element_initial);
7092 putFile8Bit(file, ei->collect_score_initial);
7093 putFile8Bit(file, ei->collect_count_initial);
7095 putFile8Bit(file, ei->drop_delay_fixed);
7096 putFile8Bit(file, ei->push_delay_fixed);
7097 putFile8Bit(file, ei->drop_delay_random);
7098 putFile8Bit(file, ei->push_delay_random);
7099 putFile16BitBE(file, ei->move_delay_fixed);
7100 putFile16BitBE(file, ei->move_delay_random);
7102 // bits 0 - 15 of "move_pattern" ...
7103 putFile16BitBE(file, ei->move_pattern & 0xffff);
7104 putFile8Bit(file, ei->move_direction_initial);
7105 putFile8Bit(file, ei->move_stepsize);
7107 putFile8Bit(file, ei->slippery_type);
7109 for (y = 0; y < 3; y++)
7110 for (x = 0; x < 3; x++)
7111 putFile16BitBE(file, ei->content.e[x][y]);
7113 putFile16BitBE(file, ei->move_enter_element);
7114 putFile16BitBE(file, ei->move_leave_element);
7115 putFile8Bit(file, ei->move_leave_type);
7117 // ... bits 16 - 31 of "move_pattern" (not nice, but downward compatible)
7118 putFile16BitBE(file, (ei->move_pattern >> 16) & 0xffff);
7120 putFile8Bit(file, ei->access_direction);
7122 putFile8Bit(file, ei->explosion_delay);
7123 putFile8Bit(file, ei->ignition_delay);
7124 putFile8Bit(file, ei->explosion_type);
7126 // some free bytes for future custom property values and padding
7127 WriteUnusedBytesToFile(file, 1);
7129 // ---------- change page property values (48 bytes) ------------------------
7131 for (i = 0; i < ei->num_change_pages; i++)
7133 struct ElementChangeInfo *change = &ei->change_page[i];
7134 unsigned int event_bits;
7136 // bits 0 - 31 of "has_event[]" ...
7138 for (j = 0; j < MIN(NUM_CHANGE_EVENTS, 32); j++)
7139 if (change->has_event[j])
7140 event_bits |= (1 << j);
7141 putFile32BitBE(file, event_bits);
7143 putFile16BitBE(file, change->target_element);
7145 putFile16BitBE(file, change->delay_fixed);
7146 putFile16BitBE(file, change->delay_random);
7147 putFile16BitBE(file, change->delay_frames);
7149 putFile16BitBE(file, change->initial_trigger_element);
7151 putFile8Bit(file, change->explode);
7152 putFile8Bit(file, change->use_target_content);
7153 putFile8Bit(file, change->only_if_complete);
7154 putFile8Bit(file, change->use_random_replace);
7156 putFile8Bit(file, change->random_percentage);
7157 putFile8Bit(file, change->replace_when);
7159 for (y = 0; y < 3; y++)
7160 for (x = 0; x < 3; x++)
7161 putFile16BitBE(file, change->target_content.e[x][y]);
7163 putFile8Bit(file, change->can_change);
7165 putFile8Bit(file, change->trigger_side);
7167 putFile8Bit(file, change->trigger_player);
7168 putFile8Bit(file, (change->trigger_page == CH_PAGE_ANY ? CH_PAGE_ANY_FILE :
7169 log_2(change->trigger_page)));
7171 putFile8Bit(file, change->has_action);
7172 putFile8Bit(file, change->action_type);
7173 putFile8Bit(file, change->action_mode);
7174 putFile16BitBE(file, change->action_arg);
7176 // ... bits 32 - 39 of "has_event[]" (not nice, but downward compatible)
7178 for (j = 32; j < NUM_CHANGE_EVENTS; j++)
7179 if (change->has_event[j])
7180 event_bits |= (1 << (j - 32));
7181 putFile8Bit(file, event_bits);
7186 #if ENABLE_HISTORIC_CHUNKS
7187 static void SaveLevel_GRP1(FILE *file, struct LevelInfo *level, int element)
7189 struct ElementInfo *ei = &element_info[element];
7190 struct ElementGroupInfo *group = ei->group;
7193 putFile16BitBE(file, element);
7195 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
7196 putFile8Bit(file, ei->description[i]);
7198 putFile8Bit(file, group->num_elements);
7200 putFile8Bit(file, ei->use_gfx_element);
7201 putFile16BitBE(file, ei->gfx_element_initial);
7203 putFile8Bit(file, group->choice_mode);
7205 // some free bytes for future values and padding
7206 WriteUnusedBytesToFile(file, 3);
7208 for (i = 0; i < MAX_ELEMENTS_IN_GROUP; i++)
7209 putFile16BitBE(file, group->element[i]);
7213 static int SaveLevel_MicroChunk(FILE *file, struct LevelFileConfigInfo *entry,
7214 boolean write_element)
7216 int save_type = entry->save_type;
7217 int data_type = entry->data_type;
7218 int conf_type = entry->conf_type;
7219 int byte_mask = conf_type & CONF_MASK_BYTES;
7220 int element = entry->element;
7221 int default_value = entry->default_value;
7223 boolean modified = FALSE;
7225 if (byte_mask != CONF_MASK_MULTI_BYTES)
7227 void *value_ptr = entry->value;
7228 int value = (data_type == TYPE_BOOLEAN ? *(boolean *)value_ptr :
7231 // check if any settings have been modified before saving them
7232 if (value != default_value)
7235 // do not save if explicitly told or if unmodified default settings
7236 if ((save_type == SAVE_CONF_NEVER) ||
7237 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
7241 num_bytes += putFile16BitBE(file, element);
7243 num_bytes += putFile8Bit(file, conf_type);
7244 num_bytes += (byte_mask == CONF_MASK_1_BYTE ? putFile8Bit (file, value) :
7245 byte_mask == CONF_MASK_2_BYTE ? putFile16BitBE(file, value) :
7246 byte_mask == CONF_MASK_4_BYTE ? putFile32BitBE(file, value) :
7249 else if (data_type == TYPE_STRING)
7251 char *default_string = entry->default_string;
7252 char *string = (char *)(entry->value);
7253 int string_length = strlen(string);
7256 // check if any settings have been modified before saving them
7257 if (!strEqual(string, default_string))
7260 // do not save if explicitly told or if unmodified default settings
7261 if ((save_type == SAVE_CONF_NEVER) ||
7262 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
7266 num_bytes += putFile16BitBE(file, element);
7268 num_bytes += putFile8Bit(file, conf_type);
7269 num_bytes += putFile16BitBE(file, string_length);
7271 for (i = 0; i < string_length; i++)
7272 num_bytes += putFile8Bit(file, string[i]);
7274 else if (data_type == TYPE_ELEMENT_LIST)
7276 int *element_array = (int *)(entry->value);
7277 int num_elements = *(int *)(entry->num_entities);
7280 // check if any settings have been modified before saving them
7281 for (i = 0; i < num_elements; i++)
7282 if (element_array[i] != default_value)
7285 // do not save if explicitly told or if unmodified default settings
7286 if ((save_type == SAVE_CONF_NEVER) ||
7287 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
7291 num_bytes += putFile16BitBE(file, element);
7293 num_bytes += putFile8Bit(file, conf_type);
7294 num_bytes += putFile16BitBE(file, num_elements * CONF_ELEMENT_NUM_BYTES);
7296 for (i = 0; i < num_elements; i++)
7297 num_bytes += putFile16BitBE(file, element_array[i]);
7299 else if (data_type == TYPE_CONTENT_LIST)
7301 struct Content *content = (struct Content *)(entry->value);
7302 int num_contents = *(int *)(entry->num_entities);
7305 // check if any settings have been modified before saving them
7306 for (i = 0; i < num_contents; i++)
7307 for (y = 0; y < 3; y++)
7308 for (x = 0; x < 3; x++)
7309 if (content[i].e[x][y] != default_value)
7312 // do not save if explicitly told or if unmodified default settings
7313 if ((save_type == SAVE_CONF_NEVER) ||
7314 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
7318 num_bytes += putFile16BitBE(file, element);
7320 num_bytes += putFile8Bit(file, conf_type);
7321 num_bytes += putFile16BitBE(file, num_contents * CONF_CONTENT_NUM_BYTES);
7323 for (i = 0; i < num_contents; i++)
7324 for (y = 0; y < 3; y++)
7325 for (x = 0; x < 3; x++)
7326 num_bytes += putFile16BitBE(file, content[i].e[x][y]);
7332 static int SaveLevel_INFO(FILE *file, struct LevelInfo *level)
7337 li = *level; // copy level data into temporary buffer
7339 for (i = 0; chunk_config_INFO[i].data_type != -1; i++)
7340 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_INFO[i], FALSE);
7345 static int SaveLevel_ELEM(FILE *file, struct LevelInfo *level)
7350 li = *level; // copy level data into temporary buffer
7352 for (i = 0; chunk_config_ELEM[i].data_type != -1; i++)
7353 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_ELEM[i], TRUE);
7358 static int SaveLevel_NOTE(FILE *file, struct LevelInfo *level, int element)
7360 int envelope_nr = element - EL_ENVELOPE_1;
7364 chunk_size += putFile16BitBE(file, element);
7366 // copy envelope data into temporary buffer
7367 xx_envelope = level->envelope[envelope_nr];
7369 for (i = 0; chunk_config_NOTE[i].data_type != -1; i++)
7370 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_NOTE[i], FALSE);
7375 static int SaveLevel_CUSX(FILE *file, struct LevelInfo *level, int element)
7377 struct ElementInfo *ei = &element_info[element];
7381 chunk_size += putFile16BitBE(file, element);
7383 xx_ei = *ei; // copy element data into temporary buffer
7385 // set default description string for this specific element
7386 strcpy(xx_default_description, getDefaultElementDescription(ei));
7388 for (i = 0; chunk_config_CUSX_base[i].data_type != -1; i++)
7389 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_CUSX_base[i], FALSE);
7391 for (i = 0; i < ei->num_change_pages; i++)
7393 struct ElementChangeInfo *change = &ei->change_page[i];
7395 xx_current_change_page = i;
7397 xx_change = *change; // copy change data into temporary buffer
7400 setEventBitsFromEventFlags(change);
7402 for (j = 0; chunk_config_CUSX_change[j].data_type != -1; j++)
7403 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_CUSX_change[j],
7410 static int SaveLevel_GRPX(FILE *file, struct LevelInfo *level, int element)
7412 struct ElementInfo *ei = &element_info[element];
7413 struct ElementGroupInfo *group = ei->group;
7417 chunk_size += putFile16BitBE(file, element);
7419 xx_ei = *ei; // copy element data into temporary buffer
7420 xx_group = *group; // copy group data into temporary buffer
7422 // set default description string for this specific element
7423 strcpy(xx_default_description, getDefaultElementDescription(ei));
7425 for (i = 0; chunk_config_GRPX[i].data_type != -1; i++)
7426 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_GRPX[i], FALSE);
7431 static void SaveLevelFromFilename(struct LevelInfo *level, char *filename,
7432 boolean save_as_template)
7438 if (!(file = fopen(filename, MODE_WRITE)))
7440 Warn("cannot save level file '%s'", filename);
7445 level->file_version = FILE_VERSION_ACTUAL;
7446 level->game_version = GAME_VERSION_ACTUAL;
7448 level->creation_date = getCurrentDate();
7450 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
7451 putFileChunkBE(file, "CAVE", CHUNK_SIZE_NONE);
7453 chunk_size = SaveLevel_VERS(NULL, level);
7454 putFileChunkBE(file, "VERS", chunk_size);
7455 SaveLevel_VERS(file, level);
7457 chunk_size = SaveLevel_DATE(NULL, level);
7458 putFileChunkBE(file, "DATE", chunk_size);
7459 SaveLevel_DATE(file, level);
7461 chunk_size = SaveLevel_NAME(NULL, level);
7462 putFileChunkBE(file, "NAME", chunk_size);
7463 SaveLevel_NAME(file, level);
7465 chunk_size = SaveLevel_AUTH(NULL, level);
7466 putFileChunkBE(file, "AUTH", chunk_size);
7467 SaveLevel_AUTH(file, level);
7469 chunk_size = SaveLevel_INFO(NULL, level);
7470 putFileChunkBE(file, "INFO", chunk_size);
7471 SaveLevel_INFO(file, level);
7473 chunk_size = SaveLevel_BODY(NULL, level);
7474 putFileChunkBE(file, "BODY", chunk_size);
7475 SaveLevel_BODY(file, level);
7477 chunk_size = SaveLevel_ELEM(NULL, level);
7478 if (chunk_size > LEVEL_CHUNK_ELEM_UNCHANGED) // save if changed
7480 putFileChunkBE(file, "ELEM", chunk_size);
7481 SaveLevel_ELEM(file, level);
7484 for (i = 0; i < NUM_ENVELOPES; i++)
7486 int element = EL_ENVELOPE_1 + i;
7488 chunk_size = SaveLevel_NOTE(NULL, level, element);
7489 if (chunk_size > LEVEL_CHUNK_NOTE_UNCHANGED) // save if changed
7491 putFileChunkBE(file, "NOTE", chunk_size);
7492 SaveLevel_NOTE(file, level, element);
7496 // if not using template level, check for non-default custom/group elements
7497 if (!level->use_custom_template || save_as_template)
7499 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7501 int element = EL_CUSTOM_START + i;
7503 chunk_size = SaveLevel_CUSX(NULL, level, element);
7504 if (chunk_size > LEVEL_CHUNK_CUSX_UNCHANGED) // save if changed
7506 putFileChunkBE(file, "CUSX", chunk_size);
7507 SaveLevel_CUSX(file, level, element);
7511 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
7513 int element = EL_GROUP_START + i;
7515 chunk_size = SaveLevel_GRPX(NULL, level, element);
7516 if (chunk_size > LEVEL_CHUNK_GRPX_UNCHANGED) // save if changed
7518 putFileChunkBE(file, "GRPX", chunk_size);
7519 SaveLevel_GRPX(file, level, element);
7526 SetFilePermissions(filename, PERMS_PRIVATE);
7529 void SaveLevel(int nr)
7531 char *filename = getDefaultLevelFilename(nr);
7533 SaveLevelFromFilename(&level, filename, FALSE);
7536 void SaveLevelTemplate(void)
7538 char *filename = getLocalLevelTemplateFilename();
7540 SaveLevelFromFilename(&level, filename, TRUE);
7543 boolean SaveLevelChecked(int nr)
7545 char *filename = getDefaultLevelFilename(nr);
7546 boolean new_level = !fileExists(filename);
7547 boolean level_saved = FALSE;
7549 if (new_level || Request("Save this level and kill the old?", REQ_ASK))
7554 Request("Level saved!", REQ_CONFIRM);
7562 void DumpLevel(struct LevelInfo *level)
7564 if (level->no_level_file || level->no_valid_file)
7566 Warn("cannot dump -- no valid level file found");
7572 Print("Level xxx (file version %08d, game version %08d)\n",
7573 level->file_version, level->game_version);
7576 Print("Level author: '%s'\n", level->author);
7577 Print("Level title: '%s'\n", level->name);
7579 Print("Playfield size: %d x %d\n", level->fieldx, level->fieldy);
7581 Print("Level time: %d seconds\n", level->time);
7582 Print("Gems needed: %d\n", level->gems_needed);
7584 Print("Time for magic wall: %d seconds\n", level->time_magic_wall);
7585 Print("Time for wheel: %d seconds\n", level->time_wheel);
7586 Print("Time for light: %d seconds\n", level->time_light);
7587 Print("Time for timegate: %d seconds\n", level->time_timegate);
7589 Print("Amoeba speed: %d\n", level->amoeba_speed);
7592 Print("EM style slippery gems: %s\n", (level->em_slippery_gems ? "yes" : "no"));
7593 Print("Player blocks last field: %s\n", (level->block_last_field ? "yes" : "no"));
7594 Print("SP player blocks last field: %s\n", (level->sp_block_last_field ? "yes" : "no"));
7595 Print("use spring bug: %s\n", (level->use_spring_bug ? "yes" : "no"));
7596 Print("use step counter: %s\n", (level->use_step_counter ? "yes" : "no"));
7602 // ============================================================================
7603 // tape file functions
7604 // ============================================================================
7606 static void setTapeInfoToDefaults(void)
7610 // always start with reliable default values (empty tape)
7613 // default values (also for pre-1.2 tapes) with only the first player
7614 tape.player_participates[0] = TRUE;
7615 for (i = 1; i < MAX_PLAYERS; i++)
7616 tape.player_participates[i] = FALSE;
7618 // at least one (default: the first) player participates in every tape
7619 tape.num_participating_players = 1;
7621 tape.property_bits = TAPE_PROPERTY_NONE;
7623 tape.level_nr = level_nr;
7625 tape.changed = FALSE;
7627 tape.recording = FALSE;
7628 tape.playing = FALSE;
7629 tape.pausing = FALSE;
7631 tape.scr_fieldx = SCR_FIELDX_DEFAULT;
7632 tape.scr_fieldy = SCR_FIELDY_DEFAULT;
7634 tape.no_valid_file = FALSE;
7637 static int getTapePosSize(struct TapeInfo *tape)
7639 int tape_pos_size = 0;
7641 if (tape->use_key_actions)
7642 tape_pos_size += tape->num_participating_players;
7644 if (tape->use_mouse_actions)
7645 tape_pos_size += 3; // x and y position and mouse button mask
7647 tape_pos_size += 1; // tape action delay value
7649 return tape_pos_size;
7652 static void setTapeActionFlags(struct TapeInfo *tape, int value)
7654 tape->use_key_actions = FALSE;
7655 tape->use_mouse_actions = FALSE;
7657 if (value != TAPE_USE_MOUSE_ACTIONS_ONLY)
7658 tape->use_key_actions = TRUE;
7660 if (value != TAPE_USE_KEY_ACTIONS_ONLY)
7661 tape->use_mouse_actions = TRUE;
7664 static int getTapeActionValue(struct TapeInfo *tape)
7666 return (tape->use_key_actions &&
7667 tape->use_mouse_actions ? TAPE_USE_KEY_AND_MOUSE_ACTIONS :
7668 tape->use_key_actions ? TAPE_USE_KEY_ACTIONS_ONLY :
7669 tape->use_mouse_actions ? TAPE_USE_MOUSE_ACTIONS_ONLY :
7670 TAPE_ACTIONS_DEFAULT);
7673 static int LoadTape_VERS(File *file, int chunk_size, struct TapeInfo *tape)
7675 tape->file_version = getFileVersion(file);
7676 tape->game_version = getFileVersion(file);
7681 static int LoadTape_HEAD(File *file, int chunk_size, struct TapeInfo *tape)
7685 tape->random_seed = getFile32BitBE(file);
7686 tape->date = getFile32BitBE(file);
7687 tape->length = getFile32BitBE(file);
7689 // read header fields that are new since version 1.2
7690 if (tape->file_version >= FILE_VERSION_1_2)
7692 byte store_participating_players = getFile8Bit(file);
7695 // since version 1.2, tapes store which players participate in the tape
7696 tape->num_participating_players = 0;
7697 for (i = 0; i < MAX_PLAYERS; i++)
7699 tape->player_participates[i] = FALSE;
7701 if (store_participating_players & (1 << i))
7703 tape->player_participates[i] = TRUE;
7704 tape->num_participating_players++;
7708 setTapeActionFlags(tape, getFile8Bit(file));
7710 tape->property_bits = getFile8Bit(file);
7712 ReadUnusedBytesFromFile(file, TAPE_CHUNK_HEAD_UNUSED);
7714 engine_version = getFileVersion(file);
7715 if (engine_version > 0)
7716 tape->engine_version = engine_version;
7718 tape->engine_version = tape->game_version;
7724 static int LoadTape_INFO(File *file, int chunk_size, struct TapeInfo *tape)
7726 int level_identifier_size;
7729 level_identifier_size = getFile16BitBE(file);
7731 tape->level_identifier =
7732 checked_realloc(tape->level_identifier, level_identifier_size);
7734 for (i = 0; i < level_identifier_size; i++)
7735 tape->level_identifier[i] = getFile8Bit(file);
7737 tape->level_nr = getFile16BitBE(file);
7739 chunk_size = 2 + level_identifier_size + 2;
7744 static int LoadTape_BODY(File *file, int chunk_size, struct TapeInfo *tape)
7747 int tape_pos_size = getTapePosSize(tape);
7748 int chunk_size_expected = tape_pos_size * tape->length;
7750 if (chunk_size_expected != chunk_size)
7752 ReadUnusedBytesFromFile(file, chunk_size);
7753 return chunk_size_expected;
7756 for (i = 0; i < tape->length; i++)
7758 if (i >= MAX_TAPE_LEN)
7760 Warn("tape truncated -- size exceeds maximum tape size %d",
7763 // tape too large; read and ignore remaining tape data from this chunk
7764 for (;i < tape->length; i++)
7765 ReadUnusedBytesFromFile(file, tape_pos_size);
7770 if (tape->use_key_actions)
7772 for (j = 0; j < MAX_PLAYERS; j++)
7774 tape->pos[i].action[j] = MV_NONE;
7776 if (tape->player_participates[j])
7777 tape->pos[i].action[j] = getFile8Bit(file);
7781 if (tape->use_mouse_actions)
7783 tape->pos[i].action[TAPE_ACTION_LX] = getFile8Bit(file);
7784 tape->pos[i].action[TAPE_ACTION_LY] = getFile8Bit(file);
7785 tape->pos[i].action[TAPE_ACTION_BUTTON] = getFile8Bit(file);
7788 tape->pos[i].delay = getFile8Bit(file);
7790 if (tape->file_version == FILE_VERSION_1_0)
7792 // eliminate possible diagonal moves in old tapes
7793 // this is only for backward compatibility
7795 byte joy_dir[4] = { JOY_LEFT, JOY_RIGHT, JOY_UP, JOY_DOWN };
7796 byte action = tape->pos[i].action[0];
7797 int k, num_moves = 0;
7799 for (k = 0; k<4; k++)
7801 if (action & joy_dir[k])
7803 tape->pos[i + num_moves].action[0] = joy_dir[k];
7805 tape->pos[i + num_moves].delay = 0;
7814 tape->length += num_moves;
7817 else if (tape->file_version < FILE_VERSION_2_0)
7819 // convert pre-2.0 tapes to new tape format
7821 if (tape->pos[i].delay > 1)
7824 tape->pos[i + 1] = tape->pos[i];
7825 tape->pos[i + 1].delay = 1;
7828 for (j = 0; j < MAX_PLAYERS; j++)
7829 tape->pos[i].action[j] = MV_NONE;
7830 tape->pos[i].delay--;
7837 if (checkEndOfFile(file))
7841 if (i != tape->length)
7842 chunk_size = tape_pos_size * i;
7847 static void LoadTape_SokobanSolution(char *filename)
7850 int move_delay = TILESIZE / level.initial_player_stepsize[0];
7852 if (!(file = openFile(filename, MODE_READ)))
7854 tape.no_valid_file = TRUE;
7859 while (!checkEndOfFile(file))
7861 unsigned char c = getByteFromFile(file);
7863 if (checkEndOfFile(file))
7870 tape.pos[tape.length].action[0] = MV_UP;
7871 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
7877 tape.pos[tape.length].action[0] = MV_DOWN;
7878 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
7884 tape.pos[tape.length].action[0] = MV_LEFT;
7885 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
7891 tape.pos[tape.length].action[0] = MV_RIGHT;
7892 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
7900 // ignore white-space characters
7904 tape.no_valid_file = TRUE;
7906 Warn("unsupported Sokoban solution file '%s' ['%d']", filename, c);
7914 if (tape.no_valid_file)
7917 tape.length_frames = GetTapeLengthFrames();
7918 tape.length_seconds = GetTapeLengthSeconds();
7921 void LoadTapeFromFilename(char *filename)
7923 char cookie[MAX_LINE_LEN];
7924 char chunk_name[CHUNK_ID_LEN + 1];
7928 // always start with reliable default values
7929 setTapeInfoToDefaults();
7931 if (strSuffix(filename, ".sln"))
7933 LoadTape_SokobanSolution(filename);
7938 if (!(file = openFile(filename, MODE_READ)))
7940 tape.no_valid_file = TRUE;
7945 getFileChunkBE(file, chunk_name, NULL);
7946 if (strEqual(chunk_name, "RND1"))
7948 getFile32BitBE(file); // not used
7950 getFileChunkBE(file, chunk_name, NULL);
7951 if (!strEqual(chunk_name, "TAPE"))
7953 tape.no_valid_file = TRUE;
7955 Warn("unknown format of tape file '%s'", filename);
7962 else // check for pre-2.0 file format with cookie string
7964 strcpy(cookie, chunk_name);
7965 if (getStringFromFile(file, &cookie[4], MAX_LINE_LEN - 4) == NULL)
7967 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
7968 cookie[strlen(cookie) - 1] = '\0';
7970 if (!checkCookieString(cookie, TAPE_COOKIE_TMPL))
7972 tape.no_valid_file = TRUE;
7974 Warn("unknown format of tape file '%s'", filename);
7981 if ((tape.file_version = getFileVersionFromCookieString(cookie)) == -1)
7983 tape.no_valid_file = TRUE;
7985 Warn("unsupported version of tape file '%s'", filename);
7992 // pre-2.0 tape files have no game version, so use file version here
7993 tape.game_version = tape.file_version;
7996 if (tape.file_version < FILE_VERSION_1_2)
7998 // tape files from versions before 1.2.0 without chunk structure
7999 LoadTape_HEAD(file, TAPE_CHUNK_HEAD_SIZE, &tape);
8000 LoadTape_BODY(file, 2 * tape.length, &tape);
8008 int (*loader)(File *, int, struct TapeInfo *);
8012 { "VERS", TAPE_CHUNK_VERS_SIZE, LoadTape_VERS },
8013 { "HEAD", TAPE_CHUNK_HEAD_SIZE, LoadTape_HEAD },
8014 { "INFO", -1, LoadTape_INFO },
8015 { "BODY", -1, LoadTape_BODY },
8019 while (getFileChunkBE(file, chunk_name, &chunk_size))
8023 while (chunk_info[i].name != NULL &&
8024 !strEqual(chunk_name, chunk_info[i].name))
8027 if (chunk_info[i].name == NULL)
8029 Warn("unknown chunk '%s' in tape file '%s'",
8030 chunk_name, filename);
8032 ReadUnusedBytesFromFile(file, chunk_size);
8034 else if (chunk_info[i].size != -1 &&
8035 chunk_info[i].size != chunk_size)
8037 Warn("wrong size (%d) of chunk '%s' in tape file '%s'",
8038 chunk_size, chunk_name, filename);
8040 ReadUnusedBytesFromFile(file, chunk_size);
8044 // call function to load this tape chunk
8045 int chunk_size_expected =
8046 (chunk_info[i].loader)(file, chunk_size, &tape);
8048 // the size of some chunks cannot be checked before reading other
8049 // chunks first (like "HEAD" and "BODY") that contain some header
8050 // information, so check them here
8051 if (chunk_size_expected != chunk_size)
8053 Warn("wrong size (%d) of chunk '%s' in tape file '%s'",
8054 chunk_size, chunk_name, filename);
8062 tape.length_frames = GetTapeLengthFrames();
8063 tape.length_seconds = GetTapeLengthSeconds();
8066 Debug("files:LoadTapeFromFilename", "tape file version: %d",
8068 Debug("files:LoadTapeFromFilename", "tape game version: %d",
8070 Debug("files:LoadTapeFromFilename", "tape engine version: %d",
8071 tape.engine_version);
8075 void LoadTape(int nr)
8077 char *filename = getTapeFilename(nr);
8079 LoadTapeFromFilename(filename);
8082 void LoadSolutionTape(int nr)
8084 char *filename = getSolutionTapeFilename(nr);
8086 LoadTapeFromFilename(filename);
8088 if (TAPE_IS_EMPTY(tape) &&
8089 level.game_engine_type == GAME_ENGINE_TYPE_SP &&
8090 level.native_sp_level->demo.is_available)
8091 CopyNativeTape_SP_to_RND(&level);
8094 static void SaveTape_VERS(FILE *file, struct TapeInfo *tape)
8096 putFileVersion(file, tape->file_version);
8097 putFileVersion(file, tape->game_version);
8100 static void SaveTape_HEAD(FILE *file, struct TapeInfo *tape)
8103 byte store_participating_players = 0;
8105 // set bits for participating players for compact storage
8106 for (i = 0; i < MAX_PLAYERS; i++)
8107 if (tape->player_participates[i])
8108 store_participating_players |= (1 << i);
8110 putFile32BitBE(file, tape->random_seed);
8111 putFile32BitBE(file, tape->date);
8112 putFile32BitBE(file, tape->length);
8114 putFile8Bit(file, store_participating_players);
8116 putFile8Bit(file, getTapeActionValue(tape));
8118 putFile8Bit(file, tape->property_bits);
8120 // unused bytes not at the end here for 4-byte alignment of engine_version
8121 WriteUnusedBytesToFile(file, TAPE_CHUNK_HEAD_UNUSED);
8123 putFileVersion(file, tape->engine_version);
8126 static void SaveTape_INFO(FILE *file, struct TapeInfo *tape)
8128 int level_identifier_size = strlen(tape->level_identifier) + 1;
8131 putFile16BitBE(file, level_identifier_size);
8133 for (i = 0; i < level_identifier_size; i++)
8134 putFile8Bit(file, tape->level_identifier[i]);
8136 putFile16BitBE(file, tape->level_nr);
8139 static void SaveTape_BODY(FILE *file, struct TapeInfo *tape)
8143 for (i = 0; i < tape->length; i++)
8145 if (tape->use_key_actions)
8147 for (j = 0; j < MAX_PLAYERS; j++)
8148 if (tape->player_participates[j])
8149 putFile8Bit(file, tape->pos[i].action[j]);
8152 if (tape->use_mouse_actions)
8154 putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_LX]);
8155 putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_LY]);
8156 putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_BUTTON]);
8159 putFile8Bit(file, tape->pos[i].delay);
8163 void SaveTapeToFilename(char *filename)
8167 int info_chunk_size;
8168 int body_chunk_size;
8170 if (!(file = fopen(filename, MODE_WRITE)))
8172 Warn("cannot save level recording file '%s'", filename);
8177 tape_pos_size = getTapePosSize(&tape);
8179 info_chunk_size = 2 + (strlen(tape.level_identifier) + 1) + 2;
8180 body_chunk_size = tape_pos_size * tape.length;
8182 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
8183 putFileChunkBE(file, "TAPE", CHUNK_SIZE_NONE);
8185 putFileChunkBE(file, "VERS", TAPE_CHUNK_VERS_SIZE);
8186 SaveTape_VERS(file, &tape);
8188 putFileChunkBE(file, "HEAD", TAPE_CHUNK_HEAD_SIZE);
8189 SaveTape_HEAD(file, &tape);
8191 putFileChunkBE(file, "INFO", info_chunk_size);
8192 SaveTape_INFO(file, &tape);
8194 putFileChunkBE(file, "BODY", body_chunk_size);
8195 SaveTape_BODY(file, &tape);
8199 SetFilePermissions(filename, PERMS_PRIVATE);
8202 void SaveTape(int nr)
8204 char *filename = getTapeFilename(nr);
8207 InitTapeDirectory(leveldir_current->subdir);
8209 tape.file_version = FILE_VERSION_ACTUAL;
8210 tape.game_version = GAME_VERSION_ACTUAL;
8212 tape.num_participating_players = 0;
8214 // count number of participating players
8215 for (i = 0; i < MAX_PLAYERS; i++)
8216 if (tape.player_participates[i])
8217 tape.num_participating_players++;
8219 SaveTapeToFilename(filename);
8221 tape.changed = FALSE;
8224 static boolean SaveTapeCheckedExt(int nr, char *msg_replace, char *msg_saved,
8225 unsigned int req_state_added)
8227 char *filename = getTapeFilename(nr);
8228 boolean new_tape = !fileExists(filename);
8229 boolean tape_saved = FALSE;
8231 if (new_tape || Request(msg_replace, REQ_ASK | req_state_added))
8236 Request(msg_saved, REQ_CONFIRM | req_state_added);
8244 boolean SaveTapeChecked(int nr)
8246 return SaveTapeCheckedExt(nr, "Replace old tape?", "Tape saved!", 0);
8249 boolean SaveTapeChecked_LevelSolved(int nr)
8251 return SaveTapeCheckedExt(nr, "Level solved! Replace old tape?",
8252 "Level solved! Tape saved!", REQ_STAY_OPEN);
8255 void DumpTape(struct TapeInfo *tape)
8257 int tape_frame_counter;
8260 if (tape->no_valid_file)
8262 Warn("cannot dump -- no valid tape file found");
8268 Print("Tape of Level %03d (file version %08d, game version %08d)\n",
8269 tape->level_nr, tape->file_version, tape->game_version);
8270 Print(" (effective engine version %08d)\n",
8271 tape->engine_version);
8272 Print("Level series identifier: '%s'\n", tape->level_identifier);
8275 tape_frame_counter = 0;
8277 for (i = 0; i < tape->length; i++)
8279 if (i >= MAX_TAPE_LEN)
8284 for (j = 0; j < MAX_PLAYERS; j++)
8286 if (tape->player_participates[j])
8288 int action = tape->pos[i].action[j];
8290 Print("%d:%02x ", j, action);
8291 Print("[%c%c%c%c|%c%c] - ",
8292 (action & JOY_LEFT ? '<' : ' '),
8293 (action & JOY_RIGHT ? '>' : ' '),
8294 (action & JOY_UP ? '^' : ' '),
8295 (action & JOY_DOWN ? 'v' : ' '),
8296 (action & JOY_BUTTON_1 ? '1' : ' '),
8297 (action & JOY_BUTTON_2 ? '2' : ' '));
8301 Print("(%03d) ", tape->pos[i].delay);
8302 Print("[%05d]\n", tape_frame_counter);
8304 tape_frame_counter += tape->pos[i].delay;
8311 // ============================================================================
8312 // score file functions
8313 // ============================================================================
8315 void LoadScore(int nr)
8318 char *filename = getScoreFilename(nr);
8319 char cookie[MAX_LINE_LEN];
8320 char line[MAX_LINE_LEN];
8324 // always start with reliable default values
8325 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
8327 strcpy(highscore[i].Name, EMPTY_PLAYER_NAME);
8328 highscore[i].Score = 0;
8331 if (!(file = fopen(filename, MODE_READ)))
8334 // check file identifier
8335 if (fgets(cookie, MAX_LINE_LEN, file) == NULL)
8337 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
8338 cookie[strlen(cookie) - 1] = '\0';
8340 if (!checkCookieString(cookie, SCORE_COOKIE))
8342 Warn("unknown format of score file '%s'", filename);
8349 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
8351 if (fscanf(file, "%d", &highscore[i].Score) == EOF)
8352 Warn("fscanf() failed; %s", strerror(errno));
8354 if (fgets(line, MAX_LINE_LEN, file) == NULL)
8357 if (strlen(line) > 0 && line[strlen(line) - 1] == '\n')
8358 line[strlen(line) - 1] = '\0';
8360 for (line_ptr = line; *line_ptr; line_ptr++)
8362 if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != '\0')
8364 strncpy(highscore[i].Name, line_ptr, MAX_PLAYER_NAME_LEN);
8365 highscore[i].Name[MAX_PLAYER_NAME_LEN] = '\0';
8374 void SaveScore(int nr)
8377 int permissions = (program.global_scores ? PERMS_PUBLIC : PERMS_PRIVATE);
8378 char *filename = getScoreFilename(nr);
8381 // used instead of "leveldir_current->subdir" (for network games)
8382 InitScoreDirectory(levelset.identifier);
8384 if (!(file = fopen(filename, MODE_WRITE)))
8386 Warn("cannot save score for level %d", nr);
8391 fprintf(file, "%s\n\n", SCORE_COOKIE);
8393 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
8394 fprintf(file, "%d %s\n", highscore[i].Score, highscore[i].Name);
8398 SetFilePermissions(filename, permissions);
8402 // ============================================================================
8403 // setup file functions
8404 // ============================================================================
8406 #define TOKEN_STR_PLAYER_PREFIX "player_"
8409 static struct TokenInfo global_setup_tokens[] =
8413 &setup.player_name, "player_name"
8417 &setup.multiple_users, "multiple_users"
8421 &setup.sound, "sound"
8425 &setup.sound_loops, "repeating_sound_loops"
8429 &setup.sound_music, "background_music"
8433 &setup.sound_simple, "simple_sound_effects"
8437 &setup.toons, "toons"
8441 &setup.scroll_delay, "scroll_delay"
8445 &setup.forced_scroll_delay, "forced_scroll_delay"
8449 &setup.scroll_delay_value, "scroll_delay_value"
8453 &setup.engine_snapshot_mode, "engine_snapshot_mode"
8457 &setup.engine_snapshot_memory, "engine_snapshot_memory"
8461 &setup.fade_screens, "fade_screens"
8465 &setup.autorecord, "automatic_tape_recording"
8469 &setup.show_titlescreen, "show_titlescreen"
8473 &setup.quick_doors, "quick_doors"
8477 &setup.team_mode, "team_mode"
8481 &setup.handicap, "handicap"
8485 &setup.skip_levels, "skip_levels"
8489 &setup.increment_levels, "increment_levels"
8493 &setup.auto_play_next_level, "auto_play_next_level"
8497 &setup.skip_scores_after_game, "skip_scores_after_game"
8501 &setup.time_limit, "time_limit"
8505 &setup.fullscreen, "fullscreen"
8509 &setup.window_scaling_percent, "window_scaling_percent"
8513 &setup.window_scaling_quality, "window_scaling_quality"
8517 &setup.screen_rendering_mode, "screen_rendering_mode"
8521 &setup.vsync_mode, "vsync_mode"
8525 &setup.ask_on_escape, "ask_on_escape"
8529 &setup.ask_on_escape_editor, "ask_on_escape_editor"
8533 &setup.ask_on_game_over, "ask_on_game_over"
8537 &setup.quick_switch, "quick_player_switch"
8541 &setup.input_on_focus, "input_on_focus"
8545 &setup.prefer_aga_graphics, "prefer_aga_graphics"
8549 &setup.prefer_lowpass_sounds, "prefer_lowpass_sounds"
8553 &setup.prefer_extra_panel_items, "prefer_extra_panel_items"
8557 &setup.game_speed_extended, "game_speed_extended"
8561 &setup.game_frame_delay, "game_frame_delay"
8565 &setup.sp_show_border_elements, "sp_show_border_elements"
8569 &setup.small_game_graphics, "small_game_graphics"
8573 &setup.show_snapshot_buttons, "show_snapshot_buttons"
8577 &setup.graphics_set, "graphics_set"
8581 &setup.sounds_set, "sounds_set"
8585 &setup.music_set, "music_set"
8589 &setup.override_level_graphics, "override_level_graphics"
8593 &setup.override_level_sounds, "override_level_sounds"
8597 &setup.override_level_music, "override_level_music"
8601 &setup.volume_simple, "volume_simple"
8605 &setup.volume_loops, "volume_loops"
8609 &setup.volume_music, "volume_music"
8613 &setup.network_mode, "network_mode"
8617 &setup.network_player_nr, "network_player"
8621 &setup.network_server_hostname, "network_server_hostname"
8625 &setup.touch.control_type, "touch.control_type"
8629 &setup.touch.move_distance, "touch.move_distance"
8633 &setup.touch.drop_distance, "touch.drop_distance"
8637 &setup.touch.transparency, "touch.transparency"
8641 &setup.touch.draw_outlined, "touch.draw_outlined"
8645 &setup.touch.draw_pressed, "touch.draw_pressed"
8649 &setup.touch.grid_xsize[0], "touch.virtual_buttons.0.xsize"
8653 &setup.touch.grid_ysize[0], "touch.virtual_buttons.0.ysize"
8657 &setup.touch.grid_xsize[1], "touch.virtual_buttons.1.xsize"
8661 &setup.touch.grid_ysize[1], "touch.virtual_buttons.1.ysize"
8665 static struct TokenInfo auto_setup_tokens[] =
8669 &setup.auto_setup.editor_zoom_tilesize, "editor.zoom_tilesize"
8673 static struct TokenInfo editor_setup_tokens[] =
8677 &setup.editor.el_classic, "editor.el_classic"
8681 &setup.editor.el_custom, "editor.el_custom"
8685 &setup.editor.el_user_defined, "editor.el_user_defined"
8689 &setup.editor.el_dynamic, "editor.el_dynamic"
8693 &setup.editor.el_headlines, "editor.el_headlines"
8697 &setup.editor.show_element_token, "editor.show_element_token"
8701 static struct TokenInfo editor_cascade_setup_tokens[] =
8705 &setup.editor_cascade.el_bd, "editor.cascade.el_bd"
8709 &setup.editor_cascade.el_em, "editor.cascade.el_em"
8713 &setup.editor_cascade.el_emc, "editor.cascade.el_emc"
8717 &setup.editor_cascade.el_rnd, "editor.cascade.el_rnd"
8721 &setup.editor_cascade.el_sb, "editor.cascade.el_sb"
8725 &setup.editor_cascade.el_sp, "editor.cascade.el_sp"
8729 &setup.editor_cascade.el_dc, "editor.cascade.el_dc"
8733 &setup.editor_cascade.el_dx, "editor.cascade.el_dx"
8737 &setup.editor_cascade.el_mm, "editor.cascade.el_mm"
8741 &setup.editor_cascade.el_df, "editor.cascade.el_df"
8745 &setup.editor_cascade.el_chars, "editor.cascade.el_chars"
8749 &setup.editor_cascade.el_steel_chars, "editor.cascade.el_steel_chars"
8753 &setup.editor_cascade.el_ce, "editor.cascade.el_ce"
8757 &setup.editor_cascade.el_ge, "editor.cascade.el_ge"
8761 &setup.editor_cascade.el_ref, "editor.cascade.el_ref"
8765 &setup.editor_cascade.el_user, "editor.cascade.el_user"
8769 &setup.editor_cascade.el_dynamic, "editor.cascade.el_dynamic"
8773 static struct TokenInfo shortcut_setup_tokens[] =
8777 &setup.shortcut.save_game, "shortcut.save_game"
8781 &setup.shortcut.load_game, "shortcut.load_game"
8785 &setup.shortcut.toggle_pause, "shortcut.toggle_pause"
8789 &setup.shortcut.focus_player[0], "shortcut.focus_player_1"
8793 &setup.shortcut.focus_player[1], "shortcut.focus_player_2"
8797 &setup.shortcut.focus_player[2], "shortcut.focus_player_3"
8801 &setup.shortcut.focus_player[3], "shortcut.focus_player_4"
8805 &setup.shortcut.focus_player_all, "shortcut.focus_player_all"
8809 &setup.shortcut.tape_eject, "shortcut.tape_eject"
8813 &setup.shortcut.tape_extra, "shortcut.tape_extra"
8817 &setup.shortcut.tape_stop, "shortcut.tape_stop"
8821 &setup.shortcut.tape_pause, "shortcut.tape_pause"
8825 &setup.shortcut.tape_record, "shortcut.tape_record"
8829 &setup.shortcut.tape_play, "shortcut.tape_play"
8833 &setup.shortcut.sound_simple, "shortcut.sound_simple"
8837 &setup.shortcut.sound_loops, "shortcut.sound_loops"
8841 &setup.shortcut.sound_music, "shortcut.sound_music"
8845 &setup.shortcut.snap_left, "shortcut.snap_left"
8849 &setup.shortcut.snap_right, "shortcut.snap_right"
8853 &setup.shortcut.snap_up, "shortcut.snap_up"
8857 &setup.shortcut.snap_down, "shortcut.snap_down"
8861 static struct SetupInputInfo setup_input;
8862 static struct TokenInfo player_setup_tokens[] =
8866 &setup_input.use_joystick, ".use_joystick"
8870 &setup_input.joy.device_name, ".joy.device_name"
8874 &setup_input.joy.xleft, ".joy.xleft"
8878 &setup_input.joy.xmiddle, ".joy.xmiddle"
8882 &setup_input.joy.xright, ".joy.xright"
8886 &setup_input.joy.yupper, ".joy.yupper"
8890 &setup_input.joy.ymiddle, ".joy.ymiddle"
8894 &setup_input.joy.ylower, ".joy.ylower"
8898 &setup_input.joy.snap, ".joy.snap_field"
8902 &setup_input.joy.drop, ".joy.place_bomb"
8906 &setup_input.key.left, ".key.move_left"
8910 &setup_input.key.right, ".key.move_right"
8914 &setup_input.key.up, ".key.move_up"
8918 &setup_input.key.down, ".key.move_down"
8922 &setup_input.key.snap, ".key.snap_field"
8926 &setup_input.key.drop, ".key.place_bomb"
8930 static struct TokenInfo system_setup_tokens[] =
8934 &setup.system.sdl_renderdriver, "system.sdl_renderdriver"
8938 &setup.system.sdl_videodriver, "system.sdl_videodriver"
8942 &setup.system.sdl_audiodriver, "system.sdl_audiodriver"
8946 &setup.system.audio_fragment_size, "system.audio_fragment_size"
8950 static struct TokenInfo internal_setup_tokens[] =
8954 &setup.internal.program_title, "program_title"
8958 &setup.internal.program_version, "program_version"
8962 &setup.internal.program_author, "program_author"
8966 &setup.internal.program_email, "program_email"
8970 &setup.internal.program_website, "program_website"
8974 &setup.internal.program_copyright, "program_copyright"
8978 &setup.internal.program_company, "program_company"
8982 &setup.internal.program_icon_file, "program_icon_file"
8986 &setup.internal.default_graphics_set, "default_graphics_set"
8990 &setup.internal.default_sounds_set, "default_sounds_set"
8994 &setup.internal.default_music_set, "default_music_set"
8998 &setup.internal.fallback_graphics_file, "fallback_graphics_file"
9002 &setup.internal.fallback_sounds_file, "fallback_sounds_file"
9006 &setup.internal.fallback_music_file, "fallback_music_file"
9010 &setup.internal.default_level_series, "default_level_series"
9014 &setup.internal.default_window_width, "default_window_width"
9018 &setup.internal.default_window_height, "default_window_height"
9022 &setup.internal.choose_from_top_leveldir, "choose_from_top_leveldir"
9026 &setup.internal.show_scaling_in_title, "show_scaling_in_title"
9030 &setup.internal.create_user_levelset, "create_user_levelset"
9034 &setup.internal.menu_game, "menu_game"
9038 &setup.internal.menu_editor, "menu_editor"
9042 &setup.internal.menu_graphics, "menu_graphics"
9046 &setup.internal.menu_sound, "menu_sound"
9050 &setup.internal.menu_artwork, "menu_artwork"
9054 &setup.internal.menu_input, "menu_input"
9058 &setup.internal.menu_touch, "menu_touch"
9062 &setup.internal.menu_shortcuts, "menu_shortcuts"
9066 &setup.internal.menu_exit, "menu_exit"
9070 &setup.internal.menu_save_and_exit, "menu_save_and_exit"
9074 static struct TokenInfo debug_setup_tokens[] =
9078 &setup.debug.frame_delay[0], "debug.frame_delay_0"
9082 &setup.debug.frame_delay[1], "debug.frame_delay_1"
9086 &setup.debug.frame_delay[2], "debug.frame_delay_2"
9090 &setup.debug.frame_delay[3], "debug.frame_delay_3"
9094 &setup.debug.frame_delay[4], "debug.frame_delay_4"
9098 &setup.debug.frame_delay[5], "debug.frame_delay_5"
9102 &setup.debug.frame_delay[6], "debug.frame_delay_6"
9106 &setup.debug.frame_delay[7], "debug.frame_delay_7"
9110 &setup.debug.frame_delay[8], "debug.frame_delay_8"
9114 &setup.debug.frame_delay[9], "debug.frame_delay_9"
9118 &setup.debug.frame_delay_key[0], "debug.key.frame_delay_0"
9122 &setup.debug.frame_delay_key[1], "debug.key.frame_delay_1"
9126 &setup.debug.frame_delay_key[2], "debug.key.frame_delay_2"
9130 &setup.debug.frame_delay_key[3], "debug.key.frame_delay_3"
9134 &setup.debug.frame_delay_key[4], "debug.key.frame_delay_4"
9138 &setup.debug.frame_delay_key[5], "debug.key.frame_delay_5"
9142 &setup.debug.frame_delay_key[6], "debug.key.frame_delay_6"
9146 &setup.debug.frame_delay_key[7], "debug.key.frame_delay_7"
9150 &setup.debug.frame_delay_key[8], "debug.key.frame_delay_8"
9154 &setup.debug.frame_delay_key[9], "debug.key.frame_delay_9"
9158 &setup.debug.frame_delay_use_mod_key, "debug.frame_delay.use_mod_key"},
9161 &setup.debug.frame_delay_game_only, "debug.frame_delay.game_only"
9165 &setup.debug.show_frames_per_second, "debug.show_frames_per_second"
9169 &setup.debug.xsn_mode, "debug.xsn_mode"
9173 &setup.debug.xsn_percent, "debug.xsn_percent"
9177 static struct TokenInfo options_setup_tokens[] =
9181 &setup.options.verbose, "options.verbose"
9185 static void setSetupInfoToDefaults(struct SetupInfo *si)
9189 si->player_name = getStringCopy(getDefaultUserName(user.nr));
9191 si->multiple_users = TRUE;
9194 si->sound_loops = TRUE;
9195 si->sound_music = TRUE;
9196 si->sound_simple = TRUE;
9198 si->scroll_delay = TRUE;
9199 si->forced_scroll_delay = FALSE;
9200 si->scroll_delay_value = STD_SCROLL_DELAY;
9201 si->engine_snapshot_mode = getStringCopy(STR_SNAPSHOT_MODE_DEFAULT);
9202 si->engine_snapshot_memory = SNAPSHOT_MEMORY_DEFAULT;
9203 si->fade_screens = TRUE;
9204 si->autorecord = TRUE;
9205 si->show_titlescreen = TRUE;
9206 si->quick_doors = FALSE;
9207 si->team_mode = FALSE;
9208 si->handicap = TRUE;
9209 si->skip_levels = TRUE;
9210 si->increment_levels = TRUE;
9211 si->auto_play_next_level = TRUE;
9212 si->skip_scores_after_game = FALSE;
9213 si->time_limit = TRUE;
9214 si->fullscreen = FALSE;
9215 si->window_scaling_percent = STD_WINDOW_SCALING_PERCENT;
9216 si->window_scaling_quality = getStringCopy(SCALING_QUALITY_DEFAULT);
9217 si->screen_rendering_mode = getStringCopy(STR_SPECIAL_RENDERING_DEFAULT);
9218 si->vsync_mode = getStringCopy(STR_VSYNC_MODE_DEFAULT);
9219 si->ask_on_escape = TRUE;
9220 si->ask_on_escape_editor = TRUE;
9221 si->ask_on_game_over = TRUE;
9222 si->quick_switch = FALSE;
9223 si->input_on_focus = FALSE;
9224 si->prefer_aga_graphics = TRUE;
9225 si->prefer_lowpass_sounds = FALSE;
9226 si->prefer_extra_panel_items = TRUE;
9227 si->game_speed_extended = FALSE;
9228 si->game_frame_delay = GAME_FRAME_DELAY;
9229 si->sp_show_border_elements = FALSE;
9230 si->small_game_graphics = FALSE;
9231 si->show_snapshot_buttons = FALSE;
9233 si->graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
9234 si->sounds_set = getStringCopy(SND_CLASSIC_SUBDIR);
9235 si->music_set = getStringCopy(MUS_CLASSIC_SUBDIR);
9237 si->override_level_graphics = FALSE;
9238 si->override_level_sounds = FALSE;
9239 si->override_level_music = FALSE;
9241 si->volume_simple = 100; // percent
9242 si->volume_loops = 100; // percent
9243 si->volume_music = 100; // percent
9245 si->network_mode = FALSE;
9246 si->network_player_nr = 0; // first player
9247 si->network_server_hostname = getStringCopy(STR_NETWORK_AUTO_DETECT);
9249 si->touch.control_type = getStringCopy(TOUCH_CONTROL_DEFAULT);
9250 si->touch.move_distance = TOUCH_MOVE_DISTANCE_DEFAULT; // percent
9251 si->touch.drop_distance = TOUCH_DROP_DISTANCE_DEFAULT; // percent
9252 si->touch.transparency = TOUCH_TRANSPARENCY_DEFAULT; // percent
9253 si->touch.draw_outlined = TRUE;
9254 si->touch.draw_pressed = TRUE;
9256 for (i = 0; i < 2; i++)
9258 char *default_grid_button[6][2] =
9264 { "111222", " vv " },
9265 { "111222", " vv " }
9267 int grid_xsize = DEFAULT_GRID_XSIZE(i);
9268 int grid_ysize = DEFAULT_GRID_YSIZE(i);
9269 int min_xsize = MIN(6, grid_xsize);
9270 int min_ysize = MIN(6, grid_ysize);
9271 int startx = grid_xsize - min_xsize;
9272 int starty = grid_ysize - min_ysize;
9275 // virtual buttons grid can only be set to defaults if video is initialized
9276 // (this will be repeated if virtual buttons are not loaded from setup file)
9277 if (video.initialized)
9279 si->touch.grid_xsize[i] = grid_xsize;
9280 si->touch.grid_ysize[i] = grid_ysize;
9284 si->touch.grid_xsize[i] = -1;
9285 si->touch.grid_ysize[i] = -1;
9288 for (x = 0; x < MAX_GRID_XSIZE; x++)
9289 for (y = 0; y < MAX_GRID_YSIZE; y++)
9290 si->touch.grid_button[i][x][y] = CHAR_GRID_BUTTON_NONE;
9292 for (x = 0; x < min_xsize; x++)
9293 for (y = 0; y < min_ysize; y++)
9294 si->touch.grid_button[i][x][starty + y] =
9295 default_grid_button[y][0][x];
9297 for (x = 0; x < min_xsize; x++)
9298 for (y = 0; y < min_ysize; y++)
9299 si->touch.grid_button[i][startx + x][starty + y] =
9300 default_grid_button[y][1][x];
9303 si->touch.grid_initialized = video.initialized;
9305 si->editor.el_boulderdash = TRUE;
9306 si->editor.el_emerald_mine = TRUE;
9307 si->editor.el_emerald_mine_club = TRUE;
9308 si->editor.el_more = TRUE;
9309 si->editor.el_sokoban = TRUE;
9310 si->editor.el_supaplex = TRUE;
9311 si->editor.el_diamond_caves = TRUE;
9312 si->editor.el_dx_boulderdash = TRUE;
9314 si->editor.el_mirror_magic = TRUE;
9315 si->editor.el_deflektor = TRUE;
9317 si->editor.el_chars = TRUE;
9318 si->editor.el_steel_chars = TRUE;
9320 si->editor.el_classic = TRUE;
9321 si->editor.el_custom = TRUE;
9323 si->editor.el_user_defined = FALSE;
9324 si->editor.el_dynamic = TRUE;
9326 si->editor.el_headlines = TRUE;
9328 si->editor.show_element_token = FALSE;
9330 si->editor.use_template_for_new_levels = TRUE;
9332 si->shortcut.save_game = DEFAULT_KEY_SAVE_GAME;
9333 si->shortcut.load_game = DEFAULT_KEY_LOAD_GAME;
9334 si->shortcut.toggle_pause = DEFAULT_KEY_TOGGLE_PAUSE;
9336 si->shortcut.focus_player[0] = DEFAULT_KEY_FOCUS_PLAYER_1;
9337 si->shortcut.focus_player[1] = DEFAULT_KEY_FOCUS_PLAYER_2;
9338 si->shortcut.focus_player[2] = DEFAULT_KEY_FOCUS_PLAYER_3;
9339 si->shortcut.focus_player[3] = DEFAULT_KEY_FOCUS_PLAYER_4;
9340 si->shortcut.focus_player_all = DEFAULT_KEY_FOCUS_PLAYER_ALL;
9342 si->shortcut.tape_eject = DEFAULT_KEY_TAPE_EJECT;
9343 si->shortcut.tape_extra = DEFAULT_KEY_TAPE_EXTRA;
9344 si->shortcut.tape_stop = DEFAULT_KEY_TAPE_STOP;
9345 si->shortcut.tape_pause = DEFAULT_KEY_TAPE_PAUSE;
9346 si->shortcut.tape_record = DEFAULT_KEY_TAPE_RECORD;
9347 si->shortcut.tape_play = DEFAULT_KEY_TAPE_PLAY;
9349 si->shortcut.sound_simple = DEFAULT_KEY_SOUND_SIMPLE;
9350 si->shortcut.sound_loops = DEFAULT_KEY_SOUND_LOOPS;
9351 si->shortcut.sound_music = DEFAULT_KEY_SOUND_MUSIC;
9353 si->shortcut.snap_left = DEFAULT_KEY_SNAP_LEFT;
9354 si->shortcut.snap_right = DEFAULT_KEY_SNAP_RIGHT;
9355 si->shortcut.snap_up = DEFAULT_KEY_SNAP_UP;
9356 si->shortcut.snap_down = DEFAULT_KEY_SNAP_DOWN;
9358 for (i = 0; i < MAX_PLAYERS; i++)
9360 si->input[i].use_joystick = FALSE;
9361 si->input[i].joy.device_name=getStringCopy(getDeviceNameFromJoystickNr(i));
9362 si->input[i].joy.xleft = JOYSTICK_XLEFT;
9363 si->input[i].joy.xmiddle = JOYSTICK_XMIDDLE;
9364 si->input[i].joy.xright = JOYSTICK_XRIGHT;
9365 si->input[i].joy.yupper = JOYSTICK_YUPPER;
9366 si->input[i].joy.ymiddle = JOYSTICK_YMIDDLE;
9367 si->input[i].joy.ylower = JOYSTICK_YLOWER;
9368 si->input[i].joy.snap = (i == 0 ? JOY_BUTTON_1 : 0);
9369 si->input[i].joy.drop = (i == 0 ? JOY_BUTTON_2 : 0);
9370 si->input[i].key.left = (i == 0 ? DEFAULT_KEY_LEFT : KSYM_UNDEFINED);
9371 si->input[i].key.right = (i == 0 ? DEFAULT_KEY_RIGHT : KSYM_UNDEFINED);
9372 si->input[i].key.up = (i == 0 ? DEFAULT_KEY_UP : KSYM_UNDEFINED);
9373 si->input[i].key.down = (i == 0 ? DEFAULT_KEY_DOWN : KSYM_UNDEFINED);
9374 si->input[i].key.snap = (i == 0 ? DEFAULT_KEY_SNAP : KSYM_UNDEFINED);
9375 si->input[i].key.drop = (i == 0 ? DEFAULT_KEY_DROP : KSYM_UNDEFINED);
9378 si->system.sdl_renderdriver = getStringCopy(ARG_DEFAULT);
9379 si->system.sdl_videodriver = getStringCopy(ARG_DEFAULT);
9380 si->system.sdl_audiodriver = getStringCopy(ARG_DEFAULT);
9381 si->system.audio_fragment_size = DEFAULT_AUDIO_FRAGMENT_SIZE;
9383 si->internal.program_title = getStringCopy(PROGRAM_TITLE_STRING);
9384 si->internal.program_version = getStringCopy(getProgramRealVersionString());
9385 si->internal.program_author = getStringCopy(PROGRAM_AUTHOR_STRING);
9386 si->internal.program_email = getStringCopy(PROGRAM_EMAIL_STRING);
9387 si->internal.program_website = getStringCopy(PROGRAM_WEBSITE_STRING);
9388 si->internal.program_copyright = getStringCopy(PROGRAM_COPYRIGHT_STRING);
9389 si->internal.program_company = getStringCopy(PROGRAM_COMPANY_STRING);
9391 si->internal.program_icon_file = getStringCopy(PROGRAM_ICON_FILENAME);
9393 si->internal.default_graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
9394 si->internal.default_sounds_set = getStringCopy(SND_CLASSIC_SUBDIR);
9395 si->internal.default_music_set = getStringCopy(MUS_CLASSIC_SUBDIR);
9397 si->internal.fallback_graphics_file = getStringCopy(UNDEFINED_FILENAME);
9398 si->internal.fallback_sounds_file = getStringCopy(UNDEFINED_FILENAME);
9399 si->internal.fallback_music_file = getStringCopy(UNDEFINED_FILENAME);
9401 si->internal.default_level_series = getStringCopy(UNDEFINED_LEVELSET);
9402 si->internal.choose_from_top_leveldir = FALSE;
9403 si->internal.show_scaling_in_title = TRUE;
9404 si->internal.create_user_levelset = TRUE;
9406 si->internal.default_window_width = WIN_XSIZE_DEFAULT;
9407 si->internal.default_window_height = WIN_YSIZE_DEFAULT;
9409 si->debug.frame_delay[0] = DEFAULT_FRAME_DELAY_0;
9410 si->debug.frame_delay[1] = DEFAULT_FRAME_DELAY_1;
9411 si->debug.frame_delay[2] = DEFAULT_FRAME_DELAY_2;
9412 si->debug.frame_delay[3] = DEFAULT_FRAME_DELAY_3;
9413 si->debug.frame_delay[4] = DEFAULT_FRAME_DELAY_4;
9414 si->debug.frame_delay[5] = DEFAULT_FRAME_DELAY_5;
9415 si->debug.frame_delay[6] = DEFAULT_FRAME_DELAY_6;
9416 si->debug.frame_delay[7] = DEFAULT_FRAME_DELAY_7;
9417 si->debug.frame_delay[8] = DEFAULT_FRAME_DELAY_8;
9418 si->debug.frame_delay[9] = DEFAULT_FRAME_DELAY_9;
9420 si->debug.frame_delay_key[0] = DEFAULT_KEY_FRAME_DELAY_0;
9421 si->debug.frame_delay_key[1] = DEFAULT_KEY_FRAME_DELAY_1;
9422 si->debug.frame_delay_key[2] = DEFAULT_KEY_FRAME_DELAY_2;
9423 si->debug.frame_delay_key[3] = DEFAULT_KEY_FRAME_DELAY_3;
9424 si->debug.frame_delay_key[4] = DEFAULT_KEY_FRAME_DELAY_4;
9425 si->debug.frame_delay_key[5] = DEFAULT_KEY_FRAME_DELAY_5;
9426 si->debug.frame_delay_key[6] = DEFAULT_KEY_FRAME_DELAY_6;
9427 si->debug.frame_delay_key[7] = DEFAULT_KEY_FRAME_DELAY_7;
9428 si->debug.frame_delay_key[8] = DEFAULT_KEY_FRAME_DELAY_8;
9429 si->debug.frame_delay_key[9] = DEFAULT_KEY_FRAME_DELAY_9;
9431 si->debug.frame_delay_use_mod_key = DEFAULT_FRAME_DELAY_USE_MOD_KEY;
9432 si->debug.frame_delay_game_only = DEFAULT_FRAME_DELAY_GAME_ONLY;
9434 si->debug.show_frames_per_second = FALSE;
9436 si->debug.xsn_mode = AUTO;
9437 si->debug.xsn_percent = 0;
9439 si->options.verbose = FALSE;
9441 #if defined(PLATFORM_ANDROID)
9442 si->fullscreen = TRUE;
9445 setHideSetupEntry(&setup.debug.xsn_mode);
9448 static void setSetupInfoToDefaults_AutoSetup(struct SetupInfo *si)
9450 si->auto_setup.editor_zoom_tilesize = MINI_TILESIZE;
9453 static void setSetupInfoToDefaults_EditorCascade(struct SetupInfo *si)
9455 si->editor_cascade.el_bd = TRUE;
9456 si->editor_cascade.el_em = TRUE;
9457 si->editor_cascade.el_emc = TRUE;
9458 si->editor_cascade.el_rnd = TRUE;
9459 si->editor_cascade.el_sb = TRUE;
9460 si->editor_cascade.el_sp = TRUE;
9461 si->editor_cascade.el_dc = TRUE;
9462 si->editor_cascade.el_dx = TRUE;
9464 si->editor_cascade.el_mm = TRUE;
9465 si->editor_cascade.el_df = TRUE;
9467 si->editor_cascade.el_chars = FALSE;
9468 si->editor_cascade.el_steel_chars = FALSE;
9469 si->editor_cascade.el_ce = FALSE;
9470 si->editor_cascade.el_ge = FALSE;
9471 si->editor_cascade.el_ref = FALSE;
9472 si->editor_cascade.el_user = FALSE;
9473 si->editor_cascade.el_dynamic = FALSE;
9476 #define MAX_HIDE_SETUP_TOKEN_SIZE 20
9478 static char *getHideSetupToken(void *setup_value)
9480 static char hide_setup_token[MAX_HIDE_SETUP_TOKEN_SIZE];
9482 if (setup_value != NULL)
9483 snprintf(hide_setup_token, MAX_HIDE_SETUP_TOKEN_SIZE, "%p", setup_value);
9485 return hide_setup_token;
9488 void setHideSetupEntry(void *setup_value)
9490 char *hide_setup_token = getHideSetupToken(setup_value);
9492 if (hide_setup_hash == NULL)
9493 hide_setup_hash = newSetupFileHash();
9495 if (setup_value != NULL)
9496 setHashEntry(hide_setup_hash, hide_setup_token, "");
9499 void removeHideSetupEntry(void *setup_value)
9501 char *hide_setup_token = getHideSetupToken(setup_value);
9503 if (setup_value != NULL)
9504 removeHashEntry(hide_setup_hash, hide_setup_token);
9507 boolean hideSetupEntry(void *setup_value)
9509 char *hide_setup_token = getHideSetupToken(setup_value);
9511 return (setup_value != NULL &&
9512 getHashEntry(hide_setup_hash, hide_setup_token) != NULL);
9515 static void setSetupInfoFromTokenText(SetupFileHash *setup_file_hash,
9516 struct TokenInfo *token_info,
9517 int token_nr, char *token_text)
9519 char *token_hide_text = getStringCat2(token_text, ".hide");
9520 char *token_hide_value = getHashEntry(setup_file_hash, token_hide_text);
9522 // set the value of this setup option in the setup option structure
9523 setSetupInfo(token_info, token_nr, getHashEntry(setup_file_hash, token_text));
9525 // check if this setup option should be hidden in the setup menu
9526 if (token_hide_value != NULL && get_boolean_from_string(token_hide_value))
9527 setHideSetupEntry(token_info[token_nr].value);
9529 free(token_hide_text);
9532 static void setSetupInfoFromTokenInfo(SetupFileHash *setup_file_hash,
9533 struct TokenInfo *token_info,
9536 setSetupInfoFromTokenText(setup_file_hash, token_info, token_nr,
9537 token_info[token_nr].text);
9540 static void decodeSetupFileHash(SetupFileHash *setup_file_hash)
9544 if (!setup_file_hash)
9547 for (i = 0; i < ARRAY_SIZE(global_setup_tokens); i++)
9548 setSetupInfoFromTokenInfo(setup_file_hash, global_setup_tokens, i);
9550 setup.touch.grid_initialized = TRUE;
9551 for (i = 0; i < 2; i++)
9553 int grid_xsize = setup.touch.grid_xsize[i];
9554 int grid_ysize = setup.touch.grid_ysize[i];
9557 // if virtual buttons are not loaded from setup file, repeat initializing
9558 // virtual buttons grid with default values later when video is initialized
9559 if (grid_xsize == -1 ||
9562 setup.touch.grid_initialized = FALSE;
9567 for (y = 0; y < grid_ysize; y++)
9569 char token_string[MAX_LINE_LEN];
9571 sprintf(token_string, "touch.virtual_buttons.%d.%02d", i, y);
9573 char *value_string = getHashEntry(setup_file_hash, token_string);
9575 if (value_string == NULL)
9578 for (x = 0; x < grid_xsize; x++)
9580 char c = value_string[x];
9582 setup.touch.grid_button[i][x][y] =
9583 (c == '.' ? CHAR_GRID_BUTTON_NONE : c);
9588 for (i = 0; i < ARRAY_SIZE(editor_setup_tokens); i++)
9589 setSetupInfoFromTokenInfo(setup_file_hash, editor_setup_tokens, i);
9591 for (i = 0; i < ARRAY_SIZE(shortcut_setup_tokens); i++)
9592 setSetupInfoFromTokenInfo(setup_file_hash, shortcut_setup_tokens, i);
9594 for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
9598 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
9600 setup_input = setup.input[pnr];
9601 for (i = 0; i < ARRAY_SIZE(player_setup_tokens); i++)
9603 char full_token[100];
9605 sprintf(full_token, "%s%s", prefix, player_setup_tokens[i].text);
9606 setSetupInfoFromTokenText(setup_file_hash, player_setup_tokens, i,
9609 setup.input[pnr] = setup_input;
9612 for (i = 0; i < ARRAY_SIZE(system_setup_tokens); i++)
9613 setSetupInfoFromTokenInfo(setup_file_hash, system_setup_tokens, i);
9615 for (i = 0; i < ARRAY_SIZE(internal_setup_tokens); i++)
9616 setSetupInfoFromTokenInfo(setup_file_hash, internal_setup_tokens, i);
9618 for (i = 0; i < ARRAY_SIZE(debug_setup_tokens); i++)
9619 setSetupInfoFromTokenInfo(setup_file_hash, debug_setup_tokens, i);
9621 for (i = 0; i < ARRAY_SIZE(options_setup_tokens); i++)
9622 setSetupInfoFromTokenInfo(setup_file_hash, options_setup_tokens, i);
9624 setHideRelatedSetupEntries();
9627 static void decodeSetupFileHash_AutoSetup(SetupFileHash *setup_file_hash)
9631 if (!setup_file_hash)
9634 for (i = 0; i < ARRAY_SIZE(auto_setup_tokens); i++)
9635 setSetupInfo(auto_setup_tokens, i,
9636 getHashEntry(setup_file_hash,
9637 auto_setup_tokens[i].text));
9640 static void decodeSetupFileHash_EditorCascade(SetupFileHash *setup_file_hash)
9644 if (!setup_file_hash)
9647 for (i = 0; i < ARRAY_SIZE(editor_cascade_setup_tokens); i++)
9648 setSetupInfo(editor_cascade_setup_tokens, i,
9649 getHashEntry(setup_file_hash,
9650 editor_cascade_setup_tokens[i].text));
9653 void LoadUserNames(void)
9655 int last_user_nr = user.nr;
9658 if (global.user_names != NULL)
9660 for (i = 0; i < MAX_PLAYER_NAMES; i++)
9661 checked_free(global.user_names[i]);
9663 checked_free(global.user_names);
9666 global.user_names = checked_calloc(MAX_PLAYER_NAMES * sizeof(char *));
9668 for (i = 0; i < MAX_PLAYER_NAMES; i++)
9672 SetupFileHash *setup_file_hash = loadSetupFileHash(getSetupFilename());
9674 if (setup_file_hash)
9676 char *player_name = getHashEntry(setup_file_hash, "player_name");
9678 global.user_names[i] = getFixedUserName(player_name);
9680 freeSetupFileHash(setup_file_hash);
9683 if (global.user_names[i] == NULL)
9684 global.user_names[i] = getStringCopy(getDefaultUserName(i));
9687 user.nr = last_user_nr;
9690 void LoadSetupFromFilename(char *filename)
9692 SetupFileHash *setup_file_hash = loadSetupFileHash(filename);
9694 if (setup_file_hash)
9696 decodeSetupFileHash(setup_file_hash);
9698 freeSetupFileHash(setup_file_hash);
9702 Debug("setup", "using default setup values");
9706 static void LoadSetup_SpecialPostProcessing(void)
9708 char *player_name_new;
9710 // needed to work around problems with fixed length strings
9711 player_name_new = getFixedUserName(setup.player_name);
9712 free(setup.player_name);
9713 setup.player_name = player_name_new;
9715 // "scroll_delay: on(3) / off(0)" was replaced by scroll delay value
9716 if (setup.scroll_delay == FALSE)
9718 setup.scroll_delay_value = MIN_SCROLL_DELAY;
9719 setup.scroll_delay = TRUE; // now always "on"
9722 // make sure that scroll delay value stays inside valid range
9723 setup.scroll_delay_value =
9724 MIN(MAX(MIN_SCROLL_DELAY, setup.scroll_delay_value), MAX_SCROLL_DELAY);
9727 void LoadSetup(void)
9731 // always start with reliable default values
9732 setSetupInfoToDefaults(&setup);
9734 // try to load setup values from default setup file
9735 filename = getDefaultSetupFilename();
9737 if (fileExists(filename))
9738 LoadSetupFromFilename(filename);
9740 // try to load setup values from user setup file
9741 filename = getSetupFilename();
9743 LoadSetupFromFilename(filename);
9745 LoadSetup_SpecialPostProcessing();
9748 void LoadSetup_AutoSetup(void)
9750 char *filename = getPath2(getSetupDir(), AUTOSETUP_FILENAME);
9751 SetupFileHash *setup_file_hash = NULL;
9753 // always start with reliable default values
9754 setSetupInfoToDefaults_AutoSetup(&setup);
9756 setup_file_hash = loadSetupFileHash(filename);
9758 if (setup_file_hash)
9760 decodeSetupFileHash_AutoSetup(setup_file_hash);
9762 freeSetupFileHash(setup_file_hash);
9768 void LoadSetup_EditorCascade(void)
9770 char *filename = getPath2(getSetupDir(), EDITORCASCADE_FILENAME);
9771 SetupFileHash *setup_file_hash = NULL;
9773 // always start with reliable default values
9774 setSetupInfoToDefaults_EditorCascade(&setup);
9776 setup_file_hash = loadSetupFileHash(filename);
9778 if (setup_file_hash)
9780 decodeSetupFileHash_EditorCascade(setup_file_hash);
9782 freeSetupFileHash(setup_file_hash);
9788 static void addGameControllerMappingToHash(SetupFileHash *mappings_hash,
9791 char mapping_guid[MAX_LINE_LEN];
9792 char *mapping_start, *mapping_end;
9794 // get GUID from game controller mapping line: copy complete line
9795 strncpy(mapping_guid, mapping_line, MAX_LINE_LEN - 1);
9796 mapping_guid[MAX_LINE_LEN - 1] = '\0';
9798 // get GUID from game controller mapping line: cut after GUID part
9799 mapping_start = strchr(mapping_guid, ',');
9800 if (mapping_start != NULL)
9801 *mapping_start = '\0';
9803 // cut newline from game controller mapping line
9804 mapping_end = strchr(mapping_line, '\n');
9805 if (mapping_end != NULL)
9806 *mapping_end = '\0';
9808 // add mapping entry to game controller mappings hash
9809 setHashEntry(mappings_hash, mapping_guid, mapping_line);
9812 static void LoadSetup_ReadGameControllerMappings(SetupFileHash *mappings_hash,
9817 if (!(file = fopen(filename, MODE_READ)))
9819 Warn("cannot read game controller mappings file '%s'", filename);
9826 char line[MAX_LINE_LEN];
9828 if (!fgets(line, MAX_LINE_LEN, file))
9831 addGameControllerMappingToHash(mappings_hash, line);
9837 void SaveSetup(void)
9839 char *filename = getSetupFilename();
9843 InitUserDataDirectory();
9845 if (!(file = fopen(filename, MODE_WRITE)))
9847 Warn("cannot write setup file '%s'", filename);
9852 fprintFileHeader(file, SETUP_FILENAME);
9854 for (i = 0; i < ARRAY_SIZE(global_setup_tokens); i++)
9856 // just to make things nicer :)
9857 if (global_setup_tokens[i].value == &setup.multiple_users ||
9858 global_setup_tokens[i].value == &setup.sound ||
9859 global_setup_tokens[i].value == &setup.graphics_set ||
9860 global_setup_tokens[i].value == &setup.volume_simple ||
9861 global_setup_tokens[i].value == &setup.network_mode ||
9862 global_setup_tokens[i].value == &setup.touch.control_type ||
9863 global_setup_tokens[i].value == &setup.touch.grid_xsize[0] ||
9864 global_setup_tokens[i].value == &setup.touch.grid_xsize[1])
9865 fprintf(file, "\n");
9867 fprintf(file, "%s\n", getSetupLine(global_setup_tokens, "", i));
9870 for (i = 0; i < 2; i++)
9872 int grid_xsize = setup.touch.grid_xsize[i];
9873 int grid_ysize = setup.touch.grid_ysize[i];
9876 fprintf(file, "\n");
9878 for (y = 0; y < grid_ysize; y++)
9880 char token_string[MAX_LINE_LEN];
9881 char value_string[MAX_LINE_LEN];
9883 sprintf(token_string, "touch.virtual_buttons.%d.%02d", i, y);
9885 for (x = 0; x < grid_xsize; x++)
9887 char c = setup.touch.grid_button[i][x][y];
9889 value_string[x] = (c == CHAR_GRID_BUTTON_NONE ? '.' : c);
9892 value_string[grid_xsize] = '\0';
9894 fprintf(file, "%s\n", getFormattedSetupEntry(token_string, value_string));
9898 fprintf(file, "\n");
9899 for (i = 0; i < ARRAY_SIZE(editor_setup_tokens); i++)
9900 fprintf(file, "%s\n", getSetupLine(editor_setup_tokens, "", i));
9902 fprintf(file, "\n");
9903 for (i = 0; i < ARRAY_SIZE(shortcut_setup_tokens); i++)
9904 fprintf(file, "%s\n", getSetupLine(shortcut_setup_tokens, "", i));
9906 for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
9910 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
9911 fprintf(file, "\n");
9913 setup_input = setup.input[pnr];
9914 for (i = 0; i < ARRAY_SIZE(player_setup_tokens); i++)
9915 fprintf(file, "%s\n", getSetupLine(player_setup_tokens, prefix, i));
9918 fprintf(file, "\n");
9919 for (i = 0; i < ARRAY_SIZE(system_setup_tokens); i++)
9920 fprintf(file, "%s\n", getSetupLine(system_setup_tokens, "", i));
9922 // (internal setup values not saved to user setup file)
9924 fprintf(file, "\n");
9925 for (i = 0; i < ARRAY_SIZE(debug_setup_tokens); i++)
9926 if (!strPrefix(debug_setup_tokens[i].text, "debug.xsn_") ||
9927 setup.debug.xsn_mode != AUTO)
9928 fprintf(file, "%s\n", getSetupLine(debug_setup_tokens, "", i));
9930 fprintf(file, "\n");
9931 for (i = 0; i < ARRAY_SIZE(options_setup_tokens); i++)
9932 fprintf(file, "%s\n", getSetupLine(options_setup_tokens, "", i));
9936 SetFilePermissions(filename, PERMS_PRIVATE);
9939 void SaveSetup_AutoSetup(void)
9941 char *filename = getPath2(getSetupDir(), AUTOSETUP_FILENAME);
9945 InitUserDataDirectory();
9947 if (!(file = fopen(filename, MODE_WRITE)))
9949 Warn("cannot write auto setup file '%s'", filename);
9956 fprintFileHeader(file, AUTOSETUP_FILENAME);
9958 for (i = 0; i < ARRAY_SIZE(auto_setup_tokens); i++)
9959 fprintf(file, "%s\n", getSetupLine(auto_setup_tokens, "", i));
9963 SetFilePermissions(filename, PERMS_PRIVATE);
9968 void SaveSetup_EditorCascade(void)
9970 char *filename = getPath2(getSetupDir(), EDITORCASCADE_FILENAME);
9974 InitUserDataDirectory();
9976 if (!(file = fopen(filename, MODE_WRITE)))
9978 Warn("cannot write editor cascade state file '%s'", filename);
9985 fprintFileHeader(file, EDITORCASCADE_FILENAME);
9987 for (i = 0; i < ARRAY_SIZE(editor_cascade_setup_tokens); i++)
9988 fprintf(file, "%s\n", getSetupLine(editor_cascade_setup_tokens, "", i));
9992 SetFilePermissions(filename, PERMS_PRIVATE);
9997 static void SaveSetup_WriteGameControllerMappings(SetupFileHash *mappings_hash,
10002 if (!(file = fopen(filename, MODE_WRITE)))
10004 Warn("cannot write game controller mappings file '%s'", filename);
10009 BEGIN_HASH_ITERATION(mappings_hash, itr)
10011 fprintf(file, "%s\n", HASH_ITERATION_VALUE(itr));
10013 END_HASH_ITERATION(mappings_hash, itr)
10018 void SaveSetup_AddGameControllerMapping(char *mapping)
10020 char *filename = getPath2(getSetupDir(), GAMECONTROLLER_BASENAME);
10021 SetupFileHash *mappings_hash = newSetupFileHash();
10023 InitUserDataDirectory();
10025 // load existing personal game controller mappings
10026 LoadSetup_ReadGameControllerMappings(mappings_hash, filename);
10028 // add new mapping to personal game controller mappings
10029 addGameControllerMappingToHash(mappings_hash, mapping);
10031 // save updated personal game controller mappings
10032 SaveSetup_WriteGameControllerMappings(mappings_hash, filename);
10034 freeSetupFileHash(mappings_hash);
10038 void LoadCustomElementDescriptions(void)
10040 char *filename = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
10041 SetupFileHash *setup_file_hash;
10044 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
10046 if (element_info[i].custom_description != NULL)
10048 free(element_info[i].custom_description);
10049 element_info[i].custom_description = NULL;
10053 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
10056 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
10058 char *token = getStringCat2(element_info[i].token_name, ".name");
10059 char *value = getHashEntry(setup_file_hash, token);
10062 element_info[i].custom_description = getStringCopy(value);
10067 freeSetupFileHash(setup_file_hash);
10070 static int getElementFromToken(char *token)
10072 char *value = getHashEntry(element_token_hash, token);
10075 return atoi(value);
10077 Warn("unknown element token '%s'", token);
10079 return EL_UNDEFINED;
10082 void FreeGlobalAnimEventInfo(void)
10084 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
10086 if (gaei->event_list == NULL)
10091 for (i = 0; i < gaei->num_event_lists; i++)
10093 checked_free(gaei->event_list[i]->event_value);
10094 checked_free(gaei->event_list[i]);
10097 checked_free(gaei->event_list);
10099 gaei->event_list = NULL;
10100 gaei->num_event_lists = 0;
10103 static int AddGlobalAnimEventList(void)
10105 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
10106 int list_pos = gaei->num_event_lists++;
10108 gaei->event_list = checked_realloc(gaei->event_list, gaei->num_event_lists *
10109 sizeof(struct GlobalAnimEventListInfo *));
10111 gaei->event_list[list_pos] =
10112 checked_calloc(sizeof(struct GlobalAnimEventListInfo));
10114 struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
10116 gaeli->event_value = NULL;
10117 gaeli->num_event_values = 0;
10122 static int AddGlobalAnimEventValue(int list_pos, int event_value)
10124 // do not add empty global animation events
10125 if (event_value == ANIM_EVENT_NONE)
10128 // if list position is undefined, create new list
10129 if (list_pos == ANIM_EVENT_UNDEFINED)
10130 list_pos = AddGlobalAnimEventList();
10132 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
10133 struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
10134 int value_pos = gaeli->num_event_values++;
10136 gaeli->event_value = checked_realloc(gaeli->event_value,
10137 gaeli->num_event_values * sizeof(int *));
10139 gaeli->event_value[value_pos] = event_value;
10144 int GetGlobalAnimEventValue(int list_pos, int value_pos)
10146 if (list_pos == ANIM_EVENT_UNDEFINED)
10147 return ANIM_EVENT_NONE;
10149 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
10150 struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
10152 return gaeli->event_value[value_pos];
10155 int GetGlobalAnimEventValueCount(int list_pos)
10157 if (list_pos == ANIM_EVENT_UNDEFINED)
10160 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
10161 struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
10163 return gaeli->num_event_values;
10166 // This function checks if a string <s> of the format "string1, string2, ..."
10167 // exactly contains a string <s_contained>.
10169 static boolean string_has_parameter(char *s, char *s_contained)
10173 if (s == NULL || s_contained == NULL)
10176 if (strlen(s_contained) > strlen(s))
10179 if (strncmp(s, s_contained, strlen(s_contained)) == 0)
10181 char next_char = s[strlen(s_contained)];
10183 // check if next character is delimiter or whitespace
10184 return (next_char == ',' || next_char == '\0' ||
10185 next_char == ' ' || next_char == '\t' ? TRUE : FALSE);
10188 // check if string contains another parameter string after a comma
10189 substring = strchr(s, ',');
10190 if (substring == NULL) // string does not contain a comma
10193 // advance string pointer to next character after the comma
10196 // skip potential whitespaces after the comma
10197 while (*substring == ' ' || *substring == '\t')
10200 return string_has_parameter(substring, s_contained);
10203 static int get_anim_parameter_value(char *s)
10205 int event_value[] =
10213 char *pattern_1[] =
10221 char *pattern_2 = ".part_";
10222 char *matching_char = NULL;
10224 int pattern_1_len = 0;
10225 int result = ANIM_EVENT_NONE;
10228 for (i = 0; i < ARRAY_SIZE(event_value); i++)
10230 matching_char = strstr(s_ptr, pattern_1[i]);
10231 pattern_1_len = strlen(pattern_1[i]);
10232 result = event_value[i];
10234 if (matching_char != NULL)
10238 if (matching_char == NULL)
10239 return ANIM_EVENT_NONE;
10241 s_ptr = matching_char + pattern_1_len;
10243 // check for main animation number ("anim_X" or "anim_XX")
10244 if (*s_ptr >= '0' && *s_ptr <= '9')
10246 int gic_anim_nr = (*s_ptr++ - '0');
10248 if (*s_ptr >= '0' && *s_ptr <= '9')
10249 gic_anim_nr = 10 * gic_anim_nr + (*s_ptr++ - '0');
10251 if (gic_anim_nr < 1 || gic_anim_nr > MAX_GLOBAL_ANIMS)
10252 return ANIM_EVENT_NONE;
10254 result |= gic_anim_nr << ANIM_EVENT_ANIM_BIT;
10258 // invalid main animation number specified
10260 return ANIM_EVENT_NONE;
10263 // check for animation part number ("part_X" or "part_XX") (optional)
10264 if (strPrefix(s_ptr, pattern_2))
10266 s_ptr += strlen(pattern_2);
10268 if (*s_ptr >= '0' && *s_ptr <= '9')
10270 int gic_part_nr = (*s_ptr++ - '0');
10272 if (*s_ptr >= '0' && *s_ptr <= '9')
10273 gic_part_nr = 10 * gic_part_nr + (*s_ptr++ - '0');
10275 if (gic_part_nr < 1 || gic_part_nr > MAX_GLOBAL_ANIM_PARTS)
10276 return ANIM_EVENT_NONE;
10278 result |= gic_part_nr << ANIM_EVENT_PART_BIT;
10282 // invalid animation part number specified
10284 return ANIM_EVENT_NONE;
10288 // discard result if next character is neither delimiter nor whitespace
10289 if (!(*s_ptr == ',' || *s_ptr == '\0' ||
10290 *s_ptr == ' ' || *s_ptr == '\t'))
10291 return ANIM_EVENT_NONE;
10296 static int get_anim_parameter_values(char *s)
10298 int list_pos = ANIM_EVENT_UNDEFINED;
10299 int event_value = ANIM_EVENT_DEFAULT;
10301 if (string_has_parameter(s, "any"))
10302 event_value |= ANIM_EVENT_ANY;
10304 if (string_has_parameter(s, "click:self") ||
10305 string_has_parameter(s, "click") ||
10306 string_has_parameter(s, "self"))
10307 event_value |= ANIM_EVENT_SELF;
10309 if (string_has_parameter(s, "unclick:any"))
10310 event_value |= ANIM_EVENT_UNCLICK_ANY;
10312 // if animation event found, add it to global animation event list
10313 if (event_value != ANIM_EVENT_NONE)
10314 list_pos = AddGlobalAnimEventValue(list_pos, event_value);
10318 // add optional "click:anim_X" or "click:anim_X.part_X" parameter
10319 event_value = get_anim_parameter_value(s);
10321 // if animation event found, add it to global animation event list
10322 if (event_value != ANIM_EVENT_NONE)
10323 list_pos = AddGlobalAnimEventValue(list_pos, event_value);
10325 // continue with next part of the string, starting with next comma
10326 s = strchr(s + 1, ',');
10332 static int get_anim_action_parameter_value(char *token)
10334 // check most common default case first to massively speed things up
10335 if (strEqual(token, ARG_UNDEFINED))
10336 return ANIM_EVENT_ACTION_NONE;
10338 int result = getImageIDFromToken(token);
10342 char *gfx_token = getStringCat2("gfx.", token);
10344 result = getImageIDFromToken(gfx_token);
10346 checked_free(gfx_token);
10351 Key key = getKeyFromX11KeyName(token);
10353 if (key != KSYM_UNDEFINED)
10354 result = -(int)key;
10358 result = ANIM_EVENT_ACTION_NONE;
10363 int get_parameter_value(char *value_raw, char *suffix, int type)
10365 char *value = getStringToLower(value_raw);
10366 int result = 0; // probably a save default value
10368 if (strEqual(suffix, ".direction"))
10370 result = (strEqual(value, "left") ? MV_LEFT :
10371 strEqual(value, "right") ? MV_RIGHT :
10372 strEqual(value, "up") ? MV_UP :
10373 strEqual(value, "down") ? MV_DOWN : MV_NONE);
10375 else if (strEqual(suffix, ".position"))
10377 result = (strEqual(value, "left") ? POS_LEFT :
10378 strEqual(value, "right") ? POS_RIGHT :
10379 strEqual(value, "top") ? POS_TOP :
10380 strEqual(value, "upper") ? POS_UPPER :
10381 strEqual(value, "middle") ? POS_MIDDLE :
10382 strEqual(value, "lower") ? POS_LOWER :
10383 strEqual(value, "bottom") ? POS_BOTTOM :
10384 strEqual(value, "any") ? POS_ANY :
10385 strEqual(value, "last") ? POS_LAST : POS_UNDEFINED);
10387 else if (strEqual(suffix, ".align"))
10389 result = (strEqual(value, "left") ? ALIGN_LEFT :
10390 strEqual(value, "right") ? ALIGN_RIGHT :
10391 strEqual(value, "center") ? ALIGN_CENTER :
10392 strEqual(value, "middle") ? ALIGN_CENTER : ALIGN_DEFAULT);
10394 else if (strEqual(suffix, ".valign"))
10396 result = (strEqual(value, "top") ? VALIGN_TOP :
10397 strEqual(value, "bottom") ? VALIGN_BOTTOM :
10398 strEqual(value, "middle") ? VALIGN_MIDDLE :
10399 strEqual(value, "center") ? VALIGN_MIDDLE : VALIGN_DEFAULT);
10401 else if (strEqual(suffix, ".anim_mode"))
10403 result = (string_has_parameter(value, "none") ? ANIM_NONE :
10404 string_has_parameter(value, "loop") ? ANIM_LOOP :
10405 string_has_parameter(value, "linear") ? ANIM_LINEAR :
10406 string_has_parameter(value, "pingpong") ? ANIM_PINGPONG :
10407 string_has_parameter(value, "pingpong2") ? ANIM_PINGPONG2 :
10408 string_has_parameter(value, "random") ? ANIM_RANDOM :
10409 string_has_parameter(value, "ce_value") ? ANIM_CE_VALUE :
10410 string_has_parameter(value, "ce_score") ? ANIM_CE_SCORE :
10411 string_has_parameter(value, "ce_delay") ? ANIM_CE_DELAY :
10412 string_has_parameter(value, "horizontal") ? ANIM_HORIZONTAL :
10413 string_has_parameter(value, "vertical") ? ANIM_VERTICAL :
10414 string_has_parameter(value, "centered") ? ANIM_CENTERED :
10415 string_has_parameter(value, "all") ? ANIM_ALL :
10418 if (string_has_parameter(value, "once"))
10419 result |= ANIM_ONCE;
10421 if (string_has_parameter(value, "reverse"))
10422 result |= ANIM_REVERSE;
10424 if (string_has_parameter(value, "opaque_player"))
10425 result |= ANIM_OPAQUE_PLAYER;
10427 if (string_has_parameter(value, "static_panel"))
10428 result |= ANIM_STATIC_PANEL;
10430 else if (strEqual(suffix, ".init_event") ||
10431 strEqual(suffix, ".anim_event"))
10433 result = get_anim_parameter_values(value);
10435 else if (strEqual(suffix, ".init_delay_action") ||
10436 strEqual(suffix, ".anim_delay_action") ||
10437 strEqual(suffix, ".post_delay_action") ||
10438 strEqual(suffix, ".init_event_action") ||
10439 strEqual(suffix, ".anim_event_action"))
10441 result = get_anim_action_parameter_value(value_raw);
10443 else if (strEqual(suffix, ".class"))
10445 result = (strEqual(value, ARG_UNDEFINED) ? ARG_UNDEFINED_VALUE :
10446 get_hash_from_key(value));
10448 else if (strEqual(suffix, ".style"))
10450 result = STYLE_DEFAULT;
10452 if (string_has_parameter(value, "accurate_borders"))
10453 result |= STYLE_ACCURATE_BORDERS;
10455 if (string_has_parameter(value, "inner_corners"))
10456 result |= STYLE_INNER_CORNERS;
10458 if (string_has_parameter(value, "reverse"))
10459 result |= STYLE_REVERSE;
10461 if (string_has_parameter(value, "leftmost_position"))
10462 result |= STYLE_LEFTMOST_POSITION;
10464 if (string_has_parameter(value, "block_clicks"))
10465 result |= STYLE_BLOCK;
10467 if (string_has_parameter(value, "passthrough_clicks"))
10468 result |= STYLE_PASSTHROUGH;
10470 if (string_has_parameter(value, "multiple_actions"))
10471 result |= STYLE_MULTIPLE_ACTIONS;
10473 else if (strEqual(suffix, ".fade_mode"))
10475 result = (string_has_parameter(value, "none") ? FADE_MODE_NONE :
10476 string_has_parameter(value, "fade") ? FADE_MODE_FADE :
10477 string_has_parameter(value, "crossfade") ? FADE_MODE_CROSSFADE :
10478 string_has_parameter(value, "melt") ? FADE_MODE_MELT :
10479 string_has_parameter(value, "curtain") ? FADE_MODE_CURTAIN :
10480 FADE_MODE_DEFAULT);
10482 else if (strEqual(suffix, ".auto_delay_unit"))
10484 result = (string_has_parameter(value, "ms") ? AUTO_DELAY_UNIT_MS :
10485 string_has_parameter(value, "frames") ? AUTO_DELAY_UNIT_FRAMES :
10486 AUTO_DELAY_UNIT_DEFAULT);
10488 else if (strPrefix(suffix, ".font")) // (may also be ".font_xyz")
10490 result = gfx.get_font_from_token_function(value);
10492 else // generic parameter of type integer or boolean
10494 result = (strEqual(value, ARG_UNDEFINED) ? ARG_UNDEFINED_VALUE :
10495 type == TYPE_INTEGER ? get_integer_from_string(value) :
10496 type == TYPE_BOOLEAN ? get_boolean_from_string(value) :
10497 ARG_UNDEFINED_VALUE);
10505 static int get_token_parameter_value(char *token, char *value_raw)
10509 if (token == NULL || value_raw == NULL)
10510 return ARG_UNDEFINED_VALUE;
10512 suffix = strrchr(token, '.');
10513 if (suffix == NULL)
10516 if (strEqual(suffix, ".element"))
10517 return getElementFromToken(value_raw);
10519 // !!! USE CORRECT VALUE TYPE (currently works also for TYPE_BOOLEAN) !!!
10520 return get_parameter_value(value_raw, suffix, TYPE_INTEGER);
10523 void InitMenuDesignSettings_Static(void)
10527 // always start with reliable default values from static default config
10528 for (i = 0; image_config_vars[i].token != NULL; i++)
10530 char *value = getHashEntry(image_config_hash, image_config_vars[i].token);
10533 *image_config_vars[i].value =
10534 get_token_parameter_value(image_config_vars[i].token, value);
10538 static void InitMenuDesignSettings_SpecialPreProcessing(void)
10542 // the following initializes hierarchical values from static configuration
10544 // special case: initialize "ARG_DEFAULT" values in static default config
10545 // (e.g., initialize "[titlemessage].fade_mode" from "[title].fade_mode")
10546 titlescreen_initial_first_default.fade_mode =
10547 title_initial_first_default.fade_mode;
10548 titlescreen_initial_first_default.fade_delay =
10549 title_initial_first_default.fade_delay;
10550 titlescreen_initial_first_default.post_delay =
10551 title_initial_first_default.post_delay;
10552 titlescreen_initial_first_default.auto_delay =
10553 title_initial_first_default.auto_delay;
10554 titlescreen_initial_first_default.auto_delay_unit =
10555 title_initial_first_default.auto_delay_unit;
10556 titlescreen_first_default.fade_mode = title_first_default.fade_mode;
10557 titlescreen_first_default.fade_delay = title_first_default.fade_delay;
10558 titlescreen_first_default.post_delay = title_first_default.post_delay;
10559 titlescreen_first_default.auto_delay = title_first_default.auto_delay;
10560 titlescreen_first_default.auto_delay_unit =
10561 title_first_default.auto_delay_unit;
10562 titlemessage_initial_first_default.fade_mode =
10563 title_initial_first_default.fade_mode;
10564 titlemessage_initial_first_default.fade_delay =
10565 title_initial_first_default.fade_delay;
10566 titlemessage_initial_first_default.post_delay =
10567 title_initial_first_default.post_delay;
10568 titlemessage_initial_first_default.auto_delay =
10569 title_initial_first_default.auto_delay;
10570 titlemessage_initial_first_default.auto_delay_unit =
10571 title_initial_first_default.auto_delay_unit;
10572 titlemessage_first_default.fade_mode = title_first_default.fade_mode;
10573 titlemessage_first_default.fade_delay = title_first_default.fade_delay;
10574 titlemessage_first_default.post_delay = title_first_default.post_delay;
10575 titlemessage_first_default.auto_delay = title_first_default.auto_delay;
10576 titlemessage_first_default.auto_delay_unit =
10577 title_first_default.auto_delay_unit;
10579 titlescreen_initial_default.fade_mode = title_initial_default.fade_mode;
10580 titlescreen_initial_default.fade_delay = title_initial_default.fade_delay;
10581 titlescreen_initial_default.post_delay = title_initial_default.post_delay;
10582 titlescreen_initial_default.auto_delay = title_initial_default.auto_delay;
10583 titlescreen_initial_default.auto_delay_unit =
10584 title_initial_default.auto_delay_unit;
10585 titlescreen_default.fade_mode = title_default.fade_mode;
10586 titlescreen_default.fade_delay = title_default.fade_delay;
10587 titlescreen_default.post_delay = title_default.post_delay;
10588 titlescreen_default.auto_delay = title_default.auto_delay;
10589 titlescreen_default.auto_delay_unit = title_default.auto_delay_unit;
10590 titlemessage_initial_default.fade_mode = title_initial_default.fade_mode;
10591 titlemessage_initial_default.fade_delay = title_initial_default.fade_delay;
10592 titlemessage_initial_default.post_delay = title_initial_default.post_delay;
10593 titlemessage_initial_default.auto_delay_unit =
10594 title_initial_default.auto_delay_unit;
10595 titlemessage_default.fade_mode = title_default.fade_mode;
10596 titlemessage_default.fade_delay = title_default.fade_delay;
10597 titlemessage_default.post_delay = title_default.post_delay;
10598 titlemessage_default.auto_delay = title_default.auto_delay;
10599 titlemessage_default.auto_delay_unit = title_default.auto_delay_unit;
10601 // special case: initialize "ARG_DEFAULT" values in static default config
10602 // (e.g., init "titlemessage_1.fade_mode" from "[titlemessage].fade_mode")
10603 for (i = 0; i < MAX_NUM_TITLE_MESSAGES; i++)
10605 titlescreen_initial_first[i] = titlescreen_initial_first_default;
10606 titlescreen_first[i] = titlescreen_first_default;
10607 titlemessage_initial_first[i] = titlemessage_initial_first_default;
10608 titlemessage_first[i] = titlemessage_first_default;
10610 titlescreen_initial[i] = titlescreen_initial_default;
10611 titlescreen[i] = titlescreen_default;
10612 titlemessage_initial[i] = titlemessage_initial_default;
10613 titlemessage[i] = titlemessage_default;
10616 // special case: initialize "ARG_DEFAULT" values in static default config
10617 // (eg, init "menu.enter_screen.SCORES.xyz" from "menu.enter_screen.xyz")
10618 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
10620 if (i == GFX_SPECIAL_ARG_TITLE) // title values already initialized
10623 menu.enter_screen[i] = menu.enter_screen[GFX_SPECIAL_ARG_DEFAULT];
10624 menu.leave_screen[i] = menu.leave_screen[GFX_SPECIAL_ARG_DEFAULT];
10625 menu.next_screen[i] = menu.next_screen[GFX_SPECIAL_ARG_DEFAULT];
10628 // special case: initialize "ARG_DEFAULT" values in static default config
10629 // (eg, init "viewport.door_1.MAIN.xyz" from "viewport.door_1.xyz")
10630 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
10632 viewport.window[i] = viewport.window[GFX_SPECIAL_ARG_DEFAULT];
10633 viewport.playfield[i] = viewport.playfield[GFX_SPECIAL_ARG_DEFAULT];
10634 viewport.door_1[i] = viewport.door_1[GFX_SPECIAL_ARG_DEFAULT];
10636 if (i == GFX_SPECIAL_ARG_EDITOR) // editor values already initialized
10639 viewport.door_2[i] = viewport.door_2[GFX_SPECIAL_ARG_DEFAULT];
10643 static void InitMenuDesignSettings_SpecialPostProcessing(void)
10647 struct XY *dst, *src;
10649 game_buttons_xy[] =
10651 { &game.button.save, &game.button.stop },
10652 { &game.button.pause2, &game.button.pause },
10653 { &game.button.load, &game.button.play },
10654 { &game.button.undo, &game.button.stop },
10655 { &game.button.redo, &game.button.play },
10661 // special case: initialize later added SETUP list size from LEVELS value
10662 if (menu.list_size[GAME_MODE_SETUP] == -1)
10663 menu.list_size[GAME_MODE_SETUP] = menu.list_size[GAME_MODE_LEVELS];
10665 // set default position for snapshot buttons to stop/pause/play buttons
10666 for (i = 0; game_buttons_xy[i].dst != NULL; i++)
10667 if ((*game_buttons_xy[i].dst).x == -1 &&
10668 (*game_buttons_xy[i].dst).y == -1)
10669 *game_buttons_xy[i].dst = *game_buttons_xy[i].src;
10671 // --------------------------------------------------------------------------
10672 // dynamic viewports (including playfield margins, borders and alignments)
10673 // --------------------------------------------------------------------------
10675 // dynamic viewports currently only supported for landscape mode
10676 int display_width = MAX(video.display_width, video.display_height);
10677 int display_height = MIN(video.display_width, video.display_height);
10679 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
10681 struct RectWithBorder *vp_window = &viewport.window[i];
10682 struct RectWithBorder *vp_playfield = &viewport.playfield[i];
10683 struct RectWithBorder *vp_door_1 = &viewport.door_1[i];
10684 struct RectWithBorder *vp_door_2 = &viewport.door_2[i];
10685 boolean dynamic_window_width = (vp_window->min_width != -1);
10686 boolean dynamic_window_height = (vp_window->min_height != -1);
10687 boolean dynamic_playfield_width = (vp_playfield->min_width != -1);
10688 boolean dynamic_playfield_height = (vp_playfield->min_height != -1);
10690 // adjust window size if min/max width/height is specified
10692 if (vp_window->min_width != -1)
10694 int window_width = display_width;
10696 // when using static window height, use aspect ratio of display
10697 if (vp_window->min_height == -1)
10698 window_width = vp_window->height * display_width / display_height;
10700 vp_window->width = MAX(vp_window->min_width, window_width);
10703 if (vp_window->min_height != -1)
10705 int window_height = display_height;
10707 // when using static window width, use aspect ratio of display
10708 if (vp_window->min_width == -1)
10709 window_height = vp_window->width * display_height / display_width;
10711 vp_window->height = MAX(vp_window->min_height, window_height);
10714 if (vp_window->max_width != -1)
10715 vp_window->width = MIN(vp_window->width, vp_window->max_width);
10717 if (vp_window->max_height != -1)
10718 vp_window->height = MIN(vp_window->height, vp_window->max_height);
10720 int playfield_width = vp_window->width;
10721 int playfield_height = vp_window->height;
10723 // adjust playfield size and position according to specified margins
10725 playfield_width -= vp_playfield->margin_left;
10726 playfield_width -= vp_playfield->margin_right;
10728 playfield_height -= vp_playfield->margin_top;
10729 playfield_height -= vp_playfield->margin_bottom;
10731 // adjust playfield size if min/max width/height is specified
10733 if (vp_playfield->min_width != -1)
10734 vp_playfield->width = MAX(vp_playfield->min_width, playfield_width);
10736 if (vp_playfield->min_height != -1)
10737 vp_playfield->height = MAX(vp_playfield->min_height, playfield_height);
10739 if (vp_playfield->max_width != -1)
10740 vp_playfield->width = MIN(vp_playfield->width, vp_playfield->max_width);
10742 if (vp_playfield->max_height != -1)
10743 vp_playfield->height = MIN(vp_playfield->height,vp_playfield->max_height);
10745 // adjust playfield position according to specified alignment
10747 if (vp_playfield->align == ALIGN_LEFT || vp_playfield->x > 0)
10748 vp_playfield->x = ALIGNED_VP_XPOS(vp_playfield);
10749 else if (vp_playfield->align == ALIGN_CENTER)
10750 vp_playfield->x = playfield_width / 2 - vp_playfield->width / 2;
10751 else if (vp_playfield->align == ALIGN_RIGHT)
10752 vp_playfield->x += playfield_width - vp_playfield->width;
10754 if (vp_playfield->valign == VALIGN_TOP || vp_playfield->y > 0)
10755 vp_playfield->y = ALIGNED_VP_YPOS(vp_playfield);
10756 else if (vp_playfield->valign == VALIGN_MIDDLE)
10757 vp_playfield->y = playfield_height / 2 - vp_playfield->height / 2;
10758 else if (vp_playfield->valign == VALIGN_BOTTOM)
10759 vp_playfield->y += playfield_height - vp_playfield->height;
10761 vp_playfield->x += vp_playfield->margin_left;
10762 vp_playfield->y += vp_playfield->margin_top;
10764 // adjust individual playfield borders if only default border is specified
10766 if (vp_playfield->border_left == -1)
10767 vp_playfield->border_left = vp_playfield->border_size;
10768 if (vp_playfield->border_right == -1)
10769 vp_playfield->border_right = vp_playfield->border_size;
10770 if (vp_playfield->border_top == -1)
10771 vp_playfield->border_top = vp_playfield->border_size;
10772 if (vp_playfield->border_bottom == -1)
10773 vp_playfield->border_bottom = vp_playfield->border_size;
10775 // set dynamic playfield borders if borders are specified as undefined
10776 // (but only if window size was dynamic and playfield size was static)
10778 if (dynamic_window_width && !dynamic_playfield_width)
10780 if (vp_playfield->border_left == -1)
10782 vp_playfield->border_left = (vp_playfield->x -
10783 vp_playfield->margin_left);
10784 vp_playfield->x -= vp_playfield->border_left;
10785 vp_playfield->width += vp_playfield->border_left;
10788 if (vp_playfield->border_right == -1)
10790 vp_playfield->border_right = (vp_window->width -
10792 vp_playfield->width -
10793 vp_playfield->margin_right);
10794 vp_playfield->width += vp_playfield->border_right;
10798 if (dynamic_window_height && !dynamic_playfield_height)
10800 if (vp_playfield->border_top == -1)
10802 vp_playfield->border_top = (vp_playfield->y -
10803 vp_playfield->margin_top);
10804 vp_playfield->y -= vp_playfield->border_top;
10805 vp_playfield->height += vp_playfield->border_top;
10808 if (vp_playfield->border_bottom == -1)
10810 vp_playfield->border_bottom = (vp_window->height -
10812 vp_playfield->height -
10813 vp_playfield->margin_bottom);
10814 vp_playfield->height += vp_playfield->border_bottom;
10818 // adjust playfield size to be a multiple of a defined alignment tile size
10820 int align_size = vp_playfield->align_size;
10821 int playfield_xtiles = vp_playfield->width / align_size;
10822 int playfield_ytiles = vp_playfield->height / align_size;
10823 int playfield_width_corrected = playfield_xtiles * align_size;
10824 int playfield_height_corrected = playfield_ytiles * align_size;
10825 boolean is_playfield_mode = (i == GFX_SPECIAL_ARG_PLAYING ||
10826 i == GFX_SPECIAL_ARG_EDITOR);
10828 if (is_playfield_mode &&
10829 dynamic_playfield_width &&
10830 vp_playfield->width != playfield_width_corrected)
10832 int playfield_xdiff = vp_playfield->width - playfield_width_corrected;
10834 vp_playfield->width = playfield_width_corrected;
10836 if (vp_playfield->align == ALIGN_LEFT)
10838 vp_playfield->border_left += playfield_xdiff;
10840 else if (vp_playfield->align == ALIGN_RIGHT)
10842 vp_playfield->border_right += playfield_xdiff;
10844 else if (vp_playfield->align == ALIGN_CENTER)
10846 int border_left_diff = playfield_xdiff / 2;
10847 int border_right_diff = playfield_xdiff - border_left_diff;
10849 vp_playfield->border_left += border_left_diff;
10850 vp_playfield->border_right += border_right_diff;
10854 if (is_playfield_mode &&
10855 dynamic_playfield_height &&
10856 vp_playfield->height != playfield_height_corrected)
10858 int playfield_ydiff = vp_playfield->height - playfield_height_corrected;
10860 vp_playfield->height = playfield_height_corrected;
10862 if (vp_playfield->valign == VALIGN_TOP)
10864 vp_playfield->border_top += playfield_ydiff;
10866 else if (vp_playfield->align == VALIGN_BOTTOM)
10868 vp_playfield->border_right += playfield_ydiff;
10870 else if (vp_playfield->align == VALIGN_MIDDLE)
10872 int border_top_diff = playfield_ydiff / 2;
10873 int border_bottom_diff = playfield_ydiff - border_top_diff;
10875 vp_playfield->border_top += border_top_diff;
10876 vp_playfield->border_bottom += border_bottom_diff;
10880 // adjust door positions according to specified alignment
10882 for (j = 0; j < 2; j++)
10884 struct RectWithBorder *vp_door = (j == 0 ? vp_door_1 : vp_door_2);
10886 if (vp_door->align == ALIGN_LEFT || vp_door->x > 0)
10887 vp_door->x = ALIGNED_VP_XPOS(vp_door);
10888 else if (vp_door->align == ALIGN_CENTER)
10889 vp_door->x = vp_window->width / 2 - vp_door->width / 2;
10890 else if (vp_door->align == ALIGN_RIGHT)
10891 vp_door->x += vp_window->width - vp_door->width;
10893 if (vp_door->valign == VALIGN_TOP || vp_door->y > 0)
10894 vp_door->y = ALIGNED_VP_YPOS(vp_door);
10895 else if (vp_door->valign == VALIGN_MIDDLE)
10896 vp_door->y = vp_window->height / 2 - vp_door->height / 2;
10897 else if (vp_door->valign == VALIGN_BOTTOM)
10898 vp_door->y += vp_window->height - vp_door->height;
10903 static void InitMenuDesignSettings_SpecialPostProcessing_AfterGraphics(void)
10907 struct XYTileSize *dst, *src;
10910 editor_buttons_xy[] =
10913 &editor.button.element_left, &editor.palette.element_left,
10914 IMG_GFX_EDITOR_BUTTON_ELEMENT_LEFT
10917 &editor.button.element_middle, &editor.palette.element_middle,
10918 IMG_GFX_EDITOR_BUTTON_ELEMENT_MIDDLE
10921 &editor.button.element_right, &editor.palette.element_right,
10922 IMG_GFX_EDITOR_BUTTON_ELEMENT_RIGHT
10929 // set default position for element buttons to element graphics
10930 for (i = 0; editor_buttons_xy[i].dst != NULL; i++)
10932 if ((*editor_buttons_xy[i].dst).x == -1 &&
10933 (*editor_buttons_xy[i].dst).y == -1)
10935 struct GraphicInfo *gd = &graphic_info[editor_buttons_xy[i].graphic];
10937 gd->width = gd->height = editor_buttons_xy[i].src->tile_size;
10939 *editor_buttons_xy[i].dst = *editor_buttons_xy[i].src;
10943 // adjust editor palette rows and columns if specified to be dynamic
10945 if (editor.palette.cols == -1)
10947 int vp_width = viewport.playfield[GFX_SPECIAL_ARG_EDITOR].width;
10948 int bt_width = graphic_info[IMG_EDITOR_PALETTE_BUTTON].width;
10949 int sc_width = graphic_info[IMG_EDITOR_PALETTE_SCROLLBAR].width;
10951 editor.palette.cols = (vp_width - sc_width) / bt_width;
10953 if (editor.palette.x == -1)
10955 int palette_width = editor.palette.cols * bt_width + sc_width;
10957 editor.palette.x = (vp_width - palette_width) / 2;
10961 if (editor.palette.rows == -1)
10963 int vp_height = viewport.playfield[GFX_SPECIAL_ARG_EDITOR].height;
10964 int bt_height = graphic_info[IMG_EDITOR_PALETTE_BUTTON].height;
10965 int tx_height = getFontHeight(FONT_TEXT_2);
10967 editor.palette.rows = (vp_height - tx_height) / bt_height;
10969 if (editor.palette.y == -1)
10971 int palette_height = editor.palette.rows * bt_height + tx_height;
10973 editor.palette.y = (vp_height - palette_height) / 2;
10978 static void LoadMenuDesignSettingsFromFilename(char *filename)
10980 static struct TitleFadingInfo tfi;
10981 static struct TitleMessageInfo tmi;
10982 static struct TokenInfo title_tokens[] =
10984 { TYPE_INTEGER, &tfi.fade_mode, ".fade_mode" },
10985 { TYPE_INTEGER, &tfi.fade_delay, ".fade_delay" },
10986 { TYPE_INTEGER, &tfi.post_delay, ".post_delay" },
10987 { TYPE_INTEGER, &tfi.auto_delay, ".auto_delay" },
10988 { TYPE_INTEGER, &tfi.auto_delay_unit, ".auto_delay_unit" },
10992 static struct TokenInfo titlemessage_tokens[] =
10994 { TYPE_INTEGER, &tmi.x, ".x" },
10995 { TYPE_INTEGER, &tmi.y, ".y" },
10996 { TYPE_INTEGER, &tmi.width, ".width" },
10997 { TYPE_INTEGER, &tmi.height, ".height" },
10998 { TYPE_INTEGER, &tmi.chars, ".chars" },
10999 { TYPE_INTEGER, &tmi.lines, ".lines" },
11000 { TYPE_INTEGER, &tmi.align, ".align" },
11001 { TYPE_INTEGER, &tmi.valign, ".valign" },
11002 { TYPE_INTEGER, &tmi.font, ".font" },
11003 { TYPE_BOOLEAN, &tmi.autowrap, ".autowrap" },
11004 { TYPE_BOOLEAN, &tmi.centered, ".centered" },
11005 { TYPE_BOOLEAN, &tmi.parse_comments, ".parse_comments" },
11006 { TYPE_INTEGER, &tmi.sort_priority, ".sort_priority" },
11007 { TYPE_INTEGER, &tmi.fade_mode, ".fade_mode" },
11008 { TYPE_INTEGER, &tmi.fade_delay, ".fade_delay" },
11009 { TYPE_INTEGER, &tmi.post_delay, ".post_delay" },
11010 { TYPE_INTEGER, &tmi.auto_delay, ".auto_delay" },
11011 { TYPE_INTEGER, &tmi.auto_delay_unit, ".auto_delay_unit" },
11017 struct TitleFadingInfo *info;
11022 // initialize first titles from "enter screen" definitions, if defined
11023 { &title_initial_first_default, "menu.enter_screen.TITLE" },
11024 { &title_first_default, "menu.enter_screen.TITLE" },
11026 // initialize title screens from "next screen" definitions, if defined
11027 { &title_initial_default, "menu.next_screen.TITLE" },
11028 { &title_default, "menu.next_screen.TITLE" },
11034 struct TitleMessageInfo *array;
11037 titlemessage_arrays[] =
11039 // initialize first titles from "enter screen" definitions, if defined
11040 { titlescreen_initial_first, "menu.enter_screen.TITLE" },
11041 { titlescreen_first, "menu.enter_screen.TITLE" },
11042 { titlemessage_initial_first, "menu.enter_screen.TITLE" },
11043 { titlemessage_first, "menu.enter_screen.TITLE" },
11045 // initialize titles from "next screen" definitions, if defined
11046 { titlescreen_initial, "menu.next_screen.TITLE" },
11047 { titlescreen, "menu.next_screen.TITLE" },
11048 { titlemessage_initial, "menu.next_screen.TITLE" },
11049 { titlemessage, "menu.next_screen.TITLE" },
11051 // overwrite titles with title definitions, if defined
11052 { titlescreen_initial_first, "[title_initial]" },
11053 { titlescreen_first, "[title]" },
11054 { titlemessage_initial_first, "[title_initial]" },
11055 { titlemessage_first, "[title]" },
11057 { titlescreen_initial, "[title_initial]" },
11058 { titlescreen, "[title]" },
11059 { titlemessage_initial, "[title_initial]" },
11060 { titlemessage, "[title]" },
11062 // overwrite titles with title screen/message definitions, if defined
11063 { titlescreen_initial_first, "[titlescreen_initial]" },
11064 { titlescreen_first, "[titlescreen]" },
11065 { titlemessage_initial_first, "[titlemessage_initial]" },
11066 { titlemessage_first, "[titlemessage]" },
11068 { titlescreen_initial, "[titlescreen_initial]" },
11069 { titlescreen, "[titlescreen]" },
11070 { titlemessage_initial, "[titlemessage_initial]" },
11071 { titlemessage, "[titlemessage]" },
11075 SetupFileHash *setup_file_hash;
11078 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
11081 // the following initializes hierarchical values from dynamic configuration
11083 // special case: initialize with default values that may be overwritten
11084 // (e.g., init "menu.draw_xoffset.INFO" from "menu.draw_xoffset")
11085 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
11087 struct TokenIntPtrInfo menu_config[] =
11089 { "menu.draw_xoffset", &menu.draw_xoffset[i] },
11090 { "menu.draw_yoffset", &menu.draw_yoffset[i] },
11091 { "menu.list_size", &menu.list_size[i] }
11094 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
11096 char *token = menu_config[j].token;
11097 char *value = getHashEntry(setup_file_hash, token);
11100 *menu_config[j].value = get_integer_from_string(value);
11104 // special case: initialize with default values that may be overwritten
11105 // (eg, init "menu.draw_xoffset.INFO[XXX]" from "menu.draw_xoffset.INFO")
11106 for (i = 0; i < NUM_SPECIAL_GFX_INFO_ARGS; i++)
11108 struct TokenIntPtrInfo menu_config[] =
11110 { "menu.draw_xoffset.INFO", &menu.draw_xoffset_info[i] },
11111 { "menu.draw_yoffset.INFO", &menu.draw_yoffset_info[i] },
11112 { "menu.list_size.INFO", &menu.list_size_info[i] }
11115 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
11117 char *token = menu_config[j].token;
11118 char *value = getHashEntry(setup_file_hash, token);
11121 *menu_config[j].value = get_integer_from_string(value);
11125 // special case: initialize with default values that may be overwritten
11126 // (eg, init "menu.draw_xoffset.SETUP[XXX]" from "menu.draw_xoffset.SETUP")
11127 for (i = 0; i < NUM_SPECIAL_GFX_SETUP_ARGS; i++)
11129 struct TokenIntPtrInfo menu_config[] =
11131 { "menu.draw_xoffset.SETUP", &menu.draw_xoffset_setup[i] },
11132 { "menu.draw_yoffset.SETUP", &menu.draw_yoffset_setup[i] }
11135 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
11137 char *token = menu_config[j].token;
11138 char *value = getHashEntry(setup_file_hash, token);
11141 *menu_config[j].value = get_integer_from_string(value);
11145 // special case: initialize with default values that may be overwritten
11146 // (eg, init "menu.line_spacing.INFO[XXX]" from "menu.line_spacing.INFO")
11147 for (i = 0; i < NUM_SPECIAL_GFX_INFO_ARGS; i++)
11149 struct TokenIntPtrInfo menu_config[] =
11151 { "menu.left_spacing.INFO", &menu.left_spacing_info[i] },
11152 { "menu.right_spacing.INFO", &menu.right_spacing_info[i] },
11153 { "menu.top_spacing.INFO", &menu.top_spacing_info[i] },
11154 { "menu.bottom_spacing.INFO", &menu.bottom_spacing_info[i] },
11155 { "menu.paragraph_spacing.INFO", &menu.paragraph_spacing_info[i] },
11156 { "menu.headline1_spacing.INFO", &menu.headline1_spacing_info[i] },
11157 { "menu.headline2_spacing.INFO", &menu.headline2_spacing_info[i] },
11158 { "menu.line_spacing.INFO", &menu.line_spacing_info[i] },
11159 { "menu.extra_spacing.INFO", &menu.extra_spacing_info[i] },
11162 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
11164 char *token = menu_config[j].token;
11165 char *value = getHashEntry(setup_file_hash, token);
11168 *menu_config[j].value = get_integer_from_string(value);
11172 // special case: initialize with default values that may be overwritten
11173 // (eg, init "menu.enter_screen.SCORES.xyz" from "menu.enter_screen.xyz")
11174 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
11176 struct TokenIntPtrInfo menu_config[] =
11178 { "menu.enter_screen.fade_mode", &menu.enter_screen[i].fade_mode },
11179 { "menu.enter_screen.fade_delay", &menu.enter_screen[i].fade_delay },
11180 { "menu.enter_screen.post_delay", &menu.enter_screen[i].post_delay },
11181 { "menu.leave_screen.fade_mode", &menu.leave_screen[i].fade_mode },
11182 { "menu.leave_screen.fade_delay", &menu.leave_screen[i].fade_delay },
11183 { "menu.leave_screen.post_delay", &menu.leave_screen[i].post_delay },
11184 { "menu.next_screen.fade_mode", &menu.next_screen[i].fade_mode },
11185 { "menu.next_screen.fade_delay", &menu.next_screen[i].fade_delay },
11186 { "menu.next_screen.post_delay", &menu.next_screen[i].post_delay }
11189 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
11191 char *token = menu_config[j].token;
11192 char *value = getHashEntry(setup_file_hash, token);
11195 *menu_config[j].value = get_token_parameter_value(token, value);
11199 // special case: initialize with default values that may be overwritten
11200 // (eg, init "viewport.door_1.MAIN.xyz" from "viewport.door_1.xyz")
11201 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
11205 char *token_prefix;
11206 struct RectWithBorder *struct_ptr;
11210 { "viewport.window", &viewport.window[i] },
11211 { "viewport.playfield", &viewport.playfield[i] },
11212 { "viewport.door_1", &viewport.door_1[i] },
11213 { "viewport.door_2", &viewport.door_2[i] }
11216 for (j = 0; j < ARRAY_SIZE(vp_struct); j++)
11218 struct TokenIntPtrInfo vp_config[] =
11220 { ".x", &vp_struct[j].struct_ptr->x },
11221 { ".y", &vp_struct[j].struct_ptr->y },
11222 { ".width", &vp_struct[j].struct_ptr->width },
11223 { ".height", &vp_struct[j].struct_ptr->height },
11224 { ".min_width", &vp_struct[j].struct_ptr->min_width },
11225 { ".min_height", &vp_struct[j].struct_ptr->min_height },
11226 { ".max_width", &vp_struct[j].struct_ptr->max_width },
11227 { ".max_height", &vp_struct[j].struct_ptr->max_height },
11228 { ".margin_left", &vp_struct[j].struct_ptr->margin_left },
11229 { ".margin_right", &vp_struct[j].struct_ptr->margin_right },
11230 { ".margin_top", &vp_struct[j].struct_ptr->margin_top },
11231 { ".margin_bottom", &vp_struct[j].struct_ptr->margin_bottom },
11232 { ".border_left", &vp_struct[j].struct_ptr->border_left },
11233 { ".border_right", &vp_struct[j].struct_ptr->border_right },
11234 { ".border_top", &vp_struct[j].struct_ptr->border_top },
11235 { ".border_bottom", &vp_struct[j].struct_ptr->border_bottom },
11236 { ".border_size", &vp_struct[j].struct_ptr->border_size },
11237 { ".align_size", &vp_struct[j].struct_ptr->align_size },
11238 { ".align", &vp_struct[j].struct_ptr->align },
11239 { ".valign", &vp_struct[j].struct_ptr->valign }
11242 for (k = 0; k < ARRAY_SIZE(vp_config); k++)
11244 char *token = getStringCat2(vp_struct[j].token_prefix,
11245 vp_config[k].token);
11246 char *value = getHashEntry(setup_file_hash, token);
11249 *vp_config[k].value = get_token_parameter_value(token, value);
11256 // special case: initialize with default values that may be overwritten
11257 // (e.g., init "[title].fade_mode" from "menu.next_screen.TITLE.fade_mode")
11258 for (i = 0; title_info[i].info != NULL; i++)
11260 struct TitleFadingInfo *info = title_info[i].info;
11261 char *base_token = title_info[i].text;
11263 for (j = 0; title_tokens[j].type != -1; j++)
11265 char *token = getStringCat2(base_token, title_tokens[j].text);
11266 char *value = getHashEntry(setup_file_hash, token);
11270 int parameter_value = get_token_parameter_value(token, value);
11274 *(int *)title_tokens[j].value = (int)parameter_value;
11283 // special case: initialize with default values that may be overwritten
11284 // (e.g., init "titlemessage_1.fade_mode" from "[titlemessage].fade_mode")
11285 for (i = 0; titlemessage_arrays[i].array != NULL; i++)
11287 struct TitleMessageInfo *array = titlemessage_arrays[i].array;
11288 char *base_token = titlemessage_arrays[i].text;
11290 for (j = 0; titlemessage_tokens[j].type != -1; j++)
11292 char *token = getStringCat2(base_token, titlemessage_tokens[j].text);
11293 char *value = getHashEntry(setup_file_hash, token);
11297 int parameter_value = get_token_parameter_value(token, value);
11299 for (k = 0; k < MAX_NUM_TITLE_MESSAGES; k++)
11303 if (titlemessage_tokens[j].type == TYPE_INTEGER)
11304 *(int *)titlemessage_tokens[j].value = (int)parameter_value;
11306 *(boolean *)titlemessage_tokens[j].value = (boolean)parameter_value;
11316 // special case: check if network and preview player positions are redefined,
11317 // to compare this later against the main menu level preview being redefined
11318 struct TokenIntPtrInfo menu_config_players[] =
11320 { "main.network_players.x", &menu.main.network_players.redefined },
11321 { "main.network_players.y", &menu.main.network_players.redefined },
11322 { "main.preview_players.x", &menu.main.preview_players.redefined },
11323 { "main.preview_players.y", &menu.main.preview_players.redefined },
11324 { "preview.x", &preview.redefined },
11325 { "preview.y", &preview.redefined }
11328 for (i = 0; i < ARRAY_SIZE(menu_config_players); i++)
11329 *menu_config_players[i].value = FALSE;
11331 for (i = 0; i < ARRAY_SIZE(menu_config_players); i++)
11332 if (getHashEntry(setup_file_hash, menu_config_players[i].token) != NULL)
11333 *menu_config_players[i].value = TRUE;
11335 // read (and overwrite with) values that may be specified in config file
11336 for (i = 0; image_config_vars[i].token != NULL; i++)
11338 char *value = getHashEntry(setup_file_hash, image_config_vars[i].token);
11340 // (ignore definitions set to "[DEFAULT]" which are already initialized)
11341 if (value != NULL && !strEqual(value, ARG_DEFAULT))
11342 *image_config_vars[i].value =
11343 get_token_parameter_value(image_config_vars[i].token, value);
11346 freeSetupFileHash(setup_file_hash);
11349 void LoadMenuDesignSettings(void)
11351 char *filename_base = UNDEFINED_FILENAME, *filename_local;
11353 InitMenuDesignSettings_Static();
11354 InitMenuDesignSettings_SpecialPreProcessing();
11356 if (!GFX_OVERRIDE_ARTWORK(ARTWORK_TYPE_GRAPHICS))
11358 // first look for special settings configured in level series config
11359 filename_base = getCustomArtworkLevelConfigFilename(ARTWORK_TYPE_GRAPHICS);
11361 if (fileExists(filename_base))
11362 LoadMenuDesignSettingsFromFilename(filename_base);
11365 filename_local = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
11367 if (filename_local != NULL && !strEqual(filename_base, filename_local))
11368 LoadMenuDesignSettingsFromFilename(filename_local);
11370 InitMenuDesignSettings_SpecialPostProcessing();
11373 void LoadMenuDesignSettings_AfterGraphics(void)
11375 InitMenuDesignSettings_SpecialPostProcessing_AfterGraphics();
11378 void LoadUserDefinedEditorElementList(int **elements, int *num_elements)
11380 char *filename = getEditorSetupFilename();
11381 SetupFileList *setup_file_list, *list;
11382 SetupFileHash *element_hash;
11383 int num_unknown_tokens = 0;
11386 if ((setup_file_list = loadSetupFileList(filename)) == NULL)
11389 element_hash = newSetupFileHash();
11391 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
11392 setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
11394 // determined size may be larger than needed (due to unknown elements)
11396 for (list = setup_file_list; list != NULL; list = list->next)
11399 // add space for up to 3 more elements for padding that may be needed
11400 *num_elements += 3;
11402 // free memory for old list of elements, if needed
11403 checked_free(*elements);
11405 // allocate memory for new list of elements
11406 *elements = checked_malloc(*num_elements * sizeof(int));
11409 for (list = setup_file_list; list != NULL; list = list->next)
11411 char *value = getHashEntry(element_hash, list->token);
11413 if (value == NULL) // try to find obsolete token mapping
11415 char *mapped_token = get_mapped_token(list->token);
11417 if (mapped_token != NULL)
11419 value = getHashEntry(element_hash, mapped_token);
11421 free(mapped_token);
11427 (*elements)[(*num_elements)++] = atoi(value);
11431 if (num_unknown_tokens == 0)
11434 Warn("unknown token(s) found in config file:");
11435 Warn("- config file: '%s'", filename);
11437 num_unknown_tokens++;
11440 Warn("- token: '%s'", list->token);
11444 if (num_unknown_tokens > 0)
11447 while (*num_elements % 4) // pad with empty elements, if needed
11448 (*elements)[(*num_elements)++] = EL_EMPTY;
11450 freeSetupFileList(setup_file_list);
11451 freeSetupFileHash(element_hash);
11454 for (i = 0; i < *num_elements; i++)
11455 Debug("editor", "element '%s' [%d]\n",
11456 element_info[(*elements)[i]].token_name, (*elements)[i]);
11460 static struct MusicFileInfo *get_music_file_info_ext(char *basename, int music,
11463 SetupFileHash *setup_file_hash = NULL;
11464 struct MusicFileInfo tmp_music_file_info, *new_music_file_info;
11465 char *filename_music, *filename_prefix, *filename_info;
11471 token_to_value_ptr[] =
11473 { "title_header", &tmp_music_file_info.title_header },
11474 { "artist_header", &tmp_music_file_info.artist_header },
11475 { "album_header", &tmp_music_file_info.album_header },
11476 { "year_header", &tmp_music_file_info.year_header },
11478 { "title", &tmp_music_file_info.title },
11479 { "artist", &tmp_music_file_info.artist },
11480 { "album", &tmp_music_file_info.album },
11481 { "year", &tmp_music_file_info.year },
11487 filename_music = (is_sound ? getCustomSoundFilename(basename) :
11488 getCustomMusicFilename(basename));
11490 if (filename_music == NULL)
11493 // ---------- try to replace file extension ----------
11495 filename_prefix = getStringCopy(filename_music);
11496 if (strrchr(filename_prefix, '.') != NULL)
11497 *strrchr(filename_prefix, '.') = '\0';
11498 filename_info = getStringCat2(filename_prefix, ".txt");
11500 if (fileExists(filename_info))
11501 setup_file_hash = loadSetupFileHash(filename_info);
11503 free(filename_prefix);
11504 free(filename_info);
11506 if (setup_file_hash == NULL)
11508 // ---------- try to add file extension ----------
11510 filename_prefix = getStringCopy(filename_music);
11511 filename_info = getStringCat2(filename_prefix, ".txt");
11513 if (fileExists(filename_info))
11514 setup_file_hash = loadSetupFileHash(filename_info);
11516 free(filename_prefix);
11517 free(filename_info);
11520 if (setup_file_hash == NULL)
11523 // ---------- music file info found ----------
11525 clear_mem(&tmp_music_file_info, sizeof(struct MusicFileInfo));
11527 for (i = 0; token_to_value_ptr[i].token != NULL; i++)
11529 char *value = getHashEntry(setup_file_hash, token_to_value_ptr[i].token);
11531 *token_to_value_ptr[i].value_ptr =
11532 getStringCopy(value != NULL && *value != '\0' ? value : UNKNOWN_NAME);
11535 tmp_music_file_info.basename = getStringCopy(basename);
11536 tmp_music_file_info.music = music;
11537 tmp_music_file_info.is_sound = is_sound;
11539 new_music_file_info = checked_malloc(sizeof(struct MusicFileInfo));
11540 *new_music_file_info = tmp_music_file_info;
11542 return new_music_file_info;
11545 static struct MusicFileInfo *get_music_file_info(char *basename, int music)
11547 return get_music_file_info_ext(basename, music, FALSE);
11550 static struct MusicFileInfo *get_sound_file_info(char *basename, int sound)
11552 return get_music_file_info_ext(basename, sound, TRUE);
11555 static boolean music_info_listed_ext(struct MusicFileInfo *list,
11556 char *basename, boolean is_sound)
11558 for (; list != NULL; list = list->next)
11559 if (list->is_sound == is_sound && strEqual(list->basename, basename))
11565 static boolean music_info_listed(struct MusicFileInfo *list, char *basename)
11567 return music_info_listed_ext(list, basename, FALSE);
11570 static boolean sound_info_listed(struct MusicFileInfo *list, char *basename)
11572 return music_info_listed_ext(list, basename, TRUE);
11575 void LoadMusicInfo(void)
11577 char *music_directory = getCustomMusicDirectory();
11578 int num_music = getMusicListSize();
11579 int num_music_noconf = 0;
11580 int num_sounds = getSoundListSize();
11582 DirectoryEntry *dir_entry;
11583 struct FileInfo *music, *sound;
11584 struct MusicFileInfo *next, **new;
11587 while (music_file_info != NULL)
11589 next = music_file_info->next;
11591 checked_free(music_file_info->basename);
11593 checked_free(music_file_info->title_header);
11594 checked_free(music_file_info->artist_header);
11595 checked_free(music_file_info->album_header);
11596 checked_free(music_file_info->year_header);
11598 checked_free(music_file_info->title);
11599 checked_free(music_file_info->artist);
11600 checked_free(music_file_info->album);
11601 checked_free(music_file_info->year);
11603 free(music_file_info);
11605 music_file_info = next;
11608 new = &music_file_info;
11610 for (i = 0; i < num_music; i++)
11612 music = getMusicListEntry(i);
11614 if (music->filename == NULL)
11617 if (strEqual(music->filename, UNDEFINED_FILENAME))
11620 // a configured file may be not recognized as music
11621 if (!FileIsMusic(music->filename))
11624 if (!music_info_listed(music_file_info, music->filename))
11626 *new = get_music_file_info(music->filename, i);
11629 new = &(*new)->next;
11633 if ((dir = openDirectory(music_directory)) == NULL)
11635 Warn("cannot read music directory '%s'", music_directory);
11640 while ((dir_entry = readDirectory(dir)) != NULL) // loop all entries
11642 char *basename = dir_entry->basename;
11643 boolean music_already_used = FALSE;
11646 // skip all music files that are configured in music config file
11647 for (i = 0; i < num_music; i++)
11649 music = getMusicListEntry(i);
11651 if (music->filename == NULL)
11654 if (strEqual(basename, music->filename))
11656 music_already_used = TRUE;
11661 if (music_already_used)
11664 if (!FileIsMusic(dir_entry->filename))
11667 if (!music_info_listed(music_file_info, basename))
11669 *new = get_music_file_info(basename, MAP_NOCONF_MUSIC(num_music_noconf));
11672 new = &(*new)->next;
11675 num_music_noconf++;
11678 closeDirectory(dir);
11680 for (i = 0; i < num_sounds; i++)
11682 sound = getSoundListEntry(i);
11684 if (sound->filename == NULL)
11687 if (strEqual(sound->filename, UNDEFINED_FILENAME))
11690 // a configured file may be not recognized as sound
11691 if (!FileIsSound(sound->filename))
11694 if (!sound_info_listed(music_file_info, sound->filename))
11696 *new = get_sound_file_info(sound->filename, i);
11698 new = &(*new)->next;
11703 static void add_helpanim_entry(int element, int action, int direction,
11704 int delay, int *num_list_entries)
11706 struct HelpAnimInfo *new_list_entry;
11707 (*num_list_entries)++;
11710 checked_realloc(helpanim_info,
11711 *num_list_entries * sizeof(struct HelpAnimInfo));
11712 new_list_entry = &helpanim_info[*num_list_entries - 1];
11714 new_list_entry->element = element;
11715 new_list_entry->action = action;
11716 new_list_entry->direction = direction;
11717 new_list_entry->delay = delay;
11720 static void print_unknown_token(char *filename, char *token, int token_nr)
11725 Warn("unknown token(s) found in config file:");
11726 Warn("- config file: '%s'", filename);
11729 Warn("- token: '%s'", token);
11732 static void print_unknown_token_end(int token_nr)
11738 void LoadHelpAnimInfo(void)
11740 char *filename = getHelpAnimFilename();
11741 SetupFileList *setup_file_list = NULL, *list;
11742 SetupFileHash *element_hash, *action_hash, *direction_hash;
11743 int num_list_entries = 0;
11744 int num_unknown_tokens = 0;
11747 if (fileExists(filename))
11748 setup_file_list = loadSetupFileList(filename);
11750 if (setup_file_list == NULL)
11752 // use reliable default values from static configuration
11753 SetupFileList *insert_ptr;
11755 insert_ptr = setup_file_list =
11756 newSetupFileList(helpanim_config[0].token,
11757 helpanim_config[0].value);
11759 for (i = 1; helpanim_config[i].token; i++)
11760 insert_ptr = addListEntry(insert_ptr,
11761 helpanim_config[i].token,
11762 helpanim_config[i].value);
11765 element_hash = newSetupFileHash();
11766 action_hash = newSetupFileHash();
11767 direction_hash = newSetupFileHash();
11769 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
11770 setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
11772 for (i = 0; i < NUM_ACTIONS; i++)
11773 setHashEntry(action_hash, element_action_info[i].suffix,
11774 i_to_a(element_action_info[i].value));
11776 // do not store direction index (bit) here, but direction value!
11777 for (i = 0; i < NUM_DIRECTIONS_FULL; i++)
11778 setHashEntry(direction_hash, element_direction_info[i].suffix,
11779 i_to_a(1 << element_direction_info[i].value));
11781 for (list = setup_file_list; list != NULL; list = list->next)
11783 char *element_token, *action_token, *direction_token;
11784 char *element_value, *action_value, *direction_value;
11785 int delay = atoi(list->value);
11787 if (strEqual(list->token, "end"))
11789 add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
11794 /* first try to break element into element/action/direction parts;
11795 if this does not work, also accept combined "element[.act][.dir]"
11796 elements (like "dynamite.active"), which are unique elements */
11798 if (strchr(list->token, '.') == NULL) // token contains no '.'
11800 element_value = getHashEntry(element_hash, list->token);
11801 if (element_value != NULL) // element found
11802 add_helpanim_entry(atoi(element_value), -1, -1, delay,
11803 &num_list_entries);
11806 // no further suffixes found -- this is not an element
11807 print_unknown_token(filename, list->token, num_unknown_tokens++);
11813 // token has format "<prefix>.<something>"
11815 action_token = strchr(list->token, '.'); // suffix may be action ...
11816 direction_token = action_token; // ... or direction
11818 element_token = getStringCopy(list->token);
11819 *strchr(element_token, '.') = '\0';
11821 element_value = getHashEntry(element_hash, element_token);
11823 if (element_value == NULL) // this is no element
11825 element_value = getHashEntry(element_hash, list->token);
11826 if (element_value != NULL) // combined element found
11827 add_helpanim_entry(atoi(element_value), -1, -1, delay,
11828 &num_list_entries);
11830 print_unknown_token(filename, list->token, num_unknown_tokens++);
11832 free(element_token);
11837 action_value = getHashEntry(action_hash, action_token);
11839 if (action_value != NULL) // action found
11841 add_helpanim_entry(atoi(element_value), atoi(action_value), -1, delay,
11842 &num_list_entries);
11844 free(element_token);
11849 direction_value = getHashEntry(direction_hash, direction_token);
11851 if (direction_value != NULL) // direction found
11853 add_helpanim_entry(atoi(element_value), -1, atoi(direction_value), delay,
11854 &num_list_entries);
11856 free(element_token);
11861 if (strchr(action_token + 1, '.') == NULL)
11863 // no further suffixes found -- this is not an action nor direction
11865 element_value = getHashEntry(element_hash, list->token);
11866 if (element_value != NULL) // combined element found
11867 add_helpanim_entry(atoi(element_value), -1, -1, delay,
11868 &num_list_entries);
11870 print_unknown_token(filename, list->token, num_unknown_tokens++);
11872 free(element_token);
11877 // token has format "<prefix>.<suffix>.<something>"
11879 direction_token = strchr(action_token + 1, '.');
11881 action_token = getStringCopy(action_token);
11882 *strchr(action_token + 1, '.') = '\0';
11884 action_value = getHashEntry(action_hash, action_token);
11886 if (action_value == NULL) // this is no action
11888 element_value = getHashEntry(element_hash, list->token);
11889 if (element_value != NULL) // combined element found
11890 add_helpanim_entry(atoi(element_value), -1, -1, delay,
11891 &num_list_entries);
11893 print_unknown_token(filename, list->token, num_unknown_tokens++);
11895 free(element_token);
11896 free(action_token);
11901 direction_value = getHashEntry(direction_hash, direction_token);
11903 if (direction_value != NULL) // direction found
11905 add_helpanim_entry(atoi(element_value), atoi(action_value),
11906 atoi(direction_value), delay, &num_list_entries);
11908 free(element_token);
11909 free(action_token);
11914 // this is no direction
11916 element_value = getHashEntry(element_hash, list->token);
11917 if (element_value != NULL) // combined element found
11918 add_helpanim_entry(atoi(element_value), -1, -1, delay,
11919 &num_list_entries);
11921 print_unknown_token(filename, list->token, num_unknown_tokens++);
11923 free(element_token);
11924 free(action_token);
11927 print_unknown_token_end(num_unknown_tokens);
11929 add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
11930 add_helpanim_entry(HELPANIM_LIST_END, -1, -1, -1, &num_list_entries);
11932 freeSetupFileList(setup_file_list);
11933 freeSetupFileHash(element_hash);
11934 freeSetupFileHash(action_hash);
11935 freeSetupFileHash(direction_hash);
11938 for (i = 0; i < num_list_entries; i++)
11939 Debug("files:LoadHelpAnimInfo", "'%s': %d, %d, %d => %d",
11940 EL_NAME(helpanim_info[i].element),
11941 helpanim_info[i].element,
11942 helpanim_info[i].action,
11943 helpanim_info[i].direction,
11944 helpanim_info[i].delay);
11948 void LoadHelpTextInfo(void)
11950 char *filename = getHelpTextFilename();
11953 if (helptext_info != NULL)
11955 freeSetupFileHash(helptext_info);
11956 helptext_info = NULL;
11959 if (fileExists(filename))
11960 helptext_info = loadSetupFileHash(filename);
11962 if (helptext_info == NULL)
11964 // use reliable default values from static configuration
11965 helptext_info = newSetupFileHash();
11967 for (i = 0; helptext_config[i].token; i++)
11968 setHashEntry(helptext_info,
11969 helptext_config[i].token,
11970 helptext_config[i].value);
11974 BEGIN_HASH_ITERATION(helptext_info, itr)
11976 Debug("files:LoadHelpTextInfo", "'%s' => '%s'",
11977 HASH_ITERATION_TOKEN(itr), HASH_ITERATION_VALUE(itr));
11979 END_HASH_ITERATION(hash, itr)
11984 // ----------------------------------------------------------------------------
11986 // ----------------------------------------------------------------------------
11988 #define MAX_NUM_CONVERT_LEVELS 1000
11990 void ConvertLevels(void)
11992 static LevelDirTree *convert_leveldir = NULL;
11993 static int convert_level_nr = -1;
11994 static int num_levels_handled = 0;
11995 static int num_levels_converted = 0;
11996 static boolean levels_failed[MAX_NUM_CONVERT_LEVELS];
11999 convert_leveldir = getTreeInfoFromIdentifier(leveldir_first,
12000 global.convert_leveldir);
12002 if (convert_leveldir == NULL)
12003 Fail("no such level identifier: '%s'", global.convert_leveldir);
12005 leveldir_current = convert_leveldir;
12007 if (global.convert_level_nr != -1)
12009 convert_leveldir->first_level = global.convert_level_nr;
12010 convert_leveldir->last_level = global.convert_level_nr;
12013 convert_level_nr = convert_leveldir->first_level;
12015 PrintLine("=", 79);
12016 Print("Converting levels\n");
12017 PrintLine("-", 79);
12018 Print("Level series identifier: '%s'\n", convert_leveldir->identifier);
12019 Print("Level series name: '%s'\n", convert_leveldir->name);
12020 Print("Level series author: '%s'\n", convert_leveldir->author);
12021 Print("Number of levels: %d\n", convert_leveldir->levels);
12022 PrintLine("=", 79);
12025 for (i = 0; i < MAX_NUM_CONVERT_LEVELS; i++)
12026 levels_failed[i] = FALSE;
12028 while (convert_level_nr <= convert_leveldir->last_level)
12030 char *level_filename;
12033 level_nr = convert_level_nr++;
12035 Print("Level %03d: ", level_nr);
12037 LoadLevel(level_nr);
12038 if (level.no_level_file || level.no_valid_file)
12040 Print("(no level)\n");
12044 Print("converting level ... ");
12046 level_filename = getDefaultLevelFilename(level_nr);
12047 new_level = !fileExists(level_filename);
12051 SaveLevel(level_nr);
12053 num_levels_converted++;
12055 Print("converted.\n");
12059 if (level_nr >= 0 && level_nr < MAX_NUM_CONVERT_LEVELS)
12060 levels_failed[level_nr] = TRUE;
12062 Print("NOT CONVERTED -- LEVEL ALREADY EXISTS.\n");
12065 num_levels_handled++;
12069 PrintLine("=", 79);
12070 Print("Number of levels handled: %d\n", num_levels_handled);
12071 Print("Number of levels converted: %d (%d%%)\n", num_levels_converted,
12072 (num_levels_handled ?
12073 num_levels_converted * 100 / num_levels_handled : 0));
12074 PrintLine("-", 79);
12075 Print("Summary (for automatic parsing by scripts):\n");
12076 Print("LEVELDIR '%s', CONVERTED %d/%d (%d%%)",
12077 convert_leveldir->identifier, num_levels_converted,
12078 num_levels_handled,
12079 (num_levels_handled ?
12080 num_levels_converted * 100 / num_levels_handled : 0));
12082 if (num_levels_handled != num_levels_converted)
12084 Print(", FAILED:");
12085 for (i = 0; i < MAX_NUM_CONVERT_LEVELS; i++)
12086 if (levels_failed[i])
12091 PrintLine("=", 79);
12093 CloseAllAndExit(0);
12097 // ----------------------------------------------------------------------------
12098 // create and save images for use in level sketches (raw BMP format)
12099 // ----------------------------------------------------------------------------
12101 void CreateLevelSketchImages(void)
12107 InitElementPropertiesGfxElement();
12109 bitmap1 = CreateBitmap(TILEX, TILEY, DEFAULT_DEPTH);
12110 bitmap2 = CreateBitmap(MINI_TILEX, MINI_TILEY, DEFAULT_DEPTH);
12112 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
12114 int element = getMappedElement(i);
12115 char basename1[16];
12116 char basename2[16];
12120 sprintf(basename1, "%04d.bmp", i);
12121 sprintf(basename2, "%04ds.bmp", i);
12123 filename1 = getPath2(global.create_images_dir, basename1);
12124 filename2 = getPath2(global.create_images_dir, basename2);
12126 DrawSizedElement(0, 0, element, TILESIZE);
12127 BlitBitmap(drawto, bitmap1, SX, SY, TILEX, TILEY, 0, 0);
12129 if (SDL_SaveBMP(bitmap1->surface, filename1) != 0)
12130 Fail("cannot save level sketch image file '%s'", filename1);
12132 DrawSizedElement(0, 0, element, MINI_TILESIZE);
12133 BlitBitmap(drawto, bitmap2, SX, SY, MINI_TILEX, MINI_TILEY, 0, 0);
12135 if (SDL_SaveBMP(bitmap2->surface, filename2) != 0)
12136 Fail("cannot save level sketch image file '%s'", filename2);
12141 // create corresponding SQL statements (for normal and small images)
12144 printf("insert into phpbb_words values (NULL, '`%03d', '<IMG class=\"levelsketch\" src=\"/I/%04d.png\"/>');\n", i, i);
12145 printf("insert into phpbb_words values (NULL, '¸%03d', '<IMG class=\"levelsketch\" src=\"/I/%04ds.png\"/>');\n", i, i);
12148 printf("insert into phpbb_words values (NULL, '`%04d', '<IMG class=\"levelsketch\" src=\"/I/%04d.png\"/>');\n", i, i);
12149 printf("insert into phpbb_words values (NULL, '¸%04d', '<IMG class=\"levelsketch\" src=\"/I/%04ds.png\"/>');\n", i, i);
12151 // optional: create content for forum level sketch demonstration post
12153 fprintf(stderr, "%03d `%03d%c", i, i, (i % 10 < 9 ? ' ' : '\n'));
12156 FreeBitmap(bitmap1);
12157 FreeBitmap(bitmap2);
12160 fprintf(stderr, "\n");
12162 Info("%d normal and small images created", NUM_FILE_ELEMENTS);
12164 CloseAllAndExit(0);
12168 // ----------------------------------------------------------------------------
12169 // create and save images for custom and group elements (raw BMP format)
12170 // ----------------------------------------------------------------------------
12172 void CreateCustomElementImages(char *directory)
12174 char *src_basename = "RocksCE-template.ilbm";
12175 char *dst_basename = "RocksCE.bmp";
12176 char *src_filename = getPath2(directory, src_basename);
12177 char *dst_filename = getPath2(directory, dst_basename);
12178 Bitmap *src_bitmap;
12180 int yoffset_ce = 0;
12181 int yoffset_ge = (TILEY * NUM_CUSTOM_ELEMENTS / 16);
12184 InitVideoDefaults();
12186 ReCreateBitmap(&backbuffer, video.width, video.height);
12188 src_bitmap = LoadImage(src_filename);
12190 bitmap = CreateBitmap(TILEX * 16 * 2,
12191 TILEY * (NUM_CUSTOM_ELEMENTS + NUM_GROUP_ELEMENTS) / 16,
12194 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
12201 BlitBitmap(src_bitmap, bitmap, 0, 0, TILEX, TILEY,
12202 TILEX * x, TILEY * y + yoffset_ce);
12204 BlitBitmap(src_bitmap, bitmap, 0, TILEY,
12206 TILEX * x + TILEX * 16,
12207 TILEY * y + yoffset_ce);
12209 for (j = 2; j >= 0; j--)
12213 BlitBitmap(src_bitmap, bitmap,
12214 TILEX + c * 7, 0, 6, 10,
12215 TILEX * x + 6 + j * 7,
12216 TILEY * y + 11 + yoffset_ce);
12218 BlitBitmap(src_bitmap, bitmap,
12219 TILEX + c * 8, TILEY, 6, 10,
12220 TILEX * 16 + TILEX * x + 6 + j * 8,
12221 TILEY * y + 10 + yoffset_ce);
12227 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
12234 BlitBitmap(src_bitmap, bitmap, 0, 0, TILEX, TILEY,
12235 TILEX * x, TILEY * y + yoffset_ge);
12237 BlitBitmap(src_bitmap, bitmap, 0, TILEY,
12239 TILEX * x + TILEX * 16,
12240 TILEY * y + yoffset_ge);
12242 for (j = 1; j >= 0; j--)
12246 BlitBitmap(src_bitmap, bitmap, TILEX + c * 10, 11, 10, 10,
12247 TILEX * x + 6 + j * 10,
12248 TILEY * y + 11 + yoffset_ge);
12250 BlitBitmap(src_bitmap, bitmap,
12251 TILEX + c * 8, TILEY + 12, 6, 10,
12252 TILEX * 16 + TILEX * x + 10 + j * 8,
12253 TILEY * y + 10 + yoffset_ge);
12259 if (SDL_SaveBMP(bitmap->surface, dst_filename) != 0)
12260 Fail("cannot save CE graphics file '%s'", dst_filename);
12262 FreeBitmap(bitmap);
12264 CloseAllAndExit(0);