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))
1908 setElementDescriptionToDefault(ei);
1910 ei->modified_settings = FALSE;
1913 if (IS_CUSTOM_ELEMENT(element) ||
1914 IS_INTERNAL_ELEMENT(element))
1916 // internal values used in level editor
1918 ei->access_type = 0;
1919 ei->access_layer = 0;
1920 ei->access_protected = 0;
1921 ei->walk_to_action = 0;
1922 ei->smash_targets = 0;
1925 ei->can_explode_by_fire = FALSE;
1926 ei->can_explode_smashed = FALSE;
1927 ei->can_explode_impact = FALSE;
1929 ei->current_change_page = 0;
1932 if (IS_GROUP_ELEMENT(element) ||
1933 IS_INTERNAL_ELEMENT(element))
1935 struct ElementGroupInfo *group;
1937 // initialize memory for list of elements in group
1938 if (ei->group == NULL)
1939 ei->group = checked_malloc(sizeof(struct ElementGroupInfo));
1943 xx_group = *group; // copy group data into temporary buffer
1945 setConfigToDefaultsFromConfigList(chunk_config_GRPX);
1950 if (IS_EMPTY_ELEMENT(element) ||
1951 IS_INTERNAL_ELEMENT(element))
1953 xx_ei = *ei; // copy element data into temporary buffer
1955 setConfigToDefaultsFromConfigList(chunk_config_EMPX);
1961 clipboard_elements_initialized = TRUE;
1964 static void setLevelInfoToDefaults(struct LevelInfo *level,
1965 boolean level_info_only,
1966 boolean reset_file_status)
1968 setLevelInfoToDefaults_Level(level);
1970 if (!level_info_only)
1971 setLevelInfoToDefaults_Elements(level);
1973 if (reset_file_status)
1975 level->no_valid_file = FALSE;
1976 level->no_level_file = FALSE;
1979 level->changed = FALSE;
1982 static void setFileInfoToDefaults(struct LevelFileInfo *level_file_info)
1984 level_file_info->nr = 0;
1985 level_file_info->type = LEVEL_FILE_TYPE_UNKNOWN;
1986 level_file_info->packed = FALSE;
1988 setString(&level_file_info->basename, NULL);
1989 setString(&level_file_info->filename, NULL);
1992 int getMappedElement_SB(int, boolean);
1994 static void ActivateLevelTemplate(void)
1998 if (check_special_flags("load_xsb_to_ces"))
2000 // fill smaller playfields with padding "beyond border wall" elements
2001 if (level.fieldx < level_template.fieldx ||
2002 level.fieldy < level_template.fieldy)
2004 short field[level.fieldx][level.fieldy];
2005 int new_fieldx = MAX(level.fieldx, level_template.fieldx);
2006 int new_fieldy = MAX(level.fieldy, level_template.fieldy);
2007 int pos_fieldx = (new_fieldx - level.fieldx) / 2;
2008 int pos_fieldy = (new_fieldy - level.fieldy) / 2;
2010 // copy old playfield (which is smaller than the visible area)
2011 for (y = 0; y < level.fieldy; y++) for (x = 0; x < level.fieldx; x++)
2012 field[x][y] = level.field[x][y];
2014 // fill new, larger playfield with "beyond border wall" elements
2015 for (y = 0; y < new_fieldy; y++) for (x = 0; x < new_fieldx; x++)
2016 level.field[x][y] = getMappedElement_SB('_', TRUE);
2018 // copy the old playfield to the middle of the new playfield
2019 for (y = 0; y < level.fieldy; y++) for (x = 0; x < level.fieldx; x++)
2020 level.field[pos_fieldx + x][pos_fieldy + y] = field[x][y];
2022 level.fieldx = new_fieldx;
2023 level.fieldy = new_fieldy;
2027 // Currently there is no special action needed to activate the template
2028 // data, because 'element_info' property settings overwrite the original
2029 // level data, while all other variables do not change.
2031 // Exception: 'from_level_template' elements in the original level playfield
2032 // are overwritten with the corresponding elements at the same position in
2033 // playfield from the level template.
2035 for (x = 0; x < level.fieldx; x++)
2036 for (y = 0; y < level.fieldy; y++)
2037 if (level.field[x][y] == EL_FROM_LEVEL_TEMPLATE)
2038 level.field[x][y] = level_template.field[x][y];
2040 if (check_special_flags("load_xsb_to_ces"))
2042 struct LevelInfo level_backup = level;
2044 // overwrite all individual level settings from template level settings
2045 level = level_template;
2047 // restore level file info
2048 level.file_info = level_backup.file_info;
2050 // restore playfield size
2051 level.fieldx = level_backup.fieldx;
2052 level.fieldy = level_backup.fieldy;
2054 // restore playfield content
2055 for (x = 0; x < level.fieldx; x++)
2056 for (y = 0; y < level.fieldy; y++)
2057 level.field[x][y] = level_backup.field[x][y];
2059 // restore name and author from individual level
2060 strcpy(level.name, level_backup.name);
2061 strcpy(level.author, level_backup.author);
2063 // restore flag "use_custom_template"
2064 level.use_custom_template = level_backup.use_custom_template;
2068 static char *getLevelFilenameFromBasename(char *basename)
2070 static char *filename = NULL;
2072 checked_free(filename);
2074 filename = getPath2(getCurrentLevelDir(), basename);
2079 static int getFileTypeFromBasename(char *basename)
2081 // !!! ALSO SEE COMMENT IN checkForPackageFromBasename() !!!
2083 static char *filename = NULL;
2084 struct stat file_status;
2086 // ---------- try to determine file type from filename ----------
2088 // check for typical filename of a Supaplex level package file
2089 if (strlen(basename) == 10 && strPrefixLower(basename, "levels.d"))
2090 return LEVEL_FILE_TYPE_SP;
2092 // check for typical filename of a Diamond Caves II level package file
2093 if (strSuffixLower(basename, ".dc") ||
2094 strSuffixLower(basename, ".dc2"))
2095 return LEVEL_FILE_TYPE_DC;
2097 // check for typical filename of a Sokoban level package file
2098 if (strSuffixLower(basename, ".xsb") &&
2099 strchr(basename, '%') == NULL)
2100 return LEVEL_FILE_TYPE_SB;
2102 // ---------- try to determine file type from filesize ----------
2104 checked_free(filename);
2105 filename = getPath2(getCurrentLevelDir(), basename);
2107 if (stat(filename, &file_status) == 0)
2109 // check for typical filesize of a Supaplex level package file
2110 if (file_status.st_size == 170496)
2111 return LEVEL_FILE_TYPE_SP;
2114 return LEVEL_FILE_TYPE_UNKNOWN;
2117 static int getFileTypeFromMagicBytes(char *filename, int type)
2121 if ((file = openFile(filename, MODE_READ)))
2123 char chunk_name[CHUNK_ID_LEN + 1];
2125 getFileChunkBE(file, chunk_name, NULL);
2127 if (strEqual(chunk_name, "MMII") ||
2128 strEqual(chunk_name, "MIRR"))
2129 type = LEVEL_FILE_TYPE_MM;
2137 static boolean checkForPackageFromBasename(char *basename)
2139 // !!! WON'T WORK ANYMORE IF getFileTypeFromBasename() ALSO DETECTS !!!
2140 // !!! SINGLE LEVELS (CURRENTLY ONLY DETECTS LEVEL PACKAGES !!!
2142 return (getFileTypeFromBasename(basename) != LEVEL_FILE_TYPE_UNKNOWN);
2145 static char *getSingleLevelBasenameExt(int nr, char *extension)
2147 static char basename[MAX_FILENAME_LEN];
2150 sprintf(basename, "%s", LEVELTEMPLATE_FILENAME);
2152 sprintf(basename, "%03d.%s", nr, extension);
2157 static char *getSingleLevelBasename(int nr)
2159 return getSingleLevelBasenameExt(nr, LEVELFILE_EXTENSION);
2162 static char *getPackedLevelBasename(int type)
2164 static char basename[MAX_FILENAME_LEN];
2165 char *directory = getCurrentLevelDir();
2167 DirectoryEntry *dir_entry;
2169 strcpy(basename, UNDEFINED_FILENAME); // default: undefined file
2171 if ((dir = openDirectory(directory)) == NULL)
2173 Warn("cannot read current level directory '%s'", directory);
2178 while ((dir_entry = readDirectory(dir)) != NULL) // loop all entries
2180 char *entry_basename = dir_entry->basename;
2181 int entry_type = getFileTypeFromBasename(entry_basename);
2183 if (entry_type != LEVEL_FILE_TYPE_UNKNOWN) // found valid level package
2185 if (type == LEVEL_FILE_TYPE_UNKNOWN ||
2188 strcpy(basename, entry_basename);
2195 closeDirectory(dir);
2200 static char *getSingleLevelFilename(int nr)
2202 return getLevelFilenameFromBasename(getSingleLevelBasename(nr));
2205 #if ENABLE_UNUSED_CODE
2206 static char *getPackedLevelFilename(int type)
2208 return getLevelFilenameFromBasename(getPackedLevelBasename(type));
2212 char *getDefaultLevelFilename(int nr)
2214 return getSingleLevelFilename(nr);
2217 #if ENABLE_UNUSED_CODE
2218 static void setLevelFileInfo_SingleLevelFilename(struct LevelFileInfo *lfi,
2222 lfi->packed = FALSE;
2224 setString(&lfi->basename, getSingleLevelBasename(lfi->nr, lfi->type));
2225 setString(&lfi->filename, getLevelFilenameFromBasename(lfi->basename));
2229 static void setLevelFileInfo_FormatLevelFilename(struct LevelFileInfo *lfi,
2230 int type, char *format, ...)
2232 static char basename[MAX_FILENAME_LEN];
2235 va_start(ap, format);
2236 vsprintf(basename, format, ap);
2240 lfi->packed = FALSE;
2242 setString(&lfi->basename, basename);
2243 setString(&lfi->filename, getLevelFilenameFromBasename(lfi->basename));
2246 static void setLevelFileInfo_PackedLevelFilename(struct LevelFileInfo *lfi,
2252 setString(&lfi->basename, getPackedLevelBasename(lfi->type));
2253 setString(&lfi->filename, getLevelFilenameFromBasename(lfi->basename));
2256 static int getFiletypeFromID(char *filetype_id)
2258 char *filetype_id_lower;
2259 int filetype = LEVEL_FILE_TYPE_UNKNOWN;
2262 if (filetype_id == NULL)
2263 return LEVEL_FILE_TYPE_UNKNOWN;
2265 filetype_id_lower = getStringToLower(filetype_id);
2267 for (i = 0; filetype_id_list[i].id != NULL; i++)
2269 char *id_lower = getStringToLower(filetype_id_list[i].id);
2271 if (strEqual(filetype_id_lower, id_lower))
2272 filetype = filetype_id_list[i].filetype;
2276 if (filetype != LEVEL_FILE_TYPE_UNKNOWN)
2280 free(filetype_id_lower);
2285 char *getLocalLevelTemplateFilename(void)
2287 return getDefaultLevelFilename(-1);
2290 char *getGlobalLevelTemplateFilename(void)
2292 // global variable "leveldir_current" must be modified in the loop below
2293 LevelDirTree *leveldir_current_last = leveldir_current;
2294 char *filename = NULL;
2296 // check for template level in path from current to topmost tree node
2298 while (leveldir_current != NULL)
2300 filename = getDefaultLevelFilename(-1);
2302 if (fileExists(filename))
2305 leveldir_current = leveldir_current->node_parent;
2308 // restore global variable "leveldir_current" modified in above loop
2309 leveldir_current = leveldir_current_last;
2314 static void determineLevelFileInfo_Filename(struct LevelFileInfo *lfi)
2318 // special case: level number is negative => check for level template file
2321 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
2322 getSingleLevelBasename(-1));
2324 // replace local level template filename with global template filename
2325 setString(&lfi->filename, getGlobalLevelTemplateFilename());
2327 // no fallback if template file not existing
2331 // special case: check for file name/pattern specified in "levelinfo.conf"
2332 if (leveldir_current->level_filename != NULL)
2334 int filetype = getFiletypeFromID(leveldir_current->level_filetype);
2336 setLevelFileInfo_FormatLevelFilename(lfi, filetype,
2337 leveldir_current->level_filename, nr);
2339 lfi->packed = checkForPackageFromBasename(leveldir_current->level_filename);
2341 if (fileExists(lfi->filename))
2344 else if (leveldir_current->level_filetype != NULL)
2346 int filetype = getFiletypeFromID(leveldir_current->level_filetype);
2348 // check for specified native level file with standard file name
2349 setLevelFileInfo_FormatLevelFilename(lfi, filetype,
2350 "%03d.%s", nr, LEVELFILE_EXTENSION);
2351 if (fileExists(lfi->filename))
2355 // check for native Rocks'n'Diamonds level file
2356 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
2357 "%03d.%s", nr, LEVELFILE_EXTENSION);
2358 if (fileExists(lfi->filename))
2361 // check for Emerald Mine level file (V1)
2362 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "a%c%c",
2363 'a' + (nr / 10) % 26, '0' + nr % 10);
2364 if (fileExists(lfi->filename))
2366 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "A%c%c",
2367 'A' + (nr / 10) % 26, '0' + nr % 10);
2368 if (fileExists(lfi->filename))
2371 // check for Emerald Mine level file (V2 to V5)
2372 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%d", nr);
2373 if (fileExists(lfi->filename))
2376 // check for Emerald Mine level file (V6 / single mode)
2377 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02ds", nr);
2378 if (fileExists(lfi->filename))
2380 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02dS", nr);
2381 if (fileExists(lfi->filename))
2384 // check for Emerald Mine level file (V6 / teamwork mode)
2385 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02dt", nr);
2386 if (fileExists(lfi->filename))
2388 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02dT", nr);
2389 if (fileExists(lfi->filename))
2392 // check for various packed level file formats
2393 setLevelFileInfo_PackedLevelFilename(lfi, LEVEL_FILE_TYPE_UNKNOWN);
2394 if (fileExists(lfi->filename))
2397 // no known level file found -- use default values (and fail later)
2398 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
2399 "%03d.%s", nr, LEVELFILE_EXTENSION);
2402 static void determineLevelFileInfo_Filetype(struct LevelFileInfo *lfi)
2404 if (lfi->type == LEVEL_FILE_TYPE_UNKNOWN)
2405 lfi->type = getFileTypeFromBasename(lfi->basename);
2407 if (lfi->type == LEVEL_FILE_TYPE_RND)
2408 lfi->type = getFileTypeFromMagicBytes(lfi->filename, lfi->type);
2411 static void setLevelFileInfo(struct LevelFileInfo *level_file_info, int nr)
2413 // always start with reliable default values
2414 setFileInfoToDefaults(level_file_info);
2416 level_file_info->nr = nr; // set requested level number
2418 determineLevelFileInfo_Filename(level_file_info);
2419 determineLevelFileInfo_Filetype(level_file_info);
2422 static void copyLevelFileInfo(struct LevelFileInfo *lfi_from,
2423 struct LevelFileInfo *lfi_to)
2425 lfi_to->nr = lfi_from->nr;
2426 lfi_to->type = lfi_from->type;
2427 lfi_to->packed = lfi_from->packed;
2429 setString(&lfi_to->basename, lfi_from->basename);
2430 setString(&lfi_to->filename, lfi_from->filename);
2433 // ----------------------------------------------------------------------------
2434 // functions for loading R'n'D level
2435 // ----------------------------------------------------------------------------
2437 int getMappedElement(int element)
2439 // remap some (historic, now obsolete) elements
2443 case EL_PLAYER_OBSOLETE:
2444 element = EL_PLAYER_1;
2447 case EL_KEY_OBSOLETE:
2451 case EL_EM_KEY_1_FILE_OBSOLETE:
2452 element = EL_EM_KEY_1;
2455 case EL_EM_KEY_2_FILE_OBSOLETE:
2456 element = EL_EM_KEY_2;
2459 case EL_EM_KEY_3_FILE_OBSOLETE:
2460 element = EL_EM_KEY_3;
2463 case EL_EM_KEY_4_FILE_OBSOLETE:
2464 element = EL_EM_KEY_4;
2467 case EL_ENVELOPE_OBSOLETE:
2468 element = EL_ENVELOPE_1;
2476 if (element >= NUM_FILE_ELEMENTS)
2478 Warn("invalid level element %d", element);
2480 element = EL_UNKNOWN;
2488 static int getMappedElementByVersion(int element, int game_version)
2490 // remap some elements due to certain game version
2492 if (game_version <= VERSION_IDENT(2,2,0,0))
2494 // map game font elements
2495 element = (element == EL_CHAR('[') ? EL_CHAR_AUMLAUT :
2496 element == EL_CHAR('\\') ? EL_CHAR_OUMLAUT :
2497 element == EL_CHAR(']') ? EL_CHAR_UUMLAUT :
2498 element == EL_CHAR('^') ? EL_CHAR_COPYRIGHT : element);
2501 if (game_version < VERSION_IDENT(3,0,0,0))
2503 // map Supaplex gravity tube elements
2504 element = (element == EL_SP_GRAVITY_PORT_LEFT ? EL_SP_PORT_LEFT :
2505 element == EL_SP_GRAVITY_PORT_RIGHT ? EL_SP_PORT_RIGHT :
2506 element == EL_SP_GRAVITY_PORT_UP ? EL_SP_PORT_UP :
2507 element == EL_SP_GRAVITY_PORT_DOWN ? EL_SP_PORT_DOWN :
2514 static int LoadLevel_VERS(File *file, int chunk_size, struct LevelInfo *level)
2516 level->file_version = getFileVersion(file);
2517 level->game_version = getFileVersion(file);
2522 static int LoadLevel_DATE(File *file, int chunk_size, struct LevelInfo *level)
2524 level->creation_date.year = getFile16BitBE(file);
2525 level->creation_date.month = getFile8Bit(file);
2526 level->creation_date.day = getFile8Bit(file);
2528 level->creation_date.src = DATE_SRC_LEVELFILE;
2533 static int LoadLevel_HEAD(File *file, int chunk_size, struct LevelInfo *level)
2535 int initial_player_stepsize;
2536 int initial_player_gravity;
2539 level->fieldx = getFile8Bit(file);
2540 level->fieldy = getFile8Bit(file);
2542 level->time = getFile16BitBE(file);
2543 level->gems_needed = getFile16BitBE(file);
2545 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
2546 level->name[i] = getFile8Bit(file);
2547 level->name[MAX_LEVEL_NAME_LEN] = 0;
2549 for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
2550 level->score[i] = getFile8Bit(file);
2552 level->num_yamyam_contents = STD_ELEMENT_CONTENTS;
2553 for (i = 0; i < STD_ELEMENT_CONTENTS; i++)
2554 for (y = 0; y < 3; y++)
2555 for (x = 0; x < 3; x++)
2556 level->yamyam_content[i].e[x][y] = getMappedElement(getFile8Bit(file));
2558 level->amoeba_speed = getFile8Bit(file);
2559 level->time_magic_wall = getFile8Bit(file);
2560 level->time_wheel = getFile8Bit(file);
2561 level->amoeba_content = getMappedElement(getFile8Bit(file));
2563 initial_player_stepsize = (getFile8Bit(file) == 1 ? STEPSIZE_FAST :
2566 for (i = 0; i < MAX_PLAYERS; i++)
2567 level->initial_player_stepsize[i] = initial_player_stepsize;
2569 initial_player_gravity = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2571 for (i = 0; i < MAX_PLAYERS; i++)
2572 level->initial_player_gravity[i] = initial_player_gravity;
2574 level->encoding_16bit_field = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2575 level->em_slippery_gems = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2577 level->use_custom_template = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2579 level->block_last_field = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2580 level->sp_block_last_field = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2581 level->can_move_into_acid_bits = getFile32BitBE(file);
2582 level->dont_collide_with_bits = getFile8Bit(file);
2584 level->use_spring_bug = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2585 level->use_step_counter = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2587 level->instant_relocation = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2588 level->can_pass_to_walkable = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2589 level->grow_into_diggable = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2591 level->game_engine_type = getFile8Bit(file);
2593 ReadUnusedBytesFromFile(file, LEVEL_CHUNK_HEAD_UNUSED);
2598 static int LoadLevel_NAME(File *file, int chunk_size, struct LevelInfo *level)
2602 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
2603 level->name[i] = getFile8Bit(file);
2604 level->name[MAX_LEVEL_NAME_LEN] = 0;
2609 static int LoadLevel_AUTH(File *file, int chunk_size, struct LevelInfo *level)
2613 for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
2614 level->author[i] = getFile8Bit(file);
2615 level->author[MAX_LEVEL_AUTHOR_LEN] = 0;
2620 static int LoadLevel_BODY(File *file, int chunk_size, struct LevelInfo *level)
2623 int chunk_size_expected = level->fieldx * level->fieldy;
2625 /* Note: "chunk_size" was wrong before version 2.0 when elements are
2626 stored with 16-bit encoding (and should be twice as big then).
2627 Even worse, playfield data was stored 16-bit when only yamyam content
2628 contained 16-bit elements and vice versa. */
2630 if (level->encoding_16bit_field && level->file_version >= FILE_VERSION_2_0)
2631 chunk_size_expected *= 2;
2633 if (chunk_size_expected != chunk_size)
2635 ReadUnusedBytesFromFile(file, chunk_size);
2636 return chunk_size_expected;
2639 for (y = 0; y < level->fieldy; y++)
2640 for (x = 0; x < level->fieldx; x++)
2641 level->field[x][y] =
2642 getMappedElement(level->encoding_16bit_field ? getFile16BitBE(file) :
2647 static int LoadLevel_CONT(File *file, int chunk_size, struct LevelInfo *level)
2650 int header_size = 4;
2651 int content_size = MAX_ELEMENT_CONTENTS * 3 * 3;
2652 int chunk_size_expected = header_size + content_size;
2654 /* Note: "chunk_size" was wrong before version 2.0 when elements are
2655 stored with 16-bit encoding (and should be twice as big then).
2656 Even worse, playfield data was stored 16-bit when only yamyam content
2657 contained 16-bit elements and vice versa. */
2659 if (level->encoding_16bit_field && level->file_version >= FILE_VERSION_2_0)
2660 chunk_size_expected += content_size;
2662 if (chunk_size_expected != chunk_size)
2664 ReadUnusedBytesFromFile(file, chunk_size);
2665 return chunk_size_expected;
2669 level->num_yamyam_contents = getFile8Bit(file);
2673 // correct invalid number of content fields -- should never happen
2674 if (level->num_yamyam_contents < 1 ||
2675 level->num_yamyam_contents > MAX_ELEMENT_CONTENTS)
2676 level->num_yamyam_contents = STD_ELEMENT_CONTENTS;
2678 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
2679 for (y = 0; y < 3; y++)
2680 for (x = 0; x < 3; x++)
2681 level->yamyam_content[i].e[x][y] =
2682 getMappedElement(level->encoding_16bit_field ?
2683 getFile16BitBE(file) : getFile8Bit(file));
2687 static int LoadLevel_CNT2(File *file, int chunk_size, struct LevelInfo *level)
2692 int content_array[MAX_ELEMENT_CONTENTS][3][3];
2694 element = getMappedElement(getFile16BitBE(file));
2695 num_contents = getFile8Bit(file);
2697 getFile8Bit(file); // content x size (unused)
2698 getFile8Bit(file); // content y size (unused)
2700 ReadUnusedBytesFromFile(file, LEVEL_CHUNK_CNT2_UNUSED);
2702 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
2703 for (y = 0; y < 3; y++)
2704 for (x = 0; x < 3; x++)
2705 content_array[i][x][y] = getMappedElement(getFile16BitBE(file));
2707 // correct invalid number of content fields -- should never happen
2708 if (num_contents < 1 || num_contents > MAX_ELEMENT_CONTENTS)
2709 num_contents = STD_ELEMENT_CONTENTS;
2711 if (element == EL_YAMYAM)
2713 level->num_yamyam_contents = num_contents;
2715 for (i = 0; i < num_contents; i++)
2716 for (y = 0; y < 3; y++)
2717 for (x = 0; x < 3; x++)
2718 level->yamyam_content[i].e[x][y] = content_array[i][x][y];
2720 else if (element == EL_BD_AMOEBA)
2722 level->amoeba_content = content_array[0][0][0];
2726 Warn("cannot load content for element '%d'", element);
2732 static int LoadLevel_CNT3(File *file, int chunk_size, struct LevelInfo *level)
2738 int chunk_size_expected;
2740 element = getMappedElement(getFile16BitBE(file));
2741 if (!IS_ENVELOPE(element))
2742 element = EL_ENVELOPE_1;
2744 envelope_nr = element - EL_ENVELOPE_1;
2746 envelope_len = getFile16BitBE(file);
2748 level->envelope[envelope_nr].xsize = getFile8Bit(file);
2749 level->envelope[envelope_nr].ysize = getFile8Bit(file);
2751 ReadUnusedBytesFromFile(file, LEVEL_CHUNK_CNT3_UNUSED);
2753 chunk_size_expected = LEVEL_CHUNK_CNT3_SIZE(envelope_len);
2754 if (chunk_size_expected != chunk_size)
2756 ReadUnusedBytesFromFile(file, chunk_size - LEVEL_CHUNK_CNT3_HEADER);
2757 return chunk_size_expected;
2760 for (i = 0; i < envelope_len; i++)
2761 level->envelope[envelope_nr].text[i] = getFile8Bit(file);
2766 static int LoadLevel_CUS1(File *file, int chunk_size, struct LevelInfo *level)
2768 int num_changed_custom_elements = getFile16BitBE(file);
2769 int chunk_size_expected = 2 + num_changed_custom_elements * 6;
2772 if (chunk_size_expected != chunk_size)
2774 ReadUnusedBytesFromFile(file, chunk_size - 2);
2775 return chunk_size_expected;
2778 for (i = 0; i < num_changed_custom_elements; i++)
2780 int element = getMappedElement(getFile16BitBE(file));
2781 int properties = getFile32BitBE(file);
2783 if (IS_CUSTOM_ELEMENT(element))
2784 element_info[element].properties[EP_BITFIELD_BASE_NR] = properties;
2786 Warn("invalid custom element number %d", element);
2788 // older game versions that wrote level files with CUS1 chunks used
2789 // different default push delay values (not yet stored in level file)
2790 element_info[element].push_delay_fixed = 2;
2791 element_info[element].push_delay_random = 8;
2794 level->file_has_custom_elements = TRUE;
2799 static int LoadLevel_CUS2(File *file, int chunk_size, struct LevelInfo *level)
2801 int num_changed_custom_elements = getFile16BitBE(file);
2802 int chunk_size_expected = 2 + num_changed_custom_elements * 4;
2805 if (chunk_size_expected != chunk_size)
2807 ReadUnusedBytesFromFile(file, chunk_size - 2);
2808 return chunk_size_expected;
2811 for (i = 0; i < num_changed_custom_elements; i++)
2813 int element = getMappedElement(getFile16BitBE(file));
2814 int custom_target_element = getMappedElement(getFile16BitBE(file));
2816 if (IS_CUSTOM_ELEMENT(element))
2817 element_info[element].change->target_element = custom_target_element;
2819 Warn("invalid custom element number %d", element);
2822 level->file_has_custom_elements = TRUE;
2827 static int LoadLevel_CUS3(File *file, int chunk_size, struct LevelInfo *level)
2829 int num_changed_custom_elements = getFile16BitBE(file);
2830 int chunk_size_expected = LEVEL_CHUNK_CUS3_SIZE(num_changed_custom_elements);
2833 if (chunk_size_expected != chunk_size)
2835 ReadUnusedBytesFromFile(file, chunk_size - 2);
2836 return chunk_size_expected;
2839 for (i = 0; i < num_changed_custom_elements; i++)
2841 int element = getMappedElement(getFile16BitBE(file));
2842 struct ElementInfo *ei = &element_info[element];
2843 unsigned int event_bits;
2845 if (!IS_CUSTOM_ELEMENT(element))
2847 Warn("invalid custom element number %d", element);
2849 element = EL_INTERNAL_DUMMY;
2852 for (j = 0; j < MAX_ELEMENT_NAME_LEN; j++)
2853 ei->description[j] = getFile8Bit(file);
2854 ei->description[MAX_ELEMENT_NAME_LEN] = 0;
2856 ei->properties[EP_BITFIELD_BASE_NR] = getFile32BitBE(file);
2858 // some free bytes for future properties and padding
2859 ReadUnusedBytesFromFile(file, 7);
2861 ei->use_gfx_element = getFile8Bit(file);
2862 ei->gfx_element_initial = getMappedElement(getFile16BitBE(file));
2864 ei->collect_score_initial = getFile8Bit(file);
2865 ei->collect_count_initial = getFile8Bit(file);
2867 ei->push_delay_fixed = getFile16BitBE(file);
2868 ei->push_delay_random = getFile16BitBE(file);
2869 ei->move_delay_fixed = getFile16BitBE(file);
2870 ei->move_delay_random = getFile16BitBE(file);
2872 ei->move_pattern = getFile16BitBE(file);
2873 ei->move_direction_initial = getFile8Bit(file);
2874 ei->move_stepsize = getFile8Bit(file);
2876 for (y = 0; y < 3; y++)
2877 for (x = 0; x < 3; x++)
2878 ei->content.e[x][y] = getMappedElement(getFile16BitBE(file));
2880 // bits 0 - 31 of "has_event[]"
2881 event_bits = getFile32BitBE(file);
2882 for (j = 0; j < MIN(NUM_CHANGE_EVENTS, 32); j++)
2883 if (event_bits & (1u << j))
2884 ei->change->has_event[j] = TRUE;
2886 ei->change->target_element = getMappedElement(getFile16BitBE(file));
2888 ei->change->delay_fixed = getFile16BitBE(file);
2889 ei->change->delay_random = getFile16BitBE(file);
2890 ei->change->delay_frames = getFile16BitBE(file);
2892 ei->change->initial_trigger_element= getMappedElement(getFile16BitBE(file));
2894 ei->change->explode = getFile8Bit(file);
2895 ei->change->use_target_content = getFile8Bit(file);
2896 ei->change->only_if_complete = getFile8Bit(file);
2897 ei->change->use_random_replace = getFile8Bit(file);
2899 ei->change->random_percentage = getFile8Bit(file);
2900 ei->change->replace_when = getFile8Bit(file);
2902 for (y = 0; y < 3; y++)
2903 for (x = 0; x < 3; x++)
2904 ei->change->target_content.e[x][y] =
2905 getMappedElement(getFile16BitBE(file));
2907 ei->slippery_type = getFile8Bit(file);
2909 // some free bytes for future properties and padding
2910 ReadUnusedBytesFromFile(file, LEVEL_CPART_CUS3_UNUSED);
2912 // mark that this custom element has been modified
2913 ei->modified_settings = TRUE;
2916 level->file_has_custom_elements = TRUE;
2921 static int LoadLevel_CUS4(File *file, int chunk_size, struct LevelInfo *level)
2923 struct ElementInfo *ei;
2924 int chunk_size_expected;
2928 // ---------- custom element base property values (96 bytes) ----------------
2930 element = getMappedElement(getFile16BitBE(file));
2932 if (!IS_CUSTOM_ELEMENT(element))
2934 Warn("invalid custom element number %d", element);
2936 ReadUnusedBytesFromFile(file, chunk_size - 2);
2941 ei = &element_info[element];
2943 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
2944 ei->description[i] = getFile8Bit(file);
2945 ei->description[MAX_ELEMENT_NAME_LEN] = 0;
2947 ei->properties[EP_BITFIELD_BASE_NR] = getFile32BitBE(file);
2949 ReadUnusedBytesFromFile(file, 4); // reserved for more base properties
2951 ei->num_change_pages = getFile8Bit(file);
2953 chunk_size_expected = LEVEL_CHUNK_CUS4_SIZE(ei->num_change_pages);
2954 if (chunk_size_expected != chunk_size)
2956 ReadUnusedBytesFromFile(file, chunk_size - 43);
2957 return chunk_size_expected;
2960 ei->ce_value_fixed_initial = getFile16BitBE(file);
2961 ei->ce_value_random_initial = getFile16BitBE(file);
2962 ei->use_last_ce_value = getFile8Bit(file);
2964 ei->use_gfx_element = getFile8Bit(file);
2965 ei->gfx_element_initial = getMappedElement(getFile16BitBE(file));
2967 ei->collect_score_initial = getFile8Bit(file);
2968 ei->collect_count_initial = getFile8Bit(file);
2970 ei->drop_delay_fixed = getFile8Bit(file);
2971 ei->push_delay_fixed = getFile8Bit(file);
2972 ei->drop_delay_random = getFile8Bit(file);
2973 ei->push_delay_random = getFile8Bit(file);
2974 ei->move_delay_fixed = getFile16BitBE(file);
2975 ei->move_delay_random = getFile16BitBE(file);
2977 // bits 0 - 15 of "move_pattern" ...
2978 ei->move_pattern = getFile16BitBE(file);
2979 ei->move_direction_initial = getFile8Bit(file);
2980 ei->move_stepsize = getFile8Bit(file);
2982 ei->slippery_type = getFile8Bit(file);
2984 for (y = 0; y < 3; y++)
2985 for (x = 0; x < 3; x++)
2986 ei->content.e[x][y] = getMappedElement(getFile16BitBE(file));
2988 ei->move_enter_element = getMappedElement(getFile16BitBE(file));
2989 ei->move_leave_element = getMappedElement(getFile16BitBE(file));
2990 ei->move_leave_type = getFile8Bit(file);
2992 // ... bits 16 - 31 of "move_pattern" (not nice, but downward compatible)
2993 ei->move_pattern |= (getFile16BitBE(file) << 16);
2995 ei->access_direction = getFile8Bit(file);
2997 ei->explosion_delay = getFile8Bit(file);
2998 ei->ignition_delay = getFile8Bit(file);
2999 ei->explosion_type = getFile8Bit(file);
3001 // some free bytes for future custom property values and padding
3002 ReadUnusedBytesFromFile(file, 1);
3004 // ---------- change page property values (48 bytes) ------------------------
3006 setElementChangePages(ei, ei->num_change_pages);
3008 for (i = 0; i < ei->num_change_pages; i++)
3010 struct ElementChangeInfo *change = &ei->change_page[i];
3011 unsigned int event_bits;
3013 // always start with reliable default values
3014 setElementChangeInfoToDefaults(change);
3016 // bits 0 - 31 of "has_event[]" ...
3017 event_bits = getFile32BitBE(file);
3018 for (j = 0; j < MIN(NUM_CHANGE_EVENTS, 32); j++)
3019 if (event_bits & (1u << j))
3020 change->has_event[j] = TRUE;
3022 change->target_element = getMappedElement(getFile16BitBE(file));
3024 change->delay_fixed = getFile16BitBE(file);
3025 change->delay_random = getFile16BitBE(file);
3026 change->delay_frames = getFile16BitBE(file);
3028 change->initial_trigger_element = getMappedElement(getFile16BitBE(file));
3030 change->explode = getFile8Bit(file);
3031 change->use_target_content = getFile8Bit(file);
3032 change->only_if_complete = getFile8Bit(file);
3033 change->use_random_replace = getFile8Bit(file);
3035 change->random_percentage = getFile8Bit(file);
3036 change->replace_when = getFile8Bit(file);
3038 for (y = 0; y < 3; y++)
3039 for (x = 0; x < 3; x++)
3040 change->target_content.e[x][y]= getMappedElement(getFile16BitBE(file));
3042 change->can_change = getFile8Bit(file);
3044 change->trigger_side = getFile8Bit(file);
3046 change->trigger_player = getFile8Bit(file);
3047 change->trigger_page = getFile8Bit(file);
3049 change->trigger_page = (change->trigger_page == CH_PAGE_ANY_FILE ?
3050 CH_PAGE_ANY : (1 << change->trigger_page));
3052 change->has_action = getFile8Bit(file);
3053 change->action_type = getFile8Bit(file);
3054 change->action_mode = getFile8Bit(file);
3055 change->action_arg = getFile16BitBE(file);
3057 // ... bits 32 - 39 of "has_event[]" (not nice, but downward compatible)
3058 event_bits = getFile8Bit(file);
3059 for (j = 32; j < NUM_CHANGE_EVENTS; j++)
3060 if (event_bits & (1u << (j - 32)))
3061 change->has_event[j] = TRUE;
3064 // mark this custom element as modified
3065 ei->modified_settings = TRUE;
3067 level->file_has_custom_elements = TRUE;
3072 static int LoadLevel_GRP1(File *file, int chunk_size, struct LevelInfo *level)
3074 struct ElementInfo *ei;
3075 struct ElementGroupInfo *group;
3079 element = getMappedElement(getFile16BitBE(file));
3081 if (!IS_GROUP_ELEMENT(element))
3083 Warn("invalid group element number %d", element);
3085 ReadUnusedBytesFromFile(file, chunk_size - 2);
3090 ei = &element_info[element];
3092 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
3093 ei->description[i] = getFile8Bit(file);
3094 ei->description[MAX_ELEMENT_NAME_LEN] = 0;
3096 group = element_info[element].group;
3098 group->num_elements = getFile8Bit(file);
3100 ei->use_gfx_element = getFile8Bit(file);
3101 ei->gfx_element_initial = getMappedElement(getFile16BitBE(file));
3103 group->choice_mode = getFile8Bit(file);
3105 // some free bytes for future values and padding
3106 ReadUnusedBytesFromFile(file, 3);
3108 for (i = 0; i < MAX_ELEMENTS_IN_GROUP; i++)
3109 group->element[i] = getMappedElement(getFile16BitBE(file));
3111 // mark this group element as modified
3112 element_info[element].modified_settings = TRUE;
3114 level->file_has_custom_elements = TRUE;
3119 static int LoadLevel_MicroChunk(File *file, struct LevelFileConfigInfo *conf,
3120 int element, int real_element)
3122 int micro_chunk_size = 0;
3123 int conf_type = getFile8Bit(file);
3124 int byte_mask = conf_type & CONF_MASK_BYTES;
3125 boolean element_found = FALSE;
3128 micro_chunk_size += 1;
3130 if (byte_mask == CONF_MASK_MULTI_BYTES)
3132 int num_bytes = getFile16BitBE(file);
3133 byte *buffer = checked_malloc(num_bytes);
3135 ReadBytesFromFile(file, buffer, num_bytes);
3137 for (i = 0; conf[i].data_type != -1; i++)
3139 if (conf[i].element == element &&
3140 conf[i].conf_type == conf_type)
3142 int data_type = conf[i].data_type;
3143 int num_entities = num_bytes / CONF_ENTITY_NUM_BYTES(data_type);
3144 int max_num_entities = conf[i].max_num_entities;
3146 if (num_entities > max_num_entities)
3148 Warn("truncating number of entities for element %d from %d to %d",
3149 element, num_entities, max_num_entities);
3151 num_entities = max_num_entities;
3154 if (num_entities == 0 && (data_type == TYPE_ELEMENT_LIST ||
3155 data_type == TYPE_CONTENT_LIST))
3157 // for element and content lists, zero entities are not allowed
3158 Warn("found empty list of entities for element %d", element);
3160 // do not set "num_entities" here to prevent reading behind buffer
3162 *(int *)(conf[i].num_entities) = 1; // at least one is required
3166 *(int *)(conf[i].num_entities) = num_entities;
3169 element_found = TRUE;
3171 if (data_type == TYPE_STRING)
3173 char *string = (char *)(conf[i].value);
3176 for (j = 0; j < max_num_entities; j++)
3177 string[j] = (j < num_entities ? buffer[j] : '\0');
3179 else if (data_type == TYPE_ELEMENT_LIST)
3181 int *element_array = (int *)(conf[i].value);
3184 for (j = 0; j < num_entities; j++)
3186 getMappedElement(CONF_ELEMENTS_ELEMENT(buffer, j));
3188 else if (data_type == TYPE_CONTENT_LIST)
3190 struct Content *content= (struct Content *)(conf[i].value);
3193 for (c = 0; c < num_entities; c++)
3194 for (y = 0; y < 3; y++)
3195 for (x = 0; x < 3; x++)
3196 content[c].e[x][y] =
3197 getMappedElement(CONF_CONTENTS_ELEMENT(buffer, c, x, y));
3200 element_found = FALSE;
3206 checked_free(buffer);
3208 micro_chunk_size += 2 + num_bytes;
3210 else // constant size configuration data (1, 2 or 4 bytes)
3212 int value = (byte_mask == CONF_MASK_1_BYTE ? getFile8Bit (file) :
3213 byte_mask == CONF_MASK_2_BYTE ? getFile16BitBE(file) :
3214 byte_mask == CONF_MASK_4_BYTE ? getFile32BitBE(file) : 0);
3216 for (i = 0; conf[i].data_type != -1; i++)
3218 if (conf[i].element == element &&
3219 conf[i].conf_type == conf_type)
3221 int data_type = conf[i].data_type;
3223 if (data_type == TYPE_ELEMENT)
3224 value = getMappedElement(value);
3226 if (data_type == TYPE_BOOLEAN)
3227 *(boolean *)(conf[i].value) = (value ? TRUE : FALSE);
3229 *(int *) (conf[i].value) = value;
3231 element_found = TRUE;
3237 micro_chunk_size += CONF_VALUE_NUM_BYTES(byte_mask);
3242 char *error_conf_chunk_bytes =
3243 (byte_mask == CONF_MASK_1_BYTE ? "CONF_VALUE_8_BIT" :
3244 byte_mask == CONF_MASK_2_BYTE ? "CONF_VALUE_16_BIT" :
3245 byte_mask == CONF_MASK_4_BYTE ? "CONF_VALUE_32_BIT" :"CONF_VALUE_BYTES");
3246 int error_conf_chunk_token = conf_type & CONF_MASK_TOKEN;
3247 int error_element = real_element;
3249 Warn("cannot load micro chunk '%s(%d)' value for element %d ['%s']",
3250 error_conf_chunk_bytes, error_conf_chunk_token,
3251 error_element, EL_NAME(error_element));
3254 return micro_chunk_size;
3257 static int LoadLevel_INFO(File *file, int chunk_size, struct LevelInfo *level)
3259 int real_chunk_size = 0;
3261 li = *level; // copy level data into temporary buffer
3263 while (!checkEndOfFile(file))
3265 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_INFO, -1, -1);
3267 if (real_chunk_size >= chunk_size)
3271 *level = li; // copy temporary buffer back to level data
3273 return real_chunk_size;
3276 static int LoadLevel_CONF(File *file, int chunk_size, struct LevelInfo *level)
3278 int real_chunk_size = 0;
3280 li = *level; // copy level data into temporary buffer
3282 while (!checkEndOfFile(file))
3284 int element = getMappedElement(getFile16BitBE(file));
3286 real_chunk_size += 2;
3287 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_CONF,
3289 if (real_chunk_size >= chunk_size)
3293 *level = li; // copy temporary buffer back to level data
3295 return real_chunk_size;
3298 static int LoadLevel_ELEM(File *file, int chunk_size, struct LevelInfo *level)
3300 int real_chunk_size = 0;
3302 li = *level; // copy level data into temporary buffer
3304 while (!checkEndOfFile(file))
3306 int element = getMappedElement(getFile16BitBE(file));
3308 real_chunk_size += 2;
3309 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_ELEM,
3311 if (real_chunk_size >= chunk_size)
3315 *level = li; // copy temporary buffer back to level data
3317 return real_chunk_size;
3320 static int LoadLevel_NOTE(File *file, int chunk_size, struct LevelInfo *level)
3322 int element = getMappedElement(getFile16BitBE(file));
3323 int envelope_nr = element - EL_ENVELOPE_1;
3324 int real_chunk_size = 2;
3326 xx_envelope = level->envelope[envelope_nr]; // copy into temporary buffer
3328 while (!checkEndOfFile(file))
3330 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_NOTE,
3333 if (real_chunk_size >= chunk_size)
3337 level->envelope[envelope_nr] = xx_envelope; // copy from temporary buffer
3339 return real_chunk_size;
3342 static int LoadLevel_CUSX(File *file, int chunk_size, struct LevelInfo *level)
3344 int element = getMappedElement(getFile16BitBE(file));
3345 int real_chunk_size = 2;
3346 struct ElementInfo *ei = &element_info[element];
3349 xx_ei = *ei; // copy element data into temporary buffer
3351 xx_ei.num_change_pages = -1;
3353 while (!checkEndOfFile(file))
3355 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_CUSX_base,
3357 if (xx_ei.num_change_pages != -1)
3360 if (real_chunk_size >= chunk_size)
3366 if (ei->num_change_pages == -1)
3368 Warn("LoadLevel_CUSX(): missing 'num_change_pages' for '%s'",
3371 ei->num_change_pages = 1;
3373 setElementChangePages(ei, 1);
3374 setElementChangeInfoToDefaults(ei->change);
3376 return real_chunk_size;
3379 // initialize number of change pages stored for this custom element
3380 setElementChangePages(ei, ei->num_change_pages);
3381 for (i = 0; i < ei->num_change_pages; i++)
3382 setElementChangeInfoToDefaults(&ei->change_page[i]);
3384 // start with reading properties for the first change page
3385 xx_current_change_page = 0;
3387 while (!checkEndOfFile(file))
3389 // level file might contain invalid change page number
3390 if (xx_current_change_page >= ei->num_change_pages)
3393 struct ElementChangeInfo *change = &ei->change_page[xx_current_change_page];
3395 xx_change = *change; // copy change data into temporary buffer
3397 resetEventBits(); // reset bits; change page might have changed
3399 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_CUSX_change,
3402 *change = xx_change;
3404 setEventFlagsFromEventBits(change);
3406 if (real_chunk_size >= chunk_size)
3410 level->file_has_custom_elements = TRUE;
3412 return real_chunk_size;
3415 static int LoadLevel_GRPX(File *file, int chunk_size, struct LevelInfo *level)
3417 int element = getMappedElement(getFile16BitBE(file));
3418 int real_chunk_size = 2;
3419 struct ElementInfo *ei = &element_info[element];
3420 struct ElementGroupInfo *group = ei->group;
3425 xx_ei = *ei; // copy element data into temporary buffer
3426 xx_group = *group; // copy group data into temporary buffer
3428 while (!checkEndOfFile(file))
3430 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_GRPX,
3433 if (real_chunk_size >= chunk_size)
3440 level->file_has_custom_elements = TRUE;
3442 return real_chunk_size;
3445 static int LoadLevel_EMPX(File *file, int chunk_size, struct LevelInfo *level)
3447 int element = getMappedElement(getFile16BitBE(file));
3448 int real_chunk_size = 2;
3449 struct ElementInfo *ei = &element_info[element];
3451 xx_ei = *ei; // copy element data into temporary buffer
3453 while (!checkEndOfFile(file))
3455 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_EMPX,
3458 if (real_chunk_size >= chunk_size)
3464 level->file_has_custom_elements = TRUE;
3466 return real_chunk_size;
3469 static void LoadLevelFromFileInfo_RND(struct LevelInfo *level,
3470 struct LevelFileInfo *level_file_info,
3471 boolean level_info_only)
3473 char *filename = level_file_info->filename;
3474 char cookie[MAX_LINE_LEN];
3475 char chunk_name[CHUNK_ID_LEN + 1];
3479 if (!(file = openFile(filename, MODE_READ)))
3481 level->no_valid_file = TRUE;
3482 level->no_level_file = TRUE;
3484 if (level_info_only)
3487 Warn("cannot read level '%s' -- using empty level", filename);
3489 if (!setup.editor.use_template_for_new_levels)
3492 // if level file not found, try to initialize level data from template
3493 filename = getGlobalLevelTemplateFilename();
3495 if (!(file = openFile(filename, MODE_READ)))
3498 // default: for empty levels, use level template for custom elements
3499 level->use_custom_template = TRUE;
3501 level->no_valid_file = FALSE;
3504 getFileChunkBE(file, chunk_name, NULL);
3505 if (strEqual(chunk_name, "RND1"))
3507 getFile32BitBE(file); // not used
3509 getFileChunkBE(file, chunk_name, NULL);
3510 if (!strEqual(chunk_name, "CAVE"))
3512 level->no_valid_file = TRUE;
3514 Warn("unknown format of level file '%s'", filename);
3521 else // check for pre-2.0 file format with cookie string
3523 strcpy(cookie, chunk_name);
3524 if (getStringFromFile(file, &cookie[4], MAX_LINE_LEN - 4) == NULL)
3526 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
3527 cookie[strlen(cookie) - 1] = '\0';
3529 if (!checkCookieString(cookie, LEVEL_COOKIE_TMPL))
3531 level->no_valid_file = TRUE;
3533 Warn("unknown format of level file '%s'", filename);
3540 if ((level->file_version = getFileVersionFromCookieString(cookie)) == -1)
3542 level->no_valid_file = TRUE;
3544 Warn("unsupported version of level file '%s'", filename);
3551 // pre-2.0 level files have no game version, so use file version here
3552 level->game_version = level->file_version;
3555 if (level->file_version < FILE_VERSION_1_2)
3557 // level files from versions before 1.2.0 without chunk structure
3558 LoadLevel_HEAD(file, LEVEL_CHUNK_HEAD_SIZE, level);
3559 LoadLevel_BODY(file, level->fieldx * level->fieldy, level);
3567 int (*loader)(File *, int, struct LevelInfo *);
3571 { "VERS", LEVEL_CHUNK_VERS_SIZE, LoadLevel_VERS },
3572 { "DATE", LEVEL_CHUNK_DATE_SIZE, LoadLevel_DATE },
3573 { "HEAD", LEVEL_CHUNK_HEAD_SIZE, LoadLevel_HEAD },
3574 { "NAME", LEVEL_CHUNK_NAME_SIZE, LoadLevel_NAME },
3575 { "AUTH", LEVEL_CHUNK_AUTH_SIZE, LoadLevel_AUTH },
3576 { "INFO", -1, LoadLevel_INFO },
3577 { "BODY", -1, LoadLevel_BODY },
3578 { "CONT", -1, LoadLevel_CONT },
3579 { "CNT2", LEVEL_CHUNK_CNT2_SIZE, LoadLevel_CNT2 },
3580 { "CNT3", -1, LoadLevel_CNT3 },
3581 { "CUS1", -1, LoadLevel_CUS1 },
3582 { "CUS2", -1, LoadLevel_CUS2 },
3583 { "CUS3", -1, LoadLevel_CUS3 },
3584 { "CUS4", -1, LoadLevel_CUS4 },
3585 { "GRP1", -1, LoadLevel_GRP1 },
3586 { "CONF", -1, LoadLevel_CONF },
3587 { "ELEM", -1, LoadLevel_ELEM },
3588 { "NOTE", -1, LoadLevel_NOTE },
3589 { "CUSX", -1, LoadLevel_CUSX },
3590 { "GRPX", -1, LoadLevel_GRPX },
3591 { "EMPX", -1, LoadLevel_EMPX },
3596 while (getFileChunkBE(file, chunk_name, &chunk_size))
3600 while (chunk_info[i].name != NULL &&
3601 !strEqual(chunk_name, chunk_info[i].name))
3604 if (chunk_info[i].name == NULL)
3606 Warn("unknown chunk '%s' in level file '%s'",
3607 chunk_name, filename);
3609 ReadUnusedBytesFromFile(file, chunk_size);
3611 else if (chunk_info[i].size != -1 &&
3612 chunk_info[i].size != chunk_size)
3614 Warn("wrong size (%d) of chunk '%s' in level file '%s'",
3615 chunk_size, chunk_name, filename);
3617 ReadUnusedBytesFromFile(file, chunk_size);
3621 // call function to load this level chunk
3622 int chunk_size_expected =
3623 (chunk_info[i].loader)(file, chunk_size, level);
3625 if (chunk_size_expected < 0)
3627 Warn("error reading chunk '%s' in level file '%s'",
3628 chunk_name, filename);
3633 // the size of some chunks cannot be checked before reading other
3634 // chunks first (like "HEAD" and "BODY") that contain some header
3635 // information, so check them here
3636 if (chunk_size_expected != chunk_size)
3638 Warn("wrong size (%d) of chunk '%s' in level file '%s'",
3639 chunk_size, chunk_name, filename);
3651 // ----------------------------------------------------------------------------
3652 // functions for loading EM level
3653 // ----------------------------------------------------------------------------
3655 static void CopyNativeLevel_RND_to_EM(struct LevelInfo *level)
3657 static int ball_xy[8][2] =
3668 struct LevelInfo_EM *level_em = level->native_em_level;
3669 struct CAVE *cav = level_em->cav;
3672 cav->width = MIN(level->fieldx, MAX_PLAYFIELD_WIDTH);
3673 cav->height = MIN(level->fieldy, MAX_PLAYFIELD_HEIGHT);
3675 cav->time_seconds = level->time;
3676 cav->gems_needed = level->gems_needed;
3678 cav->emerald_score = level->score[SC_EMERALD];
3679 cav->diamond_score = level->score[SC_DIAMOND];
3680 cav->alien_score = level->score[SC_ROBOT];
3681 cav->tank_score = level->score[SC_SPACESHIP];
3682 cav->bug_score = level->score[SC_BUG];
3683 cav->eater_score = level->score[SC_YAMYAM];
3684 cav->nut_score = level->score[SC_NUT];
3685 cav->dynamite_score = level->score[SC_DYNAMITE];
3686 cav->key_score = level->score[SC_KEY];
3687 cav->exit_score = level->score[SC_TIME_BONUS];
3689 cav->num_eater_arrays = level->num_yamyam_contents;
3691 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
3692 for (y = 0; y < 3; y++)
3693 for (x = 0; x < 3; x++)
3694 cav->eater_array[i][y * 3 + x] =
3695 map_element_RND_to_EM_cave(level->yamyam_content[i].e[x][y]);
3697 cav->amoeba_time = level->amoeba_speed;
3698 cav->wonderwall_time = level->time_magic_wall;
3699 cav->wheel_time = level->time_wheel;
3701 cav->android_move_time = level->android_move_time;
3702 cav->android_clone_time = level->android_clone_time;
3703 cav->ball_random = level->ball_random;
3704 cav->ball_active = level->ball_active_initial;
3705 cav->ball_time = level->ball_time;
3706 cav->num_ball_arrays = level->num_ball_contents;
3708 cav->lenses_score = level->lenses_score;
3709 cav->magnify_score = level->magnify_score;
3710 cav->slurp_score = level->slurp_score;
3712 cav->lenses_time = level->lenses_time;
3713 cav->magnify_time = level->magnify_time;
3715 cav->wind_time = 9999;
3716 cav->wind_direction =
3717 map_direction_RND_to_EM(level->wind_direction_initial);
3719 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
3720 for (j = 0; j < 8; j++)
3721 cav->ball_array[i][j] =
3722 map_element_RND_to_EM_cave(level->ball_content[i].
3723 e[ball_xy[j][0]][ball_xy[j][1]]);
3725 map_android_clone_elements_RND_to_EM(level);
3727 // first fill the complete playfield with the empty space element
3728 for (y = 0; y < EM_MAX_CAVE_HEIGHT; y++)
3729 for (x = 0; x < EM_MAX_CAVE_WIDTH; x++)
3730 cav->cave[x][y] = Cblank;
3732 // then copy the real level contents from level file into the playfield
3733 for (y = 0; y < cav->height; y++) for (x = 0; x < cav->width; x++)
3735 int new_element = map_element_RND_to_EM_cave(level->field[x][y]);
3737 if (level->field[x][y] == EL_AMOEBA_DEAD)
3738 new_element = map_element_RND_to_EM_cave(EL_AMOEBA_WET);
3740 cav->cave[x][y] = new_element;
3743 for (i = 0; i < MAX_PLAYERS; i++)
3745 cav->player_x[i] = -1;
3746 cav->player_y[i] = -1;
3749 // initialize player positions and delete players from the playfield
3750 for (y = 0; y < cav->height; y++) for (x = 0; x < cav->width; x++)
3752 if (IS_PLAYER_ELEMENT(level->field[x][y]))
3754 int player_nr = GET_PLAYER_NR(level->field[x][y]);
3756 cav->player_x[player_nr] = x;
3757 cav->player_y[player_nr] = y;
3759 cav->cave[x][y] = map_element_RND_to_EM_cave(EL_EMPTY);
3764 static void CopyNativeLevel_EM_to_RND(struct LevelInfo *level)
3766 static int ball_xy[8][2] =
3777 struct LevelInfo_EM *level_em = level->native_em_level;
3778 struct CAVE *cav = level_em->cav;
3781 level->fieldx = MIN(cav->width, MAX_LEV_FIELDX);
3782 level->fieldy = MIN(cav->height, MAX_LEV_FIELDY);
3784 level->time = cav->time_seconds;
3785 level->gems_needed = cav->gems_needed;
3787 sprintf(level->name, "Level %d", level->file_info.nr);
3789 level->score[SC_EMERALD] = cav->emerald_score;
3790 level->score[SC_DIAMOND] = cav->diamond_score;
3791 level->score[SC_ROBOT] = cav->alien_score;
3792 level->score[SC_SPACESHIP] = cav->tank_score;
3793 level->score[SC_BUG] = cav->bug_score;
3794 level->score[SC_YAMYAM] = cav->eater_score;
3795 level->score[SC_NUT] = cav->nut_score;
3796 level->score[SC_DYNAMITE] = cav->dynamite_score;
3797 level->score[SC_KEY] = cav->key_score;
3798 level->score[SC_TIME_BONUS] = cav->exit_score;
3800 level->num_yamyam_contents = cav->num_eater_arrays;
3802 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
3803 for (y = 0; y < 3; y++)
3804 for (x = 0; x < 3; x++)
3805 level->yamyam_content[i].e[x][y] =
3806 map_element_EM_to_RND_cave(cav->eater_array[i][y * 3 + x]);
3808 level->amoeba_speed = cav->amoeba_time;
3809 level->time_magic_wall = cav->wonderwall_time;
3810 level->time_wheel = cav->wheel_time;
3812 level->android_move_time = cav->android_move_time;
3813 level->android_clone_time = cav->android_clone_time;
3814 level->ball_random = cav->ball_random;
3815 level->ball_active_initial = cav->ball_active;
3816 level->ball_time = cav->ball_time;
3817 level->num_ball_contents = cav->num_ball_arrays;
3819 level->lenses_score = cav->lenses_score;
3820 level->magnify_score = cav->magnify_score;
3821 level->slurp_score = cav->slurp_score;
3823 level->lenses_time = cav->lenses_time;
3824 level->magnify_time = cav->magnify_time;
3826 level->wind_direction_initial =
3827 map_direction_EM_to_RND(cav->wind_direction);
3829 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
3830 for (j = 0; j < 8; j++)
3831 level->ball_content[i].e[ball_xy[j][0]][ball_xy[j][1]] =
3832 map_element_EM_to_RND_cave(cav->ball_array[i][j]);
3834 map_android_clone_elements_EM_to_RND(level);
3836 // convert the playfield (some elements need special treatment)
3837 for (y = 0; y < level->fieldy; y++) for (x = 0; x < level->fieldx; x++)
3839 int new_element = map_element_EM_to_RND_cave(cav->cave[x][y]);
3841 if (new_element == EL_AMOEBA_WET && level->amoeba_speed == 0)
3842 new_element = EL_AMOEBA_DEAD;
3844 level->field[x][y] = new_element;
3847 for (i = 0; i < MAX_PLAYERS; i++)
3849 // in case of all players set to the same field, use the first player
3850 int nr = MAX_PLAYERS - i - 1;
3851 int jx = cav->player_x[nr];
3852 int jy = cav->player_y[nr];
3854 if (jx != -1 && jy != -1)
3855 level->field[jx][jy] = EL_PLAYER_1 + nr;
3858 // time score is counted for each 10 seconds left in Emerald Mine levels
3859 level->time_score_base = 10;
3863 // ----------------------------------------------------------------------------
3864 // functions for loading SP level
3865 // ----------------------------------------------------------------------------
3867 static void CopyNativeLevel_RND_to_SP(struct LevelInfo *level)
3869 struct LevelInfo_SP *level_sp = level->native_sp_level;
3870 LevelInfoType *header = &level_sp->header;
3873 level_sp->width = level->fieldx;
3874 level_sp->height = level->fieldy;
3876 for (x = 0; x < level->fieldx; x++)
3877 for (y = 0; y < level->fieldy; y++)
3878 level_sp->playfield[x][y] = map_element_RND_to_SP(level->field[x][y]);
3880 header->InitialGravity = (level->initial_player_gravity[0] ? 1 : 0);
3882 for (i = 0; i < SP_LEVEL_NAME_LEN; i++)
3883 header->LevelTitle[i] = level->name[i];
3884 // !!! NO STRING TERMINATION IN SUPAPLEX VB CODE YET -- FIX THIS !!!
3886 header->InfotronsNeeded = level->gems_needed;
3888 header->SpecialPortCount = 0;
3890 for (x = 0; x < level->fieldx; x++) for (y = 0; y < level->fieldy; y++)
3892 boolean gravity_port_found = FALSE;
3893 boolean gravity_port_valid = FALSE;
3894 int gravity_port_flag;
3895 int gravity_port_base_element;
3896 int element = level->field[x][y];
3898 if (element >= EL_SP_GRAVITY_ON_PORT_RIGHT &&
3899 element <= EL_SP_GRAVITY_ON_PORT_UP)
3901 gravity_port_found = TRUE;
3902 gravity_port_valid = TRUE;
3903 gravity_port_flag = 1;
3904 gravity_port_base_element = EL_SP_GRAVITY_ON_PORT_RIGHT;
3906 else if (element >= EL_SP_GRAVITY_OFF_PORT_RIGHT &&
3907 element <= EL_SP_GRAVITY_OFF_PORT_UP)
3909 gravity_port_found = TRUE;
3910 gravity_port_valid = TRUE;
3911 gravity_port_flag = 0;
3912 gravity_port_base_element = EL_SP_GRAVITY_OFF_PORT_RIGHT;
3914 else if (element >= EL_SP_GRAVITY_PORT_RIGHT &&
3915 element <= EL_SP_GRAVITY_PORT_UP)
3917 // change R'n'D style gravity inverting special port to normal port
3918 // (there are no gravity inverting ports in native Supaplex engine)
3920 gravity_port_found = TRUE;
3921 gravity_port_valid = FALSE;
3922 gravity_port_base_element = EL_SP_GRAVITY_PORT_RIGHT;
3925 if (gravity_port_found)
3927 if (gravity_port_valid &&
3928 header->SpecialPortCount < SP_MAX_SPECIAL_PORTS)
3930 SpecialPortType *port = &header->SpecialPort[header->SpecialPortCount];
3932 port->PortLocation = (y * level->fieldx + x) * 2;
3933 port->Gravity = gravity_port_flag;
3935 element += EL_SP_GRAVITY_PORT_RIGHT - gravity_port_base_element;
3937 header->SpecialPortCount++;
3941 // change special gravity port to normal port
3943 element += EL_SP_PORT_RIGHT - gravity_port_base_element;
3946 level_sp->playfield[x][y] = element - EL_SP_START;
3951 static void CopyNativeLevel_SP_to_RND(struct LevelInfo *level)
3953 struct LevelInfo_SP *level_sp = level->native_sp_level;
3954 LevelInfoType *header = &level_sp->header;
3955 boolean num_invalid_elements = 0;
3958 level->fieldx = level_sp->width;
3959 level->fieldy = level_sp->height;
3961 for (x = 0; x < level->fieldx; x++)
3963 for (y = 0; y < level->fieldy; y++)
3965 int element_old = level_sp->playfield[x][y];
3966 int element_new = getMappedElement(map_element_SP_to_RND(element_old));
3968 if (element_new == EL_UNKNOWN)
3970 num_invalid_elements++;
3972 Debug("level:native:SP", "invalid element %d at position %d, %d",
3976 level->field[x][y] = element_new;
3980 if (num_invalid_elements > 0)
3981 Warn("found %d invalid elements%s", num_invalid_elements,
3982 (!options.debug ? " (use '--debug' for more details)" : ""));
3984 for (i = 0; i < MAX_PLAYERS; i++)
3985 level->initial_player_gravity[i] =
3986 (header->InitialGravity == 1 ? TRUE : FALSE);
3988 // skip leading spaces
3989 for (i = 0; i < SP_LEVEL_NAME_LEN; i++)
3990 if (header->LevelTitle[i] != ' ')
3994 for (j = 0; i < SP_LEVEL_NAME_LEN; i++, j++)
3995 level->name[j] = header->LevelTitle[i];
3996 level->name[j] = '\0';
3998 // cut trailing spaces
4000 if (level->name[j - 1] == ' ' && level->name[j] == '\0')
4001 level->name[j - 1] = '\0';
4003 level->gems_needed = header->InfotronsNeeded;
4005 for (i = 0; i < header->SpecialPortCount; i++)
4007 SpecialPortType *port = &header->SpecialPort[i];
4008 int port_location = port->PortLocation;
4009 int gravity = port->Gravity;
4010 int port_x, port_y, port_element;
4012 port_x = (port_location / 2) % level->fieldx;
4013 port_y = (port_location / 2) / level->fieldx;
4015 if (port_x < 0 || port_x >= level->fieldx ||
4016 port_y < 0 || port_y >= level->fieldy)
4018 Warn("special port position (%d, %d) out of bounds", port_x, port_y);
4023 port_element = level->field[port_x][port_y];
4025 if (port_element < EL_SP_GRAVITY_PORT_RIGHT ||
4026 port_element > EL_SP_GRAVITY_PORT_UP)
4028 Warn("no special port at position (%d, %d)", port_x, port_y);
4033 // change previous (wrong) gravity inverting special port to either
4034 // gravity enabling special port or gravity disabling special port
4035 level->field[port_x][port_y] +=
4036 (gravity == 1 ? EL_SP_GRAVITY_ON_PORT_RIGHT :
4037 EL_SP_GRAVITY_OFF_PORT_RIGHT) - EL_SP_GRAVITY_PORT_RIGHT;
4040 // change special gravity ports without database entries to normal ports
4041 for (x = 0; x < level->fieldx; x++)
4042 for (y = 0; y < level->fieldy; y++)
4043 if (level->field[x][y] >= EL_SP_GRAVITY_PORT_RIGHT &&
4044 level->field[x][y] <= EL_SP_GRAVITY_PORT_UP)
4045 level->field[x][y] += EL_SP_PORT_RIGHT - EL_SP_GRAVITY_PORT_RIGHT;
4047 level->time = 0; // no time limit
4048 level->amoeba_speed = 0;
4049 level->time_magic_wall = 0;
4050 level->time_wheel = 0;
4051 level->amoeba_content = EL_EMPTY;
4053 // original Supaplex does not use score values -- rate by playing time
4054 for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
4055 level->score[i] = 0;
4057 level->rate_time_over_score = TRUE;
4059 // there are no yamyams in supaplex levels
4060 for (i = 0; i < level->num_yamyam_contents; i++)
4061 for (x = 0; x < 3; x++)
4062 for (y = 0; y < 3; y++)
4063 level->yamyam_content[i].e[x][y] = EL_EMPTY;
4066 static void CopyNativeTape_RND_to_SP(struct LevelInfo *level)
4068 struct LevelInfo_SP *level_sp = level->native_sp_level;
4069 struct DemoInfo_SP *demo = &level_sp->demo;
4072 // always start with reliable default values
4073 demo->is_available = FALSE;
4076 if (TAPE_IS_EMPTY(tape))
4079 demo->level_nr = tape.level_nr; // (currently not used)
4081 level_sp->header.DemoRandomSeed = tape.random_seed;
4085 for (i = 0; i < tape.length; i++)
4087 int demo_action = map_key_RND_to_SP(tape.pos[i].action[0]);
4088 int demo_repeat = tape.pos[i].delay;
4089 int demo_entries = (demo_repeat + 15) / 16;
4091 if (demo->length + demo_entries >= SP_MAX_TAPE_LEN)
4093 Warn("tape truncated: size exceeds maximum SP demo size %d",
4099 for (j = 0; j < demo_repeat / 16; j++)
4100 demo->data[demo->length++] = 0xf0 | demo_action;
4102 if (demo_repeat % 16)
4103 demo->data[demo->length++] = ((demo_repeat % 16 - 1) << 4) | demo_action;
4106 demo->is_available = TRUE;
4109 static void setTapeInfoToDefaults(void);
4111 static void CopyNativeTape_SP_to_RND(struct LevelInfo *level)
4113 struct LevelInfo_SP *level_sp = level->native_sp_level;
4114 struct DemoInfo_SP *demo = &level_sp->demo;
4115 char *filename = level->file_info.filename;
4118 // always start with reliable default values
4119 setTapeInfoToDefaults();
4121 if (!demo->is_available)
4124 tape.level_nr = demo->level_nr; // (currently not used)
4125 tape.random_seed = level_sp->header.DemoRandomSeed;
4127 TapeSetDateFromEpochSeconds(getFileTimestampEpochSeconds(filename));
4130 tape.pos[tape.counter].delay = 0;
4132 for (i = 0; i < demo->length; i++)
4134 int demo_action = demo->data[i] & 0x0f;
4135 int demo_repeat = (demo->data[i] & 0xf0) >> 4;
4136 int tape_action = map_key_SP_to_RND(demo_action);
4137 int tape_repeat = demo_repeat + 1;
4138 byte action[MAX_TAPE_ACTIONS] = { tape_action };
4139 boolean success = 0;
4142 for (j = 0; j < tape_repeat; j++)
4143 success = TapeAddAction(action);
4147 Warn("SP demo truncated: size exceeds maximum tape size %d",
4154 TapeHaltRecording();
4158 // ----------------------------------------------------------------------------
4159 // functions for loading MM level
4160 // ----------------------------------------------------------------------------
4162 static void CopyNativeLevel_RND_to_MM(struct LevelInfo *level)
4164 struct LevelInfo_MM *level_mm = level->native_mm_level;
4167 level_mm->fieldx = MIN(level->fieldx, MM_MAX_PLAYFIELD_WIDTH);
4168 level_mm->fieldy = MIN(level->fieldy, MM_MAX_PLAYFIELD_HEIGHT);
4170 level_mm->time = level->time;
4171 level_mm->kettles_needed = level->gems_needed;
4172 level_mm->auto_count_kettles = level->auto_count_gems;
4174 level_mm->mm_laser_red = level->mm_laser_red;
4175 level_mm->mm_laser_green = level->mm_laser_green;
4176 level_mm->mm_laser_blue = level->mm_laser_blue;
4178 level_mm->df_laser_red = level->df_laser_red;
4179 level_mm->df_laser_green = level->df_laser_green;
4180 level_mm->df_laser_blue = level->df_laser_blue;
4182 strcpy(level_mm->name, level->name);
4183 strcpy(level_mm->author, level->author);
4185 level_mm->score[SC_EMERALD] = level->score[SC_EMERALD];
4186 level_mm->score[SC_PACMAN] = level->score[SC_PACMAN];
4187 level_mm->score[SC_KEY] = level->score[SC_KEY];
4188 level_mm->score[SC_TIME_BONUS] = level->score[SC_TIME_BONUS];
4189 level_mm->score[SC_ELEM_BONUS] = level->score[SC_ELEM_BONUS];
4191 level_mm->amoeba_speed = level->amoeba_speed;
4192 level_mm->time_fuse = level->mm_time_fuse;
4193 level_mm->time_bomb = level->mm_time_bomb;
4194 level_mm->time_ball = level->mm_time_ball;
4195 level_mm->time_block = level->mm_time_block;
4197 level_mm->num_ball_contents = level->num_mm_ball_contents;
4198 level_mm->ball_choice_mode = level->mm_ball_choice_mode;
4199 level_mm->rotate_ball_content = level->rotate_mm_ball_content;
4200 level_mm->explode_ball = level->explode_mm_ball;
4202 for (i = 0; i < level->num_mm_ball_contents; i++)
4203 level_mm->ball_content[i] =
4204 map_element_RND_to_MM(level->mm_ball_content[i]);
4206 for (x = 0; x < level->fieldx; x++)
4207 for (y = 0; y < level->fieldy; y++)
4209 level_mm->field[x][y] = map_element_RND_to_MM(level->field[x][y]);
4212 static void CopyNativeLevel_MM_to_RND(struct LevelInfo *level)
4214 struct LevelInfo_MM *level_mm = level->native_mm_level;
4217 level->fieldx = MIN(level_mm->fieldx, MAX_LEV_FIELDX);
4218 level->fieldy = MIN(level_mm->fieldy, MAX_LEV_FIELDY);
4220 level->time = level_mm->time;
4221 level->gems_needed = level_mm->kettles_needed;
4222 level->auto_count_gems = level_mm->auto_count_kettles;
4224 level->mm_laser_red = level_mm->mm_laser_red;
4225 level->mm_laser_green = level_mm->mm_laser_green;
4226 level->mm_laser_blue = level_mm->mm_laser_blue;
4228 level->df_laser_red = level_mm->df_laser_red;
4229 level->df_laser_green = level_mm->df_laser_green;
4230 level->df_laser_blue = level_mm->df_laser_blue;
4232 strcpy(level->name, level_mm->name);
4234 // only overwrite author from 'levelinfo.conf' if author defined in level
4235 if (!strEqual(level_mm->author, ANONYMOUS_NAME))
4236 strcpy(level->author, level_mm->author);
4238 level->score[SC_EMERALD] = level_mm->score[SC_EMERALD];
4239 level->score[SC_PACMAN] = level_mm->score[SC_PACMAN];
4240 level->score[SC_KEY] = level_mm->score[SC_KEY];
4241 level->score[SC_TIME_BONUS] = level_mm->score[SC_TIME_BONUS];
4242 level->score[SC_ELEM_BONUS] = level_mm->score[SC_ELEM_BONUS];
4244 level->amoeba_speed = level_mm->amoeba_speed;
4245 level->mm_time_fuse = level_mm->time_fuse;
4246 level->mm_time_bomb = level_mm->time_bomb;
4247 level->mm_time_ball = level_mm->time_ball;
4248 level->mm_time_block = level_mm->time_block;
4250 level->num_mm_ball_contents = level_mm->num_ball_contents;
4251 level->mm_ball_choice_mode = level_mm->ball_choice_mode;
4252 level->rotate_mm_ball_content = level_mm->rotate_ball_content;
4253 level->explode_mm_ball = level_mm->explode_ball;
4255 for (i = 0; i < level->num_mm_ball_contents; i++)
4256 level->mm_ball_content[i] =
4257 map_element_MM_to_RND(level_mm->ball_content[i]);
4259 for (x = 0; x < level->fieldx; x++)
4260 for (y = 0; y < level->fieldy; y++)
4261 level->field[x][y] = map_element_MM_to_RND(level_mm->field[x][y]);
4265 // ----------------------------------------------------------------------------
4266 // functions for loading DC level
4267 // ----------------------------------------------------------------------------
4269 #define DC_LEVEL_HEADER_SIZE 344
4271 static unsigned short getDecodedWord_DC(unsigned short data_encoded,
4274 static int last_data_encoded;
4278 int diff_hi, diff_lo;
4279 int data_hi, data_lo;
4280 unsigned short data_decoded;
4284 last_data_encoded = 0;
4291 diff = data_encoded - last_data_encoded;
4292 diff_hi = diff & ~0xff;
4293 diff_lo = diff & 0xff;
4297 data_hi = diff_hi - (offset1 << 8) + (offset2 & 0xff00);
4298 data_lo = (diff_lo + (data_hi >> 16)) & 0x00ff;
4299 data_hi = data_hi & 0xff00;
4301 data_decoded = data_hi | data_lo;
4303 last_data_encoded = data_encoded;
4305 offset1 = (offset1 + 1) % 31;
4306 offset2 = offset2 & 0xff;
4308 return data_decoded;
4311 static int getMappedElement_DC(int element)
4319 // 0x0117 - 0x036e: (?)
4322 // 0x042d - 0x0684: (?)
4338 element = EL_CRYSTAL;
4341 case 0x0e77: // quicksand (boulder)
4342 element = EL_QUICKSAND_FAST_FULL;
4345 case 0x0e99: // slow quicksand (boulder)
4346 element = EL_QUICKSAND_FULL;
4350 element = EL_EM_EXIT_OPEN;
4354 element = EL_EM_EXIT_CLOSED;
4358 element = EL_EM_STEEL_EXIT_OPEN;
4362 element = EL_EM_STEEL_EXIT_CLOSED;
4365 case 0x0f4f: // dynamite (lit 1)
4366 element = EL_EM_DYNAMITE_ACTIVE;
4369 case 0x0f57: // dynamite (lit 2)
4370 element = EL_EM_DYNAMITE_ACTIVE;
4373 case 0x0f5f: // dynamite (lit 3)
4374 element = EL_EM_DYNAMITE_ACTIVE;
4377 case 0x0f67: // dynamite (lit 4)
4378 element = EL_EM_DYNAMITE_ACTIVE;
4385 element = EL_AMOEBA_WET;
4389 element = EL_AMOEBA_DROP;
4393 element = EL_DC_MAGIC_WALL;
4397 element = EL_SPACESHIP_UP;
4401 element = EL_SPACESHIP_DOWN;
4405 element = EL_SPACESHIP_LEFT;
4409 element = EL_SPACESHIP_RIGHT;
4413 element = EL_BUG_UP;
4417 element = EL_BUG_DOWN;
4421 element = EL_BUG_LEFT;
4425 element = EL_BUG_RIGHT;
4429 element = EL_MOLE_UP;
4433 element = EL_MOLE_DOWN;
4437 element = EL_MOLE_LEFT;
4441 element = EL_MOLE_RIGHT;
4449 element = EL_YAMYAM_UP;
4453 element = EL_SWITCHGATE_OPEN;
4457 element = EL_SWITCHGATE_CLOSED;
4461 element = EL_DC_SWITCHGATE_SWITCH_UP;
4465 element = EL_TIMEGATE_CLOSED;
4468 case 0x144c: // conveyor belt switch (green)
4469 element = EL_CONVEYOR_BELT_3_SWITCH_MIDDLE;
4472 case 0x144f: // conveyor belt switch (red)
4473 element = EL_CONVEYOR_BELT_1_SWITCH_MIDDLE;
4476 case 0x1452: // conveyor belt switch (blue)
4477 element = EL_CONVEYOR_BELT_4_SWITCH_MIDDLE;
4481 element = EL_CONVEYOR_BELT_3_MIDDLE;
4485 element = EL_CONVEYOR_BELT_3_LEFT;
4489 element = EL_CONVEYOR_BELT_3_RIGHT;
4493 element = EL_CONVEYOR_BELT_1_MIDDLE;
4497 element = EL_CONVEYOR_BELT_1_LEFT;
4501 element = EL_CONVEYOR_BELT_1_RIGHT;
4505 element = EL_CONVEYOR_BELT_4_MIDDLE;
4509 element = EL_CONVEYOR_BELT_4_LEFT;
4513 element = EL_CONVEYOR_BELT_4_RIGHT;
4517 element = EL_EXPANDABLE_WALL_HORIZONTAL;
4521 element = EL_EXPANDABLE_WALL_VERTICAL;
4525 element = EL_EXPANDABLE_WALL_ANY;
4528 case 0x14ce: // growing steel wall (left/right)
4529 element = EL_EXPANDABLE_STEELWALL_HORIZONTAL;
4532 case 0x14df: // growing steel wall (up/down)
4533 element = EL_EXPANDABLE_STEELWALL_VERTICAL;
4536 case 0x14e8: // growing steel wall (up/down/left/right)
4537 element = EL_EXPANDABLE_STEELWALL_ANY;
4541 element = EL_SHIELD_DEADLY;
4545 element = EL_EXTRA_TIME;
4553 element = EL_EMPTY_SPACE;
4556 case 0x1578: // quicksand (empty)
4557 element = EL_QUICKSAND_FAST_EMPTY;
4560 case 0x1579: // slow quicksand (empty)
4561 element = EL_QUICKSAND_EMPTY;
4571 element = EL_EM_DYNAMITE;
4574 case 0x15a1: // key (red)
4575 element = EL_EM_KEY_1;
4578 case 0x15a2: // key (yellow)
4579 element = EL_EM_KEY_2;
4582 case 0x15a3: // key (blue)
4583 element = EL_EM_KEY_4;
4586 case 0x15a4: // key (green)
4587 element = EL_EM_KEY_3;
4590 case 0x15a5: // key (white)
4591 element = EL_DC_KEY_WHITE;
4595 element = EL_WALL_SLIPPERY;
4602 case 0x15a8: // wall (not round)
4606 case 0x15a9: // (blue)
4607 element = EL_CHAR_A;
4610 case 0x15aa: // (blue)
4611 element = EL_CHAR_B;
4614 case 0x15ab: // (blue)
4615 element = EL_CHAR_C;
4618 case 0x15ac: // (blue)
4619 element = EL_CHAR_D;
4622 case 0x15ad: // (blue)
4623 element = EL_CHAR_E;
4626 case 0x15ae: // (blue)
4627 element = EL_CHAR_F;
4630 case 0x15af: // (blue)
4631 element = EL_CHAR_G;
4634 case 0x15b0: // (blue)
4635 element = EL_CHAR_H;
4638 case 0x15b1: // (blue)
4639 element = EL_CHAR_I;
4642 case 0x15b2: // (blue)
4643 element = EL_CHAR_J;
4646 case 0x15b3: // (blue)
4647 element = EL_CHAR_K;
4650 case 0x15b4: // (blue)
4651 element = EL_CHAR_L;
4654 case 0x15b5: // (blue)
4655 element = EL_CHAR_M;
4658 case 0x15b6: // (blue)
4659 element = EL_CHAR_N;
4662 case 0x15b7: // (blue)
4663 element = EL_CHAR_O;
4666 case 0x15b8: // (blue)
4667 element = EL_CHAR_P;
4670 case 0x15b9: // (blue)
4671 element = EL_CHAR_Q;
4674 case 0x15ba: // (blue)
4675 element = EL_CHAR_R;
4678 case 0x15bb: // (blue)
4679 element = EL_CHAR_S;
4682 case 0x15bc: // (blue)
4683 element = EL_CHAR_T;
4686 case 0x15bd: // (blue)
4687 element = EL_CHAR_U;
4690 case 0x15be: // (blue)
4691 element = EL_CHAR_V;
4694 case 0x15bf: // (blue)
4695 element = EL_CHAR_W;
4698 case 0x15c0: // (blue)
4699 element = EL_CHAR_X;
4702 case 0x15c1: // (blue)
4703 element = EL_CHAR_Y;
4706 case 0x15c2: // (blue)
4707 element = EL_CHAR_Z;
4710 case 0x15c3: // (blue)
4711 element = EL_CHAR_AUMLAUT;
4714 case 0x15c4: // (blue)
4715 element = EL_CHAR_OUMLAUT;
4718 case 0x15c5: // (blue)
4719 element = EL_CHAR_UUMLAUT;
4722 case 0x15c6: // (blue)
4723 element = EL_CHAR_0;
4726 case 0x15c7: // (blue)
4727 element = EL_CHAR_1;
4730 case 0x15c8: // (blue)
4731 element = EL_CHAR_2;
4734 case 0x15c9: // (blue)
4735 element = EL_CHAR_3;
4738 case 0x15ca: // (blue)
4739 element = EL_CHAR_4;
4742 case 0x15cb: // (blue)
4743 element = EL_CHAR_5;
4746 case 0x15cc: // (blue)
4747 element = EL_CHAR_6;
4750 case 0x15cd: // (blue)
4751 element = EL_CHAR_7;
4754 case 0x15ce: // (blue)
4755 element = EL_CHAR_8;
4758 case 0x15cf: // (blue)
4759 element = EL_CHAR_9;
4762 case 0x15d0: // (blue)
4763 element = EL_CHAR_PERIOD;
4766 case 0x15d1: // (blue)
4767 element = EL_CHAR_EXCLAM;
4770 case 0x15d2: // (blue)
4771 element = EL_CHAR_COLON;
4774 case 0x15d3: // (blue)
4775 element = EL_CHAR_LESS;
4778 case 0x15d4: // (blue)
4779 element = EL_CHAR_GREATER;
4782 case 0x15d5: // (blue)
4783 element = EL_CHAR_QUESTION;
4786 case 0x15d6: // (blue)
4787 element = EL_CHAR_COPYRIGHT;
4790 case 0x15d7: // (blue)
4791 element = EL_CHAR_UP;
4794 case 0x15d8: // (blue)
4795 element = EL_CHAR_DOWN;
4798 case 0x15d9: // (blue)
4799 element = EL_CHAR_BUTTON;
4802 case 0x15da: // (blue)
4803 element = EL_CHAR_PLUS;
4806 case 0x15db: // (blue)
4807 element = EL_CHAR_MINUS;
4810 case 0x15dc: // (blue)
4811 element = EL_CHAR_APOSTROPHE;
4814 case 0x15dd: // (blue)
4815 element = EL_CHAR_PARENLEFT;
4818 case 0x15de: // (blue)
4819 element = EL_CHAR_PARENRIGHT;
4822 case 0x15df: // (green)
4823 element = EL_CHAR_A;
4826 case 0x15e0: // (green)
4827 element = EL_CHAR_B;
4830 case 0x15e1: // (green)
4831 element = EL_CHAR_C;
4834 case 0x15e2: // (green)
4835 element = EL_CHAR_D;
4838 case 0x15e3: // (green)
4839 element = EL_CHAR_E;
4842 case 0x15e4: // (green)
4843 element = EL_CHAR_F;
4846 case 0x15e5: // (green)
4847 element = EL_CHAR_G;
4850 case 0x15e6: // (green)
4851 element = EL_CHAR_H;
4854 case 0x15e7: // (green)
4855 element = EL_CHAR_I;
4858 case 0x15e8: // (green)
4859 element = EL_CHAR_J;
4862 case 0x15e9: // (green)
4863 element = EL_CHAR_K;
4866 case 0x15ea: // (green)
4867 element = EL_CHAR_L;
4870 case 0x15eb: // (green)
4871 element = EL_CHAR_M;
4874 case 0x15ec: // (green)
4875 element = EL_CHAR_N;
4878 case 0x15ed: // (green)
4879 element = EL_CHAR_O;
4882 case 0x15ee: // (green)
4883 element = EL_CHAR_P;
4886 case 0x15ef: // (green)
4887 element = EL_CHAR_Q;
4890 case 0x15f0: // (green)
4891 element = EL_CHAR_R;
4894 case 0x15f1: // (green)
4895 element = EL_CHAR_S;
4898 case 0x15f2: // (green)
4899 element = EL_CHAR_T;
4902 case 0x15f3: // (green)
4903 element = EL_CHAR_U;
4906 case 0x15f4: // (green)
4907 element = EL_CHAR_V;
4910 case 0x15f5: // (green)
4911 element = EL_CHAR_W;
4914 case 0x15f6: // (green)
4915 element = EL_CHAR_X;
4918 case 0x15f7: // (green)
4919 element = EL_CHAR_Y;
4922 case 0x15f8: // (green)
4923 element = EL_CHAR_Z;
4926 case 0x15f9: // (green)
4927 element = EL_CHAR_AUMLAUT;
4930 case 0x15fa: // (green)
4931 element = EL_CHAR_OUMLAUT;
4934 case 0x15fb: // (green)
4935 element = EL_CHAR_UUMLAUT;
4938 case 0x15fc: // (green)
4939 element = EL_CHAR_0;
4942 case 0x15fd: // (green)
4943 element = EL_CHAR_1;
4946 case 0x15fe: // (green)
4947 element = EL_CHAR_2;
4950 case 0x15ff: // (green)
4951 element = EL_CHAR_3;
4954 case 0x1600: // (green)
4955 element = EL_CHAR_4;
4958 case 0x1601: // (green)
4959 element = EL_CHAR_5;
4962 case 0x1602: // (green)
4963 element = EL_CHAR_6;
4966 case 0x1603: // (green)
4967 element = EL_CHAR_7;
4970 case 0x1604: // (green)
4971 element = EL_CHAR_8;
4974 case 0x1605: // (green)
4975 element = EL_CHAR_9;
4978 case 0x1606: // (green)
4979 element = EL_CHAR_PERIOD;
4982 case 0x1607: // (green)
4983 element = EL_CHAR_EXCLAM;
4986 case 0x1608: // (green)
4987 element = EL_CHAR_COLON;
4990 case 0x1609: // (green)
4991 element = EL_CHAR_LESS;
4994 case 0x160a: // (green)
4995 element = EL_CHAR_GREATER;
4998 case 0x160b: // (green)
4999 element = EL_CHAR_QUESTION;
5002 case 0x160c: // (green)
5003 element = EL_CHAR_COPYRIGHT;
5006 case 0x160d: // (green)
5007 element = EL_CHAR_UP;
5010 case 0x160e: // (green)
5011 element = EL_CHAR_DOWN;
5014 case 0x160f: // (green)
5015 element = EL_CHAR_BUTTON;
5018 case 0x1610: // (green)
5019 element = EL_CHAR_PLUS;
5022 case 0x1611: // (green)
5023 element = EL_CHAR_MINUS;
5026 case 0x1612: // (green)
5027 element = EL_CHAR_APOSTROPHE;
5030 case 0x1613: // (green)
5031 element = EL_CHAR_PARENLEFT;
5034 case 0x1614: // (green)
5035 element = EL_CHAR_PARENRIGHT;
5038 case 0x1615: // (blue steel)
5039 element = EL_STEEL_CHAR_A;
5042 case 0x1616: // (blue steel)
5043 element = EL_STEEL_CHAR_B;
5046 case 0x1617: // (blue steel)
5047 element = EL_STEEL_CHAR_C;
5050 case 0x1618: // (blue steel)
5051 element = EL_STEEL_CHAR_D;
5054 case 0x1619: // (blue steel)
5055 element = EL_STEEL_CHAR_E;
5058 case 0x161a: // (blue steel)
5059 element = EL_STEEL_CHAR_F;
5062 case 0x161b: // (blue steel)
5063 element = EL_STEEL_CHAR_G;
5066 case 0x161c: // (blue steel)
5067 element = EL_STEEL_CHAR_H;
5070 case 0x161d: // (blue steel)
5071 element = EL_STEEL_CHAR_I;
5074 case 0x161e: // (blue steel)
5075 element = EL_STEEL_CHAR_J;
5078 case 0x161f: // (blue steel)
5079 element = EL_STEEL_CHAR_K;
5082 case 0x1620: // (blue steel)
5083 element = EL_STEEL_CHAR_L;
5086 case 0x1621: // (blue steel)
5087 element = EL_STEEL_CHAR_M;
5090 case 0x1622: // (blue steel)
5091 element = EL_STEEL_CHAR_N;
5094 case 0x1623: // (blue steel)
5095 element = EL_STEEL_CHAR_O;
5098 case 0x1624: // (blue steel)
5099 element = EL_STEEL_CHAR_P;
5102 case 0x1625: // (blue steel)
5103 element = EL_STEEL_CHAR_Q;
5106 case 0x1626: // (blue steel)
5107 element = EL_STEEL_CHAR_R;
5110 case 0x1627: // (blue steel)
5111 element = EL_STEEL_CHAR_S;
5114 case 0x1628: // (blue steel)
5115 element = EL_STEEL_CHAR_T;
5118 case 0x1629: // (blue steel)
5119 element = EL_STEEL_CHAR_U;
5122 case 0x162a: // (blue steel)
5123 element = EL_STEEL_CHAR_V;
5126 case 0x162b: // (blue steel)
5127 element = EL_STEEL_CHAR_W;
5130 case 0x162c: // (blue steel)
5131 element = EL_STEEL_CHAR_X;
5134 case 0x162d: // (blue steel)
5135 element = EL_STEEL_CHAR_Y;
5138 case 0x162e: // (blue steel)
5139 element = EL_STEEL_CHAR_Z;
5142 case 0x162f: // (blue steel)
5143 element = EL_STEEL_CHAR_AUMLAUT;
5146 case 0x1630: // (blue steel)
5147 element = EL_STEEL_CHAR_OUMLAUT;
5150 case 0x1631: // (blue steel)
5151 element = EL_STEEL_CHAR_UUMLAUT;
5154 case 0x1632: // (blue steel)
5155 element = EL_STEEL_CHAR_0;
5158 case 0x1633: // (blue steel)
5159 element = EL_STEEL_CHAR_1;
5162 case 0x1634: // (blue steel)
5163 element = EL_STEEL_CHAR_2;
5166 case 0x1635: // (blue steel)
5167 element = EL_STEEL_CHAR_3;
5170 case 0x1636: // (blue steel)
5171 element = EL_STEEL_CHAR_4;
5174 case 0x1637: // (blue steel)
5175 element = EL_STEEL_CHAR_5;
5178 case 0x1638: // (blue steel)
5179 element = EL_STEEL_CHAR_6;
5182 case 0x1639: // (blue steel)
5183 element = EL_STEEL_CHAR_7;
5186 case 0x163a: // (blue steel)
5187 element = EL_STEEL_CHAR_8;
5190 case 0x163b: // (blue steel)
5191 element = EL_STEEL_CHAR_9;
5194 case 0x163c: // (blue steel)
5195 element = EL_STEEL_CHAR_PERIOD;
5198 case 0x163d: // (blue steel)
5199 element = EL_STEEL_CHAR_EXCLAM;
5202 case 0x163e: // (blue steel)
5203 element = EL_STEEL_CHAR_COLON;
5206 case 0x163f: // (blue steel)
5207 element = EL_STEEL_CHAR_LESS;
5210 case 0x1640: // (blue steel)
5211 element = EL_STEEL_CHAR_GREATER;
5214 case 0x1641: // (blue steel)
5215 element = EL_STEEL_CHAR_QUESTION;
5218 case 0x1642: // (blue steel)
5219 element = EL_STEEL_CHAR_COPYRIGHT;
5222 case 0x1643: // (blue steel)
5223 element = EL_STEEL_CHAR_UP;
5226 case 0x1644: // (blue steel)
5227 element = EL_STEEL_CHAR_DOWN;
5230 case 0x1645: // (blue steel)
5231 element = EL_STEEL_CHAR_BUTTON;
5234 case 0x1646: // (blue steel)
5235 element = EL_STEEL_CHAR_PLUS;
5238 case 0x1647: // (blue steel)
5239 element = EL_STEEL_CHAR_MINUS;
5242 case 0x1648: // (blue steel)
5243 element = EL_STEEL_CHAR_APOSTROPHE;
5246 case 0x1649: // (blue steel)
5247 element = EL_STEEL_CHAR_PARENLEFT;
5250 case 0x164a: // (blue steel)
5251 element = EL_STEEL_CHAR_PARENRIGHT;
5254 case 0x164b: // (green steel)
5255 element = EL_STEEL_CHAR_A;
5258 case 0x164c: // (green steel)
5259 element = EL_STEEL_CHAR_B;
5262 case 0x164d: // (green steel)
5263 element = EL_STEEL_CHAR_C;
5266 case 0x164e: // (green steel)
5267 element = EL_STEEL_CHAR_D;
5270 case 0x164f: // (green steel)
5271 element = EL_STEEL_CHAR_E;
5274 case 0x1650: // (green steel)
5275 element = EL_STEEL_CHAR_F;
5278 case 0x1651: // (green steel)
5279 element = EL_STEEL_CHAR_G;
5282 case 0x1652: // (green steel)
5283 element = EL_STEEL_CHAR_H;
5286 case 0x1653: // (green steel)
5287 element = EL_STEEL_CHAR_I;
5290 case 0x1654: // (green steel)
5291 element = EL_STEEL_CHAR_J;
5294 case 0x1655: // (green steel)
5295 element = EL_STEEL_CHAR_K;
5298 case 0x1656: // (green steel)
5299 element = EL_STEEL_CHAR_L;
5302 case 0x1657: // (green steel)
5303 element = EL_STEEL_CHAR_M;
5306 case 0x1658: // (green steel)
5307 element = EL_STEEL_CHAR_N;
5310 case 0x1659: // (green steel)
5311 element = EL_STEEL_CHAR_O;
5314 case 0x165a: // (green steel)
5315 element = EL_STEEL_CHAR_P;
5318 case 0x165b: // (green steel)
5319 element = EL_STEEL_CHAR_Q;
5322 case 0x165c: // (green steel)
5323 element = EL_STEEL_CHAR_R;
5326 case 0x165d: // (green steel)
5327 element = EL_STEEL_CHAR_S;
5330 case 0x165e: // (green steel)
5331 element = EL_STEEL_CHAR_T;
5334 case 0x165f: // (green steel)
5335 element = EL_STEEL_CHAR_U;
5338 case 0x1660: // (green steel)
5339 element = EL_STEEL_CHAR_V;
5342 case 0x1661: // (green steel)
5343 element = EL_STEEL_CHAR_W;
5346 case 0x1662: // (green steel)
5347 element = EL_STEEL_CHAR_X;
5350 case 0x1663: // (green steel)
5351 element = EL_STEEL_CHAR_Y;
5354 case 0x1664: // (green steel)
5355 element = EL_STEEL_CHAR_Z;
5358 case 0x1665: // (green steel)
5359 element = EL_STEEL_CHAR_AUMLAUT;
5362 case 0x1666: // (green steel)
5363 element = EL_STEEL_CHAR_OUMLAUT;
5366 case 0x1667: // (green steel)
5367 element = EL_STEEL_CHAR_UUMLAUT;
5370 case 0x1668: // (green steel)
5371 element = EL_STEEL_CHAR_0;
5374 case 0x1669: // (green steel)
5375 element = EL_STEEL_CHAR_1;
5378 case 0x166a: // (green steel)
5379 element = EL_STEEL_CHAR_2;
5382 case 0x166b: // (green steel)
5383 element = EL_STEEL_CHAR_3;
5386 case 0x166c: // (green steel)
5387 element = EL_STEEL_CHAR_4;
5390 case 0x166d: // (green steel)
5391 element = EL_STEEL_CHAR_5;
5394 case 0x166e: // (green steel)
5395 element = EL_STEEL_CHAR_6;
5398 case 0x166f: // (green steel)
5399 element = EL_STEEL_CHAR_7;
5402 case 0x1670: // (green steel)
5403 element = EL_STEEL_CHAR_8;
5406 case 0x1671: // (green steel)
5407 element = EL_STEEL_CHAR_9;
5410 case 0x1672: // (green steel)
5411 element = EL_STEEL_CHAR_PERIOD;
5414 case 0x1673: // (green steel)
5415 element = EL_STEEL_CHAR_EXCLAM;
5418 case 0x1674: // (green steel)
5419 element = EL_STEEL_CHAR_COLON;
5422 case 0x1675: // (green steel)
5423 element = EL_STEEL_CHAR_LESS;
5426 case 0x1676: // (green steel)
5427 element = EL_STEEL_CHAR_GREATER;
5430 case 0x1677: // (green steel)
5431 element = EL_STEEL_CHAR_QUESTION;
5434 case 0x1678: // (green steel)
5435 element = EL_STEEL_CHAR_COPYRIGHT;
5438 case 0x1679: // (green steel)
5439 element = EL_STEEL_CHAR_UP;
5442 case 0x167a: // (green steel)
5443 element = EL_STEEL_CHAR_DOWN;
5446 case 0x167b: // (green steel)
5447 element = EL_STEEL_CHAR_BUTTON;
5450 case 0x167c: // (green steel)
5451 element = EL_STEEL_CHAR_PLUS;
5454 case 0x167d: // (green steel)
5455 element = EL_STEEL_CHAR_MINUS;
5458 case 0x167e: // (green steel)
5459 element = EL_STEEL_CHAR_APOSTROPHE;
5462 case 0x167f: // (green steel)
5463 element = EL_STEEL_CHAR_PARENLEFT;
5466 case 0x1680: // (green steel)
5467 element = EL_STEEL_CHAR_PARENRIGHT;
5470 case 0x1681: // gate (red)
5471 element = EL_EM_GATE_1;
5474 case 0x1682: // secret gate (red)
5475 element = EL_EM_GATE_1_GRAY;
5478 case 0x1683: // gate (yellow)
5479 element = EL_EM_GATE_2;
5482 case 0x1684: // secret gate (yellow)
5483 element = EL_EM_GATE_2_GRAY;
5486 case 0x1685: // gate (blue)
5487 element = EL_EM_GATE_4;
5490 case 0x1686: // secret gate (blue)
5491 element = EL_EM_GATE_4_GRAY;
5494 case 0x1687: // gate (green)
5495 element = EL_EM_GATE_3;
5498 case 0x1688: // secret gate (green)
5499 element = EL_EM_GATE_3_GRAY;
5502 case 0x1689: // gate (white)
5503 element = EL_DC_GATE_WHITE;
5506 case 0x168a: // secret gate (white)
5507 element = EL_DC_GATE_WHITE_GRAY;
5510 case 0x168b: // secret gate (no key)
5511 element = EL_DC_GATE_FAKE_GRAY;
5515 element = EL_ROBOT_WHEEL;
5519 element = EL_DC_TIMEGATE_SWITCH;
5523 element = EL_ACID_POOL_BOTTOM;
5527 element = EL_ACID_POOL_TOPLEFT;
5531 element = EL_ACID_POOL_TOPRIGHT;
5535 element = EL_ACID_POOL_BOTTOMLEFT;
5539 element = EL_ACID_POOL_BOTTOMRIGHT;
5543 element = EL_STEELWALL;
5547 element = EL_STEELWALL_SLIPPERY;
5550 case 0x1695: // steel wall (not round)
5551 element = EL_STEELWALL;
5554 case 0x1696: // steel wall (left)
5555 element = EL_DC_STEELWALL_1_LEFT;
5558 case 0x1697: // steel wall (bottom)
5559 element = EL_DC_STEELWALL_1_BOTTOM;
5562 case 0x1698: // steel wall (right)
5563 element = EL_DC_STEELWALL_1_RIGHT;
5566 case 0x1699: // steel wall (top)
5567 element = EL_DC_STEELWALL_1_TOP;
5570 case 0x169a: // steel wall (left/bottom)
5571 element = EL_DC_STEELWALL_1_BOTTOMLEFT;
5574 case 0x169b: // steel wall (right/bottom)
5575 element = EL_DC_STEELWALL_1_BOTTOMRIGHT;
5578 case 0x169c: // steel wall (right/top)
5579 element = EL_DC_STEELWALL_1_TOPRIGHT;
5582 case 0x169d: // steel wall (left/top)
5583 element = EL_DC_STEELWALL_1_TOPLEFT;
5586 case 0x169e: // steel wall (right/bottom small)
5587 element = EL_DC_STEELWALL_1_BOTTOMRIGHT_2;
5590 case 0x169f: // steel wall (left/bottom small)
5591 element = EL_DC_STEELWALL_1_BOTTOMLEFT_2;
5594 case 0x16a0: // steel wall (right/top small)
5595 element = EL_DC_STEELWALL_1_TOPRIGHT_2;
5598 case 0x16a1: // steel wall (left/top small)
5599 element = EL_DC_STEELWALL_1_TOPLEFT_2;
5602 case 0x16a2: // steel wall (left/right)
5603 element = EL_DC_STEELWALL_1_VERTICAL;
5606 case 0x16a3: // steel wall (top/bottom)
5607 element = EL_DC_STEELWALL_1_HORIZONTAL;
5610 case 0x16a4: // steel wall 2 (left end)
5611 element = EL_DC_STEELWALL_2_LEFT;
5614 case 0x16a5: // steel wall 2 (right end)
5615 element = EL_DC_STEELWALL_2_RIGHT;
5618 case 0x16a6: // steel wall 2 (top end)
5619 element = EL_DC_STEELWALL_2_TOP;
5622 case 0x16a7: // steel wall 2 (bottom end)
5623 element = EL_DC_STEELWALL_2_BOTTOM;
5626 case 0x16a8: // steel wall 2 (left/right)
5627 element = EL_DC_STEELWALL_2_HORIZONTAL;
5630 case 0x16a9: // steel wall 2 (up/down)
5631 element = EL_DC_STEELWALL_2_VERTICAL;
5634 case 0x16aa: // steel wall 2 (mid)
5635 element = EL_DC_STEELWALL_2_MIDDLE;
5639 element = EL_SIGN_EXCLAMATION;
5643 element = EL_SIGN_RADIOACTIVITY;
5647 element = EL_SIGN_STOP;
5651 element = EL_SIGN_WHEELCHAIR;
5655 element = EL_SIGN_PARKING;
5659 element = EL_SIGN_NO_ENTRY;
5663 element = EL_SIGN_HEART;
5667 element = EL_SIGN_GIVE_WAY;
5671 element = EL_SIGN_ENTRY_FORBIDDEN;
5675 element = EL_SIGN_EMERGENCY_EXIT;
5679 element = EL_SIGN_YIN_YANG;
5683 element = EL_WALL_EMERALD;
5687 element = EL_WALL_DIAMOND;
5691 element = EL_WALL_PEARL;
5695 element = EL_WALL_CRYSTAL;
5699 element = EL_INVISIBLE_WALL;
5703 element = EL_INVISIBLE_STEELWALL;
5707 // EL_INVISIBLE_SAND
5710 element = EL_LIGHT_SWITCH;
5714 element = EL_ENVELOPE_1;
5718 if (element >= 0x0117 && element <= 0x036e) // (?)
5719 element = EL_DIAMOND;
5720 else if (element >= 0x042d && element <= 0x0684) // (?)
5721 element = EL_EMERALD;
5722 else if (element >= 0x157c && element <= 0x158b)
5724 else if (element >= 0x1590 && element <= 0x159f)
5725 element = EL_DC_LANDMINE;
5726 else if (element >= 0x16bc && element <= 0x16cb)
5727 element = EL_INVISIBLE_SAND;
5730 Warn("unknown Diamond Caves element 0x%04x", element);
5732 element = EL_UNKNOWN;
5737 return getMappedElement(element);
5740 static void LoadLevelFromFileStream_DC(File *file, struct LevelInfo *level)
5742 byte header[DC_LEVEL_HEADER_SIZE];
5744 int envelope_header_pos = 62;
5745 int envelope_content_pos = 94;
5746 int level_name_pos = 251;
5747 int level_author_pos = 292;
5748 int envelope_header_len;
5749 int envelope_content_len;
5751 int level_author_len;
5753 int num_yamyam_contents;
5756 getDecodedWord_DC(0, TRUE); // initialize DC2 decoding engine
5758 for (i = 0; i < DC_LEVEL_HEADER_SIZE / 2; i++)
5760 unsigned short header_word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
5762 header[i * 2 + 0] = header_word >> 8;
5763 header[i * 2 + 1] = header_word & 0xff;
5766 // read some values from level header to check level decoding integrity
5767 fieldx = header[6] | (header[7] << 8);
5768 fieldy = header[8] | (header[9] << 8);
5769 num_yamyam_contents = header[60] | (header[61] << 8);
5771 // do some simple sanity checks to ensure that level was correctly decoded
5772 if (fieldx < 1 || fieldx > 256 ||
5773 fieldy < 1 || fieldy > 256 ||
5774 num_yamyam_contents < 1 || num_yamyam_contents > 8)
5776 level->no_valid_file = TRUE;
5778 Warn("cannot decode level from stream -- using empty level");
5783 // maximum envelope header size is 31 bytes
5784 envelope_header_len = header[envelope_header_pos];
5785 // maximum envelope content size is 110 (156?) bytes
5786 envelope_content_len = header[envelope_content_pos];
5788 // maximum level title size is 40 bytes
5789 level_name_len = MIN(header[level_name_pos], MAX_LEVEL_NAME_LEN);
5790 // maximum level author size is 30 (51?) bytes
5791 level_author_len = MIN(header[level_author_pos], MAX_LEVEL_AUTHOR_LEN);
5795 for (i = 0; i < envelope_header_len; i++)
5796 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
5797 level->envelope[0].text[envelope_size++] =
5798 header[envelope_header_pos + 1 + i];
5800 if (envelope_header_len > 0 && envelope_content_len > 0)
5802 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
5803 level->envelope[0].text[envelope_size++] = '\n';
5804 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
5805 level->envelope[0].text[envelope_size++] = '\n';
5808 for (i = 0; i < envelope_content_len; i++)
5809 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
5810 level->envelope[0].text[envelope_size++] =
5811 header[envelope_content_pos + 1 + i];
5813 level->envelope[0].text[envelope_size] = '\0';
5815 level->envelope[0].xsize = MAX_ENVELOPE_XSIZE;
5816 level->envelope[0].ysize = 10;
5817 level->envelope[0].autowrap = TRUE;
5818 level->envelope[0].centered = TRUE;
5820 for (i = 0; i < level_name_len; i++)
5821 level->name[i] = header[level_name_pos + 1 + i];
5822 level->name[level_name_len] = '\0';
5824 for (i = 0; i < level_author_len; i++)
5825 level->author[i] = header[level_author_pos + 1 + i];
5826 level->author[level_author_len] = '\0';
5828 num_yamyam_contents = header[60] | (header[61] << 8);
5829 level->num_yamyam_contents =
5830 MIN(MAX(MIN_ELEMENT_CONTENTS, num_yamyam_contents), MAX_ELEMENT_CONTENTS);
5832 for (i = 0; i < num_yamyam_contents; i++)
5834 for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
5836 unsigned short word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
5837 int element_dc = ((word & 0xff) << 8) | ((word >> 8) & 0xff);
5839 if (i < MAX_ELEMENT_CONTENTS)
5840 level->yamyam_content[i].e[x][y] = getMappedElement_DC(element_dc);
5844 fieldx = header[6] | (header[7] << 8);
5845 fieldy = header[8] | (header[9] << 8);
5846 level->fieldx = MIN(MAX(MIN_LEV_FIELDX, fieldx), MAX_LEV_FIELDX);
5847 level->fieldy = MIN(MAX(MIN_LEV_FIELDY, fieldy), MAX_LEV_FIELDY);
5849 for (y = 0; y < fieldy; y++) for (x = 0; x < fieldx; x++)
5851 unsigned short word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
5852 int element_dc = ((word & 0xff) << 8) | ((word >> 8) & 0xff);
5854 if (x < MAX_LEV_FIELDX && y < MAX_LEV_FIELDY)
5855 level->field[x][y] = getMappedElement_DC(element_dc);
5858 x = MIN(MAX(0, (header[10] | (header[11] << 8)) - 1), MAX_LEV_FIELDX - 1);
5859 y = MIN(MAX(0, (header[12] | (header[13] << 8)) - 1), MAX_LEV_FIELDY - 1);
5860 level->field[x][y] = EL_PLAYER_1;
5862 x = MIN(MAX(0, (header[14] | (header[15] << 8)) - 1), MAX_LEV_FIELDX - 1);
5863 y = MIN(MAX(0, (header[16] | (header[17] << 8)) - 1), MAX_LEV_FIELDY - 1);
5864 level->field[x][y] = EL_PLAYER_2;
5866 level->gems_needed = header[18] | (header[19] << 8);
5868 level->score[SC_EMERALD] = header[20] | (header[21] << 8);
5869 level->score[SC_DIAMOND] = header[22] | (header[23] << 8);
5870 level->score[SC_PEARL] = header[24] | (header[25] << 8);
5871 level->score[SC_CRYSTAL] = header[26] | (header[27] << 8);
5872 level->score[SC_NUT] = header[28] | (header[29] << 8);
5873 level->score[SC_ROBOT] = header[30] | (header[31] << 8);
5874 level->score[SC_SPACESHIP] = header[32] | (header[33] << 8);
5875 level->score[SC_BUG] = header[34] | (header[35] << 8);
5876 level->score[SC_YAMYAM] = header[36] | (header[37] << 8);
5877 level->score[SC_DYNAMITE] = header[38] | (header[39] << 8);
5878 level->score[SC_KEY] = header[40] | (header[41] << 8);
5879 level->score[SC_TIME_BONUS] = header[42] | (header[43] << 8);
5881 level->time = header[44] | (header[45] << 8);
5883 level->amoeba_speed = header[46] | (header[47] << 8);
5884 level->time_light = header[48] | (header[49] << 8);
5885 level->time_timegate = header[50] | (header[51] << 8);
5886 level->time_wheel = header[52] | (header[53] << 8);
5887 level->time_magic_wall = header[54] | (header[55] << 8);
5888 level->extra_time = header[56] | (header[57] << 8);
5889 level->shield_normal_time = header[58] | (header[59] << 8);
5891 // shield and extra time elements do not have a score
5892 level->score[SC_SHIELD] = 0;
5893 level->extra_time_score = 0;
5895 // set time for normal and deadly shields to the same value
5896 level->shield_deadly_time = level->shield_normal_time;
5898 // Diamond Caves has the same (strange) behaviour as Emerald Mine that gems
5899 // can slip down from flat walls, like normal walls and steel walls
5900 level->em_slippery_gems = TRUE;
5902 // time score is counted for each 10 seconds left in Diamond Caves levels
5903 level->time_score_base = 10;
5906 static void LoadLevelFromFileInfo_DC(struct LevelInfo *level,
5907 struct LevelFileInfo *level_file_info,
5908 boolean level_info_only)
5910 char *filename = level_file_info->filename;
5912 int num_magic_bytes = 8;
5913 char magic_bytes[num_magic_bytes + 1];
5914 int num_levels_to_skip = level_file_info->nr - leveldir_current->first_level;
5916 if (!(file = openFile(filename, MODE_READ)))
5918 level->no_valid_file = TRUE;
5920 if (!level_info_only)
5921 Warn("cannot read level '%s' -- using empty level", filename);
5926 // fseek(file, 0x0000, SEEK_SET);
5928 if (level_file_info->packed)
5930 // read "magic bytes" from start of file
5931 if (getStringFromFile(file, magic_bytes, num_magic_bytes + 1) == NULL)
5932 magic_bytes[0] = '\0';
5934 // check "magic bytes" for correct file format
5935 if (!strPrefix(magic_bytes, "DC2"))
5937 level->no_valid_file = TRUE;
5939 Warn("unknown DC level file '%s' -- using empty level", filename);
5944 if (strPrefix(magic_bytes, "DC2Win95") ||
5945 strPrefix(magic_bytes, "DC2Win98"))
5947 int position_first_level = 0x00fa;
5948 int extra_bytes = 4;
5951 // advance file stream to first level inside the level package
5952 skip_bytes = position_first_level - num_magic_bytes - extra_bytes;
5954 // each block of level data is followed by block of non-level data
5955 num_levels_to_skip *= 2;
5957 // at least skip header bytes, therefore use ">= 0" instead of "> 0"
5958 while (num_levels_to_skip >= 0)
5960 // advance file stream to next level inside the level package
5961 if (seekFile(file, skip_bytes, SEEK_CUR) != 0)
5963 level->no_valid_file = TRUE;
5965 Warn("cannot fseek in file '%s' -- using empty level", filename);
5970 // skip apparently unused extra bytes following each level
5971 ReadUnusedBytesFromFile(file, extra_bytes);
5973 // read size of next level in level package
5974 skip_bytes = getFile32BitLE(file);
5976 num_levels_to_skip--;
5981 level->no_valid_file = TRUE;
5983 Warn("unknown DC2 level file '%s' -- using empty level", filename);
5989 LoadLevelFromFileStream_DC(file, level);
5995 // ----------------------------------------------------------------------------
5996 // functions for loading SB level
5997 // ----------------------------------------------------------------------------
5999 int getMappedElement_SB(int element_ascii, boolean use_ces)
6007 sb_element_mapping[] =
6009 { ' ', EL_EMPTY, EL_CUSTOM_1 }, // floor (space)
6010 { '#', EL_STEELWALL, EL_CUSTOM_2 }, // wall
6011 { '@', EL_PLAYER_1, EL_CUSTOM_3 }, // player
6012 { '$', EL_SOKOBAN_OBJECT, EL_CUSTOM_4 }, // box
6013 { '.', EL_SOKOBAN_FIELD_EMPTY, EL_CUSTOM_5 }, // goal square
6014 { '*', EL_SOKOBAN_FIELD_FULL, EL_CUSTOM_6 }, // box on goal square
6015 { '+', EL_SOKOBAN_FIELD_PLAYER, EL_CUSTOM_7 }, // player on goal square
6016 { '_', EL_INVISIBLE_STEELWALL, EL_FROM_LEVEL_TEMPLATE }, // floor beyond border
6023 for (i = 0; sb_element_mapping[i].ascii != 0; i++)
6024 if (element_ascii == sb_element_mapping[i].ascii)
6025 return (use_ces ? sb_element_mapping[i].ce : sb_element_mapping[i].sb);
6027 return EL_UNDEFINED;
6030 static void SetLevelSettings_SB(struct LevelInfo *level)
6034 level->use_step_counter = TRUE;
6037 level->score[SC_TIME_BONUS] = 0;
6038 level->time_score_base = 1;
6039 level->rate_time_over_score = TRUE;
6042 level->auto_exit_sokoban = TRUE;
6045 static void LoadLevelFromFileInfo_SB(struct LevelInfo *level,
6046 struct LevelFileInfo *level_file_info,
6047 boolean level_info_only)
6049 char *filename = level_file_info->filename;
6050 char line[MAX_LINE_LEN], line_raw[MAX_LINE_LEN], previous_line[MAX_LINE_LEN];
6051 char last_comment[MAX_LINE_LEN];
6052 char level_name[MAX_LINE_LEN];
6055 int num_levels_to_skip = level_file_info->nr - leveldir_current->first_level;
6056 boolean read_continued_line = FALSE;
6057 boolean reading_playfield = FALSE;
6058 boolean got_valid_playfield_line = FALSE;
6059 boolean invalid_playfield_char = FALSE;
6060 boolean load_xsb_to_ces = check_special_flags("load_xsb_to_ces");
6061 int file_level_nr = 0;
6063 int x = 0, y = 0; // initialized to make compilers happy
6065 last_comment[0] = '\0';
6066 level_name[0] = '\0';
6068 if (!(file = openFile(filename, MODE_READ)))
6070 level->no_valid_file = TRUE;
6072 if (!level_info_only)
6073 Warn("cannot read level '%s' -- using empty level", filename);
6078 while (!checkEndOfFile(file))
6080 // level successfully read, but next level may follow here
6081 if (!got_valid_playfield_line && reading_playfield)
6083 // read playfield from single level file -- skip remaining file
6084 if (!level_file_info->packed)
6087 if (file_level_nr >= num_levels_to_skip)
6092 last_comment[0] = '\0';
6093 level_name[0] = '\0';
6095 reading_playfield = FALSE;
6098 got_valid_playfield_line = FALSE;
6100 // read next line of input file
6101 if (!getStringFromFile(file, line, MAX_LINE_LEN))
6104 // check if line was completely read and is terminated by line break
6105 if (strlen(line) > 0 && line[strlen(line) - 1] == '\n')
6108 // cut trailing line break (this can be newline and/or carriage return)
6109 for (line_ptr = &line[strlen(line)]; line_ptr >= line; line_ptr--)
6110 if ((*line_ptr == '\n' || *line_ptr == '\r') && *(line_ptr + 1) == '\0')
6113 // copy raw input line for later use (mainly debugging output)
6114 strcpy(line_raw, line);
6116 if (read_continued_line)
6118 // append new line to existing line, if there is enough space
6119 if (strlen(previous_line) + strlen(line_ptr) < MAX_LINE_LEN)
6120 strcat(previous_line, line_ptr);
6122 strcpy(line, previous_line); // copy storage buffer to line
6124 read_continued_line = FALSE;
6127 // if the last character is '\', continue at next line
6128 if (strlen(line) > 0 && line[strlen(line) - 1] == '\\')
6130 line[strlen(line) - 1] = '\0'; // cut off trailing backslash
6131 strcpy(previous_line, line); // copy line to storage buffer
6133 read_continued_line = TRUE;
6139 if (line[0] == '\0')
6142 // extract comment text from comment line
6145 for (line_ptr = line; *line_ptr; line_ptr++)
6146 if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != ';')
6149 strcpy(last_comment, line_ptr);
6154 // extract level title text from line containing level title
6155 if (line[0] == '\'')
6157 strcpy(level_name, &line[1]);
6159 if (strlen(level_name) > 0 && level_name[strlen(level_name) - 1] == '\'')
6160 level_name[strlen(level_name) - 1] = '\0';
6165 // skip lines containing only spaces (or empty lines)
6166 for (line_ptr = line; *line_ptr; line_ptr++)
6167 if (*line_ptr != ' ')
6169 if (*line_ptr == '\0')
6172 // at this point, we have found a line containing part of a playfield
6174 got_valid_playfield_line = TRUE;
6176 if (!reading_playfield)
6178 reading_playfield = TRUE;
6179 invalid_playfield_char = FALSE;
6181 for (x = 0; x < MAX_LEV_FIELDX; x++)
6182 for (y = 0; y < MAX_LEV_FIELDY; y++)
6183 level->field[x][y] = getMappedElement_SB(' ', load_xsb_to_ces);
6188 // start with topmost tile row
6192 // skip playfield line if larger row than allowed
6193 if (y >= MAX_LEV_FIELDY)
6196 // start with leftmost tile column
6199 // read playfield elements from line
6200 for (line_ptr = line; *line_ptr; line_ptr++)
6202 int mapped_sb_element = getMappedElement_SB(*line_ptr, load_xsb_to_ces);
6204 // stop parsing playfield line if larger column than allowed
6205 if (x >= MAX_LEV_FIELDX)
6208 if (mapped_sb_element == EL_UNDEFINED)
6210 invalid_playfield_char = TRUE;
6215 level->field[x][y] = mapped_sb_element;
6217 // continue with next tile column
6220 level->fieldx = MAX(x, level->fieldx);
6223 if (invalid_playfield_char)
6225 // if first playfield line, treat invalid lines as comment lines
6227 reading_playfield = FALSE;
6232 // continue with next tile row
6240 level->fieldx = MIN(MAX(MIN_LEV_FIELDX, level->fieldx), MAX_LEV_FIELDX);
6241 level->fieldy = MIN(MAX(MIN_LEV_FIELDY, level->fieldy), MAX_LEV_FIELDY);
6243 if (!reading_playfield)
6245 level->no_valid_file = TRUE;
6247 Warn("cannot read level '%s' -- using empty level", filename);
6252 if (*level_name != '\0')
6254 strncpy(level->name, level_name, MAX_LEVEL_NAME_LEN);
6255 level->name[MAX_LEVEL_NAME_LEN] = '\0';
6257 else if (*last_comment != '\0')
6259 strncpy(level->name, last_comment, MAX_LEVEL_NAME_LEN);
6260 level->name[MAX_LEVEL_NAME_LEN] = '\0';
6264 sprintf(level->name, "--> Level %d <--", level_file_info->nr);
6267 // set all empty fields beyond the border walls to invisible steel wall
6268 for (y = 0; y < level->fieldy; y++) for (x = 0; x < level->fieldx; x++)
6270 if ((x == 0 || x == level->fieldx - 1 ||
6271 y == 0 || y == level->fieldy - 1) &&
6272 level->field[x][y] == getMappedElement_SB(' ', load_xsb_to_ces))
6273 FloodFillLevel(x, y, getMappedElement_SB('_', load_xsb_to_ces),
6274 level->field, level->fieldx, level->fieldy);
6277 // set special level settings for Sokoban levels
6278 SetLevelSettings_SB(level);
6280 if (load_xsb_to_ces)
6282 // special global settings can now be set in level template
6283 level->use_custom_template = TRUE;
6288 // -------------------------------------------------------------------------
6289 // functions for handling native levels
6290 // -------------------------------------------------------------------------
6292 static void LoadLevelFromFileInfo_EM(struct LevelInfo *level,
6293 struct LevelFileInfo *level_file_info,
6294 boolean level_info_only)
6296 if (!LoadNativeLevel_EM(level_file_info->filename, level_info_only))
6297 level->no_valid_file = TRUE;
6300 static void LoadLevelFromFileInfo_SP(struct LevelInfo *level,
6301 struct LevelFileInfo *level_file_info,
6302 boolean level_info_only)
6306 // determine position of requested level inside level package
6307 if (level_file_info->packed)
6308 pos = level_file_info->nr - leveldir_current->first_level;
6310 if (!LoadNativeLevel_SP(level_file_info->filename, pos, level_info_only))
6311 level->no_valid_file = TRUE;
6314 static void LoadLevelFromFileInfo_MM(struct LevelInfo *level,
6315 struct LevelFileInfo *level_file_info,
6316 boolean level_info_only)
6318 if (!LoadNativeLevel_MM(level_file_info->filename, level_info_only))
6319 level->no_valid_file = TRUE;
6322 void CopyNativeLevel_RND_to_Native(struct LevelInfo *level)
6324 if (level->game_engine_type == GAME_ENGINE_TYPE_EM)
6325 CopyNativeLevel_RND_to_EM(level);
6326 else if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
6327 CopyNativeLevel_RND_to_SP(level);
6328 else if (level->game_engine_type == GAME_ENGINE_TYPE_MM)
6329 CopyNativeLevel_RND_to_MM(level);
6332 void CopyNativeLevel_Native_to_RND(struct LevelInfo *level)
6334 if (level->game_engine_type == GAME_ENGINE_TYPE_EM)
6335 CopyNativeLevel_EM_to_RND(level);
6336 else if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
6337 CopyNativeLevel_SP_to_RND(level);
6338 else if (level->game_engine_type == GAME_ENGINE_TYPE_MM)
6339 CopyNativeLevel_MM_to_RND(level);
6342 void SaveNativeLevel(struct LevelInfo *level)
6344 if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
6346 char *basename = getSingleLevelBasenameExt(level->file_info.nr, "sp");
6347 char *filename = getLevelFilenameFromBasename(basename);
6349 CopyNativeLevel_RND_to_SP(level);
6350 CopyNativeTape_RND_to_SP(level);
6352 SaveNativeLevel_SP(filename);
6357 // ----------------------------------------------------------------------------
6358 // functions for loading generic level
6359 // ----------------------------------------------------------------------------
6361 static void LoadLevelFromFileInfo(struct LevelInfo *level,
6362 struct LevelFileInfo *level_file_info,
6363 boolean level_info_only)
6365 // always start with reliable default values
6366 setLevelInfoToDefaults(level, level_info_only, TRUE);
6368 switch (level_file_info->type)
6370 case LEVEL_FILE_TYPE_RND:
6371 LoadLevelFromFileInfo_RND(level, level_file_info, level_info_only);
6374 case LEVEL_FILE_TYPE_EM:
6375 LoadLevelFromFileInfo_EM(level, level_file_info, level_info_only);
6376 level->game_engine_type = GAME_ENGINE_TYPE_EM;
6379 case LEVEL_FILE_TYPE_SP:
6380 LoadLevelFromFileInfo_SP(level, level_file_info, level_info_only);
6381 level->game_engine_type = GAME_ENGINE_TYPE_SP;
6384 case LEVEL_FILE_TYPE_MM:
6385 LoadLevelFromFileInfo_MM(level, level_file_info, level_info_only);
6386 level->game_engine_type = GAME_ENGINE_TYPE_MM;
6389 case LEVEL_FILE_TYPE_DC:
6390 LoadLevelFromFileInfo_DC(level, level_file_info, level_info_only);
6393 case LEVEL_FILE_TYPE_SB:
6394 LoadLevelFromFileInfo_SB(level, level_file_info, level_info_only);
6398 LoadLevelFromFileInfo_RND(level, level_file_info, level_info_only);
6402 // if level file is invalid, restore level structure to default values
6403 if (level->no_valid_file)
6404 setLevelInfoToDefaults(level, level_info_only, FALSE);
6406 if (level->game_engine_type == GAME_ENGINE_TYPE_UNKNOWN)
6407 level->game_engine_type = GAME_ENGINE_TYPE_RND;
6409 if (level_file_info->type != LEVEL_FILE_TYPE_RND)
6410 CopyNativeLevel_Native_to_RND(level);
6413 void LoadLevelFromFilename(struct LevelInfo *level, char *filename)
6415 static struct LevelFileInfo level_file_info;
6417 // always start with reliable default values
6418 setFileInfoToDefaults(&level_file_info);
6420 level_file_info.nr = 0; // unknown level number
6421 level_file_info.type = LEVEL_FILE_TYPE_RND; // no others supported yet
6423 setString(&level_file_info.filename, filename);
6425 LoadLevelFromFileInfo(level, &level_file_info, FALSE);
6428 static void LoadLevel_InitVersion(struct LevelInfo *level)
6432 if (leveldir_current == NULL) // only when dumping level
6435 // all engine modifications also valid for levels which use latest engine
6436 if (level->game_version < VERSION_IDENT(3,2,0,5))
6438 // time bonus score was given for 10 s instead of 1 s before 3.2.0-5
6439 level->time_score_base = 10;
6442 if (leveldir_current->latest_engine)
6444 // ---------- use latest game engine --------------------------------------
6446 /* For all levels which are forced to use the latest game engine version
6447 (normally all but user contributed, private and undefined levels), set
6448 the game engine version to the actual version; this allows for actual
6449 corrections in the game engine to take effect for existing, converted
6450 levels (from "classic" or other existing games) to make the emulation
6451 of the corresponding game more accurate, while (hopefully) not breaking
6452 existing levels created from other players. */
6454 level->game_version = GAME_VERSION_ACTUAL;
6456 /* Set special EM style gems behaviour: EM style gems slip down from
6457 normal, steel and growing wall. As this is a more fundamental change,
6458 it seems better to set the default behaviour to "off" (as it is more
6459 natural) and make it configurable in the level editor (as a property
6460 of gem style elements). Already existing converted levels (neither
6461 private nor contributed levels) are changed to the new behaviour. */
6463 if (level->file_version < FILE_VERSION_2_0)
6464 level->em_slippery_gems = TRUE;
6469 // ---------- use game engine the level was created with --------------------
6471 /* For all levels which are not forced to use the latest game engine
6472 version (normally user contributed, private and undefined levels),
6473 use the version of the game engine the levels were created for.
6475 Since 2.0.1, the game engine version is now directly stored
6476 in the level file (chunk "VERS"), so there is no need anymore
6477 to set the game version from the file version (except for old,
6478 pre-2.0 levels, where the game version is still taken from the
6479 file format version used to store the level -- see above). */
6481 // player was faster than enemies in 1.0.0 and before
6482 if (level->file_version == FILE_VERSION_1_0)
6483 for (i = 0; i < MAX_PLAYERS; i++)
6484 level->initial_player_stepsize[i] = STEPSIZE_FAST;
6486 // default behaviour for EM style gems was "slippery" only in 2.0.1
6487 if (level->game_version == VERSION_IDENT(2,0,1,0))
6488 level->em_slippery_gems = TRUE;
6490 // springs could be pushed over pits before (pre-release version) 2.2.0
6491 if (level->game_version < VERSION_IDENT(2,2,0,0))
6492 level->use_spring_bug = TRUE;
6494 if (level->game_version < VERSION_IDENT(3,2,0,5))
6496 // time orb caused limited time in endless time levels before 3.2.0-5
6497 level->use_time_orb_bug = TRUE;
6499 // default behaviour for snapping was "no snap delay" before 3.2.0-5
6500 level->block_snap_field = FALSE;
6502 // extra time score was same value as time left score before 3.2.0-5
6503 level->extra_time_score = level->score[SC_TIME_BONUS];
6506 if (level->game_version < VERSION_IDENT(3,2,0,7))
6508 // default behaviour for snapping was "not continuous" before 3.2.0-7
6509 level->continuous_snapping = FALSE;
6512 // only few elements were able to actively move into acid before 3.1.0
6513 // trigger settings did not exist before 3.1.0; set to default "any"
6514 if (level->game_version < VERSION_IDENT(3,1,0,0))
6516 // correct "can move into acid" settings (all zero in old levels)
6518 level->can_move_into_acid_bits = 0; // nothing can move into acid
6519 level->dont_collide_with_bits = 0; // nothing is deadly when colliding
6521 setMoveIntoAcidProperty(level, EL_ROBOT, TRUE);
6522 setMoveIntoAcidProperty(level, EL_SATELLITE, TRUE);
6523 setMoveIntoAcidProperty(level, EL_PENGUIN, TRUE);
6524 setMoveIntoAcidProperty(level, EL_BALLOON, TRUE);
6526 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6527 SET_PROPERTY(EL_CUSTOM_START + i, EP_CAN_MOVE_INTO_ACID, TRUE);
6529 // correct trigger settings (stored as zero == "none" in old levels)
6531 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6533 int element = EL_CUSTOM_START + i;
6534 struct ElementInfo *ei = &element_info[element];
6536 for (j = 0; j < ei->num_change_pages; j++)
6538 struct ElementChangeInfo *change = &ei->change_page[j];
6540 change->trigger_player = CH_PLAYER_ANY;
6541 change->trigger_page = CH_PAGE_ANY;
6546 // try to detect and fix "Snake Bite" levels, which are broken with 3.2.0
6548 int element = EL_CUSTOM_256;
6549 struct ElementInfo *ei = &element_info[element];
6550 struct ElementChangeInfo *change = &ei->change_page[0];
6552 /* This is needed to fix a problem that was caused by a bugfix in function
6553 game.c/CreateFieldExt() introduced with 3.2.0 that corrects the behaviour
6554 when a custom element changes to EL_SOKOBAN_FIELD_PLAYER (before, it did
6555 not replace walkable elements, but instead just placed the player on it,
6556 without placing the Sokoban field under the player). Unfortunately, this
6557 breaks "Snake Bite" style levels when the snake is halfway through a door
6558 that just closes (the snake head is still alive and can be moved in this
6559 case). This can be fixed by replacing the EL_SOKOBAN_FIELD_PLAYER by the
6560 player (without Sokoban element) which then gets killed as designed). */
6562 if ((strncmp(leveldir_current->identifier, "snake_bite", 10) == 0 ||
6563 strncmp(ei->description, "pause b4 death", 14) == 0) &&
6564 change->target_element == EL_SOKOBAN_FIELD_PLAYER)
6565 change->target_element = EL_PLAYER_1;
6568 // try to detect and fix "Zelda" style levels, which are broken with 3.2.5
6569 if (level->game_version < VERSION_IDENT(3,2,5,0))
6571 /* This is needed to fix a problem that was caused by a bugfix in function
6572 game.c/CheckTriggeredElementChangeExt() introduced with 3.2.5 that
6573 corrects the behaviour when a custom element changes to another custom
6574 element with a higher element number that has change actions defined.
6575 Normally, only one change per frame is allowed for custom elements.
6576 Therefore, it is checked if a custom element already changed in the
6577 current frame; if it did, subsequent changes are suppressed.
6578 Unfortunately, this is only checked for element changes, but not for
6579 change actions, which are still executed. As the function above loops
6580 through all custom elements from lower to higher, an element change
6581 resulting in a lower CE number won't be checked again, while a target
6582 element with a higher number will also be checked, and potential change
6583 actions will get executed for this CE, too (which is wrong), while
6584 further changes are ignored (which is correct). As this bugfix breaks
6585 Zelda II (and introduces graphical bugs to Zelda I, and also breaks a
6586 few other levels like Alan Bond's "FMV"), allow the previous, incorrect
6587 behaviour for existing levels and tapes that make use of this bug */
6589 level->use_action_after_change_bug = TRUE;
6592 // not centering level after relocating player was default only in 3.2.3
6593 if (level->game_version == VERSION_IDENT(3,2,3,0)) // (no pre-releases)
6594 level->shifted_relocation = TRUE;
6596 // EM style elements always chain-exploded in R'n'D engine before 3.2.6
6597 if (level->game_version < VERSION_IDENT(3,2,6,0))
6598 level->em_explodes_by_fire = TRUE;
6600 // levels were solved by the first player entering an exit up to 4.1.0.0
6601 if (level->game_version <= VERSION_IDENT(4,1,0,0))
6602 level->solved_by_one_player = TRUE;
6604 // game logic of "game of life" and "biomaze" was buggy before 4.1.1.1
6605 if (level->game_version < VERSION_IDENT(4,1,1,1))
6606 level->use_life_bugs = TRUE;
6608 // only Sokoban fields (but not objects) had to be solved before 4.1.1.1
6609 if (level->game_version < VERSION_IDENT(4,1,1,1))
6610 level->sb_objects_needed = FALSE;
6612 // CE actions were triggered by unfinished digging/collecting up to 4.2.2.0
6613 if (level->game_version <= VERSION_IDENT(4,2,2,0))
6614 level->finish_dig_collect = FALSE;
6616 // CE changing to player was kept under the player if walkable up to 4.2.3.1
6617 if (level->game_version <= VERSION_IDENT(4,2,3,1))
6618 level->keep_walkable_ce = TRUE;
6621 static void LoadLevel_InitSettings_SB(struct LevelInfo *level)
6623 boolean is_sokoban_level = TRUE; // unless non-Sokoban elements found
6626 // check if this level is (not) a Sokoban level
6627 for (y = 0; y < level->fieldy; y++)
6628 for (x = 0; x < level->fieldx; x++)
6629 if (!IS_SB_ELEMENT(Tile[x][y]))
6630 is_sokoban_level = FALSE;
6632 if (is_sokoban_level)
6634 // set special level settings for Sokoban levels
6635 SetLevelSettings_SB(level);
6639 static void LoadLevel_InitSettings(struct LevelInfo *level)
6641 // adjust level settings for (non-native) Sokoban-style levels
6642 LoadLevel_InitSettings_SB(level);
6644 // rename levels with title "nameless level" or if renaming is forced
6645 if (leveldir_current->empty_level_name != NULL &&
6646 (strEqual(level->name, NAMELESS_LEVEL_NAME) ||
6647 leveldir_current->force_level_name))
6648 snprintf(level->name, MAX_LEVEL_NAME_LEN + 1,
6649 leveldir_current->empty_level_name, level_nr);
6652 static void LoadLevel_InitStandardElements(struct LevelInfo *level)
6656 // map elements that have changed in newer versions
6657 level->amoeba_content = getMappedElementByVersion(level->amoeba_content,
6658 level->game_version);
6659 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
6660 for (x = 0; x < 3; x++)
6661 for (y = 0; y < 3; y++)
6662 level->yamyam_content[i].e[x][y] =
6663 getMappedElementByVersion(level->yamyam_content[i].e[x][y],
6664 level->game_version);
6668 static void LoadLevel_InitCustomElements(struct LevelInfo *level)
6672 // map custom element change events that have changed in newer versions
6673 // (these following values were accidentally changed in version 3.0.1)
6674 // (this seems to be needed only for 'ab_levelset3' and 'ab_levelset4')
6675 if (level->game_version <= VERSION_IDENT(3,0,0,0))
6677 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6679 int element = EL_CUSTOM_START + i;
6681 // order of checking and copying events to be mapped is important
6682 // (do not change the start and end value -- they are constant)
6683 for (j = CE_BY_OTHER_ACTION; j >= CE_VALUE_GETS_ZERO; j--)
6685 if (HAS_CHANGE_EVENT(element, j - 2))
6687 SET_CHANGE_EVENT(element, j - 2, FALSE);
6688 SET_CHANGE_EVENT(element, j, TRUE);
6692 // order of checking and copying events to be mapped is important
6693 // (do not change the start and end value -- they are constant)
6694 for (j = CE_PLAYER_COLLECTS_X; j >= CE_HITTING_SOMETHING; j--)
6696 if (HAS_CHANGE_EVENT(element, j - 1))
6698 SET_CHANGE_EVENT(element, j - 1, FALSE);
6699 SET_CHANGE_EVENT(element, j, TRUE);
6705 // initialize "can_change" field for old levels with only one change page
6706 if (level->game_version <= VERSION_IDENT(3,0,2,0))
6708 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6710 int element = EL_CUSTOM_START + i;
6712 if (CAN_CHANGE(element))
6713 element_info[element].change->can_change = TRUE;
6717 // correct custom element values (for old levels without these options)
6718 if (level->game_version < VERSION_IDENT(3,1,1,0))
6720 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6722 int element = EL_CUSTOM_START + i;
6723 struct ElementInfo *ei = &element_info[element];
6725 if (ei->access_direction == MV_NO_DIRECTION)
6726 ei->access_direction = MV_ALL_DIRECTIONS;
6730 // correct custom element values (fix invalid values for all versions)
6733 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6735 int element = EL_CUSTOM_START + i;
6736 struct ElementInfo *ei = &element_info[element];
6738 for (j = 0; j < ei->num_change_pages; j++)
6740 struct ElementChangeInfo *change = &ei->change_page[j];
6742 if (change->trigger_player == CH_PLAYER_NONE)
6743 change->trigger_player = CH_PLAYER_ANY;
6745 if (change->trigger_side == CH_SIDE_NONE)
6746 change->trigger_side = CH_SIDE_ANY;
6751 // initialize "can_explode" field for old levels which did not store this
6752 // !!! CHECK THIS -- "<= 3,1,0,0" IS PROBABLY WRONG !!!
6753 if (level->game_version <= VERSION_IDENT(3,1,0,0))
6755 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6757 int element = EL_CUSTOM_START + i;
6759 if (EXPLODES_1X1_OLD(element))
6760 element_info[element].explosion_type = EXPLODES_1X1;
6762 SET_PROPERTY(element, EP_CAN_EXPLODE, (EXPLODES_BY_FIRE(element) ||
6763 EXPLODES_SMASHED(element) ||
6764 EXPLODES_IMPACT(element)));
6768 // correct previously hard-coded move delay values for maze runner style
6769 if (level->game_version < VERSION_IDENT(3,1,1,0))
6771 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6773 int element = EL_CUSTOM_START + i;
6775 if (element_info[element].move_pattern & MV_MAZE_RUNNER_STYLE)
6777 // previously hard-coded and therefore ignored
6778 element_info[element].move_delay_fixed = 9;
6779 element_info[element].move_delay_random = 0;
6784 // set some other uninitialized values of custom elements in older levels
6785 if (level->game_version < VERSION_IDENT(3,1,0,0))
6787 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6789 int element = EL_CUSTOM_START + i;
6791 element_info[element].access_direction = MV_ALL_DIRECTIONS;
6793 element_info[element].explosion_delay = 17;
6794 element_info[element].ignition_delay = 8;
6798 // set mouse click change events to work for left/middle/right mouse button
6799 if (level->game_version < VERSION_IDENT(4,2,3,0))
6801 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6803 int element = EL_CUSTOM_START + i;
6804 struct ElementInfo *ei = &element_info[element];
6806 for (j = 0; j < ei->num_change_pages; j++)
6808 struct ElementChangeInfo *change = &ei->change_page[j];
6810 if (change->has_event[CE_CLICKED_BY_MOUSE] ||
6811 change->has_event[CE_PRESSED_BY_MOUSE] ||
6812 change->has_event[CE_MOUSE_CLICKED_ON_X] ||
6813 change->has_event[CE_MOUSE_PRESSED_ON_X])
6814 change->trigger_side = CH_SIDE_ANY;
6820 static void LoadLevel_InitElements(struct LevelInfo *level)
6822 LoadLevel_InitStandardElements(level);
6824 if (level->file_has_custom_elements)
6825 LoadLevel_InitCustomElements(level);
6827 // initialize element properties for level editor etc.
6828 InitElementPropertiesEngine(level->game_version);
6829 InitElementPropertiesGfxElement();
6832 static void LoadLevel_InitPlayfield(struct LevelInfo *level)
6836 // map elements that have changed in newer versions
6837 for (y = 0; y < level->fieldy; y++)
6838 for (x = 0; x < level->fieldx; x++)
6839 level->field[x][y] = getMappedElementByVersion(level->field[x][y],
6840 level->game_version);
6842 // clear unused playfield data (nicer if level gets resized in editor)
6843 for (x = 0; x < MAX_LEV_FIELDX; x++)
6844 for (y = 0; y < MAX_LEV_FIELDY; y++)
6845 if (x >= level->fieldx || y >= level->fieldy)
6846 level->field[x][y] = EL_EMPTY;
6848 // copy elements to runtime playfield array
6849 for (x = 0; x < MAX_LEV_FIELDX; x++)
6850 for (y = 0; y < MAX_LEV_FIELDY; y++)
6851 Tile[x][y] = level->field[x][y];
6853 // initialize level size variables for faster access
6854 lev_fieldx = level->fieldx;
6855 lev_fieldy = level->fieldy;
6857 // determine border element for this level
6858 if (level->file_info.type == LEVEL_FILE_TYPE_DC)
6859 BorderElement = EL_EMPTY; // (in editor, SetBorderElement() is used)
6864 static void LoadLevel_InitNativeEngines(struct LevelInfo *level)
6866 struct LevelFileInfo *level_file_info = &level->file_info;
6868 if (level_file_info->type == LEVEL_FILE_TYPE_RND)
6869 CopyNativeLevel_RND_to_Native(level);
6872 static void LoadLevelTemplate_LoadAndInit(void)
6874 LoadLevelFromFileInfo(&level_template, &level_template.file_info, FALSE);
6876 LoadLevel_InitVersion(&level_template);
6877 LoadLevel_InitElements(&level_template);
6878 LoadLevel_InitSettings(&level_template);
6880 ActivateLevelTemplate();
6883 void LoadLevelTemplate(int nr)
6885 if (!fileExists(getGlobalLevelTemplateFilename()))
6887 Warn("no level template found for this level");
6892 setLevelFileInfo(&level_template.file_info, nr);
6894 LoadLevelTemplate_LoadAndInit();
6897 static void LoadNetworkLevelTemplate(struct NetworkLevelInfo *network_level)
6899 copyLevelFileInfo(&network_level->tmpl_info, &level_template.file_info);
6901 LoadLevelTemplate_LoadAndInit();
6904 static void LoadLevel_LoadAndInit(struct NetworkLevelInfo *network_level)
6906 LoadLevelFromFileInfo(&level, &level.file_info, FALSE);
6908 if (level.use_custom_template)
6910 if (network_level != NULL)
6911 LoadNetworkLevelTemplate(network_level);
6913 LoadLevelTemplate(-1);
6916 LoadLevel_InitVersion(&level);
6917 LoadLevel_InitElements(&level);
6918 LoadLevel_InitPlayfield(&level);
6919 LoadLevel_InitSettings(&level);
6921 LoadLevel_InitNativeEngines(&level);
6924 void LoadLevel(int nr)
6926 SetLevelSetInfo(leveldir_current->identifier, nr);
6928 setLevelFileInfo(&level.file_info, nr);
6930 LoadLevel_LoadAndInit(NULL);
6933 void LoadLevelInfoOnly(int nr)
6935 setLevelFileInfo(&level.file_info, nr);
6937 LoadLevelFromFileInfo(&level, &level.file_info, TRUE);
6940 void LoadNetworkLevel(struct NetworkLevelInfo *network_level)
6942 SetLevelSetInfo(network_level->leveldir_identifier,
6943 network_level->file_info.nr);
6945 copyLevelFileInfo(&network_level->file_info, &level.file_info);
6947 LoadLevel_LoadAndInit(network_level);
6950 static int SaveLevel_VERS(FILE *file, struct LevelInfo *level)
6954 chunk_size += putFileVersion(file, level->file_version);
6955 chunk_size += putFileVersion(file, level->game_version);
6960 static int SaveLevel_DATE(FILE *file, struct LevelInfo *level)
6964 chunk_size += putFile16BitBE(file, level->creation_date.year);
6965 chunk_size += putFile8Bit(file, level->creation_date.month);
6966 chunk_size += putFile8Bit(file, level->creation_date.day);
6971 #if ENABLE_HISTORIC_CHUNKS
6972 static void SaveLevel_HEAD(FILE *file, struct LevelInfo *level)
6976 putFile8Bit(file, level->fieldx);
6977 putFile8Bit(file, level->fieldy);
6979 putFile16BitBE(file, level->time);
6980 putFile16BitBE(file, level->gems_needed);
6982 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
6983 putFile8Bit(file, level->name[i]);
6985 for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
6986 putFile8Bit(file, level->score[i]);
6988 for (i = 0; i < STD_ELEMENT_CONTENTS; i++)
6989 for (y = 0; y < 3; y++)
6990 for (x = 0; x < 3; x++)
6991 putFile8Bit(file, (level->encoding_16bit_yamyam ? EL_EMPTY :
6992 level->yamyam_content[i].e[x][y]));
6993 putFile8Bit(file, level->amoeba_speed);
6994 putFile8Bit(file, level->time_magic_wall);
6995 putFile8Bit(file, level->time_wheel);
6996 putFile8Bit(file, (level->encoding_16bit_amoeba ? EL_EMPTY :
6997 level->amoeba_content));
6998 putFile8Bit(file, (level->initial_player_stepsize == STEPSIZE_FAST ? 1 : 0));
6999 putFile8Bit(file, (level->initial_gravity ? 1 : 0));
7000 putFile8Bit(file, (level->encoding_16bit_field ? 1 : 0));
7001 putFile8Bit(file, (level->em_slippery_gems ? 1 : 0));
7003 putFile8Bit(file, (level->use_custom_template ? 1 : 0));
7005 putFile8Bit(file, (level->block_last_field ? 1 : 0));
7006 putFile8Bit(file, (level->sp_block_last_field ? 1 : 0));
7007 putFile32BitBE(file, level->can_move_into_acid_bits);
7008 putFile8Bit(file, level->dont_collide_with_bits);
7010 putFile8Bit(file, (level->use_spring_bug ? 1 : 0));
7011 putFile8Bit(file, (level->use_step_counter ? 1 : 0));
7013 putFile8Bit(file, (level->instant_relocation ? 1 : 0));
7014 putFile8Bit(file, (level->can_pass_to_walkable ? 1 : 0));
7015 putFile8Bit(file, (level->grow_into_diggable ? 1 : 0));
7017 putFile8Bit(file, level->game_engine_type);
7019 WriteUnusedBytesToFile(file, LEVEL_CHUNK_HEAD_UNUSED);
7023 static int SaveLevel_NAME(FILE *file, struct LevelInfo *level)
7028 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
7029 chunk_size += putFile8Bit(file, level->name[i]);
7034 static int SaveLevel_AUTH(FILE *file, struct LevelInfo *level)
7039 for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
7040 chunk_size += putFile8Bit(file, level->author[i]);
7045 #if ENABLE_HISTORIC_CHUNKS
7046 static int SaveLevel_BODY(FILE *file, struct LevelInfo *level)
7051 for (y = 0; y < level->fieldy; y++)
7052 for (x = 0; x < level->fieldx; x++)
7053 if (level->encoding_16bit_field)
7054 chunk_size += putFile16BitBE(file, level->field[x][y]);
7056 chunk_size += putFile8Bit(file, level->field[x][y]);
7062 static int SaveLevel_BODY(FILE *file, struct LevelInfo *level)
7067 for (y = 0; y < level->fieldy; y++)
7068 for (x = 0; x < level->fieldx; x++)
7069 chunk_size += putFile16BitBE(file, level->field[x][y]);
7074 #if ENABLE_HISTORIC_CHUNKS
7075 static void SaveLevel_CONT(FILE *file, struct LevelInfo *level)
7079 putFile8Bit(file, EL_YAMYAM);
7080 putFile8Bit(file, level->num_yamyam_contents);
7081 putFile8Bit(file, 0);
7082 putFile8Bit(file, 0);
7084 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
7085 for (y = 0; y < 3; y++)
7086 for (x = 0; x < 3; x++)
7087 if (level->encoding_16bit_field)
7088 putFile16BitBE(file, level->yamyam_content[i].e[x][y]);
7090 putFile8Bit(file, level->yamyam_content[i].e[x][y]);
7094 #if ENABLE_HISTORIC_CHUNKS
7095 static void SaveLevel_CNT2(FILE *file, struct LevelInfo *level, int element)
7098 int num_contents, content_xsize, content_ysize;
7099 int content_array[MAX_ELEMENT_CONTENTS][3][3];
7101 if (element == EL_YAMYAM)
7103 num_contents = level->num_yamyam_contents;
7107 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
7108 for (y = 0; y < 3; y++)
7109 for (x = 0; x < 3; x++)
7110 content_array[i][x][y] = level->yamyam_content[i].e[x][y];
7112 else if (element == EL_BD_AMOEBA)
7118 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
7119 for (y = 0; y < 3; y++)
7120 for (x = 0; x < 3; x++)
7121 content_array[i][x][y] = EL_EMPTY;
7122 content_array[0][0][0] = level->amoeba_content;
7126 // chunk header already written -- write empty chunk data
7127 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_SIZE);
7129 Warn("cannot save content for element '%d'", element);
7134 putFile16BitBE(file, element);
7135 putFile8Bit(file, num_contents);
7136 putFile8Bit(file, content_xsize);
7137 putFile8Bit(file, content_ysize);
7139 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_UNUSED);
7141 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
7142 for (y = 0; y < 3; y++)
7143 for (x = 0; x < 3; x++)
7144 putFile16BitBE(file, content_array[i][x][y]);
7148 #if ENABLE_HISTORIC_CHUNKS
7149 static int SaveLevel_CNT3(FILE *file, struct LevelInfo *level, int element)
7151 int envelope_nr = element - EL_ENVELOPE_1;
7152 int envelope_len = strlen(level->envelope_text[envelope_nr]) + 1;
7156 chunk_size += putFile16BitBE(file, element);
7157 chunk_size += putFile16BitBE(file, envelope_len);
7158 chunk_size += putFile8Bit(file, level->envelope_xsize[envelope_nr]);
7159 chunk_size += putFile8Bit(file, level->envelope_ysize[envelope_nr]);
7161 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT3_UNUSED);
7162 chunk_size += LEVEL_CHUNK_CNT3_UNUSED;
7164 for (i = 0; i < envelope_len; i++)
7165 chunk_size += putFile8Bit(file, level->envelope_text[envelope_nr][i]);
7171 #if ENABLE_HISTORIC_CHUNKS
7172 static void SaveLevel_CUS1(FILE *file, struct LevelInfo *level,
7173 int num_changed_custom_elements)
7177 putFile16BitBE(file, num_changed_custom_elements);
7179 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7181 int element = EL_CUSTOM_START + i;
7183 struct ElementInfo *ei = &element_info[element];
7185 if (ei->properties[EP_BITFIELD_BASE_NR] != EP_BITMASK_DEFAULT)
7187 if (check < num_changed_custom_elements)
7189 putFile16BitBE(file, element);
7190 putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
7197 if (check != num_changed_custom_elements) // should not happen
7198 Warn("inconsistent number of custom element properties");
7202 #if ENABLE_HISTORIC_CHUNKS
7203 static void SaveLevel_CUS2(FILE *file, struct LevelInfo *level,
7204 int num_changed_custom_elements)
7208 putFile16BitBE(file, num_changed_custom_elements);
7210 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7212 int element = EL_CUSTOM_START + i;
7214 if (element_info[element].change->target_element != EL_EMPTY_SPACE)
7216 if (check < num_changed_custom_elements)
7218 putFile16BitBE(file, element);
7219 putFile16BitBE(file, element_info[element].change->target_element);
7226 if (check != num_changed_custom_elements) // should not happen
7227 Warn("inconsistent number of custom target elements");
7231 #if ENABLE_HISTORIC_CHUNKS
7232 static void SaveLevel_CUS3(FILE *file, struct LevelInfo *level,
7233 int num_changed_custom_elements)
7235 int i, j, x, y, check = 0;
7237 putFile16BitBE(file, num_changed_custom_elements);
7239 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7241 int element = EL_CUSTOM_START + i;
7242 struct ElementInfo *ei = &element_info[element];
7244 if (ei->modified_settings)
7246 if (check < num_changed_custom_elements)
7248 putFile16BitBE(file, element);
7250 for (j = 0; j < MAX_ELEMENT_NAME_LEN; j++)
7251 putFile8Bit(file, ei->description[j]);
7253 putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
7255 // some free bytes for future properties and padding
7256 WriteUnusedBytesToFile(file, 7);
7258 putFile8Bit(file, ei->use_gfx_element);
7259 putFile16BitBE(file, ei->gfx_element_initial);
7261 putFile8Bit(file, ei->collect_score_initial);
7262 putFile8Bit(file, ei->collect_count_initial);
7264 putFile16BitBE(file, ei->push_delay_fixed);
7265 putFile16BitBE(file, ei->push_delay_random);
7266 putFile16BitBE(file, ei->move_delay_fixed);
7267 putFile16BitBE(file, ei->move_delay_random);
7269 putFile16BitBE(file, ei->move_pattern);
7270 putFile8Bit(file, ei->move_direction_initial);
7271 putFile8Bit(file, ei->move_stepsize);
7273 for (y = 0; y < 3; y++)
7274 for (x = 0; x < 3; x++)
7275 putFile16BitBE(file, ei->content.e[x][y]);
7277 putFile32BitBE(file, ei->change->events);
7279 putFile16BitBE(file, ei->change->target_element);
7281 putFile16BitBE(file, ei->change->delay_fixed);
7282 putFile16BitBE(file, ei->change->delay_random);
7283 putFile16BitBE(file, ei->change->delay_frames);
7285 putFile16BitBE(file, ei->change->initial_trigger_element);
7287 putFile8Bit(file, ei->change->explode);
7288 putFile8Bit(file, ei->change->use_target_content);
7289 putFile8Bit(file, ei->change->only_if_complete);
7290 putFile8Bit(file, ei->change->use_random_replace);
7292 putFile8Bit(file, ei->change->random_percentage);
7293 putFile8Bit(file, ei->change->replace_when);
7295 for (y = 0; y < 3; y++)
7296 for (x = 0; x < 3; x++)
7297 putFile16BitBE(file, ei->change->content.e[x][y]);
7299 putFile8Bit(file, ei->slippery_type);
7301 // some free bytes for future properties and padding
7302 WriteUnusedBytesToFile(file, LEVEL_CPART_CUS3_UNUSED);
7309 if (check != num_changed_custom_elements) // should not happen
7310 Warn("inconsistent number of custom element properties");
7314 #if ENABLE_HISTORIC_CHUNKS
7315 static void SaveLevel_CUS4(FILE *file, struct LevelInfo *level, int element)
7317 struct ElementInfo *ei = &element_info[element];
7320 // ---------- custom element base property values (96 bytes) ----------------
7322 putFile16BitBE(file, element);
7324 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
7325 putFile8Bit(file, ei->description[i]);
7327 putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
7329 WriteUnusedBytesToFile(file, 4); // reserved for more base properties
7331 putFile8Bit(file, ei->num_change_pages);
7333 putFile16BitBE(file, ei->ce_value_fixed_initial);
7334 putFile16BitBE(file, ei->ce_value_random_initial);
7335 putFile8Bit(file, ei->use_last_ce_value);
7337 putFile8Bit(file, ei->use_gfx_element);
7338 putFile16BitBE(file, ei->gfx_element_initial);
7340 putFile8Bit(file, ei->collect_score_initial);
7341 putFile8Bit(file, ei->collect_count_initial);
7343 putFile8Bit(file, ei->drop_delay_fixed);
7344 putFile8Bit(file, ei->push_delay_fixed);
7345 putFile8Bit(file, ei->drop_delay_random);
7346 putFile8Bit(file, ei->push_delay_random);
7347 putFile16BitBE(file, ei->move_delay_fixed);
7348 putFile16BitBE(file, ei->move_delay_random);
7350 // bits 0 - 15 of "move_pattern" ...
7351 putFile16BitBE(file, ei->move_pattern & 0xffff);
7352 putFile8Bit(file, ei->move_direction_initial);
7353 putFile8Bit(file, ei->move_stepsize);
7355 putFile8Bit(file, ei->slippery_type);
7357 for (y = 0; y < 3; y++)
7358 for (x = 0; x < 3; x++)
7359 putFile16BitBE(file, ei->content.e[x][y]);
7361 putFile16BitBE(file, ei->move_enter_element);
7362 putFile16BitBE(file, ei->move_leave_element);
7363 putFile8Bit(file, ei->move_leave_type);
7365 // ... bits 16 - 31 of "move_pattern" (not nice, but downward compatible)
7366 putFile16BitBE(file, (ei->move_pattern >> 16) & 0xffff);
7368 putFile8Bit(file, ei->access_direction);
7370 putFile8Bit(file, ei->explosion_delay);
7371 putFile8Bit(file, ei->ignition_delay);
7372 putFile8Bit(file, ei->explosion_type);
7374 // some free bytes for future custom property values and padding
7375 WriteUnusedBytesToFile(file, 1);
7377 // ---------- change page property values (48 bytes) ------------------------
7379 for (i = 0; i < ei->num_change_pages; i++)
7381 struct ElementChangeInfo *change = &ei->change_page[i];
7382 unsigned int event_bits;
7384 // bits 0 - 31 of "has_event[]" ...
7386 for (j = 0; j < MIN(NUM_CHANGE_EVENTS, 32); j++)
7387 if (change->has_event[j])
7388 event_bits |= (1u << j);
7389 putFile32BitBE(file, event_bits);
7391 putFile16BitBE(file, change->target_element);
7393 putFile16BitBE(file, change->delay_fixed);
7394 putFile16BitBE(file, change->delay_random);
7395 putFile16BitBE(file, change->delay_frames);
7397 putFile16BitBE(file, change->initial_trigger_element);
7399 putFile8Bit(file, change->explode);
7400 putFile8Bit(file, change->use_target_content);
7401 putFile8Bit(file, change->only_if_complete);
7402 putFile8Bit(file, change->use_random_replace);
7404 putFile8Bit(file, change->random_percentage);
7405 putFile8Bit(file, change->replace_when);
7407 for (y = 0; y < 3; y++)
7408 for (x = 0; x < 3; x++)
7409 putFile16BitBE(file, change->target_content.e[x][y]);
7411 putFile8Bit(file, change->can_change);
7413 putFile8Bit(file, change->trigger_side);
7415 putFile8Bit(file, change->trigger_player);
7416 putFile8Bit(file, (change->trigger_page == CH_PAGE_ANY ? CH_PAGE_ANY_FILE :
7417 log_2(change->trigger_page)));
7419 putFile8Bit(file, change->has_action);
7420 putFile8Bit(file, change->action_type);
7421 putFile8Bit(file, change->action_mode);
7422 putFile16BitBE(file, change->action_arg);
7424 // ... bits 32 - 39 of "has_event[]" (not nice, but downward compatible)
7426 for (j = 32; j < NUM_CHANGE_EVENTS; j++)
7427 if (change->has_event[j])
7428 event_bits |= (1u << (j - 32));
7429 putFile8Bit(file, event_bits);
7434 #if ENABLE_HISTORIC_CHUNKS
7435 static void SaveLevel_GRP1(FILE *file, struct LevelInfo *level, int element)
7437 struct ElementInfo *ei = &element_info[element];
7438 struct ElementGroupInfo *group = ei->group;
7441 putFile16BitBE(file, element);
7443 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
7444 putFile8Bit(file, ei->description[i]);
7446 putFile8Bit(file, group->num_elements);
7448 putFile8Bit(file, ei->use_gfx_element);
7449 putFile16BitBE(file, ei->gfx_element_initial);
7451 putFile8Bit(file, group->choice_mode);
7453 // some free bytes for future values and padding
7454 WriteUnusedBytesToFile(file, 3);
7456 for (i = 0; i < MAX_ELEMENTS_IN_GROUP; i++)
7457 putFile16BitBE(file, group->element[i]);
7461 static int SaveLevel_MicroChunk(FILE *file, struct LevelFileConfigInfo *entry,
7462 boolean write_element)
7464 int save_type = entry->save_type;
7465 int data_type = entry->data_type;
7466 int conf_type = entry->conf_type;
7467 int byte_mask = conf_type & CONF_MASK_BYTES;
7468 int element = entry->element;
7469 int default_value = entry->default_value;
7471 boolean modified = FALSE;
7473 if (byte_mask != CONF_MASK_MULTI_BYTES)
7475 void *value_ptr = entry->value;
7476 int value = (data_type == TYPE_BOOLEAN ? *(boolean *)value_ptr :
7479 // check if any settings have been modified before saving them
7480 if (value != default_value)
7483 // do not save if explicitly told or if unmodified default settings
7484 if ((save_type == SAVE_CONF_NEVER) ||
7485 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
7489 num_bytes += putFile16BitBE(file, element);
7491 num_bytes += putFile8Bit(file, conf_type);
7492 num_bytes += (byte_mask == CONF_MASK_1_BYTE ? putFile8Bit (file, value) :
7493 byte_mask == CONF_MASK_2_BYTE ? putFile16BitBE(file, value) :
7494 byte_mask == CONF_MASK_4_BYTE ? putFile32BitBE(file, value) :
7497 else if (data_type == TYPE_STRING)
7499 char *default_string = entry->default_string;
7500 char *string = (char *)(entry->value);
7501 int string_length = strlen(string);
7504 // check if any settings have been modified before saving them
7505 if (!strEqual(string, default_string))
7508 // do not save if explicitly told or if unmodified default settings
7509 if ((save_type == SAVE_CONF_NEVER) ||
7510 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
7514 num_bytes += putFile16BitBE(file, element);
7516 num_bytes += putFile8Bit(file, conf_type);
7517 num_bytes += putFile16BitBE(file, string_length);
7519 for (i = 0; i < string_length; i++)
7520 num_bytes += putFile8Bit(file, string[i]);
7522 else if (data_type == TYPE_ELEMENT_LIST)
7524 int *element_array = (int *)(entry->value);
7525 int num_elements = *(int *)(entry->num_entities);
7528 // check if any settings have been modified before saving them
7529 for (i = 0; i < num_elements; i++)
7530 if (element_array[i] != default_value)
7533 // do not save if explicitly told or if unmodified default settings
7534 if ((save_type == SAVE_CONF_NEVER) ||
7535 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
7539 num_bytes += putFile16BitBE(file, element);
7541 num_bytes += putFile8Bit(file, conf_type);
7542 num_bytes += putFile16BitBE(file, num_elements * CONF_ELEMENT_NUM_BYTES);
7544 for (i = 0; i < num_elements; i++)
7545 num_bytes += putFile16BitBE(file, element_array[i]);
7547 else if (data_type == TYPE_CONTENT_LIST)
7549 struct Content *content = (struct Content *)(entry->value);
7550 int num_contents = *(int *)(entry->num_entities);
7553 // check if any settings have been modified before saving them
7554 for (i = 0; i < num_contents; i++)
7555 for (y = 0; y < 3; y++)
7556 for (x = 0; x < 3; x++)
7557 if (content[i].e[x][y] != default_value)
7560 // do not save if explicitly told or if unmodified default settings
7561 if ((save_type == SAVE_CONF_NEVER) ||
7562 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
7566 num_bytes += putFile16BitBE(file, element);
7568 num_bytes += putFile8Bit(file, conf_type);
7569 num_bytes += putFile16BitBE(file, num_contents * CONF_CONTENT_NUM_BYTES);
7571 for (i = 0; i < num_contents; i++)
7572 for (y = 0; y < 3; y++)
7573 for (x = 0; x < 3; x++)
7574 num_bytes += putFile16BitBE(file, content[i].e[x][y]);
7580 static int SaveLevel_INFO(FILE *file, struct LevelInfo *level)
7585 li = *level; // copy level data into temporary buffer
7587 for (i = 0; chunk_config_INFO[i].data_type != -1; i++)
7588 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_INFO[i], FALSE);
7593 static int SaveLevel_ELEM(FILE *file, struct LevelInfo *level)
7598 li = *level; // copy level data into temporary buffer
7600 for (i = 0; chunk_config_ELEM[i].data_type != -1; i++)
7601 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_ELEM[i], TRUE);
7606 static int SaveLevel_NOTE(FILE *file, struct LevelInfo *level, int element)
7608 int envelope_nr = element - EL_ENVELOPE_1;
7612 chunk_size += putFile16BitBE(file, element);
7614 // copy envelope data into temporary buffer
7615 xx_envelope = level->envelope[envelope_nr];
7617 for (i = 0; chunk_config_NOTE[i].data_type != -1; i++)
7618 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_NOTE[i], FALSE);
7623 static int SaveLevel_CUSX(FILE *file, struct LevelInfo *level, int element)
7625 struct ElementInfo *ei = &element_info[element];
7629 chunk_size += putFile16BitBE(file, element);
7631 xx_ei = *ei; // copy element data into temporary buffer
7633 // set default description string for this specific element
7634 strcpy(xx_default_description, getDefaultElementDescription(ei));
7636 for (i = 0; chunk_config_CUSX_base[i].data_type != -1; i++)
7637 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_CUSX_base[i], FALSE);
7639 for (i = 0; i < ei->num_change_pages; i++)
7641 struct ElementChangeInfo *change = &ei->change_page[i];
7643 xx_current_change_page = i;
7645 xx_change = *change; // copy change data into temporary buffer
7648 setEventBitsFromEventFlags(change);
7650 for (j = 0; chunk_config_CUSX_change[j].data_type != -1; j++)
7651 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_CUSX_change[j],
7658 static int SaveLevel_GRPX(FILE *file, struct LevelInfo *level, int element)
7660 struct ElementInfo *ei = &element_info[element];
7661 struct ElementGroupInfo *group = ei->group;
7665 chunk_size += putFile16BitBE(file, element);
7667 xx_ei = *ei; // copy element data into temporary buffer
7668 xx_group = *group; // copy group data into temporary buffer
7670 // set default description string for this specific element
7671 strcpy(xx_default_description, getDefaultElementDescription(ei));
7673 for (i = 0; chunk_config_GRPX[i].data_type != -1; i++)
7674 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_GRPX[i], FALSE);
7679 static int SaveLevel_EMPX(FILE *file, struct LevelInfo *level, int element)
7681 struct ElementInfo *ei = &element_info[element];
7685 chunk_size += putFile16BitBE(file, element);
7687 xx_ei = *ei; // copy element data into temporary buffer
7689 for (i = 0; chunk_config_EMPX[i].data_type != -1; i++)
7690 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_EMPX[i], FALSE);
7695 static void SaveLevelFromFilename(struct LevelInfo *level, char *filename,
7696 boolean save_as_template)
7702 if (!(file = fopen(filename, MODE_WRITE)))
7704 Warn("cannot save level file '%s'", filename);
7709 level->file_version = FILE_VERSION_ACTUAL;
7710 level->game_version = GAME_VERSION_ACTUAL;
7712 level->creation_date = getCurrentDate();
7714 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
7715 putFileChunkBE(file, "CAVE", CHUNK_SIZE_NONE);
7717 chunk_size = SaveLevel_VERS(NULL, level);
7718 putFileChunkBE(file, "VERS", chunk_size);
7719 SaveLevel_VERS(file, level);
7721 chunk_size = SaveLevel_DATE(NULL, level);
7722 putFileChunkBE(file, "DATE", chunk_size);
7723 SaveLevel_DATE(file, level);
7725 chunk_size = SaveLevel_NAME(NULL, level);
7726 putFileChunkBE(file, "NAME", chunk_size);
7727 SaveLevel_NAME(file, level);
7729 chunk_size = SaveLevel_AUTH(NULL, level);
7730 putFileChunkBE(file, "AUTH", chunk_size);
7731 SaveLevel_AUTH(file, level);
7733 chunk_size = SaveLevel_INFO(NULL, level);
7734 putFileChunkBE(file, "INFO", chunk_size);
7735 SaveLevel_INFO(file, level);
7737 chunk_size = SaveLevel_BODY(NULL, level);
7738 putFileChunkBE(file, "BODY", chunk_size);
7739 SaveLevel_BODY(file, level);
7741 chunk_size = SaveLevel_ELEM(NULL, level);
7742 if (chunk_size > LEVEL_CHUNK_ELEM_UNCHANGED) // save if changed
7744 putFileChunkBE(file, "ELEM", chunk_size);
7745 SaveLevel_ELEM(file, level);
7748 for (i = 0; i < NUM_ENVELOPES; i++)
7750 int element = EL_ENVELOPE_1 + i;
7752 chunk_size = SaveLevel_NOTE(NULL, level, element);
7753 if (chunk_size > LEVEL_CHUNK_NOTE_UNCHANGED) // save if changed
7755 putFileChunkBE(file, "NOTE", chunk_size);
7756 SaveLevel_NOTE(file, level, element);
7760 // if not using template level, check for non-default custom/group elements
7761 if (!level->use_custom_template || save_as_template)
7763 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7765 int element = EL_CUSTOM_START + i;
7767 chunk_size = SaveLevel_CUSX(NULL, level, element);
7768 if (chunk_size > LEVEL_CHUNK_CUSX_UNCHANGED) // save if changed
7770 putFileChunkBE(file, "CUSX", chunk_size);
7771 SaveLevel_CUSX(file, level, element);
7775 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
7777 int element = EL_GROUP_START + i;
7779 chunk_size = SaveLevel_GRPX(NULL, level, element);
7780 if (chunk_size > LEVEL_CHUNK_GRPX_UNCHANGED) // save if changed
7782 putFileChunkBE(file, "GRPX", chunk_size);
7783 SaveLevel_GRPX(file, level, element);
7787 for (i = 0; i < NUM_EMPTY_ELEMENTS_ALL; i++)
7789 int element = GET_EMPTY_ELEMENT(i);
7791 chunk_size = SaveLevel_EMPX(NULL, level, element);
7792 if (chunk_size > LEVEL_CHUNK_EMPX_UNCHANGED) // save if changed
7794 putFileChunkBE(file, "EMPX", chunk_size);
7795 SaveLevel_EMPX(file, level, element);
7802 SetFilePermissions(filename, PERMS_PRIVATE);
7805 void SaveLevel(int nr)
7807 char *filename = getDefaultLevelFilename(nr);
7809 SaveLevelFromFilename(&level, filename, FALSE);
7812 void SaveLevelTemplate(void)
7814 char *filename = getLocalLevelTemplateFilename();
7816 SaveLevelFromFilename(&level, filename, TRUE);
7819 boolean SaveLevelChecked(int nr)
7821 char *filename = getDefaultLevelFilename(nr);
7822 boolean new_level = !fileExists(filename);
7823 boolean level_saved = FALSE;
7825 if (new_level || Request("Save this level and kill the old?", REQ_ASK))
7830 Request("Level saved!", REQ_CONFIRM);
7838 void DumpLevel(struct LevelInfo *level)
7840 if (level->no_level_file || level->no_valid_file)
7842 Warn("cannot dump -- no valid level file found");
7848 Print("Level xxx (file version %08d, game version %08d)\n",
7849 level->file_version, level->game_version);
7852 Print("Level author: '%s'\n", level->author);
7853 Print("Level title: '%s'\n", level->name);
7855 Print("Playfield size: %d x %d\n", level->fieldx, level->fieldy);
7857 Print("Level time: %d seconds\n", level->time);
7858 Print("Gems needed: %d\n", level->gems_needed);
7860 Print("Time for magic wall: %d seconds\n", level->time_magic_wall);
7861 Print("Time for wheel: %d seconds\n", level->time_wheel);
7862 Print("Time for light: %d seconds\n", level->time_light);
7863 Print("Time for timegate: %d seconds\n", level->time_timegate);
7865 Print("Amoeba speed: %d\n", level->amoeba_speed);
7868 Print("EM style slippery gems: %s\n", (level->em_slippery_gems ? "yes" : "no"));
7869 Print("Player blocks last field: %s\n", (level->block_last_field ? "yes" : "no"));
7870 Print("SP player blocks last field: %s\n", (level->sp_block_last_field ? "yes" : "no"));
7871 Print("use spring bug: %s\n", (level->use_spring_bug ? "yes" : "no"));
7872 Print("use step counter: %s\n", (level->use_step_counter ? "yes" : "no"));
7873 Print("rate time over score: %s\n", (level->rate_time_over_score ? "yes" : "no"));
7879 for (i = 0; i < NUM_ENVELOPES; i++)
7881 char *text = level->envelope[i].text;
7882 int text_len = strlen(text);
7883 boolean has_text = FALSE;
7885 for (j = 0; j < text_len; j++)
7886 if (text[j] != ' ' && text[j] != '\n')
7892 Print("Envelope %d:\n'%s'\n", i + 1, text);
7900 void DumpLevels(void)
7902 static LevelDirTree *dumplevel_leveldir = NULL;
7904 dumplevel_leveldir = getTreeInfoFromIdentifier(leveldir_first,
7905 global.dumplevel_leveldir);
7907 if (dumplevel_leveldir == NULL)
7908 Fail("no such level identifier: '%s'", global.dumplevel_leveldir);
7910 if (global.dumplevel_level_nr < dumplevel_leveldir->first_level ||
7911 global.dumplevel_level_nr > dumplevel_leveldir->last_level)
7912 Fail("no such level number: %d", global.dumplevel_level_nr);
7914 leveldir_current = dumplevel_leveldir;
7916 LoadLevel(global.dumplevel_level_nr);
7923 // ============================================================================
7924 // tape file functions
7925 // ============================================================================
7927 static void setTapeInfoToDefaults(void)
7931 // always start with reliable default values (empty tape)
7934 // default values (also for pre-1.2 tapes) with only the first player
7935 tape.player_participates[0] = TRUE;
7936 for (i = 1; i < MAX_PLAYERS; i++)
7937 tape.player_participates[i] = FALSE;
7939 // at least one (default: the first) player participates in every tape
7940 tape.num_participating_players = 1;
7942 tape.property_bits = TAPE_PROPERTY_NONE;
7944 tape.level_nr = level_nr;
7946 tape.changed = FALSE;
7947 tape.solved = FALSE;
7949 tape.recording = FALSE;
7950 tape.playing = FALSE;
7951 tape.pausing = FALSE;
7953 tape.scr_fieldx = SCR_FIELDX_DEFAULT;
7954 tape.scr_fieldy = SCR_FIELDY_DEFAULT;
7956 tape.no_info_chunk = TRUE;
7957 tape.no_valid_file = FALSE;
7960 static int getTapePosSize(struct TapeInfo *tape)
7962 int tape_pos_size = 0;
7964 if (tape->use_key_actions)
7965 tape_pos_size += tape->num_participating_players;
7967 if (tape->use_mouse_actions)
7968 tape_pos_size += 3; // x and y position and mouse button mask
7970 tape_pos_size += 1; // tape action delay value
7972 return tape_pos_size;
7975 static void setTapeActionFlags(struct TapeInfo *tape, int value)
7977 tape->use_key_actions = FALSE;
7978 tape->use_mouse_actions = FALSE;
7980 if (value != TAPE_USE_MOUSE_ACTIONS_ONLY)
7981 tape->use_key_actions = TRUE;
7983 if (value != TAPE_USE_KEY_ACTIONS_ONLY)
7984 tape->use_mouse_actions = TRUE;
7987 static int getTapeActionValue(struct TapeInfo *tape)
7989 return (tape->use_key_actions &&
7990 tape->use_mouse_actions ? TAPE_USE_KEY_AND_MOUSE_ACTIONS :
7991 tape->use_key_actions ? TAPE_USE_KEY_ACTIONS_ONLY :
7992 tape->use_mouse_actions ? TAPE_USE_MOUSE_ACTIONS_ONLY :
7993 TAPE_ACTIONS_DEFAULT);
7996 static int LoadTape_VERS(File *file, int chunk_size, struct TapeInfo *tape)
7998 tape->file_version = getFileVersion(file);
7999 tape->game_version = getFileVersion(file);
8004 static int LoadTape_HEAD(File *file, int chunk_size, struct TapeInfo *tape)
8008 tape->random_seed = getFile32BitBE(file);
8009 tape->date = getFile32BitBE(file);
8010 tape->length = getFile32BitBE(file);
8012 // read header fields that are new since version 1.2
8013 if (tape->file_version >= FILE_VERSION_1_2)
8015 byte store_participating_players = getFile8Bit(file);
8018 // since version 1.2, tapes store which players participate in the tape
8019 tape->num_participating_players = 0;
8020 for (i = 0; i < MAX_PLAYERS; i++)
8022 tape->player_participates[i] = FALSE;
8024 if (store_participating_players & (1 << i))
8026 tape->player_participates[i] = TRUE;
8027 tape->num_participating_players++;
8031 setTapeActionFlags(tape, getFile8Bit(file));
8033 tape->property_bits = getFile8Bit(file);
8034 tape->solved = getFile8Bit(file);
8036 engine_version = getFileVersion(file);
8037 if (engine_version > 0)
8038 tape->engine_version = engine_version;
8040 tape->engine_version = tape->game_version;
8046 static int LoadTape_SCRN(File *file, int chunk_size, struct TapeInfo *tape)
8048 tape->scr_fieldx = getFile8Bit(file);
8049 tape->scr_fieldy = getFile8Bit(file);
8054 static int LoadTape_INFO(File *file, int chunk_size, struct TapeInfo *tape)
8056 char *level_identifier = NULL;
8057 int level_identifier_size;
8060 tape->no_info_chunk = FALSE;
8062 level_identifier_size = getFile16BitBE(file);
8064 level_identifier = checked_malloc(level_identifier_size);
8066 for (i = 0; i < level_identifier_size; i++)
8067 level_identifier[i] = getFile8Bit(file);
8069 strncpy(tape->level_identifier, level_identifier, MAX_FILENAME_LEN);
8070 tape->level_identifier[MAX_FILENAME_LEN] = '\0';
8072 checked_free(level_identifier);
8074 tape->level_nr = getFile16BitBE(file);
8076 chunk_size = 2 + level_identifier_size + 2;
8081 static int LoadTape_BODY(File *file, int chunk_size, struct TapeInfo *tape)
8084 int tape_pos_size = getTapePosSize(tape);
8085 int chunk_size_expected = tape_pos_size * tape->length;
8087 if (chunk_size_expected != chunk_size)
8089 ReadUnusedBytesFromFile(file, chunk_size);
8090 return chunk_size_expected;
8093 for (i = 0; i < tape->length; i++)
8095 if (i >= MAX_TAPE_LEN)
8097 Warn("tape truncated -- size exceeds maximum tape size %d",
8100 // tape too large; read and ignore remaining tape data from this chunk
8101 for (;i < tape->length; i++)
8102 ReadUnusedBytesFromFile(file, tape_pos_size);
8107 if (tape->use_key_actions)
8109 for (j = 0; j < MAX_PLAYERS; j++)
8111 tape->pos[i].action[j] = MV_NONE;
8113 if (tape->player_participates[j])
8114 tape->pos[i].action[j] = getFile8Bit(file);
8118 if (tape->use_mouse_actions)
8120 tape->pos[i].action[TAPE_ACTION_LX] = getFile8Bit(file);
8121 tape->pos[i].action[TAPE_ACTION_LY] = getFile8Bit(file);
8122 tape->pos[i].action[TAPE_ACTION_BUTTON] = getFile8Bit(file);
8125 tape->pos[i].delay = getFile8Bit(file);
8127 if (tape->file_version == FILE_VERSION_1_0)
8129 // eliminate possible diagonal moves in old tapes
8130 // this is only for backward compatibility
8132 byte joy_dir[4] = { JOY_LEFT, JOY_RIGHT, JOY_UP, JOY_DOWN };
8133 byte action = tape->pos[i].action[0];
8134 int k, num_moves = 0;
8136 for (k = 0; k<4; k++)
8138 if (action & joy_dir[k])
8140 tape->pos[i + num_moves].action[0] = joy_dir[k];
8142 tape->pos[i + num_moves].delay = 0;
8151 tape->length += num_moves;
8154 else if (tape->file_version < FILE_VERSION_2_0)
8156 // convert pre-2.0 tapes to new tape format
8158 if (tape->pos[i].delay > 1)
8161 tape->pos[i + 1] = tape->pos[i];
8162 tape->pos[i + 1].delay = 1;
8165 for (j = 0; j < MAX_PLAYERS; j++)
8166 tape->pos[i].action[j] = MV_NONE;
8167 tape->pos[i].delay--;
8174 if (checkEndOfFile(file))
8178 if (i != tape->length)
8179 chunk_size = tape_pos_size * i;
8184 static void LoadTape_SokobanSolution(char *filename)
8187 int move_delay = TILESIZE / level.initial_player_stepsize[0];
8189 if (!(file = openFile(filename, MODE_READ)))
8191 tape.no_valid_file = TRUE;
8196 while (!checkEndOfFile(file))
8198 unsigned char c = getByteFromFile(file);
8200 if (checkEndOfFile(file))
8207 tape.pos[tape.length].action[0] = MV_UP;
8208 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
8214 tape.pos[tape.length].action[0] = MV_DOWN;
8215 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
8221 tape.pos[tape.length].action[0] = MV_LEFT;
8222 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
8228 tape.pos[tape.length].action[0] = MV_RIGHT;
8229 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
8237 // ignore white-space characters
8241 tape.no_valid_file = TRUE;
8243 Warn("unsupported Sokoban solution file '%s' ['%d']", filename, c);
8251 if (tape.no_valid_file)
8254 tape.length_frames = GetTapeLengthFrames();
8255 tape.length_seconds = GetTapeLengthSeconds();
8258 void LoadTapeFromFilename(char *filename)
8260 char cookie[MAX_LINE_LEN];
8261 char chunk_name[CHUNK_ID_LEN + 1];
8265 // always start with reliable default values
8266 setTapeInfoToDefaults();
8268 if (strSuffix(filename, ".sln"))
8270 LoadTape_SokobanSolution(filename);
8275 if (!(file = openFile(filename, MODE_READ)))
8277 tape.no_valid_file = TRUE;
8282 getFileChunkBE(file, chunk_name, NULL);
8283 if (strEqual(chunk_name, "RND1"))
8285 getFile32BitBE(file); // not used
8287 getFileChunkBE(file, chunk_name, NULL);
8288 if (!strEqual(chunk_name, "TAPE"))
8290 tape.no_valid_file = TRUE;
8292 Warn("unknown format of tape file '%s'", filename);
8299 else // check for pre-2.0 file format with cookie string
8301 strcpy(cookie, chunk_name);
8302 if (getStringFromFile(file, &cookie[4], MAX_LINE_LEN - 4) == NULL)
8304 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
8305 cookie[strlen(cookie) - 1] = '\0';
8307 if (!checkCookieString(cookie, TAPE_COOKIE_TMPL))
8309 tape.no_valid_file = TRUE;
8311 Warn("unknown format of tape file '%s'", filename);
8318 if ((tape.file_version = getFileVersionFromCookieString(cookie)) == -1)
8320 tape.no_valid_file = TRUE;
8322 Warn("unsupported version of tape file '%s'", filename);
8329 // pre-2.0 tape files have no game version, so use file version here
8330 tape.game_version = tape.file_version;
8333 if (tape.file_version < FILE_VERSION_1_2)
8335 // tape files from versions before 1.2.0 without chunk structure
8336 LoadTape_HEAD(file, TAPE_CHUNK_HEAD_SIZE, &tape);
8337 LoadTape_BODY(file, 2 * tape.length, &tape);
8345 int (*loader)(File *, int, struct TapeInfo *);
8349 { "VERS", TAPE_CHUNK_VERS_SIZE, LoadTape_VERS },
8350 { "HEAD", TAPE_CHUNK_HEAD_SIZE, LoadTape_HEAD },
8351 { "SCRN", TAPE_CHUNK_SCRN_SIZE, LoadTape_SCRN },
8352 { "INFO", -1, LoadTape_INFO },
8353 { "BODY", -1, LoadTape_BODY },
8357 while (getFileChunkBE(file, chunk_name, &chunk_size))
8361 while (chunk_info[i].name != NULL &&
8362 !strEqual(chunk_name, chunk_info[i].name))
8365 if (chunk_info[i].name == NULL)
8367 Warn("unknown chunk '%s' in tape file '%s'",
8368 chunk_name, filename);
8370 ReadUnusedBytesFromFile(file, chunk_size);
8372 else if (chunk_info[i].size != -1 &&
8373 chunk_info[i].size != chunk_size)
8375 Warn("wrong size (%d) of chunk '%s' in tape file '%s'",
8376 chunk_size, chunk_name, filename);
8378 ReadUnusedBytesFromFile(file, chunk_size);
8382 // call function to load this tape chunk
8383 int chunk_size_expected =
8384 (chunk_info[i].loader)(file, chunk_size, &tape);
8386 // the size of some chunks cannot be checked before reading other
8387 // chunks first (like "HEAD" and "BODY") that contain some header
8388 // information, so check them here
8389 if (chunk_size_expected != chunk_size)
8391 Warn("wrong size (%d) of chunk '%s' in tape file '%s'",
8392 chunk_size, chunk_name, filename);
8400 tape.length_frames = GetTapeLengthFrames();
8401 tape.length_seconds = GetTapeLengthSeconds();
8404 Debug("files:LoadTapeFromFilename", "tape file version: %d",
8406 Debug("files:LoadTapeFromFilename", "tape game version: %d",
8408 Debug("files:LoadTapeFromFilename", "tape engine version: %d",
8409 tape.engine_version);
8413 void LoadTape(int nr)
8415 char *filename = getTapeFilename(nr);
8417 LoadTapeFromFilename(filename);
8420 void LoadSolutionTape(int nr)
8422 char *filename = getSolutionTapeFilename(nr);
8424 LoadTapeFromFilename(filename);
8426 if (TAPE_IS_EMPTY(tape) &&
8427 level.game_engine_type == GAME_ENGINE_TYPE_SP &&
8428 level.native_sp_level->demo.is_available)
8429 CopyNativeTape_SP_to_RND(&level);
8432 void LoadScoreTape(char *score_tape_basename, int nr)
8434 char *filename = getScoreTapeFilename(score_tape_basename, nr);
8436 LoadTapeFromFilename(filename);
8439 void LoadScoreCacheTape(char *score_tape_basename, int nr)
8441 char *filename = getScoreCacheTapeFilename(score_tape_basename, nr);
8443 LoadTapeFromFilename(filename);
8446 static boolean checkSaveTape_SCRN(struct TapeInfo *tape)
8448 // chunk required for team mode tapes with non-default screen size
8449 return (tape->num_participating_players > 1 &&
8450 (tape->scr_fieldx != SCR_FIELDX_DEFAULT ||
8451 tape->scr_fieldy != SCR_FIELDY_DEFAULT));
8454 static void SaveTape_VERS(FILE *file, struct TapeInfo *tape)
8456 putFileVersion(file, tape->file_version);
8457 putFileVersion(file, tape->game_version);
8460 static void SaveTape_HEAD(FILE *file, struct TapeInfo *tape)
8463 byte store_participating_players = 0;
8465 // set bits for participating players for compact storage
8466 for (i = 0; i < MAX_PLAYERS; i++)
8467 if (tape->player_participates[i])
8468 store_participating_players |= (1 << i);
8470 putFile32BitBE(file, tape->random_seed);
8471 putFile32BitBE(file, tape->date);
8472 putFile32BitBE(file, tape->length);
8474 putFile8Bit(file, store_participating_players);
8476 putFile8Bit(file, getTapeActionValue(tape));
8478 putFile8Bit(file, tape->property_bits);
8479 putFile8Bit(file, tape->solved);
8481 putFileVersion(file, tape->engine_version);
8484 static void SaveTape_SCRN(FILE *file, struct TapeInfo *tape)
8486 putFile8Bit(file, tape->scr_fieldx);
8487 putFile8Bit(file, tape->scr_fieldy);
8490 static void SaveTape_INFO(FILE *file, struct TapeInfo *tape)
8492 int level_identifier_size = strlen(tape->level_identifier) + 1;
8495 putFile16BitBE(file, level_identifier_size);
8497 for (i = 0; i < level_identifier_size; i++)
8498 putFile8Bit(file, tape->level_identifier[i]);
8500 putFile16BitBE(file, tape->level_nr);
8503 static void SaveTape_BODY(FILE *file, struct TapeInfo *tape)
8507 for (i = 0; i < tape->length; i++)
8509 if (tape->use_key_actions)
8511 for (j = 0; j < MAX_PLAYERS; j++)
8512 if (tape->player_participates[j])
8513 putFile8Bit(file, tape->pos[i].action[j]);
8516 if (tape->use_mouse_actions)
8518 putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_LX]);
8519 putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_LY]);
8520 putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_BUTTON]);
8523 putFile8Bit(file, tape->pos[i].delay);
8527 void SaveTapeToFilename(char *filename)
8531 int info_chunk_size;
8532 int body_chunk_size;
8534 if (!(file = fopen(filename, MODE_WRITE)))
8536 Warn("cannot save level recording file '%s'", filename);
8541 tape_pos_size = getTapePosSize(&tape);
8543 info_chunk_size = 2 + (strlen(tape.level_identifier) + 1) + 2;
8544 body_chunk_size = tape_pos_size * tape.length;
8546 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
8547 putFileChunkBE(file, "TAPE", CHUNK_SIZE_NONE);
8549 putFileChunkBE(file, "VERS", TAPE_CHUNK_VERS_SIZE);
8550 SaveTape_VERS(file, &tape);
8552 putFileChunkBE(file, "HEAD", TAPE_CHUNK_HEAD_SIZE);
8553 SaveTape_HEAD(file, &tape);
8555 if (checkSaveTape_SCRN(&tape))
8557 putFileChunkBE(file, "SCRN", TAPE_CHUNK_SCRN_SIZE);
8558 SaveTape_SCRN(file, &tape);
8561 putFileChunkBE(file, "INFO", info_chunk_size);
8562 SaveTape_INFO(file, &tape);
8564 putFileChunkBE(file, "BODY", body_chunk_size);
8565 SaveTape_BODY(file, &tape);
8569 SetFilePermissions(filename, PERMS_PRIVATE);
8572 static void SaveTapeExt(char *filename)
8576 tape.file_version = FILE_VERSION_ACTUAL;
8577 tape.game_version = GAME_VERSION_ACTUAL;
8579 tape.num_participating_players = 0;
8581 // count number of participating players
8582 for (i = 0; i < MAX_PLAYERS; i++)
8583 if (tape.player_participates[i])
8584 tape.num_participating_players++;
8586 SaveTapeToFilename(filename);
8588 tape.changed = FALSE;
8591 void SaveTape(int nr)
8593 char *filename = getTapeFilename(nr);
8595 InitTapeDirectory(leveldir_current->subdir);
8597 SaveTapeExt(filename);
8600 void SaveScoreTape(int nr)
8602 char *filename = getScoreTapeFilename(tape.score_tape_basename, nr);
8604 // used instead of "leveldir_current->subdir" (for network games)
8605 InitScoreTapeDirectory(levelset.identifier, nr);
8607 SaveTapeExt(filename);
8610 static boolean SaveTapeCheckedExt(int nr, char *msg_replace, char *msg_saved,
8611 unsigned int req_state_added)
8613 char *filename = getTapeFilename(nr);
8614 boolean new_tape = !fileExists(filename);
8615 boolean tape_saved = FALSE;
8617 if (new_tape || Request(msg_replace, REQ_ASK | req_state_added))
8622 Request(msg_saved, REQ_CONFIRM | req_state_added);
8630 boolean SaveTapeChecked(int nr)
8632 return SaveTapeCheckedExt(nr, "Replace old tape?", "Tape saved!", 0);
8635 boolean SaveTapeChecked_LevelSolved(int nr)
8637 return SaveTapeCheckedExt(nr, "Level solved! Replace old tape?",
8638 "Level solved! Tape saved!", REQ_STAY_OPEN);
8641 void DumpTape(struct TapeInfo *tape)
8643 int tape_frame_counter;
8646 if (tape->no_valid_file)
8648 Warn("cannot dump -- no valid tape file found");
8655 Print("Tape of Level %03d (file version %08d, game version %08d)\n",
8656 tape->level_nr, tape->file_version, tape->game_version);
8657 Print(" (effective engine version %08d)\n",
8658 tape->engine_version);
8659 Print("Level series identifier: '%s'\n", tape->level_identifier);
8661 Print("Solution tape: %s\n",
8662 tape->solved ? "yes" :
8663 tape->game_version < VERSION_IDENT(4,3,2,3) ? "unknown" : "no");
8665 Print("Special tape properties: ");
8666 if (tape->property_bits == TAPE_PROPERTY_NONE)
8668 if (tape->property_bits & TAPE_PROPERTY_EM_RANDOM_BUG)
8669 Print("[em_random_bug]");
8670 if (tape->property_bits & TAPE_PROPERTY_GAME_SPEED)
8671 Print("[game_speed]");
8672 if (tape->property_bits & TAPE_PROPERTY_PAUSE_MODE)
8674 if (tape->property_bits & TAPE_PROPERTY_SINGLE_STEP)
8675 Print("[single_step]");
8676 if (tape->property_bits & TAPE_PROPERTY_SNAPSHOT)
8677 Print("[snapshot]");
8678 if (tape->property_bits & TAPE_PROPERTY_REPLAYED)
8679 Print("[replayed]");
8680 if (tape->property_bits & TAPE_PROPERTY_TAS_KEYS)
8681 Print("[tas_keys]");
8682 if (tape->property_bits & TAPE_PROPERTY_SMALL_GRAPHICS)
8683 Print("[small_graphics]");
8686 int year2 = tape->date / 10000;
8687 int year4 = (year2 < 70 ? 2000 + year2 : 1900 + year2);
8688 int month_index_raw = (tape->date / 100) % 100;
8689 int month_index = month_index_raw % 12; // prevent invalid index
8690 int month = month_index + 1;
8691 int day = tape->date % 100;
8693 Print("Tape date: %04d-%02d-%02d\n", year4, month, day);
8697 tape_frame_counter = 0;
8699 for (i = 0; i < tape->length; i++)
8701 if (i >= MAX_TAPE_LEN)
8706 for (j = 0; j < MAX_PLAYERS; j++)
8708 if (tape->player_participates[j])
8710 int action = tape->pos[i].action[j];
8712 Print("%d:%02x ", j, action);
8713 Print("[%c%c%c%c|%c%c] - ",
8714 (action & JOY_LEFT ? '<' : ' '),
8715 (action & JOY_RIGHT ? '>' : ' '),
8716 (action & JOY_UP ? '^' : ' '),
8717 (action & JOY_DOWN ? 'v' : ' '),
8718 (action & JOY_BUTTON_1 ? '1' : ' '),
8719 (action & JOY_BUTTON_2 ? '2' : ' '));
8723 Print("(%03d) ", tape->pos[i].delay);
8724 Print("[%05d]\n", tape_frame_counter);
8726 tape_frame_counter += tape->pos[i].delay;
8732 void DumpTapes(void)
8734 static LevelDirTree *dumptape_leveldir = NULL;
8736 dumptape_leveldir = getTreeInfoFromIdentifier(leveldir_first,
8737 global.dumptape_leveldir);
8739 if (dumptape_leveldir == NULL)
8740 Fail("no such level identifier: '%s'", global.dumptape_leveldir);
8742 if (global.dumptape_level_nr < dumptape_leveldir->first_level ||
8743 global.dumptape_level_nr > dumptape_leveldir->last_level)
8744 Fail("no such level number: %d", global.dumptape_level_nr);
8746 leveldir_current = dumptape_leveldir;
8748 if (options.mytapes)
8749 LoadTape(global.dumptape_level_nr);
8751 LoadSolutionTape(global.dumptape_level_nr);
8759 // ============================================================================
8760 // score file functions
8761 // ============================================================================
8763 static void setScoreInfoToDefaultsExt(struct ScoreInfo *scores)
8767 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
8769 strcpy(scores->entry[i].tape_basename, UNDEFINED_FILENAME);
8770 strcpy(scores->entry[i].name, EMPTY_PLAYER_NAME);
8771 scores->entry[i].score = 0;
8772 scores->entry[i].time = 0;
8774 scores->entry[i].id = -1;
8775 strcpy(scores->entry[i].tape_date, UNKNOWN_NAME);
8776 strcpy(scores->entry[i].platform, UNKNOWN_NAME);
8777 strcpy(scores->entry[i].version, UNKNOWN_NAME);
8778 strcpy(scores->entry[i].country_name, UNKNOWN_NAME);
8779 strcpy(scores->entry[i].country_code, "??");
8782 scores->num_entries = 0;
8783 scores->last_added = -1;
8784 scores->last_added_local = -1;
8786 scores->updated = FALSE;
8787 scores->uploaded = FALSE;
8788 scores->tape_downloaded = FALSE;
8789 scores->force_last_added = FALSE;
8791 // The following values are intentionally not reset here:
8795 // - continue_playing
8796 // - continue_on_return
8799 static void setScoreInfoToDefaults(void)
8801 setScoreInfoToDefaultsExt(&scores);
8804 static void setServerScoreInfoToDefaults(void)
8806 setScoreInfoToDefaultsExt(&server_scores);
8809 static void LoadScore_OLD(int nr)
8812 char *filename = getScoreFilename(nr);
8813 char cookie[MAX_LINE_LEN];
8814 char line[MAX_LINE_LEN];
8818 if (!(file = fopen(filename, MODE_READ)))
8821 // check file identifier
8822 if (fgets(cookie, MAX_LINE_LEN, file) == NULL)
8824 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
8825 cookie[strlen(cookie) - 1] = '\0';
8827 if (!checkCookieString(cookie, SCORE_COOKIE_TMPL))
8829 Warn("unknown format of score file '%s'", filename);
8836 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
8838 if (fscanf(file, "%d", &scores.entry[i].score) == EOF)
8839 Warn("fscanf() failed; %s", strerror(errno));
8841 if (fgets(line, MAX_LINE_LEN, file) == NULL)
8844 if (strlen(line) > 0 && line[strlen(line) - 1] == '\n')
8845 line[strlen(line) - 1] = '\0';
8847 for (line_ptr = line; *line_ptr; line_ptr++)
8849 if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != '\0')
8851 strncpy(scores.entry[i].name, line_ptr, MAX_PLAYER_NAME_LEN);
8852 scores.entry[i].name[MAX_PLAYER_NAME_LEN] = '\0';
8861 static void ConvertScore_OLD(void)
8863 // only convert score to time for levels that rate playing time over score
8864 if (!level.rate_time_over_score)
8867 // convert old score to playing time for score-less levels (like Supaplex)
8868 int time_final_max = 999;
8871 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
8873 int score = scores.entry[i].score;
8875 if (score > 0 && score < time_final_max)
8876 scores.entry[i].time = (time_final_max - score - 1) * FRAMES_PER_SECOND;
8880 static int LoadScore_VERS(File *file, int chunk_size, struct ScoreInfo *scores)
8882 scores->file_version = getFileVersion(file);
8883 scores->game_version = getFileVersion(file);
8888 static int LoadScore_INFO(File *file, int chunk_size, struct ScoreInfo *scores)
8890 char *level_identifier = NULL;
8891 int level_identifier_size;
8894 level_identifier_size = getFile16BitBE(file);
8896 level_identifier = checked_malloc(level_identifier_size);
8898 for (i = 0; i < level_identifier_size; i++)
8899 level_identifier[i] = getFile8Bit(file);
8901 strncpy(scores->level_identifier, level_identifier, MAX_FILENAME_LEN);
8902 scores->level_identifier[MAX_FILENAME_LEN] = '\0';
8904 checked_free(level_identifier);
8906 scores->level_nr = getFile16BitBE(file);
8907 scores->num_entries = getFile16BitBE(file);
8909 chunk_size = 2 + level_identifier_size + 2 + 2;
8914 static int LoadScore_NAME(File *file, int chunk_size, struct ScoreInfo *scores)
8918 for (i = 0; i < scores->num_entries; i++)
8920 for (j = 0; j < MAX_PLAYER_NAME_LEN; j++)
8921 scores->entry[i].name[j] = getFile8Bit(file);
8923 scores->entry[i].name[MAX_PLAYER_NAME_LEN] = '\0';
8926 chunk_size = scores->num_entries * MAX_PLAYER_NAME_LEN;
8931 static int LoadScore_SCOR(File *file, int chunk_size, struct ScoreInfo *scores)
8935 for (i = 0; i < scores->num_entries; i++)
8936 scores->entry[i].score = getFile16BitBE(file);
8938 chunk_size = scores->num_entries * 2;
8943 static int LoadScore_SC4R(File *file, int chunk_size, struct ScoreInfo *scores)
8947 for (i = 0; i < scores->num_entries; i++)
8948 scores->entry[i].score = getFile32BitBE(file);
8950 chunk_size = scores->num_entries * 4;
8955 static int LoadScore_TIME(File *file, int chunk_size, struct ScoreInfo *scores)
8959 for (i = 0; i < scores->num_entries; i++)
8960 scores->entry[i].time = getFile32BitBE(file);
8962 chunk_size = scores->num_entries * 4;
8967 static int LoadScore_TAPE(File *file, int chunk_size, struct ScoreInfo *scores)
8971 for (i = 0; i < scores->num_entries; i++)
8973 for (j = 0; j < MAX_SCORE_TAPE_BASENAME_LEN; j++)
8974 scores->entry[i].tape_basename[j] = getFile8Bit(file);
8976 scores->entry[i].tape_basename[MAX_SCORE_TAPE_BASENAME_LEN] = '\0';
8979 chunk_size = scores->num_entries * MAX_SCORE_TAPE_BASENAME_LEN;
8984 void LoadScore(int nr)
8986 char *filename = getScoreFilename(nr);
8987 char cookie[MAX_LINE_LEN];
8988 char chunk_name[CHUNK_ID_LEN + 1];
8990 boolean old_score_file_format = FALSE;
8993 // always start with reliable default values
8994 setScoreInfoToDefaults();
8996 if (!(file = openFile(filename, MODE_READ)))
8999 getFileChunkBE(file, chunk_name, NULL);
9000 if (strEqual(chunk_name, "RND1"))
9002 getFile32BitBE(file); // not used
9004 getFileChunkBE(file, chunk_name, NULL);
9005 if (!strEqual(chunk_name, "SCOR"))
9007 Warn("unknown format of score file '%s'", filename);
9014 else // check for old file format with cookie string
9016 strcpy(cookie, chunk_name);
9017 if (getStringFromFile(file, &cookie[4], MAX_LINE_LEN - 4) == NULL)
9019 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
9020 cookie[strlen(cookie) - 1] = '\0';
9022 if (!checkCookieString(cookie, SCORE_COOKIE_TMPL))
9024 Warn("unknown format of score file '%s'", filename);
9031 old_score_file_format = TRUE;
9034 if (old_score_file_format)
9036 // score files from versions before 4.2.4.0 without chunk structure
9039 // convert score to time, if possible (mainly for Supaplex levels)
9048 int (*loader)(File *, int, struct ScoreInfo *);
9052 { "VERS", SCORE_CHUNK_VERS_SIZE, LoadScore_VERS },
9053 { "INFO", -1, LoadScore_INFO },
9054 { "NAME", -1, LoadScore_NAME },
9055 { "SCOR", -1, LoadScore_SCOR },
9056 { "SC4R", -1, LoadScore_SC4R },
9057 { "TIME", -1, LoadScore_TIME },
9058 { "TAPE", -1, LoadScore_TAPE },
9063 while (getFileChunkBE(file, chunk_name, &chunk_size))
9067 while (chunk_info[i].name != NULL &&
9068 !strEqual(chunk_name, chunk_info[i].name))
9071 if (chunk_info[i].name == NULL)
9073 Warn("unknown chunk '%s' in score file '%s'",
9074 chunk_name, filename);
9076 ReadUnusedBytesFromFile(file, chunk_size);
9078 else if (chunk_info[i].size != -1 &&
9079 chunk_info[i].size != chunk_size)
9081 Warn("wrong size (%d) of chunk '%s' in score file '%s'",
9082 chunk_size, chunk_name, filename);
9084 ReadUnusedBytesFromFile(file, chunk_size);
9088 // call function to load this score chunk
9089 int chunk_size_expected =
9090 (chunk_info[i].loader)(file, chunk_size, &scores);
9092 // the size of some chunks cannot be checked before reading other
9093 // chunks first (like "HEAD" and "BODY") that contain some header
9094 // information, so check them here
9095 if (chunk_size_expected != chunk_size)
9097 Warn("wrong size (%d) of chunk '%s' in score file '%s'",
9098 chunk_size, chunk_name, filename);
9107 #if ENABLE_HISTORIC_CHUNKS
9108 void SaveScore_OLD(int nr)
9111 char *filename = getScoreFilename(nr);
9114 // used instead of "leveldir_current->subdir" (for network games)
9115 InitScoreDirectory(levelset.identifier);
9117 if (!(file = fopen(filename, MODE_WRITE)))
9119 Warn("cannot save score for level %d", nr);
9124 fprintf(file, "%s\n\n", SCORE_COOKIE);
9126 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9127 fprintf(file, "%d %s\n", scores.entry[i].score, scores.entry[i].name);
9131 SetFilePermissions(filename, PERMS_PRIVATE);
9135 static void SaveScore_VERS(FILE *file, struct ScoreInfo *scores)
9137 putFileVersion(file, scores->file_version);
9138 putFileVersion(file, scores->game_version);
9141 static void SaveScore_INFO(FILE *file, struct ScoreInfo *scores)
9143 int level_identifier_size = strlen(scores->level_identifier) + 1;
9146 putFile16BitBE(file, level_identifier_size);
9148 for (i = 0; i < level_identifier_size; i++)
9149 putFile8Bit(file, scores->level_identifier[i]);
9151 putFile16BitBE(file, scores->level_nr);
9152 putFile16BitBE(file, scores->num_entries);
9155 static void SaveScore_NAME(FILE *file, struct ScoreInfo *scores)
9159 for (i = 0; i < scores->num_entries; i++)
9161 int name_size = strlen(scores->entry[i].name);
9163 for (j = 0; j < MAX_PLAYER_NAME_LEN; j++)
9164 putFile8Bit(file, (j < name_size ? scores->entry[i].name[j] : 0));
9168 static void SaveScore_SCOR(FILE *file, struct ScoreInfo *scores)
9172 for (i = 0; i < scores->num_entries; i++)
9173 putFile16BitBE(file, scores->entry[i].score);
9176 static void SaveScore_SC4R(FILE *file, struct ScoreInfo *scores)
9180 for (i = 0; i < scores->num_entries; i++)
9181 putFile32BitBE(file, scores->entry[i].score);
9184 static void SaveScore_TIME(FILE *file, struct ScoreInfo *scores)
9188 for (i = 0; i < scores->num_entries; i++)
9189 putFile32BitBE(file, scores->entry[i].time);
9192 static void SaveScore_TAPE(FILE *file, struct ScoreInfo *scores)
9196 for (i = 0; i < scores->num_entries; i++)
9198 int size = strlen(scores->entry[i].tape_basename);
9200 for (j = 0; j < MAX_SCORE_TAPE_BASENAME_LEN; j++)
9201 putFile8Bit(file, (j < size ? scores->entry[i].tape_basename[j] : 0));
9205 static void SaveScoreToFilename(char *filename)
9208 int info_chunk_size;
9209 int name_chunk_size;
9210 int scor_chunk_size;
9211 int sc4r_chunk_size;
9212 int time_chunk_size;
9213 int tape_chunk_size;
9214 boolean has_large_score_values;
9217 if (!(file = fopen(filename, MODE_WRITE)))
9219 Warn("cannot save score file '%s'", filename);
9224 info_chunk_size = 2 + (strlen(scores.level_identifier) + 1) + 2 + 2;
9225 name_chunk_size = scores.num_entries * MAX_PLAYER_NAME_LEN;
9226 scor_chunk_size = scores.num_entries * 2;
9227 sc4r_chunk_size = scores.num_entries * 4;
9228 time_chunk_size = scores.num_entries * 4;
9229 tape_chunk_size = scores.num_entries * MAX_SCORE_TAPE_BASENAME_LEN;
9231 has_large_score_values = FALSE;
9232 for (i = 0; i < scores.num_entries; i++)
9233 if (scores.entry[i].score > 0xffff)
9234 has_large_score_values = TRUE;
9236 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
9237 putFileChunkBE(file, "SCOR", CHUNK_SIZE_NONE);
9239 putFileChunkBE(file, "VERS", SCORE_CHUNK_VERS_SIZE);
9240 SaveScore_VERS(file, &scores);
9242 putFileChunkBE(file, "INFO", info_chunk_size);
9243 SaveScore_INFO(file, &scores);
9245 putFileChunkBE(file, "NAME", name_chunk_size);
9246 SaveScore_NAME(file, &scores);
9248 if (has_large_score_values)
9250 putFileChunkBE(file, "SC4R", sc4r_chunk_size);
9251 SaveScore_SC4R(file, &scores);
9255 putFileChunkBE(file, "SCOR", scor_chunk_size);
9256 SaveScore_SCOR(file, &scores);
9259 putFileChunkBE(file, "TIME", time_chunk_size);
9260 SaveScore_TIME(file, &scores);
9262 putFileChunkBE(file, "TAPE", tape_chunk_size);
9263 SaveScore_TAPE(file, &scores);
9267 SetFilePermissions(filename, PERMS_PRIVATE);
9270 void SaveScore(int nr)
9272 char *filename = getScoreFilename(nr);
9275 // used instead of "leveldir_current->subdir" (for network games)
9276 InitScoreDirectory(levelset.identifier);
9278 scores.file_version = FILE_VERSION_ACTUAL;
9279 scores.game_version = GAME_VERSION_ACTUAL;
9281 strncpy(scores.level_identifier, levelset.identifier, MAX_FILENAME_LEN);
9282 scores.level_identifier[MAX_FILENAME_LEN] = '\0';
9283 scores.level_nr = level_nr;
9285 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9286 if (scores.entry[i].score == 0 &&
9287 scores.entry[i].time == 0 &&
9288 strEqual(scores.entry[i].name, EMPTY_PLAYER_NAME))
9291 scores.num_entries = i;
9293 if (scores.num_entries == 0)
9296 SaveScoreToFilename(filename);
9299 static void LoadServerScoreFromCache(int nr)
9301 struct ScoreEntry score_entry;
9310 { &score_entry.score, FALSE, 0 },
9311 { &score_entry.time, FALSE, 0 },
9312 { score_entry.name, TRUE, MAX_PLAYER_NAME_LEN },
9313 { score_entry.tape_basename, TRUE, MAX_FILENAME_LEN },
9314 { score_entry.tape_date, TRUE, MAX_ISO_DATE_LEN },
9315 { &score_entry.id, FALSE, 0 },
9316 { score_entry.platform, TRUE, MAX_PLATFORM_TEXT_LEN },
9317 { score_entry.version, TRUE, MAX_VERSION_TEXT_LEN },
9318 { score_entry.country_code, TRUE, MAX_COUNTRY_CODE_LEN },
9319 { score_entry.country_name, TRUE, MAX_COUNTRY_NAME_LEN },
9323 char *filename = getScoreCacheFilename(nr);
9324 SetupFileHash *score_hash = loadSetupFileHash(filename);
9327 server_scores.num_entries = 0;
9329 if (score_hash == NULL)
9332 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9334 score_entry = server_scores.entry[i];
9336 for (j = 0; score_mapping[j].value != NULL; j++)
9340 sprintf(token, "%02d.%d", i, j);
9342 char *value = getHashEntry(score_hash, token);
9347 if (score_mapping[j].is_string)
9349 char *score_value = (char *)score_mapping[j].value;
9350 int value_size = score_mapping[j].string_size;
9352 strncpy(score_value, value, value_size);
9353 score_value[value_size] = '\0';
9357 int *score_value = (int *)score_mapping[j].value;
9359 *score_value = atoi(value);
9362 server_scores.num_entries = i + 1;
9365 server_scores.entry[i] = score_entry;
9368 freeSetupFileHash(score_hash);
9371 void LoadServerScore(int nr, boolean download_score)
9373 if (!setup.use_api_server)
9376 // always start with reliable default values
9377 setServerScoreInfoToDefaults();
9379 // 1st step: load server scores from cache file (which may not exist)
9380 // (this should prevent reading it while the thread is writing to it)
9381 LoadServerScoreFromCache(nr);
9383 if (download_score && runtime.use_api_server)
9385 // 2nd step: download server scores from score server to cache file
9386 // (as thread, as it might time out if the server is not reachable)
9387 ApiGetScoreAsThread(nr);
9391 void PrepareScoreTapesForUpload(char *leveldir_subdir)
9393 MarkTapeDirectoryUploadsAsIncomplete(leveldir_subdir);
9395 // if score tape not uploaded, ask for uploading missing tapes later
9396 if (!setup.has_remaining_tapes)
9397 setup.ask_for_remaining_tapes = TRUE;
9399 setup.provide_uploading_tapes = TRUE;
9400 setup.has_remaining_tapes = TRUE;
9402 SaveSetup_ServerSetup();
9405 void SaveServerScore(int nr, boolean tape_saved)
9407 if (!runtime.use_api_server)
9409 PrepareScoreTapesForUpload(leveldir_current->subdir);
9414 ApiAddScoreAsThread(nr, tape_saved, NULL);
9417 void SaveServerScoreFromFile(int nr, boolean tape_saved,
9418 char *score_tape_filename)
9420 if (!runtime.use_api_server)
9423 ApiAddScoreAsThread(nr, tape_saved, score_tape_filename);
9426 void LoadLocalAndServerScore(int nr, boolean download_score)
9428 int last_added_local = scores.last_added_local;
9429 boolean force_last_added = scores.force_last_added;
9431 // needed if only showing server scores
9432 setScoreInfoToDefaults();
9434 if (!strEqual(setup.scores_in_highscore_list, STR_SCORES_TYPE_SERVER_ONLY))
9437 // restore last added local score entry (before merging server scores)
9438 scores.last_added = scores.last_added_local = last_added_local;
9440 if (setup.use_api_server &&
9441 !strEqual(setup.scores_in_highscore_list, STR_SCORES_TYPE_LOCAL_ONLY))
9443 // load server scores from cache file and trigger update from server
9444 LoadServerScore(nr, download_score);
9446 // merge local scores with scores from server
9450 if (force_last_added)
9451 scores.force_last_added = force_last_added;
9455 // ============================================================================
9456 // setup file functions
9457 // ============================================================================
9459 #define TOKEN_STR_PLAYER_PREFIX "player_"
9462 static struct TokenInfo global_setup_tokens[] =
9466 &setup.player_name, "player_name"
9470 &setup.multiple_users, "multiple_users"
9474 &setup.sound, "sound"
9478 &setup.sound_loops, "repeating_sound_loops"
9482 &setup.sound_music, "background_music"
9486 &setup.sound_simple, "simple_sound_effects"
9490 &setup.toons, "toons"
9494 &setup.global_animations, "global_animations"
9498 &setup.scroll_delay, "scroll_delay"
9502 &setup.forced_scroll_delay, "forced_scroll_delay"
9506 &setup.scroll_delay_value, "scroll_delay_value"
9510 &setup.engine_snapshot_mode, "engine_snapshot_mode"
9514 &setup.engine_snapshot_memory, "engine_snapshot_memory"
9518 &setup.fade_screens, "fade_screens"
9522 &setup.autorecord, "automatic_tape_recording"
9526 &setup.autorecord_after_replay, "autorecord_after_replay"
9530 &setup.auto_pause_on_start, "auto_pause_on_start"
9534 &setup.show_titlescreen, "show_titlescreen"
9538 &setup.quick_doors, "quick_doors"
9542 &setup.team_mode, "team_mode"
9546 &setup.handicap, "handicap"
9550 &setup.skip_levels, "skip_levels"
9554 &setup.increment_levels, "increment_levels"
9558 &setup.auto_play_next_level, "auto_play_next_level"
9562 &setup.count_score_after_game, "count_score_after_game"
9566 &setup.show_scores_after_game, "show_scores_after_game"
9570 &setup.time_limit, "time_limit"
9574 &setup.fullscreen, "fullscreen"
9578 &setup.window_scaling_percent, "window_scaling_percent"
9582 &setup.window_scaling_quality, "window_scaling_quality"
9586 &setup.screen_rendering_mode, "screen_rendering_mode"
9590 &setup.vsync_mode, "vsync_mode"
9594 &setup.ask_on_escape, "ask_on_escape"
9598 &setup.ask_on_escape_editor, "ask_on_escape_editor"
9602 &setup.ask_on_game_over, "ask_on_game_over"
9606 &setup.ask_on_quit_game, "ask_on_quit_game"
9610 &setup.ask_on_quit_program, "ask_on_quit_program"
9614 &setup.quick_switch, "quick_player_switch"
9618 &setup.input_on_focus, "input_on_focus"
9622 &setup.prefer_aga_graphics, "prefer_aga_graphics"
9626 &setup.prefer_lowpass_sounds, "prefer_lowpass_sounds"
9630 &setup.prefer_extra_panel_items, "prefer_extra_panel_items"
9634 &setup.game_speed_extended, "game_speed_extended"
9638 &setup.game_frame_delay, "game_frame_delay"
9642 &setup.sp_show_border_elements, "sp_show_border_elements"
9646 &setup.small_game_graphics, "small_game_graphics"
9650 &setup.show_load_save_buttons, "show_load_save_buttons"
9654 &setup.show_undo_redo_buttons, "show_undo_redo_buttons"
9658 &setup.scores_in_highscore_list, "scores_in_highscore_list"
9662 &setup.graphics_set, "graphics_set"
9666 &setup.sounds_set, "sounds_set"
9670 &setup.music_set, "music_set"
9674 &setup.override_level_graphics, "override_level_graphics"
9678 &setup.override_level_sounds, "override_level_sounds"
9682 &setup.override_level_music, "override_level_music"
9686 &setup.volume_simple, "volume_simple"
9690 &setup.volume_loops, "volume_loops"
9694 &setup.volume_music, "volume_music"
9698 &setup.network_mode, "network_mode"
9702 &setup.network_player_nr, "network_player"
9706 &setup.network_server_hostname, "network_server_hostname"
9710 &setup.touch.control_type, "touch.control_type"
9714 &setup.touch.move_distance, "touch.move_distance"
9718 &setup.touch.drop_distance, "touch.drop_distance"
9722 &setup.touch.transparency, "touch.transparency"
9726 &setup.touch.draw_outlined, "touch.draw_outlined"
9730 &setup.touch.draw_pressed, "touch.draw_pressed"
9734 &setup.touch.grid_xsize[0], "touch.virtual_buttons.0.xsize"
9738 &setup.touch.grid_ysize[0], "touch.virtual_buttons.0.ysize"
9742 &setup.touch.grid_xsize[1], "touch.virtual_buttons.1.xsize"
9746 &setup.touch.grid_ysize[1], "touch.virtual_buttons.1.ysize"
9750 &setup.touch.overlay_buttons, "touch.overlay_buttons"
9754 static struct TokenInfo auto_setup_tokens[] =
9758 &setup.auto_setup.editor_zoom_tilesize, "editor.zoom_tilesize"
9762 static struct TokenInfo server_setup_tokens[] =
9766 &setup.player_uuid, "player_uuid"
9770 &setup.player_version, "player_version"
9774 &setup.use_api_server, TEST_PREFIX "use_api_server"
9778 &setup.api_server_hostname, TEST_PREFIX "api_server_hostname"
9782 &setup.api_server_password, TEST_PREFIX "api_server_password"
9786 &setup.ask_for_uploading_tapes, TEST_PREFIX "ask_for_uploading_tapes"
9790 &setup.ask_for_remaining_tapes, TEST_PREFIX "ask_for_remaining_tapes"
9794 &setup.provide_uploading_tapes, TEST_PREFIX "provide_uploading_tapes"
9798 &setup.ask_for_using_api_server,TEST_PREFIX "ask_for_using_api_server"
9802 &setup.has_remaining_tapes, TEST_PREFIX "has_remaining_tapes"
9806 static struct TokenInfo editor_setup_tokens[] =
9810 &setup.editor.el_classic, "editor.el_classic"
9814 &setup.editor.el_custom, "editor.el_custom"
9818 &setup.editor.el_user_defined, "editor.el_user_defined"
9822 &setup.editor.el_dynamic, "editor.el_dynamic"
9826 &setup.editor.el_headlines, "editor.el_headlines"
9830 &setup.editor.show_element_token, "editor.show_element_token"
9834 &setup.editor.show_read_only_warning, "editor.show_read_only_warning"
9838 static struct TokenInfo editor_cascade_setup_tokens[] =
9842 &setup.editor_cascade.el_bd, "editor.cascade.el_bd"
9846 &setup.editor_cascade.el_em, "editor.cascade.el_em"
9850 &setup.editor_cascade.el_emc, "editor.cascade.el_emc"
9854 &setup.editor_cascade.el_rnd, "editor.cascade.el_rnd"
9858 &setup.editor_cascade.el_sb, "editor.cascade.el_sb"
9862 &setup.editor_cascade.el_sp, "editor.cascade.el_sp"
9866 &setup.editor_cascade.el_dc, "editor.cascade.el_dc"
9870 &setup.editor_cascade.el_dx, "editor.cascade.el_dx"
9874 &setup.editor_cascade.el_mm, "editor.cascade.el_mm"
9878 &setup.editor_cascade.el_df, "editor.cascade.el_df"
9882 &setup.editor_cascade.el_chars, "editor.cascade.el_chars"
9886 &setup.editor_cascade.el_steel_chars, "editor.cascade.el_steel_chars"
9890 &setup.editor_cascade.el_ce, "editor.cascade.el_ce"
9894 &setup.editor_cascade.el_ge, "editor.cascade.el_ge"
9898 &setup.editor_cascade.el_es, "editor.cascade.el_es"
9902 &setup.editor_cascade.el_ref, "editor.cascade.el_ref"
9906 &setup.editor_cascade.el_user, "editor.cascade.el_user"
9910 &setup.editor_cascade.el_dynamic, "editor.cascade.el_dynamic"
9914 static struct TokenInfo shortcut_setup_tokens[] =
9918 &setup.shortcut.save_game, "shortcut.save_game"
9922 &setup.shortcut.load_game, "shortcut.load_game"
9926 &setup.shortcut.restart_game, "shortcut.restart_game"
9930 &setup.shortcut.pause_before_end, "shortcut.pause_before_end"
9934 &setup.shortcut.toggle_pause, "shortcut.toggle_pause"
9938 &setup.shortcut.focus_player[0], "shortcut.focus_player_1"
9942 &setup.shortcut.focus_player[1], "shortcut.focus_player_2"
9946 &setup.shortcut.focus_player[2], "shortcut.focus_player_3"
9950 &setup.shortcut.focus_player[3], "shortcut.focus_player_4"
9954 &setup.shortcut.focus_player_all, "shortcut.focus_player_all"
9958 &setup.shortcut.tape_eject, "shortcut.tape_eject"
9962 &setup.shortcut.tape_extra, "shortcut.tape_extra"
9966 &setup.shortcut.tape_stop, "shortcut.tape_stop"
9970 &setup.shortcut.tape_pause, "shortcut.tape_pause"
9974 &setup.shortcut.tape_record, "shortcut.tape_record"
9978 &setup.shortcut.tape_play, "shortcut.tape_play"
9982 &setup.shortcut.sound_simple, "shortcut.sound_simple"
9986 &setup.shortcut.sound_loops, "shortcut.sound_loops"
9990 &setup.shortcut.sound_music, "shortcut.sound_music"
9994 &setup.shortcut.snap_left, "shortcut.snap_left"
9998 &setup.shortcut.snap_right, "shortcut.snap_right"
10002 &setup.shortcut.snap_up, "shortcut.snap_up"
10006 &setup.shortcut.snap_down, "shortcut.snap_down"
10010 static struct SetupInputInfo setup_input;
10011 static struct TokenInfo player_setup_tokens[] =
10015 &setup_input.use_joystick, ".use_joystick"
10019 &setup_input.joy.device_name, ".joy.device_name"
10023 &setup_input.joy.xleft, ".joy.xleft"
10027 &setup_input.joy.xmiddle, ".joy.xmiddle"
10031 &setup_input.joy.xright, ".joy.xright"
10035 &setup_input.joy.yupper, ".joy.yupper"
10039 &setup_input.joy.ymiddle, ".joy.ymiddle"
10043 &setup_input.joy.ylower, ".joy.ylower"
10047 &setup_input.joy.snap, ".joy.snap_field"
10051 &setup_input.joy.drop, ".joy.place_bomb"
10055 &setup_input.key.left, ".key.move_left"
10059 &setup_input.key.right, ".key.move_right"
10063 &setup_input.key.up, ".key.move_up"
10067 &setup_input.key.down, ".key.move_down"
10071 &setup_input.key.snap, ".key.snap_field"
10075 &setup_input.key.drop, ".key.place_bomb"
10079 static struct TokenInfo system_setup_tokens[] =
10083 &setup.system.sdl_renderdriver, "system.sdl_renderdriver"
10087 &setup.system.sdl_videodriver, "system.sdl_videodriver"
10091 &setup.system.sdl_audiodriver, "system.sdl_audiodriver"
10095 &setup.system.audio_fragment_size, "system.audio_fragment_size"
10099 static struct TokenInfo internal_setup_tokens[] =
10103 &setup.internal.program_title, "program_title"
10107 &setup.internal.program_version, "program_version"
10111 &setup.internal.program_author, "program_author"
10115 &setup.internal.program_email, "program_email"
10119 &setup.internal.program_website, "program_website"
10123 &setup.internal.program_copyright, "program_copyright"
10127 &setup.internal.program_company, "program_company"
10131 &setup.internal.program_icon_file, "program_icon_file"
10135 &setup.internal.default_graphics_set, "default_graphics_set"
10139 &setup.internal.default_sounds_set, "default_sounds_set"
10143 &setup.internal.default_music_set, "default_music_set"
10147 &setup.internal.fallback_graphics_file, "fallback_graphics_file"
10151 &setup.internal.fallback_sounds_file, "fallback_sounds_file"
10155 &setup.internal.fallback_music_file, "fallback_music_file"
10159 &setup.internal.default_level_series, "default_level_series"
10163 &setup.internal.default_window_width, "default_window_width"
10167 &setup.internal.default_window_height, "default_window_height"
10171 &setup.internal.choose_from_top_leveldir, "choose_from_top_leveldir"
10175 &setup.internal.show_scaling_in_title, "show_scaling_in_title"
10179 &setup.internal.create_user_levelset, "create_user_levelset"
10183 &setup.internal.info_screens_from_main, "info_screens_from_main"
10187 &setup.internal.menu_game, "menu_game"
10191 &setup.internal.menu_engines, "menu_engines"
10195 &setup.internal.menu_editor, "menu_editor"
10199 &setup.internal.menu_graphics, "menu_graphics"
10203 &setup.internal.menu_sound, "menu_sound"
10207 &setup.internal.menu_artwork, "menu_artwork"
10211 &setup.internal.menu_input, "menu_input"
10215 &setup.internal.menu_touch, "menu_touch"
10219 &setup.internal.menu_shortcuts, "menu_shortcuts"
10223 &setup.internal.menu_exit, "menu_exit"
10227 &setup.internal.menu_save_and_exit, "menu_save_and_exit"
10231 &setup.internal.menu_shortcuts_various, "menu_shortcuts_various"
10235 &setup.internal.menu_shortcuts_focus, "menu_shortcuts_focus"
10239 &setup.internal.menu_shortcuts_tape, "menu_shortcuts_tape"
10243 &setup.internal.menu_shortcuts_sound, "menu_shortcuts_sound"
10247 &setup.internal.menu_shortcuts_snap, "menu_shortcuts_snap"
10251 &setup.internal.info_title, "info_title"
10255 &setup.internal.info_elements, "info_elements"
10259 &setup.internal.info_music, "info_music"
10263 &setup.internal.info_credits, "info_credits"
10267 &setup.internal.info_program, "info_program"
10271 &setup.internal.info_version, "info_version"
10275 &setup.internal.info_levelset, "info_levelset"
10279 &setup.internal.info_exit, "info_exit"
10283 static struct TokenInfo debug_setup_tokens[] =
10287 &setup.debug.frame_delay[0], "debug.frame_delay_0"
10291 &setup.debug.frame_delay[1], "debug.frame_delay_1"
10295 &setup.debug.frame_delay[2], "debug.frame_delay_2"
10299 &setup.debug.frame_delay[3], "debug.frame_delay_3"
10303 &setup.debug.frame_delay[4], "debug.frame_delay_4"
10307 &setup.debug.frame_delay[5], "debug.frame_delay_5"
10311 &setup.debug.frame_delay[6], "debug.frame_delay_6"
10315 &setup.debug.frame_delay[7], "debug.frame_delay_7"
10319 &setup.debug.frame_delay[8], "debug.frame_delay_8"
10323 &setup.debug.frame_delay[9], "debug.frame_delay_9"
10327 &setup.debug.frame_delay_key[0], "debug.key.frame_delay_0"
10331 &setup.debug.frame_delay_key[1], "debug.key.frame_delay_1"
10335 &setup.debug.frame_delay_key[2], "debug.key.frame_delay_2"
10339 &setup.debug.frame_delay_key[3], "debug.key.frame_delay_3"
10343 &setup.debug.frame_delay_key[4], "debug.key.frame_delay_4"
10347 &setup.debug.frame_delay_key[5], "debug.key.frame_delay_5"
10351 &setup.debug.frame_delay_key[6], "debug.key.frame_delay_6"
10355 &setup.debug.frame_delay_key[7], "debug.key.frame_delay_7"
10359 &setup.debug.frame_delay_key[8], "debug.key.frame_delay_8"
10363 &setup.debug.frame_delay_key[9], "debug.key.frame_delay_9"
10367 &setup.debug.frame_delay_use_mod_key, "debug.frame_delay.use_mod_key"},
10370 &setup.debug.frame_delay_game_only, "debug.frame_delay.game_only"
10374 &setup.debug.show_frames_per_second, "debug.show_frames_per_second"
10378 &setup.debug.xsn_mode, "debug.xsn_mode"
10382 &setup.debug.xsn_percent, "debug.xsn_percent"
10386 static struct TokenInfo options_setup_tokens[] =
10390 &setup.options.verbose, "options.verbose"
10394 &setup.options.debug, "options.debug"
10398 &setup.options.debug_mode, "options.debug_mode"
10402 static void setSetupInfoToDefaults(struct SetupInfo *si)
10406 si->player_name = getStringCopy(getDefaultUserName(user.nr));
10408 si->multiple_users = TRUE;
10411 si->sound_loops = TRUE;
10412 si->sound_music = TRUE;
10413 si->sound_simple = TRUE;
10415 si->global_animations = TRUE;
10416 si->scroll_delay = TRUE;
10417 si->forced_scroll_delay = FALSE;
10418 si->scroll_delay_value = STD_SCROLL_DELAY;
10419 si->engine_snapshot_mode = getStringCopy(STR_SNAPSHOT_MODE_DEFAULT);
10420 si->engine_snapshot_memory = SNAPSHOT_MEMORY_DEFAULT;
10421 si->fade_screens = TRUE;
10422 si->autorecord = TRUE;
10423 si->autorecord_after_replay = TRUE;
10424 si->auto_pause_on_start = FALSE;
10425 si->show_titlescreen = TRUE;
10426 si->quick_doors = FALSE;
10427 si->team_mode = FALSE;
10428 si->handicap = TRUE;
10429 si->skip_levels = TRUE;
10430 si->increment_levels = TRUE;
10431 si->auto_play_next_level = TRUE;
10432 si->count_score_after_game = TRUE;
10433 si->show_scores_after_game = TRUE;
10434 si->time_limit = TRUE;
10435 si->fullscreen = FALSE;
10436 si->window_scaling_percent = STD_WINDOW_SCALING_PERCENT;
10437 si->window_scaling_quality = getStringCopy(SCALING_QUALITY_DEFAULT);
10438 si->screen_rendering_mode = getStringCopy(STR_SPECIAL_RENDERING_DEFAULT);
10439 si->vsync_mode = getStringCopy(STR_VSYNC_MODE_DEFAULT);
10440 si->ask_on_escape = TRUE;
10441 si->ask_on_escape_editor = TRUE;
10442 si->ask_on_game_over = TRUE;
10443 si->ask_on_quit_game = TRUE;
10444 si->ask_on_quit_program = TRUE;
10445 si->quick_switch = FALSE;
10446 si->input_on_focus = FALSE;
10447 si->prefer_aga_graphics = TRUE;
10448 si->prefer_lowpass_sounds = FALSE;
10449 si->prefer_extra_panel_items = TRUE;
10450 si->game_speed_extended = FALSE;
10451 si->game_frame_delay = GAME_FRAME_DELAY;
10452 si->sp_show_border_elements = FALSE;
10453 si->small_game_graphics = FALSE;
10454 si->show_load_save_buttons = FALSE;
10455 si->show_undo_redo_buttons = FALSE;
10456 si->scores_in_highscore_list = getStringCopy(STR_SCORES_TYPE_DEFAULT);
10458 si->graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
10459 si->sounds_set = getStringCopy(SND_CLASSIC_SUBDIR);
10460 si->music_set = getStringCopy(MUS_CLASSIC_SUBDIR);
10462 si->override_level_graphics = FALSE;
10463 si->override_level_sounds = FALSE;
10464 si->override_level_music = FALSE;
10466 si->volume_simple = 100; // percent
10467 si->volume_loops = 100; // percent
10468 si->volume_music = 100; // percent
10470 si->network_mode = FALSE;
10471 si->network_player_nr = 0; // first player
10472 si->network_server_hostname = getStringCopy(STR_NETWORK_AUTO_DETECT);
10474 si->touch.control_type = getStringCopy(TOUCH_CONTROL_DEFAULT);
10475 si->touch.move_distance = TOUCH_MOVE_DISTANCE_DEFAULT; // percent
10476 si->touch.drop_distance = TOUCH_DROP_DISTANCE_DEFAULT; // percent
10477 si->touch.transparency = TOUCH_TRANSPARENCY_DEFAULT; // percent
10478 si->touch.draw_outlined = TRUE;
10479 si->touch.draw_pressed = TRUE;
10481 for (i = 0; i < 2; i++)
10483 char *default_grid_button[6][2] =
10489 { "111222", " vv " },
10490 { "111222", " vv " }
10492 int grid_xsize = DEFAULT_GRID_XSIZE(i);
10493 int grid_ysize = DEFAULT_GRID_YSIZE(i);
10494 int min_xsize = MIN(6, grid_xsize);
10495 int min_ysize = MIN(6, grid_ysize);
10496 int startx = grid_xsize - min_xsize;
10497 int starty = grid_ysize - min_ysize;
10500 // virtual buttons grid can only be set to defaults if video is initialized
10501 // (this will be repeated if virtual buttons are not loaded from setup file)
10502 if (video.initialized)
10504 si->touch.grid_xsize[i] = grid_xsize;
10505 si->touch.grid_ysize[i] = grid_ysize;
10509 si->touch.grid_xsize[i] = -1;
10510 si->touch.grid_ysize[i] = -1;
10513 for (x = 0; x < MAX_GRID_XSIZE; x++)
10514 for (y = 0; y < MAX_GRID_YSIZE; y++)
10515 si->touch.grid_button[i][x][y] = CHAR_GRID_BUTTON_NONE;
10517 for (x = 0; x < min_xsize; x++)
10518 for (y = 0; y < min_ysize; y++)
10519 si->touch.grid_button[i][x][starty + y] =
10520 default_grid_button[y][0][x];
10522 for (x = 0; x < min_xsize; x++)
10523 for (y = 0; y < min_ysize; y++)
10524 si->touch.grid_button[i][startx + x][starty + y] =
10525 default_grid_button[y][1][x];
10528 si->touch.grid_initialized = video.initialized;
10530 si->touch.overlay_buttons = FALSE;
10532 si->editor.el_boulderdash = TRUE;
10533 si->editor.el_emerald_mine = TRUE;
10534 si->editor.el_emerald_mine_club = TRUE;
10535 si->editor.el_more = TRUE;
10536 si->editor.el_sokoban = TRUE;
10537 si->editor.el_supaplex = TRUE;
10538 si->editor.el_diamond_caves = TRUE;
10539 si->editor.el_dx_boulderdash = TRUE;
10541 si->editor.el_mirror_magic = TRUE;
10542 si->editor.el_deflektor = TRUE;
10544 si->editor.el_chars = TRUE;
10545 si->editor.el_steel_chars = TRUE;
10547 si->editor.el_classic = TRUE;
10548 si->editor.el_custom = TRUE;
10550 si->editor.el_user_defined = FALSE;
10551 si->editor.el_dynamic = TRUE;
10553 si->editor.el_headlines = TRUE;
10555 si->editor.show_element_token = FALSE;
10557 si->editor.show_read_only_warning = TRUE;
10559 si->editor.use_template_for_new_levels = TRUE;
10561 si->shortcut.save_game = DEFAULT_KEY_SAVE_GAME;
10562 si->shortcut.load_game = DEFAULT_KEY_LOAD_GAME;
10563 si->shortcut.restart_game = DEFAULT_KEY_RESTART_GAME;
10564 si->shortcut.pause_before_end = DEFAULT_KEY_PAUSE_BEFORE_END;
10565 si->shortcut.toggle_pause = DEFAULT_KEY_TOGGLE_PAUSE;
10567 si->shortcut.focus_player[0] = DEFAULT_KEY_FOCUS_PLAYER_1;
10568 si->shortcut.focus_player[1] = DEFAULT_KEY_FOCUS_PLAYER_2;
10569 si->shortcut.focus_player[2] = DEFAULT_KEY_FOCUS_PLAYER_3;
10570 si->shortcut.focus_player[3] = DEFAULT_KEY_FOCUS_PLAYER_4;
10571 si->shortcut.focus_player_all = DEFAULT_KEY_FOCUS_PLAYER_ALL;
10573 si->shortcut.tape_eject = DEFAULT_KEY_TAPE_EJECT;
10574 si->shortcut.tape_extra = DEFAULT_KEY_TAPE_EXTRA;
10575 si->shortcut.tape_stop = DEFAULT_KEY_TAPE_STOP;
10576 si->shortcut.tape_pause = DEFAULT_KEY_TAPE_PAUSE;
10577 si->shortcut.tape_record = DEFAULT_KEY_TAPE_RECORD;
10578 si->shortcut.tape_play = DEFAULT_KEY_TAPE_PLAY;
10580 si->shortcut.sound_simple = DEFAULT_KEY_SOUND_SIMPLE;
10581 si->shortcut.sound_loops = DEFAULT_KEY_SOUND_LOOPS;
10582 si->shortcut.sound_music = DEFAULT_KEY_SOUND_MUSIC;
10584 si->shortcut.snap_left = DEFAULT_KEY_SNAP_LEFT;
10585 si->shortcut.snap_right = DEFAULT_KEY_SNAP_RIGHT;
10586 si->shortcut.snap_up = DEFAULT_KEY_SNAP_UP;
10587 si->shortcut.snap_down = DEFAULT_KEY_SNAP_DOWN;
10589 for (i = 0; i < MAX_PLAYERS; i++)
10591 si->input[i].use_joystick = FALSE;
10592 si->input[i].joy.device_name=getStringCopy(getDeviceNameFromJoystickNr(i));
10593 si->input[i].joy.xleft = JOYSTICK_XLEFT;
10594 si->input[i].joy.xmiddle = JOYSTICK_XMIDDLE;
10595 si->input[i].joy.xright = JOYSTICK_XRIGHT;
10596 si->input[i].joy.yupper = JOYSTICK_YUPPER;
10597 si->input[i].joy.ymiddle = JOYSTICK_YMIDDLE;
10598 si->input[i].joy.ylower = JOYSTICK_YLOWER;
10599 si->input[i].joy.snap = (i == 0 ? JOY_BUTTON_1 : 0);
10600 si->input[i].joy.drop = (i == 0 ? JOY_BUTTON_2 : 0);
10601 si->input[i].key.left = (i == 0 ? DEFAULT_KEY_LEFT : KSYM_UNDEFINED);
10602 si->input[i].key.right = (i == 0 ? DEFAULT_KEY_RIGHT : KSYM_UNDEFINED);
10603 si->input[i].key.up = (i == 0 ? DEFAULT_KEY_UP : KSYM_UNDEFINED);
10604 si->input[i].key.down = (i == 0 ? DEFAULT_KEY_DOWN : KSYM_UNDEFINED);
10605 si->input[i].key.snap = (i == 0 ? DEFAULT_KEY_SNAP : KSYM_UNDEFINED);
10606 si->input[i].key.drop = (i == 0 ? DEFAULT_KEY_DROP : KSYM_UNDEFINED);
10609 si->system.sdl_renderdriver = getStringCopy(ARG_DEFAULT);
10610 si->system.sdl_videodriver = getStringCopy(ARG_DEFAULT);
10611 si->system.sdl_audiodriver = getStringCopy(ARG_DEFAULT);
10612 si->system.audio_fragment_size = DEFAULT_AUDIO_FRAGMENT_SIZE;
10614 si->internal.program_title = getStringCopy(PROGRAM_TITLE_STRING);
10615 si->internal.program_version = getStringCopy(getProgramRealVersionString());
10616 si->internal.program_author = getStringCopy(PROGRAM_AUTHOR_STRING);
10617 si->internal.program_email = getStringCopy(PROGRAM_EMAIL_STRING);
10618 si->internal.program_website = getStringCopy(PROGRAM_WEBSITE_STRING);
10619 si->internal.program_copyright = getStringCopy(PROGRAM_COPYRIGHT_STRING);
10620 si->internal.program_company = getStringCopy(PROGRAM_COMPANY_STRING);
10622 si->internal.program_icon_file = getStringCopy(PROGRAM_ICON_FILENAME);
10624 si->internal.default_graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
10625 si->internal.default_sounds_set = getStringCopy(SND_CLASSIC_SUBDIR);
10626 si->internal.default_music_set = getStringCopy(MUS_CLASSIC_SUBDIR);
10628 si->internal.fallback_graphics_file = getStringCopy(UNDEFINED_FILENAME);
10629 si->internal.fallback_sounds_file = getStringCopy(UNDEFINED_FILENAME);
10630 si->internal.fallback_music_file = getStringCopy(UNDEFINED_FILENAME);
10632 si->internal.default_level_series = getStringCopy(UNDEFINED_LEVELSET);
10633 si->internal.choose_from_top_leveldir = FALSE;
10634 si->internal.show_scaling_in_title = TRUE;
10635 si->internal.create_user_levelset = TRUE;
10636 si->internal.info_screens_from_main = FALSE;
10638 si->internal.default_window_width = WIN_XSIZE_DEFAULT;
10639 si->internal.default_window_height = WIN_YSIZE_DEFAULT;
10641 si->debug.frame_delay[0] = DEFAULT_FRAME_DELAY_0;
10642 si->debug.frame_delay[1] = DEFAULT_FRAME_DELAY_1;
10643 si->debug.frame_delay[2] = DEFAULT_FRAME_DELAY_2;
10644 si->debug.frame_delay[3] = DEFAULT_FRAME_DELAY_3;
10645 si->debug.frame_delay[4] = DEFAULT_FRAME_DELAY_4;
10646 si->debug.frame_delay[5] = DEFAULT_FRAME_DELAY_5;
10647 si->debug.frame_delay[6] = DEFAULT_FRAME_DELAY_6;
10648 si->debug.frame_delay[7] = DEFAULT_FRAME_DELAY_7;
10649 si->debug.frame_delay[8] = DEFAULT_FRAME_DELAY_8;
10650 si->debug.frame_delay[9] = DEFAULT_FRAME_DELAY_9;
10652 si->debug.frame_delay_key[0] = DEFAULT_KEY_FRAME_DELAY_0;
10653 si->debug.frame_delay_key[1] = DEFAULT_KEY_FRAME_DELAY_1;
10654 si->debug.frame_delay_key[2] = DEFAULT_KEY_FRAME_DELAY_2;
10655 si->debug.frame_delay_key[3] = DEFAULT_KEY_FRAME_DELAY_3;
10656 si->debug.frame_delay_key[4] = DEFAULT_KEY_FRAME_DELAY_4;
10657 si->debug.frame_delay_key[5] = DEFAULT_KEY_FRAME_DELAY_5;
10658 si->debug.frame_delay_key[6] = DEFAULT_KEY_FRAME_DELAY_6;
10659 si->debug.frame_delay_key[7] = DEFAULT_KEY_FRAME_DELAY_7;
10660 si->debug.frame_delay_key[8] = DEFAULT_KEY_FRAME_DELAY_8;
10661 si->debug.frame_delay_key[9] = DEFAULT_KEY_FRAME_DELAY_9;
10663 si->debug.frame_delay_use_mod_key = DEFAULT_FRAME_DELAY_USE_MOD_KEY;
10664 si->debug.frame_delay_game_only = DEFAULT_FRAME_DELAY_GAME_ONLY;
10666 si->debug.show_frames_per_second = FALSE;
10668 si->debug.xsn_mode = AUTO;
10669 si->debug.xsn_percent = 0;
10671 si->options.verbose = FALSE;
10672 si->options.debug = FALSE;
10673 si->options.debug_mode = getStringCopy(ARG_UNDEFINED_STRING);
10675 #if defined(PLATFORM_ANDROID)
10676 si->fullscreen = TRUE;
10677 si->touch.overlay_buttons = TRUE;
10680 setHideSetupEntry(&setup.debug.xsn_mode);
10683 static void setSetupInfoToDefaults_AutoSetup(struct SetupInfo *si)
10685 si->auto_setup.editor_zoom_tilesize = MINI_TILESIZE;
10688 static void setSetupInfoToDefaults_ServerSetup(struct SetupInfo *si)
10690 si->player_uuid = NULL; // (will be set later)
10691 si->player_version = 1; // (will be set later)
10693 si->use_api_server = TRUE;
10694 si->api_server_hostname = getStringCopy(API_SERVER_HOSTNAME);
10695 si->api_server_password = getStringCopy(UNDEFINED_PASSWORD);
10696 si->ask_for_uploading_tapes = TRUE;
10697 si->ask_for_remaining_tapes = FALSE;
10698 si->provide_uploading_tapes = TRUE;
10699 si->ask_for_using_api_server = TRUE;
10700 si->has_remaining_tapes = FALSE;
10703 static void setSetupInfoToDefaults_EditorCascade(struct SetupInfo *si)
10705 si->editor_cascade.el_bd = TRUE;
10706 si->editor_cascade.el_em = TRUE;
10707 si->editor_cascade.el_emc = TRUE;
10708 si->editor_cascade.el_rnd = TRUE;
10709 si->editor_cascade.el_sb = TRUE;
10710 si->editor_cascade.el_sp = TRUE;
10711 si->editor_cascade.el_dc = TRUE;
10712 si->editor_cascade.el_dx = TRUE;
10714 si->editor_cascade.el_mm = TRUE;
10715 si->editor_cascade.el_df = TRUE;
10717 si->editor_cascade.el_chars = FALSE;
10718 si->editor_cascade.el_steel_chars = FALSE;
10719 si->editor_cascade.el_ce = FALSE;
10720 si->editor_cascade.el_ge = FALSE;
10721 si->editor_cascade.el_es = FALSE;
10722 si->editor_cascade.el_ref = FALSE;
10723 si->editor_cascade.el_user = FALSE;
10724 si->editor_cascade.el_dynamic = FALSE;
10727 #define MAX_HIDE_SETUP_TOKEN_SIZE 20
10729 static char *getHideSetupToken(void *setup_value)
10731 static char hide_setup_token[MAX_HIDE_SETUP_TOKEN_SIZE];
10733 if (setup_value != NULL)
10734 snprintf(hide_setup_token, MAX_HIDE_SETUP_TOKEN_SIZE, "%p", setup_value);
10736 return hide_setup_token;
10739 void setHideSetupEntry(void *setup_value)
10741 char *hide_setup_token = getHideSetupToken(setup_value);
10743 if (hide_setup_hash == NULL)
10744 hide_setup_hash = newSetupFileHash();
10746 if (setup_value != NULL)
10747 setHashEntry(hide_setup_hash, hide_setup_token, "");
10750 void removeHideSetupEntry(void *setup_value)
10752 char *hide_setup_token = getHideSetupToken(setup_value);
10754 if (setup_value != NULL)
10755 removeHashEntry(hide_setup_hash, hide_setup_token);
10758 boolean hideSetupEntry(void *setup_value)
10760 char *hide_setup_token = getHideSetupToken(setup_value);
10762 return (setup_value != NULL &&
10763 getHashEntry(hide_setup_hash, hide_setup_token) != NULL);
10766 static void setSetupInfoFromTokenText(SetupFileHash *setup_file_hash,
10767 struct TokenInfo *token_info,
10768 int token_nr, char *token_text)
10770 char *token_hide_text = getStringCat2(token_text, ".hide");
10771 char *token_hide_value = getHashEntry(setup_file_hash, token_hide_text);
10773 // set the value of this setup option in the setup option structure
10774 setSetupInfo(token_info, token_nr, getHashEntry(setup_file_hash, token_text));
10776 // check if this setup option should be hidden in the setup menu
10777 if (token_hide_value != NULL && get_boolean_from_string(token_hide_value))
10778 setHideSetupEntry(token_info[token_nr].value);
10780 free(token_hide_text);
10783 static void setSetupInfoFromTokenInfo(SetupFileHash *setup_file_hash,
10784 struct TokenInfo *token_info,
10787 setSetupInfoFromTokenText(setup_file_hash, token_info, token_nr,
10788 token_info[token_nr].text);
10791 static void decodeSetupFileHash_Default(SetupFileHash *setup_file_hash)
10795 if (!setup_file_hash)
10798 for (i = 0; i < ARRAY_SIZE(global_setup_tokens); i++)
10799 setSetupInfoFromTokenInfo(setup_file_hash, global_setup_tokens, i);
10801 setup.touch.grid_initialized = TRUE;
10802 for (i = 0; i < 2; i++)
10804 int grid_xsize = setup.touch.grid_xsize[i];
10805 int grid_ysize = setup.touch.grid_ysize[i];
10808 // if virtual buttons are not loaded from setup file, repeat initializing
10809 // virtual buttons grid with default values later when video is initialized
10810 if (grid_xsize == -1 ||
10813 setup.touch.grid_initialized = FALSE;
10818 for (y = 0; y < grid_ysize; y++)
10820 char token_string[MAX_LINE_LEN];
10822 sprintf(token_string, "touch.virtual_buttons.%d.%02d", i, y);
10824 char *value_string = getHashEntry(setup_file_hash, token_string);
10826 if (value_string == NULL)
10829 for (x = 0; x < grid_xsize; x++)
10831 char c = value_string[x];
10833 setup.touch.grid_button[i][x][y] =
10834 (c == '.' ? CHAR_GRID_BUTTON_NONE : c);
10839 for (i = 0; i < ARRAY_SIZE(editor_setup_tokens); i++)
10840 setSetupInfoFromTokenInfo(setup_file_hash, editor_setup_tokens, i);
10842 for (i = 0; i < ARRAY_SIZE(shortcut_setup_tokens); i++)
10843 setSetupInfoFromTokenInfo(setup_file_hash, shortcut_setup_tokens, i);
10845 for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
10849 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
10851 setup_input = setup.input[pnr];
10852 for (i = 0; i < ARRAY_SIZE(player_setup_tokens); i++)
10854 char full_token[100];
10856 sprintf(full_token, "%s%s", prefix, player_setup_tokens[i].text);
10857 setSetupInfoFromTokenText(setup_file_hash, player_setup_tokens, i,
10860 setup.input[pnr] = setup_input;
10863 for (i = 0; i < ARRAY_SIZE(system_setup_tokens); i++)
10864 setSetupInfoFromTokenInfo(setup_file_hash, system_setup_tokens, i);
10866 for (i = 0; i < ARRAY_SIZE(internal_setup_tokens); i++)
10867 setSetupInfoFromTokenInfo(setup_file_hash, internal_setup_tokens, i);
10869 for (i = 0; i < ARRAY_SIZE(debug_setup_tokens); i++)
10870 setSetupInfoFromTokenInfo(setup_file_hash, debug_setup_tokens, i);
10872 for (i = 0; i < ARRAY_SIZE(options_setup_tokens); i++)
10873 setSetupInfoFromTokenInfo(setup_file_hash, options_setup_tokens, i);
10875 setHideRelatedSetupEntries();
10878 static void decodeSetupFileHash_AutoSetup(SetupFileHash *setup_file_hash)
10882 if (!setup_file_hash)
10885 for (i = 0; i < ARRAY_SIZE(auto_setup_tokens); i++)
10886 setSetupInfo(auto_setup_tokens, i,
10887 getHashEntry(setup_file_hash,
10888 auto_setup_tokens[i].text));
10891 static void decodeSetupFileHash_ServerSetup(SetupFileHash *setup_file_hash)
10895 if (!setup_file_hash)
10898 for (i = 0; i < ARRAY_SIZE(server_setup_tokens); i++)
10899 setSetupInfo(server_setup_tokens, i,
10900 getHashEntry(setup_file_hash,
10901 server_setup_tokens[i].text));
10904 static void decodeSetupFileHash_EditorCascade(SetupFileHash *setup_file_hash)
10908 if (!setup_file_hash)
10911 for (i = 0; i < ARRAY_SIZE(editor_cascade_setup_tokens); i++)
10912 setSetupInfo(editor_cascade_setup_tokens, i,
10913 getHashEntry(setup_file_hash,
10914 editor_cascade_setup_tokens[i].text));
10917 void LoadUserNames(void)
10919 int last_user_nr = user.nr;
10922 if (global.user_names != NULL)
10924 for (i = 0; i < MAX_PLAYER_NAMES; i++)
10925 checked_free(global.user_names[i]);
10927 checked_free(global.user_names);
10930 global.user_names = checked_calloc(MAX_PLAYER_NAMES * sizeof(char *));
10932 for (i = 0; i < MAX_PLAYER_NAMES; i++)
10936 SetupFileHash *setup_file_hash = loadSetupFileHash(getSetupFilename());
10938 if (setup_file_hash)
10940 char *player_name = getHashEntry(setup_file_hash, "player_name");
10942 global.user_names[i] = getFixedUserName(player_name);
10944 freeSetupFileHash(setup_file_hash);
10947 if (global.user_names[i] == NULL)
10948 global.user_names[i] = getStringCopy(getDefaultUserName(i));
10951 user.nr = last_user_nr;
10954 void LoadSetupFromFilename(char *filename)
10956 SetupFileHash *setup_file_hash = loadSetupFileHash(filename);
10958 if (setup_file_hash)
10960 decodeSetupFileHash_Default(setup_file_hash);
10962 freeSetupFileHash(setup_file_hash);
10966 Debug("setup", "using default setup values");
10970 static void LoadSetup_SpecialPostProcessing(void)
10972 char *player_name_new;
10974 // needed to work around problems with fixed length strings
10975 player_name_new = getFixedUserName(setup.player_name);
10976 free(setup.player_name);
10977 setup.player_name = player_name_new;
10979 // "scroll_delay: on(3) / off(0)" was replaced by scroll delay value
10980 if (setup.scroll_delay == FALSE)
10982 setup.scroll_delay_value = MIN_SCROLL_DELAY;
10983 setup.scroll_delay = TRUE; // now always "on"
10986 // make sure that scroll delay value stays inside valid range
10987 setup.scroll_delay_value =
10988 MIN(MAX(MIN_SCROLL_DELAY, setup.scroll_delay_value), MAX_SCROLL_DELAY);
10991 void LoadSetup_Default(void)
10995 // always start with reliable default values
10996 setSetupInfoToDefaults(&setup);
10998 // try to load setup values from default setup file
10999 filename = getDefaultSetupFilename();
11001 if (fileExists(filename))
11002 LoadSetupFromFilename(filename);
11004 // try to load setup values from platform setup file
11005 filename = getPlatformSetupFilename();
11007 if (fileExists(filename))
11008 LoadSetupFromFilename(filename);
11010 // try to load setup values from user setup file
11011 filename = getSetupFilename();
11013 LoadSetupFromFilename(filename);
11015 LoadSetup_SpecialPostProcessing();
11018 void LoadSetup_AutoSetup(void)
11020 char *filename = getPath2(getSetupDir(), AUTOSETUP_FILENAME);
11021 SetupFileHash *setup_file_hash = NULL;
11023 // always start with reliable default values
11024 setSetupInfoToDefaults_AutoSetup(&setup);
11026 setup_file_hash = loadSetupFileHash(filename);
11028 if (setup_file_hash)
11030 decodeSetupFileHash_AutoSetup(setup_file_hash);
11032 freeSetupFileHash(setup_file_hash);
11038 void LoadSetup_ServerSetup(void)
11040 char *filename = getPath2(getSetupDir(), SERVERSETUP_FILENAME);
11041 SetupFileHash *setup_file_hash = NULL;
11043 // always start with reliable default values
11044 setSetupInfoToDefaults_ServerSetup(&setup);
11046 setup_file_hash = loadSetupFileHash(filename);
11048 if (setup_file_hash)
11050 decodeSetupFileHash_ServerSetup(setup_file_hash);
11052 freeSetupFileHash(setup_file_hash);
11057 if (setup.player_uuid == NULL)
11059 // player UUID does not yet exist in setup file
11060 setup.player_uuid = getStringCopy(getUUID());
11061 setup.player_version = 2;
11063 SaveSetup_ServerSetup();
11067 void LoadSetup_EditorCascade(void)
11069 char *filename = getPath2(getSetupDir(), EDITORCASCADE_FILENAME);
11070 SetupFileHash *setup_file_hash = NULL;
11072 // always start with reliable default values
11073 setSetupInfoToDefaults_EditorCascade(&setup);
11075 setup_file_hash = loadSetupFileHash(filename);
11077 if (setup_file_hash)
11079 decodeSetupFileHash_EditorCascade(setup_file_hash);
11081 freeSetupFileHash(setup_file_hash);
11087 void LoadSetup(void)
11089 LoadSetup_Default();
11090 LoadSetup_AutoSetup();
11091 LoadSetup_ServerSetup();
11092 LoadSetup_EditorCascade();
11095 static void addGameControllerMappingToHash(SetupFileHash *mappings_hash,
11096 char *mapping_line)
11098 char mapping_guid[MAX_LINE_LEN];
11099 char *mapping_start, *mapping_end;
11101 // get GUID from game controller mapping line: copy complete line
11102 strncpy(mapping_guid, mapping_line, MAX_LINE_LEN - 1);
11103 mapping_guid[MAX_LINE_LEN - 1] = '\0';
11105 // get GUID from game controller mapping line: cut after GUID part
11106 mapping_start = strchr(mapping_guid, ',');
11107 if (mapping_start != NULL)
11108 *mapping_start = '\0';
11110 // cut newline from game controller mapping line
11111 mapping_end = strchr(mapping_line, '\n');
11112 if (mapping_end != NULL)
11113 *mapping_end = '\0';
11115 // add mapping entry to game controller mappings hash
11116 setHashEntry(mappings_hash, mapping_guid, mapping_line);
11119 static void LoadSetup_ReadGameControllerMappings(SetupFileHash *mappings_hash,
11124 if (!(file = fopen(filename, MODE_READ)))
11126 Warn("cannot read game controller mappings file '%s'", filename);
11131 while (!feof(file))
11133 char line[MAX_LINE_LEN];
11135 if (!fgets(line, MAX_LINE_LEN, file))
11138 addGameControllerMappingToHash(mappings_hash, line);
11144 void SaveSetup_Default(void)
11146 char *filename = getSetupFilename();
11150 InitUserDataDirectory();
11152 if (!(file = fopen(filename, MODE_WRITE)))
11154 Warn("cannot write setup file '%s'", filename);
11159 fprintFileHeader(file, SETUP_FILENAME);
11161 for (i = 0; i < ARRAY_SIZE(global_setup_tokens); i++)
11163 // just to make things nicer :)
11164 if (global_setup_tokens[i].value == &setup.multiple_users ||
11165 global_setup_tokens[i].value == &setup.sound ||
11166 global_setup_tokens[i].value == &setup.graphics_set ||
11167 global_setup_tokens[i].value == &setup.volume_simple ||
11168 global_setup_tokens[i].value == &setup.network_mode ||
11169 global_setup_tokens[i].value == &setup.touch.control_type ||
11170 global_setup_tokens[i].value == &setup.touch.grid_xsize[0] ||
11171 global_setup_tokens[i].value == &setup.touch.grid_xsize[1])
11172 fprintf(file, "\n");
11174 fprintf(file, "%s\n", getSetupLine(global_setup_tokens, "", i));
11177 for (i = 0; i < 2; i++)
11179 int grid_xsize = setup.touch.grid_xsize[i];
11180 int grid_ysize = setup.touch.grid_ysize[i];
11183 fprintf(file, "\n");
11185 for (y = 0; y < grid_ysize; y++)
11187 char token_string[MAX_LINE_LEN];
11188 char value_string[MAX_LINE_LEN];
11190 sprintf(token_string, "touch.virtual_buttons.%d.%02d", i, y);
11192 for (x = 0; x < grid_xsize; x++)
11194 char c = setup.touch.grid_button[i][x][y];
11196 value_string[x] = (c == CHAR_GRID_BUTTON_NONE ? '.' : c);
11199 value_string[grid_xsize] = '\0';
11201 fprintf(file, "%s\n", getFormattedSetupEntry(token_string, value_string));
11205 fprintf(file, "\n");
11206 for (i = 0; i < ARRAY_SIZE(editor_setup_tokens); i++)
11207 fprintf(file, "%s\n", getSetupLine(editor_setup_tokens, "", i));
11209 fprintf(file, "\n");
11210 for (i = 0; i < ARRAY_SIZE(shortcut_setup_tokens); i++)
11211 fprintf(file, "%s\n", getSetupLine(shortcut_setup_tokens, "", i));
11213 for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
11217 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
11218 fprintf(file, "\n");
11220 setup_input = setup.input[pnr];
11221 for (i = 0; i < ARRAY_SIZE(player_setup_tokens); i++)
11222 fprintf(file, "%s\n", getSetupLine(player_setup_tokens, prefix, i));
11225 fprintf(file, "\n");
11226 for (i = 0; i < ARRAY_SIZE(system_setup_tokens); i++)
11227 fprintf(file, "%s\n", getSetupLine(system_setup_tokens, "", i));
11229 // (internal setup values not saved to user setup file)
11231 fprintf(file, "\n");
11232 for (i = 0; i < ARRAY_SIZE(debug_setup_tokens); i++)
11233 if (!strPrefix(debug_setup_tokens[i].text, "debug.xsn_") ||
11234 setup.debug.xsn_mode != AUTO)
11235 fprintf(file, "%s\n", getSetupLine(debug_setup_tokens, "", i));
11237 fprintf(file, "\n");
11238 for (i = 0; i < ARRAY_SIZE(options_setup_tokens); i++)
11239 fprintf(file, "%s\n", getSetupLine(options_setup_tokens, "", i));
11243 SetFilePermissions(filename, PERMS_PRIVATE);
11246 void SaveSetup_AutoSetup(void)
11248 char *filename = getPath2(getSetupDir(), AUTOSETUP_FILENAME);
11252 InitUserDataDirectory();
11254 if (!(file = fopen(filename, MODE_WRITE)))
11256 Warn("cannot write auto setup file '%s'", filename);
11263 fprintFileHeader(file, AUTOSETUP_FILENAME);
11265 for (i = 0; i < ARRAY_SIZE(auto_setup_tokens); i++)
11266 fprintf(file, "%s\n", getSetupLine(auto_setup_tokens, "", i));
11270 SetFilePermissions(filename, PERMS_PRIVATE);
11275 void SaveSetup_ServerSetup(void)
11277 char *filename = getPath2(getSetupDir(), SERVERSETUP_FILENAME);
11281 InitUserDataDirectory();
11283 if (!(file = fopen(filename, MODE_WRITE)))
11285 Warn("cannot write server setup file '%s'", filename);
11292 fprintFileHeader(file, SERVERSETUP_FILENAME);
11294 for (i = 0; i < ARRAY_SIZE(server_setup_tokens); i++)
11296 // just to make things nicer :)
11297 if (server_setup_tokens[i].value == &setup.use_api_server)
11298 fprintf(file, "\n");
11300 fprintf(file, "%s\n", getSetupLine(server_setup_tokens, "", i));
11305 SetFilePermissions(filename, PERMS_PRIVATE);
11310 void SaveSetup_EditorCascade(void)
11312 char *filename = getPath2(getSetupDir(), EDITORCASCADE_FILENAME);
11316 InitUserDataDirectory();
11318 if (!(file = fopen(filename, MODE_WRITE)))
11320 Warn("cannot write editor cascade state file '%s'", filename);
11327 fprintFileHeader(file, EDITORCASCADE_FILENAME);
11329 for (i = 0; i < ARRAY_SIZE(editor_cascade_setup_tokens); i++)
11330 fprintf(file, "%s\n", getSetupLine(editor_cascade_setup_tokens, "", i));
11334 SetFilePermissions(filename, PERMS_PRIVATE);
11339 void SaveSetup(void)
11341 SaveSetup_Default();
11342 SaveSetup_AutoSetup();
11343 SaveSetup_ServerSetup();
11344 SaveSetup_EditorCascade();
11347 static void SaveSetup_WriteGameControllerMappings(SetupFileHash *mappings_hash,
11352 if (!(file = fopen(filename, MODE_WRITE)))
11354 Warn("cannot write game controller mappings file '%s'", filename);
11359 BEGIN_HASH_ITERATION(mappings_hash, itr)
11361 fprintf(file, "%s\n", HASH_ITERATION_VALUE(itr));
11363 END_HASH_ITERATION(mappings_hash, itr)
11368 void SaveSetup_AddGameControllerMapping(char *mapping)
11370 char *filename = getPath2(getSetupDir(), GAMECONTROLLER_BASENAME);
11371 SetupFileHash *mappings_hash = newSetupFileHash();
11373 InitUserDataDirectory();
11375 // load existing personal game controller mappings
11376 LoadSetup_ReadGameControllerMappings(mappings_hash, filename);
11378 // add new mapping to personal game controller mappings
11379 addGameControllerMappingToHash(mappings_hash, mapping);
11381 // save updated personal game controller mappings
11382 SaveSetup_WriteGameControllerMappings(mappings_hash, filename);
11384 freeSetupFileHash(mappings_hash);
11388 void LoadCustomElementDescriptions(void)
11390 char *filename = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
11391 SetupFileHash *setup_file_hash;
11394 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
11396 if (element_info[i].custom_description != NULL)
11398 free(element_info[i].custom_description);
11399 element_info[i].custom_description = NULL;
11403 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
11406 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
11408 char *token = getStringCat2(element_info[i].token_name, ".name");
11409 char *value = getHashEntry(setup_file_hash, token);
11412 element_info[i].custom_description = getStringCopy(value);
11417 freeSetupFileHash(setup_file_hash);
11420 static int getElementFromToken(char *token)
11422 char *value = getHashEntry(element_token_hash, token);
11425 return atoi(value);
11427 Warn("unknown element token '%s'", token);
11429 return EL_UNDEFINED;
11432 void FreeGlobalAnimEventInfo(void)
11434 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
11436 if (gaei->event_list == NULL)
11441 for (i = 0; i < gaei->num_event_lists; i++)
11443 checked_free(gaei->event_list[i]->event_value);
11444 checked_free(gaei->event_list[i]);
11447 checked_free(gaei->event_list);
11449 gaei->event_list = NULL;
11450 gaei->num_event_lists = 0;
11453 static int AddGlobalAnimEventList(void)
11455 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
11456 int list_pos = gaei->num_event_lists++;
11458 gaei->event_list = checked_realloc(gaei->event_list, gaei->num_event_lists *
11459 sizeof(struct GlobalAnimEventListInfo *));
11461 gaei->event_list[list_pos] =
11462 checked_calloc(sizeof(struct GlobalAnimEventListInfo));
11464 struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
11466 gaeli->event_value = NULL;
11467 gaeli->num_event_values = 0;
11472 static int AddGlobalAnimEventValue(int list_pos, int event_value)
11474 // do not add empty global animation events
11475 if (event_value == ANIM_EVENT_NONE)
11478 // if list position is undefined, create new list
11479 if (list_pos == ANIM_EVENT_UNDEFINED)
11480 list_pos = AddGlobalAnimEventList();
11482 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
11483 struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
11484 int value_pos = gaeli->num_event_values++;
11486 gaeli->event_value = checked_realloc(gaeli->event_value,
11487 gaeli->num_event_values * sizeof(int *));
11489 gaeli->event_value[value_pos] = event_value;
11494 int GetGlobalAnimEventValue(int list_pos, int value_pos)
11496 if (list_pos == ANIM_EVENT_UNDEFINED)
11497 return ANIM_EVENT_NONE;
11499 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
11500 struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
11502 return gaeli->event_value[value_pos];
11505 int GetGlobalAnimEventValueCount(int list_pos)
11507 if (list_pos == ANIM_EVENT_UNDEFINED)
11510 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
11511 struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
11513 return gaeli->num_event_values;
11516 // This function checks if a string <s> of the format "string1, string2, ..."
11517 // exactly contains a string <s_contained>.
11519 static boolean string_has_parameter(char *s, char *s_contained)
11523 if (s == NULL || s_contained == NULL)
11526 if (strlen(s_contained) > strlen(s))
11529 if (strncmp(s, s_contained, strlen(s_contained)) == 0)
11531 char next_char = s[strlen(s_contained)];
11533 // check if next character is delimiter or whitespace
11534 if (next_char == ',' || next_char == '\0' ||
11535 next_char == ' ' || next_char == '\t')
11539 // check if string contains another parameter string after a comma
11540 substring = strchr(s, ',');
11541 if (substring == NULL) // string does not contain a comma
11544 // advance string pointer to next character after the comma
11547 // skip potential whitespaces after the comma
11548 while (*substring == ' ' || *substring == '\t')
11551 return string_has_parameter(substring, s_contained);
11554 static int get_anim_parameter_value_ce(char *s)
11557 char *pattern_1 = "ce_change:custom_";
11558 char *pattern_2 = ".page_";
11559 int pattern_1_len = strlen(pattern_1);
11560 char *matching_char = strstr(s_ptr, pattern_1);
11561 int result = ANIM_EVENT_NONE;
11563 if (matching_char == NULL)
11564 return ANIM_EVENT_NONE;
11566 result = ANIM_EVENT_CE_CHANGE;
11568 s_ptr = matching_char + pattern_1_len;
11570 // check for custom element number ("custom_X", "custom_XX" or "custom_XXX")
11571 if (*s_ptr >= '0' && *s_ptr <= '9')
11573 int gic_ce_nr = (*s_ptr++ - '0');
11575 if (*s_ptr >= '0' && *s_ptr <= '9')
11577 gic_ce_nr = 10 * gic_ce_nr + (*s_ptr++ - '0');
11579 if (*s_ptr >= '0' && *s_ptr <= '9')
11580 gic_ce_nr = 10 * gic_ce_nr + (*s_ptr++ - '0');
11583 if (gic_ce_nr < 1 || gic_ce_nr > NUM_CUSTOM_ELEMENTS)
11584 return ANIM_EVENT_NONE;
11586 // custom element stored as 0 to 255
11589 result |= gic_ce_nr << ANIM_EVENT_CE_BIT;
11593 // invalid custom element number specified
11595 return ANIM_EVENT_NONE;
11598 // check for change page number ("page_X" or "page_XX") (optional)
11599 if (strPrefix(s_ptr, pattern_2))
11601 s_ptr += strlen(pattern_2);
11603 if (*s_ptr >= '0' && *s_ptr <= '9')
11605 int gic_page_nr = (*s_ptr++ - '0');
11607 if (*s_ptr >= '0' && *s_ptr <= '9')
11608 gic_page_nr = 10 * gic_page_nr + (*s_ptr++ - '0');
11610 if (gic_page_nr < 1 || gic_page_nr > MAX_CHANGE_PAGES)
11611 return ANIM_EVENT_NONE;
11613 // change page stored as 1 to 32 (0 means "all change pages")
11615 result |= gic_page_nr << ANIM_EVENT_PAGE_BIT;
11619 // invalid animation part number specified
11621 return ANIM_EVENT_NONE;
11625 // discard result if next character is neither delimiter nor whitespace
11626 if (!(*s_ptr == ',' || *s_ptr == '\0' ||
11627 *s_ptr == ' ' || *s_ptr == '\t'))
11628 return ANIM_EVENT_NONE;
11633 static int get_anim_parameter_value(char *s)
11635 int event_value[] =
11643 char *pattern_1[] =
11651 char *pattern_2 = ".part_";
11652 char *matching_char = NULL;
11654 int pattern_1_len = 0;
11655 int result = ANIM_EVENT_NONE;
11658 result = get_anim_parameter_value_ce(s);
11660 if (result != ANIM_EVENT_NONE)
11663 for (i = 0; i < ARRAY_SIZE(event_value); i++)
11665 matching_char = strstr(s_ptr, pattern_1[i]);
11666 pattern_1_len = strlen(pattern_1[i]);
11667 result = event_value[i];
11669 if (matching_char != NULL)
11673 if (matching_char == NULL)
11674 return ANIM_EVENT_NONE;
11676 s_ptr = matching_char + pattern_1_len;
11678 // check for main animation number ("anim_X" or "anim_XX")
11679 if (*s_ptr >= '0' && *s_ptr <= '9')
11681 int gic_anim_nr = (*s_ptr++ - '0');
11683 if (*s_ptr >= '0' && *s_ptr <= '9')
11684 gic_anim_nr = 10 * gic_anim_nr + (*s_ptr++ - '0');
11686 if (gic_anim_nr < 1 || gic_anim_nr > MAX_GLOBAL_ANIMS)
11687 return ANIM_EVENT_NONE;
11689 result |= gic_anim_nr << ANIM_EVENT_ANIM_BIT;
11693 // invalid main animation number specified
11695 return ANIM_EVENT_NONE;
11698 // check for animation part number ("part_X" or "part_XX") (optional)
11699 if (strPrefix(s_ptr, pattern_2))
11701 s_ptr += strlen(pattern_2);
11703 if (*s_ptr >= '0' && *s_ptr <= '9')
11705 int gic_part_nr = (*s_ptr++ - '0');
11707 if (*s_ptr >= '0' && *s_ptr <= '9')
11708 gic_part_nr = 10 * gic_part_nr + (*s_ptr++ - '0');
11710 if (gic_part_nr < 1 || gic_part_nr > MAX_GLOBAL_ANIM_PARTS)
11711 return ANIM_EVENT_NONE;
11713 result |= gic_part_nr << ANIM_EVENT_PART_BIT;
11717 // invalid animation part number specified
11719 return ANIM_EVENT_NONE;
11723 // discard result if next character is neither delimiter nor whitespace
11724 if (!(*s_ptr == ',' || *s_ptr == '\0' ||
11725 *s_ptr == ' ' || *s_ptr == '\t'))
11726 return ANIM_EVENT_NONE;
11731 static int get_anim_parameter_values(char *s)
11733 int list_pos = ANIM_EVENT_UNDEFINED;
11734 int event_value = ANIM_EVENT_DEFAULT;
11736 if (string_has_parameter(s, "any"))
11737 event_value |= ANIM_EVENT_ANY;
11739 if (string_has_parameter(s, "click:self") ||
11740 string_has_parameter(s, "click") ||
11741 string_has_parameter(s, "self"))
11742 event_value |= ANIM_EVENT_SELF;
11744 if (string_has_parameter(s, "unclick:any"))
11745 event_value |= ANIM_EVENT_UNCLICK_ANY;
11747 // if animation event found, add it to global animation event list
11748 if (event_value != ANIM_EVENT_NONE)
11749 list_pos = AddGlobalAnimEventValue(list_pos, event_value);
11753 // add optional "click:anim_X" or "click:anim_X.part_X" parameter
11754 event_value = get_anim_parameter_value(s);
11756 // if animation event found, add it to global animation event list
11757 if (event_value != ANIM_EVENT_NONE)
11758 list_pos = AddGlobalAnimEventValue(list_pos, event_value);
11760 // continue with next part of the string, starting with next comma
11761 s = strchr(s + 1, ',');
11767 static int get_anim_action_parameter_value(char *token)
11769 // check most common default case first to massively speed things up
11770 if (strEqual(token, ARG_UNDEFINED))
11771 return ANIM_EVENT_ACTION_NONE;
11773 int result = getImageIDFromToken(token);
11777 char *gfx_token = getStringCat2("gfx.", token);
11779 result = getImageIDFromToken(gfx_token);
11781 checked_free(gfx_token);
11786 Key key = getKeyFromX11KeyName(token);
11788 if (key != KSYM_UNDEFINED)
11789 result = -(int)key;
11796 result = get_hash_from_key(token); // unsigned int => int
11797 result = ABS(result); // may be negative now
11798 result += (result < MAX_IMAGE_FILES ? MAX_IMAGE_FILES : 0);
11800 setHashEntry(anim_url_hash, int2str(result, 0), token);
11805 result = ANIM_EVENT_ACTION_NONE;
11810 int get_parameter_value(char *value_raw, char *suffix, int type)
11812 char *value = getStringToLower(value_raw);
11813 int result = 0; // probably a save default value
11815 if (strEqual(suffix, ".direction"))
11817 result = (strEqual(value, "left") ? MV_LEFT :
11818 strEqual(value, "right") ? MV_RIGHT :
11819 strEqual(value, "up") ? MV_UP :
11820 strEqual(value, "down") ? MV_DOWN : MV_NONE);
11822 else if (strEqual(suffix, ".position"))
11824 result = (strEqual(value, "left") ? POS_LEFT :
11825 strEqual(value, "right") ? POS_RIGHT :
11826 strEqual(value, "top") ? POS_TOP :
11827 strEqual(value, "upper") ? POS_UPPER :
11828 strEqual(value, "middle") ? POS_MIDDLE :
11829 strEqual(value, "lower") ? POS_LOWER :
11830 strEqual(value, "bottom") ? POS_BOTTOM :
11831 strEqual(value, "any") ? POS_ANY :
11832 strEqual(value, "ce") ? POS_CE :
11833 strEqual(value, "ce_trigger") ? POS_CE_TRIGGER :
11834 strEqual(value, "last") ? POS_LAST : POS_UNDEFINED);
11836 else if (strEqual(suffix, ".align"))
11838 result = (strEqual(value, "left") ? ALIGN_LEFT :
11839 strEqual(value, "right") ? ALIGN_RIGHT :
11840 strEqual(value, "center") ? ALIGN_CENTER :
11841 strEqual(value, "middle") ? ALIGN_CENTER : ALIGN_DEFAULT);
11843 else if (strEqual(suffix, ".valign"))
11845 result = (strEqual(value, "top") ? VALIGN_TOP :
11846 strEqual(value, "bottom") ? VALIGN_BOTTOM :
11847 strEqual(value, "middle") ? VALIGN_MIDDLE :
11848 strEqual(value, "center") ? VALIGN_MIDDLE : VALIGN_DEFAULT);
11850 else if (strEqual(suffix, ".anim_mode"))
11852 result = (string_has_parameter(value, "none") ? ANIM_NONE :
11853 string_has_parameter(value, "loop") ? ANIM_LOOP :
11854 string_has_parameter(value, "linear") ? ANIM_LINEAR :
11855 string_has_parameter(value, "pingpong") ? ANIM_PINGPONG :
11856 string_has_parameter(value, "pingpong2") ? ANIM_PINGPONG2 :
11857 string_has_parameter(value, "random") ? ANIM_RANDOM :
11858 string_has_parameter(value, "random_static") ? ANIM_RANDOM_STATIC :
11859 string_has_parameter(value, "ce_value") ? ANIM_CE_VALUE :
11860 string_has_parameter(value, "ce_score") ? ANIM_CE_SCORE :
11861 string_has_parameter(value, "ce_delay") ? ANIM_CE_DELAY :
11862 string_has_parameter(value, "horizontal") ? ANIM_HORIZONTAL :
11863 string_has_parameter(value, "vertical") ? ANIM_VERTICAL :
11864 string_has_parameter(value, "centered") ? ANIM_CENTERED :
11865 string_has_parameter(value, "all") ? ANIM_ALL :
11866 string_has_parameter(value, "tiled") ? ANIM_TILED :
11867 string_has_parameter(value, "level_nr") ? ANIM_LEVEL_NR :
11870 if (string_has_parameter(value, "once"))
11871 result |= ANIM_ONCE;
11873 if (string_has_parameter(value, "reverse"))
11874 result |= ANIM_REVERSE;
11876 if (string_has_parameter(value, "opaque_player"))
11877 result |= ANIM_OPAQUE_PLAYER;
11879 if (string_has_parameter(value, "static_panel"))
11880 result |= ANIM_STATIC_PANEL;
11882 else if (strEqual(suffix, ".init_event") ||
11883 strEqual(suffix, ".anim_event"))
11885 result = get_anim_parameter_values(value);
11887 else if (strEqual(suffix, ".init_delay_action") ||
11888 strEqual(suffix, ".anim_delay_action") ||
11889 strEqual(suffix, ".post_delay_action") ||
11890 strEqual(suffix, ".init_event_action") ||
11891 strEqual(suffix, ".anim_event_action"))
11893 result = get_anim_action_parameter_value(value_raw);
11895 else if (strEqual(suffix, ".class"))
11897 result = (strEqual(value, ARG_UNDEFINED) ? ARG_UNDEFINED_VALUE :
11898 get_hash_from_key(value));
11900 else if (strEqual(suffix, ".style"))
11902 result = STYLE_DEFAULT;
11904 if (string_has_parameter(value, "accurate_borders"))
11905 result |= STYLE_ACCURATE_BORDERS;
11907 if (string_has_parameter(value, "inner_corners"))
11908 result |= STYLE_INNER_CORNERS;
11910 if (string_has_parameter(value, "reverse"))
11911 result |= STYLE_REVERSE;
11913 if (string_has_parameter(value, "leftmost_position"))
11914 result |= STYLE_LEFTMOST_POSITION;
11916 if (string_has_parameter(value, "block_clicks"))
11917 result |= STYLE_BLOCK;
11919 if (string_has_parameter(value, "passthrough_clicks"))
11920 result |= STYLE_PASSTHROUGH;
11922 if (string_has_parameter(value, "multiple_actions"))
11923 result |= STYLE_MULTIPLE_ACTIONS;
11925 if (string_has_parameter(value, "consume_ce_event"))
11926 result |= STYLE_CONSUME_CE_EVENT;
11928 else if (strEqual(suffix, ".fade_mode"))
11930 result = (string_has_parameter(value, "none") ? FADE_MODE_NONE :
11931 string_has_parameter(value, "fade") ? FADE_MODE_FADE :
11932 string_has_parameter(value, "fade_in") ? FADE_MODE_FADE_IN :
11933 string_has_parameter(value, "fade_out") ? FADE_MODE_FADE_OUT :
11934 string_has_parameter(value, "crossfade") ? FADE_MODE_CROSSFADE :
11935 string_has_parameter(value, "melt") ? FADE_MODE_MELT :
11936 string_has_parameter(value, "curtain") ? FADE_MODE_CURTAIN :
11937 FADE_MODE_DEFAULT);
11939 else if (strEqual(suffix, ".auto_delay_unit"))
11941 result = (string_has_parameter(value, "ms") ? AUTO_DELAY_UNIT_MS :
11942 string_has_parameter(value, "frames") ? AUTO_DELAY_UNIT_FRAMES :
11943 AUTO_DELAY_UNIT_DEFAULT);
11945 else if (strPrefix(suffix, ".font")) // (may also be ".font_xyz")
11947 result = gfx.get_font_from_token_function(value);
11949 else // generic parameter of type integer or boolean
11951 result = (strEqual(value, ARG_UNDEFINED) ? ARG_UNDEFINED_VALUE :
11952 type == TYPE_INTEGER ? get_integer_from_string(value) :
11953 type == TYPE_BOOLEAN ? get_boolean_from_string(value) :
11954 ARG_UNDEFINED_VALUE);
11962 static int get_token_parameter_value(char *token, char *value_raw)
11966 if (token == NULL || value_raw == NULL)
11967 return ARG_UNDEFINED_VALUE;
11969 suffix = strrchr(token, '.');
11970 if (suffix == NULL)
11973 if (strEqual(suffix, ".element"))
11974 return getElementFromToken(value_raw);
11976 // !!! USE CORRECT VALUE TYPE (currently works also for TYPE_BOOLEAN) !!!
11977 return get_parameter_value(value_raw, suffix, TYPE_INTEGER);
11980 void InitMenuDesignSettings_FromHash(SetupFileHash *setup_file_hash,
11981 boolean ignore_defaults)
11985 for (i = 0; image_config_vars[i].token != NULL; i++)
11987 char *value = getHashEntry(setup_file_hash, image_config_vars[i].token);
11989 // (ignore definitions set to "[DEFAULT]" which are already initialized)
11990 if (ignore_defaults && strEqual(value, ARG_DEFAULT))
11994 *image_config_vars[i].value =
11995 get_token_parameter_value(image_config_vars[i].token, value);
11999 void InitMenuDesignSettings_Static(void)
12001 // always start with reliable default values from static default config
12002 InitMenuDesignSettings_FromHash(image_config_hash, FALSE);
12005 static void InitMenuDesignSettings_SpecialPreProcessing(void)
12009 // the following initializes hierarchical values from static configuration
12011 // special case: initialize "ARG_DEFAULT" values in static default config
12012 // (e.g., initialize "[titlemessage].fade_mode" from "[title].fade_mode")
12013 titlescreen_initial_first_default.fade_mode =
12014 title_initial_first_default.fade_mode;
12015 titlescreen_initial_first_default.fade_delay =
12016 title_initial_first_default.fade_delay;
12017 titlescreen_initial_first_default.post_delay =
12018 title_initial_first_default.post_delay;
12019 titlescreen_initial_first_default.auto_delay =
12020 title_initial_first_default.auto_delay;
12021 titlescreen_initial_first_default.auto_delay_unit =
12022 title_initial_first_default.auto_delay_unit;
12023 titlescreen_first_default.fade_mode = title_first_default.fade_mode;
12024 titlescreen_first_default.fade_delay = title_first_default.fade_delay;
12025 titlescreen_first_default.post_delay = title_first_default.post_delay;
12026 titlescreen_first_default.auto_delay = title_first_default.auto_delay;
12027 titlescreen_first_default.auto_delay_unit =
12028 title_first_default.auto_delay_unit;
12029 titlemessage_initial_first_default.fade_mode =
12030 title_initial_first_default.fade_mode;
12031 titlemessage_initial_first_default.fade_delay =
12032 title_initial_first_default.fade_delay;
12033 titlemessage_initial_first_default.post_delay =
12034 title_initial_first_default.post_delay;
12035 titlemessage_initial_first_default.auto_delay =
12036 title_initial_first_default.auto_delay;
12037 titlemessage_initial_first_default.auto_delay_unit =
12038 title_initial_first_default.auto_delay_unit;
12039 titlemessage_first_default.fade_mode = title_first_default.fade_mode;
12040 titlemessage_first_default.fade_delay = title_first_default.fade_delay;
12041 titlemessage_first_default.post_delay = title_first_default.post_delay;
12042 titlemessage_first_default.auto_delay = title_first_default.auto_delay;
12043 titlemessage_first_default.auto_delay_unit =
12044 title_first_default.auto_delay_unit;
12046 titlescreen_initial_default.fade_mode = title_initial_default.fade_mode;
12047 titlescreen_initial_default.fade_delay = title_initial_default.fade_delay;
12048 titlescreen_initial_default.post_delay = title_initial_default.post_delay;
12049 titlescreen_initial_default.auto_delay = title_initial_default.auto_delay;
12050 titlescreen_initial_default.auto_delay_unit =
12051 title_initial_default.auto_delay_unit;
12052 titlescreen_default.fade_mode = title_default.fade_mode;
12053 titlescreen_default.fade_delay = title_default.fade_delay;
12054 titlescreen_default.post_delay = title_default.post_delay;
12055 titlescreen_default.auto_delay = title_default.auto_delay;
12056 titlescreen_default.auto_delay_unit = title_default.auto_delay_unit;
12057 titlemessage_initial_default.fade_mode = title_initial_default.fade_mode;
12058 titlemessage_initial_default.fade_delay = title_initial_default.fade_delay;
12059 titlemessage_initial_default.post_delay = title_initial_default.post_delay;
12060 titlemessage_initial_default.auto_delay_unit =
12061 title_initial_default.auto_delay_unit;
12062 titlemessage_default.fade_mode = title_default.fade_mode;
12063 titlemessage_default.fade_delay = title_default.fade_delay;
12064 titlemessage_default.post_delay = title_default.post_delay;
12065 titlemessage_default.auto_delay = title_default.auto_delay;
12066 titlemessage_default.auto_delay_unit = title_default.auto_delay_unit;
12068 // special case: initialize "ARG_DEFAULT" values in static default config
12069 // (e.g., init "titlemessage_1.fade_mode" from "[titlemessage].fade_mode")
12070 for (i = 0; i < MAX_NUM_TITLE_MESSAGES; i++)
12072 titlescreen_initial_first[i] = titlescreen_initial_first_default;
12073 titlescreen_first[i] = titlescreen_first_default;
12074 titlemessage_initial_first[i] = titlemessage_initial_first_default;
12075 titlemessage_first[i] = titlemessage_first_default;
12077 titlescreen_initial[i] = titlescreen_initial_default;
12078 titlescreen[i] = titlescreen_default;
12079 titlemessage_initial[i] = titlemessage_initial_default;
12080 titlemessage[i] = titlemessage_default;
12083 // special case: initialize "ARG_DEFAULT" values in static default config
12084 // (eg, init "menu.enter_screen.SCORES.xyz" from "menu.enter_screen.xyz")
12085 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
12087 if (i == GFX_SPECIAL_ARG_TITLE) // title values already initialized
12090 menu.enter_screen[i] = menu.enter_screen[GFX_SPECIAL_ARG_DEFAULT];
12091 menu.leave_screen[i] = menu.leave_screen[GFX_SPECIAL_ARG_DEFAULT];
12092 menu.next_screen[i] = menu.next_screen[GFX_SPECIAL_ARG_DEFAULT];
12095 // special case: initialize "ARG_DEFAULT" values in static default config
12096 // (eg, init "viewport.door_1.MAIN.xyz" from "viewport.door_1.xyz")
12097 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
12099 viewport.window[i] = viewport.window[GFX_SPECIAL_ARG_DEFAULT];
12100 viewport.playfield[i] = viewport.playfield[GFX_SPECIAL_ARG_DEFAULT];
12101 viewport.door_1[i] = viewport.door_1[GFX_SPECIAL_ARG_DEFAULT];
12103 if (i == GFX_SPECIAL_ARG_EDITOR) // editor values already initialized
12106 viewport.door_2[i] = viewport.door_2[GFX_SPECIAL_ARG_DEFAULT];
12110 static void InitMenuDesignSettings_SpecialPostProcessing(void)
12114 struct XY *dst, *src;
12116 game_buttons_xy[] =
12118 { &game.button.save, &game.button.stop },
12119 { &game.button.pause2, &game.button.pause },
12120 { &game.button.load, &game.button.play },
12121 { &game.button.undo, &game.button.stop },
12122 { &game.button.redo, &game.button.play },
12128 // special case: initialize later added SETUP list size from LEVELS value
12129 if (menu.list_size[GAME_MODE_SETUP] == -1)
12130 menu.list_size[GAME_MODE_SETUP] = menu.list_size[GAME_MODE_LEVELS];
12132 // set default position for snapshot buttons to stop/pause/play buttons
12133 for (i = 0; game_buttons_xy[i].dst != NULL; i++)
12134 if ((*game_buttons_xy[i].dst).x == -1 &&
12135 (*game_buttons_xy[i].dst).y == -1)
12136 *game_buttons_xy[i].dst = *game_buttons_xy[i].src;
12138 // --------------------------------------------------------------------------
12139 // dynamic viewports (including playfield margins, borders and alignments)
12140 // --------------------------------------------------------------------------
12142 // dynamic viewports currently only supported for landscape mode
12143 int display_width = MAX(video.display_width, video.display_height);
12144 int display_height = MIN(video.display_width, video.display_height);
12146 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
12148 struct RectWithBorder *vp_window = &viewport.window[i];
12149 struct RectWithBorder *vp_playfield = &viewport.playfield[i];
12150 struct RectWithBorder *vp_door_1 = &viewport.door_1[i];
12151 struct RectWithBorder *vp_door_2 = &viewport.door_2[i];
12152 boolean dynamic_window_width = (vp_window->min_width != -1);
12153 boolean dynamic_window_height = (vp_window->min_height != -1);
12154 boolean dynamic_playfield_width = (vp_playfield->min_width != -1);
12155 boolean dynamic_playfield_height = (vp_playfield->min_height != -1);
12157 // adjust window size if min/max width/height is specified
12159 if (vp_window->min_width != -1)
12161 int window_width = display_width;
12163 // when using static window height, use aspect ratio of display
12164 if (vp_window->min_height == -1)
12165 window_width = vp_window->height * display_width / display_height;
12167 vp_window->width = MAX(vp_window->min_width, window_width);
12170 if (vp_window->min_height != -1)
12172 int window_height = display_height;
12174 // when using static window width, use aspect ratio of display
12175 if (vp_window->min_width == -1)
12176 window_height = vp_window->width * display_height / display_width;
12178 vp_window->height = MAX(vp_window->min_height, window_height);
12181 if (vp_window->max_width != -1)
12182 vp_window->width = MIN(vp_window->width, vp_window->max_width);
12184 if (vp_window->max_height != -1)
12185 vp_window->height = MIN(vp_window->height, vp_window->max_height);
12187 int playfield_width = vp_window->width;
12188 int playfield_height = vp_window->height;
12190 // adjust playfield size and position according to specified margins
12192 playfield_width -= vp_playfield->margin_left;
12193 playfield_width -= vp_playfield->margin_right;
12195 playfield_height -= vp_playfield->margin_top;
12196 playfield_height -= vp_playfield->margin_bottom;
12198 // adjust playfield size if min/max width/height is specified
12200 if (vp_playfield->min_width != -1)
12201 vp_playfield->width = MAX(vp_playfield->min_width, playfield_width);
12203 if (vp_playfield->min_height != -1)
12204 vp_playfield->height = MAX(vp_playfield->min_height, playfield_height);
12206 if (vp_playfield->max_width != -1)
12207 vp_playfield->width = MIN(vp_playfield->width, vp_playfield->max_width);
12209 if (vp_playfield->max_height != -1)
12210 vp_playfield->height = MIN(vp_playfield->height, vp_playfield->max_height);
12212 // adjust playfield position according to specified alignment
12214 if (vp_playfield->align == ALIGN_LEFT || vp_playfield->x > 0)
12215 vp_playfield->x = ALIGNED_VP_XPOS(vp_playfield);
12216 else if (vp_playfield->align == ALIGN_CENTER)
12217 vp_playfield->x = playfield_width / 2 - vp_playfield->width / 2;
12218 else if (vp_playfield->align == ALIGN_RIGHT)
12219 vp_playfield->x += playfield_width - vp_playfield->width;
12221 if (vp_playfield->valign == VALIGN_TOP || vp_playfield->y > 0)
12222 vp_playfield->y = ALIGNED_VP_YPOS(vp_playfield);
12223 else if (vp_playfield->valign == VALIGN_MIDDLE)
12224 vp_playfield->y = playfield_height / 2 - vp_playfield->height / 2;
12225 else if (vp_playfield->valign == VALIGN_BOTTOM)
12226 vp_playfield->y += playfield_height - vp_playfield->height;
12228 vp_playfield->x += vp_playfield->margin_left;
12229 vp_playfield->y += vp_playfield->margin_top;
12231 // adjust individual playfield borders if only default border is specified
12233 if (vp_playfield->border_left == -1)
12234 vp_playfield->border_left = vp_playfield->border_size;
12235 if (vp_playfield->border_right == -1)
12236 vp_playfield->border_right = vp_playfield->border_size;
12237 if (vp_playfield->border_top == -1)
12238 vp_playfield->border_top = vp_playfield->border_size;
12239 if (vp_playfield->border_bottom == -1)
12240 vp_playfield->border_bottom = vp_playfield->border_size;
12242 // set dynamic playfield borders if borders are specified as undefined
12243 // (but only if window size was dynamic and playfield size was static)
12245 if (dynamic_window_width && !dynamic_playfield_width)
12247 if (vp_playfield->border_left == -1)
12249 vp_playfield->border_left = (vp_playfield->x -
12250 vp_playfield->margin_left);
12251 vp_playfield->x -= vp_playfield->border_left;
12252 vp_playfield->width += vp_playfield->border_left;
12255 if (vp_playfield->border_right == -1)
12257 vp_playfield->border_right = (vp_window->width -
12259 vp_playfield->width -
12260 vp_playfield->margin_right);
12261 vp_playfield->width += vp_playfield->border_right;
12265 if (dynamic_window_height && !dynamic_playfield_height)
12267 if (vp_playfield->border_top == -1)
12269 vp_playfield->border_top = (vp_playfield->y -
12270 vp_playfield->margin_top);
12271 vp_playfield->y -= vp_playfield->border_top;
12272 vp_playfield->height += vp_playfield->border_top;
12275 if (vp_playfield->border_bottom == -1)
12277 vp_playfield->border_bottom = (vp_window->height -
12279 vp_playfield->height -
12280 vp_playfield->margin_bottom);
12281 vp_playfield->height += vp_playfield->border_bottom;
12285 // adjust playfield size to be a multiple of a defined alignment tile size
12287 int align_size = vp_playfield->align_size;
12288 int playfield_xtiles = vp_playfield->width / align_size;
12289 int playfield_ytiles = vp_playfield->height / align_size;
12290 int playfield_width_corrected = playfield_xtiles * align_size;
12291 int playfield_height_corrected = playfield_ytiles * align_size;
12292 boolean is_playfield_mode = (i == GFX_SPECIAL_ARG_PLAYING ||
12293 i == GFX_SPECIAL_ARG_EDITOR);
12295 if (is_playfield_mode &&
12296 dynamic_playfield_width &&
12297 vp_playfield->width != playfield_width_corrected)
12299 int playfield_xdiff = vp_playfield->width - playfield_width_corrected;
12301 vp_playfield->width = playfield_width_corrected;
12303 if (vp_playfield->align == ALIGN_LEFT)
12305 vp_playfield->border_left += playfield_xdiff;
12307 else if (vp_playfield->align == ALIGN_RIGHT)
12309 vp_playfield->border_right += playfield_xdiff;
12311 else if (vp_playfield->align == ALIGN_CENTER)
12313 int border_left_diff = playfield_xdiff / 2;
12314 int border_right_diff = playfield_xdiff - border_left_diff;
12316 vp_playfield->border_left += border_left_diff;
12317 vp_playfield->border_right += border_right_diff;
12321 if (is_playfield_mode &&
12322 dynamic_playfield_height &&
12323 vp_playfield->height != playfield_height_corrected)
12325 int playfield_ydiff = vp_playfield->height - playfield_height_corrected;
12327 vp_playfield->height = playfield_height_corrected;
12329 if (vp_playfield->valign == VALIGN_TOP)
12331 vp_playfield->border_top += playfield_ydiff;
12333 else if (vp_playfield->align == VALIGN_BOTTOM)
12335 vp_playfield->border_right += playfield_ydiff;
12337 else if (vp_playfield->align == VALIGN_MIDDLE)
12339 int border_top_diff = playfield_ydiff / 2;
12340 int border_bottom_diff = playfield_ydiff - border_top_diff;
12342 vp_playfield->border_top += border_top_diff;
12343 vp_playfield->border_bottom += border_bottom_diff;
12347 // adjust door positions according to specified alignment
12349 for (j = 0; j < 2; j++)
12351 struct RectWithBorder *vp_door = (j == 0 ? vp_door_1 : vp_door_2);
12353 if (vp_door->align == ALIGN_LEFT || vp_door->x > 0)
12354 vp_door->x = ALIGNED_VP_XPOS(vp_door);
12355 else if (vp_door->align == ALIGN_CENTER)
12356 vp_door->x = vp_window->width / 2 - vp_door->width / 2;
12357 else if (vp_door->align == ALIGN_RIGHT)
12358 vp_door->x += vp_window->width - vp_door->width;
12360 if (vp_door->valign == VALIGN_TOP || vp_door->y > 0)
12361 vp_door->y = ALIGNED_VP_YPOS(vp_door);
12362 else if (vp_door->valign == VALIGN_MIDDLE)
12363 vp_door->y = vp_window->height / 2 - vp_door->height / 2;
12364 else if (vp_door->valign == VALIGN_BOTTOM)
12365 vp_door->y += vp_window->height - vp_door->height;
12370 static void InitMenuDesignSettings_SpecialPostProcessing_AfterGraphics(void)
12374 struct XYTileSize *dst, *src;
12377 editor_buttons_xy[] =
12380 &editor.button.element_left, &editor.palette.element_left,
12381 IMG_GFX_EDITOR_BUTTON_ELEMENT_LEFT
12384 &editor.button.element_middle, &editor.palette.element_middle,
12385 IMG_GFX_EDITOR_BUTTON_ELEMENT_MIDDLE
12388 &editor.button.element_right, &editor.palette.element_right,
12389 IMG_GFX_EDITOR_BUTTON_ELEMENT_RIGHT
12396 // set default position for element buttons to element graphics
12397 for (i = 0; editor_buttons_xy[i].dst != NULL; i++)
12399 if ((*editor_buttons_xy[i].dst).x == -1 &&
12400 (*editor_buttons_xy[i].dst).y == -1)
12402 struct GraphicInfo *gd = &graphic_info[editor_buttons_xy[i].graphic];
12404 gd->width = gd->height = editor_buttons_xy[i].src->tile_size;
12406 *editor_buttons_xy[i].dst = *editor_buttons_xy[i].src;
12410 // adjust editor palette rows and columns if specified to be dynamic
12412 if (editor.palette.cols == -1)
12414 int vp_width = viewport.playfield[GFX_SPECIAL_ARG_EDITOR].width;
12415 int bt_width = graphic_info[IMG_EDITOR_PALETTE_BUTTON].width;
12416 int sc_width = graphic_info[IMG_EDITOR_PALETTE_SCROLLBAR].width;
12418 editor.palette.cols = (vp_width - sc_width) / bt_width;
12420 if (editor.palette.x == -1)
12422 int palette_width = editor.palette.cols * bt_width + sc_width;
12424 editor.palette.x = (vp_width - palette_width) / 2;
12428 if (editor.palette.rows == -1)
12430 int vp_height = viewport.playfield[GFX_SPECIAL_ARG_EDITOR].height;
12431 int bt_height = graphic_info[IMG_EDITOR_PALETTE_BUTTON].height;
12432 int tx_height = getFontHeight(FONT_TEXT_2);
12434 editor.palette.rows = (vp_height - tx_height) / bt_height;
12436 if (editor.palette.y == -1)
12438 int palette_height = editor.palette.rows * bt_height + tx_height;
12440 editor.palette.y = (vp_height - palette_height) / 2;
12445 static void InitMenuDesignSettings_PreviewPlayers_Ext(SetupFileHash *hash,
12446 boolean initialize)
12448 // special case: check if network and preview player positions are redefined,
12449 // to compare this later against the main menu level preview being redefined
12450 struct TokenIntPtrInfo menu_config_players[] =
12452 { "main.network_players.x", &menu.main.network_players.redefined },
12453 { "main.network_players.y", &menu.main.network_players.redefined },
12454 { "main.preview_players.x", &menu.main.preview_players.redefined },
12455 { "main.preview_players.y", &menu.main.preview_players.redefined },
12456 { "preview.x", &preview.redefined },
12457 { "preview.y", &preview.redefined }
12463 for (i = 0; i < ARRAY_SIZE(menu_config_players); i++)
12464 *menu_config_players[i].value = FALSE;
12468 for (i = 0; i < ARRAY_SIZE(menu_config_players); i++)
12469 if (getHashEntry(hash, menu_config_players[i].token) != NULL)
12470 *menu_config_players[i].value = TRUE;
12474 static void InitMenuDesignSettings_PreviewPlayers(void)
12476 InitMenuDesignSettings_PreviewPlayers_Ext(NULL, TRUE);
12479 static void InitMenuDesignSettings_PreviewPlayers_FromHash(SetupFileHash *hash)
12481 InitMenuDesignSettings_PreviewPlayers_Ext(hash, FALSE);
12484 static void LoadMenuDesignSettingsFromFilename(char *filename)
12486 static struct TitleFadingInfo tfi;
12487 static struct TitleMessageInfo tmi;
12488 static struct TokenInfo title_tokens[] =
12490 { TYPE_INTEGER, &tfi.fade_mode, ".fade_mode" },
12491 { TYPE_INTEGER, &tfi.fade_delay, ".fade_delay" },
12492 { TYPE_INTEGER, &tfi.post_delay, ".post_delay" },
12493 { TYPE_INTEGER, &tfi.auto_delay, ".auto_delay" },
12494 { TYPE_INTEGER, &tfi.auto_delay_unit, ".auto_delay_unit" },
12498 static struct TokenInfo titlemessage_tokens[] =
12500 { TYPE_INTEGER, &tmi.x, ".x" },
12501 { TYPE_INTEGER, &tmi.y, ".y" },
12502 { TYPE_INTEGER, &tmi.width, ".width" },
12503 { TYPE_INTEGER, &tmi.height, ".height" },
12504 { TYPE_INTEGER, &tmi.chars, ".chars" },
12505 { TYPE_INTEGER, &tmi.lines, ".lines" },
12506 { TYPE_INTEGER, &tmi.align, ".align" },
12507 { TYPE_INTEGER, &tmi.valign, ".valign" },
12508 { TYPE_INTEGER, &tmi.font, ".font" },
12509 { TYPE_BOOLEAN, &tmi.autowrap, ".autowrap" },
12510 { TYPE_BOOLEAN, &tmi.centered, ".centered" },
12511 { TYPE_BOOLEAN, &tmi.parse_comments, ".parse_comments" },
12512 { TYPE_INTEGER, &tmi.sort_priority, ".sort_priority" },
12513 { TYPE_INTEGER, &tmi.fade_mode, ".fade_mode" },
12514 { TYPE_INTEGER, &tmi.fade_delay, ".fade_delay" },
12515 { TYPE_INTEGER, &tmi.post_delay, ".post_delay" },
12516 { TYPE_INTEGER, &tmi.auto_delay, ".auto_delay" },
12517 { TYPE_INTEGER, &tmi.auto_delay_unit, ".auto_delay_unit" },
12523 struct TitleFadingInfo *info;
12528 // initialize first titles from "enter screen" definitions, if defined
12529 { &title_initial_first_default, "menu.enter_screen.TITLE" },
12530 { &title_first_default, "menu.enter_screen.TITLE" },
12532 // initialize title screens from "next screen" definitions, if defined
12533 { &title_initial_default, "menu.next_screen.TITLE" },
12534 { &title_default, "menu.next_screen.TITLE" },
12540 struct TitleMessageInfo *array;
12543 titlemessage_arrays[] =
12545 // initialize first titles from "enter screen" definitions, if defined
12546 { titlescreen_initial_first, "menu.enter_screen.TITLE" },
12547 { titlescreen_first, "menu.enter_screen.TITLE" },
12548 { titlemessage_initial_first, "menu.enter_screen.TITLE" },
12549 { titlemessage_first, "menu.enter_screen.TITLE" },
12551 // initialize titles from "next screen" definitions, if defined
12552 { titlescreen_initial, "menu.next_screen.TITLE" },
12553 { titlescreen, "menu.next_screen.TITLE" },
12554 { titlemessage_initial, "menu.next_screen.TITLE" },
12555 { titlemessage, "menu.next_screen.TITLE" },
12557 // overwrite titles with title definitions, if defined
12558 { titlescreen_initial_first, "[title_initial]" },
12559 { titlescreen_first, "[title]" },
12560 { titlemessage_initial_first, "[title_initial]" },
12561 { titlemessage_first, "[title]" },
12563 { titlescreen_initial, "[title_initial]" },
12564 { titlescreen, "[title]" },
12565 { titlemessage_initial, "[title_initial]" },
12566 { titlemessage, "[title]" },
12568 // overwrite titles with title screen/message definitions, if defined
12569 { titlescreen_initial_first, "[titlescreen_initial]" },
12570 { titlescreen_first, "[titlescreen]" },
12571 { titlemessage_initial_first, "[titlemessage_initial]" },
12572 { titlemessage_first, "[titlemessage]" },
12574 { titlescreen_initial, "[titlescreen_initial]" },
12575 { titlescreen, "[titlescreen]" },
12576 { titlemessage_initial, "[titlemessage_initial]" },
12577 { titlemessage, "[titlemessage]" },
12581 SetupFileHash *setup_file_hash;
12584 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
12587 // the following initializes hierarchical values from dynamic configuration
12589 // special case: initialize with default values that may be overwritten
12590 // (e.g., init "menu.draw_xoffset.INFO" from "menu.draw_xoffset")
12591 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
12593 struct TokenIntPtrInfo menu_config[] =
12595 { "menu.draw_xoffset", &menu.draw_xoffset[i] },
12596 { "menu.draw_yoffset", &menu.draw_yoffset[i] },
12597 { "menu.list_size", &menu.list_size[i] }
12600 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
12602 char *token = menu_config[j].token;
12603 char *value = getHashEntry(setup_file_hash, token);
12606 *menu_config[j].value = get_integer_from_string(value);
12610 // special case: initialize with default values that may be overwritten
12611 // (eg, init "menu.draw_xoffset.INFO[XXX]" from "menu.draw_xoffset.INFO")
12612 for (i = 0; i < NUM_SPECIAL_GFX_INFO_ARGS; i++)
12614 struct TokenIntPtrInfo menu_config[] =
12616 { "menu.draw_xoffset.INFO", &menu.draw_xoffset_info[i] },
12617 { "menu.draw_yoffset.INFO", &menu.draw_yoffset_info[i] },
12618 { "menu.list_size.INFO", &menu.list_size_info[i] },
12619 { "menu.list_entry_size.INFO", &menu.list_entry_size_info[i] },
12620 { "menu.tile_size.INFO", &menu.tile_size_info[i] }
12623 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
12625 char *token = menu_config[j].token;
12626 char *value = getHashEntry(setup_file_hash, token);
12629 *menu_config[j].value = get_integer_from_string(value);
12633 // special case: initialize with default values that may be overwritten
12634 // (eg, init "menu.draw_xoffset.SETUP[XXX]" from "menu.draw_xoffset.SETUP")
12635 for (i = 0; i < NUM_SPECIAL_GFX_SETUP_ARGS; i++)
12637 struct TokenIntPtrInfo menu_config[] =
12639 { "menu.draw_xoffset.SETUP", &menu.draw_xoffset_setup[i] },
12640 { "menu.draw_yoffset.SETUP", &menu.draw_yoffset_setup[i] }
12643 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
12645 char *token = menu_config[j].token;
12646 char *value = getHashEntry(setup_file_hash, token);
12649 *menu_config[j].value = get_integer_from_string(value);
12653 // special case: initialize with default values that may be overwritten
12654 // (eg, init "menu.line_spacing.INFO[XXX]" from "menu.line_spacing.INFO")
12655 for (i = 0; i < NUM_SPECIAL_GFX_INFO_ARGS; i++)
12657 struct TokenIntPtrInfo menu_config[] =
12659 { "menu.left_spacing.INFO", &menu.left_spacing_info[i] },
12660 { "menu.middle_spacing.INFO", &menu.middle_spacing_info[i] },
12661 { "menu.right_spacing.INFO", &menu.right_spacing_info[i] },
12662 { "menu.top_spacing.INFO", &menu.top_spacing_info[i] },
12663 { "menu.bottom_spacing.INFO", &menu.bottom_spacing_info[i] },
12664 { "menu.paragraph_spacing.INFO", &menu.paragraph_spacing_info[i] },
12665 { "menu.headline1_spacing.INFO", &menu.headline1_spacing_info[i] },
12666 { "menu.headline2_spacing.INFO", &menu.headline2_spacing_info[i] },
12667 { "menu.line_spacing.INFO", &menu.line_spacing_info[i] },
12668 { "menu.extra_spacing.INFO", &menu.extra_spacing_info[i] },
12671 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
12673 char *token = menu_config[j].token;
12674 char *value = getHashEntry(setup_file_hash, token);
12677 *menu_config[j].value = get_integer_from_string(value);
12681 // special case: initialize with default values that may be overwritten
12682 // (eg, init "menu.enter_screen.SCORES.xyz" from "menu.enter_screen.xyz")
12683 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
12685 struct TokenIntPtrInfo menu_config[] =
12687 { "menu.enter_screen.fade_mode", &menu.enter_screen[i].fade_mode },
12688 { "menu.enter_screen.fade_delay", &menu.enter_screen[i].fade_delay },
12689 { "menu.enter_screen.post_delay", &menu.enter_screen[i].post_delay },
12690 { "menu.leave_screen.fade_mode", &menu.leave_screen[i].fade_mode },
12691 { "menu.leave_screen.fade_delay", &menu.leave_screen[i].fade_delay },
12692 { "menu.leave_screen.post_delay", &menu.leave_screen[i].post_delay },
12693 { "menu.next_screen.fade_mode", &menu.next_screen[i].fade_mode },
12694 { "menu.next_screen.fade_delay", &menu.next_screen[i].fade_delay },
12695 { "menu.next_screen.post_delay", &menu.next_screen[i].post_delay }
12698 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
12700 char *token = menu_config[j].token;
12701 char *value = getHashEntry(setup_file_hash, token);
12704 *menu_config[j].value = get_token_parameter_value(token, value);
12708 // special case: initialize with default values that may be overwritten
12709 // (eg, init "viewport.door_1.MAIN.xyz" from "viewport.door_1.xyz")
12710 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
12714 char *token_prefix;
12715 struct RectWithBorder *struct_ptr;
12719 { "viewport.window", &viewport.window[i] },
12720 { "viewport.playfield", &viewport.playfield[i] },
12721 { "viewport.door_1", &viewport.door_1[i] },
12722 { "viewport.door_2", &viewport.door_2[i] }
12725 for (j = 0; j < ARRAY_SIZE(vp_struct); j++)
12727 struct TokenIntPtrInfo vp_config[] =
12729 { ".x", &vp_struct[j].struct_ptr->x },
12730 { ".y", &vp_struct[j].struct_ptr->y },
12731 { ".width", &vp_struct[j].struct_ptr->width },
12732 { ".height", &vp_struct[j].struct_ptr->height },
12733 { ".min_width", &vp_struct[j].struct_ptr->min_width },
12734 { ".min_height", &vp_struct[j].struct_ptr->min_height },
12735 { ".max_width", &vp_struct[j].struct_ptr->max_width },
12736 { ".max_height", &vp_struct[j].struct_ptr->max_height },
12737 { ".margin_left", &vp_struct[j].struct_ptr->margin_left },
12738 { ".margin_right", &vp_struct[j].struct_ptr->margin_right },
12739 { ".margin_top", &vp_struct[j].struct_ptr->margin_top },
12740 { ".margin_bottom", &vp_struct[j].struct_ptr->margin_bottom },
12741 { ".border_left", &vp_struct[j].struct_ptr->border_left },
12742 { ".border_right", &vp_struct[j].struct_ptr->border_right },
12743 { ".border_top", &vp_struct[j].struct_ptr->border_top },
12744 { ".border_bottom", &vp_struct[j].struct_ptr->border_bottom },
12745 { ".border_size", &vp_struct[j].struct_ptr->border_size },
12746 { ".align_size", &vp_struct[j].struct_ptr->align_size },
12747 { ".align", &vp_struct[j].struct_ptr->align },
12748 { ".valign", &vp_struct[j].struct_ptr->valign }
12751 for (k = 0; k < ARRAY_SIZE(vp_config); k++)
12753 char *token = getStringCat2(vp_struct[j].token_prefix,
12754 vp_config[k].token);
12755 char *value = getHashEntry(setup_file_hash, token);
12758 *vp_config[k].value = get_token_parameter_value(token, value);
12765 // special case: initialize with default values that may be overwritten
12766 // (e.g., init "[title].fade_mode" from "menu.next_screen.TITLE.fade_mode")
12767 for (i = 0; title_info[i].info != NULL; i++)
12769 struct TitleFadingInfo *info = title_info[i].info;
12770 char *base_token = title_info[i].text;
12772 for (j = 0; title_tokens[j].type != -1; j++)
12774 char *token = getStringCat2(base_token, title_tokens[j].text);
12775 char *value = getHashEntry(setup_file_hash, token);
12779 int parameter_value = get_token_parameter_value(token, value);
12783 *(int *)title_tokens[j].value = (int)parameter_value;
12792 // special case: initialize with default values that may be overwritten
12793 // (e.g., init "titlemessage_1.fade_mode" from "[titlemessage].fade_mode")
12794 for (i = 0; titlemessage_arrays[i].array != NULL; i++)
12796 struct TitleMessageInfo *array = titlemessage_arrays[i].array;
12797 char *base_token = titlemessage_arrays[i].text;
12799 for (j = 0; titlemessage_tokens[j].type != -1; j++)
12801 char *token = getStringCat2(base_token, titlemessage_tokens[j].text);
12802 char *value = getHashEntry(setup_file_hash, token);
12806 int parameter_value = get_token_parameter_value(token, value);
12808 for (k = 0; k < MAX_NUM_TITLE_MESSAGES; k++)
12812 if (titlemessage_tokens[j].type == TYPE_INTEGER)
12813 *(int *)titlemessage_tokens[j].value = (int)parameter_value;
12815 *(boolean *)titlemessage_tokens[j].value = (boolean)parameter_value;
12825 // read (and overwrite with) values that may be specified in config file
12826 InitMenuDesignSettings_FromHash(setup_file_hash, TRUE);
12828 // special case: check if network and preview player positions are redefined
12829 InitMenuDesignSettings_PreviewPlayers_FromHash(setup_file_hash);
12831 freeSetupFileHash(setup_file_hash);
12834 void LoadMenuDesignSettings(void)
12836 char *filename_base = UNDEFINED_FILENAME, *filename_local;
12838 InitMenuDesignSettings_Static();
12839 InitMenuDesignSettings_SpecialPreProcessing();
12840 InitMenuDesignSettings_PreviewPlayers();
12842 if (!GFX_OVERRIDE_ARTWORK(ARTWORK_TYPE_GRAPHICS))
12844 // first look for special settings configured in level series config
12845 filename_base = getCustomArtworkLevelConfigFilename(ARTWORK_TYPE_GRAPHICS);
12847 if (fileExists(filename_base))
12848 LoadMenuDesignSettingsFromFilename(filename_base);
12851 filename_local = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
12853 if (filename_local != NULL && !strEqual(filename_base, filename_local))
12854 LoadMenuDesignSettingsFromFilename(filename_local);
12856 InitMenuDesignSettings_SpecialPostProcessing();
12859 void LoadMenuDesignSettings_AfterGraphics(void)
12861 InitMenuDesignSettings_SpecialPostProcessing_AfterGraphics();
12864 void LoadUserDefinedEditorElementList(int **elements, int *num_elements)
12866 char *filename = getEditorSetupFilename();
12867 SetupFileList *setup_file_list, *list;
12868 SetupFileHash *element_hash;
12869 int num_unknown_tokens = 0;
12872 if ((setup_file_list = loadSetupFileList(filename)) == NULL)
12875 element_hash = newSetupFileHash();
12877 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
12878 setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
12880 // determined size may be larger than needed (due to unknown elements)
12882 for (list = setup_file_list; list != NULL; list = list->next)
12885 // add space for up to 3 more elements for padding that may be needed
12886 *num_elements += 3;
12888 // free memory for old list of elements, if needed
12889 checked_free(*elements);
12891 // allocate memory for new list of elements
12892 *elements = checked_malloc(*num_elements * sizeof(int));
12895 for (list = setup_file_list; list != NULL; list = list->next)
12897 char *value = getHashEntry(element_hash, list->token);
12899 if (value == NULL) // try to find obsolete token mapping
12901 char *mapped_token = get_mapped_token(list->token);
12903 if (mapped_token != NULL)
12905 value = getHashEntry(element_hash, mapped_token);
12907 free(mapped_token);
12913 (*elements)[(*num_elements)++] = atoi(value);
12917 if (num_unknown_tokens == 0)
12920 Warn("unknown token(s) found in config file:");
12921 Warn("- config file: '%s'", filename);
12923 num_unknown_tokens++;
12926 Warn("- token: '%s'", list->token);
12930 if (num_unknown_tokens > 0)
12933 while (*num_elements % 4) // pad with empty elements, if needed
12934 (*elements)[(*num_elements)++] = EL_EMPTY;
12936 freeSetupFileList(setup_file_list);
12937 freeSetupFileHash(element_hash);
12940 for (i = 0; i < *num_elements; i++)
12941 Debug("editor", "element '%s' [%d]\n",
12942 element_info[(*elements)[i]].token_name, (*elements)[i]);
12946 static struct MusicFileInfo *get_music_file_info_ext(char *basename, int music,
12949 SetupFileHash *setup_file_hash = NULL;
12950 struct MusicFileInfo tmp_music_file_info, *new_music_file_info;
12951 char *filename_music, *filename_prefix, *filename_info;
12957 token_to_value_ptr[] =
12959 { "title_header", &tmp_music_file_info.title_header },
12960 { "artist_header", &tmp_music_file_info.artist_header },
12961 { "album_header", &tmp_music_file_info.album_header },
12962 { "year_header", &tmp_music_file_info.year_header },
12963 { "played_header", &tmp_music_file_info.played_header },
12965 { "title", &tmp_music_file_info.title },
12966 { "artist", &tmp_music_file_info.artist },
12967 { "album", &tmp_music_file_info.album },
12968 { "year", &tmp_music_file_info.year },
12969 { "played", &tmp_music_file_info.played },
12975 filename_music = (is_sound ? getCustomSoundFilename(basename) :
12976 getCustomMusicFilename(basename));
12978 if (filename_music == NULL)
12981 // ---------- try to replace file extension ----------
12983 filename_prefix = getStringCopy(filename_music);
12984 if (strrchr(filename_prefix, '.') != NULL)
12985 *strrchr(filename_prefix, '.') = '\0';
12986 filename_info = getStringCat2(filename_prefix, ".txt");
12988 if (fileExists(filename_info))
12989 setup_file_hash = loadSetupFileHash(filename_info);
12991 free(filename_prefix);
12992 free(filename_info);
12994 if (setup_file_hash == NULL)
12996 // ---------- try to add file extension ----------
12998 filename_prefix = getStringCopy(filename_music);
12999 filename_info = getStringCat2(filename_prefix, ".txt");
13001 if (fileExists(filename_info))
13002 setup_file_hash = loadSetupFileHash(filename_info);
13004 free(filename_prefix);
13005 free(filename_info);
13008 if (setup_file_hash == NULL)
13011 // ---------- music file info found ----------
13013 clear_mem(&tmp_music_file_info, sizeof(struct MusicFileInfo));
13015 for (i = 0; token_to_value_ptr[i].token != NULL; i++)
13017 char *value = getHashEntry(setup_file_hash, token_to_value_ptr[i].token);
13019 *token_to_value_ptr[i].value_ptr =
13020 getStringCopy(value != NULL && *value != '\0' ? value : UNKNOWN_NAME);
13023 tmp_music_file_info.basename = getStringCopy(basename);
13024 tmp_music_file_info.music = music;
13025 tmp_music_file_info.is_sound = is_sound;
13027 new_music_file_info = checked_malloc(sizeof(struct MusicFileInfo));
13028 *new_music_file_info = tmp_music_file_info;
13030 return new_music_file_info;
13033 static struct MusicFileInfo *get_music_file_info(char *basename, int music)
13035 return get_music_file_info_ext(basename, music, FALSE);
13038 static struct MusicFileInfo *get_sound_file_info(char *basename, int sound)
13040 return get_music_file_info_ext(basename, sound, TRUE);
13043 static boolean music_info_listed_ext(struct MusicFileInfo *list,
13044 char *basename, boolean is_sound)
13046 for (; list != NULL; list = list->next)
13047 if (list->is_sound == is_sound && strEqual(list->basename, basename))
13053 static boolean music_info_listed(struct MusicFileInfo *list, char *basename)
13055 return music_info_listed_ext(list, basename, FALSE);
13058 static boolean sound_info_listed(struct MusicFileInfo *list, char *basename)
13060 return music_info_listed_ext(list, basename, TRUE);
13063 void LoadMusicInfo(void)
13065 int num_music_noconf = getMusicListSize_NoConf();
13066 int num_music = getMusicListSize();
13067 int num_sounds = getSoundListSize();
13068 struct FileInfo *music, *sound;
13069 struct MusicFileInfo *next, **new;
13073 while (music_file_info != NULL)
13075 next = music_file_info->next;
13077 checked_free(music_file_info->basename);
13079 checked_free(music_file_info->title_header);
13080 checked_free(music_file_info->artist_header);
13081 checked_free(music_file_info->album_header);
13082 checked_free(music_file_info->year_header);
13083 checked_free(music_file_info->played_header);
13085 checked_free(music_file_info->title);
13086 checked_free(music_file_info->artist);
13087 checked_free(music_file_info->album);
13088 checked_free(music_file_info->year);
13089 checked_free(music_file_info->played);
13091 free(music_file_info);
13093 music_file_info = next;
13096 new = &music_file_info;
13098 // get (configured or unconfigured) music file info for all levels
13099 for (i = leveldir_current->first_level;
13100 i <= leveldir_current->last_level; i++)
13104 if (levelset.music[i] != MUS_UNDEFINED)
13106 // get music file info for configured level music
13107 music_nr = levelset.music[i];
13109 else if (num_music_noconf > 0)
13111 // get music file info for unconfigured level music
13112 int level_pos = i - leveldir_current->first_level;
13114 music_nr = MAP_NOCONF_MUSIC(level_pos % num_music_noconf);
13121 char *basename = getMusicInfoEntryFilename(music_nr);
13123 if (basename == NULL)
13126 if (!music_info_listed(music_file_info, basename))
13128 *new = get_music_file_info(basename, music_nr);
13131 new = &(*new)->next;
13135 // get music file info for all remaining configured music files
13136 for (i = 0; i < num_music; i++)
13138 music = getMusicListEntry(i);
13140 if (music->filename == NULL)
13143 if (strEqual(music->filename, UNDEFINED_FILENAME))
13146 // a configured file may be not recognized as music
13147 if (!FileIsMusic(music->filename))
13150 if (!music_info_listed(music_file_info, music->filename))
13152 *new = get_music_file_info(music->filename, i);
13155 new = &(*new)->next;
13159 // get sound file info for all configured sound files
13160 for (i = 0; i < num_sounds; i++)
13162 sound = getSoundListEntry(i);
13164 if (sound->filename == NULL)
13167 if (strEqual(sound->filename, UNDEFINED_FILENAME))
13170 // a configured file may be not recognized as sound
13171 if (!FileIsSound(sound->filename))
13174 if (!sound_info_listed(music_file_info, sound->filename))
13176 *new = get_sound_file_info(sound->filename, i);
13178 new = &(*new)->next;
13182 // add pointers to previous list nodes
13184 struct MusicFileInfo *node = music_file_info;
13186 while (node != NULL)
13189 node->next->prev = node;
13195 static void add_helpanim_entry(int element, int action, int direction,
13196 int delay, int *num_list_entries)
13198 struct HelpAnimInfo *new_list_entry;
13199 (*num_list_entries)++;
13202 checked_realloc(helpanim_info,
13203 *num_list_entries * sizeof(struct HelpAnimInfo));
13204 new_list_entry = &helpanim_info[*num_list_entries - 1];
13206 new_list_entry->element = element;
13207 new_list_entry->action = action;
13208 new_list_entry->direction = direction;
13209 new_list_entry->delay = delay;
13212 static void print_unknown_token(char *filename, char *token, int token_nr)
13217 Warn("unknown token(s) found in config file:");
13218 Warn("- config file: '%s'", filename);
13221 Warn("- token: '%s'", token);
13224 static void print_unknown_token_end(int token_nr)
13230 void LoadHelpAnimInfo(void)
13232 char *filename = getHelpAnimFilename();
13233 SetupFileList *setup_file_list = NULL, *list;
13234 SetupFileHash *element_hash, *action_hash, *direction_hash;
13235 int num_list_entries = 0;
13236 int num_unknown_tokens = 0;
13239 if (fileExists(filename))
13240 setup_file_list = loadSetupFileList(filename);
13242 if (setup_file_list == NULL)
13244 // use reliable default values from static configuration
13245 SetupFileList *insert_ptr;
13247 insert_ptr = setup_file_list =
13248 newSetupFileList(helpanim_config[0].token,
13249 helpanim_config[0].value);
13251 for (i = 1; helpanim_config[i].token; i++)
13252 insert_ptr = addListEntry(insert_ptr,
13253 helpanim_config[i].token,
13254 helpanim_config[i].value);
13257 element_hash = newSetupFileHash();
13258 action_hash = newSetupFileHash();
13259 direction_hash = newSetupFileHash();
13261 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
13262 setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
13264 for (i = 0; i < NUM_ACTIONS; i++)
13265 setHashEntry(action_hash, element_action_info[i].suffix,
13266 i_to_a(element_action_info[i].value));
13268 // do not store direction index (bit) here, but direction value!
13269 for (i = 0; i < NUM_DIRECTIONS_FULL; i++)
13270 setHashEntry(direction_hash, element_direction_info[i].suffix,
13271 i_to_a(1 << element_direction_info[i].value));
13273 for (list = setup_file_list; list != NULL; list = list->next)
13275 char *element_token, *action_token, *direction_token;
13276 char *element_value, *action_value, *direction_value;
13277 int delay = atoi(list->value);
13279 if (strEqual(list->token, "end"))
13281 add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
13286 /* first try to break element into element/action/direction parts;
13287 if this does not work, also accept combined "element[.act][.dir]"
13288 elements (like "dynamite.active"), which are unique elements */
13290 if (strchr(list->token, '.') == NULL) // token contains no '.'
13292 element_value = getHashEntry(element_hash, list->token);
13293 if (element_value != NULL) // element found
13294 add_helpanim_entry(atoi(element_value), -1, -1, delay,
13295 &num_list_entries);
13298 // no further suffixes found -- this is not an element
13299 print_unknown_token(filename, list->token, num_unknown_tokens++);
13305 // token has format "<prefix>.<something>"
13307 action_token = strchr(list->token, '.'); // suffix may be action ...
13308 direction_token = action_token; // ... or direction
13310 element_token = getStringCopy(list->token);
13311 *strchr(element_token, '.') = '\0';
13313 element_value = getHashEntry(element_hash, element_token);
13315 if (element_value == NULL) // this is no element
13317 element_value = getHashEntry(element_hash, list->token);
13318 if (element_value != NULL) // combined element found
13319 add_helpanim_entry(atoi(element_value), -1, -1, delay,
13320 &num_list_entries);
13322 print_unknown_token(filename, list->token, num_unknown_tokens++);
13324 free(element_token);
13329 action_value = getHashEntry(action_hash, action_token);
13331 if (action_value != NULL) // action found
13333 add_helpanim_entry(atoi(element_value), atoi(action_value), -1, delay,
13334 &num_list_entries);
13336 free(element_token);
13341 direction_value = getHashEntry(direction_hash, direction_token);
13343 if (direction_value != NULL) // direction found
13345 add_helpanim_entry(atoi(element_value), -1, atoi(direction_value), delay,
13346 &num_list_entries);
13348 free(element_token);
13353 if (strchr(action_token + 1, '.') == NULL)
13355 // no further suffixes found -- this is not an action nor direction
13357 element_value = getHashEntry(element_hash, list->token);
13358 if (element_value != NULL) // combined element found
13359 add_helpanim_entry(atoi(element_value), -1, -1, delay,
13360 &num_list_entries);
13362 print_unknown_token(filename, list->token, num_unknown_tokens++);
13364 free(element_token);
13369 // token has format "<prefix>.<suffix>.<something>"
13371 direction_token = strchr(action_token + 1, '.');
13373 action_token = getStringCopy(action_token);
13374 *strchr(action_token + 1, '.') = '\0';
13376 action_value = getHashEntry(action_hash, action_token);
13378 if (action_value == NULL) // this is no action
13380 element_value = getHashEntry(element_hash, list->token);
13381 if (element_value != NULL) // combined element found
13382 add_helpanim_entry(atoi(element_value), -1, -1, delay,
13383 &num_list_entries);
13385 print_unknown_token(filename, list->token, num_unknown_tokens++);
13387 free(element_token);
13388 free(action_token);
13393 direction_value = getHashEntry(direction_hash, direction_token);
13395 if (direction_value != NULL) // direction found
13397 add_helpanim_entry(atoi(element_value), atoi(action_value),
13398 atoi(direction_value), delay, &num_list_entries);
13400 free(element_token);
13401 free(action_token);
13406 // this is no direction
13408 element_value = getHashEntry(element_hash, list->token);
13409 if (element_value != NULL) // combined element found
13410 add_helpanim_entry(atoi(element_value), -1, -1, delay,
13411 &num_list_entries);
13413 print_unknown_token(filename, list->token, num_unknown_tokens++);
13415 free(element_token);
13416 free(action_token);
13419 print_unknown_token_end(num_unknown_tokens);
13421 add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
13422 add_helpanim_entry(HELPANIM_LIST_END, -1, -1, -1, &num_list_entries);
13424 freeSetupFileList(setup_file_list);
13425 freeSetupFileHash(element_hash);
13426 freeSetupFileHash(action_hash);
13427 freeSetupFileHash(direction_hash);
13430 for (i = 0; i < num_list_entries; i++)
13431 Debug("files:LoadHelpAnimInfo", "'%s': %d, %d, %d => %d",
13432 EL_NAME(helpanim_info[i].element),
13433 helpanim_info[i].element,
13434 helpanim_info[i].action,
13435 helpanim_info[i].direction,
13436 helpanim_info[i].delay);
13440 void LoadHelpTextInfo(void)
13442 char *filename = getHelpTextFilename();
13445 if (helptext_info != NULL)
13447 freeSetupFileHash(helptext_info);
13448 helptext_info = NULL;
13451 if (fileExists(filename))
13452 helptext_info = loadSetupFileHash(filename);
13454 if (helptext_info == NULL)
13456 // use reliable default values from static configuration
13457 helptext_info = newSetupFileHash();
13459 for (i = 0; helptext_config[i].token; i++)
13460 setHashEntry(helptext_info,
13461 helptext_config[i].token,
13462 helptext_config[i].value);
13466 BEGIN_HASH_ITERATION(helptext_info, itr)
13468 Debug("files:LoadHelpTextInfo", "'%s' => '%s'",
13469 HASH_ITERATION_TOKEN(itr), HASH_ITERATION_VALUE(itr));
13471 END_HASH_ITERATION(hash, itr)
13476 // ----------------------------------------------------------------------------
13478 // ----------------------------------------------------------------------------
13480 #define MAX_NUM_CONVERT_LEVELS 1000
13482 void ConvertLevels(void)
13484 static LevelDirTree *convert_leveldir = NULL;
13485 static int convert_level_nr = -1;
13486 static int num_levels_handled = 0;
13487 static int num_levels_converted = 0;
13488 static boolean levels_failed[MAX_NUM_CONVERT_LEVELS];
13491 convert_leveldir = getTreeInfoFromIdentifier(leveldir_first,
13492 global.convert_leveldir);
13494 if (convert_leveldir == NULL)
13495 Fail("no such level identifier: '%s'", global.convert_leveldir);
13497 leveldir_current = convert_leveldir;
13499 if (global.convert_level_nr != -1)
13501 convert_leveldir->first_level = global.convert_level_nr;
13502 convert_leveldir->last_level = global.convert_level_nr;
13505 convert_level_nr = convert_leveldir->first_level;
13507 PrintLine("=", 79);
13508 Print("Converting levels\n");
13509 PrintLine("-", 79);
13510 Print("Level series identifier: '%s'\n", convert_leveldir->identifier);
13511 Print("Level series name: '%s'\n", convert_leveldir->name);
13512 Print("Level series author: '%s'\n", convert_leveldir->author);
13513 Print("Number of levels: %d\n", convert_leveldir->levels);
13514 PrintLine("=", 79);
13517 for (i = 0; i < MAX_NUM_CONVERT_LEVELS; i++)
13518 levels_failed[i] = FALSE;
13520 while (convert_level_nr <= convert_leveldir->last_level)
13522 char *level_filename;
13525 level_nr = convert_level_nr++;
13527 Print("Level %03d: ", level_nr);
13529 LoadLevel(level_nr);
13530 if (level.no_level_file || level.no_valid_file)
13532 Print("(no level)\n");
13536 Print("converting level ... ");
13539 // special case: conversion of some EMC levels as requested by ACME
13540 level.game_engine_type = GAME_ENGINE_TYPE_RND;
13543 level_filename = getDefaultLevelFilename(level_nr);
13544 new_level = !fileExists(level_filename);
13548 SaveLevel(level_nr);
13550 num_levels_converted++;
13552 Print("converted.\n");
13556 if (level_nr >= 0 && level_nr < MAX_NUM_CONVERT_LEVELS)
13557 levels_failed[level_nr] = TRUE;
13559 Print("NOT CONVERTED -- LEVEL ALREADY EXISTS.\n");
13562 num_levels_handled++;
13566 PrintLine("=", 79);
13567 Print("Number of levels handled: %d\n", num_levels_handled);
13568 Print("Number of levels converted: %d (%d%%)\n", num_levels_converted,
13569 (num_levels_handled ?
13570 num_levels_converted * 100 / num_levels_handled : 0));
13571 PrintLine("-", 79);
13572 Print("Summary (for automatic parsing by scripts):\n");
13573 Print("LEVELDIR '%s', CONVERTED %d/%d (%d%%)",
13574 convert_leveldir->identifier, num_levels_converted,
13575 num_levels_handled,
13576 (num_levels_handled ?
13577 num_levels_converted * 100 / num_levels_handled : 0));
13579 if (num_levels_handled != num_levels_converted)
13581 Print(", FAILED:");
13582 for (i = 0; i < MAX_NUM_CONVERT_LEVELS; i++)
13583 if (levels_failed[i])
13588 PrintLine("=", 79);
13590 CloseAllAndExit(0);
13594 // ----------------------------------------------------------------------------
13595 // create and save images for use in level sketches (raw BMP format)
13596 // ----------------------------------------------------------------------------
13598 void CreateLevelSketchImages(void)
13604 InitElementPropertiesGfxElement();
13606 bitmap1 = CreateBitmap(TILEX, TILEY, DEFAULT_DEPTH);
13607 bitmap2 = CreateBitmap(MINI_TILEX, MINI_TILEY, DEFAULT_DEPTH);
13609 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
13611 int element = getMappedElement(i);
13612 char basename1[16];
13613 char basename2[16];
13617 sprintf(basename1, "%04d.bmp", i);
13618 sprintf(basename2, "%04ds.bmp", i);
13620 filename1 = getPath2(global.create_sketch_images_dir, basename1);
13621 filename2 = getPath2(global.create_sketch_images_dir, basename2);
13623 DrawSizedElement(0, 0, element, TILESIZE);
13624 BlitBitmap(drawto, bitmap1, SX, SY, TILEX, TILEY, 0, 0);
13626 if (SDL_SaveBMP(bitmap1->surface, filename1) != 0)
13627 Fail("cannot save level sketch image file '%s'", filename1);
13629 DrawSizedElement(0, 0, element, MINI_TILESIZE);
13630 BlitBitmap(drawto, bitmap2, SX, SY, MINI_TILEX, MINI_TILEY, 0, 0);
13632 if (SDL_SaveBMP(bitmap2->surface, filename2) != 0)
13633 Fail("cannot save level sketch image file '%s'", filename2);
13638 // create corresponding SQL statements (for normal and small images)
13641 printf("insert into phpbb_words values (NULL, '`%03d', '<IMG class=\"levelsketch\" src=\"/I/%04d.png\"/>');\n", i, i);
13642 printf("insert into phpbb_words values (NULL, '¸%03d', '<IMG class=\"levelsketch\" src=\"/I/%04ds.png\"/>');\n", i, i);
13645 printf("insert into phpbb_words values (NULL, '`%04d', '<IMG class=\"levelsketch\" src=\"/I/%04d.png\"/>');\n", i, i);
13646 printf("insert into phpbb_words values (NULL, '¸%04d', '<IMG class=\"levelsketch\" src=\"/I/%04ds.png\"/>');\n", i, i);
13648 // optional: create content for forum level sketch demonstration post
13650 fprintf(stderr, "%03d `%03d%c", i, i, (i % 10 < 9 ? ' ' : '\n'));
13653 FreeBitmap(bitmap1);
13654 FreeBitmap(bitmap2);
13657 fprintf(stderr, "\n");
13659 Info("%d normal and small images created", NUM_FILE_ELEMENTS);
13661 CloseAllAndExit(0);
13665 // ----------------------------------------------------------------------------
13666 // create and save images for element collecting animations (raw BMP format)
13667 // ----------------------------------------------------------------------------
13669 static boolean createCollectImage(int element)
13671 return (IS_COLLECTIBLE(element) && !IS_SP_ELEMENT(element));
13674 void CreateCollectElementImages(void)
13678 int anim_frames = num_steps - 1;
13679 int tile_size = TILESIZE;
13680 int anim_width = tile_size * anim_frames;
13681 int anim_height = tile_size;
13682 int num_collect_images = 0;
13683 int pos_collect_images = 0;
13685 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
13686 if (createCollectImage(i))
13687 num_collect_images++;
13689 Info("Creating %d element collecting animation images ...",
13690 num_collect_images);
13692 int dst_width = anim_width * 2;
13693 int dst_height = anim_height * num_collect_images / 2;
13694 Bitmap *dst_bitmap = CreateBitmap(dst_width, dst_height, DEFAULT_DEPTH);
13695 char *basename_bmp = "RocksCollect.bmp";
13696 char *basename_png = "RocksCollect.png";
13697 char *filename_bmp = getPath2(global.create_collect_images_dir, basename_bmp);
13698 char *filename_png = getPath2(global.create_collect_images_dir, basename_png);
13699 int len_filename_bmp = strlen(filename_bmp);
13700 int len_filename_png = strlen(filename_png);
13701 int max_command_len = MAX_FILENAME_LEN + len_filename_bmp + len_filename_png;
13702 char cmd_convert[max_command_len];
13704 snprintf(cmd_convert, max_command_len, "convert \"%s\" \"%s\"",
13708 // force using RGBA surface for destination bitmap
13709 SDL_SetColorKey(dst_bitmap->surface, SET_TRANSPARENT_PIXEL,
13710 SDL_MapRGB(dst_bitmap->surface->format, 0x00, 0x00, 0x00));
13712 dst_bitmap->surface =
13713 SDL_ConvertSurfaceFormat(dst_bitmap->surface, SDL_PIXELFORMAT_ARGB8888, 0);
13715 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
13717 if (!createCollectImage(i))
13720 int dst_x = (pos_collect_images / (num_collect_images / 2)) * anim_width;
13721 int dst_y = (pos_collect_images % (num_collect_images / 2)) * anim_height;
13722 int graphic = el2img(i);
13723 char *token_name = element_info[i].token_name;
13724 Bitmap *tmp_bitmap = CreateBitmap(tile_size, tile_size, DEFAULT_DEPTH);
13725 Bitmap *src_bitmap;
13728 Info("- creating collecting image for '%s' ...", token_name);
13730 getGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
13732 BlitBitmap(src_bitmap, tmp_bitmap, src_x, src_y,
13733 tile_size, tile_size, 0, 0);
13735 // force using RGBA surface for temporary bitmap (using transparent black)
13736 SDL_SetColorKey(tmp_bitmap->surface, SET_TRANSPARENT_PIXEL,
13737 SDL_MapRGB(tmp_bitmap->surface->format, 0x00, 0x00, 0x00));
13739 tmp_bitmap->surface =
13740 SDL_ConvertSurfaceFormat(tmp_bitmap->surface, SDL_PIXELFORMAT_ARGB8888, 0);
13742 tmp_bitmap->surface_masked = tmp_bitmap->surface;
13744 for (j = 0; j < anim_frames; j++)
13746 int frame_size_final = tile_size * (anim_frames - j) / num_steps;
13747 int frame_size = frame_size_final * num_steps;
13748 int offset = (tile_size - frame_size_final) / 2;
13749 Bitmap *frame_bitmap = ZoomBitmap(tmp_bitmap, frame_size, frame_size);
13751 while (frame_size > frame_size_final)
13755 Bitmap *half_bitmap = ZoomBitmap(frame_bitmap, frame_size, frame_size);
13757 FreeBitmap(frame_bitmap);
13759 frame_bitmap = half_bitmap;
13762 BlitBitmapMasked(frame_bitmap, dst_bitmap, 0, 0,
13763 frame_size_final, frame_size_final,
13764 dst_x + j * tile_size + offset, dst_y + offset);
13766 FreeBitmap(frame_bitmap);
13769 tmp_bitmap->surface_masked = NULL;
13771 FreeBitmap(tmp_bitmap);
13773 pos_collect_images++;
13776 if (SDL_SaveBMP(dst_bitmap->surface, filename_bmp) != 0)
13777 Fail("cannot save element collecting image file '%s'", filename_bmp);
13779 FreeBitmap(dst_bitmap);
13781 Info("Converting image file from BMP to PNG ...");
13783 if (system(cmd_convert) != 0)
13784 Fail("converting image file failed");
13786 unlink(filename_bmp);
13790 CloseAllAndExit(0);
13794 // ----------------------------------------------------------------------------
13795 // create and save images for custom and group elements (raw BMP format)
13796 // ----------------------------------------------------------------------------
13798 void CreateCustomElementImages(char *directory)
13800 char *src_basename = "RocksCE-template.ilbm";
13801 char *dst_basename = "RocksCE.bmp";
13802 char *src_filename = getPath2(directory, src_basename);
13803 char *dst_filename = getPath2(directory, dst_basename);
13804 Bitmap *src_bitmap;
13806 int yoffset_ce = 0;
13807 int yoffset_ge = (TILEY * NUM_CUSTOM_ELEMENTS / 16);
13810 InitVideoDefaults();
13812 ReCreateBitmap(&backbuffer, video.width, video.height);
13814 src_bitmap = LoadImage(src_filename);
13816 bitmap = CreateBitmap(TILEX * 16 * 2,
13817 TILEY * (NUM_CUSTOM_ELEMENTS + NUM_GROUP_ELEMENTS) / 16,
13820 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
13827 BlitBitmap(src_bitmap, bitmap, 0, 0, TILEX, TILEY,
13828 TILEX * x, TILEY * y + yoffset_ce);
13830 BlitBitmap(src_bitmap, bitmap, 0, TILEY,
13832 TILEX * x + TILEX * 16,
13833 TILEY * y + yoffset_ce);
13835 for (j = 2; j >= 0; j--)
13839 BlitBitmap(src_bitmap, bitmap,
13840 TILEX + c * 7, 0, 6, 10,
13841 TILEX * x + 6 + j * 7,
13842 TILEY * y + 11 + yoffset_ce);
13844 BlitBitmap(src_bitmap, bitmap,
13845 TILEX + c * 8, TILEY, 6, 10,
13846 TILEX * 16 + TILEX * x + 6 + j * 8,
13847 TILEY * y + 10 + yoffset_ce);
13853 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
13860 BlitBitmap(src_bitmap, bitmap, 0, 0, TILEX, TILEY,
13861 TILEX * x, TILEY * y + yoffset_ge);
13863 BlitBitmap(src_bitmap, bitmap, 0, TILEY,
13865 TILEX * x + TILEX * 16,
13866 TILEY * y + yoffset_ge);
13868 for (j = 1; j >= 0; j--)
13872 BlitBitmap(src_bitmap, bitmap, TILEX + c * 10, 11, 10, 10,
13873 TILEX * x + 6 + j * 10,
13874 TILEY * y + 11 + yoffset_ge);
13876 BlitBitmap(src_bitmap, bitmap,
13877 TILEX + c * 8, TILEY + 12, 6, 10,
13878 TILEX * 16 + TILEX * x + 10 + j * 8,
13879 TILEY * y + 10 + yoffset_ge);
13885 if (SDL_SaveBMP(bitmap->surface, dst_filename) != 0)
13886 Fail("cannot save CE graphics file '%s'", dst_filename);
13888 FreeBitmap(bitmap);
13890 CloseAllAndExit(0);