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 // check for typical filename of a Boulder Dash (GDash) level package file
2105 if (strSuffixLower(basename, ".bd") ||
2106 strSuffixLower(basename, ".bdr") ||
2107 strSuffixLower(basename, ".brc") ||
2108 strSuffixLower(basename, ".gds"))
2109 return LEVEL_FILE_TYPE_BD;
2111 // ---------- try to determine file type from filesize ----------
2113 checked_free(filename);
2114 filename = getPath2(getCurrentLevelDir(), basename);
2116 if (stat(filename, &file_status) == 0)
2118 // check for typical filesize of a Supaplex level package file
2119 if (file_status.st_size == 170496)
2120 return LEVEL_FILE_TYPE_SP;
2123 return LEVEL_FILE_TYPE_UNKNOWN;
2126 static int getFileTypeFromMagicBytes(char *filename, int type)
2130 if ((file = openFile(filename, MODE_READ)))
2132 char chunk_name[CHUNK_ID_LEN + 1];
2134 getFileChunkBE(file, chunk_name, NULL);
2136 if (strEqual(chunk_name, "MMII") ||
2137 strEqual(chunk_name, "MIRR"))
2138 type = LEVEL_FILE_TYPE_MM;
2146 static boolean checkForPackageFromBasename(char *basename)
2148 // !!! WON'T WORK ANYMORE IF getFileTypeFromBasename() ALSO DETECTS !!!
2149 // !!! SINGLE LEVELS (CURRENTLY ONLY DETECTS LEVEL PACKAGES !!!
2151 return (getFileTypeFromBasename(basename) != LEVEL_FILE_TYPE_UNKNOWN);
2154 static char *getSingleLevelBasenameExt(int nr, char *extension)
2156 static char basename[MAX_FILENAME_LEN];
2159 sprintf(basename, "%s", LEVELTEMPLATE_FILENAME);
2161 sprintf(basename, "%03d.%s", nr, extension);
2166 static char *getSingleLevelBasename(int nr)
2168 return getSingleLevelBasenameExt(nr, LEVELFILE_EXTENSION);
2171 static char *getPackedLevelBasename(int type)
2173 static char basename[MAX_FILENAME_LEN];
2174 char *directory = getCurrentLevelDir();
2176 DirectoryEntry *dir_entry;
2178 strcpy(basename, UNDEFINED_FILENAME); // default: undefined file
2180 if ((dir = openDirectory(directory)) == NULL)
2182 Warn("cannot read current level directory '%s'", directory);
2187 while ((dir_entry = readDirectory(dir)) != NULL) // loop all entries
2189 char *entry_basename = dir_entry->basename;
2190 int entry_type = getFileTypeFromBasename(entry_basename);
2192 if (entry_type != LEVEL_FILE_TYPE_UNKNOWN) // found valid level package
2194 if (type == LEVEL_FILE_TYPE_UNKNOWN ||
2197 strcpy(basename, entry_basename);
2204 closeDirectory(dir);
2209 static char *getSingleLevelFilename(int nr)
2211 return getLevelFilenameFromBasename(getSingleLevelBasename(nr));
2214 #if ENABLE_UNUSED_CODE
2215 static char *getPackedLevelFilename(int type)
2217 return getLevelFilenameFromBasename(getPackedLevelBasename(type));
2221 char *getDefaultLevelFilename(int nr)
2223 return getSingleLevelFilename(nr);
2226 #if ENABLE_UNUSED_CODE
2227 static void setLevelFileInfo_SingleLevelFilename(struct LevelFileInfo *lfi,
2231 lfi->packed = FALSE;
2233 setString(&lfi->basename, getSingleLevelBasename(lfi->nr, lfi->type));
2234 setString(&lfi->filename, getLevelFilenameFromBasename(lfi->basename));
2238 static void setLevelFileInfo_FormatLevelFilename(struct LevelFileInfo *lfi,
2239 int type, char *format, ...)
2241 static char basename[MAX_FILENAME_LEN];
2244 va_start(ap, format);
2245 vsprintf(basename, format, ap);
2249 lfi->packed = FALSE;
2251 setString(&lfi->basename, basename);
2252 setString(&lfi->filename, getLevelFilenameFromBasename(lfi->basename));
2255 static void setLevelFileInfo_PackedLevelFilename(struct LevelFileInfo *lfi,
2261 setString(&lfi->basename, getPackedLevelBasename(lfi->type));
2262 setString(&lfi->filename, getLevelFilenameFromBasename(lfi->basename));
2265 static int getFiletypeFromID(char *filetype_id)
2267 char *filetype_id_lower;
2268 int filetype = LEVEL_FILE_TYPE_UNKNOWN;
2271 if (filetype_id == NULL)
2272 return LEVEL_FILE_TYPE_UNKNOWN;
2274 filetype_id_lower = getStringToLower(filetype_id);
2276 for (i = 0; filetype_id_list[i].id != NULL; i++)
2278 char *id_lower = getStringToLower(filetype_id_list[i].id);
2280 if (strEqual(filetype_id_lower, id_lower))
2281 filetype = filetype_id_list[i].filetype;
2285 if (filetype != LEVEL_FILE_TYPE_UNKNOWN)
2289 free(filetype_id_lower);
2294 char *getLocalLevelTemplateFilename(void)
2296 return getDefaultLevelFilename(-1);
2299 char *getGlobalLevelTemplateFilename(void)
2301 // global variable "leveldir_current" must be modified in the loop below
2302 LevelDirTree *leveldir_current_last = leveldir_current;
2303 char *filename = NULL;
2305 // check for template level in path from current to topmost tree node
2307 while (leveldir_current != NULL)
2309 filename = getDefaultLevelFilename(-1);
2311 if (fileExists(filename))
2314 leveldir_current = leveldir_current->node_parent;
2317 // restore global variable "leveldir_current" modified in above loop
2318 leveldir_current = leveldir_current_last;
2323 static void determineLevelFileInfo_Filename(struct LevelFileInfo *lfi)
2327 // special case: level number is negative => check for level template file
2330 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
2331 getSingleLevelBasename(-1));
2333 // replace local level template filename with global template filename
2334 setString(&lfi->filename, getGlobalLevelTemplateFilename());
2336 // no fallback if template file not existing
2340 // special case: check for file name/pattern specified in "levelinfo.conf"
2341 if (leveldir_current->level_filename != NULL)
2343 int filetype = getFiletypeFromID(leveldir_current->level_filetype);
2345 setLevelFileInfo_FormatLevelFilename(lfi, filetype,
2346 leveldir_current->level_filename, nr);
2348 lfi->packed = checkForPackageFromBasename(leveldir_current->level_filename);
2350 if (fileExists(lfi->filename))
2353 else if (leveldir_current->level_filetype != NULL)
2355 int filetype = getFiletypeFromID(leveldir_current->level_filetype);
2357 // check for specified native level file with standard file name
2358 setLevelFileInfo_FormatLevelFilename(lfi, filetype,
2359 "%03d.%s", nr, LEVELFILE_EXTENSION);
2360 if (fileExists(lfi->filename))
2364 // check for native Rocks'n'Diamonds level file
2365 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
2366 "%03d.%s", nr, LEVELFILE_EXTENSION);
2367 if (fileExists(lfi->filename))
2370 // check for Emerald Mine level file (V1)
2371 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "a%c%c",
2372 'a' + (nr / 10) % 26, '0' + nr % 10);
2373 if (fileExists(lfi->filename))
2375 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "A%c%c",
2376 'A' + (nr / 10) % 26, '0' + nr % 10);
2377 if (fileExists(lfi->filename))
2380 // check for Emerald Mine level file (V2 to V5)
2381 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%d", nr);
2382 if (fileExists(lfi->filename))
2385 // check for Emerald Mine level file (V6 / single mode)
2386 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02ds", nr);
2387 if (fileExists(lfi->filename))
2389 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02dS", nr);
2390 if (fileExists(lfi->filename))
2393 // check for Emerald Mine level file (V6 / teamwork mode)
2394 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02dt", nr);
2395 if (fileExists(lfi->filename))
2397 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02dT", nr);
2398 if (fileExists(lfi->filename))
2401 // check for various packed level file formats
2402 setLevelFileInfo_PackedLevelFilename(lfi, LEVEL_FILE_TYPE_UNKNOWN);
2403 if (fileExists(lfi->filename))
2406 // no known level file found -- use default values (and fail later)
2407 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
2408 "%03d.%s", nr, LEVELFILE_EXTENSION);
2411 static void determineLevelFileInfo_Filetype(struct LevelFileInfo *lfi)
2413 if (lfi->type == LEVEL_FILE_TYPE_UNKNOWN)
2414 lfi->type = getFileTypeFromBasename(lfi->basename);
2416 if (lfi->type == LEVEL_FILE_TYPE_RND)
2417 lfi->type = getFileTypeFromMagicBytes(lfi->filename, lfi->type);
2420 static void setLevelFileInfo(struct LevelFileInfo *level_file_info, int nr)
2422 // always start with reliable default values
2423 setFileInfoToDefaults(level_file_info);
2425 level_file_info->nr = nr; // set requested level number
2427 determineLevelFileInfo_Filename(level_file_info);
2428 determineLevelFileInfo_Filetype(level_file_info);
2431 static void copyLevelFileInfo(struct LevelFileInfo *lfi_from,
2432 struct LevelFileInfo *lfi_to)
2434 lfi_to->nr = lfi_from->nr;
2435 lfi_to->type = lfi_from->type;
2436 lfi_to->packed = lfi_from->packed;
2438 setString(&lfi_to->basename, lfi_from->basename);
2439 setString(&lfi_to->filename, lfi_from->filename);
2442 // ----------------------------------------------------------------------------
2443 // functions for loading R'n'D level
2444 // ----------------------------------------------------------------------------
2446 int getMappedElement(int element)
2448 // remap some (historic, now obsolete) elements
2452 case EL_PLAYER_OBSOLETE:
2453 element = EL_PLAYER_1;
2456 case EL_KEY_OBSOLETE:
2460 case EL_EM_KEY_1_FILE_OBSOLETE:
2461 element = EL_EM_KEY_1;
2464 case EL_EM_KEY_2_FILE_OBSOLETE:
2465 element = EL_EM_KEY_2;
2468 case EL_EM_KEY_3_FILE_OBSOLETE:
2469 element = EL_EM_KEY_3;
2472 case EL_EM_KEY_4_FILE_OBSOLETE:
2473 element = EL_EM_KEY_4;
2476 case EL_ENVELOPE_OBSOLETE:
2477 element = EL_ENVELOPE_1;
2485 if (element >= NUM_FILE_ELEMENTS)
2487 Warn("invalid level element %d", element);
2489 element = EL_UNKNOWN;
2497 static int getMappedElementByVersion(int element, int game_version)
2499 // remap some elements due to certain game version
2501 if (game_version <= VERSION_IDENT(2,2,0,0))
2503 // map game font elements
2504 element = (element == EL_CHAR('[') ? EL_CHAR_AUMLAUT :
2505 element == EL_CHAR('\\') ? EL_CHAR_OUMLAUT :
2506 element == EL_CHAR(']') ? EL_CHAR_UUMLAUT :
2507 element == EL_CHAR('^') ? EL_CHAR_COPYRIGHT : element);
2510 if (game_version < VERSION_IDENT(3,0,0,0))
2512 // map Supaplex gravity tube elements
2513 element = (element == EL_SP_GRAVITY_PORT_LEFT ? EL_SP_PORT_LEFT :
2514 element == EL_SP_GRAVITY_PORT_RIGHT ? EL_SP_PORT_RIGHT :
2515 element == EL_SP_GRAVITY_PORT_UP ? EL_SP_PORT_UP :
2516 element == EL_SP_GRAVITY_PORT_DOWN ? EL_SP_PORT_DOWN :
2523 static int LoadLevel_VERS(File *file, int chunk_size, struct LevelInfo *level)
2525 level->file_version = getFileVersion(file);
2526 level->game_version = getFileVersion(file);
2531 static int LoadLevel_DATE(File *file, int chunk_size, struct LevelInfo *level)
2533 level->creation_date.year = getFile16BitBE(file);
2534 level->creation_date.month = getFile8Bit(file);
2535 level->creation_date.day = getFile8Bit(file);
2537 level->creation_date.src = DATE_SRC_LEVELFILE;
2542 static int LoadLevel_HEAD(File *file, int chunk_size, struct LevelInfo *level)
2544 int initial_player_stepsize;
2545 int initial_player_gravity;
2548 level->fieldx = getFile8Bit(file);
2549 level->fieldy = getFile8Bit(file);
2551 level->time = getFile16BitBE(file);
2552 level->gems_needed = getFile16BitBE(file);
2554 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
2555 level->name[i] = getFile8Bit(file);
2556 level->name[MAX_LEVEL_NAME_LEN] = 0;
2558 for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
2559 level->score[i] = getFile8Bit(file);
2561 level->num_yamyam_contents = STD_ELEMENT_CONTENTS;
2562 for (i = 0; i < STD_ELEMENT_CONTENTS; i++)
2563 for (y = 0; y < 3; y++)
2564 for (x = 0; x < 3; x++)
2565 level->yamyam_content[i].e[x][y] = getMappedElement(getFile8Bit(file));
2567 level->amoeba_speed = getFile8Bit(file);
2568 level->time_magic_wall = getFile8Bit(file);
2569 level->time_wheel = getFile8Bit(file);
2570 level->amoeba_content = getMappedElement(getFile8Bit(file));
2572 initial_player_stepsize = (getFile8Bit(file) == 1 ? STEPSIZE_FAST :
2575 for (i = 0; i < MAX_PLAYERS; i++)
2576 level->initial_player_stepsize[i] = initial_player_stepsize;
2578 initial_player_gravity = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2580 for (i = 0; i < MAX_PLAYERS; i++)
2581 level->initial_player_gravity[i] = initial_player_gravity;
2583 level->encoding_16bit_field = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2584 level->em_slippery_gems = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2586 level->use_custom_template = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2588 level->block_last_field = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2589 level->sp_block_last_field = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2590 level->can_move_into_acid_bits = getFile32BitBE(file);
2591 level->dont_collide_with_bits = getFile8Bit(file);
2593 level->use_spring_bug = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2594 level->use_step_counter = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2596 level->instant_relocation = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2597 level->can_pass_to_walkable = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2598 level->grow_into_diggable = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2600 level->game_engine_type = getFile8Bit(file);
2602 ReadUnusedBytesFromFile(file, LEVEL_CHUNK_HEAD_UNUSED);
2607 static int LoadLevel_NAME(File *file, int chunk_size, struct LevelInfo *level)
2611 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
2612 level->name[i] = getFile8Bit(file);
2613 level->name[MAX_LEVEL_NAME_LEN] = 0;
2618 static int LoadLevel_AUTH(File *file, int chunk_size, struct LevelInfo *level)
2622 for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
2623 level->author[i] = getFile8Bit(file);
2624 level->author[MAX_LEVEL_AUTHOR_LEN] = 0;
2629 static int LoadLevel_BODY(File *file, int chunk_size, struct LevelInfo *level)
2632 int chunk_size_expected = level->fieldx * level->fieldy;
2634 /* Note: "chunk_size" was wrong before version 2.0 when elements are
2635 stored with 16-bit encoding (and should be twice as big then).
2636 Even worse, playfield data was stored 16-bit when only yamyam content
2637 contained 16-bit elements and vice versa. */
2639 if (level->encoding_16bit_field && level->file_version >= FILE_VERSION_2_0)
2640 chunk_size_expected *= 2;
2642 if (chunk_size_expected != chunk_size)
2644 ReadUnusedBytesFromFile(file, chunk_size);
2645 return chunk_size_expected;
2648 for (y = 0; y < level->fieldy; y++)
2649 for (x = 0; x < level->fieldx; x++)
2650 level->field[x][y] =
2651 getMappedElement(level->encoding_16bit_field ? getFile16BitBE(file) :
2656 static int LoadLevel_CONT(File *file, int chunk_size, struct LevelInfo *level)
2659 int header_size = 4;
2660 int content_size = MAX_ELEMENT_CONTENTS * 3 * 3;
2661 int chunk_size_expected = header_size + content_size;
2663 /* Note: "chunk_size" was wrong before version 2.0 when elements are
2664 stored with 16-bit encoding (and should be twice as big then).
2665 Even worse, playfield data was stored 16-bit when only yamyam content
2666 contained 16-bit elements and vice versa. */
2668 if (level->encoding_16bit_field && level->file_version >= FILE_VERSION_2_0)
2669 chunk_size_expected += content_size;
2671 if (chunk_size_expected != chunk_size)
2673 ReadUnusedBytesFromFile(file, chunk_size);
2674 return chunk_size_expected;
2678 level->num_yamyam_contents = getFile8Bit(file);
2682 // correct invalid number of content fields -- should never happen
2683 if (level->num_yamyam_contents < 1 ||
2684 level->num_yamyam_contents > MAX_ELEMENT_CONTENTS)
2685 level->num_yamyam_contents = STD_ELEMENT_CONTENTS;
2687 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
2688 for (y = 0; y < 3; y++)
2689 for (x = 0; x < 3; x++)
2690 level->yamyam_content[i].e[x][y] =
2691 getMappedElement(level->encoding_16bit_field ?
2692 getFile16BitBE(file) : getFile8Bit(file));
2696 static int LoadLevel_CNT2(File *file, int chunk_size, struct LevelInfo *level)
2701 int content_array[MAX_ELEMENT_CONTENTS][3][3];
2703 element = getMappedElement(getFile16BitBE(file));
2704 num_contents = getFile8Bit(file);
2706 getFile8Bit(file); // content x size (unused)
2707 getFile8Bit(file); // content y size (unused)
2709 ReadUnusedBytesFromFile(file, LEVEL_CHUNK_CNT2_UNUSED);
2711 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
2712 for (y = 0; y < 3; y++)
2713 for (x = 0; x < 3; x++)
2714 content_array[i][x][y] = getMappedElement(getFile16BitBE(file));
2716 // correct invalid number of content fields -- should never happen
2717 if (num_contents < 1 || num_contents > MAX_ELEMENT_CONTENTS)
2718 num_contents = STD_ELEMENT_CONTENTS;
2720 if (element == EL_YAMYAM)
2722 level->num_yamyam_contents = num_contents;
2724 for (i = 0; i < num_contents; i++)
2725 for (y = 0; y < 3; y++)
2726 for (x = 0; x < 3; x++)
2727 level->yamyam_content[i].e[x][y] = content_array[i][x][y];
2729 else if (element == EL_BD_AMOEBA)
2731 level->amoeba_content = content_array[0][0][0];
2735 Warn("cannot load content for element '%d'", element);
2741 static int LoadLevel_CNT3(File *file, int chunk_size, struct LevelInfo *level)
2747 int chunk_size_expected;
2749 element = getMappedElement(getFile16BitBE(file));
2750 if (!IS_ENVELOPE(element))
2751 element = EL_ENVELOPE_1;
2753 envelope_nr = element - EL_ENVELOPE_1;
2755 envelope_len = getFile16BitBE(file);
2757 level->envelope[envelope_nr].xsize = getFile8Bit(file);
2758 level->envelope[envelope_nr].ysize = getFile8Bit(file);
2760 ReadUnusedBytesFromFile(file, LEVEL_CHUNK_CNT3_UNUSED);
2762 chunk_size_expected = LEVEL_CHUNK_CNT3_SIZE(envelope_len);
2763 if (chunk_size_expected != chunk_size)
2765 ReadUnusedBytesFromFile(file, chunk_size - LEVEL_CHUNK_CNT3_HEADER);
2766 return chunk_size_expected;
2769 for (i = 0; i < envelope_len; i++)
2770 level->envelope[envelope_nr].text[i] = getFile8Bit(file);
2775 static int LoadLevel_CUS1(File *file, int chunk_size, struct LevelInfo *level)
2777 int num_changed_custom_elements = getFile16BitBE(file);
2778 int chunk_size_expected = 2 + num_changed_custom_elements * 6;
2781 if (chunk_size_expected != chunk_size)
2783 ReadUnusedBytesFromFile(file, chunk_size - 2);
2784 return chunk_size_expected;
2787 for (i = 0; i < num_changed_custom_elements; i++)
2789 int element = getMappedElement(getFile16BitBE(file));
2790 int properties = getFile32BitBE(file);
2792 if (IS_CUSTOM_ELEMENT(element))
2793 element_info[element].properties[EP_BITFIELD_BASE_NR] = properties;
2795 Warn("invalid custom element number %d", element);
2797 // older game versions that wrote level files with CUS1 chunks used
2798 // different default push delay values (not yet stored in level file)
2799 element_info[element].push_delay_fixed = 2;
2800 element_info[element].push_delay_random = 8;
2803 level->file_has_custom_elements = TRUE;
2808 static int LoadLevel_CUS2(File *file, int chunk_size, struct LevelInfo *level)
2810 int num_changed_custom_elements = getFile16BitBE(file);
2811 int chunk_size_expected = 2 + num_changed_custom_elements * 4;
2814 if (chunk_size_expected != chunk_size)
2816 ReadUnusedBytesFromFile(file, chunk_size - 2);
2817 return chunk_size_expected;
2820 for (i = 0; i < num_changed_custom_elements; i++)
2822 int element = getMappedElement(getFile16BitBE(file));
2823 int custom_target_element = getMappedElement(getFile16BitBE(file));
2825 if (IS_CUSTOM_ELEMENT(element))
2826 element_info[element].change->target_element = custom_target_element;
2828 Warn("invalid custom element number %d", element);
2831 level->file_has_custom_elements = TRUE;
2836 static int LoadLevel_CUS3(File *file, int chunk_size, struct LevelInfo *level)
2838 int num_changed_custom_elements = getFile16BitBE(file);
2839 int chunk_size_expected = LEVEL_CHUNK_CUS3_SIZE(num_changed_custom_elements);
2842 if (chunk_size_expected != chunk_size)
2844 ReadUnusedBytesFromFile(file, chunk_size - 2);
2845 return chunk_size_expected;
2848 for (i = 0; i < num_changed_custom_elements; i++)
2850 int element = getMappedElement(getFile16BitBE(file));
2851 struct ElementInfo *ei = &element_info[element];
2852 unsigned int event_bits;
2854 if (!IS_CUSTOM_ELEMENT(element))
2856 Warn("invalid custom element number %d", element);
2858 element = EL_INTERNAL_DUMMY;
2861 for (j = 0; j < MAX_ELEMENT_NAME_LEN; j++)
2862 ei->description[j] = getFile8Bit(file);
2863 ei->description[MAX_ELEMENT_NAME_LEN] = 0;
2865 ei->properties[EP_BITFIELD_BASE_NR] = getFile32BitBE(file);
2867 // some free bytes for future properties and padding
2868 ReadUnusedBytesFromFile(file, 7);
2870 ei->use_gfx_element = getFile8Bit(file);
2871 ei->gfx_element_initial = getMappedElement(getFile16BitBE(file));
2873 ei->collect_score_initial = getFile8Bit(file);
2874 ei->collect_count_initial = getFile8Bit(file);
2876 ei->push_delay_fixed = getFile16BitBE(file);
2877 ei->push_delay_random = getFile16BitBE(file);
2878 ei->move_delay_fixed = getFile16BitBE(file);
2879 ei->move_delay_random = getFile16BitBE(file);
2881 ei->move_pattern = getFile16BitBE(file);
2882 ei->move_direction_initial = getFile8Bit(file);
2883 ei->move_stepsize = getFile8Bit(file);
2885 for (y = 0; y < 3; y++)
2886 for (x = 0; x < 3; x++)
2887 ei->content.e[x][y] = getMappedElement(getFile16BitBE(file));
2889 // bits 0 - 31 of "has_event[]"
2890 event_bits = getFile32BitBE(file);
2891 for (j = 0; j < MIN(NUM_CHANGE_EVENTS, 32); j++)
2892 if (event_bits & (1u << j))
2893 ei->change->has_event[j] = TRUE;
2895 ei->change->target_element = getMappedElement(getFile16BitBE(file));
2897 ei->change->delay_fixed = getFile16BitBE(file);
2898 ei->change->delay_random = getFile16BitBE(file);
2899 ei->change->delay_frames = getFile16BitBE(file);
2901 ei->change->initial_trigger_element = getMappedElement(getFile16BitBE(file));
2903 ei->change->explode = getFile8Bit(file);
2904 ei->change->use_target_content = getFile8Bit(file);
2905 ei->change->only_if_complete = getFile8Bit(file);
2906 ei->change->use_random_replace = getFile8Bit(file);
2908 ei->change->random_percentage = getFile8Bit(file);
2909 ei->change->replace_when = getFile8Bit(file);
2911 for (y = 0; y < 3; y++)
2912 for (x = 0; x < 3; x++)
2913 ei->change->target_content.e[x][y] =
2914 getMappedElement(getFile16BitBE(file));
2916 ei->slippery_type = getFile8Bit(file);
2918 // some free bytes for future properties and padding
2919 ReadUnusedBytesFromFile(file, LEVEL_CPART_CUS3_UNUSED);
2921 // mark that this custom element has been modified
2922 ei->modified_settings = TRUE;
2925 level->file_has_custom_elements = TRUE;
2930 static int LoadLevel_CUS4(File *file, int chunk_size, struct LevelInfo *level)
2932 struct ElementInfo *ei;
2933 int chunk_size_expected;
2937 // ---------- custom element base property values (96 bytes) ----------------
2939 element = getMappedElement(getFile16BitBE(file));
2941 if (!IS_CUSTOM_ELEMENT(element))
2943 Warn("invalid custom element number %d", element);
2945 ReadUnusedBytesFromFile(file, chunk_size - 2);
2950 ei = &element_info[element];
2952 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
2953 ei->description[i] = getFile8Bit(file);
2954 ei->description[MAX_ELEMENT_NAME_LEN] = 0;
2956 ei->properties[EP_BITFIELD_BASE_NR] = getFile32BitBE(file);
2958 ReadUnusedBytesFromFile(file, 4); // reserved for more base properties
2960 ei->num_change_pages = getFile8Bit(file);
2962 chunk_size_expected = LEVEL_CHUNK_CUS4_SIZE(ei->num_change_pages);
2963 if (chunk_size_expected != chunk_size)
2965 ReadUnusedBytesFromFile(file, chunk_size - 43);
2966 return chunk_size_expected;
2969 ei->ce_value_fixed_initial = getFile16BitBE(file);
2970 ei->ce_value_random_initial = getFile16BitBE(file);
2971 ei->use_last_ce_value = getFile8Bit(file);
2973 ei->use_gfx_element = getFile8Bit(file);
2974 ei->gfx_element_initial = getMappedElement(getFile16BitBE(file));
2976 ei->collect_score_initial = getFile8Bit(file);
2977 ei->collect_count_initial = getFile8Bit(file);
2979 ei->drop_delay_fixed = getFile8Bit(file);
2980 ei->push_delay_fixed = getFile8Bit(file);
2981 ei->drop_delay_random = getFile8Bit(file);
2982 ei->push_delay_random = getFile8Bit(file);
2983 ei->move_delay_fixed = getFile16BitBE(file);
2984 ei->move_delay_random = getFile16BitBE(file);
2986 // bits 0 - 15 of "move_pattern" ...
2987 ei->move_pattern = getFile16BitBE(file);
2988 ei->move_direction_initial = getFile8Bit(file);
2989 ei->move_stepsize = getFile8Bit(file);
2991 ei->slippery_type = getFile8Bit(file);
2993 for (y = 0; y < 3; y++)
2994 for (x = 0; x < 3; x++)
2995 ei->content.e[x][y] = getMappedElement(getFile16BitBE(file));
2997 ei->move_enter_element = getMappedElement(getFile16BitBE(file));
2998 ei->move_leave_element = getMappedElement(getFile16BitBE(file));
2999 ei->move_leave_type = getFile8Bit(file);
3001 // ... bits 16 - 31 of "move_pattern" (not nice, but downward compatible)
3002 ei->move_pattern |= (getFile16BitBE(file) << 16);
3004 ei->access_direction = getFile8Bit(file);
3006 ei->explosion_delay = getFile8Bit(file);
3007 ei->ignition_delay = getFile8Bit(file);
3008 ei->explosion_type = getFile8Bit(file);
3010 // some free bytes for future custom property values and padding
3011 ReadUnusedBytesFromFile(file, 1);
3013 // ---------- change page property values (48 bytes) ------------------------
3015 setElementChangePages(ei, ei->num_change_pages);
3017 for (i = 0; i < ei->num_change_pages; i++)
3019 struct ElementChangeInfo *change = &ei->change_page[i];
3020 unsigned int event_bits;
3022 // always start with reliable default values
3023 setElementChangeInfoToDefaults(change);
3025 // bits 0 - 31 of "has_event[]" ...
3026 event_bits = getFile32BitBE(file);
3027 for (j = 0; j < MIN(NUM_CHANGE_EVENTS, 32); j++)
3028 if (event_bits & (1u << j))
3029 change->has_event[j] = TRUE;
3031 change->target_element = getMappedElement(getFile16BitBE(file));
3033 change->delay_fixed = getFile16BitBE(file);
3034 change->delay_random = getFile16BitBE(file);
3035 change->delay_frames = getFile16BitBE(file);
3037 change->initial_trigger_element = getMappedElement(getFile16BitBE(file));
3039 change->explode = getFile8Bit(file);
3040 change->use_target_content = getFile8Bit(file);
3041 change->only_if_complete = getFile8Bit(file);
3042 change->use_random_replace = getFile8Bit(file);
3044 change->random_percentage = getFile8Bit(file);
3045 change->replace_when = getFile8Bit(file);
3047 for (y = 0; y < 3; y++)
3048 for (x = 0; x < 3; x++)
3049 change->target_content.e[x][y]= getMappedElement(getFile16BitBE(file));
3051 change->can_change = getFile8Bit(file);
3053 change->trigger_side = getFile8Bit(file);
3055 change->trigger_player = getFile8Bit(file);
3056 change->trigger_page = getFile8Bit(file);
3058 change->trigger_page = (change->trigger_page == CH_PAGE_ANY_FILE ?
3059 CH_PAGE_ANY : (1 << change->trigger_page));
3061 change->has_action = getFile8Bit(file);
3062 change->action_type = getFile8Bit(file);
3063 change->action_mode = getFile8Bit(file);
3064 change->action_arg = getFile16BitBE(file);
3066 // ... bits 32 - 39 of "has_event[]" (not nice, but downward compatible)
3067 event_bits = getFile8Bit(file);
3068 for (j = 32; j < NUM_CHANGE_EVENTS; j++)
3069 if (event_bits & (1u << (j - 32)))
3070 change->has_event[j] = TRUE;
3073 // mark this custom element as modified
3074 ei->modified_settings = TRUE;
3076 level->file_has_custom_elements = TRUE;
3081 static int LoadLevel_GRP1(File *file, int chunk_size, struct LevelInfo *level)
3083 struct ElementInfo *ei;
3084 struct ElementGroupInfo *group;
3088 element = getMappedElement(getFile16BitBE(file));
3090 if (!IS_GROUP_ELEMENT(element))
3092 Warn("invalid group element number %d", element);
3094 ReadUnusedBytesFromFile(file, chunk_size - 2);
3099 ei = &element_info[element];
3101 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
3102 ei->description[i] = getFile8Bit(file);
3103 ei->description[MAX_ELEMENT_NAME_LEN] = 0;
3105 group = element_info[element].group;
3107 group->num_elements = getFile8Bit(file);
3109 ei->use_gfx_element = getFile8Bit(file);
3110 ei->gfx_element_initial = getMappedElement(getFile16BitBE(file));
3112 group->choice_mode = getFile8Bit(file);
3114 // some free bytes for future values and padding
3115 ReadUnusedBytesFromFile(file, 3);
3117 for (i = 0; i < MAX_ELEMENTS_IN_GROUP; i++)
3118 group->element[i] = getMappedElement(getFile16BitBE(file));
3120 // mark this group element as modified
3121 element_info[element].modified_settings = TRUE;
3123 level->file_has_custom_elements = TRUE;
3128 static int LoadLevel_MicroChunk(File *file, struct LevelFileConfigInfo *conf,
3129 int element, int real_element)
3131 int micro_chunk_size = 0;
3132 int conf_type = getFile8Bit(file);
3133 int byte_mask = conf_type & CONF_MASK_BYTES;
3134 boolean element_found = FALSE;
3137 micro_chunk_size += 1;
3139 if (byte_mask == CONF_MASK_MULTI_BYTES)
3141 int num_bytes = getFile16BitBE(file);
3142 byte *buffer = checked_malloc(num_bytes);
3144 ReadBytesFromFile(file, buffer, num_bytes);
3146 for (i = 0; conf[i].data_type != -1; i++)
3148 if (conf[i].element == element &&
3149 conf[i].conf_type == conf_type)
3151 int data_type = conf[i].data_type;
3152 int num_entities = num_bytes / CONF_ENTITY_NUM_BYTES(data_type);
3153 int max_num_entities = conf[i].max_num_entities;
3155 if (num_entities > max_num_entities)
3157 Warn("truncating number of entities for element %d from %d to %d",
3158 element, num_entities, max_num_entities);
3160 num_entities = max_num_entities;
3163 if (num_entities == 0 && (data_type == TYPE_ELEMENT_LIST ||
3164 data_type == TYPE_CONTENT_LIST))
3166 // for element and content lists, zero entities are not allowed
3167 Warn("found empty list of entities for element %d", element);
3169 // do not set "num_entities" here to prevent reading behind buffer
3171 *(int *)(conf[i].num_entities) = 1; // at least one is required
3175 *(int *)(conf[i].num_entities) = num_entities;
3178 element_found = TRUE;
3180 if (data_type == TYPE_STRING)
3182 char *string = (char *)(conf[i].value);
3185 for (j = 0; j < max_num_entities; j++)
3186 string[j] = (j < num_entities ? buffer[j] : '\0');
3188 else if (data_type == TYPE_ELEMENT_LIST)
3190 int *element_array = (int *)(conf[i].value);
3193 for (j = 0; j < num_entities; j++)
3195 getMappedElement(CONF_ELEMENTS_ELEMENT(buffer, j));
3197 else if (data_type == TYPE_CONTENT_LIST)
3199 struct Content *content= (struct Content *)(conf[i].value);
3202 for (c = 0; c < num_entities; c++)
3203 for (y = 0; y < 3; y++)
3204 for (x = 0; x < 3; x++)
3205 content[c].e[x][y] =
3206 getMappedElement(CONF_CONTENTS_ELEMENT(buffer, c, x, y));
3209 element_found = FALSE;
3215 checked_free(buffer);
3217 micro_chunk_size += 2 + num_bytes;
3219 else // constant size configuration data (1, 2 or 4 bytes)
3221 int value = (byte_mask == CONF_MASK_1_BYTE ? getFile8Bit (file) :
3222 byte_mask == CONF_MASK_2_BYTE ? getFile16BitBE(file) :
3223 byte_mask == CONF_MASK_4_BYTE ? getFile32BitBE(file) : 0);
3225 for (i = 0; conf[i].data_type != -1; i++)
3227 if (conf[i].element == element &&
3228 conf[i].conf_type == conf_type)
3230 int data_type = conf[i].data_type;
3232 if (data_type == TYPE_ELEMENT)
3233 value = getMappedElement(value);
3235 if (data_type == TYPE_BOOLEAN)
3236 *(boolean *)(conf[i].value) = (value ? TRUE : FALSE);
3238 *(int *) (conf[i].value) = value;
3240 element_found = TRUE;
3246 micro_chunk_size += CONF_VALUE_NUM_BYTES(byte_mask);
3251 char *error_conf_chunk_bytes =
3252 (byte_mask == CONF_MASK_1_BYTE ? "CONF_VALUE_8_BIT" :
3253 byte_mask == CONF_MASK_2_BYTE ? "CONF_VALUE_16_BIT" :
3254 byte_mask == CONF_MASK_4_BYTE ? "CONF_VALUE_32_BIT" :"CONF_VALUE_BYTES");
3255 int error_conf_chunk_token = conf_type & CONF_MASK_TOKEN;
3256 int error_element = real_element;
3258 Warn("cannot load micro chunk '%s(%d)' value for element %d ['%s']",
3259 error_conf_chunk_bytes, error_conf_chunk_token,
3260 error_element, EL_NAME(error_element));
3263 return micro_chunk_size;
3266 static int LoadLevel_INFO(File *file, int chunk_size, struct LevelInfo *level)
3268 int real_chunk_size = 0;
3270 li = *level; // copy level data into temporary buffer
3272 while (!checkEndOfFile(file))
3274 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_INFO, -1, -1);
3276 if (real_chunk_size >= chunk_size)
3280 *level = li; // copy temporary buffer back to level data
3282 return real_chunk_size;
3285 static int LoadLevel_CONF(File *file, int chunk_size, struct LevelInfo *level)
3287 int real_chunk_size = 0;
3289 li = *level; // copy level data into temporary buffer
3291 while (!checkEndOfFile(file))
3293 int element = getMappedElement(getFile16BitBE(file));
3295 real_chunk_size += 2;
3296 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_CONF,
3298 if (real_chunk_size >= chunk_size)
3302 *level = li; // copy temporary buffer back to level data
3304 return real_chunk_size;
3307 static int LoadLevel_ELEM(File *file, int chunk_size, struct LevelInfo *level)
3309 int real_chunk_size = 0;
3311 li = *level; // copy level data into temporary buffer
3313 while (!checkEndOfFile(file))
3315 int element = getMappedElement(getFile16BitBE(file));
3317 real_chunk_size += 2;
3318 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_ELEM,
3320 if (real_chunk_size >= chunk_size)
3324 *level = li; // copy temporary buffer back to level data
3326 return real_chunk_size;
3329 static int LoadLevel_NOTE(File *file, int chunk_size, struct LevelInfo *level)
3331 int element = getMappedElement(getFile16BitBE(file));
3332 int envelope_nr = element - EL_ENVELOPE_1;
3333 int real_chunk_size = 2;
3335 xx_envelope = level->envelope[envelope_nr]; // copy into temporary buffer
3337 while (!checkEndOfFile(file))
3339 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_NOTE,
3342 if (real_chunk_size >= chunk_size)
3346 level->envelope[envelope_nr] = xx_envelope; // copy from temporary buffer
3348 return real_chunk_size;
3351 static int LoadLevel_CUSX(File *file, int chunk_size, struct LevelInfo *level)
3353 int element = getMappedElement(getFile16BitBE(file));
3354 int real_chunk_size = 2;
3355 struct ElementInfo *ei = &element_info[element];
3358 xx_ei = *ei; // copy element data into temporary buffer
3360 xx_ei.num_change_pages = -1;
3362 while (!checkEndOfFile(file))
3364 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_CUSX_base,
3366 if (xx_ei.num_change_pages != -1)
3369 if (real_chunk_size >= chunk_size)
3375 if (ei->num_change_pages == -1)
3377 Warn("LoadLevel_CUSX(): missing 'num_change_pages' for '%s'",
3380 ei->num_change_pages = 1;
3382 setElementChangePages(ei, 1);
3383 setElementChangeInfoToDefaults(ei->change);
3385 return real_chunk_size;
3388 // initialize number of change pages stored for this custom element
3389 setElementChangePages(ei, ei->num_change_pages);
3390 for (i = 0; i < ei->num_change_pages; i++)
3391 setElementChangeInfoToDefaults(&ei->change_page[i]);
3393 // start with reading properties for the first change page
3394 xx_current_change_page = 0;
3396 while (!checkEndOfFile(file))
3398 // level file might contain invalid change page number
3399 if (xx_current_change_page >= ei->num_change_pages)
3402 struct ElementChangeInfo *change = &ei->change_page[xx_current_change_page];
3404 xx_change = *change; // copy change data into temporary buffer
3406 resetEventBits(); // reset bits; change page might have changed
3408 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_CUSX_change,
3411 *change = xx_change;
3413 setEventFlagsFromEventBits(change);
3415 if (real_chunk_size >= chunk_size)
3419 level->file_has_custom_elements = TRUE;
3421 return real_chunk_size;
3424 static int LoadLevel_GRPX(File *file, int chunk_size, struct LevelInfo *level)
3426 int element = getMappedElement(getFile16BitBE(file));
3427 int real_chunk_size = 2;
3428 struct ElementInfo *ei = &element_info[element];
3429 struct ElementGroupInfo *group = ei->group;
3434 xx_ei = *ei; // copy element data into temporary buffer
3435 xx_group = *group; // copy group data into temporary buffer
3437 while (!checkEndOfFile(file))
3439 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_GRPX,
3442 if (real_chunk_size >= chunk_size)
3449 level->file_has_custom_elements = TRUE;
3451 return real_chunk_size;
3454 static int LoadLevel_EMPX(File *file, int chunk_size, struct LevelInfo *level)
3456 int element = getMappedElement(getFile16BitBE(file));
3457 int real_chunk_size = 2;
3458 struct ElementInfo *ei = &element_info[element];
3460 xx_ei = *ei; // copy element data into temporary buffer
3462 while (!checkEndOfFile(file))
3464 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_EMPX,
3467 if (real_chunk_size >= chunk_size)
3473 level->file_has_custom_elements = TRUE;
3475 return real_chunk_size;
3478 static void LoadLevelFromFileInfo_RND(struct LevelInfo *level,
3479 struct LevelFileInfo *level_file_info,
3480 boolean level_info_only)
3482 char *filename = level_file_info->filename;
3483 char cookie[MAX_LINE_LEN];
3484 char chunk_name[CHUNK_ID_LEN + 1];
3488 if (!(file = openFile(filename, MODE_READ)))
3490 level->no_valid_file = TRUE;
3491 level->no_level_file = TRUE;
3493 if (level_info_only)
3496 Warn("cannot read level '%s' -- using empty level", filename);
3498 if (!setup.editor.use_template_for_new_levels)
3501 // if level file not found, try to initialize level data from template
3502 filename = getGlobalLevelTemplateFilename();
3504 if (!(file = openFile(filename, MODE_READ)))
3507 // default: for empty levels, use level template for custom elements
3508 level->use_custom_template = TRUE;
3510 level->no_valid_file = FALSE;
3513 getFileChunkBE(file, chunk_name, NULL);
3514 if (strEqual(chunk_name, "RND1"))
3516 getFile32BitBE(file); // not used
3518 getFileChunkBE(file, chunk_name, NULL);
3519 if (!strEqual(chunk_name, "CAVE"))
3521 level->no_valid_file = TRUE;
3523 Warn("unknown format of level file '%s'", filename);
3530 else // check for pre-2.0 file format with cookie string
3532 strcpy(cookie, chunk_name);
3533 if (getStringFromFile(file, &cookie[4], MAX_LINE_LEN - 4) == NULL)
3535 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
3536 cookie[strlen(cookie) - 1] = '\0';
3538 if (!checkCookieString(cookie, LEVEL_COOKIE_TMPL))
3540 level->no_valid_file = TRUE;
3542 Warn("unknown format of level file '%s'", filename);
3549 if ((level->file_version = getFileVersionFromCookieString(cookie)) == -1)
3551 level->no_valid_file = TRUE;
3553 Warn("unsupported version of level file '%s'", filename);
3560 // pre-2.0 level files have no game version, so use file version here
3561 level->game_version = level->file_version;
3564 if (level->file_version < FILE_VERSION_1_2)
3566 // level files from versions before 1.2.0 without chunk structure
3567 LoadLevel_HEAD(file, LEVEL_CHUNK_HEAD_SIZE, level);
3568 LoadLevel_BODY(file, level->fieldx * level->fieldy, level);
3576 int (*loader)(File *, int, struct LevelInfo *);
3580 { "VERS", LEVEL_CHUNK_VERS_SIZE, LoadLevel_VERS },
3581 { "DATE", LEVEL_CHUNK_DATE_SIZE, LoadLevel_DATE },
3582 { "HEAD", LEVEL_CHUNK_HEAD_SIZE, LoadLevel_HEAD },
3583 { "NAME", LEVEL_CHUNK_NAME_SIZE, LoadLevel_NAME },
3584 { "AUTH", LEVEL_CHUNK_AUTH_SIZE, LoadLevel_AUTH },
3585 { "INFO", -1, LoadLevel_INFO },
3586 { "BODY", -1, LoadLevel_BODY },
3587 { "CONT", -1, LoadLevel_CONT },
3588 { "CNT2", LEVEL_CHUNK_CNT2_SIZE, LoadLevel_CNT2 },
3589 { "CNT3", -1, LoadLevel_CNT3 },
3590 { "CUS1", -1, LoadLevel_CUS1 },
3591 { "CUS2", -1, LoadLevel_CUS2 },
3592 { "CUS3", -1, LoadLevel_CUS3 },
3593 { "CUS4", -1, LoadLevel_CUS4 },
3594 { "GRP1", -1, LoadLevel_GRP1 },
3595 { "CONF", -1, LoadLevel_CONF },
3596 { "ELEM", -1, LoadLevel_ELEM },
3597 { "NOTE", -1, LoadLevel_NOTE },
3598 { "CUSX", -1, LoadLevel_CUSX },
3599 { "GRPX", -1, LoadLevel_GRPX },
3600 { "EMPX", -1, LoadLevel_EMPX },
3605 while (getFileChunkBE(file, chunk_name, &chunk_size))
3609 while (chunk_info[i].name != NULL &&
3610 !strEqual(chunk_name, chunk_info[i].name))
3613 if (chunk_info[i].name == NULL)
3615 Warn("unknown chunk '%s' in level file '%s'",
3616 chunk_name, filename);
3618 ReadUnusedBytesFromFile(file, chunk_size);
3620 else if (chunk_info[i].size != -1 &&
3621 chunk_info[i].size != chunk_size)
3623 Warn("wrong size (%d) of chunk '%s' in level file '%s'",
3624 chunk_size, chunk_name, filename);
3626 ReadUnusedBytesFromFile(file, chunk_size);
3630 // call function to load this level chunk
3631 int chunk_size_expected =
3632 (chunk_info[i].loader)(file, chunk_size, level);
3634 if (chunk_size_expected < 0)
3636 Warn("error reading chunk '%s' in level file '%s'",
3637 chunk_name, filename);
3642 // the size of some chunks cannot be checked before reading other
3643 // chunks first (like "HEAD" and "BODY") that contain some header
3644 // information, so check them here
3645 if (chunk_size_expected != chunk_size)
3647 Warn("wrong size (%d) of chunk '%s' in level file '%s'",
3648 chunk_size, chunk_name, filename);
3660 // ----------------------------------------------------------------------------
3661 // functions for loading BD level
3662 // ----------------------------------------------------------------------------
3664 static void CopyNativeLevel_RND_to_BD(struct LevelInfo *level)
3666 struct LevelInfo_BD *level_bd = level->native_bd_level;
3667 GdCave *cave = NULL; // will be changed below
3668 int cave_w = MIN(level->fieldx, MAX_PLAYFIELD_WIDTH);
3669 int cave_h = MIN(level->fieldy, MAX_PLAYFIELD_HEIGHT);
3672 setLevelInfoToDefaults_BD_Ext(cave_w, cave_h);
3674 // cave and map newly allocated when set to defaults above
3675 cave = level_bd->cave;
3677 for (i = 0; i < 5; i++)
3679 cave->level_time[i] = level->time;
3680 cave->level_diamonds[i] = level->gems_needed;
3681 cave->level_magic_wall_time[i] = level->time_magic_wall;
3682 cave->level_timevalue[i] = level->score[SC_TIME_BONUS];
3685 cave->diamond_value = level->score[SC_DIAMOND];
3686 cave->extra_diamond_value = level->score[SC_DIAMOND];
3688 cave->level_speed[0] = 160; // set cave speed
3690 strncpy(cave->name, level->name, sizeof(GdString));
3691 cave->name[sizeof(GdString) - 1] = '\0';
3693 for (x = 0; x < cave->w; x++)
3694 for (y = 0; y < cave->h; y++)
3695 cave->map[y][x] = map_element_RND_to_BD(level->field[x][y]);
3698 static void CopyNativeLevel_BD_to_RND(struct LevelInfo *level)
3700 struct LevelInfo_BD *level_bd = level->native_bd_level;
3701 GdCave *cave = level_bd->cave;
3702 int bd_level_nr = level_bd->level_nr;
3705 level->fieldx = MIN(cave->w, MAX_LEV_FIELDX);
3706 level->fieldy = MIN(cave->h, MAX_LEV_FIELDY);
3708 level->time = cave->level_time[bd_level_nr];
3709 level->gems_needed = cave->level_diamonds[bd_level_nr];
3710 level->time_magic_wall = cave->level_magic_wall_time[bd_level_nr];
3712 level->score[SC_TIME_BONUS] = cave->level_timevalue[bd_level_nr];
3713 level->score[SC_DIAMOND] = cave->diamond_value;
3715 strncpy(level->name, cave->name, MAX_LEVEL_NAME_LEN);
3716 level->name[MAX_LEVEL_NAME_LEN] = '\0';
3718 for (x = 0; x < level->fieldx; x++)
3719 for (y = 0; y < level->fieldy; y++)
3720 level->field[x][y] = map_element_BD_to_RND(cave->map[y][x]);
3723 static void setTapeInfoToDefaults(void);
3725 static void CopyNativeTape_BD_to_RND(struct LevelInfo *level)
3727 struct LevelInfo_BD *level_bd = level->native_bd_level;
3728 GdCave *cave = level_bd->cave;
3729 GdReplay *replay = level_bd->replay;
3735 // always start with reliable default values
3736 setTapeInfoToDefaults();
3738 tape.level_nr = level_nr; // (currently not used)
3739 tape.random_seed = replay->seed;
3741 TapeSetDateFromIsoDateString(replay->date);
3744 tape.pos[tape.counter].delay = 0;
3746 tape.bd_replay = TRUE;
3748 // all time calculations only used to display approximate tape time
3749 int cave_speed = cave->speed;
3750 int milliseconds_game = 0;
3751 int milliseconds_elapsed = 20;
3753 for (i = 0; i < replay->movements->len; i++)
3755 int replay_action = replay->movements->data[i];
3756 int tape_action = map_action_BD_to_RND(replay_action);
3757 byte action[MAX_TAPE_ACTIONS] = { tape_action };
3758 boolean success = 0;
3762 success = TapeAddAction(action);
3764 milliseconds_game += milliseconds_elapsed;
3766 if (milliseconds_game >= cave_speed)
3768 milliseconds_game -= cave_speed;
3775 tape.pos[tape.counter].delay = 0;
3776 tape.pos[tape.counter].action[0] = 0;
3780 Warn("BD replay truncated: size exceeds maximum tape size %d", MAX_TAPE_LEN);
3786 TapeHaltRecording();
3790 // ----------------------------------------------------------------------------
3791 // functions for loading EM level
3792 // ----------------------------------------------------------------------------
3794 static void CopyNativeLevel_RND_to_EM(struct LevelInfo *level)
3796 static int ball_xy[8][2] =
3807 struct LevelInfo_EM *level_em = level->native_em_level;
3808 struct CAVE *cav = level_em->cav;
3811 cav->width = MIN(level->fieldx, MAX_PLAYFIELD_WIDTH);
3812 cav->height = MIN(level->fieldy, MAX_PLAYFIELD_HEIGHT);
3814 cav->time_seconds = level->time;
3815 cav->gems_needed = level->gems_needed;
3817 cav->emerald_score = level->score[SC_EMERALD];
3818 cav->diamond_score = level->score[SC_DIAMOND];
3819 cav->alien_score = level->score[SC_ROBOT];
3820 cav->tank_score = level->score[SC_SPACESHIP];
3821 cav->bug_score = level->score[SC_BUG];
3822 cav->eater_score = level->score[SC_YAMYAM];
3823 cav->nut_score = level->score[SC_NUT];
3824 cav->dynamite_score = level->score[SC_DYNAMITE];
3825 cav->key_score = level->score[SC_KEY];
3826 cav->exit_score = level->score[SC_TIME_BONUS];
3828 cav->num_eater_arrays = level->num_yamyam_contents;
3830 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
3831 for (y = 0; y < 3; y++)
3832 for (x = 0; x < 3; x++)
3833 cav->eater_array[i][y * 3 + x] =
3834 map_element_RND_to_EM_cave(level->yamyam_content[i].e[x][y]);
3836 cav->amoeba_time = level->amoeba_speed;
3837 cav->wonderwall_time = level->time_magic_wall;
3838 cav->wheel_time = level->time_wheel;
3840 cav->android_move_time = level->android_move_time;
3841 cav->android_clone_time = level->android_clone_time;
3842 cav->ball_random = level->ball_random;
3843 cav->ball_active = level->ball_active_initial;
3844 cav->ball_time = level->ball_time;
3845 cav->num_ball_arrays = level->num_ball_contents;
3847 cav->lenses_score = level->lenses_score;
3848 cav->magnify_score = level->magnify_score;
3849 cav->slurp_score = level->slurp_score;
3851 cav->lenses_time = level->lenses_time;
3852 cav->magnify_time = level->magnify_time;
3854 cav->wind_time = 9999;
3855 cav->wind_direction =
3856 map_direction_RND_to_EM(level->wind_direction_initial);
3858 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
3859 for (j = 0; j < 8; j++)
3860 cav->ball_array[i][j] =
3861 map_element_RND_to_EM_cave(level->ball_content[i].
3862 e[ball_xy[j][0]][ball_xy[j][1]]);
3864 map_android_clone_elements_RND_to_EM(level);
3866 // first fill the complete playfield with the empty space element
3867 for (y = 0; y < EM_MAX_CAVE_HEIGHT; y++)
3868 for (x = 0; x < EM_MAX_CAVE_WIDTH; x++)
3869 cav->cave[x][y] = Cblank;
3871 // then copy the real level contents from level file into the playfield
3872 for (y = 0; y < cav->height; y++) for (x = 0; x < cav->width; x++)
3874 int new_element = map_element_RND_to_EM_cave(level->field[x][y]);
3876 if (level->field[x][y] == EL_AMOEBA_DEAD)
3877 new_element = map_element_RND_to_EM_cave(EL_AMOEBA_WET);
3879 cav->cave[x][y] = new_element;
3882 for (i = 0; i < MAX_PLAYERS; i++)
3884 cav->player_x[i] = -1;
3885 cav->player_y[i] = -1;
3888 // initialize player positions and delete players from the playfield
3889 for (y = 0; y < cav->height; y++) for (x = 0; x < cav->width; x++)
3891 if (IS_PLAYER_ELEMENT(level->field[x][y]))
3893 int player_nr = GET_PLAYER_NR(level->field[x][y]);
3895 cav->player_x[player_nr] = x;
3896 cav->player_y[player_nr] = y;
3898 cav->cave[x][y] = map_element_RND_to_EM_cave(EL_EMPTY);
3903 static void CopyNativeLevel_EM_to_RND(struct LevelInfo *level)
3905 static int ball_xy[8][2] =
3916 struct LevelInfo_EM *level_em = level->native_em_level;
3917 struct CAVE *cav = level_em->cav;
3920 level->fieldx = MIN(cav->width, MAX_LEV_FIELDX);
3921 level->fieldy = MIN(cav->height, MAX_LEV_FIELDY);
3923 level->time = cav->time_seconds;
3924 level->gems_needed = cav->gems_needed;
3926 sprintf(level->name, "Level %d", level->file_info.nr);
3928 level->score[SC_EMERALD] = cav->emerald_score;
3929 level->score[SC_DIAMOND] = cav->diamond_score;
3930 level->score[SC_ROBOT] = cav->alien_score;
3931 level->score[SC_SPACESHIP] = cav->tank_score;
3932 level->score[SC_BUG] = cav->bug_score;
3933 level->score[SC_YAMYAM] = cav->eater_score;
3934 level->score[SC_NUT] = cav->nut_score;
3935 level->score[SC_DYNAMITE] = cav->dynamite_score;
3936 level->score[SC_KEY] = cav->key_score;
3937 level->score[SC_TIME_BONUS] = cav->exit_score;
3939 level->num_yamyam_contents = cav->num_eater_arrays;
3941 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
3942 for (y = 0; y < 3; y++)
3943 for (x = 0; x < 3; x++)
3944 level->yamyam_content[i].e[x][y] =
3945 map_element_EM_to_RND_cave(cav->eater_array[i][y * 3 + x]);
3947 level->amoeba_speed = cav->amoeba_time;
3948 level->time_magic_wall = cav->wonderwall_time;
3949 level->time_wheel = cav->wheel_time;
3951 level->android_move_time = cav->android_move_time;
3952 level->android_clone_time = cav->android_clone_time;
3953 level->ball_random = cav->ball_random;
3954 level->ball_active_initial = cav->ball_active;
3955 level->ball_time = cav->ball_time;
3956 level->num_ball_contents = cav->num_ball_arrays;
3958 level->lenses_score = cav->lenses_score;
3959 level->magnify_score = cav->magnify_score;
3960 level->slurp_score = cav->slurp_score;
3962 level->lenses_time = cav->lenses_time;
3963 level->magnify_time = cav->magnify_time;
3965 level->wind_direction_initial =
3966 map_direction_EM_to_RND(cav->wind_direction);
3968 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
3969 for (j = 0; j < 8; j++)
3970 level->ball_content[i].e[ball_xy[j][0]][ball_xy[j][1]] =
3971 map_element_EM_to_RND_cave(cav->ball_array[i][j]);
3973 map_android_clone_elements_EM_to_RND(level);
3975 // convert the playfield (some elements need special treatment)
3976 for (y = 0; y < level->fieldy; y++) for (x = 0; x < level->fieldx; x++)
3978 int new_element = map_element_EM_to_RND_cave(cav->cave[x][y]);
3980 if (new_element == EL_AMOEBA_WET && level->amoeba_speed == 0)
3981 new_element = EL_AMOEBA_DEAD;
3983 level->field[x][y] = new_element;
3986 for (i = 0; i < MAX_PLAYERS; i++)
3988 // in case of all players set to the same field, use the first player
3989 int nr = MAX_PLAYERS - i - 1;
3990 int jx = cav->player_x[nr];
3991 int jy = cav->player_y[nr];
3993 if (jx != -1 && jy != -1)
3994 level->field[jx][jy] = EL_PLAYER_1 + nr;
3997 // time score is counted for each 10 seconds left in Emerald Mine levels
3998 level->time_score_base = 10;
4002 // ----------------------------------------------------------------------------
4003 // functions for loading SP level
4004 // ----------------------------------------------------------------------------
4006 static void CopyNativeLevel_RND_to_SP(struct LevelInfo *level)
4008 struct LevelInfo_SP *level_sp = level->native_sp_level;
4009 LevelInfoType *header = &level_sp->header;
4012 level_sp->width = level->fieldx;
4013 level_sp->height = level->fieldy;
4015 for (x = 0; x < level->fieldx; x++)
4016 for (y = 0; y < level->fieldy; y++)
4017 level_sp->playfield[x][y] = map_element_RND_to_SP(level->field[x][y]);
4019 header->InitialGravity = (level->initial_player_gravity[0] ? 1 : 0);
4021 for (i = 0; i < SP_LEVEL_NAME_LEN; i++)
4022 header->LevelTitle[i] = level->name[i];
4023 // !!! NO STRING TERMINATION IN SUPAPLEX VB CODE YET -- FIX THIS !!!
4025 header->InfotronsNeeded = level->gems_needed;
4027 header->SpecialPortCount = 0;
4029 for (x = 0; x < level->fieldx; x++) for (y = 0; y < level->fieldy; y++)
4031 boolean gravity_port_found = FALSE;
4032 boolean gravity_port_valid = FALSE;
4033 int gravity_port_flag;
4034 int gravity_port_base_element;
4035 int element = level->field[x][y];
4037 if (element >= EL_SP_GRAVITY_ON_PORT_RIGHT &&
4038 element <= EL_SP_GRAVITY_ON_PORT_UP)
4040 gravity_port_found = TRUE;
4041 gravity_port_valid = TRUE;
4042 gravity_port_flag = 1;
4043 gravity_port_base_element = EL_SP_GRAVITY_ON_PORT_RIGHT;
4045 else if (element >= EL_SP_GRAVITY_OFF_PORT_RIGHT &&
4046 element <= EL_SP_GRAVITY_OFF_PORT_UP)
4048 gravity_port_found = TRUE;
4049 gravity_port_valid = TRUE;
4050 gravity_port_flag = 0;
4051 gravity_port_base_element = EL_SP_GRAVITY_OFF_PORT_RIGHT;
4053 else if (element >= EL_SP_GRAVITY_PORT_RIGHT &&
4054 element <= EL_SP_GRAVITY_PORT_UP)
4056 // change R'n'D style gravity inverting special port to normal port
4057 // (there are no gravity inverting ports in native Supaplex engine)
4059 gravity_port_found = TRUE;
4060 gravity_port_valid = FALSE;
4061 gravity_port_base_element = EL_SP_GRAVITY_PORT_RIGHT;
4064 if (gravity_port_found)
4066 if (gravity_port_valid &&
4067 header->SpecialPortCount < SP_MAX_SPECIAL_PORTS)
4069 SpecialPortType *port = &header->SpecialPort[header->SpecialPortCount];
4071 port->PortLocation = (y * level->fieldx + x) * 2;
4072 port->Gravity = gravity_port_flag;
4074 element += EL_SP_GRAVITY_PORT_RIGHT - gravity_port_base_element;
4076 header->SpecialPortCount++;
4080 // change special gravity port to normal port
4082 element += EL_SP_PORT_RIGHT - gravity_port_base_element;
4085 level_sp->playfield[x][y] = element - EL_SP_START;
4090 static void CopyNativeLevel_SP_to_RND(struct LevelInfo *level)
4092 struct LevelInfo_SP *level_sp = level->native_sp_level;
4093 LevelInfoType *header = &level_sp->header;
4094 boolean num_invalid_elements = 0;
4097 level->fieldx = level_sp->width;
4098 level->fieldy = level_sp->height;
4100 for (x = 0; x < level->fieldx; x++)
4102 for (y = 0; y < level->fieldy; y++)
4104 int element_old = level_sp->playfield[x][y];
4105 int element_new = getMappedElement(map_element_SP_to_RND(element_old));
4107 if (element_new == EL_UNKNOWN)
4109 num_invalid_elements++;
4111 Debug("level:native:SP", "invalid element %d at position %d, %d",
4115 level->field[x][y] = element_new;
4119 if (num_invalid_elements > 0)
4120 Warn("found %d invalid elements%s", num_invalid_elements,
4121 (!options.debug ? " (use '--debug' for more details)" : ""));
4123 for (i = 0; i < MAX_PLAYERS; i++)
4124 level->initial_player_gravity[i] =
4125 (header->InitialGravity == 1 ? TRUE : FALSE);
4127 // skip leading spaces
4128 for (i = 0; i < SP_LEVEL_NAME_LEN; i++)
4129 if (header->LevelTitle[i] != ' ')
4133 for (j = 0; i < SP_LEVEL_NAME_LEN; i++, j++)
4134 level->name[j] = header->LevelTitle[i];
4135 level->name[j] = '\0';
4137 // cut trailing spaces
4139 if (level->name[j - 1] == ' ' && level->name[j] == '\0')
4140 level->name[j - 1] = '\0';
4142 level->gems_needed = header->InfotronsNeeded;
4144 for (i = 0; i < header->SpecialPortCount; i++)
4146 SpecialPortType *port = &header->SpecialPort[i];
4147 int port_location = port->PortLocation;
4148 int gravity = port->Gravity;
4149 int port_x, port_y, port_element;
4151 port_x = (port_location / 2) % level->fieldx;
4152 port_y = (port_location / 2) / level->fieldx;
4154 if (port_x < 0 || port_x >= level->fieldx ||
4155 port_y < 0 || port_y >= level->fieldy)
4157 Warn("special port position (%d, %d) out of bounds", port_x, port_y);
4162 port_element = level->field[port_x][port_y];
4164 if (port_element < EL_SP_GRAVITY_PORT_RIGHT ||
4165 port_element > EL_SP_GRAVITY_PORT_UP)
4167 Warn("no special port at position (%d, %d)", port_x, port_y);
4172 // change previous (wrong) gravity inverting special port to either
4173 // gravity enabling special port or gravity disabling special port
4174 level->field[port_x][port_y] +=
4175 (gravity == 1 ? EL_SP_GRAVITY_ON_PORT_RIGHT :
4176 EL_SP_GRAVITY_OFF_PORT_RIGHT) - EL_SP_GRAVITY_PORT_RIGHT;
4179 // change special gravity ports without database entries to normal ports
4180 for (x = 0; x < level->fieldx; x++)
4181 for (y = 0; y < level->fieldy; y++)
4182 if (level->field[x][y] >= EL_SP_GRAVITY_PORT_RIGHT &&
4183 level->field[x][y] <= EL_SP_GRAVITY_PORT_UP)
4184 level->field[x][y] += EL_SP_PORT_RIGHT - EL_SP_GRAVITY_PORT_RIGHT;
4186 level->time = 0; // no time limit
4187 level->amoeba_speed = 0;
4188 level->time_magic_wall = 0;
4189 level->time_wheel = 0;
4190 level->amoeba_content = EL_EMPTY;
4192 // original Supaplex does not use score values -- rate by playing time
4193 for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
4194 level->score[i] = 0;
4196 level->rate_time_over_score = TRUE;
4198 // there are no yamyams in supaplex levels
4199 for (i = 0; i < level->num_yamyam_contents; i++)
4200 for (x = 0; x < 3; x++)
4201 for (y = 0; y < 3; y++)
4202 level->yamyam_content[i].e[x][y] = EL_EMPTY;
4205 static void CopyNativeTape_RND_to_SP(struct LevelInfo *level)
4207 struct LevelInfo_SP *level_sp = level->native_sp_level;
4208 struct DemoInfo_SP *demo = &level_sp->demo;
4211 // always start with reliable default values
4212 demo->is_available = FALSE;
4215 if (TAPE_IS_EMPTY(tape))
4218 demo->level_nr = tape.level_nr; // (currently not used)
4220 level_sp->header.DemoRandomSeed = tape.random_seed;
4224 for (i = 0; i < tape.length; i++)
4226 int demo_action = map_key_RND_to_SP(tape.pos[i].action[0]);
4227 int demo_repeat = tape.pos[i].delay;
4228 int demo_entries = (demo_repeat + 15) / 16;
4230 if (demo->length + demo_entries >= SP_MAX_TAPE_LEN)
4232 Warn("tape truncated: size exceeds maximum SP demo size %d",
4238 for (j = 0; j < demo_repeat / 16; j++)
4239 demo->data[demo->length++] = 0xf0 | demo_action;
4241 if (demo_repeat % 16)
4242 demo->data[demo->length++] = ((demo_repeat % 16 - 1) << 4) | demo_action;
4245 demo->is_available = TRUE;
4248 static void CopyNativeTape_SP_to_RND(struct LevelInfo *level)
4250 struct LevelInfo_SP *level_sp = level->native_sp_level;
4251 struct DemoInfo_SP *demo = &level_sp->demo;
4252 char *filename = level->file_info.filename;
4255 // always start with reliable default values
4256 setTapeInfoToDefaults();
4258 if (!demo->is_available)
4261 tape.level_nr = demo->level_nr; // (currently not used)
4262 tape.random_seed = level_sp->header.DemoRandomSeed;
4264 TapeSetDateFromEpochSeconds(getFileTimestampEpochSeconds(filename));
4267 tape.pos[tape.counter].delay = 0;
4269 for (i = 0; i < demo->length; i++)
4271 int demo_action = demo->data[i] & 0x0f;
4272 int demo_repeat = (demo->data[i] & 0xf0) >> 4;
4273 int tape_action = map_key_SP_to_RND(demo_action);
4274 int tape_repeat = demo_repeat + 1;
4275 byte action[MAX_TAPE_ACTIONS] = { tape_action };
4276 boolean success = 0;
4279 for (j = 0; j < tape_repeat; j++)
4280 success = TapeAddAction(action);
4284 Warn("SP demo truncated: size exceeds maximum tape size %d",
4291 TapeHaltRecording();
4295 // ----------------------------------------------------------------------------
4296 // functions for loading MM level
4297 // ----------------------------------------------------------------------------
4299 static void CopyNativeLevel_RND_to_MM(struct LevelInfo *level)
4301 struct LevelInfo_MM *level_mm = level->native_mm_level;
4304 level_mm->fieldx = MIN(level->fieldx, MM_MAX_PLAYFIELD_WIDTH);
4305 level_mm->fieldy = MIN(level->fieldy, MM_MAX_PLAYFIELD_HEIGHT);
4307 level_mm->time = level->time;
4308 level_mm->kettles_needed = level->gems_needed;
4309 level_mm->auto_count_kettles = level->auto_count_gems;
4311 level_mm->mm_laser_red = level->mm_laser_red;
4312 level_mm->mm_laser_green = level->mm_laser_green;
4313 level_mm->mm_laser_blue = level->mm_laser_blue;
4315 level_mm->df_laser_red = level->df_laser_red;
4316 level_mm->df_laser_green = level->df_laser_green;
4317 level_mm->df_laser_blue = level->df_laser_blue;
4319 strcpy(level_mm->name, level->name);
4320 strcpy(level_mm->author, level->author);
4322 level_mm->score[SC_EMERALD] = level->score[SC_EMERALD];
4323 level_mm->score[SC_PACMAN] = level->score[SC_PACMAN];
4324 level_mm->score[SC_KEY] = level->score[SC_KEY];
4325 level_mm->score[SC_TIME_BONUS] = level->score[SC_TIME_BONUS];
4326 level_mm->score[SC_ELEM_BONUS] = level->score[SC_ELEM_BONUS];
4328 level_mm->amoeba_speed = level->amoeba_speed;
4329 level_mm->time_fuse = level->mm_time_fuse;
4330 level_mm->time_bomb = level->mm_time_bomb;
4331 level_mm->time_ball = level->mm_time_ball;
4332 level_mm->time_block = level->mm_time_block;
4334 level_mm->num_ball_contents = level->num_mm_ball_contents;
4335 level_mm->ball_choice_mode = level->mm_ball_choice_mode;
4336 level_mm->rotate_ball_content = level->rotate_mm_ball_content;
4337 level_mm->explode_ball = level->explode_mm_ball;
4339 for (i = 0; i < level->num_mm_ball_contents; i++)
4340 level_mm->ball_content[i] =
4341 map_element_RND_to_MM(level->mm_ball_content[i]);
4343 for (x = 0; x < level->fieldx; x++)
4344 for (y = 0; y < level->fieldy; y++)
4346 level_mm->field[x][y] = map_element_RND_to_MM(level->field[x][y]);
4349 static void CopyNativeLevel_MM_to_RND(struct LevelInfo *level)
4351 struct LevelInfo_MM *level_mm = level->native_mm_level;
4354 level->fieldx = MIN(level_mm->fieldx, MAX_LEV_FIELDX);
4355 level->fieldy = MIN(level_mm->fieldy, MAX_LEV_FIELDY);
4357 level->time = level_mm->time;
4358 level->gems_needed = level_mm->kettles_needed;
4359 level->auto_count_gems = level_mm->auto_count_kettles;
4361 level->mm_laser_red = level_mm->mm_laser_red;
4362 level->mm_laser_green = level_mm->mm_laser_green;
4363 level->mm_laser_blue = level_mm->mm_laser_blue;
4365 level->df_laser_red = level_mm->df_laser_red;
4366 level->df_laser_green = level_mm->df_laser_green;
4367 level->df_laser_blue = level_mm->df_laser_blue;
4369 strcpy(level->name, level_mm->name);
4371 // only overwrite author from 'levelinfo.conf' if author defined in level
4372 if (!strEqual(level_mm->author, ANONYMOUS_NAME))
4373 strcpy(level->author, level_mm->author);
4375 level->score[SC_EMERALD] = level_mm->score[SC_EMERALD];
4376 level->score[SC_PACMAN] = level_mm->score[SC_PACMAN];
4377 level->score[SC_KEY] = level_mm->score[SC_KEY];
4378 level->score[SC_TIME_BONUS] = level_mm->score[SC_TIME_BONUS];
4379 level->score[SC_ELEM_BONUS] = level_mm->score[SC_ELEM_BONUS];
4381 level->amoeba_speed = level_mm->amoeba_speed;
4382 level->mm_time_fuse = level_mm->time_fuse;
4383 level->mm_time_bomb = level_mm->time_bomb;
4384 level->mm_time_ball = level_mm->time_ball;
4385 level->mm_time_block = level_mm->time_block;
4387 level->num_mm_ball_contents = level_mm->num_ball_contents;
4388 level->mm_ball_choice_mode = level_mm->ball_choice_mode;
4389 level->rotate_mm_ball_content = level_mm->rotate_ball_content;
4390 level->explode_mm_ball = level_mm->explode_ball;
4392 for (i = 0; i < level->num_mm_ball_contents; i++)
4393 level->mm_ball_content[i] =
4394 map_element_MM_to_RND(level_mm->ball_content[i]);
4396 for (x = 0; x < level->fieldx; x++)
4397 for (y = 0; y < level->fieldy; y++)
4398 level->field[x][y] = map_element_MM_to_RND(level_mm->field[x][y]);
4402 // ----------------------------------------------------------------------------
4403 // functions for loading DC level
4404 // ----------------------------------------------------------------------------
4406 #define DC_LEVEL_HEADER_SIZE 344
4408 static unsigned short getDecodedWord_DC(unsigned short data_encoded,
4411 static int last_data_encoded;
4415 int diff_hi, diff_lo;
4416 int data_hi, data_lo;
4417 unsigned short data_decoded;
4421 last_data_encoded = 0;
4428 diff = data_encoded - last_data_encoded;
4429 diff_hi = diff & ~0xff;
4430 diff_lo = diff & 0xff;
4434 data_hi = diff_hi - (offset1 << 8) + (offset2 & 0xff00);
4435 data_lo = (diff_lo + (data_hi >> 16)) & 0x00ff;
4436 data_hi = data_hi & 0xff00;
4438 data_decoded = data_hi | data_lo;
4440 last_data_encoded = data_encoded;
4442 offset1 = (offset1 + 1) % 31;
4443 offset2 = offset2 & 0xff;
4445 return data_decoded;
4448 static int getMappedElement_DC(int element)
4456 // 0x0117 - 0x036e: (?)
4459 // 0x042d - 0x0684: (?)
4475 element = EL_CRYSTAL;
4478 case 0x0e77: // quicksand (boulder)
4479 element = EL_QUICKSAND_FAST_FULL;
4482 case 0x0e99: // slow quicksand (boulder)
4483 element = EL_QUICKSAND_FULL;
4487 element = EL_EM_EXIT_OPEN;
4491 element = EL_EM_EXIT_CLOSED;
4495 element = EL_EM_STEEL_EXIT_OPEN;
4499 element = EL_EM_STEEL_EXIT_CLOSED;
4502 case 0x0f4f: // dynamite (lit 1)
4503 element = EL_EM_DYNAMITE_ACTIVE;
4506 case 0x0f57: // dynamite (lit 2)
4507 element = EL_EM_DYNAMITE_ACTIVE;
4510 case 0x0f5f: // dynamite (lit 3)
4511 element = EL_EM_DYNAMITE_ACTIVE;
4514 case 0x0f67: // dynamite (lit 4)
4515 element = EL_EM_DYNAMITE_ACTIVE;
4522 element = EL_AMOEBA_WET;
4526 element = EL_AMOEBA_DROP;
4530 element = EL_DC_MAGIC_WALL;
4534 element = EL_SPACESHIP_UP;
4538 element = EL_SPACESHIP_DOWN;
4542 element = EL_SPACESHIP_LEFT;
4546 element = EL_SPACESHIP_RIGHT;
4550 element = EL_BUG_UP;
4554 element = EL_BUG_DOWN;
4558 element = EL_BUG_LEFT;
4562 element = EL_BUG_RIGHT;
4566 element = EL_MOLE_UP;
4570 element = EL_MOLE_DOWN;
4574 element = EL_MOLE_LEFT;
4578 element = EL_MOLE_RIGHT;
4586 element = EL_YAMYAM_UP;
4590 element = EL_SWITCHGATE_OPEN;
4594 element = EL_SWITCHGATE_CLOSED;
4598 element = EL_DC_SWITCHGATE_SWITCH_UP;
4602 element = EL_TIMEGATE_CLOSED;
4605 case 0x144c: // conveyor belt switch (green)
4606 element = EL_CONVEYOR_BELT_3_SWITCH_MIDDLE;
4609 case 0x144f: // conveyor belt switch (red)
4610 element = EL_CONVEYOR_BELT_1_SWITCH_MIDDLE;
4613 case 0x1452: // conveyor belt switch (blue)
4614 element = EL_CONVEYOR_BELT_4_SWITCH_MIDDLE;
4618 element = EL_CONVEYOR_BELT_3_MIDDLE;
4622 element = EL_CONVEYOR_BELT_3_LEFT;
4626 element = EL_CONVEYOR_BELT_3_RIGHT;
4630 element = EL_CONVEYOR_BELT_1_MIDDLE;
4634 element = EL_CONVEYOR_BELT_1_LEFT;
4638 element = EL_CONVEYOR_BELT_1_RIGHT;
4642 element = EL_CONVEYOR_BELT_4_MIDDLE;
4646 element = EL_CONVEYOR_BELT_4_LEFT;
4650 element = EL_CONVEYOR_BELT_4_RIGHT;
4654 element = EL_EXPANDABLE_WALL_HORIZONTAL;
4658 element = EL_EXPANDABLE_WALL_VERTICAL;
4662 element = EL_EXPANDABLE_WALL_ANY;
4665 case 0x14ce: // growing steel wall (left/right)
4666 element = EL_EXPANDABLE_STEELWALL_HORIZONTAL;
4669 case 0x14df: // growing steel wall (up/down)
4670 element = EL_EXPANDABLE_STEELWALL_VERTICAL;
4673 case 0x14e8: // growing steel wall (up/down/left/right)
4674 element = EL_EXPANDABLE_STEELWALL_ANY;
4678 element = EL_SHIELD_DEADLY;
4682 element = EL_EXTRA_TIME;
4690 element = EL_EMPTY_SPACE;
4693 case 0x1578: // quicksand (empty)
4694 element = EL_QUICKSAND_FAST_EMPTY;
4697 case 0x1579: // slow quicksand (empty)
4698 element = EL_QUICKSAND_EMPTY;
4708 element = EL_EM_DYNAMITE;
4711 case 0x15a1: // key (red)
4712 element = EL_EM_KEY_1;
4715 case 0x15a2: // key (yellow)
4716 element = EL_EM_KEY_2;
4719 case 0x15a3: // key (blue)
4720 element = EL_EM_KEY_4;
4723 case 0x15a4: // key (green)
4724 element = EL_EM_KEY_3;
4727 case 0x15a5: // key (white)
4728 element = EL_DC_KEY_WHITE;
4732 element = EL_WALL_SLIPPERY;
4739 case 0x15a8: // wall (not round)
4743 case 0x15a9: // (blue)
4744 element = EL_CHAR_A;
4747 case 0x15aa: // (blue)
4748 element = EL_CHAR_B;
4751 case 0x15ab: // (blue)
4752 element = EL_CHAR_C;
4755 case 0x15ac: // (blue)
4756 element = EL_CHAR_D;
4759 case 0x15ad: // (blue)
4760 element = EL_CHAR_E;
4763 case 0x15ae: // (blue)
4764 element = EL_CHAR_F;
4767 case 0x15af: // (blue)
4768 element = EL_CHAR_G;
4771 case 0x15b0: // (blue)
4772 element = EL_CHAR_H;
4775 case 0x15b1: // (blue)
4776 element = EL_CHAR_I;
4779 case 0x15b2: // (blue)
4780 element = EL_CHAR_J;
4783 case 0x15b3: // (blue)
4784 element = EL_CHAR_K;
4787 case 0x15b4: // (blue)
4788 element = EL_CHAR_L;
4791 case 0x15b5: // (blue)
4792 element = EL_CHAR_M;
4795 case 0x15b6: // (blue)
4796 element = EL_CHAR_N;
4799 case 0x15b7: // (blue)
4800 element = EL_CHAR_O;
4803 case 0x15b8: // (blue)
4804 element = EL_CHAR_P;
4807 case 0x15b9: // (blue)
4808 element = EL_CHAR_Q;
4811 case 0x15ba: // (blue)
4812 element = EL_CHAR_R;
4815 case 0x15bb: // (blue)
4816 element = EL_CHAR_S;
4819 case 0x15bc: // (blue)
4820 element = EL_CHAR_T;
4823 case 0x15bd: // (blue)
4824 element = EL_CHAR_U;
4827 case 0x15be: // (blue)
4828 element = EL_CHAR_V;
4831 case 0x15bf: // (blue)
4832 element = EL_CHAR_W;
4835 case 0x15c0: // (blue)
4836 element = EL_CHAR_X;
4839 case 0x15c1: // (blue)
4840 element = EL_CHAR_Y;
4843 case 0x15c2: // (blue)
4844 element = EL_CHAR_Z;
4847 case 0x15c3: // (blue)
4848 element = EL_CHAR_AUMLAUT;
4851 case 0x15c4: // (blue)
4852 element = EL_CHAR_OUMLAUT;
4855 case 0x15c5: // (blue)
4856 element = EL_CHAR_UUMLAUT;
4859 case 0x15c6: // (blue)
4860 element = EL_CHAR_0;
4863 case 0x15c7: // (blue)
4864 element = EL_CHAR_1;
4867 case 0x15c8: // (blue)
4868 element = EL_CHAR_2;
4871 case 0x15c9: // (blue)
4872 element = EL_CHAR_3;
4875 case 0x15ca: // (blue)
4876 element = EL_CHAR_4;
4879 case 0x15cb: // (blue)
4880 element = EL_CHAR_5;
4883 case 0x15cc: // (blue)
4884 element = EL_CHAR_6;
4887 case 0x15cd: // (blue)
4888 element = EL_CHAR_7;
4891 case 0x15ce: // (blue)
4892 element = EL_CHAR_8;
4895 case 0x15cf: // (blue)
4896 element = EL_CHAR_9;
4899 case 0x15d0: // (blue)
4900 element = EL_CHAR_PERIOD;
4903 case 0x15d1: // (blue)
4904 element = EL_CHAR_EXCLAM;
4907 case 0x15d2: // (blue)
4908 element = EL_CHAR_COLON;
4911 case 0x15d3: // (blue)
4912 element = EL_CHAR_LESS;
4915 case 0x15d4: // (blue)
4916 element = EL_CHAR_GREATER;
4919 case 0x15d5: // (blue)
4920 element = EL_CHAR_QUESTION;
4923 case 0x15d6: // (blue)
4924 element = EL_CHAR_COPYRIGHT;
4927 case 0x15d7: // (blue)
4928 element = EL_CHAR_UP;
4931 case 0x15d8: // (blue)
4932 element = EL_CHAR_DOWN;
4935 case 0x15d9: // (blue)
4936 element = EL_CHAR_BUTTON;
4939 case 0x15da: // (blue)
4940 element = EL_CHAR_PLUS;
4943 case 0x15db: // (blue)
4944 element = EL_CHAR_MINUS;
4947 case 0x15dc: // (blue)
4948 element = EL_CHAR_APOSTROPHE;
4951 case 0x15dd: // (blue)
4952 element = EL_CHAR_PARENLEFT;
4955 case 0x15de: // (blue)
4956 element = EL_CHAR_PARENRIGHT;
4959 case 0x15df: // (green)
4960 element = EL_CHAR_A;
4963 case 0x15e0: // (green)
4964 element = EL_CHAR_B;
4967 case 0x15e1: // (green)
4968 element = EL_CHAR_C;
4971 case 0x15e2: // (green)
4972 element = EL_CHAR_D;
4975 case 0x15e3: // (green)
4976 element = EL_CHAR_E;
4979 case 0x15e4: // (green)
4980 element = EL_CHAR_F;
4983 case 0x15e5: // (green)
4984 element = EL_CHAR_G;
4987 case 0x15e6: // (green)
4988 element = EL_CHAR_H;
4991 case 0x15e7: // (green)
4992 element = EL_CHAR_I;
4995 case 0x15e8: // (green)
4996 element = EL_CHAR_J;
4999 case 0x15e9: // (green)
5000 element = EL_CHAR_K;
5003 case 0x15ea: // (green)
5004 element = EL_CHAR_L;
5007 case 0x15eb: // (green)
5008 element = EL_CHAR_M;
5011 case 0x15ec: // (green)
5012 element = EL_CHAR_N;
5015 case 0x15ed: // (green)
5016 element = EL_CHAR_O;
5019 case 0x15ee: // (green)
5020 element = EL_CHAR_P;
5023 case 0x15ef: // (green)
5024 element = EL_CHAR_Q;
5027 case 0x15f0: // (green)
5028 element = EL_CHAR_R;
5031 case 0x15f1: // (green)
5032 element = EL_CHAR_S;
5035 case 0x15f2: // (green)
5036 element = EL_CHAR_T;
5039 case 0x15f3: // (green)
5040 element = EL_CHAR_U;
5043 case 0x15f4: // (green)
5044 element = EL_CHAR_V;
5047 case 0x15f5: // (green)
5048 element = EL_CHAR_W;
5051 case 0x15f6: // (green)
5052 element = EL_CHAR_X;
5055 case 0x15f7: // (green)
5056 element = EL_CHAR_Y;
5059 case 0x15f8: // (green)
5060 element = EL_CHAR_Z;
5063 case 0x15f9: // (green)
5064 element = EL_CHAR_AUMLAUT;
5067 case 0x15fa: // (green)
5068 element = EL_CHAR_OUMLAUT;
5071 case 0x15fb: // (green)
5072 element = EL_CHAR_UUMLAUT;
5075 case 0x15fc: // (green)
5076 element = EL_CHAR_0;
5079 case 0x15fd: // (green)
5080 element = EL_CHAR_1;
5083 case 0x15fe: // (green)
5084 element = EL_CHAR_2;
5087 case 0x15ff: // (green)
5088 element = EL_CHAR_3;
5091 case 0x1600: // (green)
5092 element = EL_CHAR_4;
5095 case 0x1601: // (green)
5096 element = EL_CHAR_5;
5099 case 0x1602: // (green)
5100 element = EL_CHAR_6;
5103 case 0x1603: // (green)
5104 element = EL_CHAR_7;
5107 case 0x1604: // (green)
5108 element = EL_CHAR_8;
5111 case 0x1605: // (green)
5112 element = EL_CHAR_9;
5115 case 0x1606: // (green)
5116 element = EL_CHAR_PERIOD;
5119 case 0x1607: // (green)
5120 element = EL_CHAR_EXCLAM;
5123 case 0x1608: // (green)
5124 element = EL_CHAR_COLON;
5127 case 0x1609: // (green)
5128 element = EL_CHAR_LESS;
5131 case 0x160a: // (green)
5132 element = EL_CHAR_GREATER;
5135 case 0x160b: // (green)
5136 element = EL_CHAR_QUESTION;
5139 case 0x160c: // (green)
5140 element = EL_CHAR_COPYRIGHT;
5143 case 0x160d: // (green)
5144 element = EL_CHAR_UP;
5147 case 0x160e: // (green)
5148 element = EL_CHAR_DOWN;
5151 case 0x160f: // (green)
5152 element = EL_CHAR_BUTTON;
5155 case 0x1610: // (green)
5156 element = EL_CHAR_PLUS;
5159 case 0x1611: // (green)
5160 element = EL_CHAR_MINUS;
5163 case 0x1612: // (green)
5164 element = EL_CHAR_APOSTROPHE;
5167 case 0x1613: // (green)
5168 element = EL_CHAR_PARENLEFT;
5171 case 0x1614: // (green)
5172 element = EL_CHAR_PARENRIGHT;
5175 case 0x1615: // (blue steel)
5176 element = EL_STEEL_CHAR_A;
5179 case 0x1616: // (blue steel)
5180 element = EL_STEEL_CHAR_B;
5183 case 0x1617: // (blue steel)
5184 element = EL_STEEL_CHAR_C;
5187 case 0x1618: // (blue steel)
5188 element = EL_STEEL_CHAR_D;
5191 case 0x1619: // (blue steel)
5192 element = EL_STEEL_CHAR_E;
5195 case 0x161a: // (blue steel)
5196 element = EL_STEEL_CHAR_F;
5199 case 0x161b: // (blue steel)
5200 element = EL_STEEL_CHAR_G;
5203 case 0x161c: // (blue steel)
5204 element = EL_STEEL_CHAR_H;
5207 case 0x161d: // (blue steel)
5208 element = EL_STEEL_CHAR_I;
5211 case 0x161e: // (blue steel)
5212 element = EL_STEEL_CHAR_J;
5215 case 0x161f: // (blue steel)
5216 element = EL_STEEL_CHAR_K;
5219 case 0x1620: // (blue steel)
5220 element = EL_STEEL_CHAR_L;
5223 case 0x1621: // (blue steel)
5224 element = EL_STEEL_CHAR_M;
5227 case 0x1622: // (blue steel)
5228 element = EL_STEEL_CHAR_N;
5231 case 0x1623: // (blue steel)
5232 element = EL_STEEL_CHAR_O;
5235 case 0x1624: // (blue steel)
5236 element = EL_STEEL_CHAR_P;
5239 case 0x1625: // (blue steel)
5240 element = EL_STEEL_CHAR_Q;
5243 case 0x1626: // (blue steel)
5244 element = EL_STEEL_CHAR_R;
5247 case 0x1627: // (blue steel)
5248 element = EL_STEEL_CHAR_S;
5251 case 0x1628: // (blue steel)
5252 element = EL_STEEL_CHAR_T;
5255 case 0x1629: // (blue steel)
5256 element = EL_STEEL_CHAR_U;
5259 case 0x162a: // (blue steel)
5260 element = EL_STEEL_CHAR_V;
5263 case 0x162b: // (blue steel)
5264 element = EL_STEEL_CHAR_W;
5267 case 0x162c: // (blue steel)
5268 element = EL_STEEL_CHAR_X;
5271 case 0x162d: // (blue steel)
5272 element = EL_STEEL_CHAR_Y;
5275 case 0x162e: // (blue steel)
5276 element = EL_STEEL_CHAR_Z;
5279 case 0x162f: // (blue steel)
5280 element = EL_STEEL_CHAR_AUMLAUT;
5283 case 0x1630: // (blue steel)
5284 element = EL_STEEL_CHAR_OUMLAUT;
5287 case 0x1631: // (blue steel)
5288 element = EL_STEEL_CHAR_UUMLAUT;
5291 case 0x1632: // (blue steel)
5292 element = EL_STEEL_CHAR_0;
5295 case 0x1633: // (blue steel)
5296 element = EL_STEEL_CHAR_1;
5299 case 0x1634: // (blue steel)
5300 element = EL_STEEL_CHAR_2;
5303 case 0x1635: // (blue steel)
5304 element = EL_STEEL_CHAR_3;
5307 case 0x1636: // (blue steel)
5308 element = EL_STEEL_CHAR_4;
5311 case 0x1637: // (blue steel)
5312 element = EL_STEEL_CHAR_5;
5315 case 0x1638: // (blue steel)
5316 element = EL_STEEL_CHAR_6;
5319 case 0x1639: // (blue steel)
5320 element = EL_STEEL_CHAR_7;
5323 case 0x163a: // (blue steel)
5324 element = EL_STEEL_CHAR_8;
5327 case 0x163b: // (blue steel)
5328 element = EL_STEEL_CHAR_9;
5331 case 0x163c: // (blue steel)
5332 element = EL_STEEL_CHAR_PERIOD;
5335 case 0x163d: // (blue steel)
5336 element = EL_STEEL_CHAR_EXCLAM;
5339 case 0x163e: // (blue steel)
5340 element = EL_STEEL_CHAR_COLON;
5343 case 0x163f: // (blue steel)
5344 element = EL_STEEL_CHAR_LESS;
5347 case 0x1640: // (blue steel)
5348 element = EL_STEEL_CHAR_GREATER;
5351 case 0x1641: // (blue steel)
5352 element = EL_STEEL_CHAR_QUESTION;
5355 case 0x1642: // (blue steel)
5356 element = EL_STEEL_CHAR_COPYRIGHT;
5359 case 0x1643: // (blue steel)
5360 element = EL_STEEL_CHAR_UP;
5363 case 0x1644: // (blue steel)
5364 element = EL_STEEL_CHAR_DOWN;
5367 case 0x1645: // (blue steel)
5368 element = EL_STEEL_CHAR_BUTTON;
5371 case 0x1646: // (blue steel)
5372 element = EL_STEEL_CHAR_PLUS;
5375 case 0x1647: // (blue steel)
5376 element = EL_STEEL_CHAR_MINUS;
5379 case 0x1648: // (blue steel)
5380 element = EL_STEEL_CHAR_APOSTROPHE;
5383 case 0x1649: // (blue steel)
5384 element = EL_STEEL_CHAR_PARENLEFT;
5387 case 0x164a: // (blue steel)
5388 element = EL_STEEL_CHAR_PARENRIGHT;
5391 case 0x164b: // (green steel)
5392 element = EL_STEEL_CHAR_A;
5395 case 0x164c: // (green steel)
5396 element = EL_STEEL_CHAR_B;
5399 case 0x164d: // (green steel)
5400 element = EL_STEEL_CHAR_C;
5403 case 0x164e: // (green steel)
5404 element = EL_STEEL_CHAR_D;
5407 case 0x164f: // (green steel)
5408 element = EL_STEEL_CHAR_E;
5411 case 0x1650: // (green steel)
5412 element = EL_STEEL_CHAR_F;
5415 case 0x1651: // (green steel)
5416 element = EL_STEEL_CHAR_G;
5419 case 0x1652: // (green steel)
5420 element = EL_STEEL_CHAR_H;
5423 case 0x1653: // (green steel)
5424 element = EL_STEEL_CHAR_I;
5427 case 0x1654: // (green steel)
5428 element = EL_STEEL_CHAR_J;
5431 case 0x1655: // (green steel)
5432 element = EL_STEEL_CHAR_K;
5435 case 0x1656: // (green steel)
5436 element = EL_STEEL_CHAR_L;
5439 case 0x1657: // (green steel)
5440 element = EL_STEEL_CHAR_M;
5443 case 0x1658: // (green steel)
5444 element = EL_STEEL_CHAR_N;
5447 case 0x1659: // (green steel)
5448 element = EL_STEEL_CHAR_O;
5451 case 0x165a: // (green steel)
5452 element = EL_STEEL_CHAR_P;
5455 case 0x165b: // (green steel)
5456 element = EL_STEEL_CHAR_Q;
5459 case 0x165c: // (green steel)
5460 element = EL_STEEL_CHAR_R;
5463 case 0x165d: // (green steel)
5464 element = EL_STEEL_CHAR_S;
5467 case 0x165e: // (green steel)
5468 element = EL_STEEL_CHAR_T;
5471 case 0x165f: // (green steel)
5472 element = EL_STEEL_CHAR_U;
5475 case 0x1660: // (green steel)
5476 element = EL_STEEL_CHAR_V;
5479 case 0x1661: // (green steel)
5480 element = EL_STEEL_CHAR_W;
5483 case 0x1662: // (green steel)
5484 element = EL_STEEL_CHAR_X;
5487 case 0x1663: // (green steel)
5488 element = EL_STEEL_CHAR_Y;
5491 case 0x1664: // (green steel)
5492 element = EL_STEEL_CHAR_Z;
5495 case 0x1665: // (green steel)
5496 element = EL_STEEL_CHAR_AUMLAUT;
5499 case 0x1666: // (green steel)
5500 element = EL_STEEL_CHAR_OUMLAUT;
5503 case 0x1667: // (green steel)
5504 element = EL_STEEL_CHAR_UUMLAUT;
5507 case 0x1668: // (green steel)
5508 element = EL_STEEL_CHAR_0;
5511 case 0x1669: // (green steel)
5512 element = EL_STEEL_CHAR_1;
5515 case 0x166a: // (green steel)
5516 element = EL_STEEL_CHAR_2;
5519 case 0x166b: // (green steel)
5520 element = EL_STEEL_CHAR_3;
5523 case 0x166c: // (green steel)
5524 element = EL_STEEL_CHAR_4;
5527 case 0x166d: // (green steel)
5528 element = EL_STEEL_CHAR_5;
5531 case 0x166e: // (green steel)
5532 element = EL_STEEL_CHAR_6;
5535 case 0x166f: // (green steel)
5536 element = EL_STEEL_CHAR_7;
5539 case 0x1670: // (green steel)
5540 element = EL_STEEL_CHAR_8;
5543 case 0x1671: // (green steel)
5544 element = EL_STEEL_CHAR_9;
5547 case 0x1672: // (green steel)
5548 element = EL_STEEL_CHAR_PERIOD;
5551 case 0x1673: // (green steel)
5552 element = EL_STEEL_CHAR_EXCLAM;
5555 case 0x1674: // (green steel)
5556 element = EL_STEEL_CHAR_COLON;
5559 case 0x1675: // (green steel)
5560 element = EL_STEEL_CHAR_LESS;
5563 case 0x1676: // (green steel)
5564 element = EL_STEEL_CHAR_GREATER;
5567 case 0x1677: // (green steel)
5568 element = EL_STEEL_CHAR_QUESTION;
5571 case 0x1678: // (green steel)
5572 element = EL_STEEL_CHAR_COPYRIGHT;
5575 case 0x1679: // (green steel)
5576 element = EL_STEEL_CHAR_UP;
5579 case 0x167a: // (green steel)
5580 element = EL_STEEL_CHAR_DOWN;
5583 case 0x167b: // (green steel)
5584 element = EL_STEEL_CHAR_BUTTON;
5587 case 0x167c: // (green steel)
5588 element = EL_STEEL_CHAR_PLUS;
5591 case 0x167d: // (green steel)
5592 element = EL_STEEL_CHAR_MINUS;
5595 case 0x167e: // (green steel)
5596 element = EL_STEEL_CHAR_APOSTROPHE;
5599 case 0x167f: // (green steel)
5600 element = EL_STEEL_CHAR_PARENLEFT;
5603 case 0x1680: // (green steel)
5604 element = EL_STEEL_CHAR_PARENRIGHT;
5607 case 0x1681: // gate (red)
5608 element = EL_EM_GATE_1;
5611 case 0x1682: // secret gate (red)
5612 element = EL_EM_GATE_1_GRAY;
5615 case 0x1683: // gate (yellow)
5616 element = EL_EM_GATE_2;
5619 case 0x1684: // secret gate (yellow)
5620 element = EL_EM_GATE_2_GRAY;
5623 case 0x1685: // gate (blue)
5624 element = EL_EM_GATE_4;
5627 case 0x1686: // secret gate (blue)
5628 element = EL_EM_GATE_4_GRAY;
5631 case 0x1687: // gate (green)
5632 element = EL_EM_GATE_3;
5635 case 0x1688: // secret gate (green)
5636 element = EL_EM_GATE_3_GRAY;
5639 case 0x1689: // gate (white)
5640 element = EL_DC_GATE_WHITE;
5643 case 0x168a: // secret gate (white)
5644 element = EL_DC_GATE_WHITE_GRAY;
5647 case 0x168b: // secret gate (no key)
5648 element = EL_DC_GATE_FAKE_GRAY;
5652 element = EL_ROBOT_WHEEL;
5656 element = EL_DC_TIMEGATE_SWITCH;
5660 element = EL_ACID_POOL_BOTTOM;
5664 element = EL_ACID_POOL_TOPLEFT;
5668 element = EL_ACID_POOL_TOPRIGHT;
5672 element = EL_ACID_POOL_BOTTOMLEFT;
5676 element = EL_ACID_POOL_BOTTOMRIGHT;
5680 element = EL_STEELWALL;
5684 element = EL_STEELWALL_SLIPPERY;
5687 case 0x1695: // steel wall (not round)
5688 element = EL_STEELWALL;
5691 case 0x1696: // steel wall (left)
5692 element = EL_DC_STEELWALL_1_LEFT;
5695 case 0x1697: // steel wall (bottom)
5696 element = EL_DC_STEELWALL_1_BOTTOM;
5699 case 0x1698: // steel wall (right)
5700 element = EL_DC_STEELWALL_1_RIGHT;
5703 case 0x1699: // steel wall (top)
5704 element = EL_DC_STEELWALL_1_TOP;
5707 case 0x169a: // steel wall (left/bottom)
5708 element = EL_DC_STEELWALL_1_BOTTOMLEFT;
5711 case 0x169b: // steel wall (right/bottom)
5712 element = EL_DC_STEELWALL_1_BOTTOMRIGHT;
5715 case 0x169c: // steel wall (right/top)
5716 element = EL_DC_STEELWALL_1_TOPRIGHT;
5719 case 0x169d: // steel wall (left/top)
5720 element = EL_DC_STEELWALL_1_TOPLEFT;
5723 case 0x169e: // steel wall (right/bottom small)
5724 element = EL_DC_STEELWALL_1_BOTTOMRIGHT_2;
5727 case 0x169f: // steel wall (left/bottom small)
5728 element = EL_DC_STEELWALL_1_BOTTOMLEFT_2;
5731 case 0x16a0: // steel wall (right/top small)
5732 element = EL_DC_STEELWALL_1_TOPRIGHT_2;
5735 case 0x16a1: // steel wall (left/top small)
5736 element = EL_DC_STEELWALL_1_TOPLEFT_2;
5739 case 0x16a2: // steel wall (left/right)
5740 element = EL_DC_STEELWALL_1_VERTICAL;
5743 case 0x16a3: // steel wall (top/bottom)
5744 element = EL_DC_STEELWALL_1_HORIZONTAL;
5747 case 0x16a4: // steel wall 2 (left end)
5748 element = EL_DC_STEELWALL_2_LEFT;
5751 case 0x16a5: // steel wall 2 (right end)
5752 element = EL_DC_STEELWALL_2_RIGHT;
5755 case 0x16a6: // steel wall 2 (top end)
5756 element = EL_DC_STEELWALL_2_TOP;
5759 case 0x16a7: // steel wall 2 (bottom end)
5760 element = EL_DC_STEELWALL_2_BOTTOM;
5763 case 0x16a8: // steel wall 2 (left/right)
5764 element = EL_DC_STEELWALL_2_HORIZONTAL;
5767 case 0x16a9: // steel wall 2 (up/down)
5768 element = EL_DC_STEELWALL_2_VERTICAL;
5771 case 0x16aa: // steel wall 2 (mid)
5772 element = EL_DC_STEELWALL_2_MIDDLE;
5776 element = EL_SIGN_EXCLAMATION;
5780 element = EL_SIGN_RADIOACTIVITY;
5784 element = EL_SIGN_STOP;
5788 element = EL_SIGN_WHEELCHAIR;
5792 element = EL_SIGN_PARKING;
5796 element = EL_SIGN_NO_ENTRY;
5800 element = EL_SIGN_HEART;
5804 element = EL_SIGN_GIVE_WAY;
5808 element = EL_SIGN_ENTRY_FORBIDDEN;
5812 element = EL_SIGN_EMERGENCY_EXIT;
5816 element = EL_SIGN_YIN_YANG;
5820 element = EL_WALL_EMERALD;
5824 element = EL_WALL_DIAMOND;
5828 element = EL_WALL_PEARL;
5832 element = EL_WALL_CRYSTAL;
5836 element = EL_INVISIBLE_WALL;
5840 element = EL_INVISIBLE_STEELWALL;
5844 // EL_INVISIBLE_SAND
5847 element = EL_LIGHT_SWITCH;
5851 element = EL_ENVELOPE_1;
5855 if (element >= 0x0117 && element <= 0x036e) // (?)
5856 element = EL_DIAMOND;
5857 else if (element >= 0x042d && element <= 0x0684) // (?)
5858 element = EL_EMERALD;
5859 else if (element >= 0x157c && element <= 0x158b)
5861 else if (element >= 0x1590 && element <= 0x159f)
5862 element = EL_DC_LANDMINE;
5863 else if (element >= 0x16bc && element <= 0x16cb)
5864 element = EL_INVISIBLE_SAND;
5867 Warn("unknown Diamond Caves element 0x%04x", element);
5869 element = EL_UNKNOWN;
5874 return getMappedElement(element);
5877 static void LoadLevelFromFileStream_DC(File *file, struct LevelInfo *level)
5879 byte header[DC_LEVEL_HEADER_SIZE];
5881 int envelope_header_pos = 62;
5882 int envelope_content_pos = 94;
5883 int level_name_pos = 251;
5884 int level_author_pos = 292;
5885 int envelope_header_len;
5886 int envelope_content_len;
5888 int level_author_len;
5890 int num_yamyam_contents;
5893 getDecodedWord_DC(0, TRUE); // initialize DC2 decoding engine
5895 for (i = 0; i < DC_LEVEL_HEADER_SIZE / 2; i++)
5897 unsigned short header_word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
5899 header[i * 2 + 0] = header_word >> 8;
5900 header[i * 2 + 1] = header_word & 0xff;
5903 // read some values from level header to check level decoding integrity
5904 fieldx = header[6] | (header[7] << 8);
5905 fieldy = header[8] | (header[9] << 8);
5906 num_yamyam_contents = header[60] | (header[61] << 8);
5908 // do some simple sanity checks to ensure that level was correctly decoded
5909 if (fieldx < 1 || fieldx > 256 ||
5910 fieldy < 1 || fieldy > 256 ||
5911 num_yamyam_contents < 1 || num_yamyam_contents > 8)
5913 level->no_valid_file = TRUE;
5915 Warn("cannot decode level from stream -- using empty level");
5920 // maximum envelope header size is 31 bytes
5921 envelope_header_len = header[envelope_header_pos];
5922 // maximum envelope content size is 110 (156?) bytes
5923 envelope_content_len = header[envelope_content_pos];
5925 // maximum level title size is 40 bytes
5926 level_name_len = MIN(header[level_name_pos], MAX_LEVEL_NAME_LEN);
5927 // maximum level author size is 30 (51?) bytes
5928 level_author_len = MIN(header[level_author_pos], MAX_LEVEL_AUTHOR_LEN);
5932 for (i = 0; i < envelope_header_len; i++)
5933 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
5934 level->envelope[0].text[envelope_size++] =
5935 header[envelope_header_pos + 1 + i];
5937 if (envelope_header_len > 0 && envelope_content_len > 0)
5939 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
5940 level->envelope[0].text[envelope_size++] = '\n';
5941 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
5942 level->envelope[0].text[envelope_size++] = '\n';
5945 for (i = 0; i < envelope_content_len; i++)
5946 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
5947 level->envelope[0].text[envelope_size++] =
5948 header[envelope_content_pos + 1 + i];
5950 level->envelope[0].text[envelope_size] = '\0';
5952 level->envelope[0].xsize = MAX_ENVELOPE_XSIZE;
5953 level->envelope[0].ysize = 10;
5954 level->envelope[0].autowrap = TRUE;
5955 level->envelope[0].centered = TRUE;
5957 for (i = 0; i < level_name_len; i++)
5958 level->name[i] = header[level_name_pos + 1 + i];
5959 level->name[level_name_len] = '\0';
5961 for (i = 0; i < level_author_len; i++)
5962 level->author[i] = header[level_author_pos + 1 + i];
5963 level->author[level_author_len] = '\0';
5965 num_yamyam_contents = header[60] | (header[61] << 8);
5966 level->num_yamyam_contents =
5967 MIN(MAX(MIN_ELEMENT_CONTENTS, num_yamyam_contents), MAX_ELEMENT_CONTENTS);
5969 for (i = 0; i < num_yamyam_contents; i++)
5971 for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
5973 unsigned short word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
5974 int element_dc = ((word & 0xff) << 8) | ((word >> 8) & 0xff);
5976 if (i < MAX_ELEMENT_CONTENTS)
5977 level->yamyam_content[i].e[x][y] = getMappedElement_DC(element_dc);
5981 fieldx = header[6] | (header[7] << 8);
5982 fieldy = header[8] | (header[9] << 8);
5983 level->fieldx = MIN(MAX(MIN_LEV_FIELDX, fieldx), MAX_LEV_FIELDX);
5984 level->fieldy = MIN(MAX(MIN_LEV_FIELDY, fieldy), MAX_LEV_FIELDY);
5986 for (y = 0; y < fieldy; y++) for (x = 0; x < fieldx; x++)
5988 unsigned short word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
5989 int element_dc = ((word & 0xff) << 8) | ((word >> 8) & 0xff);
5991 if (x < MAX_LEV_FIELDX && y < MAX_LEV_FIELDY)
5992 level->field[x][y] = getMappedElement_DC(element_dc);
5995 x = MIN(MAX(0, (header[10] | (header[11] << 8)) - 1), MAX_LEV_FIELDX - 1);
5996 y = MIN(MAX(0, (header[12] | (header[13] << 8)) - 1), MAX_LEV_FIELDY - 1);
5997 level->field[x][y] = EL_PLAYER_1;
5999 x = MIN(MAX(0, (header[14] | (header[15] << 8)) - 1), MAX_LEV_FIELDX - 1);
6000 y = MIN(MAX(0, (header[16] | (header[17] << 8)) - 1), MAX_LEV_FIELDY - 1);
6001 level->field[x][y] = EL_PLAYER_2;
6003 level->gems_needed = header[18] | (header[19] << 8);
6005 level->score[SC_EMERALD] = header[20] | (header[21] << 8);
6006 level->score[SC_DIAMOND] = header[22] | (header[23] << 8);
6007 level->score[SC_PEARL] = header[24] | (header[25] << 8);
6008 level->score[SC_CRYSTAL] = header[26] | (header[27] << 8);
6009 level->score[SC_NUT] = header[28] | (header[29] << 8);
6010 level->score[SC_ROBOT] = header[30] | (header[31] << 8);
6011 level->score[SC_SPACESHIP] = header[32] | (header[33] << 8);
6012 level->score[SC_BUG] = header[34] | (header[35] << 8);
6013 level->score[SC_YAMYAM] = header[36] | (header[37] << 8);
6014 level->score[SC_DYNAMITE] = header[38] | (header[39] << 8);
6015 level->score[SC_KEY] = header[40] | (header[41] << 8);
6016 level->score[SC_TIME_BONUS] = header[42] | (header[43] << 8);
6018 level->time = header[44] | (header[45] << 8);
6020 level->amoeba_speed = header[46] | (header[47] << 8);
6021 level->time_light = header[48] | (header[49] << 8);
6022 level->time_timegate = header[50] | (header[51] << 8);
6023 level->time_wheel = header[52] | (header[53] << 8);
6024 level->time_magic_wall = header[54] | (header[55] << 8);
6025 level->extra_time = header[56] | (header[57] << 8);
6026 level->shield_normal_time = header[58] | (header[59] << 8);
6028 // shield and extra time elements do not have a score
6029 level->score[SC_SHIELD] = 0;
6030 level->extra_time_score = 0;
6032 // set time for normal and deadly shields to the same value
6033 level->shield_deadly_time = level->shield_normal_time;
6035 // Diamond Caves has the same (strange) behaviour as Emerald Mine that gems
6036 // can slip down from flat walls, like normal walls and steel walls
6037 level->em_slippery_gems = TRUE;
6039 // time score is counted for each 10 seconds left in Diamond Caves levels
6040 level->time_score_base = 10;
6043 static void LoadLevelFromFileInfo_DC(struct LevelInfo *level,
6044 struct LevelFileInfo *level_file_info,
6045 boolean level_info_only)
6047 char *filename = level_file_info->filename;
6049 int num_magic_bytes = 8;
6050 char magic_bytes[num_magic_bytes + 1];
6051 int num_levels_to_skip = level_file_info->nr - leveldir_current->first_level;
6053 if (!(file = openFile(filename, MODE_READ)))
6055 level->no_valid_file = TRUE;
6057 if (!level_info_only)
6058 Warn("cannot read level '%s' -- using empty level", filename);
6063 // fseek(file, 0x0000, SEEK_SET);
6065 if (level_file_info->packed)
6067 // read "magic bytes" from start of file
6068 if (getStringFromFile(file, magic_bytes, num_magic_bytes + 1) == NULL)
6069 magic_bytes[0] = '\0';
6071 // check "magic bytes" for correct file format
6072 if (!strPrefix(magic_bytes, "DC2"))
6074 level->no_valid_file = TRUE;
6076 Warn("unknown DC level file '%s' -- using empty level", filename);
6081 if (strPrefix(magic_bytes, "DC2Win95") ||
6082 strPrefix(magic_bytes, "DC2Win98"))
6084 int position_first_level = 0x00fa;
6085 int extra_bytes = 4;
6088 // advance file stream to first level inside the level package
6089 skip_bytes = position_first_level - num_magic_bytes - extra_bytes;
6091 // each block of level data is followed by block of non-level data
6092 num_levels_to_skip *= 2;
6094 // at least skip header bytes, therefore use ">= 0" instead of "> 0"
6095 while (num_levels_to_skip >= 0)
6097 // advance file stream to next level inside the level package
6098 if (seekFile(file, skip_bytes, SEEK_CUR) != 0)
6100 level->no_valid_file = TRUE;
6102 Warn("cannot fseek in file '%s' -- using empty level", filename);
6107 // skip apparently unused extra bytes following each level
6108 ReadUnusedBytesFromFile(file, extra_bytes);
6110 // read size of next level in level package
6111 skip_bytes = getFile32BitLE(file);
6113 num_levels_to_skip--;
6118 level->no_valid_file = TRUE;
6120 Warn("unknown DC2 level file '%s' -- using empty level", filename);
6126 LoadLevelFromFileStream_DC(file, level);
6132 // ----------------------------------------------------------------------------
6133 // functions for loading SB level
6134 // ----------------------------------------------------------------------------
6136 int getMappedElement_SB(int element_ascii, boolean use_ces)
6144 sb_element_mapping[] =
6146 { ' ', EL_EMPTY, EL_CUSTOM_1 }, // floor (space)
6147 { '#', EL_STEELWALL, EL_CUSTOM_2 }, // wall
6148 { '@', EL_PLAYER_1, EL_CUSTOM_3 }, // player
6149 { '$', EL_SOKOBAN_OBJECT, EL_CUSTOM_4 }, // box
6150 { '.', EL_SOKOBAN_FIELD_EMPTY, EL_CUSTOM_5 }, // goal square
6151 { '*', EL_SOKOBAN_FIELD_FULL, EL_CUSTOM_6 }, // box on goal square
6152 { '+', EL_SOKOBAN_FIELD_PLAYER, EL_CUSTOM_7 }, // player on goal square
6153 { '_', EL_INVISIBLE_STEELWALL, EL_FROM_LEVEL_TEMPLATE }, // floor beyond border
6160 for (i = 0; sb_element_mapping[i].ascii != 0; i++)
6161 if (element_ascii == sb_element_mapping[i].ascii)
6162 return (use_ces ? sb_element_mapping[i].ce : sb_element_mapping[i].sb);
6164 return EL_UNDEFINED;
6167 static void SetLevelSettings_SB(struct LevelInfo *level)
6171 level->use_step_counter = TRUE;
6174 level->score[SC_TIME_BONUS] = 0;
6175 level->time_score_base = 1;
6176 level->rate_time_over_score = TRUE;
6179 level->auto_exit_sokoban = TRUE;
6182 static void LoadLevelFromFileInfo_SB(struct LevelInfo *level,
6183 struct LevelFileInfo *level_file_info,
6184 boolean level_info_only)
6186 char *filename = level_file_info->filename;
6187 char line[MAX_LINE_LEN], line_raw[MAX_LINE_LEN], previous_line[MAX_LINE_LEN];
6188 char last_comment[MAX_LINE_LEN];
6189 char level_name[MAX_LINE_LEN];
6192 int num_levels_to_skip = level_file_info->nr - leveldir_current->first_level;
6193 boolean read_continued_line = FALSE;
6194 boolean reading_playfield = FALSE;
6195 boolean got_valid_playfield_line = FALSE;
6196 boolean invalid_playfield_char = FALSE;
6197 boolean load_xsb_to_ces = check_special_flags("load_xsb_to_ces");
6198 int file_level_nr = 0;
6199 int x = 0, y = 0; // initialized to make compilers happy
6201 last_comment[0] = '\0';
6202 level_name[0] = '\0';
6204 if (!(file = openFile(filename, MODE_READ)))
6206 level->no_valid_file = TRUE;
6208 if (!level_info_only)
6209 Warn("cannot read level '%s' -- using empty level", filename);
6214 while (!checkEndOfFile(file))
6216 // level successfully read, but next level may follow here
6217 if (!got_valid_playfield_line && reading_playfield)
6219 // read playfield from single level file -- skip remaining file
6220 if (!level_file_info->packed)
6223 if (file_level_nr >= num_levels_to_skip)
6228 last_comment[0] = '\0';
6229 level_name[0] = '\0';
6231 reading_playfield = FALSE;
6234 got_valid_playfield_line = FALSE;
6236 // read next line of input file
6237 if (!getStringFromFile(file, line, MAX_LINE_LEN))
6240 // cut trailing line break (this can be newline and/or carriage return)
6241 for (line_ptr = &line[strlen(line)]; line_ptr >= line; line_ptr--)
6242 if ((*line_ptr == '\n' || *line_ptr == '\r') && *(line_ptr + 1) == '\0')
6245 // copy raw input line for later use (mainly debugging output)
6246 strcpy(line_raw, line);
6248 if (read_continued_line)
6250 // append new line to existing line, if there is enough space
6251 if (strlen(previous_line) + strlen(line_ptr) < MAX_LINE_LEN)
6252 strcat(previous_line, line_ptr);
6254 strcpy(line, previous_line); // copy storage buffer to line
6256 read_continued_line = FALSE;
6259 // if the last character is '\', continue at next line
6260 if (strlen(line) > 0 && line[strlen(line) - 1] == '\\')
6262 line[strlen(line) - 1] = '\0'; // cut off trailing backslash
6263 strcpy(previous_line, line); // copy line to storage buffer
6265 read_continued_line = TRUE;
6271 if (line[0] == '\0')
6274 // extract comment text from comment line
6277 for (line_ptr = line; *line_ptr; line_ptr++)
6278 if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != ';')
6281 strcpy(last_comment, line_ptr);
6286 // extract level title text from line containing level title
6287 if (line[0] == '\'')
6289 strcpy(level_name, &line[1]);
6291 if (strlen(level_name) > 0 && level_name[strlen(level_name) - 1] == '\'')
6292 level_name[strlen(level_name) - 1] = '\0';
6297 // skip lines containing only spaces (or empty lines)
6298 for (line_ptr = line; *line_ptr; line_ptr++)
6299 if (*line_ptr != ' ')
6301 if (*line_ptr == '\0')
6304 // at this point, we have found a line containing part of a playfield
6306 got_valid_playfield_line = TRUE;
6308 if (!reading_playfield)
6310 reading_playfield = TRUE;
6311 invalid_playfield_char = FALSE;
6313 for (x = 0; x < MAX_LEV_FIELDX; x++)
6314 for (y = 0; y < MAX_LEV_FIELDY; y++)
6315 level->field[x][y] = getMappedElement_SB(' ', load_xsb_to_ces);
6320 // start with topmost tile row
6324 // skip playfield line if larger row than allowed
6325 if (y >= MAX_LEV_FIELDY)
6328 // start with leftmost tile column
6331 // read playfield elements from line
6332 for (line_ptr = line; *line_ptr; line_ptr++)
6334 int mapped_sb_element = getMappedElement_SB(*line_ptr, load_xsb_to_ces);
6336 // stop parsing playfield line if larger column than allowed
6337 if (x >= MAX_LEV_FIELDX)
6340 if (mapped_sb_element == EL_UNDEFINED)
6342 invalid_playfield_char = TRUE;
6347 level->field[x][y] = mapped_sb_element;
6349 // continue with next tile column
6352 level->fieldx = MAX(x, level->fieldx);
6355 if (invalid_playfield_char)
6357 // if first playfield line, treat invalid lines as comment lines
6359 reading_playfield = FALSE;
6364 // continue with next tile row
6372 level->fieldx = MIN(MAX(MIN_LEV_FIELDX, level->fieldx), MAX_LEV_FIELDX);
6373 level->fieldy = MIN(MAX(MIN_LEV_FIELDY, level->fieldy), MAX_LEV_FIELDY);
6375 if (!reading_playfield)
6377 level->no_valid_file = TRUE;
6379 Warn("cannot read level '%s' -- using empty level", filename);
6384 if (*level_name != '\0')
6386 strncpy(level->name, level_name, MAX_LEVEL_NAME_LEN);
6387 level->name[MAX_LEVEL_NAME_LEN] = '\0';
6389 else if (*last_comment != '\0')
6391 strncpy(level->name, last_comment, MAX_LEVEL_NAME_LEN);
6392 level->name[MAX_LEVEL_NAME_LEN] = '\0';
6396 sprintf(level->name, "--> Level %d <--", level_file_info->nr);
6399 // set all empty fields beyond the border walls to invisible steel wall
6400 for (y = 0; y < level->fieldy; y++) for (x = 0; x < level->fieldx; x++)
6402 if ((x == 0 || x == level->fieldx - 1 ||
6403 y == 0 || y == level->fieldy - 1) &&
6404 level->field[x][y] == getMappedElement_SB(' ', load_xsb_to_ces))
6405 FloodFillLevel(x, y, getMappedElement_SB('_', load_xsb_to_ces),
6406 level->field, level->fieldx, level->fieldy);
6409 // set special level settings for Sokoban levels
6410 SetLevelSettings_SB(level);
6412 if (load_xsb_to_ces)
6414 // special global settings can now be set in level template
6415 level->use_custom_template = TRUE;
6420 // -------------------------------------------------------------------------
6421 // functions for handling native levels
6422 // -------------------------------------------------------------------------
6424 static void LoadLevelFromFileInfo_BD(struct LevelInfo *level,
6425 struct LevelFileInfo *level_file_info,
6426 boolean level_info_only)
6430 // determine position of requested level inside level package
6431 if (level_file_info->packed)
6432 pos = level_file_info->nr - leveldir_current->first_level;
6434 if (!LoadNativeLevel_BD(level_file_info->filename, pos, level_info_only))
6435 level->no_valid_file = TRUE;
6438 static void LoadLevelFromFileInfo_EM(struct LevelInfo *level,
6439 struct LevelFileInfo *level_file_info,
6440 boolean level_info_only)
6442 if (!LoadNativeLevel_EM(level_file_info->filename, level_info_only))
6443 level->no_valid_file = TRUE;
6446 static void LoadLevelFromFileInfo_SP(struct LevelInfo *level,
6447 struct LevelFileInfo *level_file_info,
6448 boolean level_info_only)
6452 // determine position of requested level inside level package
6453 if (level_file_info->packed)
6454 pos = level_file_info->nr - leveldir_current->first_level;
6456 if (!LoadNativeLevel_SP(level_file_info->filename, pos, level_info_only))
6457 level->no_valid_file = TRUE;
6460 static void LoadLevelFromFileInfo_MM(struct LevelInfo *level,
6461 struct LevelFileInfo *level_file_info,
6462 boolean level_info_only)
6464 if (!LoadNativeLevel_MM(level_file_info->filename, level_info_only))
6465 level->no_valid_file = TRUE;
6468 void CopyNativeLevel_RND_to_Native(struct LevelInfo *level)
6470 if (level->game_engine_type == GAME_ENGINE_TYPE_BD)
6471 CopyNativeLevel_RND_to_BD(level);
6472 else if (level->game_engine_type == GAME_ENGINE_TYPE_EM)
6473 CopyNativeLevel_RND_to_EM(level);
6474 else if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
6475 CopyNativeLevel_RND_to_SP(level);
6476 else if (level->game_engine_type == GAME_ENGINE_TYPE_MM)
6477 CopyNativeLevel_RND_to_MM(level);
6480 void CopyNativeLevel_Native_to_RND(struct LevelInfo *level)
6482 if (level->game_engine_type == GAME_ENGINE_TYPE_BD)
6483 CopyNativeLevel_BD_to_RND(level);
6484 else if (level->game_engine_type == GAME_ENGINE_TYPE_EM)
6485 CopyNativeLevel_EM_to_RND(level);
6486 else if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
6487 CopyNativeLevel_SP_to_RND(level);
6488 else if (level->game_engine_type == GAME_ENGINE_TYPE_MM)
6489 CopyNativeLevel_MM_to_RND(level);
6492 void SaveNativeLevel(struct LevelInfo *level)
6494 // saving native level files only supported for some game engines
6495 if (level->game_engine_type != GAME_ENGINE_TYPE_BD &&
6496 level->game_engine_type != GAME_ENGINE_TYPE_SP)
6499 char *file_ext = (level->game_engine_type == GAME_ENGINE_TYPE_BD ? "bd" :
6500 level->game_engine_type == GAME_ENGINE_TYPE_SP ? "sp" : "");
6501 char *basename = getSingleLevelBasenameExt(level->file_info.nr, file_ext);
6502 char *filename = getLevelFilenameFromBasename(basename);
6504 if (fileExists(filename) && !Request("Native level file already exists! Overwrite it?", REQ_ASK))
6507 boolean success = FALSE;
6509 if (level->game_engine_type == GAME_ENGINE_TYPE_BD)
6511 CopyNativeLevel_RND_to_BD(level);
6512 // CopyNativeTape_RND_to_BD(level);
6514 success = SaveNativeLevel_BD(filename);
6516 else if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
6518 CopyNativeLevel_RND_to_SP(level);
6519 CopyNativeTape_RND_to_SP(level);
6521 success = SaveNativeLevel_SP(filename);
6525 Request("Native level file saved!", REQ_CONFIRM);
6527 Request("Failed to save native level file!", REQ_CONFIRM);
6531 // ----------------------------------------------------------------------------
6532 // functions for loading generic level
6533 // ----------------------------------------------------------------------------
6535 static void LoadLevelFromFileInfo(struct LevelInfo *level,
6536 struct LevelFileInfo *level_file_info,
6537 boolean level_info_only)
6539 // always start with reliable default values
6540 setLevelInfoToDefaults(level, level_info_only, TRUE);
6542 switch (level_file_info->type)
6544 case LEVEL_FILE_TYPE_RND:
6545 LoadLevelFromFileInfo_RND(level, level_file_info, level_info_only);
6548 case LEVEL_FILE_TYPE_BD:
6549 LoadLevelFromFileInfo_BD(level, level_file_info, level_info_only);
6550 level->game_engine_type = GAME_ENGINE_TYPE_BD;
6553 case LEVEL_FILE_TYPE_EM:
6554 LoadLevelFromFileInfo_EM(level, level_file_info, level_info_only);
6555 level->game_engine_type = GAME_ENGINE_TYPE_EM;
6558 case LEVEL_FILE_TYPE_SP:
6559 LoadLevelFromFileInfo_SP(level, level_file_info, level_info_only);
6560 level->game_engine_type = GAME_ENGINE_TYPE_SP;
6563 case LEVEL_FILE_TYPE_MM:
6564 LoadLevelFromFileInfo_MM(level, level_file_info, level_info_only);
6565 level->game_engine_type = GAME_ENGINE_TYPE_MM;
6568 case LEVEL_FILE_TYPE_DC:
6569 LoadLevelFromFileInfo_DC(level, level_file_info, level_info_only);
6572 case LEVEL_FILE_TYPE_SB:
6573 LoadLevelFromFileInfo_SB(level, level_file_info, level_info_only);
6577 LoadLevelFromFileInfo_RND(level, level_file_info, level_info_only);
6581 // if level file is invalid, restore level structure to default values
6582 if (level->no_valid_file)
6583 setLevelInfoToDefaults(level, level_info_only, FALSE);
6585 if (check_special_flags("use_native_bd_game_engine"))
6586 level->game_engine_type = GAME_ENGINE_TYPE_BD;
6588 if (level->game_engine_type == GAME_ENGINE_TYPE_UNKNOWN)
6589 level->game_engine_type = GAME_ENGINE_TYPE_RND;
6591 if (level_file_info->type != LEVEL_FILE_TYPE_RND)
6592 CopyNativeLevel_Native_to_RND(level);
6595 void LoadLevelFromFilename(struct LevelInfo *level, char *filename)
6597 static struct LevelFileInfo level_file_info;
6599 // always start with reliable default values
6600 setFileInfoToDefaults(&level_file_info);
6602 level_file_info.nr = 0; // unknown level number
6603 level_file_info.type = LEVEL_FILE_TYPE_RND; // no others supported yet
6605 setString(&level_file_info.filename, filename);
6607 LoadLevelFromFileInfo(level, &level_file_info, FALSE);
6610 static void LoadLevel_InitVersion(struct LevelInfo *level)
6614 if (leveldir_current == NULL) // only when dumping level
6617 // all engine modifications also valid for levels which use latest engine
6618 if (level->game_version < VERSION_IDENT(3,2,0,5))
6620 // time bonus score was given for 10 s instead of 1 s before 3.2.0-5
6621 level->time_score_base = 10;
6624 if (leveldir_current->latest_engine)
6626 // ---------- use latest game engine --------------------------------------
6628 /* For all levels which are forced to use the latest game engine version
6629 (normally all but user contributed, private and undefined levels), set
6630 the game engine version to the actual version; this allows for actual
6631 corrections in the game engine to take effect for existing, converted
6632 levels (from "classic" or other existing games) to make the emulation
6633 of the corresponding game more accurate, while (hopefully) not breaking
6634 existing levels created from other players. */
6636 level->game_version = GAME_VERSION_ACTUAL;
6638 /* Set special EM style gems behaviour: EM style gems slip down from
6639 normal, steel and growing wall. As this is a more fundamental change,
6640 it seems better to set the default behaviour to "off" (as it is more
6641 natural) and make it configurable in the level editor (as a property
6642 of gem style elements). Already existing converted levels (neither
6643 private nor contributed levels) are changed to the new behaviour. */
6645 if (level->file_version < FILE_VERSION_2_0)
6646 level->em_slippery_gems = TRUE;
6651 // ---------- use game engine the level was created with --------------------
6653 /* For all levels which are not forced to use the latest game engine
6654 version (normally user contributed, private and undefined levels),
6655 use the version of the game engine the levels were created for.
6657 Since 2.0.1, the game engine version is now directly stored
6658 in the level file (chunk "VERS"), so there is no need anymore
6659 to set the game version from the file version (except for old,
6660 pre-2.0 levels, where the game version is still taken from the
6661 file format version used to store the level -- see above). */
6663 // player was faster than enemies in 1.0.0 and before
6664 if (level->file_version == FILE_VERSION_1_0)
6665 for (i = 0; i < MAX_PLAYERS; i++)
6666 level->initial_player_stepsize[i] = STEPSIZE_FAST;
6668 // default behaviour for EM style gems was "slippery" only in 2.0.1
6669 if (level->game_version == VERSION_IDENT(2,0,1,0))
6670 level->em_slippery_gems = TRUE;
6672 // springs could be pushed over pits before (pre-release version) 2.2.0
6673 if (level->game_version < VERSION_IDENT(2,2,0,0))
6674 level->use_spring_bug = TRUE;
6676 if (level->game_version < VERSION_IDENT(3,2,0,5))
6678 // time orb caused limited time in endless time levels before 3.2.0-5
6679 level->use_time_orb_bug = TRUE;
6681 // default behaviour for snapping was "no snap delay" before 3.2.0-5
6682 level->block_snap_field = FALSE;
6684 // extra time score was same value as time left score before 3.2.0-5
6685 level->extra_time_score = level->score[SC_TIME_BONUS];
6688 if (level->game_version < VERSION_IDENT(3,2,0,7))
6690 // default behaviour for snapping was "not continuous" before 3.2.0-7
6691 level->continuous_snapping = FALSE;
6694 // only few elements were able to actively move into acid before 3.1.0
6695 // trigger settings did not exist before 3.1.0; set to default "any"
6696 if (level->game_version < VERSION_IDENT(3,1,0,0))
6698 // correct "can move into acid" settings (all zero in old levels)
6700 level->can_move_into_acid_bits = 0; // nothing can move into acid
6701 level->dont_collide_with_bits = 0; // nothing is deadly when colliding
6703 setMoveIntoAcidProperty(level, EL_ROBOT, TRUE);
6704 setMoveIntoAcidProperty(level, EL_SATELLITE, TRUE);
6705 setMoveIntoAcidProperty(level, EL_PENGUIN, TRUE);
6706 setMoveIntoAcidProperty(level, EL_BALLOON, TRUE);
6708 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6709 SET_PROPERTY(EL_CUSTOM_START + i, EP_CAN_MOVE_INTO_ACID, TRUE);
6711 // correct trigger settings (stored as zero == "none" in old levels)
6713 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6715 int element = EL_CUSTOM_START + i;
6716 struct ElementInfo *ei = &element_info[element];
6718 for (j = 0; j < ei->num_change_pages; j++)
6720 struct ElementChangeInfo *change = &ei->change_page[j];
6722 change->trigger_player = CH_PLAYER_ANY;
6723 change->trigger_page = CH_PAGE_ANY;
6728 // try to detect and fix "Snake Bite" levels, which are broken with 3.2.0
6730 int element = EL_CUSTOM_256;
6731 struct ElementInfo *ei = &element_info[element];
6732 struct ElementChangeInfo *change = &ei->change_page[0];
6734 /* This is needed to fix a problem that was caused by a bugfix in function
6735 game.c/CreateFieldExt() introduced with 3.2.0 that corrects the behaviour
6736 when a custom element changes to EL_SOKOBAN_FIELD_PLAYER (before, it did
6737 not replace walkable elements, but instead just placed the player on it,
6738 without placing the Sokoban field under the player). Unfortunately, this
6739 breaks "Snake Bite" style levels when the snake is halfway through a door
6740 that just closes (the snake head is still alive and can be moved in this
6741 case). This can be fixed by replacing the EL_SOKOBAN_FIELD_PLAYER by the
6742 player (without Sokoban element) which then gets killed as designed). */
6744 if ((strncmp(leveldir_current->identifier, "snake_bite", 10) == 0 ||
6745 strncmp(ei->description, "pause b4 death", 14) == 0) &&
6746 change->target_element == EL_SOKOBAN_FIELD_PLAYER)
6747 change->target_element = EL_PLAYER_1;
6750 // try to detect and fix "Zelda" style levels, which are broken with 3.2.5
6751 if (level->game_version < VERSION_IDENT(3,2,5,0))
6753 /* This is needed to fix a problem that was caused by a bugfix in function
6754 game.c/CheckTriggeredElementChangeExt() introduced with 3.2.5 that
6755 corrects the behaviour when a custom element changes to another custom
6756 element with a higher element number that has change actions defined.
6757 Normally, only one change per frame is allowed for custom elements.
6758 Therefore, it is checked if a custom element already changed in the
6759 current frame; if it did, subsequent changes are suppressed.
6760 Unfortunately, this is only checked for element changes, but not for
6761 change actions, which are still executed. As the function above loops
6762 through all custom elements from lower to higher, an element change
6763 resulting in a lower CE number won't be checked again, while a target
6764 element with a higher number will also be checked, and potential change
6765 actions will get executed for this CE, too (which is wrong), while
6766 further changes are ignored (which is correct). As this bugfix breaks
6767 Zelda II (and introduces graphical bugs to Zelda I, and also breaks a
6768 few other levels like Alan Bond's "FMV"), allow the previous, incorrect
6769 behaviour for existing levels and tapes that make use of this bug */
6771 level->use_action_after_change_bug = TRUE;
6774 // not centering level after relocating player was default only in 3.2.3
6775 if (level->game_version == VERSION_IDENT(3,2,3,0)) // (no pre-releases)
6776 level->shifted_relocation = TRUE;
6778 // EM style elements always chain-exploded in R'n'D engine before 3.2.6
6779 if (level->game_version < VERSION_IDENT(3,2,6,0))
6780 level->em_explodes_by_fire = TRUE;
6782 // levels were solved by the first player entering an exit up to 4.1.0.0
6783 if (level->game_version <= VERSION_IDENT(4,1,0,0))
6784 level->solved_by_one_player = TRUE;
6786 // game logic of "game of life" and "biomaze" was buggy before 4.1.1.1
6787 if (level->game_version < VERSION_IDENT(4,1,1,1))
6788 level->use_life_bugs = TRUE;
6790 // only Sokoban fields (but not objects) had to be solved before 4.1.1.1
6791 if (level->game_version < VERSION_IDENT(4,1,1,1))
6792 level->sb_objects_needed = FALSE;
6794 // CE actions were triggered by unfinished digging/collecting up to 4.2.2.0
6795 if (level->game_version <= VERSION_IDENT(4,2,2,0))
6796 level->finish_dig_collect = FALSE;
6798 // CE changing to player was kept under the player if walkable up to 4.2.3.1
6799 if (level->game_version <= VERSION_IDENT(4,2,3,1))
6800 level->keep_walkable_ce = TRUE;
6803 static void LoadLevel_InitSettings_SB(struct LevelInfo *level)
6805 boolean is_sokoban_level = TRUE; // unless non-Sokoban elements found
6808 // check if this level is (not) a Sokoban level
6809 for (y = 0; y < level->fieldy; y++)
6810 for (x = 0; x < level->fieldx; x++)
6811 if (!IS_SB_ELEMENT(Tile[x][y]))
6812 is_sokoban_level = FALSE;
6814 if (is_sokoban_level)
6816 // set special level settings for Sokoban levels
6817 SetLevelSettings_SB(level);
6821 static void LoadLevel_InitSettings(struct LevelInfo *level)
6823 // adjust level settings for (non-native) Sokoban-style levels
6824 LoadLevel_InitSettings_SB(level);
6826 // rename levels with title "nameless level" or if renaming is forced
6827 if (leveldir_current->empty_level_name != NULL &&
6828 (strEqual(level->name, NAMELESS_LEVEL_NAME) ||
6829 leveldir_current->force_level_name))
6830 snprintf(level->name, MAX_LEVEL_NAME_LEN + 1,
6831 leveldir_current->empty_level_name, level_nr);
6834 static void LoadLevel_InitStandardElements(struct LevelInfo *level)
6838 // map elements that have changed in newer versions
6839 level->amoeba_content = getMappedElementByVersion(level->amoeba_content,
6840 level->game_version);
6841 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
6842 for (x = 0; x < 3; x++)
6843 for (y = 0; y < 3; y++)
6844 level->yamyam_content[i].e[x][y] =
6845 getMappedElementByVersion(level->yamyam_content[i].e[x][y],
6846 level->game_version);
6850 static void LoadLevel_InitCustomElements(struct LevelInfo *level)
6854 // map custom element change events that have changed in newer versions
6855 // (these following values were accidentally changed in version 3.0.1)
6856 // (this seems to be needed only for 'ab_levelset3' and 'ab_levelset4')
6857 if (level->game_version <= VERSION_IDENT(3,0,0,0))
6859 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6861 int element = EL_CUSTOM_START + i;
6863 // order of checking and copying events to be mapped is important
6864 // (do not change the start and end value -- they are constant)
6865 for (j = CE_BY_OTHER_ACTION; j >= CE_VALUE_GETS_ZERO; j--)
6867 if (HAS_CHANGE_EVENT(element, j - 2))
6869 SET_CHANGE_EVENT(element, j - 2, FALSE);
6870 SET_CHANGE_EVENT(element, j, TRUE);
6874 // order of checking and copying events to be mapped is important
6875 // (do not change the start and end value -- they are constant)
6876 for (j = CE_PLAYER_COLLECTS_X; j >= CE_HITTING_SOMETHING; j--)
6878 if (HAS_CHANGE_EVENT(element, j - 1))
6880 SET_CHANGE_EVENT(element, j - 1, FALSE);
6881 SET_CHANGE_EVENT(element, j, TRUE);
6887 // initialize "can_change" field for old levels with only one change page
6888 if (level->game_version <= VERSION_IDENT(3,0,2,0))
6890 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6892 int element = EL_CUSTOM_START + i;
6894 if (CAN_CHANGE(element))
6895 element_info[element].change->can_change = TRUE;
6899 // correct custom element values (for old levels without these options)
6900 if (level->game_version < VERSION_IDENT(3,1,1,0))
6902 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6904 int element = EL_CUSTOM_START + i;
6905 struct ElementInfo *ei = &element_info[element];
6907 if (ei->access_direction == MV_NO_DIRECTION)
6908 ei->access_direction = MV_ALL_DIRECTIONS;
6912 // correct custom element values (fix invalid values for all versions)
6915 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6917 int element = EL_CUSTOM_START + i;
6918 struct ElementInfo *ei = &element_info[element];
6920 for (j = 0; j < ei->num_change_pages; j++)
6922 struct ElementChangeInfo *change = &ei->change_page[j];
6924 if (change->trigger_player == CH_PLAYER_NONE)
6925 change->trigger_player = CH_PLAYER_ANY;
6927 if (change->trigger_side == CH_SIDE_NONE)
6928 change->trigger_side = CH_SIDE_ANY;
6933 // initialize "can_explode" field for old levels which did not store this
6934 // !!! CHECK THIS -- "<= 3,1,0,0" IS PROBABLY WRONG !!!
6935 if (level->game_version <= VERSION_IDENT(3,1,0,0))
6937 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6939 int element = EL_CUSTOM_START + i;
6941 if (EXPLODES_1X1_OLD(element))
6942 element_info[element].explosion_type = EXPLODES_1X1;
6944 SET_PROPERTY(element, EP_CAN_EXPLODE, (EXPLODES_BY_FIRE(element) ||
6945 EXPLODES_SMASHED(element) ||
6946 EXPLODES_IMPACT(element)));
6950 // correct previously hard-coded move delay values for maze runner style
6951 if (level->game_version < VERSION_IDENT(3,1,1,0))
6953 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6955 int element = EL_CUSTOM_START + i;
6957 if (element_info[element].move_pattern & MV_MAZE_RUNNER_STYLE)
6959 // previously hard-coded and therefore ignored
6960 element_info[element].move_delay_fixed = 9;
6961 element_info[element].move_delay_random = 0;
6966 // set some other uninitialized values of custom elements in older levels
6967 if (level->game_version < VERSION_IDENT(3,1,0,0))
6969 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6971 int element = EL_CUSTOM_START + i;
6973 element_info[element].access_direction = MV_ALL_DIRECTIONS;
6975 element_info[element].explosion_delay = 17;
6976 element_info[element].ignition_delay = 8;
6980 // set mouse click change events to work for left/middle/right mouse button
6981 if (level->game_version < VERSION_IDENT(4,2,3,0))
6983 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6985 int element = EL_CUSTOM_START + i;
6986 struct ElementInfo *ei = &element_info[element];
6988 for (j = 0; j < ei->num_change_pages; j++)
6990 struct ElementChangeInfo *change = &ei->change_page[j];
6992 if (change->has_event[CE_CLICKED_BY_MOUSE] ||
6993 change->has_event[CE_PRESSED_BY_MOUSE] ||
6994 change->has_event[CE_MOUSE_CLICKED_ON_X] ||
6995 change->has_event[CE_MOUSE_PRESSED_ON_X])
6996 change->trigger_side = CH_SIDE_ANY;
7002 static void LoadLevel_InitElements(struct LevelInfo *level)
7004 LoadLevel_InitStandardElements(level);
7006 if (level->file_has_custom_elements)
7007 LoadLevel_InitCustomElements(level);
7009 // initialize element properties for level editor etc.
7010 InitElementPropertiesEngine(level->game_version);
7011 InitElementPropertiesGfxElement();
7014 static void LoadLevel_InitPlayfield(struct LevelInfo *level)
7018 // map elements that have changed in newer versions
7019 for (y = 0; y < level->fieldy; y++)
7020 for (x = 0; x < level->fieldx; x++)
7021 level->field[x][y] = getMappedElementByVersion(level->field[x][y],
7022 level->game_version);
7024 // clear unused playfield data (nicer if level gets resized in editor)
7025 for (x = 0; x < MAX_LEV_FIELDX; x++)
7026 for (y = 0; y < MAX_LEV_FIELDY; y++)
7027 if (x >= level->fieldx || y >= level->fieldy)
7028 level->field[x][y] = EL_EMPTY;
7030 // copy elements to runtime playfield array
7031 for (x = 0; x < MAX_LEV_FIELDX; x++)
7032 for (y = 0; y < MAX_LEV_FIELDY; y++)
7033 Tile[x][y] = level->field[x][y];
7035 // initialize level size variables for faster access
7036 lev_fieldx = level->fieldx;
7037 lev_fieldy = level->fieldy;
7039 // determine border element for this level
7040 if (level->file_info.type == LEVEL_FILE_TYPE_DC)
7041 BorderElement = EL_EMPTY; // (in editor, SetBorderElement() is used)
7046 static void LoadLevel_InitNativeEngines(struct LevelInfo *level)
7048 struct LevelFileInfo *level_file_info = &level->file_info;
7050 if (level_file_info->type == LEVEL_FILE_TYPE_RND)
7051 CopyNativeLevel_RND_to_Native(level);
7054 static void LoadLevelTemplate_LoadAndInit(void)
7056 LoadLevelFromFileInfo(&level_template, &level_template.file_info, FALSE);
7058 LoadLevel_InitVersion(&level_template);
7059 LoadLevel_InitElements(&level_template);
7060 LoadLevel_InitSettings(&level_template);
7062 ActivateLevelTemplate();
7065 void LoadLevelTemplate(int nr)
7067 if (!fileExists(getGlobalLevelTemplateFilename()))
7069 Warn("no level template found for this level");
7074 setLevelFileInfo(&level_template.file_info, nr);
7076 LoadLevelTemplate_LoadAndInit();
7079 static void LoadNetworkLevelTemplate(struct NetworkLevelInfo *network_level)
7081 copyLevelFileInfo(&network_level->tmpl_info, &level_template.file_info);
7083 LoadLevelTemplate_LoadAndInit();
7086 static void LoadLevel_LoadAndInit(struct NetworkLevelInfo *network_level)
7088 LoadLevelFromFileInfo(&level, &level.file_info, FALSE);
7090 if (level.use_custom_template)
7092 if (network_level != NULL)
7093 LoadNetworkLevelTemplate(network_level);
7095 LoadLevelTemplate(-1);
7098 LoadLevel_InitVersion(&level);
7099 LoadLevel_InitElements(&level);
7100 LoadLevel_InitPlayfield(&level);
7101 LoadLevel_InitSettings(&level);
7103 LoadLevel_InitNativeEngines(&level);
7106 void LoadLevel(int nr)
7108 SetLevelSetInfo(leveldir_current->identifier, nr);
7110 setLevelFileInfo(&level.file_info, nr);
7112 LoadLevel_LoadAndInit(NULL);
7115 void LoadLevelInfoOnly(int nr)
7117 setLevelFileInfo(&level.file_info, nr);
7119 LoadLevelFromFileInfo(&level, &level.file_info, TRUE);
7122 void LoadNetworkLevel(struct NetworkLevelInfo *network_level)
7124 SetLevelSetInfo(network_level->leveldir_identifier,
7125 network_level->file_info.nr);
7127 copyLevelFileInfo(&network_level->file_info, &level.file_info);
7129 LoadLevel_LoadAndInit(network_level);
7132 static int SaveLevel_VERS(FILE *file, struct LevelInfo *level)
7136 chunk_size += putFileVersion(file, level->file_version);
7137 chunk_size += putFileVersion(file, level->game_version);
7142 static int SaveLevel_DATE(FILE *file, struct LevelInfo *level)
7146 chunk_size += putFile16BitBE(file, level->creation_date.year);
7147 chunk_size += putFile8Bit(file, level->creation_date.month);
7148 chunk_size += putFile8Bit(file, level->creation_date.day);
7153 #if ENABLE_HISTORIC_CHUNKS
7154 static void SaveLevel_HEAD(FILE *file, struct LevelInfo *level)
7158 putFile8Bit(file, level->fieldx);
7159 putFile8Bit(file, level->fieldy);
7161 putFile16BitBE(file, level->time);
7162 putFile16BitBE(file, level->gems_needed);
7164 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
7165 putFile8Bit(file, level->name[i]);
7167 for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
7168 putFile8Bit(file, level->score[i]);
7170 for (i = 0; i < STD_ELEMENT_CONTENTS; i++)
7171 for (y = 0; y < 3; y++)
7172 for (x = 0; x < 3; x++)
7173 putFile8Bit(file, (level->encoding_16bit_yamyam ? EL_EMPTY :
7174 level->yamyam_content[i].e[x][y]));
7175 putFile8Bit(file, level->amoeba_speed);
7176 putFile8Bit(file, level->time_magic_wall);
7177 putFile8Bit(file, level->time_wheel);
7178 putFile8Bit(file, (level->encoding_16bit_amoeba ? EL_EMPTY :
7179 level->amoeba_content));
7180 putFile8Bit(file, (level->initial_player_stepsize == STEPSIZE_FAST ? 1 : 0));
7181 putFile8Bit(file, (level->initial_gravity ? 1 : 0));
7182 putFile8Bit(file, (level->encoding_16bit_field ? 1 : 0));
7183 putFile8Bit(file, (level->em_slippery_gems ? 1 : 0));
7185 putFile8Bit(file, (level->use_custom_template ? 1 : 0));
7187 putFile8Bit(file, (level->block_last_field ? 1 : 0));
7188 putFile8Bit(file, (level->sp_block_last_field ? 1 : 0));
7189 putFile32BitBE(file, level->can_move_into_acid_bits);
7190 putFile8Bit(file, level->dont_collide_with_bits);
7192 putFile8Bit(file, (level->use_spring_bug ? 1 : 0));
7193 putFile8Bit(file, (level->use_step_counter ? 1 : 0));
7195 putFile8Bit(file, (level->instant_relocation ? 1 : 0));
7196 putFile8Bit(file, (level->can_pass_to_walkable ? 1 : 0));
7197 putFile8Bit(file, (level->grow_into_diggable ? 1 : 0));
7199 putFile8Bit(file, level->game_engine_type);
7201 WriteUnusedBytesToFile(file, LEVEL_CHUNK_HEAD_UNUSED);
7205 static int SaveLevel_NAME(FILE *file, struct LevelInfo *level)
7210 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
7211 chunk_size += putFile8Bit(file, level->name[i]);
7216 static int SaveLevel_AUTH(FILE *file, struct LevelInfo *level)
7221 for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
7222 chunk_size += putFile8Bit(file, level->author[i]);
7227 #if ENABLE_HISTORIC_CHUNKS
7228 static int SaveLevel_BODY(FILE *file, struct LevelInfo *level)
7233 for (y = 0; y < level->fieldy; y++)
7234 for (x = 0; x < level->fieldx; x++)
7235 if (level->encoding_16bit_field)
7236 chunk_size += putFile16BitBE(file, level->field[x][y]);
7238 chunk_size += putFile8Bit(file, level->field[x][y]);
7244 static int SaveLevel_BODY(FILE *file, struct LevelInfo *level)
7249 for (y = 0; y < level->fieldy; y++)
7250 for (x = 0; x < level->fieldx; x++)
7251 chunk_size += putFile16BitBE(file, level->field[x][y]);
7256 #if ENABLE_HISTORIC_CHUNKS
7257 static void SaveLevel_CONT(FILE *file, struct LevelInfo *level)
7261 putFile8Bit(file, EL_YAMYAM);
7262 putFile8Bit(file, level->num_yamyam_contents);
7263 putFile8Bit(file, 0);
7264 putFile8Bit(file, 0);
7266 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
7267 for (y = 0; y < 3; y++)
7268 for (x = 0; x < 3; x++)
7269 if (level->encoding_16bit_field)
7270 putFile16BitBE(file, level->yamyam_content[i].e[x][y]);
7272 putFile8Bit(file, level->yamyam_content[i].e[x][y]);
7276 #if ENABLE_HISTORIC_CHUNKS
7277 static void SaveLevel_CNT2(FILE *file, struct LevelInfo *level, int element)
7280 int num_contents, content_xsize, content_ysize;
7281 int content_array[MAX_ELEMENT_CONTENTS][3][3];
7283 if (element == EL_YAMYAM)
7285 num_contents = level->num_yamyam_contents;
7289 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
7290 for (y = 0; y < 3; y++)
7291 for (x = 0; x < 3; x++)
7292 content_array[i][x][y] = level->yamyam_content[i].e[x][y];
7294 else if (element == EL_BD_AMOEBA)
7300 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
7301 for (y = 0; y < 3; y++)
7302 for (x = 0; x < 3; x++)
7303 content_array[i][x][y] = EL_EMPTY;
7304 content_array[0][0][0] = level->amoeba_content;
7308 // chunk header already written -- write empty chunk data
7309 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_SIZE);
7311 Warn("cannot save content for element '%d'", element);
7316 putFile16BitBE(file, element);
7317 putFile8Bit(file, num_contents);
7318 putFile8Bit(file, content_xsize);
7319 putFile8Bit(file, content_ysize);
7321 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_UNUSED);
7323 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
7324 for (y = 0; y < 3; y++)
7325 for (x = 0; x < 3; x++)
7326 putFile16BitBE(file, content_array[i][x][y]);
7330 #if ENABLE_HISTORIC_CHUNKS
7331 static int SaveLevel_CNT3(FILE *file, struct LevelInfo *level, int element)
7333 int envelope_nr = element - EL_ENVELOPE_1;
7334 int envelope_len = strlen(level->envelope_text[envelope_nr]) + 1;
7338 chunk_size += putFile16BitBE(file, element);
7339 chunk_size += putFile16BitBE(file, envelope_len);
7340 chunk_size += putFile8Bit(file, level->envelope_xsize[envelope_nr]);
7341 chunk_size += putFile8Bit(file, level->envelope_ysize[envelope_nr]);
7343 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT3_UNUSED);
7344 chunk_size += LEVEL_CHUNK_CNT3_UNUSED;
7346 for (i = 0; i < envelope_len; i++)
7347 chunk_size += putFile8Bit(file, level->envelope_text[envelope_nr][i]);
7353 #if ENABLE_HISTORIC_CHUNKS
7354 static void SaveLevel_CUS1(FILE *file, struct LevelInfo *level,
7355 int num_changed_custom_elements)
7359 putFile16BitBE(file, num_changed_custom_elements);
7361 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7363 int element = EL_CUSTOM_START + i;
7365 struct ElementInfo *ei = &element_info[element];
7367 if (ei->properties[EP_BITFIELD_BASE_NR] != EP_BITMASK_DEFAULT)
7369 if (check < num_changed_custom_elements)
7371 putFile16BitBE(file, element);
7372 putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
7379 if (check != num_changed_custom_elements) // should not happen
7380 Warn("inconsistent number of custom element properties");
7384 #if ENABLE_HISTORIC_CHUNKS
7385 static void SaveLevel_CUS2(FILE *file, struct LevelInfo *level,
7386 int num_changed_custom_elements)
7390 putFile16BitBE(file, num_changed_custom_elements);
7392 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7394 int element = EL_CUSTOM_START + i;
7396 if (element_info[element].change->target_element != EL_EMPTY_SPACE)
7398 if (check < num_changed_custom_elements)
7400 putFile16BitBE(file, element);
7401 putFile16BitBE(file, element_info[element].change->target_element);
7408 if (check != num_changed_custom_elements) // should not happen
7409 Warn("inconsistent number of custom target elements");
7413 #if ENABLE_HISTORIC_CHUNKS
7414 static void SaveLevel_CUS3(FILE *file, struct LevelInfo *level,
7415 int num_changed_custom_elements)
7417 int i, j, x, y, check = 0;
7419 putFile16BitBE(file, num_changed_custom_elements);
7421 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7423 int element = EL_CUSTOM_START + i;
7424 struct ElementInfo *ei = &element_info[element];
7426 if (ei->modified_settings)
7428 if (check < num_changed_custom_elements)
7430 putFile16BitBE(file, element);
7432 for (j = 0; j < MAX_ELEMENT_NAME_LEN; j++)
7433 putFile8Bit(file, ei->description[j]);
7435 putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
7437 // some free bytes for future properties and padding
7438 WriteUnusedBytesToFile(file, 7);
7440 putFile8Bit(file, ei->use_gfx_element);
7441 putFile16BitBE(file, ei->gfx_element_initial);
7443 putFile8Bit(file, ei->collect_score_initial);
7444 putFile8Bit(file, ei->collect_count_initial);
7446 putFile16BitBE(file, ei->push_delay_fixed);
7447 putFile16BitBE(file, ei->push_delay_random);
7448 putFile16BitBE(file, ei->move_delay_fixed);
7449 putFile16BitBE(file, ei->move_delay_random);
7451 putFile16BitBE(file, ei->move_pattern);
7452 putFile8Bit(file, ei->move_direction_initial);
7453 putFile8Bit(file, ei->move_stepsize);
7455 for (y = 0; y < 3; y++)
7456 for (x = 0; x < 3; x++)
7457 putFile16BitBE(file, ei->content.e[x][y]);
7459 putFile32BitBE(file, ei->change->events);
7461 putFile16BitBE(file, ei->change->target_element);
7463 putFile16BitBE(file, ei->change->delay_fixed);
7464 putFile16BitBE(file, ei->change->delay_random);
7465 putFile16BitBE(file, ei->change->delay_frames);
7467 putFile16BitBE(file, ei->change->initial_trigger_element);
7469 putFile8Bit(file, ei->change->explode);
7470 putFile8Bit(file, ei->change->use_target_content);
7471 putFile8Bit(file, ei->change->only_if_complete);
7472 putFile8Bit(file, ei->change->use_random_replace);
7474 putFile8Bit(file, ei->change->random_percentage);
7475 putFile8Bit(file, ei->change->replace_when);
7477 for (y = 0; y < 3; y++)
7478 for (x = 0; x < 3; x++)
7479 putFile16BitBE(file, ei->change->content.e[x][y]);
7481 putFile8Bit(file, ei->slippery_type);
7483 // some free bytes for future properties and padding
7484 WriteUnusedBytesToFile(file, LEVEL_CPART_CUS3_UNUSED);
7491 if (check != num_changed_custom_elements) // should not happen
7492 Warn("inconsistent number of custom element properties");
7496 #if ENABLE_HISTORIC_CHUNKS
7497 static void SaveLevel_CUS4(FILE *file, struct LevelInfo *level, int element)
7499 struct ElementInfo *ei = &element_info[element];
7502 // ---------- custom element base property values (96 bytes) ----------------
7504 putFile16BitBE(file, element);
7506 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
7507 putFile8Bit(file, ei->description[i]);
7509 putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
7511 WriteUnusedBytesToFile(file, 4); // reserved for more base properties
7513 putFile8Bit(file, ei->num_change_pages);
7515 putFile16BitBE(file, ei->ce_value_fixed_initial);
7516 putFile16BitBE(file, ei->ce_value_random_initial);
7517 putFile8Bit(file, ei->use_last_ce_value);
7519 putFile8Bit(file, ei->use_gfx_element);
7520 putFile16BitBE(file, ei->gfx_element_initial);
7522 putFile8Bit(file, ei->collect_score_initial);
7523 putFile8Bit(file, ei->collect_count_initial);
7525 putFile8Bit(file, ei->drop_delay_fixed);
7526 putFile8Bit(file, ei->push_delay_fixed);
7527 putFile8Bit(file, ei->drop_delay_random);
7528 putFile8Bit(file, ei->push_delay_random);
7529 putFile16BitBE(file, ei->move_delay_fixed);
7530 putFile16BitBE(file, ei->move_delay_random);
7532 // bits 0 - 15 of "move_pattern" ...
7533 putFile16BitBE(file, ei->move_pattern & 0xffff);
7534 putFile8Bit(file, ei->move_direction_initial);
7535 putFile8Bit(file, ei->move_stepsize);
7537 putFile8Bit(file, ei->slippery_type);
7539 for (y = 0; y < 3; y++)
7540 for (x = 0; x < 3; x++)
7541 putFile16BitBE(file, ei->content.e[x][y]);
7543 putFile16BitBE(file, ei->move_enter_element);
7544 putFile16BitBE(file, ei->move_leave_element);
7545 putFile8Bit(file, ei->move_leave_type);
7547 // ... bits 16 - 31 of "move_pattern" (not nice, but downward compatible)
7548 putFile16BitBE(file, (ei->move_pattern >> 16) & 0xffff);
7550 putFile8Bit(file, ei->access_direction);
7552 putFile8Bit(file, ei->explosion_delay);
7553 putFile8Bit(file, ei->ignition_delay);
7554 putFile8Bit(file, ei->explosion_type);
7556 // some free bytes for future custom property values and padding
7557 WriteUnusedBytesToFile(file, 1);
7559 // ---------- change page property values (48 bytes) ------------------------
7561 for (i = 0; i < ei->num_change_pages; i++)
7563 struct ElementChangeInfo *change = &ei->change_page[i];
7564 unsigned int event_bits;
7566 // bits 0 - 31 of "has_event[]" ...
7568 for (j = 0; j < MIN(NUM_CHANGE_EVENTS, 32); j++)
7569 if (change->has_event[j])
7570 event_bits |= (1u << j);
7571 putFile32BitBE(file, event_bits);
7573 putFile16BitBE(file, change->target_element);
7575 putFile16BitBE(file, change->delay_fixed);
7576 putFile16BitBE(file, change->delay_random);
7577 putFile16BitBE(file, change->delay_frames);
7579 putFile16BitBE(file, change->initial_trigger_element);
7581 putFile8Bit(file, change->explode);
7582 putFile8Bit(file, change->use_target_content);
7583 putFile8Bit(file, change->only_if_complete);
7584 putFile8Bit(file, change->use_random_replace);
7586 putFile8Bit(file, change->random_percentage);
7587 putFile8Bit(file, change->replace_when);
7589 for (y = 0; y < 3; y++)
7590 for (x = 0; x < 3; x++)
7591 putFile16BitBE(file, change->target_content.e[x][y]);
7593 putFile8Bit(file, change->can_change);
7595 putFile8Bit(file, change->trigger_side);
7597 putFile8Bit(file, change->trigger_player);
7598 putFile8Bit(file, (change->trigger_page == CH_PAGE_ANY ? CH_PAGE_ANY_FILE :
7599 log_2(change->trigger_page)));
7601 putFile8Bit(file, change->has_action);
7602 putFile8Bit(file, change->action_type);
7603 putFile8Bit(file, change->action_mode);
7604 putFile16BitBE(file, change->action_arg);
7606 // ... bits 32 - 39 of "has_event[]" (not nice, but downward compatible)
7608 for (j = 32; j < NUM_CHANGE_EVENTS; j++)
7609 if (change->has_event[j])
7610 event_bits |= (1u << (j - 32));
7611 putFile8Bit(file, event_bits);
7616 #if ENABLE_HISTORIC_CHUNKS
7617 static void SaveLevel_GRP1(FILE *file, struct LevelInfo *level, int element)
7619 struct ElementInfo *ei = &element_info[element];
7620 struct ElementGroupInfo *group = ei->group;
7623 putFile16BitBE(file, element);
7625 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
7626 putFile8Bit(file, ei->description[i]);
7628 putFile8Bit(file, group->num_elements);
7630 putFile8Bit(file, ei->use_gfx_element);
7631 putFile16BitBE(file, ei->gfx_element_initial);
7633 putFile8Bit(file, group->choice_mode);
7635 // some free bytes for future values and padding
7636 WriteUnusedBytesToFile(file, 3);
7638 for (i = 0; i < MAX_ELEMENTS_IN_GROUP; i++)
7639 putFile16BitBE(file, group->element[i]);
7643 static int SaveLevel_MicroChunk(FILE *file, struct LevelFileConfigInfo *entry,
7644 boolean write_element)
7646 int save_type = entry->save_type;
7647 int data_type = entry->data_type;
7648 int conf_type = entry->conf_type;
7649 int byte_mask = conf_type & CONF_MASK_BYTES;
7650 int element = entry->element;
7651 int default_value = entry->default_value;
7653 boolean modified = FALSE;
7655 if (byte_mask != CONF_MASK_MULTI_BYTES)
7657 void *value_ptr = entry->value;
7658 int value = (data_type == TYPE_BOOLEAN ? *(boolean *)value_ptr :
7661 // check if any settings have been modified before saving them
7662 if (value != default_value)
7665 // do not save if explicitly told or if unmodified default settings
7666 if ((save_type == SAVE_CONF_NEVER) ||
7667 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
7671 num_bytes += putFile16BitBE(file, element);
7673 num_bytes += putFile8Bit(file, conf_type);
7674 num_bytes += (byte_mask == CONF_MASK_1_BYTE ? putFile8Bit (file, value) :
7675 byte_mask == CONF_MASK_2_BYTE ? putFile16BitBE(file, value) :
7676 byte_mask == CONF_MASK_4_BYTE ? putFile32BitBE(file, value) :
7679 else if (data_type == TYPE_STRING)
7681 char *default_string = entry->default_string;
7682 char *string = (char *)(entry->value);
7683 int string_length = strlen(string);
7686 // check if any settings have been modified before saving them
7687 if (!strEqual(string, default_string))
7690 // do not save if explicitly told or if unmodified default settings
7691 if ((save_type == SAVE_CONF_NEVER) ||
7692 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
7696 num_bytes += putFile16BitBE(file, element);
7698 num_bytes += putFile8Bit(file, conf_type);
7699 num_bytes += putFile16BitBE(file, string_length);
7701 for (i = 0; i < string_length; i++)
7702 num_bytes += putFile8Bit(file, string[i]);
7704 else if (data_type == TYPE_ELEMENT_LIST)
7706 int *element_array = (int *)(entry->value);
7707 int num_elements = *(int *)(entry->num_entities);
7710 // check if any settings have been modified before saving them
7711 for (i = 0; i < num_elements; i++)
7712 if (element_array[i] != default_value)
7715 // do not save if explicitly told or if unmodified default settings
7716 if ((save_type == SAVE_CONF_NEVER) ||
7717 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
7721 num_bytes += putFile16BitBE(file, element);
7723 num_bytes += putFile8Bit(file, conf_type);
7724 num_bytes += putFile16BitBE(file, num_elements * CONF_ELEMENT_NUM_BYTES);
7726 for (i = 0; i < num_elements; i++)
7727 num_bytes += putFile16BitBE(file, element_array[i]);
7729 else if (data_type == TYPE_CONTENT_LIST)
7731 struct Content *content = (struct Content *)(entry->value);
7732 int num_contents = *(int *)(entry->num_entities);
7735 // check if any settings have been modified before saving them
7736 for (i = 0; i < num_contents; i++)
7737 for (y = 0; y < 3; y++)
7738 for (x = 0; x < 3; x++)
7739 if (content[i].e[x][y] != default_value)
7742 // do not save if explicitly told or if unmodified default settings
7743 if ((save_type == SAVE_CONF_NEVER) ||
7744 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
7748 num_bytes += putFile16BitBE(file, element);
7750 num_bytes += putFile8Bit(file, conf_type);
7751 num_bytes += putFile16BitBE(file, num_contents * CONF_CONTENT_NUM_BYTES);
7753 for (i = 0; i < num_contents; i++)
7754 for (y = 0; y < 3; y++)
7755 for (x = 0; x < 3; x++)
7756 num_bytes += putFile16BitBE(file, content[i].e[x][y]);
7762 static int SaveLevel_INFO(FILE *file, struct LevelInfo *level)
7767 li = *level; // copy level data into temporary buffer
7769 for (i = 0; chunk_config_INFO[i].data_type != -1; i++)
7770 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_INFO[i], FALSE);
7775 static int SaveLevel_ELEM(FILE *file, struct LevelInfo *level)
7780 li = *level; // copy level data into temporary buffer
7782 for (i = 0; chunk_config_ELEM[i].data_type != -1; i++)
7783 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_ELEM[i], TRUE);
7788 static int SaveLevel_NOTE(FILE *file, struct LevelInfo *level, int element)
7790 int envelope_nr = element - EL_ENVELOPE_1;
7794 chunk_size += putFile16BitBE(file, element);
7796 // copy envelope data into temporary buffer
7797 xx_envelope = level->envelope[envelope_nr];
7799 for (i = 0; chunk_config_NOTE[i].data_type != -1; i++)
7800 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_NOTE[i], FALSE);
7805 static int SaveLevel_CUSX(FILE *file, struct LevelInfo *level, int element)
7807 struct ElementInfo *ei = &element_info[element];
7811 chunk_size += putFile16BitBE(file, element);
7813 xx_ei = *ei; // copy element data into temporary buffer
7815 // set default description string for this specific element
7816 strcpy(xx_default_description, getDefaultElementDescription(ei));
7818 for (i = 0; chunk_config_CUSX_base[i].data_type != -1; i++)
7819 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_CUSX_base[i], FALSE);
7821 for (i = 0; i < ei->num_change_pages; i++)
7823 struct ElementChangeInfo *change = &ei->change_page[i];
7825 xx_current_change_page = i;
7827 xx_change = *change; // copy change data into temporary buffer
7830 setEventBitsFromEventFlags(change);
7832 for (j = 0; chunk_config_CUSX_change[j].data_type != -1; j++)
7833 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_CUSX_change[j],
7840 static int SaveLevel_GRPX(FILE *file, struct LevelInfo *level, int element)
7842 struct ElementInfo *ei = &element_info[element];
7843 struct ElementGroupInfo *group = ei->group;
7847 chunk_size += putFile16BitBE(file, element);
7849 xx_ei = *ei; // copy element data into temporary buffer
7850 xx_group = *group; // copy group data into temporary buffer
7852 // set default description string for this specific element
7853 strcpy(xx_default_description, getDefaultElementDescription(ei));
7855 for (i = 0; chunk_config_GRPX[i].data_type != -1; i++)
7856 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_GRPX[i], FALSE);
7861 static int SaveLevel_EMPX(FILE *file, struct LevelInfo *level, int element)
7863 struct ElementInfo *ei = &element_info[element];
7867 chunk_size += putFile16BitBE(file, element);
7869 xx_ei = *ei; // copy element data into temporary buffer
7871 for (i = 0; chunk_config_EMPX[i].data_type != -1; i++)
7872 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_EMPX[i], FALSE);
7877 static void SaveLevelFromFilename(struct LevelInfo *level, char *filename,
7878 boolean save_as_template)
7884 if (!(file = fopen(filename, MODE_WRITE)))
7886 Warn("cannot save level file '%s'", filename);
7891 level->file_version = FILE_VERSION_ACTUAL;
7892 level->game_version = GAME_VERSION_ACTUAL;
7894 level->creation_date = getCurrentDate();
7896 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
7897 putFileChunkBE(file, "CAVE", CHUNK_SIZE_NONE);
7899 chunk_size = SaveLevel_VERS(NULL, level);
7900 putFileChunkBE(file, "VERS", chunk_size);
7901 SaveLevel_VERS(file, level);
7903 chunk_size = SaveLevel_DATE(NULL, level);
7904 putFileChunkBE(file, "DATE", chunk_size);
7905 SaveLevel_DATE(file, level);
7907 chunk_size = SaveLevel_NAME(NULL, level);
7908 putFileChunkBE(file, "NAME", chunk_size);
7909 SaveLevel_NAME(file, level);
7911 chunk_size = SaveLevel_AUTH(NULL, level);
7912 putFileChunkBE(file, "AUTH", chunk_size);
7913 SaveLevel_AUTH(file, level);
7915 chunk_size = SaveLevel_INFO(NULL, level);
7916 putFileChunkBE(file, "INFO", chunk_size);
7917 SaveLevel_INFO(file, level);
7919 chunk_size = SaveLevel_BODY(NULL, level);
7920 putFileChunkBE(file, "BODY", chunk_size);
7921 SaveLevel_BODY(file, level);
7923 chunk_size = SaveLevel_ELEM(NULL, level);
7924 if (chunk_size > LEVEL_CHUNK_ELEM_UNCHANGED) // save if changed
7926 putFileChunkBE(file, "ELEM", chunk_size);
7927 SaveLevel_ELEM(file, level);
7930 for (i = 0; i < NUM_ENVELOPES; i++)
7932 int element = EL_ENVELOPE_1 + i;
7934 chunk_size = SaveLevel_NOTE(NULL, level, element);
7935 if (chunk_size > LEVEL_CHUNK_NOTE_UNCHANGED) // save if changed
7937 putFileChunkBE(file, "NOTE", chunk_size);
7938 SaveLevel_NOTE(file, level, element);
7942 // if not using template level, check for non-default custom/group elements
7943 if (!level->use_custom_template || save_as_template)
7945 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7947 int element = EL_CUSTOM_START + i;
7949 chunk_size = SaveLevel_CUSX(NULL, level, element);
7950 if (chunk_size > LEVEL_CHUNK_CUSX_UNCHANGED) // save if changed
7952 putFileChunkBE(file, "CUSX", chunk_size);
7953 SaveLevel_CUSX(file, level, element);
7957 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
7959 int element = EL_GROUP_START + i;
7961 chunk_size = SaveLevel_GRPX(NULL, level, element);
7962 if (chunk_size > LEVEL_CHUNK_GRPX_UNCHANGED) // save if changed
7964 putFileChunkBE(file, "GRPX", chunk_size);
7965 SaveLevel_GRPX(file, level, element);
7969 for (i = 0; i < NUM_EMPTY_ELEMENTS_ALL; i++)
7971 int element = GET_EMPTY_ELEMENT(i);
7973 chunk_size = SaveLevel_EMPX(NULL, level, element);
7974 if (chunk_size > LEVEL_CHUNK_EMPX_UNCHANGED) // save if changed
7976 putFileChunkBE(file, "EMPX", chunk_size);
7977 SaveLevel_EMPX(file, level, element);
7984 SetFilePermissions(filename, PERMS_PRIVATE);
7987 void SaveLevel(int nr)
7989 char *filename = getDefaultLevelFilename(nr);
7991 SaveLevelFromFilename(&level, filename, FALSE);
7994 void SaveLevelTemplate(void)
7996 char *filename = getLocalLevelTemplateFilename();
7998 SaveLevelFromFilename(&level, filename, TRUE);
8001 boolean SaveLevelChecked(int nr)
8003 char *filename = getDefaultLevelFilename(nr);
8004 boolean new_level = !fileExists(filename);
8005 boolean level_saved = FALSE;
8007 if (new_level || Request("Save this level and kill the old?", REQ_ASK))
8012 Request("Level saved!", REQ_CONFIRM);
8020 void DumpLevel(struct LevelInfo *level)
8022 if (level->no_level_file || level->no_valid_file)
8024 Warn("cannot dump -- no valid level file found");
8030 Print("Level xxx (file version %08d, game version %08d)\n",
8031 level->file_version, level->game_version);
8034 Print("Level author: '%s'\n", level->author);
8035 Print("Level title: '%s'\n", level->name);
8037 Print("Playfield size: %d x %d\n", level->fieldx, level->fieldy);
8039 Print("Level time: %d seconds\n", level->time);
8040 Print("Gems needed: %d\n", level->gems_needed);
8042 Print("Time for magic wall: %d seconds\n", level->time_magic_wall);
8043 Print("Time for wheel: %d seconds\n", level->time_wheel);
8044 Print("Time for light: %d seconds\n", level->time_light);
8045 Print("Time for timegate: %d seconds\n", level->time_timegate);
8047 Print("Amoeba speed: %d\n", level->amoeba_speed);
8050 Print("EM style slippery gems: %s\n", (level->em_slippery_gems ? "yes" : "no"));
8051 Print("Player blocks last field: %s\n", (level->block_last_field ? "yes" : "no"));
8052 Print("SP player blocks last field: %s\n", (level->sp_block_last_field ? "yes" : "no"));
8053 Print("use spring bug: %s\n", (level->use_spring_bug ? "yes" : "no"));
8054 Print("use step counter: %s\n", (level->use_step_counter ? "yes" : "no"));
8055 Print("rate time over score: %s\n", (level->rate_time_over_score ? "yes" : "no"));
8061 for (i = 0; i < NUM_ENVELOPES; i++)
8063 char *text = level->envelope[i].text;
8064 int text_len = strlen(text);
8065 boolean has_text = FALSE;
8067 for (j = 0; j < text_len; j++)
8068 if (text[j] != ' ' && text[j] != '\n')
8074 Print("Envelope %d:\n'%s'\n", i + 1, text);
8082 void DumpLevels(void)
8084 static LevelDirTree *dumplevel_leveldir = NULL;
8086 dumplevel_leveldir = getTreeInfoFromIdentifier(leveldir_first,
8087 global.dumplevel_leveldir);
8089 if (dumplevel_leveldir == NULL)
8090 Fail("no such level identifier: '%s'", global.dumplevel_leveldir);
8092 if (global.dumplevel_level_nr < dumplevel_leveldir->first_level ||
8093 global.dumplevel_level_nr > dumplevel_leveldir->last_level)
8094 Fail("no such level number: %d", global.dumplevel_level_nr);
8096 leveldir_current = dumplevel_leveldir;
8098 LoadLevel(global.dumplevel_level_nr);
8105 // ============================================================================
8106 // tape file functions
8107 // ============================================================================
8109 static void setTapeInfoToDefaults(void)
8113 // always start with reliable default values (empty tape)
8116 // default values (also for pre-1.2 tapes) with only the first player
8117 tape.player_participates[0] = TRUE;
8118 for (i = 1; i < MAX_PLAYERS; i++)
8119 tape.player_participates[i] = FALSE;
8121 // at least one (default: the first) player participates in every tape
8122 tape.num_participating_players = 1;
8124 tape.property_bits = TAPE_PROPERTY_NONE;
8126 tape.level_nr = level_nr;
8128 tape.changed = FALSE;
8129 tape.solved = FALSE;
8131 tape.recording = FALSE;
8132 tape.playing = FALSE;
8133 tape.pausing = FALSE;
8135 tape.scr_fieldx = SCR_FIELDX_DEFAULT;
8136 tape.scr_fieldy = SCR_FIELDY_DEFAULT;
8138 tape.no_info_chunk = TRUE;
8139 tape.no_valid_file = FALSE;
8142 static int getTapePosSize(struct TapeInfo *tape)
8144 int tape_pos_size = 0;
8146 if (tape->use_key_actions)
8147 tape_pos_size += tape->num_participating_players;
8149 if (tape->use_mouse_actions)
8150 tape_pos_size += 3; // x and y position and mouse button mask
8152 tape_pos_size += 1; // tape action delay value
8154 return tape_pos_size;
8157 static void setTapeActionFlags(struct TapeInfo *tape, int value)
8159 tape->use_key_actions = FALSE;
8160 tape->use_mouse_actions = FALSE;
8162 if (value != TAPE_USE_MOUSE_ACTIONS_ONLY)
8163 tape->use_key_actions = TRUE;
8165 if (value != TAPE_USE_KEY_ACTIONS_ONLY)
8166 tape->use_mouse_actions = TRUE;
8169 static int getTapeActionValue(struct TapeInfo *tape)
8171 return (tape->use_key_actions &&
8172 tape->use_mouse_actions ? TAPE_USE_KEY_AND_MOUSE_ACTIONS :
8173 tape->use_key_actions ? TAPE_USE_KEY_ACTIONS_ONLY :
8174 tape->use_mouse_actions ? TAPE_USE_MOUSE_ACTIONS_ONLY :
8175 TAPE_ACTIONS_DEFAULT);
8178 static int LoadTape_VERS(File *file, int chunk_size, struct TapeInfo *tape)
8180 tape->file_version = getFileVersion(file);
8181 tape->game_version = getFileVersion(file);
8186 static int LoadTape_HEAD(File *file, int chunk_size, struct TapeInfo *tape)
8190 tape->random_seed = getFile32BitBE(file);
8191 tape->date = getFile32BitBE(file);
8192 tape->length = getFile32BitBE(file);
8194 // read header fields that are new since version 1.2
8195 if (tape->file_version >= FILE_VERSION_1_2)
8197 byte store_participating_players = getFile8Bit(file);
8200 // since version 1.2, tapes store which players participate in the tape
8201 tape->num_participating_players = 0;
8202 for (i = 0; i < MAX_PLAYERS; i++)
8204 tape->player_participates[i] = FALSE;
8206 if (store_participating_players & (1 << i))
8208 tape->player_participates[i] = TRUE;
8209 tape->num_participating_players++;
8213 setTapeActionFlags(tape, getFile8Bit(file));
8215 tape->property_bits = getFile8Bit(file);
8216 tape->solved = getFile8Bit(file);
8218 engine_version = getFileVersion(file);
8219 if (engine_version > 0)
8220 tape->engine_version = engine_version;
8222 tape->engine_version = tape->game_version;
8228 static int LoadTape_SCRN(File *file, int chunk_size, struct TapeInfo *tape)
8230 tape->scr_fieldx = getFile8Bit(file);
8231 tape->scr_fieldy = getFile8Bit(file);
8236 static int LoadTape_INFO(File *file, int chunk_size, struct TapeInfo *tape)
8238 char *level_identifier = NULL;
8239 int level_identifier_size;
8242 tape->no_info_chunk = FALSE;
8244 level_identifier_size = getFile16BitBE(file);
8246 level_identifier = checked_malloc(level_identifier_size);
8248 for (i = 0; i < level_identifier_size; i++)
8249 level_identifier[i] = getFile8Bit(file);
8251 strncpy(tape->level_identifier, level_identifier, MAX_FILENAME_LEN);
8252 tape->level_identifier[MAX_FILENAME_LEN] = '\0';
8254 checked_free(level_identifier);
8256 tape->level_nr = getFile16BitBE(file);
8258 chunk_size = 2 + level_identifier_size + 2;
8263 static int LoadTape_BODY(File *file, int chunk_size, struct TapeInfo *tape)
8266 int tape_pos_size = getTapePosSize(tape);
8267 int chunk_size_expected = tape_pos_size * tape->length;
8269 if (chunk_size_expected != chunk_size)
8271 ReadUnusedBytesFromFile(file, chunk_size);
8272 return chunk_size_expected;
8275 for (i = 0; i < tape->length; i++)
8277 if (i >= MAX_TAPE_LEN)
8279 Warn("tape truncated -- size exceeds maximum tape size %d",
8282 // tape too large; read and ignore remaining tape data from this chunk
8283 for (;i < tape->length; i++)
8284 ReadUnusedBytesFromFile(file, tape_pos_size);
8289 if (tape->use_key_actions)
8291 for (j = 0; j < MAX_PLAYERS; j++)
8293 tape->pos[i].action[j] = MV_NONE;
8295 if (tape->player_participates[j])
8296 tape->pos[i].action[j] = getFile8Bit(file);
8300 if (tape->use_mouse_actions)
8302 tape->pos[i].action[TAPE_ACTION_LX] = getFile8Bit(file);
8303 tape->pos[i].action[TAPE_ACTION_LY] = getFile8Bit(file);
8304 tape->pos[i].action[TAPE_ACTION_BUTTON] = getFile8Bit(file);
8307 tape->pos[i].delay = getFile8Bit(file);
8309 if (tape->file_version == FILE_VERSION_1_0)
8311 // eliminate possible diagonal moves in old tapes
8312 // this is only for backward compatibility
8314 byte joy_dir[4] = { JOY_LEFT, JOY_RIGHT, JOY_UP, JOY_DOWN };
8315 byte action = tape->pos[i].action[0];
8316 int k, num_moves = 0;
8318 for (k = 0; k < 4; k++)
8320 if (action & joy_dir[k])
8322 tape->pos[i + num_moves].action[0] = joy_dir[k];
8324 tape->pos[i + num_moves].delay = 0;
8333 tape->length += num_moves;
8336 else if (tape->file_version < FILE_VERSION_2_0)
8338 // convert pre-2.0 tapes to new tape format
8340 if (tape->pos[i].delay > 1)
8343 tape->pos[i + 1] = tape->pos[i];
8344 tape->pos[i + 1].delay = 1;
8347 for (j = 0; j < MAX_PLAYERS; j++)
8348 tape->pos[i].action[j] = MV_NONE;
8349 tape->pos[i].delay--;
8356 if (checkEndOfFile(file))
8360 if (i != tape->length)
8361 chunk_size = tape_pos_size * i;
8366 static void LoadTape_SokobanSolution(char *filename)
8369 int move_delay = TILESIZE / level.initial_player_stepsize[0];
8371 if (!(file = openFile(filename, MODE_READ)))
8373 tape.no_valid_file = TRUE;
8378 while (!checkEndOfFile(file))
8380 unsigned char c = getByteFromFile(file);
8382 if (checkEndOfFile(file))
8389 tape.pos[tape.length].action[0] = MV_UP;
8390 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
8396 tape.pos[tape.length].action[0] = MV_DOWN;
8397 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
8403 tape.pos[tape.length].action[0] = MV_LEFT;
8404 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
8410 tape.pos[tape.length].action[0] = MV_RIGHT;
8411 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
8419 // ignore white-space characters
8423 tape.no_valid_file = TRUE;
8425 Warn("unsupported Sokoban solution file '%s' ['%d']", filename, c);
8433 if (tape.no_valid_file)
8436 tape.length_frames = GetTapeLengthFrames();
8437 tape.length_seconds = GetTapeLengthSeconds();
8440 void LoadTapeFromFilename(char *filename)
8442 char cookie[MAX_LINE_LEN];
8443 char chunk_name[CHUNK_ID_LEN + 1];
8447 // always start with reliable default values
8448 setTapeInfoToDefaults();
8450 if (strSuffix(filename, ".sln"))
8452 LoadTape_SokobanSolution(filename);
8457 if (!(file = openFile(filename, MODE_READ)))
8459 tape.no_valid_file = TRUE;
8464 getFileChunkBE(file, chunk_name, NULL);
8465 if (strEqual(chunk_name, "RND1"))
8467 getFile32BitBE(file); // not used
8469 getFileChunkBE(file, chunk_name, NULL);
8470 if (!strEqual(chunk_name, "TAPE"))
8472 tape.no_valid_file = TRUE;
8474 Warn("unknown format of tape file '%s'", filename);
8481 else // check for pre-2.0 file format with cookie string
8483 strcpy(cookie, chunk_name);
8484 if (getStringFromFile(file, &cookie[4], MAX_LINE_LEN - 4) == NULL)
8486 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
8487 cookie[strlen(cookie) - 1] = '\0';
8489 if (!checkCookieString(cookie, TAPE_COOKIE_TMPL))
8491 tape.no_valid_file = TRUE;
8493 Warn("unknown format of tape file '%s'", filename);
8500 if ((tape.file_version = getFileVersionFromCookieString(cookie)) == -1)
8502 tape.no_valid_file = TRUE;
8504 Warn("unsupported version of tape file '%s'", filename);
8511 // pre-2.0 tape files have no game version, so use file version here
8512 tape.game_version = tape.file_version;
8515 if (tape.file_version < FILE_VERSION_1_2)
8517 // tape files from versions before 1.2.0 without chunk structure
8518 LoadTape_HEAD(file, TAPE_CHUNK_HEAD_SIZE, &tape);
8519 LoadTape_BODY(file, 2 * tape.length, &tape);
8527 int (*loader)(File *, int, struct TapeInfo *);
8531 { "VERS", TAPE_CHUNK_VERS_SIZE, LoadTape_VERS },
8532 { "HEAD", TAPE_CHUNK_HEAD_SIZE, LoadTape_HEAD },
8533 { "SCRN", TAPE_CHUNK_SCRN_SIZE, LoadTape_SCRN },
8534 { "INFO", -1, LoadTape_INFO },
8535 { "BODY", -1, LoadTape_BODY },
8539 while (getFileChunkBE(file, chunk_name, &chunk_size))
8543 while (chunk_info[i].name != NULL &&
8544 !strEqual(chunk_name, chunk_info[i].name))
8547 if (chunk_info[i].name == NULL)
8549 Warn("unknown chunk '%s' in tape file '%s'",
8550 chunk_name, filename);
8552 ReadUnusedBytesFromFile(file, chunk_size);
8554 else if (chunk_info[i].size != -1 &&
8555 chunk_info[i].size != chunk_size)
8557 Warn("wrong size (%d) of chunk '%s' in tape file '%s'",
8558 chunk_size, chunk_name, filename);
8560 ReadUnusedBytesFromFile(file, chunk_size);
8564 // call function to load this tape chunk
8565 int chunk_size_expected =
8566 (chunk_info[i].loader)(file, chunk_size, &tape);
8568 // the size of some chunks cannot be checked before reading other
8569 // chunks first (like "HEAD" and "BODY") that contain some header
8570 // information, so check them here
8571 if (chunk_size_expected != chunk_size)
8573 Warn("wrong size (%d) of chunk '%s' in tape file '%s'",
8574 chunk_size, chunk_name, filename);
8582 tape.length_frames = GetTapeLengthFrames();
8583 tape.length_seconds = GetTapeLengthSeconds();
8586 Debug("files:LoadTapeFromFilename", "tape file version: %d",
8588 Debug("files:LoadTapeFromFilename", "tape game version: %d",
8590 Debug("files:LoadTapeFromFilename", "tape engine version: %d",
8591 tape.engine_version);
8595 void LoadTape(int nr)
8597 char *filename = getTapeFilename(nr);
8599 LoadTapeFromFilename(filename);
8602 void LoadSolutionTape(int nr)
8604 char *filename = getSolutionTapeFilename(nr);
8606 LoadTapeFromFilename(filename);
8608 if (TAPE_IS_EMPTY(tape))
8610 if (level.game_engine_type == GAME_ENGINE_TYPE_BD &&
8611 level.native_bd_level->replay != NULL)
8612 CopyNativeTape_BD_to_RND(&level);
8613 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP &&
8614 level.native_sp_level->demo.is_available)
8615 CopyNativeTape_SP_to_RND(&level);
8619 void LoadScoreTape(char *score_tape_basename, int nr)
8621 char *filename = getScoreTapeFilename(score_tape_basename, nr);
8623 LoadTapeFromFilename(filename);
8626 void LoadScoreCacheTape(char *score_tape_basename, int nr)
8628 char *filename = getScoreCacheTapeFilename(score_tape_basename, nr);
8630 LoadTapeFromFilename(filename);
8633 static boolean checkSaveTape_SCRN(struct TapeInfo *tape)
8635 // chunk required for team mode tapes with non-default screen size
8636 return (tape->num_participating_players > 1 &&
8637 (tape->scr_fieldx != SCR_FIELDX_DEFAULT ||
8638 tape->scr_fieldy != SCR_FIELDY_DEFAULT));
8641 static void SaveTape_VERS(FILE *file, struct TapeInfo *tape)
8643 putFileVersion(file, tape->file_version);
8644 putFileVersion(file, tape->game_version);
8647 static void SaveTape_HEAD(FILE *file, struct TapeInfo *tape)
8650 byte store_participating_players = 0;
8652 // set bits for participating players for compact storage
8653 for (i = 0; i < MAX_PLAYERS; i++)
8654 if (tape->player_participates[i])
8655 store_participating_players |= (1 << i);
8657 putFile32BitBE(file, tape->random_seed);
8658 putFile32BitBE(file, tape->date);
8659 putFile32BitBE(file, tape->length);
8661 putFile8Bit(file, store_participating_players);
8663 putFile8Bit(file, getTapeActionValue(tape));
8665 putFile8Bit(file, tape->property_bits);
8666 putFile8Bit(file, tape->solved);
8668 putFileVersion(file, tape->engine_version);
8671 static void SaveTape_SCRN(FILE *file, struct TapeInfo *tape)
8673 putFile8Bit(file, tape->scr_fieldx);
8674 putFile8Bit(file, tape->scr_fieldy);
8677 static void SaveTape_INFO(FILE *file, struct TapeInfo *tape)
8679 int level_identifier_size = strlen(tape->level_identifier) + 1;
8682 putFile16BitBE(file, level_identifier_size);
8684 for (i = 0; i < level_identifier_size; i++)
8685 putFile8Bit(file, tape->level_identifier[i]);
8687 putFile16BitBE(file, tape->level_nr);
8690 static void SaveTape_BODY(FILE *file, struct TapeInfo *tape)
8694 for (i = 0; i < tape->length; i++)
8696 if (tape->use_key_actions)
8698 for (j = 0; j < MAX_PLAYERS; j++)
8699 if (tape->player_participates[j])
8700 putFile8Bit(file, tape->pos[i].action[j]);
8703 if (tape->use_mouse_actions)
8705 putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_LX]);
8706 putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_LY]);
8707 putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_BUTTON]);
8710 putFile8Bit(file, tape->pos[i].delay);
8714 void SaveTapeToFilename(char *filename)
8718 int info_chunk_size;
8719 int body_chunk_size;
8721 if (!(file = fopen(filename, MODE_WRITE)))
8723 Warn("cannot save level recording file '%s'", filename);
8728 tape_pos_size = getTapePosSize(&tape);
8730 info_chunk_size = 2 + (strlen(tape.level_identifier) + 1) + 2;
8731 body_chunk_size = tape_pos_size * tape.length;
8733 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
8734 putFileChunkBE(file, "TAPE", CHUNK_SIZE_NONE);
8736 putFileChunkBE(file, "VERS", TAPE_CHUNK_VERS_SIZE);
8737 SaveTape_VERS(file, &tape);
8739 putFileChunkBE(file, "HEAD", TAPE_CHUNK_HEAD_SIZE);
8740 SaveTape_HEAD(file, &tape);
8742 if (checkSaveTape_SCRN(&tape))
8744 putFileChunkBE(file, "SCRN", TAPE_CHUNK_SCRN_SIZE);
8745 SaveTape_SCRN(file, &tape);
8748 putFileChunkBE(file, "INFO", info_chunk_size);
8749 SaveTape_INFO(file, &tape);
8751 putFileChunkBE(file, "BODY", body_chunk_size);
8752 SaveTape_BODY(file, &tape);
8756 SetFilePermissions(filename, PERMS_PRIVATE);
8759 static void SaveTapeExt(char *filename)
8763 tape.file_version = FILE_VERSION_ACTUAL;
8764 tape.game_version = GAME_VERSION_ACTUAL;
8766 tape.num_participating_players = 0;
8768 // count number of participating players
8769 for (i = 0; i < MAX_PLAYERS; i++)
8770 if (tape.player_participates[i])
8771 tape.num_participating_players++;
8773 SaveTapeToFilename(filename);
8775 tape.changed = FALSE;
8778 void SaveTape(int nr)
8780 char *filename = getTapeFilename(nr);
8782 InitTapeDirectory(leveldir_current->subdir);
8784 SaveTapeExt(filename);
8787 void SaveScoreTape(int nr)
8789 char *filename = getScoreTapeFilename(tape.score_tape_basename, nr);
8791 // used instead of "leveldir_current->subdir" (for network games)
8792 InitScoreTapeDirectory(levelset.identifier, nr);
8794 SaveTapeExt(filename);
8797 static boolean SaveTapeCheckedExt(int nr, char *msg_replace, char *msg_saved,
8798 unsigned int req_state_added)
8800 char *filename = getTapeFilename(nr);
8801 boolean new_tape = !fileExists(filename);
8802 boolean tape_saved = FALSE;
8804 if (new_tape || Request(msg_replace, REQ_ASK | req_state_added))
8809 Request(msg_saved, REQ_CONFIRM | req_state_added);
8817 boolean SaveTapeChecked(int nr)
8819 return SaveTapeCheckedExt(nr, "Replace old tape?", "Tape saved!", 0);
8822 boolean SaveTapeChecked_LevelSolved(int nr)
8824 return SaveTapeCheckedExt(nr, "Level solved! Replace old tape?",
8825 "Level solved! Tape saved!", REQ_STAY_OPEN);
8828 void DumpTape(struct TapeInfo *tape)
8830 int tape_frame_counter;
8833 if (tape->no_valid_file)
8835 Warn("cannot dump -- no valid tape file found");
8842 Print("Tape of Level %03d (file version %08d, game version %08d)\n",
8843 tape->level_nr, tape->file_version, tape->game_version);
8844 Print(" (effective engine version %08d)\n",
8845 tape->engine_version);
8846 Print("Level series identifier: '%s'\n", tape->level_identifier);
8848 Print("Solution tape: %s\n",
8849 tape->solved ? "yes" :
8850 tape->game_version < VERSION_IDENT(4,3,2,3) ? "unknown" : "no");
8852 Print("Special tape properties: ");
8853 if (tape->property_bits == TAPE_PROPERTY_NONE)
8855 if (tape->property_bits & TAPE_PROPERTY_EM_RANDOM_BUG)
8856 Print("[em_random_bug]");
8857 if (tape->property_bits & TAPE_PROPERTY_GAME_SPEED)
8858 Print("[game_speed]");
8859 if (tape->property_bits & TAPE_PROPERTY_PAUSE_MODE)
8861 if (tape->property_bits & TAPE_PROPERTY_SINGLE_STEP)
8862 Print("[single_step]");
8863 if (tape->property_bits & TAPE_PROPERTY_SNAPSHOT)
8864 Print("[snapshot]");
8865 if (tape->property_bits & TAPE_PROPERTY_REPLAYED)
8866 Print("[replayed]");
8867 if (tape->property_bits & TAPE_PROPERTY_TAS_KEYS)
8868 Print("[tas_keys]");
8869 if (tape->property_bits & TAPE_PROPERTY_SMALL_GRAPHICS)
8870 Print("[small_graphics]");
8873 int year2 = tape->date / 10000;
8874 int year4 = (year2 < 70 ? 2000 + year2 : 1900 + year2);
8875 int month_index_raw = (tape->date / 100) % 100;
8876 int month_index = month_index_raw % 12; // prevent invalid index
8877 int month = month_index + 1;
8878 int day = tape->date % 100;
8880 Print("Tape date: %04d-%02d-%02d\n", year4, month, day);
8884 tape_frame_counter = 0;
8886 for (i = 0; i < tape->length; i++)
8888 if (i >= MAX_TAPE_LEN)
8893 for (j = 0; j < MAX_PLAYERS; j++)
8895 if (tape->player_participates[j])
8897 int action = tape->pos[i].action[j];
8899 Print("%d:%02x ", j, action);
8900 Print("[%c%c%c%c|%c%c] - ",
8901 (action & JOY_LEFT ? '<' : ' '),
8902 (action & JOY_RIGHT ? '>' : ' '),
8903 (action & JOY_UP ? '^' : ' '),
8904 (action & JOY_DOWN ? 'v' : ' '),
8905 (action & JOY_BUTTON_1 ? '1' : ' '),
8906 (action & JOY_BUTTON_2 ? '2' : ' '));
8910 Print("(%03d) ", tape->pos[i].delay);
8911 Print("[%05d]\n", tape_frame_counter);
8913 tape_frame_counter += tape->pos[i].delay;
8919 void DumpTapes(void)
8921 static LevelDirTree *dumptape_leveldir = NULL;
8923 dumptape_leveldir = getTreeInfoFromIdentifier(leveldir_first,
8924 global.dumptape_leveldir);
8926 if (dumptape_leveldir == NULL)
8927 Fail("no such level identifier: '%s'", global.dumptape_leveldir);
8929 if (global.dumptape_level_nr < dumptape_leveldir->first_level ||
8930 global.dumptape_level_nr > dumptape_leveldir->last_level)
8931 Fail("no such level number: %d", global.dumptape_level_nr);
8933 leveldir_current = dumptape_leveldir;
8935 if (options.mytapes)
8936 LoadTape(global.dumptape_level_nr);
8938 LoadSolutionTape(global.dumptape_level_nr);
8946 // ============================================================================
8947 // score file functions
8948 // ============================================================================
8950 static void setScoreInfoToDefaultsExt(struct ScoreInfo *scores)
8954 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
8956 strcpy(scores->entry[i].tape_basename, UNDEFINED_FILENAME);
8957 strcpy(scores->entry[i].name, EMPTY_PLAYER_NAME);
8958 scores->entry[i].score = 0;
8959 scores->entry[i].time = 0;
8961 scores->entry[i].id = -1;
8962 strcpy(scores->entry[i].tape_date, UNKNOWN_NAME);
8963 strcpy(scores->entry[i].platform, UNKNOWN_NAME);
8964 strcpy(scores->entry[i].version, UNKNOWN_NAME);
8965 strcpy(scores->entry[i].country_name, UNKNOWN_NAME);
8966 strcpy(scores->entry[i].country_code, "??");
8969 scores->num_entries = 0;
8970 scores->last_added = -1;
8971 scores->last_added_local = -1;
8973 scores->updated = FALSE;
8974 scores->uploaded = FALSE;
8975 scores->tape_downloaded = FALSE;
8976 scores->force_last_added = FALSE;
8978 // The following values are intentionally not reset here:
8982 // - continue_playing
8983 // - continue_on_return
8986 static void setScoreInfoToDefaults(void)
8988 setScoreInfoToDefaultsExt(&scores);
8991 static void setServerScoreInfoToDefaults(void)
8993 setScoreInfoToDefaultsExt(&server_scores);
8996 static void LoadScore_OLD(int nr)
8999 char *filename = getScoreFilename(nr);
9000 char cookie[MAX_LINE_LEN];
9001 char line[MAX_LINE_LEN];
9005 if (!(file = fopen(filename, MODE_READ)))
9008 // check file identifier
9009 if (fgets(cookie, MAX_LINE_LEN, file) == NULL)
9011 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
9012 cookie[strlen(cookie) - 1] = '\0';
9014 if (!checkCookieString(cookie, SCORE_COOKIE_TMPL))
9016 Warn("unknown format of score file '%s'", filename);
9023 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9025 if (fscanf(file, "%d", &scores.entry[i].score) == EOF)
9026 Warn("fscanf() failed; %s", strerror(errno));
9028 if (fgets(line, MAX_LINE_LEN, file) == NULL)
9031 if (strlen(line) > 0 && line[strlen(line) - 1] == '\n')
9032 line[strlen(line) - 1] = '\0';
9034 for (line_ptr = line; *line_ptr; line_ptr++)
9036 if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != '\0')
9038 strncpy(scores.entry[i].name, line_ptr, MAX_PLAYER_NAME_LEN);
9039 scores.entry[i].name[MAX_PLAYER_NAME_LEN] = '\0';
9048 static void ConvertScore_OLD(void)
9050 // only convert score to time for levels that rate playing time over score
9051 if (!level.rate_time_over_score)
9054 // convert old score to playing time for score-less levels (like Supaplex)
9055 int time_final_max = 999;
9058 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9060 int score = scores.entry[i].score;
9062 if (score > 0 && score < time_final_max)
9063 scores.entry[i].time = (time_final_max - score - 1) * FRAMES_PER_SECOND;
9067 static int LoadScore_VERS(File *file, int chunk_size, struct ScoreInfo *scores)
9069 scores->file_version = getFileVersion(file);
9070 scores->game_version = getFileVersion(file);
9075 static int LoadScore_INFO(File *file, int chunk_size, struct ScoreInfo *scores)
9077 char *level_identifier = NULL;
9078 int level_identifier_size;
9081 level_identifier_size = getFile16BitBE(file);
9083 level_identifier = checked_malloc(level_identifier_size);
9085 for (i = 0; i < level_identifier_size; i++)
9086 level_identifier[i] = getFile8Bit(file);
9088 strncpy(scores->level_identifier, level_identifier, MAX_FILENAME_LEN);
9089 scores->level_identifier[MAX_FILENAME_LEN] = '\0';
9091 checked_free(level_identifier);
9093 scores->level_nr = getFile16BitBE(file);
9094 scores->num_entries = getFile16BitBE(file);
9096 chunk_size = 2 + level_identifier_size + 2 + 2;
9101 static int LoadScore_NAME(File *file, int chunk_size, struct ScoreInfo *scores)
9105 for (i = 0; i < scores->num_entries; i++)
9107 for (j = 0; j < MAX_PLAYER_NAME_LEN; j++)
9108 scores->entry[i].name[j] = getFile8Bit(file);
9110 scores->entry[i].name[MAX_PLAYER_NAME_LEN] = '\0';
9113 chunk_size = scores->num_entries * MAX_PLAYER_NAME_LEN;
9118 static int LoadScore_SCOR(File *file, int chunk_size, struct ScoreInfo *scores)
9122 for (i = 0; i < scores->num_entries; i++)
9123 scores->entry[i].score = getFile16BitBE(file);
9125 chunk_size = scores->num_entries * 2;
9130 static int LoadScore_SC4R(File *file, int chunk_size, struct ScoreInfo *scores)
9134 for (i = 0; i < scores->num_entries; i++)
9135 scores->entry[i].score = getFile32BitBE(file);
9137 chunk_size = scores->num_entries * 4;
9142 static int LoadScore_TIME(File *file, int chunk_size, struct ScoreInfo *scores)
9146 for (i = 0; i < scores->num_entries; i++)
9147 scores->entry[i].time = getFile32BitBE(file);
9149 chunk_size = scores->num_entries * 4;
9154 static int LoadScore_TAPE(File *file, int chunk_size, struct ScoreInfo *scores)
9158 for (i = 0; i < scores->num_entries; i++)
9160 for (j = 0; j < MAX_SCORE_TAPE_BASENAME_LEN; j++)
9161 scores->entry[i].tape_basename[j] = getFile8Bit(file);
9163 scores->entry[i].tape_basename[MAX_SCORE_TAPE_BASENAME_LEN] = '\0';
9166 chunk_size = scores->num_entries * MAX_SCORE_TAPE_BASENAME_LEN;
9171 void LoadScore(int nr)
9173 char *filename = getScoreFilename(nr);
9174 char cookie[MAX_LINE_LEN];
9175 char chunk_name[CHUNK_ID_LEN + 1];
9177 boolean old_score_file_format = FALSE;
9180 // always start with reliable default values
9181 setScoreInfoToDefaults();
9183 if (!(file = openFile(filename, MODE_READ)))
9186 getFileChunkBE(file, chunk_name, NULL);
9187 if (strEqual(chunk_name, "RND1"))
9189 getFile32BitBE(file); // not used
9191 getFileChunkBE(file, chunk_name, NULL);
9192 if (!strEqual(chunk_name, "SCOR"))
9194 Warn("unknown format of score file '%s'", filename);
9201 else // check for old file format with cookie string
9203 strcpy(cookie, chunk_name);
9204 if (getStringFromFile(file, &cookie[4], MAX_LINE_LEN - 4) == NULL)
9206 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
9207 cookie[strlen(cookie) - 1] = '\0';
9209 if (!checkCookieString(cookie, SCORE_COOKIE_TMPL))
9211 Warn("unknown format of score file '%s'", filename);
9218 old_score_file_format = TRUE;
9221 if (old_score_file_format)
9223 // score files from versions before 4.2.4.0 without chunk structure
9226 // convert score to time, if possible (mainly for Supaplex levels)
9235 int (*loader)(File *, int, struct ScoreInfo *);
9239 { "VERS", SCORE_CHUNK_VERS_SIZE, LoadScore_VERS },
9240 { "INFO", -1, LoadScore_INFO },
9241 { "NAME", -1, LoadScore_NAME },
9242 { "SCOR", -1, LoadScore_SCOR },
9243 { "SC4R", -1, LoadScore_SC4R },
9244 { "TIME", -1, LoadScore_TIME },
9245 { "TAPE", -1, LoadScore_TAPE },
9250 while (getFileChunkBE(file, chunk_name, &chunk_size))
9254 while (chunk_info[i].name != NULL &&
9255 !strEqual(chunk_name, chunk_info[i].name))
9258 if (chunk_info[i].name == NULL)
9260 Warn("unknown chunk '%s' in score file '%s'",
9261 chunk_name, filename);
9263 ReadUnusedBytesFromFile(file, chunk_size);
9265 else if (chunk_info[i].size != -1 &&
9266 chunk_info[i].size != chunk_size)
9268 Warn("wrong size (%d) of chunk '%s' in score file '%s'",
9269 chunk_size, chunk_name, filename);
9271 ReadUnusedBytesFromFile(file, chunk_size);
9275 // call function to load this score chunk
9276 int chunk_size_expected =
9277 (chunk_info[i].loader)(file, chunk_size, &scores);
9279 // the size of some chunks cannot be checked before reading other
9280 // chunks first (like "HEAD" and "BODY") that contain some header
9281 // information, so check them here
9282 if (chunk_size_expected != chunk_size)
9284 Warn("wrong size (%d) of chunk '%s' in score file '%s'",
9285 chunk_size, chunk_name, filename);
9294 #if ENABLE_HISTORIC_CHUNKS
9295 void SaveScore_OLD(int nr)
9298 char *filename = getScoreFilename(nr);
9301 // used instead of "leveldir_current->subdir" (for network games)
9302 InitScoreDirectory(levelset.identifier);
9304 if (!(file = fopen(filename, MODE_WRITE)))
9306 Warn("cannot save score for level %d", nr);
9311 fprintf(file, "%s\n\n", SCORE_COOKIE);
9313 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9314 fprintf(file, "%d %s\n", scores.entry[i].score, scores.entry[i].name);
9318 SetFilePermissions(filename, PERMS_PRIVATE);
9322 static void SaveScore_VERS(FILE *file, struct ScoreInfo *scores)
9324 putFileVersion(file, scores->file_version);
9325 putFileVersion(file, scores->game_version);
9328 static void SaveScore_INFO(FILE *file, struct ScoreInfo *scores)
9330 int level_identifier_size = strlen(scores->level_identifier) + 1;
9333 putFile16BitBE(file, level_identifier_size);
9335 for (i = 0; i < level_identifier_size; i++)
9336 putFile8Bit(file, scores->level_identifier[i]);
9338 putFile16BitBE(file, scores->level_nr);
9339 putFile16BitBE(file, scores->num_entries);
9342 static void SaveScore_NAME(FILE *file, struct ScoreInfo *scores)
9346 for (i = 0; i < scores->num_entries; i++)
9348 int name_size = strlen(scores->entry[i].name);
9350 for (j = 0; j < MAX_PLAYER_NAME_LEN; j++)
9351 putFile8Bit(file, (j < name_size ? scores->entry[i].name[j] : 0));
9355 static void SaveScore_SCOR(FILE *file, struct ScoreInfo *scores)
9359 for (i = 0; i < scores->num_entries; i++)
9360 putFile16BitBE(file, scores->entry[i].score);
9363 static void SaveScore_SC4R(FILE *file, struct ScoreInfo *scores)
9367 for (i = 0; i < scores->num_entries; i++)
9368 putFile32BitBE(file, scores->entry[i].score);
9371 static void SaveScore_TIME(FILE *file, struct ScoreInfo *scores)
9375 for (i = 0; i < scores->num_entries; i++)
9376 putFile32BitBE(file, scores->entry[i].time);
9379 static void SaveScore_TAPE(FILE *file, struct ScoreInfo *scores)
9383 for (i = 0; i < scores->num_entries; i++)
9385 int size = strlen(scores->entry[i].tape_basename);
9387 for (j = 0; j < MAX_SCORE_TAPE_BASENAME_LEN; j++)
9388 putFile8Bit(file, (j < size ? scores->entry[i].tape_basename[j] : 0));
9392 static void SaveScoreToFilename(char *filename)
9395 int info_chunk_size;
9396 int name_chunk_size;
9397 int scor_chunk_size;
9398 int sc4r_chunk_size;
9399 int time_chunk_size;
9400 int tape_chunk_size;
9401 boolean has_large_score_values;
9404 if (!(file = fopen(filename, MODE_WRITE)))
9406 Warn("cannot save score file '%s'", filename);
9411 info_chunk_size = 2 + (strlen(scores.level_identifier) + 1) + 2 + 2;
9412 name_chunk_size = scores.num_entries * MAX_PLAYER_NAME_LEN;
9413 scor_chunk_size = scores.num_entries * 2;
9414 sc4r_chunk_size = scores.num_entries * 4;
9415 time_chunk_size = scores.num_entries * 4;
9416 tape_chunk_size = scores.num_entries * MAX_SCORE_TAPE_BASENAME_LEN;
9418 has_large_score_values = FALSE;
9419 for (i = 0; i < scores.num_entries; i++)
9420 if (scores.entry[i].score > 0xffff)
9421 has_large_score_values = TRUE;
9423 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
9424 putFileChunkBE(file, "SCOR", CHUNK_SIZE_NONE);
9426 putFileChunkBE(file, "VERS", SCORE_CHUNK_VERS_SIZE);
9427 SaveScore_VERS(file, &scores);
9429 putFileChunkBE(file, "INFO", info_chunk_size);
9430 SaveScore_INFO(file, &scores);
9432 putFileChunkBE(file, "NAME", name_chunk_size);
9433 SaveScore_NAME(file, &scores);
9435 if (has_large_score_values)
9437 putFileChunkBE(file, "SC4R", sc4r_chunk_size);
9438 SaveScore_SC4R(file, &scores);
9442 putFileChunkBE(file, "SCOR", scor_chunk_size);
9443 SaveScore_SCOR(file, &scores);
9446 putFileChunkBE(file, "TIME", time_chunk_size);
9447 SaveScore_TIME(file, &scores);
9449 putFileChunkBE(file, "TAPE", tape_chunk_size);
9450 SaveScore_TAPE(file, &scores);
9454 SetFilePermissions(filename, PERMS_PRIVATE);
9457 void SaveScore(int nr)
9459 char *filename = getScoreFilename(nr);
9462 // used instead of "leveldir_current->subdir" (for network games)
9463 InitScoreDirectory(levelset.identifier);
9465 scores.file_version = FILE_VERSION_ACTUAL;
9466 scores.game_version = GAME_VERSION_ACTUAL;
9468 strncpy(scores.level_identifier, levelset.identifier, MAX_FILENAME_LEN);
9469 scores.level_identifier[MAX_FILENAME_LEN] = '\0';
9470 scores.level_nr = level_nr;
9472 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9473 if (scores.entry[i].score == 0 &&
9474 scores.entry[i].time == 0 &&
9475 strEqual(scores.entry[i].name, EMPTY_PLAYER_NAME))
9478 scores.num_entries = i;
9480 if (scores.num_entries == 0)
9483 SaveScoreToFilename(filename);
9486 static void LoadServerScoreFromCache(int nr)
9488 struct ScoreEntry score_entry;
9497 { &score_entry.score, FALSE, 0 },
9498 { &score_entry.time, FALSE, 0 },
9499 { score_entry.name, TRUE, MAX_PLAYER_NAME_LEN },
9500 { score_entry.tape_basename, TRUE, MAX_FILENAME_LEN },
9501 { score_entry.tape_date, TRUE, MAX_ISO_DATE_LEN },
9502 { &score_entry.id, FALSE, 0 },
9503 { score_entry.platform, TRUE, MAX_PLATFORM_TEXT_LEN },
9504 { score_entry.version, TRUE, MAX_VERSION_TEXT_LEN },
9505 { score_entry.country_code, TRUE, MAX_COUNTRY_CODE_LEN },
9506 { score_entry.country_name, TRUE, MAX_COUNTRY_NAME_LEN },
9510 char *filename = getScoreCacheFilename(nr);
9511 SetupFileHash *score_hash = loadSetupFileHash(filename);
9514 server_scores.num_entries = 0;
9516 if (score_hash == NULL)
9519 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9521 score_entry = server_scores.entry[i];
9523 for (j = 0; score_mapping[j].value != NULL; j++)
9527 sprintf(token, "%02d.%d", i, j);
9529 char *value = getHashEntry(score_hash, token);
9534 if (score_mapping[j].is_string)
9536 char *score_value = (char *)score_mapping[j].value;
9537 int value_size = score_mapping[j].string_size;
9539 strncpy(score_value, value, value_size);
9540 score_value[value_size] = '\0';
9544 int *score_value = (int *)score_mapping[j].value;
9546 *score_value = atoi(value);
9549 server_scores.num_entries = i + 1;
9552 server_scores.entry[i] = score_entry;
9555 freeSetupFileHash(score_hash);
9558 void LoadServerScore(int nr, boolean download_score)
9560 if (!setup.use_api_server)
9563 // always start with reliable default values
9564 setServerScoreInfoToDefaults();
9566 // 1st step: load server scores from cache file (which may not exist)
9567 // (this should prevent reading it while the thread is writing to it)
9568 LoadServerScoreFromCache(nr);
9570 if (download_score && runtime.use_api_server)
9572 // 2nd step: download server scores from score server to cache file
9573 // (as thread, as it might time out if the server is not reachable)
9574 ApiGetScoreAsThread(nr);
9578 void PrepareScoreTapesForUpload(char *leveldir_subdir)
9580 MarkTapeDirectoryUploadsAsIncomplete(leveldir_subdir);
9582 // if score tape not uploaded, ask for uploading missing tapes later
9583 if (!setup.has_remaining_tapes)
9584 setup.ask_for_remaining_tapes = TRUE;
9586 setup.provide_uploading_tapes = TRUE;
9587 setup.has_remaining_tapes = TRUE;
9589 SaveSetup_ServerSetup();
9592 void SaveServerScore(int nr, boolean tape_saved)
9594 if (!runtime.use_api_server)
9596 PrepareScoreTapesForUpload(leveldir_current->subdir);
9601 ApiAddScoreAsThread(nr, tape_saved, NULL);
9604 void SaveServerScoreFromFile(int nr, boolean tape_saved,
9605 char *score_tape_filename)
9607 if (!runtime.use_api_server)
9610 ApiAddScoreAsThread(nr, tape_saved, score_tape_filename);
9613 void LoadLocalAndServerScore(int nr, boolean download_score)
9615 int last_added_local = scores.last_added_local;
9616 boolean force_last_added = scores.force_last_added;
9618 // needed if only showing server scores
9619 setScoreInfoToDefaults();
9621 if (!strEqual(setup.scores_in_highscore_list, STR_SCORES_TYPE_SERVER_ONLY))
9624 // restore last added local score entry (before merging server scores)
9625 scores.last_added = scores.last_added_local = last_added_local;
9627 if (setup.use_api_server &&
9628 !strEqual(setup.scores_in_highscore_list, STR_SCORES_TYPE_LOCAL_ONLY))
9630 // load server scores from cache file and trigger update from server
9631 LoadServerScore(nr, download_score);
9633 // merge local scores with scores from server
9637 if (force_last_added)
9638 scores.force_last_added = force_last_added;
9642 // ============================================================================
9643 // setup file functions
9644 // ============================================================================
9646 #define TOKEN_STR_PLAYER_PREFIX "player_"
9649 static struct TokenInfo global_setup_tokens[] =
9653 &setup.player_name, "player_name"
9657 &setup.multiple_users, "multiple_users"
9661 &setup.sound, "sound"
9665 &setup.sound_loops, "repeating_sound_loops"
9669 &setup.sound_music, "background_music"
9673 &setup.sound_simple, "simple_sound_effects"
9677 &setup.toons, "toons"
9681 &setup.global_animations, "global_animations"
9685 &setup.scroll_delay, "scroll_delay"
9689 &setup.forced_scroll_delay, "forced_scroll_delay"
9693 &setup.scroll_delay_value, "scroll_delay_value"
9697 &setup.engine_snapshot_mode, "engine_snapshot_mode"
9701 &setup.engine_snapshot_memory, "engine_snapshot_memory"
9705 &setup.fade_screens, "fade_screens"
9709 &setup.autorecord, "automatic_tape_recording"
9713 &setup.autorecord_after_replay, "autorecord_after_replay"
9717 &setup.auto_pause_on_start, "auto_pause_on_start"
9721 &setup.show_titlescreen, "show_titlescreen"
9725 &setup.quick_doors, "quick_doors"
9729 &setup.team_mode, "team_mode"
9733 &setup.handicap, "handicap"
9737 &setup.skip_levels, "skip_levels"
9741 &setup.increment_levels, "increment_levels"
9745 &setup.auto_play_next_level, "auto_play_next_level"
9749 &setup.count_score_after_game, "count_score_after_game"
9753 &setup.show_scores_after_game, "show_scores_after_game"
9757 &setup.time_limit, "time_limit"
9761 &setup.fullscreen, "fullscreen"
9765 &setup.window_scaling_percent, "window_scaling_percent"
9769 &setup.window_scaling_quality, "window_scaling_quality"
9773 &setup.screen_rendering_mode, "screen_rendering_mode"
9777 &setup.vsync_mode, "vsync_mode"
9781 &setup.ask_on_escape, "ask_on_escape"
9785 &setup.ask_on_escape_editor, "ask_on_escape_editor"
9789 &setup.ask_on_game_over, "ask_on_game_over"
9793 &setup.ask_on_quit_game, "ask_on_quit_game"
9797 &setup.ask_on_quit_program, "ask_on_quit_program"
9801 &setup.quick_switch, "quick_player_switch"
9805 &setup.input_on_focus, "input_on_focus"
9809 &setup.prefer_aga_graphics, "prefer_aga_graphics"
9813 &setup.prefer_lowpass_sounds, "prefer_lowpass_sounds"
9817 &setup.prefer_extra_panel_items, "prefer_extra_panel_items"
9821 &setup.game_speed_extended, "game_speed_extended"
9825 &setup.game_frame_delay, "game_frame_delay"
9829 &setup.bd_skip_uncovering, "bd_skip_uncovering"
9833 &setup.bd_skip_hatching, "bd_skip_hatching"
9837 &setup.bd_scroll_delay, "bd_scroll_delay"
9841 &setup.bd_smooth_movements, "bd_smooth_movements"
9845 &setup.sp_show_border_elements, "sp_show_border_elements"
9849 &setup.small_game_graphics, "small_game_graphics"
9853 &setup.show_load_save_buttons, "show_load_save_buttons"
9857 &setup.show_undo_redo_buttons, "show_undo_redo_buttons"
9861 &setup.scores_in_highscore_list, "scores_in_highscore_list"
9865 &setup.graphics_set, "graphics_set"
9869 &setup.sounds_set, "sounds_set"
9873 &setup.music_set, "music_set"
9877 &setup.override_level_graphics, "override_level_graphics"
9881 &setup.override_level_sounds, "override_level_sounds"
9885 &setup.override_level_music, "override_level_music"
9889 &setup.volume_simple, "volume_simple"
9893 &setup.volume_loops, "volume_loops"
9897 &setup.volume_music, "volume_music"
9901 &setup.network_mode, "network_mode"
9905 &setup.network_player_nr, "network_player"
9909 &setup.network_server_hostname, "network_server_hostname"
9913 &setup.touch.control_type, "touch.control_type"
9917 &setup.touch.move_distance, "touch.move_distance"
9921 &setup.touch.drop_distance, "touch.drop_distance"
9925 &setup.touch.transparency, "touch.transparency"
9929 &setup.touch.draw_outlined, "touch.draw_outlined"
9933 &setup.touch.draw_pressed, "touch.draw_pressed"
9937 &setup.touch.grid_xsize[0], "touch.virtual_buttons.0.xsize"
9941 &setup.touch.grid_ysize[0], "touch.virtual_buttons.0.ysize"
9945 &setup.touch.grid_xsize[1], "touch.virtual_buttons.1.xsize"
9949 &setup.touch.grid_ysize[1], "touch.virtual_buttons.1.ysize"
9953 &setup.touch.overlay_buttons, "touch.overlay_buttons"
9957 static struct TokenInfo auto_setup_tokens[] =
9961 &setup.auto_setup.editor_zoom_tilesize, "editor.zoom_tilesize"
9965 static struct TokenInfo server_setup_tokens[] =
9969 &setup.player_uuid, "player_uuid"
9973 &setup.player_version, "player_version"
9977 &setup.use_api_server, TEST_PREFIX "use_api_server"
9981 &setup.api_server_hostname, TEST_PREFIX "api_server_hostname"
9985 &setup.api_server_password, TEST_PREFIX "api_server_password"
9989 &setup.ask_for_uploading_tapes, TEST_PREFIX "ask_for_uploading_tapes"
9993 &setup.ask_for_remaining_tapes, TEST_PREFIX "ask_for_remaining_tapes"
9997 &setup.provide_uploading_tapes, TEST_PREFIX "provide_uploading_tapes"
10001 &setup.ask_for_using_api_server,TEST_PREFIX "ask_for_using_api_server"
10005 &setup.has_remaining_tapes, TEST_PREFIX "has_remaining_tapes"
10009 static struct TokenInfo editor_setup_tokens[] =
10013 &setup.editor.el_classic, "editor.el_classic"
10017 &setup.editor.el_custom, "editor.el_custom"
10021 &setup.editor.el_user_defined, "editor.el_user_defined"
10025 &setup.editor.el_dynamic, "editor.el_dynamic"
10029 &setup.editor.el_headlines, "editor.el_headlines"
10033 &setup.editor.show_element_token, "editor.show_element_token"
10037 &setup.editor.show_read_only_warning, "editor.show_read_only_warning"
10041 static struct TokenInfo editor_cascade_setup_tokens[] =
10045 &setup.editor_cascade.el_bd, "editor.cascade.el_bd"
10049 &setup.editor_cascade.el_bd_native, "editor.cascade.el_bd_native"
10053 &setup.editor_cascade.el_em, "editor.cascade.el_em"
10057 &setup.editor_cascade.el_emc, "editor.cascade.el_emc"
10061 &setup.editor_cascade.el_rnd, "editor.cascade.el_rnd"
10065 &setup.editor_cascade.el_sb, "editor.cascade.el_sb"
10069 &setup.editor_cascade.el_sp, "editor.cascade.el_sp"
10073 &setup.editor_cascade.el_dc, "editor.cascade.el_dc"
10077 &setup.editor_cascade.el_dx, "editor.cascade.el_dx"
10081 &setup.editor_cascade.el_mm, "editor.cascade.el_mm"
10085 &setup.editor_cascade.el_df, "editor.cascade.el_df"
10089 &setup.editor_cascade.el_chars, "editor.cascade.el_chars"
10093 &setup.editor_cascade.el_steel_chars, "editor.cascade.el_steel_chars"
10097 &setup.editor_cascade.el_ce, "editor.cascade.el_ce"
10101 &setup.editor_cascade.el_ge, "editor.cascade.el_ge"
10105 &setup.editor_cascade.el_es, "editor.cascade.el_es"
10109 &setup.editor_cascade.el_ref, "editor.cascade.el_ref"
10113 &setup.editor_cascade.el_user, "editor.cascade.el_user"
10117 &setup.editor_cascade.el_dynamic, "editor.cascade.el_dynamic"
10121 static struct TokenInfo shortcut_setup_tokens[] =
10125 &setup.shortcut.save_game, "shortcut.save_game"
10129 &setup.shortcut.load_game, "shortcut.load_game"
10133 &setup.shortcut.restart_game, "shortcut.restart_game"
10137 &setup.shortcut.pause_before_end, "shortcut.pause_before_end"
10141 &setup.shortcut.toggle_pause, "shortcut.toggle_pause"
10145 &setup.shortcut.focus_player[0], "shortcut.focus_player_1"
10149 &setup.shortcut.focus_player[1], "shortcut.focus_player_2"
10153 &setup.shortcut.focus_player[2], "shortcut.focus_player_3"
10157 &setup.shortcut.focus_player[3], "shortcut.focus_player_4"
10161 &setup.shortcut.focus_player_all, "shortcut.focus_player_all"
10165 &setup.shortcut.tape_eject, "shortcut.tape_eject"
10169 &setup.shortcut.tape_extra, "shortcut.tape_extra"
10173 &setup.shortcut.tape_stop, "shortcut.tape_stop"
10177 &setup.shortcut.tape_pause, "shortcut.tape_pause"
10181 &setup.shortcut.tape_record, "shortcut.tape_record"
10185 &setup.shortcut.tape_play, "shortcut.tape_play"
10189 &setup.shortcut.sound_simple, "shortcut.sound_simple"
10193 &setup.shortcut.sound_loops, "shortcut.sound_loops"
10197 &setup.shortcut.sound_music, "shortcut.sound_music"
10201 &setup.shortcut.snap_left, "shortcut.snap_left"
10205 &setup.shortcut.snap_right, "shortcut.snap_right"
10209 &setup.shortcut.snap_up, "shortcut.snap_up"
10213 &setup.shortcut.snap_down, "shortcut.snap_down"
10217 static struct SetupInputInfo setup_input;
10218 static struct TokenInfo player_setup_tokens[] =
10222 &setup_input.use_joystick, ".use_joystick"
10226 &setup_input.joy.device_name, ".joy.device_name"
10230 &setup_input.joy.xleft, ".joy.xleft"
10234 &setup_input.joy.xmiddle, ".joy.xmiddle"
10238 &setup_input.joy.xright, ".joy.xright"
10242 &setup_input.joy.yupper, ".joy.yupper"
10246 &setup_input.joy.ymiddle, ".joy.ymiddle"
10250 &setup_input.joy.ylower, ".joy.ylower"
10254 &setup_input.joy.snap, ".joy.snap_field"
10258 &setup_input.joy.drop, ".joy.place_bomb"
10262 &setup_input.key.left, ".key.move_left"
10266 &setup_input.key.right, ".key.move_right"
10270 &setup_input.key.up, ".key.move_up"
10274 &setup_input.key.down, ".key.move_down"
10278 &setup_input.key.snap, ".key.snap_field"
10282 &setup_input.key.drop, ".key.place_bomb"
10286 static struct TokenInfo system_setup_tokens[] =
10290 &setup.system.sdl_renderdriver, "system.sdl_renderdriver"
10294 &setup.system.sdl_videodriver, "system.sdl_videodriver"
10298 &setup.system.sdl_audiodriver, "system.sdl_audiodriver"
10302 &setup.system.audio_fragment_size, "system.audio_fragment_size"
10306 static struct TokenInfo internal_setup_tokens[] =
10310 &setup.internal.program_title, "program_title"
10314 &setup.internal.program_version, "program_version"
10318 &setup.internal.program_author, "program_author"
10322 &setup.internal.program_email, "program_email"
10326 &setup.internal.program_website, "program_website"
10330 &setup.internal.program_copyright, "program_copyright"
10334 &setup.internal.program_company, "program_company"
10338 &setup.internal.program_icon_file, "program_icon_file"
10342 &setup.internal.default_graphics_set, "default_graphics_set"
10346 &setup.internal.default_sounds_set, "default_sounds_set"
10350 &setup.internal.default_music_set, "default_music_set"
10354 &setup.internal.fallback_graphics_file, "fallback_graphics_file"
10358 &setup.internal.fallback_sounds_file, "fallback_sounds_file"
10362 &setup.internal.fallback_music_file, "fallback_music_file"
10366 &setup.internal.default_level_series, "default_level_series"
10370 &setup.internal.default_window_width, "default_window_width"
10374 &setup.internal.default_window_height, "default_window_height"
10378 &setup.internal.choose_from_top_leveldir, "choose_from_top_leveldir"
10382 &setup.internal.show_scaling_in_title, "show_scaling_in_title"
10386 &setup.internal.create_user_levelset, "create_user_levelset"
10390 &setup.internal.info_screens_from_main, "info_screens_from_main"
10394 &setup.internal.menu_game, "menu_game"
10398 &setup.internal.menu_engines, "menu_engines"
10402 &setup.internal.menu_editor, "menu_editor"
10406 &setup.internal.menu_graphics, "menu_graphics"
10410 &setup.internal.menu_sound, "menu_sound"
10414 &setup.internal.menu_artwork, "menu_artwork"
10418 &setup.internal.menu_input, "menu_input"
10422 &setup.internal.menu_touch, "menu_touch"
10426 &setup.internal.menu_shortcuts, "menu_shortcuts"
10430 &setup.internal.menu_exit, "menu_exit"
10434 &setup.internal.menu_save_and_exit, "menu_save_and_exit"
10438 &setup.internal.menu_shortcuts_various, "menu_shortcuts_various"
10442 &setup.internal.menu_shortcuts_focus, "menu_shortcuts_focus"
10446 &setup.internal.menu_shortcuts_tape, "menu_shortcuts_tape"
10450 &setup.internal.menu_shortcuts_sound, "menu_shortcuts_sound"
10454 &setup.internal.menu_shortcuts_snap, "menu_shortcuts_snap"
10458 &setup.internal.info_title, "info_title"
10462 &setup.internal.info_elements, "info_elements"
10466 &setup.internal.info_music, "info_music"
10470 &setup.internal.info_credits, "info_credits"
10474 &setup.internal.info_program, "info_program"
10478 &setup.internal.info_version, "info_version"
10482 &setup.internal.info_levelset, "info_levelset"
10486 &setup.internal.info_exit, "info_exit"
10490 static struct TokenInfo debug_setup_tokens[] =
10494 &setup.debug.frame_delay[0], "debug.frame_delay_0"
10498 &setup.debug.frame_delay[1], "debug.frame_delay_1"
10502 &setup.debug.frame_delay[2], "debug.frame_delay_2"
10506 &setup.debug.frame_delay[3], "debug.frame_delay_3"
10510 &setup.debug.frame_delay[4], "debug.frame_delay_4"
10514 &setup.debug.frame_delay[5], "debug.frame_delay_5"
10518 &setup.debug.frame_delay[6], "debug.frame_delay_6"
10522 &setup.debug.frame_delay[7], "debug.frame_delay_7"
10526 &setup.debug.frame_delay[8], "debug.frame_delay_8"
10530 &setup.debug.frame_delay[9], "debug.frame_delay_9"
10534 &setup.debug.frame_delay_key[0], "debug.key.frame_delay_0"
10538 &setup.debug.frame_delay_key[1], "debug.key.frame_delay_1"
10542 &setup.debug.frame_delay_key[2], "debug.key.frame_delay_2"
10546 &setup.debug.frame_delay_key[3], "debug.key.frame_delay_3"
10550 &setup.debug.frame_delay_key[4], "debug.key.frame_delay_4"
10554 &setup.debug.frame_delay_key[5], "debug.key.frame_delay_5"
10558 &setup.debug.frame_delay_key[6], "debug.key.frame_delay_6"
10562 &setup.debug.frame_delay_key[7], "debug.key.frame_delay_7"
10566 &setup.debug.frame_delay_key[8], "debug.key.frame_delay_8"
10570 &setup.debug.frame_delay_key[9], "debug.key.frame_delay_9"
10574 &setup.debug.frame_delay_use_mod_key, "debug.frame_delay.use_mod_key"},
10577 &setup.debug.frame_delay_game_only, "debug.frame_delay.game_only"
10581 &setup.debug.show_frames_per_second, "debug.show_frames_per_second"
10585 &setup.debug.xsn_mode, "debug.xsn_mode"
10589 &setup.debug.xsn_percent, "debug.xsn_percent"
10593 static struct TokenInfo options_setup_tokens[] =
10597 &setup.options.verbose, "options.verbose"
10601 &setup.options.debug, "options.debug"
10605 &setup.options.debug_mode, "options.debug_mode"
10609 static void setSetupInfoToDefaults(struct SetupInfo *si)
10613 si->player_name = getStringCopy(getDefaultUserName(user.nr));
10615 si->multiple_users = TRUE;
10618 si->sound_loops = TRUE;
10619 si->sound_music = TRUE;
10620 si->sound_simple = TRUE;
10622 si->global_animations = TRUE;
10623 si->scroll_delay = TRUE;
10624 si->forced_scroll_delay = FALSE;
10625 si->scroll_delay_value = STD_SCROLL_DELAY;
10626 si->engine_snapshot_mode = getStringCopy(STR_SNAPSHOT_MODE_DEFAULT);
10627 si->engine_snapshot_memory = SNAPSHOT_MEMORY_DEFAULT;
10628 si->fade_screens = TRUE;
10629 si->autorecord = TRUE;
10630 si->autorecord_after_replay = TRUE;
10631 si->auto_pause_on_start = FALSE;
10632 si->show_titlescreen = TRUE;
10633 si->quick_doors = FALSE;
10634 si->team_mode = FALSE;
10635 si->handicap = TRUE;
10636 si->skip_levels = TRUE;
10637 si->increment_levels = TRUE;
10638 si->auto_play_next_level = TRUE;
10639 si->count_score_after_game = TRUE;
10640 si->show_scores_after_game = TRUE;
10641 si->time_limit = TRUE;
10642 si->fullscreen = FALSE;
10643 si->window_scaling_percent = STD_WINDOW_SCALING_PERCENT;
10644 si->window_scaling_quality = getStringCopy(SCALING_QUALITY_DEFAULT);
10645 si->screen_rendering_mode = getStringCopy(STR_SPECIAL_RENDERING_DEFAULT);
10646 si->vsync_mode = getStringCopy(STR_VSYNC_MODE_DEFAULT);
10647 si->ask_on_escape = TRUE;
10648 si->ask_on_escape_editor = TRUE;
10649 si->ask_on_game_over = TRUE;
10650 si->ask_on_quit_game = TRUE;
10651 si->ask_on_quit_program = TRUE;
10652 si->quick_switch = FALSE;
10653 si->input_on_focus = FALSE;
10654 si->prefer_aga_graphics = TRUE;
10655 si->prefer_lowpass_sounds = FALSE;
10656 si->prefer_extra_panel_items = TRUE;
10657 si->game_speed_extended = FALSE;
10658 si->game_frame_delay = GAME_FRAME_DELAY;
10659 si->bd_skip_uncovering = FALSE;
10660 si->bd_skip_hatching = FALSE;
10661 si->bd_scroll_delay = TRUE;
10662 si->bd_smooth_movements = AUTO;
10663 si->sp_show_border_elements = FALSE;
10664 si->small_game_graphics = FALSE;
10665 si->show_load_save_buttons = FALSE;
10666 si->show_undo_redo_buttons = FALSE;
10667 si->scores_in_highscore_list = getStringCopy(STR_SCORES_TYPE_DEFAULT);
10669 si->graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
10670 si->sounds_set = getStringCopy(SND_CLASSIC_SUBDIR);
10671 si->music_set = getStringCopy(MUS_CLASSIC_SUBDIR);
10673 si->override_level_graphics = FALSE;
10674 si->override_level_sounds = FALSE;
10675 si->override_level_music = FALSE;
10677 si->volume_simple = 100; // percent
10678 si->volume_loops = 100; // percent
10679 si->volume_music = 100; // percent
10681 si->network_mode = FALSE;
10682 si->network_player_nr = 0; // first player
10683 si->network_server_hostname = getStringCopy(STR_NETWORK_AUTO_DETECT);
10685 si->touch.control_type = getStringCopy(TOUCH_CONTROL_DEFAULT);
10686 si->touch.move_distance = TOUCH_MOVE_DISTANCE_DEFAULT; // percent
10687 si->touch.drop_distance = TOUCH_DROP_DISTANCE_DEFAULT; // percent
10688 si->touch.transparency = TOUCH_TRANSPARENCY_DEFAULT; // percent
10689 si->touch.draw_outlined = TRUE;
10690 si->touch.draw_pressed = TRUE;
10692 for (i = 0; i < 2; i++)
10694 char *default_grid_button[6][2] =
10700 { "111222", " vv " },
10701 { "111222", " vv " }
10703 int grid_xsize = DEFAULT_GRID_XSIZE(i);
10704 int grid_ysize = DEFAULT_GRID_YSIZE(i);
10705 int min_xsize = MIN(6, grid_xsize);
10706 int min_ysize = MIN(6, grid_ysize);
10707 int startx = grid_xsize - min_xsize;
10708 int starty = grid_ysize - min_ysize;
10711 // virtual buttons grid can only be set to defaults if video is initialized
10712 // (this will be repeated if virtual buttons are not loaded from setup file)
10713 if (video.initialized)
10715 si->touch.grid_xsize[i] = grid_xsize;
10716 si->touch.grid_ysize[i] = grid_ysize;
10720 si->touch.grid_xsize[i] = -1;
10721 si->touch.grid_ysize[i] = -1;
10724 for (x = 0; x < MAX_GRID_XSIZE; x++)
10725 for (y = 0; y < MAX_GRID_YSIZE; y++)
10726 si->touch.grid_button[i][x][y] = CHAR_GRID_BUTTON_NONE;
10728 for (x = 0; x < min_xsize; x++)
10729 for (y = 0; y < min_ysize; y++)
10730 si->touch.grid_button[i][x][starty + y] =
10731 default_grid_button[y][0][x];
10733 for (x = 0; x < min_xsize; x++)
10734 for (y = 0; y < min_ysize; y++)
10735 si->touch.grid_button[i][startx + x][starty + y] =
10736 default_grid_button[y][1][x];
10739 si->touch.grid_initialized = video.initialized;
10741 si->touch.overlay_buttons = FALSE;
10743 si->editor.el_boulderdash = TRUE;
10744 si->editor.el_boulderdash_native = TRUE;
10745 si->editor.el_emerald_mine = TRUE;
10746 si->editor.el_emerald_mine_club = TRUE;
10747 si->editor.el_more = TRUE;
10748 si->editor.el_sokoban = TRUE;
10749 si->editor.el_supaplex = TRUE;
10750 si->editor.el_diamond_caves = TRUE;
10751 si->editor.el_dx_boulderdash = TRUE;
10753 si->editor.el_mirror_magic = TRUE;
10754 si->editor.el_deflektor = TRUE;
10756 si->editor.el_chars = TRUE;
10757 si->editor.el_steel_chars = TRUE;
10759 si->editor.el_classic = TRUE;
10760 si->editor.el_custom = TRUE;
10762 si->editor.el_user_defined = FALSE;
10763 si->editor.el_dynamic = TRUE;
10765 si->editor.el_headlines = TRUE;
10767 si->editor.show_element_token = FALSE;
10769 si->editor.show_read_only_warning = TRUE;
10771 si->editor.use_template_for_new_levels = TRUE;
10773 si->shortcut.save_game = DEFAULT_KEY_SAVE_GAME;
10774 si->shortcut.load_game = DEFAULT_KEY_LOAD_GAME;
10775 si->shortcut.restart_game = DEFAULT_KEY_RESTART_GAME;
10776 si->shortcut.pause_before_end = DEFAULT_KEY_PAUSE_BEFORE_END;
10777 si->shortcut.toggle_pause = DEFAULT_KEY_TOGGLE_PAUSE;
10779 si->shortcut.focus_player[0] = DEFAULT_KEY_FOCUS_PLAYER_1;
10780 si->shortcut.focus_player[1] = DEFAULT_KEY_FOCUS_PLAYER_2;
10781 si->shortcut.focus_player[2] = DEFAULT_KEY_FOCUS_PLAYER_3;
10782 si->shortcut.focus_player[3] = DEFAULT_KEY_FOCUS_PLAYER_4;
10783 si->shortcut.focus_player_all = DEFAULT_KEY_FOCUS_PLAYER_ALL;
10785 si->shortcut.tape_eject = DEFAULT_KEY_TAPE_EJECT;
10786 si->shortcut.tape_extra = DEFAULT_KEY_TAPE_EXTRA;
10787 si->shortcut.tape_stop = DEFAULT_KEY_TAPE_STOP;
10788 si->shortcut.tape_pause = DEFAULT_KEY_TAPE_PAUSE;
10789 si->shortcut.tape_record = DEFAULT_KEY_TAPE_RECORD;
10790 si->shortcut.tape_play = DEFAULT_KEY_TAPE_PLAY;
10792 si->shortcut.sound_simple = DEFAULT_KEY_SOUND_SIMPLE;
10793 si->shortcut.sound_loops = DEFAULT_KEY_SOUND_LOOPS;
10794 si->shortcut.sound_music = DEFAULT_KEY_SOUND_MUSIC;
10796 si->shortcut.snap_left = DEFAULT_KEY_SNAP_LEFT;
10797 si->shortcut.snap_right = DEFAULT_KEY_SNAP_RIGHT;
10798 si->shortcut.snap_up = DEFAULT_KEY_SNAP_UP;
10799 si->shortcut.snap_down = DEFAULT_KEY_SNAP_DOWN;
10801 for (i = 0; i < MAX_PLAYERS; i++)
10803 si->input[i].use_joystick = FALSE;
10804 si->input[i].joy.device_name = getStringCopy(getDeviceNameFromJoystickNr(i));
10805 si->input[i].joy.xleft = JOYSTICK_XLEFT;
10806 si->input[i].joy.xmiddle = JOYSTICK_XMIDDLE;
10807 si->input[i].joy.xright = JOYSTICK_XRIGHT;
10808 si->input[i].joy.yupper = JOYSTICK_YUPPER;
10809 si->input[i].joy.ymiddle = JOYSTICK_YMIDDLE;
10810 si->input[i].joy.ylower = JOYSTICK_YLOWER;
10811 si->input[i].joy.snap = (i == 0 ? JOY_BUTTON_1 : 0);
10812 si->input[i].joy.drop = (i == 0 ? JOY_BUTTON_2 : 0);
10813 si->input[i].key.left = (i == 0 ? DEFAULT_KEY_LEFT : KSYM_UNDEFINED);
10814 si->input[i].key.right = (i == 0 ? DEFAULT_KEY_RIGHT : KSYM_UNDEFINED);
10815 si->input[i].key.up = (i == 0 ? DEFAULT_KEY_UP : KSYM_UNDEFINED);
10816 si->input[i].key.down = (i == 0 ? DEFAULT_KEY_DOWN : KSYM_UNDEFINED);
10817 si->input[i].key.snap = (i == 0 ? DEFAULT_KEY_SNAP : KSYM_UNDEFINED);
10818 si->input[i].key.drop = (i == 0 ? DEFAULT_KEY_DROP : KSYM_UNDEFINED);
10821 si->system.sdl_renderdriver = getStringCopy(ARG_DEFAULT);
10822 si->system.sdl_videodriver = getStringCopy(ARG_DEFAULT);
10823 si->system.sdl_audiodriver = getStringCopy(ARG_DEFAULT);
10824 si->system.audio_fragment_size = DEFAULT_AUDIO_FRAGMENT_SIZE;
10826 si->internal.program_title = getStringCopy(PROGRAM_TITLE_STRING);
10827 si->internal.program_version = getStringCopy(getProgramRealVersionString());
10828 si->internal.program_author = getStringCopy(PROGRAM_AUTHOR_STRING);
10829 si->internal.program_email = getStringCopy(PROGRAM_EMAIL_STRING);
10830 si->internal.program_website = getStringCopy(PROGRAM_WEBSITE_STRING);
10831 si->internal.program_copyright = getStringCopy(PROGRAM_COPYRIGHT_STRING);
10832 si->internal.program_company = getStringCopy(PROGRAM_COMPANY_STRING);
10834 si->internal.program_icon_file = getStringCopy(PROGRAM_ICON_FILENAME);
10836 si->internal.default_graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
10837 si->internal.default_sounds_set = getStringCopy(SND_CLASSIC_SUBDIR);
10838 si->internal.default_music_set = getStringCopy(MUS_CLASSIC_SUBDIR);
10840 si->internal.fallback_graphics_file = getStringCopy(UNDEFINED_FILENAME);
10841 si->internal.fallback_sounds_file = getStringCopy(UNDEFINED_FILENAME);
10842 si->internal.fallback_music_file = getStringCopy(UNDEFINED_FILENAME);
10844 si->internal.default_level_series = getStringCopy(UNDEFINED_LEVELSET);
10845 si->internal.choose_from_top_leveldir = FALSE;
10846 si->internal.show_scaling_in_title = TRUE;
10847 si->internal.create_user_levelset = TRUE;
10848 si->internal.info_screens_from_main = FALSE;
10850 si->internal.default_window_width = WIN_XSIZE_DEFAULT;
10851 si->internal.default_window_height = WIN_YSIZE_DEFAULT;
10853 si->debug.frame_delay[0] = DEFAULT_FRAME_DELAY_0;
10854 si->debug.frame_delay[1] = DEFAULT_FRAME_DELAY_1;
10855 si->debug.frame_delay[2] = DEFAULT_FRAME_DELAY_2;
10856 si->debug.frame_delay[3] = DEFAULT_FRAME_DELAY_3;
10857 si->debug.frame_delay[4] = DEFAULT_FRAME_DELAY_4;
10858 si->debug.frame_delay[5] = DEFAULT_FRAME_DELAY_5;
10859 si->debug.frame_delay[6] = DEFAULT_FRAME_DELAY_6;
10860 si->debug.frame_delay[7] = DEFAULT_FRAME_DELAY_7;
10861 si->debug.frame_delay[8] = DEFAULT_FRAME_DELAY_8;
10862 si->debug.frame_delay[9] = DEFAULT_FRAME_DELAY_9;
10864 si->debug.frame_delay_key[0] = DEFAULT_KEY_FRAME_DELAY_0;
10865 si->debug.frame_delay_key[1] = DEFAULT_KEY_FRAME_DELAY_1;
10866 si->debug.frame_delay_key[2] = DEFAULT_KEY_FRAME_DELAY_2;
10867 si->debug.frame_delay_key[3] = DEFAULT_KEY_FRAME_DELAY_3;
10868 si->debug.frame_delay_key[4] = DEFAULT_KEY_FRAME_DELAY_4;
10869 si->debug.frame_delay_key[5] = DEFAULT_KEY_FRAME_DELAY_5;
10870 si->debug.frame_delay_key[6] = DEFAULT_KEY_FRAME_DELAY_6;
10871 si->debug.frame_delay_key[7] = DEFAULT_KEY_FRAME_DELAY_7;
10872 si->debug.frame_delay_key[8] = DEFAULT_KEY_FRAME_DELAY_8;
10873 si->debug.frame_delay_key[9] = DEFAULT_KEY_FRAME_DELAY_9;
10875 si->debug.frame_delay_use_mod_key = DEFAULT_FRAME_DELAY_USE_MOD_KEY;
10876 si->debug.frame_delay_game_only = DEFAULT_FRAME_DELAY_GAME_ONLY;
10878 si->debug.show_frames_per_second = FALSE;
10880 si->debug.xsn_mode = AUTO;
10881 si->debug.xsn_percent = 0;
10883 si->options.verbose = FALSE;
10884 si->options.debug = FALSE;
10885 si->options.debug_mode = getStringCopy(ARG_UNDEFINED_STRING);
10887 #if defined(PLATFORM_ANDROID)
10888 si->fullscreen = TRUE;
10889 si->touch.overlay_buttons = TRUE;
10892 setHideSetupEntry(&setup.debug.xsn_mode);
10895 static void setSetupInfoToDefaults_AutoSetup(struct SetupInfo *si)
10897 si->auto_setup.editor_zoom_tilesize = MINI_TILESIZE;
10900 static void setSetupInfoToDefaults_ServerSetup(struct SetupInfo *si)
10902 si->player_uuid = NULL; // (will be set later)
10903 si->player_version = 1; // (will be set later)
10905 si->use_api_server = TRUE;
10906 si->api_server_hostname = getStringCopy(API_SERVER_HOSTNAME);
10907 si->api_server_password = getStringCopy(UNDEFINED_PASSWORD);
10908 si->ask_for_uploading_tapes = TRUE;
10909 si->ask_for_remaining_tapes = FALSE;
10910 si->provide_uploading_tapes = TRUE;
10911 si->ask_for_using_api_server = TRUE;
10912 si->has_remaining_tapes = FALSE;
10915 static void setSetupInfoToDefaults_EditorCascade(struct SetupInfo *si)
10917 si->editor_cascade.el_bd = TRUE;
10918 si->editor_cascade.el_bd_native = TRUE;
10919 si->editor_cascade.el_em = TRUE;
10920 si->editor_cascade.el_emc = TRUE;
10921 si->editor_cascade.el_rnd = TRUE;
10922 si->editor_cascade.el_sb = TRUE;
10923 si->editor_cascade.el_sp = TRUE;
10924 si->editor_cascade.el_dc = TRUE;
10925 si->editor_cascade.el_dx = TRUE;
10927 si->editor_cascade.el_mm = TRUE;
10928 si->editor_cascade.el_df = TRUE;
10930 si->editor_cascade.el_chars = FALSE;
10931 si->editor_cascade.el_steel_chars = FALSE;
10932 si->editor_cascade.el_ce = FALSE;
10933 si->editor_cascade.el_ge = FALSE;
10934 si->editor_cascade.el_es = FALSE;
10935 si->editor_cascade.el_ref = FALSE;
10936 si->editor_cascade.el_user = FALSE;
10937 si->editor_cascade.el_dynamic = FALSE;
10940 #define MAX_HIDE_SETUP_TOKEN_SIZE 20
10942 static char *getHideSetupToken(void *setup_value)
10944 static char hide_setup_token[MAX_HIDE_SETUP_TOKEN_SIZE];
10946 if (setup_value != NULL)
10947 snprintf(hide_setup_token, MAX_HIDE_SETUP_TOKEN_SIZE, "%p", setup_value);
10949 return hide_setup_token;
10952 void setHideSetupEntry(void *setup_value)
10954 char *hide_setup_token = getHideSetupToken(setup_value);
10956 if (hide_setup_hash == NULL)
10957 hide_setup_hash = newSetupFileHash();
10959 if (setup_value != NULL)
10960 setHashEntry(hide_setup_hash, hide_setup_token, "");
10963 void removeHideSetupEntry(void *setup_value)
10965 char *hide_setup_token = getHideSetupToken(setup_value);
10967 if (setup_value != NULL)
10968 removeHashEntry(hide_setup_hash, hide_setup_token);
10971 boolean hideSetupEntry(void *setup_value)
10973 char *hide_setup_token = getHideSetupToken(setup_value);
10975 return (setup_value != NULL &&
10976 getHashEntry(hide_setup_hash, hide_setup_token) != NULL);
10979 static void setSetupInfoFromTokenText(SetupFileHash *setup_file_hash,
10980 struct TokenInfo *token_info,
10981 int token_nr, char *token_text)
10983 char *token_hide_text = getStringCat2(token_text, ".hide");
10984 char *token_hide_value = getHashEntry(setup_file_hash, token_hide_text);
10986 // set the value of this setup option in the setup option structure
10987 setSetupInfo(token_info, token_nr, getHashEntry(setup_file_hash, token_text));
10989 // check if this setup option should be hidden in the setup menu
10990 if (token_hide_value != NULL && get_boolean_from_string(token_hide_value))
10991 setHideSetupEntry(token_info[token_nr].value);
10993 free(token_hide_text);
10996 static void setSetupInfoFromTokenInfo(SetupFileHash *setup_file_hash,
10997 struct TokenInfo *token_info,
11000 setSetupInfoFromTokenText(setup_file_hash, token_info, token_nr,
11001 token_info[token_nr].text);
11004 static void decodeSetupFileHash_Default(SetupFileHash *setup_file_hash)
11008 if (!setup_file_hash)
11011 for (i = 0; i < ARRAY_SIZE(global_setup_tokens); i++)
11012 setSetupInfoFromTokenInfo(setup_file_hash, global_setup_tokens, i);
11014 setup.touch.grid_initialized = TRUE;
11015 for (i = 0; i < 2; i++)
11017 int grid_xsize = setup.touch.grid_xsize[i];
11018 int grid_ysize = setup.touch.grid_ysize[i];
11021 // if virtual buttons are not loaded from setup file, repeat initializing
11022 // virtual buttons grid with default values later when video is initialized
11023 if (grid_xsize == -1 ||
11026 setup.touch.grid_initialized = FALSE;
11031 for (y = 0; y < grid_ysize; y++)
11033 char token_string[MAX_LINE_LEN];
11035 sprintf(token_string, "touch.virtual_buttons.%d.%02d", i, y);
11037 char *value_string = getHashEntry(setup_file_hash, token_string);
11039 if (value_string == NULL)
11042 for (x = 0; x < grid_xsize; x++)
11044 char c = value_string[x];
11046 setup.touch.grid_button[i][x][y] =
11047 (c == '.' ? CHAR_GRID_BUTTON_NONE : c);
11052 for (i = 0; i < ARRAY_SIZE(editor_setup_tokens); i++)
11053 setSetupInfoFromTokenInfo(setup_file_hash, editor_setup_tokens, i);
11055 for (i = 0; i < ARRAY_SIZE(shortcut_setup_tokens); i++)
11056 setSetupInfoFromTokenInfo(setup_file_hash, shortcut_setup_tokens, i);
11058 for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
11062 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
11064 setup_input = setup.input[pnr];
11065 for (i = 0; i < ARRAY_SIZE(player_setup_tokens); i++)
11067 char full_token[100];
11069 sprintf(full_token, "%s%s", prefix, player_setup_tokens[i].text);
11070 setSetupInfoFromTokenText(setup_file_hash, player_setup_tokens, i,
11073 setup.input[pnr] = setup_input;
11076 for (i = 0; i < ARRAY_SIZE(system_setup_tokens); i++)
11077 setSetupInfoFromTokenInfo(setup_file_hash, system_setup_tokens, i);
11079 for (i = 0; i < ARRAY_SIZE(internal_setup_tokens); i++)
11080 setSetupInfoFromTokenInfo(setup_file_hash, internal_setup_tokens, i);
11082 for (i = 0; i < ARRAY_SIZE(debug_setup_tokens); i++)
11083 setSetupInfoFromTokenInfo(setup_file_hash, debug_setup_tokens, i);
11085 for (i = 0; i < ARRAY_SIZE(options_setup_tokens); i++)
11086 setSetupInfoFromTokenInfo(setup_file_hash, options_setup_tokens, i);
11088 setHideRelatedSetupEntries();
11091 static void decodeSetupFileHash_AutoSetup(SetupFileHash *setup_file_hash)
11095 if (!setup_file_hash)
11098 for (i = 0; i < ARRAY_SIZE(auto_setup_tokens); i++)
11099 setSetupInfo(auto_setup_tokens, i,
11100 getHashEntry(setup_file_hash,
11101 auto_setup_tokens[i].text));
11104 static void decodeSetupFileHash_ServerSetup(SetupFileHash *setup_file_hash)
11108 if (!setup_file_hash)
11111 for (i = 0; i < ARRAY_SIZE(server_setup_tokens); i++)
11112 setSetupInfo(server_setup_tokens, i,
11113 getHashEntry(setup_file_hash,
11114 server_setup_tokens[i].text));
11117 static void decodeSetupFileHash_EditorCascade(SetupFileHash *setup_file_hash)
11121 if (!setup_file_hash)
11124 for (i = 0; i < ARRAY_SIZE(editor_cascade_setup_tokens); i++)
11125 setSetupInfo(editor_cascade_setup_tokens, i,
11126 getHashEntry(setup_file_hash,
11127 editor_cascade_setup_tokens[i].text));
11130 void LoadUserNames(void)
11132 int last_user_nr = user.nr;
11135 if (global.user_names != NULL)
11137 for (i = 0; i < MAX_PLAYER_NAMES; i++)
11138 checked_free(global.user_names[i]);
11140 checked_free(global.user_names);
11143 global.user_names = checked_calloc(MAX_PLAYER_NAMES * sizeof(char *));
11145 for (i = 0; i < MAX_PLAYER_NAMES; i++)
11149 SetupFileHash *setup_file_hash = loadSetupFileHash(getSetupFilename());
11151 if (setup_file_hash)
11153 char *player_name = getHashEntry(setup_file_hash, "player_name");
11155 global.user_names[i] = getFixedUserName(player_name);
11157 freeSetupFileHash(setup_file_hash);
11160 if (global.user_names[i] == NULL)
11161 global.user_names[i] = getStringCopy(getDefaultUserName(i));
11164 user.nr = last_user_nr;
11167 void LoadSetupFromFilename(char *filename)
11169 SetupFileHash *setup_file_hash = loadSetupFileHash(filename);
11171 if (setup_file_hash)
11173 decodeSetupFileHash_Default(setup_file_hash);
11175 freeSetupFileHash(setup_file_hash);
11179 Debug("setup", "using default setup values");
11183 static void LoadSetup_SpecialPostProcessing(void)
11185 char *player_name_new;
11187 // needed to work around problems with fixed length strings
11188 player_name_new = getFixedUserName(setup.player_name);
11189 free(setup.player_name);
11190 setup.player_name = player_name_new;
11192 // "scroll_delay: on(3) / off(0)" was replaced by scroll delay value
11193 if (setup.scroll_delay == FALSE)
11195 setup.scroll_delay_value = MIN_SCROLL_DELAY;
11196 setup.scroll_delay = TRUE; // now always "on"
11199 // make sure that scroll delay value stays inside valid range
11200 setup.scroll_delay_value =
11201 MIN(MAX(MIN_SCROLL_DELAY, setup.scroll_delay_value), MAX_SCROLL_DELAY);
11204 void LoadSetup_Default(void)
11208 // always start with reliable default values
11209 setSetupInfoToDefaults(&setup);
11211 // try to load setup values from default setup file
11212 filename = getDefaultSetupFilename();
11214 if (fileExists(filename))
11215 LoadSetupFromFilename(filename);
11217 // try to load setup values from platform setup file
11218 filename = getPlatformSetupFilename();
11220 if (fileExists(filename))
11221 LoadSetupFromFilename(filename);
11223 // try to load setup values from user setup file
11224 filename = getSetupFilename();
11226 LoadSetupFromFilename(filename);
11228 LoadSetup_SpecialPostProcessing();
11231 void LoadSetup_AutoSetup(void)
11233 char *filename = getPath2(getSetupDir(), AUTOSETUP_FILENAME);
11234 SetupFileHash *setup_file_hash = NULL;
11236 // always start with reliable default values
11237 setSetupInfoToDefaults_AutoSetup(&setup);
11239 setup_file_hash = loadSetupFileHash(filename);
11241 if (setup_file_hash)
11243 decodeSetupFileHash_AutoSetup(setup_file_hash);
11245 freeSetupFileHash(setup_file_hash);
11251 void LoadSetup_ServerSetup(void)
11253 char *filename = getPath2(getSetupDir(), SERVERSETUP_FILENAME);
11254 SetupFileHash *setup_file_hash = NULL;
11256 // always start with reliable default values
11257 setSetupInfoToDefaults_ServerSetup(&setup);
11259 setup_file_hash = loadSetupFileHash(filename);
11261 if (setup_file_hash)
11263 decodeSetupFileHash_ServerSetup(setup_file_hash);
11265 freeSetupFileHash(setup_file_hash);
11270 if (setup.player_uuid == NULL)
11272 // player UUID does not yet exist in setup file
11273 setup.player_uuid = getStringCopy(getUUID());
11274 setup.player_version = 2;
11276 SaveSetup_ServerSetup();
11280 void LoadSetup_EditorCascade(void)
11282 char *filename = getPath2(getSetupDir(), EDITORCASCADE_FILENAME);
11283 SetupFileHash *setup_file_hash = NULL;
11285 // always start with reliable default values
11286 setSetupInfoToDefaults_EditorCascade(&setup);
11288 setup_file_hash = loadSetupFileHash(filename);
11290 if (setup_file_hash)
11292 decodeSetupFileHash_EditorCascade(setup_file_hash);
11294 freeSetupFileHash(setup_file_hash);
11300 void LoadSetup(void)
11302 LoadSetup_Default();
11303 LoadSetup_AutoSetup();
11304 LoadSetup_ServerSetup();
11305 LoadSetup_EditorCascade();
11308 static void addGameControllerMappingToHash(SetupFileHash *mappings_hash,
11309 char *mapping_line)
11311 char mapping_guid[MAX_LINE_LEN];
11312 char *mapping_start, *mapping_end;
11314 // get GUID from game controller mapping line: copy complete line
11315 strncpy(mapping_guid, mapping_line, MAX_LINE_LEN - 1);
11316 mapping_guid[MAX_LINE_LEN - 1] = '\0';
11318 // get GUID from game controller mapping line: cut after GUID part
11319 mapping_start = strchr(mapping_guid, ',');
11320 if (mapping_start != NULL)
11321 *mapping_start = '\0';
11323 // cut newline from game controller mapping line
11324 mapping_end = strchr(mapping_line, '\n');
11325 if (mapping_end != NULL)
11326 *mapping_end = '\0';
11328 // add mapping entry to game controller mappings hash
11329 setHashEntry(mappings_hash, mapping_guid, mapping_line);
11332 static void LoadSetup_ReadGameControllerMappings(SetupFileHash *mappings_hash,
11337 if (!(file = fopen(filename, MODE_READ)))
11339 Warn("cannot read game controller mappings file '%s'", filename);
11344 while (!feof(file))
11346 char line[MAX_LINE_LEN];
11348 if (!fgets(line, MAX_LINE_LEN, file))
11351 addGameControllerMappingToHash(mappings_hash, line);
11357 void SaveSetup_Default(void)
11359 char *filename = getSetupFilename();
11363 InitUserDataDirectory();
11365 if (!(file = fopen(filename, MODE_WRITE)))
11367 Warn("cannot write setup file '%s'", filename);
11372 fprintFileHeader(file, SETUP_FILENAME);
11374 for (i = 0; i < ARRAY_SIZE(global_setup_tokens); i++)
11376 // just to make things nicer :)
11377 if (global_setup_tokens[i].value == &setup.multiple_users ||
11378 global_setup_tokens[i].value == &setup.sound ||
11379 global_setup_tokens[i].value == &setup.graphics_set ||
11380 global_setup_tokens[i].value == &setup.volume_simple ||
11381 global_setup_tokens[i].value == &setup.network_mode ||
11382 global_setup_tokens[i].value == &setup.touch.control_type ||
11383 global_setup_tokens[i].value == &setup.touch.grid_xsize[0] ||
11384 global_setup_tokens[i].value == &setup.touch.grid_xsize[1])
11385 fprintf(file, "\n");
11387 fprintf(file, "%s\n", getSetupLine(global_setup_tokens, "", i));
11390 for (i = 0; i < 2; i++)
11392 int grid_xsize = setup.touch.grid_xsize[i];
11393 int grid_ysize = setup.touch.grid_ysize[i];
11396 fprintf(file, "\n");
11398 for (y = 0; y < grid_ysize; y++)
11400 char token_string[MAX_LINE_LEN];
11401 char value_string[MAX_LINE_LEN];
11403 sprintf(token_string, "touch.virtual_buttons.%d.%02d", i, y);
11405 for (x = 0; x < grid_xsize; x++)
11407 char c = setup.touch.grid_button[i][x][y];
11409 value_string[x] = (c == CHAR_GRID_BUTTON_NONE ? '.' : c);
11412 value_string[grid_xsize] = '\0';
11414 fprintf(file, "%s\n", getFormattedSetupEntry(token_string, value_string));
11418 fprintf(file, "\n");
11419 for (i = 0; i < ARRAY_SIZE(editor_setup_tokens); i++)
11420 fprintf(file, "%s\n", getSetupLine(editor_setup_tokens, "", i));
11422 fprintf(file, "\n");
11423 for (i = 0; i < ARRAY_SIZE(shortcut_setup_tokens); i++)
11424 fprintf(file, "%s\n", getSetupLine(shortcut_setup_tokens, "", i));
11426 for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
11430 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
11431 fprintf(file, "\n");
11433 setup_input = setup.input[pnr];
11434 for (i = 0; i < ARRAY_SIZE(player_setup_tokens); i++)
11435 fprintf(file, "%s\n", getSetupLine(player_setup_tokens, prefix, i));
11438 fprintf(file, "\n");
11439 for (i = 0; i < ARRAY_SIZE(system_setup_tokens); i++)
11440 fprintf(file, "%s\n", getSetupLine(system_setup_tokens, "", i));
11442 // (internal setup values not saved to user setup file)
11444 fprintf(file, "\n");
11445 for (i = 0; i < ARRAY_SIZE(debug_setup_tokens); i++)
11446 if (!strPrefix(debug_setup_tokens[i].text, "debug.xsn_") ||
11447 setup.debug.xsn_mode != AUTO)
11448 fprintf(file, "%s\n", getSetupLine(debug_setup_tokens, "", i));
11450 fprintf(file, "\n");
11451 for (i = 0; i < ARRAY_SIZE(options_setup_tokens); i++)
11452 fprintf(file, "%s\n", getSetupLine(options_setup_tokens, "", i));
11456 SetFilePermissions(filename, PERMS_PRIVATE);
11459 void SaveSetup_AutoSetup(void)
11461 char *filename = getPath2(getSetupDir(), AUTOSETUP_FILENAME);
11465 InitUserDataDirectory();
11467 if (!(file = fopen(filename, MODE_WRITE)))
11469 Warn("cannot write auto setup file '%s'", filename);
11476 fprintFileHeader(file, AUTOSETUP_FILENAME);
11478 for (i = 0; i < ARRAY_SIZE(auto_setup_tokens); i++)
11479 fprintf(file, "%s\n", getSetupLine(auto_setup_tokens, "", i));
11483 SetFilePermissions(filename, PERMS_PRIVATE);
11488 void SaveSetup_ServerSetup(void)
11490 char *filename = getPath2(getSetupDir(), SERVERSETUP_FILENAME);
11494 InitUserDataDirectory();
11496 if (!(file = fopen(filename, MODE_WRITE)))
11498 Warn("cannot write server setup file '%s'", filename);
11505 fprintFileHeader(file, SERVERSETUP_FILENAME);
11507 for (i = 0; i < ARRAY_SIZE(server_setup_tokens); i++)
11509 // just to make things nicer :)
11510 if (server_setup_tokens[i].value == &setup.use_api_server)
11511 fprintf(file, "\n");
11513 fprintf(file, "%s\n", getSetupLine(server_setup_tokens, "", i));
11518 SetFilePermissions(filename, PERMS_PRIVATE);
11523 void SaveSetup_EditorCascade(void)
11525 char *filename = getPath2(getSetupDir(), EDITORCASCADE_FILENAME);
11529 InitUserDataDirectory();
11531 if (!(file = fopen(filename, MODE_WRITE)))
11533 Warn("cannot write editor cascade state file '%s'", filename);
11540 fprintFileHeader(file, EDITORCASCADE_FILENAME);
11542 for (i = 0; i < ARRAY_SIZE(editor_cascade_setup_tokens); i++)
11543 fprintf(file, "%s\n", getSetupLine(editor_cascade_setup_tokens, "", i));
11547 SetFilePermissions(filename, PERMS_PRIVATE);
11552 void SaveSetup(void)
11554 SaveSetup_Default();
11555 SaveSetup_AutoSetup();
11556 SaveSetup_ServerSetup();
11557 SaveSetup_EditorCascade();
11560 static void SaveSetup_WriteGameControllerMappings(SetupFileHash *mappings_hash,
11565 if (!(file = fopen(filename, MODE_WRITE)))
11567 Warn("cannot write game controller mappings file '%s'", filename);
11572 BEGIN_HASH_ITERATION(mappings_hash, itr)
11574 fprintf(file, "%s\n", HASH_ITERATION_VALUE(itr));
11576 END_HASH_ITERATION(mappings_hash, itr)
11581 void SaveSetup_AddGameControllerMapping(char *mapping)
11583 char *filename = getPath2(getSetupDir(), GAMECONTROLLER_BASENAME);
11584 SetupFileHash *mappings_hash = newSetupFileHash();
11586 InitUserDataDirectory();
11588 // load existing personal game controller mappings
11589 LoadSetup_ReadGameControllerMappings(mappings_hash, filename);
11591 // add new mapping to personal game controller mappings
11592 addGameControllerMappingToHash(mappings_hash, mapping);
11594 // save updated personal game controller mappings
11595 SaveSetup_WriteGameControllerMappings(mappings_hash, filename);
11597 freeSetupFileHash(mappings_hash);
11601 void LoadCustomElementDescriptions(void)
11603 char *filename = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
11604 SetupFileHash *setup_file_hash;
11607 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
11609 if (element_info[i].custom_description != NULL)
11611 free(element_info[i].custom_description);
11612 element_info[i].custom_description = NULL;
11616 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
11619 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
11621 char *token = getStringCat2(element_info[i].token_name, ".name");
11622 char *value = getHashEntry(setup_file_hash, token);
11625 element_info[i].custom_description = getStringCopy(value);
11630 freeSetupFileHash(setup_file_hash);
11633 static int getElementFromToken(char *token)
11635 char *value = getHashEntry(element_token_hash, token);
11638 return atoi(value);
11640 Warn("unknown element token '%s'", token);
11642 return EL_UNDEFINED;
11645 void FreeGlobalAnimEventInfo(void)
11647 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
11649 if (gaei->event_list == NULL)
11654 for (i = 0; i < gaei->num_event_lists; i++)
11656 checked_free(gaei->event_list[i]->event_value);
11657 checked_free(gaei->event_list[i]);
11660 checked_free(gaei->event_list);
11662 gaei->event_list = NULL;
11663 gaei->num_event_lists = 0;
11666 static int AddGlobalAnimEventList(void)
11668 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
11669 int list_pos = gaei->num_event_lists++;
11671 gaei->event_list = checked_realloc(gaei->event_list, gaei->num_event_lists *
11672 sizeof(struct GlobalAnimEventListInfo *));
11674 gaei->event_list[list_pos] =
11675 checked_calloc(sizeof(struct GlobalAnimEventListInfo));
11677 struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
11679 gaeli->event_value = NULL;
11680 gaeli->num_event_values = 0;
11685 static int AddGlobalAnimEventValue(int list_pos, int event_value)
11687 // do not add empty global animation events
11688 if (event_value == ANIM_EVENT_NONE)
11691 // if list position is undefined, create new list
11692 if (list_pos == ANIM_EVENT_UNDEFINED)
11693 list_pos = AddGlobalAnimEventList();
11695 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
11696 struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
11697 int value_pos = gaeli->num_event_values++;
11699 gaeli->event_value = checked_realloc(gaeli->event_value,
11700 gaeli->num_event_values * sizeof(int *));
11702 gaeli->event_value[value_pos] = event_value;
11707 int GetGlobalAnimEventValue(int list_pos, int value_pos)
11709 if (list_pos == ANIM_EVENT_UNDEFINED)
11710 return ANIM_EVENT_NONE;
11712 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
11713 struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
11715 return gaeli->event_value[value_pos];
11718 int GetGlobalAnimEventValueCount(int list_pos)
11720 if (list_pos == ANIM_EVENT_UNDEFINED)
11723 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
11724 struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
11726 return gaeli->num_event_values;
11729 // This function checks if a string <s> of the format "string1, string2, ..."
11730 // exactly contains a string <s_contained>.
11732 static boolean string_has_parameter(char *s, char *s_contained)
11736 if (s == NULL || s_contained == NULL)
11739 if (strlen(s_contained) > strlen(s))
11742 if (strncmp(s, s_contained, strlen(s_contained)) == 0)
11744 char next_char = s[strlen(s_contained)];
11746 // check if next character is delimiter or whitespace
11747 if (next_char == ',' || next_char == '\0' ||
11748 next_char == ' ' || next_char == '\t')
11752 // check if string contains another parameter string after a comma
11753 substring = strchr(s, ',');
11754 if (substring == NULL) // string does not contain a comma
11757 // advance string pointer to next character after the comma
11760 // skip potential whitespaces after the comma
11761 while (*substring == ' ' || *substring == '\t')
11764 return string_has_parameter(substring, s_contained);
11767 static int get_anim_parameter_value_ce(char *s)
11770 char *pattern_1 = "ce_change:custom_";
11771 char *pattern_2 = ".page_";
11772 int pattern_1_len = strlen(pattern_1);
11773 char *matching_char = strstr(s_ptr, pattern_1);
11774 int result = ANIM_EVENT_NONE;
11776 if (matching_char == NULL)
11777 return ANIM_EVENT_NONE;
11779 result = ANIM_EVENT_CE_CHANGE;
11781 s_ptr = matching_char + pattern_1_len;
11783 // check for custom element number ("custom_X", "custom_XX" or "custom_XXX")
11784 if (*s_ptr >= '0' && *s_ptr <= '9')
11786 int gic_ce_nr = (*s_ptr++ - '0');
11788 if (*s_ptr >= '0' && *s_ptr <= '9')
11790 gic_ce_nr = 10 * gic_ce_nr + (*s_ptr++ - '0');
11792 if (*s_ptr >= '0' && *s_ptr <= '9')
11793 gic_ce_nr = 10 * gic_ce_nr + (*s_ptr++ - '0');
11796 if (gic_ce_nr < 1 || gic_ce_nr > NUM_CUSTOM_ELEMENTS)
11797 return ANIM_EVENT_NONE;
11799 // custom element stored as 0 to 255
11802 result |= gic_ce_nr << ANIM_EVENT_CE_BIT;
11806 // invalid custom element number specified
11808 return ANIM_EVENT_NONE;
11811 // check for change page number ("page_X" or "page_XX") (optional)
11812 if (strPrefix(s_ptr, pattern_2))
11814 s_ptr += strlen(pattern_2);
11816 if (*s_ptr >= '0' && *s_ptr <= '9')
11818 int gic_page_nr = (*s_ptr++ - '0');
11820 if (*s_ptr >= '0' && *s_ptr <= '9')
11821 gic_page_nr = 10 * gic_page_nr + (*s_ptr++ - '0');
11823 if (gic_page_nr < 1 || gic_page_nr > MAX_CHANGE_PAGES)
11824 return ANIM_EVENT_NONE;
11826 // change page stored as 1 to 32 (0 means "all change pages")
11828 result |= gic_page_nr << ANIM_EVENT_PAGE_BIT;
11832 // invalid animation part number specified
11834 return ANIM_EVENT_NONE;
11838 // discard result if next character is neither delimiter nor whitespace
11839 if (!(*s_ptr == ',' || *s_ptr == '\0' ||
11840 *s_ptr == ' ' || *s_ptr == '\t'))
11841 return ANIM_EVENT_NONE;
11846 static int get_anim_parameter_value(char *s)
11848 int event_value[] =
11856 char *pattern_1[] =
11864 char *pattern_2 = ".part_";
11865 char *matching_char = NULL;
11867 int pattern_1_len = 0;
11868 int result = ANIM_EVENT_NONE;
11871 result = get_anim_parameter_value_ce(s);
11873 if (result != ANIM_EVENT_NONE)
11876 for (i = 0; i < ARRAY_SIZE(event_value); i++)
11878 matching_char = strstr(s_ptr, pattern_1[i]);
11879 pattern_1_len = strlen(pattern_1[i]);
11880 result = event_value[i];
11882 if (matching_char != NULL)
11886 if (matching_char == NULL)
11887 return ANIM_EVENT_NONE;
11889 s_ptr = matching_char + pattern_1_len;
11891 // check for main animation number ("anim_X" or "anim_XX")
11892 if (*s_ptr >= '0' && *s_ptr <= '9')
11894 int gic_anim_nr = (*s_ptr++ - '0');
11896 if (*s_ptr >= '0' && *s_ptr <= '9')
11897 gic_anim_nr = 10 * gic_anim_nr + (*s_ptr++ - '0');
11899 if (gic_anim_nr < 1 || gic_anim_nr > MAX_GLOBAL_ANIMS)
11900 return ANIM_EVENT_NONE;
11902 result |= gic_anim_nr << ANIM_EVENT_ANIM_BIT;
11906 // invalid main animation number specified
11908 return ANIM_EVENT_NONE;
11911 // check for animation part number ("part_X" or "part_XX") (optional)
11912 if (strPrefix(s_ptr, pattern_2))
11914 s_ptr += strlen(pattern_2);
11916 if (*s_ptr >= '0' && *s_ptr <= '9')
11918 int gic_part_nr = (*s_ptr++ - '0');
11920 if (*s_ptr >= '0' && *s_ptr <= '9')
11921 gic_part_nr = 10 * gic_part_nr + (*s_ptr++ - '0');
11923 if (gic_part_nr < 1 || gic_part_nr > MAX_GLOBAL_ANIM_PARTS)
11924 return ANIM_EVENT_NONE;
11926 result |= gic_part_nr << ANIM_EVENT_PART_BIT;
11930 // invalid animation part number specified
11932 return ANIM_EVENT_NONE;
11936 // discard result if next character is neither delimiter nor whitespace
11937 if (!(*s_ptr == ',' || *s_ptr == '\0' ||
11938 *s_ptr == ' ' || *s_ptr == '\t'))
11939 return ANIM_EVENT_NONE;
11944 static int get_anim_parameter_values(char *s)
11946 int list_pos = ANIM_EVENT_UNDEFINED;
11947 int event_value = ANIM_EVENT_DEFAULT;
11949 if (string_has_parameter(s, "any"))
11950 event_value |= ANIM_EVENT_ANY;
11952 if (string_has_parameter(s, "click:self") ||
11953 string_has_parameter(s, "click") ||
11954 string_has_parameter(s, "self"))
11955 event_value |= ANIM_EVENT_SELF;
11957 if (string_has_parameter(s, "unclick:any"))
11958 event_value |= ANIM_EVENT_UNCLICK_ANY;
11960 // if animation event found, add it to global animation event list
11961 if (event_value != ANIM_EVENT_NONE)
11962 list_pos = AddGlobalAnimEventValue(list_pos, event_value);
11966 // add optional "click:anim_X" or "click:anim_X.part_X" parameter
11967 event_value = get_anim_parameter_value(s);
11969 // if animation event found, add it to global animation event list
11970 if (event_value != ANIM_EVENT_NONE)
11971 list_pos = AddGlobalAnimEventValue(list_pos, event_value);
11973 // continue with next part of the string, starting with next comma
11974 s = strchr(s + 1, ',');
11980 static int get_anim_action_parameter_value(char *token)
11982 // check most common default case first to massively speed things up
11983 if (strEqual(token, ARG_UNDEFINED))
11984 return ANIM_EVENT_ACTION_NONE;
11986 int result = getImageIDFromToken(token);
11990 char *gfx_token = getStringCat2("gfx.", token);
11992 result = getImageIDFromToken(gfx_token);
11994 checked_free(gfx_token);
11999 Key key = getKeyFromX11KeyName(token);
12001 if (key != KSYM_UNDEFINED)
12002 result = -(int)key;
12009 result = get_hash_from_string(token); // unsigned int => int
12010 result = ABS(result); // may be negative now
12011 result += (result < MAX_IMAGE_FILES ? MAX_IMAGE_FILES : 0);
12013 setHashEntry(anim_url_hash, int2str(result, 0), token);
12018 result = ANIM_EVENT_ACTION_NONE;
12023 int get_parameter_value(char *value_raw, char *suffix, int type)
12025 char *value = getStringToLower(value_raw);
12026 int result = 0; // probably a save default value
12028 if (strEqual(suffix, ".direction"))
12030 result = (strEqual(value, "left") ? MV_LEFT :
12031 strEqual(value, "right") ? MV_RIGHT :
12032 strEqual(value, "up") ? MV_UP :
12033 strEqual(value, "down") ? MV_DOWN : MV_NONE);
12035 else if (strEqual(suffix, ".position"))
12037 result = (strEqual(value, "left") ? POS_LEFT :
12038 strEqual(value, "right") ? POS_RIGHT :
12039 strEqual(value, "top") ? POS_TOP :
12040 strEqual(value, "upper") ? POS_UPPER :
12041 strEqual(value, "middle") ? POS_MIDDLE :
12042 strEqual(value, "lower") ? POS_LOWER :
12043 strEqual(value, "bottom") ? POS_BOTTOM :
12044 strEqual(value, "any") ? POS_ANY :
12045 strEqual(value, "ce") ? POS_CE :
12046 strEqual(value, "ce_trigger") ? POS_CE_TRIGGER :
12047 strEqual(value, "last") ? POS_LAST : POS_UNDEFINED);
12049 else if (strEqual(suffix, ".align"))
12051 result = (strEqual(value, "left") ? ALIGN_LEFT :
12052 strEqual(value, "right") ? ALIGN_RIGHT :
12053 strEqual(value, "center") ? ALIGN_CENTER :
12054 strEqual(value, "middle") ? ALIGN_CENTER : ALIGN_DEFAULT);
12056 else if (strEqual(suffix, ".valign"))
12058 result = (strEqual(value, "top") ? VALIGN_TOP :
12059 strEqual(value, "bottom") ? VALIGN_BOTTOM :
12060 strEqual(value, "middle") ? VALIGN_MIDDLE :
12061 strEqual(value, "center") ? VALIGN_MIDDLE : VALIGN_DEFAULT);
12063 else if (strEqual(suffix, ".anim_mode"))
12065 result = (string_has_parameter(value, "none") ? ANIM_NONE :
12066 string_has_parameter(value, "loop") ? ANIM_LOOP :
12067 string_has_parameter(value, "linear") ? ANIM_LINEAR :
12068 string_has_parameter(value, "pingpong") ? ANIM_PINGPONG :
12069 string_has_parameter(value, "pingpong2") ? ANIM_PINGPONG2 :
12070 string_has_parameter(value, "random") ? ANIM_RANDOM :
12071 string_has_parameter(value, "random_static") ? ANIM_RANDOM_STATIC :
12072 string_has_parameter(value, "ce_value") ? ANIM_CE_VALUE :
12073 string_has_parameter(value, "ce_score") ? ANIM_CE_SCORE :
12074 string_has_parameter(value, "ce_delay") ? ANIM_CE_DELAY :
12075 string_has_parameter(value, "horizontal") ? ANIM_HORIZONTAL :
12076 string_has_parameter(value, "vertical") ? ANIM_VERTICAL :
12077 string_has_parameter(value, "centered") ? ANIM_CENTERED :
12078 string_has_parameter(value, "all") ? ANIM_ALL :
12079 string_has_parameter(value, "tiled") ? ANIM_TILED :
12080 string_has_parameter(value, "level_nr") ? ANIM_LEVEL_NR :
12083 if (string_has_parameter(value, "once"))
12084 result |= ANIM_ONCE;
12086 if (string_has_parameter(value, "reverse"))
12087 result |= ANIM_REVERSE;
12089 if (string_has_parameter(value, "opaque_player"))
12090 result |= ANIM_OPAQUE_PLAYER;
12092 if (string_has_parameter(value, "static_panel"))
12093 result |= ANIM_STATIC_PANEL;
12095 else if (strEqual(suffix, ".init_event") ||
12096 strEqual(suffix, ".anim_event"))
12098 result = get_anim_parameter_values(value);
12100 else if (strEqual(suffix, ".init_delay_action") ||
12101 strEqual(suffix, ".anim_delay_action") ||
12102 strEqual(suffix, ".post_delay_action") ||
12103 strEqual(suffix, ".init_event_action") ||
12104 strEqual(suffix, ".anim_event_action"))
12106 result = get_anim_action_parameter_value(value_raw);
12108 else if (strEqual(suffix, ".class"))
12110 result = (strEqual(value, ARG_UNDEFINED) ? ARG_UNDEFINED_VALUE :
12111 get_hash_from_string(value));
12113 else if (strEqual(suffix, ".style"))
12115 result = STYLE_DEFAULT;
12117 if (string_has_parameter(value, "accurate_borders"))
12118 result |= STYLE_ACCURATE_BORDERS;
12120 if (string_has_parameter(value, "inner_corners"))
12121 result |= STYLE_INNER_CORNERS;
12123 if (string_has_parameter(value, "reverse"))
12124 result |= STYLE_REVERSE;
12126 if (string_has_parameter(value, "leftmost_position"))
12127 result |= STYLE_LEFTMOST_POSITION;
12129 if (string_has_parameter(value, "block_clicks"))
12130 result |= STYLE_BLOCK;
12132 if (string_has_parameter(value, "passthrough_clicks"))
12133 result |= STYLE_PASSTHROUGH;
12135 if (string_has_parameter(value, "multiple_actions"))
12136 result |= STYLE_MULTIPLE_ACTIONS;
12138 if (string_has_parameter(value, "consume_ce_event"))
12139 result |= STYLE_CONSUME_CE_EVENT;
12141 else if (strEqual(suffix, ".fade_mode"))
12143 result = (string_has_parameter(value, "none") ? FADE_MODE_NONE :
12144 string_has_parameter(value, "fade") ? FADE_MODE_FADE :
12145 string_has_parameter(value, "fade_in") ? FADE_MODE_FADE_IN :
12146 string_has_parameter(value, "fade_out") ? FADE_MODE_FADE_OUT :
12147 string_has_parameter(value, "crossfade") ? FADE_MODE_CROSSFADE :
12148 string_has_parameter(value, "melt") ? FADE_MODE_MELT :
12149 string_has_parameter(value, "curtain") ? FADE_MODE_CURTAIN :
12150 FADE_MODE_DEFAULT);
12152 else if (strEqual(suffix, ".auto_delay_unit"))
12154 result = (string_has_parameter(value, "ms") ? AUTO_DELAY_UNIT_MS :
12155 string_has_parameter(value, "frames") ? AUTO_DELAY_UNIT_FRAMES :
12156 AUTO_DELAY_UNIT_DEFAULT);
12158 else if (strPrefix(suffix, ".font")) // (may also be ".font_xyz")
12160 result = gfx.get_font_from_token_function(value);
12162 else // generic parameter of type integer or boolean
12164 result = (strEqual(value, ARG_UNDEFINED) ? ARG_UNDEFINED_VALUE :
12165 type == TYPE_INTEGER ? get_integer_from_string(value) :
12166 type == TYPE_BOOLEAN ? get_boolean_from_string(value) :
12167 ARG_UNDEFINED_VALUE);
12175 static int get_token_parameter_value(char *token, char *value_raw)
12179 if (token == NULL || value_raw == NULL)
12180 return ARG_UNDEFINED_VALUE;
12182 suffix = strrchr(token, '.');
12183 if (suffix == NULL)
12186 if (strEqual(suffix, ".element"))
12187 return getElementFromToken(value_raw);
12189 // !!! USE CORRECT VALUE TYPE (currently works also for TYPE_BOOLEAN) !!!
12190 return get_parameter_value(value_raw, suffix, TYPE_INTEGER);
12193 void InitMenuDesignSettings_FromHash(SetupFileHash *setup_file_hash,
12194 boolean ignore_defaults)
12198 for (i = 0; image_config_vars[i].token != NULL; i++)
12200 char *value = getHashEntry(setup_file_hash, image_config_vars[i].token);
12202 // (ignore definitions set to "[DEFAULT]" which are already initialized)
12203 if (ignore_defaults && strEqual(value, ARG_DEFAULT))
12207 *image_config_vars[i].value =
12208 get_token_parameter_value(image_config_vars[i].token, value);
12212 void InitMenuDesignSettings_Static(void)
12214 // always start with reliable default values from static default config
12215 InitMenuDesignSettings_FromHash(image_config_hash, FALSE);
12218 static void InitMenuDesignSettings_SpecialPreProcessing(void)
12222 // the following initializes hierarchical values from static configuration
12224 // special case: initialize "ARG_DEFAULT" values in static default config
12225 // (e.g., initialize "[titlemessage].fade_mode" from "[title].fade_mode")
12226 titlescreen_initial_first_default.fade_mode =
12227 title_initial_first_default.fade_mode;
12228 titlescreen_initial_first_default.fade_delay =
12229 title_initial_first_default.fade_delay;
12230 titlescreen_initial_first_default.post_delay =
12231 title_initial_first_default.post_delay;
12232 titlescreen_initial_first_default.auto_delay =
12233 title_initial_first_default.auto_delay;
12234 titlescreen_initial_first_default.auto_delay_unit =
12235 title_initial_first_default.auto_delay_unit;
12236 titlescreen_first_default.fade_mode = title_first_default.fade_mode;
12237 titlescreen_first_default.fade_delay = title_first_default.fade_delay;
12238 titlescreen_first_default.post_delay = title_first_default.post_delay;
12239 titlescreen_first_default.auto_delay = title_first_default.auto_delay;
12240 titlescreen_first_default.auto_delay_unit =
12241 title_first_default.auto_delay_unit;
12242 titlemessage_initial_first_default.fade_mode =
12243 title_initial_first_default.fade_mode;
12244 titlemessage_initial_first_default.fade_delay =
12245 title_initial_first_default.fade_delay;
12246 titlemessage_initial_first_default.post_delay =
12247 title_initial_first_default.post_delay;
12248 titlemessage_initial_first_default.auto_delay =
12249 title_initial_first_default.auto_delay;
12250 titlemessage_initial_first_default.auto_delay_unit =
12251 title_initial_first_default.auto_delay_unit;
12252 titlemessage_first_default.fade_mode = title_first_default.fade_mode;
12253 titlemessage_first_default.fade_delay = title_first_default.fade_delay;
12254 titlemessage_first_default.post_delay = title_first_default.post_delay;
12255 titlemessage_first_default.auto_delay = title_first_default.auto_delay;
12256 titlemessage_first_default.auto_delay_unit =
12257 title_first_default.auto_delay_unit;
12259 titlescreen_initial_default.fade_mode = title_initial_default.fade_mode;
12260 titlescreen_initial_default.fade_delay = title_initial_default.fade_delay;
12261 titlescreen_initial_default.post_delay = title_initial_default.post_delay;
12262 titlescreen_initial_default.auto_delay = title_initial_default.auto_delay;
12263 titlescreen_initial_default.auto_delay_unit =
12264 title_initial_default.auto_delay_unit;
12265 titlescreen_default.fade_mode = title_default.fade_mode;
12266 titlescreen_default.fade_delay = title_default.fade_delay;
12267 titlescreen_default.post_delay = title_default.post_delay;
12268 titlescreen_default.auto_delay = title_default.auto_delay;
12269 titlescreen_default.auto_delay_unit = title_default.auto_delay_unit;
12270 titlemessage_initial_default.fade_mode = title_initial_default.fade_mode;
12271 titlemessage_initial_default.fade_delay = title_initial_default.fade_delay;
12272 titlemessage_initial_default.post_delay = title_initial_default.post_delay;
12273 titlemessage_initial_default.auto_delay_unit =
12274 title_initial_default.auto_delay_unit;
12275 titlemessage_default.fade_mode = title_default.fade_mode;
12276 titlemessage_default.fade_delay = title_default.fade_delay;
12277 titlemessage_default.post_delay = title_default.post_delay;
12278 titlemessage_default.auto_delay = title_default.auto_delay;
12279 titlemessage_default.auto_delay_unit = title_default.auto_delay_unit;
12281 // special case: initialize "ARG_DEFAULT" values in static default config
12282 // (e.g., init "titlemessage_1.fade_mode" from "[titlemessage].fade_mode")
12283 for (i = 0; i < MAX_NUM_TITLE_MESSAGES; i++)
12285 titlescreen_initial_first[i] = titlescreen_initial_first_default;
12286 titlescreen_first[i] = titlescreen_first_default;
12287 titlemessage_initial_first[i] = titlemessage_initial_first_default;
12288 titlemessage_first[i] = titlemessage_first_default;
12290 titlescreen_initial[i] = titlescreen_initial_default;
12291 titlescreen[i] = titlescreen_default;
12292 titlemessage_initial[i] = titlemessage_initial_default;
12293 titlemessage[i] = titlemessage_default;
12296 // special case: initialize "ARG_DEFAULT" values in static default config
12297 // (eg, init "menu.enter_screen.SCORES.xyz" from "menu.enter_screen.xyz")
12298 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
12300 if (i == GFX_SPECIAL_ARG_TITLE) // title values already initialized
12303 menu.enter_screen[i] = menu.enter_screen[GFX_SPECIAL_ARG_DEFAULT];
12304 menu.leave_screen[i] = menu.leave_screen[GFX_SPECIAL_ARG_DEFAULT];
12305 menu.next_screen[i] = menu.next_screen[GFX_SPECIAL_ARG_DEFAULT];
12308 // special case: initialize "ARG_DEFAULT" values in static default config
12309 // (eg, init "viewport.door_1.MAIN.xyz" from "viewport.door_1.xyz")
12310 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
12312 viewport.window[i] = viewport.window[GFX_SPECIAL_ARG_DEFAULT];
12313 viewport.playfield[i] = viewport.playfield[GFX_SPECIAL_ARG_DEFAULT];
12314 viewport.door_1[i] = viewport.door_1[GFX_SPECIAL_ARG_DEFAULT];
12316 if (i == GFX_SPECIAL_ARG_EDITOR) // editor values already initialized
12319 viewport.door_2[i] = viewport.door_2[GFX_SPECIAL_ARG_DEFAULT];
12323 static void InitMenuDesignSettings_SpecialPostProcessing(void)
12327 struct XY *dst, *src;
12329 game_buttons_xy[] =
12331 { &game.button.save, &game.button.stop },
12332 { &game.button.pause2, &game.button.pause },
12333 { &game.button.load, &game.button.play },
12334 { &game.button.undo, &game.button.stop },
12335 { &game.button.redo, &game.button.play },
12341 // special case: initialize later added SETUP list size from LEVELS value
12342 if (menu.list_size[GAME_MODE_SETUP] == -1)
12343 menu.list_size[GAME_MODE_SETUP] = menu.list_size[GAME_MODE_LEVELS];
12345 // set default position for snapshot buttons to stop/pause/play buttons
12346 for (i = 0; game_buttons_xy[i].dst != NULL; i++)
12347 if ((*game_buttons_xy[i].dst).x == -1 &&
12348 (*game_buttons_xy[i].dst).y == -1)
12349 *game_buttons_xy[i].dst = *game_buttons_xy[i].src;
12351 // --------------------------------------------------------------------------
12352 // dynamic viewports (including playfield margins, borders and alignments)
12353 // --------------------------------------------------------------------------
12355 // dynamic viewports currently only supported for landscape mode
12356 int display_width = MAX(video.display_width, video.display_height);
12357 int display_height = MIN(video.display_width, video.display_height);
12359 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
12361 struct RectWithBorder *vp_window = &viewport.window[i];
12362 struct RectWithBorder *vp_playfield = &viewport.playfield[i];
12363 struct RectWithBorder *vp_door_1 = &viewport.door_1[i];
12364 struct RectWithBorder *vp_door_2 = &viewport.door_2[i];
12365 boolean dynamic_window_width = (vp_window->min_width != -1);
12366 boolean dynamic_window_height = (vp_window->min_height != -1);
12367 boolean dynamic_playfield_width = (vp_playfield->min_width != -1);
12368 boolean dynamic_playfield_height = (vp_playfield->min_height != -1);
12370 // adjust window size if min/max width/height is specified
12372 if (vp_window->min_width != -1)
12374 int window_width = display_width;
12376 // when using static window height, use aspect ratio of display
12377 if (vp_window->min_height == -1)
12378 window_width = vp_window->height * display_width / display_height;
12380 vp_window->width = MAX(vp_window->min_width, window_width);
12383 if (vp_window->min_height != -1)
12385 int window_height = display_height;
12387 // when using static window width, use aspect ratio of display
12388 if (vp_window->min_width == -1)
12389 window_height = vp_window->width * display_height / display_width;
12391 vp_window->height = MAX(vp_window->min_height, window_height);
12394 if (vp_window->max_width != -1)
12395 vp_window->width = MIN(vp_window->width, vp_window->max_width);
12397 if (vp_window->max_height != -1)
12398 vp_window->height = MIN(vp_window->height, vp_window->max_height);
12400 int playfield_width = vp_window->width;
12401 int playfield_height = vp_window->height;
12403 // adjust playfield size and position according to specified margins
12405 playfield_width -= vp_playfield->margin_left;
12406 playfield_width -= vp_playfield->margin_right;
12408 playfield_height -= vp_playfield->margin_top;
12409 playfield_height -= vp_playfield->margin_bottom;
12411 // adjust playfield size if min/max width/height is specified
12413 if (vp_playfield->min_width != -1)
12414 vp_playfield->width = MAX(vp_playfield->min_width, playfield_width);
12416 if (vp_playfield->min_height != -1)
12417 vp_playfield->height = MAX(vp_playfield->min_height, playfield_height);
12419 if (vp_playfield->max_width != -1)
12420 vp_playfield->width = MIN(vp_playfield->width, vp_playfield->max_width);
12422 if (vp_playfield->max_height != -1)
12423 vp_playfield->height = MIN(vp_playfield->height, vp_playfield->max_height);
12425 // adjust playfield position according to specified alignment
12427 if (vp_playfield->align == ALIGN_LEFT || vp_playfield->x > 0)
12428 vp_playfield->x = ALIGNED_VP_XPOS(vp_playfield);
12429 else if (vp_playfield->align == ALIGN_CENTER)
12430 vp_playfield->x = playfield_width / 2 - vp_playfield->width / 2;
12431 else if (vp_playfield->align == ALIGN_RIGHT)
12432 vp_playfield->x += playfield_width - vp_playfield->width;
12434 if (vp_playfield->valign == VALIGN_TOP || vp_playfield->y > 0)
12435 vp_playfield->y = ALIGNED_VP_YPOS(vp_playfield);
12436 else if (vp_playfield->valign == VALIGN_MIDDLE)
12437 vp_playfield->y = playfield_height / 2 - vp_playfield->height / 2;
12438 else if (vp_playfield->valign == VALIGN_BOTTOM)
12439 vp_playfield->y += playfield_height - vp_playfield->height;
12441 vp_playfield->x += vp_playfield->margin_left;
12442 vp_playfield->y += vp_playfield->margin_top;
12444 // adjust individual playfield borders if only default border is specified
12446 if (vp_playfield->border_left == -1)
12447 vp_playfield->border_left = vp_playfield->border_size;
12448 if (vp_playfield->border_right == -1)
12449 vp_playfield->border_right = vp_playfield->border_size;
12450 if (vp_playfield->border_top == -1)
12451 vp_playfield->border_top = vp_playfield->border_size;
12452 if (vp_playfield->border_bottom == -1)
12453 vp_playfield->border_bottom = vp_playfield->border_size;
12455 // set dynamic playfield borders if borders are specified as undefined
12456 // (but only if window size was dynamic and playfield size was static)
12458 if (dynamic_window_width && !dynamic_playfield_width)
12460 if (vp_playfield->border_left == -1)
12462 vp_playfield->border_left = (vp_playfield->x -
12463 vp_playfield->margin_left);
12464 vp_playfield->x -= vp_playfield->border_left;
12465 vp_playfield->width += vp_playfield->border_left;
12468 if (vp_playfield->border_right == -1)
12470 vp_playfield->border_right = (vp_window->width -
12472 vp_playfield->width -
12473 vp_playfield->margin_right);
12474 vp_playfield->width += vp_playfield->border_right;
12478 if (dynamic_window_height && !dynamic_playfield_height)
12480 if (vp_playfield->border_top == -1)
12482 vp_playfield->border_top = (vp_playfield->y -
12483 vp_playfield->margin_top);
12484 vp_playfield->y -= vp_playfield->border_top;
12485 vp_playfield->height += vp_playfield->border_top;
12488 if (vp_playfield->border_bottom == -1)
12490 vp_playfield->border_bottom = (vp_window->height -
12492 vp_playfield->height -
12493 vp_playfield->margin_bottom);
12494 vp_playfield->height += vp_playfield->border_bottom;
12498 // adjust playfield size to be a multiple of a defined alignment tile size
12500 int align_size = vp_playfield->align_size;
12501 int playfield_xtiles = vp_playfield->width / align_size;
12502 int playfield_ytiles = vp_playfield->height / align_size;
12503 int playfield_width_corrected = playfield_xtiles * align_size;
12504 int playfield_height_corrected = playfield_ytiles * align_size;
12505 boolean is_playfield_mode = (i == GFX_SPECIAL_ARG_PLAYING ||
12506 i == GFX_SPECIAL_ARG_EDITOR);
12508 if (is_playfield_mode &&
12509 dynamic_playfield_width &&
12510 vp_playfield->width != playfield_width_corrected)
12512 int playfield_xdiff = vp_playfield->width - playfield_width_corrected;
12514 vp_playfield->width = playfield_width_corrected;
12516 if (vp_playfield->align == ALIGN_LEFT)
12518 vp_playfield->border_left += playfield_xdiff;
12520 else if (vp_playfield->align == ALIGN_RIGHT)
12522 vp_playfield->border_right += playfield_xdiff;
12524 else if (vp_playfield->align == ALIGN_CENTER)
12526 int border_left_diff = playfield_xdiff / 2;
12527 int border_right_diff = playfield_xdiff - border_left_diff;
12529 vp_playfield->border_left += border_left_diff;
12530 vp_playfield->border_right += border_right_diff;
12534 if (is_playfield_mode &&
12535 dynamic_playfield_height &&
12536 vp_playfield->height != playfield_height_corrected)
12538 int playfield_ydiff = vp_playfield->height - playfield_height_corrected;
12540 vp_playfield->height = playfield_height_corrected;
12542 if (vp_playfield->valign == VALIGN_TOP)
12544 vp_playfield->border_top += playfield_ydiff;
12546 else if (vp_playfield->align == VALIGN_BOTTOM)
12548 vp_playfield->border_right += playfield_ydiff;
12550 else if (vp_playfield->align == VALIGN_MIDDLE)
12552 int border_top_diff = playfield_ydiff / 2;
12553 int border_bottom_diff = playfield_ydiff - border_top_diff;
12555 vp_playfield->border_top += border_top_diff;
12556 vp_playfield->border_bottom += border_bottom_diff;
12560 // adjust door positions according to specified alignment
12562 for (j = 0; j < 2; j++)
12564 struct RectWithBorder *vp_door = (j == 0 ? vp_door_1 : vp_door_2);
12566 if (vp_door->align == ALIGN_LEFT || vp_door->x > 0)
12567 vp_door->x = ALIGNED_VP_XPOS(vp_door);
12568 else if (vp_door->align == ALIGN_CENTER)
12569 vp_door->x = vp_window->width / 2 - vp_door->width / 2;
12570 else if (vp_door->align == ALIGN_RIGHT)
12571 vp_door->x += vp_window->width - vp_door->width;
12573 if (vp_door->valign == VALIGN_TOP || vp_door->y > 0)
12574 vp_door->y = ALIGNED_VP_YPOS(vp_door);
12575 else if (vp_door->valign == VALIGN_MIDDLE)
12576 vp_door->y = vp_window->height / 2 - vp_door->height / 2;
12577 else if (vp_door->valign == VALIGN_BOTTOM)
12578 vp_door->y += vp_window->height - vp_door->height;
12583 static void InitMenuDesignSettings_SpecialPostProcessing_AfterGraphics(void)
12587 struct XYTileSize *dst, *src;
12590 editor_buttons_xy[] =
12593 &editor.button.element_left, &editor.palette.element_left,
12594 IMG_GFX_EDITOR_BUTTON_ELEMENT_LEFT
12597 &editor.button.element_middle, &editor.palette.element_middle,
12598 IMG_GFX_EDITOR_BUTTON_ELEMENT_MIDDLE
12601 &editor.button.element_right, &editor.palette.element_right,
12602 IMG_GFX_EDITOR_BUTTON_ELEMENT_RIGHT
12609 // set default position for element buttons to element graphics
12610 for (i = 0; editor_buttons_xy[i].dst != NULL; i++)
12612 if ((*editor_buttons_xy[i].dst).x == -1 &&
12613 (*editor_buttons_xy[i].dst).y == -1)
12615 struct GraphicInfo *gd = &graphic_info[editor_buttons_xy[i].graphic];
12617 gd->width = gd->height = editor_buttons_xy[i].src->tile_size;
12619 *editor_buttons_xy[i].dst = *editor_buttons_xy[i].src;
12623 // adjust editor palette rows and columns if specified to be dynamic
12625 if (editor.palette.cols == -1)
12627 int vp_width = viewport.playfield[GFX_SPECIAL_ARG_EDITOR].width;
12628 int bt_width = graphic_info[IMG_EDITOR_PALETTE_BUTTON].width;
12629 int sc_width = graphic_info[IMG_EDITOR_PALETTE_SCROLLBAR].width;
12631 editor.palette.cols = (vp_width - sc_width) / bt_width;
12633 if (editor.palette.x == -1)
12635 int palette_width = editor.palette.cols * bt_width + sc_width;
12637 editor.palette.x = (vp_width - palette_width) / 2;
12641 if (editor.palette.rows == -1)
12643 int vp_height = viewport.playfield[GFX_SPECIAL_ARG_EDITOR].height;
12644 int bt_height = graphic_info[IMG_EDITOR_PALETTE_BUTTON].height;
12645 int tx_height = getFontHeight(FONT_TEXT_2);
12647 editor.palette.rows = (vp_height - tx_height) / bt_height;
12649 if (editor.palette.y == -1)
12651 int palette_height = editor.palette.rows * bt_height + tx_height;
12653 editor.palette.y = (vp_height - palette_height) / 2;
12658 static void InitMenuDesignSettings_PreviewPlayers_Ext(SetupFileHash *hash,
12659 boolean initialize)
12661 // special case: check if network and preview player positions are redefined,
12662 // to compare this later against the main menu level preview being redefined
12663 struct TokenIntPtrInfo menu_config_players[] =
12665 { "main.network_players.x", &menu.main.network_players.redefined },
12666 { "main.network_players.y", &menu.main.network_players.redefined },
12667 { "main.preview_players.x", &menu.main.preview_players.redefined },
12668 { "main.preview_players.y", &menu.main.preview_players.redefined },
12669 { "preview.x", &preview.redefined },
12670 { "preview.y", &preview.redefined }
12676 for (i = 0; i < ARRAY_SIZE(menu_config_players); i++)
12677 *menu_config_players[i].value = FALSE;
12681 for (i = 0; i < ARRAY_SIZE(menu_config_players); i++)
12682 if (getHashEntry(hash, menu_config_players[i].token) != NULL)
12683 *menu_config_players[i].value = TRUE;
12687 static void InitMenuDesignSettings_PreviewPlayers(void)
12689 InitMenuDesignSettings_PreviewPlayers_Ext(NULL, TRUE);
12692 static void InitMenuDesignSettings_PreviewPlayers_FromHash(SetupFileHash *hash)
12694 InitMenuDesignSettings_PreviewPlayers_Ext(hash, FALSE);
12697 static void LoadMenuDesignSettingsFromFilename(char *filename)
12699 static struct TitleFadingInfo tfi;
12700 static struct TitleMessageInfo tmi;
12701 static struct TokenInfo title_tokens[] =
12703 { TYPE_INTEGER, &tfi.fade_mode, ".fade_mode" },
12704 { TYPE_INTEGER, &tfi.fade_delay, ".fade_delay" },
12705 { TYPE_INTEGER, &tfi.post_delay, ".post_delay" },
12706 { TYPE_INTEGER, &tfi.auto_delay, ".auto_delay" },
12707 { TYPE_INTEGER, &tfi.auto_delay_unit, ".auto_delay_unit" },
12711 static struct TokenInfo titlemessage_tokens[] =
12713 { TYPE_INTEGER, &tmi.x, ".x" },
12714 { TYPE_INTEGER, &tmi.y, ".y" },
12715 { TYPE_INTEGER, &tmi.width, ".width" },
12716 { TYPE_INTEGER, &tmi.height, ".height" },
12717 { TYPE_INTEGER, &tmi.chars, ".chars" },
12718 { TYPE_INTEGER, &tmi.lines, ".lines" },
12719 { TYPE_INTEGER, &tmi.align, ".align" },
12720 { TYPE_INTEGER, &tmi.valign, ".valign" },
12721 { TYPE_INTEGER, &tmi.font, ".font" },
12722 { TYPE_BOOLEAN, &tmi.autowrap, ".autowrap" },
12723 { TYPE_BOOLEAN, &tmi.centered, ".centered" },
12724 { TYPE_BOOLEAN, &tmi.parse_comments, ".parse_comments" },
12725 { TYPE_INTEGER, &tmi.sort_priority, ".sort_priority" },
12726 { TYPE_INTEGER, &tmi.fade_mode, ".fade_mode" },
12727 { TYPE_INTEGER, &tmi.fade_delay, ".fade_delay" },
12728 { TYPE_INTEGER, &tmi.post_delay, ".post_delay" },
12729 { TYPE_INTEGER, &tmi.auto_delay, ".auto_delay" },
12730 { TYPE_INTEGER, &tmi.auto_delay_unit, ".auto_delay_unit" },
12736 struct TitleFadingInfo *info;
12741 // initialize first titles from "enter screen" definitions, if defined
12742 { &title_initial_first_default, "menu.enter_screen.TITLE" },
12743 { &title_first_default, "menu.enter_screen.TITLE" },
12745 // initialize title screens from "next screen" definitions, if defined
12746 { &title_initial_default, "menu.next_screen.TITLE" },
12747 { &title_default, "menu.next_screen.TITLE" },
12753 struct TitleMessageInfo *array;
12756 titlemessage_arrays[] =
12758 // initialize first titles from "enter screen" definitions, if defined
12759 { titlescreen_initial_first, "menu.enter_screen.TITLE" },
12760 { titlescreen_first, "menu.enter_screen.TITLE" },
12761 { titlemessage_initial_first, "menu.enter_screen.TITLE" },
12762 { titlemessage_first, "menu.enter_screen.TITLE" },
12764 // initialize titles from "next screen" definitions, if defined
12765 { titlescreen_initial, "menu.next_screen.TITLE" },
12766 { titlescreen, "menu.next_screen.TITLE" },
12767 { titlemessage_initial, "menu.next_screen.TITLE" },
12768 { titlemessage, "menu.next_screen.TITLE" },
12770 // overwrite titles with title definitions, if defined
12771 { titlescreen_initial_first, "[title_initial]" },
12772 { titlescreen_first, "[title]" },
12773 { titlemessage_initial_first, "[title_initial]" },
12774 { titlemessage_first, "[title]" },
12776 { titlescreen_initial, "[title_initial]" },
12777 { titlescreen, "[title]" },
12778 { titlemessage_initial, "[title_initial]" },
12779 { titlemessage, "[title]" },
12781 // overwrite titles with title screen/message definitions, if defined
12782 { titlescreen_initial_first, "[titlescreen_initial]" },
12783 { titlescreen_first, "[titlescreen]" },
12784 { titlemessage_initial_first, "[titlemessage_initial]" },
12785 { titlemessage_first, "[titlemessage]" },
12787 { titlescreen_initial, "[titlescreen_initial]" },
12788 { titlescreen, "[titlescreen]" },
12789 { titlemessage_initial, "[titlemessage_initial]" },
12790 { titlemessage, "[titlemessage]" },
12794 SetupFileHash *setup_file_hash;
12797 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
12800 // the following initializes hierarchical values from dynamic configuration
12802 // special case: initialize with default values that may be overwritten
12803 // (e.g., init "menu.draw_xoffset.INFO" from "menu.draw_xoffset")
12804 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
12806 struct TokenIntPtrInfo menu_config[] =
12808 { "menu.draw_xoffset", &menu.draw_xoffset[i] },
12809 { "menu.draw_yoffset", &menu.draw_yoffset[i] },
12810 { "menu.list_size", &menu.list_size[i] }
12813 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
12815 char *token = menu_config[j].token;
12816 char *value = getHashEntry(setup_file_hash, token);
12819 *menu_config[j].value = get_integer_from_string(value);
12823 // special case: initialize with default values that may be overwritten
12824 // (eg, init "menu.draw_xoffset.INFO[XXX]" from "menu.draw_xoffset.INFO")
12825 for (i = 0; i < NUM_SPECIAL_GFX_INFO_ARGS; i++)
12827 struct TokenIntPtrInfo menu_config[] =
12829 { "menu.draw_xoffset.INFO", &menu.draw_xoffset_info[i] },
12830 { "menu.draw_yoffset.INFO", &menu.draw_yoffset_info[i] },
12831 { "menu.list_size.INFO", &menu.list_size_info[i] },
12832 { "menu.list_entry_size.INFO", &menu.list_entry_size_info[i] },
12833 { "menu.tile_size.INFO", &menu.tile_size_info[i] }
12836 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
12838 char *token = menu_config[j].token;
12839 char *value = getHashEntry(setup_file_hash, token);
12842 *menu_config[j].value = get_integer_from_string(value);
12846 // special case: initialize with default values that may be overwritten
12847 // (eg, init "menu.draw_xoffset.SETUP[XXX]" from "menu.draw_xoffset.SETUP")
12848 for (i = 0; i < NUM_SPECIAL_GFX_SETUP_ARGS; i++)
12850 struct TokenIntPtrInfo menu_config[] =
12852 { "menu.draw_xoffset.SETUP", &menu.draw_xoffset_setup[i] },
12853 { "menu.draw_yoffset.SETUP", &menu.draw_yoffset_setup[i] }
12856 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
12858 char *token = menu_config[j].token;
12859 char *value = getHashEntry(setup_file_hash, token);
12862 *menu_config[j].value = get_integer_from_string(value);
12866 // special case: initialize with default values that may be overwritten
12867 // (eg, init "menu.line_spacing.INFO[XXX]" from "menu.line_spacing.INFO")
12868 for (i = 0; i < NUM_SPECIAL_GFX_INFO_ARGS; i++)
12870 struct TokenIntPtrInfo menu_config[] =
12872 { "menu.left_spacing.INFO", &menu.left_spacing_info[i] },
12873 { "menu.middle_spacing.INFO", &menu.middle_spacing_info[i] },
12874 { "menu.right_spacing.INFO", &menu.right_spacing_info[i] },
12875 { "menu.top_spacing.INFO", &menu.top_spacing_info[i] },
12876 { "menu.bottom_spacing.INFO", &menu.bottom_spacing_info[i] },
12877 { "menu.paragraph_spacing.INFO", &menu.paragraph_spacing_info[i] },
12878 { "menu.headline1_spacing.INFO", &menu.headline1_spacing_info[i] },
12879 { "menu.headline2_spacing.INFO", &menu.headline2_spacing_info[i] },
12880 { "menu.line_spacing.INFO", &menu.line_spacing_info[i] },
12881 { "menu.extra_spacing.INFO", &menu.extra_spacing_info[i] },
12884 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
12886 char *token = menu_config[j].token;
12887 char *value = getHashEntry(setup_file_hash, token);
12890 *menu_config[j].value = get_integer_from_string(value);
12894 // special case: initialize with default values that may be overwritten
12895 // (eg, init "menu.enter_screen.SCORES.xyz" from "menu.enter_screen.xyz")
12896 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
12898 struct TokenIntPtrInfo menu_config[] =
12900 { "menu.enter_screen.fade_mode", &menu.enter_screen[i].fade_mode },
12901 { "menu.enter_screen.fade_delay", &menu.enter_screen[i].fade_delay },
12902 { "menu.enter_screen.post_delay", &menu.enter_screen[i].post_delay },
12903 { "menu.leave_screen.fade_mode", &menu.leave_screen[i].fade_mode },
12904 { "menu.leave_screen.fade_delay", &menu.leave_screen[i].fade_delay },
12905 { "menu.leave_screen.post_delay", &menu.leave_screen[i].post_delay },
12906 { "menu.next_screen.fade_mode", &menu.next_screen[i].fade_mode },
12907 { "menu.next_screen.fade_delay", &menu.next_screen[i].fade_delay },
12908 { "menu.next_screen.post_delay", &menu.next_screen[i].post_delay }
12911 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
12913 char *token = menu_config[j].token;
12914 char *value = getHashEntry(setup_file_hash, token);
12917 *menu_config[j].value = get_token_parameter_value(token, value);
12921 // special case: initialize with default values that may be overwritten
12922 // (eg, init "viewport.door_1.MAIN.xyz" from "viewport.door_1.xyz")
12923 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
12927 char *token_prefix;
12928 struct RectWithBorder *struct_ptr;
12932 { "viewport.window", &viewport.window[i] },
12933 { "viewport.playfield", &viewport.playfield[i] },
12934 { "viewport.door_1", &viewport.door_1[i] },
12935 { "viewport.door_2", &viewport.door_2[i] }
12938 for (j = 0; j < ARRAY_SIZE(vp_struct); j++)
12940 struct TokenIntPtrInfo vp_config[] =
12942 { ".x", &vp_struct[j].struct_ptr->x },
12943 { ".y", &vp_struct[j].struct_ptr->y },
12944 { ".width", &vp_struct[j].struct_ptr->width },
12945 { ".height", &vp_struct[j].struct_ptr->height },
12946 { ".min_width", &vp_struct[j].struct_ptr->min_width },
12947 { ".min_height", &vp_struct[j].struct_ptr->min_height },
12948 { ".max_width", &vp_struct[j].struct_ptr->max_width },
12949 { ".max_height", &vp_struct[j].struct_ptr->max_height },
12950 { ".margin_left", &vp_struct[j].struct_ptr->margin_left },
12951 { ".margin_right", &vp_struct[j].struct_ptr->margin_right },
12952 { ".margin_top", &vp_struct[j].struct_ptr->margin_top },
12953 { ".margin_bottom", &vp_struct[j].struct_ptr->margin_bottom },
12954 { ".border_left", &vp_struct[j].struct_ptr->border_left },
12955 { ".border_right", &vp_struct[j].struct_ptr->border_right },
12956 { ".border_top", &vp_struct[j].struct_ptr->border_top },
12957 { ".border_bottom", &vp_struct[j].struct_ptr->border_bottom },
12958 { ".border_size", &vp_struct[j].struct_ptr->border_size },
12959 { ".align_size", &vp_struct[j].struct_ptr->align_size },
12960 { ".align", &vp_struct[j].struct_ptr->align },
12961 { ".valign", &vp_struct[j].struct_ptr->valign }
12964 for (k = 0; k < ARRAY_SIZE(vp_config); k++)
12966 char *token = getStringCat2(vp_struct[j].token_prefix,
12967 vp_config[k].token);
12968 char *value = getHashEntry(setup_file_hash, token);
12971 *vp_config[k].value = get_token_parameter_value(token, value);
12978 // special case: initialize with default values that may be overwritten
12979 // (e.g., init "[title].fade_mode" from "menu.next_screen.TITLE.fade_mode")
12980 for (i = 0; title_info[i].info != NULL; i++)
12982 struct TitleFadingInfo *info = title_info[i].info;
12983 char *base_token = title_info[i].text;
12985 for (j = 0; title_tokens[j].type != -1; j++)
12987 char *token = getStringCat2(base_token, title_tokens[j].text);
12988 char *value = getHashEntry(setup_file_hash, token);
12992 int parameter_value = get_token_parameter_value(token, value);
12996 *(int *)title_tokens[j].value = (int)parameter_value;
13005 // special case: initialize with default values that may be overwritten
13006 // (e.g., init "titlemessage_1.fade_mode" from "[titlemessage].fade_mode")
13007 for (i = 0; titlemessage_arrays[i].array != NULL; i++)
13009 struct TitleMessageInfo *array = titlemessage_arrays[i].array;
13010 char *base_token = titlemessage_arrays[i].text;
13012 for (j = 0; titlemessage_tokens[j].type != -1; j++)
13014 char *token = getStringCat2(base_token, titlemessage_tokens[j].text);
13015 char *value = getHashEntry(setup_file_hash, token);
13019 int parameter_value = get_token_parameter_value(token, value);
13021 for (k = 0; k < MAX_NUM_TITLE_MESSAGES; k++)
13025 if (titlemessage_tokens[j].type == TYPE_INTEGER)
13026 *(int *)titlemessage_tokens[j].value = (int)parameter_value;
13028 *(boolean *)titlemessage_tokens[j].value = (boolean)parameter_value;
13038 // read (and overwrite with) values that may be specified in config file
13039 InitMenuDesignSettings_FromHash(setup_file_hash, TRUE);
13041 // special case: check if network and preview player positions are redefined
13042 InitMenuDesignSettings_PreviewPlayers_FromHash(setup_file_hash);
13044 freeSetupFileHash(setup_file_hash);
13047 void LoadMenuDesignSettings(void)
13049 char *filename_base = UNDEFINED_FILENAME, *filename_local;
13051 InitMenuDesignSettings_Static();
13052 InitMenuDesignSettings_SpecialPreProcessing();
13053 InitMenuDesignSettings_PreviewPlayers();
13055 if (!GFX_OVERRIDE_ARTWORK(ARTWORK_TYPE_GRAPHICS))
13057 // first look for special settings configured in level series config
13058 filename_base = getCustomArtworkLevelConfigFilename(ARTWORK_TYPE_GRAPHICS);
13060 if (fileExists(filename_base))
13061 LoadMenuDesignSettingsFromFilename(filename_base);
13064 filename_local = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
13066 if (filename_local != NULL && !strEqual(filename_base, filename_local))
13067 LoadMenuDesignSettingsFromFilename(filename_local);
13069 InitMenuDesignSettings_SpecialPostProcessing();
13072 void LoadMenuDesignSettings_AfterGraphics(void)
13074 InitMenuDesignSettings_SpecialPostProcessing_AfterGraphics();
13077 void InitSoundSettings_FromHash(SetupFileHash *setup_file_hash,
13078 boolean ignore_defaults)
13082 for (i = 0; sound_config_vars[i].token != NULL; i++)
13084 char *value = getHashEntry(setup_file_hash, sound_config_vars[i].token);
13086 // (ignore definitions set to "[DEFAULT]" which are already initialized)
13087 if (ignore_defaults && strEqual(value, ARG_DEFAULT))
13091 *sound_config_vars[i].value =
13092 get_token_parameter_value(sound_config_vars[i].token, value);
13096 void InitSoundSettings_Static(void)
13098 // always start with reliable default values from static default config
13099 InitSoundSettings_FromHash(sound_config_hash, FALSE);
13102 static void LoadSoundSettingsFromFilename(char *filename)
13104 SetupFileHash *setup_file_hash;
13106 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
13109 // read (and overwrite with) values that may be specified in config file
13110 InitSoundSettings_FromHash(setup_file_hash, TRUE);
13112 freeSetupFileHash(setup_file_hash);
13115 void LoadSoundSettings(void)
13117 char *filename_base = UNDEFINED_FILENAME, *filename_local;
13119 InitSoundSettings_Static();
13121 if (!GFX_OVERRIDE_ARTWORK(ARTWORK_TYPE_SOUNDS))
13123 // first look for special settings configured in level series config
13124 filename_base = getCustomArtworkLevelConfigFilename(ARTWORK_TYPE_SOUNDS);
13126 if (fileExists(filename_base))
13127 LoadSoundSettingsFromFilename(filename_base);
13130 filename_local = getCustomArtworkConfigFilename(ARTWORK_TYPE_SOUNDS);
13132 if (filename_local != NULL && !strEqual(filename_base, filename_local))
13133 LoadSoundSettingsFromFilename(filename_local);
13136 void LoadUserDefinedEditorElementList(int **elements, int *num_elements)
13138 char *filename = getEditorSetupFilename();
13139 SetupFileList *setup_file_list, *list;
13140 SetupFileHash *element_hash;
13141 int num_unknown_tokens = 0;
13144 if ((setup_file_list = loadSetupFileList(filename)) == NULL)
13147 element_hash = newSetupFileHash();
13149 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
13150 setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
13152 // determined size may be larger than needed (due to unknown elements)
13154 for (list = setup_file_list; list != NULL; list = list->next)
13157 // add space for up to 3 more elements for padding that may be needed
13158 *num_elements += 3;
13160 // free memory for old list of elements, if needed
13161 checked_free(*elements);
13163 // allocate memory for new list of elements
13164 *elements = checked_malloc(*num_elements * sizeof(int));
13167 for (list = setup_file_list; list != NULL; list = list->next)
13169 char *value = getHashEntry(element_hash, list->token);
13171 if (value == NULL) // try to find obsolete token mapping
13173 char *mapped_token = get_mapped_token(list->token);
13175 if (mapped_token != NULL)
13177 value = getHashEntry(element_hash, mapped_token);
13179 free(mapped_token);
13185 (*elements)[(*num_elements)++] = atoi(value);
13189 if (num_unknown_tokens == 0)
13192 Warn("unknown token(s) found in config file:");
13193 Warn("- config file: '%s'", filename);
13195 num_unknown_tokens++;
13198 Warn("- token: '%s'", list->token);
13202 if (num_unknown_tokens > 0)
13205 while (*num_elements % 4) // pad with empty elements, if needed
13206 (*elements)[(*num_elements)++] = EL_EMPTY;
13208 freeSetupFileList(setup_file_list);
13209 freeSetupFileHash(element_hash);
13212 for (i = 0; i < *num_elements; i++)
13213 Debug("editor", "element '%s' [%d]\n",
13214 element_info[(*elements)[i]].token_name, (*elements)[i]);
13218 static struct MusicFileInfo *get_music_file_info_ext(char *basename, int music,
13221 SetupFileHash *setup_file_hash = NULL;
13222 struct MusicFileInfo tmp_music_file_info, *new_music_file_info;
13223 char *filename_music, *filename_prefix, *filename_info;
13229 token_to_value_ptr[] =
13231 { "title_header", &tmp_music_file_info.title_header },
13232 { "artist_header", &tmp_music_file_info.artist_header },
13233 { "album_header", &tmp_music_file_info.album_header },
13234 { "year_header", &tmp_music_file_info.year_header },
13235 { "played_header", &tmp_music_file_info.played_header },
13237 { "title", &tmp_music_file_info.title },
13238 { "artist", &tmp_music_file_info.artist },
13239 { "album", &tmp_music_file_info.album },
13240 { "year", &tmp_music_file_info.year },
13241 { "played", &tmp_music_file_info.played },
13247 filename_music = (is_sound ? getCustomSoundFilename(basename) :
13248 getCustomMusicFilename(basename));
13250 if (filename_music == NULL)
13253 // ---------- try to replace file extension ----------
13255 filename_prefix = getStringCopy(filename_music);
13256 if (strrchr(filename_prefix, '.') != NULL)
13257 *strrchr(filename_prefix, '.') = '\0';
13258 filename_info = getStringCat2(filename_prefix, ".txt");
13260 if (fileExists(filename_info))
13261 setup_file_hash = loadSetupFileHash(filename_info);
13263 free(filename_prefix);
13264 free(filename_info);
13266 if (setup_file_hash == NULL)
13268 // ---------- try to add file extension ----------
13270 filename_prefix = getStringCopy(filename_music);
13271 filename_info = getStringCat2(filename_prefix, ".txt");
13273 if (fileExists(filename_info))
13274 setup_file_hash = loadSetupFileHash(filename_info);
13276 free(filename_prefix);
13277 free(filename_info);
13280 if (setup_file_hash == NULL)
13283 // ---------- music file info found ----------
13285 clear_mem(&tmp_music_file_info, sizeof(struct MusicFileInfo));
13287 for (i = 0; token_to_value_ptr[i].token != NULL; i++)
13289 char *value = getHashEntry(setup_file_hash, token_to_value_ptr[i].token);
13291 *token_to_value_ptr[i].value_ptr =
13292 getStringCopy(value != NULL && *value != '\0' ? value : UNKNOWN_NAME);
13295 tmp_music_file_info.basename = getStringCopy(basename);
13296 tmp_music_file_info.music = music;
13297 tmp_music_file_info.is_sound = is_sound;
13299 new_music_file_info = checked_malloc(sizeof(struct MusicFileInfo));
13300 *new_music_file_info = tmp_music_file_info;
13302 return new_music_file_info;
13305 static struct MusicFileInfo *get_music_file_info(char *basename, int music)
13307 return get_music_file_info_ext(basename, music, FALSE);
13310 static struct MusicFileInfo *get_sound_file_info(char *basename, int sound)
13312 return get_music_file_info_ext(basename, sound, TRUE);
13315 static boolean music_info_listed_ext(struct MusicFileInfo *list,
13316 char *basename, boolean is_sound)
13318 for (; list != NULL; list = list->next)
13319 if (list->is_sound == is_sound && strEqual(list->basename, basename))
13325 static boolean music_info_listed(struct MusicFileInfo *list, char *basename)
13327 return music_info_listed_ext(list, basename, FALSE);
13330 static boolean sound_info_listed(struct MusicFileInfo *list, char *basename)
13332 return music_info_listed_ext(list, basename, TRUE);
13335 void LoadMusicInfo(void)
13337 int num_music_noconf = getMusicListSize_NoConf();
13338 int num_music = getMusicListSize();
13339 int num_sounds = getSoundListSize();
13340 struct FileInfo *music, *sound;
13341 struct MusicFileInfo *next, **new;
13345 while (music_file_info != NULL)
13347 next = music_file_info->next;
13349 checked_free(music_file_info->basename);
13351 checked_free(music_file_info->title_header);
13352 checked_free(music_file_info->artist_header);
13353 checked_free(music_file_info->album_header);
13354 checked_free(music_file_info->year_header);
13355 checked_free(music_file_info->played_header);
13357 checked_free(music_file_info->title);
13358 checked_free(music_file_info->artist);
13359 checked_free(music_file_info->album);
13360 checked_free(music_file_info->year);
13361 checked_free(music_file_info->played);
13363 free(music_file_info);
13365 music_file_info = next;
13368 new = &music_file_info;
13370 // get (configured or unconfigured) music file info for all levels
13371 for (i = leveldir_current->first_level;
13372 i <= leveldir_current->last_level; i++)
13376 if (levelset.music[i] != MUS_UNDEFINED)
13378 // get music file info for configured level music
13379 music_nr = levelset.music[i];
13381 else if (num_music_noconf > 0)
13383 // get music file info for unconfigured level music
13384 int level_pos = i - leveldir_current->first_level;
13386 music_nr = MAP_NOCONF_MUSIC(level_pos % num_music_noconf);
13393 char *basename = getMusicInfoEntryFilename(music_nr);
13395 if (basename == NULL)
13398 if (!music_info_listed(music_file_info, basename))
13400 *new = get_music_file_info(basename, music_nr);
13403 new = &(*new)->next;
13407 // get music file info for all remaining configured music files
13408 for (i = 0; i < num_music; i++)
13410 music = getMusicListEntry(i);
13412 if (music->filename == NULL)
13415 if (strEqual(music->filename, UNDEFINED_FILENAME))
13418 // a configured file may be not recognized as music
13419 if (!FileIsMusic(music->filename))
13422 if (!music_info_listed(music_file_info, music->filename))
13424 *new = get_music_file_info(music->filename, i);
13427 new = &(*new)->next;
13431 // get sound file info for all configured sound files
13432 for (i = 0; i < num_sounds; i++)
13434 sound = getSoundListEntry(i);
13436 if (sound->filename == NULL)
13439 if (strEqual(sound->filename, UNDEFINED_FILENAME))
13442 // a configured file may be not recognized as sound
13443 if (!FileIsSound(sound->filename))
13446 if (!sound_info_listed(music_file_info, sound->filename))
13448 *new = get_sound_file_info(sound->filename, i);
13450 new = &(*new)->next;
13454 // add pointers to previous list nodes
13456 struct MusicFileInfo *node = music_file_info;
13458 while (node != NULL)
13461 node->next->prev = node;
13467 static void add_helpanim_entry(int element, int action, int direction,
13468 int delay, int *num_list_entries)
13470 struct HelpAnimInfo *new_list_entry;
13471 (*num_list_entries)++;
13474 checked_realloc(helpanim_info,
13475 *num_list_entries * sizeof(struct HelpAnimInfo));
13476 new_list_entry = &helpanim_info[*num_list_entries - 1];
13478 new_list_entry->element = element;
13479 new_list_entry->action = action;
13480 new_list_entry->direction = direction;
13481 new_list_entry->delay = delay;
13484 static void print_unknown_token(char *filename, char *token, int token_nr)
13489 Warn("unknown token(s) found in config file:");
13490 Warn("- config file: '%s'", filename);
13493 Warn("- token: '%s'", token);
13496 static void print_unknown_token_end(int token_nr)
13502 void LoadHelpAnimInfo(void)
13504 char *filename = getHelpAnimFilename();
13505 SetupFileList *setup_file_list = NULL, *list;
13506 SetupFileHash *element_hash, *action_hash, *direction_hash;
13507 int num_list_entries = 0;
13508 int num_unknown_tokens = 0;
13511 if (fileExists(filename))
13512 setup_file_list = loadSetupFileList(filename);
13514 if (setup_file_list == NULL)
13516 // use reliable default values from static configuration
13517 SetupFileList *insert_ptr;
13519 insert_ptr = setup_file_list =
13520 newSetupFileList(helpanim_config[0].token,
13521 helpanim_config[0].value);
13523 for (i = 1; helpanim_config[i].token; i++)
13524 insert_ptr = addListEntry(insert_ptr,
13525 helpanim_config[i].token,
13526 helpanim_config[i].value);
13529 element_hash = newSetupFileHash();
13530 action_hash = newSetupFileHash();
13531 direction_hash = newSetupFileHash();
13533 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
13534 setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
13536 for (i = 0; i < NUM_ACTIONS; i++)
13537 setHashEntry(action_hash, element_action_info[i].suffix,
13538 i_to_a(element_action_info[i].value));
13540 // do not store direction index (bit) here, but direction value!
13541 for (i = 0; i < NUM_DIRECTIONS_FULL; i++)
13542 setHashEntry(direction_hash, element_direction_info[i].suffix,
13543 i_to_a(1 << element_direction_info[i].value));
13545 for (list = setup_file_list; list != NULL; list = list->next)
13547 char *element_token, *action_token, *direction_token;
13548 char *element_value, *action_value, *direction_value;
13549 int delay = atoi(list->value);
13551 if (strEqual(list->token, "end"))
13553 add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
13558 /* first try to break element into element/action/direction parts;
13559 if this does not work, also accept combined "element[.act][.dir]"
13560 elements (like "dynamite.active"), which are unique elements */
13562 if (strchr(list->token, '.') == NULL) // token contains no '.'
13564 element_value = getHashEntry(element_hash, list->token);
13565 if (element_value != NULL) // element found
13566 add_helpanim_entry(atoi(element_value), -1, -1, delay,
13567 &num_list_entries);
13570 // no further suffixes found -- this is not an element
13571 print_unknown_token(filename, list->token, num_unknown_tokens++);
13577 // token has format "<prefix>.<something>"
13579 action_token = strchr(list->token, '.'); // suffix may be action ...
13580 direction_token = action_token; // ... or direction
13582 element_token = getStringCopy(list->token);
13583 *strchr(element_token, '.') = '\0';
13585 element_value = getHashEntry(element_hash, element_token);
13587 if (element_value == NULL) // this is no element
13589 element_value = getHashEntry(element_hash, list->token);
13590 if (element_value != NULL) // combined element found
13591 add_helpanim_entry(atoi(element_value), -1, -1, delay,
13592 &num_list_entries);
13594 print_unknown_token(filename, list->token, num_unknown_tokens++);
13596 free(element_token);
13601 action_value = getHashEntry(action_hash, action_token);
13603 if (action_value != NULL) // action found
13605 add_helpanim_entry(atoi(element_value), atoi(action_value), -1, delay,
13606 &num_list_entries);
13608 free(element_token);
13613 direction_value = getHashEntry(direction_hash, direction_token);
13615 if (direction_value != NULL) // direction found
13617 add_helpanim_entry(atoi(element_value), -1, atoi(direction_value), delay,
13618 &num_list_entries);
13620 free(element_token);
13625 if (strchr(action_token + 1, '.') == NULL)
13627 // no further suffixes found -- this is not an action nor direction
13629 element_value = getHashEntry(element_hash, list->token);
13630 if (element_value != NULL) // combined element found
13631 add_helpanim_entry(atoi(element_value), -1, -1, delay,
13632 &num_list_entries);
13634 print_unknown_token(filename, list->token, num_unknown_tokens++);
13636 free(element_token);
13641 // token has format "<prefix>.<suffix>.<something>"
13643 direction_token = strchr(action_token + 1, '.');
13645 action_token = getStringCopy(action_token);
13646 *strchr(action_token + 1, '.') = '\0';
13648 action_value = getHashEntry(action_hash, action_token);
13650 if (action_value == NULL) // this is no action
13652 element_value = getHashEntry(element_hash, list->token);
13653 if (element_value != NULL) // combined element found
13654 add_helpanim_entry(atoi(element_value), -1, -1, delay,
13655 &num_list_entries);
13657 print_unknown_token(filename, list->token, num_unknown_tokens++);
13659 free(element_token);
13660 free(action_token);
13665 direction_value = getHashEntry(direction_hash, direction_token);
13667 if (direction_value != NULL) // direction found
13669 add_helpanim_entry(atoi(element_value), atoi(action_value),
13670 atoi(direction_value), delay, &num_list_entries);
13672 free(element_token);
13673 free(action_token);
13678 // this is no direction
13680 element_value = getHashEntry(element_hash, list->token);
13681 if (element_value != NULL) // combined element found
13682 add_helpanim_entry(atoi(element_value), -1, -1, delay,
13683 &num_list_entries);
13685 print_unknown_token(filename, list->token, num_unknown_tokens++);
13687 free(element_token);
13688 free(action_token);
13691 print_unknown_token_end(num_unknown_tokens);
13693 add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
13694 add_helpanim_entry(HELPANIM_LIST_END, -1, -1, -1, &num_list_entries);
13696 freeSetupFileList(setup_file_list);
13697 freeSetupFileHash(element_hash);
13698 freeSetupFileHash(action_hash);
13699 freeSetupFileHash(direction_hash);
13702 for (i = 0; i < num_list_entries; i++)
13703 Debug("files:LoadHelpAnimInfo", "'%s': %d, %d, %d => %d",
13704 EL_NAME(helpanim_info[i].element),
13705 helpanim_info[i].element,
13706 helpanim_info[i].action,
13707 helpanim_info[i].direction,
13708 helpanim_info[i].delay);
13712 void LoadHelpTextInfo(void)
13714 char *filename = getHelpTextFilename();
13717 if (helptext_info != NULL)
13719 freeSetupFileHash(helptext_info);
13720 helptext_info = NULL;
13723 if (fileExists(filename))
13724 helptext_info = loadSetupFileHash(filename);
13726 if (helptext_info == NULL)
13728 // use reliable default values from static configuration
13729 helptext_info = newSetupFileHash();
13731 for (i = 0; helptext_config[i].token; i++)
13732 setHashEntry(helptext_info,
13733 helptext_config[i].token,
13734 helptext_config[i].value);
13738 BEGIN_HASH_ITERATION(helptext_info, itr)
13740 Debug("files:LoadHelpTextInfo", "'%s' => '%s'",
13741 HASH_ITERATION_TOKEN(itr), HASH_ITERATION_VALUE(itr));
13743 END_HASH_ITERATION(hash, itr)
13748 // ----------------------------------------------------------------------------
13750 // ----------------------------------------------------------------------------
13752 #define MAX_NUM_CONVERT_LEVELS 1000
13754 void ConvertLevels(void)
13756 static LevelDirTree *convert_leveldir = NULL;
13757 static int convert_level_nr = -1;
13758 static int num_levels_handled = 0;
13759 static int num_levels_converted = 0;
13760 static boolean levels_failed[MAX_NUM_CONVERT_LEVELS];
13763 convert_leveldir = getTreeInfoFromIdentifier(leveldir_first,
13764 global.convert_leveldir);
13766 if (convert_leveldir == NULL)
13767 Fail("no such level identifier: '%s'", global.convert_leveldir);
13769 leveldir_current = convert_leveldir;
13771 if (global.convert_level_nr != -1)
13773 convert_leveldir->first_level = global.convert_level_nr;
13774 convert_leveldir->last_level = global.convert_level_nr;
13777 convert_level_nr = convert_leveldir->first_level;
13779 PrintLine("=", 79);
13780 Print("Converting levels\n");
13781 PrintLine("-", 79);
13782 Print("Level series identifier: '%s'\n", convert_leveldir->identifier);
13783 Print("Level series name: '%s'\n", convert_leveldir->name);
13784 Print("Level series author: '%s'\n", convert_leveldir->author);
13785 Print("Number of levels: %d\n", convert_leveldir->levels);
13786 PrintLine("=", 79);
13789 for (i = 0; i < MAX_NUM_CONVERT_LEVELS; i++)
13790 levels_failed[i] = FALSE;
13792 while (convert_level_nr <= convert_leveldir->last_level)
13794 char *level_filename;
13797 level_nr = convert_level_nr++;
13799 Print("Level %03d: ", level_nr);
13801 LoadLevel(level_nr);
13802 if (level.no_level_file || level.no_valid_file)
13804 Print("(no level)\n");
13808 Print("converting level ... ");
13811 // special case: conversion of some EMC levels as requested by ACME
13812 level.game_engine_type = GAME_ENGINE_TYPE_RND;
13815 level_filename = getDefaultLevelFilename(level_nr);
13816 new_level = !fileExists(level_filename);
13820 SaveLevel(level_nr);
13822 num_levels_converted++;
13824 Print("converted.\n");
13828 if (level_nr >= 0 && level_nr < MAX_NUM_CONVERT_LEVELS)
13829 levels_failed[level_nr] = TRUE;
13831 Print("NOT CONVERTED -- LEVEL ALREADY EXISTS.\n");
13834 num_levels_handled++;
13838 PrintLine("=", 79);
13839 Print("Number of levels handled: %d\n", num_levels_handled);
13840 Print("Number of levels converted: %d (%d%%)\n", num_levels_converted,
13841 (num_levels_handled ?
13842 num_levels_converted * 100 / num_levels_handled : 0));
13843 PrintLine("-", 79);
13844 Print("Summary (for automatic parsing by scripts):\n");
13845 Print("LEVELDIR '%s', CONVERTED %d/%d (%d%%)",
13846 convert_leveldir->identifier, num_levels_converted,
13847 num_levels_handled,
13848 (num_levels_handled ?
13849 num_levels_converted * 100 / num_levels_handled : 0));
13851 if (num_levels_handled != num_levels_converted)
13853 Print(", FAILED:");
13854 for (i = 0; i < MAX_NUM_CONVERT_LEVELS; i++)
13855 if (levels_failed[i])
13860 PrintLine("=", 79);
13862 CloseAllAndExit(0);
13866 // ----------------------------------------------------------------------------
13867 // create and save images for use in level sketches (raw BMP format)
13868 // ----------------------------------------------------------------------------
13870 void CreateLevelSketchImages(void)
13876 InitElementPropertiesGfxElement();
13878 bitmap1 = CreateBitmap(TILEX, TILEY, DEFAULT_DEPTH);
13879 bitmap2 = CreateBitmap(MINI_TILEX, MINI_TILEY, DEFAULT_DEPTH);
13881 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
13883 int element = getMappedElement(i);
13884 char basename1[16];
13885 char basename2[16];
13889 sprintf(basename1, "%04d.bmp", i);
13890 sprintf(basename2, "%04ds.bmp", i);
13892 filename1 = getPath2(global.create_sketch_images_dir, basename1);
13893 filename2 = getPath2(global.create_sketch_images_dir, basename2);
13895 DrawSizedElement(0, 0, element, TILESIZE);
13896 BlitBitmap(drawto, bitmap1, SX, SY, TILEX, TILEY, 0, 0);
13898 if (SDL_SaveBMP(bitmap1->surface, filename1) != 0)
13899 Fail("cannot save level sketch image file '%s'", filename1);
13901 DrawSizedElement(0, 0, element, MINI_TILESIZE);
13902 BlitBitmap(drawto, bitmap2, SX, SY, MINI_TILEX, MINI_TILEY, 0, 0);
13904 if (SDL_SaveBMP(bitmap2->surface, filename2) != 0)
13905 Fail("cannot save level sketch image file '%s'", filename2);
13910 // create corresponding SQL statements (for normal and small images)
13913 printf("insert into phpbb_words values (NULL, '`%03d', '<IMG class=\"levelsketch\" src=\"/I/%04d.png\"/>');\n", i, i);
13914 printf("insert into phpbb_words values (NULL, '¸%03d', '<IMG class=\"levelsketch\" src=\"/I/%04ds.png\"/>');\n", i, i);
13917 printf("insert into phpbb_words values (NULL, '`%04d', '<IMG class=\"levelsketch\" src=\"/I/%04d.png\"/>');\n", i, i);
13918 printf("insert into phpbb_words values (NULL, '¸%04d', '<IMG class=\"levelsketch\" src=\"/I/%04ds.png\"/>');\n", i, i);
13920 // optional: create content for forum level sketch demonstration post
13922 fprintf(stderr, "%03d `%03d%c", i, i, (i % 10 < 9 ? ' ' : '\n'));
13925 FreeBitmap(bitmap1);
13926 FreeBitmap(bitmap2);
13929 fprintf(stderr, "\n");
13931 Info("%d normal and small images created", NUM_FILE_ELEMENTS);
13933 CloseAllAndExit(0);
13937 // ----------------------------------------------------------------------------
13938 // create and save images for element collecting animations (raw BMP format)
13939 // ----------------------------------------------------------------------------
13941 static boolean createCollectImage(int element)
13943 return (IS_COLLECTIBLE(element) && !IS_SP_ELEMENT(element));
13946 void CreateCollectElementImages(void)
13950 int anim_frames = num_steps - 1;
13951 int tile_size = TILESIZE;
13952 int anim_width = tile_size * anim_frames;
13953 int anim_height = tile_size;
13954 int num_collect_images = 0;
13955 int pos_collect_images = 0;
13957 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
13958 if (createCollectImage(i))
13959 num_collect_images++;
13961 Info("Creating %d element collecting animation images ...",
13962 num_collect_images);
13964 int dst_width = anim_width * 2;
13965 int dst_height = anim_height * num_collect_images / 2;
13966 Bitmap *dst_bitmap = CreateBitmap(dst_width, dst_height, DEFAULT_DEPTH);
13967 char *basename_bmp = "RocksCollect.bmp";
13968 char *basename_png = "RocksCollect.png";
13969 char *filename_bmp = getPath2(global.create_collect_images_dir, basename_bmp);
13970 char *filename_png = getPath2(global.create_collect_images_dir, basename_png);
13971 int len_filename_bmp = strlen(filename_bmp);
13972 int len_filename_png = strlen(filename_png);
13973 int max_command_len = MAX_FILENAME_LEN + len_filename_bmp + len_filename_png;
13974 char cmd_convert[max_command_len];
13976 snprintf(cmd_convert, max_command_len, "convert \"%s\" \"%s\"",
13980 // force using RGBA surface for destination bitmap
13981 SDL_SetColorKey(dst_bitmap->surface, SET_TRANSPARENT_PIXEL,
13982 SDL_MapRGB(dst_bitmap->surface->format, 0x00, 0x00, 0x00));
13984 dst_bitmap->surface =
13985 SDL_ConvertSurfaceFormat(dst_bitmap->surface, SDL_PIXELFORMAT_ARGB8888, 0);
13987 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
13989 if (!createCollectImage(i))
13992 int dst_x = (pos_collect_images / (num_collect_images / 2)) * anim_width;
13993 int dst_y = (pos_collect_images % (num_collect_images / 2)) * anim_height;
13994 int graphic = el2img(i);
13995 char *token_name = element_info[i].token_name;
13996 Bitmap *tmp_bitmap = CreateBitmap(tile_size, tile_size, DEFAULT_DEPTH);
13997 Bitmap *src_bitmap;
14000 Info("- creating collecting image for '%s' ...", token_name);
14002 getGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
14004 BlitBitmap(src_bitmap, tmp_bitmap, src_x, src_y,
14005 tile_size, tile_size, 0, 0);
14007 // force using RGBA surface for temporary bitmap (using transparent black)
14008 SDL_SetColorKey(tmp_bitmap->surface, SET_TRANSPARENT_PIXEL,
14009 SDL_MapRGB(tmp_bitmap->surface->format, 0x00, 0x00, 0x00));
14011 tmp_bitmap->surface =
14012 SDL_ConvertSurfaceFormat(tmp_bitmap->surface, SDL_PIXELFORMAT_ARGB8888, 0);
14014 tmp_bitmap->surface_masked = tmp_bitmap->surface;
14016 for (j = 0; j < anim_frames; j++)
14018 int frame_size_final = tile_size * (anim_frames - j) / num_steps;
14019 int frame_size = frame_size_final * num_steps;
14020 int offset = (tile_size - frame_size_final) / 2;
14021 Bitmap *frame_bitmap = ZoomBitmap(tmp_bitmap, frame_size, frame_size);
14023 while (frame_size > frame_size_final)
14027 Bitmap *half_bitmap = ZoomBitmap(frame_bitmap, frame_size, frame_size);
14029 FreeBitmap(frame_bitmap);
14031 frame_bitmap = half_bitmap;
14034 BlitBitmapMasked(frame_bitmap, dst_bitmap, 0, 0,
14035 frame_size_final, frame_size_final,
14036 dst_x + j * tile_size + offset, dst_y + offset);
14038 FreeBitmap(frame_bitmap);
14041 tmp_bitmap->surface_masked = NULL;
14043 FreeBitmap(tmp_bitmap);
14045 pos_collect_images++;
14048 if (SDL_SaveBMP(dst_bitmap->surface, filename_bmp) != 0)
14049 Fail("cannot save element collecting image file '%s'", filename_bmp);
14051 FreeBitmap(dst_bitmap);
14053 Info("Converting image file from BMP to PNG ...");
14055 if (system(cmd_convert) != 0)
14056 Fail("converting image file failed");
14058 unlink(filename_bmp);
14062 CloseAllAndExit(0);
14066 // ----------------------------------------------------------------------------
14067 // create and save images for custom and group elements (raw BMP format)
14068 // ----------------------------------------------------------------------------
14070 void CreateCustomElementImages(char *directory)
14072 char *src_basename = "RocksCE-template.ilbm";
14073 char *dst_basename = "RocksCE.bmp";
14074 char *src_filename = getPath2(directory, src_basename);
14075 char *dst_filename = getPath2(directory, dst_basename);
14076 Bitmap *src_bitmap;
14078 int yoffset_ce = 0;
14079 int yoffset_ge = (TILEY * NUM_CUSTOM_ELEMENTS / 16);
14082 InitVideoDefaults();
14084 ReCreateBitmap(&backbuffer, video.width, video.height);
14086 src_bitmap = LoadImage(src_filename);
14088 bitmap = CreateBitmap(TILEX * 16 * 2,
14089 TILEY * (NUM_CUSTOM_ELEMENTS + NUM_GROUP_ELEMENTS) / 16,
14092 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
14099 BlitBitmap(src_bitmap, bitmap, 0, 0, TILEX, TILEY,
14100 TILEX * x, TILEY * y + yoffset_ce);
14102 BlitBitmap(src_bitmap, bitmap, 0, TILEY,
14104 TILEX * x + TILEX * 16,
14105 TILEY * y + yoffset_ce);
14107 for (j = 2; j >= 0; j--)
14111 BlitBitmap(src_bitmap, bitmap,
14112 TILEX + c * 7, 0, 6, 10,
14113 TILEX * x + 6 + j * 7,
14114 TILEY * y + 11 + yoffset_ce);
14116 BlitBitmap(src_bitmap, bitmap,
14117 TILEX + c * 8, TILEY, 6, 10,
14118 TILEX * 16 + TILEX * x + 6 + j * 8,
14119 TILEY * y + 10 + yoffset_ce);
14125 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
14132 BlitBitmap(src_bitmap, bitmap, 0, 0, TILEX, TILEY,
14133 TILEX * x, TILEY * y + yoffset_ge);
14135 BlitBitmap(src_bitmap, bitmap, 0, TILEY,
14137 TILEX * x + TILEX * 16,
14138 TILEY * y + yoffset_ge);
14140 for (j = 1; j >= 0; j--)
14144 BlitBitmap(src_bitmap, bitmap, TILEX + c * 10, 11, 10, 10,
14145 TILEX * x + 6 + j * 10,
14146 TILEY * y + 11 + yoffset_ge);
14148 BlitBitmap(src_bitmap, bitmap,
14149 TILEX + c * 8, TILEY + 12, 6, 10,
14150 TILEX * 16 + TILEX * x + 10 + j * 8,
14151 TILEY * y + 10 + yoffset_ge);
14157 if (SDL_SaveBMP(bitmap->surface, dst_filename) != 0)
14158 Fail("cannot save CE graphics file '%s'", dst_filename);
14160 FreeBitmap(bitmap);
14162 CloseAllAndExit(0);