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_es, "editor.cascade.el_es"
10223 &setup.editor_cascade.el_ref, "editor.cascade.el_ref"
10227 &setup.editor_cascade.el_user, "editor.cascade.el_user"
10231 &setup.editor_cascade.el_dynamic, "editor.cascade.el_dynamic"
10235 static struct TokenInfo shortcut_setup_tokens[] =
10239 &setup.shortcut.save_game, "shortcut.save_game"
10243 &setup.shortcut.load_game, "shortcut.load_game"
10247 &setup.shortcut.toggle_pause, "shortcut.toggle_pause"
10251 &setup.shortcut.focus_player[0], "shortcut.focus_player_1"
10255 &setup.shortcut.focus_player[1], "shortcut.focus_player_2"
10259 &setup.shortcut.focus_player[2], "shortcut.focus_player_3"
10263 &setup.shortcut.focus_player[3], "shortcut.focus_player_4"
10267 &setup.shortcut.focus_player_all, "shortcut.focus_player_all"
10271 &setup.shortcut.tape_eject, "shortcut.tape_eject"
10275 &setup.shortcut.tape_extra, "shortcut.tape_extra"
10279 &setup.shortcut.tape_stop, "shortcut.tape_stop"
10283 &setup.shortcut.tape_pause, "shortcut.tape_pause"
10287 &setup.shortcut.tape_record, "shortcut.tape_record"
10291 &setup.shortcut.tape_play, "shortcut.tape_play"
10295 &setup.shortcut.sound_simple, "shortcut.sound_simple"
10299 &setup.shortcut.sound_loops, "shortcut.sound_loops"
10303 &setup.shortcut.sound_music, "shortcut.sound_music"
10307 &setup.shortcut.snap_left, "shortcut.snap_left"
10311 &setup.shortcut.snap_right, "shortcut.snap_right"
10315 &setup.shortcut.snap_up, "shortcut.snap_up"
10319 &setup.shortcut.snap_down, "shortcut.snap_down"
10323 static struct SetupInputInfo setup_input;
10324 static struct TokenInfo player_setup_tokens[] =
10328 &setup_input.use_joystick, ".use_joystick"
10332 &setup_input.joy.device_name, ".joy.device_name"
10336 &setup_input.joy.xleft, ".joy.xleft"
10340 &setup_input.joy.xmiddle, ".joy.xmiddle"
10344 &setup_input.joy.xright, ".joy.xright"
10348 &setup_input.joy.yupper, ".joy.yupper"
10352 &setup_input.joy.ymiddle, ".joy.ymiddle"
10356 &setup_input.joy.ylower, ".joy.ylower"
10360 &setup_input.joy.snap, ".joy.snap_field"
10364 &setup_input.joy.drop, ".joy.place_bomb"
10368 &setup_input.key.left, ".key.move_left"
10372 &setup_input.key.right, ".key.move_right"
10376 &setup_input.key.up, ".key.move_up"
10380 &setup_input.key.down, ".key.move_down"
10384 &setup_input.key.snap, ".key.snap_field"
10388 &setup_input.key.drop, ".key.place_bomb"
10392 static struct TokenInfo system_setup_tokens[] =
10396 &setup.system.sdl_renderdriver, "system.sdl_renderdriver"
10400 &setup.system.sdl_videodriver, "system.sdl_videodriver"
10404 &setup.system.sdl_audiodriver, "system.sdl_audiodriver"
10408 &setup.system.audio_fragment_size, "system.audio_fragment_size"
10412 static struct TokenInfo internal_setup_tokens[] =
10416 &setup.internal.program_title, "program_title"
10420 &setup.internal.program_version, "program_version"
10424 &setup.internal.program_author, "program_author"
10428 &setup.internal.program_email, "program_email"
10432 &setup.internal.program_website, "program_website"
10436 &setup.internal.program_copyright, "program_copyright"
10440 &setup.internal.program_company, "program_company"
10444 &setup.internal.program_icon_file, "program_icon_file"
10448 &setup.internal.default_graphics_set, "default_graphics_set"
10452 &setup.internal.default_sounds_set, "default_sounds_set"
10456 &setup.internal.default_music_set, "default_music_set"
10460 &setup.internal.fallback_graphics_file, "fallback_graphics_file"
10464 &setup.internal.fallback_sounds_file, "fallback_sounds_file"
10468 &setup.internal.fallback_music_file, "fallback_music_file"
10472 &setup.internal.default_level_series, "default_level_series"
10476 &setup.internal.default_window_width, "default_window_width"
10480 &setup.internal.default_window_height, "default_window_height"
10484 &setup.internal.choose_from_top_leveldir, "choose_from_top_leveldir"
10488 &setup.internal.show_scaling_in_title, "show_scaling_in_title"
10492 &setup.internal.create_user_levelset, "create_user_levelset"
10496 &setup.internal.menu_game, "menu_game"
10500 &setup.internal.menu_editor, "menu_editor"
10504 &setup.internal.menu_graphics, "menu_graphics"
10508 &setup.internal.menu_sound, "menu_sound"
10512 &setup.internal.menu_artwork, "menu_artwork"
10516 &setup.internal.menu_input, "menu_input"
10520 &setup.internal.menu_touch, "menu_touch"
10524 &setup.internal.menu_shortcuts, "menu_shortcuts"
10528 &setup.internal.menu_exit, "menu_exit"
10532 &setup.internal.menu_save_and_exit, "menu_save_and_exit"
10536 static struct TokenInfo debug_setup_tokens[] =
10540 &setup.debug.frame_delay[0], "debug.frame_delay_0"
10544 &setup.debug.frame_delay[1], "debug.frame_delay_1"
10548 &setup.debug.frame_delay[2], "debug.frame_delay_2"
10552 &setup.debug.frame_delay[3], "debug.frame_delay_3"
10556 &setup.debug.frame_delay[4], "debug.frame_delay_4"
10560 &setup.debug.frame_delay[5], "debug.frame_delay_5"
10564 &setup.debug.frame_delay[6], "debug.frame_delay_6"
10568 &setup.debug.frame_delay[7], "debug.frame_delay_7"
10572 &setup.debug.frame_delay[8], "debug.frame_delay_8"
10576 &setup.debug.frame_delay[9], "debug.frame_delay_9"
10580 &setup.debug.frame_delay_key[0], "debug.key.frame_delay_0"
10584 &setup.debug.frame_delay_key[1], "debug.key.frame_delay_1"
10588 &setup.debug.frame_delay_key[2], "debug.key.frame_delay_2"
10592 &setup.debug.frame_delay_key[3], "debug.key.frame_delay_3"
10596 &setup.debug.frame_delay_key[4], "debug.key.frame_delay_4"
10600 &setup.debug.frame_delay_key[5], "debug.key.frame_delay_5"
10604 &setup.debug.frame_delay_key[6], "debug.key.frame_delay_6"
10608 &setup.debug.frame_delay_key[7], "debug.key.frame_delay_7"
10612 &setup.debug.frame_delay_key[8], "debug.key.frame_delay_8"
10616 &setup.debug.frame_delay_key[9], "debug.key.frame_delay_9"
10620 &setup.debug.frame_delay_use_mod_key, "debug.frame_delay.use_mod_key"},
10623 &setup.debug.frame_delay_game_only, "debug.frame_delay.game_only"
10627 &setup.debug.show_frames_per_second, "debug.show_frames_per_second"
10631 &setup.debug.xsn_mode, "debug.xsn_mode"
10635 &setup.debug.xsn_percent, "debug.xsn_percent"
10639 static struct TokenInfo options_setup_tokens[] =
10643 &setup.options.verbose, "options.verbose"
10647 static void setSetupInfoToDefaults(struct SetupInfo *si)
10651 si->player_name = getStringCopy(getDefaultUserName(user.nr));
10653 si->multiple_users = TRUE;
10656 si->sound_loops = TRUE;
10657 si->sound_music = TRUE;
10658 si->sound_simple = TRUE;
10660 si->scroll_delay = TRUE;
10661 si->forced_scroll_delay = FALSE;
10662 si->scroll_delay_value = STD_SCROLL_DELAY;
10663 si->engine_snapshot_mode = getStringCopy(STR_SNAPSHOT_MODE_DEFAULT);
10664 si->engine_snapshot_memory = SNAPSHOT_MEMORY_DEFAULT;
10665 si->fade_screens = TRUE;
10666 si->autorecord = TRUE;
10667 si->show_titlescreen = TRUE;
10668 si->quick_doors = FALSE;
10669 si->team_mode = FALSE;
10670 si->handicap = TRUE;
10671 si->skip_levels = TRUE;
10672 si->increment_levels = TRUE;
10673 si->auto_play_next_level = TRUE;
10674 si->count_score_after_game = TRUE;
10675 si->show_scores_after_game = TRUE;
10676 si->time_limit = TRUE;
10677 si->fullscreen = FALSE;
10678 si->window_scaling_percent = STD_WINDOW_SCALING_PERCENT;
10679 si->window_scaling_quality = getStringCopy(SCALING_QUALITY_DEFAULT);
10680 si->screen_rendering_mode = getStringCopy(STR_SPECIAL_RENDERING_DEFAULT);
10681 si->vsync_mode = getStringCopy(STR_VSYNC_MODE_DEFAULT);
10682 si->ask_on_escape = TRUE;
10683 si->ask_on_escape_editor = TRUE;
10684 si->ask_on_game_over = TRUE;
10685 si->ask_on_quit_game = TRUE;
10686 si->ask_on_quit_program = TRUE;
10687 si->quick_switch = FALSE;
10688 si->input_on_focus = FALSE;
10689 si->prefer_aga_graphics = TRUE;
10690 si->prefer_lowpass_sounds = FALSE;
10691 si->prefer_extra_panel_items = TRUE;
10692 si->game_speed_extended = FALSE;
10693 si->game_frame_delay = GAME_FRAME_DELAY;
10694 si->sp_show_border_elements = FALSE;
10695 si->small_game_graphics = FALSE;
10696 si->show_load_save_buttons = FALSE;
10697 si->show_undo_redo_buttons = FALSE;
10698 si->scores_in_highscore_list = getStringCopy(STR_SCORES_TYPE_DEFAULT);
10700 si->graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
10701 si->sounds_set = getStringCopy(SND_CLASSIC_SUBDIR);
10702 si->music_set = getStringCopy(MUS_CLASSIC_SUBDIR);
10704 si->override_level_graphics = FALSE;
10705 si->override_level_sounds = FALSE;
10706 si->override_level_music = FALSE;
10708 si->volume_simple = 100; // percent
10709 si->volume_loops = 100; // percent
10710 si->volume_music = 100; // percent
10712 si->network_mode = FALSE;
10713 si->network_player_nr = 0; // first player
10714 si->network_server_hostname = getStringCopy(STR_NETWORK_AUTO_DETECT);
10716 si->touch.control_type = getStringCopy(TOUCH_CONTROL_DEFAULT);
10717 si->touch.move_distance = TOUCH_MOVE_DISTANCE_DEFAULT; // percent
10718 si->touch.drop_distance = TOUCH_DROP_DISTANCE_DEFAULT; // percent
10719 si->touch.transparency = TOUCH_TRANSPARENCY_DEFAULT; // percent
10720 si->touch.draw_outlined = TRUE;
10721 si->touch.draw_pressed = TRUE;
10723 for (i = 0; i < 2; i++)
10725 char *default_grid_button[6][2] =
10731 { "111222", " vv " },
10732 { "111222", " vv " }
10734 int grid_xsize = DEFAULT_GRID_XSIZE(i);
10735 int grid_ysize = DEFAULT_GRID_YSIZE(i);
10736 int min_xsize = MIN(6, grid_xsize);
10737 int min_ysize = MIN(6, grid_ysize);
10738 int startx = grid_xsize - min_xsize;
10739 int starty = grid_ysize - min_ysize;
10742 // virtual buttons grid can only be set to defaults if video is initialized
10743 // (this will be repeated if virtual buttons are not loaded from setup file)
10744 if (video.initialized)
10746 si->touch.grid_xsize[i] = grid_xsize;
10747 si->touch.grid_ysize[i] = grid_ysize;
10751 si->touch.grid_xsize[i] = -1;
10752 si->touch.grid_ysize[i] = -1;
10755 for (x = 0; x < MAX_GRID_XSIZE; x++)
10756 for (y = 0; y < MAX_GRID_YSIZE; y++)
10757 si->touch.grid_button[i][x][y] = CHAR_GRID_BUTTON_NONE;
10759 for (x = 0; x < min_xsize; x++)
10760 for (y = 0; y < min_ysize; y++)
10761 si->touch.grid_button[i][x][starty + y] =
10762 default_grid_button[y][0][x];
10764 for (x = 0; x < min_xsize; x++)
10765 for (y = 0; y < min_ysize; y++)
10766 si->touch.grid_button[i][startx + x][starty + y] =
10767 default_grid_button[y][1][x];
10770 si->touch.grid_initialized = video.initialized;
10772 si->editor.el_boulderdash = TRUE;
10773 si->editor.el_emerald_mine = TRUE;
10774 si->editor.el_emerald_mine_club = TRUE;
10775 si->editor.el_more = TRUE;
10776 si->editor.el_sokoban = TRUE;
10777 si->editor.el_supaplex = TRUE;
10778 si->editor.el_diamond_caves = TRUE;
10779 si->editor.el_dx_boulderdash = TRUE;
10781 si->editor.el_mirror_magic = TRUE;
10782 si->editor.el_deflektor = TRUE;
10784 si->editor.el_chars = TRUE;
10785 si->editor.el_steel_chars = TRUE;
10787 si->editor.el_classic = TRUE;
10788 si->editor.el_custom = TRUE;
10790 si->editor.el_user_defined = FALSE;
10791 si->editor.el_dynamic = TRUE;
10793 si->editor.el_headlines = TRUE;
10795 si->editor.show_element_token = FALSE;
10797 si->editor.show_read_only_warning = TRUE;
10799 si->editor.use_template_for_new_levels = TRUE;
10801 si->shortcut.save_game = DEFAULT_KEY_SAVE_GAME;
10802 si->shortcut.load_game = DEFAULT_KEY_LOAD_GAME;
10803 si->shortcut.toggle_pause = DEFAULT_KEY_TOGGLE_PAUSE;
10805 si->shortcut.focus_player[0] = DEFAULT_KEY_FOCUS_PLAYER_1;
10806 si->shortcut.focus_player[1] = DEFAULT_KEY_FOCUS_PLAYER_2;
10807 si->shortcut.focus_player[2] = DEFAULT_KEY_FOCUS_PLAYER_3;
10808 si->shortcut.focus_player[3] = DEFAULT_KEY_FOCUS_PLAYER_4;
10809 si->shortcut.focus_player_all = DEFAULT_KEY_FOCUS_PLAYER_ALL;
10811 si->shortcut.tape_eject = DEFAULT_KEY_TAPE_EJECT;
10812 si->shortcut.tape_extra = DEFAULT_KEY_TAPE_EXTRA;
10813 si->shortcut.tape_stop = DEFAULT_KEY_TAPE_STOP;
10814 si->shortcut.tape_pause = DEFAULT_KEY_TAPE_PAUSE;
10815 si->shortcut.tape_record = DEFAULT_KEY_TAPE_RECORD;
10816 si->shortcut.tape_play = DEFAULT_KEY_TAPE_PLAY;
10818 si->shortcut.sound_simple = DEFAULT_KEY_SOUND_SIMPLE;
10819 si->shortcut.sound_loops = DEFAULT_KEY_SOUND_LOOPS;
10820 si->shortcut.sound_music = DEFAULT_KEY_SOUND_MUSIC;
10822 si->shortcut.snap_left = DEFAULT_KEY_SNAP_LEFT;
10823 si->shortcut.snap_right = DEFAULT_KEY_SNAP_RIGHT;
10824 si->shortcut.snap_up = DEFAULT_KEY_SNAP_UP;
10825 si->shortcut.snap_down = DEFAULT_KEY_SNAP_DOWN;
10827 for (i = 0; i < MAX_PLAYERS; i++)
10829 si->input[i].use_joystick = FALSE;
10830 si->input[i].joy.device_name=getStringCopy(getDeviceNameFromJoystickNr(i));
10831 si->input[i].joy.xleft = JOYSTICK_XLEFT;
10832 si->input[i].joy.xmiddle = JOYSTICK_XMIDDLE;
10833 si->input[i].joy.xright = JOYSTICK_XRIGHT;
10834 si->input[i].joy.yupper = JOYSTICK_YUPPER;
10835 si->input[i].joy.ymiddle = JOYSTICK_YMIDDLE;
10836 si->input[i].joy.ylower = JOYSTICK_YLOWER;
10837 si->input[i].joy.snap = (i == 0 ? JOY_BUTTON_1 : 0);
10838 si->input[i].joy.drop = (i == 0 ? JOY_BUTTON_2 : 0);
10839 si->input[i].key.left = (i == 0 ? DEFAULT_KEY_LEFT : KSYM_UNDEFINED);
10840 si->input[i].key.right = (i == 0 ? DEFAULT_KEY_RIGHT : KSYM_UNDEFINED);
10841 si->input[i].key.up = (i == 0 ? DEFAULT_KEY_UP : KSYM_UNDEFINED);
10842 si->input[i].key.down = (i == 0 ? DEFAULT_KEY_DOWN : KSYM_UNDEFINED);
10843 si->input[i].key.snap = (i == 0 ? DEFAULT_KEY_SNAP : KSYM_UNDEFINED);
10844 si->input[i].key.drop = (i == 0 ? DEFAULT_KEY_DROP : KSYM_UNDEFINED);
10847 si->system.sdl_renderdriver = getStringCopy(ARG_DEFAULT);
10848 si->system.sdl_videodriver = getStringCopy(ARG_DEFAULT);
10849 si->system.sdl_audiodriver = getStringCopy(ARG_DEFAULT);
10850 si->system.audio_fragment_size = DEFAULT_AUDIO_FRAGMENT_SIZE;
10852 si->internal.program_title = getStringCopy(PROGRAM_TITLE_STRING);
10853 si->internal.program_version = getStringCopy(getProgramRealVersionString());
10854 si->internal.program_author = getStringCopy(PROGRAM_AUTHOR_STRING);
10855 si->internal.program_email = getStringCopy(PROGRAM_EMAIL_STRING);
10856 si->internal.program_website = getStringCopy(PROGRAM_WEBSITE_STRING);
10857 si->internal.program_copyright = getStringCopy(PROGRAM_COPYRIGHT_STRING);
10858 si->internal.program_company = getStringCopy(PROGRAM_COMPANY_STRING);
10860 si->internal.program_icon_file = getStringCopy(PROGRAM_ICON_FILENAME);
10862 si->internal.default_graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
10863 si->internal.default_sounds_set = getStringCopy(SND_CLASSIC_SUBDIR);
10864 si->internal.default_music_set = getStringCopy(MUS_CLASSIC_SUBDIR);
10866 si->internal.fallback_graphics_file = getStringCopy(UNDEFINED_FILENAME);
10867 si->internal.fallback_sounds_file = getStringCopy(UNDEFINED_FILENAME);
10868 si->internal.fallback_music_file = getStringCopy(UNDEFINED_FILENAME);
10870 si->internal.default_level_series = getStringCopy(UNDEFINED_LEVELSET);
10871 si->internal.choose_from_top_leveldir = FALSE;
10872 si->internal.show_scaling_in_title = TRUE;
10873 si->internal.create_user_levelset = TRUE;
10875 si->internal.default_window_width = WIN_XSIZE_DEFAULT;
10876 si->internal.default_window_height = WIN_YSIZE_DEFAULT;
10878 si->debug.frame_delay[0] = DEFAULT_FRAME_DELAY_0;
10879 si->debug.frame_delay[1] = DEFAULT_FRAME_DELAY_1;
10880 si->debug.frame_delay[2] = DEFAULT_FRAME_DELAY_2;
10881 si->debug.frame_delay[3] = DEFAULT_FRAME_DELAY_3;
10882 si->debug.frame_delay[4] = DEFAULT_FRAME_DELAY_4;
10883 si->debug.frame_delay[5] = DEFAULT_FRAME_DELAY_5;
10884 si->debug.frame_delay[6] = DEFAULT_FRAME_DELAY_6;
10885 si->debug.frame_delay[7] = DEFAULT_FRAME_DELAY_7;
10886 si->debug.frame_delay[8] = DEFAULT_FRAME_DELAY_8;
10887 si->debug.frame_delay[9] = DEFAULT_FRAME_DELAY_9;
10889 si->debug.frame_delay_key[0] = DEFAULT_KEY_FRAME_DELAY_0;
10890 si->debug.frame_delay_key[1] = DEFAULT_KEY_FRAME_DELAY_1;
10891 si->debug.frame_delay_key[2] = DEFAULT_KEY_FRAME_DELAY_2;
10892 si->debug.frame_delay_key[3] = DEFAULT_KEY_FRAME_DELAY_3;
10893 si->debug.frame_delay_key[4] = DEFAULT_KEY_FRAME_DELAY_4;
10894 si->debug.frame_delay_key[5] = DEFAULT_KEY_FRAME_DELAY_5;
10895 si->debug.frame_delay_key[6] = DEFAULT_KEY_FRAME_DELAY_6;
10896 si->debug.frame_delay_key[7] = DEFAULT_KEY_FRAME_DELAY_7;
10897 si->debug.frame_delay_key[8] = DEFAULT_KEY_FRAME_DELAY_8;
10898 si->debug.frame_delay_key[9] = DEFAULT_KEY_FRAME_DELAY_9;
10900 si->debug.frame_delay_use_mod_key = DEFAULT_FRAME_DELAY_USE_MOD_KEY;
10901 si->debug.frame_delay_game_only = DEFAULT_FRAME_DELAY_GAME_ONLY;
10903 si->debug.show_frames_per_second = FALSE;
10905 si->debug.xsn_mode = AUTO;
10906 si->debug.xsn_percent = 0;
10908 si->options.verbose = FALSE;
10910 #if defined(PLATFORM_ANDROID)
10911 si->fullscreen = TRUE;
10914 setHideSetupEntry(&setup.debug.xsn_mode);
10917 static void setSetupInfoToDefaults_AutoSetup(struct SetupInfo *si)
10919 si->auto_setup.editor_zoom_tilesize = MINI_TILESIZE;
10922 static void setSetupInfoToDefaults_ServerSetup(struct SetupInfo *si)
10924 si->player_uuid = NULL; // (will be set later)
10925 si->player_version = 1; // (will be set later)
10927 si->use_api_server = TRUE;
10928 si->api_server_hostname = getStringCopy(API_SERVER_HOSTNAME);
10929 si->api_server_password = getStringCopy(UNDEFINED_PASSWORD);
10930 si->ask_for_uploading_tapes = TRUE;
10931 si->ask_for_remaining_tapes = FALSE;
10932 si->provide_uploading_tapes = TRUE;
10933 si->ask_for_using_api_server = TRUE;
10934 si->has_remaining_tapes = FALSE;
10937 static void setSetupInfoToDefaults_EditorCascade(struct SetupInfo *si)
10939 si->editor_cascade.el_bd = TRUE;
10940 si->editor_cascade.el_em = TRUE;
10941 si->editor_cascade.el_emc = TRUE;
10942 si->editor_cascade.el_rnd = TRUE;
10943 si->editor_cascade.el_sb = TRUE;
10944 si->editor_cascade.el_sp = TRUE;
10945 si->editor_cascade.el_dc = TRUE;
10946 si->editor_cascade.el_dx = TRUE;
10948 si->editor_cascade.el_mm = TRUE;
10949 si->editor_cascade.el_df = TRUE;
10951 si->editor_cascade.el_chars = FALSE;
10952 si->editor_cascade.el_steel_chars = FALSE;
10953 si->editor_cascade.el_ce = FALSE;
10954 si->editor_cascade.el_ge = FALSE;
10955 si->editor_cascade.el_es = FALSE;
10956 si->editor_cascade.el_ref = FALSE;
10957 si->editor_cascade.el_user = FALSE;
10958 si->editor_cascade.el_dynamic = FALSE;
10961 #define MAX_HIDE_SETUP_TOKEN_SIZE 20
10963 static char *getHideSetupToken(void *setup_value)
10965 static char hide_setup_token[MAX_HIDE_SETUP_TOKEN_SIZE];
10967 if (setup_value != NULL)
10968 snprintf(hide_setup_token, MAX_HIDE_SETUP_TOKEN_SIZE, "%p", setup_value);
10970 return hide_setup_token;
10973 void setHideSetupEntry(void *setup_value)
10975 char *hide_setup_token = getHideSetupToken(setup_value);
10977 if (hide_setup_hash == NULL)
10978 hide_setup_hash = newSetupFileHash();
10980 if (setup_value != NULL)
10981 setHashEntry(hide_setup_hash, hide_setup_token, "");
10984 void removeHideSetupEntry(void *setup_value)
10986 char *hide_setup_token = getHideSetupToken(setup_value);
10988 if (setup_value != NULL)
10989 removeHashEntry(hide_setup_hash, hide_setup_token);
10992 boolean hideSetupEntry(void *setup_value)
10994 char *hide_setup_token = getHideSetupToken(setup_value);
10996 return (setup_value != NULL &&
10997 getHashEntry(hide_setup_hash, hide_setup_token) != NULL);
11000 static void setSetupInfoFromTokenText(SetupFileHash *setup_file_hash,
11001 struct TokenInfo *token_info,
11002 int token_nr, char *token_text)
11004 char *token_hide_text = getStringCat2(token_text, ".hide");
11005 char *token_hide_value = getHashEntry(setup_file_hash, token_hide_text);
11007 // set the value of this setup option in the setup option structure
11008 setSetupInfo(token_info, token_nr, getHashEntry(setup_file_hash, token_text));
11010 // check if this setup option should be hidden in the setup menu
11011 if (token_hide_value != NULL && get_boolean_from_string(token_hide_value))
11012 setHideSetupEntry(token_info[token_nr].value);
11014 free(token_hide_text);
11017 static void setSetupInfoFromTokenInfo(SetupFileHash *setup_file_hash,
11018 struct TokenInfo *token_info,
11021 setSetupInfoFromTokenText(setup_file_hash, token_info, token_nr,
11022 token_info[token_nr].text);
11025 static void decodeSetupFileHash_Default(SetupFileHash *setup_file_hash)
11029 if (!setup_file_hash)
11032 for (i = 0; i < ARRAY_SIZE(global_setup_tokens); i++)
11033 setSetupInfoFromTokenInfo(setup_file_hash, global_setup_tokens, i);
11035 setup.touch.grid_initialized = TRUE;
11036 for (i = 0; i < 2; i++)
11038 int grid_xsize = setup.touch.grid_xsize[i];
11039 int grid_ysize = setup.touch.grid_ysize[i];
11042 // if virtual buttons are not loaded from setup file, repeat initializing
11043 // virtual buttons grid with default values later when video is initialized
11044 if (grid_xsize == -1 ||
11047 setup.touch.grid_initialized = FALSE;
11052 for (y = 0; y < grid_ysize; y++)
11054 char token_string[MAX_LINE_LEN];
11056 sprintf(token_string, "touch.virtual_buttons.%d.%02d", i, y);
11058 char *value_string = getHashEntry(setup_file_hash, token_string);
11060 if (value_string == NULL)
11063 for (x = 0; x < grid_xsize; x++)
11065 char c = value_string[x];
11067 setup.touch.grid_button[i][x][y] =
11068 (c == '.' ? CHAR_GRID_BUTTON_NONE : c);
11073 for (i = 0; i < ARRAY_SIZE(editor_setup_tokens); i++)
11074 setSetupInfoFromTokenInfo(setup_file_hash, editor_setup_tokens, i);
11076 for (i = 0; i < ARRAY_SIZE(shortcut_setup_tokens); i++)
11077 setSetupInfoFromTokenInfo(setup_file_hash, shortcut_setup_tokens, i);
11079 for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
11083 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
11085 setup_input = setup.input[pnr];
11086 for (i = 0; i < ARRAY_SIZE(player_setup_tokens); i++)
11088 char full_token[100];
11090 sprintf(full_token, "%s%s", prefix, player_setup_tokens[i].text);
11091 setSetupInfoFromTokenText(setup_file_hash, player_setup_tokens, i,
11094 setup.input[pnr] = setup_input;
11097 for (i = 0; i < ARRAY_SIZE(system_setup_tokens); i++)
11098 setSetupInfoFromTokenInfo(setup_file_hash, system_setup_tokens, i);
11100 for (i = 0; i < ARRAY_SIZE(internal_setup_tokens); i++)
11101 setSetupInfoFromTokenInfo(setup_file_hash, internal_setup_tokens, i);
11103 for (i = 0; i < ARRAY_SIZE(debug_setup_tokens); i++)
11104 setSetupInfoFromTokenInfo(setup_file_hash, debug_setup_tokens, i);
11106 for (i = 0; i < ARRAY_SIZE(options_setup_tokens); i++)
11107 setSetupInfoFromTokenInfo(setup_file_hash, options_setup_tokens, i);
11109 setHideRelatedSetupEntries();
11112 static void decodeSetupFileHash_AutoSetup(SetupFileHash *setup_file_hash)
11116 if (!setup_file_hash)
11119 for (i = 0; i < ARRAY_SIZE(auto_setup_tokens); i++)
11120 setSetupInfo(auto_setup_tokens, i,
11121 getHashEntry(setup_file_hash,
11122 auto_setup_tokens[i].text));
11125 static void decodeSetupFileHash_ServerSetup(SetupFileHash *setup_file_hash)
11129 if (!setup_file_hash)
11132 for (i = 0; i < ARRAY_SIZE(server_setup_tokens); i++)
11133 setSetupInfo(server_setup_tokens, i,
11134 getHashEntry(setup_file_hash,
11135 server_setup_tokens[i].text));
11138 static void decodeSetupFileHash_EditorCascade(SetupFileHash *setup_file_hash)
11142 if (!setup_file_hash)
11145 for (i = 0; i < ARRAY_SIZE(editor_cascade_setup_tokens); i++)
11146 setSetupInfo(editor_cascade_setup_tokens, i,
11147 getHashEntry(setup_file_hash,
11148 editor_cascade_setup_tokens[i].text));
11151 void LoadUserNames(void)
11153 int last_user_nr = user.nr;
11156 if (global.user_names != NULL)
11158 for (i = 0; i < MAX_PLAYER_NAMES; i++)
11159 checked_free(global.user_names[i]);
11161 checked_free(global.user_names);
11164 global.user_names = checked_calloc(MAX_PLAYER_NAMES * sizeof(char *));
11166 for (i = 0; i < MAX_PLAYER_NAMES; i++)
11170 SetupFileHash *setup_file_hash = loadSetupFileHash(getSetupFilename());
11172 if (setup_file_hash)
11174 char *player_name = getHashEntry(setup_file_hash, "player_name");
11176 global.user_names[i] = getFixedUserName(player_name);
11178 freeSetupFileHash(setup_file_hash);
11181 if (global.user_names[i] == NULL)
11182 global.user_names[i] = getStringCopy(getDefaultUserName(i));
11185 user.nr = last_user_nr;
11188 void LoadSetupFromFilename(char *filename)
11190 SetupFileHash *setup_file_hash = loadSetupFileHash(filename);
11192 if (setup_file_hash)
11194 decodeSetupFileHash_Default(setup_file_hash);
11196 freeSetupFileHash(setup_file_hash);
11200 Debug("setup", "using default setup values");
11204 static void LoadSetup_SpecialPostProcessing(void)
11206 char *player_name_new;
11208 // needed to work around problems with fixed length strings
11209 player_name_new = getFixedUserName(setup.player_name);
11210 free(setup.player_name);
11211 setup.player_name = player_name_new;
11213 // "scroll_delay: on(3) / off(0)" was replaced by scroll delay value
11214 if (setup.scroll_delay == FALSE)
11216 setup.scroll_delay_value = MIN_SCROLL_DELAY;
11217 setup.scroll_delay = TRUE; // now always "on"
11220 // make sure that scroll delay value stays inside valid range
11221 setup.scroll_delay_value =
11222 MIN(MAX(MIN_SCROLL_DELAY, setup.scroll_delay_value), MAX_SCROLL_DELAY);
11225 void LoadSetup_Default(void)
11229 // always start with reliable default values
11230 setSetupInfoToDefaults(&setup);
11232 // try to load setup values from default setup file
11233 filename = getDefaultSetupFilename();
11235 if (fileExists(filename))
11236 LoadSetupFromFilename(filename);
11238 // try to load setup values from user setup file
11239 filename = getSetupFilename();
11241 LoadSetupFromFilename(filename);
11243 LoadSetup_SpecialPostProcessing();
11246 void LoadSetup_AutoSetup(void)
11248 char *filename = getPath2(getSetupDir(), AUTOSETUP_FILENAME);
11249 SetupFileHash *setup_file_hash = NULL;
11251 // always start with reliable default values
11252 setSetupInfoToDefaults_AutoSetup(&setup);
11254 setup_file_hash = loadSetupFileHash(filename);
11256 if (setup_file_hash)
11258 decodeSetupFileHash_AutoSetup(setup_file_hash);
11260 freeSetupFileHash(setup_file_hash);
11266 void LoadSetup_ServerSetup(void)
11268 char *filename = getPath2(getSetupDir(), SERVERSETUP_FILENAME);
11269 SetupFileHash *setup_file_hash = NULL;
11271 // always start with reliable default values
11272 setSetupInfoToDefaults_ServerSetup(&setup);
11274 setup_file_hash = loadSetupFileHash(filename);
11276 if (setup_file_hash)
11278 decodeSetupFileHash_ServerSetup(setup_file_hash);
11280 freeSetupFileHash(setup_file_hash);
11285 if (setup.player_uuid == NULL)
11287 // player UUID does not yet exist in setup file
11288 setup.player_uuid = getStringCopy(getUUID());
11289 setup.player_version = 2;
11291 SaveSetup_ServerSetup();
11295 void LoadSetup_EditorCascade(void)
11297 char *filename = getPath2(getSetupDir(), EDITORCASCADE_FILENAME);
11298 SetupFileHash *setup_file_hash = NULL;
11300 // always start with reliable default values
11301 setSetupInfoToDefaults_EditorCascade(&setup);
11303 setup_file_hash = loadSetupFileHash(filename);
11305 if (setup_file_hash)
11307 decodeSetupFileHash_EditorCascade(setup_file_hash);
11309 freeSetupFileHash(setup_file_hash);
11315 void LoadSetup(void)
11317 LoadSetup_Default();
11318 LoadSetup_AutoSetup();
11319 LoadSetup_ServerSetup();
11320 LoadSetup_EditorCascade();
11323 static void addGameControllerMappingToHash(SetupFileHash *mappings_hash,
11324 char *mapping_line)
11326 char mapping_guid[MAX_LINE_LEN];
11327 char *mapping_start, *mapping_end;
11329 // get GUID from game controller mapping line: copy complete line
11330 strncpy(mapping_guid, mapping_line, MAX_LINE_LEN - 1);
11331 mapping_guid[MAX_LINE_LEN - 1] = '\0';
11333 // get GUID from game controller mapping line: cut after GUID part
11334 mapping_start = strchr(mapping_guid, ',');
11335 if (mapping_start != NULL)
11336 *mapping_start = '\0';
11338 // cut newline from game controller mapping line
11339 mapping_end = strchr(mapping_line, '\n');
11340 if (mapping_end != NULL)
11341 *mapping_end = '\0';
11343 // add mapping entry to game controller mappings hash
11344 setHashEntry(mappings_hash, mapping_guid, mapping_line);
11347 static void LoadSetup_ReadGameControllerMappings(SetupFileHash *mappings_hash,
11352 if (!(file = fopen(filename, MODE_READ)))
11354 Warn("cannot read game controller mappings file '%s'", filename);
11359 while (!feof(file))
11361 char line[MAX_LINE_LEN];
11363 if (!fgets(line, MAX_LINE_LEN, file))
11366 addGameControllerMappingToHash(mappings_hash, line);
11372 void SaveSetup_Default(void)
11374 char *filename = getSetupFilename();
11378 InitUserDataDirectory();
11380 if (!(file = fopen(filename, MODE_WRITE)))
11382 Warn("cannot write setup file '%s'", filename);
11387 fprintFileHeader(file, SETUP_FILENAME);
11389 for (i = 0; i < ARRAY_SIZE(global_setup_tokens); i++)
11391 // just to make things nicer :)
11392 if (global_setup_tokens[i].value == &setup.multiple_users ||
11393 global_setup_tokens[i].value == &setup.sound ||
11394 global_setup_tokens[i].value == &setup.graphics_set ||
11395 global_setup_tokens[i].value == &setup.volume_simple ||
11396 global_setup_tokens[i].value == &setup.network_mode ||
11397 global_setup_tokens[i].value == &setup.touch.control_type ||
11398 global_setup_tokens[i].value == &setup.touch.grid_xsize[0] ||
11399 global_setup_tokens[i].value == &setup.touch.grid_xsize[1])
11400 fprintf(file, "\n");
11402 fprintf(file, "%s\n", getSetupLine(global_setup_tokens, "", i));
11405 for (i = 0; i < 2; i++)
11407 int grid_xsize = setup.touch.grid_xsize[i];
11408 int grid_ysize = setup.touch.grid_ysize[i];
11411 fprintf(file, "\n");
11413 for (y = 0; y < grid_ysize; y++)
11415 char token_string[MAX_LINE_LEN];
11416 char value_string[MAX_LINE_LEN];
11418 sprintf(token_string, "touch.virtual_buttons.%d.%02d", i, y);
11420 for (x = 0; x < grid_xsize; x++)
11422 char c = setup.touch.grid_button[i][x][y];
11424 value_string[x] = (c == CHAR_GRID_BUTTON_NONE ? '.' : c);
11427 value_string[grid_xsize] = '\0';
11429 fprintf(file, "%s\n", getFormattedSetupEntry(token_string, value_string));
11433 fprintf(file, "\n");
11434 for (i = 0; i < ARRAY_SIZE(editor_setup_tokens); i++)
11435 fprintf(file, "%s\n", getSetupLine(editor_setup_tokens, "", i));
11437 fprintf(file, "\n");
11438 for (i = 0; i < ARRAY_SIZE(shortcut_setup_tokens); i++)
11439 fprintf(file, "%s\n", getSetupLine(shortcut_setup_tokens, "", i));
11441 for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
11445 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
11446 fprintf(file, "\n");
11448 setup_input = setup.input[pnr];
11449 for (i = 0; i < ARRAY_SIZE(player_setup_tokens); i++)
11450 fprintf(file, "%s\n", getSetupLine(player_setup_tokens, prefix, i));
11453 fprintf(file, "\n");
11454 for (i = 0; i < ARRAY_SIZE(system_setup_tokens); i++)
11455 fprintf(file, "%s\n", getSetupLine(system_setup_tokens, "", i));
11457 // (internal setup values not saved to user setup file)
11459 fprintf(file, "\n");
11460 for (i = 0; i < ARRAY_SIZE(debug_setup_tokens); i++)
11461 if (!strPrefix(debug_setup_tokens[i].text, "debug.xsn_") ||
11462 setup.debug.xsn_mode != AUTO)
11463 fprintf(file, "%s\n", getSetupLine(debug_setup_tokens, "", i));
11465 fprintf(file, "\n");
11466 for (i = 0; i < ARRAY_SIZE(options_setup_tokens); i++)
11467 fprintf(file, "%s\n", getSetupLine(options_setup_tokens, "", i));
11471 SetFilePermissions(filename, PERMS_PRIVATE);
11474 void SaveSetup_AutoSetup(void)
11476 char *filename = getPath2(getSetupDir(), AUTOSETUP_FILENAME);
11480 InitUserDataDirectory();
11482 if (!(file = fopen(filename, MODE_WRITE)))
11484 Warn("cannot write auto setup file '%s'", filename);
11491 fprintFileHeader(file, AUTOSETUP_FILENAME);
11493 for (i = 0; i < ARRAY_SIZE(auto_setup_tokens); i++)
11494 fprintf(file, "%s\n", getSetupLine(auto_setup_tokens, "", i));
11498 SetFilePermissions(filename, PERMS_PRIVATE);
11503 void SaveSetup_ServerSetup(void)
11505 char *filename = getPath2(getSetupDir(), SERVERSETUP_FILENAME);
11509 InitUserDataDirectory();
11511 if (!(file = fopen(filename, MODE_WRITE)))
11513 Warn("cannot write server setup file '%s'", filename);
11520 fprintFileHeader(file, SERVERSETUP_FILENAME);
11522 for (i = 0; i < ARRAY_SIZE(server_setup_tokens); i++)
11524 // just to make things nicer :)
11525 if (server_setup_tokens[i].value == &setup.use_api_server)
11526 fprintf(file, "\n");
11528 fprintf(file, "%s\n", getSetupLine(server_setup_tokens, "", i));
11533 SetFilePermissions(filename, PERMS_PRIVATE);
11538 void SaveSetup_EditorCascade(void)
11540 char *filename = getPath2(getSetupDir(), EDITORCASCADE_FILENAME);
11544 InitUserDataDirectory();
11546 if (!(file = fopen(filename, MODE_WRITE)))
11548 Warn("cannot write editor cascade state file '%s'", filename);
11555 fprintFileHeader(file, EDITORCASCADE_FILENAME);
11557 for (i = 0; i < ARRAY_SIZE(editor_cascade_setup_tokens); i++)
11558 fprintf(file, "%s\n", getSetupLine(editor_cascade_setup_tokens, "", i));
11562 SetFilePermissions(filename, PERMS_PRIVATE);
11567 void SaveSetup(void)
11569 SaveSetup_Default();
11570 SaveSetup_AutoSetup();
11571 SaveSetup_ServerSetup();
11572 SaveSetup_EditorCascade();
11575 static void SaveSetup_WriteGameControllerMappings(SetupFileHash *mappings_hash,
11580 if (!(file = fopen(filename, MODE_WRITE)))
11582 Warn("cannot write game controller mappings file '%s'", filename);
11587 BEGIN_HASH_ITERATION(mappings_hash, itr)
11589 fprintf(file, "%s\n", HASH_ITERATION_VALUE(itr));
11591 END_HASH_ITERATION(mappings_hash, itr)
11596 void SaveSetup_AddGameControllerMapping(char *mapping)
11598 char *filename = getPath2(getSetupDir(), GAMECONTROLLER_BASENAME);
11599 SetupFileHash *mappings_hash = newSetupFileHash();
11601 InitUserDataDirectory();
11603 // load existing personal game controller mappings
11604 LoadSetup_ReadGameControllerMappings(mappings_hash, filename);
11606 // add new mapping to personal game controller mappings
11607 addGameControllerMappingToHash(mappings_hash, mapping);
11609 // save updated personal game controller mappings
11610 SaveSetup_WriteGameControllerMappings(mappings_hash, filename);
11612 freeSetupFileHash(mappings_hash);
11616 void LoadCustomElementDescriptions(void)
11618 char *filename = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
11619 SetupFileHash *setup_file_hash;
11622 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
11624 if (element_info[i].custom_description != NULL)
11626 free(element_info[i].custom_description);
11627 element_info[i].custom_description = NULL;
11631 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
11634 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
11636 char *token = getStringCat2(element_info[i].token_name, ".name");
11637 char *value = getHashEntry(setup_file_hash, token);
11640 element_info[i].custom_description = getStringCopy(value);
11645 freeSetupFileHash(setup_file_hash);
11648 static int getElementFromToken(char *token)
11650 char *value = getHashEntry(element_token_hash, token);
11653 return atoi(value);
11655 Warn("unknown element token '%s'", token);
11657 return EL_UNDEFINED;
11660 void FreeGlobalAnimEventInfo(void)
11662 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
11664 if (gaei->event_list == NULL)
11669 for (i = 0; i < gaei->num_event_lists; i++)
11671 checked_free(gaei->event_list[i]->event_value);
11672 checked_free(gaei->event_list[i]);
11675 checked_free(gaei->event_list);
11677 gaei->event_list = NULL;
11678 gaei->num_event_lists = 0;
11681 static int AddGlobalAnimEventList(void)
11683 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
11684 int list_pos = gaei->num_event_lists++;
11686 gaei->event_list = checked_realloc(gaei->event_list, gaei->num_event_lists *
11687 sizeof(struct GlobalAnimEventListInfo *));
11689 gaei->event_list[list_pos] =
11690 checked_calloc(sizeof(struct GlobalAnimEventListInfo));
11692 struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
11694 gaeli->event_value = NULL;
11695 gaeli->num_event_values = 0;
11700 static int AddGlobalAnimEventValue(int list_pos, int event_value)
11702 // do not add empty global animation events
11703 if (event_value == ANIM_EVENT_NONE)
11706 // if list position is undefined, create new list
11707 if (list_pos == ANIM_EVENT_UNDEFINED)
11708 list_pos = AddGlobalAnimEventList();
11710 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
11711 struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
11712 int value_pos = gaeli->num_event_values++;
11714 gaeli->event_value = checked_realloc(gaeli->event_value,
11715 gaeli->num_event_values * sizeof(int *));
11717 gaeli->event_value[value_pos] = event_value;
11722 int GetGlobalAnimEventValue(int list_pos, int value_pos)
11724 if (list_pos == ANIM_EVENT_UNDEFINED)
11725 return ANIM_EVENT_NONE;
11727 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
11728 struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
11730 return gaeli->event_value[value_pos];
11733 int GetGlobalAnimEventValueCount(int list_pos)
11735 if (list_pos == ANIM_EVENT_UNDEFINED)
11738 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
11739 struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
11741 return gaeli->num_event_values;
11744 // This function checks if a string <s> of the format "string1, string2, ..."
11745 // exactly contains a string <s_contained>.
11747 static boolean string_has_parameter(char *s, char *s_contained)
11751 if (s == NULL || s_contained == NULL)
11754 if (strlen(s_contained) > strlen(s))
11757 if (strncmp(s, s_contained, strlen(s_contained)) == 0)
11759 char next_char = s[strlen(s_contained)];
11761 // check if next character is delimiter or whitespace
11762 return (next_char == ',' || next_char == '\0' ||
11763 next_char == ' ' || next_char == '\t' ? TRUE : FALSE);
11766 // check if string contains another parameter string after a comma
11767 substring = strchr(s, ',');
11768 if (substring == NULL) // string does not contain a comma
11771 // advance string pointer to next character after the comma
11774 // skip potential whitespaces after the comma
11775 while (*substring == ' ' || *substring == '\t')
11778 return string_has_parameter(substring, s_contained);
11781 static int get_anim_parameter_value(char *s)
11783 int event_value[] =
11791 char *pattern_1[] =
11799 char *pattern_2 = ".part_";
11800 char *matching_char = NULL;
11802 int pattern_1_len = 0;
11803 int result = ANIM_EVENT_NONE;
11806 for (i = 0; i < ARRAY_SIZE(event_value); i++)
11808 matching_char = strstr(s_ptr, pattern_1[i]);
11809 pattern_1_len = strlen(pattern_1[i]);
11810 result = event_value[i];
11812 if (matching_char != NULL)
11816 if (matching_char == NULL)
11817 return ANIM_EVENT_NONE;
11819 s_ptr = matching_char + pattern_1_len;
11821 // check for main animation number ("anim_X" or "anim_XX")
11822 if (*s_ptr >= '0' && *s_ptr <= '9')
11824 int gic_anim_nr = (*s_ptr++ - '0');
11826 if (*s_ptr >= '0' && *s_ptr <= '9')
11827 gic_anim_nr = 10 * gic_anim_nr + (*s_ptr++ - '0');
11829 if (gic_anim_nr < 1 || gic_anim_nr > MAX_GLOBAL_ANIMS)
11830 return ANIM_EVENT_NONE;
11832 result |= gic_anim_nr << ANIM_EVENT_ANIM_BIT;
11836 // invalid main animation number specified
11838 return ANIM_EVENT_NONE;
11841 // check for animation part number ("part_X" or "part_XX") (optional)
11842 if (strPrefix(s_ptr, pattern_2))
11844 s_ptr += strlen(pattern_2);
11846 if (*s_ptr >= '0' && *s_ptr <= '9')
11848 int gic_part_nr = (*s_ptr++ - '0');
11850 if (*s_ptr >= '0' && *s_ptr <= '9')
11851 gic_part_nr = 10 * gic_part_nr + (*s_ptr++ - '0');
11853 if (gic_part_nr < 1 || gic_part_nr > MAX_GLOBAL_ANIM_PARTS)
11854 return ANIM_EVENT_NONE;
11856 result |= gic_part_nr << ANIM_EVENT_PART_BIT;
11860 // invalid animation part number specified
11862 return ANIM_EVENT_NONE;
11866 // discard result if next character is neither delimiter nor whitespace
11867 if (!(*s_ptr == ',' || *s_ptr == '\0' ||
11868 *s_ptr == ' ' || *s_ptr == '\t'))
11869 return ANIM_EVENT_NONE;
11874 static int get_anim_parameter_values(char *s)
11876 int list_pos = ANIM_EVENT_UNDEFINED;
11877 int event_value = ANIM_EVENT_DEFAULT;
11879 if (string_has_parameter(s, "any"))
11880 event_value |= ANIM_EVENT_ANY;
11882 if (string_has_parameter(s, "click:self") ||
11883 string_has_parameter(s, "click") ||
11884 string_has_parameter(s, "self"))
11885 event_value |= ANIM_EVENT_SELF;
11887 if (string_has_parameter(s, "unclick:any"))
11888 event_value |= ANIM_EVENT_UNCLICK_ANY;
11890 // if animation event found, add it to global animation event list
11891 if (event_value != ANIM_EVENT_NONE)
11892 list_pos = AddGlobalAnimEventValue(list_pos, event_value);
11896 // add optional "click:anim_X" or "click:anim_X.part_X" parameter
11897 event_value = get_anim_parameter_value(s);
11899 // if animation event found, add it to global animation event list
11900 if (event_value != ANIM_EVENT_NONE)
11901 list_pos = AddGlobalAnimEventValue(list_pos, event_value);
11903 // continue with next part of the string, starting with next comma
11904 s = strchr(s + 1, ',');
11910 static int get_anim_action_parameter_value(char *token)
11912 // check most common default case first to massively speed things up
11913 if (strEqual(token, ARG_UNDEFINED))
11914 return ANIM_EVENT_ACTION_NONE;
11916 int result = getImageIDFromToken(token);
11920 char *gfx_token = getStringCat2("gfx.", token);
11922 result = getImageIDFromToken(gfx_token);
11924 checked_free(gfx_token);
11929 Key key = getKeyFromX11KeyName(token);
11931 if (key != KSYM_UNDEFINED)
11932 result = -(int)key;
11936 result = ANIM_EVENT_ACTION_NONE;
11941 int get_parameter_value(char *value_raw, char *suffix, int type)
11943 char *value = getStringToLower(value_raw);
11944 int result = 0; // probably a save default value
11946 if (strEqual(suffix, ".direction"))
11948 result = (strEqual(value, "left") ? MV_LEFT :
11949 strEqual(value, "right") ? MV_RIGHT :
11950 strEqual(value, "up") ? MV_UP :
11951 strEqual(value, "down") ? MV_DOWN : MV_NONE);
11953 else if (strEqual(suffix, ".position"))
11955 result = (strEqual(value, "left") ? POS_LEFT :
11956 strEqual(value, "right") ? POS_RIGHT :
11957 strEqual(value, "top") ? POS_TOP :
11958 strEqual(value, "upper") ? POS_UPPER :
11959 strEqual(value, "middle") ? POS_MIDDLE :
11960 strEqual(value, "lower") ? POS_LOWER :
11961 strEqual(value, "bottom") ? POS_BOTTOM :
11962 strEqual(value, "any") ? POS_ANY :
11963 strEqual(value, "last") ? POS_LAST : POS_UNDEFINED);
11965 else if (strEqual(suffix, ".align"))
11967 result = (strEqual(value, "left") ? ALIGN_LEFT :
11968 strEqual(value, "right") ? ALIGN_RIGHT :
11969 strEqual(value, "center") ? ALIGN_CENTER :
11970 strEqual(value, "middle") ? ALIGN_CENTER : ALIGN_DEFAULT);
11972 else if (strEqual(suffix, ".valign"))
11974 result = (strEqual(value, "top") ? VALIGN_TOP :
11975 strEqual(value, "bottom") ? VALIGN_BOTTOM :
11976 strEqual(value, "middle") ? VALIGN_MIDDLE :
11977 strEqual(value, "center") ? VALIGN_MIDDLE : VALIGN_DEFAULT);
11979 else if (strEqual(suffix, ".anim_mode"))
11981 result = (string_has_parameter(value, "none") ? ANIM_NONE :
11982 string_has_parameter(value, "loop") ? ANIM_LOOP :
11983 string_has_parameter(value, "linear") ? ANIM_LINEAR :
11984 string_has_parameter(value, "pingpong") ? ANIM_PINGPONG :
11985 string_has_parameter(value, "pingpong2") ? ANIM_PINGPONG2 :
11986 string_has_parameter(value, "random") ? ANIM_RANDOM :
11987 string_has_parameter(value, "random_static") ? ANIM_RANDOM_STATIC :
11988 string_has_parameter(value, "ce_value") ? ANIM_CE_VALUE :
11989 string_has_parameter(value, "ce_score") ? ANIM_CE_SCORE :
11990 string_has_parameter(value, "ce_delay") ? ANIM_CE_DELAY :
11991 string_has_parameter(value, "horizontal") ? ANIM_HORIZONTAL :
11992 string_has_parameter(value, "vertical") ? ANIM_VERTICAL :
11993 string_has_parameter(value, "centered") ? ANIM_CENTERED :
11994 string_has_parameter(value, "all") ? ANIM_ALL :
11995 string_has_parameter(value, "tiled") ? ANIM_TILED :
11998 if (string_has_parameter(value, "once"))
11999 result |= ANIM_ONCE;
12001 if (string_has_parameter(value, "reverse"))
12002 result |= ANIM_REVERSE;
12004 if (string_has_parameter(value, "opaque_player"))
12005 result |= ANIM_OPAQUE_PLAYER;
12007 if (string_has_parameter(value, "static_panel"))
12008 result |= ANIM_STATIC_PANEL;
12010 else if (strEqual(suffix, ".init_event") ||
12011 strEqual(suffix, ".anim_event"))
12013 result = get_anim_parameter_values(value);
12015 else if (strEqual(suffix, ".init_delay_action") ||
12016 strEqual(suffix, ".anim_delay_action") ||
12017 strEqual(suffix, ".post_delay_action") ||
12018 strEqual(suffix, ".init_event_action") ||
12019 strEqual(suffix, ".anim_event_action"))
12021 result = get_anim_action_parameter_value(value_raw);
12023 else if (strEqual(suffix, ".class"))
12025 result = (strEqual(value, ARG_UNDEFINED) ? ARG_UNDEFINED_VALUE :
12026 get_hash_from_key(value));
12028 else if (strEqual(suffix, ".style"))
12030 result = STYLE_DEFAULT;
12032 if (string_has_parameter(value, "accurate_borders"))
12033 result |= STYLE_ACCURATE_BORDERS;
12035 if (string_has_parameter(value, "inner_corners"))
12036 result |= STYLE_INNER_CORNERS;
12038 if (string_has_parameter(value, "reverse"))
12039 result |= STYLE_REVERSE;
12041 if (string_has_parameter(value, "leftmost_position"))
12042 result |= STYLE_LEFTMOST_POSITION;
12044 if (string_has_parameter(value, "block_clicks"))
12045 result |= STYLE_BLOCK;
12047 if (string_has_parameter(value, "passthrough_clicks"))
12048 result |= STYLE_PASSTHROUGH;
12050 if (string_has_parameter(value, "multiple_actions"))
12051 result |= STYLE_MULTIPLE_ACTIONS;
12053 else if (strEqual(suffix, ".fade_mode"))
12055 result = (string_has_parameter(value, "none") ? FADE_MODE_NONE :
12056 string_has_parameter(value, "fade") ? FADE_MODE_FADE :
12057 string_has_parameter(value, "crossfade") ? FADE_MODE_CROSSFADE :
12058 string_has_parameter(value, "melt") ? FADE_MODE_MELT :
12059 string_has_parameter(value, "curtain") ? FADE_MODE_CURTAIN :
12060 FADE_MODE_DEFAULT);
12062 else if (strEqual(suffix, ".auto_delay_unit"))
12064 result = (string_has_parameter(value, "ms") ? AUTO_DELAY_UNIT_MS :
12065 string_has_parameter(value, "frames") ? AUTO_DELAY_UNIT_FRAMES :
12066 AUTO_DELAY_UNIT_DEFAULT);
12068 else if (strPrefix(suffix, ".font")) // (may also be ".font_xyz")
12070 result = gfx.get_font_from_token_function(value);
12072 else // generic parameter of type integer or boolean
12074 result = (strEqual(value, ARG_UNDEFINED) ? ARG_UNDEFINED_VALUE :
12075 type == TYPE_INTEGER ? get_integer_from_string(value) :
12076 type == TYPE_BOOLEAN ? get_boolean_from_string(value) :
12077 ARG_UNDEFINED_VALUE);
12085 static int get_token_parameter_value(char *token, char *value_raw)
12089 if (token == NULL || value_raw == NULL)
12090 return ARG_UNDEFINED_VALUE;
12092 suffix = strrchr(token, '.');
12093 if (suffix == NULL)
12096 if (strEqual(suffix, ".element"))
12097 return getElementFromToken(value_raw);
12099 // !!! USE CORRECT VALUE TYPE (currently works also for TYPE_BOOLEAN) !!!
12100 return get_parameter_value(value_raw, suffix, TYPE_INTEGER);
12103 void InitMenuDesignSettings_Static(void)
12107 // always start with reliable default values from static default config
12108 for (i = 0; image_config_vars[i].token != NULL; i++)
12110 char *value = getHashEntry(image_config_hash, image_config_vars[i].token);
12113 *image_config_vars[i].value =
12114 get_token_parameter_value(image_config_vars[i].token, value);
12118 static void InitMenuDesignSettings_SpecialPreProcessing(void)
12122 // the following initializes hierarchical values from static configuration
12124 // special case: initialize "ARG_DEFAULT" values in static default config
12125 // (e.g., initialize "[titlemessage].fade_mode" from "[title].fade_mode")
12126 titlescreen_initial_first_default.fade_mode =
12127 title_initial_first_default.fade_mode;
12128 titlescreen_initial_first_default.fade_delay =
12129 title_initial_first_default.fade_delay;
12130 titlescreen_initial_first_default.post_delay =
12131 title_initial_first_default.post_delay;
12132 titlescreen_initial_first_default.auto_delay =
12133 title_initial_first_default.auto_delay;
12134 titlescreen_initial_first_default.auto_delay_unit =
12135 title_initial_first_default.auto_delay_unit;
12136 titlescreen_first_default.fade_mode = title_first_default.fade_mode;
12137 titlescreen_first_default.fade_delay = title_first_default.fade_delay;
12138 titlescreen_first_default.post_delay = title_first_default.post_delay;
12139 titlescreen_first_default.auto_delay = title_first_default.auto_delay;
12140 titlescreen_first_default.auto_delay_unit =
12141 title_first_default.auto_delay_unit;
12142 titlemessage_initial_first_default.fade_mode =
12143 title_initial_first_default.fade_mode;
12144 titlemessage_initial_first_default.fade_delay =
12145 title_initial_first_default.fade_delay;
12146 titlemessage_initial_first_default.post_delay =
12147 title_initial_first_default.post_delay;
12148 titlemessage_initial_first_default.auto_delay =
12149 title_initial_first_default.auto_delay;
12150 titlemessage_initial_first_default.auto_delay_unit =
12151 title_initial_first_default.auto_delay_unit;
12152 titlemessage_first_default.fade_mode = title_first_default.fade_mode;
12153 titlemessage_first_default.fade_delay = title_first_default.fade_delay;
12154 titlemessage_first_default.post_delay = title_first_default.post_delay;
12155 titlemessage_first_default.auto_delay = title_first_default.auto_delay;
12156 titlemessage_first_default.auto_delay_unit =
12157 title_first_default.auto_delay_unit;
12159 titlescreen_initial_default.fade_mode = title_initial_default.fade_mode;
12160 titlescreen_initial_default.fade_delay = title_initial_default.fade_delay;
12161 titlescreen_initial_default.post_delay = title_initial_default.post_delay;
12162 titlescreen_initial_default.auto_delay = title_initial_default.auto_delay;
12163 titlescreen_initial_default.auto_delay_unit =
12164 title_initial_default.auto_delay_unit;
12165 titlescreen_default.fade_mode = title_default.fade_mode;
12166 titlescreen_default.fade_delay = title_default.fade_delay;
12167 titlescreen_default.post_delay = title_default.post_delay;
12168 titlescreen_default.auto_delay = title_default.auto_delay;
12169 titlescreen_default.auto_delay_unit = title_default.auto_delay_unit;
12170 titlemessage_initial_default.fade_mode = title_initial_default.fade_mode;
12171 titlemessage_initial_default.fade_delay = title_initial_default.fade_delay;
12172 titlemessage_initial_default.post_delay = title_initial_default.post_delay;
12173 titlemessage_initial_default.auto_delay_unit =
12174 title_initial_default.auto_delay_unit;
12175 titlemessage_default.fade_mode = title_default.fade_mode;
12176 titlemessage_default.fade_delay = title_default.fade_delay;
12177 titlemessage_default.post_delay = title_default.post_delay;
12178 titlemessage_default.auto_delay = title_default.auto_delay;
12179 titlemessage_default.auto_delay_unit = title_default.auto_delay_unit;
12181 // special case: initialize "ARG_DEFAULT" values in static default config
12182 // (e.g., init "titlemessage_1.fade_mode" from "[titlemessage].fade_mode")
12183 for (i = 0; i < MAX_NUM_TITLE_MESSAGES; i++)
12185 titlescreen_initial_first[i] = titlescreen_initial_first_default;
12186 titlescreen_first[i] = titlescreen_first_default;
12187 titlemessage_initial_first[i] = titlemessage_initial_first_default;
12188 titlemessage_first[i] = titlemessage_first_default;
12190 titlescreen_initial[i] = titlescreen_initial_default;
12191 titlescreen[i] = titlescreen_default;
12192 titlemessage_initial[i] = titlemessage_initial_default;
12193 titlemessage[i] = titlemessage_default;
12196 // special case: initialize "ARG_DEFAULT" values in static default config
12197 // (eg, init "menu.enter_screen.SCORES.xyz" from "menu.enter_screen.xyz")
12198 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
12200 if (i == GFX_SPECIAL_ARG_TITLE) // title values already initialized
12203 menu.enter_screen[i] = menu.enter_screen[GFX_SPECIAL_ARG_DEFAULT];
12204 menu.leave_screen[i] = menu.leave_screen[GFX_SPECIAL_ARG_DEFAULT];
12205 menu.next_screen[i] = menu.next_screen[GFX_SPECIAL_ARG_DEFAULT];
12208 // special case: initialize "ARG_DEFAULT" values in static default config
12209 // (eg, init "viewport.door_1.MAIN.xyz" from "viewport.door_1.xyz")
12210 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
12212 viewport.window[i] = viewport.window[GFX_SPECIAL_ARG_DEFAULT];
12213 viewport.playfield[i] = viewport.playfield[GFX_SPECIAL_ARG_DEFAULT];
12214 viewport.door_1[i] = viewport.door_1[GFX_SPECIAL_ARG_DEFAULT];
12216 if (i == GFX_SPECIAL_ARG_EDITOR) // editor values already initialized
12219 viewport.door_2[i] = viewport.door_2[GFX_SPECIAL_ARG_DEFAULT];
12223 static void InitMenuDesignSettings_SpecialPostProcessing(void)
12227 struct XY *dst, *src;
12229 game_buttons_xy[] =
12231 { &game.button.save, &game.button.stop },
12232 { &game.button.pause2, &game.button.pause },
12233 { &game.button.load, &game.button.play },
12234 { &game.button.undo, &game.button.stop },
12235 { &game.button.redo, &game.button.play },
12241 // special case: initialize later added SETUP list size from LEVELS value
12242 if (menu.list_size[GAME_MODE_SETUP] == -1)
12243 menu.list_size[GAME_MODE_SETUP] = menu.list_size[GAME_MODE_LEVELS];
12245 // set default position for snapshot buttons to stop/pause/play buttons
12246 for (i = 0; game_buttons_xy[i].dst != NULL; i++)
12247 if ((*game_buttons_xy[i].dst).x == -1 &&
12248 (*game_buttons_xy[i].dst).y == -1)
12249 *game_buttons_xy[i].dst = *game_buttons_xy[i].src;
12251 // --------------------------------------------------------------------------
12252 // dynamic viewports (including playfield margins, borders and alignments)
12253 // --------------------------------------------------------------------------
12255 // dynamic viewports currently only supported for landscape mode
12256 int display_width = MAX(video.display_width, video.display_height);
12257 int display_height = MIN(video.display_width, video.display_height);
12259 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
12261 struct RectWithBorder *vp_window = &viewport.window[i];
12262 struct RectWithBorder *vp_playfield = &viewport.playfield[i];
12263 struct RectWithBorder *vp_door_1 = &viewport.door_1[i];
12264 struct RectWithBorder *vp_door_2 = &viewport.door_2[i];
12265 boolean dynamic_window_width = (vp_window->min_width != -1);
12266 boolean dynamic_window_height = (vp_window->min_height != -1);
12267 boolean dynamic_playfield_width = (vp_playfield->min_width != -1);
12268 boolean dynamic_playfield_height = (vp_playfield->min_height != -1);
12270 // adjust window size if min/max width/height is specified
12272 if (vp_window->min_width != -1)
12274 int window_width = display_width;
12276 // when using static window height, use aspect ratio of display
12277 if (vp_window->min_height == -1)
12278 window_width = vp_window->height * display_width / display_height;
12280 vp_window->width = MAX(vp_window->min_width, window_width);
12283 if (vp_window->min_height != -1)
12285 int window_height = display_height;
12287 // when using static window width, use aspect ratio of display
12288 if (vp_window->min_width == -1)
12289 window_height = vp_window->width * display_height / display_width;
12291 vp_window->height = MAX(vp_window->min_height, window_height);
12294 if (vp_window->max_width != -1)
12295 vp_window->width = MIN(vp_window->width, vp_window->max_width);
12297 if (vp_window->max_height != -1)
12298 vp_window->height = MIN(vp_window->height, vp_window->max_height);
12300 int playfield_width = vp_window->width;
12301 int playfield_height = vp_window->height;
12303 // adjust playfield size and position according to specified margins
12305 playfield_width -= vp_playfield->margin_left;
12306 playfield_width -= vp_playfield->margin_right;
12308 playfield_height -= vp_playfield->margin_top;
12309 playfield_height -= vp_playfield->margin_bottom;
12311 // adjust playfield size if min/max width/height is specified
12313 if (vp_playfield->min_width != -1)
12314 vp_playfield->width = MAX(vp_playfield->min_width, playfield_width);
12316 if (vp_playfield->min_height != -1)
12317 vp_playfield->height = MAX(vp_playfield->min_height, playfield_height);
12319 if (vp_playfield->max_width != -1)
12320 vp_playfield->width = MIN(vp_playfield->width, vp_playfield->max_width);
12322 if (vp_playfield->max_height != -1)
12323 vp_playfield->height = MIN(vp_playfield->height,vp_playfield->max_height);
12325 // adjust playfield position according to specified alignment
12327 if (vp_playfield->align == ALIGN_LEFT || vp_playfield->x > 0)
12328 vp_playfield->x = ALIGNED_VP_XPOS(vp_playfield);
12329 else if (vp_playfield->align == ALIGN_CENTER)
12330 vp_playfield->x = playfield_width / 2 - vp_playfield->width / 2;
12331 else if (vp_playfield->align == ALIGN_RIGHT)
12332 vp_playfield->x += playfield_width - vp_playfield->width;
12334 if (vp_playfield->valign == VALIGN_TOP || vp_playfield->y > 0)
12335 vp_playfield->y = ALIGNED_VP_YPOS(vp_playfield);
12336 else if (vp_playfield->valign == VALIGN_MIDDLE)
12337 vp_playfield->y = playfield_height / 2 - vp_playfield->height / 2;
12338 else if (vp_playfield->valign == VALIGN_BOTTOM)
12339 vp_playfield->y += playfield_height - vp_playfield->height;
12341 vp_playfield->x += vp_playfield->margin_left;
12342 vp_playfield->y += vp_playfield->margin_top;
12344 // adjust individual playfield borders if only default border is specified
12346 if (vp_playfield->border_left == -1)
12347 vp_playfield->border_left = vp_playfield->border_size;
12348 if (vp_playfield->border_right == -1)
12349 vp_playfield->border_right = vp_playfield->border_size;
12350 if (vp_playfield->border_top == -1)
12351 vp_playfield->border_top = vp_playfield->border_size;
12352 if (vp_playfield->border_bottom == -1)
12353 vp_playfield->border_bottom = vp_playfield->border_size;
12355 // set dynamic playfield borders if borders are specified as undefined
12356 // (but only if window size was dynamic and playfield size was static)
12358 if (dynamic_window_width && !dynamic_playfield_width)
12360 if (vp_playfield->border_left == -1)
12362 vp_playfield->border_left = (vp_playfield->x -
12363 vp_playfield->margin_left);
12364 vp_playfield->x -= vp_playfield->border_left;
12365 vp_playfield->width += vp_playfield->border_left;
12368 if (vp_playfield->border_right == -1)
12370 vp_playfield->border_right = (vp_window->width -
12372 vp_playfield->width -
12373 vp_playfield->margin_right);
12374 vp_playfield->width += vp_playfield->border_right;
12378 if (dynamic_window_height && !dynamic_playfield_height)
12380 if (vp_playfield->border_top == -1)
12382 vp_playfield->border_top = (vp_playfield->y -
12383 vp_playfield->margin_top);
12384 vp_playfield->y -= vp_playfield->border_top;
12385 vp_playfield->height += vp_playfield->border_top;
12388 if (vp_playfield->border_bottom == -1)
12390 vp_playfield->border_bottom = (vp_window->height -
12392 vp_playfield->height -
12393 vp_playfield->margin_bottom);
12394 vp_playfield->height += vp_playfield->border_bottom;
12398 // adjust playfield size to be a multiple of a defined alignment tile size
12400 int align_size = vp_playfield->align_size;
12401 int playfield_xtiles = vp_playfield->width / align_size;
12402 int playfield_ytiles = vp_playfield->height / align_size;
12403 int playfield_width_corrected = playfield_xtiles * align_size;
12404 int playfield_height_corrected = playfield_ytiles * align_size;
12405 boolean is_playfield_mode = (i == GFX_SPECIAL_ARG_PLAYING ||
12406 i == GFX_SPECIAL_ARG_EDITOR);
12408 if (is_playfield_mode &&
12409 dynamic_playfield_width &&
12410 vp_playfield->width != playfield_width_corrected)
12412 int playfield_xdiff = vp_playfield->width - playfield_width_corrected;
12414 vp_playfield->width = playfield_width_corrected;
12416 if (vp_playfield->align == ALIGN_LEFT)
12418 vp_playfield->border_left += playfield_xdiff;
12420 else if (vp_playfield->align == ALIGN_RIGHT)
12422 vp_playfield->border_right += playfield_xdiff;
12424 else if (vp_playfield->align == ALIGN_CENTER)
12426 int border_left_diff = playfield_xdiff / 2;
12427 int border_right_diff = playfield_xdiff - border_left_diff;
12429 vp_playfield->border_left += border_left_diff;
12430 vp_playfield->border_right += border_right_diff;
12434 if (is_playfield_mode &&
12435 dynamic_playfield_height &&
12436 vp_playfield->height != playfield_height_corrected)
12438 int playfield_ydiff = vp_playfield->height - playfield_height_corrected;
12440 vp_playfield->height = playfield_height_corrected;
12442 if (vp_playfield->valign == VALIGN_TOP)
12444 vp_playfield->border_top += playfield_ydiff;
12446 else if (vp_playfield->align == VALIGN_BOTTOM)
12448 vp_playfield->border_right += playfield_ydiff;
12450 else if (vp_playfield->align == VALIGN_MIDDLE)
12452 int border_top_diff = playfield_ydiff / 2;
12453 int border_bottom_diff = playfield_ydiff - border_top_diff;
12455 vp_playfield->border_top += border_top_diff;
12456 vp_playfield->border_bottom += border_bottom_diff;
12460 // adjust door positions according to specified alignment
12462 for (j = 0; j < 2; j++)
12464 struct RectWithBorder *vp_door = (j == 0 ? vp_door_1 : vp_door_2);
12466 if (vp_door->align == ALIGN_LEFT || vp_door->x > 0)
12467 vp_door->x = ALIGNED_VP_XPOS(vp_door);
12468 else if (vp_door->align == ALIGN_CENTER)
12469 vp_door->x = vp_window->width / 2 - vp_door->width / 2;
12470 else if (vp_door->align == ALIGN_RIGHT)
12471 vp_door->x += vp_window->width - vp_door->width;
12473 if (vp_door->valign == VALIGN_TOP || vp_door->y > 0)
12474 vp_door->y = ALIGNED_VP_YPOS(vp_door);
12475 else if (vp_door->valign == VALIGN_MIDDLE)
12476 vp_door->y = vp_window->height / 2 - vp_door->height / 2;
12477 else if (vp_door->valign == VALIGN_BOTTOM)
12478 vp_door->y += vp_window->height - vp_door->height;
12483 static void InitMenuDesignSettings_SpecialPostProcessing_AfterGraphics(void)
12487 struct XYTileSize *dst, *src;
12490 editor_buttons_xy[] =
12493 &editor.button.element_left, &editor.palette.element_left,
12494 IMG_GFX_EDITOR_BUTTON_ELEMENT_LEFT
12497 &editor.button.element_middle, &editor.palette.element_middle,
12498 IMG_GFX_EDITOR_BUTTON_ELEMENT_MIDDLE
12501 &editor.button.element_right, &editor.palette.element_right,
12502 IMG_GFX_EDITOR_BUTTON_ELEMENT_RIGHT
12509 // set default position for element buttons to element graphics
12510 for (i = 0; editor_buttons_xy[i].dst != NULL; i++)
12512 if ((*editor_buttons_xy[i].dst).x == -1 &&
12513 (*editor_buttons_xy[i].dst).y == -1)
12515 struct GraphicInfo *gd = &graphic_info[editor_buttons_xy[i].graphic];
12517 gd->width = gd->height = editor_buttons_xy[i].src->tile_size;
12519 *editor_buttons_xy[i].dst = *editor_buttons_xy[i].src;
12523 // adjust editor palette rows and columns if specified to be dynamic
12525 if (editor.palette.cols == -1)
12527 int vp_width = viewport.playfield[GFX_SPECIAL_ARG_EDITOR].width;
12528 int bt_width = graphic_info[IMG_EDITOR_PALETTE_BUTTON].width;
12529 int sc_width = graphic_info[IMG_EDITOR_PALETTE_SCROLLBAR].width;
12531 editor.palette.cols = (vp_width - sc_width) / bt_width;
12533 if (editor.palette.x == -1)
12535 int palette_width = editor.palette.cols * bt_width + sc_width;
12537 editor.palette.x = (vp_width - palette_width) / 2;
12541 if (editor.palette.rows == -1)
12543 int vp_height = viewport.playfield[GFX_SPECIAL_ARG_EDITOR].height;
12544 int bt_height = graphic_info[IMG_EDITOR_PALETTE_BUTTON].height;
12545 int tx_height = getFontHeight(FONT_TEXT_2);
12547 editor.palette.rows = (vp_height - tx_height) / bt_height;
12549 if (editor.palette.y == -1)
12551 int palette_height = editor.palette.rows * bt_height + tx_height;
12553 editor.palette.y = (vp_height - palette_height) / 2;
12558 static void LoadMenuDesignSettingsFromFilename(char *filename)
12560 static struct TitleFadingInfo tfi;
12561 static struct TitleMessageInfo tmi;
12562 static struct TokenInfo title_tokens[] =
12564 { TYPE_INTEGER, &tfi.fade_mode, ".fade_mode" },
12565 { TYPE_INTEGER, &tfi.fade_delay, ".fade_delay" },
12566 { TYPE_INTEGER, &tfi.post_delay, ".post_delay" },
12567 { TYPE_INTEGER, &tfi.auto_delay, ".auto_delay" },
12568 { TYPE_INTEGER, &tfi.auto_delay_unit, ".auto_delay_unit" },
12572 static struct TokenInfo titlemessage_tokens[] =
12574 { TYPE_INTEGER, &tmi.x, ".x" },
12575 { TYPE_INTEGER, &tmi.y, ".y" },
12576 { TYPE_INTEGER, &tmi.width, ".width" },
12577 { TYPE_INTEGER, &tmi.height, ".height" },
12578 { TYPE_INTEGER, &tmi.chars, ".chars" },
12579 { TYPE_INTEGER, &tmi.lines, ".lines" },
12580 { TYPE_INTEGER, &tmi.align, ".align" },
12581 { TYPE_INTEGER, &tmi.valign, ".valign" },
12582 { TYPE_INTEGER, &tmi.font, ".font" },
12583 { TYPE_BOOLEAN, &tmi.autowrap, ".autowrap" },
12584 { TYPE_BOOLEAN, &tmi.centered, ".centered" },
12585 { TYPE_BOOLEAN, &tmi.parse_comments, ".parse_comments" },
12586 { TYPE_INTEGER, &tmi.sort_priority, ".sort_priority" },
12587 { TYPE_INTEGER, &tmi.fade_mode, ".fade_mode" },
12588 { TYPE_INTEGER, &tmi.fade_delay, ".fade_delay" },
12589 { TYPE_INTEGER, &tmi.post_delay, ".post_delay" },
12590 { TYPE_INTEGER, &tmi.auto_delay, ".auto_delay" },
12591 { TYPE_INTEGER, &tmi.auto_delay_unit, ".auto_delay_unit" },
12597 struct TitleFadingInfo *info;
12602 // initialize first titles from "enter screen" definitions, if defined
12603 { &title_initial_first_default, "menu.enter_screen.TITLE" },
12604 { &title_first_default, "menu.enter_screen.TITLE" },
12606 // initialize title screens from "next screen" definitions, if defined
12607 { &title_initial_default, "menu.next_screen.TITLE" },
12608 { &title_default, "menu.next_screen.TITLE" },
12614 struct TitleMessageInfo *array;
12617 titlemessage_arrays[] =
12619 // initialize first titles from "enter screen" definitions, if defined
12620 { titlescreen_initial_first, "menu.enter_screen.TITLE" },
12621 { titlescreen_first, "menu.enter_screen.TITLE" },
12622 { titlemessage_initial_first, "menu.enter_screen.TITLE" },
12623 { titlemessage_first, "menu.enter_screen.TITLE" },
12625 // initialize titles from "next screen" definitions, if defined
12626 { titlescreen_initial, "menu.next_screen.TITLE" },
12627 { titlescreen, "menu.next_screen.TITLE" },
12628 { titlemessage_initial, "menu.next_screen.TITLE" },
12629 { titlemessage, "menu.next_screen.TITLE" },
12631 // overwrite titles with title definitions, if defined
12632 { titlescreen_initial_first, "[title_initial]" },
12633 { titlescreen_first, "[title]" },
12634 { titlemessage_initial_first, "[title_initial]" },
12635 { titlemessage_first, "[title]" },
12637 { titlescreen_initial, "[title_initial]" },
12638 { titlescreen, "[title]" },
12639 { titlemessage_initial, "[title_initial]" },
12640 { titlemessage, "[title]" },
12642 // overwrite titles with title screen/message definitions, if defined
12643 { titlescreen_initial_first, "[titlescreen_initial]" },
12644 { titlescreen_first, "[titlescreen]" },
12645 { titlemessage_initial_first, "[titlemessage_initial]" },
12646 { titlemessage_first, "[titlemessage]" },
12648 { titlescreen_initial, "[titlescreen_initial]" },
12649 { titlescreen, "[titlescreen]" },
12650 { titlemessage_initial, "[titlemessage_initial]" },
12651 { titlemessage, "[titlemessage]" },
12655 SetupFileHash *setup_file_hash;
12658 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
12661 // the following initializes hierarchical values from dynamic configuration
12663 // special case: initialize with default values that may be overwritten
12664 // (e.g., init "menu.draw_xoffset.INFO" from "menu.draw_xoffset")
12665 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
12667 struct TokenIntPtrInfo menu_config[] =
12669 { "menu.draw_xoffset", &menu.draw_xoffset[i] },
12670 { "menu.draw_yoffset", &menu.draw_yoffset[i] },
12671 { "menu.list_size", &menu.list_size[i] }
12674 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
12676 char *token = menu_config[j].token;
12677 char *value = getHashEntry(setup_file_hash, token);
12680 *menu_config[j].value = get_integer_from_string(value);
12684 // special case: initialize with default values that may be overwritten
12685 // (eg, init "menu.draw_xoffset.INFO[XXX]" from "menu.draw_xoffset.INFO")
12686 for (i = 0; i < NUM_SPECIAL_GFX_INFO_ARGS; i++)
12688 struct TokenIntPtrInfo menu_config[] =
12690 { "menu.draw_xoffset.INFO", &menu.draw_xoffset_info[i] },
12691 { "menu.draw_yoffset.INFO", &menu.draw_yoffset_info[i] },
12692 { "menu.list_size.INFO", &menu.list_size_info[i] }
12695 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
12697 char *token = menu_config[j].token;
12698 char *value = getHashEntry(setup_file_hash, token);
12701 *menu_config[j].value = get_integer_from_string(value);
12705 // special case: initialize with default values that may be overwritten
12706 // (eg, init "menu.draw_xoffset.SETUP[XXX]" from "menu.draw_xoffset.SETUP")
12707 for (i = 0; i < NUM_SPECIAL_GFX_SETUP_ARGS; i++)
12709 struct TokenIntPtrInfo menu_config[] =
12711 { "menu.draw_xoffset.SETUP", &menu.draw_xoffset_setup[i] },
12712 { "menu.draw_yoffset.SETUP", &menu.draw_yoffset_setup[i] }
12715 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
12717 char *token = menu_config[j].token;
12718 char *value = getHashEntry(setup_file_hash, token);
12721 *menu_config[j].value = get_integer_from_string(value);
12725 // special case: initialize with default values that may be overwritten
12726 // (eg, init "menu.line_spacing.INFO[XXX]" from "menu.line_spacing.INFO")
12727 for (i = 0; i < NUM_SPECIAL_GFX_INFO_ARGS; i++)
12729 struct TokenIntPtrInfo menu_config[] =
12731 { "menu.left_spacing.INFO", &menu.left_spacing_info[i] },
12732 { "menu.right_spacing.INFO", &menu.right_spacing_info[i] },
12733 { "menu.top_spacing.INFO", &menu.top_spacing_info[i] },
12734 { "menu.bottom_spacing.INFO", &menu.bottom_spacing_info[i] },
12735 { "menu.paragraph_spacing.INFO", &menu.paragraph_spacing_info[i] },
12736 { "menu.headline1_spacing.INFO", &menu.headline1_spacing_info[i] },
12737 { "menu.headline2_spacing.INFO", &menu.headline2_spacing_info[i] },
12738 { "menu.line_spacing.INFO", &menu.line_spacing_info[i] },
12739 { "menu.extra_spacing.INFO", &menu.extra_spacing_info[i] },
12742 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
12744 char *token = menu_config[j].token;
12745 char *value = getHashEntry(setup_file_hash, token);
12748 *menu_config[j].value = get_integer_from_string(value);
12752 // special case: initialize with default values that may be overwritten
12753 // (eg, init "menu.enter_screen.SCORES.xyz" from "menu.enter_screen.xyz")
12754 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
12756 struct TokenIntPtrInfo menu_config[] =
12758 { "menu.enter_screen.fade_mode", &menu.enter_screen[i].fade_mode },
12759 { "menu.enter_screen.fade_delay", &menu.enter_screen[i].fade_delay },
12760 { "menu.enter_screen.post_delay", &menu.enter_screen[i].post_delay },
12761 { "menu.leave_screen.fade_mode", &menu.leave_screen[i].fade_mode },
12762 { "menu.leave_screen.fade_delay", &menu.leave_screen[i].fade_delay },
12763 { "menu.leave_screen.post_delay", &menu.leave_screen[i].post_delay },
12764 { "menu.next_screen.fade_mode", &menu.next_screen[i].fade_mode },
12765 { "menu.next_screen.fade_delay", &menu.next_screen[i].fade_delay },
12766 { "menu.next_screen.post_delay", &menu.next_screen[i].post_delay }
12769 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
12771 char *token = menu_config[j].token;
12772 char *value = getHashEntry(setup_file_hash, token);
12775 *menu_config[j].value = get_token_parameter_value(token, value);
12779 // special case: initialize with default values that may be overwritten
12780 // (eg, init "viewport.door_1.MAIN.xyz" from "viewport.door_1.xyz")
12781 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
12785 char *token_prefix;
12786 struct RectWithBorder *struct_ptr;
12790 { "viewport.window", &viewport.window[i] },
12791 { "viewport.playfield", &viewport.playfield[i] },
12792 { "viewport.door_1", &viewport.door_1[i] },
12793 { "viewport.door_2", &viewport.door_2[i] }
12796 for (j = 0; j < ARRAY_SIZE(vp_struct); j++)
12798 struct TokenIntPtrInfo vp_config[] =
12800 { ".x", &vp_struct[j].struct_ptr->x },
12801 { ".y", &vp_struct[j].struct_ptr->y },
12802 { ".width", &vp_struct[j].struct_ptr->width },
12803 { ".height", &vp_struct[j].struct_ptr->height },
12804 { ".min_width", &vp_struct[j].struct_ptr->min_width },
12805 { ".min_height", &vp_struct[j].struct_ptr->min_height },
12806 { ".max_width", &vp_struct[j].struct_ptr->max_width },
12807 { ".max_height", &vp_struct[j].struct_ptr->max_height },
12808 { ".margin_left", &vp_struct[j].struct_ptr->margin_left },
12809 { ".margin_right", &vp_struct[j].struct_ptr->margin_right },
12810 { ".margin_top", &vp_struct[j].struct_ptr->margin_top },
12811 { ".margin_bottom", &vp_struct[j].struct_ptr->margin_bottom },
12812 { ".border_left", &vp_struct[j].struct_ptr->border_left },
12813 { ".border_right", &vp_struct[j].struct_ptr->border_right },
12814 { ".border_top", &vp_struct[j].struct_ptr->border_top },
12815 { ".border_bottom", &vp_struct[j].struct_ptr->border_bottom },
12816 { ".border_size", &vp_struct[j].struct_ptr->border_size },
12817 { ".align_size", &vp_struct[j].struct_ptr->align_size },
12818 { ".align", &vp_struct[j].struct_ptr->align },
12819 { ".valign", &vp_struct[j].struct_ptr->valign }
12822 for (k = 0; k < ARRAY_SIZE(vp_config); k++)
12824 char *token = getStringCat2(vp_struct[j].token_prefix,
12825 vp_config[k].token);
12826 char *value = getHashEntry(setup_file_hash, token);
12829 *vp_config[k].value = get_token_parameter_value(token, value);
12836 // special case: initialize with default values that may be overwritten
12837 // (e.g., init "[title].fade_mode" from "menu.next_screen.TITLE.fade_mode")
12838 for (i = 0; title_info[i].info != NULL; i++)
12840 struct TitleFadingInfo *info = title_info[i].info;
12841 char *base_token = title_info[i].text;
12843 for (j = 0; title_tokens[j].type != -1; j++)
12845 char *token = getStringCat2(base_token, title_tokens[j].text);
12846 char *value = getHashEntry(setup_file_hash, token);
12850 int parameter_value = get_token_parameter_value(token, value);
12854 *(int *)title_tokens[j].value = (int)parameter_value;
12863 // special case: initialize with default values that may be overwritten
12864 // (e.g., init "titlemessage_1.fade_mode" from "[titlemessage].fade_mode")
12865 for (i = 0; titlemessage_arrays[i].array != NULL; i++)
12867 struct TitleMessageInfo *array = titlemessage_arrays[i].array;
12868 char *base_token = titlemessage_arrays[i].text;
12870 for (j = 0; titlemessage_tokens[j].type != -1; j++)
12872 char *token = getStringCat2(base_token, titlemessage_tokens[j].text);
12873 char *value = getHashEntry(setup_file_hash, token);
12877 int parameter_value = get_token_parameter_value(token, value);
12879 for (k = 0; k < MAX_NUM_TITLE_MESSAGES; k++)
12883 if (titlemessage_tokens[j].type == TYPE_INTEGER)
12884 *(int *)titlemessage_tokens[j].value = (int)parameter_value;
12886 *(boolean *)titlemessage_tokens[j].value = (boolean)parameter_value;
12896 // special case: check if network and preview player positions are redefined,
12897 // to compare this later against the main menu level preview being redefined
12898 struct TokenIntPtrInfo menu_config_players[] =
12900 { "main.network_players.x", &menu.main.network_players.redefined },
12901 { "main.network_players.y", &menu.main.network_players.redefined },
12902 { "main.preview_players.x", &menu.main.preview_players.redefined },
12903 { "main.preview_players.y", &menu.main.preview_players.redefined },
12904 { "preview.x", &preview.redefined },
12905 { "preview.y", &preview.redefined }
12908 for (i = 0; i < ARRAY_SIZE(menu_config_players); i++)
12909 *menu_config_players[i].value = FALSE;
12911 for (i = 0; i < ARRAY_SIZE(menu_config_players); i++)
12912 if (getHashEntry(setup_file_hash, menu_config_players[i].token) != NULL)
12913 *menu_config_players[i].value = TRUE;
12915 // read (and overwrite with) values that may be specified in config file
12916 for (i = 0; image_config_vars[i].token != NULL; i++)
12918 char *value = getHashEntry(setup_file_hash, image_config_vars[i].token);
12920 // (ignore definitions set to "[DEFAULT]" which are already initialized)
12921 if (value != NULL && !strEqual(value, ARG_DEFAULT))
12922 *image_config_vars[i].value =
12923 get_token_parameter_value(image_config_vars[i].token, value);
12926 freeSetupFileHash(setup_file_hash);
12929 void LoadMenuDesignSettings(void)
12931 char *filename_base = UNDEFINED_FILENAME, *filename_local;
12933 InitMenuDesignSettings_Static();
12934 InitMenuDesignSettings_SpecialPreProcessing();
12936 if (!GFX_OVERRIDE_ARTWORK(ARTWORK_TYPE_GRAPHICS))
12938 // first look for special settings configured in level series config
12939 filename_base = getCustomArtworkLevelConfigFilename(ARTWORK_TYPE_GRAPHICS);
12941 if (fileExists(filename_base))
12942 LoadMenuDesignSettingsFromFilename(filename_base);
12945 filename_local = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
12947 if (filename_local != NULL && !strEqual(filename_base, filename_local))
12948 LoadMenuDesignSettingsFromFilename(filename_local);
12950 InitMenuDesignSettings_SpecialPostProcessing();
12953 void LoadMenuDesignSettings_AfterGraphics(void)
12955 InitMenuDesignSettings_SpecialPostProcessing_AfterGraphics();
12958 void LoadUserDefinedEditorElementList(int **elements, int *num_elements)
12960 char *filename = getEditorSetupFilename();
12961 SetupFileList *setup_file_list, *list;
12962 SetupFileHash *element_hash;
12963 int num_unknown_tokens = 0;
12966 if ((setup_file_list = loadSetupFileList(filename)) == NULL)
12969 element_hash = newSetupFileHash();
12971 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
12972 setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
12974 // determined size may be larger than needed (due to unknown elements)
12976 for (list = setup_file_list; list != NULL; list = list->next)
12979 // add space for up to 3 more elements for padding that may be needed
12980 *num_elements += 3;
12982 // free memory for old list of elements, if needed
12983 checked_free(*elements);
12985 // allocate memory for new list of elements
12986 *elements = checked_malloc(*num_elements * sizeof(int));
12989 for (list = setup_file_list; list != NULL; list = list->next)
12991 char *value = getHashEntry(element_hash, list->token);
12993 if (value == NULL) // try to find obsolete token mapping
12995 char *mapped_token = get_mapped_token(list->token);
12997 if (mapped_token != NULL)
12999 value = getHashEntry(element_hash, mapped_token);
13001 free(mapped_token);
13007 (*elements)[(*num_elements)++] = atoi(value);
13011 if (num_unknown_tokens == 0)
13014 Warn("unknown token(s) found in config file:");
13015 Warn("- config file: '%s'", filename);
13017 num_unknown_tokens++;
13020 Warn("- token: '%s'", list->token);
13024 if (num_unknown_tokens > 0)
13027 while (*num_elements % 4) // pad with empty elements, if needed
13028 (*elements)[(*num_elements)++] = EL_EMPTY;
13030 freeSetupFileList(setup_file_list);
13031 freeSetupFileHash(element_hash);
13034 for (i = 0; i < *num_elements; i++)
13035 Debug("editor", "element '%s' [%d]\n",
13036 element_info[(*elements)[i]].token_name, (*elements)[i]);
13040 static struct MusicFileInfo *get_music_file_info_ext(char *basename, int music,
13043 SetupFileHash *setup_file_hash = NULL;
13044 struct MusicFileInfo tmp_music_file_info, *new_music_file_info;
13045 char *filename_music, *filename_prefix, *filename_info;
13051 token_to_value_ptr[] =
13053 { "title_header", &tmp_music_file_info.title_header },
13054 { "artist_header", &tmp_music_file_info.artist_header },
13055 { "album_header", &tmp_music_file_info.album_header },
13056 { "year_header", &tmp_music_file_info.year_header },
13058 { "title", &tmp_music_file_info.title },
13059 { "artist", &tmp_music_file_info.artist },
13060 { "album", &tmp_music_file_info.album },
13061 { "year", &tmp_music_file_info.year },
13067 filename_music = (is_sound ? getCustomSoundFilename(basename) :
13068 getCustomMusicFilename(basename));
13070 if (filename_music == NULL)
13073 // ---------- try to replace file extension ----------
13075 filename_prefix = getStringCopy(filename_music);
13076 if (strrchr(filename_prefix, '.') != NULL)
13077 *strrchr(filename_prefix, '.') = '\0';
13078 filename_info = getStringCat2(filename_prefix, ".txt");
13080 if (fileExists(filename_info))
13081 setup_file_hash = loadSetupFileHash(filename_info);
13083 free(filename_prefix);
13084 free(filename_info);
13086 if (setup_file_hash == NULL)
13088 // ---------- try to add file extension ----------
13090 filename_prefix = getStringCopy(filename_music);
13091 filename_info = getStringCat2(filename_prefix, ".txt");
13093 if (fileExists(filename_info))
13094 setup_file_hash = loadSetupFileHash(filename_info);
13096 free(filename_prefix);
13097 free(filename_info);
13100 if (setup_file_hash == NULL)
13103 // ---------- music file info found ----------
13105 clear_mem(&tmp_music_file_info, sizeof(struct MusicFileInfo));
13107 for (i = 0; token_to_value_ptr[i].token != NULL; i++)
13109 char *value = getHashEntry(setup_file_hash, token_to_value_ptr[i].token);
13111 *token_to_value_ptr[i].value_ptr =
13112 getStringCopy(value != NULL && *value != '\0' ? value : UNKNOWN_NAME);
13115 tmp_music_file_info.basename = getStringCopy(basename);
13116 tmp_music_file_info.music = music;
13117 tmp_music_file_info.is_sound = is_sound;
13119 new_music_file_info = checked_malloc(sizeof(struct MusicFileInfo));
13120 *new_music_file_info = tmp_music_file_info;
13122 return new_music_file_info;
13125 static struct MusicFileInfo *get_music_file_info(char *basename, int music)
13127 return get_music_file_info_ext(basename, music, FALSE);
13130 static struct MusicFileInfo *get_sound_file_info(char *basename, int sound)
13132 return get_music_file_info_ext(basename, sound, TRUE);
13135 static boolean music_info_listed_ext(struct MusicFileInfo *list,
13136 char *basename, boolean is_sound)
13138 for (; list != NULL; list = list->next)
13139 if (list->is_sound == is_sound && strEqual(list->basename, basename))
13145 static boolean music_info_listed(struct MusicFileInfo *list, char *basename)
13147 return music_info_listed_ext(list, basename, FALSE);
13150 static boolean sound_info_listed(struct MusicFileInfo *list, char *basename)
13152 return music_info_listed_ext(list, basename, TRUE);
13155 void LoadMusicInfo(void)
13157 char *music_directory = getCustomMusicDirectory();
13158 int num_music = getMusicListSize();
13159 int num_music_noconf = 0;
13160 int num_sounds = getSoundListSize();
13162 DirectoryEntry *dir_entry;
13163 struct FileInfo *music, *sound;
13164 struct MusicFileInfo *next, **new;
13167 while (music_file_info != NULL)
13169 next = music_file_info->next;
13171 checked_free(music_file_info->basename);
13173 checked_free(music_file_info->title_header);
13174 checked_free(music_file_info->artist_header);
13175 checked_free(music_file_info->album_header);
13176 checked_free(music_file_info->year_header);
13178 checked_free(music_file_info->title);
13179 checked_free(music_file_info->artist);
13180 checked_free(music_file_info->album);
13181 checked_free(music_file_info->year);
13183 free(music_file_info);
13185 music_file_info = next;
13188 new = &music_file_info;
13190 for (i = 0; i < num_music; i++)
13192 music = getMusicListEntry(i);
13194 if (music->filename == NULL)
13197 if (strEqual(music->filename, UNDEFINED_FILENAME))
13200 // a configured file may be not recognized as music
13201 if (!FileIsMusic(music->filename))
13204 if (!music_info_listed(music_file_info, music->filename))
13206 *new = get_music_file_info(music->filename, i);
13209 new = &(*new)->next;
13213 if ((dir = openDirectory(music_directory)) == NULL)
13215 Warn("cannot read music directory '%s'", music_directory);
13220 while ((dir_entry = readDirectory(dir)) != NULL) // loop all entries
13222 char *basename = dir_entry->basename;
13223 boolean music_already_used = FALSE;
13226 // skip all music files that are configured in music config file
13227 for (i = 0; i < num_music; i++)
13229 music = getMusicListEntry(i);
13231 if (music->filename == NULL)
13234 if (strEqual(basename, music->filename))
13236 music_already_used = TRUE;
13241 if (music_already_used)
13244 if (!FileIsMusic(dir_entry->filename))
13247 if (!music_info_listed(music_file_info, basename))
13249 *new = get_music_file_info(basename, MAP_NOCONF_MUSIC(num_music_noconf));
13252 new = &(*new)->next;
13255 num_music_noconf++;
13258 closeDirectory(dir);
13260 for (i = 0; i < num_sounds; i++)
13262 sound = getSoundListEntry(i);
13264 if (sound->filename == NULL)
13267 if (strEqual(sound->filename, UNDEFINED_FILENAME))
13270 // a configured file may be not recognized as sound
13271 if (!FileIsSound(sound->filename))
13274 if (!sound_info_listed(music_file_info, sound->filename))
13276 *new = get_sound_file_info(sound->filename, i);
13278 new = &(*new)->next;
13283 static void add_helpanim_entry(int element, int action, int direction,
13284 int delay, int *num_list_entries)
13286 struct HelpAnimInfo *new_list_entry;
13287 (*num_list_entries)++;
13290 checked_realloc(helpanim_info,
13291 *num_list_entries * sizeof(struct HelpAnimInfo));
13292 new_list_entry = &helpanim_info[*num_list_entries - 1];
13294 new_list_entry->element = element;
13295 new_list_entry->action = action;
13296 new_list_entry->direction = direction;
13297 new_list_entry->delay = delay;
13300 static void print_unknown_token(char *filename, char *token, int token_nr)
13305 Warn("unknown token(s) found in config file:");
13306 Warn("- config file: '%s'", filename);
13309 Warn("- token: '%s'", token);
13312 static void print_unknown_token_end(int token_nr)
13318 void LoadHelpAnimInfo(void)
13320 char *filename = getHelpAnimFilename();
13321 SetupFileList *setup_file_list = NULL, *list;
13322 SetupFileHash *element_hash, *action_hash, *direction_hash;
13323 int num_list_entries = 0;
13324 int num_unknown_tokens = 0;
13327 if (fileExists(filename))
13328 setup_file_list = loadSetupFileList(filename);
13330 if (setup_file_list == NULL)
13332 // use reliable default values from static configuration
13333 SetupFileList *insert_ptr;
13335 insert_ptr = setup_file_list =
13336 newSetupFileList(helpanim_config[0].token,
13337 helpanim_config[0].value);
13339 for (i = 1; helpanim_config[i].token; i++)
13340 insert_ptr = addListEntry(insert_ptr,
13341 helpanim_config[i].token,
13342 helpanim_config[i].value);
13345 element_hash = newSetupFileHash();
13346 action_hash = newSetupFileHash();
13347 direction_hash = newSetupFileHash();
13349 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
13350 setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
13352 for (i = 0; i < NUM_ACTIONS; i++)
13353 setHashEntry(action_hash, element_action_info[i].suffix,
13354 i_to_a(element_action_info[i].value));
13356 // do not store direction index (bit) here, but direction value!
13357 for (i = 0; i < NUM_DIRECTIONS_FULL; i++)
13358 setHashEntry(direction_hash, element_direction_info[i].suffix,
13359 i_to_a(1 << element_direction_info[i].value));
13361 for (list = setup_file_list; list != NULL; list = list->next)
13363 char *element_token, *action_token, *direction_token;
13364 char *element_value, *action_value, *direction_value;
13365 int delay = atoi(list->value);
13367 if (strEqual(list->token, "end"))
13369 add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
13374 /* first try to break element into element/action/direction parts;
13375 if this does not work, also accept combined "element[.act][.dir]"
13376 elements (like "dynamite.active"), which are unique elements */
13378 if (strchr(list->token, '.') == NULL) // token contains no '.'
13380 element_value = getHashEntry(element_hash, list->token);
13381 if (element_value != NULL) // element found
13382 add_helpanim_entry(atoi(element_value), -1, -1, delay,
13383 &num_list_entries);
13386 // no further suffixes found -- this is not an element
13387 print_unknown_token(filename, list->token, num_unknown_tokens++);
13393 // token has format "<prefix>.<something>"
13395 action_token = strchr(list->token, '.'); // suffix may be action ...
13396 direction_token = action_token; // ... or direction
13398 element_token = getStringCopy(list->token);
13399 *strchr(element_token, '.') = '\0';
13401 element_value = getHashEntry(element_hash, element_token);
13403 if (element_value == NULL) // this is no element
13405 element_value = getHashEntry(element_hash, list->token);
13406 if (element_value != NULL) // combined element found
13407 add_helpanim_entry(atoi(element_value), -1, -1, delay,
13408 &num_list_entries);
13410 print_unknown_token(filename, list->token, num_unknown_tokens++);
13412 free(element_token);
13417 action_value = getHashEntry(action_hash, action_token);
13419 if (action_value != NULL) // action found
13421 add_helpanim_entry(atoi(element_value), atoi(action_value), -1, delay,
13422 &num_list_entries);
13424 free(element_token);
13429 direction_value = getHashEntry(direction_hash, direction_token);
13431 if (direction_value != NULL) // direction found
13433 add_helpanim_entry(atoi(element_value), -1, atoi(direction_value), delay,
13434 &num_list_entries);
13436 free(element_token);
13441 if (strchr(action_token + 1, '.') == NULL)
13443 // no further suffixes found -- this is not an action nor direction
13445 element_value = getHashEntry(element_hash, list->token);
13446 if (element_value != NULL) // combined element found
13447 add_helpanim_entry(atoi(element_value), -1, -1, delay,
13448 &num_list_entries);
13450 print_unknown_token(filename, list->token, num_unknown_tokens++);
13452 free(element_token);
13457 // token has format "<prefix>.<suffix>.<something>"
13459 direction_token = strchr(action_token + 1, '.');
13461 action_token = getStringCopy(action_token);
13462 *strchr(action_token + 1, '.') = '\0';
13464 action_value = getHashEntry(action_hash, action_token);
13466 if (action_value == NULL) // this is no action
13468 element_value = getHashEntry(element_hash, list->token);
13469 if (element_value != NULL) // combined element found
13470 add_helpanim_entry(atoi(element_value), -1, -1, delay,
13471 &num_list_entries);
13473 print_unknown_token(filename, list->token, num_unknown_tokens++);
13475 free(element_token);
13476 free(action_token);
13481 direction_value = getHashEntry(direction_hash, direction_token);
13483 if (direction_value != NULL) // direction found
13485 add_helpanim_entry(atoi(element_value), atoi(action_value),
13486 atoi(direction_value), delay, &num_list_entries);
13488 free(element_token);
13489 free(action_token);
13494 // this is no direction
13496 element_value = getHashEntry(element_hash, list->token);
13497 if (element_value != NULL) // combined element found
13498 add_helpanim_entry(atoi(element_value), -1, -1, delay,
13499 &num_list_entries);
13501 print_unknown_token(filename, list->token, num_unknown_tokens++);
13503 free(element_token);
13504 free(action_token);
13507 print_unknown_token_end(num_unknown_tokens);
13509 add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
13510 add_helpanim_entry(HELPANIM_LIST_END, -1, -1, -1, &num_list_entries);
13512 freeSetupFileList(setup_file_list);
13513 freeSetupFileHash(element_hash);
13514 freeSetupFileHash(action_hash);
13515 freeSetupFileHash(direction_hash);
13518 for (i = 0; i < num_list_entries; i++)
13519 Debug("files:LoadHelpAnimInfo", "'%s': %d, %d, %d => %d",
13520 EL_NAME(helpanim_info[i].element),
13521 helpanim_info[i].element,
13522 helpanim_info[i].action,
13523 helpanim_info[i].direction,
13524 helpanim_info[i].delay);
13528 void LoadHelpTextInfo(void)
13530 char *filename = getHelpTextFilename();
13533 if (helptext_info != NULL)
13535 freeSetupFileHash(helptext_info);
13536 helptext_info = NULL;
13539 if (fileExists(filename))
13540 helptext_info = loadSetupFileHash(filename);
13542 if (helptext_info == NULL)
13544 // use reliable default values from static configuration
13545 helptext_info = newSetupFileHash();
13547 for (i = 0; helptext_config[i].token; i++)
13548 setHashEntry(helptext_info,
13549 helptext_config[i].token,
13550 helptext_config[i].value);
13554 BEGIN_HASH_ITERATION(helptext_info, itr)
13556 Debug("files:LoadHelpTextInfo", "'%s' => '%s'",
13557 HASH_ITERATION_TOKEN(itr), HASH_ITERATION_VALUE(itr));
13559 END_HASH_ITERATION(hash, itr)
13564 // ----------------------------------------------------------------------------
13566 // ----------------------------------------------------------------------------
13568 #define MAX_NUM_CONVERT_LEVELS 1000
13570 void ConvertLevels(void)
13572 static LevelDirTree *convert_leveldir = NULL;
13573 static int convert_level_nr = -1;
13574 static int num_levels_handled = 0;
13575 static int num_levels_converted = 0;
13576 static boolean levels_failed[MAX_NUM_CONVERT_LEVELS];
13579 convert_leveldir = getTreeInfoFromIdentifier(leveldir_first,
13580 global.convert_leveldir);
13582 if (convert_leveldir == NULL)
13583 Fail("no such level identifier: '%s'", global.convert_leveldir);
13585 leveldir_current = convert_leveldir;
13587 if (global.convert_level_nr != -1)
13589 convert_leveldir->first_level = global.convert_level_nr;
13590 convert_leveldir->last_level = global.convert_level_nr;
13593 convert_level_nr = convert_leveldir->first_level;
13595 PrintLine("=", 79);
13596 Print("Converting levels\n");
13597 PrintLine("-", 79);
13598 Print("Level series identifier: '%s'\n", convert_leveldir->identifier);
13599 Print("Level series name: '%s'\n", convert_leveldir->name);
13600 Print("Level series author: '%s'\n", convert_leveldir->author);
13601 Print("Number of levels: %d\n", convert_leveldir->levels);
13602 PrintLine("=", 79);
13605 for (i = 0; i < MAX_NUM_CONVERT_LEVELS; i++)
13606 levels_failed[i] = FALSE;
13608 while (convert_level_nr <= convert_leveldir->last_level)
13610 char *level_filename;
13613 level_nr = convert_level_nr++;
13615 Print("Level %03d: ", level_nr);
13617 LoadLevel(level_nr);
13618 if (level.no_level_file || level.no_valid_file)
13620 Print("(no level)\n");
13624 Print("converting level ... ");
13627 // special case: conversion of some EMC levels as requested by ACME
13628 level.game_engine_type = GAME_ENGINE_TYPE_RND;
13631 level_filename = getDefaultLevelFilename(level_nr);
13632 new_level = !fileExists(level_filename);
13636 SaveLevel(level_nr);
13638 num_levels_converted++;
13640 Print("converted.\n");
13644 if (level_nr >= 0 && level_nr < MAX_NUM_CONVERT_LEVELS)
13645 levels_failed[level_nr] = TRUE;
13647 Print("NOT CONVERTED -- LEVEL ALREADY EXISTS.\n");
13650 num_levels_handled++;
13654 PrintLine("=", 79);
13655 Print("Number of levels handled: %d\n", num_levels_handled);
13656 Print("Number of levels converted: %d (%d%%)\n", num_levels_converted,
13657 (num_levels_handled ?
13658 num_levels_converted * 100 / num_levels_handled : 0));
13659 PrintLine("-", 79);
13660 Print("Summary (for automatic parsing by scripts):\n");
13661 Print("LEVELDIR '%s', CONVERTED %d/%d (%d%%)",
13662 convert_leveldir->identifier, num_levels_converted,
13663 num_levels_handled,
13664 (num_levels_handled ?
13665 num_levels_converted * 100 / num_levels_handled : 0));
13667 if (num_levels_handled != num_levels_converted)
13669 Print(", FAILED:");
13670 for (i = 0; i < MAX_NUM_CONVERT_LEVELS; i++)
13671 if (levels_failed[i])
13676 PrintLine("=", 79);
13678 CloseAllAndExit(0);
13682 // ----------------------------------------------------------------------------
13683 // create and save images for use in level sketches (raw BMP format)
13684 // ----------------------------------------------------------------------------
13686 void CreateLevelSketchImages(void)
13692 InitElementPropertiesGfxElement();
13694 bitmap1 = CreateBitmap(TILEX, TILEY, DEFAULT_DEPTH);
13695 bitmap2 = CreateBitmap(MINI_TILEX, MINI_TILEY, DEFAULT_DEPTH);
13697 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
13699 int element = getMappedElement(i);
13700 char basename1[16];
13701 char basename2[16];
13705 sprintf(basename1, "%04d.bmp", i);
13706 sprintf(basename2, "%04ds.bmp", i);
13708 filename1 = getPath2(global.create_sketch_images_dir, basename1);
13709 filename2 = getPath2(global.create_sketch_images_dir, basename2);
13711 DrawSizedElement(0, 0, element, TILESIZE);
13712 BlitBitmap(drawto, bitmap1, SX, SY, TILEX, TILEY, 0, 0);
13714 if (SDL_SaveBMP(bitmap1->surface, filename1) != 0)
13715 Fail("cannot save level sketch image file '%s'", filename1);
13717 DrawSizedElement(0, 0, element, MINI_TILESIZE);
13718 BlitBitmap(drawto, bitmap2, SX, SY, MINI_TILEX, MINI_TILEY, 0, 0);
13720 if (SDL_SaveBMP(bitmap2->surface, filename2) != 0)
13721 Fail("cannot save level sketch image file '%s'", filename2);
13726 // create corresponding SQL statements (for normal and small images)
13729 printf("insert into phpbb_words values (NULL, '`%03d', '<IMG class=\"levelsketch\" src=\"/I/%04d.png\"/>');\n", i, i);
13730 printf("insert into phpbb_words values (NULL, '¸%03d', '<IMG class=\"levelsketch\" src=\"/I/%04ds.png\"/>');\n", i, i);
13733 printf("insert into phpbb_words values (NULL, '`%04d', '<IMG class=\"levelsketch\" src=\"/I/%04d.png\"/>');\n", i, i);
13734 printf("insert into phpbb_words values (NULL, '¸%04d', '<IMG class=\"levelsketch\" src=\"/I/%04ds.png\"/>');\n", i, i);
13736 // optional: create content for forum level sketch demonstration post
13738 fprintf(stderr, "%03d `%03d%c", i, i, (i % 10 < 9 ? ' ' : '\n'));
13741 FreeBitmap(bitmap1);
13742 FreeBitmap(bitmap2);
13745 fprintf(stderr, "\n");
13747 Info("%d normal and small images created", NUM_FILE_ELEMENTS);
13749 CloseAllAndExit(0);
13753 // ----------------------------------------------------------------------------
13754 // create and save images for element collecting animations (raw BMP format)
13755 // ----------------------------------------------------------------------------
13757 static boolean createCollectImage(int element)
13759 return (IS_COLLECTIBLE(element) && !IS_SP_ELEMENT(element));
13762 void CreateCollectElementImages(void)
13766 int anim_frames = num_steps - 1;
13767 int tile_size = TILESIZE;
13768 int anim_width = tile_size * anim_frames;
13769 int anim_height = tile_size;
13770 int num_collect_images = 0;
13771 int pos_collect_images = 0;
13773 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
13774 if (createCollectImage(i))
13775 num_collect_images++;
13777 Info("Creating %d element collecting animation images ...",
13778 num_collect_images);
13780 int dst_width = anim_width * 2;
13781 int dst_height = anim_height * num_collect_images / 2;
13782 Bitmap *dst_bitmap = CreateBitmap(dst_width, dst_height, DEFAULT_DEPTH);
13783 char *basename = "RocksCollect.bmp";
13784 char *filename = getPath2(global.create_collect_images_dir, basename);
13786 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
13788 if (!createCollectImage(i))
13791 int dst_x = (pos_collect_images / (num_collect_images / 2)) * anim_width;
13792 int dst_y = (pos_collect_images % (num_collect_images / 2)) * anim_height;
13793 int graphic = el2img(i);
13794 char *token_name = element_info[i].token_name;
13795 Bitmap *tmp_bitmap = CreateBitmap(tile_size, tile_size, DEFAULT_DEPTH);
13796 Bitmap *src_bitmap;
13799 Info("- creating collecting image for '%s' ...", token_name);
13801 getGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
13803 BlitBitmap(src_bitmap, tmp_bitmap, src_x, src_y,
13804 tile_size, tile_size, 0, 0);
13806 tmp_bitmap->surface_masked = tmp_bitmap->surface;
13808 for (j = 0; j < anim_frames; j++)
13810 int frame_size_final = tile_size * (anim_frames - j) / num_steps;
13811 int frame_size = frame_size_final * num_steps;
13812 int offset = (tile_size - frame_size_final) / 2;
13813 Bitmap *frame_bitmap = ZoomBitmap(tmp_bitmap, frame_size, frame_size);
13815 while (frame_size > frame_size_final)
13819 Bitmap *half_bitmap = ZoomBitmap(frame_bitmap, frame_size, frame_size);
13821 FreeBitmap(frame_bitmap);
13823 frame_bitmap = half_bitmap;
13826 BlitBitmap(frame_bitmap, dst_bitmap, 0, 0,
13827 frame_size_final, frame_size_final,
13828 dst_x + j * tile_size + offset, dst_y + offset);
13830 FreeBitmap(frame_bitmap);
13833 tmp_bitmap->surface_masked = NULL;
13835 FreeBitmap(tmp_bitmap);
13837 pos_collect_images++;
13840 if (SDL_SaveBMP(dst_bitmap->surface, filename) != 0)
13841 Fail("cannot save element collecting image file '%s'", filename);
13843 FreeBitmap(dst_bitmap);
13847 CloseAllAndExit(0);
13851 // ----------------------------------------------------------------------------
13852 // create and save images for custom and group elements (raw BMP format)
13853 // ----------------------------------------------------------------------------
13855 void CreateCustomElementImages(char *directory)
13857 char *src_basename = "RocksCE-template.ilbm";
13858 char *dst_basename = "RocksCE.bmp";
13859 char *src_filename = getPath2(directory, src_basename);
13860 char *dst_filename = getPath2(directory, dst_basename);
13861 Bitmap *src_bitmap;
13863 int yoffset_ce = 0;
13864 int yoffset_ge = (TILEY * NUM_CUSTOM_ELEMENTS / 16);
13867 InitVideoDefaults();
13869 ReCreateBitmap(&backbuffer, video.width, video.height);
13871 src_bitmap = LoadImage(src_filename);
13873 bitmap = CreateBitmap(TILEX * 16 * 2,
13874 TILEY * (NUM_CUSTOM_ELEMENTS + NUM_GROUP_ELEMENTS) / 16,
13877 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
13884 BlitBitmap(src_bitmap, bitmap, 0, 0, TILEX, TILEY,
13885 TILEX * x, TILEY * y + yoffset_ce);
13887 BlitBitmap(src_bitmap, bitmap, 0, TILEY,
13889 TILEX * x + TILEX * 16,
13890 TILEY * y + yoffset_ce);
13892 for (j = 2; j >= 0; j--)
13896 BlitBitmap(src_bitmap, bitmap,
13897 TILEX + c * 7, 0, 6, 10,
13898 TILEX * x + 6 + j * 7,
13899 TILEY * y + 11 + yoffset_ce);
13901 BlitBitmap(src_bitmap, bitmap,
13902 TILEX + c * 8, TILEY, 6, 10,
13903 TILEX * 16 + TILEX * x + 6 + j * 8,
13904 TILEY * y + 10 + yoffset_ce);
13910 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
13917 BlitBitmap(src_bitmap, bitmap, 0, 0, TILEX, TILEY,
13918 TILEX * x, TILEY * y + yoffset_ge);
13920 BlitBitmap(src_bitmap, bitmap, 0, TILEY,
13922 TILEX * x + TILEX * 16,
13923 TILEY * y + yoffset_ge);
13925 for (j = 1; j >= 0; j--)
13929 BlitBitmap(src_bitmap, bitmap, TILEX + c * 10, 11, 10, 10,
13930 TILEX * x + 6 + j * 10,
13931 TILEY * y + 11 + yoffset_ge);
13933 BlitBitmap(src_bitmap, bitmap,
13934 TILEX + c * 8, TILEY + 12, 6, 10,
13935 TILEX * 16 + TILEX * x + 10 + j * 8,
13936 TILEY * y + 10 + yoffset_ge);
13942 if (SDL_SaveBMP(bitmap->surface, dst_filename) != 0)
13943 Fail("cannot save CE graphics file '%s'", dst_filename);
13945 FreeBitmap(bitmap);
13947 CloseAllAndExit(0);