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"
27 #define ENABLE_UNUSED_CODE 0 // currently unused functions
28 #define ENABLE_HISTORIC_CHUNKS 0 // only for historic reference
29 #define ENABLE_RESERVED_CODE 0 // reserved for later use
31 #define CHUNK_ID_LEN 4 // IFF style chunk id length
32 #define CHUNK_SIZE_UNDEFINED 0 // undefined chunk size == 0
33 #define CHUNK_SIZE_NONE -1 // do not write chunk size
35 #define LEVEL_CHUNK_NAME_SIZE MAX_LEVEL_NAME_LEN
36 #define LEVEL_CHUNK_AUTH_SIZE MAX_LEVEL_AUTHOR_LEN
38 #define LEVEL_CHUNK_VERS_SIZE 8 // size of file version chunk
39 #define LEVEL_CHUNK_DATE_SIZE 4 // size of file date chunk
40 #define LEVEL_CHUNK_HEAD_SIZE 80 // size of level file header
41 #define LEVEL_CHUNK_HEAD_UNUSED 0 // unused level header bytes
42 #define LEVEL_CHUNK_CNT2_SIZE 160 // size of level CNT2 chunk
43 #define LEVEL_CHUNK_CNT2_UNUSED 11 // unused CNT2 chunk bytes
44 #define LEVEL_CHUNK_CNT3_HEADER 16 // size of level CNT3 header
45 #define LEVEL_CHUNK_CNT3_UNUSED 10 // unused CNT3 chunk bytes
46 #define LEVEL_CPART_CUS3_SIZE 134 // size of CUS3 chunk part
47 #define LEVEL_CPART_CUS3_UNUSED 15 // unused CUS3 bytes / part
48 #define LEVEL_CHUNK_GRP1_SIZE 74 // size of level GRP1 chunk
50 // (element number, number of change pages, change page number)
51 #define LEVEL_CHUNK_CUSX_UNCHANGED (2 + (1 + 1) + (1 + 1))
53 // (element number only)
54 #define LEVEL_CHUNK_GRPX_UNCHANGED 2
55 #define LEVEL_CHUNK_EMPX_UNCHANGED 2
56 #define LEVEL_CHUNK_NOTE_UNCHANGED 2
58 // (nothing at all if unchanged)
59 #define LEVEL_CHUNK_ELEM_UNCHANGED 0
61 #define TAPE_CHUNK_VERS_SIZE 8 // size of file version chunk
62 #define TAPE_CHUNK_HEAD_SIZE 20 // size of tape file header
63 #define TAPE_CHUNK_SCRN_SIZE 2 // size of screen size chunk
65 #define SCORE_CHUNK_VERS_SIZE 8 // size of file version chunk
67 #define LEVEL_CHUNK_CNT3_SIZE(x) (LEVEL_CHUNK_CNT3_HEADER + (x))
68 #define LEVEL_CHUNK_CUS3_SIZE(x) (2 + (x) * LEVEL_CPART_CUS3_SIZE)
69 #define LEVEL_CHUNK_CUS4_SIZE(x) (96 + (x) * 48)
71 // file identifier strings
72 #define LEVEL_COOKIE_TMPL "ROCKSNDIAMONDS_LEVEL_FILE_VERSION_x.x"
73 #define TAPE_COOKIE_TMPL "ROCKSNDIAMONDS_TAPE_FILE_VERSION_x.x"
74 #define SCORE_COOKIE_TMPL "ROCKSNDIAMONDS_SCORE_FILE_VERSION_x.x"
76 // values for deciding when (not) to save configuration data
77 #define SAVE_CONF_NEVER 0
78 #define SAVE_CONF_ALWAYS 1
79 #define SAVE_CONF_WHEN_CHANGED -1
81 // values for chunks using micro chunks
82 #define CONF_MASK_1_BYTE 0x00
83 #define CONF_MASK_2_BYTE 0x40
84 #define CONF_MASK_4_BYTE 0x80
85 #define CONF_MASK_MULTI_BYTES 0xc0
87 #define CONF_MASK_BYTES 0xc0
88 #define CONF_MASK_TOKEN 0x3f
90 #define CONF_VALUE_1_BYTE(x) (CONF_MASK_1_BYTE | (x))
91 #define CONF_VALUE_2_BYTE(x) (CONF_MASK_2_BYTE | (x))
92 #define CONF_VALUE_4_BYTE(x) (CONF_MASK_4_BYTE | (x))
93 #define CONF_VALUE_MULTI_BYTES(x) (CONF_MASK_MULTI_BYTES | (x))
95 // these definitions are just for convenience of use and readability
96 #define CONF_VALUE_8_BIT(x) CONF_VALUE_1_BYTE(x)
97 #define CONF_VALUE_16_BIT(x) CONF_VALUE_2_BYTE(x)
98 #define CONF_VALUE_32_BIT(x) CONF_VALUE_4_BYTE(x)
99 #define CONF_VALUE_BYTES(x) CONF_VALUE_MULTI_BYTES(x)
101 #define CONF_VALUE_NUM_BYTES(x) ((x) == CONF_MASK_1_BYTE ? 1 : \
102 (x) == CONF_MASK_2_BYTE ? 2 : \
103 (x) == CONF_MASK_4_BYTE ? 4 : 0)
105 #define CONF_CONTENT_NUM_ELEMENTS (3 * 3)
106 #define CONF_CONTENT_NUM_BYTES (CONF_CONTENT_NUM_ELEMENTS * 2)
107 #define CONF_ELEMENT_NUM_BYTES (2)
109 #define CONF_ENTITY_NUM_BYTES(t) ((t) == TYPE_ELEMENT || \
110 (t) == TYPE_ELEMENT_LIST ? \
111 CONF_ELEMENT_NUM_BYTES : \
112 (t) == TYPE_CONTENT || \
113 (t) == TYPE_CONTENT_LIST ? \
114 CONF_CONTENT_NUM_BYTES : 1)
116 #define CONF_ELEMENT_BYTE_POS(i) ((i) * CONF_ELEMENT_NUM_BYTES)
117 #define CONF_ELEMENTS_ELEMENT(b,i) ((b[CONF_ELEMENT_BYTE_POS(i)] << 8) | \
118 (b[CONF_ELEMENT_BYTE_POS(i) + 1]))
120 #define CONF_CONTENT_ELEMENT_POS(c,x,y) ((c) * CONF_CONTENT_NUM_ELEMENTS + \
122 #define CONF_CONTENT_BYTE_POS(c,x,y) (CONF_CONTENT_ELEMENT_POS(c,x,y) * \
123 CONF_ELEMENT_NUM_BYTES)
124 #define CONF_CONTENTS_ELEMENT(b,c,x,y) ((b[CONF_CONTENT_BYTE_POS(c,x,y)]<< 8)|\
125 (b[CONF_CONTENT_BYTE_POS(c,x,y) + 1]))
127 // temporary variables used to store pointers to structure members
128 static struct LevelInfo li;
129 static struct ElementInfo xx_ei, yy_ei;
130 static struct ElementChangeInfo xx_change;
131 static struct ElementGroupInfo xx_group;
132 static struct EnvelopeInfo xx_envelope;
133 static unsigned int xx_event_bits[NUM_CE_BITFIELDS];
134 static char xx_default_description[MAX_ELEMENT_NAME_LEN + 1];
135 static int xx_num_contents;
136 static int xx_current_change_page;
137 static char xx_default_string_empty[1] = "";
138 static int xx_string_length_unused;
140 struct LevelFileConfigInfo
142 int element; // element for which data is to be stored
143 int save_type; // save data always, never or when changed
144 int data_type; // data type (used internally, not stored)
145 int conf_type; // micro chunk identifier (stored in file)
148 void *value; // variable that holds the data to be stored
149 int default_value; // initial default value for this variable
152 void *value_copy; // variable that holds the data to be copied
153 void *num_entities; // number of entities for multi-byte data
154 int default_num_entities; // default number of entities for this data
155 int max_num_entities; // maximal number of entities for this data
156 char *default_string; // optional default string for string data
159 static struct LevelFileConfigInfo chunk_config_INFO[] =
161 // ---------- values not related to single elements -------------------------
164 -1, SAVE_CONF_ALWAYS,
165 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
166 &li.game_engine_type, GAME_ENGINE_TYPE_RND
170 -1, SAVE_CONF_ALWAYS,
171 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
172 &li.fieldx, STD_LEV_FIELDX
175 -1, SAVE_CONF_ALWAYS,
176 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
177 &li.fieldy, STD_LEV_FIELDY
181 -1, SAVE_CONF_ALWAYS,
182 TYPE_INTEGER, CONF_VALUE_16_BIT(3),
187 -1, SAVE_CONF_ALWAYS,
188 TYPE_INTEGER, CONF_VALUE_16_BIT(4),
194 TYPE_INTEGER, CONF_VALUE_32_BIT(2),
200 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
201 &li.use_step_counter, FALSE
206 TYPE_BITFIELD, CONF_VALUE_8_BIT(4),
207 &li.wind_direction_initial, MV_NONE
212 TYPE_BOOLEAN, CONF_VALUE_8_BIT(5),
213 &li.em_slippery_gems, FALSE
218 TYPE_BOOLEAN, CONF_VALUE_8_BIT(6),
219 &li.use_custom_template, FALSE
224 TYPE_BITFIELD, CONF_VALUE_32_BIT(1),
225 &li.can_move_into_acid_bits, ~0 // default: everything can
230 TYPE_BITFIELD, CONF_VALUE_8_BIT(7),
231 &li.dont_collide_with_bits, ~0 // default: always deadly
236 TYPE_BOOLEAN, CONF_VALUE_8_BIT(8),
237 &li.em_explodes_by_fire, FALSE
242 TYPE_INTEGER, CONF_VALUE_16_BIT(5),
243 &li.score[SC_TIME_BONUS], 1
248 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
249 &li.auto_exit_sokoban, FALSE
254 TYPE_BOOLEAN, CONF_VALUE_8_BIT(10),
255 &li.auto_count_gems, FALSE
260 TYPE_BOOLEAN, CONF_VALUE_8_BIT(11),
261 &li.solved_by_one_player, FALSE
266 TYPE_INTEGER, CONF_VALUE_8_BIT(12),
267 &li.time_score_base, 1
272 TYPE_BOOLEAN, CONF_VALUE_8_BIT(13),
273 &li.rate_time_over_score, FALSE
283 static struct LevelFileConfigInfo chunk_config_ELEM[] =
285 // (these values are the same for each player)
288 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
289 &li.block_last_field, FALSE // default case for EM levels
293 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
294 &li.sp_block_last_field, TRUE // default case for SP levels
298 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
299 &li.instant_relocation, FALSE
303 TYPE_BOOLEAN, CONF_VALUE_8_BIT(4),
304 &li.can_pass_to_walkable, FALSE
308 TYPE_BOOLEAN, CONF_VALUE_8_BIT(5),
309 &li.block_snap_field, TRUE
313 TYPE_BOOLEAN, CONF_VALUE_8_BIT(6),
314 &li.continuous_snapping, TRUE
318 TYPE_BOOLEAN, CONF_VALUE_8_BIT(12),
319 &li.shifted_relocation, FALSE
323 TYPE_BOOLEAN, CONF_VALUE_8_BIT(15),
324 &li.lazy_relocation, FALSE
328 TYPE_BOOLEAN, CONF_VALUE_8_BIT(16),
329 &li.finish_dig_collect, TRUE
333 TYPE_BOOLEAN, CONF_VALUE_8_BIT(17),
334 &li.keep_walkable_ce, FALSE
337 // (these values are different for each player)
340 TYPE_INTEGER, CONF_VALUE_8_BIT(7),
341 &li.initial_player_stepsize[0], STEPSIZE_NORMAL
345 TYPE_BOOLEAN, CONF_VALUE_8_BIT(8),
346 &li.initial_player_gravity[0], FALSE
350 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
351 &li.use_start_element[0], FALSE
355 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
356 &li.start_element[0], EL_PLAYER_1
360 TYPE_BOOLEAN, CONF_VALUE_8_BIT(10),
361 &li.use_artwork_element[0], FALSE
365 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
366 &li.artwork_element[0], EL_PLAYER_1
370 TYPE_BOOLEAN, CONF_VALUE_8_BIT(11),
371 &li.use_explosion_element[0], FALSE
375 TYPE_ELEMENT, CONF_VALUE_16_BIT(3),
376 &li.explosion_element[0], EL_PLAYER_1
380 TYPE_BOOLEAN, CONF_VALUE_8_BIT(13),
381 &li.use_initial_inventory[0], FALSE
385 TYPE_BOOLEAN, CONF_VALUE_8_BIT(14),
386 &li.initial_inventory_size[0], 1
390 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(1),
391 &li.initial_inventory_content[0][0],EL_EMPTY, NULL,
392 &li.initial_inventory_size[0], 1, MAX_INITIAL_INVENTORY_SIZE
397 TYPE_INTEGER, CONF_VALUE_8_BIT(7),
398 &li.initial_player_stepsize[1], STEPSIZE_NORMAL
402 TYPE_BOOLEAN, CONF_VALUE_8_BIT(8),
403 &li.initial_player_gravity[1], FALSE
407 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
408 &li.use_start_element[1], FALSE
412 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
413 &li.start_element[1], EL_PLAYER_2
417 TYPE_BOOLEAN, CONF_VALUE_8_BIT(10),
418 &li.use_artwork_element[1], FALSE
422 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
423 &li.artwork_element[1], EL_PLAYER_2
427 TYPE_BOOLEAN, CONF_VALUE_8_BIT(11),
428 &li.use_explosion_element[1], FALSE
432 TYPE_ELEMENT, CONF_VALUE_16_BIT(3),
433 &li.explosion_element[1], EL_PLAYER_2
437 TYPE_BOOLEAN, CONF_VALUE_8_BIT(13),
438 &li.use_initial_inventory[1], FALSE
442 TYPE_BOOLEAN, CONF_VALUE_8_BIT(14),
443 &li.initial_inventory_size[1], 1
447 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(1),
448 &li.initial_inventory_content[1][0],EL_EMPTY, NULL,
449 &li.initial_inventory_size[1], 1, MAX_INITIAL_INVENTORY_SIZE
454 TYPE_INTEGER, CONF_VALUE_8_BIT(7),
455 &li.initial_player_stepsize[2], STEPSIZE_NORMAL
459 TYPE_BOOLEAN, CONF_VALUE_8_BIT(8),
460 &li.initial_player_gravity[2], FALSE
464 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
465 &li.use_start_element[2], FALSE
469 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
470 &li.start_element[2], EL_PLAYER_3
474 TYPE_BOOLEAN, CONF_VALUE_8_BIT(10),
475 &li.use_artwork_element[2], FALSE
479 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
480 &li.artwork_element[2], EL_PLAYER_3
484 TYPE_BOOLEAN, CONF_VALUE_8_BIT(11),
485 &li.use_explosion_element[2], FALSE
489 TYPE_ELEMENT, CONF_VALUE_16_BIT(3),
490 &li.explosion_element[2], EL_PLAYER_3
494 TYPE_BOOLEAN, CONF_VALUE_8_BIT(13),
495 &li.use_initial_inventory[2], FALSE
499 TYPE_BOOLEAN, CONF_VALUE_8_BIT(14),
500 &li.initial_inventory_size[2], 1
504 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(1),
505 &li.initial_inventory_content[2][0],EL_EMPTY, NULL,
506 &li.initial_inventory_size[2], 1, MAX_INITIAL_INVENTORY_SIZE
511 TYPE_INTEGER, CONF_VALUE_8_BIT(7),
512 &li.initial_player_stepsize[3], STEPSIZE_NORMAL
516 TYPE_BOOLEAN, CONF_VALUE_8_BIT(8),
517 &li.initial_player_gravity[3], FALSE
521 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
522 &li.use_start_element[3], FALSE
526 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
527 &li.start_element[3], EL_PLAYER_4
531 TYPE_BOOLEAN, CONF_VALUE_8_BIT(10),
532 &li.use_artwork_element[3], FALSE
536 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
537 &li.artwork_element[3], EL_PLAYER_4
541 TYPE_BOOLEAN, CONF_VALUE_8_BIT(11),
542 &li.use_explosion_element[3], FALSE
546 TYPE_ELEMENT, CONF_VALUE_16_BIT(3),
547 &li.explosion_element[3], EL_PLAYER_4
551 TYPE_BOOLEAN, CONF_VALUE_8_BIT(13),
552 &li.use_initial_inventory[3], FALSE
556 TYPE_BOOLEAN, CONF_VALUE_8_BIT(14),
557 &li.initial_inventory_size[3], 1
561 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(1),
562 &li.initial_inventory_content[3][0],EL_EMPTY, NULL,
563 &li.initial_inventory_size[3], 1, MAX_INITIAL_INVENTORY_SIZE
568 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
569 &li.score[SC_EMERALD], 10
574 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
575 &li.score[SC_DIAMOND], 10
580 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
581 &li.score[SC_BUG], 10
586 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
587 &li.score[SC_SPACESHIP], 10
592 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
593 &li.score[SC_PACMAN], 10
598 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
599 &li.score[SC_NUT], 10
604 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
605 &li.score[SC_DYNAMITE], 10
610 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
611 &li.score[SC_KEY], 10
616 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
617 &li.score[SC_PEARL], 10
622 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
623 &li.score[SC_CRYSTAL], 10
628 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
629 &li.amoeba_content, EL_DIAMOND
633 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
638 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
639 &li.grow_into_diggable, TRUE
644 TYPE_CONTENT_LIST, CONF_VALUE_BYTES(1),
645 &li.yamyam_content, EL_ROCK, NULL,
646 &li.num_yamyam_contents, 4, MAX_ELEMENT_CONTENTS
650 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
651 &li.score[SC_YAMYAM], 10
656 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
657 &li.score[SC_ROBOT], 10
661 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
667 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
673 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
674 &li.time_magic_wall, 10
679 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
680 &li.game_of_life[0], 2
684 TYPE_INTEGER, CONF_VALUE_8_BIT(2),
685 &li.game_of_life[1], 3
689 TYPE_INTEGER, CONF_VALUE_8_BIT(3),
690 &li.game_of_life[2], 3
694 TYPE_INTEGER, CONF_VALUE_8_BIT(4),
695 &li.game_of_life[3], 3
699 TYPE_BOOLEAN, CONF_VALUE_8_BIT(5),
700 &li.use_life_bugs, FALSE
705 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
710 TYPE_INTEGER, CONF_VALUE_8_BIT(2),
715 TYPE_INTEGER, CONF_VALUE_8_BIT(3),
720 TYPE_INTEGER, CONF_VALUE_8_BIT(4),
725 EL_TIMEGATE_SWITCH, -1,
726 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
727 &li.time_timegate, 10
731 EL_LIGHT_SWITCH_ACTIVE, -1,
732 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
737 EL_SHIELD_NORMAL, -1,
738 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
739 &li.shield_normal_time, 10
742 EL_SHIELD_NORMAL, -1,
743 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
744 &li.score[SC_SHIELD], 10
748 EL_SHIELD_DEADLY, -1,
749 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
750 &li.shield_deadly_time, 10
753 EL_SHIELD_DEADLY, -1,
754 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
755 &li.score[SC_SHIELD], 10
760 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
765 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
766 &li.extra_time_score, 10
770 EL_TIME_ORB_FULL, -1,
771 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
772 &li.time_orb_time, 10
775 EL_TIME_ORB_FULL, -1,
776 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
777 &li.use_time_orb_bug, FALSE
782 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
783 &li.use_spring_bug, FALSE
788 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
789 &li.android_move_time, 10
793 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
794 &li.android_clone_time, 10
797 EL_EMC_ANDROID, SAVE_CONF_NEVER,
798 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(1),
799 &li.android_clone_element[0], EL_EMPTY, NULL,
800 &li.num_android_clone_elements, 1, MAX_ANDROID_ELEMENTS_OLD
804 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(2),
805 &li.android_clone_element[0], EL_EMPTY, NULL,
806 &li.num_android_clone_elements, 1, MAX_ANDROID_ELEMENTS
811 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
816 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
821 EL_EMC_MAGNIFIER, -1,
822 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
823 &li.magnify_score, 10
826 EL_EMC_MAGNIFIER, -1,
827 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
832 EL_EMC_MAGIC_BALL, -1,
833 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
837 EL_EMC_MAGIC_BALL, -1,
838 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
839 &li.ball_random, FALSE
842 EL_EMC_MAGIC_BALL, -1,
843 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
844 &li.ball_active_initial, FALSE
847 EL_EMC_MAGIC_BALL, -1,
848 TYPE_CONTENT_LIST, CONF_VALUE_BYTES(1),
849 &li.ball_content, EL_EMPTY, NULL,
850 &li.num_ball_contents, 4, MAX_ELEMENT_CONTENTS
854 EL_SOKOBAN_FIELD_EMPTY, -1,
855 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
856 &li.sb_fields_needed, TRUE
860 EL_SOKOBAN_OBJECT, -1,
861 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
862 &li.sb_objects_needed, TRUE
867 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
868 &li.mm_laser_red, FALSE
872 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
873 &li.mm_laser_green, FALSE
877 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
878 &li.mm_laser_blue, TRUE
883 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
884 &li.df_laser_red, TRUE
888 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
889 &li.df_laser_green, TRUE
893 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
894 &li.df_laser_blue, FALSE
898 EL_MM_FUSE_ACTIVE, -1,
899 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
904 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
910 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
915 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
916 &li.mm_ball_choice_mode, ANIM_RANDOM
920 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(1),
921 &li.mm_ball_content, EL_EMPTY, NULL,
922 &li.num_mm_ball_contents, 8, MAX_MM_BALL_CONTENTS
926 EL_MM_STEEL_BLOCK, -1,
927 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
928 &li.mm_time_block, 75
932 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
933 &li.score[SC_ELEM_BONUS], 10
936 // ---------- unused values -------------------------------------------------
939 EL_UNKNOWN, SAVE_CONF_NEVER,
940 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
941 &li.score[SC_UNKNOWN_15], 10
951 static struct LevelFileConfigInfo chunk_config_NOTE[] =
955 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
956 &xx_envelope.xsize, MAX_ENVELOPE_XSIZE,
960 TYPE_INTEGER, CONF_VALUE_8_BIT(2),
961 &xx_envelope.ysize, MAX_ENVELOPE_YSIZE,
966 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
967 &xx_envelope.autowrap, FALSE
971 TYPE_BOOLEAN, CONF_VALUE_8_BIT(4),
972 &xx_envelope.centered, FALSE
977 TYPE_STRING, CONF_VALUE_BYTES(1),
978 &xx_envelope.text, -1, NULL,
979 &xx_string_length_unused, -1, MAX_ENVELOPE_TEXT_LEN,
980 &xx_default_string_empty[0]
990 static struct LevelFileConfigInfo chunk_config_CUSX_base[] =
994 TYPE_STRING, CONF_VALUE_BYTES(1),
995 &xx_ei.description[0], -1,
996 &yy_ei.description[0],
997 &xx_string_length_unused, -1, MAX_ELEMENT_NAME_LEN,
998 &xx_default_description[0]
1003 TYPE_BITFIELD, CONF_VALUE_32_BIT(1),
1004 &xx_ei.properties[EP_BITFIELD_BASE_NR], EP_BITMASK_BASE_DEFAULT,
1005 &yy_ei.properties[EP_BITFIELD_BASE_NR]
1007 #if ENABLE_RESERVED_CODE
1008 // (reserved for later use)
1011 TYPE_BITFIELD, CONF_VALUE_32_BIT(2),
1012 &xx_ei.properties[EP_BITFIELD_BASE_NR + 1], EP_BITMASK_DEFAULT,
1013 &yy_ei.properties[EP_BITFIELD_BASE_NR + 1]
1019 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1020 &xx_ei.use_gfx_element, FALSE,
1021 &yy_ei.use_gfx_element
1025 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
1026 &xx_ei.gfx_element_initial, EL_EMPTY_SPACE,
1027 &yy_ei.gfx_element_initial
1032 TYPE_BITFIELD, CONF_VALUE_8_BIT(2),
1033 &xx_ei.access_direction, MV_ALL_DIRECTIONS,
1034 &yy_ei.access_direction
1039 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
1040 &xx_ei.collect_score_initial, 10,
1041 &yy_ei.collect_score_initial
1045 TYPE_INTEGER, CONF_VALUE_16_BIT(3),
1046 &xx_ei.collect_count_initial, 1,
1047 &yy_ei.collect_count_initial
1052 TYPE_INTEGER, CONF_VALUE_16_BIT(4),
1053 &xx_ei.ce_value_fixed_initial, 0,
1054 &yy_ei.ce_value_fixed_initial
1058 TYPE_INTEGER, CONF_VALUE_16_BIT(5),
1059 &xx_ei.ce_value_random_initial, 0,
1060 &yy_ei.ce_value_random_initial
1064 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
1065 &xx_ei.use_last_ce_value, FALSE,
1066 &yy_ei.use_last_ce_value
1071 TYPE_INTEGER, CONF_VALUE_16_BIT(6),
1072 &xx_ei.push_delay_fixed, 8,
1073 &yy_ei.push_delay_fixed
1077 TYPE_INTEGER, CONF_VALUE_16_BIT(7),
1078 &xx_ei.push_delay_random, 8,
1079 &yy_ei.push_delay_random
1083 TYPE_INTEGER, CONF_VALUE_16_BIT(8),
1084 &xx_ei.drop_delay_fixed, 0,
1085 &yy_ei.drop_delay_fixed
1089 TYPE_INTEGER, CONF_VALUE_16_BIT(9),
1090 &xx_ei.drop_delay_random, 0,
1091 &yy_ei.drop_delay_random
1095 TYPE_INTEGER, CONF_VALUE_16_BIT(10),
1096 &xx_ei.move_delay_fixed, 0,
1097 &yy_ei.move_delay_fixed
1101 TYPE_INTEGER, CONF_VALUE_16_BIT(11),
1102 &xx_ei.move_delay_random, 0,
1103 &yy_ei.move_delay_random
1107 TYPE_INTEGER, CONF_VALUE_16_BIT(16),
1108 &xx_ei.step_delay_fixed, 0,
1109 &yy_ei.step_delay_fixed
1113 TYPE_INTEGER, CONF_VALUE_16_BIT(17),
1114 &xx_ei.step_delay_random, 0,
1115 &yy_ei.step_delay_random
1120 TYPE_BITFIELD, CONF_VALUE_32_BIT(3),
1121 &xx_ei.move_pattern, MV_ALL_DIRECTIONS,
1126 TYPE_BITFIELD, CONF_VALUE_8_BIT(4),
1127 &xx_ei.move_direction_initial, MV_START_AUTOMATIC,
1128 &yy_ei.move_direction_initial
1132 TYPE_INTEGER, CONF_VALUE_8_BIT(5),
1133 &xx_ei.move_stepsize, TILEX / 8,
1134 &yy_ei.move_stepsize
1139 TYPE_ELEMENT, CONF_VALUE_16_BIT(12),
1140 &xx_ei.move_enter_element, EL_EMPTY_SPACE,
1141 &yy_ei.move_enter_element
1145 TYPE_ELEMENT, CONF_VALUE_16_BIT(13),
1146 &xx_ei.move_leave_element, EL_EMPTY_SPACE,
1147 &yy_ei.move_leave_element
1151 TYPE_INTEGER, CONF_VALUE_8_BIT(6),
1152 &xx_ei.move_leave_type, LEAVE_TYPE_UNLIMITED,
1153 &yy_ei.move_leave_type
1158 TYPE_INTEGER, CONF_VALUE_8_BIT(7),
1159 &xx_ei.slippery_type, SLIPPERY_ANY_RANDOM,
1160 &yy_ei.slippery_type
1165 TYPE_INTEGER, CONF_VALUE_8_BIT(8),
1166 &xx_ei.explosion_type, EXPLODES_3X3,
1167 &yy_ei.explosion_type
1171 TYPE_INTEGER, CONF_VALUE_16_BIT(14),
1172 &xx_ei.explosion_delay, 16,
1173 &yy_ei.explosion_delay
1177 TYPE_INTEGER, CONF_VALUE_16_BIT(15),
1178 &xx_ei.ignition_delay, 8,
1179 &yy_ei.ignition_delay
1184 TYPE_CONTENT_LIST, CONF_VALUE_BYTES(2),
1185 &xx_ei.content, EL_EMPTY_SPACE,
1187 &xx_num_contents, 1, 1
1190 // ---------- "num_change_pages" must be the last entry ---------------------
1193 -1, SAVE_CONF_ALWAYS,
1194 TYPE_INTEGER, CONF_VALUE_8_BIT(9),
1195 &xx_ei.num_change_pages, 1,
1196 &yy_ei.num_change_pages
1207 static struct LevelFileConfigInfo chunk_config_CUSX_change[] =
1209 // ---------- "current_change_page" must be the first entry -----------------
1212 -1, SAVE_CONF_ALWAYS,
1213 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
1214 &xx_current_change_page, -1
1217 // ---------- (the remaining entries can be in any order) -------------------
1221 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
1222 &xx_change.can_change, FALSE
1227 TYPE_BITFIELD, CONF_VALUE_32_BIT(1),
1228 &xx_event_bits[0], 0
1232 TYPE_BITFIELD, CONF_VALUE_32_BIT(2),
1233 &xx_event_bits[1], 0
1238 TYPE_BITFIELD, CONF_VALUE_8_BIT(3),
1239 &xx_change.trigger_player, CH_PLAYER_ANY
1243 TYPE_BITFIELD, CONF_VALUE_8_BIT(4),
1244 &xx_change.trigger_side, CH_SIDE_ANY
1248 TYPE_BITFIELD, CONF_VALUE_32_BIT(3),
1249 &xx_change.trigger_page, CH_PAGE_ANY
1254 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
1255 &xx_change.target_element, EL_EMPTY_SPACE
1260 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
1261 &xx_change.delay_fixed, 0
1265 TYPE_INTEGER, CONF_VALUE_16_BIT(3),
1266 &xx_change.delay_random, 0
1270 TYPE_INTEGER, CONF_VALUE_16_BIT(4),
1271 &xx_change.delay_frames, FRAMES_PER_SECOND
1276 TYPE_ELEMENT, CONF_VALUE_16_BIT(5),
1277 &xx_change.initial_trigger_element, EL_EMPTY_SPACE
1282 TYPE_BOOLEAN, CONF_VALUE_8_BIT(6),
1283 &xx_change.explode, FALSE
1287 TYPE_BOOLEAN, CONF_VALUE_8_BIT(7),
1288 &xx_change.use_target_content, FALSE
1292 TYPE_BOOLEAN, CONF_VALUE_8_BIT(8),
1293 &xx_change.only_if_complete, FALSE
1297 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
1298 &xx_change.use_random_replace, FALSE
1302 TYPE_INTEGER, CONF_VALUE_8_BIT(10),
1303 &xx_change.random_percentage, 100
1307 TYPE_INTEGER, CONF_VALUE_8_BIT(11),
1308 &xx_change.replace_when, CP_WHEN_EMPTY
1313 TYPE_BOOLEAN, CONF_VALUE_8_BIT(12),
1314 &xx_change.has_action, FALSE
1318 TYPE_INTEGER, CONF_VALUE_8_BIT(13),
1319 &xx_change.action_type, CA_NO_ACTION
1323 TYPE_INTEGER, CONF_VALUE_8_BIT(14),
1324 &xx_change.action_mode, CA_MODE_UNDEFINED
1328 TYPE_INTEGER, CONF_VALUE_16_BIT(6),
1329 &xx_change.action_arg, CA_ARG_UNDEFINED
1334 TYPE_ELEMENT, CONF_VALUE_16_BIT(7),
1335 &xx_change.action_element, EL_EMPTY_SPACE
1340 TYPE_CONTENT_LIST, CONF_VALUE_BYTES(1),
1341 &xx_change.target_content, EL_EMPTY_SPACE, NULL,
1342 &xx_num_contents, 1, 1
1352 static struct LevelFileConfigInfo chunk_config_GRPX[] =
1356 TYPE_STRING, CONF_VALUE_BYTES(1),
1357 &xx_ei.description[0], -1, NULL,
1358 &xx_string_length_unused, -1, MAX_ELEMENT_NAME_LEN,
1359 &xx_default_description[0]
1364 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1365 &xx_ei.use_gfx_element, FALSE
1369 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
1370 &xx_ei.gfx_element_initial, EL_EMPTY_SPACE
1375 TYPE_INTEGER, CONF_VALUE_8_BIT(2),
1376 &xx_group.choice_mode, ANIM_RANDOM
1381 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(2),
1382 &xx_group.element[0], EL_EMPTY_SPACE, NULL,
1383 &xx_group.num_elements, 1, MAX_ELEMENTS_IN_GROUP
1393 static struct LevelFileConfigInfo chunk_config_EMPX[] =
1397 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1398 &xx_ei.use_gfx_element, FALSE
1402 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
1403 &xx_ei.gfx_element_initial, EL_EMPTY_SPACE
1413 static struct LevelFileConfigInfo chunk_config_CONF[] = // (OBSOLETE)
1417 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
1418 &li.block_snap_field, TRUE
1422 TYPE_BOOLEAN, CONF_VALUE_8_BIT(13),
1423 &li.continuous_snapping, TRUE
1427 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
1428 &li.initial_player_stepsize[0], STEPSIZE_NORMAL
1432 TYPE_BOOLEAN, CONF_VALUE_8_BIT(10),
1433 &li.use_start_element[0], FALSE
1437 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
1438 &li.start_element[0], EL_PLAYER_1
1442 TYPE_BOOLEAN, CONF_VALUE_8_BIT(11),
1443 &li.use_artwork_element[0], FALSE
1447 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
1448 &li.artwork_element[0], EL_PLAYER_1
1452 TYPE_BOOLEAN, CONF_VALUE_8_BIT(12),
1453 &li.use_explosion_element[0], FALSE
1457 TYPE_ELEMENT, CONF_VALUE_16_BIT(3),
1458 &li.explosion_element[0], EL_PLAYER_1
1473 filetype_id_list[] =
1475 { LEVEL_FILE_TYPE_RND, "RND" },
1476 { LEVEL_FILE_TYPE_BD, "BD" },
1477 { LEVEL_FILE_TYPE_EM, "EM" },
1478 { LEVEL_FILE_TYPE_SP, "SP" },
1479 { LEVEL_FILE_TYPE_DX, "DX" },
1480 { LEVEL_FILE_TYPE_SB, "SB" },
1481 { LEVEL_FILE_TYPE_DC, "DC" },
1482 { LEVEL_FILE_TYPE_MM, "MM" },
1483 { LEVEL_FILE_TYPE_MM, "DF" },
1488 // ============================================================================
1489 // level file functions
1490 // ============================================================================
1492 static boolean check_special_flags(char *flag)
1494 if (strEqual(options.special_flags, flag) ||
1495 strEqual(leveldir_current->special_flags, flag))
1501 static struct DateInfo getCurrentDate(void)
1503 time_t epoch_seconds = time(NULL);
1504 struct tm *now = localtime(&epoch_seconds);
1505 struct DateInfo date;
1507 date.year = now->tm_year + 1900;
1508 date.month = now->tm_mon + 1;
1509 date.day = now->tm_mday;
1511 date.src = DATE_SRC_CLOCK;
1516 static void resetEventFlags(struct ElementChangeInfo *change)
1520 for (i = 0; i < NUM_CHANGE_EVENTS; i++)
1521 change->has_event[i] = FALSE;
1524 static void resetEventBits(void)
1528 for (i = 0; i < NUM_CE_BITFIELDS; i++)
1529 xx_event_bits[i] = 0;
1532 static void setEventFlagsFromEventBits(struct ElementChangeInfo *change)
1536 /* important: only change event flag if corresponding event bit is set
1537 (this is because all xx_event_bits[] values are loaded separately,
1538 and all xx_event_bits[] values are set back to zero before loading
1539 another value xx_event_bits[x] (each value representing 32 flags)) */
1541 for (i = 0; i < NUM_CHANGE_EVENTS; i++)
1542 if (xx_event_bits[CH_EVENT_BITFIELD_NR(i)] & CH_EVENT_BIT(i))
1543 change->has_event[i] = TRUE;
1546 static void setEventBitsFromEventFlags(struct ElementChangeInfo *change)
1550 /* in contrast to the above function setEventFlagsFromEventBits(), it
1551 would also be possible to set all bits in xx_event_bits[] to 0 or 1
1552 depending on the corresponding change->has_event[i] values here, as
1553 all xx_event_bits[] values are reset in resetEventBits() before */
1555 for (i = 0; i < NUM_CHANGE_EVENTS; i++)
1556 if (change->has_event[i])
1557 xx_event_bits[CH_EVENT_BITFIELD_NR(i)] |= CH_EVENT_BIT(i);
1560 static char *getDefaultElementDescription(struct ElementInfo *ei)
1562 static char description[MAX_ELEMENT_NAME_LEN + 1];
1563 char *default_description = (ei->custom_description != NULL ?
1564 ei->custom_description :
1565 ei->editor_description);
1568 // always start with reliable default values
1569 for (i = 0; i < MAX_ELEMENT_NAME_LEN + 1; i++)
1570 description[i] = '\0';
1572 // truncate element description to MAX_ELEMENT_NAME_LEN bytes
1573 strncpy(description, default_description, MAX_ELEMENT_NAME_LEN);
1575 return &description[0];
1578 static void setElementDescriptionToDefault(struct ElementInfo *ei)
1580 char *default_description = getDefaultElementDescription(ei);
1583 for (i = 0; i < MAX_ELEMENT_NAME_LEN + 1; i++)
1584 ei->description[i] = default_description[i];
1587 static void setConfigToDefaultsFromConfigList(struct LevelFileConfigInfo *conf)
1591 for (i = 0; conf[i].data_type != -1; i++)
1593 int default_value = conf[i].default_value;
1594 int data_type = conf[i].data_type;
1595 int conf_type = conf[i].conf_type;
1596 int byte_mask = conf_type & CONF_MASK_BYTES;
1598 if (byte_mask == CONF_MASK_MULTI_BYTES)
1600 int default_num_entities = conf[i].default_num_entities;
1601 int max_num_entities = conf[i].max_num_entities;
1603 *(int *)(conf[i].num_entities) = default_num_entities;
1605 if (data_type == TYPE_STRING)
1607 char *default_string = conf[i].default_string;
1608 char *string = (char *)(conf[i].value);
1610 strncpy(string, default_string, max_num_entities);
1612 else if (data_type == TYPE_ELEMENT_LIST)
1614 int *element_array = (int *)(conf[i].value);
1617 for (j = 0; j < max_num_entities; j++)
1618 element_array[j] = default_value;
1620 else if (data_type == TYPE_CONTENT_LIST)
1622 struct Content *content = (struct Content *)(conf[i].value);
1625 for (c = 0; c < max_num_entities; c++)
1626 for (y = 0; y < 3; y++)
1627 for (x = 0; x < 3; x++)
1628 content[c].e[x][y] = default_value;
1631 else // constant size configuration data (1, 2 or 4 bytes)
1633 if (data_type == TYPE_BOOLEAN)
1634 *(boolean *)(conf[i].value) = default_value;
1636 *(int *) (conf[i].value) = default_value;
1641 static void copyConfigFromConfigList(struct LevelFileConfigInfo *conf)
1645 for (i = 0; conf[i].data_type != -1; i++)
1647 int data_type = conf[i].data_type;
1648 int conf_type = conf[i].conf_type;
1649 int byte_mask = conf_type & CONF_MASK_BYTES;
1651 if (byte_mask == CONF_MASK_MULTI_BYTES)
1653 int max_num_entities = conf[i].max_num_entities;
1655 if (data_type == TYPE_STRING)
1657 char *string = (char *)(conf[i].value);
1658 char *string_copy = (char *)(conf[i].value_copy);
1660 strncpy(string_copy, string, max_num_entities);
1662 else if (data_type == TYPE_ELEMENT_LIST)
1664 int *element_array = (int *)(conf[i].value);
1665 int *element_array_copy = (int *)(conf[i].value_copy);
1668 for (j = 0; j < max_num_entities; j++)
1669 element_array_copy[j] = element_array[j];
1671 else if (data_type == TYPE_CONTENT_LIST)
1673 struct Content *content = (struct Content *)(conf[i].value);
1674 struct Content *content_copy = (struct Content *)(conf[i].value_copy);
1677 for (c = 0; c < max_num_entities; c++)
1678 for (y = 0; y < 3; y++)
1679 for (x = 0; x < 3; x++)
1680 content_copy[c].e[x][y] = content[c].e[x][y];
1683 else // constant size configuration data (1, 2 or 4 bytes)
1685 if (data_type == TYPE_BOOLEAN)
1686 *(boolean *)(conf[i].value_copy) = *(boolean *)(conf[i].value);
1688 *(int *) (conf[i].value_copy) = *(int *) (conf[i].value);
1693 void copyElementInfo(struct ElementInfo *ei_from, struct ElementInfo *ei_to)
1697 xx_ei = *ei_from; // copy element data into temporary buffer
1698 yy_ei = *ei_to; // copy element data into temporary buffer
1700 copyConfigFromConfigList(chunk_config_CUSX_base);
1705 // ---------- reinitialize and copy change pages ----------
1707 ei_to->num_change_pages = ei_from->num_change_pages;
1708 ei_to->current_change_page = ei_from->current_change_page;
1710 setElementChangePages(ei_to, ei_to->num_change_pages);
1712 for (i = 0; i < ei_to->num_change_pages; i++)
1713 ei_to->change_page[i] = ei_from->change_page[i];
1715 // ---------- copy group element info ----------
1716 if (ei_from->group != NULL && ei_to->group != NULL) // group or internal
1717 *ei_to->group = *ei_from->group;
1719 // mark this custom element as modified
1720 ei_to->modified_settings = TRUE;
1723 void setElementChangePages(struct ElementInfo *ei, int change_pages)
1725 int change_page_size = sizeof(struct ElementChangeInfo);
1727 ei->num_change_pages = MAX(1, change_pages);
1730 checked_realloc(ei->change_page, ei->num_change_pages * change_page_size);
1732 if (ei->current_change_page >= ei->num_change_pages)
1733 ei->current_change_page = ei->num_change_pages - 1;
1735 ei->change = &ei->change_page[ei->current_change_page];
1738 void setElementChangeInfoToDefaults(struct ElementChangeInfo *change)
1740 xx_change = *change; // copy change data into temporary buffer
1742 setConfigToDefaultsFromConfigList(chunk_config_CUSX_change);
1744 *change = xx_change;
1746 resetEventFlags(change);
1748 change->direct_action = 0;
1749 change->other_action = 0;
1751 change->pre_change_function = NULL;
1752 change->change_function = NULL;
1753 change->post_change_function = NULL;
1756 static void setLevelInfoToDefaults_Level(struct LevelInfo *level)
1760 li = *level; // copy level data into temporary buffer
1761 setConfigToDefaultsFromConfigList(chunk_config_INFO);
1762 *level = li; // copy temporary buffer back to level data
1764 setLevelInfoToDefaults_EM();
1765 setLevelInfoToDefaults_SP();
1766 setLevelInfoToDefaults_MM();
1768 level->native_em_level = &native_em_level;
1769 level->native_sp_level = &native_sp_level;
1770 level->native_mm_level = &native_mm_level;
1772 level->file_version = FILE_VERSION_ACTUAL;
1773 level->game_version = GAME_VERSION_ACTUAL;
1775 level->creation_date = getCurrentDate();
1777 level->encoding_16bit_field = TRUE;
1778 level->encoding_16bit_yamyam = TRUE;
1779 level->encoding_16bit_amoeba = TRUE;
1781 // clear level name and level author string buffers
1782 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
1783 level->name[i] = '\0';
1784 for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
1785 level->author[i] = '\0';
1787 // set level name and level author to default values
1788 strcpy(level->name, NAMELESS_LEVEL_NAME);
1789 strcpy(level->author, ANONYMOUS_NAME);
1791 // set level playfield to playable default level with player and exit
1792 for (x = 0; x < MAX_LEV_FIELDX; x++)
1793 for (y = 0; y < MAX_LEV_FIELDY; y++)
1794 level->field[x][y] = EL_SAND;
1796 level->field[0][0] = EL_PLAYER_1;
1797 level->field[STD_LEV_FIELDX - 1][STD_LEV_FIELDY - 1] = EL_EXIT_CLOSED;
1799 BorderElement = EL_STEELWALL;
1801 // detect custom elements when loading them
1802 level->file_has_custom_elements = FALSE;
1804 // set all bug compatibility flags to "false" => do not emulate this bug
1805 level->use_action_after_change_bug = FALSE;
1807 if (leveldir_current)
1809 // try to determine better author name than 'anonymous'
1810 if (!strEqual(leveldir_current->author, ANONYMOUS_NAME))
1812 strncpy(level->author, leveldir_current->author, MAX_LEVEL_AUTHOR_LEN);
1813 level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
1817 switch (LEVELCLASS(leveldir_current))
1819 case LEVELCLASS_TUTORIAL:
1820 strcpy(level->author, PROGRAM_AUTHOR_STRING);
1823 case LEVELCLASS_CONTRIB:
1824 strncpy(level->author, leveldir_current->name, MAX_LEVEL_AUTHOR_LEN);
1825 level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
1828 case LEVELCLASS_PRIVATE:
1829 strncpy(level->author, getRealName(), MAX_LEVEL_AUTHOR_LEN);
1830 level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
1834 // keep default value
1841 static void setLevelInfoToDefaults_Elements(struct LevelInfo *level)
1843 static boolean clipboard_elements_initialized = FALSE;
1846 InitElementPropertiesStatic();
1848 li = *level; // copy level data into temporary buffer
1849 setConfigToDefaultsFromConfigList(chunk_config_ELEM);
1850 *level = li; // copy temporary buffer back to level data
1852 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1855 struct ElementInfo *ei = &element_info[element];
1857 if (element == EL_MM_GRAY_BALL)
1859 struct LevelInfo_MM *level_mm = level->native_mm_level;
1862 for (j = 0; j < level->num_mm_ball_contents; j++)
1863 level->mm_ball_content[j] =
1864 map_element_MM_to_RND(level_mm->ball_content[j]);
1867 // never initialize clipboard elements after the very first time
1868 // (to be able to use clipboard elements between several levels)
1869 if (IS_CLIPBOARD_ELEMENT(element) && clipboard_elements_initialized)
1872 if (IS_ENVELOPE(element))
1874 int envelope_nr = element - EL_ENVELOPE_1;
1876 setConfigToDefaultsFromConfigList(chunk_config_NOTE);
1878 level->envelope[envelope_nr] = xx_envelope;
1881 if (IS_CUSTOM_ELEMENT(element) ||
1882 IS_GROUP_ELEMENT(element) ||
1883 IS_INTERNAL_ELEMENT(element))
1885 xx_ei = *ei; // copy element data into temporary buffer
1887 setConfigToDefaultsFromConfigList(chunk_config_CUSX_base);
1892 setElementChangePages(ei, 1);
1893 setElementChangeInfoToDefaults(ei->change);
1895 if (IS_CUSTOM_ELEMENT(element) ||
1896 IS_GROUP_ELEMENT(element) ||
1897 IS_INTERNAL_ELEMENT(element))
1899 setElementDescriptionToDefault(ei);
1901 ei->modified_settings = FALSE;
1904 if (IS_CUSTOM_ELEMENT(element) ||
1905 IS_INTERNAL_ELEMENT(element))
1907 // internal values used in level editor
1909 ei->access_type = 0;
1910 ei->access_layer = 0;
1911 ei->access_protected = 0;
1912 ei->walk_to_action = 0;
1913 ei->smash_targets = 0;
1916 ei->can_explode_by_fire = FALSE;
1917 ei->can_explode_smashed = FALSE;
1918 ei->can_explode_impact = FALSE;
1920 ei->current_change_page = 0;
1923 if (IS_GROUP_ELEMENT(element) ||
1924 IS_INTERNAL_ELEMENT(element))
1926 struct ElementGroupInfo *group;
1928 // initialize memory for list of elements in group
1929 if (ei->group == NULL)
1930 ei->group = checked_malloc(sizeof(struct ElementGroupInfo));
1934 xx_group = *group; // copy group data into temporary buffer
1936 setConfigToDefaultsFromConfigList(chunk_config_GRPX);
1941 if (IS_EMPTY_ELEMENT(element) ||
1942 IS_INTERNAL_ELEMENT(element))
1944 xx_ei = *ei; // copy element data into temporary buffer
1946 setConfigToDefaultsFromConfigList(chunk_config_EMPX);
1952 clipboard_elements_initialized = TRUE;
1955 static void setLevelInfoToDefaults(struct LevelInfo *level,
1956 boolean level_info_only,
1957 boolean reset_file_status)
1959 setLevelInfoToDefaults_Level(level);
1961 if (!level_info_only)
1962 setLevelInfoToDefaults_Elements(level);
1964 if (reset_file_status)
1966 level->no_valid_file = FALSE;
1967 level->no_level_file = FALSE;
1970 level->changed = FALSE;
1973 static void setFileInfoToDefaults(struct LevelFileInfo *level_file_info)
1975 level_file_info->nr = 0;
1976 level_file_info->type = LEVEL_FILE_TYPE_UNKNOWN;
1977 level_file_info->packed = FALSE;
1979 setString(&level_file_info->basename, NULL);
1980 setString(&level_file_info->filename, NULL);
1983 int getMappedElement_SB(int, boolean);
1985 static void ActivateLevelTemplate(void)
1989 if (check_special_flags("load_xsb_to_ces"))
1991 // fill smaller playfields with padding "beyond border wall" elements
1992 if (level.fieldx < level_template.fieldx ||
1993 level.fieldy < level_template.fieldy)
1995 short field[level.fieldx][level.fieldy];
1996 int new_fieldx = MAX(level.fieldx, level_template.fieldx);
1997 int new_fieldy = MAX(level.fieldy, level_template.fieldy);
1998 int pos_fieldx = (new_fieldx - level.fieldx) / 2;
1999 int pos_fieldy = (new_fieldy - level.fieldy) / 2;
2001 // copy old playfield (which is smaller than the visible area)
2002 for (y = 0; y < level.fieldy; y++) for (x = 0; x < level.fieldx; x++)
2003 field[x][y] = level.field[x][y];
2005 // fill new, larger playfield with "beyond border wall" elements
2006 for (y = 0; y < new_fieldy; y++) for (x = 0; x < new_fieldx; x++)
2007 level.field[x][y] = getMappedElement_SB('_', TRUE);
2009 // copy the old playfield to the middle of the new playfield
2010 for (y = 0; y < level.fieldy; y++) for (x = 0; x < level.fieldx; x++)
2011 level.field[pos_fieldx + x][pos_fieldy + y] = field[x][y];
2013 level.fieldx = new_fieldx;
2014 level.fieldy = new_fieldy;
2018 // Currently there is no special action needed to activate the template
2019 // data, because 'element_info' property settings overwrite the original
2020 // level data, while all other variables do not change.
2022 // Exception: 'from_level_template' elements in the original level playfield
2023 // are overwritten with the corresponding elements at the same position in
2024 // playfield from the level template.
2026 for (x = 0; x < level.fieldx; x++)
2027 for (y = 0; y < level.fieldy; y++)
2028 if (level.field[x][y] == EL_FROM_LEVEL_TEMPLATE)
2029 level.field[x][y] = level_template.field[x][y];
2031 if (check_special_flags("load_xsb_to_ces"))
2033 struct LevelInfo level_backup = level;
2035 // overwrite all individual level settings from template level settings
2036 level = level_template;
2038 // restore level file info
2039 level.file_info = level_backup.file_info;
2041 // restore playfield size
2042 level.fieldx = level_backup.fieldx;
2043 level.fieldy = level_backup.fieldy;
2045 // restore playfield content
2046 for (x = 0; x < level.fieldx; x++)
2047 for (y = 0; y < level.fieldy; y++)
2048 level.field[x][y] = level_backup.field[x][y];
2050 // restore name and author from individual level
2051 strcpy(level.name, level_backup.name);
2052 strcpy(level.author, level_backup.author);
2054 // restore flag "use_custom_template"
2055 level.use_custom_template = level_backup.use_custom_template;
2059 static char *getLevelFilenameFromBasename(char *basename)
2061 static char *filename = NULL;
2063 checked_free(filename);
2065 filename = getPath2(getCurrentLevelDir(), basename);
2070 static int getFileTypeFromBasename(char *basename)
2072 // !!! ALSO SEE COMMENT IN checkForPackageFromBasename() !!!
2074 static char *filename = NULL;
2075 struct stat file_status;
2077 // ---------- try to determine file type from filename ----------
2079 // check for typical filename of a Supaplex level package file
2080 if (strlen(basename) == 10 && strPrefixLower(basename, "levels.d"))
2081 return LEVEL_FILE_TYPE_SP;
2083 // check for typical filename of a Diamond Caves II level package file
2084 if (strSuffixLower(basename, ".dc") ||
2085 strSuffixLower(basename, ".dc2"))
2086 return LEVEL_FILE_TYPE_DC;
2088 // check for typical filename of a Sokoban level package file
2089 if (strSuffixLower(basename, ".xsb") &&
2090 strchr(basename, '%') == NULL)
2091 return LEVEL_FILE_TYPE_SB;
2093 // ---------- try to determine file type from filesize ----------
2095 checked_free(filename);
2096 filename = getPath2(getCurrentLevelDir(), basename);
2098 if (stat(filename, &file_status) == 0)
2100 // check for typical filesize of a Supaplex level package file
2101 if (file_status.st_size == 170496)
2102 return LEVEL_FILE_TYPE_SP;
2105 return LEVEL_FILE_TYPE_UNKNOWN;
2108 static int getFileTypeFromMagicBytes(char *filename, int type)
2112 if ((file = openFile(filename, MODE_READ)))
2114 char chunk_name[CHUNK_ID_LEN + 1];
2116 getFileChunkBE(file, chunk_name, NULL);
2118 if (strEqual(chunk_name, "MMII") ||
2119 strEqual(chunk_name, "MIRR"))
2120 type = LEVEL_FILE_TYPE_MM;
2128 static boolean checkForPackageFromBasename(char *basename)
2130 // !!! WON'T WORK ANYMORE IF getFileTypeFromBasename() ALSO DETECTS !!!
2131 // !!! SINGLE LEVELS (CURRENTLY ONLY DETECTS LEVEL PACKAGES !!!
2133 return (getFileTypeFromBasename(basename) != LEVEL_FILE_TYPE_UNKNOWN);
2136 static char *getSingleLevelBasenameExt(int nr, char *extension)
2138 static char basename[MAX_FILENAME_LEN];
2141 sprintf(basename, "%s", LEVELTEMPLATE_FILENAME);
2143 sprintf(basename, "%03d.%s", nr, extension);
2148 static char *getSingleLevelBasename(int nr)
2150 return getSingleLevelBasenameExt(nr, LEVELFILE_EXTENSION);
2153 static char *getPackedLevelBasename(int type)
2155 static char basename[MAX_FILENAME_LEN];
2156 char *directory = getCurrentLevelDir();
2158 DirectoryEntry *dir_entry;
2160 strcpy(basename, UNDEFINED_FILENAME); // default: undefined file
2162 if ((dir = openDirectory(directory)) == NULL)
2164 Warn("cannot read current level directory '%s'", directory);
2169 while ((dir_entry = readDirectory(dir)) != NULL) // loop all entries
2171 char *entry_basename = dir_entry->basename;
2172 int entry_type = getFileTypeFromBasename(entry_basename);
2174 if (entry_type != LEVEL_FILE_TYPE_UNKNOWN) // found valid level package
2176 if (type == LEVEL_FILE_TYPE_UNKNOWN ||
2179 strcpy(basename, entry_basename);
2186 closeDirectory(dir);
2191 static char *getSingleLevelFilename(int nr)
2193 return getLevelFilenameFromBasename(getSingleLevelBasename(nr));
2196 #if ENABLE_UNUSED_CODE
2197 static char *getPackedLevelFilename(int type)
2199 return getLevelFilenameFromBasename(getPackedLevelBasename(type));
2203 char *getDefaultLevelFilename(int nr)
2205 return getSingleLevelFilename(nr);
2208 #if ENABLE_UNUSED_CODE
2209 static void setLevelFileInfo_SingleLevelFilename(struct LevelFileInfo *lfi,
2213 lfi->packed = FALSE;
2215 setString(&lfi->basename, getSingleLevelBasename(lfi->nr, lfi->type));
2216 setString(&lfi->filename, getLevelFilenameFromBasename(lfi->basename));
2220 static void setLevelFileInfo_FormatLevelFilename(struct LevelFileInfo *lfi,
2221 int type, char *format, ...)
2223 static char basename[MAX_FILENAME_LEN];
2226 va_start(ap, format);
2227 vsprintf(basename, format, ap);
2231 lfi->packed = FALSE;
2233 setString(&lfi->basename, basename);
2234 setString(&lfi->filename, getLevelFilenameFromBasename(lfi->basename));
2237 static void setLevelFileInfo_PackedLevelFilename(struct LevelFileInfo *lfi,
2243 setString(&lfi->basename, getPackedLevelBasename(lfi->type));
2244 setString(&lfi->filename, getLevelFilenameFromBasename(lfi->basename));
2247 static int getFiletypeFromID(char *filetype_id)
2249 char *filetype_id_lower;
2250 int filetype = LEVEL_FILE_TYPE_UNKNOWN;
2253 if (filetype_id == NULL)
2254 return LEVEL_FILE_TYPE_UNKNOWN;
2256 filetype_id_lower = getStringToLower(filetype_id);
2258 for (i = 0; filetype_id_list[i].id != NULL; i++)
2260 char *id_lower = getStringToLower(filetype_id_list[i].id);
2262 if (strEqual(filetype_id_lower, id_lower))
2263 filetype = filetype_id_list[i].filetype;
2267 if (filetype != LEVEL_FILE_TYPE_UNKNOWN)
2271 free(filetype_id_lower);
2276 char *getLocalLevelTemplateFilename(void)
2278 return getDefaultLevelFilename(-1);
2281 char *getGlobalLevelTemplateFilename(void)
2283 // global variable "leveldir_current" must be modified in the loop below
2284 LevelDirTree *leveldir_current_last = leveldir_current;
2285 char *filename = NULL;
2287 // check for template level in path from current to topmost tree node
2289 while (leveldir_current != NULL)
2291 filename = getDefaultLevelFilename(-1);
2293 if (fileExists(filename))
2296 leveldir_current = leveldir_current->node_parent;
2299 // restore global variable "leveldir_current" modified in above loop
2300 leveldir_current = leveldir_current_last;
2305 static void determineLevelFileInfo_Filename(struct LevelFileInfo *lfi)
2309 // special case: level number is negative => check for level template file
2312 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
2313 getSingleLevelBasename(-1));
2315 // replace local level template filename with global template filename
2316 setString(&lfi->filename, getGlobalLevelTemplateFilename());
2318 // no fallback if template file not existing
2322 // special case: check for file name/pattern specified in "levelinfo.conf"
2323 if (leveldir_current->level_filename != NULL)
2325 int filetype = getFiletypeFromID(leveldir_current->level_filetype);
2327 setLevelFileInfo_FormatLevelFilename(lfi, filetype,
2328 leveldir_current->level_filename, nr);
2330 lfi->packed = checkForPackageFromBasename(leveldir_current->level_filename);
2332 if (fileExists(lfi->filename))
2335 else if (leveldir_current->level_filetype != NULL)
2337 int filetype = getFiletypeFromID(leveldir_current->level_filetype);
2339 // check for specified native level file with standard file name
2340 setLevelFileInfo_FormatLevelFilename(lfi, filetype,
2341 "%03d.%s", nr, LEVELFILE_EXTENSION);
2342 if (fileExists(lfi->filename))
2346 // check for native Rocks'n'Diamonds level file
2347 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
2348 "%03d.%s", nr, LEVELFILE_EXTENSION);
2349 if (fileExists(lfi->filename))
2352 // check for Emerald Mine level file (V1)
2353 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "a%c%c",
2354 'a' + (nr / 10) % 26, '0' + nr % 10);
2355 if (fileExists(lfi->filename))
2357 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "A%c%c",
2358 'A' + (nr / 10) % 26, '0' + nr % 10);
2359 if (fileExists(lfi->filename))
2362 // check for Emerald Mine level file (V2 to V5)
2363 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%d", nr);
2364 if (fileExists(lfi->filename))
2367 // check for Emerald Mine level file (V6 / single mode)
2368 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02ds", nr);
2369 if (fileExists(lfi->filename))
2371 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02dS", nr);
2372 if (fileExists(lfi->filename))
2375 // check for Emerald Mine level file (V6 / teamwork mode)
2376 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02dt", nr);
2377 if (fileExists(lfi->filename))
2379 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02dT", nr);
2380 if (fileExists(lfi->filename))
2383 // check for various packed level file formats
2384 setLevelFileInfo_PackedLevelFilename(lfi, LEVEL_FILE_TYPE_UNKNOWN);
2385 if (fileExists(lfi->filename))
2388 // no known level file found -- use default values (and fail later)
2389 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
2390 "%03d.%s", nr, LEVELFILE_EXTENSION);
2393 static void determineLevelFileInfo_Filetype(struct LevelFileInfo *lfi)
2395 if (lfi->type == LEVEL_FILE_TYPE_UNKNOWN)
2396 lfi->type = getFileTypeFromBasename(lfi->basename);
2398 if (lfi->type == LEVEL_FILE_TYPE_RND)
2399 lfi->type = getFileTypeFromMagicBytes(lfi->filename, lfi->type);
2402 static void setLevelFileInfo(struct LevelFileInfo *level_file_info, int nr)
2404 // always start with reliable default values
2405 setFileInfoToDefaults(level_file_info);
2407 level_file_info->nr = nr; // set requested level number
2409 determineLevelFileInfo_Filename(level_file_info);
2410 determineLevelFileInfo_Filetype(level_file_info);
2413 static void copyLevelFileInfo(struct LevelFileInfo *lfi_from,
2414 struct LevelFileInfo *lfi_to)
2416 lfi_to->nr = lfi_from->nr;
2417 lfi_to->type = lfi_from->type;
2418 lfi_to->packed = lfi_from->packed;
2420 setString(&lfi_to->basename, lfi_from->basename);
2421 setString(&lfi_to->filename, lfi_from->filename);
2424 // ----------------------------------------------------------------------------
2425 // functions for loading R'n'D level
2426 // ----------------------------------------------------------------------------
2428 int getMappedElement(int element)
2430 // remap some (historic, now obsolete) elements
2434 case EL_PLAYER_OBSOLETE:
2435 element = EL_PLAYER_1;
2438 case EL_KEY_OBSOLETE:
2442 case EL_EM_KEY_1_FILE_OBSOLETE:
2443 element = EL_EM_KEY_1;
2446 case EL_EM_KEY_2_FILE_OBSOLETE:
2447 element = EL_EM_KEY_2;
2450 case EL_EM_KEY_3_FILE_OBSOLETE:
2451 element = EL_EM_KEY_3;
2454 case EL_EM_KEY_4_FILE_OBSOLETE:
2455 element = EL_EM_KEY_4;
2458 case EL_ENVELOPE_OBSOLETE:
2459 element = EL_ENVELOPE_1;
2467 if (element >= NUM_FILE_ELEMENTS)
2469 Warn("invalid level element %d", element);
2471 element = EL_UNKNOWN;
2479 static int getMappedElementByVersion(int element, int game_version)
2481 // remap some elements due to certain game version
2483 if (game_version <= VERSION_IDENT(2,2,0,0))
2485 // map game font elements
2486 element = (element == EL_CHAR('[') ? EL_CHAR_AUMLAUT :
2487 element == EL_CHAR('\\') ? EL_CHAR_OUMLAUT :
2488 element == EL_CHAR(']') ? EL_CHAR_UUMLAUT :
2489 element == EL_CHAR('^') ? EL_CHAR_COPYRIGHT : element);
2492 if (game_version < VERSION_IDENT(3,0,0,0))
2494 // map Supaplex gravity tube elements
2495 element = (element == EL_SP_GRAVITY_PORT_LEFT ? EL_SP_PORT_LEFT :
2496 element == EL_SP_GRAVITY_PORT_RIGHT ? EL_SP_PORT_RIGHT :
2497 element == EL_SP_GRAVITY_PORT_UP ? EL_SP_PORT_UP :
2498 element == EL_SP_GRAVITY_PORT_DOWN ? EL_SP_PORT_DOWN :
2505 static int LoadLevel_VERS(File *file, int chunk_size, struct LevelInfo *level)
2507 level->file_version = getFileVersion(file);
2508 level->game_version = getFileVersion(file);
2513 static int LoadLevel_DATE(File *file, int chunk_size, struct LevelInfo *level)
2515 level->creation_date.year = getFile16BitBE(file);
2516 level->creation_date.month = getFile8Bit(file);
2517 level->creation_date.day = getFile8Bit(file);
2519 level->creation_date.src = DATE_SRC_LEVELFILE;
2524 static int LoadLevel_HEAD(File *file, int chunk_size, struct LevelInfo *level)
2526 int initial_player_stepsize;
2527 int initial_player_gravity;
2530 level->fieldx = getFile8Bit(file);
2531 level->fieldy = getFile8Bit(file);
2533 level->time = getFile16BitBE(file);
2534 level->gems_needed = getFile16BitBE(file);
2536 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
2537 level->name[i] = getFile8Bit(file);
2538 level->name[MAX_LEVEL_NAME_LEN] = 0;
2540 for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
2541 level->score[i] = getFile8Bit(file);
2543 level->num_yamyam_contents = STD_ELEMENT_CONTENTS;
2544 for (i = 0; i < STD_ELEMENT_CONTENTS; i++)
2545 for (y = 0; y < 3; y++)
2546 for (x = 0; x < 3; x++)
2547 level->yamyam_content[i].e[x][y] = getMappedElement(getFile8Bit(file));
2549 level->amoeba_speed = getFile8Bit(file);
2550 level->time_magic_wall = getFile8Bit(file);
2551 level->time_wheel = getFile8Bit(file);
2552 level->amoeba_content = getMappedElement(getFile8Bit(file));
2554 initial_player_stepsize = (getFile8Bit(file) == 1 ? STEPSIZE_FAST :
2557 for (i = 0; i < MAX_PLAYERS; i++)
2558 level->initial_player_stepsize[i] = initial_player_stepsize;
2560 initial_player_gravity = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2562 for (i = 0; i < MAX_PLAYERS; i++)
2563 level->initial_player_gravity[i] = initial_player_gravity;
2565 level->encoding_16bit_field = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2566 level->em_slippery_gems = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2568 level->use_custom_template = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2570 level->block_last_field = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2571 level->sp_block_last_field = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2572 level->can_move_into_acid_bits = getFile32BitBE(file);
2573 level->dont_collide_with_bits = getFile8Bit(file);
2575 level->use_spring_bug = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2576 level->use_step_counter = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2578 level->instant_relocation = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2579 level->can_pass_to_walkable = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2580 level->grow_into_diggable = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2582 level->game_engine_type = getFile8Bit(file);
2584 ReadUnusedBytesFromFile(file, LEVEL_CHUNK_HEAD_UNUSED);
2589 static int LoadLevel_NAME(File *file, int chunk_size, struct LevelInfo *level)
2593 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
2594 level->name[i] = getFile8Bit(file);
2595 level->name[MAX_LEVEL_NAME_LEN] = 0;
2600 static int LoadLevel_AUTH(File *file, int chunk_size, struct LevelInfo *level)
2604 for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
2605 level->author[i] = getFile8Bit(file);
2606 level->author[MAX_LEVEL_AUTHOR_LEN] = 0;
2611 static int LoadLevel_BODY(File *file, int chunk_size, struct LevelInfo *level)
2614 int chunk_size_expected = level->fieldx * level->fieldy;
2616 /* Note: "chunk_size" was wrong before version 2.0 when elements are
2617 stored with 16-bit encoding (and should be twice as big then).
2618 Even worse, playfield data was stored 16-bit when only yamyam content
2619 contained 16-bit elements and vice versa. */
2621 if (level->encoding_16bit_field && level->file_version >= FILE_VERSION_2_0)
2622 chunk_size_expected *= 2;
2624 if (chunk_size_expected != chunk_size)
2626 ReadUnusedBytesFromFile(file, chunk_size);
2627 return chunk_size_expected;
2630 for (y = 0; y < level->fieldy; y++)
2631 for (x = 0; x < level->fieldx; x++)
2632 level->field[x][y] =
2633 getMappedElement(level->encoding_16bit_field ? getFile16BitBE(file) :
2638 static int LoadLevel_CONT(File *file, int chunk_size, struct LevelInfo *level)
2641 int header_size = 4;
2642 int content_size = MAX_ELEMENT_CONTENTS * 3 * 3;
2643 int chunk_size_expected = header_size + content_size;
2645 /* Note: "chunk_size" was wrong before version 2.0 when elements are
2646 stored with 16-bit encoding (and should be twice as big then).
2647 Even worse, playfield data was stored 16-bit when only yamyam content
2648 contained 16-bit elements and vice versa. */
2650 if (level->encoding_16bit_field && level->file_version >= FILE_VERSION_2_0)
2651 chunk_size_expected += content_size;
2653 if (chunk_size_expected != chunk_size)
2655 ReadUnusedBytesFromFile(file, chunk_size);
2656 return chunk_size_expected;
2660 level->num_yamyam_contents = getFile8Bit(file);
2664 // correct invalid number of content fields -- should never happen
2665 if (level->num_yamyam_contents < 1 ||
2666 level->num_yamyam_contents > MAX_ELEMENT_CONTENTS)
2667 level->num_yamyam_contents = STD_ELEMENT_CONTENTS;
2669 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
2670 for (y = 0; y < 3; y++)
2671 for (x = 0; x < 3; x++)
2672 level->yamyam_content[i].e[x][y] =
2673 getMappedElement(level->encoding_16bit_field ?
2674 getFile16BitBE(file) : getFile8Bit(file));
2678 static int LoadLevel_CNT2(File *file, int chunk_size, struct LevelInfo *level)
2683 int content_array[MAX_ELEMENT_CONTENTS][3][3];
2685 element = getMappedElement(getFile16BitBE(file));
2686 num_contents = getFile8Bit(file);
2688 getFile8Bit(file); // content x size (unused)
2689 getFile8Bit(file); // content y size (unused)
2691 ReadUnusedBytesFromFile(file, LEVEL_CHUNK_CNT2_UNUSED);
2693 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
2694 for (y = 0; y < 3; y++)
2695 for (x = 0; x < 3; x++)
2696 content_array[i][x][y] = getMappedElement(getFile16BitBE(file));
2698 // correct invalid number of content fields -- should never happen
2699 if (num_contents < 1 || num_contents > MAX_ELEMENT_CONTENTS)
2700 num_contents = STD_ELEMENT_CONTENTS;
2702 if (element == EL_YAMYAM)
2704 level->num_yamyam_contents = num_contents;
2706 for (i = 0; i < num_contents; i++)
2707 for (y = 0; y < 3; y++)
2708 for (x = 0; x < 3; x++)
2709 level->yamyam_content[i].e[x][y] = content_array[i][x][y];
2711 else if (element == EL_BD_AMOEBA)
2713 level->amoeba_content = content_array[0][0][0];
2717 Warn("cannot load content for element '%d'", element);
2723 static int LoadLevel_CNT3(File *file, int chunk_size, struct LevelInfo *level)
2729 int chunk_size_expected;
2731 element = getMappedElement(getFile16BitBE(file));
2732 if (!IS_ENVELOPE(element))
2733 element = EL_ENVELOPE_1;
2735 envelope_nr = element - EL_ENVELOPE_1;
2737 envelope_len = getFile16BitBE(file);
2739 level->envelope[envelope_nr].xsize = getFile8Bit(file);
2740 level->envelope[envelope_nr].ysize = getFile8Bit(file);
2742 ReadUnusedBytesFromFile(file, LEVEL_CHUNK_CNT3_UNUSED);
2744 chunk_size_expected = LEVEL_CHUNK_CNT3_SIZE(envelope_len);
2745 if (chunk_size_expected != chunk_size)
2747 ReadUnusedBytesFromFile(file, chunk_size - LEVEL_CHUNK_CNT3_HEADER);
2748 return chunk_size_expected;
2751 for (i = 0; i < envelope_len; i++)
2752 level->envelope[envelope_nr].text[i] = getFile8Bit(file);
2757 static int LoadLevel_CUS1(File *file, int chunk_size, struct LevelInfo *level)
2759 int num_changed_custom_elements = getFile16BitBE(file);
2760 int chunk_size_expected = 2 + num_changed_custom_elements * 6;
2763 if (chunk_size_expected != chunk_size)
2765 ReadUnusedBytesFromFile(file, chunk_size - 2);
2766 return chunk_size_expected;
2769 for (i = 0; i < num_changed_custom_elements; i++)
2771 int element = getMappedElement(getFile16BitBE(file));
2772 int properties = getFile32BitBE(file);
2774 if (IS_CUSTOM_ELEMENT(element))
2775 element_info[element].properties[EP_BITFIELD_BASE_NR] = properties;
2777 Warn("invalid custom element number %d", element);
2779 // older game versions that wrote level files with CUS1 chunks used
2780 // different default push delay values (not yet stored in level file)
2781 element_info[element].push_delay_fixed = 2;
2782 element_info[element].push_delay_random = 8;
2785 level->file_has_custom_elements = TRUE;
2790 static int LoadLevel_CUS2(File *file, int chunk_size, struct LevelInfo *level)
2792 int num_changed_custom_elements = getFile16BitBE(file);
2793 int chunk_size_expected = 2 + num_changed_custom_elements * 4;
2796 if (chunk_size_expected != chunk_size)
2798 ReadUnusedBytesFromFile(file, chunk_size - 2);
2799 return chunk_size_expected;
2802 for (i = 0; i < num_changed_custom_elements; i++)
2804 int element = getMappedElement(getFile16BitBE(file));
2805 int custom_target_element = getMappedElement(getFile16BitBE(file));
2807 if (IS_CUSTOM_ELEMENT(element))
2808 element_info[element].change->target_element = custom_target_element;
2810 Warn("invalid custom element number %d", element);
2813 level->file_has_custom_elements = TRUE;
2818 static int LoadLevel_CUS3(File *file, int chunk_size, struct LevelInfo *level)
2820 int num_changed_custom_elements = getFile16BitBE(file);
2821 int chunk_size_expected = LEVEL_CHUNK_CUS3_SIZE(num_changed_custom_elements);
2824 if (chunk_size_expected != chunk_size)
2826 ReadUnusedBytesFromFile(file, chunk_size - 2);
2827 return chunk_size_expected;
2830 for (i = 0; i < num_changed_custom_elements; i++)
2832 int element = getMappedElement(getFile16BitBE(file));
2833 struct ElementInfo *ei = &element_info[element];
2834 unsigned int event_bits;
2836 if (!IS_CUSTOM_ELEMENT(element))
2838 Warn("invalid custom element number %d", element);
2840 element = EL_INTERNAL_DUMMY;
2843 for (j = 0; j < MAX_ELEMENT_NAME_LEN; j++)
2844 ei->description[j] = getFile8Bit(file);
2845 ei->description[MAX_ELEMENT_NAME_LEN] = 0;
2847 ei->properties[EP_BITFIELD_BASE_NR] = getFile32BitBE(file);
2849 // some free bytes for future properties and padding
2850 ReadUnusedBytesFromFile(file, 7);
2852 ei->use_gfx_element = getFile8Bit(file);
2853 ei->gfx_element_initial = getMappedElement(getFile16BitBE(file));
2855 ei->collect_score_initial = getFile8Bit(file);
2856 ei->collect_count_initial = getFile8Bit(file);
2858 ei->push_delay_fixed = getFile16BitBE(file);
2859 ei->push_delay_random = getFile16BitBE(file);
2860 ei->move_delay_fixed = getFile16BitBE(file);
2861 ei->move_delay_random = getFile16BitBE(file);
2863 ei->move_pattern = getFile16BitBE(file);
2864 ei->move_direction_initial = getFile8Bit(file);
2865 ei->move_stepsize = getFile8Bit(file);
2867 for (y = 0; y < 3; y++)
2868 for (x = 0; x < 3; x++)
2869 ei->content.e[x][y] = getMappedElement(getFile16BitBE(file));
2871 // bits 0 - 31 of "has_event[]"
2872 event_bits = getFile32BitBE(file);
2873 for (j = 0; j < MIN(NUM_CHANGE_EVENTS, 32); j++)
2874 if (event_bits & (1u << j))
2875 ei->change->has_event[j] = TRUE;
2877 ei->change->target_element = getMappedElement(getFile16BitBE(file));
2879 ei->change->delay_fixed = getFile16BitBE(file);
2880 ei->change->delay_random = getFile16BitBE(file);
2881 ei->change->delay_frames = getFile16BitBE(file);
2883 ei->change->initial_trigger_element= getMappedElement(getFile16BitBE(file));
2885 ei->change->explode = getFile8Bit(file);
2886 ei->change->use_target_content = getFile8Bit(file);
2887 ei->change->only_if_complete = getFile8Bit(file);
2888 ei->change->use_random_replace = getFile8Bit(file);
2890 ei->change->random_percentage = getFile8Bit(file);
2891 ei->change->replace_when = getFile8Bit(file);
2893 for (y = 0; y < 3; y++)
2894 for (x = 0; x < 3; x++)
2895 ei->change->target_content.e[x][y] =
2896 getMappedElement(getFile16BitBE(file));
2898 ei->slippery_type = getFile8Bit(file);
2900 // some free bytes for future properties and padding
2901 ReadUnusedBytesFromFile(file, LEVEL_CPART_CUS3_UNUSED);
2903 // mark that this custom element has been modified
2904 ei->modified_settings = TRUE;
2907 level->file_has_custom_elements = TRUE;
2912 static int LoadLevel_CUS4(File *file, int chunk_size, struct LevelInfo *level)
2914 struct ElementInfo *ei;
2915 int chunk_size_expected;
2919 // ---------- custom element base property values (96 bytes) ----------------
2921 element = getMappedElement(getFile16BitBE(file));
2923 if (!IS_CUSTOM_ELEMENT(element))
2925 Warn("invalid custom element number %d", element);
2927 ReadUnusedBytesFromFile(file, chunk_size - 2);
2932 ei = &element_info[element];
2934 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
2935 ei->description[i] = getFile8Bit(file);
2936 ei->description[MAX_ELEMENT_NAME_LEN] = 0;
2938 ei->properties[EP_BITFIELD_BASE_NR] = getFile32BitBE(file);
2940 ReadUnusedBytesFromFile(file, 4); // reserved for more base properties
2942 ei->num_change_pages = getFile8Bit(file);
2944 chunk_size_expected = LEVEL_CHUNK_CUS4_SIZE(ei->num_change_pages);
2945 if (chunk_size_expected != chunk_size)
2947 ReadUnusedBytesFromFile(file, chunk_size - 43);
2948 return chunk_size_expected;
2951 ei->ce_value_fixed_initial = getFile16BitBE(file);
2952 ei->ce_value_random_initial = getFile16BitBE(file);
2953 ei->use_last_ce_value = getFile8Bit(file);
2955 ei->use_gfx_element = getFile8Bit(file);
2956 ei->gfx_element_initial = getMappedElement(getFile16BitBE(file));
2958 ei->collect_score_initial = getFile8Bit(file);
2959 ei->collect_count_initial = getFile8Bit(file);
2961 ei->drop_delay_fixed = getFile8Bit(file);
2962 ei->push_delay_fixed = getFile8Bit(file);
2963 ei->drop_delay_random = getFile8Bit(file);
2964 ei->push_delay_random = getFile8Bit(file);
2965 ei->move_delay_fixed = getFile16BitBE(file);
2966 ei->move_delay_random = getFile16BitBE(file);
2968 // bits 0 - 15 of "move_pattern" ...
2969 ei->move_pattern = getFile16BitBE(file);
2970 ei->move_direction_initial = getFile8Bit(file);
2971 ei->move_stepsize = getFile8Bit(file);
2973 ei->slippery_type = getFile8Bit(file);
2975 for (y = 0; y < 3; y++)
2976 for (x = 0; x < 3; x++)
2977 ei->content.e[x][y] = getMappedElement(getFile16BitBE(file));
2979 ei->move_enter_element = getMappedElement(getFile16BitBE(file));
2980 ei->move_leave_element = getMappedElement(getFile16BitBE(file));
2981 ei->move_leave_type = getFile8Bit(file);
2983 // ... bits 16 - 31 of "move_pattern" (not nice, but downward compatible)
2984 ei->move_pattern |= (getFile16BitBE(file) << 16);
2986 ei->access_direction = getFile8Bit(file);
2988 ei->explosion_delay = getFile8Bit(file);
2989 ei->ignition_delay = getFile8Bit(file);
2990 ei->explosion_type = getFile8Bit(file);
2992 // some free bytes for future custom property values and padding
2993 ReadUnusedBytesFromFile(file, 1);
2995 // ---------- change page property values (48 bytes) ------------------------
2997 setElementChangePages(ei, ei->num_change_pages);
2999 for (i = 0; i < ei->num_change_pages; i++)
3001 struct ElementChangeInfo *change = &ei->change_page[i];
3002 unsigned int event_bits;
3004 // always start with reliable default values
3005 setElementChangeInfoToDefaults(change);
3007 // bits 0 - 31 of "has_event[]" ...
3008 event_bits = getFile32BitBE(file);
3009 for (j = 0; j < MIN(NUM_CHANGE_EVENTS, 32); j++)
3010 if (event_bits & (1u << j))
3011 change->has_event[j] = TRUE;
3013 change->target_element = getMappedElement(getFile16BitBE(file));
3015 change->delay_fixed = getFile16BitBE(file);
3016 change->delay_random = getFile16BitBE(file);
3017 change->delay_frames = getFile16BitBE(file);
3019 change->initial_trigger_element = getMappedElement(getFile16BitBE(file));
3021 change->explode = getFile8Bit(file);
3022 change->use_target_content = getFile8Bit(file);
3023 change->only_if_complete = getFile8Bit(file);
3024 change->use_random_replace = getFile8Bit(file);
3026 change->random_percentage = getFile8Bit(file);
3027 change->replace_when = getFile8Bit(file);
3029 for (y = 0; y < 3; y++)
3030 for (x = 0; x < 3; x++)
3031 change->target_content.e[x][y]= getMappedElement(getFile16BitBE(file));
3033 change->can_change = getFile8Bit(file);
3035 change->trigger_side = getFile8Bit(file);
3037 change->trigger_player = getFile8Bit(file);
3038 change->trigger_page = getFile8Bit(file);
3040 change->trigger_page = (change->trigger_page == CH_PAGE_ANY_FILE ?
3041 CH_PAGE_ANY : (1 << change->trigger_page));
3043 change->has_action = getFile8Bit(file);
3044 change->action_type = getFile8Bit(file);
3045 change->action_mode = getFile8Bit(file);
3046 change->action_arg = getFile16BitBE(file);
3048 // ... bits 32 - 39 of "has_event[]" (not nice, but downward compatible)
3049 event_bits = getFile8Bit(file);
3050 for (j = 32; j < NUM_CHANGE_EVENTS; j++)
3051 if (event_bits & (1u << (j - 32)))
3052 change->has_event[j] = TRUE;
3055 // mark this custom element as modified
3056 ei->modified_settings = TRUE;
3058 level->file_has_custom_elements = TRUE;
3063 static int LoadLevel_GRP1(File *file, int chunk_size, struct LevelInfo *level)
3065 struct ElementInfo *ei;
3066 struct ElementGroupInfo *group;
3070 element = getMappedElement(getFile16BitBE(file));
3072 if (!IS_GROUP_ELEMENT(element))
3074 Warn("invalid group element number %d", element);
3076 ReadUnusedBytesFromFile(file, chunk_size - 2);
3081 ei = &element_info[element];
3083 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
3084 ei->description[i] = getFile8Bit(file);
3085 ei->description[MAX_ELEMENT_NAME_LEN] = 0;
3087 group = element_info[element].group;
3089 group->num_elements = getFile8Bit(file);
3091 ei->use_gfx_element = getFile8Bit(file);
3092 ei->gfx_element_initial = getMappedElement(getFile16BitBE(file));
3094 group->choice_mode = getFile8Bit(file);
3096 // some free bytes for future values and padding
3097 ReadUnusedBytesFromFile(file, 3);
3099 for (i = 0; i < MAX_ELEMENTS_IN_GROUP; i++)
3100 group->element[i] = getMappedElement(getFile16BitBE(file));
3102 // mark this group element as modified
3103 element_info[element].modified_settings = TRUE;
3105 level->file_has_custom_elements = TRUE;
3110 static int LoadLevel_MicroChunk(File *file, struct LevelFileConfigInfo *conf,
3111 int element, int real_element)
3113 int micro_chunk_size = 0;
3114 int conf_type = getFile8Bit(file);
3115 int byte_mask = conf_type & CONF_MASK_BYTES;
3116 boolean element_found = FALSE;
3119 micro_chunk_size += 1;
3121 if (byte_mask == CONF_MASK_MULTI_BYTES)
3123 int num_bytes = getFile16BitBE(file);
3124 byte *buffer = checked_malloc(num_bytes);
3126 ReadBytesFromFile(file, buffer, num_bytes);
3128 for (i = 0; conf[i].data_type != -1; i++)
3130 if (conf[i].element == element &&
3131 conf[i].conf_type == conf_type)
3133 int data_type = conf[i].data_type;
3134 int num_entities = num_bytes / CONF_ENTITY_NUM_BYTES(data_type);
3135 int max_num_entities = conf[i].max_num_entities;
3137 if (num_entities > max_num_entities)
3139 Warn("truncating number of entities for element %d from %d to %d",
3140 element, num_entities, max_num_entities);
3142 num_entities = max_num_entities;
3145 if (num_entities == 0 && (data_type == TYPE_ELEMENT_LIST ||
3146 data_type == TYPE_CONTENT_LIST))
3148 // for element and content lists, zero entities are not allowed
3149 Warn("found empty list of entities for element %d", element);
3151 // do not set "num_entities" here to prevent reading behind buffer
3153 *(int *)(conf[i].num_entities) = 1; // at least one is required
3157 *(int *)(conf[i].num_entities) = num_entities;
3160 element_found = TRUE;
3162 if (data_type == TYPE_STRING)
3164 char *string = (char *)(conf[i].value);
3167 for (j = 0; j < max_num_entities; j++)
3168 string[j] = (j < num_entities ? buffer[j] : '\0');
3170 else if (data_type == TYPE_ELEMENT_LIST)
3172 int *element_array = (int *)(conf[i].value);
3175 for (j = 0; j < num_entities; j++)
3177 getMappedElement(CONF_ELEMENTS_ELEMENT(buffer, j));
3179 else if (data_type == TYPE_CONTENT_LIST)
3181 struct Content *content= (struct Content *)(conf[i].value);
3184 for (c = 0; c < num_entities; c++)
3185 for (y = 0; y < 3; y++)
3186 for (x = 0; x < 3; x++)
3187 content[c].e[x][y] =
3188 getMappedElement(CONF_CONTENTS_ELEMENT(buffer, c, x, y));
3191 element_found = FALSE;
3197 checked_free(buffer);
3199 micro_chunk_size += 2 + num_bytes;
3201 else // constant size configuration data (1, 2 or 4 bytes)
3203 int value = (byte_mask == CONF_MASK_1_BYTE ? getFile8Bit (file) :
3204 byte_mask == CONF_MASK_2_BYTE ? getFile16BitBE(file) :
3205 byte_mask == CONF_MASK_4_BYTE ? getFile32BitBE(file) : 0);
3207 for (i = 0; conf[i].data_type != -1; i++)
3209 if (conf[i].element == element &&
3210 conf[i].conf_type == conf_type)
3212 int data_type = conf[i].data_type;
3214 if (data_type == TYPE_ELEMENT)
3215 value = getMappedElement(value);
3217 if (data_type == TYPE_BOOLEAN)
3218 *(boolean *)(conf[i].value) = (value ? TRUE : FALSE);
3220 *(int *) (conf[i].value) = value;
3222 element_found = TRUE;
3228 micro_chunk_size += CONF_VALUE_NUM_BYTES(byte_mask);
3233 char *error_conf_chunk_bytes =
3234 (byte_mask == CONF_MASK_1_BYTE ? "CONF_VALUE_8_BIT" :
3235 byte_mask == CONF_MASK_2_BYTE ? "CONF_VALUE_16_BIT" :
3236 byte_mask == CONF_MASK_4_BYTE ? "CONF_VALUE_32_BIT" :"CONF_VALUE_BYTES");
3237 int error_conf_chunk_token = conf_type & CONF_MASK_TOKEN;
3238 int error_element = real_element;
3240 Warn("cannot load micro chunk '%s(%d)' value for element %d ['%s']",
3241 error_conf_chunk_bytes, error_conf_chunk_token,
3242 error_element, EL_NAME(error_element));
3245 return micro_chunk_size;
3248 static int LoadLevel_INFO(File *file, int chunk_size, struct LevelInfo *level)
3250 int real_chunk_size = 0;
3252 li = *level; // copy level data into temporary buffer
3254 while (!checkEndOfFile(file))
3256 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_INFO, -1, -1);
3258 if (real_chunk_size >= chunk_size)
3262 *level = li; // copy temporary buffer back to level data
3264 return real_chunk_size;
3267 static int LoadLevel_CONF(File *file, int chunk_size, struct LevelInfo *level)
3269 int real_chunk_size = 0;
3271 li = *level; // copy level data into temporary buffer
3273 while (!checkEndOfFile(file))
3275 int element = getMappedElement(getFile16BitBE(file));
3277 real_chunk_size += 2;
3278 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_CONF,
3280 if (real_chunk_size >= chunk_size)
3284 *level = li; // copy temporary buffer back to level data
3286 return real_chunk_size;
3289 static int LoadLevel_ELEM(File *file, int chunk_size, struct LevelInfo *level)
3291 int real_chunk_size = 0;
3293 li = *level; // copy level data into temporary buffer
3295 while (!checkEndOfFile(file))
3297 int element = getMappedElement(getFile16BitBE(file));
3299 real_chunk_size += 2;
3300 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_ELEM,
3302 if (real_chunk_size >= chunk_size)
3306 *level = li; // copy temporary buffer back to level data
3308 return real_chunk_size;
3311 static int LoadLevel_NOTE(File *file, int chunk_size, struct LevelInfo *level)
3313 int element = getMappedElement(getFile16BitBE(file));
3314 int envelope_nr = element - EL_ENVELOPE_1;
3315 int real_chunk_size = 2;
3317 xx_envelope = level->envelope[envelope_nr]; // copy into temporary buffer
3319 while (!checkEndOfFile(file))
3321 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_NOTE,
3324 if (real_chunk_size >= chunk_size)
3328 level->envelope[envelope_nr] = xx_envelope; // copy from temporary buffer
3330 return real_chunk_size;
3333 static int LoadLevel_CUSX(File *file, int chunk_size, struct LevelInfo *level)
3335 int element = getMappedElement(getFile16BitBE(file));
3336 int real_chunk_size = 2;
3337 struct ElementInfo *ei = &element_info[element];
3340 xx_ei = *ei; // copy element data into temporary buffer
3342 xx_ei.num_change_pages = -1;
3344 while (!checkEndOfFile(file))
3346 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_CUSX_base,
3348 if (xx_ei.num_change_pages != -1)
3351 if (real_chunk_size >= chunk_size)
3357 if (ei->num_change_pages == -1)
3359 Warn("LoadLevel_CUSX(): missing 'num_change_pages' for '%s'",
3362 ei->num_change_pages = 1;
3364 setElementChangePages(ei, 1);
3365 setElementChangeInfoToDefaults(ei->change);
3367 return real_chunk_size;
3370 // initialize number of change pages stored for this custom element
3371 setElementChangePages(ei, ei->num_change_pages);
3372 for (i = 0; i < ei->num_change_pages; i++)
3373 setElementChangeInfoToDefaults(&ei->change_page[i]);
3375 // start with reading properties for the first change page
3376 xx_current_change_page = 0;
3378 while (!checkEndOfFile(file))
3380 // level file might contain invalid change page number
3381 if (xx_current_change_page >= ei->num_change_pages)
3384 struct ElementChangeInfo *change = &ei->change_page[xx_current_change_page];
3386 xx_change = *change; // copy change data into temporary buffer
3388 resetEventBits(); // reset bits; change page might have changed
3390 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_CUSX_change,
3393 *change = xx_change;
3395 setEventFlagsFromEventBits(change);
3397 if (real_chunk_size >= chunk_size)
3401 level->file_has_custom_elements = TRUE;
3403 return real_chunk_size;
3406 static int LoadLevel_GRPX(File *file, int chunk_size, struct LevelInfo *level)
3408 int element = getMappedElement(getFile16BitBE(file));
3409 int real_chunk_size = 2;
3410 struct ElementInfo *ei = &element_info[element];
3411 struct ElementGroupInfo *group = ei->group;
3416 xx_ei = *ei; // copy element data into temporary buffer
3417 xx_group = *group; // copy group data into temporary buffer
3419 while (!checkEndOfFile(file))
3421 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_GRPX,
3424 if (real_chunk_size >= chunk_size)
3431 level->file_has_custom_elements = TRUE;
3433 return real_chunk_size;
3436 static int LoadLevel_EMPX(File *file, int chunk_size, struct LevelInfo *level)
3438 int element = getMappedElement(getFile16BitBE(file));
3439 int real_chunk_size = 2;
3440 struct ElementInfo *ei = &element_info[element];
3442 xx_ei = *ei; // copy element data into temporary buffer
3444 while (!checkEndOfFile(file))
3446 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_EMPX,
3449 if (real_chunk_size >= chunk_size)
3455 level->file_has_custom_elements = TRUE;
3457 return real_chunk_size;
3460 static void LoadLevelFromFileInfo_RND(struct LevelInfo *level,
3461 struct LevelFileInfo *level_file_info,
3462 boolean level_info_only)
3464 char *filename = level_file_info->filename;
3465 char cookie[MAX_LINE_LEN];
3466 char chunk_name[CHUNK_ID_LEN + 1];
3470 if (!(file = openFile(filename, MODE_READ)))
3472 level->no_valid_file = TRUE;
3473 level->no_level_file = TRUE;
3475 if (level_info_only)
3478 Warn("cannot read level '%s' -- using empty level", filename);
3480 if (!setup.editor.use_template_for_new_levels)
3483 // if level file not found, try to initialize level data from template
3484 filename = getGlobalLevelTemplateFilename();
3486 if (!(file = openFile(filename, MODE_READ)))
3489 // default: for empty levels, use level template for custom elements
3490 level->use_custom_template = TRUE;
3492 level->no_valid_file = FALSE;
3495 getFileChunkBE(file, chunk_name, NULL);
3496 if (strEqual(chunk_name, "RND1"))
3498 getFile32BitBE(file); // not used
3500 getFileChunkBE(file, chunk_name, NULL);
3501 if (!strEqual(chunk_name, "CAVE"))
3503 level->no_valid_file = TRUE;
3505 Warn("unknown format of level file '%s'", filename);
3512 else // check for pre-2.0 file format with cookie string
3514 strcpy(cookie, chunk_name);
3515 if (getStringFromFile(file, &cookie[4], MAX_LINE_LEN - 4) == NULL)
3517 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
3518 cookie[strlen(cookie) - 1] = '\0';
3520 if (!checkCookieString(cookie, LEVEL_COOKIE_TMPL))
3522 level->no_valid_file = TRUE;
3524 Warn("unknown format of level file '%s'", filename);
3531 if ((level->file_version = getFileVersionFromCookieString(cookie)) == -1)
3533 level->no_valid_file = TRUE;
3535 Warn("unsupported version of level file '%s'", filename);
3542 // pre-2.0 level files have no game version, so use file version here
3543 level->game_version = level->file_version;
3546 if (level->file_version < FILE_VERSION_1_2)
3548 // level files from versions before 1.2.0 without chunk structure
3549 LoadLevel_HEAD(file, LEVEL_CHUNK_HEAD_SIZE, level);
3550 LoadLevel_BODY(file, level->fieldx * level->fieldy, level);
3558 int (*loader)(File *, int, struct LevelInfo *);
3562 { "VERS", LEVEL_CHUNK_VERS_SIZE, LoadLevel_VERS },
3563 { "DATE", LEVEL_CHUNK_DATE_SIZE, LoadLevel_DATE },
3564 { "HEAD", LEVEL_CHUNK_HEAD_SIZE, LoadLevel_HEAD },
3565 { "NAME", LEVEL_CHUNK_NAME_SIZE, LoadLevel_NAME },
3566 { "AUTH", LEVEL_CHUNK_AUTH_SIZE, LoadLevel_AUTH },
3567 { "INFO", -1, LoadLevel_INFO },
3568 { "BODY", -1, LoadLevel_BODY },
3569 { "CONT", -1, LoadLevel_CONT },
3570 { "CNT2", LEVEL_CHUNK_CNT2_SIZE, LoadLevel_CNT2 },
3571 { "CNT3", -1, LoadLevel_CNT3 },
3572 { "CUS1", -1, LoadLevel_CUS1 },
3573 { "CUS2", -1, LoadLevel_CUS2 },
3574 { "CUS3", -1, LoadLevel_CUS3 },
3575 { "CUS4", -1, LoadLevel_CUS4 },
3576 { "GRP1", -1, LoadLevel_GRP1 },
3577 { "CONF", -1, LoadLevel_CONF },
3578 { "ELEM", -1, LoadLevel_ELEM },
3579 { "NOTE", -1, LoadLevel_NOTE },
3580 { "CUSX", -1, LoadLevel_CUSX },
3581 { "GRPX", -1, LoadLevel_GRPX },
3582 { "EMPX", -1, LoadLevel_EMPX },
3587 while (getFileChunkBE(file, chunk_name, &chunk_size))
3591 while (chunk_info[i].name != NULL &&
3592 !strEqual(chunk_name, chunk_info[i].name))
3595 if (chunk_info[i].name == NULL)
3597 Warn("unknown chunk '%s' in level file '%s'",
3598 chunk_name, filename);
3600 ReadUnusedBytesFromFile(file, chunk_size);
3602 else if (chunk_info[i].size != -1 &&
3603 chunk_info[i].size != chunk_size)
3605 Warn("wrong size (%d) of chunk '%s' in level file '%s'",
3606 chunk_size, chunk_name, filename);
3608 ReadUnusedBytesFromFile(file, chunk_size);
3612 // call function to load this level chunk
3613 int chunk_size_expected =
3614 (chunk_info[i].loader)(file, chunk_size, level);
3616 if (chunk_size_expected < 0)
3618 Warn("error reading chunk '%s' in level file '%s'",
3619 chunk_name, filename);
3624 // the size of some chunks cannot be checked before reading other
3625 // chunks first (like "HEAD" and "BODY") that contain some header
3626 // information, so check them here
3627 if (chunk_size_expected != chunk_size)
3629 Warn("wrong size (%d) of chunk '%s' in level file '%s'",
3630 chunk_size, chunk_name, filename);
3642 // ----------------------------------------------------------------------------
3643 // functions for loading EM level
3644 // ----------------------------------------------------------------------------
3646 static void CopyNativeLevel_RND_to_EM(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 cav->width = MIN(level->fieldx, MAX_PLAYFIELD_WIDTH);
3664 cav->height = MIN(level->fieldy, MAX_PLAYFIELD_HEIGHT);
3666 cav->time_seconds = level->time;
3667 cav->gems_needed = level->gems_needed;
3669 cav->emerald_score = level->score[SC_EMERALD];
3670 cav->diamond_score = level->score[SC_DIAMOND];
3671 cav->alien_score = level->score[SC_ROBOT];
3672 cav->tank_score = level->score[SC_SPACESHIP];
3673 cav->bug_score = level->score[SC_BUG];
3674 cav->eater_score = level->score[SC_YAMYAM];
3675 cav->nut_score = level->score[SC_NUT];
3676 cav->dynamite_score = level->score[SC_DYNAMITE];
3677 cav->key_score = level->score[SC_KEY];
3678 cav->exit_score = level->score[SC_TIME_BONUS];
3680 cav->num_eater_arrays = level->num_yamyam_contents;
3682 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
3683 for (y = 0; y < 3; y++)
3684 for (x = 0; x < 3; x++)
3685 cav->eater_array[i][y * 3 + x] =
3686 map_element_RND_to_EM_cave(level->yamyam_content[i].e[x][y]);
3688 cav->amoeba_time = level->amoeba_speed;
3689 cav->wonderwall_time = level->time_magic_wall;
3690 cav->wheel_time = level->time_wheel;
3692 cav->android_move_time = level->android_move_time;
3693 cav->android_clone_time = level->android_clone_time;
3694 cav->ball_random = level->ball_random;
3695 cav->ball_active = level->ball_active_initial;
3696 cav->ball_time = level->ball_time;
3697 cav->num_ball_arrays = level->num_ball_contents;
3699 cav->lenses_score = level->lenses_score;
3700 cav->magnify_score = level->magnify_score;
3701 cav->slurp_score = level->slurp_score;
3703 cav->lenses_time = level->lenses_time;
3704 cav->magnify_time = level->magnify_time;
3706 cav->wind_direction =
3707 map_direction_RND_to_EM(level->wind_direction_initial);
3709 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
3710 for (j = 0; j < 8; j++)
3711 cav->ball_array[i][j] =
3712 map_element_RND_to_EM_cave(level->ball_content[i].
3713 e[ball_xy[j][0]][ball_xy[j][1]]);
3715 map_android_clone_elements_RND_to_EM(level);
3717 // first fill the complete playfield with the empty space element
3718 for (y = 0; y < EM_MAX_CAVE_HEIGHT; y++)
3719 for (x = 0; x < EM_MAX_CAVE_WIDTH; x++)
3720 cav->cave[x][y] = Cblank;
3722 // then copy the real level contents from level file into the playfield
3723 for (y = 0; y < cav->height; y++) for (x = 0; x < cav->width; x++)
3725 int new_element = map_element_RND_to_EM_cave(level->field[x][y]);
3727 if (level->field[x][y] == EL_AMOEBA_DEAD)
3728 new_element = map_element_RND_to_EM_cave(EL_AMOEBA_WET);
3730 cav->cave[x][y] = new_element;
3733 for (i = 0; i < MAX_PLAYERS; i++)
3735 cav->player_x[i] = -1;
3736 cav->player_y[i] = -1;
3739 // initialize player positions and delete players from the playfield
3740 for (y = 0; y < cav->height; y++) for (x = 0; x < cav->width; x++)
3742 if (IS_PLAYER_ELEMENT(level->field[x][y]))
3744 int player_nr = GET_PLAYER_NR(level->field[x][y]);
3746 cav->player_x[player_nr] = x;
3747 cav->player_y[player_nr] = y;
3749 cav->cave[x][y] = map_element_RND_to_EM_cave(EL_EMPTY);
3754 static void CopyNativeLevel_EM_to_RND(struct LevelInfo *level)
3756 static int ball_xy[8][2] =
3767 struct LevelInfo_EM *level_em = level->native_em_level;
3768 struct CAVE *cav = level_em->cav;
3771 level->fieldx = MIN(cav->width, MAX_LEV_FIELDX);
3772 level->fieldy = MIN(cav->height, MAX_LEV_FIELDY);
3774 level->time = cav->time_seconds;
3775 level->gems_needed = cav->gems_needed;
3777 sprintf(level->name, "Level %d", level->file_info.nr);
3779 level->score[SC_EMERALD] = cav->emerald_score;
3780 level->score[SC_DIAMOND] = cav->diamond_score;
3781 level->score[SC_ROBOT] = cav->alien_score;
3782 level->score[SC_SPACESHIP] = cav->tank_score;
3783 level->score[SC_BUG] = cav->bug_score;
3784 level->score[SC_YAMYAM] = cav->eater_score;
3785 level->score[SC_NUT] = cav->nut_score;
3786 level->score[SC_DYNAMITE] = cav->dynamite_score;
3787 level->score[SC_KEY] = cav->key_score;
3788 level->score[SC_TIME_BONUS] = cav->exit_score;
3790 level->num_yamyam_contents = cav->num_eater_arrays;
3792 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
3793 for (y = 0; y < 3; y++)
3794 for (x = 0; x < 3; x++)
3795 level->yamyam_content[i].e[x][y] =
3796 map_element_EM_to_RND_cave(cav->eater_array[i][y * 3 + x]);
3798 level->amoeba_speed = cav->amoeba_time;
3799 level->time_magic_wall = cav->wonderwall_time;
3800 level->time_wheel = cav->wheel_time;
3802 level->android_move_time = cav->android_move_time;
3803 level->android_clone_time = cav->android_clone_time;
3804 level->ball_random = cav->ball_random;
3805 level->ball_active_initial = cav->ball_active;
3806 level->ball_time = cav->ball_time;
3807 level->num_ball_contents = cav->num_ball_arrays;
3809 level->lenses_score = cav->lenses_score;
3810 level->magnify_score = cav->magnify_score;
3811 level->slurp_score = cav->slurp_score;
3813 level->lenses_time = cav->lenses_time;
3814 level->magnify_time = cav->magnify_time;
3816 level->wind_direction_initial =
3817 map_direction_EM_to_RND(cav->wind_direction);
3819 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
3820 for (j = 0; j < 8; j++)
3821 level->ball_content[i].e[ball_xy[j][0]][ball_xy[j][1]] =
3822 map_element_EM_to_RND_cave(cav->ball_array[i][j]);
3824 map_android_clone_elements_EM_to_RND(level);
3826 // convert the playfield (some elements need special treatment)
3827 for (y = 0; y < level->fieldy; y++) for (x = 0; x < level->fieldx; x++)
3829 int new_element = map_element_EM_to_RND_cave(cav->cave[x][y]);
3831 if (new_element == EL_AMOEBA_WET && level->amoeba_speed == 0)
3832 new_element = EL_AMOEBA_DEAD;
3834 level->field[x][y] = new_element;
3837 for (i = 0; i < MAX_PLAYERS; i++)
3839 // in case of all players set to the same field, use the first player
3840 int nr = MAX_PLAYERS - i - 1;
3841 int jx = cav->player_x[nr];
3842 int jy = cav->player_y[nr];
3844 if (jx != -1 && jy != -1)
3845 level->field[jx][jy] = EL_PLAYER_1 + nr;
3848 // time score is counted for each 10 seconds left in Emerald Mine levels
3849 level->time_score_base = 10;
3853 // ----------------------------------------------------------------------------
3854 // functions for loading SP level
3855 // ----------------------------------------------------------------------------
3857 static void CopyNativeLevel_RND_to_SP(struct LevelInfo *level)
3859 struct LevelInfo_SP *level_sp = level->native_sp_level;
3860 LevelInfoType *header = &level_sp->header;
3863 level_sp->width = level->fieldx;
3864 level_sp->height = level->fieldy;
3866 for (x = 0; x < level->fieldx; x++)
3867 for (y = 0; y < level->fieldy; y++)
3868 level_sp->playfield[x][y] = map_element_RND_to_SP(level->field[x][y]);
3870 header->InitialGravity = (level->initial_player_gravity[0] ? 1 : 0);
3872 for (i = 0; i < SP_LEVEL_NAME_LEN; i++)
3873 header->LevelTitle[i] = level->name[i];
3874 // !!! NO STRING TERMINATION IN SUPAPLEX VB CODE YET -- FIX THIS !!!
3876 header->InfotronsNeeded = level->gems_needed;
3878 header->SpecialPortCount = 0;
3880 for (x = 0; x < level->fieldx; x++) for (y = 0; y < level->fieldy; y++)
3882 boolean gravity_port_found = FALSE;
3883 boolean gravity_port_valid = FALSE;
3884 int gravity_port_flag;
3885 int gravity_port_base_element;
3886 int element = level->field[x][y];
3888 if (element >= EL_SP_GRAVITY_ON_PORT_RIGHT &&
3889 element <= EL_SP_GRAVITY_ON_PORT_UP)
3891 gravity_port_found = TRUE;
3892 gravity_port_valid = TRUE;
3893 gravity_port_flag = 1;
3894 gravity_port_base_element = EL_SP_GRAVITY_ON_PORT_RIGHT;
3896 else if (element >= EL_SP_GRAVITY_OFF_PORT_RIGHT &&
3897 element <= EL_SP_GRAVITY_OFF_PORT_UP)
3899 gravity_port_found = TRUE;
3900 gravity_port_valid = TRUE;
3901 gravity_port_flag = 0;
3902 gravity_port_base_element = EL_SP_GRAVITY_OFF_PORT_RIGHT;
3904 else if (element >= EL_SP_GRAVITY_PORT_RIGHT &&
3905 element <= EL_SP_GRAVITY_PORT_UP)
3907 // change R'n'D style gravity inverting special port to normal port
3908 // (there are no gravity inverting ports in native Supaplex engine)
3910 gravity_port_found = TRUE;
3911 gravity_port_valid = FALSE;
3912 gravity_port_base_element = EL_SP_GRAVITY_PORT_RIGHT;
3915 if (gravity_port_found)
3917 if (gravity_port_valid &&
3918 header->SpecialPortCount < SP_MAX_SPECIAL_PORTS)
3920 SpecialPortType *port = &header->SpecialPort[header->SpecialPortCount];
3922 port->PortLocation = (y * level->fieldx + x) * 2;
3923 port->Gravity = gravity_port_flag;
3925 element += EL_SP_GRAVITY_PORT_RIGHT - gravity_port_base_element;
3927 header->SpecialPortCount++;
3931 // change special gravity port to normal port
3933 element += EL_SP_PORT_RIGHT - gravity_port_base_element;
3936 level_sp->playfield[x][y] = element - EL_SP_START;
3941 static void CopyNativeLevel_SP_to_RND(struct LevelInfo *level)
3943 struct LevelInfo_SP *level_sp = level->native_sp_level;
3944 LevelInfoType *header = &level_sp->header;
3945 boolean num_invalid_elements = 0;
3948 level->fieldx = level_sp->width;
3949 level->fieldy = level_sp->height;
3951 for (x = 0; x < level->fieldx; x++)
3953 for (y = 0; y < level->fieldy; y++)
3955 int element_old = level_sp->playfield[x][y];
3956 int element_new = getMappedElement(map_element_SP_to_RND(element_old));
3958 if (element_new == EL_UNKNOWN)
3960 num_invalid_elements++;
3962 Debug("level:native:SP", "invalid element %d at position %d, %d",
3966 level->field[x][y] = element_new;
3970 if (num_invalid_elements > 0)
3971 Warn("found %d invalid elements%s", num_invalid_elements,
3972 (!options.debug ? " (use '--debug' for more details)" : ""));
3974 for (i = 0; i < MAX_PLAYERS; i++)
3975 level->initial_player_gravity[i] =
3976 (header->InitialGravity == 1 ? TRUE : FALSE);
3978 // skip leading spaces
3979 for (i = 0; i < SP_LEVEL_NAME_LEN; i++)
3980 if (header->LevelTitle[i] != ' ')
3984 for (j = 0; i < SP_LEVEL_NAME_LEN; i++, j++)
3985 level->name[j] = header->LevelTitle[i];
3986 level->name[j] = '\0';
3988 // cut trailing spaces
3990 if (level->name[j - 1] == ' ' && level->name[j] == '\0')
3991 level->name[j - 1] = '\0';
3993 level->gems_needed = header->InfotronsNeeded;
3995 for (i = 0; i < header->SpecialPortCount; i++)
3997 SpecialPortType *port = &header->SpecialPort[i];
3998 int port_location = port->PortLocation;
3999 int gravity = port->Gravity;
4000 int port_x, port_y, port_element;
4002 port_x = (port_location / 2) % level->fieldx;
4003 port_y = (port_location / 2) / level->fieldx;
4005 if (port_x < 0 || port_x >= level->fieldx ||
4006 port_y < 0 || port_y >= level->fieldy)
4008 Warn("special port position (%d, %d) out of bounds", port_x, port_y);
4013 port_element = level->field[port_x][port_y];
4015 if (port_element < EL_SP_GRAVITY_PORT_RIGHT ||
4016 port_element > EL_SP_GRAVITY_PORT_UP)
4018 Warn("no special port at position (%d, %d)", port_x, port_y);
4023 // change previous (wrong) gravity inverting special port to either
4024 // gravity enabling special port or gravity disabling special port
4025 level->field[port_x][port_y] +=
4026 (gravity == 1 ? EL_SP_GRAVITY_ON_PORT_RIGHT :
4027 EL_SP_GRAVITY_OFF_PORT_RIGHT) - EL_SP_GRAVITY_PORT_RIGHT;
4030 // change special gravity ports without database entries to normal ports
4031 for (x = 0; x < level->fieldx; x++)
4032 for (y = 0; y < level->fieldy; y++)
4033 if (level->field[x][y] >= EL_SP_GRAVITY_PORT_RIGHT &&
4034 level->field[x][y] <= EL_SP_GRAVITY_PORT_UP)
4035 level->field[x][y] += EL_SP_PORT_RIGHT - EL_SP_GRAVITY_PORT_RIGHT;
4037 level->time = 0; // no time limit
4038 level->amoeba_speed = 0;
4039 level->time_magic_wall = 0;
4040 level->time_wheel = 0;
4041 level->amoeba_content = EL_EMPTY;
4043 // original Supaplex does not use score values -- rate by playing time
4044 for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
4045 level->score[i] = 0;
4047 level->rate_time_over_score = TRUE;
4049 // there are no yamyams in supaplex levels
4050 for (i = 0; i < level->num_yamyam_contents; i++)
4051 for (x = 0; x < 3; x++)
4052 for (y = 0; y < 3; y++)
4053 level->yamyam_content[i].e[x][y] = EL_EMPTY;
4056 static void CopyNativeTape_RND_to_SP(struct LevelInfo *level)
4058 struct LevelInfo_SP *level_sp = level->native_sp_level;
4059 struct DemoInfo_SP *demo = &level_sp->demo;
4062 // always start with reliable default values
4063 demo->is_available = FALSE;
4066 if (TAPE_IS_EMPTY(tape))
4069 demo->level_nr = tape.level_nr; // (currently not used)
4071 level_sp->header.DemoRandomSeed = tape.random_seed;
4075 for (i = 0; i < tape.length; i++)
4077 int demo_action = map_key_RND_to_SP(tape.pos[i].action[0]);
4078 int demo_repeat = tape.pos[i].delay;
4079 int demo_entries = (demo_repeat + 15) / 16;
4081 if (demo->length + demo_entries >= SP_MAX_TAPE_LEN)
4083 Warn("tape truncated: size exceeds maximum SP demo size %d",
4089 for (j = 0; j < demo_repeat / 16; j++)
4090 demo->data[demo->length++] = 0xf0 | demo_action;
4092 if (demo_repeat % 16)
4093 demo->data[demo->length++] = ((demo_repeat % 16 - 1) << 4) | demo_action;
4096 demo->is_available = TRUE;
4099 static void setTapeInfoToDefaults(void);
4101 static void CopyNativeTape_SP_to_RND(struct LevelInfo *level)
4103 struct LevelInfo_SP *level_sp = level->native_sp_level;
4104 struct DemoInfo_SP *demo = &level_sp->demo;
4105 char *filename = level->file_info.filename;
4108 // always start with reliable default values
4109 setTapeInfoToDefaults();
4111 if (!demo->is_available)
4114 tape.level_nr = demo->level_nr; // (currently not used)
4115 tape.random_seed = level_sp->header.DemoRandomSeed;
4117 TapeSetDateFromEpochSeconds(getFileTimestampEpochSeconds(filename));
4120 tape.pos[tape.counter].delay = 0;
4122 for (i = 0; i < demo->length; i++)
4124 int demo_action = demo->data[i] & 0x0f;
4125 int demo_repeat = (demo->data[i] & 0xf0) >> 4;
4126 int tape_action = map_key_SP_to_RND(demo_action);
4127 int tape_repeat = demo_repeat + 1;
4128 byte action[MAX_TAPE_ACTIONS] = { tape_action };
4129 boolean success = 0;
4132 for (j = 0; j < tape_repeat; j++)
4133 success = TapeAddAction(action);
4137 Warn("SP demo truncated: size exceeds maximum tape size %d",
4144 TapeHaltRecording();
4148 // ----------------------------------------------------------------------------
4149 // functions for loading MM level
4150 // ----------------------------------------------------------------------------
4152 static void CopyNativeLevel_RND_to_MM(struct LevelInfo *level)
4154 struct LevelInfo_MM *level_mm = level->native_mm_level;
4157 level_mm->fieldx = MIN(level->fieldx, MM_MAX_PLAYFIELD_WIDTH);
4158 level_mm->fieldy = MIN(level->fieldy, MM_MAX_PLAYFIELD_HEIGHT);
4160 level_mm->time = level->time;
4161 level_mm->kettles_needed = level->gems_needed;
4162 level_mm->auto_count_kettles = level->auto_count_gems;
4164 level_mm->mm_laser_red = level->mm_laser_red;
4165 level_mm->mm_laser_green = level->mm_laser_green;
4166 level_mm->mm_laser_blue = level->mm_laser_blue;
4168 level_mm->df_laser_red = level->df_laser_red;
4169 level_mm->df_laser_green = level->df_laser_green;
4170 level_mm->df_laser_blue = level->df_laser_blue;
4172 strcpy(level_mm->name, level->name);
4173 strcpy(level_mm->author, level->author);
4175 level_mm->score[SC_EMERALD] = level->score[SC_EMERALD];
4176 level_mm->score[SC_PACMAN] = level->score[SC_PACMAN];
4177 level_mm->score[SC_KEY] = level->score[SC_KEY];
4178 level_mm->score[SC_TIME_BONUS] = level->score[SC_TIME_BONUS];
4179 level_mm->score[SC_ELEM_BONUS] = level->score[SC_ELEM_BONUS];
4181 level_mm->amoeba_speed = level->amoeba_speed;
4182 level_mm->time_fuse = level->mm_time_fuse;
4183 level_mm->time_bomb = level->mm_time_bomb;
4184 level_mm->time_ball = level->mm_time_ball;
4185 level_mm->time_block = level->mm_time_block;
4187 level_mm->num_ball_contents = level->num_mm_ball_contents;
4188 level_mm->ball_choice_mode = level->mm_ball_choice_mode;
4190 for (i = 0; i < level->num_mm_ball_contents; i++)
4191 level_mm->ball_content[i] =
4192 map_element_RND_to_MM(level->mm_ball_content[i]);
4194 for (x = 0; x < level->fieldx; x++)
4195 for (y = 0; y < level->fieldy; y++)
4197 level_mm->field[x][y] = map_element_RND_to_MM(level->field[x][y]);
4200 static void CopyNativeLevel_MM_to_RND(struct LevelInfo *level)
4202 struct LevelInfo_MM *level_mm = level->native_mm_level;
4205 level->fieldx = MIN(level_mm->fieldx, MAX_LEV_FIELDX);
4206 level->fieldy = MIN(level_mm->fieldy, MAX_LEV_FIELDY);
4208 level->time = level_mm->time;
4209 level->gems_needed = level_mm->kettles_needed;
4210 level->auto_count_gems = level_mm->auto_count_kettles;
4212 level->mm_laser_red = level_mm->mm_laser_red;
4213 level->mm_laser_green = level_mm->mm_laser_green;
4214 level->mm_laser_blue = level_mm->mm_laser_blue;
4216 level->df_laser_red = level_mm->df_laser_red;
4217 level->df_laser_green = level_mm->df_laser_green;
4218 level->df_laser_blue = level_mm->df_laser_blue;
4220 strcpy(level->name, level_mm->name);
4222 // only overwrite author from 'levelinfo.conf' if author defined in level
4223 if (!strEqual(level_mm->author, ANONYMOUS_NAME))
4224 strcpy(level->author, level_mm->author);
4226 level->score[SC_EMERALD] = level_mm->score[SC_EMERALD];
4227 level->score[SC_PACMAN] = level_mm->score[SC_PACMAN];
4228 level->score[SC_KEY] = level_mm->score[SC_KEY];
4229 level->score[SC_TIME_BONUS] = level_mm->score[SC_TIME_BONUS];
4230 level->score[SC_ELEM_BONUS] = level_mm->score[SC_ELEM_BONUS];
4232 level->amoeba_speed = level_mm->amoeba_speed;
4233 level->mm_time_fuse = level_mm->time_fuse;
4234 level->mm_time_bomb = level_mm->time_bomb;
4235 level->mm_time_ball = level_mm->time_ball;
4236 level->mm_time_block = level_mm->time_block;
4238 level->num_mm_ball_contents = level_mm->num_ball_contents;
4239 level->mm_ball_choice_mode = level_mm->ball_choice_mode;
4241 for (i = 0; i < level->num_mm_ball_contents; i++)
4242 level->mm_ball_content[i] =
4243 map_element_MM_to_RND(level_mm->ball_content[i]);
4245 for (x = 0; x < level->fieldx; x++)
4246 for (y = 0; y < level->fieldy; y++)
4247 level->field[x][y] = map_element_MM_to_RND(level_mm->field[x][y]);
4251 // ----------------------------------------------------------------------------
4252 // functions for loading DC level
4253 // ----------------------------------------------------------------------------
4255 #define DC_LEVEL_HEADER_SIZE 344
4257 static unsigned short getDecodedWord_DC(unsigned short data_encoded,
4260 static int last_data_encoded;
4264 int diff_hi, diff_lo;
4265 int data_hi, data_lo;
4266 unsigned short data_decoded;
4270 last_data_encoded = 0;
4277 diff = data_encoded - last_data_encoded;
4278 diff_hi = diff & ~0xff;
4279 diff_lo = diff & 0xff;
4283 data_hi = diff_hi - (offset1 << 8) + (offset2 & 0xff00);
4284 data_lo = (diff_lo + (data_hi >> 16)) & 0x00ff;
4285 data_hi = data_hi & 0xff00;
4287 data_decoded = data_hi | data_lo;
4289 last_data_encoded = data_encoded;
4291 offset1 = (offset1 + 1) % 31;
4292 offset2 = offset2 & 0xff;
4294 return data_decoded;
4297 static int getMappedElement_DC(int element)
4305 // 0x0117 - 0x036e: (?)
4308 // 0x042d - 0x0684: (?)
4324 element = EL_CRYSTAL;
4327 case 0x0e77: // quicksand (boulder)
4328 element = EL_QUICKSAND_FAST_FULL;
4331 case 0x0e99: // slow quicksand (boulder)
4332 element = EL_QUICKSAND_FULL;
4336 element = EL_EM_EXIT_OPEN;
4340 element = EL_EM_EXIT_CLOSED;
4344 element = EL_EM_STEEL_EXIT_OPEN;
4348 element = EL_EM_STEEL_EXIT_CLOSED;
4351 case 0x0f4f: // dynamite (lit 1)
4352 element = EL_EM_DYNAMITE_ACTIVE;
4355 case 0x0f57: // dynamite (lit 2)
4356 element = EL_EM_DYNAMITE_ACTIVE;
4359 case 0x0f5f: // dynamite (lit 3)
4360 element = EL_EM_DYNAMITE_ACTIVE;
4363 case 0x0f67: // dynamite (lit 4)
4364 element = EL_EM_DYNAMITE_ACTIVE;
4371 element = EL_AMOEBA_WET;
4375 element = EL_AMOEBA_DROP;
4379 element = EL_DC_MAGIC_WALL;
4383 element = EL_SPACESHIP_UP;
4387 element = EL_SPACESHIP_DOWN;
4391 element = EL_SPACESHIP_LEFT;
4395 element = EL_SPACESHIP_RIGHT;
4399 element = EL_BUG_UP;
4403 element = EL_BUG_DOWN;
4407 element = EL_BUG_LEFT;
4411 element = EL_BUG_RIGHT;
4415 element = EL_MOLE_UP;
4419 element = EL_MOLE_DOWN;
4423 element = EL_MOLE_LEFT;
4427 element = EL_MOLE_RIGHT;
4435 element = EL_YAMYAM_UP;
4439 element = EL_SWITCHGATE_OPEN;
4443 element = EL_SWITCHGATE_CLOSED;
4447 element = EL_DC_SWITCHGATE_SWITCH_UP;
4451 element = EL_TIMEGATE_CLOSED;
4454 case 0x144c: // conveyor belt switch (green)
4455 element = EL_CONVEYOR_BELT_3_SWITCH_MIDDLE;
4458 case 0x144f: // conveyor belt switch (red)
4459 element = EL_CONVEYOR_BELT_1_SWITCH_MIDDLE;
4462 case 0x1452: // conveyor belt switch (blue)
4463 element = EL_CONVEYOR_BELT_4_SWITCH_MIDDLE;
4467 element = EL_CONVEYOR_BELT_3_MIDDLE;
4471 element = EL_CONVEYOR_BELT_3_LEFT;
4475 element = EL_CONVEYOR_BELT_3_RIGHT;
4479 element = EL_CONVEYOR_BELT_1_MIDDLE;
4483 element = EL_CONVEYOR_BELT_1_LEFT;
4487 element = EL_CONVEYOR_BELT_1_RIGHT;
4491 element = EL_CONVEYOR_BELT_4_MIDDLE;
4495 element = EL_CONVEYOR_BELT_4_LEFT;
4499 element = EL_CONVEYOR_BELT_4_RIGHT;
4503 element = EL_EXPANDABLE_WALL_HORIZONTAL;
4507 element = EL_EXPANDABLE_WALL_VERTICAL;
4511 element = EL_EXPANDABLE_WALL_ANY;
4514 case 0x14ce: // growing steel wall (left/right)
4515 element = EL_EXPANDABLE_STEELWALL_HORIZONTAL;
4518 case 0x14df: // growing steel wall (up/down)
4519 element = EL_EXPANDABLE_STEELWALL_VERTICAL;
4522 case 0x14e8: // growing steel wall (up/down/left/right)
4523 element = EL_EXPANDABLE_STEELWALL_ANY;
4527 element = EL_SHIELD_DEADLY;
4531 element = EL_EXTRA_TIME;
4539 element = EL_EMPTY_SPACE;
4542 case 0x1578: // quicksand (empty)
4543 element = EL_QUICKSAND_FAST_EMPTY;
4546 case 0x1579: // slow quicksand (empty)
4547 element = EL_QUICKSAND_EMPTY;
4557 element = EL_EM_DYNAMITE;
4560 case 0x15a1: // key (red)
4561 element = EL_EM_KEY_1;
4564 case 0x15a2: // key (yellow)
4565 element = EL_EM_KEY_2;
4568 case 0x15a3: // key (blue)
4569 element = EL_EM_KEY_4;
4572 case 0x15a4: // key (green)
4573 element = EL_EM_KEY_3;
4576 case 0x15a5: // key (white)
4577 element = EL_DC_KEY_WHITE;
4581 element = EL_WALL_SLIPPERY;
4588 case 0x15a8: // wall (not round)
4592 case 0x15a9: // (blue)
4593 element = EL_CHAR_A;
4596 case 0x15aa: // (blue)
4597 element = EL_CHAR_B;
4600 case 0x15ab: // (blue)
4601 element = EL_CHAR_C;
4604 case 0x15ac: // (blue)
4605 element = EL_CHAR_D;
4608 case 0x15ad: // (blue)
4609 element = EL_CHAR_E;
4612 case 0x15ae: // (blue)
4613 element = EL_CHAR_F;
4616 case 0x15af: // (blue)
4617 element = EL_CHAR_G;
4620 case 0x15b0: // (blue)
4621 element = EL_CHAR_H;
4624 case 0x15b1: // (blue)
4625 element = EL_CHAR_I;
4628 case 0x15b2: // (blue)
4629 element = EL_CHAR_J;
4632 case 0x15b3: // (blue)
4633 element = EL_CHAR_K;
4636 case 0x15b4: // (blue)
4637 element = EL_CHAR_L;
4640 case 0x15b5: // (blue)
4641 element = EL_CHAR_M;
4644 case 0x15b6: // (blue)
4645 element = EL_CHAR_N;
4648 case 0x15b7: // (blue)
4649 element = EL_CHAR_O;
4652 case 0x15b8: // (blue)
4653 element = EL_CHAR_P;
4656 case 0x15b9: // (blue)
4657 element = EL_CHAR_Q;
4660 case 0x15ba: // (blue)
4661 element = EL_CHAR_R;
4664 case 0x15bb: // (blue)
4665 element = EL_CHAR_S;
4668 case 0x15bc: // (blue)
4669 element = EL_CHAR_T;
4672 case 0x15bd: // (blue)
4673 element = EL_CHAR_U;
4676 case 0x15be: // (blue)
4677 element = EL_CHAR_V;
4680 case 0x15bf: // (blue)
4681 element = EL_CHAR_W;
4684 case 0x15c0: // (blue)
4685 element = EL_CHAR_X;
4688 case 0x15c1: // (blue)
4689 element = EL_CHAR_Y;
4692 case 0x15c2: // (blue)
4693 element = EL_CHAR_Z;
4696 case 0x15c3: // (blue)
4697 element = EL_CHAR_AUMLAUT;
4700 case 0x15c4: // (blue)
4701 element = EL_CHAR_OUMLAUT;
4704 case 0x15c5: // (blue)
4705 element = EL_CHAR_UUMLAUT;
4708 case 0x15c6: // (blue)
4709 element = EL_CHAR_0;
4712 case 0x15c7: // (blue)
4713 element = EL_CHAR_1;
4716 case 0x15c8: // (blue)
4717 element = EL_CHAR_2;
4720 case 0x15c9: // (blue)
4721 element = EL_CHAR_3;
4724 case 0x15ca: // (blue)
4725 element = EL_CHAR_4;
4728 case 0x15cb: // (blue)
4729 element = EL_CHAR_5;
4732 case 0x15cc: // (blue)
4733 element = EL_CHAR_6;
4736 case 0x15cd: // (blue)
4737 element = EL_CHAR_7;
4740 case 0x15ce: // (blue)
4741 element = EL_CHAR_8;
4744 case 0x15cf: // (blue)
4745 element = EL_CHAR_9;
4748 case 0x15d0: // (blue)
4749 element = EL_CHAR_PERIOD;
4752 case 0x15d1: // (blue)
4753 element = EL_CHAR_EXCLAM;
4756 case 0x15d2: // (blue)
4757 element = EL_CHAR_COLON;
4760 case 0x15d3: // (blue)
4761 element = EL_CHAR_LESS;
4764 case 0x15d4: // (blue)
4765 element = EL_CHAR_GREATER;
4768 case 0x15d5: // (blue)
4769 element = EL_CHAR_QUESTION;
4772 case 0x15d6: // (blue)
4773 element = EL_CHAR_COPYRIGHT;
4776 case 0x15d7: // (blue)
4777 element = EL_CHAR_UP;
4780 case 0x15d8: // (blue)
4781 element = EL_CHAR_DOWN;
4784 case 0x15d9: // (blue)
4785 element = EL_CHAR_BUTTON;
4788 case 0x15da: // (blue)
4789 element = EL_CHAR_PLUS;
4792 case 0x15db: // (blue)
4793 element = EL_CHAR_MINUS;
4796 case 0x15dc: // (blue)
4797 element = EL_CHAR_APOSTROPHE;
4800 case 0x15dd: // (blue)
4801 element = EL_CHAR_PARENLEFT;
4804 case 0x15de: // (blue)
4805 element = EL_CHAR_PARENRIGHT;
4808 case 0x15df: // (green)
4809 element = EL_CHAR_A;
4812 case 0x15e0: // (green)
4813 element = EL_CHAR_B;
4816 case 0x15e1: // (green)
4817 element = EL_CHAR_C;
4820 case 0x15e2: // (green)
4821 element = EL_CHAR_D;
4824 case 0x15e3: // (green)
4825 element = EL_CHAR_E;
4828 case 0x15e4: // (green)
4829 element = EL_CHAR_F;
4832 case 0x15e5: // (green)
4833 element = EL_CHAR_G;
4836 case 0x15e6: // (green)
4837 element = EL_CHAR_H;
4840 case 0x15e7: // (green)
4841 element = EL_CHAR_I;
4844 case 0x15e8: // (green)
4845 element = EL_CHAR_J;
4848 case 0x15e9: // (green)
4849 element = EL_CHAR_K;
4852 case 0x15ea: // (green)
4853 element = EL_CHAR_L;
4856 case 0x15eb: // (green)
4857 element = EL_CHAR_M;
4860 case 0x15ec: // (green)
4861 element = EL_CHAR_N;
4864 case 0x15ed: // (green)
4865 element = EL_CHAR_O;
4868 case 0x15ee: // (green)
4869 element = EL_CHAR_P;
4872 case 0x15ef: // (green)
4873 element = EL_CHAR_Q;
4876 case 0x15f0: // (green)
4877 element = EL_CHAR_R;
4880 case 0x15f1: // (green)
4881 element = EL_CHAR_S;
4884 case 0x15f2: // (green)
4885 element = EL_CHAR_T;
4888 case 0x15f3: // (green)
4889 element = EL_CHAR_U;
4892 case 0x15f4: // (green)
4893 element = EL_CHAR_V;
4896 case 0x15f5: // (green)
4897 element = EL_CHAR_W;
4900 case 0x15f6: // (green)
4901 element = EL_CHAR_X;
4904 case 0x15f7: // (green)
4905 element = EL_CHAR_Y;
4908 case 0x15f8: // (green)
4909 element = EL_CHAR_Z;
4912 case 0x15f9: // (green)
4913 element = EL_CHAR_AUMLAUT;
4916 case 0x15fa: // (green)
4917 element = EL_CHAR_OUMLAUT;
4920 case 0x15fb: // (green)
4921 element = EL_CHAR_UUMLAUT;
4924 case 0x15fc: // (green)
4925 element = EL_CHAR_0;
4928 case 0x15fd: // (green)
4929 element = EL_CHAR_1;
4932 case 0x15fe: // (green)
4933 element = EL_CHAR_2;
4936 case 0x15ff: // (green)
4937 element = EL_CHAR_3;
4940 case 0x1600: // (green)
4941 element = EL_CHAR_4;
4944 case 0x1601: // (green)
4945 element = EL_CHAR_5;
4948 case 0x1602: // (green)
4949 element = EL_CHAR_6;
4952 case 0x1603: // (green)
4953 element = EL_CHAR_7;
4956 case 0x1604: // (green)
4957 element = EL_CHAR_8;
4960 case 0x1605: // (green)
4961 element = EL_CHAR_9;
4964 case 0x1606: // (green)
4965 element = EL_CHAR_PERIOD;
4968 case 0x1607: // (green)
4969 element = EL_CHAR_EXCLAM;
4972 case 0x1608: // (green)
4973 element = EL_CHAR_COLON;
4976 case 0x1609: // (green)
4977 element = EL_CHAR_LESS;
4980 case 0x160a: // (green)
4981 element = EL_CHAR_GREATER;
4984 case 0x160b: // (green)
4985 element = EL_CHAR_QUESTION;
4988 case 0x160c: // (green)
4989 element = EL_CHAR_COPYRIGHT;
4992 case 0x160d: // (green)
4993 element = EL_CHAR_UP;
4996 case 0x160e: // (green)
4997 element = EL_CHAR_DOWN;
5000 case 0x160f: // (green)
5001 element = EL_CHAR_BUTTON;
5004 case 0x1610: // (green)
5005 element = EL_CHAR_PLUS;
5008 case 0x1611: // (green)
5009 element = EL_CHAR_MINUS;
5012 case 0x1612: // (green)
5013 element = EL_CHAR_APOSTROPHE;
5016 case 0x1613: // (green)
5017 element = EL_CHAR_PARENLEFT;
5020 case 0x1614: // (green)
5021 element = EL_CHAR_PARENRIGHT;
5024 case 0x1615: // (blue steel)
5025 element = EL_STEEL_CHAR_A;
5028 case 0x1616: // (blue steel)
5029 element = EL_STEEL_CHAR_B;
5032 case 0x1617: // (blue steel)
5033 element = EL_STEEL_CHAR_C;
5036 case 0x1618: // (blue steel)
5037 element = EL_STEEL_CHAR_D;
5040 case 0x1619: // (blue steel)
5041 element = EL_STEEL_CHAR_E;
5044 case 0x161a: // (blue steel)
5045 element = EL_STEEL_CHAR_F;
5048 case 0x161b: // (blue steel)
5049 element = EL_STEEL_CHAR_G;
5052 case 0x161c: // (blue steel)
5053 element = EL_STEEL_CHAR_H;
5056 case 0x161d: // (blue steel)
5057 element = EL_STEEL_CHAR_I;
5060 case 0x161e: // (blue steel)
5061 element = EL_STEEL_CHAR_J;
5064 case 0x161f: // (blue steel)
5065 element = EL_STEEL_CHAR_K;
5068 case 0x1620: // (blue steel)
5069 element = EL_STEEL_CHAR_L;
5072 case 0x1621: // (blue steel)
5073 element = EL_STEEL_CHAR_M;
5076 case 0x1622: // (blue steel)
5077 element = EL_STEEL_CHAR_N;
5080 case 0x1623: // (blue steel)
5081 element = EL_STEEL_CHAR_O;
5084 case 0x1624: // (blue steel)
5085 element = EL_STEEL_CHAR_P;
5088 case 0x1625: // (blue steel)
5089 element = EL_STEEL_CHAR_Q;
5092 case 0x1626: // (blue steel)
5093 element = EL_STEEL_CHAR_R;
5096 case 0x1627: // (blue steel)
5097 element = EL_STEEL_CHAR_S;
5100 case 0x1628: // (blue steel)
5101 element = EL_STEEL_CHAR_T;
5104 case 0x1629: // (blue steel)
5105 element = EL_STEEL_CHAR_U;
5108 case 0x162a: // (blue steel)
5109 element = EL_STEEL_CHAR_V;
5112 case 0x162b: // (blue steel)
5113 element = EL_STEEL_CHAR_W;
5116 case 0x162c: // (blue steel)
5117 element = EL_STEEL_CHAR_X;
5120 case 0x162d: // (blue steel)
5121 element = EL_STEEL_CHAR_Y;
5124 case 0x162e: // (blue steel)
5125 element = EL_STEEL_CHAR_Z;
5128 case 0x162f: // (blue steel)
5129 element = EL_STEEL_CHAR_AUMLAUT;
5132 case 0x1630: // (blue steel)
5133 element = EL_STEEL_CHAR_OUMLAUT;
5136 case 0x1631: // (blue steel)
5137 element = EL_STEEL_CHAR_UUMLAUT;
5140 case 0x1632: // (blue steel)
5141 element = EL_STEEL_CHAR_0;
5144 case 0x1633: // (blue steel)
5145 element = EL_STEEL_CHAR_1;
5148 case 0x1634: // (blue steel)
5149 element = EL_STEEL_CHAR_2;
5152 case 0x1635: // (blue steel)
5153 element = EL_STEEL_CHAR_3;
5156 case 0x1636: // (blue steel)
5157 element = EL_STEEL_CHAR_4;
5160 case 0x1637: // (blue steel)
5161 element = EL_STEEL_CHAR_5;
5164 case 0x1638: // (blue steel)
5165 element = EL_STEEL_CHAR_6;
5168 case 0x1639: // (blue steel)
5169 element = EL_STEEL_CHAR_7;
5172 case 0x163a: // (blue steel)
5173 element = EL_STEEL_CHAR_8;
5176 case 0x163b: // (blue steel)
5177 element = EL_STEEL_CHAR_9;
5180 case 0x163c: // (blue steel)
5181 element = EL_STEEL_CHAR_PERIOD;
5184 case 0x163d: // (blue steel)
5185 element = EL_STEEL_CHAR_EXCLAM;
5188 case 0x163e: // (blue steel)
5189 element = EL_STEEL_CHAR_COLON;
5192 case 0x163f: // (blue steel)
5193 element = EL_STEEL_CHAR_LESS;
5196 case 0x1640: // (blue steel)
5197 element = EL_STEEL_CHAR_GREATER;
5200 case 0x1641: // (blue steel)
5201 element = EL_STEEL_CHAR_QUESTION;
5204 case 0x1642: // (blue steel)
5205 element = EL_STEEL_CHAR_COPYRIGHT;
5208 case 0x1643: // (blue steel)
5209 element = EL_STEEL_CHAR_UP;
5212 case 0x1644: // (blue steel)
5213 element = EL_STEEL_CHAR_DOWN;
5216 case 0x1645: // (blue steel)
5217 element = EL_STEEL_CHAR_BUTTON;
5220 case 0x1646: // (blue steel)
5221 element = EL_STEEL_CHAR_PLUS;
5224 case 0x1647: // (blue steel)
5225 element = EL_STEEL_CHAR_MINUS;
5228 case 0x1648: // (blue steel)
5229 element = EL_STEEL_CHAR_APOSTROPHE;
5232 case 0x1649: // (blue steel)
5233 element = EL_STEEL_CHAR_PARENLEFT;
5236 case 0x164a: // (blue steel)
5237 element = EL_STEEL_CHAR_PARENRIGHT;
5240 case 0x164b: // (green steel)
5241 element = EL_STEEL_CHAR_A;
5244 case 0x164c: // (green steel)
5245 element = EL_STEEL_CHAR_B;
5248 case 0x164d: // (green steel)
5249 element = EL_STEEL_CHAR_C;
5252 case 0x164e: // (green steel)
5253 element = EL_STEEL_CHAR_D;
5256 case 0x164f: // (green steel)
5257 element = EL_STEEL_CHAR_E;
5260 case 0x1650: // (green steel)
5261 element = EL_STEEL_CHAR_F;
5264 case 0x1651: // (green steel)
5265 element = EL_STEEL_CHAR_G;
5268 case 0x1652: // (green steel)
5269 element = EL_STEEL_CHAR_H;
5272 case 0x1653: // (green steel)
5273 element = EL_STEEL_CHAR_I;
5276 case 0x1654: // (green steel)
5277 element = EL_STEEL_CHAR_J;
5280 case 0x1655: // (green steel)
5281 element = EL_STEEL_CHAR_K;
5284 case 0x1656: // (green steel)
5285 element = EL_STEEL_CHAR_L;
5288 case 0x1657: // (green steel)
5289 element = EL_STEEL_CHAR_M;
5292 case 0x1658: // (green steel)
5293 element = EL_STEEL_CHAR_N;
5296 case 0x1659: // (green steel)
5297 element = EL_STEEL_CHAR_O;
5300 case 0x165a: // (green steel)
5301 element = EL_STEEL_CHAR_P;
5304 case 0x165b: // (green steel)
5305 element = EL_STEEL_CHAR_Q;
5308 case 0x165c: // (green steel)
5309 element = EL_STEEL_CHAR_R;
5312 case 0x165d: // (green steel)
5313 element = EL_STEEL_CHAR_S;
5316 case 0x165e: // (green steel)
5317 element = EL_STEEL_CHAR_T;
5320 case 0x165f: // (green steel)
5321 element = EL_STEEL_CHAR_U;
5324 case 0x1660: // (green steel)
5325 element = EL_STEEL_CHAR_V;
5328 case 0x1661: // (green steel)
5329 element = EL_STEEL_CHAR_W;
5332 case 0x1662: // (green steel)
5333 element = EL_STEEL_CHAR_X;
5336 case 0x1663: // (green steel)
5337 element = EL_STEEL_CHAR_Y;
5340 case 0x1664: // (green steel)
5341 element = EL_STEEL_CHAR_Z;
5344 case 0x1665: // (green steel)
5345 element = EL_STEEL_CHAR_AUMLAUT;
5348 case 0x1666: // (green steel)
5349 element = EL_STEEL_CHAR_OUMLAUT;
5352 case 0x1667: // (green steel)
5353 element = EL_STEEL_CHAR_UUMLAUT;
5356 case 0x1668: // (green steel)
5357 element = EL_STEEL_CHAR_0;
5360 case 0x1669: // (green steel)
5361 element = EL_STEEL_CHAR_1;
5364 case 0x166a: // (green steel)
5365 element = EL_STEEL_CHAR_2;
5368 case 0x166b: // (green steel)
5369 element = EL_STEEL_CHAR_3;
5372 case 0x166c: // (green steel)
5373 element = EL_STEEL_CHAR_4;
5376 case 0x166d: // (green steel)
5377 element = EL_STEEL_CHAR_5;
5380 case 0x166e: // (green steel)
5381 element = EL_STEEL_CHAR_6;
5384 case 0x166f: // (green steel)
5385 element = EL_STEEL_CHAR_7;
5388 case 0x1670: // (green steel)
5389 element = EL_STEEL_CHAR_8;
5392 case 0x1671: // (green steel)
5393 element = EL_STEEL_CHAR_9;
5396 case 0x1672: // (green steel)
5397 element = EL_STEEL_CHAR_PERIOD;
5400 case 0x1673: // (green steel)
5401 element = EL_STEEL_CHAR_EXCLAM;
5404 case 0x1674: // (green steel)
5405 element = EL_STEEL_CHAR_COLON;
5408 case 0x1675: // (green steel)
5409 element = EL_STEEL_CHAR_LESS;
5412 case 0x1676: // (green steel)
5413 element = EL_STEEL_CHAR_GREATER;
5416 case 0x1677: // (green steel)
5417 element = EL_STEEL_CHAR_QUESTION;
5420 case 0x1678: // (green steel)
5421 element = EL_STEEL_CHAR_COPYRIGHT;
5424 case 0x1679: // (green steel)
5425 element = EL_STEEL_CHAR_UP;
5428 case 0x167a: // (green steel)
5429 element = EL_STEEL_CHAR_DOWN;
5432 case 0x167b: // (green steel)
5433 element = EL_STEEL_CHAR_BUTTON;
5436 case 0x167c: // (green steel)
5437 element = EL_STEEL_CHAR_PLUS;
5440 case 0x167d: // (green steel)
5441 element = EL_STEEL_CHAR_MINUS;
5444 case 0x167e: // (green steel)
5445 element = EL_STEEL_CHAR_APOSTROPHE;
5448 case 0x167f: // (green steel)
5449 element = EL_STEEL_CHAR_PARENLEFT;
5452 case 0x1680: // (green steel)
5453 element = EL_STEEL_CHAR_PARENRIGHT;
5456 case 0x1681: // gate (red)
5457 element = EL_EM_GATE_1;
5460 case 0x1682: // secret gate (red)
5461 element = EL_EM_GATE_1_GRAY;
5464 case 0x1683: // gate (yellow)
5465 element = EL_EM_GATE_2;
5468 case 0x1684: // secret gate (yellow)
5469 element = EL_EM_GATE_2_GRAY;
5472 case 0x1685: // gate (blue)
5473 element = EL_EM_GATE_4;
5476 case 0x1686: // secret gate (blue)
5477 element = EL_EM_GATE_4_GRAY;
5480 case 0x1687: // gate (green)
5481 element = EL_EM_GATE_3;
5484 case 0x1688: // secret gate (green)
5485 element = EL_EM_GATE_3_GRAY;
5488 case 0x1689: // gate (white)
5489 element = EL_DC_GATE_WHITE;
5492 case 0x168a: // secret gate (white)
5493 element = EL_DC_GATE_WHITE_GRAY;
5496 case 0x168b: // secret gate (no key)
5497 element = EL_DC_GATE_FAKE_GRAY;
5501 element = EL_ROBOT_WHEEL;
5505 element = EL_DC_TIMEGATE_SWITCH;
5509 element = EL_ACID_POOL_BOTTOM;
5513 element = EL_ACID_POOL_TOPLEFT;
5517 element = EL_ACID_POOL_TOPRIGHT;
5521 element = EL_ACID_POOL_BOTTOMLEFT;
5525 element = EL_ACID_POOL_BOTTOMRIGHT;
5529 element = EL_STEELWALL;
5533 element = EL_STEELWALL_SLIPPERY;
5536 case 0x1695: // steel wall (not round)
5537 element = EL_STEELWALL;
5540 case 0x1696: // steel wall (left)
5541 element = EL_DC_STEELWALL_1_LEFT;
5544 case 0x1697: // steel wall (bottom)
5545 element = EL_DC_STEELWALL_1_BOTTOM;
5548 case 0x1698: // steel wall (right)
5549 element = EL_DC_STEELWALL_1_RIGHT;
5552 case 0x1699: // steel wall (top)
5553 element = EL_DC_STEELWALL_1_TOP;
5556 case 0x169a: // steel wall (left/bottom)
5557 element = EL_DC_STEELWALL_1_BOTTOMLEFT;
5560 case 0x169b: // steel wall (right/bottom)
5561 element = EL_DC_STEELWALL_1_BOTTOMRIGHT;
5564 case 0x169c: // steel wall (right/top)
5565 element = EL_DC_STEELWALL_1_TOPRIGHT;
5568 case 0x169d: // steel wall (left/top)
5569 element = EL_DC_STEELWALL_1_TOPLEFT;
5572 case 0x169e: // steel wall (right/bottom small)
5573 element = EL_DC_STEELWALL_1_BOTTOMRIGHT_2;
5576 case 0x169f: // steel wall (left/bottom small)
5577 element = EL_DC_STEELWALL_1_BOTTOMLEFT_2;
5580 case 0x16a0: // steel wall (right/top small)
5581 element = EL_DC_STEELWALL_1_TOPRIGHT_2;
5584 case 0x16a1: // steel wall (left/top small)
5585 element = EL_DC_STEELWALL_1_TOPLEFT_2;
5588 case 0x16a2: // steel wall (left/right)
5589 element = EL_DC_STEELWALL_1_VERTICAL;
5592 case 0x16a3: // steel wall (top/bottom)
5593 element = EL_DC_STEELWALL_1_HORIZONTAL;
5596 case 0x16a4: // steel wall 2 (left end)
5597 element = EL_DC_STEELWALL_2_LEFT;
5600 case 0x16a5: // steel wall 2 (right end)
5601 element = EL_DC_STEELWALL_2_RIGHT;
5604 case 0x16a6: // steel wall 2 (top end)
5605 element = EL_DC_STEELWALL_2_TOP;
5608 case 0x16a7: // steel wall 2 (bottom end)
5609 element = EL_DC_STEELWALL_2_BOTTOM;
5612 case 0x16a8: // steel wall 2 (left/right)
5613 element = EL_DC_STEELWALL_2_HORIZONTAL;
5616 case 0x16a9: // steel wall 2 (up/down)
5617 element = EL_DC_STEELWALL_2_VERTICAL;
5620 case 0x16aa: // steel wall 2 (mid)
5621 element = EL_DC_STEELWALL_2_MIDDLE;
5625 element = EL_SIGN_EXCLAMATION;
5629 element = EL_SIGN_RADIOACTIVITY;
5633 element = EL_SIGN_STOP;
5637 element = EL_SIGN_WHEELCHAIR;
5641 element = EL_SIGN_PARKING;
5645 element = EL_SIGN_NO_ENTRY;
5649 element = EL_SIGN_HEART;
5653 element = EL_SIGN_GIVE_WAY;
5657 element = EL_SIGN_ENTRY_FORBIDDEN;
5661 element = EL_SIGN_EMERGENCY_EXIT;
5665 element = EL_SIGN_YIN_YANG;
5669 element = EL_WALL_EMERALD;
5673 element = EL_WALL_DIAMOND;
5677 element = EL_WALL_PEARL;
5681 element = EL_WALL_CRYSTAL;
5685 element = EL_INVISIBLE_WALL;
5689 element = EL_INVISIBLE_STEELWALL;
5693 // EL_INVISIBLE_SAND
5696 element = EL_LIGHT_SWITCH;
5700 element = EL_ENVELOPE_1;
5704 if (element >= 0x0117 && element <= 0x036e) // (?)
5705 element = EL_DIAMOND;
5706 else if (element >= 0x042d && element <= 0x0684) // (?)
5707 element = EL_EMERALD;
5708 else if (element >= 0x157c && element <= 0x158b)
5710 else if (element >= 0x1590 && element <= 0x159f)
5711 element = EL_DC_LANDMINE;
5712 else if (element >= 0x16bc && element <= 0x16cb)
5713 element = EL_INVISIBLE_SAND;
5716 Warn("unknown Diamond Caves element 0x%04x", element);
5718 element = EL_UNKNOWN;
5723 return getMappedElement(element);
5726 static void LoadLevelFromFileStream_DC(File *file, struct LevelInfo *level)
5728 byte header[DC_LEVEL_HEADER_SIZE];
5730 int envelope_header_pos = 62;
5731 int envelope_content_pos = 94;
5732 int level_name_pos = 251;
5733 int level_author_pos = 292;
5734 int envelope_header_len;
5735 int envelope_content_len;
5737 int level_author_len;
5739 int num_yamyam_contents;
5742 getDecodedWord_DC(0, TRUE); // initialize DC2 decoding engine
5744 for (i = 0; i < DC_LEVEL_HEADER_SIZE / 2; i++)
5746 unsigned short header_word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
5748 header[i * 2 + 0] = header_word >> 8;
5749 header[i * 2 + 1] = header_word & 0xff;
5752 // read some values from level header to check level decoding integrity
5753 fieldx = header[6] | (header[7] << 8);
5754 fieldy = header[8] | (header[9] << 8);
5755 num_yamyam_contents = header[60] | (header[61] << 8);
5757 // do some simple sanity checks to ensure that level was correctly decoded
5758 if (fieldx < 1 || fieldx > 256 ||
5759 fieldy < 1 || fieldy > 256 ||
5760 num_yamyam_contents < 1 || num_yamyam_contents > 8)
5762 level->no_valid_file = TRUE;
5764 Warn("cannot decode level from stream -- using empty level");
5769 // maximum envelope header size is 31 bytes
5770 envelope_header_len = header[envelope_header_pos];
5771 // maximum envelope content size is 110 (156?) bytes
5772 envelope_content_len = header[envelope_content_pos];
5774 // maximum level title size is 40 bytes
5775 level_name_len = MIN(header[level_name_pos], MAX_LEVEL_NAME_LEN);
5776 // maximum level author size is 30 (51?) bytes
5777 level_author_len = MIN(header[level_author_pos], MAX_LEVEL_AUTHOR_LEN);
5781 for (i = 0; i < envelope_header_len; i++)
5782 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
5783 level->envelope[0].text[envelope_size++] =
5784 header[envelope_header_pos + 1 + i];
5786 if (envelope_header_len > 0 && envelope_content_len > 0)
5788 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
5789 level->envelope[0].text[envelope_size++] = '\n';
5790 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
5791 level->envelope[0].text[envelope_size++] = '\n';
5794 for (i = 0; i < envelope_content_len; i++)
5795 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
5796 level->envelope[0].text[envelope_size++] =
5797 header[envelope_content_pos + 1 + i];
5799 level->envelope[0].text[envelope_size] = '\0';
5801 level->envelope[0].xsize = MAX_ENVELOPE_XSIZE;
5802 level->envelope[0].ysize = 10;
5803 level->envelope[0].autowrap = TRUE;
5804 level->envelope[0].centered = TRUE;
5806 for (i = 0; i < level_name_len; i++)
5807 level->name[i] = header[level_name_pos + 1 + i];
5808 level->name[level_name_len] = '\0';
5810 for (i = 0; i < level_author_len; i++)
5811 level->author[i] = header[level_author_pos + 1 + i];
5812 level->author[level_author_len] = '\0';
5814 num_yamyam_contents = header[60] | (header[61] << 8);
5815 level->num_yamyam_contents =
5816 MIN(MAX(MIN_ELEMENT_CONTENTS, num_yamyam_contents), MAX_ELEMENT_CONTENTS);
5818 for (i = 0; i < num_yamyam_contents; i++)
5820 for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
5822 unsigned short word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
5823 int element_dc = ((word & 0xff) << 8) | ((word >> 8) & 0xff);
5825 if (i < MAX_ELEMENT_CONTENTS)
5826 level->yamyam_content[i].e[x][y] = getMappedElement_DC(element_dc);
5830 fieldx = header[6] | (header[7] << 8);
5831 fieldy = header[8] | (header[9] << 8);
5832 level->fieldx = MIN(MAX(MIN_LEV_FIELDX, fieldx), MAX_LEV_FIELDX);
5833 level->fieldy = MIN(MAX(MIN_LEV_FIELDY, fieldy), MAX_LEV_FIELDY);
5835 for (y = 0; y < fieldy; y++) for (x = 0; x < fieldx; x++)
5837 unsigned short word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
5838 int element_dc = ((word & 0xff) << 8) | ((word >> 8) & 0xff);
5840 if (x < MAX_LEV_FIELDX && y < MAX_LEV_FIELDY)
5841 level->field[x][y] = getMappedElement_DC(element_dc);
5844 x = MIN(MAX(0, (header[10] | (header[11] << 8)) - 1), MAX_LEV_FIELDX - 1);
5845 y = MIN(MAX(0, (header[12] | (header[13] << 8)) - 1), MAX_LEV_FIELDY - 1);
5846 level->field[x][y] = EL_PLAYER_1;
5848 x = MIN(MAX(0, (header[14] | (header[15] << 8)) - 1), MAX_LEV_FIELDX - 1);
5849 y = MIN(MAX(0, (header[16] | (header[17] << 8)) - 1), MAX_LEV_FIELDY - 1);
5850 level->field[x][y] = EL_PLAYER_2;
5852 level->gems_needed = header[18] | (header[19] << 8);
5854 level->score[SC_EMERALD] = header[20] | (header[21] << 8);
5855 level->score[SC_DIAMOND] = header[22] | (header[23] << 8);
5856 level->score[SC_PEARL] = header[24] | (header[25] << 8);
5857 level->score[SC_CRYSTAL] = header[26] | (header[27] << 8);
5858 level->score[SC_NUT] = header[28] | (header[29] << 8);
5859 level->score[SC_ROBOT] = header[30] | (header[31] << 8);
5860 level->score[SC_SPACESHIP] = header[32] | (header[33] << 8);
5861 level->score[SC_BUG] = header[34] | (header[35] << 8);
5862 level->score[SC_YAMYAM] = header[36] | (header[37] << 8);
5863 level->score[SC_DYNAMITE] = header[38] | (header[39] << 8);
5864 level->score[SC_KEY] = header[40] | (header[41] << 8);
5865 level->score[SC_TIME_BONUS] = header[42] | (header[43] << 8);
5867 level->time = header[44] | (header[45] << 8);
5869 level->amoeba_speed = header[46] | (header[47] << 8);
5870 level->time_light = header[48] | (header[49] << 8);
5871 level->time_timegate = header[50] | (header[51] << 8);
5872 level->time_wheel = header[52] | (header[53] << 8);
5873 level->time_magic_wall = header[54] | (header[55] << 8);
5874 level->extra_time = header[56] | (header[57] << 8);
5875 level->shield_normal_time = header[58] | (header[59] << 8);
5877 // shield and extra time elements do not have a score
5878 level->score[SC_SHIELD] = 0;
5879 level->extra_time_score = 0;
5881 // set time for normal and deadly shields to the same value
5882 level->shield_deadly_time = level->shield_normal_time;
5884 // Diamond Caves has the same (strange) behaviour as Emerald Mine that gems
5885 // can slip down from flat walls, like normal walls and steel walls
5886 level->em_slippery_gems = TRUE;
5888 // time score is counted for each 10 seconds left in Diamond Caves levels
5889 level->time_score_base = 10;
5892 static void LoadLevelFromFileInfo_DC(struct LevelInfo *level,
5893 struct LevelFileInfo *level_file_info,
5894 boolean level_info_only)
5896 char *filename = level_file_info->filename;
5898 int num_magic_bytes = 8;
5899 char magic_bytes[num_magic_bytes + 1];
5900 int num_levels_to_skip = level_file_info->nr - leveldir_current->first_level;
5902 if (!(file = openFile(filename, MODE_READ)))
5904 level->no_valid_file = TRUE;
5906 if (!level_info_only)
5907 Warn("cannot read level '%s' -- using empty level", filename);
5912 // fseek(file, 0x0000, SEEK_SET);
5914 if (level_file_info->packed)
5916 // read "magic bytes" from start of file
5917 if (getStringFromFile(file, magic_bytes, num_magic_bytes + 1) == NULL)
5918 magic_bytes[0] = '\0';
5920 // check "magic bytes" for correct file format
5921 if (!strPrefix(magic_bytes, "DC2"))
5923 level->no_valid_file = TRUE;
5925 Warn("unknown DC level file '%s' -- using empty level", filename);
5930 if (strPrefix(magic_bytes, "DC2Win95") ||
5931 strPrefix(magic_bytes, "DC2Win98"))
5933 int position_first_level = 0x00fa;
5934 int extra_bytes = 4;
5937 // advance file stream to first level inside the level package
5938 skip_bytes = position_first_level - num_magic_bytes - extra_bytes;
5940 // each block of level data is followed by block of non-level data
5941 num_levels_to_skip *= 2;
5943 // at least skip header bytes, therefore use ">= 0" instead of "> 0"
5944 while (num_levels_to_skip >= 0)
5946 // advance file stream to next level inside the level package
5947 if (seekFile(file, skip_bytes, SEEK_CUR) != 0)
5949 level->no_valid_file = TRUE;
5951 Warn("cannot fseek in file '%s' -- using empty level", filename);
5956 // skip apparently unused extra bytes following each level
5957 ReadUnusedBytesFromFile(file, extra_bytes);
5959 // read size of next level in level package
5960 skip_bytes = getFile32BitLE(file);
5962 num_levels_to_skip--;
5967 level->no_valid_file = TRUE;
5969 Warn("unknown DC2 level file '%s' -- using empty level", filename);
5975 LoadLevelFromFileStream_DC(file, level);
5981 // ----------------------------------------------------------------------------
5982 // functions for loading SB level
5983 // ----------------------------------------------------------------------------
5985 int getMappedElement_SB(int element_ascii, boolean use_ces)
5993 sb_element_mapping[] =
5995 { ' ', EL_EMPTY, EL_CUSTOM_1 }, // floor (space)
5996 { '#', EL_STEELWALL, EL_CUSTOM_2 }, // wall
5997 { '@', EL_PLAYER_1, EL_CUSTOM_3 }, // player
5998 { '$', EL_SOKOBAN_OBJECT, EL_CUSTOM_4 }, // box
5999 { '.', EL_SOKOBAN_FIELD_EMPTY, EL_CUSTOM_5 }, // goal square
6000 { '*', EL_SOKOBAN_FIELD_FULL, EL_CUSTOM_6 }, // box on goal square
6001 { '+', EL_SOKOBAN_FIELD_PLAYER, EL_CUSTOM_7 }, // player on goal square
6002 { '_', EL_INVISIBLE_STEELWALL, EL_FROM_LEVEL_TEMPLATE }, // floor beyond border
6009 for (i = 0; sb_element_mapping[i].ascii != 0; i++)
6010 if (element_ascii == sb_element_mapping[i].ascii)
6011 return (use_ces ? sb_element_mapping[i].ce : sb_element_mapping[i].sb);
6013 return EL_UNDEFINED;
6016 static void SetLevelSettings_SB(struct LevelInfo *level)
6020 level->use_step_counter = TRUE;
6023 level->score[SC_TIME_BONUS] = 0;
6024 level->time_score_base = 1;
6025 level->rate_time_over_score = TRUE;
6028 level->auto_exit_sokoban = TRUE;
6031 static void LoadLevelFromFileInfo_SB(struct LevelInfo *level,
6032 struct LevelFileInfo *level_file_info,
6033 boolean level_info_only)
6035 char *filename = level_file_info->filename;
6036 char line[MAX_LINE_LEN], line_raw[MAX_LINE_LEN], previous_line[MAX_LINE_LEN];
6037 char last_comment[MAX_LINE_LEN];
6038 char level_name[MAX_LINE_LEN];
6041 int num_levels_to_skip = level_file_info->nr - leveldir_current->first_level;
6042 boolean read_continued_line = FALSE;
6043 boolean reading_playfield = FALSE;
6044 boolean got_valid_playfield_line = FALSE;
6045 boolean invalid_playfield_char = FALSE;
6046 boolean load_xsb_to_ces = check_special_flags("load_xsb_to_ces");
6047 int file_level_nr = 0;
6049 int x = 0, y = 0; // initialized to make compilers happy
6051 last_comment[0] = '\0';
6052 level_name[0] = '\0';
6054 if (!(file = openFile(filename, MODE_READ)))
6056 level->no_valid_file = TRUE;
6058 if (!level_info_only)
6059 Warn("cannot read level '%s' -- using empty level", filename);
6064 while (!checkEndOfFile(file))
6066 // level successfully read, but next level may follow here
6067 if (!got_valid_playfield_line && reading_playfield)
6069 // read playfield from single level file -- skip remaining file
6070 if (!level_file_info->packed)
6073 if (file_level_nr >= num_levels_to_skip)
6078 last_comment[0] = '\0';
6079 level_name[0] = '\0';
6081 reading_playfield = FALSE;
6084 got_valid_playfield_line = FALSE;
6086 // read next line of input file
6087 if (!getStringFromFile(file, line, MAX_LINE_LEN))
6090 // check if line was completely read and is terminated by line break
6091 if (strlen(line) > 0 && line[strlen(line) - 1] == '\n')
6094 // cut trailing line break (this can be newline and/or carriage return)
6095 for (line_ptr = &line[strlen(line)]; line_ptr >= line; line_ptr--)
6096 if ((*line_ptr == '\n' || *line_ptr == '\r') && *(line_ptr + 1) == '\0')
6099 // copy raw input line for later use (mainly debugging output)
6100 strcpy(line_raw, line);
6102 if (read_continued_line)
6104 // append new line to existing line, if there is enough space
6105 if (strlen(previous_line) + strlen(line_ptr) < MAX_LINE_LEN)
6106 strcat(previous_line, line_ptr);
6108 strcpy(line, previous_line); // copy storage buffer to line
6110 read_continued_line = FALSE;
6113 // if the last character is '\', continue at next line
6114 if (strlen(line) > 0 && line[strlen(line) - 1] == '\\')
6116 line[strlen(line) - 1] = '\0'; // cut off trailing backslash
6117 strcpy(previous_line, line); // copy line to storage buffer
6119 read_continued_line = TRUE;
6125 if (line[0] == '\0')
6128 // extract comment text from comment line
6131 for (line_ptr = line; *line_ptr; line_ptr++)
6132 if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != ';')
6135 strcpy(last_comment, line_ptr);
6140 // extract level title text from line containing level title
6141 if (line[0] == '\'')
6143 strcpy(level_name, &line[1]);
6145 if (strlen(level_name) > 0 && level_name[strlen(level_name) - 1] == '\'')
6146 level_name[strlen(level_name) - 1] = '\0';
6151 // skip lines containing only spaces (or empty lines)
6152 for (line_ptr = line; *line_ptr; line_ptr++)
6153 if (*line_ptr != ' ')
6155 if (*line_ptr == '\0')
6158 // at this point, we have found a line containing part of a playfield
6160 got_valid_playfield_line = TRUE;
6162 if (!reading_playfield)
6164 reading_playfield = TRUE;
6165 invalid_playfield_char = FALSE;
6167 for (x = 0; x < MAX_LEV_FIELDX; x++)
6168 for (y = 0; y < MAX_LEV_FIELDY; y++)
6169 level->field[x][y] = getMappedElement_SB(' ', load_xsb_to_ces);
6174 // start with topmost tile row
6178 // skip playfield line if larger row than allowed
6179 if (y >= MAX_LEV_FIELDY)
6182 // start with leftmost tile column
6185 // read playfield elements from line
6186 for (line_ptr = line; *line_ptr; line_ptr++)
6188 int mapped_sb_element = getMappedElement_SB(*line_ptr, load_xsb_to_ces);
6190 // stop parsing playfield line if larger column than allowed
6191 if (x >= MAX_LEV_FIELDX)
6194 if (mapped_sb_element == EL_UNDEFINED)
6196 invalid_playfield_char = TRUE;
6201 level->field[x][y] = mapped_sb_element;
6203 // continue with next tile column
6206 level->fieldx = MAX(x, level->fieldx);
6209 if (invalid_playfield_char)
6211 // if first playfield line, treat invalid lines as comment lines
6213 reading_playfield = FALSE;
6218 // continue with next tile row
6226 level->fieldx = MIN(MAX(MIN_LEV_FIELDX, level->fieldx), MAX_LEV_FIELDX);
6227 level->fieldy = MIN(MAX(MIN_LEV_FIELDY, level->fieldy), MAX_LEV_FIELDY);
6229 if (!reading_playfield)
6231 level->no_valid_file = TRUE;
6233 Warn("cannot read level '%s' -- using empty level", filename);
6238 if (*level_name != '\0')
6240 strncpy(level->name, level_name, MAX_LEVEL_NAME_LEN);
6241 level->name[MAX_LEVEL_NAME_LEN] = '\0';
6243 else if (*last_comment != '\0')
6245 strncpy(level->name, last_comment, MAX_LEVEL_NAME_LEN);
6246 level->name[MAX_LEVEL_NAME_LEN] = '\0';
6250 sprintf(level->name, "--> Level %d <--", level_file_info->nr);
6253 // set all empty fields beyond the border walls to invisible steel wall
6254 for (y = 0; y < level->fieldy; y++) for (x = 0; x < level->fieldx; x++)
6256 if ((x == 0 || x == level->fieldx - 1 ||
6257 y == 0 || y == level->fieldy - 1) &&
6258 level->field[x][y] == getMappedElement_SB(' ', load_xsb_to_ces))
6259 FloodFillLevel(x, y, getMappedElement_SB('_', load_xsb_to_ces),
6260 level->field, level->fieldx, level->fieldy);
6263 // set special level settings for Sokoban levels
6264 SetLevelSettings_SB(level);
6266 if (load_xsb_to_ces)
6268 // special global settings can now be set in level template
6269 level->use_custom_template = TRUE;
6274 // -------------------------------------------------------------------------
6275 // functions for handling native levels
6276 // -------------------------------------------------------------------------
6278 static void LoadLevelFromFileInfo_EM(struct LevelInfo *level,
6279 struct LevelFileInfo *level_file_info,
6280 boolean level_info_only)
6282 if (!LoadNativeLevel_EM(level_file_info->filename, level_info_only))
6283 level->no_valid_file = TRUE;
6286 static void LoadLevelFromFileInfo_SP(struct LevelInfo *level,
6287 struct LevelFileInfo *level_file_info,
6288 boolean level_info_only)
6292 // determine position of requested level inside level package
6293 if (level_file_info->packed)
6294 pos = level_file_info->nr - leveldir_current->first_level;
6296 if (!LoadNativeLevel_SP(level_file_info->filename, pos, level_info_only))
6297 level->no_valid_file = TRUE;
6300 static void LoadLevelFromFileInfo_MM(struct LevelInfo *level,
6301 struct LevelFileInfo *level_file_info,
6302 boolean level_info_only)
6304 if (!LoadNativeLevel_MM(level_file_info->filename, level_info_only))
6305 level->no_valid_file = TRUE;
6308 void CopyNativeLevel_RND_to_Native(struct LevelInfo *level)
6310 if (level->game_engine_type == GAME_ENGINE_TYPE_EM)
6311 CopyNativeLevel_RND_to_EM(level);
6312 else if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
6313 CopyNativeLevel_RND_to_SP(level);
6314 else if (level->game_engine_type == GAME_ENGINE_TYPE_MM)
6315 CopyNativeLevel_RND_to_MM(level);
6318 void CopyNativeLevel_Native_to_RND(struct LevelInfo *level)
6320 if (level->game_engine_type == GAME_ENGINE_TYPE_EM)
6321 CopyNativeLevel_EM_to_RND(level);
6322 else if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
6323 CopyNativeLevel_SP_to_RND(level);
6324 else if (level->game_engine_type == GAME_ENGINE_TYPE_MM)
6325 CopyNativeLevel_MM_to_RND(level);
6328 void SaveNativeLevel(struct LevelInfo *level)
6330 if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
6332 char *basename = getSingleLevelBasenameExt(level->file_info.nr, "sp");
6333 char *filename = getLevelFilenameFromBasename(basename);
6335 CopyNativeLevel_RND_to_SP(level);
6336 CopyNativeTape_RND_to_SP(level);
6338 SaveNativeLevel_SP(filename);
6343 // ----------------------------------------------------------------------------
6344 // functions for loading generic level
6345 // ----------------------------------------------------------------------------
6347 static void LoadLevelFromFileInfo(struct LevelInfo *level,
6348 struct LevelFileInfo *level_file_info,
6349 boolean level_info_only)
6351 // always start with reliable default values
6352 setLevelInfoToDefaults(level, level_info_only, TRUE);
6354 switch (level_file_info->type)
6356 case LEVEL_FILE_TYPE_RND:
6357 LoadLevelFromFileInfo_RND(level, level_file_info, level_info_only);
6360 case LEVEL_FILE_TYPE_EM:
6361 LoadLevelFromFileInfo_EM(level, level_file_info, level_info_only);
6362 level->game_engine_type = GAME_ENGINE_TYPE_EM;
6365 case LEVEL_FILE_TYPE_SP:
6366 LoadLevelFromFileInfo_SP(level, level_file_info, level_info_only);
6367 level->game_engine_type = GAME_ENGINE_TYPE_SP;
6370 case LEVEL_FILE_TYPE_MM:
6371 LoadLevelFromFileInfo_MM(level, level_file_info, level_info_only);
6372 level->game_engine_type = GAME_ENGINE_TYPE_MM;
6375 case LEVEL_FILE_TYPE_DC:
6376 LoadLevelFromFileInfo_DC(level, level_file_info, level_info_only);
6379 case LEVEL_FILE_TYPE_SB:
6380 LoadLevelFromFileInfo_SB(level, level_file_info, level_info_only);
6384 LoadLevelFromFileInfo_RND(level, level_file_info, level_info_only);
6388 // if level file is invalid, restore level structure to default values
6389 if (level->no_valid_file)
6390 setLevelInfoToDefaults(level, level_info_only, FALSE);
6392 if (level->game_engine_type == GAME_ENGINE_TYPE_UNKNOWN)
6393 level->game_engine_type = GAME_ENGINE_TYPE_RND;
6395 if (level_file_info->type != LEVEL_FILE_TYPE_RND)
6396 CopyNativeLevel_Native_to_RND(level);
6399 void LoadLevelFromFilename(struct LevelInfo *level, char *filename)
6401 static struct LevelFileInfo level_file_info;
6403 // always start with reliable default values
6404 setFileInfoToDefaults(&level_file_info);
6406 level_file_info.nr = 0; // unknown level number
6407 level_file_info.type = LEVEL_FILE_TYPE_RND; // no others supported yet
6409 setString(&level_file_info.filename, filename);
6411 LoadLevelFromFileInfo(level, &level_file_info, FALSE);
6414 static void LoadLevel_InitVersion(struct LevelInfo *level)
6418 if (leveldir_current == NULL) // only when dumping level
6421 // all engine modifications also valid for levels which use latest engine
6422 if (level->game_version < VERSION_IDENT(3,2,0,5))
6424 // time bonus score was given for 10 s instead of 1 s before 3.2.0-5
6425 level->time_score_base = 10;
6428 if (leveldir_current->latest_engine)
6430 // ---------- use latest game engine --------------------------------------
6432 /* For all levels which are forced to use the latest game engine version
6433 (normally all but user contributed, private and undefined levels), set
6434 the game engine version to the actual version; this allows for actual
6435 corrections in the game engine to take effect for existing, converted
6436 levels (from "classic" or other existing games) to make the emulation
6437 of the corresponding game more accurate, while (hopefully) not breaking
6438 existing levels created from other players. */
6440 level->game_version = GAME_VERSION_ACTUAL;
6442 /* Set special EM style gems behaviour: EM style gems slip down from
6443 normal, steel and growing wall. As this is a more fundamental change,
6444 it seems better to set the default behaviour to "off" (as it is more
6445 natural) and make it configurable in the level editor (as a property
6446 of gem style elements). Already existing converted levels (neither
6447 private nor contributed levels) are changed to the new behaviour. */
6449 if (level->file_version < FILE_VERSION_2_0)
6450 level->em_slippery_gems = TRUE;
6455 // ---------- use game engine the level was created with --------------------
6457 /* For all levels which are not forced to use the latest game engine
6458 version (normally user contributed, private and undefined levels),
6459 use the version of the game engine the levels were created for.
6461 Since 2.0.1, the game engine version is now directly stored
6462 in the level file (chunk "VERS"), so there is no need anymore
6463 to set the game version from the file version (except for old,
6464 pre-2.0 levels, where the game version is still taken from the
6465 file format version used to store the level -- see above). */
6467 // player was faster than enemies in 1.0.0 and before
6468 if (level->file_version == FILE_VERSION_1_0)
6469 for (i = 0; i < MAX_PLAYERS; i++)
6470 level->initial_player_stepsize[i] = STEPSIZE_FAST;
6472 // default behaviour for EM style gems was "slippery" only in 2.0.1
6473 if (level->game_version == VERSION_IDENT(2,0,1,0))
6474 level->em_slippery_gems = TRUE;
6476 // springs could be pushed over pits before (pre-release version) 2.2.0
6477 if (level->game_version < VERSION_IDENT(2,2,0,0))
6478 level->use_spring_bug = TRUE;
6480 if (level->game_version < VERSION_IDENT(3,2,0,5))
6482 // time orb caused limited time in endless time levels before 3.2.0-5
6483 level->use_time_orb_bug = TRUE;
6485 // default behaviour for snapping was "no snap delay" before 3.2.0-5
6486 level->block_snap_field = FALSE;
6488 // extra time score was same value as time left score before 3.2.0-5
6489 level->extra_time_score = level->score[SC_TIME_BONUS];
6492 if (level->game_version < VERSION_IDENT(3,2,0,7))
6494 // default behaviour for snapping was "not continuous" before 3.2.0-7
6495 level->continuous_snapping = FALSE;
6498 // only few elements were able to actively move into acid before 3.1.0
6499 // trigger settings did not exist before 3.1.0; set to default "any"
6500 if (level->game_version < VERSION_IDENT(3,1,0,0))
6502 // correct "can move into acid" settings (all zero in old levels)
6504 level->can_move_into_acid_bits = 0; // nothing can move into acid
6505 level->dont_collide_with_bits = 0; // nothing is deadly when colliding
6507 setMoveIntoAcidProperty(level, EL_ROBOT, TRUE);
6508 setMoveIntoAcidProperty(level, EL_SATELLITE, TRUE);
6509 setMoveIntoAcidProperty(level, EL_PENGUIN, TRUE);
6510 setMoveIntoAcidProperty(level, EL_BALLOON, TRUE);
6512 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6513 SET_PROPERTY(EL_CUSTOM_START + i, EP_CAN_MOVE_INTO_ACID, TRUE);
6515 // correct trigger settings (stored as zero == "none" in old levels)
6517 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6519 int element = EL_CUSTOM_START + i;
6520 struct ElementInfo *ei = &element_info[element];
6522 for (j = 0; j < ei->num_change_pages; j++)
6524 struct ElementChangeInfo *change = &ei->change_page[j];
6526 change->trigger_player = CH_PLAYER_ANY;
6527 change->trigger_page = CH_PAGE_ANY;
6532 // try to detect and fix "Snake Bite" levels, which are broken with 3.2.0
6534 int element = EL_CUSTOM_256;
6535 struct ElementInfo *ei = &element_info[element];
6536 struct ElementChangeInfo *change = &ei->change_page[0];
6538 /* This is needed to fix a problem that was caused by a bugfix in function
6539 game.c/CreateFieldExt() introduced with 3.2.0 that corrects the behaviour
6540 when a custom element changes to EL_SOKOBAN_FIELD_PLAYER (before, it did
6541 not replace walkable elements, but instead just placed the player on it,
6542 without placing the Sokoban field under the player). Unfortunately, this
6543 breaks "Snake Bite" style levels when the snake is halfway through a door
6544 that just closes (the snake head is still alive and can be moved in this
6545 case). This can be fixed by replacing the EL_SOKOBAN_FIELD_PLAYER by the
6546 player (without Sokoban element) which then gets killed as designed). */
6548 if ((strncmp(leveldir_current->identifier, "snake_bite", 10) == 0 ||
6549 strncmp(ei->description, "pause b4 death", 14) == 0) &&
6550 change->target_element == EL_SOKOBAN_FIELD_PLAYER)
6551 change->target_element = EL_PLAYER_1;
6554 // try to detect and fix "Zelda" style levels, which are broken with 3.2.5
6555 if (level->game_version < VERSION_IDENT(3,2,5,0))
6557 /* This is needed to fix a problem that was caused by a bugfix in function
6558 game.c/CheckTriggeredElementChangeExt() introduced with 3.2.5 that
6559 corrects the behaviour when a custom element changes to another custom
6560 element with a higher element number that has change actions defined.
6561 Normally, only one change per frame is allowed for custom elements.
6562 Therefore, it is checked if a custom element already changed in the
6563 current frame; if it did, subsequent changes are suppressed.
6564 Unfortunately, this is only checked for element changes, but not for
6565 change actions, which are still executed. As the function above loops
6566 through all custom elements from lower to higher, an element change
6567 resulting in a lower CE number won't be checked again, while a target
6568 element with a higher number will also be checked, and potential change
6569 actions will get executed for this CE, too (which is wrong), while
6570 further changes are ignored (which is correct). As this bugfix breaks
6571 Zelda II (and introduces graphical bugs to Zelda I, and also breaks a
6572 few other levels like Alan Bond's "FMV"), allow the previous, incorrect
6573 behaviour for existing levels and tapes that make use of this bug */
6575 level->use_action_after_change_bug = TRUE;
6578 // not centering level after relocating player was default only in 3.2.3
6579 if (level->game_version == VERSION_IDENT(3,2,3,0)) // (no pre-releases)
6580 level->shifted_relocation = TRUE;
6582 // EM style elements always chain-exploded in R'n'D engine before 3.2.6
6583 if (level->game_version < VERSION_IDENT(3,2,6,0))
6584 level->em_explodes_by_fire = TRUE;
6586 // levels were solved by the first player entering an exit up to 4.1.0.0
6587 if (level->game_version <= VERSION_IDENT(4,1,0,0))
6588 level->solved_by_one_player = TRUE;
6590 // game logic of "game of life" and "biomaze" was buggy before 4.1.1.1
6591 if (level->game_version < VERSION_IDENT(4,1,1,1))
6592 level->use_life_bugs = TRUE;
6594 // only Sokoban fields (but not objects) had to be solved before 4.1.1.1
6595 if (level->game_version < VERSION_IDENT(4,1,1,1))
6596 level->sb_objects_needed = FALSE;
6598 // CE actions were triggered by unfinished digging/collecting up to 4.2.2.0
6599 if (level->game_version <= VERSION_IDENT(4,2,2,0))
6600 level->finish_dig_collect = FALSE;
6602 // CE changing to player was kept under the player if walkable up to 4.2.3.1
6603 if (level->game_version <= VERSION_IDENT(4,2,3,1))
6604 level->keep_walkable_ce = TRUE;
6607 static void LoadLevel_InitSettings_SB(struct LevelInfo *level)
6609 boolean is_sokoban_level = TRUE; // unless non-Sokoban elements found
6612 // check if this level is (not) a Sokoban level
6613 for (y = 0; y < level->fieldy; y++)
6614 for (x = 0; x < level->fieldx; x++)
6615 if (!IS_SB_ELEMENT(Tile[x][y]))
6616 is_sokoban_level = FALSE;
6618 if (is_sokoban_level)
6620 // set special level settings for Sokoban levels
6621 SetLevelSettings_SB(level);
6625 static void LoadLevel_InitSettings(struct LevelInfo *level)
6627 // adjust level settings for (non-native) Sokoban-style levels
6628 LoadLevel_InitSettings_SB(level);
6630 // rename levels with title "nameless level" or if renaming is forced
6631 if (leveldir_current->empty_level_name != NULL &&
6632 (strEqual(level->name, NAMELESS_LEVEL_NAME) ||
6633 leveldir_current->force_level_name))
6634 snprintf(level->name, MAX_LEVEL_NAME_LEN + 1,
6635 leveldir_current->empty_level_name, level_nr);
6638 static void LoadLevel_InitStandardElements(struct LevelInfo *level)
6642 // map elements that have changed in newer versions
6643 level->amoeba_content = getMappedElementByVersion(level->amoeba_content,
6644 level->game_version);
6645 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
6646 for (x = 0; x < 3; x++)
6647 for (y = 0; y < 3; y++)
6648 level->yamyam_content[i].e[x][y] =
6649 getMappedElementByVersion(level->yamyam_content[i].e[x][y],
6650 level->game_version);
6654 static void LoadLevel_InitCustomElements(struct LevelInfo *level)
6658 // map custom element change events that have changed in newer versions
6659 // (these following values were accidentally changed in version 3.0.1)
6660 // (this seems to be needed only for 'ab_levelset3' and 'ab_levelset4')
6661 if (level->game_version <= VERSION_IDENT(3,0,0,0))
6663 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6665 int element = EL_CUSTOM_START + i;
6667 // order of checking and copying events to be mapped is important
6668 // (do not change the start and end value -- they are constant)
6669 for (j = CE_BY_OTHER_ACTION; j >= CE_VALUE_GETS_ZERO; j--)
6671 if (HAS_CHANGE_EVENT(element, j - 2))
6673 SET_CHANGE_EVENT(element, j - 2, FALSE);
6674 SET_CHANGE_EVENT(element, j, TRUE);
6678 // order of checking and copying events to be mapped is important
6679 // (do not change the start and end value -- they are constant)
6680 for (j = CE_PLAYER_COLLECTS_X; j >= CE_HITTING_SOMETHING; j--)
6682 if (HAS_CHANGE_EVENT(element, j - 1))
6684 SET_CHANGE_EVENT(element, j - 1, FALSE);
6685 SET_CHANGE_EVENT(element, j, TRUE);
6691 // initialize "can_change" field for old levels with only one change page
6692 if (level->game_version <= VERSION_IDENT(3,0,2,0))
6694 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6696 int element = EL_CUSTOM_START + i;
6698 if (CAN_CHANGE(element))
6699 element_info[element].change->can_change = TRUE;
6703 // correct custom element values (for old levels without these options)
6704 if (level->game_version < VERSION_IDENT(3,1,1,0))
6706 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6708 int element = EL_CUSTOM_START + i;
6709 struct ElementInfo *ei = &element_info[element];
6711 if (ei->access_direction == MV_NO_DIRECTION)
6712 ei->access_direction = MV_ALL_DIRECTIONS;
6716 // correct custom element values (fix invalid values for all versions)
6719 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6721 int element = EL_CUSTOM_START + i;
6722 struct ElementInfo *ei = &element_info[element];
6724 for (j = 0; j < ei->num_change_pages; j++)
6726 struct ElementChangeInfo *change = &ei->change_page[j];
6728 if (change->trigger_player == CH_PLAYER_NONE)
6729 change->trigger_player = CH_PLAYER_ANY;
6731 if (change->trigger_side == CH_SIDE_NONE)
6732 change->trigger_side = CH_SIDE_ANY;
6737 // initialize "can_explode" field for old levels which did not store this
6738 // !!! CHECK THIS -- "<= 3,1,0,0" IS PROBABLY WRONG !!!
6739 if (level->game_version <= VERSION_IDENT(3,1,0,0))
6741 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6743 int element = EL_CUSTOM_START + i;
6745 if (EXPLODES_1X1_OLD(element))
6746 element_info[element].explosion_type = EXPLODES_1X1;
6748 SET_PROPERTY(element, EP_CAN_EXPLODE, (EXPLODES_BY_FIRE(element) ||
6749 EXPLODES_SMASHED(element) ||
6750 EXPLODES_IMPACT(element)));
6754 // correct previously hard-coded move delay values for maze runner style
6755 if (level->game_version < VERSION_IDENT(3,1,1,0))
6757 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6759 int element = EL_CUSTOM_START + i;
6761 if (element_info[element].move_pattern & MV_MAZE_RUNNER_STYLE)
6763 // previously hard-coded and therefore ignored
6764 element_info[element].move_delay_fixed = 9;
6765 element_info[element].move_delay_random = 0;
6770 // set some other uninitialized values of custom elements in older levels
6771 if (level->game_version < VERSION_IDENT(3,1,0,0))
6773 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6775 int element = EL_CUSTOM_START + i;
6777 element_info[element].access_direction = MV_ALL_DIRECTIONS;
6779 element_info[element].explosion_delay = 17;
6780 element_info[element].ignition_delay = 8;
6784 // set mouse click change events to work for left/middle/right mouse button
6785 if (level->game_version < VERSION_IDENT(4,2,3,0))
6787 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6789 int element = EL_CUSTOM_START + i;
6790 struct ElementInfo *ei = &element_info[element];
6792 for (j = 0; j < ei->num_change_pages; j++)
6794 struct ElementChangeInfo *change = &ei->change_page[j];
6796 if (change->has_event[CE_CLICKED_BY_MOUSE] ||
6797 change->has_event[CE_PRESSED_BY_MOUSE] ||
6798 change->has_event[CE_MOUSE_CLICKED_ON_X] ||
6799 change->has_event[CE_MOUSE_PRESSED_ON_X])
6800 change->trigger_side = CH_SIDE_ANY;
6806 static void LoadLevel_InitElements(struct LevelInfo *level)
6808 LoadLevel_InitStandardElements(level);
6810 if (level->file_has_custom_elements)
6811 LoadLevel_InitCustomElements(level);
6813 // initialize element properties for level editor etc.
6814 InitElementPropertiesEngine(level->game_version);
6815 InitElementPropertiesGfxElement();
6818 static void LoadLevel_InitPlayfield(struct LevelInfo *level)
6822 // map elements that have changed in newer versions
6823 for (y = 0; y < level->fieldy; y++)
6824 for (x = 0; x < level->fieldx; x++)
6825 level->field[x][y] = getMappedElementByVersion(level->field[x][y],
6826 level->game_version);
6828 // clear unused playfield data (nicer if level gets resized in editor)
6829 for (x = 0; x < MAX_LEV_FIELDX; x++)
6830 for (y = 0; y < MAX_LEV_FIELDY; y++)
6831 if (x >= level->fieldx || y >= level->fieldy)
6832 level->field[x][y] = EL_EMPTY;
6834 // copy elements to runtime playfield array
6835 for (x = 0; x < MAX_LEV_FIELDX; x++)
6836 for (y = 0; y < MAX_LEV_FIELDY; y++)
6837 Tile[x][y] = level->field[x][y];
6839 // initialize level size variables for faster access
6840 lev_fieldx = level->fieldx;
6841 lev_fieldy = level->fieldy;
6843 // determine border element for this level
6844 if (level->file_info.type == LEVEL_FILE_TYPE_DC)
6845 BorderElement = EL_EMPTY; // (in editor, SetBorderElement() is used)
6850 static void LoadLevel_InitNativeEngines(struct LevelInfo *level)
6852 struct LevelFileInfo *level_file_info = &level->file_info;
6854 if (level_file_info->type == LEVEL_FILE_TYPE_RND)
6855 CopyNativeLevel_RND_to_Native(level);
6858 static void LoadLevelTemplate_LoadAndInit(void)
6860 LoadLevelFromFileInfo(&level_template, &level_template.file_info, FALSE);
6862 LoadLevel_InitVersion(&level_template);
6863 LoadLevel_InitElements(&level_template);
6864 LoadLevel_InitSettings(&level_template);
6866 ActivateLevelTemplate();
6869 void LoadLevelTemplate(int nr)
6871 if (!fileExists(getGlobalLevelTemplateFilename()))
6873 Warn("no level template found for this level");
6878 setLevelFileInfo(&level_template.file_info, nr);
6880 LoadLevelTemplate_LoadAndInit();
6883 static void LoadNetworkLevelTemplate(struct NetworkLevelInfo *network_level)
6885 copyLevelFileInfo(&network_level->tmpl_info, &level_template.file_info);
6887 LoadLevelTemplate_LoadAndInit();
6890 static void LoadLevel_LoadAndInit(struct NetworkLevelInfo *network_level)
6892 LoadLevelFromFileInfo(&level, &level.file_info, FALSE);
6894 if (level.use_custom_template)
6896 if (network_level != NULL)
6897 LoadNetworkLevelTemplate(network_level);
6899 LoadLevelTemplate(-1);
6902 LoadLevel_InitVersion(&level);
6903 LoadLevel_InitElements(&level);
6904 LoadLevel_InitPlayfield(&level);
6905 LoadLevel_InitSettings(&level);
6907 LoadLevel_InitNativeEngines(&level);
6910 void LoadLevel(int nr)
6912 SetLevelSetInfo(leveldir_current->identifier, nr);
6914 setLevelFileInfo(&level.file_info, nr);
6916 LoadLevel_LoadAndInit(NULL);
6919 void LoadLevelInfoOnly(int nr)
6921 setLevelFileInfo(&level.file_info, nr);
6923 LoadLevelFromFileInfo(&level, &level.file_info, TRUE);
6926 void LoadNetworkLevel(struct NetworkLevelInfo *network_level)
6928 SetLevelSetInfo(network_level->leveldir_identifier,
6929 network_level->file_info.nr);
6931 copyLevelFileInfo(&network_level->file_info, &level.file_info);
6933 LoadLevel_LoadAndInit(network_level);
6936 static int SaveLevel_VERS(FILE *file, struct LevelInfo *level)
6940 chunk_size += putFileVersion(file, level->file_version);
6941 chunk_size += putFileVersion(file, level->game_version);
6946 static int SaveLevel_DATE(FILE *file, struct LevelInfo *level)
6950 chunk_size += putFile16BitBE(file, level->creation_date.year);
6951 chunk_size += putFile8Bit(file, level->creation_date.month);
6952 chunk_size += putFile8Bit(file, level->creation_date.day);
6957 #if ENABLE_HISTORIC_CHUNKS
6958 static void SaveLevel_HEAD(FILE *file, struct LevelInfo *level)
6962 putFile8Bit(file, level->fieldx);
6963 putFile8Bit(file, level->fieldy);
6965 putFile16BitBE(file, level->time);
6966 putFile16BitBE(file, level->gems_needed);
6968 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
6969 putFile8Bit(file, level->name[i]);
6971 for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
6972 putFile8Bit(file, level->score[i]);
6974 for (i = 0; i < STD_ELEMENT_CONTENTS; i++)
6975 for (y = 0; y < 3; y++)
6976 for (x = 0; x < 3; x++)
6977 putFile8Bit(file, (level->encoding_16bit_yamyam ? EL_EMPTY :
6978 level->yamyam_content[i].e[x][y]));
6979 putFile8Bit(file, level->amoeba_speed);
6980 putFile8Bit(file, level->time_magic_wall);
6981 putFile8Bit(file, level->time_wheel);
6982 putFile8Bit(file, (level->encoding_16bit_amoeba ? EL_EMPTY :
6983 level->amoeba_content));
6984 putFile8Bit(file, (level->initial_player_stepsize == STEPSIZE_FAST ? 1 : 0));
6985 putFile8Bit(file, (level->initial_gravity ? 1 : 0));
6986 putFile8Bit(file, (level->encoding_16bit_field ? 1 : 0));
6987 putFile8Bit(file, (level->em_slippery_gems ? 1 : 0));
6989 putFile8Bit(file, (level->use_custom_template ? 1 : 0));
6991 putFile8Bit(file, (level->block_last_field ? 1 : 0));
6992 putFile8Bit(file, (level->sp_block_last_field ? 1 : 0));
6993 putFile32BitBE(file, level->can_move_into_acid_bits);
6994 putFile8Bit(file, level->dont_collide_with_bits);
6996 putFile8Bit(file, (level->use_spring_bug ? 1 : 0));
6997 putFile8Bit(file, (level->use_step_counter ? 1 : 0));
6999 putFile8Bit(file, (level->instant_relocation ? 1 : 0));
7000 putFile8Bit(file, (level->can_pass_to_walkable ? 1 : 0));
7001 putFile8Bit(file, (level->grow_into_diggable ? 1 : 0));
7003 putFile8Bit(file, level->game_engine_type);
7005 WriteUnusedBytesToFile(file, LEVEL_CHUNK_HEAD_UNUSED);
7009 static int SaveLevel_NAME(FILE *file, struct LevelInfo *level)
7014 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
7015 chunk_size += putFile8Bit(file, level->name[i]);
7020 static int SaveLevel_AUTH(FILE *file, struct LevelInfo *level)
7025 for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
7026 chunk_size += putFile8Bit(file, level->author[i]);
7031 #if ENABLE_HISTORIC_CHUNKS
7032 static int SaveLevel_BODY(FILE *file, struct LevelInfo *level)
7037 for (y = 0; y < level->fieldy; y++)
7038 for (x = 0; x < level->fieldx; x++)
7039 if (level->encoding_16bit_field)
7040 chunk_size += putFile16BitBE(file, level->field[x][y]);
7042 chunk_size += putFile8Bit(file, level->field[x][y]);
7048 static int SaveLevel_BODY(FILE *file, struct LevelInfo *level)
7053 for (y = 0; y < level->fieldy; y++)
7054 for (x = 0; x < level->fieldx; x++)
7055 chunk_size += putFile16BitBE(file, level->field[x][y]);
7060 #if ENABLE_HISTORIC_CHUNKS
7061 static void SaveLevel_CONT(FILE *file, struct LevelInfo *level)
7065 putFile8Bit(file, EL_YAMYAM);
7066 putFile8Bit(file, level->num_yamyam_contents);
7067 putFile8Bit(file, 0);
7068 putFile8Bit(file, 0);
7070 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
7071 for (y = 0; y < 3; y++)
7072 for (x = 0; x < 3; x++)
7073 if (level->encoding_16bit_field)
7074 putFile16BitBE(file, level->yamyam_content[i].e[x][y]);
7076 putFile8Bit(file, level->yamyam_content[i].e[x][y]);
7080 #if ENABLE_HISTORIC_CHUNKS
7081 static void SaveLevel_CNT2(FILE *file, struct LevelInfo *level, int element)
7084 int num_contents, content_xsize, content_ysize;
7085 int content_array[MAX_ELEMENT_CONTENTS][3][3];
7087 if (element == EL_YAMYAM)
7089 num_contents = level->num_yamyam_contents;
7093 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
7094 for (y = 0; y < 3; y++)
7095 for (x = 0; x < 3; x++)
7096 content_array[i][x][y] = level->yamyam_content[i].e[x][y];
7098 else if (element == EL_BD_AMOEBA)
7104 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
7105 for (y = 0; y < 3; y++)
7106 for (x = 0; x < 3; x++)
7107 content_array[i][x][y] = EL_EMPTY;
7108 content_array[0][0][0] = level->amoeba_content;
7112 // chunk header already written -- write empty chunk data
7113 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_SIZE);
7115 Warn("cannot save content for element '%d'", element);
7120 putFile16BitBE(file, element);
7121 putFile8Bit(file, num_contents);
7122 putFile8Bit(file, content_xsize);
7123 putFile8Bit(file, content_ysize);
7125 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_UNUSED);
7127 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
7128 for (y = 0; y < 3; y++)
7129 for (x = 0; x < 3; x++)
7130 putFile16BitBE(file, content_array[i][x][y]);
7134 #if ENABLE_HISTORIC_CHUNKS
7135 static int SaveLevel_CNT3(FILE *file, struct LevelInfo *level, int element)
7137 int envelope_nr = element - EL_ENVELOPE_1;
7138 int envelope_len = strlen(level->envelope_text[envelope_nr]) + 1;
7142 chunk_size += putFile16BitBE(file, element);
7143 chunk_size += putFile16BitBE(file, envelope_len);
7144 chunk_size += putFile8Bit(file, level->envelope_xsize[envelope_nr]);
7145 chunk_size += putFile8Bit(file, level->envelope_ysize[envelope_nr]);
7147 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT3_UNUSED);
7148 chunk_size += LEVEL_CHUNK_CNT3_UNUSED;
7150 for (i = 0; i < envelope_len; i++)
7151 chunk_size += putFile8Bit(file, level->envelope_text[envelope_nr][i]);
7157 #if ENABLE_HISTORIC_CHUNKS
7158 static void SaveLevel_CUS1(FILE *file, struct LevelInfo *level,
7159 int num_changed_custom_elements)
7163 putFile16BitBE(file, num_changed_custom_elements);
7165 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7167 int element = EL_CUSTOM_START + i;
7169 struct ElementInfo *ei = &element_info[element];
7171 if (ei->properties[EP_BITFIELD_BASE_NR] != EP_BITMASK_DEFAULT)
7173 if (check < num_changed_custom_elements)
7175 putFile16BitBE(file, element);
7176 putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
7183 if (check != num_changed_custom_elements) // should not happen
7184 Warn("inconsistent number of custom element properties");
7188 #if ENABLE_HISTORIC_CHUNKS
7189 static void SaveLevel_CUS2(FILE *file, struct LevelInfo *level,
7190 int num_changed_custom_elements)
7194 putFile16BitBE(file, num_changed_custom_elements);
7196 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7198 int element = EL_CUSTOM_START + i;
7200 if (element_info[element].change->target_element != EL_EMPTY_SPACE)
7202 if (check < num_changed_custom_elements)
7204 putFile16BitBE(file, element);
7205 putFile16BitBE(file, element_info[element].change->target_element);
7212 if (check != num_changed_custom_elements) // should not happen
7213 Warn("inconsistent number of custom target elements");
7217 #if ENABLE_HISTORIC_CHUNKS
7218 static void SaveLevel_CUS3(FILE *file, struct LevelInfo *level,
7219 int num_changed_custom_elements)
7221 int i, j, x, y, check = 0;
7223 putFile16BitBE(file, num_changed_custom_elements);
7225 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7227 int element = EL_CUSTOM_START + i;
7228 struct ElementInfo *ei = &element_info[element];
7230 if (ei->modified_settings)
7232 if (check < num_changed_custom_elements)
7234 putFile16BitBE(file, element);
7236 for (j = 0; j < MAX_ELEMENT_NAME_LEN; j++)
7237 putFile8Bit(file, ei->description[j]);
7239 putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
7241 // some free bytes for future properties and padding
7242 WriteUnusedBytesToFile(file, 7);
7244 putFile8Bit(file, ei->use_gfx_element);
7245 putFile16BitBE(file, ei->gfx_element_initial);
7247 putFile8Bit(file, ei->collect_score_initial);
7248 putFile8Bit(file, ei->collect_count_initial);
7250 putFile16BitBE(file, ei->push_delay_fixed);
7251 putFile16BitBE(file, ei->push_delay_random);
7252 putFile16BitBE(file, ei->move_delay_fixed);
7253 putFile16BitBE(file, ei->move_delay_random);
7255 putFile16BitBE(file, ei->move_pattern);
7256 putFile8Bit(file, ei->move_direction_initial);
7257 putFile8Bit(file, ei->move_stepsize);
7259 for (y = 0; y < 3; y++)
7260 for (x = 0; x < 3; x++)
7261 putFile16BitBE(file, ei->content.e[x][y]);
7263 putFile32BitBE(file, ei->change->events);
7265 putFile16BitBE(file, ei->change->target_element);
7267 putFile16BitBE(file, ei->change->delay_fixed);
7268 putFile16BitBE(file, ei->change->delay_random);
7269 putFile16BitBE(file, ei->change->delay_frames);
7271 putFile16BitBE(file, ei->change->initial_trigger_element);
7273 putFile8Bit(file, ei->change->explode);
7274 putFile8Bit(file, ei->change->use_target_content);
7275 putFile8Bit(file, ei->change->only_if_complete);
7276 putFile8Bit(file, ei->change->use_random_replace);
7278 putFile8Bit(file, ei->change->random_percentage);
7279 putFile8Bit(file, ei->change->replace_when);
7281 for (y = 0; y < 3; y++)
7282 for (x = 0; x < 3; x++)
7283 putFile16BitBE(file, ei->change->content.e[x][y]);
7285 putFile8Bit(file, ei->slippery_type);
7287 // some free bytes for future properties and padding
7288 WriteUnusedBytesToFile(file, LEVEL_CPART_CUS3_UNUSED);
7295 if (check != num_changed_custom_elements) // should not happen
7296 Warn("inconsistent number of custom element properties");
7300 #if ENABLE_HISTORIC_CHUNKS
7301 static void SaveLevel_CUS4(FILE *file, struct LevelInfo *level, int element)
7303 struct ElementInfo *ei = &element_info[element];
7306 // ---------- custom element base property values (96 bytes) ----------------
7308 putFile16BitBE(file, element);
7310 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
7311 putFile8Bit(file, ei->description[i]);
7313 putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
7315 WriteUnusedBytesToFile(file, 4); // reserved for more base properties
7317 putFile8Bit(file, ei->num_change_pages);
7319 putFile16BitBE(file, ei->ce_value_fixed_initial);
7320 putFile16BitBE(file, ei->ce_value_random_initial);
7321 putFile8Bit(file, ei->use_last_ce_value);
7323 putFile8Bit(file, ei->use_gfx_element);
7324 putFile16BitBE(file, ei->gfx_element_initial);
7326 putFile8Bit(file, ei->collect_score_initial);
7327 putFile8Bit(file, ei->collect_count_initial);
7329 putFile8Bit(file, ei->drop_delay_fixed);
7330 putFile8Bit(file, ei->push_delay_fixed);
7331 putFile8Bit(file, ei->drop_delay_random);
7332 putFile8Bit(file, ei->push_delay_random);
7333 putFile16BitBE(file, ei->move_delay_fixed);
7334 putFile16BitBE(file, ei->move_delay_random);
7336 // bits 0 - 15 of "move_pattern" ...
7337 putFile16BitBE(file, ei->move_pattern & 0xffff);
7338 putFile8Bit(file, ei->move_direction_initial);
7339 putFile8Bit(file, ei->move_stepsize);
7341 putFile8Bit(file, ei->slippery_type);
7343 for (y = 0; y < 3; y++)
7344 for (x = 0; x < 3; x++)
7345 putFile16BitBE(file, ei->content.e[x][y]);
7347 putFile16BitBE(file, ei->move_enter_element);
7348 putFile16BitBE(file, ei->move_leave_element);
7349 putFile8Bit(file, ei->move_leave_type);
7351 // ... bits 16 - 31 of "move_pattern" (not nice, but downward compatible)
7352 putFile16BitBE(file, (ei->move_pattern >> 16) & 0xffff);
7354 putFile8Bit(file, ei->access_direction);
7356 putFile8Bit(file, ei->explosion_delay);
7357 putFile8Bit(file, ei->ignition_delay);
7358 putFile8Bit(file, ei->explosion_type);
7360 // some free bytes for future custom property values and padding
7361 WriteUnusedBytesToFile(file, 1);
7363 // ---------- change page property values (48 bytes) ------------------------
7365 for (i = 0; i < ei->num_change_pages; i++)
7367 struct ElementChangeInfo *change = &ei->change_page[i];
7368 unsigned int event_bits;
7370 // bits 0 - 31 of "has_event[]" ...
7372 for (j = 0; j < MIN(NUM_CHANGE_EVENTS, 32); j++)
7373 if (change->has_event[j])
7374 event_bits |= (1u << j);
7375 putFile32BitBE(file, event_bits);
7377 putFile16BitBE(file, change->target_element);
7379 putFile16BitBE(file, change->delay_fixed);
7380 putFile16BitBE(file, change->delay_random);
7381 putFile16BitBE(file, change->delay_frames);
7383 putFile16BitBE(file, change->initial_trigger_element);
7385 putFile8Bit(file, change->explode);
7386 putFile8Bit(file, change->use_target_content);
7387 putFile8Bit(file, change->only_if_complete);
7388 putFile8Bit(file, change->use_random_replace);
7390 putFile8Bit(file, change->random_percentage);
7391 putFile8Bit(file, change->replace_when);
7393 for (y = 0; y < 3; y++)
7394 for (x = 0; x < 3; x++)
7395 putFile16BitBE(file, change->target_content.e[x][y]);
7397 putFile8Bit(file, change->can_change);
7399 putFile8Bit(file, change->trigger_side);
7401 putFile8Bit(file, change->trigger_player);
7402 putFile8Bit(file, (change->trigger_page == CH_PAGE_ANY ? CH_PAGE_ANY_FILE :
7403 log_2(change->trigger_page)));
7405 putFile8Bit(file, change->has_action);
7406 putFile8Bit(file, change->action_type);
7407 putFile8Bit(file, change->action_mode);
7408 putFile16BitBE(file, change->action_arg);
7410 // ... bits 32 - 39 of "has_event[]" (not nice, but downward compatible)
7412 for (j = 32; j < NUM_CHANGE_EVENTS; j++)
7413 if (change->has_event[j])
7414 event_bits |= (1u << (j - 32));
7415 putFile8Bit(file, event_bits);
7420 #if ENABLE_HISTORIC_CHUNKS
7421 static void SaveLevel_GRP1(FILE *file, struct LevelInfo *level, int element)
7423 struct ElementInfo *ei = &element_info[element];
7424 struct ElementGroupInfo *group = ei->group;
7427 putFile16BitBE(file, element);
7429 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
7430 putFile8Bit(file, ei->description[i]);
7432 putFile8Bit(file, group->num_elements);
7434 putFile8Bit(file, ei->use_gfx_element);
7435 putFile16BitBE(file, ei->gfx_element_initial);
7437 putFile8Bit(file, group->choice_mode);
7439 // some free bytes for future values and padding
7440 WriteUnusedBytesToFile(file, 3);
7442 for (i = 0; i < MAX_ELEMENTS_IN_GROUP; i++)
7443 putFile16BitBE(file, group->element[i]);
7447 static int SaveLevel_MicroChunk(FILE *file, struct LevelFileConfigInfo *entry,
7448 boolean write_element)
7450 int save_type = entry->save_type;
7451 int data_type = entry->data_type;
7452 int conf_type = entry->conf_type;
7453 int byte_mask = conf_type & CONF_MASK_BYTES;
7454 int element = entry->element;
7455 int default_value = entry->default_value;
7457 boolean modified = FALSE;
7459 if (byte_mask != CONF_MASK_MULTI_BYTES)
7461 void *value_ptr = entry->value;
7462 int value = (data_type == TYPE_BOOLEAN ? *(boolean *)value_ptr :
7465 // check if any settings have been modified before saving them
7466 if (value != default_value)
7469 // do not save if explicitly told or if unmodified default settings
7470 if ((save_type == SAVE_CONF_NEVER) ||
7471 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
7475 num_bytes += putFile16BitBE(file, element);
7477 num_bytes += putFile8Bit(file, conf_type);
7478 num_bytes += (byte_mask == CONF_MASK_1_BYTE ? putFile8Bit (file, value) :
7479 byte_mask == CONF_MASK_2_BYTE ? putFile16BitBE(file, value) :
7480 byte_mask == CONF_MASK_4_BYTE ? putFile32BitBE(file, value) :
7483 else if (data_type == TYPE_STRING)
7485 char *default_string = entry->default_string;
7486 char *string = (char *)(entry->value);
7487 int string_length = strlen(string);
7490 // check if any settings have been modified before saving them
7491 if (!strEqual(string, default_string))
7494 // do not save if explicitly told or if unmodified default settings
7495 if ((save_type == SAVE_CONF_NEVER) ||
7496 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
7500 num_bytes += putFile16BitBE(file, element);
7502 num_bytes += putFile8Bit(file, conf_type);
7503 num_bytes += putFile16BitBE(file, string_length);
7505 for (i = 0; i < string_length; i++)
7506 num_bytes += putFile8Bit(file, string[i]);
7508 else if (data_type == TYPE_ELEMENT_LIST)
7510 int *element_array = (int *)(entry->value);
7511 int num_elements = *(int *)(entry->num_entities);
7514 // check if any settings have been modified before saving them
7515 for (i = 0; i < num_elements; i++)
7516 if (element_array[i] != default_value)
7519 // do not save if explicitly told or if unmodified default settings
7520 if ((save_type == SAVE_CONF_NEVER) ||
7521 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
7525 num_bytes += putFile16BitBE(file, element);
7527 num_bytes += putFile8Bit(file, conf_type);
7528 num_bytes += putFile16BitBE(file, num_elements * CONF_ELEMENT_NUM_BYTES);
7530 for (i = 0; i < num_elements; i++)
7531 num_bytes += putFile16BitBE(file, element_array[i]);
7533 else if (data_type == TYPE_CONTENT_LIST)
7535 struct Content *content = (struct Content *)(entry->value);
7536 int num_contents = *(int *)(entry->num_entities);
7539 // check if any settings have been modified before saving them
7540 for (i = 0; i < num_contents; i++)
7541 for (y = 0; y < 3; y++)
7542 for (x = 0; x < 3; x++)
7543 if (content[i].e[x][y] != default_value)
7546 // do not save if explicitly told or if unmodified default settings
7547 if ((save_type == SAVE_CONF_NEVER) ||
7548 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
7552 num_bytes += putFile16BitBE(file, element);
7554 num_bytes += putFile8Bit(file, conf_type);
7555 num_bytes += putFile16BitBE(file, num_contents * CONF_CONTENT_NUM_BYTES);
7557 for (i = 0; i < num_contents; i++)
7558 for (y = 0; y < 3; y++)
7559 for (x = 0; x < 3; x++)
7560 num_bytes += putFile16BitBE(file, content[i].e[x][y]);
7566 static int SaveLevel_INFO(FILE *file, struct LevelInfo *level)
7571 li = *level; // copy level data into temporary buffer
7573 for (i = 0; chunk_config_INFO[i].data_type != -1; i++)
7574 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_INFO[i], FALSE);
7579 static int SaveLevel_ELEM(FILE *file, struct LevelInfo *level)
7584 li = *level; // copy level data into temporary buffer
7586 for (i = 0; chunk_config_ELEM[i].data_type != -1; i++)
7587 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_ELEM[i], TRUE);
7592 static int SaveLevel_NOTE(FILE *file, struct LevelInfo *level, int element)
7594 int envelope_nr = element - EL_ENVELOPE_1;
7598 chunk_size += putFile16BitBE(file, element);
7600 // copy envelope data into temporary buffer
7601 xx_envelope = level->envelope[envelope_nr];
7603 for (i = 0; chunk_config_NOTE[i].data_type != -1; i++)
7604 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_NOTE[i], FALSE);
7609 static int SaveLevel_CUSX(FILE *file, struct LevelInfo *level, int element)
7611 struct ElementInfo *ei = &element_info[element];
7615 chunk_size += putFile16BitBE(file, element);
7617 xx_ei = *ei; // copy element data into temporary buffer
7619 // set default description string for this specific element
7620 strcpy(xx_default_description, getDefaultElementDescription(ei));
7622 for (i = 0; chunk_config_CUSX_base[i].data_type != -1; i++)
7623 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_CUSX_base[i], FALSE);
7625 for (i = 0; i < ei->num_change_pages; i++)
7627 struct ElementChangeInfo *change = &ei->change_page[i];
7629 xx_current_change_page = i;
7631 xx_change = *change; // copy change data into temporary buffer
7634 setEventBitsFromEventFlags(change);
7636 for (j = 0; chunk_config_CUSX_change[j].data_type != -1; j++)
7637 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_CUSX_change[j],
7644 static int SaveLevel_GRPX(FILE *file, struct LevelInfo *level, int element)
7646 struct ElementInfo *ei = &element_info[element];
7647 struct ElementGroupInfo *group = ei->group;
7651 chunk_size += putFile16BitBE(file, element);
7653 xx_ei = *ei; // copy element data into temporary buffer
7654 xx_group = *group; // copy group data into temporary buffer
7656 // set default description string for this specific element
7657 strcpy(xx_default_description, getDefaultElementDescription(ei));
7659 for (i = 0; chunk_config_GRPX[i].data_type != -1; i++)
7660 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_GRPX[i], FALSE);
7665 static int SaveLevel_EMPX(FILE *file, struct LevelInfo *level, int element)
7667 struct ElementInfo *ei = &element_info[element];
7671 chunk_size += putFile16BitBE(file, element);
7673 xx_ei = *ei; // copy element data into temporary buffer
7675 for (i = 0; chunk_config_EMPX[i].data_type != -1; i++)
7676 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_EMPX[i], FALSE);
7681 static void SaveLevelFromFilename(struct LevelInfo *level, char *filename,
7682 boolean save_as_template)
7688 if (!(file = fopen(filename, MODE_WRITE)))
7690 Warn("cannot save level file '%s'", filename);
7695 level->file_version = FILE_VERSION_ACTUAL;
7696 level->game_version = GAME_VERSION_ACTUAL;
7698 level->creation_date = getCurrentDate();
7700 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
7701 putFileChunkBE(file, "CAVE", CHUNK_SIZE_NONE);
7703 chunk_size = SaveLevel_VERS(NULL, level);
7704 putFileChunkBE(file, "VERS", chunk_size);
7705 SaveLevel_VERS(file, level);
7707 chunk_size = SaveLevel_DATE(NULL, level);
7708 putFileChunkBE(file, "DATE", chunk_size);
7709 SaveLevel_DATE(file, level);
7711 chunk_size = SaveLevel_NAME(NULL, level);
7712 putFileChunkBE(file, "NAME", chunk_size);
7713 SaveLevel_NAME(file, level);
7715 chunk_size = SaveLevel_AUTH(NULL, level);
7716 putFileChunkBE(file, "AUTH", chunk_size);
7717 SaveLevel_AUTH(file, level);
7719 chunk_size = SaveLevel_INFO(NULL, level);
7720 putFileChunkBE(file, "INFO", chunk_size);
7721 SaveLevel_INFO(file, level);
7723 chunk_size = SaveLevel_BODY(NULL, level);
7724 putFileChunkBE(file, "BODY", chunk_size);
7725 SaveLevel_BODY(file, level);
7727 chunk_size = SaveLevel_ELEM(NULL, level);
7728 if (chunk_size > LEVEL_CHUNK_ELEM_UNCHANGED) // save if changed
7730 putFileChunkBE(file, "ELEM", chunk_size);
7731 SaveLevel_ELEM(file, level);
7734 for (i = 0; i < NUM_ENVELOPES; i++)
7736 int element = EL_ENVELOPE_1 + i;
7738 chunk_size = SaveLevel_NOTE(NULL, level, element);
7739 if (chunk_size > LEVEL_CHUNK_NOTE_UNCHANGED) // save if changed
7741 putFileChunkBE(file, "NOTE", chunk_size);
7742 SaveLevel_NOTE(file, level, element);
7746 // if not using template level, check for non-default custom/group elements
7747 if (!level->use_custom_template || save_as_template)
7749 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7751 int element = EL_CUSTOM_START + i;
7753 chunk_size = SaveLevel_CUSX(NULL, level, element);
7754 if (chunk_size > LEVEL_CHUNK_CUSX_UNCHANGED) // save if changed
7756 putFileChunkBE(file, "CUSX", chunk_size);
7757 SaveLevel_CUSX(file, level, element);
7761 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
7763 int element = EL_GROUP_START + i;
7765 chunk_size = SaveLevel_GRPX(NULL, level, element);
7766 if (chunk_size > LEVEL_CHUNK_GRPX_UNCHANGED) // save if changed
7768 putFileChunkBE(file, "GRPX", chunk_size);
7769 SaveLevel_GRPX(file, level, element);
7773 for (i = 0; i < NUM_EMPTY_ELEMENTS_ALL; i++)
7775 int element = GET_EMPTY_ELEMENT(i);
7777 chunk_size = SaveLevel_EMPX(NULL, level, element);
7778 if (chunk_size > LEVEL_CHUNK_EMPX_UNCHANGED) // save if changed
7780 putFileChunkBE(file, "EMPX", chunk_size);
7781 SaveLevel_EMPX(file, level, element);
7788 SetFilePermissions(filename, PERMS_PRIVATE);
7791 void SaveLevel(int nr)
7793 char *filename = getDefaultLevelFilename(nr);
7795 SaveLevelFromFilename(&level, filename, FALSE);
7798 void SaveLevelTemplate(void)
7800 char *filename = getLocalLevelTemplateFilename();
7802 SaveLevelFromFilename(&level, filename, TRUE);
7805 boolean SaveLevelChecked(int nr)
7807 char *filename = getDefaultLevelFilename(nr);
7808 boolean new_level = !fileExists(filename);
7809 boolean level_saved = FALSE;
7811 if (new_level || Request("Save this level and kill the old?", REQ_ASK))
7816 Request("Level saved!", REQ_CONFIRM);
7824 void DumpLevel(struct LevelInfo *level)
7826 if (level->no_level_file || level->no_valid_file)
7828 Warn("cannot dump -- no valid level file found");
7834 Print("Level xxx (file version %08d, game version %08d)\n",
7835 level->file_version, level->game_version);
7838 Print("Level author: '%s'\n", level->author);
7839 Print("Level title: '%s'\n", level->name);
7841 Print("Playfield size: %d x %d\n", level->fieldx, level->fieldy);
7843 Print("Level time: %d seconds\n", level->time);
7844 Print("Gems needed: %d\n", level->gems_needed);
7846 Print("Time for magic wall: %d seconds\n", level->time_magic_wall);
7847 Print("Time for wheel: %d seconds\n", level->time_wheel);
7848 Print("Time for light: %d seconds\n", level->time_light);
7849 Print("Time for timegate: %d seconds\n", level->time_timegate);
7851 Print("Amoeba speed: %d\n", level->amoeba_speed);
7854 Print("EM style slippery gems: %s\n", (level->em_slippery_gems ? "yes" : "no"));
7855 Print("Player blocks last field: %s\n", (level->block_last_field ? "yes" : "no"));
7856 Print("SP player blocks last field: %s\n", (level->sp_block_last_field ? "yes" : "no"));
7857 Print("use spring bug: %s\n", (level->use_spring_bug ? "yes" : "no"));
7858 Print("use step counter: %s\n", (level->use_step_counter ? "yes" : "no"));
7859 Print("rate time over score: %s\n", (level->rate_time_over_score ? "yes" : "no"));
7865 for (i = 0; i < NUM_ENVELOPES; i++)
7867 char *text = level->envelope[i].text;
7868 int text_len = strlen(text);
7869 boolean has_text = FALSE;
7871 for (j = 0; j < text_len; j++)
7872 if (text[j] != ' ' && text[j] != '\n')
7878 Print("Envelope %d:\n'%s'\n", i + 1, text);
7886 void DumpLevels(void)
7888 static LevelDirTree *dumplevel_leveldir = NULL;
7890 dumplevel_leveldir = getTreeInfoFromIdentifier(leveldir_first,
7891 global.dumplevel_leveldir);
7893 if (dumplevel_leveldir == NULL)
7894 Fail("no such level identifier: '%s'", global.dumplevel_leveldir);
7896 if (global.dumplevel_level_nr < dumplevel_leveldir->first_level ||
7897 global.dumplevel_level_nr > dumplevel_leveldir->last_level)
7898 Fail("no such level number: %d", global.dumplevel_level_nr);
7900 leveldir_current = dumplevel_leveldir;
7902 LoadLevel(global.dumplevel_level_nr);
7909 // ============================================================================
7910 // tape file functions
7911 // ============================================================================
7913 static void setTapeInfoToDefaults(void)
7917 // always start with reliable default values (empty tape)
7920 // default values (also for pre-1.2 tapes) with only the first player
7921 tape.player_participates[0] = TRUE;
7922 for (i = 1; i < MAX_PLAYERS; i++)
7923 tape.player_participates[i] = FALSE;
7925 // at least one (default: the first) player participates in every tape
7926 tape.num_participating_players = 1;
7928 tape.property_bits = TAPE_PROPERTY_NONE;
7930 tape.level_nr = level_nr;
7932 tape.changed = FALSE;
7933 tape.solved = FALSE;
7935 tape.recording = FALSE;
7936 tape.playing = FALSE;
7937 tape.pausing = FALSE;
7939 tape.scr_fieldx = SCR_FIELDX_DEFAULT;
7940 tape.scr_fieldy = SCR_FIELDY_DEFAULT;
7942 tape.no_info_chunk = TRUE;
7943 tape.no_valid_file = FALSE;
7946 static int getTapePosSize(struct TapeInfo *tape)
7948 int tape_pos_size = 0;
7950 if (tape->use_key_actions)
7951 tape_pos_size += tape->num_participating_players;
7953 if (tape->use_mouse_actions)
7954 tape_pos_size += 3; // x and y position and mouse button mask
7956 tape_pos_size += 1; // tape action delay value
7958 return tape_pos_size;
7961 static void setTapeActionFlags(struct TapeInfo *tape, int value)
7963 tape->use_key_actions = FALSE;
7964 tape->use_mouse_actions = FALSE;
7966 if (value != TAPE_USE_MOUSE_ACTIONS_ONLY)
7967 tape->use_key_actions = TRUE;
7969 if (value != TAPE_USE_KEY_ACTIONS_ONLY)
7970 tape->use_mouse_actions = TRUE;
7973 static int getTapeActionValue(struct TapeInfo *tape)
7975 return (tape->use_key_actions &&
7976 tape->use_mouse_actions ? TAPE_USE_KEY_AND_MOUSE_ACTIONS :
7977 tape->use_key_actions ? TAPE_USE_KEY_ACTIONS_ONLY :
7978 tape->use_mouse_actions ? TAPE_USE_MOUSE_ACTIONS_ONLY :
7979 TAPE_ACTIONS_DEFAULT);
7982 static int LoadTape_VERS(File *file, int chunk_size, struct TapeInfo *tape)
7984 tape->file_version = getFileVersion(file);
7985 tape->game_version = getFileVersion(file);
7990 static int LoadTape_HEAD(File *file, int chunk_size, struct TapeInfo *tape)
7994 tape->random_seed = getFile32BitBE(file);
7995 tape->date = getFile32BitBE(file);
7996 tape->length = getFile32BitBE(file);
7998 // read header fields that are new since version 1.2
7999 if (tape->file_version >= FILE_VERSION_1_2)
8001 byte store_participating_players = getFile8Bit(file);
8004 // since version 1.2, tapes store which players participate in the tape
8005 tape->num_participating_players = 0;
8006 for (i = 0; i < MAX_PLAYERS; i++)
8008 tape->player_participates[i] = FALSE;
8010 if (store_participating_players & (1 << i))
8012 tape->player_participates[i] = TRUE;
8013 tape->num_participating_players++;
8017 setTapeActionFlags(tape, getFile8Bit(file));
8019 tape->property_bits = getFile8Bit(file);
8020 tape->solved = getFile8Bit(file);
8022 engine_version = getFileVersion(file);
8023 if (engine_version > 0)
8024 tape->engine_version = engine_version;
8026 tape->engine_version = tape->game_version;
8032 static int LoadTape_SCRN(File *file, int chunk_size, struct TapeInfo *tape)
8034 tape->scr_fieldx = getFile8Bit(file);
8035 tape->scr_fieldy = getFile8Bit(file);
8040 static int LoadTape_INFO(File *file, int chunk_size, struct TapeInfo *tape)
8042 char *level_identifier = NULL;
8043 int level_identifier_size;
8046 tape->no_info_chunk = FALSE;
8048 level_identifier_size = getFile16BitBE(file);
8050 level_identifier = checked_malloc(level_identifier_size);
8052 for (i = 0; i < level_identifier_size; i++)
8053 level_identifier[i] = getFile8Bit(file);
8055 strncpy(tape->level_identifier, level_identifier, MAX_FILENAME_LEN);
8056 tape->level_identifier[MAX_FILENAME_LEN] = '\0';
8058 checked_free(level_identifier);
8060 tape->level_nr = getFile16BitBE(file);
8062 chunk_size = 2 + level_identifier_size + 2;
8067 static int LoadTape_BODY(File *file, int chunk_size, struct TapeInfo *tape)
8070 int tape_pos_size = getTapePosSize(tape);
8071 int chunk_size_expected = tape_pos_size * tape->length;
8073 if (chunk_size_expected != chunk_size)
8075 ReadUnusedBytesFromFile(file, chunk_size);
8076 return chunk_size_expected;
8079 for (i = 0; i < tape->length; i++)
8081 if (i >= MAX_TAPE_LEN)
8083 Warn("tape truncated -- size exceeds maximum tape size %d",
8086 // tape too large; read and ignore remaining tape data from this chunk
8087 for (;i < tape->length; i++)
8088 ReadUnusedBytesFromFile(file, tape_pos_size);
8093 if (tape->use_key_actions)
8095 for (j = 0; j < MAX_PLAYERS; j++)
8097 tape->pos[i].action[j] = MV_NONE;
8099 if (tape->player_participates[j])
8100 tape->pos[i].action[j] = getFile8Bit(file);
8104 if (tape->use_mouse_actions)
8106 tape->pos[i].action[TAPE_ACTION_LX] = getFile8Bit(file);
8107 tape->pos[i].action[TAPE_ACTION_LY] = getFile8Bit(file);
8108 tape->pos[i].action[TAPE_ACTION_BUTTON] = getFile8Bit(file);
8111 tape->pos[i].delay = getFile8Bit(file);
8113 if (tape->file_version == FILE_VERSION_1_0)
8115 // eliminate possible diagonal moves in old tapes
8116 // this is only for backward compatibility
8118 byte joy_dir[4] = { JOY_LEFT, JOY_RIGHT, JOY_UP, JOY_DOWN };
8119 byte action = tape->pos[i].action[0];
8120 int k, num_moves = 0;
8122 for (k = 0; k<4; k++)
8124 if (action & joy_dir[k])
8126 tape->pos[i + num_moves].action[0] = joy_dir[k];
8128 tape->pos[i + num_moves].delay = 0;
8137 tape->length += num_moves;
8140 else if (tape->file_version < FILE_VERSION_2_0)
8142 // convert pre-2.0 tapes to new tape format
8144 if (tape->pos[i].delay > 1)
8147 tape->pos[i + 1] = tape->pos[i];
8148 tape->pos[i + 1].delay = 1;
8151 for (j = 0; j < MAX_PLAYERS; j++)
8152 tape->pos[i].action[j] = MV_NONE;
8153 tape->pos[i].delay--;
8160 if (checkEndOfFile(file))
8164 if (i != tape->length)
8165 chunk_size = tape_pos_size * i;
8170 static void LoadTape_SokobanSolution(char *filename)
8173 int move_delay = TILESIZE / level.initial_player_stepsize[0];
8175 if (!(file = openFile(filename, MODE_READ)))
8177 tape.no_valid_file = TRUE;
8182 while (!checkEndOfFile(file))
8184 unsigned char c = getByteFromFile(file);
8186 if (checkEndOfFile(file))
8193 tape.pos[tape.length].action[0] = MV_UP;
8194 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
8200 tape.pos[tape.length].action[0] = MV_DOWN;
8201 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
8207 tape.pos[tape.length].action[0] = MV_LEFT;
8208 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
8214 tape.pos[tape.length].action[0] = MV_RIGHT;
8215 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
8223 // ignore white-space characters
8227 tape.no_valid_file = TRUE;
8229 Warn("unsupported Sokoban solution file '%s' ['%d']", filename, c);
8237 if (tape.no_valid_file)
8240 tape.length_frames = GetTapeLengthFrames();
8241 tape.length_seconds = GetTapeLengthSeconds();
8244 void LoadTapeFromFilename(char *filename)
8246 char cookie[MAX_LINE_LEN];
8247 char chunk_name[CHUNK_ID_LEN + 1];
8251 // always start with reliable default values
8252 setTapeInfoToDefaults();
8254 if (strSuffix(filename, ".sln"))
8256 LoadTape_SokobanSolution(filename);
8261 if (!(file = openFile(filename, MODE_READ)))
8263 tape.no_valid_file = TRUE;
8268 getFileChunkBE(file, chunk_name, NULL);
8269 if (strEqual(chunk_name, "RND1"))
8271 getFile32BitBE(file); // not used
8273 getFileChunkBE(file, chunk_name, NULL);
8274 if (!strEqual(chunk_name, "TAPE"))
8276 tape.no_valid_file = TRUE;
8278 Warn("unknown format of tape file '%s'", filename);
8285 else // check for pre-2.0 file format with cookie string
8287 strcpy(cookie, chunk_name);
8288 if (getStringFromFile(file, &cookie[4], MAX_LINE_LEN - 4) == NULL)
8290 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
8291 cookie[strlen(cookie) - 1] = '\0';
8293 if (!checkCookieString(cookie, TAPE_COOKIE_TMPL))
8295 tape.no_valid_file = TRUE;
8297 Warn("unknown format of tape file '%s'", filename);
8304 if ((tape.file_version = getFileVersionFromCookieString(cookie)) == -1)
8306 tape.no_valid_file = TRUE;
8308 Warn("unsupported version of tape file '%s'", filename);
8315 // pre-2.0 tape files have no game version, so use file version here
8316 tape.game_version = tape.file_version;
8319 if (tape.file_version < FILE_VERSION_1_2)
8321 // tape files from versions before 1.2.0 without chunk structure
8322 LoadTape_HEAD(file, TAPE_CHUNK_HEAD_SIZE, &tape);
8323 LoadTape_BODY(file, 2 * tape.length, &tape);
8331 int (*loader)(File *, int, struct TapeInfo *);
8335 { "VERS", TAPE_CHUNK_VERS_SIZE, LoadTape_VERS },
8336 { "HEAD", TAPE_CHUNK_HEAD_SIZE, LoadTape_HEAD },
8337 { "SCRN", TAPE_CHUNK_SCRN_SIZE, LoadTape_SCRN },
8338 { "INFO", -1, LoadTape_INFO },
8339 { "BODY", -1, LoadTape_BODY },
8343 while (getFileChunkBE(file, chunk_name, &chunk_size))
8347 while (chunk_info[i].name != NULL &&
8348 !strEqual(chunk_name, chunk_info[i].name))
8351 if (chunk_info[i].name == NULL)
8353 Warn("unknown chunk '%s' in tape file '%s'",
8354 chunk_name, filename);
8356 ReadUnusedBytesFromFile(file, chunk_size);
8358 else if (chunk_info[i].size != -1 &&
8359 chunk_info[i].size != chunk_size)
8361 Warn("wrong size (%d) of chunk '%s' in tape file '%s'",
8362 chunk_size, chunk_name, filename);
8364 ReadUnusedBytesFromFile(file, chunk_size);
8368 // call function to load this tape chunk
8369 int chunk_size_expected =
8370 (chunk_info[i].loader)(file, chunk_size, &tape);
8372 // the size of some chunks cannot be checked before reading other
8373 // chunks first (like "HEAD" and "BODY") that contain some header
8374 // information, so check them here
8375 if (chunk_size_expected != chunk_size)
8377 Warn("wrong size (%d) of chunk '%s' in tape file '%s'",
8378 chunk_size, chunk_name, filename);
8386 tape.length_frames = GetTapeLengthFrames();
8387 tape.length_seconds = GetTapeLengthSeconds();
8390 Debug("files:LoadTapeFromFilename", "tape file version: %d",
8392 Debug("files:LoadTapeFromFilename", "tape game version: %d",
8394 Debug("files:LoadTapeFromFilename", "tape engine version: %d",
8395 tape.engine_version);
8399 void LoadTape(int nr)
8401 char *filename = getTapeFilename(nr);
8403 LoadTapeFromFilename(filename);
8406 void LoadSolutionTape(int nr)
8408 char *filename = getSolutionTapeFilename(nr);
8410 LoadTapeFromFilename(filename);
8412 if (TAPE_IS_EMPTY(tape) &&
8413 level.game_engine_type == GAME_ENGINE_TYPE_SP &&
8414 level.native_sp_level->demo.is_available)
8415 CopyNativeTape_SP_to_RND(&level);
8418 void LoadScoreTape(char *score_tape_basename, int nr)
8420 char *filename = getScoreTapeFilename(score_tape_basename, nr);
8422 LoadTapeFromFilename(filename);
8425 void LoadScoreCacheTape(char *score_tape_basename, int nr)
8427 char *filename = getScoreCacheTapeFilename(score_tape_basename, nr);
8429 LoadTapeFromFilename(filename);
8432 static boolean checkSaveTape_SCRN(struct TapeInfo *tape)
8434 // chunk required for team mode tapes with non-default screen size
8435 return (tape->num_participating_players > 1 &&
8436 (tape->scr_fieldx != SCR_FIELDX_DEFAULT ||
8437 tape->scr_fieldy != SCR_FIELDY_DEFAULT));
8440 static void SaveTape_VERS(FILE *file, struct TapeInfo *tape)
8442 putFileVersion(file, tape->file_version);
8443 putFileVersion(file, tape->game_version);
8446 static void SaveTape_HEAD(FILE *file, struct TapeInfo *tape)
8449 byte store_participating_players = 0;
8451 // set bits for participating players for compact storage
8452 for (i = 0; i < MAX_PLAYERS; i++)
8453 if (tape->player_participates[i])
8454 store_participating_players |= (1 << i);
8456 putFile32BitBE(file, tape->random_seed);
8457 putFile32BitBE(file, tape->date);
8458 putFile32BitBE(file, tape->length);
8460 putFile8Bit(file, store_participating_players);
8462 putFile8Bit(file, getTapeActionValue(tape));
8464 putFile8Bit(file, tape->property_bits);
8465 putFile8Bit(file, tape->solved);
8467 putFileVersion(file, tape->engine_version);
8470 static void SaveTape_SCRN(FILE *file, struct TapeInfo *tape)
8472 putFile8Bit(file, tape->scr_fieldx);
8473 putFile8Bit(file, tape->scr_fieldy);
8476 static void SaveTape_INFO(FILE *file, struct TapeInfo *tape)
8478 int level_identifier_size = strlen(tape->level_identifier) + 1;
8481 putFile16BitBE(file, level_identifier_size);
8483 for (i = 0; i < level_identifier_size; i++)
8484 putFile8Bit(file, tape->level_identifier[i]);
8486 putFile16BitBE(file, tape->level_nr);
8489 static void SaveTape_BODY(FILE *file, struct TapeInfo *tape)
8493 for (i = 0; i < tape->length; i++)
8495 if (tape->use_key_actions)
8497 for (j = 0; j < MAX_PLAYERS; j++)
8498 if (tape->player_participates[j])
8499 putFile8Bit(file, tape->pos[i].action[j]);
8502 if (tape->use_mouse_actions)
8504 putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_LX]);
8505 putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_LY]);
8506 putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_BUTTON]);
8509 putFile8Bit(file, tape->pos[i].delay);
8513 void SaveTapeToFilename(char *filename)
8517 int info_chunk_size;
8518 int body_chunk_size;
8520 if (!(file = fopen(filename, MODE_WRITE)))
8522 Warn("cannot save level recording file '%s'", filename);
8527 tape_pos_size = getTapePosSize(&tape);
8529 info_chunk_size = 2 + (strlen(tape.level_identifier) + 1) + 2;
8530 body_chunk_size = tape_pos_size * tape.length;
8532 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
8533 putFileChunkBE(file, "TAPE", CHUNK_SIZE_NONE);
8535 putFileChunkBE(file, "VERS", TAPE_CHUNK_VERS_SIZE);
8536 SaveTape_VERS(file, &tape);
8538 putFileChunkBE(file, "HEAD", TAPE_CHUNK_HEAD_SIZE);
8539 SaveTape_HEAD(file, &tape);
8541 if (checkSaveTape_SCRN(&tape))
8543 putFileChunkBE(file, "SCRN", TAPE_CHUNK_SCRN_SIZE);
8544 SaveTape_SCRN(file, &tape);
8547 putFileChunkBE(file, "INFO", info_chunk_size);
8548 SaveTape_INFO(file, &tape);
8550 putFileChunkBE(file, "BODY", body_chunk_size);
8551 SaveTape_BODY(file, &tape);
8555 SetFilePermissions(filename, PERMS_PRIVATE);
8558 static void SaveTapeExt(char *filename)
8562 tape.file_version = FILE_VERSION_ACTUAL;
8563 tape.game_version = GAME_VERSION_ACTUAL;
8565 tape.num_participating_players = 0;
8567 // count number of participating players
8568 for (i = 0; i < MAX_PLAYERS; i++)
8569 if (tape.player_participates[i])
8570 tape.num_participating_players++;
8572 SaveTapeToFilename(filename);
8574 tape.changed = FALSE;
8577 void SaveTape(int nr)
8579 char *filename = getTapeFilename(nr);
8581 InitTapeDirectory(leveldir_current->subdir);
8583 SaveTapeExt(filename);
8586 void SaveScoreTape(int nr)
8588 char *filename = getScoreTapeFilename(tape.score_tape_basename, nr);
8590 // used instead of "leveldir_current->subdir" (for network games)
8591 InitScoreTapeDirectory(levelset.identifier, nr);
8593 SaveTapeExt(filename);
8596 static boolean SaveTapeCheckedExt(int nr, char *msg_replace, char *msg_saved,
8597 unsigned int req_state_added)
8599 char *filename = getTapeFilename(nr);
8600 boolean new_tape = !fileExists(filename);
8601 boolean tape_saved = FALSE;
8603 if (new_tape || Request(msg_replace, REQ_ASK | req_state_added))
8608 Request(msg_saved, REQ_CONFIRM | req_state_added);
8616 boolean SaveTapeChecked(int nr)
8618 return SaveTapeCheckedExt(nr, "Replace old tape?", "Tape saved!", 0);
8621 boolean SaveTapeChecked_LevelSolved(int nr)
8623 return SaveTapeCheckedExt(nr, "Level solved! Replace old tape?",
8624 "Level solved! Tape saved!", REQ_STAY_OPEN);
8627 void DumpTape(struct TapeInfo *tape)
8629 int tape_frame_counter;
8632 if (tape->no_valid_file)
8634 Warn("cannot dump -- no valid tape file found");
8641 Print("Tape of Level %03d (file version %08d, game version %08d)\n",
8642 tape->level_nr, tape->file_version, tape->game_version);
8643 Print(" (effective engine version %08d)\n",
8644 tape->engine_version);
8645 Print("Level series identifier: '%s'\n", tape->level_identifier);
8647 Print("Solution tape: %s\n",
8648 tape->solved ? "yes" :
8649 tape->game_version < VERSION_IDENT(4,3,2,3) ? "unknown" : "no");
8651 Print("Special tape properties: ");
8652 if (tape->property_bits == TAPE_PROPERTY_NONE)
8654 if (tape->property_bits & TAPE_PROPERTY_EM_RANDOM_BUG)
8655 Print("[em_random_bug]");
8656 if (tape->property_bits & TAPE_PROPERTY_GAME_SPEED)
8657 Print("[game_speed]");
8658 if (tape->property_bits & TAPE_PROPERTY_PAUSE_MODE)
8660 if (tape->property_bits & TAPE_PROPERTY_SINGLE_STEP)
8661 Print("[single_step]");
8662 if (tape->property_bits & TAPE_PROPERTY_SNAPSHOT)
8663 Print("[snapshot]");
8664 if (tape->property_bits & TAPE_PROPERTY_REPLAYED)
8665 Print("[replayed]");
8666 if (tape->property_bits & TAPE_PROPERTY_TAS_KEYS)
8667 Print("[tas_keys]");
8668 if (tape->property_bits & TAPE_PROPERTY_SMALL_GRAPHICS)
8669 Print("[small_graphics]");
8672 int year2 = tape->date / 10000;
8673 int year4 = (year2 < 70 ? 2000 + year2 : 1900 + year2);
8674 int month_index_raw = (tape->date / 100) % 100;
8675 int month_index = month_index_raw % 12; // prevent invalid index
8676 int month = month_index + 1;
8677 int day = tape->date % 100;
8679 Print("Tape date: %04d-%02d-%02d\n", year4, month, day);
8683 tape_frame_counter = 0;
8685 for (i = 0; i < tape->length; i++)
8687 if (i >= MAX_TAPE_LEN)
8692 for (j = 0; j < MAX_PLAYERS; j++)
8694 if (tape->player_participates[j])
8696 int action = tape->pos[i].action[j];
8698 Print("%d:%02x ", j, action);
8699 Print("[%c%c%c%c|%c%c] - ",
8700 (action & JOY_LEFT ? '<' : ' '),
8701 (action & JOY_RIGHT ? '>' : ' '),
8702 (action & JOY_UP ? '^' : ' '),
8703 (action & JOY_DOWN ? 'v' : ' '),
8704 (action & JOY_BUTTON_1 ? '1' : ' '),
8705 (action & JOY_BUTTON_2 ? '2' : ' '));
8709 Print("(%03d) ", tape->pos[i].delay);
8710 Print("[%05d]\n", tape_frame_counter);
8712 tape_frame_counter += tape->pos[i].delay;
8718 void DumpTapes(void)
8720 static LevelDirTree *dumptape_leveldir = NULL;
8722 dumptape_leveldir = getTreeInfoFromIdentifier(leveldir_first,
8723 global.dumptape_leveldir);
8725 if (dumptape_leveldir == NULL)
8726 Fail("no such level identifier: '%s'", global.dumptape_leveldir);
8728 if (global.dumptape_level_nr < dumptape_leveldir->first_level ||
8729 global.dumptape_level_nr > dumptape_leveldir->last_level)
8730 Fail("no such level number: %d", global.dumptape_level_nr);
8732 leveldir_current = dumptape_leveldir;
8734 if (options.mytapes)
8735 LoadTape(global.dumptape_level_nr);
8737 LoadSolutionTape(global.dumptape_level_nr);
8745 // ============================================================================
8746 // score file functions
8747 // ============================================================================
8749 static void setScoreInfoToDefaultsExt(struct ScoreInfo *scores)
8753 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
8755 strcpy(scores->entry[i].tape_basename, UNDEFINED_FILENAME);
8756 strcpy(scores->entry[i].name, EMPTY_PLAYER_NAME);
8757 scores->entry[i].score = 0;
8758 scores->entry[i].time = 0;
8760 scores->entry[i].id = -1;
8761 strcpy(scores->entry[i].tape_date, UNKNOWN_NAME);
8762 strcpy(scores->entry[i].platform, UNKNOWN_NAME);
8763 strcpy(scores->entry[i].version, UNKNOWN_NAME);
8764 strcpy(scores->entry[i].country_name, UNKNOWN_NAME);
8765 strcpy(scores->entry[i].country_code, "??");
8768 scores->num_entries = 0;
8769 scores->last_added = -1;
8770 scores->last_added_local = -1;
8772 scores->updated = FALSE;
8773 scores->uploaded = FALSE;
8774 scores->tape_downloaded = FALSE;
8775 scores->force_last_added = FALSE;
8777 // The following values are intentionally not reset here:
8781 // - continue_playing
8782 // - continue_on_return
8785 static void setScoreInfoToDefaults(void)
8787 setScoreInfoToDefaultsExt(&scores);
8790 static void setServerScoreInfoToDefaults(void)
8792 setScoreInfoToDefaultsExt(&server_scores);
8795 static void LoadScore_OLD(int nr)
8798 char *filename = getScoreFilename(nr);
8799 char cookie[MAX_LINE_LEN];
8800 char line[MAX_LINE_LEN];
8804 if (!(file = fopen(filename, MODE_READ)))
8807 // check file identifier
8808 if (fgets(cookie, MAX_LINE_LEN, file) == NULL)
8810 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
8811 cookie[strlen(cookie) - 1] = '\0';
8813 if (!checkCookieString(cookie, SCORE_COOKIE_TMPL))
8815 Warn("unknown format of score file '%s'", filename);
8822 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
8824 if (fscanf(file, "%d", &scores.entry[i].score) == EOF)
8825 Warn("fscanf() failed; %s", strerror(errno));
8827 if (fgets(line, MAX_LINE_LEN, file) == NULL)
8830 if (strlen(line) > 0 && line[strlen(line) - 1] == '\n')
8831 line[strlen(line) - 1] = '\0';
8833 for (line_ptr = line; *line_ptr; line_ptr++)
8835 if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != '\0')
8837 strncpy(scores.entry[i].name, line_ptr, MAX_PLAYER_NAME_LEN);
8838 scores.entry[i].name[MAX_PLAYER_NAME_LEN] = '\0';
8847 static void ConvertScore_OLD(void)
8849 // only convert score to time for levels that rate playing time over score
8850 if (!level.rate_time_over_score)
8853 // convert old score to playing time for score-less levels (like Supaplex)
8854 int time_final_max = 999;
8857 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
8859 int score = scores.entry[i].score;
8861 if (score > 0 && score < time_final_max)
8862 scores.entry[i].time = (time_final_max - score - 1) * FRAMES_PER_SECOND;
8866 static int LoadScore_VERS(File *file, int chunk_size, struct ScoreInfo *scores)
8868 scores->file_version = getFileVersion(file);
8869 scores->game_version = getFileVersion(file);
8874 static int LoadScore_INFO(File *file, int chunk_size, struct ScoreInfo *scores)
8876 char *level_identifier = NULL;
8877 int level_identifier_size;
8880 level_identifier_size = getFile16BitBE(file);
8882 level_identifier = checked_malloc(level_identifier_size);
8884 for (i = 0; i < level_identifier_size; i++)
8885 level_identifier[i] = getFile8Bit(file);
8887 strncpy(scores->level_identifier, level_identifier, MAX_FILENAME_LEN);
8888 scores->level_identifier[MAX_FILENAME_LEN] = '\0';
8890 checked_free(level_identifier);
8892 scores->level_nr = getFile16BitBE(file);
8893 scores->num_entries = getFile16BitBE(file);
8895 chunk_size = 2 + level_identifier_size + 2 + 2;
8900 static int LoadScore_NAME(File *file, int chunk_size, struct ScoreInfo *scores)
8904 for (i = 0; i < scores->num_entries; i++)
8906 for (j = 0; j < MAX_PLAYER_NAME_LEN; j++)
8907 scores->entry[i].name[j] = getFile8Bit(file);
8909 scores->entry[i].name[MAX_PLAYER_NAME_LEN] = '\0';
8912 chunk_size = scores->num_entries * MAX_PLAYER_NAME_LEN;
8917 static int LoadScore_SCOR(File *file, int chunk_size, struct ScoreInfo *scores)
8921 for (i = 0; i < scores->num_entries; i++)
8922 scores->entry[i].score = getFile16BitBE(file);
8924 chunk_size = scores->num_entries * 2;
8929 static int LoadScore_SC4R(File *file, int chunk_size, struct ScoreInfo *scores)
8933 for (i = 0; i < scores->num_entries; i++)
8934 scores->entry[i].score = getFile32BitBE(file);
8936 chunk_size = scores->num_entries * 4;
8941 static int LoadScore_TIME(File *file, int chunk_size, struct ScoreInfo *scores)
8945 for (i = 0; i < scores->num_entries; i++)
8946 scores->entry[i].time = getFile32BitBE(file);
8948 chunk_size = scores->num_entries * 4;
8953 static int LoadScore_TAPE(File *file, int chunk_size, struct ScoreInfo *scores)
8957 for (i = 0; i < scores->num_entries; i++)
8959 for (j = 0; j < MAX_SCORE_TAPE_BASENAME_LEN; j++)
8960 scores->entry[i].tape_basename[j] = getFile8Bit(file);
8962 scores->entry[i].tape_basename[MAX_SCORE_TAPE_BASENAME_LEN] = '\0';
8965 chunk_size = scores->num_entries * MAX_SCORE_TAPE_BASENAME_LEN;
8970 void LoadScore(int nr)
8972 char *filename = getScoreFilename(nr);
8973 char cookie[MAX_LINE_LEN];
8974 char chunk_name[CHUNK_ID_LEN + 1];
8976 boolean old_score_file_format = FALSE;
8979 // always start with reliable default values
8980 setScoreInfoToDefaults();
8982 if (!(file = openFile(filename, MODE_READ)))
8985 getFileChunkBE(file, chunk_name, NULL);
8986 if (strEqual(chunk_name, "RND1"))
8988 getFile32BitBE(file); // not used
8990 getFileChunkBE(file, chunk_name, NULL);
8991 if (!strEqual(chunk_name, "SCOR"))
8993 Warn("unknown format of score file '%s'", filename);
9000 else // check for old file format with cookie string
9002 strcpy(cookie, chunk_name);
9003 if (getStringFromFile(file, &cookie[4], MAX_LINE_LEN - 4) == NULL)
9005 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
9006 cookie[strlen(cookie) - 1] = '\0';
9008 if (!checkCookieString(cookie, SCORE_COOKIE_TMPL))
9010 Warn("unknown format of score file '%s'", filename);
9017 old_score_file_format = TRUE;
9020 if (old_score_file_format)
9022 // score files from versions before 4.2.4.0 without chunk structure
9025 // convert score to time, if possible (mainly for Supaplex levels)
9034 int (*loader)(File *, int, struct ScoreInfo *);
9038 { "VERS", SCORE_CHUNK_VERS_SIZE, LoadScore_VERS },
9039 { "INFO", -1, LoadScore_INFO },
9040 { "NAME", -1, LoadScore_NAME },
9041 { "SCOR", -1, LoadScore_SCOR },
9042 { "SC4R", -1, LoadScore_SC4R },
9043 { "TIME", -1, LoadScore_TIME },
9044 { "TAPE", -1, LoadScore_TAPE },
9049 while (getFileChunkBE(file, chunk_name, &chunk_size))
9053 while (chunk_info[i].name != NULL &&
9054 !strEqual(chunk_name, chunk_info[i].name))
9057 if (chunk_info[i].name == NULL)
9059 Warn("unknown chunk '%s' in score file '%s'",
9060 chunk_name, filename);
9062 ReadUnusedBytesFromFile(file, chunk_size);
9064 else if (chunk_info[i].size != -1 &&
9065 chunk_info[i].size != chunk_size)
9067 Warn("wrong size (%d) of chunk '%s' in score file '%s'",
9068 chunk_size, chunk_name, filename);
9070 ReadUnusedBytesFromFile(file, chunk_size);
9074 // call function to load this score chunk
9075 int chunk_size_expected =
9076 (chunk_info[i].loader)(file, chunk_size, &scores);
9078 // the size of some chunks cannot be checked before reading other
9079 // chunks first (like "HEAD" and "BODY") that contain some header
9080 // information, so check them here
9081 if (chunk_size_expected != chunk_size)
9083 Warn("wrong size (%d) of chunk '%s' in score file '%s'",
9084 chunk_size, chunk_name, filename);
9093 #if ENABLE_HISTORIC_CHUNKS
9094 void SaveScore_OLD(int nr)
9097 char *filename = getScoreFilename(nr);
9100 // used instead of "leveldir_current->subdir" (for network games)
9101 InitScoreDirectory(levelset.identifier);
9103 if (!(file = fopen(filename, MODE_WRITE)))
9105 Warn("cannot save score for level %d", nr);
9110 fprintf(file, "%s\n\n", SCORE_COOKIE);
9112 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9113 fprintf(file, "%d %s\n", scores.entry[i].score, scores.entry[i].name);
9117 SetFilePermissions(filename, PERMS_PRIVATE);
9121 static void SaveScore_VERS(FILE *file, struct ScoreInfo *scores)
9123 putFileVersion(file, scores->file_version);
9124 putFileVersion(file, scores->game_version);
9127 static void SaveScore_INFO(FILE *file, struct ScoreInfo *scores)
9129 int level_identifier_size = strlen(scores->level_identifier) + 1;
9132 putFile16BitBE(file, level_identifier_size);
9134 for (i = 0; i < level_identifier_size; i++)
9135 putFile8Bit(file, scores->level_identifier[i]);
9137 putFile16BitBE(file, scores->level_nr);
9138 putFile16BitBE(file, scores->num_entries);
9141 static void SaveScore_NAME(FILE *file, struct ScoreInfo *scores)
9145 for (i = 0; i < scores->num_entries; i++)
9147 int name_size = strlen(scores->entry[i].name);
9149 for (j = 0; j < MAX_PLAYER_NAME_LEN; j++)
9150 putFile8Bit(file, (j < name_size ? scores->entry[i].name[j] : 0));
9154 static void SaveScore_SCOR(FILE *file, struct ScoreInfo *scores)
9158 for (i = 0; i < scores->num_entries; i++)
9159 putFile16BitBE(file, scores->entry[i].score);
9162 static void SaveScore_SC4R(FILE *file, struct ScoreInfo *scores)
9166 for (i = 0; i < scores->num_entries; i++)
9167 putFile32BitBE(file, scores->entry[i].score);
9170 static void SaveScore_TIME(FILE *file, struct ScoreInfo *scores)
9174 for (i = 0; i < scores->num_entries; i++)
9175 putFile32BitBE(file, scores->entry[i].time);
9178 static void SaveScore_TAPE(FILE *file, struct ScoreInfo *scores)
9182 for (i = 0; i < scores->num_entries; i++)
9184 int size = strlen(scores->entry[i].tape_basename);
9186 for (j = 0; j < MAX_SCORE_TAPE_BASENAME_LEN; j++)
9187 putFile8Bit(file, (j < size ? scores->entry[i].tape_basename[j] : 0));
9191 static void SaveScoreToFilename(char *filename)
9194 int info_chunk_size;
9195 int name_chunk_size;
9196 int scor_chunk_size;
9197 int sc4r_chunk_size;
9198 int time_chunk_size;
9199 int tape_chunk_size;
9200 boolean has_large_score_values;
9203 if (!(file = fopen(filename, MODE_WRITE)))
9205 Warn("cannot save score file '%s'", filename);
9210 info_chunk_size = 2 + (strlen(scores.level_identifier) + 1) + 2 + 2;
9211 name_chunk_size = scores.num_entries * MAX_PLAYER_NAME_LEN;
9212 scor_chunk_size = scores.num_entries * 2;
9213 sc4r_chunk_size = scores.num_entries * 4;
9214 time_chunk_size = scores.num_entries * 4;
9215 tape_chunk_size = scores.num_entries * MAX_SCORE_TAPE_BASENAME_LEN;
9217 has_large_score_values = FALSE;
9218 for (i = 0; i < scores.num_entries; i++)
9219 if (scores.entry[i].score > 0xffff)
9220 has_large_score_values = TRUE;
9222 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
9223 putFileChunkBE(file, "SCOR", CHUNK_SIZE_NONE);
9225 putFileChunkBE(file, "VERS", SCORE_CHUNK_VERS_SIZE);
9226 SaveScore_VERS(file, &scores);
9228 putFileChunkBE(file, "INFO", info_chunk_size);
9229 SaveScore_INFO(file, &scores);
9231 putFileChunkBE(file, "NAME", name_chunk_size);
9232 SaveScore_NAME(file, &scores);
9234 if (has_large_score_values)
9236 putFileChunkBE(file, "SC4R", sc4r_chunk_size);
9237 SaveScore_SC4R(file, &scores);
9241 putFileChunkBE(file, "SCOR", scor_chunk_size);
9242 SaveScore_SCOR(file, &scores);
9245 putFileChunkBE(file, "TIME", time_chunk_size);
9246 SaveScore_TIME(file, &scores);
9248 putFileChunkBE(file, "TAPE", tape_chunk_size);
9249 SaveScore_TAPE(file, &scores);
9253 SetFilePermissions(filename, PERMS_PRIVATE);
9256 void SaveScore(int nr)
9258 char *filename = getScoreFilename(nr);
9261 // used instead of "leveldir_current->subdir" (for network games)
9262 InitScoreDirectory(levelset.identifier);
9264 scores.file_version = FILE_VERSION_ACTUAL;
9265 scores.game_version = GAME_VERSION_ACTUAL;
9267 strncpy(scores.level_identifier, levelset.identifier, MAX_FILENAME_LEN);
9268 scores.level_identifier[MAX_FILENAME_LEN] = '\0';
9269 scores.level_nr = level_nr;
9271 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9272 if (scores.entry[i].score == 0 &&
9273 scores.entry[i].time == 0 &&
9274 strEqual(scores.entry[i].name, EMPTY_PLAYER_NAME))
9277 scores.num_entries = i;
9279 if (scores.num_entries == 0)
9282 SaveScoreToFilename(filename);
9285 static void LoadServerScoreFromCache(int nr)
9287 struct ScoreEntry score_entry;
9296 { &score_entry.score, FALSE, 0 },
9297 { &score_entry.time, FALSE, 0 },
9298 { score_entry.name, TRUE, MAX_PLAYER_NAME_LEN },
9299 { score_entry.tape_basename, TRUE, MAX_FILENAME_LEN },
9300 { score_entry.tape_date, TRUE, MAX_ISO_DATE_LEN },
9301 { &score_entry.id, FALSE, 0 },
9302 { score_entry.platform, TRUE, MAX_PLATFORM_TEXT_LEN },
9303 { score_entry.version, TRUE, MAX_VERSION_TEXT_LEN },
9304 { score_entry.country_code, TRUE, MAX_COUNTRY_CODE_LEN },
9305 { score_entry.country_name, TRUE, MAX_COUNTRY_NAME_LEN },
9309 char *filename = getScoreCacheFilename(nr);
9310 SetupFileHash *score_hash = loadSetupFileHash(filename);
9313 server_scores.num_entries = 0;
9315 if (score_hash == NULL)
9318 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9320 score_entry = server_scores.entry[i];
9322 for (j = 0; score_mapping[j].value != NULL; j++)
9326 sprintf(token, "%02d.%d", i, j);
9328 char *value = getHashEntry(score_hash, token);
9333 if (score_mapping[j].is_string)
9335 char *score_value = (char *)score_mapping[j].value;
9336 int value_size = score_mapping[j].string_size;
9338 strncpy(score_value, value, value_size);
9339 score_value[value_size] = '\0';
9343 int *score_value = (int *)score_mapping[j].value;
9345 *score_value = atoi(value);
9348 server_scores.num_entries = i + 1;
9351 server_scores.entry[i] = score_entry;
9354 freeSetupFileHash(score_hash);
9357 void LoadServerScore(int nr, boolean download_score)
9359 if (!setup.use_api_server)
9362 // always start with reliable default values
9363 setServerScoreInfoToDefaults();
9365 // 1st step: load server scores from cache file (which may not exist)
9366 // (this should prevent reading it while the thread is writing to it)
9367 LoadServerScoreFromCache(nr);
9369 if (download_score && runtime.use_api_server)
9371 // 2nd step: download server scores from score server to cache file
9372 // (as thread, as it might time out if the server is not reachable)
9373 ApiGetScoreAsThread(nr);
9377 void PrepareScoreTapesForUpload(char *leveldir_subdir)
9379 MarkTapeDirectoryUploadsAsIncomplete(leveldir_subdir);
9381 // if score tape not uploaded, ask for uploading missing tapes later
9382 if (!setup.has_remaining_tapes)
9383 setup.ask_for_remaining_tapes = TRUE;
9385 setup.provide_uploading_tapes = TRUE;
9386 setup.has_remaining_tapes = TRUE;
9388 SaveSetup_ServerSetup();
9391 void SaveServerScore(int nr, boolean tape_saved)
9393 if (!runtime.use_api_server)
9395 PrepareScoreTapesForUpload(leveldir_current->subdir);
9400 ApiAddScoreAsThread(nr, tape_saved, NULL);
9403 void SaveServerScoreFromFile(int nr, boolean tape_saved,
9404 char *score_tape_filename)
9406 if (!runtime.use_api_server)
9409 ApiAddScoreAsThread(nr, tape_saved, score_tape_filename);
9412 void LoadLocalAndServerScore(int nr, boolean download_score)
9414 int last_added_local = scores.last_added_local;
9415 boolean force_last_added = scores.force_last_added;
9417 // needed if only showing server scores
9418 setScoreInfoToDefaults();
9420 if (!strEqual(setup.scores_in_highscore_list, STR_SCORES_TYPE_SERVER_ONLY))
9423 // restore last added local score entry (before merging server scores)
9424 scores.last_added = scores.last_added_local = last_added_local;
9426 if (setup.use_api_server &&
9427 !strEqual(setup.scores_in_highscore_list, STR_SCORES_TYPE_LOCAL_ONLY))
9429 // load server scores from cache file and trigger update from server
9430 LoadServerScore(nr, download_score);
9432 // merge local scores with scores from server
9436 if (force_last_added)
9437 scores.force_last_added = force_last_added;
9441 // ============================================================================
9442 // setup file functions
9443 // ============================================================================
9445 #define TOKEN_STR_PLAYER_PREFIX "player_"
9448 static struct TokenInfo global_setup_tokens[] =
9452 &setup.player_name, "player_name"
9456 &setup.multiple_users, "multiple_users"
9460 &setup.sound, "sound"
9464 &setup.sound_loops, "repeating_sound_loops"
9468 &setup.sound_music, "background_music"
9472 &setup.sound_simple, "simple_sound_effects"
9476 &setup.toons, "toons"
9480 &setup.scroll_delay, "scroll_delay"
9484 &setup.forced_scroll_delay, "forced_scroll_delay"
9488 &setup.scroll_delay_value, "scroll_delay_value"
9492 &setup.engine_snapshot_mode, "engine_snapshot_mode"
9496 &setup.engine_snapshot_memory, "engine_snapshot_memory"
9500 &setup.fade_screens, "fade_screens"
9504 &setup.autorecord, "automatic_tape_recording"
9508 &setup.autorecord_after_replay, "autorecord_after_replay"
9512 &setup.auto_pause_on_start, "auto_pause_on_start"
9516 &setup.show_titlescreen, "show_titlescreen"
9520 &setup.quick_doors, "quick_doors"
9524 &setup.team_mode, "team_mode"
9528 &setup.handicap, "handicap"
9532 &setup.skip_levels, "skip_levels"
9536 &setup.increment_levels, "increment_levels"
9540 &setup.auto_play_next_level, "auto_play_next_level"
9544 &setup.count_score_after_game, "count_score_after_game"
9548 &setup.show_scores_after_game, "show_scores_after_game"
9552 &setup.time_limit, "time_limit"
9556 &setup.fullscreen, "fullscreen"
9560 &setup.window_scaling_percent, "window_scaling_percent"
9564 &setup.window_scaling_quality, "window_scaling_quality"
9568 &setup.screen_rendering_mode, "screen_rendering_mode"
9572 &setup.vsync_mode, "vsync_mode"
9576 &setup.ask_on_escape, "ask_on_escape"
9580 &setup.ask_on_escape_editor, "ask_on_escape_editor"
9584 &setup.ask_on_game_over, "ask_on_game_over"
9588 &setup.ask_on_quit_game, "ask_on_quit_game"
9592 &setup.ask_on_quit_program, "ask_on_quit_program"
9596 &setup.quick_switch, "quick_player_switch"
9600 &setup.input_on_focus, "input_on_focus"
9604 &setup.prefer_aga_graphics, "prefer_aga_graphics"
9608 &setup.prefer_lowpass_sounds, "prefer_lowpass_sounds"
9612 &setup.prefer_extra_panel_items, "prefer_extra_panel_items"
9616 &setup.game_speed_extended, "game_speed_extended"
9620 &setup.game_frame_delay, "game_frame_delay"
9624 &setup.sp_show_border_elements, "sp_show_border_elements"
9628 &setup.small_game_graphics, "small_game_graphics"
9632 &setup.show_load_save_buttons, "show_load_save_buttons"
9636 &setup.show_undo_redo_buttons, "show_undo_redo_buttons"
9640 &setup.scores_in_highscore_list, "scores_in_highscore_list"
9644 &setup.graphics_set, "graphics_set"
9648 &setup.sounds_set, "sounds_set"
9652 &setup.music_set, "music_set"
9656 &setup.override_level_graphics, "override_level_graphics"
9660 &setup.override_level_sounds, "override_level_sounds"
9664 &setup.override_level_music, "override_level_music"
9668 &setup.volume_simple, "volume_simple"
9672 &setup.volume_loops, "volume_loops"
9676 &setup.volume_music, "volume_music"
9680 &setup.network_mode, "network_mode"
9684 &setup.network_player_nr, "network_player"
9688 &setup.network_server_hostname, "network_server_hostname"
9692 &setup.touch.control_type, "touch.control_type"
9696 &setup.touch.move_distance, "touch.move_distance"
9700 &setup.touch.drop_distance, "touch.drop_distance"
9704 &setup.touch.transparency, "touch.transparency"
9708 &setup.touch.draw_outlined, "touch.draw_outlined"
9712 &setup.touch.draw_pressed, "touch.draw_pressed"
9716 &setup.touch.grid_xsize[0], "touch.virtual_buttons.0.xsize"
9720 &setup.touch.grid_ysize[0], "touch.virtual_buttons.0.ysize"
9724 &setup.touch.grid_xsize[1], "touch.virtual_buttons.1.xsize"
9728 &setup.touch.grid_ysize[1], "touch.virtual_buttons.1.ysize"
9732 &setup.touch.overlay_buttons, "touch.overlay_buttons"
9736 static struct TokenInfo auto_setup_tokens[] =
9740 &setup.auto_setup.editor_zoom_tilesize, "editor.zoom_tilesize"
9744 static struct TokenInfo server_setup_tokens[] =
9748 &setup.player_uuid, "player_uuid"
9752 &setup.player_version, "player_version"
9756 &setup.use_api_server, TEST_PREFIX "use_api_server"
9760 &setup.api_server_hostname, TEST_PREFIX "api_server_hostname"
9764 &setup.api_server_password, TEST_PREFIX "api_server_password"
9768 &setup.ask_for_uploading_tapes, TEST_PREFIX "ask_for_uploading_tapes"
9772 &setup.ask_for_remaining_tapes, TEST_PREFIX "ask_for_remaining_tapes"
9776 &setup.provide_uploading_tapes, TEST_PREFIX "provide_uploading_tapes"
9780 &setup.ask_for_using_api_server,TEST_PREFIX "ask_for_using_api_server"
9784 &setup.has_remaining_tapes, TEST_PREFIX "has_remaining_tapes"
9788 static struct TokenInfo editor_setup_tokens[] =
9792 &setup.editor.el_classic, "editor.el_classic"
9796 &setup.editor.el_custom, "editor.el_custom"
9800 &setup.editor.el_user_defined, "editor.el_user_defined"
9804 &setup.editor.el_dynamic, "editor.el_dynamic"
9808 &setup.editor.el_headlines, "editor.el_headlines"
9812 &setup.editor.show_element_token, "editor.show_element_token"
9816 &setup.editor.show_read_only_warning, "editor.show_read_only_warning"
9820 static struct TokenInfo editor_cascade_setup_tokens[] =
9824 &setup.editor_cascade.el_bd, "editor.cascade.el_bd"
9828 &setup.editor_cascade.el_em, "editor.cascade.el_em"
9832 &setup.editor_cascade.el_emc, "editor.cascade.el_emc"
9836 &setup.editor_cascade.el_rnd, "editor.cascade.el_rnd"
9840 &setup.editor_cascade.el_sb, "editor.cascade.el_sb"
9844 &setup.editor_cascade.el_sp, "editor.cascade.el_sp"
9848 &setup.editor_cascade.el_dc, "editor.cascade.el_dc"
9852 &setup.editor_cascade.el_dx, "editor.cascade.el_dx"
9856 &setup.editor_cascade.el_mm, "editor.cascade.el_mm"
9860 &setup.editor_cascade.el_df, "editor.cascade.el_df"
9864 &setup.editor_cascade.el_chars, "editor.cascade.el_chars"
9868 &setup.editor_cascade.el_steel_chars, "editor.cascade.el_steel_chars"
9872 &setup.editor_cascade.el_ce, "editor.cascade.el_ce"
9876 &setup.editor_cascade.el_ge, "editor.cascade.el_ge"
9880 &setup.editor_cascade.el_es, "editor.cascade.el_es"
9884 &setup.editor_cascade.el_ref, "editor.cascade.el_ref"
9888 &setup.editor_cascade.el_user, "editor.cascade.el_user"
9892 &setup.editor_cascade.el_dynamic, "editor.cascade.el_dynamic"
9896 static struct TokenInfo shortcut_setup_tokens[] =
9900 &setup.shortcut.save_game, "shortcut.save_game"
9904 &setup.shortcut.load_game, "shortcut.load_game"
9908 &setup.shortcut.restart_game, "shortcut.restart_game"
9912 &setup.shortcut.pause_before_end, "shortcut.pause_before_end"
9916 &setup.shortcut.toggle_pause, "shortcut.toggle_pause"
9920 &setup.shortcut.focus_player[0], "shortcut.focus_player_1"
9924 &setup.shortcut.focus_player[1], "shortcut.focus_player_2"
9928 &setup.shortcut.focus_player[2], "shortcut.focus_player_3"
9932 &setup.shortcut.focus_player[3], "shortcut.focus_player_4"
9936 &setup.shortcut.focus_player_all, "shortcut.focus_player_all"
9940 &setup.shortcut.tape_eject, "shortcut.tape_eject"
9944 &setup.shortcut.tape_extra, "shortcut.tape_extra"
9948 &setup.shortcut.tape_stop, "shortcut.tape_stop"
9952 &setup.shortcut.tape_pause, "shortcut.tape_pause"
9956 &setup.shortcut.tape_record, "shortcut.tape_record"
9960 &setup.shortcut.tape_play, "shortcut.tape_play"
9964 &setup.shortcut.sound_simple, "shortcut.sound_simple"
9968 &setup.shortcut.sound_loops, "shortcut.sound_loops"
9972 &setup.shortcut.sound_music, "shortcut.sound_music"
9976 &setup.shortcut.snap_left, "shortcut.snap_left"
9980 &setup.shortcut.snap_right, "shortcut.snap_right"
9984 &setup.shortcut.snap_up, "shortcut.snap_up"
9988 &setup.shortcut.snap_down, "shortcut.snap_down"
9992 static struct SetupInputInfo setup_input;
9993 static struct TokenInfo player_setup_tokens[] =
9997 &setup_input.use_joystick, ".use_joystick"
10001 &setup_input.joy.device_name, ".joy.device_name"
10005 &setup_input.joy.xleft, ".joy.xleft"
10009 &setup_input.joy.xmiddle, ".joy.xmiddle"
10013 &setup_input.joy.xright, ".joy.xright"
10017 &setup_input.joy.yupper, ".joy.yupper"
10021 &setup_input.joy.ymiddle, ".joy.ymiddle"
10025 &setup_input.joy.ylower, ".joy.ylower"
10029 &setup_input.joy.snap, ".joy.snap_field"
10033 &setup_input.joy.drop, ".joy.place_bomb"
10037 &setup_input.key.left, ".key.move_left"
10041 &setup_input.key.right, ".key.move_right"
10045 &setup_input.key.up, ".key.move_up"
10049 &setup_input.key.down, ".key.move_down"
10053 &setup_input.key.snap, ".key.snap_field"
10057 &setup_input.key.drop, ".key.place_bomb"
10061 static struct TokenInfo system_setup_tokens[] =
10065 &setup.system.sdl_renderdriver, "system.sdl_renderdriver"
10069 &setup.system.sdl_videodriver, "system.sdl_videodriver"
10073 &setup.system.sdl_audiodriver, "system.sdl_audiodriver"
10077 &setup.system.audio_fragment_size, "system.audio_fragment_size"
10081 static struct TokenInfo internal_setup_tokens[] =
10085 &setup.internal.program_title, "program_title"
10089 &setup.internal.program_version, "program_version"
10093 &setup.internal.program_author, "program_author"
10097 &setup.internal.program_email, "program_email"
10101 &setup.internal.program_website, "program_website"
10105 &setup.internal.program_copyright, "program_copyright"
10109 &setup.internal.program_company, "program_company"
10113 &setup.internal.program_icon_file, "program_icon_file"
10117 &setup.internal.default_graphics_set, "default_graphics_set"
10121 &setup.internal.default_sounds_set, "default_sounds_set"
10125 &setup.internal.default_music_set, "default_music_set"
10129 &setup.internal.fallback_graphics_file, "fallback_graphics_file"
10133 &setup.internal.fallback_sounds_file, "fallback_sounds_file"
10137 &setup.internal.fallback_music_file, "fallback_music_file"
10141 &setup.internal.default_level_series, "default_level_series"
10145 &setup.internal.default_window_width, "default_window_width"
10149 &setup.internal.default_window_height, "default_window_height"
10153 &setup.internal.choose_from_top_leveldir, "choose_from_top_leveldir"
10157 &setup.internal.show_scaling_in_title, "show_scaling_in_title"
10161 &setup.internal.create_user_levelset, "create_user_levelset"
10165 &setup.internal.info_screens_from_main, "info_screens_from_main"
10169 &setup.internal.menu_game, "menu_game"
10173 &setup.internal.menu_engines, "menu_engines"
10177 &setup.internal.menu_editor, "menu_editor"
10181 &setup.internal.menu_graphics, "menu_graphics"
10185 &setup.internal.menu_sound, "menu_sound"
10189 &setup.internal.menu_artwork, "menu_artwork"
10193 &setup.internal.menu_input, "menu_input"
10197 &setup.internal.menu_touch, "menu_touch"
10201 &setup.internal.menu_shortcuts, "menu_shortcuts"
10205 &setup.internal.menu_exit, "menu_exit"
10209 &setup.internal.menu_save_and_exit, "menu_save_and_exit"
10213 &setup.internal.menu_shortcuts_various, "menu_shortcuts_various"
10217 &setup.internal.menu_shortcuts_focus, "menu_shortcuts_focus"
10221 &setup.internal.menu_shortcuts_tape, "menu_shortcuts_tape"
10225 &setup.internal.menu_shortcuts_sound, "menu_shortcuts_sound"
10229 &setup.internal.menu_shortcuts_snap, "menu_shortcuts_snap"
10233 &setup.internal.info_title, "info_title"
10237 &setup.internal.info_elements, "info_elements"
10241 &setup.internal.info_music, "info_music"
10245 &setup.internal.info_credits, "info_credits"
10249 &setup.internal.info_program, "info_program"
10253 &setup.internal.info_version, "info_version"
10257 &setup.internal.info_levelset, "info_levelset"
10261 &setup.internal.info_exit, "info_exit"
10265 static struct TokenInfo debug_setup_tokens[] =
10269 &setup.debug.frame_delay[0], "debug.frame_delay_0"
10273 &setup.debug.frame_delay[1], "debug.frame_delay_1"
10277 &setup.debug.frame_delay[2], "debug.frame_delay_2"
10281 &setup.debug.frame_delay[3], "debug.frame_delay_3"
10285 &setup.debug.frame_delay[4], "debug.frame_delay_4"
10289 &setup.debug.frame_delay[5], "debug.frame_delay_5"
10293 &setup.debug.frame_delay[6], "debug.frame_delay_6"
10297 &setup.debug.frame_delay[7], "debug.frame_delay_7"
10301 &setup.debug.frame_delay[8], "debug.frame_delay_8"
10305 &setup.debug.frame_delay[9], "debug.frame_delay_9"
10309 &setup.debug.frame_delay_key[0], "debug.key.frame_delay_0"
10313 &setup.debug.frame_delay_key[1], "debug.key.frame_delay_1"
10317 &setup.debug.frame_delay_key[2], "debug.key.frame_delay_2"
10321 &setup.debug.frame_delay_key[3], "debug.key.frame_delay_3"
10325 &setup.debug.frame_delay_key[4], "debug.key.frame_delay_4"
10329 &setup.debug.frame_delay_key[5], "debug.key.frame_delay_5"
10333 &setup.debug.frame_delay_key[6], "debug.key.frame_delay_6"
10337 &setup.debug.frame_delay_key[7], "debug.key.frame_delay_7"
10341 &setup.debug.frame_delay_key[8], "debug.key.frame_delay_8"
10345 &setup.debug.frame_delay_key[9], "debug.key.frame_delay_9"
10349 &setup.debug.frame_delay_use_mod_key, "debug.frame_delay.use_mod_key"},
10352 &setup.debug.frame_delay_game_only, "debug.frame_delay.game_only"
10356 &setup.debug.show_frames_per_second, "debug.show_frames_per_second"
10360 &setup.debug.xsn_mode, "debug.xsn_mode"
10364 &setup.debug.xsn_percent, "debug.xsn_percent"
10368 static struct TokenInfo options_setup_tokens[] =
10372 &setup.options.verbose, "options.verbose"
10376 static void setSetupInfoToDefaults(struct SetupInfo *si)
10380 si->player_name = getStringCopy(getDefaultUserName(user.nr));
10382 si->multiple_users = TRUE;
10385 si->sound_loops = TRUE;
10386 si->sound_music = TRUE;
10387 si->sound_simple = TRUE;
10389 si->scroll_delay = TRUE;
10390 si->forced_scroll_delay = FALSE;
10391 si->scroll_delay_value = STD_SCROLL_DELAY;
10392 si->engine_snapshot_mode = getStringCopy(STR_SNAPSHOT_MODE_DEFAULT);
10393 si->engine_snapshot_memory = SNAPSHOT_MEMORY_DEFAULT;
10394 si->fade_screens = TRUE;
10395 si->autorecord = TRUE;
10396 si->autorecord_after_replay = TRUE;
10397 si->auto_pause_on_start = FALSE;
10398 si->show_titlescreen = TRUE;
10399 si->quick_doors = FALSE;
10400 si->team_mode = FALSE;
10401 si->handicap = TRUE;
10402 si->skip_levels = TRUE;
10403 si->increment_levels = TRUE;
10404 si->auto_play_next_level = TRUE;
10405 si->count_score_after_game = TRUE;
10406 si->show_scores_after_game = TRUE;
10407 si->time_limit = TRUE;
10408 si->fullscreen = FALSE;
10409 si->window_scaling_percent = STD_WINDOW_SCALING_PERCENT;
10410 si->window_scaling_quality = getStringCopy(SCALING_QUALITY_DEFAULT);
10411 si->screen_rendering_mode = getStringCopy(STR_SPECIAL_RENDERING_DEFAULT);
10412 si->vsync_mode = getStringCopy(STR_VSYNC_MODE_DEFAULT);
10413 si->ask_on_escape = TRUE;
10414 si->ask_on_escape_editor = TRUE;
10415 si->ask_on_game_over = TRUE;
10416 si->ask_on_quit_game = TRUE;
10417 si->ask_on_quit_program = TRUE;
10418 si->quick_switch = FALSE;
10419 si->input_on_focus = FALSE;
10420 si->prefer_aga_graphics = TRUE;
10421 si->prefer_lowpass_sounds = FALSE;
10422 si->prefer_extra_panel_items = TRUE;
10423 si->game_speed_extended = FALSE;
10424 si->game_frame_delay = GAME_FRAME_DELAY;
10425 si->sp_show_border_elements = FALSE;
10426 si->small_game_graphics = FALSE;
10427 si->show_load_save_buttons = FALSE;
10428 si->show_undo_redo_buttons = FALSE;
10429 si->scores_in_highscore_list = getStringCopy(STR_SCORES_TYPE_DEFAULT);
10431 si->graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
10432 si->sounds_set = getStringCopy(SND_CLASSIC_SUBDIR);
10433 si->music_set = getStringCopy(MUS_CLASSIC_SUBDIR);
10435 si->override_level_graphics = FALSE;
10436 si->override_level_sounds = FALSE;
10437 si->override_level_music = FALSE;
10439 si->volume_simple = 100; // percent
10440 si->volume_loops = 100; // percent
10441 si->volume_music = 100; // percent
10443 si->network_mode = FALSE;
10444 si->network_player_nr = 0; // first player
10445 si->network_server_hostname = getStringCopy(STR_NETWORK_AUTO_DETECT);
10447 si->touch.control_type = getStringCopy(TOUCH_CONTROL_DEFAULT);
10448 si->touch.move_distance = TOUCH_MOVE_DISTANCE_DEFAULT; // percent
10449 si->touch.drop_distance = TOUCH_DROP_DISTANCE_DEFAULT; // percent
10450 si->touch.transparency = TOUCH_TRANSPARENCY_DEFAULT; // percent
10451 si->touch.draw_outlined = TRUE;
10452 si->touch.draw_pressed = TRUE;
10454 for (i = 0; i < 2; i++)
10456 char *default_grid_button[6][2] =
10462 { "111222", " vv " },
10463 { "111222", " vv " }
10465 int grid_xsize = DEFAULT_GRID_XSIZE(i);
10466 int grid_ysize = DEFAULT_GRID_YSIZE(i);
10467 int min_xsize = MIN(6, grid_xsize);
10468 int min_ysize = MIN(6, grid_ysize);
10469 int startx = grid_xsize - min_xsize;
10470 int starty = grid_ysize - min_ysize;
10473 // virtual buttons grid can only be set to defaults if video is initialized
10474 // (this will be repeated if virtual buttons are not loaded from setup file)
10475 if (video.initialized)
10477 si->touch.grid_xsize[i] = grid_xsize;
10478 si->touch.grid_ysize[i] = grid_ysize;
10482 si->touch.grid_xsize[i] = -1;
10483 si->touch.grid_ysize[i] = -1;
10486 for (x = 0; x < MAX_GRID_XSIZE; x++)
10487 for (y = 0; y < MAX_GRID_YSIZE; y++)
10488 si->touch.grid_button[i][x][y] = CHAR_GRID_BUTTON_NONE;
10490 for (x = 0; x < min_xsize; x++)
10491 for (y = 0; y < min_ysize; y++)
10492 si->touch.grid_button[i][x][starty + y] =
10493 default_grid_button[y][0][x];
10495 for (x = 0; x < min_xsize; x++)
10496 for (y = 0; y < min_ysize; y++)
10497 si->touch.grid_button[i][startx + x][starty + y] =
10498 default_grid_button[y][1][x];
10501 si->touch.grid_initialized = video.initialized;
10503 si->touch.overlay_buttons = FALSE;
10505 si->editor.el_boulderdash = TRUE;
10506 si->editor.el_emerald_mine = TRUE;
10507 si->editor.el_emerald_mine_club = TRUE;
10508 si->editor.el_more = TRUE;
10509 si->editor.el_sokoban = TRUE;
10510 si->editor.el_supaplex = TRUE;
10511 si->editor.el_diamond_caves = TRUE;
10512 si->editor.el_dx_boulderdash = TRUE;
10514 si->editor.el_mirror_magic = TRUE;
10515 si->editor.el_deflektor = TRUE;
10517 si->editor.el_chars = TRUE;
10518 si->editor.el_steel_chars = TRUE;
10520 si->editor.el_classic = TRUE;
10521 si->editor.el_custom = TRUE;
10523 si->editor.el_user_defined = FALSE;
10524 si->editor.el_dynamic = TRUE;
10526 si->editor.el_headlines = TRUE;
10528 si->editor.show_element_token = FALSE;
10530 si->editor.show_read_only_warning = TRUE;
10532 si->editor.use_template_for_new_levels = TRUE;
10534 si->shortcut.save_game = DEFAULT_KEY_SAVE_GAME;
10535 si->shortcut.load_game = DEFAULT_KEY_LOAD_GAME;
10536 si->shortcut.restart_game = DEFAULT_KEY_RESTART_GAME;
10537 si->shortcut.pause_before_end = DEFAULT_KEY_PAUSE_BEFORE_END;
10538 si->shortcut.toggle_pause = DEFAULT_KEY_TOGGLE_PAUSE;
10540 si->shortcut.focus_player[0] = DEFAULT_KEY_FOCUS_PLAYER_1;
10541 si->shortcut.focus_player[1] = DEFAULT_KEY_FOCUS_PLAYER_2;
10542 si->shortcut.focus_player[2] = DEFAULT_KEY_FOCUS_PLAYER_3;
10543 si->shortcut.focus_player[3] = DEFAULT_KEY_FOCUS_PLAYER_4;
10544 si->shortcut.focus_player_all = DEFAULT_KEY_FOCUS_PLAYER_ALL;
10546 si->shortcut.tape_eject = DEFAULT_KEY_TAPE_EJECT;
10547 si->shortcut.tape_extra = DEFAULT_KEY_TAPE_EXTRA;
10548 si->shortcut.tape_stop = DEFAULT_KEY_TAPE_STOP;
10549 si->shortcut.tape_pause = DEFAULT_KEY_TAPE_PAUSE;
10550 si->shortcut.tape_record = DEFAULT_KEY_TAPE_RECORD;
10551 si->shortcut.tape_play = DEFAULT_KEY_TAPE_PLAY;
10553 si->shortcut.sound_simple = DEFAULT_KEY_SOUND_SIMPLE;
10554 si->shortcut.sound_loops = DEFAULT_KEY_SOUND_LOOPS;
10555 si->shortcut.sound_music = DEFAULT_KEY_SOUND_MUSIC;
10557 si->shortcut.snap_left = DEFAULT_KEY_SNAP_LEFT;
10558 si->shortcut.snap_right = DEFAULT_KEY_SNAP_RIGHT;
10559 si->shortcut.snap_up = DEFAULT_KEY_SNAP_UP;
10560 si->shortcut.snap_down = DEFAULT_KEY_SNAP_DOWN;
10562 for (i = 0; i < MAX_PLAYERS; i++)
10564 si->input[i].use_joystick = FALSE;
10565 si->input[i].joy.device_name=getStringCopy(getDeviceNameFromJoystickNr(i));
10566 si->input[i].joy.xleft = JOYSTICK_XLEFT;
10567 si->input[i].joy.xmiddle = JOYSTICK_XMIDDLE;
10568 si->input[i].joy.xright = JOYSTICK_XRIGHT;
10569 si->input[i].joy.yupper = JOYSTICK_YUPPER;
10570 si->input[i].joy.ymiddle = JOYSTICK_YMIDDLE;
10571 si->input[i].joy.ylower = JOYSTICK_YLOWER;
10572 si->input[i].joy.snap = (i == 0 ? JOY_BUTTON_1 : 0);
10573 si->input[i].joy.drop = (i == 0 ? JOY_BUTTON_2 : 0);
10574 si->input[i].key.left = (i == 0 ? DEFAULT_KEY_LEFT : KSYM_UNDEFINED);
10575 si->input[i].key.right = (i == 0 ? DEFAULT_KEY_RIGHT : KSYM_UNDEFINED);
10576 si->input[i].key.up = (i == 0 ? DEFAULT_KEY_UP : KSYM_UNDEFINED);
10577 si->input[i].key.down = (i == 0 ? DEFAULT_KEY_DOWN : KSYM_UNDEFINED);
10578 si->input[i].key.snap = (i == 0 ? DEFAULT_KEY_SNAP : KSYM_UNDEFINED);
10579 si->input[i].key.drop = (i == 0 ? DEFAULT_KEY_DROP : KSYM_UNDEFINED);
10582 si->system.sdl_renderdriver = getStringCopy(ARG_DEFAULT);
10583 si->system.sdl_videodriver = getStringCopy(ARG_DEFAULT);
10584 si->system.sdl_audiodriver = getStringCopy(ARG_DEFAULT);
10585 si->system.audio_fragment_size = DEFAULT_AUDIO_FRAGMENT_SIZE;
10587 si->internal.program_title = getStringCopy(PROGRAM_TITLE_STRING);
10588 si->internal.program_version = getStringCopy(getProgramRealVersionString());
10589 si->internal.program_author = getStringCopy(PROGRAM_AUTHOR_STRING);
10590 si->internal.program_email = getStringCopy(PROGRAM_EMAIL_STRING);
10591 si->internal.program_website = getStringCopy(PROGRAM_WEBSITE_STRING);
10592 si->internal.program_copyright = getStringCopy(PROGRAM_COPYRIGHT_STRING);
10593 si->internal.program_company = getStringCopy(PROGRAM_COMPANY_STRING);
10595 si->internal.program_icon_file = getStringCopy(PROGRAM_ICON_FILENAME);
10597 si->internal.default_graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
10598 si->internal.default_sounds_set = getStringCopy(SND_CLASSIC_SUBDIR);
10599 si->internal.default_music_set = getStringCopy(MUS_CLASSIC_SUBDIR);
10601 si->internal.fallback_graphics_file = getStringCopy(UNDEFINED_FILENAME);
10602 si->internal.fallback_sounds_file = getStringCopy(UNDEFINED_FILENAME);
10603 si->internal.fallback_music_file = getStringCopy(UNDEFINED_FILENAME);
10605 si->internal.default_level_series = getStringCopy(UNDEFINED_LEVELSET);
10606 si->internal.choose_from_top_leveldir = FALSE;
10607 si->internal.show_scaling_in_title = TRUE;
10608 si->internal.create_user_levelset = TRUE;
10609 si->internal.info_screens_from_main = FALSE;
10611 si->internal.default_window_width = WIN_XSIZE_DEFAULT;
10612 si->internal.default_window_height = WIN_YSIZE_DEFAULT;
10614 si->debug.frame_delay[0] = DEFAULT_FRAME_DELAY_0;
10615 si->debug.frame_delay[1] = DEFAULT_FRAME_DELAY_1;
10616 si->debug.frame_delay[2] = DEFAULT_FRAME_DELAY_2;
10617 si->debug.frame_delay[3] = DEFAULT_FRAME_DELAY_3;
10618 si->debug.frame_delay[4] = DEFAULT_FRAME_DELAY_4;
10619 si->debug.frame_delay[5] = DEFAULT_FRAME_DELAY_5;
10620 si->debug.frame_delay[6] = DEFAULT_FRAME_DELAY_6;
10621 si->debug.frame_delay[7] = DEFAULT_FRAME_DELAY_7;
10622 si->debug.frame_delay[8] = DEFAULT_FRAME_DELAY_8;
10623 si->debug.frame_delay[9] = DEFAULT_FRAME_DELAY_9;
10625 si->debug.frame_delay_key[0] = DEFAULT_KEY_FRAME_DELAY_0;
10626 si->debug.frame_delay_key[1] = DEFAULT_KEY_FRAME_DELAY_1;
10627 si->debug.frame_delay_key[2] = DEFAULT_KEY_FRAME_DELAY_2;
10628 si->debug.frame_delay_key[3] = DEFAULT_KEY_FRAME_DELAY_3;
10629 si->debug.frame_delay_key[4] = DEFAULT_KEY_FRAME_DELAY_4;
10630 si->debug.frame_delay_key[5] = DEFAULT_KEY_FRAME_DELAY_5;
10631 si->debug.frame_delay_key[6] = DEFAULT_KEY_FRAME_DELAY_6;
10632 si->debug.frame_delay_key[7] = DEFAULT_KEY_FRAME_DELAY_7;
10633 si->debug.frame_delay_key[8] = DEFAULT_KEY_FRAME_DELAY_8;
10634 si->debug.frame_delay_key[9] = DEFAULT_KEY_FRAME_DELAY_9;
10636 si->debug.frame_delay_use_mod_key = DEFAULT_FRAME_DELAY_USE_MOD_KEY;
10637 si->debug.frame_delay_game_only = DEFAULT_FRAME_DELAY_GAME_ONLY;
10639 si->debug.show_frames_per_second = FALSE;
10641 si->debug.xsn_mode = AUTO;
10642 si->debug.xsn_percent = 0;
10644 si->options.verbose = FALSE;
10646 #if defined(PLATFORM_ANDROID)
10647 si->fullscreen = TRUE;
10648 si->touch.overlay_buttons = TRUE;
10651 setHideSetupEntry(&setup.debug.xsn_mode);
10654 static void setSetupInfoToDefaults_AutoSetup(struct SetupInfo *si)
10656 si->auto_setup.editor_zoom_tilesize = MINI_TILESIZE;
10659 static void setSetupInfoToDefaults_ServerSetup(struct SetupInfo *si)
10661 si->player_uuid = NULL; // (will be set later)
10662 si->player_version = 1; // (will be set later)
10664 si->use_api_server = TRUE;
10665 si->api_server_hostname = getStringCopy(API_SERVER_HOSTNAME);
10666 si->api_server_password = getStringCopy(UNDEFINED_PASSWORD);
10667 si->ask_for_uploading_tapes = TRUE;
10668 si->ask_for_remaining_tapes = FALSE;
10669 si->provide_uploading_tapes = TRUE;
10670 si->ask_for_using_api_server = TRUE;
10671 si->has_remaining_tapes = FALSE;
10674 static void setSetupInfoToDefaults_EditorCascade(struct SetupInfo *si)
10676 si->editor_cascade.el_bd = TRUE;
10677 si->editor_cascade.el_em = TRUE;
10678 si->editor_cascade.el_emc = TRUE;
10679 si->editor_cascade.el_rnd = TRUE;
10680 si->editor_cascade.el_sb = TRUE;
10681 si->editor_cascade.el_sp = TRUE;
10682 si->editor_cascade.el_dc = TRUE;
10683 si->editor_cascade.el_dx = TRUE;
10685 si->editor_cascade.el_mm = TRUE;
10686 si->editor_cascade.el_df = TRUE;
10688 si->editor_cascade.el_chars = FALSE;
10689 si->editor_cascade.el_steel_chars = FALSE;
10690 si->editor_cascade.el_ce = FALSE;
10691 si->editor_cascade.el_ge = FALSE;
10692 si->editor_cascade.el_es = FALSE;
10693 si->editor_cascade.el_ref = FALSE;
10694 si->editor_cascade.el_user = FALSE;
10695 si->editor_cascade.el_dynamic = FALSE;
10698 #define MAX_HIDE_SETUP_TOKEN_SIZE 20
10700 static char *getHideSetupToken(void *setup_value)
10702 static char hide_setup_token[MAX_HIDE_SETUP_TOKEN_SIZE];
10704 if (setup_value != NULL)
10705 snprintf(hide_setup_token, MAX_HIDE_SETUP_TOKEN_SIZE, "%p", setup_value);
10707 return hide_setup_token;
10710 void setHideSetupEntry(void *setup_value)
10712 char *hide_setup_token = getHideSetupToken(setup_value);
10714 if (hide_setup_hash == NULL)
10715 hide_setup_hash = newSetupFileHash();
10717 if (setup_value != NULL)
10718 setHashEntry(hide_setup_hash, hide_setup_token, "");
10721 void removeHideSetupEntry(void *setup_value)
10723 char *hide_setup_token = getHideSetupToken(setup_value);
10725 if (setup_value != NULL)
10726 removeHashEntry(hide_setup_hash, hide_setup_token);
10729 boolean hideSetupEntry(void *setup_value)
10731 char *hide_setup_token = getHideSetupToken(setup_value);
10733 return (setup_value != NULL &&
10734 getHashEntry(hide_setup_hash, hide_setup_token) != NULL);
10737 static void setSetupInfoFromTokenText(SetupFileHash *setup_file_hash,
10738 struct TokenInfo *token_info,
10739 int token_nr, char *token_text)
10741 char *token_hide_text = getStringCat2(token_text, ".hide");
10742 char *token_hide_value = getHashEntry(setup_file_hash, token_hide_text);
10744 // set the value of this setup option in the setup option structure
10745 setSetupInfo(token_info, token_nr, getHashEntry(setup_file_hash, token_text));
10747 // check if this setup option should be hidden in the setup menu
10748 if (token_hide_value != NULL && get_boolean_from_string(token_hide_value))
10749 setHideSetupEntry(token_info[token_nr].value);
10751 free(token_hide_text);
10754 static void setSetupInfoFromTokenInfo(SetupFileHash *setup_file_hash,
10755 struct TokenInfo *token_info,
10758 setSetupInfoFromTokenText(setup_file_hash, token_info, token_nr,
10759 token_info[token_nr].text);
10762 static void decodeSetupFileHash_Default(SetupFileHash *setup_file_hash)
10766 if (!setup_file_hash)
10769 for (i = 0; i < ARRAY_SIZE(global_setup_tokens); i++)
10770 setSetupInfoFromTokenInfo(setup_file_hash, global_setup_tokens, i);
10772 setup.touch.grid_initialized = TRUE;
10773 for (i = 0; i < 2; i++)
10775 int grid_xsize = setup.touch.grid_xsize[i];
10776 int grid_ysize = setup.touch.grid_ysize[i];
10779 // if virtual buttons are not loaded from setup file, repeat initializing
10780 // virtual buttons grid with default values later when video is initialized
10781 if (grid_xsize == -1 ||
10784 setup.touch.grid_initialized = FALSE;
10789 for (y = 0; y < grid_ysize; y++)
10791 char token_string[MAX_LINE_LEN];
10793 sprintf(token_string, "touch.virtual_buttons.%d.%02d", i, y);
10795 char *value_string = getHashEntry(setup_file_hash, token_string);
10797 if (value_string == NULL)
10800 for (x = 0; x < grid_xsize; x++)
10802 char c = value_string[x];
10804 setup.touch.grid_button[i][x][y] =
10805 (c == '.' ? CHAR_GRID_BUTTON_NONE : c);
10810 for (i = 0; i < ARRAY_SIZE(editor_setup_tokens); i++)
10811 setSetupInfoFromTokenInfo(setup_file_hash, editor_setup_tokens, i);
10813 for (i = 0; i < ARRAY_SIZE(shortcut_setup_tokens); i++)
10814 setSetupInfoFromTokenInfo(setup_file_hash, shortcut_setup_tokens, i);
10816 for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
10820 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
10822 setup_input = setup.input[pnr];
10823 for (i = 0; i < ARRAY_SIZE(player_setup_tokens); i++)
10825 char full_token[100];
10827 sprintf(full_token, "%s%s", prefix, player_setup_tokens[i].text);
10828 setSetupInfoFromTokenText(setup_file_hash, player_setup_tokens, i,
10831 setup.input[pnr] = setup_input;
10834 for (i = 0; i < ARRAY_SIZE(system_setup_tokens); i++)
10835 setSetupInfoFromTokenInfo(setup_file_hash, system_setup_tokens, i);
10837 for (i = 0; i < ARRAY_SIZE(internal_setup_tokens); i++)
10838 setSetupInfoFromTokenInfo(setup_file_hash, internal_setup_tokens, i);
10840 for (i = 0; i < ARRAY_SIZE(debug_setup_tokens); i++)
10841 setSetupInfoFromTokenInfo(setup_file_hash, debug_setup_tokens, i);
10843 for (i = 0; i < ARRAY_SIZE(options_setup_tokens); i++)
10844 setSetupInfoFromTokenInfo(setup_file_hash, options_setup_tokens, i);
10846 setHideRelatedSetupEntries();
10849 static void decodeSetupFileHash_AutoSetup(SetupFileHash *setup_file_hash)
10853 if (!setup_file_hash)
10856 for (i = 0; i < ARRAY_SIZE(auto_setup_tokens); i++)
10857 setSetupInfo(auto_setup_tokens, i,
10858 getHashEntry(setup_file_hash,
10859 auto_setup_tokens[i].text));
10862 static void decodeSetupFileHash_ServerSetup(SetupFileHash *setup_file_hash)
10866 if (!setup_file_hash)
10869 for (i = 0; i < ARRAY_SIZE(server_setup_tokens); i++)
10870 setSetupInfo(server_setup_tokens, i,
10871 getHashEntry(setup_file_hash,
10872 server_setup_tokens[i].text));
10875 static void decodeSetupFileHash_EditorCascade(SetupFileHash *setup_file_hash)
10879 if (!setup_file_hash)
10882 for (i = 0; i < ARRAY_SIZE(editor_cascade_setup_tokens); i++)
10883 setSetupInfo(editor_cascade_setup_tokens, i,
10884 getHashEntry(setup_file_hash,
10885 editor_cascade_setup_tokens[i].text));
10888 void LoadUserNames(void)
10890 int last_user_nr = user.nr;
10893 if (global.user_names != NULL)
10895 for (i = 0; i < MAX_PLAYER_NAMES; i++)
10896 checked_free(global.user_names[i]);
10898 checked_free(global.user_names);
10901 global.user_names = checked_calloc(MAX_PLAYER_NAMES * sizeof(char *));
10903 for (i = 0; i < MAX_PLAYER_NAMES; i++)
10907 SetupFileHash *setup_file_hash = loadSetupFileHash(getSetupFilename());
10909 if (setup_file_hash)
10911 char *player_name = getHashEntry(setup_file_hash, "player_name");
10913 global.user_names[i] = getFixedUserName(player_name);
10915 freeSetupFileHash(setup_file_hash);
10918 if (global.user_names[i] == NULL)
10919 global.user_names[i] = getStringCopy(getDefaultUserName(i));
10922 user.nr = last_user_nr;
10925 void LoadSetupFromFilename(char *filename)
10927 SetupFileHash *setup_file_hash = loadSetupFileHash(filename);
10929 if (setup_file_hash)
10931 decodeSetupFileHash_Default(setup_file_hash);
10933 freeSetupFileHash(setup_file_hash);
10937 Debug("setup", "using default setup values");
10941 static void LoadSetup_SpecialPostProcessing(void)
10943 char *player_name_new;
10945 // needed to work around problems with fixed length strings
10946 player_name_new = getFixedUserName(setup.player_name);
10947 free(setup.player_name);
10948 setup.player_name = player_name_new;
10950 // "scroll_delay: on(3) / off(0)" was replaced by scroll delay value
10951 if (setup.scroll_delay == FALSE)
10953 setup.scroll_delay_value = MIN_SCROLL_DELAY;
10954 setup.scroll_delay = TRUE; // now always "on"
10957 // make sure that scroll delay value stays inside valid range
10958 setup.scroll_delay_value =
10959 MIN(MAX(MIN_SCROLL_DELAY, setup.scroll_delay_value), MAX_SCROLL_DELAY);
10962 void LoadSetup_Default(void)
10966 // always start with reliable default values
10967 setSetupInfoToDefaults(&setup);
10969 // try to load setup values from default setup file
10970 filename = getDefaultSetupFilename();
10972 if (fileExists(filename))
10973 LoadSetupFromFilename(filename);
10975 // try to load setup values from platform setup file
10976 filename = getPlatformSetupFilename();
10978 if (fileExists(filename))
10979 LoadSetupFromFilename(filename);
10981 // try to load setup values from user setup file
10982 filename = getSetupFilename();
10984 LoadSetupFromFilename(filename);
10986 LoadSetup_SpecialPostProcessing();
10989 void LoadSetup_AutoSetup(void)
10991 char *filename = getPath2(getSetupDir(), AUTOSETUP_FILENAME);
10992 SetupFileHash *setup_file_hash = NULL;
10994 // always start with reliable default values
10995 setSetupInfoToDefaults_AutoSetup(&setup);
10997 setup_file_hash = loadSetupFileHash(filename);
10999 if (setup_file_hash)
11001 decodeSetupFileHash_AutoSetup(setup_file_hash);
11003 freeSetupFileHash(setup_file_hash);
11009 void LoadSetup_ServerSetup(void)
11011 char *filename = getPath2(getSetupDir(), SERVERSETUP_FILENAME);
11012 SetupFileHash *setup_file_hash = NULL;
11014 // always start with reliable default values
11015 setSetupInfoToDefaults_ServerSetup(&setup);
11017 setup_file_hash = loadSetupFileHash(filename);
11019 if (setup_file_hash)
11021 decodeSetupFileHash_ServerSetup(setup_file_hash);
11023 freeSetupFileHash(setup_file_hash);
11028 if (setup.player_uuid == NULL)
11030 // player UUID does not yet exist in setup file
11031 setup.player_uuid = getStringCopy(getUUID());
11032 setup.player_version = 2;
11034 SaveSetup_ServerSetup();
11038 void LoadSetup_EditorCascade(void)
11040 char *filename = getPath2(getSetupDir(), EDITORCASCADE_FILENAME);
11041 SetupFileHash *setup_file_hash = NULL;
11043 // always start with reliable default values
11044 setSetupInfoToDefaults_EditorCascade(&setup);
11046 setup_file_hash = loadSetupFileHash(filename);
11048 if (setup_file_hash)
11050 decodeSetupFileHash_EditorCascade(setup_file_hash);
11052 freeSetupFileHash(setup_file_hash);
11058 void LoadSetup(void)
11060 LoadSetup_Default();
11061 LoadSetup_AutoSetup();
11062 LoadSetup_ServerSetup();
11063 LoadSetup_EditorCascade();
11066 static void addGameControllerMappingToHash(SetupFileHash *mappings_hash,
11067 char *mapping_line)
11069 char mapping_guid[MAX_LINE_LEN];
11070 char *mapping_start, *mapping_end;
11072 // get GUID from game controller mapping line: copy complete line
11073 strncpy(mapping_guid, mapping_line, MAX_LINE_LEN - 1);
11074 mapping_guid[MAX_LINE_LEN - 1] = '\0';
11076 // get GUID from game controller mapping line: cut after GUID part
11077 mapping_start = strchr(mapping_guid, ',');
11078 if (mapping_start != NULL)
11079 *mapping_start = '\0';
11081 // cut newline from game controller mapping line
11082 mapping_end = strchr(mapping_line, '\n');
11083 if (mapping_end != NULL)
11084 *mapping_end = '\0';
11086 // add mapping entry to game controller mappings hash
11087 setHashEntry(mappings_hash, mapping_guid, mapping_line);
11090 static void LoadSetup_ReadGameControllerMappings(SetupFileHash *mappings_hash,
11095 if (!(file = fopen(filename, MODE_READ)))
11097 Warn("cannot read game controller mappings file '%s'", filename);
11102 while (!feof(file))
11104 char line[MAX_LINE_LEN];
11106 if (!fgets(line, MAX_LINE_LEN, file))
11109 addGameControllerMappingToHash(mappings_hash, line);
11115 void SaveSetup_Default(void)
11117 char *filename = getSetupFilename();
11121 InitUserDataDirectory();
11123 if (!(file = fopen(filename, MODE_WRITE)))
11125 Warn("cannot write setup file '%s'", filename);
11130 fprintFileHeader(file, SETUP_FILENAME);
11132 for (i = 0; i < ARRAY_SIZE(global_setup_tokens); i++)
11134 // just to make things nicer :)
11135 if (global_setup_tokens[i].value == &setup.multiple_users ||
11136 global_setup_tokens[i].value == &setup.sound ||
11137 global_setup_tokens[i].value == &setup.graphics_set ||
11138 global_setup_tokens[i].value == &setup.volume_simple ||
11139 global_setup_tokens[i].value == &setup.network_mode ||
11140 global_setup_tokens[i].value == &setup.touch.control_type ||
11141 global_setup_tokens[i].value == &setup.touch.grid_xsize[0] ||
11142 global_setup_tokens[i].value == &setup.touch.grid_xsize[1])
11143 fprintf(file, "\n");
11145 fprintf(file, "%s\n", getSetupLine(global_setup_tokens, "", i));
11148 for (i = 0; i < 2; i++)
11150 int grid_xsize = setup.touch.grid_xsize[i];
11151 int grid_ysize = setup.touch.grid_ysize[i];
11154 fprintf(file, "\n");
11156 for (y = 0; y < grid_ysize; y++)
11158 char token_string[MAX_LINE_LEN];
11159 char value_string[MAX_LINE_LEN];
11161 sprintf(token_string, "touch.virtual_buttons.%d.%02d", i, y);
11163 for (x = 0; x < grid_xsize; x++)
11165 char c = setup.touch.grid_button[i][x][y];
11167 value_string[x] = (c == CHAR_GRID_BUTTON_NONE ? '.' : c);
11170 value_string[grid_xsize] = '\0';
11172 fprintf(file, "%s\n", getFormattedSetupEntry(token_string, value_string));
11176 fprintf(file, "\n");
11177 for (i = 0; i < ARRAY_SIZE(editor_setup_tokens); i++)
11178 fprintf(file, "%s\n", getSetupLine(editor_setup_tokens, "", i));
11180 fprintf(file, "\n");
11181 for (i = 0; i < ARRAY_SIZE(shortcut_setup_tokens); i++)
11182 fprintf(file, "%s\n", getSetupLine(shortcut_setup_tokens, "", i));
11184 for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
11188 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
11189 fprintf(file, "\n");
11191 setup_input = setup.input[pnr];
11192 for (i = 0; i < ARRAY_SIZE(player_setup_tokens); i++)
11193 fprintf(file, "%s\n", getSetupLine(player_setup_tokens, prefix, i));
11196 fprintf(file, "\n");
11197 for (i = 0; i < ARRAY_SIZE(system_setup_tokens); i++)
11198 fprintf(file, "%s\n", getSetupLine(system_setup_tokens, "", i));
11200 // (internal setup values not saved to user setup file)
11202 fprintf(file, "\n");
11203 for (i = 0; i < ARRAY_SIZE(debug_setup_tokens); i++)
11204 if (!strPrefix(debug_setup_tokens[i].text, "debug.xsn_") ||
11205 setup.debug.xsn_mode != AUTO)
11206 fprintf(file, "%s\n", getSetupLine(debug_setup_tokens, "", i));
11208 fprintf(file, "\n");
11209 for (i = 0; i < ARRAY_SIZE(options_setup_tokens); i++)
11210 fprintf(file, "%s\n", getSetupLine(options_setup_tokens, "", i));
11214 SetFilePermissions(filename, PERMS_PRIVATE);
11217 void SaveSetup_AutoSetup(void)
11219 char *filename = getPath2(getSetupDir(), AUTOSETUP_FILENAME);
11223 InitUserDataDirectory();
11225 if (!(file = fopen(filename, MODE_WRITE)))
11227 Warn("cannot write auto setup file '%s'", filename);
11234 fprintFileHeader(file, AUTOSETUP_FILENAME);
11236 for (i = 0; i < ARRAY_SIZE(auto_setup_tokens); i++)
11237 fprintf(file, "%s\n", getSetupLine(auto_setup_tokens, "", i));
11241 SetFilePermissions(filename, PERMS_PRIVATE);
11246 void SaveSetup_ServerSetup(void)
11248 char *filename = getPath2(getSetupDir(), SERVERSETUP_FILENAME);
11252 InitUserDataDirectory();
11254 if (!(file = fopen(filename, MODE_WRITE)))
11256 Warn("cannot write server setup file '%s'", filename);
11263 fprintFileHeader(file, SERVERSETUP_FILENAME);
11265 for (i = 0; i < ARRAY_SIZE(server_setup_tokens); i++)
11267 // just to make things nicer :)
11268 if (server_setup_tokens[i].value == &setup.use_api_server)
11269 fprintf(file, "\n");
11271 fprintf(file, "%s\n", getSetupLine(server_setup_tokens, "", i));
11276 SetFilePermissions(filename, PERMS_PRIVATE);
11281 void SaveSetup_EditorCascade(void)
11283 char *filename = getPath2(getSetupDir(), EDITORCASCADE_FILENAME);
11287 InitUserDataDirectory();
11289 if (!(file = fopen(filename, MODE_WRITE)))
11291 Warn("cannot write editor cascade state file '%s'", filename);
11298 fprintFileHeader(file, EDITORCASCADE_FILENAME);
11300 for (i = 0; i < ARRAY_SIZE(editor_cascade_setup_tokens); i++)
11301 fprintf(file, "%s\n", getSetupLine(editor_cascade_setup_tokens, "", i));
11305 SetFilePermissions(filename, PERMS_PRIVATE);
11310 void SaveSetup(void)
11312 SaveSetup_Default();
11313 SaveSetup_AutoSetup();
11314 SaveSetup_ServerSetup();
11315 SaveSetup_EditorCascade();
11318 static void SaveSetup_WriteGameControllerMappings(SetupFileHash *mappings_hash,
11323 if (!(file = fopen(filename, MODE_WRITE)))
11325 Warn("cannot write game controller mappings file '%s'", filename);
11330 BEGIN_HASH_ITERATION(mappings_hash, itr)
11332 fprintf(file, "%s\n", HASH_ITERATION_VALUE(itr));
11334 END_HASH_ITERATION(mappings_hash, itr)
11339 void SaveSetup_AddGameControllerMapping(char *mapping)
11341 char *filename = getPath2(getSetupDir(), GAMECONTROLLER_BASENAME);
11342 SetupFileHash *mappings_hash = newSetupFileHash();
11344 InitUserDataDirectory();
11346 // load existing personal game controller mappings
11347 LoadSetup_ReadGameControllerMappings(mappings_hash, filename);
11349 // add new mapping to personal game controller mappings
11350 addGameControllerMappingToHash(mappings_hash, mapping);
11352 // save updated personal game controller mappings
11353 SaveSetup_WriteGameControllerMappings(mappings_hash, filename);
11355 freeSetupFileHash(mappings_hash);
11359 void LoadCustomElementDescriptions(void)
11361 char *filename = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
11362 SetupFileHash *setup_file_hash;
11365 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
11367 if (element_info[i].custom_description != NULL)
11369 free(element_info[i].custom_description);
11370 element_info[i].custom_description = NULL;
11374 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
11377 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
11379 char *token = getStringCat2(element_info[i].token_name, ".name");
11380 char *value = getHashEntry(setup_file_hash, token);
11383 element_info[i].custom_description = getStringCopy(value);
11388 freeSetupFileHash(setup_file_hash);
11391 static int getElementFromToken(char *token)
11393 char *value = getHashEntry(element_token_hash, token);
11396 return atoi(value);
11398 Warn("unknown element token '%s'", token);
11400 return EL_UNDEFINED;
11403 void FreeGlobalAnimEventInfo(void)
11405 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
11407 if (gaei->event_list == NULL)
11412 for (i = 0; i < gaei->num_event_lists; i++)
11414 checked_free(gaei->event_list[i]->event_value);
11415 checked_free(gaei->event_list[i]);
11418 checked_free(gaei->event_list);
11420 gaei->event_list = NULL;
11421 gaei->num_event_lists = 0;
11424 static int AddGlobalAnimEventList(void)
11426 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
11427 int list_pos = gaei->num_event_lists++;
11429 gaei->event_list = checked_realloc(gaei->event_list, gaei->num_event_lists *
11430 sizeof(struct GlobalAnimEventListInfo *));
11432 gaei->event_list[list_pos] =
11433 checked_calloc(sizeof(struct GlobalAnimEventListInfo));
11435 struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
11437 gaeli->event_value = NULL;
11438 gaeli->num_event_values = 0;
11443 static int AddGlobalAnimEventValue(int list_pos, int event_value)
11445 // do not add empty global animation events
11446 if (event_value == ANIM_EVENT_NONE)
11449 // if list position is undefined, create new list
11450 if (list_pos == ANIM_EVENT_UNDEFINED)
11451 list_pos = AddGlobalAnimEventList();
11453 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
11454 struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
11455 int value_pos = gaeli->num_event_values++;
11457 gaeli->event_value = checked_realloc(gaeli->event_value,
11458 gaeli->num_event_values * sizeof(int *));
11460 gaeli->event_value[value_pos] = event_value;
11465 int GetGlobalAnimEventValue(int list_pos, int value_pos)
11467 if (list_pos == ANIM_EVENT_UNDEFINED)
11468 return ANIM_EVENT_NONE;
11470 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
11471 struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
11473 return gaeli->event_value[value_pos];
11476 int GetGlobalAnimEventValueCount(int list_pos)
11478 if (list_pos == ANIM_EVENT_UNDEFINED)
11481 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
11482 struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
11484 return gaeli->num_event_values;
11487 // This function checks if a string <s> of the format "string1, string2, ..."
11488 // exactly contains a string <s_contained>.
11490 static boolean string_has_parameter(char *s, char *s_contained)
11494 if (s == NULL || s_contained == NULL)
11497 if (strlen(s_contained) > strlen(s))
11500 if (strncmp(s, s_contained, strlen(s_contained)) == 0)
11502 char next_char = s[strlen(s_contained)];
11504 // check if next character is delimiter or whitespace
11505 return (next_char == ',' || next_char == '\0' ||
11506 next_char == ' ' || next_char == '\t' ? TRUE : FALSE);
11509 // check if string contains another parameter string after a comma
11510 substring = strchr(s, ',');
11511 if (substring == NULL) // string does not contain a comma
11514 // advance string pointer to next character after the comma
11517 // skip potential whitespaces after the comma
11518 while (*substring == ' ' || *substring == '\t')
11521 return string_has_parameter(substring, s_contained);
11524 static int get_anim_parameter_value(char *s)
11526 int event_value[] =
11534 char *pattern_1[] =
11542 char *pattern_2 = ".part_";
11543 char *matching_char = NULL;
11545 int pattern_1_len = 0;
11546 int result = ANIM_EVENT_NONE;
11549 for (i = 0; i < ARRAY_SIZE(event_value); i++)
11551 matching_char = strstr(s_ptr, pattern_1[i]);
11552 pattern_1_len = strlen(pattern_1[i]);
11553 result = event_value[i];
11555 if (matching_char != NULL)
11559 if (matching_char == NULL)
11560 return ANIM_EVENT_NONE;
11562 s_ptr = matching_char + pattern_1_len;
11564 // check for main animation number ("anim_X" or "anim_XX")
11565 if (*s_ptr >= '0' && *s_ptr <= '9')
11567 int gic_anim_nr = (*s_ptr++ - '0');
11569 if (*s_ptr >= '0' && *s_ptr <= '9')
11570 gic_anim_nr = 10 * gic_anim_nr + (*s_ptr++ - '0');
11572 if (gic_anim_nr < 1 || gic_anim_nr > MAX_GLOBAL_ANIMS)
11573 return ANIM_EVENT_NONE;
11575 result |= gic_anim_nr << ANIM_EVENT_ANIM_BIT;
11579 // invalid main animation number specified
11581 return ANIM_EVENT_NONE;
11584 // check for animation part number ("part_X" or "part_XX") (optional)
11585 if (strPrefix(s_ptr, pattern_2))
11587 s_ptr += strlen(pattern_2);
11589 if (*s_ptr >= '0' && *s_ptr <= '9')
11591 int gic_part_nr = (*s_ptr++ - '0');
11593 if (*s_ptr >= '0' && *s_ptr <= '9')
11594 gic_part_nr = 10 * gic_part_nr + (*s_ptr++ - '0');
11596 if (gic_part_nr < 1 || gic_part_nr > MAX_GLOBAL_ANIM_PARTS)
11597 return ANIM_EVENT_NONE;
11599 result |= gic_part_nr << ANIM_EVENT_PART_BIT;
11603 // invalid animation part number specified
11605 return ANIM_EVENT_NONE;
11609 // discard result if next character is neither delimiter nor whitespace
11610 if (!(*s_ptr == ',' || *s_ptr == '\0' ||
11611 *s_ptr == ' ' || *s_ptr == '\t'))
11612 return ANIM_EVENT_NONE;
11617 static int get_anim_parameter_values(char *s)
11619 int list_pos = ANIM_EVENT_UNDEFINED;
11620 int event_value = ANIM_EVENT_DEFAULT;
11622 if (string_has_parameter(s, "any"))
11623 event_value |= ANIM_EVENT_ANY;
11625 if (string_has_parameter(s, "click:self") ||
11626 string_has_parameter(s, "click") ||
11627 string_has_parameter(s, "self"))
11628 event_value |= ANIM_EVENT_SELF;
11630 if (string_has_parameter(s, "unclick:any"))
11631 event_value |= ANIM_EVENT_UNCLICK_ANY;
11633 // if animation event found, add it to global animation event list
11634 if (event_value != ANIM_EVENT_NONE)
11635 list_pos = AddGlobalAnimEventValue(list_pos, event_value);
11639 // add optional "click:anim_X" or "click:anim_X.part_X" parameter
11640 event_value = get_anim_parameter_value(s);
11642 // if animation event found, add it to global animation event list
11643 if (event_value != ANIM_EVENT_NONE)
11644 list_pos = AddGlobalAnimEventValue(list_pos, event_value);
11646 // continue with next part of the string, starting with next comma
11647 s = strchr(s + 1, ',');
11653 static int get_anim_action_parameter_value(char *token)
11655 // check most common default case first to massively speed things up
11656 if (strEqual(token, ARG_UNDEFINED))
11657 return ANIM_EVENT_ACTION_NONE;
11659 int result = getImageIDFromToken(token);
11663 char *gfx_token = getStringCat2("gfx.", token);
11665 result = getImageIDFromToken(gfx_token);
11667 checked_free(gfx_token);
11672 Key key = getKeyFromX11KeyName(token);
11674 if (key != KSYM_UNDEFINED)
11675 result = -(int)key;
11682 result = get_hash_from_key(token); // unsigned int => int
11683 result = ABS(result); // may be negative now
11684 result += (result < MAX_IMAGE_FILES ? MAX_IMAGE_FILES : 0);
11686 setHashEntry(anim_url_hash, int2str(result, 0), token);
11691 result = ANIM_EVENT_ACTION_NONE;
11696 int get_parameter_value(char *value_raw, char *suffix, int type)
11698 char *value = getStringToLower(value_raw);
11699 int result = 0; // probably a save default value
11701 if (strEqual(suffix, ".direction"))
11703 result = (strEqual(value, "left") ? MV_LEFT :
11704 strEqual(value, "right") ? MV_RIGHT :
11705 strEqual(value, "up") ? MV_UP :
11706 strEqual(value, "down") ? MV_DOWN : MV_NONE);
11708 else if (strEqual(suffix, ".position"))
11710 result = (strEqual(value, "left") ? POS_LEFT :
11711 strEqual(value, "right") ? POS_RIGHT :
11712 strEqual(value, "top") ? POS_TOP :
11713 strEqual(value, "upper") ? POS_UPPER :
11714 strEqual(value, "middle") ? POS_MIDDLE :
11715 strEqual(value, "lower") ? POS_LOWER :
11716 strEqual(value, "bottom") ? POS_BOTTOM :
11717 strEqual(value, "any") ? POS_ANY :
11718 strEqual(value, "last") ? POS_LAST : POS_UNDEFINED);
11720 else if (strEqual(suffix, ".align"))
11722 result = (strEqual(value, "left") ? ALIGN_LEFT :
11723 strEqual(value, "right") ? ALIGN_RIGHT :
11724 strEqual(value, "center") ? ALIGN_CENTER :
11725 strEqual(value, "middle") ? ALIGN_CENTER : ALIGN_DEFAULT);
11727 else if (strEqual(suffix, ".valign"))
11729 result = (strEqual(value, "top") ? VALIGN_TOP :
11730 strEqual(value, "bottom") ? VALIGN_BOTTOM :
11731 strEqual(value, "middle") ? VALIGN_MIDDLE :
11732 strEqual(value, "center") ? VALIGN_MIDDLE : VALIGN_DEFAULT);
11734 else if (strEqual(suffix, ".anim_mode"))
11736 result = (string_has_parameter(value, "none") ? ANIM_NONE :
11737 string_has_parameter(value, "loop") ? ANIM_LOOP :
11738 string_has_parameter(value, "linear") ? ANIM_LINEAR :
11739 string_has_parameter(value, "pingpong") ? ANIM_PINGPONG :
11740 string_has_parameter(value, "pingpong2") ? ANIM_PINGPONG2 :
11741 string_has_parameter(value, "random") ? ANIM_RANDOM :
11742 string_has_parameter(value, "random_static") ? ANIM_RANDOM_STATIC :
11743 string_has_parameter(value, "ce_value") ? ANIM_CE_VALUE :
11744 string_has_parameter(value, "ce_score") ? ANIM_CE_SCORE :
11745 string_has_parameter(value, "ce_delay") ? ANIM_CE_DELAY :
11746 string_has_parameter(value, "horizontal") ? ANIM_HORIZONTAL :
11747 string_has_parameter(value, "vertical") ? ANIM_VERTICAL :
11748 string_has_parameter(value, "centered") ? ANIM_CENTERED :
11749 string_has_parameter(value, "all") ? ANIM_ALL :
11750 string_has_parameter(value, "tiled") ? ANIM_TILED :
11753 if (string_has_parameter(value, "once"))
11754 result |= ANIM_ONCE;
11756 if (string_has_parameter(value, "reverse"))
11757 result |= ANIM_REVERSE;
11759 if (string_has_parameter(value, "opaque_player"))
11760 result |= ANIM_OPAQUE_PLAYER;
11762 if (string_has_parameter(value, "static_panel"))
11763 result |= ANIM_STATIC_PANEL;
11765 else if (strEqual(suffix, ".init_event") ||
11766 strEqual(suffix, ".anim_event"))
11768 result = get_anim_parameter_values(value);
11770 else if (strEqual(suffix, ".init_delay_action") ||
11771 strEqual(suffix, ".anim_delay_action") ||
11772 strEqual(suffix, ".post_delay_action") ||
11773 strEqual(suffix, ".init_event_action") ||
11774 strEqual(suffix, ".anim_event_action"))
11776 result = get_anim_action_parameter_value(value_raw);
11778 else if (strEqual(suffix, ".class"))
11780 result = (strEqual(value, ARG_UNDEFINED) ? ARG_UNDEFINED_VALUE :
11781 get_hash_from_key(value));
11783 else if (strEqual(suffix, ".style"))
11785 result = STYLE_DEFAULT;
11787 if (string_has_parameter(value, "accurate_borders"))
11788 result |= STYLE_ACCURATE_BORDERS;
11790 if (string_has_parameter(value, "inner_corners"))
11791 result |= STYLE_INNER_CORNERS;
11793 if (string_has_parameter(value, "reverse"))
11794 result |= STYLE_REVERSE;
11796 if (string_has_parameter(value, "leftmost_position"))
11797 result |= STYLE_LEFTMOST_POSITION;
11799 if (string_has_parameter(value, "block_clicks"))
11800 result |= STYLE_BLOCK;
11802 if (string_has_parameter(value, "passthrough_clicks"))
11803 result |= STYLE_PASSTHROUGH;
11805 if (string_has_parameter(value, "multiple_actions"))
11806 result |= STYLE_MULTIPLE_ACTIONS;
11808 else if (strEqual(suffix, ".fade_mode"))
11810 result = (string_has_parameter(value, "none") ? FADE_MODE_NONE :
11811 string_has_parameter(value, "fade") ? FADE_MODE_FADE :
11812 string_has_parameter(value, "crossfade") ? FADE_MODE_CROSSFADE :
11813 string_has_parameter(value, "melt") ? FADE_MODE_MELT :
11814 string_has_parameter(value, "curtain") ? FADE_MODE_CURTAIN :
11815 FADE_MODE_DEFAULT);
11817 else if (strEqual(suffix, ".auto_delay_unit"))
11819 result = (string_has_parameter(value, "ms") ? AUTO_DELAY_UNIT_MS :
11820 string_has_parameter(value, "frames") ? AUTO_DELAY_UNIT_FRAMES :
11821 AUTO_DELAY_UNIT_DEFAULT);
11823 else if (strPrefix(suffix, ".font")) // (may also be ".font_xyz")
11825 result = gfx.get_font_from_token_function(value);
11827 else // generic parameter of type integer or boolean
11829 result = (strEqual(value, ARG_UNDEFINED) ? ARG_UNDEFINED_VALUE :
11830 type == TYPE_INTEGER ? get_integer_from_string(value) :
11831 type == TYPE_BOOLEAN ? get_boolean_from_string(value) :
11832 ARG_UNDEFINED_VALUE);
11840 static int get_token_parameter_value(char *token, char *value_raw)
11844 if (token == NULL || value_raw == NULL)
11845 return ARG_UNDEFINED_VALUE;
11847 suffix = strrchr(token, '.');
11848 if (suffix == NULL)
11851 if (strEqual(suffix, ".element"))
11852 return getElementFromToken(value_raw);
11854 // !!! USE CORRECT VALUE TYPE (currently works also for TYPE_BOOLEAN) !!!
11855 return get_parameter_value(value_raw, suffix, TYPE_INTEGER);
11858 void InitMenuDesignSettings_FromHash(SetupFileHash *setup_file_hash,
11859 boolean ignore_defaults)
11863 for (i = 0; image_config_vars[i].token != NULL; i++)
11865 char *value = getHashEntry(setup_file_hash, image_config_vars[i].token);
11867 // (ignore definitions set to "[DEFAULT]" which are already initialized)
11868 if (ignore_defaults && strEqual(value, ARG_DEFAULT))
11872 *image_config_vars[i].value =
11873 get_token_parameter_value(image_config_vars[i].token, value);
11877 void InitMenuDesignSettings_Static(void)
11879 // always start with reliable default values from static default config
11880 InitMenuDesignSettings_FromHash(image_config_hash, FALSE);
11883 static void InitMenuDesignSettings_SpecialPreProcessing(void)
11887 // the following initializes hierarchical values from static configuration
11889 // special case: initialize "ARG_DEFAULT" values in static default config
11890 // (e.g., initialize "[titlemessage].fade_mode" from "[title].fade_mode")
11891 titlescreen_initial_first_default.fade_mode =
11892 title_initial_first_default.fade_mode;
11893 titlescreen_initial_first_default.fade_delay =
11894 title_initial_first_default.fade_delay;
11895 titlescreen_initial_first_default.post_delay =
11896 title_initial_first_default.post_delay;
11897 titlescreen_initial_first_default.auto_delay =
11898 title_initial_first_default.auto_delay;
11899 titlescreen_initial_first_default.auto_delay_unit =
11900 title_initial_first_default.auto_delay_unit;
11901 titlescreen_first_default.fade_mode = title_first_default.fade_mode;
11902 titlescreen_first_default.fade_delay = title_first_default.fade_delay;
11903 titlescreen_first_default.post_delay = title_first_default.post_delay;
11904 titlescreen_first_default.auto_delay = title_first_default.auto_delay;
11905 titlescreen_first_default.auto_delay_unit =
11906 title_first_default.auto_delay_unit;
11907 titlemessage_initial_first_default.fade_mode =
11908 title_initial_first_default.fade_mode;
11909 titlemessage_initial_first_default.fade_delay =
11910 title_initial_first_default.fade_delay;
11911 titlemessage_initial_first_default.post_delay =
11912 title_initial_first_default.post_delay;
11913 titlemessage_initial_first_default.auto_delay =
11914 title_initial_first_default.auto_delay;
11915 titlemessage_initial_first_default.auto_delay_unit =
11916 title_initial_first_default.auto_delay_unit;
11917 titlemessage_first_default.fade_mode = title_first_default.fade_mode;
11918 titlemessage_first_default.fade_delay = title_first_default.fade_delay;
11919 titlemessage_first_default.post_delay = title_first_default.post_delay;
11920 titlemessage_first_default.auto_delay = title_first_default.auto_delay;
11921 titlemessage_first_default.auto_delay_unit =
11922 title_first_default.auto_delay_unit;
11924 titlescreen_initial_default.fade_mode = title_initial_default.fade_mode;
11925 titlescreen_initial_default.fade_delay = title_initial_default.fade_delay;
11926 titlescreen_initial_default.post_delay = title_initial_default.post_delay;
11927 titlescreen_initial_default.auto_delay = title_initial_default.auto_delay;
11928 titlescreen_initial_default.auto_delay_unit =
11929 title_initial_default.auto_delay_unit;
11930 titlescreen_default.fade_mode = title_default.fade_mode;
11931 titlescreen_default.fade_delay = title_default.fade_delay;
11932 titlescreen_default.post_delay = title_default.post_delay;
11933 titlescreen_default.auto_delay = title_default.auto_delay;
11934 titlescreen_default.auto_delay_unit = title_default.auto_delay_unit;
11935 titlemessage_initial_default.fade_mode = title_initial_default.fade_mode;
11936 titlemessage_initial_default.fade_delay = title_initial_default.fade_delay;
11937 titlemessage_initial_default.post_delay = title_initial_default.post_delay;
11938 titlemessage_initial_default.auto_delay_unit =
11939 title_initial_default.auto_delay_unit;
11940 titlemessage_default.fade_mode = title_default.fade_mode;
11941 titlemessage_default.fade_delay = title_default.fade_delay;
11942 titlemessage_default.post_delay = title_default.post_delay;
11943 titlemessage_default.auto_delay = title_default.auto_delay;
11944 titlemessage_default.auto_delay_unit = title_default.auto_delay_unit;
11946 // special case: initialize "ARG_DEFAULT" values in static default config
11947 // (e.g., init "titlemessage_1.fade_mode" from "[titlemessage].fade_mode")
11948 for (i = 0; i < MAX_NUM_TITLE_MESSAGES; i++)
11950 titlescreen_initial_first[i] = titlescreen_initial_first_default;
11951 titlescreen_first[i] = titlescreen_first_default;
11952 titlemessage_initial_first[i] = titlemessage_initial_first_default;
11953 titlemessage_first[i] = titlemessage_first_default;
11955 titlescreen_initial[i] = titlescreen_initial_default;
11956 titlescreen[i] = titlescreen_default;
11957 titlemessage_initial[i] = titlemessage_initial_default;
11958 titlemessage[i] = titlemessage_default;
11961 // special case: initialize "ARG_DEFAULT" values in static default config
11962 // (eg, init "menu.enter_screen.SCORES.xyz" from "menu.enter_screen.xyz")
11963 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
11965 if (i == GFX_SPECIAL_ARG_TITLE) // title values already initialized
11968 menu.enter_screen[i] = menu.enter_screen[GFX_SPECIAL_ARG_DEFAULT];
11969 menu.leave_screen[i] = menu.leave_screen[GFX_SPECIAL_ARG_DEFAULT];
11970 menu.next_screen[i] = menu.next_screen[GFX_SPECIAL_ARG_DEFAULT];
11973 // special case: initialize "ARG_DEFAULT" values in static default config
11974 // (eg, init "viewport.door_1.MAIN.xyz" from "viewport.door_1.xyz")
11975 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
11977 viewport.window[i] = viewport.window[GFX_SPECIAL_ARG_DEFAULT];
11978 viewport.playfield[i] = viewport.playfield[GFX_SPECIAL_ARG_DEFAULT];
11979 viewport.door_1[i] = viewport.door_1[GFX_SPECIAL_ARG_DEFAULT];
11981 if (i == GFX_SPECIAL_ARG_EDITOR) // editor values already initialized
11984 viewport.door_2[i] = viewport.door_2[GFX_SPECIAL_ARG_DEFAULT];
11988 static void InitMenuDesignSettings_SpecialPostProcessing(void)
11992 struct XY *dst, *src;
11994 game_buttons_xy[] =
11996 { &game.button.save, &game.button.stop },
11997 { &game.button.pause2, &game.button.pause },
11998 { &game.button.load, &game.button.play },
11999 { &game.button.undo, &game.button.stop },
12000 { &game.button.redo, &game.button.play },
12006 // special case: initialize later added SETUP list size from LEVELS value
12007 if (menu.list_size[GAME_MODE_SETUP] == -1)
12008 menu.list_size[GAME_MODE_SETUP] = menu.list_size[GAME_MODE_LEVELS];
12010 // set default position for snapshot buttons to stop/pause/play buttons
12011 for (i = 0; game_buttons_xy[i].dst != NULL; i++)
12012 if ((*game_buttons_xy[i].dst).x == -1 &&
12013 (*game_buttons_xy[i].dst).y == -1)
12014 *game_buttons_xy[i].dst = *game_buttons_xy[i].src;
12016 // --------------------------------------------------------------------------
12017 // dynamic viewports (including playfield margins, borders and alignments)
12018 // --------------------------------------------------------------------------
12020 // dynamic viewports currently only supported for landscape mode
12021 int display_width = MAX(video.display_width, video.display_height);
12022 int display_height = MIN(video.display_width, video.display_height);
12024 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
12026 struct RectWithBorder *vp_window = &viewport.window[i];
12027 struct RectWithBorder *vp_playfield = &viewport.playfield[i];
12028 struct RectWithBorder *vp_door_1 = &viewport.door_1[i];
12029 struct RectWithBorder *vp_door_2 = &viewport.door_2[i];
12030 boolean dynamic_window_width = (vp_window->min_width != -1);
12031 boolean dynamic_window_height = (vp_window->min_height != -1);
12032 boolean dynamic_playfield_width = (vp_playfield->min_width != -1);
12033 boolean dynamic_playfield_height = (vp_playfield->min_height != -1);
12035 // adjust window size if min/max width/height is specified
12037 if (vp_window->min_width != -1)
12039 int window_width = display_width;
12041 // when using static window height, use aspect ratio of display
12042 if (vp_window->min_height == -1)
12043 window_width = vp_window->height * display_width / display_height;
12045 vp_window->width = MAX(vp_window->min_width, window_width);
12048 if (vp_window->min_height != -1)
12050 int window_height = display_height;
12052 // when using static window width, use aspect ratio of display
12053 if (vp_window->min_width == -1)
12054 window_height = vp_window->width * display_height / display_width;
12056 vp_window->height = MAX(vp_window->min_height, window_height);
12059 if (vp_window->max_width != -1)
12060 vp_window->width = MIN(vp_window->width, vp_window->max_width);
12062 if (vp_window->max_height != -1)
12063 vp_window->height = MIN(vp_window->height, vp_window->max_height);
12065 int playfield_width = vp_window->width;
12066 int playfield_height = vp_window->height;
12068 // adjust playfield size and position according to specified margins
12070 playfield_width -= vp_playfield->margin_left;
12071 playfield_width -= vp_playfield->margin_right;
12073 playfield_height -= vp_playfield->margin_top;
12074 playfield_height -= vp_playfield->margin_bottom;
12076 // adjust playfield size if min/max width/height is specified
12078 if (vp_playfield->min_width != -1)
12079 vp_playfield->width = MAX(vp_playfield->min_width, playfield_width);
12081 if (vp_playfield->min_height != -1)
12082 vp_playfield->height = MAX(vp_playfield->min_height, playfield_height);
12084 if (vp_playfield->max_width != -1)
12085 vp_playfield->width = MIN(vp_playfield->width, vp_playfield->max_width);
12087 if (vp_playfield->max_height != -1)
12088 vp_playfield->height = MIN(vp_playfield->height,vp_playfield->max_height);
12090 // adjust playfield position according to specified alignment
12092 if (vp_playfield->align == ALIGN_LEFT || vp_playfield->x > 0)
12093 vp_playfield->x = ALIGNED_VP_XPOS(vp_playfield);
12094 else if (vp_playfield->align == ALIGN_CENTER)
12095 vp_playfield->x = playfield_width / 2 - vp_playfield->width / 2;
12096 else if (vp_playfield->align == ALIGN_RIGHT)
12097 vp_playfield->x += playfield_width - vp_playfield->width;
12099 if (vp_playfield->valign == VALIGN_TOP || vp_playfield->y > 0)
12100 vp_playfield->y = ALIGNED_VP_YPOS(vp_playfield);
12101 else if (vp_playfield->valign == VALIGN_MIDDLE)
12102 vp_playfield->y = playfield_height / 2 - vp_playfield->height / 2;
12103 else if (vp_playfield->valign == VALIGN_BOTTOM)
12104 vp_playfield->y += playfield_height - vp_playfield->height;
12106 vp_playfield->x += vp_playfield->margin_left;
12107 vp_playfield->y += vp_playfield->margin_top;
12109 // adjust individual playfield borders if only default border is specified
12111 if (vp_playfield->border_left == -1)
12112 vp_playfield->border_left = vp_playfield->border_size;
12113 if (vp_playfield->border_right == -1)
12114 vp_playfield->border_right = vp_playfield->border_size;
12115 if (vp_playfield->border_top == -1)
12116 vp_playfield->border_top = vp_playfield->border_size;
12117 if (vp_playfield->border_bottom == -1)
12118 vp_playfield->border_bottom = vp_playfield->border_size;
12120 // set dynamic playfield borders if borders are specified as undefined
12121 // (but only if window size was dynamic and playfield size was static)
12123 if (dynamic_window_width && !dynamic_playfield_width)
12125 if (vp_playfield->border_left == -1)
12127 vp_playfield->border_left = (vp_playfield->x -
12128 vp_playfield->margin_left);
12129 vp_playfield->x -= vp_playfield->border_left;
12130 vp_playfield->width += vp_playfield->border_left;
12133 if (vp_playfield->border_right == -1)
12135 vp_playfield->border_right = (vp_window->width -
12137 vp_playfield->width -
12138 vp_playfield->margin_right);
12139 vp_playfield->width += vp_playfield->border_right;
12143 if (dynamic_window_height && !dynamic_playfield_height)
12145 if (vp_playfield->border_top == -1)
12147 vp_playfield->border_top = (vp_playfield->y -
12148 vp_playfield->margin_top);
12149 vp_playfield->y -= vp_playfield->border_top;
12150 vp_playfield->height += vp_playfield->border_top;
12153 if (vp_playfield->border_bottom == -1)
12155 vp_playfield->border_bottom = (vp_window->height -
12157 vp_playfield->height -
12158 vp_playfield->margin_bottom);
12159 vp_playfield->height += vp_playfield->border_bottom;
12163 // adjust playfield size to be a multiple of a defined alignment tile size
12165 int align_size = vp_playfield->align_size;
12166 int playfield_xtiles = vp_playfield->width / align_size;
12167 int playfield_ytiles = vp_playfield->height / align_size;
12168 int playfield_width_corrected = playfield_xtiles * align_size;
12169 int playfield_height_corrected = playfield_ytiles * align_size;
12170 boolean is_playfield_mode = (i == GFX_SPECIAL_ARG_PLAYING ||
12171 i == GFX_SPECIAL_ARG_EDITOR);
12173 if (is_playfield_mode &&
12174 dynamic_playfield_width &&
12175 vp_playfield->width != playfield_width_corrected)
12177 int playfield_xdiff = vp_playfield->width - playfield_width_corrected;
12179 vp_playfield->width = playfield_width_corrected;
12181 if (vp_playfield->align == ALIGN_LEFT)
12183 vp_playfield->border_left += playfield_xdiff;
12185 else if (vp_playfield->align == ALIGN_RIGHT)
12187 vp_playfield->border_right += playfield_xdiff;
12189 else if (vp_playfield->align == ALIGN_CENTER)
12191 int border_left_diff = playfield_xdiff / 2;
12192 int border_right_diff = playfield_xdiff - border_left_diff;
12194 vp_playfield->border_left += border_left_diff;
12195 vp_playfield->border_right += border_right_diff;
12199 if (is_playfield_mode &&
12200 dynamic_playfield_height &&
12201 vp_playfield->height != playfield_height_corrected)
12203 int playfield_ydiff = vp_playfield->height - playfield_height_corrected;
12205 vp_playfield->height = playfield_height_corrected;
12207 if (vp_playfield->valign == VALIGN_TOP)
12209 vp_playfield->border_top += playfield_ydiff;
12211 else if (vp_playfield->align == VALIGN_BOTTOM)
12213 vp_playfield->border_right += playfield_ydiff;
12215 else if (vp_playfield->align == VALIGN_MIDDLE)
12217 int border_top_diff = playfield_ydiff / 2;
12218 int border_bottom_diff = playfield_ydiff - border_top_diff;
12220 vp_playfield->border_top += border_top_diff;
12221 vp_playfield->border_bottom += border_bottom_diff;
12225 // adjust door positions according to specified alignment
12227 for (j = 0; j < 2; j++)
12229 struct RectWithBorder *vp_door = (j == 0 ? vp_door_1 : vp_door_2);
12231 if (vp_door->align == ALIGN_LEFT || vp_door->x > 0)
12232 vp_door->x = ALIGNED_VP_XPOS(vp_door);
12233 else if (vp_door->align == ALIGN_CENTER)
12234 vp_door->x = vp_window->width / 2 - vp_door->width / 2;
12235 else if (vp_door->align == ALIGN_RIGHT)
12236 vp_door->x += vp_window->width - vp_door->width;
12238 if (vp_door->valign == VALIGN_TOP || vp_door->y > 0)
12239 vp_door->y = ALIGNED_VP_YPOS(vp_door);
12240 else if (vp_door->valign == VALIGN_MIDDLE)
12241 vp_door->y = vp_window->height / 2 - vp_door->height / 2;
12242 else if (vp_door->valign == VALIGN_BOTTOM)
12243 vp_door->y += vp_window->height - vp_door->height;
12248 static void InitMenuDesignSettings_SpecialPostProcessing_AfterGraphics(void)
12252 struct XYTileSize *dst, *src;
12255 editor_buttons_xy[] =
12258 &editor.button.element_left, &editor.palette.element_left,
12259 IMG_GFX_EDITOR_BUTTON_ELEMENT_LEFT
12262 &editor.button.element_middle, &editor.palette.element_middle,
12263 IMG_GFX_EDITOR_BUTTON_ELEMENT_MIDDLE
12266 &editor.button.element_right, &editor.palette.element_right,
12267 IMG_GFX_EDITOR_BUTTON_ELEMENT_RIGHT
12274 // set default position for element buttons to element graphics
12275 for (i = 0; editor_buttons_xy[i].dst != NULL; i++)
12277 if ((*editor_buttons_xy[i].dst).x == -1 &&
12278 (*editor_buttons_xy[i].dst).y == -1)
12280 struct GraphicInfo *gd = &graphic_info[editor_buttons_xy[i].graphic];
12282 gd->width = gd->height = editor_buttons_xy[i].src->tile_size;
12284 *editor_buttons_xy[i].dst = *editor_buttons_xy[i].src;
12288 // adjust editor palette rows and columns if specified to be dynamic
12290 if (editor.palette.cols == -1)
12292 int vp_width = viewport.playfield[GFX_SPECIAL_ARG_EDITOR].width;
12293 int bt_width = graphic_info[IMG_EDITOR_PALETTE_BUTTON].width;
12294 int sc_width = graphic_info[IMG_EDITOR_PALETTE_SCROLLBAR].width;
12296 editor.palette.cols = (vp_width - sc_width) / bt_width;
12298 if (editor.palette.x == -1)
12300 int palette_width = editor.palette.cols * bt_width + sc_width;
12302 editor.palette.x = (vp_width - palette_width) / 2;
12306 if (editor.palette.rows == -1)
12308 int vp_height = viewport.playfield[GFX_SPECIAL_ARG_EDITOR].height;
12309 int bt_height = graphic_info[IMG_EDITOR_PALETTE_BUTTON].height;
12310 int tx_height = getFontHeight(FONT_TEXT_2);
12312 editor.palette.rows = (vp_height - tx_height) / bt_height;
12314 if (editor.palette.y == -1)
12316 int palette_height = editor.palette.rows * bt_height + tx_height;
12318 editor.palette.y = (vp_height - palette_height) / 2;
12323 static void LoadMenuDesignSettingsFromFilename(char *filename)
12325 static struct TitleFadingInfo tfi;
12326 static struct TitleMessageInfo tmi;
12327 static struct TokenInfo title_tokens[] =
12329 { TYPE_INTEGER, &tfi.fade_mode, ".fade_mode" },
12330 { TYPE_INTEGER, &tfi.fade_delay, ".fade_delay" },
12331 { TYPE_INTEGER, &tfi.post_delay, ".post_delay" },
12332 { TYPE_INTEGER, &tfi.auto_delay, ".auto_delay" },
12333 { TYPE_INTEGER, &tfi.auto_delay_unit, ".auto_delay_unit" },
12337 static struct TokenInfo titlemessage_tokens[] =
12339 { TYPE_INTEGER, &tmi.x, ".x" },
12340 { TYPE_INTEGER, &tmi.y, ".y" },
12341 { TYPE_INTEGER, &tmi.width, ".width" },
12342 { TYPE_INTEGER, &tmi.height, ".height" },
12343 { TYPE_INTEGER, &tmi.chars, ".chars" },
12344 { TYPE_INTEGER, &tmi.lines, ".lines" },
12345 { TYPE_INTEGER, &tmi.align, ".align" },
12346 { TYPE_INTEGER, &tmi.valign, ".valign" },
12347 { TYPE_INTEGER, &tmi.font, ".font" },
12348 { TYPE_BOOLEAN, &tmi.autowrap, ".autowrap" },
12349 { TYPE_BOOLEAN, &tmi.centered, ".centered" },
12350 { TYPE_BOOLEAN, &tmi.parse_comments, ".parse_comments" },
12351 { TYPE_INTEGER, &tmi.sort_priority, ".sort_priority" },
12352 { TYPE_INTEGER, &tmi.fade_mode, ".fade_mode" },
12353 { TYPE_INTEGER, &tmi.fade_delay, ".fade_delay" },
12354 { TYPE_INTEGER, &tmi.post_delay, ".post_delay" },
12355 { TYPE_INTEGER, &tmi.auto_delay, ".auto_delay" },
12356 { TYPE_INTEGER, &tmi.auto_delay_unit, ".auto_delay_unit" },
12362 struct TitleFadingInfo *info;
12367 // initialize first titles from "enter screen" definitions, if defined
12368 { &title_initial_first_default, "menu.enter_screen.TITLE" },
12369 { &title_first_default, "menu.enter_screen.TITLE" },
12371 // initialize title screens from "next screen" definitions, if defined
12372 { &title_initial_default, "menu.next_screen.TITLE" },
12373 { &title_default, "menu.next_screen.TITLE" },
12379 struct TitleMessageInfo *array;
12382 titlemessage_arrays[] =
12384 // initialize first titles from "enter screen" definitions, if defined
12385 { titlescreen_initial_first, "menu.enter_screen.TITLE" },
12386 { titlescreen_first, "menu.enter_screen.TITLE" },
12387 { titlemessage_initial_first, "menu.enter_screen.TITLE" },
12388 { titlemessage_first, "menu.enter_screen.TITLE" },
12390 // initialize titles from "next screen" definitions, if defined
12391 { titlescreen_initial, "menu.next_screen.TITLE" },
12392 { titlescreen, "menu.next_screen.TITLE" },
12393 { titlemessage_initial, "menu.next_screen.TITLE" },
12394 { titlemessage, "menu.next_screen.TITLE" },
12396 // overwrite titles with title definitions, if defined
12397 { titlescreen_initial_first, "[title_initial]" },
12398 { titlescreen_first, "[title]" },
12399 { titlemessage_initial_first, "[title_initial]" },
12400 { titlemessage_first, "[title]" },
12402 { titlescreen_initial, "[title_initial]" },
12403 { titlescreen, "[title]" },
12404 { titlemessage_initial, "[title_initial]" },
12405 { titlemessage, "[title]" },
12407 // overwrite titles with title screen/message definitions, if defined
12408 { titlescreen_initial_first, "[titlescreen_initial]" },
12409 { titlescreen_first, "[titlescreen]" },
12410 { titlemessage_initial_first, "[titlemessage_initial]" },
12411 { titlemessage_first, "[titlemessage]" },
12413 { titlescreen_initial, "[titlescreen_initial]" },
12414 { titlescreen, "[titlescreen]" },
12415 { titlemessage_initial, "[titlemessage_initial]" },
12416 { titlemessage, "[titlemessage]" },
12420 SetupFileHash *setup_file_hash;
12423 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
12426 // the following initializes hierarchical values from dynamic configuration
12428 // special case: initialize with default values that may be overwritten
12429 // (e.g., init "menu.draw_xoffset.INFO" from "menu.draw_xoffset")
12430 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
12432 struct TokenIntPtrInfo menu_config[] =
12434 { "menu.draw_xoffset", &menu.draw_xoffset[i] },
12435 { "menu.draw_yoffset", &menu.draw_yoffset[i] },
12436 { "menu.list_size", &menu.list_size[i] }
12439 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
12441 char *token = menu_config[j].token;
12442 char *value = getHashEntry(setup_file_hash, token);
12445 *menu_config[j].value = get_integer_from_string(value);
12449 // special case: initialize with default values that may be overwritten
12450 // (eg, init "menu.draw_xoffset.INFO[XXX]" from "menu.draw_xoffset.INFO")
12451 for (i = 0; i < NUM_SPECIAL_GFX_INFO_ARGS; i++)
12453 struct TokenIntPtrInfo menu_config[] =
12455 { "menu.draw_xoffset.INFO", &menu.draw_xoffset_info[i] },
12456 { "menu.draw_yoffset.INFO", &menu.draw_yoffset_info[i] },
12457 { "menu.list_size.INFO", &menu.list_size_info[i] }
12460 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
12462 char *token = menu_config[j].token;
12463 char *value = getHashEntry(setup_file_hash, token);
12466 *menu_config[j].value = get_integer_from_string(value);
12470 // special case: initialize with default values that may be overwritten
12471 // (eg, init "menu.draw_xoffset.SETUP[XXX]" from "menu.draw_xoffset.SETUP")
12472 for (i = 0; i < NUM_SPECIAL_GFX_SETUP_ARGS; i++)
12474 struct TokenIntPtrInfo menu_config[] =
12476 { "menu.draw_xoffset.SETUP", &menu.draw_xoffset_setup[i] },
12477 { "menu.draw_yoffset.SETUP", &menu.draw_yoffset_setup[i] }
12480 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
12482 char *token = menu_config[j].token;
12483 char *value = getHashEntry(setup_file_hash, token);
12486 *menu_config[j].value = get_integer_from_string(value);
12490 // special case: initialize with default values that may be overwritten
12491 // (eg, init "menu.line_spacing.INFO[XXX]" from "menu.line_spacing.INFO")
12492 for (i = 0; i < NUM_SPECIAL_GFX_INFO_ARGS; i++)
12494 struct TokenIntPtrInfo menu_config[] =
12496 { "menu.left_spacing.INFO", &menu.left_spacing_info[i] },
12497 { "menu.right_spacing.INFO", &menu.right_spacing_info[i] },
12498 { "menu.top_spacing.INFO", &menu.top_spacing_info[i] },
12499 { "menu.bottom_spacing.INFO", &menu.bottom_spacing_info[i] },
12500 { "menu.paragraph_spacing.INFO", &menu.paragraph_spacing_info[i] },
12501 { "menu.headline1_spacing.INFO", &menu.headline1_spacing_info[i] },
12502 { "menu.headline2_spacing.INFO", &menu.headline2_spacing_info[i] },
12503 { "menu.line_spacing.INFO", &menu.line_spacing_info[i] },
12504 { "menu.extra_spacing.INFO", &menu.extra_spacing_info[i] },
12507 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
12509 char *token = menu_config[j].token;
12510 char *value = getHashEntry(setup_file_hash, token);
12513 *menu_config[j].value = get_integer_from_string(value);
12517 // special case: initialize with default values that may be overwritten
12518 // (eg, init "menu.enter_screen.SCORES.xyz" from "menu.enter_screen.xyz")
12519 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
12521 struct TokenIntPtrInfo menu_config[] =
12523 { "menu.enter_screen.fade_mode", &menu.enter_screen[i].fade_mode },
12524 { "menu.enter_screen.fade_delay", &menu.enter_screen[i].fade_delay },
12525 { "menu.enter_screen.post_delay", &menu.enter_screen[i].post_delay },
12526 { "menu.leave_screen.fade_mode", &menu.leave_screen[i].fade_mode },
12527 { "menu.leave_screen.fade_delay", &menu.leave_screen[i].fade_delay },
12528 { "menu.leave_screen.post_delay", &menu.leave_screen[i].post_delay },
12529 { "menu.next_screen.fade_mode", &menu.next_screen[i].fade_mode },
12530 { "menu.next_screen.fade_delay", &menu.next_screen[i].fade_delay },
12531 { "menu.next_screen.post_delay", &menu.next_screen[i].post_delay }
12534 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
12536 char *token = menu_config[j].token;
12537 char *value = getHashEntry(setup_file_hash, token);
12540 *menu_config[j].value = get_token_parameter_value(token, value);
12544 // special case: initialize with default values that may be overwritten
12545 // (eg, init "viewport.door_1.MAIN.xyz" from "viewport.door_1.xyz")
12546 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
12550 char *token_prefix;
12551 struct RectWithBorder *struct_ptr;
12555 { "viewport.window", &viewport.window[i] },
12556 { "viewport.playfield", &viewport.playfield[i] },
12557 { "viewport.door_1", &viewport.door_1[i] },
12558 { "viewport.door_2", &viewport.door_2[i] }
12561 for (j = 0; j < ARRAY_SIZE(vp_struct); j++)
12563 struct TokenIntPtrInfo vp_config[] =
12565 { ".x", &vp_struct[j].struct_ptr->x },
12566 { ".y", &vp_struct[j].struct_ptr->y },
12567 { ".width", &vp_struct[j].struct_ptr->width },
12568 { ".height", &vp_struct[j].struct_ptr->height },
12569 { ".min_width", &vp_struct[j].struct_ptr->min_width },
12570 { ".min_height", &vp_struct[j].struct_ptr->min_height },
12571 { ".max_width", &vp_struct[j].struct_ptr->max_width },
12572 { ".max_height", &vp_struct[j].struct_ptr->max_height },
12573 { ".margin_left", &vp_struct[j].struct_ptr->margin_left },
12574 { ".margin_right", &vp_struct[j].struct_ptr->margin_right },
12575 { ".margin_top", &vp_struct[j].struct_ptr->margin_top },
12576 { ".margin_bottom", &vp_struct[j].struct_ptr->margin_bottom },
12577 { ".border_left", &vp_struct[j].struct_ptr->border_left },
12578 { ".border_right", &vp_struct[j].struct_ptr->border_right },
12579 { ".border_top", &vp_struct[j].struct_ptr->border_top },
12580 { ".border_bottom", &vp_struct[j].struct_ptr->border_bottom },
12581 { ".border_size", &vp_struct[j].struct_ptr->border_size },
12582 { ".align_size", &vp_struct[j].struct_ptr->align_size },
12583 { ".align", &vp_struct[j].struct_ptr->align },
12584 { ".valign", &vp_struct[j].struct_ptr->valign }
12587 for (k = 0; k < ARRAY_SIZE(vp_config); k++)
12589 char *token = getStringCat2(vp_struct[j].token_prefix,
12590 vp_config[k].token);
12591 char *value = getHashEntry(setup_file_hash, token);
12594 *vp_config[k].value = get_token_parameter_value(token, value);
12601 // special case: initialize with default values that may be overwritten
12602 // (e.g., init "[title].fade_mode" from "menu.next_screen.TITLE.fade_mode")
12603 for (i = 0; title_info[i].info != NULL; i++)
12605 struct TitleFadingInfo *info = title_info[i].info;
12606 char *base_token = title_info[i].text;
12608 for (j = 0; title_tokens[j].type != -1; j++)
12610 char *token = getStringCat2(base_token, title_tokens[j].text);
12611 char *value = getHashEntry(setup_file_hash, token);
12615 int parameter_value = get_token_parameter_value(token, value);
12619 *(int *)title_tokens[j].value = (int)parameter_value;
12628 // special case: initialize with default values that may be overwritten
12629 // (e.g., init "titlemessage_1.fade_mode" from "[titlemessage].fade_mode")
12630 for (i = 0; titlemessage_arrays[i].array != NULL; i++)
12632 struct TitleMessageInfo *array = titlemessage_arrays[i].array;
12633 char *base_token = titlemessage_arrays[i].text;
12635 for (j = 0; titlemessage_tokens[j].type != -1; j++)
12637 char *token = getStringCat2(base_token, titlemessage_tokens[j].text);
12638 char *value = getHashEntry(setup_file_hash, token);
12642 int parameter_value = get_token_parameter_value(token, value);
12644 for (k = 0; k < MAX_NUM_TITLE_MESSAGES; k++)
12648 if (titlemessage_tokens[j].type == TYPE_INTEGER)
12649 *(int *)titlemessage_tokens[j].value = (int)parameter_value;
12651 *(boolean *)titlemessage_tokens[j].value = (boolean)parameter_value;
12661 // special case: check if network and preview player positions are redefined,
12662 // to compare this later against the main menu level preview being redefined
12663 struct TokenIntPtrInfo menu_config_players[] =
12665 { "main.network_players.x", &menu.main.network_players.redefined },
12666 { "main.network_players.y", &menu.main.network_players.redefined },
12667 { "main.preview_players.x", &menu.main.preview_players.redefined },
12668 { "main.preview_players.y", &menu.main.preview_players.redefined },
12669 { "preview.x", &preview.redefined },
12670 { "preview.y", &preview.redefined }
12673 for (i = 0; i < ARRAY_SIZE(menu_config_players); i++)
12674 *menu_config_players[i].value = FALSE;
12676 for (i = 0; i < ARRAY_SIZE(menu_config_players); i++)
12677 if (getHashEntry(setup_file_hash, menu_config_players[i].token) != NULL)
12678 *menu_config_players[i].value = TRUE;
12680 // read (and overwrite with) values that may be specified in config file
12681 InitMenuDesignSettings_FromHash(setup_file_hash, TRUE);
12683 freeSetupFileHash(setup_file_hash);
12686 void LoadMenuDesignSettings(void)
12688 char *filename_base = UNDEFINED_FILENAME, *filename_local;
12690 InitMenuDesignSettings_Static();
12691 InitMenuDesignSettings_SpecialPreProcessing();
12693 if (!GFX_OVERRIDE_ARTWORK(ARTWORK_TYPE_GRAPHICS))
12695 // first look for special settings configured in level series config
12696 filename_base = getCustomArtworkLevelConfigFilename(ARTWORK_TYPE_GRAPHICS);
12698 if (fileExists(filename_base))
12699 LoadMenuDesignSettingsFromFilename(filename_base);
12702 filename_local = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
12704 if (filename_local != NULL && !strEqual(filename_base, filename_local))
12705 LoadMenuDesignSettingsFromFilename(filename_local);
12707 InitMenuDesignSettings_SpecialPostProcessing();
12710 void LoadMenuDesignSettings_AfterGraphics(void)
12712 InitMenuDesignSettings_SpecialPostProcessing_AfterGraphics();
12715 void LoadUserDefinedEditorElementList(int **elements, int *num_elements)
12717 char *filename = getEditorSetupFilename();
12718 SetupFileList *setup_file_list, *list;
12719 SetupFileHash *element_hash;
12720 int num_unknown_tokens = 0;
12723 if ((setup_file_list = loadSetupFileList(filename)) == NULL)
12726 element_hash = newSetupFileHash();
12728 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
12729 setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
12731 // determined size may be larger than needed (due to unknown elements)
12733 for (list = setup_file_list; list != NULL; list = list->next)
12736 // add space for up to 3 more elements for padding that may be needed
12737 *num_elements += 3;
12739 // free memory for old list of elements, if needed
12740 checked_free(*elements);
12742 // allocate memory for new list of elements
12743 *elements = checked_malloc(*num_elements * sizeof(int));
12746 for (list = setup_file_list; list != NULL; list = list->next)
12748 char *value = getHashEntry(element_hash, list->token);
12750 if (value == NULL) // try to find obsolete token mapping
12752 char *mapped_token = get_mapped_token(list->token);
12754 if (mapped_token != NULL)
12756 value = getHashEntry(element_hash, mapped_token);
12758 free(mapped_token);
12764 (*elements)[(*num_elements)++] = atoi(value);
12768 if (num_unknown_tokens == 0)
12771 Warn("unknown token(s) found in config file:");
12772 Warn("- config file: '%s'", filename);
12774 num_unknown_tokens++;
12777 Warn("- token: '%s'", list->token);
12781 if (num_unknown_tokens > 0)
12784 while (*num_elements % 4) // pad with empty elements, if needed
12785 (*elements)[(*num_elements)++] = EL_EMPTY;
12787 freeSetupFileList(setup_file_list);
12788 freeSetupFileHash(element_hash);
12791 for (i = 0; i < *num_elements; i++)
12792 Debug("editor", "element '%s' [%d]\n",
12793 element_info[(*elements)[i]].token_name, (*elements)[i]);
12797 static struct MusicFileInfo *get_music_file_info_ext(char *basename, int music,
12800 SetupFileHash *setup_file_hash = NULL;
12801 struct MusicFileInfo tmp_music_file_info, *new_music_file_info;
12802 char *filename_music, *filename_prefix, *filename_info;
12808 token_to_value_ptr[] =
12810 { "title_header", &tmp_music_file_info.title_header },
12811 { "artist_header", &tmp_music_file_info.artist_header },
12812 { "album_header", &tmp_music_file_info.album_header },
12813 { "year_header", &tmp_music_file_info.year_header },
12815 { "title", &tmp_music_file_info.title },
12816 { "artist", &tmp_music_file_info.artist },
12817 { "album", &tmp_music_file_info.album },
12818 { "year", &tmp_music_file_info.year },
12824 filename_music = (is_sound ? getCustomSoundFilename(basename) :
12825 getCustomMusicFilename(basename));
12827 if (filename_music == NULL)
12830 // ---------- try to replace file extension ----------
12832 filename_prefix = getStringCopy(filename_music);
12833 if (strrchr(filename_prefix, '.') != NULL)
12834 *strrchr(filename_prefix, '.') = '\0';
12835 filename_info = getStringCat2(filename_prefix, ".txt");
12837 if (fileExists(filename_info))
12838 setup_file_hash = loadSetupFileHash(filename_info);
12840 free(filename_prefix);
12841 free(filename_info);
12843 if (setup_file_hash == NULL)
12845 // ---------- try to add file extension ----------
12847 filename_prefix = getStringCopy(filename_music);
12848 filename_info = getStringCat2(filename_prefix, ".txt");
12850 if (fileExists(filename_info))
12851 setup_file_hash = loadSetupFileHash(filename_info);
12853 free(filename_prefix);
12854 free(filename_info);
12857 if (setup_file_hash == NULL)
12860 // ---------- music file info found ----------
12862 clear_mem(&tmp_music_file_info, sizeof(struct MusicFileInfo));
12864 for (i = 0; token_to_value_ptr[i].token != NULL; i++)
12866 char *value = getHashEntry(setup_file_hash, token_to_value_ptr[i].token);
12868 *token_to_value_ptr[i].value_ptr =
12869 getStringCopy(value != NULL && *value != '\0' ? value : UNKNOWN_NAME);
12872 tmp_music_file_info.basename = getStringCopy(basename);
12873 tmp_music_file_info.music = music;
12874 tmp_music_file_info.is_sound = is_sound;
12876 new_music_file_info = checked_malloc(sizeof(struct MusicFileInfo));
12877 *new_music_file_info = tmp_music_file_info;
12879 return new_music_file_info;
12882 static struct MusicFileInfo *get_music_file_info(char *basename, int music)
12884 return get_music_file_info_ext(basename, music, FALSE);
12887 static struct MusicFileInfo *get_sound_file_info(char *basename, int sound)
12889 return get_music_file_info_ext(basename, sound, TRUE);
12892 static boolean music_info_listed_ext(struct MusicFileInfo *list,
12893 char *basename, boolean is_sound)
12895 for (; list != NULL; list = list->next)
12896 if (list->is_sound == is_sound && strEqual(list->basename, basename))
12902 static boolean music_info_listed(struct MusicFileInfo *list, char *basename)
12904 return music_info_listed_ext(list, basename, FALSE);
12907 static boolean sound_info_listed(struct MusicFileInfo *list, char *basename)
12909 return music_info_listed_ext(list, basename, TRUE);
12912 void LoadMusicInfo(void)
12914 char *music_directory = getCustomMusicDirectory_NoConf();
12915 int num_music = getMusicListSize();
12916 int num_music_noconf = 0;
12917 int num_sounds = getSoundListSize();
12919 DirectoryEntry *dir_entry;
12920 struct FileInfo *music, *sound;
12921 struct MusicFileInfo *next, **new;
12924 while (music_file_info != NULL)
12926 next = music_file_info->next;
12928 checked_free(music_file_info->basename);
12930 checked_free(music_file_info->title_header);
12931 checked_free(music_file_info->artist_header);
12932 checked_free(music_file_info->album_header);
12933 checked_free(music_file_info->year_header);
12935 checked_free(music_file_info->title);
12936 checked_free(music_file_info->artist);
12937 checked_free(music_file_info->album);
12938 checked_free(music_file_info->year);
12940 free(music_file_info);
12942 music_file_info = next;
12945 new = &music_file_info;
12947 for (i = 0; i < num_music; i++)
12949 music = getMusicListEntry(i);
12951 if (music->filename == NULL)
12954 if (strEqual(music->filename, UNDEFINED_FILENAME))
12957 // a configured file may be not recognized as music
12958 if (!FileIsMusic(music->filename))
12961 if (!music_info_listed(music_file_info, music->filename))
12963 *new = get_music_file_info(music->filename, i);
12966 new = &(*new)->next;
12970 if ((dir = openDirectory(music_directory)) == NULL)
12972 Warn("cannot read music directory '%s'", music_directory);
12977 while ((dir_entry = readDirectory(dir)) != NULL) // loop all entries
12979 char *basename = dir_entry->basename;
12980 boolean music_already_used = FALSE;
12983 // skip all music files that are configured in music config file
12984 for (i = 0; i < num_music; i++)
12986 music = getMusicListEntry(i);
12988 if (music->filename == NULL)
12991 if (strEqual(basename, music->filename))
12993 music_already_used = TRUE;
12998 if (music_already_used)
13001 if (!FileIsMusic(dir_entry->filename))
13004 if (!music_info_listed(music_file_info, basename))
13006 *new = get_music_file_info(basename, MAP_NOCONF_MUSIC(num_music_noconf));
13009 new = &(*new)->next;
13012 num_music_noconf++;
13015 closeDirectory(dir);
13017 for (i = 0; i < num_sounds; i++)
13019 sound = getSoundListEntry(i);
13021 if (sound->filename == NULL)
13024 if (strEqual(sound->filename, UNDEFINED_FILENAME))
13027 // a configured file may be not recognized as sound
13028 if (!FileIsSound(sound->filename))
13031 if (!sound_info_listed(music_file_info, sound->filename))
13033 *new = get_sound_file_info(sound->filename, i);
13035 new = &(*new)->next;
13039 // add pointers to previous list nodes
13041 struct MusicFileInfo *node = music_file_info;
13043 while (node != NULL)
13046 node->next->prev = node;
13052 static void add_helpanim_entry(int element, int action, int direction,
13053 int delay, int *num_list_entries)
13055 struct HelpAnimInfo *new_list_entry;
13056 (*num_list_entries)++;
13059 checked_realloc(helpanim_info,
13060 *num_list_entries * sizeof(struct HelpAnimInfo));
13061 new_list_entry = &helpanim_info[*num_list_entries - 1];
13063 new_list_entry->element = element;
13064 new_list_entry->action = action;
13065 new_list_entry->direction = direction;
13066 new_list_entry->delay = delay;
13069 static void print_unknown_token(char *filename, char *token, int token_nr)
13074 Warn("unknown token(s) found in config file:");
13075 Warn("- config file: '%s'", filename);
13078 Warn("- token: '%s'", token);
13081 static void print_unknown_token_end(int token_nr)
13087 void LoadHelpAnimInfo(void)
13089 char *filename = getHelpAnimFilename();
13090 SetupFileList *setup_file_list = NULL, *list;
13091 SetupFileHash *element_hash, *action_hash, *direction_hash;
13092 int num_list_entries = 0;
13093 int num_unknown_tokens = 0;
13096 if (fileExists(filename))
13097 setup_file_list = loadSetupFileList(filename);
13099 if (setup_file_list == NULL)
13101 // use reliable default values from static configuration
13102 SetupFileList *insert_ptr;
13104 insert_ptr = setup_file_list =
13105 newSetupFileList(helpanim_config[0].token,
13106 helpanim_config[0].value);
13108 for (i = 1; helpanim_config[i].token; i++)
13109 insert_ptr = addListEntry(insert_ptr,
13110 helpanim_config[i].token,
13111 helpanim_config[i].value);
13114 element_hash = newSetupFileHash();
13115 action_hash = newSetupFileHash();
13116 direction_hash = newSetupFileHash();
13118 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
13119 setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
13121 for (i = 0; i < NUM_ACTIONS; i++)
13122 setHashEntry(action_hash, element_action_info[i].suffix,
13123 i_to_a(element_action_info[i].value));
13125 // do not store direction index (bit) here, but direction value!
13126 for (i = 0; i < NUM_DIRECTIONS_FULL; i++)
13127 setHashEntry(direction_hash, element_direction_info[i].suffix,
13128 i_to_a(1 << element_direction_info[i].value));
13130 for (list = setup_file_list; list != NULL; list = list->next)
13132 char *element_token, *action_token, *direction_token;
13133 char *element_value, *action_value, *direction_value;
13134 int delay = atoi(list->value);
13136 if (strEqual(list->token, "end"))
13138 add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
13143 /* first try to break element into element/action/direction parts;
13144 if this does not work, also accept combined "element[.act][.dir]"
13145 elements (like "dynamite.active"), which are unique elements */
13147 if (strchr(list->token, '.') == NULL) // token contains no '.'
13149 element_value = getHashEntry(element_hash, list->token);
13150 if (element_value != NULL) // element found
13151 add_helpanim_entry(atoi(element_value), -1, -1, delay,
13152 &num_list_entries);
13155 // no further suffixes found -- this is not an element
13156 print_unknown_token(filename, list->token, num_unknown_tokens++);
13162 // token has format "<prefix>.<something>"
13164 action_token = strchr(list->token, '.'); // suffix may be action ...
13165 direction_token = action_token; // ... or direction
13167 element_token = getStringCopy(list->token);
13168 *strchr(element_token, '.') = '\0';
13170 element_value = getHashEntry(element_hash, element_token);
13172 if (element_value == NULL) // this is no element
13174 element_value = getHashEntry(element_hash, list->token);
13175 if (element_value != NULL) // combined element found
13176 add_helpanim_entry(atoi(element_value), -1, -1, delay,
13177 &num_list_entries);
13179 print_unknown_token(filename, list->token, num_unknown_tokens++);
13181 free(element_token);
13186 action_value = getHashEntry(action_hash, action_token);
13188 if (action_value != NULL) // action found
13190 add_helpanim_entry(atoi(element_value), atoi(action_value), -1, delay,
13191 &num_list_entries);
13193 free(element_token);
13198 direction_value = getHashEntry(direction_hash, direction_token);
13200 if (direction_value != NULL) // direction found
13202 add_helpanim_entry(atoi(element_value), -1, atoi(direction_value), delay,
13203 &num_list_entries);
13205 free(element_token);
13210 if (strchr(action_token + 1, '.') == NULL)
13212 // no further suffixes found -- this is not an action nor direction
13214 element_value = getHashEntry(element_hash, list->token);
13215 if (element_value != NULL) // combined element found
13216 add_helpanim_entry(atoi(element_value), -1, -1, delay,
13217 &num_list_entries);
13219 print_unknown_token(filename, list->token, num_unknown_tokens++);
13221 free(element_token);
13226 // token has format "<prefix>.<suffix>.<something>"
13228 direction_token = strchr(action_token + 1, '.');
13230 action_token = getStringCopy(action_token);
13231 *strchr(action_token + 1, '.') = '\0';
13233 action_value = getHashEntry(action_hash, action_token);
13235 if (action_value == NULL) // this is no action
13237 element_value = getHashEntry(element_hash, list->token);
13238 if (element_value != NULL) // combined element found
13239 add_helpanim_entry(atoi(element_value), -1, -1, delay,
13240 &num_list_entries);
13242 print_unknown_token(filename, list->token, num_unknown_tokens++);
13244 free(element_token);
13245 free(action_token);
13250 direction_value = getHashEntry(direction_hash, direction_token);
13252 if (direction_value != NULL) // direction found
13254 add_helpanim_entry(atoi(element_value), atoi(action_value),
13255 atoi(direction_value), delay, &num_list_entries);
13257 free(element_token);
13258 free(action_token);
13263 // this is no direction
13265 element_value = getHashEntry(element_hash, list->token);
13266 if (element_value != NULL) // combined element found
13267 add_helpanim_entry(atoi(element_value), -1, -1, delay,
13268 &num_list_entries);
13270 print_unknown_token(filename, list->token, num_unknown_tokens++);
13272 free(element_token);
13273 free(action_token);
13276 print_unknown_token_end(num_unknown_tokens);
13278 add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
13279 add_helpanim_entry(HELPANIM_LIST_END, -1, -1, -1, &num_list_entries);
13281 freeSetupFileList(setup_file_list);
13282 freeSetupFileHash(element_hash);
13283 freeSetupFileHash(action_hash);
13284 freeSetupFileHash(direction_hash);
13287 for (i = 0; i < num_list_entries; i++)
13288 Debug("files:LoadHelpAnimInfo", "'%s': %d, %d, %d => %d",
13289 EL_NAME(helpanim_info[i].element),
13290 helpanim_info[i].element,
13291 helpanim_info[i].action,
13292 helpanim_info[i].direction,
13293 helpanim_info[i].delay);
13297 void LoadHelpTextInfo(void)
13299 char *filename = getHelpTextFilename();
13302 if (helptext_info != NULL)
13304 freeSetupFileHash(helptext_info);
13305 helptext_info = NULL;
13308 if (fileExists(filename))
13309 helptext_info = loadSetupFileHash(filename);
13311 if (helptext_info == NULL)
13313 // use reliable default values from static configuration
13314 helptext_info = newSetupFileHash();
13316 for (i = 0; helptext_config[i].token; i++)
13317 setHashEntry(helptext_info,
13318 helptext_config[i].token,
13319 helptext_config[i].value);
13323 BEGIN_HASH_ITERATION(helptext_info, itr)
13325 Debug("files:LoadHelpTextInfo", "'%s' => '%s'",
13326 HASH_ITERATION_TOKEN(itr), HASH_ITERATION_VALUE(itr));
13328 END_HASH_ITERATION(hash, itr)
13333 // ----------------------------------------------------------------------------
13335 // ----------------------------------------------------------------------------
13337 #define MAX_NUM_CONVERT_LEVELS 1000
13339 void ConvertLevels(void)
13341 static LevelDirTree *convert_leveldir = NULL;
13342 static int convert_level_nr = -1;
13343 static int num_levels_handled = 0;
13344 static int num_levels_converted = 0;
13345 static boolean levels_failed[MAX_NUM_CONVERT_LEVELS];
13348 convert_leveldir = getTreeInfoFromIdentifier(leveldir_first,
13349 global.convert_leveldir);
13351 if (convert_leveldir == NULL)
13352 Fail("no such level identifier: '%s'", global.convert_leveldir);
13354 leveldir_current = convert_leveldir;
13356 if (global.convert_level_nr != -1)
13358 convert_leveldir->first_level = global.convert_level_nr;
13359 convert_leveldir->last_level = global.convert_level_nr;
13362 convert_level_nr = convert_leveldir->first_level;
13364 PrintLine("=", 79);
13365 Print("Converting levels\n");
13366 PrintLine("-", 79);
13367 Print("Level series identifier: '%s'\n", convert_leveldir->identifier);
13368 Print("Level series name: '%s'\n", convert_leveldir->name);
13369 Print("Level series author: '%s'\n", convert_leveldir->author);
13370 Print("Number of levels: %d\n", convert_leveldir->levels);
13371 PrintLine("=", 79);
13374 for (i = 0; i < MAX_NUM_CONVERT_LEVELS; i++)
13375 levels_failed[i] = FALSE;
13377 while (convert_level_nr <= convert_leveldir->last_level)
13379 char *level_filename;
13382 level_nr = convert_level_nr++;
13384 Print("Level %03d: ", level_nr);
13386 LoadLevel(level_nr);
13387 if (level.no_level_file || level.no_valid_file)
13389 Print("(no level)\n");
13393 Print("converting level ... ");
13396 // special case: conversion of some EMC levels as requested by ACME
13397 level.game_engine_type = GAME_ENGINE_TYPE_RND;
13400 level_filename = getDefaultLevelFilename(level_nr);
13401 new_level = !fileExists(level_filename);
13405 SaveLevel(level_nr);
13407 num_levels_converted++;
13409 Print("converted.\n");
13413 if (level_nr >= 0 && level_nr < MAX_NUM_CONVERT_LEVELS)
13414 levels_failed[level_nr] = TRUE;
13416 Print("NOT CONVERTED -- LEVEL ALREADY EXISTS.\n");
13419 num_levels_handled++;
13423 PrintLine("=", 79);
13424 Print("Number of levels handled: %d\n", num_levels_handled);
13425 Print("Number of levels converted: %d (%d%%)\n", num_levels_converted,
13426 (num_levels_handled ?
13427 num_levels_converted * 100 / num_levels_handled : 0));
13428 PrintLine("-", 79);
13429 Print("Summary (for automatic parsing by scripts):\n");
13430 Print("LEVELDIR '%s', CONVERTED %d/%d (%d%%)",
13431 convert_leveldir->identifier, num_levels_converted,
13432 num_levels_handled,
13433 (num_levels_handled ?
13434 num_levels_converted * 100 / num_levels_handled : 0));
13436 if (num_levels_handled != num_levels_converted)
13438 Print(", FAILED:");
13439 for (i = 0; i < MAX_NUM_CONVERT_LEVELS; i++)
13440 if (levels_failed[i])
13445 PrintLine("=", 79);
13447 CloseAllAndExit(0);
13451 // ----------------------------------------------------------------------------
13452 // create and save images for use in level sketches (raw BMP format)
13453 // ----------------------------------------------------------------------------
13455 void CreateLevelSketchImages(void)
13461 InitElementPropertiesGfxElement();
13463 bitmap1 = CreateBitmap(TILEX, TILEY, DEFAULT_DEPTH);
13464 bitmap2 = CreateBitmap(MINI_TILEX, MINI_TILEY, DEFAULT_DEPTH);
13466 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
13468 int element = getMappedElement(i);
13469 char basename1[16];
13470 char basename2[16];
13474 sprintf(basename1, "%04d.bmp", i);
13475 sprintf(basename2, "%04ds.bmp", i);
13477 filename1 = getPath2(global.create_sketch_images_dir, basename1);
13478 filename2 = getPath2(global.create_sketch_images_dir, basename2);
13480 DrawSizedElement(0, 0, element, TILESIZE);
13481 BlitBitmap(drawto, bitmap1, SX, SY, TILEX, TILEY, 0, 0);
13483 if (SDL_SaveBMP(bitmap1->surface, filename1) != 0)
13484 Fail("cannot save level sketch image file '%s'", filename1);
13486 DrawSizedElement(0, 0, element, MINI_TILESIZE);
13487 BlitBitmap(drawto, bitmap2, SX, SY, MINI_TILEX, MINI_TILEY, 0, 0);
13489 if (SDL_SaveBMP(bitmap2->surface, filename2) != 0)
13490 Fail("cannot save level sketch image file '%s'", filename2);
13495 // create corresponding SQL statements (for normal and small images)
13498 printf("insert into phpbb_words values (NULL, '`%03d', '<IMG class=\"levelsketch\" src=\"/I/%04d.png\"/>');\n", i, i);
13499 printf("insert into phpbb_words values (NULL, '¸%03d', '<IMG class=\"levelsketch\" src=\"/I/%04ds.png\"/>');\n", i, i);
13502 printf("insert into phpbb_words values (NULL, '`%04d', '<IMG class=\"levelsketch\" src=\"/I/%04d.png\"/>');\n", i, i);
13503 printf("insert into phpbb_words values (NULL, '¸%04d', '<IMG class=\"levelsketch\" src=\"/I/%04ds.png\"/>');\n", i, i);
13505 // optional: create content for forum level sketch demonstration post
13507 fprintf(stderr, "%03d `%03d%c", i, i, (i % 10 < 9 ? ' ' : '\n'));
13510 FreeBitmap(bitmap1);
13511 FreeBitmap(bitmap2);
13514 fprintf(stderr, "\n");
13516 Info("%d normal and small images created", NUM_FILE_ELEMENTS);
13518 CloseAllAndExit(0);
13522 // ----------------------------------------------------------------------------
13523 // create and save images for element collecting animations (raw BMP format)
13524 // ----------------------------------------------------------------------------
13526 static boolean createCollectImage(int element)
13528 return (IS_COLLECTIBLE(element) && !IS_SP_ELEMENT(element));
13531 void CreateCollectElementImages(void)
13535 int anim_frames = num_steps - 1;
13536 int tile_size = TILESIZE;
13537 int anim_width = tile_size * anim_frames;
13538 int anim_height = tile_size;
13539 int num_collect_images = 0;
13540 int pos_collect_images = 0;
13542 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
13543 if (createCollectImage(i))
13544 num_collect_images++;
13546 Info("Creating %d element collecting animation images ...",
13547 num_collect_images);
13549 int dst_width = anim_width * 2;
13550 int dst_height = anim_height * num_collect_images / 2;
13551 Bitmap *dst_bitmap = CreateBitmap(dst_width, dst_height, DEFAULT_DEPTH);
13552 char *basename_bmp = "RocksCollect.bmp";
13553 char *basename_png = "RocksCollect.png";
13554 char *filename_bmp = getPath2(global.create_collect_images_dir, basename_bmp);
13555 char *filename_png = getPath2(global.create_collect_images_dir, basename_png);
13556 int len_filename_bmp = strlen(filename_bmp);
13557 int len_filename_png = strlen(filename_png);
13558 int max_command_len = MAX_FILENAME_LEN + len_filename_bmp + len_filename_png;
13559 char cmd_convert[max_command_len];
13561 snprintf(cmd_convert, max_command_len, "convert \"%s\" \"%s\"",
13565 // force using RGBA surface for destination bitmap
13566 SDL_SetColorKey(dst_bitmap->surface, SET_TRANSPARENT_PIXEL,
13567 SDL_MapRGB(dst_bitmap->surface->format, 0x00, 0x00, 0x00));
13569 dst_bitmap->surface =
13570 SDL_ConvertSurfaceFormat(dst_bitmap->surface, SDL_PIXELFORMAT_ARGB8888, 0);
13572 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
13574 if (!createCollectImage(i))
13577 int dst_x = (pos_collect_images / (num_collect_images / 2)) * anim_width;
13578 int dst_y = (pos_collect_images % (num_collect_images / 2)) * anim_height;
13579 int graphic = el2img(i);
13580 char *token_name = element_info[i].token_name;
13581 Bitmap *tmp_bitmap = CreateBitmap(tile_size, tile_size, DEFAULT_DEPTH);
13582 Bitmap *src_bitmap;
13585 Info("- creating collecting image for '%s' ...", token_name);
13587 getGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
13589 BlitBitmap(src_bitmap, tmp_bitmap, src_x, src_y,
13590 tile_size, tile_size, 0, 0);
13592 // force using RGBA surface for temporary bitmap (using transparent black)
13593 SDL_SetColorKey(tmp_bitmap->surface, SET_TRANSPARENT_PIXEL,
13594 SDL_MapRGB(tmp_bitmap->surface->format, 0x00, 0x00, 0x00));
13596 tmp_bitmap->surface =
13597 SDL_ConvertSurfaceFormat(tmp_bitmap->surface, SDL_PIXELFORMAT_ARGB8888, 0);
13599 tmp_bitmap->surface_masked = tmp_bitmap->surface;
13601 for (j = 0; j < anim_frames; j++)
13603 int frame_size_final = tile_size * (anim_frames - j) / num_steps;
13604 int frame_size = frame_size_final * num_steps;
13605 int offset = (tile_size - frame_size_final) / 2;
13606 Bitmap *frame_bitmap = ZoomBitmap(tmp_bitmap, frame_size, frame_size);
13608 while (frame_size > frame_size_final)
13612 Bitmap *half_bitmap = ZoomBitmap(frame_bitmap, frame_size, frame_size);
13614 FreeBitmap(frame_bitmap);
13616 frame_bitmap = half_bitmap;
13619 BlitBitmapMasked(frame_bitmap, dst_bitmap, 0, 0,
13620 frame_size_final, frame_size_final,
13621 dst_x + j * tile_size + offset, dst_y + offset);
13623 FreeBitmap(frame_bitmap);
13626 tmp_bitmap->surface_masked = NULL;
13628 FreeBitmap(tmp_bitmap);
13630 pos_collect_images++;
13633 if (SDL_SaveBMP(dst_bitmap->surface, filename_bmp) != 0)
13634 Fail("cannot save element collecting image file '%s'", filename_bmp);
13636 FreeBitmap(dst_bitmap);
13638 Info("Converting image file from BMP to PNG ...");
13640 if (system(cmd_convert) != 0)
13641 Fail("converting image file failed");
13643 unlink(filename_bmp);
13647 CloseAllAndExit(0);
13651 // ----------------------------------------------------------------------------
13652 // create and save images for custom and group elements (raw BMP format)
13653 // ----------------------------------------------------------------------------
13655 void CreateCustomElementImages(char *directory)
13657 char *src_basename = "RocksCE-template.ilbm";
13658 char *dst_basename = "RocksCE.bmp";
13659 char *src_filename = getPath2(directory, src_basename);
13660 char *dst_filename = getPath2(directory, dst_basename);
13661 Bitmap *src_bitmap;
13663 int yoffset_ce = 0;
13664 int yoffset_ge = (TILEY * NUM_CUSTOM_ELEMENTS / 16);
13667 InitVideoDefaults();
13669 ReCreateBitmap(&backbuffer, video.width, video.height);
13671 src_bitmap = LoadImage(src_filename);
13673 bitmap = CreateBitmap(TILEX * 16 * 2,
13674 TILEY * (NUM_CUSTOM_ELEMENTS + NUM_GROUP_ELEMENTS) / 16,
13677 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
13684 BlitBitmap(src_bitmap, bitmap, 0, 0, TILEX, TILEY,
13685 TILEX * x, TILEY * y + yoffset_ce);
13687 BlitBitmap(src_bitmap, bitmap, 0, TILEY,
13689 TILEX * x + TILEX * 16,
13690 TILEY * y + yoffset_ce);
13692 for (j = 2; j >= 0; j--)
13696 BlitBitmap(src_bitmap, bitmap,
13697 TILEX + c * 7, 0, 6, 10,
13698 TILEX * x + 6 + j * 7,
13699 TILEY * y + 11 + yoffset_ce);
13701 BlitBitmap(src_bitmap, bitmap,
13702 TILEX + c * 8, TILEY, 6, 10,
13703 TILEX * 16 + TILEX * x + 6 + j * 8,
13704 TILEY * y + 10 + yoffset_ce);
13710 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
13717 BlitBitmap(src_bitmap, bitmap, 0, 0, TILEX, TILEY,
13718 TILEX * x, TILEY * y + yoffset_ge);
13720 BlitBitmap(src_bitmap, bitmap, 0, TILEY,
13722 TILEX * x + TILEX * 16,
13723 TILEY * y + yoffset_ge);
13725 for (j = 1; j >= 0; j--)
13729 BlitBitmap(src_bitmap, bitmap, TILEX + c * 10, 11, 10, 10,
13730 TILEX * x + 6 + j * 10,
13731 TILEY * y + 11 + yoffset_ge);
13733 BlitBitmap(src_bitmap, bitmap,
13734 TILEX + c * 8, TILEY + 12, 6, 10,
13735 TILEX * 16 + TILEX * x + 10 + j * 8,
13736 TILEY * y + 10 + yoffset_ge);
13742 if (SDL_SaveBMP(bitmap->surface, dst_filename) != 0)
13743 Fail("cannot save CE graphics file '%s'", dst_filename);
13745 FreeBitmap(bitmap);
13747 CloseAllAndExit(0);