1 // ============================================================================
2 // Rocks'n'Diamonds - McDuffin Strikes Back!
3 // ----------------------------------------------------------------------------
4 // (c) 1995-2014 by Artsoft Entertainment
7 // https://www.artsoft.org/
8 // ----------------------------------------------------------------------------
10 // ============================================================================
17 #include "libgame/libgame.h"
27 #define ENABLE_UNUSED_CODE 0 // currently unused functions
28 #define ENABLE_HISTORIC_CHUNKS 0 // only for historic reference
29 #define ENABLE_RESERVED_CODE 0 // reserved for later use
31 #define CHUNK_ID_LEN 4 // IFF style chunk id length
32 #define CHUNK_SIZE_UNDEFINED 0 // undefined chunk size == 0
33 #define CHUNK_SIZE_NONE -1 // do not write chunk size
35 #define LEVEL_CHUNK_NAME_SIZE MAX_LEVEL_NAME_LEN
36 #define LEVEL_CHUNK_AUTH_SIZE MAX_LEVEL_AUTHOR_LEN
38 #define LEVEL_CHUNK_VERS_SIZE 8 // size of file version chunk
39 #define LEVEL_CHUNK_DATE_SIZE 4 // size of file date chunk
40 #define LEVEL_CHUNK_HEAD_SIZE 80 // size of level file header
41 #define LEVEL_CHUNK_HEAD_UNUSED 0 // unused level header bytes
42 #define LEVEL_CHUNK_CNT2_SIZE 160 // size of level CNT2 chunk
43 #define LEVEL_CHUNK_CNT2_UNUSED 11 // unused CNT2 chunk bytes
44 #define LEVEL_CHUNK_CNT3_HEADER 16 // size of level CNT3 header
45 #define LEVEL_CHUNK_CNT3_UNUSED 10 // unused CNT3 chunk bytes
46 #define LEVEL_CPART_CUS3_SIZE 134 // size of CUS3 chunk part
47 #define LEVEL_CPART_CUS3_UNUSED 15 // unused CUS3 bytes / part
48 #define LEVEL_CHUNK_GRP1_SIZE 74 // size of level GRP1 chunk
50 // (element number, number of change pages, change page number)
51 #define LEVEL_CHUNK_CUSX_UNCHANGED (2 + (1 + 1) + (1 + 1))
53 // (element number only)
54 #define LEVEL_CHUNK_GRPX_UNCHANGED 2
55 #define LEVEL_CHUNK_EMPX_UNCHANGED 2
56 #define LEVEL_CHUNK_NOTE_UNCHANGED 2
58 // (nothing at all if unchanged)
59 #define LEVEL_CHUNK_ELEM_UNCHANGED 0
61 #define TAPE_CHUNK_VERS_SIZE 8 // size of file version chunk
62 #define TAPE_CHUNK_HEAD_SIZE 20 // size of tape file header
63 #define TAPE_CHUNK_HEAD_UNUSED 1 // unused tape header bytes
64 #define TAPE_CHUNK_SCRN_SIZE 2 // size of screen size chunk
66 #define SCORE_CHUNK_VERS_SIZE 8 // size of file version chunk
68 #define LEVEL_CHUNK_CNT3_SIZE(x) (LEVEL_CHUNK_CNT3_HEADER + (x))
69 #define LEVEL_CHUNK_CUS3_SIZE(x) (2 + (x) * LEVEL_CPART_CUS3_SIZE)
70 #define LEVEL_CHUNK_CUS4_SIZE(x) (96 + (x) * 48)
72 // file identifier strings
73 #define LEVEL_COOKIE_TMPL "ROCKSNDIAMONDS_LEVEL_FILE_VERSION_x.x"
74 #define TAPE_COOKIE_TMPL "ROCKSNDIAMONDS_TAPE_FILE_VERSION_x.x"
75 #define SCORE_COOKIE_TMPL "ROCKSNDIAMONDS_SCORE_FILE_VERSION_x.x"
77 // values for deciding when (not) to save configuration data
78 #define SAVE_CONF_NEVER 0
79 #define SAVE_CONF_ALWAYS 1
80 #define SAVE_CONF_WHEN_CHANGED -1
82 // values for chunks using micro chunks
83 #define CONF_MASK_1_BYTE 0x00
84 #define CONF_MASK_2_BYTE 0x40
85 #define CONF_MASK_4_BYTE 0x80
86 #define CONF_MASK_MULTI_BYTES 0xc0
88 #define CONF_MASK_BYTES 0xc0
89 #define CONF_MASK_TOKEN 0x3f
91 #define CONF_VALUE_1_BYTE(x) (CONF_MASK_1_BYTE | (x))
92 #define CONF_VALUE_2_BYTE(x) (CONF_MASK_2_BYTE | (x))
93 #define CONF_VALUE_4_BYTE(x) (CONF_MASK_4_BYTE | (x))
94 #define CONF_VALUE_MULTI_BYTES(x) (CONF_MASK_MULTI_BYTES | (x))
96 // these definitions are just for convenience of use and readability
97 #define CONF_VALUE_8_BIT(x) CONF_VALUE_1_BYTE(x)
98 #define CONF_VALUE_16_BIT(x) CONF_VALUE_2_BYTE(x)
99 #define CONF_VALUE_32_BIT(x) CONF_VALUE_4_BYTE(x)
100 #define CONF_VALUE_BYTES(x) CONF_VALUE_MULTI_BYTES(x)
102 #define CONF_VALUE_NUM_BYTES(x) ((x) == CONF_MASK_1_BYTE ? 1 : \
103 (x) == CONF_MASK_2_BYTE ? 2 : \
104 (x) == CONF_MASK_4_BYTE ? 4 : 0)
106 #define CONF_CONTENT_NUM_ELEMENTS (3 * 3)
107 #define CONF_CONTENT_NUM_BYTES (CONF_CONTENT_NUM_ELEMENTS * 2)
108 #define CONF_ELEMENT_NUM_BYTES (2)
110 #define CONF_ENTITY_NUM_BYTES(t) ((t) == TYPE_ELEMENT || \
111 (t) == TYPE_ELEMENT_LIST ? \
112 CONF_ELEMENT_NUM_BYTES : \
113 (t) == TYPE_CONTENT || \
114 (t) == TYPE_CONTENT_LIST ? \
115 CONF_CONTENT_NUM_BYTES : 1)
117 #define CONF_ELEMENT_BYTE_POS(i) ((i) * CONF_ELEMENT_NUM_BYTES)
118 #define CONF_ELEMENTS_ELEMENT(b,i) ((b[CONF_ELEMENT_BYTE_POS(i)] << 8) | \
119 (b[CONF_ELEMENT_BYTE_POS(i) + 1]))
121 #define CONF_CONTENT_ELEMENT_POS(c,x,y) ((c) * CONF_CONTENT_NUM_ELEMENTS + \
123 #define CONF_CONTENT_BYTE_POS(c,x,y) (CONF_CONTENT_ELEMENT_POS(c,x,y) * \
124 CONF_ELEMENT_NUM_BYTES)
125 #define CONF_CONTENTS_ELEMENT(b,c,x,y) ((b[CONF_CONTENT_BYTE_POS(c,x,y)]<< 8)|\
126 (b[CONF_CONTENT_BYTE_POS(c,x,y) + 1]))
128 // temporary variables used to store pointers to structure members
129 static struct LevelInfo li;
130 static struct ElementInfo xx_ei, yy_ei;
131 static struct ElementChangeInfo xx_change;
132 static struct ElementGroupInfo xx_group;
133 static struct EnvelopeInfo xx_envelope;
134 static unsigned int xx_event_bits[NUM_CE_BITFIELDS];
135 static char xx_default_description[MAX_ELEMENT_NAME_LEN + 1];
136 static int xx_num_contents;
137 static int xx_current_change_page;
138 static char xx_default_string_empty[1] = "";
139 static int xx_string_length_unused;
141 struct LevelFileConfigInfo
143 int element; // element for which data is to be stored
144 int save_type; // save data always, never or when changed
145 int data_type; // data type (used internally, not stored)
146 int conf_type; // micro chunk identifier (stored in file)
149 void *value; // variable that holds the data to be stored
150 int default_value; // initial default value for this variable
153 void *value_copy; // variable that holds the data to be copied
154 void *num_entities; // number of entities for multi-byte data
155 int default_num_entities; // default number of entities for this data
156 int max_num_entities; // maximal number of entities for this data
157 char *default_string; // optional default string for string data
160 static struct LevelFileConfigInfo chunk_config_INFO[] =
162 // ---------- values not related to single elements -------------------------
165 -1, SAVE_CONF_ALWAYS,
166 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
167 &li.game_engine_type, GAME_ENGINE_TYPE_RND
171 -1, SAVE_CONF_ALWAYS,
172 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
173 &li.fieldx, STD_LEV_FIELDX
176 -1, SAVE_CONF_ALWAYS,
177 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
178 &li.fieldy, STD_LEV_FIELDY
182 -1, SAVE_CONF_ALWAYS,
183 TYPE_INTEGER, CONF_VALUE_16_BIT(3),
188 -1, SAVE_CONF_ALWAYS,
189 TYPE_INTEGER, CONF_VALUE_16_BIT(4),
195 TYPE_INTEGER, CONF_VALUE_32_BIT(2),
201 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
202 &li.use_step_counter, FALSE
207 TYPE_BITFIELD, CONF_VALUE_8_BIT(4),
208 &li.wind_direction_initial, MV_NONE
213 TYPE_BOOLEAN, CONF_VALUE_8_BIT(5),
214 &li.em_slippery_gems, FALSE
219 TYPE_BOOLEAN, CONF_VALUE_8_BIT(6),
220 &li.use_custom_template, FALSE
225 TYPE_BITFIELD, CONF_VALUE_32_BIT(1),
226 &li.can_move_into_acid_bits, ~0 // default: everything can
231 TYPE_BITFIELD, CONF_VALUE_8_BIT(7),
232 &li.dont_collide_with_bits, ~0 // default: always deadly
237 TYPE_BOOLEAN, CONF_VALUE_8_BIT(8),
238 &li.em_explodes_by_fire, FALSE
243 TYPE_INTEGER, CONF_VALUE_16_BIT(5),
244 &li.score[SC_TIME_BONUS], 1
249 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
250 &li.auto_exit_sokoban, FALSE
255 TYPE_BOOLEAN, CONF_VALUE_8_BIT(10),
256 &li.auto_count_gems, FALSE
261 TYPE_BOOLEAN, CONF_VALUE_8_BIT(11),
262 &li.solved_by_one_player, FALSE
267 TYPE_INTEGER, CONF_VALUE_8_BIT(12),
268 &li.time_score_base, 1
273 TYPE_BOOLEAN, CONF_VALUE_8_BIT(13),
274 &li.rate_time_over_score, FALSE
284 static struct LevelFileConfigInfo chunk_config_ELEM[] =
286 // (these values are the same for each player)
289 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
290 &li.block_last_field, FALSE // default case for EM levels
294 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
295 &li.sp_block_last_field, TRUE // default case for SP levels
299 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
300 &li.instant_relocation, FALSE
304 TYPE_BOOLEAN, CONF_VALUE_8_BIT(4),
305 &li.can_pass_to_walkable, FALSE
309 TYPE_BOOLEAN, CONF_VALUE_8_BIT(5),
310 &li.block_snap_field, TRUE
314 TYPE_BOOLEAN, CONF_VALUE_8_BIT(6),
315 &li.continuous_snapping, TRUE
319 TYPE_BOOLEAN, CONF_VALUE_8_BIT(12),
320 &li.shifted_relocation, FALSE
324 TYPE_BOOLEAN, CONF_VALUE_8_BIT(15),
325 &li.lazy_relocation, FALSE
329 TYPE_BOOLEAN, CONF_VALUE_8_BIT(16),
330 &li.finish_dig_collect, TRUE
334 TYPE_BOOLEAN, CONF_VALUE_8_BIT(17),
335 &li.keep_walkable_ce, FALSE
338 // (these values are different for each player)
341 TYPE_INTEGER, CONF_VALUE_8_BIT(7),
342 &li.initial_player_stepsize[0], STEPSIZE_NORMAL
346 TYPE_BOOLEAN, CONF_VALUE_8_BIT(8),
347 &li.initial_player_gravity[0], FALSE
351 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
352 &li.use_start_element[0], FALSE
356 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
357 &li.start_element[0], EL_PLAYER_1
361 TYPE_BOOLEAN, CONF_VALUE_8_BIT(10),
362 &li.use_artwork_element[0], FALSE
366 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
367 &li.artwork_element[0], EL_PLAYER_1
371 TYPE_BOOLEAN, CONF_VALUE_8_BIT(11),
372 &li.use_explosion_element[0], FALSE
376 TYPE_ELEMENT, CONF_VALUE_16_BIT(3),
377 &li.explosion_element[0], EL_PLAYER_1
381 TYPE_BOOLEAN, CONF_VALUE_8_BIT(13),
382 &li.use_initial_inventory[0], FALSE
386 TYPE_BOOLEAN, CONF_VALUE_8_BIT(14),
387 &li.initial_inventory_size[0], 1
391 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(1),
392 &li.initial_inventory_content[0][0],EL_EMPTY, NULL,
393 &li.initial_inventory_size[0], 1, MAX_INITIAL_INVENTORY_SIZE
398 TYPE_INTEGER, CONF_VALUE_8_BIT(7),
399 &li.initial_player_stepsize[1], STEPSIZE_NORMAL
403 TYPE_BOOLEAN, CONF_VALUE_8_BIT(8),
404 &li.initial_player_gravity[1], FALSE
408 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
409 &li.use_start_element[1], FALSE
413 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
414 &li.start_element[1], EL_PLAYER_2
418 TYPE_BOOLEAN, CONF_VALUE_8_BIT(10),
419 &li.use_artwork_element[1], FALSE
423 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
424 &li.artwork_element[1], EL_PLAYER_2
428 TYPE_BOOLEAN, CONF_VALUE_8_BIT(11),
429 &li.use_explosion_element[1], FALSE
433 TYPE_ELEMENT, CONF_VALUE_16_BIT(3),
434 &li.explosion_element[1], EL_PLAYER_2
438 TYPE_BOOLEAN, CONF_VALUE_8_BIT(13),
439 &li.use_initial_inventory[1], FALSE
443 TYPE_BOOLEAN, CONF_VALUE_8_BIT(14),
444 &li.initial_inventory_size[1], 1
448 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(1),
449 &li.initial_inventory_content[1][0],EL_EMPTY, NULL,
450 &li.initial_inventory_size[1], 1, MAX_INITIAL_INVENTORY_SIZE
455 TYPE_INTEGER, CONF_VALUE_8_BIT(7),
456 &li.initial_player_stepsize[2], STEPSIZE_NORMAL
460 TYPE_BOOLEAN, CONF_VALUE_8_BIT(8),
461 &li.initial_player_gravity[2], FALSE
465 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
466 &li.use_start_element[2], FALSE
470 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
471 &li.start_element[2], EL_PLAYER_3
475 TYPE_BOOLEAN, CONF_VALUE_8_BIT(10),
476 &li.use_artwork_element[2], FALSE
480 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
481 &li.artwork_element[2], EL_PLAYER_3
485 TYPE_BOOLEAN, CONF_VALUE_8_BIT(11),
486 &li.use_explosion_element[2], FALSE
490 TYPE_ELEMENT, CONF_VALUE_16_BIT(3),
491 &li.explosion_element[2], EL_PLAYER_3
495 TYPE_BOOLEAN, CONF_VALUE_8_BIT(13),
496 &li.use_initial_inventory[2], FALSE
500 TYPE_BOOLEAN, CONF_VALUE_8_BIT(14),
501 &li.initial_inventory_size[2], 1
505 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(1),
506 &li.initial_inventory_content[2][0],EL_EMPTY, NULL,
507 &li.initial_inventory_size[2], 1, MAX_INITIAL_INVENTORY_SIZE
512 TYPE_INTEGER, CONF_VALUE_8_BIT(7),
513 &li.initial_player_stepsize[3], STEPSIZE_NORMAL
517 TYPE_BOOLEAN, CONF_VALUE_8_BIT(8),
518 &li.initial_player_gravity[3], FALSE
522 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
523 &li.use_start_element[3], FALSE
527 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
528 &li.start_element[3], EL_PLAYER_4
532 TYPE_BOOLEAN, CONF_VALUE_8_BIT(10),
533 &li.use_artwork_element[3], FALSE
537 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
538 &li.artwork_element[3], EL_PLAYER_4
542 TYPE_BOOLEAN, CONF_VALUE_8_BIT(11),
543 &li.use_explosion_element[3], FALSE
547 TYPE_ELEMENT, CONF_VALUE_16_BIT(3),
548 &li.explosion_element[3], EL_PLAYER_4
552 TYPE_BOOLEAN, CONF_VALUE_8_BIT(13),
553 &li.use_initial_inventory[3], FALSE
557 TYPE_BOOLEAN, CONF_VALUE_8_BIT(14),
558 &li.initial_inventory_size[3], 1
562 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(1),
563 &li.initial_inventory_content[3][0],EL_EMPTY, NULL,
564 &li.initial_inventory_size[3], 1, MAX_INITIAL_INVENTORY_SIZE
569 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
570 &li.score[SC_EMERALD], 10
575 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
576 &li.score[SC_DIAMOND], 10
581 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
582 &li.score[SC_BUG], 10
587 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
588 &li.score[SC_SPACESHIP], 10
593 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
594 &li.score[SC_PACMAN], 10
599 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
600 &li.score[SC_NUT], 10
605 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
606 &li.score[SC_DYNAMITE], 10
611 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
612 &li.score[SC_KEY], 10
617 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
618 &li.score[SC_PEARL], 10
623 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
624 &li.score[SC_CRYSTAL], 10
629 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
630 &li.amoeba_content, EL_DIAMOND
634 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
639 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
640 &li.grow_into_diggable, TRUE
645 TYPE_CONTENT_LIST, CONF_VALUE_BYTES(1),
646 &li.yamyam_content, EL_ROCK, NULL,
647 &li.num_yamyam_contents, 4, MAX_ELEMENT_CONTENTS
651 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
652 &li.score[SC_YAMYAM], 10
657 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
658 &li.score[SC_ROBOT], 10
662 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
668 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
674 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
675 &li.time_magic_wall, 10
680 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
681 &li.game_of_life[0], 2
685 TYPE_INTEGER, CONF_VALUE_8_BIT(2),
686 &li.game_of_life[1], 3
690 TYPE_INTEGER, CONF_VALUE_8_BIT(3),
691 &li.game_of_life[2], 3
695 TYPE_INTEGER, CONF_VALUE_8_BIT(4),
696 &li.game_of_life[3], 3
700 TYPE_BOOLEAN, CONF_VALUE_8_BIT(5),
701 &li.use_life_bugs, FALSE
706 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
711 TYPE_INTEGER, CONF_VALUE_8_BIT(2),
716 TYPE_INTEGER, CONF_VALUE_8_BIT(3),
721 TYPE_INTEGER, CONF_VALUE_8_BIT(4),
726 EL_TIMEGATE_SWITCH, -1,
727 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
728 &li.time_timegate, 10
732 EL_LIGHT_SWITCH_ACTIVE, -1,
733 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
738 EL_SHIELD_NORMAL, -1,
739 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
740 &li.shield_normal_time, 10
743 EL_SHIELD_NORMAL, -1,
744 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
745 &li.score[SC_SHIELD], 10
749 EL_SHIELD_DEADLY, -1,
750 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
751 &li.shield_deadly_time, 10
754 EL_SHIELD_DEADLY, -1,
755 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
756 &li.score[SC_SHIELD], 10
761 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
766 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
767 &li.extra_time_score, 10
771 EL_TIME_ORB_FULL, -1,
772 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
773 &li.time_orb_time, 10
776 EL_TIME_ORB_FULL, -1,
777 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
778 &li.use_time_orb_bug, FALSE
783 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
784 &li.use_spring_bug, FALSE
789 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
790 &li.android_move_time, 10
794 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
795 &li.android_clone_time, 10
798 EL_EMC_ANDROID, SAVE_CONF_NEVER,
799 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(1),
800 &li.android_clone_element[0], EL_EMPTY, NULL,
801 &li.num_android_clone_elements, 1, MAX_ANDROID_ELEMENTS_OLD
805 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(2),
806 &li.android_clone_element[0], EL_EMPTY, NULL,
807 &li.num_android_clone_elements, 1, MAX_ANDROID_ELEMENTS
812 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
817 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
822 EL_EMC_MAGNIFIER, -1,
823 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
824 &li.magnify_score, 10
827 EL_EMC_MAGNIFIER, -1,
828 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
833 EL_EMC_MAGIC_BALL, -1,
834 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
838 EL_EMC_MAGIC_BALL, -1,
839 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
840 &li.ball_random, FALSE
843 EL_EMC_MAGIC_BALL, -1,
844 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
845 &li.ball_active_initial, FALSE
848 EL_EMC_MAGIC_BALL, -1,
849 TYPE_CONTENT_LIST, CONF_VALUE_BYTES(1),
850 &li.ball_content, EL_EMPTY, NULL,
851 &li.num_ball_contents, 4, MAX_ELEMENT_CONTENTS
855 EL_SOKOBAN_FIELD_EMPTY, -1,
856 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
857 &li.sb_fields_needed, TRUE
861 EL_SOKOBAN_OBJECT, -1,
862 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
863 &li.sb_objects_needed, TRUE
868 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
869 &li.mm_laser_red, FALSE
873 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
874 &li.mm_laser_green, FALSE
878 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
879 &li.mm_laser_blue, TRUE
884 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
885 &li.df_laser_red, TRUE
889 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
890 &li.df_laser_green, TRUE
894 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
895 &li.df_laser_blue, FALSE
899 EL_MM_FUSE_ACTIVE, -1,
900 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
905 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
910 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
914 EL_MM_STEEL_BLOCK, -1,
915 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
916 &li.mm_time_block, 75
920 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
921 &li.score[SC_ELEM_BONUS], 10
924 // ---------- unused values -------------------------------------------------
927 EL_UNKNOWN, SAVE_CONF_NEVER,
928 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
929 &li.score[SC_UNKNOWN_15], 10
939 static struct LevelFileConfigInfo chunk_config_NOTE[] =
943 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
944 &xx_envelope.xsize, MAX_ENVELOPE_XSIZE,
948 TYPE_INTEGER, CONF_VALUE_8_BIT(2),
949 &xx_envelope.ysize, MAX_ENVELOPE_YSIZE,
954 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
955 &xx_envelope.autowrap, FALSE
959 TYPE_BOOLEAN, CONF_VALUE_8_BIT(4),
960 &xx_envelope.centered, FALSE
965 TYPE_STRING, CONF_VALUE_BYTES(1),
966 &xx_envelope.text, -1, NULL,
967 &xx_string_length_unused, -1, MAX_ENVELOPE_TEXT_LEN,
968 &xx_default_string_empty[0]
978 static struct LevelFileConfigInfo chunk_config_CUSX_base[] =
982 TYPE_STRING, CONF_VALUE_BYTES(1),
983 &xx_ei.description[0], -1,
984 &yy_ei.description[0],
985 &xx_string_length_unused, -1, MAX_ELEMENT_NAME_LEN,
986 &xx_default_description[0]
991 TYPE_BITFIELD, CONF_VALUE_32_BIT(1),
992 &xx_ei.properties[EP_BITFIELD_BASE_NR], EP_BITMASK_BASE_DEFAULT,
993 &yy_ei.properties[EP_BITFIELD_BASE_NR]
995 #if ENABLE_RESERVED_CODE
996 // (reserved for later use)
999 TYPE_BITFIELD, CONF_VALUE_32_BIT(2),
1000 &xx_ei.properties[EP_BITFIELD_BASE_NR + 1], EP_BITMASK_DEFAULT,
1001 &yy_ei.properties[EP_BITFIELD_BASE_NR + 1]
1007 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1008 &xx_ei.use_gfx_element, FALSE,
1009 &yy_ei.use_gfx_element
1013 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
1014 &xx_ei.gfx_element_initial, EL_EMPTY_SPACE,
1015 &yy_ei.gfx_element_initial
1020 TYPE_BITFIELD, CONF_VALUE_8_BIT(2),
1021 &xx_ei.access_direction, MV_ALL_DIRECTIONS,
1022 &yy_ei.access_direction
1027 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
1028 &xx_ei.collect_score_initial, 10,
1029 &yy_ei.collect_score_initial
1033 TYPE_INTEGER, CONF_VALUE_16_BIT(3),
1034 &xx_ei.collect_count_initial, 1,
1035 &yy_ei.collect_count_initial
1040 TYPE_INTEGER, CONF_VALUE_16_BIT(4),
1041 &xx_ei.ce_value_fixed_initial, 0,
1042 &yy_ei.ce_value_fixed_initial
1046 TYPE_INTEGER, CONF_VALUE_16_BIT(5),
1047 &xx_ei.ce_value_random_initial, 0,
1048 &yy_ei.ce_value_random_initial
1052 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
1053 &xx_ei.use_last_ce_value, FALSE,
1054 &yy_ei.use_last_ce_value
1059 TYPE_INTEGER, CONF_VALUE_16_BIT(6),
1060 &xx_ei.push_delay_fixed, 8,
1061 &yy_ei.push_delay_fixed
1065 TYPE_INTEGER, CONF_VALUE_16_BIT(7),
1066 &xx_ei.push_delay_random, 8,
1067 &yy_ei.push_delay_random
1071 TYPE_INTEGER, CONF_VALUE_16_BIT(8),
1072 &xx_ei.drop_delay_fixed, 0,
1073 &yy_ei.drop_delay_fixed
1077 TYPE_INTEGER, CONF_VALUE_16_BIT(9),
1078 &xx_ei.drop_delay_random, 0,
1079 &yy_ei.drop_delay_random
1083 TYPE_INTEGER, CONF_VALUE_16_BIT(10),
1084 &xx_ei.move_delay_fixed, 0,
1085 &yy_ei.move_delay_fixed
1089 TYPE_INTEGER, CONF_VALUE_16_BIT(11),
1090 &xx_ei.move_delay_random, 0,
1091 &yy_ei.move_delay_random
1095 TYPE_INTEGER, CONF_VALUE_16_BIT(16),
1096 &xx_ei.step_delay_fixed, 0,
1097 &yy_ei.step_delay_fixed
1101 TYPE_INTEGER, CONF_VALUE_16_BIT(17),
1102 &xx_ei.step_delay_random, 0,
1103 &yy_ei.step_delay_random
1108 TYPE_BITFIELD, CONF_VALUE_32_BIT(3),
1109 &xx_ei.move_pattern, MV_ALL_DIRECTIONS,
1114 TYPE_BITFIELD, CONF_VALUE_8_BIT(4),
1115 &xx_ei.move_direction_initial, MV_START_AUTOMATIC,
1116 &yy_ei.move_direction_initial
1120 TYPE_INTEGER, CONF_VALUE_8_BIT(5),
1121 &xx_ei.move_stepsize, TILEX / 8,
1122 &yy_ei.move_stepsize
1127 TYPE_ELEMENT, CONF_VALUE_16_BIT(12),
1128 &xx_ei.move_enter_element, EL_EMPTY_SPACE,
1129 &yy_ei.move_enter_element
1133 TYPE_ELEMENT, CONF_VALUE_16_BIT(13),
1134 &xx_ei.move_leave_element, EL_EMPTY_SPACE,
1135 &yy_ei.move_leave_element
1139 TYPE_INTEGER, CONF_VALUE_8_BIT(6),
1140 &xx_ei.move_leave_type, LEAVE_TYPE_UNLIMITED,
1141 &yy_ei.move_leave_type
1146 TYPE_INTEGER, CONF_VALUE_8_BIT(7),
1147 &xx_ei.slippery_type, SLIPPERY_ANY_RANDOM,
1148 &yy_ei.slippery_type
1153 TYPE_INTEGER, CONF_VALUE_8_BIT(8),
1154 &xx_ei.explosion_type, EXPLODES_3X3,
1155 &yy_ei.explosion_type
1159 TYPE_INTEGER, CONF_VALUE_16_BIT(14),
1160 &xx_ei.explosion_delay, 16,
1161 &yy_ei.explosion_delay
1165 TYPE_INTEGER, CONF_VALUE_16_BIT(15),
1166 &xx_ei.ignition_delay, 8,
1167 &yy_ei.ignition_delay
1172 TYPE_CONTENT_LIST, CONF_VALUE_BYTES(2),
1173 &xx_ei.content, EL_EMPTY_SPACE,
1175 &xx_num_contents, 1, 1
1178 // ---------- "num_change_pages" must be the last entry ---------------------
1181 -1, SAVE_CONF_ALWAYS,
1182 TYPE_INTEGER, CONF_VALUE_8_BIT(9),
1183 &xx_ei.num_change_pages, 1,
1184 &yy_ei.num_change_pages
1195 static struct LevelFileConfigInfo chunk_config_CUSX_change[] =
1197 // ---------- "current_change_page" must be the first entry -----------------
1200 -1, SAVE_CONF_ALWAYS,
1201 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
1202 &xx_current_change_page, -1
1205 // ---------- (the remaining entries can be in any order) -------------------
1209 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
1210 &xx_change.can_change, FALSE
1215 TYPE_BITFIELD, CONF_VALUE_32_BIT(1),
1216 &xx_event_bits[0], 0
1220 TYPE_BITFIELD, CONF_VALUE_32_BIT(2),
1221 &xx_event_bits[1], 0
1226 TYPE_BITFIELD, CONF_VALUE_8_BIT(3),
1227 &xx_change.trigger_player, CH_PLAYER_ANY
1231 TYPE_BITFIELD, CONF_VALUE_8_BIT(4),
1232 &xx_change.trigger_side, CH_SIDE_ANY
1236 TYPE_BITFIELD, CONF_VALUE_32_BIT(3),
1237 &xx_change.trigger_page, CH_PAGE_ANY
1242 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
1243 &xx_change.target_element, EL_EMPTY_SPACE
1248 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
1249 &xx_change.delay_fixed, 0
1253 TYPE_INTEGER, CONF_VALUE_16_BIT(3),
1254 &xx_change.delay_random, 0
1258 TYPE_INTEGER, CONF_VALUE_16_BIT(4),
1259 &xx_change.delay_frames, FRAMES_PER_SECOND
1264 TYPE_ELEMENT, CONF_VALUE_16_BIT(5),
1265 &xx_change.initial_trigger_element, EL_EMPTY_SPACE
1270 TYPE_BOOLEAN, CONF_VALUE_8_BIT(6),
1271 &xx_change.explode, FALSE
1275 TYPE_BOOLEAN, CONF_VALUE_8_BIT(7),
1276 &xx_change.use_target_content, FALSE
1280 TYPE_BOOLEAN, CONF_VALUE_8_BIT(8),
1281 &xx_change.only_if_complete, FALSE
1285 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
1286 &xx_change.use_random_replace, FALSE
1290 TYPE_INTEGER, CONF_VALUE_8_BIT(10),
1291 &xx_change.random_percentage, 100
1295 TYPE_INTEGER, CONF_VALUE_8_BIT(11),
1296 &xx_change.replace_when, CP_WHEN_EMPTY
1301 TYPE_BOOLEAN, CONF_VALUE_8_BIT(12),
1302 &xx_change.has_action, FALSE
1306 TYPE_INTEGER, CONF_VALUE_8_BIT(13),
1307 &xx_change.action_type, CA_NO_ACTION
1311 TYPE_INTEGER, CONF_VALUE_8_BIT(14),
1312 &xx_change.action_mode, CA_MODE_UNDEFINED
1316 TYPE_INTEGER, CONF_VALUE_16_BIT(6),
1317 &xx_change.action_arg, CA_ARG_UNDEFINED
1322 TYPE_ELEMENT, CONF_VALUE_16_BIT(7),
1323 &xx_change.action_element, EL_EMPTY_SPACE
1328 TYPE_CONTENT_LIST, CONF_VALUE_BYTES(1),
1329 &xx_change.target_content, EL_EMPTY_SPACE, NULL,
1330 &xx_num_contents, 1, 1
1340 static struct LevelFileConfigInfo chunk_config_GRPX[] =
1344 TYPE_STRING, CONF_VALUE_BYTES(1),
1345 &xx_ei.description[0], -1, NULL,
1346 &xx_string_length_unused, -1, MAX_ELEMENT_NAME_LEN,
1347 &xx_default_description[0]
1352 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1353 &xx_ei.use_gfx_element, FALSE
1357 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
1358 &xx_ei.gfx_element_initial, EL_EMPTY_SPACE
1363 TYPE_INTEGER, CONF_VALUE_8_BIT(2),
1364 &xx_group.choice_mode, ANIM_RANDOM
1369 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(2),
1370 &xx_group.element[0], EL_EMPTY_SPACE, NULL,
1371 &xx_group.num_elements, 1, MAX_ELEMENTS_IN_GROUP
1381 static struct LevelFileConfigInfo chunk_config_EMPX[] =
1385 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1386 &xx_ei.use_gfx_element, FALSE
1390 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
1391 &xx_ei.gfx_element_initial, EL_EMPTY_SPACE
1401 static struct LevelFileConfigInfo chunk_config_CONF[] = // (OBSOLETE)
1405 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
1406 &li.block_snap_field, TRUE
1410 TYPE_BOOLEAN, CONF_VALUE_8_BIT(13),
1411 &li.continuous_snapping, TRUE
1415 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
1416 &li.initial_player_stepsize[0], STEPSIZE_NORMAL
1420 TYPE_BOOLEAN, CONF_VALUE_8_BIT(10),
1421 &li.use_start_element[0], FALSE
1425 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
1426 &li.start_element[0], EL_PLAYER_1
1430 TYPE_BOOLEAN, CONF_VALUE_8_BIT(11),
1431 &li.use_artwork_element[0], FALSE
1435 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
1436 &li.artwork_element[0], EL_PLAYER_1
1440 TYPE_BOOLEAN, CONF_VALUE_8_BIT(12),
1441 &li.use_explosion_element[0], FALSE
1445 TYPE_ELEMENT, CONF_VALUE_16_BIT(3),
1446 &li.explosion_element[0], EL_PLAYER_1
1461 filetype_id_list[] =
1463 { LEVEL_FILE_TYPE_RND, "RND" },
1464 { LEVEL_FILE_TYPE_BD, "BD" },
1465 { LEVEL_FILE_TYPE_EM, "EM" },
1466 { LEVEL_FILE_TYPE_SP, "SP" },
1467 { LEVEL_FILE_TYPE_DX, "DX" },
1468 { LEVEL_FILE_TYPE_SB, "SB" },
1469 { LEVEL_FILE_TYPE_DC, "DC" },
1470 { LEVEL_FILE_TYPE_MM, "MM" },
1471 { LEVEL_FILE_TYPE_MM, "DF" },
1476 // ============================================================================
1477 // level file functions
1478 // ============================================================================
1480 static boolean check_special_flags(char *flag)
1482 if (strEqual(options.special_flags, flag) ||
1483 strEqual(leveldir_current->special_flags, flag))
1489 static struct DateInfo getCurrentDate(void)
1491 time_t epoch_seconds = time(NULL);
1492 struct tm *now = localtime(&epoch_seconds);
1493 struct DateInfo date;
1495 date.year = now->tm_year + 1900;
1496 date.month = now->tm_mon + 1;
1497 date.day = now->tm_mday;
1499 date.src = DATE_SRC_CLOCK;
1504 static void resetEventFlags(struct ElementChangeInfo *change)
1508 for (i = 0; i < NUM_CHANGE_EVENTS; i++)
1509 change->has_event[i] = FALSE;
1512 static void resetEventBits(void)
1516 for (i = 0; i < NUM_CE_BITFIELDS; i++)
1517 xx_event_bits[i] = 0;
1520 static void setEventFlagsFromEventBits(struct ElementChangeInfo *change)
1524 /* important: only change event flag if corresponding event bit is set
1525 (this is because all xx_event_bits[] values are loaded separately,
1526 and all xx_event_bits[] values are set back to zero before loading
1527 another value xx_event_bits[x] (each value representing 32 flags)) */
1529 for (i = 0; i < NUM_CHANGE_EVENTS; i++)
1530 if (xx_event_bits[CH_EVENT_BITFIELD_NR(i)] & CH_EVENT_BIT(i))
1531 change->has_event[i] = TRUE;
1534 static void setEventBitsFromEventFlags(struct ElementChangeInfo *change)
1538 /* in contrast to the above function setEventFlagsFromEventBits(), it
1539 would also be possible to set all bits in xx_event_bits[] to 0 or 1
1540 depending on the corresponding change->has_event[i] values here, as
1541 all xx_event_bits[] values are reset in resetEventBits() before */
1543 for (i = 0; i < NUM_CHANGE_EVENTS; i++)
1544 if (change->has_event[i])
1545 xx_event_bits[CH_EVENT_BITFIELD_NR(i)] |= CH_EVENT_BIT(i);
1548 static char *getDefaultElementDescription(struct ElementInfo *ei)
1550 static char description[MAX_ELEMENT_NAME_LEN + 1];
1551 char *default_description = (ei->custom_description != NULL ?
1552 ei->custom_description :
1553 ei->editor_description);
1556 // always start with reliable default values
1557 for (i = 0; i < MAX_ELEMENT_NAME_LEN + 1; i++)
1558 description[i] = '\0';
1560 // truncate element description to MAX_ELEMENT_NAME_LEN bytes
1561 strncpy(description, default_description, MAX_ELEMENT_NAME_LEN);
1563 return &description[0];
1566 static void setElementDescriptionToDefault(struct ElementInfo *ei)
1568 char *default_description = getDefaultElementDescription(ei);
1571 for (i = 0; i < MAX_ELEMENT_NAME_LEN + 1; i++)
1572 ei->description[i] = default_description[i];
1575 static void setConfigToDefaultsFromConfigList(struct LevelFileConfigInfo *conf)
1579 for (i = 0; conf[i].data_type != -1; i++)
1581 int default_value = conf[i].default_value;
1582 int data_type = conf[i].data_type;
1583 int conf_type = conf[i].conf_type;
1584 int byte_mask = conf_type & CONF_MASK_BYTES;
1586 if (byte_mask == CONF_MASK_MULTI_BYTES)
1588 int default_num_entities = conf[i].default_num_entities;
1589 int max_num_entities = conf[i].max_num_entities;
1591 *(int *)(conf[i].num_entities) = default_num_entities;
1593 if (data_type == TYPE_STRING)
1595 char *default_string = conf[i].default_string;
1596 char *string = (char *)(conf[i].value);
1598 strncpy(string, default_string, max_num_entities);
1600 else if (data_type == TYPE_ELEMENT_LIST)
1602 int *element_array = (int *)(conf[i].value);
1605 for (j = 0; j < max_num_entities; j++)
1606 element_array[j] = default_value;
1608 else if (data_type == TYPE_CONTENT_LIST)
1610 struct Content *content = (struct Content *)(conf[i].value);
1613 for (c = 0; c < max_num_entities; c++)
1614 for (y = 0; y < 3; y++)
1615 for (x = 0; x < 3; x++)
1616 content[c].e[x][y] = default_value;
1619 else // constant size configuration data (1, 2 or 4 bytes)
1621 if (data_type == TYPE_BOOLEAN)
1622 *(boolean *)(conf[i].value) = default_value;
1624 *(int *) (conf[i].value) = default_value;
1629 static void copyConfigFromConfigList(struct LevelFileConfigInfo *conf)
1633 for (i = 0; conf[i].data_type != -1; i++)
1635 int data_type = conf[i].data_type;
1636 int conf_type = conf[i].conf_type;
1637 int byte_mask = conf_type & CONF_MASK_BYTES;
1639 if (byte_mask == CONF_MASK_MULTI_BYTES)
1641 int max_num_entities = conf[i].max_num_entities;
1643 if (data_type == TYPE_STRING)
1645 char *string = (char *)(conf[i].value);
1646 char *string_copy = (char *)(conf[i].value_copy);
1648 strncpy(string_copy, string, max_num_entities);
1650 else if (data_type == TYPE_ELEMENT_LIST)
1652 int *element_array = (int *)(conf[i].value);
1653 int *element_array_copy = (int *)(conf[i].value_copy);
1656 for (j = 0; j < max_num_entities; j++)
1657 element_array_copy[j] = element_array[j];
1659 else if (data_type == TYPE_CONTENT_LIST)
1661 struct Content *content = (struct Content *)(conf[i].value);
1662 struct Content *content_copy = (struct Content *)(conf[i].value_copy);
1665 for (c = 0; c < max_num_entities; c++)
1666 for (y = 0; y < 3; y++)
1667 for (x = 0; x < 3; x++)
1668 content_copy[c].e[x][y] = content[c].e[x][y];
1671 else // constant size configuration data (1, 2 or 4 bytes)
1673 if (data_type == TYPE_BOOLEAN)
1674 *(boolean *)(conf[i].value_copy) = *(boolean *)(conf[i].value);
1676 *(int *) (conf[i].value_copy) = *(int *) (conf[i].value);
1681 void copyElementInfo(struct ElementInfo *ei_from, struct ElementInfo *ei_to)
1685 xx_ei = *ei_from; // copy element data into temporary buffer
1686 yy_ei = *ei_to; // copy element data into temporary buffer
1688 copyConfigFromConfigList(chunk_config_CUSX_base);
1693 // ---------- reinitialize and copy change pages ----------
1695 ei_to->num_change_pages = ei_from->num_change_pages;
1696 ei_to->current_change_page = ei_from->current_change_page;
1698 setElementChangePages(ei_to, ei_to->num_change_pages);
1700 for (i = 0; i < ei_to->num_change_pages; i++)
1701 ei_to->change_page[i] = ei_from->change_page[i];
1703 // ---------- copy group element info ----------
1704 if (ei_from->group != NULL && ei_to->group != NULL) // group or internal
1705 *ei_to->group = *ei_from->group;
1707 // mark this custom element as modified
1708 ei_to->modified_settings = TRUE;
1711 void setElementChangePages(struct ElementInfo *ei, int change_pages)
1713 int change_page_size = sizeof(struct ElementChangeInfo);
1715 ei->num_change_pages = MAX(1, change_pages);
1718 checked_realloc(ei->change_page, ei->num_change_pages * change_page_size);
1720 if (ei->current_change_page >= ei->num_change_pages)
1721 ei->current_change_page = ei->num_change_pages - 1;
1723 ei->change = &ei->change_page[ei->current_change_page];
1726 void setElementChangeInfoToDefaults(struct ElementChangeInfo *change)
1728 xx_change = *change; // copy change data into temporary buffer
1730 setConfigToDefaultsFromConfigList(chunk_config_CUSX_change);
1732 *change = xx_change;
1734 resetEventFlags(change);
1736 change->direct_action = 0;
1737 change->other_action = 0;
1739 change->pre_change_function = NULL;
1740 change->change_function = NULL;
1741 change->post_change_function = NULL;
1744 static void setLevelInfoToDefaults_Level(struct LevelInfo *level)
1748 li = *level; // copy level data into temporary buffer
1749 setConfigToDefaultsFromConfigList(chunk_config_INFO);
1750 *level = li; // copy temporary buffer back to level data
1752 setLevelInfoToDefaults_EM();
1753 setLevelInfoToDefaults_SP();
1754 setLevelInfoToDefaults_MM();
1756 level->native_em_level = &native_em_level;
1757 level->native_sp_level = &native_sp_level;
1758 level->native_mm_level = &native_mm_level;
1760 level->file_version = FILE_VERSION_ACTUAL;
1761 level->game_version = GAME_VERSION_ACTUAL;
1763 level->creation_date = getCurrentDate();
1765 level->encoding_16bit_field = TRUE;
1766 level->encoding_16bit_yamyam = TRUE;
1767 level->encoding_16bit_amoeba = TRUE;
1769 // clear level name and level author string buffers
1770 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
1771 level->name[i] = '\0';
1772 for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
1773 level->author[i] = '\0';
1775 // set level name and level author to default values
1776 strcpy(level->name, NAMELESS_LEVEL_NAME);
1777 strcpy(level->author, ANONYMOUS_NAME);
1779 // set level playfield to playable default level with player and exit
1780 for (x = 0; x < MAX_LEV_FIELDX; x++)
1781 for (y = 0; y < MAX_LEV_FIELDY; y++)
1782 level->field[x][y] = EL_SAND;
1784 level->field[0][0] = EL_PLAYER_1;
1785 level->field[STD_LEV_FIELDX - 1][STD_LEV_FIELDY - 1] = EL_EXIT_CLOSED;
1787 BorderElement = EL_STEELWALL;
1789 // detect custom elements when loading them
1790 level->file_has_custom_elements = FALSE;
1792 // set all bug compatibility flags to "false" => do not emulate this bug
1793 level->use_action_after_change_bug = FALSE;
1795 if (leveldir_current)
1797 // try to determine better author name than 'anonymous'
1798 if (!strEqual(leveldir_current->author, ANONYMOUS_NAME))
1800 strncpy(level->author, leveldir_current->author, MAX_LEVEL_AUTHOR_LEN);
1801 level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
1805 switch (LEVELCLASS(leveldir_current))
1807 case LEVELCLASS_TUTORIAL:
1808 strcpy(level->author, PROGRAM_AUTHOR_STRING);
1811 case LEVELCLASS_CONTRIB:
1812 strncpy(level->author, leveldir_current->name, MAX_LEVEL_AUTHOR_LEN);
1813 level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
1816 case LEVELCLASS_PRIVATE:
1817 strncpy(level->author, getRealName(), MAX_LEVEL_AUTHOR_LEN);
1818 level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
1822 // keep default value
1829 static void setLevelInfoToDefaults_Elements(struct LevelInfo *level)
1831 static boolean clipboard_elements_initialized = FALSE;
1834 InitElementPropertiesStatic();
1836 li = *level; // copy level data into temporary buffer
1837 setConfigToDefaultsFromConfigList(chunk_config_ELEM);
1838 *level = li; // copy temporary buffer back to level data
1840 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1843 struct ElementInfo *ei = &element_info[element];
1845 // never initialize clipboard elements after the very first time
1846 // (to be able to use clipboard elements between several levels)
1847 if (IS_CLIPBOARD_ELEMENT(element) && clipboard_elements_initialized)
1850 if (IS_ENVELOPE(element))
1852 int envelope_nr = element - EL_ENVELOPE_1;
1854 setConfigToDefaultsFromConfigList(chunk_config_NOTE);
1856 level->envelope[envelope_nr] = xx_envelope;
1859 if (IS_CUSTOM_ELEMENT(element) ||
1860 IS_GROUP_ELEMENT(element) ||
1861 IS_INTERNAL_ELEMENT(element))
1863 xx_ei = *ei; // copy element data into temporary buffer
1865 setConfigToDefaultsFromConfigList(chunk_config_CUSX_base);
1870 setElementChangePages(ei, 1);
1871 setElementChangeInfoToDefaults(ei->change);
1873 if (IS_CUSTOM_ELEMENT(element) ||
1874 IS_GROUP_ELEMENT(element) ||
1875 IS_INTERNAL_ELEMENT(element))
1877 setElementDescriptionToDefault(ei);
1879 ei->modified_settings = FALSE;
1882 if (IS_CUSTOM_ELEMENT(element) ||
1883 IS_INTERNAL_ELEMENT(element))
1885 // internal values used in level editor
1887 ei->access_type = 0;
1888 ei->access_layer = 0;
1889 ei->access_protected = 0;
1890 ei->walk_to_action = 0;
1891 ei->smash_targets = 0;
1894 ei->can_explode_by_fire = FALSE;
1895 ei->can_explode_smashed = FALSE;
1896 ei->can_explode_impact = FALSE;
1898 ei->current_change_page = 0;
1901 if (IS_GROUP_ELEMENT(element) ||
1902 IS_INTERNAL_ELEMENT(element))
1904 struct ElementGroupInfo *group;
1906 // initialize memory for list of elements in group
1907 if (ei->group == NULL)
1908 ei->group = checked_malloc(sizeof(struct ElementGroupInfo));
1912 xx_group = *group; // copy group data into temporary buffer
1914 setConfigToDefaultsFromConfigList(chunk_config_GRPX);
1919 if (IS_EMPTY_ELEMENT(element) ||
1920 IS_INTERNAL_ELEMENT(element))
1922 xx_ei = *ei; // copy element data into temporary buffer
1924 setConfigToDefaultsFromConfigList(chunk_config_EMPX);
1930 clipboard_elements_initialized = TRUE;
1933 static void setLevelInfoToDefaults(struct LevelInfo *level,
1934 boolean level_info_only,
1935 boolean reset_file_status)
1937 setLevelInfoToDefaults_Level(level);
1939 if (!level_info_only)
1940 setLevelInfoToDefaults_Elements(level);
1942 if (reset_file_status)
1944 level->no_valid_file = FALSE;
1945 level->no_level_file = FALSE;
1948 level->changed = FALSE;
1951 static void setFileInfoToDefaults(struct LevelFileInfo *level_file_info)
1953 level_file_info->nr = 0;
1954 level_file_info->type = LEVEL_FILE_TYPE_UNKNOWN;
1955 level_file_info->packed = FALSE;
1957 setString(&level_file_info->basename, NULL);
1958 setString(&level_file_info->filename, NULL);
1961 int getMappedElement_SB(int, boolean);
1963 static void ActivateLevelTemplate(void)
1967 if (check_special_flags("load_xsb_to_ces"))
1969 // fill smaller playfields with padding "beyond border wall" elements
1970 if (level.fieldx < level_template.fieldx ||
1971 level.fieldy < level_template.fieldy)
1973 short field[level.fieldx][level.fieldy];
1974 int new_fieldx = MAX(level.fieldx, level_template.fieldx);
1975 int new_fieldy = MAX(level.fieldy, level_template.fieldy);
1976 int pos_fieldx = (new_fieldx - level.fieldx) / 2;
1977 int pos_fieldy = (new_fieldy - level.fieldy) / 2;
1979 // copy old playfield (which is smaller than the visible area)
1980 for (y = 0; y < level.fieldy; y++) for (x = 0; x < level.fieldx; x++)
1981 field[x][y] = level.field[x][y];
1983 // fill new, larger playfield with "beyond border wall" elements
1984 for (y = 0; y < new_fieldy; y++) for (x = 0; x < new_fieldx; x++)
1985 level.field[x][y] = getMappedElement_SB('_', TRUE);
1987 // copy the old playfield to the middle of the new playfield
1988 for (y = 0; y < level.fieldy; y++) for (x = 0; x < level.fieldx; x++)
1989 level.field[pos_fieldx + x][pos_fieldy + y] = field[x][y];
1991 level.fieldx = new_fieldx;
1992 level.fieldy = new_fieldy;
1996 // Currently there is no special action needed to activate the template
1997 // data, because 'element_info' property settings overwrite the original
1998 // level data, while all other variables do not change.
2000 // Exception: 'from_level_template' elements in the original level playfield
2001 // are overwritten with the corresponding elements at the same position in
2002 // playfield from the level template.
2004 for (x = 0; x < level.fieldx; x++)
2005 for (y = 0; y < level.fieldy; y++)
2006 if (level.field[x][y] == EL_FROM_LEVEL_TEMPLATE)
2007 level.field[x][y] = level_template.field[x][y];
2009 if (check_special_flags("load_xsb_to_ces"))
2011 struct LevelInfo level_backup = level;
2013 // overwrite all individual level settings from template level settings
2014 level = level_template;
2016 // restore level file info
2017 level.file_info = level_backup.file_info;
2019 // restore playfield size
2020 level.fieldx = level_backup.fieldx;
2021 level.fieldy = level_backup.fieldy;
2023 // restore playfield content
2024 for (x = 0; x < level.fieldx; x++)
2025 for (y = 0; y < level.fieldy; y++)
2026 level.field[x][y] = level_backup.field[x][y];
2028 // restore name and author from individual level
2029 strcpy(level.name, level_backup.name);
2030 strcpy(level.author, level_backup.author);
2032 // restore flag "use_custom_template"
2033 level.use_custom_template = level_backup.use_custom_template;
2037 static char *getLevelFilenameFromBasename(char *basename)
2039 static char *filename = NULL;
2041 checked_free(filename);
2043 filename = getPath2(getCurrentLevelDir(), basename);
2048 static int getFileTypeFromBasename(char *basename)
2050 // !!! ALSO SEE COMMENT IN checkForPackageFromBasename() !!!
2052 static char *filename = NULL;
2053 struct stat file_status;
2055 // ---------- try to determine file type from filename ----------
2057 // check for typical filename of a Supaplex level package file
2058 if (strlen(basename) == 10 && strPrefixLower(basename, "levels.d"))
2059 return LEVEL_FILE_TYPE_SP;
2061 // check for typical filename of a Diamond Caves II level package file
2062 if (strSuffixLower(basename, ".dc") ||
2063 strSuffixLower(basename, ".dc2"))
2064 return LEVEL_FILE_TYPE_DC;
2066 // check for typical filename of a Sokoban level package file
2067 if (strSuffixLower(basename, ".xsb") &&
2068 strchr(basename, '%') == NULL)
2069 return LEVEL_FILE_TYPE_SB;
2071 // ---------- try to determine file type from filesize ----------
2073 checked_free(filename);
2074 filename = getPath2(getCurrentLevelDir(), basename);
2076 if (stat(filename, &file_status) == 0)
2078 // check for typical filesize of a Supaplex level package file
2079 if (file_status.st_size == 170496)
2080 return LEVEL_FILE_TYPE_SP;
2083 return LEVEL_FILE_TYPE_UNKNOWN;
2086 static int getFileTypeFromMagicBytes(char *filename, int type)
2090 if ((file = openFile(filename, MODE_READ)))
2092 char chunk_name[CHUNK_ID_LEN + 1];
2094 getFileChunkBE(file, chunk_name, NULL);
2096 if (strEqual(chunk_name, "MMII") ||
2097 strEqual(chunk_name, "MIRR"))
2098 type = LEVEL_FILE_TYPE_MM;
2106 static boolean checkForPackageFromBasename(char *basename)
2108 // !!! WON'T WORK ANYMORE IF getFileTypeFromBasename() ALSO DETECTS !!!
2109 // !!! SINGLE LEVELS (CURRENTLY ONLY DETECTS LEVEL PACKAGES !!!
2111 return (getFileTypeFromBasename(basename) != LEVEL_FILE_TYPE_UNKNOWN);
2114 static char *getSingleLevelBasenameExt(int nr, char *extension)
2116 static char basename[MAX_FILENAME_LEN];
2119 sprintf(basename, "%s", LEVELTEMPLATE_FILENAME);
2121 sprintf(basename, "%03d.%s", nr, extension);
2126 static char *getSingleLevelBasename(int nr)
2128 return getSingleLevelBasenameExt(nr, LEVELFILE_EXTENSION);
2131 static char *getPackedLevelBasename(int type)
2133 static char basename[MAX_FILENAME_LEN];
2134 char *directory = getCurrentLevelDir();
2136 DirectoryEntry *dir_entry;
2138 strcpy(basename, UNDEFINED_FILENAME); // default: undefined file
2140 if ((dir = openDirectory(directory)) == NULL)
2142 Warn("cannot read current level directory '%s'", directory);
2147 while ((dir_entry = readDirectory(dir)) != NULL) // loop all entries
2149 char *entry_basename = dir_entry->basename;
2150 int entry_type = getFileTypeFromBasename(entry_basename);
2152 if (entry_type != LEVEL_FILE_TYPE_UNKNOWN) // found valid level package
2154 if (type == LEVEL_FILE_TYPE_UNKNOWN ||
2157 strcpy(basename, entry_basename);
2164 closeDirectory(dir);
2169 static char *getSingleLevelFilename(int nr)
2171 return getLevelFilenameFromBasename(getSingleLevelBasename(nr));
2174 #if ENABLE_UNUSED_CODE
2175 static char *getPackedLevelFilename(int type)
2177 return getLevelFilenameFromBasename(getPackedLevelBasename(type));
2181 char *getDefaultLevelFilename(int nr)
2183 return getSingleLevelFilename(nr);
2186 #if ENABLE_UNUSED_CODE
2187 static void setLevelFileInfo_SingleLevelFilename(struct LevelFileInfo *lfi,
2191 lfi->packed = FALSE;
2193 setString(&lfi->basename, getSingleLevelBasename(lfi->nr, lfi->type));
2194 setString(&lfi->filename, getLevelFilenameFromBasename(lfi->basename));
2198 static void setLevelFileInfo_FormatLevelFilename(struct LevelFileInfo *lfi,
2199 int type, char *format, ...)
2201 static char basename[MAX_FILENAME_LEN];
2204 va_start(ap, format);
2205 vsprintf(basename, format, ap);
2209 lfi->packed = FALSE;
2211 setString(&lfi->basename, basename);
2212 setString(&lfi->filename, getLevelFilenameFromBasename(lfi->basename));
2215 static void setLevelFileInfo_PackedLevelFilename(struct LevelFileInfo *lfi,
2221 setString(&lfi->basename, getPackedLevelBasename(lfi->type));
2222 setString(&lfi->filename, getLevelFilenameFromBasename(lfi->basename));
2225 static int getFiletypeFromID(char *filetype_id)
2227 char *filetype_id_lower;
2228 int filetype = LEVEL_FILE_TYPE_UNKNOWN;
2231 if (filetype_id == NULL)
2232 return LEVEL_FILE_TYPE_UNKNOWN;
2234 filetype_id_lower = getStringToLower(filetype_id);
2236 for (i = 0; filetype_id_list[i].id != NULL; i++)
2238 char *id_lower = getStringToLower(filetype_id_list[i].id);
2240 if (strEqual(filetype_id_lower, id_lower))
2241 filetype = filetype_id_list[i].filetype;
2245 if (filetype != LEVEL_FILE_TYPE_UNKNOWN)
2249 free(filetype_id_lower);
2254 char *getLocalLevelTemplateFilename(void)
2256 return getDefaultLevelFilename(-1);
2259 char *getGlobalLevelTemplateFilename(void)
2261 // global variable "leveldir_current" must be modified in the loop below
2262 LevelDirTree *leveldir_current_last = leveldir_current;
2263 char *filename = NULL;
2265 // check for template level in path from current to topmost tree node
2267 while (leveldir_current != NULL)
2269 filename = getDefaultLevelFilename(-1);
2271 if (fileExists(filename))
2274 leveldir_current = leveldir_current->node_parent;
2277 // restore global variable "leveldir_current" modified in above loop
2278 leveldir_current = leveldir_current_last;
2283 static void determineLevelFileInfo_Filename(struct LevelFileInfo *lfi)
2287 // special case: level number is negative => check for level template file
2290 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
2291 getSingleLevelBasename(-1));
2293 // replace local level template filename with global template filename
2294 setString(&lfi->filename, getGlobalLevelTemplateFilename());
2296 // no fallback if template file not existing
2300 // special case: check for file name/pattern specified in "levelinfo.conf"
2301 if (leveldir_current->level_filename != NULL)
2303 int filetype = getFiletypeFromID(leveldir_current->level_filetype);
2305 setLevelFileInfo_FormatLevelFilename(lfi, filetype,
2306 leveldir_current->level_filename, nr);
2308 lfi->packed = checkForPackageFromBasename(leveldir_current->level_filename);
2310 if (fileExists(lfi->filename))
2313 else if (leveldir_current->level_filetype != NULL)
2315 int filetype = getFiletypeFromID(leveldir_current->level_filetype);
2317 // check for specified native level file with standard file name
2318 setLevelFileInfo_FormatLevelFilename(lfi, filetype,
2319 "%03d.%s", nr, LEVELFILE_EXTENSION);
2320 if (fileExists(lfi->filename))
2324 // check for native Rocks'n'Diamonds level file
2325 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
2326 "%03d.%s", nr, LEVELFILE_EXTENSION);
2327 if (fileExists(lfi->filename))
2330 // check for Emerald Mine level file (V1)
2331 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "a%c%c",
2332 'a' + (nr / 10) % 26, '0' + nr % 10);
2333 if (fileExists(lfi->filename))
2335 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "A%c%c",
2336 'A' + (nr / 10) % 26, '0' + nr % 10);
2337 if (fileExists(lfi->filename))
2340 // check for Emerald Mine level file (V2 to V5)
2341 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%d", nr);
2342 if (fileExists(lfi->filename))
2345 // check for Emerald Mine level file (V6 / single mode)
2346 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02ds", nr);
2347 if (fileExists(lfi->filename))
2349 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02dS", nr);
2350 if (fileExists(lfi->filename))
2353 // check for Emerald Mine level file (V6 / teamwork mode)
2354 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02dt", nr);
2355 if (fileExists(lfi->filename))
2357 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02dT", nr);
2358 if (fileExists(lfi->filename))
2361 // check for various packed level file formats
2362 setLevelFileInfo_PackedLevelFilename(lfi, LEVEL_FILE_TYPE_UNKNOWN);
2363 if (fileExists(lfi->filename))
2366 // no known level file found -- use default values (and fail later)
2367 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
2368 "%03d.%s", nr, LEVELFILE_EXTENSION);
2371 static void determineLevelFileInfo_Filetype(struct LevelFileInfo *lfi)
2373 if (lfi->type == LEVEL_FILE_TYPE_UNKNOWN)
2374 lfi->type = getFileTypeFromBasename(lfi->basename);
2376 if (lfi->type == LEVEL_FILE_TYPE_RND)
2377 lfi->type = getFileTypeFromMagicBytes(lfi->filename, lfi->type);
2380 static void setLevelFileInfo(struct LevelFileInfo *level_file_info, int nr)
2382 // always start with reliable default values
2383 setFileInfoToDefaults(level_file_info);
2385 level_file_info->nr = nr; // set requested level number
2387 determineLevelFileInfo_Filename(level_file_info);
2388 determineLevelFileInfo_Filetype(level_file_info);
2391 static void copyLevelFileInfo(struct LevelFileInfo *lfi_from,
2392 struct LevelFileInfo *lfi_to)
2394 lfi_to->nr = lfi_from->nr;
2395 lfi_to->type = lfi_from->type;
2396 lfi_to->packed = lfi_from->packed;
2398 setString(&lfi_to->basename, lfi_from->basename);
2399 setString(&lfi_to->filename, lfi_from->filename);
2402 // ----------------------------------------------------------------------------
2403 // functions for loading R'n'D level
2404 // ----------------------------------------------------------------------------
2406 int getMappedElement(int element)
2408 // remap some (historic, now obsolete) elements
2412 case EL_PLAYER_OBSOLETE:
2413 element = EL_PLAYER_1;
2416 case EL_KEY_OBSOLETE:
2420 case EL_EM_KEY_1_FILE_OBSOLETE:
2421 element = EL_EM_KEY_1;
2424 case EL_EM_KEY_2_FILE_OBSOLETE:
2425 element = EL_EM_KEY_2;
2428 case EL_EM_KEY_3_FILE_OBSOLETE:
2429 element = EL_EM_KEY_3;
2432 case EL_EM_KEY_4_FILE_OBSOLETE:
2433 element = EL_EM_KEY_4;
2436 case EL_ENVELOPE_OBSOLETE:
2437 element = EL_ENVELOPE_1;
2445 if (element >= NUM_FILE_ELEMENTS)
2447 Warn("invalid level element %d", element);
2449 element = EL_UNKNOWN;
2457 static int getMappedElementByVersion(int element, int game_version)
2459 // remap some elements due to certain game version
2461 if (game_version <= VERSION_IDENT(2,2,0,0))
2463 // map game font elements
2464 element = (element == EL_CHAR('[') ? EL_CHAR_AUMLAUT :
2465 element == EL_CHAR('\\') ? EL_CHAR_OUMLAUT :
2466 element == EL_CHAR(']') ? EL_CHAR_UUMLAUT :
2467 element == EL_CHAR('^') ? EL_CHAR_COPYRIGHT : element);
2470 if (game_version < VERSION_IDENT(3,0,0,0))
2472 // map Supaplex gravity tube elements
2473 element = (element == EL_SP_GRAVITY_PORT_LEFT ? EL_SP_PORT_LEFT :
2474 element == EL_SP_GRAVITY_PORT_RIGHT ? EL_SP_PORT_RIGHT :
2475 element == EL_SP_GRAVITY_PORT_UP ? EL_SP_PORT_UP :
2476 element == EL_SP_GRAVITY_PORT_DOWN ? EL_SP_PORT_DOWN :
2483 static int LoadLevel_VERS(File *file, int chunk_size, struct LevelInfo *level)
2485 level->file_version = getFileVersion(file);
2486 level->game_version = getFileVersion(file);
2491 static int LoadLevel_DATE(File *file, int chunk_size, struct LevelInfo *level)
2493 level->creation_date.year = getFile16BitBE(file);
2494 level->creation_date.month = getFile8Bit(file);
2495 level->creation_date.day = getFile8Bit(file);
2497 level->creation_date.src = DATE_SRC_LEVELFILE;
2502 static int LoadLevel_HEAD(File *file, int chunk_size, struct LevelInfo *level)
2504 int initial_player_stepsize;
2505 int initial_player_gravity;
2508 level->fieldx = getFile8Bit(file);
2509 level->fieldy = getFile8Bit(file);
2511 level->time = getFile16BitBE(file);
2512 level->gems_needed = getFile16BitBE(file);
2514 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
2515 level->name[i] = getFile8Bit(file);
2516 level->name[MAX_LEVEL_NAME_LEN] = 0;
2518 for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
2519 level->score[i] = getFile8Bit(file);
2521 level->num_yamyam_contents = STD_ELEMENT_CONTENTS;
2522 for (i = 0; i < STD_ELEMENT_CONTENTS; i++)
2523 for (y = 0; y < 3; y++)
2524 for (x = 0; x < 3; x++)
2525 level->yamyam_content[i].e[x][y] = getMappedElement(getFile8Bit(file));
2527 level->amoeba_speed = getFile8Bit(file);
2528 level->time_magic_wall = getFile8Bit(file);
2529 level->time_wheel = getFile8Bit(file);
2530 level->amoeba_content = getMappedElement(getFile8Bit(file));
2532 initial_player_stepsize = (getFile8Bit(file) == 1 ? STEPSIZE_FAST :
2535 for (i = 0; i < MAX_PLAYERS; i++)
2536 level->initial_player_stepsize[i] = initial_player_stepsize;
2538 initial_player_gravity = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2540 for (i = 0; i < MAX_PLAYERS; i++)
2541 level->initial_player_gravity[i] = initial_player_gravity;
2543 level->encoding_16bit_field = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2544 level->em_slippery_gems = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2546 level->use_custom_template = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2548 level->block_last_field = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2549 level->sp_block_last_field = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2550 level->can_move_into_acid_bits = getFile32BitBE(file);
2551 level->dont_collide_with_bits = getFile8Bit(file);
2553 level->use_spring_bug = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2554 level->use_step_counter = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2556 level->instant_relocation = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2557 level->can_pass_to_walkable = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2558 level->grow_into_diggable = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2560 level->game_engine_type = getFile8Bit(file);
2562 ReadUnusedBytesFromFile(file, LEVEL_CHUNK_HEAD_UNUSED);
2567 static int LoadLevel_NAME(File *file, int chunk_size, struct LevelInfo *level)
2571 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
2572 level->name[i] = getFile8Bit(file);
2573 level->name[MAX_LEVEL_NAME_LEN] = 0;
2578 static int LoadLevel_AUTH(File *file, int chunk_size, struct LevelInfo *level)
2582 for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
2583 level->author[i] = getFile8Bit(file);
2584 level->author[MAX_LEVEL_AUTHOR_LEN] = 0;
2589 static int LoadLevel_BODY(File *file, int chunk_size, struct LevelInfo *level)
2592 int chunk_size_expected = level->fieldx * level->fieldy;
2594 /* Note: "chunk_size" was wrong before version 2.0 when elements are
2595 stored with 16-bit encoding (and should be twice as big then).
2596 Even worse, playfield data was stored 16-bit when only yamyam content
2597 contained 16-bit elements and vice versa. */
2599 if (level->encoding_16bit_field && level->file_version >= FILE_VERSION_2_0)
2600 chunk_size_expected *= 2;
2602 if (chunk_size_expected != chunk_size)
2604 ReadUnusedBytesFromFile(file, chunk_size);
2605 return chunk_size_expected;
2608 for (y = 0; y < level->fieldy; y++)
2609 for (x = 0; x < level->fieldx; x++)
2610 level->field[x][y] =
2611 getMappedElement(level->encoding_16bit_field ? getFile16BitBE(file) :
2616 static int LoadLevel_CONT(File *file, int chunk_size, struct LevelInfo *level)
2619 int header_size = 4;
2620 int content_size = MAX_ELEMENT_CONTENTS * 3 * 3;
2621 int chunk_size_expected = header_size + content_size;
2623 /* Note: "chunk_size" was wrong before version 2.0 when elements are
2624 stored with 16-bit encoding (and should be twice as big then).
2625 Even worse, playfield data was stored 16-bit when only yamyam content
2626 contained 16-bit elements and vice versa. */
2628 if (level->encoding_16bit_field && level->file_version >= FILE_VERSION_2_0)
2629 chunk_size_expected += content_size;
2631 if (chunk_size_expected != chunk_size)
2633 ReadUnusedBytesFromFile(file, chunk_size);
2634 return chunk_size_expected;
2638 level->num_yamyam_contents = getFile8Bit(file);
2642 // correct invalid number of content fields -- should never happen
2643 if (level->num_yamyam_contents < 1 ||
2644 level->num_yamyam_contents > MAX_ELEMENT_CONTENTS)
2645 level->num_yamyam_contents = STD_ELEMENT_CONTENTS;
2647 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
2648 for (y = 0; y < 3; y++)
2649 for (x = 0; x < 3; x++)
2650 level->yamyam_content[i].e[x][y] =
2651 getMappedElement(level->encoding_16bit_field ?
2652 getFile16BitBE(file) : getFile8Bit(file));
2656 static int LoadLevel_CNT2(File *file, int chunk_size, struct LevelInfo *level)
2661 int content_array[MAX_ELEMENT_CONTENTS][3][3];
2663 element = getMappedElement(getFile16BitBE(file));
2664 num_contents = getFile8Bit(file);
2666 getFile8Bit(file); // content x size (unused)
2667 getFile8Bit(file); // content y size (unused)
2669 ReadUnusedBytesFromFile(file, LEVEL_CHUNK_CNT2_UNUSED);
2671 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
2672 for (y = 0; y < 3; y++)
2673 for (x = 0; x < 3; x++)
2674 content_array[i][x][y] = getMappedElement(getFile16BitBE(file));
2676 // correct invalid number of content fields -- should never happen
2677 if (num_contents < 1 || num_contents > MAX_ELEMENT_CONTENTS)
2678 num_contents = STD_ELEMENT_CONTENTS;
2680 if (element == EL_YAMYAM)
2682 level->num_yamyam_contents = num_contents;
2684 for (i = 0; i < num_contents; i++)
2685 for (y = 0; y < 3; y++)
2686 for (x = 0; x < 3; x++)
2687 level->yamyam_content[i].e[x][y] = content_array[i][x][y];
2689 else if (element == EL_BD_AMOEBA)
2691 level->amoeba_content = content_array[0][0][0];
2695 Warn("cannot load content for element '%d'", element);
2701 static int LoadLevel_CNT3(File *file, int chunk_size, struct LevelInfo *level)
2707 int chunk_size_expected;
2709 element = getMappedElement(getFile16BitBE(file));
2710 if (!IS_ENVELOPE(element))
2711 element = EL_ENVELOPE_1;
2713 envelope_nr = element - EL_ENVELOPE_1;
2715 envelope_len = getFile16BitBE(file);
2717 level->envelope[envelope_nr].xsize = getFile8Bit(file);
2718 level->envelope[envelope_nr].ysize = getFile8Bit(file);
2720 ReadUnusedBytesFromFile(file, LEVEL_CHUNK_CNT3_UNUSED);
2722 chunk_size_expected = LEVEL_CHUNK_CNT3_SIZE(envelope_len);
2723 if (chunk_size_expected != chunk_size)
2725 ReadUnusedBytesFromFile(file, chunk_size - LEVEL_CHUNK_CNT3_HEADER);
2726 return chunk_size_expected;
2729 for (i = 0; i < envelope_len; i++)
2730 level->envelope[envelope_nr].text[i] = getFile8Bit(file);
2735 static int LoadLevel_CUS1(File *file, int chunk_size, struct LevelInfo *level)
2737 int num_changed_custom_elements = getFile16BitBE(file);
2738 int chunk_size_expected = 2 + num_changed_custom_elements * 6;
2741 if (chunk_size_expected != chunk_size)
2743 ReadUnusedBytesFromFile(file, chunk_size - 2);
2744 return chunk_size_expected;
2747 for (i = 0; i < num_changed_custom_elements; i++)
2749 int element = getMappedElement(getFile16BitBE(file));
2750 int properties = getFile32BitBE(file);
2752 if (IS_CUSTOM_ELEMENT(element))
2753 element_info[element].properties[EP_BITFIELD_BASE_NR] = properties;
2755 Warn("invalid custom element number %d", element);
2757 // older game versions that wrote level files with CUS1 chunks used
2758 // different default push delay values (not yet stored in level file)
2759 element_info[element].push_delay_fixed = 2;
2760 element_info[element].push_delay_random = 8;
2763 level->file_has_custom_elements = TRUE;
2768 static int LoadLevel_CUS2(File *file, int chunk_size, struct LevelInfo *level)
2770 int num_changed_custom_elements = getFile16BitBE(file);
2771 int chunk_size_expected = 2 + num_changed_custom_elements * 4;
2774 if (chunk_size_expected != chunk_size)
2776 ReadUnusedBytesFromFile(file, chunk_size - 2);
2777 return chunk_size_expected;
2780 for (i = 0; i < num_changed_custom_elements; i++)
2782 int element = getMappedElement(getFile16BitBE(file));
2783 int custom_target_element = getMappedElement(getFile16BitBE(file));
2785 if (IS_CUSTOM_ELEMENT(element))
2786 element_info[element].change->target_element = custom_target_element;
2788 Warn("invalid custom element number %d", element);
2791 level->file_has_custom_elements = TRUE;
2796 static int LoadLevel_CUS3(File *file, int chunk_size, struct LevelInfo *level)
2798 int num_changed_custom_elements = getFile16BitBE(file);
2799 int chunk_size_expected = LEVEL_CHUNK_CUS3_SIZE(num_changed_custom_elements);
2802 if (chunk_size_expected != chunk_size)
2804 ReadUnusedBytesFromFile(file, chunk_size - 2);
2805 return chunk_size_expected;
2808 for (i = 0; i < num_changed_custom_elements; i++)
2810 int element = getMappedElement(getFile16BitBE(file));
2811 struct ElementInfo *ei = &element_info[element];
2812 unsigned int event_bits;
2814 if (!IS_CUSTOM_ELEMENT(element))
2816 Warn("invalid custom element number %d", element);
2818 element = EL_INTERNAL_DUMMY;
2821 for (j = 0; j < MAX_ELEMENT_NAME_LEN; j++)
2822 ei->description[j] = getFile8Bit(file);
2823 ei->description[MAX_ELEMENT_NAME_LEN] = 0;
2825 ei->properties[EP_BITFIELD_BASE_NR] = getFile32BitBE(file);
2827 // some free bytes for future properties and padding
2828 ReadUnusedBytesFromFile(file, 7);
2830 ei->use_gfx_element = getFile8Bit(file);
2831 ei->gfx_element_initial = getMappedElement(getFile16BitBE(file));
2833 ei->collect_score_initial = getFile8Bit(file);
2834 ei->collect_count_initial = getFile8Bit(file);
2836 ei->push_delay_fixed = getFile16BitBE(file);
2837 ei->push_delay_random = getFile16BitBE(file);
2838 ei->move_delay_fixed = getFile16BitBE(file);
2839 ei->move_delay_random = getFile16BitBE(file);
2841 ei->move_pattern = getFile16BitBE(file);
2842 ei->move_direction_initial = getFile8Bit(file);
2843 ei->move_stepsize = getFile8Bit(file);
2845 for (y = 0; y < 3; y++)
2846 for (x = 0; x < 3; x++)
2847 ei->content.e[x][y] = getMappedElement(getFile16BitBE(file));
2849 // bits 0 - 31 of "has_event[]"
2850 event_bits = getFile32BitBE(file);
2851 for (j = 0; j < MIN(NUM_CHANGE_EVENTS, 32); j++)
2852 if (event_bits & (1 << j))
2853 ei->change->has_event[j] = TRUE;
2855 ei->change->target_element = getMappedElement(getFile16BitBE(file));
2857 ei->change->delay_fixed = getFile16BitBE(file);
2858 ei->change->delay_random = getFile16BitBE(file);
2859 ei->change->delay_frames = getFile16BitBE(file);
2861 ei->change->initial_trigger_element= getMappedElement(getFile16BitBE(file));
2863 ei->change->explode = getFile8Bit(file);
2864 ei->change->use_target_content = getFile8Bit(file);
2865 ei->change->only_if_complete = getFile8Bit(file);
2866 ei->change->use_random_replace = getFile8Bit(file);
2868 ei->change->random_percentage = getFile8Bit(file);
2869 ei->change->replace_when = getFile8Bit(file);
2871 for (y = 0; y < 3; y++)
2872 for (x = 0; x < 3; x++)
2873 ei->change->target_content.e[x][y] =
2874 getMappedElement(getFile16BitBE(file));
2876 ei->slippery_type = getFile8Bit(file);
2878 // some free bytes for future properties and padding
2879 ReadUnusedBytesFromFile(file, LEVEL_CPART_CUS3_UNUSED);
2881 // mark that this custom element has been modified
2882 ei->modified_settings = TRUE;
2885 level->file_has_custom_elements = TRUE;
2890 static int LoadLevel_CUS4(File *file, int chunk_size, struct LevelInfo *level)
2892 struct ElementInfo *ei;
2893 int chunk_size_expected;
2897 // ---------- custom element base property values (96 bytes) ----------------
2899 element = getMappedElement(getFile16BitBE(file));
2901 if (!IS_CUSTOM_ELEMENT(element))
2903 Warn("invalid custom element number %d", element);
2905 ReadUnusedBytesFromFile(file, chunk_size - 2);
2910 ei = &element_info[element];
2912 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
2913 ei->description[i] = getFile8Bit(file);
2914 ei->description[MAX_ELEMENT_NAME_LEN] = 0;
2916 ei->properties[EP_BITFIELD_BASE_NR] = getFile32BitBE(file);
2918 ReadUnusedBytesFromFile(file, 4); // reserved for more base properties
2920 ei->num_change_pages = getFile8Bit(file);
2922 chunk_size_expected = LEVEL_CHUNK_CUS4_SIZE(ei->num_change_pages);
2923 if (chunk_size_expected != chunk_size)
2925 ReadUnusedBytesFromFile(file, chunk_size - 43);
2926 return chunk_size_expected;
2929 ei->ce_value_fixed_initial = getFile16BitBE(file);
2930 ei->ce_value_random_initial = getFile16BitBE(file);
2931 ei->use_last_ce_value = getFile8Bit(file);
2933 ei->use_gfx_element = getFile8Bit(file);
2934 ei->gfx_element_initial = getMappedElement(getFile16BitBE(file));
2936 ei->collect_score_initial = getFile8Bit(file);
2937 ei->collect_count_initial = getFile8Bit(file);
2939 ei->drop_delay_fixed = getFile8Bit(file);
2940 ei->push_delay_fixed = getFile8Bit(file);
2941 ei->drop_delay_random = getFile8Bit(file);
2942 ei->push_delay_random = getFile8Bit(file);
2943 ei->move_delay_fixed = getFile16BitBE(file);
2944 ei->move_delay_random = getFile16BitBE(file);
2946 // bits 0 - 15 of "move_pattern" ...
2947 ei->move_pattern = getFile16BitBE(file);
2948 ei->move_direction_initial = getFile8Bit(file);
2949 ei->move_stepsize = getFile8Bit(file);
2951 ei->slippery_type = getFile8Bit(file);
2953 for (y = 0; y < 3; y++)
2954 for (x = 0; x < 3; x++)
2955 ei->content.e[x][y] = getMappedElement(getFile16BitBE(file));
2957 ei->move_enter_element = getMappedElement(getFile16BitBE(file));
2958 ei->move_leave_element = getMappedElement(getFile16BitBE(file));
2959 ei->move_leave_type = getFile8Bit(file);
2961 // ... bits 16 - 31 of "move_pattern" (not nice, but downward compatible)
2962 ei->move_pattern |= (getFile16BitBE(file) << 16);
2964 ei->access_direction = getFile8Bit(file);
2966 ei->explosion_delay = getFile8Bit(file);
2967 ei->ignition_delay = getFile8Bit(file);
2968 ei->explosion_type = getFile8Bit(file);
2970 // some free bytes for future custom property values and padding
2971 ReadUnusedBytesFromFile(file, 1);
2973 // ---------- change page property values (48 bytes) ------------------------
2975 setElementChangePages(ei, ei->num_change_pages);
2977 for (i = 0; i < ei->num_change_pages; i++)
2979 struct ElementChangeInfo *change = &ei->change_page[i];
2980 unsigned int event_bits;
2982 // always start with reliable default values
2983 setElementChangeInfoToDefaults(change);
2985 // bits 0 - 31 of "has_event[]" ...
2986 event_bits = getFile32BitBE(file);
2987 for (j = 0; j < MIN(NUM_CHANGE_EVENTS, 32); j++)
2988 if (event_bits & (1 << j))
2989 change->has_event[j] = TRUE;
2991 change->target_element = getMappedElement(getFile16BitBE(file));
2993 change->delay_fixed = getFile16BitBE(file);
2994 change->delay_random = getFile16BitBE(file);
2995 change->delay_frames = getFile16BitBE(file);
2997 change->initial_trigger_element = getMappedElement(getFile16BitBE(file));
2999 change->explode = getFile8Bit(file);
3000 change->use_target_content = getFile8Bit(file);
3001 change->only_if_complete = getFile8Bit(file);
3002 change->use_random_replace = getFile8Bit(file);
3004 change->random_percentage = getFile8Bit(file);
3005 change->replace_when = getFile8Bit(file);
3007 for (y = 0; y < 3; y++)
3008 for (x = 0; x < 3; x++)
3009 change->target_content.e[x][y]= getMappedElement(getFile16BitBE(file));
3011 change->can_change = getFile8Bit(file);
3013 change->trigger_side = getFile8Bit(file);
3015 change->trigger_player = getFile8Bit(file);
3016 change->trigger_page = getFile8Bit(file);
3018 change->trigger_page = (change->trigger_page == CH_PAGE_ANY_FILE ?
3019 CH_PAGE_ANY : (1 << change->trigger_page));
3021 change->has_action = getFile8Bit(file);
3022 change->action_type = getFile8Bit(file);
3023 change->action_mode = getFile8Bit(file);
3024 change->action_arg = getFile16BitBE(file);
3026 // ... bits 32 - 39 of "has_event[]" (not nice, but downward compatible)
3027 event_bits = getFile8Bit(file);
3028 for (j = 32; j < NUM_CHANGE_EVENTS; j++)
3029 if (event_bits & (1 << (j - 32)))
3030 change->has_event[j] = TRUE;
3033 // mark this custom element as modified
3034 ei->modified_settings = TRUE;
3036 level->file_has_custom_elements = TRUE;
3041 static int LoadLevel_GRP1(File *file, int chunk_size, struct LevelInfo *level)
3043 struct ElementInfo *ei;
3044 struct ElementGroupInfo *group;
3048 element = getMappedElement(getFile16BitBE(file));
3050 if (!IS_GROUP_ELEMENT(element))
3052 Warn("invalid group element number %d", element);
3054 ReadUnusedBytesFromFile(file, chunk_size - 2);
3059 ei = &element_info[element];
3061 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
3062 ei->description[i] = getFile8Bit(file);
3063 ei->description[MAX_ELEMENT_NAME_LEN] = 0;
3065 group = element_info[element].group;
3067 group->num_elements = getFile8Bit(file);
3069 ei->use_gfx_element = getFile8Bit(file);
3070 ei->gfx_element_initial = getMappedElement(getFile16BitBE(file));
3072 group->choice_mode = getFile8Bit(file);
3074 // some free bytes for future values and padding
3075 ReadUnusedBytesFromFile(file, 3);
3077 for (i = 0; i < MAX_ELEMENTS_IN_GROUP; i++)
3078 group->element[i] = getMappedElement(getFile16BitBE(file));
3080 // mark this group element as modified
3081 element_info[element].modified_settings = TRUE;
3083 level->file_has_custom_elements = TRUE;
3088 static int LoadLevel_MicroChunk(File *file, struct LevelFileConfigInfo *conf,
3089 int element, int real_element)
3091 int micro_chunk_size = 0;
3092 int conf_type = getFile8Bit(file);
3093 int byte_mask = conf_type & CONF_MASK_BYTES;
3094 boolean element_found = FALSE;
3097 micro_chunk_size += 1;
3099 if (byte_mask == CONF_MASK_MULTI_BYTES)
3101 int num_bytes = getFile16BitBE(file);
3102 byte *buffer = checked_malloc(num_bytes);
3104 ReadBytesFromFile(file, buffer, num_bytes);
3106 for (i = 0; conf[i].data_type != -1; i++)
3108 if (conf[i].element == element &&
3109 conf[i].conf_type == conf_type)
3111 int data_type = conf[i].data_type;
3112 int num_entities = num_bytes / CONF_ENTITY_NUM_BYTES(data_type);
3113 int max_num_entities = conf[i].max_num_entities;
3115 if (num_entities > max_num_entities)
3117 Warn("truncating number of entities for element %d from %d to %d",
3118 element, num_entities, max_num_entities);
3120 num_entities = max_num_entities;
3123 if (num_entities == 0 && (data_type == TYPE_ELEMENT_LIST ||
3124 data_type == TYPE_CONTENT_LIST))
3126 // for element and content lists, zero entities are not allowed
3127 Warn("found empty list of entities for element %d", element);
3129 // do not set "num_entities" here to prevent reading behind buffer
3131 *(int *)(conf[i].num_entities) = 1; // at least one is required
3135 *(int *)(conf[i].num_entities) = num_entities;
3138 element_found = TRUE;
3140 if (data_type == TYPE_STRING)
3142 char *string = (char *)(conf[i].value);
3145 for (j = 0; j < max_num_entities; j++)
3146 string[j] = (j < num_entities ? buffer[j] : '\0');
3148 else if (data_type == TYPE_ELEMENT_LIST)
3150 int *element_array = (int *)(conf[i].value);
3153 for (j = 0; j < num_entities; j++)
3155 getMappedElement(CONF_ELEMENTS_ELEMENT(buffer, j));
3157 else if (data_type == TYPE_CONTENT_LIST)
3159 struct Content *content= (struct Content *)(conf[i].value);
3162 for (c = 0; c < num_entities; c++)
3163 for (y = 0; y < 3; y++)
3164 for (x = 0; x < 3; x++)
3165 content[c].e[x][y] =
3166 getMappedElement(CONF_CONTENTS_ELEMENT(buffer, c, x, y));
3169 element_found = FALSE;
3175 checked_free(buffer);
3177 micro_chunk_size += 2 + num_bytes;
3179 else // constant size configuration data (1, 2 or 4 bytes)
3181 int value = (byte_mask == CONF_MASK_1_BYTE ? getFile8Bit (file) :
3182 byte_mask == CONF_MASK_2_BYTE ? getFile16BitBE(file) :
3183 byte_mask == CONF_MASK_4_BYTE ? getFile32BitBE(file) : 0);
3185 for (i = 0; conf[i].data_type != -1; i++)
3187 if (conf[i].element == element &&
3188 conf[i].conf_type == conf_type)
3190 int data_type = conf[i].data_type;
3192 if (data_type == TYPE_ELEMENT)
3193 value = getMappedElement(value);
3195 if (data_type == TYPE_BOOLEAN)
3196 *(boolean *)(conf[i].value) = (value ? TRUE : FALSE);
3198 *(int *) (conf[i].value) = value;
3200 element_found = TRUE;
3206 micro_chunk_size += CONF_VALUE_NUM_BYTES(byte_mask);
3211 char *error_conf_chunk_bytes =
3212 (byte_mask == CONF_MASK_1_BYTE ? "CONF_VALUE_8_BIT" :
3213 byte_mask == CONF_MASK_2_BYTE ? "CONF_VALUE_16_BIT" :
3214 byte_mask == CONF_MASK_4_BYTE ? "CONF_VALUE_32_BIT" :"CONF_VALUE_BYTES");
3215 int error_conf_chunk_token = conf_type & CONF_MASK_TOKEN;
3216 int error_element = real_element;
3218 Warn("cannot load micro chunk '%s(%d)' value for element %d ['%s']",
3219 error_conf_chunk_bytes, error_conf_chunk_token,
3220 error_element, EL_NAME(error_element));
3223 return micro_chunk_size;
3226 static int LoadLevel_INFO(File *file, int chunk_size, struct LevelInfo *level)
3228 int real_chunk_size = 0;
3230 li = *level; // copy level data into temporary buffer
3232 while (!checkEndOfFile(file))
3234 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_INFO, -1, -1);
3236 if (real_chunk_size >= chunk_size)
3240 *level = li; // copy temporary buffer back to level data
3242 return real_chunk_size;
3245 static int LoadLevel_CONF(File *file, int chunk_size, struct LevelInfo *level)
3247 int real_chunk_size = 0;
3249 li = *level; // copy level data into temporary buffer
3251 while (!checkEndOfFile(file))
3253 int element = getMappedElement(getFile16BitBE(file));
3255 real_chunk_size += 2;
3256 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_CONF,
3258 if (real_chunk_size >= chunk_size)
3262 *level = li; // copy temporary buffer back to level data
3264 return real_chunk_size;
3267 static int LoadLevel_ELEM(File *file, int chunk_size, struct LevelInfo *level)
3269 int real_chunk_size = 0;
3271 li = *level; // copy level data into temporary buffer
3273 while (!checkEndOfFile(file))
3275 int element = getMappedElement(getFile16BitBE(file));
3277 real_chunk_size += 2;
3278 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_ELEM,
3280 if (real_chunk_size >= chunk_size)
3284 *level = li; // copy temporary buffer back to level data
3286 return real_chunk_size;
3289 static int LoadLevel_NOTE(File *file, int chunk_size, struct LevelInfo *level)
3291 int element = getMappedElement(getFile16BitBE(file));
3292 int envelope_nr = element - EL_ENVELOPE_1;
3293 int real_chunk_size = 2;
3295 xx_envelope = level->envelope[envelope_nr]; // copy into temporary buffer
3297 while (!checkEndOfFile(file))
3299 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_NOTE,
3302 if (real_chunk_size >= chunk_size)
3306 level->envelope[envelope_nr] = xx_envelope; // copy from temporary buffer
3308 return real_chunk_size;
3311 static int LoadLevel_CUSX(File *file, int chunk_size, struct LevelInfo *level)
3313 int element = getMappedElement(getFile16BitBE(file));
3314 int real_chunk_size = 2;
3315 struct ElementInfo *ei = &element_info[element];
3318 xx_ei = *ei; // copy element data into temporary buffer
3320 xx_ei.num_change_pages = -1;
3322 while (!checkEndOfFile(file))
3324 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_CUSX_base,
3326 if (xx_ei.num_change_pages != -1)
3329 if (real_chunk_size >= chunk_size)
3335 if (ei->num_change_pages == -1)
3337 Warn("LoadLevel_CUSX(): missing 'num_change_pages' for '%s'",
3340 ei->num_change_pages = 1;
3342 setElementChangePages(ei, 1);
3343 setElementChangeInfoToDefaults(ei->change);
3345 return real_chunk_size;
3348 // initialize number of change pages stored for this custom element
3349 setElementChangePages(ei, ei->num_change_pages);
3350 for (i = 0; i < ei->num_change_pages; i++)
3351 setElementChangeInfoToDefaults(&ei->change_page[i]);
3353 // start with reading properties for the first change page
3354 xx_current_change_page = 0;
3356 while (!checkEndOfFile(file))
3358 // level file might contain invalid change page number
3359 if (xx_current_change_page >= ei->num_change_pages)
3362 struct ElementChangeInfo *change = &ei->change_page[xx_current_change_page];
3364 xx_change = *change; // copy change data into temporary buffer
3366 resetEventBits(); // reset bits; change page might have changed
3368 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_CUSX_change,
3371 *change = xx_change;
3373 setEventFlagsFromEventBits(change);
3375 if (real_chunk_size >= chunk_size)
3379 level->file_has_custom_elements = TRUE;
3381 return real_chunk_size;
3384 static int LoadLevel_GRPX(File *file, int chunk_size, struct LevelInfo *level)
3386 int element = getMappedElement(getFile16BitBE(file));
3387 int real_chunk_size = 2;
3388 struct ElementInfo *ei = &element_info[element];
3389 struct ElementGroupInfo *group = ei->group;
3394 xx_ei = *ei; // copy element data into temporary buffer
3395 xx_group = *group; // copy group data into temporary buffer
3397 while (!checkEndOfFile(file))
3399 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_GRPX,
3402 if (real_chunk_size >= chunk_size)
3409 level->file_has_custom_elements = TRUE;
3411 return real_chunk_size;
3414 static int LoadLevel_EMPX(File *file, int chunk_size, struct LevelInfo *level)
3416 int element = getMappedElement(getFile16BitBE(file));
3417 int real_chunk_size = 2;
3418 struct ElementInfo *ei = &element_info[element];
3420 xx_ei = *ei; // copy element data into temporary buffer
3422 while (!checkEndOfFile(file))
3424 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_EMPX,
3427 if (real_chunk_size >= chunk_size)
3433 level->file_has_custom_elements = TRUE;
3435 return real_chunk_size;
3438 static void LoadLevelFromFileInfo_RND(struct LevelInfo *level,
3439 struct LevelFileInfo *level_file_info,
3440 boolean level_info_only)
3442 char *filename = level_file_info->filename;
3443 char cookie[MAX_LINE_LEN];
3444 char chunk_name[CHUNK_ID_LEN + 1];
3448 if (!(file = openFile(filename, MODE_READ)))
3450 level->no_valid_file = TRUE;
3451 level->no_level_file = TRUE;
3453 if (level_info_only)
3456 Warn("cannot read level '%s' -- using empty level", filename);
3458 if (!setup.editor.use_template_for_new_levels)
3461 // if level file not found, try to initialize level data from template
3462 filename = getGlobalLevelTemplateFilename();
3464 if (!(file = openFile(filename, MODE_READ)))
3467 // default: for empty levels, use level template for custom elements
3468 level->use_custom_template = TRUE;
3470 level->no_valid_file = FALSE;
3473 getFileChunkBE(file, chunk_name, NULL);
3474 if (strEqual(chunk_name, "RND1"))
3476 getFile32BitBE(file); // not used
3478 getFileChunkBE(file, chunk_name, NULL);
3479 if (!strEqual(chunk_name, "CAVE"))
3481 level->no_valid_file = TRUE;
3483 Warn("unknown format of level file '%s'", filename);
3490 else // check for pre-2.0 file format with cookie string
3492 strcpy(cookie, chunk_name);
3493 if (getStringFromFile(file, &cookie[4], MAX_LINE_LEN - 4) == NULL)
3495 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
3496 cookie[strlen(cookie) - 1] = '\0';
3498 if (!checkCookieString(cookie, LEVEL_COOKIE_TMPL))
3500 level->no_valid_file = TRUE;
3502 Warn("unknown format of level file '%s'", filename);
3509 if ((level->file_version = getFileVersionFromCookieString(cookie)) == -1)
3511 level->no_valid_file = TRUE;
3513 Warn("unsupported version of level file '%s'", filename);
3520 // pre-2.0 level files have no game version, so use file version here
3521 level->game_version = level->file_version;
3524 if (level->file_version < FILE_VERSION_1_2)
3526 // level files from versions before 1.2.0 without chunk structure
3527 LoadLevel_HEAD(file, LEVEL_CHUNK_HEAD_SIZE, level);
3528 LoadLevel_BODY(file, level->fieldx * level->fieldy, level);
3536 int (*loader)(File *, int, struct LevelInfo *);
3540 { "VERS", LEVEL_CHUNK_VERS_SIZE, LoadLevel_VERS },
3541 { "DATE", LEVEL_CHUNK_DATE_SIZE, LoadLevel_DATE },
3542 { "HEAD", LEVEL_CHUNK_HEAD_SIZE, LoadLevel_HEAD },
3543 { "NAME", LEVEL_CHUNK_NAME_SIZE, LoadLevel_NAME },
3544 { "AUTH", LEVEL_CHUNK_AUTH_SIZE, LoadLevel_AUTH },
3545 { "INFO", -1, LoadLevel_INFO },
3546 { "BODY", -1, LoadLevel_BODY },
3547 { "CONT", -1, LoadLevel_CONT },
3548 { "CNT2", LEVEL_CHUNK_CNT2_SIZE, LoadLevel_CNT2 },
3549 { "CNT3", -1, LoadLevel_CNT3 },
3550 { "CUS1", -1, LoadLevel_CUS1 },
3551 { "CUS2", -1, LoadLevel_CUS2 },
3552 { "CUS3", -1, LoadLevel_CUS3 },
3553 { "CUS4", -1, LoadLevel_CUS4 },
3554 { "GRP1", -1, LoadLevel_GRP1 },
3555 { "CONF", -1, LoadLevel_CONF },
3556 { "ELEM", -1, LoadLevel_ELEM },
3557 { "NOTE", -1, LoadLevel_NOTE },
3558 { "CUSX", -1, LoadLevel_CUSX },
3559 { "GRPX", -1, LoadLevel_GRPX },
3560 { "EMPX", -1, LoadLevel_EMPX },
3565 while (getFileChunkBE(file, chunk_name, &chunk_size))
3569 while (chunk_info[i].name != NULL &&
3570 !strEqual(chunk_name, chunk_info[i].name))
3573 if (chunk_info[i].name == NULL)
3575 Warn("unknown chunk '%s' in level file '%s'",
3576 chunk_name, filename);
3578 ReadUnusedBytesFromFile(file, chunk_size);
3580 else if (chunk_info[i].size != -1 &&
3581 chunk_info[i].size != chunk_size)
3583 Warn("wrong size (%d) of chunk '%s' in level file '%s'",
3584 chunk_size, chunk_name, filename);
3586 ReadUnusedBytesFromFile(file, chunk_size);
3590 // call function to load this level chunk
3591 int chunk_size_expected =
3592 (chunk_info[i].loader)(file, chunk_size, level);
3594 if (chunk_size_expected < 0)
3596 Warn("error reading chunk '%s' in level file '%s'",
3597 chunk_name, filename);
3602 // the size of some chunks cannot be checked before reading other
3603 // chunks first (like "HEAD" and "BODY") that contain some header
3604 // information, so check them here
3605 if (chunk_size_expected != chunk_size)
3607 Warn("wrong size (%d) of chunk '%s' in level file '%s'",
3608 chunk_size, chunk_name, filename);
3620 // ----------------------------------------------------------------------------
3621 // functions for loading EM level
3622 // ----------------------------------------------------------------------------
3624 static void CopyNativeLevel_RND_to_EM(struct LevelInfo *level)
3626 static int ball_xy[8][2] =
3637 struct LevelInfo_EM *level_em = level->native_em_level;
3638 struct CAVE *cav = level_em->cav;
3641 cav->width = MIN(level->fieldx, MAX_PLAYFIELD_WIDTH);
3642 cav->height = MIN(level->fieldy, MAX_PLAYFIELD_HEIGHT);
3644 cav->time_seconds = level->time;
3645 cav->gems_needed = level->gems_needed;
3647 cav->emerald_score = level->score[SC_EMERALD];
3648 cav->diamond_score = level->score[SC_DIAMOND];
3649 cav->alien_score = level->score[SC_ROBOT];
3650 cav->tank_score = level->score[SC_SPACESHIP];
3651 cav->bug_score = level->score[SC_BUG];
3652 cav->eater_score = level->score[SC_YAMYAM];
3653 cav->nut_score = level->score[SC_NUT];
3654 cav->dynamite_score = level->score[SC_DYNAMITE];
3655 cav->key_score = level->score[SC_KEY];
3656 cav->exit_score = level->score[SC_TIME_BONUS];
3658 cav->num_eater_arrays = level->num_yamyam_contents;
3660 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
3661 for (y = 0; y < 3; y++)
3662 for (x = 0; x < 3; x++)
3663 cav->eater_array[i][y * 3 + x] =
3664 map_element_RND_to_EM_cave(level->yamyam_content[i].e[x][y]);
3666 cav->amoeba_time = level->amoeba_speed;
3667 cav->wonderwall_time = level->time_magic_wall;
3668 cav->wheel_time = level->time_wheel;
3670 cav->android_move_time = level->android_move_time;
3671 cav->android_clone_time = level->android_clone_time;
3672 cav->ball_random = level->ball_random;
3673 cav->ball_active = level->ball_active_initial;
3674 cav->ball_time = level->ball_time;
3675 cav->num_ball_arrays = level->num_ball_contents;
3677 cav->lenses_score = level->lenses_score;
3678 cav->magnify_score = level->magnify_score;
3679 cav->slurp_score = level->slurp_score;
3681 cav->lenses_time = level->lenses_time;
3682 cav->magnify_time = level->magnify_time;
3684 cav->wind_direction =
3685 map_direction_RND_to_EM(level->wind_direction_initial);
3687 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
3688 for (j = 0; j < 8; j++)
3689 cav->ball_array[i][j] =
3690 map_element_RND_to_EM_cave(level->ball_content[i].
3691 e[ball_xy[j][0]][ball_xy[j][1]]);
3693 map_android_clone_elements_RND_to_EM(level);
3695 // first fill the complete playfield with the empty space element
3696 for (y = 0; y < EM_MAX_CAVE_HEIGHT; y++)
3697 for (x = 0; x < EM_MAX_CAVE_WIDTH; x++)
3698 cav->cave[x][y] = Cblank;
3700 // then copy the real level contents from level file into the playfield
3701 for (y = 0; y < cav->height; y++) for (x = 0; x < cav->width; x++)
3703 int new_element = map_element_RND_to_EM_cave(level->field[x][y]);
3705 if (level->field[x][y] == EL_AMOEBA_DEAD)
3706 new_element = map_element_RND_to_EM_cave(EL_AMOEBA_WET);
3708 cav->cave[x][y] = new_element;
3711 for (i = 0; i < MAX_PLAYERS; i++)
3713 cav->player_x[i] = -1;
3714 cav->player_y[i] = -1;
3717 // initialize player positions and delete players from the playfield
3718 for (y = 0; y < cav->height; y++) for (x = 0; x < cav->width; x++)
3720 if (IS_PLAYER_ELEMENT(level->field[x][y]))
3722 int player_nr = GET_PLAYER_NR(level->field[x][y]);
3724 cav->player_x[player_nr] = x;
3725 cav->player_y[player_nr] = y;
3727 cav->cave[x][y] = map_element_RND_to_EM_cave(EL_EMPTY);
3732 static void CopyNativeLevel_EM_to_RND(struct LevelInfo *level)
3734 static int ball_xy[8][2] =
3745 struct LevelInfo_EM *level_em = level->native_em_level;
3746 struct CAVE *cav = level_em->cav;
3749 level->fieldx = MIN(cav->width, MAX_LEV_FIELDX);
3750 level->fieldy = MIN(cav->height, MAX_LEV_FIELDY);
3752 level->time = cav->time_seconds;
3753 level->gems_needed = cav->gems_needed;
3755 sprintf(level->name, "Level %d", level->file_info.nr);
3757 level->score[SC_EMERALD] = cav->emerald_score;
3758 level->score[SC_DIAMOND] = cav->diamond_score;
3759 level->score[SC_ROBOT] = cav->alien_score;
3760 level->score[SC_SPACESHIP] = cav->tank_score;
3761 level->score[SC_BUG] = cav->bug_score;
3762 level->score[SC_YAMYAM] = cav->eater_score;
3763 level->score[SC_NUT] = cav->nut_score;
3764 level->score[SC_DYNAMITE] = cav->dynamite_score;
3765 level->score[SC_KEY] = cav->key_score;
3766 level->score[SC_TIME_BONUS] = cav->exit_score;
3768 level->num_yamyam_contents = cav->num_eater_arrays;
3770 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
3771 for (y = 0; y < 3; y++)
3772 for (x = 0; x < 3; x++)
3773 level->yamyam_content[i].e[x][y] =
3774 map_element_EM_to_RND_cave(cav->eater_array[i][y * 3 + x]);
3776 level->amoeba_speed = cav->amoeba_time;
3777 level->time_magic_wall = cav->wonderwall_time;
3778 level->time_wheel = cav->wheel_time;
3780 level->android_move_time = cav->android_move_time;
3781 level->android_clone_time = cav->android_clone_time;
3782 level->ball_random = cav->ball_random;
3783 level->ball_active_initial = cav->ball_active;
3784 level->ball_time = cav->ball_time;
3785 level->num_ball_contents = cav->num_ball_arrays;
3787 level->lenses_score = cav->lenses_score;
3788 level->magnify_score = cav->magnify_score;
3789 level->slurp_score = cav->slurp_score;
3791 level->lenses_time = cav->lenses_time;
3792 level->magnify_time = cav->magnify_time;
3794 level->wind_direction_initial =
3795 map_direction_EM_to_RND(cav->wind_direction);
3797 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
3798 for (j = 0; j < 8; j++)
3799 level->ball_content[i].e[ball_xy[j][0]][ball_xy[j][1]] =
3800 map_element_EM_to_RND_cave(cav->ball_array[i][j]);
3802 map_android_clone_elements_EM_to_RND(level);
3804 // convert the playfield (some elements need special treatment)
3805 for (y = 0; y < level->fieldy; y++) for (x = 0; x < level->fieldx; x++)
3807 int new_element = map_element_EM_to_RND_cave(cav->cave[x][y]);
3809 if (new_element == EL_AMOEBA_WET && level->amoeba_speed == 0)
3810 new_element = EL_AMOEBA_DEAD;
3812 level->field[x][y] = new_element;
3815 for (i = 0; i < MAX_PLAYERS; i++)
3817 // in case of all players set to the same field, use the first player
3818 int nr = MAX_PLAYERS - i - 1;
3819 int jx = cav->player_x[nr];
3820 int jy = cav->player_y[nr];
3822 if (jx != -1 && jy != -1)
3823 level->field[jx][jy] = EL_PLAYER_1 + nr;
3826 // time score is counted for each 10 seconds left in Emerald Mine levels
3827 level->time_score_base = 10;
3831 // ----------------------------------------------------------------------------
3832 // functions for loading SP level
3833 // ----------------------------------------------------------------------------
3835 static void CopyNativeLevel_RND_to_SP(struct LevelInfo *level)
3837 struct LevelInfo_SP *level_sp = level->native_sp_level;
3838 LevelInfoType *header = &level_sp->header;
3841 level_sp->width = level->fieldx;
3842 level_sp->height = level->fieldy;
3844 for (x = 0; x < level->fieldx; x++)
3845 for (y = 0; y < level->fieldy; y++)
3846 level_sp->playfield[x][y] = map_element_RND_to_SP(level->field[x][y]);
3848 header->InitialGravity = (level->initial_player_gravity[0] ? 1 : 0);
3850 for (i = 0; i < SP_LEVEL_NAME_LEN; i++)
3851 header->LevelTitle[i] = level->name[i];
3852 // !!! NO STRING TERMINATION IN SUPAPLEX VB CODE YET -- FIX THIS !!!
3854 header->InfotronsNeeded = level->gems_needed;
3856 header->SpecialPortCount = 0;
3858 for (x = 0; x < level->fieldx; x++) for (y = 0; y < level->fieldy; y++)
3860 boolean gravity_port_found = FALSE;
3861 boolean gravity_port_valid = FALSE;
3862 int gravity_port_flag;
3863 int gravity_port_base_element;
3864 int element = level->field[x][y];
3866 if (element >= EL_SP_GRAVITY_ON_PORT_RIGHT &&
3867 element <= EL_SP_GRAVITY_ON_PORT_UP)
3869 gravity_port_found = TRUE;
3870 gravity_port_valid = TRUE;
3871 gravity_port_flag = 1;
3872 gravity_port_base_element = EL_SP_GRAVITY_ON_PORT_RIGHT;
3874 else if (element >= EL_SP_GRAVITY_OFF_PORT_RIGHT &&
3875 element <= EL_SP_GRAVITY_OFF_PORT_UP)
3877 gravity_port_found = TRUE;
3878 gravity_port_valid = TRUE;
3879 gravity_port_flag = 0;
3880 gravity_port_base_element = EL_SP_GRAVITY_OFF_PORT_RIGHT;
3882 else if (element >= EL_SP_GRAVITY_PORT_RIGHT &&
3883 element <= EL_SP_GRAVITY_PORT_UP)
3885 // change R'n'D style gravity inverting special port to normal port
3886 // (there are no gravity inverting ports in native Supaplex engine)
3888 gravity_port_found = TRUE;
3889 gravity_port_valid = FALSE;
3890 gravity_port_base_element = EL_SP_GRAVITY_PORT_RIGHT;
3893 if (gravity_port_found)
3895 if (gravity_port_valid &&
3896 header->SpecialPortCount < SP_MAX_SPECIAL_PORTS)
3898 SpecialPortType *port = &header->SpecialPort[header->SpecialPortCount];
3900 port->PortLocation = (y * level->fieldx + x) * 2;
3901 port->Gravity = gravity_port_flag;
3903 element += EL_SP_GRAVITY_PORT_RIGHT - gravity_port_base_element;
3905 header->SpecialPortCount++;
3909 // change special gravity port to normal port
3911 element += EL_SP_PORT_RIGHT - gravity_port_base_element;
3914 level_sp->playfield[x][y] = element - EL_SP_START;
3919 static void CopyNativeLevel_SP_to_RND(struct LevelInfo *level)
3921 struct LevelInfo_SP *level_sp = level->native_sp_level;
3922 LevelInfoType *header = &level_sp->header;
3923 boolean num_invalid_elements = 0;
3926 level->fieldx = level_sp->width;
3927 level->fieldy = level_sp->height;
3929 for (x = 0; x < level->fieldx; x++)
3931 for (y = 0; y < level->fieldy; y++)
3933 int element_old = level_sp->playfield[x][y];
3934 int element_new = getMappedElement(map_element_SP_to_RND(element_old));
3936 if (element_new == EL_UNKNOWN)
3938 num_invalid_elements++;
3940 Debug("level:native:SP", "invalid element %d at position %d, %d",
3944 level->field[x][y] = element_new;
3948 if (num_invalid_elements > 0)
3949 Warn("found %d invalid elements%s", num_invalid_elements,
3950 (!options.debug ? " (use '--debug' for more details)" : ""));
3952 for (i = 0; i < MAX_PLAYERS; i++)
3953 level->initial_player_gravity[i] =
3954 (header->InitialGravity == 1 ? TRUE : FALSE);
3956 // skip leading spaces
3957 for (i = 0; i < SP_LEVEL_NAME_LEN; i++)
3958 if (header->LevelTitle[i] != ' ')
3962 for (j = 0; i < SP_LEVEL_NAME_LEN; i++, j++)
3963 level->name[j] = header->LevelTitle[i];
3964 level->name[j] = '\0';
3966 // cut trailing spaces
3968 if (level->name[j - 1] == ' ' && level->name[j] == '\0')
3969 level->name[j - 1] = '\0';
3971 level->gems_needed = header->InfotronsNeeded;
3973 for (i = 0; i < header->SpecialPortCount; i++)
3975 SpecialPortType *port = &header->SpecialPort[i];
3976 int port_location = port->PortLocation;
3977 int gravity = port->Gravity;
3978 int port_x, port_y, port_element;
3980 port_x = (port_location / 2) % level->fieldx;
3981 port_y = (port_location / 2) / level->fieldx;
3983 if (port_x < 0 || port_x >= level->fieldx ||
3984 port_y < 0 || port_y >= level->fieldy)
3986 Warn("special port position (%d, %d) out of bounds", port_x, port_y);
3991 port_element = level->field[port_x][port_y];
3993 if (port_element < EL_SP_GRAVITY_PORT_RIGHT ||
3994 port_element > EL_SP_GRAVITY_PORT_UP)
3996 Warn("no special port at position (%d, %d)", port_x, port_y);
4001 // change previous (wrong) gravity inverting special port to either
4002 // gravity enabling special port or gravity disabling special port
4003 level->field[port_x][port_y] +=
4004 (gravity == 1 ? EL_SP_GRAVITY_ON_PORT_RIGHT :
4005 EL_SP_GRAVITY_OFF_PORT_RIGHT) - EL_SP_GRAVITY_PORT_RIGHT;
4008 // change special gravity ports without database entries to normal ports
4009 for (x = 0; x < level->fieldx; x++)
4010 for (y = 0; y < level->fieldy; y++)
4011 if (level->field[x][y] >= EL_SP_GRAVITY_PORT_RIGHT &&
4012 level->field[x][y] <= EL_SP_GRAVITY_PORT_UP)
4013 level->field[x][y] += EL_SP_PORT_RIGHT - EL_SP_GRAVITY_PORT_RIGHT;
4015 level->time = 0; // no time limit
4016 level->amoeba_speed = 0;
4017 level->time_magic_wall = 0;
4018 level->time_wheel = 0;
4019 level->amoeba_content = EL_EMPTY;
4021 // original Supaplex does not use score values -- rate by playing time
4022 for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
4023 level->score[i] = 0;
4025 level->rate_time_over_score = TRUE;
4027 // there are no yamyams in supaplex levels
4028 for (i = 0; i < level->num_yamyam_contents; i++)
4029 for (x = 0; x < 3; x++)
4030 for (y = 0; y < 3; y++)
4031 level->yamyam_content[i].e[x][y] = EL_EMPTY;
4034 static void CopyNativeTape_RND_to_SP(struct LevelInfo *level)
4036 struct LevelInfo_SP *level_sp = level->native_sp_level;
4037 struct DemoInfo_SP *demo = &level_sp->demo;
4040 // always start with reliable default values
4041 demo->is_available = FALSE;
4044 if (TAPE_IS_EMPTY(tape))
4047 demo->level_nr = tape.level_nr; // (currently not used)
4049 level_sp->header.DemoRandomSeed = tape.random_seed;
4053 for (i = 0; i < tape.length; i++)
4055 int demo_action = map_key_RND_to_SP(tape.pos[i].action[0]);
4056 int demo_repeat = tape.pos[i].delay;
4057 int demo_entries = (demo_repeat + 15) / 16;
4059 if (demo->length + demo_entries >= SP_MAX_TAPE_LEN)
4061 Warn("tape truncated: size exceeds maximum SP demo size %d",
4067 for (j = 0; j < demo_repeat / 16; j++)
4068 demo->data[demo->length++] = 0xf0 | demo_action;
4070 if (demo_repeat % 16)
4071 demo->data[demo->length++] = ((demo_repeat % 16 - 1) << 4) | demo_action;
4074 demo->is_available = TRUE;
4077 static void setTapeInfoToDefaults(void);
4079 static void CopyNativeTape_SP_to_RND(struct LevelInfo *level)
4081 struct LevelInfo_SP *level_sp = level->native_sp_level;
4082 struct DemoInfo_SP *demo = &level_sp->demo;
4083 char *filename = level->file_info.filename;
4086 // always start with reliable default values
4087 setTapeInfoToDefaults();
4089 if (!demo->is_available)
4092 tape.level_nr = demo->level_nr; // (currently not used)
4093 tape.random_seed = level_sp->header.DemoRandomSeed;
4095 TapeSetDateFromEpochSeconds(getFileTimestampEpochSeconds(filename));
4098 tape.pos[tape.counter].delay = 0;
4100 for (i = 0; i < demo->length; i++)
4102 int demo_action = demo->data[i] & 0x0f;
4103 int demo_repeat = (demo->data[i] & 0xf0) >> 4;
4104 int tape_action = map_key_SP_to_RND(demo_action);
4105 int tape_repeat = demo_repeat + 1;
4106 byte action[MAX_TAPE_ACTIONS] = { tape_action };
4107 boolean success = 0;
4110 for (j = 0; j < tape_repeat; j++)
4111 success = TapeAddAction(action);
4115 Warn("SP demo truncated: size exceeds maximum tape size %d",
4122 TapeHaltRecording();
4126 // ----------------------------------------------------------------------------
4127 // functions for loading MM level
4128 // ----------------------------------------------------------------------------
4130 static void CopyNativeLevel_RND_to_MM(struct LevelInfo *level)
4132 struct LevelInfo_MM *level_mm = level->native_mm_level;
4135 level_mm->fieldx = MIN(level->fieldx, MM_MAX_PLAYFIELD_WIDTH);
4136 level_mm->fieldy = MIN(level->fieldy, MM_MAX_PLAYFIELD_HEIGHT);
4138 level_mm->time = level->time;
4139 level_mm->kettles_needed = level->gems_needed;
4140 level_mm->auto_count_kettles = level->auto_count_gems;
4142 level_mm->laser_red = level->mm_laser_red;
4143 level_mm->laser_green = level->mm_laser_green;
4144 level_mm->laser_blue = level->mm_laser_blue;
4146 strcpy(level_mm->name, level->name);
4147 strcpy(level_mm->author, level->author);
4149 level_mm->score[SC_EMERALD] = level->score[SC_EMERALD];
4150 level_mm->score[SC_PACMAN] = level->score[SC_PACMAN];
4151 level_mm->score[SC_KEY] = level->score[SC_KEY];
4152 level_mm->score[SC_TIME_BONUS] = level->score[SC_TIME_BONUS];
4153 level_mm->score[SC_ELEM_BONUS] = level->score[SC_ELEM_BONUS];
4155 level_mm->amoeba_speed = level->amoeba_speed;
4156 level_mm->time_fuse = level->mm_time_fuse;
4157 level_mm->time_bomb = level->mm_time_bomb;
4158 level_mm->time_ball = level->mm_time_ball;
4159 level_mm->time_block = level->mm_time_block;
4161 for (x = 0; x < level->fieldx; x++)
4162 for (y = 0; y < level->fieldy; y++)
4164 level_mm->field[x][y] = map_element_RND_to_MM(level->field[x][y]);
4167 static void CopyNativeLevel_MM_to_RND(struct LevelInfo *level)
4169 struct LevelInfo_MM *level_mm = level->native_mm_level;
4172 level->fieldx = MIN(level_mm->fieldx, MAX_LEV_FIELDX);
4173 level->fieldy = MIN(level_mm->fieldy, MAX_LEV_FIELDY);
4175 level->time = level_mm->time;
4176 level->gems_needed = level_mm->kettles_needed;
4177 level->auto_count_gems = level_mm->auto_count_kettles;
4179 level->mm_laser_red = level_mm->laser_red;
4180 level->mm_laser_green = level_mm->laser_green;
4181 level->mm_laser_blue = level_mm->laser_blue;
4183 strcpy(level->name, level_mm->name);
4185 // only overwrite author from 'levelinfo.conf' if author defined in level
4186 if (!strEqual(level_mm->author, ANONYMOUS_NAME))
4187 strcpy(level->author, level_mm->author);
4189 level->score[SC_EMERALD] = level_mm->score[SC_EMERALD];
4190 level->score[SC_PACMAN] = level_mm->score[SC_PACMAN];
4191 level->score[SC_KEY] = level_mm->score[SC_KEY];
4192 level->score[SC_TIME_BONUS] = level_mm->score[SC_TIME_BONUS];
4193 level->score[SC_ELEM_BONUS] = level_mm->score[SC_ELEM_BONUS];
4195 level->amoeba_speed = level_mm->amoeba_speed;
4196 level->mm_time_fuse = level_mm->time_fuse;
4197 level->mm_time_bomb = level_mm->time_bomb;
4198 level->mm_time_ball = level_mm->time_ball;
4199 level->mm_time_block = level_mm->time_block;
4201 for (x = 0; x < level->fieldx; x++)
4202 for (y = 0; y < level->fieldy; y++)
4203 level->field[x][y] = map_element_MM_to_RND(level_mm->field[x][y]);
4207 // ----------------------------------------------------------------------------
4208 // functions for loading DC level
4209 // ----------------------------------------------------------------------------
4211 #define DC_LEVEL_HEADER_SIZE 344
4213 static unsigned short getDecodedWord_DC(unsigned short data_encoded,
4216 static int last_data_encoded;
4220 int diff_hi, diff_lo;
4221 int data_hi, data_lo;
4222 unsigned short data_decoded;
4226 last_data_encoded = 0;
4233 diff = data_encoded - last_data_encoded;
4234 diff_hi = diff & ~0xff;
4235 diff_lo = diff & 0xff;
4239 data_hi = diff_hi - (offset1 << 8) + (offset2 & 0xff00);
4240 data_lo = (diff_lo + (data_hi >> 16)) & 0x00ff;
4241 data_hi = data_hi & 0xff00;
4243 data_decoded = data_hi | data_lo;
4245 last_data_encoded = data_encoded;
4247 offset1 = (offset1 + 1) % 31;
4248 offset2 = offset2 & 0xff;
4250 return data_decoded;
4253 static int getMappedElement_DC(int element)
4261 // 0x0117 - 0x036e: (?)
4264 // 0x042d - 0x0684: (?)
4280 element = EL_CRYSTAL;
4283 case 0x0e77: // quicksand (boulder)
4284 element = EL_QUICKSAND_FAST_FULL;
4287 case 0x0e99: // slow quicksand (boulder)
4288 element = EL_QUICKSAND_FULL;
4292 element = EL_EM_EXIT_OPEN;
4296 element = EL_EM_EXIT_CLOSED;
4300 element = EL_EM_STEEL_EXIT_OPEN;
4304 element = EL_EM_STEEL_EXIT_CLOSED;
4307 case 0x0f4f: // dynamite (lit 1)
4308 element = EL_EM_DYNAMITE_ACTIVE;
4311 case 0x0f57: // dynamite (lit 2)
4312 element = EL_EM_DYNAMITE_ACTIVE;
4315 case 0x0f5f: // dynamite (lit 3)
4316 element = EL_EM_DYNAMITE_ACTIVE;
4319 case 0x0f67: // dynamite (lit 4)
4320 element = EL_EM_DYNAMITE_ACTIVE;
4327 element = EL_AMOEBA_WET;
4331 element = EL_AMOEBA_DROP;
4335 element = EL_DC_MAGIC_WALL;
4339 element = EL_SPACESHIP_UP;
4343 element = EL_SPACESHIP_DOWN;
4347 element = EL_SPACESHIP_LEFT;
4351 element = EL_SPACESHIP_RIGHT;
4355 element = EL_BUG_UP;
4359 element = EL_BUG_DOWN;
4363 element = EL_BUG_LEFT;
4367 element = EL_BUG_RIGHT;
4371 element = EL_MOLE_UP;
4375 element = EL_MOLE_DOWN;
4379 element = EL_MOLE_LEFT;
4383 element = EL_MOLE_RIGHT;
4391 element = EL_YAMYAM_UP;
4395 element = EL_SWITCHGATE_OPEN;
4399 element = EL_SWITCHGATE_CLOSED;
4403 element = EL_DC_SWITCHGATE_SWITCH_UP;
4407 element = EL_TIMEGATE_CLOSED;
4410 case 0x144c: // conveyor belt switch (green)
4411 element = EL_CONVEYOR_BELT_3_SWITCH_MIDDLE;
4414 case 0x144f: // conveyor belt switch (red)
4415 element = EL_CONVEYOR_BELT_1_SWITCH_MIDDLE;
4418 case 0x1452: // conveyor belt switch (blue)
4419 element = EL_CONVEYOR_BELT_4_SWITCH_MIDDLE;
4423 element = EL_CONVEYOR_BELT_3_MIDDLE;
4427 element = EL_CONVEYOR_BELT_3_LEFT;
4431 element = EL_CONVEYOR_BELT_3_RIGHT;
4435 element = EL_CONVEYOR_BELT_1_MIDDLE;
4439 element = EL_CONVEYOR_BELT_1_LEFT;
4443 element = EL_CONVEYOR_BELT_1_RIGHT;
4447 element = EL_CONVEYOR_BELT_4_MIDDLE;
4451 element = EL_CONVEYOR_BELT_4_LEFT;
4455 element = EL_CONVEYOR_BELT_4_RIGHT;
4459 element = EL_EXPANDABLE_WALL_HORIZONTAL;
4463 element = EL_EXPANDABLE_WALL_VERTICAL;
4467 element = EL_EXPANDABLE_WALL_ANY;
4470 case 0x14ce: // growing steel wall (left/right)
4471 element = EL_EXPANDABLE_STEELWALL_HORIZONTAL;
4474 case 0x14df: // growing steel wall (up/down)
4475 element = EL_EXPANDABLE_STEELWALL_VERTICAL;
4478 case 0x14e8: // growing steel wall (up/down/left/right)
4479 element = EL_EXPANDABLE_STEELWALL_ANY;
4483 element = EL_SHIELD_DEADLY;
4487 element = EL_EXTRA_TIME;
4495 element = EL_EMPTY_SPACE;
4498 case 0x1578: // quicksand (empty)
4499 element = EL_QUICKSAND_FAST_EMPTY;
4502 case 0x1579: // slow quicksand (empty)
4503 element = EL_QUICKSAND_EMPTY;
4513 element = EL_EM_DYNAMITE;
4516 case 0x15a1: // key (red)
4517 element = EL_EM_KEY_1;
4520 case 0x15a2: // key (yellow)
4521 element = EL_EM_KEY_2;
4524 case 0x15a3: // key (blue)
4525 element = EL_EM_KEY_4;
4528 case 0x15a4: // key (green)
4529 element = EL_EM_KEY_3;
4532 case 0x15a5: // key (white)
4533 element = EL_DC_KEY_WHITE;
4537 element = EL_WALL_SLIPPERY;
4544 case 0x15a8: // wall (not round)
4548 case 0x15a9: // (blue)
4549 element = EL_CHAR_A;
4552 case 0x15aa: // (blue)
4553 element = EL_CHAR_B;
4556 case 0x15ab: // (blue)
4557 element = EL_CHAR_C;
4560 case 0x15ac: // (blue)
4561 element = EL_CHAR_D;
4564 case 0x15ad: // (blue)
4565 element = EL_CHAR_E;
4568 case 0x15ae: // (blue)
4569 element = EL_CHAR_F;
4572 case 0x15af: // (blue)
4573 element = EL_CHAR_G;
4576 case 0x15b0: // (blue)
4577 element = EL_CHAR_H;
4580 case 0x15b1: // (blue)
4581 element = EL_CHAR_I;
4584 case 0x15b2: // (blue)
4585 element = EL_CHAR_J;
4588 case 0x15b3: // (blue)
4589 element = EL_CHAR_K;
4592 case 0x15b4: // (blue)
4593 element = EL_CHAR_L;
4596 case 0x15b5: // (blue)
4597 element = EL_CHAR_M;
4600 case 0x15b6: // (blue)
4601 element = EL_CHAR_N;
4604 case 0x15b7: // (blue)
4605 element = EL_CHAR_O;
4608 case 0x15b8: // (blue)
4609 element = EL_CHAR_P;
4612 case 0x15b9: // (blue)
4613 element = EL_CHAR_Q;
4616 case 0x15ba: // (blue)
4617 element = EL_CHAR_R;
4620 case 0x15bb: // (blue)
4621 element = EL_CHAR_S;
4624 case 0x15bc: // (blue)
4625 element = EL_CHAR_T;
4628 case 0x15bd: // (blue)
4629 element = EL_CHAR_U;
4632 case 0x15be: // (blue)
4633 element = EL_CHAR_V;
4636 case 0x15bf: // (blue)
4637 element = EL_CHAR_W;
4640 case 0x15c0: // (blue)
4641 element = EL_CHAR_X;
4644 case 0x15c1: // (blue)
4645 element = EL_CHAR_Y;
4648 case 0x15c2: // (blue)
4649 element = EL_CHAR_Z;
4652 case 0x15c3: // (blue)
4653 element = EL_CHAR_AUMLAUT;
4656 case 0x15c4: // (blue)
4657 element = EL_CHAR_OUMLAUT;
4660 case 0x15c5: // (blue)
4661 element = EL_CHAR_UUMLAUT;
4664 case 0x15c6: // (blue)
4665 element = EL_CHAR_0;
4668 case 0x15c7: // (blue)
4669 element = EL_CHAR_1;
4672 case 0x15c8: // (blue)
4673 element = EL_CHAR_2;
4676 case 0x15c9: // (blue)
4677 element = EL_CHAR_3;
4680 case 0x15ca: // (blue)
4681 element = EL_CHAR_4;
4684 case 0x15cb: // (blue)
4685 element = EL_CHAR_5;
4688 case 0x15cc: // (blue)
4689 element = EL_CHAR_6;
4692 case 0x15cd: // (blue)
4693 element = EL_CHAR_7;
4696 case 0x15ce: // (blue)
4697 element = EL_CHAR_8;
4700 case 0x15cf: // (blue)
4701 element = EL_CHAR_9;
4704 case 0x15d0: // (blue)
4705 element = EL_CHAR_PERIOD;
4708 case 0x15d1: // (blue)
4709 element = EL_CHAR_EXCLAM;
4712 case 0x15d2: // (blue)
4713 element = EL_CHAR_COLON;
4716 case 0x15d3: // (blue)
4717 element = EL_CHAR_LESS;
4720 case 0x15d4: // (blue)
4721 element = EL_CHAR_GREATER;
4724 case 0x15d5: // (blue)
4725 element = EL_CHAR_QUESTION;
4728 case 0x15d6: // (blue)
4729 element = EL_CHAR_COPYRIGHT;
4732 case 0x15d7: // (blue)
4733 element = EL_CHAR_UP;
4736 case 0x15d8: // (blue)
4737 element = EL_CHAR_DOWN;
4740 case 0x15d9: // (blue)
4741 element = EL_CHAR_BUTTON;
4744 case 0x15da: // (blue)
4745 element = EL_CHAR_PLUS;
4748 case 0x15db: // (blue)
4749 element = EL_CHAR_MINUS;
4752 case 0x15dc: // (blue)
4753 element = EL_CHAR_APOSTROPHE;
4756 case 0x15dd: // (blue)
4757 element = EL_CHAR_PARENLEFT;
4760 case 0x15de: // (blue)
4761 element = EL_CHAR_PARENRIGHT;
4764 case 0x15df: // (green)
4765 element = EL_CHAR_A;
4768 case 0x15e0: // (green)
4769 element = EL_CHAR_B;
4772 case 0x15e1: // (green)
4773 element = EL_CHAR_C;
4776 case 0x15e2: // (green)
4777 element = EL_CHAR_D;
4780 case 0x15e3: // (green)
4781 element = EL_CHAR_E;
4784 case 0x15e4: // (green)
4785 element = EL_CHAR_F;
4788 case 0x15e5: // (green)
4789 element = EL_CHAR_G;
4792 case 0x15e6: // (green)
4793 element = EL_CHAR_H;
4796 case 0x15e7: // (green)
4797 element = EL_CHAR_I;
4800 case 0x15e8: // (green)
4801 element = EL_CHAR_J;
4804 case 0x15e9: // (green)
4805 element = EL_CHAR_K;
4808 case 0x15ea: // (green)
4809 element = EL_CHAR_L;
4812 case 0x15eb: // (green)
4813 element = EL_CHAR_M;
4816 case 0x15ec: // (green)
4817 element = EL_CHAR_N;
4820 case 0x15ed: // (green)
4821 element = EL_CHAR_O;
4824 case 0x15ee: // (green)
4825 element = EL_CHAR_P;
4828 case 0x15ef: // (green)
4829 element = EL_CHAR_Q;
4832 case 0x15f0: // (green)
4833 element = EL_CHAR_R;
4836 case 0x15f1: // (green)
4837 element = EL_CHAR_S;
4840 case 0x15f2: // (green)
4841 element = EL_CHAR_T;
4844 case 0x15f3: // (green)
4845 element = EL_CHAR_U;
4848 case 0x15f4: // (green)
4849 element = EL_CHAR_V;
4852 case 0x15f5: // (green)
4853 element = EL_CHAR_W;
4856 case 0x15f6: // (green)
4857 element = EL_CHAR_X;
4860 case 0x15f7: // (green)
4861 element = EL_CHAR_Y;
4864 case 0x15f8: // (green)
4865 element = EL_CHAR_Z;
4868 case 0x15f9: // (green)
4869 element = EL_CHAR_AUMLAUT;
4872 case 0x15fa: // (green)
4873 element = EL_CHAR_OUMLAUT;
4876 case 0x15fb: // (green)
4877 element = EL_CHAR_UUMLAUT;
4880 case 0x15fc: // (green)
4881 element = EL_CHAR_0;
4884 case 0x15fd: // (green)
4885 element = EL_CHAR_1;
4888 case 0x15fe: // (green)
4889 element = EL_CHAR_2;
4892 case 0x15ff: // (green)
4893 element = EL_CHAR_3;
4896 case 0x1600: // (green)
4897 element = EL_CHAR_4;
4900 case 0x1601: // (green)
4901 element = EL_CHAR_5;
4904 case 0x1602: // (green)
4905 element = EL_CHAR_6;
4908 case 0x1603: // (green)
4909 element = EL_CHAR_7;
4912 case 0x1604: // (green)
4913 element = EL_CHAR_8;
4916 case 0x1605: // (green)
4917 element = EL_CHAR_9;
4920 case 0x1606: // (green)
4921 element = EL_CHAR_PERIOD;
4924 case 0x1607: // (green)
4925 element = EL_CHAR_EXCLAM;
4928 case 0x1608: // (green)
4929 element = EL_CHAR_COLON;
4932 case 0x1609: // (green)
4933 element = EL_CHAR_LESS;
4936 case 0x160a: // (green)
4937 element = EL_CHAR_GREATER;
4940 case 0x160b: // (green)
4941 element = EL_CHAR_QUESTION;
4944 case 0x160c: // (green)
4945 element = EL_CHAR_COPYRIGHT;
4948 case 0x160d: // (green)
4949 element = EL_CHAR_UP;
4952 case 0x160e: // (green)
4953 element = EL_CHAR_DOWN;
4956 case 0x160f: // (green)
4957 element = EL_CHAR_BUTTON;
4960 case 0x1610: // (green)
4961 element = EL_CHAR_PLUS;
4964 case 0x1611: // (green)
4965 element = EL_CHAR_MINUS;
4968 case 0x1612: // (green)
4969 element = EL_CHAR_APOSTROPHE;
4972 case 0x1613: // (green)
4973 element = EL_CHAR_PARENLEFT;
4976 case 0x1614: // (green)
4977 element = EL_CHAR_PARENRIGHT;
4980 case 0x1615: // (blue steel)
4981 element = EL_STEEL_CHAR_A;
4984 case 0x1616: // (blue steel)
4985 element = EL_STEEL_CHAR_B;
4988 case 0x1617: // (blue steel)
4989 element = EL_STEEL_CHAR_C;
4992 case 0x1618: // (blue steel)
4993 element = EL_STEEL_CHAR_D;
4996 case 0x1619: // (blue steel)
4997 element = EL_STEEL_CHAR_E;
5000 case 0x161a: // (blue steel)
5001 element = EL_STEEL_CHAR_F;
5004 case 0x161b: // (blue steel)
5005 element = EL_STEEL_CHAR_G;
5008 case 0x161c: // (blue steel)
5009 element = EL_STEEL_CHAR_H;
5012 case 0x161d: // (blue steel)
5013 element = EL_STEEL_CHAR_I;
5016 case 0x161e: // (blue steel)
5017 element = EL_STEEL_CHAR_J;
5020 case 0x161f: // (blue steel)
5021 element = EL_STEEL_CHAR_K;
5024 case 0x1620: // (blue steel)
5025 element = EL_STEEL_CHAR_L;
5028 case 0x1621: // (blue steel)
5029 element = EL_STEEL_CHAR_M;
5032 case 0x1622: // (blue steel)
5033 element = EL_STEEL_CHAR_N;
5036 case 0x1623: // (blue steel)
5037 element = EL_STEEL_CHAR_O;
5040 case 0x1624: // (blue steel)
5041 element = EL_STEEL_CHAR_P;
5044 case 0x1625: // (blue steel)
5045 element = EL_STEEL_CHAR_Q;
5048 case 0x1626: // (blue steel)
5049 element = EL_STEEL_CHAR_R;
5052 case 0x1627: // (blue steel)
5053 element = EL_STEEL_CHAR_S;
5056 case 0x1628: // (blue steel)
5057 element = EL_STEEL_CHAR_T;
5060 case 0x1629: // (blue steel)
5061 element = EL_STEEL_CHAR_U;
5064 case 0x162a: // (blue steel)
5065 element = EL_STEEL_CHAR_V;
5068 case 0x162b: // (blue steel)
5069 element = EL_STEEL_CHAR_W;
5072 case 0x162c: // (blue steel)
5073 element = EL_STEEL_CHAR_X;
5076 case 0x162d: // (blue steel)
5077 element = EL_STEEL_CHAR_Y;
5080 case 0x162e: // (blue steel)
5081 element = EL_STEEL_CHAR_Z;
5084 case 0x162f: // (blue steel)
5085 element = EL_STEEL_CHAR_AUMLAUT;
5088 case 0x1630: // (blue steel)
5089 element = EL_STEEL_CHAR_OUMLAUT;
5092 case 0x1631: // (blue steel)
5093 element = EL_STEEL_CHAR_UUMLAUT;
5096 case 0x1632: // (blue steel)
5097 element = EL_STEEL_CHAR_0;
5100 case 0x1633: // (blue steel)
5101 element = EL_STEEL_CHAR_1;
5104 case 0x1634: // (blue steel)
5105 element = EL_STEEL_CHAR_2;
5108 case 0x1635: // (blue steel)
5109 element = EL_STEEL_CHAR_3;
5112 case 0x1636: // (blue steel)
5113 element = EL_STEEL_CHAR_4;
5116 case 0x1637: // (blue steel)
5117 element = EL_STEEL_CHAR_5;
5120 case 0x1638: // (blue steel)
5121 element = EL_STEEL_CHAR_6;
5124 case 0x1639: // (blue steel)
5125 element = EL_STEEL_CHAR_7;
5128 case 0x163a: // (blue steel)
5129 element = EL_STEEL_CHAR_8;
5132 case 0x163b: // (blue steel)
5133 element = EL_STEEL_CHAR_9;
5136 case 0x163c: // (blue steel)
5137 element = EL_STEEL_CHAR_PERIOD;
5140 case 0x163d: // (blue steel)
5141 element = EL_STEEL_CHAR_EXCLAM;
5144 case 0x163e: // (blue steel)
5145 element = EL_STEEL_CHAR_COLON;
5148 case 0x163f: // (blue steel)
5149 element = EL_STEEL_CHAR_LESS;
5152 case 0x1640: // (blue steel)
5153 element = EL_STEEL_CHAR_GREATER;
5156 case 0x1641: // (blue steel)
5157 element = EL_STEEL_CHAR_QUESTION;
5160 case 0x1642: // (blue steel)
5161 element = EL_STEEL_CHAR_COPYRIGHT;
5164 case 0x1643: // (blue steel)
5165 element = EL_STEEL_CHAR_UP;
5168 case 0x1644: // (blue steel)
5169 element = EL_STEEL_CHAR_DOWN;
5172 case 0x1645: // (blue steel)
5173 element = EL_STEEL_CHAR_BUTTON;
5176 case 0x1646: // (blue steel)
5177 element = EL_STEEL_CHAR_PLUS;
5180 case 0x1647: // (blue steel)
5181 element = EL_STEEL_CHAR_MINUS;
5184 case 0x1648: // (blue steel)
5185 element = EL_STEEL_CHAR_APOSTROPHE;
5188 case 0x1649: // (blue steel)
5189 element = EL_STEEL_CHAR_PARENLEFT;
5192 case 0x164a: // (blue steel)
5193 element = EL_STEEL_CHAR_PARENRIGHT;
5196 case 0x164b: // (green steel)
5197 element = EL_STEEL_CHAR_A;
5200 case 0x164c: // (green steel)
5201 element = EL_STEEL_CHAR_B;
5204 case 0x164d: // (green steel)
5205 element = EL_STEEL_CHAR_C;
5208 case 0x164e: // (green steel)
5209 element = EL_STEEL_CHAR_D;
5212 case 0x164f: // (green steel)
5213 element = EL_STEEL_CHAR_E;
5216 case 0x1650: // (green steel)
5217 element = EL_STEEL_CHAR_F;
5220 case 0x1651: // (green steel)
5221 element = EL_STEEL_CHAR_G;
5224 case 0x1652: // (green steel)
5225 element = EL_STEEL_CHAR_H;
5228 case 0x1653: // (green steel)
5229 element = EL_STEEL_CHAR_I;
5232 case 0x1654: // (green steel)
5233 element = EL_STEEL_CHAR_J;
5236 case 0x1655: // (green steel)
5237 element = EL_STEEL_CHAR_K;
5240 case 0x1656: // (green steel)
5241 element = EL_STEEL_CHAR_L;
5244 case 0x1657: // (green steel)
5245 element = EL_STEEL_CHAR_M;
5248 case 0x1658: // (green steel)
5249 element = EL_STEEL_CHAR_N;
5252 case 0x1659: // (green steel)
5253 element = EL_STEEL_CHAR_O;
5256 case 0x165a: // (green steel)
5257 element = EL_STEEL_CHAR_P;
5260 case 0x165b: // (green steel)
5261 element = EL_STEEL_CHAR_Q;
5264 case 0x165c: // (green steel)
5265 element = EL_STEEL_CHAR_R;
5268 case 0x165d: // (green steel)
5269 element = EL_STEEL_CHAR_S;
5272 case 0x165e: // (green steel)
5273 element = EL_STEEL_CHAR_T;
5276 case 0x165f: // (green steel)
5277 element = EL_STEEL_CHAR_U;
5280 case 0x1660: // (green steel)
5281 element = EL_STEEL_CHAR_V;
5284 case 0x1661: // (green steel)
5285 element = EL_STEEL_CHAR_W;
5288 case 0x1662: // (green steel)
5289 element = EL_STEEL_CHAR_X;
5292 case 0x1663: // (green steel)
5293 element = EL_STEEL_CHAR_Y;
5296 case 0x1664: // (green steel)
5297 element = EL_STEEL_CHAR_Z;
5300 case 0x1665: // (green steel)
5301 element = EL_STEEL_CHAR_AUMLAUT;
5304 case 0x1666: // (green steel)
5305 element = EL_STEEL_CHAR_OUMLAUT;
5308 case 0x1667: // (green steel)
5309 element = EL_STEEL_CHAR_UUMLAUT;
5312 case 0x1668: // (green steel)
5313 element = EL_STEEL_CHAR_0;
5316 case 0x1669: // (green steel)
5317 element = EL_STEEL_CHAR_1;
5320 case 0x166a: // (green steel)
5321 element = EL_STEEL_CHAR_2;
5324 case 0x166b: // (green steel)
5325 element = EL_STEEL_CHAR_3;
5328 case 0x166c: // (green steel)
5329 element = EL_STEEL_CHAR_4;
5332 case 0x166d: // (green steel)
5333 element = EL_STEEL_CHAR_5;
5336 case 0x166e: // (green steel)
5337 element = EL_STEEL_CHAR_6;
5340 case 0x166f: // (green steel)
5341 element = EL_STEEL_CHAR_7;
5344 case 0x1670: // (green steel)
5345 element = EL_STEEL_CHAR_8;
5348 case 0x1671: // (green steel)
5349 element = EL_STEEL_CHAR_9;
5352 case 0x1672: // (green steel)
5353 element = EL_STEEL_CHAR_PERIOD;
5356 case 0x1673: // (green steel)
5357 element = EL_STEEL_CHAR_EXCLAM;
5360 case 0x1674: // (green steel)
5361 element = EL_STEEL_CHAR_COLON;
5364 case 0x1675: // (green steel)
5365 element = EL_STEEL_CHAR_LESS;
5368 case 0x1676: // (green steel)
5369 element = EL_STEEL_CHAR_GREATER;
5372 case 0x1677: // (green steel)
5373 element = EL_STEEL_CHAR_QUESTION;
5376 case 0x1678: // (green steel)
5377 element = EL_STEEL_CHAR_COPYRIGHT;
5380 case 0x1679: // (green steel)
5381 element = EL_STEEL_CHAR_UP;
5384 case 0x167a: // (green steel)
5385 element = EL_STEEL_CHAR_DOWN;
5388 case 0x167b: // (green steel)
5389 element = EL_STEEL_CHAR_BUTTON;
5392 case 0x167c: // (green steel)
5393 element = EL_STEEL_CHAR_PLUS;
5396 case 0x167d: // (green steel)
5397 element = EL_STEEL_CHAR_MINUS;
5400 case 0x167e: // (green steel)
5401 element = EL_STEEL_CHAR_APOSTROPHE;
5404 case 0x167f: // (green steel)
5405 element = EL_STEEL_CHAR_PARENLEFT;
5408 case 0x1680: // (green steel)
5409 element = EL_STEEL_CHAR_PARENRIGHT;
5412 case 0x1681: // gate (red)
5413 element = EL_EM_GATE_1;
5416 case 0x1682: // secret gate (red)
5417 element = EL_EM_GATE_1_GRAY;
5420 case 0x1683: // gate (yellow)
5421 element = EL_EM_GATE_2;
5424 case 0x1684: // secret gate (yellow)
5425 element = EL_EM_GATE_2_GRAY;
5428 case 0x1685: // gate (blue)
5429 element = EL_EM_GATE_4;
5432 case 0x1686: // secret gate (blue)
5433 element = EL_EM_GATE_4_GRAY;
5436 case 0x1687: // gate (green)
5437 element = EL_EM_GATE_3;
5440 case 0x1688: // secret gate (green)
5441 element = EL_EM_GATE_3_GRAY;
5444 case 0x1689: // gate (white)
5445 element = EL_DC_GATE_WHITE;
5448 case 0x168a: // secret gate (white)
5449 element = EL_DC_GATE_WHITE_GRAY;
5452 case 0x168b: // secret gate (no key)
5453 element = EL_DC_GATE_FAKE_GRAY;
5457 element = EL_ROBOT_WHEEL;
5461 element = EL_DC_TIMEGATE_SWITCH;
5465 element = EL_ACID_POOL_BOTTOM;
5469 element = EL_ACID_POOL_TOPLEFT;
5473 element = EL_ACID_POOL_TOPRIGHT;
5477 element = EL_ACID_POOL_BOTTOMLEFT;
5481 element = EL_ACID_POOL_BOTTOMRIGHT;
5485 element = EL_STEELWALL;
5489 element = EL_STEELWALL_SLIPPERY;
5492 case 0x1695: // steel wall (not round)
5493 element = EL_STEELWALL;
5496 case 0x1696: // steel wall (left)
5497 element = EL_DC_STEELWALL_1_LEFT;
5500 case 0x1697: // steel wall (bottom)
5501 element = EL_DC_STEELWALL_1_BOTTOM;
5504 case 0x1698: // steel wall (right)
5505 element = EL_DC_STEELWALL_1_RIGHT;
5508 case 0x1699: // steel wall (top)
5509 element = EL_DC_STEELWALL_1_TOP;
5512 case 0x169a: // steel wall (left/bottom)
5513 element = EL_DC_STEELWALL_1_BOTTOMLEFT;
5516 case 0x169b: // steel wall (right/bottom)
5517 element = EL_DC_STEELWALL_1_BOTTOMRIGHT;
5520 case 0x169c: // steel wall (right/top)
5521 element = EL_DC_STEELWALL_1_TOPRIGHT;
5524 case 0x169d: // steel wall (left/top)
5525 element = EL_DC_STEELWALL_1_TOPLEFT;
5528 case 0x169e: // steel wall (right/bottom small)
5529 element = EL_DC_STEELWALL_1_BOTTOMRIGHT_2;
5532 case 0x169f: // steel wall (left/bottom small)
5533 element = EL_DC_STEELWALL_1_BOTTOMLEFT_2;
5536 case 0x16a0: // steel wall (right/top small)
5537 element = EL_DC_STEELWALL_1_TOPRIGHT_2;
5540 case 0x16a1: // steel wall (left/top small)
5541 element = EL_DC_STEELWALL_1_TOPLEFT_2;
5544 case 0x16a2: // steel wall (left/right)
5545 element = EL_DC_STEELWALL_1_VERTICAL;
5548 case 0x16a3: // steel wall (top/bottom)
5549 element = EL_DC_STEELWALL_1_HORIZONTAL;
5552 case 0x16a4: // steel wall 2 (left end)
5553 element = EL_DC_STEELWALL_2_LEFT;
5556 case 0x16a5: // steel wall 2 (right end)
5557 element = EL_DC_STEELWALL_2_RIGHT;
5560 case 0x16a6: // steel wall 2 (top end)
5561 element = EL_DC_STEELWALL_2_TOP;
5564 case 0x16a7: // steel wall 2 (bottom end)
5565 element = EL_DC_STEELWALL_2_BOTTOM;
5568 case 0x16a8: // steel wall 2 (left/right)
5569 element = EL_DC_STEELWALL_2_HORIZONTAL;
5572 case 0x16a9: // steel wall 2 (up/down)
5573 element = EL_DC_STEELWALL_2_VERTICAL;
5576 case 0x16aa: // steel wall 2 (mid)
5577 element = EL_DC_STEELWALL_2_MIDDLE;
5581 element = EL_SIGN_EXCLAMATION;
5585 element = EL_SIGN_RADIOACTIVITY;
5589 element = EL_SIGN_STOP;
5593 element = EL_SIGN_WHEELCHAIR;
5597 element = EL_SIGN_PARKING;
5601 element = EL_SIGN_NO_ENTRY;
5605 element = EL_SIGN_HEART;
5609 element = EL_SIGN_GIVE_WAY;
5613 element = EL_SIGN_ENTRY_FORBIDDEN;
5617 element = EL_SIGN_EMERGENCY_EXIT;
5621 element = EL_SIGN_YIN_YANG;
5625 element = EL_WALL_EMERALD;
5629 element = EL_WALL_DIAMOND;
5633 element = EL_WALL_PEARL;
5637 element = EL_WALL_CRYSTAL;
5641 element = EL_INVISIBLE_WALL;
5645 element = EL_INVISIBLE_STEELWALL;
5649 // EL_INVISIBLE_SAND
5652 element = EL_LIGHT_SWITCH;
5656 element = EL_ENVELOPE_1;
5660 if (element >= 0x0117 && element <= 0x036e) // (?)
5661 element = EL_DIAMOND;
5662 else if (element >= 0x042d && element <= 0x0684) // (?)
5663 element = EL_EMERALD;
5664 else if (element >= 0x157c && element <= 0x158b)
5666 else if (element >= 0x1590 && element <= 0x159f)
5667 element = EL_DC_LANDMINE;
5668 else if (element >= 0x16bc && element <= 0x16cb)
5669 element = EL_INVISIBLE_SAND;
5672 Warn("unknown Diamond Caves element 0x%04x", element);
5674 element = EL_UNKNOWN;
5679 return getMappedElement(element);
5682 static void LoadLevelFromFileStream_DC(File *file, struct LevelInfo *level,
5685 byte header[DC_LEVEL_HEADER_SIZE];
5687 int envelope_header_pos = 62;
5688 int envelope_content_pos = 94;
5689 int level_name_pos = 251;
5690 int level_author_pos = 292;
5691 int envelope_header_len;
5692 int envelope_content_len;
5694 int level_author_len;
5696 int num_yamyam_contents;
5699 getDecodedWord_DC(0, TRUE); // initialize DC2 decoding engine
5701 for (i = 0; i < DC_LEVEL_HEADER_SIZE / 2; i++)
5703 unsigned short header_word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
5705 header[i * 2 + 0] = header_word >> 8;
5706 header[i * 2 + 1] = header_word & 0xff;
5709 // read some values from level header to check level decoding integrity
5710 fieldx = header[6] | (header[7] << 8);
5711 fieldy = header[8] | (header[9] << 8);
5712 num_yamyam_contents = header[60] | (header[61] << 8);
5714 // do some simple sanity checks to ensure that level was correctly decoded
5715 if (fieldx < 1 || fieldx > 256 ||
5716 fieldy < 1 || fieldy > 256 ||
5717 num_yamyam_contents < 1 || num_yamyam_contents > 8)
5719 level->no_valid_file = TRUE;
5721 Warn("cannot decode level from stream -- using empty level");
5726 // maximum envelope header size is 31 bytes
5727 envelope_header_len = header[envelope_header_pos];
5728 // maximum envelope content size is 110 (156?) bytes
5729 envelope_content_len = header[envelope_content_pos];
5731 // maximum level title size is 40 bytes
5732 level_name_len = MIN(header[level_name_pos], MAX_LEVEL_NAME_LEN);
5733 // maximum level author size is 30 (51?) bytes
5734 level_author_len = MIN(header[level_author_pos], MAX_LEVEL_AUTHOR_LEN);
5738 for (i = 0; i < envelope_header_len; i++)
5739 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
5740 level->envelope[0].text[envelope_size++] =
5741 header[envelope_header_pos + 1 + i];
5743 if (envelope_header_len > 0 && envelope_content_len > 0)
5745 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
5746 level->envelope[0].text[envelope_size++] = '\n';
5747 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
5748 level->envelope[0].text[envelope_size++] = '\n';
5751 for (i = 0; i < envelope_content_len; i++)
5752 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
5753 level->envelope[0].text[envelope_size++] =
5754 header[envelope_content_pos + 1 + i];
5756 level->envelope[0].text[envelope_size] = '\0';
5758 level->envelope[0].xsize = MAX_ENVELOPE_XSIZE;
5759 level->envelope[0].ysize = 10;
5760 level->envelope[0].autowrap = TRUE;
5761 level->envelope[0].centered = TRUE;
5763 for (i = 0; i < level_name_len; i++)
5764 level->name[i] = header[level_name_pos + 1 + i];
5765 level->name[level_name_len] = '\0';
5767 for (i = 0; i < level_author_len; i++)
5768 level->author[i] = header[level_author_pos + 1 + i];
5769 level->author[level_author_len] = '\0';
5771 num_yamyam_contents = header[60] | (header[61] << 8);
5772 level->num_yamyam_contents =
5773 MIN(MAX(MIN_ELEMENT_CONTENTS, num_yamyam_contents), MAX_ELEMENT_CONTENTS);
5775 for (i = 0; i < num_yamyam_contents; i++)
5777 for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
5779 unsigned short word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
5780 int element_dc = ((word & 0xff) << 8) | ((word >> 8) & 0xff);
5782 if (i < MAX_ELEMENT_CONTENTS)
5783 level->yamyam_content[i].e[x][y] = getMappedElement_DC(element_dc);
5787 fieldx = header[6] | (header[7] << 8);
5788 fieldy = header[8] | (header[9] << 8);
5789 level->fieldx = MIN(MAX(MIN_LEV_FIELDX, fieldx), MAX_LEV_FIELDX);
5790 level->fieldy = MIN(MAX(MIN_LEV_FIELDY, fieldy), MAX_LEV_FIELDY);
5792 for (y = 0; y < fieldy; y++) for (x = 0; x < fieldx; x++)
5794 unsigned short word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
5795 int element_dc = ((word & 0xff) << 8) | ((word >> 8) & 0xff);
5797 if (x < MAX_LEV_FIELDX && y < MAX_LEV_FIELDY)
5798 level->field[x][y] = getMappedElement_DC(element_dc);
5801 x = MIN(MAX(0, (header[10] | (header[11] << 8)) - 1), MAX_LEV_FIELDX - 1);
5802 y = MIN(MAX(0, (header[12] | (header[13] << 8)) - 1), MAX_LEV_FIELDY - 1);
5803 level->field[x][y] = EL_PLAYER_1;
5805 x = MIN(MAX(0, (header[14] | (header[15] << 8)) - 1), MAX_LEV_FIELDX - 1);
5806 y = MIN(MAX(0, (header[16] | (header[17] << 8)) - 1), MAX_LEV_FIELDY - 1);
5807 level->field[x][y] = EL_PLAYER_2;
5809 level->gems_needed = header[18] | (header[19] << 8);
5811 level->score[SC_EMERALD] = header[20] | (header[21] << 8);
5812 level->score[SC_DIAMOND] = header[22] | (header[23] << 8);
5813 level->score[SC_PEARL] = header[24] | (header[25] << 8);
5814 level->score[SC_CRYSTAL] = header[26] | (header[27] << 8);
5815 level->score[SC_NUT] = header[28] | (header[29] << 8);
5816 level->score[SC_ROBOT] = header[30] | (header[31] << 8);
5817 level->score[SC_SPACESHIP] = header[32] | (header[33] << 8);
5818 level->score[SC_BUG] = header[34] | (header[35] << 8);
5819 level->score[SC_YAMYAM] = header[36] | (header[37] << 8);
5820 level->score[SC_DYNAMITE] = header[38] | (header[39] << 8);
5821 level->score[SC_KEY] = header[40] | (header[41] << 8);
5822 level->score[SC_TIME_BONUS] = header[42] | (header[43] << 8);
5824 level->time = header[44] | (header[45] << 8);
5826 level->amoeba_speed = header[46] | (header[47] << 8);
5827 level->time_light = header[48] | (header[49] << 8);
5828 level->time_timegate = header[50] | (header[51] << 8);
5829 level->time_wheel = header[52] | (header[53] << 8);
5830 level->time_magic_wall = header[54] | (header[55] << 8);
5831 level->extra_time = header[56] | (header[57] << 8);
5832 level->shield_normal_time = header[58] | (header[59] << 8);
5834 // shield and extra time elements do not have a score
5835 level->score[SC_SHIELD] = 0;
5836 level->extra_time_score = 0;
5838 // set time for normal and deadly shields to the same value
5839 level->shield_deadly_time = level->shield_normal_time;
5841 // Diamond Caves has the same (strange) behaviour as Emerald Mine that gems
5842 // can slip down from flat walls, like normal walls and steel walls
5843 level->em_slippery_gems = TRUE;
5845 // time score is counted for each 10 seconds left in Diamond Caves levels
5846 level->time_score_base = 10;
5849 static void LoadLevelFromFileInfo_DC(struct LevelInfo *level,
5850 struct LevelFileInfo *level_file_info,
5851 boolean level_info_only)
5853 char *filename = level_file_info->filename;
5855 int num_magic_bytes = 8;
5856 char magic_bytes[num_magic_bytes + 1];
5857 int num_levels_to_skip = level_file_info->nr - leveldir_current->first_level;
5859 if (!(file = openFile(filename, MODE_READ)))
5861 level->no_valid_file = TRUE;
5863 if (!level_info_only)
5864 Warn("cannot read level '%s' -- using empty level", filename);
5869 // fseek(file, 0x0000, SEEK_SET);
5871 if (level_file_info->packed)
5873 // read "magic bytes" from start of file
5874 if (getStringFromFile(file, magic_bytes, num_magic_bytes + 1) == NULL)
5875 magic_bytes[0] = '\0';
5877 // check "magic bytes" for correct file format
5878 if (!strPrefix(magic_bytes, "DC2"))
5880 level->no_valid_file = TRUE;
5882 Warn("unknown DC level file '%s' -- using empty level", filename);
5887 if (strPrefix(magic_bytes, "DC2Win95") ||
5888 strPrefix(magic_bytes, "DC2Win98"))
5890 int position_first_level = 0x00fa;
5891 int extra_bytes = 4;
5894 // advance file stream to first level inside the level package
5895 skip_bytes = position_first_level - num_magic_bytes - extra_bytes;
5897 // each block of level data is followed by block of non-level data
5898 num_levels_to_skip *= 2;
5900 // at least skip header bytes, therefore use ">= 0" instead of "> 0"
5901 while (num_levels_to_skip >= 0)
5903 // advance file stream to next level inside the level package
5904 if (seekFile(file, skip_bytes, SEEK_CUR) != 0)
5906 level->no_valid_file = TRUE;
5908 Warn("cannot fseek in file '%s' -- using empty level", filename);
5913 // skip apparently unused extra bytes following each level
5914 ReadUnusedBytesFromFile(file, extra_bytes);
5916 // read size of next level in level package
5917 skip_bytes = getFile32BitLE(file);
5919 num_levels_to_skip--;
5924 level->no_valid_file = TRUE;
5926 Warn("unknown DC2 level file '%s' -- using empty level", filename);
5932 LoadLevelFromFileStream_DC(file, level, level_file_info->nr);
5938 // ----------------------------------------------------------------------------
5939 // functions for loading SB level
5940 // ----------------------------------------------------------------------------
5942 int getMappedElement_SB(int element_ascii, boolean use_ces)
5950 sb_element_mapping[] =
5952 { ' ', EL_EMPTY, EL_CUSTOM_1 }, // floor (space)
5953 { '#', EL_STEELWALL, EL_CUSTOM_2 }, // wall
5954 { '@', EL_PLAYER_1, EL_CUSTOM_3 }, // player
5955 { '$', EL_SOKOBAN_OBJECT, EL_CUSTOM_4 }, // box
5956 { '.', EL_SOKOBAN_FIELD_EMPTY, EL_CUSTOM_5 }, // goal square
5957 { '*', EL_SOKOBAN_FIELD_FULL, EL_CUSTOM_6 }, // box on goal square
5958 { '+', EL_SOKOBAN_FIELD_PLAYER, EL_CUSTOM_7 }, // player on goal square
5959 { '_', EL_INVISIBLE_STEELWALL, EL_FROM_LEVEL_TEMPLATE }, // floor beyond border
5966 for (i = 0; sb_element_mapping[i].ascii != 0; i++)
5967 if (element_ascii == sb_element_mapping[i].ascii)
5968 return (use_ces ? sb_element_mapping[i].ce : sb_element_mapping[i].sb);
5970 return EL_UNDEFINED;
5973 static void SetLevelSettings_SB(struct LevelInfo *level)
5977 level->use_step_counter = TRUE;
5980 level->score[SC_TIME_BONUS] = 0;
5981 level->time_score_base = 1;
5982 level->rate_time_over_score = TRUE;
5985 level->auto_exit_sokoban = TRUE;
5988 static void LoadLevelFromFileInfo_SB(struct LevelInfo *level,
5989 struct LevelFileInfo *level_file_info,
5990 boolean level_info_only)
5992 char *filename = level_file_info->filename;
5993 char line[MAX_LINE_LEN], line_raw[MAX_LINE_LEN], previous_line[MAX_LINE_LEN];
5994 char last_comment[MAX_LINE_LEN];
5995 char level_name[MAX_LINE_LEN];
5998 int num_levels_to_skip = level_file_info->nr - leveldir_current->first_level;
5999 boolean read_continued_line = FALSE;
6000 boolean reading_playfield = FALSE;
6001 boolean got_valid_playfield_line = FALSE;
6002 boolean invalid_playfield_char = FALSE;
6003 boolean load_xsb_to_ces = check_special_flags("load_xsb_to_ces");
6004 int file_level_nr = 0;
6006 int x = 0, y = 0; // initialized to make compilers happy
6008 last_comment[0] = '\0';
6009 level_name[0] = '\0';
6011 if (!(file = openFile(filename, MODE_READ)))
6013 level->no_valid_file = TRUE;
6015 if (!level_info_only)
6016 Warn("cannot read level '%s' -- using empty level", filename);
6021 while (!checkEndOfFile(file))
6023 // level successfully read, but next level may follow here
6024 if (!got_valid_playfield_line && reading_playfield)
6026 // read playfield from single level file -- skip remaining file
6027 if (!level_file_info->packed)
6030 if (file_level_nr >= num_levels_to_skip)
6035 last_comment[0] = '\0';
6036 level_name[0] = '\0';
6038 reading_playfield = FALSE;
6041 got_valid_playfield_line = FALSE;
6043 // read next line of input file
6044 if (!getStringFromFile(file, line, MAX_LINE_LEN))
6047 // check if line was completely read and is terminated by line break
6048 if (strlen(line) > 0 && line[strlen(line) - 1] == '\n')
6051 // cut trailing line break (this can be newline and/or carriage return)
6052 for (line_ptr = &line[strlen(line)]; line_ptr >= line; line_ptr--)
6053 if ((*line_ptr == '\n' || *line_ptr == '\r') && *(line_ptr + 1) == '\0')
6056 // copy raw input line for later use (mainly debugging output)
6057 strcpy(line_raw, line);
6059 if (read_continued_line)
6061 // append new line to existing line, if there is enough space
6062 if (strlen(previous_line) + strlen(line_ptr) < MAX_LINE_LEN)
6063 strcat(previous_line, line_ptr);
6065 strcpy(line, previous_line); // copy storage buffer to line
6067 read_continued_line = FALSE;
6070 // if the last character is '\', continue at next line
6071 if (strlen(line) > 0 && line[strlen(line) - 1] == '\\')
6073 line[strlen(line) - 1] = '\0'; // cut off trailing backslash
6074 strcpy(previous_line, line); // copy line to storage buffer
6076 read_continued_line = TRUE;
6082 if (line[0] == '\0')
6085 // extract comment text from comment line
6088 for (line_ptr = line; *line_ptr; line_ptr++)
6089 if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != ';')
6092 strcpy(last_comment, line_ptr);
6097 // extract level title text from line containing level title
6098 if (line[0] == '\'')
6100 strcpy(level_name, &line[1]);
6102 if (strlen(level_name) > 0 && level_name[strlen(level_name) - 1] == '\'')
6103 level_name[strlen(level_name) - 1] = '\0';
6108 // skip lines containing only spaces (or empty lines)
6109 for (line_ptr = line; *line_ptr; line_ptr++)
6110 if (*line_ptr != ' ')
6112 if (*line_ptr == '\0')
6115 // at this point, we have found a line containing part of a playfield
6117 got_valid_playfield_line = TRUE;
6119 if (!reading_playfield)
6121 reading_playfield = TRUE;
6122 invalid_playfield_char = FALSE;
6124 for (x = 0; x < MAX_LEV_FIELDX; x++)
6125 for (y = 0; y < MAX_LEV_FIELDY; y++)
6126 level->field[x][y] = getMappedElement_SB(' ', load_xsb_to_ces);
6131 // start with topmost tile row
6135 // skip playfield line if larger row than allowed
6136 if (y >= MAX_LEV_FIELDY)
6139 // start with leftmost tile column
6142 // read playfield elements from line
6143 for (line_ptr = line; *line_ptr; line_ptr++)
6145 int mapped_sb_element = getMappedElement_SB(*line_ptr, load_xsb_to_ces);
6147 // stop parsing playfield line if larger column than allowed
6148 if (x >= MAX_LEV_FIELDX)
6151 if (mapped_sb_element == EL_UNDEFINED)
6153 invalid_playfield_char = TRUE;
6158 level->field[x][y] = mapped_sb_element;
6160 // continue with next tile column
6163 level->fieldx = MAX(x, level->fieldx);
6166 if (invalid_playfield_char)
6168 // if first playfield line, treat invalid lines as comment lines
6170 reading_playfield = FALSE;
6175 // continue with next tile row
6183 level->fieldx = MIN(MAX(MIN_LEV_FIELDX, level->fieldx), MAX_LEV_FIELDX);
6184 level->fieldy = MIN(MAX(MIN_LEV_FIELDY, level->fieldy), MAX_LEV_FIELDY);
6186 if (!reading_playfield)
6188 level->no_valid_file = TRUE;
6190 Warn("cannot read level '%s' -- using empty level", filename);
6195 if (*level_name != '\0')
6197 strncpy(level->name, level_name, MAX_LEVEL_NAME_LEN);
6198 level->name[MAX_LEVEL_NAME_LEN] = '\0';
6200 else if (*last_comment != '\0')
6202 strncpy(level->name, last_comment, MAX_LEVEL_NAME_LEN);
6203 level->name[MAX_LEVEL_NAME_LEN] = '\0';
6207 sprintf(level->name, "--> Level %d <--", level_file_info->nr);
6210 // set all empty fields beyond the border walls to invisible steel wall
6211 for (y = 0; y < level->fieldy; y++) for (x = 0; x < level->fieldx; x++)
6213 if ((x == 0 || x == level->fieldx - 1 ||
6214 y == 0 || y == level->fieldy - 1) &&
6215 level->field[x][y] == getMappedElement_SB(' ', load_xsb_to_ces))
6216 FloodFillLevel(x, y, getMappedElement_SB('_', load_xsb_to_ces),
6217 level->field, level->fieldx, level->fieldy);
6220 // set special level settings for Sokoban levels
6221 SetLevelSettings_SB(level);
6223 if (load_xsb_to_ces)
6225 // special global settings can now be set in level template
6226 level->use_custom_template = TRUE;
6231 // -------------------------------------------------------------------------
6232 // functions for handling native levels
6233 // -------------------------------------------------------------------------
6235 static void LoadLevelFromFileInfo_EM(struct LevelInfo *level,
6236 struct LevelFileInfo *level_file_info,
6237 boolean level_info_only)
6239 if (!LoadNativeLevel_EM(level_file_info->filename, level_info_only))
6240 level->no_valid_file = TRUE;
6243 static void LoadLevelFromFileInfo_SP(struct LevelInfo *level,
6244 struct LevelFileInfo *level_file_info,
6245 boolean level_info_only)
6249 // determine position of requested level inside level package
6250 if (level_file_info->packed)
6251 pos = level_file_info->nr - leveldir_current->first_level;
6253 if (!LoadNativeLevel_SP(level_file_info->filename, pos, level_info_only))
6254 level->no_valid_file = TRUE;
6257 static void LoadLevelFromFileInfo_MM(struct LevelInfo *level,
6258 struct LevelFileInfo *level_file_info,
6259 boolean level_info_only)
6261 if (!LoadNativeLevel_MM(level_file_info->filename, level_info_only))
6262 level->no_valid_file = TRUE;
6265 void CopyNativeLevel_RND_to_Native(struct LevelInfo *level)
6267 if (level->game_engine_type == GAME_ENGINE_TYPE_EM)
6268 CopyNativeLevel_RND_to_EM(level);
6269 else if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
6270 CopyNativeLevel_RND_to_SP(level);
6271 else if (level->game_engine_type == GAME_ENGINE_TYPE_MM)
6272 CopyNativeLevel_RND_to_MM(level);
6275 void CopyNativeLevel_Native_to_RND(struct LevelInfo *level)
6277 if (level->game_engine_type == GAME_ENGINE_TYPE_EM)
6278 CopyNativeLevel_EM_to_RND(level);
6279 else if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
6280 CopyNativeLevel_SP_to_RND(level);
6281 else if (level->game_engine_type == GAME_ENGINE_TYPE_MM)
6282 CopyNativeLevel_MM_to_RND(level);
6285 void SaveNativeLevel(struct LevelInfo *level)
6287 if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
6289 char *basename = getSingleLevelBasenameExt(level->file_info.nr, "sp");
6290 char *filename = getLevelFilenameFromBasename(basename);
6292 CopyNativeLevel_RND_to_SP(level);
6293 CopyNativeTape_RND_to_SP(level);
6295 SaveNativeLevel_SP(filename);
6300 // ----------------------------------------------------------------------------
6301 // functions for loading generic level
6302 // ----------------------------------------------------------------------------
6304 static void LoadLevelFromFileInfo(struct LevelInfo *level,
6305 struct LevelFileInfo *level_file_info,
6306 boolean level_info_only)
6308 // always start with reliable default values
6309 setLevelInfoToDefaults(level, level_info_only, TRUE);
6311 switch (level_file_info->type)
6313 case LEVEL_FILE_TYPE_RND:
6314 LoadLevelFromFileInfo_RND(level, level_file_info, level_info_only);
6317 case LEVEL_FILE_TYPE_EM:
6318 LoadLevelFromFileInfo_EM(level, level_file_info, level_info_only);
6319 level->game_engine_type = GAME_ENGINE_TYPE_EM;
6322 case LEVEL_FILE_TYPE_SP:
6323 LoadLevelFromFileInfo_SP(level, level_file_info, level_info_only);
6324 level->game_engine_type = GAME_ENGINE_TYPE_SP;
6327 case LEVEL_FILE_TYPE_MM:
6328 LoadLevelFromFileInfo_MM(level, level_file_info, level_info_only);
6329 level->game_engine_type = GAME_ENGINE_TYPE_MM;
6332 case LEVEL_FILE_TYPE_DC:
6333 LoadLevelFromFileInfo_DC(level, level_file_info, level_info_only);
6336 case LEVEL_FILE_TYPE_SB:
6337 LoadLevelFromFileInfo_SB(level, level_file_info, level_info_only);
6341 LoadLevelFromFileInfo_RND(level, level_file_info, level_info_only);
6345 // if level file is invalid, restore level structure to default values
6346 if (level->no_valid_file)
6347 setLevelInfoToDefaults(level, level_info_only, FALSE);
6349 if (level->game_engine_type == GAME_ENGINE_TYPE_UNKNOWN)
6350 level->game_engine_type = GAME_ENGINE_TYPE_RND;
6352 if (level_file_info->type != LEVEL_FILE_TYPE_RND)
6353 CopyNativeLevel_Native_to_RND(level);
6356 void LoadLevelFromFilename(struct LevelInfo *level, char *filename)
6358 static struct LevelFileInfo level_file_info;
6360 // always start with reliable default values
6361 setFileInfoToDefaults(&level_file_info);
6363 level_file_info.nr = 0; // unknown level number
6364 level_file_info.type = LEVEL_FILE_TYPE_RND; // no others supported yet
6366 setString(&level_file_info.filename, filename);
6368 LoadLevelFromFileInfo(level, &level_file_info, FALSE);
6371 static void LoadLevel_InitVersion(struct LevelInfo *level)
6375 if (leveldir_current == NULL) // only when dumping level
6378 // all engine modifications also valid for levels which use latest engine
6379 if (level->game_version < VERSION_IDENT(3,2,0,5))
6381 // time bonus score was given for 10 s instead of 1 s before 3.2.0-5
6382 level->time_score_base = 10;
6385 if (leveldir_current->latest_engine)
6387 // ---------- use latest game engine --------------------------------------
6389 /* For all levels which are forced to use the latest game engine version
6390 (normally all but user contributed, private and undefined levels), set
6391 the game engine version to the actual version; this allows for actual
6392 corrections in the game engine to take effect for existing, converted
6393 levels (from "classic" or other existing games) to make the emulation
6394 of the corresponding game more accurate, while (hopefully) not breaking
6395 existing levels created from other players. */
6397 level->game_version = GAME_VERSION_ACTUAL;
6399 /* Set special EM style gems behaviour: EM style gems slip down from
6400 normal, steel and growing wall. As this is a more fundamental change,
6401 it seems better to set the default behaviour to "off" (as it is more
6402 natural) and make it configurable in the level editor (as a property
6403 of gem style elements). Already existing converted levels (neither
6404 private nor contributed levels) are changed to the new behaviour. */
6406 if (level->file_version < FILE_VERSION_2_0)
6407 level->em_slippery_gems = TRUE;
6412 // ---------- use game engine the level was created with --------------------
6414 /* For all levels which are not forced to use the latest game engine
6415 version (normally user contributed, private and undefined levels),
6416 use the version of the game engine the levels were created for.
6418 Since 2.0.1, the game engine version is now directly stored
6419 in the level file (chunk "VERS"), so there is no need anymore
6420 to set the game version from the file version (except for old,
6421 pre-2.0 levels, where the game version is still taken from the
6422 file format version used to store the level -- see above). */
6424 // player was faster than enemies in 1.0.0 and before
6425 if (level->file_version == FILE_VERSION_1_0)
6426 for (i = 0; i < MAX_PLAYERS; i++)
6427 level->initial_player_stepsize[i] = STEPSIZE_FAST;
6429 // default behaviour for EM style gems was "slippery" only in 2.0.1
6430 if (level->game_version == VERSION_IDENT(2,0,1,0))
6431 level->em_slippery_gems = TRUE;
6433 // springs could be pushed over pits before (pre-release version) 2.2.0
6434 if (level->game_version < VERSION_IDENT(2,2,0,0))
6435 level->use_spring_bug = TRUE;
6437 if (level->game_version < VERSION_IDENT(3,2,0,5))
6439 // time orb caused limited time in endless time levels before 3.2.0-5
6440 level->use_time_orb_bug = TRUE;
6442 // default behaviour for snapping was "no snap delay" before 3.2.0-5
6443 level->block_snap_field = FALSE;
6445 // extra time score was same value as time left score before 3.2.0-5
6446 level->extra_time_score = level->score[SC_TIME_BONUS];
6449 if (level->game_version < VERSION_IDENT(3,2,0,7))
6451 // default behaviour for snapping was "not continuous" before 3.2.0-7
6452 level->continuous_snapping = FALSE;
6455 // only few elements were able to actively move into acid before 3.1.0
6456 // trigger settings did not exist before 3.1.0; set to default "any"
6457 if (level->game_version < VERSION_IDENT(3,1,0,0))
6459 // correct "can move into acid" settings (all zero in old levels)
6461 level->can_move_into_acid_bits = 0; // nothing can move into acid
6462 level->dont_collide_with_bits = 0; // nothing is deadly when colliding
6464 setMoveIntoAcidProperty(level, EL_ROBOT, TRUE);
6465 setMoveIntoAcidProperty(level, EL_SATELLITE, TRUE);
6466 setMoveIntoAcidProperty(level, EL_PENGUIN, TRUE);
6467 setMoveIntoAcidProperty(level, EL_BALLOON, TRUE);
6469 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6470 SET_PROPERTY(EL_CUSTOM_START + i, EP_CAN_MOVE_INTO_ACID, TRUE);
6472 // correct trigger settings (stored as zero == "none" in old levels)
6474 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6476 int element = EL_CUSTOM_START + i;
6477 struct ElementInfo *ei = &element_info[element];
6479 for (j = 0; j < ei->num_change_pages; j++)
6481 struct ElementChangeInfo *change = &ei->change_page[j];
6483 change->trigger_player = CH_PLAYER_ANY;
6484 change->trigger_page = CH_PAGE_ANY;
6489 // try to detect and fix "Snake Bite" levels, which are broken with 3.2.0
6491 int element = EL_CUSTOM_256;
6492 struct ElementInfo *ei = &element_info[element];
6493 struct ElementChangeInfo *change = &ei->change_page[0];
6495 /* This is needed to fix a problem that was caused by a bugfix in function
6496 game.c/CreateFieldExt() introduced with 3.2.0 that corrects the behaviour
6497 when a custom element changes to EL_SOKOBAN_FIELD_PLAYER (before, it did
6498 not replace walkable elements, but instead just placed the player on it,
6499 without placing the Sokoban field under the player). Unfortunately, this
6500 breaks "Snake Bite" style levels when the snake is halfway through a door
6501 that just closes (the snake head is still alive and can be moved in this
6502 case). This can be fixed by replacing the EL_SOKOBAN_FIELD_PLAYER by the
6503 player (without Sokoban element) which then gets killed as designed). */
6505 if ((strncmp(leveldir_current->identifier, "snake_bite", 10) == 0 ||
6506 strncmp(ei->description, "pause b4 death", 14) == 0) &&
6507 change->target_element == EL_SOKOBAN_FIELD_PLAYER)
6508 change->target_element = EL_PLAYER_1;
6511 // try to detect and fix "Zelda" style levels, which are broken with 3.2.5
6512 if (level->game_version < VERSION_IDENT(3,2,5,0))
6514 /* This is needed to fix a problem that was caused by a bugfix in function
6515 game.c/CheckTriggeredElementChangeExt() introduced with 3.2.5 that
6516 corrects the behaviour when a custom element changes to another custom
6517 element with a higher element number that has change actions defined.
6518 Normally, only one change per frame is allowed for custom elements.
6519 Therefore, it is checked if a custom element already changed in the
6520 current frame; if it did, subsequent changes are suppressed.
6521 Unfortunately, this is only checked for element changes, but not for
6522 change actions, which are still executed. As the function above loops
6523 through all custom elements from lower to higher, an element change
6524 resulting in a lower CE number won't be checked again, while a target
6525 element with a higher number will also be checked, and potential change
6526 actions will get executed for this CE, too (which is wrong), while
6527 further changes are ignored (which is correct). As this bugfix breaks
6528 Zelda II (and introduces graphical bugs to Zelda I, and also breaks a
6529 few other levels like Alan Bond's "FMV"), allow the previous, incorrect
6530 behaviour for existing levels and tapes that make use of this bug */
6532 level->use_action_after_change_bug = TRUE;
6535 // not centering level after relocating player was default only in 3.2.3
6536 if (level->game_version == VERSION_IDENT(3,2,3,0)) // (no pre-releases)
6537 level->shifted_relocation = TRUE;
6539 // EM style elements always chain-exploded in R'n'D engine before 3.2.6
6540 if (level->game_version < VERSION_IDENT(3,2,6,0))
6541 level->em_explodes_by_fire = TRUE;
6543 // levels were solved by the first player entering an exit up to 4.1.0.0
6544 if (level->game_version <= VERSION_IDENT(4,1,0,0))
6545 level->solved_by_one_player = TRUE;
6547 // game logic of "game of life" and "biomaze" was buggy before 4.1.1.1
6548 if (level->game_version < VERSION_IDENT(4,1,1,1))
6549 level->use_life_bugs = TRUE;
6551 // only Sokoban fields (but not objects) had to be solved before 4.1.1.1
6552 if (level->game_version < VERSION_IDENT(4,1,1,1))
6553 level->sb_objects_needed = FALSE;
6555 // CE actions were triggered by unfinished digging/collecting up to 4.2.2.0
6556 if (level->game_version <= VERSION_IDENT(4,2,2,0))
6557 level->finish_dig_collect = FALSE;
6559 // CE changing to player was kept under the player if walkable up to 4.2.3.1
6560 if (level->game_version <= VERSION_IDENT(4,2,3,1))
6561 level->keep_walkable_ce = TRUE;
6564 static void LoadLevel_InitSettings_SB(struct LevelInfo *level)
6566 boolean is_sokoban_level = TRUE; // unless non-Sokoban elements found
6569 // check if this level is (not) a Sokoban level
6570 for (y = 0; y < level->fieldy; y++)
6571 for (x = 0; x < level->fieldx; x++)
6572 if (!IS_SB_ELEMENT(Tile[x][y]))
6573 is_sokoban_level = FALSE;
6575 if (is_sokoban_level)
6577 // set special level settings for Sokoban levels
6578 SetLevelSettings_SB(level);
6582 static void LoadLevel_InitSettings(struct LevelInfo *level)
6584 // adjust level settings for (non-native) Sokoban-style levels
6585 LoadLevel_InitSettings_SB(level);
6587 // rename levels with title "nameless level" or if renaming is forced
6588 if (leveldir_current->empty_level_name != NULL &&
6589 (strEqual(level->name, NAMELESS_LEVEL_NAME) ||
6590 leveldir_current->force_level_name))
6591 snprintf(level->name, MAX_LEVEL_NAME_LEN + 1,
6592 leveldir_current->empty_level_name, level_nr);
6595 static void LoadLevel_InitStandardElements(struct LevelInfo *level)
6599 // map elements that have changed in newer versions
6600 level->amoeba_content = getMappedElementByVersion(level->amoeba_content,
6601 level->game_version);
6602 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
6603 for (x = 0; x < 3; x++)
6604 for (y = 0; y < 3; y++)
6605 level->yamyam_content[i].e[x][y] =
6606 getMappedElementByVersion(level->yamyam_content[i].e[x][y],
6607 level->game_version);
6611 static void LoadLevel_InitCustomElements(struct LevelInfo *level)
6615 // map custom element change events that have changed in newer versions
6616 // (these following values were accidentally changed in version 3.0.1)
6617 // (this seems to be needed only for 'ab_levelset3' and 'ab_levelset4')
6618 if (level->game_version <= VERSION_IDENT(3,0,0,0))
6620 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6622 int element = EL_CUSTOM_START + i;
6624 // order of checking and copying events to be mapped is important
6625 // (do not change the start and end value -- they are constant)
6626 for (j = CE_BY_OTHER_ACTION; j >= CE_VALUE_GETS_ZERO; j--)
6628 if (HAS_CHANGE_EVENT(element, j - 2))
6630 SET_CHANGE_EVENT(element, j - 2, FALSE);
6631 SET_CHANGE_EVENT(element, j, TRUE);
6635 // order of checking and copying events to be mapped is important
6636 // (do not change the start and end value -- they are constant)
6637 for (j = CE_PLAYER_COLLECTS_X; j >= CE_HITTING_SOMETHING; j--)
6639 if (HAS_CHANGE_EVENT(element, j - 1))
6641 SET_CHANGE_EVENT(element, j - 1, FALSE);
6642 SET_CHANGE_EVENT(element, j, TRUE);
6648 // initialize "can_change" field for old levels with only one change page
6649 if (level->game_version <= VERSION_IDENT(3,0,2,0))
6651 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6653 int element = EL_CUSTOM_START + i;
6655 if (CAN_CHANGE(element))
6656 element_info[element].change->can_change = TRUE;
6660 // correct custom element values (for old levels without these options)
6661 if (level->game_version < VERSION_IDENT(3,1,1,0))
6663 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6665 int element = EL_CUSTOM_START + i;
6666 struct ElementInfo *ei = &element_info[element];
6668 if (ei->access_direction == MV_NO_DIRECTION)
6669 ei->access_direction = MV_ALL_DIRECTIONS;
6673 // correct custom element values (fix invalid values for all versions)
6676 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6678 int element = EL_CUSTOM_START + i;
6679 struct ElementInfo *ei = &element_info[element];
6681 for (j = 0; j < ei->num_change_pages; j++)
6683 struct ElementChangeInfo *change = &ei->change_page[j];
6685 if (change->trigger_player == CH_PLAYER_NONE)
6686 change->trigger_player = CH_PLAYER_ANY;
6688 if (change->trigger_side == CH_SIDE_NONE)
6689 change->trigger_side = CH_SIDE_ANY;
6694 // initialize "can_explode" field for old levels which did not store this
6695 // !!! CHECK THIS -- "<= 3,1,0,0" IS PROBABLY WRONG !!!
6696 if (level->game_version <= VERSION_IDENT(3,1,0,0))
6698 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6700 int element = EL_CUSTOM_START + i;
6702 if (EXPLODES_1X1_OLD(element))
6703 element_info[element].explosion_type = EXPLODES_1X1;
6705 SET_PROPERTY(element, EP_CAN_EXPLODE, (EXPLODES_BY_FIRE(element) ||
6706 EXPLODES_SMASHED(element) ||
6707 EXPLODES_IMPACT(element)));
6711 // correct previously hard-coded move delay values for maze runner style
6712 if (level->game_version < VERSION_IDENT(3,1,1,0))
6714 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6716 int element = EL_CUSTOM_START + i;
6718 if (element_info[element].move_pattern & MV_MAZE_RUNNER_STYLE)
6720 // previously hard-coded and therefore ignored
6721 element_info[element].move_delay_fixed = 9;
6722 element_info[element].move_delay_random = 0;
6727 // set some other uninitialized values of custom elements in older levels
6728 if (level->game_version < VERSION_IDENT(3,1,0,0))
6730 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6732 int element = EL_CUSTOM_START + i;
6734 element_info[element].access_direction = MV_ALL_DIRECTIONS;
6736 element_info[element].explosion_delay = 17;
6737 element_info[element].ignition_delay = 8;
6741 // set mouse click change events to work for left/middle/right mouse button
6742 if (level->game_version < VERSION_IDENT(4,2,3,0))
6744 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6746 int element = EL_CUSTOM_START + i;
6747 struct ElementInfo *ei = &element_info[element];
6749 for (j = 0; j < ei->num_change_pages; j++)
6751 struct ElementChangeInfo *change = &ei->change_page[j];
6753 if (change->has_event[CE_CLICKED_BY_MOUSE] ||
6754 change->has_event[CE_PRESSED_BY_MOUSE] ||
6755 change->has_event[CE_MOUSE_CLICKED_ON_X] ||
6756 change->has_event[CE_MOUSE_PRESSED_ON_X])
6757 change->trigger_side = CH_SIDE_ANY;
6763 static void LoadLevel_InitElements(struct LevelInfo *level)
6765 LoadLevel_InitStandardElements(level);
6767 if (level->file_has_custom_elements)
6768 LoadLevel_InitCustomElements(level);
6770 // initialize element properties for level editor etc.
6771 InitElementPropertiesEngine(level->game_version);
6772 InitElementPropertiesGfxElement();
6775 static void LoadLevel_InitPlayfield(struct LevelInfo *level)
6779 // map elements that have changed in newer versions
6780 for (y = 0; y < level->fieldy; y++)
6781 for (x = 0; x < level->fieldx; x++)
6782 level->field[x][y] = getMappedElementByVersion(level->field[x][y],
6783 level->game_version);
6785 // clear unused playfield data (nicer if level gets resized in editor)
6786 for (x = 0; x < MAX_LEV_FIELDX; x++)
6787 for (y = 0; y < MAX_LEV_FIELDY; y++)
6788 if (x >= level->fieldx || y >= level->fieldy)
6789 level->field[x][y] = EL_EMPTY;
6791 // copy elements to runtime playfield array
6792 for (x = 0; x < MAX_LEV_FIELDX; x++)
6793 for (y = 0; y < MAX_LEV_FIELDY; y++)
6794 Tile[x][y] = level->field[x][y];
6796 // initialize level size variables for faster access
6797 lev_fieldx = level->fieldx;
6798 lev_fieldy = level->fieldy;
6800 // determine border element for this level
6801 if (level->file_info.type == LEVEL_FILE_TYPE_DC)
6802 BorderElement = EL_EMPTY; // (in editor, SetBorderElement() is used)
6807 static void LoadLevel_InitNativeEngines(struct LevelInfo *level)
6809 struct LevelFileInfo *level_file_info = &level->file_info;
6811 if (level_file_info->type == LEVEL_FILE_TYPE_RND)
6812 CopyNativeLevel_RND_to_Native(level);
6815 static void LoadLevelTemplate_LoadAndInit(void)
6817 LoadLevelFromFileInfo(&level_template, &level_template.file_info, FALSE);
6819 LoadLevel_InitVersion(&level_template);
6820 LoadLevel_InitElements(&level_template);
6821 LoadLevel_InitSettings(&level_template);
6823 ActivateLevelTemplate();
6826 void LoadLevelTemplate(int nr)
6828 if (!fileExists(getGlobalLevelTemplateFilename()))
6830 Warn("no level template found for this level");
6835 setLevelFileInfo(&level_template.file_info, nr);
6837 LoadLevelTemplate_LoadAndInit();
6840 static void LoadNetworkLevelTemplate(struct NetworkLevelInfo *network_level)
6842 copyLevelFileInfo(&network_level->tmpl_info, &level_template.file_info);
6844 LoadLevelTemplate_LoadAndInit();
6847 static void LoadLevel_LoadAndInit(struct NetworkLevelInfo *network_level)
6849 LoadLevelFromFileInfo(&level, &level.file_info, FALSE);
6851 if (level.use_custom_template)
6853 if (network_level != NULL)
6854 LoadNetworkLevelTemplate(network_level);
6856 LoadLevelTemplate(-1);
6859 LoadLevel_InitVersion(&level);
6860 LoadLevel_InitElements(&level);
6861 LoadLevel_InitPlayfield(&level);
6862 LoadLevel_InitSettings(&level);
6864 LoadLevel_InitNativeEngines(&level);
6867 void LoadLevel(int nr)
6869 SetLevelSetInfo(leveldir_current->identifier, nr);
6871 setLevelFileInfo(&level.file_info, nr);
6873 LoadLevel_LoadAndInit(NULL);
6876 void LoadLevelInfoOnly(int nr)
6878 setLevelFileInfo(&level.file_info, nr);
6880 LoadLevelFromFileInfo(&level, &level.file_info, TRUE);
6883 void LoadNetworkLevel(struct NetworkLevelInfo *network_level)
6885 SetLevelSetInfo(network_level->leveldir_identifier,
6886 network_level->file_info.nr);
6888 copyLevelFileInfo(&network_level->file_info, &level.file_info);
6890 LoadLevel_LoadAndInit(network_level);
6893 static int SaveLevel_VERS(FILE *file, struct LevelInfo *level)
6897 chunk_size += putFileVersion(file, level->file_version);
6898 chunk_size += putFileVersion(file, level->game_version);
6903 static int SaveLevel_DATE(FILE *file, struct LevelInfo *level)
6907 chunk_size += putFile16BitBE(file, level->creation_date.year);
6908 chunk_size += putFile8Bit(file, level->creation_date.month);
6909 chunk_size += putFile8Bit(file, level->creation_date.day);
6914 #if ENABLE_HISTORIC_CHUNKS
6915 static void SaveLevel_HEAD(FILE *file, struct LevelInfo *level)
6919 putFile8Bit(file, level->fieldx);
6920 putFile8Bit(file, level->fieldy);
6922 putFile16BitBE(file, level->time);
6923 putFile16BitBE(file, level->gems_needed);
6925 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
6926 putFile8Bit(file, level->name[i]);
6928 for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
6929 putFile8Bit(file, level->score[i]);
6931 for (i = 0; i < STD_ELEMENT_CONTENTS; i++)
6932 for (y = 0; y < 3; y++)
6933 for (x = 0; x < 3; x++)
6934 putFile8Bit(file, (level->encoding_16bit_yamyam ? EL_EMPTY :
6935 level->yamyam_content[i].e[x][y]));
6936 putFile8Bit(file, level->amoeba_speed);
6937 putFile8Bit(file, level->time_magic_wall);
6938 putFile8Bit(file, level->time_wheel);
6939 putFile8Bit(file, (level->encoding_16bit_amoeba ? EL_EMPTY :
6940 level->amoeba_content));
6941 putFile8Bit(file, (level->initial_player_stepsize == STEPSIZE_FAST ? 1 : 0));
6942 putFile8Bit(file, (level->initial_gravity ? 1 : 0));
6943 putFile8Bit(file, (level->encoding_16bit_field ? 1 : 0));
6944 putFile8Bit(file, (level->em_slippery_gems ? 1 : 0));
6946 putFile8Bit(file, (level->use_custom_template ? 1 : 0));
6948 putFile8Bit(file, (level->block_last_field ? 1 : 0));
6949 putFile8Bit(file, (level->sp_block_last_field ? 1 : 0));
6950 putFile32BitBE(file, level->can_move_into_acid_bits);
6951 putFile8Bit(file, level->dont_collide_with_bits);
6953 putFile8Bit(file, (level->use_spring_bug ? 1 : 0));
6954 putFile8Bit(file, (level->use_step_counter ? 1 : 0));
6956 putFile8Bit(file, (level->instant_relocation ? 1 : 0));
6957 putFile8Bit(file, (level->can_pass_to_walkable ? 1 : 0));
6958 putFile8Bit(file, (level->grow_into_diggable ? 1 : 0));
6960 putFile8Bit(file, level->game_engine_type);
6962 WriteUnusedBytesToFile(file, LEVEL_CHUNK_HEAD_UNUSED);
6966 static int SaveLevel_NAME(FILE *file, struct LevelInfo *level)
6971 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
6972 chunk_size += putFile8Bit(file, level->name[i]);
6977 static int SaveLevel_AUTH(FILE *file, struct LevelInfo *level)
6982 for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
6983 chunk_size += putFile8Bit(file, level->author[i]);
6988 #if ENABLE_HISTORIC_CHUNKS
6989 static int SaveLevel_BODY(FILE *file, struct LevelInfo *level)
6994 for (y = 0; y < level->fieldy; y++)
6995 for (x = 0; x < level->fieldx; x++)
6996 if (level->encoding_16bit_field)
6997 chunk_size += putFile16BitBE(file, level->field[x][y]);
6999 chunk_size += putFile8Bit(file, level->field[x][y]);
7005 static int SaveLevel_BODY(FILE *file, struct LevelInfo *level)
7010 for (y = 0; y < level->fieldy; y++)
7011 for (x = 0; x < level->fieldx; x++)
7012 chunk_size += putFile16BitBE(file, level->field[x][y]);
7017 #if ENABLE_HISTORIC_CHUNKS
7018 static void SaveLevel_CONT(FILE *file, struct LevelInfo *level)
7022 putFile8Bit(file, EL_YAMYAM);
7023 putFile8Bit(file, level->num_yamyam_contents);
7024 putFile8Bit(file, 0);
7025 putFile8Bit(file, 0);
7027 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
7028 for (y = 0; y < 3; y++)
7029 for (x = 0; x < 3; x++)
7030 if (level->encoding_16bit_field)
7031 putFile16BitBE(file, level->yamyam_content[i].e[x][y]);
7033 putFile8Bit(file, level->yamyam_content[i].e[x][y]);
7037 #if ENABLE_HISTORIC_CHUNKS
7038 static void SaveLevel_CNT2(FILE *file, struct LevelInfo *level, int element)
7041 int num_contents, content_xsize, content_ysize;
7042 int content_array[MAX_ELEMENT_CONTENTS][3][3];
7044 if (element == EL_YAMYAM)
7046 num_contents = level->num_yamyam_contents;
7050 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
7051 for (y = 0; y < 3; y++)
7052 for (x = 0; x < 3; x++)
7053 content_array[i][x][y] = level->yamyam_content[i].e[x][y];
7055 else if (element == EL_BD_AMOEBA)
7061 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
7062 for (y = 0; y < 3; y++)
7063 for (x = 0; x < 3; x++)
7064 content_array[i][x][y] = EL_EMPTY;
7065 content_array[0][0][0] = level->amoeba_content;
7069 // chunk header already written -- write empty chunk data
7070 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_SIZE);
7072 Warn("cannot save content for element '%d'", element);
7077 putFile16BitBE(file, element);
7078 putFile8Bit(file, num_contents);
7079 putFile8Bit(file, content_xsize);
7080 putFile8Bit(file, content_ysize);
7082 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_UNUSED);
7084 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
7085 for (y = 0; y < 3; y++)
7086 for (x = 0; x < 3; x++)
7087 putFile16BitBE(file, content_array[i][x][y]);
7091 #if ENABLE_HISTORIC_CHUNKS
7092 static int SaveLevel_CNT3(FILE *file, struct LevelInfo *level, int element)
7094 int envelope_nr = element - EL_ENVELOPE_1;
7095 int envelope_len = strlen(level->envelope_text[envelope_nr]) + 1;
7099 chunk_size += putFile16BitBE(file, element);
7100 chunk_size += putFile16BitBE(file, envelope_len);
7101 chunk_size += putFile8Bit(file, level->envelope_xsize[envelope_nr]);
7102 chunk_size += putFile8Bit(file, level->envelope_ysize[envelope_nr]);
7104 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT3_UNUSED);
7105 chunk_size += LEVEL_CHUNK_CNT3_UNUSED;
7107 for (i = 0; i < envelope_len; i++)
7108 chunk_size += putFile8Bit(file, level->envelope_text[envelope_nr][i]);
7114 #if ENABLE_HISTORIC_CHUNKS
7115 static void SaveLevel_CUS1(FILE *file, struct LevelInfo *level,
7116 int num_changed_custom_elements)
7120 putFile16BitBE(file, num_changed_custom_elements);
7122 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7124 int element = EL_CUSTOM_START + i;
7126 struct ElementInfo *ei = &element_info[element];
7128 if (ei->properties[EP_BITFIELD_BASE_NR] != EP_BITMASK_DEFAULT)
7130 if (check < num_changed_custom_elements)
7132 putFile16BitBE(file, element);
7133 putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
7140 if (check != num_changed_custom_elements) // should not happen
7141 Warn("inconsistent number of custom element properties");
7145 #if ENABLE_HISTORIC_CHUNKS
7146 static void SaveLevel_CUS2(FILE *file, struct LevelInfo *level,
7147 int num_changed_custom_elements)
7151 putFile16BitBE(file, num_changed_custom_elements);
7153 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7155 int element = EL_CUSTOM_START + i;
7157 if (element_info[element].change->target_element != EL_EMPTY_SPACE)
7159 if (check < num_changed_custom_elements)
7161 putFile16BitBE(file, element);
7162 putFile16BitBE(file, element_info[element].change->target_element);
7169 if (check != num_changed_custom_elements) // should not happen
7170 Warn("inconsistent number of custom target elements");
7174 #if ENABLE_HISTORIC_CHUNKS
7175 static void SaveLevel_CUS3(FILE *file, struct LevelInfo *level,
7176 int num_changed_custom_elements)
7178 int i, j, x, y, check = 0;
7180 putFile16BitBE(file, num_changed_custom_elements);
7182 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7184 int element = EL_CUSTOM_START + i;
7185 struct ElementInfo *ei = &element_info[element];
7187 if (ei->modified_settings)
7189 if (check < num_changed_custom_elements)
7191 putFile16BitBE(file, element);
7193 for (j = 0; j < MAX_ELEMENT_NAME_LEN; j++)
7194 putFile8Bit(file, ei->description[j]);
7196 putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
7198 // some free bytes for future properties and padding
7199 WriteUnusedBytesToFile(file, 7);
7201 putFile8Bit(file, ei->use_gfx_element);
7202 putFile16BitBE(file, ei->gfx_element_initial);
7204 putFile8Bit(file, ei->collect_score_initial);
7205 putFile8Bit(file, ei->collect_count_initial);
7207 putFile16BitBE(file, ei->push_delay_fixed);
7208 putFile16BitBE(file, ei->push_delay_random);
7209 putFile16BitBE(file, ei->move_delay_fixed);
7210 putFile16BitBE(file, ei->move_delay_random);
7212 putFile16BitBE(file, ei->move_pattern);
7213 putFile8Bit(file, ei->move_direction_initial);
7214 putFile8Bit(file, ei->move_stepsize);
7216 for (y = 0; y < 3; y++)
7217 for (x = 0; x < 3; x++)
7218 putFile16BitBE(file, ei->content.e[x][y]);
7220 putFile32BitBE(file, ei->change->events);
7222 putFile16BitBE(file, ei->change->target_element);
7224 putFile16BitBE(file, ei->change->delay_fixed);
7225 putFile16BitBE(file, ei->change->delay_random);
7226 putFile16BitBE(file, ei->change->delay_frames);
7228 putFile16BitBE(file, ei->change->initial_trigger_element);
7230 putFile8Bit(file, ei->change->explode);
7231 putFile8Bit(file, ei->change->use_target_content);
7232 putFile8Bit(file, ei->change->only_if_complete);
7233 putFile8Bit(file, ei->change->use_random_replace);
7235 putFile8Bit(file, ei->change->random_percentage);
7236 putFile8Bit(file, ei->change->replace_when);
7238 for (y = 0; y < 3; y++)
7239 for (x = 0; x < 3; x++)
7240 putFile16BitBE(file, ei->change->content.e[x][y]);
7242 putFile8Bit(file, ei->slippery_type);
7244 // some free bytes for future properties and padding
7245 WriteUnusedBytesToFile(file, LEVEL_CPART_CUS3_UNUSED);
7252 if (check != num_changed_custom_elements) // should not happen
7253 Warn("inconsistent number of custom element properties");
7257 #if ENABLE_HISTORIC_CHUNKS
7258 static void SaveLevel_CUS4(FILE *file, struct LevelInfo *level, int element)
7260 struct ElementInfo *ei = &element_info[element];
7263 // ---------- custom element base property values (96 bytes) ----------------
7265 putFile16BitBE(file, element);
7267 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
7268 putFile8Bit(file, ei->description[i]);
7270 putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
7272 WriteUnusedBytesToFile(file, 4); // reserved for more base properties
7274 putFile8Bit(file, ei->num_change_pages);
7276 putFile16BitBE(file, ei->ce_value_fixed_initial);
7277 putFile16BitBE(file, ei->ce_value_random_initial);
7278 putFile8Bit(file, ei->use_last_ce_value);
7280 putFile8Bit(file, ei->use_gfx_element);
7281 putFile16BitBE(file, ei->gfx_element_initial);
7283 putFile8Bit(file, ei->collect_score_initial);
7284 putFile8Bit(file, ei->collect_count_initial);
7286 putFile8Bit(file, ei->drop_delay_fixed);
7287 putFile8Bit(file, ei->push_delay_fixed);
7288 putFile8Bit(file, ei->drop_delay_random);
7289 putFile8Bit(file, ei->push_delay_random);
7290 putFile16BitBE(file, ei->move_delay_fixed);
7291 putFile16BitBE(file, ei->move_delay_random);
7293 // bits 0 - 15 of "move_pattern" ...
7294 putFile16BitBE(file, ei->move_pattern & 0xffff);
7295 putFile8Bit(file, ei->move_direction_initial);
7296 putFile8Bit(file, ei->move_stepsize);
7298 putFile8Bit(file, ei->slippery_type);
7300 for (y = 0; y < 3; y++)
7301 for (x = 0; x < 3; x++)
7302 putFile16BitBE(file, ei->content.e[x][y]);
7304 putFile16BitBE(file, ei->move_enter_element);
7305 putFile16BitBE(file, ei->move_leave_element);
7306 putFile8Bit(file, ei->move_leave_type);
7308 // ... bits 16 - 31 of "move_pattern" (not nice, but downward compatible)
7309 putFile16BitBE(file, (ei->move_pattern >> 16) & 0xffff);
7311 putFile8Bit(file, ei->access_direction);
7313 putFile8Bit(file, ei->explosion_delay);
7314 putFile8Bit(file, ei->ignition_delay);
7315 putFile8Bit(file, ei->explosion_type);
7317 // some free bytes for future custom property values and padding
7318 WriteUnusedBytesToFile(file, 1);
7320 // ---------- change page property values (48 bytes) ------------------------
7322 for (i = 0; i < ei->num_change_pages; i++)
7324 struct ElementChangeInfo *change = &ei->change_page[i];
7325 unsigned int event_bits;
7327 // bits 0 - 31 of "has_event[]" ...
7329 for (j = 0; j < MIN(NUM_CHANGE_EVENTS, 32); j++)
7330 if (change->has_event[j])
7331 event_bits |= (1 << j);
7332 putFile32BitBE(file, event_bits);
7334 putFile16BitBE(file, change->target_element);
7336 putFile16BitBE(file, change->delay_fixed);
7337 putFile16BitBE(file, change->delay_random);
7338 putFile16BitBE(file, change->delay_frames);
7340 putFile16BitBE(file, change->initial_trigger_element);
7342 putFile8Bit(file, change->explode);
7343 putFile8Bit(file, change->use_target_content);
7344 putFile8Bit(file, change->only_if_complete);
7345 putFile8Bit(file, change->use_random_replace);
7347 putFile8Bit(file, change->random_percentage);
7348 putFile8Bit(file, change->replace_when);
7350 for (y = 0; y < 3; y++)
7351 for (x = 0; x < 3; x++)
7352 putFile16BitBE(file, change->target_content.e[x][y]);
7354 putFile8Bit(file, change->can_change);
7356 putFile8Bit(file, change->trigger_side);
7358 putFile8Bit(file, change->trigger_player);
7359 putFile8Bit(file, (change->trigger_page == CH_PAGE_ANY ? CH_PAGE_ANY_FILE :
7360 log_2(change->trigger_page)));
7362 putFile8Bit(file, change->has_action);
7363 putFile8Bit(file, change->action_type);
7364 putFile8Bit(file, change->action_mode);
7365 putFile16BitBE(file, change->action_arg);
7367 // ... bits 32 - 39 of "has_event[]" (not nice, but downward compatible)
7369 for (j = 32; j < NUM_CHANGE_EVENTS; j++)
7370 if (change->has_event[j])
7371 event_bits |= (1 << (j - 32));
7372 putFile8Bit(file, event_bits);
7377 #if ENABLE_HISTORIC_CHUNKS
7378 static void SaveLevel_GRP1(FILE *file, struct LevelInfo *level, int element)
7380 struct ElementInfo *ei = &element_info[element];
7381 struct ElementGroupInfo *group = ei->group;
7384 putFile16BitBE(file, element);
7386 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
7387 putFile8Bit(file, ei->description[i]);
7389 putFile8Bit(file, group->num_elements);
7391 putFile8Bit(file, ei->use_gfx_element);
7392 putFile16BitBE(file, ei->gfx_element_initial);
7394 putFile8Bit(file, group->choice_mode);
7396 // some free bytes for future values and padding
7397 WriteUnusedBytesToFile(file, 3);
7399 for (i = 0; i < MAX_ELEMENTS_IN_GROUP; i++)
7400 putFile16BitBE(file, group->element[i]);
7404 static int SaveLevel_MicroChunk(FILE *file, struct LevelFileConfigInfo *entry,
7405 boolean write_element)
7407 int save_type = entry->save_type;
7408 int data_type = entry->data_type;
7409 int conf_type = entry->conf_type;
7410 int byte_mask = conf_type & CONF_MASK_BYTES;
7411 int element = entry->element;
7412 int default_value = entry->default_value;
7414 boolean modified = FALSE;
7416 if (byte_mask != CONF_MASK_MULTI_BYTES)
7418 void *value_ptr = entry->value;
7419 int value = (data_type == TYPE_BOOLEAN ? *(boolean *)value_ptr :
7422 // check if any settings have been modified before saving them
7423 if (value != default_value)
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 += (byte_mask == CONF_MASK_1_BYTE ? putFile8Bit (file, value) :
7436 byte_mask == CONF_MASK_2_BYTE ? putFile16BitBE(file, value) :
7437 byte_mask == CONF_MASK_4_BYTE ? putFile32BitBE(file, value) :
7440 else if (data_type == TYPE_STRING)
7442 char *default_string = entry->default_string;
7443 char *string = (char *)(entry->value);
7444 int string_length = strlen(string);
7447 // check if any settings have been modified before saving them
7448 if (!strEqual(string, default_string))
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, string_length);
7462 for (i = 0; i < string_length; i++)
7463 num_bytes += putFile8Bit(file, string[i]);
7465 else if (data_type == TYPE_ELEMENT_LIST)
7467 int *element_array = (int *)(entry->value);
7468 int num_elements = *(int *)(entry->num_entities);
7471 // check if any settings have been modified before saving them
7472 for (i = 0; i < num_elements; i++)
7473 if (element_array[i] != default_value)
7476 // do not save if explicitly told or if unmodified default settings
7477 if ((save_type == SAVE_CONF_NEVER) ||
7478 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
7482 num_bytes += putFile16BitBE(file, element);
7484 num_bytes += putFile8Bit(file, conf_type);
7485 num_bytes += putFile16BitBE(file, num_elements * CONF_ELEMENT_NUM_BYTES);
7487 for (i = 0; i < num_elements; i++)
7488 num_bytes += putFile16BitBE(file, element_array[i]);
7490 else if (data_type == TYPE_CONTENT_LIST)
7492 struct Content *content = (struct Content *)(entry->value);
7493 int num_contents = *(int *)(entry->num_entities);
7496 // check if any settings have been modified before saving them
7497 for (i = 0; i < num_contents; i++)
7498 for (y = 0; y < 3; y++)
7499 for (x = 0; x < 3; x++)
7500 if (content[i].e[x][y] != default_value)
7503 // do not save if explicitly told or if unmodified default settings
7504 if ((save_type == SAVE_CONF_NEVER) ||
7505 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
7509 num_bytes += putFile16BitBE(file, element);
7511 num_bytes += putFile8Bit(file, conf_type);
7512 num_bytes += putFile16BitBE(file, num_contents * CONF_CONTENT_NUM_BYTES);
7514 for (i = 0; i < num_contents; i++)
7515 for (y = 0; y < 3; y++)
7516 for (x = 0; x < 3; x++)
7517 num_bytes += putFile16BitBE(file, content[i].e[x][y]);
7523 static int SaveLevel_INFO(FILE *file, struct LevelInfo *level)
7528 li = *level; // copy level data into temporary buffer
7530 for (i = 0; chunk_config_INFO[i].data_type != -1; i++)
7531 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_INFO[i], FALSE);
7536 static int SaveLevel_ELEM(FILE *file, struct LevelInfo *level)
7541 li = *level; // copy level data into temporary buffer
7543 for (i = 0; chunk_config_ELEM[i].data_type != -1; i++)
7544 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_ELEM[i], TRUE);
7549 static int SaveLevel_NOTE(FILE *file, struct LevelInfo *level, int element)
7551 int envelope_nr = element - EL_ENVELOPE_1;
7555 chunk_size += putFile16BitBE(file, element);
7557 // copy envelope data into temporary buffer
7558 xx_envelope = level->envelope[envelope_nr];
7560 for (i = 0; chunk_config_NOTE[i].data_type != -1; i++)
7561 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_NOTE[i], FALSE);
7566 static int SaveLevel_CUSX(FILE *file, struct LevelInfo *level, int element)
7568 struct ElementInfo *ei = &element_info[element];
7572 chunk_size += putFile16BitBE(file, element);
7574 xx_ei = *ei; // copy element data into temporary buffer
7576 // set default description string for this specific element
7577 strcpy(xx_default_description, getDefaultElementDescription(ei));
7579 for (i = 0; chunk_config_CUSX_base[i].data_type != -1; i++)
7580 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_CUSX_base[i], FALSE);
7582 for (i = 0; i < ei->num_change_pages; i++)
7584 struct ElementChangeInfo *change = &ei->change_page[i];
7586 xx_current_change_page = i;
7588 xx_change = *change; // copy change data into temporary buffer
7591 setEventBitsFromEventFlags(change);
7593 for (j = 0; chunk_config_CUSX_change[j].data_type != -1; j++)
7594 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_CUSX_change[j],
7601 static int SaveLevel_GRPX(FILE *file, struct LevelInfo *level, int element)
7603 struct ElementInfo *ei = &element_info[element];
7604 struct ElementGroupInfo *group = ei->group;
7608 chunk_size += putFile16BitBE(file, element);
7610 xx_ei = *ei; // copy element data into temporary buffer
7611 xx_group = *group; // copy group data into temporary buffer
7613 // set default description string for this specific element
7614 strcpy(xx_default_description, getDefaultElementDescription(ei));
7616 for (i = 0; chunk_config_GRPX[i].data_type != -1; i++)
7617 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_GRPX[i], FALSE);
7622 static int SaveLevel_EMPX(FILE *file, struct LevelInfo *level, int element)
7624 struct ElementInfo *ei = &element_info[element];
7628 chunk_size += putFile16BitBE(file, element);
7630 xx_ei = *ei; // copy element data into temporary buffer
7632 for (i = 0; chunk_config_EMPX[i].data_type != -1; i++)
7633 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_EMPX[i], FALSE);
7638 static void SaveLevelFromFilename(struct LevelInfo *level, char *filename,
7639 boolean save_as_template)
7645 if (!(file = fopen(filename, MODE_WRITE)))
7647 Warn("cannot save level file '%s'", filename);
7652 level->file_version = FILE_VERSION_ACTUAL;
7653 level->game_version = GAME_VERSION_ACTUAL;
7655 level->creation_date = getCurrentDate();
7657 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
7658 putFileChunkBE(file, "CAVE", CHUNK_SIZE_NONE);
7660 chunk_size = SaveLevel_VERS(NULL, level);
7661 putFileChunkBE(file, "VERS", chunk_size);
7662 SaveLevel_VERS(file, level);
7664 chunk_size = SaveLevel_DATE(NULL, level);
7665 putFileChunkBE(file, "DATE", chunk_size);
7666 SaveLevel_DATE(file, level);
7668 chunk_size = SaveLevel_NAME(NULL, level);
7669 putFileChunkBE(file, "NAME", chunk_size);
7670 SaveLevel_NAME(file, level);
7672 chunk_size = SaveLevel_AUTH(NULL, level);
7673 putFileChunkBE(file, "AUTH", chunk_size);
7674 SaveLevel_AUTH(file, level);
7676 chunk_size = SaveLevel_INFO(NULL, level);
7677 putFileChunkBE(file, "INFO", chunk_size);
7678 SaveLevel_INFO(file, level);
7680 chunk_size = SaveLevel_BODY(NULL, level);
7681 putFileChunkBE(file, "BODY", chunk_size);
7682 SaveLevel_BODY(file, level);
7684 chunk_size = SaveLevel_ELEM(NULL, level);
7685 if (chunk_size > LEVEL_CHUNK_ELEM_UNCHANGED) // save if changed
7687 putFileChunkBE(file, "ELEM", chunk_size);
7688 SaveLevel_ELEM(file, level);
7691 for (i = 0; i < NUM_ENVELOPES; i++)
7693 int element = EL_ENVELOPE_1 + i;
7695 chunk_size = SaveLevel_NOTE(NULL, level, element);
7696 if (chunk_size > LEVEL_CHUNK_NOTE_UNCHANGED) // save if changed
7698 putFileChunkBE(file, "NOTE", chunk_size);
7699 SaveLevel_NOTE(file, level, element);
7703 // if not using template level, check for non-default custom/group elements
7704 if (!level->use_custom_template || save_as_template)
7706 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7708 int element = EL_CUSTOM_START + i;
7710 chunk_size = SaveLevel_CUSX(NULL, level, element);
7711 if (chunk_size > LEVEL_CHUNK_CUSX_UNCHANGED) // save if changed
7713 putFileChunkBE(file, "CUSX", chunk_size);
7714 SaveLevel_CUSX(file, level, element);
7718 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
7720 int element = EL_GROUP_START + i;
7722 chunk_size = SaveLevel_GRPX(NULL, level, element);
7723 if (chunk_size > LEVEL_CHUNK_GRPX_UNCHANGED) // save if changed
7725 putFileChunkBE(file, "GRPX", chunk_size);
7726 SaveLevel_GRPX(file, level, element);
7730 for (i = 0; i < NUM_EMPTY_ELEMENTS_ALL; i++)
7732 int element = GET_EMPTY_ELEMENT(i);
7734 chunk_size = SaveLevel_EMPX(NULL, level, element);
7735 if (chunk_size > LEVEL_CHUNK_EMPX_UNCHANGED) // save if changed
7737 putFileChunkBE(file, "EMPX", chunk_size);
7738 SaveLevel_EMPX(file, level, element);
7745 SetFilePermissions(filename, PERMS_PRIVATE);
7748 void SaveLevel(int nr)
7750 char *filename = getDefaultLevelFilename(nr);
7752 SaveLevelFromFilename(&level, filename, FALSE);
7755 void SaveLevelTemplate(void)
7757 char *filename = getLocalLevelTemplateFilename();
7759 SaveLevelFromFilename(&level, filename, TRUE);
7762 boolean SaveLevelChecked(int nr)
7764 char *filename = getDefaultLevelFilename(nr);
7765 boolean new_level = !fileExists(filename);
7766 boolean level_saved = FALSE;
7768 if (new_level || Request("Save this level and kill the old?", REQ_ASK))
7773 Request("Level saved!", REQ_CONFIRM);
7781 void DumpLevel(struct LevelInfo *level)
7783 if (level->no_level_file || level->no_valid_file)
7785 Warn("cannot dump -- no valid level file found");
7791 Print("Level xxx (file version %08d, game version %08d)\n",
7792 level->file_version, level->game_version);
7795 Print("Level author: '%s'\n", level->author);
7796 Print("Level title: '%s'\n", level->name);
7798 Print("Playfield size: %d x %d\n", level->fieldx, level->fieldy);
7800 Print("Level time: %d seconds\n", level->time);
7801 Print("Gems needed: %d\n", level->gems_needed);
7803 Print("Time for magic wall: %d seconds\n", level->time_magic_wall);
7804 Print("Time for wheel: %d seconds\n", level->time_wheel);
7805 Print("Time for light: %d seconds\n", level->time_light);
7806 Print("Time for timegate: %d seconds\n", level->time_timegate);
7808 Print("Amoeba speed: %d\n", level->amoeba_speed);
7811 Print("EM style slippery gems: %s\n", (level->em_slippery_gems ? "yes" : "no"));
7812 Print("Player blocks last field: %s\n", (level->block_last_field ? "yes" : "no"));
7813 Print("SP player blocks last field: %s\n", (level->sp_block_last_field ? "yes" : "no"));
7814 Print("use spring bug: %s\n", (level->use_spring_bug ? "yes" : "no"));
7815 Print("use step counter: %s\n", (level->use_step_counter ? "yes" : "no"));
7816 Print("rate time over score: %s\n", (level->rate_time_over_score ? "yes" : "no"));
7822 for (i = 0; i < NUM_ENVELOPES; i++)
7824 char *text = level->envelope[i].text;
7825 int text_len = strlen(text);
7826 boolean has_text = FALSE;
7828 for (j = 0; j < text_len; j++)
7829 if (text[j] != ' ' && text[j] != '\n')
7835 Print("Envelope %d:\n'%s'\n", i + 1, text);
7843 void DumpLevels(void)
7845 static LevelDirTree *dumplevel_leveldir = NULL;
7847 dumplevel_leveldir = getTreeInfoFromIdentifier(leveldir_first,
7848 global.dumplevel_leveldir);
7850 if (dumplevel_leveldir == NULL)
7851 Fail("no such level identifier: '%s'", global.dumplevel_leveldir);
7853 if (global.dumplevel_level_nr < dumplevel_leveldir->first_level ||
7854 global.dumplevel_level_nr > dumplevel_leveldir->last_level)
7855 Fail("no such level number: %d", global.dumplevel_level_nr);
7857 leveldir_current = dumplevel_leveldir;
7859 LoadLevel(global.dumplevel_level_nr);
7866 // ============================================================================
7867 // tape file functions
7868 // ============================================================================
7870 static void setTapeInfoToDefaults(void)
7874 // always start with reliable default values (empty tape)
7877 // default values (also for pre-1.2 tapes) with only the first player
7878 tape.player_participates[0] = TRUE;
7879 for (i = 1; i < MAX_PLAYERS; i++)
7880 tape.player_participates[i] = FALSE;
7882 // at least one (default: the first) player participates in every tape
7883 tape.num_participating_players = 1;
7885 tape.property_bits = TAPE_PROPERTY_NONE;
7887 tape.level_nr = level_nr;
7889 tape.changed = FALSE;
7891 tape.recording = FALSE;
7892 tape.playing = FALSE;
7893 tape.pausing = FALSE;
7895 tape.scr_fieldx = SCR_FIELDX_DEFAULT;
7896 tape.scr_fieldy = SCR_FIELDY_DEFAULT;
7898 tape.no_info_chunk = TRUE;
7899 tape.no_valid_file = FALSE;
7902 static int getTapePosSize(struct TapeInfo *tape)
7904 int tape_pos_size = 0;
7906 if (tape->use_key_actions)
7907 tape_pos_size += tape->num_participating_players;
7909 if (tape->use_mouse_actions)
7910 tape_pos_size += 3; // x and y position and mouse button mask
7912 tape_pos_size += 1; // tape action delay value
7914 return tape_pos_size;
7917 static void setTapeActionFlags(struct TapeInfo *tape, int value)
7919 tape->use_key_actions = FALSE;
7920 tape->use_mouse_actions = FALSE;
7922 if (value != TAPE_USE_MOUSE_ACTIONS_ONLY)
7923 tape->use_key_actions = TRUE;
7925 if (value != TAPE_USE_KEY_ACTIONS_ONLY)
7926 tape->use_mouse_actions = TRUE;
7929 static int getTapeActionValue(struct TapeInfo *tape)
7931 return (tape->use_key_actions &&
7932 tape->use_mouse_actions ? TAPE_USE_KEY_AND_MOUSE_ACTIONS :
7933 tape->use_key_actions ? TAPE_USE_KEY_ACTIONS_ONLY :
7934 tape->use_mouse_actions ? TAPE_USE_MOUSE_ACTIONS_ONLY :
7935 TAPE_ACTIONS_DEFAULT);
7938 static int LoadTape_VERS(File *file, int chunk_size, struct TapeInfo *tape)
7940 tape->file_version = getFileVersion(file);
7941 tape->game_version = getFileVersion(file);
7946 static int LoadTape_HEAD(File *file, int chunk_size, struct TapeInfo *tape)
7950 tape->random_seed = getFile32BitBE(file);
7951 tape->date = getFile32BitBE(file);
7952 tape->length = getFile32BitBE(file);
7954 // read header fields that are new since version 1.2
7955 if (tape->file_version >= FILE_VERSION_1_2)
7957 byte store_participating_players = getFile8Bit(file);
7960 // since version 1.2, tapes store which players participate in the tape
7961 tape->num_participating_players = 0;
7962 for (i = 0; i < MAX_PLAYERS; i++)
7964 tape->player_participates[i] = FALSE;
7966 if (store_participating_players & (1 << i))
7968 tape->player_participates[i] = TRUE;
7969 tape->num_participating_players++;
7973 setTapeActionFlags(tape, getFile8Bit(file));
7975 tape->property_bits = getFile8Bit(file);
7977 ReadUnusedBytesFromFile(file, TAPE_CHUNK_HEAD_UNUSED);
7979 engine_version = getFileVersion(file);
7980 if (engine_version > 0)
7981 tape->engine_version = engine_version;
7983 tape->engine_version = tape->game_version;
7989 static int LoadTape_SCRN(File *file, int chunk_size, struct TapeInfo *tape)
7991 tape->scr_fieldx = getFile8Bit(file);
7992 tape->scr_fieldy = getFile8Bit(file);
7997 static int LoadTape_INFO(File *file, int chunk_size, struct TapeInfo *tape)
7999 char *level_identifier = NULL;
8000 int level_identifier_size;
8003 tape->no_info_chunk = FALSE;
8005 level_identifier_size = getFile16BitBE(file);
8007 level_identifier = checked_malloc(level_identifier_size);
8009 for (i = 0; i < level_identifier_size; i++)
8010 level_identifier[i] = getFile8Bit(file);
8012 strncpy(tape->level_identifier, level_identifier, MAX_FILENAME_LEN);
8013 tape->level_identifier[MAX_FILENAME_LEN] = '\0';
8015 checked_free(level_identifier);
8017 tape->level_nr = getFile16BitBE(file);
8019 chunk_size = 2 + level_identifier_size + 2;
8024 static int LoadTape_BODY(File *file, int chunk_size, struct TapeInfo *tape)
8027 int tape_pos_size = getTapePosSize(tape);
8028 int chunk_size_expected = tape_pos_size * tape->length;
8030 if (chunk_size_expected != chunk_size)
8032 ReadUnusedBytesFromFile(file, chunk_size);
8033 return chunk_size_expected;
8036 for (i = 0; i < tape->length; i++)
8038 if (i >= MAX_TAPE_LEN)
8040 Warn("tape truncated -- size exceeds maximum tape size %d",
8043 // tape too large; read and ignore remaining tape data from this chunk
8044 for (;i < tape->length; i++)
8045 ReadUnusedBytesFromFile(file, tape_pos_size);
8050 if (tape->use_key_actions)
8052 for (j = 0; j < MAX_PLAYERS; j++)
8054 tape->pos[i].action[j] = MV_NONE;
8056 if (tape->player_participates[j])
8057 tape->pos[i].action[j] = getFile8Bit(file);
8061 if (tape->use_mouse_actions)
8063 tape->pos[i].action[TAPE_ACTION_LX] = getFile8Bit(file);
8064 tape->pos[i].action[TAPE_ACTION_LY] = getFile8Bit(file);
8065 tape->pos[i].action[TAPE_ACTION_BUTTON] = getFile8Bit(file);
8068 tape->pos[i].delay = getFile8Bit(file);
8070 if (tape->file_version == FILE_VERSION_1_0)
8072 // eliminate possible diagonal moves in old tapes
8073 // this is only for backward compatibility
8075 byte joy_dir[4] = { JOY_LEFT, JOY_RIGHT, JOY_UP, JOY_DOWN };
8076 byte action = tape->pos[i].action[0];
8077 int k, num_moves = 0;
8079 for (k = 0; k<4; k++)
8081 if (action & joy_dir[k])
8083 tape->pos[i + num_moves].action[0] = joy_dir[k];
8085 tape->pos[i + num_moves].delay = 0;
8094 tape->length += num_moves;
8097 else if (tape->file_version < FILE_VERSION_2_0)
8099 // convert pre-2.0 tapes to new tape format
8101 if (tape->pos[i].delay > 1)
8104 tape->pos[i + 1] = tape->pos[i];
8105 tape->pos[i + 1].delay = 1;
8108 for (j = 0; j < MAX_PLAYERS; j++)
8109 tape->pos[i].action[j] = MV_NONE;
8110 tape->pos[i].delay--;
8117 if (checkEndOfFile(file))
8121 if (i != tape->length)
8122 chunk_size = tape_pos_size * i;
8127 static void LoadTape_SokobanSolution(char *filename)
8130 int move_delay = TILESIZE / level.initial_player_stepsize[0];
8132 if (!(file = openFile(filename, MODE_READ)))
8134 tape.no_valid_file = TRUE;
8139 while (!checkEndOfFile(file))
8141 unsigned char c = getByteFromFile(file);
8143 if (checkEndOfFile(file))
8150 tape.pos[tape.length].action[0] = MV_UP;
8151 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
8157 tape.pos[tape.length].action[0] = MV_DOWN;
8158 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
8164 tape.pos[tape.length].action[0] = MV_LEFT;
8165 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
8171 tape.pos[tape.length].action[0] = MV_RIGHT;
8172 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
8180 // ignore white-space characters
8184 tape.no_valid_file = TRUE;
8186 Warn("unsupported Sokoban solution file '%s' ['%d']", filename, c);
8194 if (tape.no_valid_file)
8197 tape.length_frames = GetTapeLengthFrames();
8198 tape.length_seconds = GetTapeLengthSeconds();
8201 void LoadTapeFromFilename(char *filename)
8203 char cookie[MAX_LINE_LEN];
8204 char chunk_name[CHUNK_ID_LEN + 1];
8208 // always start with reliable default values
8209 setTapeInfoToDefaults();
8211 if (strSuffix(filename, ".sln"))
8213 LoadTape_SokobanSolution(filename);
8218 if (!(file = openFile(filename, MODE_READ)))
8220 tape.no_valid_file = TRUE;
8225 getFileChunkBE(file, chunk_name, NULL);
8226 if (strEqual(chunk_name, "RND1"))
8228 getFile32BitBE(file); // not used
8230 getFileChunkBE(file, chunk_name, NULL);
8231 if (!strEqual(chunk_name, "TAPE"))
8233 tape.no_valid_file = TRUE;
8235 Warn("unknown format of tape file '%s'", filename);
8242 else // check for pre-2.0 file format with cookie string
8244 strcpy(cookie, chunk_name);
8245 if (getStringFromFile(file, &cookie[4], MAX_LINE_LEN - 4) == NULL)
8247 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
8248 cookie[strlen(cookie) - 1] = '\0';
8250 if (!checkCookieString(cookie, TAPE_COOKIE_TMPL))
8252 tape.no_valid_file = TRUE;
8254 Warn("unknown format of tape file '%s'", filename);
8261 if ((tape.file_version = getFileVersionFromCookieString(cookie)) == -1)
8263 tape.no_valid_file = TRUE;
8265 Warn("unsupported version of tape file '%s'", filename);
8272 // pre-2.0 tape files have no game version, so use file version here
8273 tape.game_version = tape.file_version;
8276 if (tape.file_version < FILE_VERSION_1_2)
8278 // tape files from versions before 1.2.0 without chunk structure
8279 LoadTape_HEAD(file, TAPE_CHUNK_HEAD_SIZE, &tape);
8280 LoadTape_BODY(file, 2 * tape.length, &tape);
8288 int (*loader)(File *, int, struct TapeInfo *);
8292 { "VERS", TAPE_CHUNK_VERS_SIZE, LoadTape_VERS },
8293 { "HEAD", TAPE_CHUNK_HEAD_SIZE, LoadTape_HEAD },
8294 { "SCRN", TAPE_CHUNK_SCRN_SIZE, LoadTape_SCRN },
8295 { "INFO", -1, LoadTape_INFO },
8296 { "BODY", -1, LoadTape_BODY },
8300 while (getFileChunkBE(file, chunk_name, &chunk_size))
8304 while (chunk_info[i].name != NULL &&
8305 !strEqual(chunk_name, chunk_info[i].name))
8308 if (chunk_info[i].name == NULL)
8310 Warn("unknown chunk '%s' in tape file '%s'",
8311 chunk_name, filename);
8313 ReadUnusedBytesFromFile(file, chunk_size);
8315 else if (chunk_info[i].size != -1 &&
8316 chunk_info[i].size != chunk_size)
8318 Warn("wrong size (%d) of chunk '%s' in tape file '%s'",
8319 chunk_size, chunk_name, filename);
8321 ReadUnusedBytesFromFile(file, chunk_size);
8325 // call function to load this tape chunk
8326 int chunk_size_expected =
8327 (chunk_info[i].loader)(file, chunk_size, &tape);
8329 // the size of some chunks cannot be checked before reading other
8330 // chunks first (like "HEAD" and "BODY") that contain some header
8331 // information, so check them here
8332 if (chunk_size_expected != chunk_size)
8334 Warn("wrong size (%d) of chunk '%s' in tape file '%s'",
8335 chunk_size, chunk_name, filename);
8343 tape.length_frames = GetTapeLengthFrames();
8344 tape.length_seconds = GetTapeLengthSeconds();
8347 Debug("files:LoadTapeFromFilename", "tape file version: %d",
8349 Debug("files:LoadTapeFromFilename", "tape game version: %d",
8351 Debug("files:LoadTapeFromFilename", "tape engine version: %d",
8352 tape.engine_version);
8356 void LoadTape(int nr)
8358 char *filename = getTapeFilename(nr);
8360 LoadTapeFromFilename(filename);
8363 void LoadSolutionTape(int nr)
8365 char *filename = getSolutionTapeFilename(nr);
8367 LoadTapeFromFilename(filename);
8369 if (TAPE_IS_EMPTY(tape) &&
8370 level.game_engine_type == GAME_ENGINE_TYPE_SP &&
8371 level.native_sp_level->demo.is_available)
8372 CopyNativeTape_SP_to_RND(&level);
8375 void LoadScoreTape(char *score_tape_basename, int nr)
8377 char *filename = getScoreTapeFilename(score_tape_basename, nr);
8379 LoadTapeFromFilename(filename);
8382 void LoadScoreCacheTape(char *score_tape_basename, int nr)
8384 char *filename = getScoreCacheTapeFilename(score_tape_basename, nr);
8386 LoadTapeFromFilename(filename);
8389 static boolean checkSaveTape_SCRN(struct TapeInfo *tape)
8391 // chunk required for team mode tapes with non-default screen size
8392 return (tape->num_participating_players > 1 &&
8393 (tape->scr_fieldx != SCR_FIELDX_DEFAULT ||
8394 tape->scr_fieldy != SCR_FIELDY_DEFAULT));
8397 static void SaveTape_VERS(FILE *file, struct TapeInfo *tape)
8399 putFileVersion(file, tape->file_version);
8400 putFileVersion(file, tape->game_version);
8403 static void SaveTape_HEAD(FILE *file, struct TapeInfo *tape)
8406 byte store_participating_players = 0;
8408 // set bits for participating players for compact storage
8409 for (i = 0; i < MAX_PLAYERS; i++)
8410 if (tape->player_participates[i])
8411 store_participating_players |= (1 << i);
8413 putFile32BitBE(file, tape->random_seed);
8414 putFile32BitBE(file, tape->date);
8415 putFile32BitBE(file, tape->length);
8417 putFile8Bit(file, store_participating_players);
8419 putFile8Bit(file, getTapeActionValue(tape));
8421 putFile8Bit(file, tape->property_bits);
8423 // unused bytes not at the end here for 4-byte alignment of engine_version
8424 WriteUnusedBytesToFile(file, TAPE_CHUNK_HEAD_UNUSED);
8426 putFileVersion(file, tape->engine_version);
8429 static void SaveTape_SCRN(FILE *file, struct TapeInfo *tape)
8431 putFile8Bit(file, tape->scr_fieldx);
8432 putFile8Bit(file, tape->scr_fieldy);
8435 static void SaveTape_INFO(FILE *file, struct TapeInfo *tape)
8437 int level_identifier_size = strlen(tape->level_identifier) + 1;
8440 putFile16BitBE(file, level_identifier_size);
8442 for (i = 0; i < level_identifier_size; i++)
8443 putFile8Bit(file, tape->level_identifier[i]);
8445 putFile16BitBE(file, tape->level_nr);
8448 static void SaveTape_BODY(FILE *file, struct TapeInfo *tape)
8452 for (i = 0; i < tape->length; i++)
8454 if (tape->use_key_actions)
8456 for (j = 0; j < MAX_PLAYERS; j++)
8457 if (tape->player_participates[j])
8458 putFile8Bit(file, tape->pos[i].action[j]);
8461 if (tape->use_mouse_actions)
8463 putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_LX]);
8464 putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_LY]);
8465 putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_BUTTON]);
8468 putFile8Bit(file, tape->pos[i].delay);
8472 void SaveTapeToFilename(char *filename)
8476 int info_chunk_size;
8477 int body_chunk_size;
8479 if (!(file = fopen(filename, MODE_WRITE)))
8481 Warn("cannot save level recording file '%s'", filename);
8486 tape_pos_size = getTapePosSize(&tape);
8488 info_chunk_size = 2 + (strlen(tape.level_identifier) + 1) + 2;
8489 body_chunk_size = tape_pos_size * tape.length;
8491 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
8492 putFileChunkBE(file, "TAPE", CHUNK_SIZE_NONE);
8494 putFileChunkBE(file, "VERS", TAPE_CHUNK_VERS_SIZE);
8495 SaveTape_VERS(file, &tape);
8497 putFileChunkBE(file, "HEAD", TAPE_CHUNK_HEAD_SIZE);
8498 SaveTape_HEAD(file, &tape);
8500 if (checkSaveTape_SCRN(&tape))
8502 putFileChunkBE(file, "SCRN", TAPE_CHUNK_SCRN_SIZE);
8503 SaveTape_SCRN(file, &tape);
8506 putFileChunkBE(file, "INFO", info_chunk_size);
8507 SaveTape_INFO(file, &tape);
8509 putFileChunkBE(file, "BODY", body_chunk_size);
8510 SaveTape_BODY(file, &tape);
8514 SetFilePermissions(filename, PERMS_PRIVATE);
8517 static void SaveTapeExt(char *filename)
8521 tape.file_version = FILE_VERSION_ACTUAL;
8522 tape.game_version = GAME_VERSION_ACTUAL;
8524 tape.num_participating_players = 0;
8526 // count number of participating players
8527 for (i = 0; i < MAX_PLAYERS; i++)
8528 if (tape.player_participates[i])
8529 tape.num_participating_players++;
8531 SaveTapeToFilename(filename);
8533 tape.changed = FALSE;
8536 void SaveTape(int nr)
8538 char *filename = getTapeFilename(nr);
8540 InitTapeDirectory(leveldir_current->subdir);
8542 SaveTapeExt(filename);
8545 void SaveScoreTape(int nr)
8547 char *filename = getScoreTapeFilename(tape.score_tape_basename, nr);
8549 // used instead of "leveldir_current->subdir" (for network games)
8550 InitScoreTapeDirectory(levelset.identifier, nr);
8552 SaveTapeExt(filename);
8555 static boolean SaveTapeCheckedExt(int nr, char *msg_replace, char *msg_saved,
8556 unsigned int req_state_added)
8558 char *filename = getTapeFilename(nr);
8559 boolean new_tape = !fileExists(filename);
8560 boolean tape_saved = FALSE;
8562 if (new_tape || Request(msg_replace, REQ_ASK | req_state_added))
8567 Request(msg_saved, REQ_CONFIRM | req_state_added);
8575 boolean SaveTapeChecked(int nr)
8577 return SaveTapeCheckedExt(nr, "Replace old tape?", "Tape saved!", 0);
8580 boolean SaveTapeChecked_LevelSolved(int nr)
8582 return SaveTapeCheckedExt(nr, "Level solved! Replace old tape?",
8583 "Level solved! Tape saved!", REQ_STAY_OPEN);
8586 void DumpTape(struct TapeInfo *tape)
8588 int tape_frame_counter;
8591 if (tape->no_valid_file)
8593 Warn("cannot dump -- no valid tape file found");
8600 Print("Tape of Level %03d (file version %08d, game version %08d)\n",
8601 tape->level_nr, tape->file_version, tape->game_version);
8602 Print(" (effective engine version %08d)\n",
8603 tape->engine_version);
8604 Print("Level series identifier: '%s'\n", tape->level_identifier);
8606 Print("Special tape properties: ");
8607 if (tape->property_bits == TAPE_PROPERTY_NONE)
8609 if (tape->property_bits & TAPE_PROPERTY_EM_RANDOM_BUG)
8610 Print("[em_random_bug]");
8611 if (tape->property_bits & TAPE_PROPERTY_GAME_SPEED)
8612 Print("[game_speed]");
8613 if (tape->property_bits & TAPE_PROPERTY_PAUSE_MODE)
8615 if (tape->property_bits & TAPE_PROPERTY_SINGLE_STEP)
8616 Print("[single_step]");
8617 if (tape->property_bits & TAPE_PROPERTY_SNAPSHOT)
8618 Print("[snapshot]");
8619 if (tape->property_bits & TAPE_PROPERTY_REPLAYED)
8620 Print("[replayed]");
8621 if (tape->property_bits & TAPE_PROPERTY_TAS_KEYS)
8622 Print("[tas_keys]");
8623 if (tape->property_bits & TAPE_PROPERTY_SMALL_GRAPHICS)
8624 Print("[small_graphics]");
8627 int year2 = tape->date / 10000;
8628 int year4 = (year2 < 70 ? 2000 + year2 : 1900 + year2);
8629 int month_index_raw = (tape->date / 100) % 100;
8630 int month_index = month_index_raw % 12; // prevent invalid index
8631 int month = month_index + 1;
8632 int day = tape->date % 100;
8634 Print("Tape date: %04d-%02d-%02d\n", year4, month, day);
8638 tape_frame_counter = 0;
8640 for (i = 0; i < tape->length; i++)
8642 if (i >= MAX_TAPE_LEN)
8647 for (j = 0; j < MAX_PLAYERS; j++)
8649 if (tape->player_participates[j])
8651 int action = tape->pos[i].action[j];
8653 Print("%d:%02x ", j, action);
8654 Print("[%c%c%c%c|%c%c] - ",
8655 (action & JOY_LEFT ? '<' : ' '),
8656 (action & JOY_RIGHT ? '>' : ' '),
8657 (action & JOY_UP ? '^' : ' '),
8658 (action & JOY_DOWN ? 'v' : ' '),
8659 (action & JOY_BUTTON_1 ? '1' : ' '),
8660 (action & JOY_BUTTON_2 ? '2' : ' '));
8664 Print("(%03d) ", tape->pos[i].delay);
8665 Print("[%05d]\n", tape_frame_counter);
8667 tape_frame_counter += tape->pos[i].delay;
8673 void DumpTapes(void)
8675 static LevelDirTree *dumptape_leveldir = NULL;
8677 dumptape_leveldir = getTreeInfoFromIdentifier(leveldir_first,
8678 global.dumptape_leveldir);
8680 if (dumptape_leveldir == NULL)
8681 Fail("no such level identifier: '%s'", global.dumptape_leveldir);
8683 if (global.dumptape_level_nr < dumptape_leveldir->first_level ||
8684 global.dumptape_level_nr > dumptape_leveldir->last_level)
8685 Fail("no such level number: %d", global.dumptape_level_nr);
8687 leveldir_current = dumptape_leveldir;
8689 if (options.mytapes)
8690 LoadTape(global.dumptape_level_nr);
8692 LoadSolutionTape(global.dumptape_level_nr);
8700 // ============================================================================
8701 // score file functions
8702 // ============================================================================
8704 static void setScoreInfoToDefaultsExt(struct ScoreInfo *scores)
8708 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
8710 strcpy(scores->entry[i].tape_basename, UNDEFINED_FILENAME);
8711 strcpy(scores->entry[i].name, EMPTY_PLAYER_NAME);
8712 scores->entry[i].score = 0;
8713 scores->entry[i].time = 0;
8715 scores->entry[i].id = -1;
8716 strcpy(scores->entry[i].tape_date, UNKNOWN_NAME);
8717 strcpy(scores->entry[i].platform, UNKNOWN_NAME);
8718 strcpy(scores->entry[i].version, UNKNOWN_NAME);
8719 strcpy(scores->entry[i].country_name, UNKNOWN_NAME);
8720 strcpy(scores->entry[i].country_code, "??");
8723 scores->num_entries = 0;
8724 scores->last_added = -1;
8725 scores->last_added_local = -1;
8727 scores->updated = FALSE;
8728 scores->uploaded = FALSE;
8729 scores->tape_downloaded = FALSE;
8730 scores->force_last_added = FALSE;
8732 // The following values are intentionally not reset here:
8736 // - continue_playing
8737 // - continue_on_return
8740 static void setScoreInfoToDefaults(void)
8742 setScoreInfoToDefaultsExt(&scores);
8745 static void setServerScoreInfoToDefaults(void)
8747 setScoreInfoToDefaultsExt(&server_scores);
8750 static void LoadScore_OLD(int nr)
8753 char *filename = getScoreFilename(nr);
8754 char cookie[MAX_LINE_LEN];
8755 char line[MAX_LINE_LEN];
8759 if (!(file = fopen(filename, MODE_READ)))
8762 // check file identifier
8763 if (fgets(cookie, MAX_LINE_LEN, file) == NULL)
8765 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
8766 cookie[strlen(cookie) - 1] = '\0';
8768 if (!checkCookieString(cookie, SCORE_COOKIE_TMPL))
8770 Warn("unknown format of score file '%s'", filename);
8777 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
8779 if (fscanf(file, "%d", &scores.entry[i].score) == EOF)
8780 Warn("fscanf() failed; %s", strerror(errno));
8782 if (fgets(line, MAX_LINE_LEN, file) == NULL)
8785 if (strlen(line) > 0 && line[strlen(line) - 1] == '\n')
8786 line[strlen(line) - 1] = '\0';
8788 for (line_ptr = line; *line_ptr; line_ptr++)
8790 if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != '\0')
8792 strncpy(scores.entry[i].name, line_ptr, MAX_PLAYER_NAME_LEN);
8793 scores.entry[i].name[MAX_PLAYER_NAME_LEN] = '\0';
8802 static void ConvertScore_OLD(void)
8804 // only convert score to time for levels that rate playing time over score
8805 if (!level.rate_time_over_score)
8808 // convert old score to playing time for score-less levels (like Supaplex)
8809 int time_final_max = 999;
8812 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
8814 int score = scores.entry[i].score;
8816 if (score > 0 && score < time_final_max)
8817 scores.entry[i].time = (time_final_max - score - 1) * FRAMES_PER_SECOND;
8821 static int LoadScore_VERS(File *file, int chunk_size, struct ScoreInfo *scores)
8823 scores->file_version = getFileVersion(file);
8824 scores->game_version = getFileVersion(file);
8829 static int LoadScore_INFO(File *file, int chunk_size, struct ScoreInfo *scores)
8831 char *level_identifier = NULL;
8832 int level_identifier_size;
8835 level_identifier_size = getFile16BitBE(file);
8837 level_identifier = checked_malloc(level_identifier_size);
8839 for (i = 0; i < level_identifier_size; i++)
8840 level_identifier[i] = getFile8Bit(file);
8842 strncpy(scores->level_identifier, level_identifier, MAX_FILENAME_LEN);
8843 scores->level_identifier[MAX_FILENAME_LEN] = '\0';
8845 checked_free(level_identifier);
8847 scores->level_nr = getFile16BitBE(file);
8848 scores->num_entries = getFile16BitBE(file);
8850 chunk_size = 2 + level_identifier_size + 2 + 2;
8855 static int LoadScore_NAME(File *file, int chunk_size, struct ScoreInfo *scores)
8859 for (i = 0; i < scores->num_entries; i++)
8861 for (j = 0; j < MAX_PLAYER_NAME_LEN; j++)
8862 scores->entry[i].name[j] = getFile8Bit(file);
8864 scores->entry[i].name[MAX_PLAYER_NAME_LEN] = '\0';
8867 chunk_size = scores->num_entries * MAX_PLAYER_NAME_LEN;
8872 static int LoadScore_SCOR(File *file, int chunk_size, struct ScoreInfo *scores)
8876 for (i = 0; i < scores->num_entries; i++)
8877 scores->entry[i].score = getFile16BitBE(file);
8879 chunk_size = scores->num_entries * 2;
8884 static int LoadScore_SC4R(File *file, int chunk_size, struct ScoreInfo *scores)
8888 for (i = 0; i < scores->num_entries; i++)
8889 scores->entry[i].score = getFile32BitBE(file);
8891 chunk_size = scores->num_entries * 4;
8896 static int LoadScore_TIME(File *file, int chunk_size, struct ScoreInfo *scores)
8900 for (i = 0; i < scores->num_entries; i++)
8901 scores->entry[i].time = getFile32BitBE(file);
8903 chunk_size = scores->num_entries * 4;
8908 static int LoadScore_TAPE(File *file, int chunk_size, struct ScoreInfo *scores)
8912 for (i = 0; i < scores->num_entries; i++)
8914 for (j = 0; j < MAX_SCORE_TAPE_BASENAME_LEN; j++)
8915 scores->entry[i].tape_basename[j] = getFile8Bit(file);
8917 scores->entry[i].tape_basename[MAX_SCORE_TAPE_BASENAME_LEN] = '\0';
8920 chunk_size = scores->num_entries * MAX_SCORE_TAPE_BASENAME_LEN;
8925 void LoadScore(int nr)
8927 char *filename = getScoreFilename(nr);
8928 char cookie[MAX_LINE_LEN];
8929 char chunk_name[CHUNK_ID_LEN + 1];
8931 boolean old_score_file_format = FALSE;
8934 // always start with reliable default values
8935 setScoreInfoToDefaults();
8937 if (!(file = openFile(filename, MODE_READ)))
8940 getFileChunkBE(file, chunk_name, NULL);
8941 if (strEqual(chunk_name, "RND1"))
8943 getFile32BitBE(file); // not used
8945 getFileChunkBE(file, chunk_name, NULL);
8946 if (!strEqual(chunk_name, "SCOR"))
8948 Warn("unknown format of score file '%s'", filename);
8955 else // check for old file format with cookie string
8957 strcpy(cookie, chunk_name);
8958 if (getStringFromFile(file, &cookie[4], MAX_LINE_LEN - 4) == NULL)
8960 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
8961 cookie[strlen(cookie) - 1] = '\0';
8963 if (!checkCookieString(cookie, SCORE_COOKIE_TMPL))
8965 Warn("unknown format of score file '%s'", filename);
8972 old_score_file_format = TRUE;
8975 if (old_score_file_format)
8977 // score files from versions before 4.2.4.0 without chunk structure
8980 // convert score to time, if possible (mainly for Supaplex levels)
8989 int (*loader)(File *, int, struct ScoreInfo *);
8993 { "VERS", SCORE_CHUNK_VERS_SIZE, LoadScore_VERS },
8994 { "INFO", -1, LoadScore_INFO },
8995 { "NAME", -1, LoadScore_NAME },
8996 { "SCOR", -1, LoadScore_SCOR },
8997 { "SC4R", -1, LoadScore_SC4R },
8998 { "TIME", -1, LoadScore_TIME },
8999 { "TAPE", -1, LoadScore_TAPE },
9004 while (getFileChunkBE(file, chunk_name, &chunk_size))
9008 while (chunk_info[i].name != NULL &&
9009 !strEqual(chunk_name, chunk_info[i].name))
9012 if (chunk_info[i].name == NULL)
9014 Warn("unknown chunk '%s' in score file '%s'",
9015 chunk_name, filename);
9017 ReadUnusedBytesFromFile(file, chunk_size);
9019 else if (chunk_info[i].size != -1 &&
9020 chunk_info[i].size != chunk_size)
9022 Warn("wrong size (%d) of chunk '%s' in score file '%s'",
9023 chunk_size, chunk_name, filename);
9025 ReadUnusedBytesFromFile(file, chunk_size);
9029 // call function to load this score chunk
9030 int chunk_size_expected =
9031 (chunk_info[i].loader)(file, chunk_size, &scores);
9033 // the size of some chunks cannot be checked before reading other
9034 // chunks first (like "HEAD" and "BODY") that contain some header
9035 // information, so check them here
9036 if (chunk_size_expected != chunk_size)
9038 Warn("wrong size (%d) of chunk '%s' in score file '%s'",
9039 chunk_size, chunk_name, filename);
9048 #if ENABLE_HISTORIC_CHUNKS
9049 void SaveScore_OLD(int nr)
9052 char *filename = getScoreFilename(nr);
9055 // used instead of "leveldir_current->subdir" (for network games)
9056 InitScoreDirectory(levelset.identifier);
9058 if (!(file = fopen(filename, MODE_WRITE)))
9060 Warn("cannot save score for level %d", nr);
9065 fprintf(file, "%s\n\n", SCORE_COOKIE);
9067 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9068 fprintf(file, "%d %s\n", scores.entry[i].score, scores.entry[i].name);
9072 SetFilePermissions(filename, PERMS_PRIVATE);
9076 static void SaveScore_VERS(FILE *file, struct ScoreInfo *scores)
9078 putFileVersion(file, scores->file_version);
9079 putFileVersion(file, scores->game_version);
9082 static void SaveScore_INFO(FILE *file, struct ScoreInfo *scores)
9084 int level_identifier_size = strlen(scores->level_identifier) + 1;
9087 putFile16BitBE(file, level_identifier_size);
9089 for (i = 0; i < level_identifier_size; i++)
9090 putFile8Bit(file, scores->level_identifier[i]);
9092 putFile16BitBE(file, scores->level_nr);
9093 putFile16BitBE(file, scores->num_entries);
9096 static void SaveScore_NAME(FILE *file, struct ScoreInfo *scores)
9100 for (i = 0; i < scores->num_entries; i++)
9102 int name_size = strlen(scores->entry[i].name);
9104 for (j = 0; j < MAX_PLAYER_NAME_LEN; j++)
9105 putFile8Bit(file, (j < name_size ? scores->entry[i].name[j] : 0));
9109 static void SaveScore_SCOR(FILE *file, struct ScoreInfo *scores)
9113 for (i = 0; i < scores->num_entries; i++)
9114 putFile16BitBE(file, scores->entry[i].score);
9117 static void SaveScore_SC4R(FILE *file, struct ScoreInfo *scores)
9121 for (i = 0; i < scores->num_entries; i++)
9122 putFile32BitBE(file, scores->entry[i].score);
9125 static void SaveScore_TIME(FILE *file, struct ScoreInfo *scores)
9129 for (i = 0; i < scores->num_entries; i++)
9130 putFile32BitBE(file, scores->entry[i].time);
9133 static void SaveScore_TAPE(FILE *file, struct ScoreInfo *scores)
9137 for (i = 0; i < scores->num_entries; i++)
9139 int size = strlen(scores->entry[i].tape_basename);
9141 for (j = 0; j < MAX_SCORE_TAPE_BASENAME_LEN; j++)
9142 putFile8Bit(file, (j < size ? scores->entry[i].tape_basename[j] : 0));
9146 static void SaveScoreToFilename(char *filename)
9149 int info_chunk_size;
9150 int name_chunk_size;
9151 int scor_chunk_size;
9152 int sc4r_chunk_size;
9153 int time_chunk_size;
9154 int tape_chunk_size;
9155 boolean has_large_score_values;
9158 if (!(file = fopen(filename, MODE_WRITE)))
9160 Warn("cannot save score file '%s'", filename);
9165 info_chunk_size = 2 + (strlen(scores.level_identifier) + 1) + 2 + 2;
9166 name_chunk_size = scores.num_entries * MAX_PLAYER_NAME_LEN;
9167 scor_chunk_size = scores.num_entries * 2;
9168 sc4r_chunk_size = scores.num_entries * 4;
9169 time_chunk_size = scores.num_entries * 4;
9170 tape_chunk_size = scores.num_entries * MAX_SCORE_TAPE_BASENAME_LEN;
9172 has_large_score_values = FALSE;
9173 for (i = 0; i < scores.num_entries; i++)
9174 if (scores.entry[i].score > 0xffff)
9175 has_large_score_values = TRUE;
9177 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
9178 putFileChunkBE(file, "SCOR", CHUNK_SIZE_NONE);
9180 putFileChunkBE(file, "VERS", SCORE_CHUNK_VERS_SIZE);
9181 SaveScore_VERS(file, &scores);
9183 putFileChunkBE(file, "INFO", info_chunk_size);
9184 SaveScore_INFO(file, &scores);
9186 putFileChunkBE(file, "NAME", name_chunk_size);
9187 SaveScore_NAME(file, &scores);
9189 if (has_large_score_values)
9191 putFileChunkBE(file, "SC4R", sc4r_chunk_size);
9192 SaveScore_SC4R(file, &scores);
9196 putFileChunkBE(file, "SCOR", scor_chunk_size);
9197 SaveScore_SCOR(file, &scores);
9200 putFileChunkBE(file, "TIME", time_chunk_size);
9201 SaveScore_TIME(file, &scores);
9203 putFileChunkBE(file, "TAPE", tape_chunk_size);
9204 SaveScore_TAPE(file, &scores);
9208 SetFilePermissions(filename, PERMS_PRIVATE);
9211 void SaveScore(int nr)
9213 char *filename = getScoreFilename(nr);
9216 // used instead of "leveldir_current->subdir" (for network games)
9217 InitScoreDirectory(levelset.identifier);
9219 scores.file_version = FILE_VERSION_ACTUAL;
9220 scores.game_version = GAME_VERSION_ACTUAL;
9222 strncpy(scores.level_identifier, levelset.identifier, MAX_FILENAME_LEN);
9223 scores.level_identifier[MAX_FILENAME_LEN] = '\0';
9224 scores.level_nr = level_nr;
9226 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9227 if (scores.entry[i].score == 0 &&
9228 scores.entry[i].time == 0 &&
9229 strEqual(scores.entry[i].name, EMPTY_PLAYER_NAME))
9232 scores.num_entries = i;
9234 if (scores.num_entries == 0)
9237 SaveScoreToFilename(filename);
9240 static void LoadServerScoreFromCache(int nr)
9242 struct ScoreEntry score_entry;
9251 { &score_entry.score, FALSE, 0 },
9252 { &score_entry.time, FALSE, 0 },
9253 { score_entry.name, TRUE, MAX_PLAYER_NAME_LEN },
9254 { score_entry.tape_basename, TRUE, MAX_FILENAME_LEN },
9255 { score_entry.tape_date, TRUE, MAX_ISO_DATE_LEN },
9256 { &score_entry.id, FALSE, 0 },
9257 { score_entry.platform, TRUE, MAX_PLATFORM_TEXT_LEN },
9258 { score_entry.version, TRUE, MAX_VERSION_TEXT_LEN },
9259 { score_entry.country_code, TRUE, MAX_COUNTRY_CODE_LEN },
9260 { score_entry.country_name, TRUE, MAX_COUNTRY_NAME_LEN },
9264 char *filename = getScoreCacheFilename(nr);
9265 SetupFileHash *score_hash = loadSetupFileHash(filename);
9268 server_scores.num_entries = 0;
9270 if (score_hash == NULL)
9273 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9275 score_entry = server_scores.entry[i];
9277 for (j = 0; score_mapping[j].value != NULL; j++)
9281 sprintf(token, "%02d.%d", i, j);
9283 char *value = getHashEntry(score_hash, token);
9288 if (score_mapping[j].is_string)
9290 char *score_value = (char *)score_mapping[j].value;
9291 int value_size = score_mapping[j].string_size;
9293 strncpy(score_value, value, value_size);
9294 score_value[value_size] = '\0';
9298 int *score_value = (int *)score_mapping[j].value;
9300 *score_value = atoi(value);
9303 server_scores.num_entries = i + 1;
9306 server_scores.entry[i] = score_entry;
9309 freeSetupFileHash(score_hash);
9312 void LoadServerScore(int nr, boolean download_score)
9314 if (!setup.use_api_server)
9317 // always start with reliable default values
9318 setServerScoreInfoToDefaults();
9320 // 1st step: load server scores from cache file (which may not exist)
9321 // (this should prevent reading it while the thread is writing to it)
9322 LoadServerScoreFromCache(nr);
9324 if (download_score && runtime.use_api_server)
9326 // 2nd step: download server scores from score server to cache file
9327 // (as thread, as it might time out if the server is not reachable)
9328 ApiGetScoreAsThread(nr);
9332 void PrepareScoreTapesForUpload(char *leveldir_subdir)
9334 MarkTapeDirectoryUploadsAsIncomplete(leveldir_subdir);
9336 // if score tape not uploaded, ask for uploading missing tapes later
9337 if (!setup.has_remaining_tapes)
9338 setup.ask_for_remaining_tapes = TRUE;
9340 setup.provide_uploading_tapes = TRUE;
9341 setup.has_remaining_tapes = TRUE;
9343 SaveSetup_ServerSetup();
9346 void SaveServerScore(int nr, boolean tape_saved)
9348 if (!runtime.use_api_server)
9350 PrepareScoreTapesForUpload(leveldir_current->subdir);
9355 ApiAddScoreAsThread(nr, tape_saved, NULL);
9358 void SaveServerScoreFromFile(int nr, boolean tape_saved,
9359 char *score_tape_filename)
9361 if (!runtime.use_api_server)
9364 ApiAddScoreAsThread(nr, tape_saved, score_tape_filename);
9367 void LoadLocalAndServerScore(int nr, boolean download_score)
9369 int last_added_local = scores.last_added_local;
9370 boolean force_last_added = scores.force_last_added;
9372 // needed if only showing server scores
9373 setScoreInfoToDefaults();
9375 if (!strEqual(setup.scores_in_highscore_list, STR_SCORES_TYPE_SERVER_ONLY))
9378 // restore last added local score entry (before merging server scores)
9379 scores.last_added = scores.last_added_local = last_added_local;
9381 if (setup.use_api_server &&
9382 !strEqual(setup.scores_in_highscore_list, STR_SCORES_TYPE_LOCAL_ONLY))
9384 // load server scores from cache file and trigger update from server
9385 LoadServerScore(nr, download_score);
9387 // merge local scores with scores from server
9391 if (force_last_added)
9392 scores.force_last_added = force_last_added;
9396 // ============================================================================
9397 // setup file functions
9398 // ============================================================================
9400 #define TOKEN_STR_PLAYER_PREFIX "player_"
9403 static struct TokenInfo global_setup_tokens[] =
9407 &setup.player_name, "player_name"
9411 &setup.multiple_users, "multiple_users"
9415 &setup.sound, "sound"
9419 &setup.sound_loops, "repeating_sound_loops"
9423 &setup.sound_music, "background_music"
9427 &setup.sound_simple, "simple_sound_effects"
9431 &setup.toons, "toons"
9435 &setup.scroll_delay, "scroll_delay"
9439 &setup.forced_scroll_delay, "forced_scroll_delay"
9443 &setup.scroll_delay_value, "scroll_delay_value"
9447 &setup.engine_snapshot_mode, "engine_snapshot_mode"
9451 &setup.engine_snapshot_memory, "engine_snapshot_memory"
9455 &setup.fade_screens, "fade_screens"
9459 &setup.autorecord, "automatic_tape_recording"
9463 &setup.auto_pause_on_start, "auto_pause_on_start"
9467 &setup.show_titlescreen, "show_titlescreen"
9471 &setup.quick_doors, "quick_doors"
9475 &setup.team_mode, "team_mode"
9479 &setup.handicap, "handicap"
9483 &setup.skip_levels, "skip_levels"
9487 &setup.increment_levels, "increment_levels"
9491 &setup.auto_play_next_level, "auto_play_next_level"
9495 &setup.count_score_after_game, "count_score_after_game"
9499 &setup.show_scores_after_game, "show_scores_after_game"
9503 &setup.time_limit, "time_limit"
9507 &setup.fullscreen, "fullscreen"
9511 &setup.window_scaling_percent, "window_scaling_percent"
9515 &setup.window_scaling_quality, "window_scaling_quality"
9519 &setup.screen_rendering_mode, "screen_rendering_mode"
9523 &setup.vsync_mode, "vsync_mode"
9527 &setup.ask_on_escape, "ask_on_escape"
9531 &setup.ask_on_escape_editor, "ask_on_escape_editor"
9535 &setup.ask_on_game_over, "ask_on_game_over"
9539 &setup.ask_on_quit_game, "ask_on_quit_game"
9543 &setup.ask_on_quit_program, "ask_on_quit_program"
9547 &setup.quick_switch, "quick_player_switch"
9551 &setup.input_on_focus, "input_on_focus"
9555 &setup.prefer_aga_graphics, "prefer_aga_graphics"
9559 &setup.prefer_lowpass_sounds, "prefer_lowpass_sounds"
9563 &setup.prefer_extra_panel_items, "prefer_extra_panel_items"
9567 &setup.game_speed_extended, "game_speed_extended"
9571 &setup.game_frame_delay, "game_frame_delay"
9575 &setup.sp_show_border_elements, "sp_show_border_elements"
9579 &setup.small_game_graphics, "small_game_graphics"
9583 &setup.show_load_save_buttons, "show_load_save_buttons"
9587 &setup.show_undo_redo_buttons, "show_undo_redo_buttons"
9591 &setup.scores_in_highscore_list, "scores_in_highscore_list"
9595 &setup.graphics_set, "graphics_set"
9599 &setup.sounds_set, "sounds_set"
9603 &setup.music_set, "music_set"
9607 &setup.override_level_graphics, "override_level_graphics"
9611 &setup.override_level_sounds, "override_level_sounds"
9615 &setup.override_level_music, "override_level_music"
9619 &setup.volume_simple, "volume_simple"
9623 &setup.volume_loops, "volume_loops"
9627 &setup.volume_music, "volume_music"
9631 &setup.network_mode, "network_mode"
9635 &setup.network_player_nr, "network_player"
9639 &setup.network_server_hostname, "network_server_hostname"
9643 &setup.touch.control_type, "touch.control_type"
9647 &setup.touch.move_distance, "touch.move_distance"
9651 &setup.touch.drop_distance, "touch.drop_distance"
9655 &setup.touch.transparency, "touch.transparency"
9659 &setup.touch.draw_outlined, "touch.draw_outlined"
9663 &setup.touch.draw_pressed, "touch.draw_pressed"
9667 &setup.touch.grid_xsize[0], "touch.virtual_buttons.0.xsize"
9671 &setup.touch.grid_ysize[0], "touch.virtual_buttons.0.ysize"
9675 &setup.touch.grid_xsize[1], "touch.virtual_buttons.1.xsize"
9679 &setup.touch.grid_ysize[1], "touch.virtual_buttons.1.ysize"
9683 &setup.touch.overlay_buttons, "touch.overlay_buttons"
9687 static struct TokenInfo auto_setup_tokens[] =
9691 &setup.auto_setup.editor_zoom_tilesize, "editor.zoom_tilesize"
9695 static struct TokenInfo server_setup_tokens[] =
9699 &setup.player_uuid, "player_uuid"
9703 &setup.player_version, "player_version"
9707 &setup.use_api_server, TEST_PREFIX "use_api_server"
9711 &setup.api_server_hostname, TEST_PREFIX "api_server_hostname"
9715 &setup.api_server_password, TEST_PREFIX "api_server_password"
9719 &setup.ask_for_uploading_tapes, TEST_PREFIX "ask_for_uploading_tapes"
9723 &setup.ask_for_remaining_tapes, TEST_PREFIX "ask_for_remaining_tapes"
9727 &setup.provide_uploading_tapes, TEST_PREFIX "provide_uploading_tapes"
9731 &setup.ask_for_using_api_server,TEST_PREFIX "ask_for_using_api_server"
9735 &setup.has_remaining_tapes, TEST_PREFIX "has_remaining_tapes"
9739 static struct TokenInfo editor_setup_tokens[] =
9743 &setup.editor.el_classic, "editor.el_classic"
9747 &setup.editor.el_custom, "editor.el_custom"
9751 &setup.editor.el_user_defined, "editor.el_user_defined"
9755 &setup.editor.el_dynamic, "editor.el_dynamic"
9759 &setup.editor.el_headlines, "editor.el_headlines"
9763 &setup.editor.show_element_token, "editor.show_element_token"
9767 &setup.editor.show_read_only_warning, "editor.show_read_only_warning"
9771 static struct TokenInfo editor_cascade_setup_tokens[] =
9775 &setup.editor_cascade.el_bd, "editor.cascade.el_bd"
9779 &setup.editor_cascade.el_em, "editor.cascade.el_em"
9783 &setup.editor_cascade.el_emc, "editor.cascade.el_emc"
9787 &setup.editor_cascade.el_rnd, "editor.cascade.el_rnd"
9791 &setup.editor_cascade.el_sb, "editor.cascade.el_sb"
9795 &setup.editor_cascade.el_sp, "editor.cascade.el_sp"
9799 &setup.editor_cascade.el_dc, "editor.cascade.el_dc"
9803 &setup.editor_cascade.el_dx, "editor.cascade.el_dx"
9807 &setup.editor_cascade.el_mm, "editor.cascade.el_mm"
9811 &setup.editor_cascade.el_df, "editor.cascade.el_df"
9815 &setup.editor_cascade.el_chars, "editor.cascade.el_chars"
9819 &setup.editor_cascade.el_steel_chars, "editor.cascade.el_steel_chars"
9823 &setup.editor_cascade.el_ce, "editor.cascade.el_ce"
9827 &setup.editor_cascade.el_ge, "editor.cascade.el_ge"
9831 &setup.editor_cascade.el_es, "editor.cascade.el_es"
9835 &setup.editor_cascade.el_ref, "editor.cascade.el_ref"
9839 &setup.editor_cascade.el_user, "editor.cascade.el_user"
9843 &setup.editor_cascade.el_dynamic, "editor.cascade.el_dynamic"
9847 static struct TokenInfo shortcut_setup_tokens[] =
9851 &setup.shortcut.save_game, "shortcut.save_game"
9855 &setup.shortcut.load_game, "shortcut.load_game"
9859 &setup.shortcut.restart_game, "shortcut.restart_game"
9863 &setup.shortcut.pause_before_end, "shortcut.pause_before_end"
9867 &setup.shortcut.toggle_pause, "shortcut.toggle_pause"
9871 &setup.shortcut.focus_player[0], "shortcut.focus_player_1"
9875 &setup.shortcut.focus_player[1], "shortcut.focus_player_2"
9879 &setup.shortcut.focus_player[2], "shortcut.focus_player_3"
9883 &setup.shortcut.focus_player[3], "shortcut.focus_player_4"
9887 &setup.shortcut.focus_player_all, "shortcut.focus_player_all"
9891 &setup.shortcut.tape_eject, "shortcut.tape_eject"
9895 &setup.shortcut.tape_extra, "shortcut.tape_extra"
9899 &setup.shortcut.tape_stop, "shortcut.tape_stop"
9903 &setup.shortcut.tape_pause, "shortcut.tape_pause"
9907 &setup.shortcut.tape_record, "shortcut.tape_record"
9911 &setup.shortcut.tape_play, "shortcut.tape_play"
9915 &setup.shortcut.sound_simple, "shortcut.sound_simple"
9919 &setup.shortcut.sound_loops, "shortcut.sound_loops"
9923 &setup.shortcut.sound_music, "shortcut.sound_music"
9927 &setup.shortcut.snap_left, "shortcut.snap_left"
9931 &setup.shortcut.snap_right, "shortcut.snap_right"
9935 &setup.shortcut.snap_up, "shortcut.snap_up"
9939 &setup.shortcut.snap_down, "shortcut.snap_down"
9943 static struct SetupInputInfo setup_input;
9944 static struct TokenInfo player_setup_tokens[] =
9948 &setup_input.use_joystick, ".use_joystick"
9952 &setup_input.joy.device_name, ".joy.device_name"
9956 &setup_input.joy.xleft, ".joy.xleft"
9960 &setup_input.joy.xmiddle, ".joy.xmiddle"
9964 &setup_input.joy.xright, ".joy.xright"
9968 &setup_input.joy.yupper, ".joy.yupper"
9972 &setup_input.joy.ymiddle, ".joy.ymiddle"
9976 &setup_input.joy.ylower, ".joy.ylower"
9980 &setup_input.joy.snap, ".joy.snap_field"
9984 &setup_input.joy.drop, ".joy.place_bomb"
9988 &setup_input.key.left, ".key.move_left"
9992 &setup_input.key.right, ".key.move_right"
9996 &setup_input.key.up, ".key.move_up"
10000 &setup_input.key.down, ".key.move_down"
10004 &setup_input.key.snap, ".key.snap_field"
10008 &setup_input.key.drop, ".key.place_bomb"
10012 static struct TokenInfo system_setup_tokens[] =
10016 &setup.system.sdl_renderdriver, "system.sdl_renderdriver"
10020 &setup.system.sdl_videodriver, "system.sdl_videodriver"
10024 &setup.system.sdl_audiodriver, "system.sdl_audiodriver"
10028 &setup.system.audio_fragment_size, "system.audio_fragment_size"
10032 static struct TokenInfo internal_setup_tokens[] =
10036 &setup.internal.program_title, "program_title"
10040 &setup.internal.program_version, "program_version"
10044 &setup.internal.program_author, "program_author"
10048 &setup.internal.program_email, "program_email"
10052 &setup.internal.program_website, "program_website"
10056 &setup.internal.program_copyright, "program_copyright"
10060 &setup.internal.program_company, "program_company"
10064 &setup.internal.program_icon_file, "program_icon_file"
10068 &setup.internal.default_graphics_set, "default_graphics_set"
10072 &setup.internal.default_sounds_set, "default_sounds_set"
10076 &setup.internal.default_music_set, "default_music_set"
10080 &setup.internal.fallback_graphics_file, "fallback_graphics_file"
10084 &setup.internal.fallback_sounds_file, "fallback_sounds_file"
10088 &setup.internal.fallback_music_file, "fallback_music_file"
10092 &setup.internal.default_level_series, "default_level_series"
10096 &setup.internal.default_window_width, "default_window_width"
10100 &setup.internal.default_window_height, "default_window_height"
10104 &setup.internal.choose_from_top_leveldir, "choose_from_top_leveldir"
10108 &setup.internal.show_scaling_in_title, "show_scaling_in_title"
10112 &setup.internal.create_user_levelset, "create_user_levelset"
10116 &setup.internal.menu_game, "menu_game"
10120 &setup.internal.menu_editor, "menu_editor"
10124 &setup.internal.menu_graphics, "menu_graphics"
10128 &setup.internal.menu_sound, "menu_sound"
10132 &setup.internal.menu_artwork, "menu_artwork"
10136 &setup.internal.menu_input, "menu_input"
10140 &setup.internal.menu_touch, "menu_touch"
10144 &setup.internal.menu_shortcuts, "menu_shortcuts"
10148 &setup.internal.menu_exit, "menu_exit"
10152 &setup.internal.menu_save_and_exit, "menu_save_and_exit"
10156 &setup.internal.info_title, "info_title"
10160 &setup.internal.info_elements, "info_elements"
10164 &setup.internal.info_music, "info_music"
10168 &setup.internal.info_credits, "info_credits"
10172 &setup.internal.info_program, "info_program"
10176 &setup.internal.info_version, "info_version"
10180 &setup.internal.info_levelset, "info_levelset"
10184 &setup.internal.info_exit, "info_exit"
10188 static struct TokenInfo debug_setup_tokens[] =
10192 &setup.debug.frame_delay[0], "debug.frame_delay_0"
10196 &setup.debug.frame_delay[1], "debug.frame_delay_1"
10200 &setup.debug.frame_delay[2], "debug.frame_delay_2"
10204 &setup.debug.frame_delay[3], "debug.frame_delay_3"
10208 &setup.debug.frame_delay[4], "debug.frame_delay_4"
10212 &setup.debug.frame_delay[5], "debug.frame_delay_5"
10216 &setup.debug.frame_delay[6], "debug.frame_delay_6"
10220 &setup.debug.frame_delay[7], "debug.frame_delay_7"
10224 &setup.debug.frame_delay[8], "debug.frame_delay_8"
10228 &setup.debug.frame_delay[9], "debug.frame_delay_9"
10232 &setup.debug.frame_delay_key[0], "debug.key.frame_delay_0"
10236 &setup.debug.frame_delay_key[1], "debug.key.frame_delay_1"
10240 &setup.debug.frame_delay_key[2], "debug.key.frame_delay_2"
10244 &setup.debug.frame_delay_key[3], "debug.key.frame_delay_3"
10248 &setup.debug.frame_delay_key[4], "debug.key.frame_delay_4"
10252 &setup.debug.frame_delay_key[5], "debug.key.frame_delay_5"
10256 &setup.debug.frame_delay_key[6], "debug.key.frame_delay_6"
10260 &setup.debug.frame_delay_key[7], "debug.key.frame_delay_7"
10264 &setup.debug.frame_delay_key[8], "debug.key.frame_delay_8"
10268 &setup.debug.frame_delay_key[9], "debug.key.frame_delay_9"
10272 &setup.debug.frame_delay_use_mod_key, "debug.frame_delay.use_mod_key"},
10275 &setup.debug.frame_delay_game_only, "debug.frame_delay.game_only"
10279 &setup.debug.show_frames_per_second, "debug.show_frames_per_second"
10283 &setup.debug.xsn_mode, "debug.xsn_mode"
10287 &setup.debug.xsn_percent, "debug.xsn_percent"
10291 static struct TokenInfo options_setup_tokens[] =
10295 &setup.options.verbose, "options.verbose"
10299 static void setSetupInfoToDefaults(struct SetupInfo *si)
10303 si->player_name = getStringCopy(getDefaultUserName(user.nr));
10305 si->multiple_users = TRUE;
10308 si->sound_loops = TRUE;
10309 si->sound_music = TRUE;
10310 si->sound_simple = TRUE;
10312 si->scroll_delay = TRUE;
10313 si->forced_scroll_delay = FALSE;
10314 si->scroll_delay_value = STD_SCROLL_DELAY;
10315 si->engine_snapshot_mode = getStringCopy(STR_SNAPSHOT_MODE_DEFAULT);
10316 si->engine_snapshot_memory = SNAPSHOT_MEMORY_DEFAULT;
10317 si->fade_screens = TRUE;
10318 si->autorecord = TRUE;
10319 si->auto_pause_on_start = FALSE;
10320 si->show_titlescreen = TRUE;
10321 si->quick_doors = FALSE;
10322 si->team_mode = FALSE;
10323 si->handicap = TRUE;
10324 si->skip_levels = TRUE;
10325 si->increment_levels = TRUE;
10326 si->auto_play_next_level = TRUE;
10327 si->count_score_after_game = TRUE;
10328 si->show_scores_after_game = TRUE;
10329 si->time_limit = TRUE;
10330 si->fullscreen = FALSE;
10331 si->window_scaling_percent = STD_WINDOW_SCALING_PERCENT;
10332 si->window_scaling_quality = getStringCopy(SCALING_QUALITY_DEFAULT);
10333 si->screen_rendering_mode = getStringCopy(STR_SPECIAL_RENDERING_DEFAULT);
10334 si->vsync_mode = getStringCopy(STR_VSYNC_MODE_DEFAULT);
10335 si->ask_on_escape = TRUE;
10336 si->ask_on_escape_editor = TRUE;
10337 si->ask_on_game_over = TRUE;
10338 si->ask_on_quit_game = TRUE;
10339 si->ask_on_quit_program = TRUE;
10340 si->quick_switch = FALSE;
10341 si->input_on_focus = FALSE;
10342 si->prefer_aga_graphics = TRUE;
10343 si->prefer_lowpass_sounds = FALSE;
10344 si->prefer_extra_panel_items = TRUE;
10345 si->game_speed_extended = FALSE;
10346 si->game_frame_delay = GAME_FRAME_DELAY;
10347 si->sp_show_border_elements = FALSE;
10348 si->small_game_graphics = FALSE;
10349 si->show_load_save_buttons = FALSE;
10350 si->show_undo_redo_buttons = FALSE;
10351 si->scores_in_highscore_list = getStringCopy(STR_SCORES_TYPE_DEFAULT);
10353 si->graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
10354 si->sounds_set = getStringCopy(SND_CLASSIC_SUBDIR);
10355 si->music_set = getStringCopy(MUS_CLASSIC_SUBDIR);
10357 si->override_level_graphics = FALSE;
10358 si->override_level_sounds = FALSE;
10359 si->override_level_music = FALSE;
10361 si->volume_simple = 100; // percent
10362 si->volume_loops = 100; // percent
10363 si->volume_music = 100; // percent
10365 si->network_mode = FALSE;
10366 si->network_player_nr = 0; // first player
10367 si->network_server_hostname = getStringCopy(STR_NETWORK_AUTO_DETECT);
10369 si->touch.control_type = getStringCopy(TOUCH_CONTROL_DEFAULT);
10370 si->touch.move_distance = TOUCH_MOVE_DISTANCE_DEFAULT; // percent
10371 si->touch.drop_distance = TOUCH_DROP_DISTANCE_DEFAULT; // percent
10372 si->touch.transparency = TOUCH_TRANSPARENCY_DEFAULT; // percent
10373 si->touch.draw_outlined = TRUE;
10374 si->touch.draw_pressed = TRUE;
10376 for (i = 0; i < 2; i++)
10378 char *default_grid_button[6][2] =
10384 { "111222", " vv " },
10385 { "111222", " vv " }
10387 int grid_xsize = DEFAULT_GRID_XSIZE(i);
10388 int grid_ysize = DEFAULT_GRID_YSIZE(i);
10389 int min_xsize = MIN(6, grid_xsize);
10390 int min_ysize = MIN(6, grid_ysize);
10391 int startx = grid_xsize - min_xsize;
10392 int starty = grid_ysize - min_ysize;
10395 // virtual buttons grid can only be set to defaults if video is initialized
10396 // (this will be repeated if virtual buttons are not loaded from setup file)
10397 if (video.initialized)
10399 si->touch.grid_xsize[i] = grid_xsize;
10400 si->touch.grid_ysize[i] = grid_ysize;
10404 si->touch.grid_xsize[i] = -1;
10405 si->touch.grid_ysize[i] = -1;
10408 for (x = 0; x < MAX_GRID_XSIZE; x++)
10409 for (y = 0; y < MAX_GRID_YSIZE; y++)
10410 si->touch.grid_button[i][x][y] = CHAR_GRID_BUTTON_NONE;
10412 for (x = 0; x < min_xsize; x++)
10413 for (y = 0; y < min_ysize; y++)
10414 si->touch.grid_button[i][x][starty + y] =
10415 default_grid_button[y][0][x];
10417 for (x = 0; x < min_xsize; x++)
10418 for (y = 0; y < min_ysize; y++)
10419 si->touch.grid_button[i][startx + x][starty + y] =
10420 default_grid_button[y][1][x];
10423 si->touch.grid_initialized = video.initialized;
10425 si->touch.overlay_buttons = FALSE;
10427 si->editor.el_boulderdash = TRUE;
10428 si->editor.el_emerald_mine = TRUE;
10429 si->editor.el_emerald_mine_club = TRUE;
10430 si->editor.el_more = TRUE;
10431 si->editor.el_sokoban = TRUE;
10432 si->editor.el_supaplex = TRUE;
10433 si->editor.el_diamond_caves = TRUE;
10434 si->editor.el_dx_boulderdash = TRUE;
10436 si->editor.el_mirror_magic = TRUE;
10437 si->editor.el_deflektor = TRUE;
10439 si->editor.el_chars = TRUE;
10440 si->editor.el_steel_chars = TRUE;
10442 si->editor.el_classic = TRUE;
10443 si->editor.el_custom = TRUE;
10445 si->editor.el_user_defined = FALSE;
10446 si->editor.el_dynamic = TRUE;
10448 si->editor.el_headlines = TRUE;
10450 si->editor.show_element_token = FALSE;
10452 si->editor.show_read_only_warning = TRUE;
10454 si->editor.use_template_for_new_levels = TRUE;
10456 si->shortcut.save_game = DEFAULT_KEY_SAVE_GAME;
10457 si->shortcut.load_game = DEFAULT_KEY_LOAD_GAME;
10458 si->shortcut.restart_game = DEFAULT_KEY_RESTART_GAME;
10459 si->shortcut.pause_before_end = DEFAULT_KEY_PAUSE_BEFORE_END;
10460 si->shortcut.toggle_pause = DEFAULT_KEY_TOGGLE_PAUSE;
10462 si->shortcut.focus_player[0] = DEFAULT_KEY_FOCUS_PLAYER_1;
10463 si->shortcut.focus_player[1] = DEFAULT_KEY_FOCUS_PLAYER_2;
10464 si->shortcut.focus_player[2] = DEFAULT_KEY_FOCUS_PLAYER_3;
10465 si->shortcut.focus_player[3] = DEFAULT_KEY_FOCUS_PLAYER_4;
10466 si->shortcut.focus_player_all = DEFAULT_KEY_FOCUS_PLAYER_ALL;
10468 si->shortcut.tape_eject = DEFAULT_KEY_TAPE_EJECT;
10469 si->shortcut.tape_extra = DEFAULT_KEY_TAPE_EXTRA;
10470 si->shortcut.tape_stop = DEFAULT_KEY_TAPE_STOP;
10471 si->shortcut.tape_pause = DEFAULT_KEY_TAPE_PAUSE;
10472 si->shortcut.tape_record = DEFAULT_KEY_TAPE_RECORD;
10473 si->shortcut.tape_play = DEFAULT_KEY_TAPE_PLAY;
10475 si->shortcut.sound_simple = DEFAULT_KEY_SOUND_SIMPLE;
10476 si->shortcut.sound_loops = DEFAULT_KEY_SOUND_LOOPS;
10477 si->shortcut.sound_music = DEFAULT_KEY_SOUND_MUSIC;
10479 si->shortcut.snap_left = DEFAULT_KEY_SNAP_LEFT;
10480 si->shortcut.snap_right = DEFAULT_KEY_SNAP_RIGHT;
10481 si->shortcut.snap_up = DEFAULT_KEY_SNAP_UP;
10482 si->shortcut.snap_down = DEFAULT_KEY_SNAP_DOWN;
10484 for (i = 0; i < MAX_PLAYERS; i++)
10486 si->input[i].use_joystick = FALSE;
10487 si->input[i].joy.device_name=getStringCopy(getDeviceNameFromJoystickNr(i));
10488 si->input[i].joy.xleft = JOYSTICK_XLEFT;
10489 si->input[i].joy.xmiddle = JOYSTICK_XMIDDLE;
10490 si->input[i].joy.xright = JOYSTICK_XRIGHT;
10491 si->input[i].joy.yupper = JOYSTICK_YUPPER;
10492 si->input[i].joy.ymiddle = JOYSTICK_YMIDDLE;
10493 si->input[i].joy.ylower = JOYSTICK_YLOWER;
10494 si->input[i].joy.snap = (i == 0 ? JOY_BUTTON_1 : 0);
10495 si->input[i].joy.drop = (i == 0 ? JOY_BUTTON_2 : 0);
10496 si->input[i].key.left = (i == 0 ? DEFAULT_KEY_LEFT : KSYM_UNDEFINED);
10497 si->input[i].key.right = (i == 0 ? DEFAULT_KEY_RIGHT : KSYM_UNDEFINED);
10498 si->input[i].key.up = (i == 0 ? DEFAULT_KEY_UP : KSYM_UNDEFINED);
10499 si->input[i].key.down = (i == 0 ? DEFAULT_KEY_DOWN : KSYM_UNDEFINED);
10500 si->input[i].key.snap = (i == 0 ? DEFAULT_KEY_SNAP : KSYM_UNDEFINED);
10501 si->input[i].key.drop = (i == 0 ? DEFAULT_KEY_DROP : KSYM_UNDEFINED);
10504 si->system.sdl_renderdriver = getStringCopy(ARG_DEFAULT);
10505 si->system.sdl_videodriver = getStringCopy(ARG_DEFAULT);
10506 si->system.sdl_audiodriver = getStringCopy(ARG_DEFAULT);
10507 si->system.audio_fragment_size = DEFAULT_AUDIO_FRAGMENT_SIZE;
10509 si->internal.program_title = getStringCopy(PROGRAM_TITLE_STRING);
10510 si->internal.program_version = getStringCopy(getProgramRealVersionString());
10511 si->internal.program_author = getStringCopy(PROGRAM_AUTHOR_STRING);
10512 si->internal.program_email = getStringCopy(PROGRAM_EMAIL_STRING);
10513 si->internal.program_website = getStringCopy(PROGRAM_WEBSITE_STRING);
10514 si->internal.program_copyright = getStringCopy(PROGRAM_COPYRIGHT_STRING);
10515 si->internal.program_company = getStringCopy(PROGRAM_COMPANY_STRING);
10517 si->internal.program_icon_file = getStringCopy(PROGRAM_ICON_FILENAME);
10519 si->internal.default_graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
10520 si->internal.default_sounds_set = getStringCopy(SND_CLASSIC_SUBDIR);
10521 si->internal.default_music_set = getStringCopy(MUS_CLASSIC_SUBDIR);
10523 si->internal.fallback_graphics_file = getStringCopy(UNDEFINED_FILENAME);
10524 si->internal.fallback_sounds_file = getStringCopy(UNDEFINED_FILENAME);
10525 si->internal.fallback_music_file = getStringCopy(UNDEFINED_FILENAME);
10527 si->internal.default_level_series = getStringCopy(UNDEFINED_LEVELSET);
10528 si->internal.choose_from_top_leveldir = FALSE;
10529 si->internal.show_scaling_in_title = TRUE;
10530 si->internal.create_user_levelset = TRUE;
10532 si->internal.default_window_width = WIN_XSIZE_DEFAULT;
10533 si->internal.default_window_height = WIN_YSIZE_DEFAULT;
10535 si->debug.frame_delay[0] = DEFAULT_FRAME_DELAY_0;
10536 si->debug.frame_delay[1] = DEFAULT_FRAME_DELAY_1;
10537 si->debug.frame_delay[2] = DEFAULT_FRAME_DELAY_2;
10538 si->debug.frame_delay[3] = DEFAULT_FRAME_DELAY_3;
10539 si->debug.frame_delay[4] = DEFAULT_FRAME_DELAY_4;
10540 si->debug.frame_delay[5] = DEFAULT_FRAME_DELAY_5;
10541 si->debug.frame_delay[6] = DEFAULT_FRAME_DELAY_6;
10542 si->debug.frame_delay[7] = DEFAULT_FRAME_DELAY_7;
10543 si->debug.frame_delay[8] = DEFAULT_FRAME_DELAY_8;
10544 si->debug.frame_delay[9] = DEFAULT_FRAME_DELAY_9;
10546 si->debug.frame_delay_key[0] = DEFAULT_KEY_FRAME_DELAY_0;
10547 si->debug.frame_delay_key[1] = DEFAULT_KEY_FRAME_DELAY_1;
10548 si->debug.frame_delay_key[2] = DEFAULT_KEY_FRAME_DELAY_2;
10549 si->debug.frame_delay_key[3] = DEFAULT_KEY_FRAME_DELAY_3;
10550 si->debug.frame_delay_key[4] = DEFAULT_KEY_FRAME_DELAY_4;
10551 si->debug.frame_delay_key[5] = DEFAULT_KEY_FRAME_DELAY_5;
10552 si->debug.frame_delay_key[6] = DEFAULT_KEY_FRAME_DELAY_6;
10553 si->debug.frame_delay_key[7] = DEFAULT_KEY_FRAME_DELAY_7;
10554 si->debug.frame_delay_key[8] = DEFAULT_KEY_FRAME_DELAY_8;
10555 si->debug.frame_delay_key[9] = DEFAULT_KEY_FRAME_DELAY_9;
10557 si->debug.frame_delay_use_mod_key = DEFAULT_FRAME_DELAY_USE_MOD_KEY;
10558 si->debug.frame_delay_game_only = DEFAULT_FRAME_DELAY_GAME_ONLY;
10560 si->debug.show_frames_per_second = FALSE;
10562 si->debug.xsn_mode = AUTO;
10563 si->debug.xsn_percent = 0;
10565 si->options.verbose = FALSE;
10567 #if defined(PLATFORM_ANDROID)
10568 si->fullscreen = TRUE;
10569 si->touch.overlay_buttons = TRUE;
10572 setHideSetupEntry(&setup.debug.xsn_mode);
10575 static void setSetupInfoToDefaults_AutoSetup(struct SetupInfo *si)
10577 si->auto_setup.editor_zoom_tilesize = MINI_TILESIZE;
10580 static void setSetupInfoToDefaults_ServerSetup(struct SetupInfo *si)
10582 si->player_uuid = NULL; // (will be set later)
10583 si->player_version = 1; // (will be set later)
10585 si->use_api_server = TRUE;
10586 si->api_server_hostname = getStringCopy(API_SERVER_HOSTNAME);
10587 si->api_server_password = getStringCopy(UNDEFINED_PASSWORD);
10588 si->ask_for_uploading_tapes = TRUE;
10589 si->ask_for_remaining_tapes = FALSE;
10590 si->provide_uploading_tapes = TRUE;
10591 si->ask_for_using_api_server = TRUE;
10592 si->has_remaining_tapes = FALSE;
10595 static void setSetupInfoToDefaults_EditorCascade(struct SetupInfo *si)
10597 si->editor_cascade.el_bd = TRUE;
10598 si->editor_cascade.el_em = TRUE;
10599 si->editor_cascade.el_emc = TRUE;
10600 si->editor_cascade.el_rnd = TRUE;
10601 si->editor_cascade.el_sb = TRUE;
10602 si->editor_cascade.el_sp = TRUE;
10603 si->editor_cascade.el_dc = TRUE;
10604 si->editor_cascade.el_dx = TRUE;
10606 si->editor_cascade.el_mm = TRUE;
10607 si->editor_cascade.el_df = TRUE;
10609 si->editor_cascade.el_chars = FALSE;
10610 si->editor_cascade.el_steel_chars = FALSE;
10611 si->editor_cascade.el_ce = FALSE;
10612 si->editor_cascade.el_ge = FALSE;
10613 si->editor_cascade.el_es = FALSE;
10614 si->editor_cascade.el_ref = FALSE;
10615 si->editor_cascade.el_user = FALSE;
10616 si->editor_cascade.el_dynamic = FALSE;
10619 #define MAX_HIDE_SETUP_TOKEN_SIZE 20
10621 static char *getHideSetupToken(void *setup_value)
10623 static char hide_setup_token[MAX_HIDE_SETUP_TOKEN_SIZE];
10625 if (setup_value != NULL)
10626 snprintf(hide_setup_token, MAX_HIDE_SETUP_TOKEN_SIZE, "%p", setup_value);
10628 return hide_setup_token;
10631 void setHideSetupEntry(void *setup_value)
10633 char *hide_setup_token = getHideSetupToken(setup_value);
10635 if (hide_setup_hash == NULL)
10636 hide_setup_hash = newSetupFileHash();
10638 if (setup_value != NULL)
10639 setHashEntry(hide_setup_hash, hide_setup_token, "");
10642 void removeHideSetupEntry(void *setup_value)
10644 char *hide_setup_token = getHideSetupToken(setup_value);
10646 if (setup_value != NULL)
10647 removeHashEntry(hide_setup_hash, hide_setup_token);
10650 boolean hideSetupEntry(void *setup_value)
10652 char *hide_setup_token = getHideSetupToken(setup_value);
10654 return (setup_value != NULL &&
10655 getHashEntry(hide_setup_hash, hide_setup_token) != NULL);
10658 static void setSetupInfoFromTokenText(SetupFileHash *setup_file_hash,
10659 struct TokenInfo *token_info,
10660 int token_nr, char *token_text)
10662 char *token_hide_text = getStringCat2(token_text, ".hide");
10663 char *token_hide_value = getHashEntry(setup_file_hash, token_hide_text);
10665 // set the value of this setup option in the setup option structure
10666 setSetupInfo(token_info, token_nr, getHashEntry(setup_file_hash, token_text));
10668 // check if this setup option should be hidden in the setup menu
10669 if (token_hide_value != NULL && get_boolean_from_string(token_hide_value))
10670 setHideSetupEntry(token_info[token_nr].value);
10672 free(token_hide_text);
10675 static void setSetupInfoFromTokenInfo(SetupFileHash *setup_file_hash,
10676 struct TokenInfo *token_info,
10679 setSetupInfoFromTokenText(setup_file_hash, token_info, token_nr,
10680 token_info[token_nr].text);
10683 static void decodeSetupFileHash_Default(SetupFileHash *setup_file_hash)
10687 if (!setup_file_hash)
10690 for (i = 0; i < ARRAY_SIZE(global_setup_tokens); i++)
10691 setSetupInfoFromTokenInfo(setup_file_hash, global_setup_tokens, i);
10693 setup.touch.grid_initialized = TRUE;
10694 for (i = 0; i < 2; i++)
10696 int grid_xsize = setup.touch.grid_xsize[i];
10697 int grid_ysize = setup.touch.grid_ysize[i];
10700 // if virtual buttons are not loaded from setup file, repeat initializing
10701 // virtual buttons grid with default values later when video is initialized
10702 if (grid_xsize == -1 ||
10705 setup.touch.grid_initialized = FALSE;
10710 for (y = 0; y < grid_ysize; y++)
10712 char token_string[MAX_LINE_LEN];
10714 sprintf(token_string, "touch.virtual_buttons.%d.%02d", i, y);
10716 char *value_string = getHashEntry(setup_file_hash, token_string);
10718 if (value_string == NULL)
10721 for (x = 0; x < grid_xsize; x++)
10723 char c = value_string[x];
10725 setup.touch.grid_button[i][x][y] =
10726 (c == '.' ? CHAR_GRID_BUTTON_NONE : c);
10731 for (i = 0; i < ARRAY_SIZE(editor_setup_tokens); i++)
10732 setSetupInfoFromTokenInfo(setup_file_hash, editor_setup_tokens, i);
10734 for (i = 0; i < ARRAY_SIZE(shortcut_setup_tokens); i++)
10735 setSetupInfoFromTokenInfo(setup_file_hash, shortcut_setup_tokens, i);
10737 for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
10741 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
10743 setup_input = setup.input[pnr];
10744 for (i = 0; i < ARRAY_SIZE(player_setup_tokens); i++)
10746 char full_token[100];
10748 sprintf(full_token, "%s%s", prefix, player_setup_tokens[i].text);
10749 setSetupInfoFromTokenText(setup_file_hash, player_setup_tokens, i,
10752 setup.input[pnr] = setup_input;
10755 for (i = 0; i < ARRAY_SIZE(system_setup_tokens); i++)
10756 setSetupInfoFromTokenInfo(setup_file_hash, system_setup_tokens, i);
10758 for (i = 0; i < ARRAY_SIZE(internal_setup_tokens); i++)
10759 setSetupInfoFromTokenInfo(setup_file_hash, internal_setup_tokens, i);
10761 for (i = 0; i < ARRAY_SIZE(debug_setup_tokens); i++)
10762 setSetupInfoFromTokenInfo(setup_file_hash, debug_setup_tokens, i);
10764 for (i = 0; i < ARRAY_SIZE(options_setup_tokens); i++)
10765 setSetupInfoFromTokenInfo(setup_file_hash, options_setup_tokens, i);
10767 setHideRelatedSetupEntries();
10770 static void decodeSetupFileHash_AutoSetup(SetupFileHash *setup_file_hash)
10774 if (!setup_file_hash)
10777 for (i = 0; i < ARRAY_SIZE(auto_setup_tokens); i++)
10778 setSetupInfo(auto_setup_tokens, i,
10779 getHashEntry(setup_file_hash,
10780 auto_setup_tokens[i].text));
10783 static void decodeSetupFileHash_ServerSetup(SetupFileHash *setup_file_hash)
10787 if (!setup_file_hash)
10790 for (i = 0; i < ARRAY_SIZE(server_setup_tokens); i++)
10791 setSetupInfo(server_setup_tokens, i,
10792 getHashEntry(setup_file_hash,
10793 server_setup_tokens[i].text));
10796 static void decodeSetupFileHash_EditorCascade(SetupFileHash *setup_file_hash)
10800 if (!setup_file_hash)
10803 for (i = 0; i < ARRAY_SIZE(editor_cascade_setup_tokens); i++)
10804 setSetupInfo(editor_cascade_setup_tokens, i,
10805 getHashEntry(setup_file_hash,
10806 editor_cascade_setup_tokens[i].text));
10809 void LoadUserNames(void)
10811 int last_user_nr = user.nr;
10814 if (global.user_names != NULL)
10816 for (i = 0; i < MAX_PLAYER_NAMES; i++)
10817 checked_free(global.user_names[i]);
10819 checked_free(global.user_names);
10822 global.user_names = checked_calloc(MAX_PLAYER_NAMES * sizeof(char *));
10824 for (i = 0; i < MAX_PLAYER_NAMES; i++)
10828 SetupFileHash *setup_file_hash = loadSetupFileHash(getSetupFilename());
10830 if (setup_file_hash)
10832 char *player_name = getHashEntry(setup_file_hash, "player_name");
10834 global.user_names[i] = getFixedUserName(player_name);
10836 freeSetupFileHash(setup_file_hash);
10839 if (global.user_names[i] == NULL)
10840 global.user_names[i] = getStringCopy(getDefaultUserName(i));
10843 user.nr = last_user_nr;
10846 void LoadSetupFromFilename(char *filename)
10848 SetupFileHash *setup_file_hash = loadSetupFileHash(filename);
10850 if (setup_file_hash)
10852 decodeSetupFileHash_Default(setup_file_hash);
10854 freeSetupFileHash(setup_file_hash);
10858 Debug("setup", "using default setup values");
10862 static void LoadSetup_SpecialPostProcessing(void)
10864 char *player_name_new;
10866 // needed to work around problems with fixed length strings
10867 player_name_new = getFixedUserName(setup.player_name);
10868 free(setup.player_name);
10869 setup.player_name = player_name_new;
10871 // "scroll_delay: on(3) / off(0)" was replaced by scroll delay value
10872 if (setup.scroll_delay == FALSE)
10874 setup.scroll_delay_value = MIN_SCROLL_DELAY;
10875 setup.scroll_delay = TRUE; // now always "on"
10878 // make sure that scroll delay value stays inside valid range
10879 setup.scroll_delay_value =
10880 MIN(MAX(MIN_SCROLL_DELAY, setup.scroll_delay_value), MAX_SCROLL_DELAY);
10883 void LoadSetup_Default(void)
10887 // always start with reliable default values
10888 setSetupInfoToDefaults(&setup);
10890 // try to load setup values from default setup file
10891 filename = getDefaultSetupFilename();
10893 if (fileExists(filename))
10894 LoadSetupFromFilename(filename);
10896 // try to load setup values from platform setup file
10897 filename = getPlatformSetupFilename();
10899 if (fileExists(filename))
10900 LoadSetupFromFilename(filename);
10902 // try to load setup values from user setup file
10903 filename = getSetupFilename();
10905 LoadSetupFromFilename(filename);
10907 LoadSetup_SpecialPostProcessing();
10910 void LoadSetup_AutoSetup(void)
10912 char *filename = getPath2(getSetupDir(), AUTOSETUP_FILENAME);
10913 SetupFileHash *setup_file_hash = NULL;
10915 // always start with reliable default values
10916 setSetupInfoToDefaults_AutoSetup(&setup);
10918 setup_file_hash = loadSetupFileHash(filename);
10920 if (setup_file_hash)
10922 decodeSetupFileHash_AutoSetup(setup_file_hash);
10924 freeSetupFileHash(setup_file_hash);
10930 void LoadSetup_ServerSetup(void)
10932 char *filename = getPath2(getSetupDir(), SERVERSETUP_FILENAME);
10933 SetupFileHash *setup_file_hash = NULL;
10935 // always start with reliable default values
10936 setSetupInfoToDefaults_ServerSetup(&setup);
10938 setup_file_hash = loadSetupFileHash(filename);
10940 if (setup_file_hash)
10942 decodeSetupFileHash_ServerSetup(setup_file_hash);
10944 freeSetupFileHash(setup_file_hash);
10949 if (setup.player_uuid == NULL)
10951 // player UUID does not yet exist in setup file
10952 setup.player_uuid = getStringCopy(getUUID());
10953 setup.player_version = 2;
10955 SaveSetup_ServerSetup();
10959 void LoadSetup_EditorCascade(void)
10961 char *filename = getPath2(getSetupDir(), EDITORCASCADE_FILENAME);
10962 SetupFileHash *setup_file_hash = NULL;
10964 // always start with reliable default values
10965 setSetupInfoToDefaults_EditorCascade(&setup);
10967 setup_file_hash = loadSetupFileHash(filename);
10969 if (setup_file_hash)
10971 decodeSetupFileHash_EditorCascade(setup_file_hash);
10973 freeSetupFileHash(setup_file_hash);
10979 void LoadSetup(void)
10981 LoadSetup_Default();
10982 LoadSetup_AutoSetup();
10983 LoadSetup_ServerSetup();
10984 LoadSetup_EditorCascade();
10987 static void addGameControllerMappingToHash(SetupFileHash *mappings_hash,
10988 char *mapping_line)
10990 char mapping_guid[MAX_LINE_LEN];
10991 char *mapping_start, *mapping_end;
10993 // get GUID from game controller mapping line: copy complete line
10994 strncpy(mapping_guid, mapping_line, MAX_LINE_LEN - 1);
10995 mapping_guid[MAX_LINE_LEN - 1] = '\0';
10997 // get GUID from game controller mapping line: cut after GUID part
10998 mapping_start = strchr(mapping_guid, ',');
10999 if (mapping_start != NULL)
11000 *mapping_start = '\0';
11002 // cut newline from game controller mapping line
11003 mapping_end = strchr(mapping_line, '\n');
11004 if (mapping_end != NULL)
11005 *mapping_end = '\0';
11007 // add mapping entry to game controller mappings hash
11008 setHashEntry(mappings_hash, mapping_guid, mapping_line);
11011 static void LoadSetup_ReadGameControllerMappings(SetupFileHash *mappings_hash,
11016 if (!(file = fopen(filename, MODE_READ)))
11018 Warn("cannot read game controller mappings file '%s'", filename);
11023 while (!feof(file))
11025 char line[MAX_LINE_LEN];
11027 if (!fgets(line, MAX_LINE_LEN, file))
11030 addGameControllerMappingToHash(mappings_hash, line);
11036 void SaveSetup_Default(void)
11038 char *filename = getSetupFilename();
11042 InitUserDataDirectory();
11044 if (!(file = fopen(filename, MODE_WRITE)))
11046 Warn("cannot write setup file '%s'", filename);
11051 fprintFileHeader(file, SETUP_FILENAME);
11053 for (i = 0; i < ARRAY_SIZE(global_setup_tokens); i++)
11055 // just to make things nicer :)
11056 if (global_setup_tokens[i].value == &setup.multiple_users ||
11057 global_setup_tokens[i].value == &setup.sound ||
11058 global_setup_tokens[i].value == &setup.graphics_set ||
11059 global_setup_tokens[i].value == &setup.volume_simple ||
11060 global_setup_tokens[i].value == &setup.network_mode ||
11061 global_setup_tokens[i].value == &setup.touch.control_type ||
11062 global_setup_tokens[i].value == &setup.touch.grid_xsize[0] ||
11063 global_setup_tokens[i].value == &setup.touch.grid_xsize[1])
11064 fprintf(file, "\n");
11066 fprintf(file, "%s\n", getSetupLine(global_setup_tokens, "", i));
11069 for (i = 0; i < 2; i++)
11071 int grid_xsize = setup.touch.grid_xsize[i];
11072 int grid_ysize = setup.touch.grid_ysize[i];
11075 fprintf(file, "\n");
11077 for (y = 0; y < grid_ysize; y++)
11079 char token_string[MAX_LINE_LEN];
11080 char value_string[MAX_LINE_LEN];
11082 sprintf(token_string, "touch.virtual_buttons.%d.%02d", i, y);
11084 for (x = 0; x < grid_xsize; x++)
11086 char c = setup.touch.grid_button[i][x][y];
11088 value_string[x] = (c == CHAR_GRID_BUTTON_NONE ? '.' : c);
11091 value_string[grid_xsize] = '\0';
11093 fprintf(file, "%s\n", getFormattedSetupEntry(token_string, value_string));
11097 fprintf(file, "\n");
11098 for (i = 0; i < ARRAY_SIZE(editor_setup_tokens); i++)
11099 fprintf(file, "%s\n", getSetupLine(editor_setup_tokens, "", i));
11101 fprintf(file, "\n");
11102 for (i = 0; i < ARRAY_SIZE(shortcut_setup_tokens); i++)
11103 fprintf(file, "%s\n", getSetupLine(shortcut_setup_tokens, "", i));
11105 for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
11109 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
11110 fprintf(file, "\n");
11112 setup_input = setup.input[pnr];
11113 for (i = 0; i < ARRAY_SIZE(player_setup_tokens); i++)
11114 fprintf(file, "%s\n", getSetupLine(player_setup_tokens, prefix, i));
11117 fprintf(file, "\n");
11118 for (i = 0; i < ARRAY_SIZE(system_setup_tokens); i++)
11119 fprintf(file, "%s\n", getSetupLine(system_setup_tokens, "", i));
11121 // (internal setup values not saved to user setup file)
11123 fprintf(file, "\n");
11124 for (i = 0; i < ARRAY_SIZE(debug_setup_tokens); i++)
11125 if (!strPrefix(debug_setup_tokens[i].text, "debug.xsn_") ||
11126 setup.debug.xsn_mode != AUTO)
11127 fprintf(file, "%s\n", getSetupLine(debug_setup_tokens, "", i));
11129 fprintf(file, "\n");
11130 for (i = 0; i < ARRAY_SIZE(options_setup_tokens); i++)
11131 fprintf(file, "%s\n", getSetupLine(options_setup_tokens, "", i));
11135 SetFilePermissions(filename, PERMS_PRIVATE);
11138 void SaveSetup_AutoSetup(void)
11140 char *filename = getPath2(getSetupDir(), AUTOSETUP_FILENAME);
11144 InitUserDataDirectory();
11146 if (!(file = fopen(filename, MODE_WRITE)))
11148 Warn("cannot write auto setup file '%s'", filename);
11155 fprintFileHeader(file, AUTOSETUP_FILENAME);
11157 for (i = 0; i < ARRAY_SIZE(auto_setup_tokens); i++)
11158 fprintf(file, "%s\n", getSetupLine(auto_setup_tokens, "", i));
11162 SetFilePermissions(filename, PERMS_PRIVATE);
11167 void SaveSetup_ServerSetup(void)
11169 char *filename = getPath2(getSetupDir(), SERVERSETUP_FILENAME);
11173 InitUserDataDirectory();
11175 if (!(file = fopen(filename, MODE_WRITE)))
11177 Warn("cannot write server setup file '%s'", filename);
11184 fprintFileHeader(file, SERVERSETUP_FILENAME);
11186 for (i = 0; i < ARRAY_SIZE(server_setup_tokens); i++)
11188 // just to make things nicer :)
11189 if (server_setup_tokens[i].value == &setup.use_api_server)
11190 fprintf(file, "\n");
11192 fprintf(file, "%s\n", getSetupLine(server_setup_tokens, "", i));
11197 SetFilePermissions(filename, PERMS_PRIVATE);
11202 void SaveSetup_EditorCascade(void)
11204 char *filename = getPath2(getSetupDir(), EDITORCASCADE_FILENAME);
11208 InitUserDataDirectory();
11210 if (!(file = fopen(filename, MODE_WRITE)))
11212 Warn("cannot write editor cascade state file '%s'", filename);
11219 fprintFileHeader(file, EDITORCASCADE_FILENAME);
11221 for (i = 0; i < ARRAY_SIZE(editor_cascade_setup_tokens); i++)
11222 fprintf(file, "%s\n", getSetupLine(editor_cascade_setup_tokens, "", i));
11226 SetFilePermissions(filename, PERMS_PRIVATE);
11231 void SaveSetup(void)
11233 SaveSetup_Default();
11234 SaveSetup_AutoSetup();
11235 SaveSetup_ServerSetup();
11236 SaveSetup_EditorCascade();
11239 static void SaveSetup_WriteGameControllerMappings(SetupFileHash *mappings_hash,
11244 if (!(file = fopen(filename, MODE_WRITE)))
11246 Warn("cannot write game controller mappings file '%s'", filename);
11251 BEGIN_HASH_ITERATION(mappings_hash, itr)
11253 fprintf(file, "%s\n", HASH_ITERATION_VALUE(itr));
11255 END_HASH_ITERATION(mappings_hash, itr)
11260 void SaveSetup_AddGameControllerMapping(char *mapping)
11262 char *filename = getPath2(getSetupDir(), GAMECONTROLLER_BASENAME);
11263 SetupFileHash *mappings_hash = newSetupFileHash();
11265 InitUserDataDirectory();
11267 // load existing personal game controller mappings
11268 LoadSetup_ReadGameControllerMappings(mappings_hash, filename);
11270 // add new mapping to personal game controller mappings
11271 addGameControllerMappingToHash(mappings_hash, mapping);
11273 // save updated personal game controller mappings
11274 SaveSetup_WriteGameControllerMappings(mappings_hash, filename);
11276 freeSetupFileHash(mappings_hash);
11280 void LoadCustomElementDescriptions(void)
11282 char *filename = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
11283 SetupFileHash *setup_file_hash;
11286 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
11288 if (element_info[i].custom_description != NULL)
11290 free(element_info[i].custom_description);
11291 element_info[i].custom_description = NULL;
11295 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
11298 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
11300 char *token = getStringCat2(element_info[i].token_name, ".name");
11301 char *value = getHashEntry(setup_file_hash, token);
11304 element_info[i].custom_description = getStringCopy(value);
11309 freeSetupFileHash(setup_file_hash);
11312 static int getElementFromToken(char *token)
11314 char *value = getHashEntry(element_token_hash, token);
11317 return atoi(value);
11319 Warn("unknown element token '%s'", token);
11321 return EL_UNDEFINED;
11324 void FreeGlobalAnimEventInfo(void)
11326 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
11328 if (gaei->event_list == NULL)
11333 for (i = 0; i < gaei->num_event_lists; i++)
11335 checked_free(gaei->event_list[i]->event_value);
11336 checked_free(gaei->event_list[i]);
11339 checked_free(gaei->event_list);
11341 gaei->event_list = NULL;
11342 gaei->num_event_lists = 0;
11345 static int AddGlobalAnimEventList(void)
11347 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
11348 int list_pos = gaei->num_event_lists++;
11350 gaei->event_list = checked_realloc(gaei->event_list, gaei->num_event_lists *
11351 sizeof(struct GlobalAnimEventListInfo *));
11353 gaei->event_list[list_pos] =
11354 checked_calloc(sizeof(struct GlobalAnimEventListInfo));
11356 struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
11358 gaeli->event_value = NULL;
11359 gaeli->num_event_values = 0;
11364 static int AddGlobalAnimEventValue(int list_pos, int event_value)
11366 // do not add empty global animation events
11367 if (event_value == ANIM_EVENT_NONE)
11370 // if list position is undefined, create new list
11371 if (list_pos == ANIM_EVENT_UNDEFINED)
11372 list_pos = AddGlobalAnimEventList();
11374 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
11375 struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
11376 int value_pos = gaeli->num_event_values++;
11378 gaeli->event_value = checked_realloc(gaeli->event_value,
11379 gaeli->num_event_values * sizeof(int *));
11381 gaeli->event_value[value_pos] = event_value;
11386 int GetGlobalAnimEventValue(int list_pos, int value_pos)
11388 if (list_pos == ANIM_EVENT_UNDEFINED)
11389 return ANIM_EVENT_NONE;
11391 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
11392 struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
11394 return gaeli->event_value[value_pos];
11397 int GetGlobalAnimEventValueCount(int list_pos)
11399 if (list_pos == ANIM_EVENT_UNDEFINED)
11402 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
11403 struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
11405 return gaeli->num_event_values;
11408 // This function checks if a string <s> of the format "string1, string2, ..."
11409 // exactly contains a string <s_contained>.
11411 static boolean string_has_parameter(char *s, char *s_contained)
11415 if (s == NULL || s_contained == NULL)
11418 if (strlen(s_contained) > strlen(s))
11421 if (strncmp(s, s_contained, strlen(s_contained)) == 0)
11423 char next_char = s[strlen(s_contained)];
11425 // check if next character is delimiter or whitespace
11426 return (next_char == ',' || next_char == '\0' ||
11427 next_char == ' ' || next_char == '\t' ? TRUE : FALSE);
11430 // check if string contains another parameter string after a comma
11431 substring = strchr(s, ',');
11432 if (substring == NULL) // string does not contain a comma
11435 // advance string pointer to next character after the comma
11438 // skip potential whitespaces after the comma
11439 while (*substring == ' ' || *substring == '\t')
11442 return string_has_parameter(substring, s_contained);
11445 static int get_anim_parameter_value(char *s)
11447 int event_value[] =
11455 char *pattern_1[] =
11463 char *pattern_2 = ".part_";
11464 char *matching_char = NULL;
11466 int pattern_1_len = 0;
11467 int result = ANIM_EVENT_NONE;
11470 for (i = 0; i < ARRAY_SIZE(event_value); i++)
11472 matching_char = strstr(s_ptr, pattern_1[i]);
11473 pattern_1_len = strlen(pattern_1[i]);
11474 result = event_value[i];
11476 if (matching_char != NULL)
11480 if (matching_char == NULL)
11481 return ANIM_EVENT_NONE;
11483 s_ptr = matching_char + pattern_1_len;
11485 // check for main animation number ("anim_X" or "anim_XX")
11486 if (*s_ptr >= '0' && *s_ptr <= '9')
11488 int gic_anim_nr = (*s_ptr++ - '0');
11490 if (*s_ptr >= '0' && *s_ptr <= '9')
11491 gic_anim_nr = 10 * gic_anim_nr + (*s_ptr++ - '0');
11493 if (gic_anim_nr < 1 || gic_anim_nr > MAX_GLOBAL_ANIMS)
11494 return ANIM_EVENT_NONE;
11496 result |= gic_anim_nr << ANIM_EVENT_ANIM_BIT;
11500 // invalid main animation number specified
11502 return ANIM_EVENT_NONE;
11505 // check for animation part number ("part_X" or "part_XX") (optional)
11506 if (strPrefix(s_ptr, pattern_2))
11508 s_ptr += strlen(pattern_2);
11510 if (*s_ptr >= '0' && *s_ptr <= '9')
11512 int gic_part_nr = (*s_ptr++ - '0');
11514 if (*s_ptr >= '0' && *s_ptr <= '9')
11515 gic_part_nr = 10 * gic_part_nr + (*s_ptr++ - '0');
11517 if (gic_part_nr < 1 || gic_part_nr > MAX_GLOBAL_ANIM_PARTS)
11518 return ANIM_EVENT_NONE;
11520 result |= gic_part_nr << ANIM_EVENT_PART_BIT;
11524 // invalid animation part number specified
11526 return ANIM_EVENT_NONE;
11530 // discard result if next character is neither delimiter nor whitespace
11531 if (!(*s_ptr == ',' || *s_ptr == '\0' ||
11532 *s_ptr == ' ' || *s_ptr == '\t'))
11533 return ANIM_EVENT_NONE;
11538 static int get_anim_parameter_values(char *s)
11540 int list_pos = ANIM_EVENT_UNDEFINED;
11541 int event_value = ANIM_EVENT_DEFAULT;
11543 if (string_has_parameter(s, "any"))
11544 event_value |= ANIM_EVENT_ANY;
11546 if (string_has_parameter(s, "click:self") ||
11547 string_has_parameter(s, "click") ||
11548 string_has_parameter(s, "self"))
11549 event_value |= ANIM_EVENT_SELF;
11551 if (string_has_parameter(s, "unclick:any"))
11552 event_value |= ANIM_EVENT_UNCLICK_ANY;
11554 // if animation event found, add it to global animation event list
11555 if (event_value != ANIM_EVENT_NONE)
11556 list_pos = AddGlobalAnimEventValue(list_pos, event_value);
11560 // add optional "click:anim_X" or "click:anim_X.part_X" parameter
11561 event_value = get_anim_parameter_value(s);
11563 // if animation event found, add it to global animation event list
11564 if (event_value != ANIM_EVENT_NONE)
11565 list_pos = AddGlobalAnimEventValue(list_pos, event_value);
11567 // continue with next part of the string, starting with next comma
11568 s = strchr(s + 1, ',');
11574 static int get_anim_action_parameter_value(char *token)
11576 // check most common default case first to massively speed things up
11577 if (strEqual(token, ARG_UNDEFINED))
11578 return ANIM_EVENT_ACTION_NONE;
11580 int result = getImageIDFromToken(token);
11584 char *gfx_token = getStringCat2("gfx.", token);
11586 result = getImageIDFromToken(gfx_token);
11588 checked_free(gfx_token);
11593 Key key = getKeyFromX11KeyName(token);
11595 if (key != KSYM_UNDEFINED)
11596 result = -(int)key;
11603 result = get_hash_from_key(token); // unsigned int => int
11604 result = ABS(result); // may be negative now
11605 result += (result < MAX_IMAGE_FILES ? MAX_IMAGE_FILES : 0);
11607 setHashEntry(anim_url_hash, int2str(result, 0), token);
11612 result = ANIM_EVENT_ACTION_NONE;
11617 int get_parameter_value(char *value_raw, char *suffix, int type)
11619 char *value = getStringToLower(value_raw);
11620 int result = 0; // probably a save default value
11622 if (strEqual(suffix, ".direction"))
11624 result = (strEqual(value, "left") ? MV_LEFT :
11625 strEqual(value, "right") ? MV_RIGHT :
11626 strEqual(value, "up") ? MV_UP :
11627 strEqual(value, "down") ? MV_DOWN : MV_NONE);
11629 else if (strEqual(suffix, ".position"))
11631 result = (strEqual(value, "left") ? POS_LEFT :
11632 strEqual(value, "right") ? POS_RIGHT :
11633 strEqual(value, "top") ? POS_TOP :
11634 strEqual(value, "upper") ? POS_UPPER :
11635 strEqual(value, "middle") ? POS_MIDDLE :
11636 strEqual(value, "lower") ? POS_LOWER :
11637 strEqual(value, "bottom") ? POS_BOTTOM :
11638 strEqual(value, "any") ? POS_ANY :
11639 strEqual(value, "last") ? POS_LAST : POS_UNDEFINED);
11641 else if (strEqual(suffix, ".align"))
11643 result = (strEqual(value, "left") ? ALIGN_LEFT :
11644 strEqual(value, "right") ? ALIGN_RIGHT :
11645 strEqual(value, "center") ? ALIGN_CENTER :
11646 strEqual(value, "middle") ? ALIGN_CENTER : ALIGN_DEFAULT);
11648 else if (strEqual(suffix, ".valign"))
11650 result = (strEqual(value, "top") ? VALIGN_TOP :
11651 strEqual(value, "bottom") ? VALIGN_BOTTOM :
11652 strEqual(value, "middle") ? VALIGN_MIDDLE :
11653 strEqual(value, "center") ? VALIGN_MIDDLE : VALIGN_DEFAULT);
11655 else if (strEqual(suffix, ".anim_mode"))
11657 result = (string_has_parameter(value, "none") ? ANIM_NONE :
11658 string_has_parameter(value, "loop") ? ANIM_LOOP :
11659 string_has_parameter(value, "linear") ? ANIM_LINEAR :
11660 string_has_parameter(value, "pingpong") ? ANIM_PINGPONG :
11661 string_has_parameter(value, "pingpong2") ? ANIM_PINGPONG2 :
11662 string_has_parameter(value, "random") ? ANIM_RANDOM :
11663 string_has_parameter(value, "random_static") ? ANIM_RANDOM_STATIC :
11664 string_has_parameter(value, "ce_value") ? ANIM_CE_VALUE :
11665 string_has_parameter(value, "ce_score") ? ANIM_CE_SCORE :
11666 string_has_parameter(value, "ce_delay") ? ANIM_CE_DELAY :
11667 string_has_parameter(value, "horizontal") ? ANIM_HORIZONTAL :
11668 string_has_parameter(value, "vertical") ? ANIM_VERTICAL :
11669 string_has_parameter(value, "centered") ? ANIM_CENTERED :
11670 string_has_parameter(value, "all") ? ANIM_ALL :
11671 string_has_parameter(value, "tiled") ? ANIM_TILED :
11674 if (string_has_parameter(value, "once"))
11675 result |= ANIM_ONCE;
11677 if (string_has_parameter(value, "reverse"))
11678 result |= ANIM_REVERSE;
11680 if (string_has_parameter(value, "opaque_player"))
11681 result |= ANIM_OPAQUE_PLAYER;
11683 if (string_has_parameter(value, "static_panel"))
11684 result |= ANIM_STATIC_PANEL;
11686 else if (strEqual(suffix, ".init_event") ||
11687 strEqual(suffix, ".anim_event"))
11689 result = get_anim_parameter_values(value);
11691 else if (strEqual(suffix, ".init_delay_action") ||
11692 strEqual(suffix, ".anim_delay_action") ||
11693 strEqual(suffix, ".post_delay_action") ||
11694 strEqual(suffix, ".init_event_action") ||
11695 strEqual(suffix, ".anim_event_action"))
11697 result = get_anim_action_parameter_value(value_raw);
11699 else if (strEqual(suffix, ".class"))
11701 result = (strEqual(value, ARG_UNDEFINED) ? ARG_UNDEFINED_VALUE :
11702 get_hash_from_key(value));
11704 else if (strEqual(suffix, ".style"))
11706 result = STYLE_DEFAULT;
11708 if (string_has_parameter(value, "accurate_borders"))
11709 result |= STYLE_ACCURATE_BORDERS;
11711 if (string_has_parameter(value, "inner_corners"))
11712 result |= STYLE_INNER_CORNERS;
11714 if (string_has_parameter(value, "reverse"))
11715 result |= STYLE_REVERSE;
11717 if (string_has_parameter(value, "leftmost_position"))
11718 result |= STYLE_LEFTMOST_POSITION;
11720 if (string_has_parameter(value, "block_clicks"))
11721 result |= STYLE_BLOCK;
11723 if (string_has_parameter(value, "passthrough_clicks"))
11724 result |= STYLE_PASSTHROUGH;
11726 if (string_has_parameter(value, "multiple_actions"))
11727 result |= STYLE_MULTIPLE_ACTIONS;
11729 else if (strEqual(suffix, ".fade_mode"))
11731 result = (string_has_parameter(value, "none") ? FADE_MODE_NONE :
11732 string_has_parameter(value, "fade") ? FADE_MODE_FADE :
11733 string_has_parameter(value, "crossfade") ? FADE_MODE_CROSSFADE :
11734 string_has_parameter(value, "melt") ? FADE_MODE_MELT :
11735 string_has_parameter(value, "curtain") ? FADE_MODE_CURTAIN :
11736 FADE_MODE_DEFAULT);
11738 else if (strEqual(suffix, ".auto_delay_unit"))
11740 result = (string_has_parameter(value, "ms") ? AUTO_DELAY_UNIT_MS :
11741 string_has_parameter(value, "frames") ? AUTO_DELAY_UNIT_FRAMES :
11742 AUTO_DELAY_UNIT_DEFAULT);
11744 else if (strPrefix(suffix, ".font")) // (may also be ".font_xyz")
11746 result = gfx.get_font_from_token_function(value);
11748 else // generic parameter of type integer or boolean
11750 result = (strEqual(value, ARG_UNDEFINED) ? ARG_UNDEFINED_VALUE :
11751 type == TYPE_INTEGER ? get_integer_from_string(value) :
11752 type == TYPE_BOOLEAN ? get_boolean_from_string(value) :
11753 ARG_UNDEFINED_VALUE);
11761 static int get_token_parameter_value(char *token, char *value_raw)
11765 if (token == NULL || value_raw == NULL)
11766 return ARG_UNDEFINED_VALUE;
11768 suffix = strrchr(token, '.');
11769 if (suffix == NULL)
11772 if (strEqual(suffix, ".element"))
11773 return getElementFromToken(value_raw);
11775 // !!! USE CORRECT VALUE TYPE (currently works also for TYPE_BOOLEAN) !!!
11776 return get_parameter_value(value_raw, suffix, TYPE_INTEGER);
11779 void InitMenuDesignSettings_FromHash(SetupFileHash *setup_file_hash,
11780 boolean ignore_defaults)
11784 for (i = 0; image_config_vars[i].token != NULL; i++)
11786 char *value = getHashEntry(setup_file_hash, image_config_vars[i].token);
11788 // (ignore definitions set to "[DEFAULT]" which are already initialized)
11789 if (ignore_defaults && strEqual(value, ARG_DEFAULT))
11793 *image_config_vars[i].value =
11794 get_token_parameter_value(image_config_vars[i].token, value);
11798 void InitMenuDesignSettings_Static(void)
11800 // always start with reliable default values from static default config
11801 InitMenuDesignSettings_FromHash(image_config_hash, FALSE);
11804 static void InitMenuDesignSettings_SpecialPreProcessing(void)
11808 // the following initializes hierarchical values from static configuration
11810 // special case: initialize "ARG_DEFAULT" values in static default config
11811 // (e.g., initialize "[titlemessage].fade_mode" from "[title].fade_mode")
11812 titlescreen_initial_first_default.fade_mode =
11813 title_initial_first_default.fade_mode;
11814 titlescreen_initial_first_default.fade_delay =
11815 title_initial_first_default.fade_delay;
11816 titlescreen_initial_first_default.post_delay =
11817 title_initial_first_default.post_delay;
11818 titlescreen_initial_first_default.auto_delay =
11819 title_initial_first_default.auto_delay;
11820 titlescreen_initial_first_default.auto_delay_unit =
11821 title_initial_first_default.auto_delay_unit;
11822 titlescreen_first_default.fade_mode = title_first_default.fade_mode;
11823 titlescreen_first_default.fade_delay = title_first_default.fade_delay;
11824 titlescreen_first_default.post_delay = title_first_default.post_delay;
11825 titlescreen_first_default.auto_delay = title_first_default.auto_delay;
11826 titlescreen_first_default.auto_delay_unit =
11827 title_first_default.auto_delay_unit;
11828 titlemessage_initial_first_default.fade_mode =
11829 title_initial_first_default.fade_mode;
11830 titlemessage_initial_first_default.fade_delay =
11831 title_initial_first_default.fade_delay;
11832 titlemessage_initial_first_default.post_delay =
11833 title_initial_first_default.post_delay;
11834 titlemessage_initial_first_default.auto_delay =
11835 title_initial_first_default.auto_delay;
11836 titlemessage_initial_first_default.auto_delay_unit =
11837 title_initial_first_default.auto_delay_unit;
11838 titlemessage_first_default.fade_mode = title_first_default.fade_mode;
11839 titlemessage_first_default.fade_delay = title_first_default.fade_delay;
11840 titlemessage_first_default.post_delay = title_first_default.post_delay;
11841 titlemessage_first_default.auto_delay = title_first_default.auto_delay;
11842 titlemessage_first_default.auto_delay_unit =
11843 title_first_default.auto_delay_unit;
11845 titlescreen_initial_default.fade_mode = title_initial_default.fade_mode;
11846 titlescreen_initial_default.fade_delay = title_initial_default.fade_delay;
11847 titlescreen_initial_default.post_delay = title_initial_default.post_delay;
11848 titlescreen_initial_default.auto_delay = title_initial_default.auto_delay;
11849 titlescreen_initial_default.auto_delay_unit =
11850 title_initial_default.auto_delay_unit;
11851 titlescreen_default.fade_mode = title_default.fade_mode;
11852 titlescreen_default.fade_delay = title_default.fade_delay;
11853 titlescreen_default.post_delay = title_default.post_delay;
11854 titlescreen_default.auto_delay = title_default.auto_delay;
11855 titlescreen_default.auto_delay_unit = title_default.auto_delay_unit;
11856 titlemessage_initial_default.fade_mode = title_initial_default.fade_mode;
11857 titlemessage_initial_default.fade_delay = title_initial_default.fade_delay;
11858 titlemessage_initial_default.post_delay = title_initial_default.post_delay;
11859 titlemessage_initial_default.auto_delay_unit =
11860 title_initial_default.auto_delay_unit;
11861 titlemessage_default.fade_mode = title_default.fade_mode;
11862 titlemessage_default.fade_delay = title_default.fade_delay;
11863 titlemessage_default.post_delay = title_default.post_delay;
11864 titlemessage_default.auto_delay = title_default.auto_delay;
11865 titlemessage_default.auto_delay_unit = title_default.auto_delay_unit;
11867 // special case: initialize "ARG_DEFAULT" values in static default config
11868 // (e.g., init "titlemessage_1.fade_mode" from "[titlemessage].fade_mode")
11869 for (i = 0; i < MAX_NUM_TITLE_MESSAGES; i++)
11871 titlescreen_initial_first[i] = titlescreen_initial_first_default;
11872 titlescreen_first[i] = titlescreen_first_default;
11873 titlemessage_initial_first[i] = titlemessage_initial_first_default;
11874 titlemessage_first[i] = titlemessage_first_default;
11876 titlescreen_initial[i] = titlescreen_initial_default;
11877 titlescreen[i] = titlescreen_default;
11878 titlemessage_initial[i] = titlemessage_initial_default;
11879 titlemessage[i] = titlemessage_default;
11882 // special case: initialize "ARG_DEFAULT" values in static default config
11883 // (eg, init "menu.enter_screen.SCORES.xyz" from "menu.enter_screen.xyz")
11884 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
11886 if (i == GFX_SPECIAL_ARG_TITLE) // title values already initialized
11889 menu.enter_screen[i] = menu.enter_screen[GFX_SPECIAL_ARG_DEFAULT];
11890 menu.leave_screen[i] = menu.leave_screen[GFX_SPECIAL_ARG_DEFAULT];
11891 menu.next_screen[i] = menu.next_screen[GFX_SPECIAL_ARG_DEFAULT];
11894 // special case: initialize "ARG_DEFAULT" values in static default config
11895 // (eg, init "viewport.door_1.MAIN.xyz" from "viewport.door_1.xyz")
11896 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
11898 viewport.window[i] = viewport.window[GFX_SPECIAL_ARG_DEFAULT];
11899 viewport.playfield[i] = viewport.playfield[GFX_SPECIAL_ARG_DEFAULT];
11900 viewport.door_1[i] = viewport.door_1[GFX_SPECIAL_ARG_DEFAULT];
11902 if (i == GFX_SPECIAL_ARG_EDITOR) // editor values already initialized
11905 viewport.door_2[i] = viewport.door_2[GFX_SPECIAL_ARG_DEFAULT];
11909 static void InitMenuDesignSettings_SpecialPostProcessing(void)
11913 struct XY *dst, *src;
11915 game_buttons_xy[] =
11917 { &game.button.save, &game.button.stop },
11918 { &game.button.pause2, &game.button.pause },
11919 { &game.button.load, &game.button.play },
11920 { &game.button.undo, &game.button.stop },
11921 { &game.button.redo, &game.button.play },
11927 // special case: initialize later added SETUP list size from LEVELS value
11928 if (menu.list_size[GAME_MODE_SETUP] == -1)
11929 menu.list_size[GAME_MODE_SETUP] = menu.list_size[GAME_MODE_LEVELS];
11931 // set default position for snapshot buttons to stop/pause/play buttons
11932 for (i = 0; game_buttons_xy[i].dst != NULL; i++)
11933 if ((*game_buttons_xy[i].dst).x == -1 &&
11934 (*game_buttons_xy[i].dst).y == -1)
11935 *game_buttons_xy[i].dst = *game_buttons_xy[i].src;
11937 // --------------------------------------------------------------------------
11938 // dynamic viewports (including playfield margins, borders and alignments)
11939 // --------------------------------------------------------------------------
11941 // dynamic viewports currently only supported for landscape mode
11942 int display_width = MAX(video.display_width, video.display_height);
11943 int display_height = MIN(video.display_width, video.display_height);
11945 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
11947 struct RectWithBorder *vp_window = &viewport.window[i];
11948 struct RectWithBorder *vp_playfield = &viewport.playfield[i];
11949 struct RectWithBorder *vp_door_1 = &viewport.door_1[i];
11950 struct RectWithBorder *vp_door_2 = &viewport.door_2[i];
11951 boolean dynamic_window_width = (vp_window->min_width != -1);
11952 boolean dynamic_window_height = (vp_window->min_height != -1);
11953 boolean dynamic_playfield_width = (vp_playfield->min_width != -1);
11954 boolean dynamic_playfield_height = (vp_playfield->min_height != -1);
11956 // adjust window size if min/max width/height is specified
11958 if (vp_window->min_width != -1)
11960 int window_width = display_width;
11962 // when using static window height, use aspect ratio of display
11963 if (vp_window->min_height == -1)
11964 window_width = vp_window->height * display_width / display_height;
11966 vp_window->width = MAX(vp_window->min_width, window_width);
11969 if (vp_window->min_height != -1)
11971 int window_height = display_height;
11973 // when using static window width, use aspect ratio of display
11974 if (vp_window->min_width == -1)
11975 window_height = vp_window->width * display_height / display_width;
11977 vp_window->height = MAX(vp_window->min_height, window_height);
11980 if (vp_window->max_width != -1)
11981 vp_window->width = MIN(vp_window->width, vp_window->max_width);
11983 if (vp_window->max_height != -1)
11984 vp_window->height = MIN(vp_window->height, vp_window->max_height);
11986 int playfield_width = vp_window->width;
11987 int playfield_height = vp_window->height;
11989 // adjust playfield size and position according to specified margins
11991 playfield_width -= vp_playfield->margin_left;
11992 playfield_width -= vp_playfield->margin_right;
11994 playfield_height -= vp_playfield->margin_top;
11995 playfield_height -= vp_playfield->margin_bottom;
11997 // adjust playfield size if min/max width/height is specified
11999 if (vp_playfield->min_width != -1)
12000 vp_playfield->width = MAX(vp_playfield->min_width, playfield_width);
12002 if (vp_playfield->min_height != -1)
12003 vp_playfield->height = MAX(vp_playfield->min_height, playfield_height);
12005 if (vp_playfield->max_width != -1)
12006 vp_playfield->width = MIN(vp_playfield->width, vp_playfield->max_width);
12008 if (vp_playfield->max_height != -1)
12009 vp_playfield->height = MIN(vp_playfield->height,vp_playfield->max_height);
12011 // adjust playfield position according to specified alignment
12013 if (vp_playfield->align == ALIGN_LEFT || vp_playfield->x > 0)
12014 vp_playfield->x = ALIGNED_VP_XPOS(vp_playfield);
12015 else if (vp_playfield->align == ALIGN_CENTER)
12016 vp_playfield->x = playfield_width / 2 - vp_playfield->width / 2;
12017 else if (vp_playfield->align == ALIGN_RIGHT)
12018 vp_playfield->x += playfield_width - vp_playfield->width;
12020 if (vp_playfield->valign == VALIGN_TOP || vp_playfield->y > 0)
12021 vp_playfield->y = ALIGNED_VP_YPOS(vp_playfield);
12022 else if (vp_playfield->valign == VALIGN_MIDDLE)
12023 vp_playfield->y = playfield_height / 2 - vp_playfield->height / 2;
12024 else if (vp_playfield->valign == VALIGN_BOTTOM)
12025 vp_playfield->y += playfield_height - vp_playfield->height;
12027 vp_playfield->x += vp_playfield->margin_left;
12028 vp_playfield->y += vp_playfield->margin_top;
12030 // adjust individual playfield borders if only default border is specified
12032 if (vp_playfield->border_left == -1)
12033 vp_playfield->border_left = vp_playfield->border_size;
12034 if (vp_playfield->border_right == -1)
12035 vp_playfield->border_right = vp_playfield->border_size;
12036 if (vp_playfield->border_top == -1)
12037 vp_playfield->border_top = vp_playfield->border_size;
12038 if (vp_playfield->border_bottom == -1)
12039 vp_playfield->border_bottom = vp_playfield->border_size;
12041 // set dynamic playfield borders if borders are specified as undefined
12042 // (but only if window size was dynamic and playfield size was static)
12044 if (dynamic_window_width && !dynamic_playfield_width)
12046 if (vp_playfield->border_left == -1)
12048 vp_playfield->border_left = (vp_playfield->x -
12049 vp_playfield->margin_left);
12050 vp_playfield->x -= vp_playfield->border_left;
12051 vp_playfield->width += vp_playfield->border_left;
12054 if (vp_playfield->border_right == -1)
12056 vp_playfield->border_right = (vp_window->width -
12058 vp_playfield->width -
12059 vp_playfield->margin_right);
12060 vp_playfield->width += vp_playfield->border_right;
12064 if (dynamic_window_height && !dynamic_playfield_height)
12066 if (vp_playfield->border_top == -1)
12068 vp_playfield->border_top = (vp_playfield->y -
12069 vp_playfield->margin_top);
12070 vp_playfield->y -= vp_playfield->border_top;
12071 vp_playfield->height += vp_playfield->border_top;
12074 if (vp_playfield->border_bottom == -1)
12076 vp_playfield->border_bottom = (vp_window->height -
12078 vp_playfield->height -
12079 vp_playfield->margin_bottom);
12080 vp_playfield->height += vp_playfield->border_bottom;
12084 // adjust playfield size to be a multiple of a defined alignment tile size
12086 int align_size = vp_playfield->align_size;
12087 int playfield_xtiles = vp_playfield->width / align_size;
12088 int playfield_ytiles = vp_playfield->height / align_size;
12089 int playfield_width_corrected = playfield_xtiles * align_size;
12090 int playfield_height_corrected = playfield_ytiles * align_size;
12091 boolean is_playfield_mode = (i == GFX_SPECIAL_ARG_PLAYING ||
12092 i == GFX_SPECIAL_ARG_EDITOR);
12094 if (is_playfield_mode &&
12095 dynamic_playfield_width &&
12096 vp_playfield->width != playfield_width_corrected)
12098 int playfield_xdiff = vp_playfield->width - playfield_width_corrected;
12100 vp_playfield->width = playfield_width_corrected;
12102 if (vp_playfield->align == ALIGN_LEFT)
12104 vp_playfield->border_left += playfield_xdiff;
12106 else if (vp_playfield->align == ALIGN_RIGHT)
12108 vp_playfield->border_right += playfield_xdiff;
12110 else if (vp_playfield->align == ALIGN_CENTER)
12112 int border_left_diff = playfield_xdiff / 2;
12113 int border_right_diff = playfield_xdiff - border_left_diff;
12115 vp_playfield->border_left += border_left_diff;
12116 vp_playfield->border_right += border_right_diff;
12120 if (is_playfield_mode &&
12121 dynamic_playfield_height &&
12122 vp_playfield->height != playfield_height_corrected)
12124 int playfield_ydiff = vp_playfield->height - playfield_height_corrected;
12126 vp_playfield->height = playfield_height_corrected;
12128 if (vp_playfield->valign == VALIGN_TOP)
12130 vp_playfield->border_top += playfield_ydiff;
12132 else if (vp_playfield->align == VALIGN_BOTTOM)
12134 vp_playfield->border_right += playfield_ydiff;
12136 else if (vp_playfield->align == VALIGN_MIDDLE)
12138 int border_top_diff = playfield_ydiff / 2;
12139 int border_bottom_diff = playfield_ydiff - border_top_diff;
12141 vp_playfield->border_top += border_top_diff;
12142 vp_playfield->border_bottom += border_bottom_diff;
12146 // adjust door positions according to specified alignment
12148 for (j = 0; j < 2; j++)
12150 struct RectWithBorder *vp_door = (j == 0 ? vp_door_1 : vp_door_2);
12152 if (vp_door->align == ALIGN_LEFT || vp_door->x > 0)
12153 vp_door->x = ALIGNED_VP_XPOS(vp_door);
12154 else if (vp_door->align == ALIGN_CENTER)
12155 vp_door->x = vp_window->width / 2 - vp_door->width / 2;
12156 else if (vp_door->align == ALIGN_RIGHT)
12157 vp_door->x += vp_window->width - vp_door->width;
12159 if (vp_door->valign == VALIGN_TOP || vp_door->y > 0)
12160 vp_door->y = ALIGNED_VP_YPOS(vp_door);
12161 else if (vp_door->valign == VALIGN_MIDDLE)
12162 vp_door->y = vp_window->height / 2 - vp_door->height / 2;
12163 else if (vp_door->valign == VALIGN_BOTTOM)
12164 vp_door->y += vp_window->height - vp_door->height;
12169 static void InitMenuDesignSettings_SpecialPostProcessing_AfterGraphics(void)
12173 struct XYTileSize *dst, *src;
12176 editor_buttons_xy[] =
12179 &editor.button.element_left, &editor.palette.element_left,
12180 IMG_GFX_EDITOR_BUTTON_ELEMENT_LEFT
12183 &editor.button.element_middle, &editor.palette.element_middle,
12184 IMG_GFX_EDITOR_BUTTON_ELEMENT_MIDDLE
12187 &editor.button.element_right, &editor.palette.element_right,
12188 IMG_GFX_EDITOR_BUTTON_ELEMENT_RIGHT
12195 // set default position for element buttons to element graphics
12196 for (i = 0; editor_buttons_xy[i].dst != NULL; i++)
12198 if ((*editor_buttons_xy[i].dst).x == -1 &&
12199 (*editor_buttons_xy[i].dst).y == -1)
12201 struct GraphicInfo *gd = &graphic_info[editor_buttons_xy[i].graphic];
12203 gd->width = gd->height = editor_buttons_xy[i].src->tile_size;
12205 *editor_buttons_xy[i].dst = *editor_buttons_xy[i].src;
12209 // adjust editor palette rows and columns if specified to be dynamic
12211 if (editor.palette.cols == -1)
12213 int vp_width = viewport.playfield[GFX_SPECIAL_ARG_EDITOR].width;
12214 int bt_width = graphic_info[IMG_EDITOR_PALETTE_BUTTON].width;
12215 int sc_width = graphic_info[IMG_EDITOR_PALETTE_SCROLLBAR].width;
12217 editor.palette.cols = (vp_width - sc_width) / bt_width;
12219 if (editor.palette.x == -1)
12221 int palette_width = editor.palette.cols * bt_width + sc_width;
12223 editor.palette.x = (vp_width - palette_width) / 2;
12227 if (editor.palette.rows == -1)
12229 int vp_height = viewport.playfield[GFX_SPECIAL_ARG_EDITOR].height;
12230 int bt_height = graphic_info[IMG_EDITOR_PALETTE_BUTTON].height;
12231 int tx_height = getFontHeight(FONT_TEXT_2);
12233 editor.palette.rows = (vp_height - tx_height) / bt_height;
12235 if (editor.palette.y == -1)
12237 int palette_height = editor.palette.rows * bt_height + tx_height;
12239 editor.palette.y = (vp_height - palette_height) / 2;
12244 static void LoadMenuDesignSettingsFromFilename(char *filename)
12246 static struct TitleFadingInfo tfi;
12247 static struct TitleMessageInfo tmi;
12248 static struct TokenInfo title_tokens[] =
12250 { TYPE_INTEGER, &tfi.fade_mode, ".fade_mode" },
12251 { TYPE_INTEGER, &tfi.fade_delay, ".fade_delay" },
12252 { TYPE_INTEGER, &tfi.post_delay, ".post_delay" },
12253 { TYPE_INTEGER, &tfi.auto_delay, ".auto_delay" },
12254 { TYPE_INTEGER, &tfi.auto_delay_unit, ".auto_delay_unit" },
12258 static struct TokenInfo titlemessage_tokens[] =
12260 { TYPE_INTEGER, &tmi.x, ".x" },
12261 { TYPE_INTEGER, &tmi.y, ".y" },
12262 { TYPE_INTEGER, &tmi.width, ".width" },
12263 { TYPE_INTEGER, &tmi.height, ".height" },
12264 { TYPE_INTEGER, &tmi.chars, ".chars" },
12265 { TYPE_INTEGER, &tmi.lines, ".lines" },
12266 { TYPE_INTEGER, &tmi.align, ".align" },
12267 { TYPE_INTEGER, &tmi.valign, ".valign" },
12268 { TYPE_INTEGER, &tmi.font, ".font" },
12269 { TYPE_BOOLEAN, &tmi.autowrap, ".autowrap" },
12270 { TYPE_BOOLEAN, &tmi.centered, ".centered" },
12271 { TYPE_BOOLEAN, &tmi.parse_comments, ".parse_comments" },
12272 { TYPE_INTEGER, &tmi.sort_priority, ".sort_priority" },
12273 { TYPE_INTEGER, &tmi.fade_mode, ".fade_mode" },
12274 { TYPE_INTEGER, &tmi.fade_delay, ".fade_delay" },
12275 { TYPE_INTEGER, &tmi.post_delay, ".post_delay" },
12276 { TYPE_INTEGER, &tmi.auto_delay, ".auto_delay" },
12277 { TYPE_INTEGER, &tmi.auto_delay_unit, ".auto_delay_unit" },
12283 struct TitleFadingInfo *info;
12288 // initialize first titles from "enter screen" definitions, if defined
12289 { &title_initial_first_default, "menu.enter_screen.TITLE" },
12290 { &title_first_default, "menu.enter_screen.TITLE" },
12292 // initialize title screens from "next screen" definitions, if defined
12293 { &title_initial_default, "menu.next_screen.TITLE" },
12294 { &title_default, "menu.next_screen.TITLE" },
12300 struct TitleMessageInfo *array;
12303 titlemessage_arrays[] =
12305 // initialize first titles from "enter screen" definitions, if defined
12306 { titlescreen_initial_first, "menu.enter_screen.TITLE" },
12307 { titlescreen_first, "menu.enter_screen.TITLE" },
12308 { titlemessage_initial_first, "menu.enter_screen.TITLE" },
12309 { titlemessage_first, "menu.enter_screen.TITLE" },
12311 // initialize titles from "next screen" definitions, if defined
12312 { titlescreen_initial, "menu.next_screen.TITLE" },
12313 { titlescreen, "menu.next_screen.TITLE" },
12314 { titlemessage_initial, "menu.next_screen.TITLE" },
12315 { titlemessage, "menu.next_screen.TITLE" },
12317 // overwrite titles with title definitions, if defined
12318 { titlescreen_initial_first, "[title_initial]" },
12319 { titlescreen_first, "[title]" },
12320 { titlemessage_initial_first, "[title_initial]" },
12321 { titlemessage_first, "[title]" },
12323 { titlescreen_initial, "[title_initial]" },
12324 { titlescreen, "[title]" },
12325 { titlemessage_initial, "[title_initial]" },
12326 { titlemessage, "[title]" },
12328 // overwrite titles with title screen/message definitions, if defined
12329 { titlescreen_initial_first, "[titlescreen_initial]" },
12330 { titlescreen_first, "[titlescreen]" },
12331 { titlemessage_initial_first, "[titlemessage_initial]" },
12332 { titlemessage_first, "[titlemessage]" },
12334 { titlescreen_initial, "[titlescreen_initial]" },
12335 { titlescreen, "[titlescreen]" },
12336 { titlemessage_initial, "[titlemessage_initial]" },
12337 { titlemessage, "[titlemessage]" },
12341 SetupFileHash *setup_file_hash;
12344 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
12347 // the following initializes hierarchical values from dynamic configuration
12349 // special case: initialize with default values that may be overwritten
12350 // (e.g., init "menu.draw_xoffset.INFO" from "menu.draw_xoffset")
12351 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
12353 struct TokenIntPtrInfo menu_config[] =
12355 { "menu.draw_xoffset", &menu.draw_xoffset[i] },
12356 { "menu.draw_yoffset", &menu.draw_yoffset[i] },
12357 { "menu.list_size", &menu.list_size[i] }
12360 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
12362 char *token = menu_config[j].token;
12363 char *value = getHashEntry(setup_file_hash, token);
12366 *menu_config[j].value = get_integer_from_string(value);
12370 // special case: initialize with default values that may be overwritten
12371 // (eg, init "menu.draw_xoffset.INFO[XXX]" from "menu.draw_xoffset.INFO")
12372 for (i = 0; i < NUM_SPECIAL_GFX_INFO_ARGS; i++)
12374 struct TokenIntPtrInfo menu_config[] =
12376 { "menu.draw_xoffset.INFO", &menu.draw_xoffset_info[i] },
12377 { "menu.draw_yoffset.INFO", &menu.draw_yoffset_info[i] },
12378 { "menu.list_size.INFO", &menu.list_size_info[i] }
12381 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
12383 char *token = menu_config[j].token;
12384 char *value = getHashEntry(setup_file_hash, token);
12387 *menu_config[j].value = get_integer_from_string(value);
12391 // special case: initialize with default values that may be overwritten
12392 // (eg, init "menu.draw_xoffset.SETUP[XXX]" from "menu.draw_xoffset.SETUP")
12393 for (i = 0; i < NUM_SPECIAL_GFX_SETUP_ARGS; i++)
12395 struct TokenIntPtrInfo menu_config[] =
12397 { "menu.draw_xoffset.SETUP", &menu.draw_xoffset_setup[i] },
12398 { "menu.draw_yoffset.SETUP", &menu.draw_yoffset_setup[i] }
12401 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
12403 char *token = menu_config[j].token;
12404 char *value = getHashEntry(setup_file_hash, token);
12407 *menu_config[j].value = get_integer_from_string(value);
12411 // special case: initialize with default values that may be overwritten
12412 // (eg, init "menu.line_spacing.INFO[XXX]" from "menu.line_spacing.INFO")
12413 for (i = 0; i < NUM_SPECIAL_GFX_INFO_ARGS; i++)
12415 struct TokenIntPtrInfo menu_config[] =
12417 { "menu.left_spacing.INFO", &menu.left_spacing_info[i] },
12418 { "menu.right_spacing.INFO", &menu.right_spacing_info[i] },
12419 { "menu.top_spacing.INFO", &menu.top_spacing_info[i] },
12420 { "menu.bottom_spacing.INFO", &menu.bottom_spacing_info[i] },
12421 { "menu.paragraph_spacing.INFO", &menu.paragraph_spacing_info[i] },
12422 { "menu.headline1_spacing.INFO", &menu.headline1_spacing_info[i] },
12423 { "menu.headline2_spacing.INFO", &menu.headline2_spacing_info[i] },
12424 { "menu.line_spacing.INFO", &menu.line_spacing_info[i] },
12425 { "menu.extra_spacing.INFO", &menu.extra_spacing_info[i] },
12428 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
12430 char *token = menu_config[j].token;
12431 char *value = getHashEntry(setup_file_hash, token);
12434 *menu_config[j].value = get_integer_from_string(value);
12438 // special case: initialize with default values that may be overwritten
12439 // (eg, init "menu.enter_screen.SCORES.xyz" from "menu.enter_screen.xyz")
12440 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
12442 struct TokenIntPtrInfo menu_config[] =
12444 { "menu.enter_screen.fade_mode", &menu.enter_screen[i].fade_mode },
12445 { "menu.enter_screen.fade_delay", &menu.enter_screen[i].fade_delay },
12446 { "menu.enter_screen.post_delay", &menu.enter_screen[i].post_delay },
12447 { "menu.leave_screen.fade_mode", &menu.leave_screen[i].fade_mode },
12448 { "menu.leave_screen.fade_delay", &menu.leave_screen[i].fade_delay },
12449 { "menu.leave_screen.post_delay", &menu.leave_screen[i].post_delay },
12450 { "menu.next_screen.fade_mode", &menu.next_screen[i].fade_mode },
12451 { "menu.next_screen.fade_delay", &menu.next_screen[i].fade_delay },
12452 { "menu.next_screen.post_delay", &menu.next_screen[i].post_delay }
12455 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
12457 char *token = menu_config[j].token;
12458 char *value = getHashEntry(setup_file_hash, token);
12461 *menu_config[j].value = get_token_parameter_value(token, value);
12465 // special case: initialize with default values that may be overwritten
12466 // (eg, init "viewport.door_1.MAIN.xyz" from "viewport.door_1.xyz")
12467 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
12471 char *token_prefix;
12472 struct RectWithBorder *struct_ptr;
12476 { "viewport.window", &viewport.window[i] },
12477 { "viewport.playfield", &viewport.playfield[i] },
12478 { "viewport.door_1", &viewport.door_1[i] },
12479 { "viewport.door_2", &viewport.door_2[i] }
12482 for (j = 0; j < ARRAY_SIZE(vp_struct); j++)
12484 struct TokenIntPtrInfo vp_config[] =
12486 { ".x", &vp_struct[j].struct_ptr->x },
12487 { ".y", &vp_struct[j].struct_ptr->y },
12488 { ".width", &vp_struct[j].struct_ptr->width },
12489 { ".height", &vp_struct[j].struct_ptr->height },
12490 { ".min_width", &vp_struct[j].struct_ptr->min_width },
12491 { ".min_height", &vp_struct[j].struct_ptr->min_height },
12492 { ".max_width", &vp_struct[j].struct_ptr->max_width },
12493 { ".max_height", &vp_struct[j].struct_ptr->max_height },
12494 { ".margin_left", &vp_struct[j].struct_ptr->margin_left },
12495 { ".margin_right", &vp_struct[j].struct_ptr->margin_right },
12496 { ".margin_top", &vp_struct[j].struct_ptr->margin_top },
12497 { ".margin_bottom", &vp_struct[j].struct_ptr->margin_bottom },
12498 { ".border_left", &vp_struct[j].struct_ptr->border_left },
12499 { ".border_right", &vp_struct[j].struct_ptr->border_right },
12500 { ".border_top", &vp_struct[j].struct_ptr->border_top },
12501 { ".border_bottom", &vp_struct[j].struct_ptr->border_bottom },
12502 { ".border_size", &vp_struct[j].struct_ptr->border_size },
12503 { ".align_size", &vp_struct[j].struct_ptr->align_size },
12504 { ".align", &vp_struct[j].struct_ptr->align },
12505 { ".valign", &vp_struct[j].struct_ptr->valign }
12508 for (k = 0; k < ARRAY_SIZE(vp_config); k++)
12510 char *token = getStringCat2(vp_struct[j].token_prefix,
12511 vp_config[k].token);
12512 char *value = getHashEntry(setup_file_hash, token);
12515 *vp_config[k].value = get_token_parameter_value(token, value);
12522 // special case: initialize with default values that may be overwritten
12523 // (e.g., init "[title].fade_mode" from "menu.next_screen.TITLE.fade_mode")
12524 for (i = 0; title_info[i].info != NULL; i++)
12526 struct TitleFadingInfo *info = title_info[i].info;
12527 char *base_token = title_info[i].text;
12529 for (j = 0; title_tokens[j].type != -1; j++)
12531 char *token = getStringCat2(base_token, title_tokens[j].text);
12532 char *value = getHashEntry(setup_file_hash, token);
12536 int parameter_value = get_token_parameter_value(token, value);
12540 *(int *)title_tokens[j].value = (int)parameter_value;
12549 // special case: initialize with default values that may be overwritten
12550 // (e.g., init "titlemessage_1.fade_mode" from "[titlemessage].fade_mode")
12551 for (i = 0; titlemessage_arrays[i].array != NULL; i++)
12553 struct TitleMessageInfo *array = titlemessage_arrays[i].array;
12554 char *base_token = titlemessage_arrays[i].text;
12556 for (j = 0; titlemessage_tokens[j].type != -1; j++)
12558 char *token = getStringCat2(base_token, titlemessage_tokens[j].text);
12559 char *value = getHashEntry(setup_file_hash, token);
12563 int parameter_value = get_token_parameter_value(token, value);
12565 for (k = 0; k < MAX_NUM_TITLE_MESSAGES; k++)
12569 if (titlemessage_tokens[j].type == TYPE_INTEGER)
12570 *(int *)titlemessage_tokens[j].value = (int)parameter_value;
12572 *(boolean *)titlemessage_tokens[j].value = (boolean)parameter_value;
12582 // special case: check if network and preview player positions are redefined,
12583 // to compare this later against the main menu level preview being redefined
12584 struct TokenIntPtrInfo menu_config_players[] =
12586 { "main.network_players.x", &menu.main.network_players.redefined },
12587 { "main.network_players.y", &menu.main.network_players.redefined },
12588 { "main.preview_players.x", &menu.main.preview_players.redefined },
12589 { "main.preview_players.y", &menu.main.preview_players.redefined },
12590 { "preview.x", &preview.redefined },
12591 { "preview.y", &preview.redefined }
12594 for (i = 0; i < ARRAY_SIZE(menu_config_players); i++)
12595 *menu_config_players[i].value = FALSE;
12597 for (i = 0; i < ARRAY_SIZE(menu_config_players); i++)
12598 if (getHashEntry(setup_file_hash, menu_config_players[i].token) != NULL)
12599 *menu_config_players[i].value = TRUE;
12601 // read (and overwrite with) values that may be specified in config file
12602 InitMenuDesignSettings_FromHash(setup_file_hash, TRUE);
12604 freeSetupFileHash(setup_file_hash);
12607 void LoadMenuDesignSettings(void)
12609 char *filename_base = UNDEFINED_FILENAME, *filename_local;
12611 InitMenuDesignSettings_Static();
12612 InitMenuDesignSettings_SpecialPreProcessing();
12614 if (!GFX_OVERRIDE_ARTWORK(ARTWORK_TYPE_GRAPHICS))
12616 // first look for special settings configured in level series config
12617 filename_base = getCustomArtworkLevelConfigFilename(ARTWORK_TYPE_GRAPHICS);
12619 if (fileExists(filename_base))
12620 LoadMenuDesignSettingsFromFilename(filename_base);
12623 filename_local = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
12625 if (filename_local != NULL && !strEqual(filename_base, filename_local))
12626 LoadMenuDesignSettingsFromFilename(filename_local);
12628 InitMenuDesignSettings_SpecialPostProcessing();
12631 void LoadMenuDesignSettings_AfterGraphics(void)
12633 InitMenuDesignSettings_SpecialPostProcessing_AfterGraphics();
12636 void LoadUserDefinedEditorElementList(int **elements, int *num_elements)
12638 char *filename = getEditorSetupFilename();
12639 SetupFileList *setup_file_list, *list;
12640 SetupFileHash *element_hash;
12641 int num_unknown_tokens = 0;
12644 if ((setup_file_list = loadSetupFileList(filename)) == NULL)
12647 element_hash = newSetupFileHash();
12649 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
12650 setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
12652 // determined size may be larger than needed (due to unknown elements)
12654 for (list = setup_file_list; list != NULL; list = list->next)
12657 // add space for up to 3 more elements for padding that may be needed
12658 *num_elements += 3;
12660 // free memory for old list of elements, if needed
12661 checked_free(*elements);
12663 // allocate memory for new list of elements
12664 *elements = checked_malloc(*num_elements * sizeof(int));
12667 for (list = setup_file_list; list != NULL; list = list->next)
12669 char *value = getHashEntry(element_hash, list->token);
12671 if (value == NULL) // try to find obsolete token mapping
12673 char *mapped_token = get_mapped_token(list->token);
12675 if (mapped_token != NULL)
12677 value = getHashEntry(element_hash, mapped_token);
12679 free(mapped_token);
12685 (*elements)[(*num_elements)++] = atoi(value);
12689 if (num_unknown_tokens == 0)
12692 Warn("unknown token(s) found in config file:");
12693 Warn("- config file: '%s'", filename);
12695 num_unknown_tokens++;
12698 Warn("- token: '%s'", list->token);
12702 if (num_unknown_tokens > 0)
12705 while (*num_elements % 4) // pad with empty elements, if needed
12706 (*elements)[(*num_elements)++] = EL_EMPTY;
12708 freeSetupFileList(setup_file_list);
12709 freeSetupFileHash(element_hash);
12712 for (i = 0; i < *num_elements; i++)
12713 Debug("editor", "element '%s' [%d]\n",
12714 element_info[(*elements)[i]].token_name, (*elements)[i]);
12718 static struct MusicFileInfo *get_music_file_info_ext(char *basename, int music,
12721 SetupFileHash *setup_file_hash = NULL;
12722 struct MusicFileInfo tmp_music_file_info, *new_music_file_info;
12723 char *filename_music, *filename_prefix, *filename_info;
12729 token_to_value_ptr[] =
12731 { "title_header", &tmp_music_file_info.title_header },
12732 { "artist_header", &tmp_music_file_info.artist_header },
12733 { "album_header", &tmp_music_file_info.album_header },
12734 { "year_header", &tmp_music_file_info.year_header },
12736 { "title", &tmp_music_file_info.title },
12737 { "artist", &tmp_music_file_info.artist },
12738 { "album", &tmp_music_file_info.album },
12739 { "year", &tmp_music_file_info.year },
12745 filename_music = (is_sound ? getCustomSoundFilename(basename) :
12746 getCustomMusicFilename(basename));
12748 if (filename_music == NULL)
12751 // ---------- try to replace file extension ----------
12753 filename_prefix = getStringCopy(filename_music);
12754 if (strrchr(filename_prefix, '.') != NULL)
12755 *strrchr(filename_prefix, '.') = '\0';
12756 filename_info = getStringCat2(filename_prefix, ".txt");
12758 if (fileExists(filename_info))
12759 setup_file_hash = loadSetupFileHash(filename_info);
12761 free(filename_prefix);
12762 free(filename_info);
12764 if (setup_file_hash == NULL)
12766 // ---------- try to add file extension ----------
12768 filename_prefix = getStringCopy(filename_music);
12769 filename_info = getStringCat2(filename_prefix, ".txt");
12771 if (fileExists(filename_info))
12772 setup_file_hash = loadSetupFileHash(filename_info);
12774 free(filename_prefix);
12775 free(filename_info);
12778 if (setup_file_hash == NULL)
12781 // ---------- music file info found ----------
12783 clear_mem(&tmp_music_file_info, sizeof(struct MusicFileInfo));
12785 for (i = 0; token_to_value_ptr[i].token != NULL; i++)
12787 char *value = getHashEntry(setup_file_hash, token_to_value_ptr[i].token);
12789 *token_to_value_ptr[i].value_ptr =
12790 getStringCopy(value != NULL && *value != '\0' ? value : UNKNOWN_NAME);
12793 tmp_music_file_info.basename = getStringCopy(basename);
12794 tmp_music_file_info.music = music;
12795 tmp_music_file_info.is_sound = is_sound;
12797 new_music_file_info = checked_malloc(sizeof(struct MusicFileInfo));
12798 *new_music_file_info = tmp_music_file_info;
12800 return new_music_file_info;
12803 static struct MusicFileInfo *get_music_file_info(char *basename, int music)
12805 return get_music_file_info_ext(basename, music, FALSE);
12808 static struct MusicFileInfo *get_sound_file_info(char *basename, int sound)
12810 return get_music_file_info_ext(basename, sound, TRUE);
12813 static boolean music_info_listed_ext(struct MusicFileInfo *list,
12814 char *basename, boolean is_sound)
12816 for (; list != NULL; list = list->next)
12817 if (list->is_sound == is_sound && strEqual(list->basename, basename))
12823 static boolean music_info_listed(struct MusicFileInfo *list, char *basename)
12825 return music_info_listed_ext(list, basename, FALSE);
12828 static boolean sound_info_listed(struct MusicFileInfo *list, char *basename)
12830 return music_info_listed_ext(list, basename, TRUE);
12833 void LoadMusicInfo(void)
12835 char *music_directory = getCustomMusicDirectory();
12836 int num_music = getMusicListSize();
12837 int num_music_noconf = 0;
12838 int num_sounds = getSoundListSize();
12840 DirectoryEntry *dir_entry;
12841 struct FileInfo *music, *sound;
12842 struct MusicFileInfo *next, **new;
12845 while (music_file_info != NULL)
12847 next = music_file_info->next;
12849 checked_free(music_file_info->basename);
12851 checked_free(music_file_info->title_header);
12852 checked_free(music_file_info->artist_header);
12853 checked_free(music_file_info->album_header);
12854 checked_free(music_file_info->year_header);
12856 checked_free(music_file_info->title);
12857 checked_free(music_file_info->artist);
12858 checked_free(music_file_info->album);
12859 checked_free(music_file_info->year);
12861 free(music_file_info);
12863 music_file_info = next;
12866 new = &music_file_info;
12868 for (i = 0; i < num_music; i++)
12870 music = getMusicListEntry(i);
12872 if (music->filename == NULL)
12875 if (strEqual(music->filename, UNDEFINED_FILENAME))
12878 // a configured file may be not recognized as music
12879 if (!FileIsMusic(music->filename))
12882 if (!music_info_listed(music_file_info, music->filename))
12884 *new = get_music_file_info(music->filename, i);
12887 new = &(*new)->next;
12891 if ((dir = openDirectory(music_directory)) == NULL)
12893 Warn("cannot read music directory '%s'", music_directory);
12898 while ((dir_entry = readDirectory(dir)) != NULL) // loop all entries
12900 char *basename = dir_entry->basename;
12901 boolean music_already_used = FALSE;
12904 // skip all music files that are configured in music config file
12905 for (i = 0; i < num_music; i++)
12907 music = getMusicListEntry(i);
12909 if (music->filename == NULL)
12912 if (strEqual(basename, music->filename))
12914 music_already_used = TRUE;
12919 if (music_already_used)
12922 if (!FileIsMusic(dir_entry->filename))
12925 if (!music_info_listed(music_file_info, basename))
12927 *new = get_music_file_info(basename, MAP_NOCONF_MUSIC(num_music_noconf));
12930 new = &(*new)->next;
12933 num_music_noconf++;
12936 closeDirectory(dir);
12938 for (i = 0; i < num_sounds; i++)
12940 sound = getSoundListEntry(i);
12942 if (sound->filename == NULL)
12945 if (strEqual(sound->filename, UNDEFINED_FILENAME))
12948 // a configured file may be not recognized as sound
12949 if (!FileIsSound(sound->filename))
12952 if (!sound_info_listed(music_file_info, sound->filename))
12954 *new = get_sound_file_info(sound->filename, i);
12956 new = &(*new)->next;
12960 // add pointers to previous list nodes
12962 struct MusicFileInfo *node = music_file_info;
12964 while (node != NULL)
12967 node->next->prev = node;
12973 static void add_helpanim_entry(int element, int action, int direction,
12974 int delay, int *num_list_entries)
12976 struct HelpAnimInfo *new_list_entry;
12977 (*num_list_entries)++;
12980 checked_realloc(helpanim_info,
12981 *num_list_entries * sizeof(struct HelpAnimInfo));
12982 new_list_entry = &helpanim_info[*num_list_entries - 1];
12984 new_list_entry->element = element;
12985 new_list_entry->action = action;
12986 new_list_entry->direction = direction;
12987 new_list_entry->delay = delay;
12990 static void print_unknown_token(char *filename, char *token, int token_nr)
12995 Warn("unknown token(s) found in config file:");
12996 Warn("- config file: '%s'", filename);
12999 Warn("- token: '%s'", token);
13002 static void print_unknown_token_end(int token_nr)
13008 void LoadHelpAnimInfo(void)
13010 char *filename = getHelpAnimFilename();
13011 SetupFileList *setup_file_list = NULL, *list;
13012 SetupFileHash *element_hash, *action_hash, *direction_hash;
13013 int num_list_entries = 0;
13014 int num_unknown_tokens = 0;
13017 if (fileExists(filename))
13018 setup_file_list = loadSetupFileList(filename);
13020 if (setup_file_list == NULL)
13022 // use reliable default values from static configuration
13023 SetupFileList *insert_ptr;
13025 insert_ptr = setup_file_list =
13026 newSetupFileList(helpanim_config[0].token,
13027 helpanim_config[0].value);
13029 for (i = 1; helpanim_config[i].token; i++)
13030 insert_ptr = addListEntry(insert_ptr,
13031 helpanim_config[i].token,
13032 helpanim_config[i].value);
13035 element_hash = newSetupFileHash();
13036 action_hash = newSetupFileHash();
13037 direction_hash = newSetupFileHash();
13039 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
13040 setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
13042 for (i = 0; i < NUM_ACTIONS; i++)
13043 setHashEntry(action_hash, element_action_info[i].suffix,
13044 i_to_a(element_action_info[i].value));
13046 // do not store direction index (bit) here, but direction value!
13047 for (i = 0; i < NUM_DIRECTIONS_FULL; i++)
13048 setHashEntry(direction_hash, element_direction_info[i].suffix,
13049 i_to_a(1 << element_direction_info[i].value));
13051 for (list = setup_file_list; list != NULL; list = list->next)
13053 char *element_token, *action_token, *direction_token;
13054 char *element_value, *action_value, *direction_value;
13055 int delay = atoi(list->value);
13057 if (strEqual(list->token, "end"))
13059 add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
13064 /* first try to break element into element/action/direction parts;
13065 if this does not work, also accept combined "element[.act][.dir]"
13066 elements (like "dynamite.active"), which are unique elements */
13068 if (strchr(list->token, '.') == NULL) // token contains no '.'
13070 element_value = getHashEntry(element_hash, list->token);
13071 if (element_value != NULL) // element found
13072 add_helpanim_entry(atoi(element_value), -1, -1, delay,
13073 &num_list_entries);
13076 // no further suffixes found -- this is not an element
13077 print_unknown_token(filename, list->token, num_unknown_tokens++);
13083 // token has format "<prefix>.<something>"
13085 action_token = strchr(list->token, '.'); // suffix may be action ...
13086 direction_token = action_token; // ... or direction
13088 element_token = getStringCopy(list->token);
13089 *strchr(element_token, '.') = '\0';
13091 element_value = getHashEntry(element_hash, element_token);
13093 if (element_value == NULL) // this is no element
13095 element_value = getHashEntry(element_hash, list->token);
13096 if (element_value != NULL) // combined element found
13097 add_helpanim_entry(atoi(element_value), -1, -1, delay,
13098 &num_list_entries);
13100 print_unknown_token(filename, list->token, num_unknown_tokens++);
13102 free(element_token);
13107 action_value = getHashEntry(action_hash, action_token);
13109 if (action_value != NULL) // action found
13111 add_helpanim_entry(atoi(element_value), atoi(action_value), -1, delay,
13112 &num_list_entries);
13114 free(element_token);
13119 direction_value = getHashEntry(direction_hash, direction_token);
13121 if (direction_value != NULL) // direction found
13123 add_helpanim_entry(atoi(element_value), -1, atoi(direction_value), delay,
13124 &num_list_entries);
13126 free(element_token);
13131 if (strchr(action_token + 1, '.') == NULL)
13133 // no further suffixes found -- this is not an action nor direction
13135 element_value = getHashEntry(element_hash, list->token);
13136 if (element_value != NULL) // combined element found
13137 add_helpanim_entry(atoi(element_value), -1, -1, delay,
13138 &num_list_entries);
13140 print_unknown_token(filename, list->token, num_unknown_tokens++);
13142 free(element_token);
13147 // token has format "<prefix>.<suffix>.<something>"
13149 direction_token = strchr(action_token + 1, '.');
13151 action_token = getStringCopy(action_token);
13152 *strchr(action_token + 1, '.') = '\0';
13154 action_value = getHashEntry(action_hash, action_token);
13156 if (action_value == NULL) // this is no action
13158 element_value = getHashEntry(element_hash, list->token);
13159 if (element_value != NULL) // combined element found
13160 add_helpanim_entry(atoi(element_value), -1, -1, delay,
13161 &num_list_entries);
13163 print_unknown_token(filename, list->token, num_unknown_tokens++);
13165 free(element_token);
13166 free(action_token);
13171 direction_value = getHashEntry(direction_hash, direction_token);
13173 if (direction_value != NULL) // direction found
13175 add_helpanim_entry(atoi(element_value), atoi(action_value),
13176 atoi(direction_value), delay, &num_list_entries);
13178 free(element_token);
13179 free(action_token);
13184 // this is no direction
13186 element_value = getHashEntry(element_hash, list->token);
13187 if (element_value != NULL) // combined element found
13188 add_helpanim_entry(atoi(element_value), -1, -1, delay,
13189 &num_list_entries);
13191 print_unknown_token(filename, list->token, num_unknown_tokens++);
13193 free(element_token);
13194 free(action_token);
13197 print_unknown_token_end(num_unknown_tokens);
13199 add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
13200 add_helpanim_entry(HELPANIM_LIST_END, -1, -1, -1, &num_list_entries);
13202 freeSetupFileList(setup_file_list);
13203 freeSetupFileHash(element_hash);
13204 freeSetupFileHash(action_hash);
13205 freeSetupFileHash(direction_hash);
13208 for (i = 0; i < num_list_entries; i++)
13209 Debug("files:LoadHelpAnimInfo", "'%s': %d, %d, %d => %d",
13210 EL_NAME(helpanim_info[i].element),
13211 helpanim_info[i].element,
13212 helpanim_info[i].action,
13213 helpanim_info[i].direction,
13214 helpanim_info[i].delay);
13218 void LoadHelpTextInfo(void)
13220 char *filename = getHelpTextFilename();
13223 if (helptext_info != NULL)
13225 freeSetupFileHash(helptext_info);
13226 helptext_info = NULL;
13229 if (fileExists(filename))
13230 helptext_info = loadSetupFileHash(filename);
13232 if (helptext_info == NULL)
13234 // use reliable default values from static configuration
13235 helptext_info = newSetupFileHash();
13237 for (i = 0; helptext_config[i].token; i++)
13238 setHashEntry(helptext_info,
13239 helptext_config[i].token,
13240 helptext_config[i].value);
13244 BEGIN_HASH_ITERATION(helptext_info, itr)
13246 Debug("files:LoadHelpTextInfo", "'%s' => '%s'",
13247 HASH_ITERATION_TOKEN(itr), HASH_ITERATION_VALUE(itr));
13249 END_HASH_ITERATION(hash, itr)
13254 // ----------------------------------------------------------------------------
13256 // ----------------------------------------------------------------------------
13258 #define MAX_NUM_CONVERT_LEVELS 1000
13260 void ConvertLevels(void)
13262 static LevelDirTree *convert_leveldir = NULL;
13263 static int convert_level_nr = -1;
13264 static int num_levels_handled = 0;
13265 static int num_levels_converted = 0;
13266 static boolean levels_failed[MAX_NUM_CONVERT_LEVELS];
13269 convert_leveldir = getTreeInfoFromIdentifier(leveldir_first,
13270 global.convert_leveldir);
13272 if (convert_leveldir == NULL)
13273 Fail("no such level identifier: '%s'", global.convert_leveldir);
13275 leveldir_current = convert_leveldir;
13277 if (global.convert_level_nr != -1)
13279 convert_leveldir->first_level = global.convert_level_nr;
13280 convert_leveldir->last_level = global.convert_level_nr;
13283 convert_level_nr = convert_leveldir->first_level;
13285 PrintLine("=", 79);
13286 Print("Converting levels\n");
13287 PrintLine("-", 79);
13288 Print("Level series identifier: '%s'\n", convert_leveldir->identifier);
13289 Print("Level series name: '%s'\n", convert_leveldir->name);
13290 Print("Level series author: '%s'\n", convert_leveldir->author);
13291 Print("Number of levels: %d\n", convert_leveldir->levels);
13292 PrintLine("=", 79);
13295 for (i = 0; i < MAX_NUM_CONVERT_LEVELS; i++)
13296 levels_failed[i] = FALSE;
13298 while (convert_level_nr <= convert_leveldir->last_level)
13300 char *level_filename;
13303 level_nr = convert_level_nr++;
13305 Print("Level %03d: ", level_nr);
13307 LoadLevel(level_nr);
13308 if (level.no_level_file || level.no_valid_file)
13310 Print("(no level)\n");
13314 Print("converting level ... ");
13317 // special case: conversion of some EMC levels as requested by ACME
13318 level.game_engine_type = GAME_ENGINE_TYPE_RND;
13321 level_filename = getDefaultLevelFilename(level_nr);
13322 new_level = !fileExists(level_filename);
13326 SaveLevel(level_nr);
13328 num_levels_converted++;
13330 Print("converted.\n");
13334 if (level_nr >= 0 && level_nr < MAX_NUM_CONVERT_LEVELS)
13335 levels_failed[level_nr] = TRUE;
13337 Print("NOT CONVERTED -- LEVEL ALREADY EXISTS.\n");
13340 num_levels_handled++;
13344 PrintLine("=", 79);
13345 Print("Number of levels handled: %d\n", num_levels_handled);
13346 Print("Number of levels converted: %d (%d%%)\n", num_levels_converted,
13347 (num_levels_handled ?
13348 num_levels_converted * 100 / num_levels_handled : 0));
13349 PrintLine("-", 79);
13350 Print("Summary (for automatic parsing by scripts):\n");
13351 Print("LEVELDIR '%s', CONVERTED %d/%d (%d%%)",
13352 convert_leveldir->identifier, num_levels_converted,
13353 num_levels_handled,
13354 (num_levels_handled ?
13355 num_levels_converted * 100 / num_levels_handled : 0));
13357 if (num_levels_handled != num_levels_converted)
13359 Print(", FAILED:");
13360 for (i = 0; i < MAX_NUM_CONVERT_LEVELS; i++)
13361 if (levels_failed[i])
13366 PrintLine("=", 79);
13368 CloseAllAndExit(0);
13372 // ----------------------------------------------------------------------------
13373 // create and save images for use in level sketches (raw BMP format)
13374 // ----------------------------------------------------------------------------
13376 void CreateLevelSketchImages(void)
13382 InitElementPropertiesGfxElement();
13384 bitmap1 = CreateBitmap(TILEX, TILEY, DEFAULT_DEPTH);
13385 bitmap2 = CreateBitmap(MINI_TILEX, MINI_TILEY, DEFAULT_DEPTH);
13387 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
13389 int element = getMappedElement(i);
13390 char basename1[16];
13391 char basename2[16];
13395 sprintf(basename1, "%04d.bmp", i);
13396 sprintf(basename2, "%04ds.bmp", i);
13398 filename1 = getPath2(global.create_sketch_images_dir, basename1);
13399 filename2 = getPath2(global.create_sketch_images_dir, basename2);
13401 DrawSizedElement(0, 0, element, TILESIZE);
13402 BlitBitmap(drawto, bitmap1, SX, SY, TILEX, TILEY, 0, 0);
13404 if (SDL_SaveBMP(bitmap1->surface, filename1) != 0)
13405 Fail("cannot save level sketch image file '%s'", filename1);
13407 DrawSizedElement(0, 0, element, MINI_TILESIZE);
13408 BlitBitmap(drawto, bitmap2, SX, SY, MINI_TILEX, MINI_TILEY, 0, 0);
13410 if (SDL_SaveBMP(bitmap2->surface, filename2) != 0)
13411 Fail("cannot save level sketch image file '%s'", filename2);
13416 // create corresponding SQL statements (for normal and small images)
13419 printf("insert into phpbb_words values (NULL, '`%03d', '<IMG class=\"levelsketch\" src=\"/I/%04d.png\"/>');\n", i, i);
13420 printf("insert into phpbb_words values (NULL, '¸%03d', '<IMG class=\"levelsketch\" src=\"/I/%04ds.png\"/>');\n", i, i);
13423 printf("insert into phpbb_words values (NULL, '`%04d', '<IMG class=\"levelsketch\" src=\"/I/%04d.png\"/>');\n", i, i);
13424 printf("insert into phpbb_words values (NULL, '¸%04d', '<IMG class=\"levelsketch\" src=\"/I/%04ds.png\"/>');\n", i, i);
13426 // optional: create content for forum level sketch demonstration post
13428 fprintf(stderr, "%03d `%03d%c", i, i, (i % 10 < 9 ? ' ' : '\n'));
13431 FreeBitmap(bitmap1);
13432 FreeBitmap(bitmap2);
13435 fprintf(stderr, "\n");
13437 Info("%d normal and small images created", NUM_FILE_ELEMENTS);
13439 CloseAllAndExit(0);
13443 // ----------------------------------------------------------------------------
13444 // create and save images for element collecting animations (raw BMP format)
13445 // ----------------------------------------------------------------------------
13447 static boolean createCollectImage(int element)
13449 return (IS_COLLECTIBLE(element) && !IS_SP_ELEMENT(element));
13452 void CreateCollectElementImages(void)
13456 int anim_frames = num_steps - 1;
13457 int tile_size = TILESIZE;
13458 int anim_width = tile_size * anim_frames;
13459 int anim_height = tile_size;
13460 int num_collect_images = 0;
13461 int pos_collect_images = 0;
13463 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
13464 if (createCollectImage(i))
13465 num_collect_images++;
13467 Info("Creating %d element collecting animation images ...",
13468 num_collect_images);
13470 int dst_width = anim_width * 2;
13471 int dst_height = anim_height * num_collect_images / 2;
13472 Bitmap *dst_bitmap = CreateBitmap(dst_width, dst_height, DEFAULT_DEPTH);
13473 char *basename_bmp = "RocksCollect.bmp";
13474 char *basename_png = "RocksCollect.png";
13475 char *filename_bmp = getPath2(global.create_collect_images_dir, basename_bmp);
13476 char *filename_png = getPath2(global.create_collect_images_dir, basename_png);
13477 int len_filename_bmp = strlen(filename_bmp);
13478 int len_filename_png = strlen(filename_png);
13479 int max_command_len = MAX_FILENAME_LEN + len_filename_bmp + len_filename_png;
13480 char cmd_convert[max_command_len];
13482 snprintf(cmd_convert, max_command_len, "convert \"%s\" \"%s\"",
13486 // force using RGBA surface for destination bitmap
13487 SDL_SetColorKey(dst_bitmap->surface, SET_TRANSPARENT_PIXEL,
13488 SDL_MapRGB(dst_bitmap->surface->format, 0x00, 0x00, 0x00));
13490 dst_bitmap->surface =
13491 SDL_ConvertSurfaceFormat(dst_bitmap->surface, SDL_PIXELFORMAT_RGBA32, 0);
13493 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
13495 if (!createCollectImage(i))
13498 int dst_x = (pos_collect_images / (num_collect_images / 2)) * anim_width;
13499 int dst_y = (pos_collect_images % (num_collect_images / 2)) * anim_height;
13500 int graphic = el2img(i);
13501 char *token_name = element_info[i].token_name;
13502 Bitmap *tmp_bitmap = CreateBitmap(tile_size, tile_size, DEFAULT_DEPTH);
13503 Bitmap *src_bitmap;
13506 Info("- creating collecting image for '%s' ...", token_name);
13508 getGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
13510 BlitBitmap(src_bitmap, tmp_bitmap, src_x, src_y,
13511 tile_size, tile_size, 0, 0);
13513 // force using RGBA surface for temporary bitmap (using transparent black)
13514 SDL_SetColorKey(tmp_bitmap->surface, SET_TRANSPARENT_PIXEL,
13515 SDL_MapRGB(tmp_bitmap->surface->format, 0x00, 0x00, 0x00));
13517 tmp_bitmap->surface =
13518 SDL_ConvertSurfaceFormat(tmp_bitmap->surface, SDL_PIXELFORMAT_RGBA32, 0);
13520 tmp_bitmap->surface_masked = tmp_bitmap->surface;
13522 for (j = 0; j < anim_frames; j++)
13524 int frame_size_final = tile_size * (anim_frames - j) / num_steps;
13525 int frame_size = frame_size_final * num_steps;
13526 int offset = (tile_size - frame_size_final) / 2;
13527 Bitmap *frame_bitmap = ZoomBitmap(tmp_bitmap, frame_size, frame_size);
13529 while (frame_size > frame_size_final)
13533 Bitmap *half_bitmap = ZoomBitmap(frame_bitmap, frame_size, frame_size);
13535 FreeBitmap(frame_bitmap);
13537 frame_bitmap = half_bitmap;
13540 BlitBitmapMasked(frame_bitmap, dst_bitmap, 0, 0,
13541 frame_size_final, frame_size_final,
13542 dst_x + j * tile_size + offset, dst_y + offset);
13544 FreeBitmap(frame_bitmap);
13547 tmp_bitmap->surface_masked = NULL;
13549 FreeBitmap(tmp_bitmap);
13551 pos_collect_images++;
13554 if (SDL_SaveBMP(dst_bitmap->surface, filename_bmp) != 0)
13555 Fail("cannot save element collecting image file '%s'", filename_bmp);
13557 FreeBitmap(dst_bitmap);
13559 Info("Converting image file from BMP to PNG ...");
13561 system(cmd_convert);
13562 unlink(filename_bmp);
13566 CloseAllAndExit(0);
13570 // ----------------------------------------------------------------------------
13571 // create and save images for custom and group elements (raw BMP format)
13572 // ----------------------------------------------------------------------------
13574 void CreateCustomElementImages(char *directory)
13576 char *src_basename = "RocksCE-template.ilbm";
13577 char *dst_basename = "RocksCE.bmp";
13578 char *src_filename = getPath2(directory, src_basename);
13579 char *dst_filename = getPath2(directory, dst_basename);
13580 Bitmap *src_bitmap;
13582 int yoffset_ce = 0;
13583 int yoffset_ge = (TILEY * NUM_CUSTOM_ELEMENTS / 16);
13586 InitVideoDefaults();
13588 ReCreateBitmap(&backbuffer, video.width, video.height);
13590 src_bitmap = LoadImage(src_filename);
13592 bitmap = CreateBitmap(TILEX * 16 * 2,
13593 TILEY * (NUM_CUSTOM_ELEMENTS + NUM_GROUP_ELEMENTS) / 16,
13596 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
13603 BlitBitmap(src_bitmap, bitmap, 0, 0, TILEX, TILEY,
13604 TILEX * x, TILEY * y + yoffset_ce);
13606 BlitBitmap(src_bitmap, bitmap, 0, TILEY,
13608 TILEX * x + TILEX * 16,
13609 TILEY * y + yoffset_ce);
13611 for (j = 2; j >= 0; j--)
13615 BlitBitmap(src_bitmap, bitmap,
13616 TILEX + c * 7, 0, 6, 10,
13617 TILEX * x + 6 + j * 7,
13618 TILEY * y + 11 + yoffset_ce);
13620 BlitBitmap(src_bitmap, bitmap,
13621 TILEX + c * 8, TILEY, 6, 10,
13622 TILEX * 16 + TILEX * x + 6 + j * 8,
13623 TILEY * y + 10 + yoffset_ce);
13629 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
13636 BlitBitmap(src_bitmap, bitmap, 0, 0, TILEX, TILEY,
13637 TILEX * x, TILEY * y + yoffset_ge);
13639 BlitBitmap(src_bitmap, bitmap, 0, TILEY,
13641 TILEX * x + TILEX * 16,
13642 TILEY * y + yoffset_ge);
13644 for (j = 1; j >= 0; j--)
13648 BlitBitmap(src_bitmap, bitmap, TILEX + c * 10, 11, 10, 10,
13649 TILEX * x + 6 + j * 10,
13650 TILEY * y + 11 + yoffset_ge);
13652 BlitBitmap(src_bitmap, bitmap,
13653 TILEX + c * 8, TILEY + 12, 6, 10,
13654 TILEX * 16 + TILEX * x + 10 + j * 8,
13655 TILEY * y + 10 + yoffset_ge);
13661 if (SDL_SaveBMP(bitmap->surface, dst_filename) != 0)
13662 Fail("cannot save CE graphics file '%s'", dst_filename);
13664 FreeBitmap(bitmap);
13666 CloseAllAndExit(0);