1 // ============================================================================
2 // Rocks'n'Diamonds - McDuffin Strikes Back!
3 // ----------------------------------------------------------------------------
4 // (c) 1995-2014 by Artsoft Entertainment
7 // https://www.artsoft.org/
8 // ----------------------------------------------------------------------------
10 // ============================================================================
17 #include "libgame/libgame.h"
26 #define ENABLE_UNUSED_CODE 0 // currently unused functions
27 #define ENABLE_HISTORIC_CHUNKS 0 // only for historic reference
28 #define ENABLE_RESERVED_CODE 0 // reserved for later use
30 #define CHUNK_ID_LEN 4 // IFF style chunk id length
31 #define CHUNK_SIZE_UNDEFINED 0 // undefined chunk size == 0
32 #define CHUNK_SIZE_NONE -1 // do not write chunk size
34 #define LEVEL_CHUNK_NAME_SIZE MAX_LEVEL_NAME_LEN
35 #define LEVEL_CHUNK_AUTH_SIZE MAX_LEVEL_AUTHOR_LEN
37 #define LEVEL_CHUNK_VERS_SIZE 8 // size of file version chunk
38 #define LEVEL_CHUNK_DATE_SIZE 4 // size of file date chunk
39 #define LEVEL_CHUNK_HEAD_SIZE 80 // size of level file header
40 #define LEVEL_CHUNK_HEAD_UNUSED 0 // unused level header bytes
41 #define LEVEL_CHUNK_CNT2_SIZE 160 // size of level CNT2 chunk
42 #define LEVEL_CHUNK_CNT2_UNUSED 11 // unused CNT2 chunk bytes
43 #define LEVEL_CHUNK_CNT3_HEADER 16 // size of level CNT3 header
44 #define LEVEL_CHUNK_CNT3_UNUSED 10 // unused CNT3 chunk bytes
45 #define LEVEL_CPART_CUS3_SIZE 134 // size of CUS3 chunk part
46 #define LEVEL_CPART_CUS3_UNUSED 15 // unused CUS3 bytes / part
47 #define LEVEL_CHUNK_GRP1_SIZE 74 // size of level GRP1 chunk
49 // (element number, number of change pages, change page number)
50 #define LEVEL_CHUNK_CUSX_UNCHANGED (2 + (1 + 1) + (1 + 1))
52 // (element number only)
53 #define LEVEL_CHUNK_GRPX_UNCHANGED 2
54 #define LEVEL_CHUNK_NOTE_UNCHANGED 2
56 // (nothing at all if unchanged)
57 #define LEVEL_CHUNK_ELEM_UNCHANGED 0
59 #define TAPE_CHUNK_VERS_SIZE 8 // size of file version chunk
60 #define TAPE_CHUNK_HEAD_SIZE 20 // size of tape file header
61 #define TAPE_CHUNK_HEAD_UNUSED 1 // unused tape header bytes
62 #define TAPE_CHUNK_SCRN_SIZE 2 // size of screen size chunk
64 #define SCORE_CHUNK_VERS_SIZE 8 // size of file version chunk
66 #define LEVEL_CHUNK_CNT3_SIZE(x) (LEVEL_CHUNK_CNT3_HEADER + (x))
67 #define LEVEL_CHUNK_CUS3_SIZE(x) (2 + (x) * LEVEL_CPART_CUS3_SIZE)
68 #define LEVEL_CHUNK_CUS4_SIZE(x) (96 + (x) * 48)
70 // file identifier strings
71 #define LEVEL_COOKIE_TMPL "ROCKSNDIAMONDS_LEVEL_FILE_VERSION_x.x"
72 #define TAPE_COOKIE_TMPL "ROCKSNDIAMONDS_TAPE_FILE_VERSION_x.x"
73 #define SCORE_COOKIE_TMPL "ROCKSNDIAMONDS_SCORE_FILE_VERSION_x.x"
75 // values for deciding when (not) to save configuration data
76 #define SAVE_CONF_NEVER 0
77 #define SAVE_CONF_ALWAYS 1
78 #define SAVE_CONF_WHEN_CHANGED -1
80 // values for chunks using micro chunks
81 #define CONF_MASK_1_BYTE 0x00
82 #define CONF_MASK_2_BYTE 0x40
83 #define CONF_MASK_4_BYTE 0x80
84 #define CONF_MASK_MULTI_BYTES 0xc0
86 #define CONF_MASK_BYTES 0xc0
87 #define CONF_MASK_TOKEN 0x3f
89 #define CONF_VALUE_1_BYTE(x) (CONF_MASK_1_BYTE | (x))
90 #define CONF_VALUE_2_BYTE(x) (CONF_MASK_2_BYTE | (x))
91 #define CONF_VALUE_4_BYTE(x) (CONF_MASK_4_BYTE | (x))
92 #define CONF_VALUE_MULTI_BYTES(x) (CONF_MASK_MULTI_BYTES | (x))
94 // these definitions are just for convenience of use and readability
95 #define CONF_VALUE_8_BIT(x) CONF_VALUE_1_BYTE(x)
96 #define CONF_VALUE_16_BIT(x) CONF_VALUE_2_BYTE(x)
97 #define CONF_VALUE_32_BIT(x) CONF_VALUE_4_BYTE(x)
98 #define CONF_VALUE_BYTES(x) CONF_VALUE_MULTI_BYTES(x)
100 #define CONF_VALUE_NUM_BYTES(x) ((x) == CONF_MASK_1_BYTE ? 1 : \
101 (x) == CONF_MASK_2_BYTE ? 2 : \
102 (x) == CONF_MASK_4_BYTE ? 4 : 0)
104 #define CONF_CONTENT_NUM_ELEMENTS (3 * 3)
105 #define CONF_CONTENT_NUM_BYTES (CONF_CONTENT_NUM_ELEMENTS * 2)
106 #define CONF_ELEMENT_NUM_BYTES (2)
108 #define CONF_ENTITY_NUM_BYTES(t) ((t) == TYPE_ELEMENT || \
109 (t) == TYPE_ELEMENT_LIST ? \
110 CONF_ELEMENT_NUM_BYTES : \
111 (t) == TYPE_CONTENT || \
112 (t) == TYPE_CONTENT_LIST ? \
113 CONF_CONTENT_NUM_BYTES : 1)
115 #define CONF_ELEMENT_BYTE_POS(i) ((i) * CONF_ELEMENT_NUM_BYTES)
116 #define CONF_ELEMENTS_ELEMENT(b,i) ((b[CONF_ELEMENT_BYTE_POS(i)] << 8) | \
117 (b[CONF_ELEMENT_BYTE_POS(i) + 1]))
119 #define CONF_CONTENT_ELEMENT_POS(c,x,y) ((c) * CONF_CONTENT_NUM_ELEMENTS + \
121 #define CONF_CONTENT_BYTE_POS(c,x,y) (CONF_CONTENT_ELEMENT_POS(c,x,y) * \
122 CONF_ELEMENT_NUM_BYTES)
123 #define CONF_CONTENTS_ELEMENT(b,c,x,y) ((b[CONF_CONTENT_BYTE_POS(c,x,y)]<< 8)|\
124 (b[CONF_CONTENT_BYTE_POS(c,x,y) + 1]))
126 // temporary variables used to store pointers to structure members
127 static struct LevelInfo li;
128 static struct ElementInfo xx_ei, yy_ei;
129 static struct ElementChangeInfo xx_change;
130 static struct ElementGroupInfo xx_group;
131 static struct EnvelopeInfo xx_envelope;
132 static unsigned int xx_event_bits[NUM_CE_BITFIELDS];
133 static char xx_default_description[MAX_ELEMENT_NAME_LEN + 1];
134 static int xx_num_contents;
135 static int xx_current_change_page;
136 static char xx_default_string_empty[1] = "";
137 static int xx_string_length_unused;
139 struct LevelFileConfigInfo
141 int element; // element for which data is to be stored
142 int save_type; // save data always, never or when changed
143 int data_type; // data type (used internally, not stored)
144 int conf_type; // micro chunk identifier (stored in file)
147 void *value; // variable that holds the data to be stored
148 int default_value; // initial default value for this variable
151 void *value_copy; // variable that holds the data to be copied
152 void *num_entities; // number of entities for multi-byte data
153 int default_num_entities; // default number of entities for this data
154 int max_num_entities; // maximal number of entities for this data
155 char *default_string; // optional default string for string data
158 static struct LevelFileConfigInfo chunk_config_INFO[] =
160 // ---------- values not related to single elements -------------------------
163 -1, SAVE_CONF_ALWAYS,
164 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
165 &li.game_engine_type, GAME_ENGINE_TYPE_RND
169 -1, SAVE_CONF_ALWAYS,
170 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
171 &li.fieldx, STD_LEV_FIELDX
174 -1, SAVE_CONF_ALWAYS,
175 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
176 &li.fieldy, STD_LEV_FIELDY
180 -1, SAVE_CONF_ALWAYS,
181 TYPE_INTEGER, CONF_VALUE_16_BIT(3),
186 -1, SAVE_CONF_ALWAYS,
187 TYPE_INTEGER, CONF_VALUE_16_BIT(4),
193 TYPE_INTEGER, CONF_VALUE_32_BIT(2),
199 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
200 &li.use_step_counter, FALSE
205 TYPE_BITFIELD, CONF_VALUE_8_BIT(4),
206 &li.wind_direction_initial, MV_NONE
211 TYPE_BOOLEAN, CONF_VALUE_8_BIT(5),
212 &li.em_slippery_gems, FALSE
217 TYPE_BOOLEAN, CONF_VALUE_8_BIT(6),
218 &li.use_custom_template, FALSE
223 TYPE_BITFIELD, CONF_VALUE_32_BIT(1),
224 &li.can_move_into_acid_bits, ~0 // default: everything can
229 TYPE_BITFIELD, CONF_VALUE_8_BIT(7),
230 &li.dont_collide_with_bits, ~0 // default: always deadly
235 TYPE_BOOLEAN, CONF_VALUE_8_BIT(8),
236 &li.em_explodes_by_fire, FALSE
241 TYPE_INTEGER, CONF_VALUE_16_BIT(5),
242 &li.score[SC_TIME_BONUS], 1
247 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
248 &li.auto_exit_sokoban, FALSE
253 TYPE_BOOLEAN, CONF_VALUE_8_BIT(10),
254 &li.auto_count_gems, FALSE
259 TYPE_BOOLEAN, CONF_VALUE_8_BIT(11),
260 &li.solved_by_one_player, FALSE
265 TYPE_INTEGER, CONF_VALUE_8_BIT(12),
266 &li.time_score_base, 1
276 static struct LevelFileConfigInfo chunk_config_ELEM[] =
278 // (these values are the same for each player)
281 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
282 &li.block_last_field, FALSE // default case for EM levels
286 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
287 &li.sp_block_last_field, TRUE // default case for SP levels
291 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
292 &li.instant_relocation, FALSE
296 TYPE_BOOLEAN, CONF_VALUE_8_BIT(4),
297 &li.can_pass_to_walkable, FALSE
301 TYPE_BOOLEAN, CONF_VALUE_8_BIT(5),
302 &li.block_snap_field, TRUE
306 TYPE_BOOLEAN, CONF_VALUE_8_BIT(6),
307 &li.continuous_snapping, TRUE
311 TYPE_BOOLEAN, CONF_VALUE_8_BIT(12),
312 &li.shifted_relocation, FALSE
316 TYPE_BOOLEAN, CONF_VALUE_8_BIT(15),
317 &li.lazy_relocation, FALSE
321 TYPE_BOOLEAN, CONF_VALUE_8_BIT(16),
322 &li.finish_dig_collect, TRUE
325 // (these values are different for each player)
328 TYPE_INTEGER, CONF_VALUE_8_BIT(7),
329 &li.initial_player_stepsize[0], STEPSIZE_NORMAL
333 TYPE_BOOLEAN, CONF_VALUE_8_BIT(8),
334 &li.initial_player_gravity[0], FALSE
338 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
339 &li.use_start_element[0], FALSE
343 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
344 &li.start_element[0], EL_PLAYER_1
348 TYPE_BOOLEAN, CONF_VALUE_8_BIT(10),
349 &li.use_artwork_element[0], FALSE
353 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
354 &li.artwork_element[0], EL_PLAYER_1
358 TYPE_BOOLEAN, CONF_VALUE_8_BIT(11),
359 &li.use_explosion_element[0], FALSE
363 TYPE_ELEMENT, CONF_VALUE_16_BIT(3),
364 &li.explosion_element[0], EL_PLAYER_1
368 TYPE_BOOLEAN, CONF_VALUE_8_BIT(13),
369 &li.use_initial_inventory[0], FALSE
373 TYPE_BOOLEAN, CONF_VALUE_8_BIT(14),
374 &li.initial_inventory_size[0], 1
378 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(1),
379 &li.initial_inventory_content[0][0],EL_EMPTY, NULL,
380 &li.initial_inventory_size[0], 1, MAX_INITIAL_INVENTORY_SIZE
385 TYPE_INTEGER, CONF_VALUE_8_BIT(7),
386 &li.initial_player_stepsize[1], STEPSIZE_NORMAL
390 TYPE_BOOLEAN, CONF_VALUE_8_BIT(8),
391 &li.initial_player_gravity[1], FALSE
395 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
396 &li.use_start_element[1], FALSE
400 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
401 &li.start_element[1], EL_PLAYER_2
405 TYPE_BOOLEAN, CONF_VALUE_8_BIT(10),
406 &li.use_artwork_element[1], FALSE
410 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
411 &li.artwork_element[1], EL_PLAYER_2
415 TYPE_BOOLEAN, CONF_VALUE_8_BIT(11),
416 &li.use_explosion_element[1], FALSE
420 TYPE_ELEMENT, CONF_VALUE_16_BIT(3),
421 &li.explosion_element[1], EL_PLAYER_2
425 TYPE_BOOLEAN, CONF_VALUE_8_BIT(13),
426 &li.use_initial_inventory[1], FALSE
430 TYPE_BOOLEAN, CONF_VALUE_8_BIT(14),
431 &li.initial_inventory_size[1], 1
435 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(1),
436 &li.initial_inventory_content[1][0],EL_EMPTY, NULL,
437 &li.initial_inventory_size[1], 1, MAX_INITIAL_INVENTORY_SIZE
442 TYPE_INTEGER, CONF_VALUE_8_BIT(7),
443 &li.initial_player_stepsize[2], STEPSIZE_NORMAL
447 TYPE_BOOLEAN, CONF_VALUE_8_BIT(8),
448 &li.initial_player_gravity[2], FALSE
452 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
453 &li.use_start_element[2], FALSE
457 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
458 &li.start_element[2], EL_PLAYER_3
462 TYPE_BOOLEAN, CONF_VALUE_8_BIT(10),
463 &li.use_artwork_element[2], FALSE
467 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
468 &li.artwork_element[2], EL_PLAYER_3
472 TYPE_BOOLEAN, CONF_VALUE_8_BIT(11),
473 &li.use_explosion_element[2], FALSE
477 TYPE_ELEMENT, CONF_VALUE_16_BIT(3),
478 &li.explosion_element[2], EL_PLAYER_3
482 TYPE_BOOLEAN, CONF_VALUE_8_BIT(13),
483 &li.use_initial_inventory[2], FALSE
487 TYPE_BOOLEAN, CONF_VALUE_8_BIT(14),
488 &li.initial_inventory_size[2], 1
492 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(1),
493 &li.initial_inventory_content[2][0],EL_EMPTY, NULL,
494 &li.initial_inventory_size[2], 1, MAX_INITIAL_INVENTORY_SIZE
499 TYPE_INTEGER, CONF_VALUE_8_BIT(7),
500 &li.initial_player_stepsize[3], STEPSIZE_NORMAL
504 TYPE_BOOLEAN, CONF_VALUE_8_BIT(8),
505 &li.initial_player_gravity[3], FALSE
509 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
510 &li.use_start_element[3], FALSE
514 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
515 &li.start_element[3], EL_PLAYER_4
519 TYPE_BOOLEAN, CONF_VALUE_8_BIT(10),
520 &li.use_artwork_element[3], FALSE
524 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
525 &li.artwork_element[3], EL_PLAYER_4
529 TYPE_BOOLEAN, CONF_VALUE_8_BIT(11),
530 &li.use_explosion_element[3], FALSE
534 TYPE_ELEMENT, CONF_VALUE_16_BIT(3),
535 &li.explosion_element[3], EL_PLAYER_4
539 TYPE_BOOLEAN, CONF_VALUE_8_BIT(13),
540 &li.use_initial_inventory[3], FALSE
544 TYPE_BOOLEAN, CONF_VALUE_8_BIT(14),
545 &li.initial_inventory_size[3], 1
549 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(1),
550 &li.initial_inventory_content[3][0],EL_EMPTY, NULL,
551 &li.initial_inventory_size[3], 1, MAX_INITIAL_INVENTORY_SIZE
556 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
557 &li.score[SC_EMERALD], 10
562 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
563 &li.score[SC_DIAMOND], 10
568 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
569 &li.score[SC_BUG], 10
574 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
575 &li.score[SC_SPACESHIP], 10
580 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
581 &li.score[SC_PACMAN], 10
586 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
587 &li.score[SC_NUT], 10
592 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
593 &li.score[SC_DYNAMITE], 10
598 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
599 &li.score[SC_KEY], 10
604 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
605 &li.score[SC_PEARL], 10
610 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
611 &li.score[SC_CRYSTAL], 10
616 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
617 &li.amoeba_content, EL_DIAMOND
621 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
626 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
627 &li.grow_into_diggable, TRUE
632 TYPE_CONTENT_LIST, CONF_VALUE_BYTES(1),
633 &li.yamyam_content, EL_ROCK, NULL,
634 &li.num_yamyam_contents, 4, MAX_ELEMENT_CONTENTS
638 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
639 &li.score[SC_YAMYAM], 10
644 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
645 &li.score[SC_ROBOT], 10
649 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
655 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
661 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
662 &li.time_magic_wall, 10
667 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
668 &li.game_of_life[0], 2
672 TYPE_INTEGER, CONF_VALUE_8_BIT(2),
673 &li.game_of_life[1], 3
677 TYPE_INTEGER, CONF_VALUE_8_BIT(3),
678 &li.game_of_life[2], 3
682 TYPE_INTEGER, CONF_VALUE_8_BIT(4),
683 &li.game_of_life[3], 3
687 TYPE_BOOLEAN, CONF_VALUE_8_BIT(5),
688 &li.use_life_bugs, FALSE
693 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
698 TYPE_INTEGER, CONF_VALUE_8_BIT(2),
703 TYPE_INTEGER, CONF_VALUE_8_BIT(3),
708 TYPE_INTEGER, CONF_VALUE_8_BIT(4),
713 EL_TIMEGATE_SWITCH, -1,
714 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
715 &li.time_timegate, 10
719 EL_LIGHT_SWITCH_ACTIVE, -1,
720 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
725 EL_SHIELD_NORMAL, -1,
726 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
727 &li.shield_normal_time, 10
730 EL_SHIELD_NORMAL, -1,
731 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
732 &li.score[SC_SHIELD], 10
736 EL_SHIELD_DEADLY, -1,
737 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
738 &li.shield_deadly_time, 10
741 EL_SHIELD_DEADLY, -1,
742 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
743 &li.score[SC_SHIELD], 10
748 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
753 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
754 &li.extra_time_score, 10
758 EL_TIME_ORB_FULL, -1,
759 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
760 &li.time_orb_time, 10
763 EL_TIME_ORB_FULL, -1,
764 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
765 &li.use_time_orb_bug, FALSE
770 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
771 &li.use_spring_bug, FALSE
776 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
777 &li.android_move_time, 10
781 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
782 &li.android_clone_time, 10
785 EL_EMC_ANDROID, SAVE_CONF_NEVER,
786 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(1),
787 &li.android_clone_element[0], EL_EMPTY, NULL,
788 &li.num_android_clone_elements, 1, MAX_ANDROID_ELEMENTS_OLD
792 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(2),
793 &li.android_clone_element[0], EL_EMPTY, NULL,
794 &li.num_android_clone_elements, 1, MAX_ANDROID_ELEMENTS
799 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
804 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
809 EL_EMC_MAGNIFIER, -1,
810 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
811 &li.magnify_score, 10
814 EL_EMC_MAGNIFIER, -1,
815 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
820 EL_EMC_MAGIC_BALL, -1,
821 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
825 EL_EMC_MAGIC_BALL, -1,
826 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
827 &li.ball_random, FALSE
830 EL_EMC_MAGIC_BALL, -1,
831 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
832 &li.ball_active_initial, FALSE
835 EL_EMC_MAGIC_BALL, -1,
836 TYPE_CONTENT_LIST, CONF_VALUE_BYTES(1),
837 &li.ball_content, EL_EMPTY, NULL,
838 &li.num_ball_contents, 4, MAX_ELEMENT_CONTENTS
842 EL_SOKOBAN_FIELD_EMPTY, -1,
843 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
844 &li.sb_fields_needed, TRUE
848 EL_SOKOBAN_OBJECT, -1,
849 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
850 &li.sb_objects_needed, TRUE
855 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
856 &li.mm_laser_red, FALSE
860 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
861 &li.mm_laser_green, FALSE
865 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
866 &li.mm_laser_blue, TRUE
871 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
872 &li.df_laser_red, TRUE
876 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
877 &li.df_laser_green, TRUE
881 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
882 &li.df_laser_blue, FALSE
886 EL_MM_FUSE_ACTIVE, -1,
887 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
892 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
897 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
901 EL_MM_STEEL_BLOCK, -1,
902 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
903 &li.mm_time_block, 75
907 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
908 &li.score[SC_ELEM_BONUS], 10
911 // ---------- unused values -------------------------------------------------
914 EL_UNKNOWN, SAVE_CONF_NEVER,
915 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
916 &li.score[SC_UNKNOWN_15], 10
926 static struct LevelFileConfigInfo chunk_config_NOTE[] =
930 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
931 &xx_envelope.xsize, MAX_ENVELOPE_XSIZE,
935 TYPE_INTEGER, CONF_VALUE_8_BIT(2),
936 &xx_envelope.ysize, MAX_ENVELOPE_YSIZE,
941 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
942 &xx_envelope.autowrap, FALSE
946 TYPE_BOOLEAN, CONF_VALUE_8_BIT(4),
947 &xx_envelope.centered, FALSE
952 TYPE_STRING, CONF_VALUE_BYTES(1),
953 &xx_envelope.text, -1, NULL,
954 &xx_string_length_unused, -1, MAX_ENVELOPE_TEXT_LEN,
955 &xx_default_string_empty[0]
965 static struct LevelFileConfigInfo chunk_config_CUSX_base[] =
969 TYPE_STRING, CONF_VALUE_BYTES(1),
970 &xx_ei.description[0], -1,
971 &yy_ei.description[0],
972 &xx_string_length_unused, -1, MAX_ELEMENT_NAME_LEN,
973 &xx_default_description[0]
978 TYPE_BITFIELD, CONF_VALUE_32_BIT(1),
979 &xx_ei.properties[EP_BITFIELD_BASE_NR], EP_BITMASK_BASE_DEFAULT,
980 &yy_ei.properties[EP_BITFIELD_BASE_NR]
982 #if ENABLE_RESERVED_CODE
983 // (reserved for later use)
986 TYPE_BITFIELD, CONF_VALUE_32_BIT(2),
987 &xx_ei.properties[EP_BITFIELD_BASE_NR + 1], EP_BITMASK_DEFAULT,
988 &yy_ei.properties[EP_BITFIELD_BASE_NR + 1]
994 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
995 &xx_ei.use_gfx_element, FALSE,
996 &yy_ei.use_gfx_element
1000 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
1001 &xx_ei.gfx_element_initial, EL_EMPTY_SPACE,
1002 &yy_ei.gfx_element_initial
1007 TYPE_BITFIELD, CONF_VALUE_8_BIT(2),
1008 &xx_ei.access_direction, MV_ALL_DIRECTIONS,
1009 &yy_ei.access_direction
1014 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
1015 &xx_ei.collect_score_initial, 10,
1016 &yy_ei.collect_score_initial
1020 TYPE_INTEGER, CONF_VALUE_16_BIT(3),
1021 &xx_ei.collect_count_initial, 1,
1022 &yy_ei.collect_count_initial
1027 TYPE_INTEGER, CONF_VALUE_16_BIT(4),
1028 &xx_ei.ce_value_fixed_initial, 0,
1029 &yy_ei.ce_value_fixed_initial
1033 TYPE_INTEGER, CONF_VALUE_16_BIT(5),
1034 &xx_ei.ce_value_random_initial, 0,
1035 &yy_ei.ce_value_random_initial
1039 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
1040 &xx_ei.use_last_ce_value, FALSE,
1041 &yy_ei.use_last_ce_value
1046 TYPE_INTEGER, CONF_VALUE_16_BIT(6),
1047 &xx_ei.push_delay_fixed, 8,
1048 &yy_ei.push_delay_fixed
1052 TYPE_INTEGER, CONF_VALUE_16_BIT(7),
1053 &xx_ei.push_delay_random, 8,
1054 &yy_ei.push_delay_random
1058 TYPE_INTEGER, CONF_VALUE_16_BIT(8),
1059 &xx_ei.drop_delay_fixed, 0,
1060 &yy_ei.drop_delay_fixed
1064 TYPE_INTEGER, CONF_VALUE_16_BIT(9),
1065 &xx_ei.drop_delay_random, 0,
1066 &yy_ei.drop_delay_random
1070 TYPE_INTEGER, CONF_VALUE_16_BIT(10),
1071 &xx_ei.move_delay_fixed, 0,
1072 &yy_ei.move_delay_fixed
1076 TYPE_INTEGER, CONF_VALUE_16_BIT(11),
1077 &xx_ei.move_delay_random, 0,
1078 &yy_ei.move_delay_random
1082 TYPE_INTEGER, CONF_VALUE_16_BIT(16),
1083 &xx_ei.step_delay_fixed, 0,
1084 &yy_ei.step_delay_fixed
1088 TYPE_INTEGER, CONF_VALUE_16_BIT(17),
1089 &xx_ei.step_delay_random, 0,
1090 &yy_ei.step_delay_random
1095 TYPE_BITFIELD, CONF_VALUE_32_BIT(3),
1096 &xx_ei.move_pattern, MV_ALL_DIRECTIONS,
1101 TYPE_BITFIELD, CONF_VALUE_8_BIT(4),
1102 &xx_ei.move_direction_initial, MV_START_AUTOMATIC,
1103 &yy_ei.move_direction_initial
1107 TYPE_INTEGER, CONF_VALUE_8_BIT(5),
1108 &xx_ei.move_stepsize, TILEX / 8,
1109 &yy_ei.move_stepsize
1114 TYPE_ELEMENT, CONF_VALUE_16_BIT(12),
1115 &xx_ei.move_enter_element, EL_EMPTY_SPACE,
1116 &yy_ei.move_enter_element
1120 TYPE_ELEMENT, CONF_VALUE_16_BIT(13),
1121 &xx_ei.move_leave_element, EL_EMPTY_SPACE,
1122 &yy_ei.move_leave_element
1126 TYPE_INTEGER, CONF_VALUE_8_BIT(6),
1127 &xx_ei.move_leave_type, LEAVE_TYPE_UNLIMITED,
1128 &yy_ei.move_leave_type
1133 TYPE_INTEGER, CONF_VALUE_8_BIT(7),
1134 &xx_ei.slippery_type, SLIPPERY_ANY_RANDOM,
1135 &yy_ei.slippery_type
1140 TYPE_INTEGER, CONF_VALUE_8_BIT(8),
1141 &xx_ei.explosion_type, EXPLODES_3X3,
1142 &yy_ei.explosion_type
1146 TYPE_INTEGER, CONF_VALUE_16_BIT(14),
1147 &xx_ei.explosion_delay, 16,
1148 &yy_ei.explosion_delay
1152 TYPE_INTEGER, CONF_VALUE_16_BIT(15),
1153 &xx_ei.ignition_delay, 8,
1154 &yy_ei.ignition_delay
1159 TYPE_CONTENT_LIST, CONF_VALUE_BYTES(2),
1160 &xx_ei.content, EL_EMPTY_SPACE,
1162 &xx_num_contents, 1, 1
1165 // ---------- "num_change_pages" must be the last entry ---------------------
1168 -1, SAVE_CONF_ALWAYS,
1169 TYPE_INTEGER, CONF_VALUE_8_BIT(9),
1170 &xx_ei.num_change_pages, 1,
1171 &yy_ei.num_change_pages
1182 static struct LevelFileConfigInfo chunk_config_CUSX_change[] =
1184 // ---------- "current_change_page" must be the first entry -----------------
1187 -1, SAVE_CONF_ALWAYS,
1188 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
1189 &xx_current_change_page, -1
1192 // ---------- (the remaining entries can be in any order) -------------------
1196 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
1197 &xx_change.can_change, FALSE
1202 TYPE_BITFIELD, CONF_VALUE_32_BIT(1),
1203 &xx_event_bits[0], 0
1207 TYPE_BITFIELD, CONF_VALUE_32_BIT(2),
1208 &xx_event_bits[1], 0
1213 TYPE_BITFIELD, CONF_VALUE_8_BIT(3),
1214 &xx_change.trigger_player, CH_PLAYER_ANY
1218 TYPE_BITFIELD, CONF_VALUE_8_BIT(4),
1219 &xx_change.trigger_side, CH_SIDE_ANY
1223 TYPE_BITFIELD, CONF_VALUE_32_BIT(3),
1224 &xx_change.trigger_page, CH_PAGE_ANY
1229 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
1230 &xx_change.target_element, EL_EMPTY_SPACE
1235 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
1236 &xx_change.delay_fixed, 0
1240 TYPE_INTEGER, CONF_VALUE_16_BIT(3),
1241 &xx_change.delay_random, 0
1245 TYPE_INTEGER, CONF_VALUE_16_BIT(4),
1246 &xx_change.delay_frames, FRAMES_PER_SECOND
1251 TYPE_ELEMENT, CONF_VALUE_16_BIT(5),
1252 &xx_change.initial_trigger_element, EL_EMPTY_SPACE
1257 TYPE_BOOLEAN, CONF_VALUE_8_BIT(6),
1258 &xx_change.explode, FALSE
1262 TYPE_BOOLEAN, CONF_VALUE_8_BIT(7),
1263 &xx_change.use_target_content, FALSE
1267 TYPE_BOOLEAN, CONF_VALUE_8_BIT(8),
1268 &xx_change.only_if_complete, FALSE
1272 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
1273 &xx_change.use_random_replace, FALSE
1277 TYPE_INTEGER, CONF_VALUE_8_BIT(10),
1278 &xx_change.random_percentage, 100
1282 TYPE_INTEGER, CONF_VALUE_8_BIT(11),
1283 &xx_change.replace_when, CP_WHEN_EMPTY
1288 TYPE_BOOLEAN, CONF_VALUE_8_BIT(12),
1289 &xx_change.has_action, FALSE
1293 TYPE_INTEGER, CONF_VALUE_8_BIT(13),
1294 &xx_change.action_type, CA_NO_ACTION
1298 TYPE_INTEGER, CONF_VALUE_8_BIT(14),
1299 &xx_change.action_mode, CA_MODE_UNDEFINED
1303 TYPE_INTEGER, CONF_VALUE_16_BIT(6),
1304 &xx_change.action_arg, CA_ARG_UNDEFINED
1309 TYPE_ELEMENT, CONF_VALUE_16_BIT(7),
1310 &xx_change.action_element, EL_EMPTY_SPACE
1315 TYPE_CONTENT_LIST, CONF_VALUE_BYTES(1),
1316 &xx_change.target_content, EL_EMPTY_SPACE, NULL,
1317 &xx_num_contents, 1, 1
1327 static struct LevelFileConfigInfo chunk_config_GRPX[] =
1331 TYPE_STRING, CONF_VALUE_BYTES(1),
1332 &xx_ei.description[0], -1, NULL,
1333 &xx_string_length_unused, -1, MAX_ELEMENT_NAME_LEN,
1334 &xx_default_description[0]
1339 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1340 &xx_ei.use_gfx_element, FALSE
1344 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
1345 &xx_ei.gfx_element_initial, EL_EMPTY_SPACE
1350 TYPE_INTEGER, CONF_VALUE_8_BIT(2),
1351 &xx_group.choice_mode, ANIM_RANDOM
1356 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(2),
1357 &xx_group.element[0], EL_EMPTY_SPACE, NULL,
1358 &xx_group.num_elements, 1, MAX_ELEMENTS_IN_GROUP
1368 static struct LevelFileConfigInfo chunk_config_CONF[] = // (OBSOLETE)
1372 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
1373 &li.block_snap_field, TRUE
1377 TYPE_BOOLEAN, CONF_VALUE_8_BIT(13),
1378 &li.continuous_snapping, TRUE
1382 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
1383 &li.initial_player_stepsize[0], STEPSIZE_NORMAL
1387 TYPE_BOOLEAN, CONF_VALUE_8_BIT(10),
1388 &li.use_start_element[0], FALSE
1392 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
1393 &li.start_element[0], EL_PLAYER_1
1397 TYPE_BOOLEAN, CONF_VALUE_8_BIT(11),
1398 &li.use_artwork_element[0], FALSE
1402 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
1403 &li.artwork_element[0], EL_PLAYER_1
1407 TYPE_BOOLEAN, CONF_VALUE_8_BIT(12),
1408 &li.use_explosion_element[0], FALSE
1412 TYPE_ELEMENT, CONF_VALUE_16_BIT(3),
1413 &li.explosion_element[0], EL_PLAYER_1
1428 filetype_id_list[] =
1430 { LEVEL_FILE_TYPE_RND, "RND" },
1431 { LEVEL_FILE_TYPE_BD, "BD" },
1432 { LEVEL_FILE_TYPE_EM, "EM" },
1433 { LEVEL_FILE_TYPE_SP, "SP" },
1434 { LEVEL_FILE_TYPE_DX, "DX" },
1435 { LEVEL_FILE_TYPE_SB, "SB" },
1436 { LEVEL_FILE_TYPE_DC, "DC" },
1437 { LEVEL_FILE_TYPE_MM, "MM" },
1438 { LEVEL_FILE_TYPE_MM, "DF" },
1443 // ============================================================================
1444 // level file functions
1445 // ============================================================================
1447 static boolean check_special_flags(char *flag)
1449 if (strEqual(options.special_flags, flag) ||
1450 strEqual(leveldir_current->special_flags, flag))
1456 static struct DateInfo getCurrentDate(void)
1458 time_t epoch_seconds = time(NULL);
1459 struct tm *now = localtime(&epoch_seconds);
1460 struct DateInfo date;
1462 date.year = now->tm_year + 1900;
1463 date.month = now->tm_mon + 1;
1464 date.day = now->tm_mday;
1466 date.src = DATE_SRC_CLOCK;
1471 static void resetEventFlags(struct ElementChangeInfo *change)
1475 for (i = 0; i < NUM_CHANGE_EVENTS; i++)
1476 change->has_event[i] = FALSE;
1479 static void resetEventBits(void)
1483 for (i = 0; i < NUM_CE_BITFIELDS; i++)
1484 xx_event_bits[i] = 0;
1487 static void setEventFlagsFromEventBits(struct ElementChangeInfo *change)
1491 /* important: only change event flag if corresponding event bit is set
1492 (this is because all xx_event_bits[] values are loaded separately,
1493 and all xx_event_bits[] values are set back to zero before loading
1494 another value xx_event_bits[x] (each value representing 32 flags)) */
1496 for (i = 0; i < NUM_CHANGE_EVENTS; i++)
1497 if (xx_event_bits[CH_EVENT_BITFIELD_NR(i)] & CH_EVENT_BIT(i))
1498 change->has_event[i] = TRUE;
1501 static void setEventBitsFromEventFlags(struct ElementChangeInfo *change)
1505 /* in contrast to the above function setEventFlagsFromEventBits(), it
1506 would also be possible to set all bits in xx_event_bits[] to 0 or 1
1507 depending on the corresponding change->has_event[i] values here, as
1508 all xx_event_bits[] values are reset in resetEventBits() before */
1510 for (i = 0; i < NUM_CHANGE_EVENTS; i++)
1511 if (change->has_event[i])
1512 xx_event_bits[CH_EVENT_BITFIELD_NR(i)] |= CH_EVENT_BIT(i);
1515 static char *getDefaultElementDescription(struct ElementInfo *ei)
1517 static char description[MAX_ELEMENT_NAME_LEN + 1];
1518 char *default_description = (ei->custom_description != NULL ?
1519 ei->custom_description :
1520 ei->editor_description);
1523 // always start with reliable default values
1524 for (i = 0; i < MAX_ELEMENT_NAME_LEN + 1; i++)
1525 description[i] = '\0';
1527 // truncate element description to MAX_ELEMENT_NAME_LEN bytes
1528 strncpy(description, default_description, MAX_ELEMENT_NAME_LEN);
1530 return &description[0];
1533 static void setElementDescriptionToDefault(struct ElementInfo *ei)
1535 char *default_description = getDefaultElementDescription(ei);
1538 for (i = 0; i < MAX_ELEMENT_NAME_LEN + 1; i++)
1539 ei->description[i] = default_description[i];
1542 static void setConfigToDefaultsFromConfigList(struct LevelFileConfigInfo *conf)
1546 for (i = 0; conf[i].data_type != -1; i++)
1548 int default_value = conf[i].default_value;
1549 int data_type = conf[i].data_type;
1550 int conf_type = conf[i].conf_type;
1551 int byte_mask = conf_type & CONF_MASK_BYTES;
1553 if (byte_mask == CONF_MASK_MULTI_BYTES)
1555 int default_num_entities = conf[i].default_num_entities;
1556 int max_num_entities = conf[i].max_num_entities;
1558 *(int *)(conf[i].num_entities) = default_num_entities;
1560 if (data_type == TYPE_STRING)
1562 char *default_string = conf[i].default_string;
1563 char *string = (char *)(conf[i].value);
1565 strncpy(string, default_string, max_num_entities);
1567 else if (data_type == TYPE_ELEMENT_LIST)
1569 int *element_array = (int *)(conf[i].value);
1572 for (j = 0; j < max_num_entities; j++)
1573 element_array[j] = default_value;
1575 else if (data_type == TYPE_CONTENT_LIST)
1577 struct Content *content = (struct Content *)(conf[i].value);
1580 for (c = 0; c < max_num_entities; c++)
1581 for (y = 0; y < 3; y++)
1582 for (x = 0; x < 3; x++)
1583 content[c].e[x][y] = default_value;
1586 else // constant size configuration data (1, 2 or 4 bytes)
1588 if (data_type == TYPE_BOOLEAN)
1589 *(boolean *)(conf[i].value) = default_value;
1591 *(int *) (conf[i].value) = default_value;
1596 static void copyConfigFromConfigList(struct LevelFileConfigInfo *conf)
1600 for (i = 0; conf[i].data_type != -1; i++)
1602 int data_type = conf[i].data_type;
1603 int conf_type = conf[i].conf_type;
1604 int byte_mask = conf_type & CONF_MASK_BYTES;
1606 if (byte_mask == CONF_MASK_MULTI_BYTES)
1608 int max_num_entities = conf[i].max_num_entities;
1610 if (data_type == TYPE_STRING)
1612 char *string = (char *)(conf[i].value);
1613 char *string_copy = (char *)(conf[i].value_copy);
1615 strncpy(string_copy, string, max_num_entities);
1617 else if (data_type == TYPE_ELEMENT_LIST)
1619 int *element_array = (int *)(conf[i].value);
1620 int *element_array_copy = (int *)(conf[i].value_copy);
1623 for (j = 0; j < max_num_entities; j++)
1624 element_array_copy[j] = element_array[j];
1626 else if (data_type == TYPE_CONTENT_LIST)
1628 struct Content *content = (struct Content *)(conf[i].value);
1629 struct Content *content_copy = (struct Content *)(conf[i].value_copy);
1632 for (c = 0; c < max_num_entities; c++)
1633 for (y = 0; y < 3; y++)
1634 for (x = 0; x < 3; x++)
1635 content_copy[c].e[x][y] = content[c].e[x][y];
1638 else // constant size configuration data (1, 2 or 4 bytes)
1640 if (data_type == TYPE_BOOLEAN)
1641 *(boolean *)(conf[i].value_copy) = *(boolean *)(conf[i].value);
1643 *(int *) (conf[i].value_copy) = *(int *) (conf[i].value);
1648 void copyElementInfo(struct ElementInfo *ei_from, struct ElementInfo *ei_to)
1652 xx_ei = *ei_from; // copy element data into temporary buffer
1653 yy_ei = *ei_to; // copy element data into temporary buffer
1655 copyConfigFromConfigList(chunk_config_CUSX_base);
1660 // ---------- reinitialize and copy change pages ----------
1662 ei_to->num_change_pages = ei_from->num_change_pages;
1663 ei_to->current_change_page = ei_from->current_change_page;
1665 setElementChangePages(ei_to, ei_to->num_change_pages);
1667 for (i = 0; i < ei_to->num_change_pages; i++)
1668 ei_to->change_page[i] = ei_from->change_page[i];
1670 // ---------- copy group element info ----------
1671 if (ei_from->group != NULL && ei_to->group != NULL) // group or internal
1672 *ei_to->group = *ei_from->group;
1674 // mark this custom element as modified
1675 ei_to->modified_settings = TRUE;
1678 void setElementChangePages(struct ElementInfo *ei, int change_pages)
1680 int change_page_size = sizeof(struct ElementChangeInfo);
1682 ei->num_change_pages = MAX(1, change_pages);
1685 checked_realloc(ei->change_page, ei->num_change_pages * change_page_size);
1687 if (ei->current_change_page >= ei->num_change_pages)
1688 ei->current_change_page = ei->num_change_pages - 1;
1690 ei->change = &ei->change_page[ei->current_change_page];
1693 void setElementChangeInfoToDefaults(struct ElementChangeInfo *change)
1695 xx_change = *change; // copy change data into temporary buffer
1697 setConfigToDefaultsFromConfigList(chunk_config_CUSX_change);
1699 *change = xx_change;
1701 resetEventFlags(change);
1703 change->direct_action = 0;
1704 change->other_action = 0;
1706 change->pre_change_function = NULL;
1707 change->change_function = NULL;
1708 change->post_change_function = NULL;
1711 static void setLevelInfoToDefaults_Level(struct LevelInfo *level)
1715 li = *level; // copy level data into temporary buffer
1716 setConfigToDefaultsFromConfigList(chunk_config_INFO);
1717 *level = li; // copy temporary buffer back to level data
1719 setLevelInfoToDefaults_EM();
1720 setLevelInfoToDefaults_SP();
1721 setLevelInfoToDefaults_MM();
1723 level->native_em_level = &native_em_level;
1724 level->native_sp_level = &native_sp_level;
1725 level->native_mm_level = &native_mm_level;
1727 level->file_version = FILE_VERSION_ACTUAL;
1728 level->game_version = GAME_VERSION_ACTUAL;
1730 level->creation_date = getCurrentDate();
1732 level->encoding_16bit_field = TRUE;
1733 level->encoding_16bit_yamyam = TRUE;
1734 level->encoding_16bit_amoeba = TRUE;
1736 // clear level name and level author string buffers
1737 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
1738 level->name[i] = '\0';
1739 for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
1740 level->author[i] = '\0';
1742 // set level name and level author to default values
1743 strcpy(level->name, NAMELESS_LEVEL_NAME);
1744 strcpy(level->author, ANONYMOUS_NAME);
1746 // set level playfield to playable default level with player and exit
1747 for (x = 0; x < MAX_LEV_FIELDX; x++)
1748 for (y = 0; y < MAX_LEV_FIELDY; y++)
1749 level->field[x][y] = EL_SAND;
1751 level->field[0][0] = EL_PLAYER_1;
1752 level->field[STD_LEV_FIELDX - 1][STD_LEV_FIELDY - 1] = EL_EXIT_CLOSED;
1754 BorderElement = EL_STEELWALL;
1756 // detect custom elements when loading them
1757 level->file_has_custom_elements = FALSE;
1759 // set all bug compatibility flags to "false" => do not emulate this bug
1760 level->use_action_after_change_bug = FALSE;
1762 if (leveldir_current)
1764 // try to determine better author name than 'anonymous'
1765 if (!strEqual(leveldir_current->author, ANONYMOUS_NAME))
1767 strncpy(level->author, leveldir_current->author, MAX_LEVEL_AUTHOR_LEN);
1768 level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
1772 switch (LEVELCLASS(leveldir_current))
1774 case LEVELCLASS_TUTORIAL:
1775 strcpy(level->author, PROGRAM_AUTHOR_STRING);
1778 case LEVELCLASS_CONTRIB:
1779 strncpy(level->author, leveldir_current->name, MAX_LEVEL_AUTHOR_LEN);
1780 level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
1783 case LEVELCLASS_PRIVATE:
1784 strncpy(level->author, getRealName(), MAX_LEVEL_AUTHOR_LEN);
1785 level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
1789 // keep default value
1796 static void setLevelInfoToDefaults_Elements(struct LevelInfo *level)
1798 static boolean clipboard_elements_initialized = FALSE;
1801 InitElementPropertiesStatic();
1803 li = *level; // copy level data into temporary buffer
1804 setConfigToDefaultsFromConfigList(chunk_config_ELEM);
1805 *level = li; // copy temporary buffer back to level data
1807 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1810 struct ElementInfo *ei = &element_info[element];
1812 // never initialize clipboard elements after the very first time
1813 // (to be able to use clipboard elements between several levels)
1814 if (IS_CLIPBOARD_ELEMENT(element) && clipboard_elements_initialized)
1817 if (IS_ENVELOPE(element))
1819 int envelope_nr = element - EL_ENVELOPE_1;
1821 setConfigToDefaultsFromConfigList(chunk_config_NOTE);
1823 level->envelope[envelope_nr] = xx_envelope;
1826 if (IS_CUSTOM_ELEMENT(element) ||
1827 IS_GROUP_ELEMENT(element) ||
1828 IS_INTERNAL_ELEMENT(element))
1830 xx_ei = *ei; // copy element data into temporary buffer
1832 setConfigToDefaultsFromConfigList(chunk_config_CUSX_base);
1837 setElementChangePages(ei, 1);
1838 setElementChangeInfoToDefaults(ei->change);
1840 if (IS_CUSTOM_ELEMENT(element) ||
1841 IS_GROUP_ELEMENT(element) ||
1842 IS_INTERNAL_ELEMENT(element))
1844 setElementDescriptionToDefault(ei);
1846 ei->modified_settings = FALSE;
1849 if (IS_CUSTOM_ELEMENT(element) ||
1850 IS_INTERNAL_ELEMENT(element))
1852 // internal values used in level editor
1854 ei->access_type = 0;
1855 ei->access_layer = 0;
1856 ei->access_protected = 0;
1857 ei->walk_to_action = 0;
1858 ei->smash_targets = 0;
1861 ei->can_explode_by_fire = FALSE;
1862 ei->can_explode_smashed = FALSE;
1863 ei->can_explode_impact = FALSE;
1865 ei->current_change_page = 0;
1868 if (IS_GROUP_ELEMENT(element) ||
1869 IS_INTERNAL_ELEMENT(element))
1871 struct ElementGroupInfo *group;
1873 // initialize memory for list of elements in group
1874 if (ei->group == NULL)
1875 ei->group = checked_malloc(sizeof(struct ElementGroupInfo));
1879 xx_group = *group; // copy group data into temporary buffer
1881 setConfigToDefaultsFromConfigList(chunk_config_GRPX);
1887 clipboard_elements_initialized = TRUE;
1890 static void setLevelInfoToDefaults(struct LevelInfo *level,
1891 boolean level_info_only,
1892 boolean reset_file_status)
1894 setLevelInfoToDefaults_Level(level);
1896 if (!level_info_only)
1897 setLevelInfoToDefaults_Elements(level);
1899 if (reset_file_status)
1901 level->no_valid_file = FALSE;
1902 level->no_level_file = FALSE;
1905 level->changed = FALSE;
1908 static void setFileInfoToDefaults(struct LevelFileInfo *level_file_info)
1910 level_file_info->nr = 0;
1911 level_file_info->type = LEVEL_FILE_TYPE_UNKNOWN;
1912 level_file_info->packed = FALSE;
1914 setString(&level_file_info->basename, NULL);
1915 setString(&level_file_info->filename, NULL);
1918 int getMappedElement_SB(int, boolean);
1920 static void ActivateLevelTemplate(void)
1924 if (check_special_flags("load_xsb_to_ces"))
1926 // fill smaller playfields with padding "beyond border wall" elements
1927 if (level.fieldx < level_template.fieldx ||
1928 level.fieldy < level_template.fieldy)
1930 short field[level.fieldx][level.fieldy];
1931 int new_fieldx = MAX(level.fieldx, level_template.fieldx);
1932 int new_fieldy = MAX(level.fieldy, level_template.fieldy);
1933 int pos_fieldx = (new_fieldx - level.fieldx) / 2;
1934 int pos_fieldy = (new_fieldy - level.fieldy) / 2;
1936 // copy old playfield (which is smaller than the visible area)
1937 for (y = 0; y < level.fieldy; y++) for (x = 0; x < level.fieldx; x++)
1938 field[x][y] = level.field[x][y];
1940 // fill new, larger playfield with "beyond border wall" elements
1941 for (y = 0; y < new_fieldy; y++) for (x = 0; x < new_fieldx; x++)
1942 level.field[x][y] = getMappedElement_SB('_', TRUE);
1944 // copy the old playfield to the middle of the new playfield
1945 for (y = 0; y < level.fieldy; y++) for (x = 0; x < level.fieldx; x++)
1946 level.field[pos_fieldx + x][pos_fieldy + y] = field[x][y];
1948 level.fieldx = new_fieldx;
1949 level.fieldy = new_fieldy;
1953 // Currently there is no special action needed to activate the template
1954 // data, because 'element_info' property settings overwrite the original
1955 // level data, while all other variables do not change.
1957 // Exception: 'from_level_template' elements in the original level playfield
1958 // are overwritten with the corresponding elements at the same position in
1959 // playfield from the level template.
1961 for (x = 0; x < level.fieldx; x++)
1962 for (y = 0; y < level.fieldy; y++)
1963 if (level.field[x][y] == EL_FROM_LEVEL_TEMPLATE)
1964 level.field[x][y] = level_template.field[x][y];
1966 if (check_special_flags("load_xsb_to_ces"))
1968 struct LevelInfo level_backup = level;
1970 // overwrite all individual level settings from template level settings
1971 level = level_template;
1973 // restore level file info
1974 level.file_info = level_backup.file_info;
1976 // restore playfield size
1977 level.fieldx = level_backup.fieldx;
1978 level.fieldy = level_backup.fieldy;
1980 // restore playfield content
1981 for (x = 0; x < level.fieldx; x++)
1982 for (y = 0; y < level.fieldy; y++)
1983 level.field[x][y] = level_backup.field[x][y];
1985 // restore name and author from individual level
1986 strcpy(level.name, level_backup.name);
1987 strcpy(level.author, level_backup.author);
1989 // restore flag "use_custom_template"
1990 level.use_custom_template = level_backup.use_custom_template;
1994 static char *getLevelFilenameFromBasename(char *basename)
1996 static char *filename = NULL;
1998 checked_free(filename);
2000 filename = getPath2(getCurrentLevelDir(), basename);
2005 static int getFileTypeFromBasename(char *basename)
2007 // !!! ALSO SEE COMMENT IN checkForPackageFromBasename() !!!
2009 static char *filename = NULL;
2010 struct stat file_status;
2012 // ---------- try to determine file type from filename ----------
2014 // check for typical filename of a Supaplex level package file
2015 if (strlen(basename) == 10 && strPrefixLower(basename, "levels.d"))
2016 return LEVEL_FILE_TYPE_SP;
2018 // check for typical filename of a Diamond Caves II level package file
2019 if (strSuffixLower(basename, ".dc") ||
2020 strSuffixLower(basename, ".dc2"))
2021 return LEVEL_FILE_TYPE_DC;
2023 // check for typical filename of a Sokoban level package file
2024 if (strSuffixLower(basename, ".xsb") &&
2025 strchr(basename, '%') == NULL)
2026 return LEVEL_FILE_TYPE_SB;
2028 // ---------- try to determine file type from filesize ----------
2030 checked_free(filename);
2031 filename = getPath2(getCurrentLevelDir(), basename);
2033 if (stat(filename, &file_status) == 0)
2035 // check for typical filesize of a Supaplex level package file
2036 if (file_status.st_size == 170496)
2037 return LEVEL_FILE_TYPE_SP;
2040 return LEVEL_FILE_TYPE_UNKNOWN;
2043 static int getFileTypeFromMagicBytes(char *filename, int type)
2047 if ((file = openFile(filename, MODE_READ)))
2049 char chunk_name[CHUNK_ID_LEN + 1];
2051 getFileChunkBE(file, chunk_name, NULL);
2053 if (strEqual(chunk_name, "MMII") ||
2054 strEqual(chunk_name, "MIRR"))
2055 type = LEVEL_FILE_TYPE_MM;
2063 static boolean checkForPackageFromBasename(char *basename)
2065 // !!! WON'T WORK ANYMORE IF getFileTypeFromBasename() ALSO DETECTS !!!
2066 // !!! SINGLE LEVELS (CURRENTLY ONLY DETECTS LEVEL PACKAGES !!!
2068 return (getFileTypeFromBasename(basename) != LEVEL_FILE_TYPE_UNKNOWN);
2071 static char *getSingleLevelBasenameExt(int nr, char *extension)
2073 static char basename[MAX_FILENAME_LEN];
2076 sprintf(basename, "%s", LEVELTEMPLATE_FILENAME);
2078 sprintf(basename, "%03d.%s", nr, extension);
2083 static char *getSingleLevelBasename(int nr)
2085 return getSingleLevelBasenameExt(nr, LEVELFILE_EXTENSION);
2088 static char *getPackedLevelBasename(int type)
2090 static char basename[MAX_FILENAME_LEN];
2091 char *directory = getCurrentLevelDir();
2093 DirectoryEntry *dir_entry;
2095 strcpy(basename, UNDEFINED_FILENAME); // default: undefined file
2097 if ((dir = openDirectory(directory)) == NULL)
2099 Warn("cannot read current level directory '%s'", directory);
2104 while ((dir_entry = readDirectory(dir)) != NULL) // loop all entries
2106 char *entry_basename = dir_entry->basename;
2107 int entry_type = getFileTypeFromBasename(entry_basename);
2109 if (entry_type != LEVEL_FILE_TYPE_UNKNOWN) // found valid level package
2111 if (type == LEVEL_FILE_TYPE_UNKNOWN ||
2114 strcpy(basename, entry_basename);
2121 closeDirectory(dir);
2126 static char *getSingleLevelFilename(int nr)
2128 return getLevelFilenameFromBasename(getSingleLevelBasename(nr));
2131 #if ENABLE_UNUSED_CODE
2132 static char *getPackedLevelFilename(int type)
2134 return getLevelFilenameFromBasename(getPackedLevelBasename(type));
2138 char *getDefaultLevelFilename(int nr)
2140 return getSingleLevelFilename(nr);
2143 #if ENABLE_UNUSED_CODE
2144 static void setLevelFileInfo_SingleLevelFilename(struct LevelFileInfo *lfi,
2148 lfi->packed = FALSE;
2150 setString(&lfi->basename, getSingleLevelBasename(lfi->nr, lfi->type));
2151 setString(&lfi->filename, getLevelFilenameFromBasename(lfi->basename));
2155 static void setLevelFileInfo_FormatLevelFilename(struct LevelFileInfo *lfi,
2156 int type, char *format, ...)
2158 static char basename[MAX_FILENAME_LEN];
2161 va_start(ap, format);
2162 vsprintf(basename, format, ap);
2166 lfi->packed = FALSE;
2168 setString(&lfi->basename, basename);
2169 setString(&lfi->filename, getLevelFilenameFromBasename(lfi->basename));
2172 static void setLevelFileInfo_PackedLevelFilename(struct LevelFileInfo *lfi,
2178 setString(&lfi->basename, getPackedLevelBasename(lfi->type));
2179 setString(&lfi->filename, getLevelFilenameFromBasename(lfi->basename));
2182 static int getFiletypeFromID(char *filetype_id)
2184 char *filetype_id_lower;
2185 int filetype = LEVEL_FILE_TYPE_UNKNOWN;
2188 if (filetype_id == NULL)
2189 return LEVEL_FILE_TYPE_UNKNOWN;
2191 filetype_id_lower = getStringToLower(filetype_id);
2193 for (i = 0; filetype_id_list[i].id != NULL; i++)
2195 char *id_lower = getStringToLower(filetype_id_list[i].id);
2197 if (strEqual(filetype_id_lower, id_lower))
2198 filetype = filetype_id_list[i].filetype;
2202 if (filetype != LEVEL_FILE_TYPE_UNKNOWN)
2206 free(filetype_id_lower);
2211 char *getLocalLevelTemplateFilename(void)
2213 return getDefaultLevelFilename(-1);
2216 char *getGlobalLevelTemplateFilename(void)
2218 // global variable "leveldir_current" must be modified in the loop below
2219 LevelDirTree *leveldir_current_last = leveldir_current;
2220 char *filename = NULL;
2222 // check for template level in path from current to topmost tree node
2224 while (leveldir_current != NULL)
2226 filename = getDefaultLevelFilename(-1);
2228 if (fileExists(filename))
2231 leveldir_current = leveldir_current->node_parent;
2234 // restore global variable "leveldir_current" modified in above loop
2235 leveldir_current = leveldir_current_last;
2240 static void determineLevelFileInfo_Filename(struct LevelFileInfo *lfi)
2244 // special case: level number is negative => check for level template file
2247 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
2248 getSingleLevelBasename(-1));
2250 // replace local level template filename with global template filename
2251 setString(&lfi->filename, getGlobalLevelTemplateFilename());
2253 // no fallback if template file not existing
2257 // special case: check for file name/pattern specified in "levelinfo.conf"
2258 if (leveldir_current->level_filename != NULL)
2260 int filetype = getFiletypeFromID(leveldir_current->level_filetype);
2262 setLevelFileInfo_FormatLevelFilename(lfi, filetype,
2263 leveldir_current->level_filename, nr);
2265 lfi->packed = checkForPackageFromBasename(leveldir_current->level_filename);
2267 if (fileExists(lfi->filename))
2270 else if (leveldir_current->level_filetype != NULL)
2272 int filetype = getFiletypeFromID(leveldir_current->level_filetype);
2274 // check for specified native level file with standard file name
2275 setLevelFileInfo_FormatLevelFilename(lfi, filetype,
2276 "%03d.%s", nr, LEVELFILE_EXTENSION);
2277 if (fileExists(lfi->filename))
2281 // check for native Rocks'n'Diamonds level file
2282 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
2283 "%03d.%s", nr, LEVELFILE_EXTENSION);
2284 if (fileExists(lfi->filename))
2287 // check for Emerald Mine level file (V1)
2288 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "a%c%c",
2289 'a' + (nr / 10) % 26, '0' + nr % 10);
2290 if (fileExists(lfi->filename))
2292 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "A%c%c",
2293 'A' + (nr / 10) % 26, '0' + nr % 10);
2294 if (fileExists(lfi->filename))
2297 // check for Emerald Mine level file (V2 to V5)
2298 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%d", nr);
2299 if (fileExists(lfi->filename))
2302 // check for Emerald Mine level file (V6 / single mode)
2303 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02ds", nr);
2304 if (fileExists(lfi->filename))
2306 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02dS", nr);
2307 if (fileExists(lfi->filename))
2310 // check for Emerald Mine level file (V6 / teamwork mode)
2311 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02dt", nr);
2312 if (fileExists(lfi->filename))
2314 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02dT", nr);
2315 if (fileExists(lfi->filename))
2318 // check for various packed level file formats
2319 setLevelFileInfo_PackedLevelFilename(lfi, LEVEL_FILE_TYPE_UNKNOWN);
2320 if (fileExists(lfi->filename))
2323 // no known level file found -- use default values (and fail later)
2324 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
2325 "%03d.%s", nr, LEVELFILE_EXTENSION);
2328 static void determineLevelFileInfo_Filetype(struct LevelFileInfo *lfi)
2330 if (lfi->type == LEVEL_FILE_TYPE_UNKNOWN)
2331 lfi->type = getFileTypeFromBasename(lfi->basename);
2333 if (lfi->type == LEVEL_FILE_TYPE_RND)
2334 lfi->type = getFileTypeFromMagicBytes(lfi->filename, lfi->type);
2337 static void setLevelFileInfo(struct LevelFileInfo *level_file_info, int nr)
2339 // always start with reliable default values
2340 setFileInfoToDefaults(level_file_info);
2342 level_file_info->nr = nr; // set requested level number
2344 determineLevelFileInfo_Filename(level_file_info);
2345 determineLevelFileInfo_Filetype(level_file_info);
2348 static void copyLevelFileInfo(struct LevelFileInfo *lfi_from,
2349 struct LevelFileInfo *lfi_to)
2351 lfi_to->nr = lfi_from->nr;
2352 lfi_to->type = lfi_from->type;
2353 lfi_to->packed = lfi_from->packed;
2355 setString(&lfi_to->basename, lfi_from->basename);
2356 setString(&lfi_to->filename, lfi_from->filename);
2359 // ----------------------------------------------------------------------------
2360 // functions for loading R'n'D level
2361 // ----------------------------------------------------------------------------
2363 int getMappedElement(int element)
2365 // remap some (historic, now obsolete) elements
2369 case EL_PLAYER_OBSOLETE:
2370 element = EL_PLAYER_1;
2373 case EL_KEY_OBSOLETE:
2377 case EL_EM_KEY_1_FILE_OBSOLETE:
2378 element = EL_EM_KEY_1;
2381 case EL_EM_KEY_2_FILE_OBSOLETE:
2382 element = EL_EM_KEY_2;
2385 case EL_EM_KEY_3_FILE_OBSOLETE:
2386 element = EL_EM_KEY_3;
2389 case EL_EM_KEY_4_FILE_OBSOLETE:
2390 element = EL_EM_KEY_4;
2393 case EL_ENVELOPE_OBSOLETE:
2394 element = EL_ENVELOPE_1;
2402 if (element >= NUM_FILE_ELEMENTS)
2404 Warn("invalid level element %d", element);
2406 element = EL_UNKNOWN;
2414 static int getMappedElementByVersion(int element, int game_version)
2416 // remap some elements due to certain game version
2418 if (game_version <= VERSION_IDENT(2,2,0,0))
2420 // map game font elements
2421 element = (element == EL_CHAR('[') ? EL_CHAR_AUMLAUT :
2422 element == EL_CHAR('\\') ? EL_CHAR_OUMLAUT :
2423 element == EL_CHAR(']') ? EL_CHAR_UUMLAUT :
2424 element == EL_CHAR('^') ? EL_CHAR_COPYRIGHT : element);
2427 if (game_version < VERSION_IDENT(3,0,0,0))
2429 // map Supaplex gravity tube elements
2430 element = (element == EL_SP_GRAVITY_PORT_LEFT ? EL_SP_PORT_LEFT :
2431 element == EL_SP_GRAVITY_PORT_RIGHT ? EL_SP_PORT_RIGHT :
2432 element == EL_SP_GRAVITY_PORT_UP ? EL_SP_PORT_UP :
2433 element == EL_SP_GRAVITY_PORT_DOWN ? EL_SP_PORT_DOWN :
2440 static int LoadLevel_VERS(File *file, int chunk_size, struct LevelInfo *level)
2442 level->file_version = getFileVersion(file);
2443 level->game_version = getFileVersion(file);
2448 static int LoadLevel_DATE(File *file, int chunk_size, struct LevelInfo *level)
2450 level->creation_date.year = getFile16BitBE(file);
2451 level->creation_date.month = getFile8Bit(file);
2452 level->creation_date.day = getFile8Bit(file);
2454 level->creation_date.src = DATE_SRC_LEVELFILE;
2459 static int LoadLevel_HEAD(File *file, int chunk_size, struct LevelInfo *level)
2461 int initial_player_stepsize;
2462 int initial_player_gravity;
2465 level->fieldx = getFile8Bit(file);
2466 level->fieldy = getFile8Bit(file);
2468 level->time = getFile16BitBE(file);
2469 level->gems_needed = getFile16BitBE(file);
2471 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
2472 level->name[i] = getFile8Bit(file);
2473 level->name[MAX_LEVEL_NAME_LEN] = 0;
2475 for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
2476 level->score[i] = getFile8Bit(file);
2478 level->num_yamyam_contents = STD_ELEMENT_CONTENTS;
2479 for (i = 0; i < STD_ELEMENT_CONTENTS; i++)
2480 for (y = 0; y < 3; y++)
2481 for (x = 0; x < 3; x++)
2482 level->yamyam_content[i].e[x][y] = getMappedElement(getFile8Bit(file));
2484 level->amoeba_speed = getFile8Bit(file);
2485 level->time_magic_wall = getFile8Bit(file);
2486 level->time_wheel = getFile8Bit(file);
2487 level->amoeba_content = getMappedElement(getFile8Bit(file));
2489 initial_player_stepsize = (getFile8Bit(file) == 1 ? STEPSIZE_FAST :
2492 for (i = 0; i < MAX_PLAYERS; i++)
2493 level->initial_player_stepsize[i] = initial_player_stepsize;
2495 initial_player_gravity = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2497 for (i = 0; i < MAX_PLAYERS; i++)
2498 level->initial_player_gravity[i] = initial_player_gravity;
2500 level->encoding_16bit_field = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2501 level->em_slippery_gems = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2503 level->use_custom_template = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2505 level->block_last_field = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2506 level->sp_block_last_field = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2507 level->can_move_into_acid_bits = getFile32BitBE(file);
2508 level->dont_collide_with_bits = getFile8Bit(file);
2510 level->use_spring_bug = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2511 level->use_step_counter = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2513 level->instant_relocation = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2514 level->can_pass_to_walkable = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2515 level->grow_into_diggable = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2517 level->game_engine_type = getFile8Bit(file);
2519 ReadUnusedBytesFromFile(file, LEVEL_CHUNK_HEAD_UNUSED);
2524 static int LoadLevel_NAME(File *file, int chunk_size, struct LevelInfo *level)
2528 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
2529 level->name[i] = getFile8Bit(file);
2530 level->name[MAX_LEVEL_NAME_LEN] = 0;
2535 static int LoadLevel_AUTH(File *file, int chunk_size, struct LevelInfo *level)
2539 for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
2540 level->author[i] = getFile8Bit(file);
2541 level->author[MAX_LEVEL_AUTHOR_LEN] = 0;
2546 static int LoadLevel_BODY(File *file, int chunk_size, struct LevelInfo *level)
2549 int chunk_size_expected = level->fieldx * level->fieldy;
2551 /* Note: "chunk_size" was wrong before version 2.0 when elements are
2552 stored with 16-bit encoding (and should be twice as big then).
2553 Even worse, playfield data was stored 16-bit when only yamyam content
2554 contained 16-bit elements and vice versa. */
2556 if (level->encoding_16bit_field && level->file_version >= FILE_VERSION_2_0)
2557 chunk_size_expected *= 2;
2559 if (chunk_size_expected != chunk_size)
2561 ReadUnusedBytesFromFile(file, chunk_size);
2562 return chunk_size_expected;
2565 for (y = 0; y < level->fieldy; y++)
2566 for (x = 0; x < level->fieldx; x++)
2567 level->field[x][y] =
2568 getMappedElement(level->encoding_16bit_field ? getFile16BitBE(file) :
2573 static int LoadLevel_CONT(File *file, int chunk_size, struct LevelInfo *level)
2576 int header_size = 4;
2577 int content_size = MAX_ELEMENT_CONTENTS * 3 * 3;
2578 int chunk_size_expected = header_size + content_size;
2580 /* Note: "chunk_size" was wrong before version 2.0 when elements are
2581 stored with 16-bit encoding (and should be twice as big then).
2582 Even worse, playfield data was stored 16-bit when only yamyam content
2583 contained 16-bit elements and vice versa. */
2585 if (level->encoding_16bit_field && level->file_version >= FILE_VERSION_2_0)
2586 chunk_size_expected += content_size;
2588 if (chunk_size_expected != chunk_size)
2590 ReadUnusedBytesFromFile(file, chunk_size);
2591 return chunk_size_expected;
2595 level->num_yamyam_contents = getFile8Bit(file);
2599 // correct invalid number of content fields -- should never happen
2600 if (level->num_yamyam_contents < 1 ||
2601 level->num_yamyam_contents > MAX_ELEMENT_CONTENTS)
2602 level->num_yamyam_contents = STD_ELEMENT_CONTENTS;
2604 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
2605 for (y = 0; y < 3; y++)
2606 for (x = 0; x < 3; x++)
2607 level->yamyam_content[i].e[x][y] =
2608 getMappedElement(level->encoding_16bit_field ?
2609 getFile16BitBE(file) : getFile8Bit(file));
2613 static int LoadLevel_CNT2(File *file, int chunk_size, struct LevelInfo *level)
2618 int content_array[MAX_ELEMENT_CONTENTS][3][3];
2620 element = getMappedElement(getFile16BitBE(file));
2621 num_contents = getFile8Bit(file);
2623 getFile8Bit(file); // content x size (unused)
2624 getFile8Bit(file); // content y size (unused)
2626 ReadUnusedBytesFromFile(file, LEVEL_CHUNK_CNT2_UNUSED);
2628 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
2629 for (y = 0; y < 3; y++)
2630 for (x = 0; x < 3; x++)
2631 content_array[i][x][y] = getMappedElement(getFile16BitBE(file));
2633 // correct invalid number of content fields -- should never happen
2634 if (num_contents < 1 || num_contents > MAX_ELEMENT_CONTENTS)
2635 num_contents = STD_ELEMENT_CONTENTS;
2637 if (element == EL_YAMYAM)
2639 level->num_yamyam_contents = num_contents;
2641 for (i = 0; i < num_contents; i++)
2642 for (y = 0; y < 3; y++)
2643 for (x = 0; x < 3; x++)
2644 level->yamyam_content[i].e[x][y] = content_array[i][x][y];
2646 else if (element == EL_BD_AMOEBA)
2648 level->amoeba_content = content_array[0][0][0];
2652 Warn("cannot load content for element '%d'", element);
2658 static int LoadLevel_CNT3(File *file, int chunk_size, struct LevelInfo *level)
2664 int chunk_size_expected;
2666 element = getMappedElement(getFile16BitBE(file));
2667 if (!IS_ENVELOPE(element))
2668 element = EL_ENVELOPE_1;
2670 envelope_nr = element - EL_ENVELOPE_1;
2672 envelope_len = getFile16BitBE(file);
2674 level->envelope[envelope_nr].xsize = getFile8Bit(file);
2675 level->envelope[envelope_nr].ysize = getFile8Bit(file);
2677 ReadUnusedBytesFromFile(file, LEVEL_CHUNK_CNT3_UNUSED);
2679 chunk_size_expected = LEVEL_CHUNK_CNT3_SIZE(envelope_len);
2680 if (chunk_size_expected != chunk_size)
2682 ReadUnusedBytesFromFile(file, chunk_size - LEVEL_CHUNK_CNT3_HEADER);
2683 return chunk_size_expected;
2686 for (i = 0; i < envelope_len; i++)
2687 level->envelope[envelope_nr].text[i] = getFile8Bit(file);
2692 static int LoadLevel_CUS1(File *file, int chunk_size, struct LevelInfo *level)
2694 int num_changed_custom_elements = getFile16BitBE(file);
2695 int chunk_size_expected = 2 + num_changed_custom_elements * 6;
2698 if (chunk_size_expected != chunk_size)
2700 ReadUnusedBytesFromFile(file, chunk_size - 2);
2701 return chunk_size_expected;
2704 for (i = 0; i < num_changed_custom_elements; i++)
2706 int element = getMappedElement(getFile16BitBE(file));
2707 int properties = getFile32BitBE(file);
2709 if (IS_CUSTOM_ELEMENT(element))
2710 element_info[element].properties[EP_BITFIELD_BASE_NR] = properties;
2712 Warn("invalid custom element number %d", element);
2714 // older game versions that wrote level files with CUS1 chunks used
2715 // different default push delay values (not yet stored in level file)
2716 element_info[element].push_delay_fixed = 2;
2717 element_info[element].push_delay_random = 8;
2720 level->file_has_custom_elements = TRUE;
2725 static int LoadLevel_CUS2(File *file, int chunk_size, struct LevelInfo *level)
2727 int num_changed_custom_elements = getFile16BitBE(file);
2728 int chunk_size_expected = 2 + num_changed_custom_elements * 4;
2731 if (chunk_size_expected != chunk_size)
2733 ReadUnusedBytesFromFile(file, chunk_size - 2);
2734 return chunk_size_expected;
2737 for (i = 0; i < num_changed_custom_elements; i++)
2739 int element = getMappedElement(getFile16BitBE(file));
2740 int custom_target_element = getMappedElement(getFile16BitBE(file));
2742 if (IS_CUSTOM_ELEMENT(element))
2743 element_info[element].change->target_element = custom_target_element;
2745 Warn("invalid custom element number %d", element);
2748 level->file_has_custom_elements = TRUE;
2753 static int LoadLevel_CUS3(File *file, int chunk_size, struct LevelInfo *level)
2755 int num_changed_custom_elements = getFile16BitBE(file);
2756 int chunk_size_expected = LEVEL_CHUNK_CUS3_SIZE(num_changed_custom_elements);
2759 if (chunk_size_expected != chunk_size)
2761 ReadUnusedBytesFromFile(file, chunk_size - 2);
2762 return chunk_size_expected;
2765 for (i = 0; i < num_changed_custom_elements; i++)
2767 int element = getMappedElement(getFile16BitBE(file));
2768 struct ElementInfo *ei = &element_info[element];
2769 unsigned int event_bits;
2771 if (!IS_CUSTOM_ELEMENT(element))
2773 Warn("invalid custom element number %d", element);
2775 element = EL_INTERNAL_DUMMY;
2778 for (j = 0; j < MAX_ELEMENT_NAME_LEN; j++)
2779 ei->description[j] = getFile8Bit(file);
2780 ei->description[MAX_ELEMENT_NAME_LEN] = 0;
2782 ei->properties[EP_BITFIELD_BASE_NR] = getFile32BitBE(file);
2784 // some free bytes for future properties and padding
2785 ReadUnusedBytesFromFile(file, 7);
2787 ei->use_gfx_element = getFile8Bit(file);
2788 ei->gfx_element_initial = getMappedElement(getFile16BitBE(file));
2790 ei->collect_score_initial = getFile8Bit(file);
2791 ei->collect_count_initial = getFile8Bit(file);
2793 ei->push_delay_fixed = getFile16BitBE(file);
2794 ei->push_delay_random = getFile16BitBE(file);
2795 ei->move_delay_fixed = getFile16BitBE(file);
2796 ei->move_delay_random = getFile16BitBE(file);
2798 ei->move_pattern = getFile16BitBE(file);
2799 ei->move_direction_initial = getFile8Bit(file);
2800 ei->move_stepsize = getFile8Bit(file);
2802 for (y = 0; y < 3; y++)
2803 for (x = 0; x < 3; x++)
2804 ei->content.e[x][y] = getMappedElement(getFile16BitBE(file));
2806 event_bits = getFile32BitBE(file);
2807 for (j = 0; j < NUM_CHANGE_EVENTS; j++)
2808 if (event_bits & (1 << j))
2809 ei->change->has_event[j] = TRUE;
2811 ei->change->target_element = getMappedElement(getFile16BitBE(file));
2813 ei->change->delay_fixed = getFile16BitBE(file);
2814 ei->change->delay_random = getFile16BitBE(file);
2815 ei->change->delay_frames = getFile16BitBE(file);
2817 ei->change->initial_trigger_element= getMappedElement(getFile16BitBE(file));
2819 ei->change->explode = getFile8Bit(file);
2820 ei->change->use_target_content = getFile8Bit(file);
2821 ei->change->only_if_complete = getFile8Bit(file);
2822 ei->change->use_random_replace = getFile8Bit(file);
2824 ei->change->random_percentage = getFile8Bit(file);
2825 ei->change->replace_when = getFile8Bit(file);
2827 for (y = 0; y < 3; y++)
2828 for (x = 0; x < 3; x++)
2829 ei->change->target_content.e[x][y] =
2830 getMappedElement(getFile16BitBE(file));
2832 ei->slippery_type = getFile8Bit(file);
2834 // some free bytes for future properties and padding
2835 ReadUnusedBytesFromFile(file, LEVEL_CPART_CUS3_UNUSED);
2837 // mark that this custom element has been modified
2838 ei->modified_settings = TRUE;
2841 level->file_has_custom_elements = TRUE;
2846 static int LoadLevel_CUS4(File *file, int chunk_size, struct LevelInfo *level)
2848 struct ElementInfo *ei;
2849 int chunk_size_expected;
2853 // ---------- custom element base property values (96 bytes) ----------------
2855 element = getMappedElement(getFile16BitBE(file));
2857 if (!IS_CUSTOM_ELEMENT(element))
2859 Warn("invalid custom element number %d", element);
2861 ReadUnusedBytesFromFile(file, chunk_size - 2);
2866 ei = &element_info[element];
2868 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
2869 ei->description[i] = getFile8Bit(file);
2870 ei->description[MAX_ELEMENT_NAME_LEN] = 0;
2872 ei->properties[EP_BITFIELD_BASE_NR] = getFile32BitBE(file);
2874 ReadUnusedBytesFromFile(file, 4); // reserved for more base properties
2876 ei->num_change_pages = getFile8Bit(file);
2878 chunk_size_expected = LEVEL_CHUNK_CUS4_SIZE(ei->num_change_pages);
2879 if (chunk_size_expected != chunk_size)
2881 ReadUnusedBytesFromFile(file, chunk_size - 43);
2882 return chunk_size_expected;
2885 ei->ce_value_fixed_initial = getFile16BitBE(file);
2886 ei->ce_value_random_initial = getFile16BitBE(file);
2887 ei->use_last_ce_value = getFile8Bit(file);
2889 ei->use_gfx_element = getFile8Bit(file);
2890 ei->gfx_element_initial = getMappedElement(getFile16BitBE(file));
2892 ei->collect_score_initial = getFile8Bit(file);
2893 ei->collect_count_initial = getFile8Bit(file);
2895 ei->drop_delay_fixed = getFile8Bit(file);
2896 ei->push_delay_fixed = getFile8Bit(file);
2897 ei->drop_delay_random = getFile8Bit(file);
2898 ei->push_delay_random = getFile8Bit(file);
2899 ei->move_delay_fixed = getFile16BitBE(file);
2900 ei->move_delay_random = getFile16BitBE(file);
2902 // bits 0 - 15 of "move_pattern" ...
2903 ei->move_pattern = getFile16BitBE(file);
2904 ei->move_direction_initial = getFile8Bit(file);
2905 ei->move_stepsize = getFile8Bit(file);
2907 ei->slippery_type = getFile8Bit(file);
2909 for (y = 0; y < 3; y++)
2910 for (x = 0; x < 3; x++)
2911 ei->content.e[x][y] = getMappedElement(getFile16BitBE(file));
2913 ei->move_enter_element = getMappedElement(getFile16BitBE(file));
2914 ei->move_leave_element = getMappedElement(getFile16BitBE(file));
2915 ei->move_leave_type = getFile8Bit(file);
2917 // ... bits 16 - 31 of "move_pattern" (not nice, but downward compatible)
2918 ei->move_pattern |= (getFile16BitBE(file) << 16);
2920 ei->access_direction = getFile8Bit(file);
2922 ei->explosion_delay = getFile8Bit(file);
2923 ei->ignition_delay = getFile8Bit(file);
2924 ei->explosion_type = getFile8Bit(file);
2926 // some free bytes for future custom property values and padding
2927 ReadUnusedBytesFromFile(file, 1);
2929 // ---------- change page property values (48 bytes) ------------------------
2931 setElementChangePages(ei, ei->num_change_pages);
2933 for (i = 0; i < ei->num_change_pages; i++)
2935 struct ElementChangeInfo *change = &ei->change_page[i];
2936 unsigned int event_bits;
2938 // always start with reliable default values
2939 setElementChangeInfoToDefaults(change);
2941 // bits 0 - 31 of "has_event[]" ...
2942 event_bits = getFile32BitBE(file);
2943 for (j = 0; j < MIN(NUM_CHANGE_EVENTS, 32); j++)
2944 if (event_bits & (1 << j))
2945 change->has_event[j] = TRUE;
2947 change->target_element = getMappedElement(getFile16BitBE(file));
2949 change->delay_fixed = getFile16BitBE(file);
2950 change->delay_random = getFile16BitBE(file);
2951 change->delay_frames = getFile16BitBE(file);
2953 change->initial_trigger_element = getMappedElement(getFile16BitBE(file));
2955 change->explode = getFile8Bit(file);
2956 change->use_target_content = getFile8Bit(file);
2957 change->only_if_complete = getFile8Bit(file);
2958 change->use_random_replace = getFile8Bit(file);
2960 change->random_percentage = getFile8Bit(file);
2961 change->replace_when = getFile8Bit(file);
2963 for (y = 0; y < 3; y++)
2964 for (x = 0; x < 3; x++)
2965 change->target_content.e[x][y]= getMappedElement(getFile16BitBE(file));
2967 change->can_change = getFile8Bit(file);
2969 change->trigger_side = getFile8Bit(file);
2971 change->trigger_player = getFile8Bit(file);
2972 change->trigger_page = getFile8Bit(file);
2974 change->trigger_page = (change->trigger_page == CH_PAGE_ANY_FILE ?
2975 CH_PAGE_ANY : (1 << change->trigger_page));
2977 change->has_action = getFile8Bit(file);
2978 change->action_type = getFile8Bit(file);
2979 change->action_mode = getFile8Bit(file);
2980 change->action_arg = getFile16BitBE(file);
2982 // ... bits 32 - 39 of "has_event[]" (not nice, but downward compatible)
2983 event_bits = getFile8Bit(file);
2984 for (j = 32; j < NUM_CHANGE_EVENTS; j++)
2985 if (event_bits & (1 << (j - 32)))
2986 change->has_event[j] = TRUE;
2989 // mark this custom element as modified
2990 ei->modified_settings = TRUE;
2992 level->file_has_custom_elements = TRUE;
2997 static int LoadLevel_GRP1(File *file, int chunk_size, struct LevelInfo *level)
2999 struct ElementInfo *ei;
3000 struct ElementGroupInfo *group;
3004 element = getMappedElement(getFile16BitBE(file));
3006 if (!IS_GROUP_ELEMENT(element))
3008 Warn("invalid group element number %d", element);
3010 ReadUnusedBytesFromFile(file, chunk_size - 2);
3015 ei = &element_info[element];
3017 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
3018 ei->description[i] = getFile8Bit(file);
3019 ei->description[MAX_ELEMENT_NAME_LEN] = 0;
3021 group = element_info[element].group;
3023 group->num_elements = getFile8Bit(file);
3025 ei->use_gfx_element = getFile8Bit(file);
3026 ei->gfx_element_initial = getMappedElement(getFile16BitBE(file));
3028 group->choice_mode = getFile8Bit(file);
3030 // some free bytes for future values and padding
3031 ReadUnusedBytesFromFile(file, 3);
3033 for (i = 0; i < MAX_ELEMENTS_IN_GROUP; i++)
3034 group->element[i] = getMappedElement(getFile16BitBE(file));
3036 // mark this group element as modified
3037 element_info[element].modified_settings = TRUE;
3039 level->file_has_custom_elements = TRUE;
3044 static int LoadLevel_MicroChunk(File *file, struct LevelFileConfigInfo *conf,
3045 int element, int real_element)
3047 int micro_chunk_size = 0;
3048 int conf_type = getFile8Bit(file);
3049 int byte_mask = conf_type & CONF_MASK_BYTES;
3050 boolean element_found = FALSE;
3053 micro_chunk_size += 1;
3055 if (byte_mask == CONF_MASK_MULTI_BYTES)
3057 int num_bytes = getFile16BitBE(file);
3058 byte *buffer = checked_malloc(num_bytes);
3060 ReadBytesFromFile(file, buffer, num_bytes);
3062 for (i = 0; conf[i].data_type != -1; i++)
3064 if (conf[i].element == element &&
3065 conf[i].conf_type == conf_type)
3067 int data_type = conf[i].data_type;
3068 int num_entities = num_bytes / CONF_ENTITY_NUM_BYTES(data_type);
3069 int max_num_entities = conf[i].max_num_entities;
3071 if (num_entities > max_num_entities)
3073 Warn("truncating number of entities for element %d from %d to %d",
3074 element, num_entities, max_num_entities);
3076 num_entities = max_num_entities;
3079 if (num_entities == 0 && (data_type == TYPE_ELEMENT_LIST ||
3080 data_type == TYPE_CONTENT_LIST))
3082 // for element and content lists, zero entities are not allowed
3083 Warn("found empty list of entities for element %d", element);
3085 // do not set "num_entities" here to prevent reading behind buffer
3087 *(int *)(conf[i].num_entities) = 1; // at least one is required
3091 *(int *)(conf[i].num_entities) = num_entities;
3094 element_found = TRUE;
3096 if (data_type == TYPE_STRING)
3098 char *string = (char *)(conf[i].value);
3101 for (j = 0; j < max_num_entities; j++)
3102 string[j] = (j < num_entities ? buffer[j] : '\0');
3104 else if (data_type == TYPE_ELEMENT_LIST)
3106 int *element_array = (int *)(conf[i].value);
3109 for (j = 0; j < num_entities; j++)
3111 getMappedElement(CONF_ELEMENTS_ELEMENT(buffer, j));
3113 else if (data_type == TYPE_CONTENT_LIST)
3115 struct Content *content= (struct Content *)(conf[i].value);
3118 for (c = 0; c < num_entities; c++)
3119 for (y = 0; y < 3; y++)
3120 for (x = 0; x < 3; x++)
3121 content[c].e[x][y] =
3122 getMappedElement(CONF_CONTENTS_ELEMENT(buffer, c, x, y));
3125 element_found = FALSE;
3131 checked_free(buffer);
3133 micro_chunk_size += 2 + num_bytes;
3135 else // constant size configuration data (1, 2 or 4 bytes)
3137 int value = (byte_mask == CONF_MASK_1_BYTE ? getFile8Bit (file) :
3138 byte_mask == CONF_MASK_2_BYTE ? getFile16BitBE(file) :
3139 byte_mask == CONF_MASK_4_BYTE ? getFile32BitBE(file) : 0);
3141 for (i = 0; conf[i].data_type != -1; i++)
3143 if (conf[i].element == element &&
3144 conf[i].conf_type == conf_type)
3146 int data_type = conf[i].data_type;
3148 if (data_type == TYPE_ELEMENT)
3149 value = getMappedElement(value);
3151 if (data_type == TYPE_BOOLEAN)
3152 *(boolean *)(conf[i].value) = value;
3154 *(int *) (conf[i].value) = value;
3156 element_found = TRUE;
3162 micro_chunk_size += CONF_VALUE_NUM_BYTES(byte_mask);
3167 char *error_conf_chunk_bytes =
3168 (byte_mask == CONF_MASK_1_BYTE ? "CONF_VALUE_8_BIT" :
3169 byte_mask == CONF_MASK_2_BYTE ? "CONF_VALUE_16_BIT" :
3170 byte_mask == CONF_MASK_4_BYTE ? "CONF_VALUE_32_BIT" :"CONF_VALUE_BYTES");
3171 int error_conf_chunk_token = conf_type & CONF_MASK_TOKEN;
3172 int error_element = real_element;
3174 Warn("cannot load micro chunk '%s(%d)' value for element %d ['%s']",
3175 error_conf_chunk_bytes, error_conf_chunk_token,
3176 error_element, EL_NAME(error_element));
3179 return micro_chunk_size;
3182 static int LoadLevel_INFO(File *file, int chunk_size, struct LevelInfo *level)
3184 int real_chunk_size = 0;
3186 li = *level; // copy level data into temporary buffer
3188 while (!checkEndOfFile(file))
3190 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_INFO, -1, -1);
3192 if (real_chunk_size >= chunk_size)
3196 *level = li; // copy temporary buffer back to level data
3198 return real_chunk_size;
3201 static int LoadLevel_CONF(File *file, int chunk_size, struct LevelInfo *level)
3203 int real_chunk_size = 0;
3205 li = *level; // copy level data into temporary buffer
3207 while (!checkEndOfFile(file))
3209 int element = getMappedElement(getFile16BitBE(file));
3211 real_chunk_size += 2;
3212 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_CONF,
3214 if (real_chunk_size >= chunk_size)
3218 *level = li; // copy temporary buffer back to level data
3220 return real_chunk_size;
3223 static int LoadLevel_ELEM(File *file, int chunk_size, struct LevelInfo *level)
3225 int real_chunk_size = 0;
3227 li = *level; // copy level data into temporary buffer
3229 while (!checkEndOfFile(file))
3231 int element = getMappedElement(getFile16BitBE(file));
3233 real_chunk_size += 2;
3234 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_ELEM,
3236 if (real_chunk_size >= chunk_size)
3240 *level = li; // copy temporary buffer back to level data
3242 return real_chunk_size;
3245 static int LoadLevel_NOTE(File *file, int chunk_size, struct LevelInfo *level)
3247 int element = getMappedElement(getFile16BitBE(file));
3248 int envelope_nr = element - EL_ENVELOPE_1;
3249 int real_chunk_size = 2;
3251 xx_envelope = level->envelope[envelope_nr]; // copy into temporary buffer
3253 while (!checkEndOfFile(file))
3255 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_NOTE,
3258 if (real_chunk_size >= chunk_size)
3262 level->envelope[envelope_nr] = xx_envelope; // copy from temporary buffer
3264 return real_chunk_size;
3267 static int LoadLevel_CUSX(File *file, int chunk_size, struct LevelInfo *level)
3269 int element = getMappedElement(getFile16BitBE(file));
3270 int real_chunk_size = 2;
3271 struct ElementInfo *ei = &element_info[element];
3274 xx_ei = *ei; // copy element data into temporary buffer
3276 xx_ei.num_change_pages = -1;
3278 while (!checkEndOfFile(file))
3280 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_CUSX_base,
3282 if (xx_ei.num_change_pages != -1)
3285 if (real_chunk_size >= chunk_size)
3291 if (ei->num_change_pages == -1)
3293 Warn("LoadLevel_CUSX(): missing 'num_change_pages' for '%s'",
3296 ei->num_change_pages = 1;
3298 setElementChangePages(ei, 1);
3299 setElementChangeInfoToDefaults(ei->change);
3301 return real_chunk_size;
3304 // initialize number of change pages stored for this custom element
3305 setElementChangePages(ei, ei->num_change_pages);
3306 for (i = 0; i < ei->num_change_pages; i++)
3307 setElementChangeInfoToDefaults(&ei->change_page[i]);
3309 // start with reading properties for the first change page
3310 xx_current_change_page = 0;
3312 while (!checkEndOfFile(file))
3314 struct ElementChangeInfo *change = &ei->change_page[xx_current_change_page];
3316 xx_change = *change; // copy change data into temporary buffer
3318 resetEventBits(); // reset bits; change page might have changed
3320 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_CUSX_change,
3323 *change = xx_change;
3325 setEventFlagsFromEventBits(change);
3327 if (real_chunk_size >= chunk_size)
3331 level->file_has_custom_elements = TRUE;
3333 return real_chunk_size;
3336 static int LoadLevel_GRPX(File *file, int chunk_size, struct LevelInfo *level)
3338 int element = getMappedElement(getFile16BitBE(file));
3339 int real_chunk_size = 2;
3340 struct ElementInfo *ei = &element_info[element];
3341 struct ElementGroupInfo *group = ei->group;
3343 xx_ei = *ei; // copy element data into temporary buffer
3344 xx_group = *group; // copy group data into temporary buffer
3346 while (!checkEndOfFile(file))
3348 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_GRPX,
3351 if (real_chunk_size >= chunk_size)
3358 level->file_has_custom_elements = TRUE;
3360 return real_chunk_size;
3363 static void LoadLevelFromFileInfo_RND(struct LevelInfo *level,
3364 struct LevelFileInfo *level_file_info,
3365 boolean level_info_only)
3367 char *filename = level_file_info->filename;
3368 char cookie[MAX_LINE_LEN];
3369 char chunk_name[CHUNK_ID_LEN + 1];
3373 if (!(file = openFile(filename, MODE_READ)))
3375 level->no_valid_file = TRUE;
3376 level->no_level_file = TRUE;
3378 if (level_info_only)
3381 Warn("cannot read level '%s' -- using empty level", filename);
3383 if (!setup.editor.use_template_for_new_levels)
3386 // if level file not found, try to initialize level data from template
3387 filename = getGlobalLevelTemplateFilename();
3389 if (!(file = openFile(filename, MODE_READ)))
3392 // default: for empty levels, use level template for custom elements
3393 level->use_custom_template = TRUE;
3395 level->no_valid_file = FALSE;
3398 getFileChunkBE(file, chunk_name, NULL);
3399 if (strEqual(chunk_name, "RND1"))
3401 getFile32BitBE(file); // not used
3403 getFileChunkBE(file, chunk_name, NULL);
3404 if (!strEqual(chunk_name, "CAVE"))
3406 level->no_valid_file = TRUE;
3408 Warn("unknown format of level file '%s'", filename);
3415 else // check for pre-2.0 file format with cookie string
3417 strcpy(cookie, chunk_name);
3418 if (getStringFromFile(file, &cookie[4], MAX_LINE_LEN - 4) == NULL)
3420 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
3421 cookie[strlen(cookie) - 1] = '\0';
3423 if (!checkCookieString(cookie, LEVEL_COOKIE_TMPL))
3425 level->no_valid_file = TRUE;
3427 Warn("unknown format of level file '%s'", filename);
3434 if ((level->file_version = getFileVersionFromCookieString(cookie)) == -1)
3436 level->no_valid_file = TRUE;
3438 Warn("unsupported version of level file '%s'", filename);
3445 // pre-2.0 level files have no game version, so use file version here
3446 level->game_version = level->file_version;
3449 if (level->file_version < FILE_VERSION_1_2)
3451 // level files from versions before 1.2.0 without chunk structure
3452 LoadLevel_HEAD(file, LEVEL_CHUNK_HEAD_SIZE, level);
3453 LoadLevel_BODY(file, level->fieldx * level->fieldy, level);
3461 int (*loader)(File *, int, struct LevelInfo *);
3465 { "VERS", LEVEL_CHUNK_VERS_SIZE, LoadLevel_VERS },
3466 { "DATE", LEVEL_CHUNK_DATE_SIZE, LoadLevel_DATE },
3467 { "HEAD", LEVEL_CHUNK_HEAD_SIZE, LoadLevel_HEAD },
3468 { "NAME", LEVEL_CHUNK_NAME_SIZE, LoadLevel_NAME },
3469 { "AUTH", LEVEL_CHUNK_AUTH_SIZE, LoadLevel_AUTH },
3470 { "INFO", -1, LoadLevel_INFO },
3471 { "BODY", -1, LoadLevel_BODY },
3472 { "CONT", -1, LoadLevel_CONT },
3473 { "CNT2", LEVEL_CHUNK_CNT2_SIZE, LoadLevel_CNT2 },
3474 { "CNT3", -1, LoadLevel_CNT3 },
3475 { "CUS1", -1, LoadLevel_CUS1 },
3476 { "CUS2", -1, LoadLevel_CUS2 },
3477 { "CUS3", -1, LoadLevel_CUS3 },
3478 { "CUS4", -1, LoadLevel_CUS4 },
3479 { "GRP1", -1, LoadLevel_GRP1 },
3480 { "CONF", -1, LoadLevel_CONF },
3481 { "ELEM", -1, LoadLevel_ELEM },
3482 { "NOTE", -1, LoadLevel_NOTE },
3483 { "CUSX", -1, LoadLevel_CUSX },
3484 { "GRPX", -1, LoadLevel_GRPX },
3489 while (getFileChunkBE(file, chunk_name, &chunk_size))
3493 while (chunk_info[i].name != NULL &&
3494 !strEqual(chunk_name, chunk_info[i].name))
3497 if (chunk_info[i].name == NULL)
3499 Warn("unknown chunk '%s' in level file '%s'",
3500 chunk_name, filename);
3502 ReadUnusedBytesFromFile(file, chunk_size);
3504 else if (chunk_info[i].size != -1 &&
3505 chunk_info[i].size != chunk_size)
3507 Warn("wrong size (%d) of chunk '%s' in level file '%s'",
3508 chunk_size, chunk_name, filename);
3510 ReadUnusedBytesFromFile(file, chunk_size);
3514 // call function to load this level chunk
3515 int chunk_size_expected =
3516 (chunk_info[i].loader)(file, chunk_size, level);
3518 // the size of some chunks cannot be checked before reading other
3519 // chunks first (like "HEAD" and "BODY") that contain some header
3520 // information, so check them here
3521 if (chunk_size_expected != chunk_size)
3523 Warn("wrong size (%d) of chunk '%s' in level file '%s'",
3524 chunk_size, chunk_name, filename);
3534 // ----------------------------------------------------------------------------
3535 // functions for loading EM level
3536 // ----------------------------------------------------------------------------
3538 static void CopyNativeLevel_RND_to_EM(struct LevelInfo *level)
3540 static int ball_xy[8][2] =
3551 struct LevelInfo_EM *level_em = level->native_em_level;
3552 struct CAVE *cav = level_em->cav;
3555 cav->width = MIN(level->fieldx, MAX_PLAYFIELD_WIDTH);
3556 cav->height = MIN(level->fieldy, MAX_PLAYFIELD_HEIGHT);
3558 cav->time_seconds = level->time;
3559 cav->gems_needed = level->gems_needed;
3561 cav->emerald_score = level->score[SC_EMERALD];
3562 cav->diamond_score = level->score[SC_DIAMOND];
3563 cav->alien_score = level->score[SC_ROBOT];
3564 cav->tank_score = level->score[SC_SPACESHIP];
3565 cav->bug_score = level->score[SC_BUG];
3566 cav->eater_score = level->score[SC_YAMYAM];
3567 cav->nut_score = level->score[SC_NUT];
3568 cav->dynamite_score = level->score[SC_DYNAMITE];
3569 cav->key_score = level->score[SC_KEY];
3570 cav->exit_score = level->score[SC_TIME_BONUS];
3572 cav->num_eater_arrays = level->num_yamyam_contents;
3574 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
3575 for (y = 0; y < 3; y++)
3576 for (x = 0; x < 3; x++)
3577 cav->eater_array[i][y * 3 + x] =
3578 map_element_RND_to_EM_cave(level->yamyam_content[i].e[x][y]);
3580 cav->amoeba_time = level->amoeba_speed;
3581 cav->wonderwall_time = level->time_magic_wall;
3582 cav->wheel_time = level->time_wheel;
3584 cav->android_move_time = level->android_move_time;
3585 cav->android_clone_time = level->android_clone_time;
3586 cav->ball_random = level->ball_random;
3587 cav->ball_active = level->ball_active_initial;
3588 cav->ball_time = level->ball_time;
3589 cav->num_ball_arrays = level->num_ball_contents;
3591 cav->lenses_score = level->lenses_score;
3592 cav->magnify_score = level->magnify_score;
3593 cav->slurp_score = level->slurp_score;
3595 cav->lenses_time = level->lenses_time;
3596 cav->magnify_time = level->magnify_time;
3598 cav->wind_direction =
3599 map_direction_RND_to_EM(level->wind_direction_initial);
3601 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
3602 for (j = 0; j < 8; j++)
3603 cav->ball_array[i][j] =
3604 map_element_RND_to_EM_cave(level->ball_content[i].
3605 e[ball_xy[j][0]][ball_xy[j][1]]);
3607 map_android_clone_elements_RND_to_EM(level);
3609 // first fill the complete playfield with the empty space element
3610 for (y = 0; y < EM_MAX_CAVE_HEIGHT; y++)
3611 for (x = 0; x < EM_MAX_CAVE_WIDTH; x++)
3612 cav->cave[x][y] = Cblank;
3614 // then copy the real level contents from level file into the playfield
3615 for (y = 0; y < cav->height; y++) for (x = 0; x < cav->width; x++)
3617 int new_element = map_element_RND_to_EM_cave(level->field[x][y]);
3619 if (level->field[x][y] == EL_AMOEBA_DEAD)
3620 new_element = map_element_RND_to_EM_cave(EL_AMOEBA_WET);
3622 cav->cave[x][y] = new_element;
3625 for (i = 0; i < MAX_PLAYERS; i++)
3627 cav->player_x[i] = -1;
3628 cav->player_y[i] = -1;
3631 // initialize player positions and delete players from the playfield
3632 for (y = 0; y < cav->height; y++) for (x = 0; x < cav->width; x++)
3634 if (ELEM_IS_PLAYER(level->field[x][y]))
3636 int player_nr = GET_PLAYER_NR(level->field[x][y]);
3638 cav->player_x[player_nr] = x;
3639 cav->player_y[player_nr] = y;
3641 cav->cave[x][y] = map_element_RND_to_EM_cave(EL_EMPTY);
3646 static void CopyNativeLevel_EM_to_RND(struct LevelInfo *level)
3648 static int ball_xy[8][2] =
3659 struct LevelInfo_EM *level_em = level->native_em_level;
3660 struct CAVE *cav = level_em->cav;
3663 level->fieldx = MIN(cav->width, MAX_LEV_FIELDX);
3664 level->fieldy = MIN(cav->height, MAX_LEV_FIELDY);
3666 level->time = cav->time_seconds;
3667 level->gems_needed = cav->gems_needed;
3669 sprintf(level->name, "Level %d", level->file_info.nr);
3671 level->score[SC_EMERALD] = cav->emerald_score;
3672 level->score[SC_DIAMOND] = cav->diamond_score;
3673 level->score[SC_ROBOT] = cav->alien_score;
3674 level->score[SC_SPACESHIP] = cav->tank_score;
3675 level->score[SC_BUG] = cav->bug_score;
3676 level->score[SC_YAMYAM] = cav->eater_score;
3677 level->score[SC_NUT] = cav->nut_score;
3678 level->score[SC_DYNAMITE] = cav->dynamite_score;
3679 level->score[SC_KEY] = cav->key_score;
3680 level->score[SC_TIME_BONUS] = cav->exit_score;
3682 level->num_yamyam_contents = cav->num_eater_arrays;
3684 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
3685 for (y = 0; y < 3; y++)
3686 for (x = 0; x < 3; x++)
3687 level->yamyam_content[i].e[x][y] =
3688 map_element_EM_to_RND_cave(cav->eater_array[i][y * 3 + x]);
3690 level->amoeba_speed = cav->amoeba_time;
3691 level->time_magic_wall = cav->wonderwall_time;
3692 level->time_wheel = cav->wheel_time;
3694 level->android_move_time = cav->android_move_time;
3695 level->android_clone_time = cav->android_clone_time;
3696 level->ball_random = cav->ball_random;
3697 level->ball_active_initial = cav->ball_active;
3698 level->ball_time = cav->ball_time;
3699 level->num_ball_contents = cav->num_ball_arrays;
3701 level->lenses_score = cav->lenses_score;
3702 level->magnify_score = cav->magnify_score;
3703 level->slurp_score = cav->slurp_score;
3705 level->lenses_time = cav->lenses_time;
3706 level->magnify_time = cav->magnify_time;
3708 level->wind_direction_initial =
3709 map_direction_EM_to_RND(cav->wind_direction);
3711 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
3712 for (j = 0; j < 8; j++)
3713 level->ball_content[i].e[ball_xy[j][0]][ball_xy[j][1]] =
3714 map_element_EM_to_RND_cave(cav->ball_array[i][j]);
3716 map_android_clone_elements_EM_to_RND(level);
3718 // convert the playfield (some elements need special treatment)
3719 for (y = 0; y < level->fieldy; y++) for (x = 0; x < level->fieldx; x++)
3721 int new_element = map_element_EM_to_RND_cave(cav->cave[x][y]);
3723 if (new_element == EL_AMOEBA_WET && level->amoeba_speed == 0)
3724 new_element = EL_AMOEBA_DEAD;
3726 level->field[x][y] = new_element;
3729 for (i = 0; i < MAX_PLAYERS; i++)
3731 // in case of all players set to the same field, use the first player
3732 int nr = MAX_PLAYERS - i - 1;
3733 int jx = cav->player_x[nr];
3734 int jy = cav->player_y[nr];
3736 if (jx != -1 && jy != -1)
3737 level->field[jx][jy] = EL_PLAYER_1 + nr;
3740 // time score is counted for each 10 seconds left in Emerald Mine levels
3741 level->time_score_base = 10;
3745 // ----------------------------------------------------------------------------
3746 // functions for loading SP level
3747 // ----------------------------------------------------------------------------
3749 static void CopyNativeLevel_RND_to_SP(struct LevelInfo *level)
3751 struct LevelInfo_SP *level_sp = level->native_sp_level;
3752 LevelInfoType *header = &level_sp->header;
3755 level_sp->width = level->fieldx;
3756 level_sp->height = level->fieldy;
3758 for (x = 0; x < level->fieldx; x++)
3759 for (y = 0; y < level->fieldy; y++)
3760 level_sp->playfield[x][y] = map_element_RND_to_SP(level->field[x][y]);
3762 header->InitialGravity = (level->initial_player_gravity[0] ? 1 : 0);
3764 for (i = 0; i < SP_LEVEL_NAME_LEN; i++)
3765 header->LevelTitle[i] = level->name[i];
3766 // !!! NO STRING TERMINATION IN SUPAPLEX VB CODE YET -- FIX THIS !!!
3768 header->InfotronsNeeded = level->gems_needed;
3770 header->SpecialPortCount = 0;
3772 for (x = 0; x < level->fieldx; x++) for (y = 0; y < level->fieldy; y++)
3774 boolean gravity_port_found = FALSE;
3775 boolean gravity_port_valid = FALSE;
3776 int gravity_port_flag;
3777 int gravity_port_base_element;
3778 int element = level->field[x][y];
3780 if (element >= EL_SP_GRAVITY_ON_PORT_RIGHT &&
3781 element <= EL_SP_GRAVITY_ON_PORT_UP)
3783 gravity_port_found = TRUE;
3784 gravity_port_valid = TRUE;
3785 gravity_port_flag = 1;
3786 gravity_port_base_element = EL_SP_GRAVITY_ON_PORT_RIGHT;
3788 else if (element >= EL_SP_GRAVITY_OFF_PORT_RIGHT &&
3789 element <= EL_SP_GRAVITY_OFF_PORT_UP)
3791 gravity_port_found = TRUE;
3792 gravity_port_valid = TRUE;
3793 gravity_port_flag = 0;
3794 gravity_port_base_element = EL_SP_GRAVITY_OFF_PORT_RIGHT;
3796 else if (element >= EL_SP_GRAVITY_PORT_RIGHT &&
3797 element <= EL_SP_GRAVITY_PORT_UP)
3799 // change R'n'D style gravity inverting special port to normal port
3800 // (there are no gravity inverting ports in native Supaplex engine)
3802 gravity_port_found = TRUE;
3803 gravity_port_valid = FALSE;
3804 gravity_port_base_element = EL_SP_GRAVITY_PORT_RIGHT;
3807 if (gravity_port_found)
3809 if (gravity_port_valid &&
3810 header->SpecialPortCount < SP_MAX_SPECIAL_PORTS)
3812 SpecialPortType *port = &header->SpecialPort[header->SpecialPortCount];
3814 port->PortLocation = (y * level->fieldx + x) * 2;
3815 port->Gravity = gravity_port_flag;
3817 element += EL_SP_GRAVITY_PORT_RIGHT - gravity_port_base_element;
3819 header->SpecialPortCount++;
3823 // change special gravity port to normal port
3825 element += EL_SP_PORT_RIGHT - gravity_port_base_element;
3828 level_sp->playfield[x][y] = element - EL_SP_START;
3833 static void CopyNativeLevel_SP_to_RND(struct LevelInfo *level)
3835 struct LevelInfo_SP *level_sp = level->native_sp_level;
3836 LevelInfoType *header = &level_sp->header;
3837 boolean num_invalid_elements = 0;
3840 level->fieldx = level_sp->width;
3841 level->fieldy = level_sp->height;
3843 for (x = 0; x < level->fieldx; x++)
3845 for (y = 0; y < level->fieldy; y++)
3847 int element_old = level_sp->playfield[x][y];
3848 int element_new = getMappedElement(map_element_SP_to_RND(element_old));
3850 if (element_new == EL_UNKNOWN)
3852 num_invalid_elements++;
3854 Debug("level:native:SP", "invalid element %d at position %d, %d",
3858 level->field[x][y] = element_new;
3862 if (num_invalid_elements > 0)
3863 Warn("found %d invalid elements%s", num_invalid_elements,
3864 (!options.debug ? " (use '--debug' for more details)" : ""));
3866 for (i = 0; i < MAX_PLAYERS; i++)
3867 level->initial_player_gravity[i] =
3868 (header->InitialGravity == 1 ? TRUE : FALSE);
3870 // skip leading spaces
3871 for (i = 0; i < SP_LEVEL_NAME_LEN; i++)
3872 if (header->LevelTitle[i] != ' ')
3876 for (j = 0; i < SP_LEVEL_NAME_LEN; i++, j++)
3877 level->name[j] = header->LevelTitle[i];
3878 level->name[j] = '\0';
3880 // cut trailing spaces
3882 if (level->name[j - 1] == ' ' && level->name[j] == '\0')
3883 level->name[j - 1] = '\0';
3885 level->gems_needed = header->InfotronsNeeded;
3887 for (i = 0; i < header->SpecialPortCount; i++)
3889 SpecialPortType *port = &header->SpecialPort[i];
3890 int port_location = port->PortLocation;
3891 int gravity = port->Gravity;
3892 int port_x, port_y, port_element;
3894 port_x = (port_location / 2) % level->fieldx;
3895 port_y = (port_location / 2) / level->fieldx;
3897 if (port_x < 0 || port_x >= level->fieldx ||
3898 port_y < 0 || port_y >= level->fieldy)
3900 Warn("special port position (%d, %d) out of bounds", port_x, port_y);
3905 port_element = level->field[port_x][port_y];
3907 if (port_element < EL_SP_GRAVITY_PORT_RIGHT ||
3908 port_element > EL_SP_GRAVITY_PORT_UP)
3910 Warn("no special port at position (%d, %d)", port_x, port_y);
3915 // change previous (wrong) gravity inverting special port to either
3916 // gravity enabling special port or gravity disabling special port
3917 level->field[port_x][port_y] +=
3918 (gravity == 1 ? EL_SP_GRAVITY_ON_PORT_RIGHT :
3919 EL_SP_GRAVITY_OFF_PORT_RIGHT) - EL_SP_GRAVITY_PORT_RIGHT;
3922 // change special gravity ports without database entries to normal ports
3923 for (x = 0; x < level->fieldx; x++)
3924 for (y = 0; y < level->fieldy; y++)
3925 if (level->field[x][y] >= EL_SP_GRAVITY_PORT_RIGHT &&
3926 level->field[x][y] <= EL_SP_GRAVITY_PORT_UP)
3927 level->field[x][y] += EL_SP_PORT_RIGHT - EL_SP_GRAVITY_PORT_RIGHT;
3929 level->time = 0; // no time limit
3930 level->amoeba_speed = 0;
3931 level->time_magic_wall = 0;
3932 level->time_wheel = 0;
3933 level->amoeba_content = EL_EMPTY;
3936 // original Supaplex does not use score values -- use default values
3938 for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
3939 level->score[i] = 0;
3942 // there are no yamyams in supaplex levels
3943 for (i = 0; i < level->num_yamyam_contents; i++)
3944 for (x = 0; x < 3; x++)
3945 for (y = 0; y < 3; y++)
3946 level->yamyam_content[i].e[x][y] = EL_EMPTY;
3949 static void CopyNativeTape_RND_to_SP(struct LevelInfo *level)
3951 struct LevelInfo_SP *level_sp = level->native_sp_level;
3952 struct DemoInfo_SP *demo = &level_sp->demo;
3955 // always start with reliable default values
3956 demo->is_available = FALSE;
3959 if (TAPE_IS_EMPTY(tape))
3962 demo->level_nr = tape.level_nr; // (currently not used)
3964 level_sp->header.DemoRandomSeed = tape.random_seed;
3968 for (i = 0; i < tape.length; i++)
3970 int demo_action = map_key_RND_to_SP(tape.pos[i].action[0]);
3971 int demo_repeat = tape.pos[i].delay;
3972 int demo_entries = (demo_repeat + 15) / 16;
3974 if (demo->length + demo_entries >= SP_MAX_TAPE_LEN)
3976 Warn("tape truncated: size exceeds maximum SP demo size %d",
3982 for (j = 0; j < demo_repeat / 16; j++)
3983 demo->data[demo->length++] = 0xf0 | demo_action;
3985 if (demo_repeat % 16)
3986 demo->data[demo->length++] = ((demo_repeat % 16 - 1) << 4) | demo_action;
3989 demo->is_available = TRUE;
3992 static void setTapeInfoToDefaults(void);
3994 static void CopyNativeTape_SP_to_RND(struct LevelInfo *level)
3996 struct LevelInfo_SP *level_sp = level->native_sp_level;
3997 struct DemoInfo_SP *demo = &level_sp->demo;
3998 char *filename = level->file_info.filename;
4001 // always start with reliable default values
4002 setTapeInfoToDefaults();
4004 if (!demo->is_available)
4007 tape.level_nr = demo->level_nr; // (currently not used)
4008 tape.random_seed = level_sp->header.DemoRandomSeed;
4010 TapeSetDateFromEpochSeconds(getFileTimestampEpochSeconds(filename));
4013 tape.pos[tape.counter].delay = 0;
4015 for (i = 0; i < demo->length; i++)
4017 int demo_action = demo->data[i] & 0x0f;
4018 int demo_repeat = (demo->data[i] & 0xf0) >> 4;
4019 int tape_action = map_key_SP_to_RND(demo_action);
4020 int tape_repeat = demo_repeat + 1;
4021 byte action[MAX_TAPE_ACTIONS] = { tape_action };
4022 boolean success = 0;
4025 for (j = 0; j < tape_repeat; j++)
4026 success = TapeAddAction(action);
4030 Warn("SP demo truncated: size exceeds maximum tape size %d",
4037 TapeHaltRecording();
4041 // ----------------------------------------------------------------------------
4042 // functions for loading MM level
4043 // ----------------------------------------------------------------------------
4045 static void CopyNativeLevel_RND_to_MM(struct LevelInfo *level)
4047 struct LevelInfo_MM *level_mm = level->native_mm_level;
4050 level_mm->fieldx = MIN(level->fieldx, MM_MAX_PLAYFIELD_WIDTH);
4051 level_mm->fieldy = MIN(level->fieldy, MM_MAX_PLAYFIELD_HEIGHT);
4053 level_mm->time = level->time;
4054 level_mm->kettles_needed = level->gems_needed;
4055 level_mm->auto_count_kettles = level->auto_count_gems;
4057 level_mm->laser_red = level->mm_laser_red;
4058 level_mm->laser_green = level->mm_laser_green;
4059 level_mm->laser_blue = level->mm_laser_blue;
4061 strcpy(level_mm->name, level->name);
4062 strcpy(level_mm->author, level->author);
4064 level_mm->score[SC_EMERALD] = level->score[SC_EMERALD];
4065 level_mm->score[SC_PACMAN] = level->score[SC_PACMAN];
4066 level_mm->score[SC_KEY] = level->score[SC_KEY];
4067 level_mm->score[SC_TIME_BONUS] = level->score[SC_TIME_BONUS];
4068 level_mm->score[SC_ELEM_BONUS] = level->score[SC_ELEM_BONUS];
4070 level_mm->amoeba_speed = level->amoeba_speed;
4071 level_mm->time_fuse = level->mm_time_fuse;
4072 level_mm->time_bomb = level->mm_time_bomb;
4073 level_mm->time_ball = level->mm_time_ball;
4074 level_mm->time_block = level->mm_time_block;
4076 for (x = 0; x < level->fieldx; x++)
4077 for (y = 0; y < level->fieldy; y++)
4079 level_mm->field[x][y] = map_element_RND_to_MM(level->field[x][y]);
4082 static void CopyNativeLevel_MM_to_RND(struct LevelInfo *level)
4084 struct LevelInfo_MM *level_mm = level->native_mm_level;
4087 level->fieldx = MIN(level_mm->fieldx, MAX_LEV_FIELDX);
4088 level->fieldy = MIN(level_mm->fieldy, MAX_LEV_FIELDY);
4090 level->time = level_mm->time;
4091 level->gems_needed = level_mm->kettles_needed;
4092 level->auto_count_gems = level_mm->auto_count_kettles;
4094 level->mm_laser_red = level_mm->laser_red;
4095 level->mm_laser_green = level_mm->laser_green;
4096 level->mm_laser_blue = level_mm->laser_blue;
4098 strcpy(level->name, level_mm->name);
4100 // only overwrite author from 'levelinfo.conf' if author defined in level
4101 if (!strEqual(level_mm->author, ANONYMOUS_NAME))
4102 strcpy(level->author, level_mm->author);
4104 level->score[SC_EMERALD] = level_mm->score[SC_EMERALD];
4105 level->score[SC_PACMAN] = level_mm->score[SC_PACMAN];
4106 level->score[SC_KEY] = level_mm->score[SC_KEY];
4107 level->score[SC_TIME_BONUS] = level_mm->score[SC_TIME_BONUS];
4108 level->score[SC_ELEM_BONUS] = level_mm->score[SC_ELEM_BONUS];
4110 level->amoeba_speed = level_mm->amoeba_speed;
4111 level->mm_time_fuse = level_mm->time_fuse;
4112 level->mm_time_bomb = level_mm->time_bomb;
4113 level->mm_time_ball = level_mm->time_ball;
4114 level->mm_time_block = level_mm->time_block;
4116 for (x = 0; x < level->fieldx; x++)
4117 for (y = 0; y < level->fieldy; y++)
4118 level->field[x][y] = map_element_MM_to_RND(level_mm->field[x][y]);
4122 // ----------------------------------------------------------------------------
4123 // functions for loading DC level
4124 // ----------------------------------------------------------------------------
4126 #define DC_LEVEL_HEADER_SIZE 344
4128 static unsigned short getDecodedWord_DC(unsigned short data_encoded,
4131 static int last_data_encoded;
4135 int diff_hi, diff_lo;
4136 int data_hi, data_lo;
4137 unsigned short data_decoded;
4141 last_data_encoded = 0;
4148 diff = data_encoded - last_data_encoded;
4149 diff_hi = diff & ~0xff;
4150 diff_lo = diff & 0xff;
4154 data_hi = diff_hi - (offset1 << 8) + (offset2 & 0xff00);
4155 data_lo = (diff_lo + (data_hi >> 16)) & 0x00ff;
4156 data_hi = data_hi & 0xff00;
4158 data_decoded = data_hi | data_lo;
4160 last_data_encoded = data_encoded;
4162 offset1 = (offset1 + 1) % 31;
4163 offset2 = offset2 & 0xff;
4165 return data_decoded;
4168 static int getMappedElement_DC(int element)
4176 // 0x0117 - 0x036e: (?)
4179 // 0x042d - 0x0684: (?)
4195 element = EL_CRYSTAL;
4198 case 0x0e77: // quicksand (boulder)
4199 element = EL_QUICKSAND_FAST_FULL;
4202 case 0x0e99: // slow quicksand (boulder)
4203 element = EL_QUICKSAND_FULL;
4207 element = EL_EM_EXIT_OPEN;
4211 element = EL_EM_EXIT_CLOSED;
4215 element = EL_EM_STEEL_EXIT_OPEN;
4219 element = EL_EM_STEEL_EXIT_CLOSED;
4222 case 0x0f4f: // dynamite (lit 1)
4223 element = EL_EM_DYNAMITE_ACTIVE;
4226 case 0x0f57: // dynamite (lit 2)
4227 element = EL_EM_DYNAMITE_ACTIVE;
4230 case 0x0f5f: // dynamite (lit 3)
4231 element = EL_EM_DYNAMITE_ACTIVE;
4234 case 0x0f67: // dynamite (lit 4)
4235 element = EL_EM_DYNAMITE_ACTIVE;
4242 element = EL_AMOEBA_WET;
4246 element = EL_AMOEBA_DROP;
4250 element = EL_DC_MAGIC_WALL;
4254 element = EL_SPACESHIP_UP;
4258 element = EL_SPACESHIP_DOWN;
4262 element = EL_SPACESHIP_LEFT;
4266 element = EL_SPACESHIP_RIGHT;
4270 element = EL_BUG_UP;
4274 element = EL_BUG_DOWN;
4278 element = EL_BUG_LEFT;
4282 element = EL_BUG_RIGHT;
4286 element = EL_MOLE_UP;
4290 element = EL_MOLE_DOWN;
4294 element = EL_MOLE_LEFT;
4298 element = EL_MOLE_RIGHT;
4306 element = EL_YAMYAM_UP;
4310 element = EL_SWITCHGATE_OPEN;
4314 element = EL_SWITCHGATE_CLOSED;
4318 element = EL_DC_SWITCHGATE_SWITCH_UP;
4322 element = EL_TIMEGATE_CLOSED;
4325 case 0x144c: // conveyor belt switch (green)
4326 element = EL_CONVEYOR_BELT_3_SWITCH_MIDDLE;
4329 case 0x144f: // conveyor belt switch (red)
4330 element = EL_CONVEYOR_BELT_1_SWITCH_MIDDLE;
4333 case 0x1452: // conveyor belt switch (blue)
4334 element = EL_CONVEYOR_BELT_4_SWITCH_MIDDLE;
4338 element = EL_CONVEYOR_BELT_3_MIDDLE;
4342 element = EL_CONVEYOR_BELT_3_LEFT;
4346 element = EL_CONVEYOR_BELT_3_RIGHT;
4350 element = EL_CONVEYOR_BELT_1_MIDDLE;
4354 element = EL_CONVEYOR_BELT_1_LEFT;
4358 element = EL_CONVEYOR_BELT_1_RIGHT;
4362 element = EL_CONVEYOR_BELT_4_MIDDLE;
4366 element = EL_CONVEYOR_BELT_4_LEFT;
4370 element = EL_CONVEYOR_BELT_4_RIGHT;
4374 element = EL_EXPANDABLE_WALL_HORIZONTAL;
4378 element = EL_EXPANDABLE_WALL_VERTICAL;
4382 element = EL_EXPANDABLE_WALL_ANY;
4385 case 0x14ce: // growing steel wall (left/right)
4386 element = EL_EXPANDABLE_STEELWALL_HORIZONTAL;
4389 case 0x14df: // growing steel wall (up/down)
4390 element = EL_EXPANDABLE_STEELWALL_VERTICAL;
4393 case 0x14e8: // growing steel wall (up/down/left/right)
4394 element = EL_EXPANDABLE_STEELWALL_ANY;
4398 element = EL_SHIELD_DEADLY;
4402 element = EL_EXTRA_TIME;
4410 element = EL_EMPTY_SPACE;
4413 case 0x1578: // quicksand (empty)
4414 element = EL_QUICKSAND_FAST_EMPTY;
4417 case 0x1579: // slow quicksand (empty)
4418 element = EL_QUICKSAND_EMPTY;
4428 element = EL_EM_DYNAMITE;
4431 case 0x15a1: // key (red)
4432 element = EL_EM_KEY_1;
4435 case 0x15a2: // key (yellow)
4436 element = EL_EM_KEY_2;
4439 case 0x15a3: // key (blue)
4440 element = EL_EM_KEY_4;
4443 case 0x15a4: // key (green)
4444 element = EL_EM_KEY_3;
4447 case 0x15a5: // key (white)
4448 element = EL_DC_KEY_WHITE;
4452 element = EL_WALL_SLIPPERY;
4459 case 0x15a8: // wall (not round)
4463 case 0x15a9: // (blue)
4464 element = EL_CHAR_A;
4467 case 0x15aa: // (blue)
4468 element = EL_CHAR_B;
4471 case 0x15ab: // (blue)
4472 element = EL_CHAR_C;
4475 case 0x15ac: // (blue)
4476 element = EL_CHAR_D;
4479 case 0x15ad: // (blue)
4480 element = EL_CHAR_E;
4483 case 0x15ae: // (blue)
4484 element = EL_CHAR_F;
4487 case 0x15af: // (blue)
4488 element = EL_CHAR_G;
4491 case 0x15b0: // (blue)
4492 element = EL_CHAR_H;
4495 case 0x15b1: // (blue)
4496 element = EL_CHAR_I;
4499 case 0x15b2: // (blue)
4500 element = EL_CHAR_J;
4503 case 0x15b3: // (blue)
4504 element = EL_CHAR_K;
4507 case 0x15b4: // (blue)
4508 element = EL_CHAR_L;
4511 case 0x15b5: // (blue)
4512 element = EL_CHAR_M;
4515 case 0x15b6: // (blue)
4516 element = EL_CHAR_N;
4519 case 0x15b7: // (blue)
4520 element = EL_CHAR_O;
4523 case 0x15b8: // (blue)
4524 element = EL_CHAR_P;
4527 case 0x15b9: // (blue)
4528 element = EL_CHAR_Q;
4531 case 0x15ba: // (blue)
4532 element = EL_CHAR_R;
4535 case 0x15bb: // (blue)
4536 element = EL_CHAR_S;
4539 case 0x15bc: // (blue)
4540 element = EL_CHAR_T;
4543 case 0x15bd: // (blue)
4544 element = EL_CHAR_U;
4547 case 0x15be: // (blue)
4548 element = EL_CHAR_V;
4551 case 0x15bf: // (blue)
4552 element = EL_CHAR_W;
4555 case 0x15c0: // (blue)
4556 element = EL_CHAR_X;
4559 case 0x15c1: // (blue)
4560 element = EL_CHAR_Y;
4563 case 0x15c2: // (blue)
4564 element = EL_CHAR_Z;
4567 case 0x15c3: // (blue)
4568 element = EL_CHAR_AUMLAUT;
4571 case 0x15c4: // (blue)
4572 element = EL_CHAR_OUMLAUT;
4575 case 0x15c5: // (blue)
4576 element = EL_CHAR_UUMLAUT;
4579 case 0x15c6: // (blue)
4580 element = EL_CHAR_0;
4583 case 0x15c7: // (blue)
4584 element = EL_CHAR_1;
4587 case 0x15c8: // (blue)
4588 element = EL_CHAR_2;
4591 case 0x15c9: // (blue)
4592 element = EL_CHAR_3;
4595 case 0x15ca: // (blue)
4596 element = EL_CHAR_4;
4599 case 0x15cb: // (blue)
4600 element = EL_CHAR_5;
4603 case 0x15cc: // (blue)
4604 element = EL_CHAR_6;
4607 case 0x15cd: // (blue)
4608 element = EL_CHAR_7;
4611 case 0x15ce: // (blue)
4612 element = EL_CHAR_8;
4615 case 0x15cf: // (blue)
4616 element = EL_CHAR_9;
4619 case 0x15d0: // (blue)
4620 element = EL_CHAR_PERIOD;
4623 case 0x15d1: // (blue)
4624 element = EL_CHAR_EXCLAM;
4627 case 0x15d2: // (blue)
4628 element = EL_CHAR_COLON;
4631 case 0x15d3: // (blue)
4632 element = EL_CHAR_LESS;
4635 case 0x15d4: // (blue)
4636 element = EL_CHAR_GREATER;
4639 case 0x15d5: // (blue)
4640 element = EL_CHAR_QUESTION;
4643 case 0x15d6: // (blue)
4644 element = EL_CHAR_COPYRIGHT;
4647 case 0x15d7: // (blue)
4648 element = EL_CHAR_UP;
4651 case 0x15d8: // (blue)
4652 element = EL_CHAR_DOWN;
4655 case 0x15d9: // (blue)
4656 element = EL_CHAR_BUTTON;
4659 case 0x15da: // (blue)
4660 element = EL_CHAR_PLUS;
4663 case 0x15db: // (blue)
4664 element = EL_CHAR_MINUS;
4667 case 0x15dc: // (blue)
4668 element = EL_CHAR_APOSTROPHE;
4671 case 0x15dd: // (blue)
4672 element = EL_CHAR_PARENLEFT;
4675 case 0x15de: // (blue)
4676 element = EL_CHAR_PARENRIGHT;
4679 case 0x15df: // (green)
4680 element = EL_CHAR_A;
4683 case 0x15e0: // (green)
4684 element = EL_CHAR_B;
4687 case 0x15e1: // (green)
4688 element = EL_CHAR_C;
4691 case 0x15e2: // (green)
4692 element = EL_CHAR_D;
4695 case 0x15e3: // (green)
4696 element = EL_CHAR_E;
4699 case 0x15e4: // (green)
4700 element = EL_CHAR_F;
4703 case 0x15e5: // (green)
4704 element = EL_CHAR_G;
4707 case 0x15e6: // (green)
4708 element = EL_CHAR_H;
4711 case 0x15e7: // (green)
4712 element = EL_CHAR_I;
4715 case 0x15e8: // (green)
4716 element = EL_CHAR_J;
4719 case 0x15e9: // (green)
4720 element = EL_CHAR_K;
4723 case 0x15ea: // (green)
4724 element = EL_CHAR_L;
4727 case 0x15eb: // (green)
4728 element = EL_CHAR_M;
4731 case 0x15ec: // (green)
4732 element = EL_CHAR_N;
4735 case 0x15ed: // (green)
4736 element = EL_CHAR_O;
4739 case 0x15ee: // (green)
4740 element = EL_CHAR_P;
4743 case 0x15ef: // (green)
4744 element = EL_CHAR_Q;
4747 case 0x15f0: // (green)
4748 element = EL_CHAR_R;
4751 case 0x15f1: // (green)
4752 element = EL_CHAR_S;
4755 case 0x15f2: // (green)
4756 element = EL_CHAR_T;
4759 case 0x15f3: // (green)
4760 element = EL_CHAR_U;
4763 case 0x15f4: // (green)
4764 element = EL_CHAR_V;
4767 case 0x15f5: // (green)
4768 element = EL_CHAR_W;
4771 case 0x15f6: // (green)
4772 element = EL_CHAR_X;
4775 case 0x15f7: // (green)
4776 element = EL_CHAR_Y;
4779 case 0x15f8: // (green)
4780 element = EL_CHAR_Z;
4783 case 0x15f9: // (green)
4784 element = EL_CHAR_AUMLAUT;
4787 case 0x15fa: // (green)
4788 element = EL_CHAR_OUMLAUT;
4791 case 0x15fb: // (green)
4792 element = EL_CHAR_UUMLAUT;
4795 case 0x15fc: // (green)
4796 element = EL_CHAR_0;
4799 case 0x15fd: // (green)
4800 element = EL_CHAR_1;
4803 case 0x15fe: // (green)
4804 element = EL_CHAR_2;
4807 case 0x15ff: // (green)
4808 element = EL_CHAR_3;
4811 case 0x1600: // (green)
4812 element = EL_CHAR_4;
4815 case 0x1601: // (green)
4816 element = EL_CHAR_5;
4819 case 0x1602: // (green)
4820 element = EL_CHAR_6;
4823 case 0x1603: // (green)
4824 element = EL_CHAR_7;
4827 case 0x1604: // (green)
4828 element = EL_CHAR_8;
4831 case 0x1605: // (green)
4832 element = EL_CHAR_9;
4835 case 0x1606: // (green)
4836 element = EL_CHAR_PERIOD;
4839 case 0x1607: // (green)
4840 element = EL_CHAR_EXCLAM;
4843 case 0x1608: // (green)
4844 element = EL_CHAR_COLON;
4847 case 0x1609: // (green)
4848 element = EL_CHAR_LESS;
4851 case 0x160a: // (green)
4852 element = EL_CHAR_GREATER;
4855 case 0x160b: // (green)
4856 element = EL_CHAR_QUESTION;
4859 case 0x160c: // (green)
4860 element = EL_CHAR_COPYRIGHT;
4863 case 0x160d: // (green)
4864 element = EL_CHAR_UP;
4867 case 0x160e: // (green)
4868 element = EL_CHAR_DOWN;
4871 case 0x160f: // (green)
4872 element = EL_CHAR_BUTTON;
4875 case 0x1610: // (green)
4876 element = EL_CHAR_PLUS;
4879 case 0x1611: // (green)
4880 element = EL_CHAR_MINUS;
4883 case 0x1612: // (green)
4884 element = EL_CHAR_APOSTROPHE;
4887 case 0x1613: // (green)
4888 element = EL_CHAR_PARENLEFT;
4891 case 0x1614: // (green)
4892 element = EL_CHAR_PARENRIGHT;
4895 case 0x1615: // (blue steel)
4896 element = EL_STEEL_CHAR_A;
4899 case 0x1616: // (blue steel)
4900 element = EL_STEEL_CHAR_B;
4903 case 0x1617: // (blue steel)
4904 element = EL_STEEL_CHAR_C;
4907 case 0x1618: // (blue steel)
4908 element = EL_STEEL_CHAR_D;
4911 case 0x1619: // (blue steel)
4912 element = EL_STEEL_CHAR_E;
4915 case 0x161a: // (blue steel)
4916 element = EL_STEEL_CHAR_F;
4919 case 0x161b: // (blue steel)
4920 element = EL_STEEL_CHAR_G;
4923 case 0x161c: // (blue steel)
4924 element = EL_STEEL_CHAR_H;
4927 case 0x161d: // (blue steel)
4928 element = EL_STEEL_CHAR_I;
4931 case 0x161e: // (blue steel)
4932 element = EL_STEEL_CHAR_J;
4935 case 0x161f: // (blue steel)
4936 element = EL_STEEL_CHAR_K;
4939 case 0x1620: // (blue steel)
4940 element = EL_STEEL_CHAR_L;
4943 case 0x1621: // (blue steel)
4944 element = EL_STEEL_CHAR_M;
4947 case 0x1622: // (blue steel)
4948 element = EL_STEEL_CHAR_N;
4951 case 0x1623: // (blue steel)
4952 element = EL_STEEL_CHAR_O;
4955 case 0x1624: // (blue steel)
4956 element = EL_STEEL_CHAR_P;
4959 case 0x1625: // (blue steel)
4960 element = EL_STEEL_CHAR_Q;
4963 case 0x1626: // (blue steel)
4964 element = EL_STEEL_CHAR_R;
4967 case 0x1627: // (blue steel)
4968 element = EL_STEEL_CHAR_S;
4971 case 0x1628: // (blue steel)
4972 element = EL_STEEL_CHAR_T;
4975 case 0x1629: // (blue steel)
4976 element = EL_STEEL_CHAR_U;
4979 case 0x162a: // (blue steel)
4980 element = EL_STEEL_CHAR_V;
4983 case 0x162b: // (blue steel)
4984 element = EL_STEEL_CHAR_W;
4987 case 0x162c: // (blue steel)
4988 element = EL_STEEL_CHAR_X;
4991 case 0x162d: // (blue steel)
4992 element = EL_STEEL_CHAR_Y;
4995 case 0x162e: // (blue steel)
4996 element = EL_STEEL_CHAR_Z;
4999 case 0x162f: // (blue steel)
5000 element = EL_STEEL_CHAR_AUMLAUT;
5003 case 0x1630: // (blue steel)
5004 element = EL_STEEL_CHAR_OUMLAUT;
5007 case 0x1631: // (blue steel)
5008 element = EL_STEEL_CHAR_UUMLAUT;
5011 case 0x1632: // (blue steel)
5012 element = EL_STEEL_CHAR_0;
5015 case 0x1633: // (blue steel)
5016 element = EL_STEEL_CHAR_1;
5019 case 0x1634: // (blue steel)
5020 element = EL_STEEL_CHAR_2;
5023 case 0x1635: // (blue steel)
5024 element = EL_STEEL_CHAR_3;
5027 case 0x1636: // (blue steel)
5028 element = EL_STEEL_CHAR_4;
5031 case 0x1637: // (blue steel)
5032 element = EL_STEEL_CHAR_5;
5035 case 0x1638: // (blue steel)
5036 element = EL_STEEL_CHAR_6;
5039 case 0x1639: // (blue steel)
5040 element = EL_STEEL_CHAR_7;
5043 case 0x163a: // (blue steel)
5044 element = EL_STEEL_CHAR_8;
5047 case 0x163b: // (blue steel)
5048 element = EL_STEEL_CHAR_9;
5051 case 0x163c: // (blue steel)
5052 element = EL_STEEL_CHAR_PERIOD;
5055 case 0x163d: // (blue steel)
5056 element = EL_STEEL_CHAR_EXCLAM;
5059 case 0x163e: // (blue steel)
5060 element = EL_STEEL_CHAR_COLON;
5063 case 0x163f: // (blue steel)
5064 element = EL_STEEL_CHAR_LESS;
5067 case 0x1640: // (blue steel)
5068 element = EL_STEEL_CHAR_GREATER;
5071 case 0x1641: // (blue steel)
5072 element = EL_STEEL_CHAR_QUESTION;
5075 case 0x1642: // (blue steel)
5076 element = EL_STEEL_CHAR_COPYRIGHT;
5079 case 0x1643: // (blue steel)
5080 element = EL_STEEL_CHAR_UP;
5083 case 0x1644: // (blue steel)
5084 element = EL_STEEL_CHAR_DOWN;
5087 case 0x1645: // (blue steel)
5088 element = EL_STEEL_CHAR_BUTTON;
5091 case 0x1646: // (blue steel)
5092 element = EL_STEEL_CHAR_PLUS;
5095 case 0x1647: // (blue steel)
5096 element = EL_STEEL_CHAR_MINUS;
5099 case 0x1648: // (blue steel)
5100 element = EL_STEEL_CHAR_APOSTROPHE;
5103 case 0x1649: // (blue steel)
5104 element = EL_STEEL_CHAR_PARENLEFT;
5107 case 0x164a: // (blue steel)
5108 element = EL_STEEL_CHAR_PARENRIGHT;
5111 case 0x164b: // (green steel)
5112 element = EL_STEEL_CHAR_A;
5115 case 0x164c: // (green steel)
5116 element = EL_STEEL_CHAR_B;
5119 case 0x164d: // (green steel)
5120 element = EL_STEEL_CHAR_C;
5123 case 0x164e: // (green steel)
5124 element = EL_STEEL_CHAR_D;
5127 case 0x164f: // (green steel)
5128 element = EL_STEEL_CHAR_E;
5131 case 0x1650: // (green steel)
5132 element = EL_STEEL_CHAR_F;
5135 case 0x1651: // (green steel)
5136 element = EL_STEEL_CHAR_G;
5139 case 0x1652: // (green steel)
5140 element = EL_STEEL_CHAR_H;
5143 case 0x1653: // (green steel)
5144 element = EL_STEEL_CHAR_I;
5147 case 0x1654: // (green steel)
5148 element = EL_STEEL_CHAR_J;
5151 case 0x1655: // (green steel)
5152 element = EL_STEEL_CHAR_K;
5155 case 0x1656: // (green steel)
5156 element = EL_STEEL_CHAR_L;
5159 case 0x1657: // (green steel)
5160 element = EL_STEEL_CHAR_M;
5163 case 0x1658: // (green steel)
5164 element = EL_STEEL_CHAR_N;
5167 case 0x1659: // (green steel)
5168 element = EL_STEEL_CHAR_O;
5171 case 0x165a: // (green steel)
5172 element = EL_STEEL_CHAR_P;
5175 case 0x165b: // (green steel)
5176 element = EL_STEEL_CHAR_Q;
5179 case 0x165c: // (green steel)
5180 element = EL_STEEL_CHAR_R;
5183 case 0x165d: // (green steel)
5184 element = EL_STEEL_CHAR_S;
5187 case 0x165e: // (green steel)
5188 element = EL_STEEL_CHAR_T;
5191 case 0x165f: // (green steel)
5192 element = EL_STEEL_CHAR_U;
5195 case 0x1660: // (green steel)
5196 element = EL_STEEL_CHAR_V;
5199 case 0x1661: // (green steel)
5200 element = EL_STEEL_CHAR_W;
5203 case 0x1662: // (green steel)
5204 element = EL_STEEL_CHAR_X;
5207 case 0x1663: // (green steel)
5208 element = EL_STEEL_CHAR_Y;
5211 case 0x1664: // (green steel)
5212 element = EL_STEEL_CHAR_Z;
5215 case 0x1665: // (green steel)
5216 element = EL_STEEL_CHAR_AUMLAUT;
5219 case 0x1666: // (green steel)
5220 element = EL_STEEL_CHAR_OUMLAUT;
5223 case 0x1667: // (green steel)
5224 element = EL_STEEL_CHAR_UUMLAUT;
5227 case 0x1668: // (green steel)
5228 element = EL_STEEL_CHAR_0;
5231 case 0x1669: // (green steel)
5232 element = EL_STEEL_CHAR_1;
5235 case 0x166a: // (green steel)
5236 element = EL_STEEL_CHAR_2;
5239 case 0x166b: // (green steel)
5240 element = EL_STEEL_CHAR_3;
5243 case 0x166c: // (green steel)
5244 element = EL_STEEL_CHAR_4;
5247 case 0x166d: // (green steel)
5248 element = EL_STEEL_CHAR_5;
5251 case 0x166e: // (green steel)
5252 element = EL_STEEL_CHAR_6;
5255 case 0x166f: // (green steel)
5256 element = EL_STEEL_CHAR_7;
5259 case 0x1670: // (green steel)
5260 element = EL_STEEL_CHAR_8;
5263 case 0x1671: // (green steel)
5264 element = EL_STEEL_CHAR_9;
5267 case 0x1672: // (green steel)
5268 element = EL_STEEL_CHAR_PERIOD;
5271 case 0x1673: // (green steel)
5272 element = EL_STEEL_CHAR_EXCLAM;
5275 case 0x1674: // (green steel)
5276 element = EL_STEEL_CHAR_COLON;
5279 case 0x1675: // (green steel)
5280 element = EL_STEEL_CHAR_LESS;
5283 case 0x1676: // (green steel)
5284 element = EL_STEEL_CHAR_GREATER;
5287 case 0x1677: // (green steel)
5288 element = EL_STEEL_CHAR_QUESTION;
5291 case 0x1678: // (green steel)
5292 element = EL_STEEL_CHAR_COPYRIGHT;
5295 case 0x1679: // (green steel)
5296 element = EL_STEEL_CHAR_UP;
5299 case 0x167a: // (green steel)
5300 element = EL_STEEL_CHAR_DOWN;
5303 case 0x167b: // (green steel)
5304 element = EL_STEEL_CHAR_BUTTON;
5307 case 0x167c: // (green steel)
5308 element = EL_STEEL_CHAR_PLUS;
5311 case 0x167d: // (green steel)
5312 element = EL_STEEL_CHAR_MINUS;
5315 case 0x167e: // (green steel)
5316 element = EL_STEEL_CHAR_APOSTROPHE;
5319 case 0x167f: // (green steel)
5320 element = EL_STEEL_CHAR_PARENLEFT;
5323 case 0x1680: // (green steel)
5324 element = EL_STEEL_CHAR_PARENRIGHT;
5327 case 0x1681: // gate (red)
5328 element = EL_EM_GATE_1;
5331 case 0x1682: // secret gate (red)
5332 element = EL_EM_GATE_1_GRAY;
5335 case 0x1683: // gate (yellow)
5336 element = EL_EM_GATE_2;
5339 case 0x1684: // secret gate (yellow)
5340 element = EL_EM_GATE_2_GRAY;
5343 case 0x1685: // gate (blue)
5344 element = EL_EM_GATE_4;
5347 case 0x1686: // secret gate (blue)
5348 element = EL_EM_GATE_4_GRAY;
5351 case 0x1687: // gate (green)
5352 element = EL_EM_GATE_3;
5355 case 0x1688: // secret gate (green)
5356 element = EL_EM_GATE_3_GRAY;
5359 case 0x1689: // gate (white)
5360 element = EL_DC_GATE_WHITE;
5363 case 0x168a: // secret gate (white)
5364 element = EL_DC_GATE_WHITE_GRAY;
5367 case 0x168b: // secret gate (no key)
5368 element = EL_DC_GATE_FAKE_GRAY;
5372 element = EL_ROBOT_WHEEL;
5376 element = EL_DC_TIMEGATE_SWITCH;
5380 element = EL_ACID_POOL_BOTTOM;
5384 element = EL_ACID_POOL_TOPLEFT;
5388 element = EL_ACID_POOL_TOPRIGHT;
5392 element = EL_ACID_POOL_BOTTOMLEFT;
5396 element = EL_ACID_POOL_BOTTOMRIGHT;
5400 element = EL_STEELWALL;
5404 element = EL_STEELWALL_SLIPPERY;
5407 case 0x1695: // steel wall (not round)
5408 element = EL_STEELWALL;
5411 case 0x1696: // steel wall (left)
5412 element = EL_DC_STEELWALL_1_LEFT;
5415 case 0x1697: // steel wall (bottom)
5416 element = EL_DC_STEELWALL_1_BOTTOM;
5419 case 0x1698: // steel wall (right)
5420 element = EL_DC_STEELWALL_1_RIGHT;
5423 case 0x1699: // steel wall (top)
5424 element = EL_DC_STEELWALL_1_TOP;
5427 case 0x169a: // steel wall (left/bottom)
5428 element = EL_DC_STEELWALL_1_BOTTOMLEFT;
5431 case 0x169b: // steel wall (right/bottom)
5432 element = EL_DC_STEELWALL_1_BOTTOMRIGHT;
5435 case 0x169c: // steel wall (right/top)
5436 element = EL_DC_STEELWALL_1_TOPRIGHT;
5439 case 0x169d: // steel wall (left/top)
5440 element = EL_DC_STEELWALL_1_TOPLEFT;
5443 case 0x169e: // steel wall (right/bottom small)
5444 element = EL_DC_STEELWALL_1_BOTTOMRIGHT_2;
5447 case 0x169f: // steel wall (left/bottom small)
5448 element = EL_DC_STEELWALL_1_BOTTOMLEFT_2;
5451 case 0x16a0: // steel wall (right/top small)
5452 element = EL_DC_STEELWALL_1_TOPRIGHT_2;
5455 case 0x16a1: // steel wall (left/top small)
5456 element = EL_DC_STEELWALL_1_TOPLEFT_2;
5459 case 0x16a2: // steel wall (left/right)
5460 element = EL_DC_STEELWALL_1_VERTICAL;
5463 case 0x16a3: // steel wall (top/bottom)
5464 element = EL_DC_STEELWALL_1_HORIZONTAL;
5467 case 0x16a4: // steel wall 2 (left end)
5468 element = EL_DC_STEELWALL_2_LEFT;
5471 case 0x16a5: // steel wall 2 (right end)
5472 element = EL_DC_STEELWALL_2_RIGHT;
5475 case 0x16a6: // steel wall 2 (top end)
5476 element = EL_DC_STEELWALL_2_TOP;
5479 case 0x16a7: // steel wall 2 (bottom end)
5480 element = EL_DC_STEELWALL_2_BOTTOM;
5483 case 0x16a8: // steel wall 2 (left/right)
5484 element = EL_DC_STEELWALL_2_HORIZONTAL;
5487 case 0x16a9: // steel wall 2 (up/down)
5488 element = EL_DC_STEELWALL_2_VERTICAL;
5491 case 0x16aa: // steel wall 2 (mid)
5492 element = EL_DC_STEELWALL_2_MIDDLE;
5496 element = EL_SIGN_EXCLAMATION;
5500 element = EL_SIGN_RADIOACTIVITY;
5504 element = EL_SIGN_STOP;
5508 element = EL_SIGN_WHEELCHAIR;
5512 element = EL_SIGN_PARKING;
5516 element = EL_SIGN_NO_ENTRY;
5520 element = EL_SIGN_HEART;
5524 element = EL_SIGN_GIVE_WAY;
5528 element = EL_SIGN_ENTRY_FORBIDDEN;
5532 element = EL_SIGN_EMERGENCY_EXIT;
5536 element = EL_SIGN_YIN_YANG;
5540 element = EL_WALL_EMERALD;
5544 element = EL_WALL_DIAMOND;
5548 element = EL_WALL_PEARL;
5552 element = EL_WALL_CRYSTAL;
5556 element = EL_INVISIBLE_WALL;
5560 element = EL_INVISIBLE_STEELWALL;
5564 // EL_INVISIBLE_SAND
5567 element = EL_LIGHT_SWITCH;
5571 element = EL_ENVELOPE_1;
5575 if (element >= 0x0117 && element <= 0x036e) // (?)
5576 element = EL_DIAMOND;
5577 else if (element >= 0x042d && element <= 0x0684) // (?)
5578 element = EL_EMERALD;
5579 else if (element >= 0x157c && element <= 0x158b)
5581 else if (element >= 0x1590 && element <= 0x159f)
5582 element = EL_DC_LANDMINE;
5583 else if (element >= 0x16bc && element <= 0x16cb)
5584 element = EL_INVISIBLE_SAND;
5587 Warn("unknown Diamond Caves element 0x%04x", element);
5589 element = EL_UNKNOWN;
5594 return getMappedElement(element);
5597 static void LoadLevelFromFileStream_DC(File *file, struct LevelInfo *level,
5600 byte header[DC_LEVEL_HEADER_SIZE];
5602 int envelope_header_pos = 62;
5603 int envelope_content_pos = 94;
5604 int level_name_pos = 251;
5605 int level_author_pos = 292;
5606 int envelope_header_len;
5607 int envelope_content_len;
5609 int level_author_len;
5611 int num_yamyam_contents;
5614 getDecodedWord_DC(0, TRUE); // initialize DC2 decoding engine
5616 for (i = 0; i < DC_LEVEL_HEADER_SIZE / 2; i++)
5618 unsigned short header_word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
5620 header[i * 2 + 0] = header_word >> 8;
5621 header[i * 2 + 1] = header_word & 0xff;
5624 // read some values from level header to check level decoding integrity
5625 fieldx = header[6] | (header[7] << 8);
5626 fieldy = header[8] | (header[9] << 8);
5627 num_yamyam_contents = header[60] | (header[61] << 8);
5629 // do some simple sanity checks to ensure that level was correctly decoded
5630 if (fieldx < 1 || fieldx > 256 ||
5631 fieldy < 1 || fieldy > 256 ||
5632 num_yamyam_contents < 1 || num_yamyam_contents > 8)
5634 level->no_valid_file = TRUE;
5636 Warn("cannot decode level from stream -- using empty level");
5641 // maximum envelope header size is 31 bytes
5642 envelope_header_len = header[envelope_header_pos];
5643 // maximum envelope content size is 110 (156?) bytes
5644 envelope_content_len = header[envelope_content_pos];
5646 // maximum level title size is 40 bytes
5647 level_name_len = MIN(header[level_name_pos], MAX_LEVEL_NAME_LEN);
5648 // maximum level author size is 30 (51?) bytes
5649 level_author_len = MIN(header[level_author_pos], MAX_LEVEL_AUTHOR_LEN);
5653 for (i = 0; i < envelope_header_len; i++)
5654 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
5655 level->envelope[0].text[envelope_size++] =
5656 header[envelope_header_pos + 1 + i];
5658 if (envelope_header_len > 0 && envelope_content_len > 0)
5660 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
5661 level->envelope[0].text[envelope_size++] = '\n';
5662 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
5663 level->envelope[0].text[envelope_size++] = '\n';
5666 for (i = 0; i < envelope_content_len; i++)
5667 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
5668 level->envelope[0].text[envelope_size++] =
5669 header[envelope_content_pos + 1 + i];
5671 level->envelope[0].text[envelope_size] = '\0';
5673 level->envelope[0].xsize = MAX_ENVELOPE_XSIZE;
5674 level->envelope[0].ysize = 10;
5675 level->envelope[0].autowrap = TRUE;
5676 level->envelope[0].centered = TRUE;
5678 for (i = 0; i < level_name_len; i++)
5679 level->name[i] = header[level_name_pos + 1 + i];
5680 level->name[level_name_len] = '\0';
5682 for (i = 0; i < level_author_len; i++)
5683 level->author[i] = header[level_author_pos + 1 + i];
5684 level->author[level_author_len] = '\0';
5686 num_yamyam_contents = header[60] | (header[61] << 8);
5687 level->num_yamyam_contents =
5688 MIN(MAX(MIN_ELEMENT_CONTENTS, num_yamyam_contents), MAX_ELEMENT_CONTENTS);
5690 for (i = 0; i < num_yamyam_contents; i++)
5692 for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
5694 unsigned short word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
5695 int element_dc = ((word & 0xff) << 8) | ((word >> 8) & 0xff);
5697 if (i < MAX_ELEMENT_CONTENTS)
5698 level->yamyam_content[i].e[x][y] = getMappedElement_DC(element_dc);
5702 fieldx = header[6] | (header[7] << 8);
5703 fieldy = header[8] | (header[9] << 8);
5704 level->fieldx = MIN(MAX(MIN_LEV_FIELDX, fieldx), MAX_LEV_FIELDX);
5705 level->fieldy = MIN(MAX(MIN_LEV_FIELDY, fieldy), MAX_LEV_FIELDY);
5707 for (y = 0; y < fieldy; y++) for (x = 0; x < fieldx; x++)
5709 unsigned short word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
5710 int element_dc = ((word & 0xff) << 8) | ((word >> 8) & 0xff);
5712 if (x < MAX_LEV_FIELDX && y < MAX_LEV_FIELDY)
5713 level->field[x][y] = getMappedElement_DC(element_dc);
5716 x = MIN(MAX(0, (header[10] | (header[11] << 8)) - 1), MAX_LEV_FIELDX - 1);
5717 y = MIN(MAX(0, (header[12] | (header[13] << 8)) - 1), MAX_LEV_FIELDY - 1);
5718 level->field[x][y] = EL_PLAYER_1;
5720 x = MIN(MAX(0, (header[14] | (header[15] << 8)) - 1), MAX_LEV_FIELDX - 1);
5721 y = MIN(MAX(0, (header[16] | (header[17] << 8)) - 1), MAX_LEV_FIELDY - 1);
5722 level->field[x][y] = EL_PLAYER_2;
5724 level->gems_needed = header[18] | (header[19] << 8);
5726 level->score[SC_EMERALD] = header[20] | (header[21] << 8);
5727 level->score[SC_DIAMOND] = header[22] | (header[23] << 8);
5728 level->score[SC_PEARL] = header[24] | (header[25] << 8);
5729 level->score[SC_CRYSTAL] = header[26] | (header[27] << 8);
5730 level->score[SC_NUT] = header[28] | (header[29] << 8);
5731 level->score[SC_ROBOT] = header[30] | (header[31] << 8);
5732 level->score[SC_SPACESHIP] = header[32] | (header[33] << 8);
5733 level->score[SC_BUG] = header[34] | (header[35] << 8);
5734 level->score[SC_YAMYAM] = header[36] | (header[37] << 8);
5735 level->score[SC_DYNAMITE] = header[38] | (header[39] << 8);
5736 level->score[SC_KEY] = header[40] | (header[41] << 8);
5737 level->score[SC_TIME_BONUS] = header[42] | (header[43] << 8);
5739 level->time = header[44] | (header[45] << 8);
5741 level->amoeba_speed = header[46] | (header[47] << 8);
5742 level->time_light = header[48] | (header[49] << 8);
5743 level->time_timegate = header[50] | (header[51] << 8);
5744 level->time_wheel = header[52] | (header[53] << 8);
5745 level->time_magic_wall = header[54] | (header[55] << 8);
5746 level->extra_time = header[56] | (header[57] << 8);
5747 level->shield_normal_time = header[58] | (header[59] << 8);
5749 // shield and extra time elements do not have a score
5750 level->score[SC_SHIELD] = 0;
5751 level->extra_time_score = 0;
5753 // set time for normal and deadly shields to the same value
5754 level->shield_deadly_time = level->shield_normal_time;
5756 // Diamond Caves has the same (strange) behaviour as Emerald Mine that gems
5757 // can slip down from flat walls, like normal walls and steel walls
5758 level->em_slippery_gems = TRUE;
5760 // time score is counted for each 10 seconds left in Diamond Caves levels
5761 level->time_score_base = 10;
5764 static void LoadLevelFromFileInfo_DC(struct LevelInfo *level,
5765 struct LevelFileInfo *level_file_info,
5766 boolean level_info_only)
5768 char *filename = level_file_info->filename;
5770 int num_magic_bytes = 8;
5771 char magic_bytes[num_magic_bytes + 1];
5772 int num_levels_to_skip = level_file_info->nr - leveldir_current->first_level;
5774 if (!(file = openFile(filename, MODE_READ)))
5776 level->no_valid_file = TRUE;
5778 if (!level_info_only)
5779 Warn("cannot read level '%s' -- using empty level", filename);
5784 // fseek(file, 0x0000, SEEK_SET);
5786 if (level_file_info->packed)
5788 // read "magic bytes" from start of file
5789 if (getStringFromFile(file, magic_bytes, num_magic_bytes + 1) == NULL)
5790 magic_bytes[0] = '\0';
5792 // check "magic bytes" for correct file format
5793 if (!strPrefix(magic_bytes, "DC2"))
5795 level->no_valid_file = TRUE;
5797 Warn("unknown DC level file '%s' -- using empty level", filename);
5802 if (strPrefix(magic_bytes, "DC2Win95") ||
5803 strPrefix(magic_bytes, "DC2Win98"))
5805 int position_first_level = 0x00fa;
5806 int extra_bytes = 4;
5809 // advance file stream to first level inside the level package
5810 skip_bytes = position_first_level - num_magic_bytes - extra_bytes;
5812 // each block of level data is followed by block of non-level data
5813 num_levels_to_skip *= 2;
5815 // at least skip header bytes, therefore use ">= 0" instead of "> 0"
5816 while (num_levels_to_skip >= 0)
5818 // advance file stream to next level inside the level package
5819 if (seekFile(file, skip_bytes, SEEK_CUR) != 0)
5821 level->no_valid_file = TRUE;
5823 Warn("cannot fseek in file '%s' -- using empty level", filename);
5828 // skip apparently unused extra bytes following each level
5829 ReadUnusedBytesFromFile(file, extra_bytes);
5831 // read size of next level in level package
5832 skip_bytes = getFile32BitLE(file);
5834 num_levels_to_skip--;
5839 level->no_valid_file = TRUE;
5841 Warn("unknown DC2 level file '%s' -- using empty level", filename);
5847 LoadLevelFromFileStream_DC(file, level, level_file_info->nr);
5853 // ----------------------------------------------------------------------------
5854 // functions for loading SB level
5855 // ----------------------------------------------------------------------------
5857 int getMappedElement_SB(int element_ascii, boolean use_ces)
5865 sb_element_mapping[] =
5867 { ' ', EL_EMPTY, EL_CUSTOM_1 }, // floor (space)
5868 { '#', EL_STEELWALL, EL_CUSTOM_2 }, // wall
5869 { '@', EL_PLAYER_1, EL_CUSTOM_3 }, // player
5870 { '$', EL_SOKOBAN_OBJECT, EL_CUSTOM_4 }, // box
5871 { '.', EL_SOKOBAN_FIELD_EMPTY, EL_CUSTOM_5 }, // goal square
5872 { '*', EL_SOKOBAN_FIELD_FULL, EL_CUSTOM_6 }, // box on goal square
5873 { '+', EL_SOKOBAN_FIELD_PLAYER, EL_CUSTOM_7 }, // player on goal square
5874 { '_', EL_INVISIBLE_STEELWALL, EL_FROM_LEVEL_TEMPLATE }, // floor beyond border
5881 for (i = 0; sb_element_mapping[i].ascii != 0; i++)
5882 if (element_ascii == sb_element_mapping[i].ascii)
5883 return (use_ces ? sb_element_mapping[i].ce : sb_element_mapping[i].sb);
5885 return EL_UNDEFINED;
5888 static void LoadLevelFromFileInfo_SB(struct LevelInfo *level,
5889 struct LevelFileInfo *level_file_info,
5890 boolean level_info_only)
5892 char *filename = level_file_info->filename;
5893 char line[MAX_LINE_LEN], line_raw[MAX_LINE_LEN], previous_line[MAX_LINE_LEN];
5894 char last_comment[MAX_LINE_LEN];
5895 char level_name[MAX_LINE_LEN];
5898 int num_levels_to_skip = level_file_info->nr - leveldir_current->first_level;
5899 boolean read_continued_line = FALSE;
5900 boolean reading_playfield = FALSE;
5901 boolean got_valid_playfield_line = FALSE;
5902 boolean invalid_playfield_char = FALSE;
5903 boolean load_xsb_to_ces = check_special_flags("load_xsb_to_ces");
5904 int file_level_nr = 0;
5906 int x = 0, y = 0; // initialized to make compilers happy
5908 last_comment[0] = '\0';
5909 level_name[0] = '\0';
5911 if (!(file = openFile(filename, MODE_READ)))
5913 level->no_valid_file = TRUE;
5915 if (!level_info_only)
5916 Warn("cannot read level '%s' -- using empty level", filename);
5921 while (!checkEndOfFile(file))
5923 // level successfully read, but next level may follow here
5924 if (!got_valid_playfield_line && reading_playfield)
5926 // read playfield from single level file -- skip remaining file
5927 if (!level_file_info->packed)
5930 if (file_level_nr >= num_levels_to_skip)
5935 last_comment[0] = '\0';
5936 level_name[0] = '\0';
5938 reading_playfield = FALSE;
5941 got_valid_playfield_line = FALSE;
5943 // read next line of input file
5944 if (!getStringFromFile(file, line, MAX_LINE_LEN))
5947 // check if line was completely read and is terminated by line break
5948 if (strlen(line) > 0 && line[strlen(line) - 1] == '\n')
5951 // cut trailing line break (this can be newline and/or carriage return)
5952 for (line_ptr = &line[strlen(line)]; line_ptr >= line; line_ptr--)
5953 if ((*line_ptr == '\n' || *line_ptr == '\r') && *(line_ptr + 1) == '\0')
5956 // copy raw input line for later use (mainly debugging output)
5957 strcpy(line_raw, line);
5959 if (read_continued_line)
5961 // append new line to existing line, if there is enough space
5962 if (strlen(previous_line) + strlen(line_ptr) < MAX_LINE_LEN)
5963 strcat(previous_line, line_ptr);
5965 strcpy(line, previous_line); // copy storage buffer to line
5967 read_continued_line = FALSE;
5970 // if the last character is '\', continue at next line
5971 if (strlen(line) > 0 && line[strlen(line) - 1] == '\\')
5973 line[strlen(line) - 1] = '\0'; // cut off trailing backslash
5974 strcpy(previous_line, line); // copy line to storage buffer
5976 read_continued_line = TRUE;
5982 if (line[0] == '\0')
5985 // extract comment text from comment line
5988 for (line_ptr = line; *line_ptr; line_ptr++)
5989 if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != ';')
5992 strcpy(last_comment, line_ptr);
5997 // extract level title text from line containing level title
5998 if (line[0] == '\'')
6000 strcpy(level_name, &line[1]);
6002 if (strlen(level_name) > 0 && level_name[strlen(level_name) - 1] == '\'')
6003 level_name[strlen(level_name) - 1] = '\0';
6008 // skip lines containing only spaces (or empty lines)
6009 for (line_ptr = line; *line_ptr; line_ptr++)
6010 if (*line_ptr != ' ')
6012 if (*line_ptr == '\0')
6015 // at this point, we have found a line containing part of a playfield
6017 got_valid_playfield_line = TRUE;
6019 if (!reading_playfield)
6021 reading_playfield = TRUE;
6022 invalid_playfield_char = FALSE;
6024 for (x = 0; x < MAX_LEV_FIELDX; x++)
6025 for (y = 0; y < MAX_LEV_FIELDY; y++)
6026 level->field[x][y] = getMappedElement_SB(' ', load_xsb_to_ces);
6031 // start with topmost tile row
6035 // skip playfield line if larger row than allowed
6036 if (y >= MAX_LEV_FIELDY)
6039 // start with leftmost tile column
6042 // read playfield elements from line
6043 for (line_ptr = line; *line_ptr; line_ptr++)
6045 int mapped_sb_element = getMappedElement_SB(*line_ptr, load_xsb_to_ces);
6047 // stop parsing playfield line if larger column than allowed
6048 if (x >= MAX_LEV_FIELDX)
6051 if (mapped_sb_element == EL_UNDEFINED)
6053 invalid_playfield_char = TRUE;
6058 level->field[x][y] = mapped_sb_element;
6060 // continue with next tile column
6063 level->fieldx = MAX(x, level->fieldx);
6066 if (invalid_playfield_char)
6068 // if first playfield line, treat invalid lines as comment lines
6070 reading_playfield = FALSE;
6075 // continue with next tile row
6083 level->fieldx = MIN(MAX(MIN_LEV_FIELDX, level->fieldx), MAX_LEV_FIELDX);
6084 level->fieldy = MIN(MAX(MIN_LEV_FIELDY, level->fieldy), MAX_LEV_FIELDY);
6086 if (!reading_playfield)
6088 level->no_valid_file = TRUE;
6090 Warn("cannot read level '%s' -- using empty level", filename);
6095 if (*level_name != '\0')
6097 strncpy(level->name, level_name, MAX_LEVEL_NAME_LEN);
6098 level->name[MAX_LEVEL_NAME_LEN] = '\0';
6100 else if (*last_comment != '\0')
6102 strncpy(level->name, last_comment, MAX_LEVEL_NAME_LEN);
6103 level->name[MAX_LEVEL_NAME_LEN] = '\0';
6107 sprintf(level->name, "--> Level %d <--", level_file_info->nr);
6110 // set all empty fields beyond the border walls to invisible steel wall
6111 for (y = 0; y < level->fieldy; y++) for (x = 0; x < level->fieldx; x++)
6113 if ((x == 0 || x == level->fieldx - 1 ||
6114 y == 0 || y == level->fieldy - 1) &&
6115 level->field[x][y] == getMappedElement_SB(' ', load_xsb_to_ces))
6116 FloodFillLevel(x, y, getMappedElement_SB('_', load_xsb_to_ces),
6117 level->field, level->fieldx, level->fieldy);
6120 // set special level settings for Sokoban levels
6123 level->use_step_counter = TRUE;
6125 if (load_xsb_to_ces)
6127 // special global settings can now be set in level template
6129 level->use_custom_template = TRUE;
6134 // -------------------------------------------------------------------------
6135 // functions for handling native levels
6136 // -------------------------------------------------------------------------
6138 static void LoadLevelFromFileInfo_EM(struct LevelInfo *level,
6139 struct LevelFileInfo *level_file_info,
6140 boolean level_info_only)
6142 if (!LoadNativeLevel_EM(level_file_info->filename, level_info_only))
6143 level->no_valid_file = TRUE;
6146 static void LoadLevelFromFileInfo_SP(struct LevelInfo *level,
6147 struct LevelFileInfo *level_file_info,
6148 boolean level_info_only)
6152 // determine position of requested level inside level package
6153 if (level_file_info->packed)
6154 pos = level_file_info->nr - leveldir_current->first_level;
6156 if (!LoadNativeLevel_SP(level_file_info->filename, pos, level_info_only))
6157 level->no_valid_file = TRUE;
6160 static void LoadLevelFromFileInfo_MM(struct LevelInfo *level,
6161 struct LevelFileInfo *level_file_info,
6162 boolean level_info_only)
6164 if (!LoadNativeLevel_MM(level_file_info->filename, level_info_only))
6165 level->no_valid_file = TRUE;
6168 void CopyNativeLevel_RND_to_Native(struct LevelInfo *level)
6170 if (level->game_engine_type == GAME_ENGINE_TYPE_EM)
6171 CopyNativeLevel_RND_to_EM(level);
6172 else if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
6173 CopyNativeLevel_RND_to_SP(level);
6174 else if (level->game_engine_type == GAME_ENGINE_TYPE_MM)
6175 CopyNativeLevel_RND_to_MM(level);
6178 void CopyNativeLevel_Native_to_RND(struct LevelInfo *level)
6180 if (level->game_engine_type == GAME_ENGINE_TYPE_EM)
6181 CopyNativeLevel_EM_to_RND(level);
6182 else if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
6183 CopyNativeLevel_SP_to_RND(level);
6184 else if (level->game_engine_type == GAME_ENGINE_TYPE_MM)
6185 CopyNativeLevel_MM_to_RND(level);
6188 void SaveNativeLevel(struct LevelInfo *level)
6190 if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
6192 char *basename = getSingleLevelBasenameExt(level->file_info.nr, "sp");
6193 char *filename = getLevelFilenameFromBasename(basename);
6195 CopyNativeLevel_RND_to_SP(level);
6196 CopyNativeTape_RND_to_SP(level);
6198 SaveNativeLevel_SP(filename);
6203 // ----------------------------------------------------------------------------
6204 // functions for loading generic level
6205 // ----------------------------------------------------------------------------
6207 static void LoadLevelFromFileInfo(struct LevelInfo *level,
6208 struct LevelFileInfo *level_file_info,
6209 boolean level_info_only)
6211 // always start with reliable default values
6212 setLevelInfoToDefaults(level, level_info_only, TRUE);
6214 switch (level_file_info->type)
6216 case LEVEL_FILE_TYPE_RND:
6217 LoadLevelFromFileInfo_RND(level, level_file_info, level_info_only);
6220 case LEVEL_FILE_TYPE_EM:
6221 LoadLevelFromFileInfo_EM(level, level_file_info, level_info_only);
6222 level->game_engine_type = GAME_ENGINE_TYPE_EM;
6225 case LEVEL_FILE_TYPE_SP:
6226 LoadLevelFromFileInfo_SP(level, level_file_info, level_info_only);
6227 level->game_engine_type = GAME_ENGINE_TYPE_SP;
6230 case LEVEL_FILE_TYPE_MM:
6231 LoadLevelFromFileInfo_MM(level, level_file_info, level_info_only);
6232 level->game_engine_type = GAME_ENGINE_TYPE_MM;
6235 case LEVEL_FILE_TYPE_DC:
6236 LoadLevelFromFileInfo_DC(level, level_file_info, level_info_only);
6239 case LEVEL_FILE_TYPE_SB:
6240 LoadLevelFromFileInfo_SB(level, level_file_info, level_info_only);
6244 LoadLevelFromFileInfo_RND(level, level_file_info, level_info_only);
6248 // if level file is invalid, restore level structure to default values
6249 if (level->no_valid_file)
6250 setLevelInfoToDefaults(level, level_info_only, FALSE);
6252 if (level->game_engine_type == GAME_ENGINE_TYPE_UNKNOWN)
6253 level->game_engine_type = GAME_ENGINE_TYPE_RND;
6255 if (level_file_info->type != LEVEL_FILE_TYPE_RND)
6256 CopyNativeLevel_Native_to_RND(level);
6259 void LoadLevelFromFilename(struct LevelInfo *level, char *filename)
6261 static struct LevelFileInfo level_file_info;
6263 // always start with reliable default values
6264 setFileInfoToDefaults(&level_file_info);
6266 level_file_info.nr = 0; // unknown level number
6267 level_file_info.type = LEVEL_FILE_TYPE_RND; // no others supported yet
6269 setString(&level_file_info.filename, filename);
6271 LoadLevelFromFileInfo(level, &level_file_info, FALSE);
6274 static void LoadLevel_InitVersion(struct LevelInfo *level)
6278 if (leveldir_current == NULL) // only when dumping level
6281 // all engine modifications also valid for levels which use latest engine
6282 if (level->game_version < VERSION_IDENT(3,2,0,5))
6284 // time bonus score was given for 10 s instead of 1 s before 3.2.0-5
6285 level->time_score_base = 10;
6288 if (leveldir_current->latest_engine)
6290 // ---------- use latest game engine --------------------------------------
6292 /* For all levels which are forced to use the latest game engine version
6293 (normally all but user contributed, private and undefined levels), set
6294 the game engine version to the actual version; this allows for actual
6295 corrections in the game engine to take effect for existing, converted
6296 levels (from "classic" or other existing games) to make the emulation
6297 of the corresponding game more accurate, while (hopefully) not breaking
6298 existing levels created from other players. */
6300 level->game_version = GAME_VERSION_ACTUAL;
6302 /* Set special EM style gems behaviour: EM style gems slip down from
6303 normal, steel and growing wall. As this is a more fundamental change,
6304 it seems better to set the default behaviour to "off" (as it is more
6305 natural) and make it configurable in the level editor (as a property
6306 of gem style elements). Already existing converted levels (neither
6307 private nor contributed levels) are changed to the new behaviour. */
6309 if (level->file_version < FILE_VERSION_2_0)
6310 level->em_slippery_gems = TRUE;
6315 // ---------- use game engine the level was created with --------------------
6317 /* For all levels which are not forced to use the latest game engine
6318 version (normally user contributed, private and undefined levels),
6319 use the version of the game engine the levels were created for.
6321 Since 2.0.1, the game engine version is now directly stored
6322 in the level file (chunk "VERS"), so there is no need anymore
6323 to set the game version from the file version (except for old,
6324 pre-2.0 levels, where the game version is still taken from the
6325 file format version used to store the level -- see above). */
6327 // player was faster than enemies in 1.0.0 and before
6328 if (level->file_version == FILE_VERSION_1_0)
6329 for (i = 0; i < MAX_PLAYERS; i++)
6330 level->initial_player_stepsize[i] = STEPSIZE_FAST;
6332 // default behaviour for EM style gems was "slippery" only in 2.0.1
6333 if (level->game_version == VERSION_IDENT(2,0,1,0))
6334 level->em_slippery_gems = TRUE;
6336 // springs could be pushed over pits before (pre-release version) 2.2.0
6337 if (level->game_version < VERSION_IDENT(2,2,0,0))
6338 level->use_spring_bug = TRUE;
6340 if (level->game_version < VERSION_IDENT(3,2,0,5))
6342 // time orb caused limited time in endless time levels before 3.2.0-5
6343 level->use_time_orb_bug = TRUE;
6345 // default behaviour for snapping was "no snap delay" before 3.2.0-5
6346 level->block_snap_field = FALSE;
6348 // extra time score was same value as time left score before 3.2.0-5
6349 level->extra_time_score = level->score[SC_TIME_BONUS];
6352 if (level->game_version < VERSION_IDENT(3,2,0,7))
6354 // default behaviour for snapping was "not continuous" before 3.2.0-7
6355 level->continuous_snapping = FALSE;
6358 // only few elements were able to actively move into acid before 3.1.0
6359 // trigger settings did not exist before 3.1.0; set to default "any"
6360 if (level->game_version < VERSION_IDENT(3,1,0,0))
6362 // correct "can move into acid" settings (all zero in old levels)
6364 level->can_move_into_acid_bits = 0; // nothing can move into acid
6365 level->dont_collide_with_bits = 0; // nothing is deadly when colliding
6367 setMoveIntoAcidProperty(level, EL_ROBOT, TRUE);
6368 setMoveIntoAcidProperty(level, EL_SATELLITE, TRUE);
6369 setMoveIntoAcidProperty(level, EL_PENGUIN, TRUE);
6370 setMoveIntoAcidProperty(level, EL_BALLOON, TRUE);
6372 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6373 SET_PROPERTY(EL_CUSTOM_START + i, EP_CAN_MOVE_INTO_ACID, TRUE);
6375 // correct trigger settings (stored as zero == "none" in old levels)
6377 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6379 int element = EL_CUSTOM_START + i;
6380 struct ElementInfo *ei = &element_info[element];
6382 for (j = 0; j < ei->num_change_pages; j++)
6384 struct ElementChangeInfo *change = &ei->change_page[j];
6386 change->trigger_player = CH_PLAYER_ANY;
6387 change->trigger_page = CH_PAGE_ANY;
6392 // try to detect and fix "Snake Bite" levels, which are broken with 3.2.0
6394 int element = EL_CUSTOM_256;
6395 struct ElementInfo *ei = &element_info[element];
6396 struct ElementChangeInfo *change = &ei->change_page[0];
6398 /* This is needed to fix a problem that was caused by a bugfix in function
6399 game.c/CreateFieldExt() introduced with 3.2.0 that corrects the behaviour
6400 when a custom element changes to EL_SOKOBAN_FIELD_PLAYER (before, it did
6401 not replace walkable elements, but instead just placed the player on it,
6402 without placing the Sokoban field under the player). Unfortunately, this
6403 breaks "Snake Bite" style levels when the snake is halfway through a door
6404 that just closes (the snake head is still alive and can be moved in this
6405 case). This can be fixed by replacing the EL_SOKOBAN_FIELD_PLAYER by the
6406 player (without Sokoban element) which then gets killed as designed). */
6408 if ((strncmp(leveldir_current->identifier, "snake_bite", 10) == 0 ||
6409 strncmp(ei->description, "pause b4 death", 14) == 0) &&
6410 change->target_element == EL_SOKOBAN_FIELD_PLAYER)
6411 change->target_element = EL_PLAYER_1;
6414 // try to detect and fix "Zelda" style levels, which are broken with 3.2.5
6415 if (level->game_version < VERSION_IDENT(3,2,5,0))
6417 /* This is needed to fix a problem that was caused by a bugfix in function
6418 game.c/CheckTriggeredElementChangeExt() introduced with 3.2.5 that
6419 corrects the behaviour when a custom element changes to another custom
6420 element with a higher element number that has change actions defined.
6421 Normally, only one change per frame is allowed for custom elements.
6422 Therefore, it is checked if a custom element already changed in the
6423 current frame; if it did, subsequent changes are suppressed.
6424 Unfortunately, this is only checked for element changes, but not for
6425 change actions, which are still executed. As the function above loops
6426 through all custom elements from lower to higher, an element change
6427 resulting in a lower CE number won't be checked again, while a target
6428 element with a higher number will also be checked, and potential change
6429 actions will get executed for this CE, too (which is wrong), while
6430 further changes are ignored (which is correct). As this bugfix breaks
6431 Zelda II (and introduces graphical bugs to Zelda I, and also breaks a
6432 few other levels like Alan Bond's "FMV"), allow the previous, incorrect
6433 behaviour for existing levels and tapes that make use of this bug */
6435 level->use_action_after_change_bug = TRUE;
6438 // not centering level after relocating player was default only in 3.2.3
6439 if (level->game_version == VERSION_IDENT(3,2,3,0)) // (no pre-releases)
6440 level->shifted_relocation = TRUE;
6442 // EM style elements always chain-exploded in R'n'D engine before 3.2.6
6443 if (level->game_version < VERSION_IDENT(3,2,6,0))
6444 level->em_explodes_by_fire = TRUE;
6446 // levels were solved by the first player entering an exit up to 4.1.0.0
6447 if (level->game_version <= VERSION_IDENT(4,1,0,0))
6448 level->solved_by_one_player = TRUE;
6450 // game logic of "game of life" and "biomaze" was buggy before 4.1.1.1
6451 if (level->game_version < VERSION_IDENT(4,1,1,1))
6452 level->use_life_bugs = TRUE;
6454 // only Sokoban fields (but not objects) had to be solved before 4.1.1.1
6455 if (level->game_version < VERSION_IDENT(4,1,1,1))
6456 level->sb_objects_needed = FALSE;
6458 // CE actions were triggered by unfinished digging/collecting up to 4.2.2.0
6459 if (level->game_version <= VERSION_IDENT(4,2,2,0))
6460 level->finish_dig_collect = FALSE;
6463 static void LoadLevel_InitStandardElements(struct LevelInfo *level)
6467 // map elements that have changed in newer versions
6468 level->amoeba_content = getMappedElementByVersion(level->amoeba_content,
6469 level->game_version);
6470 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
6471 for (x = 0; x < 3; x++)
6472 for (y = 0; y < 3; y++)
6473 level->yamyam_content[i].e[x][y] =
6474 getMappedElementByVersion(level->yamyam_content[i].e[x][y],
6475 level->game_version);
6479 static void LoadLevel_InitCustomElements(struct LevelInfo *level)
6483 // map custom element change events that have changed in newer versions
6484 // (these following values were accidentally changed in version 3.0.1)
6485 // (this seems to be needed only for 'ab_levelset3' and 'ab_levelset4')
6486 if (level->game_version <= VERSION_IDENT(3,0,0,0))
6488 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6490 int element = EL_CUSTOM_START + i;
6492 // order of checking and copying events to be mapped is important
6493 // (do not change the start and end value -- they are constant)
6494 for (j = CE_BY_OTHER_ACTION; j >= CE_VALUE_GETS_ZERO; j--)
6496 if (HAS_CHANGE_EVENT(element, j - 2))
6498 SET_CHANGE_EVENT(element, j - 2, FALSE);
6499 SET_CHANGE_EVENT(element, j, TRUE);
6503 // order of checking and copying events to be mapped is important
6504 // (do not change the start and end value -- they are constant)
6505 for (j = CE_PLAYER_COLLECTS_X; j >= CE_HITTING_SOMETHING; j--)
6507 if (HAS_CHANGE_EVENT(element, j - 1))
6509 SET_CHANGE_EVENT(element, j - 1, FALSE);
6510 SET_CHANGE_EVENT(element, j, TRUE);
6516 // initialize "can_change" field for old levels with only one change page
6517 if (level->game_version <= VERSION_IDENT(3,0,2,0))
6519 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6521 int element = EL_CUSTOM_START + i;
6523 if (CAN_CHANGE(element))
6524 element_info[element].change->can_change = TRUE;
6528 // correct custom element values (for old levels without these options)
6529 if (level->game_version < VERSION_IDENT(3,1,1,0))
6531 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6533 int element = EL_CUSTOM_START + i;
6534 struct ElementInfo *ei = &element_info[element];
6536 if (ei->access_direction == MV_NO_DIRECTION)
6537 ei->access_direction = MV_ALL_DIRECTIONS;
6541 // correct custom element values (fix invalid values for all versions)
6544 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6546 int element = EL_CUSTOM_START + i;
6547 struct ElementInfo *ei = &element_info[element];
6549 for (j = 0; j < ei->num_change_pages; j++)
6551 struct ElementChangeInfo *change = &ei->change_page[j];
6553 if (change->trigger_player == CH_PLAYER_NONE)
6554 change->trigger_player = CH_PLAYER_ANY;
6556 if (change->trigger_side == CH_SIDE_NONE)
6557 change->trigger_side = CH_SIDE_ANY;
6562 // initialize "can_explode" field for old levels which did not store this
6563 // !!! CHECK THIS -- "<= 3,1,0,0" IS PROBABLY WRONG !!!
6564 if (level->game_version <= VERSION_IDENT(3,1,0,0))
6566 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6568 int element = EL_CUSTOM_START + i;
6570 if (EXPLODES_1X1_OLD(element))
6571 element_info[element].explosion_type = EXPLODES_1X1;
6573 SET_PROPERTY(element, EP_CAN_EXPLODE, (EXPLODES_BY_FIRE(element) ||
6574 EXPLODES_SMASHED(element) ||
6575 EXPLODES_IMPACT(element)));
6579 // correct previously hard-coded move delay values for maze runner style
6580 if (level->game_version < VERSION_IDENT(3,1,1,0))
6582 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6584 int element = EL_CUSTOM_START + i;
6586 if (element_info[element].move_pattern & MV_MAZE_RUNNER_STYLE)
6588 // previously hard-coded and therefore ignored
6589 element_info[element].move_delay_fixed = 9;
6590 element_info[element].move_delay_random = 0;
6595 // set some other uninitialized values of custom elements in older levels
6596 if (level->game_version < VERSION_IDENT(3,1,0,0))
6598 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6600 int element = EL_CUSTOM_START + i;
6602 element_info[element].access_direction = MV_ALL_DIRECTIONS;
6604 element_info[element].explosion_delay = 17;
6605 element_info[element].ignition_delay = 8;
6610 static void LoadLevel_InitElements(struct LevelInfo *level)
6612 LoadLevel_InitStandardElements(level);
6614 if (level->file_has_custom_elements)
6615 LoadLevel_InitCustomElements(level);
6617 // initialize element properties for level editor etc.
6618 InitElementPropertiesEngine(level->game_version);
6619 InitElementPropertiesGfxElement();
6622 static void LoadLevel_InitPlayfield(struct LevelInfo *level)
6626 // map elements that have changed in newer versions
6627 for (y = 0; y < level->fieldy; y++)
6628 for (x = 0; x < level->fieldx; x++)
6629 level->field[x][y] = getMappedElementByVersion(level->field[x][y],
6630 level->game_version);
6632 // clear unused playfield data (nicer if level gets resized in editor)
6633 for (x = 0; x < MAX_LEV_FIELDX; x++)
6634 for (y = 0; y < MAX_LEV_FIELDY; y++)
6635 if (x >= level->fieldx || y >= level->fieldy)
6636 level->field[x][y] = EL_EMPTY;
6638 // copy elements to runtime playfield array
6639 for (x = 0; x < MAX_LEV_FIELDX; x++)
6640 for (y = 0; y < MAX_LEV_FIELDY; y++)
6641 Tile[x][y] = level->field[x][y];
6643 // initialize level size variables for faster access
6644 lev_fieldx = level->fieldx;
6645 lev_fieldy = level->fieldy;
6647 // determine border element for this level
6648 if (level->file_info.type == LEVEL_FILE_TYPE_DC)
6649 BorderElement = EL_EMPTY; // (in editor, SetBorderElement() is used)
6654 static void LoadLevel_InitNativeEngines(struct LevelInfo *level)
6656 struct LevelFileInfo *level_file_info = &level->file_info;
6658 if (level_file_info->type == LEVEL_FILE_TYPE_RND)
6659 CopyNativeLevel_RND_to_Native(level);
6662 static void LoadLevelTemplate_LoadAndInit(void)
6664 LoadLevelFromFileInfo(&level_template, &level_template.file_info, FALSE);
6666 LoadLevel_InitVersion(&level_template);
6667 LoadLevel_InitElements(&level_template);
6669 ActivateLevelTemplate();
6672 void LoadLevelTemplate(int nr)
6674 if (!fileExists(getGlobalLevelTemplateFilename()))
6676 Warn("no level template found for this level");
6681 setLevelFileInfo(&level_template.file_info, nr);
6683 LoadLevelTemplate_LoadAndInit();
6686 static void LoadNetworkLevelTemplate(struct NetworkLevelInfo *network_level)
6688 copyLevelFileInfo(&network_level->tmpl_info, &level_template.file_info);
6690 LoadLevelTemplate_LoadAndInit();
6693 static void LoadLevel_LoadAndInit(struct NetworkLevelInfo *network_level)
6695 LoadLevelFromFileInfo(&level, &level.file_info, FALSE);
6697 if (level.use_custom_template)
6699 if (network_level != NULL)
6700 LoadNetworkLevelTemplate(network_level);
6702 LoadLevelTemplate(-1);
6705 LoadLevel_InitVersion(&level);
6706 LoadLevel_InitElements(&level);
6707 LoadLevel_InitPlayfield(&level);
6709 LoadLevel_InitNativeEngines(&level);
6712 void LoadLevel(int nr)
6714 SetLevelSetInfo(leveldir_current->identifier, nr);
6716 setLevelFileInfo(&level.file_info, nr);
6718 LoadLevel_LoadAndInit(NULL);
6721 void LoadLevelInfoOnly(int nr)
6723 setLevelFileInfo(&level.file_info, nr);
6725 LoadLevelFromFileInfo(&level, &level.file_info, TRUE);
6728 void LoadNetworkLevel(struct NetworkLevelInfo *network_level)
6730 SetLevelSetInfo(network_level->leveldir_identifier,
6731 network_level->file_info.nr);
6733 copyLevelFileInfo(&network_level->file_info, &level.file_info);
6735 LoadLevel_LoadAndInit(network_level);
6738 static int SaveLevel_VERS(FILE *file, struct LevelInfo *level)
6742 chunk_size += putFileVersion(file, level->file_version);
6743 chunk_size += putFileVersion(file, level->game_version);
6748 static int SaveLevel_DATE(FILE *file, struct LevelInfo *level)
6752 chunk_size += putFile16BitBE(file, level->creation_date.year);
6753 chunk_size += putFile8Bit(file, level->creation_date.month);
6754 chunk_size += putFile8Bit(file, level->creation_date.day);
6759 #if ENABLE_HISTORIC_CHUNKS
6760 static void SaveLevel_HEAD(FILE *file, struct LevelInfo *level)
6764 putFile8Bit(file, level->fieldx);
6765 putFile8Bit(file, level->fieldy);
6767 putFile16BitBE(file, level->time);
6768 putFile16BitBE(file, level->gems_needed);
6770 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
6771 putFile8Bit(file, level->name[i]);
6773 for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
6774 putFile8Bit(file, level->score[i]);
6776 for (i = 0; i < STD_ELEMENT_CONTENTS; i++)
6777 for (y = 0; y < 3; y++)
6778 for (x = 0; x < 3; x++)
6779 putFile8Bit(file, (level->encoding_16bit_yamyam ? EL_EMPTY :
6780 level->yamyam_content[i].e[x][y]));
6781 putFile8Bit(file, level->amoeba_speed);
6782 putFile8Bit(file, level->time_magic_wall);
6783 putFile8Bit(file, level->time_wheel);
6784 putFile8Bit(file, (level->encoding_16bit_amoeba ? EL_EMPTY :
6785 level->amoeba_content));
6786 putFile8Bit(file, (level->initial_player_stepsize == STEPSIZE_FAST ? 1 : 0));
6787 putFile8Bit(file, (level->initial_gravity ? 1 : 0));
6788 putFile8Bit(file, (level->encoding_16bit_field ? 1 : 0));
6789 putFile8Bit(file, (level->em_slippery_gems ? 1 : 0));
6791 putFile8Bit(file, (level->use_custom_template ? 1 : 0));
6793 putFile8Bit(file, (level->block_last_field ? 1 : 0));
6794 putFile8Bit(file, (level->sp_block_last_field ? 1 : 0));
6795 putFile32BitBE(file, level->can_move_into_acid_bits);
6796 putFile8Bit(file, level->dont_collide_with_bits);
6798 putFile8Bit(file, (level->use_spring_bug ? 1 : 0));
6799 putFile8Bit(file, (level->use_step_counter ? 1 : 0));
6801 putFile8Bit(file, (level->instant_relocation ? 1 : 0));
6802 putFile8Bit(file, (level->can_pass_to_walkable ? 1 : 0));
6803 putFile8Bit(file, (level->grow_into_diggable ? 1 : 0));
6805 putFile8Bit(file, level->game_engine_type);
6807 WriteUnusedBytesToFile(file, LEVEL_CHUNK_HEAD_UNUSED);
6811 static int SaveLevel_NAME(FILE *file, struct LevelInfo *level)
6816 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
6817 chunk_size += putFile8Bit(file, level->name[i]);
6822 static int SaveLevel_AUTH(FILE *file, struct LevelInfo *level)
6827 for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
6828 chunk_size += putFile8Bit(file, level->author[i]);
6833 #if ENABLE_HISTORIC_CHUNKS
6834 static int SaveLevel_BODY(FILE *file, struct LevelInfo *level)
6839 for (y = 0; y < level->fieldy; y++)
6840 for (x = 0; x < level->fieldx; x++)
6841 if (level->encoding_16bit_field)
6842 chunk_size += putFile16BitBE(file, level->field[x][y]);
6844 chunk_size += putFile8Bit(file, level->field[x][y]);
6850 static int SaveLevel_BODY(FILE *file, struct LevelInfo *level)
6855 for (y = 0; y < level->fieldy; y++)
6856 for (x = 0; x < level->fieldx; x++)
6857 chunk_size += putFile16BitBE(file, level->field[x][y]);
6862 #if ENABLE_HISTORIC_CHUNKS
6863 static void SaveLevel_CONT(FILE *file, struct LevelInfo *level)
6867 putFile8Bit(file, EL_YAMYAM);
6868 putFile8Bit(file, level->num_yamyam_contents);
6869 putFile8Bit(file, 0);
6870 putFile8Bit(file, 0);
6872 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
6873 for (y = 0; y < 3; y++)
6874 for (x = 0; x < 3; x++)
6875 if (level->encoding_16bit_field)
6876 putFile16BitBE(file, level->yamyam_content[i].e[x][y]);
6878 putFile8Bit(file, level->yamyam_content[i].e[x][y]);
6882 #if ENABLE_HISTORIC_CHUNKS
6883 static void SaveLevel_CNT2(FILE *file, struct LevelInfo *level, int element)
6886 int num_contents, content_xsize, content_ysize;
6887 int content_array[MAX_ELEMENT_CONTENTS][3][3];
6889 if (element == EL_YAMYAM)
6891 num_contents = level->num_yamyam_contents;
6895 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
6896 for (y = 0; y < 3; y++)
6897 for (x = 0; x < 3; x++)
6898 content_array[i][x][y] = level->yamyam_content[i].e[x][y];
6900 else if (element == EL_BD_AMOEBA)
6906 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
6907 for (y = 0; y < 3; y++)
6908 for (x = 0; x < 3; x++)
6909 content_array[i][x][y] = EL_EMPTY;
6910 content_array[0][0][0] = level->amoeba_content;
6914 // chunk header already written -- write empty chunk data
6915 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_SIZE);
6917 Warn("cannot save content for element '%d'", element);
6922 putFile16BitBE(file, element);
6923 putFile8Bit(file, num_contents);
6924 putFile8Bit(file, content_xsize);
6925 putFile8Bit(file, content_ysize);
6927 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_UNUSED);
6929 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
6930 for (y = 0; y < 3; y++)
6931 for (x = 0; x < 3; x++)
6932 putFile16BitBE(file, content_array[i][x][y]);
6936 #if ENABLE_HISTORIC_CHUNKS
6937 static int SaveLevel_CNT3(FILE *file, struct LevelInfo *level, int element)
6939 int envelope_nr = element - EL_ENVELOPE_1;
6940 int envelope_len = strlen(level->envelope_text[envelope_nr]) + 1;
6944 chunk_size += putFile16BitBE(file, element);
6945 chunk_size += putFile16BitBE(file, envelope_len);
6946 chunk_size += putFile8Bit(file, level->envelope_xsize[envelope_nr]);
6947 chunk_size += putFile8Bit(file, level->envelope_ysize[envelope_nr]);
6949 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT3_UNUSED);
6950 chunk_size += LEVEL_CHUNK_CNT3_UNUSED;
6952 for (i = 0; i < envelope_len; i++)
6953 chunk_size += putFile8Bit(file, level->envelope_text[envelope_nr][i]);
6959 #if ENABLE_HISTORIC_CHUNKS
6960 static void SaveLevel_CUS1(FILE *file, struct LevelInfo *level,
6961 int num_changed_custom_elements)
6965 putFile16BitBE(file, num_changed_custom_elements);
6967 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6969 int element = EL_CUSTOM_START + i;
6971 struct ElementInfo *ei = &element_info[element];
6973 if (ei->properties[EP_BITFIELD_BASE_NR] != EP_BITMASK_DEFAULT)
6975 if (check < num_changed_custom_elements)
6977 putFile16BitBE(file, element);
6978 putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
6985 if (check != num_changed_custom_elements) // should not happen
6986 Warn("inconsistent number of custom element properties");
6990 #if ENABLE_HISTORIC_CHUNKS
6991 static void SaveLevel_CUS2(FILE *file, struct LevelInfo *level,
6992 int num_changed_custom_elements)
6996 putFile16BitBE(file, num_changed_custom_elements);
6998 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7000 int element = EL_CUSTOM_START + i;
7002 if (element_info[element].change->target_element != EL_EMPTY_SPACE)
7004 if (check < num_changed_custom_elements)
7006 putFile16BitBE(file, element);
7007 putFile16BitBE(file, element_info[element].change->target_element);
7014 if (check != num_changed_custom_elements) // should not happen
7015 Warn("inconsistent number of custom target elements");
7019 #if ENABLE_HISTORIC_CHUNKS
7020 static void SaveLevel_CUS3(FILE *file, struct LevelInfo *level,
7021 int num_changed_custom_elements)
7023 int i, j, x, y, check = 0;
7025 putFile16BitBE(file, num_changed_custom_elements);
7027 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7029 int element = EL_CUSTOM_START + i;
7030 struct ElementInfo *ei = &element_info[element];
7032 if (ei->modified_settings)
7034 if (check < num_changed_custom_elements)
7036 putFile16BitBE(file, element);
7038 for (j = 0; j < MAX_ELEMENT_NAME_LEN; j++)
7039 putFile8Bit(file, ei->description[j]);
7041 putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
7043 // some free bytes for future properties and padding
7044 WriteUnusedBytesToFile(file, 7);
7046 putFile8Bit(file, ei->use_gfx_element);
7047 putFile16BitBE(file, ei->gfx_element_initial);
7049 putFile8Bit(file, ei->collect_score_initial);
7050 putFile8Bit(file, ei->collect_count_initial);
7052 putFile16BitBE(file, ei->push_delay_fixed);
7053 putFile16BitBE(file, ei->push_delay_random);
7054 putFile16BitBE(file, ei->move_delay_fixed);
7055 putFile16BitBE(file, ei->move_delay_random);
7057 putFile16BitBE(file, ei->move_pattern);
7058 putFile8Bit(file, ei->move_direction_initial);
7059 putFile8Bit(file, ei->move_stepsize);
7061 for (y = 0; y < 3; y++)
7062 for (x = 0; x < 3; x++)
7063 putFile16BitBE(file, ei->content.e[x][y]);
7065 putFile32BitBE(file, ei->change->events);
7067 putFile16BitBE(file, ei->change->target_element);
7069 putFile16BitBE(file, ei->change->delay_fixed);
7070 putFile16BitBE(file, ei->change->delay_random);
7071 putFile16BitBE(file, ei->change->delay_frames);
7073 putFile16BitBE(file, ei->change->initial_trigger_element);
7075 putFile8Bit(file, ei->change->explode);
7076 putFile8Bit(file, ei->change->use_target_content);
7077 putFile8Bit(file, ei->change->only_if_complete);
7078 putFile8Bit(file, ei->change->use_random_replace);
7080 putFile8Bit(file, ei->change->random_percentage);
7081 putFile8Bit(file, ei->change->replace_when);
7083 for (y = 0; y < 3; y++)
7084 for (x = 0; x < 3; x++)
7085 putFile16BitBE(file, ei->change->content.e[x][y]);
7087 putFile8Bit(file, ei->slippery_type);
7089 // some free bytes for future properties and padding
7090 WriteUnusedBytesToFile(file, LEVEL_CPART_CUS3_UNUSED);
7097 if (check != num_changed_custom_elements) // should not happen
7098 Warn("inconsistent number of custom element properties");
7102 #if ENABLE_HISTORIC_CHUNKS
7103 static void SaveLevel_CUS4(FILE *file, struct LevelInfo *level, int element)
7105 struct ElementInfo *ei = &element_info[element];
7108 // ---------- custom element base property values (96 bytes) ----------------
7110 putFile16BitBE(file, element);
7112 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
7113 putFile8Bit(file, ei->description[i]);
7115 putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
7117 WriteUnusedBytesToFile(file, 4); // reserved for more base properties
7119 putFile8Bit(file, ei->num_change_pages);
7121 putFile16BitBE(file, ei->ce_value_fixed_initial);
7122 putFile16BitBE(file, ei->ce_value_random_initial);
7123 putFile8Bit(file, ei->use_last_ce_value);
7125 putFile8Bit(file, ei->use_gfx_element);
7126 putFile16BitBE(file, ei->gfx_element_initial);
7128 putFile8Bit(file, ei->collect_score_initial);
7129 putFile8Bit(file, ei->collect_count_initial);
7131 putFile8Bit(file, ei->drop_delay_fixed);
7132 putFile8Bit(file, ei->push_delay_fixed);
7133 putFile8Bit(file, ei->drop_delay_random);
7134 putFile8Bit(file, ei->push_delay_random);
7135 putFile16BitBE(file, ei->move_delay_fixed);
7136 putFile16BitBE(file, ei->move_delay_random);
7138 // bits 0 - 15 of "move_pattern" ...
7139 putFile16BitBE(file, ei->move_pattern & 0xffff);
7140 putFile8Bit(file, ei->move_direction_initial);
7141 putFile8Bit(file, ei->move_stepsize);
7143 putFile8Bit(file, ei->slippery_type);
7145 for (y = 0; y < 3; y++)
7146 for (x = 0; x < 3; x++)
7147 putFile16BitBE(file, ei->content.e[x][y]);
7149 putFile16BitBE(file, ei->move_enter_element);
7150 putFile16BitBE(file, ei->move_leave_element);
7151 putFile8Bit(file, ei->move_leave_type);
7153 // ... bits 16 - 31 of "move_pattern" (not nice, but downward compatible)
7154 putFile16BitBE(file, (ei->move_pattern >> 16) & 0xffff);
7156 putFile8Bit(file, ei->access_direction);
7158 putFile8Bit(file, ei->explosion_delay);
7159 putFile8Bit(file, ei->ignition_delay);
7160 putFile8Bit(file, ei->explosion_type);
7162 // some free bytes for future custom property values and padding
7163 WriteUnusedBytesToFile(file, 1);
7165 // ---------- change page property values (48 bytes) ------------------------
7167 for (i = 0; i < ei->num_change_pages; i++)
7169 struct ElementChangeInfo *change = &ei->change_page[i];
7170 unsigned int event_bits;
7172 // bits 0 - 31 of "has_event[]" ...
7174 for (j = 0; j < MIN(NUM_CHANGE_EVENTS, 32); j++)
7175 if (change->has_event[j])
7176 event_bits |= (1 << j);
7177 putFile32BitBE(file, event_bits);
7179 putFile16BitBE(file, change->target_element);
7181 putFile16BitBE(file, change->delay_fixed);
7182 putFile16BitBE(file, change->delay_random);
7183 putFile16BitBE(file, change->delay_frames);
7185 putFile16BitBE(file, change->initial_trigger_element);
7187 putFile8Bit(file, change->explode);
7188 putFile8Bit(file, change->use_target_content);
7189 putFile8Bit(file, change->only_if_complete);
7190 putFile8Bit(file, change->use_random_replace);
7192 putFile8Bit(file, change->random_percentage);
7193 putFile8Bit(file, change->replace_when);
7195 for (y = 0; y < 3; y++)
7196 for (x = 0; x < 3; x++)
7197 putFile16BitBE(file, change->target_content.e[x][y]);
7199 putFile8Bit(file, change->can_change);
7201 putFile8Bit(file, change->trigger_side);
7203 putFile8Bit(file, change->trigger_player);
7204 putFile8Bit(file, (change->trigger_page == CH_PAGE_ANY ? CH_PAGE_ANY_FILE :
7205 log_2(change->trigger_page)));
7207 putFile8Bit(file, change->has_action);
7208 putFile8Bit(file, change->action_type);
7209 putFile8Bit(file, change->action_mode);
7210 putFile16BitBE(file, change->action_arg);
7212 // ... bits 32 - 39 of "has_event[]" (not nice, but downward compatible)
7214 for (j = 32; j < NUM_CHANGE_EVENTS; j++)
7215 if (change->has_event[j])
7216 event_bits |= (1 << (j - 32));
7217 putFile8Bit(file, event_bits);
7222 #if ENABLE_HISTORIC_CHUNKS
7223 static void SaveLevel_GRP1(FILE *file, struct LevelInfo *level, int element)
7225 struct ElementInfo *ei = &element_info[element];
7226 struct ElementGroupInfo *group = ei->group;
7229 putFile16BitBE(file, element);
7231 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
7232 putFile8Bit(file, ei->description[i]);
7234 putFile8Bit(file, group->num_elements);
7236 putFile8Bit(file, ei->use_gfx_element);
7237 putFile16BitBE(file, ei->gfx_element_initial);
7239 putFile8Bit(file, group->choice_mode);
7241 // some free bytes for future values and padding
7242 WriteUnusedBytesToFile(file, 3);
7244 for (i = 0; i < MAX_ELEMENTS_IN_GROUP; i++)
7245 putFile16BitBE(file, group->element[i]);
7249 static int SaveLevel_MicroChunk(FILE *file, struct LevelFileConfigInfo *entry,
7250 boolean write_element)
7252 int save_type = entry->save_type;
7253 int data_type = entry->data_type;
7254 int conf_type = entry->conf_type;
7255 int byte_mask = conf_type & CONF_MASK_BYTES;
7256 int element = entry->element;
7257 int default_value = entry->default_value;
7259 boolean modified = FALSE;
7261 if (byte_mask != CONF_MASK_MULTI_BYTES)
7263 void *value_ptr = entry->value;
7264 int value = (data_type == TYPE_BOOLEAN ? *(boolean *)value_ptr :
7267 // check if any settings have been modified before saving them
7268 if (value != default_value)
7271 // do not save if explicitly told or if unmodified default settings
7272 if ((save_type == SAVE_CONF_NEVER) ||
7273 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
7277 num_bytes += putFile16BitBE(file, element);
7279 num_bytes += putFile8Bit(file, conf_type);
7280 num_bytes += (byte_mask == CONF_MASK_1_BYTE ? putFile8Bit (file, value) :
7281 byte_mask == CONF_MASK_2_BYTE ? putFile16BitBE(file, value) :
7282 byte_mask == CONF_MASK_4_BYTE ? putFile32BitBE(file, value) :
7285 else if (data_type == TYPE_STRING)
7287 char *default_string = entry->default_string;
7288 char *string = (char *)(entry->value);
7289 int string_length = strlen(string);
7292 // check if any settings have been modified before saving them
7293 if (!strEqual(string, default_string))
7296 // do not save if explicitly told or if unmodified default settings
7297 if ((save_type == SAVE_CONF_NEVER) ||
7298 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
7302 num_bytes += putFile16BitBE(file, element);
7304 num_bytes += putFile8Bit(file, conf_type);
7305 num_bytes += putFile16BitBE(file, string_length);
7307 for (i = 0; i < string_length; i++)
7308 num_bytes += putFile8Bit(file, string[i]);
7310 else if (data_type == TYPE_ELEMENT_LIST)
7312 int *element_array = (int *)(entry->value);
7313 int num_elements = *(int *)(entry->num_entities);
7316 // check if any settings have been modified before saving them
7317 for (i = 0; i < num_elements; i++)
7318 if (element_array[i] != default_value)
7321 // do not save if explicitly told or if unmodified default settings
7322 if ((save_type == SAVE_CONF_NEVER) ||
7323 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
7327 num_bytes += putFile16BitBE(file, element);
7329 num_bytes += putFile8Bit(file, conf_type);
7330 num_bytes += putFile16BitBE(file, num_elements * CONF_ELEMENT_NUM_BYTES);
7332 for (i = 0; i < num_elements; i++)
7333 num_bytes += putFile16BitBE(file, element_array[i]);
7335 else if (data_type == TYPE_CONTENT_LIST)
7337 struct Content *content = (struct Content *)(entry->value);
7338 int num_contents = *(int *)(entry->num_entities);
7341 // check if any settings have been modified before saving them
7342 for (i = 0; i < num_contents; i++)
7343 for (y = 0; y < 3; y++)
7344 for (x = 0; x < 3; x++)
7345 if (content[i].e[x][y] != default_value)
7348 // do not save if explicitly told or if unmodified default settings
7349 if ((save_type == SAVE_CONF_NEVER) ||
7350 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
7354 num_bytes += putFile16BitBE(file, element);
7356 num_bytes += putFile8Bit(file, conf_type);
7357 num_bytes += putFile16BitBE(file, num_contents * CONF_CONTENT_NUM_BYTES);
7359 for (i = 0; i < num_contents; i++)
7360 for (y = 0; y < 3; y++)
7361 for (x = 0; x < 3; x++)
7362 num_bytes += putFile16BitBE(file, content[i].e[x][y]);
7368 static int SaveLevel_INFO(FILE *file, struct LevelInfo *level)
7373 li = *level; // copy level data into temporary buffer
7375 for (i = 0; chunk_config_INFO[i].data_type != -1; i++)
7376 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_INFO[i], FALSE);
7381 static int SaveLevel_ELEM(FILE *file, struct LevelInfo *level)
7386 li = *level; // copy level data into temporary buffer
7388 for (i = 0; chunk_config_ELEM[i].data_type != -1; i++)
7389 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_ELEM[i], TRUE);
7394 static int SaveLevel_NOTE(FILE *file, struct LevelInfo *level, int element)
7396 int envelope_nr = element - EL_ENVELOPE_1;
7400 chunk_size += putFile16BitBE(file, element);
7402 // copy envelope data into temporary buffer
7403 xx_envelope = level->envelope[envelope_nr];
7405 for (i = 0; chunk_config_NOTE[i].data_type != -1; i++)
7406 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_NOTE[i], FALSE);
7411 static int SaveLevel_CUSX(FILE *file, struct LevelInfo *level, int element)
7413 struct ElementInfo *ei = &element_info[element];
7417 chunk_size += putFile16BitBE(file, element);
7419 xx_ei = *ei; // copy element data into temporary buffer
7421 // set default description string for this specific element
7422 strcpy(xx_default_description, getDefaultElementDescription(ei));
7424 for (i = 0; chunk_config_CUSX_base[i].data_type != -1; i++)
7425 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_CUSX_base[i], FALSE);
7427 for (i = 0; i < ei->num_change_pages; i++)
7429 struct ElementChangeInfo *change = &ei->change_page[i];
7431 xx_current_change_page = i;
7433 xx_change = *change; // copy change data into temporary buffer
7436 setEventBitsFromEventFlags(change);
7438 for (j = 0; chunk_config_CUSX_change[j].data_type != -1; j++)
7439 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_CUSX_change[j],
7446 static int SaveLevel_GRPX(FILE *file, struct LevelInfo *level, int element)
7448 struct ElementInfo *ei = &element_info[element];
7449 struct ElementGroupInfo *group = ei->group;
7453 chunk_size += putFile16BitBE(file, element);
7455 xx_ei = *ei; // copy element data into temporary buffer
7456 xx_group = *group; // copy group data into temporary buffer
7458 // set default description string for this specific element
7459 strcpy(xx_default_description, getDefaultElementDescription(ei));
7461 for (i = 0; chunk_config_GRPX[i].data_type != -1; i++)
7462 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_GRPX[i], FALSE);
7467 static void SaveLevelFromFilename(struct LevelInfo *level, char *filename,
7468 boolean save_as_template)
7474 if (!(file = fopen(filename, MODE_WRITE)))
7476 Warn("cannot save level file '%s'", filename);
7481 level->file_version = FILE_VERSION_ACTUAL;
7482 level->game_version = GAME_VERSION_ACTUAL;
7484 level->creation_date = getCurrentDate();
7486 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
7487 putFileChunkBE(file, "CAVE", CHUNK_SIZE_NONE);
7489 chunk_size = SaveLevel_VERS(NULL, level);
7490 putFileChunkBE(file, "VERS", chunk_size);
7491 SaveLevel_VERS(file, level);
7493 chunk_size = SaveLevel_DATE(NULL, level);
7494 putFileChunkBE(file, "DATE", chunk_size);
7495 SaveLevel_DATE(file, level);
7497 chunk_size = SaveLevel_NAME(NULL, level);
7498 putFileChunkBE(file, "NAME", chunk_size);
7499 SaveLevel_NAME(file, level);
7501 chunk_size = SaveLevel_AUTH(NULL, level);
7502 putFileChunkBE(file, "AUTH", chunk_size);
7503 SaveLevel_AUTH(file, level);
7505 chunk_size = SaveLevel_INFO(NULL, level);
7506 putFileChunkBE(file, "INFO", chunk_size);
7507 SaveLevel_INFO(file, level);
7509 chunk_size = SaveLevel_BODY(NULL, level);
7510 putFileChunkBE(file, "BODY", chunk_size);
7511 SaveLevel_BODY(file, level);
7513 chunk_size = SaveLevel_ELEM(NULL, level);
7514 if (chunk_size > LEVEL_CHUNK_ELEM_UNCHANGED) // save if changed
7516 putFileChunkBE(file, "ELEM", chunk_size);
7517 SaveLevel_ELEM(file, level);
7520 for (i = 0; i < NUM_ENVELOPES; i++)
7522 int element = EL_ENVELOPE_1 + i;
7524 chunk_size = SaveLevel_NOTE(NULL, level, element);
7525 if (chunk_size > LEVEL_CHUNK_NOTE_UNCHANGED) // save if changed
7527 putFileChunkBE(file, "NOTE", chunk_size);
7528 SaveLevel_NOTE(file, level, element);
7532 // if not using template level, check for non-default custom/group elements
7533 if (!level->use_custom_template || save_as_template)
7535 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7537 int element = EL_CUSTOM_START + i;
7539 chunk_size = SaveLevel_CUSX(NULL, level, element);
7540 if (chunk_size > LEVEL_CHUNK_CUSX_UNCHANGED) // save if changed
7542 putFileChunkBE(file, "CUSX", chunk_size);
7543 SaveLevel_CUSX(file, level, element);
7547 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
7549 int element = EL_GROUP_START + i;
7551 chunk_size = SaveLevel_GRPX(NULL, level, element);
7552 if (chunk_size > LEVEL_CHUNK_GRPX_UNCHANGED) // save if changed
7554 putFileChunkBE(file, "GRPX", chunk_size);
7555 SaveLevel_GRPX(file, level, element);
7562 SetFilePermissions(filename, PERMS_PRIVATE);
7565 void SaveLevel(int nr)
7567 char *filename = getDefaultLevelFilename(nr);
7569 SaveLevelFromFilename(&level, filename, FALSE);
7572 void SaveLevelTemplate(void)
7574 char *filename = getLocalLevelTemplateFilename();
7576 SaveLevelFromFilename(&level, filename, TRUE);
7579 boolean SaveLevelChecked(int nr)
7581 char *filename = getDefaultLevelFilename(nr);
7582 boolean new_level = !fileExists(filename);
7583 boolean level_saved = FALSE;
7585 if (new_level || Request("Save this level and kill the old?", REQ_ASK))
7590 Request("Level saved!", REQ_CONFIRM);
7598 void DumpLevel(struct LevelInfo *level)
7600 if (level->no_level_file || level->no_valid_file)
7602 Warn("cannot dump -- no valid level file found");
7608 Print("Level xxx (file version %08d, game version %08d)\n",
7609 level->file_version, level->game_version);
7612 Print("Level author: '%s'\n", level->author);
7613 Print("Level title: '%s'\n", level->name);
7615 Print("Playfield size: %d x %d\n", level->fieldx, level->fieldy);
7617 Print("Level time: %d seconds\n", level->time);
7618 Print("Gems needed: %d\n", level->gems_needed);
7620 Print("Time for magic wall: %d seconds\n", level->time_magic_wall);
7621 Print("Time for wheel: %d seconds\n", level->time_wheel);
7622 Print("Time for light: %d seconds\n", level->time_light);
7623 Print("Time for timegate: %d seconds\n", level->time_timegate);
7625 Print("Amoeba speed: %d\n", level->amoeba_speed);
7628 Print("EM style slippery gems: %s\n", (level->em_slippery_gems ? "yes" : "no"));
7629 Print("Player blocks last field: %s\n", (level->block_last_field ? "yes" : "no"));
7630 Print("SP player blocks last field: %s\n", (level->sp_block_last_field ? "yes" : "no"));
7631 Print("use spring bug: %s\n", (level->use_spring_bug ? "yes" : "no"));
7632 Print("use step counter: %s\n", (level->use_step_counter ? "yes" : "no"));
7638 // ============================================================================
7639 // tape file functions
7640 // ============================================================================
7642 static void setTapeInfoToDefaults(void)
7646 // always start with reliable default values (empty tape)
7649 // default values (also for pre-1.2 tapes) with only the first player
7650 tape.player_participates[0] = TRUE;
7651 for (i = 1; i < MAX_PLAYERS; i++)
7652 tape.player_participates[i] = FALSE;
7654 // at least one (default: the first) player participates in every tape
7655 tape.num_participating_players = 1;
7657 tape.property_bits = TAPE_PROPERTY_NONE;
7659 tape.level_nr = level_nr;
7661 tape.changed = FALSE;
7663 tape.recording = FALSE;
7664 tape.playing = FALSE;
7665 tape.pausing = FALSE;
7667 tape.scr_fieldx = SCR_FIELDX_DEFAULT;
7668 tape.scr_fieldy = SCR_FIELDY_DEFAULT;
7670 tape.no_valid_file = FALSE;
7673 static int getTapePosSize(struct TapeInfo *tape)
7675 int tape_pos_size = 0;
7677 if (tape->use_key_actions)
7678 tape_pos_size += tape->num_participating_players;
7680 if (tape->use_mouse_actions)
7681 tape_pos_size += 3; // x and y position and mouse button mask
7683 tape_pos_size += 1; // tape action delay value
7685 return tape_pos_size;
7688 static void setTapeActionFlags(struct TapeInfo *tape, int value)
7690 tape->use_key_actions = FALSE;
7691 tape->use_mouse_actions = FALSE;
7693 if (value != TAPE_USE_MOUSE_ACTIONS_ONLY)
7694 tape->use_key_actions = TRUE;
7696 if (value != TAPE_USE_KEY_ACTIONS_ONLY)
7697 tape->use_mouse_actions = TRUE;
7700 static int getTapeActionValue(struct TapeInfo *tape)
7702 return (tape->use_key_actions &&
7703 tape->use_mouse_actions ? TAPE_USE_KEY_AND_MOUSE_ACTIONS :
7704 tape->use_key_actions ? TAPE_USE_KEY_ACTIONS_ONLY :
7705 tape->use_mouse_actions ? TAPE_USE_MOUSE_ACTIONS_ONLY :
7706 TAPE_ACTIONS_DEFAULT);
7709 static int LoadTape_VERS(File *file, int chunk_size, struct TapeInfo *tape)
7711 tape->file_version = getFileVersion(file);
7712 tape->game_version = getFileVersion(file);
7717 static int LoadTape_HEAD(File *file, int chunk_size, struct TapeInfo *tape)
7721 tape->random_seed = getFile32BitBE(file);
7722 tape->date = getFile32BitBE(file);
7723 tape->length = getFile32BitBE(file);
7725 // read header fields that are new since version 1.2
7726 if (tape->file_version >= FILE_VERSION_1_2)
7728 byte store_participating_players = getFile8Bit(file);
7731 // since version 1.2, tapes store which players participate in the tape
7732 tape->num_participating_players = 0;
7733 for (i = 0; i < MAX_PLAYERS; i++)
7735 tape->player_participates[i] = FALSE;
7737 if (store_participating_players & (1 << i))
7739 tape->player_participates[i] = TRUE;
7740 tape->num_participating_players++;
7744 setTapeActionFlags(tape, getFile8Bit(file));
7746 tape->property_bits = getFile8Bit(file);
7748 ReadUnusedBytesFromFile(file, TAPE_CHUNK_HEAD_UNUSED);
7750 engine_version = getFileVersion(file);
7751 if (engine_version > 0)
7752 tape->engine_version = engine_version;
7754 tape->engine_version = tape->game_version;
7760 static int LoadTape_SCRN(File *file, int chunk_size, struct TapeInfo *tape)
7762 tape->scr_fieldx = getFile8Bit(file);
7763 tape->scr_fieldy = getFile8Bit(file);
7768 static int LoadTape_INFO(File *file, int chunk_size, struct TapeInfo *tape)
7770 char *level_identifier = NULL;
7771 int level_identifier_size;
7774 level_identifier_size = getFile16BitBE(file);
7776 level_identifier = checked_malloc(level_identifier_size);
7778 for (i = 0; i < level_identifier_size; i++)
7779 level_identifier[i] = getFile8Bit(file);
7781 strncpy(tape->level_identifier, level_identifier, MAX_FILENAME_LEN);
7782 tape->level_identifier[MAX_FILENAME_LEN] = '\0';
7784 checked_free(level_identifier);
7786 tape->level_nr = getFile16BitBE(file);
7788 chunk_size = 2 + level_identifier_size + 2;
7793 static int LoadTape_BODY(File *file, int chunk_size, struct TapeInfo *tape)
7796 int tape_pos_size = getTapePosSize(tape);
7797 int chunk_size_expected = tape_pos_size * tape->length;
7799 if (chunk_size_expected != chunk_size)
7801 ReadUnusedBytesFromFile(file, chunk_size);
7802 return chunk_size_expected;
7805 for (i = 0; i < tape->length; i++)
7807 if (i >= MAX_TAPE_LEN)
7809 Warn("tape truncated -- size exceeds maximum tape size %d",
7812 // tape too large; read and ignore remaining tape data from this chunk
7813 for (;i < tape->length; i++)
7814 ReadUnusedBytesFromFile(file, tape_pos_size);
7819 if (tape->use_key_actions)
7821 for (j = 0; j < MAX_PLAYERS; j++)
7823 tape->pos[i].action[j] = MV_NONE;
7825 if (tape->player_participates[j])
7826 tape->pos[i].action[j] = getFile8Bit(file);
7830 if (tape->use_mouse_actions)
7832 tape->pos[i].action[TAPE_ACTION_LX] = getFile8Bit(file);
7833 tape->pos[i].action[TAPE_ACTION_LY] = getFile8Bit(file);
7834 tape->pos[i].action[TAPE_ACTION_BUTTON] = getFile8Bit(file);
7837 tape->pos[i].delay = getFile8Bit(file);
7839 if (tape->file_version == FILE_VERSION_1_0)
7841 // eliminate possible diagonal moves in old tapes
7842 // this is only for backward compatibility
7844 byte joy_dir[4] = { JOY_LEFT, JOY_RIGHT, JOY_UP, JOY_DOWN };
7845 byte action = tape->pos[i].action[0];
7846 int k, num_moves = 0;
7848 for (k = 0; k<4; k++)
7850 if (action & joy_dir[k])
7852 tape->pos[i + num_moves].action[0] = joy_dir[k];
7854 tape->pos[i + num_moves].delay = 0;
7863 tape->length += num_moves;
7866 else if (tape->file_version < FILE_VERSION_2_0)
7868 // convert pre-2.0 tapes to new tape format
7870 if (tape->pos[i].delay > 1)
7873 tape->pos[i + 1] = tape->pos[i];
7874 tape->pos[i + 1].delay = 1;
7877 for (j = 0; j < MAX_PLAYERS; j++)
7878 tape->pos[i].action[j] = MV_NONE;
7879 tape->pos[i].delay--;
7886 if (checkEndOfFile(file))
7890 if (i != tape->length)
7891 chunk_size = tape_pos_size * i;
7896 static void LoadTape_SokobanSolution(char *filename)
7899 int move_delay = TILESIZE / level.initial_player_stepsize[0];
7901 if (!(file = openFile(filename, MODE_READ)))
7903 tape.no_valid_file = TRUE;
7908 while (!checkEndOfFile(file))
7910 unsigned char c = getByteFromFile(file);
7912 if (checkEndOfFile(file))
7919 tape.pos[tape.length].action[0] = MV_UP;
7920 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
7926 tape.pos[tape.length].action[0] = MV_DOWN;
7927 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
7933 tape.pos[tape.length].action[0] = MV_LEFT;
7934 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
7940 tape.pos[tape.length].action[0] = MV_RIGHT;
7941 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
7949 // ignore white-space characters
7953 tape.no_valid_file = TRUE;
7955 Warn("unsupported Sokoban solution file '%s' ['%d']", filename, c);
7963 if (tape.no_valid_file)
7966 tape.length_frames = GetTapeLengthFrames();
7967 tape.length_seconds = GetTapeLengthSeconds();
7970 void LoadTapeFromFilename(char *filename)
7972 char cookie[MAX_LINE_LEN];
7973 char chunk_name[CHUNK_ID_LEN + 1];
7977 // always start with reliable default values
7978 setTapeInfoToDefaults();
7980 if (strSuffix(filename, ".sln"))
7982 LoadTape_SokobanSolution(filename);
7987 if (!(file = openFile(filename, MODE_READ)))
7989 tape.no_valid_file = TRUE;
7994 getFileChunkBE(file, chunk_name, NULL);
7995 if (strEqual(chunk_name, "RND1"))
7997 getFile32BitBE(file); // not used
7999 getFileChunkBE(file, chunk_name, NULL);
8000 if (!strEqual(chunk_name, "TAPE"))
8002 tape.no_valid_file = TRUE;
8004 Warn("unknown format of tape file '%s'", filename);
8011 else // check for pre-2.0 file format with cookie string
8013 strcpy(cookie, chunk_name);
8014 if (getStringFromFile(file, &cookie[4], MAX_LINE_LEN - 4) == NULL)
8016 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
8017 cookie[strlen(cookie) - 1] = '\0';
8019 if (!checkCookieString(cookie, TAPE_COOKIE_TMPL))
8021 tape.no_valid_file = TRUE;
8023 Warn("unknown format of tape file '%s'", filename);
8030 if ((tape.file_version = getFileVersionFromCookieString(cookie)) == -1)
8032 tape.no_valid_file = TRUE;
8034 Warn("unsupported version of tape file '%s'", filename);
8041 // pre-2.0 tape files have no game version, so use file version here
8042 tape.game_version = tape.file_version;
8045 if (tape.file_version < FILE_VERSION_1_2)
8047 // tape files from versions before 1.2.0 without chunk structure
8048 LoadTape_HEAD(file, TAPE_CHUNK_HEAD_SIZE, &tape);
8049 LoadTape_BODY(file, 2 * tape.length, &tape);
8057 int (*loader)(File *, int, struct TapeInfo *);
8061 { "VERS", TAPE_CHUNK_VERS_SIZE, LoadTape_VERS },
8062 { "HEAD", TAPE_CHUNK_HEAD_SIZE, LoadTape_HEAD },
8063 { "SCRN", TAPE_CHUNK_SCRN_SIZE, LoadTape_SCRN },
8064 { "INFO", -1, LoadTape_INFO },
8065 { "BODY", -1, LoadTape_BODY },
8069 while (getFileChunkBE(file, chunk_name, &chunk_size))
8073 while (chunk_info[i].name != NULL &&
8074 !strEqual(chunk_name, chunk_info[i].name))
8077 if (chunk_info[i].name == NULL)
8079 Warn("unknown chunk '%s' in tape file '%s'",
8080 chunk_name, filename);
8082 ReadUnusedBytesFromFile(file, chunk_size);
8084 else if (chunk_info[i].size != -1 &&
8085 chunk_info[i].size != chunk_size)
8087 Warn("wrong size (%d) of chunk '%s' in tape file '%s'",
8088 chunk_size, chunk_name, filename);
8090 ReadUnusedBytesFromFile(file, chunk_size);
8094 // call function to load this tape chunk
8095 int chunk_size_expected =
8096 (chunk_info[i].loader)(file, chunk_size, &tape);
8098 // the size of some chunks cannot be checked before reading other
8099 // chunks first (like "HEAD" and "BODY") that contain some header
8100 // information, so check them here
8101 if (chunk_size_expected != chunk_size)
8103 Warn("wrong size (%d) of chunk '%s' in tape file '%s'",
8104 chunk_size, chunk_name, filename);
8112 tape.length_frames = GetTapeLengthFrames();
8113 tape.length_seconds = GetTapeLengthSeconds();
8116 Debug("files:LoadTapeFromFilename", "tape file version: %d",
8118 Debug("files:LoadTapeFromFilename", "tape game version: %d",
8120 Debug("files:LoadTapeFromFilename", "tape engine version: %d",
8121 tape.engine_version);
8125 void LoadTape(int nr)
8127 char *filename = getTapeFilename(nr);
8129 LoadTapeFromFilename(filename);
8132 void LoadSolutionTape(int nr)
8134 char *filename = getSolutionTapeFilename(nr);
8136 LoadTapeFromFilename(filename);
8138 if (TAPE_IS_EMPTY(tape) &&
8139 level.game_engine_type == GAME_ENGINE_TYPE_SP &&
8140 level.native_sp_level->demo.is_available)
8141 CopyNativeTape_SP_to_RND(&level);
8144 static boolean checkSaveTape_SCRN(struct TapeInfo *tape)
8146 // chunk required for team mode tapes with non-default screen size
8147 return (tape->num_participating_players > 1 &&
8148 (tape->scr_fieldx != SCR_FIELDX_DEFAULT ||
8149 tape->scr_fieldy != SCR_FIELDY_DEFAULT));
8152 static void SaveTape_VERS(FILE *file, struct TapeInfo *tape)
8154 putFileVersion(file, tape->file_version);
8155 putFileVersion(file, tape->game_version);
8158 static void SaveTape_HEAD(FILE *file, struct TapeInfo *tape)
8161 byte store_participating_players = 0;
8163 // set bits for participating players for compact storage
8164 for (i = 0; i < MAX_PLAYERS; i++)
8165 if (tape->player_participates[i])
8166 store_participating_players |= (1 << i);
8168 putFile32BitBE(file, tape->random_seed);
8169 putFile32BitBE(file, tape->date);
8170 putFile32BitBE(file, tape->length);
8172 putFile8Bit(file, store_participating_players);
8174 putFile8Bit(file, getTapeActionValue(tape));
8176 putFile8Bit(file, tape->property_bits);
8178 // unused bytes not at the end here for 4-byte alignment of engine_version
8179 WriteUnusedBytesToFile(file, TAPE_CHUNK_HEAD_UNUSED);
8181 putFileVersion(file, tape->engine_version);
8184 static void SaveTape_SCRN(FILE *file, struct TapeInfo *tape)
8186 putFile8Bit(file, tape->scr_fieldx);
8187 putFile8Bit(file, tape->scr_fieldy);
8190 static void SaveTape_INFO(FILE *file, struct TapeInfo *tape)
8192 int level_identifier_size = strlen(tape->level_identifier) + 1;
8195 putFile16BitBE(file, level_identifier_size);
8197 for (i = 0; i < level_identifier_size; i++)
8198 putFile8Bit(file, tape->level_identifier[i]);
8200 putFile16BitBE(file, tape->level_nr);
8203 static void SaveTape_BODY(FILE *file, struct TapeInfo *tape)
8207 for (i = 0; i < tape->length; i++)
8209 if (tape->use_key_actions)
8211 for (j = 0; j < MAX_PLAYERS; j++)
8212 if (tape->player_participates[j])
8213 putFile8Bit(file, tape->pos[i].action[j]);
8216 if (tape->use_mouse_actions)
8218 putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_LX]);
8219 putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_LY]);
8220 putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_BUTTON]);
8223 putFile8Bit(file, tape->pos[i].delay);
8227 void SaveTapeToFilename(char *filename)
8231 int info_chunk_size;
8232 int body_chunk_size;
8234 if (!(file = fopen(filename, MODE_WRITE)))
8236 Warn("cannot save level recording file '%s'", filename);
8241 tape_pos_size = getTapePosSize(&tape);
8243 info_chunk_size = 2 + (strlen(tape.level_identifier) + 1) + 2;
8244 body_chunk_size = tape_pos_size * tape.length;
8246 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
8247 putFileChunkBE(file, "TAPE", CHUNK_SIZE_NONE);
8249 putFileChunkBE(file, "VERS", TAPE_CHUNK_VERS_SIZE);
8250 SaveTape_VERS(file, &tape);
8252 putFileChunkBE(file, "HEAD", TAPE_CHUNK_HEAD_SIZE);
8253 SaveTape_HEAD(file, &tape);
8255 if (checkSaveTape_SCRN(&tape))
8257 putFileChunkBE(file, "SCRN", TAPE_CHUNK_SCRN_SIZE);
8258 SaveTape_SCRN(file, &tape);
8261 putFileChunkBE(file, "INFO", info_chunk_size);
8262 SaveTape_INFO(file, &tape);
8264 putFileChunkBE(file, "BODY", body_chunk_size);
8265 SaveTape_BODY(file, &tape);
8269 SetFilePermissions(filename, PERMS_PRIVATE);
8272 void SaveTape(int nr)
8274 char *filename = getTapeFilename(nr);
8277 InitTapeDirectory(leveldir_current->subdir);
8279 tape.file_version = FILE_VERSION_ACTUAL;
8280 tape.game_version = GAME_VERSION_ACTUAL;
8282 tape.num_participating_players = 0;
8284 // count number of participating players
8285 for (i = 0; i < MAX_PLAYERS; i++)
8286 if (tape.player_participates[i])
8287 tape.num_participating_players++;
8289 SaveTapeToFilename(filename);
8291 tape.changed = FALSE;
8294 static boolean SaveTapeCheckedExt(int nr, char *msg_replace, char *msg_saved,
8295 unsigned int req_state_added)
8297 char *filename = getTapeFilename(nr);
8298 boolean new_tape = !fileExists(filename);
8299 boolean tape_saved = FALSE;
8301 if (new_tape || Request(msg_replace, REQ_ASK | req_state_added))
8306 Request(msg_saved, REQ_CONFIRM | req_state_added);
8314 boolean SaveTapeChecked(int nr)
8316 return SaveTapeCheckedExt(nr, "Replace old tape?", "Tape saved!", 0);
8319 boolean SaveTapeChecked_LevelSolved(int nr)
8321 return SaveTapeCheckedExt(nr, "Level solved! Replace old tape?",
8322 "Level solved! Tape saved!", REQ_STAY_OPEN);
8325 void DumpTape(struct TapeInfo *tape)
8327 int tape_frame_counter;
8330 if (tape->no_valid_file)
8332 Warn("cannot dump -- no valid tape file found");
8338 Print("Tape of Level %03d (file version %08d, game version %08d)\n",
8339 tape->level_nr, tape->file_version, tape->game_version);
8340 Print(" (effective engine version %08d)\n",
8341 tape->engine_version);
8342 Print("Level series identifier: '%s'\n", tape->level_identifier);
8345 tape_frame_counter = 0;
8347 for (i = 0; i < tape->length; i++)
8349 if (i >= MAX_TAPE_LEN)
8354 for (j = 0; j < MAX_PLAYERS; j++)
8356 if (tape->player_participates[j])
8358 int action = tape->pos[i].action[j];
8360 Print("%d:%02x ", j, action);
8361 Print("[%c%c%c%c|%c%c] - ",
8362 (action & JOY_LEFT ? '<' : ' '),
8363 (action & JOY_RIGHT ? '>' : ' '),
8364 (action & JOY_UP ? '^' : ' '),
8365 (action & JOY_DOWN ? 'v' : ' '),
8366 (action & JOY_BUTTON_1 ? '1' : ' '),
8367 (action & JOY_BUTTON_2 ? '2' : ' '));
8371 Print("(%03d) ", tape->pos[i].delay);
8372 Print("[%05d]\n", tape_frame_counter);
8374 tape_frame_counter += tape->pos[i].delay;
8381 // ============================================================================
8382 // score file functions
8383 // ============================================================================
8385 static void setScoreInfoToDefaults(void)
8389 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
8391 strcpy(scores.entry[i].name, EMPTY_PLAYER_NAME);
8392 scores.entry[i].score = 0;
8393 scores.entry[i].time = 0;
8397 static void LoadScore_OLD(int nr)
8400 char *filename = getScoreFilename(nr);
8401 char cookie[MAX_LINE_LEN];
8402 char line[MAX_LINE_LEN];
8406 if (!(file = fopen(filename, MODE_READ)))
8409 // check file identifier
8410 if (fgets(cookie, MAX_LINE_LEN, file) == NULL)
8412 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
8413 cookie[strlen(cookie) - 1] = '\0';
8415 if (!checkCookieString(cookie, SCORE_COOKIE_TMPL))
8417 Warn("unknown format of score file '%s'", filename);
8424 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
8426 if (fscanf(file, "%d", &scores.entry[i].score) == EOF)
8427 Warn("fscanf() failed; %s", strerror(errno));
8429 if (fgets(line, MAX_LINE_LEN, file) == NULL)
8432 if (strlen(line) > 0 && line[strlen(line) - 1] == '\n')
8433 line[strlen(line) - 1] = '\0';
8435 for (line_ptr = line; *line_ptr; line_ptr++)
8437 if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != '\0')
8439 strncpy(scores.entry[i].name, line_ptr, MAX_PLAYER_NAME_LEN);
8440 scores.entry[i].name[MAX_PLAYER_NAME_LEN] = '\0';
8449 static int LoadScore_VERS(File *file, int chunk_size, struct ScoreInfo *scores)
8451 scores->file_version = getFileVersion(file);
8452 scores->game_version = getFileVersion(file);
8457 static int LoadScore_INFO(File *file, int chunk_size, struct ScoreInfo *scores)
8459 char *level_identifier = NULL;
8460 int level_identifier_size;
8463 level_identifier_size = getFile16BitBE(file);
8465 level_identifier = checked_malloc(level_identifier_size);
8467 for (i = 0; i < level_identifier_size; i++)
8468 level_identifier[i] = getFile8Bit(file);
8470 strncpy(scores->level_identifier, level_identifier, MAX_FILENAME_LEN);
8471 scores->level_identifier[MAX_FILENAME_LEN] = '\0';
8473 checked_free(level_identifier);
8475 scores->level_nr = getFile16BitBE(file);
8476 scores->num_entries = getFile16BitBE(file);
8478 chunk_size = 2 + level_identifier_size + 2 + 2;
8483 static int LoadScore_NAME(File *file, int chunk_size, struct ScoreInfo *scores)
8487 for (i = 0; i < scores->num_entries; i++)
8489 for (j = 0; j < MAX_PLAYER_NAME_LEN; j++)
8490 scores->entry[i].name[j] = getFile8Bit(file);
8492 scores->entry[i].name[MAX_PLAYER_NAME_LEN] = '\0';
8495 chunk_size = scores->num_entries * MAX_PLAYER_NAME_LEN;
8500 static int LoadScore_SCOR(File *file, int chunk_size, struct ScoreInfo *scores)
8504 for (i = 0; i < scores->num_entries; i++)
8505 scores->entry[i].score = getFile16BitBE(file);
8507 chunk_size = scores->num_entries * 2;
8512 static int LoadScore_TIME(File *file, int chunk_size, struct ScoreInfo *scores)
8516 for (i = 0; i < scores->num_entries; i++)
8517 scores->entry[i].time = getFile32BitBE(file);
8519 chunk_size = scores->num_entries * 4;
8524 void LoadScore(int nr)
8526 char *filename = getScoreFilename(nr);
8527 char cookie[MAX_LINE_LEN];
8528 char chunk_name[CHUNK_ID_LEN + 1];
8530 boolean old_score_file_format = FALSE;
8533 // always start with reliable default values
8534 setScoreInfoToDefaults();
8536 if (!(file = openFile(filename, MODE_READ)))
8539 getFileChunkBE(file, chunk_name, NULL);
8540 if (strEqual(chunk_name, "RND1"))
8542 getFile32BitBE(file); // not used
8544 getFileChunkBE(file, chunk_name, NULL);
8545 if (!strEqual(chunk_name, "SCOR"))
8547 Warn("unknown format of score file '%s'", filename);
8554 else // check for old file format with cookie string
8556 strcpy(cookie, chunk_name);
8557 if (getStringFromFile(file, &cookie[4], MAX_LINE_LEN - 4) == NULL)
8559 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
8560 cookie[strlen(cookie) - 1] = '\0';
8562 if (!checkCookieString(cookie, SCORE_COOKIE_TMPL))
8564 Warn("unknown format of score file '%s'", filename);
8571 old_score_file_format = TRUE;
8574 if (old_score_file_format)
8576 // score files from versions before 4.2.4.0 without chunk structure
8585 int (*loader)(File *, int, struct ScoreInfo *);
8589 { "VERS", SCORE_CHUNK_VERS_SIZE, LoadScore_VERS },
8590 { "INFO", -1, LoadScore_INFO },
8591 { "NAME", -1, LoadScore_NAME },
8592 { "SCOR", -1, LoadScore_SCOR },
8593 { "TIME", -1, LoadScore_TIME },
8598 while (getFileChunkBE(file, chunk_name, &chunk_size))
8602 while (chunk_info[i].name != NULL &&
8603 !strEqual(chunk_name, chunk_info[i].name))
8606 if (chunk_info[i].name == NULL)
8608 Warn("unknown chunk '%s' in score file '%s'",
8609 chunk_name, filename);
8611 ReadUnusedBytesFromFile(file, chunk_size);
8613 else if (chunk_info[i].size != -1 &&
8614 chunk_info[i].size != chunk_size)
8616 Warn("wrong size (%d) of chunk '%s' in score file '%s'",
8617 chunk_size, chunk_name, filename);
8619 ReadUnusedBytesFromFile(file, chunk_size);
8623 // call function to load this score chunk
8624 int chunk_size_expected =
8625 (chunk_info[i].loader)(file, chunk_size, &scores);
8627 // the size of some chunks cannot be checked before reading other
8628 // chunks first (like "HEAD" and "BODY") that contain some header
8629 // information, so check them here
8630 if (chunk_size_expected != chunk_size)
8632 Warn("wrong size (%d) of chunk '%s' in score file '%s'",
8633 chunk_size, chunk_name, filename);
8642 #if ENABLE_HISTORIC_CHUNKS
8643 void SaveScore_OLD(int nr)
8646 int permissions = (program.global_scores ? PERMS_PUBLIC : PERMS_PRIVATE);
8647 char *filename = getScoreFilename(nr);
8650 // used instead of "leveldir_current->subdir" (for network games)
8651 InitScoreDirectory(levelset.identifier);
8653 if (!(file = fopen(filename, MODE_WRITE)))
8655 Warn("cannot save score for level %d", nr);
8660 fprintf(file, "%s\n\n", SCORE_COOKIE);
8662 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
8663 fprintf(file, "%d %s\n", scores.entry[i].score, scores.entry[i].name);
8667 SetFilePermissions(filename, permissions);
8671 static void SaveScore_VERS(FILE *file, struct ScoreInfo *scores)
8673 putFileVersion(file, scores->file_version);
8674 putFileVersion(file, scores->game_version);
8677 static void SaveScore_INFO(FILE *file, struct ScoreInfo *scores)
8679 int level_identifier_size = strlen(scores->level_identifier) + 1;
8682 putFile16BitBE(file, level_identifier_size);
8684 for (i = 0; i < level_identifier_size; i++)
8685 putFile8Bit(file, scores->level_identifier[i]);
8687 putFile16BitBE(file, scores->level_nr);
8688 putFile16BitBE(file, scores->num_entries);
8691 static void SaveScore_NAME(FILE *file, struct ScoreInfo *scores)
8695 for (i = 0; i < scores->num_entries; i++)
8697 int name_size = strlen(scores->entry[i].name);
8699 for (j = 0; j < MAX_PLAYER_NAME_LEN; j++)
8700 putFile8Bit(file, (j < name_size ? scores->entry[i].name[j] : 0));
8704 static void SaveScore_SCOR(FILE *file, struct ScoreInfo *scores)
8708 for (i = 0; i < scores->num_entries; i++)
8709 putFile16BitBE(file, scores->entry[i].score);
8712 static void SaveScore_TIME(FILE *file, struct ScoreInfo *scores)
8716 for (i = 0; i < scores->num_entries; i++)
8717 putFile32BitBE(file, scores->entry[i].time);
8720 static void SaveScoreToFilename(char *filename)
8723 int permissions = (program.global_scores ? PERMS_PUBLIC : PERMS_PRIVATE);
8724 int info_chunk_size;
8725 int name_chunk_size;
8726 int scor_chunk_size;
8727 int time_chunk_size;
8729 if (!(file = fopen(filename, MODE_WRITE)))
8731 Warn("cannot save score file '%s'", filename);
8736 info_chunk_size = 2 + (strlen(scores.level_identifier) + 1) + 2 + 2;
8737 name_chunk_size = scores.num_entries * MAX_PLAYER_NAME_LEN;
8738 scor_chunk_size = scores.num_entries * 2;
8739 time_chunk_size = scores.num_entries * 4;
8741 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
8742 putFileChunkBE(file, "SCOR", CHUNK_SIZE_NONE);
8744 putFileChunkBE(file, "VERS", SCORE_CHUNK_VERS_SIZE);
8745 SaveScore_VERS(file, &scores);
8747 putFileChunkBE(file, "INFO", info_chunk_size);
8748 SaveScore_INFO(file, &scores);
8750 putFileChunkBE(file, "NAME", name_chunk_size);
8751 SaveScore_NAME(file, &scores);
8753 putFileChunkBE(file, "SCOR", scor_chunk_size);
8754 SaveScore_SCOR(file, &scores);
8756 putFileChunkBE(file, "TIME", time_chunk_size);
8757 SaveScore_TIME(file, &scores);
8761 SetFilePermissions(filename, permissions);
8764 void SaveScore(int nr)
8766 char *filename = getScoreFilename(nr);
8769 // used instead of "leveldir_current->subdir" (for network games)
8770 InitScoreDirectory(levelset.identifier);
8772 scores.file_version = FILE_VERSION_ACTUAL;
8773 scores.game_version = GAME_VERSION_ACTUAL;
8775 strncpy(scores.level_identifier, levelset.identifier, MAX_FILENAME_LEN);
8776 scores.level_identifier[MAX_FILENAME_LEN] = '\0';
8777 scores.level_nr = level_nr;
8779 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
8780 if (scores.entry[i].score == 0 &&
8781 scores.entry[i].time == 0 &&
8782 strEqual(scores.entry[i].name, EMPTY_PLAYER_NAME))
8785 scores.num_entries = i;
8787 if (scores.num_entries == 0)
8790 SaveScoreToFilename(filename);
8794 // ============================================================================
8795 // setup file functions
8796 // ============================================================================
8798 #define TOKEN_STR_PLAYER_PREFIX "player_"
8801 static struct TokenInfo global_setup_tokens[] =
8805 &setup.player_name, "player_name"
8809 &setup.multiple_users, "multiple_users"
8813 &setup.sound, "sound"
8817 &setup.sound_loops, "repeating_sound_loops"
8821 &setup.sound_music, "background_music"
8825 &setup.sound_simple, "simple_sound_effects"
8829 &setup.toons, "toons"
8833 &setup.scroll_delay, "scroll_delay"
8837 &setup.forced_scroll_delay, "forced_scroll_delay"
8841 &setup.scroll_delay_value, "scroll_delay_value"
8845 &setup.engine_snapshot_mode, "engine_snapshot_mode"
8849 &setup.engine_snapshot_memory, "engine_snapshot_memory"
8853 &setup.fade_screens, "fade_screens"
8857 &setup.autorecord, "automatic_tape_recording"
8861 &setup.show_titlescreen, "show_titlescreen"
8865 &setup.quick_doors, "quick_doors"
8869 &setup.team_mode, "team_mode"
8873 &setup.handicap, "handicap"
8877 &setup.skip_levels, "skip_levels"
8881 &setup.increment_levels, "increment_levels"
8885 &setup.auto_play_next_level, "auto_play_next_level"
8889 &setup.count_score_after_game, "count_score_after_game"
8893 &setup.show_scores_after_game, "show_scores_after_game"
8897 &setup.time_limit, "time_limit"
8901 &setup.fullscreen, "fullscreen"
8905 &setup.window_scaling_percent, "window_scaling_percent"
8909 &setup.window_scaling_quality, "window_scaling_quality"
8913 &setup.screen_rendering_mode, "screen_rendering_mode"
8917 &setup.vsync_mode, "vsync_mode"
8921 &setup.ask_on_escape, "ask_on_escape"
8925 &setup.ask_on_escape_editor, "ask_on_escape_editor"
8929 &setup.ask_on_game_over, "ask_on_game_over"
8933 &setup.ask_on_quit_game, "ask_on_quit_game"
8937 &setup.ask_on_quit_program, "ask_on_quit_program"
8941 &setup.quick_switch, "quick_player_switch"
8945 &setup.input_on_focus, "input_on_focus"
8949 &setup.prefer_aga_graphics, "prefer_aga_graphics"
8953 &setup.prefer_lowpass_sounds, "prefer_lowpass_sounds"
8957 &setup.prefer_extra_panel_items, "prefer_extra_panel_items"
8961 &setup.game_speed_extended, "game_speed_extended"
8965 &setup.game_frame_delay, "game_frame_delay"
8969 &setup.sp_show_border_elements, "sp_show_border_elements"
8973 &setup.small_game_graphics, "small_game_graphics"
8977 &setup.show_snapshot_buttons, "show_snapshot_buttons"
8981 &setup.graphics_set, "graphics_set"
8985 &setup.sounds_set, "sounds_set"
8989 &setup.music_set, "music_set"
8993 &setup.override_level_graphics, "override_level_graphics"
8997 &setup.override_level_sounds, "override_level_sounds"
9001 &setup.override_level_music, "override_level_music"
9005 &setup.volume_simple, "volume_simple"
9009 &setup.volume_loops, "volume_loops"
9013 &setup.volume_music, "volume_music"
9017 &setup.network_mode, "network_mode"
9021 &setup.network_player_nr, "network_player"
9025 &setup.network_server_hostname, "network_server_hostname"
9029 &setup.touch.control_type, "touch.control_type"
9033 &setup.touch.move_distance, "touch.move_distance"
9037 &setup.touch.drop_distance, "touch.drop_distance"
9041 &setup.touch.transparency, "touch.transparency"
9045 &setup.touch.draw_outlined, "touch.draw_outlined"
9049 &setup.touch.draw_pressed, "touch.draw_pressed"
9053 &setup.touch.grid_xsize[0], "touch.virtual_buttons.0.xsize"
9057 &setup.touch.grid_ysize[0], "touch.virtual_buttons.0.ysize"
9061 &setup.touch.grid_xsize[1], "touch.virtual_buttons.1.xsize"
9065 &setup.touch.grid_ysize[1], "touch.virtual_buttons.1.ysize"
9069 static struct TokenInfo auto_setup_tokens[] =
9073 &setup.auto_setup.editor_zoom_tilesize, "editor.zoom_tilesize"
9077 static struct TokenInfo editor_setup_tokens[] =
9081 &setup.editor.el_classic, "editor.el_classic"
9085 &setup.editor.el_custom, "editor.el_custom"
9089 &setup.editor.el_user_defined, "editor.el_user_defined"
9093 &setup.editor.el_dynamic, "editor.el_dynamic"
9097 &setup.editor.el_headlines, "editor.el_headlines"
9101 &setup.editor.show_element_token, "editor.show_element_token"
9105 &setup.editor.show_read_only_warning, "editor.show_read_only_warning"
9109 static struct TokenInfo editor_cascade_setup_tokens[] =
9113 &setup.editor_cascade.el_bd, "editor.cascade.el_bd"
9117 &setup.editor_cascade.el_em, "editor.cascade.el_em"
9121 &setup.editor_cascade.el_emc, "editor.cascade.el_emc"
9125 &setup.editor_cascade.el_rnd, "editor.cascade.el_rnd"
9129 &setup.editor_cascade.el_sb, "editor.cascade.el_sb"
9133 &setup.editor_cascade.el_sp, "editor.cascade.el_sp"
9137 &setup.editor_cascade.el_dc, "editor.cascade.el_dc"
9141 &setup.editor_cascade.el_dx, "editor.cascade.el_dx"
9145 &setup.editor_cascade.el_mm, "editor.cascade.el_mm"
9149 &setup.editor_cascade.el_df, "editor.cascade.el_df"
9153 &setup.editor_cascade.el_chars, "editor.cascade.el_chars"
9157 &setup.editor_cascade.el_steel_chars, "editor.cascade.el_steel_chars"
9161 &setup.editor_cascade.el_ce, "editor.cascade.el_ce"
9165 &setup.editor_cascade.el_ge, "editor.cascade.el_ge"
9169 &setup.editor_cascade.el_ref, "editor.cascade.el_ref"
9173 &setup.editor_cascade.el_user, "editor.cascade.el_user"
9177 &setup.editor_cascade.el_dynamic, "editor.cascade.el_dynamic"
9181 static struct TokenInfo shortcut_setup_tokens[] =
9185 &setup.shortcut.save_game, "shortcut.save_game"
9189 &setup.shortcut.load_game, "shortcut.load_game"
9193 &setup.shortcut.toggle_pause, "shortcut.toggle_pause"
9197 &setup.shortcut.focus_player[0], "shortcut.focus_player_1"
9201 &setup.shortcut.focus_player[1], "shortcut.focus_player_2"
9205 &setup.shortcut.focus_player[2], "shortcut.focus_player_3"
9209 &setup.shortcut.focus_player[3], "shortcut.focus_player_4"
9213 &setup.shortcut.focus_player_all, "shortcut.focus_player_all"
9217 &setup.shortcut.tape_eject, "shortcut.tape_eject"
9221 &setup.shortcut.tape_extra, "shortcut.tape_extra"
9225 &setup.shortcut.tape_stop, "shortcut.tape_stop"
9229 &setup.shortcut.tape_pause, "shortcut.tape_pause"
9233 &setup.shortcut.tape_record, "shortcut.tape_record"
9237 &setup.shortcut.tape_play, "shortcut.tape_play"
9241 &setup.shortcut.sound_simple, "shortcut.sound_simple"
9245 &setup.shortcut.sound_loops, "shortcut.sound_loops"
9249 &setup.shortcut.sound_music, "shortcut.sound_music"
9253 &setup.shortcut.snap_left, "shortcut.snap_left"
9257 &setup.shortcut.snap_right, "shortcut.snap_right"
9261 &setup.shortcut.snap_up, "shortcut.snap_up"
9265 &setup.shortcut.snap_down, "shortcut.snap_down"
9269 static struct SetupInputInfo setup_input;
9270 static struct TokenInfo player_setup_tokens[] =
9274 &setup_input.use_joystick, ".use_joystick"
9278 &setup_input.joy.device_name, ".joy.device_name"
9282 &setup_input.joy.xleft, ".joy.xleft"
9286 &setup_input.joy.xmiddle, ".joy.xmiddle"
9290 &setup_input.joy.xright, ".joy.xright"
9294 &setup_input.joy.yupper, ".joy.yupper"
9298 &setup_input.joy.ymiddle, ".joy.ymiddle"
9302 &setup_input.joy.ylower, ".joy.ylower"
9306 &setup_input.joy.snap, ".joy.snap_field"
9310 &setup_input.joy.drop, ".joy.place_bomb"
9314 &setup_input.key.left, ".key.move_left"
9318 &setup_input.key.right, ".key.move_right"
9322 &setup_input.key.up, ".key.move_up"
9326 &setup_input.key.down, ".key.move_down"
9330 &setup_input.key.snap, ".key.snap_field"
9334 &setup_input.key.drop, ".key.place_bomb"
9338 static struct TokenInfo system_setup_tokens[] =
9342 &setup.system.sdl_renderdriver, "system.sdl_renderdriver"
9346 &setup.system.sdl_videodriver, "system.sdl_videodriver"
9350 &setup.system.sdl_audiodriver, "system.sdl_audiodriver"
9354 &setup.system.audio_fragment_size, "system.audio_fragment_size"
9358 static struct TokenInfo internal_setup_tokens[] =
9362 &setup.internal.program_title, "program_title"
9366 &setup.internal.program_version, "program_version"
9370 &setup.internal.program_author, "program_author"
9374 &setup.internal.program_email, "program_email"
9378 &setup.internal.program_website, "program_website"
9382 &setup.internal.program_copyright, "program_copyright"
9386 &setup.internal.program_company, "program_company"
9390 &setup.internal.program_icon_file, "program_icon_file"
9394 &setup.internal.default_graphics_set, "default_graphics_set"
9398 &setup.internal.default_sounds_set, "default_sounds_set"
9402 &setup.internal.default_music_set, "default_music_set"
9406 &setup.internal.fallback_graphics_file, "fallback_graphics_file"
9410 &setup.internal.fallback_sounds_file, "fallback_sounds_file"
9414 &setup.internal.fallback_music_file, "fallback_music_file"
9418 &setup.internal.default_level_series, "default_level_series"
9422 &setup.internal.default_window_width, "default_window_width"
9426 &setup.internal.default_window_height, "default_window_height"
9430 &setup.internal.choose_from_top_leveldir, "choose_from_top_leveldir"
9434 &setup.internal.show_scaling_in_title, "show_scaling_in_title"
9438 &setup.internal.create_user_levelset, "create_user_levelset"
9442 &setup.internal.menu_game, "menu_game"
9446 &setup.internal.menu_editor, "menu_editor"
9450 &setup.internal.menu_graphics, "menu_graphics"
9454 &setup.internal.menu_sound, "menu_sound"
9458 &setup.internal.menu_artwork, "menu_artwork"
9462 &setup.internal.menu_input, "menu_input"
9466 &setup.internal.menu_touch, "menu_touch"
9470 &setup.internal.menu_shortcuts, "menu_shortcuts"
9474 &setup.internal.menu_exit, "menu_exit"
9478 &setup.internal.menu_save_and_exit, "menu_save_and_exit"
9482 static struct TokenInfo debug_setup_tokens[] =
9486 &setup.debug.frame_delay[0], "debug.frame_delay_0"
9490 &setup.debug.frame_delay[1], "debug.frame_delay_1"
9494 &setup.debug.frame_delay[2], "debug.frame_delay_2"
9498 &setup.debug.frame_delay[3], "debug.frame_delay_3"
9502 &setup.debug.frame_delay[4], "debug.frame_delay_4"
9506 &setup.debug.frame_delay[5], "debug.frame_delay_5"
9510 &setup.debug.frame_delay[6], "debug.frame_delay_6"
9514 &setup.debug.frame_delay[7], "debug.frame_delay_7"
9518 &setup.debug.frame_delay[8], "debug.frame_delay_8"
9522 &setup.debug.frame_delay[9], "debug.frame_delay_9"
9526 &setup.debug.frame_delay_key[0], "debug.key.frame_delay_0"
9530 &setup.debug.frame_delay_key[1], "debug.key.frame_delay_1"
9534 &setup.debug.frame_delay_key[2], "debug.key.frame_delay_2"
9538 &setup.debug.frame_delay_key[3], "debug.key.frame_delay_3"
9542 &setup.debug.frame_delay_key[4], "debug.key.frame_delay_4"
9546 &setup.debug.frame_delay_key[5], "debug.key.frame_delay_5"
9550 &setup.debug.frame_delay_key[6], "debug.key.frame_delay_6"
9554 &setup.debug.frame_delay_key[7], "debug.key.frame_delay_7"
9558 &setup.debug.frame_delay_key[8], "debug.key.frame_delay_8"
9562 &setup.debug.frame_delay_key[9], "debug.key.frame_delay_9"
9566 &setup.debug.frame_delay_use_mod_key, "debug.frame_delay.use_mod_key"},
9569 &setup.debug.frame_delay_game_only, "debug.frame_delay.game_only"
9573 &setup.debug.show_frames_per_second, "debug.show_frames_per_second"
9577 &setup.debug.xsn_mode, "debug.xsn_mode"
9581 &setup.debug.xsn_percent, "debug.xsn_percent"
9585 static struct TokenInfo options_setup_tokens[] =
9589 &setup.options.verbose, "options.verbose"
9593 static void setSetupInfoToDefaults(struct SetupInfo *si)
9597 si->player_name = getStringCopy(getDefaultUserName(user.nr));
9599 si->multiple_users = TRUE;
9602 si->sound_loops = TRUE;
9603 si->sound_music = TRUE;
9604 si->sound_simple = TRUE;
9606 si->scroll_delay = TRUE;
9607 si->forced_scroll_delay = FALSE;
9608 si->scroll_delay_value = STD_SCROLL_DELAY;
9609 si->engine_snapshot_mode = getStringCopy(STR_SNAPSHOT_MODE_DEFAULT);
9610 si->engine_snapshot_memory = SNAPSHOT_MEMORY_DEFAULT;
9611 si->fade_screens = TRUE;
9612 si->autorecord = TRUE;
9613 si->show_titlescreen = TRUE;
9614 si->quick_doors = FALSE;
9615 si->team_mode = FALSE;
9616 si->handicap = TRUE;
9617 si->skip_levels = TRUE;
9618 si->increment_levels = TRUE;
9619 si->auto_play_next_level = TRUE;
9620 si->count_score_after_game = TRUE;
9621 si->show_scores_after_game = TRUE;
9622 si->time_limit = TRUE;
9623 si->fullscreen = FALSE;
9624 si->window_scaling_percent = STD_WINDOW_SCALING_PERCENT;
9625 si->window_scaling_quality = getStringCopy(SCALING_QUALITY_DEFAULT);
9626 si->screen_rendering_mode = getStringCopy(STR_SPECIAL_RENDERING_DEFAULT);
9627 si->vsync_mode = getStringCopy(STR_VSYNC_MODE_DEFAULT);
9628 si->ask_on_escape = TRUE;
9629 si->ask_on_escape_editor = TRUE;
9630 si->ask_on_game_over = TRUE;
9631 si->ask_on_quit_game = TRUE;
9632 si->ask_on_quit_program = TRUE;
9633 si->quick_switch = FALSE;
9634 si->input_on_focus = FALSE;
9635 si->prefer_aga_graphics = TRUE;
9636 si->prefer_lowpass_sounds = FALSE;
9637 si->prefer_extra_panel_items = TRUE;
9638 si->game_speed_extended = FALSE;
9639 si->game_frame_delay = GAME_FRAME_DELAY;
9640 si->sp_show_border_elements = FALSE;
9641 si->small_game_graphics = FALSE;
9642 si->show_snapshot_buttons = FALSE;
9644 si->graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
9645 si->sounds_set = getStringCopy(SND_CLASSIC_SUBDIR);
9646 si->music_set = getStringCopy(MUS_CLASSIC_SUBDIR);
9648 si->override_level_graphics = FALSE;
9649 si->override_level_sounds = FALSE;
9650 si->override_level_music = FALSE;
9652 si->volume_simple = 100; // percent
9653 si->volume_loops = 100; // percent
9654 si->volume_music = 100; // percent
9656 si->network_mode = FALSE;
9657 si->network_player_nr = 0; // first player
9658 si->network_server_hostname = getStringCopy(STR_NETWORK_AUTO_DETECT);
9660 si->touch.control_type = getStringCopy(TOUCH_CONTROL_DEFAULT);
9661 si->touch.move_distance = TOUCH_MOVE_DISTANCE_DEFAULT; // percent
9662 si->touch.drop_distance = TOUCH_DROP_DISTANCE_DEFAULT; // percent
9663 si->touch.transparency = TOUCH_TRANSPARENCY_DEFAULT; // percent
9664 si->touch.draw_outlined = TRUE;
9665 si->touch.draw_pressed = TRUE;
9667 for (i = 0; i < 2; i++)
9669 char *default_grid_button[6][2] =
9675 { "111222", " vv " },
9676 { "111222", " vv " }
9678 int grid_xsize = DEFAULT_GRID_XSIZE(i);
9679 int grid_ysize = DEFAULT_GRID_YSIZE(i);
9680 int min_xsize = MIN(6, grid_xsize);
9681 int min_ysize = MIN(6, grid_ysize);
9682 int startx = grid_xsize - min_xsize;
9683 int starty = grid_ysize - min_ysize;
9686 // virtual buttons grid can only be set to defaults if video is initialized
9687 // (this will be repeated if virtual buttons are not loaded from setup file)
9688 if (video.initialized)
9690 si->touch.grid_xsize[i] = grid_xsize;
9691 si->touch.grid_ysize[i] = grid_ysize;
9695 si->touch.grid_xsize[i] = -1;
9696 si->touch.grid_ysize[i] = -1;
9699 for (x = 0; x < MAX_GRID_XSIZE; x++)
9700 for (y = 0; y < MAX_GRID_YSIZE; y++)
9701 si->touch.grid_button[i][x][y] = CHAR_GRID_BUTTON_NONE;
9703 for (x = 0; x < min_xsize; x++)
9704 for (y = 0; y < min_ysize; y++)
9705 si->touch.grid_button[i][x][starty + y] =
9706 default_grid_button[y][0][x];
9708 for (x = 0; x < min_xsize; x++)
9709 for (y = 0; y < min_ysize; y++)
9710 si->touch.grid_button[i][startx + x][starty + y] =
9711 default_grid_button[y][1][x];
9714 si->touch.grid_initialized = video.initialized;
9716 si->editor.el_boulderdash = TRUE;
9717 si->editor.el_emerald_mine = TRUE;
9718 si->editor.el_emerald_mine_club = TRUE;
9719 si->editor.el_more = TRUE;
9720 si->editor.el_sokoban = TRUE;
9721 si->editor.el_supaplex = TRUE;
9722 si->editor.el_diamond_caves = TRUE;
9723 si->editor.el_dx_boulderdash = TRUE;
9725 si->editor.el_mirror_magic = TRUE;
9726 si->editor.el_deflektor = TRUE;
9728 si->editor.el_chars = TRUE;
9729 si->editor.el_steel_chars = TRUE;
9731 si->editor.el_classic = TRUE;
9732 si->editor.el_custom = TRUE;
9734 si->editor.el_user_defined = FALSE;
9735 si->editor.el_dynamic = TRUE;
9737 si->editor.el_headlines = TRUE;
9739 si->editor.show_element_token = FALSE;
9741 si->editor.show_read_only_warning = TRUE;
9743 si->editor.use_template_for_new_levels = TRUE;
9745 si->shortcut.save_game = DEFAULT_KEY_SAVE_GAME;
9746 si->shortcut.load_game = DEFAULT_KEY_LOAD_GAME;
9747 si->shortcut.toggle_pause = DEFAULT_KEY_TOGGLE_PAUSE;
9749 si->shortcut.focus_player[0] = DEFAULT_KEY_FOCUS_PLAYER_1;
9750 si->shortcut.focus_player[1] = DEFAULT_KEY_FOCUS_PLAYER_2;
9751 si->shortcut.focus_player[2] = DEFAULT_KEY_FOCUS_PLAYER_3;
9752 si->shortcut.focus_player[3] = DEFAULT_KEY_FOCUS_PLAYER_4;
9753 si->shortcut.focus_player_all = DEFAULT_KEY_FOCUS_PLAYER_ALL;
9755 si->shortcut.tape_eject = DEFAULT_KEY_TAPE_EJECT;
9756 si->shortcut.tape_extra = DEFAULT_KEY_TAPE_EXTRA;
9757 si->shortcut.tape_stop = DEFAULT_KEY_TAPE_STOP;
9758 si->shortcut.tape_pause = DEFAULT_KEY_TAPE_PAUSE;
9759 si->shortcut.tape_record = DEFAULT_KEY_TAPE_RECORD;
9760 si->shortcut.tape_play = DEFAULT_KEY_TAPE_PLAY;
9762 si->shortcut.sound_simple = DEFAULT_KEY_SOUND_SIMPLE;
9763 si->shortcut.sound_loops = DEFAULT_KEY_SOUND_LOOPS;
9764 si->shortcut.sound_music = DEFAULT_KEY_SOUND_MUSIC;
9766 si->shortcut.snap_left = DEFAULT_KEY_SNAP_LEFT;
9767 si->shortcut.snap_right = DEFAULT_KEY_SNAP_RIGHT;
9768 si->shortcut.snap_up = DEFAULT_KEY_SNAP_UP;
9769 si->shortcut.snap_down = DEFAULT_KEY_SNAP_DOWN;
9771 for (i = 0; i < MAX_PLAYERS; i++)
9773 si->input[i].use_joystick = FALSE;
9774 si->input[i].joy.device_name=getStringCopy(getDeviceNameFromJoystickNr(i));
9775 si->input[i].joy.xleft = JOYSTICK_XLEFT;
9776 si->input[i].joy.xmiddle = JOYSTICK_XMIDDLE;
9777 si->input[i].joy.xright = JOYSTICK_XRIGHT;
9778 si->input[i].joy.yupper = JOYSTICK_YUPPER;
9779 si->input[i].joy.ymiddle = JOYSTICK_YMIDDLE;
9780 si->input[i].joy.ylower = JOYSTICK_YLOWER;
9781 si->input[i].joy.snap = (i == 0 ? JOY_BUTTON_1 : 0);
9782 si->input[i].joy.drop = (i == 0 ? JOY_BUTTON_2 : 0);
9783 si->input[i].key.left = (i == 0 ? DEFAULT_KEY_LEFT : KSYM_UNDEFINED);
9784 si->input[i].key.right = (i == 0 ? DEFAULT_KEY_RIGHT : KSYM_UNDEFINED);
9785 si->input[i].key.up = (i == 0 ? DEFAULT_KEY_UP : KSYM_UNDEFINED);
9786 si->input[i].key.down = (i == 0 ? DEFAULT_KEY_DOWN : KSYM_UNDEFINED);
9787 si->input[i].key.snap = (i == 0 ? DEFAULT_KEY_SNAP : KSYM_UNDEFINED);
9788 si->input[i].key.drop = (i == 0 ? DEFAULT_KEY_DROP : KSYM_UNDEFINED);
9791 si->system.sdl_renderdriver = getStringCopy(ARG_DEFAULT);
9792 si->system.sdl_videodriver = getStringCopy(ARG_DEFAULT);
9793 si->system.sdl_audiodriver = getStringCopy(ARG_DEFAULT);
9794 si->system.audio_fragment_size = DEFAULT_AUDIO_FRAGMENT_SIZE;
9796 si->internal.program_title = getStringCopy(PROGRAM_TITLE_STRING);
9797 si->internal.program_version = getStringCopy(getProgramRealVersionString());
9798 si->internal.program_author = getStringCopy(PROGRAM_AUTHOR_STRING);
9799 si->internal.program_email = getStringCopy(PROGRAM_EMAIL_STRING);
9800 si->internal.program_website = getStringCopy(PROGRAM_WEBSITE_STRING);
9801 si->internal.program_copyright = getStringCopy(PROGRAM_COPYRIGHT_STRING);
9802 si->internal.program_company = getStringCopy(PROGRAM_COMPANY_STRING);
9804 si->internal.program_icon_file = getStringCopy(PROGRAM_ICON_FILENAME);
9806 si->internal.default_graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
9807 si->internal.default_sounds_set = getStringCopy(SND_CLASSIC_SUBDIR);
9808 si->internal.default_music_set = getStringCopy(MUS_CLASSIC_SUBDIR);
9810 si->internal.fallback_graphics_file = getStringCopy(UNDEFINED_FILENAME);
9811 si->internal.fallback_sounds_file = getStringCopy(UNDEFINED_FILENAME);
9812 si->internal.fallback_music_file = getStringCopy(UNDEFINED_FILENAME);
9814 si->internal.default_level_series = getStringCopy(UNDEFINED_LEVELSET);
9815 si->internal.choose_from_top_leveldir = FALSE;
9816 si->internal.show_scaling_in_title = TRUE;
9817 si->internal.create_user_levelset = TRUE;
9819 si->internal.default_window_width = WIN_XSIZE_DEFAULT;
9820 si->internal.default_window_height = WIN_YSIZE_DEFAULT;
9822 si->debug.frame_delay[0] = DEFAULT_FRAME_DELAY_0;
9823 si->debug.frame_delay[1] = DEFAULT_FRAME_DELAY_1;
9824 si->debug.frame_delay[2] = DEFAULT_FRAME_DELAY_2;
9825 si->debug.frame_delay[3] = DEFAULT_FRAME_DELAY_3;
9826 si->debug.frame_delay[4] = DEFAULT_FRAME_DELAY_4;
9827 si->debug.frame_delay[5] = DEFAULT_FRAME_DELAY_5;
9828 si->debug.frame_delay[6] = DEFAULT_FRAME_DELAY_6;
9829 si->debug.frame_delay[7] = DEFAULT_FRAME_DELAY_7;
9830 si->debug.frame_delay[8] = DEFAULT_FRAME_DELAY_8;
9831 si->debug.frame_delay[9] = DEFAULT_FRAME_DELAY_9;
9833 si->debug.frame_delay_key[0] = DEFAULT_KEY_FRAME_DELAY_0;
9834 si->debug.frame_delay_key[1] = DEFAULT_KEY_FRAME_DELAY_1;
9835 si->debug.frame_delay_key[2] = DEFAULT_KEY_FRAME_DELAY_2;
9836 si->debug.frame_delay_key[3] = DEFAULT_KEY_FRAME_DELAY_3;
9837 si->debug.frame_delay_key[4] = DEFAULT_KEY_FRAME_DELAY_4;
9838 si->debug.frame_delay_key[5] = DEFAULT_KEY_FRAME_DELAY_5;
9839 si->debug.frame_delay_key[6] = DEFAULT_KEY_FRAME_DELAY_6;
9840 si->debug.frame_delay_key[7] = DEFAULT_KEY_FRAME_DELAY_7;
9841 si->debug.frame_delay_key[8] = DEFAULT_KEY_FRAME_DELAY_8;
9842 si->debug.frame_delay_key[9] = DEFAULT_KEY_FRAME_DELAY_9;
9844 si->debug.frame_delay_use_mod_key = DEFAULT_FRAME_DELAY_USE_MOD_KEY;
9845 si->debug.frame_delay_game_only = DEFAULT_FRAME_DELAY_GAME_ONLY;
9847 si->debug.show_frames_per_second = FALSE;
9849 si->debug.xsn_mode = AUTO;
9850 si->debug.xsn_percent = 0;
9852 si->options.verbose = FALSE;
9854 #if defined(PLATFORM_ANDROID)
9855 si->fullscreen = TRUE;
9858 setHideSetupEntry(&setup.debug.xsn_mode);
9861 static void setSetupInfoToDefaults_AutoSetup(struct SetupInfo *si)
9863 si->auto_setup.editor_zoom_tilesize = MINI_TILESIZE;
9866 static void setSetupInfoToDefaults_EditorCascade(struct SetupInfo *si)
9868 si->editor_cascade.el_bd = TRUE;
9869 si->editor_cascade.el_em = TRUE;
9870 si->editor_cascade.el_emc = TRUE;
9871 si->editor_cascade.el_rnd = TRUE;
9872 si->editor_cascade.el_sb = TRUE;
9873 si->editor_cascade.el_sp = TRUE;
9874 si->editor_cascade.el_dc = TRUE;
9875 si->editor_cascade.el_dx = TRUE;
9877 si->editor_cascade.el_mm = TRUE;
9878 si->editor_cascade.el_df = TRUE;
9880 si->editor_cascade.el_chars = FALSE;
9881 si->editor_cascade.el_steel_chars = FALSE;
9882 si->editor_cascade.el_ce = FALSE;
9883 si->editor_cascade.el_ge = FALSE;
9884 si->editor_cascade.el_ref = FALSE;
9885 si->editor_cascade.el_user = FALSE;
9886 si->editor_cascade.el_dynamic = FALSE;
9889 #define MAX_HIDE_SETUP_TOKEN_SIZE 20
9891 static char *getHideSetupToken(void *setup_value)
9893 static char hide_setup_token[MAX_HIDE_SETUP_TOKEN_SIZE];
9895 if (setup_value != NULL)
9896 snprintf(hide_setup_token, MAX_HIDE_SETUP_TOKEN_SIZE, "%p", setup_value);
9898 return hide_setup_token;
9901 void setHideSetupEntry(void *setup_value)
9903 char *hide_setup_token = getHideSetupToken(setup_value);
9905 if (hide_setup_hash == NULL)
9906 hide_setup_hash = newSetupFileHash();
9908 if (setup_value != NULL)
9909 setHashEntry(hide_setup_hash, hide_setup_token, "");
9912 void removeHideSetupEntry(void *setup_value)
9914 char *hide_setup_token = getHideSetupToken(setup_value);
9916 if (setup_value != NULL)
9917 removeHashEntry(hide_setup_hash, hide_setup_token);
9920 boolean hideSetupEntry(void *setup_value)
9922 char *hide_setup_token = getHideSetupToken(setup_value);
9924 return (setup_value != NULL &&
9925 getHashEntry(hide_setup_hash, hide_setup_token) != NULL);
9928 static void setSetupInfoFromTokenText(SetupFileHash *setup_file_hash,
9929 struct TokenInfo *token_info,
9930 int token_nr, char *token_text)
9932 char *token_hide_text = getStringCat2(token_text, ".hide");
9933 char *token_hide_value = getHashEntry(setup_file_hash, token_hide_text);
9935 // set the value of this setup option in the setup option structure
9936 setSetupInfo(token_info, token_nr, getHashEntry(setup_file_hash, token_text));
9938 // check if this setup option should be hidden in the setup menu
9939 if (token_hide_value != NULL && get_boolean_from_string(token_hide_value))
9940 setHideSetupEntry(token_info[token_nr].value);
9942 free(token_hide_text);
9945 static void setSetupInfoFromTokenInfo(SetupFileHash *setup_file_hash,
9946 struct TokenInfo *token_info,
9949 setSetupInfoFromTokenText(setup_file_hash, token_info, token_nr,
9950 token_info[token_nr].text);
9953 static void decodeSetupFileHash(SetupFileHash *setup_file_hash)
9957 if (!setup_file_hash)
9960 for (i = 0; i < ARRAY_SIZE(global_setup_tokens); i++)
9961 setSetupInfoFromTokenInfo(setup_file_hash, global_setup_tokens, i);
9963 setup.touch.grid_initialized = TRUE;
9964 for (i = 0; i < 2; i++)
9966 int grid_xsize = setup.touch.grid_xsize[i];
9967 int grid_ysize = setup.touch.grid_ysize[i];
9970 // if virtual buttons are not loaded from setup file, repeat initializing
9971 // virtual buttons grid with default values later when video is initialized
9972 if (grid_xsize == -1 ||
9975 setup.touch.grid_initialized = FALSE;
9980 for (y = 0; y < grid_ysize; y++)
9982 char token_string[MAX_LINE_LEN];
9984 sprintf(token_string, "touch.virtual_buttons.%d.%02d", i, y);
9986 char *value_string = getHashEntry(setup_file_hash, token_string);
9988 if (value_string == NULL)
9991 for (x = 0; x < grid_xsize; x++)
9993 char c = value_string[x];
9995 setup.touch.grid_button[i][x][y] =
9996 (c == '.' ? CHAR_GRID_BUTTON_NONE : c);
10001 for (i = 0; i < ARRAY_SIZE(editor_setup_tokens); i++)
10002 setSetupInfoFromTokenInfo(setup_file_hash, editor_setup_tokens, i);
10004 for (i = 0; i < ARRAY_SIZE(shortcut_setup_tokens); i++)
10005 setSetupInfoFromTokenInfo(setup_file_hash, shortcut_setup_tokens, i);
10007 for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
10011 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
10013 setup_input = setup.input[pnr];
10014 for (i = 0; i < ARRAY_SIZE(player_setup_tokens); i++)
10016 char full_token[100];
10018 sprintf(full_token, "%s%s", prefix, player_setup_tokens[i].text);
10019 setSetupInfoFromTokenText(setup_file_hash, player_setup_tokens, i,
10022 setup.input[pnr] = setup_input;
10025 for (i = 0; i < ARRAY_SIZE(system_setup_tokens); i++)
10026 setSetupInfoFromTokenInfo(setup_file_hash, system_setup_tokens, i);
10028 for (i = 0; i < ARRAY_SIZE(internal_setup_tokens); i++)
10029 setSetupInfoFromTokenInfo(setup_file_hash, internal_setup_tokens, i);
10031 for (i = 0; i < ARRAY_SIZE(debug_setup_tokens); i++)
10032 setSetupInfoFromTokenInfo(setup_file_hash, debug_setup_tokens, i);
10034 for (i = 0; i < ARRAY_SIZE(options_setup_tokens); i++)
10035 setSetupInfoFromTokenInfo(setup_file_hash, options_setup_tokens, i);
10037 setHideRelatedSetupEntries();
10040 static void decodeSetupFileHash_AutoSetup(SetupFileHash *setup_file_hash)
10044 if (!setup_file_hash)
10047 for (i = 0; i < ARRAY_SIZE(auto_setup_tokens); i++)
10048 setSetupInfo(auto_setup_tokens, i,
10049 getHashEntry(setup_file_hash,
10050 auto_setup_tokens[i].text));
10053 static void decodeSetupFileHash_EditorCascade(SetupFileHash *setup_file_hash)
10057 if (!setup_file_hash)
10060 for (i = 0; i < ARRAY_SIZE(editor_cascade_setup_tokens); i++)
10061 setSetupInfo(editor_cascade_setup_tokens, i,
10062 getHashEntry(setup_file_hash,
10063 editor_cascade_setup_tokens[i].text));
10066 void LoadUserNames(void)
10068 int last_user_nr = user.nr;
10071 if (global.user_names != NULL)
10073 for (i = 0; i < MAX_PLAYER_NAMES; i++)
10074 checked_free(global.user_names[i]);
10076 checked_free(global.user_names);
10079 global.user_names = checked_calloc(MAX_PLAYER_NAMES * sizeof(char *));
10081 for (i = 0; i < MAX_PLAYER_NAMES; i++)
10085 SetupFileHash *setup_file_hash = loadSetupFileHash(getSetupFilename());
10087 if (setup_file_hash)
10089 char *player_name = getHashEntry(setup_file_hash, "player_name");
10091 global.user_names[i] = getFixedUserName(player_name);
10093 freeSetupFileHash(setup_file_hash);
10096 if (global.user_names[i] == NULL)
10097 global.user_names[i] = getStringCopy(getDefaultUserName(i));
10100 user.nr = last_user_nr;
10103 void LoadSetupFromFilename(char *filename)
10105 SetupFileHash *setup_file_hash = loadSetupFileHash(filename);
10107 if (setup_file_hash)
10109 decodeSetupFileHash(setup_file_hash);
10111 freeSetupFileHash(setup_file_hash);
10115 Debug("setup", "using default setup values");
10119 static void LoadSetup_SpecialPostProcessing(void)
10121 char *player_name_new;
10123 // needed to work around problems with fixed length strings
10124 player_name_new = getFixedUserName(setup.player_name);
10125 free(setup.player_name);
10126 setup.player_name = player_name_new;
10128 // "scroll_delay: on(3) / off(0)" was replaced by scroll delay value
10129 if (setup.scroll_delay == FALSE)
10131 setup.scroll_delay_value = MIN_SCROLL_DELAY;
10132 setup.scroll_delay = TRUE; // now always "on"
10135 // make sure that scroll delay value stays inside valid range
10136 setup.scroll_delay_value =
10137 MIN(MAX(MIN_SCROLL_DELAY, setup.scroll_delay_value), MAX_SCROLL_DELAY);
10140 void LoadSetup(void)
10144 // always start with reliable default values
10145 setSetupInfoToDefaults(&setup);
10147 // try to load setup values from default setup file
10148 filename = getDefaultSetupFilename();
10150 if (fileExists(filename))
10151 LoadSetupFromFilename(filename);
10153 // try to load setup values from user setup file
10154 filename = getSetupFilename();
10156 LoadSetupFromFilename(filename);
10158 LoadSetup_SpecialPostProcessing();
10161 void LoadSetup_AutoSetup(void)
10163 char *filename = getPath2(getSetupDir(), AUTOSETUP_FILENAME);
10164 SetupFileHash *setup_file_hash = NULL;
10166 // always start with reliable default values
10167 setSetupInfoToDefaults_AutoSetup(&setup);
10169 setup_file_hash = loadSetupFileHash(filename);
10171 if (setup_file_hash)
10173 decodeSetupFileHash_AutoSetup(setup_file_hash);
10175 freeSetupFileHash(setup_file_hash);
10181 void LoadSetup_EditorCascade(void)
10183 char *filename = getPath2(getSetupDir(), EDITORCASCADE_FILENAME);
10184 SetupFileHash *setup_file_hash = NULL;
10186 // always start with reliable default values
10187 setSetupInfoToDefaults_EditorCascade(&setup);
10189 setup_file_hash = loadSetupFileHash(filename);
10191 if (setup_file_hash)
10193 decodeSetupFileHash_EditorCascade(setup_file_hash);
10195 freeSetupFileHash(setup_file_hash);
10201 static void addGameControllerMappingToHash(SetupFileHash *mappings_hash,
10202 char *mapping_line)
10204 char mapping_guid[MAX_LINE_LEN];
10205 char *mapping_start, *mapping_end;
10207 // get GUID from game controller mapping line: copy complete line
10208 strncpy(mapping_guid, mapping_line, MAX_LINE_LEN - 1);
10209 mapping_guid[MAX_LINE_LEN - 1] = '\0';
10211 // get GUID from game controller mapping line: cut after GUID part
10212 mapping_start = strchr(mapping_guid, ',');
10213 if (mapping_start != NULL)
10214 *mapping_start = '\0';
10216 // cut newline from game controller mapping line
10217 mapping_end = strchr(mapping_line, '\n');
10218 if (mapping_end != NULL)
10219 *mapping_end = '\0';
10221 // add mapping entry to game controller mappings hash
10222 setHashEntry(mappings_hash, mapping_guid, mapping_line);
10225 static void LoadSetup_ReadGameControllerMappings(SetupFileHash *mappings_hash,
10230 if (!(file = fopen(filename, MODE_READ)))
10232 Warn("cannot read game controller mappings file '%s'", filename);
10237 while (!feof(file))
10239 char line[MAX_LINE_LEN];
10241 if (!fgets(line, MAX_LINE_LEN, file))
10244 addGameControllerMappingToHash(mappings_hash, line);
10250 void SaveSetup(void)
10252 char *filename = getSetupFilename();
10256 InitUserDataDirectory();
10258 if (!(file = fopen(filename, MODE_WRITE)))
10260 Warn("cannot write setup file '%s'", filename);
10265 fprintFileHeader(file, SETUP_FILENAME);
10267 for (i = 0; i < ARRAY_SIZE(global_setup_tokens); i++)
10269 // just to make things nicer :)
10270 if (global_setup_tokens[i].value == &setup.multiple_users ||
10271 global_setup_tokens[i].value == &setup.sound ||
10272 global_setup_tokens[i].value == &setup.graphics_set ||
10273 global_setup_tokens[i].value == &setup.volume_simple ||
10274 global_setup_tokens[i].value == &setup.network_mode ||
10275 global_setup_tokens[i].value == &setup.touch.control_type ||
10276 global_setup_tokens[i].value == &setup.touch.grid_xsize[0] ||
10277 global_setup_tokens[i].value == &setup.touch.grid_xsize[1])
10278 fprintf(file, "\n");
10280 fprintf(file, "%s\n", getSetupLine(global_setup_tokens, "", i));
10283 for (i = 0; i < 2; i++)
10285 int grid_xsize = setup.touch.grid_xsize[i];
10286 int grid_ysize = setup.touch.grid_ysize[i];
10289 fprintf(file, "\n");
10291 for (y = 0; y < grid_ysize; y++)
10293 char token_string[MAX_LINE_LEN];
10294 char value_string[MAX_LINE_LEN];
10296 sprintf(token_string, "touch.virtual_buttons.%d.%02d", i, y);
10298 for (x = 0; x < grid_xsize; x++)
10300 char c = setup.touch.grid_button[i][x][y];
10302 value_string[x] = (c == CHAR_GRID_BUTTON_NONE ? '.' : c);
10305 value_string[grid_xsize] = '\0';
10307 fprintf(file, "%s\n", getFormattedSetupEntry(token_string, value_string));
10311 fprintf(file, "\n");
10312 for (i = 0; i < ARRAY_SIZE(editor_setup_tokens); i++)
10313 fprintf(file, "%s\n", getSetupLine(editor_setup_tokens, "", i));
10315 fprintf(file, "\n");
10316 for (i = 0; i < ARRAY_SIZE(shortcut_setup_tokens); i++)
10317 fprintf(file, "%s\n", getSetupLine(shortcut_setup_tokens, "", i));
10319 for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
10323 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
10324 fprintf(file, "\n");
10326 setup_input = setup.input[pnr];
10327 for (i = 0; i < ARRAY_SIZE(player_setup_tokens); i++)
10328 fprintf(file, "%s\n", getSetupLine(player_setup_tokens, prefix, i));
10331 fprintf(file, "\n");
10332 for (i = 0; i < ARRAY_SIZE(system_setup_tokens); i++)
10333 fprintf(file, "%s\n", getSetupLine(system_setup_tokens, "", i));
10335 // (internal setup values not saved to user setup file)
10337 fprintf(file, "\n");
10338 for (i = 0; i < ARRAY_SIZE(debug_setup_tokens); i++)
10339 if (!strPrefix(debug_setup_tokens[i].text, "debug.xsn_") ||
10340 setup.debug.xsn_mode != AUTO)
10341 fprintf(file, "%s\n", getSetupLine(debug_setup_tokens, "", i));
10343 fprintf(file, "\n");
10344 for (i = 0; i < ARRAY_SIZE(options_setup_tokens); i++)
10345 fprintf(file, "%s\n", getSetupLine(options_setup_tokens, "", i));
10349 SetFilePermissions(filename, PERMS_PRIVATE);
10352 void SaveSetup_AutoSetup(void)
10354 char *filename = getPath2(getSetupDir(), AUTOSETUP_FILENAME);
10358 InitUserDataDirectory();
10360 if (!(file = fopen(filename, MODE_WRITE)))
10362 Warn("cannot write auto setup file '%s'", filename);
10369 fprintFileHeader(file, AUTOSETUP_FILENAME);
10371 for (i = 0; i < ARRAY_SIZE(auto_setup_tokens); i++)
10372 fprintf(file, "%s\n", getSetupLine(auto_setup_tokens, "", i));
10376 SetFilePermissions(filename, PERMS_PRIVATE);
10381 void SaveSetup_EditorCascade(void)
10383 char *filename = getPath2(getSetupDir(), EDITORCASCADE_FILENAME);
10387 InitUserDataDirectory();
10389 if (!(file = fopen(filename, MODE_WRITE)))
10391 Warn("cannot write editor cascade state file '%s'", filename);
10398 fprintFileHeader(file, EDITORCASCADE_FILENAME);
10400 for (i = 0; i < ARRAY_SIZE(editor_cascade_setup_tokens); i++)
10401 fprintf(file, "%s\n", getSetupLine(editor_cascade_setup_tokens, "", i));
10405 SetFilePermissions(filename, PERMS_PRIVATE);
10410 static void SaveSetup_WriteGameControllerMappings(SetupFileHash *mappings_hash,
10415 if (!(file = fopen(filename, MODE_WRITE)))
10417 Warn("cannot write game controller mappings file '%s'", filename);
10422 BEGIN_HASH_ITERATION(mappings_hash, itr)
10424 fprintf(file, "%s\n", HASH_ITERATION_VALUE(itr));
10426 END_HASH_ITERATION(mappings_hash, itr)
10431 void SaveSetup_AddGameControllerMapping(char *mapping)
10433 char *filename = getPath2(getSetupDir(), GAMECONTROLLER_BASENAME);
10434 SetupFileHash *mappings_hash = newSetupFileHash();
10436 InitUserDataDirectory();
10438 // load existing personal game controller mappings
10439 LoadSetup_ReadGameControllerMappings(mappings_hash, filename);
10441 // add new mapping to personal game controller mappings
10442 addGameControllerMappingToHash(mappings_hash, mapping);
10444 // save updated personal game controller mappings
10445 SaveSetup_WriteGameControllerMappings(mappings_hash, filename);
10447 freeSetupFileHash(mappings_hash);
10451 void LoadCustomElementDescriptions(void)
10453 char *filename = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
10454 SetupFileHash *setup_file_hash;
10457 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
10459 if (element_info[i].custom_description != NULL)
10461 free(element_info[i].custom_description);
10462 element_info[i].custom_description = NULL;
10466 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
10469 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
10471 char *token = getStringCat2(element_info[i].token_name, ".name");
10472 char *value = getHashEntry(setup_file_hash, token);
10475 element_info[i].custom_description = getStringCopy(value);
10480 freeSetupFileHash(setup_file_hash);
10483 static int getElementFromToken(char *token)
10485 char *value = getHashEntry(element_token_hash, token);
10488 return atoi(value);
10490 Warn("unknown element token '%s'", token);
10492 return EL_UNDEFINED;
10495 void FreeGlobalAnimEventInfo(void)
10497 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
10499 if (gaei->event_list == NULL)
10504 for (i = 0; i < gaei->num_event_lists; i++)
10506 checked_free(gaei->event_list[i]->event_value);
10507 checked_free(gaei->event_list[i]);
10510 checked_free(gaei->event_list);
10512 gaei->event_list = NULL;
10513 gaei->num_event_lists = 0;
10516 static int AddGlobalAnimEventList(void)
10518 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
10519 int list_pos = gaei->num_event_lists++;
10521 gaei->event_list = checked_realloc(gaei->event_list, gaei->num_event_lists *
10522 sizeof(struct GlobalAnimEventListInfo *));
10524 gaei->event_list[list_pos] =
10525 checked_calloc(sizeof(struct GlobalAnimEventListInfo));
10527 struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
10529 gaeli->event_value = NULL;
10530 gaeli->num_event_values = 0;
10535 static int AddGlobalAnimEventValue(int list_pos, int event_value)
10537 // do not add empty global animation events
10538 if (event_value == ANIM_EVENT_NONE)
10541 // if list position is undefined, create new list
10542 if (list_pos == ANIM_EVENT_UNDEFINED)
10543 list_pos = AddGlobalAnimEventList();
10545 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
10546 struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
10547 int value_pos = gaeli->num_event_values++;
10549 gaeli->event_value = checked_realloc(gaeli->event_value,
10550 gaeli->num_event_values * sizeof(int *));
10552 gaeli->event_value[value_pos] = event_value;
10557 int GetGlobalAnimEventValue(int list_pos, int value_pos)
10559 if (list_pos == ANIM_EVENT_UNDEFINED)
10560 return ANIM_EVENT_NONE;
10562 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
10563 struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
10565 return gaeli->event_value[value_pos];
10568 int GetGlobalAnimEventValueCount(int list_pos)
10570 if (list_pos == ANIM_EVENT_UNDEFINED)
10573 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
10574 struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
10576 return gaeli->num_event_values;
10579 // This function checks if a string <s> of the format "string1, string2, ..."
10580 // exactly contains a string <s_contained>.
10582 static boolean string_has_parameter(char *s, char *s_contained)
10586 if (s == NULL || s_contained == NULL)
10589 if (strlen(s_contained) > strlen(s))
10592 if (strncmp(s, s_contained, strlen(s_contained)) == 0)
10594 char next_char = s[strlen(s_contained)];
10596 // check if next character is delimiter or whitespace
10597 return (next_char == ',' || next_char == '\0' ||
10598 next_char == ' ' || next_char == '\t' ? TRUE : FALSE);
10601 // check if string contains another parameter string after a comma
10602 substring = strchr(s, ',');
10603 if (substring == NULL) // string does not contain a comma
10606 // advance string pointer to next character after the comma
10609 // skip potential whitespaces after the comma
10610 while (*substring == ' ' || *substring == '\t')
10613 return string_has_parameter(substring, s_contained);
10616 static int get_anim_parameter_value(char *s)
10618 int event_value[] =
10626 char *pattern_1[] =
10634 char *pattern_2 = ".part_";
10635 char *matching_char = NULL;
10637 int pattern_1_len = 0;
10638 int result = ANIM_EVENT_NONE;
10641 for (i = 0; i < ARRAY_SIZE(event_value); i++)
10643 matching_char = strstr(s_ptr, pattern_1[i]);
10644 pattern_1_len = strlen(pattern_1[i]);
10645 result = event_value[i];
10647 if (matching_char != NULL)
10651 if (matching_char == NULL)
10652 return ANIM_EVENT_NONE;
10654 s_ptr = matching_char + pattern_1_len;
10656 // check for main animation number ("anim_X" or "anim_XX")
10657 if (*s_ptr >= '0' && *s_ptr <= '9')
10659 int gic_anim_nr = (*s_ptr++ - '0');
10661 if (*s_ptr >= '0' && *s_ptr <= '9')
10662 gic_anim_nr = 10 * gic_anim_nr + (*s_ptr++ - '0');
10664 if (gic_anim_nr < 1 || gic_anim_nr > MAX_GLOBAL_ANIMS)
10665 return ANIM_EVENT_NONE;
10667 result |= gic_anim_nr << ANIM_EVENT_ANIM_BIT;
10671 // invalid main animation number specified
10673 return ANIM_EVENT_NONE;
10676 // check for animation part number ("part_X" or "part_XX") (optional)
10677 if (strPrefix(s_ptr, pattern_2))
10679 s_ptr += strlen(pattern_2);
10681 if (*s_ptr >= '0' && *s_ptr <= '9')
10683 int gic_part_nr = (*s_ptr++ - '0');
10685 if (*s_ptr >= '0' && *s_ptr <= '9')
10686 gic_part_nr = 10 * gic_part_nr + (*s_ptr++ - '0');
10688 if (gic_part_nr < 1 || gic_part_nr > MAX_GLOBAL_ANIM_PARTS)
10689 return ANIM_EVENT_NONE;
10691 result |= gic_part_nr << ANIM_EVENT_PART_BIT;
10695 // invalid animation part number specified
10697 return ANIM_EVENT_NONE;
10701 // discard result if next character is neither delimiter nor whitespace
10702 if (!(*s_ptr == ',' || *s_ptr == '\0' ||
10703 *s_ptr == ' ' || *s_ptr == '\t'))
10704 return ANIM_EVENT_NONE;
10709 static int get_anim_parameter_values(char *s)
10711 int list_pos = ANIM_EVENT_UNDEFINED;
10712 int event_value = ANIM_EVENT_DEFAULT;
10714 if (string_has_parameter(s, "any"))
10715 event_value |= ANIM_EVENT_ANY;
10717 if (string_has_parameter(s, "click:self") ||
10718 string_has_parameter(s, "click") ||
10719 string_has_parameter(s, "self"))
10720 event_value |= ANIM_EVENT_SELF;
10722 if (string_has_parameter(s, "unclick:any"))
10723 event_value |= ANIM_EVENT_UNCLICK_ANY;
10725 // if animation event found, add it to global animation event list
10726 if (event_value != ANIM_EVENT_NONE)
10727 list_pos = AddGlobalAnimEventValue(list_pos, event_value);
10731 // add optional "click:anim_X" or "click:anim_X.part_X" parameter
10732 event_value = get_anim_parameter_value(s);
10734 // if animation event found, add it to global animation event list
10735 if (event_value != ANIM_EVENT_NONE)
10736 list_pos = AddGlobalAnimEventValue(list_pos, event_value);
10738 // continue with next part of the string, starting with next comma
10739 s = strchr(s + 1, ',');
10745 static int get_anim_action_parameter_value(char *token)
10747 // check most common default case first to massively speed things up
10748 if (strEqual(token, ARG_UNDEFINED))
10749 return ANIM_EVENT_ACTION_NONE;
10751 int result = getImageIDFromToken(token);
10755 char *gfx_token = getStringCat2("gfx.", token);
10757 result = getImageIDFromToken(gfx_token);
10759 checked_free(gfx_token);
10764 Key key = getKeyFromX11KeyName(token);
10766 if (key != KSYM_UNDEFINED)
10767 result = -(int)key;
10771 result = ANIM_EVENT_ACTION_NONE;
10776 int get_parameter_value(char *value_raw, char *suffix, int type)
10778 char *value = getStringToLower(value_raw);
10779 int result = 0; // probably a save default value
10781 if (strEqual(suffix, ".direction"))
10783 result = (strEqual(value, "left") ? MV_LEFT :
10784 strEqual(value, "right") ? MV_RIGHT :
10785 strEqual(value, "up") ? MV_UP :
10786 strEqual(value, "down") ? MV_DOWN : MV_NONE);
10788 else if (strEqual(suffix, ".position"))
10790 result = (strEqual(value, "left") ? POS_LEFT :
10791 strEqual(value, "right") ? POS_RIGHT :
10792 strEqual(value, "top") ? POS_TOP :
10793 strEqual(value, "upper") ? POS_UPPER :
10794 strEqual(value, "middle") ? POS_MIDDLE :
10795 strEqual(value, "lower") ? POS_LOWER :
10796 strEqual(value, "bottom") ? POS_BOTTOM :
10797 strEqual(value, "any") ? POS_ANY :
10798 strEqual(value, "last") ? POS_LAST : POS_UNDEFINED);
10800 else if (strEqual(suffix, ".align"))
10802 result = (strEqual(value, "left") ? ALIGN_LEFT :
10803 strEqual(value, "right") ? ALIGN_RIGHT :
10804 strEqual(value, "center") ? ALIGN_CENTER :
10805 strEqual(value, "middle") ? ALIGN_CENTER : ALIGN_DEFAULT);
10807 else if (strEqual(suffix, ".valign"))
10809 result = (strEqual(value, "top") ? VALIGN_TOP :
10810 strEqual(value, "bottom") ? VALIGN_BOTTOM :
10811 strEqual(value, "middle") ? VALIGN_MIDDLE :
10812 strEqual(value, "center") ? VALIGN_MIDDLE : VALIGN_DEFAULT);
10814 else if (strEqual(suffix, ".anim_mode"))
10816 result = (string_has_parameter(value, "none") ? ANIM_NONE :
10817 string_has_parameter(value, "loop") ? ANIM_LOOP :
10818 string_has_parameter(value, "linear") ? ANIM_LINEAR :
10819 string_has_parameter(value, "pingpong") ? ANIM_PINGPONG :
10820 string_has_parameter(value, "pingpong2") ? ANIM_PINGPONG2 :
10821 string_has_parameter(value, "random") ? ANIM_RANDOM :
10822 string_has_parameter(value, "ce_value") ? ANIM_CE_VALUE :
10823 string_has_parameter(value, "ce_score") ? ANIM_CE_SCORE :
10824 string_has_parameter(value, "ce_delay") ? ANIM_CE_DELAY :
10825 string_has_parameter(value, "horizontal") ? ANIM_HORIZONTAL :
10826 string_has_parameter(value, "vertical") ? ANIM_VERTICAL :
10827 string_has_parameter(value, "centered") ? ANIM_CENTERED :
10828 string_has_parameter(value, "all") ? ANIM_ALL :
10831 if (string_has_parameter(value, "once"))
10832 result |= ANIM_ONCE;
10834 if (string_has_parameter(value, "reverse"))
10835 result |= ANIM_REVERSE;
10837 if (string_has_parameter(value, "opaque_player"))
10838 result |= ANIM_OPAQUE_PLAYER;
10840 if (string_has_parameter(value, "static_panel"))
10841 result |= ANIM_STATIC_PANEL;
10843 else if (strEqual(suffix, ".init_event") ||
10844 strEqual(suffix, ".anim_event"))
10846 result = get_anim_parameter_values(value);
10848 else if (strEqual(suffix, ".init_delay_action") ||
10849 strEqual(suffix, ".anim_delay_action") ||
10850 strEqual(suffix, ".post_delay_action") ||
10851 strEqual(suffix, ".init_event_action") ||
10852 strEqual(suffix, ".anim_event_action"))
10854 result = get_anim_action_parameter_value(value_raw);
10856 else if (strEqual(suffix, ".class"))
10858 result = (strEqual(value, ARG_UNDEFINED) ? ARG_UNDEFINED_VALUE :
10859 get_hash_from_key(value));
10861 else if (strEqual(suffix, ".style"))
10863 result = STYLE_DEFAULT;
10865 if (string_has_parameter(value, "accurate_borders"))
10866 result |= STYLE_ACCURATE_BORDERS;
10868 if (string_has_parameter(value, "inner_corners"))
10869 result |= STYLE_INNER_CORNERS;
10871 if (string_has_parameter(value, "reverse"))
10872 result |= STYLE_REVERSE;
10874 if (string_has_parameter(value, "leftmost_position"))
10875 result |= STYLE_LEFTMOST_POSITION;
10877 if (string_has_parameter(value, "block_clicks"))
10878 result |= STYLE_BLOCK;
10880 if (string_has_parameter(value, "passthrough_clicks"))
10881 result |= STYLE_PASSTHROUGH;
10883 if (string_has_parameter(value, "multiple_actions"))
10884 result |= STYLE_MULTIPLE_ACTIONS;
10886 else if (strEqual(suffix, ".fade_mode"))
10888 result = (string_has_parameter(value, "none") ? FADE_MODE_NONE :
10889 string_has_parameter(value, "fade") ? FADE_MODE_FADE :
10890 string_has_parameter(value, "crossfade") ? FADE_MODE_CROSSFADE :
10891 string_has_parameter(value, "melt") ? FADE_MODE_MELT :
10892 string_has_parameter(value, "curtain") ? FADE_MODE_CURTAIN :
10893 FADE_MODE_DEFAULT);
10895 else if (strEqual(suffix, ".auto_delay_unit"))
10897 result = (string_has_parameter(value, "ms") ? AUTO_DELAY_UNIT_MS :
10898 string_has_parameter(value, "frames") ? AUTO_DELAY_UNIT_FRAMES :
10899 AUTO_DELAY_UNIT_DEFAULT);
10901 else if (strPrefix(suffix, ".font")) // (may also be ".font_xyz")
10903 result = gfx.get_font_from_token_function(value);
10905 else // generic parameter of type integer or boolean
10907 result = (strEqual(value, ARG_UNDEFINED) ? ARG_UNDEFINED_VALUE :
10908 type == TYPE_INTEGER ? get_integer_from_string(value) :
10909 type == TYPE_BOOLEAN ? get_boolean_from_string(value) :
10910 ARG_UNDEFINED_VALUE);
10918 static int get_token_parameter_value(char *token, char *value_raw)
10922 if (token == NULL || value_raw == NULL)
10923 return ARG_UNDEFINED_VALUE;
10925 suffix = strrchr(token, '.');
10926 if (suffix == NULL)
10929 if (strEqual(suffix, ".element"))
10930 return getElementFromToken(value_raw);
10932 // !!! USE CORRECT VALUE TYPE (currently works also for TYPE_BOOLEAN) !!!
10933 return get_parameter_value(value_raw, suffix, TYPE_INTEGER);
10936 void InitMenuDesignSettings_Static(void)
10940 // always start with reliable default values from static default config
10941 for (i = 0; image_config_vars[i].token != NULL; i++)
10943 char *value = getHashEntry(image_config_hash, image_config_vars[i].token);
10946 *image_config_vars[i].value =
10947 get_token_parameter_value(image_config_vars[i].token, value);
10951 static void InitMenuDesignSettings_SpecialPreProcessing(void)
10955 // the following initializes hierarchical values from static configuration
10957 // special case: initialize "ARG_DEFAULT" values in static default config
10958 // (e.g., initialize "[titlemessage].fade_mode" from "[title].fade_mode")
10959 titlescreen_initial_first_default.fade_mode =
10960 title_initial_first_default.fade_mode;
10961 titlescreen_initial_first_default.fade_delay =
10962 title_initial_first_default.fade_delay;
10963 titlescreen_initial_first_default.post_delay =
10964 title_initial_first_default.post_delay;
10965 titlescreen_initial_first_default.auto_delay =
10966 title_initial_first_default.auto_delay;
10967 titlescreen_initial_first_default.auto_delay_unit =
10968 title_initial_first_default.auto_delay_unit;
10969 titlescreen_first_default.fade_mode = title_first_default.fade_mode;
10970 titlescreen_first_default.fade_delay = title_first_default.fade_delay;
10971 titlescreen_first_default.post_delay = title_first_default.post_delay;
10972 titlescreen_first_default.auto_delay = title_first_default.auto_delay;
10973 titlescreen_first_default.auto_delay_unit =
10974 title_first_default.auto_delay_unit;
10975 titlemessage_initial_first_default.fade_mode =
10976 title_initial_first_default.fade_mode;
10977 titlemessage_initial_first_default.fade_delay =
10978 title_initial_first_default.fade_delay;
10979 titlemessage_initial_first_default.post_delay =
10980 title_initial_first_default.post_delay;
10981 titlemessage_initial_first_default.auto_delay =
10982 title_initial_first_default.auto_delay;
10983 titlemessage_initial_first_default.auto_delay_unit =
10984 title_initial_first_default.auto_delay_unit;
10985 titlemessage_first_default.fade_mode = title_first_default.fade_mode;
10986 titlemessage_first_default.fade_delay = title_first_default.fade_delay;
10987 titlemessage_first_default.post_delay = title_first_default.post_delay;
10988 titlemessage_first_default.auto_delay = title_first_default.auto_delay;
10989 titlemessage_first_default.auto_delay_unit =
10990 title_first_default.auto_delay_unit;
10992 titlescreen_initial_default.fade_mode = title_initial_default.fade_mode;
10993 titlescreen_initial_default.fade_delay = title_initial_default.fade_delay;
10994 titlescreen_initial_default.post_delay = title_initial_default.post_delay;
10995 titlescreen_initial_default.auto_delay = title_initial_default.auto_delay;
10996 titlescreen_initial_default.auto_delay_unit =
10997 title_initial_default.auto_delay_unit;
10998 titlescreen_default.fade_mode = title_default.fade_mode;
10999 titlescreen_default.fade_delay = title_default.fade_delay;
11000 titlescreen_default.post_delay = title_default.post_delay;
11001 titlescreen_default.auto_delay = title_default.auto_delay;
11002 titlescreen_default.auto_delay_unit = title_default.auto_delay_unit;
11003 titlemessage_initial_default.fade_mode = title_initial_default.fade_mode;
11004 titlemessage_initial_default.fade_delay = title_initial_default.fade_delay;
11005 titlemessage_initial_default.post_delay = title_initial_default.post_delay;
11006 titlemessage_initial_default.auto_delay_unit =
11007 title_initial_default.auto_delay_unit;
11008 titlemessage_default.fade_mode = title_default.fade_mode;
11009 titlemessage_default.fade_delay = title_default.fade_delay;
11010 titlemessage_default.post_delay = title_default.post_delay;
11011 titlemessage_default.auto_delay = title_default.auto_delay;
11012 titlemessage_default.auto_delay_unit = title_default.auto_delay_unit;
11014 // special case: initialize "ARG_DEFAULT" values in static default config
11015 // (e.g., init "titlemessage_1.fade_mode" from "[titlemessage].fade_mode")
11016 for (i = 0; i < MAX_NUM_TITLE_MESSAGES; i++)
11018 titlescreen_initial_first[i] = titlescreen_initial_first_default;
11019 titlescreen_first[i] = titlescreen_first_default;
11020 titlemessage_initial_first[i] = titlemessage_initial_first_default;
11021 titlemessage_first[i] = titlemessage_first_default;
11023 titlescreen_initial[i] = titlescreen_initial_default;
11024 titlescreen[i] = titlescreen_default;
11025 titlemessage_initial[i] = titlemessage_initial_default;
11026 titlemessage[i] = titlemessage_default;
11029 // special case: initialize "ARG_DEFAULT" values in static default config
11030 // (eg, init "menu.enter_screen.SCORES.xyz" from "menu.enter_screen.xyz")
11031 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
11033 if (i == GFX_SPECIAL_ARG_TITLE) // title values already initialized
11036 menu.enter_screen[i] = menu.enter_screen[GFX_SPECIAL_ARG_DEFAULT];
11037 menu.leave_screen[i] = menu.leave_screen[GFX_SPECIAL_ARG_DEFAULT];
11038 menu.next_screen[i] = menu.next_screen[GFX_SPECIAL_ARG_DEFAULT];
11041 // special case: initialize "ARG_DEFAULT" values in static default config
11042 // (eg, init "viewport.door_1.MAIN.xyz" from "viewport.door_1.xyz")
11043 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
11045 viewport.window[i] = viewport.window[GFX_SPECIAL_ARG_DEFAULT];
11046 viewport.playfield[i] = viewport.playfield[GFX_SPECIAL_ARG_DEFAULT];
11047 viewport.door_1[i] = viewport.door_1[GFX_SPECIAL_ARG_DEFAULT];
11049 if (i == GFX_SPECIAL_ARG_EDITOR) // editor values already initialized
11052 viewport.door_2[i] = viewport.door_2[GFX_SPECIAL_ARG_DEFAULT];
11056 static void InitMenuDesignSettings_SpecialPostProcessing(void)
11060 struct XY *dst, *src;
11062 game_buttons_xy[] =
11064 { &game.button.save, &game.button.stop },
11065 { &game.button.pause2, &game.button.pause },
11066 { &game.button.load, &game.button.play },
11067 { &game.button.undo, &game.button.stop },
11068 { &game.button.redo, &game.button.play },
11074 // special case: initialize later added SETUP list size from LEVELS value
11075 if (menu.list_size[GAME_MODE_SETUP] == -1)
11076 menu.list_size[GAME_MODE_SETUP] = menu.list_size[GAME_MODE_LEVELS];
11078 // set default position for snapshot buttons to stop/pause/play buttons
11079 for (i = 0; game_buttons_xy[i].dst != NULL; i++)
11080 if ((*game_buttons_xy[i].dst).x == -1 &&
11081 (*game_buttons_xy[i].dst).y == -1)
11082 *game_buttons_xy[i].dst = *game_buttons_xy[i].src;
11084 // --------------------------------------------------------------------------
11085 // dynamic viewports (including playfield margins, borders and alignments)
11086 // --------------------------------------------------------------------------
11088 // dynamic viewports currently only supported for landscape mode
11089 int display_width = MAX(video.display_width, video.display_height);
11090 int display_height = MIN(video.display_width, video.display_height);
11092 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
11094 struct RectWithBorder *vp_window = &viewport.window[i];
11095 struct RectWithBorder *vp_playfield = &viewport.playfield[i];
11096 struct RectWithBorder *vp_door_1 = &viewport.door_1[i];
11097 struct RectWithBorder *vp_door_2 = &viewport.door_2[i];
11098 boolean dynamic_window_width = (vp_window->min_width != -1);
11099 boolean dynamic_window_height = (vp_window->min_height != -1);
11100 boolean dynamic_playfield_width = (vp_playfield->min_width != -1);
11101 boolean dynamic_playfield_height = (vp_playfield->min_height != -1);
11103 // adjust window size if min/max width/height is specified
11105 if (vp_window->min_width != -1)
11107 int window_width = display_width;
11109 // when using static window height, use aspect ratio of display
11110 if (vp_window->min_height == -1)
11111 window_width = vp_window->height * display_width / display_height;
11113 vp_window->width = MAX(vp_window->min_width, window_width);
11116 if (vp_window->min_height != -1)
11118 int window_height = display_height;
11120 // when using static window width, use aspect ratio of display
11121 if (vp_window->min_width == -1)
11122 window_height = vp_window->width * display_height / display_width;
11124 vp_window->height = MAX(vp_window->min_height, window_height);
11127 if (vp_window->max_width != -1)
11128 vp_window->width = MIN(vp_window->width, vp_window->max_width);
11130 if (vp_window->max_height != -1)
11131 vp_window->height = MIN(vp_window->height, vp_window->max_height);
11133 int playfield_width = vp_window->width;
11134 int playfield_height = vp_window->height;
11136 // adjust playfield size and position according to specified margins
11138 playfield_width -= vp_playfield->margin_left;
11139 playfield_width -= vp_playfield->margin_right;
11141 playfield_height -= vp_playfield->margin_top;
11142 playfield_height -= vp_playfield->margin_bottom;
11144 // adjust playfield size if min/max width/height is specified
11146 if (vp_playfield->min_width != -1)
11147 vp_playfield->width = MAX(vp_playfield->min_width, playfield_width);
11149 if (vp_playfield->min_height != -1)
11150 vp_playfield->height = MAX(vp_playfield->min_height, playfield_height);
11152 if (vp_playfield->max_width != -1)
11153 vp_playfield->width = MIN(vp_playfield->width, vp_playfield->max_width);
11155 if (vp_playfield->max_height != -1)
11156 vp_playfield->height = MIN(vp_playfield->height,vp_playfield->max_height);
11158 // adjust playfield position according to specified alignment
11160 if (vp_playfield->align == ALIGN_LEFT || vp_playfield->x > 0)
11161 vp_playfield->x = ALIGNED_VP_XPOS(vp_playfield);
11162 else if (vp_playfield->align == ALIGN_CENTER)
11163 vp_playfield->x = playfield_width / 2 - vp_playfield->width / 2;
11164 else if (vp_playfield->align == ALIGN_RIGHT)
11165 vp_playfield->x += playfield_width - vp_playfield->width;
11167 if (vp_playfield->valign == VALIGN_TOP || vp_playfield->y > 0)
11168 vp_playfield->y = ALIGNED_VP_YPOS(vp_playfield);
11169 else if (vp_playfield->valign == VALIGN_MIDDLE)
11170 vp_playfield->y = playfield_height / 2 - vp_playfield->height / 2;
11171 else if (vp_playfield->valign == VALIGN_BOTTOM)
11172 vp_playfield->y += playfield_height - vp_playfield->height;
11174 vp_playfield->x += vp_playfield->margin_left;
11175 vp_playfield->y += vp_playfield->margin_top;
11177 // adjust individual playfield borders if only default border is specified
11179 if (vp_playfield->border_left == -1)
11180 vp_playfield->border_left = vp_playfield->border_size;
11181 if (vp_playfield->border_right == -1)
11182 vp_playfield->border_right = vp_playfield->border_size;
11183 if (vp_playfield->border_top == -1)
11184 vp_playfield->border_top = vp_playfield->border_size;
11185 if (vp_playfield->border_bottom == -1)
11186 vp_playfield->border_bottom = vp_playfield->border_size;
11188 // set dynamic playfield borders if borders are specified as undefined
11189 // (but only if window size was dynamic and playfield size was static)
11191 if (dynamic_window_width && !dynamic_playfield_width)
11193 if (vp_playfield->border_left == -1)
11195 vp_playfield->border_left = (vp_playfield->x -
11196 vp_playfield->margin_left);
11197 vp_playfield->x -= vp_playfield->border_left;
11198 vp_playfield->width += vp_playfield->border_left;
11201 if (vp_playfield->border_right == -1)
11203 vp_playfield->border_right = (vp_window->width -
11205 vp_playfield->width -
11206 vp_playfield->margin_right);
11207 vp_playfield->width += vp_playfield->border_right;
11211 if (dynamic_window_height && !dynamic_playfield_height)
11213 if (vp_playfield->border_top == -1)
11215 vp_playfield->border_top = (vp_playfield->y -
11216 vp_playfield->margin_top);
11217 vp_playfield->y -= vp_playfield->border_top;
11218 vp_playfield->height += vp_playfield->border_top;
11221 if (vp_playfield->border_bottom == -1)
11223 vp_playfield->border_bottom = (vp_window->height -
11225 vp_playfield->height -
11226 vp_playfield->margin_bottom);
11227 vp_playfield->height += vp_playfield->border_bottom;
11231 // adjust playfield size to be a multiple of a defined alignment tile size
11233 int align_size = vp_playfield->align_size;
11234 int playfield_xtiles = vp_playfield->width / align_size;
11235 int playfield_ytiles = vp_playfield->height / align_size;
11236 int playfield_width_corrected = playfield_xtiles * align_size;
11237 int playfield_height_corrected = playfield_ytiles * align_size;
11238 boolean is_playfield_mode = (i == GFX_SPECIAL_ARG_PLAYING ||
11239 i == GFX_SPECIAL_ARG_EDITOR);
11241 if (is_playfield_mode &&
11242 dynamic_playfield_width &&
11243 vp_playfield->width != playfield_width_corrected)
11245 int playfield_xdiff = vp_playfield->width - playfield_width_corrected;
11247 vp_playfield->width = playfield_width_corrected;
11249 if (vp_playfield->align == ALIGN_LEFT)
11251 vp_playfield->border_left += playfield_xdiff;
11253 else if (vp_playfield->align == ALIGN_RIGHT)
11255 vp_playfield->border_right += playfield_xdiff;
11257 else if (vp_playfield->align == ALIGN_CENTER)
11259 int border_left_diff = playfield_xdiff / 2;
11260 int border_right_diff = playfield_xdiff - border_left_diff;
11262 vp_playfield->border_left += border_left_diff;
11263 vp_playfield->border_right += border_right_diff;
11267 if (is_playfield_mode &&
11268 dynamic_playfield_height &&
11269 vp_playfield->height != playfield_height_corrected)
11271 int playfield_ydiff = vp_playfield->height - playfield_height_corrected;
11273 vp_playfield->height = playfield_height_corrected;
11275 if (vp_playfield->valign == VALIGN_TOP)
11277 vp_playfield->border_top += playfield_ydiff;
11279 else if (vp_playfield->align == VALIGN_BOTTOM)
11281 vp_playfield->border_right += playfield_ydiff;
11283 else if (vp_playfield->align == VALIGN_MIDDLE)
11285 int border_top_diff = playfield_ydiff / 2;
11286 int border_bottom_diff = playfield_ydiff - border_top_diff;
11288 vp_playfield->border_top += border_top_diff;
11289 vp_playfield->border_bottom += border_bottom_diff;
11293 // adjust door positions according to specified alignment
11295 for (j = 0; j < 2; j++)
11297 struct RectWithBorder *vp_door = (j == 0 ? vp_door_1 : vp_door_2);
11299 if (vp_door->align == ALIGN_LEFT || vp_door->x > 0)
11300 vp_door->x = ALIGNED_VP_XPOS(vp_door);
11301 else if (vp_door->align == ALIGN_CENTER)
11302 vp_door->x = vp_window->width / 2 - vp_door->width / 2;
11303 else if (vp_door->align == ALIGN_RIGHT)
11304 vp_door->x += vp_window->width - vp_door->width;
11306 if (vp_door->valign == VALIGN_TOP || vp_door->y > 0)
11307 vp_door->y = ALIGNED_VP_YPOS(vp_door);
11308 else if (vp_door->valign == VALIGN_MIDDLE)
11309 vp_door->y = vp_window->height / 2 - vp_door->height / 2;
11310 else if (vp_door->valign == VALIGN_BOTTOM)
11311 vp_door->y += vp_window->height - vp_door->height;
11316 static void InitMenuDesignSettings_SpecialPostProcessing_AfterGraphics(void)
11320 struct XYTileSize *dst, *src;
11323 editor_buttons_xy[] =
11326 &editor.button.element_left, &editor.palette.element_left,
11327 IMG_GFX_EDITOR_BUTTON_ELEMENT_LEFT
11330 &editor.button.element_middle, &editor.palette.element_middle,
11331 IMG_GFX_EDITOR_BUTTON_ELEMENT_MIDDLE
11334 &editor.button.element_right, &editor.palette.element_right,
11335 IMG_GFX_EDITOR_BUTTON_ELEMENT_RIGHT
11342 // set default position for element buttons to element graphics
11343 for (i = 0; editor_buttons_xy[i].dst != NULL; i++)
11345 if ((*editor_buttons_xy[i].dst).x == -1 &&
11346 (*editor_buttons_xy[i].dst).y == -1)
11348 struct GraphicInfo *gd = &graphic_info[editor_buttons_xy[i].graphic];
11350 gd->width = gd->height = editor_buttons_xy[i].src->tile_size;
11352 *editor_buttons_xy[i].dst = *editor_buttons_xy[i].src;
11356 // adjust editor palette rows and columns if specified to be dynamic
11358 if (editor.palette.cols == -1)
11360 int vp_width = viewport.playfield[GFX_SPECIAL_ARG_EDITOR].width;
11361 int bt_width = graphic_info[IMG_EDITOR_PALETTE_BUTTON].width;
11362 int sc_width = graphic_info[IMG_EDITOR_PALETTE_SCROLLBAR].width;
11364 editor.palette.cols = (vp_width - sc_width) / bt_width;
11366 if (editor.palette.x == -1)
11368 int palette_width = editor.palette.cols * bt_width + sc_width;
11370 editor.palette.x = (vp_width - palette_width) / 2;
11374 if (editor.palette.rows == -1)
11376 int vp_height = viewport.playfield[GFX_SPECIAL_ARG_EDITOR].height;
11377 int bt_height = graphic_info[IMG_EDITOR_PALETTE_BUTTON].height;
11378 int tx_height = getFontHeight(FONT_TEXT_2);
11380 editor.palette.rows = (vp_height - tx_height) / bt_height;
11382 if (editor.palette.y == -1)
11384 int palette_height = editor.palette.rows * bt_height + tx_height;
11386 editor.palette.y = (vp_height - palette_height) / 2;
11391 static void LoadMenuDesignSettingsFromFilename(char *filename)
11393 static struct TitleFadingInfo tfi;
11394 static struct TitleMessageInfo tmi;
11395 static struct TokenInfo title_tokens[] =
11397 { TYPE_INTEGER, &tfi.fade_mode, ".fade_mode" },
11398 { TYPE_INTEGER, &tfi.fade_delay, ".fade_delay" },
11399 { TYPE_INTEGER, &tfi.post_delay, ".post_delay" },
11400 { TYPE_INTEGER, &tfi.auto_delay, ".auto_delay" },
11401 { TYPE_INTEGER, &tfi.auto_delay_unit, ".auto_delay_unit" },
11405 static struct TokenInfo titlemessage_tokens[] =
11407 { TYPE_INTEGER, &tmi.x, ".x" },
11408 { TYPE_INTEGER, &tmi.y, ".y" },
11409 { TYPE_INTEGER, &tmi.width, ".width" },
11410 { TYPE_INTEGER, &tmi.height, ".height" },
11411 { TYPE_INTEGER, &tmi.chars, ".chars" },
11412 { TYPE_INTEGER, &tmi.lines, ".lines" },
11413 { TYPE_INTEGER, &tmi.align, ".align" },
11414 { TYPE_INTEGER, &tmi.valign, ".valign" },
11415 { TYPE_INTEGER, &tmi.font, ".font" },
11416 { TYPE_BOOLEAN, &tmi.autowrap, ".autowrap" },
11417 { TYPE_BOOLEAN, &tmi.centered, ".centered" },
11418 { TYPE_BOOLEAN, &tmi.parse_comments, ".parse_comments" },
11419 { TYPE_INTEGER, &tmi.sort_priority, ".sort_priority" },
11420 { TYPE_INTEGER, &tmi.fade_mode, ".fade_mode" },
11421 { TYPE_INTEGER, &tmi.fade_delay, ".fade_delay" },
11422 { TYPE_INTEGER, &tmi.post_delay, ".post_delay" },
11423 { TYPE_INTEGER, &tmi.auto_delay, ".auto_delay" },
11424 { TYPE_INTEGER, &tmi.auto_delay_unit, ".auto_delay_unit" },
11430 struct TitleFadingInfo *info;
11435 // initialize first titles from "enter screen" definitions, if defined
11436 { &title_initial_first_default, "menu.enter_screen.TITLE" },
11437 { &title_first_default, "menu.enter_screen.TITLE" },
11439 // initialize title screens from "next screen" definitions, if defined
11440 { &title_initial_default, "menu.next_screen.TITLE" },
11441 { &title_default, "menu.next_screen.TITLE" },
11447 struct TitleMessageInfo *array;
11450 titlemessage_arrays[] =
11452 // initialize first titles from "enter screen" definitions, if defined
11453 { titlescreen_initial_first, "menu.enter_screen.TITLE" },
11454 { titlescreen_first, "menu.enter_screen.TITLE" },
11455 { titlemessage_initial_first, "menu.enter_screen.TITLE" },
11456 { titlemessage_first, "menu.enter_screen.TITLE" },
11458 // initialize titles from "next screen" definitions, if defined
11459 { titlescreen_initial, "menu.next_screen.TITLE" },
11460 { titlescreen, "menu.next_screen.TITLE" },
11461 { titlemessage_initial, "menu.next_screen.TITLE" },
11462 { titlemessage, "menu.next_screen.TITLE" },
11464 // overwrite titles with title definitions, if defined
11465 { titlescreen_initial_first, "[title_initial]" },
11466 { titlescreen_first, "[title]" },
11467 { titlemessage_initial_first, "[title_initial]" },
11468 { titlemessage_first, "[title]" },
11470 { titlescreen_initial, "[title_initial]" },
11471 { titlescreen, "[title]" },
11472 { titlemessage_initial, "[title_initial]" },
11473 { titlemessage, "[title]" },
11475 // overwrite titles with title screen/message definitions, if defined
11476 { titlescreen_initial_first, "[titlescreen_initial]" },
11477 { titlescreen_first, "[titlescreen]" },
11478 { titlemessage_initial_first, "[titlemessage_initial]" },
11479 { titlemessage_first, "[titlemessage]" },
11481 { titlescreen_initial, "[titlescreen_initial]" },
11482 { titlescreen, "[titlescreen]" },
11483 { titlemessage_initial, "[titlemessage_initial]" },
11484 { titlemessage, "[titlemessage]" },
11488 SetupFileHash *setup_file_hash;
11491 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
11494 // the following initializes hierarchical values from dynamic configuration
11496 // special case: initialize with default values that may be overwritten
11497 // (e.g., init "menu.draw_xoffset.INFO" from "menu.draw_xoffset")
11498 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
11500 struct TokenIntPtrInfo menu_config[] =
11502 { "menu.draw_xoffset", &menu.draw_xoffset[i] },
11503 { "menu.draw_yoffset", &menu.draw_yoffset[i] },
11504 { "menu.list_size", &menu.list_size[i] }
11507 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
11509 char *token = menu_config[j].token;
11510 char *value = getHashEntry(setup_file_hash, token);
11513 *menu_config[j].value = get_integer_from_string(value);
11517 // special case: initialize with default values that may be overwritten
11518 // (eg, init "menu.draw_xoffset.INFO[XXX]" from "menu.draw_xoffset.INFO")
11519 for (i = 0; i < NUM_SPECIAL_GFX_INFO_ARGS; i++)
11521 struct TokenIntPtrInfo menu_config[] =
11523 { "menu.draw_xoffset.INFO", &menu.draw_xoffset_info[i] },
11524 { "menu.draw_yoffset.INFO", &menu.draw_yoffset_info[i] },
11525 { "menu.list_size.INFO", &menu.list_size_info[i] }
11528 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
11530 char *token = menu_config[j].token;
11531 char *value = getHashEntry(setup_file_hash, token);
11534 *menu_config[j].value = get_integer_from_string(value);
11538 // special case: initialize with default values that may be overwritten
11539 // (eg, init "menu.draw_xoffset.SETUP[XXX]" from "menu.draw_xoffset.SETUP")
11540 for (i = 0; i < NUM_SPECIAL_GFX_SETUP_ARGS; i++)
11542 struct TokenIntPtrInfo menu_config[] =
11544 { "menu.draw_xoffset.SETUP", &menu.draw_xoffset_setup[i] },
11545 { "menu.draw_yoffset.SETUP", &menu.draw_yoffset_setup[i] }
11548 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
11550 char *token = menu_config[j].token;
11551 char *value = getHashEntry(setup_file_hash, token);
11554 *menu_config[j].value = get_integer_from_string(value);
11558 // special case: initialize with default values that may be overwritten
11559 // (eg, init "menu.line_spacing.INFO[XXX]" from "menu.line_spacing.INFO")
11560 for (i = 0; i < NUM_SPECIAL_GFX_INFO_ARGS; i++)
11562 struct TokenIntPtrInfo menu_config[] =
11564 { "menu.left_spacing.INFO", &menu.left_spacing_info[i] },
11565 { "menu.right_spacing.INFO", &menu.right_spacing_info[i] },
11566 { "menu.top_spacing.INFO", &menu.top_spacing_info[i] },
11567 { "menu.bottom_spacing.INFO", &menu.bottom_spacing_info[i] },
11568 { "menu.paragraph_spacing.INFO", &menu.paragraph_spacing_info[i] },
11569 { "menu.headline1_spacing.INFO", &menu.headline1_spacing_info[i] },
11570 { "menu.headline2_spacing.INFO", &menu.headline2_spacing_info[i] },
11571 { "menu.line_spacing.INFO", &menu.line_spacing_info[i] },
11572 { "menu.extra_spacing.INFO", &menu.extra_spacing_info[i] },
11575 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
11577 char *token = menu_config[j].token;
11578 char *value = getHashEntry(setup_file_hash, token);
11581 *menu_config[j].value = get_integer_from_string(value);
11585 // special case: initialize with default values that may be overwritten
11586 // (eg, init "menu.enter_screen.SCORES.xyz" from "menu.enter_screen.xyz")
11587 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
11589 struct TokenIntPtrInfo menu_config[] =
11591 { "menu.enter_screen.fade_mode", &menu.enter_screen[i].fade_mode },
11592 { "menu.enter_screen.fade_delay", &menu.enter_screen[i].fade_delay },
11593 { "menu.enter_screen.post_delay", &menu.enter_screen[i].post_delay },
11594 { "menu.leave_screen.fade_mode", &menu.leave_screen[i].fade_mode },
11595 { "menu.leave_screen.fade_delay", &menu.leave_screen[i].fade_delay },
11596 { "menu.leave_screen.post_delay", &menu.leave_screen[i].post_delay },
11597 { "menu.next_screen.fade_mode", &menu.next_screen[i].fade_mode },
11598 { "menu.next_screen.fade_delay", &menu.next_screen[i].fade_delay },
11599 { "menu.next_screen.post_delay", &menu.next_screen[i].post_delay }
11602 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
11604 char *token = menu_config[j].token;
11605 char *value = getHashEntry(setup_file_hash, token);
11608 *menu_config[j].value = get_token_parameter_value(token, value);
11612 // special case: initialize with default values that may be overwritten
11613 // (eg, init "viewport.door_1.MAIN.xyz" from "viewport.door_1.xyz")
11614 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
11618 char *token_prefix;
11619 struct RectWithBorder *struct_ptr;
11623 { "viewport.window", &viewport.window[i] },
11624 { "viewport.playfield", &viewport.playfield[i] },
11625 { "viewport.door_1", &viewport.door_1[i] },
11626 { "viewport.door_2", &viewport.door_2[i] }
11629 for (j = 0; j < ARRAY_SIZE(vp_struct); j++)
11631 struct TokenIntPtrInfo vp_config[] =
11633 { ".x", &vp_struct[j].struct_ptr->x },
11634 { ".y", &vp_struct[j].struct_ptr->y },
11635 { ".width", &vp_struct[j].struct_ptr->width },
11636 { ".height", &vp_struct[j].struct_ptr->height },
11637 { ".min_width", &vp_struct[j].struct_ptr->min_width },
11638 { ".min_height", &vp_struct[j].struct_ptr->min_height },
11639 { ".max_width", &vp_struct[j].struct_ptr->max_width },
11640 { ".max_height", &vp_struct[j].struct_ptr->max_height },
11641 { ".margin_left", &vp_struct[j].struct_ptr->margin_left },
11642 { ".margin_right", &vp_struct[j].struct_ptr->margin_right },
11643 { ".margin_top", &vp_struct[j].struct_ptr->margin_top },
11644 { ".margin_bottom", &vp_struct[j].struct_ptr->margin_bottom },
11645 { ".border_left", &vp_struct[j].struct_ptr->border_left },
11646 { ".border_right", &vp_struct[j].struct_ptr->border_right },
11647 { ".border_top", &vp_struct[j].struct_ptr->border_top },
11648 { ".border_bottom", &vp_struct[j].struct_ptr->border_bottom },
11649 { ".border_size", &vp_struct[j].struct_ptr->border_size },
11650 { ".align_size", &vp_struct[j].struct_ptr->align_size },
11651 { ".align", &vp_struct[j].struct_ptr->align },
11652 { ".valign", &vp_struct[j].struct_ptr->valign }
11655 for (k = 0; k < ARRAY_SIZE(vp_config); k++)
11657 char *token = getStringCat2(vp_struct[j].token_prefix,
11658 vp_config[k].token);
11659 char *value = getHashEntry(setup_file_hash, token);
11662 *vp_config[k].value = get_token_parameter_value(token, value);
11669 // special case: initialize with default values that may be overwritten
11670 // (e.g., init "[title].fade_mode" from "menu.next_screen.TITLE.fade_mode")
11671 for (i = 0; title_info[i].info != NULL; i++)
11673 struct TitleFadingInfo *info = title_info[i].info;
11674 char *base_token = title_info[i].text;
11676 for (j = 0; title_tokens[j].type != -1; j++)
11678 char *token = getStringCat2(base_token, title_tokens[j].text);
11679 char *value = getHashEntry(setup_file_hash, token);
11683 int parameter_value = get_token_parameter_value(token, value);
11687 *(int *)title_tokens[j].value = (int)parameter_value;
11696 // special case: initialize with default values that may be overwritten
11697 // (e.g., init "titlemessage_1.fade_mode" from "[titlemessage].fade_mode")
11698 for (i = 0; titlemessage_arrays[i].array != NULL; i++)
11700 struct TitleMessageInfo *array = titlemessage_arrays[i].array;
11701 char *base_token = titlemessage_arrays[i].text;
11703 for (j = 0; titlemessage_tokens[j].type != -1; j++)
11705 char *token = getStringCat2(base_token, titlemessage_tokens[j].text);
11706 char *value = getHashEntry(setup_file_hash, token);
11710 int parameter_value = get_token_parameter_value(token, value);
11712 for (k = 0; k < MAX_NUM_TITLE_MESSAGES; k++)
11716 if (titlemessage_tokens[j].type == TYPE_INTEGER)
11717 *(int *)titlemessage_tokens[j].value = (int)parameter_value;
11719 *(boolean *)titlemessage_tokens[j].value = (boolean)parameter_value;
11729 // special case: check if network and preview player positions are redefined,
11730 // to compare this later against the main menu level preview being redefined
11731 struct TokenIntPtrInfo menu_config_players[] =
11733 { "main.network_players.x", &menu.main.network_players.redefined },
11734 { "main.network_players.y", &menu.main.network_players.redefined },
11735 { "main.preview_players.x", &menu.main.preview_players.redefined },
11736 { "main.preview_players.y", &menu.main.preview_players.redefined },
11737 { "preview.x", &preview.redefined },
11738 { "preview.y", &preview.redefined }
11741 for (i = 0; i < ARRAY_SIZE(menu_config_players); i++)
11742 *menu_config_players[i].value = FALSE;
11744 for (i = 0; i < ARRAY_SIZE(menu_config_players); i++)
11745 if (getHashEntry(setup_file_hash, menu_config_players[i].token) != NULL)
11746 *menu_config_players[i].value = TRUE;
11748 // read (and overwrite with) values that may be specified in config file
11749 for (i = 0; image_config_vars[i].token != NULL; i++)
11751 char *value = getHashEntry(setup_file_hash, image_config_vars[i].token);
11753 // (ignore definitions set to "[DEFAULT]" which are already initialized)
11754 if (value != NULL && !strEqual(value, ARG_DEFAULT))
11755 *image_config_vars[i].value =
11756 get_token_parameter_value(image_config_vars[i].token, value);
11759 freeSetupFileHash(setup_file_hash);
11762 void LoadMenuDesignSettings(void)
11764 char *filename_base = UNDEFINED_FILENAME, *filename_local;
11766 InitMenuDesignSettings_Static();
11767 InitMenuDesignSettings_SpecialPreProcessing();
11769 if (!GFX_OVERRIDE_ARTWORK(ARTWORK_TYPE_GRAPHICS))
11771 // first look for special settings configured in level series config
11772 filename_base = getCustomArtworkLevelConfigFilename(ARTWORK_TYPE_GRAPHICS);
11774 if (fileExists(filename_base))
11775 LoadMenuDesignSettingsFromFilename(filename_base);
11778 filename_local = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
11780 if (filename_local != NULL && !strEqual(filename_base, filename_local))
11781 LoadMenuDesignSettingsFromFilename(filename_local);
11783 InitMenuDesignSettings_SpecialPostProcessing();
11786 void LoadMenuDesignSettings_AfterGraphics(void)
11788 InitMenuDesignSettings_SpecialPostProcessing_AfterGraphics();
11791 void LoadUserDefinedEditorElementList(int **elements, int *num_elements)
11793 char *filename = getEditorSetupFilename();
11794 SetupFileList *setup_file_list, *list;
11795 SetupFileHash *element_hash;
11796 int num_unknown_tokens = 0;
11799 if ((setup_file_list = loadSetupFileList(filename)) == NULL)
11802 element_hash = newSetupFileHash();
11804 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
11805 setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
11807 // determined size may be larger than needed (due to unknown elements)
11809 for (list = setup_file_list; list != NULL; list = list->next)
11812 // add space for up to 3 more elements for padding that may be needed
11813 *num_elements += 3;
11815 // free memory for old list of elements, if needed
11816 checked_free(*elements);
11818 // allocate memory for new list of elements
11819 *elements = checked_malloc(*num_elements * sizeof(int));
11822 for (list = setup_file_list; list != NULL; list = list->next)
11824 char *value = getHashEntry(element_hash, list->token);
11826 if (value == NULL) // try to find obsolete token mapping
11828 char *mapped_token = get_mapped_token(list->token);
11830 if (mapped_token != NULL)
11832 value = getHashEntry(element_hash, mapped_token);
11834 free(mapped_token);
11840 (*elements)[(*num_elements)++] = atoi(value);
11844 if (num_unknown_tokens == 0)
11847 Warn("unknown token(s) found in config file:");
11848 Warn("- config file: '%s'", filename);
11850 num_unknown_tokens++;
11853 Warn("- token: '%s'", list->token);
11857 if (num_unknown_tokens > 0)
11860 while (*num_elements % 4) // pad with empty elements, if needed
11861 (*elements)[(*num_elements)++] = EL_EMPTY;
11863 freeSetupFileList(setup_file_list);
11864 freeSetupFileHash(element_hash);
11867 for (i = 0; i < *num_elements; i++)
11868 Debug("editor", "element '%s' [%d]\n",
11869 element_info[(*elements)[i]].token_name, (*elements)[i]);
11873 static struct MusicFileInfo *get_music_file_info_ext(char *basename, int music,
11876 SetupFileHash *setup_file_hash = NULL;
11877 struct MusicFileInfo tmp_music_file_info, *new_music_file_info;
11878 char *filename_music, *filename_prefix, *filename_info;
11884 token_to_value_ptr[] =
11886 { "title_header", &tmp_music_file_info.title_header },
11887 { "artist_header", &tmp_music_file_info.artist_header },
11888 { "album_header", &tmp_music_file_info.album_header },
11889 { "year_header", &tmp_music_file_info.year_header },
11891 { "title", &tmp_music_file_info.title },
11892 { "artist", &tmp_music_file_info.artist },
11893 { "album", &tmp_music_file_info.album },
11894 { "year", &tmp_music_file_info.year },
11900 filename_music = (is_sound ? getCustomSoundFilename(basename) :
11901 getCustomMusicFilename(basename));
11903 if (filename_music == NULL)
11906 // ---------- try to replace file extension ----------
11908 filename_prefix = getStringCopy(filename_music);
11909 if (strrchr(filename_prefix, '.') != NULL)
11910 *strrchr(filename_prefix, '.') = '\0';
11911 filename_info = getStringCat2(filename_prefix, ".txt");
11913 if (fileExists(filename_info))
11914 setup_file_hash = loadSetupFileHash(filename_info);
11916 free(filename_prefix);
11917 free(filename_info);
11919 if (setup_file_hash == NULL)
11921 // ---------- try to add file extension ----------
11923 filename_prefix = getStringCopy(filename_music);
11924 filename_info = getStringCat2(filename_prefix, ".txt");
11926 if (fileExists(filename_info))
11927 setup_file_hash = loadSetupFileHash(filename_info);
11929 free(filename_prefix);
11930 free(filename_info);
11933 if (setup_file_hash == NULL)
11936 // ---------- music file info found ----------
11938 clear_mem(&tmp_music_file_info, sizeof(struct MusicFileInfo));
11940 for (i = 0; token_to_value_ptr[i].token != NULL; i++)
11942 char *value = getHashEntry(setup_file_hash, token_to_value_ptr[i].token);
11944 *token_to_value_ptr[i].value_ptr =
11945 getStringCopy(value != NULL && *value != '\0' ? value : UNKNOWN_NAME);
11948 tmp_music_file_info.basename = getStringCopy(basename);
11949 tmp_music_file_info.music = music;
11950 tmp_music_file_info.is_sound = is_sound;
11952 new_music_file_info = checked_malloc(sizeof(struct MusicFileInfo));
11953 *new_music_file_info = tmp_music_file_info;
11955 return new_music_file_info;
11958 static struct MusicFileInfo *get_music_file_info(char *basename, int music)
11960 return get_music_file_info_ext(basename, music, FALSE);
11963 static struct MusicFileInfo *get_sound_file_info(char *basename, int sound)
11965 return get_music_file_info_ext(basename, sound, TRUE);
11968 static boolean music_info_listed_ext(struct MusicFileInfo *list,
11969 char *basename, boolean is_sound)
11971 for (; list != NULL; list = list->next)
11972 if (list->is_sound == is_sound && strEqual(list->basename, basename))
11978 static boolean music_info_listed(struct MusicFileInfo *list, char *basename)
11980 return music_info_listed_ext(list, basename, FALSE);
11983 static boolean sound_info_listed(struct MusicFileInfo *list, char *basename)
11985 return music_info_listed_ext(list, basename, TRUE);
11988 void LoadMusicInfo(void)
11990 char *music_directory = getCustomMusicDirectory();
11991 int num_music = getMusicListSize();
11992 int num_music_noconf = 0;
11993 int num_sounds = getSoundListSize();
11995 DirectoryEntry *dir_entry;
11996 struct FileInfo *music, *sound;
11997 struct MusicFileInfo *next, **new;
12000 while (music_file_info != NULL)
12002 next = music_file_info->next;
12004 checked_free(music_file_info->basename);
12006 checked_free(music_file_info->title_header);
12007 checked_free(music_file_info->artist_header);
12008 checked_free(music_file_info->album_header);
12009 checked_free(music_file_info->year_header);
12011 checked_free(music_file_info->title);
12012 checked_free(music_file_info->artist);
12013 checked_free(music_file_info->album);
12014 checked_free(music_file_info->year);
12016 free(music_file_info);
12018 music_file_info = next;
12021 new = &music_file_info;
12023 for (i = 0; i < num_music; i++)
12025 music = getMusicListEntry(i);
12027 if (music->filename == NULL)
12030 if (strEqual(music->filename, UNDEFINED_FILENAME))
12033 // a configured file may be not recognized as music
12034 if (!FileIsMusic(music->filename))
12037 if (!music_info_listed(music_file_info, music->filename))
12039 *new = get_music_file_info(music->filename, i);
12042 new = &(*new)->next;
12046 if ((dir = openDirectory(music_directory)) == NULL)
12048 Warn("cannot read music directory '%s'", music_directory);
12053 while ((dir_entry = readDirectory(dir)) != NULL) // loop all entries
12055 char *basename = dir_entry->basename;
12056 boolean music_already_used = FALSE;
12059 // skip all music files that are configured in music config file
12060 for (i = 0; i < num_music; i++)
12062 music = getMusicListEntry(i);
12064 if (music->filename == NULL)
12067 if (strEqual(basename, music->filename))
12069 music_already_used = TRUE;
12074 if (music_already_used)
12077 if (!FileIsMusic(dir_entry->filename))
12080 if (!music_info_listed(music_file_info, basename))
12082 *new = get_music_file_info(basename, MAP_NOCONF_MUSIC(num_music_noconf));
12085 new = &(*new)->next;
12088 num_music_noconf++;
12091 closeDirectory(dir);
12093 for (i = 0; i < num_sounds; i++)
12095 sound = getSoundListEntry(i);
12097 if (sound->filename == NULL)
12100 if (strEqual(sound->filename, UNDEFINED_FILENAME))
12103 // a configured file may be not recognized as sound
12104 if (!FileIsSound(sound->filename))
12107 if (!sound_info_listed(music_file_info, sound->filename))
12109 *new = get_sound_file_info(sound->filename, i);
12111 new = &(*new)->next;
12116 static void add_helpanim_entry(int element, int action, int direction,
12117 int delay, int *num_list_entries)
12119 struct HelpAnimInfo *new_list_entry;
12120 (*num_list_entries)++;
12123 checked_realloc(helpanim_info,
12124 *num_list_entries * sizeof(struct HelpAnimInfo));
12125 new_list_entry = &helpanim_info[*num_list_entries - 1];
12127 new_list_entry->element = element;
12128 new_list_entry->action = action;
12129 new_list_entry->direction = direction;
12130 new_list_entry->delay = delay;
12133 static void print_unknown_token(char *filename, char *token, int token_nr)
12138 Warn("unknown token(s) found in config file:");
12139 Warn("- config file: '%s'", filename);
12142 Warn("- token: '%s'", token);
12145 static void print_unknown_token_end(int token_nr)
12151 void LoadHelpAnimInfo(void)
12153 char *filename = getHelpAnimFilename();
12154 SetupFileList *setup_file_list = NULL, *list;
12155 SetupFileHash *element_hash, *action_hash, *direction_hash;
12156 int num_list_entries = 0;
12157 int num_unknown_tokens = 0;
12160 if (fileExists(filename))
12161 setup_file_list = loadSetupFileList(filename);
12163 if (setup_file_list == NULL)
12165 // use reliable default values from static configuration
12166 SetupFileList *insert_ptr;
12168 insert_ptr = setup_file_list =
12169 newSetupFileList(helpanim_config[0].token,
12170 helpanim_config[0].value);
12172 for (i = 1; helpanim_config[i].token; i++)
12173 insert_ptr = addListEntry(insert_ptr,
12174 helpanim_config[i].token,
12175 helpanim_config[i].value);
12178 element_hash = newSetupFileHash();
12179 action_hash = newSetupFileHash();
12180 direction_hash = newSetupFileHash();
12182 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
12183 setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
12185 for (i = 0; i < NUM_ACTIONS; i++)
12186 setHashEntry(action_hash, element_action_info[i].suffix,
12187 i_to_a(element_action_info[i].value));
12189 // do not store direction index (bit) here, but direction value!
12190 for (i = 0; i < NUM_DIRECTIONS_FULL; i++)
12191 setHashEntry(direction_hash, element_direction_info[i].suffix,
12192 i_to_a(1 << element_direction_info[i].value));
12194 for (list = setup_file_list; list != NULL; list = list->next)
12196 char *element_token, *action_token, *direction_token;
12197 char *element_value, *action_value, *direction_value;
12198 int delay = atoi(list->value);
12200 if (strEqual(list->token, "end"))
12202 add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
12207 /* first try to break element into element/action/direction parts;
12208 if this does not work, also accept combined "element[.act][.dir]"
12209 elements (like "dynamite.active"), which are unique elements */
12211 if (strchr(list->token, '.') == NULL) // token contains no '.'
12213 element_value = getHashEntry(element_hash, list->token);
12214 if (element_value != NULL) // element found
12215 add_helpanim_entry(atoi(element_value), -1, -1, delay,
12216 &num_list_entries);
12219 // no further suffixes found -- this is not an element
12220 print_unknown_token(filename, list->token, num_unknown_tokens++);
12226 // token has format "<prefix>.<something>"
12228 action_token = strchr(list->token, '.'); // suffix may be action ...
12229 direction_token = action_token; // ... or direction
12231 element_token = getStringCopy(list->token);
12232 *strchr(element_token, '.') = '\0';
12234 element_value = getHashEntry(element_hash, element_token);
12236 if (element_value == NULL) // this is no element
12238 element_value = getHashEntry(element_hash, list->token);
12239 if (element_value != NULL) // combined element found
12240 add_helpanim_entry(atoi(element_value), -1, -1, delay,
12241 &num_list_entries);
12243 print_unknown_token(filename, list->token, num_unknown_tokens++);
12245 free(element_token);
12250 action_value = getHashEntry(action_hash, action_token);
12252 if (action_value != NULL) // action found
12254 add_helpanim_entry(atoi(element_value), atoi(action_value), -1, delay,
12255 &num_list_entries);
12257 free(element_token);
12262 direction_value = getHashEntry(direction_hash, direction_token);
12264 if (direction_value != NULL) // direction found
12266 add_helpanim_entry(atoi(element_value), -1, atoi(direction_value), delay,
12267 &num_list_entries);
12269 free(element_token);
12274 if (strchr(action_token + 1, '.') == NULL)
12276 // no further suffixes found -- this is not an action nor direction
12278 element_value = getHashEntry(element_hash, list->token);
12279 if (element_value != NULL) // combined element found
12280 add_helpanim_entry(atoi(element_value), -1, -1, delay,
12281 &num_list_entries);
12283 print_unknown_token(filename, list->token, num_unknown_tokens++);
12285 free(element_token);
12290 // token has format "<prefix>.<suffix>.<something>"
12292 direction_token = strchr(action_token + 1, '.');
12294 action_token = getStringCopy(action_token);
12295 *strchr(action_token + 1, '.') = '\0';
12297 action_value = getHashEntry(action_hash, action_token);
12299 if (action_value == NULL) // this is no action
12301 element_value = getHashEntry(element_hash, list->token);
12302 if (element_value != NULL) // combined element found
12303 add_helpanim_entry(atoi(element_value), -1, -1, delay,
12304 &num_list_entries);
12306 print_unknown_token(filename, list->token, num_unknown_tokens++);
12308 free(element_token);
12309 free(action_token);
12314 direction_value = getHashEntry(direction_hash, direction_token);
12316 if (direction_value != NULL) // direction found
12318 add_helpanim_entry(atoi(element_value), atoi(action_value),
12319 atoi(direction_value), delay, &num_list_entries);
12321 free(element_token);
12322 free(action_token);
12327 // this is no direction
12329 element_value = getHashEntry(element_hash, list->token);
12330 if (element_value != NULL) // combined element found
12331 add_helpanim_entry(atoi(element_value), -1, -1, delay,
12332 &num_list_entries);
12334 print_unknown_token(filename, list->token, num_unknown_tokens++);
12336 free(element_token);
12337 free(action_token);
12340 print_unknown_token_end(num_unknown_tokens);
12342 add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
12343 add_helpanim_entry(HELPANIM_LIST_END, -1, -1, -1, &num_list_entries);
12345 freeSetupFileList(setup_file_list);
12346 freeSetupFileHash(element_hash);
12347 freeSetupFileHash(action_hash);
12348 freeSetupFileHash(direction_hash);
12351 for (i = 0; i < num_list_entries; i++)
12352 Debug("files:LoadHelpAnimInfo", "'%s': %d, %d, %d => %d",
12353 EL_NAME(helpanim_info[i].element),
12354 helpanim_info[i].element,
12355 helpanim_info[i].action,
12356 helpanim_info[i].direction,
12357 helpanim_info[i].delay);
12361 void LoadHelpTextInfo(void)
12363 char *filename = getHelpTextFilename();
12366 if (helptext_info != NULL)
12368 freeSetupFileHash(helptext_info);
12369 helptext_info = NULL;
12372 if (fileExists(filename))
12373 helptext_info = loadSetupFileHash(filename);
12375 if (helptext_info == NULL)
12377 // use reliable default values from static configuration
12378 helptext_info = newSetupFileHash();
12380 for (i = 0; helptext_config[i].token; i++)
12381 setHashEntry(helptext_info,
12382 helptext_config[i].token,
12383 helptext_config[i].value);
12387 BEGIN_HASH_ITERATION(helptext_info, itr)
12389 Debug("files:LoadHelpTextInfo", "'%s' => '%s'",
12390 HASH_ITERATION_TOKEN(itr), HASH_ITERATION_VALUE(itr));
12392 END_HASH_ITERATION(hash, itr)
12397 // ----------------------------------------------------------------------------
12399 // ----------------------------------------------------------------------------
12401 #define MAX_NUM_CONVERT_LEVELS 1000
12403 void ConvertLevels(void)
12405 static LevelDirTree *convert_leveldir = NULL;
12406 static int convert_level_nr = -1;
12407 static int num_levels_handled = 0;
12408 static int num_levels_converted = 0;
12409 static boolean levels_failed[MAX_NUM_CONVERT_LEVELS];
12412 convert_leveldir = getTreeInfoFromIdentifier(leveldir_first,
12413 global.convert_leveldir);
12415 if (convert_leveldir == NULL)
12416 Fail("no such level identifier: '%s'", global.convert_leveldir);
12418 leveldir_current = convert_leveldir;
12420 if (global.convert_level_nr != -1)
12422 convert_leveldir->first_level = global.convert_level_nr;
12423 convert_leveldir->last_level = global.convert_level_nr;
12426 convert_level_nr = convert_leveldir->first_level;
12428 PrintLine("=", 79);
12429 Print("Converting levels\n");
12430 PrintLine("-", 79);
12431 Print("Level series identifier: '%s'\n", convert_leveldir->identifier);
12432 Print("Level series name: '%s'\n", convert_leveldir->name);
12433 Print("Level series author: '%s'\n", convert_leveldir->author);
12434 Print("Number of levels: %d\n", convert_leveldir->levels);
12435 PrintLine("=", 79);
12438 for (i = 0; i < MAX_NUM_CONVERT_LEVELS; i++)
12439 levels_failed[i] = FALSE;
12441 while (convert_level_nr <= convert_leveldir->last_level)
12443 char *level_filename;
12446 level_nr = convert_level_nr++;
12448 Print("Level %03d: ", level_nr);
12450 LoadLevel(level_nr);
12451 if (level.no_level_file || level.no_valid_file)
12453 Print("(no level)\n");
12457 Print("converting level ... ");
12459 level_filename = getDefaultLevelFilename(level_nr);
12460 new_level = !fileExists(level_filename);
12464 SaveLevel(level_nr);
12466 num_levels_converted++;
12468 Print("converted.\n");
12472 if (level_nr >= 0 && level_nr < MAX_NUM_CONVERT_LEVELS)
12473 levels_failed[level_nr] = TRUE;
12475 Print("NOT CONVERTED -- LEVEL ALREADY EXISTS.\n");
12478 num_levels_handled++;
12482 PrintLine("=", 79);
12483 Print("Number of levels handled: %d\n", num_levels_handled);
12484 Print("Number of levels converted: %d (%d%%)\n", num_levels_converted,
12485 (num_levels_handled ?
12486 num_levels_converted * 100 / num_levels_handled : 0));
12487 PrintLine("-", 79);
12488 Print("Summary (for automatic parsing by scripts):\n");
12489 Print("LEVELDIR '%s', CONVERTED %d/%d (%d%%)",
12490 convert_leveldir->identifier, num_levels_converted,
12491 num_levels_handled,
12492 (num_levels_handled ?
12493 num_levels_converted * 100 / num_levels_handled : 0));
12495 if (num_levels_handled != num_levels_converted)
12497 Print(", FAILED:");
12498 for (i = 0; i < MAX_NUM_CONVERT_LEVELS; i++)
12499 if (levels_failed[i])
12504 PrintLine("=", 79);
12506 CloseAllAndExit(0);
12510 // ----------------------------------------------------------------------------
12511 // create and save images for use in level sketches (raw BMP format)
12512 // ----------------------------------------------------------------------------
12514 void CreateLevelSketchImages(void)
12520 InitElementPropertiesGfxElement();
12522 bitmap1 = CreateBitmap(TILEX, TILEY, DEFAULT_DEPTH);
12523 bitmap2 = CreateBitmap(MINI_TILEX, MINI_TILEY, DEFAULT_DEPTH);
12525 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
12527 int element = getMappedElement(i);
12528 char basename1[16];
12529 char basename2[16];
12533 sprintf(basename1, "%04d.bmp", i);
12534 sprintf(basename2, "%04ds.bmp", i);
12536 filename1 = getPath2(global.create_images_dir, basename1);
12537 filename2 = getPath2(global.create_images_dir, basename2);
12539 DrawSizedElement(0, 0, element, TILESIZE);
12540 BlitBitmap(drawto, bitmap1, SX, SY, TILEX, TILEY, 0, 0);
12542 if (SDL_SaveBMP(bitmap1->surface, filename1) != 0)
12543 Fail("cannot save level sketch image file '%s'", filename1);
12545 DrawSizedElement(0, 0, element, MINI_TILESIZE);
12546 BlitBitmap(drawto, bitmap2, SX, SY, MINI_TILEX, MINI_TILEY, 0, 0);
12548 if (SDL_SaveBMP(bitmap2->surface, filename2) != 0)
12549 Fail("cannot save level sketch image file '%s'", filename2);
12554 // create corresponding SQL statements (for normal and small images)
12557 printf("insert into phpbb_words values (NULL, '`%03d', '<IMG class=\"levelsketch\" src=\"/I/%04d.png\"/>');\n", i, i);
12558 printf("insert into phpbb_words values (NULL, '¸%03d', '<IMG class=\"levelsketch\" src=\"/I/%04ds.png\"/>');\n", i, i);
12561 printf("insert into phpbb_words values (NULL, '`%04d', '<IMG class=\"levelsketch\" src=\"/I/%04d.png\"/>');\n", i, i);
12562 printf("insert into phpbb_words values (NULL, '¸%04d', '<IMG class=\"levelsketch\" src=\"/I/%04ds.png\"/>');\n", i, i);
12564 // optional: create content for forum level sketch demonstration post
12566 fprintf(stderr, "%03d `%03d%c", i, i, (i % 10 < 9 ? ' ' : '\n'));
12569 FreeBitmap(bitmap1);
12570 FreeBitmap(bitmap2);
12573 fprintf(stderr, "\n");
12575 Info("%d normal and small images created", NUM_FILE_ELEMENTS);
12577 CloseAllAndExit(0);
12581 // ----------------------------------------------------------------------------
12582 // create and save images for custom and group elements (raw BMP format)
12583 // ----------------------------------------------------------------------------
12585 void CreateCustomElementImages(char *directory)
12587 char *src_basename = "RocksCE-template.ilbm";
12588 char *dst_basename = "RocksCE.bmp";
12589 char *src_filename = getPath2(directory, src_basename);
12590 char *dst_filename = getPath2(directory, dst_basename);
12591 Bitmap *src_bitmap;
12593 int yoffset_ce = 0;
12594 int yoffset_ge = (TILEY * NUM_CUSTOM_ELEMENTS / 16);
12597 InitVideoDefaults();
12599 ReCreateBitmap(&backbuffer, video.width, video.height);
12601 src_bitmap = LoadImage(src_filename);
12603 bitmap = CreateBitmap(TILEX * 16 * 2,
12604 TILEY * (NUM_CUSTOM_ELEMENTS + NUM_GROUP_ELEMENTS) / 16,
12607 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
12614 BlitBitmap(src_bitmap, bitmap, 0, 0, TILEX, TILEY,
12615 TILEX * x, TILEY * y + yoffset_ce);
12617 BlitBitmap(src_bitmap, bitmap, 0, TILEY,
12619 TILEX * x + TILEX * 16,
12620 TILEY * y + yoffset_ce);
12622 for (j = 2; j >= 0; j--)
12626 BlitBitmap(src_bitmap, bitmap,
12627 TILEX + c * 7, 0, 6, 10,
12628 TILEX * x + 6 + j * 7,
12629 TILEY * y + 11 + yoffset_ce);
12631 BlitBitmap(src_bitmap, bitmap,
12632 TILEX + c * 8, TILEY, 6, 10,
12633 TILEX * 16 + TILEX * x + 6 + j * 8,
12634 TILEY * y + 10 + yoffset_ce);
12640 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
12647 BlitBitmap(src_bitmap, bitmap, 0, 0, TILEX, TILEY,
12648 TILEX * x, TILEY * y + yoffset_ge);
12650 BlitBitmap(src_bitmap, bitmap, 0, TILEY,
12652 TILEX * x + TILEX * 16,
12653 TILEY * y + yoffset_ge);
12655 for (j = 1; j >= 0; j--)
12659 BlitBitmap(src_bitmap, bitmap, TILEX + c * 10, 11, 10, 10,
12660 TILEX * x + 6 + j * 10,
12661 TILEY * y + 11 + yoffset_ge);
12663 BlitBitmap(src_bitmap, bitmap,
12664 TILEX + c * 8, TILEY + 12, 6, 10,
12665 TILEX * 16 + TILEX * x + 10 + j * 8,
12666 TILEY * y + 10 + yoffset_ge);
12672 if (SDL_SaveBMP(bitmap->surface, dst_filename) != 0)
12673 Fail("cannot save CE graphics file '%s'", dst_filename);
12675 FreeBitmap(bitmap);
12677 CloseAllAndExit(0);