1 // ============================================================================
2 // Rocks'n'Diamonds - McDuffin Strikes Back!
3 // ----------------------------------------------------------------------------
4 // (c) 1995-2014 by Artsoft Entertainment
7 // https://www.artsoft.org/
8 // ----------------------------------------------------------------------------
10 // ============================================================================
17 #include "libgame/libgame.h"
26 #define ENABLE_UNUSED_CODE 0 // currently unused functions
27 #define ENABLE_HISTORIC_CHUNKS 0 // only for historic reference
28 #define ENABLE_RESERVED_CODE 0 // reserved for later use
30 #define CHUNK_ID_LEN 4 // IFF style chunk id length
31 #define CHUNK_SIZE_UNDEFINED 0 // undefined chunk size == 0
32 #define CHUNK_SIZE_NONE -1 // do not write chunk size
34 #define LEVEL_CHUNK_NAME_SIZE MAX_LEVEL_NAME_LEN
35 #define LEVEL_CHUNK_AUTH_SIZE MAX_LEVEL_AUTHOR_LEN
37 #define LEVEL_CHUNK_VERS_SIZE 8 // size of file version chunk
38 #define LEVEL_CHUNK_DATE_SIZE 4 // size of file date chunk
39 #define LEVEL_CHUNK_HEAD_SIZE 80 // size of level file header
40 #define LEVEL_CHUNK_HEAD_UNUSED 0 // unused level header bytes
41 #define LEVEL_CHUNK_CNT2_SIZE 160 // size of level CNT2 chunk
42 #define LEVEL_CHUNK_CNT2_UNUSED 11 // unused CNT2 chunk bytes
43 #define LEVEL_CHUNK_CNT3_HEADER 16 // size of level CNT3 header
44 #define LEVEL_CHUNK_CNT3_UNUSED 10 // unused CNT3 chunk bytes
45 #define LEVEL_CPART_CUS3_SIZE 134 // size of CUS3 chunk part
46 #define LEVEL_CPART_CUS3_UNUSED 15 // unused CUS3 bytes / part
47 #define LEVEL_CHUNK_GRP1_SIZE 74 // size of level GRP1 chunk
49 // (element number, number of change pages, change page number)
50 #define LEVEL_CHUNK_CUSX_UNCHANGED (2 + (1 + 1) + (1 + 1))
52 // (element number only)
53 #define LEVEL_CHUNK_GRPX_UNCHANGED 2
54 #define LEVEL_CHUNK_NOTE_UNCHANGED 2
56 // (nothing at all if unchanged)
57 #define LEVEL_CHUNK_ELEM_UNCHANGED 0
59 #define TAPE_CHUNK_VERS_SIZE 8 // size of file version chunk
60 #define TAPE_CHUNK_HEAD_SIZE 20 // size of tape file header
61 #define TAPE_CHUNK_HEAD_UNUSED 1 // unused tape header bytes
62 #define TAPE_CHUNK_SCRN_SIZE 2 // size of screen size chunk
64 #define SCORE_CHUNK_VERS_SIZE 8 // size of file version chunk
66 #define LEVEL_CHUNK_CNT3_SIZE(x) (LEVEL_CHUNK_CNT3_HEADER + (x))
67 #define LEVEL_CHUNK_CUS3_SIZE(x) (2 + (x) * LEVEL_CPART_CUS3_SIZE)
68 #define LEVEL_CHUNK_CUS4_SIZE(x) (96 + (x) * 48)
70 // file identifier strings
71 #define LEVEL_COOKIE_TMPL "ROCKSNDIAMONDS_LEVEL_FILE_VERSION_x.x"
72 #define TAPE_COOKIE_TMPL "ROCKSNDIAMONDS_TAPE_FILE_VERSION_x.x"
73 #define SCORE_COOKIE_TMPL "ROCKSNDIAMONDS_SCORE_FILE_VERSION_x.x"
75 // values for deciding when (not) to save configuration data
76 #define SAVE_CONF_NEVER 0
77 #define SAVE_CONF_ALWAYS 1
78 #define SAVE_CONF_WHEN_CHANGED -1
80 // values for chunks using micro chunks
81 #define CONF_MASK_1_BYTE 0x00
82 #define CONF_MASK_2_BYTE 0x40
83 #define CONF_MASK_4_BYTE 0x80
84 #define CONF_MASK_MULTI_BYTES 0xc0
86 #define CONF_MASK_BYTES 0xc0
87 #define CONF_MASK_TOKEN 0x3f
89 #define CONF_VALUE_1_BYTE(x) (CONF_MASK_1_BYTE | (x))
90 #define CONF_VALUE_2_BYTE(x) (CONF_MASK_2_BYTE | (x))
91 #define CONF_VALUE_4_BYTE(x) (CONF_MASK_4_BYTE | (x))
92 #define CONF_VALUE_MULTI_BYTES(x) (CONF_MASK_MULTI_BYTES | (x))
94 // these definitions are just for convenience of use and readability
95 #define CONF_VALUE_8_BIT(x) CONF_VALUE_1_BYTE(x)
96 #define CONF_VALUE_16_BIT(x) CONF_VALUE_2_BYTE(x)
97 #define CONF_VALUE_32_BIT(x) CONF_VALUE_4_BYTE(x)
98 #define CONF_VALUE_BYTES(x) CONF_VALUE_MULTI_BYTES(x)
100 #define CONF_VALUE_NUM_BYTES(x) ((x) == CONF_MASK_1_BYTE ? 1 : \
101 (x) == CONF_MASK_2_BYTE ? 2 : \
102 (x) == CONF_MASK_4_BYTE ? 4 : 0)
104 #define CONF_CONTENT_NUM_ELEMENTS (3 * 3)
105 #define CONF_CONTENT_NUM_BYTES (CONF_CONTENT_NUM_ELEMENTS * 2)
106 #define CONF_ELEMENT_NUM_BYTES (2)
108 #define CONF_ENTITY_NUM_BYTES(t) ((t) == TYPE_ELEMENT || \
109 (t) == TYPE_ELEMENT_LIST ? \
110 CONF_ELEMENT_NUM_BYTES : \
111 (t) == TYPE_CONTENT || \
112 (t) == TYPE_CONTENT_LIST ? \
113 CONF_CONTENT_NUM_BYTES : 1)
115 #define CONF_ELEMENT_BYTE_POS(i) ((i) * CONF_ELEMENT_NUM_BYTES)
116 #define CONF_ELEMENTS_ELEMENT(b,i) ((b[CONF_ELEMENT_BYTE_POS(i)] << 8) | \
117 (b[CONF_ELEMENT_BYTE_POS(i) + 1]))
119 #define CONF_CONTENT_ELEMENT_POS(c,x,y) ((c) * CONF_CONTENT_NUM_ELEMENTS + \
121 #define CONF_CONTENT_BYTE_POS(c,x,y) (CONF_CONTENT_ELEMENT_POS(c,x,y) * \
122 CONF_ELEMENT_NUM_BYTES)
123 #define CONF_CONTENTS_ELEMENT(b,c,x,y) ((b[CONF_CONTENT_BYTE_POS(c,x,y)]<< 8)|\
124 (b[CONF_CONTENT_BYTE_POS(c,x,y) + 1]))
126 // temporary variables used to store pointers to structure members
127 static struct LevelInfo li;
128 static struct ElementInfo xx_ei, yy_ei;
129 static struct ElementChangeInfo xx_change;
130 static struct ElementGroupInfo xx_group;
131 static struct EnvelopeInfo xx_envelope;
132 static unsigned int xx_event_bits[NUM_CE_BITFIELDS];
133 static char xx_default_description[MAX_ELEMENT_NAME_LEN + 1];
134 static int xx_num_contents;
135 static int xx_current_change_page;
136 static char xx_default_string_empty[1] = "";
137 static int xx_string_length_unused;
139 struct LevelFileConfigInfo
141 int element; // element for which data is to be stored
142 int save_type; // save data always, never or when changed
143 int data_type; // data type (used internally, not stored)
144 int conf_type; // micro chunk identifier (stored in file)
147 void *value; // variable that holds the data to be stored
148 int default_value; // initial default value for this variable
151 void *value_copy; // variable that holds the data to be copied
152 void *num_entities; // number of entities for multi-byte data
153 int default_num_entities; // default number of entities for this data
154 int max_num_entities; // maximal number of entities for this data
155 char *default_string; // optional default string for string data
158 static struct LevelFileConfigInfo chunk_config_INFO[] =
160 // ---------- values not related to single elements -------------------------
163 -1, SAVE_CONF_ALWAYS,
164 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
165 &li.game_engine_type, GAME_ENGINE_TYPE_RND
169 -1, SAVE_CONF_ALWAYS,
170 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
171 &li.fieldx, STD_LEV_FIELDX
174 -1, SAVE_CONF_ALWAYS,
175 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
176 &li.fieldy, STD_LEV_FIELDY
180 -1, SAVE_CONF_ALWAYS,
181 TYPE_INTEGER, CONF_VALUE_16_BIT(3),
186 -1, SAVE_CONF_ALWAYS,
187 TYPE_INTEGER, CONF_VALUE_16_BIT(4),
193 TYPE_INTEGER, CONF_VALUE_32_BIT(2),
199 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
200 &li.use_step_counter, FALSE
205 TYPE_BITFIELD, CONF_VALUE_8_BIT(4),
206 &li.wind_direction_initial, MV_NONE
211 TYPE_BOOLEAN, CONF_VALUE_8_BIT(5),
212 &li.em_slippery_gems, FALSE
217 TYPE_BOOLEAN, CONF_VALUE_8_BIT(6),
218 &li.use_custom_template, FALSE
223 TYPE_BITFIELD, CONF_VALUE_32_BIT(1),
224 &li.can_move_into_acid_bits, ~0 // default: everything can
229 TYPE_BITFIELD, CONF_VALUE_8_BIT(7),
230 &li.dont_collide_with_bits, ~0 // default: always deadly
235 TYPE_BOOLEAN, CONF_VALUE_8_BIT(8),
236 &li.em_explodes_by_fire, FALSE
241 TYPE_INTEGER, CONF_VALUE_16_BIT(5),
242 &li.score[SC_TIME_BONUS], 1
247 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
248 &li.auto_exit_sokoban, FALSE
253 TYPE_BOOLEAN, CONF_VALUE_8_BIT(10),
254 &li.auto_count_gems, FALSE
259 TYPE_BOOLEAN, CONF_VALUE_8_BIT(11),
260 &li.solved_by_one_player, FALSE
265 TYPE_INTEGER, CONF_VALUE_8_BIT(12),
266 &li.time_score_base, 1
271 TYPE_BOOLEAN, CONF_VALUE_8_BIT(13),
272 &li.rate_time_over_score, FALSE
282 static struct LevelFileConfigInfo chunk_config_ELEM[] =
284 // (these values are the same for each player)
287 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
288 &li.block_last_field, FALSE // default case for EM levels
292 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
293 &li.sp_block_last_field, TRUE // default case for SP levels
297 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
298 &li.instant_relocation, FALSE
302 TYPE_BOOLEAN, CONF_VALUE_8_BIT(4),
303 &li.can_pass_to_walkable, FALSE
307 TYPE_BOOLEAN, CONF_VALUE_8_BIT(5),
308 &li.block_snap_field, TRUE
312 TYPE_BOOLEAN, CONF_VALUE_8_BIT(6),
313 &li.continuous_snapping, TRUE
317 TYPE_BOOLEAN, CONF_VALUE_8_BIT(12),
318 &li.shifted_relocation, FALSE
322 TYPE_BOOLEAN, CONF_VALUE_8_BIT(15),
323 &li.lazy_relocation, FALSE
327 TYPE_BOOLEAN, CONF_VALUE_8_BIT(16),
328 &li.finish_dig_collect, TRUE
332 TYPE_BOOLEAN, CONF_VALUE_8_BIT(17),
333 &li.keep_walkable_ce, FALSE
336 // (these values are different for each player)
339 TYPE_INTEGER, CONF_VALUE_8_BIT(7),
340 &li.initial_player_stepsize[0], STEPSIZE_NORMAL
344 TYPE_BOOLEAN, CONF_VALUE_8_BIT(8),
345 &li.initial_player_gravity[0], FALSE
349 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
350 &li.use_start_element[0], FALSE
354 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
355 &li.start_element[0], EL_PLAYER_1
359 TYPE_BOOLEAN, CONF_VALUE_8_BIT(10),
360 &li.use_artwork_element[0], FALSE
364 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
365 &li.artwork_element[0], EL_PLAYER_1
369 TYPE_BOOLEAN, CONF_VALUE_8_BIT(11),
370 &li.use_explosion_element[0], FALSE
374 TYPE_ELEMENT, CONF_VALUE_16_BIT(3),
375 &li.explosion_element[0], EL_PLAYER_1
379 TYPE_BOOLEAN, CONF_VALUE_8_BIT(13),
380 &li.use_initial_inventory[0], FALSE
384 TYPE_BOOLEAN, CONF_VALUE_8_BIT(14),
385 &li.initial_inventory_size[0], 1
389 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(1),
390 &li.initial_inventory_content[0][0],EL_EMPTY, NULL,
391 &li.initial_inventory_size[0], 1, MAX_INITIAL_INVENTORY_SIZE
396 TYPE_INTEGER, CONF_VALUE_8_BIT(7),
397 &li.initial_player_stepsize[1], STEPSIZE_NORMAL
401 TYPE_BOOLEAN, CONF_VALUE_8_BIT(8),
402 &li.initial_player_gravity[1], FALSE
406 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
407 &li.use_start_element[1], FALSE
411 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
412 &li.start_element[1], EL_PLAYER_2
416 TYPE_BOOLEAN, CONF_VALUE_8_BIT(10),
417 &li.use_artwork_element[1], FALSE
421 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
422 &li.artwork_element[1], EL_PLAYER_2
426 TYPE_BOOLEAN, CONF_VALUE_8_BIT(11),
427 &li.use_explosion_element[1], FALSE
431 TYPE_ELEMENT, CONF_VALUE_16_BIT(3),
432 &li.explosion_element[1], EL_PLAYER_2
436 TYPE_BOOLEAN, CONF_VALUE_8_BIT(13),
437 &li.use_initial_inventory[1], FALSE
441 TYPE_BOOLEAN, CONF_VALUE_8_BIT(14),
442 &li.initial_inventory_size[1], 1
446 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(1),
447 &li.initial_inventory_content[1][0],EL_EMPTY, NULL,
448 &li.initial_inventory_size[1], 1, MAX_INITIAL_INVENTORY_SIZE
453 TYPE_INTEGER, CONF_VALUE_8_BIT(7),
454 &li.initial_player_stepsize[2], STEPSIZE_NORMAL
458 TYPE_BOOLEAN, CONF_VALUE_8_BIT(8),
459 &li.initial_player_gravity[2], FALSE
463 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
464 &li.use_start_element[2], FALSE
468 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
469 &li.start_element[2], EL_PLAYER_3
473 TYPE_BOOLEAN, CONF_VALUE_8_BIT(10),
474 &li.use_artwork_element[2], FALSE
478 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
479 &li.artwork_element[2], EL_PLAYER_3
483 TYPE_BOOLEAN, CONF_VALUE_8_BIT(11),
484 &li.use_explosion_element[2], FALSE
488 TYPE_ELEMENT, CONF_VALUE_16_BIT(3),
489 &li.explosion_element[2], EL_PLAYER_3
493 TYPE_BOOLEAN, CONF_VALUE_8_BIT(13),
494 &li.use_initial_inventory[2], FALSE
498 TYPE_BOOLEAN, CONF_VALUE_8_BIT(14),
499 &li.initial_inventory_size[2], 1
503 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(1),
504 &li.initial_inventory_content[2][0],EL_EMPTY, NULL,
505 &li.initial_inventory_size[2], 1, MAX_INITIAL_INVENTORY_SIZE
510 TYPE_INTEGER, CONF_VALUE_8_BIT(7),
511 &li.initial_player_stepsize[3], STEPSIZE_NORMAL
515 TYPE_BOOLEAN, CONF_VALUE_8_BIT(8),
516 &li.initial_player_gravity[3], FALSE
520 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
521 &li.use_start_element[3], FALSE
525 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
526 &li.start_element[3], EL_PLAYER_4
530 TYPE_BOOLEAN, CONF_VALUE_8_BIT(10),
531 &li.use_artwork_element[3], FALSE
535 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
536 &li.artwork_element[3], EL_PLAYER_4
540 TYPE_BOOLEAN, CONF_VALUE_8_BIT(11),
541 &li.use_explosion_element[3], FALSE
545 TYPE_ELEMENT, CONF_VALUE_16_BIT(3),
546 &li.explosion_element[3], EL_PLAYER_4
550 TYPE_BOOLEAN, CONF_VALUE_8_BIT(13),
551 &li.use_initial_inventory[3], FALSE
555 TYPE_BOOLEAN, CONF_VALUE_8_BIT(14),
556 &li.initial_inventory_size[3], 1
560 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(1),
561 &li.initial_inventory_content[3][0],EL_EMPTY, NULL,
562 &li.initial_inventory_size[3], 1, MAX_INITIAL_INVENTORY_SIZE
567 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
568 &li.score[SC_EMERALD], 10
573 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
574 &li.score[SC_DIAMOND], 10
579 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
580 &li.score[SC_BUG], 10
585 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
586 &li.score[SC_SPACESHIP], 10
591 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
592 &li.score[SC_PACMAN], 10
597 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
598 &li.score[SC_NUT], 10
603 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
604 &li.score[SC_DYNAMITE], 10
609 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
610 &li.score[SC_KEY], 10
615 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
616 &li.score[SC_PEARL], 10
621 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
622 &li.score[SC_CRYSTAL], 10
627 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
628 &li.amoeba_content, EL_DIAMOND
632 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
637 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
638 &li.grow_into_diggable, TRUE
643 TYPE_CONTENT_LIST, CONF_VALUE_BYTES(1),
644 &li.yamyam_content, EL_ROCK, NULL,
645 &li.num_yamyam_contents, 4, MAX_ELEMENT_CONTENTS
649 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
650 &li.score[SC_YAMYAM], 10
655 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
656 &li.score[SC_ROBOT], 10
660 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
666 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
672 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
673 &li.time_magic_wall, 10
678 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
679 &li.game_of_life[0], 2
683 TYPE_INTEGER, CONF_VALUE_8_BIT(2),
684 &li.game_of_life[1], 3
688 TYPE_INTEGER, CONF_VALUE_8_BIT(3),
689 &li.game_of_life[2], 3
693 TYPE_INTEGER, CONF_VALUE_8_BIT(4),
694 &li.game_of_life[3], 3
698 TYPE_BOOLEAN, CONF_VALUE_8_BIT(5),
699 &li.use_life_bugs, FALSE
704 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
709 TYPE_INTEGER, CONF_VALUE_8_BIT(2),
714 TYPE_INTEGER, CONF_VALUE_8_BIT(3),
719 TYPE_INTEGER, CONF_VALUE_8_BIT(4),
724 EL_TIMEGATE_SWITCH, -1,
725 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
726 &li.time_timegate, 10
730 EL_LIGHT_SWITCH_ACTIVE, -1,
731 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
736 EL_SHIELD_NORMAL, -1,
737 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
738 &li.shield_normal_time, 10
741 EL_SHIELD_NORMAL, -1,
742 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
743 &li.score[SC_SHIELD], 10
747 EL_SHIELD_DEADLY, -1,
748 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
749 &li.shield_deadly_time, 10
752 EL_SHIELD_DEADLY, -1,
753 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
754 &li.score[SC_SHIELD], 10
759 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
764 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
765 &li.extra_time_score, 10
769 EL_TIME_ORB_FULL, -1,
770 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
771 &li.time_orb_time, 10
774 EL_TIME_ORB_FULL, -1,
775 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
776 &li.use_time_orb_bug, FALSE
781 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
782 &li.use_spring_bug, FALSE
787 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
788 &li.android_move_time, 10
792 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
793 &li.android_clone_time, 10
796 EL_EMC_ANDROID, SAVE_CONF_NEVER,
797 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(1),
798 &li.android_clone_element[0], EL_EMPTY, NULL,
799 &li.num_android_clone_elements, 1, MAX_ANDROID_ELEMENTS_OLD
803 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(2),
804 &li.android_clone_element[0], EL_EMPTY, NULL,
805 &li.num_android_clone_elements, 1, MAX_ANDROID_ELEMENTS
810 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
815 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
820 EL_EMC_MAGNIFIER, -1,
821 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
822 &li.magnify_score, 10
825 EL_EMC_MAGNIFIER, -1,
826 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
831 EL_EMC_MAGIC_BALL, -1,
832 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
836 EL_EMC_MAGIC_BALL, -1,
837 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
838 &li.ball_random, FALSE
841 EL_EMC_MAGIC_BALL, -1,
842 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
843 &li.ball_active_initial, FALSE
846 EL_EMC_MAGIC_BALL, -1,
847 TYPE_CONTENT_LIST, CONF_VALUE_BYTES(1),
848 &li.ball_content, EL_EMPTY, NULL,
849 &li.num_ball_contents, 4, MAX_ELEMENT_CONTENTS
853 EL_SOKOBAN_FIELD_EMPTY, -1,
854 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
855 &li.sb_fields_needed, TRUE
859 EL_SOKOBAN_OBJECT, -1,
860 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
861 &li.sb_objects_needed, TRUE
866 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
867 &li.mm_laser_red, FALSE
871 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
872 &li.mm_laser_green, FALSE
876 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
877 &li.mm_laser_blue, TRUE
882 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
883 &li.df_laser_red, TRUE
887 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
888 &li.df_laser_green, TRUE
892 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
893 &li.df_laser_blue, FALSE
897 EL_MM_FUSE_ACTIVE, -1,
898 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
903 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
908 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
912 EL_MM_STEEL_BLOCK, -1,
913 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
914 &li.mm_time_block, 75
918 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
919 &li.score[SC_ELEM_BONUS], 10
922 // ---------- unused values -------------------------------------------------
925 EL_UNKNOWN, SAVE_CONF_NEVER,
926 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
927 &li.score[SC_UNKNOWN_15], 10
937 static struct LevelFileConfigInfo chunk_config_NOTE[] =
941 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
942 &xx_envelope.xsize, MAX_ENVELOPE_XSIZE,
946 TYPE_INTEGER, CONF_VALUE_8_BIT(2),
947 &xx_envelope.ysize, MAX_ENVELOPE_YSIZE,
952 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
953 &xx_envelope.autowrap, FALSE
957 TYPE_BOOLEAN, CONF_VALUE_8_BIT(4),
958 &xx_envelope.centered, FALSE
963 TYPE_STRING, CONF_VALUE_BYTES(1),
964 &xx_envelope.text, -1, NULL,
965 &xx_string_length_unused, -1, MAX_ENVELOPE_TEXT_LEN,
966 &xx_default_string_empty[0]
976 static struct LevelFileConfigInfo chunk_config_CUSX_base[] =
980 TYPE_STRING, CONF_VALUE_BYTES(1),
981 &xx_ei.description[0], -1,
982 &yy_ei.description[0],
983 &xx_string_length_unused, -1, MAX_ELEMENT_NAME_LEN,
984 &xx_default_description[0]
989 TYPE_BITFIELD, CONF_VALUE_32_BIT(1),
990 &xx_ei.properties[EP_BITFIELD_BASE_NR], EP_BITMASK_BASE_DEFAULT,
991 &yy_ei.properties[EP_BITFIELD_BASE_NR]
993 #if ENABLE_RESERVED_CODE
994 // (reserved for later use)
997 TYPE_BITFIELD, CONF_VALUE_32_BIT(2),
998 &xx_ei.properties[EP_BITFIELD_BASE_NR + 1], EP_BITMASK_DEFAULT,
999 &yy_ei.properties[EP_BITFIELD_BASE_NR + 1]
1005 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1006 &xx_ei.use_gfx_element, FALSE,
1007 &yy_ei.use_gfx_element
1011 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
1012 &xx_ei.gfx_element_initial, EL_EMPTY_SPACE,
1013 &yy_ei.gfx_element_initial
1018 TYPE_BITFIELD, CONF_VALUE_8_BIT(2),
1019 &xx_ei.access_direction, MV_ALL_DIRECTIONS,
1020 &yy_ei.access_direction
1025 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
1026 &xx_ei.collect_score_initial, 10,
1027 &yy_ei.collect_score_initial
1031 TYPE_INTEGER, CONF_VALUE_16_BIT(3),
1032 &xx_ei.collect_count_initial, 1,
1033 &yy_ei.collect_count_initial
1038 TYPE_INTEGER, CONF_VALUE_16_BIT(4),
1039 &xx_ei.ce_value_fixed_initial, 0,
1040 &yy_ei.ce_value_fixed_initial
1044 TYPE_INTEGER, CONF_VALUE_16_BIT(5),
1045 &xx_ei.ce_value_random_initial, 0,
1046 &yy_ei.ce_value_random_initial
1050 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
1051 &xx_ei.use_last_ce_value, FALSE,
1052 &yy_ei.use_last_ce_value
1057 TYPE_INTEGER, CONF_VALUE_16_BIT(6),
1058 &xx_ei.push_delay_fixed, 8,
1059 &yy_ei.push_delay_fixed
1063 TYPE_INTEGER, CONF_VALUE_16_BIT(7),
1064 &xx_ei.push_delay_random, 8,
1065 &yy_ei.push_delay_random
1069 TYPE_INTEGER, CONF_VALUE_16_BIT(8),
1070 &xx_ei.drop_delay_fixed, 0,
1071 &yy_ei.drop_delay_fixed
1075 TYPE_INTEGER, CONF_VALUE_16_BIT(9),
1076 &xx_ei.drop_delay_random, 0,
1077 &yy_ei.drop_delay_random
1081 TYPE_INTEGER, CONF_VALUE_16_BIT(10),
1082 &xx_ei.move_delay_fixed, 0,
1083 &yy_ei.move_delay_fixed
1087 TYPE_INTEGER, CONF_VALUE_16_BIT(11),
1088 &xx_ei.move_delay_random, 0,
1089 &yy_ei.move_delay_random
1093 TYPE_INTEGER, CONF_VALUE_16_BIT(16),
1094 &xx_ei.step_delay_fixed, 0,
1095 &yy_ei.step_delay_fixed
1099 TYPE_INTEGER, CONF_VALUE_16_BIT(17),
1100 &xx_ei.step_delay_random, 0,
1101 &yy_ei.step_delay_random
1106 TYPE_BITFIELD, CONF_VALUE_32_BIT(3),
1107 &xx_ei.move_pattern, MV_ALL_DIRECTIONS,
1112 TYPE_BITFIELD, CONF_VALUE_8_BIT(4),
1113 &xx_ei.move_direction_initial, MV_START_AUTOMATIC,
1114 &yy_ei.move_direction_initial
1118 TYPE_INTEGER, CONF_VALUE_8_BIT(5),
1119 &xx_ei.move_stepsize, TILEX / 8,
1120 &yy_ei.move_stepsize
1125 TYPE_ELEMENT, CONF_VALUE_16_BIT(12),
1126 &xx_ei.move_enter_element, EL_EMPTY_SPACE,
1127 &yy_ei.move_enter_element
1131 TYPE_ELEMENT, CONF_VALUE_16_BIT(13),
1132 &xx_ei.move_leave_element, EL_EMPTY_SPACE,
1133 &yy_ei.move_leave_element
1137 TYPE_INTEGER, CONF_VALUE_8_BIT(6),
1138 &xx_ei.move_leave_type, LEAVE_TYPE_UNLIMITED,
1139 &yy_ei.move_leave_type
1144 TYPE_INTEGER, CONF_VALUE_8_BIT(7),
1145 &xx_ei.slippery_type, SLIPPERY_ANY_RANDOM,
1146 &yy_ei.slippery_type
1151 TYPE_INTEGER, CONF_VALUE_8_BIT(8),
1152 &xx_ei.explosion_type, EXPLODES_3X3,
1153 &yy_ei.explosion_type
1157 TYPE_INTEGER, CONF_VALUE_16_BIT(14),
1158 &xx_ei.explosion_delay, 16,
1159 &yy_ei.explosion_delay
1163 TYPE_INTEGER, CONF_VALUE_16_BIT(15),
1164 &xx_ei.ignition_delay, 8,
1165 &yy_ei.ignition_delay
1170 TYPE_CONTENT_LIST, CONF_VALUE_BYTES(2),
1171 &xx_ei.content, EL_EMPTY_SPACE,
1173 &xx_num_contents, 1, 1
1176 // ---------- "num_change_pages" must be the last entry ---------------------
1179 -1, SAVE_CONF_ALWAYS,
1180 TYPE_INTEGER, CONF_VALUE_8_BIT(9),
1181 &xx_ei.num_change_pages, 1,
1182 &yy_ei.num_change_pages
1193 static struct LevelFileConfigInfo chunk_config_CUSX_change[] =
1195 // ---------- "current_change_page" must be the first entry -----------------
1198 -1, SAVE_CONF_ALWAYS,
1199 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
1200 &xx_current_change_page, -1
1203 // ---------- (the remaining entries can be in any order) -------------------
1207 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
1208 &xx_change.can_change, FALSE
1213 TYPE_BITFIELD, CONF_VALUE_32_BIT(1),
1214 &xx_event_bits[0], 0
1218 TYPE_BITFIELD, CONF_VALUE_32_BIT(2),
1219 &xx_event_bits[1], 0
1224 TYPE_BITFIELD, CONF_VALUE_8_BIT(3),
1225 &xx_change.trigger_player, CH_PLAYER_ANY
1229 TYPE_BITFIELD, CONF_VALUE_8_BIT(4),
1230 &xx_change.trigger_side, CH_SIDE_ANY
1234 TYPE_BITFIELD, CONF_VALUE_32_BIT(3),
1235 &xx_change.trigger_page, CH_PAGE_ANY
1240 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
1241 &xx_change.target_element, EL_EMPTY_SPACE
1246 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
1247 &xx_change.delay_fixed, 0
1251 TYPE_INTEGER, CONF_VALUE_16_BIT(3),
1252 &xx_change.delay_random, 0
1256 TYPE_INTEGER, CONF_VALUE_16_BIT(4),
1257 &xx_change.delay_frames, FRAMES_PER_SECOND
1262 TYPE_ELEMENT, CONF_VALUE_16_BIT(5),
1263 &xx_change.initial_trigger_element, EL_EMPTY_SPACE
1268 TYPE_BOOLEAN, CONF_VALUE_8_BIT(6),
1269 &xx_change.explode, FALSE
1273 TYPE_BOOLEAN, CONF_VALUE_8_BIT(7),
1274 &xx_change.use_target_content, FALSE
1278 TYPE_BOOLEAN, CONF_VALUE_8_BIT(8),
1279 &xx_change.only_if_complete, FALSE
1283 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
1284 &xx_change.use_random_replace, FALSE
1288 TYPE_INTEGER, CONF_VALUE_8_BIT(10),
1289 &xx_change.random_percentage, 100
1293 TYPE_INTEGER, CONF_VALUE_8_BIT(11),
1294 &xx_change.replace_when, CP_WHEN_EMPTY
1299 TYPE_BOOLEAN, CONF_VALUE_8_BIT(12),
1300 &xx_change.has_action, FALSE
1304 TYPE_INTEGER, CONF_VALUE_8_BIT(13),
1305 &xx_change.action_type, CA_NO_ACTION
1309 TYPE_INTEGER, CONF_VALUE_8_BIT(14),
1310 &xx_change.action_mode, CA_MODE_UNDEFINED
1314 TYPE_INTEGER, CONF_VALUE_16_BIT(6),
1315 &xx_change.action_arg, CA_ARG_UNDEFINED
1320 TYPE_ELEMENT, CONF_VALUE_16_BIT(7),
1321 &xx_change.action_element, EL_EMPTY_SPACE
1326 TYPE_CONTENT_LIST, CONF_VALUE_BYTES(1),
1327 &xx_change.target_content, EL_EMPTY_SPACE, NULL,
1328 &xx_num_contents, 1, 1
1338 static struct LevelFileConfigInfo chunk_config_GRPX[] =
1342 TYPE_STRING, CONF_VALUE_BYTES(1),
1343 &xx_ei.description[0], -1, NULL,
1344 &xx_string_length_unused, -1, MAX_ELEMENT_NAME_LEN,
1345 &xx_default_description[0]
1350 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1351 &xx_ei.use_gfx_element, FALSE
1355 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
1356 &xx_ei.gfx_element_initial, EL_EMPTY_SPACE
1361 TYPE_INTEGER, CONF_VALUE_8_BIT(2),
1362 &xx_group.choice_mode, ANIM_RANDOM
1367 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(2),
1368 &xx_group.element[0], EL_EMPTY_SPACE, NULL,
1369 &xx_group.num_elements, 1, MAX_ELEMENTS_IN_GROUP
1379 static struct LevelFileConfigInfo chunk_config_CONF[] = // (OBSOLETE)
1383 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
1384 &li.block_snap_field, TRUE
1388 TYPE_BOOLEAN, CONF_VALUE_8_BIT(13),
1389 &li.continuous_snapping, TRUE
1393 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
1394 &li.initial_player_stepsize[0], STEPSIZE_NORMAL
1398 TYPE_BOOLEAN, CONF_VALUE_8_BIT(10),
1399 &li.use_start_element[0], FALSE
1403 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
1404 &li.start_element[0], EL_PLAYER_1
1408 TYPE_BOOLEAN, CONF_VALUE_8_BIT(11),
1409 &li.use_artwork_element[0], FALSE
1413 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
1414 &li.artwork_element[0], EL_PLAYER_1
1418 TYPE_BOOLEAN, CONF_VALUE_8_BIT(12),
1419 &li.use_explosion_element[0], FALSE
1423 TYPE_ELEMENT, CONF_VALUE_16_BIT(3),
1424 &li.explosion_element[0], EL_PLAYER_1
1439 filetype_id_list[] =
1441 { LEVEL_FILE_TYPE_RND, "RND" },
1442 { LEVEL_FILE_TYPE_BD, "BD" },
1443 { LEVEL_FILE_TYPE_EM, "EM" },
1444 { LEVEL_FILE_TYPE_SP, "SP" },
1445 { LEVEL_FILE_TYPE_DX, "DX" },
1446 { LEVEL_FILE_TYPE_SB, "SB" },
1447 { LEVEL_FILE_TYPE_DC, "DC" },
1448 { LEVEL_FILE_TYPE_MM, "MM" },
1449 { LEVEL_FILE_TYPE_MM, "DF" },
1454 // ============================================================================
1455 // level file functions
1456 // ============================================================================
1458 static boolean check_special_flags(char *flag)
1460 if (strEqual(options.special_flags, flag) ||
1461 strEqual(leveldir_current->special_flags, flag))
1467 static struct DateInfo getCurrentDate(void)
1469 time_t epoch_seconds = time(NULL);
1470 struct tm *now = localtime(&epoch_seconds);
1471 struct DateInfo date;
1473 date.year = now->tm_year + 1900;
1474 date.month = now->tm_mon + 1;
1475 date.day = now->tm_mday;
1477 date.src = DATE_SRC_CLOCK;
1482 static void resetEventFlags(struct ElementChangeInfo *change)
1486 for (i = 0; i < NUM_CHANGE_EVENTS; i++)
1487 change->has_event[i] = FALSE;
1490 static void resetEventBits(void)
1494 for (i = 0; i < NUM_CE_BITFIELDS; i++)
1495 xx_event_bits[i] = 0;
1498 static void setEventFlagsFromEventBits(struct ElementChangeInfo *change)
1502 /* important: only change event flag if corresponding event bit is set
1503 (this is because all xx_event_bits[] values are loaded separately,
1504 and all xx_event_bits[] values are set back to zero before loading
1505 another value xx_event_bits[x] (each value representing 32 flags)) */
1507 for (i = 0; i < NUM_CHANGE_EVENTS; i++)
1508 if (xx_event_bits[CH_EVENT_BITFIELD_NR(i)] & CH_EVENT_BIT(i))
1509 change->has_event[i] = TRUE;
1512 static void setEventBitsFromEventFlags(struct ElementChangeInfo *change)
1516 /* in contrast to the above function setEventFlagsFromEventBits(), it
1517 would also be possible to set all bits in xx_event_bits[] to 0 or 1
1518 depending on the corresponding change->has_event[i] values here, as
1519 all xx_event_bits[] values are reset in resetEventBits() before */
1521 for (i = 0; i < NUM_CHANGE_EVENTS; i++)
1522 if (change->has_event[i])
1523 xx_event_bits[CH_EVENT_BITFIELD_NR(i)] |= CH_EVENT_BIT(i);
1526 static char *getDefaultElementDescription(struct ElementInfo *ei)
1528 static char description[MAX_ELEMENT_NAME_LEN + 1];
1529 char *default_description = (ei->custom_description != NULL ?
1530 ei->custom_description :
1531 ei->editor_description);
1534 // always start with reliable default values
1535 for (i = 0; i < MAX_ELEMENT_NAME_LEN + 1; i++)
1536 description[i] = '\0';
1538 // truncate element description to MAX_ELEMENT_NAME_LEN bytes
1539 strncpy(description, default_description, MAX_ELEMENT_NAME_LEN);
1541 return &description[0];
1544 static void setElementDescriptionToDefault(struct ElementInfo *ei)
1546 char *default_description = getDefaultElementDescription(ei);
1549 for (i = 0; i < MAX_ELEMENT_NAME_LEN + 1; i++)
1550 ei->description[i] = default_description[i];
1553 static void setConfigToDefaultsFromConfigList(struct LevelFileConfigInfo *conf)
1557 for (i = 0; conf[i].data_type != -1; i++)
1559 int default_value = conf[i].default_value;
1560 int data_type = conf[i].data_type;
1561 int conf_type = conf[i].conf_type;
1562 int byte_mask = conf_type & CONF_MASK_BYTES;
1564 if (byte_mask == CONF_MASK_MULTI_BYTES)
1566 int default_num_entities = conf[i].default_num_entities;
1567 int max_num_entities = conf[i].max_num_entities;
1569 *(int *)(conf[i].num_entities) = default_num_entities;
1571 if (data_type == TYPE_STRING)
1573 char *default_string = conf[i].default_string;
1574 char *string = (char *)(conf[i].value);
1576 strncpy(string, default_string, max_num_entities);
1578 else if (data_type == TYPE_ELEMENT_LIST)
1580 int *element_array = (int *)(conf[i].value);
1583 for (j = 0; j < max_num_entities; j++)
1584 element_array[j] = default_value;
1586 else if (data_type == TYPE_CONTENT_LIST)
1588 struct Content *content = (struct Content *)(conf[i].value);
1591 for (c = 0; c < max_num_entities; c++)
1592 for (y = 0; y < 3; y++)
1593 for (x = 0; x < 3; x++)
1594 content[c].e[x][y] = default_value;
1597 else // constant size configuration data (1, 2 or 4 bytes)
1599 if (data_type == TYPE_BOOLEAN)
1600 *(boolean *)(conf[i].value) = default_value;
1602 *(int *) (conf[i].value) = default_value;
1607 static void copyConfigFromConfigList(struct LevelFileConfigInfo *conf)
1611 for (i = 0; conf[i].data_type != -1; i++)
1613 int data_type = conf[i].data_type;
1614 int conf_type = conf[i].conf_type;
1615 int byte_mask = conf_type & CONF_MASK_BYTES;
1617 if (byte_mask == CONF_MASK_MULTI_BYTES)
1619 int max_num_entities = conf[i].max_num_entities;
1621 if (data_type == TYPE_STRING)
1623 char *string = (char *)(conf[i].value);
1624 char *string_copy = (char *)(conf[i].value_copy);
1626 strncpy(string_copy, string, max_num_entities);
1628 else if (data_type == TYPE_ELEMENT_LIST)
1630 int *element_array = (int *)(conf[i].value);
1631 int *element_array_copy = (int *)(conf[i].value_copy);
1634 for (j = 0; j < max_num_entities; j++)
1635 element_array_copy[j] = element_array[j];
1637 else if (data_type == TYPE_CONTENT_LIST)
1639 struct Content *content = (struct Content *)(conf[i].value);
1640 struct Content *content_copy = (struct Content *)(conf[i].value_copy);
1643 for (c = 0; c < max_num_entities; c++)
1644 for (y = 0; y < 3; y++)
1645 for (x = 0; x < 3; x++)
1646 content_copy[c].e[x][y] = content[c].e[x][y];
1649 else // constant size configuration data (1, 2 or 4 bytes)
1651 if (data_type == TYPE_BOOLEAN)
1652 *(boolean *)(conf[i].value_copy) = *(boolean *)(conf[i].value);
1654 *(int *) (conf[i].value_copy) = *(int *) (conf[i].value);
1659 void copyElementInfo(struct ElementInfo *ei_from, struct ElementInfo *ei_to)
1663 xx_ei = *ei_from; // copy element data into temporary buffer
1664 yy_ei = *ei_to; // copy element data into temporary buffer
1666 copyConfigFromConfigList(chunk_config_CUSX_base);
1671 // ---------- reinitialize and copy change pages ----------
1673 ei_to->num_change_pages = ei_from->num_change_pages;
1674 ei_to->current_change_page = ei_from->current_change_page;
1676 setElementChangePages(ei_to, ei_to->num_change_pages);
1678 for (i = 0; i < ei_to->num_change_pages; i++)
1679 ei_to->change_page[i] = ei_from->change_page[i];
1681 // ---------- copy group element info ----------
1682 if (ei_from->group != NULL && ei_to->group != NULL) // group or internal
1683 *ei_to->group = *ei_from->group;
1685 // mark this custom element as modified
1686 ei_to->modified_settings = TRUE;
1689 void setElementChangePages(struct ElementInfo *ei, int change_pages)
1691 int change_page_size = sizeof(struct ElementChangeInfo);
1693 ei->num_change_pages = MAX(1, change_pages);
1696 checked_realloc(ei->change_page, ei->num_change_pages * change_page_size);
1698 if (ei->current_change_page >= ei->num_change_pages)
1699 ei->current_change_page = ei->num_change_pages - 1;
1701 ei->change = &ei->change_page[ei->current_change_page];
1704 void setElementChangeInfoToDefaults(struct ElementChangeInfo *change)
1706 xx_change = *change; // copy change data into temporary buffer
1708 setConfigToDefaultsFromConfigList(chunk_config_CUSX_change);
1710 *change = xx_change;
1712 resetEventFlags(change);
1714 change->direct_action = 0;
1715 change->other_action = 0;
1717 change->pre_change_function = NULL;
1718 change->change_function = NULL;
1719 change->post_change_function = NULL;
1722 static void setLevelInfoToDefaults_Level(struct LevelInfo *level)
1726 li = *level; // copy level data into temporary buffer
1727 setConfigToDefaultsFromConfigList(chunk_config_INFO);
1728 *level = li; // copy temporary buffer back to level data
1730 setLevelInfoToDefaults_EM();
1731 setLevelInfoToDefaults_SP();
1732 setLevelInfoToDefaults_MM();
1734 level->native_em_level = &native_em_level;
1735 level->native_sp_level = &native_sp_level;
1736 level->native_mm_level = &native_mm_level;
1738 level->file_version = FILE_VERSION_ACTUAL;
1739 level->game_version = GAME_VERSION_ACTUAL;
1741 level->creation_date = getCurrentDate();
1743 level->encoding_16bit_field = TRUE;
1744 level->encoding_16bit_yamyam = TRUE;
1745 level->encoding_16bit_amoeba = TRUE;
1747 // clear level name and level author string buffers
1748 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
1749 level->name[i] = '\0';
1750 for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
1751 level->author[i] = '\0';
1753 // set level name and level author to default values
1754 strcpy(level->name, NAMELESS_LEVEL_NAME);
1755 strcpy(level->author, ANONYMOUS_NAME);
1757 // set level playfield to playable default level with player and exit
1758 for (x = 0; x < MAX_LEV_FIELDX; x++)
1759 for (y = 0; y < MAX_LEV_FIELDY; y++)
1760 level->field[x][y] = EL_SAND;
1762 level->field[0][0] = EL_PLAYER_1;
1763 level->field[STD_LEV_FIELDX - 1][STD_LEV_FIELDY - 1] = EL_EXIT_CLOSED;
1765 BorderElement = EL_STEELWALL;
1767 // detect custom elements when loading them
1768 level->file_has_custom_elements = FALSE;
1770 // set all bug compatibility flags to "false" => do not emulate this bug
1771 level->use_action_after_change_bug = FALSE;
1773 if (leveldir_current)
1775 // try to determine better author name than 'anonymous'
1776 if (!strEqual(leveldir_current->author, ANONYMOUS_NAME))
1778 strncpy(level->author, leveldir_current->author, MAX_LEVEL_AUTHOR_LEN);
1779 level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
1783 switch (LEVELCLASS(leveldir_current))
1785 case LEVELCLASS_TUTORIAL:
1786 strcpy(level->author, PROGRAM_AUTHOR_STRING);
1789 case LEVELCLASS_CONTRIB:
1790 strncpy(level->author, leveldir_current->name, MAX_LEVEL_AUTHOR_LEN);
1791 level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
1794 case LEVELCLASS_PRIVATE:
1795 strncpy(level->author, getRealName(), MAX_LEVEL_AUTHOR_LEN);
1796 level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
1800 // keep default value
1807 static void setLevelInfoToDefaults_Elements(struct LevelInfo *level)
1809 static boolean clipboard_elements_initialized = FALSE;
1812 InitElementPropertiesStatic();
1814 li = *level; // copy level data into temporary buffer
1815 setConfigToDefaultsFromConfigList(chunk_config_ELEM);
1816 *level = li; // copy temporary buffer back to level data
1818 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1821 struct ElementInfo *ei = &element_info[element];
1823 // never initialize clipboard elements after the very first time
1824 // (to be able to use clipboard elements between several levels)
1825 if (IS_CLIPBOARD_ELEMENT(element) && clipboard_elements_initialized)
1828 if (IS_ENVELOPE(element))
1830 int envelope_nr = element - EL_ENVELOPE_1;
1832 setConfigToDefaultsFromConfigList(chunk_config_NOTE);
1834 level->envelope[envelope_nr] = xx_envelope;
1837 if (IS_CUSTOM_ELEMENT(element) ||
1838 IS_GROUP_ELEMENT(element) ||
1839 IS_INTERNAL_ELEMENT(element))
1841 xx_ei = *ei; // copy element data into temporary buffer
1843 setConfigToDefaultsFromConfigList(chunk_config_CUSX_base);
1848 setElementChangePages(ei, 1);
1849 setElementChangeInfoToDefaults(ei->change);
1851 if (IS_CUSTOM_ELEMENT(element) ||
1852 IS_GROUP_ELEMENT(element) ||
1853 IS_INTERNAL_ELEMENT(element))
1855 setElementDescriptionToDefault(ei);
1857 ei->modified_settings = FALSE;
1860 if (IS_CUSTOM_ELEMENT(element) ||
1861 IS_INTERNAL_ELEMENT(element))
1863 // internal values used in level editor
1865 ei->access_type = 0;
1866 ei->access_layer = 0;
1867 ei->access_protected = 0;
1868 ei->walk_to_action = 0;
1869 ei->smash_targets = 0;
1872 ei->can_explode_by_fire = FALSE;
1873 ei->can_explode_smashed = FALSE;
1874 ei->can_explode_impact = FALSE;
1876 ei->current_change_page = 0;
1879 if (IS_GROUP_ELEMENT(element) ||
1880 IS_INTERNAL_ELEMENT(element))
1882 struct ElementGroupInfo *group;
1884 // initialize memory for list of elements in group
1885 if (ei->group == NULL)
1886 ei->group = checked_malloc(sizeof(struct ElementGroupInfo));
1890 xx_group = *group; // copy group data into temporary buffer
1892 setConfigToDefaultsFromConfigList(chunk_config_GRPX);
1898 clipboard_elements_initialized = TRUE;
1901 static void setLevelInfoToDefaults(struct LevelInfo *level,
1902 boolean level_info_only,
1903 boolean reset_file_status)
1905 setLevelInfoToDefaults_Level(level);
1907 if (!level_info_only)
1908 setLevelInfoToDefaults_Elements(level);
1910 if (reset_file_status)
1912 level->no_valid_file = FALSE;
1913 level->no_level_file = FALSE;
1916 level->changed = FALSE;
1919 static void setFileInfoToDefaults(struct LevelFileInfo *level_file_info)
1921 level_file_info->nr = 0;
1922 level_file_info->type = LEVEL_FILE_TYPE_UNKNOWN;
1923 level_file_info->packed = FALSE;
1925 setString(&level_file_info->basename, NULL);
1926 setString(&level_file_info->filename, NULL);
1929 int getMappedElement_SB(int, boolean);
1931 static void ActivateLevelTemplate(void)
1935 if (check_special_flags("load_xsb_to_ces"))
1937 // fill smaller playfields with padding "beyond border wall" elements
1938 if (level.fieldx < level_template.fieldx ||
1939 level.fieldy < level_template.fieldy)
1941 short field[level.fieldx][level.fieldy];
1942 int new_fieldx = MAX(level.fieldx, level_template.fieldx);
1943 int new_fieldy = MAX(level.fieldy, level_template.fieldy);
1944 int pos_fieldx = (new_fieldx - level.fieldx) / 2;
1945 int pos_fieldy = (new_fieldy - level.fieldy) / 2;
1947 // copy old playfield (which is smaller than the visible area)
1948 for (y = 0; y < level.fieldy; y++) for (x = 0; x < level.fieldx; x++)
1949 field[x][y] = level.field[x][y];
1951 // fill new, larger playfield with "beyond border wall" elements
1952 for (y = 0; y < new_fieldy; y++) for (x = 0; x < new_fieldx; x++)
1953 level.field[x][y] = getMappedElement_SB('_', TRUE);
1955 // copy the old playfield to the middle of the new playfield
1956 for (y = 0; y < level.fieldy; y++) for (x = 0; x < level.fieldx; x++)
1957 level.field[pos_fieldx + x][pos_fieldy + y] = field[x][y];
1959 level.fieldx = new_fieldx;
1960 level.fieldy = new_fieldy;
1964 // Currently there is no special action needed to activate the template
1965 // data, because 'element_info' property settings overwrite the original
1966 // level data, while all other variables do not change.
1968 // Exception: 'from_level_template' elements in the original level playfield
1969 // are overwritten with the corresponding elements at the same position in
1970 // playfield from the level template.
1972 for (x = 0; x < level.fieldx; x++)
1973 for (y = 0; y < level.fieldy; y++)
1974 if (level.field[x][y] == EL_FROM_LEVEL_TEMPLATE)
1975 level.field[x][y] = level_template.field[x][y];
1977 if (check_special_flags("load_xsb_to_ces"))
1979 struct LevelInfo level_backup = level;
1981 // overwrite all individual level settings from template level settings
1982 level = level_template;
1984 // restore level file info
1985 level.file_info = level_backup.file_info;
1987 // restore playfield size
1988 level.fieldx = level_backup.fieldx;
1989 level.fieldy = level_backup.fieldy;
1991 // restore playfield content
1992 for (x = 0; x < level.fieldx; x++)
1993 for (y = 0; y < level.fieldy; y++)
1994 level.field[x][y] = level_backup.field[x][y];
1996 // restore name and author from individual level
1997 strcpy(level.name, level_backup.name);
1998 strcpy(level.author, level_backup.author);
2000 // restore flag "use_custom_template"
2001 level.use_custom_template = level_backup.use_custom_template;
2005 static char *getLevelFilenameFromBasename(char *basename)
2007 static char *filename = NULL;
2009 checked_free(filename);
2011 filename = getPath2(getCurrentLevelDir(), basename);
2016 static int getFileTypeFromBasename(char *basename)
2018 // !!! ALSO SEE COMMENT IN checkForPackageFromBasename() !!!
2020 static char *filename = NULL;
2021 struct stat file_status;
2023 // ---------- try to determine file type from filename ----------
2025 // check for typical filename of a Supaplex level package file
2026 if (strlen(basename) == 10 && strPrefixLower(basename, "levels.d"))
2027 return LEVEL_FILE_TYPE_SP;
2029 // check for typical filename of a Diamond Caves II level package file
2030 if (strSuffixLower(basename, ".dc") ||
2031 strSuffixLower(basename, ".dc2"))
2032 return LEVEL_FILE_TYPE_DC;
2034 // check for typical filename of a Sokoban level package file
2035 if (strSuffixLower(basename, ".xsb") &&
2036 strchr(basename, '%') == NULL)
2037 return LEVEL_FILE_TYPE_SB;
2039 // ---------- try to determine file type from filesize ----------
2041 checked_free(filename);
2042 filename = getPath2(getCurrentLevelDir(), basename);
2044 if (stat(filename, &file_status) == 0)
2046 // check for typical filesize of a Supaplex level package file
2047 if (file_status.st_size == 170496)
2048 return LEVEL_FILE_TYPE_SP;
2051 return LEVEL_FILE_TYPE_UNKNOWN;
2054 static int getFileTypeFromMagicBytes(char *filename, int type)
2058 if ((file = openFile(filename, MODE_READ)))
2060 char chunk_name[CHUNK_ID_LEN + 1];
2062 getFileChunkBE(file, chunk_name, NULL);
2064 if (strEqual(chunk_name, "MMII") ||
2065 strEqual(chunk_name, "MIRR"))
2066 type = LEVEL_FILE_TYPE_MM;
2074 static boolean checkForPackageFromBasename(char *basename)
2076 // !!! WON'T WORK ANYMORE IF getFileTypeFromBasename() ALSO DETECTS !!!
2077 // !!! SINGLE LEVELS (CURRENTLY ONLY DETECTS LEVEL PACKAGES !!!
2079 return (getFileTypeFromBasename(basename) != LEVEL_FILE_TYPE_UNKNOWN);
2082 static char *getSingleLevelBasenameExt(int nr, char *extension)
2084 static char basename[MAX_FILENAME_LEN];
2087 sprintf(basename, "%s", LEVELTEMPLATE_FILENAME);
2089 sprintf(basename, "%03d.%s", nr, extension);
2094 static char *getSingleLevelBasename(int nr)
2096 return getSingleLevelBasenameExt(nr, LEVELFILE_EXTENSION);
2099 static char *getPackedLevelBasename(int type)
2101 static char basename[MAX_FILENAME_LEN];
2102 char *directory = getCurrentLevelDir();
2104 DirectoryEntry *dir_entry;
2106 strcpy(basename, UNDEFINED_FILENAME); // default: undefined file
2108 if ((dir = openDirectory(directory)) == NULL)
2110 Warn("cannot read current level directory '%s'", directory);
2115 while ((dir_entry = readDirectory(dir)) != NULL) // loop all entries
2117 char *entry_basename = dir_entry->basename;
2118 int entry_type = getFileTypeFromBasename(entry_basename);
2120 if (entry_type != LEVEL_FILE_TYPE_UNKNOWN) // found valid level package
2122 if (type == LEVEL_FILE_TYPE_UNKNOWN ||
2125 strcpy(basename, entry_basename);
2132 closeDirectory(dir);
2137 static char *getSingleLevelFilename(int nr)
2139 return getLevelFilenameFromBasename(getSingleLevelBasename(nr));
2142 #if ENABLE_UNUSED_CODE
2143 static char *getPackedLevelFilename(int type)
2145 return getLevelFilenameFromBasename(getPackedLevelBasename(type));
2149 char *getDefaultLevelFilename(int nr)
2151 return getSingleLevelFilename(nr);
2154 #if ENABLE_UNUSED_CODE
2155 static void setLevelFileInfo_SingleLevelFilename(struct LevelFileInfo *lfi,
2159 lfi->packed = FALSE;
2161 setString(&lfi->basename, getSingleLevelBasename(lfi->nr, lfi->type));
2162 setString(&lfi->filename, getLevelFilenameFromBasename(lfi->basename));
2166 static void setLevelFileInfo_FormatLevelFilename(struct LevelFileInfo *lfi,
2167 int type, char *format, ...)
2169 static char basename[MAX_FILENAME_LEN];
2172 va_start(ap, format);
2173 vsprintf(basename, format, ap);
2177 lfi->packed = FALSE;
2179 setString(&lfi->basename, basename);
2180 setString(&lfi->filename, getLevelFilenameFromBasename(lfi->basename));
2183 static void setLevelFileInfo_PackedLevelFilename(struct LevelFileInfo *lfi,
2189 setString(&lfi->basename, getPackedLevelBasename(lfi->type));
2190 setString(&lfi->filename, getLevelFilenameFromBasename(lfi->basename));
2193 static int getFiletypeFromID(char *filetype_id)
2195 char *filetype_id_lower;
2196 int filetype = LEVEL_FILE_TYPE_UNKNOWN;
2199 if (filetype_id == NULL)
2200 return LEVEL_FILE_TYPE_UNKNOWN;
2202 filetype_id_lower = getStringToLower(filetype_id);
2204 for (i = 0; filetype_id_list[i].id != NULL; i++)
2206 char *id_lower = getStringToLower(filetype_id_list[i].id);
2208 if (strEqual(filetype_id_lower, id_lower))
2209 filetype = filetype_id_list[i].filetype;
2213 if (filetype != LEVEL_FILE_TYPE_UNKNOWN)
2217 free(filetype_id_lower);
2222 char *getLocalLevelTemplateFilename(void)
2224 return getDefaultLevelFilename(-1);
2227 char *getGlobalLevelTemplateFilename(void)
2229 // global variable "leveldir_current" must be modified in the loop below
2230 LevelDirTree *leveldir_current_last = leveldir_current;
2231 char *filename = NULL;
2233 // check for template level in path from current to topmost tree node
2235 while (leveldir_current != NULL)
2237 filename = getDefaultLevelFilename(-1);
2239 if (fileExists(filename))
2242 leveldir_current = leveldir_current->node_parent;
2245 // restore global variable "leveldir_current" modified in above loop
2246 leveldir_current = leveldir_current_last;
2251 static void determineLevelFileInfo_Filename(struct LevelFileInfo *lfi)
2255 // special case: level number is negative => check for level template file
2258 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
2259 getSingleLevelBasename(-1));
2261 // replace local level template filename with global template filename
2262 setString(&lfi->filename, getGlobalLevelTemplateFilename());
2264 // no fallback if template file not existing
2268 // special case: check for file name/pattern specified in "levelinfo.conf"
2269 if (leveldir_current->level_filename != NULL)
2271 int filetype = getFiletypeFromID(leveldir_current->level_filetype);
2273 setLevelFileInfo_FormatLevelFilename(lfi, filetype,
2274 leveldir_current->level_filename, nr);
2276 lfi->packed = checkForPackageFromBasename(leveldir_current->level_filename);
2278 if (fileExists(lfi->filename))
2281 else if (leveldir_current->level_filetype != NULL)
2283 int filetype = getFiletypeFromID(leveldir_current->level_filetype);
2285 // check for specified native level file with standard file name
2286 setLevelFileInfo_FormatLevelFilename(lfi, filetype,
2287 "%03d.%s", nr, LEVELFILE_EXTENSION);
2288 if (fileExists(lfi->filename))
2292 // check for native Rocks'n'Diamonds level file
2293 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
2294 "%03d.%s", nr, LEVELFILE_EXTENSION);
2295 if (fileExists(lfi->filename))
2298 // check for Emerald Mine level file (V1)
2299 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "a%c%c",
2300 'a' + (nr / 10) % 26, '0' + nr % 10);
2301 if (fileExists(lfi->filename))
2303 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "A%c%c",
2304 'A' + (nr / 10) % 26, '0' + nr % 10);
2305 if (fileExists(lfi->filename))
2308 // check for Emerald Mine level file (V2 to V5)
2309 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%d", nr);
2310 if (fileExists(lfi->filename))
2313 // check for Emerald Mine level file (V6 / single mode)
2314 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02ds", nr);
2315 if (fileExists(lfi->filename))
2317 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02dS", nr);
2318 if (fileExists(lfi->filename))
2321 // check for Emerald Mine level file (V6 / teamwork mode)
2322 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02dt", nr);
2323 if (fileExists(lfi->filename))
2325 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02dT", nr);
2326 if (fileExists(lfi->filename))
2329 // check for various packed level file formats
2330 setLevelFileInfo_PackedLevelFilename(lfi, LEVEL_FILE_TYPE_UNKNOWN);
2331 if (fileExists(lfi->filename))
2334 // no known level file found -- use default values (and fail later)
2335 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
2336 "%03d.%s", nr, LEVELFILE_EXTENSION);
2339 static void determineLevelFileInfo_Filetype(struct LevelFileInfo *lfi)
2341 if (lfi->type == LEVEL_FILE_TYPE_UNKNOWN)
2342 lfi->type = getFileTypeFromBasename(lfi->basename);
2344 if (lfi->type == LEVEL_FILE_TYPE_RND)
2345 lfi->type = getFileTypeFromMagicBytes(lfi->filename, lfi->type);
2348 static void setLevelFileInfo(struct LevelFileInfo *level_file_info, int nr)
2350 // always start with reliable default values
2351 setFileInfoToDefaults(level_file_info);
2353 level_file_info->nr = nr; // set requested level number
2355 determineLevelFileInfo_Filename(level_file_info);
2356 determineLevelFileInfo_Filetype(level_file_info);
2359 static void copyLevelFileInfo(struct LevelFileInfo *lfi_from,
2360 struct LevelFileInfo *lfi_to)
2362 lfi_to->nr = lfi_from->nr;
2363 lfi_to->type = lfi_from->type;
2364 lfi_to->packed = lfi_from->packed;
2366 setString(&lfi_to->basename, lfi_from->basename);
2367 setString(&lfi_to->filename, lfi_from->filename);
2370 // ----------------------------------------------------------------------------
2371 // functions for loading R'n'D level
2372 // ----------------------------------------------------------------------------
2374 int getMappedElement(int element)
2376 // remap some (historic, now obsolete) elements
2380 case EL_PLAYER_OBSOLETE:
2381 element = EL_PLAYER_1;
2384 case EL_KEY_OBSOLETE:
2388 case EL_EM_KEY_1_FILE_OBSOLETE:
2389 element = EL_EM_KEY_1;
2392 case EL_EM_KEY_2_FILE_OBSOLETE:
2393 element = EL_EM_KEY_2;
2396 case EL_EM_KEY_3_FILE_OBSOLETE:
2397 element = EL_EM_KEY_3;
2400 case EL_EM_KEY_4_FILE_OBSOLETE:
2401 element = EL_EM_KEY_4;
2404 case EL_ENVELOPE_OBSOLETE:
2405 element = EL_ENVELOPE_1;
2413 if (element >= NUM_FILE_ELEMENTS)
2415 Warn("invalid level element %d", element);
2417 element = EL_UNKNOWN;
2425 static int getMappedElementByVersion(int element, int game_version)
2427 // remap some elements due to certain game version
2429 if (game_version <= VERSION_IDENT(2,2,0,0))
2431 // map game font elements
2432 element = (element == EL_CHAR('[') ? EL_CHAR_AUMLAUT :
2433 element == EL_CHAR('\\') ? EL_CHAR_OUMLAUT :
2434 element == EL_CHAR(']') ? EL_CHAR_UUMLAUT :
2435 element == EL_CHAR('^') ? EL_CHAR_COPYRIGHT : element);
2438 if (game_version < VERSION_IDENT(3,0,0,0))
2440 // map Supaplex gravity tube elements
2441 element = (element == EL_SP_GRAVITY_PORT_LEFT ? EL_SP_PORT_LEFT :
2442 element == EL_SP_GRAVITY_PORT_RIGHT ? EL_SP_PORT_RIGHT :
2443 element == EL_SP_GRAVITY_PORT_UP ? EL_SP_PORT_UP :
2444 element == EL_SP_GRAVITY_PORT_DOWN ? EL_SP_PORT_DOWN :
2451 static int LoadLevel_VERS(File *file, int chunk_size, struct LevelInfo *level)
2453 level->file_version = getFileVersion(file);
2454 level->game_version = getFileVersion(file);
2459 static int LoadLevel_DATE(File *file, int chunk_size, struct LevelInfo *level)
2461 level->creation_date.year = getFile16BitBE(file);
2462 level->creation_date.month = getFile8Bit(file);
2463 level->creation_date.day = getFile8Bit(file);
2465 level->creation_date.src = DATE_SRC_LEVELFILE;
2470 static int LoadLevel_HEAD(File *file, int chunk_size, struct LevelInfo *level)
2472 int initial_player_stepsize;
2473 int initial_player_gravity;
2476 level->fieldx = getFile8Bit(file);
2477 level->fieldy = getFile8Bit(file);
2479 level->time = getFile16BitBE(file);
2480 level->gems_needed = getFile16BitBE(file);
2482 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
2483 level->name[i] = getFile8Bit(file);
2484 level->name[MAX_LEVEL_NAME_LEN] = 0;
2486 for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
2487 level->score[i] = getFile8Bit(file);
2489 level->num_yamyam_contents = STD_ELEMENT_CONTENTS;
2490 for (i = 0; i < STD_ELEMENT_CONTENTS; i++)
2491 for (y = 0; y < 3; y++)
2492 for (x = 0; x < 3; x++)
2493 level->yamyam_content[i].e[x][y] = getMappedElement(getFile8Bit(file));
2495 level->amoeba_speed = getFile8Bit(file);
2496 level->time_magic_wall = getFile8Bit(file);
2497 level->time_wheel = getFile8Bit(file);
2498 level->amoeba_content = getMappedElement(getFile8Bit(file));
2500 initial_player_stepsize = (getFile8Bit(file) == 1 ? STEPSIZE_FAST :
2503 for (i = 0; i < MAX_PLAYERS; i++)
2504 level->initial_player_stepsize[i] = initial_player_stepsize;
2506 initial_player_gravity = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2508 for (i = 0; i < MAX_PLAYERS; i++)
2509 level->initial_player_gravity[i] = initial_player_gravity;
2511 level->encoding_16bit_field = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2512 level->em_slippery_gems = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2514 level->use_custom_template = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2516 level->block_last_field = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2517 level->sp_block_last_field = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2518 level->can_move_into_acid_bits = getFile32BitBE(file);
2519 level->dont_collide_with_bits = getFile8Bit(file);
2521 level->use_spring_bug = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2522 level->use_step_counter = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2524 level->instant_relocation = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2525 level->can_pass_to_walkable = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2526 level->grow_into_diggable = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2528 level->game_engine_type = getFile8Bit(file);
2530 ReadUnusedBytesFromFile(file, LEVEL_CHUNK_HEAD_UNUSED);
2535 static int LoadLevel_NAME(File *file, int chunk_size, struct LevelInfo *level)
2539 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
2540 level->name[i] = getFile8Bit(file);
2541 level->name[MAX_LEVEL_NAME_LEN] = 0;
2546 static int LoadLevel_AUTH(File *file, int chunk_size, struct LevelInfo *level)
2550 for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
2551 level->author[i] = getFile8Bit(file);
2552 level->author[MAX_LEVEL_AUTHOR_LEN] = 0;
2557 static int LoadLevel_BODY(File *file, int chunk_size, struct LevelInfo *level)
2560 int chunk_size_expected = level->fieldx * level->fieldy;
2562 /* Note: "chunk_size" was wrong before version 2.0 when elements are
2563 stored with 16-bit encoding (and should be twice as big then).
2564 Even worse, playfield data was stored 16-bit when only yamyam content
2565 contained 16-bit elements and vice versa. */
2567 if (level->encoding_16bit_field && level->file_version >= FILE_VERSION_2_0)
2568 chunk_size_expected *= 2;
2570 if (chunk_size_expected != chunk_size)
2572 ReadUnusedBytesFromFile(file, chunk_size);
2573 return chunk_size_expected;
2576 for (y = 0; y < level->fieldy; y++)
2577 for (x = 0; x < level->fieldx; x++)
2578 level->field[x][y] =
2579 getMappedElement(level->encoding_16bit_field ? getFile16BitBE(file) :
2584 static int LoadLevel_CONT(File *file, int chunk_size, struct LevelInfo *level)
2587 int header_size = 4;
2588 int content_size = MAX_ELEMENT_CONTENTS * 3 * 3;
2589 int chunk_size_expected = header_size + content_size;
2591 /* Note: "chunk_size" was wrong before version 2.0 when elements are
2592 stored with 16-bit encoding (and should be twice as big then).
2593 Even worse, playfield data was stored 16-bit when only yamyam content
2594 contained 16-bit elements and vice versa. */
2596 if (level->encoding_16bit_field && level->file_version >= FILE_VERSION_2_0)
2597 chunk_size_expected += content_size;
2599 if (chunk_size_expected != chunk_size)
2601 ReadUnusedBytesFromFile(file, chunk_size);
2602 return chunk_size_expected;
2606 level->num_yamyam_contents = getFile8Bit(file);
2610 // correct invalid number of content fields -- should never happen
2611 if (level->num_yamyam_contents < 1 ||
2612 level->num_yamyam_contents > MAX_ELEMENT_CONTENTS)
2613 level->num_yamyam_contents = STD_ELEMENT_CONTENTS;
2615 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
2616 for (y = 0; y < 3; y++)
2617 for (x = 0; x < 3; x++)
2618 level->yamyam_content[i].e[x][y] =
2619 getMappedElement(level->encoding_16bit_field ?
2620 getFile16BitBE(file) : getFile8Bit(file));
2624 static int LoadLevel_CNT2(File *file, int chunk_size, struct LevelInfo *level)
2629 int content_array[MAX_ELEMENT_CONTENTS][3][3];
2631 element = getMappedElement(getFile16BitBE(file));
2632 num_contents = getFile8Bit(file);
2634 getFile8Bit(file); // content x size (unused)
2635 getFile8Bit(file); // content y size (unused)
2637 ReadUnusedBytesFromFile(file, LEVEL_CHUNK_CNT2_UNUSED);
2639 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
2640 for (y = 0; y < 3; y++)
2641 for (x = 0; x < 3; x++)
2642 content_array[i][x][y] = getMappedElement(getFile16BitBE(file));
2644 // correct invalid number of content fields -- should never happen
2645 if (num_contents < 1 || num_contents > MAX_ELEMENT_CONTENTS)
2646 num_contents = STD_ELEMENT_CONTENTS;
2648 if (element == EL_YAMYAM)
2650 level->num_yamyam_contents = num_contents;
2652 for (i = 0; i < num_contents; i++)
2653 for (y = 0; y < 3; y++)
2654 for (x = 0; x < 3; x++)
2655 level->yamyam_content[i].e[x][y] = content_array[i][x][y];
2657 else if (element == EL_BD_AMOEBA)
2659 level->amoeba_content = content_array[0][0][0];
2663 Warn("cannot load content for element '%d'", element);
2669 static int LoadLevel_CNT3(File *file, int chunk_size, struct LevelInfo *level)
2675 int chunk_size_expected;
2677 element = getMappedElement(getFile16BitBE(file));
2678 if (!IS_ENVELOPE(element))
2679 element = EL_ENVELOPE_1;
2681 envelope_nr = element - EL_ENVELOPE_1;
2683 envelope_len = getFile16BitBE(file);
2685 level->envelope[envelope_nr].xsize = getFile8Bit(file);
2686 level->envelope[envelope_nr].ysize = getFile8Bit(file);
2688 ReadUnusedBytesFromFile(file, LEVEL_CHUNK_CNT3_UNUSED);
2690 chunk_size_expected = LEVEL_CHUNK_CNT3_SIZE(envelope_len);
2691 if (chunk_size_expected != chunk_size)
2693 ReadUnusedBytesFromFile(file, chunk_size - LEVEL_CHUNK_CNT3_HEADER);
2694 return chunk_size_expected;
2697 for (i = 0; i < envelope_len; i++)
2698 level->envelope[envelope_nr].text[i] = getFile8Bit(file);
2703 static int LoadLevel_CUS1(File *file, int chunk_size, struct LevelInfo *level)
2705 int num_changed_custom_elements = getFile16BitBE(file);
2706 int chunk_size_expected = 2 + num_changed_custom_elements * 6;
2709 if (chunk_size_expected != chunk_size)
2711 ReadUnusedBytesFromFile(file, chunk_size - 2);
2712 return chunk_size_expected;
2715 for (i = 0; i < num_changed_custom_elements; i++)
2717 int element = getMappedElement(getFile16BitBE(file));
2718 int properties = getFile32BitBE(file);
2720 if (IS_CUSTOM_ELEMENT(element))
2721 element_info[element].properties[EP_BITFIELD_BASE_NR] = properties;
2723 Warn("invalid custom element number %d", element);
2725 // older game versions that wrote level files with CUS1 chunks used
2726 // different default push delay values (not yet stored in level file)
2727 element_info[element].push_delay_fixed = 2;
2728 element_info[element].push_delay_random = 8;
2731 level->file_has_custom_elements = TRUE;
2736 static int LoadLevel_CUS2(File *file, int chunk_size, struct LevelInfo *level)
2738 int num_changed_custom_elements = getFile16BitBE(file);
2739 int chunk_size_expected = 2 + num_changed_custom_elements * 4;
2742 if (chunk_size_expected != chunk_size)
2744 ReadUnusedBytesFromFile(file, chunk_size - 2);
2745 return chunk_size_expected;
2748 for (i = 0; i < num_changed_custom_elements; i++)
2750 int element = getMappedElement(getFile16BitBE(file));
2751 int custom_target_element = getMappedElement(getFile16BitBE(file));
2753 if (IS_CUSTOM_ELEMENT(element))
2754 element_info[element].change->target_element = custom_target_element;
2756 Warn("invalid custom element number %d", element);
2759 level->file_has_custom_elements = TRUE;
2764 static int LoadLevel_CUS3(File *file, int chunk_size, struct LevelInfo *level)
2766 int num_changed_custom_elements = getFile16BitBE(file);
2767 int chunk_size_expected = LEVEL_CHUNK_CUS3_SIZE(num_changed_custom_elements);
2770 if (chunk_size_expected != chunk_size)
2772 ReadUnusedBytesFromFile(file, chunk_size - 2);
2773 return chunk_size_expected;
2776 for (i = 0; i < num_changed_custom_elements; i++)
2778 int element = getMappedElement(getFile16BitBE(file));
2779 struct ElementInfo *ei = &element_info[element];
2780 unsigned int event_bits;
2782 if (!IS_CUSTOM_ELEMENT(element))
2784 Warn("invalid custom element number %d", element);
2786 element = EL_INTERNAL_DUMMY;
2789 for (j = 0; j < MAX_ELEMENT_NAME_LEN; j++)
2790 ei->description[j] = getFile8Bit(file);
2791 ei->description[MAX_ELEMENT_NAME_LEN] = 0;
2793 ei->properties[EP_BITFIELD_BASE_NR] = getFile32BitBE(file);
2795 // some free bytes for future properties and padding
2796 ReadUnusedBytesFromFile(file, 7);
2798 ei->use_gfx_element = getFile8Bit(file);
2799 ei->gfx_element_initial = getMappedElement(getFile16BitBE(file));
2801 ei->collect_score_initial = getFile8Bit(file);
2802 ei->collect_count_initial = getFile8Bit(file);
2804 ei->push_delay_fixed = getFile16BitBE(file);
2805 ei->push_delay_random = getFile16BitBE(file);
2806 ei->move_delay_fixed = getFile16BitBE(file);
2807 ei->move_delay_random = getFile16BitBE(file);
2809 ei->move_pattern = getFile16BitBE(file);
2810 ei->move_direction_initial = getFile8Bit(file);
2811 ei->move_stepsize = getFile8Bit(file);
2813 for (y = 0; y < 3; y++)
2814 for (x = 0; x < 3; x++)
2815 ei->content.e[x][y] = getMappedElement(getFile16BitBE(file));
2817 // bits 0 - 31 of "has_event[]"
2818 event_bits = getFile32BitBE(file);
2819 for (j = 0; j < MIN(NUM_CHANGE_EVENTS, 32); j++)
2820 if (event_bits & (1 << j))
2821 ei->change->has_event[j] = TRUE;
2823 ei->change->target_element = getMappedElement(getFile16BitBE(file));
2825 ei->change->delay_fixed = getFile16BitBE(file);
2826 ei->change->delay_random = getFile16BitBE(file);
2827 ei->change->delay_frames = getFile16BitBE(file);
2829 ei->change->initial_trigger_element= getMappedElement(getFile16BitBE(file));
2831 ei->change->explode = getFile8Bit(file);
2832 ei->change->use_target_content = getFile8Bit(file);
2833 ei->change->only_if_complete = getFile8Bit(file);
2834 ei->change->use_random_replace = getFile8Bit(file);
2836 ei->change->random_percentage = getFile8Bit(file);
2837 ei->change->replace_when = getFile8Bit(file);
2839 for (y = 0; y < 3; y++)
2840 for (x = 0; x < 3; x++)
2841 ei->change->target_content.e[x][y] =
2842 getMappedElement(getFile16BitBE(file));
2844 ei->slippery_type = getFile8Bit(file);
2846 // some free bytes for future properties and padding
2847 ReadUnusedBytesFromFile(file, LEVEL_CPART_CUS3_UNUSED);
2849 // mark that this custom element has been modified
2850 ei->modified_settings = TRUE;
2853 level->file_has_custom_elements = TRUE;
2858 static int LoadLevel_CUS4(File *file, int chunk_size, struct LevelInfo *level)
2860 struct ElementInfo *ei;
2861 int chunk_size_expected;
2865 // ---------- custom element base property values (96 bytes) ----------------
2867 element = getMappedElement(getFile16BitBE(file));
2869 if (!IS_CUSTOM_ELEMENT(element))
2871 Warn("invalid custom element number %d", element);
2873 ReadUnusedBytesFromFile(file, chunk_size - 2);
2878 ei = &element_info[element];
2880 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
2881 ei->description[i] = getFile8Bit(file);
2882 ei->description[MAX_ELEMENT_NAME_LEN] = 0;
2884 ei->properties[EP_BITFIELD_BASE_NR] = getFile32BitBE(file);
2886 ReadUnusedBytesFromFile(file, 4); // reserved for more base properties
2888 ei->num_change_pages = getFile8Bit(file);
2890 chunk_size_expected = LEVEL_CHUNK_CUS4_SIZE(ei->num_change_pages);
2891 if (chunk_size_expected != chunk_size)
2893 ReadUnusedBytesFromFile(file, chunk_size - 43);
2894 return chunk_size_expected;
2897 ei->ce_value_fixed_initial = getFile16BitBE(file);
2898 ei->ce_value_random_initial = getFile16BitBE(file);
2899 ei->use_last_ce_value = getFile8Bit(file);
2901 ei->use_gfx_element = getFile8Bit(file);
2902 ei->gfx_element_initial = getMappedElement(getFile16BitBE(file));
2904 ei->collect_score_initial = getFile8Bit(file);
2905 ei->collect_count_initial = getFile8Bit(file);
2907 ei->drop_delay_fixed = getFile8Bit(file);
2908 ei->push_delay_fixed = getFile8Bit(file);
2909 ei->drop_delay_random = getFile8Bit(file);
2910 ei->push_delay_random = getFile8Bit(file);
2911 ei->move_delay_fixed = getFile16BitBE(file);
2912 ei->move_delay_random = getFile16BitBE(file);
2914 // bits 0 - 15 of "move_pattern" ...
2915 ei->move_pattern = getFile16BitBE(file);
2916 ei->move_direction_initial = getFile8Bit(file);
2917 ei->move_stepsize = getFile8Bit(file);
2919 ei->slippery_type = getFile8Bit(file);
2921 for (y = 0; y < 3; y++)
2922 for (x = 0; x < 3; x++)
2923 ei->content.e[x][y] = getMappedElement(getFile16BitBE(file));
2925 ei->move_enter_element = getMappedElement(getFile16BitBE(file));
2926 ei->move_leave_element = getMappedElement(getFile16BitBE(file));
2927 ei->move_leave_type = getFile8Bit(file);
2929 // ... bits 16 - 31 of "move_pattern" (not nice, but downward compatible)
2930 ei->move_pattern |= (getFile16BitBE(file) << 16);
2932 ei->access_direction = getFile8Bit(file);
2934 ei->explosion_delay = getFile8Bit(file);
2935 ei->ignition_delay = getFile8Bit(file);
2936 ei->explosion_type = getFile8Bit(file);
2938 // some free bytes for future custom property values and padding
2939 ReadUnusedBytesFromFile(file, 1);
2941 // ---------- change page property values (48 bytes) ------------------------
2943 setElementChangePages(ei, ei->num_change_pages);
2945 for (i = 0; i < ei->num_change_pages; i++)
2947 struct ElementChangeInfo *change = &ei->change_page[i];
2948 unsigned int event_bits;
2950 // always start with reliable default values
2951 setElementChangeInfoToDefaults(change);
2953 // bits 0 - 31 of "has_event[]" ...
2954 event_bits = getFile32BitBE(file);
2955 for (j = 0; j < MIN(NUM_CHANGE_EVENTS, 32); j++)
2956 if (event_bits & (1 << j))
2957 change->has_event[j] = TRUE;
2959 change->target_element = getMappedElement(getFile16BitBE(file));
2961 change->delay_fixed = getFile16BitBE(file);
2962 change->delay_random = getFile16BitBE(file);
2963 change->delay_frames = getFile16BitBE(file);
2965 change->initial_trigger_element = getMappedElement(getFile16BitBE(file));
2967 change->explode = getFile8Bit(file);
2968 change->use_target_content = getFile8Bit(file);
2969 change->only_if_complete = getFile8Bit(file);
2970 change->use_random_replace = getFile8Bit(file);
2972 change->random_percentage = getFile8Bit(file);
2973 change->replace_when = getFile8Bit(file);
2975 for (y = 0; y < 3; y++)
2976 for (x = 0; x < 3; x++)
2977 change->target_content.e[x][y]= getMappedElement(getFile16BitBE(file));
2979 change->can_change = getFile8Bit(file);
2981 change->trigger_side = getFile8Bit(file);
2983 change->trigger_player = getFile8Bit(file);
2984 change->trigger_page = getFile8Bit(file);
2986 change->trigger_page = (change->trigger_page == CH_PAGE_ANY_FILE ?
2987 CH_PAGE_ANY : (1 << change->trigger_page));
2989 change->has_action = getFile8Bit(file);
2990 change->action_type = getFile8Bit(file);
2991 change->action_mode = getFile8Bit(file);
2992 change->action_arg = getFile16BitBE(file);
2994 // ... bits 32 - 39 of "has_event[]" (not nice, but downward compatible)
2995 event_bits = getFile8Bit(file);
2996 for (j = 32; j < NUM_CHANGE_EVENTS; j++)
2997 if (event_bits & (1 << (j - 32)))
2998 change->has_event[j] = TRUE;
3001 // mark this custom element as modified
3002 ei->modified_settings = TRUE;
3004 level->file_has_custom_elements = TRUE;
3009 static int LoadLevel_GRP1(File *file, int chunk_size, struct LevelInfo *level)
3011 struct ElementInfo *ei;
3012 struct ElementGroupInfo *group;
3016 element = getMappedElement(getFile16BitBE(file));
3018 if (!IS_GROUP_ELEMENT(element))
3020 Warn("invalid group element number %d", element);
3022 ReadUnusedBytesFromFile(file, chunk_size - 2);
3027 ei = &element_info[element];
3029 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
3030 ei->description[i] = getFile8Bit(file);
3031 ei->description[MAX_ELEMENT_NAME_LEN] = 0;
3033 group = element_info[element].group;
3035 group->num_elements = getFile8Bit(file);
3037 ei->use_gfx_element = getFile8Bit(file);
3038 ei->gfx_element_initial = getMappedElement(getFile16BitBE(file));
3040 group->choice_mode = getFile8Bit(file);
3042 // some free bytes for future values and padding
3043 ReadUnusedBytesFromFile(file, 3);
3045 for (i = 0; i < MAX_ELEMENTS_IN_GROUP; i++)
3046 group->element[i] = getMappedElement(getFile16BitBE(file));
3048 // mark this group element as modified
3049 element_info[element].modified_settings = TRUE;
3051 level->file_has_custom_elements = TRUE;
3056 static int LoadLevel_MicroChunk(File *file, struct LevelFileConfigInfo *conf,
3057 int element, int real_element)
3059 int micro_chunk_size = 0;
3060 int conf_type = getFile8Bit(file);
3061 int byte_mask = conf_type & CONF_MASK_BYTES;
3062 boolean element_found = FALSE;
3065 micro_chunk_size += 1;
3067 if (byte_mask == CONF_MASK_MULTI_BYTES)
3069 int num_bytes = getFile16BitBE(file);
3070 byte *buffer = checked_malloc(num_bytes);
3072 ReadBytesFromFile(file, buffer, num_bytes);
3074 for (i = 0; conf[i].data_type != -1; i++)
3076 if (conf[i].element == element &&
3077 conf[i].conf_type == conf_type)
3079 int data_type = conf[i].data_type;
3080 int num_entities = num_bytes / CONF_ENTITY_NUM_BYTES(data_type);
3081 int max_num_entities = conf[i].max_num_entities;
3083 if (num_entities > max_num_entities)
3085 Warn("truncating number of entities for element %d from %d to %d",
3086 element, num_entities, max_num_entities);
3088 num_entities = max_num_entities;
3091 if (num_entities == 0 && (data_type == TYPE_ELEMENT_LIST ||
3092 data_type == TYPE_CONTENT_LIST))
3094 // for element and content lists, zero entities are not allowed
3095 Warn("found empty list of entities for element %d", element);
3097 // do not set "num_entities" here to prevent reading behind buffer
3099 *(int *)(conf[i].num_entities) = 1; // at least one is required
3103 *(int *)(conf[i].num_entities) = num_entities;
3106 element_found = TRUE;
3108 if (data_type == TYPE_STRING)
3110 char *string = (char *)(conf[i].value);
3113 for (j = 0; j < max_num_entities; j++)
3114 string[j] = (j < num_entities ? buffer[j] : '\0');
3116 else if (data_type == TYPE_ELEMENT_LIST)
3118 int *element_array = (int *)(conf[i].value);
3121 for (j = 0; j < num_entities; j++)
3123 getMappedElement(CONF_ELEMENTS_ELEMENT(buffer, j));
3125 else if (data_type == TYPE_CONTENT_LIST)
3127 struct Content *content= (struct Content *)(conf[i].value);
3130 for (c = 0; c < num_entities; c++)
3131 for (y = 0; y < 3; y++)
3132 for (x = 0; x < 3; x++)
3133 content[c].e[x][y] =
3134 getMappedElement(CONF_CONTENTS_ELEMENT(buffer, c, x, y));
3137 element_found = FALSE;
3143 checked_free(buffer);
3145 micro_chunk_size += 2 + num_bytes;
3147 else // constant size configuration data (1, 2 or 4 bytes)
3149 int value = (byte_mask == CONF_MASK_1_BYTE ? getFile8Bit (file) :
3150 byte_mask == CONF_MASK_2_BYTE ? getFile16BitBE(file) :
3151 byte_mask == CONF_MASK_4_BYTE ? getFile32BitBE(file) : 0);
3153 for (i = 0; conf[i].data_type != -1; i++)
3155 if (conf[i].element == element &&
3156 conf[i].conf_type == conf_type)
3158 int data_type = conf[i].data_type;
3160 if (data_type == TYPE_ELEMENT)
3161 value = getMappedElement(value);
3163 if (data_type == TYPE_BOOLEAN)
3164 *(boolean *)(conf[i].value) = (value ? TRUE : FALSE);
3166 *(int *) (conf[i].value) = value;
3168 element_found = TRUE;
3174 micro_chunk_size += CONF_VALUE_NUM_BYTES(byte_mask);
3179 char *error_conf_chunk_bytes =
3180 (byte_mask == CONF_MASK_1_BYTE ? "CONF_VALUE_8_BIT" :
3181 byte_mask == CONF_MASK_2_BYTE ? "CONF_VALUE_16_BIT" :
3182 byte_mask == CONF_MASK_4_BYTE ? "CONF_VALUE_32_BIT" :"CONF_VALUE_BYTES");
3183 int error_conf_chunk_token = conf_type & CONF_MASK_TOKEN;
3184 int error_element = real_element;
3186 Warn("cannot load micro chunk '%s(%d)' value for element %d ['%s']",
3187 error_conf_chunk_bytes, error_conf_chunk_token,
3188 error_element, EL_NAME(error_element));
3191 return micro_chunk_size;
3194 static int LoadLevel_INFO(File *file, int chunk_size, struct LevelInfo *level)
3196 int real_chunk_size = 0;
3198 li = *level; // copy level data into temporary buffer
3200 while (!checkEndOfFile(file))
3202 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_INFO, -1, -1);
3204 if (real_chunk_size >= chunk_size)
3208 *level = li; // copy temporary buffer back to level data
3210 return real_chunk_size;
3213 static int LoadLevel_CONF(File *file, int chunk_size, struct LevelInfo *level)
3215 int real_chunk_size = 0;
3217 li = *level; // copy level data into temporary buffer
3219 while (!checkEndOfFile(file))
3221 int element = getMappedElement(getFile16BitBE(file));
3223 real_chunk_size += 2;
3224 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_CONF,
3226 if (real_chunk_size >= chunk_size)
3230 *level = li; // copy temporary buffer back to level data
3232 return real_chunk_size;
3235 static int LoadLevel_ELEM(File *file, int chunk_size, struct LevelInfo *level)
3237 int real_chunk_size = 0;
3239 li = *level; // copy level data into temporary buffer
3241 while (!checkEndOfFile(file))
3243 int element = getMappedElement(getFile16BitBE(file));
3245 real_chunk_size += 2;
3246 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_ELEM,
3248 if (real_chunk_size >= chunk_size)
3252 *level = li; // copy temporary buffer back to level data
3254 return real_chunk_size;
3257 static int LoadLevel_NOTE(File *file, int chunk_size, struct LevelInfo *level)
3259 int element = getMappedElement(getFile16BitBE(file));
3260 int envelope_nr = element - EL_ENVELOPE_1;
3261 int real_chunk_size = 2;
3263 xx_envelope = level->envelope[envelope_nr]; // copy into temporary buffer
3265 while (!checkEndOfFile(file))
3267 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_NOTE,
3270 if (real_chunk_size >= chunk_size)
3274 level->envelope[envelope_nr] = xx_envelope; // copy from temporary buffer
3276 return real_chunk_size;
3279 static int LoadLevel_CUSX(File *file, int chunk_size, struct LevelInfo *level)
3281 int element = getMappedElement(getFile16BitBE(file));
3282 int real_chunk_size = 2;
3283 struct ElementInfo *ei = &element_info[element];
3286 xx_ei = *ei; // copy element data into temporary buffer
3288 xx_ei.num_change_pages = -1;
3290 while (!checkEndOfFile(file))
3292 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_CUSX_base,
3294 if (xx_ei.num_change_pages != -1)
3297 if (real_chunk_size >= chunk_size)
3303 if (ei->num_change_pages == -1)
3305 Warn("LoadLevel_CUSX(): missing 'num_change_pages' for '%s'",
3308 ei->num_change_pages = 1;
3310 setElementChangePages(ei, 1);
3311 setElementChangeInfoToDefaults(ei->change);
3313 return real_chunk_size;
3316 // initialize number of change pages stored for this custom element
3317 setElementChangePages(ei, ei->num_change_pages);
3318 for (i = 0; i < ei->num_change_pages; i++)
3319 setElementChangeInfoToDefaults(&ei->change_page[i]);
3321 // start with reading properties for the first change page
3322 xx_current_change_page = 0;
3324 while (!checkEndOfFile(file))
3326 struct ElementChangeInfo *change = &ei->change_page[xx_current_change_page];
3328 xx_change = *change; // copy change data into temporary buffer
3330 resetEventBits(); // reset bits; change page might have changed
3332 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_CUSX_change,
3335 *change = xx_change;
3337 setEventFlagsFromEventBits(change);
3339 if (real_chunk_size >= chunk_size)
3343 level->file_has_custom_elements = TRUE;
3345 return real_chunk_size;
3348 static int LoadLevel_GRPX(File *file, int chunk_size, struct LevelInfo *level)
3350 int element = getMappedElement(getFile16BitBE(file));
3351 int real_chunk_size = 2;
3352 struct ElementInfo *ei = &element_info[element];
3353 struct ElementGroupInfo *group = ei->group;
3355 xx_ei = *ei; // copy element data into temporary buffer
3356 xx_group = *group; // copy group data into temporary buffer
3358 while (!checkEndOfFile(file))
3360 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_GRPX,
3363 if (real_chunk_size >= chunk_size)
3370 level->file_has_custom_elements = TRUE;
3372 return real_chunk_size;
3375 static void LoadLevelFromFileInfo_RND(struct LevelInfo *level,
3376 struct LevelFileInfo *level_file_info,
3377 boolean level_info_only)
3379 char *filename = level_file_info->filename;
3380 char cookie[MAX_LINE_LEN];
3381 char chunk_name[CHUNK_ID_LEN + 1];
3385 if (!(file = openFile(filename, MODE_READ)))
3387 level->no_valid_file = TRUE;
3388 level->no_level_file = TRUE;
3390 if (level_info_only)
3393 Warn("cannot read level '%s' -- using empty level", filename);
3395 if (!setup.editor.use_template_for_new_levels)
3398 // if level file not found, try to initialize level data from template
3399 filename = getGlobalLevelTemplateFilename();
3401 if (!(file = openFile(filename, MODE_READ)))
3404 // default: for empty levels, use level template for custom elements
3405 level->use_custom_template = TRUE;
3407 level->no_valid_file = FALSE;
3410 getFileChunkBE(file, chunk_name, NULL);
3411 if (strEqual(chunk_name, "RND1"))
3413 getFile32BitBE(file); // not used
3415 getFileChunkBE(file, chunk_name, NULL);
3416 if (!strEqual(chunk_name, "CAVE"))
3418 level->no_valid_file = TRUE;
3420 Warn("unknown format of level file '%s'", filename);
3427 else // check for pre-2.0 file format with cookie string
3429 strcpy(cookie, chunk_name);
3430 if (getStringFromFile(file, &cookie[4], MAX_LINE_LEN - 4) == NULL)
3432 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
3433 cookie[strlen(cookie) - 1] = '\0';
3435 if (!checkCookieString(cookie, LEVEL_COOKIE_TMPL))
3437 level->no_valid_file = TRUE;
3439 Warn("unknown format of level file '%s'", filename);
3446 if ((level->file_version = getFileVersionFromCookieString(cookie)) == -1)
3448 level->no_valid_file = TRUE;
3450 Warn("unsupported version of level file '%s'", filename);
3457 // pre-2.0 level files have no game version, so use file version here
3458 level->game_version = level->file_version;
3461 if (level->file_version < FILE_VERSION_1_2)
3463 // level files from versions before 1.2.0 without chunk structure
3464 LoadLevel_HEAD(file, LEVEL_CHUNK_HEAD_SIZE, level);
3465 LoadLevel_BODY(file, level->fieldx * level->fieldy, level);
3473 int (*loader)(File *, int, struct LevelInfo *);
3477 { "VERS", LEVEL_CHUNK_VERS_SIZE, LoadLevel_VERS },
3478 { "DATE", LEVEL_CHUNK_DATE_SIZE, LoadLevel_DATE },
3479 { "HEAD", LEVEL_CHUNK_HEAD_SIZE, LoadLevel_HEAD },
3480 { "NAME", LEVEL_CHUNK_NAME_SIZE, LoadLevel_NAME },
3481 { "AUTH", LEVEL_CHUNK_AUTH_SIZE, LoadLevel_AUTH },
3482 { "INFO", -1, LoadLevel_INFO },
3483 { "BODY", -1, LoadLevel_BODY },
3484 { "CONT", -1, LoadLevel_CONT },
3485 { "CNT2", LEVEL_CHUNK_CNT2_SIZE, LoadLevel_CNT2 },
3486 { "CNT3", -1, LoadLevel_CNT3 },
3487 { "CUS1", -1, LoadLevel_CUS1 },
3488 { "CUS2", -1, LoadLevel_CUS2 },
3489 { "CUS3", -1, LoadLevel_CUS3 },
3490 { "CUS4", -1, LoadLevel_CUS4 },
3491 { "GRP1", -1, LoadLevel_GRP1 },
3492 { "CONF", -1, LoadLevel_CONF },
3493 { "ELEM", -1, LoadLevel_ELEM },
3494 { "NOTE", -1, LoadLevel_NOTE },
3495 { "CUSX", -1, LoadLevel_CUSX },
3496 { "GRPX", -1, LoadLevel_GRPX },
3501 while (getFileChunkBE(file, chunk_name, &chunk_size))
3505 while (chunk_info[i].name != NULL &&
3506 !strEqual(chunk_name, chunk_info[i].name))
3509 if (chunk_info[i].name == NULL)
3511 Warn("unknown chunk '%s' in level file '%s'",
3512 chunk_name, filename);
3514 ReadUnusedBytesFromFile(file, chunk_size);
3516 else if (chunk_info[i].size != -1 &&
3517 chunk_info[i].size != chunk_size)
3519 Warn("wrong size (%d) of chunk '%s' in level file '%s'",
3520 chunk_size, chunk_name, filename);
3522 ReadUnusedBytesFromFile(file, chunk_size);
3526 // call function to load this level chunk
3527 int chunk_size_expected =
3528 (chunk_info[i].loader)(file, chunk_size, level);
3530 // the size of some chunks cannot be checked before reading other
3531 // chunks first (like "HEAD" and "BODY") that contain some header
3532 // information, so check them here
3533 if (chunk_size_expected != chunk_size)
3535 Warn("wrong size (%d) of chunk '%s' in level file '%s'",
3536 chunk_size, chunk_name, filename);
3546 // ----------------------------------------------------------------------------
3547 // functions for loading EM level
3548 // ----------------------------------------------------------------------------
3550 static void CopyNativeLevel_RND_to_EM(struct LevelInfo *level)
3552 static int ball_xy[8][2] =
3563 struct LevelInfo_EM *level_em = level->native_em_level;
3564 struct CAVE *cav = level_em->cav;
3567 cav->width = MIN(level->fieldx, MAX_PLAYFIELD_WIDTH);
3568 cav->height = MIN(level->fieldy, MAX_PLAYFIELD_HEIGHT);
3570 cav->time_seconds = level->time;
3571 cav->gems_needed = level->gems_needed;
3573 cav->emerald_score = level->score[SC_EMERALD];
3574 cav->diamond_score = level->score[SC_DIAMOND];
3575 cav->alien_score = level->score[SC_ROBOT];
3576 cav->tank_score = level->score[SC_SPACESHIP];
3577 cav->bug_score = level->score[SC_BUG];
3578 cav->eater_score = level->score[SC_YAMYAM];
3579 cav->nut_score = level->score[SC_NUT];
3580 cav->dynamite_score = level->score[SC_DYNAMITE];
3581 cav->key_score = level->score[SC_KEY];
3582 cav->exit_score = level->score[SC_TIME_BONUS];
3584 cav->num_eater_arrays = level->num_yamyam_contents;
3586 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
3587 for (y = 0; y < 3; y++)
3588 for (x = 0; x < 3; x++)
3589 cav->eater_array[i][y * 3 + x] =
3590 map_element_RND_to_EM_cave(level->yamyam_content[i].e[x][y]);
3592 cav->amoeba_time = level->amoeba_speed;
3593 cav->wonderwall_time = level->time_magic_wall;
3594 cav->wheel_time = level->time_wheel;
3596 cav->android_move_time = level->android_move_time;
3597 cav->android_clone_time = level->android_clone_time;
3598 cav->ball_random = level->ball_random;
3599 cav->ball_active = level->ball_active_initial;
3600 cav->ball_time = level->ball_time;
3601 cav->num_ball_arrays = level->num_ball_contents;
3603 cav->lenses_score = level->lenses_score;
3604 cav->magnify_score = level->magnify_score;
3605 cav->slurp_score = level->slurp_score;
3607 cav->lenses_time = level->lenses_time;
3608 cav->magnify_time = level->magnify_time;
3610 cav->wind_direction =
3611 map_direction_RND_to_EM(level->wind_direction_initial);
3613 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
3614 for (j = 0; j < 8; j++)
3615 cav->ball_array[i][j] =
3616 map_element_RND_to_EM_cave(level->ball_content[i].
3617 e[ball_xy[j][0]][ball_xy[j][1]]);
3619 map_android_clone_elements_RND_to_EM(level);
3621 // first fill the complete playfield with the empty space element
3622 for (y = 0; y < EM_MAX_CAVE_HEIGHT; y++)
3623 for (x = 0; x < EM_MAX_CAVE_WIDTH; x++)
3624 cav->cave[x][y] = Cblank;
3626 // then copy the real level contents from level file into the playfield
3627 for (y = 0; y < cav->height; y++) for (x = 0; x < cav->width; x++)
3629 int new_element = map_element_RND_to_EM_cave(level->field[x][y]);
3631 if (level->field[x][y] == EL_AMOEBA_DEAD)
3632 new_element = map_element_RND_to_EM_cave(EL_AMOEBA_WET);
3634 cav->cave[x][y] = new_element;
3637 for (i = 0; i < MAX_PLAYERS; i++)
3639 cav->player_x[i] = -1;
3640 cav->player_y[i] = -1;
3643 // initialize player positions and delete players from the playfield
3644 for (y = 0; y < cav->height; y++) for (x = 0; x < cav->width; x++)
3646 if (IS_PLAYER_ELEMENT(level->field[x][y]))
3648 int player_nr = GET_PLAYER_NR(level->field[x][y]);
3650 cav->player_x[player_nr] = x;
3651 cav->player_y[player_nr] = y;
3653 cav->cave[x][y] = map_element_RND_to_EM_cave(EL_EMPTY);
3658 static void CopyNativeLevel_EM_to_RND(struct LevelInfo *level)
3660 static int ball_xy[8][2] =
3671 struct LevelInfo_EM *level_em = level->native_em_level;
3672 struct CAVE *cav = level_em->cav;
3675 level->fieldx = MIN(cav->width, MAX_LEV_FIELDX);
3676 level->fieldy = MIN(cav->height, MAX_LEV_FIELDY);
3678 level->time = cav->time_seconds;
3679 level->gems_needed = cav->gems_needed;
3681 sprintf(level->name, "Level %d", level->file_info.nr);
3683 level->score[SC_EMERALD] = cav->emerald_score;
3684 level->score[SC_DIAMOND] = cav->diamond_score;
3685 level->score[SC_ROBOT] = cav->alien_score;
3686 level->score[SC_SPACESHIP] = cav->tank_score;
3687 level->score[SC_BUG] = cav->bug_score;
3688 level->score[SC_YAMYAM] = cav->eater_score;
3689 level->score[SC_NUT] = cav->nut_score;
3690 level->score[SC_DYNAMITE] = cav->dynamite_score;
3691 level->score[SC_KEY] = cav->key_score;
3692 level->score[SC_TIME_BONUS] = cav->exit_score;
3694 level->num_yamyam_contents = cav->num_eater_arrays;
3696 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
3697 for (y = 0; y < 3; y++)
3698 for (x = 0; x < 3; x++)
3699 level->yamyam_content[i].e[x][y] =
3700 map_element_EM_to_RND_cave(cav->eater_array[i][y * 3 + x]);
3702 level->amoeba_speed = cav->amoeba_time;
3703 level->time_magic_wall = cav->wonderwall_time;
3704 level->time_wheel = cav->wheel_time;
3706 level->android_move_time = cav->android_move_time;
3707 level->android_clone_time = cav->android_clone_time;
3708 level->ball_random = cav->ball_random;
3709 level->ball_active_initial = cav->ball_active;
3710 level->ball_time = cav->ball_time;
3711 level->num_ball_contents = cav->num_ball_arrays;
3713 level->lenses_score = cav->lenses_score;
3714 level->magnify_score = cav->magnify_score;
3715 level->slurp_score = cav->slurp_score;
3717 level->lenses_time = cav->lenses_time;
3718 level->magnify_time = cav->magnify_time;
3720 level->wind_direction_initial =
3721 map_direction_EM_to_RND(cav->wind_direction);
3723 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
3724 for (j = 0; j < 8; j++)
3725 level->ball_content[i].e[ball_xy[j][0]][ball_xy[j][1]] =
3726 map_element_EM_to_RND_cave(cav->ball_array[i][j]);
3728 map_android_clone_elements_EM_to_RND(level);
3730 // convert the playfield (some elements need special treatment)
3731 for (y = 0; y < level->fieldy; y++) for (x = 0; x < level->fieldx; x++)
3733 int new_element = map_element_EM_to_RND_cave(cav->cave[x][y]);
3735 if (new_element == EL_AMOEBA_WET && level->amoeba_speed == 0)
3736 new_element = EL_AMOEBA_DEAD;
3738 level->field[x][y] = new_element;
3741 for (i = 0; i < MAX_PLAYERS; i++)
3743 // in case of all players set to the same field, use the first player
3744 int nr = MAX_PLAYERS - i - 1;
3745 int jx = cav->player_x[nr];
3746 int jy = cav->player_y[nr];
3748 if (jx != -1 && jy != -1)
3749 level->field[jx][jy] = EL_PLAYER_1 + nr;
3752 // time score is counted for each 10 seconds left in Emerald Mine levels
3753 level->time_score_base = 10;
3757 // ----------------------------------------------------------------------------
3758 // functions for loading SP level
3759 // ----------------------------------------------------------------------------
3761 static void CopyNativeLevel_RND_to_SP(struct LevelInfo *level)
3763 struct LevelInfo_SP *level_sp = level->native_sp_level;
3764 LevelInfoType *header = &level_sp->header;
3767 level_sp->width = level->fieldx;
3768 level_sp->height = level->fieldy;
3770 for (x = 0; x < level->fieldx; x++)
3771 for (y = 0; y < level->fieldy; y++)
3772 level_sp->playfield[x][y] = map_element_RND_to_SP(level->field[x][y]);
3774 header->InitialGravity = (level->initial_player_gravity[0] ? 1 : 0);
3776 for (i = 0; i < SP_LEVEL_NAME_LEN; i++)
3777 header->LevelTitle[i] = level->name[i];
3778 // !!! NO STRING TERMINATION IN SUPAPLEX VB CODE YET -- FIX THIS !!!
3780 header->InfotronsNeeded = level->gems_needed;
3782 header->SpecialPortCount = 0;
3784 for (x = 0; x < level->fieldx; x++) for (y = 0; y < level->fieldy; y++)
3786 boolean gravity_port_found = FALSE;
3787 boolean gravity_port_valid = FALSE;
3788 int gravity_port_flag;
3789 int gravity_port_base_element;
3790 int element = level->field[x][y];
3792 if (element >= EL_SP_GRAVITY_ON_PORT_RIGHT &&
3793 element <= EL_SP_GRAVITY_ON_PORT_UP)
3795 gravity_port_found = TRUE;
3796 gravity_port_valid = TRUE;
3797 gravity_port_flag = 1;
3798 gravity_port_base_element = EL_SP_GRAVITY_ON_PORT_RIGHT;
3800 else if (element >= EL_SP_GRAVITY_OFF_PORT_RIGHT &&
3801 element <= EL_SP_GRAVITY_OFF_PORT_UP)
3803 gravity_port_found = TRUE;
3804 gravity_port_valid = TRUE;
3805 gravity_port_flag = 0;
3806 gravity_port_base_element = EL_SP_GRAVITY_OFF_PORT_RIGHT;
3808 else if (element >= EL_SP_GRAVITY_PORT_RIGHT &&
3809 element <= EL_SP_GRAVITY_PORT_UP)
3811 // change R'n'D style gravity inverting special port to normal port
3812 // (there are no gravity inverting ports in native Supaplex engine)
3814 gravity_port_found = TRUE;
3815 gravity_port_valid = FALSE;
3816 gravity_port_base_element = EL_SP_GRAVITY_PORT_RIGHT;
3819 if (gravity_port_found)
3821 if (gravity_port_valid &&
3822 header->SpecialPortCount < SP_MAX_SPECIAL_PORTS)
3824 SpecialPortType *port = &header->SpecialPort[header->SpecialPortCount];
3826 port->PortLocation = (y * level->fieldx + x) * 2;
3827 port->Gravity = gravity_port_flag;
3829 element += EL_SP_GRAVITY_PORT_RIGHT - gravity_port_base_element;
3831 header->SpecialPortCount++;
3835 // change special gravity port to normal port
3837 element += EL_SP_PORT_RIGHT - gravity_port_base_element;
3840 level_sp->playfield[x][y] = element - EL_SP_START;
3845 static void CopyNativeLevel_SP_to_RND(struct LevelInfo *level)
3847 struct LevelInfo_SP *level_sp = level->native_sp_level;
3848 LevelInfoType *header = &level_sp->header;
3849 boolean num_invalid_elements = 0;
3852 level->fieldx = level_sp->width;
3853 level->fieldy = level_sp->height;
3855 for (x = 0; x < level->fieldx; x++)
3857 for (y = 0; y < level->fieldy; y++)
3859 int element_old = level_sp->playfield[x][y];
3860 int element_new = getMappedElement(map_element_SP_to_RND(element_old));
3862 if (element_new == EL_UNKNOWN)
3864 num_invalid_elements++;
3866 Debug("level:native:SP", "invalid element %d at position %d, %d",
3870 level->field[x][y] = element_new;
3874 if (num_invalid_elements > 0)
3875 Warn("found %d invalid elements%s", num_invalid_elements,
3876 (!options.debug ? " (use '--debug' for more details)" : ""));
3878 for (i = 0; i < MAX_PLAYERS; i++)
3879 level->initial_player_gravity[i] =
3880 (header->InitialGravity == 1 ? TRUE : FALSE);
3882 // skip leading spaces
3883 for (i = 0; i < SP_LEVEL_NAME_LEN; i++)
3884 if (header->LevelTitle[i] != ' ')
3888 for (j = 0; i < SP_LEVEL_NAME_LEN; i++, j++)
3889 level->name[j] = header->LevelTitle[i];
3890 level->name[j] = '\0';
3892 // cut trailing spaces
3894 if (level->name[j - 1] == ' ' && level->name[j] == '\0')
3895 level->name[j - 1] = '\0';
3897 level->gems_needed = header->InfotronsNeeded;
3899 for (i = 0; i < header->SpecialPortCount; i++)
3901 SpecialPortType *port = &header->SpecialPort[i];
3902 int port_location = port->PortLocation;
3903 int gravity = port->Gravity;
3904 int port_x, port_y, port_element;
3906 port_x = (port_location / 2) % level->fieldx;
3907 port_y = (port_location / 2) / level->fieldx;
3909 if (port_x < 0 || port_x >= level->fieldx ||
3910 port_y < 0 || port_y >= level->fieldy)
3912 Warn("special port position (%d, %d) out of bounds", port_x, port_y);
3917 port_element = level->field[port_x][port_y];
3919 if (port_element < EL_SP_GRAVITY_PORT_RIGHT ||
3920 port_element > EL_SP_GRAVITY_PORT_UP)
3922 Warn("no special port at position (%d, %d)", port_x, port_y);
3927 // change previous (wrong) gravity inverting special port to either
3928 // gravity enabling special port or gravity disabling special port
3929 level->field[port_x][port_y] +=
3930 (gravity == 1 ? EL_SP_GRAVITY_ON_PORT_RIGHT :
3931 EL_SP_GRAVITY_OFF_PORT_RIGHT) - EL_SP_GRAVITY_PORT_RIGHT;
3934 // change special gravity ports without database entries to normal ports
3935 for (x = 0; x < level->fieldx; x++)
3936 for (y = 0; y < level->fieldy; y++)
3937 if (level->field[x][y] >= EL_SP_GRAVITY_PORT_RIGHT &&
3938 level->field[x][y] <= EL_SP_GRAVITY_PORT_UP)
3939 level->field[x][y] += EL_SP_PORT_RIGHT - EL_SP_GRAVITY_PORT_RIGHT;
3941 level->time = 0; // no time limit
3942 level->amoeba_speed = 0;
3943 level->time_magic_wall = 0;
3944 level->time_wheel = 0;
3945 level->amoeba_content = EL_EMPTY;
3947 // original Supaplex does not use score values -- rate by playing time
3948 for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
3949 level->score[i] = 0;
3951 level->rate_time_over_score = TRUE;
3953 // there are no yamyams in supaplex levels
3954 for (i = 0; i < level->num_yamyam_contents; i++)
3955 for (x = 0; x < 3; x++)
3956 for (y = 0; y < 3; y++)
3957 level->yamyam_content[i].e[x][y] = EL_EMPTY;
3960 static void CopyNativeTape_RND_to_SP(struct LevelInfo *level)
3962 struct LevelInfo_SP *level_sp = level->native_sp_level;
3963 struct DemoInfo_SP *demo = &level_sp->demo;
3966 // always start with reliable default values
3967 demo->is_available = FALSE;
3970 if (TAPE_IS_EMPTY(tape))
3973 demo->level_nr = tape.level_nr; // (currently not used)
3975 level_sp->header.DemoRandomSeed = tape.random_seed;
3979 for (i = 0; i < tape.length; i++)
3981 int demo_action = map_key_RND_to_SP(tape.pos[i].action[0]);
3982 int demo_repeat = tape.pos[i].delay;
3983 int demo_entries = (demo_repeat + 15) / 16;
3985 if (demo->length + demo_entries >= SP_MAX_TAPE_LEN)
3987 Warn("tape truncated: size exceeds maximum SP demo size %d",
3993 for (j = 0; j < demo_repeat / 16; j++)
3994 demo->data[demo->length++] = 0xf0 | demo_action;
3996 if (demo_repeat % 16)
3997 demo->data[demo->length++] = ((demo_repeat % 16 - 1) << 4) | demo_action;
4000 demo->is_available = TRUE;
4003 static void setTapeInfoToDefaults(void);
4005 static void CopyNativeTape_SP_to_RND(struct LevelInfo *level)
4007 struct LevelInfo_SP *level_sp = level->native_sp_level;
4008 struct DemoInfo_SP *demo = &level_sp->demo;
4009 char *filename = level->file_info.filename;
4012 // always start with reliable default values
4013 setTapeInfoToDefaults();
4015 if (!demo->is_available)
4018 tape.level_nr = demo->level_nr; // (currently not used)
4019 tape.random_seed = level_sp->header.DemoRandomSeed;
4021 TapeSetDateFromEpochSeconds(getFileTimestampEpochSeconds(filename));
4024 tape.pos[tape.counter].delay = 0;
4026 for (i = 0; i < demo->length; i++)
4028 int demo_action = demo->data[i] & 0x0f;
4029 int demo_repeat = (demo->data[i] & 0xf0) >> 4;
4030 int tape_action = map_key_SP_to_RND(demo_action);
4031 int tape_repeat = demo_repeat + 1;
4032 byte action[MAX_TAPE_ACTIONS] = { tape_action };
4033 boolean success = 0;
4036 for (j = 0; j < tape_repeat; j++)
4037 success = TapeAddAction(action);
4041 Warn("SP demo truncated: size exceeds maximum tape size %d",
4048 TapeHaltRecording();
4052 // ----------------------------------------------------------------------------
4053 // functions for loading MM level
4054 // ----------------------------------------------------------------------------
4056 static void CopyNativeLevel_RND_to_MM(struct LevelInfo *level)
4058 struct LevelInfo_MM *level_mm = level->native_mm_level;
4061 level_mm->fieldx = MIN(level->fieldx, MM_MAX_PLAYFIELD_WIDTH);
4062 level_mm->fieldy = MIN(level->fieldy, MM_MAX_PLAYFIELD_HEIGHT);
4064 level_mm->time = level->time;
4065 level_mm->kettles_needed = level->gems_needed;
4066 level_mm->auto_count_kettles = level->auto_count_gems;
4068 level_mm->laser_red = level->mm_laser_red;
4069 level_mm->laser_green = level->mm_laser_green;
4070 level_mm->laser_blue = level->mm_laser_blue;
4072 strcpy(level_mm->name, level->name);
4073 strcpy(level_mm->author, level->author);
4075 level_mm->score[SC_EMERALD] = level->score[SC_EMERALD];
4076 level_mm->score[SC_PACMAN] = level->score[SC_PACMAN];
4077 level_mm->score[SC_KEY] = level->score[SC_KEY];
4078 level_mm->score[SC_TIME_BONUS] = level->score[SC_TIME_BONUS];
4079 level_mm->score[SC_ELEM_BONUS] = level->score[SC_ELEM_BONUS];
4081 level_mm->amoeba_speed = level->amoeba_speed;
4082 level_mm->time_fuse = level->mm_time_fuse;
4083 level_mm->time_bomb = level->mm_time_bomb;
4084 level_mm->time_ball = level->mm_time_ball;
4085 level_mm->time_block = level->mm_time_block;
4087 for (x = 0; x < level->fieldx; x++)
4088 for (y = 0; y < level->fieldy; y++)
4090 level_mm->field[x][y] = map_element_RND_to_MM(level->field[x][y]);
4093 static void CopyNativeLevel_MM_to_RND(struct LevelInfo *level)
4095 struct LevelInfo_MM *level_mm = level->native_mm_level;
4098 level->fieldx = MIN(level_mm->fieldx, MAX_LEV_FIELDX);
4099 level->fieldy = MIN(level_mm->fieldy, MAX_LEV_FIELDY);
4101 level->time = level_mm->time;
4102 level->gems_needed = level_mm->kettles_needed;
4103 level->auto_count_gems = level_mm->auto_count_kettles;
4105 level->mm_laser_red = level_mm->laser_red;
4106 level->mm_laser_green = level_mm->laser_green;
4107 level->mm_laser_blue = level_mm->laser_blue;
4109 strcpy(level->name, level_mm->name);
4111 // only overwrite author from 'levelinfo.conf' if author defined in level
4112 if (!strEqual(level_mm->author, ANONYMOUS_NAME))
4113 strcpy(level->author, level_mm->author);
4115 level->score[SC_EMERALD] = level_mm->score[SC_EMERALD];
4116 level->score[SC_PACMAN] = level_mm->score[SC_PACMAN];
4117 level->score[SC_KEY] = level_mm->score[SC_KEY];
4118 level->score[SC_TIME_BONUS] = level_mm->score[SC_TIME_BONUS];
4119 level->score[SC_ELEM_BONUS] = level_mm->score[SC_ELEM_BONUS];
4121 level->amoeba_speed = level_mm->amoeba_speed;
4122 level->mm_time_fuse = level_mm->time_fuse;
4123 level->mm_time_bomb = level_mm->time_bomb;
4124 level->mm_time_ball = level_mm->time_ball;
4125 level->mm_time_block = level_mm->time_block;
4127 for (x = 0; x < level->fieldx; x++)
4128 for (y = 0; y < level->fieldy; y++)
4129 level->field[x][y] = map_element_MM_to_RND(level_mm->field[x][y]);
4133 // ----------------------------------------------------------------------------
4134 // functions for loading DC level
4135 // ----------------------------------------------------------------------------
4137 #define DC_LEVEL_HEADER_SIZE 344
4139 static unsigned short getDecodedWord_DC(unsigned short data_encoded,
4142 static int last_data_encoded;
4146 int diff_hi, diff_lo;
4147 int data_hi, data_lo;
4148 unsigned short data_decoded;
4152 last_data_encoded = 0;
4159 diff = data_encoded - last_data_encoded;
4160 diff_hi = diff & ~0xff;
4161 diff_lo = diff & 0xff;
4165 data_hi = diff_hi - (offset1 << 8) + (offset2 & 0xff00);
4166 data_lo = (diff_lo + (data_hi >> 16)) & 0x00ff;
4167 data_hi = data_hi & 0xff00;
4169 data_decoded = data_hi | data_lo;
4171 last_data_encoded = data_encoded;
4173 offset1 = (offset1 + 1) % 31;
4174 offset2 = offset2 & 0xff;
4176 return data_decoded;
4179 static int getMappedElement_DC(int element)
4187 // 0x0117 - 0x036e: (?)
4190 // 0x042d - 0x0684: (?)
4206 element = EL_CRYSTAL;
4209 case 0x0e77: // quicksand (boulder)
4210 element = EL_QUICKSAND_FAST_FULL;
4213 case 0x0e99: // slow quicksand (boulder)
4214 element = EL_QUICKSAND_FULL;
4218 element = EL_EM_EXIT_OPEN;
4222 element = EL_EM_EXIT_CLOSED;
4226 element = EL_EM_STEEL_EXIT_OPEN;
4230 element = EL_EM_STEEL_EXIT_CLOSED;
4233 case 0x0f4f: // dynamite (lit 1)
4234 element = EL_EM_DYNAMITE_ACTIVE;
4237 case 0x0f57: // dynamite (lit 2)
4238 element = EL_EM_DYNAMITE_ACTIVE;
4241 case 0x0f5f: // dynamite (lit 3)
4242 element = EL_EM_DYNAMITE_ACTIVE;
4245 case 0x0f67: // dynamite (lit 4)
4246 element = EL_EM_DYNAMITE_ACTIVE;
4253 element = EL_AMOEBA_WET;
4257 element = EL_AMOEBA_DROP;
4261 element = EL_DC_MAGIC_WALL;
4265 element = EL_SPACESHIP_UP;
4269 element = EL_SPACESHIP_DOWN;
4273 element = EL_SPACESHIP_LEFT;
4277 element = EL_SPACESHIP_RIGHT;
4281 element = EL_BUG_UP;
4285 element = EL_BUG_DOWN;
4289 element = EL_BUG_LEFT;
4293 element = EL_BUG_RIGHT;
4297 element = EL_MOLE_UP;
4301 element = EL_MOLE_DOWN;
4305 element = EL_MOLE_LEFT;
4309 element = EL_MOLE_RIGHT;
4317 element = EL_YAMYAM_UP;
4321 element = EL_SWITCHGATE_OPEN;
4325 element = EL_SWITCHGATE_CLOSED;
4329 element = EL_DC_SWITCHGATE_SWITCH_UP;
4333 element = EL_TIMEGATE_CLOSED;
4336 case 0x144c: // conveyor belt switch (green)
4337 element = EL_CONVEYOR_BELT_3_SWITCH_MIDDLE;
4340 case 0x144f: // conveyor belt switch (red)
4341 element = EL_CONVEYOR_BELT_1_SWITCH_MIDDLE;
4344 case 0x1452: // conveyor belt switch (blue)
4345 element = EL_CONVEYOR_BELT_4_SWITCH_MIDDLE;
4349 element = EL_CONVEYOR_BELT_3_MIDDLE;
4353 element = EL_CONVEYOR_BELT_3_LEFT;
4357 element = EL_CONVEYOR_BELT_3_RIGHT;
4361 element = EL_CONVEYOR_BELT_1_MIDDLE;
4365 element = EL_CONVEYOR_BELT_1_LEFT;
4369 element = EL_CONVEYOR_BELT_1_RIGHT;
4373 element = EL_CONVEYOR_BELT_4_MIDDLE;
4377 element = EL_CONVEYOR_BELT_4_LEFT;
4381 element = EL_CONVEYOR_BELT_4_RIGHT;
4385 element = EL_EXPANDABLE_WALL_HORIZONTAL;
4389 element = EL_EXPANDABLE_WALL_VERTICAL;
4393 element = EL_EXPANDABLE_WALL_ANY;
4396 case 0x14ce: // growing steel wall (left/right)
4397 element = EL_EXPANDABLE_STEELWALL_HORIZONTAL;
4400 case 0x14df: // growing steel wall (up/down)
4401 element = EL_EXPANDABLE_STEELWALL_VERTICAL;
4404 case 0x14e8: // growing steel wall (up/down/left/right)
4405 element = EL_EXPANDABLE_STEELWALL_ANY;
4409 element = EL_SHIELD_DEADLY;
4413 element = EL_EXTRA_TIME;
4421 element = EL_EMPTY_SPACE;
4424 case 0x1578: // quicksand (empty)
4425 element = EL_QUICKSAND_FAST_EMPTY;
4428 case 0x1579: // slow quicksand (empty)
4429 element = EL_QUICKSAND_EMPTY;
4439 element = EL_EM_DYNAMITE;
4442 case 0x15a1: // key (red)
4443 element = EL_EM_KEY_1;
4446 case 0x15a2: // key (yellow)
4447 element = EL_EM_KEY_2;
4450 case 0x15a3: // key (blue)
4451 element = EL_EM_KEY_4;
4454 case 0x15a4: // key (green)
4455 element = EL_EM_KEY_3;
4458 case 0x15a5: // key (white)
4459 element = EL_DC_KEY_WHITE;
4463 element = EL_WALL_SLIPPERY;
4470 case 0x15a8: // wall (not round)
4474 case 0x15a9: // (blue)
4475 element = EL_CHAR_A;
4478 case 0x15aa: // (blue)
4479 element = EL_CHAR_B;
4482 case 0x15ab: // (blue)
4483 element = EL_CHAR_C;
4486 case 0x15ac: // (blue)
4487 element = EL_CHAR_D;
4490 case 0x15ad: // (blue)
4491 element = EL_CHAR_E;
4494 case 0x15ae: // (blue)
4495 element = EL_CHAR_F;
4498 case 0x15af: // (blue)
4499 element = EL_CHAR_G;
4502 case 0x15b0: // (blue)
4503 element = EL_CHAR_H;
4506 case 0x15b1: // (blue)
4507 element = EL_CHAR_I;
4510 case 0x15b2: // (blue)
4511 element = EL_CHAR_J;
4514 case 0x15b3: // (blue)
4515 element = EL_CHAR_K;
4518 case 0x15b4: // (blue)
4519 element = EL_CHAR_L;
4522 case 0x15b5: // (blue)
4523 element = EL_CHAR_M;
4526 case 0x15b6: // (blue)
4527 element = EL_CHAR_N;
4530 case 0x15b7: // (blue)
4531 element = EL_CHAR_O;
4534 case 0x15b8: // (blue)
4535 element = EL_CHAR_P;
4538 case 0x15b9: // (blue)
4539 element = EL_CHAR_Q;
4542 case 0x15ba: // (blue)
4543 element = EL_CHAR_R;
4546 case 0x15bb: // (blue)
4547 element = EL_CHAR_S;
4550 case 0x15bc: // (blue)
4551 element = EL_CHAR_T;
4554 case 0x15bd: // (blue)
4555 element = EL_CHAR_U;
4558 case 0x15be: // (blue)
4559 element = EL_CHAR_V;
4562 case 0x15bf: // (blue)
4563 element = EL_CHAR_W;
4566 case 0x15c0: // (blue)
4567 element = EL_CHAR_X;
4570 case 0x15c1: // (blue)
4571 element = EL_CHAR_Y;
4574 case 0x15c2: // (blue)
4575 element = EL_CHAR_Z;
4578 case 0x15c3: // (blue)
4579 element = EL_CHAR_AUMLAUT;
4582 case 0x15c4: // (blue)
4583 element = EL_CHAR_OUMLAUT;
4586 case 0x15c5: // (blue)
4587 element = EL_CHAR_UUMLAUT;
4590 case 0x15c6: // (blue)
4591 element = EL_CHAR_0;
4594 case 0x15c7: // (blue)
4595 element = EL_CHAR_1;
4598 case 0x15c8: // (blue)
4599 element = EL_CHAR_2;
4602 case 0x15c9: // (blue)
4603 element = EL_CHAR_3;
4606 case 0x15ca: // (blue)
4607 element = EL_CHAR_4;
4610 case 0x15cb: // (blue)
4611 element = EL_CHAR_5;
4614 case 0x15cc: // (blue)
4615 element = EL_CHAR_6;
4618 case 0x15cd: // (blue)
4619 element = EL_CHAR_7;
4622 case 0x15ce: // (blue)
4623 element = EL_CHAR_8;
4626 case 0x15cf: // (blue)
4627 element = EL_CHAR_9;
4630 case 0x15d0: // (blue)
4631 element = EL_CHAR_PERIOD;
4634 case 0x15d1: // (blue)
4635 element = EL_CHAR_EXCLAM;
4638 case 0x15d2: // (blue)
4639 element = EL_CHAR_COLON;
4642 case 0x15d3: // (blue)
4643 element = EL_CHAR_LESS;
4646 case 0x15d4: // (blue)
4647 element = EL_CHAR_GREATER;
4650 case 0x15d5: // (blue)
4651 element = EL_CHAR_QUESTION;
4654 case 0x15d6: // (blue)
4655 element = EL_CHAR_COPYRIGHT;
4658 case 0x15d7: // (blue)
4659 element = EL_CHAR_UP;
4662 case 0x15d8: // (blue)
4663 element = EL_CHAR_DOWN;
4666 case 0x15d9: // (blue)
4667 element = EL_CHAR_BUTTON;
4670 case 0x15da: // (blue)
4671 element = EL_CHAR_PLUS;
4674 case 0x15db: // (blue)
4675 element = EL_CHAR_MINUS;
4678 case 0x15dc: // (blue)
4679 element = EL_CHAR_APOSTROPHE;
4682 case 0x15dd: // (blue)
4683 element = EL_CHAR_PARENLEFT;
4686 case 0x15de: // (blue)
4687 element = EL_CHAR_PARENRIGHT;
4690 case 0x15df: // (green)
4691 element = EL_CHAR_A;
4694 case 0x15e0: // (green)
4695 element = EL_CHAR_B;
4698 case 0x15e1: // (green)
4699 element = EL_CHAR_C;
4702 case 0x15e2: // (green)
4703 element = EL_CHAR_D;
4706 case 0x15e3: // (green)
4707 element = EL_CHAR_E;
4710 case 0x15e4: // (green)
4711 element = EL_CHAR_F;
4714 case 0x15e5: // (green)
4715 element = EL_CHAR_G;
4718 case 0x15e6: // (green)
4719 element = EL_CHAR_H;
4722 case 0x15e7: // (green)
4723 element = EL_CHAR_I;
4726 case 0x15e8: // (green)
4727 element = EL_CHAR_J;
4730 case 0x15e9: // (green)
4731 element = EL_CHAR_K;
4734 case 0x15ea: // (green)
4735 element = EL_CHAR_L;
4738 case 0x15eb: // (green)
4739 element = EL_CHAR_M;
4742 case 0x15ec: // (green)
4743 element = EL_CHAR_N;
4746 case 0x15ed: // (green)
4747 element = EL_CHAR_O;
4750 case 0x15ee: // (green)
4751 element = EL_CHAR_P;
4754 case 0x15ef: // (green)
4755 element = EL_CHAR_Q;
4758 case 0x15f0: // (green)
4759 element = EL_CHAR_R;
4762 case 0x15f1: // (green)
4763 element = EL_CHAR_S;
4766 case 0x15f2: // (green)
4767 element = EL_CHAR_T;
4770 case 0x15f3: // (green)
4771 element = EL_CHAR_U;
4774 case 0x15f4: // (green)
4775 element = EL_CHAR_V;
4778 case 0x15f5: // (green)
4779 element = EL_CHAR_W;
4782 case 0x15f6: // (green)
4783 element = EL_CHAR_X;
4786 case 0x15f7: // (green)
4787 element = EL_CHAR_Y;
4790 case 0x15f8: // (green)
4791 element = EL_CHAR_Z;
4794 case 0x15f9: // (green)
4795 element = EL_CHAR_AUMLAUT;
4798 case 0x15fa: // (green)
4799 element = EL_CHAR_OUMLAUT;
4802 case 0x15fb: // (green)
4803 element = EL_CHAR_UUMLAUT;
4806 case 0x15fc: // (green)
4807 element = EL_CHAR_0;
4810 case 0x15fd: // (green)
4811 element = EL_CHAR_1;
4814 case 0x15fe: // (green)
4815 element = EL_CHAR_2;
4818 case 0x15ff: // (green)
4819 element = EL_CHAR_3;
4822 case 0x1600: // (green)
4823 element = EL_CHAR_4;
4826 case 0x1601: // (green)
4827 element = EL_CHAR_5;
4830 case 0x1602: // (green)
4831 element = EL_CHAR_6;
4834 case 0x1603: // (green)
4835 element = EL_CHAR_7;
4838 case 0x1604: // (green)
4839 element = EL_CHAR_8;
4842 case 0x1605: // (green)
4843 element = EL_CHAR_9;
4846 case 0x1606: // (green)
4847 element = EL_CHAR_PERIOD;
4850 case 0x1607: // (green)
4851 element = EL_CHAR_EXCLAM;
4854 case 0x1608: // (green)
4855 element = EL_CHAR_COLON;
4858 case 0x1609: // (green)
4859 element = EL_CHAR_LESS;
4862 case 0x160a: // (green)
4863 element = EL_CHAR_GREATER;
4866 case 0x160b: // (green)
4867 element = EL_CHAR_QUESTION;
4870 case 0x160c: // (green)
4871 element = EL_CHAR_COPYRIGHT;
4874 case 0x160d: // (green)
4875 element = EL_CHAR_UP;
4878 case 0x160e: // (green)
4879 element = EL_CHAR_DOWN;
4882 case 0x160f: // (green)
4883 element = EL_CHAR_BUTTON;
4886 case 0x1610: // (green)
4887 element = EL_CHAR_PLUS;
4890 case 0x1611: // (green)
4891 element = EL_CHAR_MINUS;
4894 case 0x1612: // (green)
4895 element = EL_CHAR_APOSTROPHE;
4898 case 0x1613: // (green)
4899 element = EL_CHAR_PARENLEFT;
4902 case 0x1614: // (green)
4903 element = EL_CHAR_PARENRIGHT;
4906 case 0x1615: // (blue steel)
4907 element = EL_STEEL_CHAR_A;
4910 case 0x1616: // (blue steel)
4911 element = EL_STEEL_CHAR_B;
4914 case 0x1617: // (blue steel)
4915 element = EL_STEEL_CHAR_C;
4918 case 0x1618: // (blue steel)
4919 element = EL_STEEL_CHAR_D;
4922 case 0x1619: // (blue steel)
4923 element = EL_STEEL_CHAR_E;
4926 case 0x161a: // (blue steel)
4927 element = EL_STEEL_CHAR_F;
4930 case 0x161b: // (blue steel)
4931 element = EL_STEEL_CHAR_G;
4934 case 0x161c: // (blue steel)
4935 element = EL_STEEL_CHAR_H;
4938 case 0x161d: // (blue steel)
4939 element = EL_STEEL_CHAR_I;
4942 case 0x161e: // (blue steel)
4943 element = EL_STEEL_CHAR_J;
4946 case 0x161f: // (blue steel)
4947 element = EL_STEEL_CHAR_K;
4950 case 0x1620: // (blue steel)
4951 element = EL_STEEL_CHAR_L;
4954 case 0x1621: // (blue steel)
4955 element = EL_STEEL_CHAR_M;
4958 case 0x1622: // (blue steel)
4959 element = EL_STEEL_CHAR_N;
4962 case 0x1623: // (blue steel)
4963 element = EL_STEEL_CHAR_O;
4966 case 0x1624: // (blue steel)
4967 element = EL_STEEL_CHAR_P;
4970 case 0x1625: // (blue steel)
4971 element = EL_STEEL_CHAR_Q;
4974 case 0x1626: // (blue steel)
4975 element = EL_STEEL_CHAR_R;
4978 case 0x1627: // (blue steel)
4979 element = EL_STEEL_CHAR_S;
4982 case 0x1628: // (blue steel)
4983 element = EL_STEEL_CHAR_T;
4986 case 0x1629: // (blue steel)
4987 element = EL_STEEL_CHAR_U;
4990 case 0x162a: // (blue steel)
4991 element = EL_STEEL_CHAR_V;
4994 case 0x162b: // (blue steel)
4995 element = EL_STEEL_CHAR_W;
4998 case 0x162c: // (blue steel)
4999 element = EL_STEEL_CHAR_X;
5002 case 0x162d: // (blue steel)
5003 element = EL_STEEL_CHAR_Y;
5006 case 0x162e: // (blue steel)
5007 element = EL_STEEL_CHAR_Z;
5010 case 0x162f: // (blue steel)
5011 element = EL_STEEL_CHAR_AUMLAUT;
5014 case 0x1630: // (blue steel)
5015 element = EL_STEEL_CHAR_OUMLAUT;
5018 case 0x1631: // (blue steel)
5019 element = EL_STEEL_CHAR_UUMLAUT;
5022 case 0x1632: // (blue steel)
5023 element = EL_STEEL_CHAR_0;
5026 case 0x1633: // (blue steel)
5027 element = EL_STEEL_CHAR_1;
5030 case 0x1634: // (blue steel)
5031 element = EL_STEEL_CHAR_2;
5034 case 0x1635: // (blue steel)
5035 element = EL_STEEL_CHAR_3;
5038 case 0x1636: // (blue steel)
5039 element = EL_STEEL_CHAR_4;
5042 case 0x1637: // (blue steel)
5043 element = EL_STEEL_CHAR_5;
5046 case 0x1638: // (blue steel)
5047 element = EL_STEEL_CHAR_6;
5050 case 0x1639: // (blue steel)
5051 element = EL_STEEL_CHAR_7;
5054 case 0x163a: // (blue steel)
5055 element = EL_STEEL_CHAR_8;
5058 case 0x163b: // (blue steel)
5059 element = EL_STEEL_CHAR_9;
5062 case 0x163c: // (blue steel)
5063 element = EL_STEEL_CHAR_PERIOD;
5066 case 0x163d: // (blue steel)
5067 element = EL_STEEL_CHAR_EXCLAM;
5070 case 0x163e: // (blue steel)
5071 element = EL_STEEL_CHAR_COLON;
5074 case 0x163f: // (blue steel)
5075 element = EL_STEEL_CHAR_LESS;
5078 case 0x1640: // (blue steel)
5079 element = EL_STEEL_CHAR_GREATER;
5082 case 0x1641: // (blue steel)
5083 element = EL_STEEL_CHAR_QUESTION;
5086 case 0x1642: // (blue steel)
5087 element = EL_STEEL_CHAR_COPYRIGHT;
5090 case 0x1643: // (blue steel)
5091 element = EL_STEEL_CHAR_UP;
5094 case 0x1644: // (blue steel)
5095 element = EL_STEEL_CHAR_DOWN;
5098 case 0x1645: // (blue steel)
5099 element = EL_STEEL_CHAR_BUTTON;
5102 case 0x1646: // (blue steel)
5103 element = EL_STEEL_CHAR_PLUS;
5106 case 0x1647: // (blue steel)
5107 element = EL_STEEL_CHAR_MINUS;
5110 case 0x1648: // (blue steel)
5111 element = EL_STEEL_CHAR_APOSTROPHE;
5114 case 0x1649: // (blue steel)
5115 element = EL_STEEL_CHAR_PARENLEFT;
5118 case 0x164a: // (blue steel)
5119 element = EL_STEEL_CHAR_PARENRIGHT;
5122 case 0x164b: // (green steel)
5123 element = EL_STEEL_CHAR_A;
5126 case 0x164c: // (green steel)
5127 element = EL_STEEL_CHAR_B;
5130 case 0x164d: // (green steel)
5131 element = EL_STEEL_CHAR_C;
5134 case 0x164e: // (green steel)
5135 element = EL_STEEL_CHAR_D;
5138 case 0x164f: // (green steel)
5139 element = EL_STEEL_CHAR_E;
5142 case 0x1650: // (green steel)
5143 element = EL_STEEL_CHAR_F;
5146 case 0x1651: // (green steel)
5147 element = EL_STEEL_CHAR_G;
5150 case 0x1652: // (green steel)
5151 element = EL_STEEL_CHAR_H;
5154 case 0x1653: // (green steel)
5155 element = EL_STEEL_CHAR_I;
5158 case 0x1654: // (green steel)
5159 element = EL_STEEL_CHAR_J;
5162 case 0x1655: // (green steel)
5163 element = EL_STEEL_CHAR_K;
5166 case 0x1656: // (green steel)
5167 element = EL_STEEL_CHAR_L;
5170 case 0x1657: // (green steel)
5171 element = EL_STEEL_CHAR_M;
5174 case 0x1658: // (green steel)
5175 element = EL_STEEL_CHAR_N;
5178 case 0x1659: // (green steel)
5179 element = EL_STEEL_CHAR_O;
5182 case 0x165a: // (green steel)
5183 element = EL_STEEL_CHAR_P;
5186 case 0x165b: // (green steel)
5187 element = EL_STEEL_CHAR_Q;
5190 case 0x165c: // (green steel)
5191 element = EL_STEEL_CHAR_R;
5194 case 0x165d: // (green steel)
5195 element = EL_STEEL_CHAR_S;
5198 case 0x165e: // (green steel)
5199 element = EL_STEEL_CHAR_T;
5202 case 0x165f: // (green steel)
5203 element = EL_STEEL_CHAR_U;
5206 case 0x1660: // (green steel)
5207 element = EL_STEEL_CHAR_V;
5210 case 0x1661: // (green steel)
5211 element = EL_STEEL_CHAR_W;
5214 case 0x1662: // (green steel)
5215 element = EL_STEEL_CHAR_X;
5218 case 0x1663: // (green steel)
5219 element = EL_STEEL_CHAR_Y;
5222 case 0x1664: // (green steel)
5223 element = EL_STEEL_CHAR_Z;
5226 case 0x1665: // (green steel)
5227 element = EL_STEEL_CHAR_AUMLAUT;
5230 case 0x1666: // (green steel)
5231 element = EL_STEEL_CHAR_OUMLAUT;
5234 case 0x1667: // (green steel)
5235 element = EL_STEEL_CHAR_UUMLAUT;
5238 case 0x1668: // (green steel)
5239 element = EL_STEEL_CHAR_0;
5242 case 0x1669: // (green steel)
5243 element = EL_STEEL_CHAR_1;
5246 case 0x166a: // (green steel)
5247 element = EL_STEEL_CHAR_2;
5250 case 0x166b: // (green steel)
5251 element = EL_STEEL_CHAR_3;
5254 case 0x166c: // (green steel)
5255 element = EL_STEEL_CHAR_4;
5258 case 0x166d: // (green steel)
5259 element = EL_STEEL_CHAR_5;
5262 case 0x166e: // (green steel)
5263 element = EL_STEEL_CHAR_6;
5266 case 0x166f: // (green steel)
5267 element = EL_STEEL_CHAR_7;
5270 case 0x1670: // (green steel)
5271 element = EL_STEEL_CHAR_8;
5274 case 0x1671: // (green steel)
5275 element = EL_STEEL_CHAR_9;
5278 case 0x1672: // (green steel)
5279 element = EL_STEEL_CHAR_PERIOD;
5282 case 0x1673: // (green steel)
5283 element = EL_STEEL_CHAR_EXCLAM;
5286 case 0x1674: // (green steel)
5287 element = EL_STEEL_CHAR_COLON;
5290 case 0x1675: // (green steel)
5291 element = EL_STEEL_CHAR_LESS;
5294 case 0x1676: // (green steel)
5295 element = EL_STEEL_CHAR_GREATER;
5298 case 0x1677: // (green steel)
5299 element = EL_STEEL_CHAR_QUESTION;
5302 case 0x1678: // (green steel)
5303 element = EL_STEEL_CHAR_COPYRIGHT;
5306 case 0x1679: // (green steel)
5307 element = EL_STEEL_CHAR_UP;
5310 case 0x167a: // (green steel)
5311 element = EL_STEEL_CHAR_DOWN;
5314 case 0x167b: // (green steel)
5315 element = EL_STEEL_CHAR_BUTTON;
5318 case 0x167c: // (green steel)
5319 element = EL_STEEL_CHAR_PLUS;
5322 case 0x167d: // (green steel)
5323 element = EL_STEEL_CHAR_MINUS;
5326 case 0x167e: // (green steel)
5327 element = EL_STEEL_CHAR_APOSTROPHE;
5330 case 0x167f: // (green steel)
5331 element = EL_STEEL_CHAR_PARENLEFT;
5334 case 0x1680: // (green steel)
5335 element = EL_STEEL_CHAR_PARENRIGHT;
5338 case 0x1681: // gate (red)
5339 element = EL_EM_GATE_1;
5342 case 0x1682: // secret gate (red)
5343 element = EL_EM_GATE_1_GRAY;
5346 case 0x1683: // gate (yellow)
5347 element = EL_EM_GATE_2;
5350 case 0x1684: // secret gate (yellow)
5351 element = EL_EM_GATE_2_GRAY;
5354 case 0x1685: // gate (blue)
5355 element = EL_EM_GATE_4;
5358 case 0x1686: // secret gate (blue)
5359 element = EL_EM_GATE_4_GRAY;
5362 case 0x1687: // gate (green)
5363 element = EL_EM_GATE_3;
5366 case 0x1688: // secret gate (green)
5367 element = EL_EM_GATE_3_GRAY;
5370 case 0x1689: // gate (white)
5371 element = EL_DC_GATE_WHITE;
5374 case 0x168a: // secret gate (white)
5375 element = EL_DC_GATE_WHITE_GRAY;
5378 case 0x168b: // secret gate (no key)
5379 element = EL_DC_GATE_FAKE_GRAY;
5383 element = EL_ROBOT_WHEEL;
5387 element = EL_DC_TIMEGATE_SWITCH;
5391 element = EL_ACID_POOL_BOTTOM;
5395 element = EL_ACID_POOL_TOPLEFT;
5399 element = EL_ACID_POOL_TOPRIGHT;
5403 element = EL_ACID_POOL_BOTTOMLEFT;
5407 element = EL_ACID_POOL_BOTTOMRIGHT;
5411 element = EL_STEELWALL;
5415 element = EL_STEELWALL_SLIPPERY;
5418 case 0x1695: // steel wall (not round)
5419 element = EL_STEELWALL;
5422 case 0x1696: // steel wall (left)
5423 element = EL_DC_STEELWALL_1_LEFT;
5426 case 0x1697: // steel wall (bottom)
5427 element = EL_DC_STEELWALL_1_BOTTOM;
5430 case 0x1698: // steel wall (right)
5431 element = EL_DC_STEELWALL_1_RIGHT;
5434 case 0x1699: // steel wall (top)
5435 element = EL_DC_STEELWALL_1_TOP;
5438 case 0x169a: // steel wall (left/bottom)
5439 element = EL_DC_STEELWALL_1_BOTTOMLEFT;
5442 case 0x169b: // steel wall (right/bottom)
5443 element = EL_DC_STEELWALL_1_BOTTOMRIGHT;
5446 case 0x169c: // steel wall (right/top)
5447 element = EL_DC_STEELWALL_1_TOPRIGHT;
5450 case 0x169d: // steel wall (left/top)
5451 element = EL_DC_STEELWALL_1_TOPLEFT;
5454 case 0x169e: // steel wall (right/bottom small)
5455 element = EL_DC_STEELWALL_1_BOTTOMRIGHT_2;
5458 case 0x169f: // steel wall (left/bottom small)
5459 element = EL_DC_STEELWALL_1_BOTTOMLEFT_2;
5462 case 0x16a0: // steel wall (right/top small)
5463 element = EL_DC_STEELWALL_1_TOPRIGHT_2;
5466 case 0x16a1: // steel wall (left/top small)
5467 element = EL_DC_STEELWALL_1_TOPLEFT_2;
5470 case 0x16a2: // steel wall (left/right)
5471 element = EL_DC_STEELWALL_1_VERTICAL;
5474 case 0x16a3: // steel wall (top/bottom)
5475 element = EL_DC_STEELWALL_1_HORIZONTAL;
5478 case 0x16a4: // steel wall 2 (left end)
5479 element = EL_DC_STEELWALL_2_LEFT;
5482 case 0x16a5: // steel wall 2 (right end)
5483 element = EL_DC_STEELWALL_2_RIGHT;
5486 case 0x16a6: // steel wall 2 (top end)
5487 element = EL_DC_STEELWALL_2_TOP;
5490 case 0x16a7: // steel wall 2 (bottom end)
5491 element = EL_DC_STEELWALL_2_BOTTOM;
5494 case 0x16a8: // steel wall 2 (left/right)
5495 element = EL_DC_STEELWALL_2_HORIZONTAL;
5498 case 0x16a9: // steel wall 2 (up/down)
5499 element = EL_DC_STEELWALL_2_VERTICAL;
5502 case 0x16aa: // steel wall 2 (mid)
5503 element = EL_DC_STEELWALL_2_MIDDLE;
5507 element = EL_SIGN_EXCLAMATION;
5511 element = EL_SIGN_RADIOACTIVITY;
5515 element = EL_SIGN_STOP;
5519 element = EL_SIGN_WHEELCHAIR;
5523 element = EL_SIGN_PARKING;
5527 element = EL_SIGN_NO_ENTRY;
5531 element = EL_SIGN_HEART;
5535 element = EL_SIGN_GIVE_WAY;
5539 element = EL_SIGN_ENTRY_FORBIDDEN;
5543 element = EL_SIGN_EMERGENCY_EXIT;
5547 element = EL_SIGN_YIN_YANG;
5551 element = EL_WALL_EMERALD;
5555 element = EL_WALL_DIAMOND;
5559 element = EL_WALL_PEARL;
5563 element = EL_WALL_CRYSTAL;
5567 element = EL_INVISIBLE_WALL;
5571 element = EL_INVISIBLE_STEELWALL;
5575 // EL_INVISIBLE_SAND
5578 element = EL_LIGHT_SWITCH;
5582 element = EL_ENVELOPE_1;
5586 if (element >= 0x0117 && element <= 0x036e) // (?)
5587 element = EL_DIAMOND;
5588 else if (element >= 0x042d && element <= 0x0684) // (?)
5589 element = EL_EMERALD;
5590 else if (element >= 0x157c && element <= 0x158b)
5592 else if (element >= 0x1590 && element <= 0x159f)
5593 element = EL_DC_LANDMINE;
5594 else if (element >= 0x16bc && element <= 0x16cb)
5595 element = EL_INVISIBLE_SAND;
5598 Warn("unknown Diamond Caves element 0x%04x", element);
5600 element = EL_UNKNOWN;
5605 return getMappedElement(element);
5608 static void LoadLevelFromFileStream_DC(File *file, struct LevelInfo *level,
5611 byte header[DC_LEVEL_HEADER_SIZE];
5613 int envelope_header_pos = 62;
5614 int envelope_content_pos = 94;
5615 int level_name_pos = 251;
5616 int level_author_pos = 292;
5617 int envelope_header_len;
5618 int envelope_content_len;
5620 int level_author_len;
5622 int num_yamyam_contents;
5625 getDecodedWord_DC(0, TRUE); // initialize DC2 decoding engine
5627 for (i = 0; i < DC_LEVEL_HEADER_SIZE / 2; i++)
5629 unsigned short header_word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
5631 header[i * 2 + 0] = header_word >> 8;
5632 header[i * 2 + 1] = header_word & 0xff;
5635 // read some values from level header to check level decoding integrity
5636 fieldx = header[6] | (header[7] << 8);
5637 fieldy = header[8] | (header[9] << 8);
5638 num_yamyam_contents = header[60] | (header[61] << 8);
5640 // do some simple sanity checks to ensure that level was correctly decoded
5641 if (fieldx < 1 || fieldx > 256 ||
5642 fieldy < 1 || fieldy > 256 ||
5643 num_yamyam_contents < 1 || num_yamyam_contents > 8)
5645 level->no_valid_file = TRUE;
5647 Warn("cannot decode level from stream -- using empty level");
5652 // maximum envelope header size is 31 bytes
5653 envelope_header_len = header[envelope_header_pos];
5654 // maximum envelope content size is 110 (156?) bytes
5655 envelope_content_len = header[envelope_content_pos];
5657 // maximum level title size is 40 bytes
5658 level_name_len = MIN(header[level_name_pos], MAX_LEVEL_NAME_LEN);
5659 // maximum level author size is 30 (51?) bytes
5660 level_author_len = MIN(header[level_author_pos], MAX_LEVEL_AUTHOR_LEN);
5664 for (i = 0; i < envelope_header_len; i++)
5665 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
5666 level->envelope[0].text[envelope_size++] =
5667 header[envelope_header_pos + 1 + i];
5669 if (envelope_header_len > 0 && envelope_content_len > 0)
5671 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
5672 level->envelope[0].text[envelope_size++] = '\n';
5673 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
5674 level->envelope[0].text[envelope_size++] = '\n';
5677 for (i = 0; i < envelope_content_len; i++)
5678 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
5679 level->envelope[0].text[envelope_size++] =
5680 header[envelope_content_pos + 1 + i];
5682 level->envelope[0].text[envelope_size] = '\0';
5684 level->envelope[0].xsize = MAX_ENVELOPE_XSIZE;
5685 level->envelope[0].ysize = 10;
5686 level->envelope[0].autowrap = TRUE;
5687 level->envelope[0].centered = TRUE;
5689 for (i = 0; i < level_name_len; i++)
5690 level->name[i] = header[level_name_pos + 1 + i];
5691 level->name[level_name_len] = '\0';
5693 for (i = 0; i < level_author_len; i++)
5694 level->author[i] = header[level_author_pos + 1 + i];
5695 level->author[level_author_len] = '\0';
5697 num_yamyam_contents = header[60] | (header[61] << 8);
5698 level->num_yamyam_contents =
5699 MIN(MAX(MIN_ELEMENT_CONTENTS, num_yamyam_contents), MAX_ELEMENT_CONTENTS);
5701 for (i = 0; i < num_yamyam_contents; i++)
5703 for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
5705 unsigned short word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
5706 int element_dc = ((word & 0xff) << 8) | ((word >> 8) & 0xff);
5708 if (i < MAX_ELEMENT_CONTENTS)
5709 level->yamyam_content[i].e[x][y] = getMappedElement_DC(element_dc);
5713 fieldx = header[6] | (header[7] << 8);
5714 fieldy = header[8] | (header[9] << 8);
5715 level->fieldx = MIN(MAX(MIN_LEV_FIELDX, fieldx), MAX_LEV_FIELDX);
5716 level->fieldy = MIN(MAX(MIN_LEV_FIELDY, fieldy), MAX_LEV_FIELDY);
5718 for (y = 0; y < fieldy; y++) for (x = 0; x < fieldx; x++)
5720 unsigned short word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
5721 int element_dc = ((word & 0xff) << 8) | ((word >> 8) & 0xff);
5723 if (x < MAX_LEV_FIELDX && y < MAX_LEV_FIELDY)
5724 level->field[x][y] = getMappedElement_DC(element_dc);
5727 x = MIN(MAX(0, (header[10] | (header[11] << 8)) - 1), MAX_LEV_FIELDX - 1);
5728 y = MIN(MAX(0, (header[12] | (header[13] << 8)) - 1), MAX_LEV_FIELDY - 1);
5729 level->field[x][y] = EL_PLAYER_1;
5731 x = MIN(MAX(0, (header[14] | (header[15] << 8)) - 1), MAX_LEV_FIELDX - 1);
5732 y = MIN(MAX(0, (header[16] | (header[17] << 8)) - 1), MAX_LEV_FIELDY - 1);
5733 level->field[x][y] = EL_PLAYER_2;
5735 level->gems_needed = header[18] | (header[19] << 8);
5737 level->score[SC_EMERALD] = header[20] | (header[21] << 8);
5738 level->score[SC_DIAMOND] = header[22] | (header[23] << 8);
5739 level->score[SC_PEARL] = header[24] | (header[25] << 8);
5740 level->score[SC_CRYSTAL] = header[26] | (header[27] << 8);
5741 level->score[SC_NUT] = header[28] | (header[29] << 8);
5742 level->score[SC_ROBOT] = header[30] | (header[31] << 8);
5743 level->score[SC_SPACESHIP] = header[32] | (header[33] << 8);
5744 level->score[SC_BUG] = header[34] | (header[35] << 8);
5745 level->score[SC_YAMYAM] = header[36] | (header[37] << 8);
5746 level->score[SC_DYNAMITE] = header[38] | (header[39] << 8);
5747 level->score[SC_KEY] = header[40] | (header[41] << 8);
5748 level->score[SC_TIME_BONUS] = header[42] | (header[43] << 8);
5750 level->time = header[44] | (header[45] << 8);
5752 level->amoeba_speed = header[46] | (header[47] << 8);
5753 level->time_light = header[48] | (header[49] << 8);
5754 level->time_timegate = header[50] | (header[51] << 8);
5755 level->time_wheel = header[52] | (header[53] << 8);
5756 level->time_magic_wall = header[54] | (header[55] << 8);
5757 level->extra_time = header[56] | (header[57] << 8);
5758 level->shield_normal_time = header[58] | (header[59] << 8);
5760 // shield and extra time elements do not have a score
5761 level->score[SC_SHIELD] = 0;
5762 level->extra_time_score = 0;
5764 // set time for normal and deadly shields to the same value
5765 level->shield_deadly_time = level->shield_normal_time;
5767 // Diamond Caves has the same (strange) behaviour as Emerald Mine that gems
5768 // can slip down from flat walls, like normal walls and steel walls
5769 level->em_slippery_gems = TRUE;
5771 // time score is counted for each 10 seconds left in Diamond Caves levels
5772 level->time_score_base = 10;
5775 static void LoadLevelFromFileInfo_DC(struct LevelInfo *level,
5776 struct LevelFileInfo *level_file_info,
5777 boolean level_info_only)
5779 char *filename = level_file_info->filename;
5781 int num_magic_bytes = 8;
5782 char magic_bytes[num_magic_bytes + 1];
5783 int num_levels_to_skip = level_file_info->nr - leveldir_current->first_level;
5785 if (!(file = openFile(filename, MODE_READ)))
5787 level->no_valid_file = TRUE;
5789 if (!level_info_only)
5790 Warn("cannot read level '%s' -- using empty level", filename);
5795 // fseek(file, 0x0000, SEEK_SET);
5797 if (level_file_info->packed)
5799 // read "magic bytes" from start of file
5800 if (getStringFromFile(file, magic_bytes, num_magic_bytes + 1) == NULL)
5801 magic_bytes[0] = '\0';
5803 // check "magic bytes" for correct file format
5804 if (!strPrefix(magic_bytes, "DC2"))
5806 level->no_valid_file = TRUE;
5808 Warn("unknown DC level file '%s' -- using empty level", filename);
5813 if (strPrefix(magic_bytes, "DC2Win95") ||
5814 strPrefix(magic_bytes, "DC2Win98"))
5816 int position_first_level = 0x00fa;
5817 int extra_bytes = 4;
5820 // advance file stream to first level inside the level package
5821 skip_bytes = position_first_level - num_magic_bytes - extra_bytes;
5823 // each block of level data is followed by block of non-level data
5824 num_levels_to_skip *= 2;
5826 // at least skip header bytes, therefore use ">= 0" instead of "> 0"
5827 while (num_levels_to_skip >= 0)
5829 // advance file stream to next level inside the level package
5830 if (seekFile(file, skip_bytes, SEEK_CUR) != 0)
5832 level->no_valid_file = TRUE;
5834 Warn("cannot fseek in file '%s' -- using empty level", filename);
5839 // skip apparently unused extra bytes following each level
5840 ReadUnusedBytesFromFile(file, extra_bytes);
5842 // read size of next level in level package
5843 skip_bytes = getFile32BitLE(file);
5845 num_levels_to_skip--;
5850 level->no_valid_file = TRUE;
5852 Warn("unknown DC2 level file '%s' -- using empty level", filename);
5858 LoadLevelFromFileStream_DC(file, level, level_file_info->nr);
5864 // ----------------------------------------------------------------------------
5865 // functions for loading SB level
5866 // ----------------------------------------------------------------------------
5868 int getMappedElement_SB(int element_ascii, boolean use_ces)
5876 sb_element_mapping[] =
5878 { ' ', EL_EMPTY, EL_CUSTOM_1 }, // floor (space)
5879 { '#', EL_STEELWALL, EL_CUSTOM_2 }, // wall
5880 { '@', EL_PLAYER_1, EL_CUSTOM_3 }, // player
5881 { '$', EL_SOKOBAN_OBJECT, EL_CUSTOM_4 }, // box
5882 { '.', EL_SOKOBAN_FIELD_EMPTY, EL_CUSTOM_5 }, // goal square
5883 { '*', EL_SOKOBAN_FIELD_FULL, EL_CUSTOM_6 }, // box on goal square
5884 { '+', EL_SOKOBAN_FIELD_PLAYER, EL_CUSTOM_7 }, // player on goal square
5885 { '_', EL_INVISIBLE_STEELWALL, EL_FROM_LEVEL_TEMPLATE }, // floor beyond border
5892 for (i = 0; sb_element_mapping[i].ascii != 0; i++)
5893 if (element_ascii == sb_element_mapping[i].ascii)
5894 return (use_ces ? sb_element_mapping[i].ce : sb_element_mapping[i].sb);
5896 return EL_UNDEFINED;
5899 static void SetLevelSettings_SB(struct LevelInfo *level)
5903 level->use_step_counter = TRUE;
5906 level->score[SC_TIME_BONUS] = 0;
5907 level->time_score_base = 1;
5908 level->rate_time_over_score = TRUE;
5911 level->auto_exit_sokoban = TRUE;
5914 static void LoadLevelFromFileInfo_SB(struct LevelInfo *level,
5915 struct LevelFileInfo *level_file_info,
5916 boolean level_info_only)
5918 char *filename = level_file_info->filename;
5919 char line[MAX_LINE_LEN], line_raw[MAX_LINE_LEN], previous_line[MAX_LINE_LEN];
5920 char last_comment[MAX_LINE_LEN];
5921 char level_name[MAX_LINE_LEN];
5924 int num_levels_to_skip = level_file_info->nr - leveldir_current->first_level;
5925 boolean read_continued_line = FALSE;
5926 boolean reading_playfield = FALSE;
5927 boolean got_valid_playfield_line = FALSE;
5928 boolean invalid_playfield_char = FALSE;
5929 boolean load_xsb_to_ces = check_special_flags("load_xsb_to_ces");
5930 int file_level_nr = 0;
5932 int x = 0, y = 0; // initialized to make compilers happy
5934 last_comment[0] = '\0';
5935 level_name[0] = '\0';
5937 if (!(file = openFile(filename, MODE_READ)))
5939 level->no_valid_file = TRUE;
5941 if (!level_info_only)
5942 Warn("cannot read level '%s' -- using empty level", filename);
5947 while (!checkEndOfFile(file))
5949 // level successfully read, but next level may follow here
5950 if (!got_valid_playfield_line && reading_playfield)
5952 // read playfield from single level file -- skip remaining file
5953 if (!level_file_info->packed)
5956 if (file_level_nr >= num_levels_to_skip)
5961 last_comment[0] = '\0';
5962 level_name[0] = '\0';
5964 reading_playfield = FALSE;
5967 got_valid_playfield_line = FALSE;
5969 // read next line of input file
5970 if (!getStringFromFile(file, line, MAX_LINE_LEN))
5973 // check if line was completely read and is terminated by line break
5974 if (strlen(line) > 0 && line[strlen(line) - 1] == '\n')
5977 // cut trailing line break (this can be newline and/or carriage return)
5978 for (line_ptr = &line[strlen(line)]; line_ptr >= line; line_ptr--)
5979 if ((*line_ptr == '\n' || *line_ptr == '\r') && *(line_ptr + 1) == '\0')
5982 // copy raw input line for later use (mainly debugging output)
5983 strcpy(line_raw, line);
5985 if (read_continued_line)
5987 // append new line to existing line, if there is enough space
5988 if (strlen(previous_line) + strlen(line_ptr) < MAX_LINE_LEN)
5989 strcat(previous_line, line_ptr);
5991 strcpy(line, previous_line); // copy storage buffer to line
5993 read_continued_line = FALSE;
5996 // if the last character is '\', continue at next line
5997 if (strlen(line) > 0 && line[strlen(line) - 1] == '\\')
5999 line[strlen(line) - 1] = '\0'; // cut off trailing backslash
6000 strcpy(previous_line, line); // copy line to storage buffer
6002 read_continued_line = TRUE;
6008 if (line[0] == '\0')
6011 // extract comment text from comment line
6014 for (line_ptr = line; *line_ptr; line_ptr++)
6015 if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != ';')
6018 strcpy(last_comment, line_ptr);
6023 // extract level title text from line containing level title
6024 if (line[0] == '\'')
6026 strcpy(level_name, &line[1]);
6028 if (strlen(level_name) > 0 && level_name[strlen(level_name) - 1] == '\'')
6029 level_name[strlen(level_name) - 1] = '\0';
6034 // skip lines containing only spaces (or empty lines)
6035 for (line_ptr = line; *line_ptr; line_ptr++)
6036 if (*line_ptr != ' ')
6038 if (*line_ptr == '\0')
6041 // at this point, we have found a line containing part of a playfield
6043 got_valid_playfield_line = TRUE;
6045 if (!reading_playfield)
6047 reading_playfield = TRUE;
6048 invalid_playfield_char = FALSE;
6050 for (x = 0; x < MAX_LEV_FIELDX; x++)
6051 for (y = 0; y < MAX_LEV_FIELDY; y++)
6052 level->field[x][y] = getMappedElement_SB(' ', load_xsb_to_ces);
6057 // start with topmost tile row
6061 // skip playfield line if larger row than allowed
6062 if (y >= MAX_LEV_FIELDY)
6065 // start with leftmost tile column
6068 // read playfield elements from line
6069 for (line_ptr = line; *line_ptr; line_ptr++)
6071 int mapped_sb_element = getMappedElement_SB(*line_ptr, load_xsb_to_ces);
6073 // stop parsing playfield line if larger column than allowed
6074 if (x >= MAX_LEV_FIELDX)
6077 if (mapped_sb_element == EL_UNDEFINED)
6079 invalid_playfield_char = TRUE;
6084 level->field[x][y] = mapped_sb_element;
6086 // continue with next tile column
6089 level->fieldx = MAX(x, level->fieldx);
6092 if (invalid_playfield_char)
6094 // if first playfield line, treat invalid lines as comment lines
6096 reading_playfield = FALSE;
6101 // continue with next tile row
6109 level->fieldx = MIN(MAX(MIN_LEV_FIELDX, level->fieldx), MAX_LEV_FIELDX);
6110 level->fieldy = MIN(MAX(MIN_LEV_FIELDY, level->fieldy), MAX_LEV_FIELDY);
6112 if (!reading_playfield)
6114 level->no_valid_file = TRUE;
6116 Warn("cannot read level '%s' -- using empty level", filename);
6121 if (*level_name != '\0')
6123 strncpy(level->name, level_name, MAX_LEVEL_NAME_LEN);
6124 level->name[MAX_LEVEL_NAME_LEN] = '\0';
6126 else if (*last_comment != '\0')
6128 strncpy(level->name, last_comment, MAX_LEVEL_NAME_LEN);
6129 level->name[MAX_LEVEL_NAME_LEN] = '\0';
6133 sprintf(level->name, "--> Level %d <--", level_file_info->nr);
6136 // set all empty fields beyond the border walls to invisible steel wall
6137 for (y = 0; y < level->fieldy; y++) for (x = 0; x < level->fieldx; x++)
6139 if ((x == 0 || x == level->fieldx - 1 ||
6140 y == 0 || y == level->fieldy - 1) &&
6141 level->field[x][y] == getMappedElement_SB(' ', load_xsb_to_ces))
6142 FloodFillLevel(x, y, getMappedElement_SB('_', load_xsb_to_ces),
6143 level->field, level->fieldx, level->fieldy);
6146 // set special level settings for Sokoban levels
6147 SetLevelSettings_SB(level);
6149 if (load_xsb_to_ces)
6151 // special global settings can now be set in level template
6152 level->use_custom_template = TRUE;
6157 // -------------------------------------------------------------------------
6158 // functions for handling native levels
6159 // -------------------------------------------------------------------------
6161 static void LoadLevelFromFileInfo_EM(struct LevelInfo *level,
6162 struct LevelFileInfo *level_file_info,
6163 boolean level_info_only)
6165 if (!LoadNativeLevel_EM(level_file_info->filename, level_info_only))
6166 level->no_valid_file = TRUE;
6169 static void LoadLevelFromFileInfo_SP(struct LevelInfo *level,
6170 struct LevelFileInfo *level_file_info,
6171 boolean level_info_only)
6175 // determine position of requested level inside level package
6176 if (level_file_info->packed)
6177 pos = level_file_info->nr - leveldir_current->first_level;
6179 if (!LoadNativeLevel_SP(level_file_info->filename, pos, level_info_only))
6180 level->no_valid_file = TRUE;
6183 static void LoadLevelFromFileInfo_MM(struct LevelInfo *level,
6184 struct LevelFileInfo *level_file_info,
6185 boolean level_info_only)
6187 if (!LoadNativeLevel_MM(level_file_info->filename, level_info_only))
6188 level->no_valid_file = TRUE;
6191 void CopyNativeLevel_RND_to_Native(struct LevelInfo *level)
6193 if (level->game_engine_type == GAME_ENGINE_TYPE_EM)
6194 CopyNativeLevel_RND_to_EM(level);
6195 else if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
6196 CopyNativeLevel_RND_to_SP(level);
6197 else if (level->game_engine_type == GAME_ENGINE_TYPE_MM)
6198 CopyNativeLevel_RND_to_MM(level);
6201 void CopyNativeLevel_Native_to_RND(struct LevelInfo *level)
6203 if (level->game_engine_type == GAME_ENGINE_TYPE_EM)
6204 CopyNativeLevel_EM_to_RND(level);
6205 else if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
6206 CopyNativeLevel_SP_to_RND(level);
6207 else if (level->game_engine_type == GAME_ENGINE_TYPE_MM)
6208 CopyNativeLevel_MM_to_RND(level);
6211 void SaveNativeLevel(struct LevelInfo *level)
6213 if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
6215 char *basename = getSingleLevelBasenameExt(level->file_info.nr, "sp");
6216 char *filename = getLevelFilenameFromBasename(basename);
6218 CopyNativeLevel_RND_to_SP(level);
6219 CopyNativeTape_RND_to_SP(level);
6221 SaveNativeLevel_SP(filename);
6226 // ----------------------------------------------------------------------------
6227 // functions for loading generic level
6228 // ----------------------------------------------------------------------------
6230 static void LoadLevelFromFileInfo(struct LevelInfo *level,
6231 struct LevelFileInfo *level_file_info,
6232 boolean level_info_only)
6234 // always start with reliable default values
6235 setLevelInfoToDefaults(level, level_info_only, TRUE);
6237 switch (level_file_info->type)
6239 case LEVEL_FILE_TYPE_RND:
6240 LoadLevelFromFileInfo_RND(level, level_file_info, level_info_only);
6243 case LEVEL_FILE_TYPE_EM:
6244 LoadLevelFromFileInfo_EM(level, level_file_info, level_info_only);
6245 level->game_engine_type = GAME_ENGINE_TYPE_EM;
6248 case LEVEL_FILE_TYPE_SP:
6249 LoadLevelFromFileInfo_SP(level, level_file_info, level_info_only);
6250 level->game_engine_type = GAME_ENGINE_TYPE_SP;
6253 case LEVEL_FILE_TYPE_MM:
6254 LoadLevelFromFileInfo_MM(level, level_file_info, level_info_only);
6255 level->game_engine_type = GAME_ENGINE_TYPE_MM;
6258 case LEVEL_FILE_TYPE_DC:
6259 LoadLevelFromFileInfo_DC(level, level_file_info, level_info_only);
6262 case LEVEL_FILE_TYPE_SB:
6263 LoadLevelFromFileInfo_SB(level, level_file_info, level_info_only);
6267 LoadLevelFromFileInfo_RND(level, level_file_info, level_info_only);
6271 // if level file is invalid, restore level structure to default values
6272 if (level->no_valid_file)
6273 setLevelInfoToDefaults(level, level_info_only, FALSE);
6275 if (level->game_engine_type == GAME_ENGINE_TYPE_UNKNOWN)
6276 level->game_engine_type = GAME_ENGINE_TYPE_RND;
6278 if (level_file_info->type != LEVEL_FILE_TYPE_RND)
6279 CopyNativeLevel_Native_to_RND(level);
6282 void LoadLevelFromFilename(struct LevelInfo *level, char *filename)
6284 static struct LevelFileInfo level_file_info;
6286 // always start with reliable default values
6287 setFileInfoToDefaults(&level_file_info);
6289 level_file_info.nr = 0; // unknown level number
6290 level_file_info.type = LEVEL_FILE_TYPE_RND; // no others supported yet
6292 setString(&level_file_info.filename, filename);
6294 LoadLevelFromFileInfo(level, &level_file_info, FALSE);
6297 static void LoadLevel_InitVersion(struct LevelInfo *level)
6301 if (leveldir_current == NULL) // only when dumping level
6304 // all engine modifications also valid for levels which use latest engine
6305 if (level->game_version < VERSION_IDENT(3,2,0,5))
6307 // time bonus score was given for 10 s instead of 1 s before 3.2.0-5
6308 level->time_score_base = 10;
6311 if (leveldir_current->latest_engine)
6313 // ---------- use latest game engine --------------------------------------
6315 /* For all levels which are forced to use the latest game engine version
6316 (normally all but user contributed, private and undefined levels), set
6317 the game engine version to the actual version; this allows for actual
6318 corrections in the game engine to take effect for existing, converted
6319 levels (from "classic" or other existing games) to make the emulation
6320 of the corresponding game more accurate, while (hopefully) not breaking
6321 existing levels created from other players. */
6323 level->game_version = GAME_VERSION_ACTUAL;
6325 /* Set special EM style gems behaviour: EM style gems slip down from
6326 normal, steel and growing wall. As this is a more fundamental change,
6327 it seems better to set the default behaviour to "off" (as it is more
6328 natural) and make it configurable in the level editor (as a property
6329 of gem style elements). Already existing converted levels (neither
6330 private nor contributed levels) are changed to the new behaviour. */
6332 if (level->file_version < FILE_VERSION_2_0)
6333 level->em_slippery_gems = TRUE;
6338 // ---------- use game engine the level was created with --------------------
6340 /* For all levels which are not forced to use the latest game engine
6341 version (normally user contributed, private and undefined levels),
6342 use the version of the game engine the levels were created for.
6344 Since 2.0.1, the game engine version is now directly stored
6345 in the level file (chunk "VERS"), so there is no need anymore
6346 to set the game version from the file version (except for old,
6347 pre-2.0 levels, where the game version is still taken from the
6348 file format version used to store the level -- see above). */
6350 // player was faster than enemies in 1.0.0 and before
6351 if (level->file_version == FILE_VERSION_1_0)
6352 for (i = 0; i < MAX_PLAYERS; i++)
6353 level->initial_player_stepsize[i] = STEPSIZE_FAST;
6355 // default behaviour for EM style gems was "slippery" only in 2.0.1
6356 if (level->game_version == VERSION_IDENT(2,0,1,0))
6357 level->em_slippery_gems = TRUE;
6359 // springs could be pushed over pits before (pre-release version) 2.2.0
6360 if (level->game_version < VERSION_IDENT(2,2,0,0))
6361 level->use_spring_bug = TRUE;
6363 if (level->game_version < VERSION_IDENT(3,2,0,5))
6365 // time orb caused limited time in endless time levels before 3.2.0-5
6366 level->use_time_orb_bug = TRUE;
6368 // default behaviour for snapping was "no snap delay" before 3.2.0-5
6369 level->block_snap_field = FALSE;
6371 // extra time score was same value as time left score before 3.2.0-5
6372 level->extra_time_score = level->score[SC_TIME_BONUS];
6375 if (level->game_version < VERSION_IDENT(3,2,0,7))
6377 // default behaviour for snapping was "not continuous" before 3.2.0-7
6378 level->continuous_snapping = FALSE;
6381 // only few elements were able to actively move into acid before 3.1.0
6382 // trigger settings did not exist before 3.1.0; set to default "any"
6383 if (level->game_version < VERSION_IDENT(3,1,0,0))
6385 // correct "can move into acid" settings (all zero in old levels)
6387 level->can_move_into_acid_bits = 0; // nothing can move into acid
6388 level->dont_collide_with_bits = 0; // nothing is deadly when colliding
6390 setMoveIntoAcidProperty(level, EL_ROBOT, TRUE);
6391 setMoveIntoAcidProperty(level, EL_SATELLITE, TRUE);
6392 setMoveIntoAcidProperty(level, EL_PENGUIN, TRUE);
6393 setMoveIntoAcidProperty(level, EL_BALLOON, TRUE);
6395 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6396 SET_PROPERTY(EL_CUSTOM_START + i, EP_CAN_MOVE_INTO_ACID, TRUE);
6398 // correct trigger settings (stored as zero == "none" in old levels)
6400 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6402 int element = EL_CUSTOM_START + i;
6403 struct ElementInfo *ei = &element_info[element];
6405 for (j = 0; j < ei->num_change_pages; j++)
6407 struct ElementChangeInfo *change = &ei->change_page[j];
6409 change->trigger_player = CH_PLAYER_ANY;
6410 change->trigger_page = CH_PAGE_ANY;
6415 // try to detect and fix "Snake Bite" levels, which are broken with 3.2.0
6417 int element = EL_CUSTOM_256;
6418 struct ElementInfo *ei = &element_info[element];
6419 struct ElementChangeInfo *change = &ei->change_page[0];
6421 /* This is needed to fix a problem that was caused by a bugfix in function
6422 game.c/CreateFieldExt() introduced with 3.2.0 that corrects the behaviour
6423 when a custom element changes to EL_SOKOBAN_FIELD_PLAYER (before, it did
6424 not replace walkable elements, but instead just placed the player on it,
6425 without placing the Sokoban field under the player). Unfortunately, this
6426 breaks "Snake Bite" style levels when the snake is halfway through a door
6427 that just closes (the snake head is still alive and can be moved in this
6428 case). This can be fixed by replacing the EL_SOKOBAN_FIELD_PLAYER by the
6429 player (without Sokoban element) which then gets killed as designed). */
6431 if ((strncmp(leveldir_current->identifier, "snake_bite", 10) == 0 ||
6432 strncmp(ei->description, "pause b4 death", 14) == 0) &&
6433 change->target_element == EL_SOKOBAN_FIELD_PLAYER)
6434 change->target_element = EL_PLAYER_1;
6437 // try to detect and fix "Zelda" style levels, which are broken with 3.2.5
6438 if (level->game_version < VERSION_IDENT(3,2,5,0))
6440 /* This is needed to fix a problem that was caused by a bugfix in function
6441 game.c/CheckTriggeredElementChangeExt() introduced with 3.2.5 that
6442 corrects the behaviour when a custom element changes to another custom
6443 element with a higher element number that has change actions defined.
6444 Normally, only one change per frame is allowed for custom elements.
6445 Therefore, it is checked if a custom element already changed in the
6446 current frame; if it did, subsequent changes are suppressed.
6447 Unfortunately, this is only checked for element changes, but not for
6448 change actions, which are still executed. As the function above loops
6449 through all custom elements from lower to higher, an element change
6450 resulting in a lower CE number won't be checked again, while a target
6451 element with a higher number will also be checked, and potential change
6452 actions will get executed for this CE, too (which is wrong), while
6453 further changes are ignored (which is correct). As this bugfix breaks
6454 Zelda II (and introduces graphical bugs to Zelda I, and also breaks a
6455 few other levels like Alan Bond's "FMV"), allow the previous, incorrect
6456 behaviour for existing levels and tapes that make use of this bug */
6458 level->use_action_after_change_bug = TRUE;
6461 // not centering level after relocating player was default only in 3.2.3
6462 if (level->game_version == VERSION_IDENT(3,2,3,0)) // (no pre-releases)
6463 level->shifted_relocation = TRUE;
6465 // EM style elements always chain-exploded in R'n'D engine before 3.2.6
6466 if (level->game_version < VERSION_IDENT(3,2,6,0))
6467 level->em_explodes_by_fire = TRUE;
6469 // levels were solved by the first player entering an exit up to 4.1.0.0
6470 if (level->game_version <= VERSION_IDENT(4,1,0,0))
6471 level->solved_by_one_player = TRUE;
6473 // game logic of "game of life" and "biomaze" was buggy before 4.1.1.1
6474 if (level->game_version < VERSION_IDENT(4,1,1,1))
6475 level->use_life_bugs = TRUE;
6477 // only Sokoban fields (but not objects) had to be solved before 4.1.1.1
6478 if (level->game_version < VERSION_IDENT(4,1,1,1))
6479 level->sb_objects_needed = FALSE;
6481 // CE actions were triggered by unfinished digging/collecting up to 4.2.2.0
6482 if (level->game_version <= VERSION_IDENT(4,2,2,0))
6483 level->finish_dig_collect = FALSE;
6485 // CE changing to player was kept under the player if walkable up to 4.2.3.1
6486 if (level->game_version <= VERSION_IDENT(4,2,3,1))
6487 level->keep_walkable_ce = TRUE;
6490 static void LoadLevel_InitSettings_SB(struct LevelInfo *level)
6492 boolean is_sokoban_level = TRUE; // unless non-Sokoban elements found
6495 // check if this level is (not) a Sokoban level
6496 for (y = 0; y < level->fieldy; y++)
6497 for (x = 0; x < level->fieldx; x++)
6498 if (!IS_SB_ELEMENT(Tile[x][y]))
6499 is_sokoban_level = FALSE;
6501 if (is_sokoban_level)
6503 // set special level settings for Sokoban levels
6504 SetLevelSettings_SB(level);
6508 static void LoadLevel_InitSettings(struct LevelInfo *level)
6510 // adjust level settings for (non-native) Sokoban-style levels
6511 LoadLevel_InitSettings_SB(level);
6514 static void LoadLevel_InitStandardElements(struct LevelInfo *level)
6518 // map elements that have changed in newer versions
6519 level->amoeba_content = getMappedElementByVersion(level->amoeba_content,
6520 level->game_version);
6521 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
6522 for (x = 0; x < 3; x++)
6523 for (y = 0; y < 3; y++)
6524 level->yamyam_content[i].e[x][y] =
6525 getMappedElementByVersion(level->yamyam_content[i].e[x][y],
6526 level->game_version);
6530 static void LoadLevel_InitCustomElements(struct LevelInfo *level)
6534 // map custom element change events that have changed in newer versions
6535 // (these following values were accidentally changed in version 3.0.1)
6536 // (this seems to be needed only for 'ab_levelset3' and 'ab_levelset4')
6537 if (level->game_version <= VERSION_IDENT(3,0,0,0))
6539 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6541 int element = EL_CUSTOM_START + i;
6543 // order of checking and copying events to be mapped is important
6544 // (do not change the start and end value -- they are constant)
6545 for (j = CE_BY_OTHER_ACTION; j >= CE_VALUE_GETS_ZERO; j--)
6547 if (HAS_CHANGE_EVENT(element, j - 2))
6549 SET_CHANGE_EVENT(element, j - 2, FALSE);
6550 SET_CHANGE_EVENT(element, j, TRUE);
6554 // order of checking and copying events to be mapped is important
6555 // (do not change the start and end value -- they are constant)
6556 for (j = CE_PLAYER_COLLECTS_X; j >= CE_HITTING_SOMETHING; j--)
6558 if (HAS_CHANGE_EVENT(element, j - 1))
6560 SET_CHANGE_EVENT(element, j - 1, FALSE);
6561 SET_CHANGE_EVENT(element, j, TRUE);
6567 // initialize "can_change" field for old levels with only one change page
6568 if (level->game_version <= VERSION_IDENT(3,0,2,0))
6570 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6572 int element = EL_CUSTOM_START + i;
6574 if (CAN_CHANGE(element))
6575 element_info[element].change->can_change = TRUE;
6579 // correct custom element values (for old levels without these options)
6580 if (level->game_version < VERSION_IDENT(3,1,1,0))
6582 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6584 int element = EL_CUSTOM_START + i;
6585 struct ElementInfo *ei = &element_info[element];
6587 if (ei->access_direction == MV_NO_DIRECTION)
6588 ei->access_direction = MV_ALL_DIRECTIONS;
6592 // correct custom element values (fix invalid values for all versions)
6595 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6597 int element = EL_CUSTOM_START + i;
6598 struct ElementInfo *ei = &element_info[element];
6600 for (j = 0; j < ei->num_change_pages; j++)
6602 struct ElementChangeInfo *change = &ei->change_page[j];
6604 if (change->trigger_player == CH_PLAYER_NONE)
6605 change->trigger_player = CH_PLAYER_ANY;
6607 if (change->trigger_side == CH_SIDE_NONE)
6608 change->trigger_side = CH_SIDE_ANY;
6613 // initialize "can_explode" field for old levels which did not store this
6614 // !!! CHECK THIS -- "<= 3,1,0,0" IS PROBABLY WRONG !!!
6615 if (level->game_version <= VERSION_IDENT(3,1,0,0))
6617 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6619 int element = EL_CUSTOM_START + i;
6621 if (EXPLODES_1X1_OLD(element))
6622 element_info[element].explosion_type = EXPLODES_1X1;
6624 SET_PROPERTY(element, EP_CAN_EXPLODE, (EXPLODES_BY_FIRE(element) ||
6625 EXPLODES_SMASHED(element) ||
6626 EXPLODES_IMPACT(element)));
6630 // correct previously hard-coded move delay values for maze runner style
6631 if (level->game_version < VERSION_IDENT(3,1,1,0))
6633 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6635 int element = EL_CUSTOM_START + i;
6637 if (element_info[element].move_pattern & MV_MAZE_RUNNER_STYLE)
6639 // previously hard-coded and therefore ignored
6640 element_info[element].move_delay_fixed = 9;
6641 element_info[element].move_delay_random = 0;
6646 // set some other uninitialized values of custom elements in older levels
6647 if (level->game_version < VERSION_IDENT(3,1,0,0))
6649 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6651 int element = EL_CUSTOM_START + i;
6653 element_info[element].access_direction = MV_ALL_DIRECTIONS;
6655 element_info[element].explosion_delay = 17;
6656 element_info[element].ignition_delay = 8;
6660 // set mouse click change events to work for left/middle/right mouse button
6661 if (level->game_version < VERSION_IDENT(4,2,3,0))
6663 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6665 int element = EL_CUSTOM_START + i;
6666 struct ElementInfo *ei = &element_info[element];
6668 for (j = 0; j < ei->num_change_pages; j++)
6670 struct ElementChangeInfo *change = &ei->change_page[j];
6672 if (change->has_event[CE_CLICKED_BY_MOUSE] ||
6673 change->has_event[CE_PRESSED_BY_MOUSE] ||
6674 change->has_event[CE_MOUSE_CLICKED_ON_X] ||
6675 change->has_event[CE_MOUSE_PRESSED_ON_X])
6676 change->trigger_side = CH_SIDE_ANY;
6682 static void LoadLevel_InitElements(struct LevelInfo *level)
6684 LoadLevel_InitStandardElements(level);
6686 if (level->file_has_custom_elements)
6687 LoadLevel_InitCustomElements(level);
6689 // initialize element properties for level editor etc.
6690 InitElementPropertiesEngine(level->game_version);
6691 InitElementPropertiesGfxElement();
6694 static void LoadLevel_InitPlayfield(struct LevelInfo *level)
6698 // map elements that have changed in newer versions
6699 for (y = 0; y < level->fieldy; y++)
6700 for (x = 0; x < level->fieldx; x++)
6701 level->field[x][y] = getMappedElementByVersion(level->field[x][y],
6702 level->game_version);
6704 // clear unused playfield data (nicer if level gets resized in editor)
6705 for (x = 0; x < MAX_LEV_FIELDX; x++)
6706 for (y = 0; y < MAX_LEV_FIELDY; y++)
6707 if (x >= level->fieldx || y >= level->fieldy)
6708 level->field[x][y] = EL_EMPTY;
6710 // copy elements to runtime playfield array
6711 for (x = 0; x < MAX_LEV_FIELDX; x++)
6712 for (y = 0; y < MAX_LEV_FIELDY; y++)
6713 Tile[x][y] = level->field[x][y];
6715 // initialize level size variables for faster access
6716 lev_fieldx = level->fieldx;
6717 lev_fieldy = level->fieldy;
6719 // determine border element for this level
6720 if (level->file_info.type == LEVEL_FILE_TYPE_DC)
6721 BorderElement = EL_EMPTY; // (in editor, SetBorderElement() is used)
6726 static void LoadLevel_InitNativeEngines(struct LevelInfo *level)
6728 struct LevelFileInfo *level_file_info = &level->file_info;
6730 if (level_file_info->type == LEVEL_FILE_TYPE_RND)
6731 CopyNativeLevel_RND_to_Native(level);
6734 static void LoadLevelTemplate_LoadAndInit(void)
6736 LoadLevelFromFileInfo(&level_template, &level_template.file_info, FALSE);
6738 LoadLevel_InitVersion(&level_template);
6739 LoadLevel_InitElements(&level_template);
6740 LoadLevel_InitSettings(&level_template);
6742 ActivateLevelTemplate();
6745 void LoadLevelTemplate(int nr)
6747 if (!fileExists(getGlobalLevelTemplateFilename()))
6749 Warn("no level template found for this level");
6754 setLevelFileInfo(&level_template.file_info, nr);
6756 LoadLevelTemplate_LoadAndInit();
6759 static void LoadNetworkLevelTemplate(struct NetworkLevelInfo *network_level)
6761 copyLevelFileInfo(&network_level->tmpl_info, &level_template.file_info);
6763 LoadLevelTemplate_LoadAndInit();
6766 static void LoadLevel_LoadAndInit(struct NetworkLevelInfo *network_level)
6768 LoadLevelFromFileInfo(&level, &level.file_info, FALSE);
6770 if (level.use_custom_template)
6772 if (network_level != NULL)
6773 LoadNetworkLevelTemplate(network_level);
6775 LoadLevelTemplate(-1);
6778 LoadLevel_InitVersion(&level);
6779 LoadLevel_InitElements(&level);
6780 LoadLevel_InitPlayfield(&level);
6781 LoadLevel_InitSettings(&level);
6783 LoadLevel_InitNativeEngines(&level);
6786 void LoadLevel(int nr)
6788 SetLevelSetInfo(leveldir_current->identifier, nr);
6790 setLevelFileInfo(&level.file_info, nr);
6792 LoadLevel_LoadAndInit(NULL);
6795 void LoadLevelInfoOnly(int nr)
6797 setLevelFileInfo(&level.file_info, nr);
6799 LoadLevelFromFileInfo(&level, &level.file_info, TRUE);
6802 void LoadNetworkLevel(struct NetworkLevelInfo *network_level)
6804 SetLevelSetInfo(network_level->leveldir_identifier,
6805 network_level->file_info.nr);
6807 copyLevelFileInfo(&network_level->file_info, &level.file_info);
6809 LoadLevel_LoadAndInit(network_level);
6812 static int SaveLevel_VERS(FILE *file, struct LevelInfo *level)
6816 chunk_size += putFileVersion(file, level->file_version);
6817 chunk_size += putFileVersion(file, level->game_version);
6822 static int SaveLevel_DATE(FILE *file, struct LevelInfo *level)
6826 chunk_size += putFile16BitBE(file, level->creation_date.year);
6827 chunk_size += putFile8Bit(file, level->creation_date.month);
6828 chunk_size += putFile8Bit(file, level->creation_date.day);
6833 #if ENABLE_HISTORIC_CHUNKS
6834 static void SaveLevel_HEAD(FILE *file, struct LevelInfo *level)
6838 putFile8Bit(file, level->fieldx);
6839 putFile8Bit(file, level->fieldy);
6841 putFile16BitBE(file, level->time);
6842 putFile16BitBE(file, level->gems_needed);
6844 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
6845 putFile8Bit(file, level->name[i]);
6847 for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
6848 putFile8Bit(file, level->score[i]);
6850 for (i = 0; i < STD_ELEMENT_CONTENTS; i++)
6851 for (y = 0; y < 3; y++)
6852 for (x = 0; x < 3; x++)
6853 putFile8Bit(file, (level->encoding_16bit_yamyam ? EL_EMPTY :
6854 level->yamyam_content[i].e[x][y]));
6855 putFile8Bit(file, level->amoeba_speed);
6856 putFile8Bit(file, level->time_magic_wall);
6857 putFile8Bit(file, level->time_wheel);
6858 putFile8Bit(file, (level->encoding_16bit_amoeba ? EL_EMPTY :
6859 level->amoeba_content));
6860 putFile8Bit(file, (level->initial_player_stepsize == STEPSIZE_FAST ? 1 : 0));
6861 putFile8Bit(file, (level->initial_gravity ? 1 : 0));
6862 putFile8Bit(file, (level->encoding_16bit_field ? 1 : 0));
6863 putFile8Bit(file, (level->em_slippery_gems ? 1 : 0));
6865 putFile8Bit(file, (level->use_custom_template ? 1 : 0));
6867 putFile8Bit(file, (level->block_last_field ? 1 : 0));
6868 putFile8Bit(file, (level->sp_block_last_field ? 1 : 0));
6869 putFile32BitBE(file, level->can_move_into_acid_bits);
6870 putFile8Bit(file, level->dont_collide_with_bits);
6872 putFile8Bit(file, (level->use_spring_bug ? 1 : 0));
6873 putFile8Bit(file, (level->use_step_counter ? 1 : 0));
6875 putFile8Bit(file, (level->instant_relocation ? 1 : 0));
6876 putFile8Bit(file, (level->can_pass_to_walkable ? 1 : 0));
6877 putFile8Bit(file, (level->grow_into_diggable ? 1 : 0));
6879 putFile8Bit(file, level->game_engine_type);
6881 WriteUnusedBytesToFile(file, LEVEL_CHUNK_HEAD_UNUSED);
6885 static int SaveLevel_NAME(FILE *file, struct LevelInfo *level)
6890 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
6891 chunk_size += putFile8Bit(file, level->name[i]);
6896 static int SaveLevel_AUTH(FILE *file, struct LevelInfo *level)
6901 for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
6902 chunk_size += putFile8Bit(file, level->author[i]);
6907 #if ENABLE_HISTORIC_CHUNKS
6908 static int SaveLevel_BODY(FILE *file, struct LevelInfo *level)
6913 for (y = 0; y < level->fieldy; y++)
6914 for (x = 0; x < level->fieldx; x++)
6915 if (level->encoding_16bit_field)
6916 chunk_size += putFile16BitBE(file, level->field[x][y]);
6918 chunk_size += putFile8Bit(file, level->field[x][y]);
6924 static int SaveLevel_BODY(FILE *file, struct LevelInfo *level)
6929 for (y = 0; y < level->fieldy; y++)
6930 for (x = 0; x < level->fieldx; x++)
6931 chunk_size += putFile16BitBE(file, level->field[x][y]);
6936 #if ENABLE_HISTORIC_CHUNKS
6937 static void SaveLevel_CONT(FILE *file, struct LevelInfo *level)
6941 putFile8Bit(file, EL_YAMYAM);
6942 putFile8Bit(file, level->num_yamyam_contents);
6943 putFile8Bit(file, 0);
6944 putFile8Bit(file, 0);
6946 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
6947 for (y = 0; y < 3; y++)
6948 for (x = 0; x < 3; x++)
6949 if (level->encoding_16bit_field)
6950 putFile16BitBE(file, level->yamyam_content[i].e[x][y]);
6952 putFile8Bit(file, level->yamyam_content[i].e[x][y]);
6956 #if ENABLE_HISTORIC_CHUNKS
6957 static void SaveLevel_CNT2(FILE *file, struct LevelInfo *level, int element)
6960 int num_contents, content_xsize, content_ysize;
6961 int content_array[MAX_ELEMENT_CONTENTS][3][3];
6963 if (element == EL_YAMYAM)
6965 num_contents = level->num_yamyam_contents;
6969 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
6970 for (y = 0; y < 3; y++)
6971 for (x = 0; x < 3; x++)
6972 content_array[i][x][y] = level->yamyam_content[i].e[x][y];
6974 else if (element == EL_BD_AMOEBA)
6980 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
6981 for (y = 0; y < 3; y++)
6982 for (x = 0; x < 3; x++)
6983 content_array[i][x][y] = EL_EMPTY;
6984 content_array[0][0][0] = level->amoeba_content;
6988 // chunk header already written -- write empty chunk data
6989 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_SIZE);
6991 Warn("cannot save content for element '%d'", element);
6996 putFile16BitBE(file, element);
6997 putFile8Bit(file, num_contents);
6998 putFile8Bit(file, content_xsize);
6999 putFile8Bit(file, content_ysize);
7001 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_UNUSED);
7003 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
7004 for (y = 0; y < 3; y++)
7005 for (x = 0; x < 3; x++)
7006 putFile16BitBE(file, content_array[i][x][y]);
7010 #if ENABLE_HISTORIC_CHUNKS
7011 static int SaveLevel_CNT3(FILE *file, struct LevelInfo *level, int element)
7013 int envelope_nr = element - EL_ENVELOPE_1;
7014 int envelope_len = strlen(level->envelope_text[envelope_nr]) + 1;
7018 chunk_size += putFile16BitBE(file, element);
7019 chunk_size += putFile16BitBE(file, envelope_len);
7020 chunk_size += putFile8Bit(file, level->envelope_xsize[envelope_nr]);
7021 chunk_size += putFile8Bit(file, level->envelope_ysize[envelope_nr]);
7023 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT3_UNUSED);
7024 chunk_size += LEVEL_CHUNK_CNT3_UNUSED;
7026 for (i = 0; i < envelope_len; i++)
7027 chunk_size += putFile8Bit(file, level->envelope_text[envelope_nr][i]);
7033 #if ENABLE_HISTORIC_CHUNKS
7034 static void SaveLevel_CUS1(FILE *file, struct LevelInfo *level,
7035 int num_changed_custom_elements)
7039 putFile16BitBE(file, num_changed_custom_elements);
7041 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7043 int element = EL_CUSTOM_START + i;
7045 struct ElementInfo *ei = &element_info[element];
7047 if (ei->properties[EP_BITFIELD_BASE_NR] != EP_BITMASK_DEFAULT)
7049 if (check < num_changed_custom_elements)
7051 putFile16BitBE(file, element);
7052 putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
7059 if (check != num_changed_custom_elements) // should not happen
7060 Warn("inconsistent number of custom element properties");
7064 #if ENABLE_HISTORIC_CHUNKS
7065 static void SaveLevel_CUS2(FILE *file, struct LevelInfo *level,
7066 int num_changed_custom_elements)
7070 putFile16BitBE(file, num_changed_custom_elements);
7072 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7074 int element = EL_CUSTOM_START + i;
7076 if (element_info[element].change->target_element != EL_EMPTY_SPACE)
7078 if (check < num_changed_custom_elements)
7080 putFile16BitBE(file, element);
7081 putFile16BitBE(file, element_info[element].change->target_element);
7088 if (check != num_changed_custom_elements) // should not happen
7089 Warn("inconsistent number of custom target elements");
7093 #if ENABLE_HISTORIC_CHUNKS
7094 static void SaveLevel_CUS3(FILE *file, struct LevelInfo *level,
7095 int num_changed_custom_elements)
7097 int i, j, x, y, check = 0;
7099 putFile16BitBE(file, num_changed_custom_elements);
7101 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7103 int element = EL_CUSTOM_START + i;
7104 struct ElementInfo *ei = &element_info[element];
7106 if (ei->modified_settings)
7108 if (check < num_changed_custom_elements)
7110 putFile16BitBE(file, element);
7112 for (j = 0; j < MAX_ELEMENT_NAME_LEN; j++)
7113 putFile8Bit(file, ei->description[j]);
7115 putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
7117 // some free bytes for future properties and padding
7118 WriteUnusedBytesToFile(file, 7);
7120 putFile8Bit(file, ei->use_gfx_element);
7121 putFile16BitBE(file, ei->gfx_element_initial);
7123 putFile8Bit(file, ei->collect_score_initial);
7124 putFile8Bit(file, ei->collect_count_initial);
7126 putFile16BitBE(file, ei->push_delay_fixed);
7127 putFile16BitBE(file, ei->push_delay_random);
7128 putFile16BitBE(file, ei->move_delay_fixed);
7129 putFile16BitBE(file, ei->move_delay_random);
7131 putFile16BitBE(file, ei->move_pattern);
7132 putFile8Bit(file, ei->move_direction_initial);
7133 putFile8Bit(file, ei->move_stepsize);
7135 for (y = 0; y < 3; y++)
7136 for (x = 0; x < 3; x++)
7137 putFile16BitBE(file, ei->content.e[x][y]);
7139 putFile32BitBE(file, ei->change->events);
7141 putFile16BitBE(file, ei->change->target_element);
7143 putFile16BitBE(file, ei->change->delay_fixed);
7144 putFile16BitBE(file, ei->change->delay_random);
7145 putFile16BitBE(file, ei->change->delay_frames);
7147 putFile16BitBE(file, ei->change->initial_trigger_element);
7149 putFile8Bit(file, ei->change->explode);
7150 putFile8Bit(file, ei->change->use_target_content);
7151 putFile8Bit(file, ei->change->only_if_complete);
7152 putFile8Bit(file, ei->change->use_random_replace);
7154 putFile8Bit(file, ei->change->random_percentage);
7155 putFile8Bit(file, ei->change->replace_when);
7157 for (y = 0; y < 3; y++)
7158 for (x = 0; x < 3; x++)
7159 putFile16BitBE(file, ei->change->content.e[x][y]);
7161 putFile8Bit(file, ei->slippery_type);
7163 // some free bytes for future properties and padding
7164 WriteUnusedBytesToFile(file, LEVEL_CPART_CUS3_UNUSED);
7171 if (check != num_changed_custom_elements) // should not happen
7172 Warn("inconsistent number of custom element properties");
7176 #if ENABLE_HISTORIC_CHUNKS
7177 static void SaveLevel_CUS4(FILE *file, struct LevelInfo *level, int element)
7179 struct ElementInfo *ei = &element_info[element];
7182 // ---------- custom element base property values (96 bytes) ----------------
7184 putFile16BitBE(file, element);
7186 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
7187 putFile8Bit(file, ei->description[i]);
7189 putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
7191 WriteUnusedBytesToFile(file, 4); // reserved for more base properties
7193 putFile8Bit(file, ei->num_change_pages);
7195 putFile16BitBE(file, ei->ce_value_fixed_initial);
7196 putFile16BitBE(file, ei->ce_value_random_initial);
7197 putFile8Bit(file, ei->use_last_ce_value);
7199 putFile8Bit(file, ei->use_gfx_element);
7200 putFile16BitBE(file, ei->gfx_element_initial);
7202 putFile8Bit(file, ei->collect_score_initial);
7203 putFile8Bit(file, ei->collect_count_initial);
7205 putFile8Bit(file, ei->drop_delay_fixed);
7206 putFile8Bit(file, ei->push_delay_fixed);
7207 putFile8Bit(file, ei->drop_delay_random);
7208 putFile8Bit(file, ei->push_delay_random);
7209 putFile16BitBE(file, ei->move_delay_fixed);
7210 putFile16BitBE(file, ei->move_delay_random);
7212 // bits 0 - 15 of "move_pattern" ...
7213 putFile16BitBE(file, ei->move_pattern & 0xffff);
7214 putFile8Bit(file, ei->move_direction_initial);
7215 putFile8Bit(file, ei->move_stepsize);
7217 putFile8Bit(file, ei->slippery_type);
7219 for (y = 0; y < 3; y++)
7220 for (x = 0; x < 3; x++)
7221 putFile16BitBE(file, ei->content.e[x][y]);
7223 putFile16BitBE(file, ei->move_enter_element);
7224 putFile16BitBE(file, ei->move_leave_element);
7225 putFile8Bit(file, ei->move_leave_type);
7227 // ... bits 16 - 31 of "move_pattern" (not nice, but downward compatible)
7228 putFile16BitBE(file, (ei->move_pattern >> 16) & 0xffff);
7230 putFile8Bit(file, ei->access_direction);
7232 putFile8Bit(file, ei->explosion_delay);
7233 putFile8Bit(file, ei->ignition_delay);
7234 putFile8Bit(file, ei->explosion_type);
7236 // some free bytes for future custom property values and padding
7237 WriteUnusedBytesToFile(file, 1);
7239 // ---------- change page property values (48 bytes) ------------------------
7241 for (i = 0; i < ei->num_change_pages; i++)
7243 struct ElementChangeInfo *change = &ei->change_page[i];
7244 unsigned int event_bits;
7246 // bits 0 - 31 of "has_event[]" ...
7248 for (j = 0; j < MIN(NUM_CHANGE_EVENTS, 32); j++)
7249 if (change->has_event[j])
7250 event_bits |= (1 << j);
7251 putFile32BitBE(file, event_bits);
7253 putFile16BitBE(file, change->target_element);
7255 putFile16BitBE(file, change->delay_fixed);
7256 putFile16BitBE(file, change->delay_random);
7257 putFile16BitBE(file, change->delay_frames);
7259 putFile16BitBE(file, change->initial_trigger_element);
7261 putFile8Bit(file, change->explode);
7262 putFile8Bit(file, change->use_target_content);
7263 putFile8Bit(file, change->only_if_complete);
7264 putFile8Bit(file, change->use_random_replace);
7266 putFile8Bit(file, change->random_percentage);
7267 putFile8Bit(file, change->replace_when);
7269 for (y = 0; y < 3; y++)
7270 for (x = 0; x < 3; x++)
7271 putFile16BitBE(file, change->target_content.e[x][y]);
7273 putFile8Bit(file, change->can_change);
7275 putFile8Bit(file, change->trigger_side);
7277 putFile8Bit(file, change->trigger_player);
7278 putFile8Bit(file, (change->trigger_page == CH_PAGE_ANY ? CH_PAGE_ANY_FILE :
7279 log_2(change->trigger_page)));
7281 putFile8Bit(file, change->has_action);
7282 putFile8Bit(file, change->action_type);
7283 putFile8Bit(file, change->action_mode);
7284 putFile16BitBE(file, change->action_arg);
7286 // ... bits 32 - 39 of "has_event[]" (not nice, but downward compatible)
7288 for (j = 32; j < NUM_CHANGE_EVENTS; j++)
7289 if (change->has_event[j])
7290 event_bits |= (1 << (j - 32));
7291 putFile8Bit(file, event_bits);
7296 #if ENABLE_HISTORIC_CHUNKS
7297 static void SaveLevel_GRP1(FILE *file, struct LevelInfo *level, int element)
7299 struct ElementInfo *ei = &element_info[element];
7300 struct ElementGroupInfo *group = ei->group;
7303 putFile16BitBE(file, element);
7305 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
7306 putFile8Bit(file, ei->description[i]);
7308 putFile8Bit(file, group->num_elements);
7310 putFile8Bit(file, ei->use_gfx_element);
7311 putFile16BitBE(file, ei->gfx_element_initial);
7313 putFile8Bit(file, group->choice_mode);
7315 // some free bytes for future values and padding
7316 WriteUnusedBytesToFile(file, 3);
7318 for (i = 0; i < MAX_ELEMENTS_IN_GROUP; i++)
7319 putFile16BitBE(file, group->element[i]);
7323 static int SaveLevel_MicroChunk(FILE *file, struct LevelFileConfigInfo *entry,
7324 boolean write_element)
7326 int save_type = entry->save_type;
7327 int data_type = entry->data_type;
7328 int conf_type = entry->conf_type;
7329 int byte_mask = conf_type & CONF_MASK_BYTES;
7330 int element = entry->element;
7331 int default_value = entry->default_value;
7333 boolean modified = FALSE;
7335 if (byte_mask != CONF_MASK_MULTI_BYTES)
7337 void *value_ptr = entry->value;
7338 int value = (data_type == TYPE_BOOLEAN ? *(boolean *)value_ptr :
7341 // check if any settings have been modified before saving them
7342 if (value != default_value)
7345 // do not save if explicitly told or if unmodified default settings
7346 if ((save_type == SAVE_CONF_NEVER) ||
7347 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
7351 num_bytes += putFile16BitBE(file, element);
7353 num_bytes += putFile8Bit(file, conf_type);
7354 num_bytes += (byte_mask == CONF_MASK_1_BYTE ? putFile8Bit (file, value) :
7355 byte_mask == CONF_MASK_2_BYTE ? putFile16BitBE(file, value) :
7356 byte_mask == CONF_MASK_4_BYTE ? putFile32BitBE(file, value) :
7359 else if (data_type == TYPE_STRING)
7361 char *default_string = entry->default_string;
7362 char *string = (char *)(entry->value);
7363 int string_length = strlen(string);
7366 // check if any settings have been modified before saving them
7367 if (!strEqual(string, default_string))
7370 // do not save if explicitly told or if unmodified default settings
7371 if ((save_type == SAVE_CONF_NEVER) ||
7372 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
7376 num_bytes += putFile16BitBE(file, element);
7378 num_bytes += putFile8Bit(file, conf_type);
7379 num_bytes += putFile16BitBE(file, string_length);
7381 for (i = 0; i < string_length; i++)
7382 num_bytes += putFile8Bit(file, string[i]);
7384 else if (data_type == TYPE_ELEMENT_LIST)
7386 int *element_array = (int *)(entry->value);
7387 int num_elements = *(int *)(entry->num_entities);
7390 // check if any settings have been modified before saving them
7391 for (i = 0; i < num_elements; i++)
7392 if (element_array[i] != default_value)
7395 // do not save if explicitly told or if unmodified default settings
7396 if ((save_type == SAVE_CONF_NEVER) ||
7397 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
7401 num_bytes += putFile16BitBE(file, element);
7403 num_bytes += putFile8Bit(file, conf_type);
7404 num_bytes += putFile16BitBE(file, num_elements * CONF_ELEMENT_NUM_BYTES);
7406 for (i = 0; i < num_elements; i++)
7407 num_bytes += putFile16BitBE(file, element_array[i]);
7409 else if (data_type == TYPE_CONTENT_LIST)
7411 struct Content *content = (struct Content *)(entry->value);
7412 int num_contents = *(int *)(entry->num_entities);
7415 // check if any settings have been modified before saving them
7416 for (i = 0; i < num_contents; i++)
7417 for (y = 0; y < 3; y++)
7418 for (x = 0; x < 3; x++)
7419 if (content[i].e[x][y] != default_value)
7422 // do not save if explicitly told or if unmodified default settings
7423 if ((save_type == SAVE_CONF_NEVER) ||
7424 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
7428 num_bytes += putFile16BitBE(file, element);
7430 num_bytes += putFile8Bit(file, conf_type);
7431 num_bytes += putFile16BitBE(file, num_contents * CONF_CONTENT_NUM_BYTES);
7433 for (i = 0; i < num_contents; i++)
7434 for (y = 0; y < 3; y++)
7435 for (x = 0; x < 3; x++)
7436 num_bytes += putFile16BitBE(file, content[i].e[x][y]);
7442 static int SaveLevel_INFO(FILE *file, struct LevelInfo *level)
7447 li = *level; // copy level data into temporary buffer
7449 for (i = 0; chunk_config_INFO[i].data_type != -1; i++)
7450 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_INFO[i], FALSE);
7455 static int SaveLevel_ELEM(FILE *file, struct LevelInfo *level)
7460 li = *level; // copy level data into temporary buffer
7462 for (i = 0; chunk_config_ELEM[i].data_type != -1; i++)
7463 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_ELEM[i], TRUE);
7468 static int SaveLevel_NOTE(FILE *file, struct LevelInfo *level, int element)
7470 int envelope_nr = element - EL_ENVELOPE_1;
7474 chunk_size += putFile16BitBE(file, element);
7476 // copy envelope data into temporary buffer
7477 xx_envelope = level->envelope[envelope_nr];
7479 for (i = 0; chunk_config_NOTE[i].data_type != -1; i++)
7480 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_NOTE[i], FALSE);
7485 static int SaveLevel_CUSX(FILE *file, struct LevelInfo *level, int element)
7487 struct ElementInfo *ei = &element_info[element];
7491 chunk_size += putFile16BitBE(file, element);
7493 xx_ei = *ei; // copy element data into temporary buffer
7495 // set default description string for this specific element
7496 strcpy(xx_default_description, getDefaultElementDescription(ei));
7498 for (i = 0; chunk_config_CUSX_base[i].data_type != -1; i++)
7499 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_CUSX_base[i], FALSE);
7501 for (i = 0; i < ei->num_change_pages; i++)
7503 struct ElementChangeInfo *change = &ei->change_page[i];
7505 xx_current_change_page = i;
7507 xx_change = *change; // copy change data into temporary buffer
7510 setEventBitsFromEventFlags(change);
7512 for (j = 0; chunk_config_CUSX_change[j].data_type != -1; j++)
7513 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_CUSX_change[j],
7520 static int SaveLevel_GRPX(FILE *file, struct LevelInfo *level, int element)
7522 struct ElementInfo *ei = &element_info[element];
7523 struct ElementGroupInfo *group = ei->group;
7527 chunk_size += putFile16BitBE(file, element);
7529 xx_ei = *ei; // copy element data into temporary buffer
7530 xx_group = *group; // copy group data into temporary buffer
7532 // set default description string for this specific element
7533 strcpy(xx_default_description, getDefaultElementDescription(ei));
7535 for (i = 0; chunk_config_GRPX[i].data_type != -1; i++)
7536 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_GRPX[i], FALSE);
7541 static void SaveLevelFromFilename(struct LevelInfo *level, char *filename,
7542 boolean save_as_template)
7548 if (!(file = fopen(filename, MODE_WRITE)))
7550 Warn("cannot save level file '%s'", filename);
7555 level->file_version = FILE_VERSION_ACTUAL;
7556 level->game_version = GAME_VERSION_ACTUAL;
7558 level->creation_date = getCurrentDate();
7560 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
7561 putFileChunkBE(file, "CAVE", CHUNK_SIZE_NONE);
7563 chunk_size = SaveLevel_VERS(NULL, level);
7564 putFileChunkBE(file, "VERS", chunk_size);
7565 SaveLevel_VERS(file, level);
7567 chunk_size = SaveLevel_DATE(NULL, level);
7568 putFileChunkBE(file, "DATE", chunk_size);
7569 SaveLevel_DATE(file, level);
7571 chunk_size = SaveLevel_NAME(NULL, level);
7572 putFileChunkBE(file, "NAME", chunk_size);
7573 SaveLevel_NAME(file, level);
7575 chunk_size = SaveLevel_AUTH(NULL, level);
7576 putFileChunkBE(file, "AUTH", chunk_size);
7577 SaveLevel_AUTH(file, level);
7579 chunk_size = SaveLevel_INFO(NULL, level);
7580 putFileChunkBE(file, "INFO", chunk_size);
7581 SaveLevel_INFO(file, level);
7583 chunk_size = SaveLevel_BODY(NULL, level);
7584 putFileChunkBE(file, "BODY", chunk_size);
7585 SaveLevel_BODY(file, level);
7587 chunk_size = SaveLevel_ELEM(NULL, level);
7588 if (chunk_size > LEVEL_CHUNK_ELEM_UNCHANGED) // save if changed
7590 putFileChunkBE(file, "ELEM", chunk_size);
7591 SaveLevel_ELEM(file, level);
7594 for (i = 0; i < NUM_ENVELOPES; i++)
7596 int element = EL_ENVELOPE_1 + i;
7598 chunk_size = SaveLevel_NOTE(NULL, level, element);
7599 if (chunk_size > LEVEL_CHUNK_NOTE_UNCHANGED) // save if changed
7601 putFileChunkBE(file, "NOTE", chunk_size);
7602 SaveLevel_NOTE(file, level, element);
7606 // if not using template level, check for non-default custom/group elements
7607 if (!level->use_custom_template || save_as_template)
7609 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7611 int element = EL_CUSTOM_START + i;
7613 chunk_size = SaveLevel_CUSX(NULL, level, element);
7614 if (chunk_size > LEVEL_CHUNK_CUSX_UNCHANGED) // save if changed
7616 putFileChunkBE(file, "CUSX", chunk_size);
7617 SaveLevel_CUSX(file, level, element);
7621 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
7623 int element = EL_GROUP_START + i;
7625 chunk_size = SaveLevel_GRPX(NULL, level, element);
7626 if (chunk_size > LEVEL_CHUNK_GRPX_UNCHANGED) // save if changed
7628 putFileChunkBE(file, "GRPX", chunk_size);
7629 SaveLevel_GRPX(file, level, element);
7636 SetFilePermissions(filename, PERMS_PRIVATE);
7639 void SaveLevel(int nr)
7641 char *filename = getDefaultLevelFilename(nr);
7643 SaveLevelFromFilename(&level, filename, FALSE);
7646 void SaveLevelTemplate(void)
7648 char *filename = getLocalLevelTemplateFilename();
7650 SaveLevelFromFilename(&level, filename, TRUE);
7653 boolean SaveLevelChecked(int nr)
7655 char *filename = getDefaultLevelFilename(nr);
7656 boolean new_level = !fileExists(filename);
7657 boolean level_saved = FALSE;
7659 if (new_level || Request("Save this level and kill the old?", REQ_ASK))
7664 Request("Level saved!", REQ_CONFIRM);
7672 void DumpLevel(struct LevelInfo *level)
7674 if (level->no_level_file || level->no_valid_file)
7676 Warn("cannot dump -- no valid level file found");
7682 Print("Level xxx (file version %08d, game version %08d)\n",
7683 level->file_version, level->game_version);
7686 Print("Level author: '%s'\n", level->author);
7687 Print("Level title: '%s'\n", level->name);
7689 Print("Playfield size: %d x %d\n", level->fieldx, level->fieldy);
7691 Print("Level time: %d seconds\n", level->time);
7692 Print("Gems needed: %d\n", level->gems_needed);
7694 Print("Time for magic wall: %d seconds\n", level->time_magic_wall);
7695 Print("Time for wheel: %d seconds\n", level->time_wheel);
7696 Print("Time for light: %d seconds\n", level->time_light);
7697 Print("Time for timegate: %d seconds\n", level->time_timegate);
7699 Print("Amoeba speed: %d\n", level->amoeba_speed);
7702 Print("EM style slippery gems: %s\n", (level->em_slippery_gems ? "yes" : "no"));
7703 Print("Player blocks last field: %s\n", (level->block_last_field ? "yes" : "no"));
7704 Print("SP player blocks last field: %s\n", (level->sp_block_last_field ? "yes" : "no"));
7705 Print("use spring bug: %s\n", (level->use_spring_bug ? "yes" : "no"));
7706 Print("use step counter: %s\n", (level->use_step_counter ? "yes" : "no"));
7707 Print("rate time over score: %s\n", (level->rate_time_over_score ? "yes" : "no"));
7712 void DumpLevels(void)
7714 static LevelDirTree *dumplevel_leveldir = NULL;
7716 dumplevel_leveldir = getTreeInfoFromIdentifier(leveldir_first,
7717 global.dumplevel_leveldir);
7719 if (dumplevel_leveldir == NULL)
7720 Fail("no such level identifier: '%s'", global.dumplevel_leveldir);
7722 if (global.dumplevel_level_nr < dumplevel_leveldir->first_level ||
7723 global.dumplevel_level_nr > dumplevel_leveldir->last_level)
7724 Fail("no such level number: %d", global.dumplevel_level_nr);
7726 leveldir_current = dumplevel_leveldir;
7728 LoadLevel(global.dumplevel_level_nr);
7735 // ============================================================================
7736 // tape file functions
7737 // ============================================================================
7739 static void setTapeInfoToDefaults(void)
7743 // always start with reliable default values (empty tape)
7746 // default values (also for pre-1.2 tapes) with only the first player
7747 tape.player_participates[0] = TRUE;
7748 for (i = 1; i < MAX_PLAYERS; i++)
7749 tape.player_participates[i] = FALSE;
7751 // at least one (default: the first) player participates in every tape
7752 tape.num_participating_players = 1;
7754 tape.property_bits = TAPE_PROPERTY_NONE;
7756 tape.level_nr = level_nr;
7758 tape.changed = FALSE;
7760 tape.recording = FALSE;
7761 tape.playing = FALSE;
7762 tape.pausing = FALSE;
7764 tape.scr_fieldx = SCR_FIELDX_DEFAULT;
7765 tape.scr_fieldy = SCR_FIELDY_DEFAULT;
7767 tape.no_info_chunk = TRUE;
7768 tape.no_valid_file = FALSE;
7771 static int getTapePosSize(struct TapeInfo *tape)
7773 int tape_pos_size = 0;
7775 if (tape->use_key_actions)
7776 tape_pos_size += tape->num_participating_players;
7778 if (tape->use_mouse_actions)
7779 tape_pos_size += 3; // x and y position and mouse button mask
7781 tape_pos_size += 1; // tape action delay value
7783 return tape_pos_size;
7786 static void setTapeActionFlags(struct TapeInfo *tape, int value)
7788 tape->use_key_actions = FALSE;
7789 tape->use_mouse_actions = FALSE;
7791 if (value != TAPE_USE_MOUSE_ACTIONS_ONLY)
7792 tape->use_key_actions = TRUE;
7794 if (value != TAPE_USE_KEY_ACTIONS_ONLY)
7795 tape->use_mouse_actions = TRUE;
7798 static int getTapeActionValue(struct TapeInfo *tape)
7800 return (tape->use_key_actions &&
7801 tape->use_mouse_actions ? TAPE_USE_KEY_AND_MOUSE_ACTIONS :
7802 tape->use_key_actions ? TAPE_USE_KEY_ACTIONS_ONLY :
7803 tape->use_mouse_actions ? TAPE_USE_MOUSE_ACTIONS_ONLY :
7804 TAPE_ACTIONS_DEFAULT);
7807 static int LoadTape_VERS(File *file, int chunk_size, struct TapeInfo *tape)
7809 tape->file_version = getFileVersion(file);
7810 tape->game_version = getFileVersion(file);
7815 static int LoadTape_HEAD(File *file, int chunk_size, struct TapeInfo *tape)
7819 tape->random_seed = getFile32BitBE(file);
7820 tape->date = getFile32BitBE(file);
7821 tape->length = getFile32BitBE(file);
7823 // read header fields that are new since version 1.2
7824 if (tape->file_version >= FILE_VERSION_1_2)
7826 byte store_participating_players = getFile8Bit(file);
7829 // since version 1.2, tapes store which players participate in the tape
7830 tape->num_participating_players = 0;
7831 for (i = 0; i < MAX_PLAYERS; i++)
7833 tape->player_participates[i] = FALSE;
7835 if (store_participating_players & (1 << i))
7837 tape->player_participates[i] = TRUE;
7838 tape->num_participating_players++;
7842 setTapeActionFlags(tape, getFile8Bit(file));
7844 tape->property_bits = getFile8Bit(file);
7846 ReadUnusedBytesFromFile(file, TAPE_CHUNK_HEAD_UNUSED);
7848 engine_version = getFileVersion(file);
7849 if (engine_version > 0)
7850 tape->engine_version = engine_version;
7852 tape->engine_version = tape->game_version;
7858 static int LoadTape_SCRN(File *file, int chunk_size, struct TapeInfo *tape)
7860 tape->scr_fieldx = getFile8Bit(file);
7861 tape->scr_fieldy = getFile8Bit(file);
7866 static int LoadTape_INFO(File *file, int chunk_size, struct TapeInfo *tape)
7868 char *level_identifier = NULL;
7869 int level_identifier_size;
7872 tape->no_info_chunk = FALSE;
7874 level_identifier_size = getFile16BitBE(file);
7876 level_identifier = checked_malloc(level_identifier_size);
7878 for (i = 0; i < level_identifier_size; i++)
7879 level_identifier[i] = getFile8Bit(file);
7881 strncpy(tape->level_identifier, level_identifier, MAX_FILENAME_LEN);
7882 tape->level_identifier[MAX_FILENAME_LEN] = '\0';
7884 checked_free(level_identifier);
7886 tape->level_nr = getFile16BitBE(file);
7888 chunk_size = 2 + level_identifier_size + 2;
7893 static int LoadTape_BODY(File *file, int chunk_size, struct TapeInfo *tape)
7896 int tape_pos_size = getTapePosSize(tape);
7897 int chunk_size_expected = tape_pos_size * tape->length;
7899 if (chunk_size_expected != chunk_size)
7901 ReadUnusedBytesFromFile(file, chunk_size);
7902 return chunk_size_expected;
7905 for (i = 0; i < tape->length; i++)
7907 if (i >= MAX_TAPE_LEN)
7909 Warn("tape truncated -- size exceeds maximum tape size %d",
7912 // tape too large; read and ignore remaining tape data from this chunk
7913 for (;i < tape->length; i++)
7914 ReadUnusedBytesFromFile(file, tape_pos_size);
7919 if (tape->use_key_actions)
7921 for (j = 0; j < MAX_PLAYERS; j++)
7923 tape->pos[i].action[j] = MV_NONE;
7925 if (tape->player_participates[j])
7926 tape->pos[i].action[j] = getFile8Bit(file);
7930 if (tape->use_mouse_actions)
7932 tape->pos[i].action[TAPE_ACTION_LX] = getFile8Bit(file);
7933 tape->pos[i].action[TAPE_ACTION_LY] = getFile8Bit(file);
7934 tape->pos[i].action[TAPE_ACTION_BUTTON] = getFile8Bit(file);
7937 tape->pos[i].delay = getFile8Bit(file);
7939 if (tape->file_version == FILE_VERSION_1_0)
7941 // eliminate possible diagonal moves in old tapes
7942 // this is only for backward compatibility
7944 byte joy_dir[4] = { JOY_LEFT, JOY_RIGHT, JOY_UP, JOY_DOWN };
7945 byte action = tape->pos[i].action[0];
7946 int k, num_moves = 0;
7948 for (k = 0; k<4; k++)
7950 if (action & joy_dir[k])
7952 tape->pos[i + num_moves].action[0] = joy_dir[k];
7954 tape->pos[i + num_moves].delay = 0;
7963 tape->length += num_moves;
7966 else if (tape->file_version < FILE_VERSION_2_0)
7968 // convert pre-2.0 tapes to new tape format
7970 if (tape->pos[i].delay > 1)
7973 tape->pos[i + 1] = tape->pos[i];
7974 tape->pos[i + 1].delay = 1;
7977 for (j = 0; j < MAX_PLAYERS; j++)
7978 tape->pos[i].action[j] = MV_NONE;
7979 tape->pos[i].delay--;
7986 if (checkEndOfFile(file))
7990 if (i != tape->length)
7991 chunk_size = tape_pos_size * i;
7996 static void LoadTape_SokobanSolution(char *filename)
7999 int move_delay = TILESIZE / level.initial_player_stepsize[0];
8001 if (!(file = openFile(filename, MODE_READ)))
8003 tape.no_valid_file = TRUE;
8008 while (!checkEndOfFile(file))
8010 unsigned char c = getByteFromFile(file);
8012 if (checkEndOfFile(file))
8019 tape.pos[tape.length].action[0] = MV_UP;
8020 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
8026 tape.pos[tape.length].action[0] = MV_DOWN;
8027 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
8033 tape.pos[tape.length].action[0] = MV_LEFT;
8034 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
8040 tape.pos[tape.length].action[0] = MV_RIGHT;
8041 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
8049 // ignore white-space characters
8053 tape.no_valid_file = TRUE;
8055 Warn("unsupported Sokoban solution file '%s' ['%d']", filename, c);
8063 if (tape.no_valid_file)
8066 tape.length_frames = GetTapeLengthFrames();
8067 tape.length_seconds = GetTapeLengthSeconds();
8070 void LoadTapeFromFilename(char *filename)
8072 char cookie[MAX_LINE_LEN];
8073 char chunk_name[CHUNK_ID_LEN + 1];
8077 // always start with reliable default values
8078 setTapeInfoToDefaults();
8080 if (strSuffix(filename, ".sln"))
8082 LoadTape_SokobanSolution(filename);
8087 if (!(file = openFile(filename, MODE_READ)))
8089 tape.no_valid_file = TRUE;
8094 getFileChunkBE(file, chunk_name, NULL);
8095 if (strEqual(chunk_name, "RND1"))
8097 getFile32BitBE(file); // not used
8099 getFileChunkBE(file, chunk_name, NULL);
8100 if (!strEqual(chunk_name, "TAPE"))
8102 tape.no_valid_file = TRUE;
8104 Warn("unknown format of tape file '%s'", filename);
8111 else // check for pre-2.0 file format with cookie string
8113 strcpy(cookie, chunk_name);
8114 if (getStringFromFile(file, &cookie[4], MAX_LINE_LEN - 4) == NULL)
8116 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
8117 cookie[strlen(cookie) - 1] = '\0';
8119 if (!checkCookieString(cookie, TAPE_COOKIE_TMPL))
8121 tape.no_valid_file = TRUE;
8123 Warn("unknown format of tape file '%s'", filename);
8130 if ((tape.file_version = getFileVersionFromCookieString(cookie)) == -1)
8132 tape.no_valid_file = TRUE;
8134 Warn("unsupported version of tape file '%s'", filename);
8141 // pre-2.0 tape files have no game version, so use file version here
8142 tape.game_version = tape.file_version;
8145 if (tape.file_version < FILE_VERSION_1_2)
8147 // tape files from versions before 1.2.0 without chunk structure
8148 LoadTape_HEAD(file, TAPE_CHUNK_HEAD_SIZE, &tape);
8149 LoadTape_BODY(file, 2 * tape.length, &tape);
8157 int (*loader)(File *, int, struct TapeInfo *);
8161 { "VERS", TAPE_CHUNK_VERS_SIZE, LoadTape_VERS },
8162 { "HEAD", TAPE_CHUNK_HEAD_SIZE, LoadTape_HEAD },
8163 { "SCRN", TAPE_CHUNK_SCRN_SIZE, LoadTape_SCRN },
8164 { "INFO", -1, LoadTape_INFO },
8165 { "BODY", -1, LoadTape_BODY },
8169 while (getFileChunkBE(file, chunk_name, &chunk_size))
8173 while (chunk_info[i].name != NULL &&
8174 !strEqual(chunk_name, chunk_info[i].name))
8177 if (chunk_info[i].name == NULL)
8179 Warn("unknown chunk '%s' in tape file '%s'",
8180 chunk_name, filename);
8182 ReadUnusedBytesFromFile(file, chunk_size);
8184 else if (chunk_info[i].size != -1 &&
8185 chunk_info[i].size != chunk_size)
8187 Warn("wrong size (%d) of chunk '%s' in tape file '%s'",
8188 chunk_size, chunk_name, filename);
8190 ReadUnusedBytesFromFile(file, chunk_size);
8194 // call function to load this tape chunk
8195 int chunk_size_expected =
8196 (chunk_info[i].loader)(file, chunk_size, &tape);
8198 // the size of some chunks cannot be checked before reading other
8199 // chunks first (like "HEAD" and "BODY") that contain some header
8200 // information, so check them here
8201 if (chunk_size_expected != chunk_size)
8203 Warn("wrong size (%d) of chunk '%s' in tape file '%s'",
8204 chunk_size, chunk_name, filename);
8212 tape.length_frames = GetTapeLengthFrames();
8213 tape.length_seconds = GetTapeLengthSeconds();
8216 Debug("files:LoadTapeFromFilename", "tape file version: %d",
8218 Debug("files:LoadTapeFromFilename", "tape game version: %d",
8220 Debug("files:LoadTapeFromFilename", "tape engine version: %d",
8221 tape.engine_version);
8225 void LoadTape(int nr)
8227 char *filename = getTapeFilename(nr);
8229 LoadTapeFromFilename(filename);
8232 void LoadSolutionTape(int nr)
8234 char *filename = getSolutionTapeFilename(nr);
8236 LoadTapeFromFilename(filename);
8238 if (TAPE_IS_EMPTY(tape) &&
8239 level.game_engine_type == GAME_ENGINE_TYPE_SP &&
8240 level.native_sp_level->demo.is_available)
8241 CopyNativeTape_SP_to_RND(&level);
8244 static boolean checkSaveTape_SCRN(struct TapeInfo *tape)
8246 // chunk required for team mode tapes with non-default screen size
8247 return (tape->num_participating_players > 1 &&
8248 (tape->scr_fieldx != SCR_FIELDX_DEFAULT ||
8249 tape->scr_fieldy != SCR_FIELDY_DEFAULT));
8252 static void SaveTape_VERS(FILE *file, struct TapeInfo *tape)
8254 putFileVersion(file, tape->file_version);
8255 putFileVersion(file, tape->game_version);
8258 static void SaveTape_HEAD(FILE *file, struct TapeInfo *tape)
8261 byte store_participating_players = 0;
8263 // set bits for participating players for compact storage
8264 for (i = 0; i < MAX_PLAYERS; i++)
8265 if (tape->player_participates[i])
8266 store_participating_players |= (1 << i);
8268 putFile32BitBE(file, tape->random_seed);
8269 putFile32BitBE(file, tape->date);
8270 putFile32BitBE(file, tape->length);
8272 putFile8Bit(file, store_participating_players);
8274 putFile8Bit(file, getTapeActionValue(tape));
8276 putFile8Bit(file, tape->property_bits);
8278 // unused bytes not at the end here for 4-byte alignment of engine_version
8279 WriteUnusedBytesToFile(file, TAPE_CHUNK_HEAD_UNUSED);
8281 putFileVersion(file, tape->engine_version);
8284 static void SaveTape_SCRN(FILE *file, struct TapeInfo *tape)
8286 putFile8Bit(file, tape->scr_fieldx);
8287 putFile8Bit(file, tape->scr_fieldy);
8290 static void SaveTape_INFO(FILE *file, struct TapeInfo *tape)
8292 int level_identifier_size = strlen(tape->level_identifier) + 1;
8295 putFile16BitBE(file, level_identifier_size);
8297 for (i = 0; i < level_identifier_size; i++)
8298 putFile8Bit(file, tape->level_identifier[i]);
8300 putFile16BitBE(file, tape->level_nr);
8303 static void SaveTape_BODY(FILE *file, struct TapeInfo *tape)
8307 for (i = 0; i < tape->length; i++)
8309 if (tape->use_key_actions)
8311 for (j = 0; j < MAX_PLAYERS; j++)
8312 if (tape->player_participates[j])
8313 putFile8Bit(file, tape->pos[i].action[j]);
8316 if (tape->use_mouse_actions)
8318 putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_LX]);
8319 putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_LY]);
8320 putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_BUTTON]);
8323 putFile8Bit(file, tape->pos[i].delay);
8327 void SaveTapeToFilename(char *filename)
8331 int info_chunk_size;
8332 int body_chunk_size;
8334 if (!(file = fopen(filename, MODE_WRITE)))
8336 Warn("cannot save level recording file '%s'", filename);
8341 tape_pos_size = getTapePosSize(&tape);
8343 info_chunk_size = 2 + (strlen(tape.level_identifier) + 1) + 2;
8344 body_chunk_size = tape_pos_size * tape.length;
8346 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
8347 putFileChunkBE(file, "TAPE", CHUNK_SIZE_NONE);
8349 putFileChunkBE(file, "VERS", TAPE_CHUNK_VERS_SIZE);
8350 SaveTape_VERS(file, &tape);
8352 putFileChunkBE(file, "HEAD", TAPE_CHUNK_HEAD_SIZE);
8353 SaveTape_HEAD(file, &tape);
8355 if (checkSaveTape_SCRN(&tape))
8357 putFileChunkBE(file, "SCRN", TAPE_CHUNK_SCRN_SIZE);
8358 SaveTape_SCRN(file, &tape);
8361 putFileChunkBE(file, "INFO", info_chunk_size);
8362 SaveTape_INFO(file, &tape);
8364 putFileChunkBE(file, "BODY", body_chunk_size);
8365 SaveTape_BODY(file, &tape);
8369 SetFilePermissions(filename, PERMS_PRIVATE);
8372 static void SaveTapeExt(char *filename)
8376 tape.file_version = FILE_VERSION_ACTUAL;
8377 tape.game_version = GAME_VERSION_ACTUAL;
8379 tape.num_participating_players = 0;
8381 // count number of participating players
8382 for (i = 0; i < MAX_PLAYERS; i++)
8383 if (tape.player_participates[i])
8384 tape.num_participating_players++;
8386 SaveTapeToFilename(filename);
8388 tape.changed = FALSE;
8391 void SaveTape(int nr)
8393 char *filename = getTapeFilename(nr);
8395 InitTapeDirectory(leveldir_current->subdir);
8397 SaveTapeExt(filename);
8400 void SaveScoreTape(int nr)
8402 char *filename = getScoreTapeFilename(tape.score_tape_basename, nr);
8404 // used instead of "leveldir_current->subdir" (for network games)
8405 InitScoreTapeDirectory(levelset.identifier, nr);
8407 SaveTapeExt(filename);
8410 static boolean SaveTapeCheckedExt(int nr, char *msg_replace, char *msg_saved,
8411 unsigned int req_state_added)
8413 char *filename = getTapeFilename(nr);
8414 boolean new_tape = !fileExists(filename);
8415 boolean tape_saved = FALSE;
8417 if (new_tape || Request(msg_replace, REQ_ASK | req_state_added))
8422 Request(msg_saved, REQ_CONFIRM | req_state_added);
8430 boolean SaveTapeChecked(int nr)
8432 return SaveTapeCheckedExt(nr, "Replace old tape?", "Tape saved!", 0);
8435 boolean SaveTapeChecked_LevelSolved(int nr)
8437 return SaveTapeCheckedExt(nr, "Level solved! Replace old tape?",
8438 "Level solved! Tape saved!", REQ_STAY_OPEN);
8441 void DumpTape(struct TapeInfo *tape)
8443 int tape_frame_counter;
8446 if (tape->no_valid_file)
8448 Warn("cannot dump -- no valid tape file found");
8455 Print("Tape of Level %03d (file version %08d, game version %08d)\n",
8456 tape->level_nr, tape->file_version, tape->game_version);
8457 Print(" (effective engine version %08d)\n",
8458 tape->engine_version);
8459 Print("Level series identifier: '%s'\n", tape->level_identifier);
8461 Print("Special tape properties: ");
8462 if (tape->property_bits == TAPE_PROPERTY_NONE)
8464 if (tape->property_bits & TAPE_PROPERTY_EM_RANDOM_BUG)
8465 Print("[em_random_bug]");
8466 if (tape->property_bits & TAPE_PROPERTY_GAME_SPEED)
8467 Print("[game_speed]");
8468 if (tape->property_bits & TAPE_PROPERTY_PAUSE_MODE)
8470 if (tape->property_bits & TAPE_PROPERTY_SINGLE_STEP)
8471 Print("[single_step]");
8472 if (tape->property_bits & TAPE_PROPERTY_SNAPSHOT)
8473 Print("[snapshot]");
8474 if (tape->property_bits & TAPE_PROPERTY_REPLAYED)
8475 Print("[replayed]");
8476 if (tape->property_bits & TAPE_PROPERTY_TAS_KEYS)
8477 Print("[tas_keys]");
8478 if (tape->property_bits & TAPE_PROPERTY_SMALL_GRAPHICS)
8479 Print("[small_graphics]");
8482 int year2 = tape->date / 10000;
8483 int year4 = (year2 < 70 ? 2000 + year2 : 1900 + year2);
8484 int month_index_raw = (tape->date / 100) % 100;
8485 int month_index = month_index_raw % 12; // prevent invalid index
8486 int month = month_index + 1;
8487 int day = tape->date % 100;
8489 Print("Tape date: %04d-%02d-%02d\n", year4, month, day);
8493 tape_frame_counter = 0;
8495 for (i = 0; i < tape->length; i++)
8497 if (i >= MAX_TAPE_LEN)
8502 for (j = 0; j < MAX_PLAYERS; j++)
8504 if (tape->player_participates[j])
8506 int action = tape->pos[i].action[j];
8508 Print("%d:%02x ", j, action);
8509 Print("[%c%c%c%c|%c%c] - ",
8510 (action & JOY_LEFT ? '<' : ' '),
8511 (action & JOY_RIGHT ? '>' : ' '),
8512 (action & JOY_UP ? '^' : ' '),
8513 (action & JOY_DOWN ? 'v' : ' '),
8514 (action & JOY_BUTTON_1 ? '1' : ' '),
8515 (action & JOY_BUTTON_2 ? '2' : ' '));
8519 Print("(%03d) ", tape->pos[i].delay);
8520 Print("[%05d]\n", tape_frame_counter);
8522 tape_frame_counter += tape->pos[i].delay;
8528 void DumpTapes(void)
8530 static LevelDirTree *dumptape_leveldir = NULL;
8532 dumptape_leveldir = getTreeInfoFromIdentifier(leveldir_first,
8533 global.dumptape_leveldir);
8535 if (dumptape_leveldir == NULL)
8536 Fail("no such level identifier: '%s'", global.dumptape_leveldir);
8538 if (global.dumptape_level_nr < dumptape_leveldir->first_level ||
8539 global.dumptape_level_nr > dumptape_leveldir->last_level)
8540 Fail("no such level number: %d", global.dumptape_level_nr);
8542 leveldir_current = dumptape_leveldir;
8544 if (options.mytapes)
8545 LoadTape(global.dumptape_level_nr);
8547 LoadSolutionTape(global.dumptape_level_nr);
8555 // ============================================================================
8556 // score file functions
8557 // ============================================================================
8559 static void setScoreInfoToDefaultsExt(struct ScoreInfo *scores)
8563 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
8565 strcpy(scores->entry[i].tape_basename, UNDEFINED_FILENAME);
8566 strcpy(scores->entry[i].name, EMPTY_PLAYER_NAME);
8567 scores->entry[i].score = 0;
8568 scores->entry[i].time = 0;
8571 scores->num_entries = 0;
8572 scores->last_added = -1;
8573 scores->last_added_local = -1;
8575 scores->updated = FALSE;
8576 scores->uploaded = FALSE;
8577 scores->force_last_added = FALSE;
8580 static void setScoreInfoToDefaults(void)
8582 setScoreInfoToDefaultsExt(&scores);
8585 static void setServerScoreInfoToDefaults(void)
8587 setScoreInfoToDefaultsExt(&server_scores);
8590 static void LoadScore_OLD(int nr)
8593 char *filename = getScoreFilename(nr);
8594 char cookie[MAX_LINE_LEN];
8595 char line[MAX_LINE_LEN];
8599 if (!(file = fopen(filename, MODE_READ)))
8602 // check file identifier
8603 if (fgets(cookie, MAX_LINE_LEN, file) == NULL)
8605 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
8606 cookie[strlen(cookie) - 1] = '\0';
8608 if (!checkCookieString(cookie, SCORE_COOKIE_TMPL))
8610 Warn("unknown format of score file '%s'", filename);
8617 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
8619 if (fscanf(file, "%d", &scores.entry[i].score) == EOF)
8620 Warn("fscanf() failed; %s", strerror(errno));
8622 if (fgets(line, MAX_LINE_LEN, file) == NULL)
8625 if (strlen(line) > 0 && line[strlen(line) - 1] == '\n')
8626 line[strlen(line) - 1] = '\0';
8628 for (line_ptr = line; *line_ptr; line_ptr++)
8630 if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != '\0')
8632 strncpy(scores.entry[i].name, line_ptr, MAX_PLAYER_NAME_LEN);
8633 scores.entry[i].name[MAX_PLAYER_NAME_LEN] = '\0';
8642 static void ConvertScore_OLD(void)
8644 // only convert score to time for levels that rate playing time over score
8645 if (!level.rate_time_over_score)
8648 // convert old score to playing time for score-less levels (like Supaplex)
8649 int time_final_max = 999;
8652 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
8654 int score = scores.entry[i].score;
8656 if (score > 0 && score < time_final_max)
8657 scores.entry[i].time = (time_final_max - score - 1) * FRAMES_PER_SECOND;
8661 static int LoadScore_VERS(File *file, int chunk_size, struct ScoreInfo *scores)
8663 scores->file_version = getFileVersion(file);
8664 scores->game_version = getFileVersion(file);
8669 static int LoadScore_INFO(File *file, int chunk_size, struct ScoreInfo *scores)
8671 char *level_identifier = NULL;
8672 int level_identifier_size;
8675 level_identifier_size = getFile16BitBE(file);
8677 level_identifier = checked_malloc(level_identifier_size);
8679 for (i = 0; i < level_identifier_size; i++)
8680 level_identifier[i] = getFile8Bit(file);
8682 strncpy(scores->level_identifier, level_identifier, MAX_FILENAME_LEN);
8683 scores->level_identifier[MAX_FILENAME_LEN] = '\0';
8685 checked_free(level_identifier);
8687 scores->level_nr = getFile16BitBE(file);
8688 scores->num_entries = getFile16BitBE(file);
8690 chunk_size = 2 + level_identifier_size + 2 + 2;
8695 static int LoadScore_NAME(File *file, int chunk_size, struct ScoreInfo *scores)
8699 for (i = 0; i < scores->num_entries; i++)
8701 for (j = 0; j < MAX_PLAYER_NAME_LEN; j++)
8702 scores->entry[i].name[j] = getFile8Bit(file);
8704 scores->entry[i].name[MAX_PLAYER_NAME_LEN] = '\0';
8707 chunk_size = scores->num_entries * MAX_PLAYER_NAME_LEN;
8712 static int LoadScore_SCOR(File *file, int chunk_size, struct ScoreInfo *scores)
8716 for (i = 0; i < scores->num_entries; i++)
8717 scores->entry[i].score = getFile16BitBE(file);
8719 chunk_size = scores->num_entries * 2;
8724 static int LoadScore_TIME(File *file, int chunk_size, struct ScoreInfo *scores)
8728 for (i = 0; i < scores->num_entries; i++)
8729 scores->entry[i].time = getFile32BitBE(file);
8731 chunk_size = scores->num_entries * 4;
8736 static int LoadScore_TAPE(File *file, int chunk_size, struct ScoreInfo *scores)
8740 for (i = 0; i < scores->num_entries; i++)
8742 for (j = 0; j < MAX_SCORE_TAPE_BASENAME_LEN; j++)
8743 scores->entry[i].tape_basename[j] = getFile8Bit(file);
8745 scores->entry[i].tape_basename[MAX_SCORE_TAPE_BASENAME_LEN] = '\0';
8748 chunk_size = scores->num_entries * MAX_SCORE_TAPE_BASENAME_LEN;
8753 void LoadScore(int nr)
8755 char *filename = getScoreFilename(nr);
8756 char cookie[MAX_LINE_LEN];
8757 char chunk_name[CHUNK_ID_LEN + 1];
8759 boolean old_score_file_format = FALSE;
8762 // always start with reliable default values
8763 setScoreInfoToDefaults();
8765 if (!(file = openFile(filename, MODE_READ)))
8768 getFileChunkBE(file, chunk_name, NULL);
8769 if (strEqual(chunk_name, "RND1"))
8771 getFile32BitBE(file); // not used
8773 getFileChunkBE(file, chunk_name, NULL);
8774 if (!strEqual(chunk_name, "SCOR"))
8776 Warn("unknown format of score file '%s'", filename);
8783 else // check for old file format with cookie string
8785 strcpy(cookie, chunk_name);
8786 if (getStringFromFile(file, &cookie[4], MAX_LINE_LEN - 4) == NULL)
8788 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
8789 cookie[strlen(cookie) - 1] = '\0';
8791 if (!checkCookieString(cookie, SCORE_COOKIE_TMPL))
8793 Warn("unknown format of score file '%s'", filename);
8800 old_score_file_format = TRUE;
8803 if (old_score_file_format)
8805 // score files from versions before 4.2.4.0 without chunk structure
8808 // convert score to time, if possible (mainly for Supaplex levels)
8817 int (*loader)(File *, int, struct ScoreInfo *);
8821 { "VERS", SCORE_CHUNK_VERS_SIZE, LoadScore_VERS },
8822 { "INFO", -1, LoadScore_INFO },
8823 { "NAME", -1, LoadScore_NAME },
8824 { "SCOR", -1, LoadScore_SCOR },
8825 { "TIME", -1, LoadScore_TIME },
8826 { "TAPE", -1, LoadScore_TAPE },
8831 while (getFileChunkBE(file, chunk_name, &chunk_size))
8835 while (chunk_info[i].name != NULL &&
8836 !strEqual(chunk_name, chunk_info[i].name))
8839 if (chunk_info[i].name == NULL)
8841 Warn("unknown chunk '%s' in score file '%s'",
8842 chunk_name, filename);
8844 ReadUnusedBytesFromFile(file, chunk_size);
8846 else if (chunk_info[i].size != -1 &&
8847 chunk_info[i].size != chunk_size)
8849 Warn("wrong size (%d) of chunk '%s' in score file '%s'",
8850 chunk_size, chunk_name, filename);
8852 ReadUnusedBytesFromFile(file, chunk_size);
8856 // call function to load this score chunk
8857 int chunk_size_expected =
8858 (chunk_info[i].loader)(file, chunk_size, &scores);
8860 // the size of some chunks cannot be checked before reading other
8861 // chunks first (like "HEAD" and "BODY") that contain some header
8862 // information, so check them here
8863 if (chunk_size_expected != chunk_size)
8865 Warn("wrong size (%d) of chunk '%s' in score file '%s'",
8866 chunk_size, chunk_name, filename);
8875 #if ENABLE_HISTORIC_CHUNKS
8876 void SaveScore_OLD(int nr)
8879 char *filename = getScoreFilename(nr);
8882 // used instead of "leveldir_current->subdir" (for network games)
8883 InitScoreDirectory(levelset.identifier);
8885 if (!(file = fopen(filename, MODE_WRITE)))
8887 Warn("cannot save score for level %d", nr);
8892 fprintf(file, "%s\n\n", SCORE_COOKIE);
8894 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
8895 fprintf(file, "%d %s\n", scores.entry[i].score, scores.entry[i].name);
8899 SetFilePermissions(filename, PERMS_PRIVATE);
8903 static void SaveScore_VERS(FILE *file, struct ScoreInfo *scores)
8905 putFileVersion(file, scores->file_version);
8906 putFileVersion(file, scores->game_version);
8909 static void SaveScore_INFO(FILE *file, struct ScoreInfo *scores)
8911 int level_identifier_size = strlen(scores->level_identifier) + 1;
8914 putFile16BitBE(file, level_identifier_size);
8916 for (i = 0; i < level_identifier_size; i++)
8917 putFile8Bit(file, scores->level_identifier[i]);
8919 putFile16BitBE(file, scores->level_nr);
8920 putFile16BitBE(file, scores->num_entries);
8923 static void SaveScore_NAME(FILE *file, struct ScoreInfo *scores)
8927 for (i = 0; i < scores->num_entries; i++)
8929 int name_size = strlen(scores->entry[i].name);
8931 for (j = 0; j < MAX_PLAYER_NAME_LEN; j++)
8932 putFile8Bit(file, (j < name_size ? scores->entry[i].name[j] : 0));
8936 static void SaveScore_SCOR(FILE *file, struct ScoreInfo *scores)
8940 for (i = 0; i < scores->num_entries; i++)
8941 putFile16BitBE(file, scores->entry[i].score);
8944 static void SaveScore_TIME(FILE *file, struct ScoreInfo *scores)
8948 for (i = 0; i < scores->num_entries; i++)
8949 putFile32BitBE(file, scores->entry[i].time);
8952 static void SaveScore_TAPE(FILE *file, struct ScoreInfo *scores)
8956 for (i = 0; i < scores->num_entries; i++)
8958 int size = strlen(scores->entry[i].tape_basename);
8960 for (j = 0; j < MAX_SCORE_TAPE_BASENAME_LEN; j++)
8961 putFile8Bit(file, (j < size ? scores->entry[i].tape_basename[j] : 0));
8965 static void SaveScoreToFilename(char *filename)
8968 int info_chunk_size;
8969 int name_chunk_size;
8970 int scor_chunk_size;
8971 int time_chunk_size;
8972 int tape_chunk_size;
8974 if (!(file = fopen(filename, MODE_WRITE)))
8976 Warn("cannot save score file '%s'", filename);
8981 info_chunk_size = 2 + (strlen(scores.level_identifier) + 1) + 2 + 2;
8982 name_chunk_size = scores.num_entries * MAX_PLAYER_NAME_LEN;
8983 scor_chunk_size = scores.num_entries * 2;
8984 time_chunk_size = scores.num_entries * 4;
8985 tape_chunk_size = scores.num_entries * MAX_SCORE_TAPE_BASENAME_LEN;
8987 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
8988 putFileChunkBE(file, "SCOR", CHUNK_SIZE_NONE);
8990 putFileChunkBE(file, "VERS", SCORE_CHUNK_VERS_SIZE);
8991 SaveScore_VERS(file, &scores);
8993 putFileChunkBE(file, "INFO", info_chunk_size);
8994 SaveScore_INFO(file, &scores);
8996 putFileChunkBE(file, "NAME", name_chunk_size);
8997 SaveScore_NAME(file, &scores);
8999 putFileChunkBE(file, "SCOR", scor_chunk_size);
9000 SaveScore_SCOR(file, &scores);
9002 putFileChunkBE(file, "TIME", time_chunk_size);
9003 SaveScore_TIME(file, &scores);
9005 putFileChunkBE(file, "TAPE", tape_chunk_size);
9006 SaveScore_TAPE(file, &scores);
9010 SetFilePermissions(filename, PERMS_PRIVATE);
9013 void SaveScore(int nr)
9015 char *filename = getScoreFilename(nr);
9018 // used instead of "leveldir_current->subdir" (for network games)
9019 InitScoreDirectory(levelset.identifier);
9021 scores.file_version = FILE_VERSION_ACTUAL;
9022 scores.game_version = GAME_VERSION_ACTUAL;
9024 strncpy(scores.level_identifier, levelset.identifier, MAX_FILENAME_LEN);
9025 scores.level_identifier[MAX_FILENAME_LEN] = '\0';
9026 scores.level_nr = level_nr;
9028 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9029 if (scores.entry[i].score == 0 &&
9030 scores.entry[i].time == 0 &&
9031 strEqual(scores.entry[i].name, EMPTY_PLAYER_NAME))
9034 scores.num_entries = i;
9036 if (scores.num_entries == 0)
9039 SaveScoreToFilename(filename);
9042 void ExecuteAsThread(SDL_ThreadFunction function, char *name, void *data,
9045 #if defined(PLATFORM_EMSCRIPTEN)
9046 // threads currently not fully supported by Emscripten/SDL and some browsers
9049 SDL_Thread *thread = SDL_CreateThread(function, name, data);
9052 SDL_DetachThread(thread);
9054 Error("Cannot create thread to %s!", error);
9056 // nasty kludge to lower probability of intermingled thread error messages
9061 char *getPasswordJSON(char *password)
9063 static char password_json[MAX_FILENAME_LEN] = "";
9064 static boolean initialized = FALSE;
9068 if (password != NULL &&
9069 !strEqual(password, "") &&
9070 !strEqual(password, UNDEFINED_PASSWORD))
9071 snprintf(password_json, MAX_FILENAME_LEN,
9072 " \"password\": \"%s\",\n",
9073 setup.api_server_password);
9078 return password_json;
9081 struct ApiGetScoreThreadData
9084 char *score_cache_filename;
9087 static void *CreateThreadData_ApiGetScore(int nr)
9089 struct ApiGetScoreThreadData *data =
9090 checked_malloc(sizeof(struct ApiGetScoreThreadData));
9091 char *score_cache_filename = getScoreCacheFilename(nr);
9093 data->level_nr = nr;
9094 data->score_cache_filename = getStringCopy(score_cache_filename);
9099 static void FreeThreadData_ApiGetScore(void *data_raw)
9101 struct ApiGetScoreThreadData *data = data_raw;
9103 checked_free(data->score_cache_filename);
9107 static boolean SetRequest_ApiGetScore(struct HttpRequest *request,
9110 struct ApiGetScoreThreadData *data = data_raw;
9111 int level_nr = data->level_nr;
9113 request->hostname = setup.api_server_hostname;
9114 request->port = API_SERVER_PORT;
9115 request->method = API_SERVER_METHOD;
9116 request->uri = API_SERVER_URI_GET;
9118 char *levelset_identifier = getEscapedJSON(leveldir_current->identifier);
9119 char *levelset_name = getEscapedJSON(leveldir_current->name);
9121 snprintf(request->body, MAX_HTTP_BODY_SIZE,
9124 " \"game_version\": \"%s\",\n"
9125 " \"game_platform\": \"%s\",\n"
9126 " \"levelset_identifier\": \"%s\",\n"
9127 " \"levelset_name\": \"%s\",\n"
9128 " \"level_nr\": \"%d\"\n"
9130 getPasswordJSON(setup.api_server_password),
9131 getProgramRealVersionString(),
9132 getProgramPlatformString(),
9133 levelset_identifier,
9137 checked_free(levelset_identifier);
9138 checked_free(levelset_name);
9140 ConvertHttpRequestBodyToServerEncoding(request);
9145 static void HandleResponse_ApiGetScore(struct HttpResponse *response,
9148 struct ApiGetScoreThreadData *data = data_raw;
9150 if (response->body_size == 0)
9152 // no scores available for this level
9157 ConvertHttpResponseBodyToClientEncoding(response);
9159 char *filename = data->score_cache_filename;
9163 // used instead of "leveldir_current->subdir" (for network games)
9164 InitScoreCacheDirectory(levelset.identifier);
9166 if (!(file = fopen(filename, MODE_WRITE)))
9168 Warn("cannot save score cache file '%s'", filename);
9173 for (i = 0; i < response->body_size; i++)
9174 fputc(response->body[i], file);
9178 SetFilePermissions(filename, PERMS_PRIVATE);
9180 server_scores.updated = TRUE;
9183 #if defined(PLATFORM_EMSCRIPTEN)
9184 static void Emscripten_ApiGetScore_Loaded(unsigned handle, void *data_raw,
9185 void *buffer, unsigned int size)
9187 struct HttpResponse *response = GetHttpResponseFromBuffer(buffer, size);
9189 if (response != NULL)
9191 HandleResponse_ApiGetScore(response, data_raw);
9193 checked_free(response);
9197 Error("server response too large to handle (%d bytes)", size);
9200 FreeThreadData_ApiGetScore(data_raw);
9203 static void Emscripten_ApiGetScore_Failed(unsigned handle, void *data_raw,
9204 int code, const char *status)
9206 Error("server failed to handle request: %d %s", code, status);
9208 FreeThreadData_ApiGetScore(data_raw);
9211 static void Emscripten_ApiGetScore_Progress(unsigned handle, void *data_raw,
9212 int bytes, int size)
9214 // nothing to do here
9217 static void Emscripten_ApiGetScore_HttpRequest(struct HttpRequest *request,
9220 if (!SetRequest_ApiGetScore(request, data_raw))
9222 FreeThreadData_ApiGetScore(data_raw);
9227 emscripten_async_wget2_data(request->uri,
9232 Emscripten_ApiGetScore_Loaded,
9233 Emscripten_ApiGetScore_Failed,
9234 Emscripten_ApiGetScore_Progress);
9239 static void ApiGetScore_HttpRequestExt(struct HttpRequest *request,
9240 struct HttpResponse *response,
9243 if (!SetRequest_ApiGetScore(request, data_raw))
9246 if (!DoHttpRequest(request, response))
9248 Error("HTTP request failed: %s", GetHttpError());
9253 if (!HTTP_SUCCESS(response->status_code))
9255 // do not show error message if no scores found for this level set
9256 if (response->status_code == 404)
9259 Error("server failed to handle request: %d %s",
9260 response->status_code,
9261 response->status_text);
9266 HandleResponse_ApiGetScore(response, data_raw);
9269 static void ApiGetScore_HttpRequest(struct HttpRequest *request,
9270 struct HttpResponse *response,
9273 ApiGetScore_HttpRequestExt(request, response, data_raw);
9275 FreeThreadData_ApiGetScore(data_raw);
9279 static int ApiGetScoreThread(void *data_raw)
9281 struct HttpRequest *request = checked_calloc(sizeof(struct HttpRequest));
9282 struct HttpResponse *response = checked_calloc(sizeof(struct HttpResponse));
9284 program.api_thread_count++;
9286 #if defined(PLATFORM_EMSCRIPTEN)
9287 Emscripten_ApiGetScore_HttpRequest(request, data_raw);
9289 ApiGetScore_HttpRequest(request, response, data_raw);
9292 program.api_thread_count--;
9294 checked_free(request);
9295 checked_free(response);
9300 static void ApiGetScoreAsThread(int nr)
9302 struct ApiGetScoreThreadData *data = CreateThreadData_ApiGetScore(nr);
9304 ExecuteAsThread(ApiGetScoreThread,
9305 "ApiGetScore", data,
9306 "download scores from server");
9309 static void LoadServerScoreFromCache(int nr)
9311 struct ScoreEntry score_entry;
9320 { &score_entry.score, FALSE, 0 },
9321 { &score_entry.time, FALSE, 0 },
9322 { score_entry.name, TRUE, MAX_PLAYER_NAME_LEN },
9323 { score_entry.tape_basename, TRUE, MAX_FILENAME_LEN },
9327 char *filename = getScoreCacheFilename(nr);
9328 SetupFileHash *score_hash = loadSetupFileHash(filename);
9331 server_scores.num_entries = 0;
9333 if (score_hash == NULL)
9336 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9338 score_entry = server_scores.entry[i];
9340 for (j = 0; score_mapping[j].value != NULL; j++)
9344 sprintf(token, "%02d.%d", i, j);
9346 char *value = getHashEntry(score_hash, token);
9351 if (score_mapping[j].is_string)
9353 char *score_value = (char *)score_mapping[j].value;
9354 int value_size = score_mapping[j].string_size;
9356 strncpy(score_value, value, value_size);
9357 score_value[value_size] = '\0';
9361 int *score_value = (int *)score_mapping[j].value;
9363 *score_value = atoi(value);
9366 server_scores.num_entries = i + 1;
9369 server_scores.entry[i] = score_entry;
9372 freeSetupFileHash(score_hash);
9375 void LoadServerScore(int nr, boolean download_score)
9377 if (!setup.use_api_server)
9380 // always start with reliable default values
9381 setServerScoreInfoToDefaults();
9383 // 1st step: load server scores from cache file (which may not exist)
9384 // (this should prevent reading it while the thread is writing to it)
9385 LoadServerScoreFromCache(nr);
9387 if (download_score && runtime.use_api_server)
9389 // 2nd step: download server scores from score server to cache file
9390 // (as thread, as it might time out if the server is not reachable)
9391 ApiGetScoreAsThread(nr);
9395 static char *get_file_base64(char *filename)
9397 struct stat file_status;
9399 if (stat(filename, &file_status) != 0)
9401 Error("cannot stat file '%s'", filename);
9406 int buffer_size = file_status.st_size;
9407 byte *buffer = checked_malloc(buffer_size);
9411 if (!(file = fopen(filename, MODE_READ)))
9413 Error("cannot open file '%s'", filename);
9415 checked_free(buffer);
9420 for (i = 0; i < buffer_size; i++)
9422 int c = fgetc(file);
9426 Error("cannot read from input file '%s'", filename);
9429 checked_free(buffer);
9434 buffer[i] = (byte)c;
9439 int buffer_encoded_size = base64_encoded_size(buffer_size);
9440 char *buffer_encoded = checked_malloc(buffer_encoded_size);
9442 base64_encode(buffer_encoded, buffer, buffer_size);
9444 checked_free(buffer);
9446 return buffer_encoded;
9449 static void PrepareScoreTapesForUpload(char *leveldir_subdir)
9451 MarkTapeDirectoryUploadsAsIncomplete(leveldir_subdir);
9453 // if score tape not uploaded, ask for uploading missing tapes later
9454 if (!setup.has_remaining_tapes)
9455 setup.ask_for_remaining_tapes = TRUE;
9457 setup.provide_uploading_tapes = TRUE;
9458 setup.has_remaining_tapes = TRUE;
9460 SaveSetup_ServerSetup();
9463 struct ApiAddScoreThreadData
9467 char *leveldir_subdir;
9468 char *score_tape_filename;
9469 struct ScoreEntry score_entry;
9472 static void *CreateThreadData_ApiAddScore(int nr, boolean tape_saved,
9473 char *score_tape_filename)
9475 struct ApiAddScoreThreadData *data =
9476 checked_malloc(sizeof(struct ApiAddScoreThreadData));
9477 struct ScoreEntry *score_entry = &scores.entry[scores.last_added];
9479 if (score_tape_filename == NULL)
9480 score_tape_filename = getScoreTapeFilename(score_entry->tape_basename, nr);
9482 data->level_nr = nr;
9483 data->tape_saved = tape_saved;
9484 data->leveldir_subdir = getStringCopy(leveldir_current->subdir);
9485 data->score_tape_filename = getStringCopy(score_tape_filename);
9486 data->score_entry = *score_entry;
9491 static void FreeThreadData_ApiAddScore(void *data_raw)
9493 struct ApiAddScoreThreadData *data = data_raw;
9495 checked_free(data->leveldir_subdir);
9496 checked_free(data->score_tape_filename);
9500 static boolean SetRequest_ApiAddScore(struct HttpRequest *request,
9503 struct ApiAddScoreThreadData *data = data_raw;
9504 struct ScoreEntry *score_entry = &data->score_entry;
9505 char *score_tape_filename = data->score_tape_filename;
9506 boolean tape_saved = data->tape_saved;
9507 int level_nr = data->level_nr;
9509 request->hostname = setup.api_server_hostname;
9510 request->port = API_SERVER_PORT;
9511 request->method = API_SERVER_METHOD;
9512 request->uri = API_SERVER_URI_ADD;
9514 char *tape_base64 = get_file_base64(score_tape_filename);
9516 if (tape_base64 == NULL)
9518 Error("loading and base64 encoding score tape file failed");
9523 char *player_name_raw = score_entry->name;
9524 char *player_uuid_raw = setup.player_uuid;
9526 if (options.player_name != NULL && global.autoplay_leveldir != NULL)
9528 player_name_raw = options.player_name;
9529 player_uuid_raw = "";
9532 char *levelset_identifier = getEscapedJSON(leveldir_current->identifier);
9533 char *levelset_name = getEscapedJSON(leveldir_current->name);
9534 char *levelset_author = getEscapedJSON(leveldir_current->author);
9535 char *level_name = getEscapedJSON(level.name);
9536 char *level_author = getEscapedJSON(level.author);
9537 char *player_name = getEscapedJSON(player_name_raw);
9538 char *player_uuid = getEscapedJSON(player_uuid_raw);
9540 snprintf(request->body, MAX_HTTP_BODY_SIZE,
9543 " \"game_version\": \"%s\",\n"
9544 " \"game_platform\": \"%s\",\n"
9545 " \"batch_time\": \"%d\",\n"
9546 " \"levelset_identifier\": \"%s\",\n"
9547 " \"levelset_name\": \"%s\",\n"
9548 " \"levelset_author\": \"%s\",\n"
9549 " \"levelset_num_levels\": \"%d\",\n"
9550 " \"levelset_first_level\": \"%d\",\n"
9551 " \"level_nr\": \"%d\",\n"
9552 " \"level_name\": \"%s\",\n"
9553 " \"level_author\": \"%s\",\n"
9554 " \"use_step_counter\": \"%d\",\n"
9555 " \"rate_time_over_score\": \"%d\",\n"
9556 " \"player_name\": \"%s\",\n"
9557 " \"player_uuid\": \"%s\",\n"
9558 " \"score\": \"%d\",\n"
9559 " \"time\": \"%d\",\n"
9560 " \"tape_basename\": \"%s\",\n"
9561 " \"tape_saved\": \"%d\",\n"
9562 " \"tape\": \"%s\"\n"
9564 getPasswordJSON(setup.api_server_password),
9565 getProgramRealVersionString(),
9566 getProgramPlatformString(),
9567 (int)global.autoplay_time,
9568 levelset_identifier,
9571 leveldir_current->levels,
9572 leveldir_current->first_level,
9576 level.use_step_counter,
9577 level.rate_time_over_score,
9582 score_entry->tape_basename,
9586 checked_free(tape_base64);
9588 checked_free(levelset_identifier);
9589 checked_free(levelset_name);
9590 checked_free(levelset_author);
9591 checked_free(level_name);
9592 checked_free(level_author);
9593 checked_free(player_name);
9594 checked_free(player_uuid);
9596 ConvertHttpRequestBodyToServerEncoding(request);
9601 static void HandleResponse_ApiAddScore(struct HttpResponse *response,
9604 server_scores.uploaded = TRUE;
9607 static void HandleFailure_ApiAddScore(void *data_raw)
9609 struct ApiAddScoreThreadData *data = data_raw;
9611 PrepareScoreTapesForUpload(data->leveldir_subdir);
9614 #if defined(PLATFORM_EMSCRIPTEN)
9615 static void Emscripten_ApiAddScore_Loaded(unsigned handle, void *data_raw,
9616 void *buffer, unsigned int size)
9618 struct HttpResponse *response = GetHttpResponseFromBuffer(buffer, size);
9620 if (response != NULL)
9622 HandleResponse_ApiAddScore(response, data_raw);
9624 checked_free(response);
9628 Error("server response too large to handle (%d bytes)", size);
9630 HandleFailure_ApiAddScore(data_raw);
9633 FreeThreadData_ApiAddScore(data_raw);
9636 static void Emscripten_ApiAddScore_Failed(unsigned handle, void *data_raw,
9637 int code, const char *status)
9639 Error("server failed to handle request: %d %s", code, status);
9641 HandleFailure_ApiAddScore(data_raw);
9643 FreeThreadData_ApiAddScore(data_raw);
9646 static void Emscripten_ApiAddScore_Progress(unsigned handle, void *data_raw,
9647 int bytes, int size)
9649 // nothing to do here
9652 static void Emscripten_ApiAddScore_HttpRequest(struct HttpRequest *request,
9655 if (!SetRequest_ApiAddScore(request, data_raw))
9657 FreeThreadData_ApiAddScore(data_raw);
9662 emscripten_async_wget2_data(request->uri,
9667 Emscripten_ApiAddScore_Loaded,
9668 Emscripten_ApiAddScore_Failed,
9669 Emscripten_ApiAddScore_Progress);
9674 static void ApiAddScore_HttpRequestExt(struct HttpRequest *request,
9675 struct HttpResponse *response,
9678 if (!SetRequest_ApiAddScore(request, data_raw))
9681 if (!DoHttpRequest(request, response))
9683 Error("HTTP request failed: %s", GetHttpError());
9685 HandleFailure_ApiAddScore(data_raw);
9690 if (!HTTP_SUCCESS(response->status_code))
9692 Error("server failed to handle request: %d %s",
9693 response->status_code,
9694 response->status_text);
9696 HandleFailure_ApiAddScore(data_raw);
9701 HandleResponse_ApiAddScore(response, data_raw);
9704 static void ApiAddScore_HttpRequest(struct HttpRequest *request,
9705 struct HttpResponse *response,
9708 ApiAddScore_HttpRequestExt(request, response, data_raw);
9710 FreeThreadData_ApiAddScore(data_raw);
9714 static int ApiAddScoreThread(void *data_raw)
9716 struct HttpRequest *request = checked_calloc(sizeof(struct HttpRequest));
9717 struct HttpResponse *response = checked_calloc(sizeof(struct HttpResponse));
9719 program.api_thread_count++;
9721 #if defined(PLATFORM_EMSCRIPTEN)
9722 Emscripten_ApiAddScore_HttpRequest(request, data_raw);
9724 ApiAddScore_HttpRequest(request, response, data_raw);
9727 program.api_thread_count--;
9729 checked_free(request);
9730 checked_free(response);
9735 static void ApiAddScoreAsThread(int nr, boolean tape_saved,
9736 char *score_tape_filename)
9738 struct ApiAddScoreThreadData *data =
9739 CreateThreadData_ApiAddScore(nr, tape_saved, score_tape_filename);
9741 ExecuteAsThread(ApiAddScoreThread,
9742 "ApiAddScore", data,
9743 "upload score to server");
9746 void SaveServerScore(int nr, boolean tape_saved)
9748 if (!runtime.use_api_server)
9750 PrepareScoreTapesForUpload(leveldir_current->subdir);
9755 ApiAddScoreAsThread(nr, tape_saved, NULL);
9758 void SaveServerScoreFromFile(int nr, boolean tape_saved,
9759 char *score_tape_filename)
9761 if (!runtime.use_api_server)
9764 ApiAddScoreAsThread(nr, tape_saved, score_tape_filename);
9767 void LoadLocalAndServerScore(int nr, boolean download_score)
9769 int last_added_local = scores.last_added_local;
9771 // needed if only showing server scores
9772 setScoreInfoToDefaults();
9774 if (!strEqual(setup.scores_in_highscore_list, STR_SCORES_TYPE_SERVER_ONLY))
9777 // restore last added local score entry (before merging server scores)
9778 scores.last_added = scores.last_added_local = last_added_local;
9780 if (setup.use_api_server &&
9781 !strEqual(setup.scores_in_highscore_list, STR_SCORES_TYPE_LOCAL_ONLY))
9783 // load server scores from cache file and trigger update from server
9784 LoadServerScore(nr, download_score);
9786 // merge local scores with scores from server
9792 // ============================================================================
9793 // setup file functions
9794 // ============================================================================
9796 #define TOKEN_STR_PLAYER_PREFIX "player_"
9799 static struct TokenInfo global_setup_tokens[] =
9803 &setup.player_name, "player_name"
9807 &setup.multiple_users, "multiple_users"
9811 &setup.sound, "sound"
9815 &setup.sound_loops, "repeating_sound_loops"
9819 &setup.sound_music, "background_music"
9823 &setup.sound_simple, "simple_sound_effects"
9827 &setup.toons, "toons"
9831 &setup.scroll_delay, "scroll_delay"
9835 &setup.forced_scroll_delay, "forced_scroll_delay"
9839 &setup.scroll_delay_value, "scroll_delay_value"
9843 &setup.engine_snapshot_mode, "engine_snapshot_mode"
9847 &setup.engine_snapshot_memory, "engine_snapshot_memory"
9851 &setup.fade_screens, "fade_screens"
9855 &setup.autorecord, "automatic_tape_recording"
9859 &setup.show_titlescreen, "show_titlescreen"
9863 &setup.quick_doors, "quick_doors"
9867 &setup.team_mode, "team_mode"
9871 &setup.handicap, "handicap"
9875 &setup.skip_levels, "skip_levels"
9879 &setup.increment_levels, "increment_levels"
9883 &setup.auto_play_next_level, "auto_play_next_level"
9887 &setup.count_score_after_game, "count_score_after_game"
9891 &setup.show_scores_after_game, "show_scores_after_game"
9895 &setup.time_limit, "time_limit"
9899 &setup.fullscreen, "fullscreen"
9903 &setup.window_scaling_percent, "window_scaling_percent"
9907 &setup.window_scaling_quality, "window_scaling_quality"
9911 &setup.screen_rendering_mode, "screen_rendering_mode"
9915 &setup.vsync_mode, "vsync_mode"
9919 &setup.ask_on_escape, "ask_on_escape"
9923 &setup.ask_on_escape_editor, "ask_on_escape_editor"
9927 &setup.ask_on_game_over, "ask_on_game_over"
9931 &setup.ask_on_quit_game, "ask_on_quit_game"
9935 &setup.ask_on_quit_program, "ask_on_quit_program"
9939 &setup.quick_switch, "quick_player_switch"
9943 &setup.input_on_focus, "input_on_focus"
9947 &setup.prefer_aga_graphics, "prefer_aga_graphics"
9951 &setup.prefer_lowpass_sounds, "prefer_lowpass_sounds"
9955 &setup.prefer_extra_panel_items, "prefer_extra_panel_items"
9959 &setup.game_speed_extended, "game_speed_extended"
9963 &setup.game_frame_delay, "game_frame_delay"
9967 &setup.sp_show_border_elements, "sp_show_border_elements"
9971 &setup.small_game_graphics, "small_game_graphics"
9975 &setup.show_load_save_buttons, "show_load_save_buttons"
9979 &setup.show_undo_redo_buttons, "show_undo_redo_buttons"
9983 &setup.scores_in_highscore_list, "scores_in_highscore_list"
9987 &setup.graphics_set, "graphics_set"
9991 &setup.sounds_set, "sounds_set"
9995 &setup.music_set, "music_set"
9999 &setup.override_level_graphics, "override_level_graphics"
10003 &setup.override_level_sounds, "override_level_sounds"
10007 &setup.override_level_music, "override_level_music"
10011 &setup.volume_simple, "volume_simple"
10015 &setup.volume_loops, "volume_loops"
10019 &setup.volume_music, "volume_music"
10023 &setup.network_mode, "network_mode"
10027 &setup.network_player_nr, "network_player"
10031 &setup.network_server_hostname, "network_server_hostname"
10035 &setup.touch.control_type, "touch.control_type"
10039 &setup.touch.move_distance, "touch.move_distance"
10043 &setup.touch.drop_distance, "touch.drop_distance"
10047 &setup.touch.transparency, "touch.transparency"
10051 &setup.touch.draw_outlined, "touch.draw_outlined"
10055 &setup.touch.draw_pressed, "touch.draw_pressed"
10059 &setup.touch.grid_xsize[0], "touch.virtual_buttons.0.xsize"
10063 &setup.touch.grid_ysize[0], "touch.virtual_buttons.0.ysize"
10067 &setup.touch.grid_xsize[1], "touch.virtual_buttons.1.xsize"
10071 &setup.touch.grid_ysize[1], "touch.virtual_buttons.1.ysize"
10075 static struct TokenInfo auto_setup_tokens[] =
10079 &setup.auto_setup.editor_zoom_tilesize, "editor.zoom_tilesize"
10083 static struct TokenInfo server_setup_tokens[] =
10087 &setup.player_uuid, "player_uuid"
10091 &setup.player_version, "player_version"
10095 &setup.use_api_server, TEST_PREFIX "use_api_server"
10099 &setup.api_server_hostname, TEST_PREFIX "api_server_hostname"
10103 &setup.api_server_password, TEST_PREFIX "api_server_password"
10107 &setup.ask_for_uploading_tapes, TEST_PREFIX "ask_for_uploading_tapes"
10111 &setup.ask_for_remaining_tapes, TEST_PREFIX "ask_for_remaining_tapes"
10115 &setup.provide_uploading_tapes, TEST_PREFIX "provide_uploading_tapes"
10119 &setup.ask_for_using_api_server,TEST_PREFIX "ask_for_using_api_server"
10123 &setup.has_remaining_tapes, TEST_PREFIX "has_remaining_tapes"
10127 static struct TokenInfo editor_setup_tokens[] =
10131 &setup.editor.el_classic, "editor.el_classic"
10135 &setup.editor.el_custom, "editor.el_custom"
10139 &setup.editor.el_user_defined, "editor.el_user_defined"
10143 &setup.editor.el_dynamic, "editor.el_dynamic"
10147 &setup.editor.el_headlines, "editor.el_headlines"
10151 &setup.editor.show_element_token, "editor.show_element_token"
10155 &setup.editor.show_read_only_warning, "editor.show_read_only_warning"
10159 static struct TokenInfo editor_cascade_setup_tokens[] =
10163 &setup.editor_cascade.el_bd, "editor.cascade.el_bd"
10167 &setup.editor_cascade.el_em, "editor.cascade.el_em"
10171 &setup.editor_cascade.el_emc, "editor.cascade.el_emc"
10175 &setup.editor_cascade.el_rnd, "editor.cascade.el_rnd"
10179 &setup.editor_cascade.el_sb, "editor.cascade.el_sb"
10183 &setup.editor_cascade.el_sp, "editor.cascade.el_sp"
10187 &setup.editor_cascade.el_dc, "editor.cascade.el_dc"
10191 &setup.editor_cascade.el_dx, "editor.cascade.el_dx"
10195 &setup.editor_cascade.el_mm, "editor.cascade.el_mm"
10199 &setup.editor_cascade.el_df, "editor.cascade.el_df"
10203 &setup.editor_cascade.el_chars, "editor.cascade.el_chars"
10207 &setup.editor_cascade.el_steel_chars, "editor.cascade.el_steel_chars"
10211 &setup.editor_cascade.el_ce, "editor.cascade.el_ce"
10215 &setup.editor_cascade.el_ge, "editor.cascade.el_ge"
10219 &setup.editor_cascade.el_ref, "editor.cascade.el_ref"
10223 &setup.editor_cascade.el_user, "editor.cascade.el_user"
10227 &setup.editor_cascade.el_dynamic, "editor.cascade.el_dynamic"
10231 static struct TokenInfo shortcut_setup_tokens[] =
10235 &setup.shortcut.save_game, "shortcut.save_game"
10239 &setup.shortcut.load_game, "shortcut.load_game"
10243 &setup.shortcut.toggle_pause, "shortcut.toggle_pause"
10247 &setup.shortcut.focus_player[0], "shortcut.focus_player_1"
10251 &setup.shortcut.focus_player[1], "shortcut.focus_player_2"
10255 &setup.shortcut.focus_player[2], "shortcut.focus_player_3"
10259 &setup.shortcut.focus_player[3], "shortcut.focus_player_4"
10263 &setup.shortcut.focus_player_all, "shortcut.focus_player_all"
10267 &setup.shortcut.tape_eject, "shortcut.tape_eject"
10271 &setup.shortcut.tape_extra, "shortcut.tape_extra"
10275 &setup.shortcut.tape_stop, "shortcut.tape_stop"
10279 &setup.shortcut.tape_pause, "shortcut.tape_pause"
10283 &setup.shortcut.tape_record, "shortcut.tape_record"
10287 &setup.shortcut.tape_play, "shortcut.tape_play"
10291 &setup.shortcut.sound_simple, "shortcut.sound_simple"
10295 &setup.shortcut.sound_loops, "shortcut.sound_loops"
10299 &setup.shortcut.sound_music, "shortcut.sound_music"
10303 &setup.shortcut.snap_left, "shortcut.snap_left"
10307 &setup.shortcut.snap_right, "shortcut.snap_right"
10311 &setup.shortcut.snap_up, "shortcut.snap_up"
10315 &setup.shortcut.snap_down, "shortcut.snap_down"
10319 static struct SetupInputInfo setup_input;
10320 static struct TokenInfo player_setup_tokens[] =
10324 &setup_input.use_joystick, ".use_joystick"
10328 &setup_input.joy.device_name, ".joy.device_name"
10332 &setup_input.joy.xleft, ".joy.xleft"
10336 &setup_input.joy.xmiddle, ".joy.xmiddle"
10340 &setup_input.joy.xright, ".joy.xright"
10344 &setup_input.joy.yupper, ".joy.yupper"
10348 &setup_input.joy.ymiddle, ".joy.ymiddle"
10352 &setup_input.joy.ylower, ".joy.ylower"
10356 &setup_input.joy.snap, ".joy.snap_field"
10360 &setup_input.joy.drop, ".joy.place_bomb"
10364 &setup_input.key.left, ".key.move_left"
10368 &setup_input.key.right, ".key.move_right"
10372 &setup_input.key.up, ".key.move_up"
10376 &setup_input.key.down, ".key.move_down"
10380 &setup_input.key.snap, ".key.snap_field"
10384 &setup_input.key.drop, ".key.place_bomb"
10388 static struct TokenInfo system_setup_tokens[] =
10392 &setup.system.sdl_renderdriver, "system.sdl_renderdriver"
10396 &setup.system.sdl_videodriver, "system.sdl_videodriver"
10400 &setup.system.sdl_audiodriver, "system.sdl_audiodriver"
10404 &setup.system.audio_fragment_size, "system.audio_fragment_size"
10408 static struct TokenInfo internal_setup_tokens[] =
10412 &setup.internal.program_title, "program_title"
10416 &setup.internal.program_version, "program_version"
10420 &setup.internal.program_author, "program_author"
10424 &setup.internal.program_email, "program_email"
10428 &setup.internal.program_website, "program_website"
10432 &setup.internal.program_copyright, "program_copyright"
10436 &setup.internal.program_company, "program_company"
10440 &setup.internal.program_icon_file, "program_icon_file"
10444 &setup.internal.default_graphics_set, "default_graphics_set"
10448 &setup.internal.default_sounds_set, "default_sounds_set"
10452 &setup.internal.default_music_set, "default_music_set"
10456 &setup.internal.fallback_graphics_file, "fallback_graphics_file"
10460 &setup.internal.fallback_sounds_file, "fallback_sounds_file"
10464 &setup.internal.fallback_music_file, "fallback_music_file"
10468 &setup.internal.default_level_series, "default_level_series"
10472 &setup.internal.default_window_width, "default_window_width"
10476 &setup.internal.default_window_height, "default_window_height"
10480 &setup.internal.choose_from_top_leveldir, "choose_from_top_leveldir"
10484 &setup.internal.show_scaling_in_title, "show_scaling_in_title"
10488 &setup.internal.create_user_levelset, "create_user_levelset"
10492 &setup.internal.menu_game, "menu_game"
10496 &setup.internal.menu_editor, "menu_editor"
10500 &setup.internal.menu_graphics, "menu_graphics"
10504 &setup.internal.menu_sound, "menu_sound"
10508 &setup.internal.menu_artwork, "menu_artwork"
10512 &setup.internal.menu_input, "menu_input"
10516 &setup.internal.menu_touch, "menu_touch"
10520 &setup.internal.menu_shortcuts, "menu_shortcuts"
10524 &setup.internal.menu_exit, "menu_exit"
10528 &setup.internal.menu_save_and_exit, "menu_save_and_exit"
10532 static struct TokenInfo debug_setup_tokens[] =
10536 &setup.debug.frame_delay[0], "debug.frame_delay_0"
10540 &setup.debug.frame_delay[1], "debug.frame_delay_1"
10544 &setup.debug.frame_delay[2], "debug.frame_delay_2"
10548 &setup.debug.frame_delay[3], "debug.frame_delay_3"
10552 &setup.debug.frame_delay[4], "debug.frame_delay_4"
10556 &setup.debug.frame_delay[5], "debug.frame_delay_5"
10560 &setup.debug.frame_delay[6], "debug.frame_delay_6"
10564 &setup.debug.frame_delay[7], "debug.frame_delay_7"
10568 &setup.debug.frame_delay[8], "debug.frame_delay_8"
10572 &setup.debug.frame_delay[9], "debug.frame_delay_9"
10576 &setup.debug.frame_delay_key[0], "debug.key.frame_delay_0"
10580 &setup.debug.frame_delay_key[1], "debug.key.frame_delay_1"
10584 &setup.debug.frame_delay_key[2], "debug.key.frame_delay_2"
10588 &setup.debug.frame_delay_key[3], "debug.key.frame_delay_3"
10592 &setup.debug.frame_delay_key[4], "debug.key.frame_delay_4"
10596 &setup.debug.frame_delay_key[5], "debug.key.frame_delay_5"
10600 &setup.debug.frame_delay_key[6], "debug.key.frame_delay_6"
10604 &setup.debug.frame_delay_key[7], "debug.key.frame_delay_7"
10608 &setup.debug.frame_delay_key[8], "debug.key.frame_delay_8"
10612 &setup.debug.frame_delay_key[9], "debug.key.frame_delay_9"
10616 &setup.debug.frame_delay_use_mod_key, "debug.frame_delay.use_mod_key"},
10619 &setup.debug.frame_delay_game_only, "debug.frame_delay.game_only"
10623 &setup.debug.show_frames_per_second, "debug.show_frames_per_second"
10627 &setup.debug.xsn_mode, "debug.xsn_mode"
10631 &setup.debug.xsn_percent, "debug.xsn_percent"
10635 static struct TokenInfo options_setup_tokens[] =
10639 &setup.options.verbose, "options.verbose"
10643 static void setSetupInfoToDefaults(struct SetupInfo *si)
10647 si->player_name = getStringCopy(getDefaultUserName(user.nr));
10649 si->multiple_users = TRUE;
10652 si->sound_loops = TRUE;
10653 si->sound_music = TRUE;
10654 si->sound_simple = TRUE;
10656 si->scroll_delay = TRUE;
10657 si->forced_scroll_delay = FALSE;
10658 si->scroll_delay_value = STD_SCROLL_DELAY;
10659 si->engine_snapshot_mode = getStringCopy(STR_SNAPSHOT_MODE_DEFAULT);
10660 si->engine_snapshot_memory = SNAPSHOT_MEMORY_DEFAULT;
10661 si->fade_screens = TRUE;
10662 si->autorecord = TRUE;
10663 si->show_titlescreen = TRUE;
10664 si->quick_doors = FALSE;
10665 si->team_mode = FALSE;
10666 si->handicap = TRUE;
10667 si->skip_levels = TRUE;
10668 si->increment_levels = TRUE;
10669 si->auto_play_next_level = TRUE;
10670 si->count_score_after_game = TRUE;
10671 si->show_scores_after_game = TRUE;
10672 si->time_limit = TRUE;
10673 si->fullscreen = FALSE;
10674 si->window_scaling_percent = STD_WINDOW_SCALING_PERCENT;
10675 si->window_scaling_quality = getStringCopy(SCALING_QUALITY_DEFAULT);
10676 si->screen_rendering_mode = getStringCopy(STR_SPECIAL_RENDERING_DEFAULT);
10677 si->vsync_mode = getStringCopy(STR_VSYNC_MODE_DEFAULT);
10678 si->ask_on_escape = TRUE;
10679 si->ask_on_escape_editor = TRUE;
10680 si->ask_on_game_over = TRUE;
10681 si->ask_on_quit_game = TRUE;
10682 si->ask_on_quit_program = TRUE;
10683 si->quick_switch = FALSE;
10684 si->input_on_focus = FALSE;
10685 si->prefer_aga_graphics = TRUE;
10686 si->prefer_lowpass_sounds = FALSE;
10687 si->prefer_extra_panel_items = TRUE;
10688 si->game_speed_extended = FALSE;
10689 si->game_frame_delay = GAME_FRAME_DELAY;
10690 si->sp_show_border_elements = FALSE;
10691 si->small_game_graphics = FALSE;
10692 si->show_load_save_buttons = FALSE;
10693 si->show_undo_redo_buttons = FALSE;
10694 si->scores_in_highscore_list = getStringCopy(STR_SCORES_TYPE_DEFAULT);
10696 si->graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
10697 si->sounds_set = getStringCopy(SND_CLASSIC_SUBDIR);
10698 si->music_set = getStringCopy(MUS_CLASSIC_SUBDIR);
10700 si->override_level_graphics = FALSE;
10701 si->override_level_sounds = FALSE;
10702 si->override_level_music = FALSE;
10704 si->volume_simple = 100; // percent
10705 si->volume_loops = 100; // percent
10706 si->volume_music = 100; // percent
10708 si->network_mode = FALSE;
10709 si->network_player_nr = 0; // first player
10710 si->network_server_hostname = getStringCopy(STR_NETWORK_AUTO_DETECT);
10712 si->touch.control_type = getStringCopy(TOUCH_CONTROL_DEFAULT);
10713 si->touch.move_distance = TOUCH_MOVE_DISTANCE_DEFAULT; // percent
10714 si->touch.drop_distance = TOUCH_DROP_DISTANCE_DEFAULT; // percent
10715 si->touch.transparency = TOUCH_TRANSPARENCY_DEFAULT; // percent
10716 si->touch.draw_outlined = TRUE;
10717 si->touch.draw_pressed = TRUE;
10719 for (i = 0; i < 2; i++)
10721 char *default_grid_button[6][2] =
10727 { "111222", " vv " },
10728 { "111222", " vv " }
10730 int grid_xsize = DEFAULT_GRID_XSIZE(i);
10731 int grid_ysize = DEFAULT_GRID_YSIZE(i);
10732 int min_xsize = MIN(6, grid_xsize);
10733 int min_ysize = MIN(6, grid_ysize);
10734 int startx = grid_xsize - min_xsize;
10735 int starty = grid_ysize - min_ysize;
10738 // virtual buttons grid can only be set to defaults if video is initialized
10739 // (this will be repeated if virtual buttons are not loaded from setup file)
10740 if (video.initialized)
10742 si->touch.grid_xsize[i] = grid_xsize;
10743 si->touch.grid_ysize[i] = grid_ysize;
10747 si->touch.grid_xsize[i] = -1;
10748 si->touch.grid_ysize[i] = -1;
10751 for (x = 0; x < MAX_GRID_XSIZE; x++)
10752 for (y = 0; y < MAX_GRID_YSIZE; y++)
10753 si->touch.grid_button[i][x][y] = CHAR_GRID_BUTTON_NONE;
10755 for (x = 0; x < min_xsize; x++)
10756 for (y = 0; y < min_ysize; y++)
10757 si->touch.grid_button[i][x][starty + y] =
10758 default_grid_button[y][0][x];
10760 for (x = 0; x < min_xsize; x++)
10761 for (y = 0; y < min_ysize; y++)
10762 si->touch.grid_button[i][startx + x][starty + y] =
10763 default_grid_button[y][1][x];
10766 si->touch.grid_initialized = video.initialized;
10768 si->editor.el_boulderdash = TRUE;
10769 si->editor.el_emerald_mine = TRUE;
10770 si->editor.el_emerald_mine_club = TRUE;
10771 si->editor.el_more = TRUE;
10772 si->editor.el_sokoban = TRUE;
10773 si->editor.el_supaplex = TRUE;
10774 si->editor.el_diamond_caves = TRUE;
10775 si->editor.el_dx_boulderdash = TRUE;
10777 si->editor.el_mirror_magic = TRUE;
10778 si->editor.el_deflektor = TRUE;
10780 si->editor.el_chars = TRUE;
10781 si->editor.el_steel_chars = TRUE;
10783 si->editor.el_classic = TRUE;
10784 si->editor.el_custom = TRUE;
10786 si->editor.el_user_defined = FALSE;
10787 si->editor.el_dynamic = TRUE;
10789 si->editor.el_headlines = TRUE;
10791 si->editor.show_element_token = FALSE;
10793 si->editor.show_read_only_warning = TRUE;
10795 si->editor.use_template_for_new_levels = TRUE;
10797 si->shortcut.save_game = DEFAULT_KEY_SAVE_GAME;
10798 si->shortcut.load_game = DEFAULT_KEY_LOAD_GAME;
10799 si->shortcut.toggle_pause = DEFAULT_KEY_TOGGLE_PAUSE;
10801 si->shortcut.focus_player[0] = DEFAULT_KEY_FOCUS_PLAYER_1;
10802 si->shortcut.focus_player[1] = DEFAULT_KEY_FOCUS_PLAYER_2;
10803 si->shortcut.focus_player[2] = DEFAULT_KEY_FOCUS_PLAYER_3;
10804 si->shortcut.focus_player[3] = DEFAULT_KEY_FOCUS_PLAYER_4;
10805 si->shortcut.focus_player_all = DEFAULT_KEY_FOCUS_PLAYER_ALL;
10807 si->shortcut.tape_eject = DEFAULT_KEY_TAPE_EJECT;
10808 si->shortcut.tape_extra = DEFAULT_KEY_TAPE_EXTRA;
10809 si->shortcut.tape_stop = DEFAULT_KEY_TAPE_STOP;
10810 si->shortcut.tape_pause = DEFAULT_KEY_TAPE_PAUSE;
10811 si->shortcut.tape_record = DEFAULT_KEY_TAPE_RECORD;
10812 si->shortcut.tape_play = DEFAULT_KEY_TAPE_PLAY;
10814 si->shortcut.sound_simple = DEFAULT_KEY_SOUND_SIMPLE;
10815 si->shortcut.sound_loops = DEFAULT_KEY_SOUND_LOOPS;
10816 si->shortcut.sound_music = DEFAULT_KEY_SOUND_MUSIC;
10818 si->shortcut.snap_left = DEFAULT_KEY_SNAP_LEFT;
10819 si->shortcut.snap_right = DEFAULT_KEY_SNAP_RIGHT;
10820 si->shortcut.snap_up = DEFAULT_KEY_SNAP_UP;
10821 si->shortcut.snap_down = DEFAULT_KEY_SNAP_DOWN;
10823 for (i = 0; i < MAX_PLAYERS; i++)
10825 si->input[i].use_joystick = FALSE;
10826 si->input[i].joy.device_name=getStringCopy(getDeviceNameFromJoystickNr(i));
10827 si->input[i].joy.xleft = JOYSTICK_XLEFT;
10828 si->input[i].joy.xmiddle = JOYSTICK_XMIDDLE;
10829 si->input[i].joy.xright = JOYSTICK_XRIGHT;
10830 si->input[i].joy.yupper = JOYSTICK_YUPPER;
10831 si->input[i].joy.ymiddle = JOYSTICK_YMIDDLE;
10832 si->input[i].joy.ylower = JOYSTICK_YLOWER;
10833 si->input[i].joy.snap = (i == 0 ? JOY_BUTTON_1 : 0);
10834 si->input[i].joy.drop = (i == 0 ? JOY_BUTTON_2 : 0);
10835 si->input[i].key.left = (i == 0 ? DEFAULT_KEY_LEFT : KSYM_UNDEFINED);
10836 si->input[i].key.right = (i == 0 ? DEFAULT_KEY_RIGHT : KSYM_UNDEFINED);
10837 si->input[i].key.up = (i == 0 ? DEFAULT_KEY_UP : KSYM_UNDEFINED);
10838 si->input[i].key.down = (i == 0 ? DEFAULT_KEY_DOWN : KSYM_UNDEFINED);
10839 si->input[i].key.snap = (i == 0 ? DEFAULT_KEY_SNAP : KSYM_UNDEFINED);
10840 si->input[i].key.drop = (i == 0 ? DEFAULT_KEY_DROP : KSYM_UNDEFINED);
10843 si->system.sdl_renderdriver = getStringCopy(ARG_DEFAULT);
10844 si->system.sdl_videodriver = getStringCopy(ARG_DEFAULT);
10845 si->system.sdl_audiodriver = getStringCopy(ARG_DEFAULT);
10846 si->system.audio_fragment_size = DEFAULT_AUDIO_FRAGMENT_SIZE;
10848 si->internal.program_title = getStringCopy(PROGRAM_TITLE_STRING);
10849 si->internal.program_version = getStringCopy(getProgramRealVersionString());
10850 si->internal.program_author = getStringCopy(PROGRAM_AUTHOR_STRING);
10851 si->internal.program_email = getStringCopy(PROGRAM_EMAIL_STRING);
10852 si->internal.program_website = getStringCopy(PROGRAM_WEBSITE_STRING);
10853 si->internal.program_copyright = getStringCopy(PROGRAM_COPYRIGHT_STRING);
10854 si->internal.program_company = getStringCopy(PROGRAM_COMPANY_STRING);
10856 si->internal.program_icon_file = getStringCopy(PROGRAM_ICON_FILENAME);
10858 si->internal.default_graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
10859 si->internal.default_sounds_set = getStringCopy(SND_CLASSIC_SUBDIR);
10860 si->internal.default_music_set = getStringCopy(MUS_CLASSIC_SUBDIR);
10862 si->internal.fallback_graphics_file = getStringCopy(UNDEFINED_FILENAME);
10863 si->internal.fallback_sounds_file = getStringCopy(UNDEFINED_FILENAME);
10864 si->internal.fallback_music_file = getStringCopy(UNDEFINED_FILENAME);
10866 si->internal.default_level_series = getStringCopy(UNDEFINED_LEVELSET);
10867 si->internal.choose_from_top_leveldir = FALSE;
10868 si->internal.show_scaling_in_title = TRUE;
10869 si->internal.create_user_levelset = TRUE;
10871 si->internal.default_window_width = WIN_XSIZE_DEFAULT;
10872 si->internal.default_window_height = WIN_YSIZE_DEFAULT;
10874 si->debug.frame_delay[0] = DEFAULT_FRAME_DELAY_0;
10875 si->debug.frame_delay[1] = DEFAULT_FRAME_DELAY_1;
10876 si->debug.frame_delay[2] = DEFAULT_FRAME_DELAY_2;
10877 si->debug.frame_delay[3] = DEFAULT_FRAME_DELAY_3;
10878 si->debug.frame_delay[4] = DEFAULT_FRAME_DELAY_4;
10879 si->debug.frame_delay[5] = DEFAULT_FRAME_DELAY_5;
10880 si->debug.frame_delay[6] = DEFAULT_FRAME_DELAY_6;
10881 si->debug.frame_delay[7] = DEFAULT_FRAME_DELAY_7;
10882 si->debug.frame_delay[8] = DEFAULT_FRAME_DELAY_8;
10883 si->debug.frame_delay[9] = DEFAULT_FRAME_DELAY_9;
10885 si->debug.frame_delay_key[0] = DEFAULT_KEY_FRAME_DELAY_0;
10886 si->debug.frame_delay_key[1] = DEFAULT_KEY_FRAME_DELAY_1;
10887 si->debug.frame_delay_key[2] = DEFAULT_KEY_FRAME_DELAY_2;
10888 si->debug.frame_delay_key[3] = DEFAULT_KEY_FRAME_DELAY_3;
10889 si->debug.frame_delay_key[4] = DEFAULT_KEY_FRAME_DELAY_4;
10890 si->debug.frame_delay_key[5] = DEFAULT_KEY_FRAME_DELAY_5;
10891 si->debug.frame_delay_key[6] = DEFAULT_KEY_FRAME_DELAY_6;
10892 si->debug.frame_delay_key[7] = DEFAULT_KEY_FRAME_DELAY_7;
10893 si->debug.frame_delay_key[8] = DEFAULT_KEY_FRAME_DELAY_8;
10894 si->debug.frame_delay_key[9] = DEFAULT_KEY_FRAME_DELAY_9;
10896 si->debug.frame_delay_use_mod_key = DEFAULT_FRAME_DELAY_USE_MOD_KEY;
10897 si->debug.frame_delay_game_only = DEFAULT_FRAME_DELAY_GAME_ONLY;
10899 si->debug.show_frames_per_second = FALSE;
10901 si->debug.xsn_mode = AUTO;
10902 si->debug.xsn_percent = 0;
10904 si->options.verbose = FALSE;
10906 #if defined(PLATFORM_ANDROID)
10907 si->fullscreen = TRUE;
10910 setHideSetupEntry(&setup.debug.xsn_mode);
10913 static void setSetupInfoToDefaults_AutoSetup(struct SetupInfo *si)
10915 si->auto_setup.editor_zoom_tilesize = MINI_TILESIZE;
10918 static void setSetupInfoToDefaults_ServerSetup(struct SetupInfo *si)
10920 si->player_uuid = NULL; // (will be set later)
10921 si->player_version = 1; // (will be set later)
10923 si->use_api_server = TRUE;
10924 si->api_server_hostname = getStringCopy(API_SERVER_HOSTNAME);
10925 si->api_server_password = getStringCopy(UNDEFINED_PASSWORD);
10926 si->ask_for_uploading_tapes = TRUE;
10927 si->ask_for_remaining_tapes = FALSE;
10928 si->provide_uploading_tapes = TRUE;
10929 si->ask_for_using_api_server = TRUE;
10930 si->has_remaining_tapes = FALSE;
10933 static void setSetupInfoToDefaults_EditorCascade(struct SetupInfo *si)
10935 si->editor_cascade.el_bd = TRUE;
10936 si->editor_cascade.el_em = TRUE;
10937 si->editor_cascade.el_emc = TRUE;
10938 si->editor_cascade.el_rnd = TRUE;
10939 si->editor_cascade.el_sb = TRUE;
10940 si->editor_cascade.el_sp = TRUE;
10941 si->editor_cascade.el_dc = TRUE;
10942 si->editor_cascade.el_dx = TRUE;
10944 si->editor_cascade.el_mm = TRUE;
10945 si->editor_cascade.el_df = TRUE;
10947 si->editor_cascade.el_chars = FALSE;
10948 si->editor_cascade.el_steel_chars = FALSE;
10949 si->editor_cascade.el_ce = FALSE;
10950 si->editor_cascade.el_ge = FALSE;
10951 si->editor_cascade.el_ref = FALSE;
10952 si->editor_cascade.el_user = FALSE;
10953 si->editor_cascade.el_dynamic = FALSE;
10956 #define MAX_HIDE_SETUP_TOKEN_SIZE 20
10958 static char *getHideSetupToken(void *setup_value)
10960 static char hide_setup_token[MAX_HIDE_SETUP_TOKEN_SIZE];
10962 if (setup_value != NULL)
10963 snprintf(hide_setup_token, MAX_HIDE_SETUP_TOKEN_SIZE, "%p", setup_value);
10965 return hide_setup_token;
10968 void setHideSetupEntry(void *setup_value)
10970 char *hide_setup_token = getHideSetupToken(setup_value);
10972 if (hide_setup_hash == NULL)
10973 hide_setup_hash = newSetupFileHash();
10975 if (setup_value != NULL)
10976 setHashEntry(hide_setup_hash, hide_setup_token, "");
10979 void removeHideSetupEntry(void *setup_value)
10981 char *hide_setup_token = getHideSetupToken(setup_value);
10983 if (setup_value != NULL)
10984 removeHashEntry(hide_setup_hash, hide_setup_token);
10987 boolean hideSetupEntry(void *setup_value)
10989 char *hide_setup_token = getHideSetupToken(setup_value);
10991 return (setup_value != NULL &&
10992 getHashEntry(hide_setup_hash, hide_setup_token) != NULL);
10995 static void setSetupInfoFromTokenText(SetupFileHash *setup_file_hash,
10996 struct TokenInfo *token_info,
10997 int token_nr, char *token_text)
10999 char *token_hide_text = getStringCat2(token_text, ".hide");
11000 char *token_hide_value = getHashEntry(setup_file_hash, token_hide_text);
11002 // set the value of this setup option in the setup option structure
11003 setSetupInfo(token_info, token_nr, getHashEntry(setup_file_hash, token_text));
11005 // check if this setup option should be hidden in the setup menu
11006 if (token_hide_value != NULL && get_boolean_from_string(token_hide_value))
11007 setHideSetupEntry(token_info[token_nr].value);
11009 free(token_hide_text);
11012 static void setSetupInfoFromTokenInfo(SetupFileHash *setup_file_hash,
11013 struct TokenInfo *token_info,
11016 setSetupInfoFromTokenText(setup_file_hash, token_info, token_nr,
11017 token_info[token_nr].text);
11020 static void decodeSetupFileHash_Default(SetupFileHash *setup_file_hash)
11024 if (!setup_file_hash)
11027 for (i = 0; i < ARRAY_SIZE(global_setup_tokens); i++)
11028 setSetupInfoFromTokenInfo(setup_file_hash, global_setup_tokens, i);
11030 setup.touch.grid_initialized = TRUE;
11031 for (i = 0; i < 2; i++)
11033 int grid_xsize = setup.touch.grid_xsize[i];
11034 int grid_ysize = setup.touch.grid_ysize[i];
11037 // if virtual buttons are not loaded from setup file, repeat initializing
11038 // virtual buttons grid with default values later when video is initialized
11039 if (grid_xsize == -1 ||
11042 setup.touch.grid_initialized = FALSE;
11047 for (y = 0; y < grid_ysize; y++)
11049 char token_string[MAX_LINE_LEN];
11051 sprintf(token_string, "touch.virtual_buttons.%d.%02d", i, y);
11053 char *value_string = getHashEntry(setup_file_hash, token_string);
11055 if (value_string == NULL)
11058 for (x = 0; x < grid_xsize; x++)
11060 char c = value_string[x];
11062 setup.touch.grid_button[i][x][y] =
11063 (c == '.' ? CHAR_GRID_BUTTON_NONE : c);
11068 for (i = 0; i < ARRAY_SIZE(editor_setup_tokens); i++)
11069 setSetupInfoFromTokenInfo(setup_file_hash, editor_setup_tokens, i);
11071 for (i = 0; i < ARRAY_SIZE(shortcut_setup_tokens); i++)
11072 setSetupInfoFromTokenInfo(setup_file_hash, shortcut_setup_tokens, i);
11074 for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
11078 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
11080 setup_input = setup.input[pnr];
11081 for (i = 0; i < ARRAY_SIZE(player_setup_tokens); i++)
11083 char full_token[100];
11085 sprintf(full_token, "%s%s", prefix, player_setup_tokens[i].text);
11086 setSetupInfoFromTokenText(setup_file_hash, player_setup_tokens, i,
11089 setup.input[pnr] = setup_input;
11092 for (i = 0; i < ARRAY_SIZE(system_setup_tokens); i++)
11093 setSetupInfoFromTokenInfo(setup_file_hash, system_setup_tokens, i);
11095 for (i = 0; i < ARRAY_SIZE(internal_setup_tokens); i++)
11096 setSetupInfoFromTokenInfo(setup_file_hash, internal_setup_tokens, i);
11098 for (i = 0; i < ARRAY_SIZE(debug_setup_tokens); i++)
11099 setSetupInfoFromTokenInfo(setup_file_hash, debug_setup_tokens, i);
11101 for (i = 0; i < ARRAY_SIZE(options_setup_tokens); i++)
11102 setSetupInfoFromTokenInfo(setup_file_hash, options_setup_tokens, i);
11104 setHideRelatedSetupEntries();
11107 static void decodeSetupFileHash_AutoSetup(SetupFileHash *setup_file_hash)
11111 if (!setup_file_hash)
11114 for (i = 0; i < ARRAY_SIZE(auto_setup_tokens); i++)
11115 setSetupInfo(auto_setup_tokens, i,
11116 getHashEntry(setup_file_hash,
11117 auto_setup_tokens[i].text));
11120 static void decodeSetupFileHash_ServerSetup(SetupFileHash *setup_file_hash)
11124 if (!setup_file_hash)
11127 for (i = 0; i < ARRAY_SIZE(server_setup_tokens); i++)
11128 setSetupInfo(server_setup_tokens, i,
11129 getHashEntry(setup_file_hash,
11130 server_setup_tokens[i].text));
11133 static void decodeSetupFileHash_EditorCascade(SetupFileHash *setup_file_hash)
11137 if (!setup_file_hash)
11140 for (i = 0; i < ARRAY_SIZE(editor_cascade_setup_tokens); i++)
11141 setSetupInfo(editor_cascade_setup_tokens, i,
11142 getHashEntry(setup_file_hash,
11143 editor_cascade_setup_tokens[i].text));
11146 void LoadUserNames(void)
11148 int last_user_nr = user.nr;
11151 if (global.user_names != NULL)
11153 for (i = 0; i < MAX_PLAYER_NAMES; i++)
11154 checked_free(global.user_names[i]);
11156 checked_free(global.user_names);
11159 global.user_names = checked_calloc(MAX_PLAYER_NAMES * sizeof(char *));
11161 for (i = 0; i < MAX_PLAYER_NAMES; i++)
11165 SetupFileHash *setup_file_hash = loadSetupFileHash(getSetupFilename());
11167 if (setup_file_hash)
11169 char *player_name = getHashEntry(setup_file_hash, "player_name");
11171 global.user_names[i] = getFixedUserName(player_name);
11173 freeSetupFileHash(setup_file_hash);
11176 if (global.user_names[i] == NULL)
11177 global.user_names[i] = getStringCopy(getDefaultUserName(i));
11180 user.nr = last_user_nr;
11183 void LoadSetupFromFilename(char *filename)
11185 SetupFileHash *setup_file_hash = loadSetupFileHash(filename);
11187 if (setup_file_hash)
11189 decodeSetupFileHash_Default(setup_file_hash);
11191 freeSetupFileHash(setup_file_hash);
11195 Debug("setup", "using default setup values");
11199 static void LoadSetup_SpecialPostProcessing(void)
11201 char *player_name_new;
11203 // needed to work around problems with fixed length strings
11204 player_name_new = getFixedUserName(setup.player_name);
11205 free(setup.player_name);
11206 setup.player_name = player_name_new;
11208 // "scroll_delay: on(3) / off(0)" was replaced by scroll delay value
11209 if (setup.scroll_delay == FALSE)
11211 setup.scroll_delay_value = MIN_SCROLL_DELAY;
11212 setup.scroll_delay = TRUE; // now always "on"
11215 // make sure that scroll delay value stays inside valid range
11216 setup.scroll_delay_value =
11217 MIN(MAX(MIN_SCROLL_DELAY, setup.scroll_delay_value), MAX_SCROLL_DELAY);
11220 void LoadSetup_Default(void)
11224 // always start with reliable default values
11225 setSetupInfoToDefaults(&setup);
11227 // try to load setup values from default setup file
11228 filename = getDefaultSetupFilename();
11230 if (fileExists(filename))
11231 LoadSetupFromFilename(filename);
11233 // try to load setup values from user setup file
11234 filename = getSetupFilename();
11236 LoadSetupFromFilename(filename);
11238 LoadSetup_SpecialPostProcessing();
11241 void LoadSetup_AutoSetup(void)
11243 char *filename = getPath2(getSetupDir(), AUTOSETUP_FILENAME);
11244 SetupFileHash *setup_file_hash = NULL;
11246 // always start with reliable default values
11247 setSetupInfoToDefaults_AutoSetup(&setup);
11249 setup_file_hash = loadSetupFileHash(filename);
11251 if (setup_file_hash)
11253 decodeSetupFileHash_AutoSetup(setup_file_hash);
11255 freeSetupFileHash(setup_file_hash);
11261 void LoadSetup_ServerSetup(void)
11263 char *filename = getPath2(getSetupDir(), SERVERSETUP_FILENAME);
11264 SetupFileHash *setup_file_hash = NULL;
11266 // always start with reliable default values
11267 setSetupInfoToDefaults_ServerSetup(&setup);
11269 setup_file_hash = loadSetupFileHash(filename);
11271 if (setup_file_hash)
11273 decodeSetupFileHash_ServerSetup(setup_file_hash);
11275 freeSetupFileHash(setup_file_hash);
11280 if (setup.player_uuid == NULL)
11282 // player UUID does not yet exist in setup file
11283 setup.player_uuid = getStringCopy(getUUID());
11284 setup.player_version = 2;
11286 SaveSetup_ServerSetup();
11290 void LoadSetup_EditorCascade(void)
11292 char *filename = getPath2(getSetupDir(), EDITORCASCADE_FILENAME);
11293 SetupFileHash *setup_file_hash = NULL;
11295 // always start with reliable default values
11296 setSetupInfoToDefaults_EditorCascade(&setup);
11298 setup_file_hash = loadSetupFileHash(filename);
11300 if (setup_file_hash)
11302 decodeSetupFileHash_EditorCascade(setup_file_hash);
11304 freeSetupFileHash(setup_file_hash);
11310 void LoadSetup(void)
11312 LoadSetup_Default();
11313 LoadSetup_AutoSetup();
11314 LoadSetup_ServerSetup();
11315 LoadSetup_EditorCascade();
11318 static void addGameControllerMappingToHash(SetupFileHash *mappings_hash,
11319 char *mapping_line)
11321 char mapping_guid[MAX_LINE_LEN];
11322 char *mapping_start, *mapping_end;
11324 // get GUID from game controller mapping line: copy complete line
11325 strncpy(mapping_guid, mapping_line, MAX_LINE_LEN - 1);
11326 mapping_guid[MAX_LINE_LEN - 1] = '\0';
11328 // get GUID from game controller mapping line: cut after GUID part
11329 mapping_start = strchr(mapping_guid, ',');
11330 if (mapping_start != NULL)
11331 *mapping_start = '\0';
11333 // cut newline from game controller mapping line
11334 mapping_end = strchr(mapping_line, '\n');
11335 if (mapping_end != NULL)
11336 *mapping_end = '\0';
11338 // add mapping entry to game controller mappings hash
11339 setHashEntry(mappings_hash, mapping_guid, mapping_line);
11342 static void LoadSetup_ReadGameControllerMappings(SetupFileHash *mappings_hash,
11347 if (!(file = fopen(filename, MODE_READ)))
11349 Warn("cannot read game controller mappings file '%s'", filename);
11354 while (!feof(file))
11356 char line[MAX_LINE_LEN];
11358 if (!fgets(line, MAX_LINE_LEN, file))
11361 addGameControllerMappingToHash(mappings_hash, line);
11367 void SaveSetup_Default(void)
11369 char *filename = getSetupFilename();
11373 InitUserDataDirectory();
11375 if (!(file = fopen(filename, MODE_WRITE)))
11377 Warn("cannot write setup file '%s'", filename);
11382 fprintFileHeader(file, SETUP_FILENAME);
11384 for (i = 0; i < ARRAY_SIZE(global_setup_tokens); i++)
11386 // just to make things nicer :)
11387 if (global_setup_tokens[i].value == &setup.multiple_users ||
11388 global_setup_tokens[i].value == &setup.sound ||
11389 global_setup_tokens[i].value == &setup.graphics_set ||
11390 global_setup_tokens[i].value == &setup.volume_simple ||
11391 global_setup_tokens[i].value == &setup.network_mode ||
11392 global_setup_tokens[i].value == &setup.touch.control_type ||
11393 global_setup_tokens[i].value == &setup.touch.grid_xsize[0] ||
11394 global_setup_tokens[i].value == &setup.touch.grid_xsize[1])
11395 fprintf(file, "\n");
11397 fprintf(file, "%s\n", getSetupLine(global_setup_tokens, "", i));
11400 for (i = 0; i < 2; i++)
11402 int grid_xsize = setup.touch.grid_xsize[i];
11403 int grid_ysize = setup.touch.grid_ysize[i];
11406 fprintf(file, "\n");
11408 for (y = 0; y < grid_ysize; y++)
11410 char token_string[MAX_LINE_LEN];
11411 char value_string[MAX_LINE_LEN];
11413 sprintf(token_string, "touch.virtual_buttons.%d.%02d", i, y);
11415 for (x = 0; x < grid_xsize; x++)
11417 char c = setup.touch.grid_button[i][x][y];
11419 value_string[x] = (c == CHAR_GRID_BUTTON_NONE ? '.' : c);
11422 value_string[grid_xsize] = '\0';
11424 fprintf(file, "%s\n", getFormattedSetupEntry(token_string, value_string));
11428 fprintf(file, "\n");
11429 for (i = 0; i < ARRAY_SIZE(editor_setup_tokens); i++)
11430 fprintf(file, "%s\n", getSetupLine(editor_setup_tokens, "", i));
11432 fprintf(file, "\n");
11433 for (i = 0; i < ARRAY_SIZE(shortcut_setup_tokens); i++)
11434 fprintf(file, "%s\n", getSetupLine(shortcut_setup_tokens, "", i));
11436 for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
11440 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
11441 fprintf(file, "\n");
11443 setup_input = setup.input[pnr];
11444 for (i = 0; i < ARRAY_SIZE(player_setup_tokens); i++)
11445 fprintf(file, "%s\n", getSetupLine(player_setup_tokens, prefix, i));
11448 fprintf(file, "\n");
11449 for (i = 0; i < ARRAY_SIZE(system_setup_tokens); i++)
11450 fprintf(file, "%s\n", getSetupLine(system_setup_tokens, "", i));
11452 // (internal setup values not saved to user setup file)
11454 fprintf(file, "\n");
11455 for (i = 0; i < ARRAY_SIZE(debug_setup_tokens); i++)
11456 if (!strPrefix(debug_setup_tokens[i].text, "debug.xsn_") ||
11457 setup.debug.xsn_mode != AUTO)
11458 fprintf(file, "%s\n", getSetupLine(debug_setup_tokens, "", i));
11460 fprintf(file, "\n");
11461 for (i = 0; i < ARRAY_SIZE(options_setup_tokens); i++)
11462 fprintf(file, "%s\n", getSetupLine(options_setup_tokens, "", i));
11466 SetFilePermissions(filename, PERMS_PRIVATE);
11469 void SaveSetup_AutoSetup(void)
11471 char *filename = getPath2(getSetupDir(), AUTOSETUP_FILENAME);
11475 InitUserDataDirectory();
11477 if (!(file = fopen(filename, MODE_WRITE)))
11479 Warn("cannot write auto setup file '%s'", filename);
11486 fprintFileHeader(file, AUTOSETUP_FILENAME);
11488 for (i = 0; i < ARRAY_SIZE(auto_setup_tokens); i++)
11489 fprintf(file, "%s\n", getSetupLine(auto_setup_tokens, "", i));
11493 SetFilePermissions(filename, PERMS_PRIVATE);
11498 void SaveSetup_ServerSetup(void)
11500 char *filename = getPath2(getSetupDir(), SERVERSETUP_FILENAME);
11504 InitUserDataDirectory();
11506 if (!(file = fopen(filename, MODE_WRITE)))
11508 Warn("cannot write server setup file '%s'", filename);
11515 fprintFileHeader(file, SERVERSETUP_FILENAME);
11517 for (i = 0; i < ARRAY_SIZE(server_setup_tokens); i++)
11519 // just to make things nicer :)
11520 if (server_setup_tokens[i].value == &setup.use_api_server)
11521 fprintf(file, "\n");
11523 fprintf(file, "%s\n", getSetupLine(server_setup_tokens, "", i));
11528 SetFilePermissions(filename, PERMS_PRIVATE);
11533 void SaveSetup_EditorCascade(void)
11535 char *filename = getPath2(getSetupDir(), EDITORCASCADE_FILENAME);
11539 InitUserDataDirectory();
11541 if (!(file = fopen(filename, MODE_WRITE)))
11543 Warn("cannot write editor cascade state file '%s'", filename);
11550 fprintFileHeader(file, EDITORCASCADE_FILENAME);
11552 for (i = 0; i < ARRAY_SIZE(editor_cascade_setup_tokens); i++)
11553 fprintf(file, "%s\n", getSetupLine(editor_cascade_setup_tokens, "", i));
11557 SetFilePermissions(filename, PERMS_PRIVATE);
11562 void SaveSetup(void)
11564 SaveSetup_Default();
11565 SaveSetup_AutoSetup();
11566 SaveSetup_ServerSetup();
11567 SaveSetup_EditorCascade();
11570 static void SaveSetup_WriteGameControllerMappings(SetupFileHash *mappings_hash,
11575 if (!(file = fopen(filename, MODE_WRITE)))
11577 Warn("cannot write game controller mappings file '%s'", filename);
11582 BEGIN_HASH_ITERATION(mappings_hash, itr)
11584 fprintf(file, "%s\n", HASH_ITERATION_VALUE(itr));
11586 END_HASH_ITERATION(mappings_hash, itr)
11591 void SaveSetup_AddGameControllerMapping(char *mapping)
11593 char *filename = getPath2(getSetupDir(), GAMECONTROLLER_BASENAME);
11594 SetupFileHash *mappings_hash = newSetupFileHash();
11596 InitUserDataDirectory();
11598 // load existing personal game controller mappings
11599 LoadSetup_ReadGameControllerMappings(mappings_hash, filename);
11601 // add new mapping to personal game controller mappings
11602 addGameControllerMappingToHash(mappings_hash, mapping);
11604 // save updated personal game controller mappings
11605 SaveSetup_WriteGameControllerMappings(mappings_hash, filename);
11607 freeSetupFileHash(mappings_hash);
11611 void LoadCustomElementDescriptions(void)
11613 char *filename = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
11614 SetupFileHash *setup_file_hash;
11617 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
11619 if (element_info[i].custom_description != NULL)
11621 free(element_info[i].custom_description);
11622 element_info[i].custom_description = NULL;
11626 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
11629 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
11631 char *token = getStringCat2(element_info[i].token_name, ".name");
11632 char *value = getHashEntry(setup_file_hash, token);
11635 element_info[i].custom_description = getStringCopy(value);
11640 freeSetupFileHash(setup_file_hash);
11643 static int getElementFromToken(char *token)
11645 char *value = getHashEntry(element_token_hash, token);
11648 return atoi(value);
11650 Warn("unknown element token '%s'", token);
11652 return EL_UNDEFINED;
11655 void FreeGlobalAnimEventInfo(void)
11657 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
11659 if (gaei->event_list == NULL)
11664 for (i = 0; i < gaei->num_event_lists; i++)
11666 checked_free(gaei->event_list[i]->event_value);
11667 checked_free(gaei->event_list[i]);
11670 checked_free(gaei->event_list);
11672 gaei->event_list = NULL;
11673 gaei->num_event_lists = 0;
11676 static int AddGlobalAnimEventList(void)
11678 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
11679 int list_pos = gaei->num_event_lists++;
11681 gaei->event_list = checked_realloc(gaei->event_list, gaei->num_event_lists *
11682 sizeof(struct GlobalAnimEventListInfo *));
11684 gaei->event_list[list_pos] =
11685 checked_calloc(sizeof(struct GlobalAnimEventListInfo));
11687 struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
11689 gaeli->event_value = NULL;
11690 gaeli->num_event_values = 0;
11695 static int AddGlobalAnimEventValue(int list_pos, int event_value)
11697 // do not add empty global animation events
11698 if (event_value == ANIM_EVENT_NONE)
11701 // if list position is undefined, create new list
11702 if (list_pos == ANIM_EVENT_UNDEFINED)
11703 list_pos = AddGlobalAnimEventList();
11705 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
11706 struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
11707 int value_pos = gaeli->num_event_values++;
11709 gaeli->event_value = checked_realloc(gaeli->event_value,
11710 gaeli->num_event_values * sizeof(int *));
11712 gaeli->event_value[value_pos] = event_value;
11717 int GetGlobalAnimEventValue(int list_pos, int value_pos)
11719 if (list_pos == ANIM_EVENT_UNDEFINED)
11720 return ANIM_EVENT_NONE;
11722 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
11723 struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
11725 return gaeli->event_value[value_pos];
11728 int GetGlobalAnimEventValueCount(int list_pos)
11730 if (list_pos == ANIM_EVENT_UNDEFINED)
11733 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
11734 struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
11736 return gaeli->num_event_values;
11739 // This function checks if a string <s> of the format "string1, string2, ..."
11740 // exactly contains a string <s_contained>.
11742 static boolean string_has_parameter(char *s, char *s_contained)
11746 if (s == NULL || s_contained == NULL)
11749 if (strlen(s_contained) > strlen(s))
11752 if (strncmp(s, s_contained, strlen(s_contained)) == 0)
11754 char next_char = s[strlen(s_contained)];
11756 // check if next character is delimiter or whitespace
11757 return (next_char == ',' || next_char == '\0' ||
11758 next_char == ' ' || next_char == '\t' ? TRUE : FALSE);
11761 // check if string contains another parameter string after a comma
11762 substring = strchr(s, ',');
11763 if (substring == NULL) // string does not contain a comma
11766 // advance string pointer to next character after the comma
11769 // skip potential whitespaces after the comma
11770 while (*substring == ' ' || *substring == '\t')
11773 return string_has_parameter(substring, s_contained);
11776 static int get_anim_parameter_value(char *s)
11778 int event_value[] =
11786 char *pattern_1[] =
11794 char *pattern_2 = ".part_";
11795 char *matching_char = NULL;
11797 int pattern_1_len = 0;
11798 int result = ANIM_EVENT_NONE;
11801 for (i = 0; i < ARRAY_SIZE(event_value); i++)
11803 matching_char = strstr(s_ptr, pattern_1[i]);
11804 pattern_1_len = strlen(pattern_1[i]);
11805 result = event_value[i];
11807 if (matching_char != NULL)
11811 if (matching_char == NULL)
11812 return ANIM_EVENT_NONE;
11814 s_ptr = matching_char + pattern_1_len;
11816 // check for main animation number ("anim_X" or "anim_XX")
11817 if (*s_ptr >= '0' && *s_ptr <= '9')
11819 int gic_anim_nr = (*s_ptr++ - '0');
11821 if (*s_ptr >= '0' && *s_ptr <= '9')
11822 gic_anim_nr = 10 * gic_anim_nr + (*s_ptr++ - '0');
11824 if (gic_anim_nr < 1 || gic_anim_nr > MAX_GLOBAL_ANIMS)
11825 return ANIM_EVENT_NONE;
11827 result |= gic_anim_nr << ANIM_EVENT_ANIM_BIT;
11831 // invalid main animation number specified
11833 return ANIM_EVENT_NONE;
11836 // check for animation part number ("part_X" or "part_XX") (optional)
11837 if (strPrefix(s_ptr, pattern_2))
11839 s_ptr += strlen(pattern_2);
11841 if (*s_ptr >= '0' && *s_ptr <= '9')
11843 int gic_part_nr = (*s_ptr++ - '0');
11845 if (*s_ptr >= '0' && *s_ptr <= '9')
11846 gic_part_nr = 10 * gic_part_nr + (*s_ptr++ - '0');
11848 if (gic_part_nr < 1 || gic_part_nr > MAX_GLOBAL_ANIM_PARTS)
11849 return ANIM_EVENT_NONE;
11851 result |= gic_part_nr << ANIM_EVENT_PART_BIT;
11855 // invalid animation part number specified
11857 return ANIM_EVENT_NONE;
11861 // discard result if next character is neither delimiter nor whitespace
11862 if (!(*s_ptr == ',' || *s_ptr == '\0' ||
11863 *s_ptr == ' ' || *s_ptr == '\t'))
11864 return ANIM_EVENT_NONE;
11869 static int get_anim_parameter_values(char *s)
11871 int list_pos = ANIM_EVENT_UNDEFINED;
11872 int event_value = ANIM_EVENT_DEFAULT;
11874 if (string_has_parameter(s, "any"))
11875 event_value |= ANIM_EVENT_ANY;
11877 if (string_has_parameter(s, "click:self") ||
11878 string_has_parameter(s, "click") ||
11879 string_has_parameter(s, "self"))
11880 event_value |= ANIM_EVENT_SELF;
11882 if (string_has_parameter(s, "unclick:any"))
11883 event_value |= ANIM_EVENT_UNCLICK_ANY;
11885 // if animation event found, add it to global animation event list
11886 if (event_value != ANIM_EVENT_NONE)
11887 list_pos = AddGlobalAnimEventValue(list_pos, event_value);
11891 // add optional "click:anim_X" or "click:anim_X.part_X" parameter
11892 event_value = get_anim_parameter_value(s);
11894 // if animation event found, add it to global animation event list
11895 if (event_value != ANIM_EVENT_NONE)
11896 list_pos = AddGlobalAnimEventValue(list_pos, event_value);
11898 // continue with next part of the string, starting with next comma
11899 s = strchr(s + 1, ',');
11905 static int get_anim_action_parameter_value(char *token)
11907 // check most common default case first to massively speed things up
11908 if (strEqual(token, ARG_UNDEFINED))
11909 return ANIM_EVENT_ACTION_NONE;
11911 int result = getImageIDFromToken(token);
11915 char *gfx_token = getStringCat2("gfx.", token);
11917 result = getImageIDFromToken(gfx_token);
11919 checked_free(gfx_token);
11924 Key key = getKeyFromX11KeyName(token);
11926 if (key != KSYM_UNDEFINED)
11927 result = -(int)key;
11931 result = ANIM_EVENT_ACTION_NONE;
11936 int get_parameter_value(char *value_raw, char *suffix, int type)
11938 char *value = getStringToLower(value_raw);
11939 int result = 0; // probably a save default value
11941 if (strEqual(suffix, ".direction"))
11943 result = (strEqual(value, "left") ? MV_LEFT :
11944 strEqual(value, "right") ? MV_RIGHT :
11945 strEqual(value, "up") ? MV_UP :
11946 strEqual(value, "down") ? MV_DOWN : MV_NONE);
11948 else if (strEqual(suffix, ".position"))
11950 result = (strEqual(value, "left") ? POS_LEFT :
11951 strEqual(value, "right") ? POS_RIGHT :
11952 strEqual(value, "top") ? POS_TOP :
11953 strEqual(value, "upper") ? POS_UPPER :
11954 strEqual(value, "middle") ? POS_MIDDLE :
11955 strEqual(value, "lower") ? POS_LOWER :
11956 strEqual(value, "bottom") ? POS_BOTTOM :
11957 strEqual(value, "any") ? POS_ANY :
11958 strEqual(value, "last") ? POS_LAST : POS_UNDEFINED);
11960 else if (strEqual(suffix, ".align"))
11962 result = (strEqual(value, "left") ? ALIGN_LEFT :
11963 strEqual(value, "right") ? ALIGN_RIGHT :
11964 strEqual(value, "center") ? ALIGN_CENTER :
11965 strEqual(value, "middle") ? ALIGN_CENTER : ALIGN_DEFAULT);
11967 else if (strEqual(suffix, ".valign"))
11969 result = (strEqual(value, "top") ? VALIGN_TOP :
11970 strEqual(value, "bottom") ? VALIGN_BOTTOM :
11971 strEqual(value, "middle") ? VALIGN_MIDDLE :
11972 strEqual(value, "center") ? VALIGN_MIDDLE : VALIGN_DEFAULT);
11974 else if (strEqual(suffix, ".anim_mode"))
11976 result = (string_has_parameter(value, "none") ? ANIM_NONE :
11977 string_has_parameter(value, "loop") ? ANIM_LOOP :
11978 string_has_parameter(value, "linear") ? ANIM_LINEAR :
11979 string_has_parameter(value, "pingpong") ? ANIM_PINGPONG :
11980 string_has_parameter(value, "pingpong2") ? ANIM_PINGPONG2 :
11981 string_has_parameter(value, "random") ? ANIM_RANDOM :
11982 string_has_parameter(value, "random_static") ? ANIM_RANDOM_STATIC :
11983 string_has_parameter(value, "ce_value") ? ANIM_CE_VALUE :
11984 string_has_parameter(value, "ce_score") ? ANIM_CE_SCORE :
11985 string_has_parameter(value, "ce_delay") ? ANIM_CE_DELAY :
11986 string_has_parameter(value, "horizontal") ? ANIM_HORIZONTAL :
11987 string_has_parameter(value, "vertical") ? ANIM_VERTICAL :
11988 string_has_parameter(value, "centered") ? ANIM_CENTERED :
11989 string_has_parameter(value, "all") ? ANIM_ALL :
11990 string_has_parameter(value, "tiled") ? ANIM_TILED :
11993 if (string_has_parameter(value, "once"))
11994 result |= ANIM_ONCE;
11996 if (string_has_parameter(value, "reverse"))
11997 result |= ANIM_REVERSE;
11999 if (string_has_parameter(value, "opaque_player"))
12000 result |= ANIM_OPAQUE_PLAYER;
12002 if (string_has_parameter(value, "static_panel"))
12003 result |= ANIM_STATIC_PANEL;
12005 else if (strEqual(suffix, ".init_event") ||
12006 strEqual(suffix, ".anim_event"))
12008 result = get_anim_parameter_values(value);
12010 else if (strEqual(suffix, ".init_delay_action") ||
12011 strEqual(suffix, ".anim_delay_action") ||
12012 strEqual(suffix, ".post_delay_action") ||
12013 strEqual(suffix, ".init_event_action") ||
12014 strEqual(suffix, ".anim_event_action"))
12016 result = get_anim_action_parameter_value(value_raw);
12018 else if (strEqual(suffix, ".class"))
12020 result = (strEqual(value, ARG_UNDEFINED) ? ARG_UNDEFINED_VALUE :
12021 get_hash_from_key(value));
12023 else if (strEqual(suffix, ".style"))
12025 result = STYLE_DEFAULT;
12027 if (string_has_parameter(value, "accurate_borders"))
12028 result |= STYLE_ACCURATE_BORDERS;
12030 if (string_has_parameter(value, "inner_corners"))
12031 result |= STYLE_INNER_CORNERS;
12033 if (string_has_parameter(value, "reverse"))
12034 result |= STYLE_REVERSE;
12036 if (string_has_parameter(value, "leftmost_position"))
12037 result |= STYLE_LEFTMOST_POSITION;
12039 if (string_has_parameter(value, "block_clicks"))
12040 result |= STYLE_BLOCK;
12042 if (string_has_parameter(value, "passthrough_clicks"))
12043 result |= STYLE_PASSTHROUGH;
12045 if (string_has_parameter(value, "multiple_actions"))
12046 result |= STYLE_MULTIPLE_ACTIONS;
12048 else if (strEqual(suffix, ".fade_mode"))
12050 result = (string_has_parameter(value, "none") ? FADE_MODE_NONE :
12051 string_has_parameter(value, "fade") ? FADE_MODE_FADE :
12052 string_has_parameter(value, "crossfade") ? FADE_MODE_CROSSFADE :
12053 string_has_parameter(value, "melt") ? FADE_MODE_MELT :
12054 string_has_parameter(value, "curtain") ? FADE_MODE_CURTAIN :
12055 FADE_MODE_DEFAULT);
12057 else if (strEqual(suffix, ".auto_delay_unit"))
12059 result = (string_has_parameter(value, "ms") ? AUTO_DELAY_UNIT_MS :
12060 string_has_parameter(value, "frames") ? AUTO_DELAY_UNIT_FRAMES :
12061 AUTO_DELAY_UNIT_DEFAULT);
12063 else if (strPrefix(suffix, ".font")) // (may also be ".font_xyz")
12065 result = gfx.get_font_from_token_function(value);
12067 else // generic parameter of type integer or boolean
12069 result = (strEqual(value, ARG_UNDEFINED) ? ARG_UNDEFINED_VALUE :
12070 type == TYPE_INTEGER ? get_integer_from_string(value) :
12071 type == TYPE_BOOLEAN ? get_boolean_from_string(value) :
12072 ARG_UNDEFINED_VALUE);
12080 static int get_token_parameter_value(char *token, char *value_raw)
12084 if (token == NULL || value_raw == NULL)
12085 return ARG_UNDEFINED_VALUE;
12087 suffix = strrchr(token, '.');
12088 if (suffix == NULL)
12091 if (strEqual(suffix, ".element"))
12092 return getElementFromToken(value_raw);
12094 // !!! USE CORRECT VALUE TYPE (currently works also for TYPE_BOOLEAN) !!!
12095 return get_parameter_value(value_raw, suffix, TYPE_INTEGER);
12098 void InitMenuDesignSettings_Static(void)
12102 // always start with reliable default values from static default config
12103 for (i = 0; image_config_vars[i].token != NULL; i++)
12105 char *value = getHashEntry(image_config_hash, image_config_vars[i].token);
12108 *image_config_vars[i].value =
12109 get_token_parameter_value(image_config_vars[i].token, value);
12113 static void InitMenuDesignSettings_SpecialPreProcessing(void)
12117 // the following initializes hierarchical values from static configuration
12119 // special case: initialize "ARG_DEFAULT" values in static default config
12120 // (e.g., initialize "[titlemessage].fade_mode" from "[title].fade_mode")
12121 titlescreen_initial_first_default.fade_mode =
12122 title_initial_first_default.fade_mode;
12123 titlescreen_initial_first_default.fade_delay =
12124 title_initial_first_default.fade_delay;
12125 titlescreen_initial_first_default.post_delay =
12126 title_initial_first_default.post_delay;
12127 titlescreen_initial_first_default.auto_delay =
12128 title_initial_first_default.auto_delay;
12129 titlescreen_initial_first_default.auto_delay_unit =
12130 title_initial_first_default.auto_delay_unit;
12131 titlescreen_first_default.fade_mode = title_first_default.fade_mode;
12132 titlescreen_first_default.fade_delay = title_first_default.fade_delay;
12133 titlescreen_first_default.post_delay = title_first_default.post_delay;
12134 titlescreen_first_default.auto_delay = title_first_default.auto_delay;
12135 titlescreen_first_default.auto_delay_unit =
12136 title_first_default.auto_delay_unit;
12137 titlemessage_initial_first_default.fade_mode =
12138 title_initial_first_default.fade_mode;
12139 titlemessage_initial_first_default.fade_delay =
12140 title_initial_first_default.fade_delay;
12141 titlemessage_initial_first_default.post_delay =
12142 title_initial_first_default.post_delay;
12143 titlemessage_initial_first_default.auto_delay =
12144 title_initial_first_default.auto_delay;
12145 titlemessage_initial_first_default.auto_delay_unit =
12146 title_initial_first_default.auto_delay_unit;
12147 titlemessage_first_default.fade_mode = title_first_default.fade_mode;
12148 titlemessage_first_default.fade_delay = title_first_default.fade_delay;
12149 titlemessage_first_default.post_delay = title_first_default.post_delay;
12150 titlemessage_first_default.auto_delay = title_first_default.auto_delay;
12151 titlemessage_first_default.auto_delay_unit =
12152 title_first_default.auto_delay_unit;
12154 titlescreen_initial_default.fade_mode = title_initial_default.fade_mode;
12155 titlescreen_initial_default.fade_delay = title_initial_default.fade_delay;
12156 titlescreen_initial_default.post_delay = title_initial_default.post_delay;
12157 titlescreen_initial_default.auto_delay = title_initial_default.auto_delay;
12158 titlescreen_initial_default.auto_delay_unit =
12159 title_initial_default.auto_delay_unit;
12160 titlescreen_default.fade_mode = title_default.fade_mode;
12161 titlescreen_default.fade_delay = title_default.fade_delay;
12162 titlescreen_default.post_delay = title_default.post_delay;
12163 titlescreen_default.auto_delay = title_default.auto_delay;
12164 titlescreen_default.auto_delay_unit = title_default.auto_delay_unit;
12165 titlemessage_initial_default.fade_mode = title_initial_default.fade_mode;
12166 titlemessage_initial_default.fade_delay = title_initial_default.fade_delay;
12167 titlemessage_initial_default.post_delay = title_initial_default.post_delay;
12168 titlemessage_initial_default.auto_delay_unit =
12169 title_initial_default.auto_delay_unit;
12170 titlemessage_default.fade_mode = title_default.fade_mode;
12171 titlemessage_default.fade_delay = title_default.fade_delay;
12172 titlemessage_default.post_delay = title_default.post_delay;
12173 titlemessage_default.auto_delay = title_default.auto_delay;
12174 titlemessage_default.auto_delay_unit = title_default.auto_delay_unit;
12176 // special case: initialize "ARG_DEFAULT" values in static default config
12177 // (e.g., init "titlemessage_1.fade_mode" from "[titlemessage].fade_mode")
12178 for (i = 0; i < MAX_NUM_TITLE_MESSAGES; i++)
12180 titlescreen_initial_first[i] = titlescreen_initial_first_default;
12181 titlescreen_first[i] = titlescreen_first_default;
12182 titlemessage_initial_first[i] = titlemessage_initial_first_default;
12183 titlemessage_first[i] = titlemessage_first_default;
12185 titlescreen_initial[i] = titlescreen_initial_default;
12186 titlescreen[i] = titlescreen_default;
12187 titlemessage_initial[i] = titlemessage_initial_default;
12188 titlemessage[i] = titlemessage_default;
12191 // special case: initialize "ARG_DEFAULT" values in static default config
12192 // (eg, init "menu.enter_screen.SCORES.xyz" from "menu.enter_screen.xyz")
12193 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
12195 if (i == GFX_SPECIAL_ARG_TITLE) // title values already initialized
12198 menu.enter_screen[i] = menu.enter_screen[GFX_SPECIAL_ARG_DEFAULT];
12199 menu.leave_screen[i] = menu.leave_screen[GFX_SPECIAL_ARG_DEFAULT];
12200 menu.next_screen[i] = menu.next_screen[GFX_SPECIAL_ARG_DEFAULT];
12203 // special case: initialize "ARG_DEFAULT" values in static default config
12204 // (eg, init "viewport.door_1.MAIN.xyz" from "viewport.door_1.xyz")
12205 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
12207 viewport.window[i] = viewport.window[GFX_SPECIAL_ARG_DEFAULT];
12208 viewport.playfield[i] = viewport.playfield[GFX_SPECIAL_ARG_DEFAULT];
12209 viewport.door_1[i] = viewport.door_1[GFX_SPECIAL_ARG_DEFAULT];
12211 if (i == GFX_SPECIAL_ARG_EDITOR) // editor values already initialized
12214 viewport.door_2[i] = viewport.door_2[GFX_SPECIAL_ARG_DEFAULT];
12218 static void InitMenuDesignSettings_SpecialPostProcessing(void)
12222 struct XY *dst, *src;
12224 game_buttons_xy[] =
12226 { &game.button.save, &game.button.stop },
12227 { &game.button.pause2, &game.button.pause },
12228 { &game.button.load, &game.button.play },
12229 { &game.button.undo, &game.button.stop },
12230 { &game.button.redo, &game.button.play },
12236 // special case: initialize later added SETUP list size from LEVELS value
12237 if (menu.list_size[GAME_MODE_SETUP] == -1)
12238 menu.list_size[GAME_MODE_SETUP] = menu.list_size[GAME_MODE_LEVELS];
12240 // set default position for snapshot buttons to stop/pause/play buttons
12241 for (i = 0; game_buttons_xy[i].dst != NULL; i++)
12242 if ((*game_buttons_xy[i].dst).x == -1 &&
12243 (*game_buttons_xy[i].dst).y == -1)
12244 *game_buttons_xy[i].dst = *game_buttons_xy[i].src;
12246 // --------------------------------------------------------------------------
12247 // dynamic viewports (including playfield margins, borders and alignments)
12248 // --------------------------------------------------------------------------
12250 // dynamic viewports currently only supported for landscape mode
12251 int display_width = MAX(video.display_width, video.display_height);
12252 int display_height = MIN(video.display_width, video.display_height);
12254 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
12256 struct RectWithBorder *vp_window = &viewport.window[i];
12257 struct RectWithBorder *vp_playfield = &viewport.playfield[i];
12258 struct RectWithBorder *vp_door_1 = &viewport.door_1[i];
12259 struct RectWithBorder *vp_door_2 = &viewport.door_2[i];
12260 boolean dynamic_window_width = (vp_window->min_width != -1);
12261 boolean dynamic_window_height = (vp_window->min_height != -1);
12262 boolean dynamic_playfield_width = (vp_playfield->min_width != -1);
12263 boolean dynamic_playfield_height = (vp_playfield->min_height != -1);
12265 // adjust window size if min/max width/height is specified
12267 if (vp_window->min_width != -1)
12269 int window_width = display_width;
12271 // when using static window height, use aspect ratio of display
12272 if (vp_window->min_height == -1)
12273 window_width = vp_window->height * display_width / display_height;
12275 vp_window->width = MAX(vp_window->min_width, window_width);
12278 if (vp_window->min_height != -1)
12280 int window_height = display_height;
12282 // when using static window width, use aspect ratio of display
12283 if (vp_window->min_width == -1)
12284 window_height = vp_window->width * display_height / display_width;
12286 vp_window->height = MAX(vp_window->min_height, window_height);
12289 if (vp_window->max_width != -1)
12290 vp_window->width = MIN(vp_window->width, vp_window->max_width);
12292 if (vp_window->max_height != -1)
12293 vp_window->height = MIN(vp_window->height, vp_window->max_height);
12295 int playfield_width = vp_window->width;
12296 int playfield_height = vp_window->height;
12298 // adjust playfield size and position according to specified margins
12300 playfield_width -= vp_playfield->margin_left;
12301 playfield_width -= vp_playfield->margin_right;
12303 playfield_height -= vp_playfield->margin_top;
12304 playfield_height -= vp_playfield->margin_bottom;
12306 // adjust playfield size if min/max width/height is specified
12308 if (vp_playfield->min_width != -1)
12309 vp_playfield->width = MAX(vp_playfield->min_width, playfield_width);
12311 if (vp_playfield->min_height != -1)
12312 vp_playfield->height = MAX(vp_playfield->min_height, playfield_height);
12314 if (vp_playfield->max_width != -1)
12315 vp_playfield->width = MIN(vp_playfield->width, vp_playfield->max_width);
12317 if (vp_playfield->max_height != -1)
12318 vp_playfield->height = MIN(vp_playfield->height,vp_playfield->max_height);
12320 // adjust playfield position according to specified alignment
12322 if (vp_playfield->align == ALIGN_LEFT || vp_playfield->x > 0)
12323 vp_playfield->x = ALIGNED_VP_XPOS(vp_playfield);
12324 else if (vp_playfield->align == ALIGN_CENTER)
12325 vp_playfield->x = playfield_width / 2 - vp_playfield->width / 2;
12326 else if (vp_playfield->align == ALIGN_RIGHT)
12327 vp_playfield->x += playfield_width - vp_playfield->width;
12329 if (vp_playfield->valign == VALIGN_TOP || vp_playfield->y > 0)
12330 vp_playfield->y = ALIGNED_VP_YPOS(vp_playfield);
12331 else if (vp_playfield->valign == VALIGN_MIDDLE)
12332 vp_playfield->y = playfield_height / 2 - vp_playfield->height / 2;
12333 else if (vp_playfield->valign == VALIGN_BOTTOM)
12334 vp_playfield->y += playfield_height - vp_playfield->height;
12336 vp_playfield->x += vp_playfield->margin_left;
12337 vp_playfield->y += vp_playfield->margin_top;
12339 // adjust individual playfield borders if only default border is specified
12341 if (vp_playfield->border_left == -1)
12342 vp_playfield->border_left = vp_playfield->border_size;
12343 if (vp_playfield->border_right == -1)
12344 vp_playfield->border_right = vp_playfield->border_size;
12345 if (vp_playfield->border_top == -1)
12346 vp_playfield->border_top = vp_playfield->border_size;
12347 if (vp_playfield->border_bottom == -1)
12348 vp_playfield->border_bottom = vp_playfield->border_size;
12350 // set dynamic playfield borders if borders are specified as undefined
12351 // (but only if window size was dynamic and playfield size was static)
12353 if (dynamic_window_width && !dynamic_playfield_width)
12355 if (vp_playfield->border_left == -1)
12357 vp_playfield->border_left = (vp_playfield->x -
12358 vp_playfield->margin_left);
12359 vp_playfield->x -= vp_playfield->border_left;
12360 vp_playfield->width += vp_playfield->border_left;
12363 if (vp_playfield->border_right == -1)
12365 vp_playfield->border_right = (vp_window->width -
12367 vp_playfield->width -
12368 vp_playfield->margin_right);
12369 vp_playfield->width += vp_playfield->border_right;
12373 if (dynamic_window_height && !dynamic_playfield_height)
12375 if (vp_playfield->border_top == -1)
12377 vp_playfield->border_top = (vp_playfield->y -
12378 vp_playfield->margin_top);
12379 vp_playfield->y -= vp_playfield->border_top;
12380 vp_playfield->height += vp_playfield->border_top;
12383 if (vp_playfield->border_bottom == -1)
12385 vp_playfield->border_bottom = (vp_window->height -
12387 vp_playfield->height -
12388 vp_playfield->margin_bottom);
12389 vp_playfield->height += vp_playfield->border_bottom;
12393 // adjust playfield size to be a multiple of a defined alignment tile size
12395 int align_size = vp_playfield->align_size;
12396 int playfield_xtiles = vp_playfield->width / align_size;
12397 int playfield_ytiles = vp_playfield->height / align_size;
12398 int playfield_width_corrected = playfield_xtiles * align_size;
12399 int playfield_height_corrected = playfield_ytiles * align_size;
12400 boolean is_playfield_mode = (i == GFX_SPECIAL_ARG_PLAYING ||
12401 i == GFX_SPECIAL_ARG_EDITOR);
12403 if (is_playfield_mode &&
12404 dynamic_playfield_width &&
12405 vp_playfield->width != playfield_width_corrected)
12407 int playfield_xdiff = vp_playfield->width - playfield_width_corrected;
12409 vp_playfield->width = playfield_width_corrected;
12411 if (vp_playfield->align == ALIGN_LEFT)
12413 vp_playfield->border_left += playfield_xdiff;
12415 else if (vp_playfield->align == ALIGN_RIGHT)
12417 vp_playfield->border_right += playfield_xdiff;
12419 else if (vp_playfield->align == ALIGN_CENTER)
12421 int border_left_diff = playfield_xdiff / 2;
12422 int border_right_diff = playfield_xdiff - border_left_diff;
12424 vp_playfield->border_left += border_left_diff;
12425 vp_playfield->border_right += border_right_diff;
12429 if (is_playfield_mode &&
12430 dynamic_playfield_height &&
12431 vp_playfield->height != playfield_height_corrected)
12433 int playfield_ydiff = vp_playfield->height - playfield_height_corrected;
12435 vp_playfield->height = playfield_height_corrected;
12437 if (vp_playfield->valign == VALIGN_TOP)
12439 vp_playfield->border_top += playfield_ydiff;
12441 else if (vp_playfield->align == VALIGN_BOTTOM)
12443 vp_playfield->border_right += playfield_ydiff;
12445 else if (vp_playfield->align == VALIGN_MIDDLE)
12447 int border_top_diff = playfield_ydiff / 2;
12448 int border_bottom_diff = playfield_ydiff - border_top_diff;
12450 vp_playfield->border_top += border_top_diff;
12451 vp_playfield->border_bottom += border_bottom_diff;
12455 // adjust door positions according to specified alignment
12457 for (j = 0; j < 2; j++)
12459 struct RectWithBorder *vp_door = (j == 0 ? vp_door_1 : vp_door_2);
12461 if (vp_door->align == ALIGN_LEFT || vp_door->x > 0)
12462 vp_door->x = ALIGNED_VP_XPOS(vp_door);
12463 else if (vp_door->align == ALIGN_CENTER)
12464 vp_door->x = vp_window->width / 2 - vp_door->width / 2;
12465 else if (vp_door->align == ALIGN_RIGHT)
12466 vp_door->x += vp_window->width - vp_door->width;
12468 if (vp_door->valign == VALIGN_TOP || vp_door->y > 0)
12469 vp_door->y = ALIGNED_VP_YPOS(vp_door);
12470 else if (vp_door->valign == VALIGN_MIDDLE)
12471 vp_door->y = vp_window->height / 2 - vp_door->height / 2;
12472 else if (vp_door->valign == VALIGN_BOTTOM)
12473 vp_door->y += vp_window->height - vp_door->height;
12478 static void InitMenuDesignSettings_SpecialPostProcessing_AfterGraphics(void)
12482 struct XYTileSize *dst, *src;
12485 editor_buttons_xy[] =
12488 &editor.button.element_left, &editor.palette.element_left,
12489 IMG_GFX_EDITOR_BUTTON_ELEMENT_LEFT
12492 &editor.button.element_middle, &editor.palette.element_middle,
12493 IMG_GFX_EDITOR_BUTTON_ELEMENT_MIDDLE
12496 &editor.button.element_right, &editor.palette.element_right,
12497 IMG_GFX_EDITOR_BUTTON_ELEMENT_RIGHT
12504 // set default position for element buttons to element graphics
12505 for (i = 0; editor_buttons_xy[i].dst != NULL; i++)
12507 if ((*editor_buttons_xy[i].dst).x == -1 &&
12508 (*editor_buttons_xy[i].dst).y == -1)
12510 struct GraphicInfo *gd = &graphic_info[editor_buttons_xy[i].graphic];
12512 gd->width = gd->height = editor_buttons_xy[i].src->tile_size;
12514 *editor_buttons_xy[i].dst = *editor_buttons_xy[i].src;
12518 // adjust editor palette rows and columns if specified to be dynamic
12520 if (editor.palette.cols == -1)
12522 int vp_width = viewport.playfield[GFX_SPECIAL_ARG_EDITOR].width;
12523 int bt_width = graphic_info[IMG_EDITOR_PALETTE_BUTTON].width;
12524 int sc_width = graphic_info[IMG_EDITOR_PALETTE_SCROLLBAR].width;
12526 editor.palette.cols = (vp_width - sc_width) / bt_width;
12528 if (editor.palette.x == -1)
12530 int palette_width = editor.palette.cols * bt_width + sc_width;
12532 editor.palette.x = (vp_width - palette_width) / 2;
12536 if (editor.palette.rows == -1)
12538 int vp_height = viewport.playfield[GFX_SPECIAL_ARG_EDITOR].height;
12539 int bt_height = graphic_info[IMG_EDITOR_PALETTE_BUTTON].height;
12540 int tx_height = getFontHeight(FONT_TEXT_2);
12542 editor.palette.rows = (vp_height - tx_height) / bt_height;
12544 if (editor.palette.y == -1)
12546 int palette_height = editor.palette.rows * bt_height + tx_height;
12548 editor.palette.y = (vp_height - palette_height) / 2;
12553 static void LoadMenuDesignSettingsFromFilename(char *filename)
12555 static struct TitleFadingInfo tfi;
12556 static struct TitleMessageInfo tmi;
12557 static struct TokenInfo title_tokens[] =
12559 { TYPE_INTEGER, &tfi.fade_mode, ".fade_mode" },
12560 { TYPE_INTEGER, &tfi.fade_delay, ".fade_delay" },
12561 { TYPE_INTEGER, &tfi.post_delay, ".post_delay" },
12562 { TYPE_INTEGER, &tfi.auto_delay, ".auto_delay" },
12563 { TYPE_INTEGER, &tfi.auto_delay_unit, ".auto_delay_unit" },
12567 static struct TokenInfo titlemessage_tokens[] =
12569 { TYPE_INTEGER, &tmi.x, ".x" },
12570 { TYPE_INTEGER, &tmi.y, ".y" },
12571 { TYPE_INTEGER, &tmi.width, ".width" },
12572 { TYPE_INTEGER, &tmi.height, ".height" },
12573 { TYPE_INTEGER, &tmi.chars, ".chars" },
12574 { TYPE_INTEGER, &tmi.lines, ".lines" },
12575 { TYPE_INTEGER, &tmi.align, ".align" },
12576 { TYPE_INTEGER, &tmi.valign, ".valign" },
12577 { TYPE_INTEGER, &tmi.font, ".font" },
12578 { TYPE_BOOLEAN, &tmi.autowrap, ".autowrap" },
12579 { TYPE_BOOLEAN, &tmi.centered, ".centered" },
12580 { TYPE_BOOLEAN, &tmi.parse_comments, ".parse_comments" },
12581 { TYPE_INTEGER, &tmi.sort_priority, ".sort_priority" },
12582 { TYPE_INTEGER, &tmi.fade_mode, ".fade_mode" },
12583 { TYPE_INTEGER, &tmi.fade_delay, ".fade_delay" },
12584 { TYPE_INTEGER, &tmi.post_delay, ".post_delay" },
12585 { TYPE_INTEGER, &tmi.auto_delay, ".auto_delay" },
12586 { TYPE_INTEGER, &tmi.auto_delay_unit, ".auto_delay_unit" },
12592 struct TitleFadingInfo *info;
12597 // initialize first titles from "enter screen" definitions, if defined
12598 { &title_initial_first_default, "menu.enter_screen.TITLE" },
12599 { &title_first_default, "menu.enter_screen.TITLE" },
12601 // initialize title screens from "next screen" definitions, if defined
12602 { &title_initial_default, "menu.next_screen.TITLE" },
12603 { &title_default, "menu.next_screen.TITLE" },
12609 struct TitleMessageInfo *array;
12612 titlemessage_arrays[] =
12614 // initialize first titles from "enter screen" definitions, if defined
12615 { titlescreen_initial_first, "menu.enter_screen.TITLE" },
12616 { titlescreen_first, "menu.enter_screen.TITLE" },
12617 { titlemessage_initial_first, "menu.enter_screen.TITLE" },
12618 { titlemessage_first, "menu.enter_screen.TITLE" },
12620 // initialize titles from "next screen" definitions, if defined
12621 { titlescreen_initial, "menu.next_screen.TITLE" },
12622 { titlescreen, "menu.next_screen.TITLE" },
12623 { titlemessage_initial, "menu.next_screen.TITLE" },
12624 { titlemessage, "menu.next_screen.TITLE" },
12626 // overwrite titles with title definitions, if defined
12627 { titlescreen_initial_first, "[title_initial]" },
12628 { titlescreen_first, "[title]" },
12629 { titlemessage_initial_first, "[title_initial]" },
12630 { titlemessage_first, "[title]" },
12632 { titlescreen_initial, "[title_initial]" },
12633 { titlescreen, "[title]" },
12634 { titlemessage_initial, "[title_initial]" },
12635 { titlemessage, "[title]" },
12637 // overwrite titles with title screen/message definitions, if defined
12638 { titlescreen_initial_first, "[titlescreen_initial]" },
12639 { titlescreen_first, "[titlescreen]" },
12640 { titlemessage_initial_first, "[titlemessage_initial]" },
12641 { titlemessage_first, "[titlemessage]" },
12643 { titlescreen_initial, "[titlescreen_initial]" },
12644 { titlescreen, "[titlescreen]" },
12645 { titlemessage_initial, "[titlemessage_initial]" },
12646 { titlemessage, "[titlemessage]" },
12650 SetupFileHash *setup_file_hash;
12653 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
12656 // the following initializes hierarchical values from dynamic configuration
12658 // special case: initialize with default values that may be overwritten
12659 // (e.g., init "menu.draw_xoffset.INFO" from "menu.draw_xoffset")
12660 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
12662 struct TokenIntPtrInfo menu_config[] =
12664 { "menu.draw_xoffset", &menu.draw_xoffset[i] },
12665 { "menu.draw_yoffset", &menu.draw_yoffset[i] },
12666 { "menu.list_size", &menu.list_size[i] }
12669 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
12671 char *token = menu_config[j].token;
12672 char *value = getHashEntry(setup_file_hash, token);
12675 *menu_config[j].value = get_integer_from_string(value);
12679 // special case: initialize with default values that may be overwritten
12680 // (eg, init "menu.draw_xoffset.INFO[XXX]" from "menu.draw_xoffset.INFO")
12681 for (i = 0; i < NUM_SPECIAL_GFX_INFO_ARGS; i++)
12683 struct TokenIntPtrInfo menu_config[] =
12685 { "menu.draw_xoffset.INFO", &menu.draw_xoffset_info[i] },
12686 { "menu.draw_yoffset.INFO", &menu.draw_yoffset_info[i] },
12687 { "menu.list_size.INFO", &menu.list_size_info[i] }
12690 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
12692 char *token = menu_config[j].token;
12693 char *value = getHashEntry(setup_file_hash, token);
12696 *menu_config[j].value = get_integer_from_string(value);
12700 // special case: initialize with default values that may be overwritten
12701 // (eg, init "menu.draw_xoffset.SETUP[XXX]" from "menu.draw_xoffset.SETUP")
12702 for (i = 0; i < NUM_SPECIAL_GFX_SETUP_ARGS; i++)
12704 struct TokenIntPtrInfo menu_config[] =
12706 { "menu.draw_xoffset.SETUP", &menu.draw_xoffset_setup[i] },
12707 { "menu.draw_yoffset.SETUP", &menu.draw_yoffset_setup[i] }
12710 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
12712 char *token = menu_config[j].token;
12713 char *value = getHashEntry(setup_file_hash, token);
12716 *menu_config[j].value = get_integer_from_string(value);
12720 // special case: initialize with default values that may be overwritten
12721 // (eg, init "menu.line_spacing.INFO[XXX]" from "menu.line_spacing.INFO")
12722 for (i = 0; i < NUM_SPECIAL_GFX_INFO_ARGS; i++)
12724 struct TokenIntPtrInfo menu_config[] =
12726 { "menu.left_spacing.INFO", &menu.left_spacing_info[i] },
12727 { "menu.right_spacing.INFO", &menu.right_spacing_info[i] },
12728 { "menu.top_spacing.INFO", &menu.top_spacing_info[i] },
12729 { "menu.bottom_spacing.INFO", &menu.bottom_spacing_info[i] },
12730 { "menu.paragraph_spacing.INFO", &menu.paragraph_spacing_info[i] },
12731 { "menu.headline1_spacing.INFO", &menu.headline1_spacing_info[i] },
12732 { "menu.headline2_spacing.INFO", &menu.headline2_spacing_info[i] },
12733 { "menu.line_spacing.INFO", &menu.line_spacing_info[i] },
12734 { "menu.extra_spacing.INFO", &menu.extra_spacing_info[i] },
12737 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
12739 char *token = menu_config[j].token;
12740 char *value = getHashEntry(setup_file_hash, token);
12743 *menu_config[j].value = get_integer_from_string(value);
12747 // special case: initialize with default values that may be overwritten
12748 // (eg, init "menu.enter_screen.SCORES.xyz" from "menu.enter_screen.xyz")
12749 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
12751 struct TokenIntPtrInfo menu_config[] =
12753 { "menu.enter_screen.fade_mode", &menu.enter_screen[i].fade_mode },
12754 { "menu.enter_screen.fade_delay", &menu.enter_screen[i].fade_delay },
12755 { "menu.enter_screen.post_delay", &menu.enter_screen[i].post_delay },
12756 { "menu.leave_screen.fade_mode", &menu.leave_screen[i].fade_mode },
12757 { "menu.leave_screen.fade_delay", &menu.leave_screen[i].fade_delay },
12758 { "menu.leave_screen.post_delay", &menu.leave_screen[i].post_delay },
12759 { "menu.next_screen.fade_mode", &menu.next_screen[i].fade_mode },
12760 { "menu.next_screen.fade_delay", &menu.next_screen[i].fade_delay },
12761 { "menu.next_screen.post_delay", &menu.next_screen[i].post_delay }
12764 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
12766 char *token = menu_config[j].token;
12767 char *value = getHashEntry(setup_file_hash, token);
12770 *menu_config[j].value = get_token_parameter_value(token, value);
12774 // special case: initialize with default values that may be overwritten
12775 // (eg, init "viewport.door_1.MAIN.xyz" from "viewport.door_1.xyz")
12776 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
12780 char *token_prefix;
12781 struct RectWithBorder *struct_ptr;
12785 { "viewport.window", &viewport.window[i] },
12786 { "viewport.playfield", &viewport.playfield[i] },
12787 { "viewport.door_1", &viewport.door_1[i] },
12788 { "viewport.door_2", &viewport.door_2[i] }
12791 for (j = 0; j < ARRAY_SIZE(vp_struct); j++)
12793 struct TokenIntPtrInfo vp_config[] =
12795 { ".x", &vp_struct[j].struct_ptr->x },
12796 { ".y", &vp_struct[j].struct_ptr->y },
12797 { ".width", &vp_struct[j].struct_ptr->width },
12798 { ".height", &vp_struct[j].struct_ptr->height },
12799 { ".min_width", &vp_struct[j].struct_ptr->min_width },
12800 { ".min_height", &vp_struct[j].struct_ptr->min_height },
12801 { ".max_width", &vp_struct[j].struct_ptr->max_width },
12802 { ".max_height", &vp_struct[j].struct_ptr->max_height },
12803 { ".margin_left", &vp_struct[j].struct_ptr->margin_left },
12804 { ".margin_right", &vp_struct[j].struct_ptr->margin_right },
12805 { ".margin_top", &vp_struct[j].struct_ptr->margin_top },
12806 { ".margin_bottom", &vp_struct[j].struct_ptr->margin_bottom },
12807 { ".border_left", &vp_struct[j].struct_ptr->border_left },
12808 { ".border_right", &vp_struct[j].struct_ptr->border_right },
12809 { ".border_top", &vp_struct[j].struct_ptr->border_top },
12810 { ".border_bottom", &vp_struct[j].struct_ptr->border_bottom },
12811 { ".border_size", &vp_struct[j].struct_ptr->border_size },
12812 { ".align_size", &vp_struct[j].struct_ptr->align_size },
12813 { ".align", &vp_struct[j].struct_ptr->align },
12814 { ".valign", &vp_struct[j].struct_ptr->valign }
12817 for (k = 0; k < ARRAY_SIZE(vp_config); k++)
12819 char *token = getStringCat2(vp_struct[j].token_prefix,
12820 vp_config[k].token);
12821 char *value = getHashEntry(setup_file_hash, token);
12824 *vp_config[k].value = get_token_parameter_value(token, value);
12831 // special case: initialize with default values that may be overwritten
12832 // (e.g., init "[title].fade_mode" from "menu.next_screen.TITLE.fade_mode")
12833 for (i = 0; title_info[i].info != NULL; i++)
12835 struct TitleFadingInfo *info = title_info[i].info;
12836 char *base_token = title_info[i].text;
12838 for (j = 0; title_tokens[j].type != -1; j++)
12840 char *token = getStringCat2(base_token, title_tokens[j].text);
12841 char *value = getHashEntry(setup_file_hash, token);
12845 int parameter_value = get_token_parameter_value(token, value);
12849 *(int *)title_tokens[j].value = (int)parameter_value;
12858 // special case: initialize with default values that may be overwritten
12859 // (e.g., init "titlemessage_1.fade_mode" from "[titlemessage].fade_mode")
12860 for (i = 0; titlemessage_arrays[i].array != NULL; i++)
12862 struct TitleMessageInfo *array = titlemessage_arrays[i].array;
12863 char *base_token = titlemessage_arrays[i].text;
12865 for (j = 0; titlemessage_tokens[j].type != -1; j++)
12867 char *token = getStringCat2(base_token, titlemessage_tokens[j].text);
12868 char *value = getHashEntry(setup_file_hash, token);
12872 int parameter_value = get_token_parameter_value(token, value);
12874 for (k = 0; k < MAX_NUM_TITLE_MESSAGES; k++)
12878 if (titlemessage_tokens[j].type == TYPE_INTEGER)
12879 *(int *)titlemessage_tokens[j].value = (int)parameter_value;
12881 *(boolean *)titlemessage_tokens[j].value = (boolean)parameter_value;
12891 // special case: check if network and preview player positions are redefined,
12892 // to compare this later against the main menu level preview being redefined
12893 struct TokenIntPtrInfo menu_config_players[] =
12895 { "main.network_players.x", &menu.main.network_players.redefined },
12896 { "main.network_players.y", &menu.main.network_players.redefined },
12897 { "main.preview_players.x", &menu.main.preview_players.redefined },
12898 { "main.preview_players.y", &menu.main.preview_players.redefined },
12899 { "preview.x", &preview.redefined },
12900 { "preview.y", &preview.redefined }
12903 for (i = 0; i < ARRAY_SIZE(menu_config_players); i++)
12904 *menu_config_players[i].value = FALSE;
12906 for (i = 0; i < ARRAY_SIZE(menu_config_players); i++)
12907 if (getHashEntry(setup_file_hash, menu_config_players[i].token) != NULL)
12908 *menu_config_players[i].value = TRUE;
12910 // read (and overwrite with) values that may be specified in config file
12911 for (i = 0; image_config_vars[i].token != NULL; i++)
12913 char *value = getHashEntry(setup_file_hash, image_config_vars[i].token);
12915 // (ignore definitions set to "[DEFAULT]" which are already initialized)
12916 if (value != NULL && !strEqual(value, ARG_DEFAULT))
12917 *image_config_vars[i].value =
12918 get_token_parameter_value(image_config_vars[i].token, value);
12921 freeSetupFileHash(setup_file_hash);
12924 void LoadMenuDesignSettings(void)
12926 char *filename_base = UNDEFINED_FILENAME, *filename_local;
12928 InitMenuDesignSettings_Static();
12929 InitMenuDesignSettings_SpecialPreProcessing();
12931 if (!GFX_OVERRIDE_ARTWORK(ARTWORK_TYPE_GRAPHICS))
12933 // first look for special settings configured in level series config
12934 filename_base = getCustomArtworkLevelConfigFilename(ARTWORK_TYPE_GRAPHICS);
12936 if (fileExists(filename_base))
12937 LoadMenuDesignSettingsFromFilename(filename_base);
12940 filename_local = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
12942 if (filename_local != NULL && !strEqual(filename_base, filename_local))
12943 LoadMenuDesignSettingsFromFilename(filename_local);
12945 InitMenuDesignSettings_SpecialPostProcessing();
12948 void LoadMenuDesignSettings_AfterGraphics(void)
12950 InitMenuDesignSettings_SpecialPostProcessing_AfterGraphics();
12953 void LoadUserDefinedEditorElementList(int **elements, int *num_elements)
12955 char *filename = getEditorSetupFilename();
12956 SetupFileList *setup_file_list, *list;
12957 SetupFileHash *element_hash;
12958 int num_unknown_tokens = 0;
12961 if ((setup_file_list = loadSetupFileList(filename)) == NULL)
12964 element_hash = newSetupFileHash();
12966 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
12967 setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
12969 // determined size may be larger than needed (due to unknown elements)
12971 for (list = setup_file_list; list != NULL; list = list->next)
12974 // add space for up to 3 more elements for padding that may be needed
12975 *num_elements += 3;
12977 // free memory for old list of elements, if needed
12978 checked_free(*elements);
12980 // allocate memory for new list of elements
12981 *elements = checked_malloc(*num_elements * sizeof(int));
12984 for (list = setup_file_list; list != NULL; list = list->next)
12986 char *value = getHashEntry(element_hash, list->token);
12988 if (value == NULL) // try to find obsolete token mapping
12990 char *mapped_token = get_mapped_token(list->token);
12992 if (mapped_token != NULL)
12994 value = getHashEntry(element_hash, mapped_token);
12996 free(mapped_token);
13002 (*elements)[(*num_elements)++] = atoi(value);
13006 if (num_unknown_tokens == 0)
13009 Warn("unknown token(s) found in config file:");
13010 Warn("- config file: '%s'", filename);
13012 num_unknown_tokens++;
13015 Warn("- token: '%s'", list->token);
13019 if (num_unknown_tokens > 0)
13022 while (*num_elements % 4) // pad with empty elements, if needed
13023 (*elements)[(*num_elements)++] = EL_EMPTY;
13025 freeSetupFileList(setup_file_list);
13026 freeSetupFileHash(element_hash);
13029 for (i = 0; i < *num_elements; i++)
13030 Debug("editor", "element '%s' [%d]\n",
13031 element_info[(*elements)[i]].token_name, (*elements)[i]);
13035 static struct MusicFileInfo *get_music_file_info_ext(char *basename, int music,
13038 SetupFileHash *setup_file_hash = NULL;
13039 struct MusicFileInfo tmp_music_file_info, *new_music_file_info;
13040 char *filename_music, *filename_prefix, *filename_info;
13046 token_to_value_ptr[] =
13048 { "title_header", &tmp_music_file_info.title_header },
13049 { "artist_header", &tmp_music_file_info.artist_header },
13050 { "album_header", &tmp_music_file_info.album_header },
13051 { "year_header", &tmp_music_file_info.year_header },
13053 { "title", &tmp_music_file_info.title },
13054 { "artist", &tmp_music_file_info.artist },
13055 { "album", &tmp_music_file_info.album },
13056 { "year", &tmp_music_file_info.year },
13062 filename_music = (is_sound ? getCustomSoundFilename(basename) :
13063 getCustomMusicFilename(basename));
13065 if (filename_music == NULL)
13068 // ---------- try to replace file extension ----------
13070 filename_prefix = getStringCopy(filename_music);
13071 if (strrchr(filename_prefix, '.') != NULL)
13072 *strrchr(filename_prefix, '.') = '\0';
13073 filename_info = getStringCat2(filename_prefix, ".txt");
13075 if (fileExists(filename_info))
13076 setup_file_hash = loadSetupFileHash(filename_info);
13078 free(filename_prefix);
13079 free(filename_info);
13081 if (setup_file_hash == NULL)
13083 // ---------- try to add file extension ----------
13085 filename_prefix = getStringCopy(filename_music);
13086 filename_info = getStringCat2(filename_prefix, ".txt");
13088 if (fileExists(filename_info))
13089 setup_file_hash = loadSetupFileHash(filename_info);
13091 free(filename_prefix);
13092 free(filename_info);
13095 if (setup_file_hash == NULL)
13098 // ---------- music file info found ----------
13100 clear_mem(&tmp_music_file_info, sizeof(struct MusicFileInfo));
13102 for (i = 0; token_to_value_ptr[i].token != NULL; i++)
13104 char *value = getHashEntry(setup_file_hash, token_to_value_ptr[i].token);
13106 *token_to_value_ptr[i].value_ptr =
13107 getStringCopy(value != NULL && *value != '\0' ? value : UNKNOWN_NAME);
13110 tmp_music_file_info.basename = getStringCopy(basename);
13111 tmp_music_file_info.music = music;
13112 tmp_music_file_info.is_sound = is_sound;
13114 new_music_file_info = checked_malloc(sizeof(struct MusicFileInfo));
13115 *new_music_file_info = tmp_music_file_info;
13117 return new_music_file_info;
13120 static struct MusicFileInfo *get_music_file_info(char *basename, int music)
13122 return get_music_file_info_ext(basename, music, FALSE);
13125 static struct MusicFileInfo *get_sound_file_info(char *basename, int sound)
13127 return get_music_file_info_ext(basename, sound, TRUE);
13130 static boolean music_info_listed_ext(struct MusicFileInfo *list,
13131 char *basename, boolean is_sound)
13133 for (; list != NULL; list = list->next)
13134 if (list->is_sound == is_sound && strEqual(list->basename, basename))
13140 static boolean music_info_listed(struct MusicFileInfo *list, char *basename)
13142 return music_info_listed_ext(list, basename, FALSE);
13145 static boolean sound_info_listed(struct MusicFileInfo *list, char *basename)
13147 return music_info_listed_ext(list, basename, TRUE);
13150 void LoadMusicInfo(void)
13152 char *music_directory = getCustomMusicDirectory();
13153 int num_music = getMusicListSize();
13154 int num_music_noconf = 0;
13155 int num_sounds = getSoundListSize();
13157 DirectoryEntry *dir_entry;
13158 struct FileInfo *music, *sound;
13159 struct MusicFileInfo *next, **new;
13162 while (music_file_info != NULL)
13164 next = music_file_info->next;
13166 checked_free(music_file_info->basename);
13168 checked_free(music_file_info->title_header);
13169 checked_free(music_file_info->artist_header);
13170 checked_free(music_file_info->album_header);
13171 checked_free(music_file_info->year_header);
13173 checked_free(music_file_info->title);
13174 checked_free(music_file_info->artist);
13175 checked_free(music_file_info->album);
13176 checked_free(music_file_info->year);
13178 free(music_file_info);
13180 music_file_info = next;
13183 new = &music_file_info;
13185 for (i = 0; i < num_music; i++)
13187 music = getMusicListEntry(i);
13189 if (music->filename == NULL)
13192 if (strEqual(music->filename, UNDEFINED_FILENAME))
13195 // a configured file may be not recognized as music
13196 if (!FileIsMusic(music->filename))
13199 if (!music_info_listed(music_file_info, music->filename))
13201 *new = get_music_file_info(music->filename, i);
13204 new = &(*new)->next;
13208 if ((dir = openDirectory(music_directory)) == NULL)
13210 Warn("cannot read music directory '%s'", music_directory);
13215 while ((dir_entry = readDirectory(dir)) != NULL) // loop all entries
13217 char *basename = dir_entry->basename;
13218 boolean music_already_used = FALSE;
13221 // skip all music files that are configured in music config file
13222 for (i = 0; i < num_music; i++)
13224 music = getMusicListEntry(i);
13226 if (music->filename == NULL)
13229 if (strEqual(basename, music->filename))
13231 music_already_used = TRUE;
13236 if (music_already_used)
13239 if (!FileIsMusic(dir_entry->filename))
13242 if (!music_info_listed(music_file_info, basename))
13244 *new = get_music_file_info(basename, MAP_NOCONF_MUSIC(num_music_noconf));
13247 new = &(*new)->next;
13250 num_music_noconf++;
13253 closeDirectory(dir);
13255 for (i = 0; i < num_sounds; i++)
13257 sound = getSoundListEntry(i);
13259 if (sound->filename == NULL)
13262 if (strEqual(sound->filename, UNDEFINED_FILENAME))
13265 // a configured file may be not recognized as sound
13266 if (!FileIsSound(sound->filename))
13269 if (!sound_info_listed(music_file_info, sound->filename))
13271 *new = get_sound_file_info(sound->filename, i);
13273 new = &(*new)->next;
13278 static void add_helpanim_entry(int element, int action, int direction,
13279 int delay, int *num_list_entries)
13281 struct HelpAnimInfo *new_list_entry;
13282 (*num_list_entries)++;
13285 checked_realloc(helpanim_info,
13286 *num_list_entries * sizeof(struct HelpAnimInfo));
13287 new_list_entry = &helpanim_info[*num_list_entries - 1];
13289 new_list_entry->element = element;
13290 new_list_entry->action = action;
13291 new_list_entry->direction = direction;
13292 new_list_entry->delay = delay;
13295 static void print_unknown_token(char *filename, char *token, int token_nr)
13300 Warn("unknown token(s) found in config file:");
13301 Warn("- config file: '%s'", filename);
13304 Warn("- token: '%s'", token);
13307 static void print_unknown_token_end(int token_nr)
13313 void LoadHelpAnimInfo(void)
13315 char *filename = getHelpAnimFilename();
13316 SetupFileList *setup_file_list = NULL, *list;
13317 SetupFileHash *element_hash, *action_hash, *direction_hash;
13318 int num_list_entries = 0;
13319 int num_unknown_tokens = 0;
13322 if (fileExists(filename))
13323 setup_file_list = loadSetupFileList(filename);
13325 if (setup_file_list == NULL)
13327 // use reliable default values from static configuration
13328 SetupFileList *insert_ptr;
13330 insert_ptr = setup_file_list =
13331 newSetupFileList(helpanim_config[0].token,
13332 helpanim_config[0].value);
13334 for (i = 1; helpanim_config[i].token; i++)
13335 insert_ptr = addListEntry(insert_ptr,
13336 helpanim_config[i].token,
13337 helpanim_config[i].value);
13340 element_hash = newSetupFileHash();
13341 action_hash = newSetupFileHash();
13342 direction_hash = newSetupFileHash();
13344 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
13345 setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
13347 for (i = 0; i < NUM_ACTIONS; i++)
13348 setHashEntry(action_hash, element_action_info[i].suffix,
13349 i_to_a(element_action_info[i].value));
13351 // do not store direction index (bit) here, but direction value!
13352 for (i = 0; i < NUM_DIRECTIONS_FULL; i++)
13353 setHashEntry(direction_hash, element_direction_info[i].suffix,
13354 i_to_a(1 << element_direction_info[i].value));
13356 for (list = setup_file_list; list != NULL; list = list->next)
13358 char *element_token, *action_token, *direction_token;
13359 char *element_value, *action_value, *direction_value;
13360 int delay = atoi(list->value);
13362 if (strEqual(list->token, "end"))
13364 add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
13369 /* first try to break element into element/action/direction parts;
13370 if this does not work, also accept combined "element[.act][.dir]"
13371 elements (like "dynamite.active"), which are unique elements */
13373 if (strchr(list->token, '.') == NULL) // token contains no '.'
13375 element_value = getHashEntry(element_hash, list->token);
13376 if (element_value != NULL) // element found
13377 add_helpanim_entry(atoi(element_value), -1, -1, delay,
13378 &num_list_entries);
13381 // no further suffixes found -- this is not an element
13382 print_unknown_token(filename, list->token, num_unknown_tokens++);
13388 // token has format "<prefix>.<something>"
13390 action_token = strchr(list->token, '.'); // suffix may be action ...
13391 direction_token = action_token; // ... or direction
13393 element_token = getStringCopy(list->token);
13394 *strchr(element_token, '.') = '\0';
13396 element_value = getHashEntry(element_hash, element_token);
13398 if (element_value == NULL) // this is no element
13400 element_value = getHashEntry(element_hash, list->token);
13401 if (element_value != NULL) // combined element found
13402 add_helpanim_entry(atoi(element_value), -1, -1, delay,
13403 &num_list_entries);
13405 print_unknown_token(filename, list->token, num_unknown_tokens++);
13407 free(element_token);
13412 action_value = getHashEntry(action_hash, action_token);
13414 if (action_value != NULL) // action found
13416 add_helpanim_entry(atoi(element_value), atoi(action_value), -1, delay,
13417 &num_list_entries);
13419 free(element_token);
13424 direction_value = getHashEntry(direction_hash, direction_token);
13426 if (direction_value != NULL) // direction found
13428 add_helpanim_entry(atoi(element_value), -1, atoi(direction_value), delay,
13429 &num_list_entries);
13431 free(element_token);
13436 if (strchr(action_token + 1, '.') == NULL)
13438 // no further suffixes found -- this is not an action nor direction
13440 element_value = getHashEntry(element_hash, list->token);
13441 if (element_value != NULL) // combined element found
13442 add_helpanim_entry(atoi(element_value), -1, -1, delay,
13443 &num_list_entries);
13445 print_unknown_token(filename, list->token, num_unknown_tokens++);
13447 free(element_token);
13452 // token has format "<prefix>.<suffix>.<something>"
13454 direction_token = strchr(action_token + 1, '.');
13456 action_token = getStringCopy(action_token);
13457 *strchr(action_token + 1, '.') = '\0';
13459 action_value = getHashEntry(action_hash, action_token);
13461 if (action_value == NULL) // this is no action
13463 element_value = getHashEntry(element_hash, list->token);
13464 if (element_value != NULL) // combined element found
13465 add_helpanim_entry(atoi(element_value), -1, -1, delay,
13466 &num_list_entries);
13468 print_unknown_token(filename, list->token, num_unknown_tokens++);
13470 free(element_token);
13471 free(action_token);
13476 direction_value = getHashEntry(direction_hash, direction_token);
13478 if (direction_value != NULL) // direction found
13480 add_helpanim_entry(atoi(element_value), atoi(action_value),
13481 atoi(direction_value), delay, &num_list_entries);
13483 free(element_token);
13484 free(action_token);
13489 // this is no direction
13491 element_value = getHashEntry(element_hash, list->token);
13492 if (element_value != NULL) // combined element found
13493 add_helpanim_entry(atoi(element_value), -1, -1, delay,
13494 &num_list_entries);
13496 print_unknown_token(filename, list->token, num_unknown_tokens++);
13498 free(element_token);
13499 free(action_token);
13502 print_unknown_token_end(num_unknown_tokens);
13504 add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
13505 add_helpanim_entry(HELPANIM_LIST_END, -1, -1, -1, &num_list_entries);
13507 freeSetupFileList(setup_file_list);
13508 freeSetupFileHash(element_hash);
13509 freeSetupFileHash(action_hash);
13510 freeSetupFileHash(direction_hash);
13513 for (i = 0; i < num_list_entries; i++)
13514 Debug("files:LoadHelpAnimInfo", "'%s': %d, %d, %d => %d",
13515 EL_NAME(helpanim_info[i].element),
13516 helpanim_info[i].element,
13517 helpanim_info[i].action,
13518 helpanim_info[i].direction,
13519 helpanim_info[i].delay);
13523 void LoadHelpTextInfo(void)
13525 char *filename = getHelpTextFilename();
13528 if (helptext_info != NULL)
13530 freeSetupFileHash(helptext_info);
13531 helptext_info = NULL;
13534 if (fileExists(filename))
13535 helptext_info = loadSetupFileHash(filename);
13537 if (helptext_info == NULL)
13539 // use reliable default values from static configuration
13540 helptext_info = newSetupFileHash();
13542 for (i = 0; helptext_config[i].token; i++)
13543 setHashEntry(helptext_info,
13544 helptext_config[i].token,
13545 helptext_config[i].value);
13549 BEGIN_HASH_ITERATION(helptext_info, itr)
13551 Debug("files:LoadHelpTextInfo", "'%s' => '%s'",
13552 HASH_ITERATION_TOKEN(itr), HASH_ITERATION_VALUE(itr));
13554 END_HASH_ITERATION(hash, itr)
13559 // ----------------------------------------------------------------------------
13561 // ----------------------------------------------------------------------------
13563 #define MAX_NUM_CONVERT_LEVELS 1000
13565 void ConvertLevels(void)
13567 static LevelDirTree *convert_leveldir = NULL;
13568 static int convert_level_nr = -1;
13569 static int num_levels_handled = 0;
13570 static int num_levels_converted = 0;
13571 static boolean levels_failed[MAX_NUM_CONVERT_LEVELS];
13574 convert_leveldir = getTreeInfoFromIdentifier(leveldir_first,
13575 global.convert_leveldir);
13577 if (convert_leveldir == NULL)
13578 Fail("no such level identifier: '%s'", global.convert_leveldir);
13580 leveldir_current = convert_leveldir;
13582 if (global.convert_level_nr != -1)
13584 convert_leveldir->first_level = global.convert_level_nr;
13585 convert_leveldir->last_level = global.convert_level_nr;
13588 convert_level_nr = convert_leveldir->first_level;
13590 PrintLine("=", 79);
13591 Print("Converting levels\n");
13592 PrintLine("-", 79);
13593 Print("Level series identifier: '%s'\n", convert_leveldir->identifier);
13594 Print("Level series name: '%s'\n", convert_leveldir->name);
13595 Print("Level series author: '%s'\n", convert_leveldir->author);
13596 Print("Number of levels: %d\n", convert_leveldir->levels);
13597 PrintLine("=", 79);
13600 for (i = 0; i < MAX_NUM_CONVERT_LEVELS; i++)
13601 levels_failed[i] = FALSE;
13603 while (convert_level_nr <= convert_leveldir->last_level)
13605 char *level_filename;
13608 level_nr = convert_level_nr++;
13610 Print("Level %03d: ", level_nr);
13612 LoadLevel(level_nr);
13613 if (level.no_level_file || level.no_valid_file)
13615 Print("(no level)\n");
13619 Print("converting level ... ");
13622 // special case: conversion of some EMC levels as requested by ACME
13623 level.game_engine_type = GAME_ENGINE_TYPE_RND;
13626 level_filename = getDefaultLevelFilename(level_nr);
13627 new_level = !fileExists(level_filename);
13631 SaveLevel(level_nr);
13633 num_levels_converted++;
13635 Print("converted.\n");
13639 if (level_nr >= 0 && level_nr < MAX_NUM_CONVERT_LEVELS)
13640 levels_failed[level_nr] = TRUE;
13642 Print("NOT CONVERTED -- LEVEL ALREADY EXISTS.\n");
13645 num_levels_handled++;
13649 PrintLine("=", 79);
13650 Print("Number of levels handled: %d\n", num_levels_handled);
13651 Print("Number of levels converted: %d (%d%%)\n", num_levels_converted,
13652 (num_levels_handled ?
13653 num_levels_converted * 100 / num_levels_handled : 0));
13654 PrintLine("-", 79);
13655 Print("Summary (for automatic parsing by scripts):\n");
13656 Print("LEVELDIR '%s', CONVERTED %d/%d (%d%%)",
13657 convert_leveldir->identifier, num_levels_converted,
13658 num_levels_handled,
13659 (num_levels_handled ?
13660 num_levels_converted * 100 / num_levels_handled : 0));
13662 if (num_levels_handled != num_levels_converted)
13664 Print(", FAILED:");
13665 for (i = 0; i < MAX_NUM_CONVERT_LEVELS; i++)
13666 if (levels_failed[i])
13671 PrintLine("=", 79);
13673 CloseAllAndExit(0);
13677 // ----------------------------------------------------------------------------
13678 // create and save images for use in level sketches (raw BMP format)
13679 // ----------------------------------------------------------------------------
13681 void CreateLevelSketchImages(void)
13687 InitElementPropertiesGfxElement();
13689 bitmap1 = CreateBitmap(TILEX, TILEY, DEFAULT_DEPTH);
13690 bitmap2 = CreateBitmap(MINI_TILEX, MINI_TILEY, DEFAULT_DEPTH);
13692 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
13694 int element = getMappedElement(i);
13695 char basename1[16];
13696 char basename2[16];
13700 sprintf(basename1, "%04d.bmp", i);
13701 sprintf(basename2, "%04ds.bmp", i);
13703 filename1 = getPath2(global.create_sketch_images_dir, basename1);
13704 filename2 = getPath2(global.create_sketch_images_dir, basename2);
13706 DrawSizedElement(0, 0, element, TILESIZE);
13707 BlitBitmap(drawto, bitmap1, SX, SY, TILEX, TILEY, 0, 0);
13709 if (SDL_SaveBMP(bitmap1->surface, filename1) != 0)
13710 Fail("cannot save level sketch image file '%s'", filename1);
13712 DrawSizedElement(0, 0, element, MINI_TILESIZE);
13713 BlitBitmap(drawto, bitmap2, SX, SY, MINI_TILEX, MINI_TILEY, 0, 0);
13715 if (SDL_SaveBMP(bitmap2->surface, filename2) != 0)
13716 Fail("cannot save level sketch image file '%s'", filename2);
13721 // create corresponding SQL statements (for normal and small images)
13724 printf("insert into phpbb_words values (NULL, '`%03d', '<IMG class=\"levelsketch\" src=\"/I/%04d.png\"/>');\n", i, i);
13725 printf("insert into phpbb_words values (NULL, '¸%03d', '<IMG class=\"levelsketch\" src=\"/I/%04ds.png\"/>');\n", i, i);
13728 printf("insert into phpbb_words values (NULL, '`%04d', '<IMG class=\"levelsketch\" src=\"/I/%04d.png\"/>');\n", i, i);
13729 printf("insert into phpbb_words values (NULL, '¸%04d', '<IMG class=\"levelsketch\" src=\"/I/%04ds.png\"/>');\n", i, i);
13731 // optional: create content for forum level sketch demonstration post
13733 fprintf(stderr, "%03d `%03d%c", i, i, (i % 10 < 9 ? ' ' : '\n'));
13736 FreeBitmap(bitmap1);
13737 FreeBitmap(bitmap2);
13740 fprintf(stderr, "\n");
13742 Info("%d normal and small images created", NUM_FILE_ELEMENTS);
13744 CloseAllAndExit(0);
13748 // ----------------------------------------------------------------------------
13749 // create and save images for element collecting animations (raw BMP format)
13750 // ----------------------------------------------------------------------------
13752 static boolean createCollectImage(int element)
13754 return (IS_COLLECTIBLE(element) && !IS_SP_ELEMENT(element));
13757 void CreateCollectElementImages(void)
13761 int anim_frames = num_steps - 1;
13762 int tile_size = TILESIZE;
13763 int anim_width = tile_size * anim_frames;
13764 int anim_height = tile_size;
13765 int num_collect_images = 0;
13766 int pos_collect_images = 0;
13768 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
13769 if (createCollectImage(i))
13770 num_collect_images++;
13772 Info("Creating %d element collecting animation images ...",
13773 num_collect_images);
13775 int dst_width = anim_width * 2;
13776 int dst_height = anim_height * num_collect_images / 2;
13777 Bitmap *dst_bitmap = CreateBitmap(dst_width, dst_height, DEFAULT_DEPTH);
13778 char *basename = "RocksCollect.bmp";
13779 char *filename = getPath2(global.create_collect_images_dir, basename);
13781 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
13783 if (!createCollectImage(i))
13786 int dst_x = (pos_collect_images / (num_collect_images / 2)) * anim_width;
13787 int dst_y = (pos_collect_images % (num_collect_images / 2)) * anim_height;
13788 int graphic = el2img(i);
13789 char *token_name = element_info[i].token_name;
13790 Bitmap *tmp_bitmap = CreateBitmap(tile_size, tile_size, DEFAULT_DEPTH);
13791 Bitmap *src_bitmap;
13794 Info("- creating collecting image for '%s' ...", token_name);
13796 getGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
13798 BlitBitmap(src_bitmap, tmp_bitmap, src_x, src_y,
13799 tile_size, tile_size, 0, 0);
13801 tmp_bitmap->surface_masked = tmp_bitmap->surface;
13803 for (j = 0; j < anim_frames; j++)
13805 int frame_size_final = tile_size * (anim_frames - j) / num_steps;
13806 int frame_size = frame_size_final * num_steps;
13807 int offset = (tile_size - frame_size_final) / 2;
13808 Bitmap *frame_bitmap = ZoomBitmap(tmp_bitmap, frame_size, frame_size);
13810 while (frame_size > frame_size_final)
13814 Bitmap *half_bitmap = ZoomBitmap(frame_bitmap, frame_size, frame_size);
13816 FreeBitmap(frame_bitmap);
13818 frame_bitmap = half_bitmap;
13821 BlitBitmap(frame_bitmap, dst_bitmap, 0, 0,
13822 frame_size_final, frame_size_final,
13823 dst_x + j * tile_size + offset, dst_y + offset);
13825 FreeBitmap(frame_bitmap);
13828 tmp_bitmap->surface_masked = NULL;
13830 FreeBitmap(tmp_bitmap);
13832 pos_collect_images++;
13835 if (SDL_SaveBMP(dst_bitmap->surface, filename) != 0)
13836 Fail("cannot save element collecting image file '%s'", filename);
13838 FreeBitmap(dst_bitmap);
13842 CloseAllAndExit(0);
13846 // ----------------------------------------------------------------------------
13847 // create and save images for custom and group elements (raw BMP format)
13848 // ----------------------------------------------------------------------------
13850 void CreateCustomElementImages(char *directory)
13852 char *src_basename = "RocksCE-template.ilbm";
13853 char *dst_basename = "RocksCE.bmp";
13854 char *src_filename = getPath2(directory, src_basename);
13855 char *dst_filename = getPath2(directory, dst_basename);
13856 Bitmap *src_bitmap;
13858 int yoffset_ce = 0;
13859 int yoffset_ge = (TILEY * NUM_CUSTOM_ELEMENTS / 16);
13862 InitVideoDefaults();
13864 ReCreateBitmap(&backbuffer, video.width, video.height);
13866 src_bitmap = LoadImage(src_filename);
13868 bitmap = CreateBitmap(TILEX * 16 * 2,
13869 TILEY * (NUM_CUSTOM_ELEMENTS + NUM_GROUP_ELEMENTS) / 16,
13872 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
13879 BlitBitmap(src_bitmap, bitmap, 0, 0, TILEX, TILEY,
13880 TILEX * x, TILEY * y + yoffset_ce);
13882 BlitBitmap(src_bitmap, bitmap, 0, TILEY,
13884 TILEX * x + TILEX * 16,
13885 TILEY * y + yoffset_ce);
13887 for (j = 2; j >= 0; j--)
13891 BlitBitmap(src_bitmap, bitmap,
13892 TILEX + c * 7, 0, 6, 10,
13893 TILEX * x + 6 + j * 7,
13894 TILEY * y + 11 + yoffset_ce);
13896 BlitBitmap(src_bitmap, bitmap,
13897 TILEX + c * 8, TILEY, 6, 10,
13898 TILEX * 16 + TILEX * x + 6 + j * 8,
13899 TILEY * y + 10 + yoffset_ce);
13905 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
13912 BlitBitmap(src_bitmap, bitmap, 0, 0, TILEX, TILEY,
13913 TILEX * x, TILEY * y + yoffset_ge);
13915 BlitBitmap(src_bitmap, bitmap, 0, TILEY,
13917 TILEX * x + TILEX * 16,
13918 TILEY * y + yoffset_ge);
13920 for (j = 1; j >= 0; j--)
13924 BlitBitmap(src_bitmap, bitmap, TILEX + c * 10, 11, 10, 10,
13925 TILEX * x + 6 + j * 10,
13926 TILEY * y + 11 + yoffset_ge);
13928 BlitBitmap(src_bitmap, bitmap,
13929 TILEX + c * 8, TILEY + 12, 6, 10,
13930 TILEX * 16 + TILEX * x + 10 + j * 8,
13931 TILEY * y + 10 + yoffset_ge);
13937 if (SDL_SaveBMP(bitmap->surface, dst_filename) != 0)
13938 Fail("cannot save CE graphics file '%s'", dst_filename);
13940 FreeBitmap(bitmap);
13942 CloseAllAndExit(0);