1 // ============================================================================
2 // Rocks'n'Diamonds - McDuffin Strikes Back!
3 // ----------------------------------------------------------------------------
4 // (c) 1995-2014 by Artsoft Entertainment
7 // https://www.artsoft.org/
8 // ----------------------------------------------------------------------------
10 // ============================================================================
17 #include "libgame/libgame.h"
27 #define ENABLE_UNUSED_CODE 0 // currently unused functions
28 #define ENABLE_HISTORIC_CHUNKS 0 // only for historic reference
29 #define ENABLE_RESERVED_CODE 0 // reserved for later use
31 #define CHUNK_ID_LEN 4 // IFF style chunk id length
32 #define CHUNK_SIZE_UNDEFINED 0 // undefined chunk size == 0
33 #define CHUNK_SIZE_NONE -1 // do not write chunk size
35 #define LEVEL_CHUNK_NAME_SIZE MAX_LEVEL_NAME_LEN
36 #define LEVEL_CHUNK_AUTH_SIZE MAX_LEVEL_AUTHOR_LEN
38 #define LEVEL_CHUNK_VERS_SIZE 8 // size of file version chunk
39 #define LEVEL_CHUNK_DATE_SIZE 4 // size of file date chunk
40 #define LEVEL_CHUNK_HEAD_SIZE 80 // size of level file header
41 #define LEVEL_CHUNK_HEAD_UNUSED 0 // unused level header bytes
42 #define LEVEL_CHUNK_CNT2_SIZE 160 // size of level CNT2 chunk
43 #define LEVEL_CHUNK_CNT2_UNUSED 11 // unused CNT2 chunk bytes
44 #define LEVEL_CHUNK_CNT3_HEADER 16 // size of level CNT3 header
45 #define LEVEL_CHUNK_CNT3_UNUSED 10 // unused CNT3 chunk bytes
46 #define LEVEL_CPART_CUS3_SIZE 134 // size of CUS3 chunk part
47 #define LEVEL_CPART_CUS3_UNUSED 15 // unused CUS3 bytes / part
48 #define LEVEL_CHUNK_GRP1_SIZE 74 // size of level GRP1 chunk
50 // (element number, number of change pages, change page number)
51 #define LEVEL_CHUNK_CUSX_UNCHANGED (2 + (1 + 1) + (1 + 1))
53 // (element number only)
54 #define LEVEL_CHUNK_GRPX_UNCHANGED 2
55 #define LEVEL_CHUNK_EMPX_UNCHANGED 2
56 #define LEVEL_CHUNK_NOTE_UNCHANGED 2
58 // (nothing at all if unchanged)
59 #define LEVEL_CHUNK_ELEM_UNCHANGED 0
61 #define TAPE_CHUNK_VERS_SIZE 8 // size of file version chunk
62 #define TAPE_CHUNK_HEAD_SIZE 20 // size of tape file header
63 #define TAPE_CHUNK_SCRN_SIZE 2 // size of screen size chunk
65 #define SCORE_CHUNK_VERS_SIZE 8 // size of file version chunk
67 #define LEVEL_CHUNK_CNT3_SIZE(x) (LEVEL_CHUNK_CNT3_HEADER + (x))
68 #define LEVEL_CHUNK_CUS3_SIZE(x) (2 + (x) * LEVEL_CPART_CUS3_SIZE)
69 #define LEVEL_CHUNK_CUS4_SIZE(x) (96 + (x) * 48)
71 // file identifier strings
72 #define LEVEL_COOKIE_TMPL "ROCKSNDIAMONDS_LEVEL_FILE_VERSION_x.x"
73 #define TAPE_COOKIE_TMPL "ROCKSNDIAMONDS_TAPE_FILE_VERSION_x.x"
74 #define SCORE_COOKIE_TMPL "ROCKSNDIAMONDS_SCORE_FILE_VERSION_x.x"
76 // values for deciding when (not) to save configuration data
77 #define SAVE_CONF_NEVER 0
78 #define SAVE_CONF_ALWAYS 1
79 #define SAVE_CONF_WHEN_CHANGED -1
81 // values for chunks using micro chunks
82 #define CONF_MASK_1_BYTE 0x00
83 #define CONF_MASK_2_BYTE 0x40
84 #define CONF_MASK_4_BYTE 0x80
85 #define CONF_MASK_MULTI_BYTES 0xc0
87 #define CONF_MASK_BYTES 0xc0
88 #define CONF_MASK_TOKEN 0x3f
90 #define CONF_VALUE_1_BYTE(x) (CONF_MASK_1_BYTE | (x))
91 #define CONF_VALUE_2_BYTE(x) (CONF_MASK_2_BYTE | (x))
92 #define CONF_VALUE_4_BYTE(x) (CONF_MASK_4_BYTE | (x))
93 #define CONF_VALUE_MULTI_BYTES(x) (CONF_MASK_MULTI_BYTES | (x))
95 // these definitions are just for convenience of use and readability
96 #define CONF_VALUE_8_BIT(x) CONF_VALUE_1_BYTE(x)
97 #define CONF_VALUE_16_BIT(x) CONF_VALUE_2_BYTE(x)
98 #define CONF_VALUE_32_BIT(x) CONF_VALUE_4_BYTE(x)
99 #define CONF_VALUE_BYTES(x) CONF_VALUE_MULTI_BYTES(x)
101 #define CONF_VALUE_NUM_BYTES(x) ((x) == CONF_MASK_1_BYTE ? 1 : \
102 (x) == CONF_MASK_2_BYTE ? 2 : \
103 (x) == CONF_MASK_4_BYTE ? 4 : 0)
105 #define CONF_CONTENT_NUM_ELEMENTS (3 * 3)
106 #define CONF_CONTENT_NUM_BYTES (CONF_CONTENT_NUM_ELEMENTS * 2)
107 #define CONF_ELEMENT_NUM_BYTES (2)
109 #define CONF_ENTITY_NUM_BYTES(t) ((t) == TYPE_ELEMENT || \
110 (t) == TYPE_ELEMENT_LIST ? \
111 CONF_ELEMENT_NUM_BYTES : \
112 (t) == TYPE_CONTENT || \
113 (t) == TYPE_CONTENT_LIST ? \
114 CONF_CONTENT_NUM_BYTES : 1)
116 #define CONF_ELEMENT_BYTE_POS(i) ((i) * CONF_ELEMENT_NUM_BYTES)
117 #define CONF_ELEMENTS_ELEMENT(b, i) ((b[CONF_ELEMENT_BYTE_POS(i)] << 8) | \
118 (b[CONF_ELEMENT_BYTE_POS(i) + 1]))
120 #define CONF_CONTENT_ELEMENT_POS(c,x,y) ((c) * CONF_CONTENT_NUM_ELEMENTS + \
122 #define CONF_CONTENT_BYTE_POS(c,x,y) (CONF_CONTENT_ELEMENT_POS(c,x,y) * \
123 CONF_ELEMENT_NUM_BYTES)
124 #define CONF_CONTENTS_ELEMENT(b,c,x,y) ((b[CONF_CONTENT_BYTE_POS(c,x,y)]<< 8)|\
125 (b[CONF_CONTENT_BYTE_POS(c,x,y) + 1]))
127 // temporary variables used to store pointers to structure members
128 static struct LevelInfo li;
129 static struct ElementInfo xx_ei, yy_ei;
130 static struct ElementChangeInfo xx_change;
131 static struct ElementGroupInfo xx_group;
132 static struct EnvelopeInfo xx_envelope;
133 static unsigned int xx_event_bits[NUM_CE_BITFIELDS];
134 static char xx_default_description[MAX_ELEMENT_NAME_LEN + 1];
135 static int xx_num_contents;
136 static int xx_current_change_page;
137 static char xx_default_string_empty[1] = "";
138 static int xx_string_length_unused;
140 struct LevelFileConfigInfo
142 int element; // element for which data is to be stored
143 int save_type; // save data always, never or when changed
144 int data_type; // data type (used internally, not stored)
145 int conf_type; // micro chunk identifier (stored in file)
148 void *value; // variable that holds the data to be stored
149 int default_value; // initial default value for this variable
152 void *value_copy; // variable that holds the data to be copied
153 void *num_entities; // number of entities for multi-byte data
154 int default_num_entities; // default number of entities for this data
155 int max_num_entities; // maximal number of entities for this data
156 char *default_string; // optional default string for string data
159 static struct LevelFileConfigInfo chunk_config_INFO[] =
161 // ---------- values not related to single elements -------------------------
164 -1, SAVE_CONF_ALWAYS,
165 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
166 &li.game_engine_type, GAME_ENGINE_TYPE_RND
170 -1, SAVE_CONF_ALWAYS,
171 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
172 &li.fieldx, STD_LEV_FIELDX
175 -1, SAVE_CONF_ALWAYS,
176 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
177 &li.fieldy, STD_LEV_FIELDY
181 -1, SAVE_CONF_ALWAYS,
182 TYPE_INTEGER, CONF_VALUE_16_BIT(3),
187 -1, SAVE_CONF_ALWAYS,
188 TYPE_INTEGER, CONF_VALUE_16_BIT(4),
194 TYPE_INTEGER, CONF_VALUE_32_BIT(2),
200 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
201 &li.use_step_counter, FALSE
206 TYPE_BITFIELD, CONF_VALUE_8_BIT(4),
207 &li.wind_direction_initial, MV_NONE
212 TYPE_BOOLEAN, CONF_VALUE_8_BIT(5),
213 &li.em_slippery_gems, FALSE
218 TYPE_BOOLEAN, CONF_VALUE_8_BIT(6),
219 &li.use_custom_template, FALSE
224 TYPE_BITFIELD, CONF_VALUE_32_BIT(1),
225 &li.can_move_into_acid_bits, ~0 // default: everything can
230 TYPE_BITFIELD, CONF_VALUE_8_BIT(7),
231 &li.dont_collide_with_bits, ~0 // default: always deadly
236 TYPE_BOOLEAN, CONF_VALUE_8_BIT(8),
237 &li.em_explodes_by_fire, FALSE
242 TYPE_INTEGER, CONF_VALUE_16_BIT(5),
243 &li.score[SC_TIME_BONUS], 1
248 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
249 &li.auto_exit_sokoban, FALSE
254 TYPE_BOOLEAN, CONF_VALUE_8_BIT(10),
255 &li.auto_count_gems, FALSE
260 TYPE_BOOLEAN, CONF_VALUE_8_BIT(11),
261 &li.solved_by_one_player, FALSE
266 TYPE_INTEGER, CONF_VALUE_8_BIT(12),
267 &li.time_score_base, 1
272 TYPE_BOOLEAN, CONF_VALUE_8_BIT(13),
273 &li.rate_time_over_score, FALSE
283 static struct LevelFileConfigInfo chunk_config_ELEM[] =
285 // (these values are the same for each player)
288 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
289 &li.block_last_field, FALSE // default case for EM levels
293 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
294 &li.sp_block_last_field, TRUE // default case for SP levels
298 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
299 &li.instant_relocation, FALSE
303 TYPE_BOOLEAN, CONF_VALUE_8_BIT(4),
304 &li.can_pass_to_walkable, FALSE
308 TYPE_BOOLEAN, CONF_VALUE_8_BIT(5),
309 &li.block_snap_field, TRUE
313 TYPE_BOOLEAN, CONF_VALUE_8_BIT(6),
314 &li.continuous_snapping, TRUE
318 TYPE_BOOLEAN, CONF_VALUE_8_BIT(12),
319 &li.shifted_relocation, FALSE
323 TYPE_BOOLEAN, CONF_VALUE_8_BIT(15),
324 &li.lazy_relocation, FALSE
328 TYPE_BOOLEAN, CONF_VALUE_8_BIT(16),
329 &li.finish_dig_collect, TRUE
333 TYPE_BOOLEAN, CONF_VALUE_8_BIT(17),
334 &li.keep_walkable_ce, FALSE
337 // (these values are different for each player)
340 TYPE_INTEGER, CONF_VALUE_8_BIT(7),
341 &li.initial_player_stepsize[0], STEPSIZE_NORMAL
345 TYPE_BOOLEAN, CONF_VALUE_8_BIT(8),
346 &li.initial_player_gravity[0], FALSE
350 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
351 &li.use_start_element[0], FALSE
355 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
356 &li.start_element[0], EL_PLAYER_1
360 TYPE_BOOLEAN, CONF_VALUE_8_BIT(10),
361 &li.use_artwork_element[0], FALSE
365 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
366 &li.artwork_element[0], EL_PLAYER_1
370 TYPE_BOOLEAN, CONF_VALUE_8_BIT(11),
371 &li.use_explosion_element[0], FALSE
375 TYPE_ELEMENT, CONF_VALUE_16_BIT(3),
376 &li.explosion_element[0], EL_PLAYER_1
380 TYPE_BOOLEAN, CONF_VALUE_8_BIT(13),
381 &li.use_initial_inventory[0], FALSE
385 TYPE_BOOLEAN, CONF_VALUE_8_BIT(14),
386 &li.initial_inventory_size[0], 1
390 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(1),
391 &li.initial_inventory_content[0][0],EL_EMPTY, NULL,
392 &li.initial_inventory_size[0], 1, MAX_INITIAL_INVENTORY_SIZE
397 TYPE_INTEGER, CONF_VALUE_8_BIT(7),
398 &li.initial_player_stepsize[1], STEPSIZE_NORMAL
402 TYPE_BOOLEAN, CONF_VALUE_8_BIT(8),
403 &li.initial_player_gravity[1], FALSE
407 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
408 &li.use_start_element[1], FALSE
412 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
413 &li.start_element[1], EL_PLAYER_2
417 TYPE_BOOLEAN, CONF_VALUE_8_BIT(10),
418 &li.use_artwork_element[1], FALSE
422 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
423 &li.artwork_element[1], EL_PLAYER_2
427 TYPE_BOOLEAN, CONF_VALUE_8_BIT(11),
428 &li.use_explosion_element[1], FALSE
432 TYPE_ELEMENT, CONF_VALUE_16_BIT(3),
433 &li.explosion_element[1], EL_PLAYER_2
437 TYPE_BOOLEAN, CONF_VALUE_8_BIT(13),
438 &li.use_initial_inventory[1], FALSE
442 TYPE_BOOLEAN, CONF_VALUE_8_BIT(14),
443 &li.initial_inventory_size[1], 1
447 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(1),
448 &li.initial_inventory_content[1][0],EL_EMPTY, NULL,
449 &li.initial_inventory_size[1], 1, MAX_INITIAL_INVENTORY_SIZE
454 TYPE_INTEGER, CONF_VALUE_8_BIT(7),
455 &li.initial_player_stepsize[2], STEPSIZE_NORMAL
459 TYPE_BOOLEAN, CONF_VALUE_8_BIT(8),
460 &li.initial_player_gravity[2], FALSE
464 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
465 &li.use_start_element[2], FALSE
469 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
470 &li.start_element[2], EL_PLAYER_3
474 TYPE_BOOLEAN, CONF_VALUE_8_BIT(10),
475 &li.use_artwork_element[2], FALSE
479 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
480 &li.artwork_element[2], EL_PLAYER_3
484 TYPE_BOOLEAN, CONF_VALUE_8_BIT(11),
485 &li.use_explosion_element[2], FALSE
489 TYPE_ELEMENT, CONF_VALUE_16_BIT(3),
490 &li.explosion_element[2], EL_PLAYER_3
494 TYPE_BOOLEAN, CONF_VALUE_8_BIT(13),
495 &li.use_initial_inventory[2], FALSE
499 TYPE_BOOLEAN, CONF_VALUE_8_BIT(14),
500 &li.initial_inventory_size[2], 1
504 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(1),
505 &li.initial_inventory_content[2][0],EL_EMPTY, NULL,
506 &li.initial_inventory_size[2], 1, MAX_INITIAL_INVENTORY_SIZE
511 TYPE_INTEGER, CONF_VALUE_8_BIT(7),
512 &li.initial_player_stepsize[3], STEPSIZE_NORMAL
516 TYPE_BOOLEAN, CONF_VALUE_8_BIT(8),
517 &li.initial_player_gravity[3], FALSE
521 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
522 &li.use_start_element[3], FALSE
526 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
527 &li.start_element[3], EL_PLAYER_4
531 TYPE_BOOLEAN, CONF_VALUE_8_BIT(10),
532 &li.use_artwork_element[3], FALSE
536 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
537 &li.artwork_element[3], EL_PLAYER_4
541 TYPE_BOOLEAN, CONF_VALUE_8_BIT(11),
542 &li.use_explosion_element[3], FALSE
546 TYPE_ELEMENT, CONF_VALUE_16_BIT(3),
547 &li.explosion_element[3], EL_PLAYER_4
551 TYPE_BOOLEAN, CONF_VALUE_8_BIT(13),
552 &li.use_initial_inventory[3], FALSE
556 TYPE_BOOLEAN, CONF_VALUE_8_BIT(14),
557 &li.initial_inventory_size[3], 1
561 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(1),
562 &li.initial_inventory_content[3][0],EL_EMPTY, NULL,
563 &li.initial_inventory_size[3], 1, MAX_INITIAL_INVENTORY_SIZE
568 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
569 &li.score[SC_EMERALD], 10
574 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
575 &li.score[SC_DIAMOND], 10
580 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
581 &li.score[SC_BUG], 10
586 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
587 &li.score[SC_SPACESHIP], 10
592 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
593 &li.score[SC_PACMAN], 10
598 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
599 &li.score[SC_NUT], 10
604 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
605 &li.score[SC_DYNAMITE], 10
610 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
611 &li.score[SC_KEY], 10
616 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
617 &li.score[SC_PEARL], 10
622 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
623 &li.score[SC_CRYSTAL], 10
628 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
629 &li.amoeba_content, EL_DIAMOND
633 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
638 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
639 &li.grow_into_diggable, TRUE
644 TYPE_CONTENT_LIST, CONF_VALUE_BYTES(1),
645 &li.yamyam_content, EL_ROCK, NULL,
646 &li.num_yamyam_contents, 4, MAX_ELEMENT_CONTENTS
650 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
651 &li.score[SC_YAMYAM], 10
656 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
657 &li.score[SC_ROBOT], 10
661 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
667 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
673 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
674 &li.time_magic_wall, 10
679 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
680 &li.game_of_life[0], 2
684 TYPE_INTEGER, CONF_VALUE_8_BIT(2),
685 &li.game_of_life[1], 3
689 TYPE_INTEGER, CONF_VALUE_8_BIT(3),
690 &li.game_of_life[2], 3
694 TYPE_INTEGER, CONF_VALUE_8_BIT(4),
695 &li.game_of_life[3], 3
699 TYPE_BOOLEAN, CONF_VALUE_8_BIT(5),
700 &li.use_life_bugs, FALSE
705 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
710 TYPE_INTEGER, CONF_VALUE_8_BIT(2),
715 TYPE_INTEGER, CONF_VALUE_8_BIT(3),
720 TYPE_INTEGER, CONF_VALUE_8_BIT(4),
725 EL_TIMEGATE_SWITCH, -1,
726 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
727 &li.time_timegate, 10
731 EL_LIGHT_SWITCH_ACTIVE, -1,
732 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
737 EL_SHIELD_NORMAL, -1,
738 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
739 &li.shield_normal_time, 10
742 EL_SHIELD_NORMAL, -1,
743 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
744 &li.score[SC_SHIELD], 10
748 EL_SHIELD_DEADLY, -1,
749 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
750 &li.shield_deadly_time, 10
753 EL_SHIELD_DEADLY, -1,
754 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
755 &li.score[SC_SHIELD], 10
760 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
765 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
766 &li.extra_time_score, 10
770 EL_TIME_ORB_FULL, -1,
771 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
772 &li.time_orb_time, 10
775 EL_TIME_ORB_FULL, -1,
776 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
777 &li.use_time_orb_bug, FALSE
782 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
783 &li.use_spring_bug, FALSE
788 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
789 &li.android_move_time, 10
793 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
794 &li.android_clone_time, 10
797 EL_EMC_ANDROID, SAVE_CONF_NEVER,
798 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(1),
799 &li.android_clone_element[0], EL_EMPTY, NULL,
800 &li.num_android_clone_elements, 1, MAX_ANDROID_ELEMENTS_OLD
804 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(2),
805 &li.android_clone_element[0], EL_EMPTY, NULL,
806 &li.num_android_clone_elements, 1, MAX_ANDROID_ELEMENTS
811 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
816 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
821 EL_EMC_MAGNIFIER, -1,
822 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
823 &li.magnify_score, 10
826 EL_EMC_MAGNIFIER, -1,
827 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
832 EL_EMC_MAGIC_BALL, -1,
833 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
837 EL_EMC_MAGIC_BALL, -1,
838 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
839 &li.ball_random, FALSE
842 EL_EMC_MAGIC_BALL, -1,
843 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
844 &li.ball_active_initial, FALSE
847 EL_EMC_MAGIC_BALL, -1,
848 TYPE_CONTENT_LIST, CONF_VALUE_BYTES(1),
849 &li.ball_content, EL_EMPTY, NULL,
850 &li.num_ball_contents, 4, MAX_ELEMENT_CONTENTS
854 EL_SOKOBAN_FIELD_EMPTY, -1,
855 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
856 &li.sb_fields_needed, TRUE
860 EL_SOKOBAN_OBJECT, -1,
861 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
862 &li.sb_objects_needed, TRUE
867 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
868 &li.mm_laser_red, FALSE
872 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
873 &li.mm_laser_green, FALSE
877 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
878 &li.mm_laser_blue, TRUE
883 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
884 &li.df_laser_red, TRUE
888 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
889 &li.df_laser_green, TRUE
893 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
894 &li.df_laser_blue, FALSE
898 EL_MM_FUSE_ACTIVE, -1,
899 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
904 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
910 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
915 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
916 &li.mm_ball_choice_mode, ANIM_RANDOM
920 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(1),
921 &li.mm_ball_content, EL_EMPTY, NULL,
922 &li.num_mm_ball_contents, 8, MAX_MM_BALL_CONTENTS
926 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
927 &li.rotate_mm_ball_content, TRUE
931 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
932 &li.explode_mm_ball, FALSE
936 EL_MM_STEEL_BLOCK, -1,
937 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
938 &li.mm_time_block, 75
942 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
943 &li.score[SC_ELEM_BONUS], 10
946 // ---------- unused values -------------------------------------------------
949 EL_UNKNOWN, SAVE_CONF_NEVER,
950 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
951 &li.score[SC_UNKNOWN_15], 10
961 static struct LevelFileConfigInfo chunk_config_NOTE[] =
965 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
966 &xx_envelope.xsize, MAX_ENVELOPE_XSIZE,
970 TYPE_INTEGER, CONF_VALUE_8_BIT(2),
971 &xx_envelope.ysize, MAX_ENVELOPE_YSIZE,
976 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
977 &xx_envelope.autowrap, FALSE
981 TYPE_BOOLEAN, CONF_VALUE_8_BIT(4),
982 &xx_envelope.centered, FALSE
987 TYPE_STRING, CONF_VALUE_BYTES(1),
988 &xx_envelope.text, -1, NULL,
989 &xx_string_length_unused, -1, MAX_ENVELOPE_TEXT_LEN,
990 &xx_default_string_empty[0]
1000 static struct LevelFileConfigInfo chunk_config_CUSX_base[] =
1004 TYPE_STRING, CONF_VALUE_BYTES(1),
1005 &xx_ei.description[0], -1,
1006 &yy_ei.description[0],
1007 &xx_string_length_unused, -1, MAX_ELEMENT_NAME_LEN,
1008 &xx_default_description[0]
1013 TYPE_BITFIELD, CONF_VALUE_32_BIT(1),
1014 &xx_ei.properties[EP_BITFIELD_BASE_NR], EP_BITMASK_BASE_DEFAULT,
1015 &yy_ei.properties[EP_BITFIELD_BASE_NR]
1017 #if ENABLE_RESERVED_CODE
1018 // (reserved for later use)
1021 TYPE_BITFIELD, CONF_VALUE_32_BIT(2),
1022 &xx_ei.properties[EP_BITFIELD_BASE_NR + 1], EP_BITMASK_DEFAULT,
1023 &yy_ei.properties[EP_BITFIELD_BASE_NR + 1]
1029 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1030 &xx_ei.use_gfx_element, FALSE,
1031 &yy_ei.use_gfx_element
1035 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
1036 &xx_ei.gfx_element_initial, EL_EMPTY_SPACE,
1037 &yy_ei.gfx_element_initial
1042 TYPE_BITFIELD, CONF_VALUE_8_BIT(2),
1043 &xx_ei.access_direction, MV_ALL_DIRECTIONS,
1044 &yy_ei.access_direction
1049 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
1050 &xx_ei.collect_score_initial, 10,
1051 &yy_ei.collect_score_initial
1055 TYPE_INTEGER, CONF_VALUE_16_BIT(3),
1056 &xx_ei.collect_count_initial, 1,
1057 &yy_ei.collect_count_initial
1062 TYPE_INTEGER, CONF_VALUE_16_BIT(4),
1063 &xx_ei.ce_value_fixed_initial, 0,
1064 &yy_ei.ce_value_fixed_initial
1068 TYPE_INTEGER, CONF_VALUE_16_BIT(5),
1069 &xx_ei.ce_value_random_initial, 0,
1070 &yy_ei.ce_value_random_initial
1074 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
1075 &xx_ei.use_last_ce_value, FALSE,
1076 &yy_ei.use_last_ce_value
1081 TYPE_INTEGER, CONF_VALUE_16_BIT(6),
1082 &xx_ei.push_delay_fixed, 8,
1083 &yy_ei.push_delay_fixed
1087 TYPE_INTEGER, CONF_VALUE_16_BIT(7),
1088 &xx_ei.push_delay_random, 8,
1089 &yy_ei.push_delay_random
1093 TYPE_INTEGER, CONF_VALUE_16_BIT(8),
1094 &xx_ei.drop_delay_fixed, 0,
1095 &yy_ei.drop_delay_fixed
1099 TYPE_INTEGER, CONF_VALUE_16_BIT(9),
1100 &xx_ei.drop_delay_random, 0,
1101 &yy_ei.drop_delay_random
1105 TYPE_INTEGER, CONF_VALUE_16_BIT(10),
1106 &xx_ei.move_delay_fixed, 0,
1107 &yy_ei.move_delay_fixed
1111 TYPE_INTEGER, CONF_VALUE_16_BIT(11),
1112 &xx_ei.move_delay_random, 0,
1113 &yy_ei.move_delay_random
1117 TYPE_INTEGER, CONF_VALUE_16_BIT(16),
1118 &xx_ei.step_delay_fixed, 0,
1119 &yy_ei.step_delay_fixed
1123 TYPE_INTEGER, CONF_VALUE_16_BIT(17),
1124 &xx_ei.step_delay_random, 0,
1125 &yy_ei.step_delay_random
1130 TYPE_BITFIELD, CONF_VALUE_32_BIT(3),
1131 &xx_ei.move_pattern, MV_ALL_DIRECTIONS,
1136 TYPE_BITFIELD, CONF_VALUE_8_BIT(4),
1137 &xx_ei.move_direction_initial, MV_START_AUTOMATIC,
1138 &yy_ei.move_direction_initial
1142 TYPE_INTEGER, CONF_VALUE_8_BIT(5),
1143 &xx_ei.move_stepsize, TILEX / 8,
1144 &yy_ei.move_stepsize
1149 TYPE_ELEMENT, CONF_VALUE_16_BIT(12),
1150 &xx_ei.move_enter_element, EL_EMPTY_SPACE,
1151 &yy_ei.move_enter_element
1155 TYPE_ELEMENT, CONF_VALUE_16_BIT(13),
1156 &xx_ei.move_leave_element, EL_EMPTY_SPACE,
1157 &yy_ei.move_leave_element
1161 TYPE_INTEGER, CONF_VALUE_8_BIT(6),
1162 &xx_ei.move_leave_type, LEAVE_TYPE_UNLIMITED,
1163 &yy_ei.move_leave_type
1168 TYPE_INTEGER, CONF_VALUE_8_BIT(7),
1169 &xx_ei.slippery_type, SLIPPERY_ANY_RANDOM,
1170 &yy_ei.slippery_type
1175 TYPE_INTEGER, CONF_VALUE_8_BIT(8),
1176 &xx_ei.explosion_type, EXPLODES_3X3,
1177 &yy_ei.explosion_type
1181 TYPE_INTEGER, CONF_VALUE_16_BIT(14),
1182 &xx_ei.explosion_delay, 16,
1183 &yy_ei.explosion_delay
1187 TYPE_INTEGER, CONF_VALUE_16_BIT(15),
1188 &xx_ei.ignition_delay, 8,
1189 &yy_ei.ignition_delay
1194 TYPE_CONTENT_LIST, CONF_VALUE_BYTES(2),
1195 &xx_ei.content, EL_EMPTY_SPACE,
1197 &xx_num_contents, 1, 1
1200 // ---------- "num_change_pages" must be the last entry ---------------------
1203 -1, SAVE_CONF_ALWAYS,
1204 TYPE_INTEGER, CONF_VALUE_8_BIT(9),
1205 &xx_ei.num_change_pages, 1,
1206 &yy_ei.num_change_pages
1217 static struct LevelFileConfigInfo chunk_config_CUSX_change[] =
1219 // ---------- "current_change_page" must be the first entry -----------------
1222 -1, SAVE_CONF_ALWAYS,
1223 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
1224 &xx_current_change_page, -1
1227 // ---------- (the remaining entries can be in any order) -------------------
1231 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
1232 &xx_change.can_change, FALSE
1237 TYPE_BITFIELD, CONF_VALUE_32_BIT(1),
1238 &xx_event_bits[0], 0
1242 TYPE_BITFIELD, CONF_VALUE_32_BIT(2),
1243 &xx_event_bits[1], 0
1248 TYPE_BITFIELD, CONF_VALUE_8_BIT(3),
1249 &xx_change.trigger_player, CH_PLAYER_ANY
1253 TYPE_BITFIELD, CONF_VALUE_8_BIT(4),
1254 &xx_change.trigger_side, CH_SIDE_ANY
1258 TYPE_BITFIELD, CONF_VALUE_32_BIT(3),
1259 &xx_change.trigger_page, CH_PAGE_ANY
1264 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
1265 &xx_change.target_element, EL_EMPTY_SPACE
1270 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
1271 &xx_change.delay_fixed, 0
1275 TYPE_INTEGER, CONF_VALUE_16_BIT(3),
1276 &xx_change.delay_random, 0
1280 TYPE_INTEGER, CONF_VALUE_16_BIT(4),
1281 &xx_change.delay_frames, FRAMES_PER_SECOND
1286 TYPE_ELEMENT, CONF_VALUE_16_BIT(5),
1287 &xx_change.initial_trigger_element, EL_EMPTY_SPACE
1292 TYPE_BOOLEAN, CONF_VALUE_8_BIT(6),
1293 &xx_change.explode, FALSE
1297 TYPE_BOOLEAN, CONF_VALUE_8_BIT(7),
1298 &xx_change.use_target_content, FALSE
1302 TYPE_BOOLEAN, CONF_VALUE_8_BIT(8),
1303 &xx_change.only_if_complete, FALSE
1307 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
1308 &xx_change.use_random_replace, FALSE
1312 TYPE_INTEGER, CONF_VALUE_8_BIT(10),
1313 &xx_change.random_percentage, 100
1317 TYPE_INTEGER, CONF_VALUE_8_BIT(11),
1318 &xx_change.replace_when, CP_WHEN_EMPTY
1323 TYPE_BOOLEAN, CONF_VALUE_8_BIT(12),
1324 &xx_change.has_action, FALSE
1328 TYPE_INTEGER, CONF_VALUE_8_BIT(13),
1329 &xx_change.action_type, CA_NO_ACTION
1333 TYPE_INTEGER, CONF_VALUE_8_BIT(14),
1334 &xx_change.action_mode, CA_MODE_UNDEFINED
1338 TYPE_INTEGER, CONF_VALUE_16_BIT(6),
1339 &xx_change.action_arg, CA_ARG_UNDEFINED
1344 TYPE_ELEMENT, CONF_VALUE_16_BIT(7),
1345 &xx_change.action_element, EL_EMPTY_SPACE
1350 TYPE_CONTENT_LIST, CONF_VALUE_BYTES(1),
1351 &xx_change.target_content, EL_EMPTY_SPACE, NULL,
1352 &xx_num_contents, 1, 1
1362 static struct LevelFileConfigInfo chunk_config_GRPX[] =
1366 TYPE_STRING, CONF_VALUE_BYTES(1),
1367 &xx_ei.description[0], -1, NULL,
1368 &xx_string_length_unused, -1, MAX_ELEMENT_NAME_LEN,
1369 &xx_default_description[0]
1374 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1375 &xx_ei.use_gfx_element, FALSE
1379 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
1380 &xx_ei.gfx_element_initial, EL_EMPTY_SPACE
1385 TYPE_INTEGER, CONF_VALUE_8_BIT(2),
1386 &xx_group.choice_mode, ANIM_RANDOM
1391 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(2),
1392 &xx_group.element[0], EL_EMPTY_SPACE, NULL,
1393 &xx_group.num_elements, 1, MAX_ELEMENTS_IN_GROUP
1403 static struct LevelFileConfigInfo chunk_config_EMPX[] =
1407 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1408 &xx_ei.use_gfx_element, FALSE
1412 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
1413 &xx_ei.gfx_element_initial, EL_EMPTY_SPACE
1423 static struct LevelFileConfigInfo chunk_config_CONF[] = // (OBSOLETE)
1427 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
1428 &li.block_snap_field, TRUE
1432 TYPE_BOOLEAN, CONF_VALUE_8_BIT(13),
1433 &li.continuous_snapping, TRUE
1437 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
1438 &li.initial_player_stepsize[0], STEPSIZE_NORMAL
1442 TYPE_BOOLEAN, CONF_VALUE_8_BIT(10),
1443 &li.use_start_element[0], FALSE
1447 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
1448 &li.start_element[0], EL_PLAYER_1
1452 TYPE_BOOLEAN, CONF_VALUE_8_BIT(11),
1453 &li.use_artwork_element[0], FALSE
1457 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
1458 &li.artwork_element[0], EL_PLAYER_1
1462 TYPE_BOOLEAN, CONF_VALUE_8_BIT(12),
1463 &li.use_explosion_element[0], FALSE
1467 TYPE_ELEMENT, CONF_VALUE_16_BIT(3),
1468 &li.explosion_element[0], EL_PLAYER_1
1483 filetype_id_list[] =
1485 { LEVEL_FILE_TYPE_RND, "RND" },
1486 { LEVEL_FILE_TYPE_BD, "BD" },
1487 { LEVEL_FILE_TYPE_EM, "EM" },
1488 { LEVEL_FILE_TYPE_SP, "SP" },
1489 { LEVEL_FILE_TYPE_DX, "DX" },
1490 { LEVEL_FILE_TYPE_SB, "SB" },
1491 { LEVEL_FILE_TYPE_DC, "DC" },
1492 { LEVEL_FILE_TYPE_MM, "MM" },
1493 { LEVEL_FILE_TYPE_MM, "DF" },
1498 // ============================================================================
1499 // level file functions
1500 // ============================================================================
1502 static boolean check_special_flags(char *flag)
1504 if (strEqual(options.special_flags, flag) ||
1505 strEqual(leveldir_current->special_flags, flag))
1511 static struct DateInfo getCurrentDate(void)
1513 time_t epoch_seconds = time(NULL);
1514 struct tm *now = localtime(&epoch_seconds);
1515 struct DateInfo date;
1517 date.year = now->tm_year + 1900;
1518 date.month = now->tm_mon + 1;
1519 date.day = now->tm_mday;
1521 date.src = DATE_SRC_CLOCK;
1526 static void resetEventFlags(struct ElementChangeInfo *change)
1530 for (i = 0; i < NUM_CHANGE_EVENTS; i++)
1531 change->has_event[i] = FALSE;
1534 static void resetEventBits(void)
1538 for (i = 0; i < NUM_CE_BITFIELDS; i++)
1539 xx_event_bits[i] = 0;
1542 static void setEventFlagsFromEventBits(struct ElementChangeInfo *change)
1546 /* important: only change event flag if corresponding event bit is set
1547 (this is because all xx_event_bits[] values are loaded separately,
1548 and all xx_event_bits[] values are set back to zero before loading
1549 another value xx_event_bits[x] (each value representing 32 flags)) */
1551 for (i = 0; i < NUM_CHANGE_EVENTS; i++)
1552 if (xx_event_bits[CH_EVENT_BITFIELD_NR(i)] & CH_EVENT_BIT(i))
1553 change->has_event[i] = TRUE;
1556 static void setEventBitsFromEventFlags(struct ElementChangeInfo *change)
1560 /* in contrast to the above function setEventFlagsFromEventBits(), it
1561 would also be possible to set all bits in xx_event_bits[] to 0 or 1
1562 depending on the corresponding change->has_event[i] values here, as
1563 all xx_event_bits[] values are reset in resetEventBits() before */
1565 for (i = 0; i < NUM_CHANGE_EVENTS; i++)
1566 if (change->has_event[i])
1567 xx_event_bits[CH_EVENT_BITFIELD_NR(i)] |= CH_EVENT_BIT(i);
1570 static char *getDefaultElementDescription(struct ElementInfo *ei)
1572 static char description[MAX_ELEMENT_NAME_LEN + 1];
1573 char *default_description = (ei->custom_description != NULL ?
1574 ei->custom_description :
1575 ei->editor_description);
1578 // always start with reliable default values
1579 for (i = 0; i < MAX_ELEMENT_NAME_LEN + 1; i++)
1580 description[i] = '\0';
1582 // truncate element description to MAX_ELEMENT_NAME_LEN bytes
1583 strncpy(description, default_description, MAX_ELEMENT_NAME_LEN);
1585 return &description[0];
1588 static void setElementDescriptionToDefault(struct ElementInfo *ei)
1590 char *default_description = getDefaultElementDescription(ei);
1593 for (i = 0; i < MAX_ELEMENT_NAME_LEN + 1; i++)
1594 ei->description[i] = default_description[i];
1597 static void setConfigToDefaultsFromConfigList(struct LevelFileConfigInfo *conf)
1601 for (i = 0; conf[i].data_type != -1; i++)
1603 int default_value = conf[i].default_value;
1604 int data_type = conf[i].data_type;
1605 int conf_type = conf[i].conf_type;
1606 int byte_mask = conf_type & CONF_MASK_BYTES;
1608 if (byte_mask == CONF_MASK_MULTI_BYTES)
1610 int default_num_entities = conf[i].default_num_entities;
1611 int max_num_entities = conf[i].max_num_entities;
1613 *(int *)(conf[i].num_entities) = default_num_entities;
1615 if (data_type == TYPE_STRING)
1617 char *default_string = conf[i].default_string;
1618 char *string = (char *)(conf[i].value);
1620 strncpy(string, default_string, max_num_entities);
1622 else if (data_type == TYPE_ELEMENT_LIST)
1624 int *element_array = (int *)(conf[i].value);
1627 for (j = 0; j < max_num_entities; j++)
1628 element_array[j] = default_value;
1630 else if (data_type == TYPE_CONTENT_LIST)
1632 struct Content *content = (struct Content *)(conf[i].value);
1635 for (c = 0; c < max_num_entities; c++)
1636 for (y = 0; y < 3; y++)
1637 for (x = 0; x < 3; x++)
1638 content[c].e[x][y] = default_value;
1641 else // constant size configuration data (1, 2 or 4 bytes)
1643 if (data_type == TYPE_BOOLEAN)
1644 *(boolean *)(conf[i].value) = default_value;
1646 *(int *) (conf[i].value) = default_value;
1651 static void copyConfigFromConfigList(struct LevelFileConfigInfo *conf)
1655 for (i = 0; conf[i].data_type != -1; i++)
1657 int data_type = conf[i].data_type;
1658 int conf_type = conf[i].conf_type;
1659 int byte_mask = conf_type & CONF_MASK_BYTES;
1661 if (byte_mask == CONF_MASK_MULTI_BYTES)
1663 int max_num_entities = conf[i].max_num_entities;
1665 if (data_type == TYPE_STRING)
1667 char *string = (char *)(conf[i].value);
1668 char *string_copy = (char *)(conf[i].value_copy);
1670 strncpy(string_copy, string, max_num_entities);
1672 else if (data_type == TYPE_ELEMENT_LIST)
1674 int *element_array = (int *)(conf[i].value);
1675 int *element_array_copy = (int *)(conf[i].value_copy);
1678 for (j = 0; j < max_num_entities; j++)
1679 element_array_copy[j] = element_array[j];
1681 else if (data_type == TYPE_CONTENT_LIST)
1683 struct Content *content = (struct Content *)(conf[i].value);
1684 struct Content *content_copy = (struct Content *)(conf[i].value_copy);
1687 for (c = 0; c < max_num_entities; c++)
1688 for (y = 0; y < 3; y++)
1689 for (x = 0; x < 3; x++)
1690 content_copy[c].e[x][y] = content[c].e[x][y];
1693 else // constant size configuration data (1, 2 or 4 bytes)
1695 if (data_type == TYPE_BOOLEAN)
1696 *(boolean *)(conf[i].value_copy) = *(boolean *)(conf[i].value);
1698 *(int *) (conf[i].value_copy) = *(int *) (conf[i].value);
1703 void copyElementInfo(struct ElementInfo *ei_from, struct ElementInfo *ei_to)
1707 xx_ei = *ei_from; // copy element data into temporary buffer
1708 yy_ei = *ei_to; // copy element data into temporary buffer
1710 copyConfigFromConfigList(chunk_config_CUSX_base);
1715 // ---------- reinitialize and copy change pages ----------
1717 ei_to->num_change_pages = ei_from->num_change_pages;
1718 ei_to->current_change_page = ei_from->current_change_page;
1720 setElementChangePages(ei_to, ei_to->num_change_pages);
1722 for (i = 0; i < ei_to->num_change_pages; i++)
1723 ei_to->change_page[i] = ei_from->change_page[i];
1725 // ---------- copy group element info ----------
1726 if (ei_from->group != NULL && ei_to->group != NULL) // group or internal
1727 *ei_to->group = *ei_from->group;
1729 // mark this custom element as modified
1730 ei_to->modified_settings = TRUE;
1733 void setElementChangePages(struct ElementInfo *ei, int change_pages)
1735 int change_page_size = sizeof(struct ElementChangeInfo);
1737 ei->num_change_pages = MAX(1, change_pages);
1740 checked_realloc(ei->change_page, ei->num_change_pages * change_page_size);
1742 if (ei->current_change_page >= ei->num_change_pages)
1743 ei->current_change_page = ei->num_change_pages - 1;
1745 ei->change = &ei->change_page[ei->current_change_page];
1748 void setElementChangeInfoToDefaults(struct ElementChangeInfo *change)
1750 xx_change = *change; // copy change data into temporary buffer
1752 setConfigToDefaultsFromConfigList(chunk_config_CUSX_change);
1754 *change = xx_change;
1756 resetEventFlags(change);
1758 change->direct_action = 0;
1759 change->other_action = 0;
1761 change->pre_change_function = NULL;
1762 change->change_function = NULL;
1763 change->post_change_function = NULL;
1766 static void setLevelInfoToDefaults_Level(struct LevelInfo *level)
1770 li = *level; // copy level data into temporary buffer
1771 setConfigToDefaultsFromConfigList(chunk_config_INFO);
1772 *level = li; // copy temporary buffer back to level data
1774 setLevelInfoToDefaults_EM();
1775 setLevelInfoToDefaults_SP();
1776 setLevelInfoToDefaults_MM();
1778 level->native_em_level = &native_em_level;
1779 level->native_sp_level = &native_sp_level;
1780 level->native_mm_level = &native_mm_level;
1782 level->file_version = FILE_VERSION_ACTUAL;
1783 level->game_version = GAME_VERSION_ACTUAL;
1785 level->creation_date = getCurrentDate();
1787 level->encoding_16bit_field = TRUE;
1788 level->encoding_16bit_yamyam = TRUE;
1789 level->encoding_16bit_amoeba = TRUE;
1791 // clear level name and level author string buffers
1792 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
1793 level->name[i] = '\0';
1794 for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
1795 level->author[i] = '\0';
1797 // set level name and level author to default values
1798 strcpy(level->name, NAMELESS_LEVEL_NAME);
1799 strcpy(level->author, ANONYMOUS_NAME);
1801 // set level playfield to playable default level with player and exit
1802 for (x = 0; x < MAX_LEV_FIELDX; x++)
1803 for (y = 0; y < MAX_LEV_FIELDY; y++)
1804 level->field[x][y] = EL_SAND;
1806 level->field[0][0] = EL_PLAYER_1;
1807 level->field[STD_LEV_FIELDX - 1][STD_LEV_FIELDY - 1] = EL_EXIT_CLOSED;
1809 BorderElement = EL_STEELWALL;
1811 // detect custom elements when loading them
1812 level->file_has_custom_elements = FALSE;
1814 // set all bug compatibility flags to "false" => do not emulate this bug
1815 level->use_action_after_change_bug = FALSE;
1817 if (leveldir_current)
1819 // try to determine better author name than 'anonymous'
1820 if (!strEqual(leveldir_current->author, ANONYMOUS_NAME))
1822 strncpy(level->author, leveldir_current->author, MAX_LEVEL_AUTHOR_LEN);
1823 level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
1827 switch (LEVELCLASS(leveldir_current))
1829 case LEVELCLASS_TUTORIAL:
1830 strcpy(level->author, PROGRAM_AUTHOR_STRING);
1833 case LEVELCLASS_CONTRIB:
1834 strncpy(level->author, leveldir_current->name, MAX_LEVEL_AUTHOR_LEN);
1835 level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
1838 case LEVELCLASS_PRIVATE:
1839 strncpy(level->author, getRealName(), MAX_LEVEL_AUTHOR_LEN);
1840 level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
1844 // keep default value
1851 static void setLevelInfoToDefaults_Elements(struct LevelInfo *level)
1853 static boolean clipboard_elements_initialized = FALSE;
1856 InitElementPropertiesStatic();
1858 li = *level; // copy level data into temporary buffer
1859 setConfigToDefaultsFromConfigList(chunk_config_ELEM);
1860 *level = li; // copy temporary buffer back to level data
1862 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1865 struct ElementInfo *ei = &element_info[element];
1867 if (element == EL_MM_GRAY_BALL)
1869 struct LevelInfo_MM *level_mm = level->native_mm_level;
1872 for (j = 0; j < level->num_mm_ball_contents; j++)
1873 level->mm_ball_content[j] =
1874 map_element_MM_to_RND(level_mm->ball_content[j]);
1877 // never initialize clipboard elements after the very first time
1878 // (to be able to use clipboard elements between several levels)
1879 if (IS_CLIPBOARD_ELEMENT(element) && clipboard_elements_initialized)
1882 if (IS_ENVELOPE(element))
1884 int envelope_nr = element - EL_ENVELOPE_1;
1886 setConfigToDefaultsFromConfigList(chunk_config_NOTE);
1888 level->envelope[envelope_nr] = xx_envelope;
1891 if (IS_CUSTOM_ELEMENT(element) ||
1892 IS_GROUP_ELEMENT(element) ||
1893 IS_INTERNAL_ELEMENT(element))
1895 xx_ei = *ei; // copy element data into temporary buffer
1897 setConfigToDefaultsFromConfigList(chunk_config_CUSX_base);
1902 setElementChangePages(ei, 1);
1903 setElementChangeInfoToDefaults(ei->change);
1905 if (IS_CUSTOM_ELEMENT(element) ||
1906 IS_GROUP_ELEMENT(element) ||
1907 IS_INTERNAL_ELEMENT(element))
1909 setElementDescriptionToDefault(ei);
1911 ei->modified_settings = FALSE;
1914 if (IS_CUSTOM_ELEMENT(element) ||
1915 IS_INTERNAL_ELEMENT(element))
1917 // internal values used in level editor
1919 ei->access_type = 0;
1920 ei->access_layer = 0;
1921 ei->access_protected = 0;
1922 ei->walk_to_action = 0;
1923 ei->smash_targets = 0;
1926 ei->can_explode_by_fire = FALSE;
1927 ei->can_explode_smashed = FALSE;
1928 ei->can_explode_impact = FALSE;
1930 ei->current_change_page = 0;
1933 if (IS_GROUP_ELEMENT(element) ||
1934 IS_INTERNAL_ELEMENT(element))
1936 struct ElementGroupInfo *group;
1938 // initialize memory for list of elements in group
1939 if (ei->group == NULL)
1940 ei->group = checked_malloc(sizeof(struct ElementGroupInfo));
1944 xx_group = *group; // copy group data into temporary buffer
1946 setConfigToDefaultsFromConfigList(chunk_config_GRPX);
1951 if (IS_EMPTY_ELEMENT(element) ||
1952 IS_INTERNAL_ELEMENT(element))
1954 xx_ei = *ei; // copy element data into temporary buffer
1956 setConfigToDefaultsFromConfigList(chunk_config_EMPX);
1962 clipboard_elements_initialized = TRUE;
1965 static void setLevelInfoToDefaults(struct LevelInfo *level,
1966 boolean level_info_only,
1967 boolean reset_file_status)
1969 setLevelInfoToDefaults_Level(level);
1971 if (!level_info_only)
1972 setLevelInfoToDefaults_Elements(level);
1974 if (reset_file_status)
1976 level->no_valid_file = FALSE;
1977 level->no_level_file = FALSE;
1980 level->changed = FALSE;
1983 static void setFileInfoToDefaults(struct LevelFileInfo *level_file_info)
1985 level_file_info->nr = 0;
1986 level_file_info->type = LEVEL_FILE_TYPE_UNKNOWN;
1987 level_file_info->packed = FALSE;
1989 setString(&level_file_info->basename, NULL);
1990 setString(&level_file_info->filename, NULL);
1993 int getMappedElement_SB(int, boolean);
1995 static void ActivateLevelTemplate(void)
1999 if (check_special_flags("load_xsb_to_ces"))
2001 // fill smaller playfields with padding "beyond border wall" elements
2002 if (level.fieldx < level_template.fieldx ||
2003 level.fieldy < level_template.fieldy)
2005 short field[level.fieldx][level.fieldy];
2006 int new_fieldx = MAX(level.fieldx, level_template.fieldx);
2007 int new_fieldy = MAX(level.fieldy, level_template.fieldy);
2008 int pos_fieldx = (new_fieldx - level.fieldx) / 2;
2009 int pos_fieldy = (new_fieldy - level.fieldy) / 2;
2011 // copy old playfield (which is smaller than the visible area)
2012 for (y = 0; y < level.fieldy; y++) for (x = 0; x < level.fieldx; x++)
2013 field[x][y] = level.field[x][y];
2015 // fill new, larger playfield with "beyond border wall" elements
2016 for (y = 0; y < new_fieldy; y++) for (x = 0; x < new_fieldx; x++)
2017 level.field[x][y] = getMappedElement_SB('_', TRUE);
2019 // copy the old playfield to the middle of the new playfield
2020 for (y = 0; y < level.fieldy; y++) for (x = 0; x < level.fieldx; x++)
2021 level.field[pos_fieldx + x][pos_fieldy + y] = field[x][y];
2023 level.fieldx = new_fieldx;
2024 level.fieldy = new_fieldy;
2028 // Currently there is no special action needed to activate the template
2029 // data, because 'element_info' property settings overwrite the original
2030 // level data, while all other variables do not change.
2032 // Exception: 'from_level_template' elements in the original level playfield
2033 // are overwritten with the corresponding elements at the same position in
2034 // playfield from the level template.
2036 for (x = 0; x < level.fieldx; x++)
2037 for (y = 0; y < level.fieldy; y++)
2038 if (level.field[x][y] == EL_FROM_LEVEL_TEMPLATE)
2039 level.field[x][y] = level_template.field[x][y];
2041 if (check_special_flags("load_xsb_to_ces"))
2043 struct LevelInfo level_backup = level;
2045 // overwrite all individual level settings from template level settings
2046 level = level_template;
2048 // restore level file info
2049 level.file_info = level_backup.file_info;
2051 // restore playfield size
2052 level.fieldx = level_backup.fieldx;
2053 level.fieldy = level_backup.fieldy;
2055 // restore playfield content
2056 for (x = 0; x < level.fieldx; x++)
2057 for (y = 0; y < level.fieldy; y++)
2058 level.field[x][y] = level_backup.field[x][y];
2060 // restore name and author from individual level
2061 strcpy(level.name, level_backup.name);
2062 strcpy(level.author, level_backup.author);
2064 // restore flag "use_custom_template"
2065 level.use_custom_template = level_backup.use_custom_template;
2069 static char *getLevelFilenameFromBasename(char *basename)
2071 static char *filename = NULL;
2073 checked_free(filename);
2075 filename = getPath2(getCurrentLevelDir(), basename);
2080 static int getFileTypeFromBasename(char *basename)
2082 // !!! ALSO SEE COMMENT IN checkForPackageFromBasename() !!!
2084 static char *filename = NULL;
2085 struct stat file_status;
2087 // ---------- try to determine file type from filename ----------
2089 // check for typical filename of a Supaplex level package file
2090 if (strlen(basename) == 10 && strPrefixLower(basename, "levels.d"))
2091 return LEVEL_FILE_TYPE_SP;
2093 // check for typical filename of a Diamond Caves II level package file
2094 if (strSuffixLower(basename, ".dc") ||
2095 strSuffixLower(basename, ".dc2"))
2096 return LEVEL_FILE_TYPE_DC;
2098 // check for typical filename of a Sokoban level package file
2099 if (strSuffixLower(basename, ".xsb") &&
2100 strchr(basename, '%') == NULL)
2101 return LEVEL_FILE_TYPE_SB;
2103 // ---------- try to determine file type from filesize ----------
2105 checked_free(filename);
2106 filename = getPath2(getCurrentLevelDir(), basename);
2108 if (stat(filename, &file_status) == 0)
2110 // check for typical filesize of a Supaplex level package file
2111 if (file_status.st_size == 170496)
2112 return LEVEL_FILE_TYPE_SP;
2115 return LEVEL_FILE_TYPE_UNKNOWN;
2118 static int getFileTypeFromMagicBytes(char *filename, int type)
2122 if ((file = openFile(filename, MODE_READ)))
2124 char chunk_name[CHUNK_ID_LEN + 1];
2126 getFileChunkBE(file, chunk_name, NULL);
2128 if (strEqual(chunk_name, "MMII") ||
2129 strEqual(chunk_name, "MIRR"))
2130 type = LEVEL_FILE_TYPE_MM;
2138 static boolean checkForPackageFromBasename(char *basename)
2140 // !!! WON'T WORK ANYMORE IF getFileTypeFromBasename() ALSO DETECTS !!!
2141 // !!! SINGLE LEVELS (CURRENTLY ONLY DETECTS LEVEL PACKAGES !!!
2143 return (getFileTypeFromBasename(basename) != LEVEL_FILE_TYPE_UNKNOWN);
2146 static char *getSingleLevelBasenameExt(int nr, char *extension)
2148 static char basename[MAX_FILENAME_LEN];
2151 sprintf(basename, "%s", LEVELTEMPLATE_FILENAME);
2153 sprintf(basename, "%03d.%s", nr, extension);
2158 static char *getSingleLevelBasename(int nr)
2160 return getSingleLevelBasenameExt(nr, LEVELFILE_EXTENSION);
2163 static char *getPackedLevelBasename(int type)
2165 static char basename[MAX_FILENAME_LEN];
2166 char *directory = getCurrentLevelDir();
2168 DirectoryEntry *dir_entry;
2170 strcpy(basename, UNDEFINED_FILENAME); // default: undefined file
2172 if ((dir = openDirectory(directory)) == NULL)
2174 Warn("cannot read current level directory '%s'", directory);
2179 while ((dir_entry = readDirectory(dir)) != NULL) // loop all entries
2181 char *entry_basename = dir_entry->basename;
2182 int entry_type = getFileTypeFromBasename(entry_basename);
2184 if (entry_type != LEVEL_FILE_TYPE_UNKNOWN) // found valid level package
2186 if (type == LEVEL_FILE_TYPE_UNKNOWN ||
2189 strcpy(basename, entry_basename);
2196 closeDirectory(dir);
2201 static char *getSingleLevelFilename(int nr)
2203 return getLevelFilenameFromBasename(getSingleLevelBasename(nr));
2206 #if ENABLE_UNUSED_CODE
2207 static char *getPackedLevelFilename(int type)
2209 return getLevelFilenameFromBasename(getPackedLevelBasename(type));
2213 char *getDefaultLevelFilename(int nr)
2215 return getSingleLevelFilename(nr);
2218 #if ENABLE_UNUSED_CODE
2219 static void setLevelFileInfo_SingleLevelFilename(struct LevelFileInfo *lfi,
2223 lfi->packed = FALSE;
2225 setString(&lfi->basename, getSingleLevelBasename(lfi->nr, lfi->type));
2226 setString(&lfi->filename, getLevelFilenameFromBasename(lfi->basename));
2230 static void setLevelFileInfo_FormatLevelFilename(struct LevelFileInfo *lfi,
2231 int type, char *format, ...)
2233 static char basename[MAX_FILENAME_LEN];
2236 va_start(ap, format);
2237 vsprintf(basename, format, ap);
2241 lfi->packed = FALSE;
2243 setString(&lfi->basename, basename);
2244 setString(&lfi->filename, getLevelFilenameFromBasename(lfi->basename));
2247 static void setLevelFileInfo_PackedLevelFilename(struct LevelFileInfo *lfi,
2253 setString(&lfi->basename, getPackedLevelBasename(lfi->type));
2254 setString(&lfi->filename, getLevelFilenameFromBasename(lfi->basename));
2257 static int getFiletypeFromID(char *filetype_id)
2259 char *filetype_id_lower;
2260 int filetype = LEVEL_FILE_TYPE_UNKNOWN;
2263 if (filetype_id == NULL)
2264 return LEVEL_FILE_TYPE_UNKNOWN;
2266 filetype_id_lower = getStringToLower(filetype_id);
2268 for (i = 0; filetype_id_list[i].id != NULL; i++)
2270 char *id_lower = getStringToLower(filetype_id_list[i].id);
2272 if (strEqual(filetype_id_lower, id_lower))
2273 filetype = filetype_id_list[i].filetype;
2277 if (filetype != LEVEL_FILE_TYPE_UNKNOWN)
2281 free(filetype_id_lower);
2286 char *getLocalLevelTemplateFilename(void)
2288 return getDefaultLevelFilename(-1);
2291 char *getGlobalLevelTemplateFilename(void)
2293 // global variable "leveldir_current" must be modified in the loop below
2294 LevelDirTree *leveldir_current_last = leveldir_current;
2295 char *filename = NULL;
2297 // check for template level in path from current to topmost tree node
2299 while (leveldir_current != NULL)
2301 filename = getDefaultLevelFilename(-1);
2303 if (fileExists(filename))
2306 leveldir_current = leveldir_current->node_parent;
2309 // restore global variable "leveldir_current" modified in above loop
2310 leveldir_current = leveldir_current_last;
2315 static void determineLevelFileInfo_Filename(struct LevelFileInfo *lfi)
2319 // special case: level number is negative => check for level template file
2322 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
2323 getSingleLevelBasename(-1));
2325 // replace local level template filename with global template filename
2326 setString(&lfi->filename, getGlobalLevelTemplateFilename());
2328 // no fallback if template file not existing
2332 // special case: check for file name/pattern specified in "levelinfo.conf"
2333 if (leveldir_current->level_filename != NULL)
2335 int filetype = getFiletypeFromID(leveldir_current->level_filetype);
2337 setLevelFileInfo_FormatLevelFilename(lfi, filetype,
2338 leveldir_current->level_filename, nr);
2340 lfi->packed = checkForPackageFromBasename(leveldir_current->level_filename);
2342 if (fileExists(lfi->filename))
2345 else if (leveldir_current->level_filetype != NULL)
2347 int filetype = getFiletypeFromID(leveldir_current->level_filetype);
2349 // check for specified native level file with standard file name
2350 setLevelFileInfo_FormatLevelFilename(lfi, filetype,
2351 "%03d.%s", nr, LEVELFILE_EXTENSION);
2352 if (fileExists(lfi->filename))
2356 // check for native Rocks'n'Diamonds level file
2357 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
2358 "%03d.%s", nr, LEVELFILE_EXTENSION);
2359 if (fileExists(lfi->filename))
2362 // check for Emerald Mine level file (V1)
2363 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "a%c%c",
2364 'a' + (nr / 10) % 26, '0' + nr % 10);
2365 if (fileExists(lfi->filename))
2367 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "A%c%c",
2368 'A' + (nr / 10) % 26, '0' + nr % 10);
2369 if (fileExists(lfi->filename))
2372 // check for Emerald Mine level file (V2 to V5)
2373 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%d", nr);
2374 if (fileExists(lfi->filename))
2377 // check for Emerald Mine level file (V6 / single mode)
2378 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02ds", nr);
2379 if (fileExists(lfi->filename))
2381 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02dS", nr);
2382 if (fileExists(lfi->filename))
2385 // check for Emerald Mine level file (V6 / teamwork mode)
2386 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02dt", nr);
2387 if (fileExists(lfi->filename))
2389 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02dT", nr);
2390 if (fileExists(lfi->filename))
2393 // check for various packed level file formats
2394 setLevelFileInfo_PackedLevelFilename(lfi, LEVEL_FILE_TYPE_UNKNOWN);
2395 if (fileExists(lfi->filename))
2398 // no known level file found -- use default values (and fail later)
2399 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
2400 "%03d.%s", nr, LEVELFILE_EXTENSION);
2403 static void determineLevelFileInfo_Filetype(struct LevelFileInfo *lfi)
2405 if (lfi->type == LEVEL_FILE_TYPE_UNKNOWN)
2406 lfi->type = getFileTypeFromBasename(lfi->basename);
2408 if (lfi->type == LEVEL_FILE_TYPE_RND)
2409 lfi->type = getFileTypeFromMagicBytes(lfi->filename, lfi->type);
2412 static void setLevelFileInfo(struct LevelFileInfo *level_file_info, int nr)
2414 // always start with reliable default values
2415 setFileInfoToDefaults(level_file_info);
2417 level_file_info->nr = nr; // set requested level number
2419 determineLevelFileInfo_Filename(level_file_info);
2420 determineLevelFileInfo_Filetype(level_file_info);
2423 static void copyLevelFileInfo(struct LevelFileInfo *lfi_from,
2424 struct LevelFileInfo *lfi_to)
2426 lfi_to->nr = lfi_from->nr;
2427 lfi_to->type = lfi_from->type;
2428 lfi_to->packed = lfi_from->packed;
2430 setString(&lfi_to->basename, lfi_from->basename);
2431 setString(&lfi_to->filename, lfi_from->filename);
2434 // ----------------------------------------------------------------------------
2435 // functions for loading R'n'D level
2436 // ----------------------------------------------------------------------------
2438 int getMappedElement(int element)
2440 // remap some (historic, now obsolete) elements
2444 case EL_PLAYER_OBSOLETE:
2445 element = EL_PLAYER_1;
2448 case EL_KEY_OBSOLETE:
2452 case EL_EM_KEY_1_FILE_OBSOLETE:
2453 element = EL_EM_KEY_1;
2456 case EL_EM_KEY_2_FILE_OBSOLETE:
2457 element = EL_EM_KEY_2;
2460 case EL_EM_KEY_3_FILE_OBSOLETE:
2461 element = EL_EM_KEY_3;
2464 case EL_EM_KEY_4_FILE_OBSOLETE:
2465 element = EL_EM_KEY_4;
2468 case EL_ENVELOPE_OBSOLETE:
2469 element = EL_ENVELOPE_1;
2477 if (element >= NUM_FILE_ELEMENTS)
2479 Warn("invalid level element %d", element);
2481 element = EL_UNKNOWN;
2489 static int getMappedElementByVersion(int element, int game_version)
2491 // remap some elements due to certain game version
2493 if (game_version <= VERSION_IDENT(2,2,0,0))
2495 // map game font elements
2496 element = (element == EL_CHAR('[') ? EL_CHAR_AUMLAUT :
2497 element == EL_CHAR('\\') ? EL_CHAR_OUMLAUT :
2498 element == EL_CHAR(']') ? EL_CHAR_UUMLAUT :
2499 element == EL_CHAR('^') ? EL_CHAR_COPYRIGHT : element);
2502 if (game_version < VERSION_IDENT(3,0,0,0))
2504 // map Supaplex gravity tube elements
2505 element = (element == EL_SP_GRAVITY_PORT_LEFT ? EL_SP_PORT_LEFT :
2506 element == EL_SP_GRAVITY_PORT_RIGHT ? EL_SP_PORT_RIGHT :
2507 element == EL_SP_GRAVITY_PORT_UP ? EL_SP_PORT_UP :
2508 element == EL_SP_GRAVITY_PORT_DOWN ? EL_SP_PORT_DOWN :
2515 static int LoadLevel_VERS(File *file, int chunk_size, struct LevelInfo *level)
2517 level->file_version = getFileVersion(file);
2518 level->game_version = getFileVersion(file);
2523 static int LoadLevel_DATE(File *file, int chunk_size, struct LevelInfo *level)
2525 level->creation_date.year = getFile16BitBE(file);
2526 level->creation_date.month = getFile8Bit(file);
2527 level->creation_date.day = getFile8Bit(file);
2529 level->creation_date.src = DATE_SRC_LEVELFILE;
2534 static int LoadLevel_HEAD(File *file, int chunk_size, struct LevelInfo *level)
2536 int initial_player_stepsize;
2537 int initial_player_gravity;
2540 level->fieldx = getFile8Bit(file);
2541 level->fieldy = getFile8Bit(file);
2543 level->time = getFile16BitBE(file);
2544 level->gems_needed = getFile16BitBE(file);
2546 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
2547 level->name[i] = getFile8Bit(file);
2548 level->name[MAX_LEVEL_NAME_LEN] = 0;
2550 for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
2551 level->score[i] = getFile8Bit(file);
2553 level->num_yamyam_contents = STD_ELEMENT_CONTENTS;
2554 for (i = 0; i < STD_ELEMENT_CONTENTS; i++)
2555 for (y = 0; y < 3; y++)
2556 for (x = 0; x < 3; x++)
2557 level->yamyam_content[i].e[x][y] = getMappedElement(getFile8Bit(file));
2559 level->amoeba_speed = getFile8Bit(file);
2560 level->time_magic_wall = getFile8Bit(file);
2561 level->time_wheel = getFile8Bit(file);
2562 level->amoeba_content = getMappedElement(getFile8Bit(file));
2564 initial_player_stepsize = (getFile8Bit(file) == 1 ? STEPSIZE_FAST :
2567 for (i = 0; i < MAX_PLAYERS; i++)
2568 level->initial_player_stepsize[i] = initial_player_stepsize;
2570 initial_player_gravity = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2572 for (i = 0; i < MAX_PLAYERS; i++)
2573 level->initial_player_gravity[i] = initial_player_gravity;
2575 level->encoding_16bit_field = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2576 level->em_slippery_gems = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2578 level->use_custom_template = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2580 level->block_last_field = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2581 level->sp_block_last_field = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2582 level->can_move_into_acid_bits = getFile32BitBE(file);
2583 level->dont_collide_with_bits = getFile8Bit(file);
2585 level->use_spring_bug = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2586 level->use_step_counter = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2588 level->instant_relocation = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2589 level->can_pass_to_walkable = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2590 level->grow_into_diggable = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2592 level->game_engine_type = getFile8Bit(file);
2594 ReadUnusedBytesFromFile(file, LEVEL_CHUNK_HEAD_UNUSED);
2599 static int LoadLevel_NAME(File *file, int chunk_size, struct LevelInfo *level)
2603 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
2604 level->name[i] = getFile8Bit(file);
2605 level->name[MAX_LEVEL_NAME_LEN] = 0;
2610 static int LoadLevel_AUTH(File *file, int chunk_size, struct LevelInfo *level)
2614 for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
2615 level->author[i] = getFile8Bit(file);
2616 level->author[MAX_LEVEL_AUTHOR_LEN] = 0;
2621 static int LoadLevel_BODY(File *file, int chunk_size, struct LevelInfo *level)
2624 int chunk_size_expected = level->fieldx * level->fieldy;
2626 /* Note: "chunk_size" was wrong before version 2.0 when elements are
2627 stored with 16-bit encoding (and should be twice as big then).
2628 Even worse, playfield data was stored 16-bit when only yamyam content
2629 contained 16-bit elements and vice versa. */
2631 if (level->encoding_16bit_field && level->file_version >= FILE_VERSION_2_0)
2632 chunk_size_expected *= 2;
2634 if (chunk_size_expected != chunk_size)
2636 ReadUnusedBytesFromFile(file, chunk_size);
2637 return chunk_size_expected;
2640 for (y = 0; y < level->fieldy; y++)
2641 for (x = 0; x < level->fieldx; x++)
2642 level->field[x][y] =
2643 getMappedElement(level->encoding_16bit_field ? getFile16BitBE(file) :
2648 static int LoadLevel_CONT(File *file, int chunk_size, struct LevelInfo *level)
2651 int header_size = 4;
2652 int content_size = MAX_ELEMENT_CONTENTS * 3 * 3;
2653 int chunk_size_expected = header_size + content_size;
2655 /* Note: "chunk_size" was wrong before version 2.0 when elements are
2656 stored with 16-bit encoding (and should be twice as big then).
2657 Even worse, playfield data was stored 16-bit when only yamyam content
2658 contained 16-bit elements and vice versa. */
2660 if (level->encoding_16bit_field && level->file_version >= FILE_VERSION_2_0)
2661 chunk_size_expected += content_size;
2663 if (chunk_size_expected != chunk_size)
2665 ReadUnusedBytesFromFile(file, chunk_size);
2666 return chunk_size_expected;
2670 level->num_yamyam_contents = getFile8Bit(file);
2674 // correct invalid number of content fields -- should never happen
2675 if (level->num_yamyam_contents < 1 ||
2676 level->num_yamyam_contents > MAX_ELEMENT_CONTENTS)
2677 level->num_yamyam_contents = STD_ELEMENT_CONTENTS;
2679 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
2680 for (y = 0; y < 3; y++)
2681 for (x = 0; x < 3; x++)
2682 level->yamyam_content[i].e[x][y] =
2683 getMappedElement(level->encoding_16bit_field ?
2684 getFile16BitBE(file) : getFile8Bit(file));
2688 static int LoadLevel_CNT2(File *file, int chunk_size, struct LevelInfo *level)
2693 int content_array[MAX_ELEMENT_CONTENTS][3][3];
2695 element = getMappedElement(getFile16BitBE(file));
2696 num_contents = getFile8Bit(file);
2698 getFile8Bit(file); // content x size (unused)
2699 getFile8Bit(file); // content y size (unused)
2701 ReadUnusedBytesFromFile(file, LEVEL_CHUNK_CNT2_UNUSED);
2703 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
2704 for (y = 0; y < 3; y++)
2705 for (x = 0; x < 3; x++)
2706 content_array[i][x][y] = getMappedElement(getFile16BitBE(file));
2708 // correct invalid number of content fields -- should never happen
2709 if (num_contents < 1 || num_contents > MAX_ELEMENT_CONTENTS)
2710 num_contents = STD_ELEMENT_CONTENTS;
2712 if (element == EL_YAMYAM)
2714 level->num_yamyam_contents = num_contents;
2716 for (i = 0; i < num_contents; i++)
2717 for (y = 0; y < 3; y++)
2718 for (x = 0; x < 3; x++)
2719 level->yamyam_content[i].e[x][y] = content_array[i][x][y];
2721 else if (element == EL_BD_AMOEBA)
2723 level->amoeba_content = content_array[0][0][0];
2727 Warn("cannot load content for element '%d'", element);
2733 static int LoadLevel_CNT3(File *file, int chunk_size, struct LevelInfo *level)
2739 int chunk_size_expected;
2741 element = getMappedElement(getFile16BitBE(file));
2742 if (!IS_ENVELOPE(element))
2743 element = EL_ENVELOPE_1;
2745 envelope_nr = element - EL_ENVELOPE_1;
2747 envelope_len = getFile16BitBE(file);
2749 level->envelope[envelope_nr].xsize = getFile8Bit(file);
2750 level->envelope[envelope_nr].ysize = getFile8Bit(file);
2752 ReadUnusedBytesFromFile(file, LEVEL_CHUNK_CNT3_UNUSED);
2754 chunk_size_expected = LEVEL_CHUNK_CNT3_SIZE(envelope_len);
2755 if (chunk_size_expected != chunk_size)
2757 ReadUnusedBytesFromFile(file, chunk_size - LEVEL_CHUNK_CNT3_HEADER);
2758 return chunk_size_expected;
2761 for (i = 0; i < envelope_len; i++)
2762 level->envelope[envelope_nr].text[i] = getFile8Bit(file);
2767 static int LoadLevel_CUS1(File *file, int chunk_size, struct LevelInfo *level)
2769 int num_changed_custom_elements = getFile16BitBE(file);
2770 int chunk_size_expected = 2 + num_changed_custom_elements * 6;
2773 if (chunk_size_expected != chunk_size)
2775 ReadUnusedBytesFromFile(file, chunk_size - 2);
2776 return chunk_size_expected;
2779 for (i = 0; i < num_changed_custom_elements; i++)
2781 int element = getMappedElement(getFile16BitBE(file));
2782 int properties = getFile32BitBE(file);
2784 if (IS_CUSTOM_ELEMENT(element))
2785 element_info[element].properties[EP_BITFIELD_BASE_NR] = properties;
2787 Warn("invalid custom element number %d", element);
2789 // older game versions that wrote level files with CUS1 chunks used
2790 // different default push delay values (not yet stored in level file)
2791 element_info[element].push_delay_fixed = 2;
2792 element_info[element].push_delay_random = 8;
2795 level->file_has_custom_elements = TRUE;
2800 static int LoadLevel_CUS2(File *file, int chunk_size, struct LevelInfo *level)
2802 int num_changed_custom_elements = getFile16BitBE(file);
2803 int chunk_size_expected = 2 + num_changed_custom_elements * 4;
2806 if (chunk_size_expected != chunk_size)
2808 ReadUnusedBytesFromFile(file, chunk_size - 2);
2809 return chunk_size_expected;
2812 for (i = 0; i < num_changed_custom_elements; i++)
2814 int element = getMappedElement(getFile16BitBE(file));
2815 int custom_target_element = getMappedElement(getFile16BitBE(file));
2817 if (IS_CUSTOM_ELEMENT(element))
2818 element_info[element].change->target_element = custom_target_element;
2820 Warn("invalid custom element number %d", element);
2823 level->file_has_custom_elements = TRUE;
2828 static int LoadLevel_CUS3(File *file, int chunk_size, struct LevelInfo *level)
2830 int num_changed_custom_elements = getFile16BitBE(file);
2831 int chunk_size_expected = LEVEL_CHUNK_CUS3_SIZE(num_changed_custom_elements);
2834 if (chunk_size_expected != chunk_size)
2836 ReadUnusedBytesFromFile(file, chunk_size - 2);
2837 return chunk_size_expected;
2840 for (i = 0; i < num_changed_custom_elements; i++)
2842 int element = getMappedElement(getFile16BitBE(file));
2843 struct ElementInfo *ei = &element_info[element];
2844 unsigned int event_bits;
2846 if (!IS_CUSTOM_ELEMENT(element))
2848 Warn("invalid custom element number %d", element);
2850 element = EL_INTERNAL_DUMMY;
2853 for (j = 0; j < MAX_ELEMENT_NAME_LEN; j++)
2854 ei->description[j] = getFile8Bit(file);
2855 ei->description[MAX_ELEMENT_NAME_LEN] = 0;
2857 ei->properties[EP_BITFIELD_BASE_NR] = getFile32BitBE(file);
2859 // some free bytes for future properties and padding
2860 ReadUnusedBytesFromFile(file, 7);
2862 ei->use_gfx_element = getFile8Bit(file);
2863 ei->gfx_element_initial = getMappedElement(getFile16BitBE(file));
2865 ei->collect_score_initial = getFile8Bit(file);
2866 ei->collect_count_initial = getFile8Bit(file);
2868 ei->push_delay_fixed = getFile16BitBE(file);
2869 ei->push_delay_random = getFile16BitBE(file);
2870 ei->move_delay_fixed = getFile16BitBE(file);
2871 ei->move_delay_random = getFile16BitBE(file);
2873 ei->move_pattern = getFile16BitBE(file);
2874 ei->move_direction_initial = getFile8Bit(file);
2875 ei->move_stepsize = getFile8Bit(file);
2877 for (y = 0; y < 3; y++)
2878 for (x = 0; x < 3; x++)
2879 ei->content.e[x][y] = getMappedElement(getFile16BitBE(file));
2881 // bits 0 - 31 of "has_event[]"
2882 event_bits = getFile32BitBE(file);
2883 for (j = 0; j < MIN(NUM_CHANGE_EVENTS, 32); j++)
2884 if (event_bits & (1u << j))
2885 ei->change->has_event[j] = TRUE;
2887 ei->change->target_element = getMappedElement(getFile16BitBE(file));
2889 ei->change->delay_fixed = getFile16BitBE(file);
2890 ei->change->delay_random = getFile16BitBE(file);
2891 ei->change->delay_frames = getFile16BitBE(file);
2893 ei->change->initial_trigger_element= getMappedElement(getFile16BitBE(file));
2895 ei->change->explode = getFile8Bit(file);
2896 ei->change->use_target_content = getFile8Bit(file);
2897 ei->change->only_if_complete = getFile8Bit(file);
2898 ei->change->use_random_replace = getFile8Bit(file);
2900 ei->change->random_percentage = getFile8Bit(file);
2901 ei->change->replace_when = getFile8Bit(file);
2903 for (y = 0; y < 3; y++)
2904 for (x = 0; x < 3; x++)
2905 ei->change->target_content.e[x][y] =
2906 getMappedElement(getFile16BitBE(file));
2908 ei->slippery_type = getFile8Bit(file);
2910 // some free bytes for future properties and padding
2911 ReadUnusedBytesFromFile(file, LEVEL_CPART_CUS3_UNUSED);
2913 // mark that this custom element has been modified
2914 ei->modified_settings = TRUE;
2917 level->file_has_custom_elements = TRUE;
2922 static int LoadLevel_CUS4(File *file, int chunk_size, struct LevelInfo *level)
2924 struct ElementInfo *ei;
2925 int chunk_size_expected;
2929 // ---------- custom element base property values (96 bytes) ----------------
2931 element = getMappedElement(getFile16BitBE(file));
2933 if (!IS_CUSTOM_ELEMENT(element))
2935 Warn("invalid custom element number %d", element);
2937 ReadUnusedBytesFromFile(file, chunk_size - 2);
2942 ei = &element_info[element];
2944 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
2945 ei->description[i] = getFile8Bit(file);
2946 ei->description[MAX_ELEMENT_NAME_LEN] = 0;
2948 ei->properties[EP_BITFIELD_BASE_NR] = getFile32BitBE(file);
2950 ReadUnusedBytesFromFile(file, 4); // reserved for more base properties
2952 ei->num_change_pages = getFile8Bit(file);
2954 chunk_size_expected = LEVEL_CHUNK_CUS4_SIZE(ei->num_change_pages);
2955 if (chunk_size_expected != chunk_size)
2957 ReadUnusedBytesFromFile(file, chunk_size - 43);
2958 return chunk_size_expected;
2961 ei->ce_value_fixed_initial = getFile16BitBE(file);
2962 ei->ce_value_random_initial = getFile16BitBE(file);
2963 ei->use_last_ce_value = getFile8Bit(file);
2965 ei->use_gfx_element = getFile8Bit(file);
2966 ei->gfx_element_initial = getMappedElement(getFile16BitBE(file));
2968 ei->collect_score_initial = getFile8Bit(file);
2969 ei->collect_count_initial = getFile8Bit(file);
2971 ei->drop_delay_fixed = getFile8Bit(file);
2972 ei->push_delay_fixed = getFile8Bit(file);
2973 ei->drop_delay_random = getFile8Bit(file);
2974 ei->push_delay_random = getFile8Bit(file);
2975 ei->move_delay_fixed = getFile16BitBE(file);
2976 ei->move_delay_random = getFile16BitBE(file);
2978 // bits 0 - 15 of "move_pattern" ...
2979 ei->move_pattern = getFile16BitBE(file);
2980 ei->move_direction_initial = getFile8Bit(file);
2981 ei->move_stepsize = getFile8Bit(file);
2983 ei->slippery_type = getFile8Bit(file);
2985 for (y = 0; y < 3; y++)
2986 for (x = 0; x < 3; x++)
2987 ei->content.e[x][y] = getMappedElement(getFile16BitBE(file));
2989 ei->move_enter_element = getMappedElement(getFile16BitBE(file));
2990 ei->move_leave_element = getMappedElement(getFile16BitBE(file));
2991 ei->move_leave_type = getFile8Bit(file);
2993 // ... bits 16 - 31 of "move_pattern" (not nice, but downward compatible)
2994 ei->move_pattern |= (getFile16BitBE(file) << 16);
2996 ei->access_direction = getFile8Bit(file);
2998 ei->explosion_delay = getFile8Bit(file);
2999 ei->ignition_delay = getFile8Bit(file);
3000 ei->explosion_type = getFile8Bit(file);
3002 // some free bytes for future custom property values and padding
3003 ReadUnusedBytesFromFile(file, 1);
3005 // ---------- change page property values (48 bytes) ------------------------
3007 setElementChangePages(ei, ei->num_change_pages);
3009 for (i = 0; i < ei->num_change_pages; i++)
3011 struct ElementChangeInfo *change = &ei->change_page[i];
3012 unsigned int event_bits;
3014 // always start with reliable default values
3015 setElementChangeInfoToDefaults(change);
3017 // bits 0 - 31 of "has_event[]" ...
3018 event_bits = getFile32BitBE(file);
3019 for (j = 0; j < MIN(NUM_CHANGE_EVENTS, 32); j++)
3020 if (event_bits & (1u << j))
3021 change->has_event[j] = TRUE;
3023 change->target_element = getMappedElement(getFile16BitBE(file));
3025 change->delay_fixed = getFile16BitBE(file);
3026 change->delay_random = getFile16BitBE(file);
3027 change->delay_frames = getFile16BitBE(file);
3029 change->initial_trigger_element = getMappedElement(getFile16BitBE(file));
3031 change->explode = getFile8Bit(file);
3032 change->use_target_content = getFile8Bit(file);
3033 change->only_if_complete = getFile8Bit(file);
3034 change->use_random_replace = getFile8Bit(file);
3036 change->random_percentage = getFile8Bit(file);
3037 change->replace_when = getFile8Bit(file);
3039 for (y = 0; y < 3; y++)
3040 for (x = 0; x < 3; x++)
3041 change->target_content.e[x][y]= getMappedElement(getFile16BitBE(file));
3043 change->can_change = getFile8Bit(file);
3045 change->trigger_side = getFile8Bit(file);
3047 change->trigger_player = getFile8Bit(file);
3048 change->trigger_page = getFile8Bit(file);
3050 change->trigger_page = (change->trigger_page == CH_PAGE_ANY_FILE ?
3051 CH_PAGE_ANY : (1 << change->trigger_page));
3053 change->has_action = getFile8Bit(file);
3054 change->action_type = getFile8Bit(file);
3055 change->action_mode = getFile8Bit(file);
3056 change->action_arg = getFile16BitBE(file);
3058 // ... bits 32 - 39 of "has_event[]" (not nice, but downward compatible)
3059 event_bits = getFile8Bit(file);
3060 for (j = 32; j < NUM_CHANGE_EVENTS; j++)
3061 if (event_bits & (1u << (j - 32)))
3062 change->has_event[j] = TRUE;
3065 // mark this custom element as modified
3066 ei->modified_settings = TRUE;
3068 level->file_has_custom_elements = TRUE;
3073 static int LoadLevel_GRP1(File *file, int chunk_size, struct LevelInfo *level)
3075 struct ElementInfo *ei;
3076 struct ElementGroupInfo *group;
3080 element = getMappedElement(getFile16BitBE(file));
3082 if (!IS_GROUP_ELEMENT(element))
3084 Warn("invalid group element number %d", element);
3086 ReadUnusedBytesFromFile(file, chunk_size - 2);
3091 ei = &element_info[element];
3093 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
3094 ei->description[i] = getFile8Bit(file);
3095 ei->description[MAX_ELEMENT_NAME_LEN] = 0;
3097 group = element_info[element].group;
3099 group->num_elements = getFile8Bit(file);
3101 ei->use_gfx_element = getFile8Bit(file);
3102 ei->gfx_element_initial = getMappedElement(getFile16BitBE(file));
3104 group->choice_mode = getFile8Bit(file);
3106 // some free bytes for future values and padding
3107 ReadUnusedBytesFromFile(file, 3);
3109 for (i = 0; i < MAX_ELEMENTS_IN_GROUP; i++)
3110 group->element[i] = getMappedElement(getFile16BitBE(file));
3112 // mark this group element as modified
3113 element_info[element].modified_settings = TRUE;
3115 level->file_has_custom_elements = TRUE;
3120 static int LoadLevel_MicroChunk(File *file, struct LevelFileConfigInfo *conf,
3121 int element, int real_element)
3123 int micro_chunk_size = 0;
3124 int conf_type = getFile8Bit(file);
3125 int byte_mask = conf_type & CONF_MASK_BYTES;
3126 boolean element_found = FALSE;
3129 micro_chunk_size += 1;
3131 if (byte_mask == CONF_MASK_MULTI_BYTES)
3133 int num_bytes = getFile16BitBE(file);
3134 byte *buffer = checked_malloc(num_bytes);
3136 ReadBytesFromFile(file, buffer, num_bytes);
3138 for (i = 0; conf[i].data_type != -1; i++)
3140 if (conf[i].element == element &&
3141 conf[i].conf_type == conf_type)
3143 int data_type = conf[i].data_type;
3144 int num_entities = num_bytes / CONF_ENTITY_NUM_BYTES(data_type);
3145 int max_num_entities = conf[i].max_num_entities;
3147 if (num_entities > max_num_entities)
3149 Warn("truncating number of entities for element %d from %d to %d",
3150 element, num_entities, max_num_entities);
3152 num_entities = max_num_entities;
3155 if (num_entities == 0 && (data_type == TYPE_ELEMENT_LIST ||
3156 data_type == TYPE_CONTENT_LIST))
3158 // for element and content lists, zero entities are not allowed
3159 Warn("found empty list of entities for element %d", element);
3161 // do not set "num_entities" here to prevent reading behind buffer
3163 *(int *)(conf[i].num_entities) = 1; // at least one is required
3167 *(int *)(conf[i].num_entities) = num_entities;
3170 element_found = TRUE;
3172 if (data_type == TYPE_STRING)
3174 char *string = (char *)(conf[i].value);
3177 for (j = 0; j < max_num_entities; j++)
3178 string[j] = (j < num_entities ? buffer[j] : '\0');
3180 else if (data_type == TYPE_ELEMENT_LIST)
3182 int *element_array = (int *)(conf[i].value);
3185 for (j = 0; j < num_entities; j++)
3187 getMappedElement(CONF_ELEMENTS_ELEMENT(buffer, j));
3189 else if (data_type == TYPE_CONTENT_LIST)
3191 struct Content *content= (struct Content *)(conf[i].value);
3194 for (c = 0; c < num_entities; c++)
3195 for (y = 0; y < 3; y++)
3196 for (x = 0; x < 3; x++)
3197 content[c].e[x][y] =
3198 getMappedElement(CONF_CONTENTS_ELEMENT(buffer, c, x, y));
3201 element_found = FALSE;
3207 checked_free(buffer);
3209 micro_chunk_size += 2 + num_bytes;
3211 else // constant size configuration data (1, 2 or 4 bytes)
3213 int value = (byte_mask == CONF_MASK_1_BYTE ? getFile8Bit (file) :
3214 byte_mask == CONF_MASK_2_BYTE ? getFile16BitBE(file) :
3215 byte_mask == CONF_MASK_4_BYTE ? getFile32BitBE(file) : 0);
3217 for (i = 0; conf[i].data_type != -1; i++)
3219 if (conf[i].element == element &&
3220 conf[i].conf_type == conf_type)
3222 int data_type = conf[i].data_type;
3224 if (data_type == TYPE_ELEMENT)
3225 value = getMappedElement(value);
3227 if (data_type == TYPE_BOOLEAN)
3228 *(boolean *)(conf[i].value) = (value ? TRUE : FALSE);
3230 *(int *) (conf[i].value) = value;
3232 element_found = TRUE;
3238 micro_chunk_size += CONF_VALUE_NUM_BYTES(byte_mask);
3243 char *error_conf_chunk_bytes =
3244 (byte_mask == CONF_MASK_1_BYTE ? "CONF_VALUE_8_BIT" :
3245 byte_mask == CONF_MASK_2_BYTE ? "CONF_VALUE_16_BIT" :
3246 byte_mask == CONF_MASK_4_BYTE ? "CONF_VALUE_32_BIT" :"CONF_VALUE_BYTES");
3247 int error_conf_chunk_token = conf_type & CONF_MASK_TOKEN;
3248 int error_element = real_element;
3250 Warn("cannot load micro chunk '%s(%d)' value for element %d ['%s']",
3251 error_conf_chunk_bytes, error_conf_chunk_token,
3252 error_element, EL_NAME(error_element));
3255 return micro_chunk_size;
3258 static int LoadLevel_INFO(File *file, int chunk_size, struct LevelInfo *level)
3260 int real_chunk_size = 0;
3262 li = *level; // copy level data into temporary buffer
3264 while (!checkEndOfFile(file))
3266 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_INFO, -1, -1);
3268 if (real_chunk_size >= chunk_size)
3272 *level = li; // copy temporary buffer back to level data
3274 return real_chunk_size;
3277 static int LoadLevel_CONF(File *file, int chunk_size, struct LevelInfo *level)
3279 int real_chunk_size = 0;
3281 li = *level; // copy level data into temporary buffer
3283 while (!checkEndOfFile(file))
3285 int element = getMappedElement(getFile16BitBE(file));
3287 real_chunk_size += 2;
3288 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_CONF,
3290 if (real_chunk_size >= chunk_size)
3294 *level = li; // copy temporary buffer back to level data
3296 return real_chunk_size;
3299 static int LoadLevel_ELEM(File *file, int chunk_size, struct LevelInfo *level)
3301 int real_chunk_size = 0;
3303 li = *level; // copy level data into temporary buffer
3305 while (!checkEndOfFile(file))
3307 int element = getMappedElement(getFile16BitBE(file));
3309 real_chunk_size += 2;
3310 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_ELEM,
3312 if (real_chunk_size >= chunk_size)
3316 *level = li; // copy temporary buffer back to level data
3318 return real_chunk_size;
3321 static int LoadLevel_NOTE(File *file, int chunk_size, struct LevelInfo *level)
3323 int element = getMappedElement(getFile16BitBE(file));
3324 int envelope_nr = element - EL_ENVELOPE_1;
3325 int real_chunk_size = 2;
3327 xx_envelope = level->envelope[envelope_nr]; // copy into temporary buffer
3329 while (!checkEndOfFile(file))
3331 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_NOTE,
3334 if (real_chunk_size >= chunk_size)
3338 level->envelope[envelope_nr] = xx_envelope; // copy from temporary buffer
3340 return real_chunk_size;
3343 static int LoadLevel_CUSX(File *file, int chunk_size, struct LevelInfo *level)
3345 int element = getMappedElement(getFile16BitBE(file));
3346 int real_chunk_size = 2;
3347 struct ElementInfo *ei = &element_info[element];
3350 xx_ei = *ei; // copy element data into temporary buffer
3352 xx_ei.num_change_pages = -1;
3354 while (!checkEndOfFile(file))
3356 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_CUSX_base,
3358 if (xx_ei.num_change_pages != -1)
3361 if (real_chunk_size >= chunk_size)
3367 if (ei->num_change_pages == -1)
3369 Warn("LoadLevel_CUSX(): missing 'num_change_pages' for '%s'",
3372 ei->num_change_pages = 1;
3374 setElementChangePages(ei, 1);
3375 setElementChangeInfoToDefaults(ei->change);
3377 return real_chunk_size;
3380 // initialize number of change pages stored for this custom element
3381 setElementChangePages(ei, ei->num_change_pages);
3382 for (i = 0; i < ei->num_change_pages; i++)
3383 setElementChangeInfoToDefaults(&ei->change_page[i]);
3385 // start with reading properties for the first change page
3386 xx_current_change_page = 0;
3388 while (!checkEndOfFile(file))
3390 // level file might contain invalid change page number
3391 if (xx_current_change_page >= ei->num_change_pages)
3394 struct ElementChangeInfo *change = &ei->change_page[xx_current_change_page];
3396 xx_change = *change; // copy change data into temporary buffer
3398 resetEventBits(); // reset bits; change page might have changed
3400 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_CUSX_change,
3403 *change = xx_change;
3405 setEventFlagsFromEventBits(change);
3407 if (real_chunk_size >= chunk_size)
3411 level->file_has_custom_elements = TRUE;
3413 return real_chunk_size;
3416 static int LoadLevel_GRPX(File *file, int chunk_size, struct LevelInfo *level)
3418 int element = getMappedElement(getFile16BitBE(file));
3419 int real_chunk_size = 2;
3420 struct ElementInfo *ei = &element_info[element];
3421 struct ElementGroupInfo *group = ei->group;
3426 xx_ei = *ei; // copy element data into temporary buffer
3427 xx_group = *group; // copy group data into temporary buffer
3429 while (!checkEndOfFile(file))
3431 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_GRPX,
3434 if (real_chunk_size >= chunk_size)
3441 level->file_has_custom_elements = TRUE;
3443 return real_chunk_size;
3446 static int LoadLevel_EMPX(File *file, int chunk_size, struct LevelInfo *level)
3448 int element = getMappedElement(getFile16BitBE(file));
3449 int real_chunk_size = 2;
3450 struct ElementInfo *ei = &element_info[element];
3452 xx_ei = *ei; // copy element data into temporary buffer
3454 while (!checkEndOfFile(file))
3456 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_EMPX,
3459 if (real_chunk_size >= chunk_size)
3465 level->file_has_custom_elements = TRUE;
3467 return real_chunk_size;
3470 static void LoadLevelFromFileInfo_RND(struct LevelInfo *level,
3471 struct LevelFileInfo *level_file_info,
3472 boolean level_info_only)
3474 char *filename = level_file_info->filename;
3475 char cookie[MAX_LINE_LEN];
3476 char chunk_name[CHUNK_ID_LEN + 1];
3480 if (!(file = openFile(filename, MODE_READ)))
3482 level->no_valid_file = TRUE;
3483 level->no_level_file = TRUE;
3485 if (level_info_only)
3488 Warn("cannot read level '%s' -- using empty level", filename);
3490 if (!setup.editor.use_template_for_new_levels)
3493 // if level file not found, try to initialize level data from template
3494 filename = getGlobalLevelTemplateFilename();
3496 if (!(file = openFile(filename, MODE_READ)))
3499 // default: for empty levels, use level template for custom elements
3500 level->use_custom_template = TRUE;
3502 level->no_valid_file = FALSE;
3505 getFileChunkBE(file, chunk_name, NULL);
3506 if (strEqual(chunk_name, "RND1"))
3508 getFile32BitBE(file); // not used
3510 getFileChunkBE(file, chunk_name, NULL);
3511 if (!strEqual(chunk_name, "CAVE"))
3513 level->no_valid_file = TRUE;
3515 Warn("unknown format of level file '%s'", filename);
3522 else // check for pre-2.0 file format with cookie string
3524 strcpy(cookie, chunk_name);
3525 if (getStringFromFile(file, &cookie[4], MAX_LINE_LEN - 4) == NULL)
3527 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
3528 cookie[strlen(cookie) - 1] = '\0';
3530 if (!checkCookieString(cookie, LEVEL_COOKIE_TMPL))
3532 level->no_valid_file = TRUE;
3534 Warn("unknown format of level file '%s'", filename);
3541 if ((level->file_version = getFileVersionFromCookieString(cookie)) == -1)
3543 level->no_valid_file = TRUE;
3545 Warn("unsupported version of level file '%s'", filename);
3552 // pre-2.0 level files have no game version, so use file version here
3553 level->game_version = level->file_version;
3556 if (level->file_version < FILE_VERSION_1_2)
3558 // level files from versions before 1.2.0 without chunk structure
3559 LoadLevel_HEAD(file, LEVEL_CHUNK_HEAD_SIZE, level);
3560 LoadLevel_BODY(file, level->fieldx * level->fieldy, level);
3568 int (*loader)(File *, int, struct LevelInfo *);
3572 { "VERS", LEVEL_CHUNK_VERS_SIZE, LoadLevel_VERS },
3573 { "DATE", LEVEL_CHUNK_DATE_SIZE, LoadLevel_DATE },
3574 { "HEAD", LEVEL_CHUNK_HEAD_SIZE, LoadLevel_HEAD },
3575 { "NAME", LEVEL_CHUNK_NAME_SIZE, LoadLevel_NAME },
3576 { "AUTH", LEVEL_CHUNK_AUTH_SIZE, LoadLevel_AUTH },
3577 { "INFO", -1, LoadLevel_INFO },
3578 { "BODY", -1, LoadLevel_BODY },
3579 { "CONT", -1, LoadLevel_CONT },
3580 { "CNT2", LEVEL_CHUNK_CNT2_SIZE, LoadLevel_CNT2 },
3581 { "CNT3", -1, LoadLevel_CNT3 },
3582 { "CUS1", -1, LoadLevel_CUS1 },
3583 { "CUS2", -1, LoadLevel_CUS2 },
3584 { "CUS3", -1, LoadLevel_CUS3 },
3585 { "CUS4", -1, LoadLevel_CUS4 },
3586 { "GRP1", -1, LoadLevel_GRP1 },
3587 { "CONF", -1, LoadLevel_CONF },
3588 { "ELEM", -1, LoadLevel_ELEM },
3589 { "NOTE", -1, LoadLevel_NOTE },
3590 { "CUSX", -1, LoadLevel_CUSX },
3591 { "GRPX", -1, LoadLevel_GRPX },
3592 { "EMPX", -1, LoadLevel_EMPX },
3597 while (getFileChunkBE(file, chunk_name, &chunk_size))
3601 while (chunk_info[i].name != NULL &&
3602 !strEqual(chunk_name, chunk_info[i].name))
3605 if (chunk_info[i].name == NULL)
3607 Warn("unknown chunk '%s' in level file '%s'",
3608 chunk_name, filename);
3610 ReadUnusedBytesFromFile(file, chunk_size);
3612 else if (chunk_info[i].size != -1 &&
3613 chunk_info[i].size != chunk_size)
3615 Warn("wrong size (%d) of chunk '%s' in level file '%s'",
3616 chunk_size, chunk_name, filename);
3618 ReadUnusedBytesFromFile(file, chunk_size);
3622 // call function to load this level chunk
3623 int chunk_size_expected =
3624 (chunk_info[i].loader)(file, chunk_size, level);
3626 if (chunk_size_expected < 0)
3628 Warn("error reading chunk '%s' in level file '%s'",
3629 chunk_name, filename);
3634 // the size of some chunks cannot be checked before reading other
3635 // chunks first (like "HEAD" and "BODY") that contain some header
3636 // information, so check them here
3637 if (chunk_size_expected != chunk_size)
3639 Warn("wrong size (%d) of chunk '%s' in level file '%s'",
3640 chunk_size, chunk_name, filename);
3652 // ----------------------------------------------------------------------------
3653 // functions for loading EM level
3654 // ----------------------------------------------------------------------------
3656 static void CopyNativeLevel_RND_to_EM(struct LevelInfo *level)
3658 static int ball_xy[8][2] =
3669 struct LevelInfo_EM *level_em = level->native_em_level;
3670 struct CAVE *cav = level_em->cav;
3673 cav->width = MIN(level->fieldx, MAX_PLAYFIELD_WIDTH);
3674 cav->height = MIN(level->fieldy, MAX_PLAYFIELD_HEIGHT);
3676 cav->time_seconds = level->time;
3677 cav->gems_needed = level->gems_needed;
3679 cav->emerald_score = level->score[SC_EMERALD];
3680 cav->diamond_score = level->score[SC_DIAMOND];
3681 cav->alien_score = level->score[SC_ROBOT];
3682 cav->tank_score = level->score[SC_SPACESHIP];
3683 cav->bug_score = level->score[SC_BUG];
3684 cav->eater_score = level->score[SC_YAMYAM];
3685 cav->nut_score = level->score[SC_NUT];
3686 cav->dynamite_score = level->score[SC_DYNAMITE];
3687 cav->key_score = level->score[SC_KEY];
3688 cav->exit_score = level->score[SC_TIME_BONUS];
3690 cav->num_eater_arrays = level->num_yamyam_contents;
3692 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
3693 for (y = 0; y < 3; y++)
3694 for (x = 0; x < 3; x++)
3695 cav->eater_array[i][y * 3 + x] =
3696 map_element_RND_to_EM_cave(level->yamyam_content[i].e[x][y]);
3698 cav->amoeba_time = level->amoeba_speed;
3699 cav->wonderwall_time = level->time_magic_wall;
3700 cav->wheel_time = level->time_wheel;
3702 cav->android_move_time = level->android_move_time;
3703 cav->android_clone_time = level->android_clone_time;
3704 cav->ball_random = level->ball_random;
3705 cav->ball_active = level->ball_active_initial;
3706 cav->ball_time = level->ball_time;
3707 cav->num_ball_arrays = level->num_ball_contents;
3709 cav->lenses_score = level->lenses_score;
3710 cav->magnify_score = level->magnify_score;
3711 cav->slurp_score = level->slurp_score;
3713 cav->lenses_time = level->lenses_time;
3714 cav->magnify_time = level->magnify_time;
3716 cav->wind_time = 9999;
3717 cav->wind_direction =
3718 map_direction_RND_to_EM(level->wind_direction_initial);
3720 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
3721 for (j = 0; j < 8; j++)
3722 cav->ball_array[i][j] =
3723 map_element_RND_to_EM_cave(level->ball_content[i].
3724 e[ball_xy[j][0]][ball_xy[j][1]]);
3726 map_android_clone_elements_RND_to_EM(level);
3728 // first fill the complete playfield with the empty space element
3729 for (y = 0; y < EM_MAX_CAVE_HEIGHT; y++)
3730 for (x = 0; x < EM_MAX_CAVE_WIDTH; x++)
3731 cav->cave[x][y] = Cblank;
3733 // then copy the real level contents from level file into the playfield
3734 for (y = 0; y < cav->height; y++) for (x = 0; x < cav->width; x++)
3736 int new_element = map_element_RND_to_EM_cave(level->field[x][y]);
3738 if (level->field[x][y] == EL_AMOEBA_DEAD)
3739 new_element = map_element_RND_to_EM_cave(EL_AMOEBA_WET);
3741 cav->cave[x][y] = new_element;
3744 for (i = 0; i < MAX_PLAYERS; i++)
3746 cav->player_x[i] = -1;
3747 cav->player_y[i] = -1;
3750 // initialize player positions and delete players from the playfield
3751 for (y = 0; y < cav->height; y++) for (x = 0; x < cav->width; x++)
3753 if (IS_PLAYER_ELEMENT(level->field[x][y]))
3755 int player_nr = GET_PLAYER_NR(level->field[x][y]);
3757 cav->player_x[player_nr] = x;
3758 cav->player_y[player_nr] = y;
3760 cav->cave[x][y] = map_element_RND_to_EM_cave(EL_EMPTY);
3765 static void CopyNativeLevel_EM_to_RND(struct LevelInfo *level)
3767 static int ball_xy[8][2] =
3778 struct LevelInfo_EM *level_em = level->native_em_level;
3779 struct CAVE *cav = level_em->cav;
3782 level->fieldx = MIN(cav->width, MAX_LEV_FIELDX);
3783 level->fieldy = MIN(cav->height, MAX_LEV_FIELDY);
3785 level->time = cav->time_seconds;
3786 level->gems_needed = cav->gems_needed;
3788 sprintf(level->name, "Level %d", level->file_info.nr);
3790 level->score[SC_EMERALD] = cav->emerald_score;
3791 level->score[SC_DIAMOND] = cav->diamond_score;
3792 level->score[SC_ROBOT] = cav->alien_score;
3793 level->score[SC_SPACESHIP] = cav->tank_score;
3794 level->score[SC_BUG] = cav->bug_score;
3795 level->score[SC_YAMYAM] = cav->eater_score;
3796 level->score[SC_NUT] = cav->nut_score;
3797 level->score[SC_DYNAMITE] = cav->dynamite_score;
3798 level->score[SC_KEY] = cav->key_score;
3799 level->score[SC_TIME_BONUS] = cav->exit_score;
3801 level->num_yamyam_contents = cav->num_eater_arrays;
3803 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
3804 for (y = 0; y < 3; y++)
3805 for (x = 0; x < 3; x++)
3806 level->yamyam_content[i].e[x][y] =
3807 map_element_EM_to_RND_cave(cav->eater_array[i][y * 3 + x]);
3809 level->amoeba_speed = cav->amoeba_time;
3810 level->time_magic_wall = cav->wonderwall_time;
3811 level->time_wheel = cav->wheel_time;
3813 level->android_move_time = cav->android_move_time;
3814 level->android_clone_time = cav->android_clone_time;
3815 level->ball_random = cav->ball_random;
3816 level->ball_active_initial = cav->ball_active;
3817 level->ball_time = cav->ball_time;
3818 level->num_ball_contents = cav->num_ball_arrays;
3820 level->lenses_score = cav->lenses_score;
3821 level->magnify_score = cav->magnify_score;
3822 level->slurp_score = cav->slurp_score;
3824 level->lenses_time = cav->lenses_time;
3825 level->magnify_time = cav->magnify_time;
3827 level->wind_direction_initial =
3828 map_direction_EM_to_RND(cav->wind_direction);
3830 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
3831 for (j = 0; j < 8; j++)
3832 level->ball_content[i].e[ball_xy[j][0]][ball_xy[j][1]] =
3833 map_element_EM_to_RND_cave(cav->ball_array[i][j]);
3835 map_android_clone_elements_EM_to_RND(level);
3837 // convert the playfield (some elements need special treatment)
3838 for (y = 0; y < level->fieldy; y++) for (x = 0; x < level->fieldx; x++)
3840 int new_element = map_element_EM_to_RND_cave(cav->cave[x][y]);
3842 if (new_element == EL_AMOEBA_WET && level->amoeba_speed == 0)
3843 new_element = EL_AMOEBA_DEAD;
3845 level->field[x][y] = new_element;
3848 for (i = 0; i < MAX_PLAYERS; i++)
3850 // in case of all players set to the same field, use the first player
3851 int nr = MAX_PLAYERS - i - 1;
3852 int jx = cav->player_x[nr];
3853 int jy = cav->player_y[nr];
3855 if (jx != -1 && jy != -1)
3856 level->field[jx][jy] = EL_PLAYER_1 + nr;
3859 // time score is counted for each 10 seconds left in Emerald Mine levels
3860 level->time_score_base = 10;
3864 // ----------------------------------------------------------------------------
3865 // functions for loading SP level
3866 // ----------------------------------------------------------------------------
3868 static void CopyNativeLevel_RND_to_SP(struct LevelInfo *level)
3870 struct LevelInfo_SP *level_sp = level->native_sp_level;
3871 LevelInfoType *header = &level_sp->header;
3874 level_sp->width = level->fieldx;
3875 level_sp->height = level->fieldy;
3877 for (x = 0; x < level->fieldx; x++)
3878 for (y = 0; y < level->fieldy; y++)
3879 level_sp->playfield[x][y] = map_element_RND_to_SP(level->field[x][y]);
3881 header->InitialGravity = (level->initial_player_gravity[0] ? 1 : 0);
3883 for (i = 0; i < SP_LEVEL_NAME_LEN; i++)
3884 header->LevelTitle[i] = level->name[i];
3885 // !!! NO STRING TERMINATION IN SUPAPLEX VB CODE YET -- FIX THIS !!!
3887 header->InfotronsNeeded = level->gems_needed;
3889 header->SpecialPortCount = 0;
3891 for (x = 0; x < level->fieldx; x++) for (y = 0; y < level->fieldy; y++)
3893 boolean gravity_port_found = FALSE;
3894 boolean gravity_port_valid = FALSE;
3895 int gravity_port_flag;
3896 int gravity_port_base_element;
3897 int element = level->field[x][y];
3899 if (element >= EL_SP_GRAVITY_ON_PORT_RIGHT &&
3900 element <= EL_SP_GRAVITY_ON_PORT_UP)
3902 gravity_port_found = TRUE;
3903 gravity_port_valid = TRUE;
3904 gravity_port_flag = 1;
3905 gravity_port_base_element = EL_SP_GRAVITY_ON_PORT_RIGHT;
3907 else if (element >= EL_SP_GRAVITY_OFF_PORT_RIGHT &&
3908 element <= EL_SP_GRAVITY_OFF_PORT_UP)
3910 gravity_port_found = TRUE;
3911 gravity_port_valid = TRUE;
3912 gravity_port_flag = 0;
3913 gravity_port_base_element = EL_SP_GRAVITY_OFF_PORT_RIGHT;
3915 else if (element >= EL_SP_GRAVITY_PORT_RIGHT &&
3916 element <= EL_SP_GRAVITY_PORT_UP)
3918 // change R'n'D style gravity inverting special port to normal port
3919 // (there are no gravity inverting ports in native Supaplex engine)
3921 gravity_port_found = TRUE;
3922 gravity_port_valid = FALSE;
3923 gravity_port_base_element = EL_SP_GRAVITY_PORT_RIGHT;
3926 if (gravity_port_found)
3928 if (gravity_port_valid &&
3929 header->SpecialPortCount < SP_MAX_SPECIAL_PORTS)
3931 SpecialPortType *port = &header->SpecialPort[header->SpecialPortCount];
3933 port->PortLocation = (y * level->fieldx + x) * 2;
3934 port->Gravity = gravity_port_flag;
3936 element += EL_SP_GRAVITY_PORT_RIGHT - gravity_port_base_element;
3938 header->SpecialPortCount++;
3942 // change special gravity port to normal port
3944 element += EL_SP_PORT_RIGHT - gravity_port_base_element;
3947 level_sp->playfield[x][y] = element - EL_SP_START;
3952 static void CopyNativeLevel_SP_to_RND(struct LevelInfo *level)
3954 struct LevelInfo_SP *level_sp = level->native_sp_level;
3955 LevelInfoType *header = &level_sp->header;
3956 boolean num_invalid_elements = 0;
3959 level->fieldx = level_sp->width;
3960 level->fieldy = level_sp->height;
3962 for (x = 0; x < level->fieldx; x++)
3964 for (y = 0; y < level->fieldy; y++)
3966 int element_old = level_sp->playfield[x][y];
3967 int element_new = getMappedElement(map_element_SP_to_RND(element_old));
3969 if (element_new == EL_UNKNOWN)
3971 num_invalid_elements++;
3973 Debug("level:native:SP", "invalid element %d at position %d, %d",
3977 level->field[x][y] = element_new;
3981 if (num_invalid_elements > 0)
3982 Warn("found %d invalid elements%s", num_invalid_elements,
3983 (!options.debug ? " (use '--debug' for more details)" : ""));
3985 for (i = 0; i < MAX_PLAYERS; i++)
3986 level->initial_player_gravity[i] =
3987 (header->InitialGravity == 1 ? TRUE : FALSE);
3989 // skip leading spaces
3990 for (i = 0; i < SP_LEVEL_NAME_LEN; i++)
3991 if (header->LevelTitle[i] != ' ')
3995 for (j = 0; i < SP_LEVEL_NAME_LEN; i++, j++)
3996 level->name[j] = header->LevelTitle[i];
3997 level->name[j] = '\0';
3999 // cut trailing spaces
4001 if (level->name[j - 1] == ' ' && level->name[j] == '\0')
4002 level->name[j - 1] = '\0';
4004 level->gems_needed = header->InfotronsNeeded;
4006 for (i = 0; i < header->SpecialPortCount; i++)
4008 SpecialPortType *port = &header->SpecialPort[i];
4009 int port_location = port->PortLocation;
4010 int gravity = port->Gravity;
4011 int port_x, port_y, port_element;
4013 port_x = (port_location / 2) % level->fieldx;
4014 port_y = (port_location / 2) / level->fieldx;
4016 if (port_x < 0 || port_x >= level->fieldx ||
4017 port_y < 0 || port_y >= level->fieldy)
4019 Warn("special port position (%d, %d) out of bounds", port_x, port_y);
4024 port_element = level->field[port_x][port_y];
4026 if (port_element < EL_SP_GRAVITY_PORT_RIGHT ||
4027 port_element > EL_SP_GRAVITY_PORT_UP)
4029 Warn("no special port at position (%d, %d)", port_x, port_y);
4034 // change previous (wrong) gravity inverting special port to either
4035 // gravity enabling special port or gravity disabling special port
4036 level->field[port_x][port_y] +=
4037 (gravity == 1 ? EL_SP_GRAVITY_ON_PORT_RIGHT :
4038 EL_SP_GRAVITY_OFF_PORT_RIGHT) - EL_SP_GRAVITY_PORT_RIGHT;
4041 // change special gravity ports without database entries to normal ports
4042 for (x = 0; x < level->fieldx; x++)
4043 for (y = 0; y < level->fieldy; y++)
4044 if (level->field[x][y] >= EL_SP_GRAVITY_PORT_RIGHT &&
4045 level->field[x][y] <= EL_SP_GRAVITY_PORT_UP)
4046 level->field[x][y] += EL_SP_PORT_RIGHT - EL_SP_GRAVITY_PORT_RIGHT;
4048 level->time = 0; // no time limit
4049 level->amoeba_speed = 0;
4050 level->time_magic_wall = 0;
4051 level->time_wheel = 0;
4052 level->amoeba_content = EL_EMPTY;
4054 // original Supaplex does not use score values -- rate by playing time
4055 for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
4056 level->score[i] = 0;
4058 level->rate_time_over_score = TRUE;
4060 // there are no yamyams in supaplex levels
4061 for (i = 0; i < level->num_yamyam_contents; i++)
4062 for (x = 0; x < 3; x++)
4063 for (y = 0; y < 3; y++)
4064 level->yamyam_content[i].e[x][y] = EL_EMPTY;
4067 static void CopyNativeTape_RND_to_SP(struct LevelInfo *level)
4069 struct LevelInfo_SP *level_sp = level->native_sp_level;
4070 struct DemoInfo_SP *demo = &level_sp->demo;
4073 // always start with reliable default values
4074 demo->is_available = FALSE;
4077 if (TAPE_IS_EMPTY(tape))
4080 demo->level_nr = tape.level_nr; // (currently not used)
4082 level_sp->header.DemoRandomSeed = tape.random_seed;
4086 for (i = 0; i < tape.length; i++)
4088 int demo_action = map_key_RND_to_SP(tape.pos[i].action[0]);
4089 int demo_repeat = tape.pos[i].delay;
4090 int demo_entries = (demo_repeat + 15) / 16;
4092 if (demo->length + demo_entries >= SP_MAX_TAPE_LEN)
4094 Warn("tape truncated: size exceeds maximum SP demo size %d",
4100 for (j = 0; j < demo_repeat / 16; j++)
4101 demo->data[demo->length++] = 0xf0 | demo_action;
4103 if (demo_repeat % 16)
4104 demo->data[demo->length++] = ((demo_repeat % 16 - 1) << 4) | demo_action;
4107 demo->is_available = TRUE;
4110 static void setTapeInfoToDefaults(void);
4112 static void CopyNativeTape_SP_to_RND(struct LevelInfo *level)
4114 struct LevelInfo_SP *level_sp = level->native_sp_level;
4115 struct DemoInfo_SP *demo = &level_sp->demo;
4116 char *filename = level->file_info.filename;
4119 // always start with reliable default values
4120 setTapeInfoToDefaults();
4122 if (!demo->is_available)
4125 tape.level_nr = demo->level_nr; // (currently not used)
4126 tape.random_seed = level_sp->header.DemoRandomSeed;
4128 TapeSetDateFromEpochSeconds(getFileTimestampEpochSeconds(filename));
4131 tape.pos[tape.counter].delay = 0;
4133 for (i = 0; i < demo->length; i++)
4135 int demo_action = demo->data[i] & 0x0f;
4136 int demo_repeat = (demo->data[i] & 0xf0) >> 4;
4137 int tape_action = map_key_SP_to_RND(demo_action);
4138 int tape_repeat = demo_repeat + 1;
4139 byte action[MAX_TAPE_ACTIONS] = { tape_action };
4140 boolean success = 0;
4143 for (j = 0; j < tape_repeat; j++)
4144 success = TapeAddAction(action);
4148 Warn("SP demo truncated: size exceeds maximum tape size %d",
4155 TapeHaltRecording();
4159 // ----------------------------------------------------------------------------
4160 // functions for loading MM level
4161 // ----------------------------------------------------------------------------
4163 static void CopyNativeLevel_RND_to_MM(struct LevelInfo *level)
4165 struct LevelInfo_MM *level_mm = level->native_mm_level;
4168 level_mm->fieldx = MIN(level->fieldx, MM_MAX_PLAYFIELD_WIDTH);
4169 level_mm->fieldy = MIN(level->fieldy, MM_MAX_PLAYFIELD_HEIGHT);
4171 level_mm->time = level->time;
4172 level_mm->kettles_needed = level->gems_needed;
4173 level_mm->auto_count_kettles = level->auto_count_gems;
4175 level_mm->mm_laser_red = level->mm_laser_red;
4176 level_mm->mm_laser_green = level->mm_laser_green;
4177 level_mm->mm_laser_blue = level->mm_laser_blue;
4179 level_mm->df_laser_red = level->df_laser_red;
4180 level_mm->df_laser_green = level->df_laser_green;
4181 level_mm->df_laser_blue = level->df_laser_blue;
4183 strcpy(level_mm->name, level->name);
4184 strcpy(level_mm->author, level->author);
4186 level_mm->score[SC_EMERALD] = level->score[SC_EMERALD];
4187 level_mm->score[SC_PACMAN] = level->score[SC_PACMAN];
4188 level_mm->score[SC_KEY] = level->score[SC_KEY];
4189 level_mm->score[SC_TIME_BONUS] = level->score[SC_TIME_BONUS];
4190 level_mm->score[SC_ELEM_BONUS] = level->score[SC_ELEM_BONUS];
4192 level_mm->amoeba_speed = level->amoeba_speed;
4193 level_mm->time_fuse = level->mm_time_fuse;
4194 level_mm->time_bomb = level->mm_time_bomb;
4195 level_mm->time_ball = level->mm_time_ball;
4196 level_mm->time_block = level->mm_time_block;
4198 level_mm->num_ball_contents = level->num_mm_ball_contents;
4199 level_mm->ball_choice_mode = level->mm_ball_choice_mode;
4200 level_mm->rotate_ball_content = level->rotate_mm_ball_content;
4201 level_mm->explode_ball = level->explode_mm_ball;
4203 for (i = 0; i < level->num_mm_ball_contents; i++)
4204 level_mm->ball_content[i] =
4205 map_element_RND_to_MM(level->mm_ball_content[i]);
4207 for (x = 0; x < level->fieldx; x++)
4208 for (y = 0; y < level->fieldy; y++)
4210 level_mm->field[x][y] = map_element_RND_to_MM(level->field[x][y]);
4213 static void CopyNativeLevel_MM_to_RND(struct LevelInfo *level)
4215 struct LevelInfo_MM *level_mm = level->native_mm_level;
4218 level->fieldx = MIN(level_mm->fieldx, MAX_LEV_FIELDX);
4219 level->fieldy = MIN(level_mm->fieldy, MAX_LEV_FIELDY);
4221 level->time = level_mm->time;
4222 level->gems_needed = level_mm->kettles_needed;
4223 level->auto_count_gems = level_mm->auto_count_kettles;
4225 level->mm_laser_red = level_mm->mm_laser_red;
4226 level->mm_laser_green = level_mm->mm_laser_green;
4227 level->mm_laser_blue = level_mm->mm_laser_blue;
4229 level->df_laser_red = level_mm->df_laser_red;
4230 level->df_laser_green = level_mm->df_laser_green;
4231 level->df_laser_blue = level_mm->df_laser_blue;
4233 strcpy(level->name, level_mm->name);
4235 // only overwrite author from 'levelinfo.conf' if author defined in level
4236 if (!strEqual(level_mm->author, ANONYMOUS_NAME))
4237 strcpy(level->author, level_mm->author);
4239 level->score[SC_EMERALD] = level_mm->score[SC_EMERALD];
4240 level->score[SC_PACMAN] = level_mm->score[SC_PACMAN];
4241 level->score[SC_KEY] = level_mm->score[SC_KEY];
4242 level->score[SC_TIME_BONUS] = level_mm->score[SC_TIME_BONUS];
4243 level->score[SC_ELEM_BONUS] = level_mm->score[SC_ELEM_BONUS];
4245 level->amoeba_speed = level_mm->amoeba_speed;
4246 level->mm_time_fuse = level_mm->time_fuse;
4247 level->mm_time_bomb = level_mm->time_bomb;
4248 level->mm_time_ball = level_mm->time_ball;
4249 level->mm_time_block = level_mm->time_block;
4251 level->num_mm_ball_contents = level_mm->num_ball_contents;
4252 level->mm_ball_choice_mode = level_mm->ball_choice_mode;
4253 level->rotate_mm_ball_content = level_mm->rotate_ball_content;
4254 level->explode_mm_ball = level_mm->explode_ball;
4256 for (i = 0; i < level->num_mm_ball_contents; i++)
4257 level->mm_ball_content[i] =
4258 map_element_MM_to_RND(level_mm->ball_content[i]);
4260 for (x = 0; x < level->fieldx; x++)
4261 for (y = 0; y < level->fieldy; y++)
4262 level->field[x][y] = map_element_MM_to_RND(level_mm->field[x][y]);
4266 // ----------------------------------------------------------------------------
4267 // functions for loading DC level
4268 // ----------------------------------------------------------------------------
4270 #define DC_LEVEL_HEADER_SIZE 344
4272 static unsigned short getDecodedWord_DC(unsigned short data_encoded,
4275 static int last_data_encoded;
4279 int diff_hi, diff_lo;
4280 int data_hi, data_lo;
4281 unsigned short data_decoded;
4285 last_data_encoded = 0;
4292 diff = data_encoded - last_data_encoded;
4293 diff_hi = diff & ~0xff;
4294 diff_lo = diff & 0xff;
4298 data_hi = diff_hi - (offset1 << 8) + (offset2 & 0xff00);
4299 data_lo = (diff_lo + (data_hi >> 16)) & 0x00ff;
4300 data_hi = data_hi & 0xff00;
4302 data_decoded = data_hi | data_lo;
4304 last_data_encoded = data_encoded;
4306 offset1 = (offset1 + 1) % 31;
4307 offset2 = offset2 & 0xff;
4309 return data_decoded;
4312 static int getMappedElement_DC(int element)
4320 // 0x0117 - 0x036e: (?)
4323 // 0x042d - 0x0684: (?)
4339 element = EL_CRYSTAL;
4342 case 0x0e77: // quicksand (boulder)
4343 element = EL_QUICKSAND_FAST_FULL;
4346 case 0x0e99: // slow quicksand (boulder)
4347 element = EL_QUICKSAND_FULL;
4351 element = EL_EM_EXIT_OPEN;
4355 element = EL_EM_EXIT_CLOSED;
4359 element = EL_EM_STEEL_EXIT_OPEN;
4363 element = EL_EM_STEEL_EXIT_CLOSED;
4366 case 0x0f4f: // dynamite (lit 1)
4367 element = EL_EM_DYNAMITE_ACTIVE;
4370 case 0x0f57: // dynamite (lit 2)
4371 element = EL_EM_DYNAMITE_ACTIVE;
4374 case 0x0f5f: // dynamite (lit 3)
4375 element = EL_EM_DYNAMITE_ACTIVE;
4378 case 0x0f67: // dynamite (lit 4)
4379 element = EL_EM_DYNAMITE_ACTIVE;
4386 element = EL_AMOEBA_WET;
4390 element = EL_AMOEBA_DROP;
4394 element = EL_DC_MAGIC_WALL;
4398 element = EL_SPACESHIP_UP;
4402 element = EL_SPACESHIP_DOWN;
4406 element = EL_SPACESHIP_LEFT;
4410 element = EL_SPACESHIP_RIGHT;
4414 element = EL_BUG_UP;
4418 element = EL_BUG_DOWN;
4422 element = EL_BUG_LEFT;
4426 element = EL_BUG_RIGHT;
4430 element = EL_MOLE_UP;
4434 element = EL_MOLE_DOWN;
4438 element = EL_MOLE_LEFT;
4442 element = EL_MOLE_RIGHT;
4450 element = EL_YAMYAM_UP;
4454 element = EL_SWITCHGATE_OPEN;
4458 element = EL_SWITCHGATE_CLOSED;
4462 element = EL_DC_SWITCHGATE_SWITCH_UP;
4466 element = EL_TIMEGATE_CLOSED;
4469 case 0x144c: // conveyor belt switch (green)
4470 element = EL_CONVEYOR_BELT_3_SWITCH_MIDDLE;
4473 case 0x144f: // conveyor belt switch (red)
4474 element = EL_CONVEYOR_BELT_1_SWITCH_MIDDLE;
4477 case 0x1452: // conveyor belt switch (blue)
4478 element = EL_CONVEYOR_BELT_4_SWITCH_MIDDLE;
4482 element = EL_CONVEYOR_BELT_3_MIDDLE;
4486 element = EL_CONVEYOR_BELT_3_LEFT;
4490 element = EL_CONVEYOR_BELT_3_RIGHT;
4494 element = EL_CONVEYOR_BELT_1_MIDDLE;
4498 element = EL_CONVEYOR_BELT_1_LEFT;
4502 element = EL_CONVEYOR_BELT_1_RIGHT;
4506 element = EL_CONVEYOR_BELT_4_MIDDLE;
4510 element = EL_CONVEYOR_BELT_4_LEFT;
4514 element = EL_CONVEYOR_BELT_4_RIGHT;
4518 element = EL_EXPANDABLE_WALL_HORIZONTAL;
4522 element = EL_EXPANDABLE_WALL_VERTICAL;
4526 element = EL_EXPANDABLE_WALL_ANY;
4529 case 0x14ce: // growing steel wall (left/right)
4530 element = EL_EXPANDABLE_STEELWALL_HORIZONTAL;
4533 case 0x14df: // growing steel wall (up/down)
4534 element = EL_EXPANDABLE_STEELWALL_VERTICAL;
4537 case 0x14e8: // growing steel wall (up/down/left/right)
4538 element = EL_EXPANDABLE_STEELWALL_ANY;
4542 element = EL_SHIELD_DEADLY;
4546 element = EL_EXTRA_TIME;
4554 element = EL_EMPTY_SPACE;
4557 case 0x1578: // quicksand (empty)
4558 element = EL_QUICKSAND_FAST_EMPTY;
4561 case 0x1579: // slow quicksand (empty)
4562 element = EL_QUICKSAND_EMPTY;
4572 element = EL_EM_DYNAMITE;
4575 case 0x15a1: // key (red)
4576 element = EL_EM_KEY_1;
4579 case 0x15a2: // key (yellow)
4580 element = EL_EM_KEY_2;
4583 case 0x15a3: // key (blue)
4584 element = EL_EM_KEY_4;
4587 case 0x15a4: // key (green)
4588 element = EL_EM_KEY_3;
4591 case 0x15a5: // key (white)
4592 element = EL_DC_KEY_WHITE;
4596 element = EL_WALL_SLIPPERY;
4603 case 0x15a8: // wall (not round)
4607 case 0x15a9: // (blue)
4608 element = EL_CHAR_A;
4611 case 0x15aa: // (blue)
4612 element = EL_CHAR_B;
4615 case 0x15ab: // (blue)
4616 element = EL_CHAR_C;
4619 case 0x15ac: // (blue)
4620 element = EL_CHAR_D;
4623 case 0x15ad: // (blue)
4624 element = EL_CHAR_E;
4627 case 0x15ae: // (blue)
4628 element = EL_CHAR_F;
4631 case 0x15af: // (blue)
4632 element = EL_CHAR_G;
4635 case 0x15b0: // (blue)
4636 element = EL_CHAR_H;
4639 case 0x15b1: // (blue)
4640 element = EL_CHAR_I;
4643 case 0x15b2: // (blue)
4644 element = EL_CHAR_J;
4647 case 0x15b3: // (blue)
4648 element = EL_CHAR_K;
4651 case 0x15b4: // (blue)
4652 element = EL_CHAR_L;
4655 case 0x15b5: // (blue)
4656 element = EL_CHAR_M;
4659 case 0x15b6: // (blue)
4660 element = EL_CHAR_N;
4663 case 0x15b7: // (blue)
4664 element = EL_CHAR_O;
4667 case 0x15b8: // (blue)
4668 element = EL_CHAR_P;
4671 case 0x15b9: // (blue)
4672 element = EL_CHAR_Q;
4675 case 0x15ba: // (blue)
4676 element = EL_CHAR_R;
4679 case 0x15bb: // (blue)
4680 element = EL_CHAR_S;
4683 case 0x15bc: // (blue)
4684 element = EL_CHAR_T;
4687 case 0x15bd: // (blue)
4688 element = EL_CHAR_U;
4691 case 0x15be: // (blue)
4692 element = EL_CHAR_V;
4695 case 0x15bf: // (blue)
4696 element = EL_CHAR_W;
4699 case 0x15c0: // (blue)
4700 element = EL_CHAR_X;
4703 case 0x15c1: // (blue)
4704 element = EL_CHAR_Y;
4707 case 0x15c2: // (blue)
4708 element = EL_CHAR_Z;
4711 case 0x15c3: // (blue)
4712 element = EL_CHAR_AUMLAUT;
4715 case 0x15c4: // (blue)
4716 element = EL_CHAR_OUMLAUT;
4719 case 0x15c5: // (blue)
4720 element = EL_CHAR_UUMLAUT;
4723 case 0x15c6: // (blue)
4724 element = EL_CHAR_0;
4727 case 0x15c7: // (blue)
4728 element = EL_CHAR_1;
4731 case 0x15c8: // (blue)
4732 element = EL_CHAR_2;
4735 case 0x15c9: // (blue)
4736 element = EL_CHAR_3;
4739 case 0x15ca: // (blue)
4740 element = EL_CHAR_4;
4743 case 0x15cb: // (blue)
4744 element = EL_CHAR_5;
4747 case 0x15cc: // (blue)
4748 element = EL_CHAR_6;
4751 case 0x15cd: // (blue)
4752 element = EL_CHAR_7;
4755 case 0x15ce: // (blue)
4756 element = EL_CHAR_8;
4759 case 0x15cf: // (blue)
4760 element = EL_CHAR_9;
4763 case 0x15d0: // (blue)
4764 element = EL_CHAR_PERIOD;
4767 case 0x15d1: // (blue)
4768 element = EL_CHAR_EXCLAM;
4771 case 0x15d2: // (blue)
4772 element = EL_CHAR_COLON;
4775 case 0x15d3: // (blue)
4776 element = EL_CHAR_LESS;
4779 case 0x15d4: // (blue)
4780 element = EL_CHAR_GREATER;
4783 case 0x15d5: // (blue)
4784 element = EL_CHAR_QUESTION;
4787 case 0x15d6: // (blue)
4788 element = EL_CHAR_COPYRIGHT;
4791 case 0x15d7: // (blue)
4792 element = EL_CHAR_UP;
4795 case 0x15d8: // (blue)
4796 element = EL_CHAR_DOWN;
4799 case 0x15d9: // (blue)
4800 element = EL_CHAR_BUTTON;
4803 case 0x15da: // (blue)
4804 element = EL_CHAR_PLUS;
4807 case 0x15db: // (blue)
4808 element = EL_CHAR_MINUS;
4811 case 0x15dc: // (blue)
4812 element = EL_CHAR_APOSTROPHE;
4815 case 0x15dd: // (blue)
4816 element = EL_CHAR_PARENLEFT;
4819 case 0x15de: // (blue)
4820 element = EL_CHAR_PARENRIGHT;
4823 case 0x15df: // (green)
4824 element = EL_CHAR_A;
4827 case 0x15e0: // (green)
4828 element = EL_CHAR_B;
4831 case 0x15e1: // (green)
4832 element = EL_CHAR_C;
4835 case 0x15e2: // (green)
4836 element = EL_CHAR_D;
4839 case 0x15e3: // (green)
4840 element = EL_CHAR_E;
4843 case 0x15e4: // (green)
4844 element = EL_CHAR_F;
4847 case 0x15e5: // (green)
4848 element = EL_CHAR_G;
4851 case 0x15e6: // (green)
4852 element = EL_CHAR_H;
4855 case 0x15e7: // (green)
4856 element = EL_CHAR_I;
4859 case 0x15e8: // (green)
4860 element = EL_CHAR_J;
4863 case 0x15e9: // (green)
4864 element = EL_CHAR_K;
4867 case 0x15ea: // (green)
4868 element = EL_CHAR_L;
4871 case 0x15eb: // (green)
4872 element = EL_CHAR_M;
4875 case 0x15ec: // (green)
4876 element = EL_CHAR_N;
4879 case 0x15ed: // (green)
4880 element = EL_CHAR_O;
4883 case 0x15ee: // (green)
4884 element = EL_CHAR_P;
4887 case 0x15ef: // (green)
4888 element = EL_CHAR_Q;
4891 case 0x15f0: // (green)
4892 element = EL_CHAR_R;
4895 case 0x15f1: // (green)
4896 element = EL_CHAR_S;
4899 case 0x15f2: // (green)
4900 element = EL_CHAR_T;
4903 case 0x15f3: // (green)
4904 element = EL_CHAR_U;
4907 case 0x15f4: // (green)
4908 element = EL_CHAR_V;
4911 case 0x15f5: // (green)
4912 element = EL_CHAR_W;
4915 case 0x15f6: // (green)
4916 element = EL_CHAR_X;
4919 case 0x15f7: // (green)
4920 element = EL_CHAR_Y;
4923 case 0x15f8: // (green)
4924 element = EL_CHAR_Z;
4927 case 0x15f9: // (green)
4928 element = EL_CHAR_AUMLAUT;
4931 case 0x15fa: // (green)
4932 element = EL_CHAR_OUMLAUT;
4935 case 0x15fb: // (green)
4936 element = EL_CHAR_UUMLAUT;
4939 case 0x15fc: // (green)
4940 element = EL_CHAR_0;
4943 case 0x15fd: // (green)
4944 element = EL_CHAR_1;
4947 case 0x15fe: // (green)
4948 element = EL_CHAR_2;
4951 case 0x15ff: // (green)
4952 element = EL_CHAR_3;
4955 case 0x1600: // (green)
4956 element = EL_CHAR_4;
4959 case 0x1601: // (green)
4960 element = EL_CHAR_5;
4963 case 0x1602: // (green)
4964 element = EL_CHAR_6;
4967 case 0x1603: // (green)
4968 element = EL_CHAR_7;
4971 case 0x1604: // (green)
4972 element = EL_CHAR_8;
4975 case 0x1605: // (green)
4976 element = EL_CHAR_9;
4979 case 0x1606: // (green)
4980 element = EL_CHAR_PERIOD;
4983 case 0x1607: // (green)
4984 element = EL_CHAR_EXCLAM;
4987 case 0x1608: // (green)
4988 element = EL_CHAR_COLON;
4991 case 0x1609: // (green)
4992 element = EL_CHAR_LESS;
4995 case 0x160a: // (green)
4996 element = EL_CHAR_GREATER;
4999 case 0x160b: // (green)
5000 element = EL_CHAR_QUESTION;
5003 case 0x160c: // (green)
5004 element = EL_CHAR_COPYRIGHT;
5007 case 0x160d: // (green)
5008 element = EL_CHAR_UP;
5011 case 0x160e: // (green)
5012 element = EL_CHAR_DOWN;
5015 case 0x160f: // (green)
5016 element = EL_CHAR_BUTTON;
5019 case 0x1610: // (green)
5020 element = EL_CHAR_PLUS;
5023 case 0x1611: // (green)
5024 element = EL_CHAR_MINUS;
5027 case 0x1612: // (green)
5028 element = EL_CHAR_APOSTROPHE;
5031 case 0x1613: // (green)
5032 element = EL_CHAR_PARENLEFT;
5035 case 0x1614: // (green)
5036 element = EL_CHAR_PARENRIGHT;
5039 case 0x1615: // (blue steel)
5040 element = EL_STEEL_CHAR_A;
5043 case 0x1616: // (blue steel)
5044 element = EL_STEEL_CHAR_B;
5047 case 0x1617: // (blue steel)
5048 element = EL_STEEL_CHAR_C;
5051 case 0x1618: // (blue steel)
5052 element = EL_STEEL_CHAR_D;
5055 case 0x1619: // (blue steel)
5056 element = EL_STEEL_CHAR_E;
5059 case 0x161a: // (blue steel)
5060 element = EL_STEEL_CHAR_F;
5063 case 0x161b: // (blue steel)
5064 element = EL_STEEL_CHAR_G;
5067 case 0x161c: // (blue steel)
5068 element = EL_STEEL_CHAR_H;
5071 case 0x161d: // (blue steel)
5072 element = EL_STEEL_CHAR_I;
5075 case 0x161e: // (blue steel)
5076 element = EL_STEEL_CHAR_J;
5079 case 0x161f: // (blue steel)
5080 element = EL_STEEL_CHAR_K;
5083 case 0x1620: // (blue steel)
5084 element = EL_STEEL_CHAR_L;
5087 case 0x1621: // (blue steel)
5088 element = EL_STEEL_CHAR_M;
5091 case 0x1622: // (blue steel)
5092 element = EL_STEEL_CHAR_N;
5095 case 0x1623: // (blue steel)
5096 element = EL_STEEL_CHAR_O;
5099 case 0x1624: // (blue steel)
5100 element = EL_STEEL_CHAR_P;
5103 case 0x1625: // (blue steel)
5104 element = EL_STEEL_CHAR_Q;
5107 case 0x1626: // (blue steel)
5108 element = EL_STEEL_CHAR_R;
5111 case 0x1627: // (blue steel)
5112 element = EL_STEEL_CHAR_S;
5115 case 0x1628: // (blue steel)
5116 element = EL_STEEL_CHAR_T;
5119 case 0x1629: // (blue steel)
5120 element = EL_STEEL_CHAR_U;
5123 case 0x162a: // (blue steel)
5124 element = EL_STEEL_CHAR_V;
5127 case 0x162b: // (blue steel)
5128 element = EL_STEEL_CHAR_W;
5131 case 0x162c: // (blue steel)
5132 element = EL_STEEL_CHAR_X;
5135 case 0x162d: // (blue steel)
5136 element = EL_STEEL_CHAR_Y;
5139 case 0x162e: // (blue steel)
5140 element = EL_STEEL_CHAR_Z;
5143 case 0x162f: // (blue steel)
5144 element = EL_STEEL_CHAR_AUMLAUT;
5147 case 0x1630: // (blue steel)
5148 element = EL_STEEL_CHAR_OUMLAUT;
5151 case 0x1631: // (blue steel)
5152 element = EL_STEEL_CHAR_UUMLAUT;
5155 case 0x1632: // (blue steel)
5156 element = EL_STEEL_CHAR_0;
5159 case 0x1633: // (blue steel)
5160 element = EL_STEEL_CHAR_1;
5163 case 0x1634: // (blue steel)
5164 element = EL_STEEL_CHAR_2;
5167 case 0x1635: // (blue steel)
5168 element = EL_STEEL_CHAR_3;
5171 case 0x1636: // (blue steel)
5172 element = EL_STEEL_CHAR_4;
5175 case 0x1637: // (blue steel)
5176 element = EL_STEEL_CHAR_5;
5179 case 0x1638: // (blue steel)
5180 element = EL_STEEL_CHAR_6;
5183 case 0x1639: // (blue steel)
5184 element = EL_STEEL_CHAR_7;
5187 case 0x163a: // (blue steel)
5188 element = EL_STEEL_CHAR_8;
5191 case 0x163b: // (blue steel)
5192 element = EL_STEEL_CHAR_9;
5195 case 0x163c: // (blue steel)
5196 element = EL_STEEL_CHAR_PERIOD;
5199 case 0x163d: // (blue steel)
5200 element = EL_STEEL_CHAR_EXCLAM;
5203 case 0x163e: // (blue steel)
5204 element = EL_STEEL_CHAR_COLON;
5207 case 0x163f: // (blue steel)
5208 element = EL_STEEL_CHAR_LESS;
5211 case 0x1640: // (blue steel)
5212 element = EL_STEEL_CHAR_GREATER;
5215 case 0x1641: // (blue steel)
5216 element = EL_STEEL_CHAR_QUESTION;
5219 case 0x1642: // (blue steel)
5220 element = EL_STEEL_CHAR_COPYRIGHT;
5223 case 0x1643: // (blue steel)
5224 element = EL_STEEL_CHAR_UP;
5227 case 0x1644: // (blue steel)
5228 element = EL_STEEL_CHAR_DOWN;
5231 case 0x1645: // (blue steel)
5232 element = EL_STEEL_CHAR_BUTTON;
5235 case 0x1646: // (blue steel)
5236 element = EL_STEEL_CHAR_PLUS;
5239 case 0x1647: // (blue steel)
5240 element = EL_STEEL_CHAR_MINUS;
5243 case 0x1648: // (blue steel)
5244 element = EL_STEEL_CHAR_APOSTROPHE;
5247 case 0x1649: // (blue steel)
5248 element = EL_STEEL_CHAR_PARENLEFT;
5251 case 0x164a: // (blue steel)
5252 element = EL_STEEL_CHAR_PARENRIGHT;
5255 case 0x164b: // (green steel)
5256 element = EL_STEEL_CHAR_A;
5259 case 0x164c: // (green steel)
5260 element = EL_STEEL_CHAR_B;
5263 case 0x164d: // (green steel)
5264 element = EL_STEEL_CHAR_C;
5267 case 0x164e: // (green steel)
5268 element = EL_STEEL_CHAR_D;
5271 case 0x164f: // (green steel)
5272 element = EL_STEEL_CHAR_E;
5275 case 0x1650: // (green steel)
5276 element = EL_STEEL_CHAR_F;
5279 case 0x1651: // (green steel)
5280 element = EL_STEEL_CHAR_G;
5283 case 0x1652: // (green steel)
5284 element = EL_STEEL_CHAR_H;
5287 case 0x1653: // (green steel)
5288 element = EL_STEEL_CHAR_I;
5291 case 0x1654: // (green steel)
5292 element = EL_STEEL_CHAR_J;
5295 case 0x1655: // (green steel)
5296 element = EL_STEEL_CHAR_K;
5299 case 0x1656: // (green steel)
5300 element = EL_STEEL_CHAR_L;
5303 case 0x1657: // (green steel)
5304 element = EL_STEEL_CHAR_M;
5307 case 0x1658: // (green steel)
5308 element = EL_STEEL_CHAR_N;
5311 case 0x1659: // (green steel)
5312 element = EL_STEEL_CHAR_O;
5315 case 0x165a: // (green steel)
5316 element = EL_STEEL_CHAR_P;
5319 case 0x165b: // (green steel)
5320 element = EL_STEEL_CHAR_Q;
5323 case 0x165c: // (green steel)
5324 element = EL_STEEL_CHAR_R;
5327 case 0x165d: // (green steel)
5328 element = EL_STEEL_CHAR_S;
5331 case 0x165e: // (green steel)
5332 element = EL_STEEL_CHAR_T;
5335 case 0x165f: // (green steel)
5336 element = EL_STEEL_CHAR_U;
5339 case 0x1660: // (green steel)
5340 element = EL_STEEL_CHAR_V;
5343 case 0x1661: // (green steel)
5344 element = EL_STEEL_CHAR_W;
5347 case 0x1662: // (green steel)
5348 element = EL_STEEL_CHAR_X;
5351 case 0x1663: // (green steel)
5352 element = EL_STEEL_CHAR_Y;
5355 case 0x1664: // (green steel)
5356 element = EL_STEEL_CHAR_Z;
5359 case 0x1665: // (green steel)
5360 element = EL_STEEL_CHAR_AUMLAUT;
5363 case 0x1666: // (green steel)
5364 element = EL_STEEL_CHAR_OUMLAUT;
5367 case 0x1667: // (green steel)
5368 element = EL_STEEL_CHAR_UUMLAUT;
5371 case 0x1668: // (green steel)
5372 element = EL_STEEL_CHAR_0;
5375 case 0x1669: // (green steel)
5376 element = EL_STEEL_CHAR_1;
5379 case 0x166a: // (green steel)
5380 element = EL_STEEL_CHAR_2;
5383 case 0x166b: // (green steel)
5384 element = EL_STEEL_CHAR_3;
5387 case 0x166c: // (green steel)
5388 element = EL_STEEL_CHAR_4;
5391 case 0x166d: // (green steel)
5392 element = EL_STEEL_CHAR_5;
5395 case 0x166e: // (green steel)
5396 element = EL_STEEL_CHAR_6;
5399 case 0x166f: // (green steel)
5400 element = EL_STEEL_CHAR_7;
5403 case 0x1670: // (green steel)
5404 element = EL_STEEL_CHAR_8;
5407 case 0x1671: // (green steel)
5408 element = EL_STEEL_CHAR_9;
5411 case 0x1672: // (green steel)
5412 element = EL_STEEL_CHAR_PERIOD;
5415 case 0x1673: // (green steel)
5416 element = EL_STEEL_CHAR_EXCLAM;
5419 case 0x1674: // (green steel)
5420 element = EL_STEEL_CHAR_COLON;
5423 case 0x1675: // (green steel)
5424 element = EL_STEEL_CHAR_LESS;
5427 case 0x1676: // (green steel)
5428 element = EL_STEEL_CHAR_GREATER;
5431 case 0x1677: // (green steel)
5432 element = EL_STEEL_CHAR_QUESTION;
5435 case 0x1678: // (green steel)
5436 element = EL_STEEL_CHAR_COPYRIGHT;
5439 case 0x1679: // (green steel)
5440 element = EL_STEEL_CHAR_UP;
5443 case 0x167a: // (green steel)
5444 element = EL_STEEL_CHAR_DOWN;
5447 case 0x167b: // (green steel)
5448 element = EL_STEEL_CHAR_BUTTON;
5451 case 0x167c: // (green steel)
5452 element = EL_STEEL_CHAR_PLUS;
5455 case 0x167d: // (green steel)
5456 element = EL_STEEL_CHAR_MINUS;
5459 case 0x167e: // (green steel)
5460 element = EL_STEEL_CHAR_APOSTROPHE;
5463 case 0x167f: // (green steel)
5464 element = EL_STEEL_CHAR_PARENLEFT;
5467 case 0x1680: // (green steel)
5468 element = EL_STEEL_CHAR_PARENRIGHT;
5471 case 0x1681: // gate (red)
5472 element = EL_EM_GATE_1;
5475 case 0x1682: // secret gate (red)
5476 element = EL_EM_GATE_1_GRAY;
5479 case 0x1683: // gate (yellow)
5480 element = EL_EM_GATE_2;
5483 case 0x1684: // secret gate (yellow)
5484 element = EL_EM_GATE_2_GRAY;
5487 case 0x1685: // gate (blue)
5488 element = EL_EM_GATE_4;
5491 case 0x1686: // secret gate (blue)
5492 element = EL_EM_GATE_4_GRAY;
5495 case 0x1687: // gate (green)
5496 element = EL_EM_GATE_3;
5499 case 0x1688: // secret gate (green)
5500 element = EL_EM_GATE_3_GRAY;
5503 case 0x1689: // gate (white)
5504 element = EL_DC_GATE_WHITE;
5507 case 0x168a: // secret gate (white)
5508 element = EL_DC_GATE_WHITE_GRAY;
5511 case 0x168b: // secret gate (no key)
5512 element = EL_DC_GATE_FAKE_GRAY;
5516 element = EL_ROBOT_WHEEL;
5520 element = EL_DC_TIMEGATE_SWITCH;
5524 element = EL_ACID_POOL_BOTTOM;
5528 element = EL_ACID_POOL_TOPLEFT;
5532 element = EL_ACID_POOL_TOPRIGHT;
5536 element = EL_ACID_POOL_BOTTOMLEFT;
5540 element = EL_ACID_POOL_BOTTOMRIGHT;
5544 element = EL_STEELWALL;
5548 element = EL_STEELWALL_SLIPPERY;
5551 case 0x1695: // steel wall (not round)
5552 element = EL_STEELWALL;
5555 case 0x1696: // steel wall (left)
5556 element = EL_DC_STEELWALL_1_LEFT;
5559 case 0x1697: // steel wall (bottom)
5560 element = EL_DC_STEELWALL_1_BOTTOM;
5563 case 0x1698: // steel wall (right)
5564 element = EL_DC_STEELWALL_1_RIGHT;
5567 case 0x1699: // steel wall (top)
5568 element = EL_DC_STEELWALL_1_TOP;
5571 case 0x169a: // steel wall (left/bottom)
5572 element = EL_DC_STEELWALL_1_BOTTOMLEFT;
5575 case 0x169b: // steel wall (right/bottom)
5576 element = EL_DC_STEELWALL_1_BOTTOMRIGHT;
5579 case 0x169c: // steel wall (right/top)
5580 element = EL_DC_STEELWALL_1_TOPRIGHT;
5583 case 0x169d: // steel wall (left/top)
5584 element = EL_DC_STEELWALL_1_TOPLEFT;
5587 case 0x169e: // steel wall (right/bottom small)
5588 element = EL_DC_STEELWALL_1_BOTTOMRIGHT_2;
5591 case 0x169f: // steel wall (left/bottom small)
5592 element = EL_DC_STEELWALL_1_BOTTOMLEFT_2;
5595 case 0x16a0: // steel wall (right/top small)
5596 element = EL_DC_STEELWALL_1_TOPRIGHT_2;
5599 case 0x16a1: // steel wall (left/top small)
5600 element = EL_DC_STEELWALL_1_TOPLEFT_2;
5603 case 0x16a2: // steel wall (left/right)
5604 element = EL_DC_STEELWALL_1_VERTICAL;
5607 case 0x16a3: // steel wall (top/bottom)
5608 element = EL_DC_STEELWALL_1_HORIZONTAL;
5611 case 0x16a4: // steel wall 2 (left end)
5612 element = EL_DC_STEELWALL_2_LEFT;
5615 case 0x16a5: // steel wall 2 (right end)
5616 element = EL_DC_STEELWALL_2_RIGHT;
5619 case 0x16a6: // steel wall 2 (top end)
5620 element = EL_DC_STEELWALL_2_TOP;
5623 case 0x16a7: // steel wall 2 (bottom end)
5624 element = EL_DC_STEELWALL_2_BOTTOM;
5627 case 0x16a8: // steel wall 2 (left/right)
5628 element = EL_DC_STEELWALL_2_HORIZONTAL;
5631 case 0x16a9: // steel wall 2 (up/down)
5632 element = EL_DC_STEELWALL_2_VERTICAL;
5635 case 0x16aa: // steel wall 2 (mid)
5636 element = EL_DC_STEELWALL_2_MIDDLE;
5640 element = EL_SIGN_EXCLAMATION;
5644 element = EL_SIGN_RADIOACTIVITY;
5648 element = EL_SIGN_STOP;
5652 element = EL_SIGN_WHEELCHAIR;
5656 element = EL_SIGN_PARKING;
5660 element = EL_SIGN_NO_ENTRY;
5664 element = EL_SIGN_HEART;
5668 element = EL_SIGN_GIVE_WAY;
5672 element = EL_SIGN_ENTRY_FORBIDDEN;
5676 element = EL_SIGN_EMERGENCY_EXIT;
5680 element = EL_SIGN_YIN_YANG;
5684 element = EL_WALL_EMERALD;
5688 element = EL_WALL_DIAMOND;
5692 element = EL_WALL_PEARL;
5696 element = EL_WALL_CRYSTAL;
5700 element = EL_INVISIBLE_WALL;
5704 element = EL_INVISIBLE_STEELWALL;
5708 // EL_INVISIBLE_SAND
5711 element = EL_LIGHT_SWITCH;
5715 element = EL_ENVELOPE_1;
5719 if (element >= 0x0117 && element <= 0x036e) // (?)
5720 element = EL_DIAMOND;
5721 else if (element >= 0x042d && element <= 0x0684) // (?)
5722 element = EL_EMERALD;
5723 else if (element >= 0x157c && element <= 0x158b)
5725 else if (element >= 0x1590 && element <= 0x159f)
5726 element = EL_DC_LANDMINE;
5727 else if (element >= 0x16bc && element <= 0x16cb)
5728 element = EL_INVISIBLE_SAND;
5731 Warn("unknown Diamond Caves element 0x%04x", element);
5733 element = EL_UNKNOWN;
5738 return getMappedElement(element);
5741 static void LoadLevelFromFileStream_DC(File *file, struct LevelInfo *level)
5743 byte header[DC_LEVEL_HEADER_SIZE];
5745 int envelope_header_pos = 62;
5746 int envelope_content_pos = 94;
5747 int level_name_pos = 251;
5748 int level_author_pos = 292;
5749 int envelope_header_len;
5750 int envelope_content_len;
5752 int level_author_len;
5754 int num_yamyam_contents;
5757 getDecodedWord_DC(0, TRUE); // initialize DC2 decoding engine
5759 for (i = 0; i < DC_LEVEL_HEADER_SIZE / 2; i++)
5761 unsigned short header_word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
5763 header[i * 2 + 0] = header_word >> 8;
5764 header[i * 2 + 1] = header_word & 0xff;
5767 // read some values from level header to check level decoding integrity
5768 fieldx = header[6] | (header[7] << 8);
5769 fieldy = header[8] | (header[9] << 8);
5770 num_yamyam_contents = header[60] | (header[61] << 8);
5772 // do some simple sanity checks to ensure that level was correctly decoded
5773 if (fieldx < 1 || fieldx > 256 ||
5774 fieldy < 1 || fieldy > 256 ||
5775 num_yamyam_contents < 1 || num_yamyam_contents > 8)
5777 level->no_valid_file = TRUE;
5779 Warn("cannot decode level from stream -- using empty level");
5784 // maximum envelope header size is 31 bytes
5785 envelope_header_len = header[envelope_header_pos];
5786 // maximum envelope content size is 110 (156?) bytes
5787 envelope_content_len = header[envelope_content_pos];
5789 // maximum level title size is 40 bytes
5790 level_name_len = MIN(header[level_name_pos], MAX_LEVEL_NAME_LEN);
5791 // maximum level author size is 30 (51?) bytes
5792 level_author_len = MIN(header[level_author_pos], MAX_LEVEL_AUTHOR_LEN);
5796 for (i = 0; i < envelope_header_len; i++)
5797 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
5798 level->envelope[0].text[envelope_size++] =
5799 header[envelope_header_pos + 1 + i];
5801 if (envelope_header_len > 0 && envelope_content_len > 0)
5803 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
5804 level->envelope[0].text[envelope_size++] = '\n';
5805 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
5806 level->envelope[0].text[envelope_size++] = '\n';
5809 for (i = 0; i < envelope_content_len; i++)
5810 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
5811 level->envelope[0].text[envelope_size++] =
5812 header[envelope_content_pos + 1 + i];
5814 level->envelope[0].text[envelope_size] = '\0';
5816 level->envelope[0].xsize = MAX_ENVELOPE_XSIZE;
5817 level->envelope[0].ysize = 10;
5818 level->envelope[0].autowrap = TRUE;
5819 level->envelope[0].centered = TRUE;
5821 for (i = 0; i < level_name_len; i++)
5822 level->name[i] = header[level_name_pos + 1 + i];
5823 level->name[level_name_len] = '\0';
5825 for (i = 0; i < level_author_len; i++)
5826 level->author[i] = header[level_author_pos + 1 + i];
5827 level->author[level_author_len] = '\0';
5829 num_yamyam_contents = header[60] | (header[61] << 8);
5830 level->num_yamyam_contents =
5831 MIN(MAX(MIN_ELEMENT_CONTENTS, num_yamyam_contents), MAX_ELEMENT_CONTENTS);
5833 for (i = 0; i < num_yamyam_contents; i++)
5835 for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
5837 unsigned short word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
5838 int element_dc = ((word & 0xff) << 8) | ((word >> 8) & 0xff);
5840 if (i < MAX_ELEMENT_CONTENTS)
5841 level->yamyam_content[i].e[x][y] = getMappedElement_DC(element_dc);
5845 fieldx = header[6] | (header[7] << 8);
5846 fieldy = header[8] | (header[9] << 8);
5847 level->fieldx = MIN(MAX(MIN_LEV_FIELDX, fieldx), MAX_LEV_FIELDX);
5848 level->fieldy = MIN(MAX(MIN_LEV_FIELDY, fieldy), MAX_LEV_FIELDY);
5850 for (y = 0; y < fieldy; y++) for (x = 0; x < fieldx; x++)
5852 unsigned short word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
5853 int element_dc = ((word & 0xff) << 8) | ((word >> 8) & 0xff);
5855 if (x < MAX_LEV_FIELDX && y < MAX_LEV_FIELDY)
5856 level->field[x][y] = getMappedElement_DC(element_dc);
5859 x = MIN(MAX(0, (header[10] | (header[11] << 8)) - 1), MAX_LEV_FIELDX - 1);
5860 y = MIN(MAX(0, (header[12] | (header[13] << 8)) - 1), MAX_LEV_FIELDY - 1);
5861 level->field[x][y] = EL_PLAYER_1;
5863 x = MIN(MAX(0, (header[14] | (header[15] << 8)) - 1), MAX_LEV_FIELDX - 1);
5864 y = MIN(MAX(0, (header[16] | (header[17] << 8)) - 1), MAX_LEV_FIELDY - 1);
5865 level->field[x][y] = EL_PLAYER_2;
5867 level->gems_needed = header[18] | (header[19] << 8);
5869 level->score[SC_EMERALD] = header[20] | (header[21] << 8);
5870 level->score[SC_DIAMOND] = header[22] | (header[23] << 8);
5871 level->score[SC_PEARL] = header[24] | (header[25] << 8);
5872 level->score[SC_CRYSTAL] = header[26] | (header[27] << 8);
5873 level->score[SC_NUT] = header[28] | (header[29] << 8);
5874 level->score[SC_ROBOT] = header[30] | (header[31] << 8);
5875 level->score[SC_SPACESHIP] = header[32] | (header[33] << 8);
5876 level->score[SC_BUG] = header[34] | (header[35] << 8);
5877 level->score[SC_YAMYAM] = header[36] | (header[37] << 8);
5878 level->score[SC_DYNAMITE] = header[38] | (header[39] << 8);
5879 level->score[SC_KEY] = header[40] | (header[41] << 8);
5880 level->score[SC_TIME_BONUS] = header[42] | (header[43] << 8);
5882 level->time = header[44] | (header[45] << 8);
5884 level->amoeba_speed = header[46] | (header[47] << 8);
5885 level->time_light = header[48] | (header[49] << 8);
5886 level->time_timegate = header[50] | (header[51] << 8);
5887 level->time_wheel = header[52] | (header[53] << 8);
5888 level->time_magic_wall = header[54] | (header[55] << 8);
5889 level->extra_time = header[56] | (header[57] << 8);
5890 level->shield_normal_time = header[58] | (header[59] << 8);
5892 // shield and extra time elements do not have a score
5893 level->score[SC_SHIELD] = 0;
5894 level->extra_time_score = 0;
5896 // set time for normal and deadly shields to the same value
5897 level->shield_deadly_time = level->shield_normal_time;
5899 // Diamond Caves has the same (strange) behaviour as Emerald Mine that gems
5900 // can slip down from flat walls, like normal walls and steel walls
5901 level->em_slippery_gems = TRUE;
5903 // time score is counted for each 10 seconds left in Diamond Caves levels
5904 level->time_score_base = 10;
5907 static void LoadLevelFromFileInfo_DC(struct LevelInfo *level,
5908 struct LevelFileInfo *level_file_info,
5909 boolean level_info_only)
5911 char *filename = level_file_info->filename;
5913 int num_magic_bytes = 8;
5914 char magic_bytes[num_magic_bytes + 1];
5915 int num_levels_to_skip = level_file_info->nr - leveldir_current->first_level;
5917 if (!(file = openFile(filename, MODE_READ)))
5919 level->no_valid_file = TRUE;
5921 if (!level_info_only)
5922 Warn("cannot read level '%s' -- using empty level", filename);
5927 // fseek(file, 0x0000, SEEK_SET);
5929 if (level_file_info->packed)
5931 // read "magic bytes" from start of file
5932 if (getStringFromFile(file, magic_bytes, num_magic_bytes + 1) == NULL)
5933 magic_bytes[0] = '\0';
5935 // check "magic bytes" for correct file format
5936 if (!strPrefix(magic_bytes, "DC2"))
5938 level->no_valid_file = TRUE;
5940 Warn("unknown DC level file '%s' -- using empty level", filename);
5945 if (strPrefix(magic_bytes, "DC2Win95") ||
5946 strPrefix(magic_bytes, "DC2Win98"))
5948 int position_first_level = 0x00fa;
5949 int extra_bytes = 4;
5952 // advance file stream to first level inside the level package
5953 skip_bytes = position_first_level - num_magic_bytes - extra_bytes;
5955 // each block of level data is followed by block of non-level data
5956 num_levels_to_skip *= 2;
5958 // at least skip header bytes, therefore use ">= 0" instead of "> 0"
5959 while (num_levels_to_skip >= 0)
5961 // advance file stream to next level inside the level package
5962 if (seekFile(file, skip_bytes, SEEK_CUR) != 0)
5964 level->no_valid_file = TRUE;
5966 Warn("cannot fseek in file '%s' -- using empty level", filename);
5971 // skip apparently unused extra bytes following each level
5972 ReadUnusedBytesFromFile(file, extra_bytes);
5974 // read size of next level in level package
5975 skip_bytes = getFile32BitLE(file);
5977 num_levels_to_skip--;
5982 level->no_valid_file = TRUE;
5984 Warn("unknown DC2 level file '%s' -- using empty level", filename);
5990 LoadLevelFromFileStream_DC(file, level);
5996 // ----------------------------------------------------------------------------
5997 // functions for loading SB level
5998 // ----------------------------------------------------------------------------
6000 int getMappedElement_SB(int element_ascii, boolean use_ces)
6008 sb_element_mapping[] =
6010 { ' ', EL_EMPTY, EL_CUSTOM_1 }, // floor (space)
6011 { '#', EL_STEELWALL, EL_CUSTOM_2 }, // wall
6012 { '@', EL_PLAYER_1, EL_CUSTOM_3 }, // player
6013 { '$', EL_SOKOBAN_OBJECT, EL_CUSTOM_4 }, // box
6014 { '.', EL_SOKOBAN_FIELD_EMPTY, EL_CUSTOM_5 }, // goal square
6015 { '*', EL_SOKOBAN_FIELD_FULL, EL_CUSTOM_6 }, // box on goal square
6016 { '+', EL_SOKOBAN_FIELD_PLAYER, EL_CUSTOM_7 }, // player on goal square
6017 { '_', EL_INVISIBLE_STEELWALL, EL_FROM_LEVEL_TEMPLATE }, // floor beyond border
6024 for (i = 0; sb_element_mapping[i].ascii != 0; i++)
6025 if (element_ascii == sb_element_mapping[i].ascii)
6026 return (use_ces ? sb_element_mapping[i].ce : sb_element_mapping[i].sb);
6028 return EL_UNDEFINED;
6031 static void SetLevelSettings_SB(struct LevelInfo *level)
6035 level->use_step_counter = TRUE;
6038 level->score[SC_TIME_BONUS] = 0;
6039 level->time_score_base = 1;
6040 level->rate_time_over_score = TRUE;
6043 level->auto_exit_sokoban = TRUE;
6046 static void LoadLevelFromFileInfo_SB(struct LevelInfo *level,
6047 struct LevelFileInfo *level_file_info,
6048 boolean level_info_only)
6050 char *filename = level_file_info->filename;
6051 char line[MAX_LINE_LEN], line_raw[MAX_LINE_LEN], previous_line[MAX_LINE_LEN];
6052 char last_comment[MAX_LINE_LEN];
6053 char level_name[MAX_LINE_LEN];
6056 int num_levels_to_skip = level_file_info->nr - leveldir_current->first_level;
6057 boolean read_continued_line = FALSE;
6058 boolean reading_playfield = FALSE;
6059 boolean got_valid_playfield_line = FALSE;
6060 boolean invalid_playfield_char = FALSE;
6061 boolean load_xsb_to_ces = check_special_flags("load_xsb_to_ces");
6062 int file_level_nr = 0;
6064 int x = 0, y = 0; // initialized to make compilers happy
6066 last_comment[0] = '\0';
6067 level_name[0] = '\0';
6069 if (!(file = openFile(filename, MODE_READ)))
6071 level->no_valid_file = TRUE;
6073 if (!level_info_only)
6074 Warn("cannot read level '%s' -- using empty level", filename);
6079 while (!checkEndOfFile(file))
6081 // level successfully read, but next level may follow here
6082 if (!got_valid_playfield_line && reading_playfield)
6084 // read playfield from single level file -- skip remaining file
6085 if (!level_file_info->packed)
6088 if (file_level_nr >= num_levels_to_skip)
6093 last_comment[0] = '\0';
6094 level_name[0] = '\0';
6096 reading_playfield = FALSE;
6099 got_valid_playfield_line = FALSE;
6101 // read next line of input file
6102 if (!getStringFromFile(file, line, MAX_LINE_LEN))
6105 // check if line was completely read and is terminated by line break
6106 if (strlen(line) > 0 && line[strlen(line) - 1] == '\n')
6109 // cut trailing line break (this can be newline and/or carriage return)
6110 for (line_ptr = &line[strlen(line)]; line_ptr >= line; line_ptr--)
6111 if ((*line_ptr == '\n' || *line_ptr == '\r') && *(line_ptr + 1) == '\0')
6114 // copy raw input line for later use (mainly debugging output)
6115 strcpy(line_raw, line);
6117 if (read_continued_line)
6119 // append new line to existing line, if there is enough space
6120 if (strlen(previous_line) + strlen(line_ptr) < MAX_LINE_LEN)
6121 strcat(previous_line, line_ptr);
6123 strcpy(line, previous_line); // copy storage buffer to line
6125 read_continued_line = FALSE;
6128 // if the last character is '\', continue at next line
6129 if (strlen(line) > 0 && line[strlen(line) - 1] == '\\')
6131 line[strlen(line) - 1] = '\0'; // cut off trailing backslash
6132 strcpy(previous_line, line); // copy line to storage buffer
6134 read_continued_line = TRUE;
6140 if (line[0] == '\0')
6143 // extract comment text from comment line
6146 for (line_ptr = line; *line_ptr; line_ptr++)
6147 if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != ';')
6150 strcpy(last_comment, line_ptr);
6155 // extract level title text from line containing level title
6156 if (line[0] == '\'')
6158 strcpy(level_name, &line[1]);
6160 if (strlen(level_name) > 0 && level_name[strlen(level_name) - 1] == '\'')
6161 level_name[strlen(level_name) - 1] = '\0';
6166 // skip lines containing only spaces (or empty lines)
6167 for (line_ptr = line; *line_ptr; line_ptr++)
6168 if (*line_ptr != ' ')
6170 if (*line_ptr == '\0')
6173 // at this point, we have found a line containing part of a playfield
6175 got_valid_playfield_line = TRUE;
6177 if (!reading_playfield)
6179 reading_playfield = TRUE;
6180 invalid_playfield_char = FALSE;
6182 for (x = 0; x < MAX_LEV_FIELDX; x++)
6183 for (y = 0; y < MAX_LEV_FIELDY; y++)
6184 level->field[x][y] = getMappedElement_SB(' ', load_xsb_to_ces);
6189 // start with topmost tile row
6193 // skip playfield line if larger row than allowed
6194 if (y >= MAX_LEV_FIELDY)
6197 // start with leftmost tile column
6200 // read playfield elements from line
6201 for (line_ptr = line; *line_ptr; line_ptr++)
6203 int mapped_sb_element = getMappedElement_SB(*line_ptr, load_xsb_to_ces);
6205 // stop parsing playfield line if larger column than allowed
6206 if (x >= MAX_LEV_FIELDX)
6209 if (mapped_sb_element == EL_UNDEFINED)
6211 invalid_playfield_char = TRUE;
6216 level->field[x][y] = mapped_sb_element;
6218 // continue with next tile column
6221 level->fieldx = MAX(x, level->fieldx);
6224 if (invalid_playfield_char)
6226 // if first playfield line, treat invalid lines as comment lines
6228 reading_playfield = FALSE;
6233 // continue with next tile row
6241 level->fieldx = MIN(MAX(MIN_LEV_FIELDX, level->fieldx), MAX_LEV_FIELDX);
6242 level->fieldy = MIN(MAX(MIN_LEV_FIELDY, level->fieldy), MAX_LEV_FIELDY);
6244 if (!reading_playfield)
6246 level->no_valid_file = TRUE;
6248 Warn("cannot read level '%s' -- using empty level", filename);
6253 if (*level_name != '\0')
6255 strncpy(level->name, level_name, MAX_LEVEL_NAME_LEN);
6256 level->name[MAX_LEVEL_NAME_LEN] = '\0';
6258 else if (*last_comment != '\0')
6260 strncpy(level->name, last_comment, MAX_LEVEL_NAME_LEN);
6261 level->name[MAX_LEVEL_NAME_LEN] = '\0';
6265 sprintf(level->name, "--> Level %d <--", level_file_info->nr);
6268 // set all empty fields beyond the border walls to invisible steel wall
6269 for (y = 0; y < level->fieldy; y++) for (x = 0; x < level->fieldx; x++)
6271 if ((x == 0 || x == level->fieldx - 1 ||
6272 y == 0 || y == level->fieldy - 1) &&
6273 level->field[x][y] == getMappedElement_SB(' ', load_xsb_to_ces))
6274 FloodFillLevel(x, y, getMappedElement_SB('_', load_xsb_to_ces),
6275 level->field, level->fieldx, level->fieldy);
6278 // set special level settings for Sokoban levels
6279 SetLevelSettings_SB(level);
6281 if (load_xsb_to_ces)
6283 // special global settings can now be set in level template
6284 level->use_custom_template = TRUE;
6289 // -------------------------------------------------------------------------
6290 // functions for handling native levels
6291 // -------------------------------------------------------------------------
6293 static void LoadLevelFromFileInfo_EM(struct LevelInfo *level,
6294 struct LevelFileInfo *level_file_info,
6295 boolean level_info_only)
6297 if (!LoadNativeLevel_EM(level_file_info->filename, level_info_only))
6298 level->no_valid_file = TRUE;
6301 static void LoadLevelFromFileInfo_SP(struct LevelInfo *level,
6302 struct LevelFileInfo *level_file_info,
6303 boolean level_info_only)
6307 // determine position of requested level inside level package
6308 if (level_file_info->packed)
6309 pos = level_file_info->nr - leveldir_current->first_level;
6311 if (!LoadNativeLevel_SP(level_file_info->filename, pos, level_info_only))
6312 level->no_valid_file = TRUE;
6315 static void LoadLevelFromFileInfo_MM(struct LevelInfo *level,
6316 struct LevelFileInfo *level_file_info,
6317 boolean level_info_only)
6319 if (!LoadNativeLevel_MM(level_file_info->filename, level_info_only))
6320 level->no_valid_file = TRUE;
6323 void CopyNativeLevel_RND_to_Native(struct LevelInfo *level)
6325 if (level->game_engine_type == GAME_ENGINE_TYPE_EM)
6326 CopyNativeLevel_RND_to_EM(level);
6327 else if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
6328 CopyNativeLevel_RND_to_SP(level);
6329 else if (level->game_engine_type == GAME_ENGINE_TYPE_MM)
6330 CopyNativeLevel_RND_to_MM(level);
6333 void CopyNativeLevel_Native_to_RND(struct LevelInfo *level)
6335 if (level->game_engine_type == GAME_ENGINE_TYPE_EM)
6336 CopyNativeLevel_EM_to_RND(level);
6337 else if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
6338 CopyNativeLevel_SP_to_RND(level);
6339 else if (level->game_engine_type == GAME_ENGINE_TYPE_MM)
6340 CopyNativeLevel_MM_to_RND(level);
6343 void SaveNativeLevel(struct LevelInfo *level)
6345 if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
6347 char *basename = getSingleLevelBasenameExt(level->file_info.nr, "sp");
6348 char *filename = getLevelFilenameFromBasename(basename);
6350 CopyNativeLevel_RND_to_SP(level);
6351 CopyNativeTape_RND_to_SP(level);
6353 SaveNativeLevel_SP(filename);
6358 // ----------------------------------------------------------------------------
6359 // functions for loading generic level
6360 // ----------------------------------------------------------------------------
6362 static void LoadLevelFromFileInfo(struct LevelInfo *level,
6363 struct LevelFileInfo *level_file_info,
6364 boolean level_info_only)
6366 // always start with reliable default values
6367 setLevelInfoToDefaults(level, level_info_only, TRUE);
6369 switch (level_file_info->type)
6371 case LEVEL_FILE_TYPE_RND:
6372 LoadLevelFromFileInfo_RND(level, level_file_info, level_info_only);
6375 case LEVEL_FILE_TYPE_EM:
6376 LoadLevelFromFileInfo_EM(level, level_file_info, level_info_only);
6377 level->game_engine_type = GAME_ENGINE_TYPE_EM;
6380 case LEVEL_FILE_TYPE_SP:
6381 LoadLevelFromFileInfo_SP(level, level_file_info, level_info_only);
6382 level->game_engine_type = GAME_ENGINE_TYPE_SP;
6385 case LEVEL_FILE_TYPE_MM:
6386 LoadLevelFromFileInfo_MM(level, level_file_info, level_info_only);
6387 level->game_engine_type = GAME_ENGINE_TYPE_MM;
6390 case LEVEL_FILE_TYPE_DC:
6391 LoadLevelFromFileInfo_DC(level, level_file_info, level_info_only);
6394 case LEVEL_FILE_TYPE_SB:
6395 LoadLevelFromFileInfo_SB(level, level_file_info, level_info_only);
6399 LoadLevelFromFileInfo_RND(level, level_file_info, level_info_only);
6403 // if level file is invalid, restore level structure to default values
6404 if (level->no_valid_file)
6405 setLevelInfoToDefaults(level, level_info_only, FALSE);
6407 if (level->game_engine_type == GAME_ENGINE_TYPE_UNKNOWN)
6408 level->game_engine_type = GAME_ENGINE_TYPE_RND;
6410 if (level_file_info->type != LEVEL_FILE_TYPE_RND)
6411 CopyNativeLevel_Native_to_RND(level);
6414 void LoadLevelFromFilename(struct LevelInfo *level, char *filename)
6416 static struct LevelFileInfo level_file_info;
6418 // always start with reliable default values
6419 setFileInfoToDefaults(&level_file_info);
6421 level_file_info.nr = 0; // unknown level number
6422 level_file_info.type = LEVEL_FILE_TYPE_RND; // no others supported yet
6424 setString(&level_file_info.filename, filename);
6426 LoadLevelFromFileInfo(level, &level_file_info, FALSE);
6429 static void LoadLevel_InitVersion(struct LevelInfo *level)
6433 if (leveldir_current == NULL) // only when dumping level
6436 // all engine modifications also valid for levels which use latest engine
6437 if (level->game_version < VERSION_IDENT(3,2,0,5))
6439 // time bonus score was given for 10 s instead of 1 s before 3.2.0-5
6440 level->time_score_base = 10;
6443 if (leveldir_current->latest_engine)
6445 // ---------- use latest game engine --------------------------------------
6447 /* For all levels which are forced to use the latest game engine version
6448 (normally all but user contributed, private and undefined levels), set
6449 the game engine version to the actual version; this allows for actual
6450 corrections in the game engine to take effect for existing, converted
6451 levels (from "classic" or other existing games) to make the emulation
6452 of the corresponding game more accurate, while (hopefully) not breaking
6453 existing levels created from other players. */
6455 level->game_version = GAME_VERSION_ACTUAL;
6457 /* Set special EM style gems behaviour: EM style gems slip down from
6458 normal, steel and growing wall. As this is a more fundamental change,
6459 it seems better to set the default behaviour to "off" (as it is more
6460 natural) and make it configurable in the level editor (as a property
6461 of gem style elements). Already existing converted levels (neither
6462 private nor contributed levels) are changed to the new behaviour. */
6464 if (level->file_version < FILE_VERSION_2_0)
6465 level->em_slippery_gems = TRUE;
6470 // ---------- use game engine the level was created with --------------------
6472 /* For all levels which are not forced to use the latest game engine
6473 version (normally user contributed, private and undefined levels),
6474 use the version of the game engine the levels were created for.
6476 Since 2.0.1, the game engine version is now directly stored
6477 in the level file (chunk "VERS"), so there is no need anymore
6478 to set the game version from the file version (except for old,
6479 pre-2.0 levels, where the game version is still taken from the
6480 file format version used to store the level -- see above). */
6482 // player was faster than enemies in 1.0.0 and before
6483 if (level->file_version == FILE_VERSION_1_0)
6484 for (i = 0; i < MAX_PLAYERS; i++)
6485 level->initial_player_stepsize[i] = STEPSIZE_FAST;
6487 // default behaviour for EM style gems was "slippery" only in 2.0.1
6488 if (level->game_version == VERSION_IDENT(2,0,1,0))
6489 level->em_slippery_gems = TRUE;
6491 // springs could be pushed over pits before (pre-release version) 2.2.0
6492 if (level->game_version < VERSION_IDENT(2,2,0,0))
6493 level->use_spring_bug = TRUE;
6495 if (level->game_version < VERSION_IDENT(3,2,0,5))
6497 // time orb caused limited time in endless time levels before 3.2.0-5
6498 level->use_time_orb_bug = TRUE;
6500 // default behaviour for snapping was "no snap delay" before 3.2.0-5
6501 level->block_snap_field = FALSE;
6503 // extra time score was same value as time left score before 3.2.0-5
6504 level->extra_time_score = level->score[SC_TIME_BONUS];
6507 if (level->game_version < VERSION_IDENT(3,2,0,7))
6509 // default behaviour for snapping was "not continuous" before 3.2.0-7
6510 level->continuous_snapping = FALSE;
6513 // only few elements were able to actively move into acid before 3.1.0
6514 // trigger settings did not exist before 3.1.0; set to default "any"
6515 if (level->game_version < VERSION_IDENT(3,1,0,0))
6517 // correct "can move into acid" settings (all zero in old levels)
6519 level->can_move_into_acid_bits = 0; // nothing can move into acid
6520 level->dont_collide_with_bits = 0; // nothing is deadly when colliding
6522 setMoveIntoAcidProperty(level, EL_ROBOT, TRUE);
6523 setMoveIntoAcidProperty(level, EL_SATELLITE, TRUE);
6524 setMoveIntoAcidProperty(level, EL_PENGUIN, TRUE);
6525 setMoveIntoAcidProperty(level, EL_BALLOON, TRUE);
6527 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6528 SET_PROPERTY(EL_CUSTOM_START + i, EP_CAN_MOVE_INTO_ACID, TRUE);
6530 // correct trigger settings (stored as zero == "none" in old levels)
6532 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6534 int element = EL_CUSTOM_START + i;
6535 struct ElementInfo *ei = &element_info[element];
6537 for (j = 0; j < ei->num_change_pages; j++)
6539 struct ElementChangeInfo *change = &ei->change_page[j];
6541 change->trigger_player = CH_PLAYER_ANY;
6542 change->trigger_page = CH_PAGE_ANY;
6547 // try to detect and fix "Snake Bite" levels, which are broken with 3.2.0
6549 int element = EL_CUSTOM_256;
6550 struct ElementInfo *ei = &element_info[element];
6551 struct ElementChangeInfo *change = &ei->change_page[0];
6553 /* This is needed to fix a problem that was caused by a bugfix in function
6554 game.c/CreateFieldExt() introduced with 3.2.0 that corrects the behaviour
6555 when a custom element changes to EL_SOKOBAN_FIELD_PLAYER (before, it did
6556 not replace walkable elements, but instead just placed the player on it,
6557 without placing the Sokoban field under the player). Unfortunately, this
6558 breaks "Snake Bite" style levels when the snake is halfway through a door
6559 that just closes (the snake head is still alive and can be moved in this
6560 case). This can be fixed by replacing the EL_SOKOBAN_FIELD_PLAYER by the
6561 player (without Sokoban element) which then gets killed as designed). */
6563 if ((strncmp(leveldir_current->identifier, "snake_bite", 10) == 0 ||
6564 strncmp(ei->description, "pause b4 death", 14) == 0) &&
6565 change->target_element == EL_SOKOBAN_FIELD_PLAYER)
6566 change->target_element = EL_PLAYER_1;
6569 // try to detect and fix "Zelda" style levels, which are broken with 3.2.5
6570 if (level->game_version < VERSION_IDENT(3,2,5,0))
6572 /* This is needed to fix a problem that was caused by a bugfix in function
6573 game.c/CheckTriggeredElementChangeExt() introduced with 3.2.5 that
6574 corrects the behaviour when a custom element changes to another custom
6575 element with a higher element number that has change actions defined.
6576 Normally, only one change per frame is allowed for custom elements.
6577 Therefore, it is checked if a custom element already changed in the
6578 current frame; if it did, subsequent changes are suppressed.
6579 Unfortunately, this is only checked for element changes, but not for
6580 change actions, which are still executed. As the function above loops
6581 through all custom elements from lower to higher, an element change
6582 resulting in a lower CE number won't be checked again, while a target
6583 element with a higher number will also be checked, and potential change
6584 actions will get executed for this CE, too (which is wrong), while
6585 further changes are ignored (which is correct). As this bugfix breaks
6586 Zelda II (and introduces graphical bugs to Zelda I, and also breaks a
6587 few other levels like Alan Bond's "FMV"), allow the previous, incorrect
6588 behaviour for existing levels and tapes that make use of this bug */
6590 level->use_action_after_change_bug = TRUE;
6593 // not centering level after relocating player was default only in 3.2.3
6594 if (level->game_version == VERSION_IDENT(3,2,3,0)) // (no pre-releases)
6595 level->shifted_relocation = TRUE;
6597 // EM style elements always chain-exploded in R'n'D engine before 3.2.6
6598 if (level->game_version < VERSION_IDENT(3,2,6,0))
6599 level->em_explodes_by_fire = TRUE;
6601 // levels were solved by the first player entering an exit up to 4.1.0.0
6602 if (level->game_version <= VERSION_IDENT(4,1,0,0))
6603 level->solved_by_one_player = TRUE;
6605 // game logic of "game of life" and "biomaze" was buggy before 4.1.1.1
6606 if (level->game_version < VERSION_IDENT(4,1,1,1))
6607 level->use_life_bugs = TRUE;
6609 // only Sokoban fields (but not objects) had to be solved before 4.1.1.1
6610 if (level->game_version < VERSION_IDENT(4,1,1,1))
6611 level->sb_objects_needed = FALSE;
6613 // CE actions were triggered by unfinished digging/collecting up to 4.2.2.0
6614 if (level->game_version <= VERSION_IDENT(4,2,2,0))
6615 level->finish_dig_collect = FALSE;
6617 // CE changing to player was kept under the player if walkable up to 4.2.3.1
6618 if (level->game_version <= VERSION_IDENT(4,2,3,1))
6619 level->keep_walkable_ce = TRUE;
6622 static void LoadLevel_InitSettings_SB(struct LevelInfo *level)
6624 boolean is_sokoban_level = TRUE; // unless non-Sokoban elements found
6627 // check if this level is (not) a Sokoban level
6628 for (y = 0; y < level->fieldy; y++)
6629 for (x = 0; x < level->fieldx; x++)
6630 if (!IS_SB_ELEMENT(Tile[x][y]))
6631 is_sokoban_level = FALSE;
6633 if (is_sokoban_level)
6635 // set special level settings for Sokoban levels
6636 SetLevelSettings_SB(level);
6640 static void LoadLevel_InitSettings(struct LevelInfo *level)
6642 // adjust level settings for (non-native) Sokoban-style levels
6643 LoadLevel_InitSettings_SB(level);
6645 // rename levels with title "nameless level" or if renaming is forced
6646 if (leveldir_current->empty_level_name != NULL &&
6647 (strEqual(level->name, NAMELESS_LEVEL_NAME) ||
6648 leveldir_current->force_level_name))
6649 snprintf(level->name, MAX_LEVEL_NAME_LEN + 1,
6650 leveldir_current->empty_level_name, level_nr);
6653 static void LoadLevel_InitStandardElements(struct LevelInfo *level)
6657 // map elements that have changed in newer versions
6658 level->amoeba_content = getMappedElementByVersion(level->amoeba_content,
6659 level->game_version);
6660 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
6661 for (x = 0; x < 3; x++)
6662 for (y = 0; y < 3; y++)
6663 level->yamyam_content[i].e[x][y] =
6664 getMappedElementByVersion(level->yamyam_content[i].e[x][y],
6665 level->game_version);
6669 static void LoadLevel_InitCustomElements(struct LevelInfo *level)
6673 // map custom element change events that have changed in newer versions
6674 // (these following values were accidentally changed in version 3.0.1)
6675 // (this seems to be needed only for 'ab_levelset3' and 'ab_levelset4')
6676 if (level->game_version <= VERSION_IDENT(3,0,0,0))
6678 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6680 int element = EL_CUSTOM_START + i;
6682 // order of checking and copying events to be mapped is important
6683 // (do not change the start and end value -- they are constant)
6684 for (j = CE_BY_OTHER_ACTION; j >= CE_VALUE_GETS_ZERO; j--)
6686 if (HAS_CHANGE_EVENT(element, j - 2))
6688 SET_CHANGE_EVENT(element, j - 2, FALSE);
6689 SET_CHANGE_EVENT(element, j, TRUE);
6693 // order of checking and copying events to be mapped is important
6694 // (do not change the start and end value -- they are constant)
6695 for (j = CE_PLAYER_COLLECTS_X; j >= CE_HITTING_SOMETHING; j--)
6697 if (HAS_CHANGE_EVENT(element, j - 1))
6699 SET_CHANGE_EVENT(element, j - 1, FALSE);
6700 SET_CHANGE_EVENT(element, j, TRUE);
6706 // initialize "can_change" field for old levels with only one change page
6707 if (level->game_version <= VERSION_IDENT(3,0,2,0))
6709 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6711 int element = EL_CUSTOM_START + i;
6713 if (CAN_CHANGE(element))
6714 element_info[element].change->can_change = TRUE;
6718 // correct custom element values (for old levels without these options)
6719 if (level->game_version < VERSION_IDENT(3,1,1,0))
6721 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6723 int element = EL_CUSTOM_START + i;
6724 struct ElementInfo *ei = &element_info[element];
6726 if (ei->access_direction == MV_NO_DIRECTION)
6727 ei->access_direction = MV_ALL_DIRECTIONS;
6731 // correct custom element values (fix invalid values for all versions)
6734 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6736 int element = EL_CUSTOM_START + i;
6737 struct ElementInfo *ei = &element_info[element];
6739 for (j = 0; j < ei->num_change_pages; j++)
6741 struct ElementChangeInfo *change = &ei->change_page[j];
6743 if (change->trigger_player == CH_PLAYER_NONE)
6744 change->trigger_player = CH_PLAYER_ANY;
6746 if (change->trigger_side == CH_SIDE_NONE)
6747 change->trigger_side = CH_SIDE_ANY;
6752 // initialize "can_explode" field for old levels which did not store this
6753 // !!! CHECK THIS -- "<= 3,1,0,0" IS PROBABLY WRONG !!!
6754 if (level->game_version <= VERSION_IDENT(3,1,0,0))
6756 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6758 int element = EL_CUSTOM_START + i;
6760 if (EXPLODES_1X1_OLD(element))
6761 element_info[element].explosion_type = EXPLODES_1X1;
6763 SET_PROPERTY(element, EP_CAN_EXPLODE, (EXPLODES_BY_FIRE(element) ||
6764 EXPLODES_SMASHED(element) ||
6765 EXPLODES_IMPACT(element)));
6769 // correct previously hard-coded move delay values for maze runner style
6770 if (level->game_version < VERSION_IDENT(3,1,1,0))
6772 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6774 int element = EL_CUSTOM_START + i;
6776 if (element_info[element].move_pattern & MV_MAZE_RUNNER_STYLE)
6778 // previously hard-coded and therefore ignored
6779 element_info[element].move_delay_fixed = 9;
6780 element_info[element].move_delay_random = 0;
6785 // set some other uninitialized values of custom elements in older levels
6786 if (level->game_version < VERSION_IDENT(3,1,0,0))
6788 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6790 int element = EL_CUSTOM_START + i;
6792 element_info[element].access_direction = MV_ALL_DIRECTIONS;
6794 element_info[element].explosion_delay = 17;
6795 element_info[element].ignition_delay = 8;
6799 // set mouse click change events to work for left/middle/right mouse button
6800 if (level->game_version < VERSION_IDENT(4,2,3,0))
6802 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6804 int element = EL_CUSTOM_START + i;
6805 struct ElementInfo *ei = &element_info[element];
6807 for (j = 0; j < ei->num_change_pages; j++)
6809 struct ElementChangeInfo *change = &ei->change_page[j];
6811 if (change->has_event[CE_CLICKED_BY_MOUSE] ||
6812 change->has_event[CE_PRESSED_BY_MOUSE] ||
6813 change->has_event[CE_MOUSE_CLICKED_ON_X] ||
6814 change->has_event[CE_MOUSE_PRESSED_ON_X])
6815 change->trigger_side = CH_SIDE_ANY;
6821 static void LoadLevel_InitElements(struct LevelInfo *level)
6823 LoadLevel_InitStandardElements(level);
6825 if (level->file_has_custom_elements)
6826 LoadLevel_InitCustomElements(level);
6828 // initialize element properties for level editor etc.
6829 InitElementPropertiesEngine(level->game_version);
6830 InitElementPropertiesGfxElement();
6833 static void LoadLevel_InitPlayfield(struct LevelInfo *level)
6837 // map elements that have changed in newer versions
6838 for (y = 0; y < level->fieldy; y++)
6839 for (x = 0; x < level->fieldx; x++)
6840 level->field[x][y] = getMappedElementByVersion(level->field[x][y],
6841 level->game_version);
6843 // clear unused playfield data (nicer if level gets resized in editor)
6844 for (x = 0; x < MAX_LEV_FIELDX; x++)
6845 for (y = 0; y < MAX_LEV_FIELDY; y++)
6846 if (x >= level->fieldx || y >= level->fieldy)
6847 level->field[x][y] = EL_EMPTY;
6849 // copy elements to runtime playfield array
6850 for (x = 0; x < MAX_LEV_FIELDX; x++)
6851 for (y = 0; y < MAX_LEV_FIELDY; y++)
6852 Tile[x][y] = level->field[x][y];
6854 // initialize level size variables for faster access
6855 lev_fieldx = level->fieldx;
6856 lev_fieldy = level->fieldy;
6858 // determine border element for this level
6859 if (level->file_info.type == LEVEL_FILE_TYPE_DC)
6860 BorderElement = EL_EMPTY; // (in editor, SetBorderElement() is used)
6865 static void LoadLevel_InitNativeEngines(struct LevelInfo *level)
6867 struct LevelFileInfo *level_file_info = &level->file_info;
6869 if (level_file_info->type == LEVEL_FILE_TYPE_RND)
6870 CopyNativeLevel_RND_to_Native(level);
6873 static void LoadLevelTemplate_LoadAndInit(void)
6875 LoadLevelFromFileInfo(&level_template, &level_template.file_info, FALSE);
6877 LoadLevel_InitVersion(&level_template);
6878 LoadLevel_InitElements(&level_template);
6879 LoadLevel_InitSettings(&level_template);
6881 ActivateLevelTemplate();
6884 void LoadLevelTemplate(int nr)
6886 if (!fileExists(getGlobalLevelTemplateFilename()))
6888 Warn("no level template found for this level");
6893 setLevelFileInfo(&level_template.file_info, nr);
6895 LoadLevelTemplate_LoadAndInit();
6898 static void LoadNetworkLevelTemplate(struct NetworkLevelInfo *network_level)
6900 copyLevelFileInfo(&network_level->tmpl_info, &level_template.file_info);
6902 LoadLevelTemplate_LoadAndInit();
6905 static void LoadLevel_LoadAndInit(struct NetworkLevelInfo *network_level)
6907 LoadLevelFromFileInfo(&level, &level.file_info, FALSE);
6909 if (level.use_custom_template)
6911 if (network_level != NULL)
6912 LoadNetworkLevelTemplate(network_level);
6914 LoadLevelTemplate(-1);
6917 LoadLevel_InitVersion(&level);
6918 LoadLevel_InitElements(&level);
6919 LoadLevel_InitPlayfield(&level);
6920 LoadLevel_InitSettings(&level);
6922 LoadLevel_InitNativeEngines(&level);
6925 void LoadLevel(int nr)
6927 SetLevelSetInfo(leveldir_current->identifier, nr);
6929 setLevelFileInfo(&level.file_info, nr);
6931 LoadLevel_LoadAndInit(NULL);
6934 void LoadLevelInfoOnly(int nr)
6936 setLevelFileInfo(&level.file_info, nr);
6938 LoadLevelFromFileInfo(&level, &level.file_info, TRUE);
6941 void LoadNetworkLevel(struct NetworkLevelInfo *network_level)
6943 SetLevelSetInfo(network_level->leveldir_identifier,
6944 network_level->file_info.nr);
6946 copyLevelFileInfo(&network_level->file_info, &level.file_info);
6948 LoadLevel_LoadAndInit(network_level);
6951 static int SaveLevel_VERS(FILE *file, struct LevelInfo *level)
6955 chunk_size += putFileVersion(file, level->file_version);
6956 chunk_size += putFileVersion(file, level->game_version);
6961 static int SaveLevel_DATE(FILE *file, struct LevelInfo *level)
6965 chunk_size += putFile16BitBE(file, level->creation_date.year);
6966 chunk_size += putFile8Bit(file, level->creation_date.month);
6967 chunk_size += putFile8Bit(file, level->creation_date.day);
6972 #if ENABLE_HISTORIC_CHUNKS
6973 static void SaveLevel_HEAD(FILE *file, struct LevelInfo *level)
6977 putFile8Bit(file, level->fieldx);
6978 putFile8Bit(file, level->fieldy);
6980 putFile16BitBE(file, level->time);
6981 putFile16BitBE(file, level->gems_needed);
6983 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
6984 putFile8Bit(file, level->name[i]);
6986 for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
6987 putFile8Bit(file, level->score[i]);
6989 for (i = 0; i < STD_ELEMENT_CONTENTS; i++)
6990 for (y = 0; y < 3; y++)
6991 for (x = 0; x < 3; x++)
6992 putFile8Bit(file, (level->encoding_16bit_yamyam ? EL_EMPTY :
6993 level->yamyam_content[i].e[x][y]));
6994 putFile8Bit(file, level->amoeba_speed);
6995 putFile8Bit(file, level->time_magic_wall);
6996 putFile8Bit(file, level->time_wheel);
6997 putFile8Bit(file, (level->encoding_16bit_amoeba ? EL_EMPTY :
6998 level->amoeba_content));
6999 putFile8Bit(file, (level->initial_player_stepsize == STEPSIZE_FAST ? 1 : 0));
7000 putFile8Bit(file, (level->initial_gravity ? 1 : 0));
7001 putFile8Bit(file, (level->encoding_16bit_field ? 1 : 0));
7002 putFile8Bit(file, (level->em_slippery_gems ? 1 : 0));
7004 putFile8Bit(file, (level->use_custom_template ? 1 : 0));
7006 putFile8Bit(file, (level->block_last_field ? 1 : 0));
7007 putFile8Bit(file, (level->sp_block_last_field ? 1 : 0));
7008 putFile32BitBE(file, level->can_move_into_acid_bits);
7009 putFile8Bit(file, level->dont_collide_with_bits);
7011 putFile8Bit(file, (level->use_spring_bug ? 1 : 0));
7012 putFile8Bit(file, (level->use_step_counter ? 1 : 0));
7014 putFile8Bit(file, (level->instant_relocation ? 1 : 0));
7015 putFile8Bit(file, (level->can_pass_to_walkable ? 1 : 0));
7016 putFile8Bit(file, (level->grow_into_diggable ? 1 : 0));
7018 putFile8Bit(file, level->game_engine_type);
7020 WriteUnusedBytesToFile(file, LEVEL_CHUNK_HEAD_UNUSED);
7024 static int SaveLevel_NAME(FILE *file, struct LevelInfo *level)
7029 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
7030 chunk_size += putFile8Bit(file, level->name[i]);
7035 static int SaveLevel_AUTH(FILE *file, struct LevelInfo *level)
7040 for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
7041 chunk_size += putFile8Bit(file, level->author[i]);
7046 #if ENABLE_HISTORIC_CHUNKS
7047 static int SaveLevel_BODY(FILE *file, struct LevelInfo *level)
7052 for (y = 0; y < level->fieldy; y++)
7053 for (x = 0; x < level->fieldx; x++)
7054 if (level->encoding_16bit_field)
7055 chunk_size += putFile16BitBE(file, level->field[x][y]);
7057 chunk_size += putFile8Bit(file, level->field[x][y]);
7063 static int SaveLevel_BODY(FILE *file, struct LevelInfo *level)
7068 for (y = 0; y < level->fieldy; y++)
7069 for (x = 0; x < level->fieldx; x++)
7070 chunk_size += putFile16BitBE(file, level->field[x][y]);
7075 #if ENABLE_HISTORIC_CHUNKS
7076 static void SaveLevel_CONT(FILE *file, struct LevelInfo *level)
7080 putFile8Bit(file, EL_YAMYAM);
7081 putFile8Bit(file, level->num_yamyam_contents);
7082 putFile8Bit(file, 0);
7083 putFile8Bit(file, 0);
7085 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
7086 for (y = 0; y < 3; y++)
7087 for (x = 0; x < 3; x++)
7088 if (level->encoding_16bit_field)
7089 putFile16BitBE(file, level->yamyam_content[i].e[x][y]);
7091 putFile8Bit(file, level->yamyam_content[i].e[x][y]);
7095 #if ENABLE_HISTORIC_CHUNKS
7096 static void SaveLevel_CNT2(FILE *file, struct LevelInfo *level, int element)
7099 int num_contents, content_xsize, content_ysize;
7100 int content_array[MAX_ELEMENT_CONTENTS][3][3];
7102 if (element == EL_YAMYAM)
7104 num_contents = level->num_yamyam_contents;
7108 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
7109 for (y = 0; y < 3; y++)
7110 for (x = 0; x < 3; x++)
7111 content_array[i][x][y] = level->yamyam_content[i].e[x][y];
7113 else if (element == EL_BD_AMOEBA)
7119 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
7120 for (y = 0; y < 3; y++)
7121 for (x = 0; x < 3; x++)
7122 content_array[i][x][y] = EL_EMPTY;
7123 content_array[0][0][0] = level->amoeba_content;
7127 // chunk header already written -- write empty chunk data
7128 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_SIZE);
7130 Warn("cannot save content for element '%d'", element);
7135 putFile16BitBE(file, element);
7136 putFile8Bit(file, num_contents);
7137 putFile8Bit(file, content_xsize);
7138 putFile8Bit(file, content_ysize);
7140 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_UNUSED);
7142 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
7143 for (y = 0; y < 3; y++)
7144 for (x = 0; x < 3; x++)
7145 putFile16BitBE(file, content_array[i][x][y]);
7149 #if ENABLE_HISTORIC_CHUNKS
7150 static int SaveLevel_CNT3(FILE *file, struct LevelInfo *level, int element)
7152 int envelope_nr = element - EL_ENVELOPE_1;
7153 int envelope_len = strlen(level->envelope_text[envelope_nr]) + 1;
7157 chunk_size += putFile16BitBE(file, element);
7158 chunk_size += putFile16BitBE(file, envelope_len);
7159 chunk_size += putFile8Bit(file, level->envelope_xsize[envelope_nr]);
7160 chunk_size += putFile8Bit(file, level->envelope_ysize[envelope_nr]);
7162 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT3_UNUSED);
7163 chunk_size += LEVEL_CHUNK_CNT3_UNUSED;
7165 for (i = 0; i < envelope_len; i++)
7166 chunk_size += putFile8Bit(file, level->envelope_text[envelope_nr][i]);
7172 #if ENABLE_HISTORIC_CHUNKS
7173 static void SaveLevel_CUS1(FILE *file, struct LevelInfo *level,
7174 int num_changed_custom_elements)
7178 putFile16BitBE(file, num_changed_custom_elements);
7180 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7182 int element = EL_CUSTOM_START + i;
7184 struct ElementInfo *ei = &element_info[element];
7186 if (ei->properties[EP_BITFIELD_BASE_NR] != EP_BITMASK_DEFAULT)
7188 if (check < num_changed_custom_elements)
7190 putFile16BitBE(file, element);
7191 putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
7198 if (check != num_changed_custom_elements) // should not happen
7199 Warn("inconsistent number of custom element properties");
7203 #if ENABLE_HISTORIC_CHUNKS
7204 static void SaveLevel_CUS2(FILE *file, struct LevelInfo *level,
7205 int num_changed_custom_elements)
7209 putFile16BitBE(file, num_changed_custom_elements);
7211 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7213 int element = EL_CUSTOM_START + i;
7215 if (element_info[element].change->target_element != EL_EMPTY_SPACE)
7217 if (check < num_changed_custom_elements)
7219 putFile16BitBE(file, element);
7220 putFile16BitBE(file, element_info[element].change->target_element);
7227 if (check != num_changed_custom_elements) // should not happen
7228 Warn("inconsistent number of custom target elements");
7232 #if ENABLE_HISTORIC_CHUNKS
7233 static void SaveLevel_CUS3(FILE *file, struct LevelInfo *level,
7234 int num_changed_custom_elements)
7236 int i, j, x, y, check = 0;
7238 putFile16BitBE(file, num_changed_custom_elements);
7240 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7242 int element = EL_CUSTOM_START + i;
7243 struct ElementInfo *ei = &element_info[element];
7245 if (ei->modified_settings)
7247 if (check < num_changed_custom_elements)
7249 putFile16BitBE(file, element);
7251 for (j = 0; j < MAX_ELEMENT_NAME_LEN; j++)
7252 putFile8Bit(file, ei->description[j]);
7254 putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
7256 // some free bytes for future properties and padding
7257 WriteUnusedBytesToFile(file, 7);
7259 putFile8Bit(file, ei->use_gfx_element);
7260 putFile16BitBE(file, ei->gfx_element_initial);
7262 putFile8Bit(file, ei->collect_score_initial);
7263 putFile8Bit(file, ei->collect_count_initial);
7265 putFile16BitBE(file, ei->push_delay_fixed);
7266 putFile16BitBE(file, ei->push_delay_random);
7267 putFile16BitBE(file, ei->move_delay_fixed);
7268 putFile16BitBE(file, ei->move_delay_random);
7270 putFile16BitBE(file, ei->move_pattern);
7271 putFile8Bit(file, ei->move_direction_initial);
7272 putFile8Bit(file, ei->move_stepsize);
7274 for (y = 0; y < 3; y++)
7275 for (x = 0; x < 3; x++)
7276 putFile16BitBE(file, ei->content.e[x][y]);
7278 putFile32BitBE(file, ei->change->events);
7280 putFile16BitBE(file, ei->change->target_element);
7282 putFile16BitBE(file, ei->change->delay_fixed);
7283 putFile16BitBE(file, ei->change->delay_random);
7284 putFile16BitBE(file, ei->change->delay_frames);
7286 putFile16BitBE(file, ei->change->initial_trigger_element);
7288 putFile8Bit(file, ei->change->explode);
7289 putFile8Bit(file, ei->change->use_target_content);
7290 putFile8Bit(file, ei->change->only_if_complete);
7291 putFile8Bit(file, ei->change->use_random_replace);
7293 putFile8Bit(file, ei->change->random_percentage);
7294 putFile8Bit(file, ei->change->replace_when);
7296 for (y = 0; y < 3; y++)
7297 for (x = 0; x < 3; x++)
7298 putFile16BitBE(file, ei->change->content.e[x][y]);
7300 putFile8Bit(file, ei->slippery_type);
7302 // some free bytes for future properties and padding
7303 WriteUnusedBytesToFile(file, LEVEL_CPART_CUS3_UNUSED);
7310 if (check != num_changed_custom_elements) // should not happen
7311 Warn("inconsistent number of custom element properties");
7315 #if ENABLE_HISTORIC_CHUNKS
7316 static void SaveLevel_CUS4(FILE *file, struct LevelInfo *level, int element)
7318 struct ElementInfo *ei = &element_info[element];
7321 // ---------- custom element base property values (96 bytes) ----------------
7323 putFile16BitBE(file, element);
7325 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
7326 putFile8Bit(file, ei->description[i]);
7328 putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
7330 WriteUnusedBytesToFile(file, 4); // reserved for more base properties
7332 putFile8Bit(file, ei->num_change_pages);
7334 putFile16BitBE(file, ei->ce_value_fixed_initial);
7335 putFile16BitBE(file, ei->ce_value_random_initial);
7336 putFile8Bit(file, ei->use_last_ce_value);
7338 putFile8Bit(file, ei->use_gfx_element);
7339 putFile16BitBE(file, ei->gfx_element_initial);
7341 putFile8Bit(file, ei->collect_score_initial);
7342 putFile8Bit(file, ei->collect_count_initial);
7344 putFile8Bit(file, ei->drop_delay_fixed);
7345 putFile8Bit(file, ei->push_delay_fixed);
7346 putFile8Bit(file, ei->drop_delay_random);
7347 putFile8Bit(file, ei->push_delay_random);
7348 putFile16BitBE(file, ei->move_delay_fixed);
7349 putFile16BitBE(file, ei->move_delay_random);
7351 // bits 0 - 15 of "move_pattern" ...
7352 putFile16BitBE(file, ei->move_pattern & 0xffff);
7353 putFile8Bit(file, ei->move_direction_initial);
7354 putFile8Bit(file, ei->move_stepsize);
7356 putFile8Bit(file, ei->slippery_type);
7358 for (y = 0; y < 3; y++)
7359 for (x = 0; x < 3; x++)
7360 putFile16BitBE(file, ei->content.e[x][y]);
7362 putFile16BitBE(file, ei->move_enter_element);
7363 putFile16BitBE(file, ei->move_leave_element);
7364 putFile8Bit(file, ei->move_leave_type);
7366 // ... bits 16 - 31 of "move_pattern" (not nice, but downward compatible)
7367 putFile16BitBE(file, (ei->move_pattern >> 16) & 0xffff);
7369 putFile8Bit(file, ei->access_direction);
7371 putFile8Bit(file, ei->explosion_delay);
7372 putFile8Bit(file, ei->ignition_delay);
7373 putFile8Bit(file, ei->explosion_type);
7375 // some free bytes for future custom property values and padding
7376 WriteUnusedBytesToFile(file, 1);
7378 // ---------- change page property values (48 bytes) ------------------------
7380 for (i = 0; i < ei->num_change_pages; i++)
7382 struct ElementChangeInfo *change = &ei->change_page[i];
7383 unsigned int event_bits;
7385 // bits 0 - 31 of "has_event[]" ...
7387 for (j = 0; j < MIN(NUM_CHANGE_EVENTS, 32); j++)
7388 if (change->has_event[j])
7389 event_bits |= (1u << j);
7390 putFile32BitBE(file, event_bits);
7392 putFile16BitBE(file, change->target_element);
7394 putFile16BitBE(file, change->delay_fixed);
7395 putFile16BitBE(file, change->delay_random);
7396 putFile16BitBE(file, change->delay_frames);
7398 putFile16BitBE(file, change->initial_trigger_element);
7400 putFile8Bit(file, change->explode);
7401 putFile8Bit(file, change->use_target_content);
7402 putFile8Bit(file, change->only_if_complete);
7403 putFile8Bit(file, change->use_random_replace);
7405 putFile8Bit(file, change->random_percentage);
7406 putFile8Bit(file, change->replace_when);
7408 for (y = 0; y < 3; y++)
7409 for (x = 0; x < 3; x++)
7410 putFile16BitBE(file, change->target_content.e[x][y]);
7412 putFile8Bit(file, change->can_change);
7414 putFile8Bit(file, change->trigger_side);
7416 putFile8Bit(file, change->trigger_player);
7417 putFile8Bit(file, (change->trigger_page == CH_PAGE_ANY ? CH_PAGE_ANY_FILE :
7418 log_2(change->trigger_page)));
7420 putFile8Bit(file, change->has_action);
7421 putFile8Bit(file, change->action_type);
7422 putFile8Bit(file, change->action_mode);
7423 putFile16BitBE(file, change->action_arg);
7425 // ... bits 32 - 39 of "has_event[]" (not nice, but downward compatible)
7427 for (j = 32; j < NUM_CHANGE_EVENTS; j++)
7428 if (change->has_event[j])
7429 event_bits |= (1u << (j - 32));
7430 putFile8Bit(file, event_bits);
7435 #if ENABLE_HISTORIC_CHUNKS
7436 static void SaveLevel_GRP1(FILE *file, struct LevelInfo *level, int element)
7438 struct ElementInfo *ei = &element_info[element];
7439 struct ElementGroupInfo *group = ei->group;
7442 putFile16BitBE(file, element);
7444 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
7445 putFile8Bit(file, ei->description[i]);
7447 putFile8Bit(file, group->num_elements);
7449 putFile8Bit(file, ei->use_gfx_element);
7450 putFile16BitBE(file, ei->gfx_element_initial);
7452 putFile8Bit(file, group->choice_mode);
7454 // some free bytes for future values and padding
7455 WriteUnusedBytesToFile(file, 3);
7457 for (i = 0; i < MAX_ELEMENTS_IN_GROUP; i++)
7458 putFile16BitBE(file, group->element[i]);
7462 static int SaveLevel_MicroChunk(FILE *file, struct LevelFileConfigInfo *entry,
7463 boolean write_element)
7465 int save_type = entry->save_type;
7466 int data_type = entry->data_type;
7467 int conf_type = entry->conf_type;
7468 int byte_mask = conf_type & CONF_MASK_BYTES;
7469 int element = entry->element;
7470 int default_value = entry->default_value;
7472 boolean modified = FALSE;
7474 if (byte_mask != CONF_MASK_MULTI_BYTES)
7476 void *value_ptr = entry->value;
7477 int value = (data_type == TYPE_BOOLEAN ? *(boolean *)value_ptr :
7480 // check if any settings have been modified before saving them
7481 if (value != default_value)
7484 // do not save if explicitly told or if unmodified default settings
7485 if ((save_type == SAVE_CONF_NEVER) ||
7486 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
7490 num_bytes += putFile16BitBE(file, element);
7492 num_bytes += putFile8Bit(file, conf_type);
7493 num_bytes += (byte_mask == CONF_MASK_1_BYTE ? putFile8Bit (file, value) :
7494 byte_mask == CONF_MASK_2_BYTE ? putFile16BitBE(file, value) :
7495 byte_mask == CONF_MASK_4_BYTE ? putFile32BitBE(file, value) :
7498 else if (data_type == TYPE_STRING)
7500 char *default_string = entry->default_string;
7501 char *string = (char *)(entry->value);
7502 int string_length = strlen(string);
7505 // check if any settings have been modified before saving them
7506 if (!strEqual(string, default_string))
7509 // do not save if explicitly told or if unmodified default settings
7510 if ((save_type == SAVE_CONF_NEVER) ||
7511 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
7515 num_bytes += putFile16BitBE(file, element);
7517 num_bytes += putFile8Bit(file, conf_type);
7518 num_bytes += putFile16BitBE(file, string_length);
7520 for (i = 0; i < string_length; i++)
7521 num_bytes += putFile8Bit(file, string[i]);
7523 else if (data_type == TYPE_ELEMENT_LIST)
7525 int *element_array = (int *)(entry->value);
7526 int num_elements = *(int *)(entry->num_entities);
7529 // check if any settings have been modified before saving them
7530 for (i = 0; i < num_elements; i++)
7531 if (element_array[i] != default_value)
7534 // do not save if explicitly told or if unmodified default settings
7535 if ((save_type == SAVE_CONF_NEVER) ||
7536 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
7540 num_bytes += putFile16BitBE(file, element);
7542 num_bytes += putFile8Bit(file, conf_type);
7543 num_bytes += putFile16BitBE(file, num_elements * CONF_ELEMENT_NUM_BYTES);
7545 for (i = 0; i < num_elements; i++)
7546 num_bytes += putFile16BitBE(file, element_array[i]);
7548 else if (data_type == TYPE_CONTENT_LIST)
7550 struct Content *content = (struct Content *)(entry->value);
7551 int num_contents = *(int *)(entry->num_entities);
7554 // check if any settings have been modified before saving them
7555 for (i = 0; i < num_contents; i++)
7556 for (y = 0; y < 3; y++)
7557 for (x = 0; x < 3; x++)
7558 if (content[i].e[x][y] != default_value)
7561 // do not save if explicitly told or if unmodified default settings
7562 if ((save_type == SAVE_CONF_NEVER) ||
7563 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
7567 num_bytes += putFile16BitBE(file, element);
7569 num_bytes += putFile8Bit(file, conf_type);
7570 num_bytes += putFile16BitBE(file, num_contents * CONF_CONTENT_NUM_BYTES);
7572 for (i = 0; i < num_contents; i++)
7573 for (y = 0; y < 3; y++)
7574 for (x = 0; x < 3; x++)
7575 num_bytes += putFile16BitBE(file, content[i].e[x][y]);
7581 static int SaveLevel_INFO(FILE *file, struct LevelInfo *level)
7586 li = *level; // copy level data into temporary buffer
7588 for (i = 0; chunk_config_INFO[i].data_type != -1; i++)
7589 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_INFO[i], FALSE);
7594 static int SaveLevel_ELEM(FILE *file, struct LevelInfo *level)
7599 li = *level; // copy level data into temporary buffer
7601 for (i = 0; chunk_config_ELEM[i].data_type != -1; i++)
7602 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_ELEM[i], TRUE);
7607 static int SaveLevel_NOTE(FILE *file, struct LevelInfo *level, int element)
7609 int envelope_nr = element - EL_ENVELOPE_1;
7613 chunk_size += putFile16BitBE(file, element);
7615 // copy envelope data into temporary buffer
7616 xx_envelope = level->envelope[envelope_nr];
7618 for (i = 0; chunk_config_NOTE[i].data_type != -1; i++)
7619 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_NOTE[i], FALSE);
7624 static int SaveLevel_CUSX(FILE *file, struct LevelInfo *level, int element)
7626 struct ElementInfo *ei = &element_info[element];
7630 chunk_size += putFile16BitBE(file, element);
7632 xx_ei = *ei; // copy element data into temporary buffer
7634 // set default description string for this specific element
7635 strcpy(xx_default_description, getDefaultElementDescription(ei));
7637 for (i = 0; chunk_config_CUSX_base[i].data_type != -1; i++)
7638 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_CUSX_base[i], FALSE);
7640 for (i = 0; i < ei->num_change_pages; i++)
7642 struct ElementChangeInfo *change = &ei->change_page[i];
7644 xx_current_change_page = i;
7646 xx_change = *change; // copy change data into temporary buffer
7649 setEventBitsFromEventFlags(change);
7651 for (j = 0; chunk_config_CUSX_change[j].data_type != -1; j++)
7652 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_CUSX_change[j],
7659 static int SaveLevel_GRPX(FILE *file, struct LevelInfo *level, int element)
7661 struct ElementInfo *ei = &element_info[element];
7662 struct ElementGroupInfo *group = ei->group;
7666 chunk_size += putFile16BitBE(file, element);
7668 xx_ei = *ei; // copy element data into temporary buffer
7669 xx_group = *group; // copy group data into temporary buffer
7671 // set default description string for this specific element
7672 strcpy(xx_default_description, getDefaultElementDescription(ei));
7674 for (i = 0; chunk_config_GRPX[i].data_type != -1; i++)
7675 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_GRPX[i], FALSE);
7680 static int SaveLevel_EMPX(FILE *file, struct LevelInfo *level, int element)
7682 struct ElementInfo *ei = &element_info[element];
7686 chunk_size += putFile16BitBE(file, element);
7688 xx_ei = *ei; // copy element data into temporary buffer
7690 for (i = 0; chunk_config_EMPX[i].data_type != -1; i++)
7691 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_EMPX[i], FALSE);
7696 static void SaveLevelFromFilename(struct LevelInfo *level, char *filename,
7697 boolean save_as_template)
7703 if (!(file = fopen(filename, MODE_WRITE)))
7705 Warn("cannot save level file '%s'", filename);
7710 level->file_version = FILE_VERSION_ACTUAL;
7711 level->game_version = GAME_VERSION_ACTUAL;
7713 level->creation_date = getCurrentDate();
7715 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
7716 putFileChunkBE(file, "CAVE", CHUNK_SIZE_NONE);
7718 chunk_size = SaveLevel_VERS(NULL, level);
7719 putFileChunkBE(file, "VERS", chunk_size);
7720 SaveLevel_VERS(file, level);
7722 chunk_size = SaveLevel_DATE(NULL, level);
7723 putFileChunkBE(file, "DATE", chunk_size);
7724 SaveLevel_DATE(file, level);
7726 chunk_size = SaveLevel_NAME(NULL, level);
7727 putFileChunkBE(file, "NAME", chunk_size);
7728 SaveLevel_NAME(file, level);
7730 chunk_size = SaveLevel_AUTH(NULL, level);
7731 putFileChunkBE(file, "AUTH", chunk_size);
7732 SaveLevel_AUTH(file, level);
7734 chunk_size = SaveLevel_INFO(NULL, level);
7735 putFileChunkBE(file, "INFO", chunk_size);
7736 SaveLevel_INFO(file, level);
7738 chunk_size = SaveLevel_BODY(NULL, level);
7739 putFileChunkBE(file, "BODY", chunk_size);
7740 SaveLevel_BODY(file, level);
7742 chunk_size = SaveLevel_ELEM(NULL, level);
7743 if (chunk_size > LEVEL_CHUNK_ELEM_UNCHANGED) // save if changed
7745 putFileChunkBE(file, "ELEM", chunk_size);
7746 SaveLevel_ELEM(file, level);
7749 for (i = 0; i < NUM_ENVELOPES; i++)
7751 int element = EL_ENVELOPE_1 + i;
7753 chunk_size = SaveLevel_NOTE(NULL, level, element);
7754 if (chunk_size > LEVEL_CHUNK_NOTE_UNCHANGED) // save if changed
7756 putFileChunkBE(file, "NOTE", chunk_size);
7757 SaveLevel_NOTE(file, level, element);
7761 // if not using template level, check for non-default custom/group elements
7762 if (!level->use_custom_template || save_as_template)
7764 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7766 int element = EL_CUSTOM_START + i;
7768 chunk_size = SaveLevel_CUSX(NULL, level, element);
7769 if (chunk_size > LEVEL_CHUNK_CUSX_UNCHANGED) // save if changed
7771 putFileChunkBE(file, "CUSX", chunk_size);
7772 SaveLevel_CUSX(file, level, element);
7776 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
7778 int element = EL_GROUP_START + i;
7780 chunk_size = SaveLevel_GRPX(NULL, level, element);
7781 if (chunk_size > LEVEL_CHUNK_GRPX_UNCHANGED) // save if changed
7783 putFileChunkBE(file, "GRPX", chunk_size);
7784 SaveLevel_GRPX(file, level, element);
7788 for (i = 0; i < NUM_EMPTY_ELEMENTS_ALL; i++)
7790 int element = GET_EMPTY_ELEMENT(i);
7792 chunk_size = SaveLevel_EMPX(NULL, level, element);
7793 if (chunk_size > LEVEL_CHUNK_EMPX_UNCHANGED) // save if changed
7795 putFileChunkBE(file, "EMPX", chunk_size);
7796 SaveLevel_EMPX(file, level, element);
7803 SetFilePermissions(filename, PERMS_PRIVATE);
7806 void SaveLevel(int nr)
7808 char *filename = getDefaultLevelFilename(nr);
7810 SaveLevelFromFilename(&level, filename, FALSE);
7813 void SaveLevelTemplate(void)
7815 char *filename = getLocalLevelTemplateFilename();
7817 SaveLevelFromFilename(&level, filename, TRUE);
7820 boolean SaveLevelChecked(int nr)
7822 char *filename = getDefaultLevelFilename(nr);
7823 boolean new_level = !fileExists(filename);
7824 boolean level_saved = FALSE;
7826 if (new_level || Request("Save this level and kill the old?", REQ_ASK))
7831 Request("Level saved!", REQ_CONFIRM);
7839 void DumpLevel(struct LevelInfo *level)
7841 if (level->no_level_file || level->no_valid_file)
7843 Warn("cannot dump -- no valid level file found");
7849 Print("Level xxx (file version %08d, game version %08d)\n",
7850 level->file_version, level->game_version);
7853 Print("Level author: '%s'\n", level->author);
7854 Print("Level title: '%s'\n", level->name);
7856 Print("Playfield size: %d x %d\n", level->fieldx, level->fieldy);
7858 Print("Level time: %d seconds\n", level->time);
7859 Print("Gems needed: %d\n", level->gems_needed);
7861 Print("Time for magic wall: %d seconds\n", level->time_magic_wall);
7862 Print("Time for wheel: %d seconds\n", level->time_wheel);
7863 Print("Time for light: %d seconds\n", level->time_light);
7864 Print("Time for timegate: %d seconds\n", level->time_timegate);
7866 Print("Amoeba speed: %d\n", level->amoeba_speed);
7869 Print("EM style slippery gems: %s\n", (level->em_slippery_gems ? "yes" : "no"));
7870 Print("Player blocks last field: %s\n", (level->block_last_field ? "yes" : "no"));
7871 Print("SP player blocks last field: %s\n", (level->sp_block_last_field ? "yes" : "no"));
7872 Print("use spring bug: %s\n", (level->use_spring_bug ? "yes" : "no"));
7873 Print("use step counter: %s\n", (level->use_step_counter ? "yes" : "no"));
7874 Print("rate time over score: %s\n", (level->rate_time_over_score ? "yes" : "no"));
7880 for (i = 0; i < NUM_ENVELOPES; i++)
7882 char *text = level->envelope[i].text;
7883 int text_len = strlen(text);
7884 boolean has_text = FALSE;
7886 for (j = 0; j < text_len; j++)
7887 if (text[j] != ' ' && text[j] != '\n')
7893 Print("Envelope %d:\n'%s'\n", i + 1, text);
7901 void DumpLevels(void)
7903 static LevelDirTree *dumplevel_leveldir = NULL;
7905 dumplevel_leveldir = getTreeInfoFromIdentifier(leveldir_first,
7906 global.dumplevel_leveldir);
7908 if (dumplevel_leveldir == NULL)
7909 Fail("no such level identifier: '%s'", global.dumplevel_leveldir);
7911 if (global.dumplevel_level_nr < dumplevel_leveldir->first_level ||
7912 global.dumplevel_level_nr > dumplevel_leveldir->last_level)
7913 Fail("no such level number: %d", global.dumplevel_level_nr);
7915 leveldir_current = dumplevel_leveldir;
7917 LoadLevel(global.dumplevel_level_nr);
7924 // ============================================================================
7925 // tape file functions
7926 // ============================================================================
7928 static void setTapeInfoToDefaults(void)
7932 // always start with reliable default values (empty tape)
7935 // default values (also for pre-1.2 tapes) with only the first player
7936 tape.player_participates[0] = TRUE;
7937 for (i = 1; i < MAX_PLAYERS; i++)
7938 tape.player_participates[i] = FALSE;
7940 // at least one (default: the first) player participates in every tape
7941 tape.num_participating_players = 1;
7943 tape.property_bits = TAPE_PROPERTY_NONE;
7945 tape.level_nr = level_nr;
7947 tape.changed = FALSE;
7948 tape.solved = FALSE;
7950 tape.recording = FALSE;
7951 tape.playing = FALSE;
7952 tape.pausing = FALSE;
7954 tape.scr_fieldx = SCR_FIELDX_DEFAULT;
7955 tape.scr_fieldy = SCR_FIELDY_DEFAULT;
7957 tape.no_info_chunk = TRUE;
7958 tape.no_valid_file = FALSE;
7961 static int getTapePosSize(struct TapeInfo *tape)
7963 int tape_pos_size = 0;
7965 if (tape->use_key_actions)
7966 tape_pos_size += tape->num_participating_players;
7968 if (tape->use_mouse_actions)
7969 tape_pos_size += 3; // x and y position and mouse button mask
7971 tape_pos_size += 1; // tape action delay value
7973 return tape_pos_size;
7976 static void setTapeActionFlags(struct TapeInfo *tape, int value)
7978 tape->use_key_actions = FALSE;
7979 tape->use_mouse_actions = FALSE;
7981 if (value != TAPE_USE_MOUSE_ACTIONS_ONLY)
7982 tape->use_key_actions = TRUE;
7984 if (value != TAPE_USE_KEY_ACTIONS_ONLY)
7985 tape->use_mouse_actions = TRUE;
7988 static int getTapeActionValue(struct TapeInfo *tape)
7990 return (tape->use_key_actions &&
7991 tape->use_mouse_actions ? TAPE_USE_KEY_AND_MOUSE_ACTIONS :
7992 tape->use_key_actions ? TAPE_USE_KEY_ACTIONS_ONLY :
7993 tape->use_mouse_actions ? TAPE_USE_MOUSE_ACTIONS_ONLY :
7994 TAPE_ACTIONS_DEFAULT);
7997 static int LoadTape_VERS(File *file, int chunk_size, struct TapeInfo *tape)
7999 tape->file_version = getFileVersion(file);
8000 tape->game_version = getFileVersion(file);
8005 static int LoadTape_HEAD(File *file, int chunk_size, struct TapeInfo *tape)
8009 tape->random_seed = getFile32BitBE(file);
8010 tape->date = getFile32BitBE(file);
8011 tape->length = getFile32BitBE(file);
8013 // read header fields that are new since version 1.2
8014 if (tape->file_version >= FILE_VERSION_1_2)
8016 byte store_participating_players = getFile8Bit(file);
8019 // since version 1.2, tapes store which players participate in the tape
8020 tape->num_participating_players = 0;
8021 for (i = 0; i < MAX_PLAYERS; i++)
8023 tape->player_participates[i] = FALSE;
8025 if (store_participating_players & (1 << i))
8027 tape->player_participates[i] = TRUE;
8028 tape->num_participating_players++;
8032 setTapeActionFlags(tape, getFile8Bit(file));
8034 tape->property_bits = getFile8Bit(file);
8035 tape->solved = getFile8Bit(file);
8037 engine_version = getFileVersion(file);
8038 if (engine_version > 0)
8039 tape->engine_version = engine_version;
8041 tape->engine_version = tape->game_version;
8047 static int LoadTape_SCRN(File *file, int chunk_size, struct TapeInfo *tape)
8049 tape->scr_fieldx = getFile8Bit(file);
8050 tape->scr_fieldy = getFile8Bit(file);
8055 static int LoadTape_INFO(File *file, int chunk_size, struct TapeInfo *tape)
8057 char *level_identifier = NULL;
8058 int level_identifier_size;
8061 tape->no_info_chunk = FALSE;
8063 level_identifier_size = getFile16BitBE(file);
8065 level_identifier = checked_malloc(level_identifier_size);
8067 for (i = 0; i < level_identifier_size; i++)
8068 level_identifier[i] = getFile8Bit(file);
8070 strncpy(tape->level_identifier, level_identifier, MAX_FILENAME_LEN);
8071 tape->level_identifier[MAX_FILENAME_LEN] = '\0';
8073 checked_free(level_identifier);
8075 tape->level_nr = getFile16BitBE(file);
8077 chunk_size = 2 + level_identifier_size + 2;
8082 static int LoadTape_BODY(File *file, int chunk_size, struct TapeInfo *tape)
8085 int tape_pos_size = getTapePosSize(tape);
8086 int chunk_size_expected = tape_pos_size * tape->length;
8088 if (chunk_size_expected != chunk_size)
8090 ReadUnusedBytesFromFile(file, chunk_size);
8091 return chunk_size_expected;
8094 for (i = 0; i < tape->length; i++)
8096 if (i >= MAX_TAPE_LEN)
8098 Warn("tape truncated -- size exceeds maximum tape size %d",
8101 // tape too large; read and ignore remaining tape data from this chunk
8102 for (;i < tape->length; i++)
8103 ReadUnusedBytesFromFile(file, tape_pos_size);
8108 if (tape->use_key_actions)
8110 for (j = 0; j < MAX_PLAYERS; j++)
8112 tape->pos[i].action[j] = MV_NONE;
8114 if (tape->player_participates[j])
8115 tape->pos[i].action[j] = getFile8Bit(file);
8119 if (tape->use_mouse_actions)
8121 tape->pos[i].action[TAPE_ACTION_LX] = getFile8Bit(file);
8122 tape->pos[i].action[TAPE_ACTION_LY] = getFile8Bit(file);
8123 tape->pos[i].action[TAPE_ACTION_BUTTON] = getFile8Bit(file);
8126 tape->pos[i].delay = getFile8Bit(file);
8128 if (tape->file_version == FILE_VERSION_1_0)
8130 // eliminate possible diagonal moves in old tapes
8131 // this is only for backward compatibility
8133 byte joy_dir[4] = { JOY_LEFT, JOY_RIGHT, JOY_UP, JOY_DOWN };
8134 byte action = tape->pos[i].action[0];
8135 int k, num_moves = 0;
8137 for (k = 0; k<4; k++)
8139 if (action & joy_dir[k])
8141 tape->pos[i + num_moves].action[0] = joy_dir[k];
8143 tape->pos[i + num_moves].delay = 0;
8152 tape->length += num_moves;
8155 else if (tape->file_version < FILE_VERSION_2_0)
8157 // convert pre-2.0 tapes to new tape format
8159 if (tape->pos[i].delay > 1)
8162 tape->pos[i + 1] = tape->pos[i];
8163 tape->pos[i + 1].delay = 1;
8166 for (j = 0; j < MAX_PLAYERS; j++)
8167 tape->pos[i].action[j] = MV_NONE;
8168 tape->pos[i].delay--;
8175 if (checkEndOfFile(file))
8179 if (i != tape->length)
8180 chunk_size = tape_pos_size * i;
8185 static void LoadTape_SokobanSolution(char *filename)
8188 int move_delay = TILESIZE / level.initial_player_stepsize[0];
8190 if (!(file = openFile(filename, MODE_READ)))
8192 tape.no_valid_file = TRUE;
8197 while (!checkEndOfFile(file))
8199 unsigned char c = getByteFromFile(file);
8201 if (checkEndOfFile(file))
8208 tape.pos[tape.length].action[0] = MV_UP;
8209 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
8215 tape.pos[tape.length].action[0] = MV_DOWN;
8216 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
8222 tape.pos[tape.length].action[0] = MV_LEFT;
8223 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
8229 tape.pos[tape.length].action[0] = MV_RIGHT;
8230 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
8238 // ignore white-space characters
8242 tape.no_valid_file = TRUE;
8244 Warn("unsupported Sokoban solution file '%s' ['%d']", filename, c);
8252 if (tape.no_valid_file)
8255 tape.length_frames = GetTapeLengthFrames();
8256 tape.length_seconds = GetTapeLengthSeconds();
8259 void LoadTapeFromFilename(char *filename)
8261 char cookie[MAX_LINE_LEN];
8262 char chunk_name[CHUNK_ID_LEN + 1];
8266 // always start with reliable default values
8267 setTapeInfoToDefaults();
8269 if (strSuffix(filename, ".sln"))
8271 LoadTape_SokobanSolution(filename);
8276 if (!(file = openFile(filename, MODE_READ)))
8278 tape.no_valid_file = TRUE;
8283 getFileChunkBE(file, chunk_name, NULL);
8284 if (strEqual(chunk_name, "RND1"))
8286 getFile32BitBE(file); // not used
8288 getFileChunkBE(file, chunk_name, NULL);
8289 if (!strEqual(chunk_name, "TAPE"))
8291 tape.no_valid_file = TRUE;
8293 Warn("unknown format of tape file '%s'", filename);
8300 else // check for pre-2.0 file format with cookie string
8302 strcpy(cookie, chunk_name);
8303 if (getStringFromFile(file, &cookie[4], MAX_LINE_LEN - 4) == NULL)
8305 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
8306 cookie[strlen(cookie) - 1] = '\0';
8308 if (!checkCookieString(cookie, TAPE_COOKIE_TMPL))
8310 tape.no_valid_file = TRUE;
8312 Warn("unknown format of tape file '%s'", filename);
8319 if ((tape.file_version = getFileVersionFromCookieString(cookie)) == -1)
8321 tape.no_valid_file = TRUE;
8323 Warn("unsupported version of tape file '%s'", filename);
8330 // pre-2.0 tape files have no game version, so use file version here
8331 tape.game_version = tape.file_version;
8334 if (tape.file_version < FILE_VERSION_1_2)
8336 // tape files from versions before 1.2.0 without chunk structure
8337 LoadTape_HEAD(file, TAPE_CHUNK_HEAD_SIZE, &tape);
8338 LoadTape_BODY(file, 2 * tape.length, &tape);
8346 int (*loader)(File *, int, struct TapeInfo *);
8350 { "VERS", TAPE_CHUNK_VERS_SIZE, LoadTape_VERS },
8351 { "HEAD", TAPE_CHUNK_HEAD_SIZE, LoadTape_HEAD },
8352 { "SCRN", TAPE_CHUNK_SCRN_SIZE, LoadTape_SCRN },
8353 { "INFO", -1, LoadTape_INFO },
8354 { "BODY", -1, LoadTape_BODY },
8358 while (getFileChunkBE(file, chunk_name, &chunk_size))
8362 while (chunk_info[i].name != NULL &&
8363 !strEqual(chunk_name, chunk_info[i].name))
8366 if (chunk_info[i].name == NULL)
8368 Warn("unknown chunk '%s' in tape file '%s'",
8369 chunk_name, filename);
8371 ReadUnusedBytesFromFile(file, chunk_size);
8373 else if (chunk_info[i].size != -1 &&
8374 chunk_info[i].size != chunk_size)
8376 Warn("wrong size (%d) of chunk '%s' in tape file '%s'",
8377 chunk_size, chunk_name, filename);
8379 ReadUnusedBytesFromFile(file, chunk_size);
8383 // call function to load this tape chunk
8384 int chunk_size_expected =
8385 (chunk_info[i].loader)(file, chunk_size, &tape);
8387 // the size of some chunks cannot be checked before reading other
8388 // chunks first (like "HEAD" and "BODY") that contain some header
8389 // information, so check them here
8390 if (chunk_size_expected != chunk_size)
8392 Warn("wrong size (%d) of chunk '%s' in tape file '%s'",
8393 chunk_size, chunk_name, filename);
8401 tape.length_frames = GetTapeLengthFrames();
8402 tape.length_seconds = GetTapeLengthSeconds();
8405 Debug("files:LoadTapeFromFilename", "tape file version: %d",
8407 Debug("files:LoadTapeFromFilename", "tape game version: %d",
8409 Debug("files:LoadTapeFromFilename", "tape engine version: %d",
8410 tape.engine_version);
8414 void LoadTape(int nr)
8416 char *filename = getTapeFilename(nr);
8418 LoadTapeFromFilename(filename);
8421 void LoadSolutionTape(int nr)
8423 char *filename = getSolutionTapeFilename(nr);
8425 LoadTapeFromFilename(filename);
8427 if (TAPE_IS_EMPTY(tape) &&
8428 level.game_engine_type == GAME_ENGINE_TYPE_SP &&
8429 level.native_sp_level->demo.is_available)
8430 CopyNativeTape_SP_to_RND(&level);
8433 void LoadScoreTape(char *score_tape_basename, int nr)
8435 char *filename = getScoreTapeFilename(score_tape_basename, nr);
8437 LoadTapeFromFilename(filename);
8440 void LoadScoreCacheTape(char *score_tape_basename, int nr)
8442 char *filename = getScoreCacheTapeFilename(score_tape_basename, nr);
8444 LoadTapeFromFilename(filename);
8447 static boolean checkSaveTape_SCRN(struct TapeInfo *tape)
8449 // chunk required for team mode tapes with non-default screen size
8450 return (tape->num_participating_players > 1 &&
8451 (tape->scr_fieldx != SCR_FIELDX_DEFAULT ||
8452 tape->scr_fieldy != SCR_FIELDY_DEFAULT));
8455 static void SaveTape_VERS(FILE *file, struct TapeInfo *tape)
8457 putFileVersion(file, tape->file_version);
8458 putFileVersion(file, tape->game_version);
8461 static void SaveTape_HEAD(FILE *file, struct TapeInfo *tape)
8464 byte store_participating_players = 0;
8466 // set bits for participating players for compact storage
8467 for (i = 0; i < MAX_PLAYERS; i++)
8468 if (tape->player_participates[i])
8469 store_participating_players |= (1 << i);
8471 putFile32BitBE(file, tape->random_seed);
8472 putFile32BitBE(file, tape->date);
8473 putFile32BitBE(file, tape->length);
8475 putFile8Bit(file, store_participating_players);
8477 putFile8Bit(file, getTapeActionValue(tape));
8479 putFile8Bit(file, tape->property_bits);
8480 putFile8Bit(file, tape->solved);
8482 putFileVersion(file, tape->engine_version);
8485 static void SaveTape_SCRN(FILE *file, struct TapeInfo *tape)
8487 putFile8Bit(file, tape->scr_fieldx);
8488 putFile8Bit(file, tape->scr_fieldy);
8491 static void SaveTape_INFO(FILE *file, struct TapeInfo *tape)
8493 int level_identifier_size = strlen(tape->level_identifier) + 1;
8496 putFile16BitBE(file, level_identifier_size);
8498 for (i = 0; i < level_identifier_size; i++)
8499 putFile8Bit(file, tape->level_identifier[i]);
8501 putFile16BitBE(file, tape->level_nr);
8504 static void SaveTape_BODY(FILE *file, struct TapeInfo *tape)
8508 for (i = 0; i < tape->length; i++)
8510 if (tape->use_key_actions)
8512 for (j = 0; j < MAX_PLAYERS; j++)
8513 if (tape->player_participates[j])
8514 putFile8Bit(file, tape->pos[i].action[j]);
8517 if (tape->use_mouse_actions)
8519 putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_LX]);
8520 putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_LY]);
8521 putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_BUTTON]);
8524 putFile8Bit(file, tape->pos[i].delay);
8528 void SaveTapeToFilename(char *filename)
8532 int info_chunk_size;
8533 int body_chunk_size;
8535 if (!(file = fopen(filename, MODE_WRITE)))
8537 Warn("cannot save level recording file '%s'", filename);
8542 tape_pos_size = getTapePosSize(&tape);
8544 info_chunk_size = 2 + (strlen(tape.level_identifier) + 1) + 2;
8545 body_chunk_size = tape_pos_size * tape.length;
8547 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
8548 putFileChunkBE(file, "TAPE", CHUNK_SIZE_NONE);
8550 putFileChunkBE(file, "VERS", TAPE_CHUNK_VERS_SIZE);
8551 SaveTape_VERS(file, &tape);
8553 putFileChunkBE(file, "HEAD", TAPE_CHUNK_HEAD_SIZE);
8554 SaveTape_HEAD(file, &tape);
8556 if (checkSaveTape_SCRN(&tape))
8558 putFileChunkBE(file, "SCRN", TAPE_CHUNK_SCRN_SIZE);
8559 SaveTape_SCRN(file, &tape);
8562 putFileChunkBE(file, "INFO", info_chunk_size);
8563 SaveTape_INFO(file, &tape);
8565 putFileChunkBE(file, "BODY", body_chunk_size);
8566 SaveTape_BODY(file, &tape);
8570 SetFilePermissions(filename, PERMS_PRIVATE);
8573 static void SaveTapeExt(char *filename)
8577 tape.file_version = FILE_VERSION_ACTUAL;
8578 tape.game_version = GAME_VERSION_ACTUAL;
8580 tape.num_participating_players = 0;
8582 // count number of participating players
8583 for (i = 0; i < MAX_PLAYERS; i++)
8584 if (tape.player_participates[i])
8585 tape.num_participating_players++;
8587 SaveTapeToFilename(filename);
8589 tape.changed = FALSE;
8592 void SaveTape(int nr)
8594 char *filename = getTapeFilename(nr);
8596 InitTapeDirectory(leveldir_current->subdir);
8598 SaveTapeExt(filename);
8601 void SaveScoreTape(int nr)
8603 char *filename = getScoreTapeFilename(tape.score_tape_basename, nr);
8605 // used instead of "leveldir_current->subdir" (for network games)
8606 InitScoreTapeDirectory(levelset.identifier, nr);
8608 SaveTapeExt(filename);
8611 static boolean SaveTapeCheckedExt(int nr, char *msg_replace, char *msg_saved,
8612 unsigned int req_state_added)
8614 char *filename = getTapeFilename(nr);
8615 boolean new_tape = !fileExists(filename);
8616 boolean tape_saved = FALSE;
8618 if (new_tape || Request(msg_replace, REQ_ASK | req_state_added))
8623 Request(msg_saved, REQ_CONFIRM | req_state_added);
8631 boolean SaveTapeChecked(int nr)
8633 return SaveTapeCheckedExt(nr, "Replace old tape?", "Tape saved!", 0);
8636 boolean SaveTapeChecked_LevelSolved(int nr)
8638 return SaveTapeCheckedExt(nr, "Level solved! Replace old tape?",
8639 "Level solved! Tape saved!", REQ_STAY_OPEN);
8642 void DumpTape(struct TapeInfo *tape)
8644 int tape_frame_counter;
8647 if (tape->no_valid_file)
8649 Warn("cannot dump -- no valid tape file found");
8656 Print("Tape of Level %03d (file version %08d, game version %08d)\n",
8657 tape->level_nr, tape->file_version, tape->game_version);
8658 Print(" (effective engine version %08d)\n",
8659 tape->engine_version);
8660 Print("Level series identifier: '%s'\n", tape->level_identifier);
8662 Print("Solution tape: %s\n",
8663 tape->solved ? "yes" :
8664 tape->game_version < VERSION_IDENT(4,3,2,3) ? "unknown" : "no");
8666 Print("Special tape properties: ");
8667 if (tape->property_bits == TAPE_PROPERTY_NONE)
8669 if (tape->property_bits & TAPE_PROPERTY_EM_RANDOM_BUG)
8670 Print("[em_random_bug]");
8671 if (tape->property_bits & TAPE_PROPERTY_GAME_SPEED)
8672 Print("[game_speed]");
8673 if (tape->property_bits & TAPE_PROPERTY_PAUSE_MODE)
8675 if (tape->property_bits & TAPE_PROPERTY_SINGLE_STEP)
8676 Print("[single_step]");
8677 if (tape->property_bits & TAPE_PROPERTY_SNAPSHOT)
8678 Print("[snapshot]");
8679 if (tape->property_bits & TAPE_PROPERTY_REPLAYED)
8680 Print("[replayed]");
8681 if (tape->property_bits & TAPE_PROPERTY_TAS_KEYS)
8682 Print("[tas_keys]");
8683 if (tape->property_bits & TAPE_PROPERTY_SMALL_GRAPHICS)
8684 Print("[small_graphics]");
8687 int year2 = tape->date / 10000;
8688 int year4 = (year2 < 70 ? 2000 + year2 : 1900 + year2);
8689 int month_index_raw = (tape->date / 100) % 100;
8690 int month_index = month_index_raw % 12; // prevent invalid index
8691 int month = month_index + 1;
8692 int day = tape->date % 100;
8694 Print("Tape date: %04d-%02d-%02d\n", year4, month, day);
8698 tape_frame_counter = 0;
8700 for (i = 0; i < tape->length; i++)
8702 if (i >= MAX_TAPE_LEN)
8707 for (j = 0; j < MAX_PLAYERS; j++)
8709 if (tape->player_participates[j])
8711 int action = tape->pos[i].action[j];
8713 Print("%d:%02x ", j, action);
8714 Print("[%c%c%c%c|%c%c] - ",
8715 (action & JOY_LEFT ? '<' : ' '),
8716 (action & JOY_RIGHT ? '>' : ' '),
8717 (action & JOY_UP ? '^' : ' '),
8718 (action & JOY_DOWN ? 'v' : ' '),
8719 (action & JOY_BUTTON_1 ? '1' : ' '),
8720 (action & JOY_BUTTON_2 ? '2' : ' '));
8724 Print("(%03d) ", tape->pos[i].delay);
8725 Print("[%05d]\n", tape_frame_counter);
8727 tape_frame_counter += tape->pos[i].delay;
8733 void DumpTapes(void)
8735 static LevelDirTree *dumptape_leveldir = NULL;
8737 dumptape_leveldir = getTreeInfoFromIdentifier(leveldir_first,
8738 global.dumptape_leveldir);
8740 if (dumptape_leveldir == NULL)
8741 Fail("no such level identifier: '%s'", global.dumptape_leveldir);
8743 if (global.dumptape_level_nr < dumptape_leveldir->first_level ||
8744 global.dumptape_level_nr > dumptape_leveldir->last_level)
8745 Fail("no such level number: %d", global.dumptape_level_nr);
8747 leveldir_current = dumptape_leveldir;
8749 if (options.mytapes)
8750 LoadTape(global.dumptape_level_nr);
8752 LoadSolutionTape(global.dumptape_level_nr);
8760 // ============================================================================
8761 // score file functions
8762 // ============================================================================
8764 static void setScoreInfoToDefaultsExt(struct ScoreInfo *scores)
8768 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
8770 strcpy(scores->entry[i].tape_basename, UNDEFINED_FILENAME);
8771 strcpy(scores->entry[i].name, EMPTY_PLAYER_NAME);
8772 scores->entry[i].score = 0;
8773 scores->entry[i].time = 0;
8775 scores->entry[i].id = -1;
8776 strcpy(scores->entry[i].tape_date, UNKNOWN_NAME);
8777 strcpy(scores->entry[i].platform, UNKNOWN_NAME);
8778 strcpy(scores->entry[i].version, UNKNOWN_NAME);
8779 strcpy(scores->entry[i].country_name, UNKNOWN_NAME);
8780 strcpy(scores->entry[i].country_code, "??");
8783 scores->num_entries = 0;
8784 scores->last_added = -1;
8785 scores->last_added_local = -1;
8787 scores->updated = FALSE;
8788 scores->uploaded = FALSE;
8789 scores->tape_downloaded = FALSE;
8790 scores->force_last_added = FALSE;
8792 // The following values are intentionally not reset here:
8796 // - continue_playing
8797 // - continue_on_return
8800 static void setScoreInfoToDefaults(void)
8802 setScoreInfoToDefaultsExt(&scores);
8805 static void setServerScoreInfoToDefaults(void)
8807 setScoreInfoToDefaultsExt(&server_scores);
8810 static void LoadScore_OLD(int nr)
8813 char *filename = getScoreFilename(nr);
8814 char cookie[MAX_LINE_LEN];
8815 char line[MAX_LINE_LEN];
8819 if (!(file = fopen(filename, MODE_READ)))
8822 // check file identifier
8823 if (fgets(cookie, MAX_LINE_LEN, file) == NULL)
8825 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
8826 cookie[strlen(cookie) - 1] = '\0';
8828 if (!checkCookieString(cookie, SCORE_COOKIE_TMPL))
8830 Warn("unknown format of score file '%s'", filename);
8837 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
8839 if (fscanf(file, "%d", &scores.entry[i].score) == EOF)
8840 Warn("fscanf() failed; %s", strerror(errno));
8842 if (fgets(line, MAX_LINE_LEN, file) == NULL)
8845 if (strlen(line) > 0 && line[strlen(line) - 1] == '\n')
8846 line[strlen(line) - 1] = '\0';
8848 for (line_ptr = line; *line_ptr; line_ptr++)
8850 if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != '\0')
8852 strncpy(scores.entry[i].name, line_ptr, MAX_PLAYER_NAME_LEN);
8853 scores.entry[i].name[MAX_PLAYER_NAME_LEN] = '\0';
8862 static void ConvertScore_OLD(void)
8864 // only convert score to time for levels that rate playing time over score
8865 if (!level.rate_time_over_score)
8868 // convert old score to playing time for score-less levels (like Supaplex)
8869 int time_final_max = 999;
8872 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
8874 int score = scores.entry[i].score;
8876 if (score > 0 && score < time_final_max)
8877 scores.entry[i].time = (time_final_max - score - 1) * FRAMES_PER_SECOND;
8881 static int LoadScore_VERS(File *file, int chunk_size, struct ScoreInfo *scores)
8883 scores->file_version = getFileVersion(file);
8884 scores->game_version = getFileVersion(file);
8889 static int LoadScore_INFO(File *file, int chunk_size, struct ScoreInfo *scores)
8891 char *level_identifier = NULL;
8892 int level_identifier_size;
8895 level_identifier_size = getFile16BitBE(file);
8897 level_identifier = checked_malloc(level_identifier_size);
8899 for (i = 0; i < level_identifier_size; i++)
8900 level_identifier[i] = getFile8Bit(file);
8902 strncpy(scores->level_identifier, level_identifier, MAX_FILENAME_LEN);
8903 scores->level_identifier[MAX_FILENAME_LEN] = '\0';
8905 checked_free(level_identifier);
8907 scores->level_nr = getFile16BitBE(file);
8908 scores->num_entries = getFile16BitBE(file);
8910 chunk_size = 2 + level_identifier_size + 2 + 2;
8915 static int LoadScore_NAME(File *file, int chunk_size, struct ScoreInfo *scores)
8919 for (i = 0; i < scores->num_entries; i++)
8921 for (j = 0; j < MAX_PLAYER_NAME_LEN; j++)
8922 scores->entry[i].name[j] = getFile8Bit(file);
8924 scores->entry[i].name[MAX_PLAYER_NAME_LEN] = '\0';
8927 chunk_size = scores->num_entries * MAX_PLAYER_NAME_LEN;
8932 static int LoadScore_SCOR(File *file, int chunk_size, struct ScoreInfo *scores)
8936 for (i = 0; i < scores->num_entries; i++)
8937 scores->entry[i].score = getFile16BitBE(file);
8939 chunk_size = scores->num_entries * 2;
8944 static int LoadScore_SC4R(File *file, int chunk_size, struct ScoreInfo *scores)
8948 for (i = 0; i < scores->num_entries; i++)
8949 scores->entry[i].score = getFile32BitBE(file);
8951 chunk_size = scores->num_entries * 4;
8956 static int LoadScore_TIME(File *file, int chunk_size, struct ScoreInfo *scores)
8960 for (i = 0; i < scores->num_entries; i++)
8961 scores->entry[i].time = getFile32BitBE(file);
8963 chunk_size = scores->num_entries * 4;
8968 static int LoadScore_TAPE(File *file, int chunk_size, struct ScoreInfo *scores)
8972 for (i = 0; i < scores->num_entries; i++)
8974 for (j = 0; j < MAX_SCORE_TAPE_BASENAME_LEN; j++)
8975 scores->entry[i].tape_basename[j] = getFile8Bit(file);
8977 scores->entry[i].tape_basename[MAX_SCORE_TAPE_BASENAME_LEN] = '\0';
8980 chunk_size = scores->num_entries * MAX_SCORE_TAPE_BASENAME_LEN;
8985 void LoadScore(int nr)
8987 char *filename = getScoreFilename(nr);
8988 char cookie[MAX_LINE_LEN];
8989 char chunk_name[CHUNK_ID_LEN + 1];
8991 boolean old_score_file_format = FALSE;
8994 // always start with reliable default values
8995 setScoreInfoToDefaults();
8997 if (!(file = openFile(filename, MODE_READ)))
9000 getFileChunkBE(file, chunk_name, NULL);
9001 if (strEqual(chunk_name, "RND1"))
9003 getFile32BitBE(file); // not used
9005 getFileChunkBE(file, chunk_name, NULL);
9006 if (!strEqual(chunk_name, "SCOR"))
9008 Warn("unknown format of score file '%s'", filename);
9015 else // check for old file format with cookie string
9017 strcpy(cookie, chunk_name);
9018 if (getStringFromFile(file, &cookie[4], MAX_LINE_LEN - 4) == NULL)
9020 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
9021 cookie[strlen(cookie) - 1] = '\0';
9023 if (!checkCookieString(cookie, SCORE_COOKIE_TMPL))
9025 Warn("unknown format of score file '%s'", filename);
9032 old_score_file_format = TRUE;
9035 if (old_score_file_format)
9037 // score files from versions before 4.2.4.0 without chunk structure
9040 // convert score to time, if possible (mainly for Supaplex levels)
9049 int (*loader)(File *, int, struct ScoreInfo *);
9053 { "VERS", SCORE_CHUNK_VERS_SIZE, LoadScore_VERS },
9054 { "INFO", -1, LoadScore_INFO },
9055 { "NAME", -1, LoadScore_NAME },
9056 { "SCOR", -1, LoadScore_SCOR },
9057 { "SC4R", -1, LoadScore_SC4R },
9058 { "TIME", -1, LoadScore_TIME },
9059 { "TAPE", -1, LoadScore_TAPE },
9064 while (getFileChunkBE(file, chunk_name, &chunk_size))
9068 while (chunk_info[i].name != NULL &&
9069 !strEqual(chunk_name, chunk_info[i].name))
9072 if (chunk_info[i].name == NULL)
9074 Warn("unknown chunk '%s' in score file '%s'",
9075 chunk_name, filename);
9077 ReadUnusedBytesFromFile(file, chunk_size);
9079 else if (chunk_info[i].size != -1 &&
9080 chunk_info[i].size != chunk_size)
9082 Warn("wrong size (%d) of chunk '%s' in score file '%s'",
9083 chunk_size, chunk_name, filename);
9085 ReadUnusedBytesFromFile(file, chunk_size);
9089 // call function to load this score chunk
9090 int chunk_size_expected =
9091 (chunk_info[i].loader)(file, chunk_size, &scores);
9093 // the size of some chunks cannot be checked before reading other
9094 // chunks first (like "HEAD" and "BODY") that contain some header
9095 // information, so check them here
9096 if (chunk_size_expected != chunk_size)
9098 Warn("wrong size (%d) of chunk '%s' in score file '%s'",
9099 chunk_size, chunk_name, filename);
9108 #if ENABLE_HISTORIC_CHUNKS
9109 void SaveScore_OLD(int nr)
9112 char *filename = getScoreFilename(nr);
9115 // used instead of "leveldir_current->subdir" (for network games)
9116 InitScoreDirectory(levelset.identifier);
9118 if (!(file = fopen(filename, MODE_WRITE)))
9120 Warn("cannot save score for level %d", nr);
9125 fprintf(file, "%s\n\n", SCORE_COOKIE);
9127 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9128 fprintf(file, "%d %s\n", scores.entry[i].score, scores.entry[i].name);
9132 SetFilePermissions(filename, PERMS_PRIVATE);
9136 static void SaveScore_VERS(FILE *file, struct ScoreInfo *scores)
9138 putFileVersion(file, scores->file_version);
9139 putFileVersion(file, scores->game_version);
9142 static void SaveScore_INFO(FILE *file, struct ScoreInfo *scores)
9144 int level_identifier_size = strlen(scores->level_identifier) + 1;
9147 putFile16BitBE(file, level_identifier_size);
9149 for (i = 0; i < level_identifier_size; i++)
9150 putFile8Bit(file, scores->level_identifier[i]);
9152 putFile16BitBE(file, scores->level_nr);
9153 putFile16BitBE(file, scores->num_entries);
9156 static void SaveScore_NAME(FILE *file, struct ScoreInfo *scores)
9160 for (i = 0; i < scores->num_entries; i++)
9162 int name_size = strlen(scores->entry[i].name);
9164 for (j = 0; j < MAX_PLAYER_NAME_LEN; j++)
9165 putFile8Bit(file, (j < name_size ? scores->entry[i].name[j] : 0));
9169 static void SaveScore_SCOR(FILE *file, struct ScoreInfo *scores)
9173 for (i = 0; i < scores->num_entries; i++)
9174 putFile16BitBE(file, scores->entry[i].score);
9177 static void SaveScore_SC4R(FILE *file, struct ScoreInfo *scores)
9181 for (i = 0; i < scores->num_entries; i++)
9182 putFile32BitBE(file, scores->entry[i].score);
9185 static void SaveScore_TIME(FILE *file, struct ScoreInfo *scores)
9189 for (i = 0; i < scores->num_entries; i++)
9190 putFile32BitBE(file, scores->entry[i].time);
9193 static void SaveScore_TAPE(FILE *file, struct ScoreInfo *scores)
9197 for (i = 0; i < scores->num_entries; i++)
9199 int size = strlen(scores->entry[i].tape_basename);
9201 for (j = 0; j < MAX_SCORE_TAPE_BASENAME_LEN; j++)
9202 putFile8Bit(file, (j < size ? scores->entry[i].tape_basename[j] : 0));
9206 static void SaveScoreToFilename(char *filename)
9209 int info_chunk_size;
9210 int name_chunk_size;
9211 int scor_chunk_size;
9212 int sc4r_chunk_size;
9213 int time_chunk_size;
9214 int tape_chunk_size;
9215 boolean has_large_score_values;
9218 if (!(file = fopen(filename, MODE_WRITE)))
9220 Warn("cannot save score file '%s'", filename);
9225 info_chunk_size = 2 + (strlen(scores.level_identifier) + 1) + 2 + 2;
9226 name_chunk_size = scores.num_entries * MAX_PLAYER_NAME_LEN;
9227 scor_chunk_size = scores.num_entries * 2;
9228 sc4r_chunk_size = scores.num_entries * 4;
9229 time_chunk_size = scores.num_entries * 4;
9230 tape_chunk_size = scores.num_entries * MAX_SCORE_TAPE_BASENAME_LEN;
9232 has_large_score_values = FALSE;
9233 for (i = 0; i < scores.num_entries; i++)
9234 if (scores.entry[i].score > 0xffff)
9235 has_large_score_values = TRUE;
9237 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
9238 putFileChunkBE(file, "SCOR", CHUNK_SIZE_NONE);
9240 putFileChunkBE(file, "VERS", SCORE_CHUNK_VERS_SIZE);
9241 SaveScore_VERS(file, &scores);
9243 putFileChunkBE(file, "INFO", info_chunk_size);
9244 SaveScore_INFO(file, &scores);
9246 putFileChunkBE(file, "NAME", name_chunk_size);
9247 SaveScore_NAME(file, &scores);
9249 if (has_large_score_values)
9251 putFileChunkBE(file, "SC4R", sc4r_chunk_size);
9252 SaveScore_SC4R(file, &scores);
9256 putFileChunkBE(file, "SCOR", scor_chunk_size);
9257 SaveScore_SCOR(file, &scores);
9260 putFileChunkBE(file, "TIME", time_chunk_size);
9261 SaveScore_TIME(file, &scores);
9263 putFileChunkBE(file, "TAPE", tape_chunk_size);
9264 SaveScore_TAPE(file, &scores);
9268 SetFilePermissions(filename, PERMS_PRIVATE);
9271 void SaveScore(int nr)
9273 char *filename = getScoreFilename(nr);
9276 // used instead of "leveldir_current->subdir" (for network games)
9277 InitScoreDirectory(levelset.identifier);
9279 scores.file_version = FILE_VERSION_ACTUAL;
9280 scores.game_version = GAME_VERSION_ACTUAL;
9282 strncpy(scores.level_identifier, levelset.identifier, MAX_FILENAME_LEN);
9283 scores.level_identifier[MAX_FILENAME_LEN] = '\0';
9284 scores.level_nr = level_nr;
9286 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9287 if (scores.entry[i].score == 0 &&
9288 scores.entry[i].time == 0 &&
9289 strEqual(scores.entry[i].name, EMPTY_PLAYER_NAME))
9292 scores.num_entries = i;
9294 if (scores.num_entries == 0)
9297 SaveScoreToFilename(filename);
9300 static void LoadServerScoreFromCache(int nr)
9302 struct ScoreEntry score_entry;
9311 { &score_entry.score, FALSE, 0 },
9312 { &score_entry.time, FALSE, 0 },
9313 { score_entry.name, TRUE, MAX_PLAYER_NAME_LEN },
9314 { score_entry.tape_basename, TRUE, MAX_FILENAME_LEN },
9315 { score_entry.tape_date, TRUE, MAX_ISO_DATE_LEN },
9316 { &score_entry.id, FALSE, 0 },
9317 { score_entry.platform, TRUE, MAX_PLATFORM_TEXT_LEN },
9318 { score_entry.version, TRUE, MAX_VERSION_TEXT_LEN },
9319 { score_entry.country_code, TRUE, MAX_COUNTRY_CODE_LEN },
9320 { score_entry.country_name, TRUE, MAX_COUNTRY_NAME_LEN },
9324 char *filename = getScoreCacheFilename(nr);
9325 SetupFileHash *score_hash = loadSetupFileHash(filename);
9328 server_scores.num_entries = 0;
9330 if (score_hash == NULL)
9333 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9335 score_entry = server_scores.entry[i];
9337 for (j = 0; score_mapping[j].value != NULL; j++)
9341 sprintf(token, "%02d.%d", i, j);
9343 char *value = getHashEntry(score_hash, token);
9348 if (score_mapping[j].is_string)
9350 char *score_value = (char *)score_mapping[j].value;
9351 int value_size = score_mapping[j].string_size;
9353 strncpy(score_value, value, value_size);
9354 score_value[value_size] = '\0';
9358 int *score_value = (int *)score_mapping[j].value;
9360 *score_value = atoi(value);
9363 server_scores.num_entries = i + 1;
9366 server_scores.entry[i] = score_entry;
9369 freeSetupFileHash(score_hash);
9372 void LoadServerScore(int nr, boolean download_score)
9374 if (!setup.use_api_server)
9377 // always start with reliable default values
9378 setServerScoreInfoToDefaults();
9380 // 1st step: load server scores from cache file (which may not exist)
9381 // (this should prevent reading it while the thread is writing to it)
9382 LoadServerScoreFromCache(nr);
9384 if (download_score && runtime.use_api_server)
9386 // 2nd step: download server scores from score server to cache file
9387 // (as thread, as it might time out if the server is not reachable)
9388 ApiGetScoreAsThread(nr);
9392 void PrepareScoreTapesForUpload(char *leveldir_subdir)
9394 MarkTapeDirectoryUploadsAsIncomplete(leveldir_subdir);
9396 // if score tape not uploaded, ask for uploading missing tapes later
9397 if (!setup.has_remaining_tapes)
9398 setup.ask_for_remaining_tapes = TRUE;
9400 setup.provide_uploading_tapes = TRUE;
9401 setup.has_remaining_tapes = TRUE;
9403 SaveSetup_ServerSetup();
9406 void SaveServerScore(int nr, boolean tape_saved)
9408 if (!runtime.use_api_server)
9410 PrepareScoreTapesForUpload(leveldir_current->subdir);
9415 ApiAddScoreAsThread(nr, tape_saved, NULL);
9418 void SaveServerScoreFromFile(int nr, boolean tape_saved,
9419 char *score_tape_filename)
9421 if (!runtime.use_api_server)
9424 ApiAddScoreAsThread(nr, tape_saved, score_tape_filename);
9427 void LoadLocalAndServerScore(int nr, boolean download_score)
9429 int last_added_local = scores.last_added_local;
9430 boolean force_last_added = scores.force_last_added;
9432 // needed if only showing server scores
9433 setScoreInfoToDefaults();
9435 if (!strEqual(setup.scores_in_highscore_list, STR_SCORES_TYPE_SERVER_ONLY))
9438 // restore last added local score entry (before merging server scores)
9439 scores.last_added = scores.last_added_local = last_added_local;
9441 if (setup.use_api_server &&
9442 !strEqual(setup.scores_in_highscore_list, STR_SCORES_TYPE_LOCAL_ONLY))
9444 // load server scores from cache file and trigger update from server
9445 LoadServerScore(nr, download_score);
9447 // merge local scores with scores from server
9451 if (force_last_added)
9452 scores.force_last_added = force_last_added;
9456 // ============================================================================
9457 // setup file functions
9458 // ============================================================================
9460 #define TOKEN_STR_PLAYER_PREFIX "player_"
9463 static struct TokenInfo global_setup_tokens[] =
9467 &setup.player_name, "player_name"
9471 &setup.multiple_users, "multiple_users"
9475 &setup.sound, "sound"
9479 &setup.sound_loops, "repeating_sound_loops"
9483 &setup.sound_music, "background_music"
9487 &setup.sound_simple, "simple_sound_effects"
9491 &setup.toons, "toons"
9495 &setup.global_animations, "global_animations"
9499 &setup.scroll_delay, "scroll_delay"
9503 &setup.forced_scroll_delay, "forced_scroll_delay"
9507 &setup.scroll_delay_value, "scroll_delay_value"
9511 &setup.engine_snapshot_mode, "engine_snapshot_mode"
9515 &setup.engine_snapshot_memory, "engine_snapshot_memory"
9519 &setup.fade_screens, "fade_screens"
9523 &setup.autorecord, "automatic_tape_recording"
9527 &setup.autorecord_after_replay, "autorecord_after_replay"
9531 &setup.auto_pause_on_start, "auto_pause_on_start"
9535 &setup.show_titlescreen, "show_titlescreen"
9539 &setup.quick_doors, "quick_doors"
9543 &setup.team_mode, "team_mode"
9547 &setup.handicap, "handicap"
9551 &setup.skip_levels, "skip_levels"
9555 &setup.increment_levels, "increment_levels"
9559 &setup.auto_play_next_level, "auto_play_next_level"
9563 &setup.count_score_after_game, "count_score_after_game"
9567 &setup.show_scores_after_game, "show_scores_after_game"
9571 &setup.time_limit, "time_limit"
9575 &setup.fullscreen, "fullscreen"
9579 &setup.window_scaling_percent, "window_scaling_percent"
9583 &setup.window_scaling_quality, "window_scaling_quality"
9587 &setup.screen_rendering_mode, "screen_rendering_mode"
9591 &setup.vsync_mode, "vsync_mode"
9595 &setup.ask_on_escape, "ask_on_escape"
9599 &setup.ask_on_escape_editor, "ask_on_escape_editor"
9603 &setup.ask_on_game_over, "ask_on_game_over"
9607 &setup.ask_on_quit_game, "ask_on_quit_game"
9611 &setup.ask_on_quit_program, "ask_on_quit_program"
9615 &setup.quick_switch, "quick_player_switch"
9619 &setup.input_on_focus, "input_on_focus"
9623 &setup.prefer_aga_graphics, "prefer_aga_graphics"
9627 &setup.prefer_lowpass_sounds, "prefer_lowpass_sounds"
9631 &setup.prefer_extra_panel_items, "prefer_extra_panel_items"
9635 &setup.game_speed_extended, "game_speed_extended"
9639 &setup.game_frame_delay, "game_frame_delay"
9643 &setup.sp_show_border_elements, "sp_show_border_elements"
9647 &setup.small_game_graphics, "small_game_graphics"
9651 &setup.show_load_save_buttons, "show_load_save_buttons"
9655 &setup.show_undo_redo_buttons, "show_undo_redo_buttons"
9659 &setup.scores_in_highscore_list, "scores_in_highscore_list"
9663 &setup.graphics_set, "graphics_set"
9667 &setup.sounds_set, "sounds_set"
9671 &setup.music_set, "music_set"
9675 &setup.override_level_graphics, "override_level_graphics"
9679 &setup.override_level_sounds, "override_level_sounds"
9683 &setup.override_level_music, "override_level_music"
9687 &setup.volume_simple, "volume_simple"
9691 &setup.volume_loops, "volume_loops"
9695 &setup.volume_music, "volume_music"
9699 &setup.network_mode, "network_mode"
9703 &setup.network_player_nr, "network_player"
9707 &setup.network_server_hostname, "network_server_hostname"
9711 &setup.touch.control_type, "touch.control_type"
9715 &setup.touch.move_distance, "touch.move_distance"
9719 &setup.touch.drop_distance, "touch.drop_distance"
9723 &setup.touch.transparency, "touch.transparency"
9727 &setup.touch.draw_outlined, "touch.draw_outlined"
9731 &setup.touch.draw_pressed, "touch.draw_pressed"
9735 &setup.touch.grid_xsize[0], "touch.virtual_buttons.0.xsize"
9739 &setup.touch.grid_ysize[0], "touch.virtual_buttons.0.ysize"
9743 &setup.touch.grid_xsize[1], "touch.virtual_buttons.1.xsize"
9747 &setup.touch.grid_ysize[1], "touch.virtual_buttons.1.ysize"
9751 &setup.touch.overlay_buttons, "touch.overlay_buttons"
9755 static struct TokenInfo auto_setup_tokens[] =
9759 &setup.auto_setup.editor_zoom_tilesize, "editor.zoom_tilesize"
9763 static struct TokenInfo server_setup_tokens[] =
9767 &setup.player_uuid, "player_uuid"
9771 &setup.player_version, "player_version"
9775 &setup.use_api_server, TEST_PREFIX "use_api_server"
9779 &setup.api_server_hostname, TEST_PREFIX "api_server_hostname"
9783 &setup.api_server_password, TEST_PREFIX "api_server_password"
9787 &setup.ask_for_uploading_tapes, TEST_PREFIX "ask_for_uploading_tapes"
9791 &setup.ask_for_remaining_tapes, TEST_PREFIX "ask_for_remaining_tapes"
9795 &setup.provide_uploading_tapes, TEST_PREFIX "provide_uploading_tapes"
9799 &setup.ask_for_using_api_server,TEST_PREFIX "ask_for_using_api_server"
9803 &setup.has_remaining_tapes, TEST_PREFIX "has_remaining_tapes"
9807 static struct TokenInfo editor_setup_tokens[] =
9811 &setup.editor.el_classic, "editor.el_classic"
9815 &setup.editor.el_custom, "editor.el_custom"
9819 &setup.editor.el_user_defined, "editor.el_user_defined"
9823 &setup.editor.el_dynamic, "editor.el_dynamic"
9827 &setup.editor.el_headlines, "editor.el_headlines"
9831 &setup.editor.show_element_token, "editor.show_element_token"
9835 &setup.editor.show_read_only_warning, "editor.show_read_only_warning"
9839 static struct TokenInfo editor_cascade_setup_tokens[] =
9843 &setup.editor_cascade.el_bd, "editor.cascade.el_bd"
9847 &setup.editor_cascade.el_em, "editor.cascade.el_em"
9851 &setup.editor_cascade.el_emc, "editor.cascade.el_emc"
9855 &setup.editor_cascade.el_rnd, "editor.cascade.el_rnd"
9859 &setup.editor_cascade.el_sb, "editor.cascade.el_sb"
9863 &setup.editor_cascade.el_sp, "editor.cascade.el_sp"
9867 &setup.editor_cascade.el_dc, "editor.cascade.el_dc"
9871 &setup.editor_cascade.el_dx, "editor.cascade.el_dx"
9875 &setup.editor_cascade.el_mm, "editor.cascade.el_mm"
9879 &setup.editor_cascade.el_df, "editor.cascade.el_df"
9883 &setup.editor_cascade.el_chars, "editor.cascade.el_chars"
9887 &setup.editor_cascade.el_steel_chars, "editor.cascade.el_steel_chars"
9891 &setup.editor_cascade.el_ce, "editor.cascade.el_ce"
9895 &setup.editor_cascade.el_ge, "editor.cascade.el_ge"
9899 &setup.editor_cascade.el_es, "editor.cascade.el_es"
9903 &setup.editor_cascade.el_ref, "editor.cascade.el_ref"
9907 &setup.editor_cascade.el_user, "editor.cascade.el_user"
9911 &setup.editor_cascade.el_dynamic, "editor.cascade.el_dynamic"
9915 static struct TokenInfo shortcut_setup_tokens[] =
9919 &setup.shortcut.save_game, "shortcut.save_game"
9923 &setup.shortcut.load_game, "shortcut.load_game"
9927 &setup.shortcut.restart_game, "shortcut.restart_game"
9931 &setup.shortcut.pause_before_end, "shortcut.pause_before_end"
9935 &setup.shortcut.toggle_pause, "shortcut.toggle_pause"
9939 &setup.shortcut.focus_player[0], "shortcut.focus_player_1"
9943 &setup.shortcut.focus_player[1], "shortcut.focus_player_2"
9947 &setup.shortcut.focus_player[2], "shortcut.focus_player_3"
9951 &setup.shortcut.focus_player[3], "shortcut.focus_player_4"
9955 &setup.shortcut.focus_player_all, "shortcut.focus_player_all"
9959 &setup.shortcut.tape_eject, "shortcut.tape_eject"
9963 &setup.shortcut.tape_extra, "shortcut.tape_extra"
9967 &setup.shortcut.tape_stop, "shortcut.tape_stop"
9971 &setup.shortcut.tape_pause, "shortcut.tape_pause"
9975 &setup.shortcut.tape_record, "shortcut.tape_record"
9979 &setup.shortcut.tape_play, "shortcut.tape_play"
9983 &setup.shortcut.sound_simple, "shortcut.sound_simple"
9987 &setup.shortcut.sound_loops, "shortcut.sound_loops"
9991 &setup.shortcut.sound_music, "shortcut.sound_music"
9995 &setup.shortcut.snap_left, "shortcut.snap_left"
9999 &setup.shortcut.snap_right, "shortcut.snap_right"
10003 &setup.shortcut.snap_up, "shortcut.snap_up"
10007 &setup.shortcut.snap_down, "shortcut.snap_down"
10011 static struct SetupInputInfo setup_input;
10012 static struct TokenInfo player_setup_tokens[] =
10016 &setup_input.use_joystick, ".use_joystick"
10020 &setup_input.joy.device_name, ".joy.device_name"
10024 &setup_input.joy.xleft, ".joy.xleft"
10028 &setup_input.joy.xmiddle, ".joy.xmiddle"
10032 &setup_input.joy.xright, ".joy.xright"
10036 &setup_input.joy.yupper, ".joy.yupper"
10040 &setup_input.joy.ymiddle, ".joy.ymiddle"
10044 &setup_input.joy.ylower, ".joy.ylower"
10048 &setup_input.joy.snap, ".joy.snap_field"
10052 &setup_input.joy.drop, ".joy.place_bomb"
10056 &setup_input.key.left, ".key.move_left"
10060 &setup_input.key.right, ".key.move_right"
10064 &setup_input.key.up, ".key.move_up"
10068 &setup_input.key.down, ".key.move_down"
10072 &setup_input.key.snap, ".key.snap_field"
10076 &setup_input.key.drop, ".key.place_bomb"
10080 static struct TokenInfo system_setup_tokens[] =
10084 &setup.system.sdl_renderdriver, "system.sdl_renderdriver"
10088 &setup.system.sdl_videodriver, "system.sdl_videodriver"
10092 &setup.system.sdl_audiodriver, "system.sdl_audiodriver"
10096 &setup.system.audio_fragment_size, "system.audio_fragment_size"
10100 static struct TokenInfo internal_setup_tokens[] =
10104 &setup.internal.program_title, "program_title"
10108 &setup.internal.program_version, "program_version"
10112 &setup.internal.program_author, "program_author"
10116 &setup.internal.program_email, "program_email"
10120 &setup.internal.program_website, "program_website"
10124 &setup.internal.program_copyright, "program_copyright"
10128 &setup.internal.program_company, "program_company"
10132 &setup.internal.program_icon_file, "program_icon_file"
10136 &setup.internal.default_graphics_set, "default_graphics_set"
10140 &setup.internal.default_sounds_set, "default_sounds_set"
10144 &setup.internal.default_music_set, "default_music_set"
10148 &setup.internal.fallback_graphics_file, "fallback_graphics_file"
10152 &setup.internal.fallback_sounds_file, "fallback_sounds_file"
10156 &setup.internal.fallback_music_file, "fallback_music_file"
10160 &setup.internal.default_level_series, "default_level_series"
10164 &setup.internal.default_window_width, "default_window_width"
10168 &setup.internal.default_window_height, "default_window_height"
10172 &setup.internal.choose_from_top_leveldir, "choose_from_top_leveldir"
10176 &setup.internal.show_scaling_in_title, "show_scaling_in_title"
10180 &setup.internal.create_user_levelset, "create_user_levelset"
10184 &setup.internal.info_screens_from_main, "info_screens_from_main"
10188 &setup.internal.menu_game, "menu_game"
10192 &setup.internal.menu_engines, "menu_engines"
10196 &setup.internal.menu_editor, "menu_editor"
10200 &setup.internal.menu_graphics, "menu_graphics"
10204 &setup.internal.menu_sound, "menu_sound"
10208 &setup.internal.menu_artwork, "menu_artwork"
10212 &setup.internal.menu_input, "menu_input"
10216 &setup.internal.menu_touch, "menu_touch"
10220 &setup.internal.menu_shortcuts, "menu_shortcuts"
10224 &setup.internal.menu_exit, "menu_exit"
10228 &setup.internal.menu_save_and_exit, "menu_save_and_exit"
10232 &setup.internal.menu_shortcuts_various, "menu_shortcuts_various"
10236 &setup.internal.menu_shortcuts_focus, "menu_shortcuts_focus"
10240 &setup.internal.menu_shortcuts_tape, "menu_shortcuts_tape"
10244 &setup.internal.menu_shortcuts_sound, "menu_shortcuts_sound"
10248 &setup.internal.menu_shortcuts_snap, "menu_shortcuts_snap"
10252 &setup.internal.info_title, "info_title"
10256 &setup.internal.info_elements, "info_elements"
10260 &setup.internal.info_music, "info_music"
10264 &setup.internal.info_credits, "info_credits"
10268 &setup.internal.info_program, "info_program"
10272 &setup.internal.info_version, "info_version"
10276 &setup.internal.info_levelset, "info_levelset"
10280 &setup.internal.info_exit, "info_exit"
10284 static struct TokenInfo debug_setup_tokens[] =
10288 &setup.debug.frame_delay[0], "debug.frame_delay_0"
10292 &setup.debug.frame_delay[1], "debug.frame_delay_1"
10296 &setup.debug.frame_delay[2], "debug.frame_delay_2"
10300 &setup.debug.frame_delay[3], "debug.frame_delay_3"
10304 &setup.debug.frame_delay[4], "debug.frame_delay_4"
10308 &setup.debug.frame_delay[5], "debug.frame_delay_5"
10312 &setup.debug.frame_delay[6], "debug.frame_delay_6"
10316 &setup.debug.frame_delay[7], "debug.frame_delay_7"
10320 &setup.debug.frame_delay[8], "debug.frame_delay_8"
10324 &setup.debug.frame_delay[9], "debug.frame_delay_9"
10328 &setup.debug.frame_delay_key[0], "debug.key.frame_delay_0"
10332 &setup.debug.frame_delay_key[1], "debug.key.frame_delay_1"
10336 &setup.debug.frame_delay_key[2], "debug.key.frame_delay_2"
10340 &setup.debug.frame_delay_key[3], "debug.key.frame_delay_3"
10344 &setup.debug.frame_delay_key[4], "debug.key.frame_delay_4"
10348 &setup.debug.frame_delay_key[5], "debug.key.frame_delay_5"
10352 &setup.debug.frame_delay_key[6], "debug.key.frame_delay_6"
10356 &setup.debug.frame_delay_key[7], "debug.key.frame_delay_7"
10360 &setup.debug.frame_delay_key[8], "debug.key.frame_delay_8"
10364 &setup.debug.frame_delay_key[9], "debug.key.frame_delay_9"
10368 &setup.debug.frame_delay_use_mod_key, "debug.frame_delay.use_mod_key"},
10371 &setup.debug.frame_delay_game_only, "debug.frame_delay.game_only"
10375 &setup.debug.show_frames_per_second, "debug.show_frames_per_second"
10379 &setup.debug.xsn_mode, "debug.xsn_mode"
10383 &setup.debug.xsn_percent, "debug.xsn_percent"
10387 static struct TokenInfo options_setup_tokens[] =
10391 &setup.options.verbose, "options.verbose"
10395 &setup.options.debug, "options.debug"
10399 &setup.options.debug_mode, "options.debug_mode"
10403 static void setSetupInfoToDefaults(struct SetupInfo *si)
10407 si->player_name = getStringCopy(getDefaultUserName(user.nr));
10409 si->multiple_users = TRUE;
10412 si->sound_loops = TRUE;
10413 si->sound_music = TRUE;
10414 si->sound_simple = TRUE;
10416 si->global_animations = TRUE;
10417 si->scroll_delay = TRUE;
10418 si->forced_scroll_delay = FALSE;
10419 si->scroll_delay_value = STD_SCROLL_DELAY;
10420 si->engine_snapshot_mode = getStringCopy(STR_SNAPSHOT_MODE_DEFAULT);
10421 si->engine_snapshot_memory = SNAPSHOT_MEMORY_DEFAULT;
10422 si->fade_screens = TRUE;
10423 si->autorecord = TRUE;
10424 si->autorecord_after_replay = TRUE;
10425 si->auto_pause_on_start = FALSE;
10426 si->show_titlescreen = TRUE;
10427 si->quick_doors = FALSE;
10428 si->team_mode = FALSE;
10429 si->handicap = TRUE;
10430 si->skip_levels = TRUE;
10431 si->increment_levels = TRUE;
10432 si->auto_play_next_level = TRUE;
10433 si->count_score_after_game = TRUE;
10434 si->show_scores_after_game = TRUE;
10435 si->time_limit = TRUE;
10436 si->fullscreen = FALSE;
10437 si->window_scaling_percent = STD_WINDOW_SCALING_PERCENT;
10438 si->window_scaling_quality = getStringCopy(SCALING_QUALITY_DEFAULT);
10439 si->screen_rendering_mode = getStringCopy(STR_SPECIAL_RENDERING_DEFAULT);
10440 si->vsync_mode = getStringCopy(STR_VSYNC_MODE_DEFAULT);
10441 si->ask_on_escape = TRUE;
10442 si->ask_on_escape_editor = TRUE;
10443 si->ask_on_game_over = TRUE;
10444 si->ask_on_quit_game = TRUE;
10445 si->ask_on_quit_program = TRUE;
10446 si->quick_switch = FALSE;
10447 si->input_on_focus = FALSE;
10448 si->prefer_aga_graphics = TRUE;
10449 si->prefer_lowpass_sounds = FALSE;
10450 si->prefer_extra_panel_items = TRUE;
10451 si->game_speed_extended = FALSE;
10452 si->game_frame_delay = GAME_FRAME_DELAY;
10453 si->sp_show_border_elements = FALSE;
10454 si->small_game_graphics = FALSE;
10455 si->show_load_save_buttons = FALSE;
10456 si->show_undo_redo_buttons = FALSE;
10457 si->scores_in_highscore_list = getStringCopy(STR_SCORES_TYPE_DEFAULT);
10459 si->graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
10460 si->sounds_set = getStringCopy(SND_CLASSIC_SUBDIR);
10461 si->music_set = getStringCopy(MUS_CLASSIC_SUBDIR);
10463 si->override_level_graphics = FALSE;
10464 si->override_level_sounds = FALSE;
10465 si->override_level_music = FALSE;
10467 si->volume_simple = 100; // percent
10468 si->volume_loops = 100; // percent
10469 si->volume_music = 100; // percent
10471 si->network_mode = FALSE;
10472 si->network_player_nr = 0; // first player
10473 si->network_server_hostname = getStringCopy(STR_NETWORK_AUTO_DETECT);
10475 si->touch.control_type = getStringCopy(TOUCH_CONTROL_DEFAULT);
10476 si->touch.move_distance = TOUCH_MOVE_DISTANCE_DEFAULT; // percent
10477 si->touch.drop_distance = TOUCH_DROP_DISTANCE_DEFAULT; // percent
10478 si->touch.transparency = TOUCH_TRANSPARENCY_DEFAULT; // percent
10479 si->touch.draw_outlined = TRUE;
10480 si->touch.draw_pressed = TRUE;
10482 for (i = 0; i < 2; i++)
10484 char *default_grid_button[6][2] =
10490 { "111222", " vv " },
10491 { "111222", " vv " }
10493 int grid_xsize = DEFAULT_GRID_XSIZE(i);
10494 int grid_ysize = DEFAULT_GRID_YSIZE(i);
10495 int min_xsize = MIN(6, grid_xsize);
10496 int min_ysize = MIN(6, grid_ysize);
10497 int startx = grid_xsize - min_xsize;
10498 int starty = grid_ysize - min_ysize;
10501 // virtual buttons grid can only be set to defaults if video is initialized
10502 // (this will be repeated if virtual buttons are not loaded from setup file)
10503 if (video.initialized)
10505 si->touch.grid_xsize[i] = grid_xsize;
10506 si->touch.grid_ysize[i] = grid_ysize;
10510 si->touch.grid_xsize[i] = -1;
10511 si->touch.grid_ysize[i] = -1;
10514 for (x = 0; x < MAX_GRID_XSIZE; x++)
10515 for (y = 0; y < MAX_GRID_YSIZE; y++)
10516 si->touch.grid_button[i][x][y] = CHAR_GRID_BUTTON_NONE;
10518 for (x = 0; x < min_xsize; x++)
10519 for (y = 0; y < min_ysize; y++)
10520 si->touch.grid_button[i][x][starty + y] =
10521 default_grid_button[y][0][x];
10523 for (x = 0; x < min_xsize; x++)
10524 for (y = 0; y < min_ysize; y++)
10525 si->touch.grid_button[i][startx + x][starty + y] =
10526 default_grid_button[y][1][x];
10529 si->touch.grid_initialized = video.initialized;
10531 si->touch.overlay_buttons = FALSE;
10533 si->editor.el_boulderdash = TRUE;
10534 si->editor.el_emerald_mine = TRUE;
10535 si->editor.el_emerald_mine_club = TRUE;
10536 si->editor.el_more = TRUE;
10537 si->editor.el_sokoban = TRUE;
10538 si->editor.el_supaplex = TRUE;
10539 si->editor.el_diamond_caves = TRUE;
10540 si->editor.el_dx_boulderdash = TRUE;
10542 si->editor.el_mirror_magic = TRUE;
10543 si->editor.el_deflektor = TRUE;
10545 si->editor.el_chars = TRUE;
10546 si->editor.el_steel_chars = TRUE;
10548 si->editor.el_classic = TRUE;
10549 si->editor.el_custom = TRUE;
10551 si->editor.el_user_defined = FALSE;
10552 si->editor.el_dynamic = TRUE;
10554 si->editor.el_headlines = TRUE;
10556 si->editor.show_element_token = FALSE;
10558 si->editor.show_read_only_warning = TRUE;
10560 si->editor.use_template_for_new_levels = TRUE;
10562 si->shortcut.save_game = DEFAULT_KEY_SAVE_GAME;
10563 si->shortcut.load_game = DEFAULT_KEY_LOAD_GAME;
10564 si->shortcut.restart_game = DEFAULT_KEY_RESTART_GAME;
10565 si->shortcut.pause_before_end = DEFAULT_KEY_PAUSE_BEFORE_END;
10566 si->shortcut.toggle_pause = DEFAULT_KEY_TOGGLE_PAUSE;
10568 si->shortcut.focus_player[0] = DEFAULT_KEY_FOCUS_PLAYER_1;
10569 si->shortcut.focus_player[1] = DEFAULT_KEY_FOCUS_PLAYER_2;
10570 si->shortcut.focus_player[2] = DEFAULT_KEY_FOCUS_PLAYER_3;
10571 si->shortcut.focus_player[3] = DEFAULT_KEY_FOCUS_PLAYER_4;
10572 si->shortcut.focus_player_all = DEFAULT_KEY_FOCUS_PLAYER_ALL;
10574 si->shortcut.tape_eject = DEFAULT_KEY_TAPE_EJECT;
10575 si->shortcut.tape_extra = DEFAULT_KEY_TAPE_EXTRA;
10576 si->shortcut.tape_stop = DEFAULT_KEY_TAPE_STOP;
10577 si->shortcut.tape_pause = DEFAULT_KEY_TAPE_PAUSE;
10578 si->shortcut.tape_record = DEFAULT_KEY_TAPE_RECORD;
10579 si->shortcut.tape_play = DEFAULT_KEY_TAPE_PLAY;
10581 si->shortcut.sound_simple = DEFAULT_KEY_SOUND_SIMPLE;
10582 si->shortcut.sound_loops = DEFAULT_KEY_SOUND_LOOPS;
10583 si->shortcut.sound_music = DEFAULT_KEY_SOUND_MUSIC;
10585 si->shortcut.snap_left = DEFAULT_KEY_SNAP_LEFT;
10586 si->shortcut.snap_right = DEFAULT_KEY_SNAP_RIGHT;
10587 si->shortcut.snap_up = DEFAULT_KEY_SNAP_UP;
10588 si->shortcut.snap_down = DEFAULT_KEY_SNAP_DOWN;
10590 for (i = 0; i < MAX_PLAYERS; i++)
10592 si->input[i].use_joystick = FALSE;
10593 si->input[i].joy.device_name=getStringCopy(getDeviceNameFromJoystickNr(i));
10594 si->input[i].joy.xleft = JOYSTICK_XLEFT;
10595 si->input[i].joy.xmiddle = JOYSTICK_XMIDDLE;
10596 si->input[i].joy.xright = JOYSTICK_XRIGHT;
10597 si->input[i].joy.yupper = JOYSTICK_YUPPER;
10598 si->input[i].joy.ymiddle = JOYSTICK_YMIDDLE;
10599 si->input[i].joy.ylower = JOYSTICK_YLOWER;
10600 si->input[i].joy.snap = (i == 0 ? JOY_BUTTON_1 : 0);
10601 si->input[i].joy.drop = (i == 0 ? JOY_BUTTON_2 : 0);
10602 si->input[i].key.left = (i == 0 ? DEFAULT_KEY_LEFT : KSYM_UNDEFINED);
10603 si->input[i].key.right = (i == 0 ? DEFAULT_KEY_RIGHT : KSYM_UNDEFINED);
10604 si->input[i].key.up = (i == 0 ? DEFAULT_KEY_UP : KSYM_UNDEFINED);
10605 si->input[i].key.down = (i == 0 ? DEFAULT_KEY_DOWN : KSYM_UNDEFINED);
10606 si->input[i].key.snap = (i == 0 ? DEFAULT_KEY_SNAP : KSYM_UNDEFINED);
10607 si->input[i].key.drop = (i == 0 ? DEFAULT_KEY_DROP : KSYM_UNDEFINED);
10610 si->system.sdl_renderdriver = getStringCopy(ARG_DEFAULT);
10611 si->system.sdl_videodriver = getStringCopy(ARG_DEFAULT);
10612 si->system.sdl_audiodriver = getStringCopy(ARG_DEFAULT);
10613 si->system.audio_fragment_size = DEFAULT_AUDIO_FRAGMENT_SIZE;
10615 si->internal.program_title = getStringCopy(PROGRAM_TITLE_STRING);
10616 si->internal.program_version = getStringCopy(getProgramRealVersionString());
10617 si->internal.program_author = getStringCopy(PROGRAM_AUTHOR_STRING);
10618 si->internal.program_email = getStringCopy(PROGRAM_EMAIL_STRING);
10619 si->internal.program_website = getStringCopy(PROGRAM_WEBSITE_STRING);
10620 si->internal.program_copyright = getStringCopy(PROGRAM_COPYRIGHT_STRING);
10621 si->internal.program_company = getStringCopy(PROGRAM_COMPANY_STRING);
10623 si->internal.program_icon_file = getStringCopy(PROGRAM_ICON_FILENAME);
10625 si->internal.default_graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
10626 si->internal.default_sounds_set = getStringCopy(SND_CLASSIC_SUBDIR);
10627 si->internal.default_music_set = getStringCopy(MUS_CLASSIC_SUBDIR);
10629 si->internal.fallback_graphics_file = getStringCopy(UNDEFINED_FILENAME);
10630 si->internal.fallback_sounds_file = getStringCopy(UNDEFINED_FILENAME);
10631 si->internal.fallback_music_file = getStringCopy(UNDEFINED_FILENAME);
10633 si->internal.default_level_series = getStringCopy(UNDEFINED_LEVELSET);
10634 si->internal.choose_from_top_leveldir = FALSE;
10635 si->internal.show_scaling_in_title = TRUE;
10636 si->internal.create_user_levelset = TRUE;
10637 si->internal.info_screens_from_main = FALSE;
10639 si->internal.default_window_width = WIN_XSIZE_DEFAULT;
10640 si->internal.default_window_height = WIN_YSIZE_DEFAULT;
10642 si->debug.frame_delay[0] = DEFAULT_FRAME_DELAY_0;
10643 si->debug.frame_delay[1] = DEFAULT_FRAME_DELAY_1;
10644 si->debug.frame_delay[2] = DEFAULT_FRAME_DELAY_2;
10645 si->debug.frame_delay[3] = DEFAULT_FRAME_DELAY_3;
10646 si->debug.frame_delay[4] = DEFAULT_FRAME_DELAY_4;
10647 si->debug.frame_delay[5] = DEFAULT_FRAME_DELAY_5;
10648 si->debug.frame_delay[6] = DEFAULT_FRAME_DELAY_6;
10649 si->debug.frame_delay[7] = DEFAULT_FRAME_DELAY_7;
10650 si->debug.frame_delay[8] = DEFAULT_FRAME_DELAY_8;
10651 si->debug.frame_delay[9] = DEFAULT_FRAME_DELAY_9;
10653 si->debug.frame_delay_key[0] = DEFAULT_KEY_FRAME_DELAY_0;
10654 si->debug.frame_delay_key[1] = DEFAULT_KEY_FRAME_DELAY_1;
10655 si->debug.frame_delay_key[2] = DEFAULT_KEY_FRAME_DELAY_2;
10656 si->debug.frame_delay_key[3] = DEFAULT_KEY_FRAME_DELAY_3;
10657 si->debug.frame_delay_key[4] = DEFAULT_KEY_FRAME_DELAY_4;
10658 si->debug.frame_delay_key[5] = DEFAULT_KEY_FRAME_DELAY_5;
10659 si->debug.frame_delay_key[6] = DEFAULT_KEY_FRAME_DELAY_6;
10660 si->debug.frame_delay_key[7] = DEFAULT_KEY_FRAME_DELAY_7;
10661 si->debug.frame_delay_key[8] = DEFAULT_KEY_FRAME_DELAY_8;
10662 si->debug.frame_delay_key[9] = DEFAULT_KEY_FRAME_DELAY_9;
10664 si->debug.frame_delay_use_mod_key = DEFAULT_FRAME_DELAY_USE_MOD_KEY;
10665 si->debug.frame_delay_game_only = DEFAULT_FRAME_DELAY_GAME_ONLY;
10667 si->debug.show_frames_per_second = FALSE;
10669 si->debug.xsn_mode = AUTO;
10670 si->debug.xsn_percent = 0;
10672 si->options.verbose = FALSE;
10673 si->options.debug = FALSE;
10674 si->options.debug_mode = getStringCopy(ARG_UNDEFINED_STRING);
10676 #if defined(PLATFORM_ANDROID)
10677 si->fullscreen = TRUE;
10678 si->touch.overlay_buttons = TRUE;
10681 setHideSetupEntry(&setup.debug.xsn_mode);
10684 static void setSetupInfoToDefaults_AutoSetup(struct SetupInfo *si)
10686 si->auto_setup.editor_zoom_tilesize = MINI_TILESIZE;
10689 static void setSetupInfoToDefaults_ServerSetup(struct SetupInfo *si)
10691 si->player_uuid = NULL; // (will be set later)
10692 si->player_version = 1; // (will be set later)
10694 si->use_api_server = TRUE;
10695 si->api_server_hostname = getStringCopy(API_SERVER_HOSTNAME);
10696 si->api_server_password = getStringCopy(UNDEFINED_PASSWORD);
10697 si->ask_for_uploading_tapes = TRUE;
10698 si->ask_for_remaining_tapes = FALSE;
10699 si->provide_uploading_tapes = TRUE;
10700 si->ask_for_using_api_server = TRUE;
10701 si->has_remaining_tapes = FALSE;
10704 static void setSetupInfoToDefaults_EditorCascade(struct SetupInfo *si)
10706 si->editor_cascade.el_bd = TRUE;
10707 si->editor_cascade.el_em = TRUE;
10708 si->editor_cascade.el_emc = TRUE;
10709 si->editor_cascade.el_rnd = TRUE;
10710 si->editor_cascade.el_sb = TRUE;
10711 si->editor_cascade.el_sp = TRUE;
10712 si->editor_cascade.el_dc = TRUE;
10713 si->editor_cascade.el_dx = TRUE;
10715 si->editor_cascade.el_mm = TRUE;
10716 si->editor_cascade.el_df = TRUE;
10718 si->editor_cascade.el_chars = FALSE;
10719 si->editor_cascade.el_steel_chars = FALSE;
10720 si->editor_cascade.el_ce = FALSE;
10721 si->editor_cascade.el_ge = FALSE;
10722 si->editor_cascade.el_es = FALSE;
10723 si->editor_cascade.el_ref = FALSE;
10724 si->editor_cascade.el_user = FALSE;
10725 si->editor_cascade.el_dynamic = FALSE;
10728 #define MAX_HIDE_SETUP_TOKEN_SIZE 20
10730 static char *getHideSetupToken(void *setup_value)
10732 static char hide_setup_token[MAX_HIDE_SETUP_TOKEN_SIZE];
10734 if (setup_value != NULL)
10735 snprintf(hide_setup_token, MAX_HIDE_SETUP_TOKEN_SIZE, "%p", setup_value);
10737 return hide_setup_token;
10740 void setHideSetupEntry(void *setup_value)
10742 char *hide_setup_token = getHideSetupToken(setup_value);
10744 if (hide_setup_hash == NULL)
10745 hide_setup_hash = newSetupFileHash();
10747 if (setup_value != NULL)
10748 setHashEntry(hide_setup_hash, hide_setup_token, "");
10751 void removeHideSetupEntry(void *setup_value)
10753 char *hide_setup_token = getHideSetupToken(setup_value);
10755 if (setup_value != NULL)
10756 removeHashEntry(hide_setup_hash, hide_setup_token);
10759 boolean hideSetupEntry(void *setup_value)
10761 char *hide_setup_token = getHideSetupToken(setup_value);
10763 return (setup_value != NULL &&
10764 getHashEntry(hide_setup_hash, hide_setup_token) != NULL);
10767 static void setSetupInfoFromTokenText(SetupFileHash *setup_file_hash,
10768 struct TokenInfo *token_info,
10769 int token_nr, char *token_text)
10771 char *token_hide_text = getStringCat2(token_text, ".hide");
10772 char *token_hide_value = getHashEntry(setup_file_hash, token_hide_text);
10774 // set the value of this setup option in the setup option structure
10775 setSetupInfo(token_info, token_nr, getHashEntry(setup_file_hash, token_text));
10777 // check if this setup option should be hidden in the setup menu
10778 if (token_hide_value != NULL && get_boolean_from_string(token_hide_value))
10779 setHideSetupEntry(token_info[token_nr].value);
10781 free(token_hide_text);
10784 static void setSetupInfoFromTokenInfo(SetupFileHash *setup_file_hash,
10785 struct TokenInfo *token_info,
10788 setSetupInfoFromTokenText(setup_file_hash, token_info, token_nr,
10789 token_info[token_nr].text);
10792 static void decodeSetupFileHash_Default(SetupFileHash *setup_file_hash)
10796 if (!setup_file_hash)
10799 for (i = 0; i < ARRAY_SIZE(global_setup_tokens); i++)
10800 setSetupInfoFromTokenInfo(setup_file_hash, global_setup_tokens, i);
10802 setup.touch.grid_initialized = TRUE;
10803 for (i = 0; i < 2; i++)
10805 int grid_xsize = setup.touch.grid_xsize[i];
10806 int grid_ysize = setup.touch.grid_ysize[i];
10809 // if virtual buttons are not loaded from setup file, repeat initializing
10810 // virtual buttons grid with default values later when video is initialized
10811 if (grid_xsize == -1 ||
10814 setup.touch.grid_initialized = FALSE;
10819 for (y = 0; y < grid_ysize; y++)
10821 char token_string[MAX_LINE_LEN];
10823 sprintf(token_string, "touch.virtual_buttons.%d.%02d", i, y);
10825 char *value_string = getHashEntry(setup_file_hash, token_string);
10827 if (value_string == NULL)
10830 for (x = 0; x < grid_xsize; x++)
10832 char c = value_string[x];
10834 setup.touch.grid_button[i][x][y] =
10835 (c == '.' ? CHAR_GRID_BUTTON_NONE : c);
10840 for (i = 0; i < ARRAY_SIZE(editor_setup_tokens); i++)
10841 setSetupInfoFromTokenInfo(setup_file_hash, editor_setup_tokens, i);
10843 for (i = 0; i < ARRAY_SIZE(shortcut_setup_tokens); i++)
10844 setSetupInfoFromTokenInfo(setup_file_hash, shortcut_setup_tokens, i);
10846 for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
10850 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
10852 setup_input = setup.input[pnr];
10853 for (i = 0; i < ARRAY_SIZE(player_setup_tokens); i++)
10855 char full_token[100];
10857 sprintf(full_token, "%s%s", prefix, player_setup_tokens[i].text);
10858 setSetupInfoFromTokenText(setup_file_hash, player_setup_tokens, i,
10861 setup.input[pnr] = setup_input;
10864 for (i = 0; i < ARRAY_SIZE(system_setup_tokens); i++)
10865 setSetupInfoFromTokenInfo(setup_file_hash, system_setup_tokens, i);
10867 for (i = 0; i < ARRAY_SIZE(internal_setup_tokens); i++)
10868 setSetupInfoFromTokenInfo(setup_file_hash, internal_setup_tokens, i);
10870 for (i = 0; i < ARRAY_SIZE(debug_setup_tokens); i++)
10871 setSetupInfoFromTokenInfo(setup_file_hash, debug_setup_tokens, i);
10873 for (i = 0; i < ARRAY_SIZE(options_setup_tokens); i++)
10874 setSetupInfoFromTokenInfo(setup_file_hash, options_setup_tokens, i);
10876 setHideRelatedSetupEntries();
10879 static void decodeSetupFileHash_AutoSetup(SetupFileHash *setup_file_hash)
10883 if (!setup_file_hash)
10886 for (i = 0; i < ARRAY_SIZE(auto_setup_tokens); i++)
10887 setSetupInfo(auto_setup_tokens, i,
10888 getHashEntry(setup_file_hash,
10889 auto_setup_tokens[i].text));
10892 static void decodeSetupFileHash_ServerSetup(SetupFileHash *setup_file_hash)
10896 if (!setup_file_hash)
10899 for (i = 0; i < ARRAY_SIZE(server_setup_tokens); i++)
10900 setSetupInfo(server_setup_tokens, i,
10901 getHashEntry(setup_file_hash,
10902 server_setup_tokens[i].text));
10905 static void decodeSetupFileHash_EditorCascade(SetupFileHash *setup_file_hash)
10909 if (!setup_file_hash)
10912 for (i = 0; i < ARRAY_SIZE(editor_cascade_setup_tokens); i++)
10913 setSetupInfo(editor_cascade_setup_tokens, i,
10914 getHashEntry(setup_file_hash,
10915 editor_cascade_setup_tokens[i].text));
10918 void LoadUserNames(void)
10920 int last_user_nr = user.nr;
10923 if (global.user_names != NULL)
10925 for (i = 0; i < MAX_PLAYER_NAMES; i++)
10926 checked_free(global.user_names[i]);
10928 checked_free(global.user_names);
10931 global.user_names = checked_calloc(MAX_PLAYER_NAMES * sizeof(char *));
10933 for (i = 0; i < MAX_PLAYER_NAMES; i++)
10937 SetupFileHash *setup_file_hash = loadSetupFileHash(getSetupFilename());
10939 if (setup_file_hash)
10941 char *player_name = getHashEntry(setup_file_hash, "player_name");
10943 global.user_names[i] = getFixedUserName(player_name);
10945 freeSetupFileHash(setup_file_hash);
10948 if (global.user_names[i] == NULL)
10949 global.user_names[i] = getStringCopy(getDefaultUserName(i));
10952 user.nr = last_user_nr;
10955 void LoadSetupFromFilename(char *filename)
10957 SetupFileHash *setup_file_hash = loadSetupFileHash(filename);
10959 if (setup_file_hash)
10961 decodeSetupFileHash_Default(setup_file_hash);
10963 freeSetupFileHash(setup_file_hash);
10967 Debug("setup", "using default setup values");
10971 static void LoadSetup_SpecialPostProcessing(void)
10973 char *player_name_new;
10975 // needed to work around problems with fixed length strings
10976 player_name_new = getFixedUserName(setup.player_name);
10977 free(setup.player_name);
10978 setup.player_name = player_name_new;
10980 // "scroll_delay: on(3) / off(0)" was replaced by scroll delay value
10981 if (setup.scroll_delay == FALSE)
10983 setup.scroll_delay_value = MIN_SCROLL_DELAY;
10984 setup.scroll_delay = TRUE; // now always "on"
10987 // make sure that scroll delay value stays inside valid range
10988 setup.scroll_delay_value =
10989 MIN(MAX(MIN_SCROLL_DELAY, setup.scroll_delay_value), MAX_SCROLL_DELAY);
10992 void LoadSetup_Default(void)
10996 // always start with reliable default values
10997 setSetupInfoToDefaults(&setup);
10999 // try to load setup values from default setup file
11000 filename = getDefaultSetupFilename();
11002 if (fileExists(filename))
11003 LoadSetupFromFilename(filename);
11005 // try to load setup values from platform setup file
11006 filename = getPlatformSetupFilename();
11008 if (fileExists(filename))
11009 LoadSetupFromFilename(filename);
11011 // try to load setup values from user setup file
11012 filename = getSetupFilename();
11014 LoadSetupFromFilename(filename);
11016 LoadSetup_SpecialPostProcessing();
11019 void LoadSetup_AutoSetup(void)
11021 char *filename = getPath2(getSetupDir(), AUTOSETUP_FILENAME);
11022 SetupFileHash *setup_file_hash = NULL;
11024 // always start with reliable default values
11025 setSetupInfoToDefaults_AutoSetup(&setup);
11027 setup_file_hash = loadSetupFileHash(filename);
11029 if (setup_file_hash)
11031 decodeSetupFileHash_AutoSetup(setup_file_hash);
11033 freeSetupFileHash(setup_file_hash);
11039 void LoadSetup_ServerSetup(void)
11041 char *filename = getPath2(getSetupDir(), SERVERSETUP_FILENAME);
11042 SetupFileHash *setup_file_hash = NULL;
11044 // always start with reliable default values
11045 setSetupInfoToDefaults_ServerSetup(&setup);
11047 setup_file_hash = loadSetupFileHash(filename);
11049 if (setup_file_hash)
11051 decodeSetupFileHash_ServerSetup(setup_file_hash);
11053 freeSetupFileHash(setup_file_hash);
11058 if (setup.player_uuid == NULL)
11060 // player UUID does not yet exist in setup file
11061 setup.player_uuid = getStringCopy(getUUID());
11062 setup.player_version = 2;
11064 SaveSetup_ServerSetup();
11068 void LoadSetup_EditorCascade(void)
11070 char *filename = getPath2(getSetupDir(), EDITORCASCADE_FILENAME);
11071 SetupFileHash *setup_file_hash = NULL;
11073 // always start with reliable default values
11074 setSetupInfoToDefaults_EditorCascade(&setup);
11076 setup_file_hash = loadSetupFileHash(filename);
11078 if (setup_file_hash)
11080 decodeSetupFileHash_EditorCascade(setup_file_hash);
11082 freeSetupFileHash(setup_file_hash);
11088 void LoadSetup(void)
11090 LoadSetup_Default();
11091 LoadSetup_AutoSetup();
11092 LoadSetup_ServerSetup();
11093 LoadSetup_EditorCascade();
11096 static void addGameControllerMappingToHash(SetupFileHash *mappings_hash,
11097 char *mapping_line)
11099 char mapping_guid[MAX_LINE_LEN];
11100 char *mapping_start, *mapping_end;
11102 // get GUID from game controller mapping line: copy complete line
11103 strncpy(mapping_guid, mapping_line, MAX_LINE_LEN - 1);
11104 mapping_guid[MAX_LINE_LEN - 1] = '\0';
11106 // get GUID from game controller mapping line: cut after GUID part
11107 mapping_start = strchr(mapping_guid, ',');
11108 if (mapping_start != NULL)
11109 *mapping_start = '\0';
11111 // cut newline from game controller mapping line
11112 mapping_end = strchr(mapping_line, '\n');
11113 if (mapping_end != NULL)
11114 *mapping_end = '\0';
11116 // add mapping entry to game controller mappings hash
11117 setHashEntry(mappings_hash, mapping_guid, mapping_line);
11120 static void LoadSetup_ReadGameControllerMappings(SetupFileHash *mappings_hash,
11125 if (!(file = fopen(filename, MODE_READ)))
11127 Warn("cannot read game controller mappings file '%s'", filename);
11132 while (!feof(file))
11134 char line[MAX_LINE_LEN];
11136 if (!fgets(line, MAX_LINE_LEN, file))
11139 addGameControllerMappingToHash(mappings_hash, line);
11145 void SaveSetup_Default(void)
11147 char *filename = getSetupFilename();
11151 InitUserDataDirectory();
11153 if (!(file = fopen(filename, MODE_WRITE)))
11155 Warn("cannot write setup file '%s'", filename);
11160 fprintFileHeader(file, SETUP_FILENAME);
11162 for (i = 0; i < ARRAY_SIZE(global_setup_tokens); i++)
11164 // just to make things nicer :)
11165 if (global_setup_tokens[i].value == &setup.multiple_users ||
11166 global_setup_tokens[i].value == &setup.sound ||
11167 global_setup_tokens[i].value == &setup.graphics_set ||
11168 global_setup_tokens[i].value == &setup.volume_simple ||
11169 global_setup_tokens[i].value == &setup.network_mode ||
11170 global_setup_tokens[i].value == &setup.touch.control_type ||
11171 global_setup_tokens[i].value == &setup.touch.grid_xsize[0] ||
11172 global_setup_tokens[i].value == &setup.touch.grid_xsize[1])
11173 fprintf(file, "\n");
11175 fprintf(file, "%s\n", getSetupLine(global_setup_tokens, "", i));
11178 for (i = 0; i < 2; i++)
11180 int grid_xsize = setup.touch.grid_xsize[i];
11181 int grid_ysize = setup.touch.grid_ysize[i];
11184 fprintf(file, "\n");
11186 for (y = 0; y < grid_ysize; y++)
11188 char token_string[MAX_LINE_LEN];
11189 char value_string[MAX_LINE_LEN];
11191 sprintf(token_string, "touch.virtual_buttons.%d.%02d", i, y);
11193 for (x = 0; x < grid_xsize; x++)
11195 char c = setup.touch.grid_button[i][x][y];
11197 value_string[x] = (c == CHAR_GRID_BUTTON_NONE ? '.' : c);
11200 value_string[grid_xsize] = '\0';
11202 fprintf(file, "%s\n", getFormattedSetupEntry(token_string, value_string));
11206 fprintf(file, "\n");
11207 for (i = 0; i < ARRAY_SIZE(editor_setup_tokens); i++)
11208 fprintf(file, "%s\n", getSetupLine(editor_setup_tokens, "", i));
11210 fprintf(file, "\n");
11211 for (i = 0; i < ARRAY_SIZE(shortcut_setup_tokens); i++)
11212 fprintf(file, "%s\n", getSetupLine(shortcut_setup_tokens, "", i));
11214 for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
11218 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
11219 fprintf(file, "\n");
11221 setup_input = setup.input[pnr];
11222 for (i = 0; i < ARRAY_SIZE(player_setup_tokens); i++)
11223 fprintf(file, "%s\n", getSetupLine(player_setup_tokens, prefix, i));
11226 fprintf(file, "\n");
11227 for (i = 0; i < ARRAY_SIZE(system_setup_tokens); i++)
11228 fprintf(file, "%s\n", getSetupLine(system_setup_tokens, "", i));
11230 // (internal setup values not saved to user setup file)
11232 fprintf(file, "\n");
11233 for (i = 0; i < ARRAY_SIZE(debug_setup_tokens); i++)
11234 if (!strPrefix(debug_setup_tokens[i].text, "debug.xsn_") ||
11235 setup.debug.xsn_mode != AUTO)
11236 fprintf(file, "%s\n", getSetupLine(debug_setup_tokens, "", i));
11238 fprintf(file, "\n");
11239 for (i = 0; i < ARRAY_SIZE(options_setup_tokens); i++)
11240 fprintf(file, "%s\n", getSetupLine(options_setup_tokens, "", i));
11244 SetFilePermissions(filename, PERMS_PRIVATE);
11247 void SaveSetup_AutoSetup(void)
11249 char *filename = getPath2(getSetupDir(), AUTOSETUP_FILENAME);
11253 InitUserDataDirectory();
11255 if (!(file = fopen(filename, MODE_WRITE)))
11257 Warn("cannot write auto setup file '%s'", filename);
11264 fprintFileHeader(file, AUTOSETUP_FILENAME);
11266 for (i = 0; i < ARRAY_SIZE(auto_setup_tokens); i++)
11267 fprintf(file, "%s\n", getSetupLine(auto_setup_tokens, "", i));
11271 SetFilePermissions(filename, PERMS_PRIVATE);
11276 void SaveSetup_ServerSetup(void)
11278 char *filename = getPath2(getSetupDir(), SERVERSETUP_FILENAME);
11282 InitUserDataDirectory();
11284 if (!(file = fopen(filename, MODE_WRITE)))
11286 Warn("cannot write server setup file '%s'", filename);
11293 fprintFileHeader(file, SERVERSETUP_FILENAME);
11295 for (i = 0; i < ARRAY_SIZE(server_setup_tokens); i++)
11297 // just to make things nicer :)
11298 if (server_setup_tokens[i].value == &setup.use_api_server)
11299 fprintf(file, "\n");
11301 fprintf(file, "%s\n", getSetupLine(server_setup_tokens, "", i));
11306 SetFilePermissions(filename, PERMS_PRIVATE);
11311 void SaveSetup_EditorCascade(void)
11313 char *filename = getPath2(getSetupDir(), EDITORCASCADE_FILENAME);
11317 InitUserDataDirectory();
11319 if (!(file = fopen(filename, MODE_WRITE)))
11321 Warn("cannot write editor cascade state file '%s'", filename);
11328 fprintFileHeader(file, EDITORCASCADE_FILENAME);
11330 for (i = 0; i < ARRAY_SIZE(editor_cascade_setup_tokens); i++)
11331 fprintf(file, "%s\n", getSetupLine(editor_cascade_setup_tokens, "", i));
11335 SetFilePermissions(filename, PERMS_PRIVATE);
11340 void SaveSetup(void)
11342 SaveSetup_Default();
11343 SaveSetup_AutoSetup();
11344 SaveSetup_ServerSetup();
11345 SaveSetup_EditorCascade();
11348 static void SaveSetup_WriteGameControllerMappings(SetupFileHash *mappings_hash,
11353 if (!(file = fopen(filename, MODE_WRITE)))
11355 Warn("cannot write game controller mappings file '%s'", filename);
11360 BEGIN_HASH_ITERATION(mappings_hash, itr)
11362 fprintf(file, "%s\n", HASH_ITERATION_VALUE(itr));
11364 END_HASH_ITERATION(mappings_hash, itr)
11369 void SaveSetup_AddGameControllerMapping(char *mapping)
11371 char *filename = getPath2(getSetupDir(), GAMECONTROLLER_BASENAME);
11372 SetupFileHash *mappings_hash = newSetupFileHash();
11374 InitUserDataDirectory();
11376 // load existing personal game controller mappings
11377 LoadSetup_ReadGameControllerMappings(mappings_hash, filename);
11379 // add new mapping to personal game controller mappings
11380 addGameControllerMappingToHash(mappings_hash, mapping);
11382 // save updated personal game controller mappings
11383 SaveSetup_WriteGameControllerMappings(mappings_hash, filename);
11385 freeSetupFileHash(mappings_hash);
11389 void LoadCustomElementDescriptions(void)
11391 char *filename = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
11392 SetupFileHash *setup_file_hash;
11395 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
11397 if (element_info[i].custom_description != NULL)
11399 free(element_info[i].custom_description);
11400 element_info[i].custom_description = NULL;
11404 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
11407 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
11409 char *token = getStringCat2(element_info[i].token_name, ".name");
11410 char *value = getHashEntry(setup_file_hash, token);
11413 element_info[i].custom_description = getStringCopy(value);
11418 freeSetupFileHash(setup_file_hash);
11421 static int getElementFromToken(char *token)
11423 char *value = getHashEntry(element_token_hash, token);
11426 return atoi(value);
11428 Warn("unknown element token '%s'", token);
11430 return EL_UNDEFINED;
11433 void FreeGlobalAnimEventInfo(void)
11435 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
11437 if (gaei->event_list == NULL)
11442 for (i = 0; i < gaei->num_event_lists; i++)
11444 checked_free(gaei->event_list[i]->event_value);
11445 checked_free(gaei->event_list[i]);
11448 checked_free(gaei->event_list);
11450 gaei->event_list = NULL;
11451 gaei->num_event_lists = 0;
11454 static int AddGlobalAnimEventList(void)
11456 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
11457 int list_pos = gaei->num_event_lists++;
11459 gaei->event_list = checked_realloc(gaei->event_list, gaei->num_event_lists *
11460 sizeof(struct GlobalAnimEventListInfo *));
11462 gaei->event_list[list_pos] =
11463 checked_calloc(sizeof(struct GlobalAnimEventListInfo));
11465 struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
11467 gaeli->event_value = NULL;
11468 gaeli->num_event_values = 0;
11473 static int AddGlobalAnimEventValue(int list_pos, int event_value)
11475 // do not add empty global animation events
11476 if (event_value == ANIM_EVENT_NONE)
11479 // if list position is undefined, create new list
11480 if (list_pos == ANIM_EVENT_UNDEFINED)
11481 list_pos = AddGlobalAnimEventList();
11483 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
11484 struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
11485 int value_pos = gaeli->num_event_values++;
11487 gaeli->event_value = checked_realloc(gaeli->event_value,
11488 gaeli->num_event_values * sizeof(int *));
11490 gaeli->event_value[value_pos] = event_value;
11495 int GetGlobalAnimEventValue(int list_pos, int value_pos)
11497 if (list_pos == ANIM_EVENT_UNDEFINED)
11498 return ANIM_EVENT_NONE;
11500 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
11501 struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
11503 return gaeli->event_value[value_pos];
11506 int GetGlobalAnimEventValueCount(int list_pos)
11508 if (list_pos == ANIM_EVENT_UNDEFINED)
11511 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
11512 struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
11514 return gaeli->num_event_values;
11517 // This function checks if a string <s> of the format "string1, string2, ..."
11518 // exactly contains a string <s_contained>.
11520 static boolean string_has_parameter(char *s, char *s_contained)
11524 if (s == NULL || s_contained == NULL)
11527 if (strlen(s_contained) > strlen(s))
11530 if (strncmp(s, s_contained, strlen(s_contained)) == 0)
11532 char next_char = s[strlen(s_contained)];
11534 // check if next character is delimiter or whitespace
11535 if (next_char == ',' || next_char == '\0' ||
11536 next_char == ' ' || next_char == '\t')
11540 // check if string contains another parameter string after a comma
11541 substring = strchr(s, ',');
11542 if (substring == NULL) // string does not contain a comma
11545 // advance string pointer to next character after the comma
11548 // skip potential whitespaces after the comma
11549 while (*substring == ' ' || *substring == '\t')
11552 return string_has_parameter(substring, s_contained);
11555 static int get_anim_parameter_value_ce(char *s)
11558 char *pattern_1 = "ce_change:custom_";
11559 char *pattern_2 = ".page_";
11560 int pattern_1_len = strlen(pattern_1);
11561 char *matching_char = strstr(s_ptr, pattern_1);
11562 int result = ANIM_EVENT_NONE;
11564 if (matching_char == NULL)
11565 return ANIM_EVENT_NONE;
11567 result = ANIM_EVENT_CE_CHANGE;
11569 s_ptr = matching_char + pattern_1_len;
11571 // check for custom element number ("custom_X", "custom_XX" or "custom_XXX")
11572 if (*s_ptr >= '0' && *s_ptr <= '9')
11574 int gic_ce_nr = (*s_ptr++ - '0');
11576 if (*s_ptr >= '0' && *s_ptr <= '9')
11578 gic_ce_nr = 10 * gic_ce_nr + (*s_ptr++ - '0');
11580 if (*s_ptr >= '0' && *s_ptr <= '9')
11581 gic_ce_nr = 10 * gic_ce_nr + (*s_ptr++ - '0');
11584 if (gic_ce_nr < 1 || gic_ce_nr > NUM_CUSTOM_ELEMENTS)
11585 return ANIM_EVENT_NONE;
11587 // custom element stored as 0 to 255
11590 result |= gic_ce_nr << ANIM_EVENT_CE_BIT;
11594 // invalid custom element number specified
11596 return ANIM_EVENT_NONE;
11599 // check for change page number ("page_X" or "page_XX") (optional)
11600 if (strPrefix(s_ptr, pattern_2))
11602 s_ptr += strlen(pattern_2);
11604 if (*s_ptr >= '0' && *s_ptr <= '9')
11606 int gic_page_nr = (*s_ptr++ - '0');
11608 if (*s_ptr >= '0' && *s_ptr <= '9')
11609 gic_page_nr = 10 * gic_page_nr + (*s_ptr++ - '0');
11611 if (gic_page_nr < 1 || gic_page_nr > MAX_CHANGE_PAGES)
11612 return ANIM_EVENT_NONE;
11614 // change page stored as 1 to 32 (0 means "all change pages")
11616 result |= gic_page_nr << ANIM_EVENT_PAGE_BIT;
11620 // invalid animation part number specified
11622 return ANIM_EVENT_NONE;
11626 // discard result if next character is neither delimiter nor whitespace
11627 if (!(*s_ptr == ',' || *s_ptr == '\0' ||
11628 *s_ptr == ' ' || *s_ptr == '\t'))
11629 return ANIM_EVENT_NONE;
11634 static int get_anim_parameter_value(char *s)
11636 int event_value[] =
11644 char *pattern_1[] =
11652 char *pattern_2 = ".part_";
11653 char *matching_char = NULL;
11655 int pattern_1_len = 0;
11656 int result = ANIM_EVENT_NONE;
11659 result = get_anim_parameter_value_ce(s);
11661 if (result != ANIM_EVENT_NONE)
11664 for (i = 0; i < ARRAY_SIZE(event_value); i++)
11666 matching_char = strstr(s_ptr, pattern_1[i]);
11667 pattern_1_len = strlen(pattern_1[i]);
11668 result = event_value[i];
11670 if (matching_char != NULL)
11674 if (matching_char == NULL)
11675 return ANIM_EVENT_NONE;
11677 s_ptr = matching_char + pattern_1_len;
11679 // check for main animation number ("anim_X" or "anim_XX")
11680 if (*s_ptr >= '0' && *s_ptr <= '9')
11682 int gic_anim_nr = (*s_ptr++ - '0');
11684 if (*s_ptr >= '0' && *s_ptr <= '9')
11685 gic_anim_nr = 10 * gic_anim_nr + (*s_ptr++ - '0');
11687 if (gic_anim_nr < 1 || gic_anim_nr > MAX_GLOBAL_ANIMS)
11688 return ANIM_EVENT_NONE;
11690 result |= gic_anim_nr << ANIM_EVENT_ANIM_BIT;
11694 // invalid main animation number specified
11696 return ANIM_EVENT_NONE;
11699 // check for animation part number ("part_X" or "part_XX") (optional)
11700 if (strPrefix(s_ptr, pattern_2))
11702 s_ptr += strlen(pattern_2);
11704 if (*s_ptr >= '0' && *s_ptr <= '9')
11706 int gic_part_nr = (*s_ptr++ - '0');
11708 if (*s_ptr >= '0' && *s_ptr <= '9')
11709 gic_part_nr = 10 * gic_part_nr + (*s_ptr++ - '0');
11711 if (gic_part_nr < 1 || gic_part_nr > MAX_GLOBAL_ANIM_PARTS)
11712 return ANIM_EVENT_NONE;
11714 result |= gic_part_nr << ANIM_EVENT_PART_BIT;
11718 // invalid animation part number specified
11720 return ANIM_EVENT_NONE;
11724 // discard result if next character is neither delimiter nor whitespace
11725 if (!(*s_ptr == ',' || *s_ptr == '\0' ||
11726 *s_ptr == ' ' || *s_ptr == '\t'))
11727 return ANIM_EVENT_NONE;
11732 static int get_anim_parameter_values(char *s)
11734 int list_pos = ANIM_EVENT_UNDEFINED;
11735 int event_value = ANIM_EVENT_DEFAULT;
11737 if (string_has_parameter(s, "any"))
11738 event_value |= ANIM_EVENT_ANY;
11740 if (string_has_parameter(s, "click:self") ||
11741 string_has_parameter(s, "click") ||
11742 string_has_parameter(s, "self"))
11743 event_value |= ANIM_EVENT_SELF;
11745 if (string_has_parameter(s, "unclick:any"))
11746 event_value |= ANIM_EVENT_UNCLICK_ANY;
11748 // if animation event found, add it to global animation event list
11749 if (event_value != ANIM_EVENT_NONE)
11750 list_pos = AddGlobalAnimEventValue(list_pos, event_value);
11754 // add optional "click:anim_X" or "click:anim_X.part_X" parameter
11755 event_value = get_anim_parameter_value(s);
11757 // if animation event found, add it to global animation event list
11758 if (event_value != ANIM_EVENT_NONE)
11759 list_pos = AddGlobalAnimEventValue(list_pos, event_value);
11761 // continue with next part of the string, starting with next comma
11762 s = strchr(s + 1, ',');
11768 static int get_anim_action_parameter_value(char *token)
11770 // check most common default case first to massively speed things up
11771 if (strEqual(token, ARG_UNDEFINED))
11772 return ANIM_EVENT_ACTION_NONE;
11774 int result = getImageIDFromToken(token);
11778 char *gfx_token = getStringCat2("gfx.", token);
11780 result = getImageIDFromToken(gfx_token);
11782 checked_free(gfx_token);
11787 Key key = getKeyFromX11KeyName(token);
11789 if (key != KSYM_UNDEFINED)
11790 result = -(int)key;
11797 result = get_hash_from_key(token); // unsigned int => int
11798 result = ABS(result); // may be negative now
11799 result += (result < MAX_IMAGE_FILES ? MAX_IMAGE_FILES : 0);
11801 setHashEntry(anim_url_hash, int2str(result, 0), token);
11806 result = ANIM_EVENT_ACTION_NONE;
11811 int get_parameter_value(char *value_raw, char *suffix, int type)
11813 char *value = getStringToLower(value_raw);
11814 int result = 0; // probably a save default value
11816 if (strEqual(suffix, ".direction"))
11818 result = (strEqual(value, "left") ? MV_LEFT :
11819 strEqual(value, "right") ? MV_RIGHT :
11820 strEqual(value, "up") ? MV_UP :
11821 strEqual(value, "down") ? MV_DOWN : MV_NONE);
11823 else if (strEqual(suffix, ".position"))
11825 result = (strEqual(value, "left") ? POS_LEFT :
11826 strEqual(value, "right") ? POS_RIGHT :
11827 strEqual(value, "top") ? POS_TOP :
11828 strEqual(value, "upper") ? POS_UPPER :
11829 strEqual(value, "middle") ? POS_MIDDLE :
11830 strEqual(value, "lower") ? POS_LOWER :
11831 strEqual(value, "bottom") ? POS_BOTTOM :
11832 strEqual(value, "any") ? POS_ANY :
11833 strEqual(value, "ce") ? POS_CE :
11834 strEqual(value, "ce_trigger") ? POS_CE_TRIGGER :
11835 strEqual(value, "last") ? POS_LAST : POS_UNDEFINED);
11837 else if (strEqual(suffix, ".align"))
11839 result = (strEqual(value, "left") ? ALIGN_LEFT :
11840 strEqual(value, "right") ? ALIGN_RIGHT :
11841 strEqual(value, "center") ? ALIGN_CENTER :
11842 strEqual(value, "middle") ? ALIGN_CENTER : ALIGN_DEFAULT);
11844 else if (strEqual(suffix, ".valign"))
11846 result = (strEqual(value, "top") ? VALIGN_TOP :
11847 strEqual(value, "bottom") ? VALIGN_BOTTOM :
11848 strEqual(value, "middle") ? VALIGN_MIDDLE :
11849 strEqual(value, "center") ? VALIGN_MIDDLE : VALIGN_DEFAULT);
11851 else if (strEqual(suffix, ".anim_mode"))
11853 result = (string_has_parameter(value, "none") ? ANIM_NONE :
11854 string_has_parameter(value, "loop") ? ANIM_LOOP :
11855 string_has_parameter(value, "linear") ? ANIM_LINEAR :
11856 string_has_parameter(value, "pingpong") ? ANIM_PINGPONG :
11857 string_has_parameter(value, "pingpong2") ? ANIM_PINGPONG2 :
11858 string_has_parameter(value, "random") ? ANIM_RANDOM :
11859 string_has_parameter(value, "random_static") ? ANIM_RANDOM_STATIC :
11860 string_has_parameter(value, "ce_value") ? ANIM_CE_VALUE :
11861 string_has_parameter(value, "ce_score") ? ANIM_CE_SCORE :
11862 string_has_parameter(value, "ce_delay") ? ANIM_CE_DELAY :
11863 string_has_parameter(value, "horizontal") ? ANIM_HORIZONTAL :
11864 string_has_parameter(value, "vertical") ? ANIM_VERTICAL :
11865 string_has_parameter(value, "centered") ? ANIM_CENTERED :
11866 string_has_parameter(value, "all") ? ANIM_ALL :
11867 string_has_parameter(value, "tiled") ? ANIM_TILED :
11868 string_has_parameter(value, "level_nr") ? ANIM_LEVEL_NR :
11871 if (string_has_parameter(value, "once"))
11872 result |= ANIM_ONCE;
11874 if (string_has_parameter(value, "reverse"))
11875 result |= ANIM_REVERSE;
11877 if (string_has_parameter(value, "opaque_player"))
11878 result |= ANIM_OPAQUE_PLAYER;
11880 if (string_has_parameter(value, "static_panel"))
11881 result |= ANIM_STATIC_PANEL;
11883 else if (strEqual(suffix, ".init_event") ||
11884 strEqual(suffix, ".anim_event"))
11886 result = get_anim_parameter_values(value);
11888 else if (strEqual(suffix, ".init_delay_action") ||
11889 strEqual(suffix, ".anim_delay_action") ||
11890 strEqual(suffix, ".post_delay_action") ||
11891 strEqual(suffix, ".init_event_action") ||
11892 strEqual(suffix, ".anim_event_action"))
11894 result = get_anim_action_parameter_value(value_raw);
11896 else if (strEqual(suffix, ".class"))
11898 result = (strEqual(value, ARG_UNDEFINED) ? ARG_UNDEFINED_VALUE :
11899 get_hash_from_key(value));
11901 else if (strEqual(suffix, ".style"))
11903 result = STYLE_DEFAULT;
11905 if (string_has_parameter(value, "accurate_borders"))
11906 result |= STYLE_ACCURATE_BORDERS;
11908 if (string_has_parameter(value, "inner_corners"))
11909 result |= STYLE_INNER_CORNERS;
11911 if (string_has_parameter(value, "reverse"))
11912 result |= STYLE_REVERSE;
11914 if (string_has_parameter(value, "leftmost_position"))
11915 result |= STYLE_LEFTMOST_POSITION;
11917 if (string_has_parameter(value, "block_clicks"))
11918 result |= STYLE_BLOCK;
11920 if (string_has_parameter(value, "passthrough_clicks"))
11921 result |= STYLE_PASSTHROUGH;
11923 if (string_has_parameter(value, "multiple_actions"))
11924 result |= STYLE_MULTIPLE_ACTIONS;
11926 if (string_has_parameter(value, "consume_ce_event"))
11927 result |= STYLE_CONSUME_CE_EVENT;
11929 else if (strEqual(suffix, ".fade_mode"))
11931 result = (string_has_parameter(value, "none") ? FADE_MODE_NONE :
11932 string_has_parameter(value, "fade") ? FADE_MODE_FADE :
11933 string_has_parameter(value, "fade_in") ? FADE_MODE_FADE_IN :
11934 string_has_parameter(value, "fade_out") ? FADE_MODE_FADE_OUT :
11935 string_has_parameter(value, "crossfade") ? FADE_MODE_CROSSFADE :
11936 string_has_parameter(value, "melt") ? FADE_MODE_MELT :
11937 string_has_parameter(value, "curtain") ? FADE_MODE_CURTAIN :
11938 FADE_MODE_DEFAULT);
11940 else if (strEqual(suffix, ".auto_delay_unit"))
11942 result = (string_has_parameter(value, "ms") ? AUTO_DELAY_UNIT_MS :
11943 string_has_parameter(value, "frames") ? AUTO_DELAY_UNIT_FRAMES :
11944 AUTO_DELAY_UNIT_DEFAULT);
11946 else if (strPrefix(suffix, ".font")) // (may also be ".font_xyz")
11948 result = gfx.get_font_from_token_function(value);
11950 else // generic parameter of type integer or boolean
11952 result = (strEqual(value, ARG_UNDEFINED) ? ARG_UNDEFINED_VALUE :
11953 type == TYPE_INTEGER ? get_integer_from_string(value) :
11954 type == TYPE_BOOLEAN ? get_boolean_from_string(value) :
11955 ARG_UNDEFINED_VALUE);
11963 static int get_token_parameter_value(char *token, char *value_raw)
11967 if (token == NULL || value_raw == NULL)
11968 return ARG_UNDEFINED_VALUE;
11970 suffix = strrchr(token, '.');
11971 if (suffix == NULL)
11974 if (strEqual(suffix, ".element"))
11975 return getElementFromToken(value_raw);
11977 // !!! USE CORRECT VALUE TYPE (currently works also for TYPE_BOOLEAN) !!!
11978 return get_parameter_value(value_raw, suffix, TYPE_INTEGER);
11981 void InitMenuDesignSettings_FromHash(SetupFileHash *setup_file_hash,
11982 boolean ignore_defaults)
11986 for (i = 0; image_config_vars[i].token != NULL; i++)
11988 char *value = getHashEntry(setup_file_hash, image_config_vars[i].token);
11990 // (ignore definitions set to "[DEFAULT]" which are already initialized)
11991 if (ignore_defaults && strEqual(value, ARG_DEFAULT))
11995 *image_config_vars[i].value =
11996 get_token_parameter_value(image_config_vars[i].token, value);
12000 void InitMenuDesignSettings_Static(void)
12002 // always start with reliable default values from static default config
12003 InitMenuDesignSettings_FromHash(image_config_hash, FALSE);
12006 static void InitMenuDesignSettings_SpecialPreProcessing(void)
12010 // the following initializes hierarchical values from static configuration
12012 // special case: initialize "ARG_DEFAULT" values in static default config
12013 // (e.g., initialize "[titlemessage].fade_mode" from "[title].fade_mode")
12014 titlescreen_initial_first_default.fade_mode =
12015 title_initial_first_default.fade_mode;
12016 titlescreen_initial_first_default.fade_delay =
12017 title_initial_first_default.fade_delay;
12018 titlescreen_initial_first_default.post_delay =
12019 title_initial_first_default.post_delay;
12020 titlescreen_initial_first_default.auto_delay =
12021 title_initial_first_default.auto_delay;
12022 titlescreen_initial_first_default.auto_delay_unit =
12023 title_initial_first_default.auto_delay_unit;
12024 titlescreen_first_default.fade_mode = title_first_default.fade_mode;
12025 titlescreen_first_default.fade_delay = title_first_default.fade_delay;
12026 titlescreen_first_default.post_delay = title_first_default.post_delay;
12027 titlescreen_first_default.auto_delay = title_first_default.auto_delay;
12028 titlescreen_first_default.auto_delay_unit =
12029 title_first_default.auto_delay_unit;
12030 titlemessage_initial_first_default.fade_mode =
12031 title_initial_first_default.fade_mode;
12032 titlemessage_initial_first_default.fade_delay =
12033 title_initial_first_default.fade_delay;
12034 titlemessage_initial_first_default.post_delay =
12035 title_initial_first_default.post_delay;
12036 titlemessage_initial_first_default.auto_delay =
12037 title_initial_first_default.auto_delay;
12038 titlemessage_initial_first_default.auto_delay_unit =
12039 title_initial_first_default.auto_delay_unit;
12040 titlemessage_first_default.fade_mode = title_first_default.fade_mode;
12041 titlemessage_first_default.fade_delay = title_first_default.fade_delay;
12042 titlemessage_first_default.post_delay = title_first_default.post_delay;
12043 titlemessage_first_default.auto_delay = title_first_default.auto_delay;
12044 titlemessage_first_default.auto_delay_unit =
12045 title_first_default.auto_delay_unit;
12047 titlescreen_initial_default.fade_mode = title_initial_default.fade_mode;
12048 titlescreen_initial_default.fade_delay = title_initial_default.fade_delay;
12049 titlescreen_initial_default.post_delay = title_initial_default.post_delay;
12050 titlescreen_initial_default.auto_delay = title_initial_default.auto_delay;
12051 titlescreen_initial_default.auto_delay_unit =
12052 title_initial_default.auto_delay_unit;
12053 titlescreen_default.fade_mode = title_default.fade_mode;
12054 titlescreen_default.fade_delay = title_default.fade_delay;
12055 titlescreen_default.post_delay = title_default.post_delay;
12056 titlescreen_default.auto_delay = title_default.auto_delay;
12057 titlescreen_default.auto_delay_unit = title_default.auto_delay_unit;
12058 titlemessage_initial_default.fade_mode = title_initial_default.fade_mode;
12059 titlemessage_initial_default.fade_delay = title_initial_default.fade_delay;
12060 titlemessage_initial_default.post_delay = title_initial_default.post_delay;
12061 titlemessage_initial_default.auto_delay_unit =
12062 title_initial_default.auto_delay_unit;
12063 titlemessage_default.fade_mode = title_default.fade_mode;
12064 titlemessage_default.fade_delay = title_default.fade_delay;
12065 titlemessage_default.post_delay = title_default.post_delay;
12066 titlemessage_default.auto_delay = title_default.auto_delay;
12067 titlemessage_default.auto_delay_unit = title_default.auto_delay_unit;
12069 // special case: initialize "ARG_DEFAULT" values in static default config
12070 // (e.g., init "titlemessage_1.fade_mode" from "[titlemessage].fade_mode")
12071 for (i = 0; i < MAX_NUM_TITLE_MESSAGES; i++)
12073 titlescreen_initial_first[i] = titlescreen_initial_first_default;
12074 titlescreen_first[i] = titlescreen_first_default;
12075 titlemessage_initial_first[i] = titlemessage_initial_first_default;
12076 titlemessage_first[i] = titlemessage_first_default;
12078 titlescreen_initial[i] = titlescreen_initial_default;
12079 titlescreen[i] = titlescreen_default;
12080 titlemessage_initial[i] = titlemessage_initial_default;
12081 titlemessage[i] = titlemessage_default;
12084 // special case: initialize "ARG_DEFAULT" values in static default config
12085 // (eg, init "menu.enter_screen.SCORES.xyz" from "menu.enter_screen.xyz")
12086 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
12088 if (i == GFX_SPECIAL_ARG_TITLE) // title values already initialized
12091 menu.enter_screen[i] = menu.enter_screen[GFX_SPECIAL_ARG_DEFAULT];
12092 menu.leave_screen[i] = menu.leave_screen[GFX_SPECIAL_ARG_DEFAULT];
12093 menu.next_screen[i] = menu.next_screen[GFX_SPECIAL_ARG_DEFAULT];
12096 // special case: initialize "ARG_DEFAULT" values in static default config
12097 // (eg, init "viewport.door_1.MAIN.xyz" from "viewport.door_1.xyz")
12098 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
12100 viewport.window[i] = viewport.window[GFX_SPECIAL_ARG_DEFAULT];
12101 viewport.playfield[i] = viewport.playfield[GFX_SPECIAL_ARG_DEFAULT];
12102 viewport.door_1[i] = viewport.door_1[GFX_SPECIAL_ARG_DEFAULT];
12104 if (i == GFX_SPECIAL_ARG_EDITOR) // editor values already initialized
12107 viewport.door_2[i] = viewport.door_2[GFX_SPECIAL_ARG_DEFAULT];
12111 static void InitMenuDesignSettings_SpecialPostProcessing(void)
12115 struct XY *dst, *src;
12117 game_buttons_xy[] =
12119 { &game.button.save, &game.button.stop },
12120 { &game.button.pause2, &game.button.pause },
12121 { &game.button.load, &game.button.play },
12122 { &game.button.undo, &game.button.stop },
12123 { &game.button.redo, &game.button.play },
12129 // special case: initialize later added SETUP list size from LEVELS value
12130 if (menu.list_size[GAME_MODE_SETUP] == -1)
12131 menu.list_size[GAME_MODE_SETUP] = menu.list_size[GAME_MODE_LEVELS];
12133 // set default position for snapshot buttons to stop/pause/play buttons
12134 for (i = 0; game_buttons_xy[i].dst != NULL; i++)
12135 if ((*game_buttons_xy[i].dst).x == -1 &&
12136 (*game_buttons_xy[i].dst).y == -1)
12137 *game_buttons_xy[i].dst = *game_buttons_xy[i].src;
12139 // --------------------------------------------------------------------------
12140 // dynamic viewports (including playfield margins, borders and alignments)
12141 // --------------------------------------------------------------------------
12143 // dynamic viewports currently only supported for landscape mode
12144 int display_width = MAX(video.display_width, video.display_height);
12145 int display_height = MIN(video.display_width, video.display_height);
12147 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
12149 struct RectWithBorder *vp_window = &viewport.window[i];
12150 struct RectWithBorder *vp_playfield = &viewport.playfield[i];
12151 struct RectWithBorder *vp_door_1 = &viewport.door_1[i];
12152 struct RectWithBorder *vp_door_2 = &viewport.door_2[i];
12153 boolean dynamic_window_width = (vp_window->min_width != -1);
12154 boolean dynamic_window_height = (vp_window->min_height != -1);
12155 boolean dynamic_playfield_width = (vp_playfield->min_width != -1);
12156 boolean dynamic_playfield_height = (vp_playfield->min_height != -1);
12158 // adjust window size if min/max width/height is specified
12160 if (vp_window->min_width != -1)
12162 int window_width = display_width;
12164 // when using static window height, use aspect ratio of display
12165 if (vp_window->min_height == -1)
12166 window_width = vp_window->height * display_width / display_height;
12168 vp_window->width = MAX(vp_window->min_width, window_width);
12171 if (vp_window->min_height != -1)
12173 int window_height = display_height;
12175 // when using static window width, use aspect ratio of display
12176 if (vp_window->min_width == -1)
12177 window_height = vp_window->width * display_height / display_width;
12179 vp_window->height = MAX(vp_window->min_height, window_height);
12182 if (vp_window->max_width != -1)
12183 vp_window->width = MIN(vp_window->width, vp_window->max_width);
12185 if (vp_window->max_height != -1)
12186 vp_window->height = MIN(vp_window->height, vp_window->max_height);
12188 int playfield_width = vp_window->width;
12189 int playfield_height = vp_window->height;
12191 // adjust playfield size and position according to specified margins
12193 playfield_width -= vp_playfield->margin_left;
12194 playfield_width -= vp_playfield->margin_right;
12196 playfield_height -= vp_playfield->margin_top;
12197 playfield_height -= vp_playfield->margin_bottom;
12199 // adjust playfield size if min/max width/height is specified
12201 if (vp_playfield->min_width != -1)
12202 vp_playfield->width = MAX(vp_playfield->min_width, playfield_width);
12204 if (vp_playfield->min_height != -1)
12205 vp_playfield->height = MAX(vp_playfield->min_height, playfield_height);
12207 if (vp_playfield->max_width != -1)
12208 vp_playfield->width = MIN(vp_playfield->width, vp_playfield->max_width);
12210 if (vp_playfield->max_height != -1)
12211 vp_playfield->height = MIN(vp_playfield->height, vp_playfield->max_height);
12213 // adjust playfield position according to specified alignment
12215 if (vp_playfield->align == ALIGN_LEFT || vp_playfield->x > 0)
12216 vp_playfield->x = ALIGNED_VP_XPOS(vp_playfield);
12217 else if (vp_playfield->align == ALIGN_CENTER)
12218 vp_playfield->x = playfield_width / 2 - vp_playfield->width / 2;
12219 else if (vp_playfield->align == ALIGN_RIGHT)
12220 vp_playfield->x += playfield_width - vp_playfield->width;
12222 if (vp_playfield->valign == VALIGN_TOP || vp_playfield->y > 0)
12223 vp_playfield->y = ALIGNED_VP_YPOS(vp_playfield);
12224 else if (vp_playfield->valign == VALIGN_MIDDLE)
12225 vp_playfield->y = playfield_height / 2 - vp_playfield->height / 2;
12226 else if (vp_playfield->valign == VALIGN_BOTTOM)
12227 vp_playfield->y += playfield_height - vp_playfield->height;
12229 vp_playfield->x += vp_playfield->margin_left;
12230 vp_playfield->y += vp_playfield->margin_top;
12232 // adjust individual playfield borders if only default border is specified
12234 if (vp_playfield->border_left == -1)
12235 vp_playfield->border_left = vp_playfield->border_size;
12236 if (vp_playfield->border_right == -1)
12237 vp_playfield->border_right = vp_playfield->border_size;
12238 if (vp_playfield->border_top == -1)
12239 vp_playfield->border_top = vp_playfield->border_size;
12240 if (vp_playfield->border_bottom == -1)
12241 vp_playfield->border_bottom = vp_playfield->border_size;
12243 // set dynamic playfield borders if borders are specified as undefined
12244 // (but only if window size was dynamic and playfield size was static)
12246 if (dynamic_window_width && !dynamic_playfield_width)
12248 if (vp_playfield->border_left == -1)
12250 vp_playfield->border_left = (vp_playfield->x -
12251 vp_playfield->margin_left);
12252 vp_playfield->x -= vp_playfield->border_left;
12253 vp_playfield->width += vp_playfield->border_left;
12256 if (vp_playfield->border_right == -1)
12258 vp_playfield->border_right = (vp_window->width -
12260 vp_playfield->width -
12261 vp_playfield->margin_right);
12262 vp_playfield->width += vp_playfield->border_right;
12266 if (dynamic_window_height && !dynamic_playfield_height)
12268 if (vp_playfield->border_top == -1)
12270 vp_playfield->border_top = (vp_playfield->y -
12271 vp_playfield->margin_top);
12272 vp_playfield->y -= vp_playfield->border_top;
12273 vp_playfield->height += vp_playfield->border_top;
12276 if (vp_playfield->border_bottom == -1)
12278 vp_playfield->border_bottom = (vp_window->height -
12280 vp_playfield->height -
12281 vp_playfield->margin_bottom);
12282 vp_playfield->height += vp_playfield->border_bottom;
12286 // adjust playfield size to be a multiple of a defined alignment tile size
12288 int align_size = vp_playfield->align_size;
12289 int playfield_xtiles = vp_playfield->width / align_size;
12290 int playfield_ytiles = vp_playfield->height / align_size;
12291 int playfield_width_corrected = playfield_xtiles * align_size;
12292 int playfield_height_corrected = playfield_ytiles * align_size;
12293 boolean is_playfield_mode = (i == GFX_SPECIAL_ARG_PLAYING ||
12294 i == GFX_SPECIAL_ARG_EDITOR);
12296 if (is_playfield_mode &&
12297 dynamic_playfield_width &&
12298 vp_playfield->width != playfield_width_corrected)
12300 int playfield_xdiff = vp_playfield->width - playfield_width_corrected;
12302 vp_playfield->width = playfield_width_corrected;
12304 if (vp_playfield->align == ALIGN_LEFT)
12306 vp_playfield->border_left += playfield_xdiff;
12308 else if (vp_playfield->align == ALIGN_RIGHT)
12310 vp_playfield->border_right += playfield_xdiff;
12312 else if (vp_playfield->align == ALIGN_CENTER)
12314 int border_left_diff = playfield_xdiff / 2;
12315 int border_right_diff = playfield_xdiff - border_left_diff;
12317 vp_playfield->border_left += border_left_diff;
12318 vp_playfield->border_right += border_right_diff;
12322 if (is_playfield_mode &&
12323 dynamic_playfield_height &&
12324 vp_playfield->height != playfield_height_corrected)
12326 int playfield_ydiff = vp_playfield->height - playfield_height_corrected;
12328 vp_playfield->height = playfield_height_corrected;
12330 if (vp_playfield->valign == VALIGN_TOP)
12332 vp_playfield->border_top += playfield_ydiff;
12334 else if (vp_playfield->align == VALIGN_BOTTOM)
12336 vp_playfield->border_right += playfield_ydiff;
12338 else if (vp_playfield->align == VALIGN_MIDDLE)
12340 int border_top_diff = playfield_ydiff / 2;
12341 int border_bottom_diff = playfield_ydiff - border_top_diff;
12343 vp_playfield->border_top += border_top_diff;
12344 vp_playfield->border_bottom += border_bottom_diff;
12348 // adjust door positions according to specified alignment
12350 for (j = 0; j < 2; j++)
12352 struct RectWithBorder *vp_door = (j == 0 ? vp_door_1 : vp_door_2);
12354 if (vp_door->align == ALIGN_LEFT || vp_door->x > 0)
12355 vp_door->x = ALIGNED_VP_XPOS(vp_door);
12356 else if (vp_door->align == ALIGN_CENTER)
12357 vp_door->x = vp_window->width / 2 - vp_door->width / 2;
12358 else if (vp_door->align == ALIGN_RIGHT)
12359 vp_door->x += vp_window->width - vp_door->width;
12361 if (vp_door->valign == VALIGN_TOP || vp_door->y > 0)
12362 vp_door->y = ALIGNED_VP_YPOS(vp_door);
12363 else if (vp_door->valign == VALIGN_MIDDLE)
12364 vp_door->y = vp_window->height / 2 - vp_door->height / 2;
12365 else if (vp_door->valign == VALIGN_BOTTOM)
12366 vp_door->y += vp_window->height - vp_door->height;
12371 static void InitMenuDesignSettings_SpecialPostProcessing_AfterGraphics(void)
12375 struct XYTileSize *dst, *src;
12378 editor_buttons_xy[] =
12381 &editor.button.element_left, &editor.palette.element_left,
12382 IMG_GFX_EDITOR_BUTTON_ELEMENT_LEFT
12385 &editor.button.element_middle, &editor.palette.element_middle,
12386 IMG_GFX_EDITOR_BUTTON_ELEMENT_MIDDLE
12389 &editor.button.element_right, &editor.palette.element_right,
12390 IMG_GFX_EDITOR_BUTTON_ELEMENT_RIGHT
12397 // set default position for element buttons to element graphics
12398 for (i = 0; editor_buttons_xy[i].dst != NULL; i++)
12400 if ((*editor_buttons_xy[i].dst).x == -1 &&
12401 (*editor_buttons_xy[i].dst).y == -1)
12403 struct GraphicInfo *gd = &graphic_info[editor_buttons_xy[i].graphic];
12405 gd->width = gd->height = editor_buttons_xy[i].src->tile_size;
12407 *editor_buttons_xy[i].dst = *editor_buttons_xy[i].src;
12411 // adjust editor palette rows and columns if specified to be dynamic
12413 if (editor.palette.cols == -1)
12415 int vp_width = viewport.playfield[GFX_SPECIAL_ARG_EDITOR].width;
12416 int bt_width = graphic_info[IMG_EDITOR_PALETTE_BUTTON].width;
12417 int sc_width = graphic_info[IMG_EDITOR_PALETTE_SCROLLBAR].width;
12419 editor.palette.cols = (vp_width - sc_width) / bt_width;
12421 if (editor.palette.x == -1)
12423 int palette_width = editor.palette.cols * bt_width + sc_width;
12425 editor.palette.x = (vp_width - palette_width) / 2;
12429 if (editor.palette.rows == -1)
12431 int vp_height = viewport.playfield[GFX_SPECIAL_ARG_EDITOR].height;
12432 int bt_height = graphic_info[IMG_EDITOR_PALETTE_BUTTON].height;
12433 int tx_height = getFontHeight(FONT_TEXT_2);
12435 editor.palette.rows = (vp_height - tx_height) / bt_height;
12437 if (editor.palette.y == -1)
12439 int palette_height = editor.palette.rows * bt_height + tx_height;
12441 editor.palette.y = (vp_height - palette_height) / 2;
12446 static void InitMenuDesignSettings_PreviewPlayers_Ext(SetupFileHash *hash,
12447 boolean initialize)
12449 // special case: check if network and preview player positions are redefined,
12450 // to compare this later against the main menu level preview being redefined
12451 struct TokenIntPtrInfo menu_config_players[] =
12453 { "main.network_players.x", &menu.main.network_players.redefined },
12454 { "main.network_players.y", &menu.main.network_players.redefined },
12455 { "main.preview_players.x", &menu.main.preview_players.redefined },
12456 { "main.preview_players.y", &menu.main.preview_players.redefined },
12457 { "preview.x", &preview.redefined },
12458 { "preview.y", &preview.redefined }
12464 for (i = 0; i < ARRAY_SIZE(menu_config_players); i++)
12465 *menu_config_players[i].value = FALSE;
12469 for (i = 0; i < ARRAY_SIZE(menu_config_players); i++)
12470 if (getHashEntry(hash, menu_config_players[i].token) != NULL)
12471 *menu_config_players[i].value = TRUE;
12475 static void InitMenuDesignSettings_PreviewPlayers(void)
12477 InitMenuDesignSettings_PreviewPlayers_Ext(NULL, TRUE);
12480 static void InitMenuDesignSettings_PreviewPlayers_FromHash(SetupFileHash *hash)
12482 InitMenuDesignSettings_PreviewPlayers_Ext(hash, FALSE);
12485 static void LoadMenuDesignSettingsFromFilename(char *filename)
12487 static struct TitleFadingInfo tfi;
12488 static struct TitleMessageInfo tmi;
12489 static struct TokenInfo title_tokens[] =
12491 { TYPE_INTEGER, &tfi.fade_mode, ".fade_mode" },
12492 { TYPE_INTEGER, &tfi.fade_delay, ".fade_delay" },
12493 { TYPE_INTEGER, &tfi.post_delay, ".post_delay" },
12494 { TYPE_INTEGER, &tfi.auto_delay, ".auto_delay" },
12495 { TYPE_INTEGER, &tfi.auto_delay_unit, ".auto_delay_unit" },
12499 static struct TokenInfo titlemessage_tokens[] =
12501 { TYPE_INTEGER, &tmi.x, ".x" },
12502 { TYPE_INTEGER, &tmi.y, ".y" },
12503 { TYPE_INTEGER, &tmi.width, ".width" },
12504 { TYPE_INTEGER, &tmi.height, ".height" },
12505 { TYPE_INTEGER, &tmi.chars, ".chars" },
12506 { TYPE_INTEGER, &tmi.lines, ".lines" },
12507 { TYPE_INTEGER, &tmi.align, ".align" },
12508 { TYPE_INTEGER, &tmi.valign, ".valign" },
12509 { TYPE_INTEGER, &tmi.font, ".font" },
12510 { TYPE_BOOLEAN, &tmi.autowrap, ".autowrap" },
12511 { TYPE_BOOLEAN, &tmi.centered, ".centered" },
12512 { TYPE_BOOLEAN, &tmi.parse_comments, ".parse_comments" },
12513 { TYPE_INTEGER, &tmi.sort_priority, ".sort_priority" },
12514 { TYPE_INTEGER, &tmi.fade_mode, ".fade_mode" },
12515 { TYPE_INTEGER, &tmi.fade_delay, ".fade_delay" },
12516 { TYPE_INTEGER, &tmi.post_delay, ".post_delay" },
12517 { TYPE_INTEGER, &tmi.auto_delay, ".auto_delay" },
12518 { TYPE_INTEGER, &tmi.auto_delay_unit, ".auto_delay_unit" },
12524 struct TitleFadingInfo *info;
12529 // initialize first titles from "enter screen" definitions, if defined
12530 { &title_initial_first_default, "menu.enter_screen.TITLE" },
12531 { &title_first_default, "menu.enter_screen.TITLE" },
12533 // initialize title screens from "next screen" definitions, if defined
12534 { &title_initial_default, "menu.next_screen.TITLE" },
12535 { &title_default, "menu.next_screen.TITLE" },
12541 struct TitleMessageInfo *array;
12544 titlemessage_arrays[] =
12546 // initialize first titles from "enter screen" definitions, if defined
12547 { titlescreen_initial_first, "menu.enter_screen.TITLE" },
12548 { titlescreen_first, "menu.enter_screen.TITLE" },
12549 { titlemessage_initial_first, "menu.enter_screen.TITLE" },
12550 { titlemessage_first, "menu.enter_screen.TITLE" },
12552 // initialize titles from "next screen" definitions, if defined
12553 { titlescreen_initial, "menu.next_screen.TITLE" },
12554 { titlescreen, "menu.next_screen.TITLE" },
12555 { titlemessage_initial, "menu.next_screen.TITLE" },
12556 { titlemessage, "menu.next_screen.TITLE" },
12558 // overwrite titles with title definitions, if defined
12559 { titlescreen_initial_first, "[title_initial]" },
12560 { titlescreen_first, "[title]" },
12561 { titlemessage_initial_first, "[title_initial]" },
12562 { titlemessage_first, "[title]" },
12564 { titlescreen_initial, "[title_initial]" },
12565 { titlescreen, "[title]" },
12566 { titlemessage_initial, "[title_initial]" },
12567 { titlemessage, "[title]" },
12569 // overwrite titles with title screen/message definitions, if defined
12570 { titlescreen_initial_first, "[titlescreen_initial]" },
12571 { titlescreen_first, "[titlescreen]" },
12572 { titlemessage_initial_first, "[titlemessage_initial]" },
12573 { titlemessage_first, "[titlemessage]" },
12575 { titlescreen_initial, "[titlescreen_initial]" },
12576 { titlescreen, "[titlescreen]" },
12577 { titlemessage_initial, "[titlemessage_initial]" },
12578 { titlemessage, "[titlemessage]" },
12582 SetupFileHash *setup_file_hash;
12585 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
12588 // the following initializes hierarchical values from dynamic configuration
12590 // special case: initialize with default values that may be overwritten
12591 // (e.g., init "menu.draw_xoffset.INFO" from "menu.draw_xoffset")
12592 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
12594 struct TokenIntPtrInfo menu_config[] =
12596 { "menu.draw_xoffset", &menu.draw_xoffset[i] },
12597 { "menu.draw_yoffset", &menu.draw_yoffset[i] },
12598 { "menu.list_size", &menu.list_size[i] }
12601 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
12603 char *token = menu_config[j].token;
12604 char *value = getHashEntry(setup_file_hash, token);
12607 *menu_config[j].value = get_integer_from_string(value);
12611 // special case: initialize with default values that may be overwritten
12612 // (eg, init "menu.draw_xoffset.INFO[XXX]" from "menu.draw_xoffset.INFO")
12613 for (i = 0; i < NUM_SPECIAL_GFX_INFO_ARGS; i++)
12615 struct TokenIntPtrInfo menu_config[] =
12617 { "menu.draw_xoffset.INFO", &menu.draw_xoffset_info[i] },
12618 { "menu.draw_yoffset.INFO", &menu.draw_yoffset_info[i] },
12619 { "menu.list_size.INFO", &menu.list_size_info[i] },
12620 { "menu.list_entry_size.INFO", &menu.list_entry_size_info[i] },
12621 { "menu.tile_size.INFO", &menu.tile_size_info[i] }
12624 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
12626 char *token = menu_config[j].token;
12627 char *value = getHashEntry(setup_file_hash, token);
12630 *menu_config[j].value = get_integer_from_string(value);
12634 // special case: initialize with default values that may be overwritten
12635 // (eg, init "menu.draw_xoffset.SETUP[XXX]" from "menu.draw_xoffset.SETUP")
12636 for (i = 0; i < NUM_SPECIAL_GFX_SETUP_ARGS; i++)
12638 struct TokenIntPtrInfo menu_config[] =
12640 { "menu.draw_xoffset.SETUP", &menu.draw_xoffset_setup[i] },
12641 { "menu.draw_yoffset.SETUP", &menu.draw_yoffset_setup[i] }
12644 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
12646 char *token = menu_config[j].token;
12647 char *value = getHashEntry(setup_file_hash, token);
12650 *menu_config[j].value = get_integer_from_string(value);
12654 // special case: initialize with default values that may be overwritten
12655 // (eg, init "menu.line_spacing.INFO[XXX]" from "menu.line_spacing.INFO")
12656 for (i = 0; i < NUM_SPECIAL_GFX_INFO_ARGS; i++)
12658 struct TokenIntPtrInfo menu_config[] =
12660 { "menu.left_spacing.INFO", &menu.left_spacing_info[i] },
12661 { "menu.middle_spacing.INFO", &menu.middle_spacing_info[i] },
12662 { "menu.right_spacing.INFO", &menu.right_spacing_info[i] },
12663 { "menu.top_spacing.INFO", &menu.top_spacing_info[i] },
12664 { "menu.bottom_spacing.INFO", &menu.bottom_spacing_info[i] },
12665 { "menu.paragraph_spacing.INFO", &menu.paragraph_spacing_info[i] },
12666 { "menu.headline1_spacing.INFO", &menu.headline1_spacing_info[i] },
12667 { "menu.headline2_spacing.INFO", &menu.headline2_spacing_info[i] },
12668 { "menu.line_spacing.INFO", &menu.line_spacing_info[i] },
12669 { "menu.extra_spacing.INFO", &menu.extra_spacing_info[i] },
12672 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
12674 char *token = menu_config[j].token;
12675 char *value = getHashEntry(setup_file_hash, token);
12678 *menu_config[j].value = get_integer_from_string(value);
12682 // special case: initialize with default values that may be overwritten
12683 // (eg, init "menu.enter_screen.SCORES.xyz" from "menu.enter_screen.xyz")
12684 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
12686 struct TokenIntPtrInfo menu_config[] =
12688 { "menu.enter_screen.fade_mode", &menu.enter_screen[i].fade_mode },
12689 { "menu.enter_screen.fade_delay", &menu.enter_screen[i].fade_delay },
12690 { "menu.enter_screen.post_delay", &menu.enter_screen[i].post_delay },
12691 { "menu.leave_screen.fade_mode", &menu.leave_screen[i].fade_mode },
12692 { "menu.leave_screen.fade_delay", &menu.leave_screen[i].fade_delay },
12693 { "menu.leave_screen.post_delay", &menu.leave_screen[i].post_delay },
12694 { "menu.next_screen.fade_mode", &menu.next_screen[i].fade_mode },
12695 { "menu.next_screen.fade_delay", &menu.next_screen[i].fade_delay },
12696 { "menu.next_screen.post_delay", &menu.next_screen[i].post_delay }
12699 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
12701 char *token = menu_config[j].token;
12702 char *value = getHashEntry(setup_file_hash, token);
12705 *menu_config[j].value = get_token_parameter_value(token, value);
12709 // special case: initialize with default values that may be overwritten
12710 // (eg, init "viewport.door_1.MAIN.xyz" from "viewport.door_1.xyz")
12711 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
12715 char *token_prefix;
12716 struct RectWithBorder *struct_ptr;
12720 { "viewport.window", &viewport.window[i] },
12721 { "viewport.playfield", &viewport.playfield[i] },
12722 { "viewport.door_1", &viewport.door_1[i] },
12723 { "viewport.door_2", &viewport.door_2[i] }
12726 for (j = 0; j < ARRAY_SIZE(vp_struct); j++)
12728 struct TokenIntPtrInfo vp_config[] =
12730 { ".x", &vp_struct[j].struct_ptr->x },
12731 { ".y", &vp_struct[j].struct_ptr->y },
12732 { ".width", &vp_struct[j].struct_ptr->width },
12733 { ".height", &vp_struct[j].struct_ptr->height },
12734 { ".min_width", &vp_struct[j].struct_ptr->min_width },
12735 { ".min_height", &vp_struct[j].struct_ptr->min_height },
12736 { ".max_width", &vp_struct[j].struct_ptr->max_width },
12737 { ".max_height", &vp_struct[j].struct_ptr->max_height },
12738 { ".margin_left", &vp_struct[j].struct_ptr->margin_left },
12739 { ".margin_right", &vp_struct[j].struct_ptr->margin_right },
12740 { ".margin_top", &vp_struct[j].struct_ptr->margin_top },
12741 { ".margin_bottom", &vp_struct[j].struct_ptr->margin_bottom },
12742 { ".border_left", &vp_struct[j].struct_ptr->border_left },
12743 { ".border_right", &vp_struct[j].struct_ptr->border_right },
12744 { ".border_top", &vp_struct[j].struct_ptr->border_top },
12745 { ".border_bottom", &vp_struct[j].struct_ptr->border_bottom },
12746 { ".border_size", &vp_struct[j].struct_ptr->border_size },
12747 { ".align_size", &vp_struct[j].struct_ptr->align_size },
12748 { ".align", &vp_struct[j].struct_ptr->align },
12749 { ".valign", &vp_struct[j].struct_ptr->valign }
12752 for (k = 0; k < ARRAY_SIZE(vp_config); k++)
12754 char *token = getStringCat2(vp_struct[j].token_prefix,
12755 vp_config[k].token);
12756 char *value = getHashEntry(setup_file_hash, token);
12759 *vp_config[k].value = get_token_parameter_value(token, value);
12766 // special case: initialize with default values that may be overwritten
12767 // (e.g., init "[title].fade_mode" from "menu.next_screen.TITLE.fade_mode")
12768 for (i = 0; title_info[i].info != NULL; i++)
12770 struct TitleFadingInfo *info = title_info[i].info;
12771 char *base_token = title_info[i].text;
12773 for (j = 0; title_tokens[j].type != -1; j++)
12775 char *token = getStringCat2(base_token, title_tokens[j].text);
12776 char *value = getHashEntry(setup_file_hash, token);
12780 int parameter_value = get_token_parameter_value(token, value);
12784 *(int *)title_tokens[j].value = (int)parameter_value;
12793 // special case: initialize with default values that may be overwritten
12794 // (e.g., init "titlemessage_1.fade_mode" from "[titlemessage].fade_mode")
12795 for (i = 0; titlemessage_arrays[i].array != NULL; i++)
12797 struct TitleMessageInfo *array = titlemessage_arrays[i].array;
12798 char *base_token = titlemessage_arrays[i].text;
12800 for (j = 0; titlemessage_tokens[j].type != -1; j++)
12802 char *token = getStringCat2(base_token, titlemessage_tokens[j].text);
12803 char *value = getHashEntry(setup_file_hash, token);
12807 int parameter_value = get_token_parameter_value(token, value);
12809 for (k = 0; k < MAX_NUM_TITLE_MESSAGES; k++)
12813 if (titlemessage_tokens[j].type == TYPE_INTEGER)
12814 *(int *)titlemessage_tokens[j].value = (int)parameter_value;
12816 *(boolean *)titlemessage_tokens[j].value = (boolean)parameter_value;
12826 // read (and overwrite with) values that may be specified in config file
12827 InitMenuDesignSettings_FromHash(setup_file_hash, TRUE);
12829 // special case: check if network and preview player positions are redefined
12830 InitMenuDesignSettings_PreviewPlayers_FromHash(setup_file_hash);
12832 freeSetupFileHash(setup_file_hash);
12835 void LoadMenuDesignSettings(void)
12837 char *filename_base = UNDEFINED_FILENAME, *filename_local;
12839 InitMenuDesignSettings_Static();
12840 InitMenuDesignSettings_SpecialPreProcessing();
12841 InitMenuDesignSettings_PreviewPlayers();
12843 if (!GFX_OVERRIDE_ARTWORK(ARTWORK_TYPE_GRAPHICS))
12845 // first look for special settings configured in level series config
12846 filename_base = getCustomArtworkLevelConfigFilename(ARTWORK_TYPE_GRAPHICS);
12848 if (fileExists(filename_base))
12849 LoadMenuDesignSettingsFromFilename(filename_base);
12852 filename_local = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
12854 if (filename_local != NULL && !strEqual(filename_base, filename_local))
12855 LoadMenuDesignSettingsFromFilename(filename_local);
12857 InitMenuDesignSettings_SpecialPostProcessing();
12860 void LoadMenuDesignSettings_AfterGraphics(void)
12862 InitMenuDesignSettings_SpecialPostProcessing_AfterGraphics();
12865 void LoadUserDefinedEditorElementList(int **elements, int *num_elements)
12867 char *filename = getEditorSetupFilename();
12868 SetupFileList *setup_file_list, *list;
12869 SetupFileHash *element_hash;
12870 int num_unknown_tokens = 0;
12873 if ((setup_file_list = loadSetupFileList(filename)) == NULL)
12876 element_hash = newSetupFileHash();
12878 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
12879 setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
12881 // determined size may be larger than needed (due to unknown elements)
12883 for (list = setup_file_list; list != NULL; list = list->next)
12886 // add space for up to 3 more elements for padding that may be needed
12887 *num_elements += 3;
12889 // free memory for old list of elements, if needed
12890 checked_free(*elements);
12892 // allocate memory for new list of elements
12893 *elements = checked_malloc(*num_elements * sizeof(int));
12896 for (list = setup_file_list; list != NULL; list = list->next)
12898 char *value = getHashEntry(element_hash, list->token);
12900 if (value == NULL) // try to find obsolete token mapping
12902 char *mapped_token = get_mapped_token(list->token);
12904 if (mapped_token != NULL)
12906 value = getHashEntry(element_hash, mapped_token);
12908 free(mapped_token);
12914 (*elements)[(*num_elements)++] = atoi(value);
12918 if (num_unknown_tokens == 0)
12921 Warn("unknown token(s) found in config file:");
12922 Warn("- config file: '%s'", filename);
12924 num_unknown_tokens++;
12927 Warn("- token: '%s'", list->token);
12931 if (num_unknown_tokens > 0)
12934 while (*num_elements % 4) // pad with empty elements, if needed
12935 (*elements)[(*num_elements)++] = EL_EMPTY;
12937 freeSetupFileList(setup_file_list);
12938 freeSetupFileHash(element_hash);
12941 for (i = 0; i < *num_elements; i++)
12942 Debug("editor", "element '%s' [%d]\n",
12943 element_info[(*elements)[i]].token_name, (*elements)[i]);
12947 static struct MusicFileInfo *get_music_file_info_ext(char *basename, int music,
12950 SetupFileHash *setup_file_hash = NULL;
12951 struct MusicFileInfo tmp_music_file_info, *new_music_file_info;
12952 char *filename_music, *filename_prefix, *filename_info;
12958 token_to_value_ptr[] =
12960 { "title_header", &tmp_music_file_info.title_header },
12961 { "artist_header", &tmp_music_file_info.artist_header },
12962 { "album_header", &tmp_music_file_info.album_header },
12963 { "year_header", &tmp_music_file_info.year_header },
12964 { "played_header", &tmp_music_file_info.played_header },
12966 { "title", &tmp_music_file_info.title },
12967 { "artist", &tmp_music_file_info.artist },
12968 { "album", &tmp_music_file_info.album },
12969 { "year", &tmp_music_file_info.year },
12970 { "played", &tmp_music_file_info.played },
12976 filename_music = (is_sound ? getCustomSoundFilename(basename) :
12977 getCustomMusicFilename(basename));
12979 if (filename_music == NULL)
12982 // ---------- try to replace file extension ----------
12984 filename_prefix = getStringCopy(filename_music);
12985 if (strrchr(filename_prefix, '.') != NULL)
12986 *strrchr(filename_prefix, '.') = '\0';
12987 filename_info = getStringCat2(filename_prefix, ".txt");
12989 if (fileExists(filename_info))
12990 setup_file_hash = loadSetupFileHash(filename_info);
12992 free(filename_prefix);
12993 free(filename_info);
12995 if (setup_file_hash == NULL)
12997 // ---------- try to add file extension ----------
12999 filename_prefix = getStringCopy(filename_music);
13000 filename_info = getStringCat2(filename_prefix, ".txt");
13002 if (fileExists(filename_info))
13003 setup_file_hash = loadSetupFileHash(filename_info);
13005 free(filename_prefix);
13006 free(filename_info);
13009 if (setup_file_hash == NULL)
13012 // ---------- music file info found ----------
13014 clear_mem(&tmp_music_file_info, sizeof(struct MusicFileInfo));
13016 for (i = 0; token_to_value_ptr[i].token != NULL; i++)
13018 char *value = getHashEntry(setup_file_hash, token_to_value_ptr[i].token);
13020 *token_to_value_ptr[i].value_ptr =
13021 getStringCopy(value != NULL && *value != '\0' ? value : UNKNOWN_NAME);
13024 tmp_music_file_info.basename = getStringCopy(basename);
13025 tmp_music_file_info.music = music;
13026 tmp_music_file_info.is_sound = is_sound;
13028 new_music_file_info = checked_malloc(sizeof(struct MusicFileInfo));
13029 *new_music_file_info = tmp_music_file_info;
13031 return new_music_file_info;
13034 static struct MusicFileInfo *get_music_file_info(char *basename, int music)
13036 return get_music_file_info_ext(basename, music, FALSE);
13039 static struct MusicFileInfo *get_sound_file_info(char *basename, int sound)
13041 return get_music_file_info_ext(basename, sound, TRUE);
13044 static boolean music_info_listed_ext(struct MusicFileInfo *list,
13045 char *basename, boolean is_sound)
13047 for (; list != NULL; list = list->next)
13048 if (list->is_sound == is_sound && strEqual(list->basename, basename))
13054 static boolean music_info_listed(struct MusicFileInfo *list, char *basename)
13056 return music_info_listed_ext(list, basename, FALSE);
13059 static boolean sound_info_listed(struct MusicFileInfo *list, char *basename)
13061 return music_info_listed_ext(list, basename, TRUE);
13064 void LoadMusicInfo(void)
13066 int num_music_noconf = getMusicListSize_NoConf();
13067 int num_music = getMusicListSize();
13068 int num_sounds = getSoundListSize();
13069 struct FileInfo *music, *sound;
13070 struct MusicFileInfo *next, **new;
13074 while (music_file_info != NULL)
13076 next = music_file_info->next;
13078 checked_free(music_file_info->basename);
13080 checked_free(music_file_info->title_header);
13081 checked_free(music_file_info->artist_header);
13082 checked_free(music_file_info->album_header);
13083 checked_free(music_file_info->year_header);
13084 checked_free(music_file_info->played_header);
13086 checked_free(music_file_info->title);
13087 checked_free(music_file_info->artist);
13088 checked_free(music_file_info->album);
13089 checked_free(music_file_info->year);
13090 checked_free(music_file_info->played);
13092 free(music_file_info);
13094 music_file_info = next;
13097 new = &music_file_info;
13099 // get (configured or unconfigured) music file info for all levels
13100 for (i = leveldir_current->first_level;
13101 i <= leveldir_current->last_level; i++)
13105 if (levelset.music[i] != MUS_UNDEFINED)
13107 // get music file info for configured level music
13108 music_nr = levelset.music[i];
13110 else if (num_music_noconf > 0)
13112 // get music file info for unconfigured level music
13113 int level_pos = i - leveldir_current->first_level;
13115 music_nr = MAP_NOCONF_MUSIC(level_pos % num_music_noconf);
13122 char *basename = getMusicInfoEntryFilename(music_nr);
13124 if (basename == NULL)
13127 if (!music_info_listed(music_file_info, basename))
13129 *new = get_music_file_info(basename, music_nr);
13132 new = &(*new)->next;
13136 // get music file info for all remaining configured music files
13137 for (i = 0; i < num_music; i++)
13139 music = getMusicListEntry(i);
13141 if (music->filename == NULL)
13144 if (strEqual(music->filename, UNDEFINED_FILENAME))
13147 // a configured file may be not recognized as music
13148 if (!FileIsMusic(music->filename))
13151 if (!music_info_listed(music_file_info, music->filename))
13153 *new = get_music_file_info(music->filename, i);
13156 new = &(*new)->next;
13160 // get sound file info for all configured sound files
13161 for (i = 0; i < num_sounds; i++)
13163 sound = getSoundListEntry(i);
13165 if (sound->filename == NULL)
13168 if (strEqual(sound->filename, UNDEFINED_FILENAME))
13171 // a configured file may be not recognized as sound
13172 if (!FileIsSound(sound->filename))
13175 if (!sound_info_listed(music_file_info, sound->filename))
13177 *new = get_sound_file_info(sound->filename, i);
13179 new = &(*new)->next;
13183 // add pointers to previous list nodes
13185 struct MusicFileInfo *node = music_file_info;
13187 while (node != NULL)
13190 node->next->prev = node;
13196 static void add_helpanim_entry(int element, int action, int direction,
13197 int delay, int *num_list_entries)
13199 struct HelpAnimInfo *new_list_entry;
13200 (*num_list_entries)++;
13203 checked_realloc(helpanim_info,
13204 *num_list_entries * sizeof(struct HelpAnimInfo));
13205 new_list_entry = &helpanim_info[*num_list_entries - 1];
13207 new_list_entry->element = element;
13208 new_list_entry->action = action;
13209 new_list_entry->direction = direction;
13210 new_list_entry->delay = delay;
13213 static void print_unknown_token(char *filename, char *token, int token_nr)
13218 Warn("unknown token(s) found in config file:");
13219 Warn("- config file: '%s'", filename);
13222 Warn("- token: '%s'", token);
13225 static void print_unknown_token_end(int token_nr)
13231 void LoadHelpAnimInfo(void)
13233 char *filename = getHelpAnimFilename();
13234 SetupFileList *setup_file_list = NULL, *list;
13235 SetupFileHash *element_hash, *action_hash, *direction_hash;
13236 int num_list_entries = 0;
13237 int num_unknown_tokens = 0;
13240 if (fileExists(filename))
13241 setup_file_list = loadSetupFileList(filename);
13243 if (setup_file_list == NULL)
13245 // use reliable default values from static configuration
13246 SetupFileList *insert_ptr;
13248 insert_ptr = setup_file_list =
13249 newSetupFileList(helpanim_config[0].token,
13250 helpanim_config[0].value);
13252 for (i = 1; helpanim_config[i].token; i++)
13253 insert_ptr = addListEntry(insert_ptr,
13254 helpanim_config[i].token,
13255 helpanim_config[i].value);
13258 element_hash = newSetupFileHash();
13259 action_hash = newSetupFileHash();
13260 direction_hash = newSetupFileHash();
13262 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
13263 setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
13265 for (i = 0; i < NUM_ACTIONS; i++)
13266 setHashEntry(action_hash, element_action_info[i].suffix,
13267 i_to_a(element_action_info[i].value));
13269 // do not store direction index (bit) here, but direction value!
13270 for (i = 0; i < NUM_DIRECTIONS_FULL; i++)
13271 setHashEntry(direction_hash, element_direction_info[i].suffix,
13272 i_to_a(1 << element_direction_info[i].value));
13274 for (list = setup_file_list; list != NULL; list = list->next)
13276 char *element_token, *action_token, *direction_token;
13277 char *element_value, *action_value, *direction_value;
13278 int delay = atoi(list->value);
13280 if (strEqual(list->token, "end"))
13282 add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
13287 /* first try to break element into element/action/direction parts;
13288 if this does not work, also accept combined "element[.act][.dir]"
13289 elements (like "dynamite.active"), which are unique elements */
13291 if (strchr(list->token, '.') == NULL) // token contains no '.'
13293 element_value = getHashEntry(element_hash, list->token);
13294 if (element_value != NULL) // element found
13295 add_helpanim_entry(atoi(element_value), -1, -1, delay,
13296 &num_list_entries);
13299 // no further suffixes found -- this is not an element
13300 print_unknown_token(filename, list->token, num_unknown_tokens++);
13306 // token has format "<prefix>.<something>"
13308 action_token = strchr(list->token, '.'); // suffix may be action ...
13309 direction_token = action_token; // ... or direction
13311 element_token = getStringCopy(list->token);
13312 *strchr(element_token, '.') = '\0';
13314 element_value = getHashEntry(element_hash, element_token);
13316 if (element_value == NULL) // this is no element
13318 element_value = getHashEntry(element_hash, list->token);
13319 if (element_value != NULL) // combined element found
13320 add_helpanim_entry(atoi(element_value), -1, -1, delay,
13321 &num_list_entries);
13323 print_unknown_token(filename, list->token, num_unknown_tokens++);
13325 free(element_token);
13330 action_value = getHashEntry(action_hash, action_token);
13332 if (action_value != NULL) // action found
13334 add_helpanim_entry(atoi(element_value), atoi(action_value), -1, delay,
13335 &num_list_entries);
13337 free(element_token);
13342 direction_value = getHashEntry(direction_hash, direction_token);
13344 if (direction_value != NULL) // direction found
13346 add_helpanim_entry(atoi(element_value), -1, atoi(direction_value), delay,
13347 &num_list_entries);
13349 free(element_token);
13354 if (strchr(action_token + 1, '.') == NULL)
13356 // no further suffixes found -- this is not an action nor direction
13358 element_value = getHashEntry(element_hash, list->token);
13359 if (element_value != NULL) // combined element found
13360 add_helpanim_entry(atoi(element_value), -1, -1, delay,
13361 &num_list_entries);
13363 print_unknown_token(filename, list->token, num_unknown_tokens++);
13365 free(element_token);
13370 // token has format "<prefix>.<suffix>.<something>"
13372 direction_token = strchr(action_token + 1, '.');
13374 action_token = getStringCopy(action_token);
13375 *strchr(action_token + 1, '.') = '\0';
13377 action_value = getHashEntry(action_hash, action_token);
13379 if (action_value == NULL) // this is no action
13381 element_value = getHashEntry(element_hash, list->token);
13382 if (element_value != NULL) // combined element found
13383 add_helpanim_entry(atoi(element_value), -1, -1, delay,
13384 &num_list_entries);
13386 print_unknown_token(filename, list->token, num_unknown_tokens++);
13388 free(element_token);
13389 free(action_token);
13394 direction_value = getHashEntry(direction_hash, direction_token);
13396 if (direction_value != NULL) // direction found
13398 add_helpanim_entry(atoi(element_value), atoi(action_value),
13399 atoi(direction_value), delay, &num_list_entries);
13401 free(element_token);
13402 free(action_token);
13407 // this is no direction
13409 element_value = getHashEntry(element_hash, list->token);
13410 if (element_value != NULL) // combined element found
13411 add_helpanim_entry(atoi(element_value), -1, -1, delay,
13412 &num_list_entries);
13414 print_unknown_token(filename, list->token, num_unknown_tokens++);
13416 free(element_token);
13417 free(action_token);
13420 print_unknown_token_end(num_unknown_tokens);
13422 add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
13423 add_helpanim_entry(HELPANIM_LIST_END, -1, -1, -1, &num_list_entries);
13425 freeSetupFileList(setup_file_list);
13426 freeSetupFileHash(element_hash);
13427 freeSetupFileHash(action_hash);
13428 freeSetupFileHash(direction_hash);
13431 for (i = 0; i < num_list_entries; i++)
13432 Debug("files:LoadHelpAnimInfo", "'%s': %d, %d, %d => %d",
13433 EL_NAME(helpanim_info[i].element),
13434 helpanim_info[i].element,
13435 helpanim_info[i].action,
13436 helpanim_info[i].direction,
13437 helpanim_info[i].delay);
13441 void LoadHelpTextInfo(void)
13443 char *filename = getHelpTextFilename();
13446 if (helptext_info != NULL)
13448 freeSetupFileHash(helptext_info);
13449 helptext_info = NULL;
13452 if (fileExists(filename))
13453 helptext_info = loadSetupFileHash(filename);
13455 if (helptext_info == NULL)
13457 // use reliable default values from static configuration
13458 helptext_info = newSetupFileHash();
13460 for (i = 0; helptext_config[i].token; i++)
13461 setHashEntry(helptext_info,
13462 helptext_config[i].token,
13463 helptext_config[i].value);
13467 BEGIN_HASH_ITERATION(helptext_info, itr)
13469 Debug("files:LoadHelpTextInfo", "'%s' => '%s'",
13470 HASH_ITERATION_TOKEN(itr), HASH_ITERATION_VALUE(itr));
13472 END_HASH_ITERATION(hash, itr)
13477 // ----------------------------------------------------------------------------
13479 // ----------------------------------------------------------------------------
13481 #define MAX_NUM_CONVERT_LEVELS 1000
13483 void ConvertLevels(void)
13485 static LevelDirTree *convert_leveldir = NULL;
13486 static int convert_level_nr = -1;
13487 static int num_levels_handled = 0;
13488 static int num_levels_converted = 0;
13489 static boolean levels_failed[MAX_NUM_CONVERT_LEVELS];
13492 convert_leveldir = getTreeInfoFromIdentifier(leveldir_first,
13493 global.convert_leveldir);
13495 if (convert_leveldir == NULL)
13496 Fail("no such level identifier: '%s'", global.convert_leveldir);
13498 leveldir_current = convert_leveldir;
13500 if (global.convert_level_nr != -1)
13502 convert_leveldir->first_level = global.convert_level_nr;
13503 convert_leveldir->last_level = global.convert_level_nr;
13506 convert_level_nr = convert_leveldir->first_level;
13508 PrintLine("=", 79);
13509 Print("Converting levels\n");
13510 PrintLine("-", 79);
13511 Print("Level series identifier: '%s'\n", convert_leveldir->identifier);
13512 Print("Level series name: '%s'\n", convert_leveldir->name);
13513 Print("Level series author: '%s'\n", convert_leveldir->author);
13514 Print("Number of levels: %d\n", convert_leveldir->levels);
13515 PrintLine("=", 79);
13518 for (i = 0; i < MAX_NUM_CONVERT_LEVELS; i++)
13519 levels_failed[i] = FALSE;
13521 while (convert_level_nr <= convert_leveldir->last_level)
13523 char *level_filename;
13526 level_nr = convert_level_nr++;
13528 Print("Level %03d: ", level_nr);
13530 LoadLevel(level_nr);
13531 if (level.no_level_file || level.no_valid_file)
13533 Print("(no level)\n");
13537 Print("converting level ... ");
13540 // special case: conversion of some EMC levels as requested by ACME
13541 level.game_engine_type = GAME_ENGINE_TYPE_RND;
13544 level_filename = getDefaultLevelFilename(level_nr);
13545 new_level = !fileExists(level_filename);
13549 SaveLevel(level_nr);
13551 num_levels_converted++;
13553 Print("converted.\n");
13557 if (level_nr >= 0 && level_nr < MAX_NUM_CONVERT_LEVELS)
13558 levels_failed[level_nr] = TRUE;
13560 Print("NOT CONVERTED -- LEVEL ALREADY EXISTS.\n");
13563 num_levels_handled++;
13567 PrintLine("=", 79);
13568 Print("Number of levels handled: %d\n", num_levels_handled);
13569 Print("Number of levels converted: %d (%d%%)\n", num_levels_converted,
13570 (num_levels_handled ?
13571 num_levels_converted * 100 / num_levels_handled : 0));
13572 PrintLine("-", 79);
13573 Print("Summary (for automatic parsing by scripts):\n");
13574 Print("LEVELDIR '%s', CONVERTED %d/%d (%d%%)",
13575 convert_leveldir->identifier, num_levels_converted,
13576 num_levels_handled,
13577 (num_levels_handled ?
13578 num_levels_converted * 100 / num_levels_handled : 0));
13580 if (num_levels_handled != num_levels_converted)
13582 Print(", FAILED:");
13583 for (i = 0; i < MAX_NUM_CONVERT_LEVELS; i++)
13584 if (levels_failed[i])
13589 PrintLine("=", 79);
13591 CloseAllAndExit(0);
13595 // ----------------------------------------------------------------------------
13596 // create and save images for use in level sketches (raw BMP format)
13597 // ----------------------------------------------------------------------------
13599 void CreateLevelSketchImages(void)
13605 InitElementPropertiesGfxElement();
13607 bitmap1 = CreateBitmap(TILEX, TILEY, DEFAULT_DEPTH);
13608 bitmap2 = CreateBitmap(MINI_TILEX, MINI_TILEY, DEFAULT_DEPTH);
13610 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
13612 int element = getMappedElement(i);
13613 char basename1[16];
13614 char basename2[16];
13618 sprintf(basename1, "%04d.bmp", i);
13619 sprintf(basename2, "%04ds.bmp", i);
13621 filename1 = getPath2(global.create_sketch_images_dir, basename1);
13622 filename2 = getPath2(global.create_sketch_images_dir, basename2);
13624 DrawSizedElement(0, 0, element, TILESIZE);
13625 BlitBitmap(drawto, bitmap1, SX, SY, TILEX, TILEY, 0, 0);
13627 if (SDL_SaveBMP(bitmap1->surface, filename1) != 0)
13628 Fail("cannot save level sketch image file '%s'", filename1);
13630 DrawSizedElement(0, 0, element, MINI_TILESIZE);
13631 BlitBitmap(drawto, bitmap2, SX, SY, MINI_TILEX, MINI_TILEY, 0, 0);
13633 if (SDL_SaveBMP(bitmap2->surface, filename2) != 0)
13634 Fail("cannot save level sketch image file '%s'", filename2);
13639 // create corresponding SQL statements (for normal and small images)
13642 printf("insert into phpbb_words values (NULL, '`%03d', '<IMG class=\"levelsketch\" src=\"/I/%04d.png\"/>');\n", i, i);
13643 printf("insert into phpbb_words values (NULL, '¸%03d', '<IMG class=\"levelsketch\" src=\"/I/%04ds.png\"/>');\n", i, i);
13646 printf("insert into phpbb_words values (NULL, '`%04d', '<IMG class=\"levelsketch\" src=\"/I/%04d.png\"/>');\n", i, i);
13647 printf("insert into phpbb_words values (NULL, '¸%04d', '<IMG class=\"levelsketch\" src=\"/I/%04ds.png\"/>');\n", i, i);
13649 // optional: create content for forum level sketch demonstration post
13651 fprintf(stderr, "%03d `%03d%c", i, i, (i % 10 < 9 ? ' ' : '\n'));
13654 FreeBitmap(bitmap1);
13655 FreeBitmap(bitmap2);
13658 fprintf(stderr, "\n");
13660 Info("%d normal and small images created", NUM_FILE_ELEMENTS);
13662 CloseAllAndExit(0);
13666 // ----------------------------------------------------------------------------
13667 // create and save images for element collecting animations (raw BMP format)
13668 // ----------------------------------------------------------------------------
13670 static boolean createCollectImage(int element)
13672 return (IS_COLLECTIBLE(element) && !IS_SP_ELEMENT(element));
13675 void CreateCollectElementImages(void)
13679 int anim_frames = num_steps - 1;
13680 int tile_size = TILESIZE;
13681 int anim_width = tile_size * anim_frames;
13682 int anim_height = tile_size;
13683 int num_collect_images = 0;
13684 int pos_collect_images = 0;
13686 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
13687 if (createCollectImage(i))
13688 num_collect_images++;
13690 Info("Creating %d element collecting animation images ...",
13691 num_collect_images);
13693 int dst_width = anim_width * 2;
13694 int dst_height = anim_height * num_collect_images / 2;
13695 Bitmap *dst_bitmap = CreateBitmap(dst_width, dst_height, DEFAULT_DEPTH);
13696 char *basename_bmp = "RocksCollect.bmp";
13697 char *basename_png = "RocksCollect.png";
13698 char *filename_bmp = getPath2(global.create_collect_images_dir, basename_bmp);
13699 char *filename_png = getPath2(global.create_collect_images_dir, basename_png);
13700 int len_filename_bmp = strlen(filename_bmp);
13701 int len_filename_png = strlen(filename_png);
13702 int max_command_len = MAX_FILENAME_LEN + len_filename_bmp + len_filename_png;
13703 char cmd_convert[max_command_len];
13705 snprintf(cmd_convert, max_command_len, "convert \"%s\" \"%s\"",
13709 // force using RGBA surface for destination bitmap
13710 SDL_SetColorKey(dst_bitmap->surface, SET_TRANSPARENT_PIXEL,
13711 SDL_MapRGB(dst_bitmap->surface->format, 0x00, 0x00, 0x00));
13713 dst_bitmap->surface =
13714 SDL_ConvertSurfaceFormat(dst_bitmap->surface, SDL_PIXELFORMAT_ARGB8888, 0);
13716 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
13718 if (!createCollectImage(i))
13721 int dst_x = (pos_collect_images / (num_collect_images / 2)) * anim_width;
13722 int dst_y = (pos_collect_images % (num_collect_images / 2)) * anim_height;
13723 int graphic = el2img(i);
13724 char *token_name = element_info[i].token_name;
13725 Bitmap *tmp_bitmap = CreateBitmap(tile_size, tile_size, DEFAULT_DEPTH);
13726 Bitmap *src_bitmap;
13729 Info("- creating collecting image for '%s' ...", token_name);
13731 getGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
13733 BlitBitmap(src_bitmap, tmp_bitmap, src_x, src_y,
13734 tile_size, tile_size, 0, 0);
13736 // force using RGBA surface for temporary bitmap (using transparent black)
13737 SDL_SetColorKey(tmp_bitmap->surface, SET_TRANSPARENT_PIXEL,
13738 SDL_MapRGB(tmp_bitmap->surface->format, 0x00, 0x00, 0x00));
13740 tmp_bitmap->surface =
13741 SDL_ConvertSurfaceFormat(tmp_bitmap->surface, SDL_PIXELFORMAT_ARGB8888, 0);
13743 tmp_bitmap->surface_masked = tmp_bitmap->surface;
13745 for (j = 0; j < anim_frames; j++)
13747 int frame_size_final = tile_size * (anim_frames - j) / num_steps;
13748 int frame_size = frame_size_final * num_steps;
13749 int offset = (tile_size - frame_size_final) / 2;
13750 Bitmap *frame_bitmap = ZoomBitmap(tmp_bitmap, frame_size, frame_size);
13752 while (frame_size > frame_size_final)
13756 Bitmap *half_bitmap = ZoomBitmap(frame_bitmap, frame_size, frame_size);
13758 FreeBitmap(frame_bitmap);
13760 frame_bitmap = half_bitmap;
13763 BlitBitmapMasked(frame_bitmap, dst_bitmap, 0, 0,
13764 frame_size_final, frame_size_final,
13765 dst_x + j * tile_size + offset, dst_y + offset);
13767 FreeBitmap(frame_bitmap);
13770 tmp_bitmap->surface_masked = NULL;
13772 FreeBitmap(tmp_bitmap);
13774 pos_collect_images++;
13777 if (SDL_SaveBMP(dst_bitmap->surface, filename_bmp) != 0)
13778 Fail("cannot save element collecting image file '%s'", filename_bmp);
13780 FreeBitmap(dst_bitmap);
13782 Info("Converting image file from BMP to PNG ...");
13784 if (system(cmd_convert) != 0)
13785 Fail("converting image file failed");
13787 unlink(filename_bmp);
13791 CloseAllAndExit(0);
13795 // ----------------------------------------------------------------------------
13796 // create and save images for custom and group elements (raw BMP format)
13797 // ----------------------------------------------------------------------------
13799 void CreateCustomElementImages(char *directory)
13801 char *src_basename = "RocksCE-template.ilbm";
13802 char *dst_basename = "RocksCE.bmp";
13803 char *src_filename = getPath2(directory, src_basename);
13804 char *dst_filename = getPath2(directory, dst_basename);
13805 Bitmap *src_bitmap;
13807 int yoffset_ce = 0;
13808 int yoffset_ge = (TILEY * NUM_CUSTOM_ELEMENTS / 16);
13811 InitVideoDefaults();
13813 ReCreateBitmap(&backbuffer, video.width, video.height);
13815 src_bitmap = LoadImage(src_filename);
13817 bitmap = CreateBitmap(TILEX * 16 * 2,
13818 TILEY * (NUM_CUSTOM_ELEMENTS + NUM_GROUP_ELEMENTS) / 16,
13821 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
13828 BlitBitmap(src_bitmap, bitmap, 0, 0, TILEX, TILEY,
13829 TILEX * x, TILEY * y + yoffset_ce);
13831 BlitBitmap(src_bitmap, bitmap, 0, TILEY,
13833 TILEX * x + TILEX * 16,
13834 TILEY * y + yoffset_ce);
13836 for (j = 2; j >= 0; j--)
13840 BlitBitmap(src_bitmap, bitmap,
13841 TILEX + c * 7, 0, 6, 10,
13842 TILEX * x + 6 + j * 7,
13843 TILEY * y + 11 + yoffset_ce);
13845 BlitBitmap(src_bitmap, bitmap,
13846 TILEX + c * 8, TILEY, 6, 10,
13847 TILEX * 16 + TILEX * x + 6 + j * 8,
13848 TILEY * y + 10 + yoffset_ce);
13854 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
13861 BlitBitmap(src_bitmap, bitmap, 0, 0, TILEX, TILEY,
13862 TILEX * x, TILEY * y + yoffset_ge);
13864 BlitBitmap(src_bitmap, bitmap, 0, TILEY,
13866 TILEX * x + TILEX * 16,
13867 TILEY * y + yoffset_ge);
13869 for (j = 1; j >= 0; j--)
13873 BlitBitmap(src_bitmap, bitmap, TILEX + c * 10, 11, 10, 10,
13874 TILEX * x + 6 + j * 10,
13875 TILEY * y + 11 + yoffset_ge);
13877 BlitBitmap(src_bitmap, bitmap,
13878 TILEX + c * 8, TILEY + 12, 6, 10,
13879 TILEX * 16 + TILEX * x + 10 + j * 8,
13880 TILEY * y + 10 + yoffset_ge);
13886 if (SDL_SaveBMP(bitmap->surface, dst_filename) != 0)
13887 Fail("cannot save CE graphics file '%s'", dst_filename);
13889 FreeBitmap(bitmap);
13891 CloseAllAndExit(0);