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;
8396 static void LoadScore_OLD(int nr)
8399 char *filename = getScoreFilename(nr);
8400 char cookie[MAX_LINE_LEN];
8401 char line[MAX_LINE_LEN];
8405 if (!(file = fopen(filename, MODE_READ)))
8408 // check file identifier
8409 if (fgets(cookie, MAX_LINE_LEN, file) == NULL)
8411 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
8412 cookie[strlen(cookie) - 1] = '\0';
8414 if (!checkCookieString(cookie, SCORE_COOKIE_TMPL))
8416 Warn("unknown format of score file '%s'", filename);
8423 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
8425 if (fscanf(file, "%d", &scores.entry[i].score) == EOF)
8426 Warn("fscanf() failed; %s", strerror(errno));
8428 if (fgets(line, MAX_LINE_LEN, file) == NULL)
8431 if (strlen(line) > 0 && line[strlen(line) - 1] == '\n')
8432 line[strlen(line) - 1] = '\0';
8434 for (line_ptr = line; *line_ptr; line_ptr++)
8436 if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != '\0')
8438 strncpy(scores.entry[i].name, line_ptr, MAX_PLAYER_NAME_LEN);
8439 scores.entry[i].name[MAX_PLAYER_NAME_LEN] = '\0';
8448 static int LoadScore_VERS(File *file, int chunk_size, struct ScoreInfo *scores)
8450 scores->file_version = getFileVersion(file);
8451 scores->game_version = getFileVersion(file);
8456 static int LoadScore_INFO(File *file, int chunk_size, struct ScoreInfo *scores)
8458 char *level_identifier = NULL;
8459 int level_identifier_size;
8462 level_identifier_size = getFile16BitBE(file);
8464 level_identifier = checked_malloc(level_identifier_size);
8466 for (i = 0; i < level_identifier_size; i++)
8467 level_identifier[i] = getFile8Bit(file);
8469 strncpy(scores->level_identifier, level_identifier, MAX_FILENAME_LEN);
8470 scores->level_identifier[MAX_FILENAME_LEN] = '\0';
8472 checked_free(level_identifier);
8474 scores->level_nr = getFile16BitBE(file);
8475 scores->num_entries = getFile16BitBE(file);
8477 chunk_size = 2 + level_identifier_size + 2 + 2;
8482 static int LoadScore_NAME(File *file, int chunk_size, struct ScoreInfo *scores)
8486 for (i = 0; i < scores->num_entries; i++)
8488 for (j = 0; j < MAX_PLAYER_NAME_LEN; j++)
8489 scores->entry[i].name[j] = getFile8Bit(file);
8491 scores->entry[i].name[MAX_PLAYER_NAME_LEN] = '\0';
8494 chunk_size = scores->num_entries * MAX_PLAYER_NAME_LEN;
8499 static int LoadScore_SCOR(File *file, int chunk_size, struct ScoreInfo *scores)
8503 for (i = 0; i < scores->num_entries; i++)
8504 scores->entry[i].score = getFile16BitBE(file);
8506 chunk_size = scores->num_entries * 2;
8511 void LoadScore(int nr)
8513 char *filename = getScoreFilename(nr);
8514 char cookie[MAX_LINE_LEN];
8515 char chunk_name[CHUNK_ID_LEN + 1];
8517 boolean old_score_file_format = FALSE;
8520 // always start with reliable default values
8521 setScoreInfoToDefaults();
8523 if (!(file = openFile(filename, MODE_READ)))
8526 getFileChunkBE(file, chunk_name, NULL);
8527 if (strEqual(chunk_name, "RND1"))
8529 getFile32BitBE(file); // not used
8531 getFileChunkBE(file, chunk_name, NULL);
8532 if (!strEqual(chunk_name, "SCOR"))
8534 Warn("unknown format of score file '%s'", filename);
8541 else // check for old file format with cookie string
8543 strcpy(cookie, chunk_name);
8544 if (getStringFromFile(file, &cookie[4], MAX_LINE_LEN - 4) == NULL)
8546 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
8547 cookie[strlen(cookie) - 1] = '\0';
8549 if (!checkCookieString(cookie, SCORE_COOKIE_TMPL))
8551 Warn("unknown format of score file '%s'", filename);
8558 old_score_file_format = TRUE;
8561 if (old_score_file_format)
8563 // score files from versions before 4.2.4.0 without chunk structure
8572 int (*loader)(File *, int, struct ScoreInfo *);
8576 { "VERS", SCORE_CHUNK_VERS_SIZE, LoadScore_VERS },
8577 { "INFO", -1, LoadScore_INFO },
8578 { "NAME", -1, LoadScore_NAME },
8579 { "SCOR", -1, LoadScore_SCOR },
8584 while (getFileChunkBE(file, chunk_name, &chunk_size))
8588 while (chunk_info[i].name != NULL &&
8589 !strEqual(chunk_name, chunk_info[i].name))
8592 if (chunk_info[i].name == NULL)
8594 Warn("unknown chunk '%s' in score file '%s'",
8595 chunk_name, filename);
8597 ReadUnusedBytesFromFile(file, chunk_size);
8599 else if (chunk_info[i].size != -1 &&
8600 chunk_info[i].size != chunk_size)
8602 Warn("wrong size (%d) of chunk '%s' in score file '%s'",
8603 chunk_size, chunk_name, filename);
8605 ReadUnusedBytesFromFile(file, chunk_size);
8609 // call function to load this score chunk
8610 int chunk_size_expected =
8611 (chunk_info[i].loader)(file, chunk_size, &scores);
8613 // the size of some chunks cannot be checked before reading other
8614 // chunks first (like "HEAD" and "BODY") that contain some header
8615 // information, so check them here
8616 if (chunk_size_expected != chunk_size)
8618 Warn("wrong size (%d) of chunk '%s' in score file '%s'",
8619 chunk_size, chunk_name, filename);
8628 #if ENABLE_HISTORIC_CHUNKS
8629 void SaveScore_OLD(int nr)
8632 int permissions = (program.global_scores ? PERMS_PUBLIC : PERMS_PRIVATE);
8633 char *filename = getScoreFilename(nr);
8636 // used instead of "leveldir_current->subdir" (for network games)
8637 InitScoreDirectory(levelset.identifier);
8639 if (!(file = fopen(filename, MODE_WRITE)))
8641 Warn("cannot save score for level %d", nr);
8646 fprintf(file, "%s\n\n", SCORE_COOKIE);
8648 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
8649 fprintf(file, "%d %s\n", scores.entry[i].score, scores.entry[i].name);
8653 SetFilePermissions(filename, permissions);
8657 static void SaveScore_VERS(FILE *file, struct ScoreInfo *scores)
8659 putFileVersion(file, scores->file_version);
8660 putFileVersion(file, scores->game_version);
8663 static void SaveScore_INFO(FILE *file, struct ScoreInfo *scores)
8665 int level_identifier_size = strlen(scores->level_identifier) + 1;
8668 putFile16BitBE(file, level_identifier_size);
8670 for (i = 0; i < level_identifier_size; i++)
8671 putFile8Bit(file, scores->level_identifier[i]);
8673 putFile16BitBE(file, scores->level_nr);
8674 putFile16BitBE(file, scores->num_entries);
8677 static void SaveScore_NAME(FILE *file, struct ScoreInfo *scores)
8681 for (i = 0; i < scores->num_entries; i++)
8683 int name_size = strlen(scores->entry[i].name);
8685 for (j = 0; j < MAX_PLAYER_NAME_LEN; j++)
8686 putFile8Bit(file, (j < name_size ? scores->entry[i].name[j] : 0));
8690 static void SaveScore_SCOR(FILE *file, struct ScoreInfo *scores)
8694 for (i = 0; i < scores->num_entries; i++)
8695 putFile16BitBE(file, scores->entry[i].score);
8698 static void SaveScoreToFilename(char *filename)
8701 int permissions = (program.global_scores ? PERMS_PUBLIC : PERMS_PRIVATE);
8702 int info_chunk_size;
8703 int name_chunk_size;
8704 int scor_chunk_size;
8706 if (!(file = fopen(filename, MODE_WRITE)))
8708 Warn("cannot save score file '%s'", filename);
8713 info_chunk_size = 2 + (strlen(scores.level_identifier) + 1) + 2 + 2;
8714 name_chunk_size = scores.num_entries * MAX_PLAYER_NAME_LEN;
8715 scor_chunk_size = scores.num_entries * 2;
8717 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
8718 putFileChunkBE(file, "SCOR", CHUNK_SIZE_NONE);
8720 putFileChunkBE(file, "VERS", SCORE_CHUNK_VERS_SIZE);
8721 SaveScore_VERS(file, &scores);
8723 putFileChunkBE(file, "INFO", info_chunk_size);
8724 SaveScore_INFO(file, &scores);
8726 putFileChunkBE(file, "NAME", name_chunk_size);
8727 SaveScore_NAME(file, &scores);
8729 putFileChunkBE(file, "SCOR", scor_chunk_size);
8730 SaveScore_SCOR(file, &scores);
8734 SetFilePermissions(filename, permissions);
8737 void SaveScore(int nr)
8739 char *filename = getScoreFilename(nr);
8742 // used instead of "leveldir_current->subdir" (for network games)
8743 InitScoreDirectory(levelset.identifier);
8745 scores.file_version = FILE_VERSION_ACTUAL;
8746 scores.game_version = GAME_VERSION_ACTUAL;
8748 strncpy(scores.level_identifier, levelset.identifier, MAX_FILENAME_LEN);
8749 scores.level_identifier[MAX_FILENAME_LEN] = '\0';
8750 scores.level_nr = level_nr;
8752 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
8753 if (scores.entry[i].score == 0 &&
8754 strEqual(scores.entry[i].name, EMPTY_PLAYER_NAME))
8757 scores.num_entries = i;
8759 if (scores.num_entries == 0)
8762 SaveScoreToFilename(filename);
8766 // ============================================================================
8767 // setup file functions
8768 // ============================================================================
8770 #define TOKEN_STR_PLAYER_PREFIX "player_"
8773 static struct TokenInfo global_setup_tokens[] =
8777 &setup.player_name, "player_name"
8781 &setup.multiple_users, "multiple_users"
8785 &setup.sound, "sound"
8789 &setup.sound_loops, "repeating_sound_loops"
8793 &setup.sound_music, "background_music"
8797 &setup.sound_simple, "simple_sound_effects"
8801 &setup.toons, "toons"
8805 &setup.scroll_delay, "scroll_delay"
8809 &setup.forced_scroll_delay, "forced_scroll_delay"
8813 &setup.scroll_delay_value, "scroll_delay_value"
8817 &setup.engine_snapshot_mode, "engine_snapshot_mode"
8821 &setup.engine_snapshot_memory, "engine_snapshot_memory"
8825 &setup.fade_screens, "fade_screens"
8829 &setup.autorecord, "automatic_tape_recording"
8833 &setup.show_titlescreen, "show_titlescreen"
8837 &setup.quick_doors, "quick_doors"
8841 &setup.team_mode, "team_mode"
8845 &setup.handicap, "handicap"
8849 &setup.skip_levels, "skip_levels"
8853 &setup.increment_levels, "increment_levels"
8857 &setup.auto_play_next_level, "auto_play_next_level"
8861 &setup.count_score_after_game, "count_score_after_game"
8865 &setup.show_scores_after_game, "show_scores_after_game"
8869 &setup.time_limit, "time_limit"
8873 &setup.fullscreen, "fullscreen"
8877 &setup.window_scaling_percent, "window_scaling_percent"
8881 &setup.window_scaling_quality, "window_scaling_quality"
8885 &setup.screen_rendering_mode, "screen_rendering_mode"
8889 &setup.vsync_mode, "vsync_mode"
8893 &setup.ask_on_escape, "ask_on_escape"
8897 &setup.ask_on_escape_editor, "ask_on_escape_editor"
8901 &setup.ask_on_game_over, "ask_on_game_over"
8905 &setup.ask_on_quit_game, "ask_on_quit_game"
8909 &setup.ask_on_quit_program, "ask_on_quit_program"
8913 &setup.quick_switch, "quick_player_switch"
8917 &setup.input_on_focus, "input_on_focus"
8921 &setup.prefer_aga_graphics, "prefer_aga_graphics"
8925 &setup.prefer_lowpass_sounds, "prefer_lowpass_sounds"
8929 &setup.prefer_extra_panel_items, "prefer_extra_panel_items"
8933 &setup.game_speed_extended, "game_speed_extended"
8937 &setup.game_frame_delay, "game_frame_delay"
8941 &setup.sp_show_border_elements, "sp_show_border_elements"
8945 &setup.small_game_graphics, "small_game_graphics"
8949 &setup.show_snapshot_buttons, "show_snapshot_buttons"
8953 &setup.graphics_set, "graphics_set"
8957 &setup.sounds_set, "sounds_set"
8961 &setup.music_set, "music_set"
8965 &setup.override_level_graphics, "override_level_graphics"
8969 &setup.override_level_sounds, "override_level_sounds"
8973 &setup.override_level_music, "override_level_music"
8977 &setup.volume_simple, "volume_simple"
8981 &setup.volume_loops, "volume_loops"
8985 &setup.volume_music, "volume_music"
8989 &setup.network_mode, "network_mode"
8993 &setup.network_player_nr, "network_player"
8997 &setup.network_server_hostname, "network_server_hostname"
9001 &setup.touch.control_type, "touch.control_type"
9005 &setup.touch.move_distance, "touch.move_distance"
9009 &setup.touch.drop_distance, "touch.drop_distance"
9013 &setup.touch.transparency, "touch.transparency"
9017 &setup.touch.draw_outlined, "touch.draw_outlined"
9021 &setup.touch.draw_pressed, "touch.draw_pressed"
9025 &setup.touch.grid_xsize[0], "touch.virtual_buttons.0.xsize"
9029 &setup.touch.grid_ysize[0], "touch.virtual_buttons.0.ysize"
9033 &setup.touch.grid_xsize[1], "touch.virtual_buttons.1.xsize"
9037 &setup.touch.grid_ysize[1], "touch.virtual_buttons.1.ysize"
9041 static struct TokenInfo auto_setup_tokens[] =
9045 &setup.auto_setup.editor_zoom_tilesize, "editor.zoom_tilesize"
9049 static struct TokenInfo editor_setup_tokens[] =
9053 &setup.editor.el_classic, "editor.el_classic"
9057 &setup.editor.el_custom, "editor.el_custom"
9061 &setup.editor.el_user_defined, "editor.el_user_defined"
9065 &setup.editor.el_dynamic, "editor.el_dynamic"
9069 &setup.editor.el_headlines, "editor.el_headlines"
9073 &setup.editor.show_element_token, "editor.show_element_token"
9077 &setup.editor.show_read_only_warning, "editor.show_read_only_warning"
9081 static struct TokenInfo editor_cascade_setup_tokens[] =
9085 &setup.editor_cascade.el_bd, "editor.cascade.el_bd"
9089 &setup.editor_cascade.el_em, "editor.cascade.el_em"
9093 &setup.editor_cascade.el_emc, "editor.cascade.el_emc"
9097 &setup.editor_cascade.el_rnd, "editor.cascade.el_rnd"
9101 &setup.editor_cascade.el_sb, "editor.cascade.el_sb"
9105 &setup.editor_cascade.el_sp, "editor.cascade.el_sp"
9109 &setup.editor_cascade.el_dc, "editor.cascade.el_dc"
9113 &setup.editor_cascade.el_dx, "editor.cascade.el_dx"
9117 &setup.editor_cascade.el_mm, "editor.cascade.el_mm"
9121 &setup.editor_cascade.el_df, "editor.cascade.el_df"
9125 &setup.editor_cascade.el_chars, "editor.cascade.el_chars"
9129 &setup.editor_cascade.el_steel_chars, "editor.cascade.el_steel_chars"
9133 &setup.editor_cascade.el_ce, "editor.cascade.el_ce"
9137 &setup.editor_cascade.el_ge, "editor.cascade.el_ge"
9141 &setup.editor_cascade.el_ref, "editor.cascade.el_ref"
9145 &setup.editor_cascade.el_user, "editor.cascade.el_user"
9149 &setup.editor_cascade.el_dynamic, "editor.cascade.el_dynamic"
9153 static struct TokenInfo shortcut_setup_tokens[] =
9157 &setup.shortcut.save_game, "shortcut.save_game"
9161 &setup.shortcut.load_game, "shortcut.load_game"
9165 &setup.shortcut.toggle_pause, "shortcut.toggle_pause"
9169 &setup.shortcut.focus_player[0], "shortcut.focus_player_1"
9173 &setup.shortcut.focus_player[1], "shortcut.focus_player_2"
9177 &setup.shortcut.focus_player[2], "shortcut.focus_player_3"
9181 &setup.shortcut.focus_player[3], "shortcut.focus_player_4"
9185 &setup.shortcut.focus_player_all, "shortcut.focus_player_all"
9189 &setup.shortcut.tape_eject, "shortcut.tape_eject"
9193 &setup.shortcut.tape_extra, "shortcut.tape_extra"
9197 &setup.shortcut.tape_stop, "shortcut.tape_stop"
9201 &setup.shortcut.tape_pause, "shortcut.tape_pause"
9205 &setup.shortcut.tape_record, "shortcut.tape_record"
9209 &setup.shortcut.tape_play, "shortcut.tape_play"
9213 &setup.shortcut.sound_simple, "shortcut.sound_simple"
9217 &setup.shortcut.sound_loops, "shortcut.sound_loops"
9221 &setup.shortcut.sound_music, "shortcut.sound_music"
9225 &setup.shortcut.snap_left, "shortcut.snap_left"
9229 &setup.shortcut.snap_right, "shortcut.snap_right"
9233 &setup.shortcut.snap_up, "shortcut.snap_up"
9237 &setup.shortcut.snap_down, "shortcut.snap_down"
9241 static struct SetupInputInfo setup_input;
9242 static struct TokenInfo player_setup_tokens[] =
9246 &setup_input.use_joystick, ".use_joystick"
9250 &setup_input.joy.device_name, ".joy.device_name"
9254 &setup_input.joy.xleft, ".joy.xleft"
9258 &setup_input.joy.xmiddle, ".joy.xmiddle"
9262 &setup_input.joy.xright, ".joy.xright"
9266 &setup_input.joy.yupper, ".joy.yupper"
9270 &setup_input.joy.ymiddle, ".joy.ymiddle"
9274 &setup_input.joy.ylower, ".joy.ylower"
9278 &setup_input.joy.snap, ".joy.snap_field"
9282 &setup_input.joy.drop, ".joy.place_bomb"
9286 &setup_input.key.left, ".key.move_left"
9290 &setup_input.key.right, ".key.move_right"
9294 &setup_input.key.up, ".key.move_up"
9298 &setup_input.key.down, ".key.move_down"
9302 &setup_input.key.snap, ".key.snap_field"
9306 &setup_input.key.drop, ".key.place_bomb"
9310 static struct TokenInfo system_setup_tokens[] =
9314 &setup.system.sdl_renderdriver, "system.sdl_renderdriver"
9318 &setup.system.sdl_videodriver, "system.sdl_videodriver"
9322 &setup.system.sdl_audiodriver, "system.sdl_audiodriver"
9326 &setup.system.audio_fragment_size, "system.audio_fragment_size"
9330 static struct TokenInfo internal_setup_tokens[] =
9334 &setup.internal.program_title, "program_title"
9338 &setup.internal.program_version, "program_version"
9342 &setup.internal.program_author, "program_author"
9346 &setup.internal.program_email, "program_email"
9350 &setup.internal.program_website, "program_website"
9354 &setup.internal.program_copyright, "program_copyright"
9358 &setup.internal.program_company, "program_company"
9362 &setup.internal.program_icon_file, "program_icon_file"
9366 &setup.internal.default_graphics_set, "default_graphics_set"
9370 &setup.internal.default_sounds_set, "default_sounds_set"
9374 &setup.internal.default_music_set, "default_music_set"
9378 &setup.internal.fallback_graphics_file, "fallback_graphics_file"
9382 &setup.internal.fallback_sounds_file, "fallback_sounds_file"
9386 &setup.internal.fallback_music_file, "fallback_music_file"
9390 &setup.internal.default_level_series, "default_level_series"
9394 &setup.internal.default_window_width, "default_window_width"
9398 &setup.internal.default_window_height, "default_window_height"
9402 &setup.internal.choose_from_top_leveldir, "choose_from_top_leveldir"
9406 &setup.internal.show_scaling_in_title, "show_scaling_in_title"
9410 &setup.internal.create_user_levelset, "create_user_levelset"
9414 &setup.internal.menu_game, "menu_game"
9418 &setup.internal.menu_editor, "menu_editor"
9422 &setup.internal.menu_graphics, "menu_graphics"
9426 &setup.internal.menu_sound, "menu_sound"
9430 &setup.internal.menu_artwork, "menu_artwork"
9434 &setup.internal.menu_input, "menu_input"
9438 &setup.internal.menu_touch, "menu_touch"
9442 &setup.internal.menu_shortcuts, "menu_shortcuts"
9446 &setup.internal.menu_exit, "menu_exit"
9450 &setup.internal.menu_save_and_exit, "menu_save_and_exit"
9454 static struct TokenInfo debug_setup_tokens[] =
9458 &setup.debug.frame_delay[0], "debug.frame_delay_0"
9462 &setup.debug.frame_delay[1], "debug.frame_delay_1"
9466 &setup.debug.frame_delay[2], "debug.frame_delay_2"
9470 &setup.debug.frame_delay[3], "debug.frame_delay_3"
9474 &setup.debug.frame_delay[4], "debug.frame_delay_4"
9478 &setup.debug.frame_delay[5], "debug.frame_delay_5"
9482 &setup.debug.frame_delay[6], "debug.frame_delay_6"
9486 &setup.debug.frame_delay[7], "debug.frame_delay_7"
9490 &setup.debug.frame_delay[8], "debug.frame_delay_8"
9494 &setup.debug.frame_delay[9], "debug.frame_delay_9"
9498 &setup.debug.frame_delay_key[0], "debug.key.frame_delay_0"
9502 &setup.debug.frame_delay_key[1], "debug.key.frame_delay_1"
9506 &setup.debug.frame_delay_key[2], "debug.key.frame_delay_2"
9510 &setup.debug.frame_delay_key[3], "debug.key.frame_delay_3"
9514 &setup.debug.frame_delay_key[4], "debug.key.frame_delay_4"
9518 &setup.debug.frame_delay_key[5], "debug.key.frame_delay_5"
9522 &setup.debug.frame_delay_key[6], "debug.key.frame_delay_6"
9526 &setup.debug.frame_delay_key[7], "debug.key.frame_delay_7"
9530 &setup.debug.frame_delay_key[8], "debug.key.frame_delay_8"
9534 &setup.debug.frame_delay_key[9], "debug.key.frame_delay_9"
9538 &setup.debug.frame_delay_use_mod_key, "debug.frame_delay.use_mod_key"},
9541 &setup.debug.frame_delay_game_only, "debug.frame_delay.game_only"
9545 &setup.debug.show_frames_per_second, "debug.show_frames_per_second"
9549 &setup.debug.xsn_mode, "debug.xsn_mode"
9553 &setup.debug.xsn_percent, "debug.xsn_percent"
9557 static struct TokenInfo options_setup_tokens[] =
9561 &setup.options.verbose, "options.verbose"
9565 static void setSetupInfoToDefaults(struct SetupInfo *si)
9569 si->player_name = getStringCopy(getDefaultUserName(user.nr));
9571 si->multiple_users = TRUE;
9574 si->sound_loops = TRUE;
9575 si->sound_music = TRUE;
9576 si->sound_simple = TRUE;
9578 si->scroll_delay = TRUE;
9579 si->forced_scroll_delay = FALSE;
9580 si->scroll_delay_value = STD_SCROLL_DELAY;
9581 si->engine_snapshot_mode = getStringCopy(STR_SNAPSHOT_MODE_DEFAULT);
9582 si->engine_snapshot_memory = SNAPSHOT_MEMORY_DEFAULT;
9583 si->fade_screens = TRUE;
9584 si->autorecord = TRUE;
9585 si->show_titlescreen = TRUE;
9586 si->quick_doors = FALSE;
9587 si->team_mode = FALSE;
9588 si->handicap = TRUE;
9589 si->skip_levels = TRUE;
9590 si->increment_levels = TRUE;
9591 si->auto_play_next_level = TRUE;
9592 si->count_score_after_game = TRUE;
9593 si->show_scores_after_game = TRUE;
9594 si->time_limit = TRUE;
9595 si->fullscreen = FALSE;
9596 si->window_scaling_percent = STD_WINDOW_SCALING_PERCENT;
9597 si->window_scaling_quality = getStringCopy(SCALING_QUALITY_DEFAULT);
9598 si->screen_rendering_mode = getStringCopy(STR_SPECIAL_RENDERING_DEFAULT);
9599 si->vsync_mode = getStringCopy(STR_VSYNC_MODE_DEFAULT);
9600 si->ask_on_escape = TRUE;
9601 si->ask_on_escape_editor = TRUE;
9602 si->ask_on_game_over = TRUE;
9603 si->ask_on_quit_game = TRUE;
9604 si->ask_on_quit_program = TRUE;
9605 si->quick_switch = FALSE;
9606 si->input_on_focus = FALSE;
9607 si->prefer_aga_graphics = TRUE;
9608 si->prefer_lowpass_sounds = FALSE;
9609 si->prefer_extra_panel_items = TRUE;
9610 si->game_speed_extended = FALSE;
9611 si->game_frame_delay = GAME_FRAME_DELAY;
9612 si->sp_show_border_elements = FALSE;
9613 si->small_game_graphics = FALSE;
9614 si->show_snapshot_buttons = FALSE;
9616 si->graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
9617 si->sounds_set = getStringCopy(SND_CLASSIC_SUBDIR);
9618 si->music_set = getStringCopy(MUS_CLASSIC_SUBDIR);
9620 si->override_level_graphics = FALSE;
9621 si->override_level_sounds = FALSE;
9622 si->override_level_music = FALSE;
9624 si->volume_simple = 100; // percent
9625 si->volume_loops = 100; // percent
9626 si->volume_music = 100; // percent
9628 si->network_mode = FALSE;
9629 si->network_player_nr = 0; // first player
9630 si->network_server_hostname = getStringCopy(STR_NETWORK_AUTO_DETECT);
9632 si->touch.control_type = getStringCopy(TOUCH_CONTROL_DEFAULT);
9633 si->touch.move_distance = TOUCH_MOVE_DISTANCE_DEFAULT; // percent
9634 si->touch.drop_distance = TOUCH_DROP_DISTANCE_DEFAULT; // percent
9635 si->touch.transparency = TOUCH_TRANSPARENCY_DEFAULT; // percent
9636 si->touch.draw_outlined = TRUE;
9637 si->touch.draw_pressed = TRUE;
9639 for (i = 0; i < 2; i++)
9641 char *default_grid_button[6][2] =
9647 { "111222", " vv " },
9648 { "111222", " vv " }
9650 int grid_xsize = DEFAULT_GRID_XSIZE(i);
9651 int grid_ysize = DEFAULT_GRID_YSIZE(i);
9652 int min_xsize = MIN(6, grid_xsize);
9653 int min_ysize = MIN(6, grid_ysize);
9654 int startx = grid_xsize - min_xsize;
9655 int starty = grid_ysize - min_ysize;
9658 // virtual buttons grid can only be set to defaults if video is initialized
9659 // (this will be repeated if virtual buttons are not loaded from setup file)
9660 if (video.initialized)
9662 si->touch.grid_xsize[i] = grid_xsize;
9663 si->touch.grid_ysize[i] = grid_ysize;
9667 si->touch.grid_xsize[i] = -1;
9668 si->touch.grid_ysize[i] = -1;
9671 for (x = 0; x < MAX_GRID_XSIZE; x++)
9672 for (y = 0; y < MAX_GRID_YSIZE; y++)
9673 si->touch.grid_button[i][x][y] = CHAR_GRID_BUTTON_NONE;
9675 for (x = 0; x < min_xsize; x++)
9676 for (y = 0; y < min_ysize; y++)
9677 si->touch.grid_button[i][x][starty + y] =
9678 default_grid_button[y][0][x];
9680 for (x = 0; x < min_xsize; x++)
9681 for (y = 0; y < min_ysize; y++)
9682 si->touch.grid_button[i][startx + x][starty + y] =
9683 default_grid_button[y][1][x];
9686 si->touch.grid_initialized = video.initialized;
9688 si->editor.el_boulderdash = TRUE;
9689 si->editor.el_emerald_mine = TRUE;
9690 si->editor.el_emerald_mine_club = TRUE;
9691 si->editor.el_more = TRUE;
9692 si->editor.el_sokoban = TRUE;
9693 si->editor.el_supaplex = TRUE;
9694 si->editor.el_diamond_caves = TRUE;
9695 si->editor.el_dx_boulderdash = TRUE;
9697 si->editor.el_mirror_magic = TRUE;
9698 si->editor.el_deflektor = TRUE;
9700 si->editor.el_chars = TRUE;
9701 si->editor.el_steel_chars = TRUE;
9703 si->editor.el_classic = TRUE;
9704 si->editor.el_custom = TRUE;
9706 si->editor.el_user_defined = FALSE;
9707 si->editor.el_dynamic = TRUE;
9709 si->editor.el_headlines = TRUE;
9711 si->editor.show_element_token = FALSE;
9713 si->editor.show_read_only_warning = TRUE;
9715 si->editor.use_template_for_new_levels = TRUE;
9717 si->shortcut.save_game = DEFAULT_KEY_SAVE_GAME;
9718 si->shortcut.load_game = DEFAULT_KEY_LOAD_GAME;
9719 si->shortcut.toggle_pause = DEFAULT_KEY_TOGGLE_PAUSE;
9721 si->shortcut.focus_player[0] = DEFAULT_KEY_FOCUS_PLAYER_1;
9722 si->shortcut.focus_player[1] = DEFAULT_KEY_FOCUS_PLAYER_2;
9723 si->shortcut.focus_player[2] = DEFAULT_KEY_FOCUS_PLAYER_3;
9724 si->shortcut.focus_player[3] = DEFAULT_KEY_FOCUS_PLAYER_4;
9725 si->shortcut.focus_player_all = DEFAULT_KEY_FOCUS_PLAYER_ALL;
9727 si->shortcut.tape_eject = DEFAULT_KEY_TAPE_EJECT;
9728 si->shortcut.tape_extra = DEFAULT_KEY_TAPE_EXTRA;
9729 si->shortcut.tape_stop = DEFAULT_KEY_TAPE_STOP;
9730 si->shortcut.tape_pause = DEFAULT_KEY_TAPE_PAUSE;
9731 si->shortcut.tape_record = DEFAULT_KEY_TAPE_RECORD;
9732 si->shortcut.tape_play = DEFAULT_KEY_TAPE_PLAY;
9734 si->shortcut.sound_simple = DEFAULT_KEY_SOUND_SIMPLE;
9735 si->shortcut.sound_loops = DEFAULT_KEY_SOUND_LOOPS;
9736 si->shortcut.sound_music = DEFAULT_KEY_SOUND_MUSIC;
9738 si->shortcut.snap_left = DEFAULT_KEY_SNAP_LEFT;
9739 si->shortcut.snap_right = DEFAULT_KEY_SNAP_RIGHT;
9740 si->shortcut.snap_up = DEFAULT_KEY_SNAP_UP;
9741 si->shortcut.snap_down = DEFAULT_KEY_SNAP_DOWN;
9743 for (i = 0; i < MAX_PLAYERS; i++)
9745 si->input[i].use_joystick = FALSE;
9746 si->input[i].joy.device_name=getStringCopy(getDeviceNameFromJoystickNr(i));
9747 si->input[i].joy.xleft = JOYSTICK_XLEFT;
9748 si->input[i].joy.xmiddle = JOYSTICK_XMIDDLE;
9749 si->input[i].joy.xright = JOYSTICK_XRIGHT;
9750 si->input[i].joy.yupper = JOYSTICK_YUPPER;
9751 si->input[i].joy.ymiddle = JOYSTICK_YMIDDLE;
9752 si->input[i].joy.ylower = JOYSTICK_YLOWER;
9753 si->input[i].joy.snap = (i == 0 ? JOY_BUTTON_1 : 0);
9754 si->input[i].joy.drop = (i == 0 ? JOY_BUTTON_2 : 0);
9755 si->input[i].key.left = (i == 0 ? DEFAULT_KEY_LEFT : KSYM_UNDEFINED);
9756 si->input[i].key.right = (i == 0 ? DEFAULT_KEY_RIGHT : KSYM_UNDEFINED);
9757 si->input[i].key.up = (i == 0 ? DEFAULT_KEY_UP : KSYM_UNDEFINED);
9758 si->input[i].key.down = (i == 0 ? DEFAULT_KEY_DOWN : KSYM_UNDEFINED);
9759 si->input[i].key.snap = (i == 0 ? DEFAULT_KEY_SNAP : KSYM_UNDEFINED);
9760 si->input[i].key.drop = (i == 0 ? DEFAULT_KEY_DROP : KSYM_UNDEFINED);
9763 si->system.sdl_renderdriver = getStringCopy(ARG_DEFAULT);
9764 si->system.sdl_videodriver = getStringCopy(ARG_DEFAULT);
9765 si->system.sdl_audiodriver = getStringCopy(ARG_DEFAULT);
9766 si->system.audio_fragment_size = DEFAULT_AUDIO_FRAGMENT_SIZE;
9768 si->internal.program_title = getStringCopy(PROGRAM_TITLE_STRING);
9769 si->internal.program_version = getStringCopy(getProgramRealVersionString());
9770 si->internal.program_author = getStringCopy(PROGRAM_AUTHOR_STRING);
9771 si->internal.program_email = getStringCopy(PROGRAM_EMAIL_STRING);
9772 si->internal.program_website = getStringCopy(PROGRAM_WEBSITE_STRING);
9773 si->internal.program_copyright = getStringCopy(PROGRAM_COPYRIGHT_STRING);
9774 si->internal.program_company = getStringCopy(PROGRAM_COMPANY_STRING);
9776 si->internal.program_icon_file = getStringCopy(PROGRAM_ICON_FILENAME);
9778 si->internal.default_graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
9779 si->internal.default_sounds_set = getStringCopy(SND_CLASSIC_SUBDIR);
9780 si->internal.default_music_set = getStringCopy(MUS_CLASSIC_SUBDIR);
9782 si->internal.fallback_graphics_file = getStringCopy(UNDEFINED_FILENAME);
9783 si->internal.fallback_sounds_file = getStringCopy(UNDEFINED_FILENAME);
9784 si->internal.fallback_music_file = getStringCopy(UNDEFINED_FILENAME);
9786 si->internal.default_level_series = getStringCopy(UNDEFINED_LEVELSET);
9787 si->internal.choose_from_top_leveldir = FALSE;
9788 si->internal.show_scaling_in_title = TRUE;
9789 si->internal.create_user_levelset = TRUE;
9791 si->internal.default_window_width = WIN_XSIZE_DEFAULT;
9792 si->internal.default_window_height = WIN_YSIZE_DEFAULT;
9794 si->debug.frame_delay[0] = DEFAULT_FRAME_DELAY_0;
9795 si->debug.frame_delay[1] = DEFAULT_FRAME_DELAY_1;
9796 si->debug.frame_delay[2] = DEFAULT_FRAME_DELAY_2;
9797 si->debug.frame_delay[3] = DEFAULT_FRAME_DELAY_3;
9798 si->debug.frame_delay[4] = DEFAULT_FRAME_DELAY_4;
9799 si->debug.frame_delay[5] = DEFAULT_FRAME_DELAY_5;
9800 si->debug.frame_delay[6] = DEFAULT_FRAME_DELAY_6;
9801 si->debug.frame_delay[7] = DEFAULT_FRAME_DELAY_7;
9802 si->debug.frame_delay[8] = DEFAULT_FRAME_DELAY_8;
9803 si->debug.frame_delay[9] = DEFAULT_FRAME_DELAY_9;
9805 si->debug.frame_delay_key[0] = DEFAULT_KEY_FRAME_DELAY_0;
9806 si->debug.frame_delay_key[1] = DEFAULT_KEY_FRAME_DELAY_1;
9807 si->debug.frame_delay_key[2] = DEFAULT_KEY_FRAME_DELAY_2;
9808 si->debug.frame_delay_key[3] = DEFAULT_KEY_FRAME_DELAY_3;
9809 si->debug.frame_delay_key[4] = DEFAULT_KEY_FRAME_DELAY_4;
9810 si->debug.frame_delay_key[5] = DEFAULT_KEY_FRAME_DELAY_5;
9811 si->debug.frame_delay_key[6] = DEFAULT_KEY_FRAME_DELAY_6;
9812 si->debug.frame_delay_key[7] = DEFAULT_KEY_FRAME_DELAY_7;
9813 si->debug.frame_delay_key[8] = DEFAULT_KEY_FRAME_DELAY_8;
9814 si->debug.frame_delay_key[9] = DEFAULT_KEY_FRAME_DELAY_9;
9816 si->debug.frame_delay_use_mod_key = DEFAULT_FRAME_DELAY_USE_MOD_KEY;
9817 si->debug.frame_delay_game_only = DEFAULT_FRAME_DELAY_GAME_ONLY;
9819 si->debug.show_frames_per_second = FALSE;
9821 si->debug.xsn_mode = AUTO;
9822 si->debug.xsn_percent = 0;
9824 si->options.verbose = FALSE;
9826 #if defined(PLATFORM_ANDROID)
9827 si->fullscreen = TRUE;
9830 setHideSetupEntry(&setup.debug.xsn_mode);
9833 static void setSetupInfoToDefaults_AutoSetup(struct SetupInfo *si)
9835 si->auto_setup.editor_zoom_tilesize = MINI_TILESIZE;
9838 static void setSetupInfoToDefaults_EditorCascade(struct SetupInfo *si)
9840 si->editor_cascade.el_bd = TRUE;
9841 si->editor_cascade.el_em = TRUE;
9842 si->editor_cascade.el_emc = TRUE;
9843 si->editor_cascade.el_rnd = TRUE;
9844 si->editor_cascade.el_sb = TRUE;
9845 si->editor_cascade.el_sp = TRUE;
9846 si->editor_cascade.el_dc = TRUE;
9847 si->editor_cascade.el_dx = TRUE;
9849 si->editor_cascade.el_mm = TRUE;
9850 si->editor_cascade.el_df = TRUE;
9852 si->editor_cascade.el_chars = FALSE;
9853 si->editor_cascade.el_steel_chars = FALSE;
9854 si->editor_cascade.el_ce = FALSE;
9855 si->editor_cascade.el_ge = FALSE;
9856 si->editor_cascade.el_ref = FALSE;
9857 si->editor_cascade.el_user = FALSE;
9858 si->editor_cascade.el_dynamic = FALSE;
9861 #define MAX_HIDE_SETUP_TOKEN_SIZE 20
9863 static char *getHideSetupToken(void *setup_value)
9865 static char hide_setup_token[MAX_HIDE_SETUP_TOKEN_SIZE];
9867 if (setup_value != NULL)
9868 snprintf(hide_setup_token, MAX_HIDE_SETUP_TOKEN_SIZE, "%p", setup_value);
9870 return hide_setup_token;
9873 void setHideSetupEntry(void *setup_value)
9875 char *hide_setup_token = getHideSetupToken(setup_value);
9877 if (hide_setup_hash == NULL)
9878 hide_setup_hash = newSetupFileHash();
9880 if (setup_value != NULL)
9881 setHashEntry(hide_setup_hash, hide_setup_token, "");
9884 void removeHideSetupEntry(void *setup_value)
9886 char *hide_setup_token = getHideSetupToken(setup_value);
9888 if (setup_value != NULL)
9889 removeHashEntry(hide_setup_hash, hide_setup_token);
9892 boolean hideSetupEntry(void *setup_value)
9894 char *hide_setup_token = getHideSetupToken(setup_value);
9896 return (setup_value != NULL &&
9897 getHashEntry(hide_setup_hash, hide_setup_token) != NULL);
9900 static void setSetupInfoFromTokenText(SetupFileHash *setup_file_hash,
9901 struct TokenInfo *token_info,
9902 int token_nr, char *token_text)
9904 char *token_hide_text = getStringCat2(token_text, ".hide");
9905 char *token_hide_value = getHashEntry(setup_file_hash, token_hide_text);
9907 // set the value of this setup option in the setup option structure
9908 setSetupInfo(token_info, token_nr, getHashEntry(setup_file_hash, token_text));
9910 // check if this setup option should be hidden in the setup menu
9911 if (token_hide_value != NULL && get_boolean_from_string(token_hide_value))
9912 setHideSetupEntry(token_info[token_nr].value);
9914 free(token_hide_text);
9917 static void setSetupInfoFromTokenInfo(SetupFileHash *setup_file_hash,
9918 struct TokenInfo *token_info,
9921 setSetupInfoFromTokenText(setup_file_hash, token_info, token_nr,
9922 token_info[token_nr].text);
9925 static void decodeSetupFileHash(SetupFileHash *setup_file_hash)
9929 if (!setup_file_hash)
9932 for (i = 0; i < ARRAY_SIZE(global_setup_tokens); i++)
9933 setSetupInfoFromTokenInfo(setup_file_hash, global_setup_tokens, i);
9935 setup.touch.grid_initialized = TRUE;
9936 for (i = 0; i < 2; i++)
9938 int grid_xsize = setup.touch.grid_xsize[i];
9939 int grid_ysize = setup.touch.grid_ysize[i];
9942 // if virtual buttons are not loaded from setup file, repeat initializing
9943 // virtual buttons grid with default values later when video is initialized
9944 if (grid_xsize == -1 ||
9947 setup.touch.grid_initialized = FALSE;
9952 for (y = 0; y < grid_ysize; y++)
9954 char token_string[MAX_LINE_LEN];
9956 sprintf(token_string, "touch.virtual_buttons.%d.%02d", i, y);
9958 char *value_string = getHashEntry(setup_file_hash, token_string);
9960 if (value_string == NULL)
9963 for (x = 0; x < grid_xsize; x++)
9965 char c = value_string[x];
9967 setup.touch.grid_button[i][x][y] =
9968 (c == '.' ? CHAR_GRID_BUTTON_NONE : c);
9973 for (i = 0; i < ARRAY_SIZE(editor_setup_tokens); i++)
9974 setSetupInfoFromTokenInfo(setup_file_hash, editor_setup_tokens, i);
9976 for (i = 0; i < ARRAY_SIZE(shortcut_setup_tokens); i++)
9977 setSetupInfoFromTokenInfo(setup_file_hash, shortcut_setup_tokens, i);
9979 for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
9983 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
9985 setup_input = setup.input[pnr];
9986 for (i = 0; i < ARRAY_SIZE(player_setup_tokens); i++)
9988 char full_token[100];
9990 sprintf(full_token, "%s%s", prefix, player_setup_tokens[i].text);
9991 setSetupInfoFromTokenText(setup_file_hash, player_setup_tokens, i,
9994 setup.input[pnr] = setup_input;
9997 for (i = 0; i < ARRAY_SIZE(system_setup_tokens); i++)
9998 setSetupInfoFromTokenInfo(setup_file_hash, system_setup_tokens, i);
10000 for (i = 0; i < ARRAY_SIZE(internal_setup_tokens); i++)
10001 setSetupInfoFromTokenInfo(setup_file_hash, internal_setup_tokens, i);
10003 for (i = 0; i < ARRAY_SIZE(debug_setup_tokens); i++)
10004 setSetupInfoFromTokenInfo(setup_file_hash, debug_setup_tokens, i);
10006 for (i = 0; i < ARRAY_SIZE(options_setup_tokens); i++)
10007 setSetupInfoFromTokenInfo(setup_file_hash, options_setup_tokens, i);
10009 setHideRelatedSetupEntries();
10012 static void decodeSetupFileHash_AutoSetup(SetupFileHash *setup_file_hash)
10016 if (!setup_file_hash)
10019 for (i = 0; i < ARRAY_SIZE(auto_setup_tokens); i++)
10020 setSetupInfo(auto_setup_tokens, i,
10021 getHashEntry(setup_file_hash,
10022 auto_setup_tokens[i].text));
10025 static void decodeSetupFileHash_EditorCascade(SetupFileHash *setup_file_hash)
10029 if (!setup_file_hash)
10032 for (i = 0; i < ARRAY_SIZE(editor_cascade_setup_tokens); i++)
10033 setSetupInfo(editor_cascade_setup_tokens, i,
10034 getHashEntry(setup_file_hash,
10035 editor_cascade_setup_tokens[i].text));
10038 void LoadUserNames(void)
10040 int last_user_nr = user.nr;
10043 if (global.user_names != NULL)
10045 for (i = 0; i < MAX_PLAYER_NAMES; i++)
10046 checked_free(global.user_names[i]);
10048 checked_free(global.user_names);
10051 global.user_names = checked_calloc(MAX_PLAYER_NAMES * sizeof(char *));
10053 for (i = 0; i < MAX_PLAYER_NAMES; i++)
10057 SetupFileHash *setup_file_hash = loadSetupFileHash(getSetupFilename());
10059 if (setup_file_hash)
10061 char *player_name = getHashEntry(setup_file_hash, "player_name");
10063 global.user_names[i] = getFixedUserName(player_name);
10065 freeSetupFileHash(setup_file_hash);
10068 if (global.user_names[i] == NULL)
10069 global.user_names[i] = getStringCopy(getDefaultUserName(i));
10072 user.nr = last_user_nr;
10075 void LoadSetupFromFilename(char *filename)
10077 SetupFileHash *setup_file_hash = loadSetupFileHash(filename);
10079 if (setup_file_hash)
10081 decodeSetupFileHash(setup_file_hash);
10083 freeSetupFileHash(setup_file_hash);
10087 Debug("setup", "using default setup values");
10091 static void LoadSetup_SpecialPostProcessing(void)
10093 char *player_name_new;
10095 // needed to work around problems with fixed length strings
10096 player_name_new = getFixedUserName(setup.player_name);
10097 free(setup.player_name);
10098 setup.player_name = player_name_new;
10100 // "scroll_delay: on(3) / off(0)" was replaced by scroll delay value
10101 if (setup.scroll_delay == FALSE)
10103 setup.scroll_delay_value = MIN_SCROLL_DELAY;
10104 setup.scroll_delay = TRUE; // now always "on"
10107 // make sure that scroll delay value stays inside valid range
10108 setup.scroll_delay_value =
10109 MIN(MAX(MIN_SCROLL_DELAY, setup.scroll_delay_value), MAX_SCROLL_DELAY);
10112 void LoadSetup(void)
10116 // always start with reliable default values
10117 setSetupInfoToDefaults(&setup);
10119 // try to load setup values from default setup file
10120 filename = getDefaultSetupFilename();
10122 if (fileExists(filename))
10123 LoadSetupFromFilename(filename);
10125 // try to load setup values from user setup file
10126 filename = getSetupFilename();
10128 LoadSetupFromFilename(filename);
10130 LoadSetup_SpecialPostProcessing();
10133 void LoadSetup_AutoSetup(void)
10135 char *filename = getPath2(getSetupDir(), AUTOSETUP_FILENAME);
10136 SetupFileHash *setup_file_hash = NULL;
10138 // always start with reliable default values
10139 setSetupInfoToDefaults_AutoSetup(&setup);
10141 setup_file_hash = loadSetupFileHash(filename);
10143 if (setup_file_hash)
10145 decodeSetupFileHash_AutoSetup(setup_file_hash);
10147 freeSetupFileHash(setup_file_hash);
10153 void LoadSetup_EditorCascade(void)
10155 char *filename = getPath2(getSetupDir(), EDITORCASCADE_FILENAME);
10156 SetupFileHash *setup_file_hash = NULL;
10158 // always start with reliable default values
10159 setSetupInfoToDefaults_EditorCascade(&setup);
10161 setup_file_hash = loadSetupFileHash(filename);
10163 if (setup_file_hash)
10165 decodeSetupFileHash_EditorCascade(setup_file_hash);
10167 freeSetupFileHash(setup_file_hash);
10173 static void addGameControllerMappingToHash(SetupFileHash *mappings_hash,
10174 char *mapping_line)
10176 char mapping_guid[MAX_LINE_LEN];
10177 char *mapping_start, *mapping_end;
10179 // get GUID from game controller mapping line: copy complete line
10180 strncpy(mapping_guid, mapping_line, MAX_LINE_LEN - 1);
10181 mapping_guid[MAX_LINE_LEN - 1] = '\0';
10183 // get GUID from game controller mapping line: cut after GUID part
10184 mapping_start = strchr(mapping_guid, ',');
10185 if (mapping_start != NULL)
10186 *mapping_start = '\0';
10188 // cut newline from game controller mapping line
10189 mapping_end = strchr(mapping_line, '\n');
10190 if (mapping_end != NULL)
10191 *mapping_end = '\0';
10193 // add mapping entry to game controller mappings hash
10194 setHashEntry(mappings_hash, mapping_guid, mapping_line);
10197 static void LoadSetup_ReadGameControllerMappings(SetupFileHash *mappings_hash,
10202 if (!(file = fopen(filename, MODE_READ)))
10204 Warn("cannot read game controller mappings file '%s'", filename);
10209 while (!feof(file))
10211 char line[MAX_LINE_LEN];
10213 if (!fgets(line, MAX_LINE_LEN, file))
10216 addGameControllerMappingToHash(mappings_hash, line);
10222 void SaveSetup(void)
10224 char *filename = getSetupFilename();
10228 InitUserDataDirectory();
10230 if (!(file = fopen(filename, MODE_WRITE)))
10232 Warn("cannot write setup file '%s'", filename);
10237 fprintFileHeader(file, SETUP_FILENAME);
10239 for (i = 0; i < ARRAY_SIZE(global_setup_tokens); i++)
10241 // just to make things nicer :)
10242 if (global_setup_tokens[i].value == &setup.multiple_users ||
10243 global_setup_tokens[i].value == &setup.sound ||
10244 global_setup_tokens[i].value == &setup.graphics_set ||
10245 global_setup_tokens[i].value == &setup.volume_simple ||
10246 global_setup_tokens[i].value == &setup.network_mode ||
10247 global_setup_tokens[i].value == &setup.touch.control_type ||
10248 global_setup_tokens[i].value == &setup.touch.grid_xsize[0] ||
10249 global_setup_tokens[i].value == &setup.touch.grid_xsize[1])
10250 fprintf(file, "\n");
10252 fprintf(file, "%s\n", getSetupLine(global_setup_tokens, "", i));
10255 for (i = 0; i < 2; i++)
10257 int grid_xsize = setup.touch.grid_xsize[i];
10258 int grid_ysize = setup.touch.grid_ysize[i];
10261 fprintf(file, "\n");
10263 for (y = 0; y < grid_ysize; y++)
10265 char token_string[MAX_LINE_LEN];
10266 char value_string[MAX_LINE_LEN];
10268 sprintf(token_string, "touch.virtual_buttons.%d.%02d", i, y);
10270 for (x = 0; x < grid_xsize; x++)
10272 char c = setup.touch.grid_button[i][x][y];
10274 value_string[x] = (c == CHAR_GRID_BUTTON_NONE ? '.' : c);
10277 value_string[grid_xsize] = '\0';
10279 fprintf(file, "%s\n", getFormattedSetupEntry(token_string, value_string));
10283 fprintf(file, "\n");
10284 for (i = 0; i < ARRAY_SIZE(editor_setup_tokens); i++)
10285 fprintf(file, "%s\n", getSetupLine(editor_setup_tokens, "", i));
10287 fprintf(file, "\n");
10288 for (i = 0; i < ARRAY_SIZE(shortcut_setup_tokens); i++)
10289 fprintf(file, "%s\n", getSetupLine(shortcut_setup_tokens, "", i));
10291 for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
10295 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
10296 fprintf(file, "\n");
10298 setup_input = setup.input[pnr];
10299 for (i = 0; i < ARRAY_SIZE(player_setup_tokens); i++)
10300 fprintf(file, "%s\n", getSetupLine(player_setup_tokens, prefix, i));
10303 fprintf(file, "\n");
10304 for (i = 0; i < ARRAY_SIZE(system_setup_tokens); i++)
10305 fprintf(file, "%s\n", getSetupLine(system_setup_tokens, "", i));
10307 // (internal setup values not saved to user setup file)
10309 fprintf(file, "\n");
10310 for (i = 0; i < ARRAY_SIZE(debug_setup_tokens); i++)
10311 if (!strPrefix(debug_setup_tokens[i].text, "debug.xsn_") ||
10312 setup.debug.xsn_mode != AUTO)
10313 fprintf(file, "%s\n", getSetupLine(debug_setup_tokens, "", i));
10315 fprintf(file, "\n");
10316 for (i = 0; i < ARRAY_SIZE(options_setup_tokens); i++)
10317 fprintf(file, "%s\n", getSetupLine(options_setup_tokens, "", i));
10321 SetFilePermissions(filename, PERMS_PRIVATE);
10324 void SaveSetup_AutoSetup(void)
10326 char *filename = getPath2(getSetupDir(), AUTOSETUP_FILENAME);
10330 InitUserDataDirectory();
10332 if (!(file = fopen(filename, MODE_WRITE)))
10334 Warn("cannot write auto setup file '%s'", filename);
10341 fprintFileHeader(file, AUTOSETUP_FILENAME);
10343 for (i = 0; i < ARRAY_SIZE(auto_setup_tokens); i++)
10344 fprintf(file, "%s\n", getSetupLine(auto_setup_tokens, "", i));
10348 SetFilePermissions(filename, PERMS_PRIVATE);
10353 void SaveSetup_EditorCascade(void)
10355 char *filename = getPath2(getSetupDir(), EDITORCASCADE_FILENAME);
10359 InitUserDataDirectory();
10361 if (!(file = fopen(filename, MODE_WRITE)))
10363 Warn("cannot write editor cascade state file '%s'", filename);
10370 fprintFileHeader(file, EDITORCASCADE_FILENAME);
10372 for (i = 0; i < ARRAY_SIZE(editor_cascade_setup_tokens); i++)
10373 fprintf(file, "%s\n", getSetupLine(editor_cascade_setup_tokens, "", i));
10377 SetFilePermissions(filename, PERMS_PRIVATE);
10382 static void SaveSetup_WriteGameControllerMappings(SetupFileHash *mappings_hash,
10387 if (!(file = fopen(filename, MODE_WRITE)))
10389 Warn("cannot write game controller mappings file '%s'", filename);
10394 BEGIN_HASH_ITERATION(mappings_hash, itr)
10396 fprintf(file, "%s\n", HASH_ITERATION_VALUE(itr));
10398 END_HASH_ITERATION(mappings_hash, itr)
10403 void SaveSetup_AddGameControllerMapping(char *mapping)
10405 char *filename = getPath2(getSetupDir(), GAMECONTROLLER_BASENAME);
10406 SetupFileHash *mappings_hash = newSetupFileHash();
10408 InitUserDataDirectory();
10410 // load existing personal game controller mappings
10411 LoadSetup_ReadGameControllerMappings(mappings_hash, filename);
10413 // add new mapping to personal game controller mappings
10414 addGameControllerMappingToHash(mappings_hash, mapping);
10416 // save updated personal game controller mappings
10417 SaveSetup_WriteGameControllerMappings(mappings_hash, filename);
10419 freeSetupFileHash(mappings_hash);
10423 void LoadCustomElementDescriptions(void)
10425 char *filename = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
10426 SetupFileHash *setup_file_hash;
10429 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
10431 if (element_info[i].custom_description != NULL)
10433 free(element_info[i].custom_description);
10434 element_info[i].custom_description = NULL;
10438 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
10441 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
10443 char *token = getStringCat2(element_info[i].token_name, ".name");
10444 char *value = getHashEntry(setup_file_hash, token);
10447 element_info[i].custom_description = getStringCopy(value);
10452 freeSetupFileHash(setup_file_hash);
10455 static int getElementFromToken(char *token)
10457 char *value = getHashEntry(element_token_hash, token);
10460 return atoi(value);
10462 Warn("unknown element token '%s'", token);
10464 return EL_UNDEFINED;
10467 void FreeGlobalAnimEventInfo(void)
10469 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
10471 if (gaei->event_list == NULL)
10476 for (i = 0; i < gaei->num_event_lists; i++)
10478 checked_free(gaei->event_list[i]->event_value);
10479 checked_free(gaei->event_list[i]);
10482 checked_free(gaei->event_list);
10484 gaei->event_list = NULL;
10485 gaei->num_event_lists = 0;
10488 static int AddGlobalAnimEventList(void)
10490 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
10491 int list_pos = gaei->num_event_lists++;
10493 gaei->event_list = checked_realloc(gaei->event_list, gaei->num_event_lists *
10494 sizeof(struct GlobalAnimEventListInfo *));
10496 gaei->event_list[list_pos] =
10497 checked_calloc(sizeof(struct GlobalAnimEventListInfo));
10499 struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
10501 gaeli->event_value = NULL;
10502 gaeli->num_event_values = 0;
10507 static int AddGlobalAnimEventValue(int list_pos, int event_value)
10509 // do not add empty global animation events
10510 if (event_value == ANIM_EVENT_NONE)
10513 // if list position is undefined, create new list
10514 if (list_pos == ANIM_EVENT_UNDEFINED)
10515 list_pos = AddGlobalAnimEventList();
10517 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
10518 struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
10519 int value_pos = gaeli->num_event_values++;
10521 gaeli->event_value = checked_realloc(gaeli->event_value,
10522 gaeli->num_event_values * sizeof(int *));
10524 gaeli->event_value[value_pos] = event_value;
10529 int GetGlobalAnimEventValue(int list_pos, int value_pos)
10531 if (list_pos == ANIM_EVENT_UNDEFINED)
10532 return ANIM_EVENT_NONE;
10534 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
10535 struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
10537 return gaeli->event_value[value_pos];
10540 int GetGlobalAnimEventValueCount(int list_pos)
10542 if (list_pos == ANIM_EVENT_UNDEFINED)
10545 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
10546 struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
10548 return gaeli->num_event_values;
10551 // This function checks if a string <s> of the format "string1, string2, ..."
10552 // exactly contains a string <s_contained>.
10554 static boolean string_has_parameter(char *s, char *s_contained)
10558 if (s == NULL || s_contained == NULL)
10561 if (strlen(s_contained) > strlen(s))
10564 if (strncmp(s, s_contained, strlen(s_contained)) == 0)
10566 char next_char = s[strlen(s_contained)];
10568 // check if next character is delimiter or whitespace
10569 return (next_char == ',' || next_char == '\0' ||
10570 next_char == ' ' || next_char == '\t' ? TRUE : FALSE);
10573 // check if string contains another parameter string after a comma
10574 substring = strchr(s, ',');
10575 if (substring == NULL) // string does not contain a comma
10578 // advance string pointer to next character after the comma
10581 // skip potential whitespaces after the comma
10582 while (*substring == ' ' || *substring == '\t')
10585 return string_has_parameter(substring, s_contained);
10588 static int get_anim_parameter_value(char *s)
10590 int event_value[] =
10598 char *pattern_1[] =
10606 char *pattern_2 = ".part_";
10607 char *matching_char = NULL;
10609 int pattern_1_len = 0;
10610 int result = ANIM_EVENT_NONE;
10613 for (i = 0; i < ARRAY_SIZE(event_value); i++)
10615 matching_char = strstr(s_ptr, pattern_1[i]);
10616 pattern_1_len = strlen(pattern_1[i]);
10617 result = event_value[i];
10619 if (matching_char != NULL)
10623 if (matching_char == NULL)
10624 return ANIM_EVENT_NONE;
10626 s_ptr = matching_char + pattern_1_len;
10628 // check for main animation number ("anim_X" or "anim_XX")
10629 if (*s_ptr >= '0' && *s_ptr <= '9')
10631 int gic_anim_nr = (*s_ptr++ - '0');
10633 if (*s_ptr >= '0' && *s_ptr <= '9')
10634 gic_anim_nr = 10 * gic_anim_nr + (*s_ptr++ - '0');
10636 if (gic_anim_nr < 1 || gic_anim_nr > MAX_GLOBAL_ANIMS)
10637 return ANIM_EVENT_NONE;
10639 result |= gic_anim_nr << ANIM_EVENT_ANIM_BIT;
10643 // invalid main animation number specified
10645 return ANIM_EVENT_NONE;
10648 // check for animation part number ("part_X" or "part_XX") (optional)
10649 if (strPrefix(s_ptr, pattern_2))
10651 s_ptr += strlen(pattern_2);
10653 if (*s_ptr >= '0' && *s_ptr <= '9')
10655 int gic_part_nr = (*s_ptr++ - '0');
10657 if (*s_ptr >= '0' && *s_ptr <= '9')
10658 gic_part_nr = 10 * gic_part_nr + (*s_ptr++ - '0');
10660 if (gic_part_nr < 1 || gic_part_nr > MAX_GLOBAL_ANIM_PARTS)
10661 return ANIM_EVENT_NONE;
10663 result |= gic_part_nr << ANIM_EVENT_PART_BIT;
10667 // invalid animation part number specified
10669 return ANIM_EVENT_NONE;
10673 // discard result if next character is neither delimiter nor whitespace
10674 if (!(*s_ptr == ',' || *s_ptr == '\0' ||
10675 *s_ptr == ' ' || *s_ptr == '\t'))
10676 return ANIM_EVENT_NONE;
10681 static int get_anim_parameter_values(char *s)
10683 int list_pos = ANIM_EVENT_UNDEFINED;
10684 int event_value = ANIM_EVENT_DEFAULT;
10686 if (string_has_parameter(s, "any"))
10687 event_value |= ANIM_EVENT_ANY;
10689 if (string_has_parameter(s, "click:self") ||
10690 string_has_parameter(s, "click") ||
10691 string_has_parameter(s, "self"))
10692 event_value |= ANIM_EVENT_SELF;
10694 if (string_has_parameter(s, "unclick:any"))
10695 event_value |= ANIM_EVENT_UNCLICK_ANY;
10697 // if animation event found, add it to global animation event list
10698 if (event_value != ANIM_EVENT_NONE)
10699 list_pos = AddGlobalAnimEventValue(list_pos, event_value);
10703 // add optional "click:anim_X" or "click:anim_X.part_X" parameter
10704 event_value = get_anim_parameter_value(s);
10706 // if animation event found, add it to global animation event list
10707 if (event_value != ANIM_EVENT_NONE)
10708 list_pos = AddGlobalAnimEventValue(list_pos, event_value);
10710 // continue with next part of the string, starting with next comma
10711 s = strchr(s + 1, ',');
10717 static int get_anim_action_parameter_value(char *token)
10719 // check most common default case first to massively speed things up
10720 if (strEqual(token, ARG_UNDEFINED))
10721 return ANIM_EVENT_ACTION_NONE;
10723 int result = getImageIDFromToken(token);
10727 char *gfx_token = getStringCat2("gfx.", token);
10729 result = getImageIDFromToken(gfx_token);
10731 checked_free(gfx_token);
10736 Key key = getKeyFromX11KeyName(token);
10738 if (key != KSYM_UNDEFINED)
10739 result = -(int)key;
10743 result = ANIM_EVENT_ACTION_NONE;
10748 int get_parameter_value(char *value_raw, char *suffix, int type)
10750 char *value = getStringToLower(value_raw);
10751 int result = 0; // probably a save default value
10753 if (strEqual(suffix, ".direction"))
10755 result = (strEqual(value, "left") ? MV_LEFT :
10756 strEqual(value, "right") ? MV_RIGHT :
10757 strEqual(value, "up") ? MV_UP :
10758 strEqual(value, "down") ? MV_DOWN : MV_NONE);
10760 else if (strEqual(suffix, ".position"))
10762 result = (strEqual(value, "left") ? POS_LEFT :
10763 strEqual(value, "right") ? POS_RIGHT :
10764 strEqual(value, "top") ? POS_TOP :
10765 strEqual(value, "upper") ? POS_UPPER :
10766 strEqual(value, "middle") ? POS_MIDDLE :
10767 strEqual(value, "lower") ? POS_LOWER :
10768 strEqual(value, "bottom") ? POS_BOTTOM :
10769 strEqual(value, "any") ? POS_ANY :
10770 strEqual(value, "last") ? POS_LAST : POS_UNDEFINED);
10772 else if (strEqual(suffix, ".align"))
10774 result = (strEqual(value, "left") ? ALIGN_LEFT :
10775 strEqual(value, "right") ? ALIGN_RIGHT :
10776 strEqual(value, "center") ? ALIGN_CENTER :
10777 strEqual(value, "middle") ? ALIGN_CENTER : ALIGN_DEFAULT);
10779 else if (strEqual(suffix, ".valign"))
10781 result = (strEqual(value, "top") ? VALIGN_TOP :
10782 strEqual(value, "bottom") ? VALIGN_BOTTOM :
10783 strEqual(value, "middle") ? VALIGN_MIDDLE :
10784 strEqual(value, "center") ? VALIGN_MIDDLE : VALIGN_DEFAULT);
10786 else if (strEqual(suffix, ".anim_mode"))
10788 result = (string_has_parameter(value, "none") ? ANIM_NONE :
10789 string_has_parameter(value, "loop") ? ANIM_LOOP :
10790 string_has_parameter(value, "linear") ? ANIM_LINEAR :
10791 string_has_parameter(value, "pingpong") ? ANIM_PINGPONG :
10792 string_has_parameter(value, "pingpong2") ? ANIM_PINGPONG2 :
10793 string_has_parameter(value, "random") ? ANIM_RANDOM :
10794 string_has_parameter(value, "ce_value") ? ANIM_CE_VALUE :
10795 string_has_parameter(value, "ce_score") ? ANIM_CE_SCORE :
10796 string_has_parameter(value, "ce_delay") ? ANIM_CE_DELAY :
10797 string_has_parameter(value, "horizontal") ? ANIM_HORIZONTAL :
10798 string_has_parameter(value, "vertical") ? ANIM_VERTICAL :
10799 string_has_parameter(value, "centered") ? ANIM_CENTERED :
10800 string_has_parameter(value, "all") ? ANIM_ALL :
10803 if (string_has_parameter(value, "once"))
10804 result |= ANIM_ONCE;
10806 if (string_has_parameter(value, "reverse"))
10807 result |= ANIM_REVERSE;
10809 if (string_has_parameter(value, "opaque_player"))
10810 result |= ANIM_OPAQUE_PLAYER;
10812 if (string_has_parameter(value, "static_panel"))
10813 result |= ANIM_STATIC_PANEL;
10815 else if (strEqual(suffix, ".init_event") ||
10816 strEqual(suffix, ".anim_event"))
10818 result = get_anim_parameter_values(value);
10820 else if (strEqual(suffix, ".init_delay_action") ||
10821 strEqual(suffix, ".anim_delay_action") ||
10822 strEqual(suffix, ".post_delay_action") ||
10823 strEqual(suffix, ".init_event_action") ||
10824 strEqual(suffix, ".anim_event_action"))
10826 result = get_anim_action_parameter_value(value_raw);
10828 else if (strEqual(suffix, ".class"))
10830 result = (strEqual(value, ARG_UNDEFINED) ? ARG_UNDEFINED_VALUE :
10831 get_hash_from_key(value));
10833 else if (strEqual(suffix, ".style"))
10835 result = STYLE_DEFAULT;
10837 if (string_has_parameter(value, "accurate_borders"))
10838 result |= STYLE_ACCURATE_BORDERS;
10840 if (string_has_parameter(value, "inner_corners"))
10841 result |= STYLE_INNER_CORNERS;
10843 if (string_has_parameter(value, "reverse"))
10844 result |= STYLE_REVERSE;
10846 if (string_has_parameter(value, "leftmost_position"))
10847 result |= STYLE_LEFTMOST_POSITION;
10849 if (string_has_parameter(value, "block_clicks"))
10850 result |= STYLE_BLOCK;
10852 if (string_has_parameter(value, "passthrough_clicks"))
10853 result |= STYLE_PASSTHROUGH;
10855 if (string_has_parameter(value, "multiple_actions"))
10856 result |= STYLE_MULTIPLE_ACTIONS;
10858 else if (strEqual(suffix, ".fade_mode"))
10860 result = (string_has_parameter(value, "none") ? FADE_MODE_NONE :
10861 string_has_parameter(value, "fade") ? FADE_MODE_FADE :
10862 string_has_parameter(value, "crossfade") ? FADE_MODE_CROSSFADE :
10863 string_has_parameter(value, "melt") ? FADE_MODE_MELT :
10864 string_has_parameter(value, "curtain") ? FADE_MODE_CURTAIN :
10865 FADE_MODE_DEFAULT);
10867 else if (strEqual(suffix, ".auto_delay_unit"))
10869 result = (string_has_parameter(value, "ms") ? AUTO_DELAY_UNIT_MS :
10870 string_has_parameter(value, "frames") ? AUTO_DELAY_UNIT_FRAMES :
10871 AUTO_DELAY_UNIT_DEFAULT);
10873 else if (strPrefix(suffix, ".font")) // (may also be ".font_xyz")
10875 result = gfx.get_font_from_token_function(value);
10877 else // generic parameter of type integer or boolean
10879 result = (strEqual(value, ARG_UNDEFINED) ? ARG_UNDEFINED_VALUE :
10880 type == TYPE_INTEGER ? get_integer_from_string(value) :
10881 type == TYPE_BOOLEAN ? get_boolean_from_string(value) :
10882 ARG_UNDEFINED_VALUE);
10890 static int get_token_parameter_value(char *token, char *value_raw)
10894 if (token == NULL || value_raw == NULL)
10895 return ARG_UNDEFINED_VALUE;
10897 suffix = strrchr(token, '.');
10898 if (suffix == NULL)
10901 if (strEqual(suffix, ".element"))
10902 return getElementFromToken(value_raw);
10904 // !!! USE CORRECT VALUE TYPE (currently works also for TYPE_BOOLEAN) !!!
10905 return get_parameter_value(value_raw, suffix, TYPE_INTEGER);
10908 void InitMenuDesignSettings_Static(void)
10912 // always start with reliable default values from static default config
10913 for (i = 0; image_config_vars[i].token != NULL; i++)
10915 char *value = getHashEntry(image_config_hash, image_config_vars[i].token);
10918 *image_config_vars[i].value =
10919 get_token_parameter_value(image_config_vars[i].token, value);
10923 static void InitMenuDesignSettings_SpecialPreProcessing(void)
10927 // the following initializes hierarchical values from static configuration
10929 // special case: initialize "ARG_DEFAULT" values in static default config
10930 // (e.g., initialize "[titlemessage].fade_mode" from "[title].fade_mode")
10931 titlescreen_initial_first_default.fade_mode =
10932 title_initial_first_default.fade_mode;
10933 titlescreen_initial_first_default.fade_delay =
10934 title_initial_first_default.fade_delay;
10935 titlescreen_initial_first_default.post_delay =
10936 title_initial_first_default.post_delay;
10937 titlescreen_initial_first_default.auto_delay =
10938 title_initial_first_default.auto_delay;
10939 titlescreen_initial_first_default.auto_delay_unit =
10940 title_initial_first_default.auto_delay_unit;
10941 titlescreen_first_default.fade_mode = title_first_default.fade_mode;
10942 titlescreen_first_default.fade_delay = title_first_default.fade_delay;
10943 titlescreen_first_default.post_delay = title_first_default.post_delay;
10944 titlescreen_first_default.auto_delay = title_first_default.auto_delay;
10945 titlescreen_first_default.auto_delay_unit =
10946 title_first_default.auto_delay_unit;
10947 titlemessage_initial_first_default.fade_mode =
10948 title_initial_first_default.fade_mode;
10949 titlemessage_initial_first_default.fade_delay =
10950 title_initial_first_default.fade_delay;
10951 titlemessage_initial_first_default.post_delay =
10952 title_initial_first_default.post_delay;
10953 titlemessage_initial_first_default.auto_delay =
10954 title_initial_first_default.auto_delay;
10955 titlemessage_initial_first_default.auto_delay_unit =
10956 title_initial_first_default.auto_delay_unit;
10957 titlemessage_first_default.fade_mode = title_first_default.fade_mode;
10958 titlemessage_first_default.fade_delay = title_first_default.fade_delay;
10959 titlemessage_first_default.post_delay = title_first_default.post_delay;
10960 titlemessage_first_default.auto_delay = title_first_default.auto_delay;
10961 titlemessage_first_default.auto_delay_unit =
10962 title_first_default.auto_delay_unit;
10964 titlescreen_initial_default.fade_mode = title_initial_default.fade_mode;
10965 titlescreen_initial_default.fade_delay = title_initial_default.fade_delay;
10966 titlescreen_initial_default.post_delay = title_initial_default.post_delay;
10967 titlescreen_initial_default.auto_delay = title_initial_default.auto_delay;
10968 titlescreen_initial_default.auto_delay_unit =
10969 title_initial_default.auto_delay_unit;
10970 titlescreen_default.fade_mode = title_default.fade_mode;
10971 titlescreen_default.fade_delay = title_default.fade_delay;
10972 titlescreen_default.post_delay = title_default.post_delay;
10973 titlescreen_default.auto_delay = title_default.auto_delay;
10974 titlescreen_default.auto_delay_unit = title_default.auto_delay_unit;
10975 titlemessage_initial_default.fade_mode = title_initial_default.fade_mode;
10976 titlemessage_initial_default.fade_delay = title_initial_default.fade_delay;
10977 titlemessage_initial_default.post_delay = title_initial_default.post_delay;
10978 titlemessage_initial_default.auto_delay_unit =
10979 title_initial_default.auto_delay_unit;
10980 titlemessage_default.fade_mode = title_default.fade_mode;
10981 titlemessage_default.fade_delay = title_default.fade_delay;
10982 titlemessage_default.post_delay = title_default.post_delay;
10983 titlemessage_default.auto_delay = title_default.auto_delay;
10984 titlemessage_default.auto_delay_unit = title_default.auto_delay_unit;
10986 // special case: initialize "ARG_DEFAULT" values in static default config
10987 // (e.g., init "titlemessage_1.fade_mode" from "[titlemessage].fade_mode")
10988 for (i = 0; i < MAX_NUM_TITLE_MESSAGES; i++)
10990 titlescreen_initial_first[i] = titlescreen_initial_first_default;
10991 titlescreen_first[i] = titlescreen_first_default;
10992 titlemessage_initial_first[i] = titlemessage_initial_first_default;
10993 titlemessage_first[i] = titlemessage_first_default;
10995 titlescreen_initial[i] = titlescreen_initial_default;
10996 titlescreen[i] = titlescreen_default;
10997 titlemessage_initial[i] = titlemessage_initial_default;
10998 titlemessage[i] = titlemessage_default;
11001 // special case: initialize "ARG_DEFAULT" values in static default config
11002 // (eg, init "menu.enter_screen.SCORES.xyz" from "menu.enter_screen.xyz")
11003 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
11005 if (i == GFX_SPECIAL_ARG_TITLE) // title values already initialized
11008 menu.enter_screen[i] = menu.enter_screen[GFX_SPECIAL_ARG_DEFAULT];
11009 menu.leave_screen[i] = menu.leave_screen[GFX_SPECIAL_ARG_DEFAULT];
11010 menu.next_screen[i] = menu.next_screen[GFX_SPECIAL_ARG_DEFAULT];
11013 // special case: initialize "ARG_DEFAULT" values in static default config
11014 // (eg, init "viewport.door_1.MAIN.xyz" from "viewport.door_1.xyz")
11015 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
11017 viewport.window[i] = viewport.window[GFX_SPECIAL_ARG_DEFAULT];
11018 viewport.playfield[i] = viewport.playfield[GFX_SPECIAL_ARG_DEFAULT];
11019 viewport.door_1[i] = viewport.door_1[GFX_SPECIAL_ARG_DEFAULT];
11021 if (i == GFX_SPECIAL_ARG_EDITOR) // editor values already initialized
11024 viewport.door_2[i] = viewport.door_2[GFX_SPECIAL_ARG_DEFAULT];
11028 static void InitMenuDesignSettings_SpecialPostProcessing(void)
11032 struct XY *dst, *src;
11034 game_buttons_xy[] =
11036 { &game.button.save, &game.button.stop },
11037 { &game.button.pause2, &game.button.pause },
11038 { &game.button.load, &game.button.play },
11039 { &game.button.undo, &game.button.stop },
11040 { &game.button.redo, &game.button.play },
11046 // special case: initialize later added SETUP list size from LEVELS value
11047 if (menu.list_size[GAME_MODE_SETUP] == -1)
11048 menu.list_size[GAME_MODE_SETUP] = menu.list_size[GAME_MODE_LEVELS];
11050 // set default position for snapshot buttons to stop/pause/play buttons
11051 for (i = 0; game_buttons_xy[i].dst != NULL; i++)
11052 if ((*game_buttons_xy[i].dst).x == -1 &&
11053 (*game_buttons_xy[i].dst).y == -1)
11054 *game_buttons_xy[i].dst = *game_buttons_xy[i].src;
11056 // --------------------------------------------------------------------------
11057 // dynamic viewports (including playfield margins, borders and alignments)
11058 // --------------------------------------------------------------------------
11060 // dynamic viewports currently only supported for landscape mode
11061 int display_width = MAX(video.display_width, video.display_height);
11062 int display_height = MIN(video.display_width, video.display_height);
11064 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
11066 struct RectWithBorder *vp_window = &viewport.window[i];
11067 struct RectWithBorder *vp_playfield = &viewport.playfield[i];
11068 struct RectWithBorder *vp_door_1 = &viewport.door_1[i];
11069 struct RectWithBorder *vp_door_2 = &viewport.door_2[i];
11070 boolean dynamic_window_width = (vp_window->min_width != -1);
11071 boolean dynamic_window_height = (vp_window->min_height != -1);
11072 boolean dynamic_playfield_width = (vp_playfield->min_width != -1);
11073 boolean dynamic_playfield_height = (vp_playfield->min_height != -1);
11075 // adjust window size if min/max width/height is specified
11077 if (vp_window->min_width != -1)
11079 int window_width = display_width;
11081 // when using static window height, use aspect ratio of display
11082 if (vp_window->min_height == -1)
11083 window_width = vp_window->height * display_width / display_height;
11085 vp_window->width = MAX(vp_window->min_width, window_width);
11088 if (vp_window->min_height != -1)
11090 int window_height = display_height;
11092 // when using static window width, use aspect ratio of display
11093 if (vp_window->min_width == -1)
11094 window_height = vp_window->width * display_height / display_width;
11096 vp_window->height = MAX(vp_window->min_height, window_height);
11099 if (vp_window->max_width != -1)
11100 vp_window->width = MIN(vp_window->width, vp_window->max_width);
11102 if (vp_window->max_height != -1)
11103 vp_window->height = MIN(vp_window->height, vp_window->max_height);
11105 int playfield_width = vp_window->width;
11106 int playfield_height = vp_window->height;
11108 // adjust playfield size and position according to specified margins
11110 playfield_width -= vp_playfield->margin_left;
11111 playfield_width -= vp_playfield->margin_right;
11113 playfield_height -= vp_playfield->margin_top;
11114 playfield_height -= vp_playfield->margin_bottom;
11116 // adjust playfield size if min/max width/height is specified
11118 if (vp_playfield->min_width != -1)
11119 vp_playfield->width = MAX(vp_playfield->min_width, playfield_width);
11121 if (vp_playfield->min_height != -1)
11122 vp_playfield->height = MAX(vp_playfield->min_height, playfield_height);
11124 if (vp_playfield->max_width != -1)
11125 vp_playfield->width = MIN(vp_playfield->width, vp_playfield->max_width);
11127 if (vp_playfield->max_height != -1)
11128 vp_playfield->height = MIN(vp_playfield->height,vp_playfield->max_height);
11130 // adjust playfield position according to specified alignment
11132 if (vp_playfield->align == ALIGN_LEFT || vp_playfield->x > 0)
11133 vp_playfield->x = ALIGNED_VP_XPOS(vp_playfield);
11134 else if (vp_playfield->align == ALIGN_CENTER)
11135 vp_playfield->x = playfield_width / 2 - vp_playfield->width / 2;
11136 else if (vp_playfield->align == ALIGN_RIGHT)
11137 vp_playfield->x += playfield_width - vp_playfield->width;
11139 if (vp_playfield->valign == VALIGN_TOP || vp_playfield->y > 0)
11140 vp_playfield->y = ALIGNED_VP_YPOS(vp_playfield);
11141 else if (vp_playfield->valign == VALIGN_MIDDLE)
11142 vp_playfield->y = playfield_height / 2 - vp_playfield->height / 2;
11143 else if (vp_playfield->valign == VALIGN_BOTTOM)
11144 vp_playfield->y += playfield_height - vp_playfield->height;
11146 vp_playfield->x += vp_playfield->margin_left;
11147 vp_playfield->y += vp_playfield->margin_top;
11149 // adjust individual playfield borders if only default border is specified
11151 if (vp_playfield->border_left == -1)
11152 vp_playfield->border_left = vp_playfield->border_size;
11153 if (vp_playfield->border_right == -1)
11154 vp_playfield->border_right = vp_playfield->border_size;
11155 if (vp_playfield->border_top == -1)
11156 vp_playfield->border_top = vp_playfield->border_size;
11157 if (vp_playfield->border_bottom == -1)
11158 vp_playfield->border_bottom = vp_playfield->border_size;
11160 // set dynamic playfield borders if borders are specified as undefined
11161 // (but only if window size was dynamic and playfield size was static)
11163 if (dynamic_window_width && !dynamic_playfield_width)
11165 if (vp_playfield->border_left == -1)
11167 vp_playfield->border_left = (vp_playfield->x -
11168 vp_playfield->margin_left);
11169 vp_playfield->x -= vp_playfield->border_left;
11170 vp_playfield->width += vp_playfield->border_left;
11173 if (vp_playfield->border_right == -1)
11175 vp_playfield->border_right = (vp_window->width -
11177 vp_playfield->width -
11178 vp_playfield->margin_right);
11179 vp_playfield->width += vp_playfield->border_right;
11183 if (dynamic_window_height && !dynamic_playfield_height)
11185 if (vp_playfield->border_top == -1)
11187 vp_playfield->border_top = (vp_playfield->y -
11188 vp_playfield->margin_top);
11189 vp_playfield->y -= vp_playfield->border_top;
11190 vp_playfield->height += vp_playfield->border_top;
11193 if (vp_playfield->border_bottom == -1)
11195 vp_playfield->border_bottom = (vp_window->height -
11197 vp_playfield->height -
11198 vp_playfield->margin_bottom);
11199 vp_playfield->height += vp_playfield->border_bottom;
11203 // adjust playfield size to be a multiple of a defined alignment tile size
11205 int align_size = vp_playfield->align_size;
11206 int playfield_xtiles = vp_playfield->width / align_size;
11207 int playfield_ytiles = vp_playfield->height / align_size;
11208 int playfield_width_corrected = playfield_xtiles * align_size;
11209 int playfield_height_corrected = playfield_ytiles * align_size;
11210 boolean is_playfield_mode = (i == GFX_SPECIAL_ARG_PLAYING ||
11211 i == GFX_SPECIAL_ARG_EDITOR);
11213 if (is_playfield_mode &&
11214 dynamic_playfield_width &&
11215 vp_playfield->width != playfield_width_corrected)
11217 int playfield_xdiff = vp_playfield->width - playfield_width_corrected;
11219 vp_playfield->width = playfield_width_corrected;
11221 if (vp_playfield->align == ALIGN_LEFT)
11223 vp_playfield->border_left += playfield_xdiff;
11225 else if (vp_playfield->align == ALIGN_RIGHT)
11227 vp_playfield->border_right += playfield_xdiff;
11229 else if (vp_playfield->align == ALIGN_CENTER)
11231 int border_left_diff = playfield_xdiff / 2;
11232 int border_right_diff = playfield_xdiff - border_left_diff;
11234 vp_playfield->border_left += border_left_diff;
11235 vp_playfield->border_right += border_right_diff;
11239 if (is_playfield_mode &&
11240 dynamic_playfield_height &&
11241 vp_playfield->height != playfield_height_corrected)
11243 int playfield_ydiff = vp_playfield->height - playfield_height_corrected;
11245 vp_playfield->height = playfield_height_corrected;
11247 if (vp_playfield->valign == VALIGN_TOP)
11249 vp_playfield->border_top += playfield_ydiff;
11251 else if (vp_playfield->align == VALIGN_BOTTOM)
11253 vp_playfield->border_right += playfield_ydiff;
11255 else if (vp_playfield->align == VALIGN_MIDDLE)
11257 int border_top_diff = playfield_ydiff / 2;
11258 int border_bottom_diff = playfield_ydiff - border_top_diff;
11260 vp_playfield->border_top += border_top_diff;
11261 vp_playfield->border_bottom += border_bottom_diff;
11265 // adjust door positions according to specified alignment
11267 for (j = 0; j < 2; j++)
11269 struct RectWithBorder *vp_door = (j == 0 ? vp_door_1 : vp_door_2);
11271 if (vp_door->align == ALIGN_LEFT || vp_door->x > 0)
11272 vp_door->x = ALIGNED_VP_XPOS(vp_door);
11273 else if (vp_door->align == ALIGN_CENTER)
11274 vp_door->x = vp_window->width / 2 - vp_door->width / 2;
11275 else if (vp_door->align == ALIGN_RIGHT)
11276 vp_door->x += vp_window->width - vp_door->width;
11278 if (vp_door->valign == VALIGN_TOP || vp_door->y > 0)
11279 vp_door->y = ALIGNED_VP_YPOS(vp_door);
11280 else if (vp_door->valign == VALIGN_MIDDLE)
11281 vp_door->y = vp_window->height / 2 - vp_door->height / 2;
11282 else if (vp_door->valign == VALIGN_BOTTOM)
11283 vp_door->y += vp_window->height - vp_door->height;
11288 static void InitMenuDesignSettings_SpecialPostProcessing_AfterGraphics(void)
11292 struct XYTileSize *dst, *src;
11295 editor_buttons_xy[] =
11298 &editor.button.element_left, &editor.palette.element_left,
11299 IMG_GFX_EDITOR_BUTTON_ELEMENT_LEFT
11302 &editor.button.element_middle, &editor.palette.element_middle,
11303 IMG_GFX_EDITOR_BUTTON_ELEMENT_MIDDLE
11306 &editor.button.element_right, &editor.palette.element_right,
11307 IMG_GFX_EDITOR_BUTTON_ELEMENT_RIGHT
11314 // set default position for element buttons to element graphics
11315 for (i = 0; editor_buttons_xy[i].dst != NULL; i++)
11317 if ((*editor_buttons_xy[i].dst).x == -1 &&
11318 (*editor_buttons_xy[i].dst).y == -1)
11320 struct GraphicInfo *gd = &graphic_info[editor_buttons_xy[i].graphic];
11322 gd->width = gd->height = editor_buttons_xy[i].src->tile_size;
11324 *editor_buttons_xy[i].dst = *editor_buttons_xy[i].src;
11328 // adjust editor palette rows and columns if specified to be dynamic
11330 if (editor.palette.cols == -1)
11332 int vp_width = viewport.playfield[GFX_SPECIAL_ARG_EDITOR].width;
11333 int bt_width = graphic_info[IMG_EDITOR_PALETTE_BUTTON].width;
11334 int sc_width = graphic_info[IMG_EDITOR_PALETTE_SCROLLBAR].width;
11336 editor.palette.cols = (vp_width - sc_width) / bt_width;
11338 if (editor.palette.x == -1)
11340 int palette_width = editor.palette.cols * bt_width + sc_width;
11342 editor.palette.x = (vp_width - palette_width) / 2;
11346 if (editor.palette.rows == -1)
11348 int vp_height = viewport.playfield[GFX_SPECIAL_ARG_EDITOR].height;
11349 int bt_height = graphic_info[IMG_EDITOR_PALETTE_BUTTON].height;
11350 int tx_height = getFontHeight(FONT_TEXT_2);
11352 editor.palette.rows = (vp_height - tx_height) / bt_height;
11354 if (editor.palette.y == -1)
11356 int palette_height = editor.palette.rows * bt_height + tx_height;
11358 editor.palette.y = (vp_height - palette_height) / 2;
11363 static void LoadMenuDesignSettingsFromFilename(char *filename)
11365 static struct TitleFadingInfo tfi;
11366 static struct TitleMessageInfo tmi;
11367 static struct TokenInfo title_tokens[] =
11369 { TYPE_INTEGER, &tfi.fade_mode, ".fade_mode" },
11370 { TYPE_INTEGER, &tfi.fade_delay, ".fade_delay" },
11371 { TYPE_INTEGER, &tfi.post_delay, ".post_delay" },
11372 { TYPE_INTEGER, &tfi.auto_delay, ".auto_delay" },
11373 { TYPE_INTEGER, &tfi.auto_delay_unit, ".auto_delay_unit" },
11377 static struct TokenInfo titlemessage_tokens[] =
11379 { TYPE_INTEGER, &tmi.x, ".x" },
11380 { TYPE_INTEGER, &tmi.y, ".y" },
11381 { TYPE_INTEGER, &tmi.width, ".width" },
11382 { TYPE_INTEGER, &tmi.height, ".height" },
11383 { TYPE_INTEGER, &tmi.chars, ".chars" },
11384 { TYPE_INTEGER, &tmi.lines, ".lines" },
11385 { TYPE_INTEGER, &tmi.align, ".align" },
11386 { TYPE_INTEGER, &tmi.valign, ".valign" },
11387 { TYPE_INTEGER, &tmi.font, ".font" },
11388 { TYPE_BOOLEAN, &tmi.autowrap, ".autowrap" },
11389 { TYPE_BOOLEAN, &tmi.centered, ".centered" },
11390 { TYPE_BOOLEAN, &tmi.parse_comments, ".parse_comments" },
11391 { TYPE_INTEGER, &tmi.sort_priority, ".sort_priority" },
11392 { TYPE_INTEGER, &tmi.fade_mode, ".fade_mode" },
11393 { TYPE_INTEGER, &tmi.fade_delay, ".fade_delay" },
11394 { TYPE_INTEGER, &tmi.post_delay, ".post_delay" },
11395 { TYPE_INTEGER, &tmi.auto_delay, ".auto_delay" },
11396 { TYPE_INTEGER, &tmi.auto_delay_unit, ".auto_delay_unit" },
11402 struct TitleFadingInfo *info;
11407 // initialize first titles from "enter screen" definitions, if defined
11408 { &title_initial_first_default, "menu.enter_screen.TITLE" },
11409 { &title_first_default, "menu.enter_screen.TITLE" },
11411 // initialize title screens from "next screen" definitions, if defined
11412 { &title_initial_default, "menu.next_screen.TITLE" },
11413 { &title_default, "menu.next_screen.TITLE" },
11419 struct TitleMessageInfo *array;
11422 titlemessage_arrays[] =
11424 // initialize first titles from "enter screen" definitions, if defined
11425 { titlescreen_initial_first, "menu.enter_screen.TITLE" },
11426 { titlescreen_first, "menu.enter_screen.TITLE" },
11427 { titlemessage_initial_first, "menu.enter_screen.TITLE" },
11428 { titlemessage_first, "menu.enter_screen.TITLE" },
11430 // initialize titles from "next screen" definitions, if defined
11431 { titlescreen_initial, "menu.next_screen.TITLE" },
11432 { titlescreen, "menu.next_screen.TITLE" },
11433 { titlemessage_initial, "menu.next_screen.TITLE" },
11434 { titlemessage, "menu.next_screen.TITLE" },
11436 // overwrite titles with title definitions, if defined
11437 { titlescreen_initial_first, "[title_initial]" },
11438 { titlescreen_first, "[title]" },
11439 { titlemessage_initial_first, "[title_initial]" },
11440 { titlemessage_first, "[title]" },
11442 { titlescreen_initial, "[title_initial]" },
11443 { titlescreen, "[title]" },
11444 { titlemessage_initial, "[title_initial]" },
11445 { titlemessage, "[title]" },
11447 // overwrite titles with title screen/message definitions, if defined
11448 { titlescreen_initial_first, "[titlescreen_initial]" },
11449 { titlescreen_first, "[titlescreen]" },
11450 { titlemessage_initial_first, "[titlemessage_initial]" },
11451 { titlemessage_first, "[titlemessage]" },
11453 { titlescreen_initial, "[titlescreen_initial]" },
11454 { titlescreen, "[titlescreen]" },
11455 { titlemessage_initial, "[titlemessage_initial]" },
11456 { titlemessage, "[titlemessage]" },
11460 SetupFileHash *setup_file_hash;
11463 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
11466 // the following initializes hierarchical values from dynamic configuration
11468 // special case: initialize with default values that may be overwritten
11469 // (e.g., init "menu.draw_xoffset.INFO" from "menu.draw_xoffset")
11470 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
11472 struct TokenIntPtrInfo menu_config[] =
11474 { "menu.draw_xoffset", &menu.draw_xoffset[i] },
11475 { "menu.draw_yoffset", &menu.draw_yoffset[i] },
11476 { "menu.list_size", &menu.list_size[i] }
11479 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
11481 char *token = menu_config[j].token;
11482 char *value = getHashEntry(setup_file_hash, token);
11485 *menu_config[j].value = get_integer_from_string(value);
11489 // special case: initialize with default values that may be overwritten
11490 // (eg, init "menu.draw_xoffset.INFO[XXX]" from "menu.draw_xoffset.INFO")
11491 for (i = 0; i < NUM_SPECIAL_GFX_INFO_ARGS; i++)
11493 struct TokenIntPtrInfo menu_config[] =
11495 { "menu.draw_xoffset.INFO", &menu.draw_xoffset_info[i] },
11496 { "menu.draw_yoffset.INFO", &menu.draw_yoffset_info[i] },
11497 { "menu.list_size.INFO", &menu.list_size_info[i] }
11500 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
11502 char *token = menu_config[j].token;
11503 char *value = getHashEntry(setup_file_hash, token);
11506 *menu_config[j].value = get_integer_from_string(value);
11510 // special case: initialize with default values that may be overwritten
11511 // (eg, init "menu.draw_xoffset.SETUP[XXX]" from "menu.draw_xoffset.SETUP")
11512 for (i = 0; i < NUM_SPECIAL_GFX_SETUP_ARGS; i++)
11514 struct TokenIntPtrInfo menu_config[] =
11516 { "menu.draw_xoffset.SETUP", &menu.draw_xoffset_setup[i] },
11517 { "menu.draw_yoffset.SETUP", &menu.draw_yoffset_setup[i] }
11520 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
11522 char *token = menu_config[j].token;
11523 char *value = getHashEntry(setup_file_hash, token);
11526 *menu_config[j].value = get_integer_from_string(value);
11530 // special case: initialize with default values that may be overwritten
11531 // (eg, init "menu.line_spacing.INFO[XXX]" from "menu.line_spacing.INFO")
11532 for (i = 0; i < NUM_SPECIAL_GFX_INFO_ARGS; i++)
11534 struct TokenIntPtrInfo menu_config[] =
11536 { "menu.left_spacing.INFO", &menu.left_spacing_info[i] },
11537 { "menu.right_spacing.INFO", &menu.right_spacing_info[i] },
11538 { "menu.top_spacing.INFO", &menu.top_spacing_info[i] },
11539 { "menu.bottom_spacing.INFO", &menu.bottom_spacing_info[i] },
11540 { "menu.paragraph_spacing.INFO", &menu.paragraph_spacing_info[i] },
11541 { "menu.headline1_spacing.INFO", &menu.headline1_spacing_info[i] },
11542 { "menu.headline2_spacing.INFO", &menu.headline2_spacing_info[i] },
11543 { "menu.line_spacing.INFO", &menu.line_spacing_info[i] },
11544 { "menu.extra_spacing.INFO", &menu.extra_spacing_info[i] },
11547 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
11549 char *token = menu_config[j].token;
11550 char *value = getHashEntry(setup_file_hash, token);
11553 *menu_config[j].value = get_integer_from_string(value);
11557 // special case: initialize with default values that may be overwritten
11558 // (eg, init "menu.enter_screen.SCORES.xyz" from "menu.enter_screen.xyz")
11559 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
11561 struct TokenIntPtrInfo menu_config[] =
11563 { "menu.enter_screen.fade_mode", &menu.enter_screen[i].fade_mode },
11564 { "menu.enter_screen.fade_delay", &menu.enter_screen[i].fade_delay },
11565 { "menu.enter_screen.post_delay", &menu.enter_screen[i].post_delay },
11566 { "menu.leave_screen.fade_mode", &menu.leave_screen[i].fade_mode },
11567 { "menu.leave_screen.fade_delay", &menu.leave_screen[i].fade_delay },
11568 { "menu.leave_screen.post_delay", &menu.leave_screen[i].post_delay },
11569 { "menu.next_screen.fade_mode", &menu.next_screen[i].fade_mode },
11570 { "menu.next_screen.fade_delay", &menu.next_screen[i].fade_delay },
11571 { "menu.next_screen.post_delay", &menu.next_screen[i].post_delay }
11574 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
11576 char *token = menu_config[j].token;
11577 char *value = getHashEntry(setup_file_hash, token);
11580 *menu_config[j].value = get_token_parameter_value(token, value);
11584 // special case: initialize with default values that may be overwritten
11585 // (eg, init "viewport.door_1.MAIN.xyz" from "viewport.door_1.xyz")
11586 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
11590 char *token_prefix;
11591 struct RectWithBorder *struct_ptr;
11595 { "viewport.window", &viewport.window[i] },
11596 { "viewport.playfield", &viewport.playfield[i] },
11597 { "viewport.door_1", &viewport.door_1[i] },
11598 { "viewport.door_2", &viewport.door_2[i] }
11601 for (j = 0; j < ARRAY_SIZE(vp_struct); j++)
11603 struct TokenIntPtrInfo vp_config[] =
11605 { ".x", &vp_struct[j].struct_ptr->x },
11606 { ".y", &vp_struct[j].struct_ptr->y },
11607 { ".width", &vp_struct[j].struct_ptr->width },
11608 { ".height", &vp_struct[j].struct_ptr->height },
11609 { ".min_width", &vp_struct[j].struct_ptr->min_width },
11610 { ".min_height", &vp_struct[j].struct_ptr->min_height },
11611 { ".max_width", &vp_struct[j].struct_ptr->max_width },
11612 { ".max_height", &vp_struct[j].struct_ptr->max_height },
11613 { ".margin_left", &vp_struct[j].struct_ptr->margin_left },
11614 { ".margin_right", &vp_struct[j].struct_ptr->margin_right },
11615 { ".margin_top", &vp_struct[j].struct_ptr->margin_top },
11616 { ".margin_bottom", &vp_struct[j].struct_ptr->margin_bottom },
11617 { ".border_left", &vp_struct[j].struct_ptr->border_left },
11618 { ".border_right", &vp_struct[j].struct_ptr->border_right },
11619 { ".border_top", &vp_struct[j].struct_ptr->border_top },
11620 { ".border_bottom", &vp_struct[j].struct_ptr->border_bottom },
11621 { ".border_size", &vp_struct[j].struct_ptr->border_size },
11622 { ".align_size", &vp_struct[j].struct_ptr->align_size },
11623 { ".align", &vp_struct[j].struct_ptr->align },
11624 { ".valign", &vp_struct[j].struct_ptr->valign }
11627 for (k = 0; k < ARRAY_SIZE(vp_config); k++)
11629 char *token = getStringCat2(vp_struct[j].token_prefix,
11630 vp_config[k].token);
11631 char *value = getHashEntry(setup_file_hash, token);
11634 *vp_config[k].value = get_token_parameter_value(token, value);
11641 // special case: initialize with default values that may be overwritten
11642 // (e.g., init "[title].fade_mode" from "menu.next_screen.TITLE.fade_mode")
11643 for (i = 0; title_info[i].info != NULL; i++)
11645 struct TitleFadingInfo *info = title_info[i].info;
11646 char *base_token = title_info[i].text;
11648 for (j = 0; title_tokens[j].type != -1; j++)
11650 char *token = getStringCat2(base_token, title_tokens[j].text);
11651 char *value = getHashEntry(setup_file_hash, token);
11655 int parameter_value = get_token_parameter_value(token, value);
11659 *(int *)title_tokens[j].value = (int)parameter_value;
11668 // special case: initialize with default values that may be overwritten
11669 // (e.g., init "titlemessage_1.fade_mode" from "[titlemessage].fade_mode")
11670 for (i = 0; titlemessage_arrays[i].array != NULL; i++)
11672 struct TitleMessageInfo *array = titlemessage_arrays[i].array;
11673 char *base_token = titlemessage_arrays[i].text;
11675 for (j = 0; titlemessage_tokens[j].type != -1; j++)
11677 char *token = getStringCat2(base_token, titlemessage_tokens[j].text);
11678 char *value = getHashEntry(setup_file_hash, token);
11682 int parameter_value = get_token_parameter_value(token, value);
11684 for (k = 0; k < MAX_NUM_TITLE_MESSAGES; k++)
11688 if (titlemessage_tokens[j].type == TYPE_INTEGER)
11689 *(int *)titlemessage_tokens[j].value = (int)parameter_value;
11691 *(boolean *)titlemessage_tokens[j].value = (boolean)parameter_value;
11701 // special case: check if network and preview player positions are redefined,
11702 // to compare this later against the main menu level preview being redefined
11703 struct TokenIntPtrInfo menu_config_players[] =
11705 { "main.network_players.x", &menu.main.network_players.redefined },
11706 { "main.network_players.y", &menu.main.network_players.redefined },
11707 { "main.preview_players.x", &menu.main.preview_players.redefined },
11708 { "main.preview_players.y", &menu.main.preview_players.redefined },
11709 { "preview.x", &preview.redefined },
11710 { "preview.y", &preview.redefined }
11713 for (i = 0; i < ARRAY_SIZE(menu_config_players); i++)
11714 *menu_config_players[i].value = FALSE;
11716 for (i = 0; i < ARRAY_SIZE(menu_config_players); i++)
11717 if (getHashEntry(setup_file_hash, menu_config_players[i].token) != NULL)
11718 *menu_config_players[i].value = TRUE;
11720 // read (and overwrite with) values that may be specified in config file
11721 for (i = 0; image_config_vars[i].token != NULL; i++)
11723 char *value = getHashEntry(setup_file_hash, image_config_vars[i].token);
11725 // (ignore definitions set to "[DEFAULT]" which are already initialized)
11726 if (value != NULL && !strEqual(value, ARG_DEFAULT))
11727 *image_config_vars[i].value =
11728 get_token_parameter_value(image_config_vars[i].token, value);
11731 freeSetupFileHash(setup_file_hash);
11734 void LoadMenuDesignSettings(void)
11736 char *filename_base = UNDEFINED_FILENAME, *filename_local;
11738 InitMenuDesignSettings_Static();
11739 InitMenuDesignSettings_SpecialPreProcessing();
11741 if (!GFX_OVERRIDE_ARTWORK(ARTWORK_TYPE_GRAPHICS))
11743 // first look for special settings configured in level series config
11744 filename_base = getCustomArtworkLevelConfigFilename(ARTWORK_TYPE_GRAPHICS);
11746 if (fileExists(filename_base))
11747 LoadMenuDesignSettingsFromFilename(filename_base);
11750 filename_local = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
11752 if (filename_local != NULL && !strEqual(filename_base, filename_local))
11753 LoadMenuDesignSettingsFromFilename(filename_local);
11755 InitMenuDesignSettings_SpecialPostProcessing();
11758 void LoadMenuDesignSettings_AfterGraphics(void)
11760 InitMenuDesignSettings_SpecialPostProcessing_AfterGraphics();
11763 void LoadUserDefinedEditorElementList(int **elements, int *num_elements)
11765 char *filename = getEditorSetupFilename();
11766 SetupFileList *setup_file_list, *list;
11767 SetupFileHash *element_hash;
11768 int num_unknown_tokens = 0;
11771 if ((setup_file_list = loadSetupFileList(filename)) == NULL)
11774 element_hash = newSetupFileHash();
11776 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
11777 setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
11779 // determined size may be larger than needed (due to unknown elements)
11781 for (list = setup_file_list; list != NULL; list = list->next)
11784 // add space for up to 3 more elements for padding that may be needed
11785 *num_elements += 3;
11787 // free memory for old list of elements, if needed
11788 checked_free(*elements);
11790 // allocate memory for new list of elements
11791 *elements = checked_malloc(*num_elements * sizeof(int));
11794 for (list = setup_file_list; list != NULL; list = list->next)
11796 char *value = getHashEntry(element_hash, list->token);
11798 if (value == NULL) // try to find obsolete token mapping
11800 char *mapped_token = get_mapped_token(list->token);
11802 if (mapped_token != NULL)
11804 value = getHashEntry(element_hash, mapped_token);
11806 free(mapped_token);
11812 (*elements)[(*num_elements)++] = atoi(value);
11816 if (num_unknown_tokens == 0)
11819 Warn("unknown token(s) found in config file:");
11820 Warn("- config file: '%s'", filename);
11822 num_unknown_tokens++;
11825 Warn("- token: '%s'", list->token);
11829 if (num_unknown_tokens > 0)
11832 while (*num_elements % 4) // pad with empty elements, if needed
11833 (*elements)[(*num_elements)++] = EL_EMPTY;
11835 freeSetupFileList(setup_file_list);
11836 freeSetupFileHash(element_hash);
11839 for (i = 0; i < *num_elements; i++)
11840 Debug("editor", "element '%s' [%d]\n",
11841 element_info[(*elements)[i]].token_name, (*elements)[i]);
11845 static struct MusicFileInfo *get_music_file_info_ext(char *basename, int music,
11848 SetupFileHash *setup_file_hash = NULL;
11849 struct MusicFileInfo tmp_music_file_info, *new_music_file_info;
11850 char *filename_music, *filename_prefix, *filename_info;
11856 token_to_value_ptr[] =
11858 { "title_header", &tmp_music_file_info.title_header },
11859 { "artist_header", &tmp_music_file_info.artist_header },
11860 { "album_header", &tmp_music_file_info.album_header },
11861 { "year_header", &tmp_music_file_info.year_header },
11863 { "title", &tmp_music_file_info.title },
11864 { "artist", &tmp_music_file_info.artist },
11865 { "album", &tmp_music_file_info.album },
11866 { "year", &tmp_music_file_info.year },
11872 filename_music = (is_sound ? getCustomSoundFilename(basename) :
11873 getCustomMusicFilename(basename));
11875 if (filename_music == NULL)
11878 // ---------- try to replace file extension ----------
11880 filename_prefix = getStringCopy(filename_music);
11881 if (strrchr(filename_prefix, '.') != NULL)
11882 *strrchr(filename_prefix, '.') = '\0';
11883 filename_info = getStringCat2(filename_prefix, ".txt");
11885 if (fileExists(filename_info))
11886 setup_file_hash = loadSetupFileHash(filename_info);
11888 free(filename_prefix);
11889 free(filename_info);
11891 if (setup_file_hash == NULL)
11893 // ---------- try to add file extension ----------
11895 filename_prefix = getStringCopy(filename_music);
11896 filename_info = getStringCat2(filename_prefix, ".txt");
11898 if (fileExists(filename_info))
11899 setup_file_hash = loadSetupFileHash(filename_info);
11901 free(filename_prefix);
11902 free(filename_info);
11905 if (setup_file_hash == NULL)
11908 // ---------- music file info found ----------
11910 clear_mem(&tmp_music_file_info, sizeof(struct MusicFileInfo));
11912 for (i = 0; token_to_value_ptr[i].token != NULL; i++)
11914 char *value = getHashEntry(setup_file_hash, token_to_value_ptr[i].token);
11916 *token_to_value_ptr[i].value_ptr =
11917 getStringCopy(value != NULL && *value != '\0' ? value : UNKNOWN_NAME);
11920 tmp_music_file_info.basename = getStringCopy(basename);
11921 tmp_music_file_info.music = music;
11922 tmp_music_file_info.is_sound = is_sound;
11924 new_music_file_info = checked_malloc(sizeof(struct MusicFileInfo));
11925 *new_music_file_info = tmp_music_file_info;
11927 return new_music_file_info;
11930 static struct MusicFileInfo *get_music_file_info(char *basename, int music)
11932 return get_music_file_info_ext(basename, music, FALSE);
11935 static struct MusicFileInfo *get_sound_file_info(char *basename, int sound)
11937 return get_music_file_info_ext(basename, sound, TRUE);
11940 static boolean music_info_listed_ext(struct MusicFileInfo *list,
11941 char *basename, boolean is_sound)
11943 for (; list != NULL; list = list->next)
11944 if (list->is_sound == is_sound && strEqual(list->basename, basename))
11950 static boolean music_info_listed(struct MusicFileInfo *list, char *basename)
11952 return music_info_listed_ext(list, basename, FALSE);
11955 static boolean sound_info_listed(struct MusicFileInfo *list, char *basename)
11957 return music_info_listed_ext(list, basename, TRUE);
11960 void LoadMusicInfo(void)
11962 char *music_directory = getCustomMusicDirectory();
11963 int num_music = getMusicListSize();
11964 int num_music_noconf = 0;
11965 int num_sounds = getSoundListSize();
11967 DirectoryEntry *dir_entry;
11968 struct FileInfo *music, *sound;
11969 struct MusicFileInfo *next, **new;
11972 while (music_file_info != NULL)
11974 next = music_file_info->next;
11976 checked_free(music_file_info->basename);
11978 checked_free(music_file_info->title_header);
11979 checked_free(music_file_info->artist_header);
11980 checked_free(music_file_info->album_header);
11981 checked_free(music_file_info->year_header);
11983 checked_free(music_file_info->title);
11984 checked_free(music_file_info->artist);
11985 checked_free(music_file_info->album);
11986 checked_free(music_file_info->year);
11988 free(music_file_info);
11990 music_file_info = next;
11993 new = &music_file_info;
11995 for (i = 0; i < num_music; i++)
11997 music = getMusicListEntry(i);
11999 if (music->filename == NULL)
12002 if (strEqual(music->filename, UNDEFINED_FILENAME))
12005 // a configured file may be not recognized as music
12006 if (!FileIsMusic(music->filename))
12009 if (!music_info_listed(music_file_info, music->filename))
12011 *new = get_music_file_info(music->filename, i);
12014 new = &(*new)->next;
12018 if ((dir = openDirectory(music_directory)) == NULL)
12020 Warn("cannot read music directory '%s'", music_directory);
12025 while ((dir_entry = readDirectory(dir)) != NULL) // loop all entries
12027 char *basename = dir_entry->basename;
12028 boolean music_already_used = FALSE;
12031 // skip all music files that are configured in music config file
12032 for (i = 0; i < num_music; i++)
12034 music = getMusicListEntry(i);
12036 if (music->filename == NULL)
12039 if (strEqual(basename, music->filename))
12041 music_already_used = TRUE;
12046 if (music_already_used)
12049 if (!FileIsMusic(dir_entry->filename))
12052 if (!music_info_listed(music_file_info, basename))
12054 *new = get_music_file_info(basename, MAP_NOCONF_MUSIC(num_music_noconf));
12057 new = &(*new)->next;
12060 num_music_noconf++;
12063 closeDirectory(dir);
12065 for (i = 0; i < num_sounds; i++)
12067 sound = getSoundListEntry(i);
12069 if (sound->filename == NULL)
12072 if (strEqual(sound->filename, UNDEFINED_FILENAME))
12075 // a configured file may be not recognized as sound
12076 if (!FileIsSound(sound->filename))
12079 if (!sound_info_listed(music_file_info, sound->filename))
12081 *new = get_sound_file_info(sound->filename, i);
12083 new = &(*new)->next;
12088 static void add_helpanim_entry(int element, int action, int direction,
12089 int delay, int *num_list_entries)
12091 struct HelpAnimInfo *new_list_entry;
12092 (*num_list_entries)++;
12095 checked_realloc(helpanim_info,
12096 *num_list_entries * sizeof(struct HelpAnimInfo));
12097 new_list_entry = &helpanim_info[*num_list_entries - 1];
12099 new_list_entry->element = element;
12100 new_list_entry->action = action;
12101 new_list_entry->direction = direction;
12102 new_list_entry->delay = delay;
12105 static void print_unknown_token(char *filename, char *token, int token_nr)
12110 Warn("unknown token(s) found in config file:");
12111 Warn("- config file: '%s'", filename);
12114 Warn("- token: '%s'", token);
12117 static void print_unknown_token_end(int token_nr)
12123 void LoadHelpAnimInfo(void)
12125 char *filename = getHelpAnimFilename();
12126 SetupFileList *setup_file_list = NULL, *list;
12127 SetupFileHash *element_hash, *action_hash, *direction_hash;
12128 int num_list_entries = 0;
12129 int num_unknown_tokens = 0;
12132 if (fileExists(filename))
12133 setup_file_list = loadSetupFileList(filename);
12135 if (setup_file_list == NULL)
12137 // use reliable default values from static configuration
12138 SetupFileList *insert_ptr;
12140 insert_ptr = setup_file_list =
12141 newSetupFileList(helpanim_config[0].token,
12142 helpanim_config[0].value);
12144 for (i = 1; helpanim_config[i].token; i++)
12145 insert_ptr = addListEntry(insert_ptr,
12146 helpanim_config[i].token,
12147 helpanim_config[i].value);
12150 element_hash = newSetupFileHash();
12151 action_hash = newSetupFileHash();
12152 direction_hash = newSetupFileHash();
12154 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
12155 setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
12157 for (i = 0; i < NUM_ACTIONS; i++)
12158 setHashEntry(action_hash, element_action_info[i].suffix,
12159 i_to_a(element_action_info[i].value));
12161 // do not store direction index (bit) here, but direction value!
12162 for (i = 0; i < NUM_DIRECTIONS_FULL; i++)
12163 setHashEntry(direction_hash, element_direction_info[i].suffix,
12164 i_to_a(1 << element_direction_info[i].value));
12166 for (list = setup_file_list; list != NULL; list = list->next)
12168 char *element_token, *action_token, *direction_token;
12169 char *element_value, *action_value, *direction_value;
12170 int delay = atoi(list->value);
12172 if (strEqual(list->token, "end"))
12174 add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
12179 /* first try to break element into element/action/direction parts;
12180 if this does not work, also accept combined "element[.act][.dir]"
12181 elements (like "dynamite.active"), which are unique elements */
12183 if (strchr(list->token, '.') == NULL) // token contains no '.'
12185 element_value = getHashEntry(element_hash, list->token);
12186 if (element_value != NULL) // element found
12187 add_helpanim_entry(atoi(element_value), -1, -1, delay,
12188 &num_list_entries);
12191 // no further suffixes found -- this is not an element
12192 print_unknown_token(filename, list->token, num_unknown_tokens++);
12198 // token has format "<prefix>.<something>"
12200 action_token = strchr(list->token, '.'); // suffix may be action ...
12201 direction_token = action_token; // ... or direction
12203 element_token = getStringCopy(list->token);
12204 *strchr(element_token, '.') = '\0';
12206 element_value = getHashEntry(element_hash, element_token);
12208 if (element_value == NULL) // this is no element
12210 element_value = getHashEntry(element_hash, list->token);
12211 if (element_value != NULL) // combined element found
12212 add_helpanim_entry(atoi(element_value), -1, -1, delay,
12213 &num_list_entries);
12215 print_unknown_token(filename, list->token, num_unknown_tokens++);
12217 free(element_token);
12222 action_value = getHashEntry(action_hash, action_token);
12224 if (action_value != NULL) // action found
12226 add_helpanim_entry(atoi(element_value), atoi(action_value), -1, delay,
12227 &num_list_entries);
12229 free(element_token);
12234 direction_value = getHashEntry(direction_hash, direction_token);
12236 if (direction_value != NULL) // direction found
12238 add_helpanim_entry(atoi(element_value), -1, atoi(direction_value), delay,
12239 &num_list_entries);
12241 free(element_token);
12246 if (strchr(action_token + 1, '.') == NULL)
12248 // no further suffixes found -- this is not an action nor direction
12250 element_value = getHashEntry(element_hash, list->token);
12251 if (element_value != NULL) // combined element found
12252 add_helpanim_entry(atoi(element_value), -1, -1, delay,
12253 &num_list_entries);
12255 print_unknown_token(filename, list->token, num_unknown_tokens++);
12257 free(element_token);
12262 // token has format "<prefix>.<suffix>.<something>"
12264 direction_token = strchr(action_token + 1, '.');
12266 action_token = getStringCopy(action_token);
12267 *strchr(action_token + 1, '.') = '\0';
12269 action_value = getHashEntry(action_hash, action_token);
12271 if (action_value == NULL) // this is no action
12273 element_value = getHashEntry(element_hash, list->token);
12274 if (element_value != NULL) // combined element found
12275 add_helpanim_entry(atoi(element_value), -1, -1, delay,
12276 &num_list_entries);
12278 print_unknown_token(filename, list->token, num_unknown_tokens++);
12280 free(element_token);
12281 free(action_token);
12286 direction_value = getHashEntry(direction_hash, direction_token);
12288 if (direction_value != NULL) // direction found
12290 add_helpanim_entry(atoi(element_value), atoi(action_value),
12291 atoi(direction_value), delay, &num_list_entries);
12293 free(element_token);
12294 free(action_token);
12299 // this is no direction
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);
12312 print_unknown_token_end(num_unknown_tokens);
12314 add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
12315 add_helpanim_entry(HELPANIM_LIST_END, -1, -1, -1, &num_list_entries);
12317 freeSetupFileList(setup_file_list);
12318 freeSetupFileHash(element_hash);
12319 freeSetupFileHash(action_hash);
12320 freeSetupFileHash(direction_hash);
12323 for (i = 0; i < num_list_entries; i++)
12324 Debug("files:LoadHelpAnimInfo", "'%s': %d, %d, %d => %d",
12325 EL_NAME(helpanim_info[i].element),
12326 helpanim_info[i].element,
12327 helpanim_info[i].action,
12328 helpanim_info[i].direction,
12329 helpanim_info[i].delay);
12333 void LoadHelpTextInfo(void)
12335 char *filename = getHelpTextFilename();
12338 if (helptext_info != NULL)
12340 freeSetupFileHash(helptext_info);
12341 helptext_info = NULL;
12344 if (fileExists(filename))
12345 helptext_info = loadSetupFileHash(filename);
12347 if (helptext_info == NULL)
12349 // use reliable default values from static configuration
12350 helptext_info = newSetupFileHash();
12352 for (i = 0; helptext_config[i].token; i++)
12353 setHashEntry(helptext_info,
12354 helptext_config[i].token,
12355 helptext_config[i].value);
12359 BEGIN_HASH_ITERATION(helptext_info, itr)
12361 Debug("files:LoadHelpTextInfo", "'%s' => '%s'",
12362 HASH_ITERATION_TOKEN(itr), HASH_ITERATION_VALUE(itr));
12364 END_HASH_ITERATION(hash, itr)
12369 // ----------------------------------------------------------------------------
12371 // ----------------------------------------------------------------------------
12373 #define MAX_NUM_CONVERT_LEVELS 1000
12375 void ConvertLevels(void)
12377 static LevelDirTree *convert_leveldir = NULL;
12378 static int convert_level_nr = -1;
12379 static int num_levels_handled = 0;
12380 static int num_levels_converted = 0;
12381 static boolean levels_failed[MAX_NUM_CONVERT_LEVELS];
12384 convert_leveldir = getTreeInfoFromIdentifier(leveldir_first,
12385 global.convert_leveldir);
12387 if (convert_leveldir == NULL)
12388 Fail("no such level identifier: '%s'", global.convert_leveldir);
12390 leveldir_current = convert_leveldir;
12392 if (global.convert_level_nr != -1)
12394 convert_leveldir->first_level = global.convert_level_nr;
12395 convert_leveldir->last_level = global.convert_level_nr;
12398 convert_level_nr = convert_leveldir->first_level;
12400 PrintLine("=", 79);
12401 Print("Converting levels\n");
12402 PrintLine("-", 79);
12403 Print("Level series identifier: '%s'\n", convert_leveldir->identifier);
12404 Print("Level series name: '%s'\n", convert_leveldir->name);
12405 Print("Level series author: '%s'\n", convert_leveldir->author);
12406 Print("Number of levels: %d\n", convert_leveldir->levels);
12407 PrintLine("=", 79);
12410 for (i = 0; i < MAX_NUM_CONVERT_LEVELS; i++)
12411 levels_failed[i] = FALSE;
12413 while (convert_level_nr <= convert_leveldir->last_level)
12415 char *level_filename;
12418 level_nr = convert_level_nr++;
12420 Print("Level %03d: ", level_nr);
12422 LoadLevel(level_nr);
12423 if (level.no_level_file || level.no_valid_file)
12425 Print("(no level)\n");
12429 Print("converting level ... ");
12431 level_filename = getDefaultLevelFilename(level_nr);
12432 new_level = !fileExists(level_filename);
12436 SaveLevel(level_nr);
12438 num_levels_converted++;
12440 Print("converted.\n");
12444 if (level_nr >= 0 && level_nr < MAX_NUM_CONVERT_LEVELS)
12445 levels_failed[level_nr] = TRUE;
12447 Print("NOT CONVERTED -- LEVEL ALREADY EXISTS.\n");
12450 num_levels_handled++;
12454 PrintLine("=", 79);
12455 Print("Number of levels handled: %d\n", num_levels_handled);
12456 Print("Number of levels converted: %d (%d%%)\n", num_levels_converted,
12457 (num_levels_handled ?
12458 num_levels_converted * 100 / num_levels_handled : 0));
12459 PrintLine("-", 79);
12460 Print("Summary (for automatic parsing by scripts):\n");
12461 Print("LEVELDIR '%s', CONVERTED %d/%d (%d%%)",
12462 convert_leveldir->identifier, num_levels_converted,
12463 num_levels_handled,
12464 (num_levels_handled ?
12465 num_levels_converted * 100 / num_levels_handled : 0));
12467 if (num_levels_handled != num_levels_converted)
12469 Print(", FAILED:");
12470 for (i = 0; i < MAX_NUM_CONVERT_LEVELS; i++)
12471 if (levels_failed[i])
12476 PrintLine("=", 79);
12478 CloseAllAndExit(0);
12482 // ----------------------------------------------------------------------------
12483 // create and save images for use in level sketches (raw BMP format)
12484 // ----------------------------------------------------------------------------
12486 void CreateLevelSketchImages(void)
12492 InitElementPropertiesGfxElement();
12494 bitmap1 = CreateBitmap(TILEX, TILEY, DEFAULT_DEPTH);
12495 bitmap2 = CreateBitmap(MINI_TILEX, MINI_TILEY, DEFAULT_DEPTH);
12497 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
12499 int element = getMappedElement(i);
12500 char basename1[16];
12501 char basename2[16];
12505 sprintf(basename1, "%04d.bmp", i);
12506 sprintf(basename2, "%04ds.bmp", i);
12508 filename1 = getPath2(global.create_images_dir, basename1);
12509 filename2 = getPath2(global.create_images_dir, basename2);
12511 DrawSizedElement(0, 0, element, TILESIZE);
12512 BlitBitmap(drawto, bitmap1, SX, SY, TILEX, TILEY, 0, 0);
12514 if (SDL_SaveBMP(bitmap1->surface, filename1) != 0)
12515 Fail("cannot save level sketch image file '%s'", filename1);
12517 DrawSizedElement(0, 0, element, MINI_TILESIZE);
12518 BlitBitmap(drawto, bitmap2, SX, SY, MINI_TILEX, MINI_TILEY, 0, 0);
12520 if (SDL_SaveBMP(bitmap2->surface, filename2) != 0)
12521 Fail("cannot save level sketch image file '%s'", filename2);
12526 // create corresponding SQL statements (for normal and small images)
12529 printf("insert into phpbb_words values (NULL, '`%03d', '<IMG class=\"levelsketch\" src=\"/I/%04d.png\"/>');\n", i, i);
12530 printf("insert into phpbb_words values (NULL, '¸%03d', '<IMG class=\"levelsketch\" src=\"/I/%04ds.png\"/>');\n", i, i);
12533 printf("insert into phpbb_words values (NULL, '`%04d', '<IMG class=\"levelsketch\" src=\"/I/%04d.png\"/>');\n", i, i);
12534 printf("insert into phpbb_words values (NULL, '¸%04d', '<IMG class=\"levelsketch\" src=\"/I/%04ds.png\"/>');\n", i, i);
12536 // optional: create content for forum level sketch demonstration post
12538 fprintf(stderr, "%03d `%03d%c", i, i, (i % 10 < 9 ? ' ' : '\n'));
12541 FreeBitmap(bitmap1);
12542 FreeBitmap(bitmap2);
12545 fprintf(stderr, "\n");
12547 Info("%d normal and small images created", NUM_FILE_ELEMENTS);
12549 CloseAllAndExit(0);
12553 // ----------------------------------------------------------------------------
12554 // create and save images for custom and group elements (raw BMP format)
12555 // ----------------------------------------------------------------------------
12557 void CreateCustomElementImages(char *directory)
12559 char *src_basename = "RocksCE-template.ilbm";
12560 char *dst_basename = "RocksCE.bmp";
12561 char *src_filename = getPath2(directory, src_basename);
12562 char *dst_filename = getPath2(directory, dst_basename);
12563 Bitmap *src_bitmap;
12565 int yoffset_ce = 0;
12566 int yoffset_ge = (TILEY * NUM_CUSTOM_ELEMENTS / 16);
12569 InitVideoDefaults();
12571 ReCreateBitmap(&backbuffer, video.width, video.height);
12573 src_bitmap = LoadImage(src_filename);
12575 bitmap = CreateBitmap(TILEX * 16 * 2,
12576 TILEY * (NUM_CUSTOM_ELEMENTS + NUM_GROUP_ELEMENTS) / 16,
12579 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
12586 BlitBitmap(src_bitmap, bitmap, 0, 0, TILEX, TILEY,
12587 TILEX * x, TILEY * y + yoffset_ce);
12589 BlitBitmap(src_bitmap, bitmap, 0, TILEY,
12591 TILEX * x + TILEX * 16,
12592 TILEY * y + yoffset_ce);
12594 for (j = 2; j >= 0; j--)
12598 BlitBitmap(src_bitmap, bitmap,
12599 TILEX + c * 7, 0, 6, 10,
12600 TILEX * x + 6 + j * 7,
12601 TILEY * y + 11 + yoffset_ce);
12603 BlitBitmap(src_bitmap, bitmap,
12604 TILEX + c * 8, TILEY, 6, 10,
12605 TILEX * 16 + TILEX * x + 6 + j * 8,
12606 TILEY * y + 10 + yoffset_ce);
12612 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
12619 BlitBitmap(src_bitmap, bitmap, 0, 0, TILEX, TILEY,
12620 TILEX * x, TILEY * y + yoffset_ge);
12622 BlitBitmap(src_bitmap, bitmap, 0, TILEY,
12624 TILEX * x + TILEX * 16,
12625 TILEY * y + yoffset_ge);
12627 for (j = 1; j >= 0; j--)
12631 BlitBitmap(src_bitmap, bitmap, TILEX + c * 10, 11, 10, 10,
12632 TILEX * x + 6 + j * 10,
12633 TILEY * y + 11 + yoffset_ge);
12635 BlitBitmap(src_bitmap, bitmap,
12636 TILEX + c * 8, TILEY + 12, 6, 10,
12637 TILEX * 16 + TILEX * x + 10 + j * 8,
12638 TILEY * y + 10 + yoffset_ge);
12644 if (SDL_SaveBMP(bitmap->surface, dst_filename) != 0)
12645 Fail("cannot save CE graphics file '%s'", dst_filename);
12647 FreeBitmap(bitmap);
12649 CloseAllAndExit(0);