1 // ============================================================================
2 // Rocks'n'Diamonds - McDuffin Strikes Back!
3 // ----------------------------------------------------------------------------
4 // (c) 1995-2014 by Artsoft Entertainment
7 // http://www.artsoft.org/
8 // ----------------------------------------------------------------------------
10 // ============================================================================
17 #include "libgame/libgame.h"
26 #define ENABLE_UNUSED_CODE 0 // currently unused functions
27 #define ENABLE_HISTORIC_CHUNKS 0 // only for historic reference
28 #define ENABLE_RESERVED_CODE 0 // reserved for later use
30 #define CHUNK_ID_LEN 4 // IFF style chunk id length
31 #define CHUNK_SIZE_UNDEFINED 0 // undefined chunk size == 0
32 #define CHUNK_SIZE_NONE -1 // do not write chunk size
34 #define LEVEL_CHUNK_NAME_SIZE MAX_LEVEL_NAME_LEN
35 #define LEVEL_CHUNK_AUTH_SIZE MAX_LEVEL_AUTHOR_LEN
37 #define LEVEL_CHUNK_VERS_SIZE 8 // size of file version chunk
38 #define LEVEL_CHUNK_DATE_SIZE 4 // size of file date chunk
39 #define LEVEL_CHUNK_HEAD_SIZE 80 // size of level file header
40 #define LEVEL_CHUNK_HEAD_UNUSED 0 // unused level header bytes
41 #define LEVEL_CHUNK_CNT2_SIZE 160 // size of level CNT2 chunk
42 #define LEVEL_CHUNK_CNT2_UNUSED 11 // unused CNT2 chunk bytes
43 #define LEVEL_CHUNK_CNT3_HEADER 16 // size of level CNT3 header
44 #define LEVEL_CHUNK_CNT3_UNUSED 10 // unused CNT3 chunk bytes
45 #define LEVEL_CPART_CUS3_SIZE 134 // size of CUS3 chunk part
46 #define LEVEL_CPART_CUS3_UNUSED 15 // unused CUS3 bytes / part
47 #define LEVEL_CHUNK_GRP1_SIZE 74 // size of level GRP1 chunk
49 // (element number, number of change pages, change page number)
50 #define LEVEL_CHUNK_CUSX_UNCHANGED (2 + (1 + 1) + (1 + 1))
52 // (element number only)
53 #define LEVEL_CHUNK_GRPX_UNCHANGED 2
54 #define LEVEL_CHUNK_NOTE_UNCHANGED 2
56 // (nothing at all if unchanged)
57 #define LEVEL_CHUNK_ELEM_UNCHANGED 0
59 #define TAPE_CHUNK_VERS_SIZE 8 // size of file version chunk
60 #define TAPE_CHUNK_HEAD_SIZE 20 // size of tape file header
61 #define TAPE_CHUNK_HEAD_UNUSED 2 // unused tape header bytes
63 #define LEVEL_CHUNK_CNT3_SIZE(x) (LEVEL_CHUNK_CNT3_HEADER + (x))
64 #define LEVEL_CHUNK_CUS3_SIZE(x) (2 + (x) * LEVEL_CPART_CUS3_SIZE)
65 #define LEVEL_CHUNK_CUS4_SIZE(x) (96 + (x) * 48)
67 // file identifier strings
68 #define LEVEL_COOKIE_TMPL "ROCKSNDIAMONDS_LEVEL_FILE_VERSION_x.x"
69 #define TAPE_COOKIE_TMPL "ROCKSNDIAMONDS_TAPE_FILE_VERSION_x.x"
70 #define SCORE_COOKIE "ROCKSNDIAMONDS_SCORE_FILE_VERSION_1.2"
72 // values for deciding when (not) to save configuration data
73 #define SAVE_CONF_NEVER 0
74 #define SAVE_CONF_ALWAYS 1
75 #define SAVE_CONF_WHEN_CHANGED -1
77 // values for chunks using micro chunks
78 #define CONF_MASK_1_BYTE 0x00
79 #define CONF_MASK_2_BYTE 0x40
80 #define CONF_MASK_4_BYTE 0x80
81 #define CONF_MASK_MULTI_BYTES 0xc0
83 #define CONF_MASK_BYTES 0xc0
84 #define CONF_MASK_TOKEN 0x3f
86 #define CONF_VALUE_1_BYTE(x) (CONF_MASK_1_BYTE | (x))
87 #define CONF_VALUE_2_BYTE(x) (CONF_MASK_2_BYTE | (x))
88 #define CONF_VALUE_4_BYTE(x) (CONF_MASK_4_BYTE | (x))
89 #define CONF_VALUE_MULTI_BYTES(x) (CONF_MASK_MULTI_BYTES | (x))
91 // these definitions are just for convenience of use and readability
92 #define CONF_VALUE_8_BIT(x) CONF_VALUE_1_BYTE(x)
93 #define CONF_VALUE_16_BIT(x) CONF_VALUE_2_BYTE(x)
94 #define CONF_VALUE_32_BIT(x) CONF_VALUE_4_BYTE(x)
95 #define CONF_VALUE_BYTES(x) CONF_VALUE_MULTI_BYTES(x)
97 #define CONF_VALUE_NUM_BYTES(x) ((x) == CONF_MASK_1_BYTE ? 1 : \
98 (x) == CONF_MASK_2_BYTE ? 2 : \
99 (x) == CONF_MASK_4_BYTE ? 4 : 0)
101 #define CONF_CONTENT_NUM_ELEMENTS (3 * 3)
102 #define CONF_CONTENT_NUM_BYTES (CONF_CONTENT_NUM_ELEMENTS * 2)
103 #define CONF_ELEMENT_NUM_BYTES (2)
105 #define CONF_ENTITY_NUM_BYTES(t) ((t) == TYPE_ELEMENT || \
106 (t) == TYPE_ELEMENT_LIST ? \
107 CONF_ELEMENT_NUM_BYTES : \
108 (t) == TYPE_CONTENT || \
109 (t) == TYPE_CONTENT_LIST ? \
110 CONF_CONTENT_NUM_BYTES : 1)
112 #define CONF_ELEMENT_BYTE_POS(i) ((i) * CONF_ELEMENT_NUM_BYTES)
113 #define CONF_ELEMENTS_ELEMENT(b,i) ((b[CONF_ELEMENT_BYTE_POS(i)] << 8) | \
114 (b[CONF_ELEMENT_BYTE_POS(i) + 1]))
116 #define CONF_CONTENT_ELEMENT_POS(c,x,y) ((c) * CONF_CONTENT_NUM_ELEMENTS + \
118 #define CONF_CONTENT_BYTE_POS(c,x,y) (CONF_CONTENT_ELEMENT_POS(c,x,y) * \
119 CONF_ELEMENT_NUM_BYTES)
120 #define CONF_CONTENTS_ELEMENT(b,c,x,y) ((b[CONF_CONTENT_BYTE_POS(c,x,y)]<< 8)|\
121 (b[CONF_CONTENT_BYTE_POS(c,x,y) + 1]))
123 // temporary variables used to store pointers to structure members
124 static struct LevelInfo li;
125 static struct ElementInfo xx_ei, yy_ei;
126 static struct ElementChangeInfo xx_change;
127 static struct ElementGroupInfo xx_group;
128 static struct EnvelopeInfo xx_envelope;
129 static unsigned int xx_event_bits[NUM_CE_BITFIELDS];
130 static char xx_default_description[MAX_ELEMENT_NAME_LEN + 1];
131 static int xx_num_contents;
132 static int xx_current_change_page;
133 static char xx_default_string_empty[1] = "";
134 static int xx_string_length_unused;
136 struct LevelFileConfigInfo
138 int element; // element for which data is to be stored
139 int save_type; // save data always, never or when changed
140 int data_type; // data type (used internally, not stored)
141 int conf_type; // micro chunk identifier (stored in file)
144 void *value; // variable that holds the data to be stored
145 int default_value; // initial default value for this variable
148 void *value_copy; // variable that holds the data to be copied
149 void *num_entities; // number of entities for multi-byte data
150 int default_num_entities; // default number of entities for this data
151 int max_num_entities; // maximal number of entities for this data
152 char *default_string; // optional default string for string data
155 static struct LevelFileConfigInfo chunk_config_INFO[] =
157 // ---------- values not related to single elements -------------------------
160 -1, SAVE_CONF_ALWAYS,
161 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
162 &li.game_engine_type, GAME_ENGINE_TYPE_RND
166 -1, SAVE_CONF_ALWAYS,
167 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
168 &li.fieldx, STD_LEV_FIELDX
171 -1, SAVE_CONF_ALWAYS,
172 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
173 &li.fieldy, STD_LEV_FIELDY
177 -1, SAVE_CONF_ALWAYS,
178 TYPE_INTEGER, CONF_VALUE_16_BIT(3),
183 -1, SAVE_CONF_ALWAYS,
184 TYPE_INTEGER, CONF_VALUE_16_BIT(4),
190 TYPE_INTEGER, CONF_VALUE_32_BIT(2),
196 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
197 &li.use_step_counter, FALSE
202 TYPE_BITFIELD, CONF_VALUE_8_BIT(4),
203 &li.wind_direction_initial, MV_NONE
208 TYPE_BOOLEAN, CONF_VALUE_8_BIT(5),
209 &li.em_slippery_gems, FALSE
214 TYPE_BOOLEAN, CONF_VALUE_8_BIT(6),
215 &li.use_custom_template, FALSE
220 TYPE_BITFIELD, CONF_VALUE_32_BIT(1),
221 &li.can_move_into_acid_bits, ~0 // default: everything can
226 TYPE_BITFIELD, CONF_VALUE_8_BIT(7),
227 &li.dont_collide_with_bits, ~0 // default: always deadly
232 TYPE_BOOLEAN, CONF_VALUE_8_BIT(8),
233 &li.em_explodes_by_fire, FALSE
238 TYPE_INTEGER, CONF_VALUE_16_BIT(5),
239 &li.score[SC_TIME_BONUS], 1
244 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
245 &li.auto_exit_sokoban, FALSE
250 TYPE_BOOLEAN, CONF_VALUE_8_BIT(10),
251 &li.auto_count_gems, FALSE
256 TYPE_BOOLEAN, CONF_VALUE_8_BIT(11),
257 &li.solved_by_one_player, FALSE
267 static struct LevelFileConfigInfo chunk_config_ELEM[] =
269 // (these values are the same for each player)
272 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
273 &li.block_last_field, FALSE // default case for EM levels
277 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
278 &li.sp_block_last_field, TRUE // default case for SP levels
282 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
283 &li.instant_relocation, FALSE
287 TYPE_BOOLEAN, CONF_VALUE_8_BIT(4),
288 &li.can_pass_to_walkable, FALSE
292 TYPE_BOOLEAN, CONF_VALUE_8_BIT(5),
293 &li.block_snap_field, TRUE
297 TYPE_BOOLEAN, CONF_VALUE_8_BIT(6),
298 &li.continuous_snapping, TRUE
302 TYPE_BOOLEAN, CONF_VALUE_8_BIT(12),
303 &li.shifted_relocation, FALSE
307 TYPE_BOOLEAN, CONF_VALUE_8_BIT(15),
308 &li.lazy_relocation, FALSE
311 // (these values are different for each player)
314 TYPE_INTEGER, CONF_VALUE_8_BIT(7),
315 &li.initial_player_stepsize[0], STEPSIZE_NORMAL
319 TYPE_BOOLEAN, CONF_VALUE_8_BIT(8),
320 &li.initial_player_gravity[0], FALSE
324 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
325 &li.use_start_element[0], FALSE
329 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
330 &li.start_element[0], EL_PLAYER_1
334 TYPE_BOOLEAN, CONF_VALUE_8_BIT(10),
335 &li.use_artwork_element[0], FALSE
339 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
340 &li.artwork_element[0], EL_PLAYER_1
344 TYPE_BOOLEAN, CONF_VALUE_8_BIT(11),
345 &li.use_explosion_element[0], FALSE
349 TYPE_ELEMENT, CONF_VALUE_16_BIT(3),
350 &li.explosion_element[0], EL_PLAYER_1
354 TYPE_BOOLEAN, CONF_VALUE_8_BIT(13),
355 &li.use_initial_inventory[0], FALSE
359 TYPE_BOOLEAN, CONF_VALUE_8_BIT(14),
360 &li.initial_inventory_size[0], 1
364 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(1),
365 &li.initial_inventory_content[0][0],EL_EMPTY, NULL,
366 &li.initial_inventory_size[0], 1, MAX_INITIAL_INVENTORY_SIZE
371 TYPE_INTEGER, CONF_VALUE_8_BIT(7),
372 &li.initial_player_stepsize[1], STEPSIZE_NORMAL
376 TYPE_BOOLEAN, CONF_VALUE_8_BIT(8),
377 &li.initial_player_gravity[1], FALSE
381 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
382 &li.use_start_element[1], FALSE
386 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
387 &li.start_element[1], EL_PLAYER_2
391 TYPE_BOOLEAN, CONF_VALUE_8_BIT(10),
392 &li.use_artwork_element[1], FALSE
396 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
397 &li.artwork_element[1], EL_PLAYER_2
401 TYPE_BOOLEAN, CONF_VALUE_8_BIT(11),
402 &li.use_explosion_element[1], FALSE
406 TYPE_ELEMENT, CONF_VALUE_16_BIT(3),
407 &li.explosion_element[1], EL_PLAYER_2
411 TYPE_BOOLEAN, CONF_VALUE_8_BIT(13),
412 &li.use_initial_inventory[1], FALSE
416 TYPE_BOOLEAN, CONF_VALUE_8_BIT(14),
417 &li.initial_inventory_size[1], 1
421 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(1),
422 &li.initial_inventory_content[1][0],EL_EMPTY, NULL,
423 &li.initial_inventory_size[1], 1, MAX_INITIAL_INVENTORY_SIZE
428 TYPE_INTEGER, CONF_VALUE_8_BIT(7),
429 &li.initial_player_stepsize[2], STEPSIZE_NORMAL
433 TYPE_BOOLEAN, CONF_VALUE_8_BIT(8),
434 &li.initial_player_gravity[2], FALSE
438 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
439 &li.use_start_element[2], FALSE
443 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
444 &li.start_element[2], EL_PLAYER_3
448 TYPE_BOOLEAN, CONF_VALUE_8_BIT(10),
449 &li.use_artwork_element[2], FALSE
453 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
454 &li.artwork_element[2], EL_PLAYER_3
458 TYPE_BOOLEAN, CONF_VALUE_8_BIT(11),
459 &li.use_explosion_element[2], FALSE
463 TYPE_ELEMENT, CONF_VALUE_16_BIT(3),
464 &li.explosion_element[2], EL_PLAYER_3
468 TYPE_BOOLEAN, CONF_VALUE_8_BIT(13),
469 &li.use_initial_inventory[2], FALSE
473 TYPE_BOOLEAN, CONF_VALUE_8_BIT(14),
474 &li.initial_inventory_size[2], 1
478 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(1),
479 &li.initial_inventory_content[2][0],EL_EMPTY, NULL,
480 &li.initial_inventory_size[2], 1, MAX_INITIAL_INVENTORY_SIZE
485 TYPE_INTEGER, CONF_VALUE_8_BIT(7),
486 &li.initial_player_stepsize[3], STEPSIZE_NORMAL
490 TYPE_BOOLEAN, CONF_VALUE_8_BIT(8),
491 &li.initial_player_gravity[3], FALSE
495 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
496 &li.use_start_element[3], FALSE
500 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
501 &li.start_element[3], EL_PLAYER_4
505 TYPE_BOOLEAN, CONF_VALUE_8_BIT(10),
506 &li.use_artwork_element[3], FALSE
510 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
511 &li.artwork_element[3], EL_PLAYER_4
515 TYPE_BOOLEAN, CONF_VALUE_8_BIT(11),
516 &li.use_explosion_element[3], FALSE
520 TYPE_ELEMENT, CONF_VALUE_16_BIT(3),
521 &li.explosion_element[3], EL_PLAYER_4
525 TYPE_BOOLEAN, CONF_VALUE_8_BIT(13),
526 &li.use_initial_inventory[3], FALSE
530 TYPE_BOOLEAN, CONF_VALUE_8_BIT(14),
531 &li.initial_inventory_size[3], 1
535 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(1),
536 &li.initial_inventory_content[3][0],EL_EMPTY, NULL,
537 &li.initial_inventory_size[3], 1, MAX_INITIAL_INVENTORY_SIZE
542 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
543 &li.score[SC_EMERALD], 10
548 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
549 &li.score[SC_DIAMOND], 10
554 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
555 &li.score[SC_BUG], 10
560 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
561 &li.score[SC_SPACESHIP], 10
566 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
567 &li.score[SC_PACMAN], 10
572 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
573 &li.score[SC_NUT], 10
578 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
579 &li.score[SC_DYNAMITE], 10
584 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
585 &li.score[SC_KEY], 10
590 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
591 &li.score[SC_PEARL], 10
596 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
597 &li.score[SC_CRYSTAL], 10
602 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
603 &li.amoeba_content, EL_DIAMOND
607 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
612 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
613 &li.grow_into_diggable, TRUE
618 TYPE_CONTENT_LIST, CONF_VALUE_BYTES(1),
619 &li.yamyam_content, EL_ROCK, NULL,
620 &li.num_yamyam_contents, 4, MAX_ELEMENT_CONTENTS
624 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
625 &li.score[SC_YAMYAM], 10
630 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
631 &li.score[SC_ROBOT], 10
635 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
641 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
647 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
648 &li.time_magic_wall, 10
653 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
654 &li.game_of_life[0], 2
658 TYPE_INTEGER, CONF_VALUE_8_BIT(2),
659 &li.game_of_life[1], 3
663 TYPE_INTEGER, CONF_VALUE_8_BIT(3),
664 &li.game_of_life[2], 3
668 TYPE_INTEGER, CONF_VALUE_8_BIT(4),
669 &li.game_of_life[3], 3
673 TYPE_BOOLEAN, CONF_VALUE_8_BIT(5),
674 &li.use_life_bugs, FALSE
679 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
684 TYPE_INTEGER, CONF_VALUE_8_BIT(2),
689 TYPE_INTEGER, CONF_VALUE_8_BIT(3),
694 TYPE_INTEGER, CONF_VALUE_8_BIT(4),
699 EL_TIMEGATE_SWITCH, -1,
700 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
701 &li.time_timegate, 10
705 EL_LIGHT_SWITCH_ACTIVE, -1,
706 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
711 EL_SHIELD_NORMAL, -1,
712 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
713 &li.shield_normal_time, 10
716 EL_SHIELD_NORMAL, -1,
717 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
718 &li.score[SC_SHIELD], 10
722 EL_SHIELD_DEADLY, -1,
723 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
724 &li.shield_deadly_time, 10
727 EL_SHIELD_DEADLY, -1,
728 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
729 &li.score[SC_SHIELD], 10
734 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
739 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
740 &li.extra_time_score, 10
744 EL_TIME_ORB_FULL, -1,
745 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
746 &li.time_orb_time, 10
749 EL_TIME_ORB_FULL, -1,
750 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
751 &li.use_time_orb_bug, FALSE
756 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
757 &li.use_spring_bug, FALSE
762 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
763 &li.android_move_time, 10
767 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
768 &li.android_clone_time, 10
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 // other flags that may be set due to certain level properties
1737 level->has_mouse_events = FALSE;
1739 if (leveldir_current)
1741 // try to determine better author name than 'anonymous'
1742 if (!strEqual(leveldir_current->author, ANONYMOUS_NAME))
1744 strncpy(level->author, leveldir_current->author, MAX_LEVEL_AUTHOR_LEN);
1745 level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
1749 switch (LEVELCLASS(leveldir_current))
1751 case LEVELCLASS_TUTORIAL:
1752 strcpy(level->author, PROGRAM_AUTHOR_STRING);
1755 case LEVELCLASS_CONTRIB:
1756 strncpy(level->author, leveldir_current->name, MAX_LEVEL_AUTHOR_LEN);
1757 level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
1760 case LEVELCLASS_PRIVATE:
1761 strncpy(level->author, getRealName(), MAX_LEVEL_AUTHOR_LEN);
1762 level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
1766 // keep default value
1773 static void setLevelInfoToDefaults_Elements(struct LevelInfo *level)
1775 static boolean clipboard_elements_initialized = FALSE;
1778 InitElementPropertiesStatic();
1780 li = *level; // copy level data into temporary buffer
1781 setConfigToDefaultsFromConfigList(chunk_config_ELEM);
1782 *level = li; // copy temporary buffer back to level data
1784 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1787 struct ElementInfo *ei = &element_info[element];
1789 // never initialize clipboard elements after the very first time
1790 // (to be able to use clipboard elements between several levels)
1791 if (IS_CLIPBOARD_ELEMENT(element) && clipboard_elements_initialized)
1794 if (IS_ENVELOPE(element))
1796 int envelope_nr = element - EL_ENVELOPE_1;
1798 setConfigToDefaultsFromConfigList(chunk_config_NOTE);
1800 level->envelope[envelope_nr] = xx_envelope;
1803 if (IS_CUSTOM_ELEMENT(element) ||
1804 IS_GROUP_ELEMENT(element) ||
1805 IS_INTERNAL_ELEMENT(element))
1807 xx_ei = *ei; // copy element data into temporary buffer
1809 setConfigToDefaultsFromConfigList(chunk_config_CUSX_base);
1814 setElementChangePages(ei, 1);
1815 setElementChangeInfoToDefaults(ei->change);
1817 if (IS_CUSTOM_ELEMENT(element) ||
1818 IS_GROUP_ELEMENT(element) ||
1819 IS_INTERNAL_ELEMENT(element))
1821 setElementDescriptionToDefault(ei);
1823 ei->modified_settings = FALSE;
1826 if (IS_CUSTOM_ELEMENT(element) ||
1827 IS_INTERNAL_ELEMENT(element))
1829 // internal values used in level editor
1831 ei->access_type = 0;
1832 ei->access_layer = 0;
1833 ei->access_protected = 0;
1834 ei->walk_to_action = 0;
1835 ei->smash_targets = 0;
1838 ei->can_explode_by_fire = FALSE;
1839 ei->can_explode_smashed = FALSE;
1840 ei->can_explode_impact = FALSE;
1842 ei->current_change_page = 0;
1845 if (IS_GROUP_ELEMENT(element) ||
1846 IS_INTERNAL_ELEMENT(element))
1848 struct ElementGroupInfo *group;
1850 // initialize memory for list of elements in group
1851 if (ei->group == NULL)
1852 ei->group = checked_malloc(sizeof(struct ElementGroupInfo));
1856 xx_group = *group; // copy group data into temporary buffer
1858 setConfigToDefaultsFromConfigList(chunk_config_GRPX);
1864 clipboard_elements_initialized = TRUE;
1867 static void setLevelInfoToDefaults(struct LevelInfo *level,
1868 boolean level_info_only,
1869 boolean reset_file_status)
1871 setLevelInfoToDefaults_Level(level);
1873 if (!level_info_only)
1874 setLevelInfoToDefaults_Elements(level);
1876 if (reset_file_status)
1878 level->no_valid_file = FALSE;
1879 level->no_level_file = FALSE;
1882 level->changed = FALSE;
1885 static void setFileInfoToDefaults(struct LevelFileInfo *level_file_info)
1887 level_file_info->nr = 0;
1888 level_file_info->type = LEVEL_FILE_TYPE_UNKNOWN;
1889 level_file_info->packed = FALSE;
1891 setString(&level_file_info->basename, NULL);
1892 setString(&level_file_info->filename, NULL);
1895 int getMappedElement_SB(int, boolean);
1897 static void ActivateLevelTemplate(void)
1901 if (check_special_flags("load_xsb_to_ces"))
1903 // fill smaller playfields with padding "beyond border wall" elements
1904 if (level.fieldx < level_template.fieldx ||
1905 level.fieldy < level_template.fieldy)
1907 short field[level.fieldx][level.fieldy];
1908 int new_fieldx = MAX(level.fieldx, level_template.fieldx);
1909 int new_fieldy = MAX(level.fieldy, level_template.fieldy);
1910 int pos_fieldx = (new_fieldx - level.fieldx) / 2;
1911 int pos_fieldy = (new_fieldy - level.fieldy) / 2;
1913 // copy old playfield (which is smaller than the visible area)
1914 for (y = 0; y < level.fieldy; y++) for (x = 0; x < level.fieldx; x++)
1915 field[x][y] = level.field[x][y];
1917 // fill new, larger playfield with "beyond border wall" elements
1918 for (y = 0; y < new_fieldy; y++) for (x = 0; x < new_fieldx; x++)
1919 level.field[x][y] = getMappedElement_SB('_', TRUE);
1921 // copy the old playfield to the middle of the new playfield
1922 for (y = 0; y < level.fieldy; y++) for (x = 0; x < level.fieldx; x++)
1923 level.field[pos_fieldx + x][pos_fieldy + y] = field[x][y];
1925 level.fieldx = new_fieldx;
1926 level.fieldy = new_fieldy;
1930 // Currently there is no special action needed to activate the template
1931 // data, because 'element_info' property settings overwrite the original
1932 // level data, while all other variables do not change.
1934 // Exception: 'from_level_template' elements in the original level playfield
1935 // are overwritten with the corresponding elements at the same position in
1936 // playfield from the level template.
1938 for (x = 0; x < level.fieldx; x++)
1939 for (y = 0; y < level.fieldy; y++)
1940 if (level.field[x][y] == EL_FROM_LEVEL_TEMPLATE)
1941 level.field[x][y] = level_template.field[x][y];
1943 if (check_special_flags("load_xsb_to_ces"))
1945 struct LevelInfo level_backup = level;
1947 // overwrite all individual level settings from template level settings
1948 level = level_template;
1950 // restore level file info
1951 level.file_info = level_backup.file_info;
1953 // restore playfield size
1954 level.fieldx = level_backup.fieldx;
1955 level.fieldy = level_backup.fieldy;
1957 // restore playfield content
1958 for (x = 0; x < level.fieldx; x++)
1959 for (y = 0; y < level.fieldy; y++)
1960 level.field[x][y] = level_backup.field[x][y];
1962 // restore name and author from individual level
1963 strcpy(level.name, level_backup.name);
1964 strcpy(level.author, level_backup.author);
1966 // restore flag "use_custom_template"
1967 level.use_custom_template = level_backup.use_custom_template;
1971 static char *getLevelFilenameFromBasename(char *basename)
1973 static char *filename = NULL;
1975 checked_free(filename);
1977 filename = getPath2(getCurrentLevelDir(), basename);
1982 static int getFileTypeFromBasename(char *basename)
1984 // !!! ALSO SEE COMMENT IN checkForPackageFromBasename() !!!
1986 static char *filename = NULL;
1987 struct stat file_status;
1989 // ---------- try to determine file type from filename ----------
1991 // check for typical filename of a Supaplex level package file
1992 if (strlen(basename) == 10 && strPrefixLower(basename, "levels.d"))
1993 return LEVEL_FILE_TYPE_SP;
1995 // check for typical filename of a Diamond Caves II level package file
1996 if (strSuffixLower(basename, ".dc") ||
1997 strSuffixLower(basename, ".dc2"))
1998 return LEVEL_FILE_TYPE_DC;
2000 // check for typical filename of a Sokoban level package file
2001 if (strSuffixLower(basename, ".xsb") &&
2002 strchr(basename, '%') == NULL)
2003 return LEVEL_FILE_TYPE_SB;
2005 // ---------- try to determine file type from filesize ----------
2007 checked_free(filename);
2008 filename = getPath2(getCurrentLevelDir(), basename);
2010 if (stat(filename, &file_status) == 0)
2012 // check for typical filesize of a Supaplex level package file
2013 if (file_status.st_size == 170496)
2014 return LEVEL_FILE_TYPE_SP;
2017 return LEVEL_FILE_TYPE_UNKNOWN;
2020 static int getFileTypeFromMagicBytes(char *filename, int type)
2024 if ((file = openFile(filename, MODE_READ)))
2026 char chunk_name[CHUNK_ID_LEN + 1];
2028 getFileChunkBE(file, chunk_name, NULL);
2030 if (strEqual(chunk_name, "MMII") ||
2031 strEqual(chunk_name, "MIRR"))
2032 type = LEVEL_FILE_TYPE_MM;
2040 static boolean checkForPackageFromBasename(char *basename)
2042 // !!! WON'T WORK ANYMORE IF getFileTypeFromBasename() ALSO DETECTS !!!
2043 // !!! SINGLE LEVELS (CURRENTLY ONLY DETECTS LEVEL PACKAGES !!!
2045 return (getFileTypeFromBasename(basename) != LEVEL_FILE_TYPE_UNKNOWN);
2048 static char *getSingleLevelBasenameExt(int nr, char *extension)
2050 static char basename[MAX_FILENAME_LEN];
2053 sprintf(basename, "%s", LEVELTEMPLATE_FILENAME);
2055 sprintf(basename, "%03d.%s", nr, extension);
2060 static char *getSingleLevelBasename(int nr)
2062 return getSingleLevelBasenameExt(nr, LEVELFILE_EXTENSION);
2065 static char *getPackedLevelBasename(int type)
2067 static char basename[MAX_FILENAME_LEN];
2068 char *directory = getCurrentLevelDir();
2070 DirectoryEntry *dir_entry;
2072 strcpy(basename, UNDEFINED_FILENAME); // default: undefined file
2074 if ((dir = openDirectory(directory)) == NULL)
2076 Error(ERR_WARN, "cannot read current level directory '%s'", directory);
2081 while ((dir_entry = readDirectory(dir)) != NULL) // loop all entries
2083 char *entry_basename = dir_entry->basename;
2084 int entry_type = getFileTypeFromBasename(entry_basename);
2086 if (entry_type != LEVEL_FILE_TYPE_UNKNOWN) // found valid level package
2088 if (type == LEVEL_FILE_TYPE_UNKNOWN ||
2091 strcpy(basename, entry_basename);
2098 closeDirectory(dir);
2103 static char *getSingleLevelFilename(int nr)
2105 return getLevelFilenameFromBasename(getSingleLevelBasename(nr));
2108 #if ENABLE_UNUSED_CODE
2109 static char *getPackedLevelFilename(int type)
2111 return getLevelFilenameFromBasename(getPackedLevelBasename(type));
2115 char *getDefaultLevelFilename(int nr)
2117 return getSingleLevelFilename(nr);
2120 #if ENABLE_UNUSED_CODE
2121 static void setLevelFileInfo_SingleLevelFilename(struct LevelFileInfo *lfi,
2125 lfi->packed = FALSE;
2127 setString(&lfi->basename, getSingleLevelBasename(lfi->nr, lfi->type));
2128 setString(&lfi->filename, getLevelFilenameFromBasename(lfi->basename));
2132 static void setLevelFileInfo_FormatLevelFilename(struct LevelFileInfo *lfi,
2133 int type, char *format, ...)
2135 static char basename[MAX_FILENAME_LEN];
2138 va_start(ap, format);
2139 vsprintf(basename, format, ap);
2143 lfi->packed = FALSE;
2145 setString(&lfi->basename, basename);
2146 setString(&lfi->filename, getLevelFilenameFromBasename(lfi->basename));
2149 static void setLevelFileInfo_PackedLevelFilename(struct LevelFileInfo *lfi,
2155 setString(&lfi->basename, getPackedLevelBasename(lfi->type));
2156 setString(&lfi->filename, getLevelFilenameFromBasename(lfi->basename));
2159 static int getFiletypeFromID(char *filetype_id)
2161 char *filetype_id_lower;
2162 int filetype = LEVEL_FILE_TYPE_UNKNOWN;
2165 if (filetype_id == NULL)
2166 return LEVEL_FILE_TYPE_UNKNOWN;
2168 filetype_id_lower = getStringToLower(filetype_id);
2170 for (i = 0; filetype_id_list[i].id != NULL; i++)
2172 char *id_lower = getStringToLower(filetype_id_list[i].id);
2174 if (strEqual(filetype_id_lower, id_lower))
2175 filetype = filetype_id_list[i].filetype;
2179 if (filetype != LEVEL_FILE_TYPE_UNKNOWN)
2183 free(filetype_id_lower);
2188 char *getLocalLevelTemplateFilename(void)
2190 return getDefaultLevelFilename(-1);
2193 char *getGlobalLevelTemplateFilename(void)
2195 // global variable "leveldir_current" must be modified in the loop below
2196 LevelDirTree *leveldir_current_last = leveldir_current;
2197 char *filename = NULL;
2199 // check for template level in path from current to topmost tree node
2201 while (leveldir_current != NULL)
2203 filename = getDefaultLevelFilename(-1);
2205 if (fileExists(filename))
2208 leveldir_current = leveldir_current->node_parent;
2211 // restore global variable "leveldir_current" modified in above loop
2212 leveldir_current = leveldir_current_last;
2217 static void determineLevelFileInfo_Filename(struct LevelFileInfo *lfi)
2221 // special case: level number is negative => check for level template file
2224 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
2225 getSingleLevelBasename(-1));
2227 // replace local level template filename with global template filename
2228 setString(&lfi->filename, getGlobalLevelTemplateFilename());
2230 // no fallback if template file not existing
2234 // special case: check for file name/pattern specified in "levelinfo.conf"
2235 if (leveldir_current->level_filename != NULL)
2237 int filetype = getFiletypeFromID(leveldir_current->level_filetype);
2239 setLevelFileInfo_FormatLevelFilename(lfi, filetype,
2240 leveldir_current->level_filename, nr);
2242 lfi->packed = checkForPackageFromBasename(leveldir_current->level_filename);
2244 if (fileExists(lfi->filename))
2247 else if (leveldir_current->level_filetype != NULL)
2249 int filetype = getFiletypeFromID(leveldir_current->level_filetype);
2251 // check for specified native level file with standard file name
2252 setLevelFileInfo_FormatLevelFilename(lfi, filetype,
2253 "%03d.%s", nr, LEVELFILE_EXTENSION);
2254 if (fileExists(lfi->filename))
2258 // check for native Rocks'n'Diamonds level file
2259 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
2260 "%03d.%s", nr, LEVELFILE_EXTENSION);
2261 if (fileExists(lfi->filename))
2264 // check for Emerald Mine level file (V1)
2265 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "a%c%c",
2266 'a' + (nr / 10) % 26, '0' + nr % 10);
2267 if (fileExists(lfi->filename))
2269 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "A%c%c",
2270 'A' + (nr / 10) % 26, '0' + nr % 10);
2271 if (fileExists(lfi->filename))
2274 // check for Emerald Mine level file (V2 to V5)
2275 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%d", nr);
2276 if (fileExists(lfi->filename))
2279 // check for Emerald Mine level file (V6 / single mode)
2280 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02ds", nr);
2281 if (fileExists(lfi->filename))
2283 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02dS", nr);
2284 if (fileExists(lfi->filename))
2287 // check for Emerald Mine level file (V6 / teamwork mode)
2288 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02dt", nr);
2289 if (fileExists(lfi->filename))
2291 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02dT", nr);
2292 if (fileExists(lfi->filename))
2295 // check for various packed level file formats
2296 setLevelFileInfo_PackedLevelFilename(lfi, LEVEL_FILE_TYPE_UNKNOWN);
2297 if (fileExists(lfi->filename))
2300 // no known level file found -- use default values (and fail later)
2301 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
2302 "%03d.%s", nr, LEVELFILE_EXTENSION);
2305 static void determineLevelFileInfo_Filetype(struct LevelFileInfo *lfi)
2307 if (lfi->type == LEVEL_FILE_TYPE_UNKNOWN)
2308 lfi->type = getFileTypeFromBasename(lfi->basename);
2310 if (lfi->type == LEVEL_FILE_TYPE_RND)
2311 lfi->type = getFileTypeFromMagicBytes(lfi->filename, lfi->type);
2314 static void setLevelFileInfo(struct LevelFileInfo *level_file_info, int nr)
2316 // always start with reliable default values
2317 setFileInfoToDefaults(level_file_info);
2319 level_file_info->nr = nr; // set requested level number
2321 determineLevelFileInfo_Filename(level_file_info);
2322 determineLevelFileInfo_Filetype(level_file_info);
2325 static void copyLevelFileInfo(struct LevelFileInfo *lfi_from,
2326 struct LevelFileInfo *lfi_to)
2328 lfi_to->nr = lfi_from->nr;
2329 lfi_to->type = lfi_from->type;
2330 lfi_to->packed = lfi_from->packed;
2332 setString(&lfi_to->basename, lfi_from->basename);
2333 setString(&lfi_to->filename, lfi_from->filename);
2336 // ----------------------------------------------------------------------------
2337 // functions for loading R'n'D level
2338 // ----------------------------------------------------------------------------
2340 static int getMappedElement(int element)
2342 // remap some (historic, now obsolete) elements
2346 case EL_PLAYER_OBSOLETE:
2347 element = EL_PLAYER_1;
2350 case EL_KEY_OBSOLETE:
2354 case EL_EM_KEY_1_FILE_OBSOLETE:
2355 element = EL_EM_KEY_1;
2358 case EL_EM_KEY_2_FILE_OBSOLETE:
2359 element = EL_EM_KEY_2;
2362 case EL_EM_KEY_3_FILE_OBSOLETE:
2363 element = EL_EM_KEY_3;
2366 case EL_EM_KEY_4_FILE_OBSOLETE:
2367 element = EL_EM_KEY_4;
2370 case EL_ENVELOPE_OBSOLETE:
2371 element = EL_ENVELOPE_1;
2379 if (element >= NUM_FILE_ELEMENTS)
2381 Error(ERR_WARN, "invalid level element %d", element);
2383 element = EL_UNKNOWN;
2391 static int getMappedElementByVersion(int element, int game_version)
2393 // remap some elements due to certain game version
2395 if (game_version <= VERSION_IDENT(2,2,0,0))
2397 // map game font elements
2398 element = (element == EL_CHAR('[') ? EL_CHAR_AUMLAUT :
2399 element == EL_CHAR('\\') ? EL_CHAR_OUMLAUT :
2400 element == EL_CHAR(']') ? EL_CHAR_UUMLAUT :
2401 element == EL_CHAR('^') ? EL_CHAR_COPYRIGHT : element);
2404 if (game_version < VERSION_IDENT(3,0,0,0))
2406 // map Supaplex gravity tube elements
2407 element = (element == EL_SP_GRAVITY_PORT_LEFT ? EL_SP_PORT_LEFT :
2408 element == EL_SP_GRAVITY_PORT_RIGHT ? EL_SP_PORT_RIGHT :
2409 element == EL_SP_GRAVITY_PORT_UP ? EL_SP_PORT_UP :
2410 element == EL_SP_GRAVITY_PORT_DOWN ? EL_SP_PORT_DOWN :
2417 static int LoadLevel_VERS(File *file, int chunk_size, struct LevelInfo *level)
2419 level->file_version = getFileVersion(file);
2420 level->game_version = getFileVersion(file);
2425 static int LoadLevel_DATE(File *file, int chunk_size, struct LevelInfo *level)
2427 level->creation_date.year = getFile16BitBE(file);
2428 level->creation_date.month = getFile8Bit(file);
2429 level->creation_date.day = getFile8Bit(file);
2431 level->creation_date.src = DATE_SRC_LEVELFILE;
2436 static int LoadLevel_HEAD(File *file, int chunk_size, struct LevelInfo *level)
2438 int initial_player_stepsize;
2439 int initial_player_gravity;
2442 level->fieldx = getFile8Bit(file);
2443 level->fieldy = getFile8Bit(file);
2445 level->time = getFile16BitBE(file);
2446 level->gems_needed = getFile16BitBE(file);
2448 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
2449 level->name[i] = getFile8Bit(file);
2450 level->name[MAX_LEVEL_NAME_LEN] = 0;
2452 for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
2453 level->score[i] = getFile8Bit(file);
2455 level->num_yamyam_contents = STD_ELEMENT_CONTENTS;
2456 for (i = 0; i < STD_ELEMENT_CONTENTS; i++)
2457 for (y = 0; y < 3; y++)
2458 for (x = 0; x < 3; x++)
2459 level->yamyam_content[i].e[x][y] = getMappedElement(getFile8Bit(file));
2461 level->amoeba_speed = getFile8Bit(file);
2462 level->time_magic_wall = getFile8Bit(file);
2463 level->time_wheel = getFile8Bit(file);
2464 level->amoeba_content = getMappedElement(getFile8Bit(file));
2466 initial_player_stepsize = (getFile8Bit(file) == 1 ? STEPSIZE_FAST :
2469 for (i = 0; i < MAX_PLAYERS; i++)
2470 level->initial_player_stepsize[i] = initial_player_stepsize;
2472 initial_player_gravity = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2474 for (i = 0; i < MAX_PLAYERS; i++)
2475 level->initial_player_gravity[i] = initial_player_gravity;
2477 level->encoding_16bit_field = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2478 level->em_slippery_gems = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2480 level->use_custom_template = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2482 level->block_last_field = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2483 level->sp_block_last_field = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2484 level->can_move_into_acid_bits = getFile32BitBE(file);
2485 level->dont_collide_with_bits = getFile8Bit(file);
2487 level->use_spring_bug = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2488 level->use_step_counter = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2490 level->instant_relocation = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2491 level->can_pass_to_walkable = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2492 level->grow_into_diggable = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2494 level->game_engine_type = getFile8Bit(file);
2496 ReadUnusedBytesFromFile(file, LEVEL_CHUNK_HEAD_UNUSED);
2501 static int LoadLevel_NAME(File *file, int chunk_size, struct LevelInfo *level)
2505 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
2506 level->name[i] = getFile8Bit(file);
2507 level->name[MAX_LEVEL_NAME_LEN] = 0;
2512 static int LoadLevel_AUTH(File *file, int chunk_size, struct LevelInfo *level)
2516 for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
2517 level->author[i] = getFile8Bit(file);
2518 level->author[MAX_LEVEL_AUTHOR_LEN] = 0;
2523 static int LoadLevel_BODY(File *file, int chunk_size, struct LevelInfo *level)
2526 int chunk_size_expected = level->fieldx * level->fieldy;
2528 /* Note: "chunk_size" was wrong before version 2.0 when elements are
2529 stored with 16-bit encoding (and should be twice as big then).
2530 Even worse, playfield data was stored 16-bit when only yamyam content
2531 contained 16-bit elements and vice versa. */
2533 if (level->encoding_16bit_field && level->file_version >= FILE_VERSION_2_0)
2534 chunk_size_expected *= 2;
2536 if (chunk_size_expected != chunk_size)
2538 ReadUnusedBytesFromFile(file, chunk_size);
2539 return chunk_size_expected;
2542 for (y = 0; y < level->fieldy; y++)
2543 for (x = 0; x < level->fieldx; x++)
2544 level->field[x][y] =
2545 getMappedElement(level->encoding_16bit_field ? getFile16BitBE(file) :
2550 static int LoadLevel_CONT(File *file, int chunk_size, struct LevelInfo *level)
2553 int header_size = 4;
2554 int content_size = MAX_ELEMENT_CONTENTS * 3 * 3;
2555 int chunk_size_expected = header_size + content_size;
2557 /* Note: "chunk_size" was wrong before version 2.0 when elements are
2558 stored with 16-bit encoding (and should be twice as big then).
2559 Even worse, playfield data was stored 16-bit when only yamyam content
2560 contained 16-bit elements and vice versa. */
2562 if (level->encoding_16bit_field && level->file_version >= FILE_VERSION_2_0)
2563 chunk_size_expected += content_size;
2565 if (chunk_size_expected != chunk_size)
2567 ReadUnusedBytesFromFile(file, chunk_size);
2568 return chunk_size_expected;
2572 level->num_yamyam_contents = getFile8Bit(file);
2576 // correct invalid number of content fields -- should never happen
2577 if (level->num_yamyam_contents < 1 ||
2578 level->num_yamyam_contents > MAX_ELEMENT_CONTENTS)
2579 level->num_yamyam_contents = STD_ELEMENT_CONTENTS;
2581 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
2582 for (y = 0; y < 3; y++)
2583 for (x = 0; x < 3; x++)
2584 level->yamyam_content[i].e[x][y] =
2585 getMappedElement(level->encoding_16bit_field ?
2586 getFile16BitBE(file) : getFile8Bit(file));
2590 static int LoadLevel_CNT2(File *file, int chunk_size, struct LevelInfo *level)
2595 int content_array[MAX_ELEMENT_CONTENTS][3][3];
2597 element = getMappedElement(getFile16BitBE(file));
2598 num_contents = getFile8Bit(file);
2600 getFile8Bit(file); // content x size (unused)
2601 getFile8Bit(file); // content y size (unused)
2603 ReadUnusedBytesFromFile(file, LEVEL_CHUNK_CNT2_UNUSED);
2605 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
2606 for (y = 0; y < 3; y++)
2607 for (x = 0; x < 3; x++)
2608 content_array[i][x][y] = getMappedElement(getFile16BitBE(file));
2610 // correct invalid number of content fields -- should never happen
2611 if (num_contents < 1 || num_contents > MAX_ELEMENT_CONTENTS)
2612 num_contents = STD_ELEMENT_CONTENTS;
2614 if (element == EL_YAMYAM)
2616 level->num_yamyam_contents = num_contents;
2618 for (i = 0; i < num_contents; i++)
2619 for (y = 0; y < 3; y++)
2620 for (x = 0; x < 3; x++)
2621 level->yamyam_content[i].e[x][y] = content_array[i][x][y];
2623 else if (element == EL_BD_AMOEBA)
2625 level->amoeba_content = content_array[0][0][0];
2629 Error(ERR_WARN, "cannot load content for element '%d'", element);
2635 static int LoadLevel_CNT3(File *file, int chunk_size, struct LevelInfo *level)
2641 int chunk_size_expected;
2643 element = getMappedElement(getFile16BitBE(file));
2644 if (!IS_ENVELOPE(element))
2645 element = EL_ENVELOPE_1;
2647 envelope_nr = element - EL_ENVELOPE_1;
2649 envelope_len = getFile16BitBE(file);
2651 level->envelope[envelope_nr].xsize = getFile8Bit(file);
2652 level->envelope[envelope_nr].ysize = getFile8Bit(file);
2654 ReadUnusedBytesFromFile(file, LEVEL_CHUNK_CNT3_UNUSED);
2656 chunk_size_expected = LEVEL_CHUNK_CNT3_SIZE(envelope_len);
2657 if (chunk_size_expected != chunk_size)
2659 ReadUnusedBytesFromFile(file, chunk_size - LEVEL_CHUNK_CNT3_HEADER);
2660 return chunk_size_expected;
2663 for (i = 0; i < envelope_len; i++)
2664 level->envelope[envelope_nr].text[i] = getFile8Bit(file);
2669 static int LoadLevel_CUS1(File *file, int chunk_size, struct LevelInfo *level)
2671 int num_changed_custom_elements = getFile16BitBE(file);
2672 int chunk_size_expected = 2 + num_changed_custom_elements * 6;
2675 if (chunk_size_expected != chunk_size)
2677 ReadUnusedBytesFromFile(file, chunk_size - 2);
2678 return chunk_size_expected;
2681 for (i = 0; i < num_changed_custom_elements; i++)
2683 int element = getMappedElement(getFile16BitBE(file));
2684 int properties = getFile32BitBE(file);
2686 if (IS_CUSTOM_ELEMENT(element))
2687 element_info[element].properties[EP_BITFIELD_BASE_NR] = properties;
2689 Error(ERR_WARN, "invalid custom element number %d", element);
2691 // older game versions that wrote level files with CUS1 chunks used
2692 // different default push delay values (not yet stored in level file)
2693 element_info[element].push_delay_fixed = 2;
2694 element_info[element].push_delay_random = 8;
2697 level->file_has_custom_elements = TRUE;
2702 static int LoadLevel_CUS2(File *file, int chunk_size, struct LevelInfo *level)
2704 int num_changed_custom_elements = getFile16BitBE(file);
2705 int chunk_size_expected = 2 + num_changed_custom_elements * 4;
2708 if (chunk_size_expected != chunk_size)
2710 ReadUnusedBytesFromFile(file, chunk_size - 2);
2711 return chunk_size_expected;
2714 for (i = 0; i < num_changed_custom_elements; i++)
2716 int element = getMappedElement(getFile16BitBE(file));
2717 int custom_target_element = getMappedElement(getFile16BitBE(file));
2719 if (IS_CUSTOM_ELEMENT(element))
2720 element_info[element].change->target_element = custom_target_element;
2722 Error(ERR_WARN, "invalid custom element number %d", element);
2725 level->file_has_custom_elements = TRUE;
2730 static int LoadLevel_CUS3(File *file, int chunk_size, struct LevelInfo *level)
2732 int num_changed_custom_elements = getFile16BitBE(file);
2733 int chunk_size_expected = LEVEL_CHUNK_CUS3_SIZE(num_changed_custom_elements);
2736 if (chunk_size_expected != chunk_size)
2738 ReadUnusedBytesFromFile(file, chunk_size - 2);
2739 return chunk_size_expected;
2742 for (i = 0; i < num_changed_custom_elements; i++)
2744 int element = getMappedElement(getFile16BitBE(file));
2745 struct ElementInfo *ei = &element_info[element];
2746 unsigned int event_bits;
2748 if (!IS_CUSTOM_ELEMENT(element))
2750 Error(ERR_WARN, "invalid custom element number %d", element);
2752 element = EL_INTERNAL_DUMMY;
2755 for (j = 0; j < MAX_ELEMENT_NAME_LEN; j++)
2756 ei->description[j] = getFile8Bit(file);
2757 ei->description[MAX_ELEMENT_NAME_LEN] = 0;
2759 ei->properties[EP_BITFIELD_BASE_NR] = getFile32BitBE(file);
2761 // some free bytes for future properties and padding
2762 ReadUnusedBytesFromFile(file, 7);
2764 ei->use_gfx_element = getFile8Bit(file);
2765 ei->gfx_element_initial = getMappedElement(getFile16BitBE(file));
2767 ei->collect_score_initial = getFile8Bit(file);
2768 ei->collect_count_initial = getFile8Bit(file);
2770 ei->push_delay_fixed = getFile16BitBE(file);
2771 ei->push_delay_random = getFile16BitBE(file);
2772 ei->move_delay_fixed = getFile16BitBE(file);
2773 ei->move_delay_random = getFile16BitBE(file);
2775 ei->move_pattern = getFile16BitBE(file);
2776 ei->move_direction_initial = getFile8Bit(file);
2777 ei->move_stepsize = getFile8Bit(file);
2779 for (y = 0; y < 3; y++)
2780 for (x = 0; x < 3; x++)
2781 ei->content.e[x][y] = getMappedElement(getFile16BitBE(file));
2783 event_bits = getFile32BitBE(file);
2784 for (j = 0; j < NUM_CHANGE_EVENTS; j++)
2785 if (event_bits & (1 << j))
2786 ei->change->has_event[j] = TRUE;
2788 ei->change->target_element = getMappedElement(getFile16BitBE(file));
2790 ei->change->delay_fixed = getFile16BitBE(file);
2791 ei->change->delay_random = getFile16BitBE(file);
2792 ei->change->delay_frames = getFile16BitBE(file);
2794 ei->change->initial_trigger_element= getMappedElement(getFile16BitBE(file));
2796 ei->change->explode = getFile8Bit(file);
2797 ei->change->use_target_content = getFile8Bit(file);
2798 ei->change->only_if_complete = getFile8Bit(file);
2799 ei->change->use_random_replace = getFile8Bit(file);
2801 ei->change->random_percentage = getFile8Bit(file);
2802 ei->change->replace_when = getFile8Bit(file);
2804 for (y = 0; y < 3; y++)
2805 for (x = 0; x < 3; x++)
2806 ei->change->target_content.e[x][y] =
2807 getMappedElement(getFile16BitBE(file));
2809 ei->slippery_type = getFile8Bit(file);
2811 // some free bytes for future properties and padding
2812 ReadUnusedBytesFromFile(file, LEVEL_CPART_CUS3_UNUSED);
2814 // mark that this custom element has been modified
2815 ei->modified_settings = TRUE;
2818 level->file_has_custom_elements = TRUE;
2823 static int LoadLevel_CUS4(File *file, int chunk_size, struct LevelInfo *level)
2825 struct ElementInfo *ei;
2826 int chunk_size_expected;
2830 // ---------- custom element base property values (96 bytes) ----------------
2832 element = getMappedElement(getFile16BitBE(file));
2834 if (!IS_CUSTOM_ELEMENT(element))
2836 Error(ERR_WARN, "invalid custom element number %d", element);
2838 ReadUnusedBytesFromFile(file, chunk_size - 2);
2842 ei = &element_info[element];
2844 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
2845 ei->description[i] = getFile8Bit(file);
2846 ei->description[MAX_ELEMENT_NAME_LEN] = 0;
2848 ei->properties[EP_BITFIELD_BASE_NR] = getFile32BitBE(file);
2850 ReadUnusedBytesFromFile(file, 4); // reserved for more base properties
2852 ei->num_change_pages = getFile8Bit(file);
2854 chunk_size_expected = LEVEL_CHUNK_CUS4_SIZE(ei->num_change_pages);
2855 if (chunk_size_expected != chunk_size)
2857 ReadUnusedBytesFromFile(file, chunk_size - 43);
2858 return chunk_size_expected;
2861 ei->ce_value_fixed_initial = getFile16BitBE(file);
2862 ei->ce_value_random_initial = getFile16BitBE(file);
2863 ei->use_last_ce_value = getFile8Bit(file);
2865 ei->use_gfx_element = getFile8Bit(file);
2866 ei->gfx_element_initial = getMappedElement(getFile16BitBE(file));
2868 ei->collect_score_initial = getFile8Bit(file);
2869 ei->collect_count_initial = getFile8Bit(file);
2871 ei->drop_delay_fixed = getFile8Bit(file);
2872 ei->push_delay_fixed = getFile8Bit(file);
2873 ei->drop_delay_random = getFile8Bit(file);
2874 ei->push_delay_random = getFile8Bit(file);
2875 ei->move_delay_fixed = getFile16BitBE(file);
2876 ei->move_delay_random = getFile16BitBE(file);
2878 // bits 0 - 15 of "move_pattern" ...
2879 ei->move_pattern = getFile16BitBE(file);
2880 ei->move_direction_initial = getFile8Bit(file);
2881 ei->move_stepsize = getFile8Bit(file);
2883 ei->slippery_type = getFile8Bit(file);
2885 for (y = 0; y < 3; y++)
2886 for (x = 0; x < 3; x++)
2887 ei->content.e[x][y] = getMappedElement(getFile16BitBE(file));
2889 ei->move_enter_element = getMappedElement(getFile16BitBE(file));
2890 ei->move_leave_element = getMappedElement(getFile16BitBE(file));
2891 ei->move_leave_type = getFile8Bit(file);
2893 // ... bits 16 - 31 of "move_pattern" (not nice, but downward compatible)
2894 ei->move_pattern |= (getFile16BitBE(file) << 16);
2896 ei->access_direction = getFile8Bit(file);
2898 ei->explosion_delay = getFile8Bit(file);
2899 ei->ignition_delay = getFile8Bit(file);
2900 ei->explosion_type = getFile8Bit(file);
2902 // some free bytes for future custom property values and padding
2903 ReadUnusedBytesFromFile(file, 1);
2905 // ---------- change page property values (48 bytes) ------------------------
2907 setElementChangePages(ei, ei->num_change_pages);
2909 for (i = 0; i < ei->num_change_pages; i++)
2911 struct ElementChangeInfo *change = &ei->change_page[i];
2912 unsigned int event_bits;
2914 // always start with reliable default values
2915 setElementChangeInfoToDefaults(change);
2917 // bits 0 - 31 of "has_event[]" ...
2918 event_bits = getFile32BitBE(file);
2919 for (j = 0; j < MIN(NUM_CHANGE_EVENTS, 32); j++)
2920 if (event_bits & (1 << j))
2921 change->has_event[j] = TRUE;
2923 change->target_element = getMappedElement(getFile16BitBE(file));
2925 change->delay_fixed = getFile16BitBE(file);
2926 change->delay_random = getFile16BitBE(file);
2927 change->delay_frames = getFile16BitBE(file);
2929 change->initial_trigger_element = getMappedElement(getFile16BitBE(file));
2931 change->explode = getFile8Bit(file);
2932 change->use_target_content = getFile8Bit(file);
2933 change->only_if_complete = getFile8Bit(file);
2934 change->use_random_replace = getFile8Bit(file);
2936 change->random_percentage = getFile8Bit(file);
2937 change->replace_when = getFile8Bit(file);
2939 for (y = 0; y < 3; y++)
2940 for (x = 0; x < 3; x++)
2941 change->target_content.e[x][y]= getMappedElement(getFile16BitBE(file));
2943 change->can_change = getFile8Bit(file);
2945 change->trigger_side = getFile8Bit(file);
2947 change->trigger_player = getFile8Bit(file);
2948 change->trigger_page = getFile8Bit(file);
2950 change->trigger_page = (change->trigger_page == CH_PAGE_ANY_FILE ?
2951 CH_PAGE_ANY : (1 << change->trigger_page));
2953 change->has_action = getFile8Bit(file);
2954 change->action_type = getFile8Bit(file);
2955 change->action_mode = getFile8Bit(file);
2956 change->action_arg = getFile16BitBE(file);
2958 // ... bits 32 - 39 of "has_event[]" (not nice, but downward compatible)
2959 event_bits = getFile8Bit(file);
2960 for (j = 32; j < NUM_CHANGE_EVENTS; j++)
2961 if (event_bits & (1 << (j - 32)))
2962 change->has_event[j] = TRUE;
2965 // mark this custom element as modified
2966 ei->modified_settings = TRUE;
2968 level->file_has_custom_elements = TRUE;
2973 static int LoadLevel_GRP1(File *file, int chunk_size, struct LevelInfo *level)
2975 struct ElementInfo *ei;
2976 struct ElementGroupInfo *group;
2980 element = getMappedElement(getFile16BitBE(file));
2982 if (!IS_GROUP_ELEMENT(element))
2984 Error(ERR_WARN, "invalid group element number %d", element);
2986 ReadUnusedBytesFromFile(file, chunk_size - 2);
2990 ei = &element_info[element];
2992 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
2993 ei->description[i] = getFile8Bit(file);
2994 ei->description[MAX_ELEMENT_NAME_LEN] = 0;
2996 group = element_info[element].group;
2998 group->num_elements = getFile8Bit(file);
3000 ei->use_gfx_element = getFile8Bit(file);
3001 ei->gfx_element_initial = getMappedElement(getFile16BitBE(file));
3003 group->choice_mode = getFile8Bit(file);
3005 // some free bytes for future values and padding
3006 ReadUnusedBytesFromFile(file, 3);
3008 for (i = 0; i < MAX_ELEMENTS_IN_GROUP; i++)
3009 group->element[i] = getMappedElement(getFile16BitBE(file));
3011 // mark this group element as modified
3012 element_info[element].modified_settings = TRUE;
3014 level->file_has_custom_elements = TRUE;
3019 static int LoadLevel_MicroChunk(File *file, struct LevelFileConfigInfo *conf,
3020 int element, int real_element)
3022 int micro_chunk_size = 0;
3023 int conf_type = getFile8Bit(file);
3024 int byte_mask = conf_type & CONF_MASK_BYTES;
3025 boolean element_found = FALSE;
3028 micro_chunk_size += 1;
3030 if (byte_mask == CONF_MASK_MULTI_BYTES)
3032 int num_bytes = getFile16BitBE(file);
3033 byte *buffer = checked_malloc(num_bytes);
3035 ReadBytesFromFile(file, buffer, num_bytes);
3037 for (i = 0; conf[i].data_type != -1; i++)
3039 if (conf[i].element == element &&
3040 conf[i].conf_type == conf_type)
3042 int data_type = conf[i].data_type;
3043 int num_entities = num_bytes / CONF_ENTITY_NUM_BYTES(data_type);
3044 int max_num_entities = conf[i].max_num_entities;
3046 if (num_entities > max_num_entities)
3049 "truncating number of entities for element %d from %d to %d",
3050 element, num_entities, max_num_entities);
3052 num_entities = max_num_entities;
3055 if (num_entities == 0 && (data_type == TYPE_ELEMENT_LIST ||
3056 data_type == TYPE_CONTENT_LIST))
3058 // for element and content lists, zero entities are not allowed
3059 Error(ERR_WARN, "found empty list of entities for element %d",
3062 // do not set "num_entities" here to prevent reading behind buffer
3064 *(int *)(conf[i].num_entities) = 1; // at least one is required
3068 *(int *)(conf[i].num_entities) = num_entities;
3071 element_found = TRUE;
3073 if (data_type == TYPE_STRING)
3075 char *string = (char *)(conf[i].value);
3078 for (j = 0; j < max_num_entities; j++)
3079 string[j] = (j < num_entities ? buffer[j] : '\0');
3081 else if (data_type == TYPE_ELEMENT_LIST)
3083 int *element_array = (int *)(conf[i].value);
3086 for (j = 0; j < num_entities; j++)
3088 getMappedElement(CONF_ELEMENTS_ELEMENT(buffer, j));
3090 else if (data_type == TYPE_CONTENT_LIST)
3092 struct Content *content= (struct Content *)(conf[i].value);
3095 for (c = 0; c < num_entities; c++)
3096 for (y = 0; y < 3; y++)
3097 for (x = 0; x < 3; x++)
3098 content[c].e[x][y] =
3099 getMappedElement(CONF_CONTENTS_ELEMENT(buffer, c, x, y));
3102 element_found = FALSE;
3108 checked_free(buffer);
3110 micro_chunk_size += 2 + num_bytes;
3112 else // constant size configuration data (1, 2 or 4 bytes)
3114 int value = (byte_mask == CONF_MASK_1_BYTE ? getFile8Bit (file) :
3115 byte_mask == CONF_MASK_2_BYTE ? getFile16BitBE(file) :
3116 byte_mask == CONF_MASK_4_BYTE ? getFile32BitBE(file) : 0);
3118 for (i = 0; conf[i].data_type != -1; i++)
3120 if (conf[i].element == element &&
3121 conf[i].conf_type == conf_type)
3123 int data_type = conf[i].data_type;
3125 if (data_type == TYPE_ELEMENT)
3126 value = getMappedElement(value);
3128 if (data_type == TYPE_BOOLEAN)
3129 *(boolean *)(conf[i].value) = value;
3131 *(int *) (conf[i].value) = value;
3133 element_found = TRUE;
3139 micro_chunk_size += CONF_VALUE_NUM_BYTES(byte_mask);
3144 char *error_conf_chunk_bytes =
3145 (byte_mask == CONF_MASK_1_BYTE ? "CONF_VALUE_8_BIT" :
3146 byte_mask == CONF_MASK_2_BYTE ? "CONF_VALUE_16_BIT" :
3147 byte_mask == CONF_MASK_4_BYTE ? "CONF_VALUE_32_BIT" :"CONF_VALUE_BYTES");
3148 int error_conf_chunk_token = conf_type & CONF_MASK_TOKEN;
3149 int error_element = real_element;
3151 Error(ERR_WARN, "cannot load micro chunk '%s(%d)' value for element %d ['%s']",
3152 error_conf_chunk_bytes, error_conf_chunk_token,
3153 error_element, EL_NAME(error_element));
3156 return micro_chunk_size;
3159 static int LoadLevel_INFO(File *file, int chunk_size, struct LevelInfo *level)
3161 int real_chunk_size = 0;
3163 li = *level; // copy level data into temporary buffer
3165 while (!checkEndOfFile(file))
3167 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_INFO, -1, -1);
3169 if (real_chunk_size >= chunk_size)
3173 *level = li; // copy temporary buffer back to level data
3175 return real_chunk_size;
3178 static int LoadLevel_CONF(File *file, int chunk_size, struct LevelInfo *level)
3180 int real_chunk_size = 0;
3182 li = *level; // copy level data into temporary buffer
3184 while (!checkEndOfFile(file))
3186 int element = getMappedElement(getFile16BitBE(file));
3188 real_chunk_size += 2;
3189 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_CONF,
3191 if (real_chunk_size >= chunk_size)
3195 *level = li; // copy temporary buffer back to level data
3197 return real_chunk_size;
3200 static int LoadLevel_ELEM(File *file, int chunk_size, struct LevelInfo *level)
3202 int real_chunk_size = 0;
3204 li = *level; // copy level data into temporary buffer
3206 while (!checkEndOfFile(file))
3208 int element = getMappedElement(getFile16BitBE(file));
3210 real_chunk_size += 2;
3211 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_ELEM,
3213 if (real_chunk_size >= chunk_size)
3217 *level = li; // copy temporary buffer back to level data
3219 return real_chunk_size;
3222 static int LoadLevel_NOTE(File *file, int chunk_size, struct LevelInfo *level)
3224 int element = getMappedElement(getFile16BitBE(file));
3225 int envelope_nr = element - EL_ENVELOPE_1;
3226 int real_chunk_size = 2;
3228 xx_envelope = level->envelope[envelope_nr]; // copy into temporary buffer
3230 while (!checkEndOfFile(file))
3232 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_NOTE,
3235 if (real_chunk_size >= chunk_size)
3239 level->envelope[envelope_nr] = xx_envelope; // copy from temporary buffer
3241 return real_chunk_size;
3244 static int LoadLevel_CUSX(File *file, int chunk_size, struct LevelInfo *level)
3246 int element = getMappedElement(getFile16BitBE(file));
3247 int real_chunk_size = 2;
3248 struct ElementInfo *ei = &element_info[element];
3251 xx_ei = *ei; // copy element data into temporary buffer
3253 xx_ei.num_change_pages = -1;
3255 while (!checkEndOfFile(file))
3257 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_CUSX_base,
3259 if (xx_ei.num_change_pages != -1)
3262 if (real_chunk_size >= chunk_size)
3268 if (ei->num_change_pages == -1)
3270 Error(ERR_WARN, "LoadLevel_CUSX(): missing 'num_change_pages' for '%s'",
3273 ei->num_change_pages = 1;
3275 setElementChangePages(ei, 1);
3276 setElementChangeInfoToDefaults(ei->change);
3278 return real_chunk_size;
3281 // initialize number of change pages stored for this custom element
3282 setElementChangePages(ei, ei->num_change_pages);
3283 for (i = 0; i < ei->num_change_pages; i++)
3284 setElementChangeInfoToDefaults(&ei->change_page[i]);
3286 // start with reading properties for the first change page
3287 xx_current_change_page = 0;
3289 while (!checkEndOfFile(file))
3291 struct ElementChangeInfo *change = &ei->change_page[xx_current_change_page];
3293 xx_change = *change; // copy change data into temporary buffer
3295 resetEventBits(); // reset bits; change page might have changed
3297 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_CUSX_change,
3300 *change = xx_change;
3302 setEventFlagsFromEventBits(change);
3304 if (real_chunk_size >= chunk_size)
3308 level->file_has_custom_elements = TRUE;
3310 return real_chunk_size;
3313 static int LoadLevel_GRPX(File *file, int chunk_size, struct LevelInfo *level)
3315 int element = getMappedElement(getFile16BitBE(file));
3316 int real_chunk_size = 2;
3317 struct ElementInfo *ei = &element_info[element];
3318 struct ElementGroupInfo *group = ei->group;
3320 xx_ei = *ei; // copy element data into temporary buffer
3321 xx_group = *group; // copy group data into temporary buffer
3323 while (!checkEndOfFile(file))
3325 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_GRPX,
3328 if (real_chunk_size >= chunk_size)
3335 level->file_has_custom_elements = TRUE;
3337 return real_chunk_size;
3340 static void LoadLevelFromFileInfo_RND(struct LevelInfo *level,
3341 struct LevelFileInfo *level_file_info,
3342 boolean level_info_only)
3344 char *filename = level_file_info->filename;
3345 char cookie[MAX_LINE_LEN];
3346 char chunk_name[CHUNK_ID_LEN + 1];
3350 if (!(file = openFile(filename, MODE_READ)))
3352 level->no_valid_file = TRUE;
3353 level->no_level_file = TRUE;
3355 if (level_info_only)
3358 Error(ERR_WARN, "cannot read level '%s' -- using empty level", filename);
3360 if (!setup.editor.use_template_for_new_levels)
3363 // if level file not found, try to initialize level data from template
3364 filename = getGlobalLevelTemplateFilename();
3366 if (!(file = openFile(filename, MODE_READ)))
3369 // default: for empty levels, use level template for custom elements
3370 level->use_custom_template = TRUE;
3372 level->no_valid_file = FALSE;
3375 getFileChunkBE(file, chunk_name, NULL);
3376 if (strEqual(chunk_name, "RND1"))
3378 getFile32BitBE(file); // not used
3380 getFileChunkBE(file, chunk_name, NULL);
3381 if (!strEqual(chunk_name, "CAVE"))
3383 level->no_valid_file = TRUE;
3385 Error(ERR_WARN, "unknown format of level file '%s'", filename);
3392 else // check for pre-2.0 file format with cookie string
3394 strcpy(cookie, chunk_name);
3395 if (getStringFromFile(file, &cookie[4], MAX_LINE_LEN - 4) == NULL)
3397 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
3398 cookie[strlen(cookie) - 1] = '\0';
3400 if (!checkCookieString(cookie, LEVEL_COOKIE_TMPL))
3402 level->no_valid_file = TRUE;
3404 Error(ERR_WARN, "unknown format of level file '%s'", filename);
3411 if ((level->file_version = getFileVersionFromCookieString(cookie)) == -1)
3413 level->no_valid_file = TRUE;
3415 Error(ERR_WARN, "unsupported version of level file '%s'", filename);
3422 // pre-2.0 level files have no game version, so use file version here
3423 level->game_version = level->file_version;
3426 if (level->file_version < FILE_VERSION_1_2)
3428 // level files from versions before 1.2.0 without chunk structure
3429 LoadLevel_HEAD(file, LEVEL_CHUNK_HEAD_SIZE, level);
3430 LoadLevel_BODY(file, level->fieldx * level->fieldy, level);
3438 int (*loader)(File *, int, struct LevelInfo *);
3442 { "VERS", LEVEL_CHUNK_VERS_SIZE, LoadLevel_VERS },
3443 { "DATE", LEVEL_CHUNK_DATE_SIZE, LoadLevel_DATE },
3444 { "HEAD", LEVEL_CHUNK_HEAD_SIZE, LoadLevel_HEAD },
3445 { "NAME", LEVEL_CHUNK_NAME_SIZE, LoadLevel_NAME },
3446 { "AUTH", LEVEL_CHUNK_AUTH_SIZE, LoadLevel_AUTH },
3447 { "INFO", -1, LoadLevel_INFO },
3448 { "BODY", -1, LoadLevel_BODY },
3449 { "CONT", -1, LoadLevel_CONT },
3450 { "CNT2", LEVEL_CHUNK_CNT2_SIZE, LoadLevel_CNT2 },
3451 { "CNT3", -1, LoadLevel_CNT3 },
3452 { "CUS1", -1, LoadLevel_CUS1 },
3453 { "CUS2", -1, LoadLevel_CUS2 },
3454 { "CUS3", -1, LoadLevel_CUS3 },
3455 { "CUS4", -1, LoadLevel_CUS4 },
3456 { "GRP1", -1, LoadLevel_GRP1 },
3457 { "CONF", -1, LoadLevel_CONF },
3458 { "ELEM", -1, LoadLevel_ELEM },
3459 { "NOTE", -1, LoadLevel_NOTE },
3460 { "CUSX", -1, LoadLevel_CUSX },
3461 { "GRPX", -1, LoadLevel_GRPX },
3466 while (getFileChunkBE(file, chunk_name, &chunk_size))
3470 while (chunk_info[i].name != NULL &&
3471 !strEqual(chunk_name, chunk_info[i].name))
3474 if (chunk_info[i].name == NULL)
3476 Error(ERR_WARN, "unknown chunk '%s' in level file '%s'",
3477 chunk_name, filename);
3478 ReadUnusedBytesFromFile(file, chunk_size);
3480 else if (chunk_info[i].size != -1 &&
3481 chunk_info[i].size != chunk_size)
3483 Error(ERR_WARN, "wrong size (%d) of chunk '%s' in level file '%s'",
3484 chunk_size, chunk_name, filename);
3485 ReadUnusedBytesFromFile(file, chunk_size);
3489 // call function to load this level chunk
3490 int chunk_size_expected =
3491 (chunk_info[i].loader)(file, chunk_size, level);
3493 // the size of some chunks cannot be checked before reading other
3494 // chunks first (like "HEAD" and "BODY") that contain some header
3495 // information, so check them here
3496 if (chunk_size_expected != chunk_size)
3498 Error(ERR_WARN, "wrong size (%d) of chunk '%s' in level file '%s'",
3499 chunk_size, chunk_name, filename);
3509 // ----------------------------------------------------------------------------
3510 // functions for loading EM level
3511 // ----------------------------------------------------------------------------
3513 static void CopyNativeLevel_RND_to_EM(struct LevelInfo *level)
3515 static int ball_xy[8][2] =
3526 struct LevelInfo_EM *level_em = level->native_em_level;
3527 struct CAVE *cav = level_em->cav;
3530 cav->width = MIN(level->fieldx, MAX_PLAYFIELD_WIDTH);
3531 cav->height = MIN(level->fieldy, MAX_PLAYFIELD_HEIGHT);
3533 cav->time_seconds = level->time;
3534 cav->gems_needed = level->gems_needed;
3536 cav->emerald_score = level->score[SC_EMERALD];
3537 cav->diamond_score = level->score[SC_DIAMOND];
3538 cav->alien_score = level->score[SC_ROBOT];
3539 cav->tank_score = level->score[SC_SPACESHIP];
3540 cav->bug_score = level->score[SC_BUG];
3541 cav->eater_score = level->score[SC_YAMYAM];
3542 cav->nut_score = level->score[SC_NUT];
3543 cav->dynamite_score = level->score[SC_DYNAMITE];
3544 cav->key_score = level->score[SC_KEY];
3545 cav->exit_score = level->score[SC_TIME_BONUS];
3547 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
3548 for (y = 0; y < 3; y++)
3549 for (x = 0; x < 3; x++)
3550 cav->eater_array[i][y * 3 + x] =
3551 map_element_RND_to_EM_cave(level->yamyam_content[i].e[x][y]);
3553 cav->amoeba_time = level->amoeba_speed;
3554 cav->wonderwall_time = level->time_magic_wall;
3555 cav->wheel_time = level->time_wheel;
3557 cav->android_move_time = level->android_move_time;
3558 cav->android_clone_time = level->android_clone_time;
3559 cav->ball_random = level->ball_random;
3560 cav->ball_active = level->ball_active_initial;
3561 cav->ball_time = level->ball_time;
3562 cav->num_ball_arrays = level->num_ball_contents;
3564 cav->lenses_score = level->lenses_score;
3565 cav->magnify_score = level->magnify_score;
3566 cav->slurp_score = level->slurp_score;
3568 cav->lenses_time = level->lenses_time;
3569 cav->magnify_time = level->magnify_time;
3571 cav->wind_direction =
3572 map_direction_RND_to_EM(level->wind_direction_initial);
3574 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
3575 for (j = 0; j < 8; j++)
3576 cav->ball_array[i][j] =
3577 map_element_RND_to_EM_cave(level->ball_content[i].
3578 e[ball_xy[j][0]][ball_xy[j][1]]);
3580 map_android_clone_elements_RND_to_EM(level);
3582 // first fill the complete playfield with the empty space element
3583 for (y = 0; y < EM_MAX_CAVE_HEIGHT; y++)
3584 for (x = 0; x < EM_MAX_CAVE_WIDTH; x++)
3585 cav->cave[x][y] = Cblank;
3587 // then copy the real level contents from level file into the playfield
3588 for (y = 0; y < cav->height; y++) for (x = 0; x < cav->width; x++)
3590 int new_element = map_element_RND_to_EM_cave(level->field[x][y]);
3592 if (level->field[x][y] == EL_AMOEBA_DEAD)
3593 new_element = map_element_RND_to_EM_cave(EL_AMOEBA_WET);
3595 cav->cave[x][y] = new_element;
3598 for (i = 0; i < MAX_PLAYERS; i++)
3600 cav->player_x[i] = -1;
3601 cav->player_y[i] = -1;
3604 // initialize player positions and delete players from the playfield
3605 for (y = 0; y < cav->height; y++) for (x = 0; x < cav->width; x++)
3607 if (ELEM_IS_PLAYER(level->field[x][y]))
3609 int player_nr = GET_PLAYER_NR(level->field[x][y]);
3611 cav->player_x[player_nr] = x;
3612 cav->player_y[player_nr] = y;
3614 cav->cave[x][y] = map_element_RND_to_EM_cave(EL_EMPTY);
3619 static void CopyNativeLevel_EM_to_RND(struct LevelInfo *level)
3621 static int ball_xy[8][2] =
3632 struct LevelInfo_EM *level_em = level->native_em_level;
3633 struct CAVE *cav = level_em->cav;
3636 level->fieldx = MIN(cav->width, MAX_LEV_FIELDX);
3637 level->fieldy = MIN(cav->height, MAX_LEV_FIELDY);
3639 level->time = cav->time_seconds;
3640 level->gems_needed = cav->gems_needed;
3642 sprintf(level->name, "Level %d", level->file_info.nr);
3644 level->score[SC_EMERALD] = cav->emerald_score;
3645 level->score[SC_DIAMOND] = cav->diamond_score;
3646 level->score[SC_ROBOT] = cav->alien_score;
3647 level->score[SC_SPACESHIP] = cav->tank_score;
3648 level->score[SC_BUG] = cav->bug_score;
3649 level->score[SC_YAMYAM] = cav->eater_score;
3650 level->score[SC_NUT] = cav->nut_score;
3651 level->score[SC_DYNAMITE] = cav->dynamite_score;
3652 level->score[SC_KEY] = cav->key_score;
3653 level->score[SC_TIME_BONUS] = cav->exit_score;
3655 level->num_yamyam_contents = MAX_ELEMENT_CONTENTS;
3657 for (i = 0; i < level->num_yamyam_contents; i++)
3658 for (y = 0; y < 3; y++)
3659 for (x = 0; x < 3; x++)
3660 level->yamyam_content[i].e[x][y] =
3661 map_element_EM_to_RND_cave(cav->eater_array[i][y * 3 + x]);
3663 level->amoeba_speed = cav->amoeba_time;
3664 level->time_magic_wall = cav->wonderwall_time;
3665 level->time_wheel = cav->wheel_time;
3667 level->android_move_time = cav->android_move_time;
3668 level->android_clone_time = cav->android_clone_time;
3669 level->ball_random = cav->ball_random;
3670 level->ball_active_initial = cav->ball_active;
3671 level->ball_time = cav->ball_time;
3672 level->num_ball_contents = cav->num_ball_arrays;
3674 level->lenses_score = cav->lenses_score;
3675 level->magnify_score = cav->magnify_score;
3676 level->slurp_score = cav->slurp_score;
3678 level->lenses_time = cav->lenses_time;
3679 level->magnify_time = cav->magnify_time;
3681 level->wind_direction_initial =
3682 map_direction_EM_to_RND(cav->wind_direction);
3684 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
3685 for (j = 0; j < 8; j++)
3686 level->ball_content[i].e[ball_xy[j][0]][ball_xy[j][1]] =
3687 map_element_EM_to_RND_cave(cav->ball_array[i][j]);
3689 map_android_clone_elements_EM_to_RND(level);
3691 // convert the playfield (some elements need special treatment)
3692 for (y = 0; y < level->fieldy; y++) for (x = 0; x < level->fieldx; x++)
3694 int new_element = map_element_EM_to_RND_cave(cav->cave[x][y]);
3696 if (new_element == EL_AMOEBA_WET && level->amoeba_speed == 0)
3697 new_element = EL_AMOEBA_DEAD;
3699 level->field[x][y] = new_element;
3702 for (i = 0; i < MAX_PLAYERS; i++)
3704 // in case of all players set to the same field, use the first player
3705 int nr = MAX_PLAYERS - i - 1;
3706 int jx = cav->player_x[nr];
3707 int jy = cav->player_y[nr];
3709 if (jx != -1 && jy != -1)
3710 level->field[jx][jy] = EL_PLAYER_1 + nr;
3715 // ----------------------------------------------------------------------------
3716 // functions for loading SP level
3717 // ----------------------------------------------------------------------------
3719 static void CopyNativeLevel_RND_to_SP(struct LevelInfo *level)
3721 struct LevelInfo_SP *level_sp = level->native_sp_level;
3722 LevelInfoType *header = &level_sp->header;
3725 level_sp->width = level->fieldx;
3726 level_sp->height = level->fieldy;
3728 for (x = 0; x < level->fieldx; x++)
3729 for (y = 0; y < level->fieldy; y++)
3730 level_sp->playfield[x][y] = map_element_RND_to_SP(level->field[x][y]);
3732 header->InitialGravity = (level->initial_player_gravity[0] ? 1 : 0);
3734 for (i = 0; i < SP_LEVEL_NAME_LEN; i++)
3735 header->LevelTitle[i] = level->name[i];
3736 // !!! NO STRING TERMINATION IN SUPAPLEX VB CODE YET -- FIX THIS !!!
3738 header->InfotronsNeeded = level->gems_needed;
3740 header->SpecialPortCount = 0;
3742 for (x = 0; x < level->fieldx; x++) for (y = 0; y < level->fieldy; y++)
3744 boolean gravity_port_found = FALSE;
3745 boolean gravity_port_valid = FALSE;
3746 int gravity_port_flag;
3747 int gravity_port_base_element;
3748 int element = level->field[x][y];
3750 if (element >= EL_SP_GRAVITY_ON_PORT_RIGHT &&
3751 element <= EL_SP_GRAVITY_ON_PORT_UP)
3753 gravity_port_found = TRUE;
3754 gravity_port_valid = TRUE;
3755 gravity_port_flag = 1;
3756 gravity_port_base_element = EL_SP_GRAVITY_ON_PORT_RIGHT;
3758 else if (element >= EL_SP_GRAVITY_OFF_PORT_RIGHT &&
3759 element <= EL_SP_GRAVITY_OFF_PORT_UP)
3761 gravity_port_found = TRUE;
3762 gravity_port_valid = TRUE;
3763 gravity_port_flag = 0;
3764 gravity_port_base_element = EL_SP_GRAVITY_OFF_PORT_RIGHT;
3766 else if (element >= EL_SP_GRAVITY_PORT_RIGHT &&
3767 element <= EL_SP_GRAVITY_PORT_UP)
3769 // change R'n'D style gravity inverting special port to normal port
3770 // (there are no gravity inverting ports in native Supaplex engine)
3772 gravity_port_found = TRUE;
3773 gravity_port_valid = FALSE;
3774 gravity_port_base_element = EL_SP_GRAVITY_PORT_RIGHT;
3777 if (gravity_port_found)
3779 if (gravity_port_valid &&
3780 header->SpecialPortCount < SP_MAX_SPECIAL_PORTS)
3782 SpecialPortType *port = &header->SpecialPort[header->SpecialPortCount];
3784 port->PortLocation = (y * level->fieldx + x) * 2;
3785 port->Gravity = gravity_port_flag;
3787 element += EL_SP_GRAVITY_PORT_RIGHT - gravity_port_base_element;
3789 header->SpecialPortCount++;
3793 // change special gravity port to normal port
3795 element += EL_SP_PORT_RIGHT - gravity_port_base_element;
3798 level_sp->playfield[x][y] = element - EL_SP_START;
3803 static void CopyNativeLevel_SP_to_RND(struct LevelInfo *level)
3805 struct LevelInfo_SP *level_sp = level->native_sp_level;
3806 LevelInfoType *header = &level_sp->header;
3807 boolean num_invalid_elements = 0;
3810 level->fieldx = level_sp->width;
3811 level->fieldy = level_sp->height;
3813 for (x = 0; x < level->fieldx; x++)
3815 for (y = 0; y < level->fieldy; y++)
3817 int element_old = level_sp->playfield[x][y];
3818 int element_new = getMappedElement(map_element_SP_to_RND(element_old));
3820 if (element_new == EL_UNKNOWN)
3822 num_invalid_elements++;
3824 Error(ERR_DEBUG, "invalid element %d at position %d, %d",
3828 level->field[x][y] = element_new;
3832 if (num_invalid_elements > 0)
3833 Error(ERR_WARN, "found %d invalid elements%s", num_invalid_elements,
3834 (!options.debug ? " (use '--debug' for more details)" : ""));
3836 for (i = 0; i < MAX_PLAYERS; i++)
3837 level->initial_player_gravity[i] =
3838 (header->InitialGravity == 1 ? TRUE : FALSE);
3840 // skip leading spaces
3841 for (i = 0; i < SP_LEVEL_NAME_LEN; i++)
3842 if (header->LevelTitle[i] != ' ')
3846 for (j = 0; i < SP_LEVEL_NAME_LEN; i++, j++)
3847 level->name[j] = header->LevelTitle[i];
3848 level->name[j] = '\0';
3850 // cut trailing spaces
3852 if (level->name[j - 1] == ' ' && level->name[j] == '\0')
3853 level->name[j - 1] = '\0';
3855 level->gems_needed = header->InfotronsNeeded;
3857 for (i = 0; i < header->SpecialPortCount; i++)
3859 SpecialPortType *port = &header->SpecialPort[i];
3860 int port_location = port->PortLocation;
3861 int gravity = port->Gravity;
3862 int port_x, port_y, port_element;
3864 port_x = (port_location / 2) % level->fieldx;
3865 port_y = (port_location / 2) / level->fieldx;
3867 if (port_x < 0 || port_x >= level->fieldx ||
3868 port_y < 0 || port_y >= level->fieldy)
3870 Error(ERR_WARN, "special port position (%d, %d) out of bounds",
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 Error(ERR_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 Error(ERR_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_PLAYERS] = { tape_action, 0, 0, 0 };
3993 boolean success = 0;
3996 for (j = 0; j < tape_repeat; j++)
3997 success = TapeAddAction(action);
4001 Error(ERR_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;
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_GATE_1_GRAY;
5306 case 0x1683: // gate (yellow)
5307 element = EL_EM_GATE_2;
5310 case 0x1684: // secret gate (yellow)
5311 element = EL_GATE_2_GRAY;
5314 case 0x1685: // gate (blue)
5315 element = EL_EM_GATE_4;
5318 case 0x1686: // secret gate (blue)
5319 element = EL_GATE_4_GRAY;
5322 case 0x1687: // gate (green)
5323 element = EL_EM_GATE_3;
5326 case 0x1688: // secret gate (green)
5327 element = EL_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 Error(ERR_WARN, "unknown Diamond Caves element 0x%04x", element);
5559 element = EL_UNKNOWN;
5564 return getMappedElement(element);
5567 static void LoadLevelFromFileStream_DC(File *file, struct LevelInfo *level,
5570 byte header[DC_LEVEL_HEADER_SIZE];
5572 int envelope_header_pos = 62;
5573 int envelope_content_pos = 94;
5574 int level_name_pos = 251;
5575 int level_author_pos = 292;
5576 int envelope_header_len;
5577 int envelope_content_len;
5579 int level_author_len;
5581 int num_yamyam_contents;
5584 getDecodedWord_DC(0, TRUE); // initialize DC2 decoding engine
5586 for (i = 0; i < DC_LEVEL_HEADER_SIZE / 2; i++)
5588 unsigned short header_word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
5590 header[i * 2 + 0] = header_word >> 8;
5591 header[i * 2 + 1] = header_word & 0xff;
5594 // read some values from level header to check level decoding integrity
5595 fieldx = header[6] | (header[7] << 8);
5596 fieldy = header[8] | (header[9] << 8);
5597 num_yamyam_contents = header[60] | (header[61] << 8);
5599 // do some simple sanity checks to ensure that level was correctly decoded
5600 if (fieldx < 1 || fieldx > 256 ||
5601 fieldy < 1 || fieldy > 256 ||
5602 num_yamyam_contents < 1 || num_yamyam_contents > 8)
5604 level->no_valid_file = TRUE;
5606 Error(ERR_WARN, "cannot decode level from stream -- using empty level");
5611 // maximum envelope header size is 31 bytes
5612 envelope_header_len = header[envelope_header_pos];
5613 // maximum envelope content size is 110 (156?) bytes
5614 envelope_content_len = header[envelope_content_pos];
5616 // maximum level title size is 40 bytes
5617 level_name_len = MIN(header[level_name_pos], MAX_LEVEL_NAME_LEN);
5618 // maximum level author size is 30 (51?) bytes
5619 level_author_len = MIN(header[level_author_pos], MAX_LEVEL_AUTHOR_LEN);
5623 for (i = 0; i < envelope_header_len; i++)
5624 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
5625 level->envelope[0].text[envelope_size++] =
5626 header[envelope_header_pos + 1 + i];
5628 if (envelope_header_len > 0 && envelope_content_len > 0)
5630 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
5631 level->envelope[0].text[envelope_size++] = '\n';
5632 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
5633 level->envelope[0].text[envelope_size++] = '\n';
5636 for (i = 0; i < envelope_content_len; i++)
5637 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
5638 level->envelope[0].text[envelope_size++] =
5639 header[envelope_content_pos + 1 + i];
5641 level->envelope[0].text[envelope_size] = '\0';
5643 level->envelope[0].xsize = MAX_ENVELOPE_XSIZE;
5644 level->envelope[0].ysize = 10;
5645 level->envelope[0].autowrap = TRUE;
5646 level->envelope[0].centered = TRUE;
5648 for (i = 0; i < level_name_len; i++)
5649 level->name[i] = header[level_name_pos + 1 + i];
5650 level->name[level_name_len] = '\0';
5652 for (i = 0; i < level_author_len; i++)
5653 level->author[i] = header[level_author_pos + 1 + i];
5654 level->author[level_author_len] = '\0';
5656 num_yamyam_contents = header[60] | (header[61] << 8);
5657 level->num_yamyam_contents =
5658 MIN(MAX(MIN_ELEMENT_CONTENTS, num_yamyam_contents), MAX_ELEMENT_CONTENTS);
5660 for (i = 0; i < num_yamyam_contents; i++)
5662 for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
5664 unsigned short word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
5665 int element_dc = ((word & 0xff) << 8) | ((word >> 8) & 0xff);
5667 if (i < MAX_ELEMENT_CONTENTS)
5668 level->yamyam_content[i].e[x][y] = getMappedElement_DC(element_dc);
5672 fieldx = header[6] | (header[7] << 8);
5673 fieldy = header[8] | (header[9] << 8);
5674 level->fieldx = MIN(MAX(MIN_LEV_FIELDX, fieldx), MAX_LEV_FIELDX);
5675 level->fieldy = MIN(MAX(MIN_LEV_FIELDY, fieldy), MAX_LEV_FIELDY);
5677 for (y = 0; y < fieldy; y++) for (x = 0; x < fieldx; x++)
5679 unsigned short word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
5680 int element_dc = ((word & 0xff) << 8) | ((word >> 8) & 0xff);
5682 if (x < MAX_LEV_FIELDX && y < MAX_LEV_FIELDY)
5683 level->field[x][y] = getMappedElement_DC(element_dc);
5686 x = MIN(MAX(0, (header[10] | (header[11] << 8)) - 1), MAX_LEV_FIELDX - 1);
5687 y = MIN(MAX(0, (header[12] | (header[13] << 8)) - 1), MAX_LEV_FIELDY - 1);
5688 level->field[x][y] = EL_PLAYER_1;
5690 x = MIN(MAX(0, (header[14] | (header[15] << 8)) - 1), MAX_LEV_FIELDX - 1);
5691 y = MIN(MAX(0, (header[16] | (header[17] << 8)) - 1), MAX_LEV_FIELDY - 1);
5692 level->field[x][y] = EL_PLAYER_2;
5694 level->gems_needed = header[18] | (header[19] << 8);
5696 level->score[SC_EMERALD] = header[20] | (header[21] << 8);
5697 level->score[SC_DIAMOND] = header[22] | (header[23] << 8);
5698 level->score[SC_PEARL] = header[24] | (header[25] << 8);
5699 level->score[SC_CRYSTAL] = header[26] | (header[27] << 8);
5700 level->score[SC_NUT] = header[28] | (header[29] << 8);
5701 level->score[SC_ROBOT] = header[30] | (header[31] << 8);
5702 level->score[SC_SPACESHIP] = header[32] | (header[33] << 8);
5703 level->score[SC_BUG] = header[34] | (header[35] << 8);
5704 level->score[SC_YAMYAM] = header[36] | (header[37] << 8);
5705 level->score[SC_DYNAMITE] = header[38] | (header[39] << 8);
5706 level->score[SC_KEY] = header[40] | (header[41] << 8);
5707 level->score[SC_TIME_BONUS] = header[42] | (header[43] << 8);
5709 level->time = header[44] | (header[45] << 8);
5711 level->amoeba_speed = header[46] | (header[47] << 8);
5712 level->time_light = header[48] | (header[49] << 8);
5713 level->time_timegate = header[50] | (header[51] << 8);
5714 level->time_wheel = header[52] | (header[53] << 8);
5715 level->time_magic_wall = header[54] | (header[55] << 8);
5716 level->extra_time = header[56] | (header[57] << 8);
5717 level->shield_normal_time = header[58] | (header[59] << 8);
5719 // Diamond Caves has the same (strange) behaviour as Emerald Mine that gems
5720 // can slip down from flat walls, like normal walls and steel walls
5721 level->em_slippery_gems = TRUE;
5724 static void LoadLevelFromFileInfo_DC(struct LevelInfo *level,
5725 struct LevelFileInfo *level_file_info,
5726 boolean level_info_only)
5728 char *filename = level_file_info->filename;
5730 int num_magic_bytes = 8;
5731 char magic_bytes[num_magic_bytes + 1];
5732 int num_levels_to_skip = level_file_info->nr - leveldir_current->first_level;
5734 if (!(file = openFile(filename, MODE_READ)))
5736 level->no_valid_file = TRUE;
5738 if (!level_info_only)
5739 Error(ERR_WARN, "cannot read level '%s' -- using empty level", filename);
5744 // fseek(file, 0x0000, SEEK_SET);
5746 if (level_file_info->packed)
5748 // read "magic bytes" from start of file
5749 if (getStringFromFile(file, magic_bytes, num_magic_bytes + 1) == NULL)
5750 magic_bytes[0] = '\0';
5752 // check "magic bytes" for correct file format
5753 if (!strPrefix(magic_bytes, "DC2"))
5755 level->no_valid_file = TRUE;
5757 Error(ERR_WARN, "unknown DC level file '%s' -- using empty level",
5763 if (strPrefix(magic_bytes, "DC2Win95") ||
5764 strPrefix(magic_bytes, "DC2Win98"))
5766 int position_first_level = 0x00fa;
5767 int extra_bytes = 4;
5770 // advance file stream to first level inside the level package
5771 skip_bytes = position_first_level - num_magic_bytes - extra_bytes;
5773 // each block of level data is followed by block of non-level data
5774 num_levels_to_skip *= 2;
5776 // at least skip header bytes, therefore use ">= 0" instead of "> 0"
5777 while (num_levels_to_skip >= 0)
5779 // advance file stream to next level inside the level package
5780 if (seekFile(file, skip_bytes, SEEK_CUR) != 0)
5782 level->no_valid_file = TRUE;
5784 Error(ERR_WARN, "cannot fseek in file '%s' -- using empty level",
5790 // skip apparently unused extra bytes following each level
5791 ReadUnusedBytesFromFile(file, extra_bytes);
5793 // read size of next level in level package
5794 skip_bytes = getFile32BitLE(file);
5796 num_levels_to_skip--;
5801 level->no_valid_file = TRUE;
5803 Error(ERR_WARN, "unknown DC2 level file '%s' -- using empty level",
5810 LoadLevelFromFileStream_DC(file, level, level_file_info->nr);
5816 // ----------------------------------------------------------------------------
5817 // functions for loading SB level
5818 // ----------------------------------------------------------------------------
5820 int getMappedElement_SB(int element_ascii, boolean use_ces)
5828 sb_element_mapping[] =
5830 { ' ', EL_EMPTY, EL_CUSTOM_1 }, // floor (space)
5831 { '#', EL_STEELWALL, EL_CUSTOM_2 }, // wall
5832 { '@', EL_PLAYER_1, EL_CUSTOM_3 }, // player
5833 { '$', EL_SOKOBAN_OBJECT, EL_CUSTOM_4 }, // box
5834 { '.', EL_SOKOBAN_FIELD_EMPTY, EL_CUSTOM_5 }, // goal square
5835 { '*', EL_SOKOBAN_FIELD_FULL, EL_CUSTOM_6 }, // box on goal square
5836 { '+', EL_SOKOBAN_FIELD_PLAYER, EL_CUSTOM_7 }, // player on goal square
5837 { '_', EL_INVISIBLE_STEELWALL, EL_FROM_LEVEL_TEMPLATE }, // floor beyond border
5844 for (i = 0; sb_element_mapping[i].ascii != 0; i++)
5845 if (element_ascii == sb_element_mapping[i].ascii)
5846 return (use_ces ? sb_element_mapping[i].ce : sb_element_mapping[i].sb);
5848 return EL_UNDEFINED;
5851 static void LoadLevelFromFileInfo_SB(struct LevelInfo *level,
5852 struct LevelFileInfo *level_file_info,
5853 boolean level_info_only)
5855 char *filename = level_file_info->filename;
5856 char line[MAX_LINE_LEN], line_raw[MAX_LINE_LEN], previous_line[MAX_LINE_LEN];
5857 char last_comment[MAX_LINE_LEN];
5858 char level_name[MAX_LINE_LEN];
5861 int num_levels_to_skip = level_file_info->nr - leveldir_current->first_level;
5862 boolean read_continued_line = FALSE;
5863 boolean reading_playfield = FALSE;
5864 boolean got_valid_playfield_line = FALSE;
5865 boolean invalid_playfield_char = FALSE;
5866 boolean load_xsb_to_ces = check_special_flags("load_xsb_to_ces");
5867 int file_level_nr = 0;
5869 int x = 0, y = 0; // initialized to make compilers happy
5871 last_comment[0] = '\0';
5872 level_name[0] = '\0';
5874 if (!(file = openFile(filename, MODE_READ)))
5876 level->no_valid_file = TRUE;
5878 if (!level_info_only)
5879 Error(ERR_WARN, "cannot read level '%s' -- using empty level", filename);
5884 while (!checkEndOfFile(file))
5886 // level successfully read, but next level may follow here
5887 if (!got_valid_playfield_line && reading_playfield)
5889 // read playfield from single level file -- skip remaining file
5890 if (!level_file_info->packed)
5893 if (file_level_nr >= num_levels_to_skip)
5898 last_comment[0] = '\0';
5899 level_name[0] = '\0';
5901 reading_playfield = FALSE;
5904 got_valid_playfield_line = FALSE;
5906 // read next line of input file
5907 if (!getStringFromFile(file, line, MAX_LINE_LEN))
5910 // check if line was completely read and is terminated by line break
5911 if (strlen(line) > 0 && line[strlen(line) - 1] == '\n')
5914 // cut trailing line break (this can be newline and/or carriage return)
5915 for (line_ptr = &line[strlen(line)]; line_ptr >= line; line_ptr--)
5916 if ((*line_ptr == '\n' || *line_ptr == '\r') && *(line_ptr + 1) == '\0')
5919 // copy raw input line for later use (mainly debugging output)
5920 strcpy(line_raw, line);
5922 if (read_continued_line)
5924 // append new line to existing line, if there is enough space
5925 if (strlen(previous_line) + strlen(line_ptr) < MAX_LINE_LEN)
5926 strcat(previous_line, line_ptr);
5928 strcpy(line, previous_line); // copy storage buffer to line
5930 read_continued_line = FALSE;
5933 // if the last character is '\', continue at next line
5934 if (strlen(line) > 0 && line[strlen(line) - 1] == '\\')
5936 line[strlen(line) - 1] = '\0'; // cut off trailing backslash
5937 strcpy(previous_line, line); // copy line to storage buffer
5939 read_continued_line = TRUE;
5945 if (line[0] == '\0')
5948 // extract comment text from comment line
5951 for (line_ptr = line; *line_ptr; line_ptr++)
5952 if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != ';')
5955 strcpy(last_comment, line_ptr);
5960 // extract level title text from line containing level title
5961 if (line[0] == '\'')
5963 strcpy(level_name, &line[1]);
5965 if (strlen(level_name) > 0 && level_name[strlen(level_name) - 1] == '\'')
5966 level_name[strlen(level_name) - 1] = '\0';
5971 // skip lines containing only spaces (or empty lines)
5972 for (line_ptr = line; *line_ptr; line_ptr++)
5973 if (*line_ptr != ' ')
5975 if (*line_ptr == '\0')
5978 // at this point, we have found a line containing part of a playfield
5980 got_valid_playfield_line = TRUE;
5982 if (!reading_playfield)
5984 reading_playfield = TRUE;
5985 invalid_playfield_char = FALSE;
5987 for (x = 0; x < MAX_LEV_FIELDX; x++)
5988 for (y = 0; y < MAX_LEV_FIELDY; y++)
5989 level->field[x][y] = getMappedElement_SB(' ', load_xsb_to_ces);
5994 // start with topmost tile row
5998 // skip playfield line if larger row than allowed
5999 if (y >= MAX_LEV_FIELDY)
6002 // start with leftmost tile column
6005 // read playfield elements from line
6006 for (line_ptr = line; *line_ptr; line_ptr++)
6008 int mapped_sb_element = getMappedElement_SB(*line_ptr, load_xsb_to_ces);
6010 // stop parsing playfield line if larger column than allowed
6011 if (x >= MAX_LEV_FIELDX)
6014 if (mapped_sb_element == EL_UNDEFINED)
6016 invalid_playfield_char = TRUE;
6021 level->field[x][y] = mapped_sb_element;
6023 // continue with next tile column
6026 level->fieldx = MAX(x, level->fieldx);
6029 if (invalid_playfield_char)
6031 // if first playfield line, treat invalid lines as comment lines
6033 reading_playfield = FALSE;
6038 // continue with next tile row
6046 level->fieldx = MIN(MAX(MIN_LEV_FIELDX, level->fieldx), MAX_LEV_FIELDX);
6047 level->fieldy = MIN(MAX(MIN_LEV_FIELDY, level->fieldy), MAX_LEV_FIELDY);
6049 if (!reading_playfield)
6051 level->no_valid_file = TRUE;
6053 Error(ERR_WARN, "cannot read level '%s' -- using empty level", filename);
6058 if (*level_name != '\0')
6060 strncpy(level->name, level_name, MAX_LEVEL_NAME_LEN);
6061 level->name[MAX_LEVEL_NAME_LEN] = '\0';
6063 else if (*last_comment != '\0')
6065 strncpy(level->name, last_comment, MAX_LEVEL_NAME_LEN);
6066 level->name[MAX_LEVEL_NAME_LEN] = '\0';
6070 sprintf(level->name, "--> Level %d <--", level_file_info->nr);
6073 // set all empty fields beyond the border walls to invisible steel wall
6074 for (y = 0; y < level->fieldy; y++) for (x = 0; x < level->fieldx; x++)
6076 if ((x == 0 || x == level->fieldx - 1 ||
6077 y == 0 || y == level->fieldy - 1) &&
6078 level->field[x][y] == getMappedElement_SB(' ', load_xsb_to_ces))
6079 FloodFillLevel(x, y, getMappedElement_SB('_', load_xsb_to_ces),
6080 level->field, level->fieldx, level->fieldy);
6083 // set special level settings for Sokoban levels
6086 level->use_step_counter = TRUE;
6088 if (load_xsb_to_ces)
6090 // special global settings can now be set in level template
6092 level->use_custom_template = TRUE;
6097 // -------------------------------------------------------------------------
6098 // functions for handling native levels
6099 // -------------------------------------------------------------------------
6101 static void LoadLevelFromFileInfo_EM(struct LevelInfo *level,
6102 struct LevelFileInfo *level_file_info,
6103 boolean level_info_only)
6105 if (!LoadNativeLevel_EM(level_file_info->filename, level_info_only))
6106 level->no_valid_file = TRUE;
6109 static void LoadLevelFromFileInfo_SP(struct LevelInfo *level,
6110 struct LevelFileInfo *level_file_info,
6111 boolean level_info_only)
6115 // determine position of requested level inside level package
6116 if (level_file_info->packed)
6117 pos = level_file_info->nr - leveldir_current->first_level;
6119 if (!LoadNativeLevel_SP(level_file_info->filename, pos, level_info_only))
6120 level->no_valid_file = TRUE;
6123 static void LoadLevelFromFileInfo_MM(struct LevelInfo *level,
6124 struct LevelFileInfo *level_file_info,
6125 boolean level_info_only)
6127 if (!LoadNativeLevel_MM(level_file_info->filename, level_info_only))
6128 level->no_valid_file = TRUE;
6131 void CopyNativeLevel_RND_to_Native(struct LevelInfo *level)
6133 if (level->game_engine_type == GAME_ENGINE_TYPE_EM)
6134 CopyNativeLevel_RND_to_EM(level);
6135 else if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
6136 CopyNativeLevel_RND_to_SP(level);
6137 else if (level->game_engine_type == GAME_ENGINE_TYPE_MM)
6138 CopyNativeLevel_RND_to_MM(level);
6141 void CopyNativeLevel_Native_to_RND(struct LevelInfo *level)
6143 if (level->game_engine_type == GAME_ENGINE_TYPE_EM)
6144 CopyNativeLevel_EM_to_RND(level);
6145 else if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
6146 CopyNativeLevel_SP_to_RND(level);
6147 else if (level->game_engine_type == GAME_ENGINE_TYPE_MM)
6148 CopyNativeLevel_MM_to_RND(level);
6151 void SaveNativeLevel(struct LevelInfo *level)
6153 if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
6155 char *basename = getSingleLevelBasenameExt(level->file_info.nr, "sp");
6156 char *filename = getLevelFilenameFromBasename(basename);
6158 CopyNativeLevel_RND_to_SP(level);
6159 CopyNativeTape_RND_to_SP(level);
6161 SaveNativeLevel_SP(filename);
6166 // ----------------------------------------------------------------------------
6167 // functions for loading generic level
6168 // ----------------------------------------------------------------------------
6170 static void LoadLevelFromFileInfo(struct LevelInfo *level,
6171 struct LevelFileInfo *level_file_info,
6172 boolean level_info_only)
6174 // always start with reliable default values
6175 setLevelInfoToDefaults(level, level_info_only, TRUE);
6177 switch (level_file_info->type)
6179 case LEVEL_FILE_TYPE_RND:
6180 LoadLevelFromFileInfo_RND(level, level_file_info, level_info_only);
6183 case LEVEL_FILE_TYPE_EM:
6184 LoadLevelFromFileInfo_EM(level, level_file_info, level_info_only);
6185 level->game_engine_type = GAME_ENGINE_TYPE_EM;
6188 case LEVEL_FILE_TYPE_SP:
6189 LoadLevelFromFileInfo_SP(level, level_file_info, level_info_only);
6190 level->game_engine_type = GAME_ENGINE_TYPE_SP;
6193 case LEVEL_FILE_TYPE_MM:
6194 LoadLevelFromFileInfo_MM(level, level_file_info, level_info_only);
6195 level->game_engine_type = GAME_ENGINE_TYPE_MM;
6198 case LEVEL_FILE_TYPE_DC:
6199 LoadLevelFromFileInfo_DC(level, level_file_info, level_info_only);
6202 case LEVEL_FILE_TYPE_SB:
6203 LoadLevelFromFileInfo_SB(level, level_file_info, level_info_only);
6207 LoadLevelFromFileInfo_RND(level, level_file_info, level_info_only);
6211 // if level file is invalid, restore level structure to default values
6212 if (level->no_valid_file)
6213 setLevelInfoToDefaults(level, level_info_only, FALSE);
6215 if (level->game_engine_type == GAME_ENGINE_TYPE_UNKNOWN)
6216 level->game_engine_type = GAME_ENGINE_TYPE_RND;
6218 if (level_file_info->type != LEVEL_FILE_TYPE_RND)
6219 CopyNativeLevel_Native_to_RND(level);
6222 void LoadLevelFromFilename(struct LevelInfo *level, char *filename)
6224 static struct LevelFileInfo level_file_info;
6226 // always start with reliable default values
6227 setFileInfoToDefaults(&level_file_info);
6229 level_file_info.nr = 0; // unknown level number
6230 level_file_info.type = LEVEL_FILE_TYPE_RND; // no others supported yet
6232 setString(&level_file_info.filename, filename);
6234 LoadLevelFromFileInfo(level, &level_file_info, FALSE);
6237 static void LoadLevel_InitVersion(struct LevelInfo *level)
6241 if (leveldir_current == NULL) // only when dumping level
6244 // all engine modifications also valid for levels which use latest engine
6245 if (level->game_version < VERSION_IDENT(3,2,0,5))
6247 // time bonus score was given for 10 s instead of 1 s before 3.2.0-5
6248 level->score[SC_TIME_BONUS] /= 10;
6251 if (leveldir_current->latest_engine)
6253 // ---------- use latest game engine --------------------------------------
6255 /* For all levels which are forced to use the latest game engine version
6256 (normally all but user contributed, private and undefined levels), set
6257 the game engine version to the actual version; this allows for actual
6258 corrections in the game engine to take effect for existing, converted
6259 levels (from "classic" or other existing games) to make the emulation
6260 of the corresponding game more accurate, while (hopefully) not breaking
6261 existing levels created from other players. */
6263 level->game_version = GAME_VERSION_ACTUAL;
6265 /* Set special EM style gems behaviour: EM style gems slip down from
6266 normal, steel and growing wall. As this is a more fundamental change,
6267 it seems better to set the default behaviour to "off" (as it is more
6268 natural) and make it configurable in the level editor (as a property
6269 of gem style elements). Already existing converted levels (neither
6270 private nor contributed levels) are changed to the new behaviour. */
6272 if (level->file_version < FILE_VERSION_2_0)
6273 level->em_slippery_gems = TRUE;
6278 // ---------- use game engine the level was created with --------------------
6280 /* For all levels which are not forced to use the latest game engine
6281 version (normally user contributed, private and undefined levels),
6282 use the version of the game engine the levels were created for.
6284 Since 2.0.1, the game engine version is now directly stored
6285 in the level file (chunk "VERS"), so there is no need anymore
6286 to set the game version from the file version (except for old,
6287 pre-2.0 levels, where the game version is still taken from the
6288 file format version used to store the level -- see above). */
6290 // player was faster than enemies in 1.0.0 and before
6291 if (level->file_version == FILE_VERSION_1_0)
6292 for (i = 0; i < MAX_PLAYERS; i++)
6293 level->initial_player_stepsize[i] = STEPSIZE_FAST;
6295 // default behaviour for EM style gems was "slippery" only in 2.0.1
6296 if (level->game_version == VERSION_IDENT(2,0,1,0))
6297 level->em_slippery_gems = TRUE;
6299 // springs could be pushed over pits before (pre-release version) 2.2.0
6300 if (level->game_version < VERSION_IDENT(2,2,0,0))
6301 level->use_spring_bug = TRUE;
6303 if (level->game_version < VERSION_IDENT(3,2,0,5))
6305 // time orb caused limited time in endless time levels before 3.2.0-5
6306 level->use_time_orb_bug = TRUE;
6308 // default behaviour for snapping was "no snap delay" before 3.2.0-5
6309 level->block_snap_field = FALSE;
6311 // extra time score was same value as time left score before 3.2.0-5
6312 level->extra_time_score = level->score[SC_TIME_BONUS];
6315 if (level->game_version < VERSION_IDENT(3,2,0,7))
6317 // default behaviour for snapping was "not continuous" before 3.2.0-7
6318 level->continuous_snapping = FALSE;
6321 // only few elements were able to actively move into acid before 3.1.0
6322 // trigger settings did not exist before 3.1.0; set to default "any"
6323 if (level->game_version < VERSION_IDENT(3,1,0,0))
6325 // correct "can move into acid" settings (all zero in old levels)
6327 level->can_move_into_acid_bits = 0; // nothing can move into acid
6328 level->dont_collide_with_bits = 0; // nothing is deadly when colliding
6330 setMoveIntoAcidProperty(level, EL_ROBOT, TRUE);
6331 setMoveIntoAcidProperty(level, EL_SATELLITE, TRUE);
6332 setMoveIntoAcidProperty(level, EL_PENGUIN, TRUE);
6333 setMoveIntoAcidProperty(level, EL_BALLOON, TRUE);
6335 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6336 SET_PROPERTY(EL_CUSTOM_START + i, EP_CAN_MOVE_INTO_ACID, TRUE);
6338 // correct trigger settings (stored as zero == "none" in old levels)
6340 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6342 int element = EL_CUSTOM_START + i;
6343 struct ElementInfo *ei = &element_info[element];
6345 for (j = 0; j < ei->num_change_pages; j++)
6347 struct ElementChangeInfo *change = &ei->change_page[j];
6349 change->trigger_player = CH_PLAYER_ANY;
6350 change->trigger_page = CH_PAGE_ANY;
6355 // try to detect and fix "Snake Bite" levels, which are broken with 3.2.0
6357 int element = EL_CUSTOM_256;
6358 struct ElementInfo *ei = &element_info[element];
6359 struct ElementChangeInfo *change = &ei->change_page[0];
6361 /* This is needed to fix a problem that was caused by a bugfix in function
6362 game.c/CreateFieldExt() introduced with 3.2.0 that corrects the behaviour
6363 when a custom element changes to EL_SOKOBAN_FIELD_PLAYER (before, it did
6364 not replace walkable elements, but instead just placed the player on it,
6365 without placing the Sokoban field under the player). Unfortunately, this
6366 breaks "Snake Bite" style levels when the snake is halfway through a door
6367 that just closes (the snake head is still alive and can be moved in this
6368 case). This can be fixed by replacing the EL_SOKOBAN_FIELD_PLAYER by the
6369 player (without Sokoban element) which then gets killed as designed). */
6371 if ((strncmp(leveldir_current->identifier, "snake_bite", 10) == 0 ||
6372 strncmp(ei->description, "pause b4 death", 14) == 0) &&
6373 change->target_element == EL_SOKOBAN_FIELD_PLAYER)
6374 change->target_element = EL_PLAYER_1;
6377 // try to detect and fix "Zelda" style levels, which are broken with 3.2.5
6378 if (level->game_version < VERSION_IDENT(3,2,5,0))
6380 /* This is needed to fix a problem that was caused by a bugfix in function
6381 game.c/CheckTriggeredElementChangeExt() introduced with 3.2.5 that
6382 corrects the behaviour when a custom element changes to another custom
6383 element with a higher element number that has change actions defined.
6384 Normally, only one change per frame is allowed for custom elements.
6385 Therefore, it is checked if a custom element already changed in the
6386 current frame; if it did, subsequent changes are suppressed.
6387 Unfortunately, this is only checked for element changes, but not for
6388 change actions, which are still executed. As the function above loops
6389 through all custom elements from lower to higher, an element change
6390 resulting in a lower CE number won't be checked again, while a target
6391 element with a higher number will also be checked, and potential change
6392 actions will get executed for this CE, too (which is wrong), while
6393 further changes are ignored (which is correct). As this bugfix breaks
6394 Zelda II (and introduces graphical bugs to Zelda I, and also breaks a
6395 few other levels like Alan Bond's "FMV"), allow the previous, incorrect
6396 behaviour for existing levels and tapes that make use of this bug */
6398 level->use_action_after_change_bug = TRUE;
6401 // not centering level after relocating player was default only in 3.2.3
6402 if (level->game_version == VERSION_IDENT(3,2,3,0)) // (no pre-releases)
6403 level->shifted_relocation = TRUE;
6405 // EM style elements always chain-exploded in R'n'D engine before 3.2.6
6406 if (level->game_version < VERSION_IDENT(3,2,6,0))
6407 level->em_explodes_by_fire = TRUE;
6409 // levels were solved by the first player entering an exit up to 4.1.0.0
6410 if (level->game_version <= VERSION_IDENT(4,1,0,0))
6411 level->solved_by_one_player = TRUE;
6413 // game logic of "game of life" and "biomaze" was buggy before 4.1.1.1
6414 if (level->game_version < VERSION_IDENT(4,1,1,1))
6415 level->use_life_bugs = TRUE;
6417 // only Sokoban fields (but not objects) had to be solved before 4.1.1.1
6418 if (level->game_version < VERSION_IDENT(4,1,1,1))
6419 level->sb_objects_needed = FALSE;
6422 static void LoadLevel_InitStandardElements(struct LevelInfo *level)
6426 // map elements that have changed in newer versions
6427 level->amoeba_content = getMappedElementByVersion(level->amoeba_content,
6428 level->game_version);
6429 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
6430 for (x = 0; x < 3; x++)
6431 for (y = 0; y < 3; y++)
6432 level->yamyam_content[i].e[x][y] =
6433 getMappedElementByVersion(level->yamyam_content[i].e[x][y],
6434 level->game_version);
6438 static void LoadLevel_InitCustomElements(struct LevelInfo *level)
6442 // map custom element change events that have changed in newer versions
6443 // (these following values were accidentally changed in version 3.0.1)
6444 // (this seems to be needed only for 'ab_levelset3' and 'ab_levelset4')
6445 if (level->game_version <= VERSION_IDENT(3,0,0,0))
6447 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6449 int element = EL_CUSTOM_START + i;
6451 // order of checking and copying events to be mapped is important
6452 // (do not change the start and end value -- they are constant)
6453 for (j = CE_BY_OTHER_ACTION; j >= CE_VALUE_GETS_ZERO; j--)
6455 if (HAS_CHANGE_EVENT(element, j - 2))
6457 SET_CHANGE_EVENT(element, j - 2, FALSE);
6458 SET_CHANGE_EVENT(element, j, TRUE);
6462 // order of checking and copying events to be mapped is important
6463 // (do not change the start and end value -- they are constant)
6464 for (j = CE_PLAYER_COLLECTS_X; j >= CE_HITTING_SOMETHING; j--)
6466 if (HAS_CHANGE_EVENT(element, j - 1))
6468 SET_CHANGE_EVENT(element, j - 1, FALSE);
6469 SET_CHANGE_EVENT(element, j, TRUE);
6475 // initialize "can_change" field for old levels with only one change page
6476 if (level->game_version <= VERSION_IDENT(3,0,2,0))
6478 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6480 int element = EL_CUSTOM_START + i;
6482 if (CAN_CHANGE(element))
6483 element_info[element].change->can_change = TRUE;
6487 // correct custom element values (for old levels without these options)
6488 if (level->game_version < VERSION_IDENT(3,1,1,0))
6490 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6492 int element = EL_CUSTOM_START + i;
6493 struct ElementInfo *ei = &element_info[element];
6495 if (ei->access_direction == MV_NO_DIRECTION)
6496 ei->access_direction = MV_ALL_DIRECTIONS;
6500 // correct custom element values (fix invalid values for all versions)
6503 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6505 int element = EL_CUSTOM_START + i;
6506 struct ElementInfo *ei = &element_info[element];
6508 for (j = 0; j < ei->num_change_pages; j++)
6510 struct ElementChangeInfo *change = &ei->change_page[j];
6512 if (change->trigger_player == CH_PLAYER_NONE)
6513 change->trigger_player = CH_PLAYER_ANY;
6515 if (change->trigger_side == CH_SIDE_NONE)
6516 change->trigger_side = CH_SIDE_ANY;
6521 // initialize "can_explode" field for old levels which did not store this
6522 // !!! CHECK THIS -- "<= 3,1,0,0" IS PROBABLY WRONG !!!
6523 if (level->game_version <= VERSION_IDENT(3,1,0,0))
6525 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6527 int element = EL_CUSTOM_START + i;
6529 if (EXPLODES_1X1_OLD(element))
6530 element_info[element].explosion_type = EXPLODES_1X1;
6532 SET_PROPERTY(element, EP_CAN_EXPLODE, (EXPLODES_BY_FIRE(element) ||
6533 EXPLODES_SMASHED(element) ||
6534 EXPLODES_IMPACT(element)));
6538 // correct previously hard-coded move delay values for maze runner style
6539 if (level->game_version < VERSION_IDENT(3,1,1,0))
6541 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6543 int element = EL_CUSTOM_START + i;
6545 if (element_info[element].move_pattern & MV_MAZE_RUNNER_STYLE)
6547 // previously hard-coded and therefore ignored
6548 element_info[element].move_delay_fixed = 9;
6549 element_info[element].move_delay_random = 0;
6554 // set some other uninitialized values of custom elements in older levels
6555 if (level->game_version < VERSION_IDENT(3,1,0,0))
6557 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6559 int element = EL_CUSTOM_START + i;
6561 element_info[element].access_direction = MV_ALL_DIRECTIONS;
6563 element_info[element].explosion_delay = 17;
6564 element_info[element].ignition_delay = 8;
6568 // check for custom elements which have mouse click events defined
6569 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6571 int element = EL_CUSTOM_START + i;
6573 if (HAS_CHANGE_EVENT(element, CE_CLICKED_BY_MOUSE) ||
6574 HAS_CHANGE_EVENT(element, CE_PRESSED_BY_MOUSE) ||
6575 HAS_CHANGE_EVENT(element, CE_MOUSE_CLICKED_ON_X) ||
6576 HAS_CHANGE_EVENT(element, CE_MOUSE_PRESSED_ON_X))
6577 level->has_mouse_events = TRUE;
6581 static void LoadLevel_InitElements(struct LevelInfo *level)
6583 LoadLevel_InitStandardElements(level);
6585 if (level->file_has_custom_elements)
6586 LoadLevel_InitCustomElements(level);
6588 // initialize element properties for level editor etc.
6589 InitElementPropertiesEngine(level->game_version);
6590 InitElementPropertiesGfxElement();
6593 static void LoadLevel_InitPlayfield(struct LevelInfo *level)
6597 // map elements that have changed in newer versions
6598 for (y = 0; y < level->fieldy; y++)
6599 for (x = 0; x < level->fieldx; x++)
6600 level->field[x][y] = getMappedElementByVersion(level->field[x][y],
6601 level->game_version);
6603 // clear unused playfield data (nicer if level gets resized in editor)
6604 for (x = 0; x < MAX_LEV_FIELDX; x++)
6605 for (y = 0; y < MAX_LEV_FIELDY; y++)
6606 if (x >= level->fieldx || y >= level->fieldy)
6607 level->field[x][y] = EL_EMPTY;
6609 // copy elements to runtime playfield array
6610 for (x = 0; x < MAX_LEV_FIELDX; x++)
6611 for (y = 0; y < MAX_LEV_FIELDY; y++)
6612 Feld[x][y] = level->field[x][y];
6614 // initialize level size variables for faster access
6615 lev_fieldx = level->fieldx;
6616 lev_fieldy = level->fieldy;
6618 // determine border element for this level
6619 if (level->file_info.type == LEVEL_FILE_TYPE_DC)
6620 BorderElement = EL_EMPTY; // (in editor, SetBorderElement() is used)
6625 static void LoadLevel_InitNativeEngines(struct LevelInfo *level)
6627 struct LevelFileInfo *level_file_info = &level->file_info;
6629 if (level_file_info->type == LEVEL_FILE_TYPE_RND)
6630 CopyNativeLevel_RND_to_Native(level);
6633 static void LoadLevelTemplate_LoadAndInit(void)
6635 LoadLevelFromFileInfo(&level_template, &level_template.file_info, FALSE);
6637 LoadLevel_InitVersion(&level_template);
6638 LoadLevel_InitElements(&level_template);
6640 ActivateLevelTemplate();
6643 void LoadLevelTemplate(int nr)
6645 if (!fileExists(getGlobalLevelTemplateFilename()))
6647 Error(ERR_WARN, "no level template found for this level");
6652 setLevelFileInfo(&level_template.file_info, nr);
6654 LoadLevelTemplate_LoadAndInit();
6657 static void LoadNetworkLevelTemplate(struct NetworkLevelInfo *network_level)
6659 copyLevelFileInfo(&network_level->tmpl_info, &level_template.file_info);
6661 LoadLevelTemplate_LoadAndInit();
6664 static void LoadLevel_LoadAndInit(struct NetworkLevelInfo *network_level)
6666 LoadLevelFromFileInfo(&level, &level.file_info, FALSE);
6668 if (level.use_custom_template)
6670 if (network_level != NULL)
6671 LoadNetworkLevelTemplate(network_level);
6673 LoadLevelTemplate(-1);
6676 LoadLevel_InitVersion(&level);
6677 LoadLevel_InitElements(&level);
6678 LoadLevel_InitPlayfield(&level);
6680 LoadLevel_InitNativeEngines(&level);
6683 void LoadLevel(int nr)
6685 SetLevelSetInfo(leveldir_current->identifier, nr);
6687 setLevelFileInfo(&level.file_info, nr);
6689 LoadLevel_LoadAndInit(NULL);
6692 void LoadLevelInfoOnly(int nr)
6694 setLevelFileInfo(&level.file_info, nr);
6696 LoadLevelFromFileInfo(&level, &level.file_info, TRUE);
6699 void LoadNetworkLevel(struct NetworkLevelInfo *network_level)
6701 SetLevelSetInfo(network_level->leveldir_identifier,
6702 network_level->file_info.nr);
6704 copyLevelFileInfo(&network_level->file_info, &level.file_info);
6706 LoadLevel_LoadAndInit(network_level);
6709 static int SaveLevel_VERS(FILE *file, struct LevelInfo *level)
6713 chunk_size += putFileVersion(file, level->file_version);
6714 chunk_size += putFileVersion(file, level->game_version);
6719 static int SaveLevel_DATE(FILE *file, struct LevelInfo *level)
6723 chunk_size += putFile16BitBE(file, level->creation_date.year);
6724 chunk_size += putFile8Bit(file, level->creation_date.month);
6725 chunk_size += putFile8Bit(file, level->creation_date.day);
6730 #if ENABLE_HISTORIC_CHUNKS
6731 static void SaveLevel_HEAD(FILE *file, struct LevelInfo *level)
6735 putFile8Bit(file, level->fieldx);
6736 putFile8Bit(file, level->fieldy);
6738 putFile16BitBE(file, level->time);
6739 putFile16BitBE(file, level->gems_needed);
6741 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
6742 putFile8Bit(file, level->name[i]);
6744 for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
6745 putFile8Bit(file, level->score[i]);
6747 for (i = 0; i < STD_ELEMENT_CONTENTS; i++)
6748 for (y = 0; y < 3; y++)
6749 for (x = 0; x < 3; x++)
6750 putFile8Bit(file, (level->encoding_16bit_yamyam ? EL_EMPTY :
6751 level->yamyam_content[i].e[x][y]));
6752 putFile8Bit(file, level->amoeba_speed);
6753 putFile8Bit(file, level->time_magic_wall);
6754 putFile8Bit(file, level->time_wheel);
6755 putFile8Bit(file, (level->encoding_16bit_amoeba ? EL_EMPTY :
6756 level->amoeba_content));
6757 putFile8Bit(file, (level->initial_player_stepsize == STEPSIZE_FAST ? 1 : 0));
6758 putFile8Bit(file, (level->initial_gravity ? 1 : 0));
6759 putFile8Bit(file, (level->encoding_16bit_field ? 1 : 0));
6760 putFile8Bit(file, (level->em_slippery_gems ? 1 : 0));
6762 putFile8Bit(file, (level->use_custom_template ? 1 : 0));
6764 putFile8Bit(file, (level->block_last_field ? 1 : 0));
6765 putFile8Bit(file, (level->sp_block_last_field ? 1 : 0));
6766 putFile32BitBE(file, level->can_move_into_acid_bits);
6767 putFile8Bit(file, level->dont_collide_with_bits);
6769 putFile8Bit(file, (level->use_spring_bug ? 1 : 0));
6770 putFile8Bit(file, (level->use_step_counter ? 1 : 0));
6772 putFile8Bit(file, (level->instant_relocation ? 1 : 0));
6773 putFile8Bit(file, (level->can_pass_to_walkable ? 1 : 0));
6774 putFile8Bit(file, (level->grow_into_diggable ? 1 : 0));
6776 putFile8Bit(file, level->game_engine_type);
6778 WriteUnusedBytesToFile(file, LEVEL_CHUNK_HEAD_UNUSED);
6782 static int SaveLevel_NAME(FILE *file, struct LevelInfo *level)
6787 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
6788 chunk_size += putFile8Bit(file, level->name[i]);
6793 static int SaveLevel_AUTH(FILE *file, struct LevelInfo *level)
6798 for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
6799 chunk_size += putFile8Bit(file, level->author[i]);
6804 #if ENABLE_HISTORIC_CHUNKS
6805 static int SaveLevel_BODY(FILE *file, struct LevelInfo *level)
6810 for (y = 0; y < level->fieldy; y++)
6811 for (x = 0; x < level->fieldx; x++)
6812 if (level->encoding_16bit_field)
6813 chunk_size += putFile16BitBE(file, level->field[x][y]);
6815 chunk_size += putFile8Bit(file, level->field[x][y]);
6821 static int SaveLevel_BODY(FILE *file, struct LevelInfo *level)
6826 for (y = 0; y < level->fieldy; y++)
6827 for (x = 0; x < level->fieldx; x++)
6828 chunk_size += putFile16BitBE(file, level->field[x][y]);
6833 #if ENABLE_HISTORIC_CHUNKS
6834 static void SaveLevel_CONT(FILE *file, struct LevelInfo *level)
6838 putFile8Bit(file, EL_YAMYAM);
6839 putFile8Bit(file, level->num_yamyam_contents);
6840 putFile8Bit(file, 0);
6841 putFile8Bit(file, 0);
6843 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
6844 for (y = 0; y < 3; y++)
6845 for (x = 0; x < 3; x++)
6846 if (level->encoding_16bit_field)
6847 putFile16BitBE(file, level->yamyam_content[i].e[x][y]);
6849 putFile8Bit(file, level->yamyam_content[i].e[x][y]);
6853 #if ENABLE_HISTORIC_CHUNKS
6854 static void SaveLevel_CNT2(FILE *file, struct LevelInfo *level, int element)
6857 int num_contents, content_xsize, content_ysize;
6858 int content_array[MAX_ELEMENT_CONTENTS][3][3];
6860 if (element == EL_YAMYAM)
6862 num_contents = level->num_yamyam_contents;
6866 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
6867 for (y = 0; y < 3; y++)
6868 for (x = 0; x < 3; x++)
6869 content_array[i][x][y] = level->yamyam_content[i].e[x][y];
6871 else if (element == EL_BD_AMOEBA)
6877 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
6878 for (y = 0; y < 3; y++)
6879 for (x = 0; x < 3; x++)
6880 content_array[i][x][y] = EL_EMPTY;
6881 content_array[0][0][0] = level->amoeba_content;
6885 // chunk header already written -- write empty chunk data
6886 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_SIZE);
6888 Error(ERR_WARN, "cannot save content for element '%d'", element);
6892 putFile16BitBE(file, element);
6893 putFile8Bit(file, num_contents);
6894 putFile8Bit(file, content_xsize);
6895 putFile8Bit(file, content_ysize);
6897 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_UNUSED);
6899 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
6900 for (y = 0; y < 3; y++)
6901 for (x = 0; x < 3; x++)
6902 putFile16BitBE(file, content_array[i][x][y]);
6906 #if ENABLE_HISTORIC_CHUNKS
6907 static int SaveLevel_CNT3(FILE *file, struct LevelInfo *level, int element)
6909 int envelope_nr = element - EL_ENVELOPE_1;
6910 int envelope_len = strlen(level->envelope_text[envelope_nr]) + 1;
6914 chunk_size += putFile16BitBE(file, element);
6915 chunk_size += putFile16BitBE(file, envelope_len);
6916 chunk_size += putFile8Bit(file, level->envelope_xsize[envelope_nr]);
6917 chunk_size += putFile8Bit(file, level->envelope_ysize[envelope_nr]);
6919 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT3_UNUSED);
6920 chunk_size += LEVEL_CHUNK_CNT3_UNUSED;
6922 for (i = 0; i < envelope_len; i++)
6923 chunk_size += putFile8Bit(file, level->envelope_text[envelope_nr][i]);
6929 #if ENABLE_HISTORIC_CHUNKS
6930 static void SaveLevel_CUS1(FILE *file, struct LevelInfo *level,
6931 int num_changed_custom_elements)
6935 putFile16BitBE(file, num_changed_custom_elements);
6937 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6939 int element = EL_CUSTOM_START + i;
6941 struct ElementInfo *ei = &element_info[element];
6943 if (ei->properties[EP_BITFIELD_BASE_NR] != EP_BITMASK_DEFAULT)
6945 if (check < num_changed_custom_elements)
6947 putFile16BitBE(file, element);
6948 putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
6955 if (check != num_changed_custom_elements) // should not happen
6956 Error(ERR_WARN, "inconsistent number of custom element properties");
6960 #if ENABLE_HISTORIC_CHUNKS
6961 static void SaveLevel_CUS2(FILE *file, struct LevelInfo *level,
6962 int num_changed_custom_elements)
6966 putFile16BitBE(file, num_changed_custom_elements);
6968 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6970 int element = EL_CUSTOM_START + i;
6972 if (element_info[element].change->target_element != EL_EMPTY_SPACE)
6974 if (check < num_changed_custom_elements)
6976 putFile16BitBE(file, element);
6977 putFile16BitBE(file, element_info[element].change->target_element);
6984 if (check != num_changed_custom_elements) // should not happen
6985 Error(ERR_WARN, "inconsistent number of custom target elements");
6989 #if ENABLE_HISTORIC_CHUNKS
6990 static void SaveLevel_CUS3(FILE *file, struct LevelInfo *level,
6991 int num_changed_custom_elements)
6993 int i, j, x, y, check = 0;
6995 putFile16BitBE(file, num_changed_custom_elements);
6997 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6999 int element = EL_CUSTOM_START + i;
7000 struct ElementInfo *ei = &element_info[element];
7002 if (ei->modified_settings)
7004 if (check < num_changed_custom_elements)
7006 putFile16BitBE(file, element);
7008 for (j = 0; j < MAX_ELEMENT_NAME_LEN; j++)
7009 putFile8Bit(file, ei->description[j]);
7011 putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
7013 // some free bytes for future properties and padding
7014 WriteUnusedBytesToFile(file, 7);
7016 putFile8Bit(file, ei->use_gfx_element);
7017 putFile16BitBE(file, ei->gfx_element_initial);
7019 putFile8Bit(file, ei->collect_score_initial);
7020 putFile8Bit(file, ei->collect_count_initial);
7022 putFile16BitBE(file, ei->push_delay_fixed);
7023 putFile16BitBE(file, ei->push_delay_random);
7024 putFile16BitBE(file, ei->move_delay_fixed);
7025 putFile16BitBE(file, ei->move_delay_random);
7027 putFile16BitBE(file, ei->move_pattern);
7028 putFile8Bit(file, ei->move_direction_initial);
7029 putFile8Bit(file, ei->move_stepsize);
7031 for (y = 0; y < 3; y++)
7032 for (x = 0; x < 3; x++)
7033 putFile16BitBE(file, ei->content.e[x][y]);
7035 putFile32BitBE(file, ei->change->events);
7037 putFile16BitBE(file, ei->change->target_element);
7039 putFile16BitBE(file, ei->change->delay_fixed);
7040 putFile16BitBE(file, ei->change->delay_random);
7041 putFile16BitBE(file, ei->change->delay_frames);
7043 putFile16BitBE(file, ei->change->initial_trigger_element);
7045 putFile8Bit(file, ei->change->explode);
7046 putFile8Bit(file, ei->change->use_target_content);
7047 putFile8Bit(file, ei->change->only_if_complete);
7048 putFile8Bit(file, ei->change->use_random_replace);
7050 putFile8Bit(file, ei->change->random_percentage);
7051 putFile8Bit(file, ei->change->replace_when);
7053 for (y = 0; y < 3; y++)
7054 for (x = 0; x < 3; x++)
7055 putFile16BitBE(file, ei->change->content.e[x][y]);
7057 putFile8Bit(file, ei->slippery_type);
7059 // some free bytes for future properties and padding
7060 WriteUnusedBytesToFile(file, LEVEL_CPART_CUS3_UNUSED);
7067 if (check != num_changed_custom_elements) // should not happen
7068 Error(ERR_WARN, "inconsistent number of custom element properties");
7072 #if ENABLE_HISTORIC_CHUNKS
7073 static void SaveLevel_CUS4(FILE *file, struct LevelInfo *level, int element)
7075 struct ElementInfo *ei = &element_info[element];
7078 // ---------- custom element base property values (96 bytes) ----------------
7080 putFile16BitBE(file, element);
7082 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
7083 putFile8Bit(file, ei->description[i]);
7085 putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
7087 WriteUnusedBytesToFile(file, 4); // reserved for more base properties
7089 putFile8Bit(file, ei->num_change_pages);
7091 putFile16BitBE(file, ei->ce_value_fixed_initial);
7092 putFile16BitBE(file, ei->ce_value_random_initial);
7093 putFile8Bit(file, ei->use_last_ce_value);
7095 putFile8Bit(file, ei->use_gfx_element);
7096 putFile16BitBE(file, ei->gfx_element_initial);
7098 putFile8Bit(file, ei->collect_score_initial);
7099 putFile8Bit(file, ei->collect_count_initial);
7101 putFile8Bit(file, ei->drop_delay_fixed);
7102 putFile8Bit(file, ei->push_delay_fixed);
7103 putFile8Bit(file, ei->drop_delay_random);
7104 putFile8Bit(file, ei->push_delay_random);
7105 putFile16BitBE(file, ei->move_delay_fixed);
7106 putFile16BitBE(file, ei->move_delay_random);
7108 // bits 0 - 15 of "move_pattern" ...
7109 putFile16BitBE(file, ei->move_pattern & 0xffff);
7110 putFile8Bit(file, ei->move_direction_initial);
7111 putFile8Bit(file, ei->move_stepsize);
7113 putFile8Bit(file, ei->slippery_type);
7115 for (y = 0; y < 3; y++)
7116 for (x = 0; x < 3; x++)
7117 putFile16BitBE(file, ei->content.e[x][y]);
7119 putFile16BitBE(file, ei->move_enter_element);
7120 putFile16BitBE(file, ei->move_leave_element);
7121 putFile8Bit(file, ei->move_leave_type);
7123 // ... bits 16 - 31 of "move_pattern" (not nice, but downward compatible)
7124 putFile16BitBE(file, (ei->move_pattern >> 16) & 0xffff);
7126 putFile8Bit(file, ei->access_direction);
7128 putFile8Bit(file, ei->explosion_delay);
7129 putFile8Bit(file, ei->ignition_delay);
7130 putFile8Bit(file, ei->explosion_type);
7132 // some free bytes for future custom property values and padding
7133 WriteUnusedBytesToFile(file, 1);
7135 // ---------- change page property values (48 bytes) ------------------------
7137 for (i = 0; i < ei->num_change_pages; i++)
7139 struct ElementChangeInfo *change = &ei->change_page[i];
7140 unsigned int event_bits;
7142 // bits 0 - 31 of "has_event[]" ...
7144 for (j = 0; j < MIN(NUM_CHANGE_EVENTS, 32); j++)
7145 if (change->has_event[j])
7146 event_bits |= (1 << j);
7147 putFile32BitBE(file, event_bits);
7149 putFile16BitBE(file, change->target_element);
7151 putFile16BitBE(file, change->delay_fixed);
7152 putFile16BitBE(file, change->delay_random);
7153 putFile16BitBE(file, change->delay_frames);
7155 putFile16BitBE(file, change->initial_trigger_element);
7157 putFile8Bit(file, change->explode);
7158 putFile8Bit(file, change->use_target_content);
7159 putFile8Bit(file, change->only_if_complete);
7160 putFile8Bit(file, change->use_random_replace);
7162 putFile8Bit(file, change->random_percentage);
7163 putFile8Bit(file, change->replace_when);
7165 for (y = 0; y < 3; y++)
7166 for (x = 0; x < 3; x++)
7167 putFile16BitBE(file, change->target_content.e[x][y]);
7169 putFile8Bit(file, change->can_change);
7171 putFile8Bit(file, change->trigger_side);
7173 putFile8Bit(file, change->trigger_player);
7174 putFile8Bit(file, (change->trigger_page == CH_PAGE_ANY ? CH_PAGE_ANY_FILE :
7175 log_2(change->trigger_page)));
7177 putFile8Bit(file, change->has_action);
7178 putFile8Bit(file, change->action_type);
7179 putFile8Bit(file, change->action_mode);
7180 putFile16BitBE(file, change->action_arg);
7182 // ... bits 32 - 39 of "has_event[]" (not nice, but downward compatible)
7184 for (j = 32; j < NUM_CHANGE_EVENTS; j++)
7185 if (change->has_event[j])
7186 event_bits |= (1 << (j - 32));
7187 putFile8Bit(file, event_bits);
7192 #if ENABLE_HISTORIC_CHUNKS
7193 static void SaveLevel_GRP1(FILE *file, struct LevelInfo *level, int element)
7195 struct ElementInfo *ei = &element_info[element];
7196 struct ElementGroupInfo *group = ei->group;
7199 putFile16BitBE(file, element);
7201 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
7202 putFile8Bit(file, ei->description[i]);
7204 putFile8Bit(file, group->num_elements);
7206 putFile8Bit(file, ei->use_gfx_element);
7207 putFile16BitBE(file, ei->gfx_element_initial);
7209 putFile8Bit(file, group->choice_mode);
7211 // some free bytes for future values and padding
7212 WriteUnusedBytesToFile(file, 3);
7214 for (i = 0; i < MAX_ELEMENTS_IN_GROUP; i++)
7215 putFile16BitBE(file, group->element[i]);
7219 static int SaveLevel_MicroChunk(FILE *file, struct LevelFileConfigInfo *entry,
7220 boolean write_element)
7222 int save_type = entry->save_type;
7223 int data_type = entry->data_type;
7224 int conf_type = entry->conf_type;
7225 int byte_mask = conf_type & CONF_MASK_BYTES;
7226 int element = entry->element;
7227 int default_value = entry->default_value;
7229 boolean modified = FALSE;
7231 if (byte_mask != CONF_MASK_MULTI_BYTES)
7233 void *value_ptr = entry->value;
7234 int value = (data_type == TYPE_BOOLEAN ? *(boolean *)value_ptr :
7237 // check if any settings have been modified before saving them
7238 if (value != default_value)
7241 // do not save if explicitly told or if unmodified default settings
7242 if ((save_type == SAVE_CONF_NEVER) ||
7243 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
7247 num_bytes += putFile16BitBE(file, element);
7249 num_bytes += putFile8Bit(file, conf_type);
7250 num_bytes += (byte_mask == CONF_MASK_1_BYTE ? putFile8Bit (file, value) :
7251 byte_mask == CONF_MASK_2_BYTE ? putFile16BitBE(file, value) :
7252 byte_mask == CONF_MASK_4_BYTE ? putFile32BitBE(file, value) :
7255 else if (data_type == TYPE_STRING)
7257 char *default_string = entry->default_string;
7258 char *string = (char *)(entry->value);
7259 int string_length = strlen(string);
7262 // check if any settings have been modified before saving them
7263 if (!strEqual(string, default_string))
7266 // do not save if explicitly told or if unmodified default settings
7267 if ((save_type == SAVE_CONF_NEVER) ||
7268 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
7272 num_bytes += putFile16BitBE(file, element);
7274 num_bytes += putFile8Bit(file, conf_type);
7275 num_bytes += putFile16BitBE(file, string_length);
7277 for (i = 0; i < string_length; i++)
7278 num_bytes += putFile8Bit(file, string[i]);
7280 else if (data_type == TYPE_ELEMENT_LIST)
7282 int *element_array = (int *)(entry->value);
7283 int num_elements = *(int *)(entry->num_entities);
7286 // check if any settings have been modified before saving them
7287 for (i = 0; i < num_elements; i++)
7288 if (element_array[i] != default_value)
7291 // do not save if explicitly told or if unmodified default settings
7292 if ((save_type == SAVE_CONF_NEVER) ||
7293 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
7297 num_bytes += putFile16BitBE(file, element);
7299 num_bytes += putFile8Bit(file, conf_type);
7300 num_bytes += putFile16BitBE(file, num_elements * CONF_ELEMENT_NUM_BYTES);
7302 for (i = 0; i < num_elements; i++)
7303 num_bytes += putFile16BitBE(file, element_array[i]);
7305 else if (data_type == TYPE_CONTENT_LIST)
7307 struct Content *content = (struct Content *)(entry->value);
7308 int num_contents = *(int *)(entry->num_entities);
7311 // check if any settings have been modified before saving them
7312 for (i = 0; i < num_contents; i++)
7313 for (y = 0; y < 3; y++)
7314 for (x = 0; x < 3; x++)
7315 if (content[i].e[x][y] != default_value)
7318 // do not save if explicitly told or if unmodified default settings
7319 if ((save_type == SAVE_CONF_NEVER) ||
7320 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
7324 num_bytes += putFile16BitBE(file, element);
7326 num_bytes += putFile8Bit(file, conf_type);
7327 num_bytes += putFile16BitBE(file, num_contents * CONF_CONTENT_NUM_BYTES);
7329 for (i = 0; i < num_contents; i++)
7330 for (y = 0; y < 3; y++)
7331 for (x = 0; x < 3; x++)
7332 num_bytes += putFile16BitBE(file, content[i].e[x][y]);
7338 static int SaveLevel_INFO(FILE *file, struct LevelInfo *level)
7343 li = *level; // copy level data into temporary buffer
7345 for (i = 0; chunk_config_INFO[i].data_type != -1; i++)
7346 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_INFO[i], FALSE);
7351 static int SaveLevel_ELEM(FILE *file, struct LevelInfo *level)
7356 li = *level; // copy level data into temporary buffer
7358 for (i = 0; chunk_config_ELEM[i].data_type != -1; i++)
7359 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_ELEM[i], TRUE);
7364 static int SaveLevel_NOTE(FILE *file, struct LevelInfo *level, int element)
7366 int envelope_nr = element - EL_ENVELOPE_1;
7370 chunk_size += putFile16BitBE(file, element);
7372 // copy envelope data into temporary buffer
7373 xx_envelope = level->envelope[envelope_nr];
7375 for (i = 0; chunk_config_NOTE[i].data_type != -1; i++)
7376 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_NOTE[i], FALSE);
7381 static int SaveLevel_CUSX(FILE *file, struct LevelInfo *level, int element)
7383 struct ElementInfo *ei = &element_info[element];
7387 chunk_size += putFile16BitBE(file, element);
7389 xx_ei = *ei; // copy element data into temporary buffer
7391 // set default description string for this specific element
7392 strcpy(xx_default_description, getDefaultElementDescription(ei));
7394 for (i = 0; chunk_config_CUSX_base[i].data_type != -1; i++)
7395 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_CUSX_base[i], FALSE);
7397 for (i = 0; i < ei->num_change_pages; i++)
7399 struct ElementChangeInfo *change = &ei->change_page[i];
7401 xx_current_change_page = i;
7403 xx_change = *change; // copy change data into temporary buffer
7406 setEventBitsFromEventFlags(change);
7408 for (j = 0; chunk_config_CUSX_change[j].data_type != -1; j++)
7409 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_CUSX_change[j],
7416 static int SaveLevel_GRPX(FILE *file, struct LevelInfo *level, int element)
7418 struct ElementInfo *ei = &element_info[element];
7419 struct ElementGroupInfo *group = ei->group;
7423 chunk_size += putFile16BitBE(file, element);
7425 xx_ei = *ei; // copy element data into temporary buffer
7426 xx_group = *group; // copy group data into temporary buffer
7428 // set default description string for this specific element
7429 strcpy(xx_default_description, getDefaultElementDescription(ei));
7431 for (i = 0; chunk_config_GRPX[i].data_type != -1; i++)
7432 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_GRPX[i], FALSE);
7437 static void SaveLevelFromFilename(struct LevelInfo *level, char *filename,
7438 boolean save_as_template)
7444 if (!(file = fopen(filename, MODE_WRITE)))
7446 Error(ERR_WARN, "cannot save level file '%s'", filename);
7450 level->file_version = FILE_VERSION_ACTUAL;
7451 level->game_version = GAME_VERSION_ACTUAL;
7453 level->creation_date = getCurrentDate();
7455 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
7456 putFileChunkBE(file, "CAVE", CHUNK_SIZE_NONE);
7458 chunk_size = SaveLevel_VERS(NULL, level);
7459 putFileChunkBE(file, "VERS", chunk_size);
7460 SaveLevel_VERS(file, level);
7462 chunk_size = SaveLevel_DATE(NULL, level);
7463 putFileChunkBE(file, "DATE", chunk_size);
7464 SaveLevel_DATE(file, level);
7466 chunk_size = SaveLevel_NAME(NULL, level);
7467 putFileChunkBE(file, "NAME", chunk_size);
7468 SaveLevel_NAME(file, level);
7470 chunk_size = SaveLevel_AUTH(NULL, level);
7471 putFileChunkBE(file, "AUTH", chunk_size);
7472 SaveLevel_AUTH(file, level);
7474 chunk_size = SaveLevel_INFO(NULL, level);
7475 putFileChunkBE(file, "INFO", chunk_size);
7476 SaveLevel_INFO(file, level);
7478 chunk_size = SaveLevel_BODY(NULL, level);
7479 putFileChunkBE(file, "BODY", chunk_size);
7480 SaveLevel_BODY(file, level);
7482 chunk_size = SaveLevel_ELEM(NULL, level);
7483 if (chunk_size > LEVEL_CHUNK_ELEM_UNCHANGED) // save if changed
7485 putFileChunkBE(file, "ELEM", chunk_size);
7486 SaveLevel_ELEM(file, level);
7489 for (i = 0; i < NUM_ENVELOPES; i++)
7491 int element = EL_ENVELOPE_1 + i;
7493 chunk_size = SaveLevel_NOTE(NULL, level, element);
7494 if (chunk_size > LEVEL_CHUNK_NOTE_UNCHANGED) // save if changed
7496 putFileChunkBE(file, "NOTE", chunk_size);
7497 SaveLevel_NOTE(file, level, element);
7501 // if not using template level, check for non-default custom/group elements
7502 if (!level->use_custom_template || save_as_template)
7504 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7506 int element = EL_CUSTOM_START + i;
7508 chunk_size = SaveLevel_CUSX(NULL, level, element);
7509 if (chunk_size > LEVEL_CHUNK_CUSX_UNCHANGED) // save if changed
7511 putFileChunkBE(file, "CUSX", chunk_size);
7512 SaveLevel_CUSX(file, level, element);
7516 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
7518 int element = EL_GROUP_START + i;
7520 chunk_size = SaveLevel_GRPX(NULL, level, element);
7521 if (chunk_size > LEVEL_CHUNK_GRPX_UNCHANGED) // save if changed
7523 putFileChunkBE(file, "GRPX", chunk_size);
7524 SaveLevel_GRPX(file, level, element);
7531 SetFilePermissions(filename, PERMS_PRIVATE);
7534 void SaveLevel(int nr)
7536 char *filename = getDefaultLevelFilename(nr);
7538 SaveLevelFromFilename(&level, filename, FALSE);
7541 void SaveLevelTemplate(void)
7543 char *filename = getLocalLevelTemplateFilename();
7545 SaveLevelFromFilename(&level, filename, TRUE);
7548 boolean SaveLevelChecked(int nr)
7550 char *filename = getDefaultLevelFilename(nr);
7551 boolean new_level = !fileExists(filename);
7552 boolean level_saved = FALSE;
7554 if (new_level || Request("Save this level and kill the old?", REQ_ASK))
7559 Request("Level saved!", REQ_CONFIRM);
7567 void DumpLevel(struct LevelInfo *level)
7569 if (level->no_level_file || level->no_valid_file)
7571 Error(ERR_WARN, "cannot dump -- no valid level file found");
7577 Print("Level xxx (file version %08d, game version %08d)\n",
7578 level->file_version, level->game_version);
7581 Print("Level author: '%s'\n", level->author);
7582 Print("Level title: '%s'\n", level->name);
7584 Print("Playfield size: %d x %d\n", level->fieldx, level->fieldy);
7586 Print("Level time: %d seconds\n", level->time);
7587 Print("Gems needed: %d\n", level->gems_needed);
7589 Print("Time for magic wall: %d seconds\n", level->time_magic_wall);
7590 Print("Time for wheel: %d seconds\n", level->time_wheel);
7591 Print("Time for light: %d seconds\n", level->time_light);
7592 Print("Time for timegate: %d seconds\n", level->time_timegate);
7594 Print("Amoeba speed: %d\n", level->amoeba_speed);
7597 Print("EM style slippery gems: %s\n", (level->em_slippery_gems ? "yes" : "no"));
7598 Print("Player blocks last field: %s\n", (level->block_last_field ? "yes" : "no"));
7599 Print("SP player blocks last field: %s\n", (level->sp_block_last_field ? "yes" : "no"));
7600 Print("use spring bug: %s\n", (level->use_spring_bug ? "yes" : "no"));
7601 Print("use step counter: %s\n", (level->use_step_counter ? "yes" : "no"));
7607 // ============================================================================
7608 // tape file functions
7609 // ============================================================================
7611 static void setTapeInfoToDefaults(void)
7615 // always start with reliable default values (empty tape)
7618 // default values (also for pre-1.2 tapes) with only the first player
7619 tape.player_participates[0] = TRUE;
7620 for (i = 1; i < MAX_PLAYERS; i++)
7621 tape.player_participates[i] = FALSE;
7623 // at least one (default: the first) player participates in every tape
7624 tape.num_participating_players = 1;
7626 tape.level_nr = level_nr;
7628 tape.changed = FALSE;
7630 tape.recording = FALSE;
7631 tape.playing = FALSE;
7632 tape.pausing = FALSE;
7634 tape.no_valid_file = FALSE;
7637 static int LoadTape_VERS(File *file, int chunk_size, struct TapeInfo *tape)
7639 tape->file_version = getFileVersion(file);
7640 tape->game_version = getFileVersion(file);
7645 static int LoadTape_HEAD(File *file, int chunk_size, struct TapeInfo *tape)
7649 tape->random_seed = getFile32BitBE(file);
7650 tape->date = getFile32BitBE(file);
7651 tape->length = getFile32BitBE(file);
7653 // read header fields that are new since version 1.2
7654 if (tape->file_version >= FILE_VERSION_1_2)
7656 byte store_participating_players = getFile8Bit(file);
7659 // since version 1.2, tapes store which players participate in the tape
7660 tape->num_participating_players = 0;
7661 for (i = 0; i < MAX_PLAYERS; i++)
7663 tape->player_participates[i] = FALSE;
7665 if (store_participating_players & (1 << i))
7667 tape->player_participates[i] = TRUE;
7668 tape->num_participating_players++;
7672 tape->use_mouse = (getFile8Bit(file) == 1 ? TRUE : FALSE);
7674 ReadUnusedBytesFromFile(file, TAPE_CHUNK_HEAD_UNUSED);
7676 engine_version = getFileVersion(file);
7677 if (engine_version > 0)
7678 tape->engine_version = engine_version;
7680 tape->engine_version = tape->game_version;
7686 static int LoadTape_INFO(File *file, int chunk_size, struct TapeInfo *tape)
7688 int level_identifier_size;
7691 level_identifier_size = getFile16BitBE(file);
7693 tape->level_identifier =
7694 checked_realloc(tape->level_identifier, level_identifier_size);
7696 for (i = 0; i < level_identifier_size; i++)
7697 tape->level_identifier[i] = getFile8Bit(file);
7699 tape->level_nr = getFile16BitBE(file);
7701 chunk_size = 2 + level_identifier_size + 2;
7706 static int LoadTape_BODY(File *file, int chunk_size, struct TapeInfo *tape)
7710 (tape->use_mouse ? 3 : tape->num_participating_players) + 1;
7711 int chunk_size_expected = tape_pos_size * tape->length;
7713 if (chunk_size_expected != chunk_size)
7715 ReadUnusedBytesFromFile(file, chunk_size);
7716 return chunk_size_expected;
7719 for (i = 0; i < tape->length; i++)
7721 if (i >= MAX_TAPE_LEN)
7723 Error(ERR_WARN, "tape truncated -- size exceeds maximum tape size %d",
7726 // tape too large; read and ignore remaining tape data from this chunk
7727 for (;i < tape->length; i++)
7728 ReadUnusedBytesFromFile(file, tape->num_participating_players + 1);
7733 if (tape->use_mouse)
7735 tape->pos[i].action[TAPE_ACTION_LX] = getFile8Bit(file);
7736 tape->pos[i].action[TAPE_ACTION_LY] = getFile8Bit(file);
7737 tape->pos[i].action[TAPE_ACTION_BUTTON] = getFile8Bit(file);
7739 tape->pos[i].action[TAPE_ACTION_UNUSED] = 0;
7743 for (j = 0; j < MAX_PLAYERS; j++)
7745 tape->pos[i].action[j] = MV_NONE;
7747 if (tape->player_participates[j])
7748 tape->pos[i].action[j] = getFile8Bit(file);
7752 tape->pos[i].delay = getFile8Bit(file);
7754 if (tape->file_version == FILE_VERSION_1_0)
7756 // eliminate possible diagonal moves in old tapes
7757 // this is only for backward compatibility
7759 byte joy_dir[4] = { JOY_LEFT, JOY_RIGHT, JOY_UP, JOY_DOWN };
7760 byte action = tape->pos[i].action[0];
7761 int k, num_moves = 0;
7763 for (k = 0; k<4; k++)
7765 if (action & joy_dir[k])
7767 tape->pos[i + num_moves].action[0] = joy_dir[k];
7769 tape->pos[i + num_moves].delay = 0;
7778 tape->length += num_moves;
7781 else if (tape->file_version < FILE_VERSION_2_0)
7783 // convert pre-2.0 tapes to new tape format
7785 if (tape->pos[i].delay > 1)
7788 tape->pos[i + 1] = tape->pos[i];
7789 tape->pos[i + 1].delay = 1;
7792 for (j = 0; j < MAX_PLAYERS; j++)
7793 tape->pos[i].action[j] = MV_NONE;
7794 tape->pos[i].delay--;
7801 if (checkEndOfFile(file))
7805 if (i != tape->length)
7806 chunk_size = tape_pos_size * i;
7811 static void LoadTape_SokobanSolution(char *filename)
7814 int move_delay = TILESIZE / level.initial_player_stepsize[0];
7816 if (!(file = openFile(filename, MODE_READ)))
7818 tape.no_valid_file = TRUE;
7823 while (!checkEndOfFile(file))
7825 unsigned char c = getByteFromFile(file);
7827 if (checkEndOfFile(file))
7834 tape.pos[tape.length].action[0] = MV_UP;
7835 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
7841 tape.pos[tape.length].action[0] = MV_DOWN;
7842 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
7848 tape.pos[tape.length].action[0] = MV_LEFT;
7849 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
7855 tape.pos[tape.length].action[0] = MV_RIGHT;
7856 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
7864 // ignore white-space characters
7868 tape.no_valid_file = TRUE;
7870 Error(ERR_WARN, "unsupported Sokoban solution file '%s' ['%d']", filename, c);
7878 if (tape.no_valid_file)
7881 tape.length_frames = GetTapeLengthFrames();
7882 tape.length_seconds = GetTapeLengthSeconds();
7885 void LoadTapeFromFilename(char *filename)
7887 char cookie[MAX_LINE_LEN];
7888 char chunk_name[CHUNK_ID_LEN + 1];
7892 // always start with reliable default values
7893 setTapeInfoToDefaults();
7895 if (strSuffix(filename, ".sln"))
7897 LoadTape_SokobanSolution(filename);
7902 if (!(file = openFile(filename, MODE_READ)))
7904 tape.no_valid_file = TRUE;
7909 getFileChunkBE(file, chunk_name, NULL);
7910 if (strEqual(chunk_name, "RND1"))
7912 getFile32BitBE(file); // not used
7914 getFileChunkBE(file, chunk_name, NULL);
7915 if (!strEqual(chunk_name, "TAPE"))
7917 tape.no_valid_file = TRUE;
7919 Error(ERR_WARN, "unknown format of tape file '%s'", filename);
7926 else // check for pre-2.0 file format with cookie string
7928 strcpy(cookie, chunk_name);
7929 if (getStringFromFile(file, &cookie[4], MAX_LINE_LEN - 4) == NULL)
7931 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
7932 cookie[strlen(cookie) - 1] = '\0';
7934 if (!checkCookieString(cookie, TAPE_COOKIE_TMPL))
7936 tape.no_valid_file = TRUE;
7938 Error(ERR_WARN, "unknown format of tape file '%s'", filename);
7945 if ((tape.file_version = getFileVersionFromCookieString(cookie)) == -1)
7947 tape.no_valid_file = TRUE;
7949 Error(ERR_WARN, "unsupported version of tape file '%s'", filename);
7956 // pre-2.0 tape files have no game version, so use file version here
7957 tape.game_version = tape.file_version;
7960 if (tape.file_version < FILE_VERSION_1_2)
7962 // tape files from versions before 1.2.0 without chunk structure
7963 LoadTape_HEAD(file, TAPE_CHUNK_HEAD_SIZE, &tape);
7964 LoadTape_BODY(file, 2 * tape.length, &tape);
7972 int (*loader)(File *, int, struct TapeInfo *);
7976 { "VERS", TAPE_CHUNK_VERS_SIZE, LoadTape_VERS },
7977 { "HEAD", TAPE_CHUNK_HEAD_SIZE, LoadTape_HEAD },
7978 { "INFO", -1, LoadTape_INFO },
7979 { "BODY", -1, LoadTape_BODY },
7983 while (getFileChunkBE(file, chunk_name, &chunk_size))
7987 while (chunk_info[i].name != NULL &&
7988 !strEqual(chunk_name, chunk_info[i].name))
7991 if (chunk_info[i].name == NULL)
7993 Error(ERR_WARN, "unknown chunk '%s' in tape file '%s'",
7994 chunk_name, filename);
7995 ReadUnusedBytesFromFile(file, chunk_size);
7997 else if (chunk_info[i].size != -1 &&
7998 chunk_info[i].size != chunk_size)
8000 Error(ERR_WARN, "wrong size (%d) of chunk '%s' in tape file '%s'",
8001 chunk_size, chunk_name, filename);
8002 ReadUnusedBytesFromFile(file, chunk_size);
8006 // call function to load this tape chunk
8007 int chunk_size_expected =
8008 (chunk_info[i].loader)(file, chunk_size, &tape);
8010 // the size of some chunks cannot be checked before reading other
8011 // chunks first (like "HEAD" and "BODY") that contain some header
8012 // information, so check them here
8013 if (chunk_size_expected != chunk_size)
8015 Error(ERR_WARN, "wrong size (%d) of chunk '%s' in tape file '%s'",
8016 chunk_size, chunk_name, filename);
8024 tape.length_frames = GetTapeLengthFrames();
8025 tape.length_seconds = GetTapeLengthSeconds();
8028 printf("::: tape file version: %d\n", tape.file_version);
8029 printf("::: tape game version: %d\n", tape.game_version);
8030 printf("::: tape engine version: %d\n", tape.engine_version);
8034 void LoadTape(int nr)
8036 char *filename = getTapeFilename(nr);
8038 LoadTapeFromFilename(filename);
8041 void LoadSolutionTape(int nr)
8043 char *filename = getSolutionTapeFilename(nr);
8045 LoadTapeFromFilename(filename);
8047 if (TAPE_IS_EMPTY(tape) &&
8048 level.game_engine_type == GAME_ENGINE_TYPE_SP &&
8049 level.native_sp_level->demo.is_available)
8050 CopyNativeTape_SP_to_RND(&level);
8053 static void SaveTape_VERS(FILE *file, struct TapeInfo *tape)
8055 putFileVersion(file, tape->file_version);
8056 putFileVersion(file, tape->game_version);
8059 static void SaveTape_HEAD(FILE *file, struct TapeInfo *tape)
8062 byte store_participating_players = 0;
8064 // set bits for participating players for compact storage
8065 for (i = 0; i < MAX_PLAYERS; i++)
8066 if (tape->player_participates[i])
8067 store_participating_players |= (1 << i);
8069 putFile32BitBE(file, tape->random_seed);
8070 putFile32BitBE(file, tape->date);
8071 putFile32BitBE(file, tape->length);
8073 putFile8Bit(file, store_participating_players);
8075 putFile8Bit(file, (tape->use_mouse ? 1 : 0));
8077 // unused bytes not at the end here for 4-byte alignment of engine_version
8078 WriteUnusedBytesToFile(file, TAPE_CHUNK_HEAD_UNUSED);
8080 putFileVersion(file, tape->engine_version);
8083 static void SaveTape_INFO(FILE *file, struct TapeInfo *tape)
8085 int level_identifier_size = strlen(tape->level_identifier) + 1;
8088 putFile16BitBE(file, level_identifier_size);
8090 for (i = 0; i < level_identifier_size; i++)
8091 putFile8Bit(file, tape->level_identifier[i]);
8093 putFile16BitBE(file, tape->level_nr);
8096 static void SaveTape_BODY(FILE *file, struct TapeInfo *tape)
8100 for (i = 0; i < tape->length; i++)
8102 if (tape->use_mouse)
8104 putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_LX]);
8105 putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_LY]);
8106 putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_BUTTON]);
8110 for (j = 0; j < MAX_PLAYERS; j++)
8111 if (tape->player_participates[j])
8112 putFile8Bit(file, tape->pos[i].action[j]);
8115 putFile8Bit(file, tape->pos[i].delay);
8119 void SaveTape(int nr)
8121 char *filename = getTapeFilename(nr);
8123 int num_participating_players = 0;
8125 int info_chunk_size;
8126 int body_chunk_size;
8129 InitTapeDirectory(leveldir_current->subdir);
8131 if (!(file = fopen(filename, MODE_WRITE)))
8133 Error(ERR_WARN, "cannot save level recording file '%s'", filename);
8137 tape.file_version = FILE_VERSION_ACTUAL;
8138 tape.game_version = GAME_VERSION_ACTUAL;
8140 // count number of participating players
8141 for (i = 0; i < MAX_PLAYERS; i++)
8142 if (tape.player_participates[i])
8143 num_participating_players++;
8145 tape_pos_size = (tape.use_mouse ? 3 : num_participating_players) + 1;
8147 info_chunk_size = 2 + (strlen(tape.level_identifier) + 1) + 2;
8148 body_chunk_size = tape_pos_size * tape.length;
8150 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
8151 putFileChunkBE(file, "TAPE", CHUNK_SIZE_NONE);
8153 putFileChunkBE(file, "VERS", TAPE_CHUNK_VERS_SIZE);
8154 SaveTape_VERS(file, &tape);
8156 putFileChunkBE(file, "HEAD", TAPE_CHUNK_HEAD_SIZE);
8157 SaveTape_HEAD(file, &tape);
8159 putFileChunkBE(file, "INFO", info_chunk_size);
8160 SaveTape_INFO(file, &tape);
8162 putFileChunkBE(file, "BODY", body_chunk_size);
8163 SaveTape_BODY(file, &tape);
8167 SetFilePermissions(filename, PERMS_PRIVATE);
8169 tape.changed = FALSE;
8172 static boolean SaveTapeCheckedExt(int nr, char *msg_replace, char *msg_saved,
8173 unsigned int req_state_added)
8175 char *filename = getTapeFilename(nr);
8176 boolean new_tape = !fileExists(filename);
8177 boolean tape_saved = FALSE;
8179 if (new_tape || Request(msg_replace, REQ_ASK | req_state_added))
8184 Request(msg_saved, REQ_CONFIRM | req_state_added);
8192 boolean SaveTapeChecked(int nr)
8194 return SaveTapeCheckedExt(nr, "Replace old tape?", "Tape saved!", 0);
8197 boolean SaveTapeChecked_LevelSolved(int nr)
8199 return SaveTapeCheckedExt(nr, "Level solved! Replace old tape?",
8200 "Level solved! Tape saved!", REQ_STAY_OPEN);
8203 void DumpTape(struct TapeInfo *tape)
8205 int tape_frame_counter;
8208 if (tape->no_valid_file)
8210 Error(ERR_WARN, "cannot dump -- no valid tape file found");
8216 Print("Tape of Level %03d (file version %08d, game version %08d)\n",
8217 tape->level_nr, tape->file_version, tape->game_version);
8218 Print(" (effective engine version %08d)\n",
8219 tape->engine_version);
8220 Print("Level series identifier: '%s'\n", tape->level_identifier);
8223 tape_frame_counter = 0;
8225 for (i = 0; i < tape->length; i++)
8227 if (i >= MAX_TAPE_LEN)
8232 for (j = 0; j < MAX_PLAYERS; j++)
8234 if (tape->player_participates[j])
8236 int action = tape->pos[i].action[j];
8238 Print("%d:%02x ", j, action);
8239 Print("[%c%c%c%c|%c%c] - ",
8240 (action & JOY_LEFT ? '<' : ' '),
8241 (action & JOY_RIGHT ? '>' : ' '),
8242 (action & JOY_UP ? '^' : ' '),
8243 (action & JOY_DOWN ? 'v' : ' '),
8244 (action & JOY_BUTTON_1 ? '1' : ' '),
8245 (action & JOY_BUTTON_2 ? '2' : ' '));
8249 Print("(%03d) ", tape->pos[i].delay);
8250 Print("[%05d]\n", tape_frame_counter);
8252 tape_frame_counter += tape->pos[i].delay;
8259 // ============================================================================
8260 // score file functions
8261 // ============================================================================
8263 void LoadScore(int nr)
8266 char *filename = getScoreFilename(nr);
8267 char cookie[MAX_LINE_LEN];
8268 char line[MAX_LINE_LEN];
8272 // always start with reliable default values
8273 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
8275 strcpy(highscore[i].Name, EMPTY_PLAYER_NAME);
8276 highscore[i].Score = 0;
8279 if (!(file = fopen(filename, MODE_READ)))
8282 // check file identifier
8283 if (fgets(cookie, MAX_LINE_LEN, file) == NULL)
8285 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
8286 cookie[strlen(cookie) - 1] = '\0';
8288 if (!checkCookieString(cookie, SCORE_COOKIE))
8290 Error(ERR_WARN, "unknown format of score file '%s'", filename);
8295 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
8297 if (fscanf(file, "%d", &highscore[i].Score) == EOF)
8298 Error(ERR_WARN, "fscanf() failed; %s", strerror(errno));
8299 if (fgets(line, MAX_LINE_LEN, file) == NULL)
8302 if (strlen(line) > 0 && line[strlen(line) - 1] == '\n')
8303 line[strlen(line) - 1] = '\0';
8305 for (line_ptr = line; *line_ptr; line_ptr++)
8307 if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != '\0')
8309 strncpy(highscore[i].Name, line_ptr, MAX_PLAYER_NAME_LEN);
8310 highscore[i].Name[MAX_PLAYER_NAME_LEN] = '\0';
8319 void SaveScore(int nr)
8322 int permissions = (program.global_scores ? PERMS_PUBLIC : PERMS_PRIVATE);
8323 char *filename = getScoreFilename(nr);
8326 // used instead of "leveldir_current->subdir" (for network games)
8327 InitScoreDirectory(levelset.identifier);
8329 if (!(file = fopen(filename, MODE_WRITE)))
8331 Error(ERR_WARN, "cannot save score for level %d", nr);
8335 fprintf(file, "%s\n\n", SCORE_COOKIE);
8337 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
8338 fprintf(file, "%d %s\n", highscore[i].Score, highscore[i].Name);
8342 SetFilePermissions(filename, permissions);
8346 // ============================================================================
8347 // setup file functions
8348 // ============================================================================
8350 #define TOKEN_STR_PLAYER_PREFIX "player_"
8353 static struct TokenInfo global_setup_tokens[] =
8357 &setup.player_name, "player_name"
8361 &setup.sound, "sound"
8365 &setup.sound_loops, "repeating_sound_loops"
8369 &setup.sound_music, "background_music"
8373 &setup.sound_simple, "simple_sound_effects"
8377 &setup.toons, "toons"
8381 &setup.scroll_delay, "scroll_delay"
8385 &setup.forced_scroll_delay, "forced_scroll_delay"
8389 &setup.scroll_delay_value, "scroll_delay_value"
8393 &setup.engine_snapshot_mode, "engine_snapshot_mode"
8397 &setup.engine_snapshot_memory, "engine_snapshot_memory"
8401 &setup.fade_screens, "fade_screens"
8405 &setup.autorecord, "automatic_tape_recording"
8409 &setup.show_titlescreen, "show_titlescreen"
8413 &setup.quick_doors, "quick_doors"
8417 &setup.team_mode, "team_mode"
8421 &setup.handicap, "handicap"
8425 &setup.skip_levels, "skip_levels"
8429 &setup.increment_levels, "increment_levels"
8433 &setup.auto_play_next_level, "auto_play_next_level"
8437 &setup.skip_scores_after_game, "skip_scores_after_game"
8441 &setup.time_limit, "time_limit"
8445 &setup.fullscreen, "fullscreen"
8449 &setup.window_scaling_percent, "window_scaling_percent"
8453 &setup.window_scaling_quality, "window_scaling_quality"
8457 &setup.screen_rendering_mode, "screen_rendering_mode"
8461 &setup.vsync_mode, "vsync_mode"
8465 &setup.ask_on_escape, "ask_on_escape"
8469 &setup.ask_on_escape_editor, "ask_on_escape_editor"
8473 &setup.ask_on_game_over, "ask_on_game_over"
8477 &setup.quick_switch, "quick_player_switch"
8481 &setup.input_on_focus, "input_on_focus"
8485 &setup.prefer_aga_graphics, "prefer_aga_graphics"
8489 &setup.game_speed_extended, "game_speed_extended"
8493 &setup.game_frame_delay, "game_frame_delay"
8497 &setup.sp_show_border_elements, "sp_show_border_elements"
8501 &setup.small_game_graphics, "small_game_graphics"
8505 &setup.show_snapshot_buttons, "show_snapshot_buttons"
8509 &setup.graphics_set, "graphics_set"
8513 &setup.sounds_set, "sounds_set"
8517 &setup.music_set, "music_set"
8521 &setup.override_level_graphics, "override_level_graphics"
8525 &setup.override_level_sounds, "override_level_sounds"
8529 &setup.override_level_music, "override_level_music"
8533 &setup.volume_simple, "volume_simple"
8537 &setup.volume_loops, "volume_loops"
8541 &setup.volume_music, "volume_music"
8545 &setup.network_mode, "network_mode"
8549 &setup.network_player_nr, "network_player"
8553 &setup.network_server_hostname, "network_server_hostname"
8557 &setup.touch.control_type, "touch.control_type"
8561 &setup.touch.move_distance, "touch.move_distance"
8565 &setup.touch.drop_distance, "touch.drop_distance"
8569 &setup.touch.transparency, "touch.transparency"
8573 &setup.touch.draw_outlined, "touch.draw_outlined"
8577 &setup.touch.draw_pressed, "touch.draw_pressed"
8581 &setup.touch.grid_xsize[0], "touch.virtual_buttons.0.xsize"
8585 &setup.touch.grid_ysize[0], "touch.virtual_buttons.0.ysize"
8589 &setup.touch.grid_xsize[1], "touch.virtual_buttons.1.xsize"
8593 &setup.touch.grid_ysize[1], "touch.virtual_buttons.1.ysize"
8597 static struct TokenInfo auto_setup_tokens[] =
8601 &setup.auto_setup.editor_zoom_tilesize, "editor.zoom_tilesize"
8605 static struct TokenInfo editor_setup_tokens[] =
8609 &setup.editor.el_classic, "editor.el_classic"
8613 &setup.editor.el_custom, "editor.el_custom"
8617 &setup.editor.el_user_defined, "editor.el_user_defined"
8621 &setup.editor.el_dynamic, "editor.el_dynamic"
8625 &setup.editor.el_headlines, "editor.el_headlines"
8629 &setup.editor.show_element_token, "editor.show_element_token"
8633 static struct TokenInfo editor_cascade_setup_tokens[] =
8637 &setup.editor_cascade.el_bd, "editor.cascade.el_bd"
8641 &setup.editor_cascade.el_em, "editor.cascade.el_em"
8645 &setup.editor_cascade.el_emc, "editor.cascade.el_emc"
8649 &setup.editor_cascade.el_rnd, "editor.cascade.el_rnd"
8653 &setup.editor_cascade.el_sb, "editor.cascade.el_sb"
8657 &setup.editor_cascade.el_sp, "editor.cascade.el_sp"
8661 &setup.editor_cascade.el_dc, "editor.cascade.el_dc"
8665 &setup.editor_cascade.el_dx, "editor.cascade.el_dx"
8669 &setup.editor_cascade.el_mm, "editor.cascade.el_mm"
8673 &setup.editor_cascade.el_df, "editor.cascade.el_df"
8677 &setup.editor_cascade.el_chars, "editor.cascade.el_chars"
8681 &setup.editor_cascade.el_steel_chars, "editor.cascade.el_steel_chars"
8685 &setup.editor_cascade.el_ce, "editor.cascade.el_ce"
8689 &setup.editor_cascade.el_ge, "editor.cascade.el_ge"
8693 &setup.editor_cascade.el_ref, "editor.cascade.el_ref"
8697 &setup.editor_cascade.el_user, "editor.cascade.el_user"
8701 &setup.editor_cascade.el_dynamic, "editor.cascade.el_dynamic"
8705 static struct TokenInfo shortcut_setup_tokens[] =
8709 &setup.shortcut.save_game, "shortcut.save_game"
8713 &setup.shortcut.load_game, "shortcut.load_game"
8717 &setup.shortcut.toggle_pause, "shortcut.toggle_pause"
8721 &setup.shortcut.focus_player[0], "shortcut.focus_player_1"
8725 &setup.shortcut.focus_player[1], "shortcut.focus_player_2"
8729 &setup.shortcut.focus_player[2], "shortcut.focus_player_3"
8733 &setup.shortcut.focus_player[3], "shortcut.focus_player_4"
8737 &setup.shortcut.focus_player_all, "shortcut.focus_player_all"
8741 &setup.shortcut.tape_eject, "shortcut.tape_eject"
8745 &setup.shortcut.tape_extra, "shortcut.tape_extra"
8749 &setup.shortcut.tape_stop, "shortcut.tape_stop"
8753 &setup.shortcut.tape_pause, "shortcut.tape_pause"
8757 &setup.shortcut.tape_record, "shortcut.tape_record"
8761 &setup.shortcut.tape_play, "shortcut.tape_play"
8765 &setup.shortcut.sound_simple, "shortcut.sound_simple"
8769 &setup.shortcut.sound_loops, "shortcut.sound_loops"
8773 &setup.shortcut.sound_music, "shortcut.sound_music"
8777 &setup.shortcut.snap_left, "shortcut.snap_left"
8781 &setup.shortcut.snap_right, "shortcut.snap_right"
8785 &setup.shortcut.snap_up, "shortcut.snap_up"
8789 &setup.shortcut.snap_down, "shortcut.snap_down"
8793 static struct SetupInputInfo setup_input;
8794 static struct TokenInfo player_setup_tokens[] =
8798 &setup_input.use_joystick, ".use_joystick"
8802 &setup_input.joy.device_name, ".joy.device_name"
8806 &setup_input.joy.xleft, ".joy.xleft"
8810 &setup_input.joy.xmiddle, ".joy.xmiddle"
8814 &setup_input.joy.xright, ".joy.xright"
8818 &setup_input.joy.yupper, ".joy.yupper"
8822 &setup_input.joy.ymiddle, ".joy.ymiddle"
8826 &setup_input.joy.ylower, ".joy.ylower"
8830 &setup_input.joy.snap, ".joy.snap_field"
8834 &setup_input.joy.drop, ".joy.place_bomb"
8838 &setup_input.key.left, ".key.move_left"
8842 &setup_input.key.right, ".key.move_right"
8846 &setup_input.key.up, ".key.move_up"
8850 &setup_input.key.down, ".key.move_down"
8854 &setup_input.key.snap, ".key.snap_field"
8858 &setup_input.key.drop, ".key.place_bomb"
8862 static struct TokenInfo system_setup_tokens[] =
8866 &setup.system.sdl_videodriver, "system.sdl_videodriver"
8870 &setup.system.sdl_audiodriver, "system.sdl_audiodriver"
8874 &setup.system.audio_fragment_size, "system.audio_fragment_size"
8878 static struct TokenInfo internal_setup_tokens[] =
8882 &setup.internal.program_title, "program_title"
8886 &setup.internal.program_version, "program_version"
8890 &setup.internal.program_author, "program_author"
8894 &setup.internal.program_email, "program_email"
8898 &setup.internal.program_website, "program_website"
8902 &setup.internal.program_copyright, "program_copyright"
8906 &setup.internal.program_company, "program_company"
8910 &setup.internal.program_icon_file, "program_icon_file"
8914 &setup.internal.default_graphics_set, "default_graphics_set"
8918 &setup.internal.default_sounds_set, "default_sounds_set"
8922 &setup.internal.default_music_set, "default_music_set"
8926 &setup.internal.fallback_graphics_file, "fallback_graphics_file"
8930 &setup.internal.fallback_sounds_file, "fallback_sounds_file"
8934 &setup.internal.fallback_music_file, "fallback_music_file"
8938 &setup.internal.default_level_series, "default_level_series"
8942 &setup.internal.default_window_width, "default_window_width"
8946 &setup.internal.default_window_height, "default_window_height"
8950 &setup.internal.choose_from_top_leveldir, "choose_from_top_leveldir"
8954 &setup.internal.show_scaling_in_title, "show_scaling_in_title"
8958 &setup.internal.create_user_levelset, "create_user_levelset"
8962 &setup.internal.menu_game, "menu_game"
8966 &setup.internal.menu_editor, "menu_editor"
8970 &setup.internal.menu_graphics, "menu_graphics"
8974 &setup.internal.menu_sound, "menu_sound"
8978 &setup.internal.menu_artwork, "menu_artwork"
8982 &setup.internal.menu_input, "menu_input"
8986 &setup.internal.menu_touch, "menu_touch"
8990 &setup.internal.menu_shortcuts, "menu_shortcuts"
8994 &setup.internal.menu_exit, "menu_exit"
8998 &setup.internal.menu_save_and_exit, "menu_save_and_exit"
9002 static struct TokenInfo debug_setup_tokens[] =
9006 &setup.debug.frame_delay[0], "debug.frame_delay_0"
9010 &setup.debug.frame_delay[1], "debug.frame_delay_1"
9014 &setup.debug.frame_delay[2], "debug.frame_delay_2"
9018 &setup.debug.frame_delay[3], "debug.frame_delay_3"
9022 &setup.debug.frame_delay[4], "debug.frame_delay_4"
9026 &setup.debug.frame_delay[5], "debug.frame_delay_5"
9030 &setup.debug.frame_delay[6], "debug.frame_delay_6"
9034 &setup.debug.frame_delay[7], "debug.frame_delay_7"
9038 &setup.debug.frame_delay[8], "debug.frame_delay_8"
9042 &setup.debug.frame_delay[9], "debug.frame_delay_9"
9046 &setup.debug.frame_delay_key[0], "debug.key.frame_delay_0"
9050 &setup.debug.frame_delay_key[1], "debug.key.frame_delay_1"
9054 &setup.debug.frame_delay_key[2], "debug.key.frame_delay_2"
9058 &setup.debug.frame_delay_key[3], "debug.key.frame_delay_3"
9062 &setup.debug.frame_delay_key[4], "debug.key.frame_delay_4"
9066 &setup.debug.frame_delay_key[5], "debug.key.frame_delay_5"
9070 &setup.debug.frame_delay_key[6], "debug.key.frame_delay_6"
9074 &setup.debug.frame_delay_key[7], "debug.key.frame_delay_7"
9078 &setup.debug.frame_delay_key[8], "debug.key.frame_delay_8"
9082 &setup.debug.frame_delay_key[9], "debug.key.frame_delay_9"
9086 &setup.debug.frame_delay_use_mod_key, "debug.frame_delay.use_mod_key"},
9089 &setup.debug.frame_delay_game_only, "debug.frame_delay.game_only"
9093 &setup.debug.show_frames_per_second, "debug.show_frames_per_second"
9097 static struct TokenInfo options_setup_tokens[] =
9101 &setup.options.verbose, "options.verbose"
9105 static char *get_corrected_login_name(char *login_name)
9107 // needed because player name must be a fixed length string
9108 char *login_name_new = checked_malloc(MAX_PLAYER_NAME_LEN + 1);
9110 strncpy(login_name_new, login_name, MAX_PLAYER_NAME_LEN);
9111 login_name_new[MAX_PLAYER_NAME_LEN] = '\0';
9113 if (strlen(login_name) > MAX_PLAYER_NAME_LEN) // name has been cut
9114 if (strchr(login_name_new, ' '))
9115 *strchr(login_name_new, ' ') = '\0';
9117 return login_name_new;
9120 static void setSetupInfoToDefaults(struct SetupInfo *si)
9124 si->player_name = get_corrected_login_name(getLoginName());
9127 si->sound_loops = TRUE;
9128 si->sound_music = TRUE;
9129 si->sound_simple = TRUE;
9131 si->scroll_delay = TRUE;
9132 si->forced_scroll_delay = FALSE;
9133 si->scroll_delay_value = STD_SCROLL_DELAY;
9134 si->engine_snapshot_mode = getStringCopy(STR_SNAPSHOT_MODE_DEFAULT);
9135 si->engine_snapshot_memory = SNAPSHOT_MEMORY_DEFAULT;
9136 si->fade_screens = TRUE;
9137 si->autorecord = TRUE;
9138 si->show_titlescreen = TRUE;
9139 si->quick_doors = FALSE;
9140 si->team_mode = FALSE;
9141 si->handicap = TRUE;
9142 si->skip_levels = TRUE;
9143 si->increment_levels = TRUE;
9144 si->auto_play_next_level = TRUE;
9145 si->skip_scores_after_game = FALSE;
9146 si->time_limit = TRUE;
9147 si->fullscreen = FALSE;
9148 si->window_scaling_percent = STD_WINDOW_SCALING_PERCENT;
9149 si->window_scaling_quality = getStringCopy(SCALING_QUALITY_DEFAULT);
9150 si->screen_rendering_mode = getStringCopy(STR_SPECIAL_RENDERING_DEFAULT);
9151 si->vsync_mode = getStringCopy(STR_VSYNC_MODE_DEFAULT);
9152 si->ask_on_escape = TRUE;
9153 si->ask_on_escape_editor = TRUE;
9154 si->ask_on_game_over = TRUE;
9155 si->quick_switch = FALSE;
9156 si->input_on_focus = FALSE;
9157 si->prefer_aga_graphics = TRUE;
9158 si->game_speed_extended = FALSE;
9159 si->game_frame_delay = GAME_FRAME_DELAY;
9160 si->sp_show_border_elements = FALSE;
9161 si->small_game_graphics = FALSE;
9162 si->show_snapshot_buttons = FALSE;
9164 si->graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
9165 si->sounds_set = getStringCopy(SND_CLASSIC_SUBDIR);
9166 si->music_set = getStringCopy(MUS_CLASSIC_SUBDIR);
9168 si->override_level_graphics = FALSE;
9169 si->override_level_sounds = FALSE;
9170 si->override_level_music = FALSE;
9172 si->volume_simple = 100; // percent
9173 si->volume_loops = 100; // percent
9174 si->volume_music = 100; // percent
9176 si->network_mode = FALSE;
9177 si->network_player_nr = 0; // first player
9178 si->network_server_hostname = getStringCopy(STR_NETWORK_AUTO_DETECT);
9180 si->touch.control_type = getStringCopy(TOUCH_CONTROL_DEFAULT);
9181 si->touch.move_distance = TOUCH_MOVE_DISTANCE_DEFAULT; // percent
9182 si->touch.drop_distance = TOUCH_DROP_DISTANCE_DEFAULT; // percent
9183 si->touch.transparency = TOUCH_TRANSPARENCY_DEFAULT; // percent
9184 si->touch.draw_outlined = TRUE;
9185 si->touch.draw_pressed = TRUE;
9187 for (i = 0; i < 2; i++)
9189 char *default_grid_button[6][2] =
9195 { "111222", " vv " },
9196 { "111222", " vv " }
9198 int grid_xsize = DEFAULT_GRID_XSIZE(i);
9199 int grid_ysize = DEFAULT_GRID_YSIZE(i);
9200 int min_xsize = MIN(6, grid_xsize);
9201 int min_ysize = MIN(6, grid_ysize);
9202 int startx = grid_xsize - min_xsize;
9203 int starty = grid_ysize - min_ysize;
9206 // virtual buttons grid can only be set to defaults if video is initialized
9207 // (this will be repeated if virtual buttons are not loaded from setup file)
9208 if (video.initialized)
9210 si->touch.grid_xsize[i] = grid_xsize;
9211 si->touch.grid_ysize[i] = grid_ysize;
9215 si->touch.grid_xsize[i] = -1;
9216 si->touch.grid_ysize[i] = -1;
9219 for (x = 0; x < MAX_GRID_XSIZE; x++)
9220 for (y = 0; y < MAX_GRID_YSIZE; y++)
9221 si->touch.grid_button[i][x][y] = CHAR_GRID_BUTTON_NONE;
9223 for (x = 0; x < min_xsize; x++)
9224 for (y = 0; y < min_ysize; y++)
9225 si->touch.grid_button[i][x][starty + y] =
9226 default_grid_button[y][0][x];
9228 for (x = 0; x < min_xsize; x++)
9229 for (y = 0; y < min_ysize; y++)
9230 si->touch.grid_button[i][startx + x][starty + y] =
9231 default_grid_button[y][1][x];
9234 si->touch.grid_initialized = video.initialized;
9236 si->editor.el_boulderdash = TRUE;
9237 si->editor.el_emerald_mine = TRUE;
9238 si->editor.el_emerald_mine_club = TRUE;
9239 si->editor.el_more = TRUE;
9240 si->editor.el_sokoban = TRUE;
9241 si->editor.el_supaplex = TRUE;
9242 si->editor.el_diamond_caves = TRUE;
9243 si->editor.el_dx_boulderdash = TRUE;
9245 si->editor.el_mirror_magic = TRUE;
9246 si->editor.el_deflektor = TRUE;
9248 si->editor.el_chars = TRUE;
9249 si->editor.el_steel_chars = TRUE;
9251 si->editor.el_classic = TRUE;
9252 si->editor.el_custom = TRUE;
9254 si->editor.el_user_defined = FALSE;
9255 si->editor.el_dynamic = TRUE;
9257 si->editor.el_headlines = TRUE;
9259 si->editor.show_element_token = FALSE;
9261 si->editor.use_template_for_new_levels = TRUE;
9263 si->shortcut.save_game = DEFAULT_KEY_SAVE_GAME;
9264 si->shortcut.load_game = DEFAULT_KEY_LOAD_GAME;
9265 si->shortcut.toggle_pause = DEFAULT_KEY_TOGGLE_PAUSE;
9267 si->shortcut.focus_player[0] = DEFAULT_KEY_FOCUS_PLAYER_1;
9268 si->shortcut.focus_player[1] = DEFAULT_KEY_FOCUS_PLAYER_2;
9269 si->shortcut.focus_player[2] = DEFAULT_KEY_FOCUS_PLAYER_3;
9270 si->shortcut.focus_player[3] = DEFAULT_KEY_FOCUS_PLAYER_4;
9271 si->shortcut.focus_player_all = DEFAULT_KEY_FOCUS_PLAYER_ALL;
9273 si->shortcut.tape_eject = DEFAULT_KEY_TAPE_EJECT;
9274 si->shortcut.tape_extra = DEFAULT_KEY_TAPE_EXTRA;
9275 si->shortcut.tape_stop = DEFAULT_KEY_TAPE_STOP;
9276 si->shortcut.tape_pause = DEFAULT_KEY_TAPE_PAUSE;
9277 si->shortcut.tape_record = DEFAULT_KEY_TAPE_RECORD;
9278 si->shortcut.tape_play = DEFAULT_KEY_TAPE_PLAY;
9280 si->shortcut.sound_simple = DEFAULT_KEY_SOUND_SIMPLE;
9281 si->shortcut.sound_loops = DEFAULT_KEY_SOUND_LOOPS;
9282 si->shortcut.sound_music = DEFAULT_KEY_SOUND_MUSIC;
9284 si->shortcut.snap_left = DEFAULT_KEY_SNAP_LEFT;
9285 si->shortcut.snap_right = DEFAULT_KEY_SNAP_RIGHT;
9286 si->shortcut.snap_up = DEFAULT_KEY_SNAP_UP;
9287 si->shortcut.snap_down = DEFAULT_KEY_SNAP_DOWN;
9289 for (i = 0; i < MAX_PLAYERS; i++)
9291 si->input[i].use_joystick = FALSE;
9292 si->input[i].joy.device_name=getStringCopy(getDeviceNameFromJoystickNr(i));
9293 si->input[i].joy.xleft = JOYSTICK_XLEFT;
9294 si->input[i].joy.xmiddle = JOYSTICK_XMIDDLE;
9295 si->input[i].joy.xright = JOYSTICK_XRIGHT;
9296 si->input[i].joy.yupper = JOYSTICK_YUPPER;
9297 si->input[i].joy.ymiddle = JOYSTICK_YMIDDLE;
9298 si->input[i].joy.ylower = JOYSTICK_YLOWER;
9299 si->input[i].joy.snap = (i == 0 ? JOY_BUTTON_1 : 0);
9300 si->input[i].joy.drop = (i == 0 ? JOY_BUTTON_2 : 0);
9301 si->input[i].key.left = (i == 0 ? DEFAULT_KEY_LEFT : KSYM_UNDEFINED);
9302 si->input[i].key.right = (i == 0 ? DEFAULT_KEY_RIGHT : KSYM_UNDEFINED);
9303 si->input[i].key.up = (i == 0 ? DEFAULT_KEY_UP : KSYM_UNDEFINED);
9304 si->input[i].key.down = (i == 0 ? DEFAULT_KEY_DOWN : KSYM_UNDEFINED);
9305 si->input[i].key.snap = (i == 0 ? DEFAULT_KEY_SNAP : KSYM_UNDEFINED);
9306 si->input[i].key.drop = (i == 0 ? DEFAULT_KEY_DROP : KSYM_UNDEFINED);
9309 si->system.sdl_videodriver = getStringCopy(ARG_DEFAULT);
9310 si->system.sdl_audiodriver = getStringCopy(ARG_DEFAULT);
9311 si->system.audio_fragment_size = DEFAULT_AUDIO_FRAGMENT_SIZE;
9313 si->internal.program_title = getStringCopy(PROGRAM_TITLE_STRING);
9314 si->internal.program_version = getStringCopy(getProgramRealVersionString());
9315 si->internal.program_author = getStringCopy(PROGRAM_AUTHOR_STRING);
9316 si->internal.program_email = getStringCopy(PROGRAM_EMAIL_STRING);
9317 si->internal.program_website = getStringCopy(PROGRAM_WEBSITE_STRING);
9318 si->internal.program_copyright = getStringCopy(PROGRAM_COPYRIGHT_STRING);
9319 si->internal.program_company = getStringCopy(PROGRAM_COMPANY_STRING);
9321 si->internal.program_icon_file = getStringCopy(PROGRAM_ICON_FILENAME);
9323 si->internal.default_graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
9324 si->internal.default_sounds_set = getStringCopy(SND_CLASSIC_SUBDIR);
9325 si->internal.default_music_set = getStringCopy(MUS_CLASSIC_SUBDIR);
9327 si->internal.fallback_graphics_file = getStringCopy(UNDEFINED_FILENAME);
9328 si->internal.fallback_sounds_file = getStringCopy(UNDEFINED_FILENAME);
9329 si->internal.fallback_music_file = getStringCopy(UNDEFINED_FILENAME);
9331 si->internal.default_level_series = getStringCopy(UNDEFINED_LEVELSET);
9332 si->internal.choose_from_top_leveldir = FALSE;
9333 si->internal.show_scaling_in_title = TRUE;
9334 si->internal.create_user_levelset = TRUE;
9336 si->internal.default_window_width = WIN_XSIZE_DEFAULT;
9337 si->internal.default_window_height = WIN_YSIZE_DEFAULT;
9339 si->debug.frame_delay[0] = DEFAULT_FRAME_DELAY_0;
9340 si->debug.frame_delay[1] = DEFAULT_FRAME_DELAY_1;
9341 si->debug.frame_delay[2] = DEFAULT_FRAME_DELAY_2;
9342 si->debug.frame_delay[3] = DEFAULT_FRAME_DELAY_3;
9343 si->debug.frame_delay[4] = DEFAULT_FRAME_DELAY_4;
9344 si->debug.frame_delay[5] = DEFAULT_FRAME_DELAY_5;
9345 si->debug.frame_delay[6] = DEFAULT_FRAME_DELAY_6;
9346 si->debug.frame_delay[7] = DEFAULT_FRAME_DELAY_7;
9347 si->debug.frame_delay[8] = DEFAULT_FRAME_DELAY_8;
9348 si->debug.frame_delay[9] = DEFAULT_FRAME_DELAY_9;
9350 si->debug.frame_delay_key[0] = DEFAULT_KEY_FRAME_DELAY_0;
9351 si->debug.frame_delay_key[1] = DEFAULT_KEY_FRAME_DELAY_1;
9352 si->debug.frame_delay_key[2] = DEFAULT_KEY_FRAME_DELAY_2;
9353 si->debug.frame_delay_key[3] = DEFAULT_KEY_FRAME_DELAY_3;
9354 si->debug.frame_delay_key[4] = DEFAULT_KEY_FRAME_DELAY_4;
9355 si->debug.frame_delay_key[5] = DEFAULT_KEY_FRAME_DELAY_5;
9356 si->debug.frame_delay_key[6] = DEFAULT_KEY_FRAME_DELAY_6;
9357 si->debug.frame_delay_key[7] = DEFAULT_KEY_FRAME_DELAY_7;
9358 si->debug.frame_delay_key[8] = DEFAULT_KEY_FRAME_DELAY_8;
9359 si->debug.frame_delay_key[9] = DEFAULT_KEY_FRAME_DELAY_9;
9361 si->debug.frame_delay_use_mod_key = DEFAULT_FRAME_DELAY_USE_MOD_KEY;
9362 si->debug.frame_delay_game_only = DEFAULT_FRAME_DELAY_GAME_ONLY;
9364 si->debug.show_frames_per_second = FALSE;
9366 si->options.verbose = FALSE;
9368 #if defined(PLATFORM_ANDROID)
9369 si->fullscreen = TRUE;
9373 static void setSetupInfoToDefaults_AutoSetup(struct SetupInfo *si)
9375 si->auto_setup.editor_zoom_tilesize = MINI_TILESIZE;
9378 static void setSetupInfoToDefaults_EditorCascade(struct SetupInfo *si)
9380 si->editor_cascade.el_bd = TRUE;
9381 si->editor_cascade.el_em = TRUE;
9382 si->editor_cascade.el_emc = TRUE;
9383 si->editor_cascade.el_rnd = TRUE;
9384 si->editor_cascade.el_sb = TRUE;
9385 si->editor_cascade.el_sp = TRUE;
9386 si->editor_cascade.el_dc = TRUE;
9387 si->editor_cascade.el_dx = TRUE;
9389 si->editor_cascade.el_mm = TRUE;
9390 si->editor_cascade.el_df = TRUE;
9392 si->editor_cascade.el_chars = FALSE;
9393 si->editor_cascade.el_steel_chars = FALSE;
9394 si->editor_cascade.el_ce = FALSE;
9395 si->editor_cascade.el_ge = FALSE;
9396 si->editor_cascade.el_ref = FALSE;
9397 si->editor_cascade.el_user = FALSE;
9398 si->editor_cascade.el_dynamic = FALSE;
9401 #define MAX_HIDE_SETUP_TOKEN_SIZE 20
9403 static char *getHideSetupToken(void *setup_value)
9405 static char hide_setup_token[MAX_HIDE_SETUP_TOKEN_SIZE];
9407 if (setup_value != NULL)
9408 snprintf(hide_setup_token, MAX_HIDE_SETUP_TOKEN_SIZE, "%p", setup_value);
9410 return hide_setup_token;
9413 void setHideSetupEntry(void *setup_value)
9415 char *hide_setup_token = getHideSetupToken(setup_value);
9417 if (setup_value != NULL)
9418 setHashEntry(hide_setup_hash, hide_setup_token, "");
9421 boolean hideSetupEntry(void *setup_value)
9423 char *hide_setup_token = getHideSetupToken(setup_value);
9425 return (setup_value != NULL &&
9426 getHashEntry(hide_setup_hash, hide_setup_token) != NULL);
9429 static void setSetupInfoFromTokenText(SetupFileHash *setup_file_hash,
9430 struct TokenInfo *token_info,
9431 int token_nr, char *token_text)
9433 char *token_hide_text = getStringCat2(token_text, ".hide");
9434 char *token_hide_value = getHashEntry(setup_file_hash, token_hide_text);
9436 // set the value of this setup option in the setup option structure
9437 setSetupInfo(token_info, token_nr, getHashEntry(setup_file_hash, token_text));
9439 // check if this setup option should be hidden in the setup menu
9440 if (token_hide_value != NULL && get_boolean_from_string(token_hide_value))
9441 setHideSetupEntry(token_info[token_nr].value);
9444 static void setSetupInfoFromTokenInfo(SetupFileHash *setup_file_hash,
9445 struct TokenInfo *token_info,
9448 setSetupInfoFromTokenText(setup_file_hash, token_info, token_nr,
9449 token_info[token_nr].text);
9452 static void decodeSetupFileHash(SetupFileHash *setup_file_hash)
9456 if (!setup_file_hash)
9459 if (hide_setup_hash == NULL)
9460 hide_setup_hash = newSetupFileHash();
9462 for (i = 0; i < ARRAY_SIZE(global_setup_tokens); i++)
9463 setSetupInfoFromTokenInfo(setup_file_hash, global_setup_tokens, i);
9465 setup.touch.grid_initialized = TRUE;
9466 for (i = 0; i < 2; i++)
9468 int grid_xsize = setup.touch.grid_xsize[i];
9469 int grid_ysize = setup.touch.grid_ysize[i];
9472 // if virtual buttons are not loaded from setup file, repeat initializing
9473 // virtual buttons grid with default values later when video is initialized
9474 if (grid_xsize == -1 ||
9477 setup.touch.grid_initialized = FALSE;
9482 for (y = 0; y < grid_ysize; y++)
9484 char token_string[MAX_LINE_LEN];
9486 sprintf(token_string, "touch.virtual_buttons.%d.%02d", i, y);
9488 char *value_string = getHashEntry(setup_file_hash, token_string);
9490 if (value_string == NULL)
9493 for (x = 0; x < grid_xsize; x++)
9495 char c = value_string[x];
9497 setup.touch.grid_button[i][x][y] =
9498 (c == '.' ? CHAR_GRID_BUTTON_NONE : c);
9503 for (i = 0; i < ARRAY_SIZE(editor_setup_tokens); i++)
9504 setSetupInfoFromTokenInfo(setup_file_hash, editor_setup_tokens, i);
9506 for (i = 0; i < ARRAY_SIZE(shortcut_setup_tokens); i++)
9507 setSetupInfoFromTokenInfo(setup_file_hash, shortcut_setup_tokens, i);
9509 for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
9513 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
9515 setup_input = setup.input[pnr];
9516 for (i = 0; i < ARRAY_SIZE(player_setup_tokens); i++)
9518 char full_token[100];
9520 sprintf(full_token, "%s%s", prefix, player_setup_tokens[i].text);
9521 setSetupInfoFromTokenText(setup_file_hash, player_setup_tokens, i,
9524 setup.input[pnr] = setup_input;
9527 for (i = 0; i < ARRAY_SIZE(system_setup_tokens); i++)
9528 setSetupInfoFromTokenInfo(setup_file_hash, system_setup_tokens, i);
9530 for (i = 0; i < ARRAY_SIZE(internal_setup_tokens); i++)
9531 setSetupInfoFromTokenInfo(setup_file_hash, internal_setup_tokens, i);
9533 for (i = 0; i < ARRAY_SIZE(debug_setup_tokens); i++)
9534 setSetupInfoFromTokenInfo(setup_file_hash, debug_setup_tokens, i);
9536 for (i = 0; i < ARRAY_SIZE(options_setup_tokens); i++)
9537 setSetupInfoFromTokenInfo(setup_file_hash, options_setup_tokens, i);
9539 setHideRelatedSetupEntries();
9542 static void decodeSetupFileHash_AutoSetup(SetupFileHash *setup_file_hash)
9546 if (!setup_file_hash)
9549 for (i = 0; i < ARRAY_SIZE(auto_setup_tokens); i++)
9550 setSetupInfo(auto_setup_tokens, i,
9551 getHashEntry(setup_file_hash,
9552 auto_setup_tokens[i].text));
9555 static void decodeSetupFileHash_EditorCascade(SetupFileHash *setup_file_hash)
9559 if (!setup_file_hash)
9562 for (i = 0; i < ARRAY_SIZE(editor_cascade_setup_tokens); i++)
9563 setSetupInfo(editor_cascade_setup_tokens, i,
9564 getHashEntry(setup_file_hash,
9565 editor_cascade_setup_tokens[i].text));
9568 void LoadSetupFromFilename(char *filename)
9570 SetupFileHash *setup_file_hash = loadSetupFileHash(filename);
9572 if (setup_file_hash)
9574 decodeSetupFileHash(setup_file_hash);
9576 freeSetupFileHash(setup_file_hash);
9580 Error(ERR_DEBUG, "using default setup values");
9584 static void LoadSetup_SpecialPostProcessing(void)
9586 char *player_name_new;
9588 // needed to work around problems with fixed length strings
9589 player_name_new = get_corrected_login_name(setup.player_name);
9590 free(setup.player_name);
9591 setup.player_name = player_name_new;
9593 // "scroll_delay: on(3) / off(0)" was replaced by scroll delay value
9594 if (setup.scroll_delay == FALSE)
9596 setup.scroll_delay_value = MIN_SCROLL_DELAY;
9597 setup.scroll_delay = TRUE; // now always "on"
9600 // make sure that scroll delay value stays inside valid range
9601 setup.scroll_delay_value =
9602 MIN(MAX(MIN_SCROLL_DELAY, setup.scroll_delay_value), MAX_SCROLL_DELAY);
9605 void LoadSetup(void)
9609 // always start with reliable default values
9610 setSetupInfoToDefaults(&setup);
9612 // try to load setup values from default setup file
9613 filename = getDefaultSetupFilename();
9615 if (fileExists(filename))
9616 LoadSetupFromFilename(filename);
9618 // try to load setup values from user setup file
9619 filename = getSetupFilename();
9621 LoadSetupFromFilename(filename);
9623 LoadSetup_SpecialPostProcessing();
9626 void LoadSetup_AutoSetup(void)
9628 char *filename = getPath2(getSetupDir(), AUTOSETUP_FILENAME);
9629 SetupFileHash *setup_file_hash = NULL;
9631 // always start with reliable default values
9632 setSetupInfoToDefaults_AutoSetup(&setup);
9634 setup_file_hash = loadSetupFileHash(filename);
9636 if (setup_file_hash)
9638 decodeSetupFileHash_AutoSetup(setup_file_hash);
9640 freeSetupFileHash(setup_file_hash);
9646 void LoadSetup_EditorCascade(void)
9648 char *filename = getPath2(getSetupDir(), EDITORCASCADE_FILENAME);
9649 SetupFileHash *setup_file_hash = NULL;
9651 // always start with reliable default values
9652 setSetupInfoToDefaults_EditorCascade(&setup);
9654 setup_file_hash = loadSetupFileHash(filename);
9656 if (setup_file_hash)
9658 decodeSetupFileHash_EditorCascade(setup_file_hash);
9660 freeSetupFileHash(setup_file_hash);
9666 static void addGameControllerMappingToHash(SetupFileHash *mappings_hash,
9669 char mapping_guid[MAX_LINE_LEN];
9670 char *mapping_start, *mapping_end;
9672 // get GUID from game controller mapping line: copy complete line
9673 strncpy(mapping_guid, mapping_line, MAX_LINE_LEN - 1);
9674 mapping_guid[MAX_LINE_LEN - 1] = '\0';
9676 // get GUID from game controller mapping line: cut after GUID part
9677 mapping_start = strchr(mapping_guid, ',');
9678 if (mapping_start != NULL)
9679 *mapping_start = '\0';
9681 // cut newline from game controller mapping line
9682 mapping_end = strchr(mapping_line, '\n');
9683 if (mapping_end != NULL)
9684 *mapping_end = '\0';
9686 // add mapping entry to game controller mappings hash
9687 setHashEntry(mappings_hash, mapping_guid, mapping_line);
9690 static void LoadSetup_ReadGameControllerMappings(SetupFileHash *mappings_hash,
9695 if (!(file = fopen(filename, MODE_READ)))
9697 Error(ERR_WARN, "cannot read game controller mappings file '%s'", filename);
9704 char line[MAX_LINE_LEN];
9706 if (!fgets(line, MAX_LINE_LEN, file))
9709 addGameControllerMappingToHash(mappings_hash, line);
9715 void SaveSetup(void)
9717 char *filename = getSetupFilename();
9721 InitUserDataDirectory();
9723 if (!(file = fopen(filename, MODE_WRITE)))
9725 Error(ERR_WARN, "cannot write setup file '%s'", filename);
9729 fprintFileHeader(file, SETUP_FILENAME);
9731 for (i = 0; i < ARRAY_SIZE(global_setup_tokens); i++)
9733 // just to make things nicer :)
9734 if (global_setup_tokens[i].value == &setup.sound ||
9735 global_setup_tokens[i].value == &setup.graphics_set ||
9736 global_setup_tokens[i].value == &setup.volume_simple ||
9737 global_setup_tokens[i].value == &setup.network_mode ||
9738 global_setup_tokens[i].value == &setup.touch.control_type ||
9739 global_setup_tokens[i].value == &setup.touch.grid_xsize[0] ||
9740 global_setup_tokens[i].value == &setup.touch.grid_xsize[1])
9741 fprintf(file, "\n");
9743 fprintf(file, "%s\n", getSetupLine(global_setup_tokens, "", i));
9746 for (i = 0; i < 2; i++)
9748 int grid_xsize = setup.touch.grid_xsize[i];
9749 int grid_ysize = setup.touch.grid_ysize[i];
9752 fprintf(file, "\n");
9754 for (y = 0; y < grid_ysize; y++)
9756 char token_string[MAX_LINE_LEN];
9757 char value_string[MAX_LINE_LEN];
9759 sprintf(token_string, "touch.virtual_buttons.%d.%02d", i, y);
9761 for (x = 0; x < grid_xsize; x++)
9763 char c = setup.touch.grid_button[i][x][y];
9765 value_string[x] = (c == CHAR_GRID_BUTTON_NONE ? '.' : c);
9768 value_string[grid_xsize] = '\0';
9770 fprintf(file, "%s\n", getFormattedSetupEntry(token_string, value_string));
9774 fprintf(file, "\n");
9775 for (i = 0; i < ARRAY_SIZE(editor_setup_tokens); i++)
9776 fprintf(file, "%s\n", getSetupLine(editor_setup_tokens, "", i));
9778 fprintf(file, "\n");
9779 for (i = 0; i < ARRAY_SIZE(shortcut_setup_tokens); i++)
9780 fprintf(file, "%s\n", getSetupLine(shortcut_setup_tokens, "", i));
9782 for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
9786 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
9787 fprintf(file, "\n");
9789 setup_input = setup.input[pnr];
9790 for (i = 0; i < ARRAY_SIZE(player_setup_tokens); i++)
9791 fprintf(file, "%s\n", getSetupLine(player_setup_tokens, prefix, i));
9794 fprintf(file, "\n");
9795 for (i = 0; i < ARRAY_SIZE(system_setup_tokens); i++)
9796 fprintf(file, "%s\n", getSetupLine(system_setup_tokens, "", i));
9798 // (internal setup values not saved to user setup file)
9800 fprintf(file, "\n");
9801 for (i = 0; i < ARRAY_SIZE(debug_setup_tokens); i++)
9802 fprintf(file, "%s\n", getSetupLine(debug_setup_tokens, "", i));
9804 fprintf(file, "\n");
9805 for (i = 0; i < ARRAY_SIZE(options_setup_tokens); i++)
9806 fprintf(file, "%s\n", getSetupLine(options_setup_tokens, "", i));
9810 SetFilePermissions(filename, PERMS_PRIVATE);
9813 void SaveSetup_AutoSetup(void)
9815 char *filename = getPath2(getSetupDir(), AUTOSETUP_FILENAME);
9819 InitUserDataDirectory();
9821 if (!(file = fopen(filename, MODE_WRITE)))
9823 Error(ERR_WARN, "cannot write auto setup file '%s'", filename);
9828 fprintFileHeader(file, AUTOSETUP_FILENAME);
9830 for (i = 0; i < ARRAY_SIZE(auto_setup_tokens); i++)
9831 fprintf(file, "%s\n", getSetupLine(auto_setup_tokens, "", i));
9835 SetFilePermissions(filename, PERMS_PRIVATE);
9840 void SaveSetup_EditorCascade(void)
9842 char *filename = getPath2(getSetupDir(), EDITORCASCADE_FILENAME);
9846 InitUserDataDirectory();
9848 if (!(file = fopen(filename, MODE_WRITE)))
9850 Error(ERR_WARN, "cannot write editor cascade state file '%s'", filename);
9855 fprintFileHeader(file, EDITORCASCADE_FILENAME);
9857 for (i = 0; i < ARRAY_SIZE(editor_cascade_setup_tokens); i++)
9858 fprintf(file, "%s\n", getSetupLine(editor_cascade_setup_tokens, "", i));
9862 SetFilePermissions(filename, PERMS_PRIVATE);
9867 static void SaveSetup_WriteGameControllerMappings(SetupFileHash *mappings_hash,
9872 if (!(file = fopen(filename, MODE_WRITE)))
9874 Error(ERR_WARN, "cannot write game controller mappings file '%s'",filename);
9879 BEGIN_HASH_ITERATION(mappings_hash, itr)
9881 fprintf(file, "%s\n", HASH_ITERATION_VALUE(itr));
9883 END_HASH_ITERATION(mappings_hash, itr)
9888 void SaveSetup_AddGameControllerMapping(char *mapping)
9890 char *filename = getPath2(getSetupDir(), GAMECONTROLLER_BASENAME);
9891 SetupFileHash *mappings_hash = newSetupFileHash();
9893 InitUserDataDirectory();
9895 // load existing personal game controller mappings
9896 LoadSetup_ReadGameControllerMappings(mappings_hash, filename);
9898 // add new mapping to personal game controller mappings
9899 addGameControllerMappingToHash(mappings_hash, mapping);
9901 // save updated personal game controller mappings
9902 SaveSetup_WriteGameControllerMappings(mappings_hash, filename);
9904 freeSetupFileHash(mappings_hash);
9908 void LoadCustomElementDescriptions(void)
9910 char *filename = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
9911 SetupFileHash *setup_file_hash;
9914 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
9916 if (element_info[i].custom_description != NULL)
9918 free(element_info[i].custom_description);
9919 element_info[i].custom_description = NULL;
9923 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
9926 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
9928 char *token = getStringCat2(element_info[i].token_name, ".name");
9929 char *value = getHashEntry(setup_file_hash, token);
9932 element_info[i].custom_description = getStringCopy(value);
9937 freeSetupFileHash(setup_file_hash);
9940 static int getElementFromToken(char *token)
9942 char *value = getHashEntry(element_token_hash, token);
9947 Error(ERR_WARN, "unknown element token '%s'", token);
9949 return EL_UNDEFINED;
9952 void FreeGlobalAnimEventInfo(void)
9954 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
9956 if (gaei->event_list == NULL)
9961 for (i = 0; i < gaei->num_event_lists; i++)
9963 checked_free(gaei->event_list[i]->event_value);
9964 checked_free(gaei->event_list[i]);
9967 checked_free(gaei->event_list);
9969 gaei->event_list = NULL;
9970 gaei->num_event_lists = 0;
9973 static int AddGlobalAnimEventList(void)
9975 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
9976 int list_pos = gaei->num_event_lists++;
9978 gaei->event_list = checked_realloc(gaei->event_list, gaei->num_event_lists *
9979 sizeof(struct GlobalAnimEventListInfo *));
9981 gaei->event_list[list_pos] =
9982 checked_calloc(sizeof(struct GlobalAnimEventListInfo));
9984 struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
9986 gaeli->event_value = NULL;
9987 gaeli->num_event_values = 0;
9992 static int AddGlobalAnimEventValue(int list_pos, int event_value)
9994 // do not add empty global animation events
9995 if (event_value == ANIM_EVENT_NONE)
9998 // if list position is undefined, create new list
9999 if (list_pos == ANIM_EVENT_UNDEFINED)
10000 list_pos = AddGlobalAnimEventList();
10002 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
10003 struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
10004 int value_pos = gaeli->num_event_values++;
10006 gaeli->event_value = checked_realloc(gaeli->event_value,
10007 gaeli->num_event_values * sizeof(int *));
10009 gaeli->event_value[value_pos] = event_value;
10014 int GetGlobalAnimEventValue(int list_pos, int value_pos)
10016 if (list_pos == ANIM_EVENT_UNDEFINED)
10017 return ANIM_EVENT_NONE;
10019 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
10020 struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
10022 return gaeli->event_value[value_pos];
10025 int GetGlobalAnimEventValueCount(int list_pos)
10027 if (list_pos == ANIM_EVENT_UNDEFINED)
10030 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
10031 struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
10033 return gaeli->num_event_values;
10036 // This function checks if a string <s> of the format "string1, string2, ..."
10037 // exactly contains a string <s_contained>.
10039 static boolean string_has_parameter(char *s, char *s_contained)
10043 if (s == NULL || s_contained == NULL)
10046 if (strlen(s_contained) > strlen(s))
10049 if (strncmp(s, s_contained, strlen(s_contained)) == 0)
10051 char next_char = s[strlen(s_contained)];
10053 // check if next character is delimiter or whitespace
10054 return (next_char == ',' || next_char == '\0' ||
10055 next_char == ' ' || next_char == '\t' ? TRUE : FALSE);
10058 // check if string contains another parameter string after a comma
10059 substring = strchr(s, ',');
10060 if (substring == NULL) // string does not contain a comma
10063 // advance string pointer to next character after the comma
10066 // skip potential whitespaces after the comma
10067 while (*substring == ' ' || *substring == '\t')
10070 return string_has_parameter(substring, s_contained);
10073 static int get_anim_parameter_value(char *s)
10075 int event_value[] =
10083 char *pattern_1[] =
10091 char *pattern_2 = ".part_";
10092 char *matching_char = NULL;
10094 int pattern_1_len = 0;
10095 int result = ANIM_EVENT_NONE;
10098 for (i = 0; i < ARRAY_SIZE(event_value); i++)
10100 matching_char = strstr(s_ptr, pattern_1[i]);
10101 pattern_1_len = strlen(pattern_1[i]);
10102 result = event_value[i];
10104 if (matching_char != NULL)
10108 if (matching_char == NULL)
10109 return ANIM_EVENT_NONE;
10111 s_ptr = matching_char + pattern_1_len;
10113 // check for main animation number ("anim_X" or "anim_XX")
10114 if (*s_ptr >= '0' && *s_ptr <= '9')
10116 int gic_anim_nr = (*s_ptr++ - '0');
10118 if (*s_ptr >= '0' && *s_ptr <= '9')
10119 gic_anim_nr = 10 * gic_anim_nr + (*s_ptr++ - '0');
10121 if (gic_anim_nr < 1 || gic_anim_nr > MAX_GLOBAL_ANIMS)
10122 return ANIM_EVENT_NONE;
10124 result |= gic_anim_nr << ANIM_EVENT_ANIM_BIT;
10128 // invalid main animation number specified
10130 return ANIM_EVENT_NONE;
10133 // check for animation part number ("part_X" or "part_XX") (optional)
10134 if (strPrefix(s_ptr, pattern_2))
10136 s_ptr += strlen(pattern_2);
10138 if (*s_ptr >= '0' && *s_ptr <= '9')
10140 int gic_part_nr = (*s_ptr++ - '0');
10142 if (*s_ptr >= '0' && *s_ptr <= '9')
10143 gic_part_nr = 10 * gic_part_nr + (*s_ptr++ - '0');
10145 if (gic_part_nr < 1 || gic_part_nr > MAX_GLOBAL_ANIM_PARTS)
10146 return ANIM_EVENT_NONE;
10148 result |= gic_part_nr << ANIM_EVENT_PART_BIT;
10152 // invalid animation part number specified
10154 return ANIM_EVENT_NONE;
10158 // discard result if next character is neither delimiter nor whitespace
10159 if (!(*s_ptr == ',' || *s_ptr == '\0' ||
10160 *s_ptr == ' ' || *s_ptr == '\t'))
10161 return ANIM_EVENT_NONE;
10166 static int get_anim_parameter_values(char *s)
10168 int list_pos = ANIM_EVENT_UNDEFINED;
10169 int event_value = ANIM_EVENT_DEFAULT;
10171 if (string_has_parameter(s, "any"))
10172 event_value |= ANIM_EVENT_ANY;
10174 if (string_has_parameter(s, "click:self") ||
10175 string_has_parameter(s, "click") ||
10176 string_has_parameter(s, "self"))
10177 event_value |= ANIM_EVENT_SELF;
10179 if (string_has_parameter(s, "unclick:any"))
10180 event_value |= ANIM_EVENT_UNCLICK_ANY;
10182 // if animation event found, add it to global animation event list
10183 if (event_value != ANIM_EVENT_NONE)
10184 list_pos = AddGlobalAnimEventValue(list_pos, event_value);
10188 // add optional "click:anim_X" or "click:anim_X.part_X" parameter
10189 event_value = get_anim_parameter_value(s);
10191 // if animation event found, add it to global animation event list
10192 if (event_value != ANIM_EVENT_NONE)
10193 list_pos = AddGlobalAnimEventValue(list_pos, event_value);
10195 // continue with next part of the string, starting with next comma
10196 s = strchr(s + 1, ',');
10202 static int get_anim_action_parameter_value(char *token)
10204 int result = getImageIDFromToken(token);
10208 char *gfx_token = getStringCat2("gfx.", token);
10210 result = getImageIDFromToken(gfx_token);
10212 checked_free(gfx_token);
10217 Key key = getKeyFromX11KeyName(token);
10219 if (key != KSYM_UNDEFINED)
10220 result = -(int)key;
10224 result = ANIM_EVENT_ACTION_NONE;
10229 int get_parameter_value(char *value_raw, char *suffix, int type)
10231 char *value = getStringToLower(value_raw);
10232 int result = 0; // probably a save default value
10234 if (strEqual(suffix, ".direction"))
10236 result = (strEqual(value, "left") ? MV_LEFT :
10237 strEqual(value, "right") ? MV_RIGHT :
10238 strEqual(value, "up") ? MV_UP :
10239 strEqual(value, "down") ? MV_DOWN : MV_NONE);
10241 else if (strEqual(suffix, ".position"))
10243 result = (strEqual(value, "left") ? POS_LEFT :
10244 strEqual(value, "right") ? POS_RIGHT :
10245 strEqual(value, "top") ? POS_TOP :
10246 strEqual(value, "upper") ? POS_UPPER :
10247 strEqual(value, "middle") ? POS_MIDDLE :
10248 strEqual(value, "lower") ? POS_LOWER :
10249 strEqual(value, "bottom") ? POS_BOTTOM :
10250 strEqual(value, "any") ? POS_ANY :
10251 strEqual(value, "last") ? POS_LAST : POS_UNDEFINED);
10253 else if (strEqual(suffix, ".align"))
10255 result = (strEqual(value, "left") ? ALIGN_LEFT :
10256 strEqual(value, "right") ? ALIGN_RIGHT :
10257 strEqual(value, "center") ? ALIGN_CENTER :
10258 strEqual(value, "middle") ? ALIGN_CENTER : ALIGN_DEFAULT);
10260 else if (strEqual(suffix, ".valign"))
10262 result = (strEqual(value, "top") ? VALIGN_TOP :
10263 strEqual(value, "bottom") ? VALIGN_BOTTOM :
10264 strEqual(value, "middle") ? VALIGN_MIDDLE :
10265 strEqual(value, "center") ? VALIGN_MIDDLE : VALIGN_DEFAULT);
10267 else if (strEqual(suffix, ".anim_mode"))
10269 result = (string_has_parameter(value, "none") ? ANIM_NONE :
10270 string_has_parameter(value, "loop") ? ANIM_LOOP :
10271 string_has_parameter(value, "linear") ? ANIM_LINEAR :
10272 string_has_parameter(value, "pingpong") ? ANIM_PINGPONG :
10273 string_has_parameter(value, "pingpong2") ? ANIM_PINGPONG2 :
10274 string_has_parameter(value, "random") ? ANIM_RANDOM :
10275 string_has_parameter(value, "ce_value") ? ANIM_CE_VALUE :
10276 string_has_parameter(value, "ce_score") ? ANIM_CE_SCORE :
10277 string_has_parameter(value, "ce_delay") ? ANIM_CE_DELAY :
10278 string_has_parameter(value, "horizontal") ? ANIM_HORIZONTAL :
10279 string_has_parameter(value, "vertical") ? ANIM_VERTICAL :
10280 string_has_parameter(value, "centered") ? ANIM_CENTERED :
10281 string_has_parameter(value, "all") ? ANIM_ALL :
10284 if (string_has_parameter(value, "once"))
10285 result |= ANIM_ONCE;
10287 if (string_has_parameter(value, "reverse"))
10288 result |= ANIM_REVERSE;
10290 if (string_has_parameter(value, "opaque_player"))
10291 result |= ANIM_OPAQUE_PLAYER;
10293 if (string_has_parameter(value, "static_panel"))
10294 result |= ANIM_STATIC_PANEL;
10296 else if (strEqual(suffix, ".init_event") ||
10297 strEqual(suffix, ".anim_event"))
10299 result = get_anim_parameter_values(value);
10301 else if (strEqual(suffix, ".init_delay_action") ||
10302 strEqual(suffix, ".anim_delay_action") ||
10303 strEqual(suffix, ".post_delay_action") ||
10304 strEqual(suffix, ".init_event_action") ||
10305 strEqual(suffix, ".anim_event_action"))
10307 result = get_anim_action_parameter_value(value_raw);
10309 else if (strEqual(suffix, ".class"))
10311 result = (strEqual(value, ARG_UNDEFINED) ? ARG_UNDEFINED_VALUE :
10312 get_hash_from_key(value));
10314 else if (strEqual(suffix, ".style"))
10316 result = STYLE_DEFAULT;
10318 if (string_has_parameter(value, "accurate_borders"))
10319 result |= STYLE_ACCURATE_BORDERS;
10321 if (string_has_parameter(value, "inner_corners"))
10322 result |= STYLE_INNER_CORNERS;
10324 if (string_has_parameter(value, "reverse"))
10325 result |= STYLE_REVERSE;
10327 if (string_has_parameter(value, "block_clicks"))
10328 result |= STYLE_BLOCK;
10330 if (string_has_parameter(value, "passthrough_clicks"))
10331 result |= STYLE_PASSTHROUGH;
10333 if (string_has_parameter(value, "multiple_actions"))
10334 result |= STYLE_MULTIPLE_ACTIONS;
10336 else if (strEqual(suffix, ".fade_mode"))
10338 result = (string_has_parameter(value, "none") ? FADE_MODE_NONE :
10339 string_has_parameter(value, "fade") ? FADE_MODE_FADE :
10340 string_has_parameter(value, "crossfade") ? FADE_MODE_CROSSFADE :
10341 string_has_parameter(value, "melt") ? FADE_MODE_MELT :
10342 string_has_parameter(value, "curtain") ? FADE_MODE_CURTAIN :
10343 FADE_MODE_DEFAULT);
10345 else if (strEqual(suffix, ".auto_delay_unit"))
10347 result = (string_has_parameter(value, "ms") ? AUTO_DELAY_UNIT_MS :
10348 string_has_parameter(value, "frames") ? AUTO_DELAY_UNIT_FRAMES :
10349 AUTO_DELAY_UNIT_DEFAULT);
10351 else if (strPrefix(suffix, ".font")) // (may also be ".font_xyz")
10353 result = gfx.get_font_from_token_function(value);
10355 else // generic parameter of type integer or boolean
10357 result = (strEqual(value, ARG_UNDEFINED) ? ARG_UNDEFINED_VALUE :
10358 type == TYPE_INTEGER ? get_integer_from_string(value) :
10359 type == TYPE_BOOLEAN ? get_boolean_from_string(value) :
10360 ARG_UNDEFINED_VALUE);
10368 static int get_token_parameter_value(char *token, char *value_raw)
10372 if (token == NULL || value_raw == NULL)
10373 return ARG_UNDEFINED_VALUE;
10375 suffix = strrchr(token, '.');
10376 if (suffix == NULL)
10379 if (strEqual(suffix, ".element"))
10380 return getElementFromToken(value_raw);
10382 // !!! USE CORRECT VALUE TYPE (currently works also for TYPE_BOOLEAN) !!!
10383 return get_parameter_value(value_raw, suffix, TYPE_INTEGER);
10386 void InitMenuDesignSettings_Static(void)
10390 // always start with reliable default values from static default config
10391 for (i = 0; image_config_vars[i].token != NULL; i++)
10393 char *value = getHashEntry(image_config_hash, image_config_vars[i].token);
10396 *image_config_vars[i].value =
10397 get_token_parameter_value(image_config_vars[i].token, value);
10401 static void InitMenuDesignSettings_SpecialPreProcessing(void)
10405 // the following initializes hierarchical values from static configuration
10407 // special case: initialize "ARG_DEFAULT" values in static default config
10408 // (e.g., initialize "[titlemessage].fade_mode" from "[title].fade_mode")
10409 titlescreen_initial_first_default.fade_mode =
10410 title_initial_first_default.fade_mode;
10411 titlescreen_initial_first_default.fade_delay =
10412 title_initial_first_default.fade_delay;
10413 titlescreen_initial_first_default.post_delay =
10414 title_initial_first_default.post_delay;
10415 titlescreen_initial_first_default.auto_delay =
10416 title_initial_first_default.auto_delay;
10417 titlescreen_initial_first_default.auto_delay_unit =
10418 title_initial_first_default.auto_delay_unit;
10419 titlescreen_first_default.fade_mode = title_first_default.fade_mode;
10420 titlescreen_first_default.fade_delay = title_first_default.fade_delay;
10421 titlescreen_first_default.post_delay = title_first_default.post_delay;
10422 titlescreen_first_default.auto_delay = title_first_default.auto_delay;
10423 titlescreen_first_default.auto_delay_unit =
10424 title_first_default.auto_delay_unit;
10425 titlemessage_initial_first_default.fade_mode =
10426 title_initial_first_default.fade_mode;
10427 titlemessage_initial_first_default.fade_delay =
10428 title_initial_first_default.fade_delay;
10429 titlemessage_initial_first_default.post_delay =
10430 title_initial_first_default.post_delay;
10431 titlemessage_initial_first_default.auto_delay =
10432 title_initial_first_default.auto_delay;
10433 titlemessage_initial_first_default.auto_delay_unit =
10434 title_initial_first_default.auto_delay_unit;
10435 titlemessage_first_default.fade_mode = title_first_default.fade_mode;
10436 titlemessage_first_default.fade_delay = title_first_default.fade_delay;
10437 titlemessage_first_default.post_delay = title_first_default.post_delay;
10438 titlemessage_first_default.auto_delay = title_first_default.auto_delay;
10439 titlemessage_first_default.auto_delay_unit =
10440 title_first_default.auto_delay_unit;
10442 titlescreen_initial_default.fade_mode = title_initial_default.fade_mode;
10443 titlescreen_initial_default.fade_delay = title_initial_default.fade_delay;
10444 titlescreen_initial_default.post_delay = title_initial_default.post_delay;
10445 titlescreen_initial_default.auto_delay = title_initial_default.auto_delay;
10446 titlescreen_initial_default.auto_delay_unit =
10447 title_initial_default.auto_delay_unit;
10448 titlescreen_default.fade_mode = title_default.fade_mode;
10449 titlescreen_default.fade_delay = title_default.fade_delay;
10450 titlescreen_default.post_delay = title_default.post_delay;
10451 titlescreen_default.auto_delay = title_default.auto_delay;
10452 titlescreen_default.auto_delay_unit = title_default.auto_delay_unit;
10453 titlemessage_initial_default.fade_mode = title_initial_default.fade_mode;
10454 titlemessage_initial_default.fade_delay = title_initial_default.fade_delay;
10455 titlemessage_initial_default.post_delay = title_initial_default.post_delay;
10456 titlemessage_initial_default.auto_delay_unit =
10457 title_initial_default.auto_delay_unit;
10458 titlemessage_default.fade_mode = title_default.fade_mode;
10459 titlemessage_default.fade_delay = title_default.fade_delay;
10460 titlemessage_default.post_delay = title_default.post_delay;
10461 titlemessage_default.auto_delay = title_default.auto_delay;
10462 titlemessage_default.auto_delay_unit = title_default.auto_delay_unit;
10464 // special case: initialize "ARG_DEFAULT" values in static default config
10465 // (e.g., init "titlemessage_1.fade_mode" from "[titlemessage].fade_mode")
10466 for (i = 0; i < MAX_NUM_TITLE_MESSAGES; i++)
10468 titlescreen_initial_first[i] = titlescreen_initial_first_default;
10469 titlescreen_first[i] = titlescreen_first_default;
10470 titlemessage_initial_first[i] = titlemessage_initial_first_default;
10471 titlemessage_first[i] = titlemessage_first_default;
10473 titlescreen_initial[i] = titlescreen_initial_default;
10474 titlescreen[i] = titlescreen_default;
10475 titlemessage_initial[i] = titlemessage_initial_default;
10476 titlemessage[i] = titlemessage_default;
10479 // special case: initialize "ARG_DEFAULT" values in static default config
10480 // (eg, init "menu.enter_screen.SCORES.xyz" from "menu.enter_screen.xyz")
10481 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
10483 if (i == GFX_SPECIAL_ARG_TITLE) // title values already initialized
10486 menu.enter_screen[i] = menu.enter_screen[GFX_SPECIAL_ARG_DEFAULT];
10487 menu.leave_screen[i] = menu.leave_screen[GFX_SPECIAL_ARG_DEFAULT];
10488 menu.next_screen[i] = menu.next_screen[GFX_SPECIAL_ARG_DEFAULT];
10491 // special case: initialize "ARG_DEFAULT" values in static default config
10492 // (eg, init "viewport.door_1.MAIN.xyz" from "viewport.door_1.xyz")
10493 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
10495 viewport.window[i] = viewport.window[GFX_SPECIAL_ARG_DEFAULT];
10496 viewport.playfield[i] = viewport.playfield[GFX_SPECIAL_ARG_DEFAULT];
10497 viewport.door_1[i] = viewport.door_1[GFX_SPECIAL_ARG_DEFAULT];
10499 if (i == GFX_SPECIAL_ARG_EDITOR) // editor values already initialized
10502 viewport.door_2[i] = viewport.door_2[GFX_SPECIAL_ARG_DEFAULT];
10506 static void InitMenuDesignSettings_SpecialPostProcessing(void)
10510 struct XY *dst, *src;
10512 game_buttons_xy[] =
10514 { &game.button.save, &game.button.stop },
10515 { &game.button.pause2, &game.button.pause },
10516 { &game.button.load, &game.button.play },
10517 { &game.button.undo, &game.button.stop },
10518 { &game.button.redo, &game.button.play },
10524 // special case: initialize later added SETUP list size from LEVELS value
10525 if (menu.list_size[GAME_MODE_SETUP] == -1)
10526 menu.list_size[GAME_MODE_SETUP] = menu.list_size[GAME_MODE_LEVELS];
10528 // set default position for snapshot buttons to stop/pause/play buttons
10529 for (i = 0; game_buttons_xy[i].dst != NULL; i++)
10530 if ((*game_buttons_xy[i].dst).x == -1 &&
10531 (*game_buttons_xy[i].dst).y == -1)
10532 *game_buttons_xy[i].dst = *game_buttons_xy[i].src;
10534 // --------------------------------------------------------------------------
10535 // dynamic viewports (including playfield margins, borders and alignments)
10536 // --------------------------------------------------------------------------
10538 // dynamic viewports currently only supported for landscape mode
10539 int display_width = MAX(video.display_width, video.display_height);
10540 int display_height = MIN(video.display_width, video.display_height);
10542 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
10544 struct RectWithBorder *vp_window = &viewport.window[i];
10545 struct RectWithBorder *vp_playfield = &viewport.playfield[i];
10546 struct RectWithBorder *vp_door_1 = &viewport.door_1[i];
10547 struct RectWithBorder *vp_door_2 = &viewport.door_2[i];
10548 boolean dynamic_window_width = (vp_window->min_width != -1);
10549 boolean dynamic_window_height = (vp_window->min_height != -1);
10550 boolean dynamic_playfield_width = (vp_playfield->min_width != -1);
10551 boolean dynamic_playfield_height = (vp_playfield->min_height != -1);
10553 // adjust window size if min/max width/height is specified
10555 if (vp_window->min_width != -1)
10557 int window_width = display_width;
10559 // when using static window height, use aspect ratio of display
10560 if (vp_window->min_height == -1)
10561 window_width = vp_window->height * display_width / display_height;
10563 vp_window->width = MAX(vp_window->min_width, window_width);
10566 if (vp_window->min_height != -1)
10568 int window_height = display_height;
10570 // when using static window width, use aspect ratio of display
10571 if (vp_window->min_width == -1)
10572 window_height = vp_window->width * display_height / display_width;
10574 vp_window->height = MAX(vp_window->min_height, window_height);
10577 if (vp_window->max_width != -1)
10578 vp_window->width = MIN(vp_window->width, vp_window->max_width);
10580 if (vp_window->max_height != -1)
10581 vp_window->height = MIN(vp_window->height, vp_window->max_height);
10583 int playfield_width = vp_window->width;
10584 int playfield_height = vp_window->height;
10586 // adjust playfield size and position according to specified margins
10588 playfield_width -= vp_playfield->margin_left;
10589 playfield_width -= vp_playfield->margin_right;
10591 playfield_height -= vp_playfield->margin_top;
10592 playfield_height -= vp_playfield->margin_bottom;
10594 // adjust playfield size if min/max width/height is specified
10596 if (vp_playfield->min_width != -1)
10597 vp_playfield->width = MAX(vp_playfield->min_width, playfield_width);
10599 if (vp_playfield->min_height != -1)
10600 vp_playfield->height = MAX(vp_playfield->min_height, playfield_height);
10602 if (vp_playfield->max_width != -1)
10603 vp_playfield->width = MIN(vp_playfield->width, vp_playfield->max_width);
10605 if (vp_playfield->max_height != -1)
10606 vp_playfield->height = MIN(vp_playfield->height,vp_playfield->max_height);
10608 // adjust playfield position according to specified alignment
10610 if (vp_playfield->align == ALIGN_LEFT || vp_playfield->x > 0)
10611 vp_playfield->x = ALIGNED_VP_XPOS(vp_playfield);
10612 else if (vp_playfield->align == ALIGN_CENTER)
10613 vp_playfield->x = playfield_width / 2 - vp_playfield->width / 2;
10614 else if (vp_playfield->align == ALIGN_RIGHT)
10615 vp_playfield->x += playfield_width - vp_playfield->width;
10617 if (vp_playfield->valign == VALIGN_TOP || vp_playfield->y > 0)
10618 vp_playfield->y = ALIGNED_VP_YPOS(vp_playfield);
10619 else if (vp_playfield->valign == VALIGN_MIDDLE)
10620 vp_playfield->y = playfield_height / 2 - vp_playfield->height / 2;
10621 else if (vp_playfield->valign == VALIGN_BOTTOM)
10622 vp_playfield->y += playfield_height - vp_playfield->height;
10624 vp_playfield->x += vp_playfield->margin_left;
10625 vp_playfield->y += vp_playfield->margin_top;
10627 // adjust individual playfield borders if only default border is specified
10629 if (vp_playfield->border_left == -1)
10630 vp_playfield->border_left = vp_playfield->border_size;
10631 if (vp_playfield->border_right == -1)
10632 vp_playfield->border_right = vp_playfield->border_size;
10633 if (vp_playfield->border_top == -1)
10634 vp_playfield->border_top = vp_playfield->border_size;
10635 if (vp_playfield->border_bottom == -1)
10636 vp_playfield->border_bottom = vp_playfield->border_size;
10638 // set dynamic playfield borders if borders are specified as undefined
10639 // (but only if window size was dynamic and playfield size was static)
10641 if (dynamic_window_width && !dynamic_playfield_width)
10643 if (vp_playfield->border_left == -1)
10645 vp_playfield->border_left = (vp_playfield->x -
10646 vp_playfield->margin_left);
10647 vp_playfield->x -= vp_playfield->border_left;
10648 vp_playfield->width += vp_playfield->border_left;
10651 if (vp_playfield->border_right == -1)
10653 vp_playfield->border_right = (vp_window->width -
10655 vp_playfield->width -
10656 vp_playfield->margin_right);
10657 vp_playfield->width += vp_playfield->border_right;
10661 if (dynamic_window_height && !dynamic_playfield_height)
10663 if (vp_playfield->border_top == -1)
10665 vp_playfield->border_top = (vp_playfield->y -
10666 vp_playfield->margin_top);
10667 vp_playfield->y -= vp_playfield->border_top;
10668 vp_playfield->height += vp_playfield->border_top;
10671 if (vp_playfield->border_bottom == -1)
10673 vp_playfield->border_bottom = (vp_window->height -
10675 vp_playfield->height -
10676 vp_playfield->margin_bottom);
10677 vp_playfield->height += vp_playfield->border_bottom;
10681 // adjust playfield size to be a multiple of a defined alignment tile size
10683 int align_size = vp_playfield->align_size;
10684 int playfield_xtiles = vp_playfield->width / align_size;
10685 int playfield_ytiles = vp_playfield->height / align_size;
10686 int playfield_width_corrected = playfield_xtiles * align_size;
10687 int playfield_height_corrected = playfield_ytiles * align_size;
10688 boolean is_playfield_mode = (i == GFX_SPECIAL_ARG_PLAYING ||
10689 i == GFX_SPECIAL_ARG_EDITOR);
10691 if (is_playfield_mode &&
10692 dynamic_playfield_width &&
10693 vp_playfield->width != playfield_width_corrected)
10695 int playfield_xdiff = vp_playfield->width - playfield_width_corrected;
10697 vp_playfield->width = playfield_width_corrected;
10699 if (vp_playfield->align == ALIGN_LEFT)
10701 vp_playfield->border_left += playfield_xdiff;
10703 else if (vp_playfield->align == ALIGN_RIGHT)
10705 vp_playfield->border_right += playfield_xdiff;
10707 else if (vp_playfield->align == ALIGN_CENTER)
10709 int border_left_diff = playfield_xdiff / 2;
10710 int border_right_diff = playfield_xdiff - border_left_diff;
10712 vp_playfield->border_left += border_left_diff;
10713 vp_playfield->border_right += border_right_diff;
10717 if (is_playfield_mode &&
10718 dynamic_playfield_height &&
10719 vp_playfield->height != playfield_height_corrected)
10721 int playfield_ydiff = vp_playfield->height - playfield_height_corrected;
10723 vp_playfield->height = playfield_height_corrected;
10725 if (vp_playfield->valign == VALIGN_TOP)
10727 vp_playfield->border_top += playfield_ydiff;
10729 else if (vp_playfield->align == VALIGN_BOTTOM)
10731 vp_playfield->border_right += playfield_ydiff;
10733 else if (vp_playfield->align == VALIGN_MIDDLE)
10735 int border_top_diff = playfield_ydiff / 2;
10736 int border_bottom_diff = playfield_ydiff - border_top_diff;
10738 vp_playfield->border_top += border_top_diff;
10739 vp_playfield->border_bottom += border_bottom_diff;
10743 // adjust door positions according to specified alignment
10745 for (j = 0; j < 2; j++)
10747 struct RectWithBorder *vp_door = (j == 0 ? vp_door_1 : vp_door_2);
10749 if (vp_door->align == ALIGN_LEFT || vp_door->x > 0)
10750 vp_door->x = ALIGNED_VP_XPOS(vp_door);
10751 else if (vp_door->align == ALIGN_CENTER)
10752 vp_door->x = vp_window->width / 2 - vp_door->width / 2;
10753 else if (vp_door->align == ALIGN_RIGHT)
10754 vp_door->x += vp_window->width - vp_door->width;
10756 if (vp_door->valign == VALIGN_TOP || vp_door->y > 0)
10757 vp_door->y = ALIGNED_VP_YPOS(vp_door);
10758 else if (vp_door->valign == VALIGN_MIDDLE)
10759 vp_door->y = vp_window->height / 2 - vp_door->height / 2;
10760 else if (vp_door->valign == VALIGN_BOTTOM)
10761 vp_door->y += vp_window->height - vp_door->height;
10766 static void InitMenuDesignSettings_SpecialPostProcessing_AfterGraphics(void)
10770 struct XYTileSize *dst, *src;
10773 editor_buttons_xy[] =
10776 &editor.button.element_left, &editor.palette.element_left,
10777 IMG_GFX_EDITOR_BUTTON_ELEMENT_LEFT
10780 &editor.button.element_middle, &editor.palette.element_middle,
10781 IMG_GFX_EDITOR_BUTTON_ELEMENT_MIDDLE
10784 &editor.button.element_right, &editor.palette.element_right,
10785 IMG_GFX_EDITOR_BUTTON_ELEMENT_RIGHT
10792 // set default position for element buttons to element graphics
10793 for (i = 0; editor_buttons_xy[i].dst != NULL; i++)
10795 if ((*editor_buttons_xy[i].dst).x == -1 &&
10796 (*editor_buttons_xy[i].dst).y == -1)
10798 struct GraphicInfo *gd = &graphic_info[editor_buttons_xy[i].graphic];
10800 gd->width = gd->height = editor_buttons_xy[i].src->tile_size;
10802 *editor_buttons_xy[i].dst = *editor_buttons_xy[i].src;
10806 // adjust editor palette rows and columns if specified to be dynamic
10808 if (editor.palette.cols == -1)
10810 int vp_width = viewport.playfield[GFX_SPECIAL_ARG_EDITOR].width;
10811 int bt_width = graphic_info[IMG_EDITOR_PALETTE_BUTTON].width;
10812 int sc_width = graphic_info[IMG_EDITOR_PALETTE_SCROLLBAR].width;
10814 editor.palette.cols = (vp_width - sc_width) / bt_width;
10816 if (editor.palette.x == -1)
10818 int palette_width = editor.palette.cols * bt_width + sc_width;
10820 editor.palette.x = (vp_width - palette_width) / 2;
10824 if (editor.palette.rows == -1)
10826 int vp_height = viewport.playfield[GFX_SPECIAL_ARG_EDITOR].height;
10827 int bt_height = graphic_info[IMG_EDITOR_PALETTE_BUTTON].height;
10828 int tx_height = getFontHeight(FONT_TEXT_2);
10830 editor.palette.rows = (vp_height - tx_height) / bt_height;
10832 if (editor.palette.y == -1)
10834 int palette_height = editor.palette.rows * bt_height + tx_height;
10836 editor.palette.y = (vp_height - palette_height) / 2;
10841 static void LoadMenuDesignSettingsFromFilename(char *filename)
10843 static struct TitleFadingInfo tfi;
10844 static struct TitleMessageInfo tmi;
10845 static struct TokenInfo title_tokens[] =
10847 { TYPE_INTEGER, &tfi.fade_mode, ".fade_mode" },
10848 { TYPE_INTEGER, &tfi.fade_delay, ".fade_delay" },
10849 { TYPE_INTEGER, &tfi.post_delay, ".post_delay" },
10850 { TYPE_INTEGER, &tfi.auto_delay, ".auto_delay" },
10851 { TYPE_INTEGER, &tfi.auto_delay_unit, ".auto_delay_unit" },
10855 static struct TokenInfo titlemessage_tokens[] =
10857 { TYPE_INTEGER, &tmi.x, ".x" },
10858 { TYPE_INTEGER, &tmi.y, ".y" },
10859 { TYPE_INTEGER, &tmi.width, ".width" },
10860 { TYPE_INTEGER, &tmi.height, ".height" },
10861 { TYPE_INTEGER, &tmi.chars, ".chars" },
10862 { TYPE_INTEGER, &tmi.lines, ".lines" },
10863 { TYPE_INTEGER, &tmi.align, ".align" },
10864 { TYPE_INTEGER, &tmi.valign, ".valign" },
10865 { TYPE_INTEGER, &tmi.font, ".font" },
10866 { TYPE_BOOLEAN, &tmi.autowrap, ".autowrap" },
10867 { TYPE_BOOLEAN, &tmi.centered, ".centered" },
10868 { TYPE_BOOLEAN, &tmi.parse_comments, ".parse_comments" },
10869 { TYPE_INTEGER, &tmi.sort_priority, ".sort_priority" },
10870 { TYPE_INTEGER, &tmi.fade_mode, ".fade_mode" },
10871 { TYPE_INTEGER, &tmi.fade_delay, ".fade_delay" },
10872 { TYPE_INTEGER, &tmi.post_delay, ".post_delay" },
10873 { TYPE_INTEGER, &tmi.auto_delay, ".auto_delay" },
10874 { TYPE_INTEGER, &tmi.auto_delay_unit, ".auto_delay_unit" },
10880 struct TitleFadingInfo *info;
10885 // initialize first titles from "enter screen" definitions, if defined
10886 { &title_initial_first_default, "menu.enter_screen.TITLE" },
10887 { &title_first_default, "menu.enter_screen.TITLE" },
10889 // initialize title screens from "next screen" definitions, if defined
10890 { &title_initial_default, "menu.next_screen.TITLE" },
10891 { &title_default, "menu.next_screen.TITLE" },
10897 struct TitleMessageInfo *array;
10900 titlemessage_arrays[] =
10902 // initialize first titles from "enter screen" definitions, if defined
10903 { titlescreen_initial_first, "menu.enter_screen.TITLE" },
10904 { titlescreen_first, "menu.enter_screen.TITLE" },
10905 { titlemessage_initial_first, "menu.enter_screen.TITLE" },
10906 { titlemessage_first, "menu.enter_screen.TITLE" },
10908 // initialize titles from "next screen" definitions, if defined
10909 { titlescreen_initial, "menu.next_screen.TITLE" },
10910 { titlescreen, "menu.next_screen.TITLE" },
10911 { titlemessage_initial, "menu.next_screen.TITLE" },
10912 { titlemessage, "menu.next_screen.TITLE" },
10914 // overwrite titles with title definitions, if defined
10915 { titlescreen_initial_first, "[title_initial]" },
10916 { titlescreen_first, "[title]" },
10917 { titlemessage_initial_first, "[title_initial]" },
10918 { titlemessage_first, "[title]" },
10920 { titlescreen_initial, "[title_initial]" },
10921 { titlescreen, "[title]" },
10922 { titlemessage_initial, "[title_initial]" },
10923 { titlemessage, "[title]" },
10925 // overwrite titles with title screen/message definitions, if defined
10926 { titlescreen_initial_first, "[titlescreen_initial]" },
10927 { titlescreen_first, "[titlescreen]" },
10928 { titlemessage_initial_first, "[titlemessage_initial]" },
10929 { titlemessage_first, "[titlemessage]" },
10931 { titlescreen_initial, "[titlescreen_initial]" },
10932 { titlescreen, "[titlescreen]" },
10933 { titlemessage_initial, "[titlemessage_initial]" },
10934 { titlemessage, "[titlemessage]" },
10938 SetupFileHash *setup_file_hash;
10941 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
10944 // the following initializes hierarchical values from dynamic configuration
10946 // special case: initialize with default values that may be overwritten
10947 // (e.g., init "menu.draw_xoffset.INFO" from "menu.draw_xoffset")
10948 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
10950 struct TokenIntPtrInfo menu_config[] =
10952 { "menu.draw_xoffset", &menu.draw_xoffset[i] },
10953 { "menu.draw_yoffset", &menu.draw_yoffset[i] },
10954 { "menu.list_size", &menu.list_size[i] }
10957 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
10959 char *token = menu_config[j].token;
10960 char *value = getHashEntry(setup_file_hash, token);
10963 *menu_config[j].value = get_integer_from_string(value);
10967 // special case: initialize with default values that may be overwritten
10968 // (eg, init "menu.draw_xoffset.INFO[XXX]" from "menu.draw_xoffset.INFO")
10969 for (i = 0; i < NUM_SPECIAL_GFX_INFO_ARGS; i++)
10971 struct TokenIntPtrInfo menu_config[] =
10973 { "menu.draw_xoffset.INFO", &menu.draw_xoffset_info[i] },
10974 { "menu.draw_yoffset.INFO", &menu.draw_yoffset_info[i] },
10975 { "menu.list_size.INFO", &menu.list_size_info[i] }
10978 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
10980 char *token = menu_config[j].token;
10981 char *value = getHashEntry(setup_file_hash, token);
10984 *menu_config[j].value = get_integer_from_string(value);
10988 // special case: initialize with default values that may be overwritten
10989 // (eg, init "menu.draw_xoffset.SETUP[XXX]" from "menu.draw_xoffset.SETUP")
10990 for (i = 0; i < NUM_SPECIAL_GFX_SETUP_ARGS; i++)
10992 struct TokenIntPtrInfo menu_config[] =
10994 { "menu.draw_xoffset.SETUP", &menu.draw_xoffset_setup[i] },
10995 { "menu.draw_yoffset.SETUP", &menu.draw_yoffset_setup[i] }
10998 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
11000 char *token = menu_config[j].token;
11001 char *value = getHashEntry(setup_file_hash, token);
11004 *menu_config[j].value = get_integer_from_string(value);
11008 // special case: initialize with default values that may be overwritten
11009 // (eg, init "menu.line_spacing.INFO[XXX]" from "menu.line_spacing.INFO")
11010 for (i = 0; i < NUM_SPECIAL_GFX_INFO_ARGS; i++)
11012 struct TokenIntPtrInfo menu_config[] =
11014 { "menu.left_spacing.INFO", &menu.left_spacing_info[i] },
11015 { "menu.right_spacing.INFO", &menu.right_spacing_info[i] },
11016 { "menu.top_spacing.INFO", &menu.top_spacing_info[i] },
11017 { "menu.bottom_spacing.INFO", &menu.bottom_spacing_info[i] },
11018 { "menu.paragraph_spacing.INFO", &menu.paragraph_spacing_info[i] },
11019 { "menu.headline1_spacing.INFO", &menu.headline1_spacing_info[i] },
11020 { "menu.headline2_spacing.INFO", &menu.headline2_spacing_info[i] },
11021 { "menu.line_spacing.INFO", &menu.line_spacing_info[i] },
11022 { "menu.extra_spacing.INFO", &menu.extra_spacing_info[i] },
11025 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
11027 char *token = menu_config[j].token;
11028 char *value = getHashEntry(setup_file_hash, token);
11031 *menu_config[j].value = get_integer_from_string(value);
11035 // special case: initialize with default values that may be overwritten
11036 // (eg, init "menu.enter_screen.SCORES.xyz" from "menu.enter_screen.xyz")
11037 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
11039 struct TokenIntPtrInfo menu_config[] =
11041 { "menu.enter_screen.fade_mode", &menu.enter_screen[i].fade_mode },
11042 { "menu.enter_screen.fade_delay", &menu.enter_screen[i].fade_delay },
11043 { "menu.enter_screen.post_delay", &menu.enter_screen[i].post_delay },
11044 { "menu.leave_screen.fade_mode", &menu.leave_screen[i].fade_mode },
11045 { "menu.leave_screen.fade_delay", &menu.leave_screen[i].fade_delay },
11046 { "menu.leave_screen.post_delay", &menu.leave_screen[i].post_delay },
11047 { "menu.next_screen.fade_mode", &menu.next_screen[i].fade_mode },
11048 { "menu.next_screen.fade_delay", &menu.next_screen[i].fade_delay },
11049 { "menu.next_screen.post_delay", &menu.next_screen[i].post_delay }
11052 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
11054 char *token = menu_config[j].token;
11055 char *value = getHashEntry(setup_file_hash, token);
11058 *menu_config[j].value = get_token_parameter_value(token, value);
11062 // special case: initialize with default values that may be overwritten
11063 // (eg, init "viewport.door_1.MAIN.xyz" from "viewport.door_1.xyz")
11064 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
11068 char *token_prefix;
11069 struct RectWithBorder *struct_ptr;
11073 { "viewport.window", &viewport.window[i] },
11074 { "viewport.playfield", &viewport.playfield[i] },
11075 { "viewport.door_1", &viewport.door_1[i] },
11076 { "viewport.door_2", &viewport.door_2[i] }
11079 for (j = 0; j < ARRAY_SIZE(vp_struct); j++)
11081 struct TokenIntPtrInfo vp_config[] =
11083 { ".x", &vp_struct[j].struct_ptr->x },
11084 { ".y", &vp_struct[j].struct_ptr->y },
11085 { ".width", &vp_struct[j].struct_ptr->width },
11086 { ".height", &vp_struct[j].struct_ptr->height },
11087 { ".min_width", &vp_struct[j].struct_ptr->min_width },
11088 { ".min_height", &vp_struct[j].struct_ptr->min_height },
11089 { ".max_width", &vp_struct[j].struct_ptr->max_width },
11090 { ".max_height", &vp_struct[j].struct_ptr->max_height },
11091 { ".margin_left", &vp_struct[j].struct_ptr->margin_left },
11092 { ".margin_right", &vp_struct[j].struct_ptr->margin_right },
11093 { ".margin_top", &vp_struct[j].struct_ptr->margin_top },
11094 { ".margin_bottom", &vp_struct[j].struct_ptr->margin_bottom },
11095 { ".border_left", &vp_struct[j].struct_ptr->border_left },
11096 { ".border_right", &vp_struct[j].struct_ptr->border_right },
11097 { ".border_top", &vp_struct[j].struct_ptr->border_top },
11098 { ".border_bottom", &vp_struct[j].struct_ptr->border_bottom },
11099 { ".border_size", &vp_struct[j].struct_ptr->border_size },
11100 { ".align_size", &vp_struct[j].struct_ptr->align_size },
11101 { ".align", &vp_struct[j].struct_ptr->align },
11102 { ".valign", &vp_struct[j].struct_ptr->valign }
11105 for (k = 0; k < ARRAY_SIZE(vp_config); k++)
11107 char *token = getStringCat2(vp_struct[j].token_prefix,
11108 vp_config[k].token);
11109 char *value = getHashEntry(setup_file_hash, token);
11112 *vp_config[k].value = get_token_parameter_value(token, value);
11119 // special case: initialize with default values that may be overwritten
11120 // (e.g., init "[title].fade_mode" from "menu.next_screen.TITLE.fade_mode")
11121 for (i = 0; title_info[i].info != NULL; i++)
11123 struct TitleFadingInfo *info = title_info[i].info;
11124 char *base_token = title_info[i].text;
11126 for (j = 0; title_tokens[j].type != -1; j++)
11128 char *token = getStringCat2(base_token, title_tokens[j].text);
11129 char *value = getHashEntry(setup_file_hash, token);
11133 int parameter_value = get_token_parameter_value(token, value);
11137 *(int *)title_tokens[j].value = (int)parameter_value;
11146 // special case: initialize with default values that may be overwritten
11147 // (e.g., init "titlemessage_1.fade_mode" from "[titlemessage].fade_mode")
11148 for (i = 0; titlemessage_arrays[i].array != NULL; i++)
11150 struct TitleMessageInfo *array = titlemessage_arrays[i].array;
11151 char *base_token = titlemessage_arrays[i].text;
11153 for (j = 0; titlemessage_tokens[j].type != -1; j++)
11155 char *token = getStringCat2(base_token, titlemessage_tokens[j].text);
11156 char *value = getHashEntry(setup_file_hash, token);
11160 int parameter_value = get_token_parameter_value(token, value);
11162 for (k = 0; k < MAX_NUM_TITLE_MESSAGES; k++)
11166 if (titlemessage_tokens[j].type == TYPE_INTEGER)
11167 *(int *)titlemessage_tokens[j].value = (int)parameter_value;
11169 *(boolean *)titlemessage_tokens[j].value = (boolean)parameter_value;
11179 // special case: check if network and preview player positions are redefined,
11180 // to compare this later against the main menu level preview being redefined
11181 struct TokenIntPtrInfo menu_config_players[] =
11183 { "main.network_players.x", &menu.main.network_players.redefined },
11184 { "main.network_players.y", &menu.main.network_players.redefined },
11185 { "main.preview_players.x", &menu.main.preview_players.redefined },
11186 { "main.preview_players.y", &menu.main.preview_players.redefined },
11187 { "preview.x", &preview.redefined },
11188 { "preview.y", &preview.redefined }
11191 for (i = 0; i < ARRAY_SIZE(menu_config_players); i++)
11192 *menu_config_players[i].value = FALSE;
11194 for (i = 0; i < ARRAY_SIZE(menu_config_players); i++)
11195 if (getHashEntry(setup_file_hash, menu_config_players[i].token) != NULL)
11196 *menu_config_players[i].value = TRUE;
11198 // read (and overwrite with) values that may be specified in config file
11199 for (i = 0; image_config_vars[i].token != NULL; i++)
11201 char *value = getHashEntry(setup_file_hash, image_config_vars[i].token);
11203 // (ignore definitions set to "[DEFAULT]" which are already initialized)
11204 if (value != NULL && !strEqual(value, ARG_DEFAULT))
11205 *image_config_vars[i].value =
11206 get_token_parameter_value(image_config_vars[i].token, value);
11209 freeSetupFileHash(setup_file_hash);
11212 void LoadMenuDesignSettings(void)
11214 char *filename_base = UNDEFINED_FILENAME, *filename_local;
11216 InitMenuDesignSettings_Static();
11217 InitMenuDesignSettings_SpecialPreProcessing();
11219 if (!GFX_OVERRIDE_ARTWORK(ARTWORK_TYPE_GRAPHICS))
11221 // first look for special settings configured in level series config
11222 filename_base = getCustomArtworkLevelConfigFilename(ARTWORK_TYPE_GRAPHICS);
11224 if (fileExists(filename_base))
11225 LoadMenuDesignSettingsFromFilename(filename_base);
11228 filename_local = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
11230 if (filename_local != NULL && !strEqual(filename_base, filename_local))
11231 LoadMenuDesignSettingsFromFilename(filename_local);
11233 InitMenuDesignSettings_SpecialPostProcessing();
11236 void LoadMenuDesignSettings_AfterGraphics(void)
11238 InitMenuDesignSettings_SpecialPostProcessing_AfterGraphics();
11241 void LoadUserDefinedEditorElementList(int **elements, int *num_elements)
11243 char *filename = getEditorSetupFilename();
11244 SetupFileList *setup_file_list, *list;
11245 SetupFileHash *element_hash;
11246 int num_unknown_tokens = 0;
11249 if ((setup_file_list = loadSetupFileList(filename)) == NULL)
11252 element_hash = newSetupFileHash();
11254 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
11255 setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
11257 // determined size may be larger than needed (due to unknown elements)
11259 for (list = setup_file_list; list != NULL; list = list->next)
11262 // add space for up to 3 more elements for padding that may be needed
11263 *num_elements += 3;
11265 // free memory for old list of elements, if needed
11266 checked_free(*elements);
11268 // allocate memory for new list of elements
11269 *elements = checked_malloc(*num_elements * sizeof(int));
11272 for (list = setup_file_list; list != NULL; list = list->next)
11274 char *value = getHashEntry(element_hash, list->token);
11276 if (value == NULL) // try to find obsolete token mapping
11278 char *mapped_token = get_mapped_token(list->token);
11280 if (mapped_token != NULL)
11282 value = getHashEntry(element_hash, mapped_token);
11284 free(mapped_token);
11290 (*elements)[(*num_elements)++] = atoi(value);
11294 if (num_unknown_tokens == 0)
11296 Error(ERR_INFO_LINE, "-");
11297 Error(ERR_INFO, "warning: unknown token(s) found in config file:");
11298 Error(ERR_INFO, "- config file: '%s'", filename);
11300 num_unknown_tokens++;
11303 Error(ERR_INFO, "- token: '%s'", list->token);
11307 if (num_unknown_tokens > 0)
11308 Error(ERR_INFO_LINE, "-");
11310 while (*num_elements % 4) // pad with empty elements, if needed
11311 (*elements)[(*num_elements)++] = EL_EMPTY;
11313 freeSetupFileList(setup_file_list);
11314 freeSetupFileHash(element_hash);
11317 for (i = 0; i < *num_elements; i++)
11318 printf("editor: element '%s' [%d]\n",
11319 element_info[(*elements)[i]].token_name, (*elements)[i]);
11323 static struct MusicFileInfo *get_music_file_info_ext(char *basename, int music,
11326 SetupFileHash *setup_file_hash = NULL;
11327 struct MusicFileInfo tmp_music_file_info, *new_music_file_info;
11328 char *filename_music, *filename_prefix, *filename_info;
11334 token_to_value_ptr[] =
11336 { "title_header", &tmp_music_file_info.title_header },
11337 { "artist_header", &tmp_music_file_info.artist_header },
11338 { "album_header", &tmp_music_file_info.album_header },
11339 { "year_header", &tmp_music_file_info.year_header },
11341 { "title", &tmp_music_file_info.title },
11342 { "artist", &tmp_music_file_info.artist },
11343 { "album", &tmp_music_file_info.album },
11344 { "year", &tmp_music_file_info.year },
11350 filename_music = (is_sound ? getCustomSoundFilename(basename) :
11351 getCustomMusicFilename(basename));
11353 if (filename_music == NULL)
11356 // ---------- try to replace file extension ----------
11358 filename_prefix = getStringCopy(filename_music);
11359 if (strrchr(filename_prefix, '.') != NULL)
11360 *strrchr(filename_prefix, '.') = '\0';
11361 filename_info = getStringCat2(filename_prefix, ".txt");
11363 if (fileExists(filename_info))
11364 setup_file_hash = loadSetupFileHash(filename_info);
11366 free(filename_prefix);
11367 free(filename_info);
11369 if (setup_file_hash == NULL)
11371 // ---------- try to add file extension ----------
11373 filename_prefix = getStringCopy(filename_music);
11374 filename_info = getStringCat2(filename_prefix, ".txt");
11376 if (fileExists(filename_info))
11377 setup_file_hash = loadSetupFileHash(filename_info);
11379 free(filename_prefix);
11380 free(filename_info);
11383 if (setup_file_hash == NULL)
11386 // ---------- music file info found ----------
11388 clear_mem(&tmp_music_file_info, sizeof(struct MusicFileInfo));
11390 for (i = 0; token_to_value_ptr[i].token != NULL; i++)
11392 char *value = getHashEntry(setup_file_hash, token_to_value_ptr[i].token);
11394 *token_to_value_ptr[i].value_ptr =
11395 getStringCopy(value != NULL && *value != '\0' ? value : UNKNOWN_NAME);
11398 tmp_music_file_info.basename = getStringCopy(basename);
11399 tmp_music_file_info.music = music;
11400 tmp_music_file_info.is_sound = is_sound;
11402 new_music_file_info = checked_malloc(sizeof(struct MusicFileInfo));
11403 *new_music_file_info = tmp_music_file_info;
11405 return new_music_file_info;
11408 static struct MusicFileInfo *get_music_file_info(char *basename, int music)
11410 return get_music_file_info_ext(basename, music, FALSE);
11413 static struct MusicFileInfo *get_sound_file_info(char *basename, int sound)
11415 return get_music_file_info_ext(basename, sound, TRUE);
11418 static boolean music_info_listed_ext(struct MusicFileInfo *list,
11419 char *basename, boolean is_sound)
11421 for (; list != NULL; list = list->next)
11422 if (list->is_sound == is_sound && strEqual(list->basename, basename))
11428 static boolean music_info_listed(struct MusicFileInfo *list, char *basename)
11430 return music_info_listed_ext(list, basename, FALSE);
11433 static boolean sound_info_listed(struct MusicFileInfo *list, char *basename)
11435 return music_info_listed_ext(list, basename, TRUE);
11438 void LoadMusicInfo(void)
11440 char *music_directory = getCustomMusicDirectory();
11441 int num_music = getMusicListSize();
11442 int num_music_noconf = 0;
11443 int num_sounds = getSoundListSize();
11445 DirectoryEntry *dir_entry;
11446 struct FileInfo *music, *sound;
11447 struct MusicFileInfo *next, **new;
11450 while (music_file_info != NULL)
11452 next = music_file_info->next;
11454 checked_free(music_file_info->basename);
11456 checked_free(music_file_info->title_header);
11457 checked_free(music_file_info->artist_header);
11458 checked_free(music_file_info->album_header);
11459 checked_free(music_file_info->year_header);
11461 checked_free(music_file_info->title);
11462 checked_free(music_file_info->artist);
11463 checked_free(music_file_info->album);
11464 checked_free(music_file_info->year);
11466 free(music_file_info);
11468 music_file_info = next;
11471 new = &music_file_info;
11473 for (i = 0; i < num_music; i++)
11475 music = getMusicListEntry(i);
11477 if (music->filename == NULL)
11480 if (strEqual(music->filename, UNDEFINED_FILENAME))
11483 // a configured file may be not recognized as music
11484 if (!FileIsMusic(music->filename))
11487 if (!music_info_listed(music_file_info, music->filename))
11489 *new = get_music_file_info(music->filename, i);
11492 new = &(*new)->next;
11496 if ((dir = openDirectory(music_directory)) == NULL)
11498 Error(ERR_WARN, "cannot read music directory '%s'", music_directory);
11502 while ((dir_entry = readDirectory(dir)) != NULL) // loop all entries
11504 char *basename = dir_entry->basename;
11505 boolean music_already_used = FALSE;
11508 // skip all music files that are configured in music config file
11509 for (i = 0; i < num_music; i++)
11511 music = getMusicListEntry(i);
11513 if (music->filename == NULL)
11516 if (strEqual(basename, music->filename))
11518 music_already_used = TRUE;
11523 if (music_already_used)
11526 if (!FileIsMusic(dir_entry->filename))
11529 if (!music_info_listed(music_file_info, basename))
11531 *new = get_music_file_info(basename, MAP_NOCONF_MUSIC(num_music_noconf));
11534 new = &(*new)->next;
11537 num_music_noconf++;
11540 closeDirectory(dir);
11542 for (i = 0; i < num_sounds; i++)
11544 sound = getSoundListEntry(i);
11546 if (sound->filename == NULL)
11549 if (strEqual(sound->filename, UNDEFINED_FILENAME))
11552 // a configured file may be not recognized as sound
11553 if (!FileIsSound(sound->filename))
11556 if (!sound_info_listed(music_file_info, sound->filename))
11558 *new = get_sound_file_info(sound->filename, i);
11560 new = &(*new)->next;
11565 static void add_helpanim_entry(int element, int action, int direction,
11566 int delay, int *num_list_entries)
11568 struct HelpAnimInfo *new_list_entry;
11569 (*num_list_entries)++;
11572 checked_realloc(helpanim_info,
11573 *num_list_entries * sizeof(struct HelpAnimInfo));
11574 new_list_entry = &helpanim_info[*num_list_entries - 1];
11576 new_list_entry->element = element;
11577 new_list_entry->action = action;
11578 new_list_entry->direction = direction;
11579 new_list_entry->delay = delay;
11582 static void print_unknown_token(char *filename, char *token, int token_nr)
11586 Error(ERR_INFO_LINE, "-");
11587 Error(ERR_INFO, "warning: unknown token(s) found in config file:");
11588 Error(ERR_INFO, "- config file: '%s'", filename);
11591 Error(ERR_INFO, "- token: '%s'", token);
11594 static void print_unknown_token_end(int token_nr)
11597 Error(ERR_INFO_LINE, "-");
11600 void LoadHelpAnimInfo(void)
11602 char *filename = getHelpAnimFilename();
11603 SetupFileList *setup_file_list = NULL, *list;
11604 SetupFileHash *element_hash, *action_hash, *direction_hash;
11605 int num_list_entries = 0;
11606 int num_unknown_tokens = 0;
11609 if (fileExists(filename))
11610 setup_file_list = loadSetupFileList(filename);
11612 if (setup_file_list == NULL)
11614 // use reliable default values from static configuration
11615 SetupFileList *insert_ptr;
11617 insert_ptr = setup_file_list =
11618 newSetupFileList(helpanim_config[0].token,
11619 helpanim_config[0].value);
11621 for (i = 1; helpanim_config[i].token; i++)
11622 insert_ptr = addListEntry(insert_ptr,
11623 helpanim_config[i].token,
11624 helpanim_config[i].value);
11627 element_hash = newSetupFileHash();
11628 action_hash = newSetupFileHash();
11629 direction_hash = newSetupFileHash();
11631 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
11632 setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
11634 for (i = 0; i < NUM_ACTIONS; i++)
11635 setHashEntry(action_hash, element_action_info[i].suffix,
11636 i_to_a(element_action_info[i].value));
11638 // do not store direction index (bit) here, but direction value!
11639 for (i = 0; i < NUM_DIRECTIONS_FULL; i++)
11640 setHashEntry(direction_hash, element_direction_info[i].suffix,
11641 i_to_a(1 << element_direction_info[i].value));
11643 for (list = setup_file_list; list != NULL; list = list->next)
11645 char *element_token, *action_token, *direction_token;
11646 char *element_value, *action_value, *direction_value;
11647 int delay = atoi(list->value);
11649 if (strEqual(list->token, "end"))
11651 add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
11656 /* first try to break element into element/action/direction parts;
11657 if this does not work, also accept combined "element[.act][.dir]"
11658 elements (like "dynamite.active"), which are unique elements */
11660 if (strchr(list->token, '.') == NULL) // token contains no '.'
11662 element_value = getHashEntry(element_hash, list->token);
11663 if (element_value != NULL) // element found
11664 add_helpanim_entry(atoi(element_value), -1, -1, delay,
11665 &num_list_entries);
11668 // no further suffixes found -- this is not an element
11669 print_unknown_token(filename, list->token, num_unknown_tokens++);
11675 // token has format "<prefix>.<something>"
11677 action_token = strchr(list->token, '.'); // suffix may be action ...
11678 direction_token = action_token; // ... or direction
11680 element_token = getStringCopy(list->token);
11681 *strchr(element_token, '.') = '\0';
11683 element_value = getHashEntry(element_hash, element_token);
11685 if (element_value == NULL) // this is no element
11687 element_value = getHashEntry(element_hash, list->token);
11688 if (element_value != NULL) // combined element found
11689 add_helpanim_entry(atoi(element_value), -1, -1, delay,
11690 &num_list_entries);
11692 print_unknown_token(filename, list->token, num_unknown_tokens++);
11694 free(element_token);
11699 action_value = getHashEntry(action_hash, action_token);
11701 if (action_value != NULL) // action found
11703 add_helpanim_entry(atoi(element_value), atoi(action_value), -1, delay,
11704 &num_list_entries);
11706 free(element_token);
11711 direction_value = getHashEntry(direction_hash, direction_token);
11713 if (direction_value != NULL) // direction found
11715 add_helpanim_entry(atoi(element_value), -1, atoi(direction_value), delay,
11716 &num_list_entries);
11718 free(element_token);
11723 if (strchr(action_token + 1, '.') == NULL)
11725 // no further suffixes found -- this is not an action nor direction
11727 element_value = getHashEntry(element_hash, list->token);
11728 if (element_value != NULL) // combined element found
11729 add_helpanim_entry(atoi(element_value), -1, -1, delay,
11730 &num_list_entries);
11732 print_unknown_token(filename, list->token, num_unknown_tokens++);
11734 free(element_token);
11739 // token has format "<prefix>.<suffix>.<something>"
11741 direction_token = strchr(action_token + 1, '.');
11743 action_token = getStringCopy(action_token);
11744 *strchr(action_token + 1, '.') = '\0';
11746 action_value = getHashEntry(action_hash, action_token);
11748 if (action_value == NULL) // this is no action
11750 element_value = getHashEntry(element_hash, list->token);
11751 if (element_value != NULL) // combined element found
11752 add_helpanim_entry(atoi(element_value), -1, -1, delay,
11753 &num_list_entries);
11755 print_unknown_token(filename, list->token, num_unknown_tokens++);
11757 free(element_token);
11758 free(action_token);
11763 direction_value = getHashEntry(direction_hash, direction_token);
11765 if (direction_value != NULL) // direction found
11767 add_helpanim_entry(atoi(element_value), atoi(action_value),
11768 atoi(direction_value), delay, &num_list_entries);
11770 free(element_token);
11771 free(action_token);
11776 // this is no direction
11778 element_value = getHashEntry(element_hash, list->token);
11779 if (element_value != NULL) // combined element found
11780 add_helpanim_entry(atoi(element_value), -1, -1, delay,
11781 &num_list_entries);
11783 print_unknown_token(filename, list->token, num_unknown_tokens++);
11785 free(element_token);
11786 free(action_token);
11789 print_unknown_token_end(num_unknown_tokens);
11791 add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
11792 add_helpanim_entry(HELPANIM_LIST_END, -1, -1, -1, &num_list_entries);
11794 freeSetupFileList(setup_file_list);
11795 freeSetupFileHash(element_hash);
11796 freeSetupFileHash(action_hash);
11797 freeSetupFileHash(direction_hash);
11800 for (i = 0; i < num_list_entries; i++)
11801 printf("::: '%s': %d, %d, %d => %d\n",
11802 EL_NAME(helpanim_info[i].element),
11803 helpanim_info[i].element,
11804 helpanim_info[i].action,
11805 helpanim_info[i].direction,
11806 helpanim_info[i].delay);
11810 void LoadHelpTextInfo(void)
11812 char *filename = getHelpTextFilename();
11815 if (helptext_info != NULL)
11817 freeSetupFileHash(helptext_info);
11818 helptext_info = NULL;
11821 if (fileExists(filename))
11822 helptext_info = loadSetupFileHash(filename);
11824 if (helptext_info == NULL)
11826 // use reliable default values from static configuration
11827 helptext_info = newSetupFileHash();
11829 for (i = 0; helptext_config[i].token; i++)
11830 setHashEntry(helptext_info,
11831 helptext_config[i].token,
11832 helptext_config[i].value);
11836 BEGIN_HASH_ITERATION(helptext_info, itr)
11838 printf("::: '%s' => '%s'\n",
11839 HASH_ITERATION_TOKEN(itr), HASH_ITERATION_VALUE(itr));
11841 END_HASH_ITERATION(hash, itr)
11846 // ----------------------------------------------------------------------------
11848 // ----------------------------------------------------------------------------
11850 #define MAX_NUM_CONVERT_LEVELS 1000
11852 void ConvertLevels(void)
11854 static LevelDirTree *convert_leveldir = NULL;
11855 static int convert_level_nr = -1;
11856 static int num_levels_handled = 0;
11857 static int num_levels_converted = 0;
11858 static boolean levels_failed[MAX_NUM_CONVERT_LEVELS];
11861 convert_leveldir = getTreeInfoFromIdentifier(leveldir_first,
11862 global.convert_leveldir);
11864 if (convert_leveldir == NULL)
11865 Error(ERR_EXIT, "no such level identifier: '%s'",
11866 global.convert_leveldir);
11868 leveldir_current = convert_leveldir;
11870 if (global.convert_level_nr != -1)
11872 convert_leveldir->first_level = global.convert_level_nr;
11873 convert_leveldir->last_level = global.convert_level_nr;
11876 convert_level_nr = convert_leveldir->first_level;
11878 PrintLine("=", 79);
11879 Print("Converting levels\n");
11880 PrintLine("-", 79);
11881 Print("Level series identifier: '%s'\n", convert_leveldir->identifier);
11882 Print("Level series name: '%s'\n", convert_leveldir->name);
11883 Print("Level series author: '%s'\n", convert_leveldir->author);
11884 Print("Number of levels: %d\n", convert_leveldir->levels);
11885 PrintLine("=", 79);
11888 for (i = 0; i < MAX_NUM_CONVERT_LEVELS; i++)
11889 levels_failed[i] = FALSE;
11891 while (convert_level_nr <= convert_leveldir->last_level)
11893 char *level_filename;
11896 level_nr = convert_level_nr++;
11898 Print("Level %03d: ", level_nr);
11900 LoadLevel(level_nr);
11901 if (level.no_level_file || level.no_valid_file)
11903 Print("(no level)\n");
11907 Print("converting level ... ");
11909 level_filename = getDefaultLevelFilename(level_nr);
11910 new_level = !fileExists(level_filename);
11914 SaveLevel(level_nr);
11916 num_levels_converted++;
11918 Print("converted.\n");
11922 if (level_nr >= 0 && level_nr < MAX_NUM_CONVERT_LEVELS)
11923 levels_failed[level_nr] = TRUE;
11925 Print("NOT CONVERTED -- LEVEL ALREADY EXISTS.\n");
11928 num_levels_handled++;
11932 PrintLine("=", 79);
11933 Print("Number of levels handled: %d\n", num_levels_handled);
11934 Print("Number of levels converted: %d (%d%%)\n", num_levels_converted,
11935 (num_levels_handled ?
11936 num_levels_converted * 100 / num_levels_handled : 0));
11937 PrintLine("-", 79);
11938 Print("Summary (for automatic parsing by scripts):\n");
11939 Print("LEVELDIR '%s', CONVERTED %d/%d (%d%%)",
11940 convert_leveldir->identifier, num_levels_converted,
11941 num_levels_handled,
11942 (num_levels_handled ?
11943 num_levels_converted * 100 / num_levels_handled : 0));
11945 if (num_levels_handled != num_levels_converted)
11947 Print(", FAILED:");
11948 for (i = 0; i < MAX_NUM_CONVERT_LEVELS; i++)
11949 if (levels_failed[i])
11954 PrintLine("=", 79);
11956 CloseAllAndExit(0);
11960 // ----------------------------------------------------------------------------
11961 // create and save images for use in level sketches (raw BMP format)
11962 // ----------------------------------------------------------------------------
11964 void CreateLevelSketchImages(void)
11970 InitElementPropertiesGfxElement();
11972 bitmap1 = CreateBitmap(TILEX, TILEY, DEFAULT_DEPTH);
11973 bitmap2 = CreateBitmap(MINI_TILEX, MINI_TILEY, DEFAULT_DEPTH);
11975 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
11977 int element = getMappedElement(i);
11978 char basename1[16];
11979 char basename2[16];
11983 sprintf(basename1, "%04d.bmp", i);
11984 sprintf(basename2, "%04ds.bmp", i);
11986 filename1 = getPath2(global.create_images_dir, basename1);
11987 filename2 = getPath2(global.create_images_dir, basename2);
11989 DrawSizedElement(0, 0, element, TILESIZE);
11990 BlitBitmap(drawto, bitmap1, SX, SY, TILEX, TILEY, 0, 0);
11992 if (SDL_SaveBMP(bitmap1->surface, filename1) != 0)
11993 Error(ERR_EXIT, "cannot save level sketch image file '%s'", filename1);
11995 DrawSizedElement(0, 0, element, MINI_TILESIZE);
11996 BlitBitmap(drawto, bitmap2, SX, SY, MINI_TILEX, MINI_TILEY, 0, 0);
11998 if (SDL_SaveBMP(bitmap2->surface, filename2) != 0)
11999 Error(ERR_EXIT, "cannot save level sketch image file '%s'", filename2);
12004 // create corresponding SQL statements (for normal and small images)
12007 printf("insert into phpbb_words values (NULL, '`%03d', '<IMG class=\"levelsketch\" src=\"/I/%04d.png\"/>');\n", i, i);
12008 printf("insert into phpbb_words values (NULL, '¸%03d', '<IMG class=\"levelsketch\" src=\"/I/%04ds.png\"/>');\n", i, i);
12011 printf("insert into phpbb_words values (NULL, '`%04d', '<IMG class=\"levelsketch\" src=\"/I/%04d.png\"/>');\n", i, i);
12012 printf("insert into phpbb_words values (NULL, '¸%04d', '<IMG class=\"levelsketch\" src=\"/I/%04ds.png\"/>');\n", i, i);
12014 // optional: create content for forum level sketch demonstration post
12016 fprintf(stderr, "%03d `%03d%c", i, i, (i % 10 < 9 ? ' ' : '\n'));
12019 FreeBitmap(bitmap1);
12020 FreeBitmap(bitmap2);
12023 fprintf(stderr, "\n");
12025 Error(ERR_INFO, "%d normal and small images created", NUM_FILE_ELEMENTS);
12027 CloseAllAndExit(0);
12031 // ----------------------------------------------------------------------------
12032 // create and save images for custom and group elements (raw BMP format)
12033 // ----------------------------------------------------------------------------
12035 void CreateCustomElementImages(char *directory)
12037 char *src_basename = "RocksCE-template.ilbm";
12038 char *dst_basename = "RocksCE.bmp";
12039 char *src_filename = getPath2(directory, src_basename);
12040 char *dst_filename = getPath2(directory, dst_basename);
12041 Bitmap *src_bitmap;
12043 int yoffset_ce = 0;
12044 int yoffset_ge = (TILEY * NUM_CUSTOM_ELEMENTS / 16);
12047 InitVideoDefaults();
12049 ReCreateBitmap(&backbuffer, video.width, video.height);
12051 src_bitmap = LoadImage(src_filename);
12053 bitmap = CreateBitmap(TILEX * 16 * 2,
12054 TILEY * (NUM_CUSTOM_ELEMENTS + NUM_GROUP_ELEMENTS) / 16,
12057 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
12064 BlitBitmap(src_bitmap, bitmap, 0, 0, TILEX, TILEY,
12065 TILEX * x, TILEY * y + yoffset_ce);
12067 BlitBitmap(src_bitmap, bitmap, 0, TILEY,
12069 TILEX * x + TILEX * 16,
12070 TILEY * y + yoffset_ce);
12072 for (j = 2; j >= 0; j--)
12076 BlitBitmap(src_bitmap, bitmap,
12077 TILEX + c * 7, 0, 6, 10,
12078 TILEX * x + 6 + j * 7,
12079 TILEY * y + 11 + yoffset_ce);
12081 BlitBitmap(src_bitmap, bitmap,
12082 TILEX + c * 8, TILEY, 6, 10,
12083 TILEX * 16 + TILEX * x + 6 + j * 8,
12084 TILEY * y + 10 + yoffset_ce);
12090 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
12097 BlitBitmap(src_bitmap, bitmap, 0, 0, TILEX, TILEY,
12098 TILEX * x, TILEY * y + yoffset_ge);
12100 BlitBitmap(src_bitmap, bitmap, 0, TILEY,
12102 TILEX * x + TILEX * 16,
12103 TILEY * y + yoffset_ge);
12105 for (j = 1; j >= 0; j--)
12109 BlitBitmap(src_bitmap, bitmap, TILEX + c * 10, 11, 10, 10,
12110 TILEX * x + 6 + j * 10,
12111 TILEY * y + 11 + yoffset_ge);
12113 BlitBitmap(src_bitmap, bitmap,
12114 TILEX + c * 8, TILEY + 12, 6, 10,
12115 TILEX * 16 + TILEX * x + 10 + j * 8,
12116 TILEY * y + 10 + yoffset_ge);
12122 if (SDL_SaveBMP(bitmap->surface, dst_filename) != 0)
12123 Error(ERR_EXIT, "cannot save CE graphics file '%s'", dst_filename);
12125 FreeBitmap(bitmap);
12127 CloseAllAndExit(0);