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;
8654 scores->entry[i].id = -1;
8655 strcpy(scores->entry[i].tape_date, UNKNOWN_NAME);
8656 strcpy(scores->entry[i].platform, UNKNOWN_NAME);
8657 strcpy(scores->entry[i].version, UNKNOWN_NAME);
8658 strcpy(scores->entry[i].country_name, UNKNOWN_NAME);
8659 strcpy(scores->entry[i].country_code, "??");
8662 scores->num_entries = 0;
8663 scores->last_added = -1;
8664 scores->last_added_local = -1;
8666 scores->updated = FALSE;
8667 scores->uploaded = FALSE;
8668 scores->force_last_added = FALSE;
8671 static void setScoreInfoToDefaults(void)
8673 setScoreInfoToDefaultsExt(&scores);
8676 static void setServerScoreInfoToDefaults(void)
8678 setScoreInfoToDefaultsExt(&server_scores);
8681 static void LoadScore_OLD(int nr)
8684 char *filename = getScoreFilename(nr);
8685 char cookie[MAX_LINE_LEN];
8686 char line[MAX_LINE_LEN];
8690 if (!(file = fopen(filename, MODE_READ)))
8693 // check file identifier
8694 if (fgets(cookie, MAX_LINE_LEN, file) == NULL)
8696 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
8697 cookie[strlen(cookie) - 1] = '\0';
8699 if (!checkCookieString(cookie, SCORE_COOKIE_TMPL))
8701 Warn("unknown format of score file '%s'", filename);
8708 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
8710 if (fscanf(file, "%d", &scores.entry[i].score) == EOF)
8711 Warn("fscanf() failed; %s", strerror(errno));
8713 if (fgets(line, MAX_LINE_LEN, file) == NULL)
8716 if (strlen(line) > 0 && line[strlen(line) - 1] == '\n')
8717 line[strlen(line) - 1] = '\0';
8719 for (line_ptr = line; *line_ptr; line_ptr++)
8721 if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != '\0')
8723 strncpy(scores.entry[i].name, line_ptr, MAX_PLAYER_NAME_LEN);
8724 scores.entry[i].name[MAX_PLAYER_NAME_LEN] = '\0';
8733 static void ConvertScore_OLD(void)
8735 // only convert score to time for levels that rate playing time over score
8736 if (!level.rate_time_over_score)
8739 // convert old score to playing time for score-less levels (like Supaplex)
8740 int time_final_max = 999;
8743 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
8745 int score = scores.entry[i].score;
8747 if (score > 0 && score < time_final_max)
8748 scores.entry[i].time = (time_final_max - score - 1) * FRAMES_PER_SECOND;
8752 static int LoadScore_VERS(File *file, int chunk_size, struct ScoreInfo *scores)
8754 scores->file_version = getFileVersion(file);
8755 scores->game_version = getFileVersion(file);
8760 static int LoadScore_INFO(File *file, int chunk_size, struct ScoreInfo *scores)
8762 char *level_identifier = NULL;
8763 int level_identifier_size;
8766 level_identifier_size = getFile16BitBE(file);
8768 level_identifier = checked_malloc(level_identifier_size);
8770 for (i = 0; i < level_identifier_size; i++)
8771 level_identifier[i] = getFile8Bit(file);
8773 strncpy(scores->level_identifier, level_identifier, MAX_FILENAME_LEN);
8774 scores->level_identifier[MAX_FILENAME_LEN] = '\0';
8776 checked_free(level_identifier);
8778 scores->level_nr = getFile16BitBE(file);
8779 scores->num_entries = getFile16BitBE(file);
8781 chunk_size = 2 + level_identifier_size + 2 + 2;
8786 static int LoadScore_NAME(File *file, int chunk_size, struct ScoreInfo *scores)
8790 for (i = 0; i < scores->num_entries; i++)
8792 for (j = 0; j < MAX_PLAYER_NAME_LEN; j++)
8793 scores->entry[i].name[j] = getFile8Bit(file);
8795 scores->entry[i].name[MAX_PLAYER_NAME_LEN] = '\0';
8798 chunk_size = scores->num_entries * MAX_PLAYER_NAME_LEN;
8803 static int LoadScore_SCOR(File *file, int chunk_size, struct ScoreInfo *scores)
8807 for (i = 0; i < scores->num_entries; i++)
8808 scores->entry[i].score = getFile16BitBE(file);
8810 chunk_size = scores->num_entries * 2;
8815 static int LoadScore_SC4R(File *file, int chunk_size, struct ScoreInfo *scores)
8819 for (i = 0; i < scores->num_entries; i++)
8820 scores->entry[i].score = getFile32BitBE(file);
8822 chunk_size = scores->num_entries * 4;
8827 static int LoadScore_TIME(File *file, int chunk_size, struct ScoreInfo *scores)
8831 for (i = 0; i < scores->num_entries; i++)
8832 scores->entry[i].time = getFile32BitBE(file);
8834 chunk_size = scores->num_entries * 4;
8839 static int LoadScore_TAPE(File *file, int chunk_size, struct ScoreInfo *scores)
8843 for (i = 0; i < scores->num_entries; i++)
8845 for (j = 0; j < MAX_SCORE_TAPE_BASENAME_LEN; j++)
8846 scores->entry[i].tape_basename[j] = getFile8Bit(file);
8848 scores->entry[i].tape_basename[MAX_SCORE_TAPE_BASENAME_LEN] = '\0';
8851 chunk_size = scores->num_entries * MAX_SCORE_TAPE_BASENAME_LEN;
8856 void LoadScore(int nr)
8858 char *filename = getScoreFilename(nr);
8859 char cookie[MAX_LINE_LEN];
8860 char chunk_name[CHUNK_ID_LEN + 1];
8862 boolean old_score_file_format = FALSE;
8865 // always start with reliable default values
8866 setScoreInfoToDefaults();
8868 if (!(file = openFile(filename, MODE_READ)))
8871 getFileChunkBE(file, chunk_name, NULL);
8872 if (strEqual(chunk_name, "RND1"))
8874 getFile32BitBE(file); // not used
8876 getFileChunkBE(file, chunk_name, NULL);
8877 if (!strEqual(chunk_name, "SCOR"))
8879 Warn("unknown format of score file '%s'", filename);
8886 else // check for old file format with cookie string
8888 strcpy(cookie, chunk_name);
8889 if (getStringFromFile(file, &cookie[4], MAX_LINE_LEN - 4) == NULL)
8891 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
8892 cookie[strlen(cookie) - 1] = '\0';
8894 if (!checkCookieString(cookie, SCORE_COOKIE_TMPL))
8896 Warn("unknown format of score file '%s'", filename);
8903 old_score_file_format = TRUE;
8906 if (old_score_file_format)
8908 // score files from versions before 4.2.4.0 without chunk structure
8911 // convert score to time, if possible (mainly for Supaplex levels)
8920 int (*loader)(File *, int, struct ScoreInfo *);
8924 { "VERS", SCORE_CHUNK_VERS_SIZE, LoadScore_VERS },
8925 { "INFO", -1, LoadScore_INFO },
8926 { "NAME", -1, LoadScore_NAME },
8927 { "SCOR", -1, LoadScore_SCOR },
8928 { "SC4R", -1, LoadScore_SC4R },
8929 { "TIME", -1, LoadScore_TIME },
8930 { "TAPE", -1, LoadScore_TAPE },
8935 while (getFileChunkBE(file, chunk_name, &chunk_size))
8939 while (chunk_info[i].name != NULL &&
8940 !strEqual(chunk_name, chunk_info[i].name))
8943 if (chunk_info[i].name == NULL)
8945 Warn("unknown chunk '%s' in score file '%s'",
8946 chunk_name, filename);
8948 ReadUnusedBytesFromFile(file, chunk_size);
8950 else if (chunk_info[i].size != -1 &&
8951 chunk_info[i].size != chunk_size)
8953 Warn("wrong size (%d) of chunk '%s' in score file '%s'",
8954 chunk_size, chunk_name, filename);
8956 ReadUnusedBytesFromFile(file, chunk_size);
8960 // call function to load this score chunk
8961 int chunk_size_expected =
8962 (chunk_info[i].loader)(file, chunk_size, &scores);
8964 // the size of some chunks cannot be checked before reading other
8965 // chunks first (like "HEAD" and "BODY") that contain some header
8966 // information, so check them here
8967 if (chunk_size_expected != chunk_size)
8969 Warn("wrong size (%d) of chunk '%s' in score file '%s'",
8970 chunk_size, chunk_name, filename);
8979 #if ENABLE_HISTORIC_CHUNKS
8980 void SaveScore_OLD(int nr)
8983 char *filename = getScoreFilename(nr);
8986 // used instead of "leveldir_current->subdir" (for network games)
8987 InitScoreDirectory(levelset.identifier);
8989 if (!(file = fopen(filename, MODE_WRITE)))
8991 Warn("cannot save score for level %d", nr);
8996 fprintf(file, "%s\n\n", SCORE_COOKIE);
8998 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
8999 fprintf(file, "%d %s\n", scores.entry[i].score, scores.entry[i].name);
9003 SetFilePermissions(filename, PERMS_PRIVATE);
9007 static void SaveScore_VERS(FILE *file, struct ScoreInfo *scores)
9009 putFileVersion(file, scores->file_version);
9010 putFileVersion(file, scores->game_version);
9013 static void SaveScore_INFO(FILE *file, struct ScoreInfo *scores)
9015 int level_identifier_size = strlen(scores->level_identifier) + 1;
9018 putFile16BitBE(file, level_identifier_size);
9020 for (i = 0; i < level_identifier_size; i++)
9021 putFile8Bit(file, scores->level_identifier[i]);
9023 putFile16BitBE(file, scores->level_nr);
9024 putFile16BitBE(file, scores->num_entries);
9027 static void SaveScore_NAME(FILE *file, struct ScoreInfo *scores)
9031 for (i = 0; i < scores->num_entries; i++)
9033 int name_size = strlen(scores->entry[i].name);
9035 for (j = 0; j < MAX_PLAYER_NAME_LEN; j++)
9036 putFile8Bit(file, (j < name_size ? scores->entry[i].name[j] : 0));
9040 static void SaveScore_SCOR(FILE *file, struct ScoreInfo *scores)
9044 for (i = 0; i < scores->num_entries; i++)
9045 putFile16BitBE(file, scores->entry[i].score);
9048 static void SaveScore_SC4R(FILE *file, struct ScoreInfo *scores)
9052 for (i = 0; i < scores->num_entries; i++)
9053 putFile32BitBE(file, scores->entry[i].score);
9056 static void SaveScore_TIME(FILE *file, struct ScoreInfo *scores)
9060 for (i = 0; i < scores->num_entries; i++)
9061 putFile32BitBE(file, scores->entry[i].time);
9064 static void SaveScore_TAPE(FILE *file, struct ScoreInfo *scores)
9068 for (i = 0; i < scores->num_entries; i++)
9070 int size = strlen(scores->entry[i].tape_basename);
9072 for (j = 0; j < MAX_SCORE_TAPE_BASENAME_LEN; j++)
9073 putFile8Bit(file, (j < size ? scores->entry[i].tape_basename[j] : 0));
9077 static void SaveScoreToFilename(char *filename)
9080 int info_chunk_size;
9081 int name_chunk_size;
9082 int scor_chunk_size;
9083 int sc4r_chunk_size;
9084 int time_chunk_size;
9085 int tape_chunk_size;
9086 boolean has_large_score_values;
9089 if (!(file = fopen(filename, MODE_WRITE)))
9091 Warn("cannot save score file '%s'", filename);
9096 info_chunk_size = 2 + (strlen(scores.level_identifier) + 1) + 2 + 2;
9097 name_chunk_size = scores.num_entries * MAX_PLAYER_NAME_LEN;
9098 scor_chunk_size = scores.num_entries * 2;
9099 sc4r_chunk_size = scores.num_entries * 4;
9100 time_chunk_size = scores.num_entries * 4;
9101 tape_chunk_size = scores.num_entries * MAX_SCORE_TAPE_BASENAME_LEN;
9103 has_large_score_values = FALSE;
9104 for (i = 0; i < scores.num_entries; i++)
9105 if (scores.entry[i].score > 0xffff)
9106 has_large_score_values = TRUE;
9108 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
9109 putFileChunkBE(file, "SCOR", CHUNK_SIZE_NONE);
9111 putFileChunkBE(file, "VERS", SCORE_CHUNK_VERS_SIZE);
9112 SaveScore_VERS(file, &scores);
9114 putFileChunkBE(file, "INFO", info_chunk_size);
9115 SaveScore_INFO(file, &scores);
9117 putFileChunkBE(file, "NAME", name_chunk_size);
9118 SaveScore_NAME(file, &scores);
9120 if (has_large_score_values)
9122 putFileChunkBE(file, "SC4R", sc4r_chunk_size);
9123 SaveScore_SC4R(file, &scores);
9127 putFileChunkBE(file, "SCOR", scor_chunk_size);
9128 SaveScore_SCOR(file, &scores);
9131 putFileChunkBE(file, "TIME", time_chunk_size);
9132 SaveScore_TIME(file, &scores);
9134 putFileChunkBE(file, "TAPE", tape_chunk_size);
9135 SaveScore_TAPE(file, &scores);
9139 SetFilePermissions(filename, PERMS_PRIVATE);
9142 void SaveScore(int nr)
9144 char *filename = getScoreFilename(nr);
9147 // used instead of "leveldir_current->subdir" (for network games)
9148 InitScoreDirectory(levelset.identifier);
9150 scores.file_version = FILE_VERSION_ACTUAL;
9151 scores.game_version = GAME_VERSION_ACTUAL;
9153 strncpy(scores.level_identifier, levelset.identifier, MAX_FILENAME_LEN);
9154 scores.level_identifier[MAX_FILENAME_LEN] = '\0';
9155 scores.level_nr = level_nr;
9157 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9158 if (scores.entry[i].score == 0 &&
9159 scores.entry[i].time == 0 &&
9160 strEqual(scores.entry[i].name, EMPTY_PLAYER_NAME))
9163 scores.num_entries = i;
9165 if (scores.num_entries == 0)
9168 SaveScoreToFilename(filename);
9171 void ExecuteAsThread(SDL_ThreadFunction function, char *name, void *data,
9174 #if defined(PLATFORM_EMSCRIPTEN)
9175 // threads currently not fully supported by Emscripten/SDL and some browsers
9178 SDL_Thread *thread = SDL_CreateThread(function, name, data);
9181 SDL_DetachThread(thread);
9183 Error("Cannot create thread to %s!", error);
9185 // nasty kludge to lower probability of intermingled thread error messages
9190 char *getPasswordJSON(char *password)
9192 static char password_json[MAX_FILENAME_LEN] = "";
9193 static boolean initialized = FALSE;
9197 if (password != NULL &&
9198 !strEqual(password, "") &&
9199 !strEqual(password, UNDEFINED_PASSWORD))
9200 snprintf(password_json, MAX_FILENAME_LEN,
9201 " \"password\": \"%s\",\n",
9202 setup.api_server_password);
9207 return password_json;
9210 struct ApiGetScoreThreadData
9213 char *score_cache_filename;
9216 static void *CreateThreadData_ApiGetScore(int nr)
9218 struct ApiGetScoreThreadData *data =
9219 checked_malloc(sizeof(struct ApiGetScoreThreadData));
9220 char *score_cache_filename = getScoreCacheFilename(nr);
9222 data->level_nr = nr;
9223 data->score_cache_filename = getStringCopy(score_cache_filename);
9228 static void FreeThreadData_ApiGetScore(void *data_raw)
9230 struct ApiGetScoreThreadData *data = data_raw;
9232 checked_free(data->score_cache_filename);
9236 static boolean SetRequest_ApiGetScore(struct HttpRequest *request,
9239 struct ApiGetScoreThreadData *data = data_raw;
9240 int level_nr = data->level_nr;
9242 request->hostname = setup.api_server_hostname;
9243 request->port = API_SERVER_PORT;
9244 request->method = API_SERVER_METHOD;
9245 request->uri = API_SERVER_URI_GET;
9247 char *levelset_identifier = getEscapedJSON(leveldir_current->identifier);
9248 char *levelset_name = getEscapedJSON(leveldir_current->name);
9250 snprintf(request->body, MAX_HTTP_BODY_SIZE,
9253 " \"game_version\": \"%s\",\n"
9254 " \"game_platform\": \"%s\",\n"
9255 " \"levelset_identifier\": \"%s\",\n"
9256 " \"levelset_name\": \"%s\",\n"
9257 " \"level_nr\": \"%d\"\n"
9259 getPasswordJSON(setup.api_server_password),
9260 getProgramRealVersionString(),
9261 getProgramPlatformString(),
9262 levelset_identifier,
9266 checked_free(levelset_identifier);
9267 checked_free(levelset_name);
9269 ConvertHttpRequestBodyToServerEncoding(request);
9274 static void HandleResponse_ApiGetScore(struct HttpResponse *response,
9277 struct ApiGetScoreThreadData *data = data_raw;
9279 if (response->body_size == 0)
9281 // no scores available for this level
9286 ConvertHttpResponseBodyToClientEncoding(response);
9288 char *filename = data->score_cache_filename;
9292 // used instead of "leveldir_current->subdir" (for network games)
9293 InitScoreCacheDirectory(levelset.identifier);
9295 if (!(file = fopen(filename, MODE_WRITE)))
9297 Warn("cannot save score cache file '%s'", filename);
9302 for (i = 0; i < response->body_size; i++)
9303 fputc(response->body[i], file);
9307 SetFilePermissions(filename, PERMS_PRIVATE);
9309 server_scores.updated = TRUE;
9312 #if defined(PLATFORM_EMSCRIPTEN)
9313 static void Emscripten_ApiGetScore_Loaded(unsigned handle, void *data_raw,
9314 void *buffer, unsigned int size)
9316 struct HttpResponse *response = GetHttpResponseFromBuffer(buffer, size);
9318 if (response != NULL)
9320 HandleResponse_ApiGetScore(response, data_raw);
9322 checked_free(response);
9326 Error("server response too large to handle (%d bytes)", size);
9329 FreeThreadData_ApiGetScore(data_raw);
9332 static void Emscripten_ApiGetScore_Failed(unsigned handle, void *data_raw,
9333 int code, const char *status)
9335 Error("server failed to handle request: %d %s", code, status);
9337 FreeThreadData_ApiGetScore(data_raw);
9340 static void Emscripten_ApiGetScore_Progress(unsigned handle, void *data_raw,
9341 int bytes, int size)
9343 // nothing to do here
9346 static void Emscripten_ApiGetScore_HttpRequest(struct HttpRequest *request,
9349 if (!SetRequest_ApiGetScore(request, data_raw))
9351 FreeThreadData_ApiGetScore(data_raw);
9356 emscripten_async_wget2_data(request->uri,
9361 Emscripten_ApiGetScore_Loaded,
9362 Emscripten_ApiGetScore_Failed,
9363 Emscripten_ApiGetScore_Progress);
9368 static void ApiGetScore_HttpRequestExt(struct HttpRequest *request,
9369 struct HttpResponse *response,
9372 if (!SetRequest_ApiGetScore(request, data_raw))
9375 if (!DoHttpRequest(request, response))
9377 Error("HTTP request failed: %s", GetHttpError());
9382 if (!HTTP_SUCCESS(response->status_code))
9384 // do not show error message if no scores found for this level set
9385 if (response->status_code == 404)
9388 Error("server failed to handle request: %d %s",
9389 response->status_code,
9390 response->status_text);
9395 HandleResponse_ApiGetScore(response, data_raw);
9398 static void ApiGetScore_HttpRequest(struct HttpRequest *request,
9399 struct HttpResponse *response,
9402 ApiGetScore_HttpRequestExt(request, response, data_raw);
9404 FreeThreadData_ApiGetScore(data_raw);
9408 static int ApiGetScoreThread(void *data_raw)
9410 struct HttpRequest *request = checked_calloc(sizeof(struct HttpRequest));
9411 struct HttpResponse *response = checked_calloc(sizeof(struct HttpResponse));
9413 program.api_thread_count++;
9415 #if defined(PLATFORM_EMSCRIPTEN)
9416 Emscripten_ApiGetScore_HttpRequest(request, data_raw);
9418 ApiGetScore_HttpRequest(request, response, data_raw);
9421 program.api_thread_count--;
9423 checked_free(request);
9424 checked_free(response);
9429 static void ApiGetScoreAsThread(int nr)
9431 struct ApiGetScoreThreadData *data = CreateThreadData_ApiGetScore(nr);
9433 ExecuteAsThread(ApiGetScoreThread,
9434 "ApiGetScore", data,
9435 "download scores from server");
9438 static void LoadServerScoreFromCache(int nr)
9440 struct ScoreEntry score_entry;
9449 { &score_entry.score, FALSE, 0 },
9450 { &score_entry.time, FALSE, 0 },
9451 { score_entry.name, TRUE, MAX_PLAYER_NAME_LEN },
9452 { score_entry.tape_basename, TRUE, MAX_FILENAME_LEN },
9453 { score_entry.tape_date, TRUE, MAX_ISO_DATE_LEN },
9454 { &score_entry.id, FALSE, 0 },
9455 { score_entry.platform, TRUE, MAX_PLATFORM_TEXT_LEN },
9456 { score_entry.version, TRUE, MAX_VERSION_TEXT_LEN },
9457 { score_entry.country_code, TRUE, MAX_COUNTRY_CODE_LEN },
9458 { score_entry.country_name, TRUE, MAX_COUNTRY_NAME_LEN },
9462 char *filename = getScoreCacheFilename(nr);
9463 SetupFileHash *score_hash = loadSetupFileHash(filename);
9466 server_scores.num_entries = 0;
9468 if (score_hash == NULL)
9471 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9473 score_entry = server_scores.entry[i];
9475 for (j = 0; score_mapping[j].value != NULL; j++)
9479 sprintf(token, "%02d.%d", i, j);
9481 char *value = getHashEntry(score_hash, token);
9486 if (score_mapping[j].is_string)
9488 char *score_value = (char *)score_mapping[j].value;
9489 int value_size = score_mapping[j].string_size;
9491 strncpy(score_value, value, value_size);
9492 score_value[value_size] = '\0';
9496 int *score_value = (int *)score_mapping[j].value;
9498 *score_value = atoi(value);
9501 server_scores.num_entries = i + 1;
9504 server_scores.entry[i] = score_entry;
9507 freeSetupFileHash(score_hash);
9510 void LoadServerScore(int nr, boolean download_score)
9512 if (!setup.use_api_server)
9515 // always start with reliable default values
9516 setServerScoreInfoToDefaults();
9518 // 1st step: load server scores from cache file (which may not exist)
9519 // (this should prevent reading it while the thread is writing to it)
9520 LoadServerScoreFromCache(nr);
9522 if (download_score && runtime.use_api_server)
9524 // 2nd step: download server scores from score server to cache file
9525 // (as thread, as it might time out if the server is not reachable)
9526 ApiGetScoreAsThread(nr);
9530 static char *get_file_base64(char *filename)
9532 struct stat file_status;
9534 if (stat(filename, &file_status) != 0)
9536 Error("cannot stat file '%s'", filename);
9541 int buffer_size = file_status.st_size;
9542 byte *buffer = checked_malloc(buffer_size);
9546 if (!(file = fopen(filename, MODE_READ)))
9548 Error("cannot open file '%s'", filename);
9550 checked_free(buffer);
9555 for (i = 0; i < buffer_size; i++)
9557 int c = fgetc(file);
9561 Error("cannot read from input file '%s'", filename);
9564 checked_free(buffer);
9569 buffer[i] = (byte)c;
9574 int buffer_encoded_size = base64_encoded_size(buffer_size);
9575 char *buffer_encoded = checked_malloc(buffer_encoded_size);
9577 base64_encode(buffer_encoded, buffer, buffer_size);
9579 checked_free(buffer);
9581 return buffer_encoded;
9584 static void PrepareScoreTapesForUpload(char *leveldir_subdir)
9586 MarkTapeDirectoryUploadsAsIncomplete(leveldir_subdir);
9588 // if score tape not uploaded, ask for uploading missing tapes later
9589 if (!setup.has_remaining_tapes)
9590 setup.ask_for_remaining_tapes = TRUE;
9592 setup.provide_uploading_tapes = TRUE;
9593 setup.has_remaining_tapes = TRUE;
9595 SaveSetup_ServerSetup();
9598 struct ApiAddScoreThreadData
9602 char *leveldir_subdir;
9603 char *score_tape_filename;
9604 struct ScoreEntry score_entry;
9607 static void *CreateThreadData_ApiAddScore(int nr, boolean tape_saved,
9608 char *score_tape_filename)
9610 struct ApiAddScoreThreadData *data =
9611 checked_malloc(sizeof(struct ApiAddScoreThreadData));
9612 struct ScoreEntry *score_entry = &scores.entry[scores.last_added];
9614 if (score_tape_filename == NULL)
9615 score_tape_filename = getScoreTapeFilename(score_entry->tape_basename, nr);
9617 data->level_nr = nr;
9618 data->tape_saved = tape_saved;
9619 data->leveldir_subdir = getStringCopy(leveldir_current->subdir);
9620 data->score_tape_filename = getStringCopy(score_tape_filename);
9621 data->score_entry = *score_entry;
9626 static void FreeThreadData_ApiAddScore(void *data_raw)
9628 struct ApiAddScoreThreadData *data = data_raw;
9630 checked_free(data->leveldir_subdir);
9631 checked_free(data->score_tape_filename);
9635 static boolean SetRequest_ApiAddScore(struct HttpRequest *request,
9638 struct ApiAddScoreThreadData *data = data_raw;
9639 struct ScoreEntry *score_entry = &data->score_entry;
9640 char *score_tape_filename = data->score_tape_filename;
9641 boolean tape_saved = data->tape_saved;
9642 int level_nr = data->level_nr;
9644 request->hostname = setup.api_server_hostname;
9645 request->port = API_SERVER_PORT;
9646 request->method = API_SERVER_METHOD;
9647 request->uri = API_SERVER_URI_ADD;
9649 char *tape_base64 = get_file_base64(score_tape_filename);
9651 if (tape_base64 == NULL)
9653 Error("loading and base64 encoding score tape file failed");
9658 char *player_name_raw = score_entry->name;
9659 char *player_uuid_raw = setup.player_uuid;
9661 if (options.player_name != NULL && global.autoplay_leveldir != NULL)
9663 player_name_raw = options.player_name;
9664 player_uuid_raw = "";
9667 char *levelset_identifier = getEscapedJSON(leveldir_current->identifier);
9668 char *levelset_name = getEscapedJSON(leveldir_current->name);
9669 char *levelset_author = getEscapedJSON(leveldir_current->author);
9670 char *level_name = getEscapedJSON(level.name);
9671 char *level_author = getEscapedJSON(level.author);
9672 char *player_name = getEscapedJSON(player_name_raw);
9673 char *player_uuid = getEscapedJSON(player_uuid_raw);
9675 snprintf(request->body, MAX_HTTP_BODY_SIZE,
9678 " \"game_version\": \"%s\",\n"
9679 " \"game_platform\": \"%s\",\n"
9680 " \"batch_time\": \"%d\",\n"
9681 " \"levelset_identifier\": \"%s\",\n"
9682 " \"levelset_name\": \"%s\",\n"
9683 " \"levelset_author\": \"%s\",\n"
9684 " \"levelset_num_levels\": \"%d\",\n"
9685 " \"levelset_first_level\": \"%d\",\n"
9686 " \"level_nr\": \"%d\",\n"
9687 " \"level_name\": \"%s\",\n"
9688 " \"level_author\": \"%s\",\n"
9689 " \"use_step_counter\": \"%d\",\n"
9690 " \"rate_time_over_score\": \"%d\",\n"
9691 " \"player_name\": \"%s\",\n"
9692 " \"player_uuid\": \"%s\",\n"
9693 " \"score\": \"%d\",\n"
9694 " \"time\": \"%d\",\n"
9695 " \"tape_basename\": \"%s\",\n"
9696 " \"tape_saved\": \"%d\",\n"
9697 " \"tape\": \"%s\"\n"
9699 getPasswordJSON(setup.api_server_password),
9700 getProgramRealVersionString(),
9701 getProgramPlatformString(),
9702 (int)global.autoplay_time,
9703 levelset_identifier,
9706 leveldir_current->levels,
9707 leveldir_current->first_level,
9711 level.use_step_counter,
9712 level.rate_time_over_score,
9717 score_entry->tape_basename,
9721 checked_free(tape_base64);
9723 checked_free(levelset_identifier);
9724 checked_free(levelset_name);
9725 checked_free(levelset_author);
9726 checked_free(level_name);
9727 checked_free(level_author);
9728 checked_free(player_name);
9729 checked_free(player_uuid);
9731 ConvertHttpRequestBodyToServerEncoding(request);
9736 static void HandleResponse_ApiAddScore(struct HttpResponse *response,
9739 server_scores.uploaded = TRUE;
9742 static void HandleFailure_ApiAddScore(void *data_raw)
9744 struct ApiAddScoreThreadData *data = data_raw;
9746 PrepareScoreTapesForUpload(data->leveldir_subdir);
9749 #if defined(PLATFORM_EMSCRIPTEN)
9750 static void Emscripten_ApiAddScore_Loaded(unsigned handle, void *data_raw,
9751 void *buffer, unsigned int size)
9753 struct HttpResponse *response = GetHttpResponseFromBuffer(buffer, size);
9755 if (response != NULL)
9757 HandleResponse_ApiAddScore(response, data_raw);
9759 checked_free(response);
9763 Error("server response too large to handle (%d bytes)", size);
9765 HandleFailure_ApiAddScore(data_raw);
9768 FreeThreadData_ApiAddScore(data_raw);
9771 static void Emscripten_ApiAddScore_Failed(unsigned handle, void *data_raw,
9772 int code, const char *status)
9774 Error("server failed to handle request: %d %s", code, status);
9776 HandleFailure_ApiAddScore(data_raw);
9778 FreeThreadData_ApiAddScore(data_raw);
9781 static void Emscripten_ApiAddScore_Progress(unsigned handle, void *data_raw,
9782 int bytes, int size)
9784 // nothing to do here
9787 static void Emscripten_ApiAddScore_HttpRequest(struct HttpRequest *request,
9790 if (!SetRequest_ApiAddScore(request, data_raw))
9792 FreeThreadData_ApiAddScore(data_raw);
9797 emscripten_async_wget2_data(request->uri,
9802 Emscripten_ApiAddScore_Loaded,
9803 Emscripten_ApiAddScore_Failed,
9804 Emscripten_ApiAddScore_Progress);
9809 static void ApiAddScore_HttpRequestExt(struct HttpRequest *request,
9810 struct HttpResponse *response,
9813 if (!SetRequest_ApiAddScore(request, data_raw))
9816 if (!DoHttpRequest(request, response))
9818 Error("HTTP request failed: %s", GetHttpError());
9820 HandleFailure_ApiAddScore(data_raw);
9825 if (!HTTP_SUCCESS(response->status_code))
9827 Error("server failed to handle request: %d %s",
9828 response->status_code,
9829 response->status_text);
9831 HandleFailure_ApiAddScore(data_raw);
9836 HandleResponse_ApiAddScore(response, data_raw);
9839 static void ApiAddScore_HttpRequest(struct HttpRequest *request,
9840 struct HttpResponse *response,
9843 ApiAddScore_HttpRequestExt(request, response, data_raw);
9845 FreeThreadData_ApiAddScore(data_raw);
9849 static int ApiAddScoreThread(void *data_raw)
9851 struct HttpRequest *request = checked_calloc(sizeof(struct HttpRequest));
9852 struct HttpResponse *response = checked_calloc(sizeof(struct HttpResponse));
9854 program.api_thread_count++;
9856 #if defined(PLATFORM_EMSCRIPTEN)
9857 Emscripten_ApiAddScore_HttpRequest(request, data_raw);
9859 ApiAddScore_HttpRequest(request, response, data_raw);
9862 program.api_thread_count--;
9864 checked_free(request);
9865 checked_free(response);
9870 static void ApiAddScoreAsThread(int nr, boolean tape_saved,
9871 char *score_tape_filename)
9873 struct ApiAddScoreThreadData *data =
9874 CreateThreadData_ApiAddScore(nr, tape_saved, score_tape_filename);
9876 ExecuteAsThread(ApiAddScoreThread,
9877 "ApiAddScore", data,
9878 "upload score to server");
9881 void SaveServerScore(int nr, boolean tape_saved)
9883 if (!runtime.use_api_server)
9885 PrepareScoreTapesForUpload(leveldir_current->subdir);
9890 ApiAddScoreAsThread(nr, tape_saved, NULL);
9893 void SaveServerScoreFromFile(int nr, boolean tape_saved,
9894 char *score_tape_filename)
9896 if (!runtime.use_api_server)
9899 ApiAddScoreAsThread(nr, tape_saved, score_tape_filename);
9902 void LoadLocalAndServerScore(int nr, boolean download_score)
9904 int last_added_local = scores.last_added_local;
9906 // needed if only showing server scores
9907 setScoreInfoToDefaults();
9909 if (!strEqual(setup.scores_in_highscore_list, STR_SCORES_TYPE_SERVER_ONLY))
9912 // restore last added local score entry (before merging server scores)
9913 scores.last_added = scores.last_added_local = last_added_local;
9915 if (setup.use_api_server &&
9916 !strEqual(setup.scores_in_highscore_list, STR_SCORES_TYPE_LOCAL_ONLY))
9918 // load server scores from cache file and trigger update from server
9919 LoadServerScore(nr, download_score);
9921 // merge local scores with scores from server
9927 // ============================================================================
9928 // setup file functions
9929 // ============================================================================
9931 #define TOKEN_STR_PLAYER_PREFIX "player_"
9934 static struct TokenInfo global_setup_tokens[] =
9938 &setup.player_name, "player_name"
9942 &setup.multiple_users, "multiple_users"
9946 &setup.sound, "sound"
9950 &setup.sound_loops, "repeating_sound_loops"
9954 &setup.sound_music, "background_music"
9958 &setup.sound_simple, "simple_sound_effects"
9962 &setup.toons, "toons"
9966 &setup.scroll_delay, "scroll_delay"
9970 &setup.forced_scroll_delay, "forced_scroll_delay"
9974 &setup.scroll_delay_value, "scroll_delay_value"
9978 &setup.engine_snapshot_mode, "engine_snapshot_mode"
9982 &setup.engine_snapshot_memory, "engine_snapshot_memory"
9986 &setup.fade_screens, "fade_screens"
9990 &setup.autorecord, "automatic_tape_recording"
9994 &setup.auto_pause_on_start, "auto_pause_on_start"
9998 &setup.show_titlescreen, "show_titlescreen"
10002 &setup.quick_doors, "quick_doors"
10006 &setup.team_mode, "team_mode"
10010 &setup.handicap, "handicap"
10014 &setup.skip_levels, "skip_levels"
10018 &setup.increment_levels, "increment_levels"
10022 &setup.auto_play_next_level, "auto_play_next_level"
10026 &setup.count_score_after_game, "count_score_after_game"
10030 &setup.show_scores_after_game, "show_scores_after_game"
10034 &setup.time_limit, "time_limit"
10038 &setup.fullscreen, "fullscreen"
10042 &setup.window_scaling_percent, "window_scaling_percent"
10046 &setup.window_scaling_quality, "window_scaling_quality"
10050 &setup.screen_rendering_mode, "screen_rendering_mode"
10054 &setup.vsync_mode, "vsync_mode"
10058 &setup.ask_on_escape, "ask_on_escape"
10062 &setup.ask_on_escape_editor, "ask_on_escape_editor"
10066 &setup.ask_on_game_over, "ask_on_game_over"
10070 &setup.ask_on_quit_game, "ask_on_quit_game"
10074 &setup.ask_on_quit_program, "ask_on_quit_program"
10078 &setup.quick_switch, "quick_player_switch"
10082 &setup.input_on_focus, "input_on_focus"
10086 &setup.prefer_aga_graphics, "prefer_aga_graphics"
10090 &setup.prefer_lowpass_sounds, "prefer_lowpass_sounds"
10094 &setup.prefer_extra_panel_items, "prefer_extra_panel_items"
10098 &setup.game_speed_extended, "game_speed_extended"
10102 &setup.game_frame_delay, "game_frame_delay"
10106 &setup.sp_show_border_elements, "sp_show_border_elements"
10110 &setup.small_game_graphics, "small_game_graphics"
10114 &setup.show_load_save_buttons, "show_load_save_buttons"
10118 &setup.show_undo_redo_buttons, "show_undo_redo_buttons"
10122 &setup.scores_in_highscore_list, "scores_in_highscore_list"
10126 &setup.graphics_set, "graphics_set"
10130 &setup.sounds_set, "sounds_set"
10134 &setup.music_set, "music_set"
10138 &setup.override_level_graphics, "override_level_graphics"
10142 &setup.override_level_sounds, "override_level_sounds"
10146 &setup.override_level_music, "override_level_music"
10150 &setup.volume_simple, "volume_simple"
10154 &setup.volume_loops, "volume_loops"
10158 &setup.volume_music, "volume_music"
10162 &setup.network_mode, "network_mode"
10166 &setup.network_player_nr, "network_player"
10170 &setup.network_server_hostname, "network_server_hostname"
10174 &setup.touch.control_type, "touch.control_type"
10178 &setup.touch.move_distance, "touch.move_distance"
10182 &setup.touch.drop_distance, "touch.drop_distance"
10186 &setup.touch.transparency, "touch.transparency"
10190 &setup.touch.draw_outlined, "touch.draw_outlined"
10194 &setup.touch.draw_pressed, "touch.draw_pressed"
10198 &setup.touch.grid_xsize[0], "touch.virtual_buttons.0.xsize"
10202 &setup.touch.grid_ysize[0], "touch.virtual_buttons.0.ysize"
10206 &setup.touch.grid_xsize[1], "touch.virtual_buttons.1.xsize"
10210 &setup.touch.grid_ysize[1], "touch.virtual_buttons.1.ysize"
10214 static struct TokenInfo auto_setup_tokens[] =
10218 &setup.auto_setup.editor_zoom_tilesize, "editor.zoom_tilesize"
10222 static struct TokenInfo server_setup_tokens[] =
10226 &setup.player_uuid, "player_uuid"
10230 &setup.player_version, "player_version"
10234 &setup.use_api_server, TEST_PREFIX "use_api_server"
10238 &setup.api_server_hostname, TEST_PREFIX "api_server_hostname"
10242 &setup.api_server_password, TEST_PREFIX "api_server_password"
10246 &setup.ask_for_uploading_tapes, TEST_PREFIX "ask_for_uploading_tapes"
10250 &setup.ask_for_remaining_tapes, TEST_PREFIX "ask_for_remaining_tapes"
10254 &setup.provide_uploading_tapes, TEST_PREFIX "provide_uploading_tapes"
10258 &setup.ask_for_using_api_server,TEST_PREFIX "ask_for_using_api_server"
10262 &setup.has_remaining_tapes, TEST_PREFIX "has_remaining_tapes"
10266 static struct TokenInfo editor_setup_tokens[] =
10270 &setup.editor.el_classic, "editor.el_classic"
10274 &setup.editor.el_custom, "editor.el_custom"
10278 &setup.editor.el_user_defined, "editor.el_user_defined"
10282 &setup.editor.el_dynamic, "editor.el_dynamic"
10286 &setup.editor.el_headlines, "editor.el_headlines"
10290 &setup.editor.show_element_token, "editor.show_element_token"
10294 &setup.editor.show_read_only_warning, "editor.show_read_only_warning"
10298 static struct TokenInfo editor_cascade_setup_tokens[] =
10302 &setup.editor_cascade.el_bd, "editor.cascade.el_bd"
10306 &setup.editor_cascade.el_em, "editor.cascade.el_em"
10310 &setup.editor_cascade.el_emc, "editor.cascade.el_emc"
10314 &setup.editor_cascade.el_rnd, "editor.cascade.el_rnd"
10318 &setup.editor_cascade.el_sb, "editor.cascade.el_sb"
10322 &setup.editor_cascade.el_sp, "editor.cascade.el_sp"
10326 &setup.editor_cascade.el_dc, "editor.cascade.el_dc"
10330 &setup.editor_cascade.el_dx, "editor.cascade.el_dx"
10334 &setup.editor_cascade.el_mm, "editor.cascade.el_mm"
10338 &setup.editor_cascade.el_df, "editor.cascade.el_df"
10342 &setup.editor_cascade.el_chars, "editor.cascade.el_chars"
10346 &setup.editor_cascade.el_steel_chars, "editor.cascade.el_steel_chars"
10350 &setup.editor_cascade.el_ce, "editor.cascade.el_ce"
10354 &setup.editor_cascade.el_ge, "editor.cascade.el_ge"
10358 &setup.editor_cascade.el_es, "editor.cascade.el_es"
10362 &setup.editor_cascade.el_ref, "editor.cascade.el_ref"
10366 &setup.editor_cascade.el_user, "editor.cascade.el_user"
10370 &setup.editor_cascade.el_dynamic, "editor.cascade.el_dynamic"
10374 static struct TokenInfo shortcut_setup_tokens[] =
10378 &setup.shortcut.save_game, "shortcut.save_game"
10382 &setup.shortcut.load_game, "shortcut.load_game"
10386 &setup.shortcut.restart_game, "shortcut.restart_game"
10390 &setup.shortcut.pause_before_end, "shortcut.pause_before_end"
10394 &setup.shortcut.toggle_pause, "shortcut.toggle_pause"
10398 &setup.shortcut.focus_player[0], "shortcut.focus_player_1"
10402 &setup.shortcut.focus_player[1], "shortcut.focus_player_2"
10406 &setup.shortcut.focus_player[2], "shortcut.focus_player_3"
10410 &setup.shortcut.focus_player[3], "shortcut.focus_player_4"
10414 &setup.shortcut.focus_player_all, "shortcut.focus_player_all"
10418 &setup.shortcut.tape_eject, "shortcut.tape_eject"
10422 &setup.shortcut.tape_extra, "shortcut.tape_extra"
10426 &setup.shortcut.tape_stop, "shortcut.tape_stop"
10430 &setup.shortcut.tape_pause, "shortcut.tape_pause"
10434 &setup.shortcut.tape_record, "shortcut.tape_record"
10438 &setup.shortcut.tape_play, "shortcut.tape_play"
10442 &setup.shortcut.sound_simple, "shortcut.sound_simple"
10446 &setup.shortcut.sound_loops, "shortcut.sound_loops"
10450 &setup.shortcut.sound_music, "shortcut.sound_music"
10454 &setup.shortcut.snap_left, "shortcut.snap_left"
10458 &setup.shortcut.snap_right, "shortcut.snap_right"
10462 &setup.shortcut.snap_up, "shortcut.snap_up"
10466 &setup.shortcut.snap_down, "shortcut.snap_down"
10470 static struct SetupInputInfo setup_input;
10471 static struct TokenInfo player_setup_tokens[] =
10475 &setup_input.use_joystick, ".use_joystick"
10479 &setup_input.joy.device_name, ".joy.device_name"
10483 &setup_input.joy.xleft, ".joy.xleft"
10487 &setup_input.joy.xmiddle, ".joy.xmiddle"
10491 &setup_input.joy.xright, ".joy.xright"
10495 &setup_input.joy.yupper, ".joy.yupper"
10499 &setup_input.joy.ymiddle, ".joy.ymiddle"
10503 &setup_input.joy.ylower, ".joy.ylower"
10507 &setup_input.joy.snap, ".joy.snap_field"
10511 &setup_input.joy.drop, ".joy.place_bomb"
10515 &setup_input.key.left, ".key.move_left"
10519 &setup_input.key.right, ".key.move_right"
10523 &setup_input.key.up, ".key.move_up"
10527 &setup_input.key.down, ".key.move_down"
10531 &setup_input.key.snap, ".key.snap_field"
10535 &setup_input.key.drop, ".key.place_bomb"
10539 static struct TokenInfo system_setup_tokens[] =
10543 &setup.system.sdl_renderdriver, "system.sdl_renderdriver"
10547 &setup.system.sdl_videodriver, "system.sdl_videodriver"
10551 &setup.system.sdl_audiodriver, "system.sdl_audiodriver"
10555 &setup.system.audio_fragment_size, "system.audio_fragment_size"
10559 static struct TokenInfo internal_setup_tokens[] =
10563 &setup.internal.program_title, "program_title"
10567 &setup.internal.program_version, "program_version"
10571 &setup.internal.program_author, "program_author"
10575 &setup.internal.program_email, "program_email"
10579 &setup.internal.program_website, "program_website"
10583 &setup.internal.program_copyright, "program_copyright"
10587 &setup.internal.program_company, "program_company"
10591 &setup.internal.program_icon_file, "program_icon_file"
10595 &setup.internal.default_graphics_set, "default_graphics_set"
10599 &setup.internal.default_sounds_set, "default_sounds_set"
10603 &setup.internal.default_music_set, "default_music_set"
10607 &setup.internal.fallback_graphics_file, "fallback_graphics_file"
10611 &setup.internal.fallback_sounds_file, "fallback_sounds_file"
10615 &setup.internal.fallback_music_file, "fallback_music_file"
10619 &setup.internal.default_level_series, "default_level_series"
10623 &setup.internal.default_window_width, "default_window_width"
10627 &setup.internal.default_window_height, "default_window_height"
10631 &setup.internal.choose_from_top_leveldir, "choose_from_top_leveldir"
10635 &setup.internal.show_scaling_in_title, "show_scaling_in_title"
10639 &setup.internal.create_user_levelset, "create_user_levelset"
10643 &setup.internal.menu_game, "menu_game"
10647 &setup.internal.menu_editor, "menu_editor"
10651 &setup.internal.menu_graphics, "menu_graphics"
10655 &setup.internal.menu_sound, "menu_sound"
10659 &setup.internal.menu_artwork, "menu_artwork"
10663 &setup.internal.menu_input, "menu_input"
10667 &setup.internal.menu_touch, "menu_touch"
10671 &setup.internal.menu_shortcuts, "menu_shortcuts"
10675 &setup.internal.menu_exit, "menu_exit"
10679 &setup.internal.menu_save_and_exit, "menu_save_and_exit"
10683 static struct TokenInfo debug_setup_tokens[] =
10687 &setup.debug.frame_delay[0], "debug.frame_delay_0"
10691 &setup.debug.frame_delay[1], "debug.frame_delay_1"
10695 &setup.debug.frame_delay[2], "debug.frame_delay_2"
10699 &setup.debug.frame_delay[3], "debug.frame_delay_3"
10703 &setup.debug.frame_delay[4], "debug.frame_delay_4"
10707 &setup.debug.frame_delay[5], "debug.frame_delay_5"
10711 &setup.debug.frame_delay[6], "debug.frame_delay_6"
10715 &setup.debug.frame_delay[7], "debug.frame_delay_7"
10719 &setup.debug.frame_delay[8], "debug.frame_delay_8"
10723 &setup.debug.frame_delay[9], "debug.frame_delay_9"
10727 &setup.debug.frame_delay_key[0], "debug.key.frame_delay_0"
10731 &setup.debug.frame_delay_key[1], "debug.key.frame_delay_1"
10735 &setup.debug.frame_delay_key[2], "debug.key.frame_delay_2"
10739 &setup.debug.frame_delay_key[3], "debug.key.frame_delay_3"
10743 &setup.debug.frame_delay_key[4], "debug.key.frame_delay_4"
10747 &setup.debug.frame_delay_key[5], "debug.key.frame_delay_5"
10751 &setup.debug.frame_delay_key[6], "debug.key.frame_delay_6"
10755 &setup.debug.frame_delay_key[7], "debug.key.frame_delay_7"
10759 &setup.debug.frame_delay_key[8], "debug.key.frame_delay_8"
10763 &setup.debug.frame_delay_key[9], "debug.key.frame_delay_9"
10767 &setup.debug.frame_delay_use_mod_key, "debug.frame_delay.use_mod_key"},
10770 &setup.debug.frame_delay_game_only, "debug.frame_delay.game_only"
10774 &setup.debug.show_frames_per_second, "debug.show_frames_per_second"
10778 &setup.debug.xsn_mode, "debug.xsn_mode"
10782 &setup.debug.xsn_percent, "debug.xsn_percent"
10786 static struct TokenInfo options_setup_tokens[] =
10790 &setup.options.verbose, "options.verbose"
10794 static void setSetupInfoToDefaults(struct SetupInfo *si)
10798 si->player_name = getStringCopy(getDefaultUserName(user.nr));
10800 si->multiple_users = TRUE;
10803 si->sound_loops = TRUE;
10804 si->sound_music = TRUE;
10805 si->sound_simple = TRUE;
10807 si->scroll_delay = TRUE;
10808 si->forced_scroll_delay = FALSE;
10809 si->scroll_delay_value = STD_SCROLL_DELAY;
10810 si->engine_snapshot_mode = getStringCopy(STR_SNAPSHOT_MODE_DEFAULT);
10811 si->engine_snapshot_memory = SNAPSHOT_MEMORY_DEFAULT;
10812 si->fade_screens = TRUE;
10813 si->autorecord = TRUE;
10814 si->auto_pause_on_start = FALSE;
10815 si->show_titlescreen = TRUE;
10816 si->quick_doors = FALSE;
10817 si->team_mode = FALSE;
10818 si->handicap = TRUE;
10819 si->skip_levels = TRUE;
10820 si->increment_levels = TRUE;
10821 si->auto_play_next_level = TRUE;
10822 si->count_score_after_game = TRUE;
10823 si->show_scores_after_game = TRUE;
10824 si->time_limit = TRUE;
10825 si->fullscreen = FALSE;
10826 si->window_scaling_percent = STD_WINDOW_SCALING_PERCENT;
10827 si->window_scaling_quality = getStringCopy(SCALING_QUALITY_DEFAULT);
10828 si->screen_rendering_mode = getStringCopy(STR_SPECIAL_RENDERING_DEFAULT);
10829 si->vsync_mode = getStringCopy(STR_VSYNC_MODE_DEFAULT);
10830 si->ask_on_escape = TRUE;
10831 si->ask_on_escape_editor = TRUE;
10832 si->ask_on_game_over = TRUE;
10833 si->ask_on_quit_game = TRUE;
10834 si->ask_on_quit_program = TRUE;
10835 si->quick_switch = FALSE;
10836 si->input_on_focus = FALSE;
10837 si->prefer_aga_graphics = TRUE;
10838 si->prefer_lowpass_sounds = FALSE;
10839 si->prefer_extra_panel_items = TRUE;
10840 si->game_speed_extended = FALSE;
10841 si->game_frame_delay = GAME_FRAME_DELAY;
10842 si->sp_show_border_elements = FALSE;
10843 si->small_game_graphics = FALSE;
10844 si->show_load_save_buttons = FALSE;
10845 si->show_undo_redo_buttons = FALSE;
10846 si->scores_in_highscore_list = getStringCopy(STR_SCORES_TYPE_DEFAULT);
10848 si->graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
10849 si->sounds_set = getStringCopy(SND_CLASSIC_SUBDIR);
10850 si->music_set = getStringCopy(MUS_CLASSIC_SUBDIR);
10852 si->override_level_graphics = FALSE;
10853 si->override_level_sounds = FALSE;
10854 si->override_level_music = FALSE;
10856 si->volume_simple = 100; // percent
10857 si->volume_loops = 100; // percent
10858 si->volume_music = 100; // percent
10860 si->network_mode = FALSE;
10861 si->network_player_nr = 0; // first player
10862 si->network_server_hostname = getStringCopy(STR_NETWORK_AUTO_DETECT);
10864 si->touch.control_type = getStringCopy(TOUCH_CONTROL_DEFAULT);
10865 si->touch.move_distance = TOUCH_MOVE_DISTANCE_DEFAULT; // percent
10866 si->touch.drop_distance = TOUCH_DROP_DISTANCE_DEFAULT; // percent
10867 si->touch.transparency = TOUCH_TRANSPARENCY_DEFAULT; // percent
10868 si->touch.draw_outlined = TRUE;
10869 si->touch.draw_pressed = TRUE;
10871 for (i = 0; i < 2; i++)
10873 char *default_grid_button[6][2] =
10879 { "111222", " vv " },
10880 { "111222", " vv " }
10882 int grid_xsize = DEFAULT_GRID_XSIZE(i);
10883 int grid_ysize = DEFAULT_GRID_YSIZE(i);
10884 int min_xsize = MIN(6, grid_xsize);
10885 int min_ysize = MIN(6, grid_ysize);
10886 int startx = grid_xsize - min_xsize;
10887 int starty = grid_ysize - min_ysize;
10890 // virtual buttons grid can only be set to defaults if video is initialized
10891 // (this will be repeated if virtual buttons are not loaded from setup file)
10892 if (video.initialized)
10894 si->touch.grid_xsize[i] = grid_xsize;
10895 si->touch.grid_ysize[i] = grid_ysize;
10899 si->touch.grid_xsize[i] = -1;
10900 si->touch.grid_ysize[i] = -1;
10903 for (x = 0; x < MAX_GRID_XSIZE; x++)
10904 for (y = 0; y < MAX_GRID_YSIZE; y++)
10905 si->touch.grid_button[i][x][y] = CHAR_GRID_BUTTON_NONE;
10907 for (x = 0; x < min_xsize; x++)
10908 for (y = 0; y < min_ysize; y++)
10909 si->touch.grid_button[i][x][starty + y] =
10910 default_grid_button[y][0][x];
10912 for (x = 0; x < min_xsize; x++)
10913 for (y = 0; y < min_ysize; y++)
10914 si->touch.grid_button[i][startx + x][starty + y] =
10915 default_grid_button[y][1][x];
10918 si->touch.grid_initialized = video.initialized;
10920 si->editor.el_boulderdash = TRUE;
10921 si->editor.el_emerald_mine = TRUE;
10922 si->editor.el_emerald_mine_club = TRUE;
10923 si->editor.el_more = TRUE;
10924 si->editor.el_sokoban = TRUE;
10925 si->editor.el_supaplex = TRUE;
10926 si->editor.el_diamond_caves = TRUE;
10927 si->editor.el_dx_boulderdash = TRUE;
10929 si->editor.el_mirror_magic = TRUE;
10930 si->editor.el_deflektor = TRUE;
10932 si->editor.el_chars = TRUE;
10933 si->editor.el_steel_chars = TRUE;
10935 si->editor.el_classic = TRUE;
10936 si->editor.el_custom = TRUE;
10938 si->editor.el_user_defined = FALSE;
10939 si->editor.el_dynamic = TRUE;
10941 si->editor.el_headlines = TRUE;
10943 si->editor.show_element_token = FALSE;
10945 si->editor.show_read_only_warning = TRUE;
10947 si->editor.use_template_for_new_levels = TRUE;
10949 si->shortcut.save_game = DEFAULT_KEY_SAVE_GAME;
10950 si->shortcut.load_game = DEFAULT_KEY_LOAD_GAME;
10951 si->shortcut.restart_game = DEFAULT_KEY_RESTART_GAME;
10952 si->shortcut.pause_before_end = DEFAULT_KEY_PAUSE_BEFORE_END;
10953 si->shortcut.toggle_pause = DEFAULT_KEY_TOGGLE_PAUSE;
10955 si->shortcut.focus_player[0] = DEFAULT_KEY_FOCUS_PLAYER_1;
10956 si->shortcut.focus_player[1] = DEFAULT_KEY_FOCUS_PLAYER_2;
10957 si->shortcut.focus_player[2] = DEFAULT_KEY_FOCUS_PLAYER_3;
10958 si->shortcut.focus_player[3] = DEFAULT_KEY_FOCUS_PLAYER_4;
10959 si->shortcut.focus_player_all = DEFAULT_KEY_FOCUS_PLAYER_ALL;
10961 si->shortcut.tape_eject = DEFAULT_KEY_TAPE_EJECT;
10962 si->shortcut.tape_extra = DEFAULT_KEY_TAPE_EXTRA;
10963 si->shortcut.tape_stop = DEFAULT_KEY_TAPE_STOP;
10964 si->shortcut.tape_pause = DEFAULT_KEY_TAPE_PAUSE;
10965 si->shortcut.tape_record = DEFAULT_KEY_TAPE_RECORD;
10966 si->shortcut.tape_play = DEFAULT_KEY_TAPE_PLAY;
10968 si->shortcut.sound_simple = DEFAULT_KEY_SOUND_SIMPLE;
10969 si->shortcut.sound_loops = DEFAULT_KEY_SOUND_LOOPS;
10970 si->shortcut.sound_music = DEFAULT_KEY_SOUND_MUSIC;
10972 si->shortcut.snap_left = DEFAULT_KEY_SNAP_LEFT;
10973 si->shortcut.snap_right = DEFAULT_KEY_SNAP_RIGHT;
10974 si->shortcut.snap_up = DEFAULT_KEY_SNAP_UP;
10975 si->shortcut.snap_down = DEFAULT_KEY_SNAP_DOWN;
10977 for (i = 0; i < MAX_PLAYERS; i++)
10979 si->input[i].use_joystick = FALSE;
10980 si->input[i].joy.device_name=getStringCopy(getDeviceNameFromJoystickNr(i));
10981 si->input[i].joy.xleft = JOYSTICK_XLEFT;
10982 si->input[i].joy.xmiddle = JOYSTICK_XMIDDLE;
10983 si->input[i].joy.xright = JOYSTICK_XRIGHT;
10984 si->input[i].joy.yupper = JOYSTICK_YUPPER;
10985 si->input[i].joy.ymiddle = JOYSTICK_YMIDDLE;
10986 si->input[i].joy.ylower = JOYSTICK_YLOWER;
10987 si->input[i].joy.snap = (i == 0 ? JOY_BUTTON_1 : 0);
10988 si->input[i].joy.drop = (i == 0 ? JOY_BUTTON_2 : 0);
10989 si->input[i].key.left = (i == 0 ? DEFAULT_KEY_LEFT : KSYM_UNDEFINED);
10990 si->input[i].key.right = (i == 0 ? DEFAULT_KEY_RIGHT : KSYM_UNDEFINED);
10991 si->input[i].key.up = (i == 0 ? DEFAULT_KEY_UP : KSYM_UNDEFINED);
10992 si->input[i].key.down = (i == 0 ? DEFAULT_KEY_DOWN : KSYM_UNDEFINED);
10993 si->input[i].key.snap = (i == 0 ? DEFAULT_KEY_SNAP : KSYM_UNDEFINED);
10994 si->input[i].key.drop = (i == 0 ? DEFAULT_KEY_DROP : KSYM_UNDEFINED);
10997 si->system.sdl_renderdriver = getStringCopy(ARG_DEFAULT);
10998 si->system.sdl_videodriver = getStringCopy(ARG_DEFAULT);
10999 si->system.sdl_audiodriver = getStringCopy(ARG_DEFAULT);
11000 si->system.audio_fragment_size = DEFAULT_AUDIO_FRAGMENT_SIZE;
11002 si->internal.program_title = getStringCopy(PROGRAM_TITLE_STRING);
11003 si->internal.program_version = getStringCopy(getProgramRealVersionString());
11004 si->internal.program_author = getStringCopy(PROGRAM_AUTHOR_STRING);
11005 si->internal.program_email = getStringCopy(PROGRAM_EMAIL_STRING);
11006 si->internal.program_website = getStringCopy(PROGRAM_WEBSITE_STRING);
11007 si->internal.program_copyright = getStringCopy(PROGRAM_COPYRIGHT_STRING);
11008 si->internal.program_company = getStringCopy(PROGRAM_COMPANY_STRING);
11010 si->internal.program_icon_file = getStringCopy(PROGRAM_ICON_FILENAME);
11012 si->internal.default_graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
11013 si->internal.default_sounds_set = getStringCopy(SND_CLASSIC_SUBDIR);
11014 si->internal.default_music_set = getStringCopy(MUS_CLASSIC_SUBDIR);
11016 si->internal.fallback_graphics_file = getStringCopy(UNDEFINED_FILENAME);
11017 si->internal.fallback_sounds_file = getStringCopy(UNDEFINED_FILENAME);
11018 si->internal.fallback_music_file = getStringCopy(UNDEFINED_FILENAME);
11020 si->internal.default_level_series = getStringCopy(UNDEFINED_LEVELSET);
11021 si->internal.choose_from_top_leveldir = FALSE;
11022 si->internal.show_scaling_in_title = TRUE;
11023 si->internal.create_user_levelset = TRUE;
11025 si->internal.default_window_width = WIN_XSIZE_DEFAULT;
11026 si->internal.default_window_height = WIN_YSIZE_DEFAULT;
11028 si->debug.frame_delay[0] = DEFAULT_FRAME_DELAY_0;
11029 si->debug.frame_delay[1] = DEFAULT_FRAME_DELAY_1;
11030 si->debug.frame_delay[2] = DEFAULT_FRAME_DELAY_2;
11031 si->debug.frame_delay[3] = DEFAULT_FRAME_DELAY_3;
11032 si->debug.frame_delay[4] = DEFAULT_FRAME_DELAY_4;
11033 si->debug.frame_delay[5] = DEFAULT_FRAME_DELAY_5;
11034 si->debug.frame_delay[6] = DEFAULT_FRAME_DELAY_6;
11035 si->debug.frame_delay[7] = DEFAULT_FRAME_DELAY_7;
11036 si->debug.frame_delay[8] = DEFAULT_FRAME_DELAY_8;
11037 si->debug.frame_delay[9] = DEFAULT_FRAME_DELAY_9;
11039 si->debug.frame_delay_key[0] = DEFAULT_KEY_FRAME_DELAY_0;
11040 si->debug.frame_delay_key[1] = DEFAULT_KEY_FRAME_DELAY_1;
11041 si->debug.frame_delay_key[2] = DEFAULT_KEY_FRAME_DELAY_2;
11042 si->debug.frame_delay_key[3] = DEFAULT_KEY_FRAME_DELAY_3;
11043 si->debug.frame_delay_key[4] = DEFAULT_KEY_FRAME_DELAY_4;
11044 si->debug.frame_delay_key[5] = DEFAULT_KEY_FRAME_DELAY_5;
11045 si->debug.frame_delay_key[6] = DEFAULT_KEY_FRAME_DELAY_6;
11046 si->debug.frame_delay_key[7] = DEFAULT_KEY_FRAME_DELAY_7;
11047 si->debug.frame_delay_key[8] = DEFAULT_KEY_FRAME_DELAY_8;
11048 si->debug.frame_delay_key[9] = DEFAULT_KEY_FRAME_DELAY_9;
11050 si->debug.frame_delay_use_mod_key = DEFAULT_FRAME_DELAY_USE_MOD_KEY;
11051 si->debug.frame_delay_game_only = DEFAULT_FRAME_DELAY_GAME_ONLY;
11053 si->debug.show_frames_per_second = FALSE;
11055 si->debug.xsn_mode = AUTO;
11056 si->debug.xsn_percent = 0;
11058 si->options.verbose = FALSE;
11060 #if defined(PLATFORM_ANDROID)
11061 si->fullscreen = TRUE;
11064 setHideSetupEntry(&setup.debug.xsn_mode);
11067 static void setSetupInfoToDefaults_AutoSetup(struct SetupInfo *si)
11069 si->auto_setup.editor_zoom_tilesize = MINI_TILESIZE;
11072 static void setSetupInfoToDefaults_ServerSetup(struct SetupInfo *si)
11074 si->player_uuid = NULL; // (will be set later)
11075 si->player_version = 1; // (will be set later)
11077 si->use_api_server = TRUE;
11078 si->api_server_hostname = getStringCopy(API_SERVER_HOSTNAME);
11079 si->api_server_password = getStringCopy(UNDEFINED_PASSWORD);
11080 si->ask_for_uploading_tapes = TRUE;
11081 si->ask_for_remaining_tapes = FALSE;
11082 si->provide_uploading_tapes = TRUE;
11083 si->ask_for_using_api_server = TRUE;
11084 si->has_remaining_tapes = FALSE;
11087 static void setSetupInfoToDefaults_EditorCascade(struct SetupInfo *si)
11089 si->editor_cascade.el_bd = TRUE;
11090 si->editor_cascade.el_em = TRUE;
11091 si->editor_cascade.el_emc = TRUE;
11092 si->editor_cascade.el_rnd = TRUE;
11093 si->editor_cascade.el_sb = TRUE;
11094 si->editor_cascade.el_sp = TRUE;
11095 si->editor_cascade.el_dc = TRUE;
11096 si->editor_cascade.el_dx = TRUE;
11098 si->editor_cascade.el_mm = TRUE;
11099 si->editor_cascade.el_df = TRUE;
11101 si->editor_cascade.el_chars = FALSE;
11102 si->editor_cascade.el_steel_chars = FALSE;
11103 si->editor_cascade.el_ce = FALSE;
11104 si->editor_cascade.el_ge = FALSE;
11105 si->editor_cascade.el_es = FALSE;
11106 si->editor_cascade.el_ref = FALSE;
11107 si->editor_cascade.el_user = FALSE;
11108 si->editor_cascade.el_dynamic = FALSE;
11111 #define MAX_HIDE_SETUP_TOKEN_SIZE 20
11113 static char *getHideSetupToken(void *setup_value)
11115 static char hide_setup_token[MAX_HIDE_SETUP_TOKEN_SIZE];
11117 if (setup_value != NULL)
11118 snprintf(hide_setup_token, MAX_HIDE_SETUP_TOKEN_SIZE, "%p", setup_value);
11120 return hide_setup_token;
11123 void setHideSetupEntry(void *setup_value)
11125 char *hide_setup_token = getHideSetupToken(setup_value);
11127 if (hide_setup_hash == NULL)
11128 hide_setup_hash = newSetupFileHash();
11130 if (setup_value != NULL)
11131 setHashEntry(hide_setup_hash, hide_setup_token, "");
11134 void removeHideSetupEntry(void *setup_value)
11136 char *hide_setup_token = getHideSetupToken(setup_value);
11138 if (setup_value != NULL)
11139 removeHashEntry(hide_setup_hash, hide_setup_token);
11142 boolean hideSetupEntry(void *setup_value)
11144 char *hide_setup_token = getHideSetupToken(setup_value);
11146 return (setup_value != NULL &&
11147 getHashEntry(hide_setup_hash, hide_setup_token) != NULL);
11150 static void setSetupInfoFromTokenText(SetupFileHash *setup_file_hash,
11151 struct TokenInfo *token_info,
11152 int token_nr, char *token_text)
11154 char *token_hide_text = getStringCat2(token_text, ".hide");
11155 char *token_hide_value = getHashEntry(setup_file_hash, token_hide_text);
11157 // set the value of this setup option in the setup option structure
11158 setSetupInfo(token_info, token_nr, getHashEntry(setup_file_hash, token_text));
11160 // check if this setup option should be hidden in the setup menu
11161 if (token_hide_value != NULL && get_boolean_from_string(token_hide_value))
11162 setHideSetupEntry(token_info[token_nr].value);
11164 free(token_hide_text);
11167 static void setSetupInfoFromTokenInfo(SetupFileHash *setup_file_hash,
11168 struct TokenInfo *token_info,
11171 setSetupInfoFromTokenText(setup_file_hash, token_info, token_nr,
11172 token_info[token_nr].text);
11175 static void decodeSetupFileHash_Default(SetupFileHash *setup_file_hash)
11179 if (!setup_file_hash)
11182 for (i = 0; i < ARRAY_SIZE(global_setup_tokens); i++)
11183 setSetupInfoFromTokenInfo(setup_file_hash, global_setup_tokens, i);
11185 setup.touch.grid_initialized = TRUE;
11186 for (i = 0; i < 2; i++)
11188 int grid_xsize = setup.touch.grid_xsize[i];
11189 int grid_ysize = setup.touch.grid_ysize[i];
11192 // if virtual buttons are not loaded from setup file, repeat initializing
11193 // virtual buttons grid with default values later when video is initialized
11194 if (grid_xsize == -1 ||
11197 setup.touch.grid_initialized = FALSE;
11202 for (y = 0; y < grid_ysize; y++)
11204 char token_string[MAX_LINE_LEN];
11206 sprintf(token_string, "touch.virtual_buttons.%d.%02d", i, y);
11208 char *value_string = getHashEntry(setup_file_hash, token_string);
11210 if (value_string == NULL)
11213 for (x = 0; x < grid_xsize; x++)
11215 char c = value_string[x];
11217 setup.touch.grid_button[i][x][y] =
11218 (c == '.' ? CHAR_GRID_BUTTON_NONE : c);
11223 for (i = 0; i < ARRAY_SIZE(editor_setup_tokens); i++)
11224 setSetupInfoFromTokenInfo(setup_file_hash, editor_setup_tokens, i);
11226 for (i = 0; i < ARRAY_SIZE(shortcut_setup_tokens); i++)
11227 setSetupInfoFromTokenInfo(setup_file_hash, shortcut_setup_tokens, i);
11229 for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
11233 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
11235 setup_input = setup.input[pnr];
11236 for (i = 0; i < ARRAY_SIZE(player_setup_tokens); i++)
11238 char full_token[100];
11240 sprintf(full_token, "%s%s", prefix, player_setup_tokens[i].text);
11241 setSetupInfoFromTokenText(setup_file_hash, player_setup_tokens, i,
11244 setup.input[pnr] = setup_input;
11247 for (i = 0; i < ARRAY_SIZE(system_setup_tokens); i++)
11248 setSetupInfoFromTokenInfo(setup_file_hash, system_setup_tokens, i);
11250 for (i = 0; i < ARRAY_SIZE(internal_setup_tokens); i++)
11251 setSetupInfoFromTokenInfo(setup_file_hash, internal_setup_tokens, i);
11253 for (i = 0; i < ARRAY_SIZE(debug_setup_tokens); i++)
11254 setSetupInfoFromTokenInfo(setup_file_hash, debug_setup_tokens, i);
11256 for (i = 0; i < ARRAY_SIZE(options_setup_tokens); i++)
11257 setSetupInfoFromTokenInfo(setup_file_hash, options_setup_tokens, i);
11259 setHideRelatedSetupEntries();
11262 static void decodeSetupFileHash_AutoSetup(SetupFileHash *setup_file_hash)
11266 if (!setup_file_hash)
11269 for (i = 0; i < ARRAY_SIZE(auto_setup_tokens); i++)
11270 setSetupInfo(auto_setup_tokens, i,
11271 getHashEntry(setup_file_hash,
11272 auto_setup_tokens[i].text));
11275 static void decodeSetupFileHash_ServerSetup(SetupFileHash *setup_file_hash)
11279 if (!setup_file_hash)
11282 for (i = 0; i < ARRAY_SIZE(server_setup_tokens); i++)
11283 setSetupInfo(server_setup_tokens, i,
11284 getHashEntry(setup_file_hash,
11285 server_setup_tokens[i].text));
11288 static void decodeSetupFileHash_EditorCascade(SetupFileHash *setup_file_hash)
11292 if (!setup_file_hash)
11295 for (i = 0; i < ARRAY_SIZE(editor_cascade_setup_tokens); i++)
11296 setSetupInfo(editor_cascade_setup_tokens, i,
11297 getHashEntry(setup_file_hash,
11298 editor_cascade_setup_tokens[i].text));
11301 void LoadUserNames(void)
11303 int last_user_nr = user.nr;
11306 if (global.user_names != NULL)
11308 for (i = 0; i < MAX_PLAYER_NAMES; i++)
11309 checked_free(global.user_names[i]);
11311 checked_free(global.user_names);
11314 global.user_names = checked_calloc(MAX_PLAYER_NAMES * sizeof(char *));
11316 for (i = 0; i < MAX_PLAYER_NAMES; i++)
11320 SetupFileHash *setup_file_hash = loadSetupFileHash(getSetupFilename());
11322 if (setup_file_hash)
11324 char *player_name = getHashEntry(setup_file_hash, "player_name");
11326 global.user_names[i] = getFixedUserName(player_name);
11328 freeSetupFileHash(setup_file_hash);
11331 if (global.user_names[i] == NULL)
11332 global.user_names[i] = getStringCopy(getDefaultUserName(i));
11335 user.nr = last_user_nr;
11338 void LoadSetupFromFilename(char *filename)
11340 SetupFileHash *setup_file_hash = loadSetupFileHash(filename);
11342 if (setup_file_hash)
11344 decodeSetupFileHash_Default(setup_file_hash);
11346 freeSetupFileHash(setup_file_hash);
11350 Debug("setup", "using default setup values");
11354 static void LoadSetup_SpecialPostProcessing(void)
11356 char *player_name_new;
11358 // needed to work around problems with fixed length strings
11359 player_name_new = getFixedUserName(setup.player_name);
11360 free(setup.player_name);
11361 setup.player_name = player_name_new;
11363 // "scroll_delay: on(3) / off(0)" was replaced by scroll delay value
11364 if (setup.scroll_delay == FALSE)
11366 setup.scroll_delay_value = MIN_SCROLL_DELAY;
11367 setup.scroll_delay = TRUE; // now always "on"
11370 // make sure that scroll delay value stays inside valid range
11371 setup.scroll_delay_value =
11372 MIN(MAX(MIN_SCROLL_DELAY, setup.scroll_delay_value), MAX_SCROLL_DELAY);
11375 void LoadSetup_Default(void)
11379 // always start with reliable default values
11380 setSetupInfoToDefaults(&setup);
11382 // try to load setup values from default setup file
11383 filename = getDefaultSetupFilename();
11385 if (fileExists(filename))
11386 LoadSetupFromFilename(filename);
11388 // try to load setup values from user setup file
11389 filename = getSetupFilename();
11391 LoadSetupFromFilename(filename);
11393 LoadSetup_SpecialPostProcessing();
11396 void LoadSetup_AutoSetup(void)
11398 char *filename = getPath2(getSetupDir(), AUTOSETUP_FILENAME);
11399 SetupFileHash *setup_file_hash = NULL;
11401 // always start with reliable default values
11402 setSetupInfoToDefaults_AutoSetup(&setup);
11404 setup_file_hash = loadSetupFileHash(filename);
11406 if (setup_file_hash)
11408 decodeSetupFileHash_AutoSetup(setup_file_hash);
11410 freeSetupFileHash(setup_file_hash);
11416 void LoadSetup_ServerSetup(void)
11418 char *filename = getPath2(getSetupDir(), SERVERSETUP_FILENAME);
11419 SetupFileHash *setup_file_hash = NULL;
11421 // always start with reliable default values
11422 setSetupInfoToDefaults_ServerSetup(&setup);
11424 setup_file_hash = loadSetupFileHash(filename);
11426 if (setup_file_hash)
11428 decodeSetupFileHash_ServerSetup(setup_file_hash);
11430 freeSetupFileHash(setup_file_hash);
11435 if (setup.player_uuid == NULL)
11437 // player UUID does not yet exist in setup file
11438 setup.player_uuid = getStringCopy(getUUID());
11439 setup.player_version = 2;
11441 SaveSetup_ServerSetup();
11445 void LoadSetup_EditorCascade(void)
11447 char *filename = getPath2(getSetupDir(), EDITORCASCADE_FILENAME);
11448 SetupFileHash *setup_file_hash = NULL;
11450 // always start with reliable default values
11451 setSetupInfoToDefaults_EditorCascade(&setup);
11453 setup_file_hash = loadSetupFileHash(filename);
11455 if (setup_file_hash)
11457 decodeSetupFileHash_EditorCascade(setup_file_hash);
11459 freeSetupFileHash(setup_file_hash);
11465 void LoadSetup(void)
11467 LoadSetup_Default();
11468 LoadSetup_AutoSetup();
11469 LoadSetup_ServerSetup();
11470 LoadSetup_EditorCascade();
11473 static void addGameControllerMappingToHash(SetupFileHash *mappings_hash,
11474 char *mapping_line)
11476 char mapping_guid[MAX_LINE_LEN];
11477 char *mapping_start, *mapping_end;
11479 // get GUID from game controller mapping line: copy complete line
11480 strncpy(mapping_guid, mapping_line, MAX_LINE_LEN - 1);
11481 mapping_guid[MAX_LINE_LEN - 1] = '\0';
11483 // get GUID from game controller mapping line: cut after GUID part
11484 mapping_start = strchr(mapping_guid, ',');
11485 if (mapping_start != NULL)
11486 *mapping_start = '\0';
11488 // cut newline from game controller mapping line
11489 mapping_end = strchr(mapping_line, '\n');
11490 if (mapping_end != NULL)
11491 *mapping_end = '\0';
11493 // add mapping entry to game controller mappings hash
11494 setHashEntry(mappings_hash, mapping_guid, mapping_line);
11497 static void LoadSetup_ReadGameControllerMappings(SetupFileHash *mappings_hash,
11502 if (!(file = fopen(filename, MODE_READ)))
11504 Warn("cannot read game controller mappings file '%s'", filename);
11509 while (!feof(file))
11511 char line[MAX_LINE_LEN];
11513 if (!fgets(line, MAX_LINE_LEN, file))
11516 addGameControllerMappingToHash(mappings_hash, line);
11522 void SaveSetup_Default(void)
11524 char *filename = getSetupFilename();
11528 InitUserDataDirectory();
11530 if (!(file = fopen(filename, MODE_WRITE)))
11532 Warn("cannot write setup file '%s'", filename);
11537 fprintFileHeader(file, SETUP_FILENAME);
11539 for (i = 0; i < ARRAY_SIZE(global_setup_tokens); i++)
11541 // just to make things nicer :)
11542 if (global_setup_tokens[i].value == &setup.multiple_users ||
11543 global_setup_tokens[i].value == &setup.sound ||
11544 global_setup_tokens[i].value == &setup.graphics_set ||
11545 global_setup_tokens[i].value == &setup.volume_simple ||
11546 global_setup_tokens[i].value == &setup.network_mode ||
11547 global_setup_tokens[i].value == &setup.touch.control_type ||
11548 global_setup_tokens[i].value == &setup.touch.grid_xsize[0] ||
11549 global_setup_tokens[i].value == &setup.touch.grid_xsize[1])
11550 fprintf(file, "\n");
11552 fprintf(file, "%s\n", getSetupLine(global_setup_tokens, "", i));
11555 for (i = 0; i < 2; i++)
11557 int grid_xsize = setup.touch.grid_xsize[i];
11558 int grid_ysize = setup.touch.grid_ysize[i];
11561 fprintf(file, "\n");
11563 for (y = 0; y < grid_ysize; y++)
11565 char token_string[MAX_LINE_LEN];
11566 char value_string[MAX_LINE_LEN];
11568 sprintf(token_string, "touch.virtual_buttons.%d.%02d", i, y);
11570 for (x = 0; x < grid_xsize; x++)
11572 char c = setup.touch.grid_button[i][x][y];
11574 value_string[x] = (c == CHAR_GRID_BUTTON_NONE ? '.' : c);
11577 value_string[grid_xsize] = '\0';
11579 fprintf(file, "%s\n", getFormattedSetupEntry(token_string, value_string));
11583 fprintf(file, "\n");
11584 for (i = 0; i < ARRAY_SIZE(editor_setup_tokens); i++)
11585 fprintf(file, "%s\n", getSetupLine(editor_setup_tokens, "", i));
11587 fprintf(file, "\n");
11588 for (i = 0; i < ARRAY_SIZE(shortcut_setup_tokens); i++)
11589 fprintf(file, "%s\n", getSetupLine(shortcut_setup_tokens, "", i));
11591 for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
11595 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
11596 fprintf(file, "\n");
11598 setup_input = setup.input[pnr];
11599 for (i = 0; i < ARRAY_SIZE(player_setup_tokens); i++)
11600 fprintf(file, "%s\n", getSetupLine(player_setup_tokens, prefix, i));
11603 fprintf(file, "\n");
11604 for (i = 0; i < ARRAY_SIZE(system_setup_tokens); i++)
11605 fprintf(file, "%s\n", getSetupLine(system_setup_tokens, "", i));
11607 // (internal setup values not saved to user setup file)
11609 fprintf(file, "\n");
11610 for (i = 0; i < ARRAY_SIZE(debug_setup_tokens); i++)
11611 if (!strPrefix(debug_setup_tokens[i].text, "debug.xsn_") ||
11612 setup.debug.xsn_mode != AUTO)
11613 fprintf(file, "%s\n", getSetupLine(debug_setup_tokens, "", i));
11615 fprintf(file, "\n");
11616 for (i = 0; i < ARRAY_SIZE(options_setup_tokens); i++)
11617 fprintf(file, "%s\n", getSetupLine(options_setup_tokens, "", i));
11621 SetFilePermissions(filename, PERMS_PRIVATE);
11624 void SaveSetup_AutoSetup(void)
11626 char *filename = getPath2(getSetupDir(), AUTOSETUP_FILENAME);
11630 InitUserDataDirectory();
11632 if (!(file = fopen(filename, MODE_WRITE)))
11634 Warn("cannot write auto setup file '%s'", filename);
11641 fprintFileHeader(file, AUTOSETUP_FILENAME);
11643 for (i = 0; i < ARRAY_SIZE(auto_setup_tokens); i++)
11644 fprintf(file, "%s\n", getSetupLine(auto_setup_tokens, "", i));
11648 SetFilePermissions(filename, PERMS_PRIVATE);
11653 void SaveSetup_ServerSetup(void)
11655 char *filename = getPath2(getSetupDir(), SERVERSETUP_FILENAME);
11659 InitUserDataDirectory();
11661 if (!(file = fopen(filename, MODE_WRITE)))
11663 Warn("cannot write server setup file '%s'", filename);
11670 fprintFileHeader(file, SERVERSETUP_FILENAME);
11672 for (i = 0; i < ARRAY_SIZE(server_setup_tokens); i++)
11674 // just to make things nicer :)
11675 if (server_setup_tokens[i].value == &setup.use_api_server)
11676 fprintf(file, "\n");
11678 fprintf(file, "%s\n", getSetupLine(server_setup_tokens, "", i));
11683 SetFilePermissions(filename, PERMS_PRIVATE);
11688 void SaveSetup_EditorCascade(void)
11690 char *filename = getPath2(getSetupDir(), EDITORCASCADE_FILENAME);
11694 InitUserDataDirectory();
11696 if (!(file = fopen(filename, MODE_WRITE)))
11698 Warn("cannot write editor cascade state file '%s'", filename);
11705 fprintFileHeader(file, EDITORCASCADE_FILENAME);
11707 for (i = 0; i < ARRAY_SIZE(editor_cascade_setup_tokens); i++)
11708 fprintf(file, "%s\n", getSetupLine(editor_cascade_setup_tokens, "", i));
11712 SetFilePermissions(filename, PERMS_PRIVATE);
11717 void SaveSetup(void)
11719 SaveSetup_Default();
11720 SaveSetup_AutoSetup();
11721 SaveSetup_ServerSetup();
11722 SaveSetup_EditorCascade();
11725 static void SaveSetup_WriteGameControllerMappings(SetupFileHash *mappings_hash,
11730 if (!(file = fopen(filename, MODE_WRITE)))
11732 Warn("cannot write game controller mappings file '%s'", filename);
11737 BEGIN_HASH_ITERATION(mappings_hash, itr)
11739 fprintf(file, "%s\n", HASH_ITERATION_VALUE(itr));
11741 END_HASH_ITERATION(mappings_hash, itr)
11746 void SaveSetup_AddGameControllerMapping(char *mapping)
11748 char *filename = getPath2(getSetupDir(), GAMECONTROLLER_BASENAME);
11749 SetupFileHash *mappings_hash = newSetupFileHash();
11751 InitUserDataDirectory();
11753 // load existing personal game controller mappings
11754 LoadSetup_ReadGameControllerMappings(mappings_hash, filename);
11756 // add new mapping to personal game controller mappings
11757 addGameControllerMappingToHash(mappings_hash, mapping);
11759 // save updated personal game controller mappings
11760 SaveSetup_WriteGameControllerMappings(mappings_hash, filename);
11762 freeSetupFileHash(mappings_hash);
11766 void LoadCustomElementDescriptions(void)
11768 char *filename = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
11769 SetupFileHash *setup_file_hash;
11772 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
11774 if (element_info[i].custom_description != NULL)
11776 free(element_info[i].custom_description);
11777 element_info[i].custom_description = NULL;
11781 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
11784 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
11786 char *token = getStringCat2(element_info[i].token_name, ".name");
11787 char *value = getHashEntry(setup_file_hash, token);
11790 element_info[i].custom_description = getStringCopy(value);
11795 freeSetupFileHash(setup_file_hash);
11798 static int getElementFromToken(char *token)
11800 char *value = getHashEntry(element_token_hash, token);
11803 return atoi(value);
11805 Warn("unknown element token '%s'", token);
11807 return EL_UNDEFINED;
11810 void FreeGlobalAnimEventInfo(void)
11812 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
11814 if (gaei->event_list == NULL)
11819 for (i = 0; i < gaei->num_event_lists; i++)
11821 checked_free(gaei->event_list[i]->event_value);
11822 checked_free(gaei->event_list[i]);
11825 checked_free(gaei->event_list);
11827 gaei->event_list = NULL;
11828 gaei->num_event_lists = 0;
11831 static int AddGlobalAnimEventList(void)
11833 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
11834 int list_pos = gaei->num_event_lists++;
11836 gaei->event_list = checked_realloc(gaei->event_list, gaei->num_event_lists *
11837 sizeof(struct GlobalAnimEventListInfo *));
11839 gaei->event_list[list_pos] =
11840 checked_calloc(sizeof(struct GlobalAnimEventListInfo));
11842 struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
11844 gaeli->event_value = NULL;
11845 gaeli->num_event_values = 0;
11850 static int AddGlobalAnimEventValue(int list_pos, int event_value)
11852 // do not add empty global animation events
11853 if (event_value == ANIM_EVENT_NONE)
11856 // if list position is undefined, create new list
11857 if (list_pos == ANIM_EVENT_UNDEFINED)
11858 list_pos = AddGlobalAnimEventList();
11860 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
11861 struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
11862 int value_pos = gaeli->num_event_values++;
11864 gaeli->event_value = checked_realloc(gaeli->event_value,
11865 gaeli->num_event_values * sizeof(int *));
11867 gaeli->event_value[value_pos] = event_value;
11872 int GetGlobalAnimEventValue(int list_pos, int value_pos)
11874 if (list_pos == ANIM_EVENT_UNDEFINED)
11875 return ANIM_EVENT_NONE;
11877 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
11878 struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
11880 return gaeli->event_value[value_pos];
11883 int GetGlobalAnimEventValueCount(int list_pos)
11885 if (list_pos == ANIM_EVENT_UNDEFINED)
11888 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
11889 struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
11891 return gaeli->num_event_values;
11894 // This function checks if a string <s> of the format "string1, string2, ..."
11895 // exactly contains a string <s_contained>.
11897 static boolean string_has_parameter(char *s, char *s_contained)
11901 if (s == NULL || s_contained == NULL)
11904 if (strlen(s_contained) > strlen(s))
11907 if (strncmp(s, s_contained, strlen(s_contained)) == 0)
11909 char next_char = s[strlen(s_contained)];
11911 // check if next character is delimiter or whitespace
11912 return (next_char == ',' || next_char == '\0' ||
11913 next_char == ' ' || next_char == '\t' ? TRUE : FALSE);
11916 // check if string contains another parameter string after a comma
11917 substring = strchr(s, ',');
11918 if (substring == NULL) // string does not contain a comma
11921 // advance string pointer to next character after the comma
11924 // skip potential whitespaces after the comma
11925 while (*substring == ' ' || *substring == '\t')
11928 return string_has_parameter(substring, s_contained);
11931 static int get_anim_parameter_value(char *s)
11933 int event_value[] =
11941 char *pattern_1[] =
11949 char *pattern_2 = ".part_";
11950 char *matching_char = NULL;
11952 int pattern_1_len = 0;
11953 int result = ANIM_EVENT_NONE;
11956 for (i = 0; i < ARRAY_SIZE(event_value); i++)
11958 matching_char = strstr(s_ptr, pattern_1[i]);
11959 pattern_1_len = strlen(pattern_1[i]);
11960 result = event_value[i];
11962 if (matching_char != NULL)
11966 if (matching_char == NULL)
11967 return ANIM_EVENT_NONE;
11969 s_ptr = matching_char + pattern_1_len;
11971 // check for main animation number ("anim_X" or "anim_XX")
11972 if (*s_ptr >= '0' && *s_ptr <= '9')
11974 int gic_anim_nr = (*s_ptr++ - '0');
11976 if (*s_ptr >= '0' && *s_ptr <= '9')
11977 gic_anim_nr = 10 * gic_anim_nr + (*s_ptr++ - '0');
11979 if (gic_anim_nr < 1 || gic_anim_nr > MAX_GLOBAL_ANIMS)
11980 return ANIM_EVENT_NONE;
11982 result |= gic_anim_nr << ANIM_EVENT_ANIM_BIT;
11986 // invalid main animation number specified
11988 return ANIM_EVENT_NONE;
11991 // check for animation part number ("part_X" or "part_XX") (optional)
11992 if (strPrefix(s_ptr, pattern_2))
11994 s_ptr += strlen(pattern_2);
11996 if (*s_ptr >= '0' && *s_ptr <= '9')
11998 int gic_part_nr = (*s_ptr++ - '0');
12000 if (*s_ptr >= '0' && *s_ptr <= '9')
12001 gic_part_nr = 10 * gic_part_nr + (*s_ptr++ - '0');
12003 if (gic_part_nr < 1 || gic_part_nr > MAX_GLOBAL_ANIM_PARTS)
12004 return ANIM_EVENT_NONE;
12006 result |= gic_part_nr << ANIM_EVENT_PART_BIT;
12010 // invalid animation part number specified
12012 return ANIM_EVENT_NONE;
12016 // discard result if next character is neither delimiter nor whitespace
12017 if (!(*s_ptr == ',' || *s_ptr == '\0' ||
12018 *s_ptr == ' ' || *s_ptr == '\t'))
12019 return ANIM_EVENT_NONE;
12024 static int get_anim_parameter_values(char *s)
12026 int list_pos = ANIM_EVENT_UNDEFINED;
12027 int event_value = ANIM_EVENT_DEFAULT;
12029 if (string_has_parameter(s, "any"))
12030 event_value |= ANIM_EVENT_ANY;
12032 if (string_has_parameter(s, "click:self") ||
12033 string_has_parameter(s, "click") ||
12034 string_has_parameter(s, "self"))
12035 event_value |= ANIM_EVENT_SELF;
12037 if (string_has_parameter(s, "unclick:any"))
12038 event_value |= ANIM_EVENT_UNCLICK_ANY;
12040 // if animation event found, add it to global animation event list
12041 if (event_value != ANIM_EVENT_NONE)
12042 list_pos = AddGlobalAnimEventValue(list_pos, event_value);
12046 // add optional "click:anim_X" or "click:anim_X.part_X" parameter
12047 event_value = get_anim_parameter_value(s);
12049 // if animation event found, add it to global animation event list
12050 if (event_value != ANIM_EVENT_NONE)
12051 list_pos = AddGlobalAnimEventValue(list_pos, event_value);
12053 // continue with next part of the string, starting with next comma
12054 s = strchr(s + 1, ',');
12060 static int get_anim_action_parameter_value(char *token)
12062 // check most common default case first to massively speed things up
12063 if (strEqual(token, ARG_UNDEFINED))
12064 return ANIM_EVENT_ACTION_NONE;
12066 int result = getImageIDFromToken(token);
12070 char *gfx_token = getStringCat2("gfx.", token);
12072 result = getImageIDFromToken(gfx_token);
12074 checked_free(gfx_token);
12079 Key key = getKeyFromX11KeyName(token);
12081 if (key != KSYM_UNDEFINED)
12082 result = -(int)key;
12086 result = ANIM_EVENT_ACTION_NONE;
12091 int get_parameter_value(char *value_raw, char *suffix, int type)
12093 char *value = getStringToLower(value_raw);
12094 int result = 0; // probably a save default value
12096 if (strEqual(suffix, ".direction"))
12098 result = (strEqual(value, "left") ? MV_LEFT :
12099 strEqual(value, "right") ? MV_RIGHT :
12100 strEqual(value, "up") ? MV_UP :
12101 strEqual(value, "down") ? MV_DOWN : MV_NONE);
12103 else if (strEqual(suffix, ".position"))
12105 result = (strEqual(value, "left") ? POS_LEFT :
12106 strEqual(value, "right") ? POS_RIGHT :
12107 strEqual(value, "top") ? POS_TOP :
12108 strEqual(value, "upper") ? POS_UPPER :
12109 strEqual(value, "middle") ? POS_MIDDLE :
12110 strEqual(value, "lower") ? POS_LOWER :
12111 strEqual(value, "bottom") ? POS_BOTTOM :
12112 strEqual(value, "any") ? POS_ANY :
12113 strEqual(value, "last") ? POS_LAST : POS_UNDEFINED);
12115 else if (strEqual(suffix, ".align"))
12117 result = (strEqual(value, "left") ? ALIGN_LEFT :
12118 strEqual(value, "right") ? ALIGN_RIGHT :
12119 strEqual(value, "center") ? ALIGN_CENTER :
12120 strEqual(value, "middle") ? ALIGN_CENTER : ALIGN_DEFAULT);
12122 else if (strEqual(suffix, ".valign"))
12124 result = (strEqual(value, "top") ? VALIGN_TOP :
12125 strEqual(value, "bottom") ? VALIGN_BOTTOM :
12126 strEqual(value, "middle") ? VALIGN_MIDDLE :
12127 strEqual(value, "center") ? VALIGN_MIDDLE : VALIGN_DEFAULT);
12129 else if (strEqual(suffix, ".anim_mode"))
12131 result = (string_has_parameter(value, "none") ? ANIM_NONE :
12132 string_has_parameter(value, "loop") ? ANIM_LOOP :
12133 string_has_parameter(value, "linear") ? ANIM_LINEAR :
12134 string_has_parameter(value, "pingpong") ? ANIM_PINGPONG :
12135 string_has_parameter(value, "pingpong2") ? ANIM_PINGPONG2 :
12136 string_has_parameter(value, "random") ? ANIM_RANDOM :
12137 string_has_parameter(value, "random_static") ? ANIM_RANDOM_STATIC :
12138 string_has_parameter(value, "ce_value") ? ANIM_CE_VALUE :
12139 string_has_parameter(value, "ce_score") ? ANIM_CE_SCORE :
12140 string_has_parameter(value, "ce_delay") ? ANIM_CE_DELAY :
12141 string_has_parameter(value, "horizontal") ? ANIM_HORIZONTAL :
12142 string_has_parameter(value, "vertical") ? ANIM_VERTICAL :
12143 string_has_parameter(value, "centered") ? ANIM_CENTERED :
12144 string_has_parameter(value, "all") ? ANIM_ALL :
12145 string_has_parameter(value, "tiled") ? ANIM_TILED :
12148 if (string_has_parameter(value, "once"))
12149 result |= ANIM_ONCE;
12151 if (string_has_parameter(value, "reverse"))
12152 result |= ANIM_REVERSE;
12154 if (string_has_parameter(value, "opaque_player"))
12155 result |= ANIM_OPAQUE_PLAYER;
12157 if (string_has_parameter(value, "static_panel"))
12158 result |= ANIM_STATIC_PANEL;
12160 else if (strEqual(suffix, ".init_event") ||
12161 strEqual(suffix, ".anim_event"))
12163 result = get_anim_parameter_values(value);
12165 else if (strEqual(suffix, ".init_delay_action") ||
12166 strEqual(suffix, ".anim_delay_action") ||
12167 strEqual(suffix, ".post_delay_action") ||
12168 strEqual(suffix, ".init_event_action") ||
12169 strEqual(suffix, ".anim_event_action"))
12171 result = get_anim_action_parameter_value(value_raw);
12173 else if (strEqual(suffix, ".class"))
12175 result = (strEqual(value, ARG_UNDEFINED) ? ARG_UNDEFINED_VALUE :
12176 get_hash_from_key(value));
12178 else if (strEqual(suffix, ".style"))
12180 result = STYLE_DEFAULT;
12182 if (string_has_parameter(value, "accurate_borders"))
12183 result |= STYLE_ACCURATE_BORDERS;
12185 if (string_has_parameter(value, "inner_corners"))
12186 result |= STYLE_INNER_CORNERS;
12188 if (string_has_parameter(value, "reverse"))
12189 result |= STYLE_REVERSE;
12191 if (string_has_parameter(value, "leftmost_position"))
12192 result |= STYLE_LEFTMOST_POSITION;
12194 if (string_has_parameter(value, "block_clicks"))
12195 result |= STYLE_BLOCK;
12197 if (string_has_parameter(value, "passthrough_clicks"))
12198 result |= STYLE_PASSTHROUGH;
12200 if (string_has_parameter(value, "multiple_actions"))
12201 result |= STYLE_MULTIPLE_ACTIONS;
12203 else if (strEqual(suffix, ".fade_mode"))
12205 result = (string_has_parameter(value, "none") ? FADE_MODE_NONE :
12206 string_has_parameter(value, "fade") ? FADE_MODE_FADE :
12207 string_has_parameter(value, "crossfade") ? FADE_MODE_CROSSFADE :
12208 string_has_parameter(value, "melt") ? FADE_MODE_MELT :
12209 string_has_parameter(value, "curtain") ? FADE_MODE_CURTAIN :
12210 FADE_MODE_DEFAULT);
12212 else if (strEqual(suffix, ".auto_delay_unit"))
12214 result = (string_has_parameter(value, "ms") ? AUTO_DELAY_UNIT_MS :
12215 string_has_parameter(value, "frames") ? AUTO_DELAY_UNIT_FRAMES :
12216 AUTO_DELAY_UNIT_DEFAULT);
12218 else if (strPrefix(suffix, ".font")) // (may also be ".font_xyz")
12220 result = gfx.get_font_from_token_function(value);
12222 else // generic parameter of type integer or boolean
12224 result = (strEqual(value, ARG_UNDEFINED) ? ARG_UNDEFINED_VALUE :
12225 type == TYPE_INTEGER ? get_integer_from_string(value) :
12226 type == TYPE_BOOLEAN ? get_boolean_from_string(value) :
12227 ARG_UNDEFINED_VALUE);
12235 static int get_token_parameter_value(char *token, char *value_raw)
12239 if (token == NULL || value_raw == NULL)
12240 return ARG_UNDEFINED_VALUE;
12242 suffix = strrchr(token, '.');
12243 if (suffix == NULL)
12246 if (strEqual(suffix, ".element"))
12247 return getElementFromToken(value_raw);
12249 // !!! USE CORRECT VALUE TYPE (currently works also for TYPE_BOOLEAN) !!!
12250 return get_parameter_value(value_raw, suffix, TYPE_INTEGER);
12253 void InitMenuDesignSettings_FromHash(SetupFileHash *setup_file_hash,
12254 boolean ignore_defaults)
12258 for (i = 0; image_config_vars[i].token != NULL; i++)
12260 char *value = getHashEntry(setup_file_hash, image_config_vars[i].token);
12262 // (ignore definitions set to "[DEFAULT]" which are already initialized)
12263 if (ignore_defaults && strEqual(value, ARG_DEFAULT))
12267 *image_config_vars[i].value =
12268 get_token_parameter_value(image_config_vars[i].token, value);
12272 void InitMenuDesignSettings_Static(void)
12274 // always start with reliable default values from static default config
12275 InitMenuDesignSettings_FromHash(image_config_hash, FALSE);
12278 static void InitMenuDesignSettings_SpecialPreProcessing(void)
12282 // the following initializes hierarchical values from static configuration
12284 // special case: initialize "ARG_DEFAULT" values in static default config
12285 // (e.g., initialize "[titlemessage].fade_mode" from "[title].fade_mode")
12286 titlescreen_initial_first_default.fade_mode =
12287 title_initial_first_default.fade_mode;
12288 titlescreen_initial_first_default.fade_delay =
12289 title_initial_first_default.fade_delay;
12290 titlescreen_initial_first_default.post_delay =
12291 title_initial_first_default.post_delay;
12292 titlescreen_initial_first_default.auto_delay =
12293 title_initial_first_default.auto_delay;
12294 titlescreen_initial_first_default.auto_delay_unit =
12295 title_initial_first_default.auto_delay_unit;
12296 titlescreen_first_default.fade_mode = title_first_default.fade_mode;
12297 titlescreen_first_default.fade_delay = title_first_default.fade_delay;
12298 titlescreen_first_default.post_delay = title_first_default.post_delay;
12299 titlescreen_first_default.auto_delay = title_first_default.auto_delay;
12300 titlescreen_first_default.auto_delay_unit =
12301 title_first_default.auto_delay_unit;
12302 titlemessage_initial_first_default.fade_mode =
12303 title_initial_first_default.fade_mode;
12304 titlemessage_initial_first_default.fade_delay =
12305 title_initial_first_default.fade_delay;
12306 titlemessage_initial_first_default.post_delay =
12307 title_initial_first_default.post_delay;
12308 titlemessage_initial_first_default.auto_delay =
12309 title_initial_first_default.auto_delay;
12310 titlemessage_initial_first_default.auto_delay_unit =
12311 title_initial_first_default.auto_delay_unit;
12312 titlemessage_first_default.fade_mode = title_first_default.fade_mode;
12313 titlemessage_first_default.fade_delay = title_first_default.fade_delay;
12314 titlemessage_first_default.post_delay = title_first_default.post_delay;
12315 titlemessage_first_default.auto_delay = title_first_default.auto_delay;
12316 titlemessage_first_default.auto_delay_unit =
12317 title_first_default.auto_delay_unit;
12319 titlescreen_initial_default.fade_mode = title_initial_default.fade_mode;
12320 titlescreen_initial_default.fade_delay = title_initial_default.fade_delay;
12321 titlescreen_initial_default.post_delay = title_initial_default.post_delay;
12322 titlescreen_initial_default.auto_delay = title_initial_default.auto_delay;
12323 titlescreen_initial_default.auto_delay_unit =
12324 title_initial_default.auto_delay_unit;
12325 titlescreen_default.fade_mode = title_default.fade_mode;
12326 titlescreen_default.fade_delay = title_default.fade_delay;
12327 titlescreen_default.post_delay = title_default.post_delay;
12328 titlescreen_default.auto_delay = title_default.auto_delay;
12329 titlescreen_default.auto_delay_unit = title_default.auto_delay_unit;
12330 titlemessage_initial_default.fade_mode = title_initial_default.fade_mode;
12331 titlemessage_initial_default.fade_delay = title_initial_default.fade_delay;
12332 titlemessage_initial_default.post_delay = title_initial_default.post_delay;
12333 titlemessage_initial_default.auto_delay_unit =
12334 title_initial_default.auto_delay_unit;
12335 titlemessage_default.fade_mode = title_default.fade_mode;
12336 titlemessage_default.fade_delay = title_default.fade_delay;
12337 titlemessage_default.post_delay = title_default.post_delay;
12338 titlemessage_default.auto_delay = title_default.auto_delay;
12339 titlemessage_default.auto_delay_unit = title_default.auto_delay_unit;
12341 // special case: initialize "ARG_DEFAULT" values in static default config
12342 // (e.g., init "titlemessage_1.fade_mode" from "[titlemessage].fade_mode")
12343 for (i = 0; i < MAX_NUM_TITLE_MESSAGES; i++)
12345 titlescreen_initial_first[i] = titlescreen_initial_first_default;
12346 titlescreen_first[i] = titlescreen_first_default;
12347 titlemessage_initial_first[i] = titlemessage_initial_first_default;
12348 titlemessage_first[i] = titlemessage_first_default;
12350 titlescreen_initial[i] = titlescreen_initial_default;
12351 titlescreen[i] = titlescreen_default;
12352 titlemessage_initial[i] = titlemessage_initial_default;
12353 titlemessage[i] = titlemessage_default;
12356 // special case: initialize "ARG_DEFAULT" values in static default config
12357 // (eg, init "menu.enter_screen.SCORES.xyz" from "menu.enter_screen.xyz")
12358 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
12360 if (i == GFX_SPECIAL_ARG_TITLE) // title values already initialized
12363 menu.enter_screen[i] = menu.enter_screen[GFX_SPECIAL_ARG_DEFAULT];
12364 menu.leave_screen[i] = menu.leave_screen[GFX_SPECIAL_ARG_DEFAULT];
12365 menu.next_screen[i] = menu.next_screen[GFX_SPECIAL_ARG_DEFAULT];
12368 // special case: initialize "ARG_DEFAULT" values in static default config
12369 // (eg, init "viewport.door_1.MAIN.xyz" from "viewport.door_1.xyz")
12370 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
12372 viewport.window[i] = viewport.window[GFX_SPECIAL_ARG_DEFAULT];
12373 viewport.playfield[i] = viewport.playfield[GFX_SPECIAL_ARG_DEFAULT];
12374 viewport.door_1[i] = viewport.door_1[GFX_SPECIAL_ARG_DEFAULT];
12376 if (i == GFX_SPECIAL_ARG_EDITOR) // editor values already initialized
12379 viewport.door_2[i] = viewport.door_2[GFX_SPECIAL_ARG_DEFAULT];
12383 static void InitMenuDesignSettings_SpecialPostProcessing(void)
12387 struct XY *dst, *src;
12389 game_buttons_xy[] =
12391 { &game.button.save, &game.button.stop },
12392 { &game.button.pause2, &game.button.pause },
12393 { &game.button.load, &game.button.play },
12394 { &game.button.undo, &game.button.stop },
12395 { &game.button.redo, &game.button.play },
12401 // special case: initialize later added SETUP list size from LEVELS value
12402 if (menu.list_size[GAME_MODE_SETUP] == -1)
12403 menu.list_size[GAME_MODE_SETUP] = menu.list_size[GAME_MODE_LEVELS];
12405 // set default position for snapshot buttons to stop/pause/play buttons
12406 for (i = 0; game_buttons_xy[i].dst != NULL; i++)
12407 if ((*game_buttons_xy[i].dst).x == -1 &&
12408 (*game_buttons_xy[i].dst).y == -1)
12409 *game_buttons_xy[i].dst = *game_buttons_xy[i].src;
12411 // --------------------------------------------------------------------------
12412 // dynamic viewports (including playfield margins, borders and alignments)
12413 // --------------------------------------------------------------------------
12415 // dynamic viewports currently only supported for landscape mode
12416 int display_width = MAX(video.display_width, video.display_height);
12417 int display_height = MIN(video.display_width, video.display_height);
12419 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
12421 struct RectWithBorder *vp_window = &viewport.window[i];
12422 struct RectWithBorder *vp_playfield = &viewport.playfield[i];
12423 struct RectWithBorder *vp_door_1 = &viewport.door_1[i];
12424 struct RectWithBorder *vp_door_2 = &viewport.door_2[i];
12425 boolean dynamic_window_width = (vp_window->min_width != -1);
12426 boolean dynamic_window_height = (vp_window->min_height != -1);
12427 boolean dynamic_playfield_width = (vp_playfield->min_width != -1);
12428 boolean dynamic_playfield_height = (vp_playfield->min_height != -1);
12430 // adjust window size if min/max width/height is specified
12432 if (vp_window->min_width != -1)
12434 int window_width = display_width;
12436 // when using static window height, use aspect ratio of display
12437 if (vp_window->min_height == -1)
12438 window_width = vp_window->height * display_width / display_height;
12440 vp_window->width = MAX(vp_window->min_width, window_width);
12443 if (vp_window->min_height != -1)
12445 int window_height = display_height;
12447 // when using static window width, use aspect ratio of display
12448 if (vp_window->min_width == -1)
12449 window_height = vp_window->width * display_height / display_width;
12451 vp_window->height = MAX(vp_window->min_height, window_height);
12454 if (vp_window->max_width != -1)
12455 vp_window->width = MIN(vp_window->width, vp_window->max_width);
12457 if (vp_window->max_height != -1)
12458 vp_window->height = MIN(vp_window->height, vp_window->max_height);
12460 int playfield_width = vp_window->width;
12461 int playfield_height = vp_window->height;
12463 // adjust playfield size and position according to specified margins
12465 playfield_width -= vp_playfield->margin_left;
12466 playfield_width -= vp_playfield->margin_right;
12468 playfield_height -= vp_playfield->margin_top;
12469 playfield_height -= vp_playfield->margin_bottom;
12471 // adjust playfield size if min/max width/height is specified
12473 if (vp_playfield->min_width != -1)
12474 vp_playfield->width = MAX(vp_playfield->min_width, playfield_width);
12476 if (vp_playfield->min_height != -1)
12477 vp_playfield->height = MAX(vp_playfield->min_height, playfield_height);
12479 if (vp_playfield->max_width != -1)
12480 vp_playfield->width = MIN(vp_playfield->width, vp_playfield->max_width);
12482 if (vp_playfield->max_height != -1)
12483 vp_playfield->height = MIN(vp_playfield->height,vp_playfield->max_height);
12485 // adjust playfield position according to specified alignment
12487 if (vp_playfield->align == ALIGN_LEFT || vp_playfield->x > 0)
12488 vp_playfield->x = ALIGNED_VP_XPOS(vp_playfield);
12489 else if (vp_playfield->align == ALIGN_CENTER)
12490 vp_playfield->x = playfield_width / 2 - vp_playfield->width / 2;
12491 else if (vp_playfield->align == ALIGN_RIGHT)
12492 vp_playfield->x += playfield_width - vp_playfield->width;
12494 if (vp_playfield->valign == VALIGN_TOP || vp_playfield->y > 0)
12495 vp_playfield->y = ALIGNED_VP_YPOS(vp_playfield);
12496 else if (vp_playfield->valign == VALIGN_MIDDLE)
12497 vp_playfield->y = playfield_height / 2 - vp_playfield->height / 2;
12498 else if (vp_playfield->valign == VALIGN_BOTTOM)
12499 vp_playfield->y += playfield_height - vp_playfield->height;
12501 vp_playfield->x += vp_playfield->margin_left;
12502 vp_playfield->y += vp_playfield->margin_top;
12504 // adjust individual playfield borders if only default border is specified
12506 if (vp_playfield->border_left == -1)
12507 vp_playfield->border_left = vp_playfield->border_size;
12508 if (vp_playfield->border_right == -1)
12509 vp_playfield->border_right = vp_playfield->border_size;
12510 if (vp_playfield->border_top == -1)
12511 vp_playfield->border_top = vp_playfield->border_size;
12512 if (vp_playfield->border_bottom == -1)
12513 vp_playfield->border_bottom = vp_playfield->border_size;
12515 // set dynamic playfield borders if borders are specified as undefined
12516 // (but only if window size was dynamic and playfield size was static)
12518 if (dynamic_window_width && !dynamic_playfield_width)
12520 if (vp_playfield->border_left == -1)
12522 vp_playfield->border_left = (vp_playfield->x -
12523 vp_playfield->margin_left);
12524 vp_playfield->x -= vp_playfield->border_left;
12525 vp_playfield->width += vp_playfield->border_left;
12528 if (vp_playfield->border_right == -1)
12530 vp_playfield->border_right = (vp_window->width -
12532 vp_playfield->width -
12533 vp_playfield->margin_right);
12534 vp_playfield->width += vp_playfield->border_right;
12538 if (dynamic_window_height && !dynamic_playfield_height)
12540 if (vp_playfield->border_top == -1)
12542 vp_playfield->border_top = (vp_playfield->y -
12543 vp_playfield->margin_top);
12544 vp_playfield->y -= vp_playfield->border_top;
12545 vp_playfield->height += vp_playfield->border_top;
12548 if (vp_playfield->border_bottom == -1)
12550 vp_playfield->border_bottom = (vp_window->height -
12552 vp_playfield->height -
12553 vp_playfield->margin_bottom);
12554 vp_playfield->height += vp_playfield->border_bottom;
12558 // adjust playfield size to be a multiple of a defined alignment tile size
12560 int align_size = vp_playfield->align_size;
12561 int playfield_xtiles = vp_playfield->width / align_size;
12562 int playfield_ytiles = vp_playfield->height / align_size;
12563 int playfield_width_corrected = playfield_xtiles * align_size;
12564 int playfield_height_corrected = playfield_ytiles * align_size;
12565 boolean is_playfield_mode = (i == GFX_SPECIAL_ARG_PLAYING ||
12566 i == GFX_SPECIAL_ARG_EDITOR);
12568 if (is_playfield_mode &&
12569 dynamic_playfield_width &&
12570 vp_playfield->width != playfield_width_corrected)
12572 int playfield_xdiff = vp_playfield->width - playfield_width_corrected;
12574 vp_playfield->width = playfield_width_corrected;
12576 if (vp_playfield->align == ALIGN_LEFT)
12578 vp_playfield->border_left += playfield_xdiff;
12580 else if (vp_playfield->align == ALIGN_RIGHT)
12582 vp_playfield->border_right += playfield_xdiff;
12584 else if (vp_playfield->align == ALIGN_CENTER)
12586 int border_left_diff = playfield_xdiff / 2;
12587 int border_right_diff = playfield_xdiff - border_left_diff;
12589 vp_playfield->border_left += border_left_diff;
12590 vp_playfield->border_right += border_right_diff;
12594 if (is_playfield_mode &&
12595 dynamic_playfield_height &&
12596 vp_playfield->height != playfield_height_corrected)
12598 int playfield_ydiff = vp_playfield->height - playfield_height_corrected;
12600 vp_playfield->height = playfield_height_corrected;
12602 if (vp_playfield->valign == VALIGN_TOP)
12604 vp_playfield->border_top += playfield_ydiff;
12606 else if (vp_playfield->align == VALIGN_BOTTOM)
12608 vp_playfield->border_right += playfield_ydiff;
12610 else if (vp_playfield->align == VALIGN_MIDDLE)
12612 int border_top_diff = playfield_ydiff / 2;
12613 int border_bottom_diff = playfield_ydiff - border_top_diff;
12615 vp_playfield->border_top += border_top_diff;
12616 vp_playfield->border_bottom += border_bottom_diff;
12620 // adjust door positions according to specified alignment
12622 for (j = 0; j < 2; j++)
12624 struct RectWithBorder *vp_door = (j == 0 ? vp_door_1 : vp_door_2);
12626 if (vp_door->align == ALIGN_LEFT || vp_door->x > 0)
12627 vp_door->x = ALIGNED_VP_XPOS(vp_door);
12628 else if (vp_door->align == ALIGN_CENTER)
12629 vp_door->x = vp_window->width / 2 - vp_door->width / 2;
12630 else if (vp_door->align == ALIGN_RIGHT)
12631 vp_door->x += vp_window->width - vp_door->width;
12633 if (vp_door->valign == VALIGN_TOP || vp_door->y > 0)
12634 vp_door->y = ALIGNED_VP_YPOS(vp_door);
12635 else if (vp_door->valign == VALIGN_MIDDLE)
12636 vp_door->y = vp_window->height / 2 - vp_door->height / 2;
12637 else if (vp_door->valign == VALIGN_BOTTOM)
12638 vp_door->y += vp_window->height - vp_door->height;
12643 static void InitMenuDesignSettings_SpecialPostProcessing_AfterGraphics(void)
12647 struct XYTileSize *dst, *src;
12650 editor_buttons_xy[] =
12653 &editor.button.element_left, &editor.palette.element_left,
12654 IMG_GFX_EDITOR_BUTTON_ELEMENT_LEFT
12657 &editor.button.element_middle, &editor.palette.element_middle,
12658 IMG_GFX_EDITOR_BUTTON_ELEMENT_MIDDLE
12661 &editor.button.element_right, &editor.palette.element_right,
12662 IMG_GFX_EDITOR_BUTTON_ELEMENT_RIGHT
12669 // set default position for element buttons to element graphics
12670 for (i = 0; editor_buttons_xy[i].dst != NULL; i++)
12672 if ((*editor_buttons_xy[i].dst).x == -1 &&
12673 (*editor_buttons_xy[i].dst).y == -1)
12675 struct GraphicInfo *gd = &graphic_info[editor_buttons_xy[i].graphic];
12677 gd->width = gd->height = editor_buttons_xy[i].src->tile_size;
12679 *editor_buttons_xy[i].dst = *editor_buttons_xy[i].src;
12683 // adjust editor palette rows and columns if specified to be dynamic
12685 if (editor.palette.cols == -1)
12687 int vp_width = viewport.playfield[GFX_SPECIAL_ARG_EDITOR].width;
12688 int bt_width = graphic_info[IMG_EDITOR_PALETTE_BUTTON].width;
12689 int sc_width = graphic_info[IMG_EDITOR_PALETTE_SCROLLBAR].width;
12691 editor.palette.cols = (vp_width - sc_width) / bt_width;
12693 if (editor.palette.x == -1)
12695 int palette_width = editor.palette.cols * bt_width + sc_width;
12697 editor.palette.x = (vp_width - palette_width) / 2;
12701 if (editor.palette.rows == -1)
12703 int vp_height = viewport.playfield[GFX_SPECIAL_ARG_EDITOR].height;
12704 int bt_height = graphic_info[IMG_EDITOR_PALETTE_BUTTON].height;
12705 int tx_height = getFontHeight(FONT_TEXT_2);
12707 editor.palette.rows = (vp_height - tx_height) / bt_height;
12709 if (editor.palette.y == -1)
12711 int palette_height = editor.palette.rows * bt_height + tx_height;
12713 editor.palette.y = (vp_height - palette_height) / 2;
12718 static void LoadMenuDesignSettingsFromFilename(char *filename)
12720 static struct TitleFadingInfo tfi;
12721 static struct TitleMessageInfo tmi;
12722 static struct TokenInfo title_tokens[] =
12724 { TYPE_INTEGER, &tfi.fade_mode, ".fade_mode" },
12725 { TYPE_INTEGER, &tfi.fade_delay, ".fade_delay" },
12726 { TYPE_INTEGER, &tfi.post_delay, ".post_delay" },
12727 { TYPE_INTEGER, &tfi.auto_delay, ".auto_delay" },
12728 { TYPE_INTEGER, &tfi.auto_delay_unit, ".auto_delay_unit" },
12732 static struct TokenInfo titlemessage_tokens[] =
12734 { TYPE_INTEGER, &tmi.x, ".x" },
12735 { TYPE_INTEGER, &tmi.y, ".y" },
12736 { TYPE_INTEGER, &tmi.width, ".width" },
12737 { TYPE_INTEGER, &tmi.height, ".height" },
12738 { TYPE_INTEGER, &tmi.chars, ".chars" },
12739 { TYPE_INTEGER, &tmi.lines, ".lines" },
12740 { TYPE_INTEGER, &tmi.align, ".align" },
12741 { TYPE_INTEGER, &tmi.valign, ".valign" },
12742 { TYPE_INTEGER, &tmi.font, ".font" },
12743 { TYPE_BOOLEAN, &tmi.autowrap, ".autowrap" },
12744 { TYPE_BOOLEAN, &tmi.centered, ".centered" },
12745 { TYPE_BOOLEAN, &tmi.parse_comments, ".parse_comments" },
12746 { TYPE_INTEGER, &tmi.sort_priority, ".sort_priority" },
12747 { TYPE_INTEGER, &tmi.fade_mode, ".fade_mode" },
12748 { TYPE_INTEGER, &tmi.fade_delay, ".fade_delay" },
12749 { TYPE_INTEGER, &tmi.post_delay, ".post_delay" },
12750 { TYPE_INTEGER, &tmi.auto_delay, ".auto_delay" },
12751 { TYPE_INTEGER, &tmi.auto_delay_unit, ".auto_delay_unit" },
12757 struct TitleFadingInfo *info;
12762 // initialize first titles from "enter screen" definitions, if defined
12763 { &title_initial_first_default, "menu.enter_screen.TITLE" },
12764 { &title_first_default, "menu.enter_screen.TITLE" },
12766 // initialize title screens from "next screen" definitions, if defined
12767 { &title_initial_default, "menu.next_screen.TITLE" },
12768 { &title_default, "menu.next_screen.TITLE" },
12774 struct TitleMessageInfo *array;
12777 titlemessage_arrays[] =
12779 // initialize first titles from "enter screen" definitions, if defined
12780 { titlescreen_initial_first, "menu.enter_screen.TITLE" },
12781 { titlescreen_first, "menu.enter_screen.TITLE" },
12782 { titlemessage_initial_first, "menu.enter_screen.TITLE" },
12783 { titlemessage_first, "menu.enter_screen.TITLE" },
12785 // initialize titles from "next screen" definitions, if defined
12786 { titlescreen_initial, "menu.next_screen.TITLE" },
12787 { titlescreen, "menu.next_screen.TITLE" },
12788 { titlemessage_initial, "menu.next_screen.TITLE" },
12789 { titlemessage, "menu.next_screen.TITLE" },
12791 // overwrite titles with title definitions, if defined
12792 { titlescreen_initial_first, "[title_initial]" },
12793 { titlescreen_first, "[title]" },
12794 { titlemessage_initial_first, "[title_initial]" },
12795 { titlemessage_first, "[title]" },
12797 { titlescreen_initial, "[title_initial]" },
12798 { titlescreen, "[title]" },
12799 { titlemessage_initial, "[title_initial]" },
12800 { titlemessage, "[title]" },
12802 // overwrite titles with title screen/message definitions, if defined
12803 { titlescreen_initial_first, "[titlescreen_initial]" },
12804 { titlescreen_first, "[titlescreen]" },
12805 { titlemessage_initial_first, "[titlemessage_initial]" },
12806 { titlemessage_first, "[titlemessage]" },
12808 { titlescreen_initial, "[titlescreen_initial]" },
12809 { titlescreen, "[titlescreen]" },
12810 { titlemessage_initial, "[titlemessage_initial]" },
12811 { titlemessage, "[titlemessage]" },
12815 SetupFileHash *setup_file_hash;
12818 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
12821 // the following initializes hierarchical values from dynamic configuration
12823 // special case: initialize with default values that may be overwritten
12824 // (e.g., init "menu.draw_xoffset.INFO" from "menu.draw_xoffset")
12825 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
12827 struct TokenIntPtrInfo menu_config[] =
12829 { "menu.draw_xoffset", &menu.draw_xoffset[i] },
12830 { "menu.draw_yoffset", &menu.draw_yoffset[i] },
12831 { "menu.list_size", &menu.list_size[i] }
12834 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
12836 char *token = menu_config[j].token;
12837 char *value = getHashEntry(setup_file_hash, token);
12840 *menu_config[j].value = get_integer_from_string(value);
12844 // special case: initialize with default values that may be overwritten
12845 // (eg, init "menu.draw_xoffset.INFO[XXX]" from "menu.draw_xoffset.INFO")
12846 for (i = 0; i < NUM_SPECIAL_GFX_INFO_ARGS; i++)
12848 struct TokenIntPtrInfo menu_config[] =
12850 { "menu.draw_xoffset.INFO", &menu.draw_xoffset_info[i] },
12851 { "menu.draw_yoffset.INFO", &menu.draw_yoffset_info[i] },
12852 { "menu.list_size.INFO", &menu.list_size_info[i] }
12855 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
12857 char *token = menu_config[j].token;
12858 char *value = getHashEntry(setup_file_hash, token);
12861 *menu_config[j].value = get_integer_from_string(value);
12865 // special case: initialize with default values that may be overwritten
12866 // (eg, init "menu.draw_xoffset.SETUP[XXX]" from "menu.draw_xoffset.SETUP")
12867 for (i = 0; i < NUM_SPECIAL_GFX_SETUP_ARGS; i++)
12869 struct TokenIntPtrInfo menu_config[] =
12871 { "menu.draw_xoffset.SETUP", &menu.draw_xoffset_setup[i] },
12872 { "menu.draw_yoffset.SETUP", &menu.draw_yoffset_setup[i] }
12875 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
12877 char *token = menu_config[j].token;
12878 char *value = getHashEntry(setup_file_hash, token);
12881 *menu_config[j].value = get_integer_from_string(value);
12885 // special case: initialize with default values that may be overwritten
12886 // (eg, init "menu.line_spacing.INFO[XXX]" from "menu.line_spacing.INFO")
12887 for (i = 0; i < NUM_SPECIAL_GFX_INFO_ARGS; i++)
12889 struct TokenIntPtrInfo menu_config[] =
12891 { "menu.left_spacing.INFO", &menu.left_spacing_info[i] },
12892 { "menu.right_spacing.INFO", &menu.right_spacing_info[i] },
12893 { "menu.top_spacing.INFO", &menu.top_spacing_info[i] },
12894 { "menu.bottom_spacing.INFO", &menu.bottom_spacing_info[i] },
12895 { "menu.paragraph_spacing.INFO", &menu.paragraph_spacing_info[i] },
12896 { "menu.headline1_spacing.INFO", &menu.headline1_spacing_info[i] },
12897 { "menu.headline2_spacing.INFO", &menu.headline2_spacing_info[i] },
12898 { "menu.line_spacing.INFO", &menu.line_spacing_info[i] },
12899 { "menu.extra_spacing.INFO", &menu.extra_spacing_info[i] },
12902 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
12904 char *token = menu_config[j].token;
12905 char *value = getHashEntry(setup_file_hash, token);
12908 *menu_config[j].value = get_integer_from_string(value);
12912 // special case: initialize with default values that may be overwritten
12913 // (eg, init "menu.enter_screen.SCORES.xyz" from "menu.enter_screen.xyz")
12914 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
12916 struct TokenIntPtrInfo menu_config[] =
12918 { "menu.enter_screen.fade_mode", &menu.enter_screen[i].fade_mode },
12919 { "menu.enter_screen.fade_delay", &menu.enter_screen[i].fade_delay },
12920 { "menu.enter_screen.post_delay", &menu.enter_screen[i].post_delay },
12921 { "menu.leave_screen.fade_mode", &menu.leave_screen[i].fade_mode },
12922 { "menu.leave_screen.fade_delay", &menu.leave_screen[i].fade_delay },
12923 { "menu.leave_screen.post_delay", &menu.leave_screen[i].post_delay },
12924 { "menu.next_screen.fade_mode", &menu.next_screen[i].fade_mode },
12925 { "menu.next_screen.fade_delay", &menu.next_screen[i].fade_delay },
12926 { "menu.next_screen.post_delay", &menu.next_screen[i].post_delay }
12929 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
12931 char *token = menu_config[j].token;
12932 char *value = getHashEntry(setup_file_hash, token);
12935 *menu_config[j].value = get_token_parameter_value(token, value);
12939 // special case: initialize with default values that may be overwritten
12940 // (eg, init "viewport.door_1.MAIN.xyz" from "viewport.door_1.xyz")
12941 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
12945 char *token_prefix;
12946 struct RectWithBorder *struct_ptr;
12950 { "viewport.window", &viewport.window[i] },
12951 { "viewport.playfield", &viewport.playfield[i] },
12952 { "viewport.door_1", &viewport.door_1[i] },
12953 { "viewport.door_2", &viewport.door_2[i] }
12956 for (j = 0; j < ARRAY_SIZE(vp_struct); j++)
12958 struct TokenIntPtrInfo vp_config[] =
12960 { ".x", &vp_struct[j].struct_ptr->x },
12961 { ".y", &vp_struct[j].struct_ptr->y },
12962 { ".width", &vp_struct[j].struct_ptr->width },
12963 { ".height", &vp_struct[j].struct_ptr->height },
12964 { ".min_width", &vp_struct[j].struct_ptr->min_width },
12965 { ".min_height", &vp_struct[j].struct_ptr->min_height },
12966 { ".max_width", &vp_struct[j].struct_ptr->max_width },
12967 { ".max_height", &vp_struct[j].struct_ptr->max_height },
12968 { ".margin_left", &vp_struct[j].struct_ptr->margin_left },
12969 { ".margin_right", &vp_struct[j].struct_ptr->margin_right },
12970 { ".margin_top", &vp_struct[j].struct_ptr->margin_top },
12971 { ".margin_bottom", &vp_struct[j].struct_ptr->margin_bottom },
12972 { ".border_left", &vp_struct[j].struct_ptr->border_left },
12973 { ".border_right", &vp_struct[j].struct_ptr->border_right },
12974 { ".border_top", &vp_struct[j].struct_ptr->border_top },
12975 { ".border_bottom", &vp_struct[j].struct_ptr->border_bottom },
12976 { ".border_size", &vp_struct[j].struct_ptr->border_size },
12977 { ".align_size", &vp_struct[j].struct_ptr->align_size },
12978 { ".align", &vp_struct[j].struct_ptr->align },
12979 { ".valign", &vp_struct[j].struct_ptr->valign }
12982 for (k = 0; k < ARRAY_SIZE(vp_config); k++)
12984 char *token = getStringCat2(vp_struct[j].token_prefix,
12985 vp_config[k].token);
12986 char *value = getHashEntry(setup_file_hash, token);
12989 *vp_config[k].value = get_token_parameter_value(token, value);
12996 // special case: initialize with default values that may be overwritten
12997 // (e.g., init "[title].fade_mode" from "menu.next_screen.TITLE.fade_mode")
12998 for (i = 0; title_info[i].info != NULL; i++)
13000 struct TitleFadingInfo *info = title_info[i].info;
13001 char *base_token = title_info[i].text;
13003 for (j = 0; title_tokens[j].type != -1; j++)
13005 char *token = getStringCat2(base_token, title_tokens[j].text);
13006 char *value = getHashEntry(setup_file_hash, token);
13010 int parameter_value = get_token_parameter_value(token, value);
13014 *(int *)title_tokens[j].value = (int)parameter_value;
13023 // special case: initialize with default values that may be overwritten
13024 // (e.g., init "titlemessage_1.fade_mode" from "[titlemessage].fade_mode")
13025 for (i = 0; titlemessage_arrays[i].array != NULL; i++)
13027 struct TitleMessageInfo *array = titlemessage_arrays[i].array;
13028 char *base_token = titlemessage_arrays[i].text;
13030 for (j = 0; titlemessage_tokens[j].type != -1; j++)
13032 char *token = getStringCat2(base_token, titlemessage_tokens[j].text);
13033 char *value = getHashEntry(setup_file_hash, token);
13037 int parameter_value = get_token_parameter_value(token, value);
13039 for (k = 0; k < MAX_NUM_TITLE_MESSAGES; k++)
13043 if (titlemessage_tokens[j].type == TYPE_INTEGER)
13044 *(int *)titlemessage_tokens[j].value = (int)parameter_value;
13046 *(boolean *)titlemessage_tokens[j].value = (boolean)parameter_value;
13056 // special case: check if network and preview player positions are redefined,
13057 // to compare this later against the main menu level preview being redefined
13058 struct TokenIntPtrInfo menu_config_players[] =
13060 { "main.network_players.x", &menu.main.network_players.redefined },
13061 { "main.network_players.y", &menu.main.network_players.redefined },
13062 { "main.preview_players.x", &menu.main.preview_players.redefined },
13063 { "main.preview_players.y", &menu.main.preview_players.redefined },
13064 { "preview.x", &preview.redefined },
13065 { "preview.y", &preview.redefined }
13068 for (i = 0; i < ARRAY_SIZE(menu_config_players); i++)
13069 *menu_config_players[i].value = FALSE;
13071 for (i = 0; i < ARRAY_SIZE(menu_config_players); i++)
13072 if (getHashEntry(setup_file_hash, menu_config_players[i].token) != NULL)
13073 *menu_config_players[i].value = TRUE;
13075 // read (and overwrite with) values that may be specified in config file
13076 InitMenuDesignSettings_FromHash(setup_file_hash, TRUE);
13078 freeSetupFileHash(setup_file_hash);
13081 void LoadMenuDesignSettings(void)
13083 char *filename_base = UNDEFINED_FILENAME, *filename_local;
13085 InitMenuDesignSettings_Static();
13086 InitMenuDesignSettings_SpecialPreProcessing();
13088 if (!GFX_OVERRIDE_ARTWORK(ARTWORK_TYPE_GRAPHICS))
13090 // first look for special settings configured in level series config
13091 filename_base = getCustomArtworkLevelConfigFilename(ARTWORK_TYPE_GRAPHICS);
13093 if (fileExists(filename_base))
13094 LoadMenuDesignSettingsFromFilename(filename_base);
13097 filename_local = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
13099 if (filename_local != NULL && !strEqual(filename_base, filename_local))
13100 LoadMenuDesignSettingsFromFilename(filename_local);
13102 InitMenuDesignSettings_SpecialPostProcessing();
13105 void LoadMenuDesignSettings_AfterGraphics(void)
13107 InitMenuDesignSettings_SpecialPostProcessing_AfterGraphics();
13110 void LoadUserDefinedEditorElementList(int **elements, int *num_elements)
13112 char *filename = getEditorSetupFilename();
13113 SetupFileList *setup_file_list, *list;
13114 SetupFileHash *element_hash;
13115 int num_unknown_tokens = 0;
13118 if ((setup_file_list = loadSetupFileList(filename)) == NULL)
13121 element_hash = newSetupFileHash();
13123 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
13124 setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
13126 // determined size may be larger than needed (due to unknown elements)
13128 for (list = setup_file_list; list != NULL; list = list->next)
13131 // add space for up to 3 more elements for padding that may be needed
13132 *num_elements += 3;
13134 // free memory for old list of elements, if needed
13135 checked_free(*elements);
13137 // allocate memory for new list of elements
13138 *elements = checked_malloc(*num_elements * sizeof(int));
13141 for (list = setup_file_list; list != NULL; list = list->next)
13143 char *value = getHashEntry(element_hash, list->token);
13145 if (value == NULL) // try to find obsolete token mapping
13147 char *mapped_token = get_mapped_token(list->token);
13149 if (mapped_token != NULL)
13151 value = getHashEntry(element_hash, mapped_token);
13153 free(mapped_token);
13159 (*elements)[(*num_elements)++] = atoi(value);
13163 if (num_unknown_tokens == 0)
13166 Warn("unknown token(s) found in config file:");
13167 Warn("- config file: '%s'", filename);
13169 num_unknown_tokens++;
13172 Warn("- token: '%s'", list->token);
13176 if (num_unknown_tokens > 0)
13179 while (*num_elements % 4) // pad with empty elements, if needed
13180 (*elements)[(*num_elements)++] = EL_EMPTY;
13182 freeSetupFileList(setup_file_list);
13183 freeSetupFileHash(element_hash);
13186 for (i = 0; i < *num_elements; i++)
13187 Debug("editor", "element '%s' [%d]\n",
13188 element_info[(*elements)[i]].token_name, (*elements)[i]);
13192 static struct MusicFileInfo *get_music_file_info_ext(char *basename, int music,
13195 SetupFileHash *setup_file_hash = NULL;
13196 struct MusicFileInfo tmp_music_file_info, *new_music_file_info;
13197 char *filename_music, *filename_prefix, *filename_info;
13203 token_to_value_ptr[] =
13205 { "title_header", &tmp_music_file_info.title_header },
13206 { "artist_header", &tmp_music_file_info.artist_header },
13207 { "album_header", &tmp_music_file_info.album_header },
13208 { "year_header", &tmp_music_file_info.year_header },
13210 { "title", &tmp_music_file_info.title },
13211 { "artist", &tmp_music_file_info.artist },
13212 { "album", &tmp_music_file_info.album },
13213 { "year", &tmp_music_file_info.year },
13219 filename_music = (is_sound ? getCustomSoundFilename(basename) :
13220 getCustomMusicFilename(basename));
13222 if (filename_music == NULL)
13225 // ---------- try to replace file extension ----------
13227 filename_prefix = getStringCopy(filename_music);
13228 if (strrchr(filename_prefix, '.') != NULL)
13229 *strrchr(filename_prefix, '.') = '\0';
13230 filename_info = getStringCat2(filename_prefix, ".txt");
13232 if (fileExists(filename_info))
13233 setup_file_hash = loadSetupFileHash(filename_info);
13235 free(filename_prefix);
13236 free(filename_info);
13238 if (setup_file_hash == NULL)
13240 // ---------- try to add file extension ----------
13242 filename_prefix = getStringCopy(filename_music);
13243 filename_info = getStringCat2(filename_prefix, ".txt");
13245 if (fileExists(filename_info))
13246 setup_file_hash = loadSetupFileHash(filename_info);
13248 free(filename_prefix);
13249 free(filename_info);
13252 if (setup_file_hash == NULL)
13255 // ---------- music file info found ----------
13257 clear_mem(&tmp_music_file_info, sizeof(struct MusicFileInfo));
13259 for (i = 0; token_to_value_ptr[i].token != NULL; i++)
13261 char *value = getHashEntry(setup_file_hash, token_to_value_ptr[i].token);
13263 *token_to_value_ptr[i].value_ptr =
13264 getStringCopy(value != NULL && *value != '\0' ? value : UNKNOWN_NAME);
13267 tmp_music_file_info.basename = getStringCopy(basename);
13268 tmp_music_file_info.music = music;
13269 tmp_music_file_info.is_sound = is_sound;
13271 new_music_file_info = checked_malloc(sizeof(struct MusicFileInfo));
13272 *new_music_file_info = tmp_music_file_info;
13274 return new_music_file_info;
13277 static struct MusicFileInfo *get_music_file_info(char *basename, int music)
13279 return get_music_file_info_ext(basename, music, FALSE);
13282 static struct MusicFileInfo *get_sound_file_info(char *basename, int sound)
13284 return get_music_file_info_ext(basename, sound, TRUE);
13287 static boolean music_info_listed_ext(struct MusicFileInfo *list,
13288 char *basename, boolean is_sound)
13290 for (; list != NULL; list = list->next)
13291 if (list->is_sound == is_sound && strEqual(list->basename, basename))
13297 static boolean music_info_listed(struct MusicFileInfo *list, char *basename)
13299 return music_info_listed_ext(list, basename, FALSE);
13302 static boolean sound_info_listed(struct MusicFileInfo *list, char *basename)
13304 return music_info_listed_ext(list, basename, TRUE);
13307 void LoadMusicInfo(void)
13309 char *music_directory = getCustomMusicDirectory();
13310 int num_music = getMusicListSize();
13311 int num_music_noconf = 0;
13312 int num_sounds = getSoundListSize();
13314 DirectoryEntry *dir_entry;
13315 struct FileInfo *music, *sound;
13316 struct MusicFileInfo *next, **new;
13319 while (music_file_info != NULL)
13321 next = music_file_info->next;
13323 checked_free(music_file_info->basename);
13325 checked_free(music_file_info->title_header);
13326 checked_free(music_file_info->artist_header);
13327 checked_free(music_file_info->album_header);
13328 checked_free(music_file_info->year_header);
13330 checked_free(music_file_info->title);
13331 checked_free(music_file_info->artist);
13332 checked_free(music_file_info->album);
13333 checked_free(music_file_info->year);
13335 free(music_file_info);
13337 music_file_info = next;
13340 new = &music_file_info;
13342 for (i = 0; i < num_music; i++)
13344 music = getMusicListEntry(i);
13346 if (music->filename == NULL)
13349 if (strEqual(music->filename, UNDEFINED_FILENAME))
13352 // a configured file may be not recognized as music
13353 if (!FileIsMusic(music->filename))
13356 if (!music_info_listed(music_file_info, music->filename))
13358 *new = get_music_file_info(music->filename, i);
13361 new = &(*new)->next;
13365 if ((dir = openDirectory(music_directory)) == NULL)
13367 Warn("cannot read music directory '%s'", music_directory);
13372 while ((dir_entry = readDirectory(dir)) != NULL) // loop all entries
13374 char *basename = dir_entry->basename;
13375 boolean music_already_used = FALSE;
13378 // skip all music files that are configured in music config file
13379 for (i = 0; i < num_music; i++)
13381 music = getMusicListEntry(i);
13383 if (music->filename == NULL)
13386 if (strEqual(basename, music->filename))
13388 music_already_used = TRUE;
13393 if (music_already_used)
13396 if (!FileIsMusic(dir_entry->filename))
13399 if (!music_info_listed(music_file_info, basename))
13401 *new = get_music_file_info(basename, MAP_NOCONF_MUSIC(num_music_noconf));
13404 new = &(*new)->next;
13407 num_music_noconf++;
13410 closeDirectory(dir);
13412 for (i = 0; i < num_sounds; i++)
13414 sound = getSoundListEntry(i);
13416 if (sound->filename == NULL)
13419 if (strEqual(sound->filename, UNDEFINED_FILENAME))
13422 // a configured file may be not recognized as sound
13423 if (!FileIsSound(sound->filename))
13426 if (!sound_info_listed(music_file_info, sound->filename))
13428 *new = get_sound_file_info(sound->filename, i);
13430 new = &(*new)->next;
13434 // add pointers to previous list nodes
13436 struct MusicFileInfo *node = music_file_info;
13438 while (node != NULL)
13441 node->next->prev = node;
13447 static void add_helpanim_entry(int element, int action, int direction,
13448 int delay, int *num_list_entries)
13450 struct HelpAnimInfo *new_list_entry;
13451 (*num_list_entries)++;
13454 checked_realloc(helpanim_info,
13455 *num_list_entries * sizeof(struct HelpAnimInfo));
13456 new_list_entry = &helpanim_info[*num_list_entries - 1];
13458 new_list_entry->element = element;
13459 new_list_entry->action = action;
13460 new_list_entry->direction = direction;
13461 new_list_entry->delay = delay;
13464 static void print_unknown_token(char *filename, char *token, int token_nr)
13469 Warn("unknown token(s) found in config file:");
13470 Warn("- config file: '%s'", filename);
13473 Warn("- token: '%s'", token);
13476 static void print_unknown_token_end(int token_nr)
13482 void LoadHelpAnimInfo(void)
13484 char *filename = getHelpAnimFilename();
13485 SetupFileList *setup_file_list = NULL, *list;
13486 SetupFileHash *element_hash, *action_hash, *direction_hash;
13487 int num_list_entries = 0;
13488 int num_unknown_tokens = 0;
13491 if (fileExists(filename))
13492 setup_file_list = loadSetupFileList(filename);
13494 if (setup_file_list == NULL)
13496 // use reliable default values from static configuration
13497 SetupFileList *insert_ptr;
13499 insert_ptr = setup_file_list =
13500 newSetupFileList(helpanim_config[0].token,
13501 helpanim_config[0].value);
13503 for (i = 1; helpanim_config[i].token; i++)
13504 insert_ptr = addListEntry(insert_ptr,
13505 helpanim_config[i].token,
13506 helpanim_config[i].value);
13509 element_hash = newSetupFileHash();
13510 action_hash = newSetupFileHash();
13511 direction_hash = newSetupFileHash();
13513 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
13514 setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
13516 for (i = 0; i < NUM_ACTIONS; i++)
13517 setHashEntry(action_hash, element_action_info[i].suffix,
13518 i_to_a(element_action_info[i].value));
13520 // do not store direction index (bit) here, but direction value!
13521 for (i = 0; i < NUM_DIRECTIONS_FULL; i++)
13522 setHashEntry(direction_hash, element_direction_info[i].suffix,
13523 i_to_a(1 << element_direction_info[i].value));
13525 for (list = setup_file_list; list != NULL; list = list->next)
13527 char *element_token, *action_token, *direction_token;
13528 char *element_value, *action_value, *direction_value;
13529 int delay = atoi(list->value);
13531 if (strEqual(list->token, "end"))
13533 add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
13538 /* first try to break element into element/action/direction parts;
13539 if this does not work, also accept combined "element[.act][.dir]"
13540 elements (like "dynamite.active"), which are unique elements */
13542 if (strchr(list->token, '.') == NULL) // token contains no '.'
13544 element_value = getHashEntry(element_hash, list->token);
13545 if (element_value != NULL) // element found
13546 add_helpanim_entry(atoi(element_value), -1, -1, delay,
13547 &num_list_entries);
13550 // no further suffixes found -- this is not an element
13551 print_unknown_token(filename, list->token, num_unknown_tokens++);
13557 // token has format "<prefix>.<something>"
13559 action_token = strchr(list->token, '.'); // suffix may be action ...
13560 direction_token = action_token; // ... or direction
13562 element_token = getStringCopy(list->token);
13563 *strchr(element_token, '.') = '\0';
13565 element_value = getHashEntry(element_hash, element_token);
13567 if (element_value == NULL) // this is no element
13569 element_value = getHashEntry(element_hash, list->token);
13570 if (element_value != NULL) // combined element found
13571 add_helpanim_entry(atoi(element_value), -1, -1, delay,
13572 &num_list_entries);
13574 print_unknown_token(filename, list->token, num_unknown_tokens++);
13576 free(element_token);
13581 action_value = getHashEntry(action_hash, action_token);
13583 if (action_value != NULL) // action found
13585 add_helpanim_entry(atoi(element_value), atoi(action_value), -1, delay,
13586 &num_list_entries);
13588 free(element_token);
13593 direction_value = getHashEntry(direction_hash, direction_token);
13595 if (direction_value != NULL) // direction found
13597 add_helpanim_entry(atoi(element_value), -1, atoi(direction_value), delay,
13598 &num_list_entries);
13600 free(element_token);
13605 if (strchr(action_token + 1, '.') == NULL)
13607 // no further suffixes found -- this is not an action nor direction
13609 element_value = getHashEntry(element_hash, list->token);
13610 if (element_value != NULL) // combined element found
13611 add_helpanim_entry(atoi(element_value), -1, -1, delay,
13612 &num_list_entries);
13614 print_unknown_token(filename, list->token, num_unknown_tokens++);
13616 free(element_token);
13621 // token has format "<prefix>.<suffix>.<something>"
13623 direction_token = strchr(action_token + 1, '.');
13625 action_token = getStringCopy(action_token);
13626 *strchr(action_token + 1, '.') = '\0';
13628 action_value = getHashEntry(action_hash, action_token);
13630 if (action_value == NULL) // this is no action
13632 element_value = getHashEntry(element_hash, list->token);
13633 if (element_value != NULL) // combined element found
13634 add_helpanim_entry(atoi(element_value), -1, -1, delay,
13635 &num_list_entries);
13637 print_unknown_token(filename, list->token, num_unknown_tokens++);
13639 free(element_token);
13640 free(action_token);
13645 direction_value = getHashEntry(direction_hash, direction_token);
13647 if (direction_value != NULL) // direction found
13649 add_helpanim_entry(atoi(element_value), atoi(action_value),
13650 atoi(direction_value), delay, &num_list_entries);
13652 free(element_token);
13653 free(action_token);
13658 // this is no direction
13660 element_value = getHashEntry(element_hash, list->token);
13661 if (element_value != NULL) // combined element found
13662 add_helpanim_entry(atoi(element_value), -1, -1, delay,
13663 &num_list_entries);
13665 print_unknown_token(filename, list->token, num_unknown_tokens++);
13667 free(element_token);
13668 free(action_token);
13671 print_unknown_token_end(num_unknown_tokens);
13673 add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
13674 add_helpanim_entry(HELPANIM_LIST_END, -1, -1, -1, &num_list_entries);
13676 freeSetupFileList(setup_file_list);
13677 freeSetupFileHash(element_hash);
13678 freeSetupFileHash(action_hash);
13679 freeSetupFileHash(direction_hash);
13682 for (i = 0; i < num_list_entries; i++)
13683 Debug("files:LoadHelpAnimInfo", "'%s': %d, %d, %d => %d",
13684 EL_NAME(helpanim_info[i].element),
13685 helpanim_info[i].element,
13686 helpanim_info[i].action,
13687 helpanim_info[i].direction,
13688 helpanim_info[i].delay);
13692 void LoadHelpTextInfo(void)
13694 char *filename = getHelpTextFilename();
13697 if (helptext_info != NULL)
13699 freeSetupFileHash(helptext_info);
13700 helptext_info = NULL;
13703 if (fileExists(filename))
13704 helptext_info = loadSetupFileHash(filename);
13706 if (helptext_info == NULL)
13708 // use reliable default values from static configuration
13709 helptext_info = newSetupFileHash();
13711 for (i = 0; helptext_config[i].token; i++)
13712 setHashEntry(helptext_info,
13713 helptext_config[i].token,
13714 helptext_config[i].value);
13718 BEGIN_HASH_ITERATION(helptext_info, itr)
13720 Debug("files:LoadHelpTextInfo", "'%s' => '%s'",
13721 HASH_ITERATION_TOKEN(itr), HASH_ITERATION_VALUE(itr));
13723 END_HASH_ITERATION(hash, itr)
13728 // ----------------------------------------------------------------------------
13730 // ----------------------------------------------------------------------------
13732 #define MAX_NUM_CONVERT_LEVELS 1000
13734 void ConvertLevels(void)
13736 static LevelDirTree *convert_leveldir = NULL;
13737 static int convert_level_nr = -1;
13738 static int num_levels_handled = 0;
13739 static int num_levels_converted = 0;
13740 static boolean levels_failed[MAX_NUM_CONVERT_LEVELS];
13743 convert_leveldir = getTreeInfoFromIdentifier(leveldir_first,
13744 global.convert_leveldir);
13746 if (convert_leveldir == NULL)
13747 Fail("no such level identifier: '%s'", global.convert_leveldir);
13749 leveldir_current = convert_leveldir;
13751 if (global.convert_level_nr != -1)
13753 convert_leveldir->first_level = global.convert_level_nr;
13754 convert_leveldir->last_level = global.convert_level_nr;
13757 convert_level_nr = convert_leveldir->first_level;
13759 PrintLine("=", 79);
13760 Print("Converting levels\n");
13761 PrintLine("-", 79);
13762 Print("Level series identifier: '%s'\n", convert_leveldir->identifier);
13763 Print("Level series name: '%s'\n", convert_leveldir->name);
13764 Print("Level series author: '%s'\n", convert_leveldir->author);
13765 Print("Number of levels: %d\n", convert_leveldir->levels);
13766 PrintLine("=", 79);
13769 for (i = 0; i < MAX_NUM_CONVERT_LEVELS; i++)
13770 levels_failed[i] = FALSE;
13772 while (convert_level_nr <= convert_leveldir->last_level)
13774 char *level_filename;
13777 level_nr = convert_level_nr++;
13779 Print("Level %03d: ", level_nr);
13781 LoadLevel(level_nr);
13782 if (level.no_level_file || level.no_valid_file)
13784 Print("(no level)\n");
13788 Print("converting level ... ");
13791 // special case: conversion of some EMC levels as requested by ACME
13792 level.game_engine_type = GAME_ENGINE_TYPE_RND;
13795 level_filename = getDefaultLevelFilename(level_nr);
13796 new_level = !fileExists(level_filename);
13800 SaveLevel(level_nr);
13802 num_levels_converted++;
13804 Print("converted.\n");
13808 if (level_nr >= 0 && level_nr < MAX_NUM_CONVERT_LEVELS)
13809 levels_failed[level_nr] = TRUE;
13811 Print("NOT CONVERTED -- LEVEL ALREADY EXISTS.\n");
13814 num_levels_handled++;
13818 PrintLine("=", 79);
13819 Print("Number of levels handled: %d\n", num_levels_handled);
13820 Print("Number of levels converted: %d (%d%%)\n", num_levels_converted,
13821 (num_levels_handled ?
13822 num_levels_converted * 100 / num_levels_handled : 0));
13823 PrintLine("-", 79);
13824 Print("Summary (for automatic parsing by scripts):\n");
13825 Print("LEVELDIR '%s', CONVERTED %d/%d (%d%%)",
13826 convert_leveldir->identifier, num_levels_converted,
13827 num_levels_handled,
13828 (num_levels_handled ?
13829 num_levels_converted * 100 / num_levels_handled : 0));
13831 if (num_levels_handled != num_levels_converted)
13833 Print(", FAILED:");
13834 for (i = 0; i < MAX_NUM_CONVERT_LEVELS; i++)
13835 if (levels_failed[i])
13840 PrintLine("=", 79);
13842 CloseAllAndExit(0);
13846 // ----------------------------------------------------------------------------
13847 // create and save images for use in level sketches (raw BMP format)
13848 // ----------------------------------------------------------------------------
13850 void CreateLevelSketchImages(void)
13856 InitElementPropertiesGfxElement();
13858 bitmap1 = CreateBitmap(TILEX, TILEY, DEFAULT_DEPTH);
13859 bitmap2 = CreateBitmap(MINI_TILEX, MINI_TILEY, DEFAULT_DEPTH);
13861 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
13863 int element = getMappedElement(i);
13864 char basename1[16];
13865 char basename2[16];
13869 sprintf(basename1, "%04d.bmp", i);
13870 sprintf(basename2, "%04ds.bmp", i);
13872 filename1 = getPath2(global.create_sketch_images_dir, basename1);
13873 filename2 = getPath2(global.create_sketch_images_dir, basename2);
13875 DrawSizedElement(0, 0, element, TILESIZE);
13876 BlitBitmap(drawto, bitmap1, SX, SY, TILEX, TILEY, 0, 0);
13878 if (SDL_SaveBMP(bitmap1->surface, filename1) != 0)
13879 Fail("cannot save level sketch image file '%s'", filename1);
13881 DrawSizedElement(0, 0, element, MINI_TILESIZE);
13882 BlitBitmap(drawto, bitmap2, SX, SY, MINI_TILEX, MINI_TILEY, 0, 0);
13884 if (SDL_SaveBMP(bitmap2->surface, filename2) != 0)
13885 Fail("cannot save level sketch image file '%s'", filename2);
13890 // create corresponding SQL statements (for normal and small images)
13893 printf("insert into phpbb_words values (NULL, '`%03d', '<IMG class=\"levelsketch\" src=\"/I/%04d.png\"/>');\n", i, i);
13894 printf("insert into phpbb_words values (NULL, '¸%03d', '<IMG class=\"levelsketch\" src=\"/I/%04ds.png\"/>');\n", i, i);
13897 printf("insert into phpbb_words values (NULL, '`%04d', '<IMG class=\"levelsketch\" src=\"/I/%04d.png\"/>');\n", i, i);
13898 printf("insert into phpbb_words values (NULL, '¸%04d', '<IMG class=\"levelsketch\" src=\"/I/%04ds.png\"/>');\n", i, i);
13900 // optional: create content for forum level sketch demonstration post
13902 fprintf(stderr, "%03d `%03d%c", i, i, (i % 10 < 9 ? ' ' : '\n'));
13905 FreeBitmap(bitmap1);
13906 FreeBitmap(bitmap2);
13909 fprintf(stderr, "\n");
13911 Info("%d normal and small images created", NUM_FILE_ELEMENTS);
13913 CloseAllAndExit(0);
13917 // ----------------------------------------------------------------------------
13918 // create and save images for element collecting animations (raw BMP format)
13919 // ----------------------------------------------------------------------------
13921 static boolean createCollectImage(int element)
13923 return (IS_COLLECTIBLE(element) && !IS_SP_ELEMENT(element));
13926 void CreateCollectElementImages(void)
13930 int anim_frames = num_steps - 1;
13931 int tile_size = TILESIZE;
13932 int anim_width = tile_size * anim_frames;
13933 int anim_height = tile_size;
13934 int num_collect_images = 0;
13935 int pos_collect_images = 0;
13937 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
13938 if (createCollectImage(i))
13939 num_collect_images++;
13941 Info("Creating %d element collecting animation images ...",
13942 num_collect_images);
13944 int dst_width = anim_width * 2;
13945 int dst_height = anim_height * num_collect_images / 2;
13946 Bitmap *dst_bitmap = CreateBitmap(dst_width, dst_height, DEFAULT_DEPTH);
13947 char *basename = "RocksCollect.bmp";
13948 char *filename = getPath2(global.create_collect_images_dir, basename);
13950 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
13952 if (!createCollectImage(i))
13955 int dst_x = (pos_collect_images / (num_collect_images / 2)) * anim_width;
13956 int dst_y = (pos_collect_images % (num_collect_images / 2)) * anim_height;
13957 int graphic = el2img(i);
13958 char *token_name = element_info[i].token_name;
13959 Bitmap *tmp_bitmap = CreateBitmap(tile_size, tile_size, DEFAULT_DEPTH);
13960 Bitmap *src_bitmap;
13963 Info("- creating collecting image for '%s' ...", token_name);
13965 getGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
13967 BlitBitmap(src_bitmap, tmp_bitmap, src_x, src_y,
13968 tile_size, tile_size, 0, 0);
13970 tmp_bitmap->surface_masked = tmp_bitmap->surface;
13972 for (j = 0; j < anim_frames; j++)
13974 int frame_size_final = tile_size * (anim_frames - j) / num_steps;
13975 int frame_size = frame_size_final * num_steps;
13976 int offset = (tile_size - frame_size_final) / 2;
13977 Bitmap *frame_bitmap = ZoomBitmap(tmp_bitmap, frame_size, frame_size);
13979 while (frame_size > frame_size_final)
13983 Bitmap *half_bitmap = ZoomBitmap(frame_bitmap, frame_size, frame_size);
13985 FreeBitmap(frame_bitmap);
13987 frame_bitmap = half_bitmap;
13990 BlitBitmap(frame_bitmap, dst_bitmap, 0, 0,
13991 frame_size_final, frame_size_final,
13992 dst_x + j * tile_size + offset, dst_y + offset);
13994 FreeBitmap(frame_bitmap);
13997 tmp_bitmap->surface_masked = NULL;
13999 FreeBitmap(tmp_bitmap);
14001 pos_collect_images++;
14004 if (SDL_SaveBMP(dst_bitmap->surface, filename) != 0)
14005 Fail("cannot save element collecting image file '%s'", filename);
14007 FreeBitmap(dst_bitmap);
14011 CloseAllAndExit(0);
14015 // ----------------------------------------------------------------------------
14016 // create and save images for custom and group elements (raw BMP format)
14017 // ----------------------------------------------------------------------------
14019 void CreateCustomElementImages(char *directory)
14021 char *src_basename = "RocksCE-template.ilbm";
14022 char *dst_basename = "RocksCE.bmp";
14023 char *src_filename = getPath2(directory, src_basename);
14024 char *dst_filename = getPath2(directory, dst_basename);
14025 Bitmap *src_bitmap;
14027 int yoffset_ce = 0;
14028 int yoffset_ge = (TILEY * NUM_CUSTOM_ELEMENTS / 16);
14031 InitVideoDefaults();
14033 ReCreateBitmap(&backbuffer, video.width, video.height);
14035 src_bitmap = LoadImage(src_filename);
14037 bitmap = CreateBitmap(TILEX * 16 * 2,
14038 TILEY * (NUM_CUSTOM_ELEMENTS + NUM_GROUP_ELEMENTS) / 16,
14041 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
14048 BlitBitmap(src_bitmap, bitmap, 0, 0, TILEX, TILEY,
14049 TILEX * x, TILEY * y + yoffset_ce);
14051 BlitBitmap(src_bitmap, bitmap, 0, TILEY,
14053 TILEX * x + TILEX * 16,
14054 TILEY * y + yoffset_ce);
14056 for (j = 2; j >= 0; j--)
14060 BlitBitmap(src_bitmap, bitmap,
14061 TILEX + c * 7, 0, 6, 10,
14062 TILEX * x + 6 + j * 7,
14063 TILEY * y + 11 + yoffset_ce);
14065 BlitBitmap(src_bitmap, bitmap,
14066 TILEX + c * 8, TILEY, 6, 10,
14067 TILEX * 16 + TILEX * x + 6 + j * 8,
14068 TILEY * y + 10 + yoffset_ce);
14074 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
14081 BlitBitmap(src_bitmap, bitmap, 0, 0, TILEX, TILEY,
14082 TILEX * x, TILEY * y + yoffset_ge);
14084 BlitBitmap(src_bitmap, bitmap, 0, TILEY,
14086 TILEX * x + TILEX * 16,
14087 TILEY * y + yoffset_ge);
14089 for (j = 1; j >= 0; j--)
14093 BlitBitmap(src_bitmap, bitmap, TILEX + c * 10, 11, 10, 10,
14094 TILEX * x + 6 + j * 10,
14095 TILEY * y + 11 + yoffset_ge);
14097 BlitBitmap(src_bitmap, bitmap,
14098 TILEX + c * 8, TILEY + 12, 6, 10,
14099 TILEX * 16 + TILEX * x + 10 + j * 8,
14100 TILEY * y + 10 + yoffset_ge);
14106 if (SDL_SaveBMP(bitmap->surface, dst_filename) != 0)
14107 Fail("cannot save CE graphics file '%s'", dst_filename);
14109 FreeBitmap(bitmap);
14111 CloseAllAndExit(0);