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 EM level
3655 // ----------------------------------------------------------------------------
3657 static void CopyNativeLevel_RND_to_EM(struct LevelInfo *level)
3659 static int ball_xy[8][2] =
3670 struct LevelInfo_EM *level_em = level->native_em_level;
3671 struct CAVE *cav = level_em->cav;
3674 cav->width = MIN(level->fieldx, MAX_PLAYFIELD_WIDTH);
3675 cav->height = MIN(level->fieldy, MAX_PLAYFIELD_HEIGHT);
3677 cav->time_seconds = level->time;
3678 cav->gems_needed = level->gems_needed;
3680 cav->emerald_score = level->score[SC_EMERALD];
3681 cav->diamond_score = level->score[SC_DIAMOND];
3682 cav->alien_score = level->score[SC_ROBOT];
3683 cav->tank_score = level->score[SC_SPACESHIP];
3684 cav->bug_score = level->score[SC_BUG];
3685 cav->eater_score = level->score[SC_YAMYAM];
3686 cav->nut_score = level->score[SC_NUT];
3687 cav->dynamite_score = level->score[SC_DYNAMITE];
3688 cav->key_score = level->score[SC_KEY];
3689 cav->exit_score = level->score[SC_TIME_BONUS];
3691 cav->num_eater_arrays = level->num_yamyam_contents;
3693 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
3694 for (y = 0; y < 3; y++)
3695 for (x = 0; x < 3; x++)
3696 cav->eater_array[i][y * 3 + x] =
3697 map_element_RND_to_EM_cave(level->yamyam_content[i].e[x][y]);
3699 cav->amoeba_time = level->amoeba_speed;
3700 cav->wonderwall_time = level->time_magic_wall;
3701 cav->wheel_time = level->time_wheel;
3703 cav->android_move_time = level->android_move_time;
3704 cav->android_clone_time = level->android_clone_time;
3705 cav->ball_random = level->ball_random;
3706 cav->ball_active = level->ball_active_initial;
3707 cav->ball_time = level->ball_time;
3708 cav->num_ball_arrays = level->num_ball_contents;
3710 cav->lenses_score = level->lenses_score;
3711 cav->magnify_score = level->magnify_score;
3712 cav->slurp_score = level->slurp_score;
3714 cav->lenses_time = level->lenses_time;
3715 cav->magnify_time = level->magnify_time;
3717 cav->wind_time = 9999;
3718 cav->wind_direction =
3719 map_direction_RND_to_EM(level->wind_direction_initial);
3721 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
3722 for (j = 0; j < 8; j++)
3723 cav->ball_array[i][j] =
3724 map_element_RND_to_EM_cave(level->ball_content[i].
3725 e[ball_xy[j][0]][ball_xy[j][1]]);
3727 map_android_clone_elements_RND_to_EM(level);
3729 // first fill the complete playfield with the empty space element
3730 for (y = 0; y < EM_MAX_CAVE_HEIGHT; y++)
3731 for (x = 0; x < EM_MAX_CAVE_WIDTH; x++)
3732 cav->cave[x][y] = Cblank;
3734 // then copy the real level contents from level file into the playfield
3735 for (y = 0; y < cav->height; y++) for (x = 0; x < cav->width; x++)
3737 int new_element = map_element_RND_to_EM_cave(level->field[x][y]);
3739 if (level->field[x][y] == EL_AMOEBA_DEAD)
3740 new_element = map_element_RND_to_EM_cave(EL_AMOEBA_WET);
3742 cav->cave[x][y] = new_element;
3745 for (i = 0; i < MAX_PLAYERS; i++)
3747 cav->player_x[i] = -1;
3748 cav->player_y[i] = -1;
3751 // initialize player positions and delete players from the playfield
3752 for (y = 0; y < cav->height; y++) for (x = 0; x < cav->width; x++)
3754 if (IS_PLAYER_ELEMENT(level->field[x][y]))
3756 int player_nr = GET_PLAYER_NR(level->field[x][y]);
3758 cav->player_x[player_nr] = x;
3759 cav->player_y[player_nr] = y;
3761 cav->cave[x][y] = map_element_RND_to_EM_cave(EL_EMPTY);
3766 static void CopyNativeLevel_EM_to_RND(struct LevelInfo *level)
3768 static int ball_xy[8][2] =
3779 struct LevelInfo_EM *level_em = level->native_em_level;
3780 struct CAVE *cav = level_em->cav;
3783 level->fieldx = MIN(cav->width, MAX_LEV_FIELDX);
3784 level->fieldy = MIN(cav->height, MAX_LEV_FIELDY);
3786 level->time = cav->time_seconds;
3787 level->gems_needed = cav->gems_needed;
3789 sprintf(level->name, "Level %d", level->file_info.nr);
3791 level->score[SC_EMERALD] = cav->emerald_score;
3792 level->score[SC_DIAMOND] = cav->diamond_score;
3793 level->score[SC_ROBOT] = cav->alien_score;
3794 level->score[SC_SPACESHIP] = cav->tank_score;
3795 level->score[SC_BUG] = cav->bug_score;
3796 level->score[SC_YAMYAM] = cav->eater_score;
3797 level->score[SC_NUT] = cav->nut_score;
3798 level->score[SC_DYNAMITE] = cav->dynamite_score;
3799 level->score[SC_KEY] = cav->key_score;
3800 level->score[SC_TIME_BONUS] = cav->exit_score;
3802 level->num_yamyam_contents = cav->num_eater_arrays;
3804 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
3805 for (y = 0; y < 3; y++)
3806 for (x = 0; x < 3; x++)
3807 level->yamyam_content[i].e[x][y] =
3808 map_element_EM_to_RND_cave(cav->eater_array[i][y * 3 + x]);
3810 level->amoeba_speed = cav->amoeba_time;
3811 level->time_magic_wall = cav->wonderwall_time;
3812 level->time_wheel = cav->wheel_time;
3814 level->android_move_time = cav->android_move_time;
3815 level->android_clone_time = cav->android_clone_time;
3816 level->ball_random = cav->ball_random;
3817 level->ball_active_initial = cav->ball_active;
3818 level->ball_time = cav->ball_time;
3819 level->num_ball_contents = cav->num_ball_arrays;
3821 level->lenses_score = cav->lenses_score;
3822 level->magnify_score = cav->magnify_score;
3823 level->slurp_score = cav->slurp_score;
3825 level->lenses_time = cav->lenses_time;
3826 level->magnify_time = cav->magnify_time;
3828 level->wind_direction_initial =
3829 map_direction_EM_to_RND(cav->wind_direction);
3831 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
3832 for (j = 0; j < 8; j++)
3833 level->ball_content[i].e[ball_xy[j][0]][ball_xy[j][1]] =
3834 map_element_EM_to_RND_cave(cav->ball_array[i][j]);
3836 map_android_clone_elements_EM_to_RND(level);
3838 // convert the playfield (some elements need special treatment)
3839 for (y = 0; y < level->fieldy; y++) for (x = 0; x < level->fieldx; x++)
3841 int new_element = map_element_EM_to_RND_cave(cav->cave[x][y]);
3843 if (new_element == EL_AMOEBA_WET && level->amoeba_speed == 0)
3844 new_element = EL_AMOEBA_DEAD;
3846 level->field[x][y] = new_element;
3849 for (i = 0; i < MAX_PLAYERS; i++)
3851 // in case of all players set to the same field, use the first player
3852 int nr = MAX_PLAYERS - i - 1;
3853 int jx = cav->player_x[nr];
3854 int jy = cav->player_y[nr];
3856 if (jx != -1 && jy != -1)
3857 level->field[jx][jy] = EL_PLAYER_1 + nr;
3860 // time score is counted for each 10 seconds left in Emerald Mine levels
3861 level->time_score_base = 10;
3865 // ----------------------------------------------------------------------------
3866 // functions for loading SP level
3867 // ----------------------------------------------------------------------------
3869 static void CopyNativeLevel_RND_to_SP(struct LevelInfo *level)
3871 struct LevelInfo_SP *level_sp = level->native_sp_level;
3872 LevelInfoType *header = &level_sp->header;
3875 level_sp->width = level->fieldx;
3876 level_sp->height = level->fieldy;
3878 for (x = 0; x < level->fieldx; x++)
3879 for (y = 0; y < level->fieldy; y++)
3880 level_sp->playfield[x][y] = map_element_RND_to_SP(level->field[x][y]);
3882 header->InitialGravity = (level->initial_player_gravity[0] ? 1 : 0);
3884 for (i = 0; i < SP_LEVEL_NAME_LEN; i++)
3885 header->LevelTitle[i] = level->name[i];
3886 // !!! NO STRING TERMINATION IN SUPAPLEX VB CODE YET -- FIX THIS !!!
3888 header->InfotronsNeeded = level->gems_needed;
3890 header->SpecialPortCount = 0;
3892 for (x = 0; x < level->fieldx; x++) for (y = 0; y < level->fieldy; y++)
3894 boolean gravity_port_found = FALSE;
3895 boolean gravity_port_valid = FALSE;
3896 int gravity_port_flag;
3897 int gravity_port_base_element;
3898 int element = level->field[x][y];
3900 if (element >= EL_SP_GRAVITY_ON_PORT_RIGHT &&
3901 element <= EL_SP_GRAVITY_ON_PORT_UP)
3903 gravity_port_found = TRUE;
3904 gravity_port_valid = TRUE;
3905 gravity_port_flag = 1;
3906 gravity_port_base_element = EL_SP_GRAVITY_ON_PORT_RIGHT;
3908 else if (element >= EL_SP_GRAVITY_OFF_PORT_RIGHT &&
3909 element <= EL_SP_GRAVITY_OFF_PORT_UP)
3911 gravity_port_found = TRUE;
3912 gravity_port_valid = TRUE;
3913 gravity_port_flag = 0;
3914 gravity_port_base_element = EL_SP_GRAVITY_OFF_PORT_RIGHT;
3916 else if (element >= EL_SP_GRAVITY_PORT_RIGHT &&
3917 element <= EL_SP_GRAVITY_PORT_UP)
3919 // change R'n'D style gravity inverting special port to normal port
3920 // (there are no gravity inverting ports in native Supaplex engine)
3922 gravity_port_found = TRUE;
3923 gravity_port_valid = FALSE;
3924 gravity_port_base_element = EL_SP_GRAVITY_PORT_RIGHT;
3927 if (gravity_port_found)
3929 if (gravity_port_valid &&
3930 header->SpecialPortCount < SP_MAX_SPECIAL_PORTS)
3932 SpecialPortType *port = &header->SpecialPort[header->SpecialPortCount];
3934 port->PortLocation = (y * level->fieldx + x) * 2;
3935 port->Gravity = gravity_port_flag;
3937 element += EL_SP_GRAVITY_PORT_RIGHT - gravity_port_base_element;
3939 header->SpecialPortCount++;
3943 // change special gravity port to normal port
3945 element += EL_SP_PORT_RIGHT - gravity_port_base_element;
3948 level_sp->playfield[x][y] = element - EL_SP_START;
3953 static void CopyNativeLevel_SP_to_RND(struct LevelInfo *level)
3955 struct LevelInfo_SP *level_sp = level->native_sp_level;
3956 LevelInfoType *header = &level_sp->header;
3957 boolean num_invalid_elements = 0;
3960 level->fieldx = level_sp->width;
3961 level->fieldy = level_sp->height;
3963 for (x = 0; x < level->fieldx; x++)
3965 for (y = 0; y < level->fieldy; y++)
3967 int element_old = level_sp->playfield[x][y];
3968 int element_new = getMappedElement(map_element_SP_to_RND(element_old));
3970 if (element_new == EL_UNKNOWN)
3972 num_invalid_elements++;
3974 Debug("level:native:SP", "invalid element %d at position %d, %d",
3978 level->field[x][y] = element_new;
3982 if (num_invalid_elements > 0)
3983 Warn("found %d invalid elements%s", num_invalid_elements,
3984 (!options.debug ? " (use '--debug' for more details)" : ""));
3986 for (i = 0; i < MAX_PLAYERS; i++)
3987 level->initial_player_gravity[i] =
3988 (header->InitialGravity == 1 ? TRUE : FALSE);
3990 // skip leading spaces
3991 for (i = 0; i < SP_LEVEL_NAME_LEN; i++)
3992 if (header->LevelTitle[i] != ' ')
3996 for (j = 0; i < SP_LEVEL_NAME_LEN; i++, j++)
3997 level->name[j] = header->LevelTitle[i];
3998 level->name[j] = '\0';
4000 // cut trailing spaces
4002 if (level->name[j - 1] == ' ' && level->name[j] == '\0')
4003 level->name[j - 1] = '\0';
4005 level->gems_needed = header->InfotronsNeeded;
4007 for (i = 0; i < header->SpecialPortCount; i++)
4009 SpecialPortType *port = &header->SpecialPort[i];
4010 int port_location = port->PortLocation;
4011 int gravity = port->Gravity;
4012 int port_x, port_y, port_element;
4014 port_x = (port_location / 2) % level->fieldx;
4015 port_y = (port_location / 2) / level->fieldx;
4017 if (port_x < 0 || port_x >= level->fieldx ||
4018 port_y < 0 || port_y >= level->fieldy)
4020 Warn("special port position (%d, %d) out of bounds", port_x, port_y);
4025 port_element = level->field[port_x][port_y];
4027 if (port_element < EL_SP_GRAVITY_PORT_RIGHT ||
4028 port_element > EL_SP_GRAVITY_PORT_UP)
4030 Warn("no special port at position (%d, %d)", port_x, port_y);
4035 // change previous (wrong) gravity inverting special port to either
4036 // gravity enabling special port or gravity disabling special port
4037 level->field[port_x][port_y] +=
4038 (gravity == 1 ? EL_SP_GRAVITY_ON_PORT_RIGHT :
4039 EL_SP_GRAVITY_OFF_PORT_RIGHT) - EL_SP_GRAVITY_PORT_RIGHT;
4042 // change special gravity ports without database entries to normal ports
4043 for (x = 0; x < level->fieldx; x++)
4044 for (y = 0; y < level->fieldy; y++)
4045 if (level->field[x][y] >= EL_SP_GRAVITY_PORT_RIGHT &&
4046 level->field[x][y] <= EL_SP_GRAVITY_PORT_UP)
4047 level->field[x][y] += EL_SP_PORT_RIGHT - EL_SP_GRAVITY_PORT_RIGHT;
4049 level->time = 0; // no time limit
4050 level->amoeba_speed = 0;
4051 level->time_magic_wall = 0;
4052 level->time_wheel = 0;
4053 level->amoeba_content = EL_EMPTY;
4055 // original Supaplex does not use score values -- rate by playing time
4056 for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
4057 level->score[i] = 0;
4059 level->rate_time_over_score = TRUE;
4061 // there are no yamyams in supaplex levels
4062 for (i = 0; i < level->num_yamyam_contents; i++)
4063 for (x = 0; x < 3; x++)
4064 for (y = 0; y < 3; y++)
4065 level->yamyam_content[i].e[x][y] = EL_EMPTY;
4068 static void CopyNativeTape_RND_to_SP(struct LevelInfo *level)
4070 struct LevelInfo_SP *level_sp = level->native_sp_level;
4071 struct DemoInfo_SP *demo = &level_sp->demo;
4074 // always start with reliable default values
4075 demo->is_available = FALSE;
4078 if (TAPE_IS_EMPTY(tape))
4081 demo->level_nr = tape.level_nr; // (currently not used)
4083 level_sp->header.DemoRandomSeed = tape.random_seed;
4087 for (i = 0; i < tape.length; i++)
4089 int demo_action = map_key_RND_to_SP(tape.pos[i].action[0]);
4090 int demo_repeat = tape.pos[i].delay;
4091 int demo_entries = (demo_repeat + 15) / 16;
4093 if (demo->length + demo_entries >= SP_MAX_TAPE_LEN)
4095 Warn("tape truncated: size exceeds maximum SP demo size %d",
4101 for (j = 0; j < demo_repeat / 16; j++)
4102 demo->data[demo->length++] = 0xf0 | demo_action;
4104 if (demo_repeat % 16)
4105 demo->data[demo->length++] = ((demo_repeat % 16 - 1) << 4) | demo_action;
4108 demo->is_available = TRUE;
4111 static void setTapeInfoToDefaults(void);
4113 static void CopyNativeTape_SP_to_RND(struct LevelInfo *level)
4115 struct LevelInfo_SP *level_sp = level->native_sp_level;
4116 struct DemoInfo_SP *demo = &level_sp->demo;
4117 char *filename = level->file_info.filename;
4120 // always start with reliable default values
4121 setTapeInfoToDefaults();
4123 if (!demo->is_available)
4126 tape.level_nr = demo->level_nr; // (currently not used)
4127 tape.random_seed = level_sp->header.DemoRandomSeed;
4129 TapeSetDateFromEpochSeconds(getFileTimestampEpochSeconds(filename));
4132 tape.pos[tape.counter].delay = 0;
4134 for (i = 0; i < demo->length; i++)
4136 int demo_action = demo->data[i] & 0x0f;
4137 int demo_repeat = (demo->data[i] & 0xf0) >> 4;
4138 int tape_action = map_key_SP_to_RND(demo_action);
4139 int tape_repeat = demo_repeat + 1;
4140 byte action[MAX_TAPE_ACTIONS] = { tape_action };
4141 boolean success = 0;
4144 for (j = 0; j < tape_repeat; j++)
4145 success = TapeAddAction(action);
4149 Warn("SP demo truncated: size exceeds maximum tape size %d",
4156 TapeHaltRecording();
4160 // ----------------------------------------------------------------------------
4161 // functions for loading MM level
4162 // ----------------------------------------------------------------------------
4164 static void CopyNativeLevel_RND_to_MM(struct LevelInfo *level)
4166 struct LevelInfo_MM *level_mm = level->native_mm_level;
4169 level_mm->fieldx = MIN(level->fieldx, MM_MAX_PLAYFIELD_WIDTH);
4170 level_mm->fieldy = MIN(level->fieldy, MM_MAX_PLAYFIELD_HEIGHT);
4172 level_mm->time = level->time;
4173 level_mm->kettles_needed = level->gems_needed;
4174 level_mm->auto_count_kettles = level->auto_count_gems;
4176 level_mm->mm_laser_red = level->mm_laser_red;
4177 level_mm->mm_laser_green = level->mm_laser_green;
4178 level_mm->mm_laser_blue = level->mm_laser_blue;
4180 level_mm->df_laser_red = level->df_laser_red;
4181 level_mm->df_laser_green = level->df_laser_green;
4182 level_mm->df_laser_blue = level->df_laser_blue;
4184 strcpy(level_mm->name, level->name);
4185 strcpy(level_mm->author, level->author);
4187 level_mm->score[SC_EMERALD] = level->score[SC_EMERALD];
4188 level_mm->score[SC_PACMAN] = level->score[SC_PACMAN];
4189 level_mm->score[SC_KEY] = level->score[SC_KEY];
4190 level_mm->score[SC_TIME_BONUS] = level->score[SC_TIME_BONUS];
4191 level_mm->score[SC_ELEM_BONUS] = level->score[SC_ELEM_BONUS];
4193 level_mm->amoeba_speed = level->amoeba_speed;
4194 level_mm->time_fuse = level->mm_time_fuse;
4195 level_mm->time_bomb = level->mm_time_bomb;
4196 level_mm->time_ball = level->mm_time_ball;
4197 level_mm->time_block = level->mm_time_block;
4199 level_mm->num_ball_contents = level->num_mm_ball_contents;
4200 level_mm->ball_choice_mode = level->mm_ball_choice_mode;
4201 level_mm->rotate_ball_content = level->rotate_mm_ball_content;
4202 level_mm->explode_ball = level->explode_mm_ball;
4204 for (i = 0; i < level->num_mm_ball_contents; i++)
4205 level_mm->ball_content[i] =
4206 map_element_RND_to_MM(level->mm_ball_content[i]);
4208 for (x = 0; x < level->fieldx; x++)
4209 for (y = 0; y < level->fieldy; y++)
4211 level_mm->field[x][y] = map_element_RND_to_MM(level->field[x][y]);
4214 static void CopyNativeLevel_MM_to_RND(struct LevelInfo *level)
4216 struct LevelInfo_MM *level_mm = level->native_mm_level;
4219 level->fieldx = MIN(level_mm->fieldx, MAX_LEV_FIELDX);
4220 level->fieldy = MIN(level_mm->fieldy, MAX_LEV_FIELDY);
4222 level->time = level_mm->time;
4223 level->gems_needed = level_mm->kettles_needed;
4224 level->auto_count_gems = level_mm->auto_count_kettles;
4226 level->mm_laser_red = level_mm->mm_laser_red;
4227 level->mm_laser_green = level_mm->mm_laser_green;
4228 level->mm_laser_blue = level_mm->mm_laser_blue;
4230 level->df_laser_red = level_mm->df_laser_red;
4231 level->df_laser_green = level_mm->df_laser_green;
4232 level->df_laser_blue = level_mm->df_laser_blue;
4234 strcpy(level->name, level_mm->name);
4236 // only overwrite author from 'levelinfo.conf' if author defined in level
4237 if (!strEqual(level_mm->author, ANONYMOUS_NAME))
4238 strcpy(level->author, level_mm->author);
4240 level->score[SC_EMERALD] = level_mm->score[SC_EMERALD];
4241 level->score[SC_PACMAN] = level_mm->score[SC_PACMAN];
4242 level->score[SC_KEY] = level_mm->score[SC_KEY];
4243 level->score[SC_TIME_BONUS] = level_mm->score[SC_TIME_BONUS];
4244 level->score[SC_ELEM_BONUS] = level_mm->score[SC_ELEM_BONUS];
4246 level->amoeba_speed = level_mm->amoeba_speed;
4247 level->mm_time_fuse = level_mm->time_fuse;
4248 level->mm_time_bomb = level_mm->time_bomb;
4249 level->mm_time_ball = level_mm->time_ball;
4250 level->mm_time_block = level_mm->time_block;
4252 level->num_mm_ball_contents = level_mm->num_ball_contents;
4253 level->mm_ball_choice_mode = level_mm->ball_choice_mode;
4254 level->rotate_mm_ball_content = level_mm->rotate_ball_content;
4255 level->explode_mm_ball = level_mm->explode_ball;
4257 for (i = 0; i < level->num_mm_ball_contents; i++)
4258 level->mm_ball_content[i] =
4259 map_element_MM_to_RND(level_mm->ball_content[i]);
4261 for (x = 0; x < level->fieldx; x++)
4262 for (y = 0; y < level->fieldy; y++)
4263 level->field[x][y] = map_element_MM_to_RND(level_mm->field[x][y]);
4267 // ----------------------------------------------------------------------------
4268 // functions for loading DC level
4269 // ----------------------------------------------------------------------------
4271 #define DC_LEVEL_HEADER_SIZE 344
4273 static unsigned short getDecodedWord_DC(unsigned short data_encoded,
4276 static int last_data_encoded;
4280 int diff_hi, diff_lo;
4281 int data_hi, data_lo;
4282 unsigned short data_decoded;
4286 last_data_encoded = 0;
4293 diff = data_encoded - last_data_encoded;
4294 diff_hi = diff & ~0xff;
4295 diff_lo = diff & 0xff;
4299 data_hi = diff_hi - (offset1 << 8) + (offset2 & 0xff00);
4300 data_lo = (diff_lo + (data_hi >> 16)) & 0x00ff;
4301 data_hi = data_hi & 0xff00;
4303 data_decoded = data_hi | data_lo;
4305 last_data_encoded = data_encoded;
4307 offset1 = (offset1 + 1) % 31;
4308 offset2 = offset2 & 0xff;
4310 return data_decoded;
4313 static int getMappedElement_DC(int element)
4321 // 0x0117 - 0x036e: (?)
4324 // 0x042d - 0x0684: (?)
4340 element = EL_CRYSTAL;
4343 case 0x0e77: // quicksand (boulder)
4344 element = EL_QUICKSAND_FAST_FULL;
4347 case 0x0e99: // slow quicksand (boulder)
4348 element = EL_QUICKSAND_FULL;
4352 element = EL_EM_EXIT_OPEN;
4356 element = EL_EM_EXIT_CLOSED;
4360 element = EL_EM_STEEL_EXIT_OPEN;
4364 element = EL_EM_STEEL_EXIT_CLOSED;
4367 case 0x0f4f: // dynamite (lit 1)
4368 element = EL_EM_DYNAMITE_ACTIVE;
4371 case 0x0f57: // dynamite (lit 2)
4372 element = EL_EM_DYNAMITE_ACTIVE;
4375 case 0x0f5f: // dynamite (lit 3)
4376 element = EL_EM_DYNAMITE_ACTIVE;
4379 case 0x0f67: // dynamite (lit 4)
4380 element = EL_EM_DYNAMITE_ACTIVE;
4387 element = EL_AMOEBA_WET;
4391 element = EL_AMOEBA_DROP;
4395 element = EL_DC_MAGIC_WALL;
4399 element = EL_SPACESHIP_UP;
4403 element = EL_SPACESHIP_DOWN;
4407 element = EL_SPACESHIP_LEFT;
4411 element = EL_SPACESHIP_RIGHT;
4415 element = EL_BUG_UP;
4419 element = EL_BUG_DOWN;
4423 element = EL_BUG_LEFT;
4427 element = EL_BUG_RIGHT;
4431 element = EL_MOLE_UP;
4435 element = EL_MOLE_DOWN;
4439 element = EL_MOLE_LEFT;
4443 element = EL_MOLE_RIGHT;
4451 element = EL_YAMYAM_UP;
4455 element = EL_SWITCHGATE_OPEN;
4459 element = EL_SWITCHGATE_CLOSED;
4463 element = EL_DC_SWITCHGATE_SWITCH_UP;
4467 element = EL_TIMEGATE_CLOSED;
4470 case 0x144c: // conveyor belt switch (green)
4471 element = EL_CONVEYOR_BELT_3_SWITCH_MIDDLE;
4474 case 0x144f: // conveyor belt switch (red)
4475 element = EL_CONVEYOR_BELT_1_SWITCH_MIDDLE;
4478 case 0x1452: // conveyor belt switch (blue)
4479 element = EL_CONVEYOR_BELT_4_SWITCH_MIDDLE;
4483 element = EL_CONVEYOR_BELT_3_MIDDLE;
4487 element = EL_CONVEYOR_BELT_3_LEFT;
4491 element = EL_CONVEYOR_BELT_3_RIGHT;
4495 element = EL_CONVEYOR_BELT_1_MIDDLE;
4499 element = EL_CONVEYOR_BELT_1_LEFT;
4503 element = EL_CONVEYOR_BELT_1_RIGHT;
4507 element = EL_CONVEYOR_BELT_4_MIDDLE;
4511 element = EL_CONVEYOR_BELT_4_LEFT;
4515 element = EL_CONVEYOR_BELT_4_RIGHT;
4519 element = EL_EXPANDABLE_WALL_HORIZONTAL;
4523 element = EL_EXPANDABLE_WALL_VERTICAL;
4527 element = EL_EXPANDABLE_WALL_ANY;
4530 case 0x14ce: // growing steel wall (left/right)
4531 element = EL_EXPANDABLE_STEELWALL_HORIZONTAL;
4534 case 0x14df: // growing steel wall (up/down)
4535 element = EL_EXPANDABLE_STEELWALL_VERTICAL;
4538 case 0x14e8: // growing steel wall (up/down/left/right)
4539 element = EL_EXPANDABLE_STEELWALL_ANY;
4543 element = EL_SHIELD_DEADLY;
4547 element = EL_EXTRA_TIME;
4555 element = EL_EMPTY_SPACE;
4558 case 0x1578: // quicksand (empty)
4559 element = EL_QUICKSAND_FAST_EMPTY;
4562 case 0x1579: // slow quicksand (empty)
4563 element = EL_QUICKSAND_EMPTY;
4573 element = EL_EM_DYNAMITE;
4576 case 0x15a1: // key (red)
4577 element = EL_EM_KEY_1;
4580 case 0x15a2: // key (yellow)
4581 element = EL_EM_KEY_2;
4584 case 0x15a3: // key (blue)
4585 element = EL_EM_KEY_4;
4588 case 0x15a4: // key (green)
4589 element = EL_EM_KEY_3;
4592 case 0x15a5: // key (white)
4593 element = EL_DC_KEY_WHITE;
4597 element = EL_WALL_SLIPPERY;
4604 case 0x15a8: // wall (not round)
4608 case 0x15a9: // (blue)
4609 element = EL_CHAR_A;
4612 case 0x15aa: // (blue)
4613 element = EL_CHAR_B;
4616 case 0x15ab: // (blue)
4617 element = EL_CHAR_C;
4620 case 0x15ac: // (blue)
4621 element = EL_CHAR_D;
4624 case 0x15ad: // (blue)
4625 element = EL_CHAR_E;
4628 case 0x15ae: // (blue)
4629 element = EL_CHAR_F;
4632 case 0x15af: // (blue)
4633 element = EL_CHAR_G;
4636 case 0x15b0: // (blue)
4637 element = EL_CHAR_H;
4640 case 0x15b1: // (blue)
4641 element = EL_CHAR_I;
4644 case 0x15b2: // (blue)
4645 element = EL_CHAR_J;
4648 case 0x15b3: // (blue)
4649 element = EL_CHAR_K;
4652 case 0x15b4: // (blue)
4653 element = EL_CHAR_L;
4656 case 0x15b5: // (blue)
4657 element = EL_CHAR_M;
4660 case 0x15b6: // (blue)
4661 element = EL_CHAR_N;
4664 case 0x15b7: // (blue)
4665 element = EL_CHAR_O;
4668 case 0x15b8: // (blue)
4669 element = EL_CHAR_P;
4672 case 0x15b9: // (blue)
4673 element = EL_CHAR_Q;
4676 case 0x15ba: // (blue)
4677 element = EL_CHAR_R;
4680 case 0x15bb: // (blue)
4681 element = EL_CHAR_S;
4684 case 0x15bc: // (blue)
4685 element = EL_CHAR_T;
4688 case 0x15bd: // (blue)
4689 element = EL_CHAR_U;
4692 case 0x15be: // (blue)
4693 element = EL_CHAR_V;
4696 case 0x15bf: // (blue)
4697 element = EL_CHAR_W;
4700 case 0x15c0: // (blue)
4701 element = EL_CHAR_X;
4704 case 0x15c1: // (blue)
4705 element = EL_CHAR_Y;
4708 case 0x15c2: // (blue)
4709 element = EL_CHAR_Z;
4712 case 0x15c3: // (blue)
4713 element = EL_CHAR_AUMLAUT;
4716 case 0x15c4: // (blue)
4717 element = EL_CHAR_OUMLAUT;
4720 case 0x15c5: // (blue)
4721 element = EL_CHAR_UUMLAUT;
4724 case 0x15c6: // (blue)
4725 element = EL_CHAR_0;
4728 case 0x15c7: // (blue)
4729 element = EL_CHAR_1;
4732 case 0x15c8: // (blue)
4733 element = EL_CHAR_2;
4736 case 0x15c9: // (blue)
4737 element = EL_CHAR_3;
4740 case 0x15ca: // (blue)
4741 element = EL_CHAR_4;
4744 case 0x15cb: // (blue)
4745 element = EL_CHAR_5;
4748 case 0x15cc: // (blue)
4749 element = EL_CHAR_6;
4752 case 0x15cd: // (blue)
4753 element = EL_CHAR_7;
4756 case 0x15ce: // (blue)
4757 element = EL_CHAR_8;
4760 case 0x15cf: // (blue)
4761 element = EL_CHAR_9;
4764 case 0x15d0: // (blue)
4765 element = EL_CHAR_PERIOD;
4768 case 0x15d1: // (blue)
4769 element = EL_CHAR_EXCLAM;
4772 case 0x15d2: // (blue)
4773 element = EL_CHAR_COLON;
4776 case 0x15d3: // (blue)
4777 element = EL_CHAR_LESS;
4780 case 0x15d4: // (blue)
4781 element = EL_CHAR_GREATER;
4784 case 0x15d5: // (blue)
4785 element = EL_CHAR_QUESTION;
4788 case 0x15d6: // (blue)
4789 element = EL_CHAR_COPYRIGHT;
4792 case 0x15d7: // (blue)
4793 element = EL_CHAR_UP;
4796 case 0x15d8: // (blue)
4797 element = EL_CHAR_DOWN;
4800 case 0x15d9: // (blue)
4801 element = EL_CHAR_BUTTON;
4804 case 0x15da: // (blue)
4805 element = EL_CHAR_PLUS;
4808 case 0x15db: // (blue)
4809 element = EL_CHAR_MINUS;
4812 case 0x15dc: // (blue)
4813 element = EL_CHAR_APOSTROPHE;
4816 case 0x15dd: // (blue)
4817 element = EL_CHAR_PARENLEFT;
4820 case 0x15de: // (blue)
4821 element = EL_CHAR_PARENRIGHT;
4824 case 0x15df: // (green)
4825 element = EL_CHAR_A;
4828 case 0x15e0: // (green)
4829 element = EL_CHAR_B;
4832 case 0x15e1: // (green)
4833 element = EL_CHAR_C;
4836 case 0x15e2: // (green)
4837 element = EL_CHAR_D;
4840 case 0x15e3: // (green)
4841 element = EL_CHAR_E;
4844 case 0x15e4: // (green)
4845 element = EL_CHAR_F;
4848 case 0x15e5: // (green)
4849 element = EL_CHAR_G;
4852 case 0x15e6: // (green)
4853 element = EL_CHAR_H;
4856 case 0x15e7: // (green)
4857 element = EL_CHAR_I;
4860 case 0x15e8: // (green)
4861 element = EL_CHAR_J;
4864 case 0x15e9: // (green)
4865 element = EL_CHAR_K;
4868 case 0x15ea: // (green)
4869 element = EL_CHAR_L;
4872 case 0x15eb: // (green)
4873 element = EL_CHAR_M;
4876 case 0x15ec: // (green)
4877 element = EL_CHAR_N;
4880 case 0x15ed: // (green)
4881 element = EL_CHAR_O;
4884 case 0x15ee: // (green)
4885 element = EL_CHAR_P;
4888 case 0x15ef: // (green)
4889 element = EL_CHAR_Q;
4892 case 0x15f0: // (green)
4893 element = EL_CHAR_R;
4896 case 0x15f1: // (green)
4897 element = EL_CHAR_S;
4900 case 0x15f2: // (green)
4901 element = EL_CHAR_T;
4904 case 0x15f3: // (green)
4905 element = EL_CHAR_U;
4908 case 0x15f4: // (green)
4909 element = EL_CHAR_V;
4912 case 0x15f5: // (green)
4913 element = EL_CHAR_W;
4916 case 0x15f6: // (green)
4917 element = EL_CHAR_X;
4920 case 0x15f7: // (green)
4921 element = EL_CHAR_Y;
4924 case 0x15f8: // (green)
4925 element = EL_CHAR_Z;
4928 case 0x15f9: // (green)
4929 element = EL_CHAR_AUMLAUT;
4932 case 0x15fa: // (green)
4933 element = EL_CHAR_OUMLAUT;
4936 case 0x15fb: // (green)
4937 element = EL_CHAR_UUMLAUT;
4940 case 0x15fc: // (green)
4941 element = EL_CHAR_0;
4944 case 0x15fd: // (green)
4945 element = EL_CHAR_1;
4948 case 0x15fe: // (green)
4949 element = EL_CHAR_2;
4952 case 0x15ff: // (green)
4953 element = EL_CHAR_3;
4956 case 0x1600: // (green)
4957 element = EL_CHAR_4;
4960 case 0x1601: // (green)
4961 element = EL_CHAR_5;
4964 case 0x1602: // (green)
4965 element = EL_CHAR_6;
4968 case 0x1603: // (green)
4969 element = EL_CHAR_7;
4972 case 0x1604: // (green)
4973 element = EL_CHAR_8;
4976 case 0x1605: // (green)
4977 element = EL_CHAR_9;
4980 case 0x1606: // (green)
4981 element = EL_CHAR_PERIOD;
4984 case 0x1607: // (green)
4985 element = EL_CHAR_EXCLAM;
4988 case 0x1608: // (green)
4989 element = EL_CHAR_COLON;
4992 case 0x1609: // (green)
4993 element = EL_CHAR_LESS;
4996 case 0x160a: // (green)
4997 element = EL_CHAR_GREATER;
5000 case 0x160b: // (green)
5001 element = EL_CHAR_QUESTION;
5004 case 0x160c: // (green)
5005 element = EL_CHAR_COPYRIGHT;
5008 case 0x160d: // (green)
5009 element = EL_CHAR_UP;
5012 case 0x160e: // (green)
5013 element = EL_CHAR_DOWN;
5016 case 0x160f: // (green)
5017 element = EL_CHAR_BUTTON;
5020 case 0x1610: // (green)
5021 element = EL_CHAR_PLUS;
5024 case 0x1611: // (green)
5025 element = EL_CHAR_MINUS;
5028 case 0x1612: // (green)
5029 element = EL_CHAR_APOSTROPHE;
5032 case 0x1613: // (green)
5033 element = EL_CHAR_PARENLEFT;
5036 case 0x1614: // (green)
5037 element = EL_CHAR_PARENRIGHT;
5040 case 0x1615: // (blue steel)
5041 element = EL_STEEL_CHAR_A;
5044 case 0x1616: // (blue steel)
5045 element = EL_STEEL_CHAR_B;
5048 case 0x1617: // (blue steel)
5049 element = EL_STEEL_CHAR_C;
5052 case 0x1618: // (blue steel)
5053 element = EL_STEEL_CHAR_D;
5056 case 0x1619: // (blue steel)
5057 element = EL_STEEL_CHAR_E;
5060 case 0x161a: // (blue steel)
5061 element = EL_STEEL_CHAR_F;
5064 case 0x161b: // (blue steel)
5065 element = EL_STEEL_CHAR_G;
5068 case 0x161c: // (blue steel)
5069 element = EL_STEEL_CHAR_H;
5072 case 0x161d: // (blue steel)
5073 element = EL_STEEL_CHAR_I;
5076 case 0x161e: // (blue steel)
5077 element = EL_STEEL_CHAR_J;
5080 case 0x161f: // (blue steel)
5081 element = EL_STEEL_CHAR_K;
5084 case 0x1620: // (blue steel)
5085 element = EL_STEEL_CHAR_L;
5088 case 0x1621: // (blue steel)
5089 element = EL_STEEL_CHAR_M;
5092 case 0x1622: // (blue steel)
5093 element = EL_STEEL_CHAR_N;
5096 case 0x1623: // (blue steel)
5097 element = EL_STEEL_CHAR_O;
5100 case 0x1624: // (blue steel)
5101 element = EL_STEEL_CHAR_P;
5104 case 0x1625: // (blue steel)
5105 element = EL_STEEL_CHAR_Q;
5108 case 0x1626: // (blue steel)
5109 element = EL_STEEL_CHAR_R;
5112 case 0x1627: // (blue steel)
5113 element = EL_STEEL_CHAR_S;
5116 case 0x1628: // (blue steel)
5117 element = EL_STEEL_CHAR_T;
5120 case 0x1629: // (blue steel)
5121 element = EL_STEEL_CHAR_U;
5124 case 0x162a: // (blue steel)
5125 element = EL_STEEL_CHAR_V;
5128 case 0x162b: // (blue steel)
5129 element = EL_STEEL_CHAR_W;
5132 case 0x162c: // (blue steel)
5133 element = EL_STEEL_CHAR_X;
5136 case 0x162d: // (blue steel)
5137 element = EL_STEEL_CHAR_Y;
5140 case 0x162e: // (blue steel)
5141 element = EL_STEEL_CHAR_Z;
5144 case 0x162f: // (blue steel)
5145 element = EL_STEEL_CHAR_AUMLAUT;
5148 case 0x1630: // (blue steel)
5149 element = EL_STEEL_CHAR_OUMLAUT;
5152 case 0x1631: // (blue steel)
5153 element = EL_STEEL_CHAR_UUMLAUT;
5156 case 0x1632: // (blue steel)
5157 element = EL_STEEL_CHAR_0;
5160 case 0x1633: // (blue steel)
5161 element = EL_STEEL_CHAR_1;
5164 case 0x1634: // (blue steel)
5165 element = EL_STEEL_CHAR_2;
5168 case 0x1635: // (blue steel)
5169 element = EL_STEEL_CHAR_3;
5172 case 0x1636: // (blue steel)
5173 element = EL_STEEL_CHAR_4;
5176 case 0x1637: // (blue steel)
5177 element = EL_STEEL_CHAR_5;
5180 case 0x1638: // (blue steel)
5181 element = EL_STEEL_CHAR_6;
5184 case 0x1639: // (blue steel)
5185 element = EL_STEEL_CHAR_7;
5188 case 0x163a: // (blue steel)
5189 element = EL_STEEL_CHAR_8;
5192 case 0x163b: // (blue steel)
5193 element = EL_STEEL_CHAR_9;
5196 case 0x163c: // (blue steel)
5197 element = EL_STEEL_CHAR_PERIOD;
5200 case 0x163d: // (blue steel)
5201 element = EL_STEEL_CHAR_EXCLAM;
5204 case 0x163e: // (blue steel)
5205 element = EL_STEEL_CHAR_COLON;
5208 case 0x163f: // (blue steel)
5209 element = EL_STEEL_CHAR_LESS;
5212 case 0x1640: // (blue steel)
5213 element = EL_STEEL_CHAR_GREATER;
5216 case 0x1641: // (blue steel)
5217 element = EL_STEEL_CHAR_QUESTION;
5220 case 0x1642: // (blue steel)
5221 element = EL_STEEL_CHAR_COPYRIGHT;
5224 case 0x1643: // (blue steel)
5225 element = EL_STEEL_CHAR_UP;
5228 case 0x1644: // (blue steel)
5229 element = EL_STEEL_CHAR_DOWN;
5232 case 0x1645: // (blue steel)
5233 element = EL_STEEL_CHAR_BUTTON;
5236 case 0x1646: // (blue steel)
5237 element = EL_STEEL_CHAR_PLUS;
5240 case 0x1647: // (blue steel)
5241 element = EL_STEEL_CHAR_MINUS;
5244 case 0x1648: // (blue steel)
5245 element = EL_STEEL_CHAR_APOSTROPHE;
5248 case 0x1649: // (blue steel)
5249 element = EL_STEEL_CHAR_PARENLEFT;
5252 case 0x164a: // (blue steel)
5253 element = EL_STEEL_CHAR_PARENRIGHT;
5256 case 0x164b: // (green steel)
5257 element = EL_STEEL_CHAR_A;
5260 case 0x164c: // (green steel)
5261 element = EL_STEEL_CHAR_B;
5264 case 0x164d: // (green steel)
5265 element = EL_STEEL_CHAR_C;
5268 case 0x164e: // (green steel)
5269 element = EL_STEEL_CHAR_D;
5272 case 0x164f: // (green steel)
5273 element = EL_STEEL_CHAR_E;
5276 case 0x1650: // (green steel)
5277 element = EL_STEEL_CHAR_F;
5280 case 0x1651: // (green steel)
5281 element = EL_STEEL_CHAR_G;
5284 case 0x1652: // (green steel)
5285 element = EL_STEEL_CHAR_H;
5288 case 0x1653: // (green steel)
5289 element = EL_STEEL_CHAR_I;
5292 case 0x1654: // (green steel)
5293 element = EL_STEEL_CHAR_J;
5296 case 0x1655: // (green steel)
5297 element = EL_STEEL_CHAR_K;
5300 case 0x1656: // (green steel)
5301 element = EL_STEEL_CHAR_L;
5304 case 0x1657: // (green steel)
5305 element = EL_STEEL_CHAR_M;
5308 case 0x1658: // (green steel)
5309 element = EL_STEEL_CHAR_N;
5312 case 0x1659: // (green steel)
5313 element = EL_STEEL_CHAR_O;
5316 case 0x165a: // (green steel)
5317 element = EL_STEEL_CHAR_P;
5320 case 0x165b: // (green steel)
5321 element = EL_STEEL_CHAR_Q;
5324 case 0x165c: // (green steel)
5325 element = EL_STEEL_CHAR_R;
5328 case 0x165d: // (green steel)
5329 element = EL_STEEL_CHAR_S;
5332 case 0x165e: // (green steel)
5333 element = EL_STEEL_CHAR_T;
5336 case 0x165f: // (green steel)
5337 element = EL_STEEL_CHAR_U;
5340 case 0x1660: // (green steel)
5341 element = EL_STEEL_CHAR_V;
5344 case 0x1661: // (green steel)
5345 element = EL_STEEL_CHAR_W;
5348 case 0x1662: // (green steel)
5349 element = EL_STEEL_CHAR_X;
5352 case 0x1663: // (green steel)
5353 element = EL_STEEL_CHAR_Y;
5356 case 0x1664: // (green steel)
5357 element = EL_STEEL_CHAR_Z;
5360 case 0x1665: // (green steel)
5361 element = EL_STEEL_CHAR_AUMLAUT;
5364 case 0x1666: // (green steel)
5365 element = EL_STEEL_CHAR_OUMLAUT;
5368 case 0x1667: // (green steel)
5369 element = EL_STEEL_CHAR_UUMLAUT;
5372 case 0x1668: // (green steel)
5373 element = EL_STEEL_CHAR_0;
5376 case 0x1669: // (green steel)
5377 element = EL_STEEL_CHAR_1;
5380 case 0x166a: // (green steel)
5381 element = EL_STEEL_CHAR_2;
5384 case 0x166b: // (green steel)
5385 element = EL_STEEL_CHAR_3;
5388 case 0x166c: // (green steel)
5389 element = EL_STEEL_CHAR_4;
5392 case 0x166d: // (green steel)
5393 element = EL_STEEL_CHAR_5;
5396 case 0x166e: // (green steel)
5397 element = EL_STEEL_CHAR_6;
5400 case 0x166f: // (green steel)
5401 element = EL_STEEL_CHAR_7;
5404 case 0x1670: // (green steel)
5405 element = EL_STEEL_CHAR_8;
5408 case 0x1671: // (green steel)
5409 element = EL_STEEL_CHAR_9;
5412 case 0x1672: // (green steel)
5413 element = EL_STEEL_CHAR_PERIOD;
5416 case 0x1673: // (green steel)
5417 element = EL_STEEL_CHAR_EXCLAM;
5420 case 0x1674: // (green steel)
5421 element = EL_STEEL_CHAR_COLON;
5424 case 0x1675: // (green steel)
5425 element = EL_STEEL_CHAR_LESS;
5428 case 0x1676: // (green steel)
5429 element = EL_STEEL_CHAR_GREATER;
5432 case 0x1677: // (green steel)
5433 element = EL_STEEL_CHAR_QUESTION;
5436 case 0x1678: // (green steel)
5437 element = EL_STEEL_CHAR_COPYRIGHT;
5440 case 0x1679: // (green steel)
5441 element = EL_STEEL_CHAR_UP;
5444 case 0x167a: // (green steel)
5445 element = EL_STEEL_CHAR_DOWN;
5448 case 0x167b: // (green steel)
5449 element = EL_STEEL_CHAR_BUTTON;
5452 case 0x167c: // (green steel)
5453 element = EL_STEEL_CHAR_PLUS;
5456 case 0x167d: // (green steel)
5457 element = EL_STEEL_CHAR_MINUS;
5460 case 0x167e: // (green steel)
5461 element = EL_STEEL_CHAR_APOSTROPHE;
5464 case 0x167f: // (green steel)
5465 element = EL_STEEL_CHAR_PARENLEFT;
5468 case 0x1680: // (green steel)
5469 element = EL_STEEL_CHAR_PARENRIGHT;
5472 case 0x1681: // gate (red)
5473 element = EL_EM_GATE_1;
5476 case 0x1682: // secret gate (red)
5477 element = EL_EM_GATE_1_GRAY;
5480 case 0x1683: // gate (yellow)
5481 element = EL_EM_GATE_2;
5484 case 0x1684: // secret gate (yellow)
5485 element = EL_EM_GATE_2_GRAY;
5488 case 0x1685: // gate (blue)
5489 element = EL_EM_GATE_4;
5492 case 0x1686: // secret gate (blue)
5493 element = EL_EM_GATE_4_GRAY;
5496 case 0x1687: // gate (green)
5497 element = EL_EM_GATE_3;
5500 case 0x1688: // secret gate (green)
5501 element = EL_EM_GATE_3_GRAY;
5504 case 0x1689: // gate (white)
5505 element = EL_DC_GATE_WHITE;
5508 case 0x168a: // secret gate (white)
5509 element = EL_DC_GATE_WHITE_GRAY;
5512 case 0x168b: // secret gate (no key)
5513 element = EL_DC_GATE_FAKE_GRAY;
5517 element = EL_ROBOT_WHEEL;
5521 element = EL_DC_TIMEGATE_SWITCH;
5525 element = EL_ACID_POOL_BOTTOM;
5529 element = EL_ACID_POOL_TOPLEFT;
5533 element = EL_ACID_POOL_TOPRIGHT;
5537 element = EL_ACID_POOL_BOTTOMLEFT;
5541 element = EL_ACID_POOL_BOTTOMRIGHT;
5545 element = EL_STEELWALL;
5549 element = EL_STEELWALL_SLIPPERY;
5552 case 0x1695: // steel wall (not round)
5553 element = EL_STEELWALL;
5556 case 0x1696: // steel wall (left)
5557 element = EL_DC_STEELWALL_1_LEFT;
5560 case 0x1697: // steel wall (bottom)
5561 element = EL_DC_STEELWALL_1_BOTTOM;
5564 case 0x1698: // steel wall (right)
5565 element = EL_DC_STEELWALL_1_RIGHT;
5568 case 0x1699: // steel wall (top)
5569 element = EL_DC_STEELWALL_1_TOP;
5572 case 0x169a: // steel wall (left/bottom)
5573 element = EL_DC_STEELWALL_1_BOTTOMLEFT;
5576 case 0x169b: // steel wall (right/bottom)
5577 element = EL_DC_STEELWALL_1_BOTTOMRIGHT;
5580 case 0x169c: // steel wall (right/top)
5581 element = EL_DC_STEELWALL_1_TOPRIGHT;
5584 case 0x169d: // steel wall (left/top)
5585 element = EL_DC_STEELWALL_1_TOPLEFT;
5588 case 0x169e: // steel wall (right/bottom small)
5589 element = EL_DC_STEELWALL_1_BOTTOMRIGHT_2;
5592 case 0x169f: // steel wall (left/bottom small)
5593 element = EL_DC_STEELWALL_1_BOTTOMLEFT_2;
5596 case 0x16a0: // steel wall (right/top small)
5597 element = EL_DC_STEELWALL_1_TOPRIGHT_2;
5600 case 0x16a1: // steel wall (left/top small)
5601 element = EL_DC_STEELWALL_1_TOPLEFT_2;
5604 case 0x16a2: // steel wall (left/right)
5605 element = EL_DC_STEELWALL_1_VERTICAL;
5608 case 0x16a3: // steel wall (top/bottom)
5609 element = EL_DC_STEELWALL_1_HORIZONTAL;
5612 case 0x16a4: // steel wall 2 (left end)
5613 element = EL_DC_STEELWALL_2_LEFT;
5616 case 0x16a5: // steel wall 2 (right end)
5617 element = EL_DC_STEELWALL_2_RIGHT;
5620 case 0x16a6: // steel wall 2 (top end)
5621 element = EL_DC_STEELWALL_2_TOP;
5624 case 0x16a7: // steel wall 2 (bottom end)
5625 element = EL_DC_STEELWALL_2_BOTTOM;
5628 case 0x16a8: // steel wall 2 (left/right)
5629 element = EL_DC_STEELWALL_2_HORIZONTAL;
5632 case 0x16a9: // steel wall 2 (up/down)
5633 element = EL_DC_STEELWALL_2_VERTICAL;
5636 case 0x16aa: // steel wall 2 (mid)
5637 element = EL_DC_STEELWALL_2_MIDDLE;
5641 element = EL_SIGN_EXCLAMATION;
5645 element = EL_SIGN_RADIOACTIVITY;
5649 element = EL_SIGN_STOP;
5653 element = EL_SIGN_WHEELCHAIR;
5657 element = EL_SIGN_PARKING;
5661 element = EL_SIGN_NO_ENTRY;
5665 element = EL_SIGN_HEART;
5669 element = EL_SIGN_GIVE_WAY;
5673 element = EL_SIGN_ENTRY_FORBIDDEN;
5677 element = EL_SIGN_EMERGENCY_EXIT;
5681 element = EL_SIGN_YIN_YANG;
5685 element = EL_WALL_EMERALD;
5689 element = EL_WALL_DIAMOND;
5693 element = EL_WALL_PEARL;
5697 element = EL_WALL_CRYSTAL;
5701 element = EL_INVISIBLE_WALL;
5705 element = EL_INVISIBLE_STEELWALL;
5709 // EL_INVISIBLE_SAND
5712 element = EL_LIGHT_SWITCH;
5716 element = EL_ENVELOPE_1;
5720 if (element >= 0x0117 && element <= 0x036e) // (?)
5721 element = EL_DIAMOND;
5722 else if (element >= 0x042d && element <= 0x0684) // (?)
5723 element = EL_EMERALD;
5724 else if (element >= 0x157c && element <= 0x158b)
5726 else if (element >= 0x1590 && element <= 0x159f)
5727 element = EL_DC_LANDMINE;
5728 else if (element >= 0x16bc && element <= 0x16cb)
5729 element = EL_INVISIBLE_SAND;
5732 Warn("unknown Diamond Caves element 0x%04x", element);
5734 element = EL_UNKNOWN;
5739 return getMappedElement(element);
5742 static void LoadLevelFromFileStream_DC(File *file, struct LevelInfo *level)
5744 byte header[DC_LEVEL_HEADER_SIZE];
5746 int envelope_header_pos = 62;
5747 int envelope_content_pos = 94;
5748 int level_name_pos = 251;
5749 int level_author_pos = 292;
5750 int envelope_header_len;
5751 int envelope_content_len;
5753 int level_author_len;
5755 int num_yamyam_contents;
5758 getDecodedWord_DC(0, TRUE); // initialize DC2 decoding engine
5760 for (i = 0; i < DC_LEVEL_HEADER_SIZE / 2; i++)
5762 unsigned short header_word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
5764 header[i * 2 + 0] = header_word >> 8;
5765 header[i * 2 + 1] = header_word & 0xff;
5768 // read some values from level header to check level decoding integrity
5769 fieldx = header[6] | (header[7] << 8);
5770 fieldy = header[8] | (header[9] << 8);
5771 num_yamyam_contents = header[60] | (header[61] << 8);
5773 // do some simple sanity checks to ensure that level was correctly decoded
5774 if (fieldx < 1 || fieldx > 256 ||
5775 fieldy < 1 || fieldy > 256 ||
5776 num_yamyam_contents < 1 || num_yamyam_contents > 8)
5778 level->no_valid_file = TRUE;
5780 Warn("cannot decode level from stream -- using empty level");
5785 // maximum envelope header size is 31 bytes
5786 envelope_header_len = header[envelope_header_pos];
5787 // maximum envelope content size is 110 (156?) bytes
5788 envelope_content_len = header[envelope_content_pos];
5790 // maximum level title size is 40 bytes
5791 level_name_len = MIN(header[level_name_pos], MAX_LEVEL_NAME_LEN);
5792 // maximum level author size is 30 (51?) bytes
5793 level_author_len = MIN(header[level_author_pos], MAX_LEVEL_AUTHOR_LEN);
5797 for (i = 0; i < envelope_header_len; i++)
5798 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
5799 level->envelope[0].text[envelope_size++] =
5800 header[envelope_header_pos + 1 + i];
5802 if (envelope_header_len > 0 && envelope_content_len > 0)
5804 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
5805 level->envelope[0].text[envelope_size++] = '\n';
5806 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
5807 level->envelope[0].text[envelope_size++] = '\n';
5810 for (i = 0; i < envelope_content_len; i++)
5811 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
5812 level->envelope[0].text[envelope_size++] =
5813 header[envelope_content_pos + 1 + i];
5815 level->envelope[0].text[envelope_size] = '\0';
5817 level->envelope[0].xsize = MAX_ENVELOPE_XSIZE;
5818 level->envelope[0].ysize = 10;
5819 level->envelope[0].autowrap = TRUE;
5820 level->envelope[0].centered = TRUE;
5822 for (i = 0; i < level_name_len; i++)
5823 level->name[i] = header[level_name_pos + 1 + i];
5824 level->name[level_name_len] = '\0';
5826 for (i = 0; i < level_author_len; i++)
5827 level->author[i] = header[level_author_pos + 1 + i];
5828 level->author[level_author_len] = '\0';
5830 num_yamyam_contents = header[60] | (header[61] << 8);
5831 level->num_yamyam_contents =
5832 MIN(MAX(MIN_ELEMENT_CONTENTS, num_yamyam_contents), MAX_ELEMENT_CONTENTS);
5834 for (i = 0; i < num_yamyam_contents; i++)
5836 for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
5838 unsigned short word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
5839 int element_dc = ((word & 0xff) << 8) | ((word >> 8) & 0xff);
5841 if (i < MAX_ELEMENT_CONTENTS)
5842 level->yamyam_content[i].e[x][y] = getMappedElement_DC(element_dc);
5846 fieldx = header[6] | (header[7] << 8);
5847 fieldy = header[8] | (header[9] << 8);
5848 level->fieldx = MIN(MAX(MIN_LEV_FIELDX, fieldx), MAX_LEV_FIELDX);
5849 level->fieldy = MIN(MAX(MIN_LEV_FIELDY, fieldy), MAX_LEV_FIELDY);
5851 for (y = 0; y < fieldy; y++) for (x = 0; x < fieldx; x++)
5853 unsigned short word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
5854 int element_dc = ((word & 0xff) << 8) | ((word >> 8) & 0xff);
5856 if (x < MAX_LEV_FIELDX && y < MAX_LEV_FIELDY)
5857 level->field[x][y] = getMappedElement_DC(element_dc);
5860 x = MIN(MAX(0, (header[10] | (header[11] << 8)) - 1), MAX_LEV_FIELDX - 1);
5861 y = MIN(MAX(0, (header[12] | (header[13] << 8)) - 1), MAX_LEV_FIELDY - 1);
5862 level->field[x][y] = EL_PLAYER_1;
5864 x = MIN(MAX(0, (header[14] | (header[15] << 8)) - 1), MAX_LEV_FIELDX - 1);
5865 y = MIN(MAX(0, (header[16] | (header[17] << 8)) - 1), MAX_LEV_FIELDY - 1);
5866 level->field[x][y] = EL_PLAYER_2;
5868 level->gems_needed = header[18] | (header[19] << 8);
5870 level->score[SC_EMERALD] = header[20] | (header[21] << 8);
5871 level->score[SC_DIAMOND] = header[22] | (header[23] << 8);
5872 level->score[SC_PEARL] = header[24] | (header[25] << 8);
5873 level->score[SC_CRYSTAL] = header[26] | (header[27] << 8);
5874 level->score[SC_NUT] = header[28] | (header[29] << 8);
5875 level->score[SC_ROBOT] = header[30] | (header[31] << 8);
5876 level->score[SC_SPACESHIP] = header[32] | (header[33] << 8);
5877 level->score[SC_BUG] = header[34] | (header[35] << 8);
5878 level->score[SC_YAMYAM] = header[36] | (header[37] << 8);
5879 level->score[SC_DYNAMITE] = header[38] | (header[39] << 8);
5880 level->score[SC_KEY] = header[40] | (header[41] << 8);
5881 level->score[SC_TIME_BONUS] = header[42] | (header[43] << 8);
5883 level->time = header[44] | (header[45] << 8);
5885 level->amoeba_speed = header[46] | (header[47] << 8);
5886 level->time_light = header[48] | (header[49] << 8);
5887 level->time_timegate = header[50] | (header[51] << 8);
5888 level->time_wheel = header[52] | (header[53] << 8);
5889 level->time_magic_wall = header[54] | (header[55] << 8);
5890 level->extra_time = header[56] | (header[57] << 8);
5891 level->shield_normal_time = header[58] | (header[59] << 8);
5893 // shield and extra time elements do not have a score
5894 level->score[SC_SHIELD] = 0;
5895 level->extra_time_score = 0;
5897 // set time for normal and deadly shields to the same value
5898 level->shield_deadly_time = level->shield_normal_time;
5900 // Diamond Caves has the same (strange) behaviour as Emerald Mine that gems
5901 // can slip down from flat walls, like normal walls and steel walls
5902 level->em_slippery_gems = TRUE;
5904 // time score is counted for each 10 seconds left in Diamond Caves levels
5905 level->time_score_base = 10;
5908 static void LoadLevelFromFileInfo_DC(struct LevelInfo *level,
5909 struct LevelFileInfo *level_file_info,
5910 boolean level_info_only)
5912 char *filename = level_file_info->filename;
5914 int num_magic_bytes = 8;
5915 char magic_bytes[num_magic_bytes + 1];
5916 int num_levels_to_skip = level_file_info->nr - leveldir_current->first_level;
5918 if (!(file = openFile(filename, MODE_READ)))
5920 level->no_valid_file = TRUE;
5922 if (!level_info_only)
5923 Warn("cannot read level '%s' -- using empty level", filename);
5928 // fseek(file, 0x0000, SEEK_SET);
5930 if (level_file_info->packed)
5932 // read "magic bytes" from start of file
5933 if (getStringFromFile(file, magic_bytes, num_magic_bytes + 1) == NULL)
5934 magic_bytes[0] = '\0';
5936 // check "magic bytes" for correct file format
5937 if (!strPrefix(magic_bytes, "DC2"))
5939 level->no_valid_file = TRUE;
5941 Warn("unknown DC level file '%s' -- using empty level", filename);
5946 if (strPrefix(magic_bytes, "DC2Win95") ||
5947 strPrefix(magic_bytes, "DC2Win98"))
5949 int position_first_level = 0x00fa;
5950 int extra_bytes = 4;
5953 // advance file stream to first level inside the level package
5954 skip_bytes = position_first_level - num_magic_bytes - extra_bytes;
5956 // each block of level data is followed by block of non-level data
5957 num_levels_to_skip *= 2;
5959 // at least skip header bytes, therefore use ">= 0" instead of "> 0"
5960 while (num_levels_to_skip >= 0)
5962 // advance file stream to next level inside the level package
5963 if (seekFile(file, skip_bytes, SEEK_CUR) != 0)
5965 level->no_valid_file = TRUE;
5967 Warn("cannot fseek in file '%s' -- using empty level", filename);
5972 // skip apparently unused extra bytes following each level
5973 ReadUnusedBytesFromFile(file, extra_bytes);
5975 // read size of next level in level package
5976 skip_bytes = getFile32BitLE(file);
5978 num_levels_to_skip--;
5983 level->no_valid_file = TRUE;
5985 Warn("unknown DC2 level file '%s' -- using empty level", filename);
5991 LoadLevelFromFileStream_DC(file, level);
5997 // ----------------------------------------------------------------------------
5998 // functions for loading SB level
5999 // ----------------------------------------------------------------------------
6001 int getMappedElement_SB(int element_ascii, boolean use_ces)
6009 sb_element_mapping[] =
6011 { ' ', EL_EMPTY, EL_CUSTOM_1 }, // floor (space)
6012 { '#', EL_STEELWALL, EL_CUSTOM_2 }, // wall
6013 { '@', EL_PLAYER_1, EL_CUSTOM_3 }, // player
6014 { '$', EL_SOKOBAN_OBJECT, EL_CUSTOM_4 }, // box
6015 { '.', EL_SOKOBAN_FIELD_EMPTY, EL_CUSTOM_5 }, // goal square
6016 { '*', EL_SOKOBAN_FIELD_FULL, EL_CUSTOM_6 }, // box on goal square
6017 { '+', EL_SOKOBAN_FIELD_PLAYER, EL_CUSTOM_7 }, // player on goal square
6018 { '_', EL_INVISIBLE_STEELWALL, EL_FROM_LEVEL_TEMPLATE }, // floor beyond border
6025 for (i = 0; sb_element_mapping[i].ascii != 0; i++)
6026 if (element_ascii == sb_element_mapping[i].ascii)
6027 return (use_ces ? sb_element_mapping[i].ce : sb_element_mapping[i].sb);
6029 return EL_UNDEFINED;
6032 static void SetLevelSettings_SB(struct LevelInfo *level)
6036 level->use_step_counter = TRUE;
6039 level->score[SC_TIME_BONUS] = 0;
6040 level->time_score_base = 1;
6041 level->rate_time_over_score = TRUE;
6044 level->auto_exit_sokoban = TRUE;
6047 static void LoadLevelFromFileInfo_SB(struct LevelInfo *level,
6048 struct LevelFileInfo *level_file_info,
6049 boolean level_info_only)
6051 char *filename = level_file_info->filename;
6052 char line[MAX_LINE_LEN], line_raw[MAX_LINE_LEN], previous_line[MAX_LINE_LEN];
6053 char last_comment[MAX_LINE_LEN];
6054 char level_name[MAX_LINE_LEN];
6057 int num_levels_to_skip = level_file_info->nr - leveldir_current->first_level;
6058 boolean read_continued_line = FALSE;
6059 boolean reading_playfield = FALSE;
6060 boolean got_valid_playfield_line = FALSE;
6061 boolean invalid_playfield_char = FALSE;
6062 boolean load_xsb_to_ces = check_special_flags("load_xsb_to_ces");
6063 int file_level_nr = 0;
6065 int x = 0, y = 0; // initialized to make compilers happy
6067 last_comment[0] = '\0';
6068 level_name[0] = '\0';
6070 if (!(file = openFile(filename, MODE_READ)))
6072 level->no_valid_file = TRUE;
6074 if (!level_info_only)
6075 Warn("cannot read level '%s' -- using empty level", filename);
6080 while (!checkEndOfFile(file))
6082 // level successfully read, but next level may follow here
6083 if (!got_valid_playfield_line && reading_playfield)
6085 // read playfield from single level file -- skip remaining file
6086 if (!level_file_info->packed)
6089 if (file_level_nr >= num_levels_to_skip)
6094 last_comment[0] = '\0';
6095 level_name[0] = '\0';
6097 reading_playfield = FALSE;
6100 got_valid_playfield_line = FALSE;
6102 // read next line of input file
6103 if (!getStringFromFile(file, line, MAX_LINE_LEN))
6106 // check if line was completely read and is terminated by line break
6107 if (strlen(line) > 0 && line[strlen(line) - 1] == '\n')
6110 // cut trailing line break (this can be newline and/or carriage return)
6111 for (line_ptr = &line[strlen(line)]; line_ptr >= line; line_ptr--)
6112 if ((*line_ptr == '\n' || *line_ptr == '\r') && *(line_ptr + 1) == '\0')
6115 // copy raw input line for later use (mainly debugging output)
6116 strcpy(line_raw, line);
6118 if (read_continued_line)
6120 // append new line to existing line, if there is enough space
6121 if (strlen(previous_line) + strlen(line_ptr) < MAX_LINE_LEN)
6122 strcat(previous_line, line_ptr);
6124 strcpy(line, previous_line); // copy storage buffer to line
6126 read_continued_line = FALSE;
6129 // if the last character is '\', continue at next line
6130 if (strlen(line) > 0 && line[strlen(line) - 1] == '\\')
6132 line[strlen(line) - 1] = '\0'; // cut off trailing backslash
6133 strcpy(previous_line, line); // copy line to storage buffer
6135 read_continued_line = TRUE;
6141 if (line[0] == '\0')
6144 // extract comment text from comment line
6147 for (line_ptr = line; *line_ptr; line_ptr++)
6148 if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != ';')
6151 strcpy(last_comment, line_ptr);
6156 // extract level title text from line containing level title
6157 if (line[0] == '\'')
6159 strcpy(level_name, &line[1]);
6161 if (strlen(level_name) > 0 && level_name[strlen(level_name) - 1] == '\'')
6162 level_name[strlen(level_name) - 1] = '\0';
6167 // skip lines containing only spaces (or empty lines)
6168 for (line_ptr = line; *line_ptr; line_ptr++)
6169 if (*line_ptr != ' ')
6171 if (*line_ptr == '\0')
6174 // at this point, we have found a line containing part of a playfield
6176 got_valid_playfield_line = TRUE;
6178 if (!reading_playfield)
6180 reading_playfield = TRUE;
6181 invalid_playfield_char = FALSE;
6183 for (x = 0; x < MAX_LEV_FIELDX; x++)
6184 for (y = 0; y < MAX_LEV_FIELDY; y++)
6185 level->field[x][y] = getMappedElement_SB(' ', load_xsb_to_ces);
6190 // start with topmost tile row
6194 // skip playfield line if larger row than allowed
6195 if (y >= MAX_LEV_FIELDY)
6198 // start with leftmost tile column
6201 // read playfield elements from line
6202 for (line_ptr = line; *line_ptr; line_ptr++)
6204 int mapped_sb_element = getMappedElement_SB(*line_ptr, load_xsb_to_ces);
6206 // stop parsing playfield line if larger column than allowed
6207 if (x >= MAX_LEV_FIELDX)
6210 if (mapped_sb_element == EL_UNDEFINED)
6212 invalid_playfield_char = TRUE;
6217 level->field[x][y] = mapped_sb_element;
6219 // continue with next tile column
6222 level->fieldx = MAX(x, level->fieldx);
6225 if (invalid_playfield_char)
6227 // if first playfield line, treat invalid lines as comment lines
6229 reading_playfield = FALSE;
6234 // continue with next tile row
6242 level->fieldx = MIN(MAX(MIN_LEV_FIELDX, level->fieldx), MAX_LEV_FIELDX);
6243 level->fieldy = MIN(MAX(MIN_LEV_FIELDY, level->fieldy), MAX_LEV_FIELDY);
6245 if (!reading_playfield)
6247 level->no_valid_file = TRUE;
6249 Warn("cannot read level '%s' -- using empty level", filename);
6254 if (*level_name != '\0')
6256 strncpy(level->name, level_name, MAX_LEVEL_NAME_LEN);
6257 level->name[MAX_LEVEL_NAME_LEN] = '\0';
6259 else if (*last_comment != '\0')
6261 strncpy(level->name, last_comment, MAX_LEVEL_NAME_LEN);
6262 level->name[MAX_LEVEL_NAME_LEN] = '\0';
6266 sprintf(level->name, "--> Level %d <--", level_file_info->nr);
6269 // set all empty fields beyond the border walls to invisible steel wall
6270 for (y = 0; y < level->fieldy; y++) for (x = 0; x < level->fieldx; x++)
6272 if ((x == 0 || x == level->fieldx - 1 ||
6273 y == 0 || y == level->fieldy - 1) &&
6274 level->field[x][y] == getMappedElement_SB(' ', load_xsb_to_ces))
6275 FloodFillLevel(x, y, getMappedElement_SB('_', load_xsb_to_ces),
6276 level->field, level->fieldx, level->fieldy);
6279 // set special level settings for Sokoban levels
6280 SetLevelSettings_SB(level);
6282 if (load_xsb_to_ces)
6284 // special global settings can now be set in level template
6285 level->use_custom_template = TRUE;
6290 // -------------------------------------------------------------------------
6291 // functions for handling native levels
6292 // -------------------------------------------------------------------------
6294 static void LoadLevelFromFileInfo_EM(struct LevelInfo *level,
6295 struct LevelFileInfo *level_file_info,
6296 boolean level_info_only)
6298 if (!LoadNativeLevel_EM(level_file_info->filename, level_info_only))
6299 level->no_valid_file = TRUE;
6302 static void LoadLevelFromFileInfo_SP(struct LevelInfo *level,
6303 struct LevelFileInfo *level_file_info,
6304 boolean level_info_only)
6308 // determine position of requested level inside level package
6309 if (level_file_info->packed)
6310 pos = level_file_info->nr - leveldir_current->first_level;
6312 if (!LoadNativeLevel_SP(level_file_info->filename, pos, level_info_only))
6313 level->no_valid_file = TRUE;
6316 static void LoadLevelFromFileInfo_MM(struct LevelInfo *level,
6317 struct LevelFileInfo *level_file_info,
6318 boolean level_info_only)
6320 if (!LoadNativeLevel_MM(level_file_info->filename, level_info_only))
6321 level->no_valid_file = TRUE;
6324 void CopyNativeLevel_RND_to_Native(struct LevelInfo *level)
6326 if (level->game_engine_type == GAME_ENGINE_TYPE_EM)
6327 CopyNativeLevel_RND_to_EM(level);
6328 else if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
6329 CopyNativeLevel_RND_to_SP(level);
6330 else if (level->game_engine_type == GAME_ENGINE_TYPE_MM)
6331 CopyNativeLevel_RND_to_MM(level);
6334 void CopyNativeLevel_Native_to_RND(struct LevelInfo *level)
6336 if (level->game_engine_type == GAME_ENGINE_TYPE_EM)
6337 CopyNativeLevel_EM_to_RND(level);
6338 else if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
6339 CopyNativeLevel_SP_to_RND(level);
6340 else if (level->game_engine_type == GAME_ENGINE_TYPE_MM)
6341 CopyNativeLevel_MM_to_RND(level);
6344 void SaveNativeLevel(struct LevelInfo *level)
6346 if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
6348 char *basename = getSingleLevelBasenameExt(level->file_info.nr, "sp");
6349 char *filename = getLevelFilenameFromBasename(basename);
6351 CopyNativeLevel_RND_to_SP(level);
6352 CopyNativeTape_RND_to_SP(level);
6354 SaveNativeLevel_SP(filename);
6359 // ----------------------------------------------------------------------------
6360 // functions for loading generic level
6361 // ----------------------------------------------------------------------------
6363 static void LoadLevelFromFileInfo(struct LevelInfo *level,
6364 struct LevelFileInfo *level_file_info,
6365 boolean level_info_only)
6367 // always start with reliable default values
6368 setLevelInfoToDefaults(level, level_info_only, TRUE);
6370 switch (level_file_info->type)
6372 case LEVEL_FILE_TYPE_RND:
6373 LoadLevelFromFileInfo_RND(level, level_file_info, level_info_only);
6376 case LEVEL_FILE_TYPE_EM:
6377 LoadLevelFromFileInfo_EM(level, level_file_info, level_info_only);
6378 level->game_engine_type = GAME_ENGINE_TYPE_EM;
6381 case LEVEL_FILE_TYPE_SP:
6382 LoadLevelFromFileInfo_SP(level, level_file_info, level_info_only);
6383 level->game_engine_type = GAME_ENGINE_TYPE_SP;
6386 case LEVEL_FILE_TYPE_MM:
6387 LoadLevelFromFileInfo_MM(level, level_file_info, level_info_only);
6388 level->game_engine_type = GAME_ENGINE_TYPE_MM;
6391 case LEVEL_FILE_TYPE_DC:
6392 LoadLevelFromFileInfo_DC(level, level_file_info, level_info_only);
6395 case LEVEL_FILE_TYPE_SB:
6396 LoadLevelFromFileInfo_SB(level, level_file_info, level_info_only);
6400 LoadLevelFromFileInfo_RND(level, level_file_info, level_info_only);
6404 // if level file is invalid, restore level structure to default values
6405 if (level->no_valid_file)
6406 setLevelInfoToDefaults(level, level_info_only, FALSE);
6408 if (level->game_engine_type == GAME_ENGINE_TYPE_UNKNOWN)
6409 level->game_engine_type = GAME_ENGINE_TYPE_RND;
6411 if (level_file_info->type != LEVEL_FILE_TYPE_RND)
6412 CopyNativeLevel_Native_to_RND(level);
6415 void LoadLevelFromFilename(struct LevelInfo *level, char *filename)
6417 static struct LevelFileInfo level_file_info;
6419 // always start with reliable default values
6420 setFileInfoToDefaults(&level_file_info);
6422 level_file_info.nr = 0; // unknown level number
6423 level_file_info.type = LEVEL_FILE_TYPE_RND; // no others supported yet
6425 setString(&level_file_info.filename, filename);
6427 LoadLevelFromFileInfo(level, &level_file_info, FALSE);
6430 static void LoadLevel_InitVersion(struct LevelInfo *level)
6434 if (leveldir_current == NULL) // only when dumping level
6437 // all engine modifications also valid for levels which use latest engine
6438 if (level->game_version < VERSION_IDENT(3,2,0,5))
6440 // time bonus score was given for 10 s instead of 1 s before 3.2.0-5
6441 level->time_score_base = 10;
6444 if (leveldir_current->latest_engine)
6446 // ---------- use latest game engine --------------------------------------
6448 /* For all levels which are forced to use the latest game engine version
6449 (normally all but user contributed, private and undefined levels), set
6450 the game engine version to the actual version; this allows for actual
6451 corrections in the game engine to take effect for existing, converted
6452 levels (from "classic" or other existing games) to make the emulation
6453 of the corresponding game more accurate, while (hopefully) not breaking
6454 existing levels created from other players. */
6456 level->game_version = GAME_VERSION_ACTUAL;
6458 /* Set special EM style gems behaviour: EM style gems slip down from
6459 normal, steel and growing wall. As this is a more fundamental change,
6460 it seems better to set the default behaviour to "off" (as it is more
6461 natural) and make it configurable in the level editor (as a property
6462 of gem style elements). Already existing converted levels (neither
6463 private nor contributed levels) are changed to the new behaviour. */
6465 if (level->file_version < FILE_VERSION_2_0)
6466 level->em_slippery_gems = TRUE;
6471 // ---------- use game engine the level was created with --------------------
6473 /* For all levels which are not forced to use the latest game engine
6474 version (normally user contributed, private and undefined levels),
6475 use the version of the game engine the levels were created for.
6477 Since 2.0.1, the game engine version is now directly stored
6478 in the level file (chunk "VERS"), so there is no need anymore
6479 to set the game version from the file version (except for old,
6480 pre-2.0 levels, where the game version is still taken from the
6481 file format version used to store the level -- see above). */
6483 // player was faster than enemies in 1.0.0 and before
6484 if (level->file_version == FILE_VERSION_1_0)
6485 for (i = 0; i < MAX_PLAYERS; i++)
6486 level->initial_player_stepsize[i] = STEPSIZE_FAST;
6488 // default behaviour for EM style gems was "slippery" only in 2.0.1
6489 if (level->game_version == VERSION_IDENT(2,0,1,0))
6490 level->em_slippery_gems = TRUE;
6492 // springs could be pushed over pits before (pre-release version) 2.2.0
6493 if (level->game_version < VERSION_IDENT(2,2,0,0))
6494 level->use_spring_bug = TRUE;
6496 if (level->game_version < VERSION_IDENT(3,2,0,5))
6498 // time orb caused limited time in endless time levels before 3.2.0-5
6499 level->use_time_orb_bug = TRUE;
6501 // default behaviour for snapping was "no snap delay" before 3.2.0-5
6502 level->block_snap_field = FALSE;
6504 // extra time score was same value as time left score before 3.2.0-5
6505 level->extra_time_score = level->score[SC_TIME_BONUS];
6508 if (level->game_version < VERSION_IDENT(3,2,0,7))
6510 // default behaviour for snapping was "not continuous" before 3.2.0-7
6511 level->continuous_snapping = FALSE;
6514 // only few elements were able to actively move into acid before 3.1.0
6515 // trigger settings did not exist before 3.1.0; set to default "any"
6516 if (level->game_version < VERSION_IDENT(3,1,0,0))
6518 // correct "can move into acid" settings (all zero in old levels)
6520 level->can_move_into_acid_bits = 0; // nothing can move into acid
6521 level->dont_collide_with_bits = 0; // nothing is deadly when colliding
6523 setMoveIntoAcidProperty(level, EL_ROBOT, TRUE);
6524 setMoveIntoAcidProperty(level, EL_SATELLITE, TRUE);
6525 setMoveIntoAcidProperty(level, EL_PENGUIN, TRUE);
6526 setMoveIntoAcidProperty(level, EL_BALLOON, TRUE);
6528 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6529 SET_PROPERTY(EL_CUSTOM_START + i, EP_CAN_MOVE_INTO_ACID, TRUE);
6531 // correct trigger settings (stored as zero == "none" in old levels)
6533 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6535 int element = EL_CUSTOM_START + i;
6536 struct ElementInfo *ei = &element_info[element];
6538 for (j = 0; j < ei->num_change_pages; j++)
6540 struct ElementChangeInfo *change = &ei->change_page[j];
6542 change->trigger_player = CH_PLAYER_ANY;
6543 change->trigger_page = CH_PAGE_ANY;
6548 // try to detect and fix "Snake Bite" levels, which are broken with 3.2.0
6550 int element = EL_CUSTOM_256;
6551 struct ElementInfo *ei = &element_info[element];
6552 struct ElementChangeInfo *change = &ei->change_page[0];
6554 /* This is needed to fix a problem that was caused by a bugfix in function
6555 game.c/CreateFieldExt() introduced with 3.2.0 that corrects the behaviour
6556 when a custom element changes to EL_SOKOBAN_FIELD_PLAYER (before, it did
6557 not replace walkable elements, but instead just placed the player on it,
6558 without placing the Sokoban field under the player). Unfortunately, this
6559 breaks "Snake Bite" style levels when the snake is halfway through a door
6560 that just closes (the snake head is still alive and can be moved in this
6561 case). This can be fixed by replacing the EL_SOKOBAN_FIELD_PLAYER by the
6562 player (without Sokoban element) which then gets killed as designed). */
6564 if ((strncmp(leveldir_current->identifier, "snake_bite", 10) == 0 ||
6565 strncmp(ei->description, "pause b4 death", 14) == 0) &&
6566 change->target_element == EL_SOKOBAN_FIELD_PLAYER)
6567 change->target_element = EL_PLAYER_1;
6570 // try to detect and fix "Zelda" style levels, which are broken with 3.2.5
6571 if (level->game_version < VERSION_IDENT(3,2,5,0))
6573 /* This is needed to fix a problem that was caused by a bugfix in function
6574 game.c/CheckTriggeredElementChangeExt() introduced with 3.2.5 that
6575 corrects the behaviour when a custom element changes to another custom
6576 element with a higher element number that has change actions defined.
6577 Normally, only one change per frame is allowed for custom elements.
6578 Therefore, it is checked if a custom element already changed in the
6579 current frame; if it did, subsequent changes are suppressed.
6580 Unfortunately, this is only checked for element changes, but not for
6581 change actions, which are still executed. As the function above loops
6582 through all custom elements from lower to higher, an element change
6583 resulting in a lower CE number won't be checked again, while a target
6584 element with a higher number will also be checked, and potential change
6585 actions will get executed for this CE, too (which is wrong), while
6586 further changes are ignored (which is correct). As this bugfix breaks
6587 Zelda II (and introduces graphical bugs to Zelda I, and also breaks a
6588 few other levels like Alan Bond's "FMV"), allow the previous, incorrect
6589 behaviour for existing levels and tapes that make use of this bug */
6591 level->use_action_after_change_bug = TRUE;
6594 // not centering level after relocating player was default only in 3.2.3
6595 if (level->game_version == VERSION_IDENT(3,2,3,0)) // (no pre-releases)
6596 level->shifted_relocation = TRUE;
6598 // EM style elements always chain-exploded in R'n'D engine before 3.2.6
6599 if (level->game_version < VERSION_IDENT(3,2,6,0))
6600 level->em_explodes_by_fire = TRUE;
6602 // levels were solved by the first player entering an exit up to 4.1.0.0
6603 if (level->game_version <= VERSION_IDENT(4,1,0,0))
6604 level->solved_by_one_player = TRUE;
6606 // game logic of "game of life" and "biomaze" was buggy before 4.1.1.1
6607 if (level->game_version < VERSION_IDENT(4,1,1,1))
6608 level->use_life_bugs = TRUE;
6610 // only Sokoban fields (but not objects) had to be solved before 4.1.1.1
6611 if (level->game_version < VERSION_IDENT(4,1,1,1))
6612 level->sb_objects_needed = FALSE;
6614 // CE actions were triggered by unfinished digging/collecting up to 4.2.2.0
6615 if (level->game_version <= VERSION_IDENT(4,2,2,0))
6616 level->finish_dig_collect = FALSE;
6618 // CE changing to player was kept under the player if walkable up to 4.2.3.1
6619 if (level->game_version <= VERSION_IDENT(4,2,3,1))
6620 level->keep_walkable_ce = TRUE;
6623 static void LoadLevel_InitSettings_SB(struct LevelInfo *level)
6625 boolean is_sokoban_level = TRUE; // unless non-Sokoban elements found
6628 // check if this level is (not) a Sokoban level
6629 for (y = 0; y < level->fieldy; y++)
6630 for (x = 0; x < level->fieldx; x++)
6631 if (!IS_SB_ELEMENT(Tile[x][y]))
6632 is_sokoban_level = FALSE;
6634 if (is_sokoban_level)
6636 // set special level settings for Sokoban levels
6637 SetLevelSettings_SB(level);
6641 static void LoadLevel_InitSettings(struct LevelInfo *level)
6643 // adjust level settings for (non-native) Sokoban-style levels
6644 LoadLevel_InitSettings_SB(level);
6646 // rename levels with title "nameless level" or if renaming is forced
6647 if (leveldir_current->empty_level_name != NULL &&
6648 (strEqual(level->name, NAMELESS_LEVEL_NAME) ||
6649 leveldir_current->force_level_name))
6650 snprintf(level->name, MAX_LEVEL_NAME_LEN + 1,
6651 leveldir_current->empty_level_name, level_nr);
6654 static void LoadLevel_InitStandardElements(struct LevelInfo *level)
6658 // map elements that have changed in newer versions
6659 level->amoeba_content = getMappedElementByVersion(level->amoeba_content,
6660 level->game_version);
6661 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
6662 for (x = 0; x < 3; x++)
6663 for (y = 0; y < 3; y++)
6664 level->yamyam_content[i].e[x][y] =
6665 getMappedElementByVersion(level->yamyam_content[i].e[x][y],
6666 level->game_version);
6670 static void LoadLevel_InitCustomElements(struct LevelInfo *level)
6674 // map custom element change events that have changed in newer versions
6675 // (these following values were accidentally changed in version 3.0.1)
6676 // (this seems to be needed only for 'ab_levelset3' and 'ab_levelset4')
6677 if (level->game_version <= VERSION_IDENT(3,0,0,0))
6679 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6681 int element = EL_CUSTOM_START + i;
6683 // order of checking and copying events to be mapped is important
6684 // (do not change the start and end value -- they are constant)
6685 for (j = CE_BY_OTHER_ACTION; j >= CE_VALUE_GETS_ZERO; j--)
6687 if (HAS_CHANGE_EVENT(element, j - 2))
6689 SET_CHANGE_EVENT(element, j - 2, FALSE);
6690 SET_CHANGE_EVENT(element, j, TRUE);
6694 // order of checking and copying events to be mapped is important
6695 // (do not change the start and end value -- they are constant)
6696 for (j = CE_PLAYER_COLLECTS_X; j >= CE_HITTING_SOMETHING; j--)
6698 if (HAS_CHANGE_EVENT(element, j - 1))
6700 SET_CHANGE_EVENT(element, j - 1, FALSE);
6701 SET_CHANGE_EVENT(element, j, TRUE);
6707 // initialize "can_change" field for old levels with only one change page
6708 if (level->game_version <= VERSION_IDENT(3,0,2,0))
6710 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6712 int element = EL_CUSTOM_START + i;
6714 if (CAN_CHANGE(element))
6715 element_info[element].change->can_change = TRUE;
6719 // correct custom element values (for old levels without these options)
6720 if (level->game_version < VERSION_IDENT(3,1,1,0))
6722 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6724 int element = EL_CUSTOM_START + i;
6725 struct ElementInfo *ei = &element_info[element];
6727 if (ei->access_direction == MV_NO_DIRECTION)
6728 ei->access_direction = MV_ALL_DIRECTIONS;
6732 // correct custom element values (fix invalid values for all versions)
6735 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6737 int element = EL_CUSTOM_START + i;
6738 struct ElementInfo *ei = &element_info[element];
6740 for (j = 0; j < ei->num_change_pages; j++)
6742 struct ElementChangeInfo *change = &ei->change_page[j];
6744 if (change->trigger_player == CH_PLAYER_NONE)
6745 change->trigger_player = CH_PLAYER_ANY;
6747 if (change->trigger_side == CH_SIDE_NONE)
6748 change->trigger_side = CH_SIDE_ANY;
6753 // initialize "can_explode" field for old levels which did not store this
6754 // !!! CHECK THIS -- "<= 3,1,0,0" IS PROBABLY WRONG !!!
6755 if (level->game_version <= VERSION_IDENT(3,1,0,0))
6757 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6759 int element = EL_CUSTOM_START + i;
6761 if (EXPLODES_1X1_OLD(element))
6762 element_info[element].explosion_type = EXPLODES_1X1;
6764 SET_PROPERTY(element, EP_CAN_EXPLODE, (EXPLODES_BY_FIRE(element) ||
6765 EXPLODES_SMASHED(element) ||
6766 EXPLODES_IMPACT(element)));
6770 // correct previously hard-coded move delay values for maze runner style
6771 if (level->game_version < VERSION_IDENT(3,1,1,0))
6773 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6775 int element = EL_CUSTOM_START + i;
6777 if (element_info[element].move_pattern & MV_MAZE_RUNNER_STYLE)
6779 // previously hard-coded and therefore ignored
6780 element_info[element].move_delay_fixed = 9;
6781 element_info[element].move_delay_random = 0;
6786 // set some other uninitialized values of custom elements in older levels
6787 if (level->game_version < VERSION_IDENT(3,1,0,0))
6789 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6791 int element = EL_CUSTOM_START + i;
6793 element_info[element].access_direction = MV_ALL_DIRECTIONS;
6795 element_info[element].explosion_delay = 17;
6796 element_info[element].ignition_delay = 8;
6800 // set mouse click change events to work for left/middle/right mouse button
6801 if (level->game_version < VERSION_IDENT(4,2,3,0))
6803 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6805 int element = EL_CUSTOM_START + i;
6806 struct ElementInfo *ei = &element_info[element];
6808 for (j = 0; j < ei->num_change_pages; j++)
6810 struct ElementChangeInfo *change = &ei->change_page[j];
6812 if (change->has_event[CE_CLICKED_BY_MOUSE] ||
6813 change->has_event[CE_PRESSED_BY_MOUSE] ||
6814 change->has_event[CE_MOUSE_CLICKED_ON_X] ||
6815 change->has_event[CE_MOUSE_PRESSED_ON_X])
6816 change->trigger_side = CH_SIDE_ANY;
6822 static void LoadLevel_InitElements(struct LevelInfo *level)
6824 LoadLevel_InitStandardElements(level);
6826 if (level->file_has_custom_elements)
6827 LoadLevel_InitCustomElements(level);
6829 // initialize element properties for level editor etc.
6830 InitElementPropertiesEngine(level->game_version);
6831 InitElementPropertiesGfxElement();
6834 static void LoadLevel_InitPlayfield(struct LevelInfo *level)
6838 // map elements that have changed in newer versions
6839 for (y = 0; y < level->fieldy; y++)
6840 for (x = 0; x < level->fieldx; x++)
6841 level->field[x][y] = getMappedElementByVersion(level->field[x][y],
6842 level->game_version);
6844 // clear unused playfield data (nicer if level gets resized in editor)
6845 for (x = 0; x < MAX_LEV_FIELDX; x++)
6846 for (y = 0; y < MAX_LEV_FIELDY; y++)
6847 if (x >= level->fieldx || y >= level->fieldy)
6848 level->field[x][y] = EL_EMPTY;
6850 // copy elements to runtime playfield array
6851 for (x = 0; x < MAX_LEV_FIELDX; x++)
6852 for (y = 0; y < MAX_LEV_FIELDY; y++)
6853 Tile[x][y] = level->field[x][y];
6855 // initialize level size variables for faster access
6856 lev_fieldx = level->fieldx;
6857 lev_fieldy = level->fieldy;
6859 // determine border element for this level
6860 if (level->file_info.type == LEVEL_FILE_TYPE_DC)
6861 BorderElement = EL_EMPTY; // (in editor, SetBorderElement() is used)
6866 static void LoadLevel_InitNativeEngines(struct LevelInfo *level)
6868 struct LevelFileInfo *level_file_info = &level->file_info;
6870 if (level_file_info->type == LEVEL_FILE_TYPE_RND)
6871 CopyNativeLevel_RND_to_Native(level);
6874 static void LoadLevelTemplate_LoadAndInit(void)
6876 LoadLevelFromFileInfo(&level_template, &level_template.file_info, FALSE);
6878 LoadLevel_InitVersion(&level_template);
6879 LoadLevel_InitElements(&level_template);
6880 LoadLevel_InitSettings(&level_template);
6882 ActivateLevelTemplate();
6885 void LoadLevelTemplate(int nr)
6887 if (!fileExists(getGlobalLevelTemplateFilename()))
6889 Warn("no level template found for this level");
6894 setLevelFileInfo(&level_template.file_info, nr);
6896 LoadLevelTemplate_LoadAndInit();
6899 static void LoadNetworkLevelTemplate(struct NetworkLevelInfo *network_level)
6901 copyLevelFileInfo(&network_level->tmpl_info, &level_template.file_info);
6903 LoadLevelTemplate_LoadAndInit();
6906 static void LoadLevel_LoadAndInit(struct NetworkLevelInfo *network_level)
6908 LoadLevelFromFileInfo(&level, &level.file_info, FALSE);
6910 if (level.use_custom_template)
6912 if (network_level != NULL)
6913 LoadNetworkLevelTemplate(network_level);
6915 LoadLevelTemplate(-1);
6918 LoadLevel_InitVersion(&level);
6919 LoadLevel_InitElements(&level);
6920 LoadLevel_InitPlayfield(&level);
6921 LoadLevel_InitSettings(&level);
6923 LoadLevel_InitNativeEngines(&level);
6926 void LoadLevel(int nr)
6928 SetLevelSetInfo(leveldir_current->identifier, nr);
6930 setLevelFileInfo(&level.file_info, nr);
6932 LoadLevel_LoadAndInit(NULL);
6935 void LoadLevelInfoOnly(int nr)
6937 setLevelFileInfo(&level.file_info, nr);
6939 LoadLevelFromFileInfo(&level, &level.file_info, TRUE);
6942 void LoadNetworkLevel(struct NetworkLevelInfo *network_level)
6944 SetLevelSetInfo(network_level->leveldir_identifier,
6945 network_level->file_info.nr);
6947 copyLevelFileInfo(&network_level->file_info, &level.file_info);
6949 LoadLevel_LoadAndInit(network_level);
6952 static int SaveLevel_VERS(FILE *file, struct LevelInfo *level)
6956 chunk_size += putFileVersion(file, level->file_version);
6957 chunk_size += putFileVersion(file, level->game_version);
6962 static int SaveLevel_DATE(FILE *file, struct LevelInfo *level)
6966 chunk_size += putFile16BitBE(file, level->creation_date.year);
6967 chunk_size += putFile8Bit(file, level->creation_date.month);
6968 chunk_size += putFile8Bit(file, level->creation_date.day);
6973 #if ENABLE_HISTORIC_CHUNKS
6974 static void SaveLevel_HEAD(FILE *file, struct LevelInfo *level)
6978 putFile8Bit(file, level->fieldx);
6979 putFile8Bit(file, level->fieldy);
6981 putFile16BitBE(file, level->time);
6982 putFile16BitBE(file, level->gems_needed);
6984 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
6985 putFile8Bit(file, level->name[i]);
6987 for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
6988 putFile8Bit(file, level->score[i]);
6990 for (i = 0; i < STD_ELEMENT_CONTENTS; i++)
6991 for (y = 0; y < 3; y++)
6992 for (x = 0; x < 3; x++)
6993 putFile8Bit(file, (level->encoding_16bit_yamyam ? EL_EMPTY :
6994 level->yamyam_content[i].e[x][y]));
6995 putFile8Bit(file, level->amoeba_speed);
6996 putFile8Bit(file, level->time_magic_wall);
6997 putFile8Bit(file, level->time_wheel);
6998 putFile8Bit(file, (level->encoding_16bit_amoeba ? EL_EMPTY :
6999 level->amoeba_content));
7000 putFile8Bit(file, (level->initial_player_stepsize == STEPSIZE_FAST ? 1 : 0));
7001 putFile8Bit(file, (level->initial_gravity ? 1 : 0));
7002 putFile8Bit(file, (level->encoding_16bit_field ? 1 : 0));
7003 putFile8Bit(file, (level->em_slippery_gems ? 1 : 0));
7005 putFile8Bit(file, (level->use_custom_template ? 1 : 0));
7007 putFile8Bit(file, (level->block_last_field ? 1 : 0));
7008 putFile8Bit(file, (level->sp_block_last_field ? 1 : 0));
7009 putFile32BitBE(file, level->can_move_into_acid_bits);
7010 putFile8Bit(file, level->dont_collide_with_bits);
7012 putFile8Bit(file, (level->use_spring_bug ? 1 : 0));
7013 putFile8Bit(file, (level->use_step_counter ? 1 : 0));
7015 putFile8Bit(file, (level->instant_relocation ? 1 : 0));
7016 putFile8Bit(file, (level->can_pass_to_walkable ? 1 : 0));
7017 putFile8Bit(file, (level->grow_into_diggable ? 1 : 0));
7019 putFile8Bit(file, level->game_engine_type);
7021 WriteUnusedBytesToFile(file, LEVEL_CHUNK_HEAD_UNUSED);
7025 static int SaveLevel_NAME(FILE *file, struct LevelInfo *level)
7030 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
7031 chunk_size += putFile8Bit(file, level->name[i]);
7036 static int SaveLevel_AUTH(FILE *file, struct LevelInfo *level)
7041 for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
7042 chunk_size += putFile8Bit(file, level->author[i]);
7047 #if ENABLE_HISTORIC_CHUNKS
7048 static int SaveLevel_BODY(FILE *file, struct LevelInfo *level)
7053 for (y = 0; y < level->fieldy; y++)
7054 for (x = 0; x < level->fieldx; x++)
7055 if (level->encoding_16bit_field)
7056 chunk_size += putFile16BitBE(file, level->field[x][y]);
7058 chunk_size += putFile8Bit(file, level->field[x][y]);
7064 static int SaveLevel_BODY(FILE *file, struct LevelInfo *level)
7069 for (y = 0; y < level->fieldy; y++)
7070 for (x = 0; x < level->fieldx; x++)
7071 chunk_size += putFile16BitBE(file, level->field[x][y]);
7076 #if ENABLE_HISTORIC_CHUNKS
7077 static void SaveLevel_CONT(FILE *file, struct LevelInfo *level)
7081 putFile8Bit(file, EL_YAMYAM);
7082 putFile8Bit(file, level->num_yamyam_contents);
7083 putFile8Bit(file, 0);
7084 putFile8Bit(file, 0);
7086 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
7087 for (y = 0; y < 3; y++)
7088 for (x = 0; x < 3; x++)
7089 if (level->encoding_16bit_field)
7090 putFile16BitBE(file, level->yamyam_content[i].e[x][y]);
7092 putFile8Bit(file, level->yamyam_content[i].e[x][y]);
7096 #if ENABLE_HISTORIC_CHUNKS
7097 static void SaveLevel_CNT2(FILE *file, struct LevelInfo *level, int element)
7100 int num_contents, content_xsize, content_ysize;
7101 int content_array[MAX_ELEMENT_CONTENTS][3][3];
7103 if (element == EL_YAMYAM)
7105 num_contents = level->num_yamyam_contents;
7109 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
7110 for (y = 0; y < 3; y++)
7111 for (x = 0; x < 3; x++)
7112 content_array[i][x][y] = level->yamyam_content[i].e[x][y];
7114 else if (element == EL_BD_AMOEBA)
7120 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
7121 for (y = 0; y < 3; y++)
7122 for (x = 0; x < 3; x++)
7123 content_array[i][x][y] = EL_EMPTY;
7124 content_array[0][0][0] = level->amoeba_content;
7128 // chunk header already written -- write empty chunk data
7129 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_SIZE);
7131 Warn("cannot save content for element '%d'", element);
7136 putFile16BitBE(file, element);
7137 putFile8Bit(file, num_contents);
7138 putFile8Bit(file, content_xsize);
7139 putFile8Bit(file, content_ysize);
7141 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_UNUSED);
7143 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
7144 for (y = 0; y < 3; y++)
7145 for (x = 0; x < 3; x++)
7146 putFile16BitBE(file, content_array[i][x][y]);
7150 #if ENABLE_HISTORIC_CHUNKS
7151 static int SaveLevel_CNT3(FILE *file, struct LevelInfo *level, int element)
7153 int envelope_nr = element - EL_ENVELOPE_1;
7154 int envelope_len = strlen(level->envelope_text[envelope_nr]) + 1;
7158 chunk_size += putFile16BitBE(file, element);
7159 chunk_size += putFile16BitBE(file, envelope_len);
7160 chunk_size += putFile8Bit(file, level->envelope_xsize[envelope_nr]);
7161 chunk_size += putFile8Bit(file, level->envelope_ysize[envelope_nr]);
7163 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT3_UNUSED);
7164 chunk_size += LEVEL_CHUNK_CNT3_UNUSED;
7166 for (i = 0; i < envelope_len; i++)
7167 chunk_size += putFile8Bit(file, level->envelope_text[envelope_nr][i]);
7173 #if ENABLE_HISTORIC_CHUNKS
7174 static void SaveLevel_CUS1(FILE *file, struct LevelInfo *level,
7175 int num_changed_custom_elements)
7179 putFile16BitBE(file, num_changed_custom_elements);
7181 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7183 int element = EL_CUSTOM_START + i;
7185 struct ElementInfo *ei = &element_info[element];
7187 if (ei->properties[EP_BITFIELD_BASE_NR] != EP_BITMASK_DEFAULT)
7189 if (check < num_changed_custom_elements)
7191 putFile16BitBE(file, element);
7192 putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
7199 if (check != num_changed_custom_elements) // should not happen
7200 Warn("inconsistent number of custom element properties");
7204 #if ENABLE_HISTORIC_CHUNKS
7205 static void SaveLevel_CUS2(FILE *file, struct LevelInfo *level,
7206 int num_changed_custom_elements)
7210 putFile16BitBE(file, num_changed_custom_elements);
7212 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7214 int element = EL_CUSTOM_START + i;
7216 if (element_info[element].change->target_element != EL_EMPTY_SPACE)
7218 if (check < num_changed_custom_elements)
7220 putFile16BitBE(file, element);
7221 putFile16BitBE(file, element_info[element].change->target_element);
7228 if (check != num_changed_custom_elements) // should not happen
7229 Warn("inconsistent number of custom target elements");
7233 #if ENABLE_HISTORIC_CHUNKS
7234 static void SaveLevel_CUS3(FILE *file, struct LevelInfo *level,
7235 int num_changed_custom_elements)
7237 int i, j, x, y, check = 0;
7239 putFile16BitBE(file, num_changed_custom_elements);
7241 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7243 int element = EL_CUSTOM_START + i;
7244 struct ElementInfo *ei = &element_info[element];
7246 if (ei->modified_settings)
7248 if (check < num_changed_custom_elements)
7250 putFile16BitBE(file, element);
7252 for (j = 0; j < MAX_ELEMENT_NAME_LEN; j++)
7253 putFile8Bit(file, ei->description[j]);
7255 putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
7257 // some free bytes for future properties and padding
7258 WriteUnusedBytesToFile(file, 7);
7260 putFile8Bit(file, ei->use_gfx_element);
7261 putFile16BitBE(file, ei->gfx_element_initial);
7263 putFile8Bit(file, ei->collect_score_initial);
7264 putFile8Bit(file, ei->collect_count_initial);
7266 putFile16BitBE(file, ei->push_delay_fixed);
7267 putFile16BitBE(file, ei->push_delay_random);
7268 putFile16BitBE(file, ei->move_delay_fixed);
7269 putFile16BitBE(file, ei->move_delay_random);
7271 putFile16BitBE(file, ei->move_pattern);
7272 putFile8Bit(file, ei->move_direction_initial);
7273 putFile8Bit(file, ei->move_stepsize);
7275 for (y = 0; y < 3; y++)
7276 for (x = 0; x < 3; x++)
7277 putFile16BitBE(file, ei->content.e[x][y]);
7279 putFile32BitBE(file, ei->change->events);
7281 putFile16BitBE(file, ei->change->target_element);
7283 putFile16BitBE(file, ei->change->delay_fixed);
7284 putFile16BitBE(file, ei->change->delay_random);
7285 putFile16BitBE(file, ei->change->delay_frames);
7287 putFile16BitBE(file, ei->change->initial_trigger_element);
7289 putFile8Bit(file, ei->change->explode);
7290 putFile8Bit(file, ei->change->use_target_content);
7291 putFile8Bit(file, ei->change->only_if_complete);
7292 putFile8Bit(file, ei->change->use_random_replace);
7294 putFile8Bit(file, ei->change->random_percentage);
7295 putFile8Bit(file, ei->change->replace_when);
7297 for (y = 0; y < 3; y++)
7298 for (x = 0; x < 3; x++)
7299 putFile16BitBE(file, ei->change->content.e[x][y]);
7301 putFile8Bit(file, ei->slippery_type);
7303 // some free bytes for future properties and padding
7304 WriteUnusedBytesToFile(file, LEVEL_CPART_CUS3_UNUSED);
7311 if (check != num_changed_custom_elements) // should not happen
7312 Warn("inconsistent number of custom element properties");
7316 #if ENABLE_HISTORIC_CHUNKS
7317 static void SaveLevel_CUS4(FILE *file, struct LevelInfo *level, int element)
7319 struct ElementInfo *ei = &element_info[element];
7322 // ---------- custom element base property values (96 bytes) ----------------
7324 putFile16BitBE(file, element);
7326 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
7327 putFile8Bit(file, ei->description[i]);
7329 putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
7331 WriteUnusedBytesToFile(file, 4); // reserved for more base properties
7333 putFile8Bit(file, ei->num_change_pages);
7335 putFile16BitBE(file, ei->ce_value_fixed_initial);
7336 putFile16BitBE(file, ei->ce_value_random_initial);
7337 putFile8Bit(file, ei->use_last_ce_value);
7339 putFile8Bit(file, ei->use_gfx_element);
7340 putFile16BitBE(file, ei->gfx_element_initial);
7342 putFile8Bit(file, ei->collect_score_initial);
7343 putFile8Bit(file, ei->collect_count_initial);
7345 putFile8Bit(file, ei->drop_delay_fixed);
7346 putFile8Bit(file, ei->push_delay_fixed);
7347 putFile8Bit(file, ei->drop_delay_random);
7348 putFile8Bit(file, ei->push_delay_random);
7349 putFile16BitBE(file, ei->move_delay_fixed);
7350 putFile16BitBE(file, ei->move_delay_random);
7352 // bits 0 - 15 of "move_pattern" ...
7353 putFile16BitBE(file, ei->move_pattern & 0xffff);
7354 putFile8Bit(file, ei->move_direction_initial);
7355 putFile8Bit(file, ei->move_stepsize);
7357 putFile8Bit(file, ei->slippery_type);
7359 for (y = 0; y < 3; y++)
7360 for (x = 0; x < 3; x++)
7361 putFile16BitBE(file, ei->content.e[x][y]);
7363 putFile16BitBE(file, ei->move_enter_element);
7364 putFile16BitBE(file, ei->move_leave_element);
7365 putFile8Bit(file, ei->move_leave_type);
7367 // ... bits 16 - 31 of "move_pattern" (not nice, but downward compatible)
7368 putFile16BitBE(file, (ei->move_pattern >> 16) & 0xffff);
7370 putFile8Bit(file, ei->access_direction);
7372 putFile8Bit(file, ei->explosion_delay);
7373 putFile8Bit(file, ei->ignition_delay);
7374 putFile8Bit(file, ei->explosion_type);
7376 // some free bytes for future custom property values and padding
7377 WriteUnusedBytesToFile(file, 1);
7379 // ---------- change page property values (48 bytes) ------------------------
7381 for (i = 0; i < ei->num_change_pages; i++)
7383 struct ElementChangeInfo *change = &ei->change_page[i];
7384 unsigned int event_bits;
7386 // bits 0 - 31 of "has_event[]" ...
7388 for (j = 0; j < MIN(NUM_CHANGE_EVENTS, 32); j++)
7389 if (change->has_event[j])
7390 event_bits |= (1u << j);
7391 putFile32BitBE(file, event_bits);
7393 putFile16BitBE(file, change->target_element);
7395 putFile16BitBE(file, change->delay_fixed);
7396 putFile16BitBE(file, change->delay_random);
7397 putFile16BitBE(file, change->delay_frames);
7399 putFile16BitBE(file, change->initial_trigger_element);
7401 putFile8Bit(file, change->explode);
7402 putFile8Bit(file, change->use_target_content);
7403 putFile8Bit(file, change->only_if_complete);
7404 putFile8Bit(file, change->use_random_replace);
7406 putFile8Bit(file, change->random_percentage);
7407 putFile8Bit(file, change->replace_when);
7409 for (y = 0; y < 3; y++)
7410 for (x = 0; x < 3; x++)
7411 putFile16BitBE(file, change->target_content.e[x][y]);
7413 putFile8Bit(file, change->can_change);
7415 putFile8Bit(file, change->trigger_side);
7417 putFile8Bit(file, change->trigger_player);
7418 putFile8Bit(file, (change->trigger_page == CH_PAGE_ANY ? CH_PAGE_ANY_FILE :
7419 log_2(change->trigger_page)));
7421 putFile8Bit(file, change->has_action);
7422 putFile8Bit(file, change->action_type);
7423 putFile8Bit(file, change->action_mode);
7424 putFile16BitBE(file, change->action_arg);
7426 // ... bits 32 - 39 of "has_event[]" (not nice, but downward compatible)
7428 for (j = 32; j < NUM_CHANGE_EVENTS; j++)
7429 if (change->has_event[j])
7430 event_bits |= (1u << (j - 32));
7431 putFile8Bit(file, event_bits);
7436 #if ENABLE_HISTORIC_CHUNKS
7437 static void SaveLevel_GRP1(FILE *file, struct LevelInfo *level, int element)
7439 struct ElementInfo *ei = &element_info[element];
7440 struct ElementGroupInfo *group = ei->group;
7443 putFile16BitBE(file, element);
7445 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
7446 putFile8Bit(file, ei->description[i]);
7448 putFile8Bit(file, group->num_elements);
7450 putFile8Bit(file, ei->use_gfx_element);
7451 putFile16BitBE(file, ei->gfx_element_initial);
7453 putFile8Bit(file, group->choice_mode);
7455 // some free bytes for future values and padding
7456 WriteUnusedBytesToFile(file, 3);
7458 for (i = 0; i < MAX_ELEMENTS_IN_GROUP; i++)
7459 putFile16BitBE(file, group->element[i]);
7463 static int SaveLevel_MicroChunk(FILE *file, struct LevelFileConfigInfo *entry,
7464 boolean write_element)
7466 int save_type = entry->save_type;
7467 int data_type = entry->data_type;
7468 int conf_type = entry->conf_type;
7469 int byte_mask = conf_type & CONF_MASK_BYTES;
7470 int element = entry->element;
7471 int default_value = entry->default_value;
7473 boolean modified = FALSE;
7475 if (byte_mask != CONF_MASK_MULTI_BYTES)
7477 void *value_ptr = entry->value;
7478 int value = (data_type == TYPE_BOOLEAN ? *(boolean *)value_ptr :
7481 // check if any settings have been modified before saving them
7482 if (value != default_value)
7485 // do not save if explicitly told or if unmodified default settings
7486 if ((save_type == SAVE_CONF_NEVER) ||
7487 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
7491 num_bytes += putFile16BitBE(file, element);
7493 num_bytes += putFile8Bit(file, conf_type);
7494 num_bytes += (byte_mask == CONF_MASK_1_BYTE ? putFile8Bit (file, value) :
7495 byte_mask == CONF_MASK_2_BYTE ? putFile16BitBE(file, value) :
7496 byte_mask == CONF_MASK_4_BYTE ? putFile32BitBE(file, value) :
7499 else if (data_type == TYPE_STRING)
7501 char *default_string = entry->default_string;
7502 char *string = (char *)(entry->value);
7503 int string_length = strlen(string);
7506 // check if any settings have been modified before saving them
7507 if (!strEqual(string, default_string))
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 += putFile16BitBE(file, string_length);
7521 for (i = 0; i < string_length; i++)
7522 num_bytes += putFile8Bit(file, string[i]);
7524 else if (data_type == TYPE_ELEMENT_LIST)
7526 int *element_array = (int *)(entry->value);
7527 int num_elements = *(int *)(entry->num_entities);
7530 // check if any settings have been modified before saving them
7531 for (i = 0; i < num_elements; i++)
7532 if (element_array[i] != default_value)
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, num_elements * CONF_ELEMENT_NUM_BYTES);
7546 for (i = 0; i < num_elements; i++)
7547 num_bytes += putFile16BitBE(file, element_array[i]);
7549 else if (data_type == TYPE_CONTENT_LIST)
7551 struct Content *content = (struct Content *)(entry->value);
7552 int num_contents = *(int *)(entry->num_entities);
7555 // check if any settings have been modified before saving them
7556 for (i = 0; i < num_contents; i++)
7557 for (y = 0; y < 3; y++)
7558 for (x = 0; x < 3; x++)
7559 if (content[i].e[x][y] != default_value)
7562 // do not save if explicitly told or if unmodified default settings
7563 if ((save_type == SAVE_CONF_NEVER) ||
7564 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
7568 num_bytes += putFile16BitBE(file, element);
7570 num_bytes += putFile8Bit(file, conf_type);
7571 num_bytes += putFile16BitBE(file, num_contents * CONF_CONTENT_NUM_BYTES);
7573 for (i = 0; i < num_contents; i++)
7574 for (y = 0; y < 3; y++)
7575 for (x = 0; x < 3; x++)
7576 num_bytes += putFile16BitBE(file, content[i].e[x][y]);
7582 static int SaveLevel_INFO(FILE *file, struct LevelInfo *level)
7587 li = *level; // copy level data into temporary buffer
7589 for (i = 0; chunk_config_INFO[i].data_type != -1; i++)
7590 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_INFO[i], FALSE);
7595 static int SaveLevel_ELEM(FILE *file, struct LevelInfo *level)
7600 li = *level; // copy level data into temporary buffer
7602 for (i = 0; chunk_config_ELEM[i].data_type != -1; i++)
7603 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_ELEM[i], TRUE);
7608 static int SaveLevel_NOTE(FILE *file, struct LevelInfo *level, int element)
7610 int envelope_nr = element - EL_ENVELOPE_1;
7614 chunk_size += putFile16BitBE(file, element);
7616 // copy envelope data into temporary buffer
7617 xx_envelope = level->envelope[envelope_nr];
7619 for (i = 0; chunk_config_NOTE[i].data_type != -1; i++)
7620 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_NOTE[i], FALSE);
7625 static int SaveLevel_CUSX(FILE *file, struct LevelInfo *level, int element)
7627 struct ElementInfo *ei = &element_info[element];
7631 chunk_size += putFile16BitBE(file, element);
7633 xx_ei = *ei; // copy element data into temporary buffer
7635 // set default description string for this specific element
7636 strcpy(xx_default_description, getDefaultElementDescription(ei));
7638 for (i = 0; chunk_config_CUSX_base[i].data_type != -1; i++)
7639 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_CUSX_base[i], FALSE);
7641 for (i = 0; i < ei->num_change_pages; i++)
7643 struct ElementChangeInfo *change = &ei->change_page[i];
7645 xx_current_change_page = i;
7647 xx_change = *change; // copy change data into temporary buffer
7650 setEventBitsFromEventFlags(change);
7652 for (j = 0; chunk_config_CUSX_change[j].data_type != -1; j++)
7653 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_CUSX_change[j],
7660 static int SaveLevel_GRPX(FILE *file, struct LevelInfo *level, int element)
7662 struct ElementInfo *ei = &element_info[element];
7663 struct ElementGroupInfo *group = ei->group;
7667 chunk_size += putFile16BitBE(file, element);
7669 xx_ei = *ei; // copy element data into temporary buffer
7670 xx_group = *group; // copy group data into temporary buffer
7672 // set default description string for this specific element
7673 strcpy(xx_default_description, getDefaultElementDescription(ei));
7675 for (i = 0; chunk_config_GRPX[i].data_type != -1; i++)
7676 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_GRPX[i], FALSE);
7681 static int SaveLevel_EMPX(FILE *file, struct LevelInfo *level, int element)
7683 struct ElementInfo *ei = &element_info[element];
7687 chunk_size += putFile16BitBE(file, element);
7689 xx_ei = *ei; // copy element data into temporary buffer
7691 for (i = 0; chunk_config_EMPX[i].data_type != -1; i++)
7692 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_EMPX[i], FALSE);
7697 static void SaveLevelFromFilename(struct LevelInfo *level, char *filename,
7698 boolean save_as_template)
7704 if (!(file = fopen(filename, MODE_WRITE)))
7706 Warn("cannot save level file '%s'", filename);
7711 level->file_version = FILE_VERSION_ACTUAL;
7712 level->game_version = GAME_VERSION_ACTUAL;
7714 level->creation_date = getCurrentDate();
7716 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
7717 putFileChunkBE(file, "CAVE", CHUNK_SIZE_NONE);
7719 chunk_size = SaveLevel_VERS(NULL, level);
7720 putFileChunkBE(file, "VERS", chunk_size);
7721 SaveLevel_VERS(file, level);
7723 chunk_size = SaveLevel_DATE(NULL, level);
7724 putFileChunkBE(file, "DATE", chunk_size);
7725 SaveLevel_DATE(file, level);
7727 chunk_size = SaveLevel_NAME(NULL, level);
7728 putFileChunkBE(file, "NAME", chunk_size);
7729 SaveLevel_NAME(file, level);
7731 chunk_size = SaveLevel_AUTH(NULL, level);
7732 putFileChunkBE(file, "AUTH", chunk_size);
7733 SaveLevel_AUTH(file, level);
7735 chunk_size = SaveLevel_INFO(NULL, level);
7736 putFileChunkBE(file, "INFO", chunk_size);
7737 SaveLevel_INFO(file, level);
7739 chunk_size = SaveLevel_BODY(NULL, level);
7740 putFileChunkBE(file, "BODY", chunk_size);
7741 SaveLevel_BODY(file, level);
7743 chunk_size = SaveLevel_ELEM(NULL, level);
7744 if (chunk_size > LEVEL_CHUNK_ELEM_UNCHANGED) // save if changed
7746 putFileChunkBE(file, "ELEM", chunk_size);
7747 SaveLevel_ELEM(file, level);
7750 for (i = 0; i < NUM_ENVELOPES; i++)
7752 int element = EL_ENVELOPE_1 + i;
7754 chunk_size = SaveLevel_NOTE(NULL, level, element);
7755 if (chunk_size > LEVEL_CHUNK_NOTE_UNCHANGED) // save if changed
7757 putFileChunkBE(file, "NOTE", chunk_size);
7758 SaveLevel_NOTE(file, level, element);
7762 // if not using template level, check for non-default custom/group elements
7763 if (!level->use_custom_template || save_as_template)
7765 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7767 int element = EL_CUSTOM_START + i;
7769 chunk_size = SaveLevel_CUSX(NULL, level, element);
7770 if (chunk_size > LEVEL_CHUNK_CUSX_UNCHANGED) // save if changed
7772 putFileChunkBE(file, "CUSX", chunk_size);
7773 SaveLevel_CUSX(file, level, element);
7777 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
7779 int element = EL_GROUP_START + i;
7781 chunk_size = SaveLevel_GRPX(NULL, level, element);
7782 if (chunk_size > LEVEL_CHUNK_GRPX_UNCHANGED) // save if changed
7784 putFileChunkBE(file, "GRPX", chunk_size);
7785 SaveLevel_GRPX(file, level, element);
7789 for (i = 0; i < NUM_EMPTY_ELEMENTS_ALL; i++)
7791 int element = GET_EMPTY_ELEMENT(i);
7793 chunk_size = SaveLevel_EMPX(NULL, level, element);
7794 if (chunk_size > LEVEL_CHUNK_EMPX_UNCHANGED) // save if changed
7796 putFileChunkBE(file, "EMPX", chunk_size);
7797 SaveLevel_EMPX(file, level, element);
7804 SetFilePermissions(filename, PERMS_PRIVATE);
7807 void SaveLevel(int nr)
7809 char *filename = getDefaultLevelFilename(nr);
7811 SaveLevelFromFilename(&level, filename, FALSE);
7814 void SaveLevelTemplate(void)
7816 char *filename = getLocalLevelTemplateFilename();
7818 SaveLevelFromFilename(&level, filename, TRUE);
7821 boolean SaveLevelChecked(int nr)
7823 char *filename = getDefaultLevelFilename(nr);
7824 boolean new_level = !fileExists(filename);
7825 boolean level_saved = FALSE;
7827 if (new_level || Request("Save this level and kill the old?", REQ_ASK))
7832 Request("Level saved!", REQ_CONFIRM);
7840 void DumpLevel(struct LevelInfo *level)
7842 if (level->no_level_file || level->no_valid_file)
7844 Warn("cannot dump -- no valid level file found");
7850 Print("Level xxx (file version %08d, game version %08d)\n",
7851 level->file_version, level->game_version);
7854 Print("Level author: '%s'\n", level->author);
7855 Print("Level title: '%s'\n", level->name);
7857 Print("Playfield size: %d x %d\n", level->fieldx, level->fieldy);
7859 Print("Level time: %d seconds\n", level->time);
7860 Print("Gems needed: %d\n", level->gems_needed);
7862 Print("Time for magic wall: %d seconds\n", level->time_magic_wall);
7863 Print("Time for wheel: %d seconds\n", level->time_wheel);
7864 Print("Time for light: %d seconds\n", level->time_light);
7865 Print("Time for timegate: %d seconds\n", level->time_timegate);
7867 Print("Amoeba speed: %d\n", level->amoeba_speed);
7870 Print("EM style slippery gems: %s\n", (level->em_slippery_gems ? "yes" : "no"));
7871 Print("Player blocks last field: %s\n", (level->block_last_field ? "yes" : "no"));
7872 Print("SP player blocks last field: %s\n", (level->sp_block_last_field ? "yes" : "no"));
7873 Print("use spring bug: %s\n", (level->use_spring_bug ? "yes" : "no"));
7874 Print("use step counter: %s\n", (level->use_step_counter ? "yes" : "no"));
7875 Print("rate time over score: %s\n", (level->rate_time_over_score ? "yes" : "no"));
7881 for (i = 0; i < NUM_ENVELOPES; i++)
7883 char *text = level->envelope[i].text;
7884 int text_len = strlen(text);
7885 boolean has_text = FALSE;
7887 for (j = 0; j < text_len; j++)
7888 if (text[j] != ' ' && text[j] != '\n')
7894 Print("Envelope %d:\n'%s'\n", i + 1, text);
7902 void DumpLevels(void)
7904 static LevelDirTree *dumplevel_leveldir = NULL;
7906 dumplevel_leveldir = getTreeInfoFromIdentifier(leveldir_first,
7907 global.dumplevel_leveldir);
7909 if (dumplevel_leveldir == NULL)
7910 Fail("no such level identifier: '%s'", global.dumplevel_leveldir);
7912 if (global.dumplevel_level_nr < dumplevel_leveldir->first_level ||
7913 global.dumplevel_level_nr > dumplevel_leveldir->last_level)
7914 Fail("no such level number: %d", global.dumplevel_level_nr);
7916 leveldir_current = dumplevel_leveldir;
7918 LoadLevel(global.dumplevel_level_nr);
7925 // ============================================================================
7926 // tape file functions
7927 // ============================================================================
7929 static void setTapeInfoToDefaults(void)
7933 // always start with reliable default values (empty tape)
7936 // default values (also for pre-1.2 tapes) with only the first player
7937 tape.player_participates[0] = TRUE;
7938 for (i = 1; i < MAX_PLAYERS; i++)
7939 tape.player_participates[i] = FALSE;
7941 // at least one (default: the first) player participates in every tape
7942 tape.num_participating_players = 1;
7944 tape.property_bits = TAPE_PROPERTY_NONE;
7946 tape.level_nr = level_nr;
7948 tape.changed = FALSE;
7949 tape.solved = FALSE;
7951 tape.recording = FALSE;
7952 tape.playing = FALSE;
7953 tape.pausing = FALSE;
7955 tape.scr_fieldx = SCR_FIELDX_DEFAULT;
7956 tape.scr_fieldy = SCR_FIELDY_DEFAULT;
7958 tape.no_info_chunk = TRUE;
7959 tape.no_valid_file = FALSE;
7962 static int getTapePosSize(struct TapeInfo *tape)
7964 int tape_pos_size = 0;
7966 if (tape->use_key_actions)
7967 tape_pos_size += tape->num_participating_players;
7969 if (tape->use_mouse_actions)
7970 tape_pos_size += 3; // x and y position and mouse button mask
7972 tape_pos_size += 1; // tape action delay value
7974 return tape_pos_size;
7977 static void setTapeActionFlags(struct TapeInfo *tape, int value)
7979 tape->use_key_actions = FALSE;
7980 tape->use_mouse_actions = FALSE;
7982 if (value != TAPE_USE_MOUSE_ACTIONS_ONLY)
7983 tape->use_key_actions = TRUE;
7985 if (value != TAPE_USE_KEY_ACTIONS_ONLY)
7986 tape->use_mouse_actions = TRUE;
7989 static int getTapeActionValue(struct TapeInfo *tape)
7991 return (tape->use_key_actions &&
7992 tape->use_mouse_actions ? TAPE_USE_KEY_AND_MOUSE_ACTIONS :
7993 tape->use_key_actions ? TAPE_USE_KEY_ACTIONS_ONLY :
7994 tape->use_mouse_actions ? TAPE_USE_MOUSE_ACTIONS_ONLY :
7995 TAPE_ACTIONS_DEFAULT);
7998 static int LoadTape_VERS(File *file, int chunk_size, struct TapeInfo *tape)
8000 tape->file_version = getFileVersion(file);
8001 tape->game_version = getFileVersion(file);
8006 static int LoadTape_HEAD(File *file, int chunk_size, struct TapeInfo *tape)
8010 tape->random_seed = getFile32BitBE(file);
8011 tape->date = getFile32BitBE(file);
8012 tape->length = getFile32BitBE(file);
8014 // read header fields that are new since version 1.2
8015 if (tape->file_version >= FILE_VERSION_1_2)
8017 byte store_participating_players = getFile8Bit(file);
8020 // since version 1.2, tapes store which players participate in the tape
8021 tape->num_participating_players = 0;
8022 for (i = 0; i < MAX_PLAYERS; i++)
8024 tape->player_participates[i] = FALSE;
8026 if (store_participating_players & (1 << i))
8028 tape->player_participates[i] = TRUE;
8029 tape->num_participating_players++;
8033 setTapeActionFlags(tape, getFile8Bit(file));
8035 tape->property_bits = getFile8Bit(file);
8036 tape->solved = getFile8Bit(file);
8038 engine_version = getFileVersion(file);
8039 if (engine_version > 0)
8040 tape->engine_version = engine_version;
8042 tape->engine_version = tape->game_version;
8048 static int LoadTape_SCRN(File *file, int chunk_size, struct TapeInfo *tape)
8050 tape->scr_fieldx = getFile8Bit(file);
8051 tape->scr_fieldy = getFile8Bit(file);
8056 static int LoadTape_INFO(File *file, int chunk_size, struct TapeInfo *tape)
8058 char *level_identifier = NULL;
8059 int level_identifier_size;
8062 tape->no_info_chunk = FALSE;
8064 level_identifier_size = getFile16BitBE(file);
8066 level_identifier = checked_malloc(level_identifier_size);
8068 for (i = 0; i < level_identifier_size; i++)
8069 level_identifier[i] = getFile8Bit(file);
8071 strncpy(tape->level_identifier, level_identifier, MAX_FILENAME_LEN);
8072 tape->level_identifier[MAX_FILENAME_LEN] = '\0';
8074 checked_free(level_identifier);
8076 tape->level_nr = getFile16BitBE(file);
8078 chunk_size = 2 + level_identifier_size + 2;
8083 static int LoadTape_BODY(File *file, int chunk_size, struct TapeInfo *tape)
8086 int tape_pos_size = getTapePosSize(tape);
8087 int chunk_size_expected = tape_pos_size * tape->length;
8089 if (chunk_size_expected != chunk_size)
8091 ReadUnusedBytesFromFile(file, chunk_size);
8092 return chunk_size_expected;
8095 for (i = 0; i < tape->length; i++)
8097 if (i >= MAX_TAPE_LEN)
8099 Warn("tape truncated -- size exceeds maximum tape size %d",
8102 // tape too large; read and ignore remaining tape data from this chunk
8103 for (;i < tape->length; i++)
8104 ReadUnusedBytesFromFile(file, tape_pos_size);
8109 if (tape->use_key_actions)
8111 for (j = 0; j < MAX_PLAYERS; j++)
8113 tape->pos[i].action[j] = MV_NONE;
8115 if (tape->player_participates[j])
8116 tape->pos[i].action[j] = getFile8Bit(file);
8120 if (tape->use_mouse_actions)
8122 tape->pos[i].action[TAPE_ACTION_LX] = getFile8Bit(file);
8123 tape->pos[i].action[TAPE_ACTION_LY] = getFile8Bit(file);
8124 tape->pos[i].action[TAPE_ACTION_BUTTON] = getFile8Bit(file);
8127 tape->pos[i].delay = getFile8Bit(file);
8129 if (tape->file_version == FILE_VERSION_1_0)
8131 // eliminate possible diagonal moves in old tapes
8132 // this is only for backward compatibility
8134 byte joy_dir[4] = { JOY_LEFT, JOY_RIGHT, JOY_UP, JOY_DOWN };
8135 byte action = tape->pos[i].action[0];
8136 int k, num_moves = 0;
8138 for (k = 0; k<4; k++)
8140 if (action & joy_dir[k])
8142 tape->pos[i + num_moves].action[0] = joy_dir[k];
8144 tape->pos[i + num_moves].delay = 0;
8153 tape->length += num_moves;
8156 else if (tape->file_version < FILE_VERSION_2_0)
8158 // convert pre-2.0 tapes to new tape format
8160 if (tape->pos[i].delay > 1)
8163 tape->pos[i + 1] = tape->pos[i];
8164 tape->pos[i + 1].delay = 1;
8167 for (j = 0; j < MAX_PLAYERS; j++)
8168 tape->pos[i].action[j] = MV_NONE;
8169 tape->pos[i].delay--;
8176 if (checkEndOfFile(file))
8180 if (i != tape->length)
8181 chunk_size = tape_pos_size * i;
8186 static void LoadTape_SokobanSolution(char *filename)
8189 int move_delay = TILESIZE / level.initial_player_stepsize[0];
8191 if (!(file = openFile(filename, MODE_READ)))
8193 tape.no_valid_file = TRUE;
8198 while (!checkEndOfFile(file))
8200 unsigned char c = getByteFromFile(file);
8202 if (checkEndOfFile(file))
8209 tape.pos[tape.length].action[0] = MV_UP;
8210 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
8216 tape.pos[tape.length].action[0] = MV_DOWN;
8217 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
8223 tape.pos[tape.length].action[0] = MV_LEFT;
8224 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
8230 tape.pos[tape.length].action[0] = MV_RIGHT;
8231 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
8239 // ignore white-space characters
8243 tape.no_valid_file = TRUE;
8245 Warn("unsupported Sokoban solution file '%s' ['%d']", filename, c);
8253 if (tape.no_valid_file)
8256 tape.length_frames = GetTapeLengthFrames();
8257 tape.length_seconds = GetTapeLengthSeconds();
8260 void LoadTapeFromFilename(char *filename)
8262 char cookie[MAX_LINE_LEN];
8263 char chunk_name[CHUNK_ID_LEN + 1];
8267 // always start with reliable default values
8268 setTapeInfoToDefaults();
8270 if (strSuffix(filename, ".sln"))
8272 LoadTape_SokobanSolution(filename);
8277 if (!(file = openFile(filename, MODE_READ)))
8279 tape.no_valid_file = TRUE;
8284 getFileChunkBE(file, chunk_name, NULL);
8285 if (strEqual(chunk_name, "RND1"))
8287 getFile32BitBE(file); // not used
8289 getFileChunkBE(file, chunk_name, NULL);
8290 if (!strEqual(chunk_name, "TAPE"))
8292 tape.no_valid_file = TRUE;
8294 Warn("unknown format of tape file '%s'", filename);
8301 else // check for pre-2.0 file format with cookie string
8303 strcpy(cookie, chunk_name);
8304 if (getStringFromFile(file, &cookie[4], MAX_LINE_LEN - 4) == NULL)
8306 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
8307 cookie[strlen(cookie) - 1] = '\0';
8309 if (!checkCookieString(cookie, TAPE_COOKIE_TMPL))
8311 tape.no_valid_file = TRUE;
8313 Warn("unknown format of tape file '%s'", filename);
8320 if ((tape.file_version = getFileVersionFromCookieString(cookie)) == -1)
8322 tape.no_valid_file = TRUE;
8324 Warn("unsupported version of tape file '%s'", filename);
8331 // pre-2.0 tape files have no game version, so use file version here
8332 tape.game_version = tape.file_version;
8335 if (tape.file_version < FILE_VERSION_1_2)
8337 // tape files from versions before 1.2.0 without chunk structure
8338 LoadTape_HEAD(file, TAPE_CHUNK_HEAD_SIZE, &tape);
8339 LoadTape_BODY(file, 2 * tape.length, &tape);
8347 int (*loader)(File *, int, struct TapeInfo *);
8351 { "VERS", TAPE_CHUNK_VERS_SIZE, LoadTape_VERS },
8352 { "HEAD", TAPE_CHUNK_HEAD_SIZE, LoadTape_HEAD },
8353 { "SCRN", TAPE_CHUNK_SCRN_SIZE, LoadTape_SCRN },
8354 { "INFO", -1, LoadTape_INFO },
8355 { "BODY", -1, LoadTape_BODY },
8359 while (getFileChunkBE(file, chunk_name, &chunk_size))
8363 while (chunk_info[i].name != NULL &&
8364 !strEqual(chunk_name, chunk_info[i].name))
8367 if (chunk_info[i].name == NULL)
8369 Warn("unknown chunk '%s' in tape file '%s'",
8370 chunk_name, filename);
8372 ReadUnusedBytesFromFile(file, chunk_size);
8374 else if (chunk_info[i].size != -1 &&
8375 chunk_info[i].size != chunk_size)
8377 Warn("wrong size (%d) of chunk '%s' in tape file '%s'",
8378 chunk_size, chunk_name, filename);
8380 ReadUnusedBytesFromFile(file, chunk_size);
8384 // call function to load this tape chunk
8385 int chunk_size_expected =
8386 (chunk_info[i].loader)(file, chunk_size, &tape);
8388 // the size of some chunks cannot be checked before reading other
8389 // chunks first (like "HEAD" and "BODY") that contain some header
8390 // information, so check them here
8391 if (chunk_size_expected != chunk_size)
8393 Warn("wrong size (%d) of chunk '%s' in tape file '%s'",
8394 chunk_size, chunk_name, filename);
8402 tape.length_frames = GetTapeLengthFrames();
8403 tape.length_seconds = GetTapeLengthSeconds();
8406 Debug("files:LoadTapeFromFilename", "tape file version: %d",
8408 Debug("files:LoadTapeFromFilename", "tape game version: %d",
8410 Debug("files:LoadTapeFromFilename", "tape engine version: %d",
8411 tape.engine_version);
8415 void LoadTape(int nr)
8417 char *filename = getTapeFilename(nr);
8419 LoadTapeFromFilename(filename);
8422 void LoadSolutionTape(int nr)
8424 char *filename = getSolutionTapeFilename(nr);
8426 LoadTapeFromFilename(filename);
8428 if (TAPE_IS_EMPTY(tape) &&
8429 level.game_engine_type == GAME_ENGINE_TYPE_SP &&
8430 level.native_sp_level->demo.is_available)
8431 CopyNativeTape_SP_to_RND(&level);
8434 void LoadScoreTape(char *score_tape_basename, int nr)
8436 char *filename = getScoreTapeFilename(score_tape_basename, nr);
8438 LoadTapeFromFilename(filename);
8441 void LoadScoreCacheTape(char *score_tape_basename, int nr)
8443 char *filename = getScoreCacheTapeFilename(score_tape_basename, nr);
8445 LoadTapeFromFilename(filename);
8448 static boolean checkSaveTape_SCRN(struct TapeInfo *tape)
8450 // chunk required for team mode tapes with non-default screen size
8451 return (tape->num_participating_players > 1 &&
8452 (tape->scr_fieldx != SCR_FIELDX_DEFAULT ||
8453 tape->scr_fieldy != SCR_FIELDY_DEFAULT));
8456 static void SaveTape_VERS(FILE *file, struct TapeInfo *tape)
8458 putFileVersion(file, tape->file_version);
8459 putFileVersion(file, tape->game_version);
8462 static void SaveTape_HEAD(FILE *file, struct TapeInfo *tape)
8465 byte store_participating_players = 0;
8467 // set bits for participating players for compact storage
8468 for (i = 0; i < MAX_PLAYERS; i++)
8469 if (tape->player_participates[i])
8470 store_participating_players |= (1 << i);
8472 putFile32BitBE(file, tape->random_seed);
8473 putFile32BitBE(file, tape->date);
8474 putFile32BitBE(file, tape->length);
8476 putFile8Bit(file, store_participating_players);
8478 putFile8Bit(file, getTapeActionValue(tape));
8480 putFile8Bit(file, tape->property_bits);
8481 putFile8Bit(file, tape->solved);
8483 putFileVersion(file, tape->engine_version);
8486 static void SaveTape_SCRN(FILE *file, struct TapeInfo *tape)
8488 putFile8Bit(file, tape->scr_fieldx);
8489 putFile8Bit(file, tape->scr_fieldy);
8492 static void SaveTape_INFO(FILE *file, struct TapeInfo *tape)
8494 int level_identifier_size = strlen(tape->level_identifier) + 1;
8497 putFile16BitBE(file, level_identifier_size);
8499 for (i = 0; i < level_identifier_size; i++)
8500 putFile8Bit(file, tape->level_identifier[i]);
8502 putFile16BitBE(file, tape->level_nr);
8505 static void SaveTape_BODY(FILE *file, struct TapeInfo *tape)
8509 for (i = 0; i < tape->length; i++)
8511 if (tape->use_key_actions)
8513 for (j = 0; j < MAX_PLAYERS; j++)
8514 if (tape->player_participates[j])
8515 putFile8Bit(file, tape->pos[i].action[j]);
8518 if (tape->use_mouse_actions)
8520 putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_LX]);
8521 putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_LY]);
8522 putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_BUTTON]);
8525 putFile8Bit(file, tape->pos[i].delay);
8529 void SaveTapeToFilename(char *filename)
8533 int info_chunk_size;
8534 int body_chunk_size;
8536 if (!(file = fopen(filename, MODE_WRITE)))
8538 Warn("cannot save level recording file '%s'", filename);
8543 tape_pos_size = getTapePosSize(&tape);
8545 info_chunk_size = 2 + (strlen(tape.level_identifier) + 1) + 2;
8546 body_chunk_size = tape_pos_size * tape.length;
8548 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
8549 putFileChunkBE(file, "TAPE", CHUNK_SIZE_NONE);
8551 putFileChunkBE(file, "VERS", TAPE_CHUNK_VERS_SIZE);
8552 SaveTape_VERS(file, &tape);
8554 putFileChunkBE(file, "HEAD", TAPE_CHUNK_HEAD_SIZE);
8555 SaveTape_HEAD(file, &tape);
8557 if (checkSaveTape_SCRN(&tape))
8559 putFileChunkBE(file, "SCRN", TAPE_CHUNK_SCRN_SIZE);
8560 SaveTape_SCRN(file, &tape);
8563 putFileChunkBE(file, "INFO", info_chunk_size);
8564 SaveTape_INFO(file, &tape);
8566 putFileChunkBE(file, "BODY", body_chunk_size);
8567 SaveTape_BODY(file, &tape);
8571 SetFilePermissions(filename, PERMS_PRIVATE);
8574 static void SaveTapeExt(char *filename)
8578 tape.file_version = FILE_VERSION_ACTUAL;
8579 tape.game_version = GAME_VERSION_ACTUAL;
8581 tape.num_participating_players = 0;
8583 // count number of participating players
8584 for (i = 0; i < MAX_PLAYERS; i++)
8585 if (tape.player_participates[i])
8586 tape.num_participating_players++;
8588 SaveTapeToFilename(filename);
8590 tape.changed = FALSE;
8593 void SaveTape(int nr)
8595 char *filename = getTapeFilename(nr);
8597 InitTapeDirectory(leveldir_current->subdir);
8599 SaveTapeExt(filename);
8602 void SaveScoreTape(int nr)
8604 char *filename = getScoreTapeFilename(tape.score_tape_basename, nr);
8606 // used instead of "leveldir_current->subdir" (for network games)
8607 InitScoreTapeDirectory(levelset.identifier, nr);
8609 SaveTapeExt(filename);
8612 static boolean SaveTapeCheckedExt(int nr, char *msg_replace, char *msg_saved,
8613 unsigned int req_state_added)
8615 char *filename = getTapeFilename(nr);
8616 boolean new_tape = !fileExists(filename);
8617 boolean tape_saved = FALSE;
8619 if (new_tape || Request(msg_replace, REQ_ASK | req_state_added))
8624 Request(msg_saved, REQ_CONFIRM | req_state_added);
8632 boolean SaveTapeChecked(int nr)
8634 return SaveTapeCheckedExt(nr, "Replace old tape?", "Tape saved!", 0);
8637 boolean SaveTapeChecked_LevelSolved(int nr)
8639 return SaveTapeCheckedExt(nr, "Level solved! Replace old tape?",
8640 "Level solved! Tape saved!", REQ_STAY_OPEN);
8643 void DumpTape(struct TapeInfo *tape)
8645 int tape_frame_counter;
8648 if (tape->no_valid_file)
8650 Warn("cannot dump -- no valid tape file found");
8657 Print("Tape of Level %03d (file version %08d, game version %08d)\n",
8658 tape->level_nr, tape->file_version, tape->game_version);
8659 Print(" (effective engine version %08d)\n",
8660 tape->engine_version);
8661 Print("Level series identifier: '%s'\n", tape->level_identifier);
8663 Print("Solution tape: %s\n",
8664 tape->solved ? "yes" :
8665 tape->game_version < VERSION_IDENT(4,3,2,3) ? "unknown" : "no");
8667 Print("Special tape properties: ");
8668 if (tape->property_bits == TAPE_PROPERTY_NONE)
8670 if (tape->property_bits & TAPE_PROPERTY_EM_RANDOM_BUG)
8671 Print("[em_random_bug]");
8672 if (tape->property_bits & TAPE_PROPERTY_GAME_SPEED)
8673 Print("[game_speed]");
8674 if (tape->property_bits & TAPE_PROPERTY_PAUSE_MODE)
8676 if (tape->property_bits & TAPE_PROPERTY_SINGLE_STEP)
8677 Print("[single_step]");
8678 if (tape->property_bits & TAPE_PROPERTY_SNAPSHOT)
8679 Print("[snapshot]");
8680 if (tape->property_bits & TAPE_PROPERTY_REPLAYED)
8681 Print("[replayed]");
8682 if (tape->property_bits & TAPE_PROPERTY_TAS_KEYS)
8683 Print("[tas_keys]");
8684 if (tape->property_bits & TAPE_PROPERTY_SMALL_GRAPHICS)
8685 Print("[small_graphics]");
8688 int year2 = tape->date / 10000;
8689 int year4 = (year2 < 70 ? 2000 + year2 : 1900 + year2);
8690 int month_index_raw = (tape->date / 100) % 100;
8691 int month_index = month_index_raw % 12; // prevent invalid index
8692 int month = month_index + 1;
8693 int day = tape->date % 100;
8695 Print("Tape date: %04d-%02d-%02d\n", year4, month, day);
8699 tape_frame_counter = 0;
8701 for (i = 0; i < tape->length; i++)
8703 if (i >= MAX_TAPE_LEN)
8708 for (j = 0; j < MAX_PLAYERS; j++)
8710 if (tape->player_participates[j])
8712 int action = tape->pos[i].action[j];
8714 Print("%d:%02x ", j, action);
8715 Print("[%c%c%c%c|%c%c] - ",
8716 (action & JOY_LEFT ? '<' : ' '),
8717 (action & JOY_RIGHT ? '>' : ' '),
8718 (action & JOY_UP ? '^' : ' '),
8719 (action & JOY_DOWN ? 'v' : ' '),
8720 (action & JOY_BUTTON_1 ? '1' : ' '),
8721 (action & JOY_BUTTON_2 ? '2' : ' '));
8725 Print("(%03d) ", tape->pos[i].delay);
8726 Print("[%05d]\n", tape_frame_counter);
8728 tape_frame_counter += tape->pos[i].delay;
8734 void DumpTapes(void)
8736 static LevelDirTree *dumptape_leveldir = NULL;
8738 dumptape_leveldir = getTreeInfoFromIdentifier(leveldir_first,
8739 global.dumptape_leveldir);
8741 if (dumptape_leveldir == NULL)
8742 Fail("no such level identifier: '%s'", global.dumptape_leveldir);
8744 if (global.dumptape_level_nr < dumptape_leveldir->first_level ||
8745 global.dumptape_level_nr > dumptape_leveldir->last_level)
8746 Fail("no such level number: %d", global.dumptape_level_nr);
8748 leveldir_current = dumptape_leveldir;
8750 if (options.mytapes)
8751 LoadTape(global.dumptape_level_nr);
8753 LoadSolutionTape(global.dumptape_level_nr);
8761 // ============================================================================
8762 // score file functions
8763 // ============================================================================
8765 static void setScoreInfoToDefaultsExt(struct ScoreInfo *scores)
8769 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
8771 strcpy(scores->entry[i].tape_basename, UNDEFINED_FILENAME);
8772 strcpy(scores->entry[i].name, EMPTY_PLAYER_NAME);
8773 scores->entry[i].score = 0;
8774 scores->entry[i].time = 0;
8776 scores->entry[i].id = -1;
8777 strcpy(scores->entry[i].tape_date, UNKNOWN_NAME);
8778 strcpy(scores->entry[i].platform, UNKNOWN_NAME);
8779 strcpy(scores->entry[i].version, UNKNOWN_NAME);
8780 strcpy(scores->entry[i].country_name, UNKNOWN_NAME);
8781 strcpy(scores->entry[i].country_code, "??");
8784 scores->num_entries = 0;
8785 scores->last_added = -1;
8786 scores->last_added_local = -1;
8788 scores->updated = FALSE;
8789 scores->uploaded = FALSE;
8790 scores->tape_downloaded = FALSE;
8791 scores->force_last_added = FALSE;
8793 // The following values are intentionally not reset here:
8797 // - continue_playing
8798 // - continue_on_return
8801 static void setScoreInfoToDefaults(void)
8803 setScoreInfoToDefaultsExt(&scores);
8806 static void setServerScoreInfoToDefaults(void)
8808 setScoreInfoToDefaultsExt(&server_scores);
8811 static void LoadScore_OLD(int nr)
8814 char *filename = getScoreFilename(nr);
8815 char cookie[MAX_LINE_LEN];
8816 char line[MAX_LINE_LEN];
8820 if (!(file = fopen(filename, MODE_READ)))
8823 // check file identifier
8824 if (fgets(cookie, MAX_LINE_LEN, file) == NULL)
8826 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
8827 cookie[strlen(cookie) - 1] = '\0';
8829 if (!checkCookieString(cookie, SCORE_COOKIE_TMPL))
8831 Warn("unknown format of score file '%s'", filename);
8838 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
8840 if (fscanf(file, "%d", &scores.entry[i].score) == EOF)
8841 Warn("fscanf() failed; %s", strerror(errno));
8843 if (fgets(line, MAX_LINE_LEN, file) == NULL)
8846 if (strlen(line) > 0 && line[strlen(line) - 1] == '\n')
8847 line[strlen(line) - 1] = '\0';
8849 for (line_ptr = line; *line_ptr; line_ptr++)
8851 if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != '\0')
8853 strncpy(scores.entry[i].name, line_ptr, MAX_PLAYER_NAME_LEN);
8854 scores.entry[i].name[MAX_PLAYER_NAME_LEN] = '\0';
8863 static void ConvertScore_OLD(void)
8865 // only convert score to time for levels that rate playing time over score
8866 if (!level.rate_time_over_score)
8869 // convert old score to playing time for score-less levels (like Supaplex)
8870 int time_final_max = 999;
8873 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
8875 int score = scores.entry[i].score;
8877 if (score > 0 && score < time_final_max)
8878 scores.entry[i].time = (time_final_max - score - 1) * FRAMES_PER_SECOND;
8882 static int LoadScore_VERS(File *file, int chunk_size, struct ScoreInfo *scores)
8884 scores->file_version = getFileVersion(file);
8885 scores->game_version = getFileVersion(file);
8890 static int LoadScore_INFO(File *file, int chunk_size, struct ScoreInfo *scores)
8892 char *level_identifier = NULL;
8893 int level_identifier_size;
8896 level_identifier_size = getFile16BitBE(file);
8898 level_identifier = checked_malloc(level_identifier_size);
8900 for (i = 0; i < level_identifier_size; i++)
8901 level_identifier[i] = getFile8Bit(file);
8903 strncpy(scores->level_identifier, level_identifier, MAX_FILENAME_LEN);
8904 scores->level_identifier[MAX_FILENAME_LEN] = '\0';
8906 checked_free(level_identifier);
8908 scores->level_nr = getFile16BitBE(file);
8909 scores->num_entries = getFile16BitBE(file);
8911 chunk_size = 2 + level_identifier_size + 2 + 2;
8916 static int LoadScore_NAME(File *file, int chunk_size, struct ScoreInfo *scores)
8920 for (i = 0; i < scores->num_entries; i++)
8922 for (j = 0; j < MAX_PLAYER_NAME_LEN; j++)
8923 scores->entry[i].name[j] = getFile8Bit(file);
8925 scores->entry[i].name[MAX_PLAYER_NAME_LEN] = '\0';
8928 chunk_size = scores->num_entries * MAX_PLAYER_NAME_LEN;
8933 static int LoadScore_SCOR(File *file, int chunk_size, struct ScoreInfo *scores)
8937 for (i = 0; i < scores->num_entries; i++)
8938 scores->entry[i].score = getFile16BitBE(file);
8940 chunk_size = scores->num_entries * 2;
8945 static int LoadScore_SC4R(File *file, int chunk_size, struct ScoreInfo *scores)
8949 for (i = 0; i < scores->num_entries; i++)
8950 scores->entry[i].score = getFile32BitBE(file);
8952 chunk_size = scores->num_entries * 4;
8957 static int LoadScore_TIME(File *file, int chunk_size, struct ScoreInfo *scores)
8961 for (i = 0; i < scores->num_entries; i++)
8962 scores->entry[i].time = getFile32BitBE(file);
8964 chunk_size = scores->num_entries * 4;
8969 static int LoadScore_TAPE(File *file, int chunk_size, struct ScoreInfo *scores)
8973 for (i = 0; i < scores->num_entries; i++)
8975 for (j = 0; j < MAX_SCORE_TAPE_BASENAME_LEN; j++)
8976 scores->entry[i].tape_basename[j] = getFile8Bit(file);
8978 scores->entry[i].tape_basename[MAX_SCORE_TAPE_BASENAME_LEN] = '\0';
8981 chunk_size = scores->num_entries * MAX_SCORE_TAPE_BASENAME_LEN;
8986 void LoadScore(int nr)
8988 char *filename = getScoreFilename(nr);
8989 char cookie[MAX_LINE_LEN];
8990 char chunk_name[CHUNK_ID_LEN + 1];
8992 boolean old_score_file_format = FALSE;
8995 // always start with reliable default values
8996 setScoreInfoToDefaults();
8998 if (!(file = openFile(filename, MODE_READ)))
9001 getFileChunkBE(file, chunk_name, NULL);
9002 if (strEqual(chunk_name, "RND1"))
9004 getFile32BitBE(file); // not used
9006 getFileChunkBE(file, chunk_name, NULL);
9007 if (!strEqual(chunk_name, "SCOR"))
9009 Warn("unknown format of score file '%s'", filename);
9016 else // check for old file format with cookie string
9018 strcpy(cookie, chunk_name);
9019 if (getStringFromFile(file, &cookie[4], MAX_LINE_LEN - 4) == NULL)
9021 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
9022 cookie[strlen(cookie) - 1] = '\0';
9024 if (!checkCookieString(cookie, SCORE_COOKIE_TMPL))
9026 Warn("unknown format of score file '%s'", filename);
9033 old_score_file_format = TRUE;
9036 if (old_score_file_format)
9038 // score files from versions before 4.2.4.0 without chunk structure
9041 // convert score to time, if possible (mainly for Supaplex levels)
9050 int (*loader)(File *, int, struct ScoreInfo *);
9054 { "VERS", SCORE_CHUNK_VERS_SIZE, LoadScore_VERS },
9055 { "INFO", -1, LoadScore_INFO },
9056 { "NAME", -1, LoadScore_NAME },
9057 { "SCOR", -1, LoadScore_SCOR },
9058 { "SC4R", -1, LoadScore_SC4R },
9059 { "TIME", -1, LoadScore_TIME },
9060 { "TAPE", -1, LoadScore_TAPE },
9065 while (getFileChunkBE(file, chunk_name, &chunk_size))
9069 while (chunk_info[i].name != NULL &&
9070 !strEqual(chunk_name, chunk_info[i].name))
9073 if (chunk_info[i].name == NULL)
9075 Warn("unknown chunk '%s' in score file '%s'",
9076 chunk_name, filename);
9078 ReadUnusedBytesFromFile(file, chunk_size);
9080 else if (chunk_info[i].size != -1 &&
9081 chunk_info[i].size != chunk_size)
9083 Warn("wrong size (%d) of chunk '%s' in score file '%s'",
9084 chunk_size, chunk_name, filename);
9086 ReadUnusedBytesFromFile(file, chunk_size);
9090 // call function to load this score chunk
9091 int chunk_size_expected =
9092 (chunk_info[i].loader)(file, chunk_size, &scores);
9094 // the size of some chunks cannot be checked before reading other
9095 // chunks first (like "HEAD" and "BODY") that contain some header
9096 // information, so check them here
9097 if (chunk_size_expected != chunk_size)
9099 Warn("wrong size (%d) of chunk '%s' in score file '%s'",
9100 chunk_size, chunk_name, filename);
9109 #if ENABLE_HISTORIC_CHUNKS
9110 void SaveScore_OLD(int nr)
9113 char *filename = getScoreFilename(nr);
9116 // used instead of "leveldir_current->subdir" (for network games)
9117 InitScoreDirectory(levelset.identifier);
9119 if (!(file = fopen(filename, MODE_WRITE)))
9121 Warn("cannot save score for level %d", nr);
9126 fprintf(file, "%s\n\n", SCORE_COOKIE);
9128 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9129 fprintf(file, "%d %s\n", scores.entry[i].score, scores.entry[i].name);
9133 SetFilePermissions(filename, PERMS_PRIVATE);
9137 static void SaveScore_VERS(FILE *file, struct ScoreInfo *scores)
9139 putFileVersion(file, scores->file_version);
9140 putFileVersion(file, scores->game_version);
9143 static void SaveScore_INFO(FILE *file, struct ScoreInfo *scores)
9145 int level_identifier_size = strlen(scores->level_identifier) + 1;
9148 putFile16BitBE(file, level_identifier_size);
9150 for (i = 0; i < level_identifier_size; i++)
9151 putFile8Bit(file, scores->level_identifier[i]);
9153 putFile16BitBE(file, scores->level_nr);
9154 putFile16BitBE(file, scores->num_entries);
9157 static void SaveScore_NAME(FILE *file, struct ScoreInfo *scores)
9161 for (i = 0; i < scores->num_entries; i++)
9163 int name_size = strlen(scores->entry[i].name);
9165 for (j = 0; j < MAX_PLAYER_NAME_LEN; j++)
9166 putFile8Bit(file, (j < name_size ? scores->entry[i].name[j] : 0));
9170 static void SaveScore_SCOR(FILE *file, struct ScoreInfo *scores)
9174 for (i = 0; i < scores->num_entries; i++)
9175 putFile16BitBE(file, scores->entry[i].score);
9178 static void SaveScore_SC4R(FILE *file, struct ScoreInfo *scores)
9182 for (i = 0; i < scores->num_entries; i++)
9183 putFile32BitBE(file, scores->entry[i].score);
9186 static void SaveScore_TIME(FILE *file, struct ScoreInfo *scores)
9190 for (i = 0; i < scores->num_entries; i++)
9191 putFile32BitBE(file, scores->entry[i].time);
9194 static void SaveScore_TAPE(FILE *file, struct ScoreInfo *scores)
9198 for (i = 0; i < scores->num_entries; i++)
9200 int size = strlen(scores->entry[i].tape_basename);
9202 for (j = 0; j < MAX_SCORE_TAPE_BASENAME_LEN; j++)
9203 putFile8Bit(file, (j < size ? scores->entry[i].tape_basename[j] : 0));
9207 static void SaveScoreToFilename(char *filename)
9210 int info_chunk_size;
9211 int name_chunk_size;
9212 int scor_chunk_size;
9213 int sc4r_chunk_size;
9214 int time_chunk_size;
9215 int tape_chunk_size;
9216 boolean has_large_score_values;
9219 if (!(file = fopen(filename, MODE_WRITE)))
9221 Warn("cannot save score file '%s'", filename);
9226 info_chunk_size = 2 + (strlen(scores.level_identifier) + 1) + 2 + 2;
9227 name_chunk_size = scores.num_entries * MAX_PLAYER_NAME_LEN;
9228 scor_chunk_size = scores.num_entries * 2;
9229 sc4r_chunk_size = scores.num_entries * 4;
9230 time_chunk_size = scores.num_entries * 4;
9231 tape_chunk_size = scores.num_entries * MAX_SCORE_TAPE_BASENAME_LEN;
9233 has_large_score_values = FALSE;
9234 for (i = 0; i < scores.num_entries; i++)
9235 if (scores.entry[i].score > 0xffff)
9236 has_large_score_values = TRUE;
9238 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
9239 putFileChunkBE(file, "SCOR", CHUNK_SIZE_NONE);
9241 putFileChunkBE(file, "VERS", SCORE_CHUNK_VERS_SIZE);
9242 SaveScore_VERS(file, &scores);
9244 putFileChunkBE(file, "INFO", info_chunk_size);
9245 SaveScore_INFO(file, &scores);
9247 putFileChunkBE(file, "NAME", name_chunk_size);
9248 SaveScore_NAME(file, &scores);
9250 if (has_large_score_values)
9252 putFileChunkBE(file, "SC4R", sc4r_chunk_size);
9253 SaveScore_SC4R(file, &scores);
9257 putFileChunkBE(file, "SCOR", scor_chunk_size);
9258 SaveScore_SCOR(file, &scores);
9261 putFileChunkBE(file, "TIME", time_chunk_size);
9262 SaveScore_TIME(file, &scores);
9264 putFileChunkBE(file, "TAPE", tape_chunk_size);
9265 SaveScore_TAPE(file, &scores);
9269 SetFilePermissions(filename, PERMS_PRIVATE);
9272 void SaveScore(int nr)
9274 char *filename = getScoreFilename(nr);
9277 // used instead of "leveldir_current->subdir" (for network games)
9278 InitScoreDirectory(levelset.identifier);
9280 scores.file_version = FILE_VERSION_ACTUAL;
9281 scores.game_version = GAME_VERSION_ACTUAL;
9283 strncpy(scores.level_identifier, levelset.identifier, MAX_FILENAME_LEN);
9284 scores.level_identifier[MAX_FILENAME_LEN] = '\0';
9285 scores.level_nr = level_nr;
9287 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9288 if (scores.entry[i].score == 0 &&
9289 scores.entry[i].time == 0 &&
9290 strEqual(scores.entry[i].name, EMPTY_PLAYER_NAME))
9293 scores.num_entries = i;
9295 if (scores.num_entries == 0)
9298 SaveScoreToFilename(filename);
9301 static void LoadServerScoreFromCache(int nr)
9303 struct ScoreEntry score_entry;
9312 { &score_entry.score, FALSE, 0 },
9313 { &score_entry.time, FALSE, 0 },
9314 { score_entry.name, TRUE, MAX_PLAYER_NAME_LEN },
9315 { score_entry.tape_basename, TRUE, MAX_FILENAME_LEN },
9316 { score_entry.tape_date, TRUE, MAX_ISO_DATE_LEN },
9317 { &score_entry.id, FALSE, 0 },
9318 { score_entry.platform, TRUE, MAX_PLATFORM_TEXT_LEN },
9319 { score_entry.version, TRUE, MAX_VERSION_TEXT_LEN },
9320 { score_entry.country_code, TRUE, MAX_COUNTRY_CODE_LEN },
9321 { score_entry.country_name, TRUE, MAX_COUNTRY_NAME_LEN },
9325 char *filename = getScoreCacheFilename(nr);
9326 SetupFileHash *score_hash = loadSetupFileHash(filename);
9329 server_scores.num_entries = 0;
9331 if (score_hash == NULL)
9334 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9336 score_entry = server_scores.entry[i];
9338 for (j = 0; score_mapping[j].value != NULL; j++)
9342 sprintf(token, "%02d.%d", i, j);
9344 char *value = getHashEntry(score_hash, token);
9349 if (score_mapping[j].is_string)
9351 char *score_value = (char *)score_mapping[j].value;
9352 int value_size = score_mapping[j].string_size;
9354 strncpy(score_value, value, value_size);
9355 score_value[value_size] = '\0';
9359 int *score_value = (int *)score_mapping[j].value;
9361 *score_value = atoi(value);
9364 server_scores.num_entries = i + 1;
9367 server_scores.entry[i] = score_entry;
9370 freeSetupFileHash(score_hash);
9373 void LoadServerScore(int nr, boolean download_score)
9375 if (!setup.use_api_server)
9378 // always start with reliable default values
9379 setServerScoreInfoToDefaults();
9381 // 1st step: load server scores from cache file (which may not exist)
9382 // (this should prevent reading it while the thread is writing to it)
9383 LoadServerScoreFromCache(nr);
9385 if (download_score && runtime.use_api_server)
9387 // 2nd step: download server scores from score server to cache file
9388 // (as thread, as it might time out if the server is not reachable)
9389 ApiGetScoreAsThread(nr);
9393 void PrepareScoreTapesForUpload(char *leveldir_subdir)
9395 MarkTapeDirectoryUploadsAsIncomplete(leveldir_subdir);
9397 // if score tape not uploaded, ask for uploading missing tapes later
9398 if (!setup.has_remaining_tapes)
9399 setup.ask_for_remaining_tapes = TRUE;
9401 setup.provide_uploading_tapes = TRUE;
9402 setup.has_remaining_tapes = TRUE;
9404 SaveSetup_ServerSetup();
9407 void SaveServerScore(int nr, boolean tape_saved)
9409 if (!runtime.use_api_server)
9411 PrepareScoreTapesForUpload(leveldir_current->subdir);
9416 ApiAddScoreAsThread(nr, tape_saved, NULL);
9419 void SaveServerScoreFromFile(int nr, boolean tape_saved,
9420 char *score_tape_filename)
9422 if (!runtime.use_api_server)
9425 ApiAddScoreAsThread(nr, tape_saved, score_tape_filename);
9428 void LoadLocalAndServerScore(int nr, boolean download_score)
9430 int last_added_local = scores.last_added_local;
9431 boolean force_last_added = scores.force_last_added;
9433 // needed if only showing server scores
9434 setScoreInfoToDefaults();
9436 if (!strEqual(setup.scores_in_highscore_list, STR_SCORES_TYPE_SERVER_ONLY))
9439 // restore last added local score entry (before merging server scores)
9440 scores.last_added = scores.last_added_local = last_added_local;
9442 if (setup.use_api_server &&
9443 !strEqual(setup.scores_in_highscore_list, STR_SCORES_TYPE_LOCAL_ONLY))
9445 // load server scores from cache file and trigger update from server
9446 LoadServerScore(nr, download_score);
9448 // merge local scores with scores from server
9452 if (force_last_added)
9453 scores.force_last_added = force_last_added;
9457 // ============================================================================
9458 // setup file functions
9459 // ============================================================================
9461 #define TOKEN_STR_PLAYER_PREFIX "player_"
9464 static struct TokenInfo global_setup_tokens[] =
9468 &setup.player_name, "player_name"
9472 &setup.multiple_users, "multiple_users"
9476 &setup.sound, "sound"
9480 &setup.sound_loops, "repeating_sound_loops"
9484 &setup.sound_music, "background_music"
9488 &setup.sound_simple, "simple_sound_effects"
9492 &setup.toons, "toons"
9496 &setup.global_animations, "global_animations"
9500 &setup.scroll_delay, "scroll_delay"
9504 &setup.forced_scroll_delay, "forced_scroll_delay"
9508 &setup.scroll_delay_value, "scroll_delay_value"
9512 &setup.engine_snapshot_mode, "engine_snapshot_mode"
9516 &setup.engine_snapshot_memory, "engine_snapshot_memory"
9520 &setup.fade_screens, "fade_screens"
9524 &setup.autorecord, "automatic_tape_recording"
9528 &setup.autorecord_after_replay, "autorecord_after_replay"
9532 &setup.auto_pause_on_start, "auto_pause_on_start"
9536 &setup.show_titlescreen, "show_titlescreen"
9540 &setup.quick_doors, "quick_doors"
9544 &setup.team_mode, "team_mode"
9548 &setup.handicap, "handicap"
9552 &setup.skip_levels, "skip_levels"
9556 &setup.increment_levels, "increment_levels"
9560 &setup.auto_play_next_level, "auto_play_next_level"
9564 &setup.count_score_after_game, "count_score_after_game"
9568 &setup.show_scores_after_game, "show_scores_after_game"
9572 &setup.time_limit, "time_limit"
9576 &setup.fullscreen, "fullscreen"
9580 &setup.window_scaling_percent, "window_scaling_percent"
9584 &setup.window_scaling_quality, "window_scaling_quality"
9588 &setup.screen_rendering_mode, "screen_rendering_mode"
9592 &setup.vsync_mode, "vsync_mode"
9596 &setup.ask_on_escape, "ask_on_escape"
9600 &setup.ask_on_escape_editor, "ask_on_escape_editor"
9604 &setup.ask_on_game_over, "ask_on_game_over"
9608 &setup.ask_on_quit_game, "ask_on_quit_game"
9612 &setup.ask_on_quit_program, "ask_on_quit_program"
9616 &setup.quick_switch, "quick_player_switch"
9620 &setup.input_on_focus, "input_on_focus"
9624 &setup.prefer_aga_graphics, "prefer_aga_graphics"
9628 &setup.prefer_lowpass_sounds, "prefer_lowpass_sounds"
9632 &setup.prefer_extra_panel_items, "prefer_extra_panel_items"
9636 &setup.game_speed_extended, "game_speed_extended"
9640 &setup.game_frame_delay, "game_frame_delay"
9644 &setup.sp_show_border_elements, "sp_show_border_elements"
9648 &setup.small_game_graphics, "small_game_graphics"
9652 &setup.show_load_save_buttons, "show_load_save_buttons"
9656 &setup.show_undo_redo_buttons, "show_undo_redo_buttons"
9660 &setup.scores_in_highscore_list, "scores_in_highscore_list"
9664 &setup.graphics_set, "graphics_set"
9668 &setup.sounds_set, "sounds_set"
9672 &setup.music_set, "music_set"
9676 &setup.override_level_graphics, "override_level_graphics"
9680 &setup.override_level_sounds, "override_level_sounds"
9684 &setup.override_level_music, "override_level_music"
9688 &setup.volume_simple, "volume_simple"
9692 &setup.volume_loops, "volume_loops"
9696 &setup.volume_music, "volume_music"
9700 &setup.network_mode, "network_mode"
9704 &setup.network_player_nr, "network_player"
9708 &setup.network_server_hostname, "network_server_hostname"
9712 &setup.touch.control_type, "touch.control_type"
9716 &setup.touch.move_distance, "touch.move_distance"
9720 &setup.touch.drop_distance, "touch.drop_distance"
9724 &setup.touch.transparency, "touch.transparency"
9728 &setup.touch.draw_outlined, "touch.draw_outlined"
9732 &setup.touch.draw_pressed, "touch.draw_pressed"
9736 &setup.touch.grid_xsize[0], "touch.virtual_buttons.0.xsize"
9740 &setup.touch.grid_ysize[0], "touch.virtual_buttons.0.ysize"
9744 &setup.touch.grid_xsize[1], "touch.virtual_buttons.1.xsize"
9748 &setup.touch.grid_ysize[1], "touch.virtual_buttons.1.ysize"
9752 &setup.touch.overlay_buttons, "touch.overlay_buttons"
9756 static struct TokenInfo auto_setup_tokens[] =
9760 &setup.auto_setup.editor_zoom_tilesize, "editor.zoom_tilesize"
9764 static struct TokenInfo server_setup_tokens[] =
9768 &setup.player_uuid, "player_uuid"
9772 &setup.player_version, "player_version"
9776 &setup.use_api_server, TEST_PREFIX "use_api_server"
9780 &setup.api_server_hostname, TEST_PREFIX "api_server_hostname"
9784 &setup.api_server_password, TEST_PREFIX "api_server_password"
9788 &setup.ask_for_uploading_tapes, TEST_PREFIX "ask_for_uploading_tapes"
9792 &setup.ask_for_remaining_tapes, TEST_PREFIX "ask_for_remaining_tapes"
9796 &setup.provide_uploading_tapes, TEST_PREFIX "provide_uploading_tapes"
9800 &setup.ask_for_using_api_server,TEST_PREFIX "ask_for_using_api_server"
9804 &setup.has_remaining_tapes, TEST_PREFIX "has_remaining_tapes"
9808 static struct TokenInfo editor_setup_tokens[] =
9812 &setup.editor.el_classic, "editor.el_classic"
9816 &setup.editor.el_custom, "editor.el_custom"
9820 &setup.editor.el_user_defined, "editor.el_user_defined"
9824 &setup.editor.el_dynamic, "editor.el_dynamic"
9828 &setup.editor.el_headlines, "editor.el_headlines"
9832 &setup.editor.show_element_token, "editor.show_element_token"
9836 &setup.editor.show_read_only_warning, "editor.show_read_only_warning"
9840 static struct TokenInfo editor_cascade_setup_tokens[] =
9844 &setup.editor_cascade.el_bd, "editor.cascade.el_bd"
9848 &setup.editor_cascade.el_em, "editor.cascade.el_em"
9852 &setup.editor_cascade.el_emc, "editor.cascade.el_emc"
9856 &setup.editor_cascade.el_rnd, "editor.cascade.el_rnd"
9860 &setup.editor_cascade.el_sb, "editor.cascade.el_sb"
9864 &setup.editor_cascade.el_sp, "editor.cascade.el_sp"
9868 &setup.editor_cascade.el_dc, "editor.cascade.el_dc"
9872 &setup.editor_cascade.el_dx, "editor.cascade.el_dx"
9876 &setup.editor_cascade.el_mm, "editor.cascade.el_mm"
9880 &setup.editor_cascade.el_df, "editor.cascade.el_df"
9884 &setup.editor_cascade.el_chars, "editor.cascade.el_chars"
9888 &setup.editor_cascade.el_steel_chars, "editor.cascade.el_steel_chars"
9892 &setup.editor_cascade.el_ce, "editor.cascade.el_ce"
9896 &setup.editor_cascade.el_ge, "editor.cascade.el_ge"
9900 &setup.editor_cascade.el_es, "editor.cascade.el_es"
9904 &setup.editor_cascade.el_ref, "editor.cascade.el_ref"
9908 &setup.editor_cascade.el_user, "editor.cascade.el_user"
9912 &setup.editor_cascade.el_dynamic, "editor.cascade.el_dynamic"
9916 static struct TokenInfo shortcut_setup_tokens[] =
9920 &setup.shortcut.save_game, "shortcut.save_game"
9924 &setup.shortcut.load_game, "shortcut.load_game"
9928 &setup.shortcut.restart_game, "shortcut.restart_game"
9932 &setup.shortcut.pause_before_end, "shortcut.pause_before_end"
9936 &setup.shortcut.toggle_pause, "shortcut.toggle_pause"
9940 &setup.shortcut.focus_player[0], "shortcut.focus_player_1"
9944 &setup.shortcut.focus_player[1], "shortcut.focus_player_2"
9948 &setup.shortcut.focus_player[2], "shortcut.focus_player_3"
9952 &setup.shortcut.focus_player[3], "shortcut.focus_player_4"
9956 &setup.shortcut.focus_player_all, "shortcut.focus_player_all"
9960 &setup.shortcut.tape_eject, "shortcut.tape_eject"
9964 &setup.shortcut.tape_extra, "shortcut.tape_extra"
9968 &setup.shortcut.tape_stop, "shortcut.tape_stop"
9972 &setup.shortcut.tape_pause, "shortcut.tape_pause"
9976 &setup.shortcut.tape_record, "shortcut.tape_record"
9980 &setup.shortcut.tape_play, "shortcut.tape_play"
9984 &setup.shortcut.sound_simple, "shortcut.sound_simple"
9988 &setup.shortcut.sound_loops, "shortcut.sound_loops"
9992 &setup.shortcut.sound_music, "shortcut.sound_music"
9996 &setup.shortcut.snap_left, "shortcut.snap_left"
10000 &setup.shortcut.snap_right, "shortcut.snap_right"
10004 &setup.shortcut.snap_up, "shortcut.snap_up"
10008 &setup.shortcut.snap_down, "shortcut.snap_down"
10012 static struct SetupInputInfo setup_input;
10013 static struct TokenInfo player_setup_tokens[] =
10017 &setup_input.use_joystick, ".use_joystick"
10021 &setup_input.joy.device_name, ".joy.device_name"
10025 &setup_input.joy.xleft, ".joy.xleft"
10029 &setup_input.joy.xmiddle, ".joy.xmiddle"
10033 &setup_input.joy.xright, ".joy.xright"
10037 &setup_input.joy.yupper, ".joy.yupper"
10041 &setup_input.joy.ymiddle, ".joy.ymiddle"
10045 &setup_input.joy.ylower, ".joy.ylower"
10049 &setup_input.joy.snap, ".joy.snap_field"
10053 &setup_input.joy.drop, ".joy.place_bomb"
10057 &setup_input.key.left, ".key.move_left"
10061 &setup_input.key.right, ".key.move_right"
10065 &setup_input.key.up, ".key.move_up"
10069 &setup_input.key.down, ".key.move_down"
10073 &setup_input.key.snap, ".key.snap_field"
10077 &setup_input.key.drop, ".key.place_bomb"
10081 static struct TokenInfo system_setup_tokens[] =
10085 &setup.system.sdl_renderdriver, "system.sdl_renderdriver"
10089 &setup.system.sdl_videodriver, "system.sdl_videodriver"
10093 &setup.system.sdl_audiodriver, "system.sdl_audiodriver"
10097 &setup.system.audio_fragment_size, "system.audio_fragment_size"
10101 static struct TokenInfo internal_setup_tokens[] =
10105 &setup.internal.program_title, "program_title"
10109 &setup.internal.program_version, "program_version"
10113 &setup.internal.program_author, "program_author"
10117 &setup.internal.program_email, "program_email"
10121 &setup.internal.program_website, "program_website"
10125 &setup.internal.program_copyright, "program_copyright"
10129 &setup.internal.program_company, "program_company"
10133 &setup.internal.program_icon_file, "program_icon_file"
10137 &setup.internal.default_graphics_set, "default_graphics_set"
10141 &setup.internal.default_sounds_set, "default_sounds_set"
10145 &setup.internal.default_music_set, "default_music_set"
10149 &setup.internal.fallback_graphics_file, "fallback_graphics_file"
10153 &setup.internal.fallback_sounds_file, "fallback_sounds_file"
10157 &setup.internal.fallback_music_file, "fallback_music_file"
10161 &setup.internal.default_level_series, "default_level_series"
10165 &setup.internal.default_window_width, "default_window_width"
10169 &setup.internal.default_window_height, "default_window_height"
10173 &setup.internal.choose_from_top_leveldir, "choose_from_top_leveldir"
10177 &setup.internal.show_scaling_in_title, "show_scaling_in_title"
10181 &setup.internal.create_user_levelset, "create_user_levelset"
10185 &setup.internal.info_screens_from_main, "info_screens_from_main"
10189 &setup.internal.menu_game, "menu_game"
10193 &setup.internal.menu_engines, "menu_engines"
10197 &setup.internal.menu_editor, "menu_editor"
10201 &setup.internal.menu_graphics, "menu_graphics"
10205 &setup.internal.menu_sound, "menu_sound"
10209 &setup.internal.menu_artwork, "menu_artwork"
10213 &setup.internal.menu_input, "menu_input"
10217 &setup.internal.menu_touch, "menu_touch"
10221 &setup.internal.menu_shortcuts, "menu_shortcuts"
10225 &setup.internal.menu_exit, "menu_exit"
10229 &setup.internal.menu_save_and_exit, "menu_save_and_exit"
10233 &setup.internal.menu_shortcuts_various, "menu_shortcuts_various"
10237 &setup.internal.menu_shortcuts_focus, "menu_shortcuts_focus"
10241 &setup.internal.menu_shortcuts_tape, "menu_shortcuts_tape"
10245 &setup.internal.menu_shortcuts_sound, "menu_shortcuts_sound"
10249 &setup.internal.menu_shortcuts_snap, "menu_shortcuts_snap"
10253 &setup.internal.info_title, "info_title"
10257 &setup.internal.info_elements, "info_elements"
10261 &setup.internal.info_music, "info_music"
10265 &setup.internal.info_credits, "info_credits"
10269 &setup.internal.info_program, "info_program"
10273 &setup.internal.info_version, "info_version"
10277 &setup.internal.info_levelset, "info_levelset"
10281 &setup.internal.info_exit, "info_exit"
10285 static struct TokenInfo debug_setup_tokens[] =
10289 &setup.debug.frame_delay[0], "debug.frame_delay_0"
10293 &setup.debug.frame_delay[1], "debug.frame_delay_1"
10297 &setup.debug.frame_delay[2], "debug.frame_delay_2"
10301 &setup.debug.frame_delay[3], "debug.frame_delay_3"
10305 &setup.debug.frame_delay[4], "debug.frame_delay_4"
10309 &setup.debug.frame_delay[5], "debug.frame_delay_5"
10313 &setup.debug.frame_delay[6], "debug.frame_delay_6"
10317 &setup.debug.frame_delay[7], "debug.frame_delay_7"
10321 &setup.debug.frame_delay[8], "debug.frame_delay_8"
10325 &setup.debug.frame_delay[9], "debug.frame_delay_9"
10329 &setup.debug.frame_delay_key[0], "debug.key.frame_delay_0"
10333 &setup.debug.frame_delay_key[1], "debug.key.frame_delay_1"
10337 &setup.debug.frame_delay_key[2], "debug.key.frame_delay_2"
10341 &setup.debug.frame_delay_key[3], "debug.key.frame_delay_3"
10345 &setup.debug.frame_delay_key[4], "debug.key.frame_delay_4"
10349 &setup.debug.frame_delay_key[5], "debug.key.frame_delay_5"
10353 &setup.debug.frame_delay_key[6], "debug.key.frame_delay_6"
10357 &setup.debug.frame_delay_key[7], "debug.key.frame_delay_7"
10361 &setup.debug.frame_delay_key[8], "debug.key.frame_delay_8"
10365 &setup.debug.frame_delay_key[9], "debug.key.frame_delay_9"
10369 &setup.debug.frame_delay_use_mod_key, "debug.frame_delay.use_mod_key"},
10372 &setup.debug.frame_delay_game_only, "debug.frame_delay.game_only"
10376 &setup.debug.show_frames_per_second, "debug.show_frames_per_second"
10380 &setup.debug.xsn_mode, "debug.xsn_mode"
10384 &setup.debug.xsn_percent, "debug.xsn_percent"
10388 static struct TokenInfo options_setup_tokens[] =
10392 &setup.options.verbose, "options.verbose"
10396 &setup.options.debug, "options.debug"
10400 &setup.options.debug_mode, "options.debug_mode"
10404 static void setSetupInfoToDefaults(struct SetupInfo *si)
10408 si->player_name = getStringCopy(getDefaultUserName(user.nr));
10410 si->multiple_users = TRUE;
10413 si->sound_loops = TRUE;
10414 si->sound_music = TRUE;
10415 si->sound_simple = TRUE;
10417 si->global_animations = TRUE;
10418 si->scroll_delay = TRUE;
10419 si->forced_scroll_delay = FALSE;
10420 si->scroll_delay_value = STD_SCROLL_DELAY;
10421 si->engine_snapshot_mode = getStringCopy(STR_SNAPSHOT_MODE_DEFAULT);
10422 si->engine_snapshot_memory = SNAPSHOT_MEMORY_DEFAULT;
10423 si->fade_screens = TRUE;
10424 si->autorecord = TRUE;
10425 si->autorecord_after_replay = TRUE;
10426 si->auto_pause_on_start = FALSE;
10427 si->show_titlescreen = TRUE;
10428 si->quick_doors = FALSE;
10429 si->team_mode = FALSE;
10430 si->handicap = TRUE;
10431 si->skip_levels = TRUE;
10432 si->increment_levels = TRUE;
10433 si->auto_play_next_level = TRUE;
10434 si->count_score_after_game = TRUE;
10435 si->show_scores_after_game = TRUE;
10436 si->time_limit = TRUE;
10437 si->fullscreen = FALSE;
10438 si->window_scaling_percent = STD_WINDOW_SCALING_PERCENT;
10439 si->window_scaling_quality = getStringCopy(SCALING_QUALITY_DEFAULT);
10440 si->screen_rendering_mode = getStringCopy(STR_SPECIAL_RENDERING_DEFAULT);
10441 si->vsync_mode = getStringCopy(STR_VSYNC_MODE_DEFAULT);
10442 si->ask_on_escape = TRUE;
10443 si->ask_on_escape_editor = TRUE;
10444 si->ask_on_game_over = TRUE;
10445 si->ask_on_quit_game = TRUE;
10446 si->ask_on_quit_program = TRUE;
10447 si->quick_switch = FALSE;
10448 si->input_on_focus = FALSE;
10449 si->prefer_aga_graphics = TRUE;
10450 si->prefer_lowpass_sounds = FALSE;
10451 si->prefer_extra_panel_items = TRUE;
10452 si->game_speed_extended = FALSE;
10453 si->game_frame_delay = GAME_FRAME_DELAY;
10454 si->sp_show_border_elements = FALSE;
10455 si->small_game_graphics = FALSE;
10456 si->show_load_save_buttons = FALSE;
10457 si->show_undo_redo_buttons = FALSE;
10458 si->scores_in_highscore_list = getStringCopy(STR_SCORES_TYPE_DEFAULT);
10460 si->graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
10461 si->sounds_set = getStringCopy(SND_CLASSIC_SUBDIR);
10462 si->music_set = getStringCopy(MUS_CLASSIC_SUBDIR);
10464 si->override_level_graphics = FALSE;
10465 si->override_level_sounds = FALSE;
10466 si->override_level_music = FALSE;
10468 si->volume_simple = 100; // percent
10469 si->volume_loops = 100; // percent
10470 si->volume_music = 100; // percent
10472 si->network_mode = FALSE;
10473 si->network_player_nr = 0; // first player
10474 si->network_server_hostname = getStringCopy(STR_NETWORK_AUTO_DETECT);
10476 si->touch.control_type = getStringCopy(TOUCH_CONTROL_DEFAULT);
10477 si->touch.move_distance = TOUCH_MOVE_DISTANCE_DEFAULT; // percent
10478 si->touch.drop_distance = TOUCH_DROP_DISTANCE_DEFAULT; // percent
10479 si->touch.transparency = TOUCH_TRANSPARENCY_DEFAULT; // percent
10480 si->touch.draw_outlined = TRUE;
10481 si->touch.draw_pressed = TRUE;
10483 for (i = 0; i < 2; i++)
10485 char *default_grid_button[6][2] =
10491 { "111222", " vv " },
10492 { "111222", " vv " }
10494 int grid_xsize = DEFAULT_GRID_XSIZE(i);
10495 int grid_ysize = DEFAULT_GRID_YSIZE(i);
10496 int min_xsize = MIN(6, grid_xsize);
10497 int min_ysize = MIN(6, grid_ysize);
10498 int startx = grid_xsize - min_xsize;
10499 int starty = grid_ysize - min_ysize;
10502 // virtual buttons grid can only be set to defaults if video is initialized
10503 // (this will be repeated if virtual buttons are not loaded from setup file)
10504 if (video.initialized)
10506 si->touch.grid_xsize[i] = grid_xsize;
10507 si->touch.grid_ysize[i] = grid_ysize;
10511 si->touch.grid_xsize[i] = -1;
10512 si->touch.grid_ysize[i] = -1;
10515 for (x = 0; x < MAX_GRID_XSIZE; x++)
10516 for (y = 0; y < MAX_GRID_YSIZE; y++)
10517 si->touch.grid_button[i][x][y] = CHAR_GRID_BUTTON_NONE;
10519 for (x = 0; x < min_xsize; x++)
10520 for (y = 0; y < min_ysize; y++)
10521 si->touch.grid_button[i][x][starty + y] =
10522 default_grid_button[y][0][x];
10524 for (x = 0; x < min_xsize; x++)
10525 for (y = 0; y < min_ysize; y++)
10526 si->touch.grid_button[i][startx + x][starty + y] =
10527 default_grid_button[y][1][x];
10530 si->touch.grid_initialized = video.initialized;
10532 si->touch.overlay_buttons = FALSE;
10534 si->editor.el_boulderdash = TRUE;
10535 si->editor.el_emerald_mine = TRUE;
10536 si->editor.el_emerald_mine_club = TRUE;
10537 si->editor.el_more = TRUE;
10538 si->editor.el_sokoban = TRUE;
10539 si->editor.el_supaplex = TRUE;
10540 si->editor.el_diamond_caves = TRUE;
10541 si->editor.el_dx_boulderdash = TRUE;
10543 si->editor.el_mirror_magic = TRUE;
10544 si->editor.el_deflektor = TRUE;
10546 si->editor.el_chars = TRUE;
10547 si->editor.el_steel_chars = TRUE;
10549 si->editor.el_classic = TRUE;
10550 si->editor.el_custom = TRUE;
10552 si->editor.el_user_defined = FALSE;
10553 si->editor.el_dynamic = TRUE;
10555 si->editor.el_headlines = TRUE;
10557 si->editor.show_element_token = FALSE;
10559 si->editor.show_read_only_warning = TRUE;
10561 si->editor.use_template_for_new_levels = TRUE;
10563 si->shortcut.save_game = DEFAULT_KEY_SAVE_GAME;
10564 si->shortcut.load_game = DEFAULT_KEY_LOAD_GAME;
10565 si->shortcut.restart_game = DEFAULT_KEY_RESTART_GAME;
10566 si->shortcut.pause_before_end = DEFAULT_KEY_PAUSE_BEFORE_END;
10567 si->shortcut.toggle_pause = DEFAULT_KEY_TOGGLE_PAUSE;
10569 si->shortcut.focus_player[0] = DEFAULT_KEY_FOCUS_PLAYER_1;
10570 si->shortcut.focus_player[1] = DEFAULT_KEY_FOCUS_PLAYER_2;
10571 si->shortcut.focus_player[2] = DEFAULT_KEY_FOCUS_PLAYER_3;
10572 si->shortcut.focus_player[3] = DEFAULT_KEY_FOCUS_PLAYER_4;
10573 si->shortcut.focus_player_all = DEFAULT_KEY_FOCUS_PLAYER_ALL;
10575 si->shortcut.tape_eject = DEFAULT_KEY_TAPE_EJECT;
10576 si->shortcut.tape_extra = DEFAULT_KEY_TAPE_EXTRA;
10577 si->shortcut.tape_stop = DEFAULT_KEY_TAPE_STOP;
10578 si->shortcut.tape_pause = DEFAULT_KEY_TAPE_PAUSE;
10579 si->shortcut.tape_record = DEFAULT_KEY_TAPE_RECORD;
10580 si->shortcut.tape_play = DEFAULT_KEY_TAPE_PLAY;
10582 si->shortcut.sound_simple = DEFAULT_KEY_SOUND_SIMPLE;
10583 si->shortcut.sound_loops = DEFAULT_KEY_SOUND_LOOPS;
10584 si->shortcut.sound_music = DEFAULT_KEY_SOUND_MUSIC;
10586 si->shortcut.snap_left = DEFAULT_KEY_SNAP_LEFT;
10587 si->shortcut.snap_right = DEFAULT_KEY_SNAP_RIGHT;
10588 si->shortcut.snap_up = DEFAULT_KEY_SNAP_UP;
10589 si->shortcut.snap_down = DEFAULT_KEY_SNAP_DOWN;
10591 for (i = 0; i < MAX_PLAYERS; i++)
10593 si->input[i].use_joystick = FALSE;
10594 si->input[i].joy.device_name=getStringCopy(getDeviceNameFromJoystickNr(i));
10595 si->input[i].joy.xleft = JOYSTICK_XLEFT;
10596 si->input[i].joy.xmiddle = JOYSTICK_XMIDDLE;
10597 si->input[i].joy.xright = JOYSTICK_XRIGHT;
10598 si->input[i].joy.yupper = JOYSTICK_YUPPER;
10599 si->input[i].joy.ymiddle = JOYSTICK_YMIDDLE;
10600 si->input[i].joy.ylower = JOYSTICK_YLOWER;
10601 si->input[i].joy.snap = (i == 0 ? JOY_BUTTON_1 : 0);
10602 si->input[i].joy.drop = (i == 0 ? JOY_BUTTON_2 : 0);
10603 si->input[i].key.left = (i == 0 ? DEFAULT_KEY_LEFT : KSYM_UNDEFINED);
10604 si->input[i].key.right = (i == 0 ? DEFAULT_KEY_RIGHT : KSYM_UNDEFINED);
10605 si->input[i].key.up = (i == 0 ? DEFAULT_KEY_UP : KSYM_UNDEFINED);
10606 si->input[i].key.down = (i == 0 ? DEFAULT_KEY_DOWN : KSYM_UNDEFINED);
10607 si->input[i].key.snap = (i == 0 ? DEFAULT_KEY_SNAP : KSYM_UNDEFINED);
10608 si->input[i].key.drop = (i == 0 ? DEFAULT_KEY_DROP : KSYM_UNDEFINED);
10611 si->system.sdl_renderdriver = getStringCopy(ARG_DEFAULT);
10612 si->system.sdl_videodriver = getStringCopy(ARG_DEFAULT);
10613 si->system.sdl_audiodriver = getStringCopy(ARG_DEFAULT);
10614 si->system.audio_fragment_size = DEFAULT_AUDIO_FRAGMENT_SIZE;
10616 si->internal.program_title = getStringCopy(PROGRAM_TITLE_STRING);
10617 si->internal.program_version = getStringCopy(getProgramRealVersionString());
10618 si->internal.program_author = getStringCopy(PROGRAM_AUTHOR_STRING);
10619 si->internal.program_email = getStringCopy(PROGRAM_EMAIL_STRING);
10620 si->internal.program_website = getStringCopy(PROGRAM_WEBSITE_STRING);
10621 si->internal.program_copyright = getStringCopy(PROGRAM_COPYRIGHT_STRING);
10622 si->internal.program_company = getStringCopy(PROGRAM_COMPANY_STRING);
10624 si->internal.program_icon_file = getStringCopy(PROGRAM_ICON_FILENAME);
10626 si->internal.default_graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
10627 si->internal.default_sounds_set = getStringCopy(SND_CLASSIC_SUBDIR);
10628 si->internal.default_music_set = getStringCopy(MUS_CLASSIC_SUBDIR);
10630 si->internal.fallback_graphics_file = getStringCopy(UNDEFINED_FILENAME);
10631 si->internal.fallback_sounds_file = getStringCopy(UNDEFINED_FILENAME);
10632 si->internal.fallback_music_file = getStringCopy(UNDEFINED_FILENAME);
10634 si->internal.default_level_series = getStringCopy(UNDEFINED_LEVELSET);
10635 si->internal.choose_from_top_leveldir = FALSE;
10636 si->internal.show_scaling_in_title = TRUE;
10637 si->internal.create_user_levelset = TRUE;
10638 si->internal.info_screens_from_main = FALSE;
10640 si->internal.default_window_width = WIN_XSIZE_DEFAULT;
10641 si->internal.default_window_height = WIN_YSIZE_DEFAULT;
10643 si->debug.frame_delay[0] = DEFAULT_FRAME_DELAY_0;
10644 si->debug.frame_delay[1] = DEFAULT_FRAME_DELAY_1;
10645 si->debug.frame_delay[2] = DEFAULT_FRAME_DELAY_2;
10646 si->debug.frame_delay[3] = DEFAULT_FRAME_DELAY_3;
10647 si->debug.frame_delay[4] = DEFAULT_FRAME_DELAY_4;
10648 si->debug.frame_delay[5] = DEFAULT_FRAME_DELAY_5;
10649 si->debug.frame_delay[6] = DEFAULT_FRAME_DELAY_6;
10650 si->debug.frame_delay[7] = DEFAULT_FRAME_DELAY_7;
10651 si->debug.frame_delay[8] = DEFAULT_FRAME_DELAY_8;
10652 si->debug.frame_delay[9] = DEFAULT_FRAME_DELAY_9;
10654 si->debug.frame_delay_key[0] = DEFAULT_KEY_FRAME_DELAY_0;
10655 si->debug.frame_delay_key[1] = DEFAULT_KEY_FRAME_DELAY_1;
10656 si->debug.frame_delay_key[2] = DEFAULT_KEY_FRAME_DELAY_2;
10657 si->debug.frame_delay_key[3] = DEFAULT_KEY_FRAME_DELAY_3;
10658 si->debug.frame_delay_key[4] = DEFAULT_KEY_FRAME_DELAY_4;
10659 si->debug.frame_delay_key[5] = DEFAULT_KEY_FRAME_DELAY_5;
10660 si->debug.frame_delay_key[6] = DEFAULT_KEY_FRAME_DELAY_6;
10661 si->debug.frame_delay_key[7] = DEFAULT_KEY_FRAME_DELAY_7;
10662 si->debug.frame_delay_key[8] = DEFAULT_KEY_FRAME_DELAY_8;
10663 si->debug.frame_delay_key[9] = DEFAULT_KEY_FRAME_DELAY_9;
10665 si->debug.frame_delay_use_mod_key = DEFAULT_FRAME_DELAY_USE_MOD_KEY;
10666 si->debug.frame_delay_game_only = DEFAULT_FRAME_DELAY_GAME_ONLY;
10668 si->debug.show_frames_per_second = FALSE;
10670 si->debug.xsn_mode = AUTO;
10671 si->debug.xsn_percent = 0;
10673 si->options.verbose = FALSE;
10674 si->options.debug = FALSE;
10675 si->options.debug_mode = getStringCopy(ARG_UNDEFINED_STRING);
10677 #if defined(PLATFORM_ANDROID)
10678 si->fullscreen = TRUE;
10679 si->touch.overlay_buttons = TRUE;
10682 setHideSetupEntry(&setup.debug.xsn_mode);
10685 static void setSetupInfoToDefaults_AutoSetup(struct SetupInfo *si)
10687 si->auto_setup.editor_zoom_tilesize = MINI_TILESIZE;
10690 static void setSetupInfoToDefaults_ServerSetup(struct SetupInfo *si)
10692 si->player_uuid = NULL; // (will be set later)
10693 si->player_version = 1; // (will be set later)
10695 si->use_api_server = TRUE;
10696 si->api_server_hostname = getStringCopy(API_SERVER_HOSTNAME);
10697 si->api_server_password = getStringCopy(UNDEFINED_PASSWORD);
10698 si->ask_for_uploading_tapes = TRUE;
10699 si->ask_for_remaining_tapes = FALSE;
10700 si->provide_uploading_tapes = TRUE;
10701 si->ask_for_using_api_server = TRUE;
10702 si->has_remaining_tapes = FALSE;
10705 static void setSetupInfoToDefaults_EditorCascade(struct SetupInfo *si)
10707 si->editor_cascade.el_bd = TRUE;
10708 si->editor_cascade.el_em = TRUE;
10709 si->editor_cascade.el_emc = TRUE;
10710 si->editor_cascade.el_rnd = TRUE;
10711 si->editor_cascade.el_sb = TRUE;
10712 si->editor_cascade.el_sp = TRUE;
10713 si->editor_cascade.el_dc = TRUE;
10714 si->editor_cascade.el_dx = TRUE;
10716 si->editor_cascade.el_mm = TRUE;
10717 si->editor_cascade.el_df = TRUE;
10719 si->editor_cascade.el_chars = FALSE;
10720 si->editor_cascade.el_steel_chars = FALSE;
10721 si->editor_cascade.el_ce = FALSE;
10722 si->editor_cascade.el_ge = FALSE;
10723 si->editor_cascade.el_es = FALSE;
10724 si->editor_cascade.el_ref = FALSE;
10725 si->editor_cascade.el_user = FALSE;
10726 si->editor_cascade.el_dynamic = FALSE;
10729 #define MAX_HIDE_SETUP_TOKEN_SIZE 20
10731 static char *getHideSetupToken(void *setup_value)
10733 static char hide_setup_token[MAX_HIDE_SETUP_TOKEN_SIZE];
10735 if (setup_value != NULL)
10736 snprintf(hide_setup_token, MAX_HIDE_SETUP_TOKEN_SIZE, "%p", setup_value);
10738 return hide_setup_token;
10741 void setHideSetupEntry(void *setup_value)
10743 char *hide_setup_token = getHideSetupToken(setup_value);
10745 if (hide_setup_hash == NULL)
10746 hide_setup_hash = newSetupFileHash();
10748 if (setup_value != NULL)
10749 setHashEntry(hide_setup_hash, hide_setup_token, "");
10752 void removeHideSetupEntry(void *setup_value)
10754 char *hide_setup_token = getHideSetupToken(setup_value);
10756 if (setup_value != NULL)
10757 removeHashEntry(hide_setup_hash, hide_setup_token);
10760 boolean hideSetupEntry(void *setup_value)
10762 char *hide_setup_token = getHideSetupToken(setup_value);
10764 return (setup_value != NULL &&
10765 getHashEntry(hide_setup_hash, hide_setup_token) != NULL);
10768 static void setSetupInfoFromTokenText(SetupFileHash *setup_file_hash,
10769 struct TokenInfo *token_info,
10770 int token_nr, char *token_text)
10772 char *token_hide_text = getStringCat2(token_text, ".hide");
10773 char *token_hide_value = getHashEntry(setup_file_hash, token_hide_text);
10775 // set the value of this setup option in the setup option structure
10776 setSetupInfo(token_info, token_nr, getHashEntry(setup_file_hash, token_text));
10778 // check if this setup option should be hidden in the setup menu
10779 if (token_hide_value != NULL && get_boolean_from_string(token_hide_value))
10780 setHideSetupEntry(token_info[token_nr].value);
10782 free(token_hide_text);
10785 static void setSetupInfoFromTokenInfo(SetupFileHash *setup_file_hash,
10786 struct TokenInfo *token_info,
10789 setSetupInfoFromTokenText(setup_file_hash, token_info, token_nr,
10790 token_info[token_nr].text);
10793 static void decodeSetupFileHash_Default(SetupFileHash *setup_file_hash)
10797 if (!setup_file_hash)
10800 for (i = 0; i < ARRAY_SIZE(global_setup_tokens); i++)
10801 setSetupInfoFromTokenInfo(setup_file_hash, global_setup_tokens, i);
10803 setup.touch.grid_initialized = TRUE;
10804 for (i = 0; i < 2; i++)
10806 int grid_xsize = setup.touch.grid_xsize[i];
10807 int grid_ysize = setup.touch.grid_ysize[i];
10810 // if virtual buttons are not loaded from setup file, repeat initializing
10811 // virtual buttons grid with default values later when video is initialized
10812 if (grid_xsize == -1 ||
10815 setup.touch.grid_initialized = FALSE;
10820 for (y = 0; y < grid_ysize; y++)
10822 char token_string[MAX_LINE_LEN];
10824 sprintf(token_string, "touch.virtual_buttons.%d.%02d", i, y);
10826 char *value_string = getHashEntry(setup_file_hash, token_string);
10828 if (value_string == NULL)
10831 for (x = 0; x < grid_xsize; x++)
10833 char c = value_string[x];
10835 setup.touch.grid_button[i][x][y] =
10836 (c == '.' ? CHAR_GRID_BUTTON_NONE : c);
10841 for (i = 0; i < ARRAY_SIZE(editor_setup_tokens); i++)
10842 setSetupInfoFromTokenInfo(setup_file_hash, editor_setup_tokens, i);
10844 for (i = 0; i < ARRAY_SIZE(shortcut_setup_tokens); i++)
10845 setSetupInfoFromTokenInfo(setup_file_hash, shortcut_setup_tokens, i);
10847 for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
10851 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
10853 setup_input = setup.input[pnr];
10854 for (i = 0; i < ARRAY_SIZE(player_setup_tokens); i++)
10856 char full_token[100];
10858 sprintf(full_token, "%s%s", prefix, player_setup_tokens[i].text);
10859 setSetupInfoFromTokenText(setup_file_hash, player_setup_tokens, i,
10862 setup.input[pnr] = setup_input;
10865 for (i = 0; i < ARRAY_SIZE(system_setup_tokens); i++)
10866 setSetupInfoFromTokenInfo(setup_file_hash, system_setup_tokens, i);
10868 for (i = 0; i < ARRAY_SIZE(internal_setup_tokens); i++)
10869 setSetupInfoFromTokenInfo(setup_file_hash, internal_setup_tokens, i);
10871 for (i = 0; i < ARRAY_SIZE(debug_setup_tokens); i++)
10872 setSetupInfoFromTokenInfo(setup_file_hash, debug_setup_tokens, i);
10874 for (i = 0; i < ARRAY_SIZE(options_setup_tokens); i++)
10875 setSetupInfoFromTokenInfo(setup_file_hash, options_setup_tokens, i);
10877 setHideRelatedSetupEntries();
10880 static void decodeSetupFileHash_AutoSetup(SetupFileHash *setup_file_hash)
10884 if (!setup_file_hash)
10887 for (i = 0; i < ARRAY_SIZE(auto_setup_tokens); i++)
10888 setSetupInfo(auto_setup_tokens, i,
10889 getHashEntry(setup_file_hash,
10890 auto_setup_tokens[i].text));
10893 static void decodeSetupFileHash_ServerSetup(SetupFileHash *setup_file_hash)
10897 if (!setup_file_hash)
10900 for (i = 0; i < ARRAY_SIZE(server_setup_tokens); i++)
10901 setSetupInfo(server_setup_tokens, i,
10902 getHashEntry(setup_file_hash,
10903 server_setup_tokens[i].text));
10906 static void decodeSetupFileHash_EditorCascade(SetupFileHash *setup_file_hash)
10910 if (!setup_file_hash)
10913 for (i = 0; i < ARRAY_SIZE(editor_cascade_setup_tokens); i++)
10914 setSetupInfo(editor_cascade_setup_tokens, i,
10915 getHashEntry(setup_file_hash,
10916 editor_cascade_setup_tokens[i].text));
10919 void LoadUserNames(void)
10921 int last_user_nr = user.nr;
10924 if (global.user_names != NULL)
10926 for (i = 0; i < MAX_PLAYER_NAMES; i++)
10927 checked_free(global.user_names[i]);
10929 checked_free(global.user_names);
10932 global.user_names = checked_calloc(MAX_PLAYER_NAMES * sizeof(char *));
10934 for (i = 0; i < MAX_PLAYER_NAMES; i++)
10938 SetupFileHash *setup_file_hash = loadSetupFileHash(getSetupFilename());
10940 if (setup_file_hash)
10942 char *player_name = getHashEntry(setup_file_hash, "player_name");
10944 global.user_names[i] = getFixedUserName(player_name);
10946 freeSetupFileHash(setup_file_hash);
10949 if (global.user_names[i] == NULL)
10950 global.user_names[i] = getStringCopy(getDefaultUserName(i));
10953 user.nr = last_user_nr;
10956 void LoadSetupFromFilename(char *filename)
10958 SetupFileHash *setup_file_hash = loadSetupFileHash(filename);
10960 if (setup_file_hash)
10962 decodeSetupFileHash_Default(setup_file_hash);
10964 freeSetupFileHash(setup_file_hash);
10968 Debug("setup", "using default setup values");
10972 static void LoadSetup_SpecialPostProcessing(void)
10974 char *player_name_new;
10976 // needed to work around problems with fixed length strings
10977 player_name_new = getFixedUserName(setup.player_name);
10978 free(setup.player_name);
10979 setup.player_name = player_name_new;
10981 // "scroll_delay: on(3) / off(0)" was replaced by scroll delay value
10982 if (setup.scroll_delay == FALSE)
10984 setup.scroll_delay_value = MIN_SCROLL_DELAY;
10985 setup.scroll_delay = TRUE; // now always "on"
10988 // make sure that scroll delay value stays inside valid range
10989 setup.scroll_delay_value =
10990 MIN(MAX(MIN_SCROLL_DELAY, setup.scroll_delay_value), MAX_SCROLL_DELAY);
10993 void LoadSetup_Default(void)
10997 // always start with reliable default values
10998 setSetupInfoToDefaults(&setup);
11000 // try to load setup values from default setup file
11001 filename = getDefaultSetupFilename();
11003 if (fileExists(filename))
11004 LoadSetupFromFilename(filename);
11006 // try to load setup values from platform setup file
11007 filename = getPlatformSetupFilename();
11009 if (fileExists(filename))
11010 LoadSetupFromFilename(filename);
11012 // try to load setup values from user setup file
11013 filename = getSetupFilename();
11015 LoadSetupFromFilename(filename);
11017 LoadSetup_SpecialPostProcessing();
11020 void LoadSetup_AutoSetup(void)
11022 char *filename = getPath2(getSetupDir(), AUTOSETUP_FILENAME);
11023 SetupFileHash *setup_file_hash = NULL;
11025 // always start with reliable default values
11026 setSetupInfoToDefaults_AutoSetup(&setup);
11028 setup_file_hash = loadSetupFileHash(filename);
11030 if (setup_file_hash)
11032 decodeSetupFileHash_AutoSetup(setup_file_hash);
11034 freeSetupFileHash(setup_file_hash);
11040 void LoadSetup_ServerSetup(void)
11042 char *filename = getPath2(getSetupDir(), SERVERSETUP_FILENAME);
11043 SetupFileHash *setup_file_hash = NULL;
11045 // always start with reliable default values
11046 setSetupInfoToDefaults_ServerSetup(&setup);
11048 setup_file_hash = loadSetupFileHash(filename);
11050 if (setup_file_hash)
11052 decodeSetupFileHash_ServerSetup(setup_file_hash);
11054 freeSetupFileHash(setup_file_hash);
11059 if (setup.player_uuid == NULL)
11061 // player UUID does not yet exist in setup file
11062 setup.player_uuid = getStringCopy(getUUID());
11063 setup.player_version = 2;
11065 SaveSetup_ServerSetup();
11069 void LoadSetup_EditorCascade(void)
11071 char *filename = getPath2(getSetupDir(), EDITORCASCADE_FILENAME);
11072 SetupFileHash *setup_file_hash = NULL;
11074 // always start with reliable default values
11075 setSetupInfoToDefaults_EditorCascade(&setup);
11077 setup_file_hash = loadSetupFileHash(filename);
11079 if (setup_file_hash)
11081 decodeSetupFileHash_EditorCascade(setup_file_hash);
11083 freeSetupFileHash(setup_file_hash);
11089 void LoadSetup(void)
11091 LoadSetup_Default();
11092 LoadSetup_AutoSetup();
11093 LoadSetup_ServerSetup();
11094 LoadSetup_EditorCascade();
11097 static void addGameControllerMappingToHash(SetupFileHash *mappings_hash,
11098 char *mapping_line)
11100 char mapping_guid[MAX_LINE_LEN];
11101 char *mapping_start, *mapping_end;
11103 // get GUID from game controller mapping line: copy complete line
11104 strncpy(mapping_guid, mapping_line, MAX_LINE_LEN - 1);
11105 mapping_guid[MAX_LINE_LEN - 1] = '\0';
11107 // get GUID from game controller mapping line: cut after GUID part
11108 mapping_start = strchr(mapping_guid, ',');
11109 if (mapping_start != NULL)
11110 *mapping_start = '\0';
11112 // cut newline from game controller mapping line
11113 mapping_end = strchr(mapping_line, '\n');
11114 if (mapping_end != NULL)
11115 *mapping_end = '\0';
11117 // add mapping entry to game controller mappings hash
11118 setHashEntry(mappings_hash, mapping_guid, mapping_line);
11121 static void LoadSetup_ReadGameControllerMappings(SetupFileHash *mappings_hash,
11126 if (!(file = fopen(filename, MODE_READ)))
11128 Warn("cannot read game controller mappings file '%s'", filename);
11133 while (!feof(file))
11135 char line[MAX_LINE_LEN];
11137 if (!fgets(line, MAX_LINE_LEN, file))
11140 addGameControllerMappingToHash(mappings_hash, line);
11146 void SaveSetup_Default(void)
11148 char *filename = getSetupFilename();
11152 InitUserDataDirectory();
11154 if (!(file = fopen(filename, MODE_WRITE)))
11156 Warn("cannot write setup file '%s'", filename);
11161 fprintFileHeader(file, SETUP_FILENAME);
11163 for (i = 0; i < ARRAY_SIZE(global_setup_tokens); i++)
11165 // just to make things nicer :)
11166 if (global_setup_tokens[i].value == &setup.multiple_users ||
11167 global_setup_tokens[i].value == &setup.sound ||
11168 global_setup_tokens[i].value == &setup.graphics_set ||
11169 global_setup_tokens[i].value == &setup.volume_simple ||
11170 global_setup_tokens[i].value == &setup.network_mode ||
11171 global_setup_tokens[i].value == &setup.touch.control_type ||
11172 global_setup_tokens[i].value == &setup.touch.grid_xsize[0] ||
11173 global_setup_tokens[i].value == &setup.touch.grid_xsize[1])
11174 fprintf(file, "\n");
11176 fprintf(file, "%s\n", getSetupLine(global_setup_tokens, "", i));
11179 for (i = 0; i < 2; i++)
11181 int grid_xsize = setup.touch.grid_xsize[i];
11182 int grid_ysize = setup.touch.grid_ysize[i];
11185 fprintf(file, "\n");
11187 for (y = 0; y < grid_ysize; y++)
11189 char token_string[MAX_LINE_LEN];
11190 char value_string[MAX_LINE_LEN];
11192 sprintf(token_string, "touch.virtual_buttons.%d.%02d", i, y);
11194 for (x = 0; x < grid_xsize; x++)
11196 char c = setup.touch.grid_button[i][x][y];
11198 value_string[x] = (c == CHAR_GRID_BUTTON_NONE ? '.' : c);
11201 value_string[grid_xsize] = '\0';
11203 fprintf(file, "%s\n", getFormattedSetupEntry(token_string, value_string));
11207 fprintf(file, "\n");
11208 for (i = 0; i < ARRAY_SIZE(editor_setup_tokens); i++)
11209 fprintf(file, "%s\n", getSetupLine(editor_setup_tokens, "", i));
11211 fprintf(file, "\n");
11212 for (i = 0; i < ARRAY_SIZE(shortcut_setup_tokens); i++)
11213 fprintf(file, "%s\n", getSetupLine(shortcut_setup_tokens, "", i));
11215 for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
11219 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
11220 fprintf(file, "\n");
11222 setup_input = setup.input[pnr];
11223 for (i = 0; i < ARRAY_SIZE(player_setup_tokens); i++)
11224 fprintf(file, "%s\n", getSetupLine(player_setup_tokens, prefix, i));
11227 fprintf(file, "\n");
11228 for (i = 0; i < ARRAY_SIZE(system_setup_tokens); i++)
11229 fprintf(file, "%s\n", getSetupLine(system_setup_tokens, "", i));
11231 // (internal setup values not saved to user setup file)
11233 fprintf(file, "\n");
11234 for (i = 0; i < ARRAY_SIZE(debug_setup_tokens); i++)
11235 if (!strPrefix(debug_setup_tokens[i].text, "debug.xsn_") ||
11236 setup.debug.xsn_mode != AUTO)
11237 fprintf(file, "%s\n", getSetupLine(debug_setup_tokens, "", i));
11239 fprintf(file, "\n");
11240 for (i = 0; i < ARRAY_SIZE(options_setup_tokens); i++)
11241 fprintf(file, "%s\n", getSetupLine(options_setup_tokens, "", i));
11245 SetFilePermissions(filename, PERMS_PRIVATE);
11248 void SaveSetup_AutoSetup(void)
11250 char *filename = getPath2(getSetupDir(), AUTOSETUP_FILENAME);
11254 InitUserDataDirectory();
11256 if (!(file = fopen(filename, MODE_WRITE)))
11258 Warn("cannot write auto setup file '%s'", filename);
11265 fprintFileHeader(file, AUTOSETUP_FILENAME);
11267 for (i = 0; i < ARRAY_SIZE(auto_setup_tokens); i++)
11268 fprintf(file, "%s\n", getSetupLine(auto_setup_tokens, "", i));
11272 SetFilePermissions(filename, PERMS_PRIVATE);
11277 void SaveSetup_ServerSetup(void)
11279 char *filename = getPath2(getSetupDir(), SERVERSETUP_FILENAME);
11283 InitUserDataDirectory();
11285 if (!(file = fopen(filename, MODE_WRITE)))
11287 Warn("cannot write server setup file '%s'", filename);
11294 fprintFileHeader(file, SERVERSETUP_FILENAME);
11296 for (i = 0; i < ARRAY_SIZE(server_setup_tokens); i++)
11298 // just to make things nicer :)
11299 if (server_setup_tokens[i].value == &setup.use_api_server)
11300 fprintf(file, "\n");
11302 fprintf(file, "%s\n", getSetupLine(server_setup_tokens, "", i));
11307 SetFilePermissions(filename, PERMS_PRIVATE);
11312 void SaveSetup_EditorCascade(void)
11314 char *filename = getPath2(getSetupDir(), EDITORCASCADE_FILENAME);
11318 InitUserDataDirectory();
11320 if (!(file = fopen(filename, MODE_WRITE)))
11322 Warn("cannot write editor cascade state file '%s'", filename);
11329 fprintFileHeader(file, EDITORCASCADE_FILENAME);
11331 for (i = 0; i < ARRAY_SIZE(editor_cascade_setup_tokens); i++)
11332 fprintf(file, "%s\n", getSetupLine(editor_cascade_setup_tokens, "", i));
11336 SetFilePermissions(filename, PERMS_PRIVATE);
11341 void SaveSetup(void)
11343 SaveSetup_Default();
11344 SaveSetup_AutoSetup();
11345 SaveSetup_ServerSetup();
11346 SaveSetup_EditorCascade();
11349 static void SaveSetup_WriteGameControllerMappings(SetupFileHash *mappings_hash,
11354 if (!(file = fopen(filename, MODE_WRITE)))
11356 Warn("cannot write game controller mappings file '%s'", filename);
11361 BEGIN_HASH_ITERATION(mappings_hash, itr)
11363 fprintf(file, "%s\n", HASH_ITERATION_VALUE(itr));
11365 END_HASH_ITERATION(mappings_hash, itr)
11370 void SaveSetup_AddGameControllerMapping(char *mapping)
11372 char *filename = getPath2(getSetupDir(), GAMECONTROLLER_BASENAME);
11373 SetupFileHash *mappings_hash = newSetupFileHash();
11375 InitUserDataDirectory();
11377 // load existing personal game controller mappings
11378 LoadSetup_ReadGameControllerMappings(mappings_hash, filename);
11380 // add new mapping to personal game controller mappings
11381 addGameControllerMappingToHash(mappings_hash, mapping);
11383 // save updated personal game controller mappings
11384 SaveSetup_WriteGameControllerMappings(mappings_hash, filename);
11386 freeSetupFileHash(mappings_hash);
11390 void LoadCustomElementDescriptions(void)
11392 char *filename = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
11393 SetupFileHash *setup_file_hash;
11396 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
11398 if (element_info[i].custom_description != NULL)
11400 free(element_info[i].custom_description);
11401 element_info[i].custom_description = NULL;
11405 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
11408 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
11410 char *token = getStringCat2(element_info[i].token_name, ".name");
11411 char *value = getHashEntry(setup_file_hash, token);
11414 element_info[i].custom_description = getStringCopy(value);
11419 freeSetupFileHash(setup_file_hash);
11422 static int getElementFromToken(char *token)
11424 char *value = getHashEntry(element_token_hash, token);
11427 return atoi(value);
11429 Warn("unknown element token '%s'", token);
11431 return EL_UNDEFINED;
11434 void FreeGlobalAnimEventInfo(void)
11436 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
11438 if (gaei->event_list == NULL)
11443 for (i = 0; i < gaei->num_event_lists; i++)
11445 checked_free(gaei->event_list[i]->event_value);
11446 checked_free(gaei->event_list[i]);
11449 checked_free(gaei->event_list);
11451 gaei->event_list = NULL;
11452 gaei->num_event_lists = 0;
11455 static int AddGlobalAnimEventList(void)
11457 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
11458 int list_pos = gaei->num_event_lists++;
11460 gaei->event_list = checked_realloc(gaei->event_list, gaei->num_event_lists *
11461 sizeof(struct GlobalAnimEventListInfo *));
11463 gaei->event_list[list_pos] =
11464 checked_calloc(sizeof(struct GlobalAnimEventListInfo));
11466 struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
11468 gaeli->event_value = NULL;
11469 gaeli->num_event_values = 0;
11474 static int AddGlobalAnimEventValue(int list_pos, int event_value)
11476 // do not add empty global animation events
11477 if (event_value == ANIM_EVENT_NONE)
11480 // if list position is undefined, create new list
11481 if (list_pos == ANIM_EVENT_UNDEFINED)
11482 list_pos = AddGlobalAnimEventList();
11484 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
11485 struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
11486 int value_pos = gaeli->num_event_values++;
11488 gaeli->event_value = checked_realloc(gaeli->event_value,
11489 gaeli->num_event_values * sizeof(int *));
11491 gaeli->event_value[value_pos] = event_value;
11496 int GetGlobalAnimEventValue(int list_pos, int value_pos)
11498 if (list_pos == ANIM_EVENT_UNDEFINED)
11499 return ANIM_EVENT_NONE;
11501 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
11502 struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
11504 return gaeli->event_value[value_pos];
11507 int GetGlobalAnimEventValueCount(int list_pos)
11509 if (list_pos == ANIM_EVENT_UNDEFINED)
11512 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
11513 struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
11515 return gaeli->num_event_values;
11518 // This function checks if a string <s> of the format "string1, string2, ..."
11519 // exactly contains a string <s_contained>.
11521 static boolean string_has_parameter(char *s, char *s_contained)
11525 if (s == NULL || s_contained == NULL)
11528 if (strlen(s_contained) > strlen(s))
11531 if (strncmp(s, s_contained, strlen(s_contained)) == 0)
11533 char next_char = s[strlen(s_contained)];
11535 // check if next character is delimiter or whitespace
11536 if (next_char == ',' || next_char == '\0' ||
11537 next_char == ' ' || next_char == '\t')
11541 // check if string contains another parameter string after a comma
11542 substring = strchr(s, ',');
11543 if (substring == NULL) // string does not contain a comma
11546 // advance string pointer to next character after the comma
11549 // skip potential whitespaces after the comma
11550 while (*substring == ' ' || *substring == '\t')
11553 return string_has_parameter(substring, s_contained);
11556 static int get_anim_parameter_value_ce(char *s)
11559 char *pattern_1 = "ce_change:custom_";
11560 char *pattern_2 = ".page_";
11561 int pattern_1_len = strlen(pattern_1);
11562 char *matching_char = strstr(s_ptr, pattern_1);
11563 int result = ANIM_EVENT_NONE;
11565 if (matching_char == NULL)
11566 return ANIM_EVENT_NONE;
11568 result = ANIM_EVENT_CE_CHANGE;
11570 s_ptr = matching_char + pattern_1_len;
11572 // check for custom element number ("custom_X", "custom_XX" or "custom_XXX")
11573 if (*s_ptr >= '0' && *s_ptr <= '9')
11575 int gic_ce_nr = (*s_ptr++ - '0');
11577 if (*s_ptr >= '0' && *s_ptr <= '9')
11579 gic_ce_nr = 10 * gic_ce_nr + (*s_ptr++ - '0');
11581 if (*s_ptr >= '0' && *s_ptr <= '9')
11582 gic_ce_nr = 10 * gic_ce_nr + (*s_ptr++ - '0');
11585 if (gic_ce_nr < 1 || gic_ce_nr > NUM_CUSTOM_ELEMENTS)
11586 return ANIM_EVENT_NONE;
11588 // custom element stored as 0 to 255
11591 result |= gic_ce_nr << ANIM_EVENT_CE_BIT;
11595 // invalid custom element number specified
11597 return ANIM_EVENT_NONE;
11600 // check for change page number ("page_X" or "page_XX") (optional)
11601 if (strPrefix(s_ptr, pattern_2))
11603 s_ptr += strlen(pattern_2);
11605 if (*s_ptr >= '0' && *s_ptr <= '9')
11607 int gic_page_nr = (*s_ptr++ - '0');
11609 if (*s_ptr >= '0' && *s_ptr <= '9')
11610 gic_page_nr = 10 * gic_page_nr + (*s_ptr++ - '0');
11612 if (gic_page_nr < 1 || gic_page_nr > MAX_CHANGE_PAGES)
11613 return ANIM_EVENT_NONE;
11615 // change page stored as 1 to 32 (0 means "all change pages")
11617 result |= gic_page_nr << ANIM_EVENT_PAGE_BIT;
11621 // invalid animation part number specified
11623 return ANIM_EVENT_NONE;
11627 // discard result if next character is neither delimiter nor whitespace
11628 if (!(*s_ptr == ',' || *s_ptr == '\0' ||
11629 *s_ptr == ' ' || *s_ptr == '\t'))
11630 return ANIM_EVENT_NONE;
11635 static int get_anim_parameter_value(char *s)
11637 int event_value[] =
11645 char *pattern_1[] =
11653 char *pattern_2 = ".part_";
11654 char *matching_char = NULL;
11656 int pattern_1_len = 0;
11657 int result = ANIM_EVENT_NONE;
11660 result = get_anim_parameter_value_ce(s);
11662 if (result != ANIM_EVENT_NONE)
11665 for (i = 0; i < ARRAY_SIZE(event_value); i++)
11667 matching_char = strstr(s_ptr, pattern_1[i]);
11668 pattern_1_len = strlen(pattern_1[i]);
11669 result = event_value[i];
11671 if (matching_char != NULL)
11675 if (matching_char == NULL)
11676 return ANIM_EVENT_NONE;
11678 s_ptr = matching_char + pattern_1_len;
11680 // check for main animation number ("anim_X" or "anim_XX")
11681 if (*s_ptr >= '0' && *s_ptr <= '9')
11683 int gic_anim_nr = (*s_ptr++ - '0');
11685 if (*s_ptr >= '0' && *s_ptr <= '9')
11686 gic_anim_nr = 10 * gic_anim_nr + (*s_ptr++ - '0');
11688 if (gic_anim_nr < 1 || gic_anim_nr > MAX_GLOBAL_ANIMS)
11689 return ANIM_EVENT_NONE;
11691 result |= gic_anim_nr << ANIM_EVENT_ANIM_BIT;
11695 // invalid main animation number specified
11697 return ANIM_EVENT_NONE;
11700 // check for animation part number ("part_X" or "part_XX") (optional)
11701 if (strPrefix(s_ptr, pattern_2))
11703 s_ptr += strlen(pattern_2);
11705 if (*s_ptr >= '0' && *s_ptr <= '9')
11707 int gic_part_nr = (*s_ptr++ - '0');
11709 if (*s_ptr >= '0' && *s_ptr <= '9')
11710 gic_part_nr = 10 * gic_part_nr + (*s_ptr++ - '0');
11712 if (gic_part_nr < 1 || gic_part_nr > MAX_GLOBAL_ANIM_PARTS)
11713 return ANIM_EVENT_NONE;
11715 result |= gic_part_nr << ANIM_EVENT_PART_BIT;
11719 // invalid animation part number specified
11721 return ANIM_EVENT_NONE;
11725 // discard result if next character is neither delimiter nor whitespace
11726 if (!(*s_ptr == ',' || *s_ptr == '\0' ||
11727 *s_ptr == ' ' || *s_ptr == '\t'))
11728 return ANIM_EVENT_NONE;
11733 static int get_anim_parameter_values(char *s)
11735 int list_pos = ANIM_EVENT_UNDEFINED;
11736 int event_value = ANIM_EVENT_DEFAULT;
11738 if (string_has_parameter(s, "any"))
11739 event_value |= ANIM_EVENT_ANY;
11741 if (string_has_parameter(s, "click:self") ||
11742 string_has_parameter(s, "click") ||
11743 string_has_parameter(s, "self"))
11744 event_value |= ANIM_EVENT_SELF;
11746 if (string_has_parameter(s, "unclick:any"))
11747 event_value |= ANIM_EVENT_UNCLICK_ANY;
11749 // if animation event found, add it to global animation event list
11750 if (event_value != ANIM_EVENT_NONE)
11751 list_pos = AddGlobalAnimEventValue(list_pos, event_value);
11755 // add optional "click:anim_X" or "click:anim_X.part_X" parameter
11756 event_value = get_anim_parameter_value(s);
11758 // if animation event found, add it to global animation event list
11759 if (event_value != ANIM_EVENT_NONE)
11760 list_pos = AddGlobalAnimEventValue(list_pos, event_value);
11762 // continue with next part of the string, starting with next comma
11763 s = strchr(s + 1, ',');
11769 static int get_anim_action_parameter_value(char *token)
11771 // check most common default case first to massively speed things up
11772 if (strEqual(token, ARG_UNDEFINED))
11773 return ANIM_EVENT_ACTION_NONE;
11775 int result = getImageIDFromToken(token);
11779 char *gfx_token = getStringCat2("gfx.", token);
11781 result = getImageIDFromToken(gfx_token);
11783 checked_free(gfx_token);
11788 Key key = getKeyFromX11KeyName(token);
11790 if (key != KSYM_UNDEFINED)
11791 result = -(int)key;
11798 result = get_hash_from_key(token); // unsigned int => int
11799 result = ABS(result); // may be negative now
11800 result += (result < MAX_IMAGE_FILES ? MAX_IMAGE_FILES : 0);
11802 setHashEntry(anim_url_hash, int2str(result, 0), token);
11807 result = ANIM_EVENT_ACTION_NONE;
11812 int get_parameter_value(char *value_raw, char *suffix, int type)
11814 char *value = getStringToLower(value_raw);
11815 int result = 0; // probably a save default value
11817 if (strEqual(suffix, ".direction"))
11819 result = (strEqual(value, "left") ? MV_LEFT :
11820 strEqual(value, "right") ? MV_RIGHT :
11821 strEqual(value, "up") ? MV_UP :
11822 strEqual(value, "down") ? MV_DOWN : MV_NONE);
11824 else if (strEqual(suffix, ".position"))
11826 result = (strEqual(value, "left") ? POS_LEFT :
11827 strEqual(value, "right") ? POS_RIGHT :
11828 strEqual(value, "top") ? POS_TOP :
11829 strEqual(value, "upper") ? POS_UPPER :
11830 strEqual(value, "middle") ? POS_MIDDLE :
11831 strEqual(value, "lower") ? POS_LOWER :
11832 strEqual(value, "bottom") ? POS_BOTTOM :
11833 strEqual(value, "any") ? POS_ANY :
11834 strEqual(value, "ce") ? POS_CE :
11835 strEqual(value, "ce_trigger") ? POS_CE_TRIGGER :
11836 strEqual(value, "last") ? POS_LAST : POS_UNDEFINED);
11838 else if (strEqual(suffix, ".align"))
11840 result = (strEqual(value, "left") ? ALIGN_LEFT :
11841 strEqual(value, "right") ? ALIGN_RIGHT :
11842 strEqual(value, "center") ? ALIGN_CENTER :
11843 strEqual(value, "middle") ? ALIGN_CENTER : ALIGN_DEFAULT);
11845 else if (strEqual(suffix, ".valign"))
11847 result = (strEqual(value, "top") ? VALIGN_TOP :
11848 strEqual(value, "bottom") ? VALIGN_BOTTOM :
11849 strEqual(value, "middle") ? VALIGN_MIDDLE :
11850 strEqual(value, "center") ? VALIGN_MIDDLE : VALIGN_DEFAULT);
11852 else if (strEqual(suffix, ".anim_mode"))
11854 result = (string_has_parameter(value, "none") ? ANIM_NONE :
11855 string_has_parameter(value, "loop") ? ANIM_LOOP :
11856 string_has_parameter(value, "linear") ? ANIM_LINEAR :
11857 string_has_parameter(value, "pingpong") ? ANIM_PINGPONG :
11858 string_has_parameter(value, "pingpong2") ? ANIM_PINGPONG2 :
11859 string_has_parameter(value, "random") ? ANIM_RANDOM :
11860 string_has_parameter(value, "random_static") ? ANIM_RANDOM_STATIC :
11861 string_has_parameter(value, "ce_value") ? ANIM_CE_VALUE :
11862 string_has_parameter(value, "ce_score") ? ANIM_CE_SCORE :
11863 string_has_parameter(value, "ce_delay") ? ANIM_CE_DELAY :
11864 string_has_parameter(value, "horizontal") ? ANIM_HORIZONTAL :
11865 string_has_parameter(value, "vertical") ? ANIM_VERTICAL :
11866 string_has_parameter(value, "centered") ? ANIM_CENTERED :
11867 string_has_parameter(value, "all") ? ANIM_ALL :
11868 string_has_parameter(value, "tiled") ? ANIM_TILED :
11869 string_has_parameter(value, "level_nr") ? ANIM_LEVEL_NR :
11872 if (string_has_parameter(value, "once"))
11873 result |= ANIM_ONCE;
11875 if (string_has_parameter(value, "reverse"))
11876 result |= ANIM_REVERSE;
11878 if (string_has_parameter(value, "opaque_player"))
11879 result |= ANIM_OPAQUE_PLAYER;
11881 if (string_has_parameter(value, "static_panel"))
11882 result |= ANIM_STATIC_PANEL;
11884 else if (strEqual(suffix, ".init_event") ||
11885 strEqual(suffix, ".anim_event"))
11887 result = get_anim_parameter_values(value);
11889 else if (strEqual(suffix, ".init_delay_action") ||
11890 strEqual(suffix, ".anim_delay_action") ||
11891 strEqual(suffix, ".post_delay_action") ||
11892 strEqual(suffix, ".init_event_action") ||
11893 strEqual(suffix, ".anim_event_action"))
11895 result = get_anim_action_parameter_value(value_raw);
11897 else if (strEqual(suffix, ".class"))
11899 result = (strEqual(value, ARG_UNDEFINED) ? ARG_UNDEFINED_VALUE :
11900 get_hash_from_key(value));
11902 else if (strEqual(suffix, ".style"))
11904 result = STYLE_DEFAULT;
11906 if (string_has_parameter(value, "accurate_borders"))
11907 result |= STYLE_ACCURATE_BORDERS;
11909 if (string_has_parameter(value, "inner_corners"))
11910 result |= STYLE_INNER_CORNERS;
11912 if (string_has_parameter(value, "reverse"))
11913 result |= STYLE_REVERSE;
11915 if (string_has_parameter(value, "leftmost_position"))
11916 result |= STYLE_LEFTMOST_POSITION;
11918 if (string_has_parameter(value, "block_clicks"))
11919 result |= STYLE_BLOCK;
11921 if (string_has_parameter(value, "passthrough_clicks"))
11922 result |= STYLE_PASSTHROUGH;
11924 if (string_has_parameter(value, "multiple_actions"))
11925 result |= STYLE_MULTIPLE_ACTIONS;
11927 if (string_has_parameter(value, "consume_ce_event"))
11928 result |= STYLE_CONSUME_CE_EVENT;
11930 else if (strEqual(suffix, ".fade_mode"))
11932 result = (string_has_parameter(value, "none") ? FADE_MODE_NONE :
11933 string_has_parameter(value, "fade") ? FADE_MODE_FADE :
11934 string_has_parameter(value, "fade_in") ? FADE_MODE_FADE_IN :
11935 string_has_parameter(value, "fade_out") ? FADE_MODE_FADE_OUT :
11936 string_has_parameter(value, "crossfade") ? FADE_MODE_CROSSFADE :
11937 string_has_parameter(value, "melt") ? FADE_MODE_MELT :
11938 string_has_parameter(value, "curtain") ? FADE_MODE_CURTAIN :
11939 FADE_MODE_DEFAULT);
11941 else if (strEqual(suffix, ".auto_delay_unit"))
11943 result = (string_has_parameter(value, "ms") ? AUTO_DELAY_UNIT_MS :
11944 string_has_parameter(value, "frames") ? AUTO_DELAY_UNIT_FRAMES :
11945 AUTO_DELAY_UNIT_DEFAULT);
11947 else if (strPrefix(suffix, ".font")) // (may also be ".font_xyz")
11949 result = gfx.get_font_from_token_function(value);
11951 else // generic parameter of type integer or boolean
11953 result = (strEqual(value, ARG_UNDEFINED) ? ARG_UNDEFINED_VALUE :
11954 type == TYPE_INTEGER ? get_integer_from_string(value) :
11955 type == TYPE_BOOLEAN ? get_boolean_from_string(value) :
11956 ARG_UNDEFINED_VALUE);
11964 static int get_token_parameter_value(char *token, char *value_raw)
11968 if (token == NULL || value_raw == NULL)
11969 return ARG_UNDEFINED_VALUE;
11971 suffix = strrchr(token, '.');
11972 if (suffix == NULL)
11975 if (strEqual(suffix, ".element"))
11976 return getElementFromToken(value_raw);
11978 // !!! USE CORRECT VALUE TYPE (currently works also for TYPE_BOOLEAN) !!!
11979 return get_parameter_value(value_raw, suffix, TYPE_INTEGER);
11982 void InitMenuDesignSettings_FromHash(SetupFileHash *setup_file_hash,
11983 boolean ignore_defaults)
11987 for (i = 0; image_config_vars[i].token != NULL; i++)
11989 char *value = getHashEntry(setup_file_hash, image_config_vars[i].token);
11991 // (ignore definitions set to "[DEFAULT]" which are already initialized)
11992 if (ignore_defaults && strEqual(value, ARG_DEFAULT))
11996 *image_config_vars[i].value =
11997 get_token_parameter_value(image_config_vars[i].token, value);
12001 void InitMenuDesignSettings_Static(void)
12003 // always start with reliable default values from static default config
12004 InitMenuDesignSettings_FromHash(image_config_hash, FALSE);
12007 static void InitMenuDesignSettings_SpecialPreProcessing(void)
12011 // the following initializes hierarchical values from static configuration
12013 // special case: initialize "ARG_DEFAULT" values in static default config
12014 // (e.g., initialize "[titlemessage].fade_mode" from "[title].fade_mode")
12015 titlescreen_initial_first_default.fade_mode =
12016 title_initial_first_default.fade_mode;
12017 titlescreen_initial_first_default.fade_delay =
12018 title_initial_first_default.fade_delay;
12019 titlescreen_initial_first_default.post_delay =
12020 title_initial_first_default.post_delay;
12021 titlescreen_initial_first_default.auto_delay =
12022 title_initial_first_default.auto_delay;
12023 titlescreen_initial_first_default.auto_delay_unit =
12024 title_initial_first_default.auto_delay_unit;
12025 titlescreen_first_default.fade_mode = title_first_default.fade_mode;
12026 titlescreen_first_default.fade_delay = title_first_default.fade_delay;
12027 titlescreen_first_default.post_delay = title_first_default.post_delay;
12028 titlescreen_first_default.auto_delay = title_first_default.auto_delay;
12029 titlescreen_first_default.auto_delay_unit =
12030 title_first_default.auto_delay_unit;
12031 titlemessage_initial_first_default.fade_mode =
12032 title_initial_first_default.fade_mode;
12033 titlemessage_initial_first_default.fade_delay =
12034 title_initial_first_default.fade_delay;
12035 titlemessage_initial_first_default.post_delay =
12036 title_initial_first_default.post_delay;
12037 titlemessage_initial_first_default.auto_delay =
12038 title_initial_first_default.auto_delay;
12039 titlemessage_initial_first_default.auto_delay_unit =
12040 title_initial_first_default.auto_delay_unit;
12041 titlemessage_first_default.fade_mode = title_first_default.fade_mode;
12042 titlemessage_first_default.fade_delay = title_first_default.fade_delay;
12043 titlemessage_first_default.post_delay = title_first_default.post_delay;
12044 titlemessage_first_default.auto_delay = title_first_default.auto_delay;
12045 titlemessage_first_default.auto_delay_unit =
12046 title_first_default.auto_delay_unit;
12048 titlescreen_initial_default.fade_mode = title_initial_default.fade_mode;
12049 titlescreen_initial_default.fade_delay = title_initial_default.fade_delay;
12050 titlescreen_initial_default.post_delay = title_initial_default.post_delay;
12051 titlescreen_initial_default.auto_delay = title_initial_default.auto_delay;
12052 titlescreen_initial_default.auto_delay_unit =
12053 title_initial_default.auto_delay_unit;
12054 titlescreen_default.fade_mode = title_default.fade_mode;
12055 titlescreen_default.fade_delay = title_default.fade_delay;
12056 titlescreen_default.post_delay = title_default.post_delay;
12057 titlescreen_default.auto_delay = title_default.auto_delay;
12058 titlescreen_default.auto_delay_unit = title_default.auto_delay_unit;
12059 titlemessage_initial_default.fade_mode = title_initial_default.fade_mode;
12060 titlemessage_initial_default.fade_delay = title_initial_default.fade_delay;
12061 titlemessage_initial_default.post_delay = title_initial_default.post_delay;
12062 titlemessage_initial_default.auto_delay_unit =
12063 title_initial_default.auto_delay_unit;
12064 titlemessage_default.fade_mode = title_default.fade_mode;
12065 titlemessage_default.fade_delay = title_default.fade_delay;
12066 titlemessage_default.post_delay = title_default.post_delay;
12067 titlemessage_default.auto_delay = title_default.auto_delay;
12068 titlemessage_default.auto_delay_unit = title_default.auto_delay_unit;
12070 // special case: initialize "ARG_DEFAULT" values in static default config
12071 // (e.g., init "titlemessage_1.fade_mode" from "[titlemessage].fade_mode")
12072 for (i = 0; i < MAX_NUM_TITLE_MESSAGES; i++)
12074 titlescreen_initial_first[i] = titlescreen_initial_first_default;
12075 titlescreen_first[i] = titlescreen_first_default;
12076 titlemessage_initial_first[i] = titlemessage_initial_first_default;
12077 titlemessage_first[i] = titlemessage_first_default;
12079 titlescreen_initial[i] = titlescreen_initial_default;
12080 titlescreen[i] = titlescreen_default;
12081 titlemessage_initial[i] = titlemessage_initial_default;
12082 titlemessage[i] = titlemessage_default;
12085 // special case: initialize "ARG_DEFAULT" values in static default config
12086 // (eg, init "menu.enter_screen.SCORES.xyz" from "menu.enter_screen.xyz")
12087 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
12089 if (i == GFX_SPECIAL_ARG_TITLE) // title values already initialized
12092 menu.enter_screen[i] = menu.enter_screen[GFX_SPECIAL_ARG_DEFAULT];
12093 menu.leave_screen[i] = menu.leave_screen[GFX_SPECIAL_ARG_DEFAULT];
12094 menu.next_screen[i] = menu.next_screen[GFX_SPECIAL_ARG_DEFAULT];
12097 // special case: initialize "ARG_DEFAULT" values in static default config
12098 // (eg, init "viewport.door_1.MAIN.xyz" from "viewport.door_1.xyz")
12099 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
12101 viewport.window[i] = viewport.window[GFX_SPECIAL_ARG_DEFAULT];
12102 viewport.playfield[i] = viewport.playfield[GFX_SPECIAL_ARG_DEFAULT];
12103 viewport.door_1[i] = viewport.door_1[GFX_SPECIAL_ARG_DEFAULT];
12105 if (i == GFX_SPECIAL_ARG_EDITOR) // editor values already initialized
12108 viewport.door_2[i] = viewport.door_2[GFX_SPECIAL_ARG_DEFAULT];
12112 static void InitMenuDesignSettings_SpecialPostProcessing(void)
12116 struct XY *dst, *src;
12118 game_buttons_xy[] =
12120 { &game.button.save, &game.button.stop },
12121 { &game.button.pause2, &game.button.pause },
12122 { &game.button.load, &game.button.play },
12123 { &game.button.undo, &game.button.stop },
12124 { &game.button.redo, &game.button.play },
12130 // special case: initialize later added SETUP list size from LEVELS value
12131 if (menu.list_size[GAME_MODE_SETUP] == -1)
12132 menu.list_size[GAME_MODE_SETUP] = menu.list_size[GAME_MODE_LEVELS];
12134 // set default position for snapshot buttons to stop/pause/play buttons
12135 for (i = 0; game_buttons_xy[i].dst != NULL; i++)
12136 if ((*game_buttons_xy[i].dst).x == -1 &&
12137 (*game_buttons_xy[i].dst).y == -1)
12138 *game_buttons_xy[i].dst = *game_buttons_xy[i].src;
12140 // --------------------------------------------------------------------------
12141 // dynamic viewports (including playfield margins, borders and alignments)
12142 // --------------------------------------------------------------------------
12144 // dynamic viewports currently only supported for landscape mode
12145 int display_width = MAX(video.display_width, video.display_height);
12146 int display_height = MIN(video.display_width, video.display_height);
12148 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
12150 struct RectWithBorder *vp_window = &viewport.window[i];
12151 struct RectWithBorder *vp_playfield = &viewport.playfield[i];
12152 struct RectWithBorder *vp_door_1 = &viewport.door_1[i];
12153 struct RectWithBorder *vp_door_2 = &viewport.door_2[i];
12154 boolean dynamic_window_width = (vp_window->min_width != -1);
12155 boolean dynamic_window_height = (vp_window->min_height != -1);
12156 boolean dynamic_playfield_width = (vp_playfield->min_width != -1);
12157 boolean dynamic_playfield_height = (vp_playfield->min_height != -1);
12159 // adjust window size if min/max width/height is specified
12161 if (vp_window->min_width != -1)
12163 int window_width = display_width;
12165 // when using static window height, use aspect ratio of display
12166 if (vp_window->min_height == -1)
12167 window_width = vp_window->height * display_width / display_height;
12169 vp_window->width = MAX(vp_window->min_width, window_width);
12172 if (vp_window->min_height != -1)
12174 int window_height = display_height;
12176 // when using static window width, use aspect ratio of display
12177 if (vp_window->min_width == -1)
12178 window_height = vp_window->width * display_height / display_width;
12180 vp_window->height = MAX(vp_window->min_height, window_height);
12183 if (vp_window->max_width != -1)
12184 vp_window->width = MIN(vp_window->width, vp_window->max_width);
12186 if (vp_window->max_height != -1)
12187 vp_window->height = MIN(vp_window->height, vp_window->max_height);
12189 int playfield_width = vp_window->width;
12190 int playfield_height = vp_window->height;
12192 // adjust playfield size and position according to specified margins
12194 playfield_width -= vp_playfield->margin_left;
12195 playfield_width -= vp_playfield->margin_right;
12197 playfield_height -= vp_playfield->margin_top;
12198 playfield_height -= vp_playfield->margin_bottom;
12200 // adjust playfield size if min/max width/height is specified
12202 if (vp_playfield->min_width != -1)
12203 vp_playfield->width = MAX(vp_playfield->min_width, playfield_width);
12205 if (vp_playfield->min_height != -1)
12206 vp_playfield->height = MAX(vp_playfield->min_height, playfield_height);
12208 if (vp_playfield->max_width != -1)
12209 vp_playfield->width = MIN(vp_playfield->width, vp_playfield->max_width);
12211 if (vp_playfield->max_height != -1)
12212 vp_playfield->height = MIN(vp_playfield->height, vp_playfield->max_height);
12214 // adjust playfield position according to specified alignment
12216 if (vp_playfield->align == ALIGN_LEFT || vp_playfield->x > 0)
12217 vp_playfield->x = ALIGNED_VP_XPOS(vp_playfield);
12218 else if (vp_playfield->align == ALIGN_CENTER)
12219 vp_playfield->x = playfield_width / 2 - vp_playfield->width / 2;
12220 else if (vp_playfield->align == ALIGN_RIGHT)
12221 vp_playfield->x += playfield_width - vp_playfield->width;
12223 if (vp_playfield->valign == VALIGN_TOP || vp_playfield->y > 0)
12224 vp_playfield->y = ALIGNED_VP_YPOS(vp_playfield);
12225 else if (vp_playfield->valign == VALIGN_MIDDLE)
12226 vp_playfield->y = playfield_height / 2 - vp_playfield->height / 2;
12227 else if (vp_playfield->valign == VALIGN_BOTTOM)
12228 vp_playfield->y += playfield_height - vp_playfield->height;
12230 vp_playfield->x += vp_playfield->margin_left;
12231 vp_playfield->y += vp_playfield->margin_top;
12233 // adjust individual playfield borders if only default border is specified
12235 if (vp_playfield->border_left == -1)
12236 vp_playfield->border_left = vp_playfield->border_size;
12237 if (vp_playfield->border_right == -1)
12238 vp_playfield->border_right = vp_playfield->border_size;
12239 if (vp_playfield->border_top == -1)
12240 vp_playfield->border_top = vp_playfield->border_size;
12241 if (vp_playfield->border_bottom == -1)
12242 vp_playfield->border_bottom = vp_playfield->border_size;
12244 // set dynamic playfield borders if borders are specified as undefined
12245 // (but only if window size was dynamic and playfield size was static)
12247 if (dynamic_window_width && !dynamic_playfield_width)
12249 if (vp_playfield->border_left == -1)
12251 vp_playfield->border_left = (vp_playfield->x -
12252 vp_playfield->margin_left);
12253 vp_playfield->x -= vp_playfield->border_left;
12254 vp_playfield->width += vp_playfield->border_left;
12257 if (vp_playfield->border_right == -1)
12259 vp_playfield->border_right = (vp_window->width -
12261 vp_playfield->width -
12262 vp_playfield->margin_right);
12263 vp_playfield->width += vp_playfield->border_right;
12267 if (dynamic_window_height && !dynamic_playfield_height)
12269 if (vp_playfield->border_top == -1)
12271 vp_playfield->border_top = (vp_playfield->y -
12272 vp_playfield->margin_top);
12273 vp_playfield->y -= vp_playfield->border_top;
12274 vp_playfield->height += vp_playfield->border_top;
12277 if (vp_playfield->border_bottom == -1)
12279 vp_playfield->border_bottom = (vp_window->height -
12281 vp_playfield->height -
12282 vp_playfield->margin_bottom);
12283 vp_playfield->height += vp_playfield->border_bottom;
12287 // adjust playfield size to be a multiple of a defined alignment tile size
12289 int align_size = vp_playfield->align_size;
12290 int playfield_xtiles = vp_playfield->width / align_size;
12291 int playfield_ytiles = vp_playfield->height / align_size;
12292 int playfield_width_corrected = playfield_xtiles * align_size;
12293 int playfield_height_corrected = playfield_ytiles * align_size;
12294 boolean is_playfield_mode = (i == GFX_SPECIAL_ARG_PLAYING ||
12295 i == GFX_SPECIAL_ARG_EDITOR);
12297 if (is_playfield_mode &&
12298 dynamic_playfield_width &&
12299 vp_playfield->width != playfield_width_corrected)
12301 int playfield_xdiff = vp_playfield->width - playfield_width_corrected;
12303 vp_playfield->width = playfield_width_corrected;
12305 if (vp_playfield->align == ALIGN_LEFT)
12307 vp_playfield->border_left += playfield_xdiff;
12309 else if (vp_playfield->align == ALIGN_RIGHT)
12311 vp_playfield->border_right += playfield_xdiff;
12313 else if (vp_playfield->align == ALIGN_CENTER)
12315 int border_left_diff = playfield_xdiff / 2;
12316 int border_right_diff = playfield_xdiff - border_left_diff;
12318 vp_playfield->border_left += border_left_diff;
12319 vp_playfield->border_right += border_right_diff;
12323 if (is_playfield_mode &&
12324 dynamic_playfield_height &&
12325 vp_playfield->height != playfield_height_corrected)
12327 int playfield_ydiff = vp_playfield->height - playfield_height_corrected;
12329 vp_playfield->height = playfield_height_corrected;
12331 if (vp_playfield->valign == VALIGN_TOP)
12333 vp_playfield->border_top += playfield_ydiff;
12335 else if (vp_playfield->align == VALIGN_BOTTOM)
12337 vp_playfield->border_right += playfield_ydiff;
12339 else if (vp_playfield->align == VALIGN_MIDDLE)
12341 int border_top_diff = playfield_ydiff / 2;
12342 int border_bottom_diff = playfield_ydiff - border_top_diff;
12344 vp_playfield->border_top += border_top_diff;
12345 vp_playfield->border_bottom += border_bottom_diff;
12349 // adjust door positions according to specified alignment
12351 for (j = 0; j < 2; j++)
12353 struct RectWithBorder *vp_door = (j == 0 ? vp_door_1 : vp_door_2);
12355 if (vp_door->align == ALIGN_LEFT || vp_door->x > 0)
12356 vp_door->x = ALIGNED_VP_XPOS(vp_door);
12357 else if (vp_door->align == ALIGN_CENTER)
12358 vp_door->x = vp_window->width / 2 - vp_door->width / 2;
12359 else if (vp_door->align == ALIGN_RIGHT)
12360 vp_door->x += vp_window->width - vp_door->width;
12362 if (vp_door->valign == VALIGN_TOP || vp_door->y > 0)
12363 vp_door->y = ALIGNED_VP_YPOS(vp_door);
12364 else if (vp_door->valign == VALIGN_MIDDLE)
12365 vp_door->y = vp_window->height / 2 - vp_door->height / 2;
12366 else if (vp_door->valign == VALIGN_BOTTOM)
12367 vp_door->y += vp_window->height - vp_door->height;
12372 static void InitMenuDesignSettings_SpecialPostProcessing_AfterGraphics(void)
12376 struct XYTileSize *dst, *src;
12379 editor_buttons_xy[] =
12382 &editor.button.element_left, &editor.palette.element_left,
12383 IMG_GFX_EDITOR_BUTTON_ELEMENT_LEFT
12386 &editor.button.element_middle, &editor.palette.element_middle,
12387 IMG_GFX_EDITOR_BUTTON_ELEMENT_MIDDLE
12390 &editor.button.element_right, &editor.palette.element_right,
12391 IMG_GFX_EDITOR_BUTTON_ELEMENT_RIGHT
12398 // set default position for element buttons to element graphics
12399 for (i = 0; editor_buttons_xy[i].dst != NULL; i++)
12401 if ((*editor_buttons_xy[i].dst).x == -1 &&
12402 (*editor_buttons_xy[i].dst).y == -1)
12404 struct GraphicInfo *gd = &graphic_info[editor_buttons_xy[i].graphic];
12406 gd->width = gd->height = editor_buttons_xy[i].src->tile_size;
12408 *editor_buttons_xy[i].dst = *editor_buttons_xy[i].src;
12412 // adjust editor palette rows and columns if specified to be dynamic
12414 if (editor.palette.cols == -1)
12416 int vp_width = viewport.playfield[GFX_SPECIAL_ARG_EDITOR].width;
12417 int bt_width = graphic_info[IMG_EDITOR_PALETTE_BUTTON].width;
12418 int sc_width = graphic_info[IMG_EDITOR_PALETTE_SCROLLBAR].width;
12420 editor.palette.cols = (vp_width - sc_width) / bt_width;
12422 if (editor.palette.x == -1)
12424 int palette_width = editor.palette.cols * bt_width + sc_width;
12426 editor.palette.x = (vp_width - palette_width) / 2;
12430 if (editor.palette.rows == -1)
12432 int vp_height = viewport.playfield[GFX_SPECIAL_ARG_EDITOR].height;
12433 int bt_height = graphic_info[IMG_EDITOR_PALETTE_BUTTON].height;
12434 int tx_height = getFontHeight(FONT_TEXT_2);
12436 editor.palette.rows = (vp_height - tx_height) / bt_height;
12438 if (editor.palette.y == -1)
12440 int palette_height = editor.palette.rows * bt_height + tx_height;
12442 editor.palette.y = (vp_height - palette_height) / 2;
12447 static void InitMenuDesignSettings_PreviewPlayers_Ext(SetupFileHash *hash,
12448 boolean initialize)
12450 // special case: check if network and preview player positions are redefined,
12451 // to compare this later against the main menu level preview being redefined
12452 struct TokenIntPtrInfo menu_config_players[] =
12454 { "main.network_players.x", &menu.main.network_players.redefined },
12455 { "main.network_players.y", &menu.main.network_players.redefined },
12456 { "main.preview_players.x", &menu.main.preview_players.redefined },
12457 { "main.preview_players.y", &menu.main.preview_players.redefined },
12458 { "preview.x", &preview.redefined },
12459 { "preview.y", &preview.redefined }
12465 for (i = 0; i < ARRAY_SIZE(menu_config_players); i++)
12466 *menu_config_players[i].value = FALSE;
12470 for (i = 0; i < ARRAY_SIZE(menu_config_players); i++)
12471 if (getHashEntry(hash, menu_config_players[i].token) != NULL)
12472 *menu_config_players[i].value = TRUE;
12476 static void InitMenuDesignSettings_PreviewPlayers(void)
12478 InitMenuDesignSettings_PreviewPlayers_Ext(NULL, TRUE);
12481 static void InitMenuDesignSettings_PreviewPlayers_FromHash(SetupFileHash *hash)
12483 InitMenuDesignSettings_PreviewPlayers_Ext(hash, FALSE);
12486 static void LoadMenuDesignSettingsFromFilename(char *filename)
12488 static struct TitleFadingInfo tfi;
12489 static struct TitleMessageInfo tmi;
12490 static struct TokenInfo title_tokens[] =
12492 { TYPE_INTEGER, &tfi.fade_mode, ".fade_mode" },
12493 { TYPE_INTEGER, &tfi.fade_delay, ".fade_delay" },
12494 { TYPE_INTEGER, &tfi.post_delay, ".post_delay" },
12495 { TYPE_INTEGER, &tfi.auto_delay, ".auto_delay" },
12496 { TYPE_INTEGER, &tfi.auto_delay_unit, ".auto_delay_unit" },
12500 static struct TokenInfo titlemessage_tokens[] =
12502 { TYPE_INTEGER, &tmi.x, ".x" },
12503 { TYPE_INTEGER, &tmi.y, ".y" },
12504 { TYPE_INTEGER, &tmi.width, ".width" },
12505 { TYPE_INTEGER, &tmi.height, ".height" },
12506 { TYPE_INTEGER, &tmi.chars, ".chars" },
12507 { TYPE_INTEGER, &tmi.lines, ".lines" },
12508 { TYPE_INTEGER, &tmi.align, ".align" },
12509 { TYPE_INTEGER, &tmi.valign, ".valign" },
12510 { TYPE_INTEGER, &tmi.font, ".font" },
12511 { TYPE_BOOLEAN, &tmi.autowrap, ".autowrap" },
12512 { TYPE_BOOLEAN, &tmi.centered, ".centered" },
12513 { TYPE_BOOLEAN, &tmi.parse_comments, ".parse_comments" },
12514 { TYPE_INTEGER, &tmi.sort_priority, ".sort_priority" },
12515 { TYPE_INTEGER, &tmi.fade_mode, ".fade_mode" },
12516 { TYPE_INTEGER, &tmi.fade_delay, ".fade_delay" },
12517 { TYPE_INTEGER, &tmi.post_delay, ".post_delay" },
12518 { TYPE_INTEGER, &tmi.auto_delay, ".auto_delay" },
12519 { TYPE_INTEGER, &tmi.auto_delay_unit, ".auto_delay_unit" },
12525 struct TitleFadingInfo *info;
12530 // initialize first titles from "enter screen" definitions, if defined
12531 { &title_initial_first_default, "menu.enter_screen.TITLE" },
12532 { &title_first_default, "menu.enter_screen.TITLE" },
12534 // initialize title screens from "next screen" definitions, if defined
12535 { &title_initial_default, "menu.next_screen.TITLE" },
12536 { &title_default, "menu.next_screen.TITLE" },
12542 struct TitleMessageInfo *array;
12545 titlemessage_arrays[] =
12547 // initialize first titles from "enter screen" definitions, if defined
12548 { titlescreen_initial_first, "menu.enter_screen.TITLE" },
12549 { titlescreen_first, "menu.enter_screen.TITLE" },
12550 { titlemessage_initial_first, "menu.enter_screen.TITLE" },
12551 { titlemessage_first, "menu.enter_screen.TITLE" },
12553 // initialize titles from "next screen" definitions, if defined
12554 { titlescreen_initial, "menu.next_screen.TITLE" },
12555 { titlescreen, "menu.next_screen.TITLE" },
12556 { titlemessage_initial, "menu.next_screen.TITLE" },
12557 { titlemessage, "menu.next_screen.TITLE" },
12559 // overwrite titles with title definitions, if defined
12560 { titlescreen_initial_first, "[title_initial]" },
12561 { titlescreen_first, "[title]" },
12562 { titlemessage_initial_first, "[title_initial]" },
12563 { titlemessage_first, "[title]" },
12565 { titlescreen_initial, "[title_initial]" },
12566 { titlescreen, "[title]" },
12567 { titlemessage_initial, "[title_initial]" },
12568 { titlemessage, "[title]" },
12570 // overwrite titles with title screen/message definitions, if defined
12571 { titlescreen_initial_first, "[titlescreen_initial]" },
12572 { titlescreen_first, "[titlescreen]" },
12573 { titlemessage_initial_first, "[titlemessage_initial]" },
12574 { titlemessage_first, "[titlemessage]" },
12576 { titlescreen_initial, "[titlescreen_initial]" },
12577 { titlescreen, "[titlescreen]" },
12578 { titlemessage_initial, "[titlemessage_initial]" },
12579 { titlemessage, "[titlemessage]" },
12583 SetupFileHash *setup_file_hash;
12586 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
12589 // the following initializes hierarchical values from dynamic configuration
12591 // special case: initialize with default values that may be overwritten
12592 // (e.g., init "menu.draw_xoffset.INFO" from "menu.draw_xoffset")
12593 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
12595 struct TokenIntPtrInfo menu_config[] =
12597 { "menu.draw_xoffset", &menu.draw_xoffset[i] },
12598 { "menu.draw_yoffset", &menu.draw_yoffset[i] },
12599 { "menu.list_size", &menu.list_size[i] }
12602 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
12604 char *token = menu_config[j].token;
12605 char *value = getHashEntry(setup_file_hash, token);
12608 *menu_config[j].value = get_integer_from_string(value);
12612 // special case: initialize with default values that may be overwritten
12613 // (eg, init "menu.draw_xoffset.INFO[XXX]" from "menu.draw_xoffset.INFO")
12614 for (i = 0; i < NUM_SPECIAL_GFX_INFO_ARGS; i++)
12616 struct TokenIntPtrInfo menu_config[] =
12618 { "menu.draw_xoffset.INFO", &menu.draw_xoffset_info[i] },
12619 { "menu.draw_yoffset.INFO", &menu.draw_yoffset_info[i] },
12620 { "menu.list_size.INFO", &menu.list_size_info[i] },
12621 { "menu.list_entry_size.INFO", &menu.list_entry_size_info[i] },
12622 { "menu.tile_size.INFO", &menu.tile_size_info[i] }
12625 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
12627 char *token = menu_config[j].token;
12628 char *value = getHashEntry(setup_file_hash, token);
12631 *menu_config[j].value = get_integer_from_string(value);
12635 // special case: initialize with default values that may be overwritten
12636 // (eg, init "menu.draw_xoffset.SETUP[XXX]" from "menu.draw_xoffset.SETUP")
12637 for (i = 0; i < NUM_SPECIAL_GFX_SETUP_ARGS; i++)
12639 struct TokenIntPtrInfo menu_config[] =
12641 { "menu.draw_xoffset.SETUP", &menu.draw_xoffset_setup[i] },
12642 { "menu.draw_yoffset.SETUP", &menu.draw_yoffset_setup[i] }
12645 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
12647 char *token = menu_config[j].token;
12648 char *value = getHashEntry(setup_file_hash, token);
12651 *menu_config[j].value = get_integer_from_string(value);
12655 // special case: initialize with default values that may be overwritten
12656 // (eg, init "menu.line_spacing.INFO[XXX]" from "menu.line_spacing.INFO")
12657 for (i = 0; i < NUM_SPECIAL_GFX_INFO_ARGS; i++)
12659 struct TokenIntPtrInfo menu_config[] =
12661 { "menu.left_spacing.INFO", &menu.left_spacing_info[i] },
12662 { "menu.middle_spacing.INFO", &menu.middle_spacing_info[i] },
12663 { "menu.right_spacing.INFO", &menu.right_spacing_info[i] },
12664 { "menu.top_spacing.INFO", &menu.top_spacing_info[i] },
12665 { "menu.bottom_spacing.INFO", &menu.bottom_spacing_info[i] },
12666 { "menu.paragraph_spacing.INFO", &menu.paragraph_spacing_info[i] },
12667 { "menu.headline1_spacing.INFO", &menu.headline1_spacing_info[i] },
12668 { "menu.headline2_spacing.INFO", &menu.headline2_spacing_info[i] },
12669 { "menu.line_spacing.INFO", &menu.line_spacing_info[i] },
12670 { "menu.extra_spacing.INFO", &menu.extra_spacing_info[i] },
12673 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
12675 char *token = menu_config[j].token;
12676 char *value = getHashEntry(setup_file_hash, token);
12679 *menu_config[j].value = get_integer_from_string(value);
12683 // special case: initialize with default values that may be overwritten
12684 // (eg, init "menu.enter_screen.SCORES.xyz" from "menu.enter_screen.xyz")
12685 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
12687 struct TokenIntPtrInfo menu_config[] =
12689 { "menu.enter_screen.fade_mode", &menu.enter_screen[i].fade_mode },
12690 { "menu.enter_screen.fade_delay", &menu.enter_screen[i].fade_delay },
12691 { "menu.enter_screen.post_delay", &menu.enter_screen[i].post_delay },
12692 { "menu.leave_screen.fade_mode", &menu.leave_screen[i].fade_mode },
12693 { "menu.leave_screen.fade_delay", &menu.leave_screen[i].fade_delay },
12694 { "menu.leave_screen.post_delay", &menu.leave_screen[i].post_delay },
12695 { "menu.next_screen.fade_mode", &menu.next_screen[i].fade_mode },
12696 { "menu.next_screen.fade_delay", &menu.next_screen[i].fade_delay },
12697 { "menu.next_screen.post_delay", &menu.next_screen[i].post_delay }
12700 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
12702 char *token = menu_config[j].token;
12703 char *value = getHashEntry(setup_file_hash, token);
12706 *menu_config[j].value = get_token_parameter_value(token, value);
12710 // special case: initialize with default values that may be overwritten
12711 // (eg, init "viewport.door_1.MAIN.xyz" from "viewport.door_1.xyz")
12712 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
12716 char *token_prefix;
12717 struct RectWithBorder *struct_ptr;
12721 { "viewport.window", &viewport.window[i] },
12722 { "viewport.playfield", &viewport.playfield[i] },
12723 { "viewport.door_1", &viewport.door_1[i] },
12724 { "viewport.door_2", &viewport.door_2[i] }
12727 for (j = 0; j < ARRAY_SIZE(vp_struct); j++)
12729 struct TokenIntPtrInfo vp_config[] =
12731 { ".x", &vp_struct[j].struct_ptr->x },
12732 { ".y", &vp_struct[j].struct_ptr->y },
12733 { ".width", &vp_struct[j].struct_ptr->width },
12734 { ".height", &vp_struct[j].struct_ptr->height },
12735 { ".min_width", &vp_struct[j].struct_ptr->min_width },
12736 { ".min_height", &vp_struct[j].struct_ptr->min_height },
12737 { ".max_width", &vp_struct[j].struct_ptr->max_width },
12738 { ".max_height", &vp_struct[j].struct_ptr->max_height },
12739 { ".margin_left", &vp_struct[j].struct_ptr->margin_left },
12740 { ".margin_right", &vp_struct[j].struct_ptr->margin_right },
12741 { ".margin_top", &vp_struct[j].struct_ptr->margin_top },
12742 { ".margin_bottom", &vp_struct[j].struct_ptr->margin_bottom },
12743 { ".border_left", &vp_struct[j].struct_ptr->border_left },
12744 { ".border_right", &vp_struct[j].struct_ptr->border_right },
12745 { ".border_top", &vp_struct[j].struct_ptr->border_top },
12746 { ".border_bottom", &vp_struct[j].struct_ptr->border_bottom },
12747 { ".border_size", &vp_struct[j].struct_ptr->border_size },
12748 { ".align_size", &vp_struct[j].struct_ptr->align_size },
12749 { ".align", &vp_struct[j].struct_ptr->align },
12750 { ".valign", &vp_struct[j].struct_ptr->valign }
12753 for (k = 0; k < ARRAY_SIZE(vp_config); k++)
12755 char *token = getStringCat2(vp_struct[j].token_prefix,
12756 vp_config[k].token);
12757 char *value = getHashEntry(setup_file_hash, token);
12760 *vp_config[k].value = get_token_parameter_value(token, value);
12767 // special case: initialize with default values that may be overwritten
12768 // (e.g., init "[title].fade_mode" from "menu.next_screen.TITLE.fade_mode")
12769 for (i = 0; title_info[i].info != NULL; i++)
12771 struct TitleFadingInfo *info = title_info[i].info;
12772 char *base_token = title_info[i].text;
12774 for (j = 0; title_tokens[j].type != -1; j++)
12776 char *token = getStringCat2(base_token, title_tokens[j].text);
12777 char *value = getHashEntry(setup_file_hash, token);
12781 int parameter_value = get_token_parameter_value(token, value);
12785 *(int *)title_tokens[j].value = (int)parameter_value;
12794 // special case: initialize with default values that may be overwritten
12795 // (e.g., init "titlemessage_1.fade_mode" from "[titlemessage].fade_mode")
12796 for (i = 0; titlemessage_arrays[i].array != NULL; i++)
12798 struct TitleMessageInfo *array = titlemessage_arrays[i].array;
12799 char *base_token = titlemessage_arrays[i].text;
12801 for (j = 0; titlemessage_tokens[j].type != -1; j++)
12803 char *token = getStringCat2(base_token, titlemessage_tokens[j].text);
12804 char *value = getHashEntry(setup_file_hash, token);
12808 int parameter_value = get_token_parameter_value(token, value);
12810 for (k = 0; k < MAX_NUM_TITLE_MESSAGES; k++)
12814 if (titlemessage_tokens[j].type == TYPE_INTEGER)
12815 *(int *)titlemessage_tokens[j].value = (int)parameter_value;
12817 *(boolean *)titlemessage_tokens[j].value = (boolean)parameter_value;
12827 // read (and overwrite with) values that may be specified in config file
12828 InitMenuDesignSettings_FromHash(setup_file_hash, TRUE);
12830 // special case: check if network and preview player positions are redefined
12831 InitMenuDesignSettings_PreviewPlayers_FromHash(setup_file_hash);
12833 freeSetupFileHash(setup_file_hash);
12836 void LoadMenuDesignSettings(void)
12838 char *filename_base = UNDEFINED_FILENAME, *filename_local;
12840 InitMenuDesignSettings_Static();
12841 InitMenuDesignSettings_SpecialPreProcessing();
12842 InitMenuDesignSettings_PreviewPlayers();
12844 if (!GFX_OVERRIDE_ARTWORK(ARTWORK_TYPE_GRAPHICS))
12846 // first look for special settings configured in level series config
12847 filename_base = getCustomArtworkLevelConfigFilename(ARTWORK_TYPE_GRAPHICS);
12849 if (fileExists(filename_base))
12850 LoadMenuDesignSettingsFromFilename(filename_base);
12853 filename_local = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
12855 if (filename_local != NULL && !strEqual(filename_base, filename_local))
12856 LoadMenuDesignSettingsFromFilename(filename_local);
12858 InitMenuDesignSettings_SpecialPostProcessing();
12861 void LoadMenuDesignSettings_AfterGraphics(void)
12863 InitMenuDesignSettings_SpecialPostProcessing_AfterGraphics();
12866 void LoadUserDefinedEditorElementList(int **elements, int *num_elements)
12868 char *filename = getEditorSetupFilename();
12869 SetupFileList *setup_file_list, *list;
12870 SetupFileHash *element_hash;
12871 int num_unknown_tokens = 0;
12874 if ((setup_file_list = loadSetupFileList(filename)) == NULL)
12877 element_hash = newSetupFileHash();
12879 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
12880 setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
12882 // determined size may be larger than needed (due to unknown elements)
12884 for (list = setup_file_list; list != NULL; list = list->next)
12887 // add space for up to 3 more elements for padding that may be needed
12888 *num_elements += 3;
12890 // free memory for old list of elements, if needed
12891 checked_free(*elements);
12893 // allocate memory for new list of elements
12894 *elements = checked_malloc(*num_elements * sizeof(int));
12897 for (list = setup_file_list; list != NULL; list = list->next)
12899 char *value = getHashEntry(element_hash, list->token);
12901 if (value == NULL) // try to find obsolete token mapping
12903 char *mapped_token = get_mapped_token(list->token);
12905 if (mapped_token != NULL)
12907 value = getHashEntry(element_hash, mapped_token);
12909 free(mapped_token);
12915 (*elements)[(*num_elements)++] = atoi(value);
12919 if (num_unknown_tokens == 0)
12922 Warn("unknown token(s) found in config file:");
12923 Warn("- config file: '%s'", filename);
12925 num_unknown_tokens++;
12928 Warn("- token: '%s'", list->token);
12932 if (num_unknown_tokens > 0)
12935 while (*num_elements % 4) // pad with empty elements, if needed
12936 (*elements)[(*num_elements)++] = EL_EMPTY;
12938 freeSetupFileList(setup_file_list);
12939 freeSetupFileHash(element_hash);
12942 for (i = 0; i < *num_elements; i++)
12943 Debug("editor", "element '%s' [%d]\n",
12944 element_info[(*elements)[i]].token_name, (*elements)[i]);
12948 static struct MusicFileInfo *get_music_file_info_ext(char *basename, int music,
12951 SetupFileHash *setup_file_hash = NULL;
12952 struct MusicFileInfo tmp_music_file_info, *new_music_file_info;
12953 char *filename_music, *filename_prefix, *filename_info;
12959 token_to_value_ptr[] =
12961 { "title_header", &tmp_music_file_info.title_header },
12962 { "artist_header", &tmp_music_file_info.artist_header },
12963 { "album_header", &tmp_music_file_info.album_header },
12964 { "year_header", &tmp_music_file_info.year_header },
12965 { "played_header", &tmp_music_file_info.played_header },
12967 { "title", &tmp_music_file_info.title },
12968 { "artist", &tmp_music_file_info.artist },
12969 { "album", &tmp_music_file_info.album },
12970 { "year", &tmp_music_file_info.year },
12971 { "played", &tmp_music_file_info.played },
12977 filename_music = (is_sound ? getCustomSoundFilename(basename) :
12978 getCustomMusicFilename(basename));
12980 if (filename_music == NULL)
12983 // ---------- try to replace file extension ----------
12985 filename_prefix = getStringCopy(filename_music);
12986 if (strrchr(filename_prefix, '.') != NULL)
12987 *strrchr(filename_prefix, '.') = '\0';
12988 filename_info = getStringCat2(filename_prefix, ".txt");
12990 if (fileExists(filename_info))
12991 setup_file_hash = loadSetupFileHash(filename_info);
12993 free(filename_prefix);
12994 free(filename_info);
12996 if (setup_file_hash == NULL)
12998 // ---------- try to add file extension ----------
13000 filename_prefix = getStringCopy(filename_music);
13001 filename_info = getStringCat2(filename_prefix, ".txt");
13003 if (fileExists(filename_info))
13004 setup_file_hash = loadSetupFileHash(filename_info);
13006 free(filename_prefix);
13007 free(filename_info);
13010 if (setup_file_hash == NULL)
13013 // ---------- music file info found ----------
13015 clear_mem(&tmp_music_file_info, sizeof(struct MusicFileInfo));
13017 for (i = 0; token_to_value_ptr[i].token != NULL; i++)
13019 char *value = getHashEntry(setup_file_hash, token_to_value_ptr[i].token);
13021 *token_to_value_ptr[i].value_ptr =
13022 getStringCopy(value != NULL && *value != '\0' ? value : UNKNOWN_NAME);
13025 tmp_music_file_info.basename = getStringCopy(basename);
13026 tmp_music_file_info.music = music;
13027 tmp_music_file_info.is_sound = is_sound;
13029 new_music_file_info = checked_malloc(sizeof(struct MusicFileInfo));
13030 *new_music_file_info = tmp_music_file_info;
13032 return new_music_file_info;
13035 static struct MusicFileInfo *get_music_file_info(char *basename, int music)
13037 return get_music_file_info_ext(basename, music, FALSE);
13040 static struct MusicFileInfo *get_sound_file_info(char *basename, int sound)
13042 return get_music_file_info_ext(basename, sound, TRUE);
13045 static boolean music_info_listed_ext(struct MusicFileInfo *list,
13046 char *basename, boolean is_sound)
13048 for (; list != NULL; list = list->next)
13049 if (list->is_sound == is_sound && strEqual(list->basename, basename))
13055 static boolean music_info_listed(struct MusicFileInfo *list, char *basename)
13057 return music_info_listed_ext(list, basename, FALSE);
13060 static boolean sound_info_listed(struct MusicFileInfo *list, char *basename)
13062 return music_info_listed_ext(list, basename, TRUE);
13065 void LoadMusicInfo(void)
13067 int num_music_noconf = getMusicListSize_NoConf();
13068 int num_music = getMusicListSize();
13069 int num_sounds = getSoundListSize();
13070 struct FileInfo *music, *sound;
13071 struct MusicFileInfo *next, **new;
13075 while (music_file_info != NULL)
13077 next = music_file_info->next;
13079 checked_free(music_file_info->basename);
13081 checked_free(music_file_info->title_header);
13082 checked_free(music_file_info->artist_header);
13083 checked_free(music_file_info->album_header);
13084 checked_free(music_file_info->year_header);
13085 checked_free(music_file_info->played_header);
13087 checked_free(music_file_info->title);
13088 checked_free(music_file_info->artist);
13089 checked_free(music_file_info->album);
13090 checked_free(music_file_info->year);
13091 checked_free(music_file_info->played);
13093 free(music_file_info);
13095 music_file_info = next;
13098 new = &music_file_info;
13100 // get (configured or unconfigured) music file info for all levels
13101 for (i = leveldir_current->first_level;
13102 i <= leveldir_current->last_level; i++)
13106 if (levelset.music[i] != MUS_UNDEFINED)
13108 // get music file info for configured level music
13109 music_nr = levelset.music[i];
13111 else if (num_music_noconf > 0)
13113 // get music file info for unconfigured level music
13114 int level_pos = i - leveldir_current->first_level;
13116 music_nr = MAP_NOCONF_MUSIC(level_pos % num_music_noconf);
13123 char *basename = getMusicInfoEntryFilename(music_nr);
13125 if (basename == NULL)
13128 if (!music_info_listed(music_file_info, basename))
13130 *new = get_music_file_info(basename, music_nr);
13133 new = &(*new)->next;
13137 // get music file info for all remaining configured music files
13138 for (i = 0; i < num_music; i++)
13140 music = getMusicListEntry(i);
13142 if (music->filename == NULL)
13145 if (strEqual(music->filename, UNDEFINED_FILENAME))
13148 // a configured file may be not recognized as music
13149 if (!FileIsMusic(music->filename))
13152 if (!music_info_listed(music_file_info, music->filename))
13154 *new = get_music_file_info(music->filename, i);
13157 new = &(*new)->next;
13161 // get sound file info for all configured sound files
13162 for (i = 0; i < num_sounds; i++)
13164 sound = getSoundListEntry(i);
13166 if (sound->filename == NULL)
13169 if (strEqual(sound->filename, UNDEFINED_FILENAME))
13172 // a configured file may be not recognized as sound
13173 if (!FileIsSound(sound->filename))
13176 if (!sound_info_listed(music_file_info, sound->filename))
13178 *new = get_sound_file_info(sound->filename, i);
13180 new = &(*new)->next;
13184 // add pointers to previous list nodes
13186 struct MusicFileInfo *node = music_file_info;
13188 while (node != NULL)
13191 node->next->prev = node;
13197 static void add_helpanim_entry(int element, int action, int direction,
13198 int delay, int *num_list_entries)
13200 struct HelpAnimInfo *new_list_entry;
13201 (*num_list_entries)++;
13204 checked_realloc(helpanim_info,
13205 *num_list_entries * sizeof(struct HelpAnimInfo));
13206 new_list_entry = &helpanim_info[*num_list_entries - 1];
13208 new_list_entry->element = element;
13209 new_list_entry->action = action;
13210 new_list_entry->direction = direction;
13211 new_list_entry->delay = delay;
13214 static void print_unknown_token(char *filename, char *token, int token_nr)
13219 Warn("unknown token(s) found in config file:");
13220 Warn("- config file: '%s'", filename);
13223 Warn("- token: '%s'", token);
13226 static void print_unknown_token_end(int token_nr)
13232 void LoadHelpAnimInfo(void)
13234 char *filename = getHelpAnimFilename();
13235 SetupFileList *setup_file_list = NULL, *list;
13236 SetupFileHash *element_hash, *action_hash, *direction_hash;
13237 int num_list_entries = 0;
13238 int num_unknown_tokens = 0;
13241 if (fileExists(filename))
13242 setup_file_list = loadSetupFileList(filename);
13244 if (setup_file_list == NULL)
13246 // use reliable default values from static configuration
13247 SetupFileList *insert_ptr;
13249 insert_ptr = setup_file_list =
13250 newSetupFileList(helpanim_config[0].token,
13251 helpanim_config[0].value);
13253 for (i = 1; helpanim_config[i].token; i++)
13254 insert_ptr = addListEntry(insert_ptr,
13255 helpanim_config[i].token,
13256 helpanim_config[i].value);
13259 element_hash = newSetupFileHash();
13260 action_hash = newSetupFileHash();
13261 direction_hash = newSetupFileHash();
13263 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
13264 setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
13266 for (i = 0; i < NUM_ACTIONS; i++)
13267 setHashEntry(action_hash, element_action_info[i].suffix,
13268 i_to_a(element_action_info[i].value));
13270 // do not store direction index (bit) here, but direction value!
13271 for (i = 0; i < NUM_DIRECTIONS_FULL; i++)
13272 setHashEntry(direction_hash, element_direction_info[i].suffix,
13273 i_to_a(1 << element_direction_info[i].value));
13275 for (list = setup_file_list; list != NULL; list = list->next)
13277 char *element_token, *action_token, *direction_token;
13278 char *element_value, *action_value, *direction_value;
13279 int delay = atoi(list->value);
13281 if (strEqual(list->token, "end"))
13283 add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
13288 /* first try to break element into element/action/direction parts;
13289 if this does not work, also accept combined "element[.act][.dir]"
13290 elements (like "dynamite.active"), which are unique elements */
13292 if (strchr(list->token, '.') == NULL) // token contains no '.'
13294 element_value = getHashEntry(element_hash, list->token);
13295 if (element_value != NULL) // element found
13296 add_helpanim_entry(atoi(element_value), -1, -1, delay,
13297 &num_list_entries);
13300 // no further suffixes found -- this is not an element
13301 print_unknown_token(filename, list->token, num_unknown_tokens++);
13307 // token has format "<prefix>.<something>"
13309 action_token = strchr(list->token, '.'); // suffix may be action ...
13310 direction_token = action_token; // ... or direction
13312 element_token = getStringCopy(list->token);
13313 *strchr(element_token, '.') = '\0';
13315 element_value = getHashEntry(element_hash, element_token);
13317 if (element_value == NULL) // this is no element
13319 element_value = getHashEntry(element_hash, list->token);
13320 if (element_value != NULL) // combined element found
13321 add_helpanim_entry(atoi(element_value), -1, -1, delay,
13322 &num_list_entries);
13324 print_unknown_token(filename, list->token, num_unknown_tokens++);
13326 free(element_token);
13331 action_value = getHashEntry(action_hash, action_token);
13333 if (action_value != NULL) // action found
13335 add_helpanim_entry(atoi(element_value), atoi(action_value), -1, delay,
13336 &num_list_entries);
13338 free(element_token);
13343 direction_value = getHashEntry(direction_hash, direction_token);
13345 if (direction_value != NULL) // direction found
13347 add_helpanim_entry(atoi(element_value), -1, atoi(direction_value), delay,
13348 &num_list_entries);
13350 free(element_token);
13355 if (strchr(action_token + 1, '.') == NULL)
13357 // no further suffixes found -- this is not an action nor direction
13359 element_value = getHashEntry(element_hash, list->token);
13360 if (element_value != NULL) // combined element found
13361 add_helpanim_entry(atoi(element_value), -1, -1, delay,
13362 &num_list_entries);
13364 print_unknown_token(filename, list->token, num_unknown_tokens++);
13366 free(element_token);
13371 // token has format "<prefix>.<suffix>.<something>"
13373 direction_token = strchr(action_token + 1, '.');
13375 action_token = getStringCopy(action_token);
13376 *strchr(action_token + 1, '.') = '\0';
13378 action_value = getHashEntry(action_hash, action_token);
13380 if (action_value == NULL) // this is no action
13382 element_value = getHashEntry(element_hash, list->token);
13383 if (element_value != NULL) // combined element found
13384 add_helpanim_entry(atoi(element_value), -1, -1, delay,
13385 &num_list_entries);
13387 print_unknown_token(filename, list->token, num_unknown_tokens++);
13389 free(element_token);
13390 free(action_token);
13395 direction_value = getHashEntry(direction_hash, direction_token);
13397 if (direction_value != NULL) // direction found
13399 add_helpanim_entry(atoi(element_value), atoi(action_value),
13400 atoi(direction_value), delay, &num_list_entries);
13402 free(element_token);
13403 free(action_token);
13408 // this is no direction
13410 element_value = getHashEntry(element_hash, list->token);
13411 if (element_value != NULL) // combined element found
13412 add_helpanim_entry(atoi(element_value), -1, -1, delay,
13413 &num_list_entries);
13415 print_unknown_token(filename, list->token, num_unknown_tokens++);
13417 free(element_token);
13418 free(action_token);
13421 print_unknown_token_end(num_unknown_tokens);
13423 add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
13424 add_helpanim_entry(HELPANIM_LIST_END, -1, -1, -1, &num_list_entries);
13426 freeSetupFileList(setup_file_list);
13427 freeSetupFileHash(element_hash);
13428 freeSetupFileHash(action_hash);
13429 freeSetupFileHash(direction_hash);
13432 for (i = 0; i < num_list_entries; i++)
13433 Debug("files:LoadHelpAnimInfo", "'%s': %d, %d, %d => %d",
13434 EL_NAME(helpanim_info[i].element),
13435 helpanim_info[i].element,
13436 helpanim_info[i].action,
13437 helpanim_info[i].direction,
13438 helpanim_info[i].delay);
13442 void LoadHelpTextInfo(void)
13444 char *filename = getHelpTextFilename();
13447 if (helptext_info != NULL)
13449 freeSetupFileHash(helptext_info);
13450 helptext_info = NULL;
13453 if (fileExists(filename))
13454 helptext_info = loadSetupFileHash(filename);
13456 if (helptext_info == NULL)
13458 // use reliable default values from static configuration
13459 helptext_info = newSetupFileHash();
13461 for (i = 0; helptext_config[i].token; i++)
13462 setHashEntry(helptext_info,
13463 helptext_config[i].token,
13464 helptext_config[i].value);
13468 BEGIN_HASH_ITERATION(helptext_info, itr)
13470 Debug("files:LoadHelpTextInfo", "'%s' => '%s'",
13471 HASH_ITERATION_TOKEN(itr), HASH_ITERATION_VALUE(itr));
13473 END_HASH_ITERATION(hash, itr)
13478 // ----------------------------------------------------------------------------
13480 // ----------------------------------------------------------------------------
13482 #define MAX_NUM_CONVERT_LEVELS 1000
13484 void ConvertLevels(void)
13486 static LevelDirTree *convert_leveldir = NULL;
13487 static int convert_level_nr = -1;
13488 static int num_levels_handled = 0;
13489 static int num_levels_converted = 0;
13490 static boolean levels_failed[MAX_NUM_CONVERT_LEVELS];
13493 convert_leveldir = getTreeInfoFromIdentifier(leveldir_first,
13494 global.convert_leveldir);
13496 if (convert_leveldir == NULL)
13497 Fail("no such level identifier: '%s'", global.convert_leveldir);
13499 leveldir_current = convert_leveldir;
13501 if (global.convert_level_nr != -1)
13503 convert_leveldir->first_level = global.convert_level_nr;
13504 convert_leveldir->last_level = global.convert_level_nr;
13507 convert_level_nr = convert_leveldir->first_level;
13509 PrintLine("=", 79);
13510 Print("Converting levels\n");
13511 PrintLine("-", 79);
13512 Print("Level series identifier: '%s'\n", convert_leveldir->identifier);
13513 Print("Level series name: '%s'\n", convert_leveldir->name);
13514 Print("Level series author: '%s'\n", convert_leveldir->author);
13515 Print("Number of levels: %d\n", convert_leveldir->levels);
13516 PrintLine("=", 79);
13519 for (i = 0; i < MAX_NUM_CONVERT_LEVELS; i++)
13520 levels_failed[i] = FALSE;
13522 while (convert_level_nr <= convert_leveldir->last_level)
13524 char *level_filename;
13527 level_nr = convert_level_nr++;
13529 Print("Level %03d: ", level_nr);
13531 LoadLevel(level_nr);
13532 if (level.no_level_file || level.no_valid_file)
13534 Print("(no level)\n");
13538 Print("converting level ... ");
13541 // special case: conversion of some EMC levels as requested by ACME
13542 level.game_engine_type = GAME_ENGINE_TYPE_RND;
13545 level_filename = getDefaultLevelFilename(level_nr);
13546 new_level = !fileExists(level_filename);
13550 SaveLevel(level_nr);
13552 num_levels_converted++;
13554 Print("converted.\n");
13558 if (level_nr >= 0 && level_nr < MAX_NUM_CONVERT_LEVELS)
13559 levels_failed[level_nr] = TRUE;
13561 Print("NOT CONVERTED -- LEVEL ALREADY EXISTS.\n");
13564 num_levels_handled++;
13568 PrintLine("=", 79);
13569 Print("Number of levels handled: %d\n", num_levels_handled);
13570 Print("Number of levels converted: %d (%d%%)\n", num_levels_converted,
13571 (num_levels_handled ?
13572 num_levels_converted * 100 / num_levels_handled : 0));
13573 PrintLine("-", 79);
13574 Print("Summary (for automatic parsing by scripts):\n");
13575 Print("LEVELDIR '%s', CONVERTED %d/%d (%d%%)",
13576 convert_leveldir->identifier, num_levels_converted,
13577 num_levels_handled,
13578 (num_levels_handled ?
13579 num_levels_converted * 100 / num_levels_handled : 0));
13581 if (num_levels_handled != num_levels_converted)
13583 Print(", FAILED:");
13584 for (i = 0; i < MAX_NUM_CONVERT_LEVELS; i++)
13585 if (levels_failed[i])
13590 PrintLine("=", 79);
13592 CloseAllAndExit(0);
13596 // ----------------------------------------------------------------------------
13597 // create and save images for use in level sketches (raw BMP format)
13598 // ----------------------------------------------------------------------------
13600 void CreateLevelSketchImages(void)
13606 InitElementPropertiesGfxElement();
13608 bitmap1 = CreateBitmap(TILEX, TILEY, DEFAULT_DEPTH);
13609 bitmap2 = CreateBitmap(MINI_TILEX, MINI_TILEY, DEFAULT_DEPTH);
13611 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
13613 int element = getMappedElement(i);
13614 char basename1[16];
13615 char basename2[16];
13619 sprintf(basename1, "%04d.bmp", i);
13620 sprintf(basename2, "%04ds.bmp", i);
13622 filename1 = getPath2(global.create_sketch_images_dir, basename1);
13623 filename2 = getPath2(global.create_sketch_images_dir, basename2);
13625 DrawSizedElement(0, 0, element, TILESIZE);
13626 BlitBitmap(drawto, bitmap1, SX, SY, TILEX, TILEY, 0, 0);
13628 if (SDL_SaveBMP(bitmap1->surface, filename1) != 0)
13629 Fail("cannot save level sketch image file '%s'", filename1);
13631 DrawSizedElement(0, 0, element, MINI_TILESIZE);
13632 BlitBitmap(drawto, bitmap2, SX, SY, MINI_TILEX, MINI_TILEY, 0, 0);
13634 if (SDL_SaveBMP(bitmap2->surface, filename2) != 0)
13635 Fail("cannot save level sketch image file '%s'", filename2);
13640 // create corresponding SQL statements (for normal and small images)
13643 printf("insert into phpbb_words values (NULL, '`%03d', '<IMG class=\"levelsketch\" src=\"/I/%04d.png\"/>');\n", i, i);
13644 printf("insert into phpbb_words values (NULL, '¸%03d', '<IMG class=\"levelsketch\" src=\"/I/%04ds.png\"/>');\n", i, i);
13647 printf("insert into phpbb_words values (NULL, '`%04d', '<IMG class=\"levelsketch\" src=\"/I/%04d.png\"/>');\n", i, i);
13648 printf("insert into phpbb_words values (NULL, '¸%04d', '<IMG class=\"levelsketch\" src=\"/I/%04ds.png\"/>');\n", i, i);
13650 // optional: create content for forum level sketch demonstration post
13652 fprintf(stderr, "%03d `%03d%c", i, i, (i % 10 < 9 ? ' ' : '\n'));
13655 FreeBitmap(bitmap1);
13656 FreeBitmap(bitmap2);
13659 fprintf(stderr, "\n");
13661 Info("%d normal and small images created", NUM_FILE_ELEMENTS);
13663 CloseAllAndExit(0);
13667 // ----------------------------------------------------------------------------
13668 // create and save images for element collecting animations (raw BMP format)
13669 // ----------------------------------------------------------------------------
13671 static boolean createCollectImage(int element)
13673 return (IS_COLLECTIBLE(element) && !IS_SP_ELEMENT(element));
13676 void CreateCollectElementImages(void)
13680 int anim_frames = num_steps - 1;
13681 int tile_size = TILESIZE;
13682 int anim_width = tile_size * anim_frames;
13683 int anim_height = tile_size;
13684 int num_collect_images = 0;
13685 int pos_collect_images = 0;
13687 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
13688 if (createCollectImage(i))
13689 num_collect_images++;
13691 Info("Creating %d element collecting animation images ...",
13692 num_collect_images);
13694 int dst_width = anim_width * 2;
13695 int dst_height = anim_height * num_collect_images / 2;
13696 Bitmap *dst_bitmap = CreateBitmap(dst_width, dst_height, DEFAULT_DEPTH);
13697 char *basename_bmp = "RocksCollect.bmp";
13698 char *basename_png = "RocksCollect.png";
13699 char *filename_bmp = getPath2(global.create_collect_images_dir, basename_bmp);
13700 char *filename_png = getPath2(global.create_collect_images_dir, basename_png);
13701 int len_filename_bmp = strlen(filename_bmp);
13702 int len_filename_png = strlen(filename_png);
13703 int max_command_len = MAX_FILENAME_LEN + len_filename_bmp + len_filename_png;
13704 char cmd_convert[max_command_len];
13706 snprintf(cmd_convert, max_command_len, "convert \"%s\" \"%s\"",
13710 // force using RGBA surface for destination bitmap
13711 SDL_SetColorKey(dst_bitmap->surface, SET_TRANSPARENT_PIXEL,
13712 SDL_MapRGB(dst_bitmap->surface->format, 0x00, 0x00, 0x00));
13714 dst_bitmap->surface =
13715 SDL_ConvertSurfaceFormat(dst_bitmap->surface, SDL_PIXELFORMAT_ARGB8888, 0);
13717 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
13719 if (!createCollectImage(i))
13722 int dst_x = (pos_collect_images / (num_collect_images / 2)) * anim_width;
13723 int dst_y = (pos_collect_images % (num_collect_images / 2)) * anim_height;
13724 int graphic = el2img(i);
13725 char *token_name = element_info[i].token_name;
13726 Bitmap *tmp_bitmap = CreateBitmap(tile_size, tile_size, DEFAULT_DEPTH);
13727 Bitmap *src_bitmap;
13730 Info("- creating collecting image for '%s' ...", token_name);
13732 getGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
13734 BlitBitmap(src_bitmap, tmp_bitmap, src_x, src_y,
13735 tile_size, tile_size, 0, 0);
13737 // force using RGBA surface for temporary bitmap (using transparent black)
13738 SDL_SetColorKey(tmp_bitmap->surface, SET_TRANSPARENT_PIXEL,
13739 SDL_MapRGB(tmp_bitmap->surface->format, 0x00, 0x00, 0x00));
13741 tmp_bitmap->surface =
13742 SDL_ConvertSurfaceFormat(tmp_bitmap->surface, SDL_PIXELFORMAT_ARGB8888, 0);
13744 tmp_bitmap->surface_masked = tmp_bitmap->surface;
13746 for (j = 0; j < anim_frames; j++)
13748 int frame_size_final = tile_size * (anim_frames - j) / num_steps;
13749 int frame_size = frame_size_final * num_steps;
13750 int offset = (tile_size - frame_size_final) / 2;
13751 Bitmap *frame_bitmap = ZoomBitmap(tmp_bitmap, frame_size, frame_size);
13753 while (frame_size > frame_size_final)
13757 Bitmap *half_bitmap = ZoomBitmap(frame_bitmap, frame_size, frame_size);
13759 FreeBitmap(frame_bitmap);
13761 frame_bitmap = half_bitmap;
13764 BlitBitmapMasked(frame_bitmap, dst_bitmap, 0, 0,
13765 frame_size_final, frame_size_final,
13766 dst_x + j * tile_size + offset, dst_y + offset);
13768 FreeBitmap(frame_bitmap);
13771 tmp_bitmap->surface_masked = NULL;
13773 FreeBitmap(tmp_bitmap);
13775 pos_collect_images++;
13778 if (SDL_SaveBMP(dst_bitmap->surface, filename_bmp) != 0)
13779 Fail("cannot save element collecting image file '%s'", filename_bmp);
13781 FreeBitmap(dst_bitmap);
13783 Info("Converting image file from BMP to PNG ...");
13785 if (system(cmd_convert) != 0)
13786 Fail("converting image file failed");
13788 unlink(filename_bmp);
13792 CloseAllAndExit(0);
13796 // ----------------------------------------------------------------------------
13797 // create and save images for custom and group elements (raw BMP format)
13798 // ----------------------------------------------------------------------------
13800 void CreateCustomElementImages(char *directory)
13802 char *src_basename = "RocksCE-template.ilbm";
13803 char *dst_basename = "RocksCE.bmp";
13804 char *src_filename = getPath2(directory, src_basename);
13805 char *dst_filename = getPath2(directory, dst_basename);
13806 Bitmap *src_bitmap;
13808 int yoffset_ce = 0;
13809 int yoffset_ge = (TILEY * NUM_CUSTOM_ELEMENTS / 16);
13812 InitVideoDefaults();
13814 ReCreateBitmap(&backbuffer, video.width, video.height);
13816 src_bitmap = LoadImage(src_filename);
13818 bitmap = CreateBitmap(TILEX * 16 * 2,
13819 TILEY * (NUM_CUSTOM_ELEMENTS + NUM_GROUP_ELEMENTS) / 16,
13822 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
13829 BlitBitmap(src_bitmap, bitmap, 0, 0, TILEX, TILEY,
13830 TILEX * x, TILEY * y + yoffset_ce);
13832 BlitBitmap(src_bitmap, bitmap, 0, TILEY,
13834 TILEX * x + TILEX * 16,
13835 TILEY * y + yoffset_ce);
13837 for (j = 2; j >= 0; j--)
13841 BlitBitmap(src_bitmap, bitmap,
13842 TILEX + c * 7, 0, 6, 10,
13843 TILEX * x + 6 + j * 7,
13844 TILEY * y + 11 + yoffset_ce);
13846 BlitBitmap(src_bitmap, bitmap,
13847 TILEX + c * 8, TILEY, 6, 10,
13848 TILEX * 16 + TILEX * x + 6 + j * 8,
13849 TILEY * y + 10 + yoffset_ce);
13855 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
13862 BlitBitmap(src_bitmap, bitmap, 0, 0, TILEX, TILEY,
13863 TILEX * x, TILEY * y + yoffset_ge);
13865 BlitBitmap(src_bitmap, bitmap, 0, TILEY,
13867 TILEX * x + TILEX * 16,
13868 TILEY * y + yoffset_ge);
13870 for (j = 1; j >= 0; j--)
13874 BlitBitmap(src_bitmap, bitmap, TILEX + c * 10, 11, 10, 10,
13875 TILEX * x + 6 + j * 10,
13876 TILEY * y + 11 + yoffset_ge);
13878 BlitBitmap(src_bitmap, bitmap,
13879 TILEX + c * 8, TILEY + 12, 6, 10,
13880 TILEX * 16 + TILEX * x + 10 + j * 8,
13881 TILEY * y + 10 + yoffset_ge);
13887 if (SDL_SaveBMP(bitmap->surface, dst_filename) != 0)
13888 Fail("cannot save CE graphics file '%s'", dst_filename);
13890 FreeBitmap(bitmap);
13892 CloseAllAndExit(0);