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"
26 #define ENABLE_UNUSED_CODE 0 // currently unused functions
27 #define ENABLE_HISTORIC_CHUNKS 0 // only for historic reference
28 #define ENABLE_RESERVED_CODE 0 // reserved for later use
30 #define CHUNK_ID_LEN 4 // IFF style chunk id length
31 #define CHUNK_SIZE_UNDEFINED 0 // undefined chunk size == 0
32 #define CHUNK_SIZE_NONE -1 // do not write chunk size
34 #define LEVEL_CHUNK_NAME_SIZE MAX_LEVEL_NAME_LEN
35 #define LEVEL_CHUNK_AUTH_SIZE MAX_LEVEL_AUTHOR_LEN
37 #define LEVEL_CHUNK_VERS_SIZE 8 // size of file version chunk
38 #define LEVEL_CHUNK_DATE_SIZE 4 // size of file date chunk
39 #define LEVEL_CHUNK_HEAD_SIZE 80 // size of level file header
40 #define LEVEL_CHUNK_HEAD_UNUSED 0 // unused level header bytes
41 #define LEVEL_CHUNK_CNT2_SIZE 160 // size of level CNT2 chunk
42 #define LEVEL_CHUNK_CNT2_UNUSED 11 // unused CNT2 chunk bytes
43 #define LEVEL_CHUNK_CNT3_HEADER 16 // size of level CNT3 header
44 #define LEVEL_CHUNK_CNT3_UNUSED 10 // unused CNT3 chunk bytes
45 #define LEVEL_CPART_CUS3_SIZE 134 // size of CUS3 chunk part
46 #define LEVEL_CPART_CUS3_UNUSED 15 // unused CUS3 bytes / part
47 #define LEVEL_CHUNK_GRP1_SIZE 74 // size of level GRP1 chunk
49 // (element number, number of change pages, change page number)
50 #define LEVEL_CHUNK_CUSX_UNCHANGED (2 + (1 + 1) + (1 + 1))
52 // (element number only)
53 #define LEVEL_CHUNK_GRPX_UNCHANGED 2
54 #define LEVEL_CHUNK_EMPX_UNCHANGED 2
55 #define LEVEL_CHUNK_NOTE_UNCHANGED 2
57 // (nothing at all if unchanged)
58 #define LEVEL_CHUNK_ELEM_UNCHANGED 0
60 #define TAPE_CHUNK_VERS_SIZE 8 // size of file version chunk
61 #define TAPE_CHUNK_HEAD_SIZE 20 // size of tape file header
62 #define TAPE_CHUNK_HEAD_UNUSED 1 // unused tape header bytes
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),
909 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
913 EL_MM_STEEL_BLOCK, -1,
914 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
915 &li.mm_time_block, 75
919 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
920 &li.score[SC_ELEM_BONUS], 10
923 // ---------- unused values -------------------------------------------------
926 EL_UNKNOWN, SAVE_CONF_NEVER,
927 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
928 &li.score[SC_UNKNOWN_15], 10
938 static struct LevelFileConfigInfo chunk_config_NOTE[] =
942 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
943 &xx_envelope.xsize, MAX_ENVELOPE_XSIZE,
947 TYPE_INTEGER, CONF_VALUE_8_BIT(2),
948 &xx_envelope.ysize, MAX_ENVELOPE_YSIZE,
953 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
954 &xx_envelope.autowrap, FALSE
958 TYPE_BOOLEAN, CONF_VALUE_8_BIT(4),
959 &xx_envelope.centered, FALSE
964 TYPE_STRING, CONF_VALUE_BYTES(1),
965 &xx_envelope.text, -1, NULL,
966 &xx_string_length_unused, -1, MAX_ENVELOPE_TEXT_LEN,
967 &xx_default_string_empty[0]
977 static struct LevelFileConfigInfo chunk_config_CUSX_base[] =
981 TYPE_STRING, CONF_VALUE_BYTES(1),
982 &xx_ei.description[0], -1,
983 &yy_ei.description[0],
984 &xx_string_length_unused, -1, MAX_ELEMENT_NAME_LEN,
985 &xx_default_description[0]
990 TYPE_BITFIELD, CONF_VALUE_32_BIT(1),
991 &xx_ei.properties[EP_BITFIELD_BASE_NR], EP_BITMASK_BASE_DEFAULT,
992 &yy_ei.properties[EP_BITFIELD_BASE_NR]
994 #if ENABLE_RESERVED_CODE
995 // (reserved for later use)
998 TYPE_BITFIELD, CONF_VALUE_32_BIT(2),
999 &xx_ei.properties[EP_BITFIELD_BASE_NR + 1], EP_BITMASK_DEFAULT,
1000 &yy_ei.properties[EP_BITFIELD_BASE_NR + 1]
1006 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1007 &xx_ei.use_gfx_element, FALSE,
1008 &yy_ei.use_gfx_element
1012 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
1013 &xx_ei.gfx_element_initial, EL_EMPTY_SPACE,
1014 &yy_ei.gfx_element_initial
1019 TYPE_BITFIELD, CONF_VALUE_8_BIT(2),
1020 &xx_ei.access_direction, MV_ALL_DIRECTIONS,
1021 &yy_ei.access_direction
1026 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
1027 &xx_ei.collect_score_initial, 10,
1028 &yy_ei.collect_score_initial
1032 TYPE_INTEGER, CONF_VALUE_16_BIT(3),
1033 &xx_ei.collect_count_initial, 1,
1034 &yy_ei.collect_count_initial
1039 TYPE_INTEGER, CONF_VALUE_16_BIT(4),
1040 &xx_ei.ce_value_fixed_initial, 0,
1041 &yy_ei.ce_value_fixed_initial
1045 TYPE_INTEGER, CONF_VALUE_16_BIT(5),
1046 &xx_ei.ce_value_random_initial, 0,
1047 &yy_ei.ce_value_random_initial
1051 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
1052 &xx_ei.use_last_ce_value, FALSE,
1053 &yy_ei.use_last_ce_value
1058 TYPE_INTEGER, CONF_VALUE_16_BIT(6),
1059 &xx_ei.push_delay_fixed, 8,
1060 &yy_ei.push_delay_fixed
1064 TYPE_INTEGER, CONF_VALUE_16_BIT(7),
1065 &xx_ei.push_delay_random, 8,
1066 &yy_ei.push_delay_random
1070 TYPE_INTEGER, CONF_VALUE_16_BIT(8),
1071 &xx_ei.drop_delay_fixed, 0,
1072 &yy_ei.drop_delay_fixed
1076 TYPE_INTEGER, CONF_VALUE_16_BIT(9),
1077 &xx_ei.drop_delay_random, 0,
1078 &yy_ei.drop_delay_random
1082 TYPE_INTEGER, CONF_VALUE_16_BIT(10),
1083 &xx_ei.move_delay_fixed, 0,
1084 &yy_ei.move_delay_fixed
1088 TYPE_INTEGER, CONF_VALUE_16_BIT(11),
1089 &xx_ei.move_delay_random, 0,
1090 &yy_ei.move_delay_random
1094 TYPE_INTEGER, CONF_VALUE_16_BIT(16),
1095 &xx_ei.step_delay_fixed, 0,
1096 &yy_ei.step_delay_fixed
1100 TYPE_INTEGER, CONF_VALUE_16_BIT(17),
1101 &xx_ei.step_delay_random, 0,
1102 &yy_ei.step_delay_random
1107 TYPE_BITFIELD, CONF_VALUE_32_BIT(3),
1108 &xx_ei.move_pattern, MV_ALL_DIRECTIONS,
1113 TYPE_BITFIELD, CONF_VALUE_8_BIT(4),
1114 &xx_ei.move_direction_initial, MV_START_AUTOMATIC,
1115 &yy_ei.move_direction_initial
1119 TYPE_INTEGER, CONF_VALUE_8_BIT(5),
1120 &xx_ei.move_stepsize, TILEX / 8,
1121 &yy_ei.move_stepsize
1126 TYPE_ELEMENT, CONF_VALUE_16_BIT(12),
1127 &xx_ei.move_enter_element, EL_EMPTY_SPACE,
1128 &yy_ei.move_enter_element
1132 TYPE_ELEMENT, CONF_VALUE_16_BIT(13),
1133 &xx_ei.move_leave_element, EL_EMPTY_SPACE,
1134 &yy_ei.move_leave_element
1138 TYPE_INTEGER, CONF_VALUE_8_BIT(6),
1139 &xx_ei.move_leave_type, LEAVE_TYPE_UNLIMITED,
1140 &yy_ei.move_leave_type
1145 TYPE_INTEGER, CONF_VALUE_8_BIT(7),
1146 &xx_ei.slippery_type, SLIPPERY_ANY_RANDOM,
1147 &yy_ei.slippery_type
1152 TYPE_INTEGER, CONF_VALUE_8_BIT(8),
1153 &xx_ei.explosion_type, EXPLODES_3X3,
1154 &yy_ei.explosion_type
1158 TYPE_INTEGER, CONF_VALUE_16_BIT(14),
1159 &xx_ei.explosion_delay, 16,
1160 &yy_ei.explosion_delay
1164 TYPE_INTEGER, CONF_VALUE_16_BIT(15),
1165 &xx_ei.ignition_delay, 8,
1166 &yy_ei.ignition_delay
1171 TYPE_CONTENT_LIST, CONF_VALUE_BYTES(2),
1172 &xx_ei.content, EL_EMPTY_SPACE,
1174 &xx_num_contents, 1, 1
1177 // ---------- "num_change_pages" must be the last entry ---------------------
1180 -1, SAVE_CONF_ALWAYS,
1181 TYPE_INTEGER, CONF_VALUE_8_BIT(9),
1182 &xx_ei.num_change_pages, 1,
1183 &yy_ei.num_change_pages
1194 static struct LevelFileConfigInfo chunk_config_CUSX_change[] =
1196 // ---------- "current_change_page" must be the first entry -----------------
1199 -1, SAVE_CONF_ALWAYS,
1200 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
1201 &xx_current_change_page, -1
1204 // ---------- (the remaining entries can be in any order) -------------------
1208 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
1209 &xx_change.can_change, FALSE
1214 TYPE_BITFIELD, CONF_VALUE_32_BIT(1),
1215 &xx_event_bits[0], 0
1219 TYPE_BITFIELD, CONF_VALUE_32_BIT(2),
1220 &xx_event_bits[1], 0
1225 TYPE_BITFIELD, CONF_VALUE_8_BIT(3),
1226 &xx_change.trigger_player, CH_PLAYER_ANY
1230 TYPE_BITFIELD, CONF_VALUE_8_BIT(4),
1231 &xx_change.trigger_side, CH_SIDE_ANY
1235 TYPE_BITFIELD, CONF_VALUE_32_BIT(3),
1236 &xx_change.trigger_page, CH_PAGE_ANY
1241 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
1242 &xx_change.target_element, EL_EMPTY_SPACE
1247 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
1248 &xx_change.delay_fixed, 0
1252 TYPE_INTEGER, CONF_VALUE_16_BIT(3),
1253 &xx_change.delay_random, 0
1257 TYPE_INTEGER, CONF_VALUE_16_BIT(4),
1258 &xx_change.delay_frames, FRAMES_PER_SECOND
1263 TYPE_ELEMENT, CONF_VALUE_16_BIT(5),
1264 &xx_change.initial_trigger_element, EL_EMPTY_SPACE
1269 TYPE_BOOLEAN, CONF_VALUE_8_BIT(6),
1270 &xx_change.explode, FALSE
1274 TYPE_BOOLEAN, CONF_VALUE_8_BIT(7),
1275 &xx_change.use_target_content, FALSE
1279 TYPE_BOOLEAN, CONF_VALUE_8_BIT(8),
1280 &xx_change.only_if_complete, FALSE
1284 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
1285 &xx_change.use_random_replace, FALSE
1289 TYPE_INTEGER, CONF_VALUE_8_BIT(10),
1290 &xx_change.random_percentage, 100
1294 TYPE_INTEGER, CONF_VALUE_8_BIT(11),
1295 &xx_change.replace_when, CP_WHEN_EMPTY
1300 TYPE_BOOLEAN, CONF_VALUE_8_BIT(12),
1301 &xx_change.has_action, FALSE
1305 TYPE_INTEGER, CONF_VALUE_8_BIT(13),
1306 &xx_change.action_type, CA_NO_ACTION
1310 TYPE_INTEGER, CONF_VALUE_8_BIT(14),
1311 &xx_change.action_mode, CA_MODE_UNDEFINED
1315 TYPE_INTEGER, CONF_VALUE_16_BIT(6),
1316 &xx_change.action_arg, CA_ARG_UNDEFINED
1321 TYPE_ELEMENT, CONF_VALUE_16_BIT(7),
1322 &xx_change.action_element, EL_EMPTY_SPACE
1327 TYPE_CONTENT_LIST, CONF_VALUE_BYTES(1),
1328 &xx_change.target_content, EL_EMPTY_SPACE, NULL,
1329 &xx_num_contents, 1, 1
1339 static struct LevelFileConfigInfo chunk_config_GRPX[] =
1343 TYPE_STRING, CONF_VALUE_BYTES(1),
1344 &xx_ei.description[0], -1, NULL,
1345 &xx_string_length_unused, -1, MAX_ELEMENT_NAME_LEN,
1346 &xx_default_description[0]
1351 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1352 &xx_ei.use_gfx_element, FALSE
1356 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
1357 &xx_ei.gfx_element_initial, EL_EMPTY_SPACE
1362 TYPE_INTEGER, CONF_VALUE_8_BIT(2),
1363 &xx_group.choice_mode, ANIM_RANDOM
1368 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(2),
1369 &xx_group.element[0], EL_EMPTY_SPACE, NULL,
1370 &xx_group.num_elements, 1, MAX_ELEMENTS_IN_GROUP
1380 static struct LevelFileConfigInfo chunk_config_EMPX[] =
1384 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1385 &xx_ei.use_gfx_element, FALSE
1389 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
1390 &xx_ei.gfx_element_initial, EL_EMPTY_SPACE
1400 static struct LevelFileConfigInfo chunk_config_CONF[] = // (OBSOLETE)
1404 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
1405 &li.block_snap_field, TRUE
1409 TYPE_BOOLEAN, CONF_VALUE_8_BIT(13),
1410 &li.continuous_snapping, TRUE
1414 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
1415 &li.initial_player_stepsize[0], STEPSIZE_NORMAL
1419 TYPE_BOOLEAN, CONF_VALUE_8_BIT(10),
1420 &li.use_start_element[0], FALSE
1424 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
1425 &li.start_element[0], EL_PLAYER_1
1429 TYPE_BOOLEAN, CONF_VALUE_8_BIT(11),
1430 &li.use_artwork_element[0], FALSE
1434 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
1435 &li.artwork_element[0], EL_PLAYER_1
1439 TYPE_BOOLEAN, CONF_VALUE_8_BIT(12),
1440 &li.use_explosion_element[0], FALSE
1444 TYPE_ELEMENT, CONF_VALUE_16_BIT(3),
1445 &li.explosion_element[0], EL_PLAYER_1
1460 filetype_id_list[] =
1462 { LEVEL_FILE_TYPE_RND, "RND" },
1463 { LEVEL_FILE_TYPE_BD, "BD" },
1464 { LEVEL_FILE_TYPE_EM, "EM" },
1465 { LEVEL_FILE_TYPE_SP, "SP" },
1466 { LEVEL_FILE_TYPE_DX, "DX" },
1467 { LEVEL_FILE_TYPE_SB, "SB" },
1468 { LEVEL_FILE_TYPE_DC, "DC" },
1469 { LEVEL_FILE_TYPE_MM, "MM" },
1470 { LEVEL_FILE_TYPE_MM, "DF" },
1475 // ============================================================================
1476 // level file functions
1477 // ============================================================================
1479 static boolean check_special_flags(char *flag)
1481 if (strEqual(options.special_flags, flag) ||
1482 strEqual(leveldir_current->special_flags, flag))
1488 static struct DateInfo getCurrentDate(void)
1490 time_t epoch_seconds = time(NULL);
1491 struct tm *now = localtime(&epoch_seconds);
1492 struct DateInfo date;
1494 date.year = now->tm_year + 1900;
1495 date.month = now->tm_mon + 1;
1496 date.day = now->tm_mday;
1498 date.src = DATE_SRC_CLOCK;
1503 static void resetEventFlags(struct ElementChangeInfo *change)
1507 for (i = 0; i < NUM_CHANGE_EVENTS; i++)
1508 change->has_event[i] = FALSE;
1511 static void resetEventBits(void)
1515 for (i = 0; i < NUM_CE_BITFIELDS; i++)
1516 xx_event_bits[i] = 0;
1519 static void setEventFlagsFromEventBits(struct ElementChangeInfo *change)
1523 /* important: only change event flag if corresponding event bit is set
1524 (this is because all xx_event_bits[] values are loaded separately,
1525 and all xx_event_bits[] values are set back to zero before loading
1526 another value xx_event_bits[x] (each value representing 32 flags)) */
1528 for (i = 0; i < NUM_CHANGE_EVENTS; i++)
1529 if (xx_event_bits[CH_EVENT_BITFIELD_NR(i)] & CH_EVENT_BIT(i))
1530 change->has_event[i] = TRUE;
1533 static void setEventBitsFromEventFlags(struct ElementChangeInfo *change)
1537 /* in contrast to the above function setEventFlagsFromEventBits(), it
1538 would also be possible to set all bits in xx_event_bits[] to 0 or 1
1539 depending on the corresponding change->has_event[i] values here, as
1540 all xx_event_bits[] values are reset in resetEventBits() before */
1542 for (i = 0; i < NUM_CHANGE_EVENTS; i++)
1543 if (change->has_event[i])
1544 xx_event_bits[CH_EVENT_BITFIELD_NR(i)] |= CH_EVENT_BIT(i);
1547 static char *getDefaultElementDescription(struct ElementInfo *ei)
1549 static char description[MAX_ELEMENT_NAME_LEN + 1];
1550 char *default_description = (ei->custom_description != NULL ?
1551 ei->custom_description :
1552 ei->editor_description);
1555 // always start with reliable default values
1556 for (i = 0; i < MAX_ELEMENT_NAME_LEN + 1; i++)
1557 description[i] = '\0';
1559 // truncate element description to MAX_ELEMENT_NAME_LEN bytes
1560 strncpy(description, default_description, MAX_ELEMENT_NAME_LEN);
1562 return &description[0];
1565 static void setElementDescriptionToDefault(struct ElementInfo *ei)
1567 char *default_description = getDefaultElementDescription(ei);
1570 for (i = 0; i < MAX_ELEMENT_NAME_LEN + 1; i++)
1571 ei->description[i] = default_description[i];
1574 static void setConfigToDefaultsFromConfigList(struct LevelFileConfigInfo *conf)
1578 for (i = 0; conf[i].data_type != -1; i++)
1580 int default_value = conf[i].default_value;
1581 int data_type = conf[i].data_type;
1582 int conf_type = conf[i].conf_type;
1583 int byte_mask = conf_type & CONF_MASK_BYTES;
1585 if (byte_mask == CONF_MASK_MULTI_BYTES)
1587 int default_num_entities = conf[i].default_num_entities;
1588 int max_num_entities = conf[i].max_num_entities;
1590 *(int *)(conf[i].num_entities) = default_num_entities;
1592 if (data_type == TYPE_STRING)
1594 char *default_string = conf[i].default_string;
1595 char *string = (char *)(conf[i].value);
1597 strncpy(string, default_string, max_num_entities);
1599 else if (data_type == TYPE_ELEMENT_LIST)
1601 int *element_array = (int *)(conf[i].value);
1604 for (j = 0; j < max_num_entities; j++)
1605 element_array[j] = default_value;
1607 else if (data_type == TYPE_CONTENT_LIST)
1609 struct Content *content = (struct Content *)(conf[i].value);
1612 for (c = 0; c < max_num_entities; c++)
1613 for (y = 0; y < 3; y++)
1614 for (x = 0; x < 3; x++)
1615 content[c].e[x][y] = default_value;
1618 else // constant size configuration data (1, 2 or 4 bytes)
1620 if (data_type == TYPE_BOOLEAN)
1621 *(boolean *)(conf[i].value) = default_value;
1623 *(int *) (conf[i].value) = default_value;
1628 static void copyConfigFromConfigList(struct LevelFileConfigInfo *conf)
1632 for (i = 0; conf[i].data_type != -1; i++)
1634 int data_type = conf[i].data_type;
1635 int conf_type = conf[i].conf_type;
1636 int byte_mask = conf_type & CONF_MASK_BYTES;
1638 if (byte_mask == CONF_MASK_MULTI_BYTES)
1640 int max_num_entities = conf[i].max_num_entities;
1642 if (data_type == TYPE_STRING)
1644 char *string = (char *)(conf[i].value);
1645 char *string_copy = (char *)(conf[i].value_copy);
1647 strncpy(string_copy, string, max_num_entities);
1649 else if (data_type == TYPE_ELEMENT_LIST)
1651 int *element_array = (int *)(conf[i].value);
1652 int *element_array_copy = (int *)(conf[i].value_copy);
1655 for (j = 0; j < max_num_entities; j++)
1656 element_array_copy[j] = element_array[j];
1658 else if (data_type == TYPE_CONTENT_LIST)
1660 struct Content *content = (struct Content *)(conf[i].value);
1661 struct Content *content_copy = (struct Content *)(conf[i].value_copy);
1664 for (c = 0; c < max_num_entities; c++)
1665 for (y = 0; y < 3; y++)
1666 for (x = 0; x < 3; x++)
1667 content_copy[c].e[x][y] = content[c].e[x][y];
1670 else // constant size configuration data (1, 2 or 4 bytes)
1672 if (data_type == TYPE_BOOLEAN)
1673 *(boolean *)(conf[i].value_copy) = *(boolean *)(conf[i].value);
1675 *(int *) (conf[i].value_copy) = *(int *) (conf[i].value);
1680 void copyElementInfo(struct ElementInfo *ei_from, struct ElementInfo *ei_to)
1684 xx_ei = *ei_from; // copy element data into temporary buffer
1685 yy_ei = *ei_to; // copy element data into temporary buffer
1687 copyConfigFromConfigList(chunk_config_CUSX_base);
1692 // ---------- reinitialize and copy change pages ----------
1694 ei_to->num_change_pages = ei_from->num_change_pages;
1695 ei_to->current_change_page = ei_from->current_change_page;
1697 setElementChangePages(ei_to, ei_to->num_change_pages);
1699 for (i = 0; i < ei_to->num_change_pages; i++)
1700 ei_to->change_page[i] = ei_from->change_page[i];
1702 // ---------- copy group element info ----------
1703 if (ei_from->group != NULL && ei_to->group != NULL) // group or internal
1704 *ei_to->group = *ei_from->group;
1706 // mark this custom element as modified
1707 ei_to->modified_settings = TRUE;
1710 void setElementChangePages(struct ElementInfo *ei, int change_pages)
1712 int change_page_size = sizeof(struct ElementChangeInfo);
1714 ei->num_change_pages = MAX(1, change_pages);
1717 checked_realloc(ei->change_page, ei->num_change_pages * change_page_size);
1719 if (ei->current_change_page >= ei->num_change_pages)
1720 ei->current_change_page = ei->num_change_pages - 1;
1722 ei->change = &ei->change_page[ei->current_change_page];
1725 void setElementChangeInfoToDefaults(struct ElementChangeInfo *change)
1727 xx_change = *change; // copy change data into temporary buffer
1729 setConfigToDefaultsFromConfigList(chunk_config_CUSX_change);
1731 *change = xx_change;
1733 resetEventFlags(change);
1735 change->direct_action = 0;
1736 change->other_action = 0;
1738 change->pre_change_function = NULL;
1739 change->change_function = NULL;
1740 change->post_change_function = NULL;
1743 static void setLevelInfoToDefaults_Level(struct LevelInfo *level)
1747 li = *level; // copy level data into temporary buffer
1748 setConfigToDefaultsFromConfigList(chunk_config_INFO);
1749 *level = li; // copy temporary buffer back to level data
1751 setLevelInfoToDefaults_EM();
1752 setLevelInfoToDefaults_SP();
1753 setLevelInfoToDefaults_MM();
1755 level->native_em_level = &native_em_level;
1756 level->native_sp_level = &native_sp_level;
1757 level->native_mm_level = &native_mm_level;
1759 level->file_version = FILE_VERSION_ACTUAL;
1760 level->game_version = GAME_VERSION_ACTUAL;
1762 level->creation_date = getCurrentDate();
1764 level->encoding_16bit_field = TRUE;
1765 level->encoding_16bit_yamyam = TRUE;
1766 level->encoding_16bit_amoeba = TRUE;
1768 // clear level name and level author string buffers
1769 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
1770 level->name[i] = '\0';
1771 for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
1772 level->author[i] = '\0';
1774 // set level name and level author to default values
1775 strcpy(level->name, NAMELESS_LEVEL_NAME);
1776 strcpy(level->author, ANONYMOUS_NAME);
1778 // set level playfield to playable default level with player and exit
1779 for (x = 0; x < MAX_LEV_FIELDX; x++)
1780 for (y = 0; y < MAX_LEV_FIELDY; y++)
1781 level->field[x][y] = EL_SAND;
1783 level->field[0][0] = EL_PLAYER_1;
1784 level->field[STD_LEV_FIELDX - 1][STD_LEV_FIELDY - 1] = EL_EXIT_CLOSED;
1786 BorderElement = EL_STEELWALL;
1788 // detect custom elements when loading them
1789 level->file_has_custom_elements = FALSE;
1791 // set all bug compatibility flags to "false" => do not emulate this bug
1792 level->use_action_after_change_bug = FALSE;
1794 if (leveldir_current)
1796 // try to determine better author name than 'anonymous'
1797 if (!strEqual(leveldir_current->author, ANONYMOUS_NAME))
1799 strncpy(level->author, leveldir_current->author, MAX_LEVEL_AUTHOR_LEN);
1800 level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
1804 switch (LEVELCLASS(leveldir_current))
1806 case LEVELCLASS_TUTORIAL:
1807 strcpy(level->author, PROGRAM_AUTHOR_STRING);
1810 case LEVELCLASS_CONTRIB:
1811 strncpy(level->author, leveldir_current->name, MAX_LEVEL_AUTHOR_LEN);
1812 level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
1815 case LEVELCLASS_PRIVATE:
1816 strncpy(level->author, getRealName(), MAX_LEVEL_AUTHOR_LEN);
1817 level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
1821 // keep default value
1828 static void setLevelInfoToDefaults_Elements(struct LevelInfo *level)
1830 static boolean clipboard_elements_initialized = FALSE;
1833 InitElementPropertiesStatic();
1835 li = *level; // copy level data into temporary buffer
1836 setConfigToDefaultsFromConfigList(chunk_config_ELEM);
1837 *level = li; // copy temporary buffer back to level data
1839 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1842 struct ElementInfo *ei = &element_info[element];
1844 // never initialize clipboard elements after the very first time
1845 // (to be able to use clipboard elements between several levels)
1846 if (IS_CLIPBOARD_ELEMENT(element) && clipboard_elements_initialized)
1849 if (IS_ENVELOPE(element))
1851 int envelope_nr = element - EL_ENVELOPE_1;
1853 setConfigToDefaultsFromConfigList(chunk_config_NOTE);
1855 level->envelope[envelope_nr] = xx_envelope;
1858 if (IS_CUSTOM_ELEMENT(element) ||
1859 IS_GROUP_ELEMENT(element) ||
1860 IS_INTERNAL_ELEMENT(element))
1862 xx_ei = *ei; // copy element data into temporary buffer
1864 setConfigToDefaultsFromConfigList(chunk_config_CUSX_base);
1869 setElementChangePages(ei, 1);
1870 setElementChangeInfoToDefaults(ei->change);
1872 if (IS_CUSTOM_ELEMENT(element) ||
1873 IS_GROUP_ELEMENT(element) ||
1874 IS_INTERNAL_ELEMENT(element))
1876 setElementDescriptionToDefault(ei);
1878 ei->modified_settings = FALSE;
1881 if (IS_CUSTOM_ELEMENT(element) ||
1882 IS_INTERNAL_ELEMENT(element))
1884 // internal values used in level editor
1886 ei->access_type = 0;
1887 ei->access_layer = 0;
1888 ei->access_protected = 0;
1889 ei->walk_to_action = 0;
1890 ei->smash_targets = 0;
1893 ei->can_explode_by_fire = FALSE;
1894 ei->can_explode_smashed = FALSE;
1895 ei->can_explode_impact = FALSE;
1897 ei->current_change_page = 0;
1900 if (IS_GROUP_ELEMENT(element) ||
1901 IS_INTERNAL_ELEMENT(element))
1903 struct ElementGroupInfo *group;
1905 // initialize memory for list of elements in group
1906 if (ei->group == NULL)
1907 ei->group = checked_malloc(sizeof(struct ElementGroupInfo));
1911 xx_group = *group; // copy group data into temporary buffer
1913 setConfigToDefaultsFromConfigList(chunk_config_GRPX);
1918 if (IS_EMPTY_ELEMENT(element) ||
1919 IS_INTERNAL_ELEMENT(element))
1921 xx_ei = *ei; // copy element data into temporary buffer
1923 setConfigToDefaultsFromConfigList(chunk_config_EMPX);
1929 clipboard_elements_initialized = TRUE;
1932 static void setLevelInfoToDefaults(struct LevelInfo *level,
1933 boolean level_info_only,
1934 boolean reset_file_status)
1936 setLevelInfoToDefaults_Level(level);
1938 if (!level_info_only)
1939 setLevelInfoToDefaults_Elements(level);
1941 if (reset_file_status)
1943 level->no_valid_file = FALSE;
1944 level->no_level_file = FALSE;
1947 level->changed = FALSE;
1950 static void setFileInfoToDefaults(struct LevelFileInfo *level_file_info)
1952 level_file_info->nr = 0;
1953 level_file_info->type = LEVEL_FILE_TYPE_UNKNOWN;
1954 level_file_info->packed = FALSE;
1956 setString(&level_file_info->basename, NULL);
1957 setString(&level_file_info->filename, NULL);
1960 int getMappedElement_SB(int, boolean);
1962 static void ActivateLevelTemplate(void)
1966 if (check_special_flags("load_xsb_to_ces"))
1968 // fill smaller playfields with padding "beyond border wall" elements
1969 if (level.fieldx < level_template.fieldx ||
1970 level.fieldy < level_template.fieldy)
1972 short field[level.fieldx][level.fieldy];
1973 int new_fieldx = MAX(level.fieldx, level_template.fieldx);
1974 int new_fieldy = MAX(level.fieldy, level_template.fieldy);
1975 int pos_fieldx = (new_fieldx - level.fieldx) / 2;
1976 int pos_fieldy = (new_fieldy - level.fieldy) / 2;
1978 // copy old playfield (which is smaller than the visible area)
1979 for (y = 0; y < level.fieldy; y++) for (x = 0; x < level.fieldx; x++)
1980 field[x][y] = level.field[x][y];
1982 // fill new, larger playfield with "beyond border wall" elements
1983 for (y = 0; y < new_fieldy; y++) for (x = 0; x < new_fieldx; x++)
1984 level.field[x][y] = getMappedElement_SB('_', TRUE);
1986 // copy the old playfield to the middle of the new playfield
1987 for (y = 0; y < level.fieldy; y++) for (x = 0; x < level.fieldx; x++)
1988 level.field[pos_fieldx + x][pos_fieldy + y] = field[x][y];
1990 level.fieldx = new_fieldx;
1991 level.fieldy = new_fieldy;
1995 // Currently there is no special action needed to activate the template
1996 // data, because 'element_info' property settings overwrite the original
1997 // level data, while all other variables do not change.
1999 // Exception: 'from_level_template' elements in the original level playfield
2000 // are overwritten with the corresponding elements at the same position in
2001 // playfield from the level template.
2003 for (x = 0; x < level.fieldx; x++)
2004 for (y = 0; y < level.fieldy; y++)
2005 if (level.field[x][y] == EL_FROM_LEVEL_TEMPLATE)
2006 level.field[x][y] = level_template.field[x][y];
2008 if (check_special_flags("load_xsb_to_ces"))
2010 struct LevelInfo level_backup = level;
2012 // overwrite all individual level settings from template level settings
2013 level = level_template;
2015 // restore level file info
2016 level.file_info = level_backup.file_info;
2018 // restore playfield size
2019 level.fieldx = level_backup.fieldx;
2020 level.fieldy = level_backup.fieldy;
2022 // restore playfield content
2023 for (x = 0; x < level.fieldx; x++)
2024 for (y = 0; y < level.fieldy; y++)
2025 level.field[x][y] = level_backup.field[x][y];
2027 // restore name and author from individual level
2028 strcpy(level.name, level_backup.name);
2029 strcpy(level.author, level_backup.author);
2031 // restore flag "use_custom_template"
2032 level.use_custom_template = level_backup.use_custom_template;
2036 static char *getLevelFilenameFromBasename(char *basename)
2038 static char *filename = NULL;
2040 checked_free(filename);
2042 filename = getPath2(getCurrentLevelDir(), basename);
2047 static int getFileTypeFromBasename(char *basename)
2049 // !!! ALSO SEE COMMENT IN checkForPackageFromBasename() !!!
2051 static char *filename = NULL;
2052 struct stat file_status;
2054 // ---------- try to determine file type from filename ----------
2056 // check for typical filename of a Supaplex level package file
2057 if (strlen(basename) == 10 && strPrefixLower(basename, "levels.d"))
2058 return LEVEL_FILE_TYPE_SP;
2060 // check for typical filename of a Diamond Caves II level package file
2061 if (strSuffixLower(basename, ".dc") ||
2062 strSuffixLower(basename, ".dc2"))
2063 return LEVEL_FILE_TYPE_DC;
2065 // check for typical filename of a Sokoban level package file
2066 if (strSuffixLower(basename, ".xsb") &&
2067 strchr(basename, '%') == NULL)
2068 return LEVEL_FILE_TYPE_SB;
2070 // ---------- try to determine file type from filesize ----------
2072 checked_free(filename);
2073 filename = getPath2(getCurrentLevelDir(), basename);
2075 if (stat(filename, &file_status) == 0)
2077 // check for typical filesize of a Supaplex level package file
2078 if (file_status.st_size == 170496)
2079 return LEVEL_FILE_TYPE_SP;
2082 return LEVEL_FILE_TYPE_UNKNOWN;
2085 static int getFileTypeFromMagicBytes(char *filename, int type)
2089 if ((file = openFile(filename, MODE_READ)))
2091 char chunk_name[CHUNK_ID_LEN + 1];
2093 getFileChunkBE(file, chunk_name, NULL);
2095 if (strEqual(chunk_name, "MMII") ||
2096 strEqual(chunk_name, "MIRR"))
2097 type = LEVEL_FILE_TYPE_MM;
2105 static boolean checkForPackageFromBasename(char *basename)
2107 // !!! WON'T WORK ANYMORE IF getFileTypeFromBasename() ALSO DETECTS !!!
2108 // !!! SINGLE LEVELS (CURRENTLY ONLY DETECTS LEVEL PACKAGES !!!
2110 return (getFileTypeFromBasename(basename) != LEVEL_FILE_TYPE_UNKNOWN);
2113 static char *getSingleLevelBasenameExt(int nr, char *extension)
2115 static char basename[MAX_FILENAME_LEN];
2118 sprintf(basename, "%s", LEVELTEMPLATE_FILENAME);
2120 sprintf(basename, "%03d.%s", nr, extension);
2125 static char *getSingleLevelBasename(int nr)
2127 return getSingleLevelBasenameExt(nr, LEVELFILE_EXTENSION);
2130 static char *getPackedLevelBasename(int type)
2132 static char basename[MAX_FILENAME_LEN];
2133 char *directory = getCurrentLevelDir();
2135 DirectoryEntry *dir_entry;
2137 strcpy(basename, UNDEFINED_FILENAME); // default: undefined file
2139 if ((dir = openDirectory(directory)) == NULL)
2141 Warn("cannot read current level directory '%s'", directory);
2146 while ((dir_entry = readDirectory(dir)) != NULL) // loop all entries
2148 char *entry_basename = dir_entry->basename;
2149 int entry_type = getFileTypeFromBasename(entry_basename);
2151 if (entry_type != LEVEL_FILE_TYPE_UNKNOWN) // found valid level package
2153 if (type == LEVEL_FILE_TYPE_UNKNOWN ||
2156 strcpy(basename, entry_basename);
2163 closeDirectory(dir);
2168 static char *getSingleLevelFilename(int nr)
2170 return getLevelFilenameFromBasename(getSingleLevelBasename(nr));
2173 #if ENABLE_UNUSED_CODE
2174 static char *getPackedLevelFilename(int type)
2176 return getLevelFilenameFromBasename(getPackedLevelBasename(type));
2180 char *getDefaultLevelFilename(int nr)
2182 return getSingleLevelFilename(nr);
2185 #if ENABLE_UNUSED_CODE
2186 static void setLevelFileInfo_SingleLevelFilename(struct LevelFileInfo *lfi,
2190 lfi->packed = FALSE;
2192 setString(&lfi->basename, getSingleLevelBasename(lfi->nr, lfi->type));
2193 setString(&lfi->filename, getLevelFilenameFromBasename(lfi->basename));
2197 static void setLevelFileInfo_FormatLevelFilename(struct LevelFileInfo *lfi,
2198 int type, char *format, ...)
2200 static char basename[MAX_FILENAME_LEN];
2203 va_start(ap, format);
2204 vsprintf(basename, format, ap);
2208 lfi->packed = FALSE;
2210 setString(&lfi->basename, basename);
2211 setString(&lfi->filename, getLevelFilenameFromBasename(lfi->basename));
2214 static void setLevelFileInfo_PackedLevelFilename(struct LevelFileInfo *lfi,
2220 setString(&lfi->basename, getPackedLevelBasename(lfi->type));
2221 setString(&lfi->filename, getLevelFilenameFromBasename(lfi->basename));
2224 static int getFiletypeFromID(char *filetype_id)
2226 char *filetype_id_lower;
2227 int filetype = LEVEL_FILE_TYPE_UNKNOWN;
2230 if (filetype_id == NULL)
2231 return LEVEL_FILE_TYPE_UNKNOWN;
2233 filetype_id_lower = getStringToLower(filetype_id);
2235 for (i = 0; filetype_id_list[i].id != NULL; i++)
2237 char *id_lower = getStringToLower(filetype_id_list[i].id);
2239 if (strEqual(filetype_id_lower, id_lower))
2240 filetype = filetype_id_list[i].filetype;
2244 if (filetype != LEVEL_FILE_TYPE_UNKNOWN)
2248 free(filetype_id_lower);
2253 char *getLocalLevelTemplateFilename(void)
2255 return getDefaultLevelFilename(-1);
2258 char *getGlobalLevelTemplateFilename(void)
2260 // global variable "leveldir_current" must be modified in the loop below
2261 LevelDirTree *leveldir_current_last = leveldir_current;
2262 char *filename = NULL;
2264 // check for template level in path from current to topmost tree node
2266 while (leveldir_current != NULL)
2268 filename = getDefaultLevelFilename(-1);
2270 if (fileExists(filename))
2273 leveldir_current = leveldir_current->node_parent;
2276 // restore global variable "leveldir_current" modified in above loop
2277 leveldir_current = leveldir_current_last;
2282 static void determineLevelFileInfo_Filename(struct LevelFileInfo *lfi)
2286 // special case: level number is negative => check for level template file
2289 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
2290 getSingleLevelBasename(-1));
2292 // replace local level template filename with global template filename
2293 setString(&lfi->filename, getGlobalLevelTemplateFilename());
2295 // no fallback if template file not existing
2299 // special case: check for file name/pattern specified in "levelinfo.conf"
2300 if (leveldir_current->level_filename != NULL)
2302 int filetype = getFiletypeFromID(leveldir_current->level_filetype);
2304 setLevelFileInfo_FormatLevelFilename(lfi, filetype,
2305 leveldir_current->level_filename, nr);
2307 lfi->packed = checkForPackageFromBasename(leveldir_current->level_filename);
2309 if (fileExists(lfi->filename))
2312 else if (leveldir_current->level_filetype != NULL)
2314 int filetype = getFiletypeFromID(leveldir_current->level_filetype);
2316 // check for specified native level file with standard file name
2317 setLevelFileInfo_FormatLevelFilename(lfi, filetype,
2318 "%03d.%s", nr, LEVELFILE_EXTENSION);
2319 if (fileExists(lfi->filename))
2323 // check for native Rocks'n'Diamonds level file
2324 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
2325 "%03d.%s", nr, LEVELFILE_EXTENSION);
2326 if (fileExists(lfi->filename))
2329 // check for Emerald Mine level file (V1)
2330 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "a%c%c",
2331 'a' + (nr / 10) % 26, '0' + nr % 10);
2332 if (fileExists(lfi->filename))
2334 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "A%c%c",
2335 'A' + (nr / 10) % 26, '0' + nr % 10);
2336 if (fileExists(lfi->filename))
2339 // check for Emerald Mine level file (V2 to V5)
2340 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%d", nr);
2341 if (fileExists(lfi->filename))
2344 // check for Emerald Mine level file (V6 / single mode)
2345 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02ds", nr);
2346 if (fileExists(lfi->filename))
2348 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02dS", nr);
2349 if (fileExists(lfi->filename))
2352 // check for Emerald Mine level file (V6 / teamwork mode)
2353 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02dt", nr);
2354 if (fileExists(lfi->filename))
2356 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02dT", nr);
2357 if (fileExists(lfi->filename))
2360 // check for various packed level file formats
2361 setLevelFileInfo_PackedLevelFilename(lfi, LEVEL_FILE_TYPE_UNKNOWN);
2362 if (fileExists(lfi->filename))
2365 // no known level file found -- use default values (and fail later)
2366 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
2367 "%03d.%s", nr, LEVELFILE_EXTENSION);
2370 static void determineLevelFileInfo_Filetype(struct LevelFileInfo *lfi)
2372 if (lfi->type == LEVEL_FILE_TYPE_UNKNOWN)
2373 lfi->type = getFileTypeFromBasename(lfi->basename);
2375 if (lfi->type == LEVEL_FILE_TYPE_RND)
2376 lfi->type = getFileTypeFromMagicBytes(lfi->filename, lfi->type);
2379 static void setLevelFileInfo(struct LevelFileInfo *level_file_info, int nr)
2381 // always start with reliable default values
2382 setFileInfoToDefaults(level_file_info);
2384 level_file_info->nr = nr; // set requested level number
2386 determineLevelFileInfo_Filename(level_file_info);
2387 determineLevelFileInfo_Filetype(level_file_info);
2390 static void copyLevelFileInfo(struct LevelFileInfo *lfi_from,
2391 struct LevelFileInfo *lfi_to)
2393 lfi_to->nr = lfi_from->nr;
2394 lfi_to->type = lfi_from->type;
2395 lfi_to->packed = lfi_from->packed;
2397 setString(&lfi_to->basename, lfi_from->basename);
2398 setString(&lfi_to->filename, lfi_from->filename);
2401 // ----------------------------------------------------------------------------
2402 // functions for loading R'n'D level
2403 // ----------------------------------------------------------------------------
2405 int getMappedElement(int element)
2407 // remap some (historic, now obsolete) elements
2411 case EL_PLAYER_OBSOLETE:
2412 element = EL_PLAYER_1;
2415 case EL_KEY_OBSOLETE:
2419 case EL_EM_KEY_1_FILE_OBSOLETE:
2420 element = EL_EM_KEY_1;
2423 case EL_EM_KEY_2_FILE_OBSOLETE:
2424 element = EL_EM_KEY_2;
2427 case EL_EM_KEY_3_FILE_OBSOLETE:
2428 element = EL_EM_KEY_3;
2431 case EL_EM_KEY_4_FILE_OBSOLETE:
2432 element = EL_EM_KEY_4;
2435 case EL_ENVELOPE_OBSOLETE:
2436 element = EL_ENVELOPE_1;
2444 if (element >= NUM_FILE_ELEMENTS)
2446 Warn("invalid level element %d", element);
2448 element = EL_UNKNOWN;
2456 static int getMappedElementByVersion(int element, int game_version)
2458 // remap some elements due to certain game version
2460 if (game_version <= VERSION_IDENT(2,2,0,0))
2462 // map game font elements
2463 element = (element == EL_CHAR('[') ? EL_CHAR_AUMLAUT :
2464 element == EL_CHAR('\\') ? EL_CHAR_OUMLAUT :
2465 element == EL_CHAR(']') ? EL_CHAR_UUMLAUT :
2466 element == EL_CHAR('^') ? EL_CHAR_COPYRIGHT : element);
2469 if (game_version < VERSION_IDENT(3,0,0,0))
2471 // map Supaplex gravity tube elements
2472 element = (element == EL_SP_GRAVITY_PORT_LEFT ? EL_SP_PORT_LEFT :
2473 element == EL_SP_GRAVITY_PORT_RIGHT ? EL_SP_PORT_RIGHT :
2474 element == EL_SP_GRAVITY_PORT_UP ? EL_SP_PORT_UP :
2475 element == EL_SP_GRAVITY_PORT_DOWN ? EL_SP_PORT_DOWN :
2482 static int LoadLevel_VERS(File *file, int chunk_size, struct LevelInfo *level)
2484 level->file_version = getFileVersion(file);
2485 level->game_version = getFileVersion(file);
2490 static int LoadLevel_DATE(File *file, int chunk_size, struct LevelInfo *level)
2492 level->creation_date.year = getFile16BitBE(file);
2493 level->creation_date.month = getFile8Bit(file);
2494 level->creation_date.day = getFile8Bit(file);
2496 level->creation_date.src = DATE_SRC_LEVELFILE;
2501 static int LoadLevel_HEAD(File *file, int chunk_size, struct LevelInfo *level)
2503 int initial_player_stepsize;
2504 int initial_player_gravity;
2507 level->fieldx = getFile8Bit(file);
2508 level->fieldy = getFile8Bit(file);
2510 level->time = getFile16BitBE(file);
2511 level->gems_needed = getFile16BitBE(file);
2513 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
2514 level->name[i] = getFile8Bit(file);
2515 level->name[MAX_LEVEL_NAME_LEN] = 0;
2517 for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
2518 level->score[i] = getFile8Bit(file);
2520 level->num_yamyam_contents = STD_ELEMENT_CONTENTS;
2521 for (i = 0; i < STD_ELEMENT_CONTENTS; i++)
2522 for (y = 0; y < 3; y++)
2523 for (x = 0; x < 3; x++)
2524 level->yamyam_content[i].e[x][y] = getMappedElement(getFile8Bit(file));
2526 level->amoeba_speed = getFile8Bit(file);
2527 level->time_magic_wall = getFile8Bit(file);
2528 level->time_wheel = getFile8Bit(file);
2529 level->amoeba_content = getMappedElement(getFile8Bit(file));
2531 initial_player_stepsize = (getFile8Bit(file) == 1 ? STEPSIZE_FAST :
2534 for (i = 0; i < MAX_PLAYERS; i++)
2535 level->initial_player_stepsize[i] = initial_player_stepsize;
2537 initial_player_gravity = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2539 for (i = 0; i < MAX_PLAYERS; i++)
2540 level->initial_player_gravity[i] = initial_player_gravity;
2542 level->encoding_16bit_field = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2543 level->em_slippery_gems = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2545 level->use_custom_template = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2547 level->block_last_field = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2548 level->sp_block_last_field = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2549 level->can_move_into_acid_bits = getFile32BitBE(file);
2550 level->dont_collide_with_bits = getFile8Bit(file);
2552 level->use_spring_bug = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2553 level->use_step_counter = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2555 level->instant_relocation = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2556 level->can_pass_to_walkable = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2557 level->grow_into_diggable = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2559 level->game_engine_type = getFile8Bit(file);
2561 ReadUnusedBytesFromFile(file, LEVEL_CHUNK_HEAD_UNUSED);
2566 static int LoadLevel_NAME(File *file, int chunk_size, struct LevelInfo *level)
2570 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
2571 level->name[i] = getFile8Bit(file);
2572 level->name[MAX_LEVEL_NAME_LEN] = 0;
2577 static int LoadLevel_AUTH(File *file, int chunk_size, struct LevelInfo *level)
2581 for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
2582 level->author[i] = getFile8Bit(file);
2583 level->author[MAX_LEVEL_AUTHOR_LEN] = 0;
2588 static int LoadLevel_BODY(File *file, int chunk_size, struct LevelInfo *level)
2591 int chunk_size_expected = level->fieldx * level->fieldy;
2593 /* Note: "chunk_size" was wrong before version 2.0 when elements are
2594 stored with 16-bit encoding (and should be twice as big then).
2595 Even worse, playfield data was stored 16-bit when only yamyam content
2596 contained 16-bit elements and vice versa. */
2598 if (level->encoding_16bit_field && level->file_version >= FILE_VERSION_2_0)
2599 chunk_size_expected *= 2;
2601 if (chunk_size_expected != chunk_size)
2603 ReadUnusedBytesFromFile(file, chunk_size);
2604 return chunk_size_expected;
2607 for (y = 0; y < level->fieldy; y++)
2608 for (x = 0; x < level->fieldx; x++)
2609 level->field[x][y] =
2610 getMappedElement(level->encoding_16bit_field ? getFile16BitBE(file) :
2615 static int LoadLevel_CONT(File *file, int chunk_size, struct LevelInfo *level)
2618 int header_size = 4;
2619 int content_size = MAX_ELEMENT_CONTENTS * 3 * 3;
2620 int chunk_size_expected = header_size + content_size;
2622 /* Note: "chunk_size" was wrong before version 2.0 when elements are
2623 stored with 16-bit encoding (and should be twice as big then).
2624 Even worse, playfield data was stored 16-bit when only yamyam content
2625 contained 16-bit elements and vice versa. */
2627 if (level->encoding_16bit_field && level->file_version >= FILE_VERSION_2_0)
2628 chunk_size_expected += content_size;
2630 if (chunk_size_expected != chunk_size)
2632 ReadUnusedBytesFromFile(file, chunk_size);
2633 return chunk_size_expected;
2637 level->num_yamyam_contents = getFile8Bit(file);
2641 // correct invalid number of content fields -- should never happen
2642 if (level->num_yamyam_contents < 1 ||
2643 level->num_yamyam_contents > MAX_ELEMENT_CONTENTS)
2644 level->num_yamyam_contents = STD_ELEMENT_CONTENTS;
2646 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
2647 for (y = 0; y < 3; y++)
2648 for (x = 0; x < 3; x++)
2649 level->yamyam_content[i].e[x][y] =
2650 getMappedElement(level->encoding_16bit_field ?
2651 getFile16BitBE(file) : getFile8Bit(file));
2655 static int LoadLevel_CNT2(File *file, int chunk_size, struct LevelInfo *level)
2660 int content_array[MAX_ELEMENT_CONTENTS][3][3];
2662 element = getMappedElement(getFile16BitBE(file));
2663 num_contents = getFile8Bit(file);
2665 getFile8Bit(file); // content x size (unused)
2666 getFile8Bit(file); // content y size (unused)
2668 ReadUnusedBytesFromFile(file, LEVEL_CHUNK_CNT2_UNUSED);
2670 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
2671 for (y = 0; y < 3; y++)
2672 for (x = 0; x < 3; x++)
2673 content_array[i][x][y] = getMappedElement(getFile16BitBE(file));
2675 // correct invalid number of content fields -- should never happen
2676 if (num_contents < 1 || num_contents > MAX_ELEMENT_CONTENTS)
2677 num_contents = STD_ELEMENT_CONTENTS;
2679 if (element == EL_YAMYAM)
2681 level->num_yamyam_contents = num_contents;
2683 for (i = 0; i < num_contents; i++)
2684 for (y = 0; y < 3; y++)
2685 for (x = 0; x < 3; x++)
2686 level->yamyam_content[i].e[x][y] = content_array[i][x][y];
2688 else if (element == EL_BD_AMOEBA)
2690 level->amoeba_content = content_array[0][0][0];
2694 Warn("cannot load content for element '%d'", element);
2700 static int LoadLevel_CNT3(File *file, int chunk_size, struct LevelInfo *level)
2706 int chunk_size_expected;
2708 element = getMappedElement(getFile16BitBE(file));
2709 if (!IS_ENVELOPE(element))
2710 element = EL_ENVELOPE_1;
2712 envelope_nr = element - EL_ENVELOPE_1;
2714 envelope_len = getFile16BitBE(file);
2716 level->envelope[envelope_nr].xsize = getFile8Bit(file);
2717 level->envelope[envelope_nr].ysize = getFile8Bit(file);
2719 ReadUnusedBytesFromFile(file, LEVEL_CHUNK_CNT3_UNUSED);
2721 chunk_size_expected = LEVEL_CHUNK_CNT3_SIZE(envelope_len);
2722 if (chunk_size_expected != chunk_size)
2724 ReadUnusedBytesFromFile(file, chunk_size - LEVEL_CHUNK_CNT3_HEADER);
2725 return chunk_size_expected;
2728 for (i = 0; i < envelope_len; i++)
2729 level->envelope[envelope_nr].text[i] = getFile8Bit(file);
2734 static int LoadLevel_CUS1(File *file, int chunk_size, struct LevelInfo *level)
2736 int num_changed_custom_elements = getFile16BitBE(file);
2737 int chunk_size_expected = 2 + num_changed_custom_elements * 6;
2740 if (chunk_size_expected != chunk_size)
2742 ReadUnusedBytesFromFile(file, chunk_size - 2);
2743 return chunk_size_expected;
2746 for (i = 0; i < num_changed_custom_elements; i++)
2748 int element = getMappedElement(getFile16BitBE(file));
2749 int properties = getFile32BitBE(file);
2751 if (IS_CUSTOM_ELEMENT(element))
2752 element_info[element].properties[EP_BITFIELD_BASE_NR] = properties;
2754 Warn("invalid custom element number %d", element);
2756 // older game versions that wrote level files with CUS1 chunks used
2757 // different default push delay values (not yet stored in level file)
2758 element_info[element].push_delay_fixed = 2;
2759 element_info[element].push_delay_random = 8;
2762 level->file_has_custom_elements = TRUE;
2767 static int LoadLevel_CUS2(File *file, int chunk_size, struct LevelInfo *level)
2769 int num_changed_custom_elements = getFile16BitBE(file);
2770 int chunk_size_expected = 2 + num_changed_custom_elements * 4;
2773 if (chunk_size_expected != chunk_size)
2775 ReadUnusedBytesFromFile(file, chunk_size - 2);
2776 return chunk_size_expected;
2779 for (i = 0; i < num_changed_custom_elements; i++)
2781 int element = getMappedElement(getFile16BitBE(file));
2782 int custom_target_element = getMappedElement(getFile16BitBE(file));
2784 if (IS_CUSTOM_ELEMENT(element))
2785 element_info[element].change->target_element = custom_target_element;
2787 Warn("invalid custom element number %d", element);
2790 level->file_has_custom_elements = TRUE;
2795 static int LoadLevel_CUS3(File *file, int chunk_size, struct LevelInfo *level)
2797 int num_changed_custom_elements = getFile16BitBE(file);
2798 int chunk_size_expected = LEVEL_CHUNK_CUS3_SIZE(num_changed_custom_elements);
2801 if (chunk_size_expected != chunk_size)
2803 ReadUnusedBytesFromFile(file, chunk_size - 2);
2804 return chunk_size_expected;
2807 for (i = 0; i < num_changed_custom_elements; i++)
2809 int element = getMappedElement(getFile16BitBE(file));
2810 struct ElementInfo *ei = &element_info[element];
2811 unsigned int event_bits;
2813 if (!IS_CUSTOM_ELEMENT(element))
2815 Warn("invalid custom element number %d", element);
2817 element = EL_INTERNAL_DUMMY;
2820 for (j = 0; j < MAX_ELEMENT_NAME_LEN; j++)
2821 ei->description[j] = getFile8Bit(file);
2822 ei->description[MAX_ELEMENT_NAME_LEN] = 0;
2824 ei->properties[EP_BITFIELD_BASE_NR] = getFile32BitBE(file);
2826 // some free bytes for future properties and padding
2827 ReadUnusedBytesFromFile(file, 7);
2829 ei->use_gfx_element = getFile8Bit(file);
2830 ei->gfx_element_initial = getMappedElement(getFile16BitBE(file));
2832 ei->collect_score_initial = getFile8Bit(file);
2833 ei->collect_count_initial = getFile8Bit(file);
2835 ei->push_delay_fixed = getFile16BitBE(file);
2836 ei->push_delay_random = getFile16BitBE(file);
2837 ei->move_delay_fixed = getFile16BitBE(file);
2838 ei->move_delay_random = getFile16BitBE(file);
2840 ei->move_pattern = getFile16BitBE(file);
2841 ei->move_direction_initial = getFile8Bit(file);
2842 ei->move_stepsize = getFile8Bit(file);
2844 for (y = 0; y < 3; y++)
2845 for (x = 0; x < 3; x++)
2846 ei->content.e[x][y] = getMappedElement(getFile16BitBE(file));
2848 // bits 0 - 31 of "has_event[]"
2849 event_bits = getFile32BitBE(file);
2850 for (j = 0; j < MIN(NUM_CHANGE_EVENTS, 32); j++)
2851 if (event_bits & (1 << j))
2852 ei->change->has_event[j] = TRUE;
2854 ei->change->target_element = getMappedElement(getFile16BitBE(file));
2856 ei->change->delay_fixed = getFile16BitBE(file);
2857 ei->change->delay_random = getFile16BitBE(file);
2858 ei->change->delay_frames = getFile16BitBE(file);
2860 ei->change->initial_trigger_element= getMappedElement(getFile16BitBE(file));
2862 ei->change->explode = getFile8Bit(file);
2863 ei->change->use_target_content = getFile8Bit(file);
2864 ei->change->only_if_complete = getFile8Bit(file);
2865 ei->change->use_random_replace = getFile8Bit(file);
2867 ei->change->random_percentage = getFile8Bit(file);
2868 ei->change->replace_when = getFile8Bit(file);
2870 for (y = 0; y < 3; y++)
2871 for (x = 0; x < 3; x++)
2872 ei->change->target_content.e[x][y] =
2873 getMappedElement(getFile16BitBE(file));
2875 ei->slippery_type = getFile8Bit(file);
2877 // some free bytes for future properties and padding
2878 ReadUnusedBytesFromFile(file, LEVEL_CPART_CUS3_UNUSED);
2880 // mark that this custom element has been modified
2881 ei->modified_settings = TRUE;
2884 level->file_has_custom_elements = TRUE;
2889 static int LoadLevel_CUS4(File *file, int chunk_size, struct LevelInfo *level)
2891 struct ElementInfo *ei;
2892 int chunk_size_expected;
2896 // ---------- custom element base property values (96 bytes) ----------------
2898 element = getMappedElement(getFile16BitBE(file));
2900 if (!IS_CUSTOM_ELEMENT(element))
2902 Warn("invalid custom element number %d", element);
2904 ReadUnusedBytesFromFile(file, chunk_size - 2);
2909 ei = &element_info[element];
2911 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
2912 ei->description[i] = getFile8Bit(file);
2913 ei->description[MAX_ELEMENT_NAME_LEN] = 0;
2915 ei->properties[EP_BITFIELD_BASE_NR] = getFile32BitBE(file);
2917 ReadUnusedBytesFromFile(file, 4); // reserved for more base properties
2919 ei->num_change_pages = getFile8Bit(file);
2921 chunk_size_expected = LEVEL_CHUNK_CUS4_SIZE(ei->num_change_pages);
2922 if (chunk_size_expected != chunk_size)
2924 ReadUnusedBytesFromFile(file, chunk_size - 43);
2925 return chunk_size_expected;
2928 ei->ce_value_fixed_initial = getFile16BitBE(file);
2929 ei->ce_value_random_initial = getFile16BitBE(file);
2930 ei->use_last_ce_value = getFile8Bit(file);
2932 ei->use_gfx_element = getFile8Bit(file);
2933 ei->gfx_element_initial = getMappedElement(getFile16BitBE(file));
2935 ei->collect_score_initial = getFile8Bit(file);
2936 ei->collect_count_initial = getFile8Bit(file);
2938 ei->drop_delay_fixed = getFile8Bit(file);
2939 ei->push_delay_fixed = getFile8Bit(file);
2940 ei->drop_delay_random = getFile8Bit(file);
2941 ei->push_delay_random = getFile8Bit(file);
2942 ei->move_delay_fixed = getFile16BitBE(file);
2943 ei->move_delay_random = getFile16BitBE(file);
2945 // bits 0 - 15 of "move_pattern" ...
2946 ei->move_pattern = getFile16BitBE(file);
2947 ei->move_direction_initial = getFile8Bit(file);
2948 ei->move_stepsize = getFile8Bit(file);
2950 ei->slippery_type = getFile8Bit(file);
2952 for (y = 0; y < 3; y++)
2953 for (x = 0; x < 3; x++)
2954 ei->content.e[x][y] = getMappedElement(getFile16BitBE(file));
2956 ei->move_enter_element = getMappedElement(getFile16BitBE(file));
2957 ei->move_leave_element = getMappedElement(getFile16BitBE(file));
2958 ei->move_leave_type = getFile8Bit(file);
2960 // ... bits 16 - 31 of "move_pattern" (not nice, but downward compatible)
2961 ei->move_pattern |= (getFile16BitBE(file) << 16);
2963 ei->access_direction = getFile8Bit(file);
2965 ei->explosion_delay = getFile8Bit(file);
2966 ei->ignition_delay = getFile8Bit(file);
2967 ei->explosion_type = getFile8Bit(file);
2969 // some free bytes for future custom property values and padding
2970 ReadUnusedBytesFromFile(file, 1);
2972 // ---------- change page property values (48 bytes) ------------------------
2974 setElementChangePages(ei, ei->num_change_pages);
2976 for (i = 0; i < ei->num_change_pages; i++)
2978 struct ElementChangeInfo *change = &ei->change_page[i];
2979 unsigned int event_bits;
2981 // always start with reliable default values
2982 setElementChangeInfoToDefaults(change);
2984 // bits 0 - 31 of "has_event[]" ...
2985 event_bits = getFile32BitBE(file);
2986 for (j = 0; j < MIN(NUM_CHANGE_EVENTS, 32); j++)
2987 if (event_bits & (1 << j))
2988 change->has_event[j] = TRUE;
2990 change->target_element = getMappedElement(getFile16BitBE(file));
2992 change->delay_fixed = getFile16BitBE(file);
2993 change->delay_random = getFile16BitBE(file);
2994 change->delay_frames = getFile16BitBE(file);
2996 change->initial_trigger_element = getMappedElement(getFile16BitBE(file));
2998 change->explode = getFile8Bit(file);
2999 change->use_target_content = getFile8Bit(file);
3000 change->only_if_complete = getFile8Bit(file);
3001 change->use_random_replace = getFile8Bit(file);
3003 change->random_percentage = getFile8Bit(file);
3004 change->replace_when = getFile8Bit(file);
3006 for (y = 0; y < 3; y++)
3007 for (x = 0; x < 3; x++)
3008 change->target_content.e[x][y]= getMappedElement(getFile16BitBE(file));
3010 change->can_change = getFile8Bit(file);
3012 change->trigger_side = getFile8Bit(file);
3014 change->trigger_player = getFile8Bit(file);
3015 change->trigger_page = getFile8Bit(file);
3017 change->trigger_page = (change->trigger_page == CH_PAGE_ANY_FILE ?
3018 CH_PAGE_ANY : (1 << change->trigger_page));
3020 change->has_action = getFile8Bit(file);
3021 change->action_type = getFile8Bit(file);
3022 change->action_mode = getFile8Bit(file);
3023 change->action_arg = getFile16BitBE(file);
3025 // ... bits 32 - 39 of "has_event[]" (not nice, but downward compatible)
3026 event_bits = getFile8Bit(file);
3027 for (j = 32; j < NUM_CHANGE_EVENTS; j++)
3028 if (event_bits & (1 << (j - 32)))
3029 change->has_event[j] = TRUE;
3032 // mark this custom element as modified
3033 ei->modified_settings = TRUE;
3035 level->file_has_custom_elements = TRUE;
3040 static int LoadLevel_GRP1(File *file, int chunk_size, struct LevelInfo *level)
3042 struct ElementInfo *ei;
3043 struct ElementGroupInfo *group;
3047 element = getMappedElement(getFile16BitBE(file));
3049 if (!IS_GROUP_ELEMENT(element))
3051 Warn("invalid group element number %d", element);
3053 ReadUnusedBytesFromFile(file, chunk_size - 2);
3058 ei = &element_info[element];
3060 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
3061 ei->description[i] = getFile8Bit(file);
3062 ei->description[MAX_ELEMENT_NAME_LEN] = 0;
3064 group = element_info[element].group;
3066 group->num_elements = getFile8Bit(file);
3068 ei->use_gfx_element = getFile8Bit(file);
3069 ei->gfx_element_initial = getMappedElement(getFile16BitBE(file));
3071 group->choice_mode = getFile8Bit(file);
3073 // some free bytes for future values and padding
3074 ReadUnusedBytesFromFile(file, 3);
3076 for (i = 0; i < MAX_ELEMENTS_IN_GROUP; i++)
3077 group->element[i] = getMappedElement(getFile16BitBE(file));
3079 // mark this group element as modified
3080 element_info[element].modified_settings = TRUE;
3082 level->file_has_custom_elements = TRUE;
3087 static int LoadLevel_MicroChunk(File *file, struct LevelFileConfigInfo *conf,
3088 int element, int real_element)
3090 int micro_chunk_size = 0;
3091 int conf_type = getFile8Bit(file);
3092 int byte_mask = conf_type & CONF_MASK_BYTES;
3093 boolean element_found = FALSE;
3096 micro_chunk_size += 1;
3098 if (byte_mask == CONF_MASK_MULTI_BYTES)
3100 int num_bytes = getFile16BitBE(file);
3101 byte *buffer = checked_malloc(num_bytes);
3103 ReadBytesFromFile(file, buffer, num_bytes);
3105 for (i = 0; conf[i].data_type != -1; i++)
3107 if (conf[i].element == element &&
3108 conf[i].conf_type == conf_type)
3110 int data_type = conf[i].data_type;
3111 int num_entities = num_bytes / CONF_ENTITY_NUM_BYTES(data_type);
3112 int max_num_entities = conf[i].max_num_entities;
3114 if (num_entities > max_num_entities)
3116 Warn("truncating number of entities for element %d from %d to %d",
3117 element, num_entities, max_num_entities);
3119 num_entities = max_num_entities;
3122 if (num_entities == 0 && (data_type == TYPE_ELEMENT_LIST ||
3123 data_type == TYPE_CONTENT_LIST))
3125 // for element and content lists, zero entities are not allowed
3126 Warn("found empty list of entities for element %d", element);
3128 // do not set "num_entities" here to prevent reading behind buffer
3130 *(int *)(conf[i].num_entities) = 1; // at least one is required
3134 *(int *)(conf[i].num_entities) = num_entities;
3137 element_found = TRUE;
3139 if (data_type == TYPE_STRING)
3141 char *string = (char *)(conf[i].value);
3144 for (j = 0; j < max_num_entities; j++)
3145 string[j] = (j < num_entities ? buffer[j] : '\0');
3147 else if (data_type == TYPE_ELEMENT_LIST)
3149 int *element_array = (int *)(conf[i].value);
3152 for (j = 0; j < num_entities; j++)
3154 getMappedElement(CONF_ELEMENTS_ELEMENT(buffer, j));
3156 else if (data_type == TYPE_CONTENT_LIST)
3158 struct Content *content= (struct Content *)(conf[i].value);
3161 for (c = 0; c < num_entities; c++)
3162 for (y = 0; y < 3; y++)
3163 for (x = 0; x < 3; x++)
3164 content[c].e[x][y] =
3165 getMappedElement(CONF_CONTENTS_ELEMENT(buffer, c, x, y));
3168 element_found = FALSE;
3174 checked_free(buffer);
3176 micro_chunk_size += 2 + num_bytes;
3178 else // constant size configuration data (1, 2 or 4 bytes)
3180 int value = (byte_mask == CONF_MASK_1_BYTE ? getFile8Bit (file) :
3181 byte_mask == CONF_MASK_2_BYTE ? getFile16BitBE(file) :
3182 byte_mask == CONF_MASK_4_BYTE ? getFile32BitBE(file) : 0);
3184 for (i = 0; conf[i].data_type != -1; i++)
3186 if (conf[i].element == element &&
3187 conf[i].conf_type == conf_type)
3189 int data_type = conf[i].data_type;
3191 if (data_type == TYPE_ELEMENT)
3192 value = getMappedElement(value);
3194 if (data_type == TYPE_BOOLEAN)
3195 *(boolean *)(conf[i].value) = (value ? TRUE : FALSE);
3197 *(int *) (conf[i].value) = value;
3199 element_found = TRUE;
3205 micro_chunk_size += CONF_VALUE_NUM_BYTES(byte_mask);
3210 char *error_conf_chunk_bytes =
3211 (byte_mask == CONF_MASK_1_BYTE ? "CONF_VALUE_8_BIT" :
3212 byte_mask == CONF_MASK_2_BYTE ? "CONF_VALUE_16_BIT" :
3213 byte_mask == CONF_MASK_4_BYTE ? "CONF_VALUE_32_BIT" :"CONF_VALUE_BYTES");
3214 int error_conf_chunk_token = conf_type & CONF_MASK_TOKEN;
3215 int error_element = real_element;
3217 Warn("cannot load micro chunk '%s(%d)' value for element %d ['%s']",
3218 error_conf_chunk_bytes, error_conf_chunk_token,
3219 error_element, EL_NAME(error_element));
3222 return micro_chunk_size;
3225 static int LoadLevel_INFO(File *file, int chunk_size, struct LevelInfo *level)
3227 int real_chunk_size = 0;
3229 li = *level; // copy level data into temporary buffer
3231 while (!checkEndOfFile(file))
3233 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_INFO, -1, -1);
3235 if (real_chunk_size >= chunk_size)
3239 *level = li; // copy temporary buffer back to level data
3241 return real_chunk_size;
3244 static int LoadLevel_CONF(File *file, int chunk_size, struct LevelInfo *level)
3246 int real_chunk_size = 0;
3248 li = *level; // copy level data into temporary buffer
3250 while (!checkEndOfFile(file))
3252 int element = getMappedElement(getFile16BitBE(file));
3254 real_chunk_size += 2;
3255 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_CONF,
3257 if (real_chunk_size >= chunk_size)
3261 *level = li; // copy temporary buffer back to level data
3263 return real_chunk_size;
3266 static int LoadLevel_ELEM(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 int element = getMappedElement(getFile16BitBE(file));
3276 real_chunk_size += 2;
3277 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_ELEM,
3279 if (real_chunk_size >= chunk_size)
3283 *level = li; // copy temporary buffer back to level data
3285 return real_chunk_size;
3288 static int LoadLevel_NOTE(File *file, int chunk_size, struct LevelInfo *level)
3290 int element = getMappedElement(getFile16BitBE(file));
3291 int envelope_nr = element - EL_ENVELOPE_1;
3292 int real_chunk_size = 2;
3294 xx_envelope = level->envelope[envelope_nr]; // copy into temporary buffer
3296 while (!checkEndOfFile(file))
3298 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_NOTE,
3301 if (real_chunk_size >= chunk_size)
3305 level->envelope[envelope_nr] = xx_envelope; // copy from temporary buffer
3307 return real_chunk_size;
3310 static int LoadLevel_CUSX(File *file, int chunk_size, struct LevelInfo *level)
3312 int element = getMappedElement(getFile16BitBE(file));
3313 int real_chunk_size = 2;
3314 struct ElementInfo *ei = &element_info[element];
3317 xx_ei = *ei; // copy element data into temporary buffer
3319 xx_ei.num_change_pages = -1;
3321 while (!checkEndOfFile(file))
3323 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_CUSX_base,
3325 if (xx_ei.num_change_pages != -1)
3328 if (real_chunk_size >= chunk_size)
3334 if (ei->num_change_pages == -1)
3336 Warn("LoadLevel_CUSX(): missing 'num_change_pages' for '%s'",
3339 ei->num_change_pages = 1;
3341 setElementChangePages(ei, 1);
3342 setElementChangeInfoToDefaults(ei->change);
3344 return real_chunk_size;
3347 // initialize number of change pages stored for this custom element
3348 setElementChangePages(ei, ei->num_change_pages);
3349 for (i = 0; i < ei->num_change_pages; i++)
3350 setElementChangeInfoToDefaults(&ei->change_page[i]);
3352 // start with reading properties for the first change page
3353 xx_current_change_page = 0;
3355 while (!checkEndOfFile(file))
3357 struct ElementChangeInfo *change = &ei->change_page[xx_current_change_page];
3359 xx_change = *change; // copy change data into temporary buffer
3361 resetEventBits(); // reset bits; change page might have changed
3363 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_CUSX_change,
3366 *change = xx_change;
3368 setEventFlagsFromEventBits(change);
3370 if (real_chunk_size >= chunk_size)
3374 level->file_has_custom_elements = TRUE;
3376 return real_chunk_size;
3379 static int LoadLevel_GRPX(File *file, int chunk_size, struct LevelInfo *level)
3381 int element = getMappedElement(getFile16BitBE(file));
3382 int real_chunk_size = 2;
3383 struct ElementInfo *ei = &element_info[element];
3384 struct ElementGroupInfo *group = ei->group;
3386 xx_ei = *ei; // copy element data into temporary buffer
3387 xx_group = *group; // copy group data into temporary buffer
3389 while (!checkEndOfFile(file))
3391 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_GRPX,
3394 if (real_chunk_size >= chunk_size)
3401 level->file_has_custom_elements = TRUE;
3403 return real_chunk_size;
3406 static int LoadLevel_EMPX(File *file, int chunk_size, struct LevelInfo *level)
3408 int element = getMappedElement(getFile16BitBE(file));
3409 int real_chunk_size = 2;
3410 struct ElementInfo *ei = &element_info[element];
3412 xx_ei = *ei; // copy element data into temporary buffer
3414 while (!checkEndOfFile(file))
3416 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_EMPX,
3419 if (real_chunk_size >= chunk_size)
3425 level->file_has_custom_elements = TRUE;
3427 return real_chunk_size;
3430 static void LoadLevelFromFileInfo_RND(struct LevelInfo *level,
3431 struct LevelFileInfo *level_file_info,
3432 boolean level_info_only)
3434 char *filename = level_file_info->filename;
3435 char cookie[MAX_LINE_LEN];
3436 char chunk_name[CHUNK_ID_LEN + 1];
3440 if (!(file = openFile(filename, MODE_READ)))
3442 level->no_valid_file = TRUE;
3443 level->no_level_file = TRUE;
3445 if (level_info_only)
3448 Warn("cannot read level '%s' -- using empty level", filename);
3450 if (!setup.editor.use_template_for_new_levels)
3453 // if level file not found, try to initialize level data from template
3454 filename = getGlobalLevelTemplateFilename();
3456 if (!(file = openFile(filename, MODE_READ)))
3459 // default: for empty levels, use level template for custom elements
3460 level->use_custom_template = TRUE;
3462 level->no_valid_file = FALSE;
3465 getFileChunkBE(file, chunk_name, NULL);
3466 if (strEqual(chunk_name, "RND1"))
3468 getFile32BitBE(file); // not used
3470 getFileChunkBE(file, chunk_name, NULL);
3471 if (!strEqual(chunk_name, "CAVE"))
3473 level->no_valid_file = TRUE;
3475 Warn("unknown format of level file '%s'", filename);
3482 else // check for pre-2.0 file format with cookie string
3484 strcpy(cookie, chunk_name);
3485 if (getStringFromFile(file, &cookie[4], MAX_LINE_LEN - 4) == NULL)
3487 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
3488 cookie[strlen(cookie) - 1] = '\0';
3490 if (!checkCookieString(cookie, LEVEL_COOKIE_TMPL))
3492 level->no_valid_file = TRUE;
3494 Warn("unknown format of level file '%s'", filename);
3501 if ((level->file_version = getFileVersionFromCookieString(cookie)) == -1)
3503 level->no_valid_file = TRUE;
3505 Warn("unsupported version of level file '%s'", filename);
3512 // pre-2.0 level files have no game version, so use file version here
3513 level->game_version = level->file_version;
3516 if (level->file_version < FILE_VERSION_1_2)
3518 // level files from versions before 1.2.0 without chunk structure
3519 LoadLevel_HEAD(file, LEVEL_CHUNK_HEAD_SIZE, level);
3520 LoadLevel_BODY(file, level->fieldx * level->fieldy, level);
3528 int (*loader)(File *, int, struct LevelInfo *);
3532 { "VERS", LEVEL_CHUNK_VERS_SIZE, LoadLevel_VERS },
3533 { "DATE", LEVEL_CHUNK_DATE_SIZE, LoadLevel_DATE },
3534 { "HEAD", LEVEL_CHUNK_HEAD_SIZE, LoadLevel_HEAD },
3535 { "NAME", LEVEL_CHUNK_NAME_SIZE, LoadLevel_NAME },
3536 { "AUTH", LEVEL_CHUNK_AUTH_SIZE, LoadLevel_AUTH },
3537 { "INFO", -1, LoadLevel_INFO },
3538 { "BODY", -1, LoadLevel_BODY },
3539 { "CONT", -1, LoadLevel_CONT },
3540 { "CNT2", LEVEL_CHUNK_CNT2_SIZE, LoadLevel_CNT2 },
3541 { "CNT3", -1, LoadLevel_CNT3 },
3542 { "CUS1", -1, LoadLevel_CUS1 },
3543 { "CUS2", -1, LoadLevel_CUS2 },
3544 { "CUS3", -1, LoadLevel_CUS3 },
3545 { "CUS4", -1, LoadLevel_CUS4 },
3546 { "GRP1", -1, LoadLevel_GRP1 },
3547 { "CONF", -1, LoadLevel_CONF },
3548 { "ELEM", -1, LoadLevel_ELEM },
3549 { "NOTE", -1, LoadLevel_NOTE },
3550 { "CUSX", -1, LoadLevel_CUSX },
3551 { "GRPX", -1, LoadLevel_GRPX },
3552 { "EMPX", -1, LoadLevel_EMPX },
3557 while (getFileChunkBE(file, chunk_name, &chunk_size))
3561 while (chunk_info[i].name != NULL &&
3562 !strEqual(chunk_name, chunk_info[i].name))
3565 if (chunk_info[i].name == NULL)
3567 Warn("unknown chunk '%s' in level file '%s'",
3568 chunk_name, filename);
3570 ReadUnusedBytesFromFile(file, chunk_size);
3572 else if (chunk_info[i].size != -1 &&
3573 chunk_info[i].size != chunk_size)
3575 Warn("wrong size (%d) of chunk '%s' in level file '%s'",
3576 chunk_size, chunk_name, filename);
3578 ReadUnusedBytesFromFile(file, chunk_size);
3582 // call function to load this level chunk
3583 int chunk_size_expected =
3584 (chunk_info[i].loader)(file, chunk_size, level);
3586 // the size of some chunks cannot be checked before reading other
3587 // chunks first (like "HEAD" and "BODY") that contain some header
3588 // information, so check them here
3589 if (chunk_size_expected != chunk_size)
3591 Warn("wrong size (%d) of chunk '%s' in level file '%s'",
3592 chunk_size, chunk_name, filename);
3602 // ----------------------------------------------------------------------------
3603 // functions for loading EM level
3604 // ----------------------------------------------------------------------------
3606 static void CopyNativeLevel_RND_to_EM(struct LevelInfo *level)
3608 static int ball_xy[8][2] =
3619 struct LevelInfo_EM *level_em = level->native_em_level;
3620 struct CAVE *cav = level_em->cav;
3623 cav->width = MIN(level->fieldx, MAX_PLAYFIELD_WIDTH);
3624 cav->height = MIN(level->fieldy, MAX_PLAYFIELD_HEIGHT);
3626 cav->time_seconds = level->time;
3627 cav->gems_needed = level->gems_needed;
3629 cav->emerald_score = level->score[SC_EMERALD];
3630 cav->diamond_score = level->score[SC_DIAMOND];
3631 cav->alien_score = level->score[SC_ROBOT];
3632 cav->tank_score = level->score[SC_SPACESHIP];
3633 cav->bug_score = level->score[SC_BUG];
3634 cav->eater_score = level->score[SC_YAMYAM];
3635 cav->nut_score = level->score[SC_NUT];
3636 cav->dynamite_score = level->score[SC_DYNAMITE];
3637 cav->key_score = level->score[SC_KEY];
3638 cav->exit_score = level->score[SC_TIME_BONUS];
3640 cav->num_eater_arrays = level->num_yamyam_contents;
3642 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
3643 for (y = 0; y < 3; y++)
3644 for (x = 0; x < 3; x++)
3645 cav->eater_array[i][y * 3 + x] =
3646 map_element_RND_to_EM_cave(level->yamyam_content[i].e[x][y]);
3648 cav->amoeba_time = level->amoeba_speed;
3649 cav->wonderwall_time = level->time_magic_wall;
3650 cav->wheel_time = level->time_wheel;
3652 cav->android_move_time = level->android_move_time;
3653 cav->android_clone_time = level->android_clone_time;
3654 cav->ball_random = level->ball_random;
3655 cav->ball_active = level->ball_active_initial;
3656 cav->ball_time = level->ball_time;
3657 cav->num_ball_arrays = level->num_ball_contents;
3659 cav->lenses_score = level->lenses_score;
3660 cav->magnify_score = level->magnify_score;
3661 cav->slurp_score = level->slurp_score;
3663 cav->lenses_time = level->lenses_time;
3664 cav->magnify_time = level->magnify_time;
3666 cav->wind_direction =
3667 map_direction_RND_to_EM(level->wind_direction_initial);
3669 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
3670 for (j = 0; j < 8; j++)
3671 cav->ball_array[i][j] =
3672 map_element_RND_to_EM_cave(level->ball_content[i].
3673 e[ball_xy[j][0]][ball_xy[j][1]]);
3675 map_android_clone_elements_RND_to_EM(level);
3677 // first fill the complete playfield with the empty space element
3678 for (y = 0; y < EM_MAX_CAVE_HEIGHT; y++)
3679 for (x = 0; x < EM_MAX_CAVE_WIDTH; x++)
3680 cav->cave[x][y] = Cblank;
3682 // then copy the real level contents from level file into the playfield
3683 for (y = 0; y < cav->height; y++) for (x = 0; x < cav->width; x++)
3685 int new_element = map_element_RND_to_EM_cave(level->field[x][y]);
3687 if (level->field[x][y] == EL_AMOEBA_DEAD)
3688 new_element = map_element_RND_to_EM_cave(EL_AMOEBA_WET);
3690 cav->cave[x][y] = new_element;
3693 for (i = 0; i < MAX_PLAYERS; i++)
3695 cav->player_x[i] = -1;
3696 cav->player_y[i] = -1;
3699 // initialize player positions and delete players from the playfield
3700 for (y = 0; y < cav->height; y++) for (x = 0; x < cav->width; x++)
3702 if (IS_PLAYER_ELEMENT(level->field[x][y]))
3704 int player_nr = GET_PLAYER_NR(level->field[x][y]);
3706 cav->player_x[player_nr] = x;
3707 cav->player_y[player_nr] = y;
3709 cav->cave[x][y] = map_element_RND_to_EM_cave(EL_EMPTY);
3714 static void CopyNativeLevel_EM_to_RND(struct LevelInfo *level)
3716 static int ball_xy[8][2] =
3727 struct LevelInfo_EM *level_em = level->native_em_level;
3728 struct CAVE *cav = level_em->cav;
3731 level->fieldx = MIN(cav->width, MAX_LEV_FIELDX);
3732 level->fieldy = MIN(cav->height, MAX_LEV_FIELDY);
3734 level->time = cav->time_seconds;
3735 level->gems_needed = cav->gems_needed;
3737 sprintf(level->name, "Level %d", level->file_info.nr);
3739 level->score[SC_EMERALD] = cav->emerald_score;
3740 level->score[SC_DIAMOND] = cav->diamond_score;
3741 level->score[SC_ROBOT] = cav->alien_score;
3742 level->score[SC_SPACESHIP] = cav->tank_score;
3743 level->score[SC_BUG] = cav->bug_score;
3744 level->score[SC_YAMYAM] = cav->eater_score;
3745 level->score[SC_NUT] = cav->nut_score;
3746 level->score[SC_DYNAMITE] = cav->dynamite_score;
3747 level->score[SC_KEY] = cav->key_score;
3748 level->score[SC_TIME_BONUS] = cav->exit_score;
3750 level->num_yamyam_contents = cav->num_eater_arrays;
3752 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
3753 for (y = 0; y < 3; y++)
3754 for (x = 0; x < 3; x++)
3755 level->yamyam_content[i].e[x][y] =
3756 map_element_EM_to_RND_cave(cav->eater_array[i][y * 3 + x]);
3758 level->amoeba_speed = cav->amoeba_time;
3759 level->time_magic_wall = cav->wonderwall_time;
3760 level->time_wheel = cav->wheel_time;
3762 level->android_move_time = cav->android_move_time;
3763 level->android_clone_time = cav->android_clone_time;
3764 level->ball_random = cav->ball_random;
3765 level->ball_active_initial = cav->ball_active;
3766 level->ball_time = cav->ball_time;
3767 level->num_ball_contents = cav->num_ball_arrays;
3769 level->lenses_score = cav->lenses_score;
3770 level->magnify_score = cav->magnify_score;
3771 level->slurp_score = cav->slurp_score;
3773 level->lenses_time = cav->lenses_time;
3774 level->magnify_time = cav->magnify_time;
3776 level->wind_direction_initial =
3777 map_direction_EM_to_RND(cav->wind_direction);
3779 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
3780 for (j = 0; j < 8; j++)
3781 level->ball_content[i].e[ball_xy[j][0]][ball_xy[j][1]] =
3782 map_element_EM_to_RND_cave(cav->ball_array[i][j]);
3784 map_android_clone_elements_EM_to_RND(level);
3786 // convert the playfield (some elements need special treatment)
3787 for (y = 0; y < level->fieldy; y++) for (x = 0; x < level->fieldx; x++)
3789 int new_element = map_element_EM_to_RND_cave(cav->cave[x][y]);
3791 if (new_element == EL_AMOEBA_WET && level->amoeba_speed == 0)
3792 new_element = EL_AMOEBA_DEAD;
3794 level->field[x][y] = new_element;
3797 for (i = 0; i < MAX_PLAYERS; i++)
3799 // in case of all players set to the same field, use the first player
3800 int nr = MAX_PLAYERS - i - 1;
3801 int jx = cav->player_x[nr];
3802 int jy = cav->player_y[nr];
3804 if (jx != -1 && jy != -1)
3805 level->field[jx][jy] = EL_PLAYER_1 + nr;
3808 // time score is counted for each 10 seconds left in Emerald Mine levels
3809 level->time_score_base = 10;
3813 // ----------------------------------------------------------------------------
3814 // functions for loading SP level
3815 // ----------------------------------------------------------------------------
3817 static void CopyNativeLevel_RND_to_SP(struct LevelInfo *level)
3819 struct LevelInfo_SP *level_sp = level->native_sp_level;
3820 LevelInfoType *header = &level_sp->header;
3823 level_sp->width = level->fieldx;
3824 level_sp->height = level->fieldy;
3826 for (x = 0; x < level->fieldx; x++)
3827 for (y = 0; y < level->fieldy; y++)
3828 level_sp->playfield[x][y] = map_element_RND_to_SP(level->field[x][y]);
3830 header->InitialGravity = (level->initial_player_gravity[0] ? 1 : 0);
3832 for (i = 0; i < SP_LEVEL_NAME_LEN; i++)
3833 header->LevelTitle[i] = level->name[i];
3834 // !!! NO STRING TERMINATION IN SUPAPLEX VB CODE YET -- FIX THIS !!!
3836 header->InfotronsNeeded = level->gems_needed;
3838 header->SpecialPortCount = 0;
3840 for (x = 0; x < level->fieldx; x++) for (y = 0; y < level->fieldy; y++)
3842 boolean gravity_port_found = FALSE;
3843 boolean gravity_port_valid = FALSE;
3844 int gravity_port_flag;
3845 int gravity_port_base_element;
3846 int element = level->field[x][y];
3848 if (element >= EL_SP_GRAVITY_ON_PORT_RIGHT &&
3849 element <= EL_SP_GRAVITY_ON_PORT_UP)
3851 gravity_port_found = TRUE;
3852 gravity_port_valid = TRUE;
3853 gravity_port_flag = 1;
3854 gravity_port_base_element = EL_SP_GRAVITY_ON_PORT_RIGHT;
3856 else if (element >= EL_SP_GRAVITY_OFF_PORT_RIGHT &&
3857 element <= EL_SP_GRAVITY_OFF_PORT_UP)
3859 gravity_port_found = TRUE;
3860 gravity_port_valid = TRUE;
3861 gravity_port_flag = 0;
3862 gravity_port_base_element = EL_SP_GRAVITY_OFF_PORT_RIGHT;
3864 else if (element >= EL_SP_GRAVITY_PORT_RIGHT &&
3865 element <= EL_SP_GRAVITY_PORT_UP)
3867 // change R'n'D style gravity inverting special port to normal port
3868 // (there are no gravity inverting ports in native Supaplex engine)
3870 gravity_port_found = TRUE;
3871 gravity_port_valid = FALSE;
3872 gravity_port_base_element = EL_SP_GRAVITY_PORT_RIGHT;
3875 if (gravity_port_found)
3877 if (gravity_port_valid &&
3878 header->SpecialPortCount < SP_MAX_SPECIAL_PORTS)
3880 SpecialPortType *port = &header->SpecialPort[header->SpecialPortCount];
3882 port->PortLocation = (y * level->fieldx + x) * 2;
3883 port->Gravity = gravity_port_flag;
3885 element += EL_SP_GRAVITY_PORT_RIGHT - gravity_port_base_element;
3887 header->SpecialPortCount++;
3891 // change special gravity port to normal port
3893 element += EL_SP_PORT_RIGHT - gravity_port_base_element;
3896 level_sp->playfield[x][y] = element - EL_SP_START;
3901 static void CopyNativeLevel_SP_to_RND(struct LevelInfo *level)
3903 struct LevelInfo_SP *level_sp = level->native_sp_level;
3904 LevelInfoType *header = &level_sp->header;
3905 boolean num_invalid_elements = 0;
3908 level->fieldx = level_sp->width;
3909 level->fieldy = level_sp->height;
3911 for (x = 0; x < level->fieldx; x++)
3913 for (y = 0; y < level->fieldy; y++)
3915 int element_old = level_sp->playfield[x][y];
3916 int element_new = getMappedElement(map_element_SP_to_RND(element_old));
3918 if (element_new == EL_UNKNOWN)
3920 num_invalid_elements++;
3922 Debug("level:native:SP", "invalid element %d at position %d, %d",
3926 level->field[x][y] = element_new;
3930 if (num_invalid_elements > 0)
3931 Warn("found %d invalid elements%s", num_invalid_elements,
3932 (!options.debug ? " (use '--debug' for more details)" : ""));
3934 for (i = 0; i < MAX_PLAYERS; i++)
3935 level->initial_player_gravity[i] =
3936 (header->InitialGravity == 1 ? TRUE : FALSE);
3938 // skip leading spaces
3939 for (i = 0; i < SP_LEVEL_NAME_LEN; i++)
3940 if (header->LevelTitle[i] != ' ')
3944 for (j = 0; i < SP_LEVEL_NAME_LEN; i++, j++)
3945 level->name[j] = header->LevelTitle[i];
3946 level->name[j] = '\0';
3948 // cut trailing spaces
3950 if (level->name[j - 1] == ' ' && level->name[j] == '\0')
3951 level->name[j - 1] = '\0';
3953 level->gems_needed = header->InfotronsNeeded;
3955 for (i = 0; i < header->SpecialPortCount; i++)
3957 SpecialPortType *port = &header->SpecialPort[i];
3958 int port_location = port->PortLocation;
3959 int gravity = port->Gravity;
3960 int port_x, port_y, port_element;
3962 port_x = (port_location / 2) % level->fieldx;
3963 port_y = (port_location / 2) / level->fieldx;
3965 if (port_x < 0 || port_x >= level->fieldx ||
3966 port_y < 0 || port_y >= level->fieldy)
3968 Warn("special port position (%d, %d) out of bounds", port_x, port_y);
3973 port_element = level->field[port_x][port_y];
3975 if (port_element < EL_SP_GRAVITY_PORT_RIGHT ||
3976 port_element > EL_SP_GRAVITY_PORT_UP)
3978 Warn("no special port at position (%d, %d)", port_x, port_y);
3983 // change previous (wrong) gravity inverting special port to either
3984 // gravity enabling special port or gravity disabling special port
3985 level->field[port_x][port_y] +=
3986 (gravity == 1 ? EL_SP_GRAVITY_ON_PORT_RIGHT :
3987 EL_SP_GRAVITY_OFF_PORT_RIGHT) - EL_SP_GRAVITY_PORT_RIGHT;
3990 // change special gravity ports without database entries to normal ports
3991 for (x = 0; x < level->fieldx; x++)
3992 for (y = 0; y < level->fieldy; y++)
3993 if (level->field[x][y] >= EL_SP_GRAVITY_PORT_RIGHT &&
3994 level->field[x][y] <= EL_SP_GRAVITY_PORT_UP)
3995 level->field[x][y] += EL_SP_PORT_RIGHT - EL_SP_GRAVITY_PORT_RIGHT;
3997 level->time = 0; // no time limit
3998 level->amoeba_speed = 0;
3999 level->time_magic_wall = 0;
4000 level->time_wheel = 0;
4001 level->amoeba_content = EL_EMPTY;
4003 // original Supaplex does not use score values -- rate by playing time
4004 for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
4005 level->score[i] = 0;
4007 level->rate_time_over_score = TRUE;
4009 // there are no yamyams in supaplex levels
4010 for (i = 0; i < level->num_yamyam_contents; i++)
4011 for (x = 0; x < 3; x++)
4012 for (y = 0; y < 3; y++)
4013 level->yamyam_content[i].e[x][y] = EL_EMPTY;
4016 static void CopyNativeTape_RND_to_SP(struct LevelInfo *level)
4018 struct LevelInfo_SP *level_sp = level->native_sp_level;
4019 struct DemoInfo_SP *demo = &level_sp->demo;
4022 // always start with reliable default values
4023 demo->is_available = FALSE;
4026 if (TAPE_IS_EMPTY(tape))
4029 demo->level_nr = tape.level_nr; // (currently not used)
4031 level_sp->header.DemoRandomSeed = tape.random_seed;
4035 for (i = 0; i < tape.length; i++)
4037 int demo_action = map_key_RND_to_SP(tape.pos[i].action[0]);
4038 int demo_repeat = tape.pos[i].delay;
4039 int demo_entries = (demo_repeat + 15) / 16;
4041 if (demo->length + demo_entries >= SP_MAX_TAPE_LEN)
4043 Warn("tape truncated: size exceeds maximum SP demo size %d",
4049 for (j = 0; j < demo_repeat / 16; j++)
4050 demo->data[demo->length++] = 0xf0 | demo_action;
4052 if (demo_repeat % 16)
4053 demo->data[demo->length++] = ((demo_repeat % 16 - 1) << 4) | demo_action;
4056 demo->is_available = TRUE;
4059 static void setTapeInfoToDefaults(void);
4061 static void CopyNativeTape_SP_to_RND(struct LevelInfo *level)
4063 struct LevelInfo_SP *level_sp = level->native_sp_level;
4064 struct DemoInfo_SP *demo = &level_sp->demo;
4065 char *filename = level->file_info.filename;
4068 // always start with reliable default values
4069 setTapeInfoToDefaults();
4071 if (!demo->is_available)
4074 tape.level_nr = demo->level_nr; // (currently not used)
4075 tape.random_seed = level_sp->header.DemoRandomSeed;
4077 TapeSetDateFromEpochSeconds(getFileTimestampEpochSeconds(filename));
4080 tape.pos[tape.counter].delay = 0;
4082 for (i = 0; i < demo->length; i++)
4084 int demo_action = demo->data[i] & 0x0f;
4085 int demo_repeat = (demo->data[i] & 0xf0) >> 4;
4086 int tape_action = map_key_SP_to_RND(demo_action);
4087 int tape_repeat = demo_repeat + 1;
4088 byte action[MAX_TAPE_ACTIONS] = { tape_action };
4089 boolean success = 0;
4092 for (j = 0; j < tape_repeat; j++)
4093 success = TapeAddAction(action);
4097 Warn("SP demo truncated: size exceeds maximum tape size %d",
4104 TapeHaltRecording();
4108 // ----------------------------------------------------------------------------
4109 // functions for loading MM level
4110 // ----------------------------------------------------------------------------
4112 static void CopyNativeLevel_RND_to_MM(struct LevelInfo *level)
4114 struct LevelInfo_MM *level_mm = level->native_mm_level;
4117 level_mm->fieldx = MIN(level->fieldx, MM_MAX_PLAYFIELD_WIDTH);
4118 level_mm->fieldy = MIN(level->fieldy, MM_MAX_PLAYFIELD_HEIGHT);
4120 level_mm->time = level->time;
4121 level_mm->kettles_needed = level->gems_needed;
4122 level_mm->auto_count_kettles = level->auto_count_gems;
4124 level_mm->laser_red = level->mm_laser_red;
4125 level_mm->laser_green = level->mm_laser_green;
4126 level_mm->laser_blue = level->mm_laser_blue;
4128 strcpy(level_mm->name, level->name);
4129 strcpy(level_mm->author, level->author);
4131 level_mm->score[SC_EMERALD] = level->score[SC_EMERALD];
4132 level_mm->score[SC_PACMAN] = level->score[SC_PACMAN];
4133 level_mm->score[SC_KEY] = level->score[SC_KEY];
4134 level_mm->score[SC_TIME_BONUS] = level->score[SC_TIME_BONUS];
4135 level_mm->score[SC_ELEM_BONUS] = level->score[SC_ELEM_BONUS];
4137 level_mm->amoeba_speed = level->amoeba_speed;
4138 level_mm->time_fuse = level->mm_time_fuse;
4139 level_mm->time_bomb = level->mm_time_bomb;
4140 level_mm->time_ball = level->mm_time_ball;
4141 level_mm->time_block = level->mm_time_block;
4143 for (x = 0; x < level->fieldx; x++)
4144 for (y = 0; y < level->fieldy; y++)
4146 level_mm->field[x][y] = map_element_RND_to_MM(level->field[x][y]);
4149 static void CopyNativeLevel_MM_to_RND(struct LevelInfo *level)
4151 struct LevelInfo_MM *level_mm = level->native_mm_level;
4154 level->fieldx = MIN(level_mm->fieldx, MAX_LEV_FIELDX);
4155 level->fieldy = MIN(level_mm->fieldy, MAX_LEV_FIELDY);
4157 level->time = level_mm->time;
4158 level->gems_needed = level_mm->kettles_needed;
4159 level->auto_count_gems = level_mm->auto_count_kettles;
4161 level->mm_laser_red = level_mm->laser_red;
4162 level->mm_laser_green = level_mm->laser_green;
4163 level->mm_laser_blue = level_mm->laser_blue;
4165 strcpy(level->name, level_mm->name);
4167 // only overwrite author from 'levelinfo.conf' if author defined in level
4168 if (!strEqual(level_mm->author, ANONYMOUS_NAME))
4169 strcpy(level->author, level_mm->author);
4171 level->score[SC_EMERALD] = level_mm->score[SC_EMERALD];
4172 level->score[SC_PACMAN] = level_mm->score[SC_PACMAN];
4173 level->score[SC_KEY] = level_mm->score[SC_KEY];
4174 level->score[SC_TIME_BONUS] = level_mm->score[SC_TIME_BONUS];
4175 level->score[SC_ELEM_BONUS] = level_mm->score[SC_ELEM_BONUS];
4177 level->amoeba_speed = level_mm->amoeba_speed;
4178 level->mm_time_fuse = level_mm->time_fuse;
4179 level->mm_time_bomb = level_mm->time_bomb;
4180 level->mm_time_ball = level_mm->time_ball;
4181 level->mm_time_block = level_mm->time_block;
4183 for (x = 0; x < level->fieldx; x++)
4184 for (y = 0; y < level->fieldy; y++)
4185 level->field[x][y] = map_element_MM_to_RND(level_mm->field[x][y]);
4189 // ----------------------------------------------------------------------------
4190 // functions for loading DC level
4191 // ----------------------------------------------------------------------------
4193 #define DC_LEVEL_HEADER_SIZE 344
4195 static unsigned short getDecodedWord_DC(unsigned short data_encoded,
4198 static int last_data_encoded;
4202 int diff_hi, diff_lo;
4203 int data_hi, data_lo;
4204 unsigned short data_decoded;
4208 last_data_encoded = 0;
4215 diff = data_encoded - last_data_encoded;
4216 diff_hi = diff & ~0xff;
4217 diff_lo = diff & 0xff;
4221 data_hi = diff_hi - (offset1 << 8) + (offset2 & 0xff00);
4222 data_lo = (diff_lo + (data_hi >> 16)) & 0x00ff;
4223 data_hi = data_hi & 0xff00;
4225 data_decoded = data_hi | data_lo;
4227 last_data_encoded = data_encoded;
4229 offset1 = (offset1 + 1) % 31;
4230 offset2 = offset2 & 0xff;
4232 return data_decoded;
4235 static int getMappedElement_DC(int element)
4243 // 0x0117 - 0x036e: (?)
4246 // 0x042d - 0x0684: (?)
4262 element = EL_CRYSTAL;
4265 case 0x0e77: // quicksand (boulder)
4266 element = EL_QUICKSAND_FAST_FULL;
4269 case 0x0e99: // slow quicksand (boulder)
4270 element = EL_QUICKSAND_FULL;
4274 element = EL_EM_EXIT_OPEN;
4278 element = EL_EM_EXIT_CLOSED;
4282 element = EL_EM_STEEL_EXIT_OPEN;
4286 element = EL_EM_STEEL_EXIT_CLOSED;
4289 case 0x0f4f: // dynamite (lit 1)
4290 element = EL_EM_DYNAMITE_ACTIVE;
4293 case 0x0f57: // dynamite (lit 2)
4294 element = EL_EM_DYNAMITE_ACTIVE;
4297 case 0x0f5f: // dynamite (lit 3)
4298 element = EL_EM_DYNAMITE_ACTIVE;
4301 case 0x0f67: // dynamite (lit 4)
4302 element = EL_EM_DYNAMITE_ACTIVE;
4309 element = EL_AMOEBA_WET;
4313 element = EL_AMOEBA_DROP;
4317 element = EL_DC_MAGIC_WALL;
4321 element = EL_SPACESHIP_UP;
4325 element = EL_SPACESHIP_DOWN;
4329 element = EL_SPACESHIP_LEFT;
4333 element = EL_SPACESHIP_RIGHT;
4337 element = EL_BUG_UP;
4341 element = EL_BUG_DOWN;
4345 element = EL_BUG_LEFT;
4349 element = EL_BUG_RIGHT;
4353 element = EL_MOLE_UP;
4357 element = EL_MOLE_DOWN;
4361 element = EL_MOLE_LEFT;
4365 element = EL_MOLE_RIGHT;
4373 element = EL_YAMYAM_UP;
4377 element = EL_SWITCHGATE_OPEN;
4381 element = EL_SWITCHGATE_CLOSED;
4385 element = EL_DC_SWITCHGATE_SWITCH_UP;
4389 element = EL_TIMEGATE_CLOSED;
4392 case 0x144c: // conveyor belt switch (green)
4393 element = EL_CONVEYOR_BELT_3_SWITCH_MIDDLE;
4396 case 0x144f: // conveyor belt switch (red)
4397 element = EL_CONVEYOR_BELT_1_SWITCH_MIDDLE;
4400 case 0x1452: // conveyor belt switch (blue)
4401 element = EL_CONVEYOR_BELT_4_SWITCH_MIDDLE;
4405 element = EL_CONVEYOR_BELT_3_MIDDLE;
4409 element = EL_CONVEYOR_BELT_3_LEFT;
4413 element = EL_CONVEYOR_BELT_3_RIGHT;
4417 element = EL_CONVEYOR_BELT_1_MIDDLE;
4421 element = EL_CONVEYOR_BELT_1_LEFT;
4425 element = EL_CONVEYOR_BELT_1_RIGHT;
4429 element = EL_CONVEYOR_BELT_4_MIDDLE;
4433 element = EL_CONVEYOR_BELT_4_LEFT;
4437 element = EL_CONVEYOR_BELT_4_RIGHT;
4441 element = EL_EXPANDABLE_WALL_HORIZONTAL;
4445 element = EL_EXPANDABLE_WALL_VERTICAL;
4449 element = EL_EXPANDABLE_WALL_ANY;
4452 case 0x14ce: // growing steel wall (left/right)
4453 element = EL_EXPANDABLE_STEELWALL_HORIZONTAL;
4456 case 0x14df: // growing steel wall (up/down)
4457 element = EL_EXPANDABLE_STEELWALL_VERTICAL;
4460 case 0x14e8: // growing steel wall (up/down/left/right)
4461 element = EL_EXPANDABLE_STEELWALL_ANY;
4465 element = EL_SHIELD_DEADLY;
4469 element = EL_EXTRA_TIME;
4477 element = EL_EMPTY_SPACE;
4480 case 0x1578: // quicksand (empty)
4481 element = EL_QUICKSAND_FAST_EMPTY;
4484 case 0x1579: // slow quicksand (empty)
4485 element = EL_QUICKSAND_EMPTY;
4495 element = EL_EM_DYNAMITE;
4498 case 0x15a1: // key (red)
4499 element = EL_EM_KEY_1;
4502 case 0x15a2: // key (yellow)
4503 element = EL_EM_KEY_2;
4506 case 0x15a3: // key (blue)
4507 element = EL_EM_KEY_4;
4510 case 0x15a4: // key (green)
4511 element = EL_EM_KEY_3;
4514 case 0x15a5: // key (white)
4515 element = EL_DC_KEY_WHITE;
4519 element = EL_WALL_SLIPPERY;
4526 case 0x15a8: // wall (not round)
4530 case 0x15a9: // (blue)
4531 element = EL_CHAR_A;
4534 case 0x15aa: // (blue)
4535 element = EL_CHAR_B;
4538 case 0x15ab: // (blue)
4539 element = EL_CHAR_C;
4542 case 0x15ac: // (blue)
4543 element = EL_CHAR_D;
4546 case 0x15ad: // (blue)
4547 element = EL_CHAR_E;
4550 case 0x15ae: // (blue)
4551 element = EL_CHAR_F;
4554 case 0x15af: // (blue)
4555 element = EL_CHAR_G;
4558 case 0x15b0: // (blue)
4559 element = EL_CHAR_H;
4562 case 0x15b1: // (blue)
4563 element = EL_CHAR_I;
4566 case 0x15b2: // (blue)
4567 element = EL_CHAR_J;
4570 case 0x15b3: // (blue)
4571 element = EL_CHAR_K;
4574 case 0x15b4: // (blue)
4575 element = EL_CHAR_L;
4578 case 0x15b5: // (blue)
4579 element = EL_CHAR_M;
4582 case 0x15b6: // (blue)
4583 element = EL_CHAR_N;
4586 case 0x15b7: // (blue)
4587 element = EL_CHAR_O;
4590 case 0x15b8: // (blue)
4591 element = EL_CHAR_P;
4594 case 0x15b9: // (blue)
4595 element = EL_CHAR_Q;
4598 case 0x15ba: // (blue)
4599 element = EL_CHAR_R;
4602 case 0x15bb: // (blue)
4603 element = EL_CHAR_S;
4606 case 0x15bc: // (blue)
4607 element = EL_CHAR_T;
4610 case 0x15bd: // (blue)
4611 element = EL_CHAR_U;
4614 case 0x15be: // (blue)
4615 element = EL_CHAR_V;
4618 case 0x15bf: // (blue)
4619 element = EL_CHAR_W;
4622 case 0x15c0: // (blue)
4623 element = EL_CHAR_X;
4626 case 0x15c1: // (blue)
4627 element = EL_CHAR_Y;
4630 case 0x15c2: // (blue)
4631 element = EL_CHAR_Z;
4634 case 0x15c3: // (blue)
4635 element = EL_CHAR_AUMLAUT;
4638 case 0x15c4: // (blue)
4639 element = EL_CHAR_OUMLAUT;
4642 case 0x15c5: // (blue)
4643 element = EL_CHAR_UUMLAUT;
4646 case 0x15c6: // (blue)
4647 element = EL_CHAR_0;
4650 case 0x15c7: // (blue)
4651 element = EL_CHAR_1;
4654 case 0x15c8: // (blue)
4655 element = EL_CHAR_2;
4658 case 0x15c9: // (blue)
4659 element = EL_CHAR_3;
4662 case 0x15ca: // (blue)
4663 element = EL_CHAR_4;
4666 case 0x15cb: // (blue)
4667 element = EL_CHAR_5;
4670 case 0x15cc: // (blue)
4671 element = EL_CHAR_6;
4674 case 0x15cd: // (blue)
4675 element = EL_CHAR_7;
4678 case 0x15ce: // (blue)
4679 element = EL_CHAR_8;
4682 case 0x15cf: // (blue)
4683 element = EL_CHAR_9;
4686 case 0x15d0: // (blue)
4687 element = EL_CHAR_PERIOD;
4690 case 0x15d1: // (blue)
4691 element = EL_CHAR_EXCLAM;
4694 case 0x15d2: // (blue)
4695 element = EL_CHAR_COLON;
4698 case 0x15d3: // (blue)
4699 element = EL_CHAR_LESS;
4702 case 0x15d4: // (blue)
4703 element = EL_CHAR_GREATER;
4706 case 0x15d5: // (blue)
4707 element = EL_CHAR_QUESTION;
4710 case 0x15d6: // (blue)
4711 element = EL_CHAR_COPYRIGHT;
4714 case 0x15d7: // (blue)
4715 element = EL_CHAR_UP;
4718 case 0x15d8: // (blue)
4719 element = EL_CHAR_DOWN;
4722 case 0x15d9: // (blue)
4723 element = EL_CHAR_BUTTON;
4726 case 0x15da: // (blue)
4727 element = EL_CHAR_PLUS;
4730 case 0x15db: // (blue)
4731 element = EL_CHAR_MINUS;
4734 case 0x15dc: // (blue)
4735 element = EL_CHAR_APOSTROPHE;
4738 case 0x15dd: // (blue)
4739 element = EL_CHAR_PARENLEFT;
4742 case 0x15de: // (blue)
4743 element = EL_CHAR_PARENRIGHT;
4746 case 0x15df: // (green)
4747 element = EL_CHAR_A;
4750 case 0x15e0: // (green)
4751 element = EL_CHAR_B;
4754 case 0x15e1: // (green)
4755 element = EL_CHAR_C;
4758 case 0x15e2: // (green)
4759 element = EL_CHAR_D;
4762 case 0x15e3: // (green)
4763 element = EL_CHAR_E;
4766 case 0x15e4: // (green)
4767 element = EL_CHAR_F;
4770 case 0x15e5: // (green)
4771 element = EL_CHAR_G;
4774 case 0x15e6: // (green)
4775 element = EL_CHAR_H;
4778 case 0x15e7: // (green)
4779 element = EL_CHAR_I;
4782 case 0x15e8: // (green)
4783 element = EL_CHAR_J;
4786 case 0x15e9: // (green)
4787 element = EL_CHAR_K;
4790 case 0x15ea: // (green)
4791 element = EL_CHAR_L;
4794 case 0x15eb: // (green)
4795 element = EL_CHAR_M;
4798 case 0x15ec: // (green)
4799 element = EL_CHAR_N;
4802 case 0x15ed: // (green)
4803 element = EL_CHAR_O;
4806 case 0x15ee: // (green)
4807 element = EL_CHAR_P;
4810 case 0x15ef: // (green)
4811 element = EL_CHAR_Q;
4814 case 0x15f0: // (green)
4815 element = EL_CHAR_R;
4818 case 0x15f1: // (green)
4819 element = EL_CHAR_S;
4822 case 0x15f2: // (green)
4823 element = EL_CHAR_T;
4826 case 0x15f3: // (green)
4827 element = EL_CHAR_U;
4830 case 0x15f4: // (green)
4831 element = EL_CHAR_V;
4834 case 0x15f5: // (green)
4835 element = EL_CHAR_W;
4838 case 0x15f6: // (green)
4839 element = EL_CHAR_X;
4842 case 0x15f7: // (green)
4843 element = EL_CHAR_Y;
4846 case 0x15f8: // (green)
4847 element = EL_CHAR_Z;
4850 case 0x15f9: // (green)
4851 element = EL_CHAR_AUMLAUT;
4854 case 0x15fa: // (green)
4855 element = EL_CHAR_OUMLAUT;
4858 case 0x15fb: // (green)
4859 element = EL_CHAR_UUMLAUT;
4862 case 0x15fc: // (green)
4863 element = EL_CHAR_0;
4866 case 0x15fd: // (green)
4867 element = EL_CHAR_1;
4870 case 0x15fe: // (green)
4871 element = EL_CHAR_2;
4874 case 0x15ff: // (green)
4875 element = EL_CHAR_3;
4878 case 0x1600: // (green)
4879 element = EL_CHAR_4;
4882 case 0x1601: // (green)
4883 element = EL_CHAR_5;
4886 case 0x1602: // (green)
4887 element = EL_CHAR_6;
4890 case 0x1603: // (green)
4891 element = EL_CHAR_7;
4894 case 0x1604: // (green)
4895 element = EL_CHAR_8;
4898 case 0x1605: // (green)
4899 element = EL_CHAR_9;
4902 case 0x1606: // (green)
4903 element = EL_CHAR_PERIOD;
4906 case 0x1607: // (green)
4907 element = EL_CHAR_EXCLAM;
4910 case 0x1608: // (green)
4911 element = EL_CHAR_COLON;
4914 case 0x1609: // (green)
4915 element = EL_CHAR_LESS;
4918 case 0x160a: // (green)
4919 element = EL_CHAR_GREATER;
4922 case 0x160b: // (green)
4923 element = EL_CHAR_QUESTION;
4926 case 0x160c: // (green)
4927 element = EL_CHAR_COPYRIGHT;
4930 case 0x160d: // (green)
4931 element = EL_CHAR_UP;
4934 case 0x160e: // (green)
4935 element = EL_CHAR_DOWN;
4938 case 0x160f: // (green)
4939 element = EL_CHAR_BUTTON;
4942 case 0x1610: // (green)
4943 element = EL_CHAR_PLUS;
4946 case 0x1611: // (green)
4947 element = EL_CHAR_MINUS;
4950 case 0x1612: // (green)
4951 element = EL_CHAR_APOSTROPHE;
4954 case 0x1613: // (green)
4955 element = EL_CHAR_PARENLEFT;
4958 case 0x1614: // (green)
4959 element = EL_CHAR_PARENRIGHT;
4962 case 0x1615: // (blue steel)
4963 element = EL_STEEL_CHAR_A;
4966 case 0x1616: // (blue steel)
4967 element = EL_STEEL_CHAR_B;
4970 case 0x1617: // (blue steel)
4971 element = EL_STEEL_CHAR_C;
4974 case 0x1618: // (blue steel)
4975 element = EL_STEEL_CHAR_D;
4978 case 0x1619: // (blue steel)
4979 element = EL_STEEL_CHAR_E;
4982 case 0x161a: // (blue steel)
4983 element = EL_STEEL_CHAR_F;
4986 case 0x161b: // (blue steel)
4987 element = EL_STEEL_CHAR_G;
4990 case 0x161c: // (blue steel)
4991 element = EL_STEEL_CHAR_H;
4994 case 0x161d: // (blue steel)
4995 element = EL_STEEL_CHAR_I;
4998 case 0x161e: // (blue steel)
4999 element = EL_STEEL_CHAR_J;
5002 case 0x161f: // (blue steel)
5003 element = EL_STEEL_CHAR_K;
5006 case 0x1620: // (blue steel)
5007 element = EL_STEEL_CHAR_L;
5010 case 0x1621: // (blue steel)
5011 element = EL_STEEL_CHAR_M;
5014 case 0x1622: // (blue steel)
5015 element = EL_STEEL_CHAR_N;
5018 case 0x1623: // (blue steel)
5019 element = EL_STEEL_CHAR_O;
5022 case 0x1624: // (blue steel)
5023 element = EL_STEEL_CHAR_P;
5026 case 0x1625: // (blue steel)
5027 element = EL_STEEL_CHAR_Q;
5030 case 0x1626: // (blue steel)
5031 element = EL_STEEL_CHAR_R;
5034 case 0x1627: // (blue steel)
5035 element = EL_STEEL_CHAR_S;
5038 case 0x1628: // (blue steel)
5039 element = EL_STEEL_CHAR_T;
5042 case 0x1629: // (blue steel)
5043 element = EL_STEEL_CHAR_U;
5046 case 0x162a: // (blue steel)
5047 element = EL_STEEL_CHAR_V;
5050 case 0x162b: // (blue steel)
5051 element = EL_STEEL_CHAR_W;
5054 case 0x162c: // (blue steel)
5055 element = EL_STEEL_CHAR_X;
5058 case 0x162d: // (blue steel)
5059 element = EL_STEEL_CHAR_Y;
5062 case 0x162e: // (blue steel)
5063 element = EL_STEEL_CHAR_Z;
5066 case 0x162f: // (blue steel)
5067 element = EL_STEEL_CHAR_AUMLAUT;
5070 case 0x1630: // (blue steel)
5071 element = EL_STEEL_CHAR_OUMLAUT;
5074 case 0x1631: // (blue steel)
5075 element = EL_STEEL_CHAR_UUMLAUT;
5078 case 0x1632: // (blue steel)
5079 element = EL_STEEL_CHAR_0;
5082 case 0x1633: // (blue steel)
5083 element = EL_STEEL_CHAR_1;
5086 case 0x1634: // (blue steel)
5087 element = EL_STEEL_CHAR_2;
5090 case 0x1635: // (blue steel)
5091 element = EL_STEEL_CHAR_3;
5094 case 0x1636: // (blue steel)
5095 element = EL_STEEL_CHAR_4;
5098 case 0x1637: // (blue steel)
5099 element = EL_STEEL_CHAR_5;
5102 case 0x1638: // (blue steel)
5103 element = EL_STEEL_CHAR_6;
5106 case 0x1639: // (blue steel)
5107 element = EL_STEEL_CHAR_7;
5110 case 0x163a: // (blue steel)
5111 element = EL_STEEL_CHAR_8;
5114 case 0x163b: // (blue steel)
5115 element = EL_STEEL_CHAR_9;
5118 case 0x163c: // (blue steel)
5119 element = EL_STEEL_CHAR_PERIOD;
5122 case 0x163d: // (blue steel)
5123 element = EL_STEEL_CHAR_EXCLAM;
5126 case 0x163e: // (blue steel)
5127 element = EL_STEEL_CHAR_COLON;
5130 case 0x163f: // (blue steel)
5131 element = EL_STEEL_CHAR_LESS;
5134 case 0x1640: // (blue steel)
5135 element = EL_STEEL_CHAR_GREATER;
5138 case 0x1641: // (blue steel)
5139 element = EL_STEEL_CHAR_QUESTION;
5142 case 0x1642: // (blue steel)
5143 element = EL_STEEL_CHAR_COPYRIGHT;
5146 case 0x1643: // (blue steel)
5147 element = EL_STEEL_CHAR_UP;
5150 case 0x1644: // (blue steel)
5151 element = EL_STEEL_CHAR_DOWN;
5154 case 0x1645: // (blue steel)
5155 element = EL_STEEL_CHAR_BUTTON;
5158 case 0x1646: // (blue steel)
5159 element = EL_STEEL_CHAR_PLUS;
5162 case 0x1647: // (blue steel)
5163 element = EL_STEEL_CHAR_MINUS;
5166 case 0x1648: // (blue steel)
5167 element = EL_STEEL_CHAR_APOSTROPHE;
5170 case 0x1649: // (blue steel)
5171 element = EL_STEEL_CHAR_PARENLEFT;
5174 case 0x164a: // (blue steel)
5175 element = EL_STEEL_CHAR_PARENRIGHT;
5178 case 0x164b: // (green steel)
5179 element = EL_STEEL_CHAR_A;
5182 case 0x164c: // (green steel)
5183 element = EL_STEEL_CHAR_B;
5186 case 0x164d: // (green steel)
5187 element = EL_STEEL_CHAR_C;
5190 case 0x164e: // (green steel)
5191 element = EL_STEEL_CHAR_D;
5194 case 0x164f: // (green steel)
5195 element = EL_STEEL_CHAR_E;
5198 case 0x1650: // (green steel)
5199 element = EL_STEEL_CHAR_F;
5202 case 0x1651: // (green steel)
5203 element = EL_STEEL_CHAR_G;
5206 case 0x1652: // (green steel)
5207 element = EL_STEEL_CHAR_H;
5210 case 0x1653: // (green steel)
5211 element = EL_STEEL_CHAR_I;
5214 case 0x1654: // (green steel)
5215 element = EL_STEEL_CHAR_J;
5218 case 0x1655: // (green steel)
5219 element = EL_STEEL_CHAR_K;
5222 case 0x1656: // (green steel)
5223 element = EL_STEEL_CHAR_L;
5226 case 0x1657: // (green steel)
5227 element = EL_STEEL_CHAR_M;
5230 case 0x1658: // (green steel)
5231 element = EL_STEEL_CHAR_N;
5234 case 0x1659: // (green steel)
5235 element = EL_STEEL_CHAR_O;
5238 case 0x165a: // (green steel)
5239 element = EL_STEEL_CHAR_P;
5242 case 0x165b: // (green steel)
5243 element = EL_STEEL_CHAR_Q;
5246 case 0x165c: // (green steel)
5247 element = EL_STEEL_CHAR_R;
5250 case 0x165d: // (green steel)
5251 element = EL_STEEL_CHAR_S;
5254 case 0x165e: // (green steel)
5255 element = EL_STEEL_CHAR_T;
5258 case 0x165f: // (green steel)
5259 element = EL_STEEL_CHAR_U;
5262 case 0x1660: // (green steel)
5263 element = EL_STEEL_CHAR_V;
5266 case 0x1661: // (green steel)
5267 element = EL_STEEL_CHAR_W;
5270 case 0x1662: // (green steel)
5271 element = EL_STEEL_CHAR_X;
5274 case 0x1663: // (green steel)
5275 element = EL_STEEL_CHAR_Y;
5278 case 0x1664: // (green steel)
5279 element = EL_STEEL_CHAR_Z;
5282 case 0x1665: // (green steel)
5283 element = EL_STEEL_CHAR_AUMLAUT;
5286 case 0x1666: // (green steel)
5287 element = EL_STEEL_CHAR_OUMLAUT;
5290 case 0x1667: // (green steel)
5291 element = EL_STEEL_CHAR_UUMLAUT;
5294 case 0x1668: // (green steel)
5295 element = EL_STEEL_CHAR_0;
5298 case 0x1669: // (green steel)
5299 element = EL_STEEL_CHAR_1;
5302 case 0x166a: // (green steel)
5303 element = EL_STEEL_CHAR_2;
5306 case 0x166b: // (green steel)
5307 element = EL_STEEL_CHAR_3;
5310 case 0x166c: // (green steel)
5311 element = EL_STEEL_CHAR_4;
5314 case 0x166d: // (green steel)
5315 element = EL_STEEL_CHAR_5;
5318 case 0x166e: // (green steel)
5319 element = EL_STEEL_CHAR_6;
5322 case 0x166f: // (green steel)
5323 element = EL_STEEL_CHAR_7;
5326 case 0x1670: // (green steel)
5327 element = EL_STEEL_CHAR_8;
5330 case 0x1671: // (green steel)
5331 element = EL_STEEL_CHAR_9;
5334 case 0x1672: // (green steel)
5335 element = EL_STEEL_CHAR_PERIOD;
5338 case 0x1673: // (green steel)
5339 element = EL_STEEL_CHAR_EXCLAM;
5342 case 0x1674: // (green steel)
5343 element = EL_STEEL_CHAR_COLON;
5346 case 0x1675: // (green steel)
5347 element = EL_STEEL_CHAR_LESS;
5350 case 0x1676: // (green steel)
5351 element = EL_STEEL_CHAR_GREATER;
5354 case 0x1677: // (green steel)
5355 element = EL_STEEL_CHAR_QUESTION;
5358 case 0x1678: // (green steel)
5359 element = EL_STEEL_CHAR_COPYRIGHT;
5362 case 0x1679: // (green steel)
5363 element = EL_STEEL_CHAR_UP;
5366 case 0x167a: // (green steel)
5367 element = EL_STEEL_CHAR_DOWN;
5370 case 0x167b: // (green steel)
5371 element = EL_STEEL_CHAR_BUTTON;
5374 case 0x167c: // (green steel)
5375 element = EL_STEEL_CHAR_PLUS;
5378 case 0x167d: // (green steel)
5379 element = EL_STEEL_CHAR_MINUS;
5382 case 0x167e: // (green steel)
5383 element = EL_STEEL_CHAR_APOSTROPHE;
5386 case 0x167f: // (green steel)
5387 element = EL_STEEL_CHAR_PARENLEFT;
5390 case 0x1680: // (green steel)
5391 element = EL_STEEL_CHAR_PARENRIGHT;
5394 case 0x1681: // gate (red)
5395 element = EL_EM_GATE_1;
5398 case 0x1682: // secret gate (red)
5399 element = EL_EM_GATE_1_GRAY;
5402 case 0x1683: // gate (yellow)
5403 element = EL_EM_GATE_2;
5406 case 0x1684: // secret gate (yellow)
5407 element = EL_EM_GATE_2_GRAY;
5410 case 0x1685: // gate (blue)
5411 element = EL_EM_GATE_4;
5414 case 0x1686: // secret gate (blue)
5415 element = EL_EM_GATE_4_GRAY;
5418 case 0x1687: // gate (green)
5419 element = EL_EM_GATE_3;
5422 case 0x1688: // secret gate (green)
5423 element = EL_EM_GATE_3_GRAY;
5426 case 0x1689: // gate (white)
5427 element = EL_DC_GATE_WHITE;
5430 case 0x168a: // secret gate (white)
5431 element = EL_DC_GATE_WHITE_GRAY;
5434 case 0x168b: // secret gate (no key)
5435 element = EL_DC_GATE_FAKE_GRAY;
5439 element = EL_ROBOT_WHEEL;
5443 element = EL_DC_TIMEGATE_SWITCH;
5447 element = EL_ACID_POOL_BOTTOM;
5451 element = EL_ACID_POOL_TOPLEFT;
5455 element = EL_ACID_POOL_TOPRIGHT;
5459 element = EL_ACID_POOL_BOTTOMLEFT;
5463 element = EL_ACID_POOL_BOTTOMRIGHT;
5467 element = EL_STEELWALL;
5471 element = EL_STEELWALL_SLIPPERY;
5474 case 0x1695: // steel wall (not round)
5475 element = EL_STEELWALL;
5478 case 0x1696: // steel wall (left)
5479 element = EL_DC_STEELWALL_1_LEFT;
5482 case 0x1697: // steel wall (bottom)
5483 element = EL_DC_STEELWALL_1_BOTTOM;
5486 case 0x1698: // steel wall (right)
5487 element = EL_DC_STEELWALL_1_RIGHT;
5490 case 0x1699: // steel wall (top)
5491 element = EL_DC_STEELWALL_1_TOP;
5494 case 0x169a: // steel wall (left/bottom)
5495 element = EL_DC_STEELWALL_1_BOTTOMLEFT;
5498 case 0x169b: // steel wall (right/bottom)
5499 element = EL_DC_STEELWALL_1_BOTTOMRIGHT;
5502 case 0x169c: // steel wall (right/top)
5503 element = EL_DC_STEELWALL_1_TOPRIGHT;
5506 case 0x169d: // steel wall (left/top)
5507 element = EL_DC_STEELWALL_1_TOPLEFT;
5510 case 0x169e: // steel wall (right/bottom small)
5511 element = EL_DC_STEELWALL_1_BOTTOMRIGHT_2;
5514 case 0x169f: // steel wall (left/bottom small)
5515 element = EL_DC_STEELWALL_1_BOTTOMLEFT_2;
5518 case 0x16a0: // steel wall (right/top small)
5519 element = EL_DC_STEELWALL_1_TOPRIGHT_2;
5522 case 0x16a1: // steel wall (left/top small)
5523 element = EL_DC_STEELWALL_1_TOPLEFT_2;
5526 case 0x16a2: // steel wall (left/right)
5527 element = EL_DC_STEELWALL_1_VERTICAL;
5530 case 0x16a3: // steel wall (top/bottom)
5531 element = EL_DC_STEELWALL_1_HORIZONTAL;
5534 case 0x16a4: // steel wall 2 (left end)
5535 element = EL_DC_STEELWALL_2_LEFT;
5538 case 0x16a5: // steel wall 2 (right end)
5539 element = EL_DC_STEELWALL_2_RIGHT;
5542 case 0x16a6: // steel wall 2 (top end)
5543 element = EL_DC_STEELWALL_2_TOP;
5546 case 0x16a7: // steel wall 2 (bottom end)
5547 element = EL_DC_STEELWALL_2_BOTTOM;
5550 case 0x16a8: // steel wall 2 (left/right)
5551 element = EL_DC_STEELWALL_2_HORIZONTAL;
5554 case 0x16a9: // steel wall 2 (up/down)
5555 element = EL_DC_STEELWALL_2_VERTICAL;
5558 case 0x16aa: // steel wall 2 (mid)
5559 element = EL_DC_STEELWALL_2_MIDDLE;
5563 element = EL_SIGN_EXCLAMATION;
5567 element = EL_SIGN_RADIOACTIVITY;
5571 element = EL_SIGN_STOP;
5575 element = EL_SIGN_WHEELCHAIR;
5579 element = EL_SIGN_PARKING;
5583 element = EL_SIGN_NO_ENTRY;
5587 element = EL_SIGN_HEART;
5591 element = EL_SIGN_GIVE_WAY;
5595 element = EL_SIGN_ENTRY_FORBIDDEN;
5599 element = EL_SIGN_EMERGENCY_EXIT;
5603 element = EL_SIGN_YIN_YANG;
5607 element = EL_WALL_EMERALD;
5611 element = EL_WALL_DIAMOND;
5615 element = EL_WALL_PEARL;
5619 element = EL_WALL_CRYSTAL;
5623 element = EL_INVISIBLE_WALL;
5627 element = EL_INVISIBLE_STEELWALL;
5631 // EL_INVISIBLE_SAND
5634 element = EL_LIGHT_SWITCH;
5638 element = EL_ENVELOPE_1;
5642 if (element >= 0x0117 && element <= 0x036e) // (?)
5643 element = EL_DIAMOND;
5644 else if (element >= 0x042d && element <= 0x0684) // (?)
5645 element = EL_EMERALD;
5646 else if (element >= 0x157c && element <= 0x158b)
5648 else if (element >= 0x1590 && element <= 0x159f)
5649 element = EL_DC_LANDMINE;
5650 else if (element >= 0x16bc && element <= 0x16cb)
5651 element = EL_INVISIBLE_SAND;
5654 Warn("unknown Diamond Caves element 0x%04x", element);
5656 element = EL_UNKNOWN;
5661 return getMappedElement(element);
5664 static void LoadLevelFromFileStream_DC(File *file, struct LevelInfo *level,
5667 byte header[DC_LEVEL_HEADER_SIZE];
5669 int envelope_header_pos = 62;
5670 int envelope_content_pos = 94;
5671 int level_name_pos = 251;
5672 int level_author_pos = 292;
5673 int envelope_header_len;
5674 int envelope_content_len;
5676 int level_author_len;
5678 int num_yamyam_contents;
5681 getDecodedWord_DC(0, TRUE); // initialize DC2 decoding engine
5683 for (i = 0; i < DC_LEVEL_HEADER_SIZE / 2; i++)
5685 unsigned short header_word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
5687 header[i * 2 + 0] = header_word >> 8;
5688 header[i * 2 + 1] = header_word & 0xff;
5691 // read some values from level header to check level decoding integrity
5692 fieldx = header[6] | (header[7] << 8);
5693 fieldy = header[8] | (header[9] << 8);
5694 num_yamyam_contents = header[60] | (header[61] << 8);
5696 // do some simple sanity checks to ensure that level was correctly decoded
5697 if (fieldx < 1 || fieldx > 256 ||
5698 fieldy < 1 || fieldy > 256 ||
5699 num_yamyam_contents < 1 || num_yamyam_contents > 8)
5701 level->no_valid_file = TRUE;
5703 Warn("cannot decode level from stream -- using empty level");
5708 // maximum envelope header size is 31 bytes
5709 envelope_header_len = header[envelope_header_pos];
5710 // maximum envelope content size is 110 (156?) bytes
5711 envelope_content_len = header[envelope_content_pos];
5713 // maximum level title size is 40 bytes
5714 level_name_len = MIN(header[level_name_pos], MAX_LEVEL_NAME_LEN);
5715 // maximum level author size is 30 (51?) bytes
5716 level_author_len = MIN(header[level_author_pos], MAX_LEVEL_AUTHOR_LEN);
5720 for (i = 0; i < envelope_header_len; i++)
5721 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
5722 level->envelope[0].text[envelope_size++] =
5723 header[envelope_header_pos + 1 + i];
5725 if (envelope_header_len > 0 && envelope_content_len > 0)
5727 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
5728 level->envelope[0].text[envelope_size++] = '\n';
5729 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
5730 level->envelope[0].text[envelope_size++] = '\n';
5733 for (i = 0; i < envelope_content_len; i++)
5734 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
5735 level->envelope[0].text[envelope_size++] =
5736 header[envelope_content_pos + 1 + i];
5738 level->envelope[0].text[envelope_size] = '\0';
5740 level->envelope[0].xsize = MAX_ENVELOPE_XSIZE;
5741 level->envelope[0].ysize = 10;
5742 level->envelope[0].autowrap = TRUE;
5743 level->envelope[0].centered = TRUE;
5745 for (i = 0; i < level_name_len; i++)
5746 level->name[i] = header[level_name_pos + 1 + i];
5747 level->name[level_name_len] = '\0';
5749 for (i = 0; i < level_author_len; i++)
5750 level->author[i] = header[level_author_pos + 1 + i];
5751 level->author[level_author_len] = '\0';
5753 num_yamyam_contents = header[60] | (header[61] << 8);
5754 level->num_yamyam_contents =
5755 MIN(MAX(MIN_ELEMENT_CONTENTS, num_yamyam_contents), MAX_ELEMENT_CONTENTS);
5757 for (i = 0; i < num_yamyam_contents; i++)
5759 for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
5761 unsigned short word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
5762 int element_dc = ((word & 0xff) << 8) | ((word >> 8) & 0xff);
5764 if (i < MAX_ELEMENT_CONTENTS)
5765 level->yamyam_content[i].e[x][y] = getMappedElement_DC(element_dc);
5769 fieldx = header[6] | (header[7] << 8);
5770 fieldy = header[8] | (header[9] << 8);
5771 level->fieldx = MIN(MAX(MIN_LEV_FIELDX, fieldx), MAX_LEV_FIELDX);
5772 level->fieldy = MIN(MAX(MIN_LEV_FIELDY, fieldy), MAX_LEV_FIELDY);
5774 for (y = 0; y < fieldy; y++) for (x = 0; x < fieldx; x++)
5776 unsigned short word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
5777 int element_dc = ((word & 0xff) << 8) | ((word >> 8) & 0xff);
5779 if (x < MAX_LEV_FIELDX && y < MAX_LEV_FIELDY)
5780 level->field[x][y] = getMappedElement_DC(element_dc);
5783 x = MIN(MAX(0, (header[10] | (header[11] << 8)) - 1), MAX_LEV_FIELDX - 1);
5784 y = MIN(MAX(0, (header[12] | (header[13] << 8)) - 1), MAX_LEV_FIELDY - 1);
5785 level->field[x][y] = EL_PLAYER_1;
5787 x = MIN(MAX(0, (header[14] | (header[15] << 8)) - 1), MAX_LEV_FIELDX - 1);
5788 y = MIN(MAX(0, (header[16] | (header[17] << 8)) - 1), MAX_LEV_FIELDY - 1);
5789 level->field[x][y] = EL_PLAYER_2;
5791 level->gems_needed = header[18] | (header[19] << 8);
5793 level->score[SC_EMERALD] = header[20] | (header[21] << 8);
5794 level->score[SC_DIAMOND] = header[22] | (header[23] << 8);
5795 level->score[SC_PEARL] = header[24] | (header[25] << 8);
5796 level->score[SC_CRYSTAL] = header[26] | (header[27] << 8);
5797 level->score[SC_NUT] = header[28] | (header[29] << 8);
5798 level->score[SC_ROBOT] = header[30] | (header[31] << 8);
5799 level->score[SC_SPACESHIP] = header[32] | (header[33] << 8);
5800 level->score[SC_BUG] = header[34] | (header[35] << 8);
5801 level->score[SC_YAMYAM] = header[36] | (header[37] << 8);
5802 level->score[SC_DYNAMITE] = header[38] | (header[39] << 8);
5803 level->score[SC_KEY] = header[40] | (header[41] << 8);
5804 level->score[SC_TIME_BONUS] = header[42] | (header[43] << 8);
5806 level->time = header[44] | (header[45] << 8);
5808 level->amoeba_speed = header[46] | (header[47] << 8);
5809 level->time_light = header[48] | (header[49] << 8);
5810 level->time_timegate = header[50] | (header[51] << 8);
5811 level->time_wheel = header[52] | (header[53] << 8);
5812 level->time_magic_wall = header[54] | (header[55] << 8);
5813 level->extra_time = header[56] | (header[57] << 8);
5814 level->shield_normal_time = header[58] | (header[59] << 8);
5816 // shield and extra time elements do not have a score
5817 level->score[SC_SHIELD] = 0;
5818 level->extra_time_score = 0;
5820 // set time for normal and deadly shields to the same value
5821 level->shield_deadly_time = level->shield_normal_time;
5823 // Diamond Caves has the same (strange) behaviour as Emerald Mine that gems
5824 // can slip down from flat walls, like normal walls and steel walls
5825 level->em_slippery_gems = TRUE;
5827 // time score is counted for each 10 seconds left in Diamond Caves levels
5828 level->time_score_base = 10;
5831 static void LoadLevelFromFileInfo_DC(struct LevelInfo *level,
5832 struct LevelFileInfo *level_file_info,
5833 boolean level_info_only)
5835 char *filename = level_file_info->filename;
5837 int num_magic_bytes = 8;
5838 char magic_bytes[num_magic_bytes + 1];
5839 int num_levels_to_skip = level_file_info->nr - leveldir_current->first_level;
5841 if (!(file = openFile(filename, MODE_READ)))
5843 level->no_valid_file = TRUE;
5845 if (!level_info_only)
5846 Warn("cannot read level '%s' -- using empty level", filename);
5851 // fseek(file, 0x0000, SEEK_SET);
5853 if (level_file_info->packed)
5855 // read "magic bytes" from start of file
5856 if (getStringFromFile(file, magic_bytes, num_magic_bytes + 1) == NULL)
5857 magic_bytes[0] = '\0';
5859 // check "magic bytes" for correct file format
5860 if (!strPrefix(magic_bytes, "DC2"))
5862 level->no_valid_file = TRUE;
5864 Warn("unknown DC level file '%s' -- using empty level", filename);
5869 if (strPrefix(magic_bytes, "DC2Win95") ||
5870 strPrefix(magic_bytes, "DC2Win98"))
5872 int position_first_level = 0x00fa;
5873 int extra_bytes = 4;
5876 // advance file stream to first level inside the level package
5877 skip_bytes = position_first_level - num_magic_bytes - extra_bytes;
5879 // each block of level data is followed by block of non-level data
5880 num_levels_to_skip *= 2;
5882 // at least skip header bytes, therefore use ">= 0" instead of "> 0"
5883 while (num_levels_to_skip >= 0)
5885 // advance file stream to next level inside the level package
5886 if (seekFile(file, skip_bytes, SEEK_CUR) != 0)
5888 level->no_valid_file = TRUE;
5890 Warn("cannot fseek in file '%s' -- using empty level", filename);
5895 // skip apparently unused extra bytes following each level
5896 ReadUnusedBytesFromFile(file, extra_bytes);
5898 // read size of next level in level package
5899 skip_bytes = getFile32BitLE(file);
5901 num_levels_to_skip--;
5906 level->no_valid_file = TRUE;
5908 Warn("unknown DC2 level file '%s' -- using empty level", filename);
5914 LoadLevelFromFileStream_DC(file, level, level_file_info->nr);
5920 // ----------------------------------------------------------------------------
5921 // functions for loading SB level
5922 // ----------------------------------------------------------------------------
5924 int getMappedElement_SB(int element_ascii, boolean use_ces)
5932 sb_element_mapping[] =
5934 { ' ', EL_EMPTY, EL_CUSTOM_1 }, // floor (space)
5935 { '#', EL_STEELWALL, EL_CUSTOM_2 }, // wall
5936 { '@', EL_PLAYER_1, EL_CUSTOM_3 }, // player
5937 { '$', EL_SOKOBAN_OBJECT, EL_CUSTOM_4 }, // box
5938 { '.', EL_SOKOBAN_FIELD_EMPTY, EL_CUSTOM_5 }, // goal square
5939 { '*', EL_SOKOBAN_FIELD_FULL, EL_CUSTOM_6 }, // box on goal square
5940 { '+', EL_SOKOBAN_FIELD_PLAYER, EL_CUSTOM_7 }, // player on goal square
5941 { '_', EL_INVISIBLE_STEELWALL, EL_FROM_LEVEL_TEMPLATE }, // floor beyond border
5948 for (i = 0; sb_element_mapping[i].ascii != 0; i++)
5949 if (element_ascii == sb_element_mapping[i].ascii)
5950 return (use_ces ? sb_element_mapping[i].ce : sb_element_mapping[i].sb);
5952 return EL_UNDEFINED;
5955 static void SetLevelSettings_SB(struct LevelInfo *level)
5959 level->use_step_counter = TRUE;
5962 level->score[SC_TIME_BONUS] = 0;
5963 level->time_score_base = 1;
5964 level->rate_time_over_score = TRUE;
5967 level->auto_exit_sokoban = TRUE;
5970 static void LoadLevelFromFileInfo_SB(struct LevelInfo *level,
5971 struct LevelFileInfo *level_file_info,
5972 boolean level_info_only)
5974 char *filename = level_file_info->filename;
5975 char line[MAX_LINE_LEN], line_raw[MAX_LINE_LEN], previous_line[MAX_LINE_LEN];
5976 char last_comment[MAX_LINE_LEN];
5977 char level_name[MAX_LINE_LEN];
5980 int num_levels_to_skip = level_file_info->nr - leveldir_current->first_level;
5981 boolean read_continued_line = FALSE;
5982 boolean reading_playfield = FALSE;
5983 boolean got_valid_playfield_line = FALSE;
5984 boolean invalid_playfield_char = FALSE;
5985 boolean load_xsb_to_ces = check_special_flags("load_xsb_to_ces");
5986 int file_level_nr = 0;
5988 int x = 0, y = 0; // initialized to make compilers happy
5990 last_comment[0] = '\0';
5991 level_name[0] = '\0';
5993 if (!(file = openFile(filename, MODE_READ)))
5995 level->no_valid_file = TRUE;
5997 if (!level_info_only)
5998 Warn("cannot read level '%s' -- using empty level", filename);
6003 while (!checkEndOfFile(file))
6005 // level successfully read, but next level may follow here
6006 if (!got_valid_playfield_line && reading_playfield)
6008 // read playfield from single level file -- skip remaining file
6009 if (!level_file_info->packed)
6012 if (file_level_nr >= num_levels_to_skip)
6017 last_comment[0] = '\0';
6018 level_name[0] = '\0';
6020 reading_playfield = FALSE;
6023 got_valid_playfield_line = FALSE;
6025 // read next line of input file
6026 if (!getStringFromFile(file, line, MAX_LINE_LEN))
6029 // check if line was completely read and is terminated by line break
6030 if (strlen(line) > 0 && line[strlen(line) - 1] == '\n')
6033 // cut trailing line break (this can be newline and/or carriage return)
6034 for (line_ptr = &line[strlen(line)]; line_ptr >= line; line_ptr--)
6035 if ((*line_ptr == '\n' || *line_ptr == '\r') && *(line_ptr + 1) == '\0')
6038 // copy raw input line for later use (mainly debugging output)
6039 strcpy(line_raw, line);
6041 if (read_continued_line)
6043 // append new line to existing line, if there is enough space
6044 if (strlen(previous_line) + strlen(line_ptr) < MAX_LINE_LEN)
6045 strcat(previous_line, line_ptr);
6047 strcpy(line, previous_line); // copy storage buffer to line
6049 read_continued_line = FALSE;
6052 // if the last character is '\', continue at next line
6053 if (strlen(line) > 0 && line[strlen(line) - 1] == '\\')
6055 line[strlen(line) - 1] = '\0'; // cut off trailing backslash
6056 strcpy(previous_line, line); // copy line to storage buffer
6058 read_continued_line = TRUE;
6064 if (line[0] == '\0')
6067 // extract comment text from comment line
6070 for (line_ptr = line; *line_ptr; line_ptr++)
6071 if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != ';')
6074 strcpy(last_comment, line_ptr);
6079 // extract level title text from line containing level title
6080 if (line[0] == '\'')
6082 strcpy(level_name, &line[1]);
6084 if (strlen(level_name) > 0 && level_name[strlen(level_name) - 1] == '\'')
6085 level_name[strlen(level_name) - 1] = '\0';
6090 // skip lines containing only spaces (or empty lines)
6091 for (line_ptr = line; *line_ptr; line_ptr++)
6092 if (*line_ptr != ' ')
6094 if (*line_ptr == '\0')
6097 // at this point, we have found a line containing part of a playfield
6099 got_valid_playfield_line = TRUE;
6101 if (!reading_playfield)
6103 reading_playfield = TRUE;
6104 invalid_playfield_char = FALSE;
6106 for (x = 0; x < MAX_LEV_FIELDX; x++)
6107 for (y = 0; y < MAX_LEV_FIELDY; y++)
6108 level->field[x][y] = getMappedElement_SB(' ', load_xsb_to_ces);
6113 // start with topmost tile row
6117 // skip playfield line if larger row than allowed
6118 if (y >= MAX_LEV_FIELDY)
6121 // start with leftmost tile column
6124 // read playfield elements from line
6125 for (line_ptr = line; *line_ptr; line_ptr++)
6127 int mapped_sb_element = getMappedElement_SB(*line_ptr, load_xsb_to_ces);
6129 // stop parsing playfield line if larger column than allowed
6130 if (x >= MAX_LEV_FIELDX)
6133 if (mapped_sb_element == EL_UNDEFINED)
6135 invalid_playfield_char = TRUE;
6140 level->field[x][y] = mapped_sb_element;
6142 // continue with next tile column
6145 level->fieldx = MAX(x, level->fieldx);
6148 if (invalid_playfield_char)
6150 // if first playfield line, treat invalid lines as comment lines
6152 reading_playfield = FALSE;
6157 // continue with next tile row
6165 level->fieldx = MIN(MAX(MIN_LEV_FIELDX, level->fieldx), MAX_LEV_FIELDX);
6166 level->fieldy = MIN(MAX(MIN_LEV_FIELDY, level->fieldy), MAX_LEV_FIELDY);
6168 if (!reading_playfield)
6170 level->no_valid_file = TRUE;
6172 Warn("cannot read level '%s' -- using empty level", filename);
6177 if (*level_name != '\0')
6179 strncpy(level->name, level_name, MAX_LEVEL_NAME_LEN);
6180 level->name[MAX_LEVEL_NAME_LEN] = '\0';
6182 else if (*last_comment != '\0')
6184 strncpy(level->name, last_comment, MAX_LEVEL_NAME_LEN);
6185 level->name[MAX_LEVEL_NAME_LEN] = '\0';
6189 sprintf(level->name, "--> Level %d <--", level_file_info->nr);
6192 // set all empty fields beyond the border walls to invisible steel wall
6193 for (y = 0; y < level->fieldy; y++) for (x = 0; x < level->fieldx; x++)
6195 if ((x == 0 || x == level->fieldx - 1 ||
6196 y == 0 || y == level->fieldy - 1) &&
6197 level->field[x][y] == getMappedElement_SB(' ', load_xsb_to_ces))
6198 FloodFillLevel(x, y, getMappedElement_SB('_', load_xsb_to_ces),
6199 level->field, level->fieldx, level->fieldy);
6202 // set special level settings for Sokoban levels
6203 SetLevelSettings_SB(level);
6205 if (load_xsb_to_ces)
6207 // special global settings can now be set in level template
6208 level->use_custom_template = TRUE;
6213 // -------------------------------------------------------------------------
6214 // functions for handling native levels
6215 // -------------------------------------------------------------------------
6217 static void LoadLevelFromFileInfo_EM(struct LevelInfo *level,
6218 struct LevelFileInfo *level_file_info,
6219 boolean level_info_only)
6221 if (!LoadNativeLevel_EM(level_file_info->filename, level_info_only))
6222 level->no_valid_file = TRUE;
6225 static void LoadLevelFromFileInfo_SP(struct LevelInfo *level,
6226 struct LevelFileInfo *level_file_info,
6227 boolean level_info_only)
6231 // determine position of requested level inside level package
6232 if (level_file_info->packed)
6233 pos = level_file_info->nr - leveldir_current->first_level;
6235 if (!LoadNativeLevel_SP(level_file_info->filename, pos, level_info_only))
6236 level->no_valid_file = TRUE;
6239 static void LoadLevelFromFileInfo_MM(struct LevelInfo *level,
6240 struct LevelFileInfo *level_file_info,
6241 boolean level_info_only)
6243 if (!LoadNativeLevel_MM(level_file_info->filename, level_info_only))
6244 level->no_valid_file = TRUE;
6247 void CopyNativeLevel_RND_to_Native(struct LevelInfo *level)
6249 if (level->game_engine_type == GAME_ENGINE_TYPE_EM)
6250 CopyNativeLevel_RND_to_EM(level);
6251 else if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
6252 CopyNativeLevel_RND_to_SP(level);
6253 else if (level->game_engine_type == GAME_ENGINE_TYPE_MM)
6254 CopyNativeLevel_RND_to_MM(level);
6257 void CopyNativeLevel_Native_to_RND(struct LevelInfo *level)
6259 if (level->game_engine_type == GAME_ENGINE_TYPE_EM)
6260 CopyNativeLevel_EM_to_RND(level);
6261 else if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
6262 CopyNativeLevel_SP_to_RND(level);
6263 else if (level->game_engine_type == GAME_ENGINE_TYPE_MM)
6264 CopyNativeLevel_MM_to_RND(level);
6267 void SaveNativeLevel(struct LevelInfo *level)
6269 if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
6271 char *basename = getSingleLevelBasenameExt(level->file_info.nr, "sp");
6272 char *filename = getLevelFilenameFromBasename(basename);
6274 CopyNativeLevel_RND_to_SP(level);
6275 CopyNativeTape_RND_to_SP(level);
6277 SaveNativeLevel_SP(filename);
6282 // ----------------------------------------------------------------------------
6283 // functions for loading generic level
6284 // ----------------------------------------------------------------------------
6286 static void LoadLevelFromFileInfo(struct LevelInfo *level,
6287 struct LevelFileInfo *level_file_info,
6288 boolean level_info_only)
6290 // always start with reliable default values
6291 setLevelInfoToDefaults(level, level_info_only, TRUE);
6293 switch (level_file_info->type)
6295 case LEVEL_FILE_TYPE_RND:
6296 LoadLevelFromFileInfo_RND(level, level_file_info, level_info_only);
6299 case LEVEL_FILE_TYPE_EM:
6300 LoadLevelFromFileInfo_EM(level, level_file_info, level_info_only);
6301 level->game_engine_type = GAME_ENGINE_TYPE_EM;
6304 case LEVEL_FILE_TYPE_SP:
6305 LoadLevelFromFileInfo_SP(level, level_file_info, level_info_only);
6306 level->game_engine_type = GAME_ENGINE_TYPE_SP;
6309 case LEVEL_FILE_TYPE_MM:
6310 LoadLevelFromFileInfo_MM(level, level_file_info, level_info_only);
6311 level->game_engine_type = GAME_ENGINE_TYPE_MM;
6314 case LEVEL_FILE_TYPE_DC:
6315 LoadLevelFromFileInfo_DC(level, level_file_info, level_info_only);
6318 case LEVEL_FILE_TYPE_SB:
6319 LoadLevelFromFileInfo_SB(level, level_file_info, level_info_only);
6323 LoadLevelFromFileInfo_RND(level, level_file_info, level_info_only);
6327 // if level file is invalid, restore level structure to default values
6328 if (level->no_valid_file)
6329 setLevelInfoToDefaults(level, level_info_only, FALSE);
6331 if (level->game_engine_type == GAME_ENGINE_TYPE_UNKNOWN)
6332 level->game_engine_type = GAME_ENGINE_TYPE_RND;
6334 if (level_file_info->type != LEVEL_FILE_TYPE_RND)
6335 CopyNativeLevel_Native_to_RND(level);
6338 void LoadLevelFromFilename(struct LevelInfo *level, char *filename)
6340 static struct LevelFileInfo level_file_info;
6342 // always start with reliable default values
6343 setFileInfoToDefaults(&level_file_info);
6345 level_file_info.nr = 0; // unknown level number
6346 level_file_info.type = LEVEL_FILE_TYPE_RND; // no others supported yet
6348 setString(&level_file_info.filename, filename);
6350 LoadLevelFromFileInfo(level, &level_file_info, FALSE);
6353 static void LoadLevel_InitVersion(struct LevelInfo *level)
6357 if (leveldir_current == NULL) // only when dumping level
6360 // all engine modifications also valid for levels which use latest engine
6361 if (level->game_version < VERSION_IDENT(3,2,0,5))
6363 // time bonus score was given for 10 s instead of 1 s before 3.2.0-5
6364 level->time_score_base = 10;
6367 if (leveldir_current->latest_engine)
6369 // ---------- use latest game engine --------------------------------------
6371 /* For all levels which are forced to use the latest game engine version
6372 (normally all but user contributed, private and undefined levels), set
6373 the game engine version to the actual version; this allows for actual
6374 corrections in the game engine to take effect for existing, converted
6375 levels (from "classic" or other existing games) to make the emulation
6376 of the corresponding game more accurate, while (hopefully) not breaking
6377 existing levels created from other players. */
6379 level->game_version = GAME_VERSION_ACTUAL;
6381 /* Set special EM style gems behaviour: EM style gems slip down from
6382 normal, steel and growing wall. As this is a more fundamental change,
6383 it seems better to set the default behaviour to "off" (as it is more
6384 natural) and make it configurable in the level editor (as a property
6385 of gem style elements). Already existing converted levels (neither
6386 private nor contributed levels) are changed to the new behaviour. */
6388 if (level->file_version < FILE_VERSION_2_0)
6389 level->em_slippery_gems = TRUE;
6394 // ---------- use game engine the level was created with --------------------
6396 /* For all levels which are not forced to use the latest game engine
6397 version (normally user contributed, private and undefined levels),
6398 use the version of the game engine the levels were created for.
6400 Since 2.0.1, the game engine version is now directly stored
6401 in the level file (chunk "VERS"), so there is no need anymore
6402 to set the game version from the file version (except for old,
6403 pre-2.0 levels, where the game version is still taken from the
6404 file format version used to store the level -- see above). */
6406 // player was faster than enemies in 1.0.0 and before
6407 if (level->file_version == FILE_VERSION_1_0)
6408 for (i = 0; i < MAX_PLAYERS; i++)
6409 level->initial_player_stepsize[i] = STEPSIZE_FAST;
6411 // default behaviour for EM style gems was "slippery" only in 2.0.1
6412 if (level->game_version == VERSION_IDENT(2,0,1,0))
6413 level->em_slippery_gems = TRUE;
6415 // springs could be pushed over pits before (pre-release version) 2.2.0
6416 if (level->game_version < VERSION_IDENT(2,2,0,0))
6417 level->use_spring_bug = TRUE;
6419 if (level->game_version < VERSION_IDENT(3,2,0,5))
6421 // time orb caused limited time in endless time levels before 3.2.0-5
6422 level->use_time_orb_bug = TRUE;
6424 // default behaviour for snapping was "no snap delay" before 3.2.0-5
6425 level->block_snap_field = FALSE;
6427 // extra time score was same value as time left score before 3.2.0-5
6428 level->extra_time_score = level->score[SC_TIME_BONUS];
6431 if (level->game_version < VERSION_IDENT(3,2,0,7))
6433 // default behaviour for snapping was "not continuous" before 3.2.0-7
6434 level->continuous_snapping = FALSE;
6437 // only few elements were able to actively move into acid before 3.1.0
6438 // trigger settings did not exist before 3.1.0; set to default "any"
6439 if (level->game_version < VERSION_IDENT(3,1,0,0))
6441 // correct "can move into acid" settings (all zero in old levels)
6443 level->can_move_into_acid_bits = 0; // nothing can move into acid
6444 level->dont_collide_with_bits = 0; // nothing is deadly when colliding
6446 setMoveIntoAcidProperty(level, EL_ROBOT, TRUE);
6447 setMoveIntoAcidProperty(level, EL_SATELLITE, TRUE);
6448 setMoveIntoAcidProperty(level, EL_PENGUIN, TRUE);
6449 setMoveIntoAcidProperty(level, EL_BALLOON, TRUE);
6451 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6452 SET_PROPERTY(EL_CUSTOM_START + i, EP_CAN_MOVE_INTO_ACID, TRUE);
6454 // correct trigger settings (stored as zero == "none" in old levels)
6456 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6458 int element = EL_CUSTOM_START + i;
6459 struct ElementInfo *ei = &element_info[element];
6461 for (j = 0; j < ei->num_change_pages; j++)
6463 struct ElementChangeInfo *change = &ei->change_page[j];
6465 change->trigger_player = CH_PLAYER_ANY;
6466 change->trigger_page = CH_PAGE_ANY;
6471 // try to detect and fix "Snake Bite" levels, which are broken with 3.2.0
6473 int element = EL_CUSTOM_256;
6474 struct ElementInfo *ei = &element_info[element];
6475 struct ElementChangeInfo *change = &ei->change_page[0];
6477 /* This is needed to fix a problem that was caused by a bugfix in function
6478 game.c/CreateFieldExt() introduced with 3.2.0 that corrects the behaviour
6479 when a custom element changes to EL_SOKOBAN_FIELD_PLAYER (before, it did
6480 not replace walkable elements, but instead just placed the player on it,
6481 without placing the Sokoban field under the player). Unfortunately, this
6482 breaks "Snake Bite" style levels when the snake is halfway through a door
6483 that just closes (the snake head is still alive and can be moved in this
6484 case). This can be fixed by replacing the EL_SOKOBAN_FIELD_PLAYER by the
6485 player (without Sokoban element) which then gets killed as designed). */
6487 if ((strncmp(leveldir_current->identifier, "snake_bite", 10) == 0 ||
6488 strncmp(ei->description, "pause b4 death", 14) == 0) &&
6489 change->target_element == EL_SOKOBAN_FIELD_PLAYER)
6490 change->target_element = EL_PLAYER_1;
6493 // try to detect and fix "Zelda" style levels, which are broken with 3.2.5
6494 if (level->game_version < VERSION_IDENT(3,2,5,0))
6496 /* This is needed to fix a problem that was caused by a bugfix in function
6497 game.c/CheckTriggeredElementChangeExt() introduced with 3.2.5 that
6498 corrects the behaviour when a custom element changes to another custom
6499 element with a higher element number that has change actions defined.
6500 Normally, only one change per frame is allowed for custom elements.
6501 Therefore, it is checked if a custom element already changed in the
6502 current frame; if it did, subsequent changes are suppressed.
6503 Unfortunately, this is only checked for element changes, but not for
6504 change actions, which are still executed. As the function above loops
6505 through all custom elements from lower to higher, an element change
6506 resulting in a lower CE number won't be checked again, while a target
6507 element with a higher number will also be checked, and potential change
6508 actions will get executed for this CE, too (which is wrong), while
6509 further changes are ignored (which is correct). As this bugfix breaks
6510 Zelda II (and introduces graphical bugs to Zelda I, and also breaks a
6511 few other levels like Alan Bond's "FMV"), allow the previous, incorrect
6512 behaviour for existing levels and tapes that make use of this bug */
6514 level->use_action_after_change_bug = TRUE;
6517 // not centering level after relocating player was default only in 3.2.3
6518 if (level->game_version == VERSION_IDENT(3,2,3,0)) // (no pre-releases)
6519 level->shifted_relocation = TRUE;
6521 // EM style elements always chain-exploded in R'n'D engine before 3.2.6
6522 if (level->game_version < VERSION_IDENT(3,2,6,0))
6523 level->em_explodes_by_fire = TRUE;
6525 // levels were solved by the first player entering an exit up to 4.1.0.0
6526 if (level->game_version <= VERSION_IDENT(4,1,0,0))
6527 level->solved_by_one_player = TRUE;
6529 // game logic of "game of life" and "biomaze" was buggy before 4.1.1.1
6530 if (level->game_version < VERSION_IDENT(4,1,1,1))
6531 level->use_life_bugs = TRUE;
6533 // only Sokoban fields (but not objects) had to be solved before 4.1.1.1
6534 if (level->game_version < VERSION_IDENT(4,1,1,1))
6535 level->sb_objects_needed = FALSE;
6537 // CE actions were triggered by unfinished digging/collecting up to 4.2.2.0
6538 if (level->game_version <= VERSION_IDENT(4,2,2,0))
6539 level->finish_dig_collect = FALSE;
6541 // CE changing to player was kept under the player if walkable up to 4.2.3.1
6542 if (level->game_version <= VERSION_IDENT(4,2,3,1))
6543 level->keep_walkable_ce = TRUE;
6546 static void LoadLevel_InitSettings_SB(struct LevelInfo *level)
6548 boolean is_sokoban_level = TRUE; // unless non-Sokoban elements found
6551 // check if this level is (not) a Sokoban level
6552 for (y = 0; y < level->fieldy; y++)
6553 for (x = 0; x < level->fieldx; x++)
6554 if (!IS_SB_ELEMENT(Tile[x][y]))
6555 is_sokoban_level = FALSE;
6557 if (is_sokoban_level)
6559 // set special level settings for Sokoban levels
6560 SetLevelSettings_SB(level);
6564 static void LoadLevel_InitSettings(struct LevelInfo *level)
6566 // adjust level settings for (non-native) Sokoban-style levels
6567 LoadLevel_InitSettings_SB(level);
6570 static void LoadLevel_InitStandardElements(struct LevelInfo *level)
6574 // map elements that have changed in newer versions
6575 level->amoeba_content = getMappedElementByVersion(level->amoeba_content,
6576 level->game_version);
6577 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
6578 for (x = 0; x < 3; x++)
6579 for (y = 0; y < 3; y++)
6580 level->yamyam_content[i].e[x][y] =
6581 getMappedElementByVersion(level->yamyam_content[i].e[x][y],
6582 level->game_version);
6586 static void LoadLevel_InitCustomElements(struct LevelInfo *level)
6590 // map custom element change events that have changed in newer versions
6591 // (these following values were accidentally changed in version 3.0.1)
6592 // (this seems to be needed only for 'ab_levelset3' and 'ab_levelset4')
6593 if (level->game_version <= VERSION_IDENT(3,0,0,0))
6595 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6597 int element = EL_CUSTOM_START + i;
6599 // order of checking and copying events to be mapped is important
6600 // (do not change the start and end value -- they are constant)
6601 for (j = CE_BY_OTHER_ACTION; j >= CE_VALUE_GETS_ZERO; j--)
6603 if (HAS_CHANGE_EVENT(element, j - 2))
6605 SET_CHANGE_EVENT(element, j - 2, FALSE);
6606 SET_CHANGE_EVENT(element, j, TRUE);
6610 // order of checking and copying events to be mapped is important
6611 // (do not change the start and end value -- they are constant)
6612 for (j = CE_PLAYER_COLLECTS_X; j >= CE_HITTING_SOMETHING; j--)
6614 if (HAS_CHANGE_EVENT(element, j - 1))
6616 SET_CHANGE_EVENT(element, j - 1, FALSE);
6617 SET_CHANGE_EVENT(element, j, TRUE);
6623 // initialize "can_change" field for old levels with only one change page
6624 if (level->game_version <= VERSION_IDENT(3,0,2,0))
6626 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6628 int element = EL_CUSTOM_START + i;
6630 if (CAN_CHANGE(element))
6631 element_info[element].change->can_change = TRUE;
6635 // correct custom element values (for old levels without these options)
6636 if (level->game_version < VERSION_IDENT(3,1,1,0))
6638 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6640 int element = EL_CUSTOM_START + i;
6641 struct ElementInfo *ei = &element_info[element];
6643 if (ei->access_direction == MV_NO_DIRECTION)
6644 ei->access_direction = MV_ALL_DIRECTIONS;
6648 // correct custom element values (fix invalid values for all versions)
6651 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6653 int element = EL_CUSTOM_START + i;
6654 struct ElementInfo *ei = &element_info[element];
6656 for (j = 0; j < ei->num_change_pages; j++)
6658 struct ElementChangeInfo *change = &ei->change_page[j];
6660 if (change->trigger_player == CH_PLAYER_NONE)
6661 change->trigger_player = CH_PLAYER_ANY;
6663 if (change->trigger_side == CH_SIDE_NONE)
6664 change->trigger_side = CH_SIDE_ANY;
6669 // initialize "can_explode" field for old levels which did not store this
6670 // !!! CHECK THIS -- "<= 3,1,0,0" IS PROBABLY WRONG !!!
6671 if (level->game_version <= VERSION_IDENT(3,1,0,0))
6673 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6675 int element = EL_CUSTOM_START + i;
6677 if (EXPLODES_1X1_OLD(element))
6678 element_info[element].explosion_type = EXPLODES_1X1;
6680 SET_PROPERTY(element, EP_CAN_EXPLODE, (EXPLODES_BY_FIRE(element) ||
6681 EXPLODES_SMASHED(element) ||
6682 EXPLODES_IMPACT(element)));
6686 // correct previously hard-coded move delay values for maze runner style
6687 if (level->game_version < VERSION_IDENT(3,1,1,0))
6689 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6691 int element = EL_CUSTOM_START + i;
6693 if (element_info[element].move_pattern & MV_MAZE_RUNNER_STYLE)
6695 // previously hard-coded and therefore ignored
6696 element_info[element].move_delay_fixed = 9;
6697 element_info[element].move_delay_random = 0;
6702 // set some other uninitialized values of custom elements in older levels
6703 if (level->game_version < VERSION_IDENT(3,1,0,0))
6705 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6707 int element = EL_CUSTOM_START + i;
6709 element_info[element].access_direction = MV_ALL_DIRECTIONS;
6711 element_info[element].explosion_delay = 17;
6712 element_info[element].ignition_delay = 8;
6716 // set mouse click change events to work for left/middle/right mouse button
6717 if (level->game_version < VERSION_IDENT(4,2,3,0))
6719 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6721 int element = EL_CUSTOM_START + i;
6722 struct ElementInfo *ei = &element_info[element];
6724 for (j = 0; j < ei->num_change_pages; j++)
6726 struct ElementChangeInfo *change = &ei->change_page[j];
6728 if (change->has_event[CE_CLICKED_BY_MOUSE] ||
6729 change->has_event[CE_PRESSED_BY_MOUSE] ||
6730 change->has_event[CE_MOUSE_CLICKED_ON_X] ||
6731 change->has_event[CE_MOUSE_PRESSED_ON_X])
6732 change->trigger_side = CH_SIDE_ANY;
6738 static void LoadLevel_InitElements(struct LevelInfo *level)
6740 LoadLevel_InitStandardElements(level);
6742 if (level->file_has_custom_elements)
6743 LoadLevel_InitCustomElements(level);
6745 // initialize element properties for level editor etc.
6746 InitElementPropertiesEngine(level->game_version);
6747 InitElementPropertiesGfxElement();
6750 static void LoadLevel_InitPlayfield(struct LevelInfo *level)
6754 // map elements that have changed in newer versions
6755 for (y = 0; y < level->fieldy; y++)
6756 for (x = 0; x < level->fieldx; x++)
6757 level->field[x][y] = getMappedElementByVersion(level->field[x][y],
6758 level->game_version);
6760 // clear unused playfield data (nicer if level gets resized in editor)
6761 for (x = 0; x < MAX_LEV_FIELDX; x++)
6762 for (y = 0; y < MAX_LEV_FIELDY; y++)
6763 if (x >= level->fieldx || y >= level->fieldy)
6764 level->field[x][y] = EL_EMPTY;
6766 // copy elements to runtime playfield array
6767 for (x = 0; x < MAX_LEV_FIELDX; x++)
6768 for (y = 0; y < MAX_LEV_FIELDY; y++)
6769 Tile[x][y] = level->field[x][y];
6771 // initialize level size variables for faster access
6772 lev_fieldx = level->fieldx;
6773 lev_fieldy = level->fieldy;
6775 // determine border element for this level
6776 if (level->file_info.type == LEVEL_FILE_TYPE_DC)
6777 BorderElement = EL_EMPTY; // (in editor, SetBorderElement() is used)
6782 static void LoadLevel_InitNativeEngines(struct LevelInfo *level)
6784 struct LevelFileInfo *level_file_info = &level->file_info;
6786 if (level_file_info->type == LEVEL_FILE_TYPE_RND)
6787 CopyNativeLevel_RND_to_Native(level);
6790 static void LoadLevelTemplate_LoadAndInit(void)
6792 LoadLevelFromFileInfo(&level_template, &level_template.file_info, FALSE);
6794 LoadLevel_InitVersion(&level_template);
6795 LoadLevel_InitElements(&level_template);
6796 LoadLevel_InitSettings(&level_template);
6798 ActivateLevelTemplate();
6801 void LoadLevelTemplate(int nr)
6803 if (!fileExists(getGlobalLevelTemplateFilename()))
6805 Warn("no level template found for this level");
6810 setLevelFileInfo(&level_template.file_info, nr);
6812 LoadLevelTemplate_LoadAndInit();
6815 static void LoadNetworkLevelTemplate(struct NetworkLevelInfo *network_level)
6817 copyLevelFileInfo(&network_level->tmpl_info, &level_template.file_info);
6819 LoadLevelTemplate_LoadAndInit();
6822 static void LoadLevel_LoadAndInit(struct NetworkLevelInfo *network_level)
6824 LoadLevelFromFileInfo(&level, &level.file_info, FALSE);
6826 if (level.use_custom_template)
6828 if (network_level != NULL)
6829 LoadNetworkLevelTemplate(network_level);
6831 LoadLevelTemplate(-1);
6834 LoadLevel_InitVersion(&level);
6835 LoadLevel_InitElements(&level);
6836 LoadLevel_InitPlayfield(&level);
6837 LoadLevel_InitSettings(&level);
6839 LoadLevel_InitNativeEngines(&level);
6842 void LoadLevel(int nr)
6844 SetLevelSetInfo(leveldir_current->identifier, nr);
6846 setLevelFileInfo(&level.file_info, nr);
6848 LoadLevel_LoadAndInit(NULL);
6851 void LoadLevelInfoOnly(int nr)
6853 setLevelFileInfo(&level.file_info, nr);
6855 LoadLevelFromFileInfo(&level, &level.file_info, TRUE);
6858 void LoadNetworkLevel(struct NetworkLevelInfo *network_level)
6860 SetLevelSetInfo(network_level->leveldir_identifier,
6861 network_level->file_info.nr);
6863 copyLevelFileInfo(&network_level->file_info, &level.file_info);
6865 LoadLevel_LoadAndInit(network_level);
6868 static int SaveLevel_VERS(FILE *file, struct LevelInfo *level)
6872 chunk_size += putFileVersion(file, level->file_version);
6873 chunk_size += putFileVersion(file, level->game_version);
6878 static int SaveLevel_DATE(FILE *file, struct LevelInfo *level)
6882 chunk_size += putFile16BitBE(file, level->creation_date.year);
6883 chunk_size += putFile8Bit(file, level->creation_date.month);
6884 chunk_size += putFile8Bit(file, level->creation_date.day);
6889 #if ENABLE_HISTORIC_CHUNKS
6890 static void SaveLevel_HEAD(FILE *file, struct LevelInfo *level)
6894 putFile8Bit(file, level->fieldx);
6895 putFile8Bit(file, level->fieldy);
6897 putFile16BitBE(file, level->time);
6898 putFile16BitBE(file, level->gems_needed);
6900 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
6901 putFile8Bit(file, level->name[i]);
6903 for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
6904 putFile8Bit(file, level->score[i]);
6906 for (i = 0; i < STD_ELEMENT_CONTENTS; i++)
6907 for (y = 0; y < 3; y++)
6908 for (x = 0; x < 3; x++)
6909 putFile8Bit(file, (level->encoding_16bit_yamyam ? EL_EMPTY :
6910 level->yamyam_content[i].e[x][y]));
6911 putFile8Bit(file, level->amoeba_speed);
6912 putFile8Bit(file, level->time_magic_wall);
6913 putFile8Bit(file, level->time_wheel);
6914 putFile8Bit(file, (level->encoding_16bit_amoeba ? EL_EMPTY :
6915 level->amoeba_content));
6916 putFile8Bit(file, (level->initial_player_stepsize == STEPSIZE_FAST ? 1 : 0));
6917 putFile8Bit(file, (level->initial_gravity ? 1 : 0));
6918 putFile8Bit(file, (level->encoding_16bit_field ? 1 : 0));
6919 putFile8Bit(file, (level->em_slippery_gems ? 1 : 0));
6921 putFile8Bit(file, (level->use_custom_template ? 1 : 0));
6923 putFile8Bit(file, (level->block_last_field ? 1 : 0));
6924 putFile8Bit(file, (level->sp_block_last_field ? 1 : 0));
6925 putFile32BitBE(file, level->can_move_into_acid_bits);
6926 putFile8Bit(file, level->dont_collide_with_bits);
6928 putFile8Bit(file, (level->use_spring_bug ? 1 : 0));
6929 putFile8Bit(file, (level->use_step_counter ? 1 : 0));
6931 putFile8Bit(file, (level->instant_relocation ? 1 : 0));
6932 putFile8Bit(file, (level->can_pass_to_walkable ? 1 : 0));
6933 putFile8Bit(file, (level->grow_into_diggable ? 1 : 0));
6935 putFile8Bit(file, level->game_engine_type);
6937 WriteUnusedBytesToFile(file, LEVEL_CHUNK_HEAD_UNUSED);
6941 static int SaveLevel_NAME(FILE *file, struct LevelInfo *level)
6946 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
6947 chunk_size += putFile8Bit(file, level->name[i]);
6952 static int SaveLevel_AUTH(FILE *file, struct LevelInfo *level)
6957 for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
6958 chunk_size += putFile8Bit(file, level->author[i]);
6963 #if ENABLE_HISTORIC_CHUNKS
6964 static int SaveLevel_BODY(FILE *file, struct LevelInfo *level)
6969 for (y = 0; y < level->fieldy; y++)
6970 for (x = 0; x < level->fieldx; x++)
6971 if (level->encoding_16bit_field)
6972 chunk_size += putFile16BitBE(file, level->field[x][y]);
6974 chunk_size += putFile8Bit(file, level->field[x][y]);
6980 static int SaveLevel_BODY(FILE *file, struct LevelInfo *level)
6985 for (y = 0; y < level->fieldy; y++)
6986 for (x = 0; x < level->fieldx; x++)
6987 chunk_size += putFile16BitBE(file, level->field[x][y]);
6992 #if ENABLE_HISTORIC_CHUNKS
6993 static void SaveLevel_CONT(FILE *file, struct LevelInfo *level)
6997 putFile8Bit(file, EL_YAMYAM);
6998 putFile8Bit(file, level->num_yamyam_contents);
6999 putFile8Bit(file, 0);
7000 putFile8Bit(file, 0);
7002 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
7003 for (y = 0; y < 3; y++)
7004 for (x = 0; x < 3; x++)
7005 if (level->encoding_16bit_field)
7006 putFile16BitBE(file, level->yamyam_content[i].e[x][y]);
7008 putFile8Bit(file, level->yamyam_content[i].e[x][y]);
7012 #if ENABLE_HISTORIC_CHUNKS
7013 static void SaveLevel_CNT2(FILE *file, struct LevelInfo *level, int element)
7016 int num_contents, content_xsize, content_ysize;
7017 int content_array[MAX_ELEMENT_CONTENTS][3][3];
7019 if (element == EL_YAMYAM)
7021 num_contents = level->num_yamyam_contents;
7025 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
7026 for (y = 0; y < 3; y++)
7027 for (x = 0; x < 3; x++)
7028 content_array[i][x][y] = level->yamyam_content[i].e[x][y];
7030 else if (element == EL_BD_AMOEBA)
7036 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
7037 for (y = 0; y < 3; y++)
7038 for (x = 0; x < 3; x++)
7039 content_array[i][x][y] = EL_EMPTY;
7040 content_array[0][0][0] = level->amoeba_content;
7044 // chunk header already written -- write empty chunk data
7045 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_SIZE);
7047 Warn("cannot save content for element '%d'", element);
7052 putFile16BitBE(file, element);
7053 putFile8Bit(file, num_contents);
7054 putFile8Bit(file, content_xsize);
7055 putFile8Bit(file, content_ysize);
7057 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_UNUSED);
7059 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
7060 for (y = 0; y < 3; y++)
7061 for (x = 0; x < 3; x++)
7062 putFile16BitBE(file, content_array[i][x][y]);
7066 #if ENABLE_HISTORIC_CHUNKS
7067 static int SaveLevel_CNT3(FILE *file, struct LevelInfo *level, int element)
7069 int envelope_nr = element - EL_ENVELOPE_1;
7070 int envelope_len = strlen(level->envelope_text[envelope_nr]) + 1;
7074 chunk_size += putFile16BitBE(file, element);
7075 chunk_size += putFile16BitBE(file, envelope_len);
7076 chunk_size += putFile8Bit(file, level->envelope_xsize[envelope_nr]);
7077 chunk_size += putFile8Bit(file, level->envelope_ysize[envelope_nr]);
7079 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT3_UNUSED);
7080 chunk_size += LEVEL_CHUNK_CNT3_UNUSED;
7082 for (i = 0; i < envelope_len; i++)
7083 chunk_size += putFile8Bit(file, level->envelope_text[envelope_nr][i]);
7089 #if ENABLE_HISTORIC_CHUNKS
7090 static void SaveLevel_CUS1(FILE *file, struct LevelInfo *level,
7091 int num_changed_custom_elements)
7095 putFile16BitBE(file, num_changed_custom_elements);
7097 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7099 int element = EL_CUSTOM_START + i;
7101 struct ElementInfo *ei = &element_info[element];
7103 if (ei->properties[EP_BITFIELD_BASE_NR] != EP_BITMASK_DEFAULT)
7105 if (check < num_changed_custom_elements)
7107 putFile16BitBE(file, element);
7108 putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
7115 if (check != num_changed_custom_elements) // should not happen
7116 Warn("inconsistent number of custom element properties");
7120 #if ENABLE_HISTORIC_CHUNKS
7121 static void SaveLevel_CUS2(FILE *file, struct LevelInfo *level,
7122 int num_changed_custom_elements)
7126 putFile16BitBE(file, num_changed_custom_elements);
7128 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7130 int element = EL_CUSTOM_START + i;
7132 if (element_info[element].change->target_element != EL_EMPTY_SPACE)
7134 if (check < num_changed_custom_elements)
7136 putFile16BitBE(file, element);
7137 putFile16BitBE(file, element_info[element].change->target_element);
7144 if (check != num_changed_custom_elements) // should not happen
7145 Warn("inconsistent number of custom target elements");
7149 #if ENABLE_HISTORIC_CHUNKS
7150 static void SaveLevel_CUS3(FILE *file, struct LevelInfo *level,
7151 int num_changed_custom_elements)
7153 int i, j, x, y, check = 0;
7155 putFile16BitBE(file, num_changed_custom_elements);
7157 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7159 int element = EL_CUSTOM_START + i;
7160 struct ElementInfo *ei = &element_info[element];
7162 if (ei->modified_settings)
7164 if (check < num_changed_custom_elements)
7166 putFile16BitBE(file, element);
7168 for (j = 0; j < MAX_ELEMENT_NAME_LEN; j++)
7169 putFile8Bit(file, ei->description[j]);
7171 putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
7173 // some free bytes for future properties and padding
7174 WriteUnusedBytesToFile(file, 7);
7176 putFile8Bit(file, ei->use_gfx_element);
7177 putFile16BitBE(file, ei->gfx_element_initial);
7179 putFile8Bit(file, ei->collect_score_initial);
7180 putFile8Bit(file, ei->collect_count_initial);
7182 putFile16BitBE(file, ei->push_delay_fixed);
7183 putFile16BitBE(file, ei->push_delay_random);
7184 putFile16BitBE(file, ei->move_delay_fixed);
7185 putFile16BitBE(file, ei->move_delay_random);
7187 putFile16BitBE(file, ei->move_pattern);
7188 putFile8Bit(file, ei->move_direction_initial);
7189 putFile8Bit(file, ei->move_stepsize);
7191 for (y = 0; y < 3; y++)
7192 for (x = 0; x < 3; x++)
7193 putFile16BitBE(file, ei->content.e[x][y]);
7195 putFile32BitBE(file, ei->change->events);
7197 putFile16BitBE(file, ei->change->target_element);
7199 putFile16BitBE(file, ei->change->delay_fixed);
7200 putFile16BitBE(file, ei->change->delay_random);
7201 putFile16BitBE(file, ei->change->delay_frames);
7203 putFile16BitBE(file, ei->change->initial_trigger_element);
7205 putFile8Bit(file, ei->change->explode);
7206 putFile8Bit(file, ei->change->use_target_content);
7207 putFile8Bit(file, ei->change->only_if_complete);
7208 putFile8Bit(file, ei->change->use_random_replace);
7210 putFile8Bit(file, ei->change->random_percentage);
7211 putFile8Bit(file, ei->change->replace_when);
7213 for (y = 0; y < 3; y++)
7214 for (x = 0; x < 3; x++)
7215 putFile16BitBE(file, ei->change->content.e[x][y]);
7217 putFile8Bit(file, ei->slippery_type);
7219 // some free bytes for future properties and padding
7220 WriteUnusedBytesToFile(file, LEVEL_CPART_CUS3_UNUSED);
7227 if (check != num_changed_custom_elements) // should not happen
7228 Warn("inconsistent number of custom element properties");
7232 #if ENABLE_HISTORIC_CHUNKS
7233 static void SaveLevel_CUS4(FILE *file, struct LevelInfo *level, int element)
7235 struct ElementInfo *ei = &element_info[element];
7238 // ---------- custom element base property values (96 bytes) ----------------
7240 putFile16BitBE(file, element);
7242 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
7243 putFile8Bit(file, ei->description[i]);
7245 putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
7247 WriteUnusedBytesToFile(file, 4); // reserved for more base properties
7249 putFile8Bit(file, ei->num_change_pages);
7251 putFile16BitBE(file, ei->ce_value_fixed_initial);
7252 putFile16BitBE(file, ei->ce_value_random_initial);
7253 putFile8Bit(file, ei->use_last_ce_value);
7255 putFile8Bit(file, ei->use_gfx_element);
7256 putFile16BitBE(file, ei->gfx_element_initial);
7258 putFile8Bit(file, ei->collect_score_initial);
7259 putFile8Bit(file, ei->collect_count_initial);
7261 putFile8Bit(file, ei->drop_delay_fixed);
7262 putFile8Bit(file, ei->push_delay_fixed);
7263 putFile8Bit(file, ei->drop_delay_random);
7264 putFile8Bit(file, ei->push_delay_random);
7265 putFile16BitBE(file, ei->move_delay_fixed);
7266 putFile16BitBE(file, ei->move_delay_random);
7268 // bits 0 - 15 of "move_pattern" ...
7269 putFile16BitBE(file, ei->move_pattern & 0xffff);
7270 putFile8Bit(file, ei->move_direction_initial);
7271 putFile8Bit(file, ei->move_stepsize);
7273 putFile8Bit(file, ei->slippery_type);
7275 for (y = 0; y < 3; y++)
7276 for (x = 0; x < 3; x++)
7277 putFile16BitBE(file, ei->content.e[x][y]);
7279 putFile16BitBE(file, ei->move_enter_element);
7280 putFile16BitBE(file, ei->move_leave_element);
7281 putFile8Bit(file, ei->move_leave_type);
7283 // ... bits 16 - 31 of "move_pattern" (not nice, but downward compatible)
7284 putFile16BitBE(file, (ei->move_pattern >> 16) & 0xffff);
7286 putFile8Bit(file, ei->access_direction);
7288 putFile8Bit(file, ei->explosion_delay);
7289 putFile8Bit(file, ei->ignition_delay);
7290 putFile8Bit(file, ei->explosion_type);
7292 // some free bytes for future custom property values and padding
7293 WriteUnusedBytesToFile(file, 1);
7295 // ---------- change page property values (48 bytes) ------------------------
7297 for (i = 0; i < ei->num_change_pages; i++)
7299 struct ElementChangeInfo *change = &ei->change_page[i];
7300 unsigned int event_bits;
7302 // bits 0 - 31 of "has_event[]" ...
7304 for (j = 0; j < MIN(NUM_CHANGE_EVENTS, 32); j++)
7305 if (change->has_event[j])
7306 event_bits |= (1 << j);
7307 putFile32BitBE(file, event_bits);
7309 putFile16BitBE(file, change->target_element);
7311 putFile16BitBE(file, change->delay_fixed);
7312 putFile16BitBE(file, change->delay_random);
7313 putFile16BitBE(file, change->delay_frames);
7315 putFile16BitBE(file, change->initial_trigger_element);
7317 putFile8Bit(file, change->explode);
7318 putFile8Bit(file, change->use_target_content);
7319 putFile8Bit(file, change->only_if_complete);
7320 putFile8Bit(file, change->use_random_replace);
7322 putFile8Bit(file, change->random_percentage);
7323 putFile8Bit(file, change->replace_when);
7325 for (y = 0; y < 3; y++)
7326 for (x = 0; x < 3; x++)
7327 putFile16BitBE(file, change->target_content.e[x][y]);
7329 putFile8Bit(file, change->can_change);
7331 putFile8Bit(file, change->trigger_side);
7333 putFile8Bit(file, change->trigger_player);
7334 putFile8Bit(file, (change->trigger_page == CH_PAGE_ANY ? CH_PAGE_ANY_FILE :
7335 log_2(change->trigger_page)));
7337 putFile8Bit(file, change->has_action);
7338 putFile8Bit(file, change->action_type);
7339 putFile8Bit(file, change->action_mode);
7340 putFile16BitBE(file, change->action_arg);
7342 // ... bits 32 - 39 of "has_event[]" (not nice, but downward compatible)
7344 for (j = 32; j < NUM_CHANGE_EVENTS; j++)
7345 if (change->has_event[j])
7346 event_bits |= (1 << (j - 32));
7347 putFile8Bit(file, event_bits);
7352 #if ENABLE_HISTORIC_CHUNKS
7353 static void SaveLevel_GRP1(FILE *file, struct LevelInfo *level, int element)
7355 struct ElementInfo *ei = &element_info[element];
7356 struct ElementGroupInfo *group = ei->group;
7359 putFile16BitBE(file, element);
7361 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
7362 putFile8Bit(file, ei->description[i]);
7364 putFile8Bit(file, group->num_elements);
7366 putFile8Bit(file, ei->use_gfx_element);
7367 putFile16BitBE(file, ei->gfx_element_initial);
7369 putFile8Bit(file, group->choice_mode);
7371 // some free bytes for future values and padding
7372 WriteUnusedBytesToFile(file, 3);
7374 for (i = 0; i < MAX_ELEMENTS_IN_GROUP; i++)
7375 putFile16BitBE(file, group->element[i]);
7379 static int SaveLevel_MicroChunk(FILE *file, struct LevelFileConfigInfo *entry,
7380 boolean write_element)
7382 int save_type = entry->save_type;
7383 int data_type = entry->data_type;
7384 int conf_type = entry->conf_type;
7385 int byte_mask = conf_type & CONF_MASK_BYTES;
7386 int element = entry->element;
7387 int default_value = entry->default_value;
7389 boolean modified = FALSE;
7391 if (byte_mask != CONF_MASK_MULTI_BYTES)
7393 void *value_ptr = entry->value;
7394 int value = (data_type == TYPE_BOOLEAN ? *(boolean *)value_ptr :
7397 // check if any settings have been modified before saving them
7398 if (value != default_value)
7401 // do not save if explicitly told or if unmodified default settings
7402 if ((save_type == SAVE_CONF_NEVER) ||
7403 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
7407 num_bytes += putFile16BitBE(file, element);
7409 num_bytes += putFile8Bit(file, conf_type);
7410 num_bytes += (byte_mask == CONF_MASK_1_BYTE ? putFile8Bit (file, value) :
7411 byte_mask == CONF_MASK_2_BYTE ? putFile16BitBE(file, value) :
7412 byte_mask == CONF_MASK_4_BYTE ? putFile32BitBE(file, value) :
7415 else if (data_type == TYPE_STRING)
7417 char *default_string = entry->default_string;
7418 char *string = (char *)(entry->value);
7419 int string_length = strlen(string);
7422 // check if any settings have been modified before saving them
7423 if (!strEqual(string, default_string))
7426 // do not save if explicitly told or if unmodified default settings
7427 if ((save_type == SAVE_CONF_NEVER) ||
7428 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
7432 num_bytes += putFile16BitBE(file, element);
7434 num_bytes += putFile8Bit(file, conf_type);
7435 num_bytes += putFile16BitBE(file, string_length);
7437 for (i = 0; i < string_length; i++)
7438 num_bytes += putFile8Bit(file, string[i]);
7440 else if (data_type == TYPE_ELEMENT_LIST)
7442 int *element_array = (int *)(entry->value);
7443 int num_elements = *(int *)(entry->num_entities);
7446 // check if any settings have been modified before saving them
7447 for (i = 0; i < num_elements; i++)
7448 if (element_array[i] != default_value)
7451 // do not save if explicitly told or if unmodified default settings
7452 if ((save_type == SAVE_CONF_NEVER) ||
7453 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
7457 num_bytes += putFile16BitBE(file, element);
7459 num_bytes += putFile8Bit(file, conf_type);
7460 num_bytes += putFile16BitBE(file, num_elements * CONF_ELEMENT_NUM_BYTES);
7462 for (i = 0; i < num_elements; i++)
7463 num_bytes += putFile16BitBE(file, element_array[i]);
7465 else if (data_type == TYPE_CONTENT_LIST)
7467 struct Content *content = (struct Content *)(entry->value);
7468 int num_contents = *(int *)(entry->num_entities);
7471 // check if any settings have been modified before saving them
7472 for (i = 0; i < num_contents; i++)
7473 for (y = 0; y < 3; y++)
7474 for (x = 0; x < 3; x++)
7475 if (content[i].e[x][y] != default_value)
7478 // do not save if explicitly told or if unmodified default settings
7479 if ((save_type == SAVE_CONF_NEVER) ||
7480 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
7484 num_bytes += putFile16BitBE(file, element);
7486 num_bytes += putFile8Bit(file, conf_type);
7487 num_bytes += putFile16BitBE(file, num_contents * CONF_CONTENT_NUM_BYTES);
7489 for (i = 0; i < num_contents; i++)
7490 for (y = 0; y < 3; y++)
7491 for (x = 0; x < 3; x++)
7492 num_bytes += putFile16BitBE(file, content[i].e[x][y]);
7498 static int SaveLevel_INFO(FILE *file, struct LevelInfo *level)
7503 li = *level; // copy level data into temporary buffer
7505 for (i = 0; chunk_config_INFO[i].data_type != -1; i++)
7506 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_INFO[i], FALSE);
7511 static int SaveLevel_ELEM(FILE *file, struct LevelInfo *level)
7516 li = *level; // copy level data into temporary buffer
7518 for (i = 0; chunk_config_ELEM[i].data_type != -1; i++)
7519 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_ELEM[i], TRUE);
7524 static int SaveLevel_NOTE(FILE *file, struct LevelInfo *level, int element)
7526 int envelope_nr = element - EL_ENVELOPE_1;
7530 chunk_size += putFile16BitBE(file, element);
7532 // copy envelope data into temporary buffer
7533 xx_envelope = level->envelope[envelope_nr];
7535 for (i = 0; chunk_config_NOTE[i].data_type != -1; i++)
7536 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_NOTE[i], FALSE);
7541 static int SaveLevel_CUSX(FILE *file, struct LevelInfo *level, int element)
7543 struct ElementInfo *ei = &element_info[element];
7547 chunk_size += putFile16BitBE(file, element);
7549 xx_ei = *ei; // copy element data into temporary buffer
7551 // set default description string for this specific element
7552 strcpy(xx_default_description, getDefaultElementDescription(ei));
7554 for (i = 0; chunk_config_CUSX_base[i].data_type != -1; i++)
7555 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_CUSX_base[i], FALSE);
7557 for (i = 0; i < ei->num_change_pages; i++)
7559 struct ElementChangeInfo *change = &ei->change_page[i];
7561 xx_current_change_page = i;
7563 xx_change = *change; // copy change data into temporary buffer
7566 setEventBitsFromEventFlags(change);
7568 for (j = 0; chunk_config_CUSX_change[j].data_type != -1; j++)
7569 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_CUSX_change[j],
7576 static int SaveLevel_GRPX(FILE *file, struct LevelInfo *level, int element)
7578 struct ElementInfo *ei = &element_info[element];
7579 struct ElementGroupInfo *group = ei->group;
7583 chunk_size += putFile16BitBE(file, element);
7585 xx_ei = *ei; // copy element data into temporary buffer
7586 xx_group = *group; // copy group data into temporary buffer
7588 // set default description string for this specific element
7589 strcpy(xx_default_description, getDefaultElementDescription(ei));
7591 for (i = 0; chunk_config_GRPX[i].data_type != -1; i++)
7592 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_GRPX[i], FALSE);
7597 static int SaveLevel_EMPX(FILE *file, struct LevelInfo *level, int element)
7599 struct ElementInfo *ei = &element_info[element];
7603 chunk_size += putFile16BitBE(file, element);
7605 xx_ei = *ei; // copy element data into temporary buffer
7607 for (i = 0; chunk_config_EMPX[i].data_type != -1; i++)
7608 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_EMPX[i], FALSE);
7613 static void SaveLevelFromFilename(struct LevelInfo *level, char *filename,
7614 boolean save_as_template)
7620 if (!(file = fopen(filename, MODE_WRITE)))
7622 Warn("cannot save level file '%s'", filename);
7627 level->file_version = FILE_VERSION_ACTUAL;
7628 level->game_version = GAME_VERSION_ACTUAL;
7630 level->creation_date = getCurrentDate();
7632 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
7633 putFileChunkBE(file, "CAVE", CHUNK_SIZE_NONE);
7635 chunk_size = SaveLevel_VERS(NULL, level);
7636 putFileChunkBE(file, "VERS", chunk_size);
7637 SaveLevel_VERS(file, level);
7639 chunk_size = SaveLevel_DATE(NULL, level);
7640 putFileChunkBE(file, "DATE", chunk_size);
7641 SaveLevel_DATE(file, level);
7643 chunk_size = SaveLevel_NAME(NULL, level);
7644 putFileChunkBE(file, "NAME", chunk_size);
7645 SaveLevel_NAME(file, level);
7647 chunk_size = SaveLevel_AUTH(NULL, level);
7648 putFileChunkBE(file, "AUTH", chunk_size);
7649 SaveLevel_AUTH(file, level);
7651 chunk_size = SaveLevel_INFO(NULL, level);
7652 putFileChunkBE(file, "INFO", chunk_size);
7653 SaveLevel_INFO(file, level);
7655 chunk_size = SaveLevel_BODY(NULL, level);
7656 putFileChunkBE(file, "BODY", chunk_size);
7657 SaveLevel_BODY(file, level);
7659 chunk_size = SaveLevel_ELEM(NULL, level);
7660 if (chunk_size > LEVEL_CHUNK_ELEM_UNCHANGED) // save if changed
7662 putFileChunkBE(file, "ELEM", chunk_size);
7663 SaveLevel_ELEM(file, level);
7666 for (i = 0; i < NUM_ENVELOPES; i++)
7668 int element = EL_ENVELOPE_1 + i;
7670 chunk_size = SaveLevel_NOTE(NULL, level, element);
7671 if (chunk_size > LEVEL_CHUNK_NOTE_UNCHANGED) // save if changed
7673 putFileChunkBE(file, "NOTE", chunk_size);
7674 SaveLevel_NOTE(file, level, element);
7678 // if not using template level, check for non-default custom/group elements
7679 if (!level->use_custom_template || save_as_template)
7681 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7683 int element = EL_CUSTOM_START + i;
7685 chunk_size = SaveLevel_CUSX(NULL, level, element);
7686 if (chunk_size > LEVEL_CHUNK_CUSX_UNCHANGED) // save if changed
7688 putFileChunkBE(file, "CUSX", chunk_size);
7689 SaveLevel_CUSX(file, level, element);
7693 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
7695 int element = EL_GROUP_START + i;
7697 chunk_size = SaveLevel_GRPX(NULL, level, element);
7698 if (chunk_size > LEVEL_CHUNK_GRPX_UNCHANGED) // save if changed
7700 putFileChunkBE(file, "GRPX", chunk_size);
7701 SaveLevel_GRPX(file, level, element);
7705 for (i = 0; i < NUM_EMPTY_ELEMENTS_ALL; i++)
7707 int element = GET_EMPTY_ELEMENT(i);
7709 chunk_size = SaveLevel_EMPX(NULL, level, element);
7710 if (chunk_size > LEVEL_CHUNK_EMPX_UNCHANGED) // save if changed
7712 putFileChunkBE(file, "EMPX", chunk_size);
7713 SaveLevel_EMPX(file, level, element);
7720 SetFilePermissions(filename, PERMS_PRIVATE);
7723 void SaveLevel(int nr)
7725 char *filename = getDefaultLevelFilename(nr);
7727 SaveLevelFromFilename(&level, filename, FALSE);
7730 void SaveLevelTemplate(void)
7732 char *filename = getLocalLevelTemplateFilename();
7734 SaveLevelFromFilename(&level, filename, TRUE);
7737 boolean SaveLevelChecked(int nr)
7739 char *filename = getDefaultLevelFilename(nr);
7740 boolean new_level = !fileExists(filename);
7741 boolean level_saved = FALSE;
7743 if (new_level || Request("Save this level and kill the old?", REQ_ASK))
7748 Request("Level saved!", REQ_CONFIRM);
7756 void DumpLevel(struct LevelInfo *level)
7758 if (level->no_level_file || level->no_valid_file)
7760 Warn("cannot dump -- no valid level file found");
7766 Print("Level xxx (file version %08d, game version %08d)\n",
7767 level->file_version, level->game_version);
7770 Print("Level author: '%s'\n", level->author);
7771 Print("Level title: '%s'\n", level->name);
7773 Print("Playfield size: %d x %d\n", level->fieldx, level->fieldy);
7775 Print("Level time: %d seconds\n", level->time);
7776 Print("Gems needed: %d\n", level->gems_needed);
7778 Print("Time for magic wall: %d seconds\n", level->time_magic_wall);
7779 Print("Time for wheel: %d seconds\n", level->time_wheel);
7780 Print("Time for light: %d seconds\n", level->time_light);
7781 Print("Time for timegate: %d seconds\n", level->time_timegate);
7783 Print("Amoeba speed: %d\n", level->amoeba_speed);
7786 Print("EM style slippery gems: %s\n", (level->em_slippery_gems ? "yes" : "no"));
7787 Print("Player blocks last field: %s\n", (level->block_last_field ? "yes" : "no"));
7788 Print("SP player blocks last field: %s\n", (level->sp_block_last_field ? "yes" : "no"));
7789 Print("use spring bug: %s\n", (level->use_spring_bug ? "yes" : "no"));
7790 Print("use step counter: %s\n", (level->use_step_counter ? "yes" : "no"));
7791 Print("rate time over score: %s\n", (level->rate_time_over_score ? "yes" : "no"));
7796 void DumpLevels(void)
7798 static LevelDirTree *dumplevel_leveldir = NULL;
7800 dumplevel_leveldir = getTreeInfoFromIdentifier(leveldir_first,
7801 global.dumplevel_leveldir);
7803 if (dumplevel_leveldir == NULL)
7804 Fail("no such level identifier: '%s'", global.dumplevel_leveldir);
7806 if (global.dumplevel_level_nr < dumplevel_leveldir->first_level ||
7807 global.dumplevel_level_nr > dumplevel_leveldir->last_level)
7808 Fail("no such level number: %d", global.dumplevel_level_nr);
7810 leveldir_current = dumplevel_leveldir;
7812 LoadLevel(global.dumplevel_level_nr);
7819 // ============================================================================
7820 // tape file functions
7821 // ============================================================================
7823 static void setTapeInfoToDefaults(void)
7827 // always start with reliable default values (empty tape)
7830 // default values (also for pre-1.2 tapes) with only the first player
7831 tape.player_participates[0] = TRUE;
7832 for (i = 1; i < MAX_PLAYERS; i++)
7833 tape.player_participates[i] = FALSE;
7835 // at least one (default: the first) player participates in every tape
7836 tape.num_participating_players = 1;
7838 tape.property_bits = TAPE_PROPERTY_NONE;
7840 tape.level_nr = level_nr;
7842 tape.changed = FALSE;
7844 tape.recording = FALSE;
7845 tape.playing = FALSE;
7846 tape.pausing = FALSE;
7848 tape.scr_fieldx = SCR_FIELDX_DEFAULT;
7849 tape.scr_fieldy = SCR_FIELDY_DEFAULT;
7851 tape.no_info_chunk = TRUE;
7852 tape.no_valid_file = FALSE;
7855 static int getTapePosSize(struct TapeInfo *tape)
7857 int tape_pos_size = 0;
7859 if (tape->use_key_actions)
7860 tape_pos_size += tape->num_participating_players;
7862 if (tape->use_mouse_actions)
7863 tape_pos_size += 3; // x and y position and mouse button mask
7865 tape_pos_size += 1; // tape action delay value
7867 return tape_pos_size;
7870 static void setTapeActionFlags(struct TapeInfo *tape, int value)
7872 tape->use_key_actions = FALSE;
7873 tape->use_mouse_actions = FALSE;
7875 if (value != TAPE_USE_MOUSE_ACTIONS_ONLY)
7876 tape->use_key_actions = TRUE;
7878 if (value != TAPE_USE_KEY_ACTIONS_ONLY)
7879 tape->use_mouse_actions = TRUE;
7882 static int getTapeActionValue(struct TapeInfo *tape)
7884 return (tape->use_key_actions &&
7885 tape->use_mouse_actions ? TAPE_USE_KEY_AND_MOUSE_ACTIONS :
7886 tape->use_key_actions ? TAPE_USE_KEY_ACTIONS_ONLY :
7887 tape->use_mouse_actions ? TAPE_USE_MOUSE_ACTIONS_ONLY :
7888 TAPE_ACTIONS_DEFAULT);
7891 static int LoadTape_VERS(File *file, int chunk_size, struct TapeInfo *tape)
7893 tape->file_version = getFileVersion(file);
7894 tape->game_version = getFileVersion(file);
7899 static int LoadTape_HEAD(File *file, int chunk_size, struct TapeInfo *tape)
7903 tape->random_seed = getFile32BitBE(file);
7904 tape->date = getFile32BitBE(file);
7905 tape->length = getFile32BitBE(file);
7907 // read header fields that are new since version 1.2
7908 if (tape->file_version >= FILE_VERSION_1_2)
7910 byte store_participating_players = getFile8Bit(file);
7913 // since version 1.2, tapes store which players participate in the tape
7914 tape->num_participating_players = 0;
7915 for (i = 0; i < MAX_PLAYERS; i++)
7917 tape->player_participates[i] = FALSE;
7919 if (store_participating_players & (1 << i))
7921 tape->player_participates[i] = TRUE;
7922 tape->num_participating_players++;
7926 setTapeActionFlags(tape, getFile8Bit(file));
7928 tape->property_bits = getFile8Bit(file);
7930 ReadUnusedBytesFromFile(file, TAPE_CHUNK_HEAD_UNUSED);
7932 engine_version = getFileVersion(file);
7933 if (engine_version > 0)
7934 tape->engine_version = engine_version;
7936 tape->engine_version = tape->game_version;
7942 static int LoadTape_SCRN(File *file, int chunk_size, struct TapeInfo *tape)
7944 tape->scr_fieldx = getFile8Bit(file);
7945 tape->scr_fieldy = getFile8Bit(file);
7950 static int LoadTape_INFO(File *file, int chunk_size, struct TapeInfo *tape)
7952 char *level_identifier = NULL;
7953 int level_identifier_size;
7956 tape->no_info_chunk = FALSE;
7958 level_identifier_size = getFile16BitBE(file);
7960 level_identifier = checked_malloc(level_identifier_size);
7962 for (i = 0; i < level_identifier_size; i++)
7963 level_identifier[i] = getFile8Bit(file);
7965 strncpy(tape->level_identifier, level_identifier, MAX_FILENAME_LEN);
7966 tape->level_identifier[MAX_FILENAME_LEN] = '\0';
7968 checked_free(level_identifier);
7970 tape->level_nr = getFile16BitBE(file);
7972 chunk_size = 2 + level_identifier_size + 2;
7977 static int LoadTape_BODY(File *file, int chunk_size, struct TapeInfo *tape)
7980 int tape_pos_size = getTapePosSize(tape);
7981 int chunk_size_expected = tape_pos_size * tape->length;
7983 if (chunk_size_expected != chunk_size)
7985 ReadUnusedBytesFromFile(file, chunk_size);
7986 return chunk_size_expected;
7989 for (i = 0; i < tape->length; i++)
7991 if (i >= MAX_TAPE_LEN)
7993 Warn("tape truncated -- size exceeds maximum tape size %d",
7996 // tape too large; read and ignore remaining tape data from this chunk
7997 for (;i < tape->length; i++)
7998 ReadUnusedBytesFromFile(file, tape_pos_size);
8003 if (tape->use_key_actions)
8005 for (j = 0; j < MAX_PLAYERS; j++)
8007 tape->pos[i].action[j] = MV_NONE;
8009 if (tape->player_participates[j])
8010 tape->pos[i].action[j] = getFile8Bit(file);
8014 if (tape->use_mouse_actions)
8016 tape->pos[i].action[TAPE_ACTION_LX] = getFile8Bit(file);
8017 tape->pos[i].action[TAPE_ACTION_LY] = getFile8Bit(file);
8018 tape->pos[i].action[TAPE_ACTION_BUTTON] = getFile8Bit(file);
8021 tape->pos[i].delay = getFile8Bit(file);
8023 if (tape->file_version == FILE_VERSION_1_0)
8025 // eliminate possible diagonal moves in old tapes
8026 // this is only for backward compatibility
8028 byte joy_dir[4] = { JOY_LEFT, JOY_RIGHT, JOY_UP, JOY_DOWN };
8029 byte action = tape->pos[i].action[0];
8030 int k, num_moves = 0;
8032 for (k = 0; k<4; k++)
8034 if (action & joy_dir[k])
8036 tape->pos[i + num_moves].action[0] = joy_dir[k];
8038 tape->pos[i + num_moves].delay = 0;
8047 tape->length += num_moves;
8050 else if (tape->file_version < FILE_VERSION_2_0)
8052 // convert pre-2.0 tapes to new tape format
8054 if (tape->pos[i].delay > 1)
8057 tape->pos[i + 1] = tape->pos[i];
8058 tape->pos[i + 1].delay = 1;
8061 for (j = 0; j < MAX_PLAYERS; j++)
8062 tape->pos[i].action[j] = MV_NONE;
8063 tape->pos[i].delay--;
8070 if (checkEndOfFile(file))
8074 if (i != tape->length)
8075 chunk_size = tape_pos_size * i;
8080 static void LoadTape_SokobanSolution(char *filename)
8083 int move_delay = TILESIZE / level.initial_player_stepsize[0];
8085 if (!(file = openFile(filename, MODE_READ)))
8087 tape.no_valid_file = TRUE;
8092 while (!checkEndOfFile(file))
8094 unsigned char c = getByteFromFile(file);
8096 if (checkEndOfFile(file))
8103 tape.pos[tape.length].action[0] = MV_UP;
8104 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
8110 tape.pos[tape.length].action[0] = MV_DOWN;
8111 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
8117 tape.pos[tape.length].action[0] = MV_LEFT;
8118 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
8124 tape.pos[tape.length].action[0] = MV_RIGHT;
8125 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
8133 // ignore white-space characters
8137 tape.no_valid_file = TRUE;
8139 Warn("unsupported Sokoban solution file '%s' ['%d']", filename, c);
8147 if (tape.no_valid_file)
8150 tape.length_frames = GetTapeLengthFrames();
8151 tape.length_seconds = GetTapeLengthSeconds();
8154 void LoadTapeFromFilename(char *filename)
8156 char cookie[MAX_LINE_LEN];
8157 char chunk_name[CHUNK_ID_LEN + 1];
8161 // always start with reliable default values
8162 setTapeInfoToDefaults();
8164 if (strSuffix(filename, ".sln"))
8166 LoadTape_SokobanSolution(filename);
8171 if (!(file = openFile(filename, MODE_READ)))
8173 tape.no_valid_file = TRUE;
8178 getFileChunkBE(file, chunk_name, NULL);
8179 if (strEqual(chunk_name, "RND1"))
8181 getFile32BitBE(file); // not used
8183 getFileChunkBE(file, chunk_name, NULL);
8184 if (!strEqual(chunk_name, "TAPE"))
8186 tape.no_valid_file = TRUE;
8188 Warn("unknown format of tape file '%s'", filename);
8195 else // check for pre-2.0 file format with cookie string
8197 strcpy(cookie, chunk_name);
8198 if (getStringFromFile(file, &cookie[4], MAX_LINE_LEN - 4) == NULL)
8200 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
8201 cookie[strlen(cookie) - 1] = '\0';
8203 if (!checkCookieString(cookie, TAPE_COOKIE_TMPL))
8205 tape.no_valid_file = TRUE;
8207 Warn("unknown format of tape file '%s'", filename);
8214 if ((tape.file_version = getFileVersionFromCookieString(cookie)) == -1)
8216 tape.no_valid_file = TRUE;
8218 Warn("unsupported version of tape file '%s'", filename);
8225 // pre-2.0 tape files have no game version, so use file version here
8226 tape.game_version = tape.file_version;
8229 if (tape.file_version < FILE_VERSION_1_2)
8231 // tape files from versions before 1.2.0 without chunk structure
8232 LoadTape_HEAD(file, TAPE_CHUNK_HEAD_SIZE, &tape);
8233 LoadTape_BODY(file, 2 * tape.length, &tape);
8241 int (*loader)(File *, int, struct TapeInfo *);
8245 { "VERS", TAPE_CHUNK_VERS_SIZE, LoadTape_VERS },
8246 { "HEAD", TAPE_CHUNK_HEAD_SIZE, LoadTape_HEAD },
8247 { "SCRN", TAPE_CHUNK_SCRN_SIZE, LoadTape_SCRN },
8248 { "INFO", -1, LoadTape_INFO },
8249 { "BODY", -1, LoadTape_BODY },
8253 while (getFileChunkBE(file, chunk_name, &chunk_size))
8257 while (chunk_info[i].name != NULL &&
8258 !strEqual(chunk_name, chunk_info[i].name))
8261 if (chunk_info[i].name == NULL)
8263 Warn("unknown chunk '%s' in tape file '%s'",
8264 chunk_name, filename);
8266 ReadUnusedBytesFromFile(file, chunk_size);
8268 else if (chunk_info[i].size != -1 &&
8269 chunk_info[i].size != chunk_size)
8271 Warn("wrong size (%d) of chunk '%s' in tape file '%s'",
8272 chunk_size, chunk_name, filename);
8274 ReadUnusedBytesFromFile(file, chunk_size);
8278 // call function to load this tape chunk
8279 int chunk_size_expected =
8280 (chunk_info[i].loader)(file, chunk_size, &tape);
8282 // the size of some chunks cannot be checked before reading other
8283 // chunks first (like "HEAD" and "BODY") that contain some header
8284 // information, so check them here
8285 if (chunk_size_expected != chunk_size)
8287 Warn("wrong size (%d) of chunk '%s' in tape file '%s'",
8288 chunk_size, chunk_name, filename);
8296 tape.length_frames = GetTapeLengthFrames();
8297 tape.length_seconds = GetTapeLengthSeconds();
8300 Debug("files:LoadTapeFromFilename", "tape file version: %d",
8302 Debug("files:LoadTapeFromFilename", "tape game version: %d",
8304 Debug("files:LoadTapeFromFilename", "tape engine version: %d",
8305 tape.engine_version);
8309 void LoadTape(int nr)
8311 char *filename = getTapeFilename(nr);
8313 LoadTapeFromFilename(filename);
8316 void LoadSolutionTape(int nr)
8318 char *filename = getSolutionTapeFilename(nr);
8320 LoadTapeFromFilename(filename);
8322 if (TAPE_IS_EMPTY(tape) &&
8323 level.game_engine_type == GAME_ENGINE_TYPE_SP &&
8324 level.native_sp_level->demo.is_available)
8325 CopyNativeTape_SP_to_RND(&level);
8328 static boolean checkSaveTape_SCRN(struct TapeInfo *tape)
8330 // chunk required for team mode tapes with non-default screen size
8331 return (tape->num_participating_players > 1 &&
8332 (tape->scr_fieldx != SCR_FIELDX_DEFAULT ||
8333 tape->scr_fieldy != SCR_FIELDY_DEFAULT));
8336 static void SaveTape_VERS(FILE *file, struct TapeInfo *tape)
8338 putFileVersion(file, tape->file_version);
8339 putFileVersion(file, tape->game_version);
8342 static void SaveTape_HEAD(FILE *file, struct TapeInfo *tape)
8345 byte store_participating_players = 0;
8347 // set bits for participating players for compact storage
8348 for (i = 0; i < MAX_PLAYERS; i++)
8349 if (tape->player_participates[i])
8350 store_participating_players |= (1 << i);
8352 putFile32BitBE(file, tape->random_seed);
8353 putFile32BitBE(file, tape->date);
8354 putFile32BitBE(file, tape->length);
8356 putFile8Bit(file, store_participating_players);
8358 putFile8Bit(file, getTapeActionValue(tape));
8360 putFile8Bit(file, tape->property_bits);
8362 // unused bytes not at the end here for 4-byte alignment of engine_version
8363 WriteUnusedBytesToFile(file, TAPE_CHUNK_HEAD_UNUSED);
8365 putFileVersion(file, tape->engine_version);
8368 static void SaveTape_SCRN(FILE *file, struct TapeInfo *tape)
8370 putFile8Bit(file, tape->scr_fieldx);
8371 putFile8Bit(file, tape->scr_fieldy);
8374 static void SaveTape_INFO(FILE *file, struct TapeInfo *tape)
8376 int level_identifier_size = strlen(tape->level_identifier) + 1;
8379 putFile16BitBE(file, level_identifier_size);
8381 for (i = 0; i < level_identifier_size; i++)
8382 putFile8Bit(file, tape->level_identifier[i]);
8384 putFile16BitBE(file, tape->level_nr);
8387 static void SaveTape_BODY(FILE *file, struct TapeInfo *tape)
8391 for (i = 0; i < tape->length; i++)
8393 if (tape->use_key_actions)
8395 for (j = 0; j < MAX_PLAYERS; j++)
8396 if (tape->player_participates[j])
8397 putFile8Bit(file, tape->pos[i].action[j]);
8400 if (tape->use_mouse_actions)
8402 putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_LX]);
8403 putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_LY]);
8404 putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_BUTTON]);
8407 putFile8Bit(file, tape->pos[i].delay);
8411 void SaveTapeToFilename(char *filename)
8415 int info_chunk_size;
8416 int body_chunk_size;
8418 if (!(file = fopen(filename, MODE_WRITE)))
8420 Warn("cannot save level recording file '%s'", filename);
8425 tape_pos_size = getTapePosSize(&tape);
8427 info_chunk_size = 2 + (strlen(tape.level_identifier) + 1) + 2;
8428 body_chunk_size = tape_pos_size * tape.length;
8430 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
8431 putFileChunkBE(file, "TAPE", CHUNK_SIZE_NONE);
8433 putFileChunkBE(file, "VERS", TAPE_CHUNK_VERS_SIZE);
8434 SaveTape_VERS(file, &tape);
8436 putFileChunkBE(file, "HEAD", TAPE_CHUNK_HEAD_SIZE);
8437 SaveTape_HEAD(file, &tape);
8439 if (checkSaveTape_SCRN(&tape))
8441 putFileChunkBE(file, "SCRN", TAPE_CHUNK_SCRN_SIZE);
8442 SaveTape_SCRN(file, &tape);
8445 putFileChunkBE(file, "INFO", info_chunk_size);
8446 SaveTape_INFO(file, &tape);
8448 putFileChunkBE(file, "BODY", body_chunk_size);
8449 SaveTape_BODY(file, &tape);
8453 SetFilePermissions(filename, PERMS_PRIVATE);
8456 static void SaveTapeExt(char *filename)
8460 tape.file_version = FILE_VERSION_ACTUAL;
8461 tape.game_version = GAME_VERSION_ACTUAL;
8463 tape.num_participating_players = 0;
8465 // count number of participating players
8466 for (i = 0; i < MAX_PLAYERS; i++)
8467 if (tape.player_participates[i])
8468 tape.num_participating_players++;
8470 SaveTapeToFilename(filename);
8472 tape.changed = FALSE;
8475 void SaveTape(int nr)
8477 char *filename = getTapeFilename(nr);
8479 InitTapeDirectory(leveldir_current->subdir);
8481 SaveTapeExt(filename);
8484 void SaveScoreTape(int nr)
8486 char *filename = getScoreTapeFilename(tape.score_tape_basename, nr);
8488 // used instead of "leveldir_current->subdir" (for network games)
8489 InitScoreTapeDirectory(levelset.identifier, nr);
8491 SaveTapeExt(filename);
8494 static boolean SaveTapeCheckedExt(int nr, char *msg_replace, char *msg_saved,
8495 unsigned int req_state_added)
8497 char *filename = getTapeFilename(nr);
8498 boolean new_tape = !fileExists(filename);
8499 boolean tape_saved = FALSE;
8501 if (new_tape || Request(msg_replace, REQ_ASK | req_state_added))
8506 Request(msg_saved, REQ_CONFIRM | req_state_added);
8514 boolean SaveTapeChecked(int nr)
8516 return SaveTapeCheckedExt(nr, "Replace old tape?", "Tape saved!", 0);
8519 boolean SaveTapeChecked_LevelSolved(int nr)
8521 return SaveTapeCheckedExt(nr, "Level solved! Replace old tape?",
8522 "Level solved! Tape saved!", REQ_STAY_OPEN);
8525 void DumpTape(struct TapeInfo *tape)
8527 int tape_frame_counter;
8530 if (tape->no_valid_file)
8532 Warn("cannot dump -- no valid tape file found");
8539 Print("Tape of Level %03d (file version %08d, game version %08d)\n",
8540 tape->level_nr, tape->file_version, tape->game_version);
8541 Print(" (effective engine version %08d)\n",
8542 tape->engine_version);
8543 Print("Level series identifier: '%s'\n", tape->level_identifier);
8545 Print("Special tape properties: ");
8546 if (tape->property_bits == TAPE_PROPERTY_NONE)
8548 if (tape->property_bits & TAPE_PROPERTY_EM_RANDOM_BUG)
8549 Print("[em_random_bug]");
8550 if (tape->property_bits & TAPE_PROPERTY_GAME_SPEED)
8551 Print("[game_speed]");
8552 if (tape->property_bits & TAPE_PROPERTY_PAUSE_MODE)
8554 if (tape->property_bits & TAPE_PROPERTY_SINGLE_STEP)
8555 Print("[single_step]");
8556 if (tape->property_bits & TAPE_PROPERTY_SNAPSHOT)
8557 Print("[snapshot]");
8558 if (tape->property_bits & TAPE_PROPERTY_REPLAYED)
8559 Print("[replayed]");
8560 if (tape->property_bits & TAPE_PROPERTY_TAS_KEYS)
8561 Print("[tas_keys]");
8562 if (tape->property_bits & TAPE_PROPERTY_SMALL_GRAPHICS)
8563 Print("[small_graphics]");
8566 int year2 = tape->date / 10000;
8567 int year4 = (year2 < 70 ? 2000 + year2 : 1900 + year2);
8568 int month_index_raw = (tape->date / 100) % 100;
8569 int month_index = month_index_raw % 12; // prevent invalid index
8570 int month = month_index + 1;
8571 int day = tape->date % 100;
8573 Print("Tape date: %04d-%02d-%02d\n", year4, month, day);
8577 tape_frame_counter = 0;
8579 for (i = 0; i < tape->length; i++)
8581 if (i >= MAX_TAPE_LEN)
8586 for (j = 0; j < MAX_PLAYERS; j++)
8588 if (tape->player_participates[j])
8590 int action = tape->pos[i].action[j];
8592 Print("%d:%02x ", j, action);
8593 Print("[%c%c%c%c|%c%c] - ",
8594 (action & JOY_LEFT ? '<' : ' '),
8595 (action & JOY_RIGHT ? '>' : ' '),
8596 (action & JOY_UP ? '^' : ' '),
8597 (action & JOY_DOWN ? 'v' : ' '),
8598 (action & JOY_BUTTON_1 ? '1' : ' '),
8599 (action & JOY_BUTTON_2 ? '2' : ' '));
8603 Print("(%03d) ", tape->pos[i].delay);
8604 Print("[%05d]\n", tape_frame_counter);
8606 tape_frame_counter += tape->pos[i].delay;
8612 void DumpTapes(void)
8614 static LevelDirTree *dumptape_leveldir = NULL;
8616 dumptape_leveldir = getTreeInfoFromIdentifier(leveldir_first,
8617 global.dumptape_leveldir);
8619 if (dumptape_leveldir == NULL)
8620 Fail("no such level identifier: '%s'", global.dumptape_leveldir);
8622 if (global.dumptape_level_nr < dumptape_leveldir->first_level ||
8623 global.dumptape_level_nr > dumptape_leveldir->last_level)
8624 Fail("no such level number: %d", global.dumptape_level_nr);
8626 leveldir_current = dumptape_leveldir;
8628 if (options.mytapes)
8629 LoadTape(global.dumptape_level_nr);
8631 LoadSolutionTape(global.dumptape_level_nr);
8639 // ============================================================================
8640 // score file functions
8641 // ============================================================================
8643 static void setScoreInfoToDefaultsExt(struct ScoreInfo *scores)
8647 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
8649 strcpy(scores->entry[i].tape_basename, UNDEFINED_FILENAME);
8650 strcpy(scores->entry[i].name, EMPTY_PLAYER_NAME);
8651 scores->entry[i].score = 0;
8652 scores->entry[i].time = 0;
8655 scores->num_entries = 0;
8656 scores->last_added = -1;
8657 scores->last_added_local = -1;
8659 scores->updated = FALSE;
8660 scores->uploaded = FALSE;
8661 scores->force_last_added = FALSE;
8664 static void setScoreInfoToDefaults(void)
8666 setScoreInfoToDefaultsExt(&scores);
8669 static void setServerScoreInfoToDefaults(void)
8671 setScoreInfoToDefaultsExt(&server_scores);
8674 static void LoadScore_OLD(int nr)
8677 char *filename = getScoreFilename(nr);
8678 char cookie[MAX_LINE_LEN];
8679 char line[MAX_LINE_LEN];
8683 if (!(file = fopen(filename, MODE_READ)))
8686 // check file identifier
8687 if (fgets(cookie, MAX_LINE_LEN, file) == NULL)
8689 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
8690 cookie[strlen(cookie) - 1] = '\0';
8692 if (!checkCookieString(cookie, SCORE_COOKIE_TMPL))
8694 Warn("unknown format of score file '%s'", filename);
8701 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
8703 if (fscanf(file, "%d", &scores.entry[i].score) == EOF)
8704 Warn("fscanf() failed; %s", strerror(errno));
8706 if (fgets(line, MAX_LINE_LEN, file) == NULL)
8709 if (strlen(line) > 0 && line[strlen(line) - 1] == '\n')
8710 line[strlen(line) - 1] = '\0';
8712 for (line_ptr = line; *line_ptr; line_ptr++)
8714 if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != '\0')
8716 strncpy(scores.entry[i].name, line_ptr, MAX_PLAYER_NAME_LEN);
8717 scores.entry[i].name[MAX_PLAYER_NAME_LEN] = '\0';
8726 static void ConvertScore_OLD(void)
8728 // only convert score to time for levels that rate playing time over score
8729 if (!level.rate_time_over_score)
8732 // convert old score to playing time for score-less levels (like Supaplex)
8733 int time_final_max = 999;
8736 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
8738 int score = scores.entry[i].score;
8740 if (score > 0 && score < time_final_max)
8741 scores.entry[i].time = (time_final_max - score - 1) * FRAMES_PER_SECOND;
8745 static int LoadScore_VERS(File *file, int chunk_size, struct ScoreInfo *scores)
8747 scores->file_version = getFileVersion(file);
8748 scores->game_version = getFileVersion(file);
8753 static int LoadScore_INFO(File *file, int chunk_size, struct ScoreInfo *scores)
8755 char *level_identifier = NULL;
8756 int level_identifier_size;
8759 level_identifier_size = getFile16BitBE(file);
8761 level_identifier = checked_malloc(level_identifier_size);
8763 for (i = 0; i < level_identifier_size; i++)
8764 level_identifier[i] = getFile8Bit(file);
8766 strncpy(scores->level_identifier, level_identifier, MAX_FILENAME_LEN);
8767 scores->level_identifier[MAX_FILENAME_LEN] = '\0';
8769 checked_free(level_identifier);
8771 scores->level_nr = getFile16BitBE(file);
8772 scores->num_entries = getFile16BitBE(file);
8774 chunk_size = 2 + level_identifier_size + 2 + 2;
8779 static int LoadScore_NAME(File *file, int chunk_size, struct ScoreInfo *scores)
8783 for (i = 0; i < scores->num_entries; i++)
8785 for (j = 0; j < MAX_PLAYER_NAME_LEN; j++)
8786 scores->entry[i].name[j] = getFile8Bit(file);
8788 scores->entry[i].name[MAX_PLAYER_NAME_LEN] = '\0';
8791 chunk_size = scores->num_entries * MAX_PLAYER_NAME_LEN;
8796 static int LoadScore_SCOR(File *file, int chunk_size, struct ScoreInfo *scores)
8800 for (i = 0; i < scores->num_entries; i++)
8801 scores->entry[i].score = getFile16BitBE(file);
8803 chunk_size = scores->num_entries * 2;
8808 static int LoadScore_SC4R(File *file, int chunk_size, struct ScoreInfo *scores)
8812 for (i = 0; i < scores->num_entries; i++)
8813 scores->entry[i].score = getFile32BitBE(file);
8815 chunk_size = scores->num_entries * 4;
8820 static int LoadScore_TIME(File *file, int chunk_size, struct ScoreInfo *scores)
8824 for (i = 0; i < scores->num_entries; i++)
8825 scores->entry[i].time = getFile32BitBE(file);
8827 chunk_size = scores->num_entries * 4;
8832 static int LoadScore_TAPE(File *file, int chunk_size, struct ScoreInfo *scores)
8836 for (i = 0; i < scores->num_entries; i++)
8838 for (j = 0; j < MAX_SCORE_TAPE_BASENAME_LEN; j++)
8839 scores->entry[i].tape_basename[j] = getFile8Bit(file);
8841 scores->entry[i].tape_basename[MAX_SCORE_TAPE_BASENAME_LEN] = '\0';
8844 chunk_size = scores->num_entries * MAX_SCORE_TAPE_BASENAME_LEN;
8849 void LoadScore(int nr)
8851 char *filename = getScoreFilename(nr);
8852 char cookie[MAX_LINE_LEN];
8853 char chunk_name[CHUNK_ID_LEN + 1];
8855 boolean old_score_file_format = FALSE;
8858 // always start with reliable default values
8859 setScoreInfoToDefaults();
8861 if (!(file = openFile(filename, MODE_READ)))
8864 getFileChunkBE(file, chunk_name, NULL);
8865 if (strEqual(chunk_name, "RND1"))
8867 getFile32BitBE(file); // not used
8869 getFileChunkBE(file, chunk_name, NULL);
8870 if (!strEqual(chunk_name, "SCOR"))
8872 Warn("unknown format of score file '%s'", filename);
8879 else // check for old file format with cookie string
8881 strcpy(cookie, chunk_name);
8882 if (getStringFromFile(file, &cookie[4], MAX_LINE_LEN - 4) == NULL)
8884 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
8885 cookie[strlen(cookie) - 1] = '\0';
8887 if (!checkCookieString(cookie, SCORE_COOKIE_TMPL))
8889 Warn("unknown format of score file '%s'", filename);
8896 old_score_file_format = TRUE;
8899 if (old_score_file_format)
8901 // score files from versions before 4.2.4.0 without chunk structure
8904 // convert score to time, if possible (mainly for Supaplex levels)
8913 int (*loader)(File *, int, struct ScoreInfo *);
8917 { "VERS", SCORE_CHUNK_VERS_SIZE, LoadScore_VERS },
8918 { "INFO", -1, LoadScore_INFO },
8919 { "NAME", -1, LoadScore_NAME },
8920 { "SCOR", -1, LoadScore_SCOR },
8921 { "SC4R", -1, LoadScore_SC4R },
8922 { "TIME", -1, LoadScore_TIME },
8923 { "TAPE", -1, LoadScore_TAPE },
8928 while (getFileChunkBE(file, chunk_name, &chunk_size))
8932 while (chunk_info[i].name != NULL &&
8933 !strEqual(chunk_name, chunk_info[i].name))
8936 if (chunk_info[i].name == NULL)
8938 Warn("unknown chunk '%s' in score file '%s'",
8939 chunk_name, filename);
8941 ReadUnusedBytesFromFile(file, chunk_size);
8943 else if (chunk_info[i].size != -1 &&
8944 chunk_info[i].size != chunk_size)
8946 Warn("wrong size (%d) of chunk '%s' in score file '%s'",
8947 chunk_size, chunk_name, filename);
8949 ReadUnusedBytesFromFile(file, chunk_size);
8953 // call function to load this score chunk
8954 int chunk_size_expected =
8955 (chunk_info[i].loader)(file, chunk_size, &scores);
8957 // the size of some chunks cannot be checked before reading other
8958 // chunks first (like "HEAD" and "BODY") that contain some header
8959 // information, so check them here
8960 if (chunk_size_expected != chunk_size)
8962 Warn("wrong size (%d) of chunk '%s' in score file '%s'",
8963 chunk_size, chunk_name, filename);
8972 #if ENABLE_HISTORIC_CHUNKS
8973 void SaveScore_OLD(int nr)
8976 char *filename = getScoreFilename(nr);
8979 // used instead of "leveldir_current->subdir" (for network games)
8980 InitScoreDirectory(levelset.identifier);
8982 if (!(file = fopen(filename, MODE_WRITE)))
8984 Warn("cannot save score for level %d", nr);
8989 fprintf(file, "%s\n\n", SCORE_COOKIE);
8991 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
8992 fprintf(file, "%d %s\n", scores.entry[i].score, scores.entry[i].name);
8996 SetFilePermissions(filename, PERMS_PRIVATE);
9000 static void SaveScore_VERS(FILE *file, struct ScoreInfo *scores)
9002 putFileVersion(file, scores->file_version);
9003 putFileVersion(file, scores->game_version);
9006 static void SaveScore_INFO(FILE *file, struct ScoreInfo *scores)
9008 int level_identifier_size = strlen(scores->level_identifier) + 1;
9011 putFile16BitBE(file, level_identifier_size);
9013 for (i = 0; i < level_identifier_size; i++)
9014 putFile8Bit(file, scores->level_identifier[i]);
9016 putFile16BitBE(file, scores->level_nr);
9017 putFile16BitBE(file, scores->num_entries);
9020 static void SaveScore_NAME(FILE *file, struct ScoreInfo *scores)
9024 for (i = 0; i < scores->num_entries; i++)
9026 int name_size = strlen(scores->entry[i].name);
9028 for (j = 0; j < MAX_PLAYER_NAME_LEN; j++)
9029 putFile8Bit(file, (j < name_size ? scores->entry[i].name[j] : 0));
9033 static void SaveScore_SCOR(FILE *file, struct ScoreInfo *scores)
9037 for (i = 0; i < scores->num_entries; i++)
9038 putFile16BitBE(file, scores->entry[i].score);
9041 static void SaveScore_SC4R(FILE *file, struct ScoreInfo *scores)
9045 for (i = 0; i < scores->num_entries; i++)
9046 putFile32BitBE(file, scores->entry[i].score);
9049 static void SaveScore_TIME(FILE *file, struct ScoreInfo *scores)
9053 for (i = 0; i < scores->num_entries; i++)
9054 putFile32BitBE(file, scores->entry[i].time);
9057 static void SaveScore_TAPE(FILE *file, struct ScoreInfo *scores)
9061 for (i = 0; i < scores->num_entries; i++)
9063 int size = strlen(scores->entry[i].tape_basename);
9065 for (j = 0; j < MAX_SCORE_TAPE_BASENAME_LEN; j++)
9066 putFile8Bit(file, (j < size ? scores->entry[i].tape_basename[j] : 0));
9070 static void SaveScoreToFilename(char *filename)
9073 int info_chunk_size;
9074 int name_chunk_size;
9075 int scor_chunk_size;
9076 int sc4r_chunk_size;
9077 int time_chunk_size;
9078 int tape_chunk_size;
9079 boolean has_large_score_values;
9082 if (!(file = fopen(filename, MODE_WRITE)))
9084 Warn("cannot save score file '%s'", filename);
9089 info_chunk_size = 2 + (strlen(scores.level_identifier) + 1) + 2 + 2;
9090 name_chunk_size = scores.num_entries * MAX_PLAYER_NAME_LEN;
9091 scor_chunk_size = scores.num_entries * 2;
9092 sc4r_chunk_size = scores.num_entries * 4;
9093 time_chunk_size = scores.num_entries * 4;
9094 tape_chunk_size = scores.num_entries * MAX_SCORE_TAPE_BASENAME_LEN;
9096 has_large_score_values = FALSE;
9097 for (i = 0; i < scores.num_entries; i++)
9098 if (scores.entry[i].score > 0xffff)
9099 has_large_score_values = TRUE;
9101 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
9102 putFileChunkBE(file, "SCOR", CHUNK_SIZE_NONE);
9104 putFileChunkBE(file, "VERS", SCORE_CHUNK_VERS_SIZE);
9105 SaveScore_VERS(file, &scores);
9107 putFileChunkBE(file, "INFO", info_chunk_size);
9108 SaveScore_INFO(file, &scores);
9110 putFileChunkBE(file, "NAME", name_chunk_size);
9111 SaveScore_NAME(file, &scores);
9113 if (has_large_score_values)
9115 putFileChunkBE(file, "SC4R", sc4r_chunk_size);
9116 SaveScore_SC4R(file, &scores);
9120 putFileChunkBE(file, "SCOR", scor_chunk_size);
9121 SaveScore_SCOR(file, &scores);
9124 putFileChunkBE(file, "TIME", time_chunk_size);
9125 SaveScore_TIME(file, &scores);
9127 putFileChunkBE(file, "TAPE", tape_chunk_size);
9128 SaveScore_TAPE(file, &scores);
9132 SetFilePermissions(filename, PERMS_PRIVATE);
9135 void SaveScore(int nr)
9137 char *filename = getScoreFilename(nr);
9140 // used instead of "leveldir_current->subdir" (for network games)
9141 InitScoreDirectory(levelset.identifier);
9143 scores.file_version = FILE_VERSION_ACTUAL;
9144 scores.game_version = GAME_VERSION_ACTUAL;
9146 strncpy(scores.level_identifier, levelset.identifier, MAX_FILENAME_LEN);
9147 scores.level_identifier[MAX_FILENAME_LEN] = '\0';
9148 scores.level_nr = level_nr;
9150 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9151 if (scores.entry[i].score == 0 &&
9152 scores.entry[i].time == 0 &&
9153 strEqual(scores.entry[i].name, EMPTY_PLAYER_NAME))
9156 scores.num_entries = i;
9158 if (scores.num_entries == 0)
9161 SaveScoreToFilename(filename);
9164 void ExecuteAsThread(SDL_ThreadFunction function, char *name, void *data,
9167 #if defined(PLATFORM_EMSCRIPTEN)
9168 // threads currently not fully supported by Emscripten/SDL and some browsers
9171 SDL_Thread *thread = SDL_CreateThread(function, name, data);
9174 SDL_DetachThread(thread);
9176 Error("Cannot create thread to %s!", error);
9178 // nasty kludge to lower probability of intermingled thread error messages
9183 char *getPasswordJSON(char *password)
9185 static char password_json[MAX_FILENAME_LEN] = "";
9186 static boolean initialized = FALSE;
9190 if (password != NULL &&
9191 !strEqual(password, "") &&
9192 !strEqual(password, UNDEFINED_PASSWORD))
9193 snprintf(password_json, MAX_FILENAME_LEN,
9194 " \"password\": \"%s\",\n",
9195 setup.api_server_password);
9200 return password_json;
9203 struct ApiGetScoreThreadData
9206 char *score_cache_filename;
9209 static void *CreateThreadData_ApiGetScore(int nr)
9211 struct ApiGetScoreThreadData *data =
9212 checked_malloc(sizeof(struct ApiGetScoreThreadData));
9213 char *score_cache_filename = getScoreCacheFilename(nr);
9215 data->level_nr = nr;
9216 data->score_cache_filename = getStringCopy(score_cache_filename);
9221 static void FreeThreadData_ApiGetScore(void *data_raw)
9223 struct ApiGetScoreThreadData *data = data_raw;
9225 checked_free(data->score_cache_filename);
9229 static boolean SetRequest_ApiGetScore(struct HttpRequest *request,
9232 struct ApiGetScoreThreadData *data = data_raw;
9233 int level_nr = data->level_nr;
9235 request->hostname = setup.api_server_hostname;
9236 request->port = API_SERVER_PORT;
9237 request->method = API_SERVER_METHOD;
9238 request->uri = API_SERVER_URI_GET;
9240 char *levelset_identifier = getEscapedJSON(leveldir_current->identifier);
9241 char *levelset_name = getEscapedJSON(leveldir_current->name);
9243 snprintf(request->body, MAX_HTTP_BODY_SIZE,
9246 " \"game_version\": \"%s\",\n"
9247 " \"game_platform\": \"%s\",\n"
9248 " \"levelset_identifier\": \"%s\",\n"
9249 " \"levelset_name\": \"%s\",\n"
9250 " \"level_nr\": \"%d\"\n"
9252 getPasswordJSON(setup.api_server_password),
9253 getProgramRealVersionString(),
9254 getProgramPlatformString(),
9255 levelset_identifier,
9259 checked_free(levelset_identifier);
9260 checked_free(levelset_name);
9262 ConvertHttpRequestBodyToServerEncoding(request);
9267 static void HandleResponse_ApiGetScore(struct HttpResponse *response,
9270 struct ApiGetScoreThreadData *data = data_raw;
9272 if (response->body_size == 0)
9274 // no scores available for this level
9279 ConvertHttpResponseBodyToClientEncoding(response);
9281 char *filename = data->score_cache_filename;
9285 // used instead of "leveldir_current->subdir" (for network games)
9286 InitScoreCacheDirectory(levelset.identifier);
9288 if (!(file = fopen(filename, MODE_WRITE)))
9290 Warn("cannot save score cache file '%s'", filename);
9295 for (i = 0; i < response->body_size; i++)
9296 fputc(response->body[i], file);
9300 SetFilePermissions(filename, PERMS_PRIVATE);
9302 server_scores.updated = TRUE;
9305 #if defined(PLATFORM_EMSCRIPTEN)
9306 static void Emscripten_ApiGetScore_Loaded(unsigned handle, void *data_raw,
9307 void *buffer, unsigned int size)
9309 struct HttpResponse *response = GetHttpResponseFromBuffer(buffer, size);
9311 if (response != NULL)
9313 HandleResponse_ApiGetScore(response, data_raw);
9315 checked_free(response);
9319 Error("server response too large to handle (%d bytes)", size);
9322 FreeThreadData_ApiGetScore(data_raw);
9325 static void Emscripten_ApiGetScore_Failed(unsigned handle, void *data_raw,
9326 int code, const char *status)
9328 Error("server failed to handle request: %d %s", code, status);
9330 FreeThreadData_ApiGetScore(data_raw);
9333 static void Emscripten_ApiGetScore_Progress(unsigned handle, void *data_raw,
9334 int bytes, int size)
9336 // nothing to do here
9339 static void Emscripten_ApiGetScore_HttpRequest(struct HttpRequest *request,
9342 if (!SetRequest_ApiGetScore(request, data_raw))
9344 FreeThreadData_ApiGetScore(data_raw);
9349 emscripten_async_wget2_data(request->uri,
9354 Emscripten_ApiGetScore_Loaded,
9355 Emscripten_ApiGetScore_Failed,
9356 Emscripten_ApiGetScore_Progress);
9361 static void ApiGetScore_HttpRequestExt(struct HttpRequest *request,
9362 struct HttpResponse *response,
9365 if (!SetRequest_ApiGetScore(request, data_raw))
9368 if (!DoHttpRequest(request, response))
9370 Error("HTTP request failed: %s", GetHttpError());
9375 if (!HTTP_SUCCESS(response->status_code))
9377 // do not show error message if no scores found for this level set
9378 if (response->status_code == 404)
9381 Error("server failed to handle request: %d %s",
9382 response->status_code,
9383 response->status_text);
9388 HandleResponse_ApiGetScore(response, data_raw);
9391 static void ApiGetScore_HttpRequest(struct HttpRequest *request,
9392 struct HttpResponse *response,
9395 ApiGetScore_HttpRequestExt(request, response, data_raw);
9397 FreeThreadData_ApiGetScore(data_raw);
9401 static int ApiGetScoreThread(void *data_raw)
9403 struct HttpRequest *request = checked_calloc(sizeof(struct HttpRequest));
9404 struct HttpResponse *response = checked_calloc(sizeof(struct HttpResponse));
9406 program.api_thread_count++;
9408 #if defined(PLATFORM_EMSCRIPTEN)
9409 Emscripten_ApiGetScore_HttpRequest(request, data_raw);
9411 ApiGetScore_HttpRequest(request, response, data_raw);
9414 program.api_thread_count--;
9416 checked_free(request);
9417 checked_free(response);
9422 static void ApiGetScoreAsThread(int nr)
9424 struct ApiGetScoreThreadData *data = CreateThreadData_ApiGetScore(nr);
9426 ExecuteAsThread(ApiGetScoreThread,
9427 "ApiGetScore", data,
9428 "download scores from server");
9431 static void LoadServerScoreFromCache(int nr)
9433 struct ScoreEntry score_entry;
9442 { &score_entry.score, FALSE, 0 },
9443 { &score_entry.time, FALSE, 0 },
9444 { score_entry.name, TRUE, MAX_PLAYER_NAME_LEN },
9445 { score_entry.tape_basename, TRUE, MAX_FILENAME_LEN },
9449 char *filename = getScoreCacheFilename(nr);
9450 SetupFileHash *score_hash = loadSetupFileHash(filename);
9453 server_scores.num_entries = 0;
9455 if (score_hash == NULL)
9458 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9460 score_entry = server_scores.entry[i];
9462 for (j = 0; score_mapping[j].value != NULL; j++)
9466 sprintf(token, "%02d.%d", i, j);
9468 char *value = getHashEntry(score_hash, token);
9473 if (score_mapping[j].is_string)
9475 char *score_value = (char *)score_mapping[j].value;
9476 int value_size = score_mapping[j].string_size;
9478 strncpy(score_value, value, value_size);
9479 score_value[value_size] = '\0';
9483 int *score_value = (int *)score_mapping[j].value;
9485 *score_value = atoi(value);
9488 server_scores.num_entries = i + 1;
9491 server_scores.entry[i] = score_entry;
9494 freeSetupFileHash(score_hash);
9497 void LoadServerScore(int nr, boolean download_score)
9499 if (!setup.use_api_server)
9502 // always start with reliable default values
9503 setServerScoreInfoToDefaults();
9505 // 1st step: load server scores from cache file (which may not exist)
9506 // (this should prevent reading it while the thread is writing to it)
9507 LoadServerScoreFromCache(nr);
9509 if (download_score && runtime.use_api_server)
9511 // 2nd step: download server scores from score server to cache file
9512 // (as thread, as it might time out if the server is not reachable)
9513 ApiGetScoreAsThread(nr);
9517 static char *get_file_base64(char *filename)
9519 struct stat file_status;
9521 if (stat(filename, &file_status) != 0)
9523 Error("cannot stat file '%s'", filename);
9528 int buffer_size = file_status.st_size;
9529 byte *buffer = checked_malloc(buffer_size);
9533 if (!(file = fopen(filename, MODE_READ)))
9535 Error("cannot open file '%s'", filename);
9537 checked_free(buffer);
9542 for (i = 0; i < buffer_size; i++)
9544 int c = fgetc(file);
9548 Error("cannot read from input file '%s'", filename);
9551 checked_free(buffer);
9556 buffer[i] = (byte)c;
9561 int buffer_encoded_size = base64_encoded_size(buffer_size);
9562 char *buffer_encoded = checked_malloc(buffer_encoded_size);
9564 base64_encode(buffer_encoded, buffer, buffer_size);
9566 checked_free(buffer);
9568 return buffer_encoded;
9571 static void PrepareScoreTapesForUpload(char *leveldir_subdir)
9573 MarkTapeDirectoryUploadsAsIncomplete(leveldir_subdir);
9575 // if score tape not uploaded, ask for uploading missing tapes later
9576 if (!setup.has_remaining_tapes)
9577 setup.ask_for_remaining_tapes = TRUE;
9579 setup.provide_uploading_tapes = TRUE;
9580 setup.has_remaining_tapes = TRUE;
9582 SaveSetup_ServerSetup();
9585 struct ApiAddScoreThreadData
9589 char *leveldir_subdir;
9590 char *score_tape_filename;
9591 struct ScoreEntry score_entry;
9594 static void *CreateThreadData_ApiAddScore(int nr, boolean tape_saved,
9595 char *score_tape_filename)
9597 struct ApiAddScoreThreadData *data =
9598 checked_malloc(sizeof(struct ApiAddScoreThreadData));
9599 struct ScoreEntry *score_entry = &scores.entry[scores.last_added];
9601 if (score_tape_filename == NULL)
9602 score_tape_filename = getScoreTapeFilename(score_entry->tape_basename, nr);
9604 data->level_nr = nr;
9605 data->tape_saved = tape_saved;
9606 data->leveldir_subdir = getStringCopy(leveldir_current->subdir);
9607 data->score_tape_filename = getStringCopy(score_tape_filename);
9608 data->score_entry = *score_entry;
9613 static void FreeThreadData_ApiAddScore(void *data_raw)
9615 struct ApiAddScoreThreadData *data = data_raw;
9617 checked_free(data->leveldir_subdir);
9618 checked_free(data->score_tape_filename);
9622 static boolean SetRequest_ApiAddScore(struct HttpRequest *request,
9625 struct ApiAddScoreThreadData *data = data_raw;
9626 struct ScoreEntry *score_entry = &data->score_entry;
9627 char *score_tape_filename = data->score_tape_filename;
9628 boolean tape_saved = data->tape_saved;
9629 int level_nr = data->level_nr;
9631 request->hostname = setup.api_server_hostname;
9632 request->port = API_SERVER_PORT;
9633 request->method = API_SERVER_METHOD;
9634 request->uri = API_SERVER_URI_ADD;
9636 char *tape_base64 = get_file_base64(score_tape_filename);
9638 if (tape_base64 == NULL)
9640 Error("loading and base64 encoding score tape file failed");
9645 char *player_name_raw = score_entry->name;
9646 char *player_uuid_raw = setup.player_uuid;
9648 if (options.player_name != NULL && global.autoplay_leveldir != NULL)
9650 player_name_raw = options.player_name;
9651 player_uuid_raw = "";
9654 char *levelset_identifier = getEscapedJSON(leveldir_current->identifier);
9655 char *levelset_name = getEscapedJSON(leveldir_current->name);
9656 char *levelset_author = getEscapedJSON(leveldir_current->author);
9657 char *level_name = getEscapedJSON(level.name);
9658 char *level_author = getEscapedJSON(level.author);
9659 char *player_name = getEscapedJSON(player_name_raw);
9660 char *player_uuid = getEscapedJSON(player_uuid_raw);
9662 snprintf(request->body, MAX_HTTP_BODY_SIZE,
9665 " \"game_version\": \"%s\",\n"
9666 " \"game_platform\": \"%s\",\n"
9667 " \"batch_time\": \"%d\",\n"
9668 " \"levelset_identifier\": \"%s\",\n"
9669 " \"levelset_name\": \"%s\",\n"
9670 " \"levelset_author\": \"%s\",\n"
9671 " \"levelset_num_levels\": \"%d\",\n"
9672 " \"levelset_first_level\": \"%d\",\n"
9673 " \"level_nr\": \"%d\",\n"
9674 " \"level_name\": \"%s\",\n"
9675 " \"level_author\": \"%s\",\n"
9676 " \"use_step_counter\": \"%d\",\n"
9677 " \"rate_time_over_score\": \"%d\",\n"
9678 " \"player_name\": \"%s\",\n"
9679 " \"player_uuid\": \"%s\",\n"
9680 " \"score\": \"%d\",\n"
9681 " \"time\": \"%d\",\n"
9682 " \"tape_basename\": \"%s\",\n"
9683 " \"tape_saved\": \"%d\",\n"
9684 " \"tape\": \"%s\"\n"
9686 getPasswordJSON(setup.api_server_password),
9687 getProgramRealVersionString(),
9688 getProgramPlatformString(),
9689 (int)global.autoplay_time,
9690 levelset_identifier,
9693 leveldir_current->levels,
9694 leveldir_current->first_level,
9698 level.use_step_counter,
9699 level.rate_time_over_score,
9704 score_entry->tape_basename,
9708 checked_free(tape_base64);
9710 checked_free(levelset_identifier);
9711 checked_free(levelset_name);
9712 checked_free(levelset_author);
9713 checked_free(level_name);
9714 checked_free(level_author);
9715 checked_free(player_name);
9716 checked_free(player_uuid);
9718 ConvertHttpRequestBodyToServerEncoding(request);
9723 static void HandleResponse_ApiAddScore(struct HttpResponse *response,
9726 server_scores.uploaded = TRUE;
9729 static void HandleFailure_ApiAddScore(void *data_raw)
9731 struct ApiAddScoreThreadData *data = data_raw;
9733 PrepareScoreTapesForUpload(data->leveldir_subdir);
9736 #if defined(PLATFORM_EMSCRIPTEN)
9737 static void Emscripten_ApiAddScore_Loaded(unsigned handle, void *data_raw,
9738 void *buffer, unsigned int size)
9740 struct HttpResponse *response = GetHttpResponseFromBuffer(buffer, size);
9742 if (response != NULL)
9744 HandleResponse_ApiAddScore(response, data_raw);
9746 checked_free(response);
9750 Error("server response too large to handle (%d bytes)", size);
9752 HandleFailure_ApiAddScore(data_raw);
9755 FreeThreadData_ApiAddScore(data_raw);
9758 static void Emscripten_ApiAddScore_Failed(unsigned handle, void *data_raw,
9759 int code, const char *status)
9761 Error("server failed to handle request: %d %s", code, status);
9763 HandleFailure_ApiAddScore(data_raw);
9765 FreeThreadData_ApiAddScore(data_raw);
9768 static void Emscripten_ApiAddScore_Progress(unsigned handle, void *data_raw,
9769 int bytes, int size)
9771 // nothing to do here
9774 static void Emscripten_ApiAddScore_HttpRequest(struct HttpRequest *request,
9777 if (!SetRequest_ApiAddScore(request, data_raw))
9779 FreeThreadData_ApiAddScore(data_raw);
9784 emscripten_async_wget2_data(request->uri,
9789 Emscripten_ApiAddScore_Loaded,
9790 Emscripten_ApiAddScore_Failed,
9791 Emscripten_ApiAddScore_Progress);
9796 static void ApiAddScore_HttpRequestExt(struct HttpRequest *request,
9797 struct HttpResponse *response,
9800 if (!SetRequest_ApiAddScore(request, data_raw))
9803 if (!DoHttpRequest(request, response))
9805 Error("HTTP request failed: %s", GetHttpError());
9807 HandleFailure_ApiAddScore(data_raw);
9812 if (!HTTP_SUCCESS(response->status_code))
9814 Error("server failed to handle request: %d %s",
9815 response->status_code,
9816 response->status_text);
9818 HandleFailure_ApiAddScore(data_raw);
9823 HandleResponse_ApiAddScore(response, data_raw);
9826 static void ApiAddScore_HttpRequest(struct HttpRequest *request,
9827 struct HttpResponse *response,
9830 ApiAddScore_HttpRequestExt(request, response, data_raw);
9832 FreeThreadData_ApiAddScore(data_raw);
9836 static int ApiAddScoreThread(void *data_raw)
9838 struct HttpRequest *request = checked_calloc(sizeof(struct HttpRequest));
9839 struct HttpResponse *response = checked_calloc(sizeof(struct HttpResponse));
9841 program.api_thread_count++;
9843 #if defined(PLATFORM_EMSCRIPTEN)
9844 Emscripten_ApiAddScore_HttpRequest(request, data_raw);
9846 ApiAddScore_HttpRequest(request, response, data_raw);
9849 program.api_thread_count--;
9851 checked_free(request);
9852 checked_free(response);
9857 static void ApiAddScoreAsThread(int nr, boolean tape_saved,
9858 char *score_tape_filename)
9860 struct ApiAddScoreThreadData *data =
9861 CreateThreadData_ApiAddScore(nr, tape_saved, score_tape_filename);
9863 ExecuteAsThread(ApiAddScoreThread,
9864 "ApiAddScore", data,
9865 "upload score to server");
9868 void SaveServerScore(int nr, boolean tape_saved)
9870 if (!runtime.use_api_server)
9872 PrepareScoreTapesForUpload(leveldir_current->subdir);
9877 ApiAddScoreAsThread(nr, tape_saved, NULL);
9880 void SaveServerScoreFromFile(int nr, boolean tape_saved,
9881 char *score_tape_filename)
9883 if (!runtime.use_api_server)
9886 ApiAddScoreAsThread(nr, tape_saved, score_tape_filename);
9889 void LoadLocalAndServerScore(int nr, boolean download_score)
9891 int last_added_local = scores.last_added_local;
9893 // needed if only showing server scores
9894 setScoreInfoToDefaults();
9896 if (!strEqual(setup.scores_in_highscore_list, STR_SCORES_TYPE_SERVER_ONLY))
9899 // restore last added local score entry (before merging server scores)
9900 scores.last_added = scores.last_added_local = last_added_local;
9902 if (setup.use_api_server &&
9903 !strEqual(setup.scores_in_highscore_list, STR_SCORES_TYPE_LOCAL_ONLY))
9905 // load server scores from cache file and trigger update from server
9906 LoadServerScore(nr, download_score);
9908 // merge local scores with scores from server
9914 // ============================================================================
9915 // setup file functions
9916 // ============================================================================
9918 #define TOKEN_STR_PLAYER_PREFIX "player_"
9921 static struct TokenInfo global_setup_tokens[] =
9925 &setup.player_name, "player_name"
9929 &setup.multiple_users, "multiple_users"
9933 &setup.sound, "sound"
9937 &setup.sound_loops, "repeating_sound_loops"
9941 &setup.sound_music, "background_music"
9945 &setup.sound_simple, "simple_sound_effects"
9949 &setup.toons, "toons"
9953 &setup.scroll_delay, "scroll_delay"
9957 &setup.forced_scroll_delay, "forced_scroll_delay"
9961 &setup.scroll_delay_value, "scroll_delay_value"
9965 &setup.engine_snapshot_mode, "engine_snapshot_mode"
9969 &setup.engine_snapshot_memory, "engine_snapshot_memory"
9973 &setup.fade_screens, "fade_screens"
9977 &setup.autorecord, "automatic_tape_recording"
9981 &setup.auto_pause_on_start, "auto_pause_on_start"
9985 &setup.show_titlescreen, "show_titlescreen"
9989 &setup.quick_doors, "quick_doors"
9993 &setup.team_mode, "team_mode"
9997 &setup.handicap, "handicap"
10001 &setup.skip_levels, "skip_levels"
10005 &setup.increment_levels, "increment_levels"
10009 &setup.auto_play_next_level, "auto_play_next_level"
10013 &setup.count_score_after_game, "count_score_after_game"
10017 &setup.show_scores_after_game, "show_scores_after_game"
10021 &setup.time_limit, "time_limit"
10025 &setup.fullscreen, "fullscreen"
10029 &setup.window_scaling_percent, "window_scaling_percent"
10033 &setup.window_scaling_quality, "window_scaling_quality"
10037 &setup.screen_rendering_mode, "screen_rendering_mode"
10041 &setup.vsync_mode, "vsync_mode"
10045 &setup.ask_on_escape, "ask_on_escape"
10049 &setup.ask_on_escape_editor, "ask_on_escape_editor"
10053 &setup.ask_on_game_over, "ask_on_game_over"
10057 &setup.ask_on_quit_game, "ask_on_quit_game"
10061 &setup.ask_on_quit_program, "ask_on_quit_program"
10065 &setup.quick_switch, "quick_player_switch"
10069 &setup.input_on_focus, "input_on_focus"
10073 &setup.prefer_aga_graphics, "prefer_aga_graphics"
10077 &setup.prefer_lowpass_sounds, "prefer_lowpass_sounds"
10081 &setup.prefer_extra_panel_items, "prefer_extra_panel_items"
10085 &setup.game_speed_extended, "game_speed_extended"
10089 &setup.game_frame_delay, "game_frame_delay"
10093 &setup.sp_show_border_elements, "sp_show_border_elements"
10097 &setup.small_game_graphics, "small_game_graphics"
10101 &setup.show_load_save_buttons, "show_load_save_buttons"
10105 &setup.show_undo_redo_buttons, "show_undo_redo_buttons"
10109 &setup.scores_in_highscore_list, "scores_in_highscore_list"
10113 &setup.graphics_set, "graphics_set"
10117 &setup.sounds_set, "sounds_set"
10121 &setup.music_set, "music_set"
10125 &setup.override_level_graphics, "override_level_graphics"
10129 &setup.override_level_sounds, "override_level_sounds"
10133 &setup.override_level_music, "override_level_music"
10137 &setup.volume_simple, "volume_simple"
10141 &setup.volume_loops, "volume_loops"
10145 &setup.volume_music, "volume_music"
10149 &setup.network_mode, "network_mode"
10153 &setup.network_player_nr, "network_player"
10157 &setup.network_server_hostname, "network_server_hostname"
10161 &setup.touch.control_type, "touch.control_type"
10165 &setup.touch.move_distance, "touch.move_distance"
10169 &setup.touch.drop_distance, "touch.drop_distance"
10173 &setup.touch.transparency, "touch.transparency"
10177 &setup.touch.draw_outlined, "touch.draw_outlined"
10181 &setup.touch.draw_pressed, "touch.draw_pressed"
10185 &setup.touch.grid_xsize[0], "touch.virtual_buttons.0.xsize"
10189 &setup.touch.grid_ysize[0], "touch.virtual_buttons.0.ysize"
10193 &setup.touch.grid_xsize[1], "touch.virtual_buttons.1.xsize"
10197 &setup.touch.grid_ysize[1], "touch.virtual_buttons.1.ysize"
10201 static struct TokenInfo auto_setup_tokens[] =
10205 &setup.auto_setup.editor_zoom_tilesize, "editor.zoom_tilesize"
10209 static struct TokenInfo server_setup_tokens[] =
10213 &setup.player_uuid, "player_uuid"
10217 &setup.player_version, "player_version"
10221 &setup.use_api_server, TEST_PREFIX "use_api_server"
10225 &setup.api_server_hostname, TEST_PREFIX "api_server_hostname"
10229 &setup.api_server_password, TEST_PREFIX "api_server_password"
10233 &setup.ask_for_uploading_tapes, TEST_PREFIX "ask_for_uploading_tapes"
10237 &setup.ask_for_remaining_tapes, TEST_PREFIX "ask_for_remaining_tapes"
10241 &setup.provide_uploading_tapes, TEST_PREFIX "provide_uploading_tapes"
10245 &setup.ask_for_using_api_server,TEST_PREFIX "ask_for_using_api_server"
10249 &setup.has_remaining_tapes, TEST_PREFIX "has_remaining_tapes"
10253 static struct TokenInfo editor_setup_tokens[] =
10257 &setup.editor.el_classic, "editor.el_classic"
10261 &setup.editor.el_custom, "editor.el_custom"
10265 &setup.editor.el_user_defined, "editor.el_user_defined"
10269 &setup.editor.el_dynamic, "editor.el_dynamic"
10273 &setup.editor.el_headlines, "editor.el_headlines"
10277 &setup.editor.show_element_token, "editor.show_element_token"
10281 &setup.editor.show_read_only_warning, "editor.show_read_only_warning"
10285 static struct TokenInfo editor_cascade_setup_tokens[] =
10289 &setup.editor_cascade.el_bd, "editor.cascade.el_bd"
10293 &setup.editor_cascade.el_em, "editor.cascade.el_em"
10297 &setup.editor_cascade.el_emc, "editor.cascade.el_emc"
10301 &setup.editor_cascade.el_rnd, "editor.cascade.el_rnd"
10305 &setup.editor_cascade.el_sb, "editor.cascade.el_sb"
10309 &setup.editor_cascade.el_sp, "editor.cascade.el_sp"
10313 &setup.editor_cascade.el_dc, "editor.cascade.el_dc"
10317 &setup.editor_cascade.el_dx, "editor.cascade.el_dx"
10321 &setup.editor_cascade.el_mm, "editor.cascade.el_mm"
10325 &setup.editor_cascade.el_df, "editor.cascade.el_df"
10329 &setup.editor_cascade.el_chars, "editor.cascade.el_chars"
10333 &setup.editor_cascade.el_steel_chars, "editor.cascade.el_steel_chars"
10337 &setup.editor_cascade.el_ce, "editor.cascade.el_ce"
10341 &setup.editor_cascade.el_ge, "editor.cascade.el_ge"
10345 &setup.editor_cascade.el_es, "editor.cascade.el_es"
10349 &setup.editor_cascade.el_ref, "editor.cascade.el_ref"
10353 &setup.editor_cascade.el_user, "editor.cascade.el_user"
10357 &setup.editor_cascade.el_dynamic, "editor.cascade.el_dynamic"
10361 static struct TokenInfo shortcut_setup_tokens[] =
10365 &setup.shortcut.save_game, "shortcut.save_game"
10369 &setup.shortcut.load_game, "shortcut.load_game"
10373 &setup.shortcut.restart_game, "shortcut.restart_game"
10377 &setup.shortcut.pause_before_end, "shortcut.pause_before_end"
10381 &setup.shortcut.toggle_pause, "shortcut.toggle_pause"
10385 &setup.shortcut.focus_player[0], "shortcut.focus_player_1"
10389 &setup.shortcut.focus_player[1], "shortcut.focus_player_2"
10393 &setup.shortcut.focus_player[2], "shortcut.focus_player_3"
10397 &setup.shortcut.focus_player[3], "shortcut.focus_player_4"
10401 &setup.shortcut.focus_player_all, "shortcut.focus_player_all"
10405 &setup.shortcut.tape_eject, "shortcut.tape_eject"
10409 &setup.shortcut.tape_extra, "shortcut.tape_extra"
10413 &setup.shortcut.tape_stop, "shortcut.tape_stop"
10417 &setup.shortcut.tape_pause, "shortcut.tape_pause"
10421 &setup.shortcut.tape_record, "shortcut.tape_record"
10425 &setup.shortcut.tape_play, "shortcut.tape_play"
10429 &setup.shortcut.sound_simple, "shortcut.sound_simple"
10433 &setup.shortcut.sound_loops, "shortcut.sound_loops"
10437 &setup.shortcut.sound_music, "shortcut.sound_music"
10441 &setup.shortcut.snap_left, "shortcut.snap_left"
10445 &setup.shortcut.snap_right, "shortcut.snap_right"
10449 &setup.shortcut.snap_up, "shortcut.snap_up"
10453 &setup.shortcut.snap_down, "shortcut.snap_down"
10457 static struct SetupInputInfo setup_input;
10458 static struct TokenInfo player_setup_tokens[] =
10462 &setup_input.use_joystick, ".use_joystick"
10466 &setup_input.joy.device_name, ".joy.device_name"
10470 &setup_input.joy.xleft, ".joy.xleft"
10474 &setup_input.joy.xmiddle, ".joy.xmiddle"
10478 &setup_input.joy.xright, ".joy.xright"
10482 &setup_input.joy.yupper, ".joy.yupper"
10486 &setup_input.joy.ymiddle, ".joy.ymiddle"
10490 &setup_input.joy.ylower, ".joy.ylower"
10494 &setup_input.joy.snap, ".joy.snap_field"
10498 &setup_input.joy.drop, ".joy.place_bomb"
10502 &setup_input.key.left, ".key.move_left"
10506 &setup_input.key.right, ".key.move_right"
10510 &setup_input.key.up, ".key.move_up"
10514 &setup_input.key.down, ".key.move_down"
10518 &setup_input.key.snap, ".key.snap_field"
10522 &setup_input.key.drop, ".key.place_bomb"
10526 static struct TokenInfo system_setup_tokens[] =
10530 &setup.system.sdl_renderdriver, "system.sdl_renderdriver"
10534 &setup.system.sdl_videodriver, "system.sdl_videodriver"
10538 &setup.system.sdl_audiodriver, "system.sdl_audiodriver"
10542 &setup.system.audio_fragment_size, "system.audio_fragment_size"
10546 static struct TokenInfo internal_setup_tokens[] =
10550 &setup.internal.program_title, "program_title"
10554 &setup.internal.program_version, "program_version"
10558 &setup.internal.program_author, "program_author"
10562 &setup.internal.program_email, "program_email"
10566 &setup.internal.program_website, "program_website"
10570 &setup.internal.program_copyright, "program_copyright"
10574 &setup.internal.program_company, "program_company"
10578 &setup.internal.program_icon_file, "program_icon_file"
10582 &setup.internal.default_graphics_set, "default_graphics_set"
10586 &setup.internal.default_sounds_set, "default_sounds_set"
10590 &setup.internal.default_music_set, "default_music_set"
10594 &setup.internal.fallback_graphics_file, "fallback_graphics_file"
10598 &setup.internal.fallback_sounds_file, "fallback_sounds_file"
10602 &setup.internal.fallback_music_file, "fallback_music_file"
10606 &setup.internal.default_level_series, "default_level_series"
10610 &setup.internal.default_window_width, "default_window_width"
10614 &setup.internal.default_window_height, "default_window_height"
10618 &setup.internal.choose_from_top_leveldir, "choose_from_top_leveldir"
10622 &setup.internal.show_scaling_in_title, "show_scaling_in_title"
10626 &setup.internal.create_user_levelset, "create_user_levelset"
10630 &setup.internal.menu_game, "menu_game"
10634 &setup.internal.menu_editor, "menu_editor"
10638 &setup.internal.menu_graphics, "menu_graphics"
10642 &setup.internal.menu_sound, "menu_sound"
10646 &setup.internal.menu_artwork, "menu_artwork"
10650 &setup.internal.menu_input, "menu_input"
10654 &setup.internal.menu_touch, "menu_touch"
10658 &setup.internal.menu_shortcuts, "menu_shortcuts"
10662 &setup.internal.menu_exit, "menu_exit"
10666 &setup.internal.menu_save_and_exit, "menu_save_and_exit"
10670 static struct TokenInfo debug_setup_tokens[] =
10674 &setup.debug.frame_delay[0], "debug.frame_delay_0"
10678 &setup.debug.frame_delay[1], "debug.frame_delay_1"
10682 &setup.debug.frame_delay[2], "debug.frame_delay_2"
10686 &setup.debug.frame_delay[3], "debug.frame_delay_3"
10690 &setup.debug.frame_delay[4], "debug.frame_delay_4"
10694 &setup.debug.frame_delay[5], "debug.frame_delay_5"
10698 &setup.debug.frame_delay[6], "debug.frame_delay_6"
10702 &setup.debug.frame_delay[7], "debug.frame_delay_7"
10706 &setup.debug.frame_delay[8], "debug.frame_delay_8"
10710 &setup.debug.frame_delay[9], "debug.frame_delay_9"
10714 &setup.debug.frame_delay_key[0], "debug.key.frame_delay_0"
10718 &setup.debug.frame_delay_key[1], "debug.key.frame_delay_1"
10722 &setup.debug.frame_delay_key[2], "debug.key.frame_delay_2"
10726 &setup.debug.frame_delay_key[3], "debug.key.frame_delay_3"
10730 &setup.debug.frame_delay_key[4], "debug.key.frame_delay_4"
10734 &setup.debug.frame_delay_key[5], "debug.key.frame_delay_5"
10738 &setup.debug.frame_delay_key[6], "debug.key.frame_delay_6"
10742 &setup.debug.frame_delay_key[7], "debug.key.frame_delay_7"
10746 &setup.debug.frame_delay_key[8], "debug.key.frame_delay_8"
10750 &setup.debug.frame_delay_key[9], "debug.key.frame_delay_9"
10754 &setup.debug.frame_delay_use_mod_key, "debug.frame_delay.use_mod_key"},
10757 &setup.debug.frame_delay_game_only, "debug.frame_delay.game_only"
10761 &setup.debug.show_frames_per_second, "debug.show_frames_per_second"
10765 &setup.debug.xsn_mode, "debug.xsn_mode"
10769 &setup.debug.xsn_percent, "debug.xsn_percent"
10773 static struct TokenInfo options_setup_tokens[] =
10777 &setup.options.verbose, "options.verbose"
10781 static void setSetupInfoToDefaults(struct SetupInfo *si)
10785 si->player_name = getStringCopy(getDefaultUserName(user.nr));
10787 si->multiple_users = TRUE;
10790 si->sound_loops = TRUE;
10791 si->sound_music = TRUE;
10792 si->sound_simple = TRUE;
10794 si->scroll_delay = TRUE;
10795 si->forced_scroll_delay = FALSE;
10796 si->scroll_delay_value = STD_SCROLL_DELAY;
10797 si->engine_snapshot_mode = getStringCopy(STR_SNAPSHOT_MODE_DEFAULT);
10798 si->engine_snapshot_memory = SNAPSHOT_MEMORY_DEFAULT;
10799 si->fade_screens = TRUE;
10800 si->autorecord = TRUE;
10801 si->auto_pause_on_start = FALSE;
10802 si->show_titlescreen = TRUE;
10803 si->quick_doors = FALSE;
10804 si->team_mode = FALSE;
10805 si->handicap = TRUE;
10806 si->skip_levels = TRUE;
10807 si->increment_levels = TRUE;
10808 si->auto_play_next_level = TRUE;
10809 si->count_score_after_game = TRUE;
10810 si->show_scores_after_game = TRUE;
10811 si->time_limit = TRUE;
10812 si->fullscreen = FALSE;
10813 si->window_scaling_percent = STD_WINDOW_SCALING_PERCENT;
10814 si->window_scaling_quality = getStringCopy(SCALING_QUALITY_DEFAULT);
10815 si->screen_rendering_mode = getStringCopy(STR_SPECIAL_RENDERING_DEFAULT);
10816 si->vsync_mode = getStringCopy(STR_VSYNC_MODE_DEFAULT);
10817 si->ask_on_escape = TRUE;
10818 si->ask_on_escape_editor = TRUE;
10819 si->ask_on_game_over = TRUE;
10820 si->ask_on_quit_game = TRUE;
10821 si->ask_on_quit_program = TRUE;
10822 si->quick_switch = FALSE;
10823 si->input_on_focus = FALSE;
10824 si->prefer_aga_graphics = TRUE;
10825 si->prefer_lowpass_sounds = FALSE;
10826 si->prefer_extra_panel_items = TRUE;
10827 si->game_speed_extended = FALSE;
10828 si->game_frame_delay = GAME_FRAME_DELAY;
10829 si->sp_show_border_elements = FALSE;
10830 si->small_game_graphics = FALSE;
10831 si->show_load_save_buttons = FALSE;
10832 si->show_undo_redo_buttons = FALSE;
10833 si->scores_in_highscore_list = getStringCopy(STR_SCORES_TYPE_DEFAULT);
10835 si->graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
10836 si->sounds_set = getStringCopy(SND_CLASSIC_SUBDIR);
10837 si->music_set = getStringCopy(MUS_CLASSIC_SUBDIR);
10839 si->override_level_graphics = FALSE;
10840 si->override_level_sounds = FALSE;
10841 si->override_level_music = FALSE;
10843 si->volume_simple = 100; // percent
10844 si->volume_loops = 100; // percent
10845 si->volume_music = 100; // percent
10847 si->network_mode = FALSE;
10848 si->network_player_nr = 0; // first player
10849 si->network_server_hostname = getStringCopy(STR_NETWORK_AUTO_DETECT);
10851 si->touch.control_type = getStringCopy(TOUCH_CONTROL_DEFAULT);
10852 si->touch.move_distance = TOUCH_MOVE_DISTANCE_DEFAULT; // percent
10853 si->touch.drop_distance = TOUCH_DROP_DISTANCE_DEFAULT; // percent
10854 si->touch.transparency = TOUCH_TRANSPARENCY_DEFAULT; // percent
10855 si->touch.draw_outlined = TRUE;
10856 si->touch.draw_pressed = TRUE;
10858 for (i = 0; i < 2; i++)
10860 char *default_grid_button[6][2] =
10866 { "111222", " vv " },
10867 { "111222", " vv " }
10869 int grid_xsize = DEFAULT_GRID_XSIZE(i);
10870 int grid_ysize = DEFAULT_GRID_YSIZE(i);
10871 int min_xsize = MIN(6, grid_xsize);
10872 int min_ysize = MIN(6, grid_ysize);
10873 int startx = grid_xsize - min_xsize;
10874 int starty = grid_ysize - min_ysize;
10877 // virtual buttons grid can only be set to defaults if video is initialized
10878 // (this will be repeated if virtual buttons are not loaded from setup file)
10879 if (video.initialized)
10881 si->touch.grid_xsize[i] = grid_xsize;
10882 si->touch.grid_ysize[i] = grid_ysize;
10886 si->touch.grid_xsize[i] = -1;
10887 si->touch.grid_ysize[i] = -1;
10890 for (x = 0; x < MAX_GRID_XSIZE; x++)
10891 for (y = 0; y < MAX_GRID_YSIZE; y++)
10892 si->touch.grid_button[i][x][y] = CHAR_GRID_BUTTON_NONE;
10894 for (x = 0; x < min_xsize; x++)
10895 for (y = 0; y < min_ysize; y++)
10896 si->touch.grid_button[i][x][starty + y] =
10897 default_grid_button[y][0][x];
10899 for (x = 0; x < min_xsize; x++)
10900 for (y = 0; y < min_ysize; y++)
10901 si->touch.grid_button[i][startx + x][starty + y] =
10902 default_grid_button[y][1][x];
10905 si->touch.grid_initialized = video.initialized;
10907 si->editor.el_boulderdash = TRUE;
10908 si->editor.el_emerald_mine = TRUE;
10909 si->editor.el_emerald_mine_club = TRUE;
10910 si->editor.el_more = TRUE;
10911 si->editor.el_sokoban = TRUE;
10912 si->editor.el_supaplex = TRUE;
10913 si->editor.el_diamond_caves = TRUE;
10914 si->editor.el_dx_boulderdash = TRUE;
10916 si->editor.el_mirror_magic = TRUE;
10917 si->editor.el_deflektor = TRUE;
10919 si->editor.el_chars = TRUE;
10920 si->editor.el_steel_chars = TRUE;
10922 si->editor.el_classic = TRUE;
10923 si->editor.el_custom = TRUE;
10925 si->editor.el_user_defined = FALSE;
10926 si->editor.el_dynamic = TRUE;
10928 si->editor.el_headlines = TRUE;
10930 si->editor.show_element_token = FALSE;
10932 si->editor.show_read_only_warning = TRUE;
10934 si->editor.use_template_for_new_levels = TRUE;
10936 si->shortcut.save_game = DEFAULT_KEY_SAVE_GAME;
10937 si->shortcut.load_game = DEFAULT_KEY_LOAD_GAME;
10938 si->shortcut.restart_game = DEFAULT_KEY_RESTART_GAME;
10939 si->shortcut.pause_before_end = DEFAULT_KEY_PAUSE_BEFORE_END;
10940 si->shortcut.toggle_pause = DEFAULT_KEY_TOGGLE_PAUSE;
10942 si->shortcut.focus_player[0] = DEFAULT_KEY_FOCUS_PLAYER_1;
10943 si->shortcut.focus_player[1] = DEFAULT_KEY_FOCUS_PLAYER_2;
10944 si->shortcut.focus_player[2] = DEFAULT_KEY_FOCUS_PLAYER_3;
10945 si->shortcut.focus_player[3] = DEFAULT_KEY_FOCUS_PLAYER_4;
10946 si->shortcut.focus_player_all = DEFAULT_KEY_FOCUS_PLAYER_ALL;
10948 si->shortcut.tape_eject = DEFAULT_KEY_TAPE_EJECT;
10949 si->shortcut.tape_extra = DEFAULT_KEY_TAPE_EXTRA;
10950 si->shortcut.tape_stop = DEFAULT_KEY_TAPE_STOP;
10951 si->shortcut.tape_pause = DEFAULT_KEY_TAPE_PAUSE;
10952 si->shortcut.tape_record = DEFAULT_KEY_TAPE_RECORD;
10953 si->shortcut.tape_play = DEFAULT_KEY_TAPE_PLAY;
10955 si->shortcut.sound_simple = DEFAULT_KEY_SOUND_SIMPLE;
10956 si->shortcut.sound_loops = DEFAULT_KEY_SOUND_LOOPS;
10957 si->shortcut.sound_music = DEFAULT_KEY_SOUND_MUSIC;
10959 si->shortcut.snap_left = DEFAULT_KEY_SNAP_LEFT;
10960 si->shortcut.snap_right = DEFAULT_KEY_SNAP_RIGHT;
10961 si->shortcut.snap_up = DEFAULT_KEY_SNAP_UP;
10962 si->shortcut.snap_down = DEFAULT_KEY_SNAP_DOWN;
10964 for (i = 0; i < MAX_PLAYERS; i++)
10966 si->input[i].use_joystick = FALSE;
10967 si->input[i].joy.device_name=getStringCopy(getDeviceNameFromJoystickNr(i));
10968 si->input[i].joy.xleft = JOYSTICK_XLEFT;
10969 si->input[i].joy.xmiddle = JOYSTICK_XMIDDLE;
10970 si->input[i].joy.xright = JOYSTICK_XRIGHT;
10971 si->input[i].joy.yupper = JOYSTICK_YUPPER;
10972 si->input[i].joy.ymiddle = JOYSTICK_YMIDDLE;
10973 si->input[i].joy.ylower = JOYSTICK_YLOWER;
10974 si->input[i].joy.snap = (i == 0 ? JOY_BUTTON_1 : 0);
10975 si->input[i].joy.drop = (i == 0 ? JOY_BUTTON_2 : 0);
10976 si->input[i].key.left = (i == 0 ? DEFAULT_KEY_LEFT : KSYM_UNDEFINED);
10977 si->input[i].key.right = (i == 0 ? DEFAULT_KEY_RIGHT : KSYM_UNDEFINED);
10978 si->input[i].key.up = (i == 0 ? DEFAULT_KEY_UP : KSYM_UNDEFINED);
10979 si->input[i].key.down = (i == 0 ? DEFAULT_KEY_DOWN : KSYM_UNDEFINED);
10980 si->input[i].key.snap = (i == 0 ? DEFAULT_KEY_SNAP : KSYM_UNDEFINED);
10981 si->input[i].key.drop = (i == 0 ? DEFAULT_KEY_DROP : KSYM_UNDEFINED);
10984 si->system.sdl_renderdriver = getStringCopy(ARG_DEFAULT);
10985 si->system.sdl_videodriver = getStringCopy(ARG_DEFAULT);
10986 si->system.sdl_audiodriver = getStringCopy(ARG_DEFAULT);
10987 si->system.audio_fragment_size = DEFAULT_AUDIO_FRAGMENT_SIZE;
10989 si->internal.program_title = getStringCopy(PROGRAM_TITLE_STRING);
10990 si->internal.program_version = getStringCopy(getProgramRealVersionString());
10991 si->internal.program_author = getStringCopy(PROGRAM_AUTHOR_STRING);
10992 si->internal.program_email = getStringCopy(PROGRAM_EMAIL_STRING);
10993 si->internal.program_website = getStringCopy(PROGRAM_WEBSITE_STRING);
10994 si->internal.program_copyright = getStringCopy(PROGRAM_COPYRIGHT_STRING);
10995 si->internal.program_company = getStringCopy(PROGRAM_COMPANY_STRING);
10997 si->internal.program_icon_file = getStringCopy(PROGRAM_ICON_FILENAME);
10999 si->internal.default_graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
11000 si->internal.default_sounds_set = getStringCopy(SND_CLASSIC_SUBDIR);
11001 si->internal.default_music_set = getStringCopy(MUS_CLASSIC_SUBDIR);
11003 si->internal.fallback_graphics_file = getStringCopy(UNDEFINED_FILENAME);
11004 si->internal.fallback_sounds_file = getStringCopy(UNDEFINED_FILENAME);
11005 si->internal.fallback_music_file = getStringCopy(UNDEFINED_FILENAME);
11007 si->internal.default_level_series = getStringCopy(UNDEFINED_LEVELSET);
11008 si->internal.choose_from_top_leveldir = FALSE;
11009 si->internal.show_scaling_in_title = TRUE;
11010 si->internal.create_user_levelset = TRUE;
11012 si->internal.default_window_width = WIN_XSIZE_DEFAULT;
11013 si->internal.default_window_height = WIN_YSIZE_DEFAULT;
11015 si->debug.frame_delay[0] = DEFAULT_FRAME_DELAY_0;
11016 si->debug.frame_delay[1] = DEFAULT_FRAME_DELAY_1;
11017 si->debug.frame_delay[2] = DEFAULT_FRAME_DELAY_2;
11018 si->debug.frame_delay[3] = DEFAULT_FRAME_DELAY_3;
11019 si->debug.frame_delay[4] = DEFAULT_FRAME_DELAY_4;
11020 si->debug.frame_delay[5] = DEFAULT_FRAME_DELAY_5;
11021 si->debug.frame_delay[6] = DEFAULT_FRAME_DELAY_6;
11022 si->debug.frame_delay[7] = DEFAULT_FRAME_DELAY_7;
11023 si->debug.frame_delay[8] = DEFAULT_FRAME_DELAY_8;
11024 si->debug.frame_delay[9] = DEFAULT_FRAME_DELAY_9;
11026 si->debug.frame_delay_key[0] = DEFAULT_KEY_FRAME_DELAY_0;
11027 si->debug.frame_delay_key[1] = DEFAULT_KEY_FRAME_DELAY_1;
11028 si->debug.frame_delay_key[2] = DEFAULT_KEY_FRAME_DELAY_2;
11029 si->debug.frame_delay_key[3] = DEFAULT_KEY_FRAME_DELAY_3;
11030 si->debug.frame_delay_key[4] = DEFAULT_KEY_FRAME_DELAY_4;
11031 si->debug.frame_delay_key[5] = DEFAULT_KEY_FRAME_DELAY_5;
11032 si->debug.frame_delay_key[6] = DEFAULT_KEY_FRAME_DELAY_6;
11033 si->debug.frame_delay_key[7] = DEFAULT_KEY_FRAME_DELAY_7;
11034 si->debug.frame_delay_key[8] = DEFAULT_KEY_FRAME_DELAY_8;
11035 si->debug.frame_delay_key[9] = DEFAULT_KEY_FRAME_DELAY_9;
11037 si->debug.frame_delay_use_mod_key = DEFAULT_FRAME_DELAY_USE_MOD_KEY;
11038 si->debug.frame_delay_game_only = DEFAULT_FRAME_DELAY_GAME_ONLY;
11040 si->debug.show_frames_per_second = FALSE;
11042 si->debug.xsn_mode = AUTO;
11043 si->debug.xsn_percent = 0;
11045 si->options.verbose = FALSE;
11047 #if defined(PLATFORM_ANDROID)
11048 si->fullscreen = TRUE;
11051 setHideSetupEntry(&setup.debug.xsn_mode);
11054 static void setSetupInfoToDefaults_AutoSetup(struct SetupInfo *si)
11056 si->auto_setup.editor_zoom_tilesize = MINI_TILESIZE;
11059 static void setSetupInfoToDefaults_ServerSetup(struct SetupInfo *si)
11061 si->player_uuid = NULL; // (will be set later)
11062 si->player_version = 1; // (will be set later)
11064 si->use_api_server = TRUE;
11065 si->api_server_hostname = getStringCopy(API_SERVER_HOSTNAME);
11066 si->api_server_password = getStringCopy(UNDEFINED_PASSWORD);
11067 si->ask_for_uploading_tapes = TRUE;
11068 si->ask_for_remaining_tapes = FALSE;
11069 si->provide_uploading_tapes = TRUE;
11070 si->ask_for_using_api_server = TRUE;
11071 si->has_remaining_tapes = FALSE;
11074 static void setSetupInfoToDefaults_EditorCascade(struct SetupInfo *si)
11076 si->editor_cascade.el_bd = TRUE;
11077 si->editor_cascade.el_em = TRUE;
11078 si->editor_cascade.el_emc = TRUE;
11079 si->editor_cascade.el_rnd = TRUE;
11080 si->editor_cascade.el_sb = TRUE;
11081 si->editor_cascade.el_sp = TRUE;
11082 si->editor_cascade.el_dc = TRUE;
11083 si->editor_cascade.el_dx = TRUE;
11085 si->editor_cascade.el_mm = TRUE;
11086 si->editor_cascade.el_df = TRUE;
11088 si->editor_cascade.el_chars = FALSE;
11089 si->editor_cascade.el_steel_chars = FALSE;
11090 si->editor_cascade.el_ce = FALSE;
11091 si->editor_cascade.el_ge = FALSE;
11092 si->editor_cascade.el_es = FALSE;
11093 si->editor_cascade.el_ref = FALSE;
11094 si->editor_cascade.el_user = FALSE;
11095 si->editor_cascade.el_dynamic = FALSE;
11098 #define MAX_HIDE_SETUP_TOKEN_SIZE 20
11100 static char *getHideSetupToken(void *setup_value)
11102 static char hide_setup_token[MAX_HIDE_SETUP_TOKEN_SIZE];
11104 if (setup_value != NULL)
11105 snprintf(hide_setup_token, MAX_HIDE_SETUP_TOKEN_SIZE, "%p", setup_value);
11107 return hide_setup_token;
11110 void setHideSetupEntry(void *setup_value)
11112 char *hide_setup_token = getHideSetupToken(setup_value);
11114 if (hide_setup_hash == NULL)
11115 hide_setup_hash = newSetupFileHash();
11117 if (setup_value != NULL)
11118 setHashEntry(hide_setup_hash, hide_setup_token, "");
11121 void removeHideSetupEntry(void *setup_value)
11123 char *hide_setup_token = getHideSetupToken(setup_value);
11125 if (setup_value != NULL)
11126 removeHashEntry(hide_setup_hash, hide_setup_token);
11129 boolean hideSetupEntry(void *setup_value)
11131 char *hide_setup_token = getHideSetupToken(setup_value);
11133 return (setup_value != NULL &&
11134 getHashEntry(hide_setup_hash, hide_setup_token) != NULL);
11137 static void setSetupInfoFromTokenText(SetupFileHash *setup_file_hash,
11138 struct TokenInfo *token_info,
11139 int token_nr, char *token_text)
11141 char *token_hide_text = getStringCat2(token_text, ".hide");
11142 char *token_hide_value = getHashEntry(setup_file_hash, token_hide_text);
11144 // set the value of this setup option in the setup option structure
11145 setSetupInfo(token_info, token_nr, getHashEntry(setup_file_hash, token_text));
11147 // check if this setup option should be hidden in the setup menu
11148 if (token_hide_value != NULL && get_boolean_from_string(token_hide_value))
11149 setHideSetupEntry(token_info[token_nr].value);
11151 free(token_hide_text);
11154 static void setSetupInfoFromTokenInfo(SetupFileHash *setup_file_hash,
11155 struct TokenInfo *token_info,
11158 setSetupInfoFromTokenText(setup_file_hash, token_info, token_nr,
11159 token_info[token_nr].text);
11162 static void decodeSetupFileHash_Default(SetupFileHash *setup_file_hash)
11166 if (!setup_file_hash)
11169 for (i = 0; i < ARRAY_SIZE(global_setup_tokens); i++)
11170 setSetupInfoFromTokenInfo(setup_file_hash, global_setup_tokens, i);
11172 setup.touch.grid_initialized = TRUE;
11173 for (i = 0; i < 2; i++)
11175 int grid_xsize = setup.touch.grid_xsize[i];
11176 int grid_ysize = setup.touch.grid_ysize[i];
11179 // if virtual buttons are not loaded from setup file, repeat initializing
11180 // virtual buttons grid with default values later when video is initialized
11181 if (grid_xsize == -1 ||
11184 setup.touch.grid_initialized = FALSE;
11189 for (y = 0; y < grid_ysize; y++)
11191 char token_string[MAX_LINE_LEN];
11193 sprintf(token_string, "touch.virtual_buttons.%d.%02d", i, y);
11195 char *value_string = getHashEntry(setup_file_hash, token_string);
11197 if (value_string == NULL)
11200 for (x = 0; x < grid_xsize; x++)
11202 char c = value_string[x];
11204 setup.touch.grid_button[i][x][y] =
11205 (c == '.' ? CHAR_GRID_BUTTON_NONE : c);
11210 for (i = 0; i < ARRAY_SIZE(editor_setup_tokens); i++)
11211 setSetupInfoFromTokenInfo(setup_file_hash, editor_setup_tokens, i);
11213 for (i = 0; i < ARRAY_SIZE(shortcut_setup_tokens); i++)
11214 setSetupInfoFromTokenInfo(setup_file_hash, shortcut_setup_tokens, i);
11216 for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
11220 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
11222 setup_input = setup.input[pnr];
11223 for (i = 0; i < ARRAY_SIZE(player_setup_tokens); i++)
11225 char full_token[100];
11227 sprintf(full_token, "%s%s", prefix, player_setup_tokens[i].text);
11228 setSetupInfoFromTokenText(setup_file_hash, player_setup_tokens, i,
11231 setup.input[pnr] = setup_input;
11234 for (i = 0; i < ARRAY_SIZE(system_setup_tokens); i++)
11235 setSetupInfoFromTokenInfo(setup_file_hash, system_setup_tokens, i);
11237 for (i = 0; i < ARRAY_SIZE(internal_setup_tokens); i++)
11238 setSetupInfoFromTokenInfo(setup_file_hash, internal_setup_tokens, i);
11240 for (i = 0; i < ARRAY_SIZE(debug_setup_tokens); i++)
11241 setSetupInfoFromTokenInfo(setup_file_hash, debug_setup_tokens, i);
11243 for (i = 0; i < ARRAY_SIZE(options_setup_tokens); i++)
11244 setSetupInfoFromTokenInfo(setup_file_hash, options_setup_tokens, i);
11246 setHideRelatedSetupEntries();
11249 static void decodeSetupFileHash_AutoSetup(SetupFileHash *setup_file_hash)
11253 if (!setup_file_hash)
11256 for (i = 0; i < ARRAY_SIZE(auto_setup_tokens); i++)
11257 setSetupInfo(auto_setup_tokens, i,
11258 getHashEntry(setup_file_hash,
11259 auto_setup_tokens[i].text));
11262 static void decodeSetupFileHash_ServerSetup(SetupFileHash *setup_file_hash)
11266 if (!setup_file_hash)
11269 for (i = 0; i < ARRAY_SIZE(server_setup_tokens); i++)
11270 setSetupInfo(server_setup_tokens, i,
11271 getHashEntry(setup_file_hash,
11272 server_setup_tokens[i].text));
11275 static void decodeSetupFileHash_EditorCascade(SetupFileHash *setup_file_hash)
11279 if (!setup_file_hash)
11282 for (i = 0; i < ARRAY_SIZE(editor_cascade_setup_tokens); i++)
11283 setSetupInfo(editor_cascade_setup_tokens, i,
11284 getHashEntry(setup_file_hash,
11285 editor_cascade_setup_tokens[i].text));
11288 void LoadUserNames(void)
11290 int last_user_nr = user.nr;
11293 if (global.user_names != NULL)
11295 for (i = 0; i < MAX_PLAYER_NAMES; i++)
11296 checked_free(global.user_names[i]);
11298 checked_free(global.user_names);
11301 global.user_names = checked_calloc(MAX_PLAYER_NAMES * sizeof(char *));
11303 for (i = 0; i < MAX_PLAYER_NAMES; i++)
11307 SetupFileHash *setup_file_hash = loadSetupFileHash(getSetupFilename());
11309 if (setup_file_hash)
11311 char *player_name = getHashEntry(setup_file_hash, "player_name");
11313 global.user_names[i] = getFixedUserName(player_name);
11315 freeSetupFileHash(setup_file_hash);
11318 if (global.user_names[i] == NULL)
11319 global.user_names[i] = getStringCopy(getDefaultUserName(i));
11322 user.nr = last_user_nr;
11325 void LoadSetupFromFilename(char *filename)
11327 SetupFileHash *setup_file_hash = loadSetupFileHash(filename);
11329 if (setup_file_hash)
11331 decodeSetupFileHash_Default(setup_file_hash);
11333 freeSetupFileHash(setup_file_hash);
11337 Debug("setup", "using default setup values");
11341 static void LoadSetup_SpecialPostProcessing(void)
11343 char *player_name_new;
11345 // needed to work around problems with fixed length strings
11346 player_name_new = getFixedUserName(setup.player_name);
11347 free(setup.player_name);
11348 setup.player_name = player_name_new;
11350 // "scroll_delay: on(3) / off(0)" was replaced by scroll delay value
11351 if (setup.scroll_delay == FALSE)
11353 setup.scroll_delay_value = MIN_SCROLL_DELAY;
11354 setup.scroll_delay = TRUE; // now always "on"
11357 // make sure that scroll delay value stays inside valid range
11358 setup.scroll_delay_value =
11359 MIN(MAX(MIN_SCROLL_DELAY, setup.scroll_delay_value), MAX_SCROLL_DELAY);
11362 void LoadSetup_Default(void)
11366 // always start with reliable default values
11367 setSetupInfoToDefaults(&setup);
11369 // try to load setup values from default setup file
11370 filename = getDefaultSetupFilename();
11372 if (fileExists(filename))
11373 LoadSetupFromFilename(filename);
11375 // try to load setup values from user setup file
11376 filename = getSetupFilename();
11378 LoadSetupFromFilename(filename);
11380 LoadSetup_SpecialPostProcessing();
11383 void LoadSetup_AutoSetup(void)
11385 char *filename = getPath2(getSetupDir(), AUTOSETUP_FILENAME);
11386 SetupFileHash *setup_file_hash = NULL;
11388 // always start with reliable default values
11389 setSetupInfoToDefaults_AutoSetup(&setup);
11391 setup_file_hash = loadSetupFileHash(filename);
11393 if (setup_file_hash)
11395 decodeSetupFileHash_AutoSetup(setup_file_hash);
11397 freeSetupFileHash(setup_file_hash);
11403 void LoadSetup_ServerSetup(void)
11405 char *filename = getPath2(getSetupDir(), SERVERSETUP_FILENAME);
11406 SetupFileHash *setup_file_hash = NULL;
11408 // always start with reliable default values
11409 setSetupInfoToDefaults_ServerSetup(&setup);
11411 setup_file_hash = loadSetupFileHash(filename);
11413 if (setup_file_hash)
11415 decodeSetupFileHash_ServerSetup(setup_file_hash);
11417 freeSetupFileHash(setup_file_hash);
11422 if (setup.player_uuid == NULL)
11424 // player UUID does not yet exist in setup file
11425 setup.player_uuid = getStringCopy(getUUID());
11426 setup.player_version = 2;
11428 SaveSetup_ServerSetup();
11432 void LoadSetup_EditorCascade(void)
11434 char *filename = getPath2(getSetupDir(), EDITORCASCADE_FILENAME);
11435 SetupFileHash *setup_file_hash = NULL;
11437 // always start with reliable default values
11438 setSetupInfoToDefaults_EditorCascade(&setup);
11440 setup_file_hash = loadSetupFileHash(filename);
11442 if (setup_file_hash)
11444 decodeSetupFileHash_EditorCascade(setup_file_hash);
11446 freeSetupFileHash(setup_file_hash);
11452 void LoadSetup(void)
11454 LoadSetup_Default();
11455 LoadSetup_AutoSetup();
11456 LoadSetup_ServerSetup();
11457 LoadSetup_EditorCascade();
11460 static void addGameControllerMappingToHash(SetupFileHash *mappings_hash,
11461 char *mapping_line)
11463 char mapping_guid[MAX_LINE_LEN];
11464 char *mapping_start, *mapping_end;
11466 // get GUID from game controller mapping line: copy complete line
11467 strncpy(mapping_guid, mapping_line, MAX_LINE_LEN - 1);
11468 mapping_guid[MAX_LINE_LEN - 1] = '\0';
11470 // get GUID from game controller mapping line: cut after GUID part
11471 mapping_start = strchr(mapping_guid, ',');
11472 if (mapping_start != NULL)
11473 *mapping_start = '\0';
11475 // cut newline from game controller mapping line
11476 mapping_end = strchr(mapping_line, '\n');
11477 if (mapping_end != NULL)
11478 *mapping_end = '\0';
11480 // add mapping entry to game controller mappings hash
11481 setHashEntry(mappings_hash, mapping_guid, mapping_line);
11484 static void LoadSetup_ReadGameControllerMappings(SetupFileHash *mappings_hash,
11489 if (!(file = fopen(filename, MODE_READ)))
11491 Warn("cannot read game controller mappings file '%s'", filename);
11496 while (!feof(file))
11498 char line[MAX_LINE_LEN];
11500 if (!fgets(line, MAX_LINE_LEN, file))
11503 addGameControllerMappingToHash(mappings_hash, line);
11509 void SaveSetup_Default(void)
11511 char *filename = getSetupFilename();
11515 InitUserDataDirectory();
11517 if (!(file = fopen(filename, MODE_WRITE)))
11519 Warn("cannot write setup file '%s'", filename);
11524 fprintFileHeader(file, SETUP_FILENAME);
11526 for (i = 0; i < ARRAY_SIZE(global_setup_tokens); i++)
11528 // just to make things nicer :)
11529 if (global_setup_tokens[i].value == &setup.multiple_users ||
11530 global_setup_tokens[i].value == &setup.sound ||
11531 global_setup_tokens[i].value == &setup.graphics_set ||
11532 global_setup_tokens[i].value == &setup.volume_simple ||
11533 global_setup_tokens[i].value == &setup.network_mode ||
11534 global_setup_tokens[i].value == &setup.touch.control_type ||
11535 global_setup_tokens[i].value == &setup.touch.grid_xsize[0] ||
11536 global_setup_tokens[i].value == &setup.touch.grid_xsize[1])
11537 fprintf(file, "\n");
11539 fprintf(file, "%s\n", getSetupLine(global_setup_tokens, "", i));
11542 for (i = 0; i < 2; i++)
11544 int grid_xsize = setup.touch.grid_xsize[i];
11545 int grid_ysize = setup.touch.grid_ysize[i];
11548 fprintf(file, "\n");
11550 for (y = 0; y < grid_ysize; y++)
11552 char token_string[MAX_LINE_LEN];
11553 char value_string[MAX_LINE_LEN];
11555 sprintf(token_string, "touch.virtual_buttons.%d.%02d", i, y);
11557 for (x = 0; x < grid_xsize; x++)
11559 char c = setup.touch.grid_button[i][x][y];
11561 value_string[x] = (c == CHAR_GRID_BUTTON_NONE ? '.' : c);
11564 value_string[grid_xsize] = '\0';
11566 fprintf(file, "%s\n", getFormattedSetupEntry(token_string, value_string));
11570 fprintf(file, "\n");
11571 for (i = 0; i < ARRAY_SIZE(editor_setup_tokens); i++)
11572 fprintf(file, "%s\n", getSetupLine(editor_setup_tokens, "", i));
11574 fprintf(file, "\n");
11575 for (i = 0; i < ARRAY_SIZE(shortcut_setup_tokens); i++)
11576 fprintf(file, "%s\n", getSetupLine(shortcut_setup_tokens, "", i));
11578 for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
11582 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
11583 fprintf(file, "\n");
11585 setup_input = setup.input[pnr];
11586 for (i = 0; i < ARRAY_SIZE(player_setup_tokens); i++)
11587 fprintf(file, "%s\n", getSetupLine(player_setup_tokens, prefix, i));
11590 fprintf(file, "\n");
11591 for (i = 0; i < ARRAY_SIZE(system_setup_tokens); i++)
11592 fprintf(file, "%s\n", getSetupLine(system_setup_tokens, "", i));
11594 // (internal setup values not saved to user setup file)
11596 fprintf(file, "\n");
11597 for (i = 0; i < ARRAY_SIZE(debug_setup_tokens); i++)
11598 if (!strPrefix(debug_setup_tokens[i].text, "debug.xsn_") ||
11599 setup.debug.xsn_mode != AUTO)
11600 fprintf(file, "%s\n", getSetupLine(debug_setup_tokens, "", i));
11602 fprintf(file, "\n");
11603 for (i = 0; i < ARRAY_SIZE(options_setup_tokens); i++)
11604 fprintf(file, "%s\n", getSetupLine(options_setup_tokens, "", i));
11608 SetFilePermissions(filename, PERMS_PRIVATE);
11611 void SaveSetup_AutoSetup(void)
11613 char *filename = getPath2(getSetupDir(), AUTOSETUP_FILENAME);
11617 InitUserDataDirectory();
11619 if (!(file = fopen(filename, MODE_WRITE)))
11621 Warn("cannot write auto setup file '%s'", filename);
11628 fprintFileHeader(file, AUTOSETUP_FILENAME);
11630 for (i = 0; i < ARRAY_SIZE(auto_setup_tokens); i++)
11631 fprintf(file, "%s\n", getSetupLine(auto_setup_tokens, "", i));
11635 SetFilePermissions(filename, PERMS_PRIVATE);
11640 void SaveSetup_ServerSetup(void)
11642 char *filename = getPath2(getSetupDir(), SERVERSETUP_FILENAME);
11646 InitUserDataDirectory();
11648 if (!(file = fopen(filename, MODE_WRITE)))
11650 Warn("cannot write server setup file '%s'", filename);
11657 fprintFileHeader(file, SERVERSETUP_FILENAME);
11659 for (i = 0; i < ARRAY_SIZE(server_setup_tokens); i++)
11661 // just to make things nicer :)
11662 if (server_setup_tokens[i].value == &setup.use_api_server)
11663 fprintf(file, "\n");
11665 fprintf(file, "%s\n", getSetupLine(server_setup_tokens, "", i));
11670 SetFilePermissions(filename, PERMS_PRIVATE);
11675 void SaveSetup_EditorCascade(void)
11677 char *filename = getPath2(getSetupDir(), EDITORCASCADE_FILENAME);
11681 InitUserDataDirectory();
11683 if (!(file = fopen(filename, MODE_WRITE)))
11685 Warn("cannot write editor cascade state file '%s'", filename);
11692 fprintFileHeader(file, EDITORCASCADE_FILENAME);
11694 for (i = 0; i < ARRAY_SIZE(editor_cascade_setup_tokens); i++)
11695 fprintf(file, "%s\n", getSetupLine(editor_cascade_setup_tokens, "", i));
11699 SetFilePermissions(filename, PERMS_PRIVATE);
11704 void SaveSetup(void)
11706 SaveSetup_Default();
11707 SaveSetup_AutoSetup();
11708 SaveSetup_ServerSetup();
11709 SaveSetup_EditorCascade();
11712 static void SaveSetup_WriteGameControllerMappings(SetupFileHash *mappings_hash,
11717 if (!(file = fopen(filename, MODE_WRITE)))
11719 Warn("cannot write game controller mappings file '%s'", filename);
11724 BEGIN_HASH_ITERATION(mappings_hash, itr)
11726 fprintf(file, "%s\n", HASH_ITERATION_VALUE(itr));
11728 END_HASH_ITERATION(mappings_hash, itr)
11733 void SaveSetup_AddGameControllerMapping(char *mapping)
11735 char *filename = getPath2(getSetupDir(), GAMECONTROLLER_BASENAME);
11736 SetupFileHash *mappings_hash = newSetupFileHash();
11738 InitUserDataDirectory();
11740 // load existing personal game controller mappings
11741 LoadSetup_ReadGameControllerMappings(mappings_hash, filename);
11743 // add new mapping to personal game controller mappings
11744 addGameControllerMappingToHash(mappings_hash, mapping);
11746 // save updated personal game controller mappings
11747 SaveSetup_WriteGameControllerMappings(mappings_hash, filename);
11749 freeSetupFileHash(mappings_hash);
11753 void LoadCustomElementDescriptions(void)
11755 char *filename = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
11756 SetupFileHash *setup_file_hash;
11759 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
11761 if (element_info[i].custom_description != NULL)
11763 free(element_info[i].custom_description);
11764 element_info[i].custom_description = NULL;
11768 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
11771 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
11773 char *token = getStringCat2(element_info[i].token_name, ".name");
11774 char *value = getHashEntry(setup_file_hash, token);
11777 element_info[i].custom_description = getStringCopy(value);
11782 freeSetupFileHash(setup_file_hash);
11785 static int getElementFromToken(char *token)
11787 char *value = getHashEntry(element_token_hash, token);
11790 return atoi(value);
11792 Warn("unknown element token '%s'", token);
11794 return EL_UNDEFINED;
11797 void FreeGlobalAnimEventInfo(void)
11799 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
11801 if (gaei->event_list == NULL)
11806 for (i = 0; i < gaei->num_event_lists; i++)
11808 checked_free(gaei->event_list[i]->event_value);
11809 checked_free(gaei->event_list[i]);
11812 checked_free(gaei->event_list);
11814 gaei->event_list = NULL;
11815 gaei->num_event_lists = 0;
11818 static int AddGlobalAnimEventList(void)
11820 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
11821 int list_pos = gaei->num_event_lists++;
11823 gaei->event_list = checked_realloc(gaei->event_list, gaei->num_event_lists *
11824 sizeof(struct GlobalAnimEventListInfo *));
11826 gaei->event_list[list_pos] =
11827 checked_calloc(sizeof(struct GlobalAnimEventListInfo));
11829 struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
11831 gaeli->event_value = NULL;
11832 gaeli->num_event_values = 0;
11837 static int AddGlobalAnimEventValue(int list_pos, int event_value)
11839 // do not add empty global animation events
11840 if (event_value == ANIM_EVENT_NONE)
11843 // if list position is undefined, create new list
11844 if (list_pos == ANIM_EVENT_UNDEFINED)
11845 list_pos = AddGlobalAnimEventList();
11847 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
11848 struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
11849 int value_pos = gaeli->num_event_values++;
11851 gaeli->event_value = checked_realloc(gaeli->event_value,
11852 gaeli->num_event_values * sizeof(int *));
11854 gaeli->event_value[value_pos] = event_value;
11859 int GetGlobalAnimEventValue(int list_pos, int value_pos)
11861 if (list_pos == ANIM_EVENT_UNDEFINED)
11862 return ANIM_EVENT_NONE;
11864 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
11865 struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
11867 return gaeli->event_value[value_pos];
11870 int GetGlobalAnimEventValueCount(int list_pos)
11872 if (list_pos == ANIM_EVENT_UNDEFINED)
11875 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
11876 struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
11878 return gaeli->num_event_values;
11881 // This function checks if a string <s> of the format "string1, string2, ..."
11882 // exactly contains a string <s_contained>.
11884 static boolean string_has_parameter(char *s, char *s_contained)
11888 if (s == NULL || s_contained == NULL)
11891 if (strlen(s_contained) > strlen(s))
11894 if (strncmp(s, s_contained, strlen(s_contained)) == 0)
11896 char next_char = s[strlen(s_contained)];
11898 // check if next character is delimiter or whitespace
11899 return (next_char == ',' || next_char == '\0' ||
11900 next_char == ' ' || next_char == '\t' ? TRUE : FALSE);
11903 // check if string contains another parameter string after a comma
11904 substring = strchr(s, ',');
11905 if (substring == NULL) // string does not contain a comma
11908 // advance string pointer to next character after the comma
11911 // skip potential whitespaces after the comma
11912 while (*substring == ' ' || *substring == '\t')
11915 return string_has_parameter(substring, s_contained);
11918 static int get_anim_parameter_value(char *s)
11920 int event_value[] =
11928 char *pattern_1[] =
11936 char *pattern_2 = ".part_";
11937 char *matching_char = NULL;
11939 int pattern_1_len = 0;
11940 int result = ANIM_EVENT_NONE;
11943 for (i = 0; i < ARRAY_SIZE(event_value); i++)
11945 matching_char = strstr(s_ptr, pattern_1[i]);
11946 pattern_1_len = strlen(pattern_1[i]);
11947 result = event_value[i];
11949 if (matching_char != NULL)
11953 if (matching_char == NULL)
11954 return ANIM_EVENT_NONE;
11956 s_ptr = matching_char + pattern_1_len;
11958 // check for main animation number ("anim_X" or "anim_XX")
11959 if (*s_ptr >= '0' && *s_ptr <= '9')
11961 int gic_anim_nr = (*s_ptr++ - '0');
11963 if (*s_ptr >= '0' && *s_ptr <= '9')
11964 gic_anim_nr = 10 * gic_anim_nr + (*s_ptr++ - '0');
11966 if (gic_anim_nr < 1 || gic_anim_nr > MAX_GLOBAL_ANIMS)
11967 return ANIM_EVENT_NONE;
11969 result |= gic_anim_nr << ANIM_EVENT_ANIM_BIT;
11973 // invalid main animation number specified
11975 return ANIM_EVENT_NONE;
11978 // check for animation part number ("part_X" or "part_XX") (optional)
11979 if (strPrefix(s_ptr, pattern_2))
11981 s_ptr += strlen(pattern_2);
11983 if (*s_ptr >= '0' && *s_ptr <= '9')
11985 int gic_part_nr = (*s_ptr++ - '0');
11987 if (*s_ptr >= '0' && *s_ptr <= '9')
11988 gic_part_nr = 10 * gic_part_nr + (*s_ptr++ - '0');
11990 if (gic_part_nr < 1 || gic_part_nr > MAX_GLOBAL_ANIM_PARTS)
11991 return ANIM_EVENT_NONE;
11993 result |= gic_part_nr << ANIM_EVENT_PART_BIT;
11997 // invalid animation part number specified
11999 return ANIM_EVENT_NONE;
12003 // discard result if next character is neither delimiter nor whitespace
12004 if (!(*s_ptr == ',' || *s_ptr == '\0' ||
12005 *s_ptr == ' ' || *s_ptr == '\t'))
12006 return ANIM_EVENT_NONE;
12011 static int get_anim_parameter_values(char *s)
12013 int list_pos = ANIM_EVENT_UNDEFINED;
12014 int event_value = ANIM_EVENT_DEFAULT;
12016 if (string_has_parameter(s, "any"))
12017 event_value |= ANIM_EVENT_ANY;
12019 if (string_has_parameter(s, "click:self") ||
12020 string_has_parameter(s, "click") ||
12021 string_has_parameter(s, "self"))
12022 event_value |= ANIM_EVENT_SELF;
12024 if (string_has_parameter(s, "unclick:any"))
12025 event_value |= ANIM_EVENT_UNCLICK_ANY;
12027 // if animation event found, add it to global animation event list
12028 if (event_value != ANIM_EVENT_NONE)
12029 list_pos = AddGlobalAnimEventValue(list_pos, event_value);
12033 // add optional "click:anim_X" or "click:anim_X.part_X" parameter
12034 event_value = get_anim_parameter_value(s);
12036 // if animation event found, add it to global animation event list
12037 if (event_value != ANIM_EVENT_NONE)
12038 list_pos = AddGlobalAnimEventValue(list_pos, event_value);
12040 // continue with next part of the string, starting with next comma
12041 s = strchr(s + 1, ',');
12047 static int get_anim_action_parameter_value(char *token)
12049 // check most common default case first to massively speed things up
12050 if (strEqual(token, ARG_UNDEFINED))
12051 return ANIM_EVENT_ACTION_NONE;
12053 int result = getImageIDFromToken(token);
12057 char *gfx_token = getStringCat2("gfx.", token);
12059 result = getImageIDFromToken(gfx_token);
12061 checked_free(gfx_token);
12066 Key key = getKeyFromX11KeyName(token);
12068 if (key != KSYM_UNDEFINED)
12069 result = -(int)key;
12073 result = ANIM_EVENT_ACTION_NONE;
12078 int get_parameter_value(char *value_raw, char *suffix, int type)
12080 char *value = getStringToLower(value_raw);
12081 int result = 0; // probably a save default value
12083 if (strEqual(suffix, ".direction"))
12085 result = (strEqual(value, "left") ? MV_LEFT :
12086 strEqual(value, "right") ? MV_RIGHT :
12087 strEqual(value, "up") ? MV_UP :
12088 strEqual(value, "down") ? MV_DOWN : MV_NONE);
12090 else if (strEqual(suffix, ".position"))
12092 result = (strEqual(value, "left") ? POS_LEFT :
12093 strEqual(value, "right") ? POS_RIGHT :
12094 strEqual(value, "top") ? POS_TOP :
12095 strEqual(value, "upper") ? POS_UPPER :
12096 strEqual(value, "middle") ? POS_MIDDLE :
12097 strEqual(value, "lower") ? POS_LOWER :
12098 strEqual(value, "bottom") ? POS_BOTTOM :
12099 strEqual(value, "any") ? POS_ANY :
12100 strEqual(value, "last") ? POS_LAST : POS_UNDEFINED);
12102 else if (strEqual(suffix, ".align"))
12104 result = (strEqual(value, "left") ? ALIGN_LEFT :
12105 strEqual(value, "right") ? ALIGN_RIGHT :
12106 strEqual(value, "center") ? ALIGN_CENTER :
12107 strEqual(value, "middle") ? ALIGN_CENTER : ALIGN_DEFAULT);
12109 else if (strEqual(suffix, ".valign"))
12111 result = (strEqual(value, "top") ? VALIGN_TOP :
12112 strEqual(value, "bottom") ? VALIGN_BOTTOM :
12113 strEqual(value, "middle") ? VALIGN_MIDDLE :
12114 strEqual(value, "center") ? VALIGN_MIDDLE : VALIGN_DEFAULT);
12116 else if (strEqual(suffix, ".anim_mode"))
12118 result = (string_has_parameter(value, "none") ? ANIM_NONE :
12119 string_has_parameter(value, "loop") ? ANIM_LOOP :
12120 string_has_parameter(value, "linear") ? ANIM_LINEAR :
12121 string_has_parameter(value, "pingpong") ? ANIM_PINGPONG :
12122 string_has_parameter(value, "pingpong2") ? ANIM_PINGPONG2 :
12123 string_has_parameter(value, "random") ? ANIM_RANDOM :
12124 string_has_parameter(value, "random_static") ? ANIM_RANDOM_STATIC :
12125 string_has_parameter(value, "ce_value") ? ANIM_CE_VALUE :
12126 string_has_parameter(value, "ce_score") ? ANIM_CE_SCORE :
12127 string_has_parameter(value, "ce_delay") ? ANIM_CE_DELAY :
12128 string_has_parameter(value, "horizontal") ? ANIM_HORIZONTAL :
12129 string_has_parameter(value, "vertical") ? ANIM_VERTICAL :
12130 string_has_parameter(value, "centered") ? ANIM_CENTERED :
12131 string_has_parameter(value, "all") ? ANIM_ALL :
12132 string_has_parameter(value, "tiled") ? ANIM_TILED :
12135 if (string_has_parameter(value, "once"))
12136 result |= ANIM_ONCE;
12138 if (string_has_parameter(value, "reverse"))
12139 result |= ANIM_REVERSE;
12141 if (string_has_parameter(value, "opaque_player"))
12142 result |= ANIM_OPAQUE_PLAYER;
12144 if (string_has_parameter(value, "static_panel"))
12145 result |= ANIM_STATIC_PANEL;
12147 else if (strEqual(suffix, ".init_event") ||
12148 strEqual(suffix, ".anim_event"))
12150 result = get_anim_parameter_values(value);
12152 else if (strEqual(suffix, ".init_delay_action") ||
12153 strEqual(suffix, ".anim_delay_action") ||
12154 strEqual(suffix, ".post_delay_action") ||
12155 strEqual(suffix, ".init_event_action") ||
12156 strEqual(suffix, ".anim_event_action"))
12158 result = get_anim_action_parameter_value(value_raw);
12160 else if (strEqual(suffix, ".class"))
12162 result = (strEqual(value, ARG_UNDEFINED) ? ARG_UNDEFINED_VALUE :
12163 get_hash_from_key(value));
12165 else if (strEqual(suffix, ".style"))
12167 result = STYLE_DEFAULT;
12169 if (string_has_parameter(value, "accurate_borders"))
12170 result |= STYLE_ACCURATE_BORDERS;
12172 if (string_has_parameter(value, "inner_corners"))
12173 result |= STYLE_INNER_CORNERS;
12175 if (string_has_parameter(value, "reverse"))
12176 result |= STYLE_REVERSE;
12178 if (string_has_parameter(value, "leftmost_position"))
12179 result |= STYLE_LEFTMOST_POSITION;
12181 if (string_has_parameter(value, "block_clicks"))
12182 result |= STYLE_BLOCK;
12184 if (string_has_parameter(value, "passthrough_clicks"))
12185 result |= STYLE_PASSTHROUGH;
12187 if (string_has_parameter(value, "multiple_actions"))
12188 result |= STYLE_MULTIPLE_ACTIONS;
12190 else if (strEqual(suffix, ".fade_mode"))
12192 result = (string_has_parameter(value, "none") ? FADE_MODE_NONE :
12193 string_has_parameter(value, "fade") ? FADE_MODE_FADE :
12194 string_has_parameter(value, "crossfade") ? FADE_MODE_CROSSFADE :
12195 string_has_parameter(value, "melt") ? FADE_MODE_MELT :
12196 string_has_parameter(value, "curtain") ? FADE_MODE_CURTAIN :
12197 FADE_MODE_DEFAULT);
12199 else if (strEqual(suffix, ".auto_delay_unit"))
12201 result = (string_has_parameter(value, "ms") ? AUTO_DELAY_UNIT_MS :
12202 string_has_parameter(value, "frames") ? AUTO_DELAY_UNIT_FRAMES :
12203 AUTO_DELAY_UNIT_DEFAULT);
12205 else if (strPrefix(suffix, ".font")) // (may also be ".font_xyz")
12207 result = gfx.get_font_from_token_function(value);
12209 else // generic parameter of type integer or boolean
12211 result = (strEqual(value, ARG_UNDEFINED) ? ARG_UNDEFINED_VALUE :
12212 type == TYPE_INTEGER ? get_integer_from_string(value) :
12213 type == TYPE_BOOLEAN ? get_boolean_from_string(value) :
12214 ARG_UNDEFINED_VALUE);
12222 static int get_token_parameter_value(char *token, char *value_raw)
12226 if (token == NULL || value_raw == NULL)
12227 return ARG_UNDEFINED_VALUE;
12229 suffix = strrchr(token, '.');
12230 if (suffix == NULL)
12233 if (strEqual(suffix, ".element"))
12234 return getElementFromToken(value_raw);
12236 // !!! USE CORRECT VALUE TYPE (currently works also for TYPE_BOOLEAN) !!!
12237 return get_parameter_value(value_raw, suffix, TYPE_INTEGER);
12240 void InitMenuDesignSettings_Static(void)
12244 // always start with reliable default values from static default config
12245 for (i = 0; image_config_vars[i].token != NULL; i++)
12247 char *value = getHashEntry(image_config_hash, image_config_vars[i].token);
12250 *image_config_vars[i].value =
12251 get_token_parameter_value(image_config_vars[i].token, value);
12255 static void InitMenuDesignSettings_SpecialPreProcessing(void)
12259 // the following initializes hierarchical values from static configuration
12261 // special case: initialize "ARG_DEFAULT" values in static default config
12262 // (e.g., initialize "[titlemessage].fade_mode" from "[title].fade_mode")
12263 titlescreen_initial_first_default.fade_mode =
12264 title_initial_first_default.fade_mode;
12265 titlescreen_initial_first_default.fade_delay =
12266 title_initial_first_default.fade_delay;
12267 titlescreen_initial_first_default.post_delay =
12268 title_initial_first_default.post_delay;
12269 titlescreen_initial_first_default.auto_delay =
12270 title_initial_first_default.auto_delay;
12271 titlescreen_initial_first_default.auto_delay_unit =
12272 title_initial_first_default.auto_delay_unit;
12273 titlescreen_first_default.fade_mode = title_first_default.fade_mode;
12274 titlescreen_first_default.fade_delay = title_first_default.fade_delay;
12275 titlescreen_first_default.post_delay = title_first_default.post_delay;
12276 titlescreen_first_default.auto_delay = title_first_default.auto_delay;
12277 titlescreen_first_default.auto_delay_unit =
12278 title_first_default.auto_delay_unit;
12279 titlemessage_initial_first_default.fade_mode =
12280 title_initial_first_default.fade_mode;
12281 titlemessage_initial_first_default.fade_delay =
12282 title_initial_first_default.fade_delay;
12283 titlemessage_initial_first_default.post_delay =
12284 title_initial_first_default.post_delay;
12285 titlemessage_initial_first_default.auto_delay =
12286 title_initial_first_default.auto_delay;
12287 titlemessage_initial_first_default.auto_delay_unit =
12288 title_initial_first_default.auto_delay_unit;
12289 titlemessage_first_default.fade_mode = title_first_default.fade_mode;
12290 titlemessage_first_default.fade_delay = title_first_default.fade_delay;
12291 titlemessage_first_default.post_delay = title_first_default.post_delay;
12292 titlemessage_first_default.auto_delay = title_first_default.auto_delay;
12293 titlemessage_first_default.auto_delay_unit =
12294 title_first_default.auto_delay_unit;
12296 titlescreen_initial_default.fade_mode = title_initial_default.fade_mode;
12297 titlescreen_initial_default.fade_delay = title_initial_default.fade_delay;
12298 titlescreen_initial_default.post_delay = title_initial_default.post_delay;
12299 titlescreen_initial_default.auto_delay = title_initial_default.auto_delay;
12300 titlescreen_initial_default.auto_delay_unit =
12301 title_initial_default.auto_delay_unit;
12302 titlescreen_default.fade_mode = title_default.fade_mode;
12303 titlescreen_default.fade_delay = title_default.fade_delay;
12304 titlescreen_default.post_delay = title_default.post_delay;
12305 titlescreen_default.auto_delay = title_default.auto_delay;
12306 titlescreen_default.auto_delay_unit = title_default.auto_delay_unit;
12307 titlemessage_initial_default.fade_mode = title_initial_default.fade_mode;
12308 titlemessage_initial_default.fade_delay = title_initial_default.fade_delay;
12309 titlemessage_initial_default.post_delay = title_initial_default.post_delay;
12310 titlemessage_initial_default.auto_delay_unit =
12311 title_initial_default.auto_delay_unit;
12312 titlemessage_default.fade_mode = title_default.fade_mode;
12313 titlemessage_default.fade_delay = title_default.fade_delay;
12314 titlemessage_default.post_delay = title_default.post_delay;
12315 titlemessage_default.auto_delay = title_default.auto_delay;
12316 titlemessage_default.auto_delay_unit = title_default.auto_delay_unit;
12318 // special case: initialize "ARG_DEFAULT" values in static default config
12319 // (e.g., init "titlemessage_1.fade_mode" from "[titlemessage].fade_mode")
12320 for (i = 0; i < MAX_NUM_TITLE_MESSAGES; i++)
12322 titlescreen_initial_first[i] = titlescreen_initial_first_default;
12323 titlescreen_first[i] = titlescreen_first_default;
12324 titlemessage_initial_first[i] = titlemessage_initial_first_default;
12325 titlemessage_first[i] = titlemessage_first_default;
12327 titlescreen_initial[i] = titlescreen_initial_default;
12328 titlescreen[i] = titlescreen_default;
12329 titlemessage_initial[i] = titlemessage_initial_default;
12330 titlemessage[i] = titlemessage_default;
12333 // special case: initialize "ARG_DEFAULT" values in static default config
12334 // (eg, init "menu.enter_screen.SCORES.xyz" from "menu.enter_screen.xyz")
12335 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
12337 if (i == GFX_SPECIAL_ARG_TITLE) // title values already initialized
12340 menu.enter_screen[i] = menu.enter_screen[GFX_SPECIAL_ARG_DEFAULT];
12341 menu.leave_screen[i] = menu.leave_screen[GFX_SPECIAL_ARG_DEFAULT];
12342 menu.next_screen[i] = menu.next_screen[GFX_SPECIAL_ARG_DEFAULT];
12345 // special case: initialize "ARG_DEFAULT" values in static default config
12346 // (eg, init "viewport.door_1.MAIN.xyz" from "viewport.door_1.xyz")
12347 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
12349 viewport.window[i] = viewport.window[GFX_SPECIAL_ARG_DEFAULT];
12350 viewport.playfield[i] = viewport.playfield[GFX_SPECIAL_ARG_DEFAULT];
12351 viewport.door_1[i] = viewport.door_1[GFX_SPECIAL_ARG_DEFAULT];
12353 if (i == GFX_SPECIAL_ARG_EDITOR) // editor values already initialized
12356 viewport.door_2[i] = viewport.door_2[GFX_SPECIAL_ARG_DEFAULT];
12360 static void InitMenuDesignSettings_SpecialPostProcessing(void)
12364 struct XY *dst, *src;
12366 game_buttons_xy[] =
12368 { &game.button.save, &game.button.stop },
12369 { &game.button.pause2, &game.button.pause },
12370 { &game.button.load, &game.button.play },
12371 { &game.button.undo, &game.button.stop },
12372 { &game.button.redo, &game.button.play },
12378 // special case: initialize later added SETUP list size from LEVELS value
12379 if (menu.list_size[GAME_MODE_SETUP] == -1)
12380 menu.list_size[GAME_MODE_SETUP] = menu.list_size[GAME_MODE_LEVELS];
12382 // set default position for snapshot buttons to stop/pause/play buttons
12383 for (i = 0; game_buttons_xy[i].dst != NULL; i++)
12384 if ((*game_buttons_xy[i].dst).x == -1 &&
12385 (*game_buttons_xy[i].dst).y == -1)
12386 *game_buttons_xy[i].dst = *game_buttons_xy[i].src;
12388 // --------------------------------------------------------------------------
12389 // dynamic viewports (including playfield margins, borders and alignments)
12390 // --------------------------------------------------------------------------
12392 // dynamic viewports currently only supported for landscape mode
12393 int display_width = MAX(video.display_width, video.display_height);
12394 int display_height = MIN(video.display_width, video.display_height);
12396 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
12398 struct RectWithBorder *vp_window = &viewport.window[i];
12399 struct RectWithBorder *vp_playfield = &viewport.playfield[i];
12400 struct RectWithBorder *vp_door_1 = &viewport.door_1[i];
12401 struct RectWithBorder *vp_door_2 = &viewport.door_2[i];
12402 boolean dynamic_window_width = (vp_window->min_width != -1);
12403 boolean dynamic_window_height = (vp_window->min_height != -1);
12404 boolean dynamic_playfield_width = (vp_playfield->min_width != -1);
12405 boolean dynamic_playfield_height = (vp_playfield->min_height != -1);
12407 // adjust window size if min/max width/height is specified
12409 if (vp_window->min_width != -1)
12411 int window_width = display_width;
12413 // when using static window height, use aspect ratio of display
12414 if (vp_window->min_height == -1)
12415 window_width = vp_window->height * display_width / display_height;
12417 vp_window->width = MAX(vp_window->min_width, window_width);
12420 if (vp_window->min_height != -1)
12422 int window_height = display_height;
12424 // when using static window width, use aspect ratio of display
12425 if (vp_window->min_width == -1)
12426 window_height = vp_window->width * display_height / display_width;
12428 vp_window->height = MAX(vp_window->min_height, window_height);
12431 if (vp_window->max_width != -1)
12432 vp_window->width = MIN(vp_window->width, vp_window->max_width);
12434 if (vp_window->max_height != -1)
12435 vp_window->height = MIN(vp_window->height, vp_window->max_height);
12437 int playfield_width = vp_window->width;
12438 int playfield_height = vp_window->height;
12440 // adjust playfield size and position according to specified margins
12442 playfield_width -= vp_playfield->margin_left;
12443 playfield_width -= vp_playfield->margin_right;
12445 playfield_height -= vp_playfield->margin_top;
12446 playfield_height -= vp_playfield->margin_bottom;
12448 // adjust playfield size if min/max width/height is specified
12450 if (vp_playfield->min_width != -1)
12451 vp_playfield->width = MAX(vp_playfield->min_width, playfield_width);
12453 if (vp_playfield->min_height != -1)
12454 vp_playfield->height = MAX(vp_playfield->min_height, playfield_height);
12456 if (vp_playfield->max_width != -1)
12457 vp_playfield->width = MIN(vp_playfield->width, vp_playfield->max_width);
12459 if (vp_playfield->max_height != -1)
12460 vp_playfield->height = MIN(vp_playfield->height,vp_playfield->max_height);
12462 // adjust playfield position according to specified alignment
12464 if (vp_playfield->align == ALIGN_LEFT || vp_playfield->x > 0)
12465 vp_playfield->x = ALIGNED_VP_XPOS(vp_playfield);
12466 else if (vp_playfield->align == ALIGN_CENTER)
12467 vp_playfield->x = playfield_width / 2 - vp_playfield->width / 2;
12468 else if (vp_playfield->align == ALIGN_RIGHT)
12469 vp_playfield->x += playfield_width - vp_playfield->width;
12471 if (vp_playfield->valign == VALIGN_TOP || vp_playfield->y > 0)
12472 vp_playfield->y = ALIGNED_VP_YPOS(vp_playfield);
12473 else if (vp_playfield->valign == VALIGN_MIDDLE)
12474 vp_playfield->y = playfield_height / 2 - vp_playfield->height / 2;
12475 else if (vp_playfield->valign == VALIGN_BOTTOM)
12476 vp_playfield->y += playfield_height - vp_playfield->height;
12478 vp_playfield->x += vp_playfield->margin_left;
12479 vp_playfield->y += vp_playfield->margin_top;
12481 // adjust individual playfield borders if only default border is specified
12483 if (vp_playfield->border_left == -1)
12484 vp_playfield->border_left = vp_playfield->border_size;
12485 if (vp_playfield->border_right == -1)
12486 vp_playfield->border_right = vp_playfield->border_size;
12487 if (vp_playfield->border_top == -1)
12488 vp_playfield->border_top = vp_playfield->border_size;
12489 if (vp_playfield->border_bottom == -1)
12490 vp_playfield->border_bottom = vp_playfield->border_size;
12492 // set dynamic playfield borders if borders are specified as undefined
12493 // (but only if window size was dynamic and playfield size was static)
12495 if (dynamic_window_width && !dynamic_playfield_width)
12497 if (vp_playfield->border_left == -1)
12499 vp_playfield->border_left = (vp_playfield->x -
12500 vp_playfield->margin_left);
12501 vp_playfield->x -= vp_playfield->border_left;
12502 vp_playfield->width += vp_playfield->border_left;
12505 if (vp_playfield->border_right == -1)
12507 vp_playfield->border_right = (vp_window->width -
12509 vp_playfield->width -
12510 vp_playfield->margin_right);
12511 vp_playfield->width += vp_playfield->border_right;
12515 if (dynamic_window_height && !dynamic_playfield_height)
12517 if (vp_playfield->border_top == -1)
12519 vp_playfield->border_top = (vp_playfield->y -
12520 vp_playfield->margin_top);
12521 vp_playfield->y -= vp_playfield->border_top;
12522 vp_playfield->height += vp_playfield->border_top;
12525 if (vp_playfield->border_bottom == -1)
12527 vp_playfield->border_bottom = (vp_window->height -
12529 vp_playfield->height -
12530 vp_playfield->margin_bottom);
12531 vp_playfield->height += vp_playfield->border_bottom;
12535 // adjust playfield size to be a multiple of a defined alignment tile size
12537 int align_size = vp_playfield->align_size;
12538 int playfield_xtiles = vp_playfield->width / align_size;
12539 int playfield_ytiles = vp_playfield->height / align_size;
12540 int playfield_width_corrected = playfield_xtiles * align_size;
12541 int playfield_height_corrected = playfield_ytiles * align_size;
12542 boolean is_playfield_mode = (i == GFX_SPECIAL_ARG_PLAYING ||
12543 i == GFX_SPECIAL_ARG_EDITOR);
12545 if (is_playfield_mode &&
12546 dynamic_playfield_width &&
12547 vp_playfield->width != playfield_width_corrected)
12549 int playfield_xdiff = vp_playfield->width - playfield_width_corrected;
12551 vp_playfield->width = playfield_width_corrected;
12553 if (vp_playfield->align == ALIGN_LEFT)
12555 vp_playfield->border_left += playfield_xdiff;
12557 else if (vp_playfield->align == ALIGN_RIGHT)
12559 vp_playfield->border_right += playfield_xdiff;
12561 else if (vp_playfield->align == ALIGN_CENTER)
12563 int border_left_diff = playfield_xdiff / 2;
12564 int border_right_diff = playfield_xdiff - border_left_diff;
12566 vp_playfield->border_left += border_left_diff;
12567 vp_playfield->border_right += border_right_diff;
12571 if (is_playfield_mode &&
12572 dynamic_playfield_height &&
12573 vp_playfield->height != playfield_height_corrected)
12575 int playfield_ydiff = vp_playfield->height - playfield_height_corrected;
12577 vp_playfield->height = playfield_height_corrected;
12579 if (vp_playfield->valign == VALIGN_TOP)
12581 vp_playfield->border_top += playfield_ydiff;
12583 else if (vp_playfield->align == VALIGN_BOTTOM)
12585 vp_playfield->border_right += playfield_ydiff;
12587 else if (vp_playfield->align == VALIGN_MIDDLE)
12589 int border_top_diff = playfield_ydiff / 2;
12590 int border_bottom_diff = playfield_ydiff - border_top_diff;
12592 vp_playfield->border_top += border_top_diff;
12593 vp_playfield->border_bottom += border_bottom_diff;
12597 // adjust door positions according to specified alignment
12599 for (j = 0; j < 2; j++)
12601 struct RectWithBorder *vp_door = (j == 0 ? vp_door_1 : vp_door_2);
12603 if (vp_door->align == ALIGN_LEFT || vp_door->x > 0)
12604 vp_door->x = ALIGNED_VP_XPOS(vp_door);
12605 else if (vp_door->align == ALIGN_CENTER)
12606 vp_door->x = vp_window->width / 2 - vp_door->width / 2;
12607 else if (vp_door->align == ALIGN_RIGHT)
12608 vp_door->x += vp_window->width - vp_door->width;
12610 if (vp_door->valign == VALIGN_TOP || vp_door->y > 0)
12611 vp_door->y = ALIGNED_VP_YPOS(vp_door);
12612 else if (vp_door->valign == VALIGN_MIDDLE)
12613 vp_door->y = vp_window->height / 2 - vp_door->height / 2;
12614 else if (vp_door->valign == VALIGN_BOTTOM)
12615 vp_door->y += vp_window->height - vp_door->height;
12620 static void InitMenuDesignSettings_SpecialPostProcessing_AfterGraphics(void)
12624 struct XYTileSize *dst, *src;
12627 editor_buttons_xy[] =
12630 &editor.button.element_left, &editor.palette.element_left,
12631 IMG_GFX_EDITOR_BUTTON_ELEMENT_LEFT
12634 &editor.button.element_middle, &editor.palette.element_middle,
12635 IMG_GFX_EDITOR_BUTTON_ELEMENT_MIDDLE
12638 &editor.button.element_right, &editor.palette.element_right,
12639 IMG_GFX_EDITOR_BUTTON_ELEMENT_RIGHT
12646 // set default position for element buttons to element graphics
12647 for (i = 0; editor_buttons_xy[i].dst != NULL; i++)
12649 if ((*editor_buttons_xy[i].dst).x == -1 &&
12650 (*editor_buttons_xy[i].dst).y == -1)
12652 struct GraphicInfo *gd = &graphic_info[editor_buttons_xy[i].graphic];
12654 gd->width = gd->height = editor_buttons_xy[i].src->tile_size;
12656 *editor_buttons_xy[i].dst = *editor_buttons_xy[i].src;
12660 // adjust editor palette rows and columns if specified to be dynamic
12662 if (editor.palette.cols == -1)
12664 int vp_width = viewport.playfield[GFX_SPECIAL_ARG_EDITOR].width;
12665 int bt_width = graphic_info[IMG_EDITOR_PALETTE_BUTTON].width;
12666 int sc_width = graphic_info[IMG_EDITOR_PALETTE_SCROLLBAR].width;
12668 editor.palette.cols = (vp_width - sc_width) / bt_width;
12670 if (editor.palette.x == -1)
12672 int palette_width = editor.palette.cols * bt_width + sc_width;
12674 editor.palette.x = (vp_width - palette_width) / 2;
12678 if (editor.palette.rows == -1)
12680 int vp_height = viewport.playfield[GFX_SPECIAL_ARG_EDITOR].height;
12681 int bt_height = graphic_info[IMG_EDITOR_PALETTE_BUTTON].height;
12682 int tx_height = getFontHeight(FONT_TEXT_2);
12684 editor.palette.rows = (vp_height - tx_height) / bt_height;
12686 if (editor.palette.y == -1)
12688 int palette_height = editor.palette.rows * bt_height + tx_height;
12690 editor.palette.y = (vp_height - palette_height) / 2;
12695 static void LoadMenuDesignSettingsFromFilename(char *filename)
12697 static struct TitleFadingInfo tfi;
12698 static struct TitleMessageInfo tmi;
12699 static struct TokenInfo title_tokens[] =
12701 { TYPE_INTEGER, &tfi.fade_mode, ".fade_mode" },
12702 { TYPE_INTEGER, &tfi.fade_delay, ".fade_delay" },
12703 { TYPE_INTEGER, &tfi.post_delay, ".post_delay" },
12704 { TYPE_INTEGER, &tfi.auto_delay, ".auto_delay" },
12705 { TYPE_INTEGER, &tfi.auto_delay_unit, ".auto_delay_unit" },
12709 static struct TokenInfo titlemessage_tokens[] =
12711 { TYPE_INTEGER, &tmi.x, ".x" },
12712 { TYPE_INTEGER, &tmi.y, ".y" },
12713 { TYPE_INTEGER, &tmi.width, ".width" },
12714 { TYPE_INTEGER, &tmi.height, ".height" },
12715 { TYPE_INTEGER, &tmi.chars, ".chars" },
12716 { TYPE_INTEGER, &tmi.lines, ".lines" },
12717 { TYPE_INTEGER, &tmi.align, ".align" },
12718 { TYPE_INTEGER, &tmi.valign, ".valign" },
12719 { TYPE_INTEGER, &tmi.font, ".font" },
12720 { TYPE_BOOLEAN, &tmi.autowrap, ".autowrap" },
12721 { TYPE_BOOLEAN, &tmi.centered, ".centered" },
12722 { TYPE_BOOLEAN, &tmi.parse_comments, ".parse_comments" },
12723 { TYPE_INTEGER, &tmi.sort_priority, ".sort_priority" },
12724 { TYPE_INTEGER, &tmi.fade_mode, ".fade_mode" },
12725 { TYPE_INTEGER, &tmi.fade_delay, ".fade_delay" },
12726 { TYPE_INTEGER, &tmi.post_delay, ".post_delay" },
12727 { TYPE_INTEGER, &tmi.auto_delay, ".auto_delay" },
12728 { TYPE_INTEGER, &tmi.auto_delay_unit, ".auto_delay_unit" },
12734 struct TitleFadingInfo *info;
12739 // initialize first titles from "enter screen" definitions, if defined
12740 { &title_initial_first_default, "menu.enter_screen.TITLE" },
12741 { &title_first_default, "menu.enter_screen.TITLE" },
12743 // initialize title screens from "next screen" definitions, if defined
12744 { &title_initial_default, "menu.next_screen.TITLE" },
12745 { &title_default, "menu.next_screen.TITLE" },
12751 struct TitleMessageInfo *array;
12754 titlemessage_arrays[] =
12756 // initialize first titles from "enter screen" definitions, if defined
12757 { titlescreen_initial_first, "menu.enter_screen.TITLE" },
12758 { titlescreen_first, "menu.enter_screen.TITLE" },
12759 { titlemessage_initial_first, "menu.enter_screen.TITLE" },
12760 { titlemessage_first, "menu.enter_screen.TITLE" },
12762 // initialize titles from "next screen" definitions, if defined
12763 { titlescreen_initial, "menu.next_screen.TITLE" },
12764 { titlescreen, "menu.next_screen.TITLE" },
12765 { titlemessage_initial, "menu.next_screen.TITLE" },
12766 { titlemessage, "menu.next_screen.TITLE" },
12768 // overwrite titles with title definitions, if defined
12769 { titlescreen_initial_first, "[title_initial]" },
12770 { titlescreen_first, "[title]" },
12771 { titlemessage_initial_first, "[title_initial]" },
12772 { titlemessage_first, "[title]" },
12774 { titlescreen_initial, "[title_initial]" },
12775 { titlescreen, "[title]" },
12776 { titlemessage_initial, "[title_initial]" },
12777 { titlemessage, "[title]" },
12779 // overwrite titles with title screen/message definitions, if defined
12780 { titlescreen_initial_first, "[titlescreen_initial]" },
12781 { titlescreen_first, "[titlescreen]" },
12782 { titlemessage_initial_first, "[titlemessage_initial]" },
12783 { titlemessage_first, "[titlemessage]" },
12785 { titlescreen_initial, "[titlescreen_initial]" },
12786 { titlescreen, "[titlescreen]" },
12787 { titlemessage_initial, "[titlemessage_initial]" },
12788 { titlemessage, "[titlemessage]" },
12792 SetupFileHash *setup_file_hash;
12795 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
12798 // the following initializes hierarchical values from dynamic configuration
12800 // special case: initialize with default values that may be overwritten
12801 // (e.g., init "menu.draw_xoffset.INFO" from "menu.draw_xoffset")
12802 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
12804 struct TokenIntPtrInfo menu_config[] =
12806 { "menu.draw_xoffset", &menu.draw_xoffset[i] },
12807 { "menu.draw_yoffset", &menu.draw_yoffset[i] },
12808 { "menu.list_size", &menu.list_size[i] }
12811 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
12813 char *token = menu_config[j].token;
12814 char *value = getHashEntry(setup_file_hash, token);
12817 *menu_config[j].value = get_integer_from_string(value);
12821 // special case: initialize with default values that may be overwritten
12822 // (eg, init "menu.draw_xoffset.INFO[XXX]" from "menu.draw_xoffset.INFO")
12823 for (i = 0; i < NUM_SPECIAL_GFX_INFO_ARGS; i++)
12825 struct TokenIntPtrInfo menu_config[] =
12827 { "menu.draw_xoffset.INFO", &menu.draw_xoffset_info[i] },
12828 { "menu.draw_yoffset.INFO", &menu.draw_yoffset_info[i] },
12829 { "menu.list_size.INFO", &menu.list_size_info[i] }
12832 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
12834 char *token = menu_config[j].token;
12835 char *value = getHashEntry(setup_file_hash, token);
12838 *menu_config[j].value = get_integer_from_string(value);
12842 // special case: initialize with default values that may be overwritten
12843 // (eg, init "menu.draw_xoffset.SETUP[XXX]" from "menu.draw_xoffset.SETUP")
12844 for (i = 0; i < NUM_SPECIAL_GFX_SETUP_ARGS; i++)
12846 struct TokenIntPtrInfo menu_config[] =
12848 { "menu.draw_xoffset.SETUP", &menu.draw_xoffset_setup[i] },
12849 { "menu.draw_yoffset.SETUP", &menu.draw_yoffset_setup[i] }
12852 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
12854 char *token = menu_config[j].token;
12855 char *value = getHashEntry(setup_file_hash, token);
12858 *menu_config[j].value = get_integer_from_string(value);
12862 // special case: initialize with default values that may be overwritten
12863 // (eg, init "menu.line_spacing.INFO[XXX]" from "menu.line_spacing.INFO")
12864 for (i = 0; i < NUM_SPECIAL_GFX_INFO_ARGS; i++)
12866 struct TokenIntPtrInfo menu_config[] =
12868 { "menu.left_spacing.INFO", &menu.left_spacing_info[i] },
12869 { "menu.right_spacing.INFO", &menu.right_spacing_info[i] },
12870 { "menu.top_spacing.INFO", &menu.top_spacing_info[i] },
12871 { "menu.bottom_spacing.INFO", &menu.bottom_spacing_info[i] },
12872 { "menu.paragraph_spacing.INFO", &menu.paragraph_spacing_info[i] },
12873 { "menu.headline1_spacing.INFO", &menu.headline1_spacing_info[i] },
12874 { "menu.headline2_spacing.INFO", &menu.headline2_spacing_info[i] },
12875 { "menu.line_spacing.INFO", &menu.line_spacing_info[i] },
12876 { "menu.extra_spacing.INFO", &menu.extra_spacing_info[i] },
12879 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
12881 char *token = menu_config[j].token;
12882 char *value = getHashEntry(setup_file_hash, token);
12885 *menu_config[j].value = get_integer_from_string(value);
12889 // special case: initialize with default values that may be overwritten
12890 // (eg, init "menu.enter_screen.SCORES.xyz" from "menu.enter_screen.xyz")
12891 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
12893 struct TokenIntPtrInfo menu_config[] =
12895 { "menu.enter_screen.fade_mode", &menu.enter_screen[i].fade_mode },
12896 { "menu.enter_screen.fade_delay", &menu.enter_screen[i].fade_delay },
12897 { "menu.enter_screen.post_delay", &menu.enter_screen[i].post_delay },
12898 { "menu.leave_screen.fade_mode", &menu.leave_screen[i].fade_mode },
12899 { "menu.leave_screen.fade_delay", &menu.leave_screen[i].fade_delay },
12900 { "menu.leave_screen.post_delay", &menu.leave_screen[i].post_delay },
12901 { "menu.next_screen.fade_mode", &menu.next_screen[i].fade_mode },
12902 { "menu.next_screen.fade_delay", &menu.next_screen[i].fade_delay },
12903 { "menu.next_screen.post_delay", &menu.next_screen[i].post_delay }
12906 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
12908 char *token = menu_config[j].token;
12909 char *value = getHashEntry(setup_file_hash, token);
12912 *menu_config[j].value = get_token_parameter_value(token, value);
12916 // special case: initialize with default values that may be overwritten
12917 // (eg, init "viewport.door_1.MAIN.xyz" from "viewport.door_1.xyz")
12918 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
12922 char *token_prefix;
12923 struct RectWithBorder *struct_ptr;
12927 { "viewport.window", &viewport.window[i] },
12928 { "viewport.playfield", &viewport.playfield[i] },
12929 { "viewport.door_1", &viewport.door_1[i] },
12930 { "viewport.door_2", &viewport.door_2[i] }
12933 for (j = 0; j < ARRAY_SIZE(vp_struct); j++)
12935 struct TokenIntPtrInfo vp_config[] =
12937 { ".x", &vp_struct[j].struct_ptr->x },
12938 { ".y", &vp_struct[j].struct_ptr->y },
12939 { ".width", &vp_struct[j].struct_ptr->width },
12940 { ".height", &vp_struct[j].struct_ptr->height },
12941 { ".min_width", &vp_struct[j].struct_ptr->min_width },
12942 { ".min_height", &vp_struct[j].struct_ptr->min_height },
12943 { ".max_width", &vp_struct[j].struct_ptr->max_width },
12944 { ".max_height", &vp_struct[j].struct_ptr->max_height },
12945 { ".margin_left", &vp_struct[j].struct_ptr->margin_left },
12946 { ".margin_right", &vp_struct[j].struct_ptr->margin_right },
12947 { ".margin_top", &vp_struct[j].struct_ptr->margin_top },
12948 { ".margin_bottom", &vp_struct[j].struct_ptr->margin_bottom },
12949 { ".border_left", &vp_struct[j].struct_ptr->border_left },
12950 { ".border_right", &vp_struct[j].struct_ptr->border_right },
12951 { ".border_top", &vp_struct[j].struct_ptr->border_top },
12952 { ".border_bottom", &vp_struct[j].struct_ptr->border_bottom },
12953 { ".border_size", &vp_struct[j].struct_ptr->border_size },
12954 { ".align_size", &vp_struct[j].struct_ptr->align_size },
12955 { ".align", &vp_struct[j].struct_ptr->align },
12956 { ".valign", &vp_struct[j].struct_ptr->valign }
12959 for (k = 0; k < ARRAY_SIZE(vp_config); k++)
12961 char *token = getStringCat2(vp_struct[j].token_prefix,
12962 vp_config[k].token);
12963 char *value = getHashEntry(setup_file_hash, token);
12966 *vp_config[k].value = get_token_parameter_value(token, value);
12973 // special case: initialize with default values that may be overwritten
12974 // (e.g., init "[title].fade_mode" from "menu.next_screen.TITLE.fade_mode")
12975 for (i = 0; title_info[i].info != NULL; i++)
12977 struct TitleFadingInfo *info = title_info[i].info;
12978 char *base_token = title_info[i].text;
12980 for (j = 0; title_tokens[j].type != -1; j++)
12982 char *token = getStringCat2(base_token, title_tokens[j].text);
12983 char *value = getHashEntry(setup_file_hash, token);
12987 int parameter_value = get_token_parameter_value(token, value);
12991 *(int *)title_tokens[j].value = (int)parameter_value;
13000 // special case: initialize with default values that may be overwritten
13001 // (e.g., init "titlemessage_1.fade_mode" from "[titlemessage].fade_mode")
13002 for (i = 0; titlemessage_arrays[i].array != NULL; i++)
13004 struct TitleMessageInfo *array = titlemessage_arrays[i].array;
13005 char *base_token = titlemessage_arrays[i].text;
13007 for (j = 0; titlemessage_tokens[j].type != -1; j++)
13009 char *token = getStringCat2(base_token, titlemessage_tokens[j].text);
13010 char *value = getHashEntry(setup_file_hash, token);
13014 int parameter_value = get_token_parameter_value(token, value);
13016 for (k = 0; k < MAX_NUM_TITLE_MESSAGES; k++)
13020 if (titlemessage_tokens[j].type == TYPE_INTEGER)
13021 *(int *)titlemessage_tokens[j].value = (int)parameter_value;
13023 *(boolean *)titlemessage_tokens[j].value = (boolean)parameter_value;
13033 // special case: check if network and preview player positions are redefined,
13034 // to compare this later against the main menu level preview being redefined
13035 struct TokenIntPtrInfo menu_config_players[] =
13037 { "main.network_players.x", &menu.main.network_players.redefined },
13038 { "main.network_players.y", &menu.main.network_players.redefined },
13039 { "main.preview_players.x", &menu.main.preview_players.redefined },
13040 { "main.preview_players.y", &menu.main.preview_players.redefined },
13041 { "preview.x", &preview.redefined },
13042 { "preview.y", &preview.redefined }
13045 for (i = 0; i < ARRAY_SIZE(menu_config_players); i++)
13046 *menu_config_players[i].value = FALSE;
13048 for (i = 0; i < ARRAY_SIZE(menu_config_players); i++)
13049 if (getHashEntry(setup_file_hash, menu_config_players[i].token) != NULL)
13050 *menu_config_players[i].value = TRUE;
13052 // read (and overwrite with) values that may be specified in config file
13053 for (i = 0; image_config_vars[i].token != NULL; i++)
13055 char *value = getHashEntry(setup_file_hash, image_config_vars[i].token);
13057 // (ignore definitions set to "[DEFAULT]" which are already initialized)
13058 if (value != NULL && !strEqual(value, ARG_DEFAULT))
13059 *image_config_vars[i].value =
13060 get_token_parameter_value(image_config_vars[i].token, value);
13063 freeSetupFileHash(setup_file_hash);
13066 void LoadMenuDesignSettings(void)
13068 char *filename_base = UNDEFINED_FILENAME, *filename_local;
13070 InitMenuDesignSettings_Static();
13071 InitMenuDesignSettings_SpecialPreProcessing();
13073 if (!GFX_OVERRIDE_ARTWORK(ARTWORK_TYPE_GRAPHICS))
13075 // first look for special settings configured in level series config
13076 filename_base = getCustomArtworkLevelConfigFilename(ARTWORK_TYPE_GRAPHICS);
13078 if (fileExists(filename_base))
13079 LoadMenuDesignSettingsFromFilename(filename_base);
13082 filename_local = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
13084 if (filename_local != NULL && !strEqual(filename_base, filename_local))
13085 LoadMenuDesignSettingsFromFilename(filename_local);
13087 InitMenuDesignSettings_SpecialPostProcessing();
13090 void LoadMenuDesignSettings_AfterGraphics(void)
13092 InitMenuDesignSettings_SpecialPostProcessing_AfterGraphics();
13095 void LoadUserDefinedEditorElementList(int **elements, int *num_elements)
13097 char *filename = getEditorSetupFilename();
13098 SetupFileList *setup_file_list, *list;
13099 SetupFileHash *element_hash;
13100 int num_unknown_tokens = 0;
13103 if ((setup_file_list = loadSetupFileList(filename)) == NULL)
13106 element_hash = newSetupFileHash();
13108 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
13109 setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
13111 // determined size may be larger than needed (due to unknown elements)
13113 for (list = setup_file_list; list != NULL; list = list->next)
13116 // add space for up to 3 more elements for padding that may be needed
13117 *num_elements += 3;
13119 // free memory for old list of elements, if needed
13120 checked_free(*elements);
13122 // allocate memory for new list of elements
13123 *elements = checked_malloc(*num_elements * sizeof(int));
13126 for (list = setup_file_list; list != NULL; list = list->next)
13128 char *value = getHashEntry(element_hash, list->token);
13130 if (value == NULL) // try to find obsolete token mapping
13132 char *mapped_token = get_mapped_token(list->token);
13134 if (mapped_token != NULL)
13136 value = getHashEntry(element_hash, mapped_token);
13138 free(mapped_token);
13144 (*elements)[(*num_elements)++] = atoi(value);
13148 if (num_unknown_tokens == 0)
13151 Warn("unknown token(s) found in config file:");
13152 Warn("- config file: '%s'", filename);
13154 num_unknown_tokens++;
13157 Warn("- token: '%s'", list->token);
13161 if (num_unknown_tokens > 0)
13164 while (*num_elements % 4) // pad with empty elements, if needed
13165 (*elements)[(*num_elements)++] = EL_EMPTY;
13167 freeSetupFileList(setup_file_list);
13168 freeSetupFileHash(element_hash);
13171 for (i = 0; i < *num_elements; i++)
13172 Debug("editor", "element '%s' [%d]\n",
13173 element_info[(*elements)[i]].token_name, (*elements)[i]);
13177 static struct MusicFileInfo *get_music_file_info_ext(char *basename, int music,
13180 SetupFileHash *setup_file_hash = NULL;
13181 struct MusicFileInfo tmp_music_file_info, *new_music_file_info;
13182 char *filename_music, *filename_prefix, *filename_info;
13188 token_to_value_ptr[] =
13190 { "title_header", &tmp_music_file_info.title_header },
13191 { "artist_header", &tmp_music_file_info.artist_header },
13192 { "album_header", &tmp_music_file_info.album_header },
13193 { "year_header", &tmp_music_file_info.year_header },
13195 { "title", &tmp_music_file_info.title },
13196 { "artist", &tmp_music_file_info.artist },
13197 { "album", &tmp_music_file_info.album },
13198 { "year", &tmp_music_file_info.year },
13204 filename_music = (is_sound ? getCustomSoundFilename(basename) :
13205 getCustomMusicFilename(basename));
13207 if (filename_music == NULL)
13210 // ---------- try to replace file extension ----------
13212 filename_prefix = getStringCopy(filename_music);
13213 if (strrchr(filename_prefix, '.') != NULL)
13214 *strrchr(filename_prefix, '.') = '\0';
13215 filename_info = getStringCat2(filename_prefix, ".txt");
13217 if (fileExists(filename_info))
13218 setup_file_hash = loadSetupFileHash(filename_info);
13220 free(filename_prefix);
13221 free(filename_info);
13223 if (setup_file_hash == NULL)
13225 // ---------- try to add file extension ----------
13227 filename_prefix = getStringCopy(filename_music);
13228 filename_info = getStringCat2(filename_prefix, ".txt");
13230 if (fileExists(filename_info))
13231 setup_file_hash = loadSetupFileHash(filename_info);
13233 free(filename_prefix);
13234 free(filename_info);
13237 if (setup_file_hash == NULL)
13240 // ---------- music file info found ----------
13242 clear_mem(&tmp_music_file_info, sizeof(struct MusicFileInfo));
13244 for (i = 0; token_to_value_ptr[i].token != NULL; i++)
13246 char *value = getHashEntry(setup_file_hash, token_to_value_ptr[i].token);
13248 *token_to_value_ptr[i].value_ptr =
13249 getStringCopy(value != NULL && *value != '\0' ? value : UNKNOWN_NAME);
13252 tmp_music_file_info.basename = getStringCopy(basename);
13253 tmp_music_file_info.music = music;
13254 tmp_music_file_info.is_sound = is_sound;
13256 new_music_file_info = checked_malloc(sizeof(struct MusicFileInfo));
13257 *new_music_file_info = tmp_music_file_info;
13259 return new_music_file_info;
13262 static struct MusicFileInfo *get_music_file_info(char *basename, int music)
13264 return get_music_file_info_ext(basename, music, FALSE);
13267 static struct MusicFileInfo *get_sound_file_info(char *basename, int sound)
13269 return get_music_file_info_ext(basename, sound, TRUE);
13272 static boolean music_info_listed_ext(struct MusicFileInfo *list,
13273 char *basename, boolean is_sound)
13275 for (; list != NULL; list = list->next)
13276 if (list->is_sound == is_sound && strEqual(list->basename, basename))
13282 static boolean music_info_listed(struct MusicFileInfo *list, char *basename)
13284 return music_info_listed_ext(list, basename, FALSE);
13287 static boolean sound_info_listed(struct MusicFileInfo *list, char *basename)
13289 return music_info_listed_ext(list, basename, TRUE);
13292 void LoadMusicInfo(void)
13294 char *music_directory = getCustomMusicDirectory();
13295 int num_music = getMusicListSize();
13296 int num_music_noconf = 0;
13297 int num_sounds = getSoundListSize();
13299 DirectoryEntry *dir_entry;
13300 struct FileInfo *music, *sound;
13301 struct MusicFileInfo *next, **new;
13304 while (music_file_info != NULL)
13306 next = music_file_info->next;
13308 checked_free(music_file_info->basename);
13310 checked_free(music_file_info->title_header);
13311 checked_free(music_file_info->artist_header);
13312 checked_free(music_file_info->album_header);
13313 checked_free(music_file_info->year_header);
13315 checked_free(music_file_info->title);
13316 checked_free(music_file_info->artist);
13317 checked_free(music_file_info->album);
13318 checked_free(music_file_info->year);
13320 free(music_file_info);
13322 music_file_info = next;
13325 new = &music_file_info;
13327 for (i = 0; i < num_music; i++)
13329 music = getMusicListEntry(i);
13331 if (music->filename == NULL)
13334 if (strEqual(music->filename, UNDEFINED_FILENAME))
13337 // a configured file may be not recognized as music
13338 if (!FileIsMusic(music->filename))
13341 if (!music_info_listed(music_file_info, music->filename))
13343 *new = get_music_file_info(music->filename, i);
13346 new = &(*new)->next;
13350 if ((dir = openDirectory(music_directory)) == NULL)
13352 Warn("cannot read music directory '%s'", music_directory);
13357 while ((dir_entry = readDirectory(dir)) != NULL) // loop all entries
13359 char *basename = dir_entry->basename;
13360 boolean music_already_used = FALSE;
13363 // skip all music files that are configured in music config file
13364 for (i = 0; i < num_music; i++)
13366 music = getMusicListEntry(i);
13368 if (music->filename == NULL)
13371 if (strEqual(basename, music->filename))
13373 music_already_used = TRUE;
13378 if (music_already_used)
13381 if (!FileIsMusic(dir_entry->filename))
13384 if (!music_info_listed(music_file_info, basename))
13386 *new = get_music_file_info(basename, MAP_NOCONF_MUSIC(num_music_noconf));
13389 new = &(*new)->next;
13392 num_music_noconf++;
13395 closeDirectory(dir);
13397 for (i = 0; i < num_sounds; i++)
13399 sound = getSoundListEntry(i);
13401 if (sound->filename == NULL)
13404 if (strEqual(sound->filename, UNDEFINED_FILENAME))
13407 // a configured file may be not recognized as sound
13408 if (!FileIsSound(sound->filename))
13411 if (!sound_info_listed(music_file_info, sound->filename))
13413 *new = get_sound_file_info(sound->filename, i);
13415 new = &(*new)->next;
13420 static void add_helpanim_entry(int element, int action, int direction,
13421 int delay, int *num_list_entries)
13423 struct HelpAnimInfo *new_list_entry;
13424 (*num_list_entries)++;
13427 checked_realloc(helpanim_info,
13428 *num_list_entries * sizeof(struct HelpAnimInfo));
13429 new_list_entry = &helpanim_info[*num_list_entries - 1];
13431 new_list_entry->element = element;
13432 new_list_entry->action = action;
13433 new_list_entry->direction = direction;
13434 new_list_entry->delay = delay;
13437 static void print_unknown_token(char *filename, char *token, int token_nr)
13442 Warn("unknown token(s) found in config file:");
13443 Warn("- config file: '%s'", filename);
13446 Warn("- token: '%s'", token);
13449 static void print_unknown_token_end(int token_nr)
13455 void LoadHelpAnimInfo(void)
13457 char *filename = getHelpAnimFilename();
13458 SetupFileList *setup_file_list = NULL, *list;
13459 SetupFileHash *element_hash, *action_hash, *direction_hash;
13460 int num_list_entries = 0;
13461 int num_unknown_tokens = 0;
13464 if (fileExists(filename))
13465 setup_file_list = loadSetupFileList(filename);
13467 if (setup_file_list == NULL)
13469 // use reliable default values from static configuration
13470 SetupFileList *insert_ptr;
13472 insert_ptr = setup_file_list =
13473 newSetupFileList(helpanim_config[0].token,
13474 helpanim_config[0].value);
13476 for (i = 1; helpanim_config[i].token; i++)
13477 insert_ptr = addListEntry(insert_ptr,
13478 helpanim_config[i].token,
13479 helpanim_config[i].value);
13482 element_hash = newSetupFileHash();
13483 action_hash = newSetupFileHash();
13484 direction_hash = newSetupFileHash();
13486 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
13487 setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
13489 for (i = 0; i < NUM_ACTIONS; i++)
13490 setHashEntry(action_hash, element_action_info[i].suffix,
13491 i_to_a(element_action_info[i].value));
13493 // do not store direction index (bit) here, but direction value!
13494 for (i = 0; i < NUM_DIRECTIONS_FULL; i++)
13495 setHashEntry(direction_hash, element_direction_info[i].suffix,
13496 i_to_a(1 << element_direction_info[i].value));
13498 for (list = setup_file_list; list != NULL; list = list->next)
13500 char *element_token, *action_token, *direction_token;
13501 char *element_value, *action_value, *direction_value;
13502 int delay = atoi(list->value);
13504 if (strEqual(list->token, "end"))
13506 add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
13511 /* first try to break element into element/action/direction parts;
13512 if this does not work, also accept combined "element[.act][.dir]"
13513 elements (like "dynamite.active"), which are unique elements */
13515 if (strchr(list->token, '.') == NULL) // token contains no '.'
13517 element_value = getHashEntry(element_hash, list->token);
13518 if (element_value != NULL) // element found
13519 add_helpanim_entry(atoi(element_value), -1, -1, delay,
13520 &num_list_entries);
13523 // no further suffixes found -- this is not an element
13524 print_unknown_token(filename, list->token, num_unknown_tokens++);
13530 // token has format "<prefix>.<something>"
13532 action_token = strchr(list->token, '.'); // suffix may be action ...
13533 direction_token = action_token; // ... or direction
13535 element_token = getStringCopy(list->token);
13536 *strchr(element_token, '.') = '\0';
13538 element_value = getHashEntry(element_hash, element_token);
13540 if (element_value == NULL) // this is no element
13542 element_value = getHashEntry(element_hash, list->token);
13543 if (element_value != NULL) // combined element found
13544 add_helpanim_entry(atoi(element_value), -1, -1, delay,
13545 &num_list_entries);
13547 print_unknown_token(filename, list->token, num_unknown_tokens++);
13549 free(element_token);
13554 action_value = getHashEntry(action_hash, action_token);
13556 if (action_value != NULL) // action found
13558 add_helpanim_entry(atoi(element_value), atoi(action_value), -1, delay,
13559 &num_list_entries);
13561 free(element_token);
13566 direction_value = getHashEntry(direction_hash, direction_token);
13568 if (direction_value != NULL) // direction found
13570 add_helpanim_entry(atoi(element_value), -1, atoi(direction_value), delay,
13571 &num_list_entries);
13573 free(element_token);
13578 if (strchr(action_token + 1, '.') == NULL)
13580 // no further suffixes found -- this is not an action nor direction
13582 element_value = getHashEntry(element_hash, list->token);
13583 if (element_value != NULL) // combined element found
13584 add_helpanim_entry(atoi(element_value), -1, -1, delay,
13585 &num_list_entries);
13587 print_unknown_token(filename, list->token, num_unknown_tokens++);
13589 free(element_token);
13594 // token has format "<prefix>.<suffix>.<something>"
13596 direction_token = strchr(action_token + 1, '.');
13598 action_token = getStringCopy(action_token);
13599 *strchr(action_token + 1, '.') = '\0';
13601 action_value = getHashEntry(action_hash, action_token);
13603 if (action_value == NULL) // this is no action
13605 element_value = getHashEntry(element_hash, list->token);
13606 if (element_value != NULL) // combined element found
13607 add_helpanim_entry(atoi(element_value), -1, -1, delay,
13608 &num_list_entries);
13610 print_unknown_token(filename, list->token, num_unknown_tokens++);
13612 free(element_token);
13613 free(action_token);
13618 direction_value = getHashEntry(direction_hash, direction_token);
13620 if (direction_value != NULL) // direction found
13622 add_helpanim_entry(atoi(element_value), atoi(action_value),
13623 atoi(direction_value), delay, &num_list_entries);
13625 free(element_token);
13626 free(action_token);
13631 // this is no direction
13633 element_value = getHashEntry(element_hash, list->token);
13634 if (element_value != NULL) // combined element found
13635 add_helpanim_entry(atoi(element_value), -1, -1, delay,
13636 &num_list_entries);
13638 print_unknown_token(filename, list->token, num_unknown_tokens++);
13640 free(element_token);
13641 free(action_token);
13644 print_unknown_token_end(num_unknown_tokens);
13646 add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
13647 add_helpanim_entry(HELPANIM_LIST_END, -1, -1, -1, &num_list_entries);
13649 freeSetupFileList(setup_file_list);
13650 freeSetupFileHash(element_hash);
13651 freeSetupFileHash(action_hash);
13652 freeSetupFileHash(direction_hash);
13655 for (i = 0; i < num_list_entries; i++)
13656 Debug("files:LoadHelpAnimInfo", "'%s': %d, %d, %d => %d",
13657 EL_NAME(helpanim_info[i].element),
13658 helpanim_info[i].element,
13659 helpanim_info[i].action,
13660 helpanim_info[i].direction,
13661 helpanim_info[i].delay);
13665 void LoadHelpTextInfo(void)
13667 char *filename = getHelpTextFilename();
13670 if (helptext_info != NULL)
13672 freeSetupFileHash(helptext_info);
13673 helptext_info = NULL;
13676 if (fileExists(filename))
13677 helptext_info = loadSetupFileHash(filename);
13679 if (helptext_info == NULL)
13681 // use reliable default values from static configuration
13682 helptext_info = newSetupFileHash();
13684 for (i = 0; helptext_config[i].token; i++)
13685 setHashEntry(helptext_info,
13686 helptext_config[i].token,
13687 helptext_config[i].value);
13691 BEGIN_HASH_ITERATION(helptext_info, itr)
13693 Debug("files:LoadHelpTextInfo", "'%s' => '%s'",
13694 HASH_ITERATION_TOKEN(itr), HASH_ITERATION_VALUE(itr));
13696 END_HASH_ITERATION(hash, itr)
13701 // ----------------------------------------------------------------------------
13703 // ----------------------------------------------------------------------------
13705 #define MAX_NUM_CONVERT_LEVELS 1000
13707 void ConvertLevels(void)
13709 static LevelDirTree *convert_leveldir = NULL;
13710 static int convert_level_nr = -1;
13711 static int num_levels_handled = 0;
13712 static int num_levels_converted = 0;
13713 static boolean levels_failed[MAX_NUM_CONVERT_LEVELS];
13716 convert_leveldir = getTreeInfoFromIdentifier(leveldir_first,
13717 global.convert_leveldir);
13719 if (convert_leveldir == NULL)
13720 Fail("no such level identifier: '%s'", global.convert_leveldir);
13722 leveldir_current = convert_leveldir;
13724 if (global.convert_level_nr != -1)
13726 convert_leveldir->first_level = global.convert_level_nr;
13727 convert_leveldir->last_level = global.convert_level_nr;
13730 convert_level_nr = convert_leveldir->first_level;
13732 PrintLine("=", 79);
13733 Print("Converting levels\n");
13734 PrintLine("-", 79);
13735 Print("Level series identifier: '%s'\n", convert_leveldir->identifier);
13736 Print("Level series name: '%s'\n", convert_leveldir->name);
13737 Print("Level series author: '%s'\n", convert_leveldir->author);
13738 Print("Number of levels: %d\n", convert_leveldir->levels);
13739 PrintLine("=", 79);
13742 for (i = 0; i < MAX_NUM_CONVERT_LEVELS; i++)
13743 levels_failed[i] = FALSE;
13745 while (convert_level_nr <= convert_leveldir->last_level)
13747 char *level_filename;
13750 level_nr = convert_level_nr++;
13752 Print("Level %03d: ", level_nr);
13754 LoadLevel(level_nr);
13755 if (level.no_level_file || level.no_valid_file)
13757 Print("(no level)\n");
13761 Print("converting level ... ");
13764 // special case: conversion of some EMC levels as requested by ACME
13765 level.game_engine_type = GAME_ENGINE_TYPE_RND;
13768 level_filename = getDefaultLevelFilename(level_nr);
13769 new_level = !fileExists(level_filename);
13773 SaveLevel(level_nr);
13775 num_levels_converted++;
13777 Print("converted.\n");
13781 if (level_nr >= 0 && level_nr < MAX_NUM_CONVERT_LEVELS)
13782 levels_failed[level_nr] = TRUE;
13784 Print("NOT CONVERTED -- LEVEL ALREADY EXISTS.\n");
13787 num_levels_handled++;
13791 PrintLine("=", 79);
13792 Print("Number of levels handled: %d\n", num_levels_handled);
13793 Print("Number of levels converted: %d (%d%%)\n", num_levels_converted,
13794 (num_levels_handled ?
13795 num_levels_converted * 100 / num_levels_handled : 0));
13796 PrintLine("-", 79);
13797 Print("Summary (for automatic parsing by scripts):\n");
13798 Print("LEVELDIR '%s', CONVERTED %d/%d (%d%%)",
13799 convert_leveldir->identifier, num_levels_converted,
13800 num_levels_handled,
13801 (num_levels_handled ?
13802 num_levels_converted * 100 / num_levels_handled : 0));
13804 if (num_levels_handled != num_levels_converted)
13806 Print(", FAILED:");
13807 for (i = 0; i < MAX_NUM_CONVERT_LEVELS; i++)
13808 if (levels_failed[i])
13813 PrintLine("=", 79);
13815 CloseAllAndExit(0);
13819 // ----------------------------------------------------------------------------
13820 // create and save images for use in level sketches (raw BMP format)
13821 // ----------------------------------------------------------------------------
13823 void CreateLevelSketchImages(void)
13829 InitElementPropertiesGfxElement();
13831 bitmap1 = CreateBitmap(TILEX, TILEY, DEFAULT_DEPTH);
13832 bitmap2 = CreateBitmap(MINI_TILEX, MINI_TILEY, DEFAULT_DEPTH);
13834 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
13836 int element = getMappedElement(i);
13837 char basename1[16];
13838 char basename2[16];
13842 sprintf(basename1, "%04d.bmp", i);
13843 sprintf(basename2, "%04ds.bmp", i);
13845 filename1 = getPath2(global.create_sketch_images_dir, basename1);
13846 filename2 = getPath2(global.create_sketch_images_dir, basename2);
13848 DrawSizedElement(0, 0, element, TILESIZE);
13849 BlitBitmap(drawto, bitmap1, SX, SY, TILEX, TILEY, 0, 0);
13851 if (SDL_SaveBMP(bitmap1->surface, filename1) != 0)
13852 Fail("cannot save level sketch image file '%s'", filename1);
13854 DrawSizedElement(0, 0, element, MINI_TILESIZE);
13855 BlitBitmap(drawto, bitmap2, SX, SY, MINI_TILEX, MINI_TILEY, 0, 0);
13857 if (SDL_SaveBMP(bitmap2->surface, filename2) != 0)
13858 Fail("cannot save level sketch image file '%s'", filename2);
13863 // create corresponding SQL statements (for normal and small images)
13866 printf("insert into phpbb_words values (NULL, '`%03d', '<IMG class=\"levelsketch\" src=\"/I/%04d.png\"/>');\n", i, i);
13867 printf("insert into phpbb_words values (NULL, '¸%03d', '<IMG class=\"levelsketch\" src=\"/I/%04ds.png\"/>');\n", i, i);
13870 printf("insert into phpbb_words values (NULL, '`%04d', '<IMG class=\"levelsketch\" src=\"/I/%04d.png\"/>');\n", i, i);
13871 printf("insert into phpbb_words values (NULL, '¸%04d', '<IMG class=\"levelsketch\" src=\"/I/%04ds.png\"/>');\n", i, i);
13873 // optional: create content for forum level sketch demonstration post
13875 fprintf(stderr, "%03d `%03d%c", i, i, (i % 10 < 9 ? ' ' : '\n'));
13878 FreeBitmap(bitmap1);
13879 FreeBitmap(bitmap2);
13882 fprintf(stderr, "\n");
13884 Info("%d normal and small images created", NUM_FILE_ELEMENTS);
13886 CloseAllAndExit(0);
13890 // ----------------------------------------------------------------------------
13891 // create and save images for element collecting animations (raw BMP format)
13892 // ----------------------------------------------------------------------------
13894 static boolean createCollectImage(int element)
13896 return (IS_COLLECTIBLE(element) && !IS_SP_ELEMENT(element));
13899 void CreateCollectElementImages(void)
13903 int anim_frames = num_steps - 1;
13904 int tile_size = TILESIZE;
13905 int anim_width = tile_size * anim_frames;
13906 int anim_height = tile_size;
13907 int num_collect_images = 0;
13908 int pos_collect_images = 0;
13910 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
13911 if (createCollectImage(i))
13912 num_collect_images++;
13914 Info("Creating %d element collecting animation images ...",
13915 num_collect_images);
13917 int dst_width = anim_width * 2;
13918 int dst_height = anim_height * num_collect_images / 2;
13919 Bitmap *dst_bitmap = CreateBitmap(dst_width, dst_height, DEFAULT_DEPTH);
13920 char *basename = "RocksCollect.bmp";
13921 char *filename = getPath2(global.create_collect_images_dir, basename);
13923 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
13925 if (!createCollectImage(i))
13928 int dst_x = (pos_collect_images / (num_collect_images / 2)) * anim_width;
13929 int dst_y = (pos_collect_images % (num_collect_images / 2)) * anim_height;
13930 int graphic = el2img(i);
13931 char *token_name = element_info[i].token_name;
13932 Bitmap *tmp_bitmap = CreateBitmap(tile_size, tile_size, DEFAULT_DEPTH);
13933 Bitmap *src_bitmap;
13936 Info("- creating collecting image for '%s' ...", token_name);
13938 getGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
13940 BlitBitmap(src_bitmap, tmp_bitmap, src_x, src_y,
13941 tile_size, tile_size, 0, 0);
13943 tmp_bitmap->surface_masked = tmp_bitmap->surface;
13945 for (j = 0; j < anim_frames; j++)
13947 int frame_size_final = tile_size * (anim_frames - j) / num_steps;
13948 int frame_size = frame_size_final * num_steps;
13949 int offset = (tile_size - frame_size_final) / 2;
13950 Bitmap *frame_bitmap = ZoomBitmap(tmp_bitmap, frame_size, frame_size);
13952 while (frame_size > frame_size_final)
13956 Bitmap *half_bitmap = ZoomBitmap(frame_bitmap, frame_size, frame_size);
13958 FreeBitmap(frame_bitmap);
13960 frame_bitmap = half_bitmap;
13963 BlitBitmap(frame_bitmap, dst_bitmap, 0, 0,
13964 frame_size_final, frame_size_final,
13965 dst_x + j * tile_size + offset, dst_y + offset);
13967 FreeBitmap(frame_bitmap);
13970 tmp_bitmap->surface_masked = NULL;
13972 FreeBitmap(tmp_bitmap);
13974 pos_collect_images++;
13977 if (SDL_SaveBMP(dst_bitmap->surface, filename) != 0)
13978 Fail("cannot save element collecting image file '%s'", filename);
13980 FreeBitmap(dst_bitmap);
13984 CloseAllAndExit(0);
13988 // ----------------------------------------------------------------------------
13989 // create and save images for custom and group elements (raw BMP format)
13990 // ----------------------------------------------------------------------------
13992 void CreateCustomElementImages(char *directory)
13994 char *src_basename = "RocksCE-template.ilbm";
13995 char *dst_basename = "RocksCE.bmp";
13996 char *src_filename = getPath2(directory, src_basename);
13997 char *dst_filename = getPath2(directory, dst_basename);
13998 Bitmap *src_bitmap;
14000 int yoffset_ce = 0;
14001 int yoffset_ge = (TILEY * NUM_CUSTOM_ELEMENTS / 16);
14004 InitVideoDefaults();
14006 ReCreateBitmap(&backbuffer, video.width, video.height);
14008 src_bitmap = LoadImage(src_filename);
14010 bitmap = CreateBitmap(TILEX * 16 * 2,
14011 TILEY * (NUM_CUSTOM_ELEMENTS + NUM_GROUP_ELEMENTS) / 16,
14014 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
14021 BlitBitmap(src_bitmap, bitmap, 0, 0, TILEX, TILEY,
14022 TILEX * x, TILEY * y + yoffset_ce);
14024 BlitBitmap(src_bitmap, bitmap, 0, TILEY,
14026 TILEX * x + TILEX * 16,
14027 TILEY * y + yoffset_ce);
14029 for (j = 2; j >= 0; j--)
14033 BlitBitmap(src_bitmap, bitmap,
14034 TILEX + c * 7, 0, 6, 10,
14035 TILEX * x + 6 + j * 7,
14036 TILEY * y + 11 + yoffset_ce);
14038 BlitBitmap(src_bitmap, bitmap,
14039 TILEX + c * 8, TILEY, 6, 10,
14040 TILEX * 16 + TILEX * x + 6 + j * 8,
14041 TILEY * y + 10 + yoffset_ce);
14047 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
14054 BlitBitmap(src_bitmap, bitmap, 0, 0, TILEX, TILEY,
14055 TILEX * x, TILEY * y + yoffset_ge);
14057 BlitBitmap(src_bitmap, bitmap, 0, TILEY,
14059 TILEX * x + TILEX * 16,
14060 TILEY * y + yoffset_ge);
14062 for (j = 1; j >= 0; j--)
14066 BlitBitmap(src_bitmap, bitmap, TILEX + c * 10, 11, 10, 10,
14067 TILEX * x + 6 + j * 10,
14068 TILEY * y + 11 + yoffset_ge);
14070 BlitBitmap(src_bitmap, bitmap,
14071 TILEX + c * 8, TILEY + 12, 6, 10,
14072 TILEX * 16 + TILEX * x + 10 + j * 8,
14073 TILEY * y + 10 + yoffset_ge);
14079 if (SDL_SaveBMP(bitmap->surface, dst_filename) != 0)
14080 Fail("cannot save CE graphics file '%s'", dst_filename);
14082 FreeBitmap(bitmap);
14084 CloseAllAndExit(0);