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_BD();
1775 setLevelInfoToDefaults_EM();
1776 setLevelInfoToDefaults_SP();
1777 setLevelInfoToDefaults_MM();
1779 level->native_bd_level = &native_bd_level;
1780 level->native_em_level = &native_em_level;
1781 level->native_sp_level = &native_sp_level;
1782 level->native_mm_level = &native_mm_level;
1784 level->file_version = FILE_VERSION_ACTUAL;
1785 level->game_version = GAME_VERSION_ACTUAL;
1787 level->creation_date = getCurrentDate();
1789 level->encoding_16bit_field = TRUE;
1790 level->encoding_16bit_yamyam = TRUE;
1791 level->encoding_16bit_amoeba = TRUE;
1793 // clear level name and level author string buffers
1794 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
1795 level->name[i] = '\0';
1796 for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
1797 level->author[i] = '\0';
1799 // set level name and level author to default values
1800 strcpy(level->name, NAMELESS_LEVEL_NAME);
1801 strcpy(level->author, ANONYMOUS_NAME);
1803 // set level playfield to playable default level with player and exit
1804 for (x = 0; x < MAX_LEV_FIELDX; x++)
1805 for (y = 0; y < MAX_LEV_FIELDY; y++)
1806 level->field[x][y] = EL_SAND;
1808 level->field[0][0] = EL_PLAYER_1;
1809 level->field[STD_LEV_FIELDX - 1][STD_LEV_FIELDY - 1] = EL_EXIT_CLOSED;
1811 BorderElement = EL_STEELWALL;
1813 // detect custom elements when loading them
1814 level->file_has_custom_elements = FALSE;
1816 // set all bug compatibility flags to "false" => do not emulate this bug
1817 level->use_action_after_change_bug = FALSE;
1819 if (leveldir_current)
1821 // try to determine better author name than 'anonymous'
1822 if (!strEqual(leveldir_current->author, ANONYMOUS_NAME))
1824 strncpy(level->author, leveldir_current->author, MAX_LEVEL_AUTHOR_LEN);
1825 level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
1829 switch (LEVELCLASS(leveldir_current))
1831 case LEVELCLASS_TUTORIAL:
1832 strcpy(level->author, PROGRAM_AUTHOR_STRING);
1835 case LEVELCLASS_CONTRIB:
1836 strncpy(level->author, leveldir_current->name, MAX_LEVEL_AUTHOR_LEN);
1837 level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
1840 case LEVELCLASS_PRIVATE:
1841 strncpy(level->author, getRealName(), MAX_LEVEL_AUTHOR_LEN);
1842 level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
1846 // keep default value
1853 static void setLevelInfoToDefaults_Elements(struct LevelInfo *level)
1855 static boolean clipboard_elements_initialized = FALSE;
1858 InitElementPropertiesStatic();
1860 li = *level; // copy level data into temporary buffer
1861 setConfigToDefaultsFromConfigList(chunk_config_ELEM);
1862 *level = li; // copy temporary buffer back to level data
1864 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1867 struct ElementInfo *ei = &element_info[element];
1869 if (element == EL_MM_GRAY_BALL)
1871 struct LevelInfo_MM *level_mm = level->native_mm_level;
1874 for (j = 0; j < level->num_mm_ball_contents; j++)
1875 level->mm_ball_content[j] =
1876 map_element_MM_to_RND(level_mm->ball_content[j]);
1879 // never initialize clipboard elements after the very first time
1880 // (to be able to use clipboard elements between several levels)
1881 if (IS_CLIPBOARD_ELEMENT(element) && clipboard_elements_initialized)
1884 if (IS_ENVELOPE(element))
1886 int envelope_nr = element - EL_ENVELOPE_1;
1888 setConfigToDefaultsFromConfigList(chunk_config_NOTE);
1890 level->envelope[envelope_nr] = xx_envelope;
1893 if (IS_CUSTOM_ELEMENT(element) ||
1894 IS_GROUP_ELEMENT(element) ||
1895 IS_INTERNAL_ELEMENT(element))
1897 xx_ei = *ei; // copy element data into temporary buffer
1899 setConfigToDefaultsFromConfigList(chunk_config_CUSX_base);
1904 setElementChangePages(ei, 1);
1905 setElementChangeInfoToDefaults(ei->change);
1907 if (IS_CUSTOM_ELEMENT(element) ||
1908 IS_GROUP_ELEMENT(element))
1910 setElementDescriptionToDefault(ei);
1912 ei->modified_settings = FALSE;
1915 if (IS_CUSTOM_ELEMENT(element) ||
1916 IS_INTERNAL_ELEMENT(element))
1918 // internal values used in level editor
1920 ei->access_type = 0;
1921 ei->access_layer = 0;
1922 ei->access_protected = 0;
1923 ei->walk_to_action = 0;
1924 ei->smash_targets = 0;
1927 ei->can_explode_by_fire = FALSE;
1928 ei->can_explode_smashed = FALSE;
1929 ei->can_explode_impact = FALSE;
1931 ei->current_change_page = 0;
1934 if (IS_GROUP_ELEMENT(element) ||
1935 IS_INTERNAL_ELEMENT(element))
1937 struct ElementGroupInfo *group;
1939 // initialize memory for list of elements in group
1940 if (ei->group == NULL)
1941 ei->group = checked_malloc(sizeof(struct ElementGroupInfo));
1945 xx_group = *group; // copy group data into temporary buffer
1947 setConfigToDefaultsFromConfigList(chunk_config_GRPX);
1952 if (IS_EMPTY_ELEMENT(element) ||
1953 IS_INTERNAL_ELEMENT(element))
1955 xx_ei = *ei; // copy element data into temporary buffer
1957 setConfigToDefaultsFromConfigList(chunk_config_EMPX);
1963 clipboard_elements_initialized = TRUE;
1966 static void setLevelInfoToDefaults(struct LevelInfo *level,
1967 boolean level_info_only,
1968 boolean reset_file_status)
1970 setLevelInfoToDefaults_Level(level);
1972 if (!level_info_only)
1973 setLevelInfoToDefaults_Elements(level);
1975 if (reset_file_status)
1977 level->no_valid_file = FALSE;
1978 level->no_level_file = FALSE;
1981 level->changed = FALSE;
1984 static void setFileInfoToDefaults(struct LevelFileInfo *level_file_info)
1986 level_file_info->nr = 0;
1987 level_file_info->type = LEVEL_FILE_TYPE_UNKNOWN;
1988 level_file_info->packed = FALSE;
1990 setString(&level_file_info->basename, NULL);
1991 setString(&level_file_info->filename, NULL);
1994 int getMappedElement_SB(int, boolean);
1996 static void ActivateLevelTemplate(void)
2000 if (check_special_flags("load_xsb_to_ces"))
2002 // fill smaller playfields with padding "beyond border wall" elements
2003 if (level.fieldx < level_template.fieldx ||
2004 level.fieldy < level_template.fieldy)
2006 short field[level.fieldx][level.fieldy];
2007 int new_fieldx = MAX(level.fieldx, level_template.fieldx);
2008 int new_fieldy = MAX(level.fieldy, level_template.fieldy);
2009 int pos_fieldx = (new_fieldx - level.fieldx) / 2;
2010 int pos_fieldy = (new_fieldy - level.fieldy) / 2;
2012 // copy old playfield (which is smaller than the visible area)
2013 for (y = 0; y < level.fieldy; y++) for (x = 0; x < level.fieldx; x++)
2014 field[x][y] = level.field[x][y];
2016 // fill new, larger playfield with "beyond border wall" elements
2017 for (y = 0; y < new_fieldy; y++) for (x = 0; x < new_fieldx; x++)
2018 level.field[x][y] = getMappedElement_SB('_', TRUE);
2020 // copy the old playfield to the middle of the new playfield
2021 for (y = 0; y < level.fieldy; y++) for (x = 0; x < level.fieldx; x++)
2022 level.field[pos_fieldx + x][pos_fieldy + y] = field[x][y];
2024 level.fieldx = new_fieldx;
2025 level.fieldy = new_fieldy;
2029 // Currently there is no special action needed to activate the template
2030 // data, because 'element_info' property settings overwrite the original
2031 // level data, while all other variables do not change.
2033 // Exception: 'from_level_template' elements in the original level playfield
2034 // are overwritten with the corresponding elements at the same position in
2035 // playfield from the level template.
2037 for (x = 0; x < level.fieldx; x++)
2038 for (y = 0; y < level.fieldy; y++)
2039 if (level.field[x][y] == EL_FROM_LEVEL_TEMPLATE)
2040 level.field[x][y] = level_template.field[x][y];
2042 if (check_special_flags("load_xsb_to_ces"))
2044 struct LevelInfo level_backup = level;
2046 // overwrite all individual level settings from template level settings
2047 level = level_template;
2049 // restore level file info
2050 level.file_info = level_backup.file_info;
2052 // restore playfield size
2053 level.fieldx = level_backup.fieldx;
2054 level.fieldy = level_backup.fieldy;
2056 // restore playfield content
2057 for (x = 0; x < level.fieldx; x++)
2058 for (y = 0; y < level.fieldy; y++)
2059 level.field[x][y] = level_backup.field[x][y];
2061 // restore name and author from individual level
2062 strcpy(level.name, level_backup.name);
2063 strcpy(level.author, level_backup.author);
2065 // restore flag "use_custom_template"
2066 level.use_custom_template = level_backup.use_custom_template;
2070 static char *getLevelFilenameFromBasename(char *basename)
2072 static char *filename = NULL;
2074 checked_free(filename);
2076 filename = getPath2(getCurrentLevelDir(), basename);
2081 static int getFileTypeFromBasename(char *basename)
2083 // !!! ALSO SEE COMMENT IN checkForPackageFromBasename() !!!
2085 static char *filename = NULL;
2086 struct stat file_status;
2088 // ---------- try to determine file type from filename ----------
2090 // check for typical filename of a Supaplex level package file
2091 if (strlen(basename) == 10 && strPrefixLower(basename, "levels.d"))
2092 return LEVEL_FILE_TYPE_SP;
2094 // check for typical filename of a Diamond Caves II level package file
2095 if (strSuffixLower(basename, ".dc") ||
2096 strSuffixLower(basename, ".dc2"))
2097 return LEVEL_FILE_TYPE_DC;
2099 // check for typical filename of a Sokoban level package file
2100 if (strSuffixLower(basename, ".xsb") &&
2101 strchr(basename, '%') == NULL)
2102 return LEVEL_FILE_TYPE_SB;
2104 // ---------- try to determine file type from filesize ----------
2106 checked_free(filename);
2107 filename = getPath2(getCurrentLevelDir(), basename);
2109 if (stat(filename, &file_status) == 0)
2111 // check for typical filesize of a Supaplex level package file
2112 if (file_status.st_size == 170496)
2113 return LEVEL_FILE_TYPE_SP;
2116 return LEVEL_FILE_TYPE_UNKNOWN;
2119 static int getFileTypeFromMagicBytes(char *filename, int type)
2123 if ((file = openFile(filename, MODE_READ)))
2125 char chunk_name[CHUNK_ID_LEN + 1];
2127 getFileChunkBE(file, chunk_name, NULL);
2129 if (strEqual(chunk_name, "MMII") ||
2130 strEqual(chunk_name, "MIRR"))
2131 type = LEVEL_FILE_TYPE_MM;
2139 static boolean checkForPackageFromBasename(char *basename)
2141 // !!! WON'T WORK ANYMORE IF getFileTypeFromBasename() ALSO DETECTS !!!
2142 // !!! SINGLE LEVELS (CURRENTLY ONLY DETECTS LEVEL PACKAGES !!!
2144 return (getFileTypeFromBasename(basename) != LEVEL_FILE_TYPE_UNKNOWN);
2147 static char *getSingleLevelBasenameExt(int nr, char *extension)
2149 static char basename[MAX_FILENAME_LEN];
2152 sprintf(basename, "%s", LEVELTEMPLATE_FILENAME);
2154 sprintf(basename, "%03d.%s", nr, extension);
2159 static char *getSingleLevelBasename(int nr)
2161 return getSingleLevelBasenameExt(nr, LEVELFILE_EXTENSION);
2164 static char *getPackedLevelBasename(int type)
2166 static char basename[MAX_FILENAME_LEN];
2167 char *directory = getCurrentLevelDir();
2169 DirectoryEntry *dir_entry;
2171 strcpy(basename, UNDEFINED_FILENAME); // default: undefined file
2173 if ((dir = openDirectory(directory)) == NULL)
2175 Warn("cannot read current level directory '%s'", directory);
2180 while ((dir_entry = readDirectory(dir)) != NULL) // loop all entries
2182 char *entry_basename = dir_entry->basename;
2183 int entry_type = getFileTypeFromBasename(entry_basename);
2185 if (entry_type != LEVEL_FILE_TYPE_UNKNOWN) // found valid level package
2187 if (type == LEVEL_FILE_TYPE_UNKNOWN ||
2190 strcpy(basename, entry_basename);
2197 closeDirectory(dir);
2202 static char *getSingleLevelFilename(int nr)
2204 return getLevelFilenameFromBasename(getSingleLevelBasename(nr));
2207 #if ENABLE_UNUSED_CODE
2208 static char *getPackedLevelFilename(int type)
2210 return getLevelFilenameFromBasename(getPackedLevelBasename(type));
2214 char *getDefaultLevelFilename(int nr)
2216 return getSingleLevelFilename(nr);
2219 #if ENABLE_UNUSED_CODE
2220 static void setLevelFileInfo_SingleLevelFilename(struct LevelFileInfo *lfi,
2224 lfi->packed = FALSE;
2226 setString(&lfi->basename, getSingleLevelBasename(lfi->nr, lfi->type));
2227 setString(&lfi->filename, getLevelFilenameFromBasename(lfi->basename));
2231 static void setLevelFileInfo_FormatLevelFilename(struct LevelFileInfo *lfi,
2232 int type, char *format, ...)
2234 static char basename[MAX_FILENAME_LEN];
2237 va_start(ap, format);
2238 vsprintf(basename, format, ap);
2242 lfi->packed = FALSE;
2244 setString(&lfi->basename, basename);
2245 setString(&lfi->filename, getLevelFilenameFromBasename(lfi->basename));
2248 static void setLevelFileInfo_PackedLevelFilename(struct LevelFileInfo *lfi,
2254 setString(&lfi->basename, getPackedLevelBasename(lfi->type));
2255 setString(&lfi->filename, getLevelFilenameFromBasename(lfi->basename));
2258 static int getFiletypeFromID(char *filetype_id)
2260 char *filetype_id_lower;
2261 int filetype = LEVEL_FILE_TYPE_UNKNOWN;
2264 if (filetype_id == NULL)
2265 return LEVEL_FILE_TYPE_UNKNOWN;
2267 filetype_id_lower = getStringToLower(filetype_id);
2269 for (i = 0; filetype_id_list[i].id != NULL; i++)
2271 char *id_lower = getStringToLower(filetype_id_list[i].id);
2273 if (strEqual(filetype_id_lower, id_lower))
2274 filetype = filetype_id_list[i].filetype;
2278 if (filetype != LEVEL_FILE_TYPE_UNKNOWN)
2282 free(filetype_id_lower);
2287 char *getLocalLevelTemplateFilename(void)
2289 return getDefaultLevelFilename(-1);
2292 char *getGlobalLevelTemplateFilename(void)
2294 // global variable "leveldir_current" must be modified in the loop below
2295 LevelDirTree *leveldir_current_last = leveldir_current;
2296 char *filename = NULL;
2298 // check for template level in path from current to topmost tree node
2300 while (leveldir_current != NULL)
2302 filename = getDefaultLevelFilename(-1);
2304 if (fileExists(filename))
2307 leveldir_current = leveldir_current->node_parent;
2310 // restore global variable "leveldir_current" modified in above loop
2311 leveldir_current = leveldir_current_last;
2316 static void determineLevelFileInfo_Filename(struct LevelFileInfo *lfi)
2320 // special case: level number is negative => check for level template file
2323 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
2324 getSingleLevelBasename(-1));
2326 // replace local level template filename with global template filename
2327 setString(&lfi->filename, getGlobalLevelTemplateFilename());
2329 // no fallback if template file not existing
2333 // special case: check for file name/pattern specified in "levelinfo.conf"
2334 if (leveldir_current->level_filename != NULL)
2336 int filetype = getFiletypeFromID(leveldir_current->level_filetype);
2338 setLevelFileInfo_FormatLevelFilename(lfi, filetype,
2339 leveldir_current->level_filename, nr);
2341 lfi->packed = checkForPackageFromBasename(leveldir_current->level_filename);
2343 if (fileExists(lfi->filename))
2346 else if (leveldir_current->level_filetype != NULL)
2348 int filetype = getFiletypeFromID(leveldir_current->level_filetype);
2350 // check for specified native level file with standard file name
2351 setLevelFileInfo_FormatLevelFilename(lfi, filetype,
2352 "%03d.%s", nr, LEVELFILE_EXTENSION);
2353 if (fileExists(lfi->filename))
2357 // check for native Rocks'n'Diamonds level file
2358 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
2359 "%03d.%s", nr, LEVELFILE_EXTENSION);
2360 if (fileExists(lfi->filename))
2363 // check for Emerald Mine level file (V1)
2364 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "a%c%c",
2365 'a' + (nr / 10) % 26, '0' + nr % 10);
2366 if (fileExists(lfi->filename))
2368 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "A%c%c",
2369 'A' + (nr / 10) % 26, '0' + nr % 10);
2370 if (fileExists(lfi->filename))
2373 // check for Emerald Mine level file (V2 to V5)
2374 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%d", nr);
2375 if (fileExists(lfi->filename))
2378 // check for Emerald Mine level file (V6 / single mode)
2379 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02ds", nr);
2380 if (fileExists(lfi->filename))
2382 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02dS", nr);
2383 if (fileExists(lfi->filename))
2386 // check for Emerald Mine level file (V6 / teamwork mode)
2387 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02dt", nr);
2388 if (fileExists(lfi->filename))
2390 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02dT", nr);
2391 if (fileExists(lfi->filename))
2394 // check for various packed level file formats
2395 setLevelFileInfo_PackedLevelFilename(lfi, LEVEL_FILE_TYPE_UNKNOWN);
2396 if (fileExists(lfi->filename))
2399 // no known level file found -- use default values (and fail later)
2400 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
2401 "%03d.%s", nr, LEVELFILE_EXTENSION);
2404 static void determineLevelFileInfo_Filetype(struct LevelFileInfo *lfi)
2406 if (lfi->type == LEVEL_FILE_TYPE_UNKNOWN)
2407 lfi->type = getFileTypeFromBasename(lfi->basename);
2409 if (lfi->type == LEVEL_FILE_TYPE_RND)
2410 lfi->type = getFileTypeFromMagicBytes(lfi->filename, lfi->type);
2413 static void setLevelFileInfo(struct LevelFileInfo *level_file_info, int nr)
2415 // always start with reliable default values
2416 setFileInfoToDefaults(level_file_info);
2418 level_file_info->nr = nr; // set requested level number
2420 determineLevelFileInfo_Filename(level_file_info);
2421 determineLevelFileInfo_Filetype(level_file_info);
2424 static void copyLevelFileInfo(struct LevelFileInfo *lfi_from,
2425 struct LevelFileInfo *lfi_to)
2427 lfi_to->nr = lfi_from->nr;
2428 lfi_to->type = lfi_from->type;
2429 lfi_to->packed = lfi_from->packed;
2431 setString(&lfi_to->basename, lfi_from->basename);
2432 setString(&lfi_to->filename, lfi_from->filename);
2435 // ----------------------------------------------------------------------------
2436 // functions for loading R'n'D level
2437 // ----------------------------------------------------------------------------
2439 int getMappedElement(int element)
2441 // remap some (historic, now obsolete) elements
2445 case EL_PLAYER_OBSOLETE:
2446 element = EL_PLAYER_1;
2449 case EL_KEY_OBSOLETE:
2453 case EL_EM_KEY_1_FILE_OBSOLETE:
2454 element = EL_EM_KEY_1;
2457 case EL_EM_KEY_2_FILE_OBSOLETE:
2458 element = EL_EM_KEY_2;
2461 case EL_EM_KEY_3_FILE_OBSOLETE:
2462 element = EL_EM_KEY_3;
2465 case EL_EM_KEY_4_FILE_OBSOLETE:
2466 element = EL_EM_KEY_4;
2469 case EL_ENVELOPE_OBSOLETE:
2470 element = EL_ENVELOPE_1;
2478 if (element >= NUM_FILE_ELEMENTS)
2480 Warn("invalid level element %d", element);
2482 element = EL_UNKNOWN;
2490 static int getMappedElementByVersion(int element, int game_version)
2492 // remap some elements due to certain game version
2494 if (game_version <= VERSION_IDENT(2,2,0,0))
2496 // map game font elements
2497 element = (element == EL_CHAR('[') ? EL_CHAR_AUMLAUT :
2498 element == EL_CHAR('\\') ? EL_CHAR_OUMLAUT :
2499 element == EL_CHAR(']') ? EL_CHAR_UUMLAUT :
2500 element == EL_CHAR('^') ? EL_CHAR_COPYRIGHT : element);
2503 if (game_version < VERSION_IDENT(3,0,0,0))
2505 // map Supaplex gravity tube elements
2506 element = (element == EL_SP_GRAVITY_PORT_LEFT ? EL_SP_PORT_LEFT :
2507 element == EL_SP_GRAVITY_PORT_RIGHT ? EL_SP_PORT_RIGHT :
2508 element == EL_SP_GRAVITY_PORT_UP ? EL_SP_PORT_UP :
2509 element == EL_SP_GRAVITY_PORT_DOWN ? EL_SP_PORT_DOWN :
2516 static int LoadLevel_VERS(File *file, int chunk_size, struct LevelInfo *level)
2518 level->file_version = getFileVersion(file);
2519 level->game_version = getFileVersion(file);
2524 static int LoadLevel_DATE(File *file, int chunk_size, struct LevelInfo *level)
2526 level->creation_date.year = getFile16BitBE(file);
2527 level->creation_date.month = getFile8Bit(file);
2528 level->creation_date.day = getFile8Bit(file);
2530 level->creation_date.src = DATE_SRC_LEVELFILE;
2535 static int LoadLevel_HEAD(File *file, int chunk_size, struct LevelInfo *level)
2537 int initial_player_stepsize;
2538 int initial_player_gravity;
2541 level->fieldx = getFile8Bit(file);
2542 level->fieldy = getFile8Bit(file);
2544 level->time = getFile16BitBE(file);
2545 level->gems_needed = getFile16BitBE(file);
2547 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
2548 level->name[i] = getFile8Bit(file);
2549 level->name[MAX_LEVEL_NAME_LEN] = 0;
2551 for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
2552 level->score[i] = getFile8Bit(file);
2554 level->num_yamyam_contents = STD_ELEMENT_CONTENTS;
2555 for (i = 0; i < STD_ELEMENT_CONTENTS; i++)
2556 for (y = 0; y < 3; y++)
2557 for (x = 0; x < 3; x++)
2558 level->yamyam_content[i].e[x][y] = getMappedElement(getFile8Bit(file));
2560 level->amoeba_speed = getFile8Bit(file);
2561 level->time_magic_wall = getFile8Bit(file);
2562 level->time_wheel = getFile8Bit(file);
2563 level->amoeba_content = getMappedElement(getFile8Bit(file));
2565 initial_player_stepsize = (getFile8Bit(file) == 1 ? STEPSIZE_FAST :
2568 for (i = 0; i < MAX_PLAYERS; i++)
2569 level->initial_player_stepsize[i] = initial_player_stepsize;
2571 initial_player_gravity = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2573 for (i = 0; i < MAX_PLAYERS; i++)
2574 level->initial_player_gravity[i] = initial_player_gravity;
2576 level->encoding_16bit_field = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2577 level->em_slippery_gems = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2579 level->use_custom_template = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2581 level->block_last_field = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2582 level->sp_block_last_field = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2583 level->can_move_into_acid_bits = getFile32BitBE(file);
2584 level->dont_collide_with_bits = getFile8Bit(file);
2586 level->use_spring_bug = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2587 level->use_step_counter = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2589 level->instant_relocation = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2590 level->can_pass_to_walkable = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2591 level->grow_into_diggable = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2593 level->game_engine_type = getFile8Bit(file);
2595 ReadUnusedBytesFromFile(file, LEVEL_CHUNK_HEAD_UNUSED);
2600 static int LoadLevel_NAME(File *file, int chunk_size, struct LevelInfo *level)
2604 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
2605 level->name[i] = getFile8Bit(file);
2606 level->name[MAX_LEVEL_NAME_LEN] = 0;
2611 static int LoadLevel_AUTH(File *file, int chunk_size, struct LevelInfo *level)
2615 for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
2616 level->author[i] = getFile8Bit(file);
2617 level->author[MAX_LEVEL_AUTHOR_LEN] = 0;
2622 static int LoadLevel_BODY(File *file, int chunk_size, struct LevelInfo *level)
2625 int chunk_size_expected = level->fieldx * level->fieldy;
2627 /* Note: "chunk_size" was wrong before version 2.0 when elements are
2628 stored with 16-bit encoding (and should be twice as big then).
2629 Even worse, playfield data was stored 16-bit when only yamyam content
2630 contained 16-bit elements and vice versa. */
2632 if (level->encoding_16bit_field && level->file_version >= FILE_VERSION_2_0)
2633 chunk_size_expected *= 2;
2635 if (chunk_size_expected != chunk_size)
2637 ReadUnusedBytesFromFile(file, chunk_size);
2638 return chunk_size_expected;
2641 for (y = 0; y < level->fieldy; y++)
2642 for (x = 0; x < level->fieldx; x++)
2643 level->field[x][y] =
2644 getMappedElement(level->encoding_16bit_field ? getFile16BitBE(file) :
2649 static int LoadLevel_CONT(File *file, int chunk_size, struct LevelInfo *level)
2652 int header_size = 4;
2653 int content_size = MAX_ELEMENT_CONTENTS * 3 * 3;
2654 int chunk_size_expected = header_size + content_size;
2656 /* Note: "chunk_size" was wrong before version 2.0 when elements are
2657 stored with 16-bit encoding (and should be twice as big then).
2658 Even worse, playfield data was stored 16-bit when only yamyam content
2659 contained 16-bit elements and vice versa. */
2661 if (level->encoding_16bit_field && level->file_version >= FILE_VERSION_2_0)
2662 chunk_size_expected += content_size;
2664 if (chunk_size_expected != chunk_size)
2666 ReadUnusedBytesFromFile(file, chunk_size);
2667 return chunk_size_expected;
2671 level->num_yamyam_contents = getFile8Bit(file);
2675 // correct invalid number of content fields -- should never happen
2676 if (level->num_yamyam_contents < 1 ||
2677 level->num_yamyam_contents > MAX_ELEMENT_CONTENTS)
2678 level->num_yamyam_contents = STD_ELEMENT_CONTENTS;
2680 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
2681 for (y = 0; y < 3; y++)
2682 for (x = 0; x < 3; x++)
2683 level->yamyam_content[i].e[x][y] =
2684 getMappedElement(level->encoding_16bit_field ?
2685 getFile16BitBE(file) : getFile8Bit(file));
2689 static int LoadLevel_CNT2(File *file, int chunk_size, struct LevelInfo *level)
2694 int content_array[MAX_ELEMENT_CONTENTS][3][3];
2696 element = getMappedElement(getFile16BitBE(file));
2697 num_contents = getFile8Bit(file);
2699 getFile8Bit(file); // content x size (unused)
2700 getFile8Bit(file); // content y size (unused)
2702 ReadUnusedBytesFromFile(file, LEVEL_CHUNK_CNT2_UNUSED);
2704 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
2705 for (y = 0; y < 3; y++)
2706 for (x = 0; x < 3; x++)
2707 content_array[i][x][y] = getMappedElement(getFile16BitBE(file));
2709 // correct invalid number of content fields -- should never happen
2710 if (num_contents < 1 || num_contents > MAX_ELEMENT_CONTENTS)
2711 num_contents = STD_ELEMENT_CONTENTS;
2713 if (element == EL_YAMYAM)
2715 level->num_yamyam_contents = num_contents;
2717 for (i = 0; i < num_contents; i++)
2718 for (y = 0; y < 3; y++)
2719 for (x = 0; x < 3; x++)
2720 level->yamyam_content[i].e[x][y] = content_array[i][x][y];
2722 else if (element == EL_BD_AMOEBA)
2724 level->amoeba_content = content_array[0][0][0];
2728 Warn("cannot load content for element '%d'", element);
2734 static int LoadLevel_CNT3(File *file, int chunk_size, struct LevelInfo *level)
2740 int chunk_size_expected;
2742 element = getMappedElement(getFile16BitBE(file));
2743 if (!IS_ENVELOPE(element))
2744 element = EL_ENVELOPE_1;
2746 envelope_nr = element - EL_ENVELOPE_1;
2748 envelope_len = getFile16BitBE(file);
2750 level->envelope[envelope_nr].xsize = getFile8Bit(file);
2751 level->envelope[envelope_nr].ysize = getFile8Bit(file);
2753 ReadUnusedBytesFromFile(file, LEVEL_CHUNK_CNT3_UNUSED);
2755 chunk_size_expected = LEVEL_CHUNK_CNT3_SIZE(envelope_len);
2756 if (chunk_size_expected != chunk_size)
2758 ReadUnusedBytesFromFile(file, chunk_size - LEVEL_CHUNK_CNT3_HEADER);
2759 return chunk_size_expected;
2762 for (i = 0; i < envelope_len; i++)
2763 level->envelope[envelope_nr].text[i] = getFile8Bit(file);
2768 static int LoadLevel_CUS1(File *file, int chunk_size, struct LevelInfo *level)
2770 int num_changed_custom_elements = getFile16BitBE(file);
2771 int chunk_size_expected = 2 + num_changed_custom_elements * 6;
2774 if (chunk_size_expected != chunk_size)
2776 ReadUnusedBytesFromFile(file, chunk_size - 2);
2777 return chunk_size_expected;
2780 for (i = 0; i < num_changed_custom_elements; i++)
2782 int element = getMappedElement(getFile16BitBE(file));
2783 int properties = getFile32BitBE(file);
2785 if (IS_CUSTOM_ELEMENT(element))
2786 element_info[element].properties[EP_BITFIELD_BASE_NR] = properties;
2788 Warn("invalid custom element number %d", element);
2790 // older game versions that wrote level files with CUS1 chunks used
2791 // different default push delay values (not yet stored in level file)
2792 element_info[element].push_delay_fixed = 2;
2793 element_info[element].push_delay_random = 8;
2796 level->file_has_custom_elements = TRUE;
2801 static int LoadLevel_CUS2(File *file, int chunk_size, struct LevelInfo *level)
2803 int num_changed_custom_elements = getFile16BitBE(file);
2804 int chunk_size_expected = 2 + num_changed_custom_elements * 4;
2807 if (chunk_size_expected != chunk_size)
2809 ReadUnusedBytesFromFile(file, chunk_size - 2);
2810 return chunk_size_expected;
2813 for (i = 0; i < num_changed_custom_elements; i++)
2815 int element = getMappedElement(getFile16BitBE(file));
2816 int custom_target_element = getMappedElement(getFile16BitBE(file));
2818 if (IS_CUSTOM_ELEMENT(element))
2819 element_info[element].change->target_element = custom_target_element;
2821 Warn("invalid custom element number %d", element);
2824 level->file_has_custom_elements = TRUE;
2829 static int LoadLevel_CUS3(File *file, int chunk_size, struct LevelInfo *level)
2831 int num_changed_custom_elements = getFile16BitBE(file);
2832 int chunk_size_expected = LEVEL_CHUNK_CUS3_SIZE(num_changed_custom_elements);
2835 if (chunk_size_expected != chunk_size)
2837 ReadUnusedBytesFromFile(file, chunk_size - 2);
2838 return chunk_size_expected;
2841 for (i = 0; i < num_changed_custom_elements; i++)
2843 int element = getMappedElement(getFile16BitBE(file));
2844 struct ElementInfo *ei = &element_info[element];
2845 unsigned int event_bits;
2847 if (!IS_CUSTOM_ELEMENT(element))
2849 Warn("invalid custom element number %d", element);
2851 element = EL_INTERNAL_DUMMY;
2854 for (j = 0; j < MAX_ELEMENT_NAME_LEN; j++)
2855 ei->description[j] = getFile8Bit(file);
2856 ei->description[MAX_ELEMENT_NAME_LEN] = 0;
2858 ei->properties[EP_BITFIELD_BASE_NR] = getFile32BitBE(file);
2860 // some free bytes for future properties and padding
2861 ReadUnusedBytesFromFile(file, 7);
2863 ei->use_gfx_element = getFile8Bit(file);
2864 ei->gfx_element_initial = getMappedElement(getFile16BitBE(file));
2866 ei->collect_score_initial = getFile8Bit(file);
2867 ei->collect_count_initial = getFile8Bit(file);
2869 ei->push_delay_fixed = getFile16BitBE(file);
2870 ei->push_delay_random = getFile16BitBE(file);
2871 ei->move_delay_fixed = getFile16BitBE(file);
2872 ei->move_delay_random = getFile16BitBE(file);
2874 ei->move_pattern = getFile16BitBE(file);
2875 ei->move_direction_initial = getFile8Bit(file);
2876 ei->move_stepsize = getFile8Bit(file);
2878 for (y = 0; y < 3; y++)
2879 for (x = 0; x < 3; x++)
2880 ei->content.e[x][y] = getMappedElement(getFile16BitBE(file));
2882 // bits 0 - 31 of "has_event[]"
2883 event_bits = getFile32BitBE(file);
2884 for (j = 0; j < MIN(NUM_CHANGE_EVENTS, 32); j++)
2885 if (event_bits & (1u << j))
2886 ei->change->has_event[j] = TRUE;
2888 ei->change->target_element = getMappedElement(getFile16BitBE(file));
2890 ei->change->delay_fixed = getFile16BitBE(file);
2891 ei->change->delay_random = getFile16BitBE(file);
2892 ei->change->delay_frames = getFile16BitBE(file);
2894 ei->change->initial_trigger_element = getMappedElement(getFile16BitBE(file));
2896 ei->change->explode = getFile8Bit(file);
2897 ei->change->use_target_content = getFile8Bit(file);
2898 ei->change->only_if_complete = getFile8Bit(file);
2899 ei->change->use_random_replace = getFile8Bit(file);
2901 ei->change->random_percentage = getFile8Bit(file);
2902 ei->change->replace_when = getFile8Bit(file);
2904 for (y = 0; y < 3; y++)
2905 for (x = 0; x < 3; x++)
2906 ei->change->target_content.e[x][y] =
2907 getMappedElement(getFile16BitBE(file));
2909 ei->slippery_type = getFile8Bit(file);
2911 // some free bytes for future properties and padding
2912 ReadUnusedBytesFromFile(file, LEVEL_CPART_CUS3_UNUSED);
2914 // mark that this custom element has been modified
2915 ei->modified_settings = TRUE;
2918 level->file_has_custom_elements = TRUE;
2923 static int LoadLevel_CUS4(File *file, int chunk_size, struct LevelInfo *level)
2925 struct ElementInfo *ei;
2926 int chunk_size_expected;
2930 // ---------- custom element base property values (96 bytes) ----------------
2932 element = getMappedElement(getFile16BitBE(file));
2934 if (!IS_CUSTOM_ELEMENT(element))
2936 Warn("invalid custom element number %d", element);
2938 ReadUnusedBytesFromFile(file, chunk_size - 2);
2943 ei = &element_info[element];
2945 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
2946 ei->description[i] = getFile8Bit(file);
2947 ei->description[MAX_ELEMENT_NAME_LEN] = 0;
2949 ei->properties[EP_BITFIELD_BASE_NR] = getFile32BitBE(file);
2951 ReadUnusedBytesFromFile(file, 4); // reserved for more base properties
2953 ei->num_change_pages = getFile8Bit(file);
2955 chunk_size_expected = LEVEL_CHUNK_CUS4_SIZE(ei->num_change_pages);
2956 if (chunk_size_expected != chunk_size)
2958 ReadUnusedBytesFromFile(file, chunk_size - 43);
2959 return chunk_size_expected;
2962 ei->ce_value_fixed_initial = getFile16BitBE(file);
2963 ei->ce_value_random_initial = getFile16BitBE(file);
2964 ei->use_last_ce_value = getFile8Bit(file);
2966 ei->use_gfx_element = getFile8Bit(file);
2967 ei->gfx_element_initial = getMappedElement(getFile16BitBE(file));
2969 ei->collect_score_initial = getFile8Bit(file);
2970 ei->collect_count_initial = getFile8Bit(file);
2972 ei->drop_delay_fixed = getFile8Bit(file);
2973 ei->push_delay_fixed = getFile8Bit(file);
2974 ei->drop_delay_random = getFile8Bit(file);
2975 ei->push_delay_random = getFile8Bit(file);
2976 ei->move_delay_fixed = getFile16BitBE(file);
2977 ei->move_delay_random = getFile16BitBE(file);
2979 // bits 0 - 15 of "move_pattern" ...
2980 ei->move_pattern = getFile16BitBE(file);
2981 ei->move_direction_initial = getFile8Bit(file);
2982 ei->move_stepsize = getFile8Bit(file);
2984 ei->slippery_type = getFile8Bit(file);
2986 for (y = 0; y < 3; y++)
2987 for (x = 0; x < 3; x++)
2988 ei->content.e[x][y] = getMappedElement(getFile16BitBE(file));
2990 ei->move_enter_element = getMappedElement(getFile16BitBE(file));
2991 ei->move_leave_element = getMappedElement(getFile16BitBE(file));
2992 ei->move_leave_type = getFile8Bit(file);
2994 // ... bits 16 - 31 of "move_pattern" (not nice, but downward compatible)
2995 ei->move_pattern |= (getFile16BitBE(file) << 16);
2997 ei->access_direction = getFile8Bit(file);
2999 ei->explosion_delay = getFile8Bit(file);
3000 ei->ignition_delay = getFile8Bit(file);
3001 ei->explosion_type = getFile8Bit(file);
3003 // some free bytes for future custom property values and padding
3004 ReadUnusedBytesFromFile(file, 1);
3006 // ---------- change page property values (48 bytes) ------------------------
3008 setElementChangePages(ei, ei->num_change_pages);
3010 for (i = 0; i < ei->num_change_pages; i++)
3012 struct ElementChangeInfo *change = &ei->change_page[i];
3013 unsigned int event_bits;
3015 // always start with reliable default values
3016 setElementChangeInfoToDefaults(change);
3018 // bits 0 - 31 of "has_event[]" ...
3019 event_bits = getFile32BitBE(file);
3020 for (j = 0; j < MIN(NUM_CHANGE_EVENTS, 32); j++)
3021 if (event_bits & (1u << j))
3022 change->has_event[j] = TRUE;
3024 change->target_element = getMappedElement(getFile16BitBE(file));
3026 change->delay_fixed = getFile16BitBE(file);
3027 change->delay_random = getFile16BitBE(file);
3028 change->delay_frames = getFile16BitBE(file);
3030 change->initial_trigger_element = getMappedElement(getFile16BitBE(file));
3032 change->explode = getFile8Bit(file);
3033 change->use_target_content = getFile8Bit(file);
3034 change->only_if_complete = getFile8Bit(file);
3035 change->use_random_replace = getFile8Bit(file);
3037 change->random_percentage = getFile8Bit(file);
3038 change->replace_when = getFile8Bit(file);
3040 for (y = 0; y < 3; y++)
3041 for (x = 0; x < 3; x++)
3042 change->target_content.e[x][y]= getMappedElement(getFile16BitBE(file));
3044 change->can_change = getFile8Bit(file);
3046 change->trigger_side = getFile8Bit(file);
3048 change->trigger_player = getFile8Bit(file);
3049 change->trigger_page = getFile8Bit(file);
3051 change->trigger_page = (change->trigger_page == CH_PAGE_ANY_FILE ?
3052 CH_PAGE_ANY : (1 << change->trigger_page));
3054 change->has_action = getFile8Bit(file);
3055 change->action_type = getFile8Bit(file);
3056 change->action_mode = getFile8Bit(file);
3057 change->action_arg = getFile16BitBE(file);
3059 // ... bits 32 - 39 of "has_event[]" (not nice, but downward compatible)
3060 event_bits = getFile8Bit(file);
3061 for (j = 32; j < NUM_CHANGE_EVENTS; j++)
3062 if (event_bits & (1u << (j - 32)))
3063 change->has_event[j] = TRUE;
3066 // mark this custom element as modified
3067 ei->modified_settings = TRUE;
3069 level->file_has_custom_elements = TRUE;
3074 static int LoadLevel_GRP1(File *file, int chunk_size, struct LevelInfo *level)
3076 struct ElementInfo *ei;
3077 struct ElementGroupInfo *group;
3081 element = getMappedElement(getFile16BitBE(file));
3083 if (!IS_GROUP_ELEMENT(element))
3085 Warn("invalid group element number %d", element);
3087 ReadUnusedBytesFromFile(file, chunk_size - 2);
3092 ei = &element_info[element];
3094 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
3095 ei->description[i] = getFile8Bit(file);
3096 ei->description[MAX_ELEMENT_NAME_LEN] = 0;
3098 group = element_info[element].group;
3100 group->num_elements = getFile8Bit(file);
3102 ei->use_gfx_element = getFile8Bit(file);
3103 ei->gfx_element_initial = getMappedElement(getFile16BitBE(file));
3105 group->choice_mode = getFile8Bit(file);
3107 // some free bytes for future values and padding
3108 ReadUnusedBytesFromFile(file, 3);
3110 for (i = 0; i < MAX_ELEMENTS_IN_GROUP; i++)
3111 group->element[i] = getMappedElement(getFile16BitBE(file));
3113 // mark this group element as modified
3114 element_info[element].modified_settings = TRUE;
3116 level->file_has_custom_elements = TRUE;
3121 static int LoadLevel_MicroChunk(File *file, struct LevelFileConfigInfo *conf,
3122 int element, int real_element)
3124 int micro_chunk_size = 0;
3125 int conf_type = getFile8Bit(file);
3126 int byte_mask = conf_type & CONF_MASK_BYTES;
3127 boolean element_found = FALSE;
3130 micro_chunk_size += 1;
3132 if (byte_mask == CONF_MASK_MULTI_BYTES)
3134 int num_bytes = getFile16BitBE(file);
3135 byte *buffer = checked_malloc(num_bytes);
3137 ReadBytesFromFile(file, buffer, num_bytes);
3139 for (i = 0; conf[i].data_type != -1; i++)
3141 if (conf[i].element == element &&
3142 conf[i].conf_type == conf_type)
3144 int data_type = conf[i].data_type;
3145 int num_entities = num_bytes / CONF_ENTITY_NUM_BYTES(data_type);
3146 int max_num_entities = conf[i].max_num_entities;
3148 if (num_entities > max_num_entities)
3150 Warn("truncating number of entities for element %d from %d to %d",
3151 element, num_entities, max_num_entities);
3153 num_entities = max_num_entities;
3156 if (num_entities == 0 && (data_type == TYPE_ELEMENT_LIST ||
3157 data_type == TYPE_CONTENT_LIST))
3159 // for element and content lists, zero entities are not allowed
3160 Warn("found empty list of entities for element %d", element);
3162 // do not set "num_entities" here to prevent reading behind buffer
3164 *(int *)(conf[i].num_entities) = 1; // at least one is required
3168 *(int *)(conf[i].num_entities) = num_entities;
3171 element_found = TRUE;
3173 if (data_type == TYPE_STRING)
3175 char *string = (char *)(conf[i].value);
3178 for (j = 0; j < max_num_entities; j++)
3179 string[j] = (j < num_entities ? buffer[j] : '\0');
3181 else if (data_type == TYPE_ELEMENT_LIST)
3183 int *element_array = (int *)(conf[i].value);
3186 for (j = 0; j < num_entities; j++)
3188 getMappedElement(CONF_ELEMENTS_ELEMENT(buffer, j));
3190 else if (data_type == TYPE_CONTENT_LIST)
3192 struct Content *content= (struct Content *)(conf[i].value);
3195 for (c = 0; c < num_entities; c++)
3196 for (y = 0; y < 3; y++)
3197 for (x = 0; x < 3; x++)
3198 content[c].e[x][y] =
3199 getMappedElement(CONF_CONTENTS_ELEMENT(buffer, c, x, y));
3202 element_found = FALSE;
3208 checked_free(buffer);
3210 micro_chunk_size += 2 + num_bytes;
3212 else // constant size configuration data (1, 2 or 4 bytes)
3214 int value = (byte_mask == CONF_MASK_1_BYTE ? getFile8Bit (file) :
3215 byte_mask == CONF_MASK_2_BYTE ? getFile16BitBE(file) :
3216 byte_mask == CONF_MASK_4_BYTE ? getFile32BitBE(file) : 0);
3218 for (i = 0; conf[i].data_type != -1; i++)
3220 if (conf[i].element == element &&
3221 conf[i].conf_type == conf_type)
3223 int data_type = conf[i].data_type;
3225 if (data_type == TYPE_ELEMENT)
3226 value = getMappedElement(value);
3228 if (data_type == TYPE_BOOLEAN)
3229 *(boolean *)(conf[i].value) = (value ? TRUE : FALSE);
3231 *(int *) (conf[i].value) = value;
3233 element_found = TRUE;
3239 micro_chunk_size += CONF_VALUE_NUM_BYTES(byte_mask);
3244 char *error_conf_chunk_bytes =
3245 (byte_mask == CONF_MASK_1_BYTE ? "CONF_VALUE_8_BIT" :
3246 byte_mask == CONF_MASK_2_BYTE ? "CONF_VALUE_16_BIT" :
3247 byte_mask == CONF_MASK_4_BYTE ? "CONF_VALUE_32_BIT" :"CONF_VALUE_BYTES");
3248 int error_conf_chunk_token = conf_type & CONF_MASK_TOKEN;
3249 int error_element = real_element;
3251 Warn("cannot load micro chunk '%s(%d)' value for element %d ['%s']",
3252 error_conf_chunk_bytes, error_conf_chunk_token,
3253 error_element, EL_NAME(error_element));
3256 return micro_chunk_size;
3259 static int LoadLevel_INFO(File *file, int chunk_size, struct LevelInfo *level)
3261 int real_chunk_size = 0;
3263 li = *level; // copy level data into temporary buffer
3265 while (!checkEndOfFile(file))
3267 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_INFO, -1, -1);
3269 if (real_chunk_size >= chunk_size)
3273 *level = li; // copy temporary buffer back to level data
3275 return real_chunk_size;
3278 static int LoadLevel_CONF(File *file, int chunk_size, struct LevelInfo *level)
3280 int real_chunk_size = 0;
3282 li = *level; // copy level data into temporary buffer
3284 while (!checkEndOfFile(file))
3286 int element = getMappedElement(getFile16BitBE(file));
3288 real_chunk_size += 2;
3289 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_CONF,
3291 if (real_chunk_size >= chunk_size)
3295 *level = li; // copy temporary buffer back to level data
3297 return real_chunk_size;
3300 static int LoadLevel_ELEM(File *file, int chunk_size, struct LevelInfo *level)
3302 int real_chunk_size = 0;
3304 li = *level; // copy level data into temporary buffer
3306 while (!checkEndOfFile(file))
3308 int element = getMappedElement(getFile16BitBE(file));
3310 real_chunk_size += 2;
3311 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_ELEM,
3313 if (real_chunk_size >= chunk_size)
3317 *level = li; // copy temporary buffer back to level data
3319 return real_chunk_size;
3322 static int LoadLevel_NOTE(File *file, int chunk_size, struct LevelInfo *level)
3324 int element = getMappedElement(getFile16BitBE(file));
3325 int envelope_nr = element - EL_ENVELOPE_1;
3326 int real_chunk_size = 2;
3328 xx_envelope = level->envelope[envelope_nr]; // copy into temporary buffer
3330 while (!checkEndOfFile(file))
3332 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_NOTE,
3335 if (real_chunk_size >= chunk_size)
3339 level->envelope[envelope_nr] = xx_envelope; // copy from temporary buffer
3341 return real_chunk_size;
3344 static int LoadLevel_CUSX(File *file, int chunk_size, struct LevelInfo *level)
3346 int element = getMappedElement(getFile16BitBE(file));
3347 int real_chunk_size = 2;
3348 struct ElementInfo *ei = &element_info[element];
3351 xx_ei = *ei; // copy element data into temporary buffer
3353 xx_ei.num_change_pages = -1;
3355 while (!checkEndOfFile(file))
3357 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_CUSX_base,
3359 if (xx_ei.num_change_pages != -1)
3362 if (real_chunk_size >= chunk_size)
3368 if (ei->num_change_pages == -1)
3370 Warn("LoadLevel_CUSX(): missing 'num_change_pages' for '%s'",
3373 ei->num_change_pages = 1;
3375 setElementChangePages(ei, 1);
3376 setElementChangeInfoToDefaults(ei->change);
3378 return real_chunk_size;
3381 // initialize number of change pages stored for this custom element
3382 setElementChangePages(ei, ei->num_change_pages);
3383 for (i = 0; i < ei->num_change_pages; i++)
3384 setElementChangeInfoToDefaults(&ei->change_page[i]);
3386 // start with reading properties for the first change page
3387 xx_current_change_page = 0;
3389 while (!checkEndOfFile(file))
3391 // level file might contain invalid change page number
3392 if (xx_current_change_page >= ei->num_change_pages)
3395 struct ElementChangeInfo *change = &ei->change_page[xx_current_change_page];
3397 xx_change = *change; // copy change data into temporary buffer
3399 resetEventBits(); // reset bits; change page might have changed
3401 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_CUSX_change,
3404 *change = xx_change;
3406 setEventFlagsFromEventBits(change);
3408 if (real_chunk_size >= chunk_size)
3412 level->file_has_custom_elements = TRUE;
3414 return real_chunk_size;
3417 static int LoadLevel_GRPX(File *file, int chunk_size, struct LevelInfo *level)
3419 int element = getMappedElement(getFile16BitBE(file));
3420 int real_chunk_size = 2;
3421 struct ElementInfo *ei = &element_info[element];
3422 struct ElementGroupInfo *group = ei->group;
3427 xx_ei = *ei; // copy element data into temporary buffer
3428 xx_group = *group; // copy group data into temporary buffer
3430 while (!checkEndOfFile(file))
3432 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_GRPX,
3435 if (real_chunk_size >= chunk_size)
3442 level->file_has_custom_elements = TRUE;
3444 return real_chunk_size;
3447 static int LoadLevel_EMPX(File *file, int chunk_size, struct LevelInfo *level)
3449 int element = getMappedElement(getFile16BitBE(file));
3450 int real_chunk_size = 2;
3451 struct ElementInfo *ei = &element_info[element];
3453 xx_ei = *ei; // copy element data into temporary buffer
3455 while (!checkEndOfFile(file))
3457 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_EMPX,
3460 if (real_chunk_size >= chunk_size)
3466 level->file_has_custom_elements = TRUE;
3468 return real_chunk_size;
3471 static void LoadLevelFromFileInfo_RND(struct LevelInfo *level,
3472 struct LevelFileInfo *level_file_info,
3473 boolean level_info_only)
3475 char *filename = level_file_info->filename;
3476 char cookie[MAX_LINE_LEN];
3477 char chunk_name[CHUNK_ID_LEN + 1];
3481 if (!(file = openFile(filename, MODE_READ)))
3483 level->no_valid_file = TRUE;
3484 level->no_level_file = TRUE;
3486 if (level_info_only)
3489 Warn("cannot read level '%s' -- using empty level", filename);
3491 if (!setup.editor.use_template_for_new_levels)
3494 // if level file not found, try to initialize level data from template
3495 filename = getGlobalLevelTemplateFilename();
3497 if (!(file = openFile(filename, MODE_READ)))
3500 // default: for empty levels, use level template for custom elements
3501 level->use_custom_template = TRUE;
3503 level->no_valid_file = FALSE;
3506 getFileChunkBE(file, chunk_name, NULL);
3507 if (strEqual(chunk_name, "RND1"))
3509 getFile32BitBE(file); // not used
3511 getFileChunkBE(file, chunk_name, NULL);
3512 if (!strEqual(chunk_name, "CAVE"))
3514 level->no_valid_file = TRUE;
3516 Warn("unknown format of level file '%s'", filename);
3523 else // check for pre-2.0 file format with cookie string
3525 strcpy(cookie, chunk_name);
3526 if (getStringFromFile(file, &cookie[4], MAX_LINE_LEN - 4) == NULL)
3528 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
3529 cookie[strlen(cookie) - 1] = '\0';
3531 if (!checkCookieString(cookie, LEVEL_COOKIE_TMPL))
3533 level->no_valid_file = TRUE;
3535 Warn("unknown format of level file '%s'", filename);
3542 if ((level->file_version = getFileVersionFromCookieString(cookie)) == -1)
3544 level->no_valid_file = TRUE;
3546 Warn("unsupported version of level file '%s'", filename);
3553 // pre-2.0 level files have no game version, so use file version here
3554 level->game_version = level->file_version;
3557 if (level->file_version < FILE_VERSION_1_2)
3559 // level files from versions before 1.2.0 without chunk structure
3560 LoadLevel_HEAD(file, LEVEL_CHUNK_HEAD_SIZE, level);
3561 LoadLevel_BODY(file, level->fieldx * level->fieldy, level);
3569 int (*loader)(File *, int, struct LevelInfo *);
3573 { "VERS", LEVEL_CHUNK_VERS_SIZE, LoadLevel_VERS },
3574 { "DATE", LEVEL_CHUNK_DATE_SIZE, LoadLevel_DATE },
3575 { "HEAD", LEVEL_CHUNK_HEAD_SIZE, LoadLevel_HEAD },
3576 { "NAME", LEVEL_CHUNK_NAME_SIZE, LoadLevel_NAME },
3577 { "AUTH", LEVEL_CHUNK_AUTH_SIZE, LoadLevel_AUTH },
3578 { "INFO", -1, LoadLevel_INFO },
3579 { "BODY", -1, LoadLevel_BODY },
3580 { "CONT", -1, LoadLevel_CONT },
3581 { "CNT2", LEVEL_CHUNK_CNT2_SIZE, LoadLevel_CNT2 },
3582 { "CNT3", -1, LoadLevel_CNT3 },
3583 { "CUS1", -1, LoadLevel_CUS1 },
3584 { "CUS2", -1, LoadLevel_CUS2 },
3585 { "CUS3", -1, LoadLevel_CUS3 },
3586 { "CUS4", -1, LoadLevel_CUS4 },
3587 { "GRP1", -1, LoadLevel_GRP1 },
3588 { "CONF", -1, LoadLevel_CONF },
3589 { "ELEM", -1, LoadLevel_ELEM },
3590 { "NOTE", -1, LoadLevel_NOTE },
3591 { "CUSX", -1, LoadLevel_CUSX },
3592 { "GRPX", -1, LoadLevel_GRPX },
3593 { "EMPX", -1, LoadLevel_EMPX },
3598 while (getFileChunkBE(file, chunk_name, &chunk_size))
3602 while (chunk_info[i].name != NULL &&
3603 !strEqual(chunk_name, chunk_info[i].name))
3606 if (chunk_info[i].name == NULL)
3608 Warn("unknown chunk '%s' in level file '%s'",
3609 chunk_name, filename);
3611 ReadUnusedBytesFromFile(file, chunk_size);
3613 else if (chunk_info[i].size != -1 &&
3614 chunk_info[i].size != chunk_size)
3616 Warn("wrong size (%d) of chunk '%s' in level file '%s'",
3617 chunk_size, chunk_name, filename);
3619 ReadUnusedBytesFromFile(file, chunk_size);
3623 // call function to load this level chunk
3624 int chunk_size_expected =
3625 (chunk_info[i].loader)(file, chunk_size, level);
3627 if (chunk_size_expected < 0)
3629 Warn("error reading chunk '%s' in level file '%s'",
3630 chunk_name, filename);
3635 // the size of some chunks cannot be checked before reading other
3636 // chunks first (like "HEAD" and "BODY") that contain some header
3637 // information, so check them here
3638 if (chunk_size_expected != chunk_size)
3640 Warn("wrong size (%d) of chunk '%s' in level file '%s'",
3641 chunk_size, chunk_name, filename);
3653 // ----------------------------------------------------------------------------
3654 // functions for loading BD level
3655 // ----------------------------------------------------------------------------
3657 static void CopyNativeLevel_RND_to_BD(struct LevelInfo *level)
3659 struct LevelInfo_BD *level_bd = level->native_bd_level;
3661 level_bd->width = MIN(level->fieldx, MAX_PLAYFIELD_WIDTH);
3662 level_bd->height = MIN(level->fieldy, MAX_PLAYFIELD_HEIGHT);
3665 static void CopyNativeLevel_BD_to_RND(struct LevelInfo *level)
3667 struct LevelInfo_BD *level_bd = level->native_bd_level;
3669 level->fieldx = MIN(level_bd->width, MAX_LEV_FIELDX);
3670 level->fieldy = MIN(level_bd->height, MAX_LEV_FIELDY);
3674 // ----------------------------------------------------------------------------
3675 // functions for loading EM level
3676 // ----------------------------------------------------------------------------
3678 static void CopyNativeLevel_RND_to_EM(struct LevelInfo *level)
3680 static int ball_xy[8][2] =
3691 struct LevelInfo_EM *level_em = level->native_em_level;
3692 struct CAVE *cav = level_em->cav;
3695 cav->width = MIN(level->fieldx, MAX_PLAYFIELD_WIDTH);
3696 cav->height = MIN(level->fieldy, MAX_PLAYFIELD_HEIGHT);
3698 cav->time_seconds = level->time;
3699 cav->gems_needed = level->gems_needed;
3701 cav->emerald_score = level->score[SC_EMERALD];
3702 cav->diamond_score = level->score[SC_DIAMOND];
3703 cav->alien_score = level->score[SC_ROBOT];
3704 cav->tank_score = level->score[SC_SPACESHIP];
3705 cav->bug_score = level->score[SC_BUG];
3706 cav->eater_score = level->score[SC_YAMYAM];
3707 cav->nut_score = level->score[SC_NUT];
3708 cav->dynamite_score = level->score[SC_DYNAMITE];
3709 cav->key_score = level->score[SC_KEY];
3710 cav->exit_score = level->score[SC_TIME_BONUS];
3712 cav->num_eater_arrays = level->num_yamyam_contents;
3714 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
3715 for (y = 0; y < 3; y++)
3716 for (x = 0; x < 3; x++)
3717 cav->eater_array[i][y * 3 + x] =
3718 map_element_RND_to_EM_cave(level->yamyam_content[i].e[x][y]);
3720 cav->amoeba_time = level->amoeba_speed;
3721 cav->wonderwall_time = level->time_magic_wall;
3722 cav->wheel_time = level->time_wheel;
3724 cav->android_move_time = level->android_move_time;
3725 cav->android_clone_time = level->android_clone_time;
3726 cav->ball_random = level->ball_random;
3727 cav->ball_active = level->ball_active_initial;
3728 cav->ball_time = level->ball_time;
3729 cav->num_ball_arrays = level->num_ball_contents;
3731 cav->lenses_score = level->lenses_score;
3732 cav->magnify_score = level->magnify_score;
3733 cav->slurp_score = level->slurp_score;
3735 cav->lenses_time = level->lenses_time;
3736 cav->magnify_time = level->magnify_time;
3738 cav->wind_time = 9999;
3739 cav->wind_direction =
3740 map_direction_RND_to_EM(level->wind_direction_initial);
3742 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
3743 for (j = 0; j < 8; j++)
3744 cav->ball_array[i][j] =
3745 map_element_RND_to_EM_cave(level->ball_content[i].
3746 e[ball_xy[j][0]][ball_xy[j][1]]);
3748 map_android_clone_elements_RND_to_EM(level);
3750 // first fill the complete playfield with the empty space element
3751 for (y = 0; y < EM_MAX_CAVE_HEIGHT; y++)
3752 for (x = 0; x < EM_MAX_CAVE_WIDTH; x++)
3753 cav->cave[x][y] = Cblank;
3755 // then copy the real level contents from level file into the playfield
3756 for (y = 0; y < cav->height; y++) for (x = 0; x < cav->width; x++)
3758 int new_element = map_element_RND_to_EM_cave(level->field[x][y]);
3760 if (level->field[x][y] == EL_AMOEBA_DEAD)
3761 new_element = map_element_RND_to_EM_cave(EL_AMOEBA_WET);
3763 cav->cave[x][y] = new_element;
3766 for (i = 0; i < MAX_PLAYERS; i++)
3768 cav->player_x[i] = -1;
3769 cav->player_y[i] = -1;
3772 // initialize player positions and delete players from the playfield
3773 for (y = 0; y < cav->height; y++) for (x = 0; x < cav->width; x++)
3775 if (IS_PLAYER_ELEMENT(level->field[x][y]))
3777 int player_nr = GET_PLAYER_NR(level->field[x][y]);
3779 cav->player_x[player_nr] = x;
3780 cav->player_y[player_nr] = y;
3782 cav->cave[x][y] = map_element_RND_to_EM_cave(EL_EMPTY);
3787 static void CopyNativeLevel_EM_to_RND(struct LevelInfo *level)
3789 static int ball_xy[8][2] =
3800 struct LevelInfo_EM *level_em = level->native_em_level;
3801 struct CAVE *cav = level_em->cav;
3804 level->fieldx = MIN(cav->width, MAX_LEV_FIELDX);
3805 level->fieldy = MIN(cav->height, MAX_LEV_FIELDY);
3807 level->time = cav->time_seconds;
3808 level->gems_needed = cav->gems_needed;
3810 sprintf(level->name, "Level %d", level->file_info.nr);
3812 level->score[SC_EMERALD] = cav->emerald_score;
3813 level->score[SC_DIAMOND] = cav->diamond_score;
3814 level->score[SC_ROBOT] = cav->alien_score;
3815 level->score[SC_SPACESHIP] = cav->tank_score;
3816 level->score[SC_BUG] = cav->bug_score;
3817 level->score[SC_YAMYAM] = cav->eater_score;
3818 level->score[SC_NUT] = cav->nut_score;
3819 level->score[SC_DYNAMITE] = cav->dynamite_score;
3820 level->score[SC_KEY] = cav->key_score;
3821 level->score[SC_TIME_BONUS] = cav->exit_score;
3823 level->num_yamyam_contents = cav->num_eater_arrays;
3825 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
3826 for (y = 0; y < 3; y++)
3827 for (x = 0; x < 3; x++)
3828 level->yamyam_content[i].e[x][y] =
3829 map_element_EM_to_RND_cave(cav->eater_array[i][y * 3 + x]);
3831 level->amoeba_speed = cav->amoeba_time;
3832 level->time_magic_wall = cav->wonderwall_time;
3833 level->time_wheel = cav->wheel_time;
3835 level->android_move_time = cav->android_move_time;
3836 level->android_clone_time = cav->android_clone_time;
3837 level->ball_random = cav->ball_random;
3838 level->ball_active_initial = cav->ball_active;
3839 level->ball_time = cav->ball_time;
3840 level->num_ball_contents = cav->num_ball_arrays;
3842 level->lenses_score = cav->lenses_score;
3843 level->magnify_score = cav->magnify_score;
3844 level->slurp_score = cav->slurp_score;
3846 level->lenses_time = cav->lenses_time;
3847 level->magnify_time = cav->magnify_time;
3849 level->wind_direction_initial =
3850 map_direction_EM_to_RND(cav->wind_direction);
3852 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
3853 for (j = 0; j < 8; j++)
3854 level->ball_content[i].e[ball_xy[j][0]][ball_xy[j][1]] =
3855 map_element_EM_to_RND_cave(cav->ball_array[i][j]);
3857 map_android_clone_elements_EM_to_RND(level);
3859 // convert the playfield (some elements need special treatment)
3860 for (y = 0; y < level->fieldy; y++) for (x = 0; x < level->fieldx; x++)
3862 int new_element = map_element_EM_to_RND_cave(cav->cave[x][y]);
3864 if (new_element == EL_AMOEBA_WET && level->amoeba_speed == 0)
3865 new_element = EL_AMOEBA_DEAD;
3867 level->field[x][y] = new_element;
3870 for (i = 0; i < MAX_PLAYERS; i++)
3872 // in case of all players set to the same field, use the first player
3873 int nr = MAX_PLAYERS - i - 1;
3874 int jx = cav->player_x[nr];
3875 int jy = cav->player_y[nr];
3877 if (jx != -1 && jy != -1)
3878 level->field[jx][jy] = EL_PLAYER_1 + nr;
3881 // time score is counted for each 10 seconds left in Emerald Mine levels
3882 level->time_score_base = 10;
3886 // ----------------------------------------------------------------------------
3887 // functions for loading SP level
3888 // ----------------------------------------------------------------------------
3890 static void CopyNativeLevel_RND_to_SP(struct LevelInfo *level)
3892 struct LevelInfo_SP *level_sp = level->native_sp_level;
3893 LevelInfoType *header = &level_sp->header;
3896 level_sp->width = level->fieldx;
3897 level_sp->height = level->fieldy;
3899 for (x = 0; x < level->fieldx; x++)
3900 for (y = 0; y < level->fieldy; y++)
3901 level_sp->playfield[x][y] = map_element_RND_to_SP(level->field[x][y]);
3903 header->InitialGravity = (level->initial_player_gravity[0] ? 1 : 0);
3905 for (i = 0; i < SP_LEVEL_NAME_LEN; i++)
3906 header->LevelTitle[i] = level->name[i];
3907 // !!! NO STRING TERMINATION IN SUPAPLEX VB CODE YET -- FIX THIS !!!
3909 header->InfotronsNeeded = level->gems_needed;
3911 header->SpecialPortCount = 0;
3913 for (x = 0; x < level->fieldx; x++) for (y = 0; y < level->fieldy; y++)
3915 boolean gravity_port_found = FALSE;
3916 boolean gravity_port_valid = FALSE;
3917 int gravity_port_flag;
3918 int gravity_port_base_element;
3919 int element = level->field[x][y];
3921 if (element >= EL_SP_GRAVITY_ON_PORT_RIGHT &&
3922 element <= EL_SP_GRAVITY_ON_PORT_UP)
3924 gravity_port_found = TRUE;
3925 gravity_port_valid = TRUE;
3926 gravity_port_flag = 1;
3927 gravity_port_base_element = EL_SP_GRAVITY_ON_PORT_RIGHT;
3929 else if (element >= EL_SP_GRAVITY_OFF_PORT_RIGHT &&
3930 element <= EL_SP_GRAVITY_OFF_PORT_UP)
3932 gravity_port_found = TRUE;
3933 gravity_port_valid = TRUE;
3934 gravity_port_flag = 0;
3935 gravity_port_base_element = EL_SP_GRAVITY_OFF_PORT_RIGHT;
3937 else if (element >= EL_SP_GRAVITY_PORT_RIGHT &&
3938 element <= EL_SP_GRAVITY_PORT_UP)
3940 // change R'n'D style gravity inverting special port to normal port
3941 // (there are no gravity inverting ports in native Supaplex engine)
3943 gravity_port_found = TRUE;
3944 gravity_port_valid = FALSE;
3945 gravity_port_base_element = EL_SP_GRAVITY_PORT_RIGHT;
3948 if (gravity_port_found)
3950 if (gravity_port_valid &&
3951 header->SpecialPortCount < SP_MAX_SPECIAL_PORTS)
3953 SpecialPortType *port = &header->SpecialPort[header->SpecialPortCount];
3955 port->PortLocation = (y * level->fieldx + x) * 2;
3956 port->Gravity = gravity_port_flag;
3958 element += EL_SP_GRAVITY_PORT_RIGHT - gravity_port_base_element;
3960 header->SpecialPortCount++;
3964 // change special gravity port to normal port
3966 element += EL_SP_PORT_RIGHT - gravity_port_base_element;
3969 level_sp->playfield[x][y] = element - EL_SP_START;
3974 static void CopyNativeLevel_SP_to_RND(struct LevelInfo *level)
3976 struct LevelInfo_SP *level_sp = level->native_sp_level;
3977 LevelInfoType *header = &level_sp->header;
3978 boolean num_invalid_elements = 0;
3981 level->fieldx = level_sp->width;
3982 level->fieldy = level_sp->height;
3984 for (x = 0; x < level->fieldx; x++)
3986 for (y = 0; y < level->fieldy; y++)
3988 int element_old = level_sp->playfield[x][y];
3989 int element_new = getMappedElement(map_element_SP_to_RND(element_old));
3991 if (element_new == EL_UNKNOWN)
3993 num_invalid_elements++;
3995 Debug("level:native:SP", "invalid element %d at position %d, %d",
3999 level->field[x][y] = element_new;
4003 if (num_invalid_elements > 0)
4004 Warn("found %d invalid elements%s", num_invalid_elements,
4005 (!options.debug ? " (use '--debug' for more details)" : ""));
4007 for (i = 0; i < MAX_PLAYERS; i++)
4008 level->initial_player_gravity[i] =
4009 (header->InitialGravity == 1 ? TRUE : FALSE);
4011 // skip leading spaces
4012 for (i = 0; i < SP_LEVEL_NAME_LEN; i++)
4013 if (header->LevelTitle[i] != ' ')
4017 for (j = 0; i < SP_LEVEL_NAME_LEN; i++, j++)
4018 level->name[j] = header->LevelTitle[i];
4019 level->name[j] = '\0';
4021 // cut trailing spaces
4023 if (level->name[j - 1] == ' ' && level->name[j] == '\0')
4024 level->name[j - 1] = '\0';
4026 level->gems_needed = header->InfotronsNeeded;
4028 for (i = 0; i < header->SpecialPortCount; i++)
4030 SpecialPortType *port = &header->SpecialPort[i];
4031 int port_location = port->PortLocation;
4032 int gravity = port->Gravity;
4033 int port_x, port_y, port_element;
4035 port_x = (port_location / 2) % level->fieldx;
4036 port_y = (port_location / 2) / level->fieldx;
4038 if (port_x < 0 || port_x >= level->fieldx ||
4039 port_y < 0 || port_y >= level->fieldy)
4041 Warn("special port position (%d, %d) out of bounds", port_x, port_y);
4046 port_element = level->field[port_x][port_y];
4048 if (port_element < EL_SP_GRAVITY_PORT_RIGHT ||
4049 port_element > EL_SP_GRAVITY_PORT_UP)
4051 Warn("no special port at position (%d, %d)", port_x, port_y);
4056 // change previous (wrong) gravity inverting special port to either
4057 // gravity enabling special port or gravity disabling special port
4058 level->field[port_x][port_y] +=
4059 (gravity == 1 ? EL_SP_GRAVITY_ON_PORT_RIGHT :
4060 EL_SP_GRAVITY_OFF_PORT_RIGHT) - EL_SP_GRAVITY_PORT_RIGHT;
4063 // change special gravity ports without database entries to normal ports
4064 for (x = 0; x < level->fieldx; x++)
4065 for (y = 0; y < level->fieldy; y++)
4066 if (level->field[x][y] >= EL_SP_GRAVITY_PORT_RIGHT &&
4067 level->field[x][y] <= EL_SP_GRAVITY_PORT_UP)
4068 level->field[x][y] += EL_SP_PORT_RIGHT - EL_SP_GRAVITY_PORT_RIGHT;
4070 level->time = 0; // no time limit
4071 level->amoeba_speed = 0;
4072 level->time_magic_wall = 0;
4073 level->time_wheel = 0;
4074 level->amoeba_content = EL_EMPTY;
4076 // original Supaplex does not use score values -- rate by playing time
4077 for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
4078 level->score[i] = 0;
4080 level->rate_time_over_score = TRUE;
4082 // there are no yamyams in supaplex levels
4083 for (i = 0; i < level->num_yamyam_contents; i++)
4084 for (x = 0; x < 3; x++)
4085 for (y = 0; y < 3; y++)
4086 level->yamyam_content[i].e[x][y] = EL_EMPTY;
4089 static void CopyNativeTape_RND_to_SP(struct LevelInfo *level)
4091 struct LevelInfo_SP *level_sp = level->native_sp_level;
4092 struct DemoInfo_SP *demo = &level_sp->demo;
4095 // always start with reliable default values
4096 demo->is_available = FALSE;
4099 if (TAPE_IS_EMPTY(tape))
4102 demo->level_nr = tape.level_nr; // (currently not used)
4104 level_sp->header.DemoRandomSeed = tape.random_seed;
4108 for (i = 0; i < tape.length; i++)
4110 int demo_action = map_key_RND_to_SP(tape.pos[i].action[0]);
4111 int demo_repeat = tape.pos[i].delay;
4112 int demo_entries = (demo_repeat + 15) / 16;
4114 if (demo->length + demo_entries >= SP_MAX_TAPE_LEN)
4116 Warn("tape truncated: size exceeds maximum SP demo size %d",
4122 for (j = 0; j < demo_repeat / 16; j++)
4123 demo->data[demo->length++] = 0xf0 | demo_action;
4125 if (demo_repeat % 16)
4126 demo->data[demo->length++] = ((demo_repeat % 16 - 1) << 4) | demo_action;
4129 demo->is_available = TRUE;
4132 static void setTapeInfoToDefaults(void);
4134 static void CopyNativeTape_SP_to_RND(struct LevelInfo *level)
4136 struct LevelInfo_SP *level_sp = level->native_sp_level;
4137 struct DemoInfo_SP *demo = &level_sp->demo;
4138 char *filename = level->file_info.filename;
4141 // always start with reliable default values
4142 setTapeInfoToDefaults();
4144 if (!demo->is_available)
4147 tape.level_nr = demo->level_nr; // (currently not used)
4148 tape.random_seed = level_sp->header.DemoRandomSeed;
4150 TapeSetDateFromEpochSeconds(getFileTimestampEpochSeconds(filename));
4153 tape.pos[tape.counter].delay = 0;
4155 for (i = 0; i < demo->length; i++)
4157 int demo_action = demo->data[i] & 0x0f;
4158 int demo_repeat = (demo->data[i] & 0xf0) >> 4;
4159 int tape_action = map_key_SP_to_RND(demo_action);
4160 int tape_repeat = demo_repeat + 1;
4161 byte action[MAX_TAPE_ACTIONS] = { tape_action };
4162 boolean success = 0;
4165 for (j = 0; j < tape_repeat; j++)
4166 success = TapeAddAction(action);
4170 Warn("SP demo truncated: size exceeds maximum tape size %d",
4177 TapeHaltRecording();
4181 // ----------------------------------------------------------------------------
4182 // functions for loading MM level
4183 // ----------------------------------------------------------------------------
4185 static void CopyNativeLevel_RND_to_MM(struct LevelInfo *level)
4187 struct LevelInfo_MM *level_mm = level->native_mm_level;
4190 level_mm->fieldx = MIN(level->fieldx, MM_MAX_PLAYFIELD_WIDTH);
4191 level_mm->fieldy = MIN(level->fieldy, MM_MAX_PLAYFIELD_HEIGHT);
4193 level_mm->time = level->time;
4194 level_mm->kettles_needed = level->gems_needed;
4195 level_mm->auto_count_kettles = level->auto_count_gems;
4197 level_mm->mm_laser_red = level->mm_laser_red;
4198 level_mm->mm_laser_green = level->mm_laser_green;
4199 level_mm->mm_laser_blue = level->mm_laser_blue;
4201 level_mm->df_laser_red = level->df_laser_red;
4202 level_mm->df_laser_green = level->df_laser_green;
4203 level_mm->df_laser_blue = level->df_laser_blue;
4205 strcpy(level_mm->name, level->name);
4206 strcpy(level_mm->author, level->author);
4208 level_mm->score[SC_EMERALD] = level->score[SC_EMERALD];
4209 level_mm->score[SC_PACMAN] = level->score[SC_PACMAN];
4210 level_mm->score[SC_KEY] = level->score[SC_KEY];
4211 level_mm->score[SC_TIME_BONUS] = level->score[SC_TIME_BONUS];
4212 level_mm->score[SC_ELEM_BONUS] = level->score[SC_ELEM_BONUS];
4214 level_mm->amoeba_speed = level->amoeba_speed;
4215 level_mm->time_fuse = level->mm_time_fuse;
4216 level_mm->time_bomb = level->mm_time_bomb;
4217 level_mm->time_ball = level->mm_time_ball;
4218 level_mm->time_block = level->mm_time_block;
4220 level_mm->num_ball_contents = level->num_mm_ball_contents;
4221 level_mm->ball_choice_mode = level->mm_ball_choice_mode;
4222 level_mm->rotate_ball_content = level->rotate_mm_ball_content;
4223 level_mm->explode_ball = level->explode_mm_ball;
4225 for (i = 0; i < level->num_mm_ball_contents; i++)
4226 level_mm->ball_content[i] =
4227 map_element_RND_to_MM(level->mm_ball_content[i]);
4229 for (x = 0; x < level->fieldx; x++)
4230 for (y = 0; y < level->fieldy; y++)
4232 level_mm->field[x][y] = map_element_RND_to_MM(level->field[x][y]);
4235 static void CopyNativeLevel_MM_to_RND(struct LevelInfo *level)
4237 struct LevelInfo_MM *level_mm = level->native_mm_level;
4240 level->fieldx = MIN(level_mm->fieldx, MAX_LEV_FIELDX);
4241 level->fieldy = MIN(level_mm->fieldy, MAX_LEV_FIELDY);
4243 level->time = level_mm->time;
4244 level->gems_needed = level_mm->kettles_needed;
4245 level->auto_count_gems = level_mm->auto_count_kettles;
4247 level->mm_laser_red = level_mm->mm_laser_red;
4248 level->mm_laser_green = level_mm->mm_laser_green;
4249 level->mm_laser_blue = level_mm->mm_laser_blue;
4251 level->df_laser_red = level_mm->df_laser_red;
4252 level->df_laser_green = level_mm->df_laser_green;
4253 level->df_laser_blue = level_mm->df_laser_blue;
4255 strcpy(level->name, level_mm->name);
4257 // only overwrite author from 'levelinfo.conf' if author defined in level
4258 if (!strEqual(level_mm->author, ANONYMOUS_NAME))
4259 strcpy(level->author, level_mm->author);
4261 level->score[SC_EMERALD] = level_mm->score[SC_EMERALD];
4262 level->score[SC_PACMAN] = level_mm->score[SC_PACMAN];
4263 level->score[SC_KEY] = level_mm->score[SC_KEY];
4264 level->score[SC_TIME_BONUS] = level_mm->score[SC_TIME_BONUS];
4265 level->score[SC_ELEM_BONUS] = level_mm->score[SC_ELEM_BONUS];
4267 level->amoeba_speed = level_mm->amoeba_speed;
4268 level->mm_time_fuse = level_mm->time_fuse;
4269 level->mm_time_bomb = level_mm->time_bomb;
4270 level->mm_time_ball = level_mm->time_ball;
4271 level->mm_time_block = level_mm->time_block;
4273 level->num_mm_ball_contents = level_mm->num_ball_contents;
4274 level->mm_ball_choice_mode = level_mm->ball_choice_mode;
4275 level->rotate_mm_ball_content = level_mm->rotate_ball_content;
4276 level->explode_mm_ball = level_mm->explode_ball;
4278 for (i = 0; i < level->num_mm_ball_contents; i++)
4279 level->mm_ball_content[i] =
4280 map_element_MM_to_RND(level_mm->ball_content[i]);
4282 for (x = 0; x < level->fieldx; x++)
4283 for (y = 0; y < level->fieldy; y++)
4284 level->field[x][y] = map_element_MM_to_RND(level_mm->field[x][y]);
4288 // ----------------------------------------------------------------------------
4289 // functions for loading DC level
4290 // ----------------------------------------------------------------------------
4292 #define DC_LEVEL_HEADER_SIZE 344
4294 static unsigned short getDecodedWord_DC(unsigned short data_encoded,
4297 static int last_data_encoded;
4301 int diff_hi, diff_lo;
4302 int data_hi, data_lo;
4303 unsigned short data_decoded;
4307 last_data_encoded = 0;
4314 diff = data_encoded - last_data_encoded;
4315 diff_hi = diff & ~0xff;
4316 diff_lo = diff & 0xff;
4320 data_hi = diff_hi - (offset1 << 8) + (offset2 & 0xff00);
4321 data_lo = (diff_lo + (data_hi >> 16)) & 0x00ff;
4322 data_hi = data_hi & 0xff00;
4324 data_decoded = data_hi | data_lo;
4326 last_data_encoded = data_encoded;
4328 offset1 = (offset1 + 1) % 31;
4329 offset2 = offset2 & 0xff;
4331 return data_decoded;
4334 static int getMappedElement_DC(int element)
4342 // 0x0117 - 0x036e: (?)
4345 // 0x042d - 0x0684: (?)
4361 element = EL_CRYSTAL;
4364 case 0x0e77: // quicksand (boulder)
4365 element = EL_QUICKSAND_FAST_FULL;
4368 case 0x0e99: // slow quicksand (boulder)
4369 element = EL_QUICKSAND_FULL;
4373 element = EL_EM_EXIT_OPEN;
4377 element = EL_EM_EXIT_CLOSED;
4381 element = EL_EM_STEEL_EXIT_OPEN;
4385 element = EL_EM_STEEL_EXIT_CLOSED;
4388 case 0x0f4f: // dynamite (lit 1)
4389 element = EL_EM_DYNAMITE_ACTIVE;
4392 case 0x0f57: // dynamite (lit 2)
4393 element = EL_EM_DYNAMITE_ACTIVE;
4396 case 0x0f5f: // dynamite (lit 3)
4397 element = EL_EM_DYNAMITE_ACTIVE;
4400 case 0x0f67: // dynamite (lit 4)
4401 element = EL_EM_DYNAMITE_ACTIVE;
4408 element = EL_AMOEBA_WET;
4412 element = EL_AMOEBA_DROP;
4416 element = EL_DC_MAGIC_WALL;
4420 element = EL_SPACESHIP_UP;
4424 element = EL_SPACESHIP_DOWN;
4428 element = EL_SPACESHIP_LEFT;
4432 element = EL_SPACESHIP_RIGHT;
4436 element = EL_BUG_UP;
4440 element = EL_BUG_DOWN;
4444 element = EL_BUG_LEFT;
4448 element = EL_BUG_RIGHT;
4452 element = EL_MOLE_UP;
4456 element = EL_MOLE_DOWN;
4460 element = EL_MOLE_LEFT;
4464 element = EL_MOLE_RIGHT;
4472 element = EL_YAMYAM_UP;
4476 element = EL_SWITCHGATE_OPEN;
4480 element = EL_SWITCHGATE_CLOSED;
4484 element = EL_DC_SWITCHGATE_SWITCH_UP;
4488 element = EL_TIMEGATE_CLOSED;
4491 case 0x144c: // conveyor belt switch (green)
4492 element = EL_CONVEYOR_BELT_3_SWITCH_MIDDLE;
4495 case 0x144f: // conveyor belt switch (red)
4496 element = EL_CONVEYOR_BELT_1_SWITCH_MIDDLE;
4499 case 0x1452: // conveyor belt switch (blue)
4500 element = EL_CONVEYOR_BELT_4_SWITCH_MIDDLE;
4504 element = EL_CONVEYOR_BELT_3_MIDDLE;
4508 element = EL_CONVEYOR_BELT_3_LEFT;
4512 element = EL_CONVEYOR_BELT_3_RIGHT;
4516 element = EL_CONVEYOR_BELT_1_MIDDLE;
4520 element = EL_CONVEYOR_BELT_1_LEFT;
4524 element = EL_CONVEYOR_BELT_1_RIGHT;
4528 element = EL_CONVEYOR_BELT_4_MIDDLE;
4532 element = EL_CONVEYOR_BELT_4_LEFT;
4536 element = EL_CONVEYOR_BELT_4_RIGHT;
4540 element = EL_EXPANDABLE_WALL_HORIZONTAL;
4544 element = EL_EXPANDABLE_WALL_VERTICAL;
4548 element = EL_EXPANDABLE_WALL_ANY;
4551 case 0x14ce: // growing steel wall (left/right)
4552 element = EL_EXPANDABLE_STEELWALL_HORIZONTAL;
4555 case 0x14df: // growing steel wall (up/down)
4556 element = EL_EXPANDABLE_STEELWALL_VERTICAL;
4559 case 0x14e8: // growing steel wall (up/down/left/right)
4560 element = EL_EXPANDABLE_STEELWALL_ANY;
4564 element = EL_SHIELD_DEADLY;
4568 element = EL_EXTRA_TIME;
4576 element = EL_EMPTY_SPACE;
4579 case 0x1578: // quicksand (empty)
4580 element = EL_QUICKSAND_FAST_EMPTY;
4583 case 0x1579: // slow quicksand (empty)
4584 element = EL_QUICKSAND_EMPTY;
4594 element = EL_EM_DYNAMITE;
4597 case 0x15a1: // key (red)
4598 element = EL_EM_KEY_1;
4601 case 0x15a2: // key (yellow)
4602 element = EL_EM_KEY_2;
4605 case 0x15a3: // key (blue)
4606 element = EL_EM_KEY_4;
4609 case 0x15a4: // key (green)
4610 element = EL_EM_KEY_3;
4613 case 0x15a5: // key (white)
4614 element = EL_DC_KEY_WHITE;
4618 element = EL_WALL_SLIPPERY;
4625 case 0x15a8: // wall (not round)
4629 case 0x15a9: // (blue)
4630 element = EL_CHAR_A;
4633 case 0x15aa: // (blue)
4634 element = EL_CHAR_B;
4637 case 0x15ab: // (blue)
4638 element = EL_CHAR_C;
4641 case 0x15ac: // (blue)
4642 element = EL_CHAR_D;
4645 case 0x15ad: // (blue)
4646 element = EL_CHAR_E;
4649 case 0x15ae: // (blue)
4650 element = EL_CHAR_F;
4653 case 0x15af: // (blue)
4654 element = EL_CHAR_G;
4657 case 0x15b0: // (blue)
4658 element = EL_CHAR_H;
4661 case 0x15b1: // (blue)
4662 element = EL_CHAR_I;
4665 case 0x15b2: // (blue)
4666 element = EL_CHAR_J;
4669 case 0x15b3: // (blue)
4670 element = EL_CHAR_K;
4673 case 0x15b4: // (blue)
4674 element = EL_CHAR_L;
4677 case 0x15b5: // (blue)
4678 element = EL_CHAR_M;
4681 case 0x15b6: // (blue)
4682 element = EL_CHAR_N;
4685 case 0x15b7: // (blue)
4686 element = EL_CHAR_O;
4689 case 0x15b8: // (blue)
4690 element = EL_CHAR_P;
4693 case 0x15b9: // (blue)
4694 element = EL_CHAR_Q;
4697 case 0x15ba: // (blue)
4698 element = EL_CHAR_R;
4701 case 0x15bb: // (blue)
4702 element = EL_CHAR_S;
4705 case 0x15bc: // (blue)
4706 element = EL_CHAR_T;
4709 case 0x15bd: // (blue)
4710 element = EL_CHAR_U;
4713 case 0x15be: // (blue)
4714 element = EL_CHAR_V;
4717 case 0x15bf: // (blue)
4718 element = EL_CHAR_W;
4721 case 0x15c0: // (blue)
4722 element = EL_CHAR_X;
4725 case 0x15c1: // (blue)
4726 element = EL_CHAR_Y;
4729 case 0x15c2: // (blue)
4730 element = EL_CHAR_Z;
4733 case 0x15c3: // (blue)
4734 element = EL_CHAR_AUMLAUT;
4737 case 0x15c4: // (blue)
4738 element = EL_CHAR_OUMLAUT;
4741 case 0x15c5: // (blue)
4742 element = EL_CHAR_UUMLAUT;
4745 case 0x15c6: // (blue)
4746 element = EL_CHAR_0;
4749 case 0x15c7: // (blue)
4750 element = EL_CHAR_1;
4753 case 0x15c8: // (blue)
4754 element = EL_CHAR_2;
4757 case 0x15c9: // (blue)
4758 element = EL_CHAR_3;
4761 case 0x15ca: // (blue)
4762 element = EL_CHAR_4;
4765 case 0x15cb: // (blue)
4766 element = EL_CHAR_5;
4769 case 0x15cc: // (blue)
4770 element = EL_CHAR_6;
4773 case 0x15cd: // (blue)
4774 element = EL_CHAR_7;
4777 case 0x15ce: // (blue)
4778 element = EL_CHAR_8;
4781 case 0x15cf: // (blue)
4782 element = EL_CHAR_9;
4785 case 0x15d0: // (blue)
4786 element = EL_CHAR_PERIOD;
4789 case 0x15d1: // (blue)
4790 element = EL_CHAR_EXCLAM;
4793 case 0x15d2: // (blue)
4794 element = EL_CHAR_COLON;
4797 case 0x15d3: // (blue)
4798 element = EL_CHAR_LESS;
4801 case 0x15d4: // (blue)
4802 element = EL_CHAR_GREATER;
4805 case 0x15d5: // (blue)
4806 element = EL_CHAR_QUESTION;
4809 case 0x15d6: // (blue)
4810 element = EL_CHAR_COPYRIGHT;
4813 case 0x15d7: // (blue)
4814 element = EL_CHAR_UP;
4817 case 0x15d8: // (blue)
4818 element = EL_CHAR_DOWN;
4821 case 0x15d9: // (blue)
4822 element = EL_CHAR_BUTTON;
4825 case 0x15da: // (blue)
4826 element = EL_CHAR_PLUS;
4829 case 0x15db: // (blue)
4830 element = EL_CHAR_MINUS;
4833 case 0x15dc: // (blue)
4834 element = EL_CHAR_APOSTROPHE;
4837 case 0x15dd: // (blue)
4838 element = EL_CHAR_PARENLEFT;
4841 case 0x15de: // (blue)
4842 element = EL_CHAR_PARENRIGHT;
4845 case 0x15df: // (green)
4846 element = EL_CHAR_A;
4849 case 0x15e0: // (green)
4850 element = EL_CHAR_B;
4853 case 0x15e1: // (green)
4854 element = EL_CHAR_C;
4857 case 0x15e2: // (green)
4858 element = EL_CHAR_D;
4861 case 0x15e3: // (green)
4862 element = EL_CHAR_E;
4865 case 0x15e4: // (green)
4866 element = EL_CHAR_F;
4869 case 0x15e5: // (green)
4870 element = EL_CHAR_G;
4873 case 0x15e6: // (green)
4874 element = EL_CHAR_H;
4877 case 0x15e7: // (green)
4878 element = EL_CHAR_I;
4881 case 0x15e8: // (green)
4882 element = EL_CHAR_J;
4885 case 0x15e9: // (green)
4886 element = EL_CHAR_K;
4889 case 0x15ea: // (green)
4890 element = EL_CHAR_L;
4893 case 0x15eb: // (green)
4894 element = EL_CHAR_M;
4897 case 0x15ec: // (green)
4898 element = EL_CHAR_N;
4901 case 0x15ed: // (green)
4902 element = EL_CHAR_O;
4905 case 0x15ee: // (green)
4906 element = EL_CHAR_P;
4909 case 0x15ef: // (green)
4910 element = EL_CHAR_Q;
4913 case 0x15f0: // (green)
4914 element = EL_CHAR_R;
4917 case 0x15f1: // (green)
4918 element = EL_CHAR_S;
4921 case 0x15f2: // (green)
4922 element = EL_CHAR_T;
4925 case 0x15f3: // (green)
4926 element = EL_CHAR_U;
4929 case 0x15f4: // (green)
4930 element = EL_CHAR_V;
4933 case 0x15f5: // (green)
4934 element = EL_CHAR_W;
4937 case 0x15f6: // (green)
4938 element = EL_CHAR_X;
4941 case 0x15f7: // (green)
4942 element = EL_CHAR_Y;
4945 case 0x15f8: // (green)
4946 element = EL_CHAR_Z;
4949 case 0x15f9: // (green)
4950 element = EL_CHAR_AUMLAUT;
4953 case 0x15fa: // (green)
4954 element = EL_CHAR_OUMLAUT;
4957 case 0x15fb: // (green)
4958 element = EL_CHAR_UUMLAUT;
4961 case 0x15fc: // (green)
4962 element = EL_CHAR_0;
4965 case 0x15fd: // (green)
4966 element = EL_CHAR_1;
4969 case 0x15fe: // (green)
4970 element = EL_CHAR_2;
4973 case 0x15ff: // (green)
4974 element = EL_CHAR_3;
4977 case 0x1600: // (green)
4978 element = EL_CHAR_4;
4981 case 0x1601: // (green)
4982 element = EL_CHAR_5;
4985 case 0x1602: // (green)
4986 element = EL_CHAR_6;
4989 case 0x1603: // (green)
4990 element = EL_CHAR_7;
4993 case 0x1604: // (green)
4994 element = EL_CHAR_8;
4997 case 0x1605: // (green)
4998 element = EL_CHAR_9;
5001 case 0x1606: // (green)
5002 element = EL_CHAR_PERIOD;
5005 case 0x1607: // (green)
5006 element = EL_CHAR_EXCLAM;
5009 case 0x1608: // (green)
5010 element = EL_CHAR_COLON;
5013 case 0x1609: // (green)
5014 element = EL_CHAR_LESS;
5017 case 0x160a: // (green)
5018 element = EL_CHAR_GREATER;
5021 case 0x160b: // (green)
5022 element = EL_CHAR_QUESTION;
5025 case 0x160c: // (green)
5026 element = EL_CHAR_COPYRIGHT;
5029 case 0x160d: // (green)
5030 element = EL_CHAR_UP;
5033 case 0x160e: // (green)
5034 element = EL_CHAR_DOWN;
5037 case 0x160f: // (green)
5038 element = EL_CHAR_BUTTON;
5041 case 0x1610: // (green)
5042 element = EL_CHAR_PLUS;
5045 case 0x1611: // (green)
5046 element = EL_CHAR_MINUS;
5049 case 0x1612: // (green)
5050 element = EL_CHAR_APOSTROPHE;
5053 case 0x1613: // (green)
5054 element = EL_CHAR_PARENLEFT;
5057 case 0x1614: // (green)
5058 element = EL_CHAR_PARENRIGHT;
5061 case 0x1615: // (blue steel)
5062 element = EL_STEEL_CHAR_A;
5065 case 0x1616: // (blue steel)
5066 element = EL_STEEL_CHAR_B;
5069 case 0x1617: // (blue steel)
5070 element = EL_STEEL_CHAR_C;
5073 case 0x1618: // (blue steel)
5074 element = EL_STEEL_CHAR_D;
5077 case 0x1619: // (blue steel)
5078 element = EL_STEEL_CHAR_E;
5081 case 0x161a: // (blue steel)
5082 element = EL_STEEL_CHAR_F;
5085 case 0x161b: // (blue steel)
5086 element = EL_STEEL_CHAR_G;
5089 case 0x161c: // (blue steel)
5090 element = EL_STEEL_CHAR_H;
5093 case 0x161d: // (blue steel)
5094 element = EL_STEEL_CHAR_I;
5097 case 0x161e: // (blue steel)
5098 element = EL_STEEL_CHAR_J;
5101 case 0x161f: // (blue steel)
5102 element = EL_STEEL_CHAR_K;
5105 case 0x1620: // (blue steel)
5106 element = EL_STEEL_CHAR_L;
5109 case 0x1621: // (blue steel)
5110 element = EL_STEEL_CHAR_M;
5113 case 0x1622: // (blue steel)
5114 element = EL_STEEL_CHAR_N;
5117 case 0x1623: // (blue steel)
5118 element = EL_STEEL_CHAR_O;
5121 case 0x1624: // (blue steel)
5122 element = EL_STEEL_CHAR_P;
5125 case 0x1625: // (blue steel)
5126 element = EL_STEEL_CHAR_Q;
5129 case 0x1626: // (blue steel)
5130 element = EL_STEEL_CHAR_R;
5133 case 0x1627: // (blue steel)
5134 element = EL_STEEL_CHAR_S;
5137 case 0x1628: // (blue steel)
5138 element = EL_STEEL_CHAR_T;
5141 case 0x1629: // (blue steel)
5142 element = EL_STEEL_CHAR_U;
5145 case 0x162a: // (blue steel)
5146 element = EL_STEEL_CHAR_V;
5149 case 0x162b: // (blue steel)
5150 element = EL_STEEL_CHAR_W;
5153 case 0x162c: // (blue steel)
5154 element = EL_STEEL_CHAR_X;
5157 case 0x162d: // (blue steel)
5158 element = EL_STEEL_CHAR_Y;
5161 case 0x162e: // (blue steel)
5162 element = EL_STEEL_CHAR_Z;
5165 case 0x162f: // (blue steel)
5166 element = EL_STEEL_CHAR_AUMLAUT;
5169 case 0x1630: // (blue steel)
5170 element = EL_STEEL_CHAR_OUMLAUT;
5173 case 0x1631: // (blue steel)
5174 element = EL_STEEL_CHAR_UUMLAUT;
5177 case 0x1632: // (blue steel)
5178 element = EL_STEEL_CHAR_0;
5181 case 0x1633: // (blue steel)
5182 element = EL_STEEL_CHAR_1;
5185 case 0x1634: // (blue steel)
5186 element = EL_STEEL_CHAR_2;
5189 case 0x1635: // (blue steel)
5190 element = EL_STEEL_CHAR_3;
5193 case 0x1636: // (blue steel)
5194 element = EL_STEEL_CHAR_4;
5197 case 0x1637: // (blue steel)
5198 element = EL_STEEL_CHAR_5;
5201 case 0x1638: // (blue steel)
5202 element = EL_STEEL_CHAR_6;
5205 case 0x1639: // (blue steel)
5206 element = EL_STEEL_CHAR_7;
5209 case 0x163a: // (blue steel)
5210 element = EL_STEEL_CHAR_8;
5213 case 0x163b: // (blue steel)
5214 element = EL_STEEL_CHAR_9;
5217 case 0x163c: // (blue steel)
5218 element = EL_STEEL_CHAR_PERIOD;
5221 case 0x163d: // (blue steel)
5222 element = EL_STEEL_CHAR_EXCLAM;
5225 case 0x163e: // (blue steel)
5226 element = EL_STEEL_CHAR_COLON;
5229 case 0x163f: // (blue steel)
5230 element = EL_STEEL_CHAR_LESS;
5233 case 0x1640: // (blue steel)
5234 element = EL_STEEL_CHAR_GREATER;
5237 case 0x1641: // (blue steel)
5238 element = EL_STEEL_CHAR_QUESTION;
5241 case 0x1642: // (blue steel)
5242 element = EL_STEEL_CHAR_COPYRIGHT;
5245 case 0x1643: // (blue steel)
5246 element = EL_STEEL_CHAR_UP;
5249 case 0x1644: // (blue steel)
5250 element = EL_STEEL_CHAR_DOWN;
5253 case 0x1645: // (blue steel)
5254 element = EL_STEEL_CHAR_BUTTON;
5257 case 0x1646: // (blue steel)
5258 element = EL_STEEL_CHAR_PLUS;
5261 case 0x1647: // (blue steel)
5262 element = EL_STEEL_CHAR_MINUS;
5265 case 0x1648: // (blue steel)
5266 element = EL_STEEL_CHAR_APOSTROPHE;
5269 case 0x1649: // (blue steel)
5270 element = EL_STEEL_CHAR_PARENLEFT;
5273 case 0x164a: // (blue steel)
5274 element = EL_STEEL_CHAR_PARENRIGHT;
5277 case 0x164b: // (green steel)
5278 element = EL_STEEL_CHAR_A;
5281 case 0x164c: // (green steel)
5282 element = EL_STEEL_CHAR_B;
5285 case 0x164d: // (green steel)
5286 element = EL_STEEL_CHAR_C;
5289 case 0x164e: // (green steel)
5290 element = EL_STEEL_CHAR_D;
5293 case 0x164f: // (green steel)
5294 element = EL_STEEL_CHAR_E;
5297 case 0x1650: // (green steel)
5298 element = EL_STEEL_CHAR_F;
5301 case 0x1651: // (green steel)
5302 element = EL_STEEL_CHAR_G;
5305 case 0x1652: // (green steel)
5306 element = EL_STEEL_CHAR_H;
5309 case 0x1653: // (green steel)
5310 element = EL_STEEL_CHAR_I;
5313 case 0x1654: // (green steel)
5314 element = EL_STEEL_CHAR_J;
5317 case 0x1655: // (green steel)
5318 element = EL_STEEL_CHAR_K;
5321 case 0x1656: // (green steel)
5322 element = EL_STEEL_CHAR_L;
5325 case 0x1657: // (green steel)
5326 element = EL_STEEL_CHAR_M;
5329 case 0x1658: // (green steel)
5330 element = EL_STEEL_CHAR_N;
5333 case 0x1659: // (green steel)
5334 element = EL_STEEL_CHAR_O;
5337 case 0x165a: // (green steel)
5338 element = EL_STEEL_CHAR_P;
5341 case 0x165b: // (green steel)
5342 element = EL_STEEL_CHAR_Q;
5345 case 0x165c: // (green steel)
5346 element = EL_STEEL_CHAR_R;
5349 case 0x165d: // (green steel)
5350 element = EL_STEEL_CHAR_S;
5353 case 0x165e: // (green steel)
5354 element = EL_STEEL_CHAR_T;
5357 case 0x165f: // (green steel)
5358 element = EL_STEEL_CHAR_U;
5361 case 0x1660: // (green steel)
5362 element = EL_STEEL_CHAR_V;
5365 case 0x1661: // (green steel)
5366 element = EL_STEEL_CHAR_W;
5369 case 0x1662: // (green steel)
5370 element = EL_STEEL_CHAR_X;
5373 case 0x1663: // (green steel)
5374 element = EL_STEEL_CHAR_Y;
5377 case 0x1664: // (green steel)
5378 element = EL_STEEL_CHAR_Z;
5381 case 0x1665: // (green steel)
5382 element = EL_STEEL_CHAR_AUMLAUT;
5385 case 0x1666: // (green steel)
5386 element = EL_STEEL_CHAR_OUMLAUT;
5389 case 0x1667: // (green steel)
5390 element = EL_STEEL_CHAR_UUMLAUT;
5393 case 0x1668: // (green steel)
5394 element = EL_STEEL_CHAR_0;
5397 case 0x1669: // (green steel)
5398 element = EL_STEEL_CHAR_1;
5401 case 0x166a: // (green steel)
5402 element = EL_STEEL_CHAR_2;
5405 case 0x166b: // (green steel)
5406 element = EL_STEEL_CHAR_3;
5409 case 0x166c: // (green steel)
5410 element = EL_STEEL_CHAR_4;
5413 case 0x166d: // (green steel)
5414 element = EL_STEEL_CHAR_5;
5417 case 0x166e: // (green steel)
5418 element = EL_STEEL_CHAR_6;
5421 case 0x166f: // (green steel)
5422 element = EL_STEEL_CHAR_7;
5425 case 0x1670: // (green steel)
5426 element = EL_STEEL_CHAR_8;
5429 case 0x1671: // (green steel)
5430 element = EL_STEEL_CHAR_9;
5433 case 0x1672: // (green steel)
5434 element = EL_STEEL_CHAR_PERIOD;
5437 case 0x1673: // (green steel)
5438 element = EL_STEEL_CHAR_EXCLAM;
5441 case 0x1674: // (green steel)
5442 element = EL_STEEL_CHAR_COLON;
5445 case 0x1675: // (green steel)
5446 element = EL_STEEL_CHAR_LESS;
5449 case 0x1676: // (green steel)
5450 element = EL_STEEL_CHAR_GREATER;
5453 case 0x1677: // (green steel)
5454 element = EL_STEEL_CHAR_QUESTION;
5457 case 0x1678: // (green steel)
5458 element = EL_STEEL_CHAR_COPYRIGHT;
5461 case 0x1679: // (green steel)
5462 element = EL_STEEL_CHAR_UP;
5465 case 0x167a: // (green steel)
5466 element = EL_STEEL_CHAR_DOWN;
5469 case 0x167b: // (green steel)
5470 element = EL_STEEL_CHAR_BUTTON;
5473 case 0x167c: // (green steel)
5474 element = EL_STEEL_CHAR_PLUS;
5477 case 0x167d: // (green steel)
5478 element = EL_STEEL_CHAR_MINUS;
5481 case 0x167e: // (green steel)
5482 element = EL_STEEL_CHAR_APOSTROPHE;
5485 case 0x167f: // (green steel)
5486 element = EL_STEEL_CHAR_PARENLEFT;
5489 case 0x1680: // (green steel)
5490 element = EL_STEEL_CHAR_PARENRIGHT;
5493 case 0x1681: // gate (red)
5494 element = EL_EM_GATE_1;
5497 case 0x1682: // secret gate (red)
5498 element = EL_EM_GATE_1_GRAY;
5501 case 0x1683: // gate (yellow)
5502 element = EL_EM_GATE_2;
5505 case 0x1684: // secret gate (yellow)
5506 element = EL_EM_GATE_2_GRAY;
5509 case 0x1685: // gate (blue)
5510 element = EL_EM_GATE_4;
5513 case 0x1686: // secret gate (blue)
5514 element = EL_EM_GATE_4_GRAY;
5517 case 0x1687: // gate (green)
5518 element = EL_EM_GATE_3;
5521 case 0x1688: // secret gate (green)
5522 element = EL_EM_GATE_3_GRAY;
5525 case 0x1689: // gate (white)
5526 element = EL_DC_GATE_WHITE;
5529 case 0x168a: // secret gate (white)
5530 element = EL_DC_GATE_WHITE_GRAY;
5533 case 0x168b: // secret gate (no key)
5534 element = EL_DC_GATE_FAKE_GRAY;
5538 element = EL_ROBOT_WHEEL;
5542 element = EL_DC_TIMEGATE_SWITCH;
5546 element = EL_ACID_POOL_BOTTOM;
5550 element = EL_ACID_POOL_TOPLEFT;
5554 element = EL_ACID_POOL_TOPRIGHT;
5558 element = EL_ACID_POOL_BOTTOMLEFT;
5562 element = EL_ACID_POOL_BOTTOMRIGHT;
5566 element = EL_STEELWALL;
5570 element = EL_STEELWALL_SLIPPERY;
5573 case 0x1695: // steel wall (not round)
5574 element = EL_STEELWALL;
5577 case 0x1696: // steel wall (left)
5578 element = EL_DC_STEELWALL_1_LEFT;
5581 case 0x1697: // steel wall (bottom)
5582 element = EL_DC_STEELWALL_1_BOTTOM;
5585 case 0x1698: // steel wall (right)
5586 element = EL_DC_STEELWALL_1_RIGHT;
5589 case 0x1699: // steel wall (top)
5590 element = EL_DC_STEELWALL_1_TOP;
5593 case 0x169a: // steel wall (left/bottom)
5594 element = EL_DC_STEELWALL_1_BOTTOMLEFT;
5597 case 0x169b: // steel wall (right/bottom)
5598 element = EL_DC_STEELWALL_1_BOTTOMRIGHT;
5601 case 0x169c: // steel wall (right/top)
5602 element = EL_DC_STEELWALL_1_TOPRIGHT;
5605 case 0x169d: // steel wall (left/top)
5606 element = EL_DC_STEELWALL_1_TOPLEFT;
5609 case 0x169e: // steel wall (right/bottom small)
5610 element = EL_DC_STEELWALL_1_BOTTOMRIGHT_2;
5613 case 0x169f: // steel wall (left/bottom small)
5614 element = EL_DC_STEELWALL_1_BOTTOMLEFT_2;
5617 case 0x16a0: // steel wall (right/top small)
5618 element = EL_DC_STEELWALL_1_TOPRIGHT_2;
5621 case 0x16a1: // steel wall (left/top small)
5622 element = EL_DC_STEELWALL_1_TOPLEFT_2;
5625 case 0x16a2: // steel wall (left/right)
5626 element = EL_DC_STEELWALL_1_VERTICAL;
5629 case 0x16a3: // steel wall (top/bottom)
5630 element = EL_DC_STEELWALL_1_HORIZONTAL;
5633 case 0x16a4: // steel wall 2 (left end)
5634 element = EL_DC_STEELWALL_2_LEFT;
5637 case 0x16a5: // steel wall 2 (right end)
5638 element = EL_DC_STEELWALL_2_RIGHT;
5641 case 0x16a6: // steel wall 2 (top end)
5642 element = EL_DC_STEELWALL_2_TOP;
5645 case 0x16a7: // steel wall 2 (bottom end)
5646 element = EL_DC_STEELWALL_2_BOTTOM;
5649 case 0x16a8: // steel wall 2 (left/right)
5650 element = EL_DC_STEELWALL_2_HORIZONTAL;
5653 case 0x16a9: // steel wall 2 (up/down)
5654 element = EL_DC_STEELWALL_2_VERTICAL;
5657 case 0x16aa: // steel wall 2 (mid)
5658 element = EL_DC_STEELWALL_2_MIDDLE;
5662 element = EL_SIGN_EXCLAMATION;
5666 element = EL_SIGN_RADIOACTIVITY;
5670 element = EL_SIGN_STOP;
5674 element = EL_SIGN_WHEELCHAIR;
5678 element = EL_SIGN_PARKING;
5682 element = EL_SIGN_NO_ENTRY;
5686 element = EL_SIGN_HEART;
5690 element = EL_SIGN_GIVE_WAY;
5694 element = EL_SIGN_ENTRY_FORBIDDEN;
5698 element = EL_SIGN_EMERGENCY_EXIT;
5702 element = EL_SIGN_YIN_YANG;
5706 element = EL_WALL_EMERALD;
5710 element = EL_WALL_DIAMOND;
5714 element = EL_WALL_PEARL;
5718 element = EL_WALL_CRYSTAL;
5722 element = EL_INVISIBLE_WALL;
5726 element = EL_INVISIBLE_STEELWALL;
5730 // EL_INVISIBLE_SAND
5733 element = EL_LIGHT_SWITCH;
5737 element = EL_ENVELOPE_1;
5741 if (element >= 0x0117 && element <= 0x036e) // (?)
5742 element = EL_DIAMOND;
5743 else if (element >= 0x042d && element <= 0x0684) // (?)
5744 element = EL_EMERALD;
5745 else if (element >= 0x157c && element <= 0x158b)
5747 else if (element >= 0x1590 && element <= 0x159f)
5748 element = EL_DC_LANDMINE;
5749 else if (element >= 0x16bc && element <= 0x16cb)
5750 element = EL_INVISIBLE_SAND;
5753 Warn("unknown Diamond Caves element 0x%04x", element);
5755 element = EL_UNKNOWN;
5760 return getMappedElement(element);
5763 static void LoadLevelFromFileStream_DC(File *file, struct LevelInfo *level)
5765 byte header[DC_LEVEL_HEADER_SIZE];
5767 int envelope_header_pos = 62;
5768 int envelope_content_pos = 94;
5769 int level_name_pos = 251;
5770 int level_author_pos = 292;
5771 int envelope_header_len;
5772 int envelope_content_len;
5774 int level_author_len;
5776 int num_yamyam_contents;
5779 getDecodedWord_DC(0, TRUE); // initialize DC2 decoding engine
5781 for (i = 0; i < DC_LEVEL_HEADER_SIZE / 2; i++)
5783 unsigned short header_word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
5785 header[i * 2 + 0] = header_word >> 8;
5786 header[i * 2 + 1] = header_word & 0xff;
5789 // read some values from level header to check level decoding integrity
5790 fieldx = header[6] | (header[7] << 8);
5791 fieldy = header[8] | (header[9] << 8);
5792 num_yamyam_contents = header[60] | (header[61] << 8);
5794 // do some simple sanity checks to ensure that level was correctly decoded
5795 if (fieldx < 1 || fieldx > 256 ||
5796 fieldy < 1 || fieldy > 256 ||
5797 num_yamyam_contents < 1 || num_yamyam_contents > 8)
5799 level->no_valid_file = TRUE;
5801 Warn("cannot decode level from stream -- using empty level");
5806 // maximum envelope header size is 31 bytes
5807 envelope_header_len = header[envelope_header_pos];
5808 // maximum envelope content size is 110 (156?) bytes
5809 envelope_content_len = header[envelope_content_pos];
5811 // maximum level title size is 40 bytes
5812 level_name_len = MIN(header[level_name_pos], MAX_LEVEL_NAME_LEN);
5813 // maximum level author size is 30 (51?) bytes
5814 level_author_len = MIN(header[level_author_pos], MAX_LEVEL_AUTHOR_LEN);
5818 for (i = 0; i < envelope_header_len; i++)
5819 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
5820 level->envelope[0].text[envelope_size++] =
5821 header[envelope_header_pos + 1 + i];
5823 if (envelope_header_len > 0 && envelope_content_len > 0)
5825 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
5826 level->envelope[0].text[envelope_size++] = '\n';
5827 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
5828 level->envelope[0].text[envelope_size++] = '\n';
5831 for (i = 0; i < envelope_content_len; i++)
5832 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
5833 level->envelope[0].text[envelope_size++] =
5834 header[envelope_content_pos + 1 + i];
5836 level->envelope[0].text[envelope_size] = '\0';
5838 level->envelope[0].xsize = MAX_ENVELOPE_XSIZE;
5839 level->envelope[0].ysize = 10;
5840 level->envelope[0].autowrap = TRUE;
5841 level->envelope[0].centered = TRUE;
5843 for (i = 0; i < level_name_len; i++)
5844 level->name[i] = header[level_name_pos + 1 + i];
5845 level->name[level_name_len] = '\0';
5847 for (i = 0; i < level_author_len; i++)
5848 level->author[i] = header[level_author_pos + 1 + i];
5849 level->author[level_author_len] = '\0';
5851 num_yamyam_contents = header[60] | (header[61] << 8);
5852 level->num_yamyam_contents =
5853 MIN(MAX(MIN_ELEMENT_CONTENTS, num_yamyam_contents), MAX_ELEMENT_CONTENTS);
5855 for (i = 0; i < num_yamyam_contents; i++)
5857 for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
5859 unsigned short word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
5860 int element_dc = ((word & 0xff) << 8) | ((word >> 8) & 0xff);
5862 if (i < MAX_ELEMENT_CONTENTS)
5863 level->yamyam_content[i].e[x][y] = getMappedElement_DC(element_dc);
5867 fieldx = header[6] | (header[7] << 8);
5868 fieldy = header[8] | (header[9] << 8);
5869 level->fieldx = MIN(MAX(MIN_LEV_FIELDX, fieldx), MAX_LEV_FIELDX);
5870 level->fieldy = MIN(MAX(MIN_LEV_FIELDY, fieldy), MAX_LEV_FIELDY);
5872 for (y = 0; y < fieldy; y++) for (x = 0; x < fieldx; x++)
5874 unsigned short word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
5875 int element_dc = ((word & 0xff) << 8) | ((word >> 8) & 0xff);
5877 if (x < MAX_LEV_FIELDX && y < MAX_LEV_FIELDY)
5878 level->field[x][y] = getMappedElement_DC(element_dc);
5881 x = MIN(MAX(0, (header[10] | (header[11] << 8)) - 1), MAX_LEV_FIELDX - 1);
5882 y = MIN(MAX(0, (header[12] | (header[13] << 8)) - 1), MAX_LEV_FIELDY - 1);
5883 level->field[x][y] = EL_PLAYER_1;
5885 x = MIN(MAX(0, (header[14] | (header[15] << 8)) - 1), MAX_LEV_FIELDX - 1);
5886 y = MIN(MAX(0, (header[16] | (header[17] << 8)) - 1), MAX_LEV_FIELDY - 1);
5887 level->field[x][y] = EL_PLAYER_2;
5889 level->gems_needed = header[18] | (header[19] << 8);
5891 level->score[SC_EMERALD] = header[20] | (header[21] << 8);
5892 level->score[SC_DIAMOND] = header[22] | (header[23] << 8);
5893 level->score[SC_PEARL] = header[24] | (header[25] << 8);
5894 level->score[SC_CRYSTAL] = header[26] | (header[27] << 8);
5895 level->score[SC_NUT] = header[28] | (header[29] << 8);
5896 level->score[SC_ROBOT] = header[30] | (header[31] << 8);
5897 level->score[SC_SPACESHIP] = header[32] | (header[33] << 8);
5898 level->score[SC_BUG] = header[34] | (header[35] << 8);
5899 level->score[SC_YAMYAM] = header[36] | (header[37] << 8);
5900 level->score[SC_DYNAMITE] = header[38] | (header[39] << 8);
5901 level->score[SC_KEY] = header[40] | (header[41] << 8);
5902 level->score[SC_TIME_BONUS] = header[42] | (header[43] << 8);
5904 level->time = header[44] | (header[45] << 8);
5906 level->amoeba_speed = header[46] | (header[47] << 8);
5907 level->time_light = header[48] | (header[49] << 8);
5908 level->time_timegate = header[50] | (header[51] << 8);
5909 level->time_wheel = header[52] | (header[53] << 8);
5910 level->time_magic_wall = header[54] | (header[55] << 8);
5911 level->extra_time = header[56] | (header[57] << 8);
5912 level->shield_normal_time = header[58] | (header[59] << 8);
5914 // shield and extra time elements do not have a score
5915 level->score[SC_SHIELD] = 0;
5916 level->extra_time_score = 0;
5918 // set time for normal and deadly shields to the same value
5919 level->shield_deadly_time = level->shield_normal_time;
5921 // Diamond Caves has the same (strange) behaviour as Emerald Mine that gems
5922 // can slip down from flat walls, like normal walls and steel walls
5923 level->em_slippery_gems = TRUE;
5925 // time score is counted for each 10 seconds left in Diamond Caves levels
5926 level->time_score_base = 10;
5929 static void LoadLevelFromFileInfo_DC(struct LevelInfo *level,
5930 struct LevelFileInfo *level_file_info,
5931 boolean level_info_only)
5933 char *filename = level_file_info->filename;
5935 int num_magic_bytes = 8;
5936 char magic_bytes[num_magic_bytes + 1];
5937 int num_levels_to_skip = level_file_info->nr - leveldir_current->first_level;
5939 if (!(file = openFile(filename, MODE_READ)))
5941 level->no_valid_file = TRUE;
5943 if (!level_info_only)
5944 Warn("cannot read level '%s' -- using empty level", filename);
5949 // fseek(file, 0x0000, SEEK_SET);
5951 if (level_file_info->packed)
5953 // read "magic bytes" from start of file
5954 if (getStringFromFile(file, magic_bytes, num_magic_bytes + 1) == NULL)
5955 magic_bytes[0] = '\0';
5957 // check "magic bytes" for correct file format
5958 if (!strPrefix(magic_bytes, "DC2"))
5960 level->no_valid_file = TRUE;
5962 Warn("unknown DC level file '%s' -- using empty level", filename);
5967 if (strPrefix(magic_bytes, "DC2Win95") ||
5968 strPrefix(magic_bytes, "DC2Win98"))
5970 int position_first_level = 0x00fa;
5971 int extra_bytes = 4;
5974 // advance file stream to first level inside the level package
5975 skip_bytes = position_first_level - num_magic_bytes - extra_bytes;
5977 // each block of level data is followed by block of non-level data
5978 num_levels_to_skip *= 2;
5980 // at least skip header bytes, therefore use ">= 0" instead of "> 0"
5981 while (num_levels_to_skip >= 0)
5983 // advance file stream to next level inside the level package
5984 if (seekFile(file, skip_bytes, SEEK_CUR) != 0)
5986 level->no_valid_file = TRUE;
5988 Warn("cannot fseek in file '%s' -- using empty level", filename);
5993 // skip apparently unused extra bytes following each level
5994 ReadUnusedBytesFromFile(file, extra_bytes);
5996 // read size of next level in level package
5997 skip_bytes = getFile32BitLE(file);
5999 num_levels_to_skip--;
6004 level->no_valid_file = TRUE;
6006 Warn("unknown DC2 level file '%s' -- using empty level", filename);
6012 LoadLevelFromFileStream_DC(file, level);
6018 // ----------------------------------------------------------------------------
6019 // functions for loading SB level
6020 // ----------------------------------------------------------------------------
6022 int getMappedElement_SB(int element_ascii, boolean use_ces)
6030 sb_element_mapping[] =
6032 { ' ', EL_EMPTY, EL_CUSTOM_1 }, // floor (space)
6033 { '#', EL_STEELWALL, EL_CUSTOM_2 }, // wall
6034 { '@', EL_PLAYER_1, EL_CUSTOM_3 }, // player
6035 { '$', EL_SOKOBAN_OBJECT, EL_CUSTOM_4 }, // box
6036 { '.', EL_SOKOBAN_FIELD_EMPTY, EL_CUSTOM_5 }, // goal square
6037 { '*', EL_SOKOBAN_FIELD_FULL, EL_CUSTOM_6 }, // box on goal square
6038 { '+', EL_SOKOBAN_FIELD_PLAYER, EL_CUSTOM_7 }, // player on goal square
6039 { '_', EL_INVISIBLE_STEELWALL, EL_FROM_LEVEL_TEMPLATE }, // floor beyond border
6046 for (i = 0; sb_element_mapping[i].ascii != 0; i++)
6047 if (element_ascii == sb_element_mapping[i].ascii)
6048 return (use_ces ? sb_element_mapping[i].ce : sb_element_mapping[i].sb);
6050 return EL_UNDEFINED;
6053 static void SetLevelSettings_SB(struct LevelInfo *level)
6057 level->use_step_counter = TRUE;
6060 level->score[SC_TIME_BONUS] = 0;
6061 level->time_score_base = 1;
6062 level->rate_time_over_score = TRUE;
6065 level->auto_exit_sokoban = TRUE;
6068 static void LoadLevelFromFileInfo_SB(struct LevelInfo *level,
6069 struct LevelFileInfo *level_file_info,
6070 boolean level_info_only)
6072 char *filename = level_file_info->filename;
6073 char line[MAX_LINE_LEN], line_raw[MAX_LINE_LEN], previous_line[MAX_LINE_LEN];
6074 char last_comment[MAX_LINE_LEN];
6075 char level_name[MAX_LINE_LEN];
6078 int num_levels_to_skip = level_file_info->nr - leveldir_current->first_level;
6079 boolean read_continued_line = FALSE;
6080 boolean reading_playfield = FALSE;
6081 boolean got_valid_playfield_line = FALSE;
6082 boolean invalid_playfield_char = FALSE;
6083 boolean load_xsb_to_ces = check_special_flags("load_xsb_to_ces");
6084 int file_level_nr = 0;
6086 int x = 0, y = 0; // initialized to make compilers happy
6088 last_comment[0] = '\0';
6089 level_name[0] = '\0';
6091 if (!(file = openFile(filename, MODE_READ)))
6093 level->no_valid_file = TRUE;
6095 if (!level_info_only)
6096 Warn("cannot read level '%s' -- using empty level", filename);
6101 while (!checkEndOfFile(file))
6103 // level successfully read, but next level may follow here
6104 if (!got_valid_playfield_line && reading_playfield)
6106 // read playfield from single level file -- skip remaining file
6107 if (!level_file_info->packed)
6110 if (file_level_nr >= num_levels_to_skip)
6115 last_comment[0] = '\0';
6116 level_name[0] = '\0';
6118 reading_playfield = FALSE;
6121 got_valid_playfield_line = FALSE;
6123 // read next line of input file
6124 if (!getStringFromFile(file, line, MAX_LINE_LEN))
6127 // check if line was completely read and is terminated by line break
6128 if (strlen(line) > 0 && line[strlen(line) - 1] == '\n')
6131 // cut trailing line break (this can be newline and/or carriage return)
6132 for (line_ptr = &line[strlen(line)]; line_ptr >= line; line_ptr--)
6133 if ((*line_ptr == '\n' || *line_ptr == '\r') && *(line_ptr + 1) == '\0')
6136 // copy raw input line for later use (mainly debugging output)
6137 strcpy(line_raw, line);
6139 if (read_continued_line)
6141 // append new line to existing line, if there is enough space
6142 if (strlen(previous_line) + strlen(line_ptr) < MAX_LINE_LEN)
6143 strcat(previous_line, line_ptr);
6145 strcpy(line, previous_line); // copy storage buffer to line
6147 read_continued_line = FALSE;
6150 // if the last character is '\', continue at next line
6151 if (strlen(line) > 0 && line[strlen(line) - 1] == '\\')
6153 line[strlen(line) - 1] = '\0'; // cut off trailing backslash
6154 strcpy(previous_line, line); // copy line to storage buffer
6156 read_continued_line = TRUE;
6162 if (line[0] == '\0')
6165 // extract comment text from comment line
6168 for (line_ptr = line; *line_ptr; line_ptr++)
6169 if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != ';')
6172 strcpy(last_comment, line_ptr);
6177 // extract level title text from line containing level title
6178 if (line[0] == '\'')
6180 strcpy(level_name, &line[1]);
6182 if (strlen(level_name) > 0 && level_name[strlen(level_name) - 1] == '\'')
6183 level_name[strlen(level_name) - 1] = '\0';
6188 // skip lines containing only spaces (or empty lines)
6189 for (line_ptr = line; *line_ptr; line_ptr++)
6190 if (*line_ptr != ' ')
6192 if (*line_ptr == '\0')
6195 // at this point, we have found a line containing part of a playfield
6197 got_valid_playfield_line = TRUE;
6199 if (!reading_playfield)
6201 reading_playfield = TRUE;
6202 invalid_playfield_char = FALSE;
6204 for (x = 0; x < MAX_LEV_FIELDX; x++)
6205 for (y = 0; y < MAX_LEV_FIELDY; y++)
6206 level->field[x][y] = getMappedElement_SB(' ', load_xsb_to_ces);
6211 // start with topmost tile row
6215 // skip playfield line if larger row than allowed
6216 if (y >= MAX_LEV_FIELDY)
6219 // start with leftmost tile column
6222 // read playfield elements from line
6223 for (line_ptr = line; *line_ptr; line_ptr++)
6225 int mapped_sb_element = getMappedElement_SB(*line_ptr, load_xsb_to_ces);
6227 // stop parsing playfield line if larger column than allowed
6228 if (x >= MAX_LEV_FIELDX)
6231 if (mapped_sb_element == EL_UNDEFINED)
6233 invalid_playfield_char = TRUE;
6238 level->field[x][y] = mapped_sb_element;
6240 // continue with next tile column
6243 level->fieldx = MAX(x, level->fieldx);
6246 if (invalid_playfield_char)
6248 // if first playfield line, treat invalid lines as comment lines
6250 reading_playfield = FALSE;
6255 // continue with next tile row
6263 level->fieldx = MIN(MAX(MIN_LEV_FIELDX, level->fieldx), MAX_LEV_FIELDX);
6264 level->fieldy = MIN(MAX(MIN_LEV_FIELDY, level->fieldy), MAX_LEV_FIELDY);
6266 if (!reading_playfield)
6268 level->no_valid_file = TRUE;
6270 Warn("cannot read level '%s' -- using empty level", filename);
6275 if (*level_name != '\0')
6277 strncpy(level->name, level_name, MAX_LEVEL_NAME_LEN);
6278 level->name[MAX_LEVEL_NAME_LEN] = '\0';
6280 else if (*last_comment != '\0')
6282 strncpy(level->name, last_comment, MAX_LEVEL_NAME_LEN);
6283 level->name[MAX_LEVEL_NAME_LEN] = '\0';
6287 sprintf(level->name, "--> Level %d <--", level_file_info->nr);
6290 // set all empty fields beyond the border walls to invisible steel wall
6291 for (y = 0; y < level->fieldy; y++) for (x = 0; x < level->fieldx; x++)
6293 if ((x == 0 || x == level->fieldx - 1 ||
6294 y == 0 || y == level->fieldy - 1) &&
6295 level->field[x][y] == getMappedElement_SB(' ', load_xsb_to_ces))
6296 FloodFillLevel(x, y, getMappedElement_SB('_', load_xsb_to_ces),
6297 level->field, level->fieldx, level->fieldy);
6300 // set special level settings for Sokoban levels
6301 SetLevelSettings_SB(level);
6303 if (load_xsb_to_ces)
6305 // special global settings can now be set in level template
6306 level->use_custom_template = TRUE;
6311 // -------------------------------------------------------------------------
6312 // functions for handling native levels
6313 // -------------------------------------------------------------------------
6315 static void LoadLevelFromFileInfo_EM(struct LevelInfo *level,
6316 struct LevelFileInfo *level_file_info,
6317 boolean level_info_only)
6319 if (!LoadNativeLevel_EM(level_file_info->filename, level_info_only))
6320 level->no_valid_file = TRUE;
6323 static void LoadLevelFromFileInfo_SP(struct LevelInfo *level,
6324 struct LevelFileInfo *level_file_info,
6325 boolean level_info_only)
6329 // determine position of requested level inside level package
6330 if (level_file_info->packed)
6331 pos = level_file_info->nr - leveldir_current->first_level;
6333 if (!LoadNativeLevel_SP(level_file_info->filename, pos, level_info_only))
6334 level->no_valid_file = TRUE;
6337 static void LoadLevelFromFileInfo_MM(struct LevelInfo *level,
6338 struct LevelFileInfo *level_file_info,
6339 boolean level_info_only)
6341 if (!LoadNativeLevel_MM(level_file_info->filename, level_info_only))
6342 level->no_valid_file = TRUE;
6345 void CopyNativeLevel_RND_to_Native(struct LevelInfo *level)
6347 if (level->game_engine_type == GAME_ENGINE_TYPE_BD)
6348 CopyNativeLevel_RND_to_BD(level);
6349 else if (level->game_engine_type == GAME_ENGINE_TYPE_EM)
6350 CopyNativeLevel_RND_to_EM(level);
6351 else if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
6352 CopyNativeLevel_RND_to_SP(level);
6353 else if (level->game_engine_type == GAME_ENGINE_TYPE_MM)
6354 CopyNativeLevel_RND_to_MM(level);
6357 void CopyNativeLevel_Native_to_RND(struct LevelInfo *level)
6359 if (level->game_engine_type == GAME_ENGINE_TYPE_BD)
6360 CopyNativeLevel_BD_to_RND(level);
6361 else if (level->game_engine_type == GAME_ENGINE_TYPE_EM)
6362 CopyNativeLevel_EM_to_RND(level);
6363 else if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
6364 CopyNativeLevel_SP_to_RND(level);
6365 else if (level->game_engine_type == GAME_ENGINE_TYPE_MM)
6366 CopyNativeLevel_MM_to_RND(level);
6369 void SaveNativeLevel(struct LevelInfo *level)
6371 if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
6373 char *basename = getSingleLevelBasenameExt(level->file_info.nr, "sp");
6374 char *filename = getLevelFilenameFromBasename(basename);
6376 CopyNativeLevel_RND_to_SP(level);
6377 CopyNativeTape_RND_to_SP(level);
6379 SaveNativeLevel_SP(filename);
6384 // ----------------------------------------------------------------------------
6385 // functions for loading generic level
6386 // ----------------------------------------------------------------------------
6388 static void LoadLevelFromFileInfo(struct LevelInfo *level,
6389 struct LevelFileInfo *level_file_info,
6390 boolean level_info_only)
6392 // always start with reliable default values
6393 setLevelInfoToDefaults(level, level_info_only, TRUE);
6395 switch (level_file_info->type)
6397 case LEVEL_FILE_TYPE_RND:
6398 LoadLevelFromFileInfo_RND(level, level_file_info, level_info_only);
6401 case LEVEL_FILE_TYPE_EM:
6402 LoadLevelFromFileInfo_EM(level, level_file_info, level_info_only);
6403 level->game_engine_type = GAME_ENGINE_TYPE_EM;
6406 case LEVEL_FILE_TYPE_SP:
6407 LoadLevelFromFileInfo_SP(level, level_file_info, level_info_only);
6408 level->game_engine_type = GAME_ENGINE_TYPE_SP;
6411 case LEVEL_FILE_TYPE_MM:
6412 LoadLevelFromFileInfo_MM(level, level_file_info, level_info_only);
6413 level->game_engine_type = GAME_ENGINE_TYPE_MM;
6416 case LEVEL_FILE_TYPE_DC:
6417 LoadLevelFromFileInfo_DC(level, level_file_info, level_info_only);
6420 case LEVEL_FILE_TYPE_SB:
6421 LoadLevelFromFileInfo_SB(level, level_file_info, level_info_only);
6425 LoadLevelFromFileInfo_RND(level, level_file_info, level_info_only);
6429 // if level file is invalid, restore level structure to default values
6430 if (level->no_valid_file)
6431 setLevelInfoToDefaults(level, level_info_only, FALSE);
6433 if (level->game_engine_type == GAME_ENGINE_TYPE_UNKNOWN)
6434 level->game_engine_type = GAME_ENGINE_TYPE_RND;
6436 if (level_file_info->type != LEVEL_FILE_TYPE_RND)
6437 CopyNativeLevel_Native_to_RND(level);
6440 void LoadLevelFromFilename(struct LevelInfo *level, char *filename)
6442 static struct LevelFileInfo level_file_info;
6444 // always start with reliable default values
6445 setFileInfoToDefaults(&level_file_info);
6447 level_file_info.nr = 0; // unknown level number
6448 level_file_info.type = LEVEL_FILE_TYPE_RND; // no others supported yet
6450 setString(&level_file_info.filename, filename);
6452 LoadLevelFromFileInfo(level, &level_file_info, FALSE);
6455 static void LoadLevel_InitVersion(struct LevelInfo *level)
6459 if (leveldir_current == NULL) // only when dumping level
6462 // all engine modifications also valid for levels which use latest engine
6463 if (level->game_version < VERSION_IDENT(3,2,0,5))
6465 // time bonus score was given for 10 s instead of 1 s before 3.2.0-5
6466 level->time_score_base = 10;
6469 if (leveldir_current->latest_engine)
6471 // ---------- use latest game engine --------------------------------------
6473 /* For all levels which are forced to use the latest game engine version
6474 (normally all but user contributed, private and undefined levels), set
6475 the game engine version to the actual version; this allows for actual
6476 corrections in the game engine to take effect for existing, converted
6477 levels (from "classic" or other existing games) to make the emulation
6478 of the corresponding game more accurate, while (hopefully) not breaking
6479 existing levels created from other players. */
6481 level->game_version = GAME_VERSION_ACTUAL;
6483 /* Set special EM style gems behaviour: EM style gems slip down from
6484 normal, steel and growing wall. As this is a more fundamental change,
6485 it seems better to set the default behaviour to "off" (as it is more
6486 natural) and make it configurable in the level editor (as a property
6487 of gem style elements). Already existing converted levels (neither
6488 private nor contributed levels) are changed to the new behaviour. */
6490 if (level->file_version < FILE_VERSION_2_0)
6491 level->em_slippery_gems = TRUE;
6496 // ---------- use game engine the level was created with --------------------
6498 /* For all levels which are not forced to use the latest game engine
6499 version (normally user contributed, private and undefined levels),
6500 use the version of the game engine the levels were created for.
6502 Since 2.0.1, the game engine version is now directly stored
6503 in the level file (chunk "VERS"), so there is no need anymore
6504 to set the game version from the file version (except for old,
6505 pre-2.0 levels, where the game version is still taken from the
6506 file format version used to store the level -- see above). */
6508 // player was faster than enemies in 1.0.0 and before
6509 if (level->file_version == FILE_VERSION_1_0)
6510 for (i = 0; i < MAX_PLAYERS; i++)
6511 level->initial_player_stepsize[i] = STEPSIZE_FAST;
6513 // default behaviour for EM style gems was "slippery" only in 2.0.1
6514 if (level->game_version == VERSION_IDENT(2,0,1,0))
6515 level->em_slippery_gems = TRUE;
6517 // springs could be pushed over pits before (pre-release version) 2.2.0
6518 if (level->game_version < VERSION_IDENT(2,2,0,0))
6519 level->use_spring_bug = TRUE;
6521 if (level->game_version < VERSION_IDENT(3,2,0,5))
6523 // time orb caused limited time in endless time levels before 3.2.0-5
6524 level->use_time_orb_bug = TRUE;
6526 // default behaviour for snapping was "no snap delay" before 3.2.0-5
6527 level->block_snap_field = FALSE;
6529 // extra time score was same value as time left score before 3.2.0-5
6530 level->extra_time_score = level->score[SC_TIME_BONUS];
6533 if (level->game_version < VERSION_IDENT(3,2,0,7))
6535 // default behaviour for snapping was "not continuous" before 3.2.0-7
6536 level->continuous_snapping = FALSE;
6539 // only few elements were able to actively move into acid before 3.1.0
6540 // trigger settings did not exist before 3.1.0; set to default "any"
6541 if (level->game_version < VERSION_IDENT(3,1,0,0))
6543 // correct "can move into acid" settings (all zero in old levels)
6545 level->can_move_into_acid_bits = 0; // nothing can move into acid
6546 level->dont_collide_with_bits = 0; // nothing is deadly when colliding
6548 setMoveIntoAcidProperty(level, EL_ROBOT, TRUE);
6549 setMoveIntoAcidProperty(level, EL_SATELLITE, TRUE);
6550 setMoveIntoAcidProperty(level, EL_PENGUIN, TRUE);
6551 setMoveIntoAcidProperty(level, EL_BALLOON, TRUE);
6553 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6554 SET_PROPERTY(EL_CUSTOM_START + i, EP_CAN_MOVE_INTO_ACID, TRUE);
6556 // correct trigger settings (stored as zero == "none" in old levels)
6558 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6560 int element = EL_CUSTOM_START + i;
6561 struct ElementInfo *ei = &element_info[element];
6563 for (j = 0; j < ei->num_change_pages; j++)
6565 struct ElementChangeInfo *change = &ei->change_page[j];
6567 change->trigger_player = CH_PLAYER_ANY;
6568 change->trigger_page = CH_PAGE_ANY;
6573 // try to detect and fix "Snake Bite" levels, which are broken with 3.2.0
6575 int element = EL_CUSTOM_256;
6576 struct ElementInfo *ei = &element_info[element];
6577 struct ElementChangeInfo *change = &ei->change_page[0];
6579 /* This is needed to fix a problem that was caused by a bugfix in function
6580 game.c/CreateFieldExt() introduced with 3.2.0 that corrects the behaviour
6581 when a custom element changes to EL_SOKOBAN_FIELD_PLAYER (before, it did
6582 not replace walkable elements, but instead just placed the player on it,
6583 without placing the Sokoban field under the player). Unfortunately, this
6584 breaks "Snake Bite" style levels when the snake is halfway through a door
6585 that just closes (the snake head is still alive and can be moved in this
6586 case). This can be fixed by replacing the EL_SOKOBAN_FIELD_PLAYER by the
6587 player (without Sokoban element) which then gets killed as designed). */
6589 if ((strncmp(leveldir_current->identifier, "snake_bite", 10) == 0 ||
6590 strncmp(ei->description, "pause b4 death", 14) == 0) &&
6591 change->target_element == EL_SOKOBAN_FIELD_PLAYER)
6592 change->target_element = EL_PLAYER_1;
6595 // try to detect and fix "Zelda" style levels, which are broken with 3.2.5
6596 if (level->game_version < VERSION_IDENT(3,2,5,0))
6598 /* This is needed to fix a problem that was caused by a bugfix in function
6599 game.c/CheckTriggeredElementChangeExt() introduced with 3.2.5 that
6600 corrects the behaviour when a custom element changes to another custom
6601 element with a higher element number that has change actions defined.
6602 Normally, only one change per frame is allowed for custom elements.
6603 Therefore, it is checked if a custom element already changed in the
6604 current frame; if it did, subsequent changes are suppressed.
6605 Unfortunately, this is only checked for element changes, but not for
6606 change actions, which are still executed. As the function above loops
6607 through all custom elements from lower to higher, an element change
6608 resulting in a lower CE number won't be checked again, while a target
6609 element with a higher number will also be checked, and potential change
6610 actions will get executed for this CE, too (which is wrong), while
6611 further changes are ignored (which is correct). As this bugfix breaks
6612 Zelda II (and introduces graphical bugs to Zelda I, and also breaks a
6613 few other levels like Alan Bond's "FMV"), allow the previous, incorrect
6614 behaviour for existing levels and tapes that make use of this bug */
6616 level->use_action_after_change_bug = TRUE;
6619 // not centering level after relocating player was default only in 3.2.3
6620 if (level->game_version == VERSION_IDENT(3,2,3,0)) // (no pre-releases)
6621 level->shifted_relocation = TRUE;
6623 // EM style elements always chain-exploded in R'n'D engine before 3.2.6
6624 if (level->game_version < VERSION_IDENT(3,2,6,0))
6625 level->em_explodes_by_fire = TRUE;
6627 // levels were solved by the first player entering an exit up to 4.1.0.0
6628 if (level->game_version <= VERSION_IDENT(4,1,0,0))
6629 level->solved_by_one_player = TRUE;
6631 // game logic of "game of life" and "biomaze" was buggy before 4.1.1.1
6632 if (level->game_version < VERSION_IDENT(4,1,1,1))
6633 level->use_life_bugs = TRUE;
6635 // only Sokoban fields (but not objects) had to be solved before 4.1.1.1
6636 if (level->game_version < VERSION_IDENT(4,1,1,1))
6637 level->sb_objects_needed = FALSE;
6639 // CE actions were triggered by unfinished digging/collecting up to 4.2.2.0
6640 if (level->game_version <= VERSION_IDENT(4,2,2,0))
6641 level->finish_dig_collect = FALSE;
6643 // CE changing to player was kept under the player if walkable up to 4.2.3.1
6644 if (level->game_version <= VERSION_IDENT(4,2,3,1))
6645 level->keep_walkable_ce = TRUE;
6648 static void LoadLevel_InitSettings_SB(struct LevelInfo *level)
6650 boolean is_sokoban_level = TRUE; // unless non-Sokoban elements found
6653 // check if this level is (not) a Sokoban level
6654 for (y = 0; y < level->fieldy; y++)
6655 for (x = 0; x < level->fieldx; x++)
6656 if (!IS_SB_ELEMENT(Tile[x][y]))
6657 is_sokoban_level = FALSE;
6659 if (is_sokoban_level)
6661 // set special level settings for Sokoban levels
6662 SetLevelSettings_SB(level);
6666 static void LoadLevel_InitSettings(struct LevelInfo *level)
6668 // adjust level settings for (non-native) Sokoban-style levels
6669 LoadLevel_InitSettings_SB(level);
6671 // rename levels with title "nameless level" or if renaming is forced
6672 if (leveldir_current->empty_level_name != NULL &&
6673 (strEqual(level->name, NAMELESS_LEVEL_NAME) ||
6674 leveldir_current->force_level_name))
6675 snprintf(level->name, MAX_LEVEL_NAME_LEN + 1,
6676 leveldir_current->empty_level_name, level_nr);
6679 static void LoadLevel_InitStandardElements(struct LevelInfo *level)
6683 // map elements that have changed in newer versions
6684 level->amoeba_content = getMappedElementByVersion(level->amoeba_content,
6685 level->game_version);
6686 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
6687 for (x = 0; x < 3; x++)
6688 for (y = 0; y < 3; y++)
6689 level->yamyam_content[i].e[x][y] =
6690 getMappedElementByVersion(level->yamyam_content[i].e[x][y],
6691 level->game_version);
6695 static void LoadLevel_InitCustomElements(struct LevelInfo *level)
6699 // map custom element change events that have changed in newer versions
6700 // (these following values were accidentally changed in version 3.0.1)
6701 // (this seems to be needed only for 'ab_levelset3' and 'ab_levelset4')
6702 if (level->game_version <= VERSION_IDENT(3,0,0,0))
6704 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6706 int element = EL_CUSTOM_START + i;
6708 // order of checking and copying events to be mapped is important
6709 // (do not change the start and end value -- they are constant)
6710 for (j = CE_BY_OTHER_ACTION; j >= CE_VALUE_GETS_ZERO; j--)
6712 if (HAS_CHANGE_EVENT(element, j - 2))
6714 SET_CHANGE_EVENT(element, j - 2, FALSE);
6715 SET_CHANGE_EVENT(element, j, TRUE);
6719 // order of checking and copying events to be mapped is important
6720 // (do not change the start and end value -- they are constant)
6721 for (j = CE_PLAYER_COLLECTS_X; j >= CE_HITTING_SOMETHING; j--)
6723 if (HAS_CHANGE_EVENT(element, j - 1))
6725 SET_CHANGE_EVENT(element, j - 1, FALSE);
6726 SET_CHANGE_EVENT(element, j, TRUE);
6732 // initialize "can_change" field for old levels with only one change page
6733 if (level->game_version <= VERSION_IDENT(3,0,2,0))
6735 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6737 int element = EL_CUSTOM_START + i;
6739 if (CAN_CHANGE(element))
6740 element_info[element].change->can_change = TRUE;
6744 // correct custom element values (for old levels without these options)
6745 if (level->game_version < VERSION_IDENT(3,1,1,0))
6747 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6749 int element = EL_CUSTOM_START + i;
6750 struct ElementInfo *ei = &element_info[element];
6752 if (ei->access_direction == MV_NO_DIRECTION)
6753 ei->access_direction = MV_ALL_DIRECTIONS;
6757 // correct custom element values (fix invalid values for all versions)
6760 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6762 int element = EL_CUSTOM_START + i;
6763 struct ElementInfo *ei = &element_info[element];
6765 for (j = 0; j < ei->num_change_pages; j++)
6767 struct ElementChangeInfo *change = &ei->change_page[j];
6769 if (change->trigger_player == CH_PLAYER_NONE)
6770 change->trigger_player = CH_PLAYER_ANY;
6772 if (change->trigger_side == CH_SIDE_NONE)
6773 change->trigger_side = CH_SIDE_ANY;
6778 // initialize "can_explode" field for old levels which did not store this
6779 // !!! CHECK THIS -- "<= 3,1,0,0" IS PROBABLY WRONG !!!
6780 if (level->game_version <= VERSION_IDENT(3,1,0,0))
6782 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6784 int element = EL_CUSTOM_START + i;
6786 if (EXPLODES_1X1_OLD(element))
6787 element_info[element].explosion_type = EXPLODES_1X1;
6789 SET_PROPERTY(element, EP_CAN_EXPLODE, (EXPLODES_BY_FIRE(element) ||
6790 EXPLODES_SMASHED(element) ||
6791 EXPLODES_IMPACT(element)));
6795 // correct previously hard-coded move delay values for maze runner style
6796 if (level->game_version < VERSION_IDENT(3,1,1,0))
6798 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6800 int element = EL_CUSTOM_START + i;
6802 if (element_info[element].move_pattern & MV_MAZE_RUNNER_STYLE)
6804 // previously hard-coded and therefore ignored
6805 element_info[element].move_delay_fixed = 9;
6806 element_info[element].move_delay_random = 0;
6811 // set some other uninitialized values of custom elements in older levels
6812 if (level->game_version < VERSION_IDENT(3,1,0,0))
6814 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6816 int element = EL_CUSTOM_START + i;
6818 element_info[element].access_direction = MV_ALL_DIRECTIONS;
6820 element_info[element].explosion_delay = 17;
6821 element_info[element].ignition_delay = 8;
6825 // set mouse click change events to work for left/middle/right mouse button
6826 if (level->game_version < VERSION_IDENT(4,2,3,0))
6828 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6830 int element = EL_CUSTOM_START + i;
6831 struct ElementInfo *ei = &element_info[element];
6833 for (j = 0; j < ei->num_change_pages; j++)
6835 struct ElementChangeInfo *change = &ei->change_page[j];
6837 if (change->has_event[CE_CLICKED_BY_MOUSE] ||
6838 change->has_event[CE_PRESSED_BY_MOUSE] ||
6839 change->has_event[CE_MOUSE_CLICKED_ON_X] ||
6840 change->has_event[CE_MOUSE_PRESSED_ON_X])
6841 change->trigger_side = CH_SIDE_ANY;
6847 static void LoadLevel_InitElements(struct LevelInfo *level)
6849 LoadLevel_InitStandardElements(level);
6851 if (level->file_has_custom_elements)
6852 LoadLevel_InitCustomElements(level);
6854 // initialize element properties for level editor etc.
6855 InitElementPropertiesEngine(level->game_version);
6856 InitElementPropertiesGfxElement();
6859 static void LoadLevel_InitPlayfield(struct LevelInfo *level)
6863 // map elements that have changed in newer versions
6864 for (y = 0; y < level->fieldy; y++)
6865 for (x = 0; x < level->fieldx; x++)
6866 level->field[x][y] = getMappedElementByVersion(level->field[x][y],
6867 level->game_version);
6869 // clear unused playfield data (nicer if level gets resized in editor)
6870 for (x = 0; x < MAX_LEV_FIELDX; x++)
6871 for (y = 0; y < MAX_LEV_FIELDY; y++)
6872 if (x >= level->fieldx || y >= level->fieldy)
6873 level->field[x][y] = EL_EMPTY;
6875 // copy elements to runtime playfield array
6876 for (x = 0; x < MAX_LEV_FIELDX; x++)
6877 for (y = 0; y < MAX_LEV_FIELDY; y++)
6878 Tile[x][y] = level->field[x][y];
6880 // initialize level size variables for faster access
6881 lev_fieldx = level->fieldx;
6882 lev_fieldy = level->fieldy;
6884 // determine border element for this level
6885 if (level->file_info.type == LEVEL_FILE_TYPE_DC)
6886 BorderElement = EL_EMPTY; // (in editor, SetBorderElement() is used)
6891 static void LoadLevel_InitNativeEngines(struct LevelInfo *level)
6893 struct LevelFileInfo *level_file_info = &level->file_info;
6895 if (level_file_info->type == LEVEL_FILE_TYPE_RND)
6896 CopyNativeLevel_RND_to_Native(level);
6899 static void LoadLevelTemplate_LoadAndInit(void)
6901 LoadLevelFromFileInfo(&level_template, &level_template.file_info, FALSE);
6903 LoadLevel_InitVersion(&level_template);
6904 LoadLevel_InitElements(&level_template);
6905 LoadLevel_InitSettings(&level_template);
6907 ActivateLevelTemplate();
6910 void LoadLevelTemplate(int nr)
6912 if (!fileExists(getGlobalLevelTemplateFilename()))
6914 Warn("no level template found for this level");
6919 setLevelFileInfo(&level_template.file_info, nr);
6921 LoadLevelTemplate_LoadAndInit();
6924 static void LoadNetworkLevelTemplate(struct NetworkLevelInfo *network_level)
6926 copyLevelFileInfo(&network_level->tmpl_info, &level_template.file_info);
6928 LoadLevelTemplate_LoadAndInit();
6931 static void LoadLevel_LoadAndInit(struct NetworkLevelInfo *network_level)
6933 LoadLevelFromFileInfo(&level, &level.file_info, FALSE);
6935 if (level.use_custom_template)
6937 if (network_level != NULL)
6938 LoadNetworkLevelTemplate(network_level);
6940 LoadLevelTemplate(-1);
6943 LoadLevel_InitVersion(&level);
6944 LoadLevel_InitElements(&level);
6945 LoadLevel_InitPlayfield(&level);
6946 LoadLevel_InitSettings(&level);
6948 LoadLevel_InitNativeEngines(&level);
6951 void LoadLevel(int nr)
6953 SetLevelSetInfo(leveldir_current->identifier, nr);
6955 setLevelFileInfo(&level.file_info, nr);
6957 LoadLevel_LoadAndInit(NULL);
6960 void LoadLevelInfoOnly(int nr)
6962 setLevelFileInfo(&level.file_info, nr);
6964 LoadLevelFromFileInfo(&level, &level.file_info, TRUE);
6967 void LoadNetworkLevel(struct NetworkLevelInfo *network_level)
6969 SetLevelSetInfo(network_level->leveldir_identifier,
6970 network_level->file_info.nr);
6972 copyLevelFileInfo(&network_level->file_info, &level.file_info);
6974 LoadLevel_LoadAndInit(network_level);
6977 static int SaveLevel_VERS(FILE *file, struct LevelInfo *level)
6981 chunk_size += putFileVersion(file, level->file_version);
6982 chunk_size += putFileVersion(file, level->game_version);
6987 static int SaveLevel_DATE(FILE *file, struct LevelInfo *level)
6991 chunk_size += putFile16BitBE(file, level->creation_date.year);
6992 chunk_size += putFile8Bit(file, level->creation_date.month);
6993 chunk_size += putFile8Bit(file, level->creation_date.day);
6998 #if ENABLE_HISTORIC_CHUNKS
6999 static void SaveLevel_HEAD(FILE *file, struct LevelInfo *level)
7003 putFile8Bit(file, level->fieldx);
7004 putFile8Bit(file, level->fieldy);
7006 putFile16BitBE(file, level->time);
7007 putFile16BitBE(file, level->gems_needed);
7009 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
7010 putFile8Bit(file, level->name[i]);
7012 for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
7013 putFile8Bit(file, level->score[i]);
7015 for (i = 0; i < STD_ELEMENT_CONTENTS; i++)
7016 for (y = 0; y < 3; y++)
7017 for (x = 0; x < 3; x++)
7018 putFile8Bit(file, (level->encoding_16bit_yamyam ? EL_EMPTY :
7019 level->yamyam_content[i].e[x][y]));
7020 putFile8Bit(file, level->amoeba_speed);
7021 putFile8Bit(file, level->time_magic_wall);
7022 putFile8Bit(file, level->time_wheel);
7023 putFile8Bit(file, (level->encoding_16bit_amoeba ? EL_EMPTY :
7024 level->amoeba_content));
7025 putFile8Bit(file, (level->initial_player_stepsize == STEPSIZE_FAST ? 1 : 0));
7026 putFile8Bit(file, (level->initial_gravity ? 1 : 0));
7027 putFile8Bit(file, (level->encoding_16bit_field ? 1 : 0));
7028 putFile8Bit(file, (level->em_slippery_gems ? 1 : 0));
7030 putFile8Bit(file, (level->use_custom_template ? 1 : 0));
7032 putFile8Bit(file, (level->block_last_field ? 1 : 0));
7033 putFile8Bit(file, (level->sp_block_last_field ? 1 : 0));
7034 putFile32BitBE(file, level->can_move_into_acid_bits);
7035 putFile8Bit(file, level->dont_collide_with_bits);
7037 putFile8Bit(file, (level->use_spring_bug ? 1 : 0));
7038 putFile8Bit(file, (level->use_step_counter ? 1 : 0));
7040 putFile8Bit(file, (level->instant_relocation ? 1 : 0));
7041 putFile8Bit(file, (level->can_pass_to_walkable ? 1 : 0));
7042 putFile8Bit(file, (level->grow_into_diggable ? 1 : 0));
7044 putFile8Bit(file, level->game_engine_type);
7046 WriteUnusedBytesToFile(file, LEVEL_CHUNK_HEAD_UNUSED);
7050 static int SaveLevel_NAME(FILE *file, struct LevelInfo *level)
7055 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
7056 chunk_size += putFile8Bit(file, level->name[i]);
7061 static int SaveLevel_AUTH(FILE *file, struct LevelInfo *level)
7066 for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
7067 chunk_size += putFile8Bit(file, level->author[i]);
7072 #if ENABLE_HISTORIC_CHUNKS
7073 static int SaveLevel_BODY(FILE *file, struct LevelInfo *level)
7078 for (y = 0; y < level->fieldy; y++)
7079 for (x = 0; x < level->fieldx; x++)
7080 if (level->encoding_16bit_field)
7081 chunk_size += putFile16BitBE(file, level->field[x][y]);
7083 chunk_size += putFile8Bit(file, level->field[x][y]);
7089 static int SaveLevel_BODY(FILE *file, struct LevelInfo *level)
7094 for (y = 0; y < level->fieldy; y++)
7095 for (x = 0; x < level->fieldx; x++)
7096 chunk_size += putFile16BitBE(file, level->field[x][y]);
7101 #if ENABLE_HISTORIC_CHUNKS
7102 static void SaveLevel_CONT(FILE *file, struct LevelInfo *level)
7106 putFile8Bit(file, EL_YAMYAM);
7107 putFile8Bit(file, level->num_yamyam_contents);
7108 putFile8Bit(file, 0);
7109 putFile8Bit(file, 0);
7111 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
7112 for (y = 0; y < 3; y++)
7113 for (x = 0; x < 3; x++)
7114 if (level->encoding_16bit_field)
7115 putFile16BitBE(file, level->yamyam_content[i].e[x][y]);
7117 putFile8Bit(file, level->yamyam_content[i].e[x][y]);
7121 #if ENABLE_HISTORIC_CHUNKS
7122 static void SaveLevel_CNT2(FILE *file, struct LevelInfo *level, int element)
7125 int num_contents, content_xsize, content_ysize;
7126 int content_array[MAX_ELEMENT_CONTENTS][3][3];
7128 if (element == EL_YAMYAM)
7130 num_contents = level->num_yamyam_contents;
7134 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
7135 for (y = 0; y < 3; y++)
7136 for (x = 0; x < 3; x++)
7137 content_array[i][x][y] = level->yamyam_content[i].e[x][y];
7139 else if (element == EL_BD_AMOEBA)
7145 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
7146 for (y = 0; y < 3; y++)
7147 for (x = 0; x < 3; x++)
7148 content_array[i][x][y] = EL_EMPTY;
7149 content_array[0][0][0] = level->amoeba_content;
7153 // chunk header already written -- write empty chunk data
7154 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_SIZE);
7156 Warn("cannot save content for element '%d'", element);
7161 putFile16BitBE(file, element);
7162 putFile8Bit(file, num_contents);
7163 putFile8Bit(file, content_xsize);
7164 putFile8Bit(file, content_ysize);
7166 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_UNUSED);
7168 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
7169 for (y = 0; y < 3; y++)
7170 for (x = 0; x < 3; x++)
7171 putFile16BitBE(file, content_array[i][x][y]);
7175 #if ENABLE_HISTORIC_CHUNKS
7176 static int SaveLevel_CNT3(FILE *file, struct LevelInfo *level, int element)
7178 int envelope_nr = element - EL_ENVELOPE_1;
7179 int envelope_len = strlen(level->envelope_text[envelope_nr]) + 1;
7183 chunk_size += putFile16BitBE(file, element);
7184 chunk_size += putFile16BitBE(file, envelope_len);
7185 chunk_size += putFile8Bit(file, level->envelope_xsize[envelope_nr]);
7186 chunk_size += putFile8Bit(file, level->envelope_ysize[envelope_nr]);
7188 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT3_UNUSED);
7189 chunk_size += LEVEL_CHUNK_CNT3_UNUSED;
7191 for (i = 0; i < envelope_len; i++)
7192 chunk_size += putFile8Bit(file, level->envelope_text[envelope_nr][i]);
7198 #if ENABLE_HISTORIC_CHUNKS
7199 static void SaveLevel_CUS1(FILE *file, struct LevelInfo *level,
7200 int num_changed_custom_elements)
7204 putFile16BitBE(file, num_changed_custom_elements);
7206 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7208 int element = EL_CUSTOM_START + i;
7210 struct ElementInfo *ei = &element_info[element];
7212 if (ei->properties[EP_BITFIELD_BASE_NR] != EP_BITMASK_DEFAULT)
7214 if (check < num_changed_custom_elements)
7216 putFile16BitBE(file, element);
7217 putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
7224 if (check != num_changed_custom_elements) // should not happen
7225 Warn("inconsistent number of custom element properties");
7229 #if ENABLE_HISTORIC_CHUNKS
7230 static void SaveLevel_CUS2(FILE *file, struct LevelInfo *level,
7231 int num_changed_custom_elements)
7235 putFile16BitBE(file, num_changed_custom_elements);
7237 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7239 int element = EL_CUSTOM_START + i;
7241 if (element_info[element].change->target_element != EL_EMPTY_SPACE)
7243 if (check < num_changed_custom_elements)
7245 putFile16BitBE(file, element);
7246 putFile16BitBE(file, element_info[element].change->target_element);
7253 if (check != num_changed_custom_elements) // should not happen
7254 Warn("inconsistent number of custom target elements");
7258 #if ENABLE_HISTORIC_CHUNKS
7259 static void SaveLevel_CUS3(FILE *file, struct LevelInfo *level,
7260 int num_changed_custom_elements)
7262 int i, j, x, y, check = 0;
7264 putFile16BitBE(file, num_changed_custom_elements);
7266 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7268 int element = EL_CUSTOM_START + i;
7269 struct ElementInfo *ei = &element_info[element];
7271 if (ei->modified_settings)
7273 if (check < num_changed_custom_elements)
7275 putFile16BitBE(file, element);
7277 for (j = 0; j < MAX_ELEMENT_NAME_LEN; j++)
7278 putFile8Bit(file, ei->description[j]);
7280 putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
7282 // some free bytes for future properties and padding
7283 WriteUnusedBytesToFile(file, 7);
7285 putFile8Bit(file, ei->use_gfx_element);
7286 putFile16BitBE(file, ei->gfx_element_initial);
7288 putFile8Bit(file, ei->collect_score_initial);
7289 putFile8Bit(file, ei->collect_count_initial);
7291 putFile16BitBE(file, ei->push_delay_fixed);
7292 putFile16BitBE(file, ei->push_delay_random);
7293 putFile16BitBE(file, ei->move_delay_fixed);
7294 putFile16BitBE(file, ei->move_delay_random);
7296 putFile16BitBE(file, ei->move_pattern);
7297 putFile8Bit(file, ei->move_direction_initial);
7298 putFile8Bit(file, ei->move_stepsize);
7300 for (y = 0; y < 3; y++)
7301 for (x = 0; x < 3; x++)
7302 putFile16BitBE(file, ei->content.e[x][y]);
7304 putFile32BitBE(file, ei->change->events);
7306 putFile16BitBE(file, ei->change->target_element);
7308 putFile16BitBE(file, ei->change->delay_fixed);
7309 putFile16BitBE(file, ei->change->delay_random);
7310 putFile16BitBE(file, ei->change->delay_frames);
7312 putFile16BitBE(file, ei->change->initial_trigger_element);
7314 putFile8Bit(file, ei->change->explode);
7315 putFile8Bit(file, ei->change->use_target_content);
7316 putFile8Bit(file, ei->change->only_if_complete);
7317 putFile8Bit(file, ei->change->use_random_replace);
7319 putFile8Bit(file, ei->change->random_percentage);
7320 putFile8Bit(file, ei->change->replace_when);
7322 for (y = 0; y < 3; y++)
7323 for (x = 0; x < 3; x++)
7324 putFile16BitBE(file, ei->change->content.e[x][y]);
7326 putFile8Bit(file, ei->slippery_type);
7328 // some free bytes for future properties and padding
7329 WriteUnusedBytesToFile(file, LEVEL_CPART_CUS3_UNUSED);
7336 if (check != num_changed_custom_elements) // should not happen
7337 Warn("inconsistent number of custom element properties");
7341 #if ENABLE_HISTORIC_CHUNKS
7342 static void SaveLevel_CUS4(FILE *file, struct LevelInfo *level, int element)
7344 struct ElementInfo *ei = &element_info[element];
7347 // ---------- custom element base property values (96 bytes) ----------------
7349 putFile16BitBE(file, element);
7351 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
7352 putFile8Bit(file, ei->description[i]);
7354 putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
7356 WriteUnusedBytesToFile(file, 4); // reserved for more base properties
7358 putFile8Bit(file, ei->num_change_pages);
7360 putFile16BitBE(file, ei->ce_value_fixed_initial);
7361 putFile16BitBE(file, ei->ce_value_random_initial);
7362 putFile8Bit(file, ei->use_last_ce_value);
7364 putFile8Bit(file, ei->use_gfx_element);
7365 putFile16BitBE(file, ei->gfx_element_initial);
7367 putFile8Bit(file, ei->collect_score_initial);
7368 putFile8Bit(file, ei->collect_count_initial);
7370 putFile8Bit(file, ei->drop_delay_fixed);
7371 putFile8Bit(file, ei->push_delay_fixed);
7372 putFile8Bit(file, ei->drop_delay_random);
7373 putFile8Bit(file, ei->push_delay_random);
7374 putFile16BitBE(file, ei->move_delay_fixed);
7375 putFile16BitBE(file, ei->move_delay_random);
7377 // bits 0 - 15 of "move_pattern" ...
7378 putFile16BitBE(file, ei->move_pattern & 0xffff);
7379 putFile8Bit(file, ei->move_direction_initial);
7380 putFile8Bit(file, ei->move_stepsize);
7382 putFile8Bit(file, ei->slippery_type);
7384 for (y = 0; y < 3; y++)
7385 for (x = 0; x < 3; x++)
7386 putFile16BitBE(file, ei->content.e[x][y]);
7388 putFile16BitBE(file, ei->move_enter_element);
7389 putFile16BitBE(file, ei->move_leave_element);
7390 putFile8Bit(file, ei->move_leave_type);
7392 // ... bits 16 - 31 of "move_pattern" (not nice, but downward compatible)
7393 putFile16BitBE(file, (ei->move_pattern >> 16) & 0xffff);
7395 putFile8Bit(file, ei->access_direction);
7397 putFile8Bit(file, ei->explosion_delay);
7398 putFile8Bit(file, ei->ignition_delay);
7399 putFile8Bit(file, ei->explosion_type);
7401 // some free bytes for future custom property values and padding
7402 WriteUnusedBytesToFile(file, 1);
7404 // ---------- change page property values (48 bytes) ------------------------
7406 for (i = 0; i < ei->num_change_pages; i++)
7408 struct ElementChangeInfo *change = &ei->change_page[i];
7409 unsigned int event_bits;
7411 // bits 0 - 31 of "has_event[]" ...
7413 for (j = 0; j < MIN(NUM_CHANGE_EVENTS, 32); j++)
7414 if (change->has_event[j])
7415 event_bits |= (1u << j);
7416 putFile32BitBE(file, event_bits);
7418 putFile16BitBE(file, change->target_element);
7420 putFile16BitBE(file, change->delay_fixed);
7421 putFile16BitBE(file, change->delay_random);
7422 putFile16BitBE(file, change->delay_frames);
7424 putFile16BitBE(file, change->initial_trigger_element);
7426 putFile8Bit(file, change->explode);
7427 putFile8Bit(file, change->use_target_content);
7428 putFile8Bit(file, change->only_if_complete);
7429 putFile8Bit(file, change->use_random_replace);
7431 putFile8Bit(file, change->random_percentage);
7432 putFile8Bit(file, change->replace_when);
7434 for (y = 0; y < 3; y++)
7435 for (x = 0; x < 3; x++)
7436 putFile16BitBE(file, change->target_content.e[x][y]);
7438 putFile8Bit(file, change->can_change);
7440 putFile8Bit(file, change->trigger_side);
7442 putFile8Bit(file, change->trigger_player);
7443 putFile8Bit(file, (change->trigger_page == CH_PAGE_ANY ? CH_PAGE_ANY_FILE :
7444 log_2(change->trigger_page)));
7446 putFile8Bit(file, change->has_action);
7447 putFile8Bit(file, change->action_type);
7448 putFile8Bit(file, change->action_mode);
7449 putFile16BitBE(file, change->action_arg);
7451 // ... bits 32 - 39 of "has_event[]" (not nice, but downward compatible)
7453 for (j = 32; j < NUM_CHANGE_EVENTS; j++)
7454 if (change->has_event[j])
7455 event_bits |= (1u << (j - 32));
7456 putFile8Bit(file, event_bits);
7461 #if ENABLE_HISTORIC_CHUNKS
7462 static void SaveLevel_GRP1(FILE *file, struct LevelInfo *level, int element)
7464 struct ElementInfo *ei = &element_info[element];
7465 struct ElementGroupInfo *group = ei->group;
7468 putFile16BitBE(file, element);
7470 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
7471 putFile8Bit(file, ei->description[i]);
7473 putFile8Bit(file, group->num_elements);
7475 putFile8Bit(file, ei->use_gfx_element);
7476 putFile16BitBE(file, ei->gfx_element_initial);
7478 putFile8Bit(file, group->choice_mode);
7480 // some free bytes for future values and padding
7481 WriteUnusedBytesToFile(file, 3);
7483 for (i = 0; i < MAX_ELEMENTS_IN_GROUP; i++)
7484 putFile16BitBE(file, group->element[i]);
7488 static int SaveLevel_MicroChunk(FILE *file, struct LevelFileConfigInfo *entry,
7489 boolean write_element)
7491 int save_type = entry->save_type;
7492 int data_type = entry->data_type;
7493 int conf_type = entry->conf_type;
7494 int byte_mask = conf_type & CONF_MASK_BYTES;
7495 int element = entry->element;
7496 int default_value = entry->default_value;
7498 boolean modified = FALSE;
7500 if (byte_mask != CONF_MASK_MULTI_BYTES)
7502 void *value_ptr = entry->value;
7503 int value = (data_type == TYPE_BOOLEAN ? *(boolean *)value_ptr :
7506 // check if any settings have been modified before saving them
7507 if (value != default_value)
7510 // do not save if explicitly told or if unmodified default settings
7511 if ((save_type == SAVE_CONF_NEVER) ||
7512 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
7516 num_bytes += putFile16BitBE(file, element);
7518 num_bytes += putFile8Bit(file, conf_type);
7519 num_bytes += (byte_mask == CONF_MASK_1_BYTE ? putFile8Bit (file, value) :
7520 byte_mask == CONF_MASK_2_BYTE ? putFile16BitBE(file, value) :
7521 byte_mask == CONF_MASK_4_BYTE ? putFile32BitBE(file, value) :
7524 else if (data_type == TYPE_STRING)
7526 char *default_string = entry->default_string;
7527 char *string = (char *)(entry->value);
7528 int string_length = strlen(string);
7531 // check if any settings have been modified before saving them
7532 if (!strEqual(string, default_string))
7535 // do not save if explicitly told or if unmodified default settings
7536 if ((save_type == SAVE_CONF_NEVER) ||
7537 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
7541 num_bytes += putFile16BitBE(file, element);
7543 num_bytes += putFile8Bit(file, conf_type);
7544 num_bytes += putFile16BitBE(file, string_length);
7546 for (i = 0; i < string_length; i++)
7547 num_bytes += putFile8Bit(file, string[i]);
7549 else if (data_type == TYPE_ELEMENT_LIST)
7551 int *element_array = (int *)(entry->value);
7552 int num_elements = *(int *)(entry->num_entities);
7555 // check if any settings have been modified before saving them
7556 for (i = 0; i < num_elements; i++)
7557 if (element_array[i] != 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_elements * CONF_ELEMENT_NUM_BYTES);
7571 for (i = 0; i < num_elements; i++)
7572 num_bytes += putFile16BitBE(file, element_array[i]);
7574 else if (data_type == TYPE_CONTENT_LIST)
7576 struct Content *content = (struct Content *)(entry->value);
7577 int num_contents = *(int *)(entry->num_entities);
7580 // check if any settings have been modified before saving them
7581 for (i = 0; i < num_contents; i++)
7582 for (y = 0; y < 3; y++)
7583 for (x = 0; x < 3; x++)
7584 if (content[i].e[x][y] != default_value)
7587 // do not save if explicitly told or if unmodified default settings
7588 if ((save_type == SAVE_CONF_NEVER) ||
7589 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
7593 num_bytes += putFile16BitBE(file, element);
7595 num_bytes += putFile8Bit(file, conf_type);
7596 num_bytes += putFile16BitBE(file, num_contents * CONF_CONTENT_NUM_BYTES);
7598 for (i = 0; i < num_contents; i++)
7599 for (y = 0; y < 3; y++)
7600 for (x = 0; x < 3; x++)
7601 num_bytes += putFile16BitBE(file, content[i].e[x][y]);
7607 static int SaveLevel_INFO(FILE *file, struct LevelInfo *level)
7612 li = *level; // copy level data into temporary buffer
7614 for (i = 0; chunk_config_INFO[i].data_type != -1; i++)
7615 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_INFO[i], FALSE);
7620 static int SaveLevel_ELEM(FILE *file, struct LevelInfo *level)
7625 li = *level; // copy level data into temporary buffer
7627 for (i = 0; chunk_config_ELEM[i].data_type != -1; i++)
7628 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_ELEM[i], TRUE);
7633 static int SaveLevel_NOTE(FILE *file, struct LevelInfo *level, int element)
7635 int envelope_nr = element - EL_ENVELOPE_1;
7639 chunk_size += putFile16BitBE(file, element);
7641 // copy envelope data into temporary buffer
7642 xx_envelope = level->envelope[envelope_nr];
7644 for (i = 0; chunk_config_NOTE[i].data_type != -1; i++)
7645 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_NOTE[i], FALSE);
7650 static int SaveLevel_CUSX(FILE *file, struct LevelInfo *level, int element)
7652 struct ElementInfo *ei = &element_info[element];
7656 chunk_size += putFile16BitBE(file, element);
7658 xx_ei = *ei; // copy element data into temporary buffer
7660 // set default description string for this specific element
7661 strcpy(xx_default_description, getDefaultElementDescription(ei));
7663 for (i = 0; chunk_config_CUSX_base[i].data_type != -1; i++)
7664 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_CUSX_base[i], FALSE);
7666 for (i = 0; i < ei->num_change_pages; i++)
7668 struct ElementChangeInfo *change = &ei->change_page[i];
7670 xx_current_change_page = i;
7672 xx_change = *change; // copy change data into temporary buffer
7675 setEventBitsFromEventFlags(change);
7677 for (j = 0; chunk_config_CUSX_change[j].data_type != -1; j++)
7678 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_CUSX_change[j],
7685 static int SaveLevel_GRPX(FILE *file, struct LevelInfo *level, int element)
7687 struct ElementInfo *ei = &element_info[element];
7688 struct ElementGroupInfo *group = ei->group;
7692 chunk_size += putFile16BitBE(file, element);
7694 xx_ei = *ei; // copy element data into temporary buffer
7695 xx_group = *group; // copy group data into temporary buffer
7697 // set default description string for this specific element
7698 strcpy(xx_default_description, getDefaultElementDescription(ei));
7700 for (i = 0; chunk_config_GRPX[i].data_type != -1; i++)
7701 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_GRPX[i], FALSE);
7706 static int SaveLevel_EMPX(FILE *file, struct LevelInfo *level, int element)
7708 struct ElementInfo *ei = &element_info[element];
7712 chunk_size += putFile16BitBE(file, element);
7714 xx_ei = *ei; // copy element data into temporary buffer
7716 for (i = 0; chunk_config_EMPX[i].data_type != -1; i++)
7717 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_EMPX[i], FALSE);
7722 static void SaveLevelFromFilename(struct LevelInfo *level, char *filename,
7723 boolean save_as_template)
7729 if (!(file = fopen(filename, MODE_WRITE)))
7731 Warn("cannot save level file '%s'", filename);
7736 level->file_version = FILE_VERSION_ACTUAL;
7737 level->game_version = GAME_VERSION_ACTUAL;
7739 level->creation_date = getCurrentDate();
7741 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
7742 putFileChunkBE(file, "CAVE", CHUNK_SIZE_NONE);
7744 chunk_size = SaveLevel_VERS(NULL, level);
7745 putFileChunkBE(file, "VERS", chunk_size);
7746 SaveLevel_VERS(file, level);
7748 chunk_size = SaveLevel_DATE(NULL, level);
7749 putFileChunkBE(file, "DATE", chunk_size);
7750 SaveLevel_DATE(file, level);
7752 chunk_size = SaveLevel_NAME(NULL, level);
7753 putFileChunkBE(file, "NAME", chunk_size);
7754 SaveLevel_NAME(file, level);
7756 chunk_size = SaveLevel_AUTH(NULL, level);
7757 putFileChunkBE(file, "AUTH", chunk_size);
7758 SaveLevel_AUTH(file, level);
7760 chunk_size = SaveLevel_INFO(NULL, level);
7761 putFileChunkBE(file, "INFO", chunk_size);
7762 SaveLevel_INFO(file, level);
7764 chunk_size = SaveLevel_BODY(NULL, level);
7765 putFileChunkBE(file, "BODY", chunk_size);
7766 SaveLevel_BODY(file, level);
7768 chunk_size = SaveLevel_ELEM(NULL, level);
7769 if (chunk_size > LEVEL_CHUNK_ELEM_UNCHANGED) // save if changed
7771 putFileChunkBE(file, "ELEM", chunk_size);
7772 SaveLevel_ELEM(file, level);
7775 for (i = 0; i < NUM_ENVELOPES; i++)
7777 int element = EL_ENVELOPE_1 + i;
7779 chunk_size = SaveLevel_NOTE(NULL, level, element);
7780 if (chunk_size > LEVEL_CHUNK_NOTE_UNCHANGED) // save if changed
7782 putFileChunkBE(file, "NOTE", chunk_size);
7783 SaveLevel_NOTE(file, level, element);
7787 // if not using template level, check for non-default custom/group elements
7788 if (!level->use_custom_template || save_as_template)
7790 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7792 int element = EL_CUSTOM_START + i;
7794 chunk_size = SaveLevel_CUSX(NULL, level, element);
7795 if (chunk_size > LEVEL_CHUNK_CUSX_UNCHANGED) // save if changed
7797 putFileChunkBE(file, "CUSX", chunk_size);
7798 SaveLevel_CUSX(file, level, element);
7802 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
7804 int element = EL_GROUP_START + i;
7806 chunk_size = SaveLevel_GRPX(NULL, level, element);
7807 if (chunk_size > LEVEL_CHUNK_GRPX_UNCHANGED) // save if changed
7809 putFileChunkBE(file, "GRPX", chunk_size);
7810 SaveLevel_GRPX(file, level, element);
7814 for (i = 0; i < NUM_EMPTY_ELEMENTS_ALL; i++)
7816 int element = GET_EMPTY_ELEMENT(i);
7818 chunk_size = SaveLevel_EMPX(NULL, level, element);
7819 if (chunk_size > LEVEL_CHUNK_EMPX_UNCHANGED) // save if changed
7821 putFileChunkBE(file, "EMPX", chunk_size);
7822 SaveLevel_EMPX(file, level, element);
7829 SetFilePermissions(filename, PERMS_PRIVATE);
7832 void SaveLevel(int nr)
7834 char *filename = getDefaultLevelFilename(nr);
7836 SaveLevelFromFilename(&level, filename, FALSE);
7839 void SaveLevelTemplate(void)
7841 char *filename = getLocalLevelTemplateFilename();
7843 SaveLevelFromFilename(&level, filename, TRUE);
7846 boolean SaveLevelChecked(int nr)
7848 char *filename = getDefaultLevelFilename(nr);
7849 boolean new_level = !fileExists(filename);
7850 boolean level_saved = FALSE;
7852 if (new_level || Request("Save this level and kill the old?", REQ_ASK))
7857 Request("Level saved!", REQ_CONFIRM);
7865 void DumpLevel(struct LevelInfo *level)
7867 if (level->no_level_file || level->no_valid_file)
7869 Warn("cannot dump -- no valid level file found");
7875 Print("Level xxx (file version %08d, game version %08d)\n",
7876 level->file_version, level->game_version);
7879 Print("Level author: '%s'\n", level->author);
7880 Print("Level title: '%s'\n", level->name);
7882 Print("Playfield size: %d x %d\n", level->fieldx, level->fieldy);
7884 Print("Level time: %d seconds\n", level->time);
7885 Print("Gems needed: %d\n", level->gems_needed);
7887 Print("Time for magic wall: %d seconds\n", level->time_magic_wall);
7888 Print("Time for wheel: %d seconds\n", level->time_wheel);
7889 Print("Time for light: %d seconds\n", level->time_light);
7890 Print("Time for timegate: %d seconds\n", level->time_timegate);
7892 Print("Amoeba speed: %d\n", level->amoeba_speed);
7895 Print("EM style slippery gems: %s\n", (level->em_slippery_gems ? "yes" : "no"));
7896 Print("Player blocks last field: %s\n", (level->block_last_field ? "yes" : "no"));
7897 Print("SP player blocks last field: %s\n", (level->sp_block_last_field ? "yes" : "no"));
7898 Print("use spring bug: %s\n", (level->use_spring_bug ? "yes" : "no"));
7899 Print("use step counter: %s\n", (level->use_step_counter ? "yes" : "no"));
7900 Print("rate time over score: %s\n", (level->rate_time_over_score ? "yes" : "no"));
7906 for (i = 0; i < NUM_ENVELOPES; i++)
7908 char *text = level->envelope[i].text;
7909 int text_len = strlen(text);
7910 boolean has_text = FALSE;
7912 for (j = 0; j < text_len; j++)
7913 if (text[j] != ' ' && text[j] != '\n')
7919 Print("Envelope %d:\n'%s'\n", i + 1, text);
7927 void DumpLevels(void)
7929 static LevelDirTree *dumplevel_leveldir = NULL;
7931 dumplevel_leveldir = getTreeInfoFromIdentifier(leveldir_first,
7932 global.dumplevel_leveldir);
7934 if (dumplevel_leveldir == NULL)
7935 Fail("no such level identifier: '%s'", global.dumplevel_leveldir);
7937 if (global.dumplevel_level_nr < dumplevel_leveldir->first_level ||
7938 global.dumplevel_level_nr > dumplevel_leveldir->last_level)
7939 Fail("no such level number: %d", global.dumplevel_level_nr);
7941 leveldir_current = dumplevel_leveldir;
7943 LoadLevel(global.dumplevel_level_nr);
7950 // ============================================================================
7951 // tape file functions
7952 // ============================================================================
7954 static void setTapeInfoToDefaults(void)
7958 // always start with reliable default values (empty tape)
7961 // default values (also for pre-1.2 tapes) with only the first player
7962 tape.player_participates[0] = TRUE;
7963 for (i = 1; i < MAX_PLAYERS; i++)
7964 tape.player_participates[i] = FALSE;
7966 // at least one (default: the first) player participates in every tape
7967 tape.num_participating_players = 1;
7969 tape.property_bits = TAPE_PROPERTY_NONE;
7971 tape.level_nr = level_nr;
7973 tape.changed = FALSE;
7974 tape.solved = FALSE;
7976 tape.recording = FALSE;
7977 tape.playing = FALSE;
7978 tape.pausing = FALSE;
7980 tape.scr_fieldx = SCR_FIELDX_DEFAULT;
7981 tape.scr_fieldy = SCR_FIELDY_DEFAULT;
7983 tape.no_info_chunk = TRUE;
7984 tape.no_valid_file = FALSE;
7987 static int getTapePosSize(struct TapeInfo *tape)
7989 int tape_pos_size = 0;
7991 if (tape->use_key_actions)
7992 tape_pos_size += tape->num_participating_players;
7994 if (tape->use_mouse_actions)
7995 tape_pos_size += 3; // x and y position and mouse button mask
7997 tape_pos_size += 1; // tape action delay value
7999 return tape_pos_size;
8002 static void setTapeActionFlags(struct TapeInfo *tape, int value)
8004 tape->use_key_actions = FALSE;
8005 tape->use_mouse_actions = FALSE;
8007 if (value != TAPE_USE_MOUSE_ACTIONS_ONLY)
8008 tape->use_key_actions = TRUE;
8010 if (value != TAPE_USE_KEY_ACTIONS_ONLY)
8011 tape->use_mouse_actions = TRUE;
8014 static int getTapeActionValue(struct TapeInfo *tape)
8016 return (tape->use_key_actions &&
8017 tape->use_mouse_actions ? TAPE_USE_KEY_AND_MOUSE_ACTIONS :
8018 tape->use_key_actions ? TAPE_USE_KEY_ACTIONS_ONLY :
8019 tape->use_mouse_actions ? TAPE_USE_MOUSE_ACTIONS_ONLY :
8020 TAPE_ACTIONS_DEFAULT);
8023 static int LoadTape_VERS(File *file, int chunk_size, struct TapeInfo *tape)
8025 tape->file_version = getFileVersion(file);
8026 tape->game_version = getFileVersion(file);
8031 static int LoadTape_HEAD(File *file, int chunk_size, struct TapeInfo *tape)
8035 tape->random_seed = getFile32BitBE(file);
8036 tape->date = getFile32BitBE(file);
8037 tape->length = getFile32BitBE(file);
8039 // read header fields that are new since version 1.2
8040 if (tape->file_version >= FILE_VERSION_1_2)
8042 byte store_participating_players = getFile8Bit(file);
8045 // since version 1.2, tapes store which players participate in the tape
8046 tape->num_participating_players = 0;
8047 for (i = 0; i < MAX_PLAYERS; i++)
8049 tape->player_participates[i] = FALSE;
8051 if (store_participating_players & (1 << i))
8053 tape->player_participates[i] = TRUE;
8054 tape->num_participating_players++;
8058 setTapeActionFlags(tape, getFile8Bit(file));
8060 tape->property_bits = getFile8Bit(file);
8061 tape->solved = getFile8Bit(file);
8063 engine_version = getFileVersion(file);
8064 if (engine_version > 0)
8065 tape->engine_version = engine_version;
8067 tape->engine_version = tape->game_version;
8073 static int LoadTape_SCRN(File *file, int chunk_size, struct TapeInfo *tape)
8075 tape->scr_fieldx = getFile8Bit(file);
8076 tape->scr_fieldy = getFile8Bit(file);
8081 static int LoadTape_INFO(File *file, int chunk_size, struct TapeInfo *tape)
8083 char *level_identifier = NULL;
8084 int level_identifier_size;
8087 tape->no_info_chunk = FALSE;
8089 level_identifier_size = getFile16BitBE(file);
8091 level_identifier = checked_malloc(level_identifier_size);
8093 for (i = 0; i < level_identifier_size; i++)
8094 level_identifier[i] = getFile8Bit(file);
8096 strncpy(tape->level_identifier, level_identifier, MAX_FILENAME_LEN);
8097 tape->level_identifier[MAX_FILENAME_LEN] = '\0';
8099 checked_free(level_identifier);
8101 tape->level_nr = getFile16BitBE(file);
8103 chunk_size = 2 + level_identifier_size + 2;
8108 static int LoadTape_BODY(File *file, int chunk_size, struct TapeInfo *tape)
8111 int tape_pos_size = getTapePosSize(tape);
8112 int chunk_size_expected = tape_pos_size * tape->length;
8114 if (chunk_size_expected != chunk_size)
8116 ReadUnusedBytesFromFile(file, chunk_size);
8117 return chunk_size_expected;
8120 for (i = 0; i < tape->length; i++)
8122 if (i >= MAX_TAPE_LEN)
8124 Warn("tape truncated -- size exceeds maximum tape size %d",
8127 // tape too large; read and ignore remaining tape data from this chunk
8128 for (;i < tape->length; i++)
8129 ReadUnusedBytesFromFile(file, tape_pos_size);
8134 if (tape->use_key_actions)
8136 for (j = 0; j < MAX_PLAYERS; j++)
8138 tape->pos[i].action[j] = MV_NONE;
8140 if (tape->player_participates[j])
8141 tape->pos[i].action[j] = getFile8Bit(file);
8145 if (tape->use_mouse_actions)
8147 tape->pos[i].action[TAPE_ACTION_LX] = getFile8Bit(file);
8148 tape->pos[i].action[TAPE_ACTION_LY] = getFile8Bit(file);
8149 tape->pos[i].action[TAPE_ACTION_BUTTON] = getFile8Bit(file);
8152 tape->pos[i].delay = getFile8Bit(file);
8154 if (tape->file_version == FILE_VERSION_1_0)
8156 // eliminate possible diagonal moves in old tapes
8157 // this is only for backward compatibility
8159 byte joy_dir[4] = { JOY_LEFT, JOY_RIGHT, JOY_UP, JOY_DOWN };
8160 byte action = tape->pos[i].action[0];
8161 int k, num_moves = 0;
8163 for (k = 0; k < 4; k++)
8165 if (action & joy_dir[k])
8167 tape->pos[i + num_moves].action[0] = joy_dir[k];
8169 tape->pos[i + num_moves].delay = 0;
8178 tape->length += num_moves;
8181 else if (tape->file_version < FILE_VERSION_2_0)
8183 // convert pre-2.0 tapes to new tape format
8185 if (tape->pos[i].delay > 1)
8188 tape->pos[i + 1] = tape->pos[i];
8189 tape->pos[i + 1].delay = 1;
8192 for (j = 0; j < MAX_PLAYERS; j++)
8193 tape->pos[i].action[j] = MV_NONE;
8194 tape->pos[i].delay--;
8201 if (checkEndOfFile(file))
8205 if (i != tape->length)
8206 chunk_size = tape_pos_size * i;
8211 static void LoadTape_SokobanSolution(char *filename)
8214 int move_delay = TILESIZE / level.initial_player_stepsize[0];
8216 if (!(file = openFile(filename, MODE_READ)))
8218 tape.no_valid_file = TRUE;
8223 while (!checkEndOfFile(file))
8225 unsigned char c = getByteFromFile(file);
8227 if (checkEndOfFile(file))
8234 tape.pos[tape.length].action[0] = MV_UP;
8235 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
8241 tape.pos[tape.length].action[0] = MV_DOWN;
8242 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
8248 tape.pos[tape.length].action[0] = MV_LEFT;
8249 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
8255 tape.pos[tape.length].action[0] = MV_RIGHT;
8256 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
8264 // ignore white-space characters
8268 tape.no_valid_file = TRUE;
8270 Warn("unsupported Sokoban solution file '%s' ['%d']", filename, c);
8278 if (tape.no_valid_file)
8281 tape.length_frames = GetTapeLengthFrames();
8282 tape.length_seconds = GetTapeLengthSeconds();
8285 void LoadTapeFromFilename(char *filename)
8287 char cookie[MAX_LINE_LEN];
8288 char chunk_name[CHUNK_ID_LEN + 1];
8292 // always start with reliable default values
8293 setTapeInfoToDefaults();
8295 if (strSuffix(filename, ".sln"))
8297 LoadTape_SokobanSolution(filename);
8302 if (!(file = openFile(filename, MODE_READ)))
8304 tape.no_valid_file = TRUE;
8309 getFileChunkBE(file, chunk_name, NULL);
8310 if (strEqual(chunk_name, "RND1"))
8312 getFile32BitBE(file); // not used
8314 getFileChunkBE(file, chunk_name, NULL);
8315 if (!strEqual(chunk_name, "TAPE"))
8317 tape.no_valid_file = TRUE;
8319 Warn("unknown format of tape file '%s'", filename);
8326 else // check for pre-2.0 file format with cookie string
8328 strcpy(cookie, chunk_name);
8329 if (getStringFromFile(file, &cookie[4], MAX_LINE_LEN - 4) == NULL)
8331 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
8332 cookie[strlen(cookie) - 1] = '\0';
8334 if (!checkCookieString(cookie, TAPE_COOKIE_TMPL))
8336 tape.no_valid_file = TRUE;
8338 Warn("unknown format of tape file '%s'", filename);
8345 if ((tape.file_version = getFileVersionFromCookieString(cookie)) == -1)
8347 tape.no_valid_file = TRUE;
8349 Warn("unsupported version of tape file '%s'", filename);
8356 // pre-2.0 tape files have no game version, so use file version here
8357 tape.game_version = tape.file_version;
8360 if (tape.file_version < FILE_VERSION_1_2)
8362 // tape files from versions before 1.2.0 without chunk structure
8363 LoadTape_HEAD(file, TAPE_CHUNK_HEAD_SIZE, &tape);
8364 LoadTape_BODY(file, 2 * tape.length, &tape);
8372 int (*loader)(File *, int, struct TapeInfo *);
8376 { "VERS", TAPE_CHUNK_VERS_SIZE, LoadTape_VERS },
8377 { "HEAD", TAPE_CHUNK_HEAD_SIZE, LoadTape_HEAD },
8378 { "SCRN", TAPE_CHUNK_SCRN_SIZE, LoadTape_SCRN },
8379 { "INFO", -1, LoadTape_INFO },
8380 { "BODY", -1, LoadTape_BODY },
8384 while (getFileChunkBE(file, chunk_name, &chunk_size))
8388 while (chunk_info[i].name != NULL &&
8389 !strEqual(chunk_name, chunk_info[i].name))
8392 if (chunk_info[i].name == NULL)
8394 Warn("unknown chunk '%s' in tape file '%s'",
8395 chunk_name, filename);
8397 ReadUnusedBytesFromFile(file, chunk_size);
8399 else if (chunk_info[i].size != -1 &&
8400 chunk_info[i].size != chunk_size)
8402 Warn("wrong size (%d) of chunk '%s' in tape file '%s'",
8403 chunk_size, chunk_name, filename);
8405 ReadUnusedBytesFromFile(file, chunk_size);
8409 // call function to load this tape chunk
8410 int chunk_size_expected =
8411 (chunk_info[i].loader)(file, chunk_size, &tape);
8413 // the size of some chunks cannot be checked before reading other
8414 // chunks first (like "HEAD" and "BODY") that contain some header
8415 // information, so check them here
8416 if (chunk_size_expected != chunk_size)
8418 Warn("wrong size (%d) of chunk '%s' in tape file '%s'",
8419 chunk_size, chunk_name, filename);
8427 tape.length_frames = GetTapeLengthFrames();
8428 tape.length_seconds = GetTapeLengthSeconds();
8431 Debug("files:LoadTapeFromFilename", "tape file version: %d",
8433 Debug("files:LoadTapeFromFilename", "tape game version: %d",
8435 Debug("files:LoadTapeFromFilename", "tape engine version: %d",
8436 tape.engine_version);
8440 void LoadTape(int nr)
8442 char *filename = getTapeFilename(nr);
8444 LoadTapeFromFilename(filename);
8447 void LoadSolutionTape(int nr)
8449 char *filename = getSolutionTapeFilename(nr);
8451 LoadTapeFromFilename(filename);
8453 if (TAPE_IS_EMPTY(tape) &&
8454 level.game_engine_type == GAME_ENGINE_TYPE_SP &&
8455 level.native_sp_level->demo.is_available)
8456 CopyNativeTape_SP_to_RND(&level);
8459 void LoadScoreTape(char *score_tape_basename, int nr)
8461 char *filename = getScoreTapeFilename(score_tape_basename, nr);
8463 LoadTapeFromFilename(filename);
8466 void LoadScoreCacheTape(char *score_tape_basename, int nr)
8468 char *filename = getScoreCacheTapeFilename(score_tape_basename, nr);
8470 LoadTapeFromFilename(filename);
8473 static boolean checkSaveTape_SCRN(struct TapeInfo *tape)
8475 // chunk required for team mode tapes with non-default screen size
8476 return (tape->num_participating_players > 1 &&
8477 (tape->scr_fieldx != SCR_FIELDX_DEFAULT ||
8478 tape->scr_fieldy != SCR_FIELDY_DEFAULT));
8481 static void SaveTape_VERS(FILE *file, struct TapeInfo *tape)
8483 putFileVersion(file, tape->file_version);
8484 putFileVersion(file, tape->game_version);
8487 static void SaveTape_HEAD(FILE *file, struct TapeInfo *tape)
8490 byte store_participating_players = 0;
8492 // set bits for participating players for compact storage
8493 for (i = 0; i < MAX_PLAYERS; i++)
8494 if (tape->player_participates[i])
8495 store_participating_players |= (1 << i);
8497 putFile32BitBE(file, tape->random_seed);
8498 putFile32BitBE(file, tape->date);
8499 putFile32BitBE(file, tape->length);
8501 putFile8Bit(file, store_participating_players);
8503 putFile8Bit(file, getTapeActionValue(tape));
8505 putFile8Bit(file, tape->property_bits);
8506 putFile8Bit(file, tape->solved);
8508 putFileVersion(file, tape->engine_version);
8511 static void SaveTape_SCRN(FILE *file, struct TapeInfo *tape)
8513 putFile8Bit(file, tape->scr_fieldx);
8514 putFile8Bit(file, tape->scr_fieldy);
8517 static void SaveTape_INFO(FILE *file, struct TapeInfo *tape)
8519 int level_identifier_size = strlen(tape->level_identifier) + 1;
8522 putFile16BitBE(file, level_identifier_size);
8524 for (i = 0; i < level_identifier_size; i++)
8525 putFile8Bit(file, tape->level_identifier[i]);
8527 putFile16BitBE(file, tape->level_nr);
8530 static void SaveTape_BODY(FILE *file, struct TapeInfo *tape)
8534 for (i = 0; i < tape->length; i++)
8536 if (tape->use_key_actions)
8538 for (j = 0; j < MAX_PLAYERS; j++)
8539 if (tape->player_participates[j])
8540 putFile8Bit(file, tape->pos[i].action[j]);
8543 if (tape->use_mouse_actions)
8545 putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_LX]);
8546 putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_LY]);
8547 putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_BUTTON]);
8550 putFile8Bit(file, tape->pos[i].delay);
8554 void SaveTapeToFilename(char *filename)
8558 int info_chunk_size;
8559 int body_chunk_size;
8561 if (!(file = fopen(filename, MODE_WRITE)))
8563 Warn("cannot save level recording file '%s'", filename);
8568 tape_pos_size = getTapePosSize(&tape);
8570 info_chunk_size = 2 + (strlen(tape.level_identifier) + 1) + 2;
8571 body_chunk_size = tape_pos_size * tape.length;
8573 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
8574 putFileChunkBE(file, "TAPE", CHUNK_SIZE_NONE);
8576 putFileChunkBE(file, "VERS", TAPE_CHUNK_VERS_SIZE);
8577 SaveTape_VERS(file, &tape);
8579 putFileChunkBE(file, "HEAD", TAPE_CHUNK_HEAD_SIZE);
8580 SaveTape_HEAD(file, &tape);
8582 if (checkSaveTape_SCRN(&tape))
8584 putFileChunkBE(file, "SCRN", TAPE_CHUNK_SCRN_SIZE);
8585 SaveTape_SCRN(file, &tape);
8588 putFileChunkBE(file, "INFO", info_chunk_size);
8589 SaveTape_INFO(file, &tape);
8591 putFileChunkBE(file, "BODY", body_chunk_size);
8592 SaveTape_BODY(file, &tape);
8596 SetFilePermissions(filename, PERMS_PRIVATE);
8599 static void SaveTapeExt(char *filename)
8603 tape.file_version = FILE_VERSION_ACTUAL;
8604 tape.game_version = GAME_VERSION_ACTUAL;
8606 tape.num_participating_players = 0;
8608 // count number of participating players
8609 for (i = 0; i < MAX_PLAYERS; i++)
8610 if (tape.player_participates[i])
8611 tape.num_participating_players++;
8613 SaveTapeToFilename(filename);
8615 tape.changed = FALSE;
8618 void SaveTape(int nr)
8620 char *filename = getTapeFilename(nr);
8622 InitTapeDirectory(leveldir_current->subdir);
8624 SaveTapeExt(filename);
8627 void SaveScoreTape(int nr)
8629 char *filename = getScoreTapeFilename(tape.score_tape_basename, nr);
8631 // used instead of "leveldir_current->subdir" (for network games)
8632 InitScoreTapeDirectory(levelset.identifier, nr);
8634 SaveTapeExt(filename);
8637 static boolean SaveTapeCheckedExt(int nr, char *msg_replace, char *msg_saved,
8638 unsigned int req_state_added)
8640 char *filename = getTapeFilename(nr);
8641 boolean new_tape = !fileExists(filename);
8642 boolean tape_saved = FALSE;
8644 if (new_tape || Request(msg_replace, REQ_ASK | req_state_added))
8649 Request(msg_saved, REQ_CONFIRM | req_state_added);
8657 boolean SaveTapeChecked(int nr)
8659 return SaveTapeCheckedExt(nr, "Replace old tape?", "Tape saved!", 0);
8662 boolean SaveTapeChecked_LevelSolved(int nr)
8664 return SaveTapeCheckedExt(nr, "Level solved! Replace old tape?",
8665 "Level solved! Tape saved!", REQ_STAY_OPEN);
8668 void DumpTape(struct TapeInfo *tape)
8670 int tape_frame_counter;
8673 if (tape->no_valid_file)
8675 Warn("cannot dump -- no valid tape file found");
8682 Print("Tape of Level %03d (file version %08d, game version %08d)\n",
8683 tape->level_nr, tape->file_version, tape->game_version);
8684 Print(" (effective engine version %08d)\n",
8685 tape->engine_version);
8686 Print("Level series identifier: '%s'\n", tape->level_identifier);
8688 Print("Solution tape: %s\n",
8689 tape->solved ? "yes" :
8690 tape->game_version < VERSION_IDENT(4,3,2,3) ? "unknown" : "no");
8692 Print("Special tape properties: ");
8693 if (tape->property_bits == TAPE_PROPERTY_NONE)
8695 if (tape->property_bits & TAPE_PROPERTY_EM_RANDOM_BUG)
8696 Print("[em_random_bug]");
8697 if (tape->property_bits & TAPE_PROPERTY_GAME_SPEED)
8698 Print("[game_speed]");
8699 if (tape->property_bits & TAPE_PROPERTY_PAUSE_MODE)
8701 if (tape->property_bits & TAPE_PROPERTY_SINGLE_STEP)
8702 Print("[single_step]");
8703 if (tape->property_bits & TAPE_PROPERTY_SNAPSHOT)
8704 Print("[snapshot]");
8705 if (tape->property_bits & TAPE_PROPERTY_REPLAYED)
8706 Print("[replayed]");
8707 if (tape->property_bits & TAPE_PROPERTY_TAS_KEYS)
8708 Print("[tas_keys]");
8709 if (tape->property_bits & TAPE_PROPERTY_SMALL_GRAPHICS)
8710 Print("[small_graphics]");
8713 int year2 = tape->date / 10000;
8714 int year4 = (year2 < 70 ? 2000 + year2 : 1900 + year2);
8715 int month_index_raw = (tape->date / 100) % 100;
8716 int month_index = month_index_raw % 12; // prevent invalid index
8717 int month = month_index + 1;
8718 int day = tape->date % 100;
8720 Print("Tape date: %04d-%02d-%02d\n", year4, month, day);
8724 tape_frame_counter = 0;
8726 for (i = 0; i < tape->length; i++)
8728 if (i >= MAX_TAPE_LEN)
8733 for (j = 0; j < MAX_PLAYERS; j++)
8735 if (tape->player_participates[j])
8737 int action = tape->pos[i].action[j];
8739 Print("%d:%02x ", j, action);
8740 Print("[%c%c%c%c|%c%c] - ",
8741 (action & JOY_LEFT ? '<' : ' '),
8742 (action & JOY_RIGHT ? '>' : ' '),
8743 (action & JOY_UP ? '^' : ' '),
8744 (action & JOY_DOWN ? 'v' : ' '),
8745 (action & JOY_BUTTON_1 ? '1' : ' '),
8746 (action & JOY_BUTTON_2 ? '2' : ' '));
8750 Print("(%03d) ", tape->pos[i].delay);
8751 Print("[%05d]\n", tape_frame_counter);
8753 tape_frame_counter += tape->pos[i].delay;
8759 void DumpTapes(void)
8761 static LevelDirTree *dumptape_leveldir = NULL;
8763 dumptape_leveldir = getTreeInfoFromIdentifier(leveldir_first,
8764 global.dumptape_leveldir);
8766 if (dumptape_leveldir == NULL)
8767 Fail("no such level identifier: '%s'", global.dumptape_leveldir);
8769 if (global.dumptape_level_nr < dumptape_leveldir->first_level ||
8770 global.dumptape_level_nr > dumptape_leveldir->last_level)
8771 Fail("no such level number: %d", global.dumptape_level_nr);
8773 leveldir_current = dumptape_leveldir;
8775 if (options.mytapes)
8776 LoadTape(global.dumptape_level_nr);
8778 LoadSolutionTape(global.dumptape_level_nr);
8786 // ============================================================================
8787 // score file functions
8788 // ============================================================================
8790 static void setScoreInfoToDefaultsExt(struct ScoreInfo *scores)
8794 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
8796 strcpy(scores->entry[i].tape_basename, UNDEFINED_FILENAME);
8797 strcpy(scores->entry[i].name, EMPTY_PLAYER_NAME);
8798 scores->entry[i].score = 0;
8799 scores->entry[i].time = 0;
8801 scores->entry[i].id = -1;
8802 strcpy(scores->entry[i].tape_date, UNKNOWN_NAME);
8803 strcpy(scores->entry[i].platform, UNKNOWN_NAME);
8804 strcpy(scores->entry[i].version, UNKNOWN_NAME);
8805 strcpy(scores->entry[i].country_name, UNKNOWN_NAME);
8806 strcpy(scores->entry[i].country_code, "??");
8809 scores->num_entries = 0;
8810 scores->last_added = -1;
8811 scores->last_added_local = -1;
8813 scores->updated = FALSE;
8814 scores->uploaded = FALSE;
8815 scores->tape_downloaded = FALSE;
8816 scores->force_last_added = FALSE;
8818 // The following values are intentionally not reset here:
8822 // - continue_playing
8823 // - continue_on_return
8826 static void setScoreInfoToDefaults(void)
8828 setScoreInfoToDefaultsExt(&scores);
8831 static void setServerScoreInfoToDefaults(void)
8833 setScoreInfoToDefaultsExt(&server_scores);
8836 static void LoadScore_OLD(int nr)
8839 char *filename = getScoreFilename(nr);
8840 char cookie[MAX_LINE_LEN];
8841 char line[MAX_LINE_LEN];
8845 if (!(file = fopen(filename, MODE_READ)))
8848 // check file identifier
8849 if (fgets(cookie, MAX_LINE_LEN, file) == NULL)
8851 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
8852 cookie[strlen(cookie) - 1] = '\0';
8854 if (!checkCookieString(cookie, SCORE_COOKIE_TMPL))
8856 Warn("unknown format of score file '%s'", filename);
8863 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
8865 if (fscanf(file, "%d", &scores.entry[i].score) == EOF)
8866 Warn("fscanf() failed; %s", strerror(errno));
8868 if (fgets(line, MAX_LINE_LEN, file) == NULL)
8871 if (strlen(line) > 0 && line[strlen(line) - 1] == '\n')
8872 line[strlen(line) - 1] = '\0';
8874 for (line_ptr = line; *line_ptr; line_ptr++)
8876 if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != '\0')
8878 strncpy(scores.entry[i].name, line_ptr, MAX_PLAYER_NAME_LEN);
8879 scores.entry[i].name[MAX_PLAYER_NAME_LEN] = '\0';
8888 static void ConvertScore_OLD(void)
8890 // only convert score to time for levels that rate playing time over score
8891 if (!level.rate_time_over_score)
8894 // convert old score to playing time for score-less levels (like Supaplex)
8895 int time_final_max = 999;
8898 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
8900 int score = scores.entry[i].score;
8902 if (score > 0 && score < time_final_max)
8903 scores.entry[i].time = (time_final_max - score - 1) * FRAMES_PER_SECOND;
8907 static int LoadScore_VERS(File *file, int chunk_size, struct ScoreInfo *scores)
8909 scores->file_version = getFileVersion(file);
8910 scores->game_version = getFileVersion(file);
8915 static int LoadScore_INFO(File *file, int chunk_size, struct ScoreInfo *scores)
8917 char *level_identifier = NULL;
8918 int level_identifier_size;
8921 level_identifier_size = getFile16BitBE(file);
8923 level_identifier = checked_malloc(level_identifier_size);
8925 for (i = 0; i < level_identifier_size; i++)
8926 level_identifier[i] = getFile8Bit(file);
8928 strncpy(scores->level_identifier, level_identifier, MAX_FILENAME_LEN);
8929 scores->level_identifier[MAX_FILENAME_LEN] = '\0';
8931 checked_free(level_identifier);
8933 scores->level_nr = getFile16BitBE(file);
8934 scores->num_entries = getFile16BitBE(file);
8936 chunk_size = 2 + level_identifier_size + 2 + 2;
8941 static int LoadScore_NAME(File *file, int chunk_size, struct ScoreInfo *scores)
8945 for (i = 0; i < scores->num_entries; i++)
8947 for (j = 0; j < MAX_PLAYER_NAME_LEN; j++)
8948 scores->entry[i].name[j] = getFile8Bit(file);
8950 scores->entry[i].name[MAX_PLAYER_NAME_LEN] = '\0';
8953 chunk_size = scores->num_entries * MAX_PLAYER_NAME_LEN;
8958 static int LoadScore_SCOR(File *file, int chunk_size, struct ScoreInfo *scores)
8962 for (i = 0; i < scores->num_entries; i++)
8963 scores->entry[i].score = getFile16BitBE(file);
8965 chunk_size = scores->num_entries * 2;
8970 static int LoadScore_SC4R(File *file, int chunk_size, struct ScoreInfo *scores)
8974 for (i = 0; i < scores->num_entries; i++)
8975 scores->entry[i].score = getFile32BitBE(file);
8977 chunk_size = scores->num_entries * 4;
8982 static int LoadScore_TIME(File *file, int chunk_size, struct ScoreInfo *scores)
8986 for (i = 0; i < scores->num_entries; i++)
8987 scores->entry[i].time = getFile32BitBE(file);
8989 chunk_size = scores->num_entries * 4;
8994 static int LoadScore_TAPE(File *file, int chunk_size, struct ScoreInfo *scores)
8998 for (i = 0; i < scores->num_entries; i++)
9000 for (j = 0; j < MAX_SCORE_TAPE_BASENAME_LEN; j++)
9001 scores->entry[i].tape_basename[j] = getFile8Bit(file);
9003 scores->entry[i].tape_basename[MAX_SCORE_TAPE_BASENAME_LEN] = '\0';
9006 chunk_size = scores->num_entries * MAX_SCORE_TAPE_BASENAME_LEN;
9011 void LoadScore(int nr)
9013 char *filename = getScoreFilename(nr);
9014 char cookie[MAX_LINE_LEN];
9015 char chunk_name[CHUNK_ID_LEN + 1];
9017 boolean old_score_file_format = FALSE;
9020 // always start with reliable default values
9021 setScoreInfoToDefaults();
9023 if (!(file = openFile(filename, MODE_READ)))
9026 getFileChunkBE(file, chunk_name, NULL);
9027 if (strEqual(chunk_name, "RND1"))
9029 getFile32BitBE(file); // not used
9031 getFileChunkBE(file, chunk_name, NULL);
9032 if (!strEqual(chunk_name, "SCOR"))
9034 Warn("unknown format of score file '%s'", filename);
9041 else // check for old file format with cookie string
9043 strcpy(cookie, chunk_name);
9044 if (getStringFromFile(file, &cookie[4], MAX_LINE_LEN - 4) == NULL)
9046 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
9047 cookie[strlen(cookie) - 1] = '\0';
9049 if (!checkCookieString(cookie, SCORE_COOKIE_TMPL))
9051 Warn("unknown format of score file '%s'", filename);
9058 old_score_file_format = TRUE;
9061 if (old_score_file_format)
9063 // score files from versions before 4.2.4.0 without chunk structure
9066 // convert score to time, if possible (mainly for Supaplex levels)
9075 int (*loader)(File *, int, struct ScoreInfo *);
9079 { "VERS", SCORE_CHUNK_VERS_SIZE, LoadScore_VERS },
9080 { "INFO", -1, LoadScore_INFO },
9081 { "NAME", -1, LoadScore_NAME },
9082 { "SCOR", -1, LoadScore_SCOR },
9083 { "SC4R", -1, LoadScore_SC4R },
9084 { "TIME", -1, LoadScore_TIME },
9085 { "TAPE", -1, LoadScore_TAPE },
9090 while (getFileChunkBE(file, chunk_name, &chunk_size))
9094 while (chunk_info[i].name != NULL &&
9095 !strEqual(chunk_name, chunk_info[i].name))
9098 if (chunk_info[i].name == NULL)
9100 Warn("unknown chunk '%s' in score file '%s'",
9101 chunk_name, filename);
9103 ReadUnusedBytesFromFile(file, chunk_size);
9105 else if (chunk_info[i].size != -1 &&
9106 chunk_info[i].size != chunk_size)
9108 Warn("wrong size (%d) of chunk '%s' in score file '%s'",
9109 chunk_size, chunk_name, filename);
9111 ReadUnusedBytesFromFile(file, chunk_size);
9115 // call function to load this score chunk
9116 int chunk_size_expected =
9117 (chunk_info[i].loader)(file, chunk_size, &scores);
9119 // the size of some chunks cannot be checked before reading other
9120 // chunks first (like "HEAD" and "BODY") that contain some header
9121 // information, so check them here
9122 if (chunk_size_expected != chunk_size)
9124 Warn("wrong size (%d) of chunk '%s' in score file '%s'",
9125 chunk_size, chunk_name, filename);
9134 #if ENABLE_HISTORIC_CHUNKS
9135 void SaveScore_OLD(int nr)
9138 char *filename = getScoreFilename(nr);
9141 // used instead of "leveldir_current->subdir" (for network games)
9142 InitScoreDirectory(levelset.identifier);
9144 if (!(file = fopen(filename, MODE_WRITE)))
9146 Warn("cannot save score for level %d", nr);
9151 fprintf(file, "%s\n\n", SCORE_COOKIE);
9153 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9154 fprintf(file, "%d %s\n", scores.entry[i].score, scores.entry[i].name);
9158 SetFilePermissions(filename, PERMS_PRIVATE);
9162 static void SaveScore_VERS(FILE *file, struct ScoreInfo *scores)
9164 putFileVersion(file, scores->file_version);
9165 putFileVersion(file, scores->game_version);
9168 static void SaveScore_INFO(FILE *file, struct ScoreInfo *scores)
9170 int level_identifier_size = strlen(scores->level_identifier) + 1;
9173 putFile16BitBE(file, level_identifier_size);
9175 for (i = 0; i < level_identifier_size; i++)
9176 putFile8Bit(file, scores->level_identifier[i]);
9178 putFile16BitBE(file, scores->level_nr);
9179 putFile16BitBE(file, scores->num_entries);
9182 static void SaveScore_NAME(FILE *file, struct ScoreInfo *scores)
9186 for (i = 0; i < scores->num_entries; i++)
9188 int name_size = strlen(scores->entry[i].name);
9190 for (j = 0; j < MAX_PLAYER_NAME_LEN; j++)
9191 putFile8Bit(file, (j < name_size ? scores->entry[i].name[j] : 0));
9195 static void SaveScore_SCOR(FILE *file, struct ScoreInfo *scores)
9199 for (i = 0; i < scores->num_entries; i++)
9200 putFile16BitBE(file, scores->entry[i].score);
9203 static void SaveScore_SC4R(FILE *file, struct ScoreInfo *scores)
9207 for (i = 0; i < scores->num_entries; i++)
9208 putFile32BitBE(file, scores->entry[i].score);
9211 static void SaveScore_TIME(FILE *file, struct ScoreInfo *scores)
9215 for (i = 0; i < scores->num_entries; i++)
9216 putFile32BitBE(file, scores->entry[i].time);
9219 static void SaveScore_TAPE(FILE *file, struct ScoreInfo *scores)
9223 for (i = 0; i < scores->num_entries; i++)
9225 int size = strlen(scores->entry[i].tape_basename);
9227 for (j = 0; j < MAX_SCORE_TAPE_BASENAME_LEN; j++)
9228 putFile8Bit(file, (j < size ? scores->entry[i].tape_basename[j] : 0));
9232 static void SaveScoreToFilename(char *filename)
9235 int info_chunk_size;
9236 int name_chunk_size;
9237 int scor_chunk_size;
9238 int sc4r_chunk_size;
9239 int time_chunk_size;
9240 int tape_chunk_size;
9241 boolean has_large_score_values;
9244 if (!(file = fopen(filename, MODE_WRITE)))
9246 Warn("cannot save score file '%s'", filename);
9251 info_chunk_size = 2 + (strlen(scores.level_identifier) + 1) + 2 + 2;
9252 name_chunk_size = scores.num_entries * MAX_PLAYER_NAME_LEN;
9253 scor_chunk_size = scores.num_entries * 2;
9254 sc4r_chunk_size = scores.num_entries * 4;
9255 time_chunk_size = scores.num_entries * 4;
9256 tape_chunk_size = scores.num_entries * MAX_SCORE_TAPE_BASENAME_LEN;
9258 has_large_score_values = FALSE;
9259 for (i = 0; i < scores.num_entries; i++)
9260 if (scores.entry[i].score > 0xffff)
9261 has_large_score_values = TRUE;
9263 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
9264 putFileChunkBE(file, "SCOR", CHUNK_SIZE_NONE);
9266 putFileChunkBE(file, "VERS", SCORE_CHUNK_VERS_SIZE);
9267 SaveScore_VERS(file, &scores);
9269 putFileChunkBE(file, "INFO", info_chunk_size);
9270 SaveScore_INFO(file, &scores);
9272 putFileChunkBE(file, "NAME", name_chunk_size);
9273 SaveScore_NAME(file, &scores);
9275 if (has_large_score_values)
9277 putFileChunkBE(file, "SC4R", sc4r_chunk_size);
9278 SaveScore_SC4R(file, &scores);
9282 putFileChunkBE(file, "SCOR", scor_chunk_size);
9283 SaveScore_SCOR(file, &scores);
9286 putFileChunkBE(file, "TIME", time_chunk_size);
9287 SaveScore_TIME(file, &scores);
9289 putFileChunkBE(file, "TAPE", tape_chunk_size);
9290 SaveScore_TAPE(file, &scores);
9294 SetFilePermissions(filename, PERMS_PRIVATE);
9297 void SaveScore(int nr)
9299 char *filename = getScoreFilename(nr);
9302 // used instead of "leveldir_current->subdir" (for network games)
9303 InitScoreDirectory(levelset.identifier);
9305 scores.file_version = FILE_VERSION_ACTUAL;
9306 scores.game_version = GAME_VERSION_ACTUAL;
9308 strncpy(scores.level_identifier, levelset.identifier, MAX_FILENAME_LEN);
9309 scores.level_identifier[MAX_FILENAME_LEN] = '\0';
9310 scores.level_nr = level_nr;
9312 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9313 if (scores.entry[i].score == 0 &&
9314 scores.entry[i].time == 0 &&
9315 strEqual(scores.entry[i].name, EMPTY_PLAYER_NAME))
9318 scores.num_entries = i;
9320 if (scores.num_entries == 0)
9323 SaveScoreToFilename(filename);
9326 static void LoadServerScoreFromCache(int nr)
9328 struct ScoreEntry score_entry;
9337 { &score_entry.score, FALSE, 0 },
9338 { &score_entry.time, FALSE, 0 },
9339 { score_entry.name, TRUE, MAX_PLAYER_NAME_LEN },
9340 { score_entry.tape_basename, TRUE, MAX_FILENAME_LEN },
9341 { score_entry.tape_date, TRUE, MAX_ISO_DATE_LEN },
9342 { &score_entry.id, FALSE, 0 },
9343 { score_entry.platform, TRUE, MAX_PLATFORM_TEXT_LEN },
9344 { score_entry.version, TRUE, MAX_VERSION_TEXT_LEN },
9345 { score_entry.country_code, TRUE, MAX_COUNTRY_CODE_LEN },
9346 { score_entry.country_name, TRUE, MAX_COUNTRY_NAME_LEN },
9350 char *filename = getScoreCacheFilename(nr);
9351 SetupFileHash *score_hash = loadSetupFileHash(filename);
9354 server_scores.num_entries = 0;
9356 if (score_hash == NULL)
9359 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9361 score_entry = server_scores.entry[i];
9363 for (j = 0; score_mapping[j].value != NULL; j++)
9367 sprintf(token, "%02d.%d", i, j);
9369 char *value = getHashEntry(score_hash, token);
9374 if (score_mapping[j].is_string)
9376 char *score_value = (char *)score_mapping[j].value;
9377 int value_size = score_mapping[j].string_size;
9379 strncpy(score_value, value, value_size);
9380 score_value[value_size] = '\0';
9384 int *score_value = (int *)score_mapping[j].value;
9386 *score_value = atoi(value);
9389 server_scores.num_entries = i + 1;
9392 server_scores.entry[i] = score_entry;
9395 freeSetupFileHash(score_hash);
9398 void LoadServerScore(int nr, boolean download_score)
9400 if (!setup.use_api_server)
9403 // always start with reliable default values
9404 setServerScoreInfoToDefaults();
9406 // 1st step: load server scores from cache file (which may not exist)
9407 // (this should prevent reading it while the thread is writing to it)
9408 LoadServerScoreFromCache(nr);
9410 if (download_score && runtime.use_api_server)
9412 // 2nd step: download server scores from score server to cache file
9413 // (as thread, as it might time out if the server is not reachable)
9414 ApiGetScoreAsThread(nr);
9418 void PrepareScoreTapesForUpload(char *leveldir_subdir)
9420 MarkTapeDirectoryUploadsAsIncomplete(leveldir_subdir);
9422 // if score tape not uploaded, ask for uploading missing tapes later
9423 if (!setup.has_remaining_tapes)
9424 setup.ask_for_remaining_tapes = TRUE;
9426 setup.provide_uploading_tapes = TRUE;
9427 setup.has_remaining_tapes = TRUE;
9429 SaveSetup_ServerSetup();
9432 void SaveServerScore(int nr, boolean tape_saved)
9434 if (!runtime.use_api_server)
9436 PrepareScoreTapesForUpload(leveldir_current->subdir);
9441 ApiAddScoreAsThread(nr, tape_saved, NULL);
9444 void SaveServerScoreFromFile(int nr, boolean tape_saved,
9445 char *score_tape_filename)
9447 if (!runtime.use_api_server)
9450 ApiAddScoreAsThread(nr, tape_saved, score_tape_filename);
9453 void LoadLocalAndServerScore(int nr, boolean download_score)
9455 int last_added_local = scores.last_added_local;
9456 boolean force_last_added = scores.force_last_added;
9458 // needed if only showing server scores
9459 setScoreInfoToDefaults();
9461 if (!strEqual(setup.scores_in_highscore_list, STR_SCORES_TYPE_SERVER_ONLY))
9464 // restore last added local score entry (before merging server scores)
9465 scores.last_added = scores.last_added_local = last_added_local;
9467 if (setup.use_api_server &&
9468 !strEqual(setup.scores_in_highscore_list, STR_SCORES_TYPE_LOCAL_ONLY))
9470 // load server scores from cache file and trigger update from server
9471 LoadServerScore(nr, download_score);
9473 // merge local scores with scores from server
9477 if (force_last_added)
9478 scores.force_last_added = force_last_added;
9482 // ============================================================================
9483 // setup file functions
9484 // ============================================================================
9486 #define TOKEN_STR_PLAYER_PREFIX "player_"
9489 static struct TokenInfo global_setup_tokens[] =
9493 &setup.player_name, "player_name"
9497 &setup.multiple_users, "multiple_users"
9501 &setup.sound, "sound"
9505 &setup.sound_loops, "repeating_sound_loops"
9509 &setup.sound_music, "background_music"
9513 &setup.sound_simple, "simple_sound_effects"
9517 &setup.toons, "toons"
9521 &setup.global_animations, "global_animations"
9525 &setup.scroll_delay, "scroll_delay"
9529 &setup.forced_scroll_delay, "forced_scroll_delay"
9533 &setup.scroll_delay_value, "scroll_delay_value"
9537 &setup.engine_snapshot_mode, "engine_snapshot_mode"
9541 &setup.engine_snapshot_memory, "engine_snapshot_memory"
9545 &setup.fade_screens, "fade_screens"
9549 &setup.autorecord, "automatic_tape_recording"
9553 &setup.autorecord_after_replay, "autorecord_after_replay"
9557 &setup.auto_pause_on_start, "auto_pause_on_start"
9561 &setup.show_titlescreen, "show_titlescreen"
9565 &setup.quick_doors, "quick_doors"
9569 &setup.team_mode, "team_mode"
9573 &setup.handicap, "handicap"
9577 &setup.skip_levels, "skip_levels"
9581 &setup.increment_levels, "increment_levels"
9585 &setup.auto_play_next_level, "auto_play_next_level"
9589 &setup.count_score_after_game, "count_score_after_game"
9593 &setup.show_scores_after_game, "show_scores_after_game"
9597 &setup.time_limit, "time_limit"
9601 &setup.fullscreen, "fullscreen"
9605 &setup.window_scaling_percent, "window_scaling_percent"
9609 &setup.window_scaling_quality, "window_scaling_quality"
9613 &setup.screen_rendering_mode, "screen_rendering_mode"
9617 &setup.vsync_mode, "vsync_mode"
9621 &setup.ask_on_escape, "ask_on_escape"
9625 &setup.ask_on_escape_editor, "ask_on_escape_editor"
9629 &setup.ask_on_game_over, "ask_on_game_over"
9633 &setup.ask_on_quit_game, "ask_on_quit_game"
9637 &setup.ask_on_quit_program, "ask_on_quit_program"
9641 &setup.quick_switch, "quick_player_switch"
9645 &setup.input_on_focus, "input_on_focus"
9649 &setup.prefer_aga_graphics, "prefer_aga_graphics"
9653 &setup.prefer_lowpass_sounds, "prefer_lowpass_sounds"
9657 &setup.prefer_extra_panel_items, "prefer_extra_panel_items"
9661 &setup.game_speed_extended, "game_speed_extended"
9665 &setup.game_frame_delay, "game_frame_delay"
9669 &setup.sp_show_border_elements, "sp_show_border_elements"
9673 &setup.small_game_graphics, "small_game_graphics"
9677 &setup.show_load_save_buttons, "show_load_save_buttons"
9681 &setup.show_undo_redo_buttons, "show_undo_redo_buttons"
9685 &setup.scores_in_highscore_list, "scores_in_highscore_list"
9689 &setup.graphics_set, "graphics_set"
9693 &setup.sounds_set, "sounds_set"
9697 &setup.music_set, "music_set"
9701 &setup.override_level_graphics, "override_level_graphics"
9705 &setup.override_level_sounds, "override_level_sounds"
9709 &setup.override_level_music, "override_level_music"
9713 &setup.volume_simple, "volume_simple"
9717 &setup.volume_loops, "volume_loops"
9721 &setup.volume_music, "volume_music"
9725 &setup.network_mode, "network_mode"
9729 &setup.network_player_nr, "network_player"
9733 &setup.network_server_hostname, "network_server_hostname"
9737 &setup.touch.control_type, "touch.control_type"
9741 &setup.touch.move_distance, "touch.move_distance"
9745 &setup.touch.drop_distance, "touch.drop_distance"
9749 &setup.touch.transparency, "touch.transparency"
9753 &setup.touch.draw_outlined, "touch.draw_outlined"
9757 &setup.touch.draw_pressed, "touch.draw_pressed"
9761 &setup.touch.grid_xsize[0], "touch.virtual_buttons.0.xsize"
9765 &setup.touch.grid_ysize[0], "touch.virtual_buttons.0.ysize"
9769 &setup.touch.grid_xsize[1], "touch.virtual_buttons.1.xsize"
9773 &setup.touch.grid_ysize[1], "touch.virtual_buttons.1.ysize"
9777 &setup.touch.overlay_buttons, "touch.overlay_buttons"
9781 static struct TokenInfo auto_setup_tokens[] =
9785 &setup.auto_setup.editor_zoom_tilesize, "editor.zoom_tilesize"
9789 static struct TokenInfo server_setup_tokens[] =
9793 &setup.player_uuid, "player_uuid"
9797 &setup.player_version, "player_version"
9801 &setup.use_api_server, TEST_PREFIX "use_api_server"
9805 &setup.api_server_hostname, TEST_PREFIX "api_server_hostname"
9809 &setup.api_server_password, TEST_PREFIX "api_server_password"
9813 &setup.ask_for_uploading_tapes, TEST_PREFIX "ask_for_uploading_tapes"
9817 &setup.ask_for_remaining_tapes, TEST_PREFIX "ask_for_remaining_tapes"
9821 &setup.provide_uploading_tapes, TEST_PREFIX "provide_uploading_tapes"
9825 &setup.ask_for_using_api_server,TEST_PREFIX "ask_for_using_api_server"
9829 &setup.has_remaining_tapes, TEST_PREFIX "has_remaining_tapes"
9833 static struct TokenInfo editor_setup_tokens[] =
9837 &setup.editor.el_classic, "editor.el_classic"
9841 &setup.editor.el_custom, "editor.el_custom"
9845 &setup.editor.el_user_defined, "editor.el_user_defined"
9849 &setup.editor.el_dynamic, "editor.el_dynamic"
9853 &setup.editor.el_headlines, "editor.el_headlines"
9857 &setup.editor.show_element_token, "editor.show_element_token"
9861 &setup.editor.show_read_only_warning, "editor.show_read_only_warning"
9865 static struct TokenInfo editor_cascade_setup_tokens[] =
9869 &setup.editor_cascade.el_bd, "editor.cascade.el_bd"
9873 &setup.editor_cascade.el_em, "editor.cascade.el_em"
9877 &setup.editor_cascade.el_emc, "editor.cascade.el_emc"
9881 &setup.editor_cascade.el_rnd, "editor.cascade.el_rnd"
9885 &setup.editor_cascade.el_sb, "editor.cascade.el_sb"
9889 &setup.editor_cascade.el_sp, "editor.cascade.el_sp"
9893 &setup.editor_cascade.el_dc, "editor.cascade.el_dc"
9897 &setup.editor_cascade.el_dx, "editor.cascade.el_dx"
9901 &setup.editor_cascade.el_mm, "editor.cascade.el_mm"
9905 &setup.editor_cascade.el_df, "editor.cascade.el_df"
9909 &setup.editor_cascade.el_chars, "editor.cascade.el_chars"
9913 &setup.editor_cascade.el_steel_chars, "editor.cascade.el_steel_chars"
9917 &setup.editor_cascade.el_ce, "editor.cascade.el_ce"
9921 &setup.editor_cascade.el_ge, "editor.cascade.el_ge"
9925 &setup.editor_cascade.el_es, "editor.cascade.el_es"
9929 &setup.editor_cascade.el_ref, "editor.cascade.el_ref"
9933 &setup.editor_cascade.el_user, "editor.cascade.el_user"
9937 &setup.editor_cascade.el_dynamic, "editor.cascade.el_dynamic"
9941 static struct TokenInfo shortcut_setup_tokens[] =
9945 &setup.shortcut.save_game, "shortcut.save_game"
9949 &setup.shortcut.load_game, "shortcut.load_game"
9953 &setup.shortcut.restart_game, "shortcut.restart_game"
9957 &setup.shortcut.pause_before_end, "shortcut.pause_before_end"
9961 &setup.shortcut.toggle_pause, "shortcut.toggle_pause"
9965 &setup.shortcut.focus_player[0], "shortcut.focus_player_1"
9969 &setup.shortcut.focus_player[1], "shortcut.focus_player_2"
9973 &setup.shortcut.focus_player[2], "shortcut.focus_player_3"
9977 &setup.shortcut.focus_player[3], "shortcut.focus_player_4"
9981 &setup.shortcut.focus_player_all, "shortcut.focus_player_all"
9985 &setup.shortcut.tape_eject, "shortcut.tape_eject"
9989 &setup.shortcut.tape_extra, "shortcut.tape_extra"
9993 &setup.shortcut.tape_stop, "shortcut.tape_stop"
9997 &setup.shortcut.tape_pause, "shortcut.tape_pause"
10001 &setup.shortcut.tape_record, "shortcut.tape_record"
10005 &setup.shortcut.tape_play, "shortcut.tape_play"
10009 &setup.shortcut.sound_simple, "shortcut.sound_simple"
10013 &setup.shortcut.sound_loops, "shortcut.sound_loops"
10017 &setup.shortcut.sound_music, "shortcut.sound_music"
10021 &setup.shortcut.snap_left, "shortcut.snap_left"
10025 &setup.shortcut.snap_right, "shortcut.snap_right"
10029 &setup.shortcut.snap_up, "shortcut.snap_up"
10033 &setup.shortcut.snap_down, "shortcut.snap_down"
10037 static struct SetupInputInfo setup_input;
10038 static struct TokenInfo player_setup_tokens[] =
10042 &setup_input.use_joystick, ".use_joystick"
10046 &setup_input.joy.device_name, ".joy.device_name"
10050 &setup_input.joy.xleft, ".joy.xleft"
10054 &setup_input.joy.xmiddle, ".joy.xmiddle"
10058 &setup_input.joy.xright, ".joy.xright"
10062 &setup_input.joy.yupper, ".joy.yupper"
10066 &setup_input.joy.ymiddle, ".joy.ymiddle"
10070 &setup_input.joy.ylower, ".joy.ylower"
10074 &setup_input.joy.snap, ".joy.snap_field"
10078 &setup_input.joy.drop, ".joy.place_bomb"
10082 &setup_input.key.left, ".key.move_left"
10086 &setup_input.key.right, ".key.move_right"
10090 &setup_input.key.up, ".key.move_up"
10094 &setup_input.key.down, ".key.move_down"
10098 &setup_input.key.snap, ".key.snap_field"
10102 &setup_input.key.drop, ".key.place_bomb"
10106 static struct TokenInfo system_setup_tokens[] =
10110 &setup.system.sdl_renderdriver, "system.sdl_renderdriver"
10114 &setup.system.sdl_videodriver, "system.sdl_videodriver"
10118 &setup.system.sdl_audiodriver, "system.sdl_audiodriver"
10122 &setup.system.audio_fragment_size, "system.audio_fragment_size"
10126 static struct TokenInfo internal_setup_tokens[] =
10130 &setup.internal.program_title, "program_title"
10134 &setup.internal.program_version, "program_version"
10138 &setup.internal.program_author, "program_author"
10142 &setup.internal.program_email, "program_email"
10146 &setup.internal.program_website, "program_website"
10150 &setup.internal.program_copyright, "program_copyright"
10154 &setup.internal.program_company, "program_company"
10158 &setup.internal.program_icon_file, "program_icon_file"
10162 &setup.internal.default_graphics_set, "default_graphics_set"
10166 &setup.internal.default_sounds_set, "default_sounds_set"
10170 &setup.internal.default_music_set, "default_music_set"
10174 &setup.internal.fallback_graphics_file, "fallback_graphics_file"
10178 &setup.internal.fallback_sounds_file, "fallback_sounds_file"
10182 &setup.internal.fallback_music_file, "fallback_music_file"
10186 &setup.internal.default_level_series, "default_level_series"
10190 &setup.internal.default_window_width, "default_window_width"
10194 &setup.internal.default_window_height, "default_window_height"
10198 &setup.internal.choose_from_top_leveldir, "choose_from_top_leveldir"
10202 &setup.internal.show_scaling_in_title, "show_scaling_in_title"
10206 &setup.internal.create_user_levelset, "create_user_levelset"
10210 &setup.internal.info_screens_from_main, "info_screens_from_main"
10214 &setup.internal.menu_game, "menu_game"
10218 &setup.internal.menu_engines, "menu_engines"
10222 &setup.internal.menu_editor, "menu_editor"
10226 &setup.internal.menu_graphics, "menu_graphics"
10230 &setup.internal.menu_sound, "menu_sound"
10234 &setup.internal.menu_artwork, "menu_artwork"
10238 &setup.internal.menu_input, "menu_input"
10242 &setup.internal.menu_touch, "menu_touch"
10246 &setup.internal.menu_shortcuts, "menu_shortcuts"
10250 &setup.internal.menu_exit, "menu_exit"
10254 &setup.internal.menu_save_and_exit, "menu_save_and_exit"
10258 &setup.internal.menu_shortcuts_various, "menu_shortcuts_various"
10262 &setup.internal.menu_shortcuts_focus, "menu_shortcuts_focus"
10266 &setup.internal.menu_shortcuts_tape, "menu_shortcuts_tape"
10270 &setup.internal.menu_shortcuts_sound, "menu_shortcuts_sound"
10274 &setup.internal.menu_shortcuts_snap, "menu_shortcuts_snap"
10278 &setup.internal.info_title, "info_title"
10282 &setup.internal.info_elements, "info_elements"
10286 &setup.internal.info_music, "info_music"
10290 &setup.internal.info_credits, "info_credits"
10294 &setup.internal.info_program, "info_program"
10298 &setup.internal.info_version, "info_version"
10302 &setup.internal.info_levelset, "info_levelset"
10306 &setup.internal.info_exit, "info_exit"
10310 static struct TokenInfo debug_setup_tokens[] =
10314 &setup.debug.frame_delay[0], "debug.frame_delay_0"
10318 &setup.debug.frame_delay[1], "debug.frame_delay_1"
10322 &setup.debug.frame_delay[2], "debug.frame_delay_2"
10326 &setup.debug.frame_delay[3], "debug.frame_delay_3"
10330 &setup.debug.frame_delay[4], "debug.frame_delay_4"
10334 &setup.debug.frame_delay[5], "debug.frame_delay_5"
10338 &setup.debug.frame_delay[6], "debug.frame_delay_6"
10342 &setup.debug.frame_delay[7], "debug.frame_delay_7"
10346 &setup.debug.frame_delay[8], "debug.frame_delay_8"
10350 &setup.debug.frame_delay[9], "debug.frame_delay_9"
10354 &setup.debug.frame_delay_key[0], "debug.key.frame_delay_0"
10358 &setup.debug.frame_delay_key[1], "debug.key.frame_delay_1"
10362 &setup.debug.frame_delay_key[2], "debug.key.frame_delay_2"
10366 &setup.debug.frame_delay_key[3], "debug.key.frame_delay_3"
10370 &setup.debug.frame_delay_key[4], "debug.key.frame_delay_4"
10374 &setup.debug.frame_delay_key[5], "debug.key.frame_delay_5"
10378 &setup.debug.frame_delay_key[6], "debug.key.frame_delay_6"
10382 &setup.debug.frame_delay_key[7], "debug.key.frame_delay_7"
10386 &setup.debug.frame_delay_key[8], "debug.key.frame_delay_8"
10390 &setup.debug.frame_delay_key[9], "debug.key.frame_delay_9"
10394 &setup.debug.frame_delay_use_mod_key, "debug.frame_delay.use_mod_key"},
10397 &setup.debug.frame_delay_game_only, "debug.frame_delay.game_only"
10401 &setup.debug.show_frames_per_second, "debug.show_frames_per_second"
10405 &setup.debug.xsn_mode, "debug.xsn_mode"
10409 &setup.debug.xsn_percent, "debug.xsn_percent"
10413 static struct TokenInfo options_setup_tokens[] =
10417 &setup.options.verbose, "options.verbose"
10421 &setup.options.debug, "options.debug"
10425 &setup.options.debug_mode, "options.debug_mode"
10429 static void setSetupInfoToDefaults(struct SetupInfo *si)
10433 si->player_name = getStringCopy(getDefaultUserName(user.nr));
10435 si->multiple_users = TRUE;
10438 si->sound_loops = TRUE;
10439 si->sound_music = TRUE;
10440 si->sound_simple = TRUE;
10442 si->global_animations = TRUE;
10443 si->scroll_delay = TRUE;
10444 si->forced_scroll_delay = FALSE;
10445 si->scroll_delay_value = STD_SCROLL_DELAY;
10446 si->engine_snapshot_mode = getStringCopy(STR_SNAPSHOT_MODE_DEFAULT);
10447 si->engine_snapshot_memory = SNAPSHOT_MEMORY_DEFAULT;
10448 si->fade_screens = TRUE;
10449 si->autorecord = TRUE;
10450 si->autorecord_after_replay = TRUE;
10451 si->auto_pause_on_start = FALSE;
10452 si->show_titlescreen = TRUE;
10453 si->quick_doors = FALSE;
10454 si->team_mode = FALSE;
10455 si->handicap = TRUE;
10456 si->skip_levels = TRUE;
10457 si->increment_levels = TRUE;
10458 si->auto_play_next_level = TRUE;
10459 si->count_score_after_game = TRUE;
10460 si->show_scores_after_game = TRUE;
10461 si->time_limit = TRUE;
10462 si->fullscreen = FALSE;
10463 si->window_scaling_percent = STD_WINDOW_SCALING_PERCENT;
10464 si->window_scaling_quality = getStringCopy(SCALING_QUALITY_DEFAULT);
10465 si->screen_rendering_mode = getStringCopy(STR_SPECIAL_RENDERING_DEFAULT);
10466 si->vsync_mode = getStringCopy(STR_VSYNC_MODE_DEFAULT);
10467 si->ask_on_escape = TRUE;
10468 si->ask_on_escape_editor = TRUE;
10469 si->ask_on_game_over = TRUE;
10470 si->ask_on_quit_game = TRUE;
10471 si->ask_on_quit_program = TRUE;
10472 si->quick_switch = FALSE;
10473 si->input_on_focus = FALSE;
10474 si->prefer_aga_graphics = TRUE;
10475 si->prefer_lowpass_sounds = FALSE;
10476 si->prefer_extra_panel_items = TRUE;
10477 si->game_speed_extended = FALSE;
10478 si->game_frame_delay = GAME_FRAME_DELAY;
10479 si->sp_show_border_elements = FALSE;
10480 si->small_game_graphics = FALSE;
10481 si->show_load_save_buttons = FALSE;
10482 si->show_undo_redo_buttons = FALSE;
10483 si->scores_in_highscore_list = getStringCopy(STR_SCORES_TYPE_DEFAULT);
10485 si->graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
10486 si->sounds_set = getStringCopy(SND_CLASSIC_SUBDIR);
10487 si->music_set = getStringCopy(MUS_CLASSIC_SUBDIR);
10489 si->override_level_graphics = FALSE;
10490 si->override_level_sounds = FALSE;
10491 si->override_level_music = FALSE;
10493 si->volume_simple = 100; // percent
10494 si->volume_loops = 100; // percent
10495 si->volume_music = 100; // percent
10497 si->network_mode = FALSE;
10498 si->network_player_nr = 0; // first player
10499 si->network_server_hostname = getStringCopy(STR_NETWORK_AUTO_DETECT);
10501 si->touch.control_type = getStringCopy(TOUCH_CONTROL_DEFAULT);
10502 si->touch.move_distance = TOUCH_MOVE_DISTANCE_DEFAULT; // percent
10503 si->touch.drop_distance = TOUCH_DROP_DISTANCE_DEFAULT; // percent
10504 si->touch.transparency = TOUCH_TRANSPARENCY_DEFAULT; // percent
10505 si->touch.draw_outlined = TRUE;
10506 si->touch.draw_pressed = TRUE;
10508 for (i = 0; i < 2; i++)
10510 char *default_grid_button[6][2] =
10516 { "111222", " vv " },
10517 { "111222", " vv " }
10519 int grid_xsize = DEFAULT_GRID_XSIZE(i);
10520 int grid_ysize = DEFAULT_GRID_YSIZE(i);
10521 int min_xsize = MIN(6, grid_xsize);
10522 int min_ysize = MIN(6, grid_ysize);
10523 int startx = grid_xsize - min_xsize;
10524 int starty = grid_ysize - min_ysize;
10527 // virtual buttons grid can only be set to defaults if video is initialized
10528 // (this will be repeated if virtual buttons are not loaded from setup file)
10529 if (video.initialized)
10531 si->touch.grid_xsize[i] = grid_xsize;
10532 si->touch.grid_ysize[i] = grid_ysize;
10536 si->touch.grid_xsize[i] = -1;
10537 si->touch.grid_ysize[i] = -1;
10540 for (x = 0; x < MAX_GRID_XSIZE; x++)
10541 for (y = 0; y < MAX_GRID_YSIZE; y++)
10542 si->touch.grid_button[i][x][y] = CHAR_GRID_BUTTON_NONE;
10544 for (x = 0; x < min_xsize; x++)
10545 for (y = 0; y < min_ysize; y++)
10546 si->touch.grid_button[i][x][starty + y] =
10547 default_grid_button[y][0][x];
10549 for (x = 0; x < min_xsize; x++)
10550 for (y = 0; y < min_ysize; y++)
10551 si->touch.grid_button[i][startx + x][starty + y] =
10552 default_grid_button[y][1][x];
10555 si->touch.grid_initialized = video.initialized;
10557 si->touch.overlay_buttons = FALSE;
10559 si->editor.el_boulderdash = TRUE;
10560 si->editor.el_emerald_mine = TRUE;
10561 si->editor.el_emerald_mine_club = TRUE;
10562 si->editor.el_more = TRUE;
10563 si->editor.el_sokoban = TRUE;
10564 si->editor.el_supaplex = TRUE;
10565 si->editor.el_diamond_caves = TRUE;
10566 si->editor.el_dx_boulderdash = TRUE;
10568 si->editor.el_mirror_magic = TRUE;
10569 si->editor.el_deflektor = TRUE;
10571 si->editor.el_chars = TRUE;
10572 si->editor.el_steel_chars = TRUE;
10574 si->editor.el_classic = TRUE;
10575 si->editor.el_custom = TRUE;
10577 si->editor.el_user_defined = FALSE;
10578 si->editor.el_dynamic = TRUE;
10580 si->editor.el_headlines = TRUE;
10582 si->editor.show_element_token = FALSE;
10584 si->editor.show_read_only_warning = TRUE;
10586 si->editor.use_template_for_new_levels = TRUE;
10588 si->shortcut.save_game = DEFAULT_KEY_SAVE_GAME;
10589 si->shortcut.load_game = DEFAULT_KEY_LOAD_GAME;
10590 si->shortcut.restart_game = DEFAULT_KEY_RESTART_GAME;
10591 si->shortcut.pause_before_end = DEFAULT_KEY_PAUSE_BEFORE_END;
10592 si->shortcut.toggle_pause = DEFAULT_KEY_TOGGLE_PAUSE;
10594 si->shortcut.focus_player[0] = DEFAULT_KEY_FOCUS_PLAYER_1;
10595 si->shortcut.focus_player[1] = DEFAULT_KEY_FOCUS_PLAYER_2;
10596 si->shortcut.focus_player[2] = DEFAULT_KEY_FOCUS_PLAYER_3;
10597 si->shortcut.focus_player[3] = DEFAULT_KEY_FOCUS_PLAYER_4;
10598 si->shortcut.focus_player_all = DEFAULT_KEY_FOCUS_PLAYER_ALL;
10600 si->shortcut.tape_eject = DEFAULT_KEY_TAPE_EJECT;
10601 si->shortcut.tape_extra = DEFAULT_KEY_TAPE_EXTRA;
10602 si->shortcut.tape_stop = DEFAULT_KEY_TAPE_STOP;
10603 si->shortcut.tape_pause = DEFAULT_KEY_TAPE_PAUSE;
10604 si->shortcut.tape_record = DEFAULT_KEY_TAPE_RECORD;
10605 si->shortcut.tape_play = DEFAULT_KEY_TAPE_PLAY;
10607 si->shortcut.sound_simple = DEFAULT_KEY_SOUND_SIMPLE;
10608 si->shortcut.sound_loops = DEFAULT_KEY_SOUND_LOOPS;
10609 si->shortcut.sound_music = DEFAULT_KEY_SOUND_MUSIC;
10611 si->shortcut.snap_left = DEFAULT_KEY_SNAP_LEFT;
10612 si->shortcut.snap_right = DEFAULT_KEY_SNAP_RIGHT;
10613 si->shortcut.snap_up = DEFAULT_KEY_SNAP_UP;
10614 si->shortcut.snap_down = DEFAULT_KEY_SNAP_DOWN;
10616 for (i = 0; i < MAX_PLAYERS; i++)
10618 si->input[i].use_joystick = FALSE;
10619 si->input[i].joy.device_name = getStringCopy(getDeviceNameFromJoystickNr(i));
10620 si->input[i].joy.xleft = JOYSTICK_XLEFT;
10621 si->input[i].joy.xmiddle = JOYSTICK_XMIDDLE;
10622 si->input[i].joy.xright = JOYSTICK_XRIGHT;
10623 si->input[i].joy.yupper = JOYSTICK_YUPPER;
10624 si->input[i].joy.ymiddle = JOYSTICK_YMIDDLE;
10625 si->input[i].joy.ylower = JOYSTICK_YLOWER;
10626 si->input[i].joy.snap = (i == 0 ? JOY_BUTTON_1 : 0);
10627 si->input[i].joy.drop = (i == 0 ? JOY_BUTTON_2 : 0);
10628 si->input[i].key.left = (i == 0 ? DEFAULT_KEY_LEFT : KSYM_UNDEFINED);
10629 si->input[i].key.right = (i == 0 ? DEFAULT_KEY_RIGHT : KSYM_UNDEFINED);
10630 si->input[i].key.up = (i == 0 ? DEFAULT_KEY_UP : KSYM_UNDEFINED);
10631 si->input[i].key.down = (i == 0 ? DEFAULT_KEY_DOWN : KSYM_UNDEFINED);
10632 si->input[i].key.snap = (i == 0 ? DEFAULT_KEY_SNAP : KSYM_UNDEFINED);
10633 si->input[i].key.drop = (i == 0 ? DEFAULT_KEY_DROP : KSYM_UNDEFINED);
10636 si->system.sdl_renderdriver = getStringCopy(ARG_DEFAULT);
10637 si->system.sdl_videodriver = getStringCopy(ARG_DEFAULT);
10638 si->system.sdl_audiodriver = getStringCopy(ARG_DEFAULT);
10639 si->system.audio_fragment_size = DEFAULT_AUDIO_FRAGMENT_SIZE;
10641 si->internal.program_title = getStringCopy(PROGRAM_TITLE_STRING);
10642 si->internal.program_version = getStringCopy(getProgramRealVersionString());
10643 si->internal.program_author = getStringCopy(PROGRAM_AUTHOR_STRING);
10644 si->internal.program_email = getStringCopy(PROGRAM_EMAIL_STRING);
10645 si->internal.program_website = getStringCopy(PROGRAM_WEBSITE_STRING);
10646 si->internal.program_copyright = getStringCopy(PROGRAM_COPYRIGHT_STRING);
10647 si->internal.program_company = getStringCopy(PROGRAM_COMPANY_STRING);
10649 si->internal.program_icon_file = getStringCopy(PROGRAM_ICON_FILENAME);
10651 si->internal.default_graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
10652 si->internal.default_sounds_set = getStringCopy(SND_CLASSIC_SUBDIR);
10653 si->internal.default_music_set = getStringCopy(MUS_CLASSIC_SUBDIR);
10655 si->internal.fallback_graphics_file = getStringCopy(UNDEFINED_FILENAME);
10656 si->internal.fallback_sounds_file = getStringCopy(UNDEFINED_FILENAME);
10657 si->internal.fallback_music_file = getStringCopy(UNDEFINED_FILENAME);
10659 si->internal.default_level_series = getStringCopy(UNDEFINED_LEVELSET);
10660 si->internal.choose_from_top_leveldir = FALSE;
10661 si->internal.show_scaling_in_title = TRUE;
10662 si->internal.create_user_levelset = TRUE;
10663 si->internal.info_screens_from_main = FALSE;
10665 si->internal.default_window_width = WIN_XSIZE_DEFAULT;
10666 si->internal.default_window_height = WIN_YSIZE_DEFAULT;
10668 si->debug.frame_delay[0] = DEFAULT_FRAME_DELAY_0;
10669 si->debug.frame_delay[1] = DEFAULT_FRAME_DELAY_1;
10670 si->debug.frame_delay[2] = DEFAULT_FRAME_DELAY_2;
10671 si->debug.frame_delay[3] = DEFAULT_FRAME_DELAY_3;
10672 si->debug.frame_delay[4] = DEFAULT_FRAME_DELAY_4;
10673 si->debug.frame_delay[5] = DEFAULT_FRAME_DELAY_5;
10674 si->debug.frame_delay[6] = DEFAULT_FRAME_DELAY_6;
10675 si->debug.frame_delay[7] = DEFAULT_FRAME_DELAY_7;
10676 si->debug.frame_delay[8] = DEFAULT_FRAME_DELAY_8;
10677 si->debug.frame_delay[9] = DEFAULT_FRAME_DELAY_9;
10679 si->debug.frame_delay_key[0] = DEFAULT_KEY_FRAME_DELAY_0;
10680 si->debug.frame_delay_key[1] = DEFAULT_KEY_FRAME_DELAY_1;
10681 si->debug.frame_delay_key[2] = DEFAULT_KEY_FRAME_DELAY_2;
10682 si->debug.frame_delay_key[3] = DEFAULT_KEY_FRAME_DELAY_3;
10683 si->debug.frame_delay_key[4] = DEFAULT_KEY_FRAME_DELAY_4;
10684 si->debug.frame_delay_key[5] = DEFAULT_KEY_FRAME_DELAY_5;
10685 si->debug.frame_delay_key[6] = DEFAULT_KEY_FRAME_DELAY_6;
10686 si->debug.frame_delay_key[7] = DEFAULT_KEY_FRAME_DELAY_7;
10687 si->debug.frame_delay_key[8] = DEFAULT_KEY_FRAME_DELAY_8;
10688 si->debug.frame_delay_key[9] = DEFAULT_KEY_FRAME_DELAY_9;
10690 si->debug.frame_delay_use_mod_key = DEFAULT_FRAME_DELAY_USE_MOD_KEY;
10691 si->debug.frame_delay_game_only = DEFAULT_FRAME_DELAY_GAME_ONLY;
10693 si->debug.show_frames_per_second = FALSE;
10695 si->debug.xsn_mode = AUTO;
10696 si->debug.xsn_percent = 0;
10698 si->options.verbose = FALSE;
10699 si->options.debug = FALSE;
10700 si->options.debug_mode = getStringCopy(ARG_UNDEFINED_STRING);
10702 #if defined(PLATFORM_ANDROID)
10703 si->fullscreen = TRUE;
10704 si->touch.overlay_buttons = TRUE;
10707 setHideSetupEntry(&setup.debug.xsn_mode);
10710 static void setSetupInfoToDefaults_AutoSetup(struct SetupInfo *si)
10712 si->auto_setup.editor_zoom_tilesize = MINI_TILESIZE;
10715 static void setSetupInfoToDefaults_ServerSetup(struct SetupInfo *si)
10717 si->player_uuid = NULL; // (will be set later)
10718 si->player_version = 1; // (will be set later)
10720 si->use_api_server = TRUE;
10721 si->api_server_hostname = getStringCopy(API_SERVER_HOSTNAME);
10722 si->api_server_password = getStringCopy(UNDEFINED_PASSWORD);
10723 si->ask_for_uploading_tapes = TRUE;
10724 si->ask_for_remaining_tapes = FALSE;
10725 si->provide_uploading_tapes = TRUE;
10726 si->ask_for_using_api_server = TRUE;
10727 si->has_remaining_tapes = FALSE;
10730 static void setSetupInfoToDefaults_EditorCascade(struct SetupInfo *si)
10732 si->editor_cascade.el_bd = TRUE;
10733 si->editor_cascade.el_em = TRUE;
10734 si->editor_cascade.el_emc = TRUE;
10735 si->editor_cascade.el_rnd = TRUE;
10736 si->editor_cascade.el_sb = TRUE;
10737 si->editor_cascade.el_sp = TRUE;
10738 si->editor_cascade.el_dc = TRUE;
10739 si->editor_cascade.el_dx = TRUE;
10741 si->editor_cascade.el_mm = TRUE;
10742 si->editor_cascade.el_df = TRUE;
10744 si->editor_cascade.el_chars = FALSE;
10745 si->editor_cascade.el_steel_chars = FALSE;
10746 si->editor_cascade.el_ce = FALSE;
10747 si->editor_cascade.el_ge = FALSE;
10748 si->editor_cascade.el_es = FALSE;
10749 si->editor_cascade.el_ref = FALSE;
10750 si->editor_cascade.el_user = FALSE;
10751 si->editor_cascade.el_dynamic = FALSE;
10754 #define MAX_HIDE_SETUP_TOKEN_SIZE 20
10756 static char *getHideSetupToken(void *setup_value)
10758 static char hide_setup_token[MAX_HIDE_SETUP_TOKEN_SIZE];
10760 if (setup_value != NULL)
10761 snprintf(hide_setup_token, MAX_HIDE_SETUP_TOKEN_SIZE, "%p", setup_value);
10763 return hide_setup_token;
10766 void setHideSetupEntry(void *setup_value)
10768 char *hide_setup_token = getHideSetupToken(setup_value);
10770 if (hide_setup_hash == NULL)
10771 hide_setup_hash = newSetupFileHash();
10773 if (setup_value != NULL)
10774 setHashEntry(hide_setup_hash, hide_setup_token, "");
10777 void removeHideSetupEntry(void *setup_value)
10779 char *hide_setup_token = getHideSetupToken(setup_value);
10781 if (setup_value != NULL)
10782 removeHashEntry(hide_setup_hash, hide_setup_token);
10785 boolean hideSetupEntry(void *setup_value)
10787 char *hide_setup_token = getHideSetupToken(setup_value);
10789 return (setup_value != NULL &&
10790 getHashEntry(hide_setup_hash, hide_setup_token) != NULL);
10793 static void setSetupInfoFromTokenText(SetupFileHash *setup_file_hash,
10794 struct TokenInfo *token_info,
10795 int token_nr, char *token_text)
10797 char *token_hide_text = getStringCat2(token_text, ".hide");
10798 char *token_hide_value = getHashEntry(setup_file_hash, token_hide_text);
10800 // set the value of this setup option in the setup option structure
10801 setSetupInfo(token_info, token_nr, getHashEntry(setup_file_hash, token_text));
10803 // check if this setup option should be hidden in the setup menu
10804 if (token_hide_value != NULL && get_boolean_from_string(token_hide_value))
10805 setHideSetupEntry(token_info[token_nr].value);
10807 free(token_hide_text);
10810 static void setSetupInfoFromTokenInfo(SetupFileHash *setup_file_hash,
10811 struct TokenInfo *token_info,
10814 setSetupInfoFromTokenText(setup_file_hash, token_info, token_nr,
10815 token_info[token_nr].text);
10818 static void decodeSetupFileHash_Default(SetupFileHash *setup_file_hash)
10822 if (!setup_file_hash)
10825 for (i = 0; i < ARRAY_SIZE(global_setup_tokens); i++)
10826 setSetupInfoFromTokenInfo(setup_file_hash, global_setup_tokens, i);
10828 setup.touch.grid_initialized = TRUE;
10829 for (i = 0; i < 2; i++)
10831 int grid_xsize = setup.touch.grid_xsize[i];
10832 int grid_ysize = setup.touch.grid_ysize[i];
10835 // if virtual buttons are not loaded from setup file, repeat initializing
10836 // virtual buttons grid with default values later when video is initialized
10837 if (grid_xsize == -1 ||
10840 setup.touch.grid_initialized = FALSE;
10845 for (y = 0; y < grid_ysize; y++)
10847 char token_string[MAX_LINE_LEN];
10849 sprintf(token_string, "touch.virtual_buttons.%d.%02d", i, y);
10851 char *value_string = getHashEntry(setup_file_hash, token_string);
10853 if (value_string == NULL)
10856 for (x = 0; x < grid_xsize; x++)
10858 char c = value_string[x];
10860 setup.touch.grid_button[i][x][y] =
10861 (c == '.' ? CHAR_GRID_BUTTON_NONE : c);
10866 for (i = 0; i < ARRAY_SIZE(editor_setup_tokens); i++)
10867 setSetupInfoFromTokenInfo(setup_file_hash, editor_setup_tokens, i);
10869 for (i = 0; i < ARRAY_SIZE(shortcut_setup_tokens); i++)
10870 setSetupInfoFromTokenInfo(setup_file_hash, shortcut_setup_tokens, i);
10872 for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
10876 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
10878 setup_input = setup.input[pnr];
10879 for (i = 0; i < ARRAY_SIZE(player_setup_tokens); i++)
10881 char full_token[100];
10883 sprintf(full_token, "%s%s", prefix, player_setup_tokens[i].text);
10884 setSetupInfoFromTokenText(setup_file_hash, player_setup_tokens, i,
10887 setup.input[pnr] = setup_input;
10890 for (i = 0; i < ARRAY_SIZE(system_setup_tokens); i++)
10891 setSetupInfoFromTokenInfo(setup_file_hash, system_setup_tokens, i);
10893 for (i = 0; i < ARRAY_SIZE(internal_setup_tokens); i++)
10894 setSetupInfoFromTokenInfo(setup_file_hash, internal_setup_tokens, i);
10896 for (i = 0; i < ARRAY_SIZE(debug_setup_tokens); i++)
10897 setSetupInfoFromTokenInfo(setup_file_hash, debug_setup_tokens, i);
10899 for (i = 0; i < ARRAY_SIZE(options_setup_tokens); i++)
10900 setSetupInfoFromTokenInfo(setup_file_hash, options_setup_tokens, i);
10902 setHideRelatedSetupEntries();
10905 static void decodeSetupFileHash_AutoSetup(SetupFileHash *setup_file_hash)
10909 if (!setup_file_hash)
10912 for (i = 0; i < ARRAY_SIZE(auto_setup_tokens); i++)
10913 setSetupInfo(auto_setup_tokens, i,
10914 getHashEntry(setup_file_hash,
10915 auto_setup_tokens[i].text));
10918 static void decodeSetupFileHash_ServerSetup(SetupFileHash *setup_file_hash)
10922 if (!setup_file_hash)
10925 for (i = 0; i < ARRAY_SIZE(server_setup_tokens); i++)
10926 setSetupInfo(server_setup_tokens, i,
10927 getHashEntry(setup_file_hash,
10928 server_setup_tokens[i].text));
10931 static void decodeSetupFileHash_EditorCascade(SetupFileHash *setup_file_hash)
10935 if (!setup_file_hash)
10938 for (i = 0; i < ARRAY_SIZE(editor_cascade_setup_tokens); i++)
10939 setSetupInfo(editor_cascade_setup_tokens, i,
10940 getHashEntry(setup_file_hash,
10941 editor_cascade_setup_tokens[i].text));
10944 void LoadUserNames(void)
10946 int last_user_nr = user.nr;
10949 if (global.user_names != NULL)
10951 for (i = 0; i < MAX_PLAYER_NAMES; i++)
10952 checked_free(global.user_names[i]);
10954 checked_free(global.user_names);
10957 global.user_names = checked_calloc(MAX_PLAYER_NAMES * sizeof(char *));
10959 for (i = 0; i < MAX_PLAYER_NAMES; i++)
10963 SetupFileHash *setup_file_hash = loadSetupFileHash(getSetupFilename());
10965 if (setup_file_hash)
10967 char *player_name = getHashEntry(setup_file_hash, "player_name");
10969 global.user_names[i] = getFixedUserName(player_name);
10971 freeSetupFileHash(setup_file_hash);
10974 if (global.user_names[i] == NULL)
10975 global.user_names[i] = getStringCopy(getDefaultUserName(i));
10978 user.nr = last_user_nr;
10981 void LoadSetupFromFilename(char *filename)
10983 SetupFileHash *setup_file_hash = loadSetupFileHash(filename);
10985 if (setup_file_hash)
10987 decodeSetupFileHash_Default(setup_file_hash);
10989 freeSetupFileHash(setup_file_hash);
10993 Debug("setup", "using default setup values");
10997 static void LoadSetup_SpecialPostProcessing(void)
10999 char *player_name_new;
11001 // needed to work around problems with fixed length strings
11002 player_name_new = getFixedUserName(setup.player_name);
11003 free(setup.player_name);
11004 setup.player_name = player_name_new;
11006 // "scroll_delay: on(3) / off(0)" was replaced by scroll delay value
11007 if (setup.scroll_delay == FALSE)
11009 setup.scroll_delay_value = MIN_SCROLL_DELAY;
11010 setup.scroll_delay = TRUE; // now always "on"
11013 // make sure that scroll delay value stays inside valid range
11014 setup.scroll_delay_value =
11015 MIN(MAX(MIN_SCROLL_DELAY, setup.scroll_delay_value), MAX_SCROLL_DELAY);
11018 void LoadSetup_Default(void)
11022 // always start with reliable default values
11023 setSetupInfoToDefaults(&setup);
11025 // try to load setup values from default setup file
11026 filename = getDefaultSetupFilename();
11028 if (fileExists(filename))
11029 LoadSetupFromFilename(filename);
11031 // try to load setup values from platform setup file
11032 filename = getPlatformSetupFilename();
11034 if (fileExists(filename))
11035 LoadSetupFromFilename(filename);
11037 // try to load setup values from user setup file
11038 filename = getSetupFilename();
11040 LoadSetupFromFilename(filename);
11042 LoadSetup_SpecialPostProcessing();
11045 void LoadSetup_AutoSetup(void)
11047 char *filename = getPath2(getSetupDir(), AUTOSETUP_FILENAME);
11048 SetupFileHash *setup_file_hash = NULL;
11050 // always start with reliable default values
11051 setSetupInfoToDefaults_AutoSetup(&setup);
11053 setup_file_hash = loadSetupFileHash(filename);
11055 if (setup_file_hash)
11057 decodeSetupFileHash_AutoSetup(setup_file_hash);
11059 freeSetupFileHash(setup_file_hash);
11065 void LoadSetup_ServerSetup(void)
11067 char *filename = getPath2(getSetupDir(), SERVERSETUP_FILENAME);
11068 SetupFileHash *setup_file_hash = NULL;
11070 // always start with reliable default values
11071 setSetupInfoToDefaults_ServerSetup(&setup);
11073 setup_file_hash = loadSetupFileHash(filename);
11075 if (setup_file_hash)
11077 decodeSetupFileHash_ServerSetup(setup_file_hash);
11079 freeSetupFileHash(setup_file_hash);
11084 if (setup.player_uuid == NULL)
11086 // player UUID does not yet exist in setup file
11087 setup.player_uuid = getStringCopy(getUUID());
11088 setup.player_version = 2;
11090 SaveSetup_ServerSetup();
11094 void LoadSetup_EditorCascade(void)
11096 char *filename = getPath2(getSetupDir(), EDITORCASCADE_FILENAME);
11097 SetupFileHash *setup_file_hash = NULL;
11099 // always start with reliable default values
11100 setSetupInfoToDefaults_EditorCascade(&setup);
11102 setup_file_hash = loadSetupFileHash(filename);
11104 if (setup_file_hash)
11106 decodeSetupFileHash_EditorCascade(setup_file_hash);
11108 freeSetupFileHash(setup_file_hash);
11114 void LoadSetup(void)
11116 LoadSetup_Default();
11117 LoadSetup_AutoSetup();
11118 LoadSetup_ServerSetup();
11119 LoadSetup_EditorCascade();
11122 static void addGameControllerMappingToHash(SetupFileHash *mappings_hash,
11123 char *mapping_line)
11125 char mapping_guid[MAX_LINE_LEN];
11126 char *mapping_start, *mapping_end;
11128 // get GUID from game controller mapping line: copy complete line
11129 strncpy(mapping_guid, mapping_line, MAX_LINE_LEN - 1);
11130 mapping_guid[MAX_LINE_LEN - 1] = '\0';
11132 // get GUID from game controller mapping line: cut after GUID part
11133 mapping_start = strchr(mapping_guid, ',');
11134 if (mapping_start != NULL)
11135 *mapping_start = '\0';
11137 // cut newline from game controller mapping line
11138 mapping_end = strchr(mapping_line, '\n');
11139 if (mapping_end != NULL)
11140 *mapping_end = '\0';
11142 // add mapping entry to game controller mappings hash
11143 setHashEntry(mappings_hash, mapping_guid, mapping_line);
11146 static void LoadSetup_ReadGameControllerMappings(SetupFileHash *mappings_hash,
11151 if (!(file = fopen(filename, MODE_READ)))
11153 Warn("cannot read game controller mappings file '%s'", filename);
11158 while (!feof(file))
11160 char line[MAX_LINE_LEN];
11162 if (!fgets(line, MAX_LINE_LEN, file))
11165 addGameControllerMappingToHash(mappings_hash, line);
11171 void SaveSetup_Default(void)
11173 char *filename = getSetupFilename();
11177 InitUserDataDirectory();
11179 if (!(file = fopen(filename, MODE_WRITE)))
11181 Warn("cannot write setup file '%s'", filename);
11186 fprintFileHeader(file, SETUP_FILENAME);
11188 for (i = 0; i < ARRAY_SIZE(global_setup_tokens); i++)
11190 // just to make things nicer :)
11191 if (global_setup_tokens[i].value == &setup.multiple_users ||
11192 global_setup_tokens[i].value == &setup.sound ||
11193 global_setup_tokens[i].value == &setup.graphics_set ||
11194 global_setup_tokens[i].value == &setup.volume_simple ||
11195 global_setup_tokens[i].value == &setup.network_mode ||
11196 global_setup_tokens[i].value == &setup.touch.control_type ||
11197 global_setup_tokens[i].value == &setup.touch.grid_xsize[0] ||
11198 global_setup_tokens[i].value == &setup.touch.grid_xsize[1])
11199 fprintf(file, "\n");
11201 fprintf(file, "%s\n", getSetupLine(global_setup_tokens, "", i));
11204 for (i = 0; i < 2; i++)
11206 int grid_xsize = setup.touch.grid_xsize[i];
11207 int grid_ysize = setup.touch.grid_ysize[i];
11210 fprintf(file, "\n");
11212 for (y = 0; y < grid_ysize; y++)
11214 char token_string[MAX_LINE_LEN];
11215 char value_string[MAX_LINE_LEN];
11217 sprintf(token_string, "touch.virtual_buttons.%d.%02d", i, y);
11219 for (x = 0; x < grid_xsize; x++)
11221 char c = setup.touch.grid_button[i][x][y];
11223 value_string[x] = (c == CHAR_GRID_BUTTON_NONE ? '.' : c);
11226 value_string[grid_xsize] = '\0';
11228 fprintf(file, "%s\n", getFormattedSetupEntry(token_string, value_string));
11232 fprintf(file, "\n");
11233 for (i = 0; i < ARRAY_SIZE(editor_setup_tokens); i++)
11234 fprintf(file, "%s\n", getSetupLine(editor_setup_tokens, "", i));
11236 fprintf(file, "\n");
11237 for (i = 0; i < ARRAY_SIZE(shortcut_setup_tokens); i++)
11238 fprintf(file, "%s\n", getSetupLine(shortcut_setup_tokens, "", i));
11240 for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
11244 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
11245 fprintf(file, "\n");
11247 setup_input = setup.input[pnr];
11248 for (i = 0; i < ARRAY_SIZE(player_setup_tokens); i++)
11249 fprintf(file, "%s\n", getSetupLine(player_setup_tokens, prefix, i));
11252 fprintf(file, "\n");
11253 for (i = 0; i < ARRAY_SIZE(system_setup_tokens); i++)
11254 fprintf(file, "%s\n", getSetupLine(system_setup_tokens, "", i));
11256 // (internal setup values not saved to user setup file)
11258 fprintf(file, "\n");
11259 for (i = 0; i < ARRAY_SIZE(debug_setup_tokens); i++)
11260 if (!strPrefix(debug_setup_tokens[i].text, "debug.xsn_") ||
11261 setup.debug.xsn_mode != AUTO)
11262 fprintf(file, "%s\n", getSetupLine(debug_setup_tokens, "", i));
11264 fprintf(file, "\n");
11265 for (i = 0; i < ARRAY_SIZE(options_setup_tokens); i++)
11266 fprintf(file, "%s\n", getSetupLine(options_setup_tokens, "", i));
11270 SetFilePermissions(filename, PERMS_PRIVATE);
11273 void SaveSetup_AutoSetup(void)
11275 char *filename = getPath2(getSetupDir(), AUTOSETUP_FILENAME);
11279 InitUserDataDirectory();
11281 if (!(file = fopen(filename, MODE_WRITE)))
11283 Warn("cannot write auto setup file '%s'", filename);
11290 fprintFileHeader(file, AUTOSETUP_FILENAME);
11292 for (i = 0; i < ARRAY_SIZE(auto_setup_tokens); i++)
11293 fprintf(file, "%s\n", getSetupLine(auto_setup_tokens, "", i));
11297 SetFilePermissions(filename, PERMS_PRIVATE);
11302 void SaveSetup_ServerSetup(void)
11304 char *filename = getPath2(getSetupDir(), SERVERSETUP_FILENAME);
11308 InitUserDataDirectory();
11310 if (!(file = fopen(filename, MODE_WRITE)))
11312 Warn("cannot write server setup file '%s'", filename);
11319 fprintFileHeader(file, SERVERSETUP_FILENAME);
11321 for (i = 0; i < ARRAY_SIZE(server_setup_tokens); i++)
11323 // just to make things nicer :)
11324 if (server_setup_tokens[i].value == &setup.use_api_server)
11325 fprintf(file, "\n");
11327 fprintf(file, "%s\n", getSetupLine(server_setup_tokens, "", i));
11332 SetFilePermissions(filename, PERMS_PRIVATE);
11337 void SaveSetup_EditorCascade(void)
11339 char *filename = getPath2(getSetupDir(), EDITORCASCADE_FILENAME);
11343 InitUserDataDirectory();
11345 if (!(file = fopen(filename, MODE_WRITE)))
11347 Warn("cannot write editor cascade state file '%s'", filename);
11354 fprintFileHeader(file, EDITORCASCADE_FILENAME);
11356 for (i = 0; i < ARRAY_SIZE(editor_cascade_setup_tokens); i++)
11357 fprintf(file, "%s\n", getSetupLine(editor_cascade_setup_tokens, "", i));
11361 SetFilePermissions(filename, PERMS_PRIVATE);
11366 void SaveSetup(void)
11368 SaveSetup_Default();
11369 SaveSetup_AutoSetup();
11370 SaveSetup_ServerSetup();
11371 SaveSetup_EditorCascade();
11374 static void SaveSetup_WriteGameControllerMappings(SetupFileHash *mappings_hash,
11379 if (!(file = fopen(filename, MODE_WRITE)))
11381 Warn("cannot write game controller mappings file '%s'", filename);
11386 BEGIN_HASH_ITERATION(mappings_hash, itr)
11388 fprintf(file, "%s\n", HASH_ITERATION_VALUE(itr));
11390 END_HASH_ITERATION(mappings_hash, itr)
11395 void SaveSetup_AddGameControllerMapping(char *mapping)
11397 char *filename = getPath2(getSetupDir(), GAMECONTROLLER_BASENAME);
11398 SetupFileHash *mappings_hash = newSetupFileHash();
11400 InitUserDataDirectory();
11402 // load existing personal game controller mappings
11403 LoadSetup_ReadGameControllerMappings(mappings_hash, filename);
11405 // add new mapping to personal game controller mappings
11406 addGameControllerMappingToHash(mappings_hash, mapping);
11408 // save updated personal game controller mappings
11409 SaveSetup_WriteGameControllerMappings(mappings_hash, filename);
11411 freeSetupFileHash(mappings_hash);
11415 void LoadCustomElementDescriptions(void)
11417 char *filename = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
11418 SetupFileHash *setup_file_hash;
11421 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
11423 if (element_info[i].custom_description != NULL)
11425 free(element_info[i].custom_description);
11426 element_info[i].custom_description = NULL;
11430 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
11433 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
11435 char *token = getStringCat2(element_info[i].token_name, ".name");
11436 char *value = getHashEntry(setup_file_hash, token);
11439 element_info[i].custom_description = getStringCopy(value);
11444 freeSetupFileHash(setup_file_hash);
11447 static int getElementFromToken(char *token)
11449 char *value = getHashEntry(element_token_hash, token);
11452 return atoi(value);
11454 Warn("unknown element token '%s'", token);
11456 return EL_UNDEFINED;
11459 void FreeGlobalAnimEventInfo(void)
11461 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
11463 if (gaei->event_list == NULL)
11468 for (i = 0; i < gaei->num_event_lists; i++)
11470 checked_free(gaei->event_list[i]->event_value);
11471 checked_free(gaei->event_list[i]);
11474 checked_free(gaei->event_list);
11476 gaei->event_list = NULL;
11477 gaei->num_event_lists = 0;
11480 static int AddGlobalAnimEventList(void)
11482 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
11483 int list_pos = gaei->num_event_lists++;
11485 gaei->event_list = checked_realloc(gaei->event_list, gaei->num_event_lists *
11486 sizeof(struct GlobalAnimEventListInfo *));
11488 gaei->event_list[list_pos] =
11489 checked_calloc(sizeof(struct GlobalAnimEventListInfo));
11491 struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
11493 gaeli->event_value = NULL;
11494 gaeli->num_event_values = 0;
11499 static int AddGlobalAnimEventValue(int list_pos, int event_value)
11501 // do not add empty global animation events
11502 if (event_value == ANIM_EVENT_NONE)
11505 // if list position is undefined, create new list
11506 if (list_pos == ANIM_EVENT_UNDEFINED)
11507 list_pos = AddGlobalAnimEventList();
11509 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
11510 struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
11511 int value_pos = gaeli->num_event_values++;
11513 gaeli->event_value = checked_realloc(gaeli->event_value,
11514 gaeli->num_event_values * sizeof(int *));
11516 gaeli->event_value[value_pos] = event_value;
11521 int GetGlobalAnimEventValue(int list_pos, int value_pos)
11523 if (list_pos == ANIM_EVENT_UNDEFINED)
11524 return ANIM_EVENT_NONE;
11526 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
11527 struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
11529 return gaeli->event_value[value_pos];
11532 int GetGlobalAnimEventValueCount(int list_pos)
11534 if (list_pos == ANIM_EVENT_UNDEFINED)
11537 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
11538 struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
11540 return gaeli->num_event_values;
11543 // This function checks if a string <s> of the format "string1, string2, ..."
11544 // exactly contains a string <s_contained>.
11546 static boolean string_has_parameter(char *s, char *s_contained)
11550 if (s == NULL || s_contained == NULL)
11553 if (strlen(s_contained) > strlen(s))
11556 if (strncmp(s, s_contained, strlen(s_contained)) == 0)
11558 char next_char = s[strlen(s_contained)];
11560 // check if next character is delimiter or whitespace
11561 if (next_char == ',' || next_char == '\0' ||
11562 next_char == ' ' || next_char == '\t')
11566 // check if string contains another parameter string after a comma
11567 substring = strchr(s, ',');
11568 if (substring == NULL) // string does not contain a comma
11571 // advance string pointer to next character after the comma
11574 // skip potential whitespaces after the comma
11575 while (*substring == ' ' || *substring == '\t')
11578 return string_has_parameter(substring, s_contained);
11581 static int get_anim_parameter_value_ce(char *s)
11584 char *pattern_1 = "ce_change:custom_";
11585 char *pattern_2 = ".page_";
11586 int pattern_1_len = strlen(pattern_1);
11587 char *matching_char = strstr(s_ptr, pattern_1);
11588 int result = ANIM_EVENT_NONE;
11590 if (matching_char == NULL)
11591 return ANIM_EVENT_NONE;
11593 result = ANIM_EVENT_CE_CHANGE;
11595 s_ptr = matching_char + pattern_1_len;
11597 // check for custom element number ("custom_X", "custom_XX" or "custom_XXX")
11598 if (*s_ptr >= '0' && *s_ptr <= '9')
11600 int gic_ce_nr = (*s_ptr++ - '0');
11602 if (*s_ptr >= '0' && *s_ptr <= '9')
11604 gic_ce_nr = 10 * gic_ce_nr + (*s_ptr++ - '0');
11606 if (*s_ptr >= '0' && *s_ptr <= '9')
11607 gic_ce_nr = 10 * gic_ce_nr + (*s_ptr++ - '0');
11610 if (gic_ce_nr < 1 || gic_ce_nr > NUM_CUSTOM_ELEMENTS)
11611 return ANIM_EVENT_NONE;
11613 // custom element stored as 0 to 255
11616 result |= gic_ce_nr << ANIM_EVENT_CE_BIT;
11620 // invalid custom element number specified
11622 return ANIM_EVENT_NONE;
11625 // check for change page number ("page_X" or "page_XX") (optional)
11626 if (strPrefix(s_ptr, pattern_2))
11628 s_ptr += strlen(pattern_2);
11630 if (*s_ptr >= '0' && *s_ptr <= '9')
11632 int gic_page_nr = (*s_ptr++ - '0');
11634 if (*s_ptr >= '0' && *s_ptr <= '9')
11635 gic_page_nr = 10 * gic_page_nr + (*s_ptr++ - '0');
11637 if (gic_page_nr < 1 || gic_page_nr > MAX_CHANGE_PAGES)
11638 return ANIM_EVENT_NONE;
11640 // change page stored as 1 to 32 (0 means "all change pages")
11642 result |= gic_page_nr << ANIM_EVENT_PAGE_BIT;
11646 // invalid animation part number specified
11648 return ANIM_EVENT_NONE;
11652 // discard result if next character is neither delimiter nor whitespace
11653 if (!(*s_ptr == ',' || *s_ptr == '\0' ||
11654 *s_ptr == ' ' || *s_ptr == '\t'))
11655 return ANIM_EVENT_NONE;
11660 static int get_anim_parameter_value(char *s)
11662 int event_value[] =
11670 char *pattern_1[] =
11678 char *pattern_2 = ".part_";
11679 char *matching_char = NULL;
11681 int pattern_1_len = 0;
11682 int result = ANIM_EVENT_NONE;
11685 result = get_anim_parameter_value_ce(s);
11687 if (result != ANIM_EVENT_NONE)
11690 for (i = 0; i < ARRAY_SIZE(event_value); i++)
11692 matching_char = strstr(s_ptr, pattern_1[i]);
11693 pattern_1_len = strlen(pattern_1[i]);
11694 result = event_value[i];
11696 if (matching_char != NULL)
11700 if (matching_char == NULL)
11701 return ANIM_EVENT_NONE;
11703 s_ptr = matching_char + pattern_1_len;
11705 // check for main animation number ("anim_X" or "anim_XX")
11706 if (*s_ptr >= '0' && *s_ptr <= '9')
11708 int gic_anim_nr = (*s_ptr++ - '0');
11710 if (*s_ptr >= '0' && *s_ptr <= '9')
11711 gic_anim_nr = 10 * gic_anim_nr + (*s_ptr++ - '0');
11713 if (gic_anim_nr < 1 || gic_anim_nr > MAX_GLOBAL_ANIMS)
11714 return ANIM_EVENT_NONE;
11716 result |= gic_anim_nr << ANIM_EVENT_ANIM_BIT;
11720 // invalid main animation number specified
11722 return ANIM_EVENT_NONE;
11725 // check for animation part number ("part_X" or "part_XX") (optional)
11726 if (strPrefix(s_ptr, pattern_2))
11728 s_ptr += strlen(pattern_2);
11730 if (*s_ptr >= '0' && *s_ptr <= '9')
11732 int gic_part_nr = (*s_ptr++ - '0');
11734 if (*s_ptr >= '0' && *s_ptr <= '9')
11735 gic_part_nr = 10 * gic_part_nr + (*s_ptr++ - '0');
11737 if (gic_part_nr < 1 || gic_part_nr > MAX_GLOBAL_ANIM_PARTS)
11738 return ANIM_EVENT_NONE;
11740 result |= gic_part_nr << ANIM_EVENT_PART_BIT;
11744 // invalid animation part number specified
11746 return ANIM_EVENT_NONE;
11750 // discard result if next character is neither delimiter nor whitespace
11751 if (!(*s_ptr == ',' || *s_ptr == '\0' ||
11752 *s_ptr == ' ' || *s_ptr == '\t'))
11753 return ANIM_EVENT_NONE;
11758 static int get_anim_parameter_values(char *s)
11760 int list_pos = ANIM_EVENT_UNDEFINED;
11761 int event_value = ANIM_EVENT_DEFAULT;
11763 if (string_has_parameter(s, "any"))
11764 event_value |= ANIM_EVENT_ANY;
11766 if (string_has_parameter(s, "click:self") ||
11767 string_has_parameter(s, "click") ||
11768 string_has_parameter(s, "self"))
11769 event_value |= ANIM_EVENT_SELF;
11771 if (string_has_parameter(s, "unclick:any"))
11772 event_value |= ANIM_EVENT_UNCLICK_ANY;
11774 // if animation event found, add it to global animation event list
11775 if (event_value != ANIM_EVENT_NONE)
11776 list_pos = AddGlobalAnimEventValue(list_pos, event_value);
11780 // add optional "click:anim_X" or "click:anim_X.part_X" parameter
11781 event_value = get_anim_parameter_value(s);
11783 // if animation event found, add it to global animation event list
11784 if (event_value != ANIM_EVENT_NONE)
11785 list_pos = AddGlobalAnimEventValue(list_pos, event_value);
11787 // continue with next part of the string, starting with next comma
11788 s = strchr(s + 1, ',');
11794 static int get_anim_action_parameter_value(char *token)
11796 // check most common default case first to massively speed things up
11797 if (strEqual(token, ARG_UNDEFINED))
11798 return ANIM_EVENT_ACTION_NONE;
11800 int result = getImageIDFromToken(token);
11804 char *gfx_token = getStringCat2("gfx.", token);
11806 result = getImageIDFromToken(gfx_token);
11808 checked_free(gfx_token);
11813 Key key = getKeyFromX11KeyName(token);
11815 if (key != KSYM_UNDEFINED)
11816 result = -(int)key;
11823 result = get_hash_from_key(token); // unsigned int => int
11824 result = ABS(result); // may be negative now
11825 result += (result < MAX_IMAGE_FILES ? MAX_IMAGE_FILES : 0);
11827 setHashEntry(anim_url_hash, int2str(result, 0), token);
11832 result = ANIM_EVENT_ACTION_NONE;
11837 int get_parameter_value(char *value_raw, char *suffix, int type)
11839 char *value = getStringToLower(value_raw);
11840 int result = 0; // probably a save default value
11842 if (strEqual(suffix, ".direction"))
11844 result = (strEqual(value, "left") ? MV_LEFT :
11845 strEqual(value, "right") ? MV_RIGHT :
11846 strEqual(value, "up") ? MV_UP :
11847 strEqual(value, "down") ? MV_DOWN : MV_NONE);
11849 else if (strEqual(suffix, ".position"))
11851 result = (strEqual(value, "left") ? POS_LEFT :
11852 strEqual(value, "right") ? POS_RIGHT :
11853 strEqual(value, "top") ? POS_TOP :
11854 strEqual(value, "upper") ? POS_UPPER :
11855 strEqual(value, "middle") ? POS_MIDDLE :
11856 strEqual(value, "lower") ? POS_LOWER :
11857 strEqual(value, "bottom") ? POS_BOTTOM :
11858 strEqual(value, "any") ? POS_ANY :
11859 strEqual(value, "ce") ? POS_CE :
11860 strEqual(value, "ce_trigger") ? POS_CE_TRIGGER :
11861 strEqual(value, "last") ? POS_LAST : POS_UNDEFINED);
11863 else if (strEqual(suffix, ".align"))
11865 result = (strEqual(value, "left") ? ALIGN_LEFT :
11866 strEqual(value, "right") ? ALIGN_RIGHT :
11867 strEqual(value, "center") ? ALIGN_CENTER :
11868 strEqual(value, "middle") ? ALIGN_CENTER : ALIGN_DEFAULT);
11870 else if (strEqual(suffix, ".valign"))
11872 result = (strEqual(value, "top") ? VALIGN_TOP :
11873 strEqual(value, "bottom") ? VALIGN_BOTTOM :
11874 strEqual(value, "middle") ? VALIGN_MIDDLE :
11875 strEqual(value, "center") ? VALIGN_MIDDLE : VALIGN_DEFAULT);
11877 else if (strEqual(suffix, ".anim_mode"))
11879 result = (string_has_parameter(value, "none") ? ANIM_NONE :
11880 string_has_parameter(value, "loop") ? ANIM_LOOP :
11881 string_has_parameter(value, "linear") ? ANIM_LINEAR :
11882 string_has_parameter(value, "pingpong") ? ANIM_PINGPONG :
11883 string_has_parameter(value, "pingpong2") ? ANIM_PINGPONG2 :
11884 string_has_parameter(value, "random") ? ANIM_RANDOM :
11885 string_has_parameter(value, "random_static") ? ANIM_RANDOM_STATIC :
11886 string_has_parameter(value, "ce_value") ? ANIM_CE_VALUE :
11887 string_has_parameter(value, "ce_score") ? ANIM_CE_SCORE :
11888 string_has_parameter(value, "ce_delay") ? ANIM_CE_DELAY :
11889 string_has_parameter(value, "horizontal") ? ANIM_HORIZONTAL :
11890 string_has_parameter(value, "vertical") ? ANIM_VERTICAL :
11891 string_has_parameter(value, "centered") ? ANIM_CENTERED :
11892 string_has_parameter(value, "all") ? ANIM_ALL :
11893 string_has_parameter(value, "tiled") ? ANIM_TILED :
11894 string_has_parameter(value, "level_nr") ? ANIM_LEVEL_NR :
11897 if (string_has_parameter(value, "once"))
11898 result |= ANIM_ONCE;
11900 if (string_has_parameter(value, "reverse"))
11901 result |= ANIM_REVERSE;
11903 if (string_has_parameter(value, "opaque_player"))
11904 result |= ANIM_OPAQUE_PLAYER;
11906 if (string_has_parameter(value, "static_panel"))
11907 result |= ANIM_STATIC_PANEL;
11909 else if (strEqual(suffix, ".init_event") ||
11910 strEqual(suffix, ".anim_event"))
11912 result = get_anim_parameter_values(value);
11914 else if (strEqual(suffix, ".init_delay_action") ||
11915 strEqual(suffix, ".anim_delay_action") ||
11916 strEqual(suffix, ".post_delay_action") ||
11917 strEqual(suffix, ".init_event_action") ||
11918 strEqual(suffix, ".anim_event_action"))
11920 result = get_anim_action_parameter_value(value_raw);
11922 else if (strEqual(suffix, ".class"))
11924 result = (strEqual(value, ARG_UNDEFINED) ? ARG_UNDEFINED_VALUE :
11925 get_hash_from_key(value));
11927 else if (strEqual(suffix, ".style"))
11929 result = STYLE_DEFAULT;
11931 if (string_has_parameter(value, "accurate_borders"))
11932 result |= STYLE_ACCURATE_BORDERS;
11934 if (string_has_parameter(value, "inner_corners"))
11935 result |= STYLE_INNER_CORNERS;
11937 if (string_has_parameter(value, "reverse"))
11938 result |= STYLE_REVERSE;
11940 if (string_has_parameter(value, "leftmost_position"))
11941 result |= STYLE_LEFTMOST_POSITION;
11943 if (string_has_parameter(value, "block_clicks"))
11944 result |= STYLE_BLOCK;
11946 if (string_has_parameter(value, "passthrough_clicks"))
11947 result |= STYLE_PASSTHROUGH;
11949 if (string_has_parameter(value, "multiple_actions"))
11950 result |= STYLE_MULTIPLE_ACTIONS;
11952 if (string_has_parameter(value, "consume_ce_event"))
11953 result |= STYLE_CONSUME_CE_EVENT;
11955 else if (strEqual(suffix, ".fade_mode"))
11957 result = (string_has_parameter(value, "none") ? FADE_MODE_NONE :
11958 string_has_parameter(value, "fade") ? FADE_MODE_FADE :
11959 string_has_parameter(value, "fade_in") ? FADE_MODE_FADE_IN :
11960 string_has_parameter(value, "fade_out") ? FADE_MODE_FADE_OUT :
11961 string_has_parameter(value, "crossfade") ? FADE_MODE_CROSSFADE :
11962 string_has_parameter(value, "melt") ? FADE_MODE_MELT :
11963 string_has_parameter(value, "curtain") ? FADE_MODE_CURTAIN :
11964 FADE_MODE_DEFAULT);
11966 else if (strEqual(suffix, ".auto_delay_unit"))
11968 result = (string_has_parameter(value, "ms") ? AUTO_DELAY_UNIT_MS :
11969 string_has_parameter(value, "frames") ? AUTO_DELAY_UNIT_FRAMES :
11970 AUTO_DELAY_UNIT_DEFAULT);
11972 else if (strPrefix(suffix, ".font")) // (may also be ".font_xyz")
11974 result = gfx.get_font_from_token_function(value);
11976 else // generic parameter of type integer or boolean
11978 result = (strEqual(value, ARG_UNDEFINED) ? ARG_UNDEFINED_VALUE :
11979 type == TYPE_INTEGER ? get_integer_from_string(value) :
11980 type == TYPE_BOOLEAN ? get_boolean_from_string(value) :
11981 ARG_UNDEFINED_VALUE);
11989 static int get_token_parameter_value(char *token, char *value_raw)
11993 if (token == NULL || value_raw == NULL)
11994 return ARG_UNDEFINED_VALUE;
11996 suffix = strrchr(token, '.');
11997 if (suffix == NULL)
12000 if (strEqual(suffix, ".element"))
12001 return getElementFromToken(value_raw);
12003 // !!! USE CORRECT VALUE TYPE (currently works also for TYPE_BOOLEAN) !!!
12004 return get_parameter_value(value_raw, suffix, TYPE_INTEGER);
12007 void InitMenuDesignSettings_FromHash(SetupFileHash *setup_file_hash,
12008 boolean ignore_defaults)
12012 for (i = 0; image_config_vars[i].token != NULL; i++)
12014 char *value = getHashEntry(setup_file_hash, image_config_vars[i].token);
12016 // (ignore definitions set to "[DEFAULT]" which are already initialized)
12017 if (ignore_defaults && strEqual(value, ARG_DEFAULT))
12021 *image_config_vars[i].value =
12022 get_token_parameter_value(image_config_vars[i].token, value);
12026 void InitMenuDesignSettings_Static(void)
12028 // always start with reliable default values from static default config
12029 InitMenuDesignSettings_FromHash(image_config_hash, FALSE);
12032 static void InitMenuDesignSettings_SpecialPreProcessing(void)
12036 // the following initializes hierarchical values from static configuration
12038 // special case: initialize "ARG_DEFAULT" values in static default config
12039 // (e.g., initialize "[titlemessage].fade_mode" from "[title].fade_mode")
12040 titlescreen_initial_first_default.fade_mode =
12041 title_initial_first_default.fade_mode;
12042 titlescreen_initial_first_default.fade_delay =
12043 title_initial_first_default.fade_delay;
12044 titlescreen_initial_first_default.post_delay =
12045 title_initial_first_default.post_delay;
12046 titlescreen_initial_first_default.auto_delay =
12047 title_initial_first_default.auto_delay;
12048 titlescreen_initial_first_default.auto_delay_unit =
12049 title_initial_first_default.auto_delay_unit;
12050 titlescreen_first_default.fade_mode = title_first_default.fade_mode;
12051 titlescreen_first_default.fade_delay = title_first_default.fade_delay;
12052 titlescreen_first_default.post_delay = title_first_default.post_delay;
12053 titlescreen_first_default.auto_delay = title_first_default.auto_delay;
12054 titlescreen_first_default.auto_delay_unit =
12055 title_first_default.auto_delay_unit;
12056 titlemessage_initial_first_default.fade_mode =
12057 title_initial_first_default.fade_mode;
12058 titlemessage_initial_first_default.fade_delay =
12059 title_initial_first_default.fade_delay;
12060 titlemessage_initial_first_default.post_delay =
12061 title_initial_first_default.post_delay;
12062 titlemessage_initial_first_default.auto_delay =
12063 title_initial_first_default.auto_delay;
12064 titlemessage_initial_first_default.auto_delay_unit =
12065 title_initial_first_default.auto_delay_unit;
12066 titlemessage_first_default.fade_mode = title_first_default.fade_mode;
12067 titlemessage_first_default.fade_delay = title_first_default.fade_delay;
12068 titlemessage_first_default.post_delay = title_first_default.post_delay;
12069 titlemessage_first_default.auto_delay = title_first_default.auto_delay;
12070 titlemessage_first_default.auto_delay_unit =
12071 title_first_default.auto_delay_unit;
12073 titlescreen_initial_default.fade_mode = title_initial_default.fade_mode;
12074 titlescreen_initial_default.fade_delay = title_initial_default.fade_delay;
12075 titlescreen_initial_default.post_delay = title_initial_default.post_delay;
12076 titlescreen_initial_default.auto_delay = title_initial_default.auto_delay;
12077 titlescreen_initial_default.auto_delay_unit =
12078 title_initial_default.auto_delay_unit;
12079 titlescreen_default.fade_mode = title_default.fade_mode;
12080 titlescreen_default.fade_delay = title_default.fade_delay;
12081 titlescreen_default.post_delay = title_default.post_delay;
12082 titlescreen_default.auto_delay = title_default.auto_delay;
12083 titlescreen_default.auto_delay_unit = title_default.auto_delay_unit;
12084 titlemessage_initial_default.fade_mode = title_initial_default.fade_mode;
12085 titlemessage_initial_default.fade_delay = title_initial_default.fade_delay;
12086 titlemessage_initial_default.post_delay = title_initial_default.post_delay;
12087 titlemessage_initial_default.auto_delay_unit =
12088 title_initial_default.auto_delay_unit;
12089 titlemessage_default.fade_mode = title_default.fade_mode;
12090 titlemessage_default.fade_delay = title_default.fade_delay;
12091 titlemessage_default.post_delay = title_default.post_delay;
12092 titlemessage_default.auto_delay = title_default.auto_delay;
12093 titlemessage_default.auto_delay_unit = title_default.auto_delay_unit;
12095 // special case: initialize "ARG_DEFAULT" values in static default config
12096 // (e.g., init "titlemessage_1.fade_mode" from "[titlemessage].fade_mode")
12097 for (i = 0; i < MAX_NUM_TITLE_MESSAGES; i++)
12099 titlescreen_initial_first[i] = titlescreen_initial_first_default;
12100 titlescreen_first[i] = titlescreen_first_default;
12101 titlemessage_initial_first[i] = titlemessage_initial_first_default;
12102 titlemessage_first[i] = titlemessage_first_default;
12104 titlescreen_initial[i] = titlescreen_initial_default;
12105 titlescreen[i] = titlescreen_default;
12106 titlemessage_initial[i] = titlemessage_initial_default;
12107 titlemessage[i] = titlemessage_default;
12110 // special case: initialize "ARG_DEFAULT" values in static default config
12111 // (eg, init "menu.enter_screen.SCORES.xyz" from "menu.enter_screen.xyz")
12112 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
12114 if (i == GFX_SPECIAL_ARG_TITLE) // title values already initialized
12117 menu.enter_screen[i] = menu.enter_screen[GFX_SPECIAL_ARG_DEFAULT];
12118 menu.leave_screen[i] = menu.leave_screen[GFX_SPECIAL_ARG_DEFAULT];
12119 menu.next_screen[i] = menu.next_screen[GFX_SPECIAL_ARG_DEFAULT];
12122 // special case: initialize "ARG_DEFAULT" values in static default config
12123 // (eg, init "viewport.door_1.MAIN.xyz" from "viewport.door_1.xyz")
12124 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
12126 viewport.window[i] = viewport.window[GFX_SPECIAL_ARG_DEFAULT];
12127 viewport.playfield[i] = viewport.playfield[GFX_SPECIAL_ARG_DEFAULT];
12128 viewport.door_1[i] = viewport.door_1[GFX_SPECIAL_ARG_DEFAULT];
12130 if (i == GFX_SPECIAL_ARG_EDITOR) // editor values already initialized
12133 viewport.door_2[i] = viewport.door_2[GFX_SPECIAL_ARG_DEFAULT];
12137 static void InitMenuDesignSettings_SpecialPostProcessing(void)
12141 struct XY *dst, *src;
12143 game_buttons_xy[] =
12145 { &game.button.save, &game.button.stop },
12146 { &game.button.pause2, &game.button.pause },
12147 { &game.button.load, &game.button.play },
12148 { &game.button.undo, &game.button.stop },
12149 { &game.button.redo, &game.button.play },
12155 // special case: initialize later added SETUP list size from LEVELS value
12156 if (menu.list_size[GAME_MODE_SETUP] == -1)
12157 menu.list_size[GAME_MODE_SETUP] = menu.list_size[GAME_MODE_LEVELS];
12159 // set default position for snapshot buttons to stop/pause/play buttons
12160 for (i = 0; game_buttons_xy[i].dst != NULL; i++)
12161 if ((*game_buttons_xy[i].dst).x == -1 &&
12162 (*game_buttons_xy[i].dst).y == -1)
12163 *game_buttons_xy[i].dst = *game_buttons_xy[i].src;
12165 // --------------------------------------------------------------------------
12166 // dynamic viewports (including playfield margins, borders and alignments)
12167 // --------------------------------------------------------------------------
12169 // dynamic viewports currently only supported for landscape mode
12170 int display_width = MAX(video.display_width, video.display_height);
12171 int display_height = MIN(video.display_width, video.display_height);
12173 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
12175 struct RectWithBorder *vp_window = &viewport.window[i];
12176 struct RectWithBorder *vp_playfield = &viewport.playfield[i];
12177 struct RectWithBorder *vp_door_1 = &viewport.door_1[i];
12178 struct RectWithBorder *vp_door_2 = &viewport.door_2[i];
12179 boolean dynamic_window_width = (vp_window->min_width != -1);
12180 boolean dynamic_window_height = (vp_window->min_height != -1);
12181 boolean dynamic_playfield_width = (vp_playfield->min_width != -1);
12182 boolean dynamic_playfield_height = (vp_playfield->min_height != -1);
12184 // adjust window size if min/max width/height is specified
12186 if (vp_window->min_width != -1)
12188 int window_width = display_width;
12190 // when using static window height, use aspect ratio of display
12191 if (vp_window->min_height == -1)
12192 window_width = vp_window->height * display_width / display_height;
12194 vp_window->width = MAX(vp_window->min_width, window_width);
12197 if (vp_window->min_height != -1)
12199 int window_height = display_height;
12201 // when using static window width, use aspect ratio of display
12202 if (vp_window->min_width == -1)
12203 window_height = vp_window->width * display_height / display_width;
12205 vp_window->height = MAX(vp_window->min_height, window_height);
12208 if (vp_window->max_width != -1)
12209 vp_window->width = MIN(vp_window->width, vp_window->max_width);
12211 if (vp_window->max_height != -1)
12212 vp_window->height = MIN(vp_window->height, vp_window->max_height);
12214 int playfield_width = vp_window->width;
12215 int playfield_height = vp_window->height;
12217 // adjust playfield size and position according to specified margins
12219 playfield_width -= vp_playfield->margin_left;
12220 playfield_width -= vp_playfield->margin_right;
12222 playfield_height -= vp_playfield->margin_top;
12223 playfield_height -= vp_playfield->margin_bottom;
12225 // adjust playfield size if min/max width/height is specified
12227 if (vp_playfield->min_width != -1)
12228 vp_playfield->width = MAX(vp_playfield->min_width, playfield_width);
12230 if (vp_playfield->min_height != -1)
12231 vp_playfield->height = MAX(vp_playfield->min_height, playfield_height);
12233 if (vp_playfield->max_width != -1)
12234 vp_playfield->width = MIN(vp_playfield->width, vp_playfield->max_width);
12236 if (vp_playfield->max_height != -1)
12237 vp_playfield->height = MIN(vp_playfield->height, vp_playfield->max_height);
12239 // adjust playfield position according to specified alignment
12241 if (vp_playfield->align == ALIGN_LEFT || vp_playfield->x > 0)
12242 vp_playfield->x = ALIGNED_VP_XPOS(vp_playfield);
12243 else if (vp_playfield->align == ALIGN_CENTER)
12244 vp_playfield->x = playfield_width / 2 - vp_playfield->width / 2;
12245 else if (vp_playfield->align == ALIGN_RIGHT)
12246 vp_playfield->x += playfield_width - vp_playfield->width;
12248 if (vp_playfield->valign == VALIGN_TOP || vp_playfield->y > 0)
12249 vp_playfield->y = ALIGNED_VP_YPOS(vp_playfield);
12250 else if (vp_playfield->valign == VALIGN_MIDDLE)
12251 vp_playfield->y = playfield_height / 2 - vp_playfield->height / 2;
12252 else if (vp_playfield->valign == VALIGN_BOTTOM)
12253 vp_playfield->y += playfield_height - vp_playfield->height;
12255 vp_playfield->x += vp_playfield->margin_left;
12256 vp_playfield->y += vp_playfield->margin_top;
12258 // adjust individual playfield borders if only default border is specified
12260 if (vp_playfield->border_left == -1)
12261 vp_playfield->border_left = vp_playfield->border_size;
12262 if (vp_playfield->border_right == -1)
12263 vp_playfield->border_right = vp_playfield->border_size;
12264 if (vp_playfield->border_top == -1)
12265 vp_playfield->border_top = vp_playfield->border_size;
12266 if (vp_playfield->border_bottom == -1)
12267 vp_playfield->border_bottom = vp_playfield->border_size;
12269 // set dynamic playfield borders if borders are specified as undefined
12270 // (but only if window size was dynamic and playfield size was static)
12272 if (dynamic_window_width && !dynamic_playfield_width)
12274 if (vp_playfield->border_left == -1)
12276 vp_playfield->border_left = (vp_playfield->x -
12277 vp_playfield->margin_left);
12278 vp_playfield->x -= vp_playfield->border_left;
12279 vp_playfield->width += vp_playfield->border_left;
12282 if (vp_playfield->border_right == -1)
12284 vp_playfield->border_right = (vp_window->width -
12286 vp_playfield->width -
12287 vp_playfield->margin_right);
12288 vp_playfield->width += vp_playfield->border_right;
12292 if (dynamic_window_height && !dynamic_playfield_height)
12294 if (vp_playfield->border_top == -1)
12296 vp_playfield->border_top = (vp_playfield->y -
12297 vp_playfield->margin_top);
12298 vp_playfield->y -= vp_playfield->border_top;
12299 vp_playfield->height += vp_playfield->border_top;
12302 if (vp_playfield->border_bottom == -1)
12304 vp_playfield->border_bottom = (vp_window->height -
12306 vp_playfield->height -
12307 vp_playfield->margin_bottom);
12308 vp_playfield->height += vp_playfield->border_bottom;
12312 // adjust playfield size to be a multiple of a defined alignment tile size
12314 int align_size = vp_playfield->align_size;
12315 int playfield_xtiles = vp_playfield->width / align_size;
12316 int playfield_ytiles = vp_playfield->height / align_size;
12317 int playfield_width_corrected = playfield_xtiles * align_size;
12318 int playfield_height_corrected = playfield_ytiles * align_size;
12319 boolean is_playfield_mode = (i == GFX_SPECIAL_ARG_PLAYING ||
12320 i == GFX_SPECIAL_ARG_EDITOR);
12322 if (is_playfield_mode &&
12323 dynamic_playfield_width &&
12324 vp_playfield->width != playfield_width_corrected)
12326 int playfield_xdiff = vp_playfield->width - playfield_width_corrected;
12328 vp_playfield->width = playfield_width_corrected;
12330 if (vp_playfield->align == ALIGN_LEFT)
12332 vp_playfield->border_left += playfield_xdiff;
12334 else if (vp_playfield->align == ALIGN_RIGHT)
12336 vp_playfield->border_right += playfield_xdiff;
12338 else if (vp_playfield->align == ALIGN_CENTER)
12340 int border_left_diff = playfield_xdiff / 2;
12341 int border_right_diff = playfield_xdiff - border_left_diff;
12343 vp_playfield->border_left += border_left_diff;
12344 vp_playfield->border_right += border_right_diff;
12348 if (is_playfield_mode &&
12349 dynamic_playfield_height &&
12350 vp_playfield->height != playfield_height_corrected)
12352 int playfield_ydiff = vp_playfield->height - playfield_height_corrected;
12354 vp_playfield->height = playfield_height_corrected;
12356 if (vp_playfield->valign == VALIGN_TOP)
12358 vp_playfield->border_top += playfield_ydiff;
12360 else if (vp_playfield->align == VALIGN_BOTTOM)
12362 vp_playfield->border_right += playfield_ydiff;
12364 else if (vp_playfield->align == VALIGN_MIDDLE)
12366 int border_top_diff = playfield_ydiff / 2;
12367 int border_bottom_diff = playfield_ydiff - border_top_diff;
12369 vp_playfield->border_top += border_top_diff;
12370 vp_playfield->border_bottom += border_bottom_diff;
12374 // adjust door positions according to specified alignment
12376 for (j = 0; j < 2; j++)
12378 struct RectWithBorder *vp_door = (j == 0 ? vp_door_1 : vp_door_2);
12380 if (vp_door->align == ALIGN_LEFT || vp_door->x > 0)
12381 vp_door->x = ALIGNED_VP_XPOS(vp_door);
12382 else if (vp_door->align == ALIGN_CENTER)
12383 vp_door->x = vp_window->width / 2 - vp_door->width / 2;
12384 else if (vp_door->align == ALIGN_RIGHT)
12385 vp_door->x += vp_window->width - vp_door->width;
12387 if (vp_door->valign == VALIGN_TOP || vp_door->y > 0)
12388 vp_door->y = ALIGNED_VP_YPOS(vp_door);
12389 else if (vp_door->valign == VALIGN_MIDDLE)
12390 vp_door->y = vp_window->height / 2 - vp_door->height / 2;
12391 else if (vp_door->valign == VALIGN_BOTTOM)
12392 vp_door->y += vp_window->height - vp_door->height;
12397 static void InitMenuDesignSettings_SpecialPostProcessing_AfterGraphics(void)
12401 struct XYTileSize *dst, *src;
12404 editor_buttons_xy[] =
12407 &editor.button.element_left, &editor.palette.element_left,
12408 IMG_GFX_EDITOR_BUTTON_ELEMENT_LEFT
12411 &editor.button.element_middle, &editor.palette.element_middle,
12412 IMG_GFX_EDITOR_BUTTON_ELEMENT_MIDDLE
12415 &editor.button.element_right, &editor.palette.element_right,
12416 IMG_GFX_EDITOR_BUTTON_ELEMENT_RIGHT
12423 // set default position for element buttons to element graphics
12424 for (i = 0; editor_buttons_xy[i].dst != NULL; i++)
12426 if ((*editor_buttons_xy[i].dst).x == -1 &&
12427 (*editor_buttons_xy[i].dst).y == -1)
12429 struct GraphicInfo *gd = &graphic_info[editor_buttons_xy[i].graphic];
12431 gd->width = gd->height = editor_buttons_xy[i].src->tile_size;
12433 *editor_buttons_xy[i].dst = *editor_buttons_xy[i].src;
12437 // adjust editor palette rows and columns if specified to be dynamic
12439 if (editor.palette.cols == -1)
12441 int vp_width = viewport.playfield[GFX_SPECIAL_ARG_EDITOR].width;
12442 int bt_width = graphic_info[IMG_EDITOR_PALETTE_BUTTON].width;
12443 int sc_width = graphic_info[IMG_EDITOR_PALETTE_SCROLLBAR].width;
12445 editor.palette.cols = (vp_width - sc_width) / bt_width;
12447 if (editor.palette.x == -1)
12449 int palette_width = editor.palette.cols * bt_width + sc_width;
12451 editor.palette.x = (vp_width - palette_width) / 2;
12455 if (editor.palette.rows == -1)
12457 int vp_height = viewport.playfield[GFX_SPECIAL_ARG_EDITOR].height;
12458 int bt_height = graphic_info[IMG_EDITOR_PALETTE_BUTTON].height;
12459 int tx_height = getFontHeight(FONT_TEXT_2);
12461 editor.palette.rows = (vp_height - tx_height) / bt_height;
12463 if (editor.palette.y == -1)
12465 int palette_height = editor.palette.rows * bt_height + tx_height;
12467 editor.palette.y = (vp_height - palette_height) / 2;
12472 static void InitMenuDesignSettings_PreviewPlayers_Ext(SetupFileHash *hash,
12473 boolean initialize)
12475 // special case: check if network and preview player positions are redefined,
12476 // to compare this later against the main menu level preview being redefined
12477 struct TokenIntPtrInfo menu_config_players[] =
12479 { "main.network_players.x", &menu.main.network_players.redefined },
12480 { "main.network_players.y", &menu.main.network_players.redefined },
12481 { "main.preview_players.x", &menu.main.preview_players.redefined },
12482 { "main.preview_players.y", &menu.main.preview_players.redefined },
12483 { "preview.x", &preview.redefined },
12484 { "preview.y", &preview.redefined }
12490 for (i = 0; i < ARRAY_SIZE(menu_config_players); i++)
12491 *menu_config_players[i].value = FALSE;
12495 for (i = 0; i < ARRAY_SIZE(menu_config_players); i++)
12496 if (getHashEntry(hash, menu_config_players[i].token) != NULL)
12497 *menu_config_players[i].value = TRUE;
12501 static void InitMenuDesignSettings_PreviewPlayers(void)
12503 InitMenuDesignSettings_PreviewPlayers_Ext(NULL, TRUE);
12506 static void InitMenuDesignSettings_PreviewPlayers_FromHash(SetupFileHash *hash)
12508 InitMenuDesignSettings_PreviewPlayers_Ext(hash, FALSE);
12511 static void LoadMenuDesignSettingsFromFilename(char *filename)
12513 static struct TitleFadingInfo tfi;
12514 static struct TitleMessageInfo tmi;
12515 static struct TokenInfo title_tokens[] =
12517 { TYPE_INTEGER, &tfi.fade_mode, ".fade_mode" },
12518 { TYPE_INTEGER, &tfi.fade_delay, ".fade_delay" },
12519 { TYPE_INTEGER, &tfi.post_delay, ".post_delay" },
12520 { TYPE_INTEGER, &tfi.auto_delay, ".auto_delay" },
12521 { TYPE_INTEGER, &tfi.auto_delay_unit, ".auto_delay_unit" },
12525 static struct TokenInfo titlemessage_tokens[] =
12527 { TYPE_INTEGER, &tmi.x, ".x" },
12528 { TYPE_INTEGER, &tmi.y, ".y" },
12529 { TYPE_INTEGER, &tmi.width, ".width" },
12530 { TYPE_INTEGER, &tmi.height, ".height" },
12531 { TYPE_INTEGER, &tmi.chars, ".chars" },
12532 { TYPE_INTEGER, &tmi.lines, ".lines" },
12533 { TYPE_INTEGER, &tmi.align, ".align" },
12534 { TYPE_INTEGER, &tmi.valign, ".valign" },
12535 { TYPE_INTEGER, &tmi.font, ".font" },
12536 { TYPE_BOOLEAN, &tmi.autowrap, ".autowrap" },
12537 { TYPE_BOOLEAN, &tmi.centered, ".centered" },
12538 { TYPE_BOOLEAN, &tmi.parse_comments, ".parse_comments" },
12539 { TYPE_INTEGER, &tmi.sort_priority, ".sort_priority" },
12540 { TYPE_INTEGER, &tmi.fade_mode, ".fade_mode" },
12541 { TYPE_INTEGER, &tmi.fade_delay, ".fade_delay" },
12542 { TYPE_INTEGER, &tmi.post_delay, ".post_delay" },
12543 { TYPE_INTEGER, &tmi.auto_delay, ".auto_delay" },
12544 { TYPE_INTEGER, &tmi.auto_delay_unit, ".auto_delay_unit" },
12550 struct TitleFadingInfo *info;
12555 // initialize first titles from "enter screen" definitions, if defined
12556 { &title_initial_first_default, "menu.enter_screen.TITLE" },
12557 { &title_first_default, "menu.enter_screen.TITLE" },
12559 // initialize title screens from "next screen" definitions, if defined
12560 { &title_initial_default, "menu.next_screen.TITLE" },
12561 { &title_default, "menu.next_screen.TITLE" },
12567 struct TitleMessageInfo *array;
12570 titlemessage_arrays[] =
12572 // initialize first titles from "enter screen" definitions, if defined
12573 { titlescreen_initial_first, "menu.enter_screen.TITLE" },
12574 { titlescreen_first, "menu.enter_screen.TITLE" },
12575 { titlemessage_initial_first, "menu.enter_screen.TITLE" },
12576 { titlemessage_first, "menu.enter_screen.TITLE" },
12578 // initialize titles from "next screen" definitions, if defined
12579 { titlescreen_initial, "menu.next_screen.TITLE" },
12580 { titlescreen, "menu.next_screen.TITLE" },
12581 { titlemessage_initial, "menu.next_screen.TITLE" },
12582 { titlemessage, "menu.next_screen.TITLE" },
12584 // overwrite titles with title definitions, if defined
12585 { titlescreen_initial_first, "[title_initial]" },
12586 { titlescreen_first, "[title]" },
12587 { titlemessage_initial_first, "[title_initial]" },
12588 { titlemessage_first, "[title]" },
12590 { titlescreen_initial, "[title_initial]" },
12591 { titlescreen, "[title]" },
12592 { titlemessage_initial, "[title_initial]" },
12593 { titlemessage, "[title]" },
12595 // overwrite titles with title screen/message definitions, if defined
12596 { titlescreen_initial_first, "[titlescreen_initial]" },
12597 { titlescreen_first, "[titlescreen]" },
12598 { titlemessage_initial_first, "[titlemessage_initial]" },
12599 { titlemessage_first, "[titlemessage]" },
12601 { titlescreen_initial, "[titlescreen_initial]" },
12602 { titlescreen, "[titlescreen]" },
12603 { titlemessage_initial, "[titlemessage_initial]" },
12604 { titlemessage, "[titlemessage]" },
12608 SetupFileHash *setup_file_hash;
12611 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
12614 // the following initializes hierarchical values from dynamic configuration
12616 // special case: initialize with default values that may be overwritten
12617 // (e.g., init "menu.draw_xoffset.INFO" from "menu.draw_xoffset")
12618 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
12620 struct TokenIntPtrInfo menu_config[] =
12622 { "menu.draw_xoffset", &menu.draw_xoffset[i] },
12623 { "menu.draw_yoffset", &menu.draw_yoffset[i] },
12624 { "menu.list_size", &menu.list_size[i] }
12627 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
12629 char *token = menu_config[j].token;
12630 char *value = getHashEntry(setup_file_hash, token);
12633 *menu_config[j].value = get_integer_from_string(value);
12637 // special case: initialize with default values that may be overwritten
12638 // (eg, init "menu.draw_xoffset.INFO[XXX]" from "menu.draw_xoffset.INFO")
12639 for (i = 0; i < NUM_SPECIAL_GFX_INFO_ARGS; i++)
12641 struct TokenIntPtrInfo menu_config[] =
12643 { "menu.draw_xoffset.INFO", &menu.draw_xoffset_info[i] },
12644 { "menu.draw_yoffset.INFO", &menu.draw_yoffset_info[i] },
12645 { "menu.list_size.INFO", &menu.list_size_info[i] },
12646 { "menu.list_entry_size.INFO", &menu.list_entry_size_info[i] },
12647 { "menu.tile_size.INFO", &menu.tile_size_info[i] }
12650 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
12652 char *token = menu_config[j].token;
12653 char *value = getHashEntry(setup_file_hash, token);
12656 *menu_config[j].value = get_integer_from_string(value);
12660 // special case: initialize with default values that may be overwritten
12661 // (eg, init "menu.draw_xoffset.SETUP[XXX]" from "menu.draw_xoffset.SETUP")
12662 for (i = 0; i < NUM_SPECIAL_GFX_SETUP_ARGS; i++)
12664 struct TokenIntPtrInfo menu_config[] =
12666 { "menu.draw_xoffset.SETUP", &menu.draw_xoffset_setup[i] },
12667 { "menu.draw_yoffset.SETUP", &menu.draw_yoffset_setup[i] }
12670 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
12672 char *token = menu_config[j].token;
12673 char *value = getHashEntry(setup_file_hash, token);
12676 *menu_config[j].value = get_integer_from_string(value);
12680 // special case: initialize with default values that may be overwritten
12681 // (eg, init "menu.line_spacing.INFO[XXX]" from "menu.line_spacing.INFO")
12682 for (i = 0; i < NUM_SPECIAL_GFX_INFO_ARGS; i++)
12684 struct TokenIntPtrInfo menu_config[] =
12686 { "menu.left_spacing.INFO", &menu.left_spacing_info[i] },
12687 { "menu.middle_spacing.INFO", &menu.middle_spacing_info[i] },
12688 { "menu.right_spacing.INFO", &menu.right_spacing_info[i] },
12689 { "menu.top_spacing.INFO", &menu.top_spacing_info[i] },
12690 { "menu.bottom_spacing.INFO", &menu.bottom_spacing_info[i] },
12691 { "menu.paragraph_spacing.INFO", &menu.paragraph_spacing_info[i] },
12692 { "menu.headline1_spacing.INFO", &menu.headline1_spacing_info[i] },
12693 { "menu.headline2_spacing.INFO", &menu.headline2_spacing_info[i] },
12694 { "menu.line_spacing.INFO", &menu.line_spacing_info[i] },
12695 { "menu.extra_spacing.INFO", &menu.extra_spacing_info[i] },
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_integer_from_string(value);
12708 // special case: initialize with default values that may be overwritten
12709 // (eg, init "menu.enter_screen.SCORES.xyz" from "menu.enter_screen.xyz")
12710 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
12712 struct TokenIntPtrInfo menu_config[] =
12714 { "menu.enter_screen.fade_mode", &menu.enter_screen[i].fade_mode },
12715 { "menu.enter_screen.fade_delay", &menu.enter_screen[i].fade_delay },
12716 { "menu.enter_screen.post_delay", &menu.enter_screen[i].post_delay },
12717 { "menu.leave_screen.fade_mode", &menu.leave_screen[i].fade_mode },
12718 { "menu.leave_screen.fade_delay", &menu.leave_screen[i].fade_delay },
12719 { "menu.leave_screen.post_delay", &menu.leave_screen[i].post_delay },
12720 { "menu.next_screen.fade_mode", &menu.next_screen[i].fade_mode },
12721 { "menu.next_screen.fade_delay", &menu.next_screen[i].fade_delay },
12722 { "menu.next_screen.post_delay", &menu.next_screen[i].post_delay }
12725 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
12727 char *token = menu_config[j].token;
12728 char *value = getHashEntry(setup_file_hash, token);
12731 *menu_config[j].value = get_token_parameter_value(token, value);
12735 // special case: initialize with default values that may be overwritten
12736 // (eg, init "viewport.door_1.MAIN.xyz" from "viewport.door_1.xyz")
12737 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
12741 char *token_prefix;
12742 struct RectWithBorder *struct_ptr;
12746 { "viewport.window", &viewport.window[i] },
12747 { "viewport.playfield", &viewport.playfield[i] },
12748 { "viewport.door_1", &viewport.door_1[i] },
12749 { "viewport.door_2", &viewport.door_2[i] }
12752 for (j = 0; j < ARRAY_SIZE(vp_struct); j++)
12754 struct TokenIntPtrInfo vp_config[] =
12756 { ".x", &vp_struct[j].struct_ptr->x },
12757 { ".y", &vp_struct[j].struct_ptr->y },
12758 { ".width", &vp_struct[j].struct_ptr->width },
12759 { ".height", &vp_struct[j].struct_ptr->height },
12760 { ".min_width", &vp_struct[j].struct_ptr->min_width },
12761 { ".min_height", &vp_struct[j].struct_ptr->min_height },
12762 { ".max_width", &vp_struct[j].struct_ptr->max_width },
12763 { ".max_height", &vp_struct[j].struct_ptr->max_height },
12764 { ".margin_left", &vp_struct[j].struct_ptr->margin_left },
12765 { ".margin_right", &vp_struct[j].struct_ptr->margin_right },
12766 { ".margin_top", &vp_struct[j].struct_ptr->margin_top },
12767 { ".margin_bottom", &vp_struct[j].struct_ptr->margin_bottom },
12768 { ".border_left", &vp_struct[j].struct_ptr->border_left },
12769 { ".border_right", &vp_struct[j].struct_ptr->border_right },
12770 { ".border_top", &vp_struct[j].struct_ptr->border_top },
12771 { ".border_bottom", &vp_struct[j].struct_ptr->border_bottom },
12772 { ".border_size", &vp_struct[j].struct_ptr->border_size },
12773 { ".align_size", &vp_struct[j].struct_ptr->align_size },
12774 { ".align", &vp_struct[j].struct_ptr->align },
12775 { ".valign", &vp_struct[j].struct_ptr->valign }
12778 for (k = 0; k < ARRAY_SIZE(vp_config); k++)
12780 char *token = getStringCat2(vp_struct[j].token_prefix,
12781 vp_config[k].token);
12782 char *value = getHashEntry(setup_file_hash, token);
12785 *vp_config[k].value = get_token_parameter_value(token, value);
12792 // special case: initialize with default values that may be overwritten
12793 // (e.g., init "[title].fade_mode" from "menu.next_screen.TITLE.fade_mode")
12794 for (i = 0; title_info[i].info != NULL; i++)
12796 struct TitleFadingInfo *info = title_info[i].info;
12797 char *base_token = title_info[i].text;
12799 for (j = 0; title_tokens[j].type != -1; j++)
12801 char *token = getStringCat2(base_token, title_tokens[j].text);
12802 char *value = getHashEntry(setup_file_hash, token);
12806 int parameter_value = get_token_parameter_value(token, value);
12810 *(int *)title_tokens[j].value = (int)parameter_value;
12819 // special case: initialize with default values that may be overwritten
12820 // (e.g., init "titlemessage_1.fade_mode" from "[titlemessage].fade_mode")
12821 for (i = 0; titlemessage_arrays[i].array != NULL; i++)
12823 struct TitleMessageInfo *array = titlemessage_arrays[i].array;
12824 char *base_token = titlemessage_arrays[i].text;
12826 for (j = 0; titlemessage_tokens[j].type != -1; j++)
12828 char *token = getStringCat2(base_token, titlemessage_tokens[j].text);
12829 char *value = getHashEntry(setup_file_hash, token);
12833 int parameter_value = get_token_parameter_value(token, value);
12835 for (k = 0; k < MAX_NUM_TITLE_MESSAGES; k++)
12839 if (titlemessage_tokens[j].type == TYPE_INTEGER)
12840 *(int *)titlemessage_tokens[j].value = (int)parameter_value;
12842 *(boolean *)titlemessage_tokens[j].value = (boolean)parameter_value;
12852 // read (and overwrite with) values that may be specified in config file
12853 InitMenuDesignSettings_FromHash(setup_file_hash, TRUE);
12855 // special case: check if network and preview player positions are redefined
12856 InitMenuDesignSettings_PreviewPlayers_FromHash(setup_file_hash);
12858 freeSetupFileHash(setup_file_hash);
12861 void LoadMenuDesignSettings(void)
12863 char *filename_base = UNDEFINED_FILENAME, *filename_local;
12865 InitMenuDesignSettings_Static();
12866 InitMenuDesignSettings_SpecialPreProcessing();
12867 InitMenuDesignSettings_PreviewPlayers();
12869 if (!GFX_OVERRIDE_ARTWORK(ARTWORK_TYPE_GRAPHICS))
12871 // first look for special settings configured in level series config
12872 filename_base = getCustomArtworkLevelConfigFilename(ARTWORK_TYPE_GRAPHICS);
12874 if (fileExists(filename_base))
12875 LoadMenuDesignSettingsFromFilename(filename_base);
12878 filename_local = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
12880 if (filename_local != NULL && !strEqual(filename_base, filename_local))
12881 LoadMenuDesignSettingsFromFilename(filename_local);
12883 InitMenuDesignSettings_SpecialPostProcessing();
12886 void LoadMenuDesignSettings_AfterGraphics(void)
12888 InitMenuDesignSettings_SpecialPostProcessing_AfterGraphics();
12891 void LoadUserDefinedEditorElementList(int **elements, int *num_elements)
12893 char *filename = getEditorSetupFilename();
12894 SetupFileList *setup_file_list, *list;
12895 SetupFileHash *element_hash;
12896 int num_unknown_tokens = 0;
12899 if ((setup_file_list = loadSetupFileList(filename)) == NULL)
12902 element_hash = newSetupFileHash();
12904 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
12905 setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
12907 // determined size may be larger than needed (due to unknown elements)
12909 for (list = setup_file_list; list != NULL; list = list->next)
12912 // add space for up to 3 more elements for padding that may be needed
12913 *num_elements += 3;
12915 // free memory for old list of elements, if needed
12916 checked_free(*elements);
12918 // allocate memory for new list of elements
12919 *elements = checked_malloc(*num_elements * sizeof(int));
12922 for (list = setup_file_list; list != NULL; list = list->next)
12924 char *value = getHashEntry(element_hash, list->token);
12926 if (value == NULL) // try to find obsolete token mapping
12928 char *mapped_token = get_mapped_token(list->token);
12930 if (mapped_token != NULL)
12932 value = getHashEntry(element_hash, mapped_token);
12934 free(mapped_token);
12940 (*elements)[(*num_elements)++] = atoi(value);
12944 if (num_unknown_tokens == 0)
12947 Warn("unknown token(s) found in config file:");
12948 Warn("- config file: '%s'", filename);
12950 num_unknown_tokens++;
12953 Warn("- token: '%s'", list->token);
12957 if (num_unknown_tokens > 0)
12960 while (*num_elements % 4) // pad with empty elements, if needed
12961 (*elements)[(*num_elements)++] = EL_EMPTY;
12963 freeSetupFileList(setup_file_list);
12964 freeSetupFileHash(element_hash);
12967 for (i = 0; i < *num_elements; i++)
12968 Debug("editor", "element '%s' [%d]\n",
12969 element_info[(*elements)[i]].token_name, (*elements)[i]);
12973 static struct MusicFileInfo *get_music_file_info_ext(char *basename, int music,
12976 SetupFileHash *setup_file_hash = NULL;
12977 struct MusicFileInfo tmp_music_file_info, *new_music_file_info;
12978 char *filename_music, *filename_prefix, *filename_info;
12984 token_to_value_ptr[] =
12986 { "title_header", &tmp_music_file_info.title_header },
12987 { "artist_header", &tmp_music_file_info.artist_header },
12988 { "album_header", &tmp_music_file_info.album_header },
12989 { "year_header", &tmp_music_file_info.year_header },
12990 { "played_header", &tmp_music_file_info.played_header },
12992 { "title", &tmp_music_file_info.title },
12993 { "artist", &tmp_music_file_info.artist },
12994 { "album", &tmp_music_file_info.album },
12995 { "year", &tmp_music_file_info.year },
12996 { "played", &tmp_music_file_info.played },
13002 filename_music = (is_sound ? getCustomSoundFilename(basename) :
13003 getCustomMusicFilename(basename));
13005 if (filename_music == NULL)
13008 // ---------- try to replace file extension ----------
13010 filename_prefix = getStringCopy(filename_music);
13011 if (strrchr(filename_prefix, '.') != NULL)
13012 *strrchr(filename_prefix, '.') = '\0';
13013 filename_info = getStringCat2(filename_prefix, ".txt");
13015 if (fileExists(filename_info))
13016 setup_file_hash = loadSetupFileHash(filename_info);
13018 free(filename_prefix);
13019 free(filename_info);
13021 if (setup_file_hash == NULL)
13023 // ---------- try to add file extension ----------
13025 filename_prefix = getStringCopy(filename_music);
13026 filename_info = getStringCat2(filename_prefix, ".txt");
13028 if (fileExists(filename_info))
13029 setup_file_hash = loadSetupFileHash(filename_info);
13031 free(filename_prefix);
13032 free(filename_info);
13035 if (setup_file_hash == NULL)
13038 // ---------- music file info found ----------
13040 clear_mem(&tmp_music_file_info, sizeof(struct MusicFileInfo));
13042 for (i = 0; token_to_value_ptr[i].token != NULL; i++)
13044 char *value = getHashEntry(setup_file_hash, token_to_value_ptr[i].token);
13046 *token_to_value_ptr[i].value_ptr =
13047 getStringCopy(value != NULL && *value != '\0' ? value : UNKNOWN_NAME);
13050 tmp_music_file_info.basename = getStringCopy(basename);
13051 tmp_music_file_info.music = music;
13052 tmp_music_file_info.is_sound = is_sound;
13054 new_music_file_info = checked_malloc(sizeof(struct MusicFileInfo));
13055 *new_music_file_info = tmp_music_file_info;
13057 return new_music_file_info;
13060 static struct MusicFileInfo *get_music_file_info(char *basename, int music)
13062 return get_music_file_info_ext(basename, music, FALSE);
13065 static struct MusicFileInfo *get_sound_file_info(char *basename, int sound)
13067 return get_music_file_info_ext(basename, sound, TRUE);
13070 static boolean music_info_listed_ext(struct MusicFileInfo *list,
13071 char *basename, boolean is_sound)
13073 for (; list != NULL; list = list->next)
13074 if (list->is_sound == is_sound && strEqual(list->basename, basename))
13080 static boolean music_info_listed(struct MusicFileInfo *list, char *basename)
13082 return music_info_listed_ext(list, basename, FALSE);
13085 static boolean sound_info_listed(struct MusicFileInfo *list, char *basename)
13087 return music_info_listed_ext(list, basename, TRUE);
13090 void LoadMusicInfo(void)
13092 int num_music_noconf = getMusicListSize_NoConf();
13093 int num_music = getMusicListSize();
13094 int num_sounds = getSoundListSize();
13095 struct FileInfo *music, *sound;
13096 struct MusicFileInfo *next, **new;
13100 while (music_file_info != NULL)
13102 next = music_file_info->next;
13104 checked_free(music_file_info->basename);
13106 checked_free(music_file_info->title_header);
13107 checked_free(music_file_info->artist_header);
13108 checked_free(music_file_info->album_header);
13109 checked_free(music_file_info->year_header);
13110 checked_free(music_file_info->played_header);
13112 checked_free(music_file_info->title);
13113 checked_free(music_file_info->artist);
13114 checked_free(music_file_info->album);
13115 checked_free(music_file_info->year);
13116 checked_free(music_file_info->played);
13118 free(music_file_info);
13120 music_file_info = next;
13123 new = &music_file_info;
13125 // get (configured or unconfigured) music file info for all levels
13126 for (i = leveldir_current->first_level;
13127 i <= leveldir_current->last_level; i++)
13131 if (levelset.music[i] != MUS_UNDEFINED)
13133 // get music file info for configured level music
13134 music_nr = levelset.music[i];
13136 else if (num_music_noconf > 0)
13138 // get music file info for unconfigured level music
13139 int level_pos = i - leveldir_current->first_level;
13141 music_nr = MAP_NOCONF_MUSIC(level_pos % num_music_noconf);
13148 char *basename = getMusicInfoEntryFilename(music_nr);
13150 if (basename == NULL)
13153 if (!music_info_listed(music_file_info, basename))
13155 *new = get_music_file_info(basename, music_nr);
13158 new = &(*new)->next;
13162 // get music file info for all remaining configured music files
13163 for (i = 0; i < num_music; i++)
13165 music = getMusicListEntry(i);
13167 if (music->filename == NULL)
13170 if (strEqual(music->filename, UNDEFINED_FILENAME))
13173 // a configured file may be not recognized as music
13174 if (!FileIsMusic(music->filename))
13177 if (!music_info_listed(music_file_info, music->filename))
13179 *new = get_music_file_info(music->filename, i);
13182 new = &(*new)->next;
13186 // get sound file info for all configured sound files
13187 for (i = 0; i < num_sounds; i++)
13189 sound = getSoundListEntry(i);
13191 if (sound->filename == NULL)
13194 if (strEqual(sound->filename, UNDEFINED_FILENAME))
13197 // a configured file may be not recognized as sound
13198 if (!FileIsSound(sound->filename))
13201 if (!sound_info_listed(music_file_info, sound->filename))
13203 *new = get_sound_file_info(sound->filename, i);
13205 new = &(*new)->next;
13209 // add pointers to previous list nodes
13211 struct MusicFileInfo *node = music_file_info;
13213 while (node != NULL)
13216 node->next->prev = node;
13222 static void add_helpanim_entry(int element, int action, int direction,
13223 int delay, int *num_list_entries)
13225 struct HelpAnimInfo *new_list_entry;
13226 (*num_list_entries)++;
13229 checked_realloc(helpanim_info,
13230 *num_list_entries * sizeof(struct HelpAnimInfo));
13231 new_list_entry = &helpanim_info[*num_list_entries - 1];
13233 new_list_entry->element = element;
13234 new_list_entry->action = action;
13235 new_list_entry->direction = direction;
13236 new_list_entry->delay = delay;
13239 static void print_unknown_token(char *filename, char *token, int token_nr)
13244 Warn("unknown token(s) found in config file:");
13245 Warn("- config file: '%s'", filename);
13248 Warn("- token: '%s'", token);
13251 static void print_unknown_token_end(int token_nr)
13257 void LoadHelpAnimInfo(void)
13259 char *filename = getHelpAnimFilename();
13260 SetupFileList *setup_file_list = NULL, *list;
13261 SetupFileHash *element_hash, *action_hash, *direction_hash;
13262 int num_list_entries = 0;
13263 int num_unknown_tokens = 0;
13266 if (fileExists(filename))
13267 setup_file_list = loadSetupFileList(filename);
13269 if (setup_file_list == NULL)
13271 // use reliable default values from static configuration
13272 SetupFileList *insert_ptr;
13274 insert_ptr = setup_file_list =
13275 newSetupFileList(helpanim_config[0].token,
13276 helpanim_config[0].value);
13278 for (i = 1; helpanim_config[i].token; i++)
13279 insert_ptr = addListEntry(insert_ptr,
13280 helpanim_config[i].token,
13281 helpanim_config[i].value);
13284 element_hash = newSetupFileHash();
13285 action_hash = newSetupFileHash();
13286 direction_hash = newSetupFileHash();
13288 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
13289 setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
13291 for (i = 0; i < NUM_ACTIONS; i++)
13292 setHashEntry(action_hash, element_action_info[i].suffix,
13293 i_to_a(element_action_info[i].value));
13295 // do not store direction index (bit) here, but direction value!
13296 for (i = 0; i < NUM_DIRECTIONS_FULL; i++)
13297 setHashEntry(direction_hash, element_direction_info[i].suffix,
13298 i_to_a(1 << element_direction_info[i].value));
13300 for (list = setup_file_list; list != NULL; list = list->next)
13302 char *element_token, *action_token, *direction_token;
13303 char *element_value, *action_value, *direction_value;
13304 int delay = atoi(list->value);
13306 if (strEqual(list->token, "end"))
13308 add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
13313 /* first try to break element into element/action/direction parts;
13314 if this does not work, also accept combined "element[.act][.dir]"
13315 elements (like "dynamite.active"), which are unique elements */
13317 if (strchr(list->token, '.') == NULL) // token contains no '.'
13319 element_value = getHashEntry(element_hash, list->token);
13320 if (element_value != NULL) // element found
13321 add_helpanim_entry(atoi(element_value), -1, -1, delay,
13322 &num_list_entries);
13325 // no further suffixes found -- this is not an element
13326 print_unknown_token(filename, list->token, num_unknown_tokens++);
13332 // token has format "<prefix>.<something>"
13334 action_token = strchr(list->token, '.'); // suffix may be action ...
13335 direction_token = action_token; // ... or direction
13337 element_token = getStringCopy(list->token);
13338 *strchr(element_token, '.') = '\0';
13340 element_value = getHashEntry(element_hash, element_token);
13342 if (element_value == NULL) // this is no element
13344 element_value = getHashEntry(element_hash, list->token);
13345 if (element_value != NULL) // combined element found
13346 add_helpanim_entry(atoi(element_value), -1, -1, delay,
13347 &num_list_entries);
13349 print_unknown_token(filename, list->token, num_unknown_tokens++);
13351 free(element_token);
13356 action_value = getHashEntry(action_hash, action_token);
13358 if (action_value != NULL) // action found
13360 add_helpanim_entry(atoi(element_value), atoi(action_value), -1, delay,
13361 &num_list_entries);
13363 free(element_token);
13368 direction_value = getHashEntry(direction_hash, direction_token);
13370 if (direction_value != NULL) // direction found
13372 add_helpanim_entry(atoi(element_value), -1, atoi(direction_value), delay,
13373 &num_list_entries);
13375 free(element_token);
13380 if (strchr(action_token + 1, '.') == NULL)
13382 // no further suffixes found -- this is not an action nor direction
13384 element_value = getHashEntry(element_hash, list->token);
13385 if (element_value != NULL) // combined element found
13386 add_helpanim_entry(atoi(element_value), -1, -1, delay,
13387 &num_list_entries);
13389 print_unknown_token(filename, list->token, num_unknown_tokens++);
13391 free(element_token);
13396 // token has format "<prefix>.<suffix>.<something>"
13398 direction_token = strchr(action_token + 1, '.');
13400 action_token = getStringCopy(action_token);
13401 *strchr(action_token + 1, '.') = '\0';
13403 action_value = getHashEntry(action_hash, action_token);
13405 if (action_value == NULL) // this is no action
13407 element_value = getHashEntry(element_hash, list->token);
13408 if (element_value != NULL) // combined element found
13409 add_helpanim_entry(atoi(element_value), -1, -1, delay,
13410 &num_list_entries);
13412 print_unknown_token(filename, list->token, num_unknown_tokens++);
13414 free(element_token);
13415 free(action_token);
13420 direction_value = getHashEntry(direction_hash, direction_token);
13422 if (direction_value != NULL) // direction found
13424 add_helpanim_entry(atoi(element_value), atoi(action_value),
13425 atoi(direction_value), delay, &num_list_entries);
13427 free(element_token);
13428 free(action_token);
13433 // this is no direction
13435 element_value = getHashEntry(element_hash, list->token);
13436 if (element_value != NULL) // combined element found
13437 add_helpanim_entry(atoi(element_value), -1, -1, delay,
13438 &num_list_entries);
13440 print_unknown_token(filename, list->token, num_unknown_tokens++);
13442 free(element_token);
13443 free(action_token);
13446 print_unknown_token_end(num_unknown_tokens);
13448 add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
13449 add_helpanim_entry(HELPANIM_LIST_END, -1, -1, -1, &num_list_entries);
13451 freeSetupFileList(setup_file_list);
13452 freeSetupFileHash(element_hash);
13453 freeSetupFileHash(action_hash);
13454 freeSetupFileHash(direction_hash);
13457 for (i = 0; i < num_list_entries; i++)
13458 Debug("files:LoadHelpAnimInfo", "'%s': %d, %d, %d => %d",
13459 EL_NAME(helpanim_info[i].element),
13460 helpanim_info[i].element,
13461 helpanim_info[i].action,
13462 helpanim_info[i].direction,
13463 helpanim_info[i].delay);
13467 void LoadHelpTextInfo(void)
13469 char *filename = getHelpTextFilename();
13472 if (helptext_info != NULL)
13474 freeSetupFileHash(helptext_info);
13475 helptext_info = NULL;
13478 if (fileExists(filename))
13479 helptext_info = loadSetupFileHash(filename);
13481 if (helptext_info == NULL)
13483 // use reliable default values from static configuration
13484 helptext_info = newSetupFileHash();
13486 for (i = 0; helptext_config[i].token; i++)
13487 setHashEntry(helptext_info,
13488 helptext_config[i].token,
13489 helptext_config[i].value);
13493 BEGIN_HASH_ITERATION(helptext_info, itr)
13495 Debug("files:LoadHelpTextInfo", "'%s' => '%s'",
13496 HASH_ITERATION_TOKEN(itr), HASH_ITERATION_VALUE(itr));
13498 END_HASH_ITERATION(hash, itr)
13503 // ----------------------------------------------------------------------------
13505 // ----------------------------------------------------------------------------
13507 #define MAX_NUM_CONVERT_LEVELS 1000
13509 void ConvertLevels(void)
13511 static LevelDirTree *convert_leveldir = NULL;
13512 static int convert_level_nr = -1;
13513 static int num_levels_handled = 0;
13514 static int num_levels_converted = 0;
13515 static boolean levels_failed[MAX_NUM_CONVERT_LEVELS];
13518 convert_leveldir = getTreeInfoFromIdentifier(leveldir_first,
13519 global.convert_leveldir);
13521 if (convert_leveldir == NULL)
13522 Fail("no such level identifier: '%s'", global.convert_leveldir);
13524 leveldir_current = convert_leveldir;
13526 if (global.convert_level_nr != -1)
13528 convert_leveldir->first_level = global.convert_level_nr;
13529 convert_leveldir->last_level = global.convert_level_nr;
13532 convert_level_nr = convert_leveldir->first_level;
13534 PrintLine("=", 79);
13535 Print("Converting levels\n");
13536 PrintLine("-", 79);
13537 Print("Level series identifier: '%s'\n", convert_leveldir->identifier);
13538 Print("Level series name: '%s'\n", convert_leveldir->name);
13539 Print("Level series author: '%s'\n", convert_leveldir->author);
13540 Print("Number of levels: %d\n", convert_leveldir->levels);
13541 PrintLine("=", 79);
13544 for (i = 0; i < MAX_NUM_CONVERT_LEVELS; i++)
13545 levels_failed[i] = FALSE;
13547 while (convert_level_nr <= convert_leveldir->last_level)
13549 char *level_filename;
13552 level_nr = convert_level_nr++;
13554 Print("Level %03d: ", level_nr);
13556 LoadLevel(level_nr);
13557 if (level.no_level_file || level.no_valid_file)
13559 Print("(no level)\n");
13563 Print("converting level ... ");
13566 // special case: conversion of some EMC levels as requested by ACME
13567 level.game_engine_type = GAME_ENGINE_TYPE_RND;
13570 level_filename = getDefaultLevelFilename(level_nr);
13571 new_level = !fileExists(level_filename);
13575 SaveLevel(level_nr);
13577 num_levels_converted++;
13579 Print("converted.\n");
13583 if (level_nr >= 0 && level_nr < MAX_NUM_CONVERT_LEVELS)
13584 levels_failed[level_nr] = TRUE;
13586 Print("NOT CONVERTED -- LEVEL ALREADY EXISTS.\n");
13589 num_levels_handled++;
13593 PrintLine("=", 79);
13594 Print("Number of levels handled: %d\n", num_levels_handled);
13595 Print("Number of levels converted: %d (%d%%)\n", num_levels_converted,
13596 (num_levels_handled ?
13597 num_levels_converted * 100 / num_levels_handled : 0));
13598 PrintLine("-", 79);
13599 Print("Summary (for automatic parsing by scripts):\n");
13600 Print("LEVELDIR '%s', CONVERTED %d/%d (%d%%)",
13601 convert_leveldir->identifier, num_levels_converted,
13602 num_levels_handled,
13603 (num_levels_handled ?
13604 num_levels_converted * 100 / num_levels_handled : 0));
13606 if (num_levels_handled != num_levels_converted)
13608 Print(", FAILED:");
13609 for (i = 0; i < MAX_NUM_CONVERT_LEVELS; i++)
13610 if (levels_failed[i])
13615 PrintLine("=", 79);
13617 CloseAllAndExit(0);
13621 // ----------------------------------------------------------------------------
13622 // create and save images for use in level sketches (raw BMP format)
13623 // ----------------------------------------------------------------------------
13625 void CreateLevelSketchImages(void)
13631 InitElementPropertiesGfxElement();
13633 bitmap1 = CreateBitmap(TILEX, TILEY, DEFAULT_DEPTH);
13634 bitmap2 = CreateBitmap(MINI_TILEX, MINI_TILEY, DEFAULT_DEPTH);
13636 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
13638 int element = getMappedElement(i);
13639 char basename1[16];
13640 char basename2[16];
13644 sprintf(basename1, "%04d.bmp", i);
13645 sprintf(basename2, "%04ds.bmp", i);
13647 filename1 = getPath2(global.create_sketch_images_dir, basename1);
13648 filename2 = getPath2(global.create_sketch_images_dir, basename2);
13650 DrawSizedElement(0, 0, element, TILESIZE);
13651 BlitBitmap(drawto, bitmap1, SX, SY, TILEX, TILEY, 0, 0);
13653 if (SDL_SaveBMP(bitmap1->surface, filename1) != 0)
13654 Fail("cannot save level sketch image file '%s'", filename1);
13656 DrawSizedElement(0, 0, element, MINI_TILESIZE);
13657 BlitBitmap(drawto, bitmap2, SX, SY, MINI_TILEX, MINI_TILEY, 0, 0);
13659 if (SDL_SaveBMP(bitmap2->surface, filename2) != 0)
13660 Fail("cannot save level sketch image file '%s'", filename2);
13665 // create corresponding SQL statements (for normal and small images)
13668 printf("insert into phpbb_words values (NULL, '`%03d', '<IMG class=\"levelsketch\" src=\"/I/%04d.png\"/>');\n", i, i);
13669 printf("insert into phpbb_words values (NULL, '¸%03d', '<IMG class=\"levelsketch\" src=\"/I/%04ds.png\"/>');\n", i, i);
13672 printf("insert into phpbb_words values (NULL, '`%04d', '<IMG class=\"levelsketch\" src=\"/I/%04d.png\"/>');\n", i, i);
13673 printf("insert into phpbb_words values (NULL, '¸%04d', '<IMG class=\"levelsketch\" src=\"/I/%04ds.png\"/>');\n", i, i);
13675 // optional: create content for forum level sketch demonstration post
13677 fprintf(stderr, "%03d `%03d%c", i, i, (i % 10 < 9 ? ' ' : '\n'));
13680 FreeBitmap(bitmap1);
13681 FreeBitmap(bitmap2);
13684 fprintf(stderr, "\n");
13686 Info("%d normal and small images created", NUM_FILE_ELEMENTS);
13688 CloseAllAndExit(0);
13692 // ----------------------------------------------------------------------------
13693 // create and save images for element collecting animations (raw BMP format)
13694 // ----------------------------------------------------------------------------
13696 static boolean createCollectImage(int element)
13698 return (IS_COLLECTIBLE(element) && !IS_SP_ELEMENT(element));
13701 void CreateCollectElementImages(void)
13705 int anim_frames = num_steps - 1;
13706 int tile_size = TILESIZE;
13707 int anim_width = tile_size * anim_frames;
13708 int anim_height = tile_size;
13709 int num_collect_images = 0;
13710 int pos_collect_images = 0;
13712 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
13713 if (createCollectImage(i))
13714 num_collect_images++;
13716 Info("Creating %d element collecting animation images ...",
13717 num_collect_images);
13719 int dst_width = anim_width * 2;
13720 int dst_height = anim_height * num_collect_images / 2;
13721 Bitmap *dst_bitmap = CreateBitmap(dst_width, dst_height, DEFAULT_DEPTH);
13722 char *basename_bmp = "RocksCollect.bmp";
13723 char *basename_png = "RocksCollect.png";
13724 char *filename_bmp = getPath2(global.create_collect_images_dir, basename_bmp);
13725 char *filename_png = getPath2(global.create_collect_images_dir, basename_png);
13726 int len_filename_bmp = strlen(filename_bmp);
13727 int len_filename_png = strlen(filename_png);
13728 int max_command_len = MAX_FILENAME_LEN + len_filename_bmp + len_filename_png;
13729 char cmd_convert[max_command_len];
13731 snprintf(cmd_convert, max_command_len, "convert \"%s\" \"%s\"",
13735 // force using RGBA surface for destination bitmap
13736 SDL_SetColorKey(dst_bitmap->surface, SET_TRANSPARENT_PIXEL,
13737 SDL_MapRGB(dst_bitmap->surface->format, 0x00, 0x00, 0x00));
13739 dst_bitmap->surface =
13740 SDL_ConvertSurfaceFormat(dst_bitmap->surface, SDL_PIXELFORMAT_ARGB8888, 0);
13742 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
13744 if (!createCollectImage(i))
13747 int dst_x = (pos_collect_images / (num_collect_images / 2)) * anim_width;
13748 int dst_y = (pos_collect_images % (num_collect_images / 2)) * anim_height;
13749 int graphic = el2img(i);
13750 char *token_name = element_info[i].token_name;
13751 Bitmap *tmp_bitmap = CreateBitmap(tile_size, tile_size, DEFAULT_DEPTH);
13752 Bitmap *src_bitmap;
13755 Info("- creating collecting image for '%s' ...", token_name);
13757 getGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
13759 BlitBitmap(src_bitmap, tmp_bitmap, src_x, src_y,
13760 tile_size, tile_size, 0, 0);
13762 // force using RGBA surface for temporary bitmap (using transparent black)
13763 SDL_SetColorKey(tmp_bitmap->surface, SET_TRANSPARENT_PIXEL,
13764 SDL_MapRGB(tmp_bitmap->surface->format, 0x00, 0x00, 0x00));
13766 tmp_bitmap->surface =
13767 SDL_ConvertSurfaceFormat(tmp_bitmap->surface, SDL_PIXELFORMAT_ARGB8888, 0);
13769 tmp_bitmap->surface_masked = tmp_bitmap->surface;
13771 for (j = 0; j < anim_frames; j++)
13773 int frame_size_final = tile_size * (anim_frames - j) / num_steps;
13774 int frame_size = frame_size_final * num_steps;
13775 int offset = (tile_size - frame_size_final) / 2;
13776 Bitmap *frame_bitmap = ZoomBitmap(tmp_bitmap, frame_size, frame_size);
13778 while (frame_size > frame_size_final)
13782 Bitmap *half_bitmap = ZoomBitmap(frame_bitmap, frame_size, frame_size);
13784 FreeBitmap(frame_bitmap);
13786 frame_bitmap = half_bitmap;
13789 BlitBitmapMasked(frame_bitmap, dst_bitmap, 0, 0,
13790 frame_size_final, frame_size_final,
13791 dst_x + j * tile_size + offset, dst_y + offset);
13793 FreeBitmap(frame_bitmap);
13796 tmp_bitmap->surface_masked = NULL;
13798 FreeBitmap(tmp_bitmap);
13800 pos_collect_images++;
13803 if (SDL_SaveBMP(dst_bitmap->surface, filename_bmp) != 0)
13804 Fail("cannot save element collecting image file '%s'", filename_bmp);
13806 FreeBitmap(dst_bitmap);
13808 Info("Converting image file from BMP to PNG ...");
13810 if (system(cmd_convert) != 0)
13811 Fail("converting image file failed");
13813 unlink(filename_bmp);
13817 CloseAllAndExit(0);
13821 // ----------------------------------------------------------------------------
13822 // create and save images for custom and group elements (raw BMP format)
13823 // ----------------------------------------------------------------------------
13825 void CreateCustomElementImages(char *directory)
13827 char *src_basename = "RocksCE-template.ilbm";
13828 char *dst_basename = "RocksCE.bmp";
13829 char *src_filename = getPath2(directory, src_basename);
13830 char *dst_filename = getPath2(directory, dst_basename);
13831 Bitmap *src_bitmap;
13833 int yoffset_ce = 0;
13834 int yoffset_ge = (TILEY * NUM_CUSTOM_ELEMENTS / 16);
13837 InitVideoDefaults();
13839 ReCreateBitmap(&backbuffer, video.width, video.height);
13841 src_bitmap = LoadImage(src_filename);
13843 bitmap = CreateBitmap(TILEX * 16 * 2,
13844 TILEY * (NUM_CUSTOM_ELEMENTS + NUM_GROUP_ELEMENTS) / 16,
13847 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
13854 BlitBitmap(src_bitmap, bitmap, 0, 0, TILEX, TILEY,
13855 TILEX * x, TILEY * y + yoffset_ce);
13857 BlitBitmap(src_bitmap, bitmap, 0, TILEY,
13859 TILEX * x + TILEX * 16,
13860 TILEY * y + yoffset_ce);
13862 for (j = 2; j >= 0; j--)
13866 BlitBitmap(src_bitmap, bitmap,
13867 TILEX + c * 7, 0, 6, 10,
13868 TILEX * x + 6 + j * 7,
13869 TILEY * y + 11 + yoffset_ce);
13871 BlitBitmap(src_bitmap, bitmap,
13872 TILEX + c * 8, TILEY, 6, 10,
13873 TILEX * 16 + TILEX * x + 6 + j * 8,
13874 TILEY * y + 10 + yoffset_ce);
13880 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
13887 BlitBitmap(src_bitmap, bitmap, 0, 0, TILEX, TILEY,
13888 TILEX * x, TILEY * y + yoffset_ge);
13890 BlitBitmap(src_bitmap, bitmap, 0, TILEY,
13892 TILEX * x + TILEX * 16,
13893 TILEY * y + yoffset_ge);
13895 for (j = 1; j >= 0; j--)
13899 BlitBitmap(src_bitmap, bitmap, TILEX + c * 10, 11, 10, 10,
13900 TILEX * x + 6 + j * 10,
13901 TILEY * y + 11 + yoffset_ge);
13903 BlitBitmap(src_bitmap, bitmap,
13904 TILEX + c * 8, TILEY + 12, 6, 10,
13905 TILEX * 16 + TILEX * x + 10 + j * 8,
13906 TILEY * y + 10 + yoffset_ge);
13912 if (SDL_SaveBMP(bitmap->surface, dst_filename) != 0)
13913 Fail("cannot save CE graphics file '%s'", dst_filename);
13915 FreeBitmap(bitmap);
13917 CloseAllAndExit(0);